From 17f4bba2f3a4a3bf74605a2bd40f2612ca8d60fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Sun, 20 Sep 2026 12:52:29 +0200 Subject: [PATCH 001/648] [PWGLF] Fix case for neutron in histogram filling (#17977) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 593e5838f84..87b437c192b 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -1995,8 +1995,11 @@ struct HadronNucleiCorrelation { case -o2::constants::physics::Pdg::kDeuteron: registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), -1. * particle.pt()); break; + case PDG_t::kNeutron: + case -PDG_t::kNeutron: + break; default: - LOG(fatal) << "Unhandled PDG code, should not happen, check the code!" << particle.pdgCode(); + LOG(fatal) << "Unhandled PDG code, should not happen, check the code! " << particle.pdgCode(); break; } } From 94313d89649bbaadd96fb4b83b67064a4bbfda1a Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Sun, 20 Sep 2026 13:14:35 +0200 Subject: [PATCH 002/648] [PWGHF] Templetise Jpsi type in HF helper functions for B2JPsi (#17980) --- PWGHF/Core/HfHelper.h | 12 ++++++------ PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx | 4 ++-- PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx | 2 +- PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PWGHF/Core/HfHelper.h b/PWGHF/Core/HfHelper.h index c128598522d..e6299b483f8 100644 --- a/PWGHF/Core/HfHelper.h +++ b/PWGHF/Core/HfHelper.h @@ -975,7 +975,7 @@ struct HfHelper { /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating B0 candidate mass /// \param useK0StarPdgMass Use PDG mass for K*0 when calculating B0 candidate mass /// \return true if candidate passes all selections - template + template static bool selectionB0ToJpsiK0StarTopol(const T1& candB0, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass, const bool useK0StarPdgMass, const bool k0StarToKPi = true) { auto ptCandB0 = candB0.pt(); @@ -990,7 +990,7 @@ struct HfHelper { : RecoDecay::m(std::array{pVecLfDau0, pVecLfDau1}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); auto ptK0Star = RecoDecay::pt(pVecLfDau0, pVecLfDau1); - auto candJpsi = candB0.jpsi(); + auto candJpsi = candB0.template jpsi_as(); float pseudoPropDecLen = candB0.decayLengthXY() * mCandB0 / ptCandB0; int binPt = o2::analysis::findBin(binsPt, ptCandB0); @@ -1106,7 +1106,7 @@ struct HfHelper { /// \param binsPt pT bin limits /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating B+ candidate mass /// \return true if candidate passes all selections - template + template static bool selectionBplusToJpsiKTopol(const T1& candBp, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass) { auto ptCandBp = candBp.pt(); @@ -1116,7 +1116,7 @@ struct HfHelper { auto const pVecKa = candBp.pVectorProng2(); auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); auto ptKa = RecoDecay::pt(pVecKa); - auto candJpsi = candBp.jpsi(); + auto candJpsi = candBp.template jpsi_as(); float pseudoPropDecLen = candBp.decayLengthXY() * mCandBp / ptCandBp; int binPt = o2::analysis::findBin(binsPt, ptCandBp); @@ -1297,7 +1297,7 @@ struct HfHelper { /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating Bs candidate mass /// \param usePhiPdgMass Use PDG mass for phi when calculating Bs candidate mass /// \return true if candidate passes all selections - template + template static bool selectionBsToJpsiPhiTopol(const T1& candBs, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass, const bool usePhiPdgMass) { auto ptCandBs = candBs.pt(); @@ -1309,7 +1309,7 @@ struct HfHelper { auto mCandPhi = RecoDecay::m(std::array{pVecKa0, pVecKa1}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); auto ptPhi = RecoDecay::pt(pVecKa0, pVecKa1); - auto candJpsi = candBs.jpsi(); + auto candJpsi = candBs.template jpsi_as(); float pseudoPropDecLen = candBs.decayLengthXY() * mCandBs / ptCandBs; int binPt = o2::analysis::findBin(binsPt, ptCandBs); diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx index 3870a9387c7..d4d5ccf7d1c 100644 --- a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -437,8 +437,8 @@ struct HfTaskB0ToJpsiK0StarReduced { SETBIT(statusB0PiK, SelectionStep::RecoSkims); // topological selection for the two K*0 mass hypotheses - bool selKPi = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, true); - bool selPiK = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, false); + bool selKPi = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, true); + bool selPiK = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, false); if (!selKPi && !selPiK && selectionFlagB0 >= BIT(SelectionStep::RecoTopol) * 2 - 1) { return; diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index d685a6decc9..8207b3dc36a 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -385,7 +385,7 @@ struct HfTaskBplusToJpsiKReduced { } SETBIT(statusBplus, SelectionStep::RecoSkims); - if (HfHelper::selectionBplusToJpsiKTopol(candidate, cuts, binsPt, useJpsiPdgMass)) { + if (HfHelper::selectionBplusToJpsiKTopol(candidate, cuts, binsPt, useJpsiPdgMass)) { SETBIT(statusBplus, SelectionStep::RecoTopol); } else if (selectionFlagBplus >= BIT(SelectionStep::RecoTopol) * 2 - 1) { return; diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index 51016464d08..9a374a53e47 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -419,7 +419,7 @@ struct HfTaskBsToJpsiPhiReduced { } SETBIT(statusBs, SelectionStep::RecoSkims); - if (HfHelper::selectionBsToJpsiPhiTopol(candidate, cuts, binsPt, useJpsiPdgMass, usePhiPdgMass)) { + if (HfHelper::selectionBsToJpsiPhiTopol(candidate, cuts, binsPt, useJpsiPdgMass, usePhiPdgMass)) { SETBIT(statusBs, SelectionStep::RecoTopol); } else if (selectionFlagBs >= BIT(SelectionStep::RecoTopol) * 2 - 1) { return; From 9fe4e7170fa62227f9b5acb678ebf3ba0b8ed6ce Mon Sep 17 00:00:00 2001 From: Francesco Mazzaschi <43742195+fmazzasc@users.noreply.github.com> Date: Sun, 20 Sep 2026 15:27:19 +0200 Subject: [PATCH 003/648] [PWGLF] doubleomegatask: fix dca calculation and update data model (#17979) Co-authored-by: Francesco Mazzaschi --- PWGLF/DataModel/LFDoubleOmegaTables.h | 22 +- .../Strangeness/doubleOmegaTreeCreator.cxx | 189 ++++++++++++------ 2 files changed, 146 insertions(+), 65 deletions(-) diff --git a/PWGLF/DataModel/LFDoubleOmegaTables.h b/PWGLF/DataModel/LFDoubleOmegaTables.h index 9cc38e08f43..493d0b6d262 100644 --- a/PWGLF/DataModel/LFDoubleOmegaTables.h +++ b/PWGLF/DataModel/LFDoubleOmegaTables.h @@ -33,12 +33,18 @@ DECLARE_SOA_COLUMN(PrimaryVtxZ, primaryVtxZ, float); DECLARE_SOA_COLUMN(CosPAOmega, cosPAOmega, float); DECLARE_SOA_COLUMN(CosPADirectLambda, cosPADirectLambda, float); DECLARE_SOA_COLUMN(CosPADoubleOmega, cosPADoubleOmega, float); +DECLARE_SOA_COLUMN(CosPAOmegaToPV, cosPAOmegaToPV, float); +DECLARE_SOA_COLUMN(CosPADirectLambdaToPV, cosPADirectLambdaToPV, float); DECLARE_SOA_COLUMN(DCAxyOmegaToPV, dcaXYOmegaToPV, float); -DECLARE_SOA_COLUMN(DCAzOmegaToPV, dcaZOmegaToPV, float); DECLARE_SOA_COLUMN(DCAxyDirectLambdaToPV, dcaXYDirectLambdaToPV, float); -DECLARE_SOA_COLUMN(DCAzDirectLambdaToPV, dcaZDirectLambdaToPV, float); DECLARE_SOA_COLUMN(DCAxyDirectKaonToPV, dcaXYDirectKaonToPV, float); -DECLARE_SOA_COLUMN(DCAzDirectKaonToPV, dcaZDirectKaonToPV, float); +// Transverse DCA to the primary vertex of the final-state tracks, ordered as +// the detector map: Omega kaon, Omega proton, Omega pion, direct proton, direct pion. +DECLARE_SOA_COLUMN(DCAxyOmegaKaonToPV, dcaXYOmegaKaonToPV, float); +DECLARE_SOA_COLUMN(DCAxyOmegaProtonToPV, dcaXYOmegaProtonToPV, float); +DECLARE_SOA_COLUMN(DCAxyOmegaPionToPV, dcaXYOmegaPionToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectProtonToPV, dcaXYDirectProtonToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectPionToPV, dcaXYDirectPionToPV, float); DECLARE_SOA_COLUMN(TPCNSigmaDirectKaon, tpcNSigmaDirectKaon, float); // -999 when TPC is unavailable or the candidate is not reconstructed. DECLARE_SOA_COLUMN(TOFNSigmaDirectKaon, tofNSigmaDirectKaon, float); // -999 when TOF is unavailable or the candidate is not reconstructed. // Three bits per daughter (ITS, TPC, TOF), ordered as: Omega kaon, Omega @@ -71,12 +77,16 @@ DECLARE_SOA_COLUMN(IsReco, isReco, bool); DoubleOmegaTables::CosPAOmega, \ DoubleOmegaTables::CosPADirectLambda, \ DoubleOmegaTables::CosPADoubleOmega, \ + DoubleOmegaTables::CosPAOmegaToPV, \ + DoubleOmegaTables::CosPADirectLambdaToPV, \ DoubleOmegaTables::DCAxyOmegaToPV, \ - DoubleOmegaTables::DCAzOmegaToPV, \ DoubleOmegaTables::DCAxyDirectLambdaToPV, \ - DoubleOmegaTables::DCAzDirectLambdaToPV, \ DoubleOmegaTables::DCAxyDirectKaonToPV, \ - DoubleOmegaTables::DCAzDirectKaonToPV, \ + DoubleOmegaTables::DCAxyOmegaKaonToPV, \ + DoubleOmegaTables::DCAxyOmegaProtonToPV, \ + DoubleOmegaTables::DCAxyOmegaPionToPV, \ + DoubleOmegaTables::DCAxyDirectProtonToPV, \ + DoubleOmegaTables::DCAxyDirectPionToPV, \ DoubleOmegaTables::TPCNSigmaDirectKaon, \ DoubleOmegaTables::TOFNSigmaDirectKaon, \ DoubleOmegaTables::DaughterDetectorMap, \ diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx index 0dc52f1beee..9ca484967f6 100644 --- a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -63,9 +63,10 @@ using CollisionsTable = soa::Join; using FullCascades = aod::CascDataExt; -using TracksFull = soa::Join; -using TracksFullIU = soa::Join; -using TracksFullIUMC = soa::Join; +// Innermost-update tracks: DCAs to the PV are recomputed in this task by propagation, +// so neither the propagated tracks nor TracksDCA are needed. +using TracksFull = soa::Join; +using TracksFullMC = soa::Join; struct DoubleOmegaCandidate { float pt = -999.f; @@ -80,12 +81,16 @@ struct DoubleOmegaCandidate { float cosPAOmega = -999.f; float cosPADirectLambda = -999.f; float cosPADoubleOmega = -999.f; + float cosPAOmegaToPV = -999.f; + float cosPADirectLambdaToPV = -999.f; float dcaXYOmegaToPV = -999.f; - float dcaZOmegaToPV = -999.f; float dcaXYDirectLambdaToPV = -999.f; - float dcaZDirectLambdaToPV = -999.f; float dcaXYDirectKaonToPV = -999.f; - float dcaZDirectKaonToPV = -999.f; + float dcaXYOmegaKaonToPV = -999.f; + float dcaXYOmegaProtonToPV = -999.f; + float dcaXYOmegaPionToPV = -999.f; + float dcaXYDirectProtonToPV = -999.f; + float dcaXYDirectPionToPV = -999.f; float tpcNSigmaDirectKaon = -999.f; float tofNSigmaDirectKaon = -999.f; uint32_t daughterDetectorMap = 0; @@ -122,6 +127,8 @@ struct BuiltLambdaCandidate { int64_t posTrackId = -1; int64_t negTrackId = -1; uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; }; struct BuiltOmegaCandidate { @@ -139,6 +146,9 @@ struct BuiltOmegaCandidate { int64_t bachelorId = -1; o2::track::TrackParCov parentTrack; uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. + float dcaXYKaonToPV = -999.f; + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; }; struct doubleOmegaTreeCreator { @@ -155,11 +165,12 @@ struct doubleOmegaTreeCreator { kFindable, kFindableSelectedTracks, kFindableCascadeAndV0, + kFindableCascadeAndV0SelectedTracks, kNFindabilitySteps }; std::vector reconstructedDoubleOmegaIds; - Preslice tracksIUPerCollision = aod::track::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; Preslice rawCascadesPerCollision = aod::cascade::collisionId; Preslice rawV0sPerCollision = aod::v0::collisionId; @@ -364,6 +375,7 @@ struct doubleOmegaTreeCreator { generatedAndFindable->GetXaxis()->SetBinLabel(kFindable + 1, "Findable"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableSelectedTracks + 1, "Findable, selected tracks"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0 + 1, "Findable cascade + V0"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0SelectedTracks + 1, "Findable cascade + V0, selected tracks"); } template @@ -386,8 +398,24 @@ struct doubleOmegaTreeCreator { return std::sqrt(std::max(0.f, massSquared)); } - template - bool buildLambda(Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) + // Transverse DCA of a track to the collision primary vertex, computed by propagating + // a copy of the track parameters. Returns false if the propagation fails. + template + bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const + { + auto trackParCov = getTrackParCov(track); + const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; + const auto matCorr = static_cast(cfgMaterialCorrection.value); + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { + return false; + } + dcaXY = dca[0]; + return true; + } + + template + bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) { if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || !selectTrack(posTrack) || !selectTrack(negTrack)) { @@ -397,9 +425,17 @@ struct doubleOmegaTreeCreator { const bool isMatter = charge < 0; const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); - if (std::abs(protonNSigma) > nSigmaTPCCut || std::abs(pionNSigma) > nSigmaTPCCut || - std::abs(posTrack.dcaXY()) < dcaV0DauToPV || std::abs(negTrack.dcaXY()) < dcaV0DauToPV) { - LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", posTrack.dcaXY(): " << posTrack.dcaXY() << ", negTrack.dcaXY()" << negTrack.dcaXY(); + if (std::abs(protonNSigma) > nSigmaTPCCut || std::abs(pionNSigma) > nSigmaTPCCut) { + return false; + } + float dcaXYPos = -999.f; + float dcaXYNeg = -999.f; + if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { + LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (std::abs(dcaXYPos) < dcaV0DauToPV || std::abs(dcaXYNeg) < dcaV0DauToPV) { + LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; return false; } @@ -451,21 +487,26 @@ struct doubleOmegaTreeCreator { // Keep particle-role ordering for matter and antimatter alike. builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) : detectorMap(negTrack) | (detectorMap(posTrack) << 3); + builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; + builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; return true; } - template - bool buildOmega(Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) + template + bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) { if (bachelor.sign() == 0 || !selectTrack(bachelor) || - std::abs(bachelor.tpcNSigmaKa()) > nSigmaTPCCut || - std::abs(bachelor.dcaXY()) < dcaBachToPV) { + std::abs(bachelor.tpcNSigmaKa()) > nSigmaTPCCut) { + return false; + } + float dcaXYBachelor = -999.f; + if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < dcaBachToPV) { return false; } BuiltLambdaCandidate lambda; - if (!buildLambda(posTrack, negTrack, bachelor.sign(), lambda)) { + if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { return false; } @@ -519,7 +560,10 @@ struct doubleOmegaTreeCreator { negTrack.globalIndex(), bachelor.globalIndex(), fitter.createParentTrackParCov(), - detectorMap(bachelor) | (lambda.daughterDetectorMap << 3)}; + detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), + dcaXYBachelor, + lambda.dcaXYProtonToPV, + lambda.dcaXYPionToPV}; return true; } @@ -597,12 +641,17 @@ struct doubleOmegaTreeCreator { cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); + // PV pointing angles use the daughter momenta at their own decay vertices. + cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); + cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); cand.dcaXYOmegaToPV = dcaOmega[0]; - cand.dcaZOmegaToPV = dcaOmega[1]; cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; - cand.dcaZDirectLambdaToPV = dcaDirectLambda[1]; cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; - cand.dcaZDirectKaonToPV = dcaDirectKaon[1]; + cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; + cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; + cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; + cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; + cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; cand.tpcNSigmaDirectKaon = kaon.hasTPC() ? kaon.tpcNSigmaKa() : -999.f; cand.tofNSigmaDirectKaon = kaon.hasTOF() ? kaon.tofNSigmaKa() : -999.f; cand.daughterDetectorMap = omega.daughterDetectorMap | @@ -811,12 +860,16 @@ struct doubleOmegaTreeCreator { cand.cosPAOmega, cand.cosPADirectLambda, cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, cand.dcaXYOmegaToPV, - cand.dcaZOmegaToPV, cand.dcaXYDirectLambdaToPV, - cand.dcaZDirectLambdaToPV, cand.dcaXYDirectKaonToPV, - cand.dcaZDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, cand.tpcNSigmaDirectKaon, cand.tofNSigmaDirectKaon, cand.daughterDetectorMap, @@ -839,12 +892,16 @@ struct doubleOmegaTreeCreator { cand.cosPAOmega, cand.cosPADirectLambda, cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, cand.dcaXYOmegaToPV, - cand.dcaZOmegaToPV, cand.dcaXYDirectLambdaToPV, - cand.dcaZDirectLambdaToPV, cand.dcaXYDirectKaonToPV, - cand.dcaZDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, cand.tpcNSigmaDirectKaon, cand.tofNSigmaDirectKaon, cand.daughterDetectorMap, @@ -891,7 +948,7 @@ struct doubleOmegaTreeCreator { const auto omegaNegTrack = omegaV0.template negTrack_as(); const auto omegaBachelorTrack = rawCascade.template bachelor_as(); BuiltOmegaCandidate omega; - if (!buildOmega(omegaPosTrack, omegaNegTrack, omegaBachelorTrack, omega)) { + if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, omegaBachelorTrack, omega)) { continue; } const float omegaPt = std::hypot(omega.px, omega.py); @@ -903,7 +960,7 @@ struct doubleOmegaTreeCreator { const auto lambdaPosTrack = rawV0.template posTrack_as(); const auto lambdaNegTrack = rawV0.template negTrack_as(); BuiltLambdaCandidate lambda; - if (!buildLambda(lambdaPosTrack, lambdaNegTrack, omega.sign, lambda)) { + if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, omega.sign, lambda)) { continue; } if (std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || @@ -948,7 +1005,7 @@ struct doubleOmegaTreeCreator { { for (const auto& omegaRow : cascades) { BuiltOmegaCandidate omega; - if (!buildOmega(omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { + if (!buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { continue; } const float omegaPt = std::hypot(omega.px, omega.py); @@ -980,7 +1037,7 @@ struct doubleOmegaTreeCreator { continue; } BuiltLambdaCandidate lambda; - if (!buildLambda(lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { + if (!buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { continue; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); @@ -1029,14 +1086,14 @@ struct doubleOmegaTreeCreator { initCCDB(bc); BuiltOmegaCandidate omega; - if (!buildOmega(omegaPosTrack, omegaNegTrack, bachelor, omega)) { + if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { return false; } const float omegaPt = std::hypot(omega.px, omega.py); histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); BuiltLambdaCandidate lambda; - if (!buildLambda(lambdaPosTrack, lambdaNegTrack, charge, lambda)) { + if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { return false; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); @@ -1070,7 +1127,7 @@ struct doubleOmegaTreeCreator { } void processData(Collisions const& collision, - TracksFullIU const& tracks, + TracksFull const& tracks, aod::V0s const& v0s, aod::Cascades const& cascades, aod::BCsWithTimestamps const& bcs) @@ -1093,7 +1150,7 @@ struct doubleOmegaTreeCreator { PROCESS_SWITCH(doubleOmegaTreeCreator, processDataFromCascades, "Reconstruct Omega + Lambda + kaon from two cascade rows", false); void processMC(CollisionsMC const& collisions, - TracksFullIUMC const& tracks, + TracksFullMC const& tracks, aod::V0s const& v0s, aod::Cascades const& cascades, aod::McParticles const& mcParticles, @@ -1107,7 +1164,7 @@ struct doubleOmegaTreeCreator { if (!acceptCollision(collision, bcs)) { continue; } - auto tracksThisCollision = tracks.sliceBy(tracksIUPerCollision, collision.globalIndex()); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); auto cascadesThisCollision = cascades.sliceBy(rawCascadesPerCollision, collision.globalIndex()); auto v0sThisCollision = v0s.sliceBy(rawV0sPerCollision, collision.globalIndex()); v0sThisCollision.bindExternalIndices(&tracks); @@ -1130,7 +1187,7 @@ struct doubleOmegaTreeCreator { PROCESS_SWITCH(doubleOmegaTreeCreator, processMC, "Reconstruct and truth-match Omega + Lambda + kaon in MC", false); void processFindableTracks(soa::Join const& collisions, - TracksFullIUMC const& tracks, + TracksFullMC const& tracks, aod::Cascades const& cascades, aod::V0s const& v0s, aod::McParticles const& mcParticles, @@ -1226,24 +1283,24 @@ struct doubleOmegaTreeCreator { } } auto printDaughterTracks = [&](const char* tag) { - LOG(debug) << "---------------------------------------- " << tag; + LOG(info) << "---------------------------------------- " << tag; for (std::size_t iDaughter = 0; iDaughter < daughterTrackIds.size(); ++iDaughter) { - LOG(debug) << "+++++++"; + LOG(info) << "+++++++"; const auto daughterTrack = tracks.rawIteratorAt(daughterTrackIds[iDaughter]); const auto daughterMCParticle = daughterTrack.mcParticle_as(); - LOG(debug) << "Dau" << iDaughter + 1 - << ": hasITS = " << daughterTrack.hasITS() - << ", hasTPC = " << daughterTrack.hasTPC() - << ", hasTOF = " << daughterTrack.hasTOF() - << ", hasTRD = " << daughterTrack.hasTRD() - << ", isITSAfterburner = " << daughterTrack.isITSAfterburner(); - LOG(debug) << "Dau" << iDaughter + 1 - << ": eta = " << daughterTrack.eta() - << ", pt = " << daughterTrack.pt() - << ", CollisionID = " << daughterTrack.collisionId() - << ", production point = (" << daughterMCParticle.vx() - << ", " << daughterMCParticle.vy() - << ", " << daughterMCParticle.vz() << ") cm"; + LOG(info) << "Dau" << iDaughter + 1 + << ": hasITS = " << daughterTrack.hasITS() + << ", hasTPC = " << daughterTrack.hasTPC() + << ", hasTOF = " << daughterTrack.hasTOF() + << ", hasTRD = " << daughterTrack.hasTRD() + << ", isITSAfterburner = " << daughterTrack.isITSAfterburner(); + LOG(info) << "Dau" << iDaughter + 1 + << ": eta = " << daughterTrack.eta() + << ", pt = " << daughterTrack.pt() + << ", CollisionID = " << daughterTrack.collisionId() + << ", production point = (" << daughterMCParticle.vx() + << ", " << daughterMCParticle.vy() + << ", " << daughterMCParticle.vz() << ") cm"; } }; @@ -1256,6 +1313,7 @@ struct doubleOmegaTreeCreator { if (allDaughtersReconstructed) { histos.fill(HIST("MC/generatedAndFindable"), kFindable); bool omegaInCascade = false; + int64_t cascadeCollisionId = -1; for (const auto& cascade : cascades) { const auto cascadeV0 = cascade.v0(); const std::array cascadeTrackIds{ @@ -1269,11 +1327,13 @@ struct doubleOmegaTreeCreator { } if (allOmegaTracksInCascade) { omegaInCascade = true; + cascadeCollisionId = cascade.collisionId(); break; } } bool directLambdaInV0 = false; + int64_t directLambdaCollisionId = -1; for (const auto& v0 : v0s) { const std::array v0TrackIds{v0.posTrackId(), v0.negTrackId()}; bool allDirectLambdaTracksInV0 = true; @@ -1285,26 +1345,37 @@ struct doubleOmegaTreeCreator { } if (allDirectLambdaTracksInV0) { directLambdaInV0 = true; + directLambdaCollisionId = v0.collisionId(); break; } } if (omegaInCascade && directLambdaInV0) { histos.fill(HIST("MC/generatedAndFindable"), kFindableCascadeAndV0); - printDaughterTracks("findable cascade and V0"); - DoubleOmegaMCInfo mcInfo; - if (!getGeneratedMCInfo(mcInfo, mcParticle)) { - continue; + if (allDaughtersSelected) { + histos.fill(HIST("MC/generatedAndFindable"), kFindableCascadeAndV0SelectedTracks); } - const bool isReco = fillFromSelectedTracks(collisions, tracks, daughterTrackIds, mcInfo); - if (!isReco) { - writeMCCandidate(DoubleOmegaCandidate{}, mcInfo, false); + const auto directKaonTrack = tracks.rawIteratorAt(daughterTrackIds[5]); + LOG(info) << "Findable collision IDs: cascade = " << cascadeCollisionId + << ", direct Lambda = " << directLambdaCollisionId + << ", direct kaon = " << directKaonTrack.collisionId(); + if (cascadeCollisionId == directLambdaCollisionId && cascadeCollisionId == directKaonTrack.collisionId()) { + LOG(info) << "All findable daughter tracks are in the same collision."; } + printDaughterTracks("findable cascade and V0"); } } if (allDaughtersSelected) { histos.fill(HIST("MC/generatedAndFindable"), kFindableSelectedTracks); printDaughterTracks("findable selected tracks"); + DoubleOmegaMCInfo mcInfo; + if (!getGeneratedMCInfo(mcInfo, mcParticle)) { + continue; + } + const bool isReco = fillFromSelectedTracks(collisions, tracks, selectedTrackIds, mcInfo); + if (!isReco) { + writeMCCandidate(DoubleOmegaCandidate{}, mcInfo, false); + } } } } From 9193deab655db5c97e59f5003eed064ee46976df Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Sun, 20 Sep 2026 22:36:08 +0200 Subject: [PATCH 004/648] [PWGHF] Add B hadron selections in dataCreatorCharmHadPReduced (#17986) --- .../dataCreatorCharmHadPiReduced.cxx | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 6f429084a95..07f0dae9c3d 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -196,6 +196,11 @@ struct HfDataCreatorCharmHadPiReduced { Configurable> binsPtPion{"binsPtPion", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "track pT bin limits for pion DCA XY pT-dependent cut"}; Configurable> cutsTrackPionDCA{"cutsTrackPionDCA", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections per pT bin for pions"}; } trackPionConfigurations; + // B hadron selection + struct : o2::framework::ConfigurableGroup { + Configurable cpaMin{"cpaMin", 0., "Minimum cosine of pointing angle for B candidates"}; + Configurable decLenMin{"decLenMin", 0., "Minimum decay length for B candidates"}; + } bhadronConfigurations; // HF flags struct : o2::framework::ConfigurableGroup { Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for D+"}; @@ -272,7 +277,7 @@ struct HfDataCreatorCharmHadPiReduced { PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; } preslices; - std::shared_ptr hCandidatesD0, hCandidatesDPlus, hCandidatesDs, hCandidatesLc, hCandidatesD0FromDstar; + std::shared_ptr hCandidatesD0, hCandidatesDPlus, hCandidatesDs, hCandidatesLc, hCandidatesD0FromDstar, hCandidatesBHadron; HistogramRegistry registry{"registry"}; OutputObj zorroSummary{"zorroSummary"}; @@ -311,6 +316,15 @@ struct HfDataCreatorCharmHadPiReduced { invMass2ChHadPiMax = (massB + configs.invMassWindowCharmHadPi) * (massB + configs.invMassWindowCharmHadPi); // Initialize fitter + // D0 + B hadrons + df2.setPropagateToPCA(vertexConfigurations.propagateToPCA); + df2.setMaxR(vertexConfigurations.maxR); + df2.setMaxDZIni(vertexConfigurations.maxDZIni); + df2.setMinParamChange(vertexConfigurations.minParamChange); + df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); + df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); + df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df2.setMatCorrType(noMatCorr); if (doprocessDplusPiData || doprocessDplusPiDataWithMl || doprocessDplusPiDataWithQvec || doprocessDplusPiDataWithMlAndQvec || doprocessDplusPiMc || doprocessDplusPiMcWithMl || doprocessDsPiData || doprocessDsPiDataWithMl || doprocessDsPiDataWithQvec || doprocessDsPiDataWithMlAndQvec || doprocessDsPiMc || doprocessDsPiMcWithMl || doprocessLcPiData || doprocessLcPiDataWithMl || doprocessLcPiMc || doprocessLcPiMcWithMl) { @@ -322,16 +336,6 @@ struct HfDataCreatorCharmHadPiReduced { df3.setUseAbsDCA(vertexConfigurations.useAbsDCA); df3.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); df3.setMatCorrType(noMatCorr); - } else if (doprocessD0PiData || doprocessD0PiDataWithMl || doprocessD0PiDataWithQvec || doprocessD0PiDataWithMlAndQvec || doprocessD0PiMc || doprocessD0PiMcWithMl || - doprocessDstarPiData || doprocessDstarPiDataWithMl || doprocessDstarPiDataWithQvec || doprocessDstarPiDataWithMlAndQvec || doprocessDstarPiMc || doprocessDstarPiMcWithMl) { - df2.setPropagateToPCA(vertexConfigurations.propagateToPCA); - df2.setMaxR(vertexConfigurations.maxR); - df2.setMaxDZIni(vertexConfigurations.maxDZIni); - df2.setMinParamChange(vertexConfigurations.minParamChange); - df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); - df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); - df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); - df2.setMatCorrType(noMatCorr); } // Configure CCDB access @@ -389,12 +393,14 @@ struct HfDataCreatorCharmHadPiReduced { hCandidatesDs = registry.add("hCandidatesDs", "Ds candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesLc = registry.add("hCandidatesLc", "Lc candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesD0FromDstar = registry.add("hCandidatesD0FromDstar", "D0 from D* candidate counter", {HistType::kTH1D, {axisCands}}); + hCandidatesBHadron = registry.add("hCandidatesBHadron", "B hadron candidate counter", {HistType::kTH1D, {axisCands}}); setLabelHistoCands(hCandidatesD0); setLabelHistoCands(hCandidatesDPlus); setLabelHistoCands(hCandidatesDs); setLabelHistoCands(hCandidatesLc); setLabelHistoCands(hCandidatesD0FromDstar); + setLabelHistoCands(hCandidatesBHadron); // init HF event selection helper hfEvSel.init(registry, &zorroSummary); @@ -1017,6 +1023,26 @@ struct HfDataCreatorCharmHadPiReduced { } } + /// B hadron preselections + /// \param momentum is the B meson momentum + /// \param secondaryVertex is the reconstructed secondary vertex + /// \param collision is the reconstructed collision + template + bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision) + { + // B candidate CPA + if (RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, momentum) < bhadronConfigurations.cpaMin) { + return false; + } + + // B candidate decay length + if (RecoDecay::distance(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex) < bhadronConfigurations.decLenMin) { + return false; + } + + return true; + } + template void runDataCreation(Coll const& collision, CCharmCands const& candsC, @@ -1307,12 +1333,33 @@ struct HfDataCreatorCharmHadPiReduced { } registry.fill(HIST("hPtPion"), trackParCovPion.getPt()); + // compute invariant mass square and apply selection auto invMass2DPi = RecoDecay::m2(std::array{pVecCharm, pVecPion}, std::array{massC, MassPiPlus}); if ((invMass2DPi < invMass2ChHadPiMin) || (invMass2DPi > invMass2ChHadPiMax)) { continue; } + std::array pVecBHad{}, pVecDHad{}, pVecDau{}; + + hCandidatesBHadron->Fill(SVFitting::BeforeFit); + try { + if (df2.process(trackParCovCharmHad, trackParCovPion) == 0) { + continue; + } + } catch (const std::runtime_error& error) { + LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; + hCandidatesBHadron->Fill(SVFitting::Fail); + continue; + } + hCandidatesBHadron->Fill(SVFitting::FitOk); + auto secondaryVertexBHad = df2.getPCACandidate(); + df2.getTrack(0).getPxPyPzGlo(pVecDHad); + df2.getTrack(1).getPxPyPzGlo(pVecDau); + pVecBHad = RecoDecay::pVec(pVecDHad, pVecDau); + if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision)) { + continue; + } // fill Pion tracks table // if information on track already stored, go to next track if (!selectedTracksPion.count(trackPion.globalIndex())) { From 2d750029165897c50e5db1f5615fbcb6d0c5831f Mon Sep 17 00:00:00 2001 From: Stefanie Mrozinski <63045530+Steffimro@users.noreply.github.com> Date: Sun, 20 Sep 2026 23:38:14 +0200 Subject: [PATCH 005/648] [PWGEM] add new pairQC Task + clean up photonhbt (#17984) Co-authored-by: Stefanie Mrozinski --- PWGEM/PhotonMeson/Tasks/CMakeLists.txt | 5 + PWGEM/PhotonMeson/Tasks/pairQcTask.cxx | 1460 +++++++++++++++++++++++ PWGEM/PhotonMeson/Utils/AnalyticV0.h | 341 ++++++ PWGEM/PhotonMeson/Utils/MCUtilities.h | 533 +++++++++ PWGEM/PhotonMeson/Utils/PairUtilities.h | 235 ++++ 5 files changed, 2574 insertions(+) create mode 100644 PWGEM/PhotonMeson/Tasks/pairQcTask.cxx create mode 100644 PWGEM/PhotonMeson/Utils/AnalyticV0.h diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index b6b0cf34469..729346bab9e 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -200,3 +200,8 @@ o2physics_add_dpl_workflow(event-normalization-task SOURCES eventNormalizationTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pair-qc-task + SOURCES pairQcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx new file mode 100644 index 00000000000..d2d05d32ae5 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx @@ -0,0 +1,1460 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pairQCTask.cxx +/// \brief This task looks at the pair of V0 candidates. +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/AnalyticV0.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep +#include +#include // IWYU pragma: keep +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::pwgem::photonmeson; +namespace mcutil = o2::aod::pwgem::photonmeson::utils::mcutil; +namespace pairutil = o2::aod::pwgem::photonmeson::utils::pairutil; + +// ─── Event Information Tables ──────────────────────────────────────────── + +using MyCollisions = soa::Join; +using MyCollisionsMC = soa::Join; + +// ─── Photon Tables ──────────────────────────────────────────── + +using MyV0Photons = soa::Join; + +// ─── Assoc. Track Tables ──────────────────────────────────────────── + +using MyMCV0Legs = soa::Join; + +struct PairQCTask { + + static constexpr float KMinMagnitude = 1e-12f; + static constexpr float KMinCosine = 1e-12f; + static constexpr float KMinSigma = 1e-9f; + static constexpr float KMe = 0.000510999f; // electron mass, GeV/c^2 + + struct LegLite { + float pt{0.f}, eta{0.f}, phi{0.f}; + float nsigEl{0.f}, dcaZ{0.f}; + float nClsFindable{0.f}, nClsITS{0.f}; + bool timed{false}; // ITS, TRD or TOF: the track has its own time + bool foreign{false}; + int64_t trackId{-1}; + }; + + struct PhotonCand { + int64_t gi{-1}; + float fPt{0.f}, fEta{0.f}, fPhi{0.f}; + float fVx{0.f}, fVy{0.f}, fVz{0.f}; // conversion point, global (cm) + float fVtxZ{0.f}; // primary vertex z of the collision + float fDcaXYToPV{0.f}, fDcaZToPV{0.f}; + float psipair{0.f}, phiv{0.f}, chi2{0.f}, cospa{0.f}, pca{0.f}; + std::array leg{}; // 0 = e+, 1 = e- + pairutil::V0PhotonLegCounts legCounts{}; + pairutil::DedupCand dedupCand{}; + bool dedupRejected{false}; + AnalyticV0 own{}; // the photon rebuilt from its own legs (WP-B, WP-F) + mcutil::PhotonMCInfo mc{}; + [[nodiscard]] float pt() const { return fPt; } + [[nodiscard]] float eta() const { return fEta; } + [[nodiscard]] float phi() const { return fPhi; } + [[nodiscard]] float vx() const { return fVx; } + [[nodiscard]] float vy() const { return fVy; } + [[nodiscard]] float vz() const { return fVz; } + [[nodiscard]] float rConv() const { return std::hypot(fVx, fVy); } + [[nodiscard]] int nTimedLegs() const { return (leg[0].timed ? 1 : 0) + (leg[1].timed ? 1 : 0); } + [[nodiscard]] int nForeignLegs() const { return (leg[0].foreign ? 1 : 0) + (leg[1].foreign ? 1 : 0); } + [[nodiscard]] bool sharesTrackWith(PhotonCand const& o) const + { + return leg[0].trackId == o.leg[0].trackId || leg[0].trackId == o.leg[1].trackId || + leg[1].trackId == o.leg[0].trackId || leg[1].trackId == o.leg[1].trackId; + } + [[nodiscard]] LegParam legParam(int il) const + { + LegParam l; + l.xyz = {fVx, fVy, fVz - fVtxZ}; + const float ptL = leg[static_cast(il)].pt; + l.mom = {ptL * std::cos(leg[static_cast(il)].phi), ptL * std::sin(leg[static_cast(il)].phi), ptL * std::sinh(leg[static_cast(il)].eta)}; + l.charge = (il == 0) ? +1 : -1; + return l; + } + }; + + // Pair observables from the two conversion points and momenta. + struct PairObs { + ROOT::Math::PtEtaPhiMVector v1, v2; + float deltaR{0.f}, deltaZ{0.f}, deltaR3D{0.f}; + float opa{0.f}, drOverCosOA{0.f}; + float deta{0.f}, dphi{0.f}; + pairutil::PairQ q{}; + bool valid{true}; + }; + + struct CrossObs { + std::array fit{}; + std::array ana{}; + int nAltValidFit{0}, nAltValidAna{0}; + float maxDcaFit{999.f}, maxDcaXYFit{999.f}, maxDcaZFit{999.f}; + float maxDcaAna{999.f}, maxDcaXYAna{999.f}, maxDcaZAna{999.f}; + std::array mee{999.f, 999.f}; + float meeOverQ{999.f}; + std::array ownMee{999.f, 999.f}; + float ownMeeMax{999.f}, deltaMee{-999.f}, m4{999.f}; + bool evaluated{false}; + }; + + Service ccdb{}; + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable cfgBzOverrideT{"cfgBzOverrideT", -999.f, "Bz in Tesla; used instead of CCDB if > -100"}; + + struct : ConfigurableGroup { + std::string prefix = "eventcut_group"; + Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require no TF border"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", true, "require no ITS ROF border"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx FT0 vs PV"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2.f, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000.f, "max. FT0C occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "no coll in time range std"}; + Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "no coll in time range strict"}; + Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "no coll in ITS ROF std"}; + Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "no coll in ITS ROF strict"}; + Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "no HM coll in prev ROF"}; + Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "ITS layer 3 OK"}; + Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "ITS layers 0-3 OK"}; + Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "all ITS layers OK"}; + } eventcuts; + + struct : ConfigurableGroup { + std::string prefix = "pcmcut_group"; + Configurable cfgRequireV0WithITSTPC{"cfgRequireV0WithITSTPC", false, "select V0s with ITS-TPC tracks"}; + Configurable cfgRequireV0WithITSOnly{"cfgRequireV0WithITSOnly", false, "select V0s with ITS-only tracks"}; + Configurable cfgRequireV0WithTPCOnly{"cfgRequireV0WithTPCOnly", false, "select V0s with TPC-only tracks"}; + Configurable cfgMinPtV0{"cfgMinPtV0", 0.1, "min pT for V0 photons at PV"}; + Configurable cfgMaxEtaV0{"cfgMaxEtaV0", 0.8, "max eta for V0 photons at PV"}; + Configurable cfgMinV0Radius{"cfgMinV0Radius", 16.0, "min V0 radius"}; + Configurable cfgMaxV0Radius{"cfgMaxV0Radius", 90.0, "max V0 radius"}; + Configurable cfgMaxAlphaAP{"cfgMaxAlphaAP", 0.95, "max alpha for AP cut"}; + Configurable cfgMaxQtAP{"cfgMaxQtAP", 0.01, "max qT for AP cut"}; + Configurable cfgMinCosPA{"cfgMinCosPA", 0.997, "min V0 CosPA"}; + Configurable cfgMaxPCA{"cfgMaxPCA", 3.0, "max distance between 2 legs"}; + Configurable cfgMaxChi2KF{"cfgMaxChi2KF", 1e+10, "max chi2/ndf with KF"}; + Configurable cfgRejectV0OnITSIB{"cfgRejectV0OnITSIB", true, "reject V0s on ITSib"}; + Configurable cfgDisableITSOnlyTrack{"cfgDisableITSOnlyTrack", false, "disable ITS-only tracks"}; + Configurable cfgDisableTPCOnlyTrack{"cfgDisableTPCOnlyTrack", false, "disable TPC-only tracks"}; + Configurable cfgMinNClusterTPC{"cfgMinNClusterTPC", 70, "min ncluster TPC"}; + Configurable cfgMinNCrossedRows{"cfgMinNCrossedRows", 70, "min crossed rows"}; + Configurable cfgMaxFracSharedClustersTPC{"cfgMaxFracSharedClustersTPC", 999.f, "max fraction of shared TPC clusters"}; + Configurable cfgMaxChi2TPC{"cfgMaxChi2TPC", 4.0, "max chi2/NclsTPC"}; + Configurable cfgMaxChi2ITS{"cfgMaxChi2ITS", 36.0, "max chi2/NclsITS"}; + Configurable cfgMinTPCNsigmaEl{"cfgMinTPCNsigmaEl", -3.5, "min TPC nsigma electron"}; + Configurable cfgMaxTPCNsigmaEl{"cfgMaxTPCNsigmaEl", +3.5, "max TPC nsigma electron"}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "centralitySelection_group"; + Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; + Configurable cfgCentMax{"cfgCentMax", 999, "max. centrality"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + } centralitySelection; + + struct : ConfigurableGroup { + std::string prefix = "ggpaircut_group"; + Configurable cfgMinDRCosOA{"cfgMinDRCosOA", -1.f, "min. dr/cosOA; <0 = disabled"}; + Configurable cfgDoRCut{"cfgDoRCut", false, "apply |R1-R2| > cfgMinDeltaR cut"}; + Configurable cfgMinDeltaR{"cfgMinDeltaR", 0.f, "minimum |R1-R2| (cm)"}; + Configurable cfgDoZCut{"cfgDoZCut", false, "apply |DeltaZ| > cfgMinDeltaZ cut"}; + Configurable cfgMinDeltaZ{"cfgMinDeltaZ", 0.f, "minimum |DeltaZ| (cm)"}; + Configurable cfgDoEllipseCut{"cfgDoEllipseCut", false, "reject pairs inside ellipse in DeltaEta-DeltaPhi"}; + Configurable cfgEllipseSigEta{"cfgEllipseSigEta", 0.1f, "sigma_eta for ellipse cut"}; + Configurable cfgEllipseSigPhi{"cfgEllipseSigPhi", 0.1f, "sigma_phi for ellipse cut"}; + Configurable cfgEllipseR2{"cfgEllipseR2", 1.0f, "R^2 threshold: reject if ellipse value < R^2"}; + Configurable cfgMaxAsymmetry{"cfgMaxAsymmetry", -1.f, "max |p_{T, 1} - p_{T, 2}|/(p_{T, 1} + p_{T, 2}) asymmetry cut"}; + Configurable cfgMaxDcaZToPV{"cfgMaxDcaZToPV", 999.f, "max |DCAz| of both V0 photons to the primary vertex (cm); 999 = off"}; + Configurable cfgMaxDcaXYToPV{"cfgMaxDcaXYToPV", 999.f, "max |DCAxy| of both V0 photons to the primary vertex (cm); 999 = off"}; + } ggpaircuts; + + struct : ConfigurableGroup { + std::string prefix = "dedup_group"; + Configurable cfgDoDedup{"cfgDoDedup", false, "REMOVE duplicate-like candidates before pairing (as photonhbt would); the QA is filled either way"}; + Configurable cfgDupMaxLegDEta{"cfgDupMaxLegDEta", 0.01f, "leg identical if |dEta| below this"}; + Configurable cfgDupMaxLegDPhi{"cfgDupMaxLegDPhi", 0.01f, "leg identical if |dPhi| below this (rad)"}; + Configurable cfgDupMaxLegPtAsym{"cfgDupMaxLegPtAsym", 0.05f, "leg identical if |pt1-pt2|/(pt1+pt2) below this"}; + Configurable cfgDupMaxLegDeDxAsym{"cfgDupMaxLegDeDxAsym", 0.05f, "leg identical if |dEdx1-dEdx2|/(dEdx1+dEdx2) below this; skipped if a leg has no TPC"}; + Configurable cfgDupRequireBothLegs{"cfgDupRequireBothLegs", false, "true: both same-charge leg pairs must be identical; false: one is enough"}; + Configurable cfgDupMaxDVtx3D{"cfgDupMaxDVtx3D", 999.f, "additionally require the 3D conversion-point distance below this (cm); 999 = off"}; + } dedup; + + struct : ConfigurableGroup { + std::string prefix = "crosspair_group"; + Configurable cfgDoCrossPairCut{"cfgDoCrossPairCut", false, "apply the crossed-pair veto (DCAFitter alternatives) as the last pair cut, as photonhbt does"}; + Configurable cfgCrossVetoMode{"cfgCrossVetoMode", 1, "veto criterion: 1 = geometric AND-chain (cfgAlt*), 2 = maxAltDca alone (cfgCrossMinMaxAltDca), 3 = 1 OR 2"}; + Configurable cfgCrossMinMaxAltDca{"cfgCrossMinMaxAltDca", 4.9f, "veto mode 2/3: reject when maxAltDca is BELOW this (cm); 0 disables"}; + Configurable cfgAltMaxQinv{"cfgAltMaxQinv", 0.15f, "evaluate the alternative pairings only below this q_inv"}; + Configurable cfgAltMaxDca{"cfgAltMaxDca", 1.5f, "alternative pairing photon-like if its legs approach closer than this at their PCA (cm) ..."}; + Configurable cfgAltMinR{"cfgAltMinR", 1.f, "... and the PCA lies at a radius above this (cm) ..."}; + Configurable cfgAltMaxR{"cfgAltMaxR", 90.f, "... and below this (cm) ..."}; + Configurable cfgAltMinCosPA{"cfgAltMinCosPA", 0.98f, "... and cosPA above this ..."}; + Configurable cfgAltMaxDcaXY{"cfgAltMaxDcaXY", 3.f, "... and DCAxy of the photon line to the vertex below this (cm) ..."}; + Configurable cfgAltMaxDcaZ{"cfgAltMaxDcaZ", 999.f, "... and DCAz below this (cm); 999 = not used"}; + Configurable cfgAltMaxMee{"cfgAltMaxMee", 0.1f, "... and its m_ee below this (GeV/c^2)"}; + Configurable cfgAltRequireBoth{"cfgAltRequireBoth", true, "true: veto only if BOTH alternative pairings are photon-like; false: one is enough"}; + } crosspair; + + struct : ConfigurableGroup { + std::string prefix = "qaflags_group"; + Configurable cfgMaxQinvForQA{"cfgMaxQinvForQA", 0.3f, "pair QA only below this q_inv (GeV/c); the cheap gate runs before any boost"}; + Configurable cfgMaxQinvForMCQA{"cfgMaxQinvForMCQA", 0.3f, "MC pair diagnostics (ancestry, true conversions) only below this q_inv"}; + Configurable cfgDoCrossPairQA{"cfgDoCrossPairQA", true, "WP-F: evaluate the alternative pairings (two DCAFitter fits AND two analytic builds per pair below cfgAltMaxQinv) and fill Pair/CrossPair/*"}; + Configurable cfgDoAnalyticV0QA{"cfgDoAnalyticV0QA", true, "WP-B: rebuild every selected photon from its own legs and fill the residuals against the builder (Photon/AnalyticV0/*)"}; + Configurable cfgDoAncestry{"cfgDoAncestry", true, "WP-E (MC): census of the four legs, common ancestor, true conversion points (Pair/MC/Ancestry/*)"}; + Configurable cfgAncestorMaxGen{"cfgAncestorMaxGen", 4, "how many MC generations to walk up when looking for a common ancestor"}; + Configurable cfgFillLegSimilarity{"cfgFillLegSimilarity", true, "like-sign leg similarity sparse (duplicate diagnostic)"}; + Configurable cfgFillTypeSparses{"cfgFillTypeSparses", true, "MC: per truth type also the (dEta, dPhi, q_inv) and (dR, dZ, q_inv) sparses (large)"}; + Configurable cfgScoreWeight{"cfgScoreWeight", 0.5f, "builder score weight w for the analytic V0 (as mixing_group.cfgScoreWeight of photonhbt)"}; + Configurable cfgMaxQinvForSparses{"cfgMaxQinvForSparses", 0.1f, "the sparses with 4 or more axes carry the COARSE q axis (confQBinsCoarse) and are filled only below this q_inv; keep it equal to the upper edge of confQBinsCoarse"}; + } qaflags; + + ConfigurableAxis confQBins{"confQBins", {60, 0, +0.3f}, "q bins"}; + ConfigurableAxis confQBinsCoarse{"confQBinsCoarse", {VARIABLE_WIDTH, 0.0, 0.01, 0.02, 0.03, 0.05, 0.1}, "coarse q bins for the sparses with 4 or more axes (merging size)"}; + ConfigurableAxis confKtBins{"confKtBins", {VARIABLE_WIDTH, 0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75}, "kT bins"}; + + AxisSpec axisQinv{confQBins, "q_{inv} (GeV/c)"}; + AxisSpec axisQinvCoarse{confQBinsCoarse, "q_{inv} (GeV/c), coarse"}; + AxisSpec axisKt{confKtBins, "k_{T} (GeV/c)"}; + AxisSpec axisDeltaEta{90, -1.6f, +1.6f, "#Delta#eta"}; + AxisSpec axisDeltaPhi{90, -o2::constants::math::PI, +o2::constants::math::PI, "#Delta#phi (rad)"}; + AxisSpec axisDeltaR{90, 0.f, 30.f, "|R_{1}-R_{2}| (cm)"}; + AxisSpec axisDeltaZ{200, -100.f, 100.f, "#Delta z (cm)"}; + AxisSpec axisDeltaR3D{100, 0.f, 100.f, "|#vec{r}_{1}-#vec{r}_{2}| (cm)"}; + AxisSpec axisTruthType{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, "truth type (1=TrueTrueDistinct,2=TrueTrueSamePhoton,3=SharedMcLeg,4=TrueFake,5=FakeFake,6=Pi0Daughters)"}; + AxisSpec axisMethod{2, -0.5f, 1.5f, "0 = DCAFitter, 1 = analytic"}; + [[nodiscard]] static AxisSpec makeAxisEnum(int nClasses, const char* title) + { + return AxisSpec{nClasses, -0.5f, static_cast(nClasses) - 0.5f, title}; + } + + static constexpr int kNPhotonSources = 6; + static constexpr const char* kPhotonSourceTitle = "parent photon source (0 direct, 1 #pi^{0}, 2 #eta, 3 other hadron, 4 electron = bremsstrahlung, 5 no photon parent)"; + template + [[nodiscard]] static int photonSource(TMCParticles const& mcParticles, int photonId) + { + if (photonId < 0) { + return 5; // o2-linter: disable=magic-number (source code) + } + const auto photon = mcParticles.iteratorAt(photonId); + if (!photon.has_mothers()) { + return 0; + } + const int motherPdg = std::abs(mcParticles.iteratorAt(photon.mothersIds()[0]).pdgCode()); + if (motherPdg == PDG_t::kPi0) { + return 1; + } + if (motherPdg == o2::constants::physics::Pdg::kEta) { + return 2; // o2-linter: disable=magic-number (source code) + } + if (motherPdg == PDG_t::kElectron) { + return 4; // o2-linter: disable=magic-number (source code) + } + return 3; // o2-linter: disable=magic-number (source code) + } + + // Track-class combination of the two legs of a V0, unordered: + // 0 ITSTPC+ITSTPC, 1 ITSTPC+ITSonly, 2 ITSTPC+TPConly, 3 ITSonly+ITSonly, 4 ITSonly+TPConly, 5 TPConly+TPConly + static constexpr int kNV0Classes = 6; + static constexpr const char* kV0ClassTitle = "V0 leg classes (0 ITSTPC+ITSTPC, 1 ITSTPC+ITSonly, 2 ITSTPC+TPConly, 3 ITSonly+ITSonly, 4 ITSonly+TPConly, 5 TPConly+TPConly)"; + [[nodiscard]] static int v0LegClass(pairutil::V0PhotonLegCounts const& c) + { + if (c.nITSTPC == 2) { // o2-linter: disable=magic-number (two legs) + return 0; + } + if (c.nITSTPC == 1) { + return (c.nITSOnly == 1) ? 1 : 2; // o2-linter: disable=magic-number (class code) + } + if (c.nITSOnly == 2) { // o2-linter: disable=magic-number (two legs) + return 3; // o2-linter: disable=magic-number (class code) + } + return (c.nITSOnly == 1) ? 4 : 5; // o2-linter: disable=magic-number (class code) + } + + HistogramRegistry fRegistry{"pairQC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + EMPhotonEventCut fEMEventCut; + V0PhotonCut fV0PhotonCut; + bool isMC = false; + int mRunNumber{0}; + float mBzT{0.f}; + PhotonLikeCuts mAltCuts{}; + pairutil::DedupConfig mDedupCfg{}; + + SliceCache cache; + Preslice perCollisionPCM = aod::v0photonkf::pmeventId; + + Filter collisionFilterCentrality = + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < centralitySelection.cfgCentMax); + Filter collisionFilterOccupancyTrack = + eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && + o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + Filter collisionFilterOccupancyFT0c = + eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && + o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + using FilteredMyCollisions = soa::Filtered; + using FilteredMyMCCollisions = soa::Filtered; + + void init(InitContext& context) + { + isMC = context.mOptions.get("processMC"); + defineEMEventCut(); + definePCMCut(); + mAltCuts.maxDca = crosspair.cfgAltMaxDca.value; + mAltCuts.minR = crosspair.cfgAltMinR.value; + mAltCuts.maxR = crosspair.cfgAltMaxR.value; + mAltCuts.minCosPA = crosspair.cfgAltMinCosPA.value; + mAltCuts.maxMee = crosspair.cfgAltMaxMee.value; + mAltCuts.maxDcaXY = crosspair.cfgAltMaxDcaXY.value; + mAltCuts.maxDcaZ = crosspair.cfgAltMaxDcaZ.value; + mDedupCfg.maxLegDEta = dedup.cfgDupMaxLegDEta.value; + mDedupCfg.maxLegDPhi = dedup.cfgDupMaxLegDPhi.value; + mDedupCfg.maxLegPtAsym = dedup.cfgDupMaxLegPtAsym.value; + mDedupCfg.maxLegDeDxAsym = dedup.cfgDupMaxLegDeDxAsym.value; + mDedupCfg.maxDVtx3D = dedup.cfgDupMaxDVtx3D.value; + mDedupCfg.requireBothLegs = dedup.cfgDupRequireBothLegs.value; + ccdb->setURL(cfgCcdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fRegistry.add("Photon/hPtEtaPhi", "selected V0 photons;p_{T} (GeV/c);#eta;#varphi (rad)", kTH3F, {{100, 0.f, 2.f}, {40, -0.8f, 0.8f}, {72, 0.f, 6.2832f}}, true); // o2-linter: disable=magic-number (axis definition) + fRegistry.add("Photon/hNPhotonsPerEvent", "selected V0 photons per event;N_{#gamma};events", kTH1F, {{21, -0.5f, 20.5f}}, true); + fRegistry.add("Photon/hV0LegClass", "leg classes of the selected V0s;;V0s", kTH1F, {makeAxisEnum(kNV0Classes, kV0ClassTitle)}, true); + fRegistry.add("Photon/hV0LegClass_vs_Pt", "leg classes of the selected V0s vs p_{T};V0 leg class;p_{T} (GeV/c)", kTH2F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {40, 0.f, 2.f}}, true); + fRegistry.add("Photon/hV0LegClass_vs_R", "leg classes of the selected V0s vs R_{conv};V0 leg class;R_{conv} (cm)", kTH2F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {50, 0.f, 100.f}}, true); + addAnalyticV0Histograms(); + addPairQAHistograms(); + addDedupHistograms(); + addCrossPairHistograms(); + if (isMC) { + addPairTruthHistograms(); // WP-C + addAncestryHistograms(); // WP-E + } + LOGF(info, "pairQC: isMC = %d, pair QA below q_inv = %.2f, cross-pair QA %s (below %.3f), analytic V0 QA %s, ancestry %s, dedup %s", + isMC, qaflags.cfgMaxQinvForQA.value, qaflags.cfgDoCrossPairQA.value ? "on" : "off", crosspair.cfgAltMaxQinv.value, qaflags.cfgDoAnalyticV0QA.value ? "on" : "off", qaflags.cfgDoAncestry.value ? "on" : "off", dedup.cfgDoDedup.value ? "ON" : "off"); + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + if (cfgBzOverrideT.value > -100.f) { // o2-linter: disable=magic-number (override sentinel) + mBzT = cfgBzOverrideT.value; + return; + } + auto grpmag = ccdb->getForRun("GLO/Config/GRPMagField", mRunNumber); + mBzT = 0.1f * static_cast(grpmag->getNominalL3Field()); // o2-linter: disable=magic-number (kGauss -> Tesla) + LOGF(info, "pairQC: run %d, Bz = %.2f T", mRunNumber, mBzT); + } + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + fV0PhotonCut.SetV0PtRange(pcmcuts.cfgMinPtV0, 1e10f); + fV0PhotonCut.SetV0EtaRange(-pcmcuts.cfgMaxEtaV0, +pcmcuts.cfgMaxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.cfgMinCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.cfgMaxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfgMaxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.cfgMinV0Radius, pcmcuts.cfgMaxV0Radius); + fV0PhotonCut.SetAPRange(pcmcuts.cfgMaxAlphaAP, pcmcuts.cfgMaxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(-1e+10, pcmcuts.cfgMaxChi2ITS); + fV0PhotonCut.SetDisableITSonly(pcmcuts.cfgDisableITSOnlyTrack); + fV0PhotonCut.SetDisableTPConly(pcmcuts.cfgDisableTPCOnlyTrack); + fV0PhotonCut.SetNClustersITS(0, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(0.0, 16.0); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.cfgRequireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.cfgRequireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.cfgRequireV0WithTPCOnly); + } + + void addAnalyticV0Histograms() + { + if (!qaflags.cfgDoAnalyticV0QA.value) { + return; + } + const AxisSpec axR{50, 0.f, 100.f, "R_{conv}^{builder} (cm)"}; + const AxisSpec axTruth{3, -1.5f, 1.5f, "-1 = data / no label, 0 = fake V0, 1 = true photon"}; + fRegistry.add("Photon/AnalyticV0/hOk", "analytic rebuild succeeded;ok;photons", kTH1F, {{2, -0.5f, 1.5f}}, true); + fRegistry.add("Photon/AnalyticV0/hdR_R_Truth", "R_{ana} - R_{builder};R_{ana} - R_{builder} (cm);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -10.f, 10.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdZ_R_Truth", "z_{ana} - z_{builder};z_{ana} - z_{builder} (cm);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -10.f, 10.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdPhiConv_R_Truth", "#varphi_{ana} - #varphi_{builder} of the conversion point;#Delta#varphi (rad);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -0.2f, 0.2f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdPtRel_Pt_Truth", "(p_{T,ana} - p_{T,builder}) / p_{T,builder};relative p_{T} difference;p_{T}^{builder} (GeV/c);truth", kTH3F, {{200, -0.5f, 0.5f}, {40, 0.f, 2.f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdCosPA_Truth", "cosPA_{ana} - cosPA_{builder};#Delta cosPA;truth", kTH2F, {{200, -0.02f, 0.02f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hPcaAna_PcaBuilder_Truth", "PCA of the two legs;PCA_{ana} (cm);PCA_{builder} (cm);truth", kTH3F, {{60, 0.f, 6.f}, {60, 0.f, 6.f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hScore_R_Truth", "builder score of the own analytic V0;score;R_{conv}^{builder} (cm);truth", kTH3F, {{200, 0.f, 30.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hPhotonLike_Truth", "own analytic V0 passes the cfgAlt* cuts;photon-like;truth", kTH2F, {{2, -0.5f, 1.5f}, axTruth}, true); + } + + void addPairQAHistograms() + { + const AxisSpec axDrCos{100, 0.f, 200.f, "|#vec{r}_{1}-#vec{r}_{2}| / cos(#alpha/2) (cm)"}; + const AxisSpec axOpa{100, 0.f, 0.5f, "opening angle of the conversion points (rad)"}; + const AxisSpec axNTimed{5, -0.5f, 4.5f, "legs with a time (ITS, TRD or TOF) among the four"}; + const AxisSpec axNForeign{5, -0.5f, 4.5f, "legs whose collisionId differs from their V0"}; + const AxisSpec axMaxDcaZ{50, 0.f, 10.f, "max |DCAz| of the two V0 photons to the PV (cm)"}; + const AxisSpec axMaxDcaXY{30, 0.f, 3.f, "max |DCAxy| of the two V0 photons to the PV (cm)"}; + const std::string base = "Pair/QA/Before/"; + fRegistry.add((base + "hDEtaDPhi_Qinv").c_str(), "pair (#Delta#eta,#Delta#phi,q_{inv});#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c)", kTH3F, {axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistry.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR}, true); + fRegistry.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2F, {axisQinv, axisDeltaZ}, true); + fRegistry.add((base + "hDeltaR3DVsQinv").c_str(), "|#vec{r}_{1}-#vec{r}_{2}| vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR3D}, true); + fRegistry.add((base + "hDrOverCosOAVsQinv").c_str(), "dr/cosOA vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| / cos(#alpha/2) (cm)", kTH2F, {axisQinv, axDrCos}, true); + fRegistry.add((base + "hOpeningAngleVsQinv").c_str(), "opening angle of the conversion points vs q_{inv};q_{inv} (GeV/c);#alpha (rad)", kTH2F, {axisQinv, axOpa}, true); + fRegistry.add((base + "hQinv_NTimed_NForeign").c_str(), "pairs by leg collision association;q_{inv} (GeV/c);legs with a time;legs with foreign collisionId", kTH3F, {axisQinv, axNTimed, axNForeign}, true); + fRegistry.add((base + "hQinv_MaxDcaZ_NTimed").c_str(), "pairs by V0 pointing to the primary vertex, z;q_{inv} (GeV/c);max |DCAz| (cm);legs with a time", kTH3F, {axisQinv, axMaxDcaZ, axNTimed}, true); + fRegistry.add((base + "hQinv_MaxDcaXY_NTimed").c_str(), "pairs by V0 pointing to the primary vertex, xy;q_{inv} (GeV/c);max |DCAxy| (cm);legs with a time", kTH3F, {axisQinv, axMaxDcaXY, axNTimed}, true); + fRegistry.add((base + "hRin_Rout_OuterSofter_Qinv").c_str(), "ordered conversion radii;R_{in} (cm);R_{out} (cm);outer photon softer (0/1);q_{inv} (GeV/c)", kTHnSparseF, {{50, 0.f, 100.f}, {50, 0.f, 100.f}, {2, -0.5f, 1.5f}, axisQinvCoarse}, true); + fRegistry.add((base + "hLegStart_dNFindable_dNits_Qinv").c_str(), "leg start-point asymmetry per V0;|#Delta N_{findable}^{TPC}| (pos-neg);|#Delta N_{cls}^{ITS}|;q_{inv} (GeV/c)", kTH3F, {{40, -0.5f, 39.5f}, {8, -0.5f, 7.5f}, axisQinv}, true); + fRegistry.add((base + "hV0_Eta_Phi_R_Qinv").c_str(), "eta, phi and conversion radius of each V0 of a pair;#eta;#varphi (rad);R_{conv} (cm);q_{inv} (GeV/c)", kTHnSparseF, {{20, -1.f, 1.f}, {72, 0.f, o2::constants::math::TwoPI}, {25, 0.f, 100.f}, axisQinvCoarse}, true); + fRegistry.add((base + "hPhi_lowerPtV0").c_str(), "azimuthal angle of the lower-p_{T} V0;#phi (rad);pairs", kTH1F, {{90, 0.f, o2::constants::math::TwoPI}}, true); + const AxisSpec axPairEta{20, -1.f, 1.f, "#eta_{pair}"}; + const AxisSpec axPairPhi{72, 0.f, o2::constants::math::TwoPI, "#varphi_{pair} (rad)"}; + const AxisSpec axDeltaRAng{100, 0.f, 0.5f, "#DeltaR = #sqrt{#Delta#eta^{2} + #Delta#varphi^{2}}"}; + fRegistry.add((base + "hDEta_DPhi_PairEta_Qinv").c_str(), "#Delta#eta, #Delta#varphi vs pair #eta;#Delta#eta;#Delta#phi (rad);#eta_{pair};q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axPairEta, axisQinvCoarse}, true); + fRegistry.add((base + "hDEta_DPhi_PairPhi_Qinv").c_str(), "#Delta#eta, #Delta#varphi vs pair #varphi;#Delta#eta;#Delta#phi (rad);#varphi_{pair} (rad);q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axPairPhi, axisQinvCoarse}, true); + fRegistry.add((base + "hDPhi_Phi1_Phi2_Qinv").c_str(), "#Delta#varphi vs the azimuth of each V0;#Delta#phi (rad);#varphi_{1} (rad);#varphi_{2} (rad);q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaPhi, axPairPhi, axPairPhi, axisQinvCoarse}, true); + fRegistry.add((base + "hDEta_Eta1_Eta2_Qinv").c_str(), "#Delta#eta vs the pseudorapidity of each V0;#Delta#eta;#eta_{1};#eta_{2};q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axPairEta, axPairEta, axisQinvCoarse}, true); + fRegistry.add((base + "hDeltaRAngVsQinv").c_str(), "#DeltaR of the pair vs q_{inv};q_{inv} (GeV/c);#DeltaR", kTH2F, {axisQinv, axDeltaRAng}, true); + fRegistry.add((base + "hDeltaRAng_PairEta_PairPhi").c_str(), "#DeltaR of the pair vs where it sits;#DeltaR;#eta_{pair};#varphi_{pair} (rad)", kTH3F, {axDeltaRAng, axPairEta, axPairPhi}, true); + fRegistry.add((base + "hV0Class1_V0Class2_Qinv").c_str(), "leg classes of the two V0s of a pair (lower class first);V0 leg class 1;V0 leg class 2;q_{inv} (GeV/c)", kTH3F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), makeAxisEnum(kNV0Classes, kV0ClassTitle), axisQinv}, true); + fRegistry.add((base + "hNLegsITSTPC_NLegsTPConly_Qinv").c_str(), "legs of the four by class;N_{ITS-TPC legs} (0-4);N_{TPC-only legs} (0-4);q_{inv} (GeV/c)", kTH3F, {{5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, axisQinv}, true); + fRegistry.addClone("Pair/QA/Before/", "Pair/QA/AfterPairCuts/"); + + if (qaflags.cfgFillLegSimilarity.value) { + fRegistry.add("Pair/LegSimilarity/hdEta_dPhi_ptRatio_dNsig_Qinv", "like-sign leg similarity;#Delta#eta(LS legs);#Delta#varphi(LS legs) (rad);|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2});|#Delta n#sigma_{e}^{TPC}|;q_{inv} (GeV/c)", + kTHnSparseF, {{100, -0.1f, 0.1f}, {100, -0.1f, 0.1f}, {50, 0.f, 1.f}, {40, 0.f, 8.f}, axisQinvCoarse}, true); // o2-linter: disable=magic-number (axis definition) + } + } + + void addDedupHistograms() + { + const AxisSpec axDEta{100, 0.f, 0.5f, "|#Delta#eta| (candidate pair)"}; + const AxisSpec axDPhi{100, 0.f, 0.5f, "|#Delta#varphi| (rad)"}; + const AxisSpec axPtAsym{50, 0.f, 1.f, "|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2})"}; + const AxisSpec axDR{100, 0.f, 20.f, "|R_{1}-R_{2}| (cm)"}; + fRegistry.add("Dedup/hdEta_dPhi_ptAsym", "distance between two V0 candidates of the same collision;|#Delta#eta|;|#Delta#varphi| (rad);p_{T} asymmetry", kTH3F, {axDEta, axDPhi, axPtAsym}, true); + fRegistry.add("Dedup/hdR_ptAsym", "distance between two V0 candidates of the same collision;|R_{1}-R_{2}| (cm);p_{T} asymmetry", kTH2F, {axDR, axPtAsym}, true); + fRegistry.add("Dedup/hNDupPartners_vs_Pt", "duplicate partners per candidate (configured tolerances);N_{partners};p_{T,#gamma} (GeV/c)", kTH2F, {{6, -0.5f, 5.5f}, {50, 0.f, 2.f}}, true); + fRegistry.add("Dedup/hNCandBeforeAfter", "selected candidates per collision;0 = before dedup, 1 = after dedup;N_{cand}", kTH2F, {{2, -0.5f, 1.5f}, {21, -0.5f, 20.5f}}, true); + const AxisSpec axLegTruth{5, -0.5f, 4.5f, "0=diffReco/diffMC, 1=diffReco/sameMC, 2=sameReco/sameMC, 3=sameReco/diffMC, 4=noMC"}; + fRegistry.add("Dedup/hLeg_dEta_dPhi_Truth", "same-charge legs of two candidates;|#Delta#eta|;|#Delta#varphi| (rad);track-level truth", kTH3F, {{50, 0.f, 0.05f}, {50, 0.f, 0.05f}, axLegTruth}, true); + fRegistry.add("Dedup/hLeg_ptAsym_dEdxAsym_Truth", "same-charge legs of two candidates;p_{T} asym.;dE/dx asym.;track-level truth", kTH3F, {{50, 0.f, 0.5f}, {50, 0.f, 0.5f}, axLegTruth}, true); + fRegistry.add("Dedup/hLeg_fShared_Truth", "same-charge legs of two candidates;max f_{shared};track-level truth", kTH2F, {{20, 0.f, 1.f}, axLegTruth}, true); + if (!isMC) { + return; + } + const AxisSpec axDup = makeAxisEnum(static_cast(mcutil::DupClass::NClasses), "dup class (0 unknown, 1 same MC photon, 2 shared MC leg, 3 cross-built, 4 distinct true, 5 involves fake)"); + const AxisSpec axFlag{2, -0.5f, 1.5f, "flagged by the criterion"}; + fRegistry.add("Dedup/MC/hNCandPerMcPhoton", "V0 candidates per MC photon;N_{cand};MC photons", kTH1F, {{6, 0.5f, 6.5f}}, true); + fRegistry.add("Dedup/MC/hNCandPerMcPhoton_vs_Pt", "V0 candidates per MC photon vs p_{T};N_{cand};p_{T,#gamma}^{reco} (GeV/c)", kTH2F, {{6, 0.5f, 6.5f}, {50, 0.f, 2.f}}, true); + fRegistry.add("Dedup/MC/hDupClass", "MC class of each candidate pair of a collision;dup class;pairs", kTH1F, {axDup}, true); + fRegistry.add("Dedup/MC/hDupClass_vs_Flagged", "MC class vs dedup decision;dup class;flagged", kTH2F, {axDup, axFlag}, true); + fRegistry.add("Dedup/MC/hDupClass_dEta_dPhi", "candidate distance by MC class;dup class;|#Delta#eta|;|#Delta#varphi| (rad)", kTH3F, {axDup, axDEta, axDPhi}, true); + fRegistry.add("Dedup/MC/hDupClass_ptAsym_dR", "candidate distance by MC class;dup class;p_{T} asymmetry;|R_{1}-R_{2}| (cm)", kTH3F, {axDup, axPtAsym, axDR}, true); + fRegistry.add("Dedup/MC/hSamePhotonDuplicateType", "same MC photon: split pattern;0 = no split reco leg, 1 = split e^{+}, 2 = split e^{-}, 3 = both;pairs", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistry.add("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged", "same MC photon: split pattern vs dedup decision;split pattern;flagged", kTH2F, {{4, -0.5f, 3.5f}, axFlag}, true); + fRegistry.add("Dedup/MC/hSplitLegPairClass", "pair class for pairs containing a split reco leg;dup class;pairs", kTH1F, {axDup}, true); + fRegistry.add("Dedup/MC/hSplitLegV0Type", "V0 truth of both candidates with a split leg;0 = same true photon, 1 = one true + one fake, 2 = both fake, 3 = other;pairs", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistry.add("Dedup/MC/hSplitLeg_ptAsym_dEdxAsym_DupClass", "split legs: distance vs pair class;p_{T} asym.;dE/dx asym.;dup class", kTH3F, {{50, 0.f, 0.5f}, {50, 0.f, 0.5f}, axDup}, true); + fRegistry.add("Dedup/MC/hSplitLeg_fShared_DupClass", "split legs: shared clusters vs pair class;max f_{shared};dup class", kTH2F, {{20, 0.f, 1.f}, axDup}, true); + fRegistry.add("Dedup/MC/hDupTrackClassMatrix", "true duplicates: ITS-TPC legs A vs B;n_{ITS-TPC}(A);n_{ITS-TPC}(B)", kTH2F, {{3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}, true); + fRegistry.add("Dedup/MC/hDupDeltaRconv", "true duplicates: radius difference;|R_{1}-R_{2}| (cm);pairs", kTH1F, {{200, 0.f, 20.f}}, true); + fRegistry.add("Dedup/MC/hChoiceOutcome", "dedup choice for flagged pairs;0 true kept/fake dropped, 1 fake kept/true dropped, 2 both true same photon, 3 both fake, 4 both true distinct;flagged pairs", kTH1F, {{5, -0.5f, 4.5f}}, true); + fRegistry.add("Dedup/MC/hLegTrackTruthClass", "track id vs MC id for same-charge legs;0 diff reco/diff MC, 1 diff reco/same MC, 2 same reco/same MC, 3 same reco/diff MC, 4 no MC;leg pairs", kTH1F, {{5, -0.5f, 4.5f}}, true); + fRegistry.add("Dedup/MC/hLegTrackTruthClass_vs_Flagged", "track-level truth vs leg flagged;class;leg flagged", kTH2F, {{5, -0.5f, 4.5f}, axFlag}, true); + fRegistry.add("Pair/MC/hTruthType_Qinv_DedupFlag", "would the dedup remove this pair?;truth type;q_{inv} (GeV/c);0 = stays, 1 = removed", kTH3F, {axisTruthType, axisQinv, axFlag}, true); + } + + void addCrossPairHistograms() + { + auto h = fRegistry.add("Pair/CrossPair/hVetoCounter", "crossed-pair veto (DCAFitter, cfgCrossVetoMode);;pairs", kTH1D, {{2, -0.5f, 1.5f}}, true); + h->GetXaxis()->SetBinLabel(1, "evaluated"); + h->GetXaxis()->SetBinLabel(2, "vetoed"); + if (!qaflags.cfgDoCrossPairQA.value) { + return; + } + const AxisSpec axNAlt{3, -0.5f, 2.5f, "N photon-like alternative pairings"}; + const AxisSpec axDca{60, 0.f, 12.f, "max DCA of the two alternative pairings (cm)"}; + const AxisSpec axDcaXY{60, 0.f, 12.f, "max DCAxy to the vertex of the two alternative pairings (cm)"}; + const AxisSpec axDcaZ{60, 0.f, 12.f, "max DCAz to the vertex of the two alternative pairings (cm)"}; + for (const auto& m : {std::string("Fit"), std::string("Ana")}) { + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_" + m).c_str(), ("alternative pairings of the four legs (" + m + ");q_{inv} (GeV/c);N photon-like;max DCA (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDca}, true); + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_" + m).c_str(), ("alternative pairings, xy (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAxy (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDcaXY}, true); + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_" + m).c_str(), ("alternative pairings, z (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAz (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDcaZ}, true); + } + fRegistry.add("Pair/CrossPair/hAltDca_Fit_vs_Ana", "alternative pairing: DCA of the legs, DCAFitter vs analytic;DCA_{fit} (cm);DCA_{ana} (cm)", kTH2F, {{120, 0.f, 12.f}, {120, 0.f, 12.f}}, true); + fRegistry.add("Pair/CrossPair/hAltCosPA_Fit_vs_Ana", "alternative pairing: cosPA, DCAFitter vs analytic;cosPA_{fit};cosPA_{ana}", kTH2F, {{100, 0.9f, 1.f}, {100, 0.9f, 1.f}}, true); + fRegistry.add("Pair/CrossPair/hNAltValid_Fit_vs_Ana", "N photon-like alternatives, DCAFitter vs analytic;N_{fit};N_{ana}", kTH2F, {axNAlt, axNAlt}, true); + fRegistry.add("Pair/CrossPair/hMeeRatio_dR_Qinv", "crossed-hypothesis mass;min(m_{ee}^{cross})/q_{inv};|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", kTH3F, {{100, 0.f, 2.f}, {80, 0.f, 80.f}, axisQinv}, true); + fRegistry.add("Pair/CrossPair/hRepairDeltaMee_OwnMee_Qinv", "re-pairing test;max(m_{ee}^{own}) - max(m_{ee}^{re-paired}) (GeV/c^{2});max(m_{ee}^{own}) (GeV/c^{2});q_{inv} (GeV/c)", kTH3F, {{120, -0.06f, 0.06f}, {60, 0.f, 0.06f}, axisQinv}, true); + fRegistry.add("Pair/CrossPair/hMee1_Mee2_M4_Qinv", "pair Dalitz plane;m_{ee}(#gamma_{1}) (GeV/c^{2});m_{ee}(#gamma_{2}) (GeV/c^{2});M(4 legs) (GeV/c^{2});q_{inv} (GeV/c)", kTHnSparseF, {{30, 0.f, 0.06f}, {30, 0.f, 0.06f}, {75, 0.f, 0.3f}, axisQinvCoarse}, true); + if (!isMC) { + return; + } + for (const auto& m : {std::string("Fit"), std::string("Ana")}) { + fRegistry.add(("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_" + m + "_Type").c_str(), ("alternative pairings per truth type (" + m + ");q_{inv} (GeV/c);N photon-like;max DCA (cm);truth type").c_str(), kTHnSparseF, {axisQinv, axNAlt, axDca, axisTruthType}, true); + fRegistry.add(("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_" + m + "_Type").c_str(), ("alternative pairings per truth type, z (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAz (cm);truth type").c_str(), kTHnSparseF, {axisQinv, axNAlt, axDcaZ, axisTruthType}, true); + } + fRegistry.add("Pair/MC/CrossPair/hMeeRatio_Qinv_Type", "crossed-pair hypothesis per truth type;min(m_{ee}^{cross})/q_{inv};q_{inv} (GeV/c);truth type", kTH3F, {{100, 0.f, 2.f}, axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hMee1_Mee2_M4_Qinv_Type", "pair Dalitz plane per truth type;m_{ee}(#gamma_{1});m_{ee}(#gamma_{2});M(4 legs);q_{inv} (GeV/c);truth type", kTHnSparseF, {{30, 0.f, 0.06f}, {30, 0.f, 0.06f}, {75, 0.f, 0.3f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hQinv_MaxDcaZ_Type", "V0 pointing per truth type, z;q_{inv} (GeV/c);max |DCAz| (cm);truth type", kTH3F, {axisQinv, {50, 0.f, 10.f}, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hQinv_MaxDcaXY_Type", "V0 pointing per truth type, xy;q_{inv} (GeV/c);max |DCAxy| (cm);truth type", kTH3F, {axisQinv, {30, 0.f, 3.f}, axisTruthType}, true); + const AxisSpec axEmu{4, -0.5f, 3.5f, "0 = not evaluated, 1 = kept, 2 = swapped, 3 = cross not photon-like"}; + fRegistry.add("Pair/MC/CrossPair/hEmulation_vs_Truth", "cross-pairing emulation (analytic score) vs truth;q_{inv} (GeV/c);truth type;outcome", kTH3F, {axisQinv, axisTruthType, axEmu}, true); + fRegistry.add("Pair/MC/CrossPair/hEmuDeltaScore_vs_Truth", "S_{cross} - S_{true} vs truth;q_{inv} (GeV/c);truth type;#DeltaS", kTH3F, {axisQinv, axisTruthType, {200, -20.f, 20.f}}, true); + fRegistry.add("Pair/MC/CrossPair/hFakeSubtype_Emu_Qinv", "fake subtype vs emulation outcome;fake subtype (0 ordinary, 1 full cross, 2 half cross);outcome;q_{inv} (GeV/c)", kTH3F, {{3, -0.5f, 2.5f}, axEmu, axisQinv}, true); + } + + void addPairTruthHistograms() + { + fRegistry.add("Pair/MC/hTruthTypeVsQinv", "truth type vs q_{inv};q_{inv} (GeV/c);truth type", kTH2F, {axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/hTruthTypeVsKt", "truth type vs k_{T};k_{T} (GeV/c);truth type", kTH2F, {axisKt, axisTruthType}, true); + fRegistry.add("Pair/MC/hFakeSubtypeVsQinv", "fake pair subtype vs q_{inv};q_{inv} (GeV/c);0=ordinary, 1=full cross, 2=half cross", kTH2F, {axisQinv, {3, -0.5f, 2.5f}}, true); + fRegistry.add("Pair/MC/hV0Class1_V0Class2_Qinv_Type", "leg classes of the two V0s of a pair vs truth type;V0 leg class 1;V0 leg class 2;q_{inv} (GeV/c);truth type", kTHnSparseF, {makeAxisEnum(kNV0Classes, kV0ClassTitle), makeAxisEnum(kNV0Classes, kV0ClassTitle), axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/hV0Class_IsTrue_Qinv", "per V0: leg class vs V0 truth;V0 leg class;V0 is a true photon;q_{inv} (GeV/c)", kTH3F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {2, -0.5f, 1.5f}, axisQinv}, true); + static constexpr std::array kTypes = {"TrueTrueDistinct/", "TrueTrueSamePhoton/", "SharedMcLeg/", "TrueFake/", "FakeFake/", "Pi0Daughters/"}; + for (const auto& label : kTypes) { + const std::string base = std::string("Pair/MC/") + std::string(label); + fRegistry.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR}, true); + fRegistry.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2F, {axisQinv, axisDeltaZ}, true); + fRegistry.add((base + "hDeltaR3DVsQinv").c_str(), "|#vec{r}_{1}-#vec{r}_{2}| vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR3D}, true); + fRegistry.add((base + "hDEtaDPhi").c_str(), "#Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2F, {axisDeltaEta, axisDeltaPhi}, true); + if (qaflags.cfgFillTypeSparses.value) { + fRegistry.add((base + "hDEtaDPhi_Qinv").c_str(), "#Delta#eta, #Delta#phi, q_{inv};#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c)", kTH3F, {axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistry.add((base + "hDeltaR_DeltaZ_Qinv").c_str(), "|R_{1}-R_{2}|, #Delta z, q_{inv};|R_{1}-R_{2}| (cm);#Delta z (cm);q_{inv} (GeV/c)", kTH3F, {axisDeltaR, axisDeltaZ, axisQinv}, true); + } + } + fRegistry.add("Pair/MC/hRreco_minus_Rmc", "conversion radius resolution by V0 truth;R_{reco} - R_{MC}(leg) (cm);R_{MC}(leg) (cm);V0 is a true photon", kTH3F, {{120, -30.f, 30.f}, {50, 0.f, 100.f}, {2, -0.5f, 1.5f}}, true); + fRegistry.add("Pair/MC/hPsiPair_PhiV_Qinv_Type_IsTrue", "per-photon conversion topology;|#psi_{pair}| (rad);#varphi_{V} (rad);q_{inv} (GeV/c);truth type;is true photon", kTHnSparseF, {{90, 0.f, o2::constants::math::PIHalf}, {90, 0.f, o2::constants::math::PI}, axisQinvCoarse, axisTruthType, {2, -0.5f, 1.5f}}, true); + fRegistry.add("Pair/MC/hMcEventPattern_NOwn_Qinv_Type", "MC event of the four legs;0 all same, 1 two consistent V0 from different events, 2 a V0 mixes events;N legs from own event;q_{inv} (GeV/c);truth type", kTHnSparseF, {{3, -0.5f, 2.5f}, {5, -0.5f, 4.5f}, axisQinv, axisTruthType}, true); + } + + void addAncestryHistograms() + { + if (!qaflags.cfgDoAncestry.value) { + return; + } + const AxisSpec axAnc = makeAxisEnum(static_cast(mcutil::AncestorKind::NClasses), "ancestor kind (0 none, 1 string/parton, 2 beam/nucleus, 3 #pi^{0}, 4 #eta, 5 other meson, 6 baryon, 7 photon, 8 electron, 9 other)"); + const AxisSpec axReco = makeAxisEnum(static_cast(mcutil::V0RecoStatus::NClasses), "V0 reco status (0 no label, 1 no conv leg, 2 one conv leg, 3 cross-built, 4 true photon)"); + const AxisSpec axSplit{6, -0.5f, 5.5f, "split code (2 n_{consistent} + shared)"}; + const AxisSpec axSame{2, -0.5f, 1.5f, "0 = different origin particles, 1 = the same"}; + const AxisSpec axSrc = makeAxisEnum(kNPhotonSources, kPhotonSourceTitle); + const AxisSpec axQtrue{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; + const std::string base = "Pair/MC/Ancestry/"; + + fRegistry.add((base + "hCensus_Qinv_Type").c_str(), "census of the four legs: code = (legs from a conversion) + 5 (distinct parent photons);census code;q_{inv} (GeV/c);truth type", kTH3F, {{25, -0.5f, 24.5f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hSplit_Qinv_Type").c_str(), "split code of the four legs;split code;q_{inv} (GeV/c);truth type", kTH3F, {axSplit, axisQinvCoarse, axisTruthType}, true); + + fRegistry.add((base + "hQtrue_Qreco_Split").c_str(), "pairs with exactly two parent photons: true q_{inv} of those two vs reconstructed, by pair class;q_{inv}^{true} (GeV/c);q_{inv}^{reco} (GeV/c);split code", kTH3F, {axQtrue, axisQinvCoarse, axSplit}, true); + fRegistry.add((base + "hAncKind_Qinv_Type").c_str(), "common ancestor of the two parent photons;common ancestor;q_{inv} (GeV/c);truth type", kTH3F, {axAnc, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hAnc1_Anc2_Same").c_str(), "origin particle of each parent photon (walk stops before beam/string);origin of photon A;origin of photon B;same origin particle?", kTH3F, {axAnc, axAnc, axSame}, true); + fRegistry.add((base + "hSame_Qinv_Type").c_str(), "the two parent photons come from the same origin particle;same origin?;q_{inv} (GeV/c);truth type", kTH3F, {axSame, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hSrc1_Src2_Type").c_str(), "pairs with exactly two parent photons: source of each (lower code first);source of photon A;source of photon B;truth type", kTH3F, {axSrc, axSrc, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_d3D_Qinv_Type").c_str(), "true conversion points of the two parent photons;|#vec{r}_{A}-#vec{r}_{B}|^{true} (cm);q_{inv} (GeV/c);truth type", kTH3F, {{50, 0.f, 50.f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_dR_dZ_Type").c_str(), "true conversion points of the two parent photons;|R_{A}-R_{B}|^{true} (cm);|z_{A}-z_{B}|^{true} (cm);truth type", kTH3F, {{50, 0.f, 25.f}, {50, 0.f, 25.f}, axisTruthType}, true); + fRegistry.add((base + "hRMeanTrue_DRTrue_Split").c_str(), "per pair: mean and difference of the two true conversion radii, by pair class;(R_{A}+R_{B})/2 (cm);|R_{A}-R_{B}| (cm);split code", kTH3F, {{100, 0.f, 100.f}, {50, 0.f, 25.f}, axSplit}, true); + fRegistry.add((base + "hTrueConv_dTheta_Qtrue_Type").c_str(), "true opening angle and q of the two parent photons;#Delta#theta_{#gamma#gamma}^{true} (mrad);q_{inv}^{true} (GeV/c);truth type", kTH3F, {{50, 0.f, 50.f}, axQtrue, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_DeltaQ_Qtrue_Type").c_str(), "q_{inv}^{reco} - q_{inv}^{true} per pair;q_{inv}^{reco} - q_{inv}^{true} (GeV/c);q_{inv}^{true} (GeV/c);truth type", kTH3F, {{140, -0.05f, 0.30f}, axQtrue, axisTruthType}, true); + + fRegistry.add((base + "hEta_Phi_Reco").c_str(), "per V0 candidate: eta, phi and reco status;#eta;#varphi (rad);V0 reco status", kTH3F, {{20, -1.f, 1.f}, {72, 0.f, o2::constants::math::TwoPI}, axReco}, true); + fRegistry.add((base + "hR_Reco_Qinv").c_str(), "per V0 candidate: conversion radius and reco status;R_{conv} (cm);V0 reco status;q_{inv} (GeV/c)", kTH3F, {{25, 0.f, 100.f}, axReco, axisQinvCoarse}, true); + fRegistry.add((base + "hVx_Vy_Split").c_str(), "reconstructed conversion point of each V0, labelled by the class of its PAIR;V_{x} (cm);V_{y} (cm);split code", kTH3F, {{200, -100.f, 100.f}, {200, -100.f, 100.f}, axSplit}, true); + fRegistry.add((base + "hVxTrue_VyTrue_Split").c_str(), "true conversion point (production vertex of the e^{+} leg), by pair class;V_{x}^{true} (cm);V_{y}^{true} (cm);split code", kTH3F, {{200, -100.f, 100.f}, {200, -100.f, 100.f}, axSplit}, true); + fRegistry.add((base + "hRTruePos_RTrueNeg_Reco").c_str(), "true conversion radius of each leg, by reco status;R^{true}(e^{+}) (cm);R^{true}(e^{-}) (cm);V0 reco status", kTH3F, {{100, 0.f, 100.f}, {100, 0.f, 100.f}, axReco}, true); + fRegistry.add((base + "hRTrueMean_RReco_Reco").c_str(), "mean true conversion radius of the two legs vs reconstructed V0 radius;(R^{true}(e^{+}) + R^{true}(e^{-}))/2 (cm);R^{reco} (cm);V0 reco status", kTH3F, {{100, 0.f, 100.f}, {100, 0.f, 100.f}, axReco}, true); + fRegistry.add((base + "hSrcPos_SrcNeg_Reco").c_str(), "per V0 candidate: parent photon sources and reco status;source of the e^{+} parent;source of the e^{-} parent;V0 reco status", kTH3F, {axSrc, axSrc, axReco}, true); + fRegistry.add((base + "hSrcPos_SrcNeg_Type").c_str(), "per V0 candidate: parent photon sources and the truth type of its pair;source of the e^{+} parent;source of the e^{-} parent;truth type", kTH3F, {axSrc, axSrc, axisTruthType}, true); + fRegistry.add((base + "hAncPos_AncNeg_Reco").c_str(), "per V0: origin particle of each leg's parent photon;origin of the e^{+} parent;origin of the e^{-} parent;V0 reco status", kTH3F, {axAnc, axAnc, axReco}, true); + fRegistry.add((base + "hAncSame_Reco").c_str(), "per V0: the two legs' parent photons have the same origin particle;same origin?;V0 reco status", kTH2F, {axSame, axReco}, true); + std::vector psiEdges; + constexpr int kPsiFine = 60, kPsiCoarse = 13; + for (int i = 0; i <= kPsiFine; ++i) { + psiEdges.push_back(0.005 * i); // o2-linter: disable=magic-number (5 mrad steps) + } + for (int i = 1; i <= kPsiCoarse; ++i) { + psiEdges.push_back(0.30 + 0.1 * i); // o2-linter: disable=magic-number (coarse part up to pi/2) + } + std::vector chi2Edges; + constexpr int kChi2Fine = 50, kChi2Coarse = 20; + for (int i = 0; i <= kChi2Fine; ++i) { + chi2Edges.push_back(0.2 * i); // o2-linter: disable=magic-number (0.2 steps to 10) + } + for (int i = 1; i <= kChi2Coarse; ++i) { + chi2Edges.push_back(10. + 2.0 * i); // o2-linter: disable=magic-number (coarse part to 50) + } + fRegistry.add((base + "hPsiPair_Chi2_Split").c_str(), "per V0: the Run-2 cut plane, by pair class;|#psi_{pair}| (rad);#chi^{2}/ndf (KF);split code", kTH3F, {{psiEdges, "|#psi_{pair}| (rad)"}, {chi2Edges, "#chi^{2}/ndf (KF)"}, axSplit}, true); + fRegistry.add((base + "hPsiPair_R_Split").c_str(), "per V0: |psi_pair| vs conversion radius, by pair class;|#psi_{pair}| (rad);R_{conv}^{reco} (cm);split code", kTH3F, {{psiEdges, "|#psi_{pair}| (rad)"}, {10, 0.f, 100.f}, axSplit}, true); + } + + [[nodiscard]] inline bool passAsymmetryCut(float pt1, float pt2) const + { + if (ggpaircuts.cfgMaxAsymmetry.value < 0.f) { + return true; + } + const float sum = pt1 + pt2; + if (sum < KMinSigma) { + return false; + } + return std::fabs(pt1 - pt2) / sum < ggpaircuts.cfgMaxAsymmetry.value; + } + + [[nodiscard]] inline bool isInsideEllipse(float deta, float dphi) const + { + if (!ggpaircuts.cfgDoEllipseCut.value) { + return false; + } + const float sE = ggpaircuts.cfgEllipseSigEta.value; + const float sP = ggpaircuts.cfgEllipseSigPhi.value; + if (sE < KMinSigma || sP < KMinSigma) { + return false; + } + return (deta / sE) * (deta / sE) + (dphi / sP) * (dphi / sP) < ggpaircuts.cfgEllipseR2.value; + } + + [[nodiscard]] inline bool passRZCut(float deltaR, float deltaZ) const + { + if (ggpaircuts.cfgDoRCut.value && deltaR < ggpaircuts.cfgMinDeltaR.value) { + return false; + } + if (ggpaircuts.cfgDoZCut.value && std::fabs(deltaZ) < ggpaircuts.cfgMinDeltaZ.value) { + return false; + } + return true; + } + + [[nodiscard]] bool passPairCuts(PairObs const& obs, PhotonCand const& a, PhotonCand const& b) const + { + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA.value) { + return false; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + return false; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + return false; + } + return std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)) < ggpaircuts.cfgMaxDcaZToPV.value && + std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)) < ggpaircuts.cfgMaxDcaXYToPV.value; + } + + [[nodiscard]] PairObs buildPairObs(PhotonCand const& g1, PhotonCand const& g2) const + { + PairObs o; + const float dx = g1.fVx - g2.fVx, dy = g1.fVy - g2.fVy, dz = g1.fVz - g2.fVz; + o.deltaR = std::fabs(g1.rConv() - g2.rConv()); + o.deltaZ = dz; + o.deltaR3D = std::sqrt(dx * dx + dy * dy + dz * dz); + const ROOT::Math::XYZVector cp1(g1.fVx, g1.fVy, g1.fVz), cp2(g2.fVx, g2.fVy, g2.fVz); + const float mag1 = std::sqrt(cp1.Mag2()), mag2 = std::sqrt(cp2.Mag2()); + if (mag1 < KMinMagnitude || mag2 < KMinMagnitude) { + o.valid = false; + return o; + } + const float cosPA = std::clamp(static_cast(cp1.Dot(cp2) / (mag1 * mag2)), -1.f, 1.f); + o.opa = std::acos(cosPA); + const float cosHalf = std::cos(0.5f * o.opa); + o.drOverCosOA = (std::fabs(cosHalf) < KMinCosine) ? 1e12f : (o.deltaR3D / cosHalf); // o2-linter: disable=magic-number (sentinel for a degenerate opening angle) + o.v1 = ROOT::Math::PtEtaPhiMVector(g1.fPt, g1.fEta, g1.fPhi, 0.f); + o.v2 = ROOT::Math::PtEtaPhiMVector(g2.fPt, g2.fEta, g2.fPhi, 0.f); + o.deta = g1.fEta - g2.fEta; + o.dphi = RecoDecay::constrainAngle(g1.fPhi - g2.fPhi, -o2::constants::math::PI); + o.q = pairutil::computePairQ(o.v1, o.v2); + return o; + } + + [[nodiscard]] CrossObs computeCrossObs(PhotonCand const& a, PhotonCand const& b, float qinv) const + { + CrossObs c; + if (qinv < crosspair.cfgAltMaxQinv.value) { + const float bzkG = 10.f * mBzT; // o2-linter: disable=magic-number (Tesla -> kGauss) + c.fit[0] = evaluateAltPairingFitter(a.legParam(0), b.legParam(1), mBzT); + c.fit[1] = evaluateAltPairingFitter(b.legParam(0), a.legParam(1), mBzT); + c.ana[0] = buildAnalyticV0(a.legParam(0), b.legParam(1), bzkG, qaflags.cfgScoreWeight.value); + c.ana[1] = buildAnalyticV0(b.legParam(0), a.legParam(1), bzkG, qaflags.cfgScoreWeight.value); + c.nAltValidFit = (fitIsPhotonLike(c.fit[0]) ? 1 : 0) + (fitIsPhotonLike(c.fit[1]) ? 1 : 0); + c.nAltValidAna = (isPhotonLike(c.ana[0], mAltCuts) ? 1 : 0) + (isPhotonLike(c.ana[1], mAltCuts) ? 1 : 0); + c.maxDcaFit = std::max(c.fit[0].dca, c.fit[1].dca); + c.maxDcaXYFit = std::max(c.fit[0].dcaxy, c.fit[1].dcaxy); + c.maxDcaZFit = std::max(c.fit[0].dcaz, c.fit[1].dcaz); + c.maxDcaAna = std::max(c.ana[0].pca, c.ana[1].pca); + c.maxDcaXYAna = std::max(c.ana[0].dcaxy, c.ana[1].dcaxy); + c.maxDcaZAna = std::max(c.ana[0].dcaz, c.ana[1].dcaz); + c.evaluated = true; + } + auto legVec = [](PhotonCand const& p, int i) { + const auto& l = p.leg[static_cast(i)]; + return ROOT::Math::PtEtaPhiMVector(l.pt, l.eta, l.phi, KMe); + }; + c.mee[0] = static_cast((legVec(a, 0) + legVec(b, 1)).M()); + c.mee[1] = static_cast((legVec(b, 0) + legVec(a, 1)).M()); + if (qinv > 0.f) { + c.meeOverQ = std::min(c.mee[0], c.mee[1]) / qinv; + } + c.ownMee[0] = static_cast((legVec(a, 0) + legVec(a, 1)).M()); + c.ownMee[1] = static_cast((legVec(b, 0) + legVec(b, 1)).M()); + c.ownMeeMax = std::max(c.ownMee[0], c.ownMee[1]); + c.deltaMee = c.ownMeeMax - std::max(c.mee[0], c.mee[1]); + c.m4 = static_cast((legVec(a, 0) + legVec(a, 1) + legVec(b, 0) + legVec(b, 1)).M()); + return c; + } + + [[nodiscard]] inline bool fitIsPhotonLike(AltPairing const& r) const + { + return r.fitted && r.dca < mAltCuts.maxDca && r.rxy > mAltCuts.minR && r.rxy < mAltCuts.maxR && + r.cospa > mAltCuts.minCosPA && r.mee < mAltCuts.maxMee && r.dcaxy < mAltCuts.maxDcaXY && r.dcaz < mAltCuts.maxDcaZ; + } + + [[nodiscard]] inline bool passCrossPairVeto(CrossObs const& c) const + { + const int need = crosspair.cfgAltRequireBoth.value ? 2 : 1; + const bool passGeom = c.nAltValidFit < need; + const bool passDca = (crosspair.cfgCrossMinMaxAltDca.value <= 0.f) || (c.maxDcaFit > crosspair.cfgCrossMinMaxAltDca.value); + switch (crosspair.cfgCrossVetoMode.value) { + case 2: // o2-linter: disable=magic-number (veto mode) + return passDca; + case 3: // o2-linter: disable=magic-number (veto mode) + return passGeom && passDca; + default: + return passGeom; + } + } + struct Emulation { + int outcome{0}; + float deltaScore{0.f}; + }; + [[nodiscard]] Emulation emulate(CrossObs const& c, PhotonCand const& a, PhotonCand const& b) const + { + Emulation e; + if (!c.evaluated || !a.own.ok || !b.own.ok) { + return e; + } + if (c.nAltValidAna < 2) { // o2-linter: disable=magic-number (both cross candidates must be photon-like) + e.outcome = 3; // o2-linter: disable=magic-number (outcome code) + return e; + } + e.deltaScore = (c.ana[0].score + c.ana[1].score) - (a.own.score + b.own.score); + e.outcome = (e.deltaScore < 0.f) ? 2 : 1; // o2-linter: disable=magic-number (outcome code) + return e; + } + + template + void fillPairQA(PairObs const& obs, PhotonCand const& a, PhotonCand const& b) + { + constexpr auto dir = (step == 0) ? "Pair/QA/Before/" : "Pair/QA/AfterPairCuts/"; + const float qinv = obs.q.qinv; + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi_Qinv"), obs.deta, obs.dphi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDeltaRVsQinv"), qinv, obs.deltaR); + fRegistry.fill(HIST(dir) + HIST("hDeltaZVsQinv"), qinv, obs.deltaZ); + fRegistry.fill(HIST(dir) + HIST("hDeltaR3DVsQinv"), qinv, obs.deltaR3D); + fRegistry.fill(HIST(dir) + HIST("hDrOverCosOAVsQinv"), qinv, std::min(obs.drOverCosOA, 199.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hOpeningAngleVsQinv"), qinv, obs.opa); + const int nTimed = a.nTimedLegs() + b.nTimedLegs(); + fRegistry.fill(HIST(dir) + HIST("hQinv_NTimed_NForeign"), qinv, static_cast(nTimed), static_cast(a.nForeignLegs() + b.nForeignLegs())); + fRegistry.fill(HIST(dir) + HIST("hQinv_MaxDcaZ_NTimed"), qinv, std::min(std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)), 9.9f), static_cast(nTimed)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hQinv_MaxDcaXY_NTimed"), qinv, std::min(std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)), 2.9f), static_cast(nTimed)); // o2-linter: disable=magic-number (clamp to the axis) + const float ra = a.rConv(), rb = b.rConv(); + const bool aInner = ra <= rb; + const float rIn = aInner ? ra : rb, rOut = aInner ? rb : ra; + const bool outerSofter = aInner ? (b.fPt < a.fPt) : (a.fPt < b.fPt); + const bool inSparseWindow = qinv < qaflags.cfgMaxQinvForSparses.value; + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hRin_Rout_OuterSofter_Qinv"), rIn, rOut, outerSofter ? 1.f : 0.f, qinv); + } + for (const PhotonCand& p : {std::cref(a), std::cref(b)}) { + fRegistry.fill(HIST(dir) + HIST("hLegStart_dNFindable_dNits_Qinv"), std::fabs(p.leg[0].nClsFindable - p.leg[1].nClsFindable), std::fabs(p.leg[0].nClsITS - p.leg[1].nClsITS), qinv); + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hV0_Eta_Phi_R_Qinv"), p.fEta, p.fPhi, std::min(p.rConv(), 99.9f), qinv); // o2-linter: disable=magic-number (clamp to the axis) + } + } + fRegistry.fill(HIST(dir) + HIST("hPhi_lowerPtV0"), (a.fPt < b.fPt) ? a.fPhi : b.fPhi); + const float pairEta = 0.5f * (a.fEta + b.fEta); + const float pairPhi = RecoDecay::constrainAngle(static_cast((obs.v1 + obs.v2).Phi()), 0.f); + const float deltaRAng = std::hypot(obs.deta, obs.dphi); + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hDEta_DPhi_PairEta_Qinv"), obs.deta, obs.dphi, pairEta, qinv); + fRegistry.fill(HIST(dir) + HIST("hDEta_DPhi_PairPhi_Qinv"), obs.deta, obs.dphi, pairPhi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDPhi_Phi1_Phi2_Qinv"), obs.dphi, a.fPhi, b.fPhi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDEta_Eta1_Eta2_Qinv"), obs.deta, a.fEta, b.fEta, qinv); + } + fRegistry.fill(HIST(dir) + HIST("hDeltaRAngVsQinv"), qinv, std::min(deltaRAng, 0.499f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hDeltaRAng_PairEta_PairPhi"), std::min(deltaRAng, 0.499f), pairEta, pairPhi); // o2-linter: disable=magic-number (clamp to the axis) + const int c1 = v0LegClass(a.legCounts), c2 = v0LegClass(b.legCounts); + fRegistry.fill(HIST(dir) + HIST("hV0Class1_V0Class2_Qinv"), static_cast(std::min(c1, c2)), static_cast(std::max(c1, c2)), qinv); + fRegistry.fill(HIST(dir) + HIST("hNLegsITSTPC_NLegsTPConly_Qinv"), static_cast(a.legCounts.nITSTPC + b.legCounts.nITSTPC), static_cast(a.legCounts.nTPCOnly + b.legCounts.nTPCOnly), qinv); + } + + void fillLegSimilarity(PhotonCand const& a, PhotonCand const& b, float qinv) + { + if (!qaflags.cfgFillLegSimilarity.value || qinv >= qaflags.cfgMaxQinvForSparses.value) { + return; + } + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (e+ with e+, e- with e-) + const auto& la = a.leg[static_cast(il)]; + const auto& lb = b.leg[static_cast(il)]; + const float ptSum = la.pt + lb.pt; + const float ptRatio = (ptSum > KMinSigma) ? std::fabs(la.pt - lb.pt) / ptSum : 1.f; + fRegistry.fill(HIST("Pair/LegSimilarity/hdEta_dPhi_ptRatio_dNsig_Qinv"), la.eta - lb.eta, + RecoDecay::constrainAngle(la.phi - lb.phi, -o2::constants::math::PI), ptRatio, + std::min(std::fabs(la.nsigEl - lb.nsigEl), 7.99f), qinv); // o2-linter: disable=magic-number (clamp to the axis) + } + } + + template + void fillAnalyticV0QA(PhotonCand const& p) + { + if (!qaflags.cfgDoAnalyticV0QA.value) { + return; + } + fRegistry.fill(HIST("Photon/AnalyticV0/hOk"), p.own.ok ? 1.f : 0.f); + if (!p.own.ok) { + return; + } + float truth = -1.f; + if constexpr (IsMC) { + if (p.mc.hasBothLabels()) { + truth = p.mc.isTruePhoton ? 1.f : 0.f; + } + } + const float rB = p.rConv(); + const float xA = p.own.vtx[0], yA = p.own.vtx[1], zA = p.own.vtx[2] + p.fVtxZ; + fRegistry.fill(HIST("Photon/AnalyticV0/hdR_R_Truth"), std::hypot(xA, yA) - rB, rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdZ_R_Truth"), zA - p.fVz, rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdPhiConv_R_Truth"), RecoDecay::constrainAngle(std::atan2(yA, xA) - std::atan2(p.fVy, p.fVx), -o2::constants::math::PI), rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdPtRel_Pt_Truth"), (p.fPt > KMinSigma) ? (p.own.pt() - p.fPt) / p.fPt : 0.f, p.fPt, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdCosPA_Truth"), p.own.cospa - p.cospa, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hPcaAna_PcaBuilder_Truth"), std::min(p.own.pca, 5.99f), std::min(p.pca, 5.99f), truth); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Photon/AnalyticV0/hScore_R_Truth"), std::min(p.own.score, 29.9f), rB, truth); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Photon/AnalyticV0/hPhotonLike_Truth"), isPhotonLike(p.own, mAltCuts) ? 1.f : 0.f, truth); + } + template + void runDedupQA(std::vector& cands) + { + const auto n = static_cast(cands.size()); + std::vector nDupPartners(cands.size(), 0); + for (int i = 0; i < n; ++i) { + for (int j = i + 1; j < n; ++j) { + auto& a = cands[static_cast(i)]; + auto& b = cands[static_cast(j)]; + const float dEta = std::fabs(a.fEta - b.fEta); + const float dPhi = std::fabs(RecoDecay::constrainAngle(a.fPhi - b.fPhi, -o2::constants::math::PI)); + const float ptSum = a.fPt + b.fPt; + const float ptAsym = (ptSum > KMinSigma) ? std::fabs(a.fPt - b.fPt) / ptSum : 0.f; + const float dR = std::fabs(a.rConv() - b.rConv()); + const bool flagged = pairutil::isDuplicatePair(a.dedupCand, b.dedupCand, mDedupCfg); + fRegistry.fill(HIST("Dedup/hdEta_dPhi_ptAsym"), dEta, dPhi, ptAsym); + fRegistry.fill(HIST("Dedup/hdR_ptAsym"), dR, ptAsym); + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (e+ and e- legs) + const auto ld = pairutil::legDistance(a.dedupCand, b.dedupCand, il); + float truth = 4.f; // o2-linter: disable=magic-number (no / invalid MC info) + if constexpr (IsMC) { + const int ida = (il == 0) ? a.mc.mcPosId : a.mc.mcNegId; + const int idb = (il == 0) ? b.mc.mcPosId : b.mc.mcNegId; + const int idbX = (il == 0) ? b.mc.mcNegId : b.mc.mcPosId; + if (ida >= 0 && (idb >= 0 || idbX >= 0)) { + const bool sameRecoTrack = (a.leg[static_cast(il)].trackId == b.leg[static_cast(il)].trackId) || + (a.leg[static_cast(il)].trackId == b.leg[static_cast(1 - il)].trackId); + const bool sameMC = (ida == idb) || (ida == idbX); + // 0 diff reco/diff MC, 1 diff reco/same MC, 2 same reco/same MC, 3 same reco/diff MC + if (!sameRecoTrack) { + truth = sameMC ? 1.f : 0.f; + } else { + truth = sameMC ? 2.f : 3.f; // o2-linter: disable=magic-number (class codes, see booking) + } + } + const bool legFlagged = pairutil::legsIdentical(a.dedupCand, b.dedupCand, il, mDedupCfg); + fRegistry.fill(HIST("Dedup/MC/hLegTrackTruthClass"), truth); + fRegistry.fill(HIST("Dedup/MC/hLegTrackTruthClass_vs_Flagged"), truth, legFlagged ? 1.f : 0.f); + } + fRegistry.fill(HIST("Dedup/hLeg_dEta_dPhi_Truth"), ld.dEta, ld.dPhi, truth); + fRegistry.fill(HIST("Dedup/hLeg_ptAsym_dEdxAsym_Truth"), ld.ptAsym, ld.dedxAsym, truth); + fRegistry.fill(HIST("Dedup/hLeg_fShared_Truth"), ld.fShared, truth); + } + if constexpr (IsMC) { + const auto dupClass = static_cast(static_cast(mcutil::classifyDupPair(a.mc, b.mc))); + fRegistry.fill(HIST("Dedup/MC/hDupClass"), dupClass); + fRegistry.fill(HIST("Dedup/MC/hDupClass_vs_Flagged"), dupClass, flagged ? 1.f : 0.f); + fRegistry.fill(HIST("Dedup/MC/hDupClass_dEta_dPhi"), dupClass, dEta, dPhi); + fRegistry.fill(HIST("Dedup/MC/hDupClass_ptAsym_dR"), dupClass, ptAsym, dR); + const std::array split = { + a.leg[0].trackId != b.leg[0].trackId && a.mc.mcPosId >= 0 && a.mc.mcPosId == b.mc.mcPosId, + a.leg[1].trackId != b.leg[1].trackId && a.mc.mcNegId >= 0 && a.mc.mcNegId == b.mc.mcNegId}; + if (split[0] || split[1]) { + fRegistry.fill(HIST("Dedup/MC/hSplitLegPairClass"), dupClass); + float v0Type = 3.f; // o2-linter: disable=magic-number (other) + if (a.mc.isTruePhoton && b.mc.isTruePhoton && a.mc.posPhotonId == b.mc.posPhotonId) { + v0Type = 0.f; + } else if (a.mc.isTruePhoton != b.mc.isTruePhoton) { + v0Type = 1.f; + } else if (!a.mc.isTruePhoton && !b.mc.isTruePhoton) { + v0Type = 2.f; // o2-linter: disable=magic-number (both fake) + } + fRegistry.fill(HIST("Dedup/MC/hSplitLegV0Type"), v0Type); + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (two legs) + if (!split[static_cast(il)]) { + continue; + } + const auto ldSplit = pairutil::legDistance(a.dedupCand, b.dedupCand, il); + fRegistry.fill(HIST("Dedup/MC/hSplitLeg_ptAsym_dEdxAsym_DupClass"), ldSplit.ptAsym, ldSplit.dedxAsym, dupClass); + fRegistry.fill(HIST("Dedup/MC/hSplitLeg_fShared_DupClass"), ldSplit.fShared, dupClass); + } + } + if (mcutil::classifyDupPair(a.mc, b.mc) == mcutil::DupClass::SameMcPhoton) { + // 0 no split leg, 1 split e+, 2 split e-, 3 both + const float splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) + fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType"), splitType); + fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged"), splitType, flagged ? 1.f : 0.f); + fRegistry.fill(HIST("Dedup/MC/hDupTrackClassMatrix"), static_cast(a.dedupCand.nITSTPC), static_cast(b.dedupCand.nITSTPC)); + fRegistry.fill(HIST("Dedup/MC/hDupDeltaRconv"), dR); + } + } + if (flagged) { + ++nDupPartners[static_cast(i)]; + ++nDupPartners[static_cast(j)]; + const bool keepA = pairutil::isBetterCand(a.dedupCand, b.dedupCand); + (keepA ? b : a).dedupRejected = true; + if constexpr (IsMC) { + const auto& kept = keepA ? a : b; + const auto& dropped = keepA ? b : a; + float outcome = 4.f; // o2-linter: disable=magic-number (both true, distinct photons) + if (kept.mc.isTruePhoton && !dropped.mc.isTruePhoton) { + outcome = 0.f; + } else if (!kept.mc.isTruePhoton && dropped.mc.isTruePhoton) { + outcome = 1.f; + } else if (kept.mc.isTruePhoton && dropped.mc.isTruePhoton && kept.mc.posPhotonId == dropped.mc.posPhotonId) { + outcome = 2.f; // o2-linter: disable=magic-number (both true, same photon) + } else if (!kept.mc.isTruePhoton && !dropped.mc.isTruePhoton) { + outcome = 3.f; // o2-linter: disable=magic-number (both fake) + } + fRegistry.fill(HIST("Dedup/MC/hChoiceOutcome"), outcome); + } + } + } + } + if constexpr (IsMC) { + std::unordered_map> perMcPhoton; // photon id -> (count, reco pT of the last candidate) + for (const auto& c : cands) { + if (c.mc.isTruePhoton) { + auto& entry = perMcPhoton[c.mc.posPhotonId]; + ++entry.first; + entry.second = c.fPt; + } + } + for (const auto& [mcId, entry] : perMcPhoton) { + const auto nCand = static_cast(std::min(entry.first, 6)); // o2-linter: disable=magic-number (last bin) + fRegistry.fill(HIST("Dedup/MC/hNCandPerMcPhoton"), nCand); + fRegistry.fill(HIST("Dedup/MC/hNCandPerMcPhoton_vs_Pt"), nCand, entry.second); + } + } + int nRejected = 0; + for (int i = 0; i < n; ++i) { + fRegistry.fill(HIST("Dedup/hNDupPartners_vs_Pt"), static_cast(std::min(nDupPartners[static_cast(i)], 5)), cands[static_cast(i)].fPt); // o2-linter: disable=magic-number (last bin) + nRejected += cands[static_cast(i)].dedupRejected ? 1 : 0; + } + fRegistry.fill(HIST("Dedup/hNCandBeforeAfter"), 0.f, static_cast(n)); + fRegistry.fill(HIST("Dedup/hNCandBeforeAfter"), 1.f, static_cast(n - nRejected)); + } + + template + void fillCrossPairQA(CrossObs const& c, PairObs const& obs, float truthAxis, PhotonCand const& a, PhotonCand const& b) + { + const float qinv = obs.q.qinv; + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaXYFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaXYAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaZFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaZAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + if (c.evaluated) { + for (int k = 0; k < 2; ++k) { // o2-linter: disable=magic-number (the two alternative pairings) + if (c.fit[static_cast(k)].fitted && c.ana[static_cast(k)].ok) { + fRegistry.fill(HIST("Pair/CrossPair/hAltDca_Fit_vs_Ana"), std::min(c.fit[static_cast(k)].dca, 11.9f), std::min(c.ana[static_cast(k)].pca, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hAltCosPA_Fit_vs_Ana"), c.fit[static_cast(k)].cospa, c.ana[static_cast(k)].cospa); + } + } + fRegistry.fill(HIST("Pair/CrossPair/hNAltValid_Fit_vs_Ana"), static_cast(c.nAltValidFit), static_cast(c.nAltValidAna)); + } + if (c.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistry.fill(HIST("Pair/CrossPair/hMeeRatio_dR_Qinv"), c.meeOverQ, obs.deltaR, qinv); + } + fRegistry.fill(HIST("Pair/CrossPair/hRepairDeltaMee_OwnMee_Qinv"), c.deltaMee, c.ownMeeMax, qinv); + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/CrossPair/hMee1_Mee2_M4_Qinv"), c.ownMee[0], c.ownMee[1], c.m4, qinv); + } + if constexpr (IsMC) { + if (truthAxis < 0.f) { + return; + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_Fit_Type"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaFit, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_Ana_Type"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaAna, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Fit_Type"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaZFit, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Ana_Type"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaZAna, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + if (c.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistry.fill(HIST("Pair/MC/CrossPair/hMeeRatio_Qinv_Type"), c.meeOverQ, qinv, truthAxis); + } + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/MC/CrossPair/hMee1_Mee2_M4_Qinv_Type"), c.ownMee[0], c.ownMee[1], c.m4, qinv, truthAxis); + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_MaxDcaZ_Type"), qinv, std::min(std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)), 9.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_MaxDcaXY_Type"), qinv, std::min(std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)), 2.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + const Emulation emu = emulate(c, a, b); + fRegistry.fill(HIST("Pair/MC/CrossPair/hEmulation_vs_Truth"), qinv, truthAxis, static_cast(emu.outcome)); + if (emu.outcome >= 1) { + fRegistry.fill(HIST("Pair/MC/CrossPair/hEmuDeltaScore_vs_Truth"), qinv, truthAxis, std::clamp(emu.deltaScore, -19.99f, 19.99f)); // o2-linter: disable=magic-number (clamp to the axis) + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hFakeSubtype_Emu_Qinv"), static_cast(mcutil::classifyFakeSubtype(a.mc, b.mc)), static_cast(emu.outcome), qinv); + } + } + + template + void fillTypeObservables(PairObs const& obs) + { + constexpr auto dir = + (T == mcutil::PairTruthType::TrueTrueDistinct) ? "Pair/MC/TrueTrueDistinct/" + : (T == mcutil::PairTruthType::TrueTrueSamePhoton) ? "Pair/MC/TrueTrueSamePhoton/" + : (T == mcutil::PairTruthType::SharedMcLeg) ? "Pair/MC/SharedMcLeg/" + : (T == mcutil::PairTruthType::TrueFake) ? "Pair/MC/TrueFake/" + : (T == mcutil::PairTruthType::FakeFake) ? "Pair/MC/FakeFake/" + : "Pair/MC/Pi0Daughters/"; + const float qinv = obs.q.qinv; + fRegistry.fill(HIST(dir) + HIST("hDeltaRVsQinv"), qinv, obs.deltaR); + fRegistry.fill(HIST(dir) + HIST("hDeltaZVsQinv"), qinv, obs.deltaZ); + fRegistry.fill(HIST(dir) + HIST("hDeltaR3DVsQinv"), qinv, obs.deltaR3D); + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi"), obs.deta, obs.dphi); + if (qaflags.cfgFillTypeSparses.value) { + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi_Qinv"), obs.deta, obs.dphi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDeltaR_DeltaZ_Qinv"), obs.deltaR, obs.deltaZ, qinv); + } + } + + void fillTypeObservables(mcutil::PairTruthType t, PairObs const& obs) + { + switch (t) { + case mcutil::PairTruthType::TrueTrueDistinct: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::TrueTrueSamePhoton: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::SharedMcLeg: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::TrueFake: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::FakeFake: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::Pi0Daughters: + fillTypeObservables(obs); + break; + default: + break; + } + } + + template + void fillAncestry(TMCParticles const& mcParticles, PhotonCand const& a, PhotonCand const& b, PairObs const& obs, float truthAxis) + { + if (!qaflags.cfgDoAncestry.value) { + return; + } + const float qinv = obs.q.qinv; + if (qinv >= qaflags.cfgMaxQinvForSparses.value) { + return; + } + const int maxGen = qaflags.cfgAncestorMaxGen.value; + const auto census = mcutil::censusMothers(a.mc, b.mc); + const auto splitAxis = static_cast(census.splitCode()); + const auto censusCode = static_cast(census.nConvLegs + 5 * census.nMothers); // o2-linter: disable=magic-number (code = legs + 5 mothers) + fRegistry.fill(HIST("Pair/MC/Ancestry/hCensus_Qinv_Type"), censusCode, qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSplit_Qinv_Type"), splitAxis, qinv, truthAxis); + + if (census.nMothers == 2) { // o2-linter: disable=magic-number (two parent photons) + const int idA = census.distinct[0], idB = census.distinct[1]; + const float qTrue = mcutil::trueQinvOfPhotons(mcParticles, idA, idB); + const auto ancKind = static_cast(static_cast(mcutil::commonAncestorKind(mcParticles, idA, idB, maxGen))); + fRegistry.fill(HIST("Pair/MC/Ancestry/hQtrue_Qreco_Split"), qTrue, qinv, splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncKind_Qinv_Type"), ancKind, qinv, truthAxis); + const int origA = mcutil::originParticleId(mcParticles, idA), origB = mcutil::originParticleId(mcParticles, idB); + const auto kindA = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(origA).pdgCode()))); + const auto kindB = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(origB).pdgCode()))); + const float same = (origA == origB) ? 1.f : 0.f; + fRegistry.fill(HIST("Pair/MC/Ancestry/hAnc1_Anc2_Same"), std::min(kindA, kindB), std::max(kindA, kindB), same); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSame_Qinv_Type"), same, qinv, truthAxis); + const int srcA = photonSource(mcParticles, idA), srcB = photonSource(mcParticles, idB); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrc1_Src2_Type"), static_cast(std::min(srcA, srcB)), static_cast(std::max(srcA, srcB)), truthAxis); + std::array cA{}, cB{}; + if (mcutil::conversionPoint(mcParticles, idA, cA) && mcutil::conversionPoint(mcParticles, idB, cB)) { + const float rA = std::hypot(cA[0], cA[1]), rB = std::hypot(cB[0], cB[1]); + const float d3 = std::hypot(cA[0] - cB[0], cA[1] - cB[1], cA[2] - cB[2]); + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_d3D_Qinv_Type"), std::min(d3, 49.9f), qinv, truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_dR_dZ_Type"), std::min(std::fabs(rA - rB), 24.9f), std::min(std::fabs(cA[2] - cB[2]), 24.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axes) + fRegistry.fill(HIST("Pair/MC/Ancestry/hRMeanTrue_DRTrue_Split"), 0.5f * (rA + rB), std::min(std::fabs(rA - rB), 24.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + } + const float theta = mcutil::trueOpeningAngle(mcParticles, idA, idB); + if (theta >= 0.f) { + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_dTheta_Qtrue_Type"), std::min(theta * 1000.f, 49.9f), qTrue, truthAxis); // o2-linter: disable=magic-number (rad -> mrad, clamp) + } + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_DeltaQ_Qtrue_Type"), qinv - qTrue, qTrue, truthAxis); + } + + // per V0 candidate + for (const PhotonCand& p : {std::cref(a), std::cref(b)}) { + const auto reco = static_cast(static_cast(mcutil::classifyV0Reco(p.mc))); + const float rReco = p.rConv(); + { + const auto srcPos = static_cast(photonSource(mcParticles, p.mc.posPhotonId)); + const auto srcNeg = static_cast(photonSource(mcParticles, p.mc.negPhotonId)); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrcPos_SrcNeg_Reco"), srcPos, srcNeg, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrcPos_SrcNeg_Type"), srcPos, srcNeg, truthAxis); + } + fRegistry.fill(HIST("Pair/MC/Ancestry/hEta_Phi_Reco"), p.fEta, p.fPhi, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hR_Reco_Qinv"), std::min(rReco, 99.9f), reco, qinv); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hVx_Vy_Split"), p.fVx, p.fVy, splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hPsiPair_Chi2_Split"), std::fabs(p.psipair), std::min(p.chi2, 49.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hPsiPair_R_Split"), std::fabs(p.psipair), std::min(rReco, 99.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + if (p.mc.mcPosId >= 0 && p.mc.mcNegId >= 0) { + const auto ePos = mcParticles.iteratorAt(p.mc.mcPosId); + const auto eNeg = mcParticles.iteratorAt(p.mc.mcNegId); + const float rPos = std::hypot(static_cast(ePos.vx()), static_cast(ePos.vy())); + const float rNeg = std::hypot(static_cast(eNeg.vx()), static_cast(eNeg.vy())); + fRegistry.fill(HIST("Pair/MC/Ancestry/hVxTrue_VyTrue_Split"), static_cast(ePos.vx()), static_cast(ePos.vy()), splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hRTruePos_RTrueNeg_Reco"), std::min(rPos, 99.9f), std::min(rNeg, 99.9f), reco); // o2-linter: disable=magic-number (clamp to the axes) + fRegistry.fill(HIST("Pair/MC/Ancestry/hRTrueMean_RReco_Reco"), std::min(0.5f * (rPos + rNeg), 99.9f), std::min(rReco, 99.9f), reco); // o2-linter: disable=magic-number (clamp to the axes) + } + if (p.mc.posPhotonId >= 0 && p.mc.negPhotonId >= 0) { + const int oPos = mcutil::originParticleId(mcParticles, p.mc.posPhotonId); + const int oNeg = mcutil::originParticleId(mcParticles, p.mc.negPhotonId); + const auto kPos = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(oPos).pdgCode()))); + const auto kNeg = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(oNeg).pdgCode()))); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncPos_AncNeg_Reco"), kPos, kNeg, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncSame_Reco"), (oPos == oNeg) ? 1.f : 0.f, reco); + } + } + } + + template + void collectPhotons(TPhotons const& photonsColl, TMCParticles const& mcParticles, float vtxZ, std::vector& cands) + { + cands.clear(); + cands.reserve(photonsColl.size()); + for (const auto& g : photonsColl) { + if (!fV0PhotonCut.template IsSelected(g)) { + continue; + } + const auto pos = g.template posTrack_as(); + const auto ele = g.template negTrack_as(); + PhotonCand c; + c.gi = g.globalIndex(); + c.fPt = g.pt(); + c.fEta = g.eta(); + c.fPhi = g.phi(); + c.fVx = g.vx(); + c.fVy = g.vy(); + c.fVz = g.vz(); + c.fVtxZ = vtxZ; + c.fDcaXYToPV = static_cast(g.dcaXYtopv()); + c.fDcaZToPV = static_cast(g.dcaZtopv()); + c.psipair = static_cast(g.psipair()); + c.phiv = static_cast(g.phiv()); + c.chi2 = static_cast(g.chiSquareNDF()); + c.cospa = static_cast(g.cospa()); + c.pca = static_cast(g.pca()); + auto fillLeg = [&](LegLite& l, auto const& t) { + l.pt = static_cast(t.pt()); + l.eta = static_cast(t.eta()); + l.phi = static_cast(t.phi()); + l.nsigEl = static_cast(t.tpcNSigmaEl()); + l.dcaZ = static_cast(t.dcaZ()); + l.nClsFindable = static_cast(t.tpcNClsFindable()); + l.nClsITS = static_cast(t.itsNCls()); + l.timed = t.hasITS() || t.hasTRD() || t.hasTOF(); + l.foreign = (t.collisionId() != g.collisionId()); + l.trackId = t.trackId(); + }; + fillLeg(c.leg[0], pos); + fillLeg(c.leg[1], ele); + c.legCounts = pairutil::getV0PhotonLegCounts(pos, ele); + c.dedupCand = pairutil::makeDedupCand(g, pos, ele); + if (qaflags.cfgDoAnalyticV0QA.value || qaflags.cfgDoCrossPairQA.value) { + c.own = buildAnalyticV0(c.legParam(0), c.legParam(1), 10.f * mBzT, qaflags.cfgScoreWeight.value); // o2-linter: disable=magic-number (Tesla -> kGauss) + } + if constexpr (IsMC) { + c.mc = mcutil::makePhotonMCInfo(g, mcParticles); + } + cands.push_back(c); + } + } + + template + void runPairQa(TCollision const& collision, std::vector const& cands, TMCParticles const& mcParticles) + { + const int evOwn = [&]() { + if constexpr (IsMC) { + return collision.has_emmcevent() ? static_cast(collision.emmceventId()) : -1; + } else { + return -1; + } + }(); + for (size_t i = 0; i < cands.size(); ++i) { + for (size_t j = i + 1; j < cands.size(); ++j) { + const auto& g1 = cands[i]; + const auto& g2 = cands[j]; + if (g1.sharesTrackWith(g2)) { + continue; + } + const bool dedupWouldRemove = g1.dedupRejected || g2.dedupRejected; + if (dedup.cfgDoDedup.value && dedupWouldRemove) { + continue; + } + if (!passAsymmetryCut(g1.fPt, g2.fPt)) { + continue; + } + if (!pairutil::pairBelowQmax(g1.fPt, g1.fEta, g1.fPhi, g2.fPt, g2.fEta, g2.fPhi, qaflags.cfgMaxQinvForQA.value)) { + continue; + } + const PairObs obs = buildPairObs(g1, g2); + if (!obs.valid) { + continue; + } + const float qinv = obs.q.qinv; + + float truthAxis = -1.f; + mcutil::PairTruthType truthType = mcutil::PairTruthType::Unknown; + if constexpr (IsMC) { + if (g1.mc.hasBothLabels() && g2.mc.hasBothLabels()) { + truthType = mcutil::pairTruthType(g1.mc, g2.mc, mcParticles); + truthAxis = static_cast(static_cast(truthType)); + } + } + + fillPairQA<0>(obs, g1, g2); + fillLegSimilarity(g1, g2, qinv); + + if (!passPairCuts(obs, g1, g2)) { + continue; + } + CrossObs cross; + if (qaflags.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + cross = computeCrossObs(g1, g2, qinv); + if (qaflags.cfgDoCrossPairQA.value) { + fillCrossPairQA(cross, obs, truthAxis, g1, g2); + } + const bool vetoed = !passCrossPairVeto(cross); + fRegistry.fill(HIST("Pair/CrossPair/hVetoCounter"), vetoed ? 1.f : 0.f); + if (crosspair.cfgDoCrossPairCut.value && vetoed) { + continue; + } + } + + fillPairQA<1>(obs, g1, g2); + + if constexpr (IsMC) { + if (truthAxis < 0.f) { + continue; + } + fRegistry.fill(HIST("Pair/MC/hTruthTypeVsQinv"), qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/hTruthTypeVsKt"), obs.q.kt, truthAxis); + fRegistry.fill(HIST("Pair/MC/hTruthType_Qinv_DedupFlag"), truthAxis, qinv, dedupWouldRemove ? 1.f : 0.f); + if (truthType == mcutil::PairTruthType::FakeFake || truthType == mcutil::PairTruthType::TrueFake) { + fRegistry.fill(HIST("Pair/MC/hFakeSubtypeVsQinv"), qinv, static_cast(mcutil::classifyFakeSubtype(g1.mc, g2.mc))); + } + fillTypeObservables(truthType, obs); + { + const int c1 = v0LegClass(g1.legCounts), c2 = v0LegClass(g2.legCounts); + fRegistry.fill(HIST("Pair/MC/hV0Class1_V0Class2_Qinv_Type"), static_cast(std::min(c1, c2)), static_cast(std::max(c1, c2)), qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/hV0Class_IsTrue_Qinv"), static_cast(c1), g1.mc.isTruePhoton ? 1.f : 0.f, qinv); + fRegistry.fill(HIST("Pair/MC/hV0Class_IsTrue_Qinv"), static_cast(c2), g2.mc.isTruePhoton ? 1.f : 0.f, qinv); + } + for (const PhotonCand& p : {std::cref(g1), std::cref(g2)}) { + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/MC/hPsiPair_PhiV_Qinv_Type_IsTrue"), std::fabs(p.psipair), p.phiv, qinv, truthAxis, p.mc.isTruePhoton ? 1.f : 0.f); + } + const float rReco = p.rConv(); + const std::array mcLegIds{p.mc.mcPosId, p.mc.mcNegId}; + for (const auto& mcLegId : mcLegIds) { + const auto legPart = mcParticles.iteratorAt(mcLegId); + const float rMc = std::hypot(static_cast(legPart.vx()), static_cast(legPart.vy())); + fRegistry.fill(HIST("Pair/MC/hRreco_minus_Rmc"), rReco - rMc, rMc, p.mc.isTruePhoton ? 1.f : 0.f); + } + } + { + const std::array legIds = {g1.mc.mcPosId, g1.mc.mcNegId, g2.mc.mcPosId, g2.mc.mcNegId}; + std::array ev{}; + int nOwn = 0; + for (size_t il = 0; il < legIds.size(); ++il) { + ev[il] = static_cast(mcParticles.iteratorAt(legIds[il]).emmceventId()); + nOwn += (ev[il] == evOwn) ? 1 : 0; + } + const float pattern = (ev[0] != ev[1] || ev[2] != ev[3]) ? 2.f : (ev[0] != ev[2]) ? 1.f + : 0.f; // o2-linter: disable=magic-number (pattern codes, see booking) + fRegistry.fill(HIST("Pair/MC/hMcEventPattern_NOwn_Qinv_Type"), pattern, static_cast(nOwn), qinv, truthAxis); + } + if (qinv < qaflags.cfgMaxQinvForMCQA.value) { + fillAncestry(mcParticles, g1, g2, obs, truthAxis); + } + } + } + } + } + + template + void runCollisions(TCollisions const& collisions, TPhotons const& photons, TLegs const& /*legs*/, TMCParticles const& mcParticles) + { + std::vector cands; + for (const auto& collision : collisions) { + initCCDB(collision); + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (cent[centralitySelection.cfgCentEstimator] < centralitySelection.cfgCentMin || + centralitySelection.cfgCentMax < cent[centralitySelection.cfgCentEstimator]) { + continue; + } + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); + fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + auto photonsColl = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + collectPhotons(photonsColl, mcParticles, collision.posZ(), cands); + fRegistry.fill(HIST("Photon/hNPhotonsPerEvent"), static_cast(std::min(cands.size(), static_cast(20)))); // o2-linter: disable=magic-number (last bin) + for (const auto& c : cands) { + fRegistry.fill(HIST("Photon/hPtEtaPhi"), c.fPt, c.fEta, c.fPhi); + const auto cls = static_cast(v0LegClass(c.legCounts)); + fRegistry.fill(HIST("Photon/hV0LegClass"), cls); + fRegistry.fill(HIST("Photon/hV0LegClass_vs_Pt"), cls, c.fPt); + fRegistry.fill(HIST("Photon/hV0LegClass_vs_R"), cls, std::min(c.rConv(), 99.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fillAnalyticV0QA(c); + } + runDedupQA(cands); + if (cands.size() < 2) { // o2-linter: disable=magic-number (suplicate number) + continue; + } + runPairQa(collision, cands, mcParticles); + } + } + + void processReco(FilteredMyCollisions const& collisions, MyV0Photons const& v0photons, aod::V0Legs const& v0legs) + { + runCollisions(collisions, v0photons, v0legs, v0legs /* placeholder, unused in data */); + } + PROCESS_SWITCH(PairQCTask, processReco, "pair QC on reconstructed data", true); + + void processMC(FilteredMyMCCollisions const& collisions, MyV0Photons const& v0photons, MyMCV0Legs const& v0legs, + aod::EMMCParticles const& mcParticles, aod::EMMCEvents const& /*mcEvents*/) + { + runCollisions(collisions, v0photons, v0legs, mcParticles); + } + PROCESS_SWITCH(PairQCTask, processMC, "pair QC on MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGEM/PhotonMeson/Utils/AnalyticV0.h b/PWGEM/PhotonMeson/Utils/AnalyticV0.h new file mode 100644 index 00000000000..90bfb05b272 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/AnalyticV0.h @@ -0,0 +1,341 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AnalyticV0.h +/// \brief Build a photon V0 candidate from two legs without a vertex fitter. +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ +#define PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ + +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" // CalculateDCAFast, getPropMomentumFromTrackHelix (soa version) + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +struct LegParam { + std::array xyz{}; // conversion point, vertex-relative (cm) + std::array mom{}; // momentum at the conversion point (GeV/c) + int charge{0}; // +1 e+, -1 e- +}; + +inline o2::track::TrackParCov trackFromLeg(LegParam const& leg) +{ + std::array cov{}; // o2-linter: disable=magic-number (lab-frame covariance has 21 elements) + cov[0] = cov[2] = cov[5] = 0.01f; // o2-linter: disable=magic-number (placeholder sigma_xyz = 0.1 cm) + cov[9] = cov[14] = cov[20] = 1e-4f; // o2-linter: disable=magic-number (placeholder sigma_p = 0.01 GeV/c) + return {leg.xyz, leg.mom, cov, leg.charge, true}; +} + +inline std::array getPropMomentumFromTrackHelix(const float s, const o2::track::TrackParCov& track, + const o2::track::TrackAuxPar& trHelix, const float bz, float addPhi = 0.f) +{ + const float pt = track.getPt(); + const float phi0 = track.getPhi(); // global azimuth + const float dphi = -static_cast(track.getSign()) * 0.3f * bz * s / 100.f / pt; // o2-linter: disable=magic-number (0.3 GeV/(T m), s in cm) + const float dotProd = std::cos(phi0 + addPhi) * pt * trHelix.xC + std::sin(phi0 + addPhi) * pt * trHelix.yC; + if (dotProd < 0.f) { + addPhi -= o2::constants::math::PI; + } + const auto phi = RecoDecay::constrainAngle(phi0 + dphi + addPhi); + return {std::cos(phi) * pt, std::sin(phi) * pt, track.getTgl() * pt}; +} + +struct AnalyticV0 { + bool ok{false}; + std::array vtx{}; // conversion point (cm), vertex-relative + std::array mom{}; // photon momentum at the vertex (GeV/c) + float pca{999.f}; // distance of the two legs at the vertex (cm) + float cospa{-2.f}; // pointing of mom from the origin (= primary vertex) + float mee{-1.f}; // m(e+e-) at the vertex (GeV/c^2) + float dcaxy{999.f}; // line through vtx along mom: distance to the origin in xy (cm) + float dcaz{999.f}; // line through vtx along mom in z at the xy-closest point (cm) + float score{999.f}; // the builder's getScoreV0 + std::array, 2> legMom{}; // e+ and e- momentum at the vertex (GeV/c) + void armenteros(float& alpha, float& qt) const + { + const float pMag = std::sqrt(mom[0] * mom[0] + mom[1] * mom[1] + mom[2] * mom[2]); + if (pMag < 1e-9f) { // o2-linter: disable=magic-number (degenerate) + alpha = 2.f; + qt = 999.f; + return; + } + const auto& pp = legMom[0]; + const auto& pn = legMom[1]; + const float plP = (pp[0] * mom[0] + pp[1] * mom[1] + pp[2] * mom[2]) / pMag; + const float plN = (pn[0] * mom[0] + pn[1] * mom[1] + pn[2] * mom[2]) / pMag; + const float pP2 = pp[0] * pp[0] + pp[1] * pp[1] + pp[2] * pp[2]; + alpha = (plP + plN > 1e-9f) ? (plP - plN) / (plP + plN) : 2.f; // o2-linter: disable=magic-number (degenerate) + qt = std::sqrt(std::max(0.f, pP2 - plP * plP)); + } + [[nodiscard]] float rxy() const { return std::hypot(vtx[0], vtx[1]); } + [[nodiscard]] float pt() const { return std::hypot(mom[0], mom[1]); } + [[nodiscard]] float eta() const + { + const float p = std::sqrt(mom[0] * mom[0] + mom[1] * mom[1] + mom[2] * mom[2]); + return (p > mom[2]) ? 0.5f * std::log((p + mom[2]) / (p - mom[2])) : 0.f; + } + [[nodiscard]] float phi() const { return RecoDecay::constrainAngle(std::atan2(mom[1], mom[0])); } +}; + +inline float analyticScoreV0(float cospa, float pca, float w) +{ + return w * 60.f * std::acos(cospa) + (1.f - w) * pca / 3.f; // o2-linter: disable=magic-number (getScoreV0 of the builder) +} + +inline AnalyticV0 buildAnalyticV0(LegParam const& pos, LegParam const& neg, float bzkG, float scoreWeight) +{ + constexpr float kMe = 0.000510999f; // electron mass, GeV/c^2 + constexpr float kTiny = 1e-6f; + AnalyticV0 v; + if (std::fabs(bzkG) < 0.1f) { // o2-linter: disable=magic-number (no field) + return v; + } + const auto tP = trackFromLeg(pos); + const auto tN = trackFromLeg(neg); + const o2::track::TrackAuxPar h1(tP, bzkG); + const o2::track::TrackAuxPar h2(tN, bzkG); + + // 1. xy + const float dcx = h2.xC - h1.xC, dcy = h2.yC - h1.yC; + const float d = std::hypot(dcx, dcy); + if (d < 1e-3f) { // o2-linter: disable=magic-number (concentric circles) + return v; + } + const float ux = dcx / d, uy = dcy / d; + const float r1 = h1.rC, r2 = h2.rC; + + int nCand = 0; + std::array c1x{}, c1y{}, c2x{}, c2y{}; + float pcaXY = 0.f; + if (d > r1 + r2) { // external gap: tangent points along the centre line + c1x[0] = h1.xC + r1 * ux; + c1y[0] = h1.yC + r1 * uy; + c2x[0] = h2.xC - r2 * ux; + c2y[0] = h2.yC - r2 * uy; + pcaXY = d - r1 - r2; + nCand = 1; + } else if (d < std::fabs(r1 - r2)) { // one circle inside the other: closest points on the far side + const float sgn = (r1 > r2) ? 1.f : -1.f; + c1x[0] = h1.xC + sgn * r1 * ux; + c1y[0] = h1.yC + sgn * r1 * uy; + c2x[0] = h2.xC + sgn * r2 * ux; + c2y[0] = h2.yC + sgn * r2 * uy; + pcaXY = std::fabs(r1 - r2) - d; + nCand = 1; + } else { // the circles cross: two points with xy distance 0 + const float a = (r1 * r1 - r2 * r2 + d * d) / (2.f * d); + const float hh = std::sqrt(std::max(0.f, r1 * r1 - a * a)); + const float bx = h1.xC + a * ux, by = h1.yC + a * uy; + c1x[0] = c2x[0] = bx - hh * uy; + c1y[0] = c2y[0] = by + hh * ux; + c1x[1] = c2x[1] = bx + hh * uy; + c1y[1] = c2y[1] = by - hh * ux; + pcaXY = 0.f; + nCand = 2; + } + + int best = 0; + float z1 = 0.f, z2 = 0.f, dzBest = 1e9f; + for (int ic = 0; ic < nCand; ++ic) { + const o2::math_utils::Point3D xy{0.5f * (c1x[ic] + c2x[ic]), 0.5f * (c1y[ic] + c2y[ic]), 0.f}; + const auto dcaP = CalculateDCAFast(tP, xy, bzkG); + const auto dcaN = CalculateDCAFast(tN, xy, bzkG); + if (std::fabs(dcaP[1] - dcaN[1]) < dzBest) { + dzBest = std::fabs(dcaP[1] - dcaN[1]); + z1 = dcaP[1]; + z2 = dcaN[1]; + best = ic; + } + } + const float p1x = c1x[best], p1y = c1y[best]; + const float p2x = c2x[best], p2y = c2y[best]; + const float convX = 0.5f * (p1x + p2x), convY = 0.5f * (p1y + p2y); + const float convZ = 0.5f * (z1 + z2); + v.vtx = {convX, convY, convZ}; + v.pca = std::hypot(pcaXY, z1 - z2); + + auto arcTo = [](o2::track::TrackAuxPar const& h, float xTrk, float yTrk, float px_, float py_) { + const float th0 = std::atan2(yTrk - h.yC, xTrk - h.xC); + const float thv = std::atan2(py_ - h.yC, px_ - h.xC); + const auto dth = RecoDecay::constrainAngle(thv - th0, -o2::constants::math::PI); + return std::fabs(h.rC * dth); + }; + const auto gP = tP.getXYZGlo(); + const auto gN = tN.getXYZGlo(); + const float bzT = bzkG / 10.f; // o2-linter: disable=magic-number (kGauss -> Tesla) + const auto pP = getPropMomentumFromTrackHelix(arcTo(h1, gP.X(), gP.Y(), p1x, p1y), tP, h1, bzT); + const auto pN = getPropMomentumFromTrackHelix(arcTo(h2, gN.X(), gN.Y(), p2x, p2y), tN, h2, bzT); + v.mom = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + v.legMom = {pP, pN}; + + const float pMag = std::sqrt(v.mom[0] * v.mom[0] + v.mom[1] * v.mom[1] + v.mom[2] * v.mom[2]); + const float vMag = std::sqrt(convX * convX + convY * convY + convZ * convZ); + if (pMag < kTiny || vMag < 1e-3f) { // o2-linter: disable=magic-number (degenerate vertex) + return v; + } + v.cospa = std::clamp((convX * v.mom[0] + convY * v.mom[1] + convZ * v.mom[2]) / (pMag * vMag), -1.f, 1.f); + const float ptSum = std::hypot(v.mom[0], v.mom[1]); + if (ptSum > kTiny) { + v.dcaxy = std::fabs(convX * v.mom[1] - convY * v.mom[0]) / ptSum; + const float t = -(convX * v.mom[0] + convY * v.mom[1]) / (ptSum * ptSum); + v.dcaz = std::fabs(convZ + t * v.mom[2]); + } + const float e0 = std::sqrt(pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2] + kMe * kMe); + const float e1 = std::sqrt(pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2] + kMe * kMe); + v.mee = std::sqrt(std::max(0.f, (e0 + e1) * (e0 + e1) - pMag * pMag)); + v.score = analyticScoreV0(v.cospa, v.pca, scoreWeight); + v.ok = true; + return v; +} + +struct PhotonLikeCuts { + float maxDca{1.5f}, minR{1.f}, maxR{90.f}, minCosPA{0.98f}, maxMee{0.1f}, maxDcaXY{3.f}, maxDcaZ{999.f}; // o2-linter: disable=magic-number (defaults = crosspair_group) +}; + +inline bool isPhotonLike(AnalyticV0 const& v, PhotonLikeCuts const& c) +{ + return v.ok && v.pca < c.maxDca && v.rxy() > c.minR && v.rxy() < c.maxR && + v.cospa > c.minCosPA && v.mee < c.maxMee && v.dcaxy < c.maxDcaXY && v.dcaz < c.maxDcaZ; +} + +struct AltPairing { + bool fitted{false}; + float dca{999.f}, rxy{-1.f}, cospa{-2.f}, mee{-1.f}, dcaz{999.f}, dcaxy{999.f}; +}; + +//_______________________________________________________________________ +/// \brief Build the candidate with the DCAFitter +/// \param pos e+ leg +/// \param neg e- leg +/// \param bzT field in Tesla +/// \param scoreWeight w of analyticScoreV0 +/// \return the candidate; ok = false when the fitter does not converge +inline AnalyticV0 buildFitterV0(o2::track::TrackParCov tPos, o2::track::TrackParCov tEle, float bzT, float scoreWeight) +{ + constexpr float kMe = 0.000510999f; // electron mass, GeV/c^2 + constexpr float kTiny = 1e-12f; + static o2::vertexing::DCAFitterN<2> fitter; + AnalyticV0 v; + if (std::fabs(bzT) < 1e-9f) { // o2-linter: disable=magic-number (no field) + return v; + } + fitter.setBz(10.f * bzT); // o2-linter: disable=magic-number (Tesla -> kGauss) + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.f); // o2-linter: disable=magic-number (fitter range, cm) + fitter.setMaxDZIni(8.f); // o2-linter: disable=magic-number (as the SVertexer photon tune) + fitter.setMaxDXYIni(8.f); // o2-linter: disable=magic-number (as the SVertexer photon tune) + fitter.setMaxChi2(1e9f); // o2-linter: disable=magic-number (no chi2 cut; the distance is judged afterwards) + fitter.setMinParamChange(1e-3f); // o2-linter: disable=magic-number (fitter convergence) + fitter.setMinRelChi2Change(0.9f); // o2-linter: disable=magic-number (fitter convergence) + fitter.setUseAbsDCA(true); + fitter.setCollinear(true); + if (fitter.process(tPos, tEle) == 0) { + return v; + } + if (!fitter.isPropagateTracksToVertexDone(0) && !fitter.propagateTracksToVertex(0)) { + return v; + } + const auto& vtx = fitter.getPCACandidatePos(0); + std::array x0{}, x1{}, p0{}, p1{}; + fitter.getTrack(0, 0).getXYZGlo(x0); + fitter.getTrack(1, 0).getXYZGlo(x1); + if (!fitter.getTrack(0, 0).getPxPyPzGlo(p0) || !fitter.getTrack(1, 0).getPxPyPzGlo(p1)) { + return v; + } + v.vtx = {static_cast(vtx[0]), static_cast(vtx[1]), static_cast(vtx[2])}; + v.mom = {p0[0] + p1[0], p0[1] + p1[1], p0[2] + p1[2]}; + v.legMom = {p0, p1}; + v.pca = std::sqrt((x0[0] - x1[0]) * (x0[0] - x1[0]) + (x0[1] - x1[1]) * (x0[1] - x1[1]) + (x0[2] - x1[2]) * (x0[2] - x1[2])); + const float pMag = std::sqrt(v.mom[0] * v.mom[0] + v.mom[1] * v.mom[1] + v.mom[2] * v.mom[2]); + const float vMag = std::sqrt(v.vtx[0] * v.vtx[0] + v.vtx[1] * v.vtx[1] + v.vtx[2] * v.vtx[2]); + if (pMag < kTiny || vMag < kTiny) { + return v; + } + v.cospa = std::clamp((v.vtx[0] * v.mom[0] + v.vtx[1] * v.mom[1] + v.vtx[2] * v.mom[2]) / (pMag * vMag), -1.f, 1.f); + const float ptSum = std::hypot(v.mom[0], v.mom[1]); + if (ptSum > kTiny) { + v.dcaxy = std::fabs(v.vtx[0] * v.mom[1] - v.vtx[1] * v.mom[0]) / ptSum; + const float t = -(v.vtx[0] * v.mom[0] + v.vtx[1] * v.mom[1]) / (ptSum * ptSum); + v.dcaz = std::fabs(v.vtx[2] + t * v.mom[2]); + } + const float e0 = std::sqrt(p0[0] * p0[0] + p0[1] * p0[1] + p0[2] * p0[2] + kMe * kMe); + const float e1 = std::sqrt(p1[0] * p1[0] + p1[1] * p1[1] + p1[2] * p1[2] + kMe * kMe); + v.mee = std::sqrt(std::max(0.f, (e0 + e1) * (e0 + e1) - pMag * pMag)); + v.score = analyticScoreV0(v.cospa, v.pca, scoreWeight); + v.ok = true; + return v; +} + +//_______________________________________________________________________ +/// \brief buildFitterV0 for two legs given as (point, momentum, charge) +inline AnalyticV0 buildFitterV0(LegParam const& pos, LegParam const& neg, float bzT, float scoreWeight) +{ + constexpr float kFitterMaxR = 200.f; // o2-linter: disable=magic-number (the fitter's own setMaxR) + const AnalyticV0 seed = buildAnalyticV0(pos, neg, 10.f * bzT, scoreWeight); // o2-linter: disable=magic-number (Tesla -> kGauss) + if (!seed.ok || seed.rxy() > kFitterMaxR) { + return AnalyticV0{}; + } + return buildFitterV0(trackFromLeg(pos), trackFromLeg(neg), bzT, scoreWeight); +} + +//_______________________________________________________________________ +/// \brief Move a track in the global frame +/// \param t the track; its local frame is rotated by alpha, so a global shift enters x and y rotated +/// \param dx shift in global x (cm) +/// \param dy shift in global y (cm) +/// \param dz shift in global z (cm) +inline void translateTrackParCov(o2::track::TrackParCov& t, float dx, float dy, float dz) +{ + const float ca = std::cos(t.getAlpha()), sa = std::sin(t.getAlpha()); + t.setX(t.getX() + ca * dx + sa * dy); + t.setY(t.getY() - sa * dx + ca * dy); + t.setZ(t.getZ() + dz); +} + +//_______________________________________________________________________ +/// \brief The DCAFitter alternative pairing +/// \param pos e+ leg +/// \param neg e- leg +/// \param bzT field in Tesla +/// \return AltPairing; fitted = false when the fitter does not converge +inline AltPairing evaluateAltPairingFitter(LegParam const& pos, LegParam const& neg, float bzT) +{ + const AnalyticV0 v = buildFitterV0(pos, neg, bzT, 0.5f); // o2-linter: disable=magic-number (score not used here) + AltPairing r; + if (!v.ok) { + return r; + } + r.fitted = true; + r.dca = v.pca; + r.rxy = v.rxy(); + r.cospa = v.cospa; + r.mee = v.mee; + r.dcaxy = v.dcaxy; + r.dcaz = v.dcaz; + return r; +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ diff --git a/PWGEM/PhotonMeson/Utils/MCUtilities.h b/PWGEM/PhotonMeson/Utils/MCUtilities.h index 4a31a33e51b..270d6d2ead9 100644 --- a/PWGEM/PhotonMeson/Utils/MCUtilities.h +++ b/PWGEM/PhotonMeson/Utils/MCUtilities.h @@ -18,6 +18,7 @@ #include "PWGEM/PhotonMeson/Utils/ParticleOrigin.h" +#include #include #include @@ -25,9 +26,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -479,6 +482,536 @@ o2::analysis::em::LeptonOrigin getLeptonOriginType(TIter const& mcLepton, TIter& } } +//_______________________________________________________________________ +/// \brief Photon mother of a conversion electron, addressed by MC index +/// \param mcParticles table of MC particles (aod::McParticles or aod::EMMCParticles) +/// \param mcId index of the MC particle behind a track or V0 leg, -1 if the track carries no label +/// \return index of the mother photon if the particle is a transport electron with a photon as first mother, else -1 +template +int photonMotherId(TMCParticles const& mcParticles, const int mcId) +{ + if (mcId < 0) { + return -1; + } + return IsEleFromPC(mcParticles.iteratorAt(mcId), mcParticles); +} + +//_______________________________________________________________________ +/// \brief MC leg information for a V0 candidate +struct PhotonMCInfo { + int mcPosId = -1; /// MC particle of the positive leg, -1 = no label + int mcNegId = -1; /// MC particle of the negative leg, -1 = no label + int posPhotonId = -1; /// photon mother of the positive leg (photonMotherId), -1 = not a conversion electron + int negPhotonId = -1; /// same for the negative leg + int motherId = -1; /// common first mother of both legs, whatever its species, -1 = none + int motherPdg = 0; /// PDG code of that common mother + bool isTruePhoton = false; /// both legs are conversion electrons of the SAME photon + bool isPhysicalPrimary = false; /// the common mother is a physical primary + [[nodiscard]] bool hasBothLabels() const { return mcPosId >= 0 && mcNegId >= 0; } + [[nodiscard]] bool bothLegsFromPhotons() const { return posPhotonId >= 0 && negPhotonId >= 0; } +}; + +//_______________________________________________________________________ +/// \brief Fills PhotonMCInfo from the two leg labels +/// \param mcParticles table of MC particles +/// \param mcPosId MC index of the positive leg, -1 if unlabelled +/// \param mcNegId MC index of the negative leg, -1 if unlabelled +/// \return truth record of the candidate; with one unlabelled leg only that leg's fields stay at -1 +template +PhotonMCInfo makePhotonMCInfo(TMCParticles const& mcParticles, const int mcPosId, const int mcNegId) +{ + PhotonMCInfo info; + info.mcPosId = mcPosId; + info.mcNegId = mcNegId; + info.posPhotonId = photonMotherId(mcParticles, mcPosId); + info.negPhotonId = photonMotherId(mcParticles, mcNegId); + info.isTruePhoton = (info.posPhotonId >= 0) && (info.posPhotonId == info.negPhotonId); + if (!info.hasBothLabels()) { + return info; + } + const auto mcPos = mcParticles.iteratorAt(mcPosId); + const auto mcNeg = mcParticles.iteratorAt(mcNegId); + const int motherPos = mcPos.has_mothers() ? mcPos.mothersIds()[0] : -1; + const int motherNeg = mcNeg.has_mothers() ? mcNeg.mothersIds()[0] : -1; + if (motherPos < 0 || motherPos != motherNeg) { + return info; // no common valid mother + } + const auto mother = mcParticles.iteratorAt(motherPos); + info.motherId = motherPos; + info.motherPdg = mother.pdgCode(); + info.isPhysicalPrimary = mother.isPhysicalPrimary(); + return info; +} + +//_______________________________________________________________________ +/// \brief Fills PhotonMCInfo for a V0 photon of the skimmed tables +/// \tparam TLegs leg table joined with the MC labels (e.g. soa::Join) +/// \param v0 iterator of the V0 photon +/// \param mcParticles table of aod::EMMCParticles +/// \return truth record of this V0 +template +PhotonMCInfo makePhotonMCInfo(TV0 const& v0, TMCParticles const& mcParticles) +{ + const auto pos = v0.template posTrack_as(); + const auto neg = v0.template negTrack_as(); + return makePhotonMCInfo(mcParticles, + pos.has_emmcparticle() ? static_cast(pos.emmcparticleId()) : -1, + neg.has_emmcparticle() ? static_cast(neg.emmcparticleId()) : -1); +} + +//_______________________________________________________________________ +/// \brief Truth class of a pair of two V0 candidates +enum class PairTruthType : int { + Unknown = 0, + TrueTrueDistinct, /// two different true photons + TrueTrueSamePhoton, /// the same true photon twice (duplicate V0) + SharedMcLeg, /// the two V0s contain the same MC particle as a leg + TrueFake, /// one true photon, one fake + FakeFake, /// two fakes + Pi0Daughters, /// two true photons from the same pi0 + NClasses +}; + +//_______________________________________________________________________ +/// \brief Truth class of a pair from the two V0 records, without the pi0 check +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return PairTruthType; TrueTrueSamePhoton has priority over SharedMcLeg, the shared-leg test runs across charges +[[nodiscard]] inline PairTruthType classifyPairTruth(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + if (m1.isTruePhoton && m2.isTruePhoton && m1.posPhotonId == m2.posPhotonId) { + return PairTruthType::TrueTrueSamePhoton; + } + const bool sharedLeg = + (m1.mcPosId >= 0 && (m1.mcPosId == m2.mcPosId || m1.mcPosId == m2.mcNegId)) || + (m1.mcNegId >= 0 && (m1.mcNegId == m2.mcPosId || m1.mcNegId == m2.mcNegId)); + if (sharedLeg) { + return PairTruthType::SharedMcLeg; + } + if (!m1.isTruePhoton && !m2.isTruePhoton) { + return PairTruthType::FakeFake; + } + if (m1.isTruePhoton != m2.isTruePhoton) { + return PairTruthType::TrueFake; + } + return PairTruthType::TrueTrueDistinct; +} + +//_______________________________________________________________________ +/// \brief Are two true photons daughters of the same pi0 +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \param mcParticles table of MC particles +/// \return true if both V0s are true photons and IsXFromY finds the same pi0 as first mother of both photons +template +bool isPi0DaughterPair(PhotonMCInfo const& m1, PhotonMCInfo const& m2, TMCParticles const& mcParticles) +{ + if (!m1.isTruePhoton || !m2.isTruePhoton) { + return false; + } + const int pi0Of1 = IsXFromY(mcParticles.iteratorAt(m1.posPhotonId), mcParticles, PDG_t::kGamma, PDG_t::kPi0); + const int pi0Of2 = IsXFromY(mcParticles.iteratorAt(m2.posPhotonId), mcParticles, PDG_t::kGamma, PDG_t::kPi0); + return pi0Of1 >= 0 && pi0Of1 == pi0Of2; +} + +//_______________________________________________________________________ +/// \brief Full truth class of a pair, as the HBT task bins it +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \param mcParticles table of MC particles +/// \return classifyPairTruth, with TrueTrueDistinct promoted to Pi0Daughters where isPi0DaughterPair holds +template +PairTruthType pairTruthType(PhotonMCInfo const& m1, PhotonMCInfo const& m2, TMCParticles const& mcParticles) +{ + const PairTruthType t = classifyPairTruth(m1, m2); + if (t == PairTruthType::TrueTrueDistinct && isPi0DaughterPair(m1, m2, mcParticles)) { + return PairTruthType::Pi0Daughters; + } + return t; +} + +//_______________________________________________________________________ +/// \brief Classifies the V0 cross-wise built candidates. The crossed pairs are evaluted whether it is from the same photon or not. +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return 1 if both directions cross (full 2x2 cross: same two photons, legs swapped), 2 if only one direction crosses, 0 otherwise +[[nodiscard]] inline int classifyFakeSubtype(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + const bool cross12 = m1.posPhotonId >= 0 && m1.posPhotonId == m2.negPhotonId; + const bool cross21 = m2.posPhotonId >= 0 && m2.posPhotonId == m1.negPhotonId; + if (cross12 && cross21) { + return 1; + } + return (cross12 || cross21) ? 2 : 0; // o2-linter: disable=magic-number (class code, see brief) +} + +//_______________________________________________________________________ +/// \brief Particle a mother chain ends at +enum class AncestorKind : int { + NoneFound = 0, /// no common ancestor within the search depth + StringOrCluster, /// PYTHIA string or cluster, or a bare parton (quark, gluon) + BeamOrNucleus, /// beam particle, nucleus, nucleon or diquark remnant + Pi0, + Eta, + OtherMeson, + Baryon, + Photon, // the ancestor is a photon: one particle descends from the other + Electron, + Other, + NClasses +}; + +//_______________________________________________________________________ +/// \brief Maps a PDG code onto AncestorKind +/// \param pdgIn PDG code, the sign is ignored +/// \return AncestorKind of the code; ranges of codes, not single particles +inline AncestorKind ancestorKindOfPdg(int pdgIn) +{ + const int pdg = std::abs(pdgIn); + constexpr int kStringLo = 91, kStringHi = 94; + constexpr int kDiquarkLo = 1103, kDiquarkHi = 5503; + constexpr int kNucleusOffset = 1000000000; + constexpr int kMesonHi = 999; + constexpr int kBaryonHi = 9999; + constexpr int kQuarkHi = 8, kGluon = 21; + if ((pdg >= kStringLo && pdg <= kStringHi) || (pdg >= 1 && pdg <= kQuarkHi) || pdg == kGluon) { + return AncestorKind::StringOrCluster; + } + if (pdg >= kNucleusOffset || (pdg >= kDiquarkLo && pdg <= kDiquarkHi)) { + return AncestorKind::BeamOrNucleus; + } + if (pdg == PDG_t::kPi0) { + return AncestorKind::Pi0; + } + if (pdg == o2::constants::physics::Pdg::kEta) { + return AncestorKind::Eta; + } + if (pdg == PDG_t::kGamma) { + return AncestorKind::Photon; + } + if (pdg == PDG_t::kElectron) { + return AncestorKind::Electron; + } + if (pdg == PDG_t::kProton || pdg == PDG_t::kNeutron) { + return AncestorKind::BeamOrNucleus; + } + if (pdg > 0 && pdg <= kMesonHi) { + return AncestorKind::OtherMeson; + } + if (pdg > kMesonHi && pdg <= kBaryonHi) { + return AncestorKind::Baryon; + } + return AncestorKind::Other; +} + +//_______________________________________________________________________ +/// \brief The particle a chain originates from, stopping before the beam +/// \param mcParticles table of MC particles +/// \param id index of the particle to start from +/// \param maxGen safety cap against self-referencing mother links, not a physics limit +/// \return index of the origin particle, or -1 if id was invalid +template +int originParticleId(TMCParticles const& mcParticles, int id, int maxGen = 50) +{ + if (id < 0) { + return -1; + } + int cur = id; + for (int gen = 0; gen < maxGen; ++gen) { + const auto p = mcParticles.iteratorAt(cur); + if (!p.has_mothers()) { + return cur; + } + const int mother = p.mothersIds()[0]; + if (mother < 0 || mother == cur) { + return cur; + } + const AncestorKind kindOfMother = ancestorKindOfPdg(mcParticles.iteratorAt(mother).pdgCode()); + if (kindOfMother == AncestorKind::BeamOrNucleus || kindOfMother == AncestorKind::StringOrCluster) { + return cur; + } + cur = mother; + } + return cur; +} + +//_______________________________________________________________________ +/// \brief Chains meetup like in findCommonAncestor +struct CommonAncestor { + int id{-1}; /// index of the lowest common ancestor, -1 if none was found + int gen1{-1}; /// generations from the first particle up to it (0 = it IS that particle) + int gen2{-1}; /// the same for the second particle +}; + +//_______________________________________________________________________ +/// \brief Lowest common ancestor of two MC particles +/// \param mcParticles table of MC particles +/// \param id1 index of the first particle +/// \param id2 index of the second particle +/// \param maxGen how many generations to walk on each side +/// \param stopAtBeam treat beam, nucleus and string as "no mother" +/// \return CommonAncestor with id = -1 if the chains do not meet within maxGen +template +CommonAncestor findCommonAncestor(TMCParticles const& mcParticles, int id1, int id2, + int maxGen = 4, bool stopAtBeam = false) +{ + CommonAncestor out; + if (id1 < 0 || id2 < 0) { + return out; + } + auto motherOf = [&mcParticles, stopAtBeam](int id) -> int { + const auto particle = mcParticles.iteratorAt(id); + if (!particle.has_mothers()) { + return -1; + } + const int mother = particle.mothersIds()[0]; + if (mother < 0 || mother == id) { + return -1; + } + if (stopAtBeam) { + const AncestorKind kind = ancestorKindOfPdg(mcParticles.iteratorAt(mother).pdgCode()); + if (kind == AncestorKind::BeamOrNucleus || kind == AncestorKind::StringOrCluster) { + return -1; + } + } + return mother; + }; + std::vector chain1; + chain1.reserve(static_cast(maxGen) + 1); + for (int id = id1, gen = 0; id >= 0 && gen <= maxGen; ++gen) { + chain1.push_back(id); + id = motherOf(id); + } + for (int id = id2, gen = 0; id >= 0 && gen <= maxGen; ++gen) { + const auto it = std::find(chain1.begin(), chain1.end(), id); + if (it != chain1.end()) { + out.id = id; + out.gen1 = static_cast(std::distance(chain1.begin(), it)); + out.gen2 = gen; + return out; + } + id = motherOf(id); + } + return out; +} + +//_______________________________________________________________________ +/// \brief Class of the lowest common ancestor of two MC particles +/// \param mcParticles table of MC particles +/// \param id1 index of the first particle +/// \param id2 index of the second particle +/// \param maxGen how many generations to walk on each side +/// \return AncestorKind of the common ancestor, AncestorKind::NoneFound when the chains do not meet within maxGen +template +AncestorKind commonAncestorKind(TMCParticles const& mcParticles, int id1, int id2, int maxGen = 4) +{ + const CommonAncestor anc = findCommonAncestor(mcParticles, id1, id2, maxGen, false); + if (anc.id < 0) { + return AncestorKind::NoneFound; + } + return ancestorKindOfPdg(mcParticles.iteratorAt(anc.id).pdgCode()); +} + +//_______________________________________________________________________ +/// \brief Distinct parents for mc truth +/// V0_a = (e+_1, e-_1), V0_b = (e+_2, e-_2) N=2, consistent=2, shared=no signal +/// V0_a = (e+_1, e-_2), V0_b = (e+_2, e-_1) N=2, consistent=0, shared=yes leg swap +/// V0_a = (e+_1, e-_1), V0_b = (e+_2, e-_3) N=3, consistent=1, shared=no one real + one swap +/// V0_a = (e+_1, e-_2), V0_b = (e+_3, e-_4) N=4, consistent=0, shared=no pure combinatorics +/// V0_a = (e+_1, e-_1), V0_b = (e+_1',e-_1') N=1, consistent=2, shared=yes duplicate (split track) +/// V0_a = (e+_1, X), V0_b = (Y, e-_1) N=1, consistent=0, shared=yes one photon, two foreign tracks +struct MotherCensus { + int nConvLegs = 0; /// legs whose mother is a photon (0-4) + int nMothers = 0; /// distinct parent photons among them (0-4) + int nConsistent = 0; /// V0s whose two legs share the same parent photon (0-2) + bool shared = false; /// one parent photon appears in BOTH V0s + std::array distinct{{-1, -1, -1, -1}}; + [[nodiscard]] int splitCode() const { return 2 * nConsistent + (shared ? 1 : 0); } // o2-linter: disable=magic-number (two values per nConsistent) +}; + +//_______________________________________________________________________ +/// \brief Census of the four legs of a photon pair +/// \param motherPhotonIds the parent photon of each leg, in the order (V0 A e+, V0 A e-, V0 B e+, V0 B e-); +/// \return MotherCensus of the four legs +inline MotherCensus censusMothers(std::array const& motherPhotonIds) +{ + constexpr int kNLegs = 4; + const auto& mp = motherPhotonIds; + MotherCensus c; + for (int i = 0; i < kNLegs; ++i) { + const auto ui = static_cast(i); + if (mp[ui] < 0) { + continue; + } + c.nConvLegs++; + bool seen = false; + for (int j = 0; j < i; ++j) { // only the first appearance counts as a new mother + seen = seen || (mp[static_cast(j)] == mp[ui]); + } + if (!seen) { + c.distinct[static_cast(c.nMothers)] = mp[ui]; // nMothers < 4: at most four legs + c.nMothers++; + } + } + c.nConsistent = ((mp[0] >= 0 && mp[0] == mp[1]) ? 1 : 0) + ((mp[2] >= 0 && mp[2] == mp[3]) ? 1 : 0); + for (int i = 0; i < 2; ++i) { // o2-linter: disable=magic-number (the two legs of the first V0) + for (int j = 2; j < kNLegs; ++j) { + c.shared = c.shared || (mp[static_cast(i)] >= 0 && mp[static_cast(i)] == mp[static_cast(j)]); + } + } + return c; +} + +//_______________________________________________________________________ +/// \brief Amount of V0 mothers per legs. +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return censusMothers of (m1 e+, m1 e-, m2 e+, m2 e-) +[[nodiscard]] inline MotherCensus censusMothers(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + return censusMothers({m1.posPhotonId, m1.negPhotonId, m2.posPhotonId, m2.negPhotonId}); +} + +//_______________________________________________________________________ +/// \brief Analysis for two photons based on their MC track label content. +enum class DupClass : int { + Unknown = 0, /// at least one candidate without both labels + SameMcPhoton, /// a duplicate + SharedMcLeg, /// share an MC particle as a leg, but different mother photons + CrossBuilt, /// legs of two true photons built crosswise (swap) + DistinctTrue, /// two different, cleanly reconstructed photons + InvolvesFake, /// at least one is not a true photon, no swap + NClasses +}; + +//_______________________________________________________________________ +/// \brief Duplicate class of two candidates from their truth records +/// \param a truth record of the first candidate +/// \param b truth record of the second candidate +/// \return DupClass; the shared-leg test runs before the label requirement, a V0 with one labelled leg can still share it +[[nodiscard]] inline DupClass classifyDupPair(PhotonMCInfo const& a, PhotonMCInfo const& b) +{ + const bool sharedLeg = + (a.mcPosId >= 0 && (a.mcPosId == b.mcPosId || a.mcPosId == b.mcNegId)) || + (a.mcNegId >= 0 && (a.mcNegId == b.mcPosId || a.mcNegId == b.mcNegId)); + if (!a.hasBothLabels() || !b.hasBothLabels()) { + return sharedLeg ? DupClass::SharedMcLeg : DupClass::Unknown; + } + if (a.isTruePhoton && b.isTruePhoton && a.posPhotonId == b.posPhotonId) { + return DupClass::SameMcPhoton; + } + if (sharedLeg) { + return DupClass::SharedMcLeg; + } + if (classifyFakeSubtype(a, b) != 0) { + return DupClass::CrossBuilt; + } + if (a.isTruePhoton && b.isTruePhoton) { + return DupClass::DistinctTrue; + } + return DupClass::InvolvesFake; +} + +//_______________________________________________________________________ +/// \brief How one V0 candidate was built, by MC truth +enum class V0RecoStatus : int { + NoLabel = 0, ///< at least one leg without MC label + NoConvLeg, ///< no leg is a conversion electron + OneConvLeg, ///< one conversion electron, one foreign track + CrossBuilt, ///< two conversion electrons of DIFFERENT photons + TruePhoton, ///< two conversion electrons of the same photon + NClasses +}; + +//_______________________________________________________________________ +/// \brief Reco status of a candidate from its truth record +/// \param m truth record of the candidate +/// \return V0RecoStatus +[[nodiscard]] inline V0RecoStatus classifyV0Reco(PhotonMCInfo const& m) +{ + if (!m.hasBothLabels()) { + return V0RecoStatus::NoLabel; + } + if (m.isTruePhoton) { + return V0RecoStatus::TruePhoton; + } + if (m.bothLegsFromPhotons()) { + return V0RecoStatus::CrossBuilt; + } + return (m.posPhotonId >= 0 || m.negPhotonId >= 0) ? V0RecoStatus::OneConvLeg : V0RecoStatus::NoConvLeg; +} + +//_______________________________________________________________________ +/// \brief True conversion point of a photon: the production vertex of its first e+/e- daughter +/// \param mcParticles table of MC particles +/// \param photonId index of the photon +/// \param xyz output, (vx, vy, vz) of the conversion (cm) +/// \return true if the photon has an electron daughter, false otherwise (xyz untouched) +template +bool conversionPoint(TMCParticles const& mcParticles, const int photonId, std::array& xyz) +{ + if (photonId < 0) { + return false; + } + const auto photon = mcParticles.iteratorAt(photonId); + if (!photon.has_daughters()) { + return false; + } + for (const auto& dId : photon.daughtersIds()) { + if (dId < 0) { + continue; + } + const auto d = mcParticles.iteratorAt(dId); + if (std::abs(d.pdgCode()) == PDG_t::kElectron) { + xyz = {static_cast(d.vx()), static_cast(d.vy()), static_cast(d.vz())}; + return true; + } + } + return false; +} + +//_______________________________________________________________________ +/// \brief True q_inv of two MC photons (massless) +/// \param mcParticles table of MC particles +/// \param id1 index of the first photon +/// \param id2 index of the second photon +/// \return q_inv = sqrt(2 (E1 E2 - p1.p2)) in GeV/c, -1 if an index is invalid +template +float trueQinvOfPhotons(TMCParticles const& mcParticles, const int id1, const int id2) +{ + if (id1 < 0 || id2 < 0) { + return -1.f; + } + const auto pa = mcParticles.iteratorAt(id1); + const auto pb = mcParticles.iteratorAt(id2); + const float e1 = std::hypot(pa.px(), pa.py(), pa.pz()); + const float e2 = std::hypot(pb.px(), pb.py(), pb.pz()); + return std::sqrt(std::max(0.f, 2.f * (e1 * e2 - pa.px() * pb.px() - pa.py() * pb.py() - pa.pz() * pb.pz()))); +} + +//_______________________________________________________________________ +/// \brief True opening angle of two MC photons +/// \param mcParticles table of MC particles +/// \param id1 index of the first photon +/// \param id2 index of the second photon +/// \return angle between the two momenta in rad, -1 if an index is invalid +template +float trueOpeningAngle(TMCParticles const& mcParticles, const int id1, const int id2) +{ + if (id1 < 0 || id2 < 0) { + return -1.f; + } + const auto pa = mcParticles.iteratorAt(id1); + const auto pb = mcParticles.iteratorAt(id2); + const float n1 = std::hypot(pa.px(), pa.py(), pa.pz()); + const float n2 = std::hypot(pb.px(), pb.py(), pb.pz()); + if (n1 < 1e-9f || n2 < 1e-9f) { // o2-linter: disable=magic-number (zero momentum guard) + return -1.f; + } + const float c = (pa.px() * pb.px() + pa.py() * pb.py() + pa.pz() * pb.pz()) / (n1 * n2); + return std::acos(std::clamp(c, -1.f, 1.f)); +} + } // namespace o2::aod::pwgem::photonmeson::utils::mcutil #endif // PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/PairUtilities.h b/PWGEM/PhotonMeson/Utils/PairUtilities.h index 691013f8100..e4deb542747 100644 --- a/PWGEM/PhotonMeson/Utils/PairUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PairUtilities.h @@ -20,7 +20,17 @@ #include #include +#include +#include +#include +#include +#include + +#include +#include #include +#include +#include namespace o2::aod::pwgem::photonmeson::utils::pairutil { @@ -147,6 +157,231 @@ V0PhotonClassSelection buildV0PhotonClassSelection(TGroup const& g) s.maxTOF = g.cfgMaxNLegsTOF.value; return s; } + +//_______________________________________________________________________ +/// \brief Pair observables of a photon pair: kT, q_inv and the LCMS decomposition +struct PairQ { + float kt{0.f}; + float qinv{0.f}; + float qabsLcms{0.f}; + float qout{0.f}, qside{0.f}, qlong{0.f}; +}; + +//_______________________________________________________________________ +/// \brief Cheap q_inv gate before any boost or sparse fill +/// \param pt1 pT of the first photon +/// \param eta1 eta of the first photon +/// \param phi1 phi of the first photon +/// \param pt2 pT of the second photon +/// \param eta2 eta of the second photon +/// \param phi2 phi of the second photon +/// \param qMax upper limit on q_inv +/// \return true if q_inv <= qMax, using q_inv^2 = 2 pT1 pT2 (cosh(deta) - cos(dphi)), exact for massless particles +[[nodiscard]] inline bool pairBelowQmax(float pt1, float eta1, float phi1, + float pt2, float eta2, float phi2, float qMax) +{ + const float q2 = 2.f * pt1 * pt2 * (std::cosh(eta1 - eta2) - std::cos(phi1 - phi2)); + return q2 <= qMax * qMax; +} + +//_______________________________________________________________________ +/// \brief Computes kT, q_inv and (q_out, q_side, q_long) in the LCMS +/// \param v1 four-vector of the first photon +/// \param v2 four-vector of the second photon +/// \return PairQ; +inline PairQ computePairQ(ROOT::Math::PtEtaPhiMVector const& v1, ROOT::Math::PtEtaPhiMVector const& v2) +{ + PairQ r; + const auto k12 = 0.5 * (v1 + v2); + r.kt = static_cast(k12.Pt()); + r.qinv = std::fabs(static_cast((v1 - v2).M())); + const double ktMag = k12.Pt(); + const ROOT::Math::XYZVector uvOut = (ktMag > 1e-9) // o2-linter: disable=magic-number (guard against a zero-pT pair) + ? ROOT::Math::XYZVector(k12.Px() / ktMag, k12.Py() / ktMag, 0.) + : ROOT::Math::XYZVector(1., 0., 0.); + const ROOT::Math::XYZVector uvLong(0., 0., 1.); + const ROOT::Math::XYZVector uvSide = uvOut.Cross(uvLong); + const ROOT::Math::PxPyPzEVector v1c(v1), v2c(v2); + const double betaZ = (v1 + v2).Beta() * std::cos((v1 + v2).Theta()); + const ROOT::Math::Boost bstZ(0., 0., -betaZ); + const auto q3Lcms = bstZ(v1c - v2c).Vect(); + r.qabsLcms = static_cast(q3Lcms.R()); + r.qout = std::fabs(static_cast(q3Lcms.Dot(uvOut))); + r.qside = std::fabs(static_cast(q3Lcms.Dot(uvSide))); + r.qlong = std::fabs(static_cast(q3Lcms.Dot(uvLong))); + return r; +} + +//_______________________________________________________________________ +/// \brief computePairQ for anything with pt(), eta() and phi(): V0 photons, MC photons, truth records +/// \param a first photon-like object +/// \param b second photon-like object +/// \return PairQ of the massless pair +template +PairQ computePairQ(TA const& a, TB const& b) +{ + return computePairQ(ROOT::Math::PtEtaPhiMVector(a.pt(), a.eta(), a.phi(), 0.f), + ROOT::Math::PtEtaPhiMVector(b.pt(), b.eta(), b.phi(), 0.f)); +} + +//_______________________________________________________________________ +/// \brief One leg of a V0 candidat +struct DedupLeg { + float pt{0.f}, eta{0.f}, phi{0.f}; + float dedx{0.f}; /// TPC dE/dx + float fracShared{0.f}; /// fraction of shared TPC clusters + bool hasTpc{false}; + int64_t trackId{-1}; +}; + +//_______________________________________________________________________ +/// \brief A selected V0 candidate +struct DedupCand { + int64_t gi{-1}; /// globalIndex of the V0 + float eta{0.f}, phi{0.f}, pt{0.f}, rConv{0.f}, chi2{1e9f}; + float vx{0.f}, vy{0.f}, vz{0.f}; + int nITSTPC{0}; + std::array leg{}; /// 0 = e+, 1 = e- +}; + +//_______________________________________________________________________ +/// \brief Tolerances of the duplicate test +struct DedupConfig { + float maxLegDEta{0.01f}; + float maxLegDPhi{0.01f}; + float maxLegPtAsym{0.05f}; + float maxLegDeDxAsym{0.05f}; + float maxDVtx3D{999.f}; + bool requireBothLegs{false}; +}; + +//_______________________________________________________________________ +/// \brief Distance of two same-charge legs of two candidates +struct LegDistance { + float dEta{0.f}, dPhi{0.f}, ptAsym{1.f}, dedxAsym{1.f}, fShared{0.f}; + bool dedxValid{false}; +}; + +//_______________________________________________________________________ +/// \brief Fills DedupLeg from a V0 leg +/// \param leg V0 leg iterator (aod::V0Legs) +/// \return the leg reduced to the duplicate-test variables +template +DedupLeg makeDedupLeg(TLeg const& leg) +{ + DedupLeg l; + l.pt = static_cast(leg.pt()); + l.eta = static_cast(leg.eta()); + l.phi = static_cast(leg.phi()); + l.hasTpc = leg.hasTPC(); + l.dedx = static_cast(leg.tpcSignal()); + l.fracShared = static_cast(leg.tpcFractionSharedCls()); + l.trackId = leg.trackId(); + return l; +} + +//_______________________________________________________________________ +/// \brief Fills DedupCand from a V0 photon and its two legs +/// \param v0 V0 photon iterator +/// \param pos positive leg +/// \param ele negative leg +/// \return the candidate reduced to what defines its identity +template +DedupCand makeDedupCand(TV0 const& v0, TLeg const& pos, TLeg const& ele) +{ + DedupCand c; + c.gi = v0.globalIndex(); + c.eta = v0.eta(); + c.phi = v0.phi(); + c.pt = v0.pt(); + c.rConv = std::hypot(v0.vx(), v0.vy()); + c.chi2 = v0.chiSquareNDF(); + c.vx = v0.vx(); + c.vy = v0.vy(); + c.vz = v0.vz(); + c.nITSTPC = getV0PhotonLegCounts(pos, ele).nITSTPC; + c.leg = {makeDedupLeg(pos), makeDedupLeg(ele)}; + return c; +} + +//_______________________________________________________________________ +/// \brief Distance of two same-charge legs +/// \param a first candidate +/// \param b second candidate +/// \param il 0 = e+ with e+, 1 = e- with e- +/// \return LegDistance; dedxValid is false when a leg has no TPC +[[nodiscard]] inline LegDistance legDistance(DedupCand const& a, DedupCand const& b, int il) +{ + constexpr float kMinSigma = 1e-9f; + const auto& la = a.leg[static_cast(il)]; + const auto& lb = b.leg[static_cast(il)]; + LegDistance d; + d.dEta = std::fabs(la.eta - lb.eta); + d.dPhi = std::fabs(RecoDecay::constrainAngle(la.phi - lb.phi, -o2::constants::math::PI)); + const float ptSum = la.pt + lb.pt; + d.ptAsym = (ptSum > kMinSigma) ? std::fabs(la.pt - lb.pt) / ptSum : 1.f; + d.dedxValid = la.hasTpc && lb.hasTpc; + const float dedxSum = la.dedx + lb.dedx; + d.dedxAsym = (d.dedxValid && dedxSum > kMinSigma) ? std::fabs(la.dedx - lb.dedx) / dedxSum : 1.f; + d.fShared = std::max(la.fracShared, lb.fracShared); + return d; +} + +//_______________________________________________________________________ +/// \brief Test of track identity on reco level: same-charge legs physically the same particle +/// \param a first candidate +/// \param b second candidate +/// \param il 0 = e+, 1 = e- +/// \param cfg tolerances +/// \return true if the two legs agree within all tolerances +[[nodiscard]] inline bool legsIdentical(DedupCand const& a, DedupCand const& b, int il, DedupConfig const& cfg) +{ + if (a.leg[static_cast(il)].trackId == b.leg[static_cast(il)].trackId) { + return false; + } + const auto d = legDistance(a, b, il); + if (d.dEta > cfg.maxLegDEta || d.dPhi > cfg.maxLegDPhi) { + return false; + } + if (d.ptAsym > cfg.maxLegPtAsym) { + return false; + } + if (d.dedxValid && d.dedxAsym > cfg.maxLegDeDxAsym) { + return false; + } + return true; +} + +//_______________________________________________________________________ +/// \brief Test: Duplicate stores +/// \param a first candidate +/// \param b second candidate +/// \param cfg tolerances +/// \return true if at least one same-charge leg pair is identical (both, with cfg.requireBothLegs) and the conversion points are within cfg.maxDVtx3D +[[nodiscard]] inline bool isDuplicatePair(DedupCand const& a, DedupCand const& b, DedupConfig const& cfg) +{ + const float dVtx = std::hypot(a.vx - b.vx, a.vy - b.vy, a.vz - b.vz); + if (dVtx > cfg.maxDVtx3D) { + return false; + } + const bool posSame = legsIdentical(a, b, 0, cfg); + const bool negSame = legsIdentical(a, b, 1, cfg); + return cfg.requireBothLegs ? (posSame && negSame) : (posSame || negSame); +} + +//_______________________________________________________________________ +/// \brief Quality order among duplicates +/// \param a first candidate +/// \param b second candidate +/// \return true if a should be kept over b +[[nodiscard]] inline bool isBetterCand(DedupCand const& a, DedupCand const& b) +{ + if (a.nITSTPC != b.nITSTPC) { + return a.nITSTPC > b.nITSTPC; + } + return a.chi2 < b.chi2; +} + } // namespace o2::aod::pwgem::photonmeson::utils::pairutil #endif // PWGEM_PHOTONMESON_UTILS_PAIRUTILITIES_H_ From 2bbd72ec9da62e29d8d1e607ead9c29003f25e92 Mon Sep 17 00:00:00 2001 From: Victor Gonzalez Date: Mon, 21 Sep 2026 01:06:16 +0200 Subject: [PATCH 006/648] [PWGCF] DptDpt - Fixing Megalinter cppcheck warnings (#17990) Co-authored-by: Victor Gonzalez --- PWGCF/TableProducer/dptDptFilter.h | 24 +++++++------ PWGCF/Tasks/dptDptCorrelations.cxx | 16 +++++---- .../Tasks/dptDptEfficiencyAndQc.cxx | 34 ++++++++----------- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/PWGCF/TableProducer/dptDptFilter.h b/PWGCF/TableProducer/dptDptFilter.h index af632d74ae6..879b8092209 100644 --- a/PWGCF/TableProducer/dptDptFilter.h +++ b/PWGCF/TableProducer/dptDptFilter.h @@ -554,6 +554,10 @@ int tracktype = 1; std::vector trackFilters = {}; // the vector of track selectors struct DptDptTrackSelection { + /* disable copying and assignment explicitly */ + DptDptTrackSelection(const DptDptTrackSelection&) = delete; + DptDptTrackSelection& operator=(const DptDptTrackSelection&) = delete; + DptDptTrackSelection(TrackSelection* stdTs, TList* outputList, const char* name) : stdTrackSelection(stdTs) { passedHistogram = new TH1F(name, name, ptbins, ptlow, ptup); @@ -836,7 +840,7 @@ float particleMaxDCAxy = 999.9f; float particleMaxDCAZ = 999.9f; bool traceCollId0 = false; -inline TList* getCCDBInput(auto& ccdb, const char* ccdbpath, const char* ccdbdate, bool periodInPath = false, const std::string& suffix = "") +inline TList* getCCDBInput(const auto& ccdb, const char* ccdbpath, const char* ccdbdate, bool periodInPath = false, const std::string& suffix = "") { std::tm cfgtm = {}; std::stringstream ss(ccdbdate); @@ -1107,7 +1111,7 @@ template inline bool triggerSelection(CollisionObject const&) { LOGF(fatal, "Trigger selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Trigger selection for reconstructed collision tables without centrality/multiplicity @@ -1280,7 +1284,7 @@ template inline bool centralitySelection(CollisionObject const&, float&) { LOGF(fatal, "Centrality selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Centrality selection for reconstructed and detector level collision tables with centrality/multiplicity information @@ -1396,7 +1400,7 @@ template inline bool occupancySelection(CollisionObject const&) { LOGF(fatal, "Occupancy selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Occupancy selection for reconstructed and detector level collision tables with centrality/multiplicity information @@ -1561,7 +1565,7 @@ struct TpcExcludeTrack { switch (method) { case kNOEXCLUSION: { return false; - } break; + } case kSTATIC: { int phiBinIx = getPhiBinIx(track); /* bins multiple of four have got sector border */ @@ -1570,7 +1574,7 @@ struct TpcExcludeTrack { } else { return true; } - } break; + } case kDYNAMIC: { float phiInTpcSector = std::fmod(track.phi(), TpcPhiSectorWidth); if (track.sign() > 0) { @@ -1578,7 +1582,7 @@ struct TpcExcludeTrack { } else { return (phiInTpcSector < negativeUpCut->Eval(track.pt())) && (negativeLowCut->Eval(track.pt()) < phiInTpcSector); } - } break; + } default: return false; } @@ -1836,14 +1840,14 @@ struct PIDSpeciesSelection { /* out debug if needed */ outnsigmasdebug(); - auto closeTo = [](auto& values, auto& mindet, auto& maxdet, uint8_t sp) { + auto closeTo = [](const auto& values, const auto& mindet, const auto& maxdet, uint8_t sp) { if (mindet[sp] <= values[sp] && values[sp] < maxdet[sp]) { return true; } else { return false; } }; - auto awayFrom = [&](auto& values, auto& mindet, auto& maxdet, uint8_t sp) { + auto awayFrom = [&](const auto& values, const auto& mindet, const auto& maxdet, uint8_t sp) { for (size_t ix = 0; ix < pdgcodes.size(); ix++) { if (ix != sp) { if (mindet[ix] <= values[ix] && values[ix] < maxdet[ix]) { @@ -1889,7 +1893,7 @@ struct PIDSpeciesSelection { } return true; }; - auto aboveThreshold = [&](auto& config) { + auto aboveThreshold = [&](const auto& config) { return ((config->mPThreshold > 0.0) && (config->mPThreshold < track.p())); }; auto isA = [&](auto& config, uint8_t sp) { diff --git a/PWGCF/Tasks/dptDptCorrelations.cxx b/PWGCF/Tasks/dptDptCorrelations.cxx index 7da4997b702..99602575f1d 100644 --- a/PWGCF/Tasks/dptDptCorrelations.cxx +++ b/PWGCF/Tasks/dptDptCorrelations.cxx @@ -112,7 +112,7 @@ struct DptDptCorrelations { // The DptDptCorrelationsAnalysisTask output objects //============================================================================================ /* histograms */ - TH1F* fhVertexZA; //! fhN1VsPt{nch, nullptr}; //! fhN1VsPtEta{nch, nullptr}; //! fhN1VsEtaPhi{nch, nullptr}; //! ptavgs) + void storePtAverages(const std::vector& ptavgs) { LOGF(info, "Stored pT average for %d track ids", ptavgs.size()); for (uint i = 0; i < ptavgs.size(); ++i) { @@ -905,9 +905,9 @@ struct DptDptCorrelations { float* fCentMultMax = nullptr; /* the data collecting engine instances */ - DataCollectingEngine** dataCE; - DataCollectingEngine** dataCEsmall; - DataCollectingEngine** dataCEME; + DataCollectingEngine** dataCE = nullptr; + DataCollectingEngine** dataCEsmall = nullptr; + DataCollectingEngine** dataCEME = nullptr; /* the input file structure from CCDB */ TList* ccdblst = nullptr; @@ -1248,14 +1248,14 @@ struct DptDptCorrelations { return dataCE[ixDCE]->isCCDBstored(); } }; - auto storePtAverages = [&](auto& ptavgs) { + auto storePtAverages = [&](const auto& ptavgs) { if (cfgSmallDCE.value) { dataCEsmall[ixDCE]->storePtAverages(ptavgs); } else { dataCE[ixDCE]->storePtAverages(ptavgs); } }; - auto storeTrackCorrections = [&](auto& corrs) { + auto storeTrackCorrections = [&](const auto& corrs) { if (cfgSmallDCE.value) { dataCEsmall[ixDCE]->storeTrackCorrections(corrs); } else { @@ -1583,6 +1583,7 @@ struct DptDptCorrelations { LOGF(DPTDPTLOGCOLLISIONS, "Received generated collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", collision1.globalIndex(), collision1.posZ(), collision1.centmult(), collision1.collisionaccepted() ? "accepted" : "not accepted", collision2.globalIndex(), collision2.posZ(), collision2.centmult(), collision2.collisionaccepted() ? "accepted" : "not accepted"); + logcomb++; } if (!collision1.collisionaccepted() || !collision2.collisionaccepted()) { LOGF(error, "Received collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", @@ -1621,6 +1622,7 @@ struct DptDptCorrelations { collision2.posZ(), collision2.centmult(), collision2.collisionaccepted() ? "accepted" : "not accepted"); + logcomb++; } if (!collision1.collisionaccepted() || !collision2.collisionaccepted()) { LOGF(error, diff --git a/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx b/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx index e084e4dfd33..a531df7a4c7 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx @@ -514,7 +514,7 @@ struct QADataCollectingEngine { fhTruePvsP->Fill(track.p(), mcparticle.p()); fhTruePvsInnerP->Fill(track.tpcInnerParam(), mcparticle.p()); - auto fillhisto = [](auto& h, float pt, float eta, bool cond1, bool cond2) { + auto fillmchisto = [](auto& h, float pt, float eta, bool cond1, bool cond2) { if (cond1 && cond2) { h->Fill(eta, pt); } @@ -522,15 +522,15 @@ struct QADataCollectingEngine { std::vector tPt = {track.pt(), mcparticle.pt()}; std::vector tEta = {track.eta(), mcparticle.eta()}; for (uint ix = 0; ix < tPt.size(); ++ix) { - fillhisto(fhPtVsEtaPrimItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isprimary); - fillhisto(fhPtVsEtaPrimItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isprimary); - fillhisto(fhPtVsEtaPrimItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isprimary); - fillhisto(fhPtVsEtaSecItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, issecdecay); - fillhisto(fhPtVsEtaSecItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, issecdecay); - fillhisto(fhPtVsEtaSecItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, issecdecay); - fillhisto(fhPtVsEtaMatItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isfrommaterial); - fillhisto(fhPtVsEtaMatItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isfrommaterial); - fillhisto(fhPtVsEtaMatItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isfrommaterial); + fillmchisto(fhPtVsEtaPrimItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isprimary); + fillmchisto(fhPtVsEtaPrimItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isprimary); + fillmchisto(fhPtVsEtaPrimItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isprimary); + fillmchisto(fhPtVsEtaSecItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, issecdecay); + fillmchisto(fhPtVsEtaSecItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, issecdecay); + fillmchisto(fhPtVsEtaSecItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, issecdecay); + fillmchisto(fhPtVsEtaMatItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isfrommaterial); + fillmchisto(fhPtVsEtaMatItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isfrommaterial); + fillmchisto(fhPtVsEtaMatItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isfrommaterial); } } } @@ -879,13 +879,10 @@ struct PidExtraDataCollectingEngine { /* only after track selection */ std::vector> fhIdTPCdEdxSignalVsP{nsp, nullptr}; std::vector> fpIdTPCdEdxSignalVsPSigmas{nsp, nullptr}; - std::vector>> fhIdTPCdEdxSignalDiffVsP{nsp, {nmainsp, nullptr}}; std::vector>> fhIdTPCnSigmasVsP{nsp, {nallmainsp, nullptr}}; std::vector> fhIdTOFSignalVsP{nsp, nullptr}; std::vector> fpIdTOFSignalVsPSigmas{nsp, nullptr}; - std::vector>> fhIdTOFSignalDiffVsP{nsp, {nmainsp, nullptr}}; std::vector>> fhIdTOFnSigmasVsP{nsp, {nallmainsp, nullptr}}; - std::vector> fhIdPvsTOFSqMass{nsp, nullptr}; template void init(HistogramRegistry& registry, const char* dirname) @@ -1005,10 +1002,10 @@ struct DptDptEfficiencyAndQc { float* fCentMultMax = nullptr; /* the data collecting engine instances */ - QADataCollectingEngine** qaDataCE; - QAExtraDataCollectingEngine** qaExtraDataCE; - PidDataCollectingEngine** pidDataCE; - PidExtraDataCollectingEngine** pidExtraDataCE; + QADataCollectingEngine** qaDataCE = nullptr; + QAExtraDataCollectingEngine** qaExtraDataCE = nullptr; + PidDataCollectingEngine** pidDataCE = nullptr; + PidExtraDataCollectingEngine** pidExtraDataCE = nullptr; /* the histogram registries */ HistogramRegistry registryOne{"registryOne", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -1179,7 +1176,6 @@ struct DptDptEfficiencyAndQc { if (doPidExtraAnalysis) { pidExtraDataCE = new PidExtraDataCollectingEngine*[ncmranges]; } - std::string recogen; if (ncmranges > registryBank.size()) { LOGF(fatal, "There are more centrality ranges configured than registries in the bank. Please fix it!"); } @@ -1434,7 +1430,7 @@ using BCsWithTimestamps = soa::Join; struct CheckTimestamp { o2::ccdb::CcdbApi ccdbApi; - int mRunNumber; + int mRunNumber = 0; uint64_t runsor = 0; uint64_t runeor = 0; std::shared_ptr hTimeStampDiffNegative = nullptr; From 498bb05b04f88cf75304d1b621acbabb029ac9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 21 Sep 2026 02:50:02 +0200 Subject: [PATCH 007/648] [ALICE3] Cleanup legacy DelphesO2 classes (#17989) --- ALICE3/Core/DelphesO2LutWriter.cxx | 551 -------------------------- ALICE3/Core/DelphesO2LutWriter.h | 97 ----- ALICE3/Core/DelphesO2TrackSmearer.cxx | 359 ----------------- ALICE3/Core/DelphesO2TrackSmearer.h | 258 ------------ 4 files changed, 1265 deletions(-) delete mode 100644 ALICE3/Core/DelphesO2LutWriter.cxx delete mode 100644 ALICE3/Core/DelphesO2LutWriter.h delete mode 100644 ALICE3/Core/DelphesO2TrackSmearer.cxx delete mode 100644 ALICE3/Core/DelphesO2TrackSmearer.h diff --git a/ALICE3/Core/DelphesO2LutWriter.cxx b/ALICE3/Core/DelphesO2LutWriter.cxx deleted file mode 100644 index 3110006d11b..00000000000 --- a/ALICE3/Core/DelphesO2LutWriter.cxx +++ /dev/null @@ -1,551 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @file DelphesO2LutWriter.cxx -/// @brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutWrite.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutWrite.cc -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it -/// - -#include "ALICE3/Core/DelphesO2LutWriter.h" - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" -#include "ALICE3/Core/FastTracker.h" -#include "ALICE3/Core/TrackUtilities.h" - -#include -#include - -#include -#include -#include -#include -#include // IWYU pragma: keep (do not replace with TMatrixDfwd.h) -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -// #define USE_FWD_PARAM -#ifdef USE_FWD_PARAM -#include "fwdRes.C" -#endif - -namespace o2::fastsim -{ - -void DelphesO2LutWriter::print() const -{ - LOG(info) << " --- Printing configuration of LUT writer --- "; - LOG(info) << " -> etaMaxBarrel = " << etaMaxBarrel; - LOG(info) << " -> usePara = " << usePara; - LOG(info) << " -> useDipole = " << useDipole; - LOG(info) << " -> useFlatDipole = " << useFlatDipole; - LOG(info) << " -> mAtLeastHits = " << mAtLeastHits; - LOG(info) << " -> mAtLeastCorr = " << mAtLeastCorr; - LOG(info) << " -> mAtLeastFake = " << mAtLeastFake; - LOG(info) << " -> Nch Binning: = " << mNchBinning.toString(); - LOG(info) << " -> Radius Binning: = " << mRadiusBinning.toString(); - LOG(info) << " -> Eta Binning: = " << mEtaBinning.toString(); - LOG(info) << " -> Pt Binning: = " << mPtBinning.toString(); - LOG(info) << " --- End of configuration --- "; -} - -std::string DelphesO2LutWriter::LutBinning::toString() const -{ - std::string str = ""; - str.append(log ? "log" : "lin"); - str.append(" nbins: "); - str.append(std::to_string(nbins)); - str.append(" min: "); - str.append(std::to_string(min)); - str.append(" max: "); - str.append(std::to_string(max)); - return str; -} - -bool DelphesO2LutWriter::fatSolve(lutEntry_t& lutEntry, - float pt, - float eta, - const float mass, - size_t itof, - size_t otof, - int q, - const float nch) -{ - lutEntry.valid = false; - - static TLorentzVector tlv; - tlv.SetPtEtaPhiM(pt, eta, 0., mass); - o2::track::TrackParCov trkIn; - o2::upgrade::convertTLorentzVectorToO2Track(q, tlv, {0., 0., 0.}, trkIn); - // tlv.Print(); - // return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}\n", - // getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName()); - // trkIn.print(); - o2::track::TrackParCov trkOut; - const int status = fat.FastTrack(trkIn, trkOut, nch); - if (status <= mAtLeastHits) { - LOGF(info, " --- fatSolve: FastTrack failed ---"); - // tlv.Print(); - return false; - } - LOGF(info, " --- fatSolve: FastTrack succeeded %d ---", status); - // trkOut.print(); - lutEntry.valid = true; - lutEntry.itof = fat.GetGoodHitProb(itof); - lutEntry.otof = fat.GetGoodHitProb(otof); - static constexpr int nCov = 15; - for (int i = 0; i < nCov; ++i) - lutEntry.covm[i] = trkOut.getCov()[i]; - - // define the efficiency - auto totfake = 0.; - lutEntry.eff = 1.; - for (size_t i = 1; i < fat.GetNLayers(); ++i) { - if (fat.IsLayerInert(i)) - continue; // skip inert layers - auto igoodhit = fat.GetGoodHitProb(i); - if (igoodhit <= 0. || i == itof || i == otof) - continue; - lutEntry.eff *= igoodhit; - auto pairfake = 0.; - for (size_t j = i + 1; j < fat.GetNLayers(); ++j) { - auto jgoodhit = fat.GetGoodHitProb(j); - if (jgoodhit <= 0. || j == itof || j == otof) - continue; - pairfake = (1. - igoodhit) * (1. - jgoodhit); - break; - } - totfake += pairfake; - } - lutEntry.eff2 = (1. - totfake); - - return true; -} - -#ifdef USE_FWD_PARAM -bool DelphesO2LutWriter::fwdSolve(float* covm, float pt, float eta, float mass) -{ - if (fwdRes(covm, pt, eta, mass) < 0) - return false; - return true; -} -#else -bool DelphesO2LutWriter::fwdSolve(float*, float, float, float) -{ - return false; -} -#endif - -bool DelphesO2LutWriter::fwdPara(lutEntry_t& lutEntry, float pt, float eta, float mass, float Bfield) -{ - lutEntry.valid = false; - - // parametrised forward response; interpolates between FAT at eta = 1.75 and a fixed parametrisation at eta = 4; only diagonal elements - static constexpr float etaLimit = 4.0f; - if (std::fabs(eta) < etaMaxBarrel || std::fabs(eta) > etaLimit) - return false; - - if (!fatSolve(lutEntry, pt, etaMaxBarrel, mass)) - return false; - static constexpr int nCov = 15; - float covmbarrel[nCov] = {0}; - for (int i = 0; i < nCov; ++i) { - covmbarrel[i] = lutEntry.covm[i]; - } - - // parametrisation at eta = 4 - const double beta = 1. / std::sqrt(1 + mass * mass / pt / pt / std::cosh(eta) / std::cosh(eta)); - const float dcaPos = 2.5e-4 / std::sqrt(3); // 2.5 micron/sqrt(3) - const float r0 = 0.5; // layer 0 radius [cm] - const float r1 = 1.3; - const float r2 = 2.5; - const float x0layer = 0.001; // material budget (rad length) per layer - const double sigmaAlpha = 0.0136 / beta / pt * std::sqrt(x0layer * std::cosh(eta)) * (1 + 0.038 * std::log(x0layer * std::cosh(eta))); - const double dcaxyMs = sigmaAlpha * r0 * std::sqrt(1 + r1 * r1 / (r2 - r0) / (r2 - r0)); - const double dcaxy2 = dcaPos * dcaPos + dcaxyMs * dcaxyMs; - - const double dcazMs = sigmaAlpha * r0 * std::cosh(eta); - const double dcaz2 = dcaPos * dcaPos + dcazMs * dcazMs; - - const float Leta = 2.8 / std::sinh(eta) - 0.01 * r0; // m - const double relmomresPos = 10e-6 * pt / 0.3 / Bfield / Leta / Leta * std::sqrt(720. / 15.); - - const float relmomresBarrel = std::sqrt(covmbarrel[14]) * pt; - const float rOuter = 1; // m - const float relmomresPosBarrel = 10e-6 * pt / 0.3 / Bfield / rOuter / rOuter / std::sqrt(720. / 15.); - const float relmomresMSBarrel = std::sqrt(relmomresBarrel * relmomresBarrel - relmomresPosBarrel * relmomresPosBarrel); - - // interpolate MS contrib (rel resolution 0.4 at eta = 4) - const float relmomresMSEta4 = 0.4 / beta * 0.5 / Bfield; - const float relmomresMS = relmomresMSEta4 * std::pow(relmomresMSEta4 / relmomresMSBarrel, (std::fabs(eta) - 4.) / (4. - etaMaxBarrel)); - const float momresTot = pt * std::sqrt(relmomresPos * relmomresPos + relmomresMS * relmomresMS); // total absolute mom reso - - // Fill cov matrix diag - for (int i = 0; i < 15; ++i) - lutEntry.covm[i] = 0; - - lutEntry.covm[0] = covmbarrel[0]; - if (dcaxy2 > lutEntry.covm[0]) - lutEntry.covm[0] = dcaxy2; - lutEntry.covm[2] = covmbarrel[2]; - if (dcaz2 > lutEntry.covm[2]) - lutEntry.covm[2] = dcaz2; - lutEntry.covm[5] = covmbarrel[5]; // sigma^2 sin(phi) - lutEntry.covm[9] = covmbarrel[9]; // sigma^2 tanl - lutEntry.covm[14] = momresTot * momresTot / pt / pt / pt / pt; // sigma^2 1/pt - // Check that all numbers are numbers - for (int i = 0; i < 15; ++i) { - if (std::isnan(lutEntry.covm[i])) { - LOGF(info, " --- lutEntry.covm[%d] is NaN", i); - return false; - } - } - return true; -} - -void DelphesO2LutWriter::lutWrite(const char* filename, int pdg, float field, size_t itof, size_t otof) -{ - - if (useFlatDipole && useDipole) { - LOGF(info, "Both dipole and dipole flat flags are on, please use only one of them"); - return; - } - - // output file - std::ofstream lutFile(filename, std::ofstream::binary); - if (!lutFile.is_open()) { - LOGF(info, "Did not manage to open output file!!"); - return; - } - - // write header - lutHeader_t lutHeader; - // pid - lutHeader.pdg = pdg; - const TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(pdg); - if (!particle) { - LOG(fatal) << "Cannot find particle with PDG code " << pdg; - return; - } - lutHeader.mass = particle->Mass(); - const int q = std::abs(particle->Charge()) / 3; - if (q <= 0) { - LOGF(info, "Negative or null charge (%f) for pdg code %i. Fix the charge!", particle->Charge(), pdg); - return; - } - lutHeader.field = field; - auto setMap = [](map_t& map, LutBinning b) { - map.log = b.log; - map.nbins = b.nbins; - map.min = b.min; - map.max = b.max; - }; - // nch - setMap(lutHeader.nchmap, mNchBinning); - // radius - setMap(lutHeader.radmap, mRadiusBinning); - // eta - setMap(lutHeader.etamap, mEtaBinning); - // pt - setMap(lutHeader.ptmap, mPtBinning); - - lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader)); - - // entries - const int nnch = lutHeader.nchmap.nbins; - const int nrad = lutHeader.radmap.nbins; - const int neta = lutHeader.etamap.nbins; - const int npt = lutHeader.ptmap.nbins; - lutEntry_t lutEntry; - - // write entries - int nCalls = 0; - int successfullCalls = 0; - int failedCalls = 0; - for (int inch = 0; inch < nnch; ++inch) { - LOGF(info, " --- writing nch = %d/%d", inch, nnch); - auto nch = lutHeader.nchmap.eval(inch); - lutEntry.nch = nch; - fat.SetdNdEtaCent(nch); - for (int irad = 0; irad < nrad; ++irad) { - LOGF(info, " --- writing irad = %d/%d", irad, nrad); - for (int ieta = 0; ieta < neta; ++ieta) { - LOGF(info, " --- writing ieta = %d/%d", ieta, neta); - auto eta = lutHeader.etamap.eval(ieta); - lutEntry.eta = lutHeader.etamap.eval(ieta); - for (int ipt = 0; ipt < npt; ++ipt) { - nCalls++; - LOGF(info, " --- writing ipt = %d/%d", ipt, npt); - lutEntry.pt = lutHeader.ptmap.eval(ipt); - lutEntry.valid = true; - if (std::fabs(eta) <= etaMaxBarrel) { // full lever arm ends at etaMaxBarrel - LOGF(info, "Solving in the barrel"); - // LOGF(info, " --- fatSolve: pt = %f, eta = %f, mass = %f, field=%f", lutEntry.pt, lutEntry.eta, lutHeader.mass, lutHeader.field); - successfullCalls++; - if (!fatSolve(lutEntry, lutEntry.pt, lutEntry.eta, lutHeader.mass, itof, otof, q)) { - // LOGF(info, " --- fatSolve: error"); - lutEntry.valid = false; - lutEntry.eff = 0.; - lutEntry.eff2 = 0.; - for (int i = 0; i < 15; ++i) { - lutEntry.covm[i] = 0.; - } - successfullCalls--; - failedCalls++; - } - } else { - LOGF(info, "Solving outside the barrel"); - // LOGF(info, " --- fwdSolve: pt = %f, eta = %f, mass = %f, field=%f", lutEntry.pt, lutEntry.eta, lutHeader.mass, lutHeader.field); - lutEntry.eff = 1.; - lutEntry.eff2 = 1.; - bool retval = true; - successfullCalls++; - if (useFlatDipole) { // Using the parametrization at the border of the barrel - retval = fatSolve(lutEntry, lutEntry.pt, etaMaxBarrel, lutHeader.mass, itof, otof, q); - } else if (usePara) { - retval = fwdPara(lutEntry, lutEntry.pt, lutEntry.eta, lutHeader.mass, field); - } else { - retval = fwdSolve(lutEntry.covm, lutEntry.pt, lutEntry.eta, lutHeader.mass); - } - if (useDipole) { // Using the parametrization at the border of the barrel only for efficiency and momentum resolution - lutEntry_t lutEntryBarrel; - retval = fatSolve(lutEntryBarrel, lutEntry.pt, etaMaxBarrel, lutHeader.mass, itof, otof, q); - lutEntry.valid = lutEntryBarrel.valid; - lutEntry.covm[14] = lutEntryBarrel.covm[14]; - lutEntry.eff = lutEntryBarrel.eff; - lutEntry.eff2 = lutEntryBarrel.eff2; - } - if (!retval) { - LOGF(info, " --- fwdSolve: error"); - lutEntry.valid = false; - for (int i = 0; i < 15; ++i) { - lutEntry.covm[i] = 0.; - } - successfullCalls--; - failedCalls++; - } - } - LOGF(info, "Diagonalizing"); - diagonalise(lutEntry); - LOGF(info, "Writing"); - lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); - } - } - } - } - LOGF(info, " --- finished writing LUT file %s", filename); - LOGF(info, " --- successfull calls: %d/%d, failed calls: %d/%d", successfullCalls, nCalls, failedCalls, nCalls); - lutFile.close(); -} - -void DelphesO2LutWriter::diagonalise(lutEntry_t& lutEntry) -{ - static constexpr int kEig = 5; - TMatrixDSym m(kEig); - for (int i = 0, k = 0; i < kEig; ++i) { - for (int j = 0; j < i + 1; ++j, ++k) { - m(i, j) = lutEntry.covm[k]; - m(j, i) = lutEntry.covm[k]; - } - } - - // m.Print(); - TMatrixDSymEigen eigen(m); - // eigenvalues vector - const TVectorD& eigenVal = eigen.GetEigenValues(); - for (int i = 0; i < kEig; ++i) - lutEntry.eigval[i] = eigenVal[i]; - // eigenvectors matrix - TMatrixD eigenVec = eigen.GetEigenVectors(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) - lutEntry.eigvec[i][j] = eigenVec[i][j]; - // inverse eigenvectors matrix - eigenVec.Invert(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) - lutEntry.eiginv[i][j] = eigenVec[i][j]; -} - -TGraph* DelphesO2LutWriter::lutRead(const char* filename, int pdg, int what, int vs, float nch, float radius, float eta, float pt) -{ - LOGF(info, " --- reading LUT file %s", filename); - // vs - static const int kNch = 0; - static const int kEta = 1; - static const int kPt = 2; - - // what - static const int kEfficiency = 0; - static const int kEfficiency2 = 1; - static const int kEfficiencyInnerTOF = 2; - static const int kEfficiencyOuterTOF = 3; - static const int kPtResolution = 4; - static const int kRPhiResolution = 5; - static const int kZResolution = 6; - - o2::delphes::DelphesO2TrackSmearer smearer; - smearer.loadTable(pdg, filename); - auto lutHeader = smearer.getLUTHeader(pdg); - lutHeader->print(); - map_t lutMap; - switch (vs) { - case kNch: - lutMap = lutHeader->nchmap; - break; - case kEta: - lutMap = lutHeader->etamap; - break; - case kPt: - lutMap = lutHeader->ptmap; - break; - } - auto nbins = lutMap.nbins; - auto g = new TGraph(); - g->SetName(Form("lut_%s_%d_vs_%d_what_%d", filename, pdg, vs, what)); - g->SetTitle(Form("LUT for %s, pdg %d, vs %d, what %d", filename, pdg, vs, what)); - switch (vs) { - case kNch: - LOGF(info, " --- vs = kNch"); - g->GetXaxis()->SetTitle("Nch"); - break; - case kEta: - LOGF(info, " --- vs = kEta"); - g->GetXaxis()->SetTitle("#eta"); - break; - case kPt: - LOGF(info, " --- vs = kPt"); - g->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - break; - default: - LOGF(info, " --- error: unknown vs %d", vs); - return nullptr; - } - switch (what) { - case kEfficiency: - LOGF(info, " --- what = kEfficiency"); - g->GetYaxis()->SetTitle("Efficiency (%)"); - break; - case kEfficiency2: - LOGF(info, " --- what = kEfficiency2"); - g->GetYaxis()->SetTitle("Efficiency2 (%)"); - break; - case kEfficiencyInnerTOF: - LOGF(info, " --- what = kEfficiencyInnerTOF"); - g->GetYaxis()->SetTitle("Inner TOF Efficiency (%)"); - break; - case kEfficiencyOuterTOF: - LOGF(info, " --- what = kEfficiencyOuterTOF"); - g->GetYaxis()->SetTitle("Outer TOF Efficiency (%)"); - break; - case kPtResolution: - LOGF(info, " --- what = kPtResolution"); - g->GetYaxis()->SetTitle("p_{T} Resolution (%)"); - break; - case kRPhiResolution: - LOGF(info, " --- what = kRPhiResolution"); - g->GetYaxis()->SetTitle("R#phi Resolution (#mum)"); - break; - case kZResolution: - LOGF(info, " --- what = kZResolution"); - g->GetYaxis()->SetTitle("Z Resolution (#mum)"); - break; - default: - LOGF(info, " --- error: unknown what %d", what); - return nullptr; - } - - bool canBeInvalid = true; - for (int i = 0; i < nbins; ++i) { - switch (vs) { - case kNch: - nch = lutMap.eval(i); - break; - case kEta: - eta = lutMap.eval(i); - break; - case kPt: - pt = lutMap.eval(i); - break; - } - float eff = 0.; - auto lutEntry = smearer.getLUTEntry(pdg, nch, radius, eta, pt, eff); - if (!lutEntry->valid || lutEntry->eff == 0.) { - if (!canBeInvalid) { - LOGF(info, " --- warning: it cannot be invalid"); - } - continue; - } - canBeInvalid = false; - - double cen = 0.; - switch (vs) { - case kNch: - cen = lutEntry->nch; - break; - case kEta: - cen = lutEntry->eta; - break; - case kPt: - cen = lutEntry->pt; - break; - } - double val = 0.; - switch (what) { - case kEfficiency: - val = lutEntry->eff * 100.; // efficiency (%) - break; - case kEfficiency2: - val = lutEntry->eff2 * 100.; // efficiency (%) - break; - case kEfficiencyInnerTOF: - val = lutEntry->itof * 100.; // efficiency (%) - break; - case kEfficiencyOuterTOF: - val = lutEntry->otof * 100.; // efficiency (%) - break; - case kPtResolution: - val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt * 100.; // pt resolution (%) - break; - case kRPhiResolution: - val = std::sqrt(lutEntry->covm[0]) * 1.e4; // rphi resolution (um) - break; - case kZResolution: - val = std::sqrt(lutEntry->covm[1]) * 1.e4; // z resolution (um) - break; - default: - LOGF(info, " --- error: unknown what %d", what); - break; - } - g->AddPoint(cen, val); - } - - return g; -} -} // namespace o2::fastsim - -ClassImp(o2::fastsim::DelphesO2LutWriter); diff --git a/ALICE3/Core/DelphesO2LutWriter.h b/ALICE3/Core/DelphesO2LutWriter.h deleted file mode 100644 index e234efd3bdb..00000000000 --- a/ALICE3/Core/DelphesO2LutWriter.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// \file DelphesO2LutWriter.h -/// \brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutWrite.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutWrite.cc -/// \author: Roberto Preghenella -/// \email: preghenella@bo.infn.it -/// - -#ifndef ALICE3_CORE_DELPHESO2LUTWRITER_H_ -#define ALICE3_CORE_DELPHESO2LUTWRITER_H_ - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" -#include "ALICE3/Core/FastTracker.h" - -#include - -#include - -#include - -#include -#include - -namespace o2::fastsim -{ -class DelphesO2LutWriter -{ - public: - DelphesO2LutWriter() = default; - virtual ~DelphesO2LutWriter() = default; - - // Setters - void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {log, nbins, min, max}; } - void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {log, nbins, min, max}; } - void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {log, nbins, min, max}; } - void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {log, nbins, min, max}; } - void setEtaMaxBarrel(float eta) { etaMaxBarrel = eta; } - void setAtLeastHits(int n) { mAtLeastHits = n; } - void setAtLeastCorr(int n) { mAtLeastCorr = n; } - void setAtLeastFake(int n) { mAtLeastFake = n; } - bool fatSolve(lutEntry_t& lutEntry, - float pt = 0.1, - float eta = 0.0, - const float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], - size_t itof = 0, - size_t otof = 0, - int q = 1, - const float nch = 1); - - void print() const; - bool fwdSolve(float* covm, float pt = 0.1, float eta = 0.0, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); - bool fwdPara(lutEntry_t& lutEntry, float pt = 0.1, float eta = 0.0, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], float Bfield = 0.5); - void lutWrite(const char* filename = "lutCovm.dat", int pdg = 211, float field = 0.2, size_t itof = 0, size_t otof = 0); - TGraph* lutRead(const char* filename, int pdg, int what, int vs, float nch = 0., float radius = 0., float eta = 0., float pt = 0.); - - o2::fastsim::FastTracker fat; - - private: - void diagonalise(lutEntry_t& lutEntry); - float etaMaxBarrel = 1.75f; - bool usePara = true; // use fwd parameterisation - bool useDipole = false; // use dipole i.e. flat parametrization for efficiency and momentum resolution - bool useFlatDipole = false; // use dipole i.e. flat parametrization outside of the barrel - - int mAtLeastHits = 4; - int mAtLeastCorr = 4; - int mAtLeastFake = 0; - - // Binning of the LUT to make - struct LutBinning { - bool log; - int nbins; - float min; - float max; - std::string toString() const; - }; - LutBinning mNchBinning = {true, 20, 0.5f, 3.5f}; - LutBinning mRadiusBinning = {false, 1, 0.0f, 100.0f}; - LutBinning mEtaBinning = {false, 80, -4.0f, 4.0f}; - LutBinning mPtBinning = {true, 200, -2.0f, 2.0f}; - - ClassDef(DelphesO2LutWriter, 1); -}; -} // namespace o2::fastsim - -#endif // ALICE3_CORE_DELPHESO2LUTWRITER_H_ diff --git a/ALICE3/Core/DelphesO2TrackSmearer.cxx b/ALICE3/Core/DelphesO2TrackSmearer.cxx deleted file mode 100644 index 7d7f6fc6077..00000000000 --- a/ALICE3/Core/DelphesO2TrackSmearer.cxx +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file DelphesO2TrackSmearer.cxx -/// \author Roberto Preghenella -/// \brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutCovm.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutCovm.hh -/// DelphesO2/src/TrackSmearer.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.cc -/// DelphesO2/src/TrackSmearer.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.hh -/// @email: preghenella@bo.infn.it -/// - -////////////////////////////// -// DelphesO2/src/lutCovm.cc // -////////////////////////////// - -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" - -#include "ALICE3/Core/GeometryContainer.h" - -#include -#include - -#include - -#include -#include -#include -#include - -namespace o2 -{ -namespace delphes -{ - -/*****************************************************************/ - -bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) -{ - if (!filename || filename[0] == '\0') { - LOG(info) << " --- No LUT file provided for PDG " << pdg << ". Skipping load."; - return false; - } - const auto ipdg = getIndexPDG(pdg); - LOGF(info, "Will load %s lut file ..: '%s'", getParticleName(pdg), filename); - if (mLUTHeader[ipdg] && !forceReload) { - LOG(info) << " --- LUT table for PDG " << pdg << " has been already loaded with index " << ipdg << std::endl; - return false; - } - - const std::string localFilename = o2::fastsim::GeometryEntry::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, 10); - mLUTHeader[ipdg] = new lutHeader_t; - - std::ifstream lutFile(localFilename, std::ifstream::binary); - if (!lutFile.is_open()) { - LOG(info) << " --- cannot open covariance matrix file for PDG " << pdg << ": " << localFilename << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - lutFile.read(reinterpret_cast(mLUTHeader[ipdg]), sizeof(lutHeader_t)); - if (lutFile.gcount() != sizeof(lutHeader_t)) { - LOG(info) << " --- troubles reading covariance matrix header for PDG " << pdg << ": " << filename << std::endl; - LOG(info) << " --- expected/detected " << sizeof(lutHeader_t) << "/" << lutFile.gcount() << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - if (mLUTHeader[ipdg]->version != LUTCOVM_VERSION) { - LOG(info) << " --- LUT header version mismatch: expected/detected = " << LUTCOVM_VERSION << "/" << mLUTHeader[ipdg]->version << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - bool specialPdgCase = false; - switch (pdg) { // Handle special cases - case o2::constants::physics::kAlpha: // Special case: Allow Alpha particles to use He3 LUT - specialPdgCase = (mLUTHeader[ipdg]->pdg == o2::constants::physics::kHelium3); - if (specialPdgCase) - LOG(info) - << " --- Alpha particles (PDG " << pdg << ") will use He3 LUT data (PDG " << mLUTHeader[ipdg]->pdg << ")" << std::endl; - break; - default: - break; - } - if (mLUTHeader[ipdg]->pdg != pdg && !specialPdgCase) { - LOG(info) << " --- LUT header PDG mismatch: expected/detected = " << pdg << "/" << mLUTHeader[ipdg]->pdg << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - const int nnch = mLUTHeader[ipdg]->nchmap.nbins; - const int nrad = mLUTHeader[ipdg]->radmap.nbins; - const int neta = mLUTHeader[ipdg]->etamap.nbins; - const int npt = mLUTHeader[ipdg]->ptmap.nbins; - mLUTEntry[ipdg] = new lutEntry_t****[nnch]; - for (int inch = 0; inch < nnch; ++inch) { - mLUTEntry[ipdg][inch] = new lutEntry_t***[nrad]; - for (int irad = 0; irad < nrad; ++irad) { - mLUTEntry[ipdg][inch][irad] = new lutEntry_t**[neta]; - for (int ieta = 0; ieta < neta; ++ieta) { - mLUTEntry[ipdg][inch][irad][ieta] = new lutEntry_t*[npt]; - for (int ipt = 0; ipt < npt; ++ipt) { - mLUTEntry[ipdg][inch][irad][ieta][ipt] = new lutEntry_t; - lutFile.read(reinterpret_cast(mLUTEntry[ipdg][inch][irad][ieta][ipt]), sizeof(lutEntry_t)); - if (lutFile.gcount() != sizeof(lutEntry_t)) { - LOG(info) << " --- troubles reading covariance matrix entry for PDG " << pdg << ": " << localFilename << std::endl; - LOG(info) << " --- expected/detected " << sizeof(lutHeader_t) << "/" << lutFile.gcount() << std::endl; - return false; - } - } - } - } - } - LOG(info) << " --- read covariance matrix table for PDG " << pdg << ": " << filename << std::endl; - mLUTHeader[ipdg]->print(); - - lutFile.close(); - return true; -} - -/*****************************************************************/ - -lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff) -{ - const int ipdg = getIndexPDG(pdg); - if (!mLUTHeader[ipdg]) { - return nullptr; - } - - auto inch = mLUTHeader[ipdg]->nchmap.find(nch); - auto irad = mLUTHeader[ipdg]->radmap.find(radius); - auto ieta = mLUTHeader[ipdg]->etamap.find(eta); - auto ipt = mLUTHeader[ipdg]->ptmap.find(pt); - - // Interpolate if requested - auto fraction = mLUTHeader[ipdg]->nchmap.fracPositionWithinBin(nch); - if (mInterpolateEfficiency) { - static constexpr float kFractionThreshold = 0.5f; - if (fraction > kFractionThreshold) { - switch (mWhatEfficiency) { - case 1: - if (inch < mLUTHeader[ipdg]->nchmap.nbins - 1) { - interpolatedEff = (1.5f - fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff + (-0.5f + fraction) * mLUTEntry[ipdg][inch + 1][irad][ieta][ipt]->eff; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - } - break; - case 2: - if (inch < mLUTHeader[ipdg]->nchmap.nbins - 1) { - interpolatedEff = (1.5f - fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2 + (-0.5f + fraction) * mLUTEntry[ipdg][inch + 1][irad][ieta][ipt]->eff2; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - } - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } else { - float comparisonValue = mLUTHeader[ipdg]->nchmap.log ? std::log10(nch) : nch; - switch (mWhatEfficiency) { - case 1: - if (inch > 0 && comparisonValue < mLUTHeader[ipdg]->nchmap.max) { - interpolatedEff = (0.5f + fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff + (0.5f - fraction) * mLUTEntry[ipdg][inch - 1][irad][ieta][ipt]->eff; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - } - break; - case 2: - if (inch > 0 && comparisonValue < mLUTHeader[ipdg]->nchmap.max) { - interpolatedEff = (0.5f + fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2 + (0.5f - fraction) * mLUTEntry[ipdg][inch - 1][irad][ieta][ipt]->eff2; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - } - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } - } else { - switch (mWhatEfficiency) { - case 1: - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - break; - case 2: - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } - return mLUTEntry[ipdg][inch][irad][ieta][ipt]; -} //; - -/*****************************************************************/ - -bool TrackSmearer::smearTrack(O2Track& o2track, lutEntry_t* lutEntry, float interpolatedEff) -{ - bool isReconstructed = true; - // generate efficiency - if (mUseEfficiency) { - auto eff = 0.; - switch (mWhatEfficiency) { - case 1: - eff = lutEntry->eff; - break; - case 2: - eff = lutEntry->eff2; - break; - } - if (mInterpolateEfficiency) - eff = interpolatedEff; - if (gRandom->Uniform() > eff) - isReconstructed = false; - } - - // return false already now in case not reco'ed - if (!isReconstructed && mSkipUnreconstructed) - return false; - - // transform params vector and smear - static constexpr int kParSize = 5; - double params[kParSize]; - for (int i = 0; i < kParSize; ++i) { - double val = 0.; - for (int j = 0; j < kParSize; ++j) - val += lutEntry->eigvec[j][i] * o2track.getParam(j); - params[i] = gRandom->Gaus(val, std::sqrt(lutEntry->eigval[i])); - } - // transform back params vector - for (int i = 0; i < kParSize; ++i) { - double val = 0.; - for (int j = 0; j < kParSize; ++j) - val += lutEntry->eiginv[j][i] * params[j]; - o2track.setParam(val, i); - } - // should make a sanity check that par[2] sin(phi) is in [-1, 1] - if (std::fabs(o2track.getParam(2)) > 1.) { - LOG(info) << " --- smearTrack failed sin(phi) sanity check: " << o2track.getParam(2) << std::endl; - } - // set covariance matrix - static constexpr int kCovMatSize = 15; - for (int i = 0; i < kCovMatSize; ++i) - o2track.setCov(lutEntry->covm[i], i); - return isReconstructed; -} - -/*****************************************************************/ - -bool TrackSmearer::smearTrack(O2Track& o2track, int pdg, float nch) -{ - auto pt = o2track.getPt(); - switch (pdg) { - case o2::constants::physics::kHelium3: - case -o2::constants::physics::kHelium3: - pt *= 2.f; - break; - } - auto eta = o2track.getEta(); - float interpolatedEff = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, interpolatedEff); - if (!lutEntry || !lutEntry->valid) - return false; - return smearTrack(o2track, lutEntry, interpolatedEff); -} - -/*****************************************************************/ -// relative uncertainty on pt -double TrackSmearer::getPtRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt; - return val; -} - -/*****************************************************************/ -// relative uncertainty on eta -double TrackSmearer::getEtaRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto sigmatgl = std::sqrt(lutEntry->covm[9]); // sigmatgl2 - auto etaRes = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * sigmatgl; // propagate tgl to eta uncertainty - etaRes /= lutEntry->eta; // relative uncertainty - return etaRes; -} -/*****************************************************************/ -// absolute uncertainty on pt -double TrackSmearer::getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt * lutEntry->pt; - return val; -} - -/*****************************************************************/ -// absolute uncertainty on eta -double TrackSmearer::getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto sigmatgl = std::sqrt(lutEntry->covm[9]); // sigmatgl2 - auto etaRes = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * sigmatgl; // propagate tgl to eta uncertainty - return etaRes; -} -/*****************************************************************/ -// efficiency -double TrackSmearer::getEfficiency(const int pdg, const float nch, const float eta, const float pt) -{ - float efficiency = 0.0f; - getLUTEntry(pdg, nch, 0., eta, pt, efficiency); - return efficiency; -} -/*****************************************************************/ -// Only in DelphesO2 -// bool TrackSmearer::smearTrack(Track& track, bool atDCA) -// { - -// O2Track o2track; -// TrackUtils::convertTrackToO2Track(track, o2track, atDCA); -// int pdg = track.PID; -// float nch = mdNdEta; // use locally stored dNch/deta for the time being -// if (!smearTrack(o2track, pdg, nch)) -// return false; -// TrackUtils::convertO2TrackToTrack(o2track, track, atDCA); -// return true; - -// #if 0 -// lutEntry_t* lutEntry = getLUTEntry(track.PID, 0., 0., track.Eta, track.PT); -// if (!lutEntry) -// return; - -// O2Track o2track; -// TrackUtils::convertTrackToO2Track(track, o2track, atDCA); -// smearTrack(o2track, lutEntry); -// TrackUtils::convertO2TrackToTrack(o2track, track, atDCA); -// #endif -// } - -/*****************************************************************/ - -} // namespace delphes -} // namespace o2 diff --git a/ALICE3/Core/DelphesO2TrackSmearer.h b/ALICE3/Core/DelphesO2TrackSmearer.h deleted file mode 100644 index 6d941f336ca..00000000000 --- a/ALICE3/Core/DelphesO2TrackSmearer.h +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @file DelphesO2TrackSmearer.h -/// @brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutCovm.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutCovm.hh -/// DelphesO2/src/TrackSmearer.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.cc -/// DelphesO2/src/TrackSmearer.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.hh -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it -/// - -#ifndef ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ -#define ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ - -#include -#include - -#include -#include -#include - -/////////////////////////////// -/// DelphesO2/src/lutCovm.hh // -/////////////////////////////// - -#define LUTCOVM_VERSION 20210801 - -struct map_t { - int nbins = 1; - float min = 0.; - float max = 1.e6; - bool log = false; - float eval(int bin) - { - float width = (max - min) / nbins; - float val = min + (bin + 0.5) * width; - if (log) - return pow(10., val); - return val; - } - // function needed to interpolate some dimensions - float fracPositionWithinBin(float val) - { - float width = (max - min) / nbins; - int bin; - float returnVal = 0.5f; - if (log) { - bin = static_cast((log10(val) - min) / width); - returnVal = ((log10(val) - min) / width) - bin; - } else { - bin = static_cast((val - min) / width); - returnVal = val / width - bin; - } - return returnVal; - } - - int find(float val) - { - float width = (max - min) / nbins; - int bin; - if (log) - bin = static_cast((log10(val) - min) / width); // Changed due to MegaLinter error. - // bin = (int)((log10(val) - min) / width); // Original line. - else - bin = static_cast((val - min) / width); // Changed due to MegaLinter error. - // bin = (int)((val - min) / width); // Original line. - if (bin < 0) - return 0; - if (bin > nbins - 1) - return nbins - 1; - return bin; - } //; - void print() { printf("nbins = %d, min = %f, max = %f, log = %s \n", nbins, min, max, log ? "on" : "off"); } //; -}; - -struct lutHeader_t { - int version = LUTCOVM_VERSION; - int pdg = 0; - float mass = 0.; - float field = 0.; - map_t nchmap; - map_t radmap; - map_t etamap; - map_t ptmap; - bool check_version() - { - return (version == LUTCOVM_VERSION); - } //; - void print() - { - printf(" version: %d \n", version); - printf(" pdg: %d \n", pdg); - printf(" field: %f \n", field); - printf(" nchmap: "); - nchmap.print(); - printf(" radmap: "); - radmap.print(); - printf(" etamap: "); - etamap.print(); - printf(" ptmap: "); - ptmap.print(); - } //; -}; - -struct lutEntry_t { - float nch = 0.; - float eta = 0.; - float pt = 0.; - bool valid = false; - float eff = 0.; - float eff2 = 0.; - float itof = 0.; - float otof = 0.; - float covm[15] = {0.}; - float eigval[5] = {0.}; - float eigvec[5][5] = {{0.}}; - float eiginv[5][5] = {{0.}}; - void print() - { - printf(" --- lutEntry: pt = %f, eta = %f (%s)\n", pt, eta, valid ? "valid" : "not valid"); - printf(" efficiency: %f\n", eff); - printf(" covMatix: "); - int k = 0; - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < i + 1; ++j) - printf("% e ", covm[k++]); - printf("\n "); - } - printf("\n"); - } -}; - -//////////////////////////////////// -/// DelphesO2/src/TrackSmearer.hh // -//////////////////////////////////// - -using O2Track = o2::track::TrackParCov; - -namespace o2 -{ -namespace delphes -{ - -class TrackSmearer -{ - - public: - TrackSmearer() = default; - ~TrackSmearer() = default; - - /** LUT methods **/ - bool loadTable(int pdg, const char* filename, bool forceReload = false); - bool hasTable(int pdg) { return (mLUTHeader[getIndexPDG(pdg)] != nullptr); } //; - void useEfficiency(bool val) { mUseEfficiency = val; } //; - void interpolateEfficiency(bool val) { mInterpolateEfficiency = val; } //; - void skipUnreconstructed(bool val) { mSkipUnreconstructed = val; } //; - void setWhatEfficiency(int val) { mWhatEfficiency = val; } //; - lutHeader_t* getLUTHeader(int pdg) { return mLUTHeader[getIndexPDG(pdg)]; } //; - lutEntry_t* getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff); - - bool smearTrack(O2Track& o2track, lutEntry_t* lutEntry, float interpolatedEff); - bool smearTrack(O2Track& o2track, int pdg, float nch); - // bool smearTrack(Track& track, bool atDCA = true); // Only in DelphesO2 - double getPtRes(const int pdg, const float nch, const float eta, const float pt); - double getEtaRes(const int pdg, const float nch, const float eta, const float pt); - double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt); - double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt); - double getEfficiency(const int pdg, const float nch, const float eta, const float pt); - - int getIndexPDG(const int pdg) - { - switch (abs(pdg)) { - case 11: - return 0; // Electron - case 13: - return 1; // Muon - case 211: - return 2; // Pion - case 321: - return 3; // Kaon - case 2212: - return 4; // Proton - case 1000010020: - return 5; // Deuteron - case 1000010030: - return 6; // Triton - case 1000020030: - return 7; // Helium3 - case 1000020040: - return 8; // Alphas - default: - return 2; // Default: pion - } - } - - const char* getParticleName(int pdg) - { - switch (abs(pdg)) { - case 11: - return "electron"; - case 13: - return "muon"; - case 211: - return "pion"; - case 321: - return "kaon"; - case 2212: - return "proton"; - case 1000010020: - return "deuteron"; - case 1000010030: - return "triton"; - case 1000020030: - return "helium3"; - case 1000020040: - return "alpha"; - default: - return "pion"; // Default: pion - } - } - void setdNdEta(float val) { mdNdEta = val; } //; - void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } //; - - protected: - static constexpr unsigned int nLUTs = 9; // Number of LUT available - lutHeader_t* mLUTHeader[nLUTs] = {nullptr}; - lutEntry_t***** mLUTEntry[nLUTs] = {nullptr}; - bool mUseEfficiency = true; - bool mInterpolateEfficiency = false; - bool mSkipUnreconstructed = true; // don't smear tracks that are not reco'ed - int mWhatEfficiency = 1; - float mdNdEta = 1600.; - - private: - o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; -}; - -} // namespace delphes -} // namespace o2 - -namespace o2::delphes -{ -using DelphesO2TrackSmearer = TrackSmearer; -} - -#endif // ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ From d57cbe395df000f12430454f0820353014a1ba11 Mon Sep 17 00:00:00 2001 From: Tucker Hwang <108156178+mhwang285@users.noreply.github.com> Date: Mon, 21 Sep 2026 01:15:21 -0700 Subject: [PATCH 008/648] [PWGJE] Update tracking efficiency task (#17969) --- PWGJE/Tasks/trackEfficiency.cxx | 147 +++++++++++++++++--------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 0eed47da158..779f6df4805 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -56,6 +56,7 @@ struct TrackEfficiency { Configurable trackSelections{"trackSelections", "globalTracks", "set track selections; other option: uniformTracks"}; Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events"}; Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + Configurable rctSelection{"rctSelection", "CBT", "RCT selection to apply if applyRCTSelections is set to true"}; // Tracking efficiency process function configurables: Configurable checkPrimaryPart{"checkPrimaryPart", true, "0: doesn't check mcparticle.isPhysicalPrimary() - 1: checks particle.isPhysicalPrimary()"}; @@ -68,7 +69,9 @@ struct TrackEfficiency { Configurable centralityMax{"centralityMax", 999, ""}; Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable trackDcaZmax{"trackDcaZmax", 99, "additional cut on dcaZ to PV for tracks; uniformTracks in particular don't cut on this at all"}; - Configurable nBinsLowPt{"nBinsLowPt", 200, "number of pt bins for low pt (below 10GeV) efficiency histograms"}; + Configurable ptHighEffMax{"ptHighEffMax", 100., "maximum pT in efficiency histograms for high pT (minimum is set by upper limit of ptLowEffBinning parameter)"}; + Configurable ptHighEffNBins{"ptHighEffNBins", 18, "number of pt bins in efficiency histograms for high pT"}; + ConfigurableAxis ptLowEffBinning{"ptLowEffBinning", {200, 0., 10.}, "binning of low pT axis in efficiency histograms"}; // Track QA process function configurables: Configurable trackQAEtaMin{"trackQAEtaMin", -0.9, "minimum eta acceptance for tracks in the processTracks QA"}; @@ -78,11 +81,10 @@ struct TrackEfficiency { Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied for reconstructed tracks, not mc particles"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied for reconstructed tracks, not mc particles"}; - Configurable> centralityBinning{"centralityBinning", {0., 10., 50., 70., 100}, "binning of centrality histograms"}; - Configurable intRateNBins{"intRateNBins", 50, "number of bins for interaction rate axis"}; - Configurable intRateMax{"intRateMax", 50000.0, "maximum value of interaction rate axis"}; - Configurable phiEffNBins{"phiEffNBins", 200, "number of bins for phi axis in efficiency plots"}; - Configurable etaEffNBins{"etaEffNBins", 200, "number of bins for eta axis in efficiency plots"}; + ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0., 10., 50., 70., 100}, "binning of centrality histograms"}; + ConfigurableAxis intRateBinning{"intRateBinning", {50, 0., 50000.0}, "binning for interaction rate axis"}; + ConfigurableAxis phiEffBinning{"phiEffBinning", {200, -1., 7.}, "binning for phi axis in efficiency histograms"}; + ConfigurableAxis etaEffBinning{"etaEffBinning", {200, -1., 1.}, "binning for eta axis in efficiency histograms"}; Configurable ptHatMin{"ptHatMin", -999, "min pT hat of collisions"}; Configurable ptHatMax{"ptHatMax", 999, "max pT hat of collisions"}; @@ -124,7 +126,7 @@ struct TrackEfficiency { } } else { const auto& aodTrack = jetTrack.template track_as>(); - if (effSystMinNCrossedRowsTPCUseAlternateCut && (aodTrack.tpcNClsCrossedRows() < 120 - 5. / aodTrack.pt())) { + if (effSystMinNCrossedRowsTPCUseAlternateCut && (aodTrack.tpcNClsCrossedRows() < 120 - 5. / aodTrack.pt())) { // o2-linter: disable=magic-number (TPC nCrossedRows calculation) return false; } if (customTrackSelection.IsSelected(aodTrack)) { @@ -247,18 +249,18 @@ struct TrackEfficiency { LOGP(info, "Using standard track selection: %s", trackSelections.value); } - AxisSpec ptAxisEff = {nBinsLowPt, 0., 10., "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec ptAxisHighEff = {18, 10., 100., "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec etaAxisEff = {etaEffNBins, -1.0, 1.0, "#eta"}; - AxisSpec phiAxisEff = {phiEffNBins, -1.0, 7., "#phi"}; + AxisSpec ptAxisEff = {ptLowEffBinning, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptAxisHighEff = {ptHighEffNBins, ptAxisEff.binEdges.back(), ptHighEffMax, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec etaAxisEff{etaEffBinning, "#eta"}; + AxisSpec phiAxisEff{phiEffBinning, "#phi"}; AxisSpec centAxis = {centralityBinning, "centrality (%)"}; - AxisSpec intRateAxis = {intRateNBins, 0., intRateMax, "int. rate (kHz)"}; + AxisSpec intRateAxis = {intRateBinning, "int. rate (kHz)"}; AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; AxisSpec dcaxyAxis = {1000, -1.0, 1.0, "dca_{xy}"}; AxisSpec dcazAxis = {4000, -4.0, 4.0, "dca_{z}"}; - if (doprocessEFficiencyPurity || doprocessEFficiencyPurityWeighted || doprocessQcCheck) { + if (doprocessEfficiencyPurity || doprocessEfficiencyPurityWeighted || doprocessQcCheck) { registry.add("hMcCollCutsCounts", "McColl cuts count checks", {HistType::kTH1F, {{10, 0., 10.}}}); registry.get(HIST("hMcCollCutsCounts"))->GetXaxis()->SetBinLabel(1, "allMcColl"); @@ -283,11 +285,11 @@ struct TrackEfficiency { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(2, "trackSel"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(3, "hasMcParticle"); - if (doprocessEFficiencyPurity || doprocessQcCheck) { + if (doprocessEfficiencyPurity || doprocessQcCheck) { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(4, "mcPartIsPrimary"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(5, "etaAcc"); // not actually applied here but it will give an idea of what will be done in the post processing } - if (doprocessEFficiencyPurityWeighted) { + if (doprocessEfficiencyPurityWeighted) { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(4, "ptHatMaxFraction"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(5, "mcPartIsPrimary"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(6, "etaAcc"); // not actually applied here but it will give an idea of what will be done in the post processing @@ -457,7 +459,7 @@ struct TrackEfficiency { Filter particleCuts = (aod::jmcparticle::pt >= trackQAPtMin && aod::jmcparticle::pt < trackQAPtMax && aod::jmcparticle::eta > trackQAEtaMin && aod::jmcparticle::eta < trackQAEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut); - void processEFficiencyPurity(aod::JetMcCollisions::iterator const& mcCollision, + void processEfficiencyPurity(aod::JetMcCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, // smallgroups gives only the collisions associated to the current mccollision, thanks to the mccollisionlabel pre-integrated in jetcollisionsmcd soa::Join const& jetTracks, soa::Join const&, @@ -489,8 +491,8 @@ struct TrackEfficiency { bool hasSel8Coll = false; bool centralityCheck = false; bool occupancyCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -502,7 +504,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -574,7 +576,7 @@ struct TrackEfficiency { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections) || !(std::abs(collision.posZ()) < vertexZCut)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { continue; } @@ -589,7 +591,6 @@ struct TrackEfficiency { if (!track.has_mcParticle()) { registry.fill(HIST("h3_track_pt_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); - registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); continue; } @@ -604,6 +605,8 @@ struct TrackEfficiency { registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + // occasionally, one particle can match to two reconstructed tracks + // since this particle will appear again in this loop over tracks, these histograms keep track of the extra entries from this duplication registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_nonprimary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -627,6 +630,8 @@ struct TrackEfficiency { registry.fill(HIST("h2_particle_pt_high_track_pt_high_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + // same as above, but doesn't overlap with the previous std::find call since we looked at non-physical primaries before and now we are only looking at physical primaries + // to get a histogram corrected for the duplication (e.g. for efficiency/resolution studies), you should subtract these histograms below from the corresponding histograms right before this if block registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_primary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -642,9 +647,9 @@ struct TrackEfficiency { } } } - PROCESS_SWITCH(TrackEfficiency, processEFficiencyPurity, "Histograms for efficiency and purity quantities", true); + PROCESS_SWITCH(TrackEfficiency, processEfficiencyPurity, "Histograms for efficiency and purity quantities", true); - void processEFficiencyPurityWeighted(aod::JetMcCollisions::iterator const& mcCollision, + void processEfficiencyPurityWeighted(aod::JetMcCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, // smallgroups gives only the collisions associated to the current mccollision, thanks to the mccollisionlabel pre-integrated in jetcollisionsmcd soa::Join const& jetTracks, soa::Join const&, @@ -675,8 +680,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -685,7 +690,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -751,7 +756,7 @@ struct TrackEfficiency { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections) || !(std::abs(collision.posZ()) < vertexZCut)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { continue; } @@ -831,13 +836,13 @@ struct TrackEfficiency { } } } - PROCESS_SWITCH(TrackEfficiency, processEFficiencyPurityWeighted, "Histograms for efficiency and purity quantities for weighted simulations", false); + PROCESS_SWITCH(TrackEfficiency, processEfficiencyPurityWeighted, "Histograms for efficiency and purity quantities for weighted simulations", false); void processTracksFromData(soa::Filtered::iterator const& collision, soa::Filtered> const& jetTracks, soa::Join const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -860,7 +865,7 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -889,7 +894,7 @@ struct TrackEfficiency { return; } float eventWeight = collision.mcCollision().weight(); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -933,8 +938,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -943,7 +948,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -996,8 +1001,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -1006,7 +1011,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1037,7 +1042,7 @@ struct TrackEfficiency { registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 0.5); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1066,7 +1071,7 @@ struct TrackEfficiency { } registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 0.5); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1103,7 +1108,7 @@ struct TrackEfficiency { float eventWeight = collision.mcCollision().weight(); registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h_collisions_weighted"), 0.5, eventWeight); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1140,9 +1145,9 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -1151,7 +1156,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1213,9 +1218,9 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -1224,7 +1229,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1276,7 +1281,7 @@ struct TrackEfficiency { void processTrackSelectionHistograms(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1330,7 +1335,7 @@ struct TrackEfficiency { registry.fill(HIST("h2_occupancy_ntracksall_presel"), occupancy, nTracksAll); registry.fill(HIST("h2_occupancy_ntrackssel_presel"), occupancy, nTracksAllAcceptanceAndSelected); registry.fill(HIST("h2_occupancy_ntracksselptetacuts_presel"), occupancy, nTracksInAcceptanceAndSelected); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h2_occupancy_ntracksall_postsel"), occupancy, nTracksAll); @@ -1341,7 +1346,7 @@ struct TrackEfficiency { void processItsTpcMatchingData(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1404,7 +1409,7 @@ struct TrackEfficiency { void processItsTpcMatchingMC(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&, aod::McParticles const&) { // could be added in future: pions+kaons vs protons distinction; easy in MC but what is the procedure for data? - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1534,8 +1539,8 @@ struct TrackEfficiency { bool hasSel8Coll = false; bool centralityCheck = false; bool occupancyCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -1547,7 +1552,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -1619,16 +1624,16 @@ struct TrackEfficiency { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections) || !(std::abs(collision.posZ()) < vertexZCut)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { continue; } auto collTracks = jetTracks.sliceBy(tracksPerJCollision, collision.globalIndex()); - int ntrack_nonassociatedtrack = 0; - int ntrack_associatedtrack_nonprimary = 0; - int ntrack_associatedtrack_primary = 0; - int ntrack_associatedtrack_split_nonprimary = 0; - int ntrack_associatedtrack_split_primary = 0; + int nTrackNonassociatedTrack = 0; + int nTrackAssociatedTrackNonprimary = 0; + int nTrackAssociatedTrackPrimary = 0; + int nTrackAssociatedTrackSplitNonprimary = 0; + int nTrackAssociatedTrackSplitPrimary = 0; for (auto const& track : collTracks) { registry.fill(HIST("hTrackCutsCounts"), 0.5); @@ -1638,7 +1643,7 @@ struct TrackEfficiency { registry.fill(HIST("hTrackCutsCounts"), 1.5); if (!track.has_mcParticle()) { - ntrack_nonassociatedtrack += 1; + nTrackNonassociatedTrack += 1; registry.fill(HIST("h3_track_pt_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); @@ -1649,7 +1654,7 @@ struct TrackEfficiency { auto jMcParticleFromTrack = track.mcParticle_as(); if (!jMcParticleFromTrack.isPhysicalPrimary()) { - ntrack_associatedtrack_nonprimary += 1; + nTrackAssociatedTrackNonprimary += 1; registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_nonprimary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -1658,7 +1663,7 @@ struct TrackEfficiency { registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { - ntrack_associatedtrack_split_nonprimary += 1; + nTrackAssociatedTrackSplitNonprimary += 1; registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_nonprimary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -1674,7 +1679,7 @@ struct TrackEfficiency { registry.fill(HIST("hTrackCutsCounts"), 3.5); - ntrack_associatedtrack_primary += 1; + nTrackAssociatedTrackPrimary += 1; registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_primary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); registry.fill(HIST("h2_particle_pt_track_pt_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); @@ -1684,7 +1689,7 @@ struct TrackEfficiency { registry.fill(HIST("h2_particle_pt_high_track_pt_high_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { - ntrack_associatedtrack_split_primary += 1; + nTrackAssociatedTrackSplitPrimary += 1; registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_primary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -1698,11 +1703,11 @@ struct TrackEfficiency { registry.fill(HIST("hTrackCutsCounts"), 4.5); } } - registry.fill(HIST("h_ntrack_nonassociatedtrack"), ntrack_nonassociatedtrack); - registry.fill(HIST("h_ntrack_associatedtrack_nonprimary"), ntrack_associatedtrack_nonprimary); - registry.fill(HIST("h_ntrack_associatedtrack_split_nonprimary"), ntrack_associatedtrack_split_nonprimary); - registry.fill(HIST("h_ntrack_associatedtrack_primary"), ntrack_associatedtrack_primary); - registry.fill(HIST("h_ntrack_associatedtrack_split_primary"), ntrack_associatedtrack_split_primary); + registry.fill(HIST("h_ntrack_nonassociatedtrack"), nTrackNonassociatedTrack); + registry.fill(HIST("h_ntrack_associatedtrack_nonprimary"), nTrackAssociatedTrackNonprimary); + registry.fill(HIST("h_ntrack_associatedtrack_split_nonprimary"), nTrackAssociatedTrackSplitNonprimary); + registry.fill(HIST("h_ntrack_associatedtrack_primary"), nTrackAssociatedTrackPrimary); + registry.fill(HIST("h_ntrack_associatedtrack_split_primary"), nTrackAssociatedTrackSplitPrimary); } } PROCESS_SWITCH(TrackEfficiency, processQcCheck, "Histograms for QC checks", false); From 8dff104e33ef2868abb0697f5ac456f0a48534f0 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Mon, 21 Sep 2026 10:15:35 +0200 Subject: [PATCH 009/648] [PWGJE] fix a few errors. introduced dcaxy cut. added event counter for subsamples (#17974) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 113 ++++++++++++++---------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 59d6c7a7598..cff612f536e 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -70,8 +70,11 @@ using namespace o2::framework::expressions; struct JetSpectraEseTask { Configurable cfgSystFlag{"cfgSystFlag", -1, "-1 = use configurable cuts, 0 = nominal defaults, >0 = systematic cut variation"}; - Configurable cfgEfficiency{"cfgEfficiency", "", "CCDB path to efficiency"}; - Configurable cfgEfficiency3D{"cfgEfficiency3D", "", "CCDB path to 3D efficiency"}; + + struct : ConfigurableGroup { + Configurable cfgEfficiency{"cfgEfficiency", "", "CCDB path to efficiency"}; + Configurable cfgEfficiency3D{"cfgEfficiency3D", "", "CCDB path to 3D efficiency"}; + } efficiencyFiles; Configurable jetPtMin{"jetPtMin", 5.0, "minimum jet pT cut"}; Configurable jetR{"jetR", 0.2, "jet resolution parameter"}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; @@ -101,8 +104,9 @@ struct JetSpectraEseTask { Configurable trackDCAzMax{"trackDCAzMax", 2.0, "maximum DCAz for tracks"}; Configurable cfgNTPCXrows{"cfgNTPCXrows", 70, "minimum number of crossed rows in the TPC for tracks"}; Configurable cfgNTPCCls{"cfgNTPCCls", 50, "minimum number of clusters in the TPC for tracks"}; - Configurable cfgChi2PrTPCcls{"cfgChi2PrTPCcls", 2.5, "cut for chi2 per TPC cluster for tracks"}; + Configurable cfgChi2PrTPCcls{"cfgChi2PrTPCcls", 4.0, "cut for chi2 per TPC cluster for tracks"}; Configurable cfgChi2PrITScls{"cfgChi2PrITScls", 36, "cut for chi2 per ITS cluster for tracks"}; + Configurable dcaXYSigmaMax{"dcaXYSigmaMax", 7.0, "maximum DCAxy sigma significance for tracks"}; } trackCuts; Configurable> trackPtRhoPhi{"trackPtRhoPhi", {0.2, 5.0}, "pT range for tracks used in rho(phi) calculation"}; @@ -162,22 +166,15 @@ struct JetSpectraEseTask { static constexpr float RScale = 100.0f; static constexpr float EtaAcceptance = 0.9f; static constexpr float LowFT0Cut = 1e-8; - static constexpr float NominalVertexZCut = 10.0f; - static constexpr float NominalTrackDCAzMax = 2.0f; - static constexpr float NominalNTPCXrows = 70.0f; - static constexpr float NominalNTPCCls = 50.0f; - static constexpr float NominalChi2PrTPCcls = 2.5f; - static constexpr float NominalChi2PrITScls = 36.0f; - static constexpr float NoDCAxySigmaCut = -1.0f; static constexpr float LooseVertexZFilter = 999.0f; struct SystematicCuts { float vertexZCut = 10.0f; float trackDCAzMax = 2.0f; - float dcaXYSigmaMax = -1.0f; + float dcaXYSigmaMax = 7.0f; float nTPCXrows = 70.0f; float nTPCCls = 50.0f; - float chi2PrTPCcls = 2.5f; + float chi2PrTPCcls = 4.0f; float chi2PrITScls = 36.0f; } systCuts; @@ -252,12 +249,13 @@ struct JetSpectraEseTask { static constexpr int NumSubSmpl = 5; static constexpr int NumSavedRhoFitEvents = 5; std::array, NumSubSmpl> hSameSub; + std::array, NumSubSmpl> hCentralityAnalyzedSub; void applySystematicPreset() { systCuts.vertexZCut = vertexZCut.value; systCuts.trackDCAzMax = trackCuts.trackDCAzMax.value; - systCuts.dcaXYSigmaMax = NoDCAxySigmaCut; + systCuts.dcaXYSigmaMax = trackCuts.dcaXYSigmaMax.value; systCuts.nTPCXrows = trackCuts.cfgNTPCXrows.value; systCuts.nTPCCls = trackCuts.cfgNTPCCls.value; systCuts.chi2PrTPCcls = trackCuts.cfgChi2PrTPCcls.value; @@ -268,15 +266,6 @@ struct JetSpectraEseTask { LOGF(info, "JetSpectraEseTask::applySystematicPreset() - using configurable cut values"); return; } - - systCuts.vertexZCut = NominalVertexZCut; - systCuts.trackDCAzMax = NominalTrackDCAzMax; - systCuts.dcaXYSigmaMax = NoDCAxySigmaCut; - systCuts.nTPCXrows = NominalNTPCXrows; - systCuts.nTPCCls = NominalNTPCCls; - systCuts.chi2PrTPCcls = NominalChi2PrTPCcls; - systCuts.chi2PrITScls = NominalChi2PrITScls; - switch (flag) { case 0: break; @@ -305,7 +294,7 @@ struct JetSpectraEseTask { systCuts.nTPCCls = 70.0f; break; case 9: - systCuts.chi2PrTPCcls = 4.0f; + systCuts.chi2PrTPCcls = 2.5f; break; case 10: systCuts.nTPCXrows = 80.0f; @@ -435,6 +424,8 @@ struct JetSpectraEseTask { for (int i = 0; i < NumSubSmpl; ++i) { std::string n = fmt::format("subsamples/thn_jethad_corr_same_subsample{}", i); hSameSub[i] = registry.add(n, "same;...", o2::framework::HistType::kTHnSparseF, axes); + std::string n2 = fmt::format("subsamples/hCentralityAnalyzed_subsample{}", i); + hCentralityAnalyzedSub[i] = registry.add(n2, ";Centrality;entries", o2::framework::HistType::kTH1F, {{centAxis}}); } registry.add("hNtrig", "", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}}); @@ -448,8 +439,10 @@ struct JetSpectraEseTask { registry.add("trackQA/after/hNClsCrossed", "", {HistType::kTH1F, {{100, 40, 140}}}); registry.add("trackQA/before/hNClsMin", "", {HistType::kTH1F, {{100, 40, 140}}}); registry.add("trackQA/after/hNClsMin", "", {HistType::kTH1F, {{100, 40, 140}}}); - registry.add("trackQA/before/hDCAz", "", {HistType::kTH1F, {{50, 0, 3}}}); - registry.add("trackQA/after/hDCAz", "", {HistType::kTH1F, {{50, 0, 3}}}); + registry.add("trackQA/before/hDCAz", "", {HistType::kTH1F, {{50, -3, 3}}}); + registry.add("trackQA/after/hDCAz", "", {HistType::kTH1F, {{50, -3, 3}}}); + registry.add("trackQA/before/hDCAxy", "", {HistType::kTH1F, {{50, -1, 1}}}); + registry.add("trackQA/after/hDCAxy", "", {HistType::kTH1F, {{50, -1, 1}}}); registry.add("trackQA/before/hChi2TPC", "", {HistType::kTH1F, {{50, 0, 6}}}); registry.add("trackQA/after/hChi2TPC", "", {HistType::kTH1F, {{50, 0, 6}}}); registry.add("trackQA/before/hChi2ITS", "", {HistType::kTH1F, {{50, 0, 38}}}); @@ -641,28 +634,27 @@ struct JetSpectraEseTask { return; } const auto efficiencyName = fmt::format("efficiency_{}", std::max(cfgSystFlag.value, 0)); - if (!cfgEfficiency.value.empty()) { - cfg.hEffList = ccdb->getForTimeStamp(cfgEfficiency, timestamp); - if (cfg.hEffList == nullptr) { - LOGF(fatal, "Could not load track efficiency list from %s", cfgEfficiency.value.c_str()); - } - cfg.hEff = dynamic_cast(cfg.hEffList->FindObject(efficiencyName.c_str())); - if (cfg.hEff == nullptr) { - LOGF(fatal, "Could not find %s as TH1F in track efficiency list %s", efficiencyName.c_str(), cfgEfficiency.value.c_str()); - } - LOGF(info, "Loaded tracking efficiency %s from %s", efficiencyName.c_str(), cfgEfficiency.value.c_str()); - } - if (!cfgEfficiency3D.value.empty()) { - cfg.h3EffList = ccdb->getForTimeStamp(cfgEfficiency3D, timestamp); + if (!efficiencyFiles.cfgEfficiency3D.value.empty()) { + cfg.h3EffList = ccdb->getForTimeStamp(efficiencyFiles.cfgEfficiency3D, timestamp); if (cfg.h3EffList == nullptr) { - LOGF(fatal, "Could not load 3D track efficiency list from %s", cfgEfficiency3D.value.c_str()); + LOGF(fatal, "Could not load 3D track efficiency list from %s", efficiencyFiles.cfgEfficiency3D.value.c_str()); } cfg.h3Eff = dynamic_cast(cfg.h3EffList->FindObject(efficiencyName.c_str())); if (cfg.h3Eff == nullptr) { - LOGF(fatal, "Could not find %s as TH3F in 3D track efficiency list %s", efficiencyName.c_str(), cfgEfficiency3D.value.c_str()); + LOGF(fatal, "Could not find %s as TH3F in 3D track efficiency list %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency3D.value.c_str()); } - LOGF(info, "Loaded 3D tracking efficiency %s from %s", efficiencyName.c_str(), cfgEfficiency3D.value.c_str()); + LOGF(info, "Loaded 3D tracking efficiency %s from %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency3D.value.c_str()); cfg.is3D = true; + } else if (!efficiencyFiles.cfgEfficiency.value.empty()) { + cfg.hEffList = ccdb->getForTimeStamp(efficiencyFiles.cfgEfficiency, timestamp); + if (cfg.hEffList == nullptr) { + LOGF(fatal, "Could not load track efficiency list from %s", efficiencyFiles.cfgEfficiency.value.c_str()); + } + cfg.hEff = dynamic_cast(cfg.hEffList->FindObject(efficiencyName.c_str())); + if (cfg.hEff == nullptr) { + LOGF(fatal, "Could not find %s as TH1F in track efficiency list %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded tracking efficiency %s from %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency.value.c_str()); } cfg.isLoaded = true; } @@ -729,6 +721,7 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hRho"), centrality, collision.rho()); registry.fill(HIST("eventQA/hCentralityAnalyzed"), centrality); int lRndInd = fRndm->Integer(NumSubSmpl); + hCentralityAnalyzedSub[lRndInd]->Fill(centrality); auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; for (const auto& jet : jets) { @@ -790,6 +783,7 @@ struct JetSpectraEseTask { registry.fill(HIST("trackQA/before/hNClsCrossed"), trk.tpcNClsCrossedRows()); registry.fill(HIST("trackQA/before/hNClsMin"), trk.tpcNClsFound()); registry.fill(HIST("trackQA/before/hDCAz"), trk.dcaZ()); + registry.fill(HIST("trackQA/before/hDCAxy"), trk.dcaXY()); registry.fill(HIST("trackQA/before/hChi2TPC"), trk.tpcChi2NCl()); registry.fill(HIST("trackQA/before/hChi2ITS"), trk.itsChi2NCl()); if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { @@ -804,6 +798,7 @@ struct JetSpectraEseTask { registry.fill(HIST("trackQA/after/hNClsCrossed"), trk.tpcNClsCrossedRows()); registry.fill(HIST("trackQA/after/hNClsMin"), trk.tpcNClsFound()); registry.fill(HIST("trackQA/after/hDCAz"), trk.dcaZ()); + registry.fill(HIST("trackQA/after/hDCAxy"), trk.dcaXY()); registry.fill(HIST("trackQA/after/hChi2TPC"), trk.tpcChi2NCl()); registry.fill(HIST("trackQA/after/hChi2ITS"), trk.itsChi2NCl()); registry.fill(HIST("h3CenttrPhiPsi2"), centrality, RecoDecay::constrainAngle(track.phi() - psi.psi2, -o2::constants::math::PI), qPerc[0]); @@ -873,7 +868,7 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hEventCounterMixed"), kRhoLocal); if (fLeadJetPtCut) { if (!isAcceptedLeadingJet(c1, jets1, centrality)) { - return; + continue; } } registry.fill(HIST("eventQA/hEventCounterMixed"), kLeadJetCut); @@ -1271,7 +1266,7 @@ struct JetSpectraEseTask { if (mcCollision.size() < 1) { return; } - if (collisions.size() < 1) { + if (collisions.size() != 1) { return; } if (!(std::abs(mcCollision.posZ()) < systCuts.vertexZCut)) { @@ -1589,7 +1584,10 @@ struct JetSpectraEseTask { modulationFit->FixParameter(2, (ep.psi2 < 0) ? RecoDecay::constrainAngle(ep.psi2) : ep.psi2); modulationFit->FixParameter(4, (ep.psi3 < 0) ? RecoDecay::constrainAngle(ep.psi3) : ep.psi3); - hPhiPt->Fit(modulationFit.get(), "QN", "", 0, o2::constants::math::TwoPI); + const int fitStatus = hPhiPt->Fit(modulationFit.get(), "QN", "", 0, o2::constants::math::TwoPI); + if (fitStatus != 0) { + return nullptr; + } if constexpr (fillHist) { registry.fill(HIST("eventQA/hfitPar0"), getCentrality(col), modulationFit->GetParameter(0)); @@ -1603,22 +1601,13 @@ struct JetSpectraEseTask { return nullptr; } - double chi2{0.}; - for (int i{0}; i < hPhiPt->GetXaxis()->GetNbins(); i++) { - if (hPhiPt->GetBinContent(i + 1) <= 0.) { - continue; - } - chi2 += std::pow((hPhiPt->GetBinContent(i + 1) - modulationFit->Eval(hPhiPt->GetXaxis()->GetBinCenter(1 + i))), 2) / hPhiPt->GetBinContent(i + 1); - } - - int nDF{1}; - int numParams{2}; - nDF = static_cast(modulationFit->GetXaxis()->GetNbins()) - numParams; + const double chi2 = modulationFit->GetChisquare(); + const int nDF = modulationFit->GetNDF(); if (nDF <= 0) { return nullptr; } - auto cDF = 1. - TMath::Gamma(nDF, chi2); + const double cDF = TMath::Prob(chi2, nDF); if constexpr (fillHist) { registry.fill(HIST("eventQA/hRhoPhiCheck"), 0.5); } @@ -1798,11 +1787,11 @@ struct JetSpectraEseTask { } } } + registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoOneLeadingJet"), getCentrality(collision), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); + registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoTwoLeadingJet"), getCentrality(collision), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); } registry.fill(HIST("h3CentdeltapTRndmConePhi_rhovsphi"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), dPhiRC); registry.fill(HIST("h3CentdeltapTRndmConePhi_localrhovsphi"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC); - registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoOneLeadingJet"), getCentrality(collision), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); - registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoTwoLeadingJet"), getCentrality(collision), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); } template bool isTrackInJet(TTracks const& track, TJets const& jet) @@ -1970,16 +1959,18 @@ struct JetSpectraEseTask { if (track.eta() <= trackCuts.trackEtaMin || track.eta() >= trackCuts.trackEtaMax) { return false; } - if (track.tpcNClsCrossedRows() <= systCuts.nTPCXrows) { + if (track.tpcNClsCrossedRows() < systCuts.nTPCXrows) { return false; } - if (track.tpcNClsFound() <= systCuts.nTPCCls) { + if (track.tpcNClsFound() < systCuts.nTPCCls) { return false; } if (std::fabs(track.dcaZ()) >= systCuts.trackDCAzMax) { return false; } - // cfgSystFlag=6 reserves systCuts.dcaXYSigmaMax for a future DCAxy significance cut. + if (std::fabs(track.dcaXY()) > (systCuts.dcaXYSigmaMax / 7.f) * (0.0105f + 0.0350f / std::pow(track.pt(), 1.1f))) { + return false; + } if (track.tpcChi2NCl() > systCuts.chi2PrTPCcls) { return false; } From 91be96c7c07aa97bab686e17e78ae69a5e7ec502 Mon Sep 17 00:00:00 2001 From: arvindkhuntia <31609955+arvindkhuntia@users.noreply.github.com> Date: Mon, 21 Sep 2026 12:55:16 +0200 Subject: [PATCH 010/648] [PWGJE] TOF-veto selection fixed (#17971) Co-authored-by: Arvind Khuntia --- PWGJE/Tasks/nucleiInJets.cxx | 241 +++++++++++++++++++++++++++++------ 1 file changed, 202 insertions(+), 39 deletions(-) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 8b9c88de34d..82ce31ad6da 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -114,6 +114,18 @@ struct nucleiInJets { } } + template + int getParticleOriginType(const T& mcParticle) + { + if (mcParticle.isPhysicalPrimary()) { + return 1; // physical primary + } + if (mcParticle.getProcess() == TMCProcess::kPDecay) { + return 3; // weak-decay feed-down + } + return 2; // other secondary / material / transport + } + Configurable cfgtrackSelections{"cfgtrackSelections", "globalTracks", "set track selections"}; Configurable isMC{"isMC", false, "flag for the MC"}; Configurable isWithJetEvents{"isWithJetEvents", true, "Events with at least one jet"}; @@ -138,6 +150,7 @@ struct nucleiInJets { Configurable cfgnITSChi2{"cfgnITShi2", 36.0, "nITS Chi2 per Cluster"}; Configurable cfgjetPtMin{"cfgjetPtMin", 5.0, "minimum jet pT cut"}; + Configurable cfgjetPtBkgSubMin{"cfgjetPtBkgSubMin", 10.0f, "minimum background-subtracted jet pT cut for data jet-event normalization"}; Configurable cfgjetR{"cfgjetR", 0.4, "jet resolution parameter"}; Configurable cDebugLevel{"cDebugLevel", 0, "print debug msg"}; Configurable cMaxPt{"cMaxPt", 10, "max pt for Hist"}; @@ -166,6 +179,7 @@ struct nucleiInJets { Configurable useMcC{"useMcC", true, "use mcC"}; Configurable usebkgSubractionMC{"usebkgSubractionMC", true, "use rho-area background subtraction for detector-level matched MC jets"}; Configurable cfgjetPtBkgSubMinMC{"cfgjetPtBkgSubMinMC", 10.0f, "minimum detector-level matched MC jet pT after optional background subtraction"}; + Configurable useFullConeAcceptance{"useFullConeAcceptance", true, "require jet/perpendicular-cone axes to satisfy |eta_axis| < cfgtrkMaxEta - cfgjetR for jet and efficiency histograms"}; Configurable useRapidityCutForPID{"useRapidityCutForPID", false, "true: use rapidity cut for PID, false: no rapidity cut for PID"}; Configurable isPOCollision{"isPOCollision", false, "true: use pO rapidity shift for inclusive PID/efficiency rapidity cuts; false: symmetric system, no rapidity shift"}; Configurable cfgPORapidityShiftForPID{"cfgPORapidityShiftForPID", 0.0, "pO rapidity shift for inclusive PID/efficiency rapidity cuts: y_{CMS}=y_{lab}-shift. Set sign/value according to beam convention"}; @@ -188,6 +202,15 @@ struct nucleiInJets { return std::abs(rap) < std::abs(static_cast(cfgtrkMaxRap)); } + bool isConeAxisAccepted(double etaAxis) + { + if (!useFullConeAcceptance) { + return true; + } + const double maxAcceptedConeAxisEta = static_cast(cfgtrkMaxEta) - static_cast(cfgjetR); + return maxAcceptedConeAxisEta > 0. && std::abs(etaAxis) < maxAcceptedConeAxisEta; + } + Configurable addpik{"addpik", true, "add pion and kaon hist"}; ConfigurableAxis binsDCA{"binsDCA", {400, -1.f, 1.f}, ""}; ConfigurableAxis binsdEdx{"binsdEdx", {1000, 0.f, 1000.f}, ""}; @@ -252,6 +275,7 @@ struct nucleiInJets { jetHist.print(); // const AxisSpec PtAxis = {100, 0, 10.0}; const AxisSpec PtJetAxis = {100, 0, 100.0}; + const AxisSpec PtJetBkgSubAxis = {240, -20.f, 100.f, "#it{p}_{T}^{jet,bkg sub} (GeV/#it{c})"}; const AxisSpec MultAxis = {100, 0, 100}; const AxisSpec dRAxis = {100, 0, 3.6}; const AxisSpec CentAxis = {100, 0, 100}; @@ -260,6 +284,7 @@ struct nucleiInJets { const AxisSpec dedxAxis{binsdEdx, "d#it{E}/d#it{x} A.U."}; const AxisSpec vzAxis{300, -15.f, 15.f, "Vz (cm)"}; const AxisSpec EtaAxis{40, -1.f, 1.f, "#eta"}; + const AxisSpec ParticleOriginAxis{3, 0.5, 3.5, "origin: 1 primary, 2 secondary/material, 3 weak decay"}; const AxisSpec betaAxis{binsBeta, "TOF #beta"}; const AxisSpec ptZHeAxis{binsPtZHe, "#it{p}_{T}"}; @@ -270,7 +295,7 @@ struct nucleiInJets { const AxisSpec massHeAxis{binsMassHe, ""}; const AxisSpec PtAxis{ptAxisConf, ""}; - jetHist.add("hNEvents", "hNEvents", {HistType::kTH1D, {{8, 0.f, 8.f}}}); + jetHist.add("hNEvents", "hNEvents", {HistType::kTH1D, {{9, 0.f, 9.f}}}); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "All"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "Skimmed"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(3, "|Vz|<10"); @@ -279,6 +304,7 @@ struct nucleiInJets { jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(6, "isGoodZvtxFT0vsPV"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(7, "OccupancySel"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(8, "nJets>0"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(9, "leading jet bkg-sub pT cut"); jetHist.add("hNEventsInc", "hNEventsInc", {HistType::kTH1D, {{6, 0.f, 6.f}}}); jetHist.get(HIST("hNEventsInc"))->GetXaxis()->SetBinLabel(1, "All"); @@ -302,12 +328,21 @@ struct nucleiInJets { jetHist.add("jet/h2JetPtVsBkgRho", "jet_{p_{T}} vs background #rho; jet_{p_{T}} (GeV/c); #rho (GeV/c/area)", kTH2F, {PtJetAxis, {100, 0, 20}}); jetHist.add("jet/h1BkgRho", "Background #rho; #rho (GeV/c/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEvents", "NumbeOfJetEvents", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut", "Background #rho for events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; #rho (GeV/#it{c}/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEta", "jet_{#eta}", kTH1F, {{100, -1.0, 1.0}}); jetHist.add("jet/h1JetPhi", "jet_{#phi}", kTH1F, {{80, -1.0, 7.}}); jetHist.add("jet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); + jetHist.add("jet/qa/h2JetConeEtaPhiBeforeAcceptance", "jet-cone axis before full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/h2JetConeEtaPhiAfterAcceptance", "jet-cone axis after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/h2PerpConeEtaPhiAfterAcceptance", "perpendicular-cone axes after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/nAcceptedJetsPerEvent", "number of full-cone-accepted jets per event;N_{jets};Entries", kTH1F, {{15, .0, 15.}}); + jetHist.add("jetBkgSub/h1LeadingJetPtBkgSub", "leading jet background-subtracted #it{p}_{T}; #it{p}_{T}^{jet,bkg sub} (GeV/#it{c}); Entries", kTH1F, {PtJetBkgSubAxis}); + jetHist.add("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ", "leading jet background-subtracted #it{p}_{T} vs V_{z}; #it{p}_{T}^{jet,bkg sub} (GeV/#it{c}); V_{z} (cm)", kTH2F, {PtJetBkgSubAxis, vzAxis}); jetHist.add("mcpJet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); jetHist.add("mcdJet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); jetHist.add("jet/vertexZ", "vertexZ (Jet flag)", kTH1F, {{vzAxis}}); + jetHist.add("jetBkgSub/vertexZ", "vertexZ for events with p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; V_{z} (cm); Entries", kTH1F, {{vzAxis}}); jetHist.add("vertexZ", "vertexZ (all)", kTH1F, {{vzAxis}}); jetHist.add("jetOut/vertexZ", "vertexZ (without z-flag)", kTH1F, {{vzAxis}}); //////////////////////////// @@ -736,6 +771,9 @@ struct nucleiInJets { jetHist.add("recmatched/h2ResponseMatrix", "matched jet pT;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); jetHist.add("recmatched/h2ResponseMatrixLeadingJet", "matched jet rec pT vs true pt;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); jetHist.add("recmatched/mcC/h2ResponseMatrixLeadingJet", "matched jet rec pT vs true pt;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); + jetHist.add("recmatched/qa/h2JetConeEtaPhiBeforeAcceptance", "matched jet-cone axis before full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("recmatched/qa/h2JetConeEtaPhiAfterAcceptance", "matched jet-cone axis after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance", "matched perpendicular-cone axes after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); ///////// jetHist.add("recmatched/hRecJetPt", "matched jet pT (Rec level);#it{p}_{T,jet part} (GeV/#it{c}); #it{p}_{T,jet part} - #it{p}_{T,jet det}", HistType::kTH1F, {{100, 0., 100.}}); @@ -776,6 +814,17 @@ struct nucleiInJets { jetHist.add("eff/recmatched/mcCSpectra/gen/pt/PtParticleType", "Pt (gen, mcCSpectra) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); jetHist.add("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType", "Pt (gen, mcC, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + + jetHist.add("feeddown/antiProton/jetCone/PtOrigin", "reconstructed #bar{p} origin in jet cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPC", "reconstructed #bar{p} origin in jet cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTOF", "reconstructed #bar{p} origin in jet cone, TOF matched; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPCTOF", "reconstructed #bar{p} origin in jet cone, TPC+TOF PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPCTOFVeto", "reconstructed #bar{p} origin in jet cone, TPC+TOF veto; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOrigin", "reconstructed #bar{p} origin in perpendicular cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPC", "reconstructed #bar{p} origin in perpendicular cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTOF", "reconstructed #bar{p} origin in perpendicular cone, TOF matched; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPCTOF", "reconstructed #bar{p} origin in perpendicular cone, TPC+TOF PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPCTOFVeto", "reconstructed #bar{p} origin in perpendicular cone, TPC+TOF veto; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); // gen matched jetHist.add("genmatched/hRecMatchedJetPt", "matched jet pT (Rec level);#it{p}_{T,jet part} (GeV/#it{c}); #it{p}_{T,jet part} - #it{p}_{T,jet det}", HistType::kTH2F, {{100, 0., 100.}, {400, -20., 20.}}); jetHist.add("genmatched/hRecMatchedVsGenJetPt", "matched jet pT (Rec level);#it{p}_{T,jet det}; #it{p}_{T,jet part} (GeV/#it{c})", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); @@ -891,7 +940,11 @@ struct nucleiInJets { bool jetFlagPerpCone = false; float jetPt = -999.; - if (isWithLeadingJet) { + const bool hasValidLeadingJet = leadingJetPtEtaPhi.size() >= 3 && leadingJetPtEtaPhi[0] > 0.f; + if (isWithLeadingJet && hasValidLeadingJet) { + if (!isConeAxisAccepted(leadingJetPtEtaPhi[1])) { + return; + } double delPhi = TVector2::Phi_mpi_pi(leadingJetPtEtaPhi[2] - trk.phi()); double delEta = leadingJetPtEtaPhi[1] - trk.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); @@ -906,15 +959,19 @@ struct nucleiInJets { double RPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (RPerpCone1 < cfgjetR || RPerpCone2 < cfgjetR) jetFlagPerpCone = true; - } else { + } else if (!isWithLeadingJet) { for (auto const& jet : jets) { + if (!isConeAxisAccepted(jet.eta())) { + continue; + } double delPhi = TVector2::Phi_mpi_pi(jet.phi() - trk.phi()); double delEta = jet.eta() - trk.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); - if (R < cfgjetR) + if (R < cfgjetR) { jetFlag = true; - jetPt = jet.pt(); - break; + jetPt = jet.pt(); + break; + } } } // tof @@ -1697,8 +1754,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("hNEvents"), 6.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + int nAcceptedJets = 0; + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; + float leadingJetPtBkgSub = -999.0f; float backgroundRho = collision.rho(); // Get background rho from collision // Fill background rho histogram once per event @@ -1708,6 +1767,7 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPt"), chargedjet.pt()); jetHist.fill(HIST("jet/h1JetEta"), chargedjet.eta()); jetHist.fill(HIST("jet/h1JetPhi"), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiBeforeAcceptance"), chargedjet.eta(), chargedjet.phi()); // Calculate background subtracted jet pt float jetArea = M_PI * cfgjetR * cfgjetR; // Assuming circular jet area @@ -1717,7 +1777,17 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPtBkgSub"), jetPtBkgSub); jetHist.fill(HIST("jet/h2JetPtVsBkgRho"), chargedjet.pt(), backgroundRho); - if (chargedjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(chargedjet.eta())) { + const auto perpConePhiJet = getPerpendicuarPhi(chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiAfterAcceptance"), chargedjet.eta(), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[0]); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[1]); + nAcceptedJets++; + } + + if (isConeAxisAccepted(chargedjet.eta()) && chargedjet.pt() > leadingJetPt) { + leadingJetPt = chargedjet.pt(); + leadingJetPtBkgSub = jetPtBkgSub; leadingJetWithPtEtaPhi[0] = chargedjet.pt(); leadingJetWithPtEtaPhi[1] = chargedjet.eta(); leadingJetWithPtEtaPhi[2] = chargedjet.phi(); @@ -1725,6 +1795,7 @@ struct nucleiInJets { nJets++; } jetHist.fill(HIST("jet/nJetsPerEvent"), nJets); + jetHist.fill(HIST("jet/qa/nAcceptedJetsPerEvent"), nAcceptedJets); jetHist.fill(HIST("vertexZ"), collision.posZ()); if (nJets > 0) { jetHist.fill(HIST("jet/vertexZ"), collision.posZ()); @@ -1732,9 +1803,23 @@ struct nucleiInJets { } else { jetHist.fill(HIST("jetOut/vertexZ"), collision.posZ()); } + if (nAcceptedJets > 0) { + jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); + jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); + } + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); + jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); + jetHist.fill(HIST("hNEvents"), 8.5); + } if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } for (const auto& track : tracks) { auto trk = track.track_as(); fillTrackInfo(trk, chargedjets, leadingJetWithPtEtaPhi, backgroundRho); @@ -1771,8 +1856,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("hNEvents"), 6.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + int nAcceptedJets = 0; + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; + float leadingJetPtBkgSub = -999.0f; float backgroundRho = collision.rho(); // Get background rho from collision // Fill background rho histogram once per event @@ -1782,6 +1869,7 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPt"), chargedjet.pt()); jetHist.fill(HIST("jet/h1JetEta"), chargedjet.eta()); jetHist.fill(HIST("jet/h1JetPhi"), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiBeforeAcceptance"), chargedjet.eta(), chargedjet.phi()); // Calculate background subtracted jet pt float jetArea = M_PI * cfgjetR * cfgjetR; // Assuming circular jet area @@ -1791,7 +1879,17 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPtBkgSub"), jetPtBkgSub); jetHist.fill(HIST("jet/h2JetPtVsBkgRho"), chargedjet.pt(), backgroundRho); - if (chargedjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(chargedjet.eta())) { + const auto perpConePhiJet = getPerpendicuarPhi(chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiAfterAcceptance"), chargedjet.eta(), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[0]); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[1]); + nAcceptedJets++; + } + + if (isConeAxisAccepted(chargedjet.eta()) && chargedjet.pt() > leadingJetPt) { + leadingJetPt = chargedjet.pt(); + leadingJetPtBkgSub = jetPtBkgSub; leadingJetWithPtEtaPhi[0] = chargedjet.pt(); leadingJetWithPtEtaPhi[1] = chargedjet.eta(); leadingJetWithPtEtaPhi[2] = chargedjet.phi(); @@ -1799,6 +1897,7 @@ struct nucleiInJets { nJets++; } jetHist.fill(HIST("jet/nJetsPerEvent"), nJets); + jetHist.fill(HIST("jet/qa/nAcceptedJetsPerEvent"), nAcceptedJets); jetHist.fill(HIST("vertexZ"), collision.posZ()); if (nJets > 0) { jetHist.fill(HIST("jet/vertexZ"), collision.posZ()); @@ -1806,9 +1905,23 @@ struct nucleiInJets { } else { jetHist.fill(HIST("jetOut/vertexZ"), collision.posZ()); } + if (nAcceptedJets > 0) { + jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); + jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); + } + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); + jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); + jetHist.fill(HIST("hNEvents"), 8.5); + } if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } for (auto& track : tracks) { auto trk = track.track_as(); fillTrackInfo(trk, chargedjets, leadingJetWithPtEtaPhi, backgroundRho); @@ -2152,10 +2265,10 @@ struct nucleiInJets { double delPhi = TVector2::Phi_mpi_pi(mcpjet.phi() - mcParticle.phi()); double delEta = mcpjet.eta() - mcParticle.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); - if (R < cfgjetR) + if (R < cfgjetR) { jetFlag = true; - // jetPt = mcpjet.pt(); - break; + break; + } } // jet if (mapPDGToValue(mcParticle.pdgCode()) != 0) { jetHist.fill(HIST("mcpJet/pt/PtParticleType"), mcParticle.pt(), jetFlag, mapPDGToValue(mcParticle.pdgCode())); @@ -2178,13 +2291,14 @@ struct nucleiInJets { jetHist.fill(HIST("mcdJet/eventStat"), 2.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; for (auto& mcdjet : mcdjets) { jetHist.fill(HIST("mcdJet/hJetPt"), mcdjet.pt()); jetHist.fill(HIST("mcdJet/hJetEta"), mcdjet.eta()); jetHist.fill(HIST("mcdJet/hJetPhi"), mcdjet.phi()); if (mcdjet.pt() > leadingJetPt) { + leadingJetPt = mcdjet.pt(); leadingJetWithPtEtaPhi[0] = mcdjet.pt(); leadingJetWithPtEtaPhi[1] = mcdjet.eta(); leadingJetWithPtEtaPhi[2] = mcdjet.phi(); @@ -2209,7 +2323,8 @@ struct nucleiInJets { bool jetFlag = false; bool jetFlagPerpCone = false; // float jetPt = -999.; - if (isWithLeadingJet) { + const bool hasValidLeadingJet = leadingJetWithPtEtaPhi.size() >= 3 && leadingJetWithPtEtaPhi[0] > 0.f; + if (isWithLeadingJet && hasValidLeadingJet) { double delPhi = TVector2::Phi_mpi_pi(leadingJetWithPtEtaPhi[2] - track.phi()); double delEta = leadingJetWithPtEtaPhi[1] - track.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); @@ -2222,15 +2337,15 @@ struct nucleiInJets { double RPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (RPerpCone1 < cfgjetR || RPerpCone2 < cfgjetR) jetFlagPerpCone = true; - } else { + } else if (!isWithLeadingJet) { for (const auto& mcdjet : mcdjets) { double delPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - track.phi()); double delEta = mcdjet.eta() - track.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); - if (R < cfgjetR) + if (R < cfgjetR) { jetFlag = true; - // jetPt = mcdjet.pt(); - break; + break; + } } // jet } if (mapPDGToValue(mcTrack.pdgCode()) != 0) { @@ -2272,7 +2387,7 @@ struct nucleiInJets { return; // LOG(info) <<" size(mcd) "< leadingJetWithPtEtaPhi(3); + std::vector leadingJetWithPtEtaPhi(3, -999.); for (const auto& mcdjet : mcdjets) { if (!mcdjet.has_matchedJetGeo()) continue; @@ -2284,6 +2399,16 @@ struct nucleiInJets { if (mcdJetPtForResponse < cfgjetPtBkgSubMinMC) { continue; } + const double jetAxisEtaForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.eta() : mcpjet.eta()) : mcdjet.eta(); + const double jetAxisPhiForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.phi() : mcpjet.phi()) : mcdjet.phi(); + jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiBeforeAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); + if (!isConeAxisAccepted(jetAxisEtaForEff)) { + continue; + } + const auto perpConePhiJet = getPerpendicuarPhi(static_cast(jetAxisPhiForEff)); + jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); + jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[0]); + jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[1]); mcdJetPt.push_back(mcdJetPtForResponse); mcdJetPhi.push_back(mcdjet.phi()); mcdJetEta.push_back(mcdjet.eta()); @@ -2338,8 +2463,6 @@ struct nucleiInJets { if (!track.has_mcParticle()) continue; auto mcTrack = track.mcParticle_as(); - if (!mcTrack.isPhysicalPrimary()) - continue; if (!isRapiditySelectedForPID(mcTrack.y())) continue; @@ -2373,30 +2496,75 @@ struct nucleiInJets { } } // jet + if (mapPDGToValue(mcTrack.pdgCode()) == -Particle::kProton) { + const int particleOriginType = getParticleOriginType(mcTrack); + const bool isAntiProtonTpcPassed = std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPr; + const bool isAntiProtonTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const bool isAntiProtonTPCTOFPassed = isAntiProtonTpcPassed && isAntiProtonTofPidPassed; + const bool isAntiProtonTPCTOFVetoPassed = isAntiProtonTpcPassed && (!isTof || isAntiProtonTofPidPassed); + + if (jetFlag) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOrigin"), completeTrack.pt(), particleOriginType); + if (isAntiProtonTpcPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPC"), completeTrack.pt(), particleOriginType); + } + if (isTof) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPCTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFVetoPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPCTOFVeto"), completeTrack.pt(), particleOriginType); + } + } + + if (jetFlagPerpCone) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOrigin"), completeTrack.pt(), particleOriginType); + if (isAntiProtonTpcPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPC"), completeTrack.pt(), particleOriginType); + } + if (isTof) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPCTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFVetoPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPCTOFVeto"), completeTrack.pt(), particleOriginType); + } + } + } + + if (!mcTrack.isPhysicalPrimary()) + continue; + const auto particleType = mapPDGToValue(mcTrack.pdgCode()); if (particleType != 0) { bool isTpcPassed = true; - bool isTOFAndTPCPreSel = isTof; + bool isTofPidPassed = isTof; switch (std::abs(particleType)) { case Particle::kProton: isTpcPassed = std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPr; - isTOFAndTPCPreSel = isTof && std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPrTOF; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; break; case Particle::kDeuteron: isTpcPassed = std::abs(completeTrack.tpcNSigmaDe()) < cfgnTPCPIDDe; - isTOFAndTPCPreSel = isTof && std::abs(completeTrack.tpcNSigmaDe()) < cfgnTPCPIDDeTOF; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaDe()) < cfgnTPCPIDDeTOF; break; case Particle::kTriton: isTpcPassed = std::abs(completeTrack.tpcNSigmaTr()) < cfgnTPCPIDTr; - isTOFAndTPCPreSel = isTof && std::abs(completeTrack.tpcNSigmaTr()) < cfgnTPCPIDTrTOF; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaTr()) < cfgnTPCPIDTrTOF; break; case Particle::kHelium: isTpcPassed = std::abs(completeTrack.tpcNSigmaHe()) < cfgnTPCPIDHe; - isTOFAndTPCPreSel = isTof && std::abs(completeTrack.tpcNSigmaHe()) < cfgnTPCPIDHeTOF; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaHe()) < cfgnTPCPIDHeTOF; break; default: break; } + const bool isTPCTOFPassed = isTpcPassed && isTofPidPassed; + const bool isTPCTOFVetoPassed = isTpcPassed && (!isTof || isTofPidPassed); jetHist.fill(HIST("eff/recmatched/pt/PtParticleType"), mcTrack.pt(), jetFlag, particleType); if (useMcC) { if (useDataLikeHist) @@ -2408,12 +2576,10 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPC"), mcTrack.pt(), jetFlag, particleType); if (isTof) jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTOF"), mcTrack.pt(), jetFlag, particleType); - if (isTOFAndTPCPreSel) { + if (isTPCTOFPassed) jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), jetFlag, particleType); + if (isTPCTOFVetoPassed) jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), jetFlag, particleType); - } else { - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), jetFlag, particleType); - } if (jetFlag) { jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); @@ -2421,12 +2587,10 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPC"), mcTrack.pt(), mcTrack.eta(), particleType); if (isTof) jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTOF"), mcTrack.pt(), mcTrack.eta(), particleType); - if (isTOFAndTPCPreSel) { + if (isTPCTOFPassed) jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOF"), mcTrack.pt(), mcTrack.eta(), particleType); + if (isTPCTOFVetoPassed) jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); - } else { - jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); - } } if (jetFlagPerpCone) { @@ -2446,12 +2610,11 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTOF"), mcTrack.pt(), particleType); jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTOF"), mcTrack.pt(), mcTrack.eta(), particleType); } - if (isTOFAndTPCPreSel) { + if (isTPCTOFPassed) { jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), particleType); jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOF"), mcTrack.pt(), mcTrack.eta(), particleType); - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), particleType); - jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); - } else { + } + if (isTPCTOFVetoPassed) { jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), particleType); jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); } From 4b8aa6586f3931377868cc773f68b6e0349cc5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E6=99=93=E5=AE=87=28Xiaoyu=20Hu=29?= <13971168438@163.com> Date: Mon, 21 Sep 2026 20:06:27 +0800 Subject: [PATCH 011/648] [PWGDQ] Track rotation ep preserving mepm rotated legs (#17983) Co-authored-by: ayatsuji <13807650+asakuratou@user.noreply.gitee.com> --- PWGDQ/Core/VarManager.h | 53 ++++++++++++++++++++++++++++- PWGDQ/Tasks/tableReader_withAssoc.h | 32 +++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 4521c911eb0..e1e1b96282e 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -1495,6 +1495,8 @@ class VarManager : public TObject static void FillPair(T1 const& t1, T2 const& t2, float* values = nullptr); template static void FillPairRotation(T1 const& t1, T2 const& t2, int rotation, float* values = nullptr); + template + static void FillPairRotation_ME(T const& t1, T const& t2, int rotation, float* values = nullptr); template static void FillPairCollision(C const& collision, T1 const& t1, T2 const& t2, float* values = nullptr); template @@ -4084,7 +4086,11 @@ void VarManager::FillPairRotation(T1 const& t1, T2 const& t2, int rotation, floa rotationphi2 = 2 * values[kPsi2A] - t2.phi() + o2::constants::math::PI; } - rotationphi2 = RecoDecay::constrainAngle(rotationphi2); + if (rotationphi2 >= o2::constants::math::TwoPI) { + rotationphi2 -= o2::constants::math::TwoPI; + } else if (rotationphi2 < 0) { + rotationphi2 += o2::constants::math::TwoPI; + } values[kCharge] = t1.sign() + t2.sign(); values[kCharge1] = t1.sign(); @@ -4547,6 +4553,51 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } } +template +void VarManager::FillPairRotation_ME(T const& t1, T const& t2, int rotation, float* values) +{ + if (!values) { + values = fgValues; + } + + float m1 = o2::constants::physics::MassElectron; + double rotationphi2 = t2.phi; + + if (rotation == 1) { + rotationphi2 = t2.phi + o2::constants::math::PI; + } else if (rotation == 2) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi; + } else if (rotation == 3) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi + o2::constants::math::PI; + } + + if (rotationphi2 >= o2::constants::math::TwoPI) { + rotationphi2 -= o2::constants::math::TwoPI; + } else if (rotationphi2 < 0) { + rotationphi2 += o2::constants::math::TwoPI; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, rotationphi2, m1); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + // values[kPhi] = v12.Phi(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kRap] = -v12.Rapidity(); + double Ptot1 = TMath::Sqrt(v1.Px() * v1.Px() + v1.Py() * v1.Py() + v1.Pz() * v1.Pz()); + double Ptot2 = TMath::Sqrt(v2.Px() * v2.Px() + v2.Py() * v2.Py() + v2.Pz() * v2.Pz()); + values[kDeltaPtotTracks] = Ptot1 - Ptot2; + + values[kPt1] = t1.pt; + values[kEta1] = t1.eta; + values[kPhi1] = t1.phi; + values[kPt2] = t2.pt; + values[kEta2] = t2.eta; + values[kPhi2] = rotationphi2; +} + template void VarManager::FillPairMEAcrossTFs(T const& t1, T const& t2, float* values) { diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index c1fccbfb333..927ce012626 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -1608,13 +1608,14 @@ struct AnalysisSameEventPairing { if (fConfigTRPairs) { names.push_back(Form("PairsBarrelTRPM_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelTRPM_ambiguousextra_%s", objArray->At(icut)->GetName())); - histNames += Form("%s;%s;", names[6].Data(), names[7].Data()); + names.push_back(Form("PairsBarrelTR_MEPM_%s", objArray->At(icut)->GetName())); + histNames += Form("%s;%s;%s;", names[6].Data(), names[7].Data(), names[8].Data()); } if (fEnableBarrelMixingHistos) { names.push_back(Form("PairsBarrelMEPM_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelMEPP_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelMEMM_%s", objArray->At(icut)->GetName())); - histNames += Form("%s;%s;%s;", names[(fConfigTRPairs ? 8 : 6)].Data(), names[(fConfigTRPairs ? 9 : 7)].Data(), names[(fConfigTRPairs ? 10 : 8)].Data()); + histNames += Form("%s;%s;%s;", names[(fConfigTRPairs ? 9 : 6)].Data(), names[(fConfigTRPairs ? 10 : 7)].Data(), names[(fConfigTRPairs ? 11 : 8)].Data()); } fTrackHistNames[icut] = names; @@ -2534,6 +2535,20 @@ struct AnalysisSameEventPairing { fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } + if (fConfigTRPairs) { + // mixing event track should be rotated, so second parameter + if (fConfigNRotations.value != 1 && fConfigNRotations.value != 3) { + LOGF(fatal, "Unsupported number of rotations: %d, only 1 and 3 are supported", fConfigNRotations.value); + } + for (int irot = 1; irot <= fConfigNRotations.value; ++irot) { + VarManager::FillPairRotation_ME(t2, t1, irot); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + fHistMan->FillHistClass(Form("PairsBarrelTR_MEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } } // run ++ pairing for (auto const& t2 : poolEvent.tracks1) { @@ -2564,6 +2579,19 @@ struct AnalysisSameEventPairing { fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } + if (fConfigTRPairs) { + if (fConfigNRotations.value != 1 && fConfigNRotations.value != 3) { + LOGF(fatal, "Unsupported number of rotations: %d, only 1 and 3 are supported", fConfigNRotations.value); + } + for (int irot = 1; irot <= fConfigNRotations.value; ++irot) { + VarManager::FillPairRotation_ME(t2, t1, irot); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + fHistMan->FillHistClass(Form("PairsBarrelTR_MEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } } // run -- pairing for (auto const& t2 : poolEvent.tracks2) { From 467bc4f874fdb93ae2a26d7e620d1b4c0acb118d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 21 Sep 2026 14:17:13 +0200 Subject: [PATCH 012/648] [Common] Improve propagationServiceV2 (#17695) --- .claude/commands/migrate-ccdb.md | 170 +++++++++++++++++- Common/DataModel/GloCCDBObjects.h | 73 +++++++- Common/DataModel/TpcCCDBObjects.h | 43 +++++ Common/DataModel/TrackTunerCCDBObjects.h | 59 ++++++ Common/TableProducer/propagationServiceV2.cxx | 99 +++++----- 5 files changed, 387 insertions(+), 57 deletions(-) create mode 100644 Common/DataModel/TpcCCDBObjects.h create mode 100644 Common/DataModel/TrackTunerCCDBObjects.h diff --git a/.claude/commands/migrate-ccdb.md b/.claude/commands/migrate-ccdb.md index 48d0f49c971..4f6bce9ab5d 100644 --- a/.claude/commands/migrate-ccdb.md +++ b/.claude/commands/migrate-ccdb.md @@ -10,9 +10,20 @@ The old approach uses `Service` and calls `ccdb->get // In namespace o2::aod (or a sub-namespace): DECLARE_SOA_CCDB_COLUMN(StructName, getterName, ConcreteType, "CCDB/Object/Path"); +// ... or, when the object needs fixing up after deserialisation, the _FULL form, whose +// trailing argument is the finaliser (see "Objects needing post-deserialisation fixup"): +DECLARE_SOA_CCDB_COLUMN_FULL(StructName, "fStructName", getterName, ConcreteType, "CCDB/Object/Path", + [](ConcreteType* o) { return fixUp(o); }); + DECLARE_SOA_TIMESTAMPED_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TABLEDESC", ns::StructName, ns::OtherColumn); +// ... or, when the object is constant across something coarser than a timestamp, the +// uniform form (see "Uniformity: how often the object can change"): +DECLARE_SOA_UNIFORM_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TABLEDESC", + ns::StructName); + // In the task — basic usage: using MyBCs = soa::Join; void process(MyBCs const& bcs) { @@ -84,6 +95,17 @@ DECLARE_SOA_TIMESTAMPED_TABLE(MyTaskCCDBObjects, aod::Timestamps, o2::aod::times } // namespace o2::aod ``` +Before writing the declaration, settle three things per column — each has its own section +below, and getting them wrong is silent rather than loud: + +1. **Does the object need fixing up after deserialisation?** If so use `DECLARE_SOA_CCDB_COLUMN_FULL` + with a finaliser — see "Objects needing post-deserialisation fixup". +2. **How often can the object change?** Timestamp (the default) or run — see "Uniformity: how + often the object can change". Choose from the object's validity, not from how the old code + happened to fetch it. +3. **Is the path the same for every run?** If it varies by period, declare the mapping in the + query string instead of porting the run-range `if/else` — see "Paths that vary by run". + Rules for naming: - `StructName` / `getterName`: derive from the type name, e.g. `GRPMagField` / `grpMagField`, `MeanVertex` / `meanVertex` - Table name: `CCDBObjects`, e.g. `SkimmerDalitzEECCDBObjects` @@ -141,8 +163,154 @@ After making changes: - **`getRunDuration()` calls**: these use `BasicCCDBManager` statically and are unrelated to per-BC fetching — do not touch them. - **`ctpRateFetcher` / other helpers**: out of scope. - **Multiple tasks in one file**: tasks can share a single CCDB table declaration if they need the same objects; otherwise each task gets its own with a unique `_Desc_`. -- **Non-BC timestamps**: if the timestamp comes from something other than a BC (e.g. computed manually), the migration is non-trivial — flag it instead of forcing it. +- **Non-BC timestamps**: if the timestamp comes from something other than a BC, the migration is non-trivial — flag it instead of forcing it. This is the single most common blocker in practice. `Common/Tools/EventSelectionModule.h:243` computes `ts = sorTimestamp / 2 + eorTimestamp / 2` (mid-run, from `getRunDuration` / `AggregatedRunInfo`) and fetches `EventSelectionParams`, `ITS/Config/AlpideParam`, `TriggerAliases` and `ITS/Calib/TimeDeadMap` at it. A BC-keyed column fetches at each BC's own timestamp instead, so migrating these silently changes which object version is served whenever an object is revised mid-run. They need a run-keyed table before they can move. - **Global/init-time fetches** (e.g. `efficiencyGlobal.cxx` style): not migratable — the timestamped-table mechanism requires a row in a BC-keyed table. - **Magnetic-field side effects**: tasks that compute `d_bz` from a fetched `GRPMagField` and seed a propagator can keep that logic, just sourcing the object from `bc.grpMagField()` instead of `ccdb->getForTimeStamp(...)`. +## Lessons learned (established in-tree, with references) + +### Why this migration matters beyond tidiness + +The per-task path Configurable is a silent-divergence trap. `propagationService` and `propagationServiceV2` share the identical `ccdb.lutPath` Configurable (`Common/Tools/StandardCCDBLoader.h:45`, default `GLO/Param/MatLUT`), but config JSONs key overrides by *device name*. Every config in the tree carries a `propagation-service` block setting `GLO/Param/MatLUTInner` and no `propagation-service-v2` block, so V2 silently fell back to the full LUT — different material corrections, no warning. After migration the path is one option on the fetcher device, and two tasks disagreeing produces a warning (`ArrowSupport.cxx:641-666`) instead of silence. + +### Objects needing post-deserialisation fixup + +Some objects are not usable straight out of the ROOT streamer. `MatLayerCylSet` is a `FlatObject`: its internal pointers are unfixed and its voxel lookup unbuilt until `MatLayerCylSet::rectifyPtrFromFile()` runs. Use the `_FULL` form, which carries the finaliser (the plain `DECLARE_SOA_CCDB_COLUMN` passes an identity one): + +```cpp +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); +``` + +The finaliser must be the **last** macro argument (commas in a lambda body are absorbed by `__VA_ARGS__`), has signature `T* (*)(T*)`, and runs on the receiving device once per (re)deserialisation, before the object is ever handed out. Ownership contract: whatever it returns is what the column cache later `delete`s, so a finaliser returning a *different* instance must dispose of the one it was given. + +Do **not** put this fixup in the task. There is no `finaliseCCDB` hook on the analysis path (`adaptAnalysisTask` wires only `EndOfStream`, `AnalysisTask.h:610-619`; grep confirms zero uses of `finaliseCCDB` in O2Physics), and even if there were, an opt-in hook means a task that forgets it gets a silently broken object. + +### Uniformity: how often the object can change + +Every CCDB table declares a *uniformity column*: rows sharing its value resolve to the same +object, so the fetcher queries once per distinct value instead of once per row. +`DECLARE_SOA_TIMESTAMPED_TABLE` defaults it to the timestamp column, which is the +pre-existing behaviour — every distinct timestamp may yield a different object. + +Pick it from the object's real validity, and only then: + +| Object changes ... | Uniformity | Declare with | +| --- | --- | --- | +| within a run (calibrations, drift velocity) | timestamp (default) | `DECLARE_SOA_TIMESTAMPED_TABLE` | +| per run or per period (geometry, material, per-period calibrations) | `aod::BCs` / `aod::bc::RunNumber` | `DECLARE_SOA_UNIFORM_TABLE` | + +Worked examples in the tree: `aod::TpcCalibCCDBObjects` keeps the timestamp default because +the TPC drift velocity genuinely varies within a run; `aod::GeomCCDBObjects` and +`aod::TrackTunerCCDBObjects` are run-uniform. + +Two consequences worth knowing before choosing: + +- The uniformity column may live in a **different table** from the timestamp — the run number + is on `aod::BCs`, the timestamp on `aod::Timestamps`. Both are handed to the fetcher + automatically (the table's `generateSources()` merges their originals) and read positionally. +- Positional reading is only sound if the two sources are **row-aligned**. ASoA encodes no + type-level relation between tables that merely have equal row counts, so this cannot be a + `static_assert`; the fetcher compares the two column lengths and fatals on a mismatch. + Anything joinable with the BCs is fine. + +### Paths that vary by run: declare a mapping, not code + +A column's path may be a plain path, or a mapping from uniformity value to path: + +``` +"520259-529691=…/pp2023/pass4/vsPhi;559348-559387=…/ppRef/polarity_positive;fallback" +``` + +Ranges are inclusive; either bound may be omitted (`-hi=path`, `lo-=path`); entries are +separated by `;`; an entry without `=` is an explicit fallback. **A value matching no range +is fatal**, deliberately — silently substituting another period's calibration is the failure +mode this whole mechanism exists to prevent. A string with no `=` is a plain path, so +existing columns are unaffected. + +The mapping is *data*, carried in the schema metadata. That matters: the CCDB fetcher is a +separate device and must not depend on code from the task that declared the column, so a +resolver lambda would not do. It also means the run ranges stop being compiled in — the whole +mapping is replaceable at runtime through the `ccdb:fXxx` option. + +This replaces hand-written run-range tables. `TrackTuner::getPathInputFileAutomaticFromCCDB()` +is the model case: ~50 lines of `else if (lo <= runNumber && runNumber <= hi)` became the +declaration in `Common/DataModel/TrackTunerCCDBObjects.h`. When porting one, **derive the +mapping mechanically and diff it against the source** — first-match-wins must reproduce the +`if/else` order, which matters whenever ranges overlap (in TrackTuner, one PbPb range sits +inside a pp range and must stay *after* it). + +### Serving migrated and un-migrated callers from one module + +Shared modules must keep working for tasks that have not migrated. Detect the capability +rather than adding a configuration flag: + +```cpp +auto const& bc = collision.template bc_as(); +if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); // column path +} else { + mVDriftMgr.update(bc.timestamp()); // legacy CCDB query +} +``` + +The discarded branch is not instantiated, so an un-migrated caller compiles exactly as before +and a migrated one never references the CCDB manager. `strangenessBuilderModule::updateVDrift` +uses this. Where a whole function parameter falls away, add an overload of different arity +that forwards (see "Shared module signatures") and put a `static_assert` with a readable +message on the ccdb-free one, so calling it with an unjoined BC table names the missing table +instead of failing somewhere inside the template. + +### Two path settings must never both be live + +After migration the column is the single source of truth for a path. If the task still has an +old `Configurable` for the same object, **fail loudly when both are set** rather +than silently preferring one — that divergence is exactly the bug this migration exists to +kill. `TrackPropagationModule::init` fatals when `trackTuner.pathInputFile` is non-empty while +the calibrations come from columns, naming the option to use instead (`ccdb:fTrackTunerDca`). + +Caveat: this test only works for Configurables whose default is empty. One with a non-empty +default cannot be distinguished from an unset one, so that hole stays open until the framework +can report whether an option was explicitly set. + +### Grouping columns into tables + +One table per **family of objects used together with similar validity intervals** — not one per consuming task. Geometry and material description (`GLO/Param/MatLUT`, and later `GLO/Config/GeometryAligned`, `GLO/Config/Geometry`, `/Calib/Align`; see `GRPGeomRequest` in `O2/Detectors/Base/src/GRPGeomHelper.cxx:44-60`) is one family with essentially static validity. The GRP family changes per run, and `GRPMagField` is requested per timeframe in O2 (`GRPGeomHelper.cxx:72`). Splitting on that boundary keeps a task from fetching a multi-hundred-MB LUT it never asked for. + +**Several timestamped tables can be joined onto the same BCs.** `soa::Join` works: the duplicated `aod::Timestamps` is deduplicated when `originals` is merged (`ASoA.h:172-186`), giving 4 originals, and every accessor resolves. Do not invent per-use-case tables to work around a limitation that does not exist. + +### Global state is not a lookup + +Migrating removes CCDB *queries*, not side effects. Two things stay: + +- `Propagator::initFieldFromGRP()` rebuilds or rescales a `MagneticField`, attaches it to `TGeoGlobalMagField::Instance()` and locks it (`O2/Detectors/Base/src/Propagator.cxx:107-149`). Keep it guarded on run change. +- `Propagator::Instance()->setMatLUT()` is a pointer store, so it is cheaper to redo unconditionally every timeframe — and doing so picks up a relocated column buffer for free instead of dangling. + +Everything else (mean vertex, run number) should become a direct read at the point of use, with no cached member and no `initCCDB()` helper. A cached pointer plus a "did the buffer move?" check is strictly worse than reading the column fresh. + +`Propagator` cannot itself become a column value: private constructor, deleted copy/move, singleton `Instance()` (`Propagator.h:157-201`). + +### Shared module signatures + +If a shared module takes a `StandardCCDBLoader`, change it to take the values it actually uses (`int runNumber`, `MeanVertexObject const*`) and keep a thin forwarding overload for un-migrated callers, so V1 tasks stay byte-identical. `TrackPropagationModule::fillTrackTables` does this — the two overloads differ in arity, so overload resolution is unambiguous. + +### What the migration does and does not buy + +The fetcher downloads once into a shm cache and the column stores `(handle, segment, size)` (`AnalysisCCDBHelpers.cxx:213-222`). What is shared is the **serialised blob**; each consumer still streams its own heap copy in the column getter. So expect fewer downloads, one configuration point and cross-device consistency — but not a per-device RSS reduction. For a `FlatObject` like the LUT, real memory sharing needs a zero-copy path (`FlatObject::setActualBufferAddress`) that does not exist yet. + +### Known gaps in the mechanism + +- **Run-dependent objects are not served correctly.** The analysis fetcher still hardcodes `.runNumber = 1, .runDependent = 0` for every column, even though `CCDBFetcherHelper.cxx:189-195` implements the run-dependent query paths. `GLO/Config/GRPECS` is marked "Run dependent !!!" in O2 and already has a column — verify before relying on it. Now that a run-uniform table gives the fetcher a run number per row, wiring this through is small and worth doing. +- **`getForRun` is not the same query.** `BasicCCDBManager::getForRun` resolves the run duration and queries at *mid-run* (`BasicCCDBManager.h:364-374`); a column queries at each BC's timestamp. Identical for objects with one version per run, divergent otherwise. +- **Row cardinality, not query count.** The uniformity column already collapses the *queries* to one per distinct value, but the table still carries one row per BC per column — a `FixedSizeList`, 24 B, rebuilt every timeframe. Collapsing the rows too needs a non-extension table plus lookup by value at the consumer, which does not exist yet. So a run-uniform table costs the same arrow memory as before; what it saves is the fetching. +- **Multi-run dataframes.** Skimmed datasets can span runs. Every existing consumer configures from `bcs.begin()` and applies it to the whole DF (`propagationServiceV2.cxx`, `StandardCCDBLoader.h:70-77`, `strangenessBuilderModule.h:850`), which is wrong for such a DF. Migrating preserves this bug unless it is fixed deliberately — do not claim the migration fixes it. + +### Practical gotchas + +- `DECLARE_SOA_CCDB_COLUMN` expands to code using `TClass` and `TBufferFile`, but `ASoA.h` only sees them forward-declared. A translation unit that includes the column header without otherwise pulling in `` and `` fails to compile. Include them if needed. +- A failed fetch is fatal, not silent: if `extractCCDBPayload` returns null the getter aborts naming the type, the path and the `ccdb:` option to check. A mistyped path therefore stops the job rather than dereferencing null. +- Do not add a `sources` member to a table's metadata struct. It makes the struct satisfy both `soa::with_sources` and `soa::with_sources_generator`, and `getInputMetadata` becomes ambiguous. +- Device options are matched by device *name*. Never look a task's own option up by a hardcoded name (`device.name == "propagation-service"` silently matched nothing in `propagation-service-v2`); take the running device from `initContext.services().get()`. Spell the type out rather than using `auto`, or the pre-existing `option.defaultValue.get()` becomes a dependent name and needs `template`. +- Verify with the *control*: when changing a shared header, compile an un-migrated consumer too. A new error appearing in both is yours; the same errors in both means you changed nothing for them. + $ARGUMENTS diff --git a/Common/DataModel/GloCCDBObjects.h b/Common/DataModel/GloCCDBObjects.h index f97b684b0a1..4612f8ba02a 100644 --- a/Common/DataModel/GloCCDBObjects.h +++ b/Common/DataModel/GloCCDBObjects.h @@ -33,9 +33,19 @@ /// `DECLARE_SOA_TIMESTAMPED_TABLE` with the relevant subset of columns from /// the `o2::aod::ccdbGlo` namespace rather than joining `aod::GloCCDBObjects`. /// -/// Note: MatLayerCylSet is intentionally omitted — it requires -/// `MatLayerCylSet::rectifyPtrFromFile()` after deserialisation, which the -/// CCDB column mechanism does not perform. +/// The material LUT lives in `aod::GeomCCDBObjects` rather than here: it belongs to +/// the geometry/material family, whose validity is essentially static, and keeping it +/// out means joining `aod::GloCCDBObjects` does not drag in a multi-hundred-MB object +/// nobody asked for. Join whichever tables you need — the duplicated `aod::Timestamps` +/// is deduplicated: +/// \code +/// using BCsWithLUT = soa::Join; +/// // rectifyPtrFromFile() is applied by the column's finaliser, so the object +/// // handed back is ready to use. Installing it is a pointer store, so it costs +/// // nothing to redo every timeframe — and doing so picks up a relocated column +/// // buffer for free instead of dangling. +/// o2::base::Propagator::Instance()->setMatLUT(&bcs.begin().matLUT()); +/// \endcode #ifndef COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ #define COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ @@ -44,8 +54,14 @@ #include #include #include +#include #include #include +#include + +#include +#include +#include namespace o2::aod { @@ -55,11 +71,62 @@ DECLARE_SOA_CCDB_COLUMN(GRPMagField, grpMagField, o2::parameters::GRPMagField, " DECLARE_SOA_CCDB_COLUMN(MeanVertex, meanVertex, o2::dataformats::MeanVertexObject, "GLO/Calib/MeanVertex"); //! DECLARE_SOA_CCDB_COLUMN(GRPECSObject, grpECS, o2::parameters::GRPECSObject, "GLO/Config/GRPECS"); //! DECLARE_SOA_CCDB_COLUMN(GRPLHCIFData, grpLHCIF, o2::parameters::GRPLHCIFData, "GLO/Config/GRPLHCIF"); //! + +/// The material LUT is a FlatObject: straight out of the ROOT streamer its internal +/// pointers are unfixed and its voxel lookup is unbuilt, so it is finalised with +/// MatLayerCylSet::rectifyPtrFromFile() before ever being handed to a task. +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", 0, //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); + +/// Returns the GRPLHCIF object for this BC: straight from the aod::GrpLHCIFCCDBObjects +/// column when the BC table carries it, else through a CCDB query at \p timestamp. Lets a +/// shared module serve migrated and un-migrated tasks from a single code path — the +/// discarded branch is not instantiated, so an un-migrated caller keeps its CCDB query and +/// a migrated one never names the CCDB manager. +template +inline o2::parameters::GRPLHCIFData* grpLHCIFFor(TBC const& bc, TCCDB& ccdb, std::string const& path, int64_t timestamp) +{ + if constexpr (requires { bc.grpLHCIF(); }) { + // The column is the single source of truth for the path; a caller that also configured + // one is telling us two different things, and silently honouring one is how conditions + // diverge unnoticed. + if (path != std::string_view{GRPLHCIFData::query}) { + LOGP(fatal, R"(A GRPLHCIF path "{}" is configured while the object is taken from the aod::GrpLHCIFCCDBObjects column, whose path is "{}". Set it through the "ccdb:{}" option instead.)", + path, GRPLHCIFData::query, GRPLHCIFData::mLabel); + } + return &bc.grpLHCIF(); + } else { + return ccdb->template getForTimeStamp(path, timestamp); + } +} } // namespace ccdbGlo /// Full table — join with aod::BCsWithTimestamps to obtain all four objects. DECLARE_SOA_TIMESTAMPED_TABLE(GloCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "GLOCCDBOBJ", //! ccdbGlo::GRPMagField, ccdbGlo::MeanVertex, ccdbGlo::GRPECSObject, ccdbGlo::GRPLHCIFData); + +/// The LHC filling scheme and beam configuration on its own. Run-uniform: GRPLHCIF is set +/// per fill and a run lies within a fill, so the fetcher resolves it once per run rather +/// than once per BC. Kept apart from aod::GloCCDBObjects so that a task needing only this +/// does not also fetch GRPMagField, MeanVertex and GRPECS — GRPLHCIF is the most widely +/// used of the four (44 files reference the path) and is usually wanted alone. +DECLARE_SOA_UNIFORM_TABLE(GrpLHCIFCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "GRPLHCIFCCDB", //! + ccdbGlo::GRPLHCIFData); + +/// Geometry and material description: objects which describe where the detector material +/// is, and which share an essentially static interval of validity. Kept apart from the GRP +/// family above, which changes per run (and, for GRPMagField, per timeframe). +/// The aligned/ideal geometry and the per-detector alignment objects belong here too when +/// they get columns; see GRPGeomRequest in O2 (GLO/Config/GeometryAligned, GLO/Config/Geometry, +/// /Calib/Align) for the family. +/// Join it alongside aod::GloCCDBObjects when a task needs both — the duplicated +/// aod::Timestamps is deduplicated when the joined table's originals are merged. +/// Uniform in the run number: the geometry/material description does not change within a +/// run, so the fetcher queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(GeomCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "GEOMCCDBOBJ", //! + ccdbGlo::MatLUT); } // namespace o2::aod #endif // COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ diff --git a/Common/DataModel/TpcCCDBObjects.h b/Common/DataModel/TpcCCDBObjects.h new file mode 100644 index 00000000000..19b455e0234 --- /dev/null +++ b/Common/DataModel/TpcCCDBObjects.h @@ -0,0 +1,43 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TpcCCDBObjects.h +/// \brief Declarative CCDB columns for TPC calibration objects. +/// +/// Unlike the geometry/material family in GloCCDBObjects.h, the drift velocity +/// genuinely varies within a run, so the table keeps the default uniformity — one +/// object per distinct timestamp — rather than collapsing per run. +/// +/// Usage: +/// \code +/// using BCsWithVDrift = soa::Join; +/// vdriftManager.update(bc.vdriftTgl()); +/// \endcode + +#ifndef COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ + +#include +#include +#include + +namespace o2::aod +{ +namespace ccdbTpc +{ +DECLARE_SOA_CCDB_COLUMN(VDriftTgl, vdriftTgl, o2::tpc::VDriftCorrFact, "TPC/Calib/VDriftTgl"); //! +} // namespace ccdbTpc + +DECLARE_SOA_TIMESTAMPED_TABLE(TpcCalibCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TPCCALIBCCDB", //! + ccdbTpc::VDriftTgl); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ diff --git a/Common/DataModel/TrackTunerCCDBObjects.h b/Common/DataModel/TrackTunerCCDBObjects.h new file mode 100644 index 00000000000..2e4817efff3 --- /dev/null +++ b/Common/DataModel/TrackTunerCCDBObjects.h @@ -0,0 +1,59 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackTunerCCDBObjects.h +/// \brief Declarative CCDB columns for the TrackTuner DCA / Q-over-pt calibrations. +/// +/// The DCA calibration is published under a different path per data-taking period, so +/// the column declares a uniformity-value-to-path mapping rather than a single path: +/// the fetcher picks the entry whose run range contains the row's run number. This +/// replaces TrackTuner::getPathInputFileAutomaticFromCCDB(), whose run ranges these are. +/// A run matching no range is a fatal error, as it was before — there is deliberately no +/// fallback entry, since silently using another period's calibration is worse than stopping. +/// +/// The ranges are the column's *default*; the whole mapping can be replaced at runtime +/// through the "ccdb:fTrackTunerDca" option, so adding a period need not be a code change. + +#ifndef COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace ccdbTrackTuner +{ +DECLARE_SOA_CCDB_COLUMN(TrackTunerDca, trackTunerDca, TList, //! + "520259-529691=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "534998-543113=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "529397-529418=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "543437-545367=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "549559-558807=Users/m/mfaggin/test/inputsTrackTuner/pp2024/pass1_minBias/vsPhi;" + "564356-564445=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25ae;" + "564468-564472=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25af;" + "559348-559387=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_positive;" + "559408-559456=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"); + +DECLARE_SOA_CCDB_COLUMN(TrackTunerQOverPt, trackTunerQOverPt, TList, //! + "Users/h/hsharma/qOverPtGraphs"); +} // namespace ccdbTrackTuner + +/// Uniform in the run number: one calibration per data-taking period, so the fetcher +/// resolves the path and queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(TrackTunerCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TRKTUNERCCDB", //! + ccdbTrackTuner::TrackTunerDca, ccdbTrackTuner::TrackTunerQOverPt); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ diff --git a/Common/TableProducer/propagationServiceV2.cxx b/Common/TableProducer/propagationServiceV2.cxx index 14f47a4c96d..0fb3f09b6b4 100644 --- a/Common/TableProducer/propagationServiceV2.cxx +++ b/Common/TableProducer/propagationServiceV2.cxx @@ -10,7 +10,8 @@ // or submit itself to any jurisdiction. /// \file propagationServiceV2.cxx -/// \brief V2: GRPMagField and MeanVertexObject sourced from aod::GloCCDBObjects declarative CCDB table. +/// \brief V2: every conditions object sourced from declarative CCDB tables, so the task +/// itself performs no CCDB query. /// \author ALICE //=============================================================== @@ -28,26 +29,22 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/GloCCDBObjects.h" #include "Common/DataModel/PIDResponseTPC.h" -#include "Common/Tools/StandardCCDBLoader.h" +#include "Common/DataModel/TpcCCDBObjects.h" +#include "Common/DataModel/TrackTunerCCDBObjects.h" #include "Common/Tools/TrackPropagationModule.h" #include "Common/Tools/TrackTuner.h" -#include -#include #include #include #include #include #include -#include #include #include #include #include -#include - using namespace o2; using namespace o2::framework; @@ -66,15 +63,21 @@ using TracksWithExtra = soa::Join; using TracksExtraWithPID = soa::Join; struct propagationServiceV2 { - // Service kept for MatLUT (rectifyPtrFromFile) and - // strangenessBuilderModule (V-drift via ccdb->instance()). - // GRPMagField and MeanVertex are sourced from CCDB columns instead. - o2::framework::Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Service ccdb; - - // propagation stuff — ccdbLoader used only for lut + mMeanVtx (set from column) + runNumber - o2::common::StandardCCDBLoaderConfigurables standardCCDBLoaderConfigurables; - o2::common::StandardCCDBLoader ccdbLoader; + // No CCDB client of any kind: every conditions object this task consumes — magnetic + // field, mean vertex, material LUT, TPC drift correction and the two TrackTuner + // calibrations — arrives as a declarative CCDB column, so the task issues no query. + // + // No ConfigurableCCDBPath is declared either: ArrowSupport already registers a + // "ccdb:fXxx" option per declared column on the CCDB fetcher device, defaulting to the + // column's own query string. A task-side ConfigurableCCDBPath would only re-supply that + // identical default. Override a path with --ccdb:fMatLUT (and friends). + + // Everything this task needs is read straight off the CCDB columns at the point of + // use; no StandardCCDBLoader, and no CCDB query of its own. The single piece of + // retained state is the run number, needed only to avoid re-installing the magnetic + // field: that one is not a lookup but global state in the Propagator / + // TGeoGlobalMagField singletons, and installing it rebuilds or rescales the field map. + int mRunNumber = -1; // boilerplate: strangeness builder stuff o2::pwglf::strangenessbuilder::products products; @@ -93,46 +96,32 @@ struct propagationServiceV2 { o2::common::TrackPropagationConfigurables trackPropagationConfigurables; o2::common::TrackPropagationModule trackPropagation; - using BCsWithCCDB = soa::Join; + using BCsWithCCDB = soa::Join; // registry HistogramRegistry histos{"histos"}; void init(o2::framework::InitContext& initContext) { - // Only needed for MatLUT fetch and strangenessBuilderModule V-drift - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setURL(ccdburl.value); - // task-specific - trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext); + trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext, /*calibFromCCDBColumns=*/true); strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, eventSelectOpts, histos, initContext); } - // Load MatLUT once (needs rectifyPtrFromFile, kept manual), set B-field and mean vertex - // once per run from GRPMagField/MeanVertex CCDB columns. + /// Install into the Propagator the two things which are global state rather than + /// values: the magnetic field and the material LUT. template - void initCCDB(TBC const& bc0) + void initPropagator(TBC const& bc0) { - if (ccdbLoader.runNumber != bc0.runNumber()) { + if (mRunNumber != bc0.runNumber()) { LOG(info) << "Setting B-field to current " << bc0.grpMagField().getL3Current() << " A for run " << bc0.runNumber() << " from GRPMagField CCDB column"; o2::base::Propagator::initFieldFromGRP(&bc0.grpMagField()); - ccdbLoader.mMeanVtx = &bc0.meanVertex(); - ccdbLoader.runNumber = bc0.runNumber(); - } else { - // Verify the CCDB column buffer has not been replaced mid-run. - // The deserialised pointer must be stable for the lifetime of a run. - if (&bc0.meanVertex() != ccdbLoader.mMeanVtx) { - LOG(fatal) << "MeanVertex CCDB column pointer changed within run " << bc0.runNumber() << " — unexpected buffer replacement"; - } - } - if (!ccdbLoader.lut) { - LOG(info) << "Loading material look-up table for run: " << bc0.runNumber(); - ccdbLoader.lut = o2::base::MatLayerCylSet::rectifyPtrFromFile( - ccdb->template getForRun(standardCCDBLoaderConfigurables.lutPath.value, bc0.runNumber())); - o2::base::Propagator::Instance()->setMatLUT(ccdbLoader.lut); + mRunNumber = bc0.runNumber(); } + // A pointer store, so it costs nothing to redo every timeframe — and doing so + // means a relocated column buffer is picked up for free instead of dangling. + // The column's finaliser has already run MatLayerCylSet::rectifyPtrFromFile. + o2::base::Propagator::Instance()->setMatLUT(&bc0.matLUT()); } void processRealData(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIU const& tracks, BCsWithCCDB const& bcs) @@ -140,9 +129,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); } void processMonteCarlo(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIU const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) @@ -150,9 +140,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); } void processRealDataWithPID(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIUWithPID const& tracks, BCsWithCCDB const& bcs) @@ -160,9 +151,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); } void processMonteCarloWithPID(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIUWithPID const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) @@ -170,9 +162,10 @@ struct propagationServiceV2 { if (bcs.size() == 0) { return; } - initCCDB(bcs.begin()); - trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, ccdbLoader, collisions, tracks, trackPropagationProducts, histos); - strangenessBuilderModule.dataProcess(ccdb, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); } PROCESS_SWITCH(propagationServiceV2, processRealData, "process real data", true); From 8e8285bd04b84d22f9bb606220f1b36ce5753051 Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Mon, 21 Sep 2026 14:44:49 +0200 Subject: [PATCH 013/648] =?UTF-8?q?[PWGJE,EMCAL]=20Add=20new=20clusterizer?= =?UTF-8?q?=20definitions,=20add=20new=20dispersion=20tables=20and=20QA=20?= =?UTF-8?q?p=E2=80=A6=20(#17948)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGJE/DataModel/EMCALClusters.h | 19 +++++++ PWGJE/TableProducer/emcalCorrectionTask.cxx | 31 +++++++---- PWGJE/Tasks/emcClusterMonitor.cxx | 59 ++++++++++++--------- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/PWGJE/DataModel/EMCALClusters.h b/PWGJE/DataModel/EMCALClusters.h index 915eecaca51..6e7dd16d676 100644 --- a/PWGJE/DataModel/EMCALClusters.h +++ b/PWGJE/DataModel/EMCALClusters.h @@ -50,6 +50,9 @@ inline const EMCALClusterDefinition kV3SmallestTimeDiff(ClusterAlgorithm::kV3, 4 inline const EMCALClusterDefinition kV3MostSplitSmallTimeDiff(ClusterAlgorithm::kV3, 43, 1, "kV3MostSplitSmallTimeDiff", 0.5, 0.1, -10000, 10000, 500, true, 0., false); inline const EMCALClusterDefinition kV3MostSplitSmallerTimeDiff(ClusterAlgorithm::kV3, 44, 1, "kV3MostSplitSmallerTimeDiff", 0.5, 0.1, -10000, 10000, 100, true, 0., false); inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiff(ClusterAlgorithm::kV3, 45, 1, "kV3MostSplitSmallestTimeDiff", 0.5, 0.1, -10000, 10000, 50, true, 0., false); +inline const EMCALClusterDefinition kV3SmallTimeDiffLowestSeed(ClusterAlgorithm::kV3, 46, 1, "kV3SmallTimeDiffLowestSeed", 0.1, 0.1, -10000, 10000, 500, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallerTimeDiffLowSeed(ClusterAlgorithm::kV3, 47, 1, "kV3SmallerTimeDiffLowSeed", 0.3, 0.1, -10000, 10000, 100, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallestTimeDiffLowerSeed(ClusterAlgorithm::kV3, 48, 1, "kV3SmallestTimeDiffLowerSeed", 0.2, 0.1, -10000, 10000, 50, true, 0.03, false); inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowestSeed(ClusterAlgorithm::kV3, 50, 1, "kV3MostSplitSmallestTimeDiffLowestSeed", 0.1, 0.1, -10000, 10000, 50, true, 0., false); inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowSeed(ClusterAlgorithm::kV3, 51, 1, "kV3MostSplitSmallestTimeDiffLowSeed", 0.3, 0.1, -10000, 10000, 50, true, 0., false); inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowerSeed(ClusterAlgorithm::kV3, 52, 1, "kV3MostSplitSmallestTimeDiffLowerSeed", 0.2, 0.1, -10000, 10000, 50, true, 0., false); @@ -94,6 +97,12 @@ inline const EMCALClusterDefinition& getClusterDefinitionFromID(int storageID) return kV3MostSplitSmallerTimeDiff; case 45: return kV3MostSplitSmallestTimeDiff; + case 46: + return kV3SmallTimeDiffLowestSeed; + case 47: + return kV3SmallerTimeDiffLowSeed; + case 48: + return kV3SmallestTimeDiffLowerSeed; case 50: return kV3MostSplitSmallestTimeDiffLowestSeed; case 51: @@ -128,6 +137,9 @@ inline const EMCALClusterDefinition& getClusterDefinitionFromString(const std::s {"kV3MostSplitSmallTimeDiff", 43}, {"kV3MostSplitSmallerTimeDiff", 44}, {"kV3MostSplitSmallestTimeDiff", 45}, + {"kV3SmallTimeDiffLowestSeed", 46}, + {"kV3SmallerTimeDiffLowSeed", 47}, + {"kV3SmallestTimeDiffLowerSeed", 48}, {"kV3MostSplitSmallestTimeDiffLowestSeed", 50}, {"kV3MostSplitSmallestTimeDiffLowSeed", 51}, {"kV3MostSplitSmallestTimeDiffLowerSeed", 52}, @@ -156,6 +168,7 @@ DECLARE_SOA_COLUMN(IsExotic, isExotic, bool); //! flag DECLARE_SOA_COLUMN(DistanceToBadChannel, distanceToBadChannel, float); //! distance to bad channel DECLARE_SOA_COLUMN(NLM, nlm, int); //! number of local maxima DECLARE_SOA_COLUMN(Definition, definition, int); //! cluster definition, see EMCALClusterDefinition.h +DECLARE_SOA_COLUMN(Dispersion, dispersion, float); //! cluster energy dispersion (cells) } // namespace emcalcluster // table of clusters that could be matched to a collision @@ -174,6 +187,12 @@ DECLARE_SOA_TABLE(EMCALAmbiguousClusters, "AOD", "EMCALAMBCLUS", //! using EMCALCluster = EMCALClusters::iterator; using EMCALAmbiguousCluster = EMCALAmbiguousClusters::iterator; +DECLARE_SOA_TABLE(Dispersions, "AOD", "DISPERSIONS", //! + o2::soa::Index<>, emcalcluster::Dispersion); + +DECLARE_SOA_TABLE(AmbigousDispersions, "AOD", "AMBDISPERSIONS", //! + o2::soa::Index<>, emcalcluster::Dispersion); + namespace emcalclustermc { DECLARE_SOA_ARRAY_INDEX_COLUMN(McParticle, mcParticle); //! Array of MC particles that deposited energy in this calo cell diff --git a/PWGJE/TableProducer/emcalCorrectionTask.cxx b/PWGJE/TableProducer/emcalCorrectionTask.cxx index 258cbe8e80c..985e622f7fa 100644 --- a/PWGJE/TableProducer/emcalCorrectionTask.cxx +++ b/PWGJE/TableProducer/emcalCorrectionTask.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -96,8 +97,10 @@ enum CellScaleMode { struct EmcalCorrectionTask { Produces clusters; Produces mcclusters; + Produces dispersions; Produces clustersAmbiguous; Produces mcclustersAmbiguous; + Produces ambigousDispersions; Produces clustercells; // cells belonging to given cluster Produces clustercellsambiguous; Produces matchedTracks; @@ -404,7 +407,7 @@ struct EmcalCorrectionTask { } } - template + template void initZorroCCDB(const BCType& bc) { if (applySoftwareTriggerSelection) { @@ -422,7 +425,9 @@ struct EmcalCorrectionTask { { LOG(debug) << "Starting process full."; clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); @@ -529,7 +534,7 @@ struct EmcalCorrectionTask { // Store the clusters in the table where a matching collision could // be identified. - fillClusterTable(col, vertexPos, iClusterizer, cellIndicesBC, &indexMapPair, &trackGlobalIndex); + fillClusterTable(col, vertexPos, iClusterizer, cellIndicesBC, &indexMapPair, &trackGlobalIndex, nullptr, nullptr); } else { mHistManager.fill(HIST("hBCMatchErrors"), 2); } @@ -582,7 +587,9 @@ struct EmcalCorrectionTask { LOG(debug) << "Starting process full."; clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); @@ -748,8 +755,10 @@ struct EmcalCorrectionTask { clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); mcclusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); mcclustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); @@ -943,8 +952,10 @@ struct EmcalCorrectionTask { clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); mcclusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); mcclustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); @@ -1139,7 +1150,9 @@ struct EmcalCorrectionTask { LOG(debug) << "Starting process standalone."; clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); @@ -1303,12 +1316,11 @@ struct EmcalCorrectionTask { LOG(debug) << "Converted to analysis clusters."; } - template + template void fillClusterTable(Collision const& col, math_utils::Point3D const& vertexPos, size_t iClusterizer, const gsl::span cellIndicesBC, MatchResult* indexMapPair = nullptr, const std::vector* trackGlobalIndex = nullptr, MatchResult* indexMapPairSecondaries = nullptr, const std::vector* secondariesGlobalIndex = nullptr) { // get the clusterType once const auto clusterType = static_cast(mClusterDefinitions[iClusterizer]); - int cellindex = -1; unsigned int iCluster = 0; float energy = 0.f; @@ -1341,11 +1353,11 @@ struct EmcalCorrectionTask { cluster.getClusterTime(), cluster.getIsExotic(), cluster.getDistanceToBadChannel(), cluster.getNExMax(), clusterType); + dispersions(cluster.getDispersion()); ++nCluster; if (!mClusterLabels.empty()) { mcclusters(mClusterLabels[iCluster].getLabels(), mClusterLabels[iCluster].getEnergyFractions()); } - // loop over cells in cluster and save to table for (int ncell = 0; ncell < cluster.getNCells(); ncell++) { cellindex = cluster.getCellIndex(ncell); LOG(debug) << "trying to find cell index " << cellindex << " in map"; @@ -1391,7 +1403,7 @@ struct EmcalCorrectionTask { } // end of cluster loop } - template + template void fillAmbigousClusterTable(BC const& bc, size_t iClusterizer, const gsl::span cellIndicesBC, bool hasCollision) { int cellindex = -1; @@ -1426,6 +1438,7 @@ struct EmcalCorrectionTask { cluster.getM20(), cluster.getNCells(), cluster.getClusterTime(), cluster.getIsExotic(), cluster.getDistanceToBadChannel(), cluster.getNExMax(), static_cast(mClusterDefinitions.at(iClusterizer))); + ambigousDispersions(cluster.getDispersion()); ++nClusterAmb; if (!mClusterLabels.empty()) { mcclustersAmbiguous(mClusterLabels[iCluster].getLabels(), mClusterLabels[iCluster].getEnergyFractions()); @@ -1439,7 +1452,7 @@ struct EmcalCorrectionTask { } // end of cluster loop } - template + template void doTrackMatching(Collision const& col, MyGlobTracks const& tracks, MatchResult& indexMapPair, std::vector& trackGlobalIndex) { auto groupedTracks = tracks.sliceBy(perCollision, col.globalIndex()); @@ -1455,7 +1468,7 @@ struct EmcalCorrectionTask { indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, MaxMatchesPerCluster); } - template + template void doSecondaryTrackMatching(Collision const& col, EMV0Legs const& v0legs, MatchResult& indexMapPair, std::vector& trackGlobalIndex, MyGlobTracks const& tracks) { auto groupedV0Legs = v0legs.sliceBy(perCollisionEMV0Legs, col.globalIndex()); @@ -1491,7 +1504,7 @@ struct EmcalCorrectionTask { indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, MaxMatchesPerCluster); } - template + template void fillTrackInfo(Tracks const& tracks, std::vector& trackPhi, std::vector& trackEta, std::vector& trackGlobalIndex) { for (const auto& track : tracks) { diff --git a/PWGJE/Tasks/emcClusterMonitor.cxx b/PWGJE/Tasks/emcClusterMonitor.cxx index b014ffae8fb..8820d975d6f 100644 --- a/PWGJE/Tasks/emcClusterMonitor.cxx +++ b/PWGJE/Tasks/emcClusterMonitor.cxx @@ -62,8 +62,8 @@ using namespace o2::framework; using namespace o2::framework::expressions; using collisionEvSelIt = o2::soa::Join::iterator; using bcEvSelIt = o2::soa::Join::iterator; -using selectedClusters = o2::soa::Filtered; -using selectedAmbiguousClusters = o2::soa::Filtered; +using selectedClusters = o2::soa::Filtered>; +using selectedAmbiguousClusters = o2::soa::Filtered>; struct ClusterMonitor { HistogramRegistry mHistManager{"ClusterMonitorHistograms"}; o2::emcal::Geometry* mGeometry = nullptr; @@ -104,6 +104,7 @@ struct ClusterMonitor { o2Axis numberClustersAxis{mNumberClusterBinning, "Number of clusters / event"}; const AxisSpec thAxisCellTimeDiff{3000, -1500, 1500, "#Delta#it{t}_{cell} (ns)"}; const AxisSpec thAxisCellTimeMean{1500, -600, 900, "#LT#it{t}_{cell}#GT (ns)"}; + const AxisSpec thAxisDispersion{100, 0, 10, "dispersion (cells)"}; // event properties mHistManager.add("eventsAll", "Number of events", o2HistType::kTH1D, {{1, 0.5, 1.5}}); @@ -124,8 +125,8 @@ struct ClusterMonitor { mHistManager.add("clusterESupermodule", "Energy of the cluster vs. supermoduleID", o2HistType::kTH2F, {energyAxis, supermoduleAxis}); mHistManager.add("clusterE_SimpleBinning", "Energy of cluster", o2HistType::kTH1D, {{2000, 0, 200}}); mHistManager.add("clusterEtaPhi", "Eta and phi of cluster", o2HistType::kTH2F, {{100, -1, 1}, {100, 0, 2 * TMath::Pi()}}); - mHistManager.add("clusterM02", "M02 of cluster", o2HistType::kTH1D, {{400, 0, 5}}); - mHistManager.add("clusterM20", "M20 of cluster", o2HistType::kTH1D, {{400, 0, 2.5}}); + mHistManager.add("clusterM02", "M02 of cluster;M_{02} (cells);#it{E} (GeV)", o2HistType::kTH2D, {{400, 0, 5}, {100, 0, 10}}); + mHistManager.add("clusterM20", "M20 of cluster", o2HistType::kTH2D, {{400, 0, 2.5}, {100, 0, 10}}); mHistManager.add("clusterNLM", "Number of local maxima of cluster", o2HistType::kTH1D, {{10, 0, 10}}); mHistManager.add("clusterNCells", "Number of cells in cluster", o2HistType::kTH1D, {{50, 0, 50}}); mHistManager.add("clusterDistanceToBadChannel", "Distance to bad channel", o2HistType::kTH1D, {{100, 0, 100}}); @@ -133,6 +134,7 @@ struct ClusterMonitor { mHistManager.add("clusterAmpFractionLeadingCell", "Fraction of energy in leading cell", o2HistType::kTH1D, {{100, 0, 1}}); mHistManager.add("clusterCellTimeDiff", "Cell time difference in clusters", o2HistType::kTH1D, {thAxisCellTimeDiff}); mHistManager.add("clusterCellTimeMean", "Mean cell time per cluster", o2HistType::kTH1D, {thAxisCellTimeMean}); + mHistManager.add("clusterDispersion", "Cluster dispersion vs energy", o2HistType::kTH2D, {thAxisDispersion, energyAxis}); // add histograms per supermodule for (int ism = 0; ism < 20; ++ism) { @@ -142,10 +144,10 @@ struct ClusterMonitor { mHistManager.add(Form("clusterM20VsESM/clusterM20VsESM%d", ism), Form("Cluster M20 vs energy in Supermodule %d", ism), o2HistType::kTH2F, {{400, 0, 2.5}, amplitudeAxisLarge}); } - if (mVetoBCID->length()) { + if (!mVetoBCID->empty()) { std::stringstream parser(mVetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -155,7 +157,7 @@ struct ClusterMonitor { if (mSelectBCID.value != "all") { std::stringstream parser(mSelectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -204,7 +206,7 @@ struct ClusterMonitor { mHistManager.fill(HIST("numberOfClustersEvents"), clusters.size()); LOG(debug) << "bunch crossing ID" << theCollision.bcId(); - std::array numberOfClustersSM; + std::array numberOfClustersSM{0}; std::fill(numberOfClustersSM.begin(), numberOfClustersSM.end(), 0); // loop over all clusters from accepted collision // auto eventClusters = clusters.select(o2::aod::emcalcluster::bcId == theCollision.bc().globalBC()); @@ -220,12 +222,13 @@ struct ClusterMonitor { mHistManager.fill(HIST("clusterE"), cluster.energy()); mHistManager.fill(HIST("clusterE_SimpleBinning"), cluster.energy()); mHistManager.fill(HIST("clusterEtaPhi"), cluster.eta(), cluster.phi()); - mHistManager.fill(HIST("clusterM02"), cluster.m02()); - mHistManager.fill(HIST("clusterM20"), cluster.m20()); + mHistManager.fill(HIST("clusterM02"), cluster.m02(), cluster.energy()); + mHistManager.fill(HIST("clusterM20"), cluster.m20(), cluster.energy()); mHistManager.fill(HIST("clusterTimeVsE"), cluster.time(), cluster.energy()); mHistManager.fill(HIST("clusterNLM"), cluster.nlm()); mHistManager.fill(HIST("clusterNCells"), cluster.nCells()); mHistManager.fill(HIST("clusterDistanceToBadChannel"), cluster.distanceToBadChannel()); + mHistManager.fill(HIST("clusterDispersion"), cluster.dispersion(), cluster.energy()); // loop over cells in cluster LOG(debug) << "Cluster energy: " << cluster.energy(); LOG(debug) << "Cluster index: " << cluster.index(); @@ -241,6 +244,7 @@ struct ClusterMonitor { } catch (o2::emcal::InvalidPositionException& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. + LOG(debug) << "Skipping cluster at sector boundary: " << e.what(); } // example of loop over all cells of current cluster @@ -290,7 +294,7 @@ struct ClusterMonitor { LOG(info) << "Event rejected because of veto BCID " << eventIR.bc; return; } - if (mSelectBCIDs.size() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { + if (!mSelectBCIDs.empty() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { return; } bool isSelected = true; @@ -311,19 +315,20 @@ struct ClusterMonitor { mHistManager.fill(HIST("eventBCSelected"), eventIR.bc); mHistManager.fill(HIST("numberOfClustersBC"), clusters.size()); - std::array numberOfClustersSM; + std::array numberOfClustersSM{0}; std::fill(numberOfClustersSM.begin(), numberOfClustersSM.end(), 0); // loop over ambiguous clusters for (const auto& cluster : clusters) { mHistManager.fill(HIST("clusterE"), cluster.energy()); mHistManager.fill(HIST("clusterE_SimpleBinning"), cluster.energy()); mHistManager.fill(HIST("clusterEtaPhi"), cluster.eta(), cluster.phi()); - mHistManager.fill(HIST("clusterM02"), cluster.m02()); - mHistManager.fill(HIST("clusterM20"), cluster.m20()); + mHistManager.fill(HIST("clusterM02"), cluster.m02(), cluster.energy()); + mHistManager.fill(HIST("clusterM20"), cluster.m20(), cluster.energy()); mHistManager.fill(HIST("clusterTimeVsE"), cluster.time(), cluster.energy()); mHistManager.fill(HIST("clusterNLM"), cluster.nlm()); mHistManager.fill(HIST("clusterNCells"), cluster.nCells()); mHistManager.fill(HIST("clusterDistanceToBadChannel"), cluster.distanceToBadChannel()); + mHistManager.fill(HIST("clusterDispersion"), cluster.dispersion(), cluster.energy()); try { auto supermoduleID = mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); @@ -333,6 +338,7 @@ struct ClusterMonitor { } catch (o2::emcal::InvalidPositionException& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. + LOG(debug) << "Skipping cluster at sector boundary: " << e.what(); } } for (int supermoduleID = 0; supermoduleID < 20; supermoduleID++) { @@ -456,33 +462,34 @@ struct ClusterMonitor { std::vector result; Int_t nBinsClusterE = 235; for (Int_t i = 0; i < nBinsClusterE + 1; i++) { - if (i < 1) + if (i < 1) { result.emplace_back(0.3 * i); - else if (i < 55) + } else if (i < 55) { result.emplace_back(0.3 + 0.05 * (i - 1)); - else if (i < 105) + } else if (i < 105) { result.emplace_back(3. + 0.1 * (i - 55)); - else if (i < 140) + } else if (i < 140) { result.emplace_back(8. + 0.2 * (i - 105)); - else if (i < 170) + } else if (i < 170) { result.emplace_back(15. + 0.5 * (i - 140)); - else if (i < 190) + } else if (i < 190) { result.emplace_back(30. + 1.0 * (i - 170)); - else if (i < 215) + } else if (i < 215) { result.emplace_back(50. + 2.0 * (i - 190)); - else if (i < 235) + } else if (i < 235) { result.emplace_back(100. + 5.0 * (i - 215)); - else if (i < 245) + } else if (i < 245) { result.emplace_back(200. + 10.0 * (i - 235)); + } } return result; } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc, TaskName{"EMCClusterMonitorTask"}, SetDefaultProcesses{{{"processCollisions", true}, {"processAmbiguous", false}}}), - adaptAnalysisTask(cfgc, TaskName{"EMCClusterMonitorTaskAmbiguous"}, SetDefaultProcesses{{{"processCollisions", false}, {"processAmbiguous", true}}})}; + adaptAnalysisTask(context, TaskName{"EMCClusterMonitorTask"}, SetDefaultProcesses{{{"processCollisions", true}, {"processAmbiguous", false}}}), + adaptAnalysisTask(context, TaskName{"EMCClusterMonitorTaskAmbiguous"}, SetDefaultProcesses{{{"processCollisions", false}, {"processAmbiguous", true}}})}; return workflow; } From 4ca72cf99678a93fab61b7382f3345de0ce5b6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 21 Sep 2026 16:23:33 +0200 Subject: [PATCH 014/648] [ALICE3] Fix code-check warnings (#17988) --- ALICE3/Core/FlatLutEntry.cxx | 74 +++-- ALICE3/Core/FlatLutEntry.h | 76 +++-- ALICE3/Core/FlatLutWriter.cxx | 98 +++--- ALICE3/Core/FlatLutWriter.h | 22 +- ALICE3/Core/FlatTrackSmearer.cxx | 95 +++--- ALICE3/Core/FlatTrackSmearer.h | 45 +-- ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx | 77 +++-- ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx | 286 ++++++++++-------- ALICE3/TableProducer/OTF/onTheFlyTracker.cxx | 4 +- .../TableProducer/OTF/onTheFlyTrackerPid.cxx | 128 ++++---- 10 files changed, 482 insertions(+), 423 deletions(-) diff --git a/ALICE3/Core/FlatLutEntry.cxx b/ALICE3/Core/FlatLutEntry.cxx index 8ced29cdb66..984157f4520 100644 --- a/ALICE3/Core/FlatLutEntry.cxx +++ b/ALICE3/Core/FlatLutEntry.cxx @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FlatLutEntry.cxx +/// \brief Flat LUT implementation for compact helper tables used by the ALICE3 track smearing workflow. + #include "FlatLutEntry.h" #include @@ -22,7 +25,7 @@ #include #include -namespace o2::delphes +namespace o2::fastsim { void lutEntry_t::print() const @@ -30,25 +33,25 @@ void lutEntry_t::print() const LOGF(info, " nch = %f, eta = %f, pt = %f, valid = %s\n", nch, eta, pt, valid ? "true" : "false"); LOGF(info, " eff = %f, eff2 = %f, itof = %f, otof = %f\n", eff, eff2, itof, otof); LOGF(info, " covm: "); - for (int i = 0; i < 15; ++i) { + for (int i = 0; i < kNumCovarianceTerms; ++i) { LOGF(info, "%f ", covm[i]); } LOGF(info, "\n"); LOGF(info, " eigval: "); - for (int i = 0; i < 5; ++i) { + for (int i = 0; i < kNumEigenModes; ++i) { LOGF(info, "%f ", eigval[i]); } LOGF(info, "\n"); LOGF(info, " eigvec:\n"); - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 5; ++j) { + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { LOGF(info, "%f ", eigvec[i][j]); } LOGF(info, "\n"); } LOGF(info, " eiginv:\n"); - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 5; ++j) { + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { LOGF(info, "%f ", eiginv[i][j]); } LOGF(info, "\n"); @@ -57,23 +60,18 @@ void lutEntry_t::print() const float map_t::fracPositionWithinBin(float val) const { - float width = (max - min) / nbins; - int bin; - float returnVal = 0.5f; + const float width = (max - min) / nbins; + const int bin = find(val); if (log) { - bin = static_cast((std::log10(val) - min) / width); - returnVal = ((std::log10(val) - min) / width) - bin; - } else { - bin = static_cast((val - min) / width); - returnVal = val / width - bin; + return ((std::log10(val) - min) / width) - bin; } - return returnVal; + return val / width - bin; } int map_t::find(float val) const { - float width = (max - min) / nbins; - int bin; + const float width = (max - min) / nbins; + int bin = 0; if (log) { bin = static_cast((std::log10(val) - min) / width); } else { @@ -93,7 +91,7 @@ void map_t::print() const LOGF(info, "nbins = %d, min = %f, max = %f, log = %s \n", nbins, min, max, log ? "on" : "off"); } -bool lutHeader_t::check_version() const +bool lutHeader_t::checkVersion() const { return (version == LUTCOVM_VERSION); } @@ -120,7 +118,7 @@ void FlatLutData::initialize(const lutHeader_t& header) mEtaBins = header.etamap.nbins; mPtBins = header.ptmap.nbins; - const size_t headerSize = sizeof(lutHeader_t); + constexpr size_t headerSize = sizeof(lutHeader_t); const size_t numEntries = static_cast(mNchBins) * mRadBins * mEtaBins * mPtBins; const size_t entriesSize = numEntries * sizeof(lutEntry_t); const size_t totalSize = headerSize + entriesSize; @@ -133,10 +131,10 @@ void FlatLutData::initialize(const lutHeader_t& header) size_t FlatLutData::getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { - static constexpr size_t headerSize = sizeof(lutHeader_t); + static constexpr size_t HeaderSize = sizeof(lutHeader_t); const size_t linearIdx = getEntryIndex(nch_bin, rad_bin, eta_bin, pt_bin); - static constexpr size_t entrySize = sizeof(lutEntry_t); - return headerSize + linearIdx * entrySize; + static constexpr size_t EntrySize = sizeof(lutEntry_t); + return HeaderSize + linearIdx * EntrySize; } const lutEntry_t* FlatLutData::getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const @@ -153,12 +151,12 @@ lutEntry_t* FlatLutData::getEntry(int nch_bin, int rad_bin, int eta_bin, int pt_ const lutHeader_t& FlatLutData::getHeaderRef() const { - return *reinterpret_cast(mDataRef.data()); + return *static_cast(static_cast(mDataRef.data())); } lutHeader_t& FlatLutData::getHeader() { - return *reinterpret_cast(mData.data()); + return *static_cast(static_cast(mData.data())); } void FlatLutData::updateRef() @@ -200,26 +198,26 @@ void FlatLutData::view(const uint8_t* buffer, size_t size) void FlatLutData::validateBuffer(const uint8_t* buffer, size_t size) { - auto header = PreviewHeader(buffer, size); - auto mNchBins = header.nchmap.nbins; - auto mRadBins = header.radmap.nbins; - auto mEtaBins = header.etamap.nbins; - auto mPtBins = header.ptmap.nbins; + auto header = previewHeader(buffer, size); + const auto nchBins = header.nchmap.nbins; + const auto radBins = header.radmap.nbins; + const auto etaBins = header.etamap.nbins; + const auto ptBins = header.ptmap.nbins; - size_t expectedSize = sizeof(lutHeader_t) + static_cast(mNchBins) * mRadBins * mEtaBins * mPtBins * sizeof(lutEntry_t); + const size_t expectedSize = sizeof(lutHeader_t) + static_cast(nchBins) * radBins * etaBins * ptBins * sizeof(lutEntry_t); if (size < expectedSize) { throw framework::runtime_error_f("Buffer size mismatch: expected %zu, got %zu", expectedSize, size); } } -lutHeader_t FlatLutData::PreviewHeader(const uint8_t* buffer, size_t size) +lutHeader_t FlatLutData::previewHeader(const uint8_t* buffer, size_t size) { if (size < sizeof(lutHeader_t)) { throw framework::runtime_error_f("Buffer too small for LUT header: expected at least %zu, got %zu", sizeof(lutHeader_t), size); } - const auto* header = reinterpret_cast(buffer); - if (!header->check_version()) { + const auto* header = static_cast(static_cast(buffer)); + if (!header->checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, header->version); } return *header; @@ -256,14 +254,14 @@ bool FlatLutData::isLoaded() const return ((!mData.empty()) || (!mDataRef.empty())); } -lutHeader_t FlatLutData::PreviewHeader(std::ifstream& file, const char* filename) +lutHeader_t FlatLutData::previewHeader(std::ifstream& file, const char* filename) { lutHeader_t tempHeader; file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); if (file.gcount() != static_cast(sizeof(lutHeader_t))) { throw framework::runtime_error_f("Failed to read LUT header from %s", filename); } - if (!tempHeader.check_version()) { + if (!tempHeader.checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, tempHeader.version); } return tempHeader; @@ -272,7 +270,7 @@ lutHeader_t FlatLutData::PreviewHeader(std::ifstream& file, const char* filename FlatLutData FlatLutData::loadFromFile(std::ifstream& file, const char* filename) { // Read header first - lutHeader_t tempHeader = PreviewHeader(file, filename); + lutHeader_t tempHeader = previewHeader(file, filename); FlatLutData data; @@ -300,4 +298,4 @@ void FlatLutData::reset() resetDimensions(); } -} // namespace o2::delphes +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatLutEntry.h b/ALICE3/Core/FlatLutEntry.h index 330c3e6df87..7e095f989c1 100644 --- a/ALICE3/Core/FlatLutEntry.h +++ b/ALICE3/Core/FlatLutEntry.h @@ -9,9 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FlatLutEntry.h +/// \brief Flat LUT data structures and buffer handling for the ALICE3 fast smearing backend. + #ifndef ALICE3_CORE_FLATLUTENTRY_H_ #define ALICE3_CORE_FLATLUTENTRY_H_ +#include #include #include #include @@ -19,15 +23,22 @@ #include #include -#define LUTCOVM_VERSION 20210801 +static constexpr int LUTCOVM_VERSION = 20210801; -namespace o2::delphes +namespace o2::fastsim { +constexpr int kNumCovarianceTerms = 15; +constexpr int kNumEigenModes = 5; +typedef std::array CovarianceArray; +typedef std::array EigenArray; +typedef std::array EigenArrayDouble; +typedef std::array, kNumEigenModes> EigenMatrix; + /** - * @brief Flat LUT entry structure + * @brief Flat LUT entry structure. */ -struct lutEntry_t { +struct LutEntry { float nch = 0.f; float eta = 0.f; float pt = 0.f; @@ -36,41 +47,46 @@ struct lutEntry_t { float eff2 = 0.f; float itof = 0.f; float otof = 0.f; - float covm[15] = {0.f}; - float eigval[5] = {0.f}; - float eigvec[5][5] = {{0.f}}; - float eiginv[5][5] = {{0.f}}; + CovarianceArray covm = {0.f}; + EigenArray eigval = {0.f}; + EigenMatrix eigvec = {{{0.f}}}; + EigenMatrix eiginv = {{{0.f}}}; void print() const; }; +using lutEntry_t = LutEntry; + /** - * @brief Binning map + * @brief Binning map. */ -struct map_t { +struct Map { int nbins = 1; float min = 0.f; float max = 1.e6f; bool log = false; - float eval(int bin) const + [[nodiscard]] float eval(int bin) const { float width = (max - min) / nbins; float val = min + (bin + 0.5f) * width; - if (log) + if (log) { return std::pow(10.f, val); + } return val; } - float fracPositionWithinBin(float val) const; - int find(float val) const; + [[nodiscard]] float fracPositionWithinBin(float val) const; + [[nodiscard]] int find(float val) const; void print() const; }; +using map_t = Map; + /** - * @brief LUT header + * @brief LUT header. */ -struct lutHeader_t { +struct LutHeader { int version = LUTCOVM_VERSION; int pdg = 0; float mass = 0.f; @@ -80,12 +96,14 @@ struct lutHeader_t { map_t etamap; map_t ptmap; - bool check_version() const; + [[nodiscard]] bool checkVersion() const; void print() const; }; +using lutHeader_t = LutHeader; + /** - * @brief Flat LUT data container - single contiguous buffer + * @brief Flat LUT data container - single contiguous buffer. * Memory layout: [header][entry_0][entry_1]...[entry_N] * * All entries stored sequentially in a single allocation. @@ -104,7 +122,7 @@ class FlatLutData */ void initialize(const lutHeader_t& header); - size_t getEntryIndex(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const + [[nodiscard]] size_t getEntryIndex(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { // Linear index: nch varies slowest, pt varies fastest // idx = nch * (rad*eta*pt) + rad * (eta*pt) + eta * pt + pt @@ -114,7 +132,7 @@ class FlatLutData /** * @brief Get LUT entry by bin indices (view) */ - const lutEntry_t* getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; + [[nodiscard]] const lutEntry_t* getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; /** * @brief Get LUT entry by bin indices (owned) @@ -124,7 +142,7 @@ class FlatLutData /** * @brief Get LUT header (view) */ - const lutHeader_t& getHeaderRef() const; + [[nodiscard]] const lutHeader_t& getHeaderRef() const; /** * @brief Get LUT header (owned) @@ -134,13 +152,13 @@ class FlatLutData /** * @brief Get raw data buffer */ - uint8_t* data() { return mData.data(); } // owned - const uint8_t* data() const { return mDataRef.data(); } // view + uint8_t* data() { return mData.data(); } // owned + [[nodiscard]] const uint8_t* data() const { return mDataRef.data(); } // view /** * @brief Total size in bytes */ - size_t bytes() const { return mDataRef.size(); } + [[nodiscard]] size_t bytes() const { return mDataRef.size(); } /** * @brief Construct a new FlatLutData from external buffer as a copy @@ -165,17 +183,17 @@ class FlatLutData /** * @brief Preview buffer header for version and other compatibility checks */ - static lutHeader_t PreviewHeader(const uint8_t* buffer, size_t size); + static lutHeader_t previewHeader(const uint8_t* buffer, size_t size); /** * @brief Preview file-stored header for version and other compatibility checks */ - static lutHeader_t PreviewHeader(std::ifstream& file, const char* filename); + static lutHeader_t previewHeader(std::ifstream& file, const char* filename); /** * @brief Check if the LUT is loaded */ - bool isLoaded() const; + [[nodiscard]] bool isLoaded() const; /** * @brief Reset LUT to empty @@ -186,7 +204,7 @@ class FlatLutData /** * @brief Linear index calculation for entry access */ - size_t getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; + [[nodiscard]] size_t getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; /** * @brief Update dimensions from the current header @@ -224,6 +242,6 @@ class FlatLutData int mPtBins = 0; }; -} // namespace o2::delphes +} // namespace o2::fastsim #endif // ALICE3_CORE_FLATLUTENTRY_H_ diff --git a/ALICE3/Core/FlatLutWriter.cxx b/ALICE3/Core/FlatLutWriter.cxx index 4fe806c4ef4..dfb874af223 100644 --- a/ALICE3/Core/FlatLutWriter.cxx +++ b/ALICE3/Core/FlatLutWriter.cxx @@ -30,12 +30,11 @@ #include #include +#include #include #include #include -using namespace o2::delphes; - namespace o2::fastsim { void FlatLutWriter::print() const @@ -57,7 +56,7 @@ void FlatLutWriter::print() const std::string FlatLutWriter::LutBinning::toString() const { - std::string str = ""; + std::string str; str.append(log ? "log" : "lin"); str.append(" nbins: "); str.append(std::to_string(nbins)); @@ -97,25 +96,28 @@ bool FlatLutWriter::fatSolve(lutEntry_t& lutEntry, lutEntry.itof = fat.getGoodHitProb(itof); lutEntry.otof = fat.getGoodHitProb(otof); - static constexpr int nCov = 15; - for (int i = 0; i < nCov; ++i) + for (int i = 0; i < kNumCovarianceTerms; ++i) { lutEntry.covm[i] = trkOut.getCov()[i]; + } // Define the efficiency auto totfake = 0.f; lutEntry.eff = 1.f; for (size_t i = 1; i < fat.getNLayers(); ++i) { - if (fat.isLayerInert(i)) + if (fat.isLayerInert(i)) { continue; // skip inert layers + } auto igoodhit = fat.getGoodHitProb(i); - if (igoodhit <= 0.f || i == itof || i == otof) + if (igoodhit <= 0.f || i == itof || i == otof) { continue; + } lutEntry.eff *= igoodhit; auto pairfake = 0.f; for (size_t j = i + 1; j < fat.getNLayers(); ++j) { auto jgoodhit = fat.getGoodHitProb(j); - if (jgoodhit <= 0.f || j == itof || j == otof) + if (jgoodhit <= 0.f || j == itof || j == otof) { continue; + } pairfake = (1.f - igoodhit) * (1.f - jgoodhit); break; } @@ -127,14 +129,14 @@ bool FlatLutWriter::fatSolve(lutEntry_t& lutEntry, } #ifdef USE_FWD_PARAM -bool FlatLutWriter::fwdSolve(float* covm, float pt, float eta, float mass) +bool FlatLutWriter::fwdSolve(CovarianceArray covm, float pt, float eta, float mass) { if (fwdRes(covm, pt, eta, mass) < 0) return false; return true; } #else -bool FlatLutWriter::fwdSolve(float*, float, float, float) +bool FlatLutWriter::fwdSolve(CovarianceArray, float, float, float) { return false; } @@ -155,9 +157,8 @@ bool FlatLutWriter::fwdPara(lutEntry_t& lutEntry, float pt, float eta, float mas return false; } - static constexpr int nCov = 15; - float covmbarrel[nCov] = {0.f}; - for (int i = 0; i < nCov; ++i) { + CovarianceArray covmbarrel = {0.f}; + for (int i = 0; i < kNumCovarianceTerms; ++i) { covmbarrel[i] = lutEntry.covm[i]; } @@ -242,7 +243,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t } lutHeader.mass = particle->Mass(); - const int q = std::abs(particle->Charge()) / 3; + const int q = std::abs(static_cast(particle->Charge())) / 3; if (q <= 0) { LOGF(error, "Negative or null charge (%f) for pdg code %d", particle->Charge(), pdg); lutFile.close(); @@ -290,7 +291,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t LOGF(info, "Writing nch bin %d/%d", inch, nnch); auto nch = lutHeader.nchmap.eval(inch); lutEntry.nch = nch; - fat.setdNdEtaCent(nch); + fat.setdNdEtaCent(static_cast(nch)); for (int irad = 0; irad < nrad; ++irad) { for (int ieta = 0; ieta < neta; ++ieta) { @@ -379,10 +380,9 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t void FlatLutWriter::diagonalise(lutEntry_t& lutEntry) { - static constexpr int kEig = 5; - TMatrixDSym m(kEig); + TMatrixDSym m(kNumEigenModes); - for (int i = 0, k = 0; i < kEig; ++i) { + for (int i = 0, k = 0; i < kNumEigenModes; ++i) { for (int j = 0; j < i + 1; ++j, ++k) { m(i, j) = lutEntry.covm[k]; m(j, i) = lutEntry.covm[k]; @@ -393,40 +393,49 @@ void FlatLutWriter::diagonalise(lutEntry_t& lutEntry) // Eigenvalues const TVectorD& eigenVal = eigen.GetEigenValues(); - for (int i = 0; i < kEig; ++i) + for (int i = 0; i < kNumEigenModes; ++i) { lutEntry.eigval[i] = eigenVal[i]; + } // Eigenvectors TMatrixD eigenVec = eigen.GetEigenVectors(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { lutEntry.eigvec[i][j] = eigenVec[i][j]; + } + } // Inverse eigenvectors eigenVec.Invert(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { lutEntry.eiginv[i][j] = eigenVec[i][j]; + } + } } TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, float nch, float radius, float eta, float pt) { LOGF(info, "Reading LUT file: %s", filename); - static const int kNch = 0; - static const int kEta = 1; - static const int kPt = 2; + enum VsType : uint8_t { + kNch = 0, + kEta = 1, + kPt = 2 + }; - static const int kEfficiency = 0; - static const int kEfficiency2 = 1; - static const int kEfficiencyInnerTOF = 2; - static const int kEfficiencyOuterTOF = 3; - static const int kPtResolution = 4; - static const int kRPhiResolution = 5; - static const int kZResolution = 6; + enum WhatType : uint8_t { + kEfficiency = 0, + kEfficiency2 = 1, + kEfficiencyInnerTOF = 2, + kEfficiencyOuterTOF = 3, + kPtResolution = 4, + kRPhiResolution = 5, + kZResolution = 6 + }; // Use TrackSmearer to load and access the LUT - o2::delphes::TrackSmearer smearer; + o2::fastsim::TrackSmearer smearer; if (!smearer.loadTable(pdg, filename)) { LOGF(error, "Failed to load LUT from %s", filename); return nullptr; @@ -452,7 +461,7 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, lutMap = lutHeader->ptmap; break; default: - LOGF(error, "Unknown vs: %d", vs); + LOG(fatal) << "Unknown vs: " << vs; return nullptr; } @@ -475,6 +484,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, LOGF(info, "Plot versus Pt"); g->GetXaxis()->SetTitle("p_{T} (GeV/c)"); break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } switch (what) { @@ -507,7 +520,7 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, g->GetYaxis()->SetTitle("Z Resolution (#mum)"); break; default: - LOGF(error, "Unknown what: %d", what); + LOG(fatal) << "Unknown what: " << what; delete g; return nullptr; } @@ -525,6 +538,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, case kPt: pt = lutMap.eval(i); break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } float eff = 0.f; @@ -549,6 +566,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, case kPt: cen = lutEntry->pt; break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } double val = 0.f; @@ -575,8 +596,9 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, val = std::sqrt(lutEntry->covm[1]) * 1.e4f; // z resolution (um) break; default: - LOGF(error, "Unknown what: %d", what); - break; + LOG(fatal) << "Unknown what: " << what; + delete g; + return nullptr; } g->AddPoint(cen, val); } diff --git a/ALICE3/Core/FlatLutWriter.h b/ALICE3/Core/FlatLutWriter.h index fd29a174c86..33de44dc8f8 100644 --- a/ALICE3/Core/FlatLutWriter.h +++ b/ALICE3/Core/FlatLutWriter.h @@ -23,7 +23,7 @@ #include namespace o2::fastsim { -using lutEntry_t = o2::delphes::lutEntry_t; +using lutEntry_t = o2::fastsim::lutEntry_t; /** * @brief LUT writer using flat binary format * @@ -39,10 +39,10 @@ class FlatLutWriter FlatLutWriter() = default; // Setters for binning configuration - void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {log, nbins, min, max}; } - void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {log, nbins, min, max}; } - void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {log, nbins, min, max}; } - void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {log, nbins, min, max}; } + void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } void setEtaMaxBarrel(float eta) { etaMaxBarrel = eta; } void setAtLeastHits(int n) { mAtLeastHits = n; } @@ -59,7 +59,7 @@ class FlatLutWriter const float nch = 1.0f); void print() const; - bool fwdSolve(float* covm, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); + bool fwdSolve(o2::fastsim::CovarianceArray covm, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); bool fwdPara(lutEntry_t& lutEntry, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], float Bfield = 0.5f); void lutWrite(const char* filename = "lutCovm.dat", int pdg = 211, float field = 0.2f, size_t itof = 0, size_t otof = 0); TGraph* lutRead(const char* filename, int pdg, int what, int vs, float nch = 0.f, float radius = 0.f, float eta = 0.f, float pt = 0.f); @@ -84,13 +84,13 @@ class FlatLutWriter int nbins; float min; float max; - std::string toString() const; + [[nodiscard]] std::string toString() const; }; - LutBinning mNchBinning = {true, 20, 0.5f, 3.5f}; - LutBinning mRadiusBinning = {false, 1, 0.0f, 100.0f}; - LutBinning mEtaBinning = {false, 80, -4.0f, 4.0f}; - LutBinning mPtBinning = {true, 200, -2.0f, 2.0f}; + LutBinning mNchBinning = {.log = true, .nbins = 20, .min = 0.5f, .max = 3.5f}; + LutBinning mRadiusBinning = {.log = false, .nbins = 1, .min = 0.0f, .max = 100.0f}; + LutBinning mEtaBinning = {.log = false, .nbins = 80, .min = -4.0f, .max = 4.0f}; + LutBinning mPtBinning = {.log = true, .nbins = 200, .min = -2.0f, .max = 2.0f}; }; } // namespace o2::fastsim diff --git a/ALICE3/Core/FlatTrackSmearer.cxx b/ALICE3/Core/FlatTrackSmearer.cxx index d90c8a8997b..64c370d52a1 100644 --- a/ALICE3/Core/FlatTrackSmearer.cxx +++ b/ALICE3/Core/FlatTrackSmearer.cxx @@ -28,9 +28,9 @@ #include #include -namespace o2::delphes +namespace o2::fastsim { -int TrackSmearer::getIndexPDG(int pdg) +int TrackSmearer::getIndexPDG(const int pdg) { switch (std::abs(pdg)) { case 11: @@ -56,7 +56,7 @@ int TrackSmearer::getIndexPDG(int pdg) } } -const char* TrackSmearer::getParticleName(int pdg) +const char* TrackSmearer::getParticleName(const int pdg) { switch (std::abs(pdg)) { case 11: @@ -82,15 +82,6 @@ const char* TrackSmearer::getParticleName(int pdg) } } -void TrackSmearer::setWhatEfficiency(int val) -{ - // FIXME: this really should be an enum - if (val > 2) { - throw framework::runtime_error_f("getLUTEntry: unknown efficiency type %d", mWhatEfficiency); - } - mWhatEfficiency = val; -} - bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) { if (!filename || filename[0] == '\0') { @@ -121,7 +112,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) LOGF(error, "LUT header PDG mismatch: expected %d, got %d; not loading", pdg, header.pdg); return false; } - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); return false; } @@ -139,13 +130,13 @@ bool TrackSmearer::adoptTable(int pdg, const uint8_t* buffer, size_t size, bool return false; } try { - auto header = FlatLutData::PreviewHeader(buffer, size); + auto header = FlatLutData::previewHeader(buffer, size); if (header.pdg != pdg && !checkSpecialCase(pdg, header)) { LOGF(error, "LUT header PDG mismatch: expected %d, got %d", pdg, header.pdg); return false; } mLUTData[ipdg] = FlatLutData::AdoptFromBuffer(buffer, size); - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); } @@ -162,13 +153,13 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f return false; } try { - auto header = FlatLutData::PreviewHeader(buffer, size); + auto header = FlatLutData::previewHeader(buffer, size); if (header.pdg != pdg && !checkSpecialCase(pdg, header)) { LOGF(error, "LUT header PDG mismatch: expected %d, got %d", pdg, header.pdg); return false; } mLUTData[ipdg] = FlatLutData::ViewFromBuffer(buffer, size); - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); } @@ -188,22 +179,14 @@ bool TrackSmearer::hasTable(int pdg) const return mLUTData[ipdg].isLoaded(); } -bool TrackSmearer::checkSpecialCase(int pdg, lutHeader_t const& header) +bool TrackSmearer::checkSpecialCase(const int pdg, lutHeader_t const& header) { // Validate header - bool specialPdgCase = false; - switch (pdg) { - case o2::constants::physics::kAlpha: - // Special case: Allow Alpha particles to use He3 LUT - specialPdgCase = (header.pdg == o2::constants::physics::kHelium3); - if (specialPdgCase) { - LOGF(info, "Alpha particles (PDG %d) will use He3 LUT data (PDG %d)", pdg, header.pdg); - } - break; - default: - break; + if (pdg == o2::constants::physics::kAlpha && (header.pdg == o2::constants::physics::kHelium3)) { // Special case: Allow Alpha particles to use He3 LUT + LOGF(info, "Alpha particles (PDG %d) will use He3 LUT data (PDG %d)", pdg, header.pdg); + return true; } - return specialPdgCase; + return false; } const lutHeader_t* TrackSmearer::getLUTHeader(int pdg) const @@ -235,7 +218,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons static constexpr float kFractionThreshold = 0.5f; if (fraction > kFractionThreshold) { switch (mWhatEfficiency) { - case 1: { + case kWhatEfficiencyReco: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch < header.nchmap.nbins - 1) { const auto* entry_next = mLUTData[ipdg].getEntryRef(inch + 1, irad, ieta, ipt); @@ -245,7 +228,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } - case 2: { + case kWhatEfficiencyRecoAndTOF: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch < header.nchmap.nbins - 1) { const auto* entry_next = mLUTData[ipdg].getEntryRef(inch + 1, irad, ieta, ipt); @@ -255,11 +238,13 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } else { float comparisonValue = header.nchmap.log ? std::log10(nch) : nch; switch (mWhatEfficiency) { - case 1: { + case kWhatEfficiencyReco: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch > 0 && comparisonValue < header.nchmap.max) { const auto* entry_prev = mLUTData[ipdg].getEntryRef(inch - 1, irad, ieta, ipt); @@ -269,7 +254,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } - case 2: { + case kWhatEfficiencyRecoAndTOF: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch > 0 && comparisonValue < header.nchmap.max) { const auto* entry_prev = mLUTData[ipdg].getEntryRef(inch - 1, irad, ieta, ipt); @@ -279,18 +264,22 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } } else { const auto* entry = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (entry) { switch (mWhatEfficiency) { - case 1: + case kWhatEfficiencyReco: interpolatedEff = entry->eff; break; - case 2: + case kWhatEfficiencyRecoAndTOF: interpolatedEff = entry->eff2; break; + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } } @@ -298,7 +287,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons return mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); } -bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, float interpolatedEff) +bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, const float interpolatedEff) { bool isReconstructed = true; @@ -306,10 +295,10 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa if (mUseEfficiency) { auto eff = 0.f; switch (mWhatEfficiency) { - case 1: + case kWhatEfficiencyReco: eff = lutEntry->eff; break; - case 2: + case kWhatEfficiencyRecoAndTOF: eff = lutEntry->eff2; break; } @@ -327,20 +316,19 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa } // Transform params vector and smear - static constexpr int kParSize = 5; - double params[kParSize]; - for (int i = 0; i < kParSize; ++i) { + EigenArrayDouble params; + for (int i = 0; i < kNumEigenModes; ++i) { double val = 0.; - for (int j = 0; j < kParSize; ++j) { + for (int j = 0; j < kNumEigenModes; ++j) { val += lutEntry->eigvec[j][i] * o2track.getParam(j); } params[i] = gRandom->Gaus(val, std::sqrt(lutEntry->eigval[i])); } // Transform back params vector - for (int i = 0; i < kParSize; ++i) { + for (int i = 0; i < kNumEigenModes; ++i) { double val = 0.; - for (int j = 0; j < kParSize; ++j) { + for (int j = 0; j < kNumEigenModes; ++j) { val += lutEntry->eiginv[j][i] * params[j]; } o2track.setParam(val, i); @@ -352,22 +340,21 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa } // Set covariance matrix - static constexpr int kCovMatSize = 15; - for (int i = 0; i < kCovMatSize; ++i) { + for (int i = 0; i < kNumCovarianceTerms; ++i) { o2track.setCov(lutEntry->covm[i], i); } return isReconstructed; } -bool TrackSmearer::smearTrack(O2Track& o2track, int pdg, float nch) +bool TrackSmearer::smearTrack(O2Track& o2track, const int pdg, const float nch) { auto pt = o2track.getPt(); - switch (pdg) { - case o2::constants::physics::kHelium3: - case -o2::constants::physics::kHelium3: - pt *= 2.f; - break; + if (pdg == o2::constants::physics::kHelium3 || + pdg == -o2::constants::physics::kHelium3 || + pdg == o2::constants::physics::kAlpha || + pdg == -o2::constants::physics::kAlpha) { + pt *= 2.f; } auto eta = o2track.getEta(); @@ -423,4 +410,4 @@ double TrackSmearer::getEfficiency(const int pdg, const float nch, const float e return efficiency; } -} // namespace o2::delphes +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatTrackSmearer.h b/ALICE3/Core/FlatTrackSmearer.h index e7943d7c1c0..6008a560e9b 100644 --- a/ALICE3/Core/FlatTrackSmearer.h +++ b/ALICE3/Core/FlatTrackSmearer.h @@ -17,11 +17,12 @@ #include #include +#include #include #include #include -namespace o2::delphes +namespace o2::fastsim { /** * @brief Track smearing with flat LUT backend @@ -37,47 +38,49 @@ class TrackSmearer bool adoptTable(int pdg, const uint8_t* buffer, size_t size, bool forceReload = false); bool viewTable(int pdg, const uint8_t* buffer, size_t size, bool forceReload = false); bool viewTable(int pdg, std::span const& span, bool forceReload = false); - bool hasTable(int pdg) const; + [[nodiscard]] bool hasTable(int pdg) const; void useEfficiency(bool val) { mUseEfficiency = val; } void interpolateEfficiency(bool val) { mInterpolateEfficiency = val; } void skipUnreconstructed(bool val) { mSkipUnreconstructed = val; } - void setWhatEfficiency(int val); + enum EfficiencyType : uint8_t { + kWhatEfficiencyReco = 0, + kWhatEfficiencyRecoAndTOF = 1 + }; + void setWhatEfficiency(EfficiencyType val) { mWhatEfficiency = val; } - const lutHeader_t* getLUTHeader(int pdg) const; - const lutEntry_t* getLUTEntry(int pdg, float nch, float radius, float eta, float pt, float& interpolatedEff) const; + [[nodiscard]] const lutHeader_t* getLUTHeader(const int pdg) const; + const lutEntry_t* getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff) const; - bool smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, float interpolatedEff); - bool smearTrack(O2Track& o2track, int pdg, float nch); + bool smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, const float interpolatedEff); + bool smearTrack(O2Track& o2track, const int pdg, const float nch); - double getPtRes(const int pdg, const float nch, const float eta, const float pt) const; - double getEtaRes(const int pdg, const float nch, const float eta, const float pt) const; - double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) const; - double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) const; - double getEfficiency(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getPtRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getEtaRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getEfficiency(const int pdg, const float nch, const float eta, const float pt) const; - static int getIndexPDG(int pdg); - static const char* getParticleName(int pdg); + static int getIndexPDG(const int pdg); + static const char* getParticleName(const int pdg); - void setdNdEta(float val) { mdNdEta = val; } void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } protected: - static constexpr unsigned int nLUTs = 9; // Number of LUT available - FlatLutData mLUTData[nLUTs]; // Flat data storage + static constexpr unsigned int nLUTs = 9; // Number of LUT available, une per particle species (electron, muon, pion, kaon, proton, deuteron, triton, helium3, alpha) + std::array mLUTData; // Flat data storage bool mUseEfficiency = true; bool mInterpolateEfficiency = false; bool mSkipUnreconstructed = true; // don't smear tracks that are not reco'ed - int mWhatEfficiency = 1; - float mdNdEta = 1600.f; + EfficiencyType mWhatEfficiency = kWhatEfficiencyReco; private: o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; - static bool checkSpecialCase(int pdg, lutHeader_t const& header); + static bool checkSpecialCase(const int pdg, lutHeader_t const& header); }; -} // namespace o2::delphes +} // namespace o2::fastsim #endif // ALICE3_CORE_FLATTRACKSMEARER_H_ diff --git a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx index 7f27c2512ea..31a915fc00d 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx @@ -78,9 +78,9 @@ struct OnTheFlyRichPid { Produces upgradeRichSignal; // necessary for particle charges - Service pdg; + Service pdgDatabase{}; // Necessary for LUTs - Service ccdb; + Service ccdb{}; // add rich-specific configurables here Configurable bRichNumberOfSectors{"bRichNumberOfSectors", 21, "barrel RICH number of sectors"}; @@ -132,7 +132,7 @@ struct OnTheFlyRichPid { o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -296,12 +296,12 @@ struct OnTheFlyRichPid { if (flagRICHLoadDelphesLUTs) { for (int icfg = 0; icfg < nGeometries; ++icfg) { - mSmearer.emplace_back(std::make_unique()); + mSmearer.emplace_back(std::make_unique()); mSmearer[icfg]->setCcdbManager(ccdb.operator->()); std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); for (const auto& entry : globalConfiguration) { int pdg = 0; - if (entry.first.find("lut") != 0) { + if (!entry.first.starts_with("lut")) { continue; } if (entry.first.find("lutEl") != std::string::npos) { @@ -359,11 +359,11 @@ struct OnTheFlyRichPid { histos.add("hSectorID", "hSectorID", kTH1F, {axisSector}); const int kNspec = 9; // electron, muon, pion, kaon, proton, deuteron, triton, helium3, alpha - std::string particleNames1[kNspec] = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "^{3}He", "#it{#alpha}"}; - std::string particleNames2[kNspec] = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; + const std::array particleNames1 = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "^{3}He", "#it{#alpha}"}; + const std::array particleNames2 = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; for (int iTrue = 0; iTrue < kNspec; iTrue++) { - std::string nameTitleBarrelTrackRes = "h2dBarrelAngularResTrack" + particleNames2[iTrue] + "VsP"; - std::string nameTitleBarrelTotalRes = "h2dBarrelAngularResTotal" + particleNames2[iTrue] + "VsP"; + const std::string nameTitleBarrelTrackRes = "h2dBarrelAngularResTrack" + particleNames2[iTrue] + "VsP"; + const std::string nameTitleBarrelTotalRes = "h2dBarrelAngularResTotal" + particleNames2[iTrue] + "VsP"; const AxisSpec axisTrackAngularRes{static_cast(nBinsAngularRes), 0.0f, +5.0f, "Track angular resolution - " + particleNames1[iTrue] + " (mrad)"}; const AxisSpec axisTotalAngularRes{static_cast(nBinsAngularRes), 0.0f, +5.0f, "Total angular resolution - " + particleNames1[iTrue] + " (mrad)"}; histos.add(nameTitleBarrelTrackRes.c_str(), nameTitleBarrelTrackRes.c_str(), kTH2F, {axisMomentum, axisTrackAngularRes}); @@ -371,7 +371,7 @@ struct OnTheFlyRichPid { } for (int iTrue = 0; iTrue < kNspec; iTrue++) { for (int iHyp = 0; iHyp < kNspec; iHyp++) { - std::string nameTitle = "h2dBarrelNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; + const std::string nameTitle = "h2dBarrelNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; if (iTrue == iHyp) { const AxisSpec axisNsigmaCorrect{static_cast(nBinsNsigmaCorrectSpecies), -10.0f, +10.0f, "N#sigma - True " + particleNames1[iTrue] + " vs " + particleNames1[iHyp] + " hypothesis"}; histos.add(nameTitle.c_str(), nameTitle.c_str(), kTH2F, {axisMomentum, axisNsigmaCorrect}); @@ -437,18 +437,14 @@ struct OnTheFlyRichPid { bool checkMagfieldLimit(o2::track::TrackParCov track, const float radius, const float magneticField) { o2::math_utils::CircleXYf_t trcCircle; - float sna, csa; + float sna = NAN, csa = NAN; track.getCircleParams(magneticField, trcCircle, sna, csa); // distance between circle centers (one circle is at origin -> easy) float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); // condition of circles touching - if not satisfied returned value if false - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - return true; - } else { - return false; - } + return centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius); } /// returns sector hit by the track (if any), -1 otherwise @@ -476,9 +472,8 @@ struct OnTheFlyRichPid { const float rSecRichSquared = rSecRich * rSecRich; const float zSecRichSquared = zSecRich * zSecRich; return (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); - } else { - return kErrorValue; } + return kErrorValue; } /// returns Cherenkov angle in rad (above threshold) or bad flag (below threshold) @@ -516,10 +511,7 @@ struct OnTheFlyRichPid { // Require at least 3 photons on average for real angle reconstruction static constexpr float kMinPhotons = 3.f; - if (meanNumberofDetectedPhotons <= kMinPhotons) { - return false; - } - return true; + return meanNumberofDetectedPhotons > kMinPhotons; } /// returns linear interpolation @@ -552,15 +544,13 @@ struct OnTheFlyRichPid { float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithAbsWalls[lowerIndex], kResRingSamplingWithAbsWalls[upperIndex]); // std::cout << "Interpolated y value: " << interpolatedY << std::endl; return interpolatedResRing; - } else { - float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithoutAbsWalls[lowerIndex], kResRingSamplingWithoutAbsWalls[upperIndex]); - // std::cout << "Interpolated y value: " << interpolatedY << std::endl; - return interpolatedResRing; } - } else { - // std::cout << "Unable to interpolate. Target x value is outside the range of available data." << std::endl; - return kErrorValue; - } + float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithoutAbsWalls[lowerIndex], kResRingSamplingWithoutAbsWalls[upperIndex]); + // std::cout << "Interpolated y value: " << interpolatedY << std::endl; + return interpolatedResRing; + + } // std::cout << "Unable to interpolate. Target x value is outside the range of available data." << std::endl; + return kErrorValue; } /// To account border effects in bRICH @@ -579,7 +569,7 @@ struct OnTheFlyRichPid { iSecor = jSector; } } - if (flagSector == false) { + if (!flagSector) { return kErrorValue; // <-- Returning negative value } float rSecRich = radCenters[iSecor].X(); @@ -588,9 +578,9 @@ struct OnTheFlyRichPid { // float zSecTof = detCenters[iSecor].Z(); const float rSecRichSquared = rSecRich * rSecRich; const float zSecRichSquared = zSecRich * zSecRich; - const float radiusRipple = (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); - const float zRipple = radiusRipple / std::tan(polar); - const float absZ = std::hypot(radiusRipple - rSecRich, zRipple - zSecRich); + const float radRipple = (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); + const float zRipple = radRipple / std::tan(polar); + const float absZ = std::hypot(radRipple - rSecRich, zRipple - zSecRich); float fraction = 1.; if (tileZlength / 2. - absZ < radius) { fraction = fraction - (1. / o2::constants::math::PI) * std::acos((tileZlength / 2. - absZ) / radius); @@ -613,8 +603,9 @@ struct OnTheFlyRichPid { float extractRingAngularResolution(const float eta, const float n, const float nGas, const float thicknessRad, const float thicknessGas, const float pixelSize, const float thetaCherenkov, const float tileZlength) { // Check if input angle is error value - if (thetaCherenkov <= kErrorValue + 1) + if (thetaCherenkov <= kErrorValue + 1) { return kErrorValue; + } // Parametrization variables (notation from https://doi.org/10.1016/0168-9002(94)90532-0) const float phiC = 0.; const float thetaP = 0.; @@ -686,8 +677,9 @@ struct OnTheFlyRichPid { // float nPhotons = (tileZlength / 2.0 > radius) ? n0Photons * multiplicitySpectrumFactor * (1.-(2.0*radius)/(o2::constants::math::PI*tileZlength)) : n0Photons * multiplicitySpectrumFactor * (1.-(2.0*radius)/(o2::constants::math::PI*tileZlength) - (2.0/(tileZlength*o2::constants::math::PI))*(-(tileZlength/(2.0))*std::acos(tileZlength/(2.0*radius)) + radius*std::sqrt(1.-std::pow(tileZlength/(2.0*radius),2.0)))); // Considering "exact" resolution (eta by eta) const float nPhotons = n0Photons * multiplicitySpectrumFactor * fractionPhotonsProjectiveRICH(eta, tileZlength, radius); - if (nPhotons <= kErrorValue + 1) + if (nPhotons <= kErrorValue + 1) { return kErrorValue; + } // Ring angular resolution const float ringAngularResolution = singlePhotonAngularResolution / std::sqrt(nPhotons); return ringAngularResolution; @@ -704,7 +696,7 @@ struct OnTheFlyRichPid { { // Compute tracking contribution to timing using the error propagation formula // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) - const double a0 = mass * mass; + const double a0 = 1.0 * mass * mass; const double a1 = refractiveIndex; const double a1Squared = a1 * a1; const float ptCoshEta = pt * std::cosh(eta); @@ -735,9 +727,10 @@ struct OnTheFlyRichPid { // First we compute the number of charged particles in the event float dNdEta = 0.f; if (flagRICHLoadDelphesLUTs) { - for (const auto& track : tracks) { - if (!track.has_mcParticle()) + for (const auto& track : tracks) { // We first compute the number of charged particles in the event + if (!track.has_mcParticle()) { continue; + } auto mcParticle = track.mcParticle(); if (std::abs(mcParticle.eta()) > multiplicityEtaRange) { continue; @@ -745,7 +738,7 @@ struct OnTheFlyRichPid { if (mcParticle.has_daughters()) { continue; } - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (!pdgInfo) { // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; continue; @@ -771,7 +764,7 @@ struct OnTheFlyRichPid { } const auto& mcParticle = track.mcParticle(); - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); // float xPv = kErrorValue; if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { @@ -779,7 +772,7 @@ struct OnTheFlyRichPid { } // get particle to calculate Cherenkov angle and resolution - auto pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (pdgInfo == nullptr) { fillDummyValues(); continue; diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index ac844c857b4..e50656494fc 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -61,26 +61,17 @@ #include #include #include +#include #include #include #include +#include #include #include -#include - using namespace o2; using namespace o2::framework; -std::array, 9> h2dInnerTimeResTrack; -std::array, 9> h2dInnerTimeResTotal; -std::array, 9> h2dOuterTimeResTrack; -std::array, 9> h2dOuterTimeResTotal; -std::array, 9>, 9> h2dInnerNsigmaTrue; -std::array, 9>, 9> h2dOuterNsigmaTrue; -std::array, 9>, 9> h2dInnerDeltaTrue; -std::array, 9>, 9> h2dOuterDeltaTrue; - struct OnTheFlyTofPid { Produces upgradeTofMC; Produces upgradeTof; @@ -88,9 +79,9 @@ struct OnTheFlyTofPid { Produces upgradeTofShortLived; // necessary for particle charges - Service pdg; + Service pdgDatabase{}; // Necessary for LUTs - Service ccdb; + Service ccdb{}; // these are the settings governing the TOF layers to be used // note that there are two layers foreseen for now: inner and outer TOF @@ -141,7 +132,7 @@ struct OnTheFlyTofPid { o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -150,45 +141,75 @@ struct OnTheFlyTofPid { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; OutputObj listEfficiency{"efficiency"}; - enum ParticleType : int { El = 0, // electron - Mu, // muon - Pi, // pion - Ka, // kaon - Pr, // proton - Sp, // sigma plus - Sm, // sigma minus - Xi, // xi - Om, // omega - De, // deuteron - Tr, // triton - He, // helium 3 - Al, // alpha - NParticles }; + enum ParticleId : int { El = 0, // electron + Mu, // muon + Pi, // pion + Ka, // kaon + Pr, // proton + Sp, // sigma plus + Sm, // sigma minus + Xi, // xi + Om, // omega + De, // deuteron + Tr, // triton + He, // helium 3 + Al, // alpha + NParticles }; + + std::array, NParticles> h2dInnerTimeResTrack; + std::array, NParticles> h2dInnerTimeResTotal; + std::array, NParticles> h2dOuterTimeResTrack; + std::array, NParticles> h2dOuterTimeResTotal; + std::array, NParticles>, NParticles> h2dInnerNsigmaTrue; + std::array, NParticles>, NParticles> h2dOuterNsigmaTrue; + std::array, NParticles>, NParticles> h2dInnerDeltaTrue; + std::array, NParticles>, NParticles> h2dOuterDeltaTrue; struct ParticleInfo { - const char* texName; - const char* name; - ParticleType type; - int pdgCode; - double mass; - float charge; + std::string_view texName{}; + std::string_view name{}; + ParticleId type{}; + int pdgCode{}; + double mass{}; + float charge{}; + void set(const std::string_view texName_, const std::string_view name_, ParticleId type_, int pdgCode_, double mass_, float charge_) + { + texName = texName_; + name = name_; + type = type_; + pdgCode = pdgCode_; + mass = mass_; + charge = charge_; + } }; - static constexpr ParticleInfo Particles[NParticles] = { - {"#it{e}", "Elec", El, PDG_t::kElectron, o2::constants::physics::MassElectron, 1.f}, - {"#it{#mu}", "Muon", Mu, PDG_t::kMuonMinus, o2::constants::physics::MassMuon, 1.f}, - {"#it{#pi}", "Pion", Pi, PDG_t::kPiPlus, o2::constants::physics::MassPionCharged, 1.f}, - {"#it{K}", "Kaon", Ka, PDG_t::kKPlus, o2::constants::physics::MassKaonCharged, 1.f}, - {"#it{p}", "Prot", Pr, PDG_t::kProton, o2::constants::physics::MassProton, 1.f}, - {"#it{#SigmaPlus}", "Sigp", Sp, PDG_t::kSigmaPlus, o2::constants::physics::MassSigmaPlus, 1.f}, - {"#it{#SigmaMinus}", "Sigm", Sm, PDG_t::kSigmaMinus, o2::constants::physics::MassSigmaMinus, 1.f}, - {"#it{#Xi}", "Xi", Xi, PDG_t::kXiMinus, o2::constants::physics::MassXiMinus, 1.f}, - {"#it{#Omega}", "Omeg", Om, PDG_t::kOmegaMinus, o2::constants::physics::MassOmegaMinus, 1.f}, - {"#it{d}", "Deut", De, o2::constants::physics::kDeuteron, o2::constants::physics::MassDeuteron, 1.f}, - {"#it{t}", "Trit", Tr, o2::constants::physics::kTriton, o2::constants::physics::MassTriton, 1.f}, - {"^{3}He", "He", He, o2::constants::physics::kHelium3, o2::constants::physics::MassHelium3, 2.f}, - {"#it{#alpha}", "Al", Al, o2::constants::physics::kAlpha, o2::constants::physics::MassAlpha, 2.f}, - }; + static constexpr ParticleInfo particleEl{.texName = "#it{e}", .name = "Elec", .type = El, .pdgCode = PDG_t::kElectron, .mass = o2::constants::physics::MassElectron, .charge = 1.f}; + static constexpr ParticleInfo particleMu{.texName = "#it{#mu}", .name = "Muon", .type = Mu, .pdgCode = PDG_t::kMuonMinus, .mass = o2::constants::physics::MassMuon, .charge = 1.f}; + static constexpr ParticleInfo particlePi{.texName = "#it{#pi}", .name = "Pion", .type = Pi, .pdgCode = PDG_t::kPiPlus, .mass = o2::constants::physics::MassPionCharged, .charge = 1.f}; + static constexpr ParticleInfo particleKa{.texName = "#it{K}", .name = "Kaon", .type = Ka, .pdgCode = PDG_t::kKPlus, .mass = o2::constants::physics::MassKaonCharged, .charge = 1.f}; + static constexpr ParticleInfo particlePr{.texName = "#it{p}", .name = "Prot", .type = Pr, .pdgCode = PDG_t::kProton, .mass = o2::constants::physics::MassProton, .charge = 1.f}; + static constexpr ParticleInfo particleSp{.texName = "#it{#SigmaPlus}", .name = "Sigp", .type = Sp, .pdgCode = PDG_t::kSigmaPlus, .mass = o2::constants::physics::MassSigmaPlus, .charge = 1.f}; + static constexpr ParticleInfo particleSm{.texName = "#it{#SigmaMinus}", .name = "Sigm", .type = Sm, .pdgCode = PDG_t::kSigmaMinus, .mass = o2::constants::physics::MassSigmaMinus, .charge = 1.f}; + static constexpr ParticleInfo particleXi{.texName = "#it{#Xi}", .name = "Xi", .type = Xi, .pdgCode = PDG_t::kXiMinus, .mass = o2::constants::physics::MassXiMinus, .charge = 1.f}; + static constexpr ParticleInfo particleOm{.texName = "#it{#Omega}", .name = "Omeg", .type = Om, .pdgCode = PDG_t::kOmegaMinus, .mass = o2::constants::physics::MassOmegaMinus, .charge = 1.f}; + static constexpr ParticleInfo particleDe{.texName = "#it{d}", .name = "Deut", .type = De, .pdgCode = o2::constants::physics::kDeuteron, .mass = o2::constants::physics::MassDeuteron, .charge = 1.f}; + static constexpr ParticleInfo particleTr{.texName = "#it{t}", .name = "Trit", .type = Tr, .pdgCode = o2::constants::physics::kTriton, .mass = o2::constants::physics::MassTriton, .charge = 1.f}; + static constexpr ParticleInfo particleHe{.texName = "^{3}He", .name = "He", .type = He, .pdgCode = o2::constants::physics::kHelium3, .mass = o2::constants::physics::MassHelium3, .charge = 2.f}; + static constexpr ParticleInfo particleAl{.texName = "#it{#alpha}", .name = "Al", .type = Al, .pdgCode = o2::constants::physics::kAlpha, .mass = o2::constants::physics::MassAlpha, .charge = 2.f}; + + static constexpr std::array Particles = {particleEl, + particleMu, + particlePi, + particleKa, + particlePr, + particleSp, + particleSm, + particleXi, + particleOm, + particleDe, + particleTr, + particleHe, + particleAl}; bool doQaForParticle(const int pdgCode) { @@ -216,13 +237,12 @@ struct OnTheFlyTofPid { pRandomNumberGenerator.SetSeed(0); // fully randomize if (simConfig.flagTOFLoadDelphesLUTs) { for (int icfg = 0; icfg < nGeometries; ++icfg) { - const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - mSmearer.emplace_back(std::make_unique()); + mSmearer.emplace_back(std::make_unique()); mSmearer[icfg]->setCcdbManager(ccdb.operator->()); std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); for (const auto& entry : globalConfiguration) { int pdg = 0; - if (entry.first.find("lut") != 0) { + if (!entry.first.starts_with("lut")) { continue; } if (entry.first.find("lutEl") != std::string::npos) { @@ -312,36 +332,38 @@ struct OnTheFlyTofPid { continue; } - auto addHistogram = [&](const std::string& name, const AxisSpec& axis) { + auto addHistoVsMomentum = [&](const std::string& name, const AxisSpec& axis) { return histos.add(name, "", kTH2F, {axisMomentum, axis}); }; - - const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::string("Track time resolution - ") + Particles[iTrue].texName + " (ps)"}; - h2dInnerTimeResTrack[iTrue] = addHistogram(std::string("iTOF/res/h2dInnerTimeResTrack") + Particles[iTrue].name + "VsP", axisTrackTimeRes); - h2dOuterTimeResTrack[iTrue] = addHistogram(std::string("oTOF/res/h2dOuterTimeResTrack") + Particles[iTrue].name + "VsP", axisTrackTimeRes); - const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::string("Total time resolution - ") + Particles[iTrue].texName + " (ps)"}; - h2dInnerTimeResTotal[iTrue] = addHistogram(std::string("iTOF/res/h2dInnerTimeResTotal") + Particles[iTrue].name + "VsP", axisTotalTimeRes); - h2dOuterTimeResTotal[iTrue] = addHistogram(std::string("oTOF/res/h2dOuterTimeResTotal") + Particles[iTrue].name + "VsP", axisTotalTimeRes); + const auto& pNameTrue = Particles[iTrue].name; + + const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::format("Track time resolution - {} (ps)", Particles[iTrue].texName)}; + h2dInnerTimeResTrack[iTrue] = addHistoVsMomentum(std::format("iTOF/res/h2dInnerTimeResTrack{}VsP", pNameTrue), axisTrackTimeRes); + h2dOuterTimeResTrack[iTrue] = addHistoVsMomentum(std::format("oTOF/res/h2dOuterTimeResTrack{}VsP", pNameTrue), axisTrackTimeRes); + const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::format("Total time resolution - {} (ps)", Particles[iTrue].texName)}; + h2dInnerTimeResTotal[iTrue] = addHistoVsMomentum(std::format("iTOF/res/h2dInnerTimeResTotal{}VsP", pNameTrue), axisTotalTimeRes); + h2dOuterTimeResTotal[iTrue] = addHistoVsMomentum(std::format("oTOF/res/h2dOuterTimeResTotal{}VsP", pNameTrue), axisTotalTimeRes); for (int iHyp = 0; iHyp < NParticles; iHyp++) { if (!doQaForParticle(Particles[iHyp].pdgCode)) { continue; } + const auto& pNameHypo = Particles[iHyp].texName; - std::string nameTitleInner = std::string("h2dInnerNsigmaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis"; - std::string nameTitleOuter = std::string("h2dOuterNsigmaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis"; - std::string nameTitleInnerDelta = std::string("h2dInnerDeltaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis"; - std::string nameTitleOuterDelta = std::string("h2dOuterDeltaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis"; + const std::string nameTitleInner = std::format("h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleOuter = std::format("h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleInnerDelta = std::format("h2dInnerDeltaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleOuterDelta = std::format("h2dOuterDeltaTrue{}Vs{}", pNameTrue, pNameHypo); const AxisSpec axisX{plotsConfig.doSeparationVsPt.value ? axisPt : axisMomentum}; - const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::string("N#sigma - True ") + Particles[iTrue].texName + " vs " + Particles[iHyp].texName + " hypothesis"}; - const AxisSpec axisDeltaCorrect{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::string("#Delta - True ") + Particles[iTrue].texName + " vs " + Particles[iHyp].texName + " hypothesis"}; - const AxisSpec axisNsigmaWrong{plotsConfig.nBinsNsigmaWrongSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::string("N#sigma - True ") + Particles[iTrue].texName + " vs " + Particles[iHyp].texName + " hypothesis"}; - const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::string("#Delta - True ") + Particles[iTrue].texName + " vs " + Particles[iHyp].texName + " hypothesis"}; + const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::format("N#sigma - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisDeltaCorrect{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::format("#Delta - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisNsigmaWrong{plotsConfig.nBinsNsigmaWrongSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::format("N#sigma - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::format("#Delta - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; const AxisSpec axisNSigma{iTrue == iHyp ? axisNsigmaCorrect : axisNsigmaWrong}; const AxisSpec axisDelta{iTrue == iHyp ? axisDeltaCorrect : axisDeltaWrong}; - h2dInnerNsigmaTrue[iTrue][iHyp] = histos.add(std::string("iTOF/nsigma/h2dInnerNsigmaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis", nameTitleInner.c_str(), kTH2F, {axisX, axisNSigma}); - h2dOuterNsigmaTrue[iTrue][iHyp] = histos.add(std::string("oTOF/nsigma/h2dOuterNsigmaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis", nameTitleOuter.c_str(), kTH2F, {axisX, axisNSigma}); - h2dInnerDeltaTrue[iTrue][iHyp] = histos.add(std::string("iTOF/delta/h2dInnerDeltaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis", nameTitleInnerDelta.c_str(), kTH2F, {axisX, axisDelta}); - h2dOuterDeltaTrue[iTrue][iHyp] = histos.add(std::string("oTOF/delta/h2dOuterDeltaTrue") + Particles[iTrue].name + "Vs" + Particles[iHyp].name + "Hypothesis", nameTitleOuterDelta.c_str(), kTH2F, {axisX, axisDelta}); + h2dInnerNsigmaTrue[iTrue][iHyp] = histos.add(std::format("iTOF/nsigma/h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleInner.c_str(), kTH2F, {axisX, axisNSigma}); + h2dOuterNsigmaTrue[iTrue][iHyp] = histos.add(std::format("oTOF/nsigma/h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleOuter.c_str(), kTH2F, {axisX, axisNSigma}); + h2dInnerDeltaTrue[iTrue][iHyp] = histos.add(std::format("iTOF/delta/h2dInnerDeltaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleInnerDelta.c_str(), kTH2F, {axisX, axisDelta}); + h2dOuterDeltaTrue[iTrue][iHyp] = histos.add(std::format("oTOF/delta/h2dOuterDeltaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleOuterDelta.c_str(), kTH2F, {axisX, axisDelta}); } } } @@ -349,12 +371,12 @@ struct OnTheFlyTofPid { struct TOFLayerEfficiency { private: - const float layerRadius; - const float layerLength; - const float pixelDimensionZ; - const float pixelDimensionRPhi; - const float fractionInactive; - const float magField; + float layerRadius; + float layerLength; + float pixelDimensionZ; + float pixelDimensionRPhi; + float fractionInactive; + float magField; TAxis* axisZ = nullptr; TAxis* axisRPhi = nullptr; @@ -368,11 +390,6 @@ struct OnTheFlyTofPid { public: ~TOFLayerEfficiency() { - if (0) { - hHitMap->SaveAs(Form("/tmp/%s.png", hHitMap->GetName())); - hHitMapInPixel->SaveAs(Form("/tmp/%s.png", hHitMapInPixel->GetName())); - hHitMapInPixelBefore->SaveAs(Form("/tmp/%s.png", hHitMapInPixelBefore->GetName())); - } delete axisZ; delete axisRPhi; @@ -383,27 +400,35 @@ struct OnTheFlyTofPid { delete hHitMapInPixelBefore; } - TOFLayerEfficiency(float r, float l, std::array pDimensions, float fIA, float m) - : layerRadius(r), layerLength(l), pixelDimensionZ(pDimensions[0]), pixelDimensionRPhi(pDimensions[1]), fractionInactive(fIA), magField(m) + TOFLayerEfficiency(const TOFLayerEfficiency&) = delete; + TOFLayerEfficiency& operator=(const TOFLayerEfficiency&) = delete; + + TOFLayerEfficiency(float r, float l, std::array pDimensions, float fIA, float m) : layerRadius(r), + layerLength(l), + pixelDimensionZ(pDimensions[0]), + pixelDimensionRPhi(pDimensions[1]), + fractionInactive(fIA), + magField(m), + axisZ(new TAxis(static_cast(layerLength / pixelDimensionZ), -layerLength / 2, layerLength)) { // Assuming square pixels for simplicity const float circumference = o2::constants::math::TwoPI * layerRadius; - axisZ = new TAxis(static_cast(layerLength / pixelDimensionZ), -layerLength / 2, layerLength); + axisRPhi = new TAxis(static_cast(circumference / pixelDimensionRPhi), 0.f, circumference); const float inactiveBorderRPhi = pixelDimensionRPhi * std::sqrt(fractionInactive) / 2; const float inactiveBorderZ = pixelDimensionZ * std::sqrt(fractionInactive) / 2; static constexpr int NDimBorderArray = 4; - const double arrayRPhi[NDimBorderArray] = {-pixelDimensionRPhi / 2, -pixelDimensionRPhi / 2 + inactiveBorderRPhi, pixelDimensionRPhi / 2 - inactiveBorderRPhi, pixelDimensionRPhi / 2}; + const std::array arrayRPhi = {-pixelDimensionRPhi / 2, -pixelDimensionRPhi / 2 + inactiveBorderRPhi, pixelDimensionRPhi / 2 - inactiveBorderRPhi, pixelDimensionRPhi / 2}; for (int i = 0; i < NDimBorderArray; i++) { LOG(info) << "arrayRPhi[" << i << "] = " << arrayRPhi[i]; } - axisInPixelRPhi = new TAxis(3, arrayRPhi); - const double arrayZ[NDimBorderArray] = {-pixelDimensionZ / 2, -pixelDimensionZ / 2 + inactiveBorderZ, pixelDimensionZ / 2 - inactiveBorderZ, pixelDimensionZ / 2}; + axisInPixelRPhi = new TAxis(3, arrayRPhi.data()); + const std::array arrayZ = {-pixelDimensionZ / 2, -pixelDimensionZ / 2 + inactiveBorderZ, pixelDimensionZ / 2 - inactiveBorderZ, pixelDimensionZ / 2}; for (int i = 0; i < NDimBorderArray; i++) { LOG(info) << "arrayZ[" << i << "] = " << arrayZ[i]; } - axisInPixelZ = new TAxis(3, arrayZ); + axisInPixelZ = new TAxis(3, arrayZ.data()); hHitMap = new TH2F(Form("hHitMap_R%.0f", layerRadius), "HitMap;z (cm); r#phi (cm)", 1000, -1000, 1000, 1000, -1000, 1000); hHitMapInPixel = new TH2F(Form("hHitMapInPixel_R%.0f", layerRadius), "HitMapInPixel;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); @@ -457,19 +482,27 @@ struct OnTheFlyTofPid { // LOG(warning) << "Local hit difference in z is bigger than the pixel size"; } hHitMapInPixelBefore->Fill(localZ, localRPhi); + enum PixelBin : int { kInactiveLeft = 0, + kInactiveRight = 1, + kInactiveBottom = 3, + kInactiveTop = 4 }; switch (axisInPixelRPhi->FindBin(localRPhi)) { - case 0: - case 1: - case 3: - case 4: + case kInactiveLeft: + case kInactiveRight: + case kInactiveBottom: + case kInactiveTop: return false; + default: + break; } switch (axisInPixelZ->FindBin(localZ)) { - case 0: - case 1: - case 3: - case 4: + case kInactiveLeft: + case kInactiveRight: + case kInactiveBottom: + case kInactiveTop: return false; + default: + break; } hHitMapInPixel->Fill(localZ, localRPhi); hHitMap->Fill(z, rphi); @@ -484,12 +517,12 @@ struct OnTheFlyTofPid { if (fractionInactive <= 0.f) { return true; } - float x, y, z; + float x = NAN, y = NAN, z = NAN; if (!track.getXatLabR(layerRadius, x, magField)) { LOG(debug) << "Could not propagate track to TOF layer at radius " << layerRadius << " cm"; return false; } - bool b; + bool b = false; ROOT::Math::PositionVector3D hit = track.getXYZGloAt(x, magField, b); if (!b) { LOG(debug) << "Could not get hit position at radius " << layerRadius << " cm"; @@ -541,7 +574,7 @@ struct OnTheFlyTofPid { }; std::vector tracksWithTime; - bool eventTime(std::vector& tracks, + bool eventTime(const std::vector& tracks, std::array& tzero) { @@ -550,7 +583,7 @@ struct OnTheFlyTofPid { // Todo: check the different mass hypothesis iteratively for (const auto& track : tracks) { - auto pdgInfo = pdg->GetParticle(track.mPdgCode); + const auto& pdgInfo = pdgDatabase->GetParticle(track.mPdgCode); if (pdgInfo == nullptr) { continue; } @@ -609,12 +642,12 @@ struct OnTheFlyTofPid { { // Compute tracking contribution to timing using the error propagation formula // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) - double a0 = mass * mass; - double a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); - double a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; - double dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); - double dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); - double trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); + const double a0 = 1.0 * mass * mass; + const double a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); + const double a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; + const double dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + const double dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + const double trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); return trackTimeResolution; } @@ -640,8 +673,9 @@ struct OnTheFlyTofPid { // First we compute the number of charged particles in the event if LUTs are loaded float dNdEta = 0.f; for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } auto mcParticle = track.mcParticle(); if (std::abs(mcParticle.eta()) > simConfig.multiplicityEtaRange) { continue; @@ -649,7 +683,7 @@ struct OnTheFlyTofPid { if (mcParticle.has_daughters()) { continue; } - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (!pdgInfo) { // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; continue; @@ -672,11 +706,11 @@ struct OnTheFlyTofPid { if (!track.has_mcParticle()) { // should always be OK but check please upgradeTofMC(-999.f, -999.f, -999.f, -999.f); continue; - } else { - LOG(debug) << "Track without mcParticle found!"; } + LOG(debug) << "Track without mcParticle found!"; + const auto& mcParticle = track.mcParticle(); - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); float xPv = -100.f; static constexpr float TrkXThreshold = -99.f; // Threshold to consider a good propagation of the track @@ -713,7 +747,7 @@ struct OnTheFlyTofPid { } // get mass to calculate velocity - auto pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (pdgInfo == nullptr) { LOG(error) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; upgradeTofMC(-999.f, -999.f, -999.f, -999.f); @@ -772,7 +806,7 @@ struct OnTheFlyTofPid { if (etStatus) { histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); } - static_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + dynamic_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); } // Then we do a second loop to compute the measured quantities with the measured event time @@ -792,15 +826,15 @@ struct OnTheFlyTofPid { const float measuredTimeInnerTOF = trkWithTime.mInnerTOFTime.first - tzero[0]; const float measuredTimeOuterTOF = trkWithTime.mOuterTOFTime.first - tzero[0]; const float momentum = trkWithTime.mMomentum.first; - const float pseudorapidity = trkWithTime.mPseudorapidity.first; + const float eta = trkWithTime.mPseudorapidity.first; const float noSmearingPt = trkWithTime.mNoSmearingPt; // Straight to Nsigma static std::array expectedTimeInnerTOF, expectedTimeOuterTOF; static std::array deltaTimeInnerTOF, deltaTimeOuterTOF; static std::array nSigmaInnerTOF, nSigmaOuterTOF; - float momentumHypotheses[NParticles]; // Store momentum hypothesis for each particle - auto truePdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + std::array momentumHypotheses{}; // Store momentum hypothesis for each particle + const auto& truePdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); float rigidity = momentum; // fallback to momentum if charge unknown // Use MC truth charge for rigidity calculation @@ -851,17 +885,17 @@ struct OnTheFlyTofPid { float innerTotalTimeReso = simConfig.innerTOFTimeReso; float outerTotalTimeReso = simConfig.outerTOFTimeReso; if (simConfig.flagIncludeTrackTimeRes) { - const float transverseMomentum = momentumHypotheses[ii] / std::cosh(pseudorapidity); + const float transverseMomentum = momentumHypotheses[ii] / std::cosh(eta); double ptResolution = transverseMomentum * transverseMomentum * std::sqrt(trkWithTime.mMomentum.second); - double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-pseudorapidity)))) * std::sqrt(trkWithTime.mPseudorapidity.second); + double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * std::sqrt(trkWithTime.mPseudorapidity.second); if (simConfig.flagTOFLoadDelphesLUTs) { if (mSmearer[collision.lutConfigId()]->hasTable(Particles[ii].pdgCode)) { // Only if the LUT for this particle was loaded - ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(Particles[ii].pdgCode, dNdEta, pseudorapidity, transverseMomentum); - etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(Particles[ii].pdgCode, dNdEta, pseudorapidity, transverseMomentum); + ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(Particles[ii].pdgCode, dNdEta, eta, transverseMomentum); + etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(Particles[ii].pdgCode, dNdEta, eta, transverseMomentum); } } - const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, Particles[ii].mass, simConfig.innerTOFRadius, mMagneticField); - const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, Particles[ii].mass, simConfig.outerTOFRadius, mMagneticField); + const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, eta, ptResolution, etaResolution, Particles[ii].mass, simConfig.innerTOFRadius, mMagneticField); + const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, eta, ptResolution, etaResolution, Particles[ii].mass, simConfig.outerTOFRadius, mMagneticField); innerTotalTimeReso = std::hypot(simConfig.innerTOFTimeReso, innerTrackTimeReso); outerTotalTimeReso = std::hypot(simConfig.outerTOFTimeReso, outerTrackTimeReso); @@ -876,7 +910,7 @@ struct OnTheFlyTofPid { h2dOuterTimeResTotal[ii]->Fill(momentumHypotheses[ii], outerTotalTimeReso); if (ii == Pi) { histos.fill(HIST("h2dRelativePtResolution"), transverseMomentum, 100.0 * ptResolution / transverseMomentum); - histos.fill(HIST("h2dRelativeEtaResolution"), pseudorapidity, 100.0 * etaResolution / (std::fabs(pseudorapidity) + 1e-6)); + histos.fill(HIST("h2dRelativeEtaResolution"), eta, 100.0 * etaResolution / (std::fabs(eta) + 1e-6)); } } } @@ -896,7 +930,7 @@ struct OnTheFlyTofPid { if (plotsConfig.doQAplots) { for (int ii = 0; ii < NParticles; ii++) { - if (!doQaForParticle(Particles[ii].pdgCode) || std::fabs(mcParticle.pdgCode()) != pdg->GetParticle(Particles[ii].pdgCode)->PdgCode()) { + if (!doQaForParticle(Particles[ii].pdgCode) || std::fabs(mcParticle.pdgCode()) != pdgDatabase->GetParticle(Particles[ii].pdgCode)->PdgCode()) { continue; } if (trackLengthRecoInnerTOF > 0) { diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index 1e400241f37..ca0bc59230d 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -379,7 +379,7 @@ struct OnTheFlyTracker { o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; // Configuration defined at init time o2::fastsim::GeometryContainer mGeoContainer; float mMagneticField = 0.0f; @@ -442,7 +442,7 @@ struct OnTheFlyTracker { decayer.setBField(mMagneticField); for (int igeo = 0; igeo < nGeometries; ++igeo) { const std::string histPath = "Configuration_" + std::to_string(igeo) + "/"; - mSmearer.emplace_back(std::make_unique()); + mSmearer.emplace_back(std::make_unique()); mSmearer[igeo]->setCcdbManager(ccdb.operator->()); std::map globalConfiguration = mGeoContainer.getConfiguration(igeo, "global"); if (enablePrimarySmearing) { diff --git a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx index 5230849ff22..bf7a1f1350e 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -117,9 +118,8 @@ class ToTLUT ~ToTLUT() { for (const auto& hist_ptr : mLUTHistogramFlat) { - if (hist_ptr) { - delete hist_ptr; - } + + delete hist_ptr; } } @@ -131,7 +131,7 @@ class ToTLUT LOG(warning) << "Provided filename is empty for PDG " << pdg; return false; } - if (filename.rfind("ccdb:", 0) == 0) { // Check if filename starts with "ccdb:" + if (filename.starts_with("ccdb:")) { // Check if filename starts with "ccdb:" std::string ccdbPath = filename.substr(5); // remove "ccdb:" prefix const std::string outPath = "/tmp/ToTLUTs/"; const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; @@ -150,10 +150,9 @@ class ToTLUT } testFile.close(); return load(pdg, localFilename); - } else { // File is found, proceed to load it - checkFile.close(); - return load(pdg, localFilename); - } + } // File is found, proceed to load it + checkFile.close(); + return load(pdg, localFilename); } // In case the file is already available locally TFile* f = TFile::Open(filename.c_str()); @@ -162,7 +161,7 @@ class ToTLUT return false; } - int currentPdgIdx; + int currentPdgIdx = 0; auto it = mPdgToIndexMap.find(pdg); if (it == mPdgToIndexMap.end()) { currentPdgIdx = mIndexToPdgMap.size(); @@ -201,7 +200,7 @@ class ToTLUT TH1F* histFromFile = dynamic_cast(f->Get(histName)); if (histFromFile) { - TH1F* clonedHist = static_cast(histFromFile->Clone()); + TH1F* clonedHist = dynamic_cast(histFromFile->Clone()); clonedHist->SetDirectory(nullptr); size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, pBin); @@ -288,26 +287,27 @@ class ToTLUT o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; }; -static constexpr int kNumHypothesisParticles = 9; -std::array, kNumHypothesisParticles>, kNumHypothesisParticles> h2dBarrelNsigmaTrue; -std::array, kNumHypothesisParticles> h2dHitsPerTrackVsP; -std::array, kNumHypothesisParticles> h2dToTvsPperParticle; -std::array, kNumHypothesisParticles> h2dHitsPerTrackVsPLin; -std::array, kNumHypothesisParticles> h2dToTvsPperParticleLin; - struct OnTheFlyTrackerPid { + static constexpr int kNumHypothesisParticles = 9; + std::array, kNumHypothesisParticles>, kNumHypothesisParticles> h2dBarrelNsigmaTrue; + std::array, kNumHypothesisParticles> h2dHitsPerTrackVsP; + std::array, kNumHypothesisParticles> h2dToTvsPperParticle; + std::array, kNumHypothesisParticles> h2dHitsPerTrackVsPLin; + std::array, kNumHypothesisParticles> h2dToTvsPperParticleLin; float calculateNsigma(float measuredToT, float expectedToT, float resolution) { - if (resolution <= 0) + if (resolution <= 0) { return 999.f; + } return (measuredToT - expectedToT) / resolution; } float getToTMeanFromMomentumSlice(const std::shared_ptr& hist, float momentum) { - if (!hist) + if (!hist) { return -1.f; + } int binX = hist->GetXaxis()->FindBin(momentum); TH1D* proj = hist->ProjectionY("temp", binX, binX); if (proj->GetEntries() < kMinEntriesForProjection) { @@ -321,8 +321,9 @@ struct OnTheFlyTrackerPid { float getToTResolutionFromMomentumSlice(const std::shared_ptr& hist, float momentum) { - if (!hist) + if (!hist) { return -1.f; + } int binX = hist->GetXaxis()->FindBin(momentum); TH1D* proj = hist->ProjectionY("temp", binX, binX); if (proj->GetEntries() < kMinEntriesForProjection) { @@ -336,15 +337,14 @@ struct OnTheFlyTrackerPid { float computeTrackLength(o2::track::TrackParCov track, float radius, float magneticField) { - float length = -100; o2::math_utils::CircleXYf_t trcCircle; - float sna, csa; + float sna = NAN, csa = NAN; track.getCircleParams(magneticField, trcCircle, sna, csa); const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - length = 0.0f; + float length = 0.0f; const float ux = trcCircle.xC / centerDistance; const float uy = trcCircle.yC / centerDistance; const float vx = -uy; @@ -356,15 +356,15 @@ struct OnTheFlyTrackerPid { (-centerDistance + trcCircle.rC + radius) * (centerDistance + trcCircle.rC + radius)); - const float point1[2] = {radical * ux + displace * vx, radical * uy + displace * vy}; - const float point2[2] = {radical * ux - displace * vx, radical * uy - displace * vy}; + const std::array point1 = {radical * ux + displace * vx, radical * uy + displace * vy}; + const std::array point2 = {radical * ux - displace * vx, radical * uy - displace * vy}; - std::array mom; + std::array mom{}; track.getPxPyPzGlo(mom); const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; - std::array startPoint; + std::array startPoint{}; track.getXYZGlo(startPoint); float cosAngle = -1000, modulus = -1000; @@ -379,15 +379,16 @@ struct OnTheFlyTrackerPid { cosAngle /= modulus; length = trcCircle.rC * std::acos(cosAngle); length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); + return length; } - return length; + return -100.f; } Produces tableUpgradeTrkPidSignals; Produces tableUpgradeTrkPids; - Service pdg; - Service ccdb; + Service pdgDatabase{}; + Service ccdb{}; std::unique_ptr mToTLUT; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -525,24 +526,25 @@ struct OnTheFlyTrackerPid { for (size_t iTrue = 0; iTrue < particleInfo.size(); ++iTrue) { std::string trueName = particleInfo[iTrue].second; std::string trueNamePretty = trueName; // Fallback - if (trueName == "Elec") + if (trueName == "Elec") { trueNamePretty = "#it{e}"; - else if (trueName == "Muon") + } else if (trueName == "Muon") { trueNamePretty = "#it{#mu}"; - else if (trueName == "Pion") + } else if (trueName == "Pion") { trueNamePretty = "#it{#pi}"; - else if (trueName == "Kaon") + } else if (trueName == "Kaon") { trueNamePretty = "#it{K}"; - else if (trueName == "Prot") + } else if (trueName == "Prot") { trueNamePretty = "#it{p}"; - else if (trueName == "Deut") + } else if (trueName == "Deut") { trueNamePretty = "#it{d}"; - else if (trueName == "Trit") + } else if (trueName == "Trit") { trueNamePretty = "#it{t}"; - else if (trueName == "He3") + } else if (trueName == "He3") { trueNamePretty = "#it{^{3}He}"; - else if (trueName == "Al") + } else if (trueName == "Al") { trueNamePretty = "#it{^{4}He}"; + } if (enableLogPBins.value) { const AxisSpec axisMomentumLog{mLogBins, "#it{p/z} (GeV/#it{c})"}; @@ -558,27 +560,28 @@ struct OnTheFlyTrackerPid { for (size_t iHyp = 0; iHyp < particleInfo.size(); ++iHyp) { std::string hypName = particleInfo[iHyp].second; std::string hypNamePretty = hypName; // Fallback - if (hypName == "Elec") + if (hypName == "Elec") { hypNamePretty = "#it{e}"; - else if (hypName == "Muon") + } else if (hypName == "Muon") { hypNamePretty = "#it{#mu}"; - else if (hypName == "Pion") + } else if (hypName == "Pion") { hypNamePretty = "#it{#pi}"; - else if (hypName == "Kaon") + } else if (hypName == "Kaon") { hypNamePretty = "#it{K}"; - else if (hypName == "Prot") + } else if (hypName == "Prot") { hypNamePretty = "#it{p}"; - else if (hypName == "Deut") + } else if (hypName == "Deut") { hypNamePretty = "#it{d}"; - else if (hypName == "Trit") + } else if (hypName == "Trit") { hypNamePretty = "#it{t}"; - else if (hypName == "He3") + } else if (hypName == "He3") { hypNamePretty = "#it{^{3}He}"; - else if (hypName == "Al") + } else if (hypName == "Al") { hypNamePretty = "#it{^{4}He}"; + } - std::string histName = "NSigma/BarrelNsigmaTrue" + trueName + "Vs" + hypName + "Hypothesis"; - std::string histTitle = "Nsigma (True " + trueNamePretty + " vs Hyp " + hypNamePretty + "); #it{p/z} (GeV/#it{c}); N#sigma"; + const std::string histName = std::format("NSigma/BarrelNsigmaTrue{}Vs{}Hypothesis", trueName, hypName); + const std::string histTitle = std::format("Nsigma (True {} vs Hyp {}); #it{{p/z}} (GeV/#it{{c}}); N#sigma", trueNamePretty, hypNamePretty); h2dBarrelNsigmaTrue[iTrue][iHyp] = histos.add(histName.c_str(), histTitle.c_str(), kTH2F, {axisMomentumLog, axisNsigma}); } } @@ -586,12 +589,12 @@ struct OnTheFlyTrackerPid { if (enableLinearPBins.value) { const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; - std::string hitsVsPLinName = "HitsPerTrackLin/hHitsPerTrackVsPLin_" + trueName; - std::string hitsVsPLinTitle = "N_hits vs #it{p/z} for " + trueNamePretty + " (linear); #it{p/z} (GeV/#it{c}); N_hits"; + const std::string hitsVsPLinName = std::format("HitsPerTrackLin/hHitsPerTrackVsPLin_{}", trueName); + const std::string hitsVsPLinTitle = std::format("N_hits vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); N_hits", trueNamePretty); h2dHitsPerTrackVsPLin[iTrue] = histos.add(hitsVsPLinName.c_str(), hitsVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisHitsPerTrack}); - std::string totVsPLinName = "ToTvsPLin/hToTvsPLin_" + trueName; - std::string totVsPLinTitle = "ToT vs #it{p/z} for " + trueNamePretty + " (linear); #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)"; + const std::string totVsPLinName = std::format("ToTvsPLin/hToTvsPLin_{}", trueName); + const std::string totVsPLinTitle = std::format("ToT vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); ToT (#mus/10#mum)", trueNamePretty); h2dToTvsPperParticleLin[iTrue] = histos.add(totVsPLinName.c_str(), totVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisToT}); } } @@ -613,7 +616,7 @@ struct OnTheFlyTrackerPid { for (const auto& track : tracks) { float truncatedMeanToT = -1.0f; - std::array nSigmaValues; + std::array nSigmaValues{}; nSigmaValues.fill(999.f); if (!track.has_mcParticle()) { @@ -625,7 +628,7 @@ struct OnTheFlyTrackerPid { const auto& mcParticle = track.mcParticle(); - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (!pdgInfo) { tableUpgradeTrkPidSignals(truncatedMeanToT); tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], @@ -658,7 +661,7 @@ struct OnTheFlyTrackerPid { uint16_t hitMap = 0; int nHitLayers = 0; - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); float xPv = -100.f; static constexpr float kTrkXThreshold = -99.f; @@ -690,7 +693,7 @@ struct OnTheFlyTrackerPid { std::vector validToTs; for (int layer = kMinLayerForTruncation; layer < maxBarrelLayers.value; ++layer) { - if ((hitMap >> layer) & 0x1) { + if (((hitMap >> layer) & 0x1) != 0) { TH1F* totHist = mToTLUT->getHistogramForSampling(truePdgIdx, layer, binnedEta, binnedP); if (totHist && totHist->GetEntries() > 1) { @@ -704,14 +707,15 @@ struct OnTheFlyTrackerPid { const size_t nValid = validToTs.size(); size_t nUse = 0; - if (nValid == kMidLowValidHits || nValid == kMidHighValidHits) + if (nValid == kMidLowValidHits || nValid == kMidHighValidHits) { nUse = kMaxValidHitsForTruncation34; - else if (nValid == kMinValidHits || nValid == kLowValidHits) + } else if (nValid == kMinValidHits || nValid == kLowValidHits) { nUse = kMaxValidHitsForTruncation12; - else if (nValid == kHighValidHits1 || nValid == kHighValidHits2) + } else if (nValid == kHighValidHits1 || nValid == kHighValidHits2) { nUse = kMaxValidHitsForTruncation56; - else if (nValid >= kMaxValidHits) + } else if (nValid >= kMaxValidHits) { nUse = kMaxValidHitsForTruncation7Plus; + } if (nUse > 0 && nValid >= nUse) { std::sort(validToTs.begin(), validToTs.end()); From 93fcf658acbab517e5121202b4fc324c7c235171 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Mon, 21 Sep 2026 16:59:34 +0200 Subject: [PATCH 015/648] [PWGHF] Add cpaJPsi and small fixes in B2JPsi code (#17987) Co-authored-by: ALICE Action Bot --- .../candidateCreatorBToJpsiReduced.cxx | 26 ++++++++++++++++--- .../dataCreatorJpsiHadReduced.cxx | 14 ++++------ PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx | 21 +++++++-------- PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx | 2 ++ PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx | 2 ++ .../DataModel/CandidateReconstructionTables.h | 14 ++++++++++ 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx index ba5527cfcab..25430a1ce18 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include // std::move using namespace o2; using namespace o2::aod; @@ -176,10 +178,26 @@ struct HfCandidateCreatorBToJpsiReduced { } for (const auto& candJpsi : candsJpsiThisColl) { - o2::track::TrackParametrizationWithError trackPosParCov( - candJpsi.xDauPos(), candJpsi.alphaDauPos(), {candJpsi.yDauPos(), candJpsi.zDauPos(), candJpsi.snpDauPos(), candJpsi.tglDauPos(), candJpsi.signed1PtDauPos()}, 1 /*Charge*/, 1 /*Muon*/); - o2::track::TrackParametrizationWithError trackNegParCov( - candJpsi.xDauNeg(), candJpsi.alphaDauNeg(), {candJpsi.yDauNeg(), candJpsi.zDauNeg(), candJpsi.snpDauNeg(), candJpsi.tglDauNeg(), candJpsi.signed1PtDauNeg()}, -1 /*Charge*/, 1 /*Muon*/); + + std::array parsTrackPos = {candJpsi.yDauPos(), candJpsi.zDauPos(), candJpsi.snpDauPos(), candJpsi.tglDauPos(), candJpsi.signed1PtDauPos()}; + std::array parsTrackNeg = {candJpsi.yDauNeg(), candJpsi.zDauNeg(), candJpsi.snpDauNeg(), candJpsi.tglDauNeg(), candJpsi.signed1PtDauNeg()}; + + std::array covTrackPos = {candJpsi.cYYDauPos(), candJpsi.cZYDauPos(), candJpsi.cZZDauPos(), + candJpsi.cSnpYDauPos(), candJpsi.cSnpZDauPos(), + candJpsi.cSnpSnpDauPos(), candJpsi.cTglYDauPos(), candJpsi.cTglZDauPos(), + candJpsi.cTglSnpDauPos(), candJpsi.cTglTglDauPos(), + candJpsi.c1PtYDauPos(), candJpsi.c1PtZDauPos(), candJpsi.c1PtSnpDauPos(), + candJpsi.c1PtTglDauPos(), candJpsi.c1Pt21Pt2DauPos()}; + + std::array covTrackNeg = {candJpsi.cYYDauNeg(), candJpsi.cZYDauNeg(), candJpsi.cZZDauNeg(), + candJpsi.cSnpYDauNeg(), candJpsi.cSnpZDauNeg(), + candJpsi.cSnpSnpDauNeg(), candJpsi.cTglYDauNeg(), candJpsi.cTglZDauNeg(), + candJpsi.cTglSnpDauNeg(), candJpsi.cTglTglDauNeg(), + candJpsi.c1PtYDauNeg(), candJpsi.c1PtZDauNeg(), candJpsi.c1PtSnpDauNeg(), + candJpsi.c1PtTglDauNeg(), candJpsi.c1Pt21Pt2DauNeg()}; + + o2::track::TrackParametrizationWithError trackPosParCov(candJpsi.xDauPos(), candJpsi.alphaDauPos(), std::move(parsTrackPos), std::move(covTrackPos)); + o2::track::TrackParametrizationWithError trackNegParCov(candJpsi.xDauNeg(), candJpsi.alphaDauNeg(), std::move(parsTrackNeg), std::move(covTrackNeg)); // --------------------------------- // reconstruct J/Psi candidate diff --git a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx index f4cfcaaa40c..0bdc38965c7 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx @@ -60,7 +60,6 @@ #include #include #include -#include #include #include @@ -106,13 +105,13 @@ enum WrongCollisionType : uint8_t { SplitCollision, }; -std::map partlyRecoDecayMapMuMu = { +std::map const partlyRecoDecayMapMuMu = { {Pdg::kB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::B0ToJpsiXToMuMuX}, {Pdg::kBPlus, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BplusToJpsiXToMuMuX}, {Pdg::kBS, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BsToJpsiXToMuMuX}, {Pdg::kLambdaB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::LbToJpsiXToMuMuX}}; -std::map partlyRecoDecayMapEE = { +std::map const partlyRecoDecayMapEE = { {Pdg::kB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::B0ToJpsiXToEEX}, {Pdg::kBPlus, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BplusToJpsiXToEEX}, {Pdg::kBS, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BsToJpsiXToEEX}, @@ -595,7 +594,7 @@ struct HfDataCreatorJpsiHadReduced { // check that the other prongs come from the same b-hadron int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index2Mother == indexRecB) { - flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu[std::abs(bHadronMotherHypo)] : signB * partlyRecoDecayMapEE[std::abs(bHadronMotherHypo)]; + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); break; } } @@ -695,7 +694,7 @@ struct HfDataCreatorJpsiHadReduced { int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); int const index3Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[3].mcParticle(), bHadronMotherHypo, true); if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index3Mother > -1 && index2Mother == indexRecB && index3Mother == indexRecB) { - flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu[std::abs(bHadronMotherHypo)] : signB * partlyRecoDecayMapEE[std::abs(bHadronMotherHypo)]; + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); break; } } @@ -796,7 +795,7 @@ struct HfDataCreatorJpsiHadReduced { int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); int const index3Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[3].mcParticle(), bHadronMotherHypo, true); if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index3Mother > -1 && index2Mother == indexRecB && index3Mother == indexRecB) { - flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu[std::abs(bHadronMotherHypo)] : signB * partlyRecoDecayMapEE[std::abs(bHadronMotherHypo)]; + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); break; } } @@ -1115,7 +1114,6 @@ struct HfDataCreatorJpsiHadReduced { } registry.fill(HIST("hFitCandidatesBPlus"), SVFitting::FitOk); - o2::track::TrackParCov trackParCovBPlus{}; std::array pVecBPlus{}, pVec0{}, pVec1{}, pVec2{}; auto secondaryVertexBPlus = df3.getPCACandidate(); @@ -1124,8 +1122,6 @@ struct HfDataCreatorJpsiHadReduced { df3.getTrack(2).getPxPyPzGlo(pVec2); pVecBPlus = RecoDecay::pVec(pVec0, pVec1, pVec2); pVecJpsi = RecoDecay::pVec(pVec0, pVec1); - trackParCovBPlus = df3.createParentTrackParCov(); - trackParCovBPlus.setAbsCharge(0); // to be sure if (!isBSelected(pVecBPlus, secondaryVertexBPlus, collision)) { continue; diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx index d4d5ccf7d1c..fdcf6be8b80 100644 --- a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -91,7 +91,7 @@ DECLARE_SOA_COLUMN(TpcChi2NClLfTrack1, tpcChi2NClLfTrack1, float); DECLARE_SOA_COLUMN(AbsEtaLfTrack1, absEtaLfTrack1, float); //! |eta| DECLARE_SOA_COLUMN(MJpsi, mJpsi, float); //! Invariant mass of Jpsi daughter candidates (GeV/c) DECLARE_SOA_COLUMN(MK0Star, mK0Star, float); //! Invariant mass of K*0 daughter candidates (GeV/c) -DECLARE_SOA_COLUMN(M, m, float); //! Invariant mass of candidate (GeV/c2) +DECLARE_SOA_COLUMN(M, m, float); //! Invariant mass of candidate particle (GeV/c2) DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) DECLARE_SOA_COLUMN(PtGen, ptGen, float); //! Transverse momentum of candidate (GeV/c) DECLARE_SOA_COLUMN(P, p, float); //! Momentum of candidate (GeV/c) @@ -127,7 +127,7 @@ DECLARE_SOA_COLUMN(DecayLength, decayLength, float); DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate -DECLARE_SOA_COLUMN(CtXY, ctXY, float); //! Pseudo-proper decay length of candidate +DECLARE_SOA_COLUMN(CtXY, ctXY, float); //! Pseudo-proper decay length of candidate particle DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of B daughters DECLARE_SOA_COLUMN(ImpactParameterProductJpsi, impactParameterProductJpsi, float); //! Impact parameter product of Jpsi daughters DECLARE_SOA_COLUMN(ImpactParameterProductK0Star, impactParameterProductK0Star, float); //! Impact parameter product of K*0 daughters @@ -154,6 +154,7 @@ DECLARE_SOA_TABLE(HfRedCandB0Lites, "AOD", "HFREDCANDB0LITE", //! Table with som hf_cand_b0tojpsik0star_lite::Y, hf_cand_b0tojpsik0star_lite::Cpa, hf_cand_b0tojpsik0star_lite::CpaXY, + hf_cand_b0tojpsik0star_lite::CpaJpsi, hf_cand::Chi2PCA, hf_cand_b0tojpsik0star_lite::DecayLength, hf_cand_b0tojpsik0star_lite::DecayLengthXY, @@ -565,37 +566,35 @@ struct HfTaskB0ToJpsiK0StarReduced { } auto fillTable = [&](bool isSelKPi) { - auto ctXY = isSelKPi ? candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}) - : candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); - auto mlScoreSig = isSelKPi ? mlScoreSigKPi : mlScoreSigPiK; - auto invMassB0 = isSelKPi ? invMassB0KPi : invMassB0PiK; - auto invMassK0Star = isSelKPi ? invMassK0StarKPi : invMassK0StarPiK; + auto ctXY = candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + auto ctXYBar = candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); hfRedCandB0Lite( // B0 - meson features - invMassB0, + isSelKPi ? invMassB0KPi : invMassB0PiK, ptCandB0, candidate.eta(), candidate.phi(), HfHelper::yB0(candidate), candidate.cpa(), candidate.cpaXY(), + candidate.cpaJpsi(), candidate.chi2PCA(), candidate.decayLength(), candidate.decayLengthXY(), candidate.decayLengthNormalised(), candidate.decayLengthXYNormalised(), - ctXY, + isSelKPi ? ctXY : ctXYBar, candidate.impactParameterProduct(), candidate.impactParameterProductJpsi(), candidate.impactParameterProductK0Star(), candidate.maxNormalisedDeltaIP(), - mlScoreSig, + isSelKPi ? mlScoreSigKPi : mlScoreSigPiK, isSelKPi ? statusB0KPi : -1, isSelKPi ? -1 : statusB0PiK, // J/Psi features invMassJpsi, ptJpsi, - invMassK0Star, + isSelKPi ? invMassK0StarKPi : invMassK0StarPiK, candidate.impactParameter0(), candidate.impactParameter1(), candidate.impactParameter2(), diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index 8207b3dc36a..bdb9a91b64f 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -134,6 +134,7 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplustojpsik_lite::Y, hf_cand_bplustojpsik_lite::Cpa, hf_cand_bplustojpsik_lite::CpaXY, + hf_cand_bplustojpsik_lite::CpaJpsi, hf_cand::Chi2PCA, hf_cand_bplustojpsik_lite::DecayLength, hf_cand_bplustojpsik_lite::DecayLengthXY, @@ -450,6 +451,7 @@ struct HfTaskBplusToJpsiKReduced { HfHelper::yBplus(candidate), candidate.cpa(), candidate.cpaXY(), + candidate.cpaJpsi(), candidate.chi2PCA(), candidate.decayLength(), candidate.decayLengthXY(), diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index 9a374a53e47..4853f6c6035 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -146,6 +146,7 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bstojpsiphi_lite::Y, hf_cand_bstojpsiphi_lite::Cpa, hf_cand_bstojpsiphi_lite::CpaXY, + hf_cand_bstojpsiphi_lite::CpaJpsi, hf_cand::Chi2PCA, hf_cand_bstojpsiphi_lite::DecayLength, hf_cand_bstojpsiphi_lite::DecayLengthXY, @@ -486,6 +487,7 @@ struct HfTaskBsToJpsiPhiReduced { HfHelper::yBs(candidate), candidate.cpa(), candidate.cpaXY(), + candidate.cpaJpsi(), candidate.chi2PCA(), candidate.decayLength(), candidate.decayLengthXY(), diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index 37147a62937..c5c3eea19f1 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -589,6 +589,10 @@ DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, // Im [](float px0, float py0, float pz0, float px1, float py1, float pz1, float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float impParK) -> float { return impParK * RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{px0, py0, pz0}, std::array{px1, py1, pz1})); }); DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProductJpsi, impactParameterProductJpsi, // J/Psi impact parameter for B+ -> J/Psi K [](float dcaDauPos, float dcaDauNeg) -> float { return dcaDauPos * dcaDauNeg; }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); enum DecayTypeMc : uint8_t { BplusToD0PiToKPiPi = 0, BplusToD0KToKPiK, @@ -900,6 +904,8 @@ DECLARE_SOA_TABLE(HfCandBpJPBase, "AOD", "HFCANDBPJPBASE", hf_cand::PVector, hf_cand::Cpa, hf_cand::CpaXY, + hf_cand_bplus::CpaJpsi, + hf_cand_bplus::CpaJpsiXY, hf_cand::Ct, hf_cand::ImpactParameterXY, hf_cand_3prong::MaxNormalisedDeltaIP, @@ -1944,6 +1950,10 @@ DECLARE_SOA_DYNAMIC_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, //! [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float errDlxy, float pxM, float pyM, float ip0, float errIp0, float ip1, float errIp1, float ip2, float errIp2, float ip3, float errIp3, float px0, float py0, float px1, float py1, float px2, float py2, float px3, float py3) -> float { return RecoDecay::maxNormalisedDeltaIP(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, errDlxy, std::array{pxM, pyM}, std::array{ip0, ip1, ip2, ip3}, std::array{errIp0, errIp1, errIp2, errIp3}, std::array{std::array{px0, py0}, std::array{px1, py1}, std::array{px2, py2}, std::array{px3, py3}}); }); DECLARE_SOA_DYNAMIC_COLUMN(CtXY, ctXY, //! [](float px0, float py0, float pz0, float px1, float py1, float pz1, float px2, float py2, float pz2, float px3, float py3, float pz3, float xVtxP, float yVtxP, float xVtxS, float yVtxS, const std::array& m) -> float { return RecoDecay::ctXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{std::array{px0, py0, pz0}, std::array{px1, py1, pz1}, std::array{px2, py2, pz2}, std::array{px3, py3, pz3}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); } // namespace hf_cand_4prong // declare dedicated B0 -> J/Psi K*0 decay candidate table @@ -1982,6 +1992,8 @@ DECLARE_SOA_TABLE(HfCandB0JPBase, "AOD", "HFCANDB0JPBASE", hf_cand::PVector, hf_cand::Cpa, hf_cand::CpaXY, + hf_cand_4prong::CpaJpsi, + hf_cand_4prong::CpaJpsiXY, hf_cand::Ct, hf_cand::ImpactParameterXY, hf_cand_4prong::MaxNormalisedDeltaIP, @@ -2037,6 +2049,8 @@ DECLARE_SOA_TABLE(HfCandBsJPBase, "AOD", "HFCANDBSJPBASE", hf_cand::PVector, hf_cand::Cpa, hf_cand::CpaXY, + hf_cand_4prong::CpaJpsi, + hf_cand_4prong::CpaJpsiXY, hf_cand::Ct, hf_cand::ImpactParameterXY, hf_cand_4prong::MaxNormalisedDeltaIP, From 8135f7c48edd8d964a4a70112fb6b40e485fc5dd Mon Sep 17 00:00:00 2001 From: abmodak <67369858+abmodak@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:07:57 +0200 Subject: [PATCH 016/648] [PWGCF] Remove extra event selection condition (#17994) Co-authored-by: ALICE Action Bot --- .../TableProducer/longrangeMaker.cxx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx index b79806b4d63..b49e5a6f554 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx @@ -119,7 +119,6 @@ struct LongrangeMaker { Configurable isApplyGoodITSLayersAll{"isApplyGoodITSLayersAll", false, "Enable GoodITSLayersAll cut"}; Configurable isApplyExtraCorrCut{"isApplyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; Configurable isApplyBestCollIndex{"isApplyBestCollIndex", true, "bestCollIndex"}; - Configurable isrejectFlangeEvent{"isrejectFlangeEvent", false, "At least one channel with -350 TDC < time < -450 TDC"}; Configurable isApplyNoCollInTimeRangeStandard{"isApplyNoCollInTimeRangeStandard", false, "Enable NoCollInTimeRangeStandard cut"}; Configurable isApplyNoCollInTimeRangeStrict{"isApplyNoCollInTimeRangeStrict", false, "Enable NoCollInTimeRangeStrict cut"}; Configurable isApplyNoCollInRofStandard{"isApplyNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; @@ -301,7 +300,7 @@ struct LongrangeMaker { x->SetBinLabel(13, "ApplyNoHighMultCollInPrevRof"); x->SetBinLabel(14, "ApplyOccupancySelection"); x->SetBinLabel(15, "ZvertexSelection"); - x->SetBinLabel(16, "reject flange event"); + histos.add("hSelectionResult", "hSelectionResult", kTH1I, {{5, -0.5, 4.5}}); histos.add("hMftTrkSel", "hMftTrkSel", kTH1D, {axisTrackSel}, false); @@ -476,14 +475,6 @@ struct LongrangeMaker { // ft0 loop if (col.has_foundFT0()) { const auto& ft0 = col.foundFT0(); - if (cfgevtsel.isrejectFlangeEvent) { - constexpr int IsFlangeEventId = 7; - std::bitset<8> ft0TriggerMask = ft0.triggerMask(); - if (ft0TriggerMask[IsFlangeEventId]) { - return; - } - } - histos.fill(HIST("EventHist"), 16); for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { auto chanelid = ft0.channelA()[iCh]; float ampl = ft0.amplitudeA()[iCh]; From 320e28842ebc58060d8db0c65cb54b871616856a Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:46:37 +0200 Subject: [PATCH 017/648] [PWGLF] Add centrality in gen. Vs recoed histograms in derivedlambdakzeroanalysis (#18000) --- PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 7a273765440..9638d3954f6 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -1205,6 +1205,8 @@ struct derivedlambdakzeroanalysis { if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { histos.add("GeneralQA/h2dRapVsRapGen", "h2dRapVsRapGen;Rapidity;Generated rapidity", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisRapidity}); histos.add("GeneralQA/h2dPtVsPtGen", "h2dPtVsPtGen;#it{p}_{T} (GeV/#it{c});#it{p}_{T}^{MC} (GeV/#it{c})", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPt}); + histos.add("GeneralQA/h3dRapVsRapGen", "h3dRapVsRapGen;Centrality (%);Rapidity;Generated rapidity", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisRapidity, axisConfigurations.axisRapidity}); + histos.add("GeneralQA/h3dPtVsPtGen", "h3dPtVsPtGen;Centrality (%);#it{p}_{T} (GeV/#it{c});#it{p}_{T}^{MC} (GeV/#it{c})", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPt}); } // Creation of histograms: MC generated @@ -2064,6 +2066,8 @@ struct derivedlambdakzeroanalysis { if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yK0Short(), rapidityK0Short); histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yK0Short(), rapidityK0Short); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); } nK0Shorts++; } @@ -2154,6 +2158,8 @@ struct derivedlambdakzeroanalysis { if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yLambda(), rapidityLambda); histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); } nLambdas++; } @@ -2244,6 +2250,8 @@ struct derivedlambdakzeroanalysis { if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yLambda(), rapidityLambda); histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); } nAntiLambdas++; } From 1d65360154044ed8f91be9cda3b34c200df1aaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 21 Sep 2026 18:58:17 +0200 Subject: [PATCH 018/648] [PWGLF] h-d corr. task add deltaY plots, extend axis range (#18001) --- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 86 +++++++++++++------ 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 87b437c192b..6eea1a825d2 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ #include #include +#include #include #include #include @@ -146,6 +148,12 @@ struct HadronNucleiCorrelation { ConfigurableAxis axisNSigma{"axisNSigma", {35, -7.f, 7.f}, "n#sigma"}; ConfigurableAxis deltaPhiAxis = {"deltaPhiAxis", {46, -1 * o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}, "#Delta#phi (rad)"}; + struct : ConfigurableGroup { + std::string prefix = "axes"; // JSON group name + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {300, -1.5, 1.5}, "#Delta#eta"}; + ConfigurableAxis axisDeltaRap{"axisDeltaRap", {300, -1.5, 1.5}, "#Delta y"}; + } settingsAxes; + using FilteredCollisions = soa::Filtered; using FilteredCollisionsExtra = soa::Filtered>; using SimCollisions = soa::Filtered; @@ -165,10 +173,10 @@ struct HadronNucleiCorrelation { // std::unique_ptr> PairMC = std::make_unique>(); // Data histograms - std::vector> hEtaPhiSameEv; - std::vector> hEtaPhiMixdEv; - std::vector> hCorrEtaPhiSameEv; - std::vector> hCorrEtaPhiMixdEv; + std::vector> hDeltaPhiSameEv; + std::vector> hDeltaPhiMixdEv; + std::vector> hCorrDeltaPhiSameEv; + std::vector> hCorrDeltaPhiMixdEv; int nBinspT = 0; TH2* hEffPtEtaProton = nullptr; @@ -185,9 +193,32 @@ struct HadronNucleiCorrelation { // compact candidate vectors instead of re-reading the full MC particle table for every pair struct GenCandidate { float ptVal, etaVal, phiVal; - float pt() const { return ptVal; } - float eta() const { return etaVal; } - float phi() const { return phiVal; } + [[nodiscard]] float pt() const { return ptVal; } + [[nodiscard]] float eta() const { return etaVal; } + [[nodiscard]] float phi() const { return phiVal; } + [[nodiscard]] float pz() const { return ptVal * std::sinh(etaVal); } + [[nodiscard]] float p() const { return std::hypot(ptVal, pz()); } + [[nodiscard]] float energyForMass(const float mass) const { return std::hypot(p(), mass); } + [[nodiscard]] float rapidityForMass(const float mass) const + { + const float e = energyForMass(mass); + return 0.5f * std::log((e + pz()) / (e - pz())); + }; + [[nodiscard]] float mass(const int pdg) const + { + switch (pdg) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + return o2::track::PID::getMass(o2::track::PID::Deuteron); + case PDG_t::kProton: + case -PDG_t::kProton: + return o2::track::PID::getMass(o2::track::PID::Proton); + default: + LOG(fatal) << "Unhandled pdg " << pdg; + return 0.f; + } + } + [[nodiscard]] float rapidityForPdg(const int pdg) const { return rapidityForMass(mass(pdg)); } }; // Per-collision information needed for generated-level same- and mixed-event pairing @@ -223,8 +254,8 @@ struct HadronNucleiCorrelation { const AxisSpec ptAxis = {200, -10.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; const AxisSpec ptAxisSmall = {100, -5.f, 5.f, "#it{p}_{T} GeV/#it{c}"}; - const AxisSpec deltaEtaAxis = {300, -1.5, 1.5, "#Delta#eta"}; - const AxisSpec deltaRapAxis = {300, -1.5, 1.5, "#Delta y"}; + const AxisSpec deltaEtaAxis = {settingsAxes.axisDeltaEta, "#Delta#eta"}; + const AxisSpec deltaRapAxis = {settingsAxes.axisDeltaRap, "#Delta y"}; if (doprocessSameEvent || doprocessSameEventEvSel) { registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{7, 0.f, 7.f}}}); @@ -288,17 +319,17 @@ struct HadronNucleiCorrelation { const TString ptTag = Form("pt%02.0f%02.0f", pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10); const TString ptInterval = Form("(%.1f(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); - hEtaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiSameEv.push_back(registry.add(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); - hCorrEtaPhiSameEv.push_back(registry.add(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); - hCorrEtaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiSameEv.push_back(registry.add(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); } else { - hEtaPhiSameEv.push_back(registry.add(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); - hEtaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiSameEv.push_back(registry.add(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); - hCorrEtaPhiSameEv.push_back(registry.add(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); - hCorrEtaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiSameEv.push_back(registry.add(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); } } } @@ -640,14 +671,14 @@ struct HadronNucleiCorrelation { } if (ME) { - hEtaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); if (corr0 != 0 && corr1 != 0) { - hCorrEtaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hCorrDeltaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); } } else { - hEtaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); if (corr0 != 0 && corr1 != 0) { - hCorrEtaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hCorrDeltaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); } } // SE } // pT condition @@ -663,17 +694,16 @@ struct HadronNucleiCorrelation { float deltaEta = part0.eta() - part1.eta(); float deltaPhi = part0.phi() - part1.phi(); deltaPhi = RecoDecay::constrainAngle(deltaPhi, -1 * o2::constants::math::PIHalf); - + // Here we have to use doRapidity + const float deltaRapidity = part0.rapidityForPdg(pdgPart0) - part1.rapidityForPdg(pdgPart1); for (int k = 0; k < nBinspT; k++) { - if (part0.pt() >= pTBins.value.at(k) && part0.pt() < pTBins.value.at(k + 1)) { - if (ME) { - hEtaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiMixdEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); + hCorrDeltaPhiMixdEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); } else { - hEtaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiSameEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); + hCorrDeltaPhiSameEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); } // SE } // pT condition } // nBinspT loop From 96803830b43c5d9a9eeba6a36757e77cca333c2f Mon Sep 17 00:00:00 2001 From: lgansbartl Date: Mon, 21 Sep 2026 19:32:32 +0200 Subject: [PATCH 019/648] [PWGEM] add option for at least one photon in Dalitz Skimmer (#17996) Co-authored-by: Laura Gansbartl --- .../TableProducer/skimmerPrimaryElectronFromDalitzEE.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h index c1102e8f112..ed671564446 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -166,6 +166,7 @@ struct skimmerPrimaryElectronFromDalitzEE { Configurable maxMee{"maxMee", 0.5, "max. mee to store dalitz ee pairs"}; Configurable minMeegamma{"minMeegamma", 0.3, "min. mee to store eegamma candidates"}; Configurable maxMeegamma{"maxMeegamma", 0.8, "max. mee to store eegamma candidates"}; + Configurable photonsNeeded{"photonsNeeded", true, "require a minimum of one photons per collision"}; Configurable fillLS{"fillLS", true, "flag to fill LS histograms for QA"}; Configurable fillingMode{"fillingMode", 1, "Filling mode| 1: fill tracks without pair selection, 2: fill tracks from selected pairs, 3: fill tracks from selected pairs that can be combined with a photon"}; Configurable fillWithEtaMassCut{"fillWithEtaMassCut", true, "only valid for fillingmode 3; true: filling tabled based on eta candidate selection with minMeegamma < M < maxMeegamma, false: fill identical to fillingmode 2"}; @@ -745,6 +746,10 @@ struct skimmerPrimaryElectronFromDalitzEE { auto varFillingMode = static_cast(fillingMode.value); const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + if (photonsNeeded && v0photons_per_coll.size() == 0) { + continue; + } + const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); @@ -927,6 +932,10 @@ struct skimmerPrimaryElectronFromDalitzEE { auto varFillingMode = static_cast(fillingMode.value); const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + if (photonsNeeded && v0photons_per_coll.size() == 0) { + continue; + } + const auto& posTracks_per_coll = posTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); const auto& negTracks_per_coll = negTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); From 519f04658d6d659c1588e9067f154dfa6e3e7045 Mon Sep 17 00:00:00 2001 From: Lorenzo Bernardinis <95907752+lorber98@users.noreply.github.com> Date: Mon, 21 Sep 2026 20:07:19 +0200 Subject: [PATCH 020/648] [PWGLF] Add selections and histograms for cascades (#17997) --- .../Strangeness/strangenessInJetsIons.cxx | 377 ++++++++++++++++-- 1 file changed, 340 insertions(+), 37 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx index 0226cd533c7..4542d3ed6ce 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx @@ -214,17 +214,21 @@ struct StrangenessInJetsIons { // Cascade analysis parameters struct : ConfigurableGroup { // std::string prefix = "configCasc"; // name in JSON - Configurable minimumCascRadius{"minimumCascRadius", 0.1f, "Minimum cascade radius"}; + Configurable minimumV0RadiusCasc{"minimumV0RadiusCasc", 1.5f, "Minimum V0 radius (cm) for cascade analysis"}; + Configurable minimumCascRadius{"minimumCascRadius", 1.5f, "Minimum cascade radius (cm)"}; // Configurable maximumCascRadius{"maximumCascRadius", 40.0f, "Maximum cascade radius"}; + Configurable dcabachtopvMin{"dcabachtopvMin", 0.07f, "Minimum DCA of bachelor to primary vertex (cm)"}; + Configurable dcaV0topvMin{"dcaV0topvMin", 0.02f, "Minimum DCA of V0 to primary vertex (cm)"}; + Configurable dcaV0DaughtersMaxCasc{"dcaV0DaughtersMaxCasc", 0.7f, "Maximum DCA between V0 daughters (cm) for cascade analysis"}; + Configurable dcaCascDaughtersMax{"dcaCascDaughtersMax", 0.8f, "Maximum DCA between daughters (cm)"}; + Configurable dcaPosToPVminV0{"dcaPosToPVminV0", 0.1f, "Minimum DCA of V0 positive track to primary vertex in cascades (cm)"}; + Configurable dcaNegToPVminV0{"dcaNegToPVminV0", 0.1f, "Minimum DCA of V0 negative track to primary vertex in cascades (cm)"}; + Configurable v0cospaMinCasc{"v0cospaMinCasc", 0.99f, "Minimum V0 cosine of pointing angle for cascade analysis"}; Configurable casccospaMin{"casccospaMin", 0.99f, "Minimum cascade cosine of pointing angle"}; - Configurable dcabachtopvMin{"dcabachtopvMin", 0.1f, "Minimum DCA of bachelor to primary vertex"}; - Configurable dcaV0topvMin{"dcaV0topvMin", 0.1f, "Minimum DCA of V0 to primary vertex"}; - Configurable dcaCascDaughtersMax{"dcaCascDaughtersMax", 0.5f, "Maximum DCA between daughters"}; - Configurable dcaNegToPVminV0{"dcaNegToPVminV0", 0.1f, "Minimum DCA of V0 negative track to primary vertex in cascades"}; - Configurable dcaPosToPVminV0{"dcaPosToPVminV0", 0.1f, "Minimum DCA of V0 positive track to primary vertex in cascades"}; - Configurable deltaMassXi{"deltaMassXi", 0.02f, "Mass window for Xi rejection"}; - Configurable deltaMassOmega{"deltaMassOmega", 0.02f, "Mass window for Omega rejection"}; - Configurable deltaMassLambda{"deltaMassLambda", 0.02f, "Mass window for Lambda inclusion"}; + + Configurable deltaMassXi{"deltaMassXi", 0.008f, "[Omega] Mass window for Xi rejection"}; + Configurable deltaMassOmega{"deltaMassOmega", 0.008f, "[Xi] Mass window for Omega rejection"}; + Configurable deltaMassLambda{"deltaMassLambda", 0.005f, "[Xi, Omega] Mass window for Lambda inclusion"}; } configCasc; // Axes @@ -369,6 +373,12 @@ struct StrangenessInJetsIons { registryData.add("OmegaPos_in_ue", "OmegaPos_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); registryData.add("OmegaNeg_in_jet", "OmegaNeg_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); registryData.add("OmegaNeg_in_ue", "OmegaNeg_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + if (doRandomConeSys) { + registryData.add("XiPos_in_rc", "XiPos_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryData.add("XiNeg_in_rc", "XiNeg_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryData.add("OmegaPos_in_rc", "OmegaPos_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + registryData.add("OmegaNeg_in_rc", "OmegaNeg_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + } } if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryData.add("Pion_in_jet", "Pion_in_jet", HistType::kTHnSparseF, {multAxis, ptAxisLongLived, nsigmaTPCAxis, nsigmaTOFAxis, dcaAxis}); @@ -448,7 +458,6 @@ struct StrangenessInJetsIons { registryMC.add("XiPos_generated_MB", "XiPos_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_generated_MB", "XiNeg_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); } - if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.add("XiPos_generated_jet", "XiPos_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiPos_generated_ue", "XiPos_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); @@ -458,11 +467,24 @@ struct StrangenessInJetsIons { registryMC.add("OmegaPos_generated_ue", "OmegaPos_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_jet", "OmegaNeg_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_ue", "OmegaNeg_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_generated_rc", "XiPos_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_rc", "XiNeg_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_rc", "OmegaPos_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_rc", "OmegaNeg_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for the full event (without jets) registryMC.add("OmegaPos_generated_MB", "OmegaPos_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_MB", "OmegaNeg_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); + + // In generated events with at least one reco collision + registryMC.add("XiPos_generated_w_reco_MB", "XiPos_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_w_reco_MB", "XiNeg_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_w_reco_MB", "OmegaPos_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_w_reco_MB", "OmegaNeg_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); } + if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryMC.add("PionPos_generated_jet", "PionPos_generated_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); registryMC.add("PionNeg_generated_jet", "PionNeg_generated_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); @@ -597,13 +619,62 @@ struct StrangenessInJetsIons { registryMC.add("OmegaPos_reconstructed_ue", "OmegaPos_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_jet", "OmegaNeg_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_ue", "OmegaNeg_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_reconstructed_rc", "XiPos_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_rc", "XiNeg_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_rc", "OmegaPos_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_rc", "OmegaNeg_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + } + + // Histograms for secondary hadrons + registryMC.add("XiPos_reconstructed_jet_incl", "XiPos_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_reconstructed_ue_incl", "XiPos_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_jet_incl", "XiNeg_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_ue_incl", "XiNeg_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_jet_incl", "OmegaPos_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_ue_incl", "OmegaPos_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_jet_incl", "OmegaNeg_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_ue_incl", "OmegaNeg_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_reconstructed_rc_incl", "XiPos_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_rc_incl", "XiNeg_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_rc_incl", "OmegaPos_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_rc_incl", "OmegaNeg_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + } + + // Histograms for generated particles in reconstructed events + registryMC.add("XiPos_gen_recoEvent_jet", "XiPos_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_gen_recoEvent_ue", "XiPos_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_jet", "XiNeg_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_ue", "XiNeg_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_jet", "OmegaPos_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_ue", "OmegaPos_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_jet", "OmegaNeg_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_ue", "OmegaNeg_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_gen_recoEvent_rc", "XiPos_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_rc", "XiNeg_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_rc", "OmegaPos_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_rc", "OmegaNeg_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for the full event (without jets) registryMC.add("XiPos_reconstructed_MB", "XiPos_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_reconstructed_MB", "XiNeg_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaPos_reconstructed_MB", "OmegaPos_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_MB", "OmegaNeg_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); + + registryMC.add("XiPos_reconstructed_MB_incl", "XiPos_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_MB_incl", "XiNeg_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_MB_incl", "OmegaPos_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_MB_incl", "OmegaNeg_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + + registryMC.add("XiPos_gen_recoEvent_MB", "XiPos_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_MB", "XiNeg_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_MB", "OmegaPos_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_MB", "OmegaNeg_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); } + if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryMC.add("Pion_reconstructed_jet", "Pion_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); registryMC.add("Pion_reconstructed_ue", "Pion_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxisLongLived}); @@ -874,7 +945,6 @@ struct StrangenessInJetsIons { registryData.fill(HIST("h2_centrality_deltaPt_RandomCone"), multiplicity, deltaPtRandomCone); registryData.fill(HIST("h2_centrality_rhoPerp"), multiplicity, rhoPerp); } else if (selProcess == 1) { - // Ricordati di definire questi istogrammi nel tuo book/registry MC! registryMC.fill(HIST("h2_centrality_deltaPt_RandomCone_gen"), multiplicity, deltaPtRandomCone); registryMC.fill(HIST("h2_centrality_rhoPerp_gen"), multiplicity, rhoPerp); } else if (selProcess == 2) { @@ -1192,12 +1262,12 @@ struct StrangenessInJetsIons { } // V0 selections - if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configV0.v0cospaMin) + if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configCasc.v0cospaMinCasc) return false; // if (casc.v0radius() < configV0.minimumV0Radius || casc.v0radius() > configV0.maximumV0Radius) - if (casc.v0radius() < configV0.minimumV0Radius) + if (casc.v0radius() < configCasc.minimumV0RadiusCasc) return false; - if (std::fabs(casc.dcaV0daughters()) > configV0.dcaV0DaughtersMax) + if (std::fabs(casc.dcaV0daughters()) > configCasc.dcaV0DaughtersMaxCasc) return false; if (std::fabs(casc.dcapostopv()) < configCasc.dcaPosToPVminV0) return false; @@ -1308,12 +1378,12 @@ struct StrangenessInJetsIons { } // V0 selections - if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configV0.v0cospaMin) + if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configCasc.v0cospaMinCasc) return false; // if (casc.v0radius() < configV0.minimumV0Radius || casc.v0radius() > configV0.maximumV0Radius) - if (casc.v0radius() < configV0.minimumV0Radius) + if (casc.v0radius() < configCasc.minimumV0RadiusCasc) return false; - if (std::fabs(casc.dcaV0daughters()) > configV0.dcaV0DaughtersMax) + if (std::fabs(casc.dcaV0daughters()) > configCasc.dcaV0DaughtersMaxCasc) return false; if (std::fabs(casc.dcapostopv()) < configCasc.dcaPosToPVminV0) return false; @@ -1514,21 +1584,33 @@ struct StrangenessInJetsIons { case kXiMinus: if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiNeg_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("XiNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kXiPlusBar: if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiPos_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("XiPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kOmegaMinus: if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.fill(HIST("OmegaNeg_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("OmegaNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kOmegaPlusBar: if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.fill(HIST("OmegaPos_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("OmegaPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kPiPlus: @@ -1693,27 +1775,80 @@ struct StrangenessInJetsIons { continue; if (std::abs(motherPos.pdgCode()) != kLambda0) continue; - if (!motherBach.isPhysicalPrimary()) - continue; - if (std::abs(motherPos.eta()) > 0.8) continue; // Xi+ - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && motherBach.pdgCode() == kXiPlusBar) { - registryMC.fill(HIST("XiPos_reconstructed_MB"), multiplicity, casc.pt()); + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + motherPos.pdgCode() == kXiPlusBar && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Xi- - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && motherBach.pdgCode() == kXiMinus) { - registryMC.fill(HIST("XiNeg_reconstructed_MB"), multiplicity, casc.pt()); + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + motherPos.pdgCode() == kXiMinus && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega+ - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && motherBach.pdgCode() == kOmegaPlusBar) { - registryMC.fill(HIST("OmegaPos_reconstructed_MB"), multiplicity, casc.pt()); + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + motherPos.pdgCode() == kOmegaPlusBar && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega- - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && motherBach.pdgCode() == kOmegaMinus) { - registryMC.fill(HIST("OmegaNeg_reconstructed_MB"), multiplicity, casc.pt()); + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + motherPos.pdgCode() == kOmegaMinus && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); + } + + if (!motherBach.isPhysicalPrimary()) + continue; + + // Rapidity generated particle + double yGen = motherPos.y(); + + // Histograms below are filled only for PhysicalPrimary particles + // Xi+ (primary) + if (motherPos.pdgCode() == kXiPlusBar) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Xi- (primary) + if (motherBach.pdgCode() == kXiMinus) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Omega+ (primary) + if (motherBach.pdgCode() == kOmegaPlusBar) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Omega- (primary) + if (motherBach.pdgCode() == kOmegaMinus) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_reconstructed_MB"), multiplicity, casc.pt()); + } } } } @@ -2485,6 +2620,13 @@ struct StrangenessInJetsIons { const double deltaPhiUe2 = getDeltaPhi(cascadeDir.Phi(), ue2[i].Phi()); const double deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Xi+ if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0) { if (deltaRjet < rJet) { @@ -2493,6 +2635,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("XiPos_in_ue"), multiplicity, casc.pt(), casc.mXi()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("XiPos_in_rc"), multiplicity, casc.pt(), casc.mXi()); + } } // Xi- if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0) { @@ -2502,6 +2647,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("XiNeg_in_ue"), multiplicity, casc.pt(), casc.mXi()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("XiNeg_in_rc"), multiplicity, casc.pt(), casc.mXi()); + } } // Omega+ if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0) { @@ -2511,6 +2659,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("OmegaPos_in_ue"), multiplicity, casc.pt(), casc.mOmega()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("OmegaPos_in_rc"), multiplicity, casc.pt(), casc.mOmega()); + } } // Omega- if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0) { @@ -2520,6 +2671,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("OmegaNeg_in_ue"), multiplicity, casc.pt(), casc.mOmega()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("OmegaNeg_in_rc"), multiplicity, casc.pt(), casc.mOmega()); + } } } } @@ -2943,6 +3097,26 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_generated_rc"), genMultiplicity, hadron.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; default: break; } @@ -3174,6 +3348,26 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_gen_recoEvent_jet"), multiplicity, momVec.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; default: break; } @@ -3195,6 +3389,26 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_gen_recoEvent_ue"), multiplicity, momVec.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; default: break; } @@ -3216,6 +3430,26 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_gen_recoEvent_rc"), multiplicity, momVec.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; default: break; } @@ -3394,8 +3628,8 @@ struct StrangenessInJetsIons { } if (pdgParent == 0) continue; - if (!isPhysPrim) - continue; + // if (!isPhysPrim) + // continue; // Compute distances from jet and UE axes TVector3 cascadeDir(casc.px(), casc.py(), casc.pz()); @@ -3409,41 +3643,110 @@ struct StrangenessInJetsIons { double deltaPhiUe2 = getDeltaPhi(cascadeDir.Phi(), ue2[i].Phi()); double deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Xi+ - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar) { + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("XiPos_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("XiPos_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiPos_reconstructed_rc"), multiplicity, casc.pt()); + } } // Xi- - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus) { + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("XiNeg_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("XiNeg_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiNeg_reconstructed_rc"), multiplicity, casc.pt()); + } } // Omega+ - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar) { + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("OmegaPos_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("OmegaPos_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaPos_reconstructed_rc"), multiplicity, casc.pt()); + } } // Omega- - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus) { + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("OmegaNeg_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("OmegaNeg_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaNeg_reconstructed_rc"), multiplicity, casc.pt()); + } + } + + // Fill inclusive histograms + // Xi+ + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("XiPos_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("XiPos_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiPos_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Xi- + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("XiNeg_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("XiNeg_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiNeg_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Omega+ + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("OmegaPos_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("OmegaPos_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaPos_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Omega- + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("OmegaNeg_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("OmegaNeg_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaNeg_reconstructed_rc_incl"), multiplicity, casc.pt()); + } } } } @@ -3898,9 +4201,9 @@ struct StrangenessInJetsIons { if (particleOfInterestDict[ParticleOfInterest::kCascades]) { // Cascades for (const auto& casc : Cascades) { // Get cascade daughters - const auto& bach = casc.bachelor_as(); - const auto& pos = casc.posTrack_as(); - const auto& neg = casc.negTrack_as(); + const auto& bach = casc.bachelor_as(); + const auto& pos = casc.posTrack_as(); + const auto& neg = casc.negTrack_as(); TVector3 cascadeDir(casc.px(), casc.py(), casc.pz()); // Xi+ From ac098d0bbd50791b347f96336a0e9445f05322be Mon Sep 17 00:00:00 2001 From: Ayan Kumar Manna Date: Mon, 21 Sep 2026 23:59:22 +0530 Subject: [PATCH 021/648] [PWGHF] Add centrality dependent d0 efficiency and track efficiency (#17991) --- PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx | 438 ++++++++++++++++--- 1 file changed, 373 insertions(+), 65 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx index 66e8da4f2bb..334b2878346 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx @@ -16,6 +16,7 @@ /// \author Samrangy Sadhu , INFN Bari /// \author Swapnesh Santosh Khade , IIT Indore +#include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" @@ -28,6 +29,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -46,6 +48,7 @@ #include #include +#include #include @@ -57,6 +60,7 @@ using namespace o2::constants::physics; using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::hf_centrality; using namespace o2::aod::hf_correlation_d0_hadron; using namespace o2::analysis::hf_correlations; @@ -93,6 +97,12 @@ struct HfTaskCorrelationD0Hadrons { Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0 (bar)"}; Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analysis"}; + Configurable useSel8ForEff{"useSel8ForEff", true, "Flag for applying sel8 for collision selection in efficiency"}; + Configurable removeCollWSplitVtx{"removeCollWSplitVtx", false, "Flag for rejecting MC collisions with more than one reconstructed collision"}; + Configurable centEstimator{"centEstimator", 3, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; + Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance in efficiency"}; + Configurable centralityMin{"centralityMin", 0., "min. centrality for efficiency"}; + Configurable centralityMax{"centralityMax", 100., "max. centrality for efficiency"}; Configurable rejectD0D0barHypothesis{"rejectD0D0barHypothesis", true, "Reject candidates satisfying both D0 and D0bar hypothesis"}; Configurable> signalRegionLeft{"signalRegionLeft", {1.7948, 1.8198, 1.8198, 1.8148, 1.8148, 1.8048, 1.8048, 1.7948, 1.7948, 1.7898, 1.7848, 1.7598}, "Inner values of signal region vs pT"}; Configurable> signalRegionRight{"signalRegionRight", {1.9098, 1.8998, 1.9048, 1.9048, 1.9148, 1.9248, 1.9298, 1.9348, 1.9398, 1.9298, 1.9398, 1.9198}, "Outer values of signal region vs pT"}; @@ -104,7 +114,7 @@ struct HfTaskCorrelationD0Hadrons { Configurable leadingParticlePtMin{"leadingParticlePtMin", 0., "Min for leading particle pt"}; enum CandidateStep { kCandidateStepMcGenAll = 0, - kCandidateStepMcGenD0ToPiKPi, + kCandidateStepMcGenD0ToKPi, kCandidateStepMcCandInAcceptance, kCandidateStepMcDaughtersInAcceptance, kCandidateStepMcReco, @@ -116,10 +126,18 @@ struct HfTaskCorrelationD0Hadrons { using CandD0McReco = soa::Filtered>; using CandD0McGen = soa::Join; using TracksWithMc = soa::Filtered>; // trackFilter applied + using CollisionsWithCent = soa::Join; + using McCollisionsWithMult = soa::Join; Filter d0Filter = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0) || (aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (aod::track::pt > ptTrackMin) && (aod::track::pt < ptTrackMax) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); + Preslice perCollisionCand = o2::aod::hf_cand::collisionId; + Preslice perCollisionCandMc = o2::aod::mcparticle::mcCollisionId; + Preslice perCollisionTrack = o2::aod::track::collisionId; + Preslice perCollisionTrackMc = o2::aod::mcparticle::mcCollisionId; + PresliceUnsorted collPerCollMc = o2::aod::mccollisionlabel::mcCollisionId; + ConfigurableAxis binsMassD{"binsMassD", {200, 1.3848, 2.3848}, "inv. mass (#pi K) (GeV/#it{c}^{2});entries"}; ConfigurableAxis binsBdtScore{"binsBdtScore", {100, 0., 1.}, "Bdt output scores"}; ConfigurableAxis binsEta{"binsEta", {100, -2., 2.}, "#it{#eta}"}; @@ -283,16 +301,61 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hBdtScoreNonPromptD0bar", "D0bar BDT non-prompt score", {HistType::kTH1F, {axisBdtScore}}); // Efficiency histograms - registry.add("hPtCandMcRecPrompt", "D0 prompt candidates pt", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates pt", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenPrompt", "D0,Hadron particles prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenNonPrompt", "D0,Hadron particles non prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenDaughterInAcc", "D0,Hadron particles non prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - - auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisMultFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); - hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); - hCandidates->GetAxis(1)->SetTitle("multiplicity"); - hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + registry.add("hPtCandMcGenDaughterInAcc", "D0 daughters in acceptance - MC Gen", {HistType::kTH1F, {axisPtD}}); + + if (useCentrality) { + registry.add("hPtCandMcRecPrompt", "D0 prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcGenPrompt", "D0 prompt particles - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcGenNonPrompt", "D0 non prompt particles - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + + auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisCentFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); + hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); + hCandidates->GetAxis(1)->SetTitle("centrality FT0M (%)"); + hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + } else { + registry.add("hPtCandMcRecPrompt", "D0 prompt candidates pt", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates pt", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcGenPrompt", "D0 prompt particles - MC Gen", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcGenNonPrompt", "D0 non prompt particles - MC Gen", {HistType::kTH1F, {axisPtD}}); + + auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisMultFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); + hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); + hCandidates->GetAxis(1)->SetTitle("multiplicity"); + hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + } + + // Associated-track efficiency histograms. + registry.add("hFakeCollisionTrackEff", "Fake collision counter - track efficiency", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake coll"}}}); + registry.add("hFakeTracksTrackEff", "Fake track counter - track efficiency", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake tracks"}}}); + if (useCentrality) { + registry.add("hPtParticleAssocMcGen", "Associated primary particles - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtParticleAssocMcRec", "Associated primary particles - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + } else { + // Minimum-bias tracking efficiency versus pT only. + registry.add("hPtParticleAssocMcGen", "Associated primary particles - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtParticleAssocMcRec", "Associated primary particles - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + } } /// D-h correlation pair filling task, from pair tables - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) @@ -861,34 +924,95 @@ struct HfTaskCorrelationD0Hadrons { PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcGen, "Process MC Gen mode", false); /// D0 reconstruction and selection efficiency - void processMcCandEfficiency(soa::Join const&, - soa::Join const&, + void processMcCandEfficiency(CollisionsWithCent const& collisions, + McCollisionsWithMult const& mcCollisions, CandD0McGen const& mcParticles, CandD0McReco const& candidates, aod::TracksWMc const&) { auto hCandidates = registry.get(HIST("hCandidates")); - /// Gen loop - float multiplicity = -1.; - for (const auto& mcParticle : mcParticles) { - // generated candidates - if (std::abs(mcParticle.pdgCode()) == Pdg::kD0) { - auto mcCollision = mcParticle.template mcCollision_as>(); - multiplicity = mcCollision.multMCFT0A() + mcCollision.multMCFT0C(); // multFT0M = multFt0A + multFT0C - hCandidates->Fill(kCandidateStepMcGenAll, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); - if (std::abs(mcParticle.flagMcMatchGen()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - hCandidates->Fill(kCandidateStepMcGenD0ToPiKPi, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); - auto yD0 = RecoDecay::y(mcParticle.pVector(), o2::constants::physics::MassD0); + /// Loop over generated MC collisions + for (const auto& mcCollision : mcCollisions) { + const auto groupedCollisions = collisions.sliceBy(collPerCollMc, mcCollision.globalIndex()); + const auto groupedMcParticles = mcParticles.sliceBy(perCollisionCandMc, mcCollision.globalIndex()); + + if (groupedCollisions.size() < 1) { // skip MC events without reconstructed collisions + continue; + } + if (groupedCollisions.size() > 1 && removeCollWSplitVtx) { // optionally reject split vertices + continue; + } + + const float multiplicityMc = mcCollision.multMCFT0A() + mcCollision.multMCFT0C(); + + float centralityMc = -1.f; + if (useCentrality) { + centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); + } + + const float activityMc = useCentrality ? centralityMc : multiplicityMc; + + /// Loop over reconstructed collisions linked to this MC collision + for (const auto& collision : groupedCollisions) { + if (useSel8ForEff && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > cutCollPosZMc) { + continue; + } + if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + + float centralityRec = -1.f; + if (useCentrality) { + centralityRec = getCentralityColl(collision, centEstimator); + + if (centralityRec < centralityMin || centralityRec > centralityMax) { + continue; + } + } + if (!collision.has_mcCollision()) { + continue; + } + + const float activityRec = useCentrality ? centralityRec : collision.multFT0M(); + const auto groupedCandidates = candidates.sliceBy(perCollisionCand, collision.globalIndex()); + + /// Generated candidate loop + for (const auto& mcParticle : groupedMcParticles) { + if (std::abs(mcParticle.pdgCode()) != Pdg::kD0) { + continue; + } + + hCandidates->Fill(kCandidateStepMcGenAll, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + + if (std::abs(mcParticle.flagMcMatchGen()) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + continue; + } + + hCandidates->Fill(kCandidateStepMcGenD0ToKPi, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + + const auto yD0 = RecoDecay::y(mcParticle.pVector(), o2::constants::physics::MassD0); if (std::abs(yD0) <= yCandGenMax) { - hCandidates->Fill(kCandidateStepMcCandInAcceptance, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); + hCandidates->Fill(kCandidateStepMcCandInAcceptance, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + if (mcParticle.originMcGen() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt()); - } - if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt()); + if (useCentrality) { + registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt(), centralityMc); + } else { + registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt()); + } + } else if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt(), centralityMc); + } else { + registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt()); + } } } + bool isDaughterInAcceptance = true; auto daughters = mcParticle.template daughters_as(); for (const auto& daughter : daughters) { @@ -897,57 +1021,241 @@ struct HfTaskCorrelationD0Hadrons { } } if (isDaughterInAcceptance) { - hCandidates->Fill(kCandidateStepMcDaughtersInAcceptance, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); + hCandidates->Fill(kCandidateStepMcDaughtersInAcceptance, mcParticle.pt(), activityMc, mcParticle.originMcGen()); registry.fill(HIST("hPtCandMcGenDaughterInAcc"), mcParticle.pt()); } - } - } - } + } // end generated candidate loop - // recontructed candidates loop - for (const auto& candidate : candidates) { - if (candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { - continue; - } - std::vector outputMlD0 = {-1., -1., -1.}; - std::vector outputMlD0bar = {-1., -1., -1.}; - if (candidate.isSelD0() < selectionFlagD0 || candidate.isSelD0bar() < selectionFlagD0) { + /// Reconstructed candidate loop + for (const auto& candidate : groupedCandidates) { + if (candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { + continue; + } + + const bool isD0 = candidate.isSelD0() >= selectionFlagD0; + const bool isD0bar = candidate.isSelD0bar() >= selectionFlagD0; + if (!isD0 && !isD0bar) { + continue; + } + if (rejectD0D0barHypothesis && isD0 && isD0bar) { + continue; + } + + std::vector outputMlD0 = {-1., -1., -1.}; + std::vector outputMlD0bar = {-1., -1., -1.}; + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + if (isD0) { + outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; + } + if (isD0bar) { + outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; + } + } + + const int ptBinMl = o2::analysis::findBin(binsPtMl, candidate.pt()); + if (ptBinMl < 0) { + continue; + } + + const bool passesD0Ml = isD0 && + outputMlD0[2] >= mlOutputPromptD0->at(ptBinMl) && + outputMlD0[0] <= mlOutputBkgD0->at(ptBinMl) && + outputMlD0[1] <= mlOutputNonPromptD0->at(ptBinMl); + + const bool passesD0barMl = isD0bar && + outputMlD0bar[2] >= mlOutputPromptD0bar->at(ptBinMl) && + outputMlD0bar[0] <= mlOutputBkgD0bar->at(ptBinMl) && + outputMlD0bar[1] <= mlOutputNonPromptD0bar->at(ptBinMl); + + if (!passesD0Ml && !passesD0barMl) { + continue; + } + + // Count only the truth-matched hypothesis that passes its own selection and ML cuts + const bool isD0Signal = + candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK && + passesD0Ml; + + const bool isD0barSignal = + candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK && + passesD0barMl; + + if (!(isD0Signal || isD0barSignal)) { + continue; + } + + hCandidates->Fill(kCandidateStepMcReco, candidate.pt(), activityRec, candidate.originMcRec()); + + if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { + continue; + } + + hCandidates->Fill(kCandidateStepMcRecoInAcceptance, candidate.pt(), activityRec, candidate.originMcRec()); + + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt(), centralityRec); + } else { + registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt()); + } + } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt(), centralityRec); + } else { + registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt()); + } + } + } // end reconstructed candidate loop + } // end reconstructed collision loop + } // end generated MC collision loop + } + PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcCandEfficiency, "Process MC for calculating candidate reconstruction efficiency", false); + + /// D0-Hadron correlation - associated-particle tracking efficiency. + /// With useCentrality=false: minimum-bias efficiency versus pT. + /// With useCentrality=true: pT x eta x z_vtx x reconstructed-centrality efficiency. + void processMcTrackEfficiency(CollisionsWithCent const& collisions, + McCollisionsWithMult const& mcCollisions, + aod::McParticles const& mcParticles, + TracksWithMc const& tracksData) + { + for (const auto& mcCollision : mcCollisions) { + const auto groupedCollisions = collisions.sliceBy(collPerCollMc, mcCollision.globalIndex()); + const auto groupedMcParticles = mcParticles.sliceBy(perCollisionTrackMc, mcCollision.globalIndex()); + + if (groupedCollisions.size() < 1) { continue; } - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; - outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; - } - int const ptBinMl = o2::analysis::findBin(binsPtMl, candidate.pt()); - if (ptBinMl < 0) { + if (groupedCollisions.size() > 1 && removeCollWSplitVtx) { continue; } - bool const passesD0Ml = outputMlD0[2] >= mlOutputPromptD0->at(ptBinMl) && outputMlD0[0] <= mlOutputBkgD0->at(ptBinMl) && outputMlD0[1] <= mlOutputNonPromptD0->at(ptBinMl); - bool const passesD0barMl = outputMlD0bar[2] >= mlOutputPromptD0bar->at(ptBinMl) && outputMlD0bar[0] <= mlOutputBkgD0bar->at(ptBinMl) && outputMlD0bar[1] <= mlOutputNonPromptD0bar->at(ptBinMl); - if (!passesD0Ml || !passesD0barMl) { - continue; - } - auto collision = candidate.template collision_as>(); - if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { - continue; + float centralityMc = -1.f; + if (useCentrality) { + // Reconstructed-collision centrality assigned to the corresponding MC collision. + centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); } - multiplicity = collision.multFT0M(); - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - hCandidates->Fill(kCandidateStepMcReco, candidate.pt(), multiplicity, candidate.originMcRec()); - if (std::abs(HfHelper::yD0(candidate)) <= yCandMax) { - hCandidates->Fill(kCandidateStepMcRecoInAcceptance, candidate.pt(), multiplicity, candidate.originMcRec()); - if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt()); + + for (const auto& collision : groupedCollisions) { + if (useSel8ForEff && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > cutCollPosZMc) { + continue; + } + if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + if (!collision.has_mcCollision()) { + registry.fill(HIST("hFakeCollisionTrackEff"), 0.); + continue; + } + + float centralityRec = -1.f; + if (useCentrality) { + centralityRec = getCentralityColl(collision, centEstimator); + if (centralityRec < centralityMin || centralityRec > centralityMax) { + continue; } - if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt()); + } + + const auto groupedTracks = tracksData.sliceBy(perCollisionTrack, collision.globalIndex()); + + // MC-gen denominator. + for (const auto& mcParticle : groupedMcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (mcParticle.pt() <= ptTrackMin || mcParticle.pt() >= ptTrackMax || std::abs(mcParticle.eta()) >= etaTrackMax) { + continue; + } + + const int pdg = std::abs(mcParticle.pdgCode()); + if (pdg != kElectron && pdg != kMuonMinus && pdg != kPiPlus && pdg != kKPlus && pdg != kProton) { + continue; + } + + if (useCentrality) { + registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } + } else { + registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt()); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt()); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt()); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt()); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt()); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt()); + } + } + } + + // MC-reco numerator. TracksWithMc already carries the configured pT/eta/DCA filter. + for (const auto& track : groupedTracks) { + if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRaws) { + continue; + } + if (!track.has_mcParticle()) { + registry.fill(HIST("hFakeTracksTrackEff"), 0.); + continue; + } + + const auto mcParticle = track.template mcParticle_as(); + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + + const int pdg = std::abs(mcParticle.pdgCode()); + if (pdg != kElectron && pdg != kMuonMinus && pdg != kPiPlus && pdg != kKPlus && pdg != kProton) { + continue; + } + + if (useCentrality) { + registry.fill(HIST("hPtParticleAssocMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } + } else { + registry.fill(HIST("hPtParticleAssocMcRec"), track.pt()); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcRec"), track.pt()); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcRec"), track.pt()); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcRec"), track.pt()); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcRec"), track.pt()); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcRec"), track.pt()); + } } } } } } - PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcCandEfficiency, "Process MC for calculating candidate reconstruction efficiency", false); + PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcTrackEfficiency, "Process MC for calculating associated-particle tracking efficiency", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From f178bcd0ad4097531188ff1aecff95acce85716e Mon Sep 17 00:00:00 2001 From: Pei-Ying Kuan <128475937+pykuan@users.noreply.github.com> Date: Mon, 21 Sep 2026 21:49:39 +0200 Subject: [PATCH 022/648] [PWGCF] add number of track cuts and histograms (#18002) Co-authored-by: Pei-Ying Kuan --- .../Tasks/multiparticleCumulants.cxx | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx index c880ec3d9a0..ff637d76aa7 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -96,6 +96,7 @@ enum EnEventHistograms { eVertexY, eVertexZ, eNumContrib, + eNumTrack, eEventHistograms_N }; @@ -105,7 +106,8 @@ static constexpr std::array EventHistNames = { "VertexX", "VertexY", "VertexZ", - "NumContrib"}; + "NumContrib", + "NumTrack"}; enum EnParticleHistograms { ePt, @@ -227,6 +229,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "Sel8 cut switch"}; Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "centrality cut switch"}; Configurable cfNumContribCutSwitch{"cfNumContribCutSwitch", true, "NContribution cut switch"}; + Configurable cfNumTrackCutSwitch{"cfNumTrackCutSwitch", true, "NTrack cut switch"}; Configurable cfCentCorrCutSwitch{"cfCentCorrCutSwitch", true, "centrality correlation outlier cut switch"}; Configurable cfMultCorrCutSwitch{"cfMultCorrCutSwitch", true, "multiplicity correlation outlier cut switch"}; Configurable cfNumContribMultCorrCutSwitch{"cfNumContribMultCorrCutSwitch", true, "NContribution vs. multiplicity correlation outlier cut switch"}; @@ -244,7 +247,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfVertexZCut{"cfVertexZCut", {-10., 10.}, "vertex z position range: {min, max}[cm]"}; Configurable> cfCentCut{"cfCentCut", {10., 20.}, "centrality range: {min, max}[%]"}; Configurable> cfNumContribCut{"cfNumContribCut", {0, 3000.}, "NContribution range: {min, max}"}; - // Configurable> cfCentCorrCut{"cfCentCorrCut", {1., 10.}, "parameters of limits in centralities 2D histograms, (x-t)/m < y < mx+t: {m, t}"}; + Configurable> cfNumTrackCut{"cfNumTrackCut", {0, 3000.}, "NTrack range: {min, max}"}; Configurable> cfCentABCorrCut{"cfCentABCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentACCorrCut{"cfCentACCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentBCCorrCut{"cfCentBCCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; @@ -277,6 +280,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfVerYBins{"cfVerYBins", {100, -0.05, 0.05}, "nVerYBins, VerYMin, VerYMax"}; Configurable> cfVerZBins{"cfVerZBins", {100, -50., 50.}, "nVerZBins, VerZMin, VerZMax"}; Configurable> cfNumContribBins{"cfNumContribBins", {100, 0., 5000.}, "nNumContribBins, NumContribMin, NumContribMax"}; + Configurable> cfNumTrackBins{"cfNumTrackBins", {100, 0., 5000.}, "nNumTrackBins, NumTrackMin, NumTrackMax"}; Configurable> cfTwoParticleCorrBins{"cfTwoParticleCorrBins", {100, 0., 2e-3}, "nTwoParticleCorrBins, TwoParticleCorrMin, TwoParticleCorrMax"}; Configurable> cfFourParticleCorrBins{"cfFourParticleCorrBins", {100, 0., 1e-7}, "nFourParticleCorrBins, FourParticleCorrMin, FourParticleCorrMax"}; Configurable> cfSixParticleCorrBins{"cfSixParticleCorrBins", {100, 0., 1e-11}, "nSixParticleCorrBins, SixParticleCorrMin, SixParticleCorrMax"}; @@ -297,6 +301,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bool fSel8CutSwitch = true; bool fCentCutSwitch = true; bool fNumContribCutSwitch = true; + bool fNumTrackCutSwitch = true; bool fCentCorrCutSwitch = true; bool fMultCorrCutSwitch = true; bool fNumContribMultCorrCutSwitch = true; @@ -312,6 +317,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fVertexZCut = {-10., 10.}; std::vector fCentCut = {10., 20.}; std::vector fNumContribCut = {0, 3000.}; + std::vector fNumTrackCut = {0, 350.}; std::vector>> fCentAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector>> fMultAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector fNumContribMultCorrCut = {0.024, -600., 0.035, 100.}; @@ -337,6 +343,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fVerYBins = {100, -0.05, 0.05}; std::vector fVerZBins = {100, -50., 50.}; std::vector fNumContribBins = {100, 0, 5000}; + std::vector fNumTrackBins = {100, 0, 5000}; std::vector fTwoParticleCorrBins = {100, 0, 2e-3}; std::vector fFourParticleCorrBins = {100, 0, 1e-7}; std::vector fSixParticleCorrBins = {100, 0, 1e-11}; @@ -1399,7 +1406,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam if (diffPt < diffPtMax) { wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); } else { - LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); + if (tc.fPrintSwitch) { + LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); + } } // Fill MC particle histograms after cut: pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); @@ -1410,6 +1419,11 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } // if constexpr (rs == eRec || rs == eRecAndSim) { } // end of for (int64_t i = 0; i < tracks.size(); i++) { + // Fill real event histograms (NumTrack) before cut: + ev.fEventHistograms[eNumTrack][eRec][eBefore]->Fill(nTracksBefore); + ev.fEventHistograms[eNumTrack][eRec][eAfter]->Fill(nTracksAfter); + bool bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + // 2-p correlations: for (int i = 0; i < NumTwoPCorrBins; i++) { int k = 2; @@ -1439,7 +1453,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eBefore][i]); } } else { - LOGF(warning, "cent=%f, nTracksBefore = %d, wTwoRecursionBefore = %e", rlCollisionCent, nTracksBefore, wTwoRecursionBefore); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wTwoRecursionBefore = %e", rlCollisionCent, nTracksBefore, wTwoRecursionBefore); + } } // After cut: @@ -1447,7 +1463,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double wTwoRecursionAfter = mccRecursion(2, harmonicsTwoDen, eAfter).Re(); ebye.fTwoParticleCorrelationEbye[eAfter][i] = twoRecursionAfter.Re(); - if (nTracksAfter > k - 1 && wTwoRecursionAfter > 0.) { + if (nTracksAfter > k - 1 && wTwoRecursionAfter > 0. && bNumTrackCut) { // Fill vn^2 profile and histogram: mc.fTwoParticleCorrelationProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); mc.fTwoParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i]); @@ -1463,7 +1479,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eAfter][i]); } } else { - LOGF(warning, "cent=%f, nTracksAfter = %d, wTwoRecursionAfter = %e", rlCollisionCent, nTracksAfter, wTwoRecursionAfter); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wTwoRecursionAfter = %e", rlCollisionCent, nTracksAfter, wTwoRecursionAfter); + } } for (int n = 0; n < NumEtaGap; n++) { @@ -1478,7 +1496,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam gapb = (qvba * TComplex::Conjugate(qvbb)).Re() / (wba * wbb); mc.fTwoParticleCorrelationGapProfiles[n][eBefore]->Fill(mc.fTwoParticleCorrelationGapProfiles[n][eBefore]->GetXaxis()->GetBinCenter(i + 1), gapb, wba * wbb); } else { - LOGF(warning, "etagap=%f, cent=%f, nTracksBeforeA = %d, nTracksBeforeB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksBeforeA[n], nTracksBeforeB[n]); + if (tc.fPrintSwitch) { + LOGF(warning, "etagap=%f, cent=%f, nTracksBeforeA = %d, nTracksBeforeB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksBeforeA[n], nTracksBeforeB[n]); + } } // After cut, with gap: @@ -1492,7 +1512,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationGapProfiles[n][eAfter]->Fill(mc.fTwoParticleCorrelationGapProfiles[n][eAfter]->GetXaxis()->GetBinCenter(i + 1), gapa, waa * wab); mc.fTwoParticleCorrelationGapHistograms[n][i]->Fill(gapa, waa * wab); } else { - LOGF(warning, "etagap=%f, cent=%f, nTracksAfterA = %d, nTracksAfterB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksAfterA[n], nTracksAfterB[n]); + if (tc.fPrintSwitch) { + LOGF(warning, "etagap=%f, cent=%f, nTracksAfterA = %d, nTracksAfterB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksAfterA[n], nTracksAfterB[n]); + } } } } @@ -1528,7 +1550,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eBefore][i]); } } else { - LOGF(warning, "cent=%f, nTracksBefore = %d, wFourRecursionBefore = %e", rlCollisionCent, nTracksBefore, wFourRecursionBefore); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wFourRecursionBefore = %e", rlCollisionCent, nTracksBefore, wFourRecursionBefore); + } } // After cut: @@ -1536,7 +1560,8 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double wFourRecursionAfter = mccRecursion(4, harmonicsFourDen, eAfter).Re(); ebye.fFourParticleCorrelationEbye[eAfter][i] = fourRecursionAfter.Re(); - if (nTracksAfter > k - 1 && wFourRecursionAfter > 0.) { + // LOGF(info, "nTracksAfter = %d", nTracksAfter); + if (nTracksAfter > k - 1 && wFourRecursionAfter > 0. && bNumTrackCut) { // Fill vn^2vm^2 profile and histogram: mc.fFourParticleCorrelationProfiles[eAfter]->Fill(mc.fFourParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); mc.fFourParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i]); @@ -1552,7 +1577,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eAfter][i]); } } else { - LOGF(warning, "cent=%f, nTracksAfter = %d, wFourRecursionAfter = %e", rlCollisionCent, nTracksAfter, wFourRecursionAfter); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wFourRecursionAfter = %e", rlCollisionCent, nTracksAfter, wFourRecursionAfter); + } } } @@ -1589,7 +1616,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eBefore][i]); } } else { - LOGF(warning, "cent=%f, nTracksBefore = %d, wSixRecursionBefore = %e", rlCollisionCent, nTracksBefore, wSixRecursionBefore); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wSixRecursionBefore = %e", rlCollisionCent, nTracksBefore, wSixRecursionBefore); + } } // After cut: @@ -1597,7 +1626,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double wSixRecursionAfter = mccRecursion(6, harmonicsSixDen, eAfter).Re(); ebye.fSixParticleCorrelationEbye[eAfter][i] = sixRecursionAfter.Re(); - if (nTracksAfter > k - 1 && wSixRecursionAfter > 0.) { + if (nTracksAfter > k - 1 && wSixRecursionAfter > 0. && bNumTrackCut) { // Fill v2^2v3^2v4^2 profile and histogram: mc.fSixParticleCorrelationProfiles[eAfter]->Fill(mc.fSixParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fSixParticleCorrelationEbye[eAfter][i], wSixRecursionAfter); mc.fSixParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fSixParticleCorrelationEbye[eAfter][i]); @@ -1613,7 +1642,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam LOGF(info, "min v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eAfter][i]); } } else { - LOGF(warning, "cent=%f, nTracksAfter = %d, wSixRecursionAfter = %e", rlCollisionCent, nTracksAfter, wSixRecursionAfter); + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wSixRecursionAfter = %e", rlCollisionCent, nTracksAfter, wSixRecursionAfter); + } } } } @@ -1684,7 +1715,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam ev.fEventHistograms[histType][eSim][ba] = new TH1F(nameSim.c_str(), nameSimfull.c_str(), nBinsCent, minCent, maxCent); ev.fEventHistograms[histType][eSim][ba]->GetXaxis()->SetTitle(EventHistNames[histType]); ev.fEventHistogramsList->Add(ev.fEventHistograms[histType][eSim][ba]); - } // No nContrib and multiplicity for processSim + } // No nContrib, multiplicity and nTrack for processSim } } } @@ -1939,6 +1970,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fSel8CutSwitch = cfSel8CutSwitch; tc.fCentCutSwitch = cfCentCutSwitch; tc.fNumContribCutSwitch = cfNumContribCutSwitch; + tc.fNumTrackCutSwitch = cfNumTrackCutSwitch; tc.fCentCorrCutSwitch = cfCentCorrCutSwitch; tc.fMultCorrCutSwitch = cfMultCorrCutSwitch; tc.fNumContribMultCorrCutSwitch = cfNumContribMultCorrCutSwitch; @@ -1954,6 +1986,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fVertexZCut = cfVertexZCut; tc.fCentCut = cfCentCut; tc.fNumContribCut = cfNumContribCut; + tc.fNumTrackCut = cfNumTrackCut; tc.fCentAllCorrCut[0][1] = cfCentABCorrCut; tc.fCentAllCorrCut[0][2] = cfCentACCorrCut; tc.fCentAllCorrCut[1][2] = cfCentBCCorrCut; @@ -1983,6 +2016,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fVerYBins = cfVerYBins; tc.fVerZBins = cfVerZBins; tc.fNumContribBins = cfNumContribBins; + tc.fNumTrackBins = cfNumTrackBins; tc.fTwoParticleCorrBins = cfTwoParticleCorrBins; tc.fFourParticleCorrBins = cfFourParticleCorrBins; tc.fSixParticleCorrBins = cfSixParticleCorrBins; @@ -2040,7 +2074,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam fBaseList->Add(mc.fMultiparticleCorrelationProfilesList); std::vector> lPcBins = {tc.fPtBins, tc.fPhiBins}; - std::vector> lEvBins = {tc.fCentBins, tc.fMultBins, tc.fVerXBins, tc.fVerYBins, tc.fVerZBins, tc.fNumContribBins}; + std::vector> lEvBins = {tc.fCentBins, tc.fMultBins, tc.fVerXBins, tc.fVerYBins, tc.fVerZBins, tc.fNumContribBins, tc.fNumTrackBins}; std::vector> lQABins = {tc.fCentBins, tc.fMultBins, tc.fNumContribBins}; std::vector> lCrBins = {tc.fCentBins, tc.fFT0CMultBins, tc.fFT0MMultBins, tc.fFV0AMultBins, tc.fNumContribBins, tc.fMultBins}; @@ -2052,6 +2086,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bookEventHistograms(lEvBins); bookEventHistograms(lEvBins); bookEventHistograms(lEvBins); + bookEventHistograms(lEvBins); bookQAHistograms(lQABins); bookQAHistograms(lQABins); bookCorrHistograms(lCrBins); From 298bd1d22e3e9fbb98477ef4d999ffe557e7ee78 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:20:03 +0200 Subject: [PATCH 023/648] [PWGCF] reduce ccdb calls and remove redundant histogram bookings in flowGenericFramework.cxx (#17993) codecheck will be fixed in the next iteration --- .../Tasks/flowGenericFramework.cxx | 213 ++++++++++++------ 1 file changed, 148 insertions(+), 65 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx index 91e05af2fe7..629804bbbbe 100644 --- a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx @@ -287,6 +287,7 @@ struct FlowGenericFramework { TH1* mEfficiency = nullptr; std::vector mPIDEfficiencies; std::vector mAcceptance; + std::map>> mAcceptanceByRun; bool correctionsLoaded = false; } cfg; @@ -433,6 +434,10 @@ struct FlowGenericFramework { LambdaSideband2, ResonanceCount }; + enum DataType { + Reco, + Gen + }; enum OutputSpecies { K0 = 0, Lambda, @@ -853,17 +858,9 @@ struct FlowGenericFramework { } if (cfgFill.cfgAnalyseK0Lambda) { registryGen.add("MCGen/npt_v02_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v02_K0_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v02_K0_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); registryGen.add("MCGen/npt_v02_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v02_Lambda_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v02_Lambda_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); registryGen.add("MCGen/npt_v0_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v0_K0_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v0_K0_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); registryGen.add("MCGen/npt_v0_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v0_Lambda_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registryGen.add("MCGen/npt_v0_Lambda_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); } } if (cfgMethod.cfgStoreCombinedFractionWeights) { @@ -882,7 +879,7 @@ struct FlowGenericFramework { if (doprocessData || doprocessRun2 || doprocessMCReco || doprocessMC) { registry.addClone(source, target); } - if (doprocessMCGen || doprocessOnTheFly || doprocessMC) { + if ((doprocessMCGen || doprocessOnTheFly || doprocessMC) && source.find("_sb") == std::string::npos) { registryGen.addClone("MCGen/" + source, "MCGen/" + target); } } @@ -942,21 +939,31 @@ struct FlowGenericFramework { if (corrconfigsV0.empty()) { LOGF(error, "Radial (V0) configuration contains vectors of different size - check the GFWCorrConfig configurable"); } + const size_t requiredChargedConfigs = static_cast(SpeciesCount); + const size_t requiredResonanceConfigs = requiredChargedConfigs + static_cast(ResonanceCount); + if (cfgFill.cfgAnalyseChargedHadrons && (corrconfigsV02.size() < requiredChargedConfigs || corrconfigsV0.size() < requiredChargedConfigs)) { + LOGF(fatal, "Charged-hadron analysis requires at least %zu V02 and V0 correlation configurations", requiredChargedConfigs); + } + if (cfgFill.cfgAnalyseK0Lambda && (corrconfigsV02.size() < requiredResonanceConfigs || corrconfigsV0.size() < requiredResonanceConfigs)) { + LOGF(fatal, "K0/Lambda analysis requires at least %zu V02 and V0 correlation configurations", requiredResonanceConfigs); + } fGFW->CreateRegions(); + const size_t v02ResonanceEnd = std::min(requiredResonanceConfigs, corrconfigsV02.size()); + const size_t v0ResonanceEnd = std::min(requiredResonanceConfigs, corrconfigsV0.size()); auto oba = new TObjArray(); - addConfigObjectsToObjArray(oba, corrconfigs, 0, corrconfigs.size()); + addConfigObjectsToObjArray(oba, corrconfigs, 0, corrconfigs.size()); if (cfgFill.cfgAnalyseChargedHadrons) { - addConfigObjectsToObjArray(oba, corrconfigsV02, 0, std::min(SpeciesCount, corrconfigsV02.size())); + addConfigObjectsToObjArray(oba, corrconfigsV02, 0, std::min(SpeciesCount, corrconfigsV02.size())); } if (cfgFill.cfgAnalyseK0Lambda) { - addConfigObjectsToObjArray(oba, corrconfigsV02, std::min(SpeciesCount, corrconfigsV02.size()), corrconfigsV02.size()); + addConfigObjectsToObjArray(oba, corrconfigsV02, std::min(SpeciesCount, corrconfigsV02.size()), v02ResonanceEnd); } if (cfgFill.cfgAnalyseChargedHadrons) { - addConfigObjectsToObjArray(oba, corrconfigsV0, 0, std::min(SpeciesCount, corrconfigsV0.size())); + addConfigObjectsToObjArray(oba, corrconfigsV0, 0, std::min(SpeciesCount, corrconfigsV0.size())); } if (cfgFill.cfgAnalyseK0Lambda) { - addConfigObjectsToObjArray(oba, corrconfigsV0, std::min(SpeciesCount, corrconfigsV0.size()), corrconfigsV0.size()); + addConfigObjectsToObjArray(oba, corrconfigsV0, std::min(SpeciesCount, corrconfigsV0.size()), v0ResonanceEnd); } if (doprocessData || doprocessRun2 || doprocessMCReco || doprocessMC) { @@ -981,10 +988,25 @@ struct FlowGenericFramework { } fFCpt->initialiseSubevent(multAxis, cfgMpar, gfwMemberCache.etagapsPtPt.size(), cfgNbootstrap); } + delete oba; + auto obaGen = new TObjArray(); + addConfigObjectsToObjArray(obaGen, corrconfigs, 0, corrconfigs.size()); + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(obaGen, corrconfigsV02, 0, std::min(SpeciesCount, corrconfigsV02.size())); + } + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(obaGen, corrconfigsV02, std::min(SpeciesCount, corrconfigsV02.size()), v02ResonanceEnd, true); + } + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(obaGen, corrconfigsV0, 0, std::min(SpeciesCount, corrconfigsV0.size())); + } + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(obaGen, corrconfigsV0, std::min(SpeciesCount, corrconfigsV0.size()), v0ResonanceEnd, true); + } if (doprocessMCGen || doprocessOnTheFly || doprocessMC) { fFCgen.setObject(new FlowContainer("FlowContainer_gen")); fFCgen->SetXAxis(fPtAxis); - fFCgen->Initialize(oba, multAxis, cfgNbootstrap); + fFCgen->Initialize(obaGen, multAxis, cfgNbootstrap); fFCptGen.setObject(new FlowPtContainer("FlowPtContainer_gen")); fFCptGen->setEventWeight(cfgMethod.cfgUsePtCorrWeights ? eventweight::TupleWeight : eventweight::UnityWeight); @@ -1003,7 +1025,7 @@ struct FlowGenericFramework { } fFCptGen->initialiseSubevent(multAxis, cfgMpar, gfwMemberCache.etagapsPtPt.size(), cfgNbootstrap); } - delete oba; + delete obaGen; fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str()), 0.001, 100); fPtDepDCAxy->SetParameter(0, cfgTrackCuts.cfgDCAxyNSigma / 7.); LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str())); @@ -1166,9 +1188,26 @@ struct FlowGenericFramework { After }; - void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs, size_t first, size_t last) + template + void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs, size_t first, size_t last, bool resonanceConfigs = false) { + if (first > last || last > configs.size()) { + LOGF(fatal, "Invalid correlation configuration range [%zu, %zu) for %zu entries", first, last, configs.size()); + } + if constexpr (dt == Gen) { + if (resonanceConfigs && first < static_cast(SpeciesCount)) { + LOGF(fatal, "Generated resonance configurations must start at index %d or later", static_cast(SpeciesCount)); + } + } for (auto it = configs.begin() + first; it != configs.begin() + last; ++it) { + if constexpr (dt == Gen) { + if (resonanceConfigs) { + const auto resonanceIndex = static_cast(std::distance(configs.begin(), it)) - SpeciesCount; + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } + } if (it->pTDif) { std::string suffix = "_ptDiff"; for (auto i = 0; i < fPtAxis->GetNbins(); ++i) { @@ -1205,16 +1244,42 @@ struct FlowGenericFramework { return; } if (!cfgAcceptance.value.empty()) { - std::string runstr = (cfgFill.cfgRunByRun) ? "RunByRun/" : ""; + const int acceptanceKey = cfgFill.cfgRunByRun ? bc.runNumber() : 0; + auto cachedAcceptance = cfg.mAcceptanceByRun.find(acceptanceKey); + if (cachedAcceptance == cfg.mAcceptanceByRun.end()) { + std::vector> acceptanceWeights; + auto loadAcceptance = [&](const std::string& path) { + auto* weights = ccdb->getForTimeStamp(path, timestamp); + if (weights == nullptr) { + LOGF(fatal, "Could not load acceptance weights from %s", path.c_str()); + return; + } + auto* clonedWeights = dynamic_cast(weights->Clone()); + if (clonedWeights == nullptr) { + LOGF(fatal, "Could not clone acceptance weights from %s", path.c_str()); + return; + } + acceptanceWeights.emplace_back(clonedWeights); + }; + + std::string runstr = (cfgFill.cfgRunByRun) ? "RunByRun/" : ""; + if (cfgFill.cfgUsePID) { + loadAcceptance(cfgAcceptance.value + runstr + "ref/"); + loadAcceptance(cfgAcceptance.value + runstr + "ch/"); + loadAcceptance(cfgAcceptance.value + runstr + "pi/"); + loadAcceptance(cfgAcceptance.value + runstr + "ka/"); + loadAcceptance(cfgAcceptance.value + runstr + "pr/"); + } else { + loadAcceptance(cfgAcceptance.value + runstr); + } + cachedAcceptance = cfg.mAcceptanceByRun.emplace(acceptanceKey, std::move(acceptanceWeights)).first; + LOGF(info, "Loaded acceptance weights for run %d", bc.runNumber()); + } + cfg.mAcceptance.clear(); - if (cfgFill.cfgUsePID) { - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ref/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ch/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "pi/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ka/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "pr/", timestamp)); - } else { - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr, timestamp)); + cfg.mAcceptance.reserve(cachedAcceptance->second.size()); + for (const auto& weights : cachedAcceptance->second) { + cfg.mAcceptance.push_back(weights.get()); } } // Run-by-run efficiencies are not supported at the moment @@ -1529,11 +1594,6 @@ struct FlowGenericFramework { return ((track.tpcNClsCrossedRows() >= tpcNClsCrossedRowsDefault) && (track.tpcNClsFound() >= tpcNClsFoundDefault) && (track.itsNCls() >= itsNclsDefault)); } - enum DataType { - Reco, - Gen - }; - template void fillWeights(const TTrack& track, const double vtxz, const int pid_index, const int run) { @@ -1664,6 +1724,11 @@ struct FlowGenericFramework { (resonance ? !cfgFill.cfgAnalyseK0Lambda : !cfgFill.cfgAnalyseChargedHadrons)) { return; } + if constexpr (dt == Gen) { + if (resonance && index != K0Signal && index != LambdaSignal) { + return; + } + } if (setup == FractionV02) { if (resonance) { switch (index) { @@ -1964,41 +2029,50 @@ struct FlowGenericFramework { } for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { - if (dnK0SB1 > 0) { - fillFractionProfile
(HIST("npt_v02_K0_sb1"), HIST("MCGen/npt_v02_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + if constexpr (dt != Gen) { + if (dnK0SB1 > 0) { + fillFractionProfile
(HIST("npt_v02_K0_sb1"), HIST("MCGen/npt_v02_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + } + if (dnK0SB2 > 0) { + fillFractionProfile
(HIST("npt_v02_K0_sb2"), HIST("MCGen/npt_v02_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); + } + if (dnLambdaSB1 > 0) { + fillFractionProfile
(HIST("npt_v02_Lambda_sb1"), HIST("MCGen/npt_v02_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); + } + if (dnLambdaSB2 > 0) { + fillFractionProfile
(HIST("npt_v02_Lambda_sb2"), HIST("MCGen/npt_v02_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); + } } if (dnK0Sig > 0) { fillFractionProfile
(HIST("npt_v02_K0_sig"), HIST("MCGen/npt_v02_K0_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Signal]->GetBinContent(i) / dnK0Sig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0Sig : 1.); } - if (dnK0SB2 > 0) { - fillFractionProfile
(HIST("npt_v02_K0_sb2"), HIST("MCGen/npt_v02_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); - } - if (dnLambdaSB1 > 0) { - fillFractionProfile
(HIST("npt_v02_Lambda_sb1"), HIST("MCGen/npt_v02_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); - } if (dnLambdaSig > 0) { fillFractionProfile
(HIST("npt_v02_Lambda_sig"), HIST("MCGen/npt_v02_Lambda_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSignal]->GetBinContent(i) / dnLambdaSig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSig : 1.); } - if (dnLambdaSB2 > 0) { - fillFractionProfile
(HIST("npt_v02_Lambda_sb2"), HIST("MCGen/npt_v02_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); - } } - std::vector dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; + std::array dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; - for (uint l_ind = 4; l_ind < corrconfigsV02.size(); ++l_ind) { + const size_t resonanceConfigEnd = std::min(static_cast(SpeciesCount) + static_cast(ResonanceCount), corrconfigsV02.size()); + for (size_t l_ind = SpeciesCount; l_ind < resonanceConfigEnd; ++l_ind) { + const auto resonanceIndex = l_ind - SpeciesCount; + if constexpr (dt == Gen) { + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } for (int i = 1; i <= fPtAxis->GetNbins(); i++) { auto dnx = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kTRUE).real(); if (dnx == 0) { continue; } auto val = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kFALSE).real() / dnx; - if (std::abs(val) < 1 && dns[l_ind - 4] > 0) { - fillCombinedFractionProfile
(FractionV02, l_ind - SpeciesCount, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][l_ind - SpeciesCount]->GetBinContent(i) / dns[l_ind - SpeciesCount], dnx * dns[l_ind - SpeciesCount]); + if (std::abs(val) < 1 && dns[resonanceIndex] > 0) { + fillCombinedFractionProfile
(FractionV02, resonanceIndex, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], dnx * dns[resonanceIndex]); if (cfgMethod.cfgUseMultiplicityFractionWeights) { - dnx *= dns[l_ind - 4]; + dnx *= dns[resonanceIndex]; } - flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * histosResoNpt[FractionV02][l_ind - 4]->GetBinContent(i) / dns[l_ind - 4], cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * histosResoNpt[FractionV02][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); } } } @@ -2024,27 +2098,29 @@ struct FlowGenericFramework { } for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { - if (dnK0SB1 > 0) { - fillFractionProfile
(HIST("npt_v0_K0_sb1"), HIST("MCGen/npt_v0_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + if constexpr (dt != Gen) { + if (dnK0SB1 > 0) { + fillFractionProfile
(HIST("npt_v0_K0_sb1"), HIST("MCGen/npt_v0_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + } + if (dnK0SB2 > 0) { + fillFractionProfile
(HIST("npt_v0_K0_sb2"), HIST("MCGen/npt_v0_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); + } + if (dnLambdaSB1 > 0) { + fillFractionProfile
(HIST("npt_v0_Lambda_sb1"), HIST("MCGen/npt_v0_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); + } + if (dnLambdaSB2 > 0) { + fillFractionProfile
(HIST("npt_v0_Lambda_sb2"), HIST("MCGen/npt_v0_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); + } } if (dnK0Sig > 0) { fillFractionProfile
(HIST("npt_v0_K0_sig"), HIST("MCGen/npt_v0_K0_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Signal]->GetBinContent(i) / dnK0Sig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0Sig : 1.); } - if (dnK0SB2 > 0) { - fillFractionProfile
(HIST("npt_v0_K0_sb2"), HIST("MCGen/npt_v0_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); - } - if (dnLambdaSB1 > 0) { - fillFractionProfile
(HIST("npt_v0_Lambda_sb1"), HIST("MCGen/npt_v0_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); - } if (dnLambdaSig > 0) { fillFractionProfile
(HIST("npt_v0_Lambda_sig"), HIST("MCGen/npt_v0_Lambda_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSignal]->GetBinContent(i) / dnLambdaSig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSig : 1.); } - if (dnLambdaSB2 > 0) { - fillFractionProfile
(HIST("npt_v0_Lambda_sb2"), HIST("MCGen/npt_v0_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); - } } - std::vector dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; + std::array dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; if (flowPtContainer.corrDenSub[0][1] == 0. || flowPtContainer.corrDenSub[1][1] == 0.) { return; @@ -2072,18 +2148,25 @@ struct FlowGenericFramework { return; } - for (uint l_ind = 4; l_ind < corrconfigsV0.size(); ++l_ind) { + const size_t resonanceConfigEnd = std::min(static_cast(SpeciesCount) + static_cast(ResonanceCount), corrconfigsV0.size()); + for (size_t l_ind = SpeciesCount; l_ind < resonanceConfigEnd; ++l_ind) { + const auto resonanceIndex = l_ind - SpeciesCount; + if constexpr (dt == Gen) { + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } for (int i = 1; i <= fPtAxis->GetNbins(); i++) { - if (dns[l_ind - 4] <= 0) { + if (dns[resonanceIndex] <= 0) { continue; } double profileWeight = dnx; if (cfgMethod.cfgUseMultiplicityFractionWeights) { - profileWeight *= dns[l_ind - 4]; + profileWeight *= dns[resonanceIndex]; } - const double value = mpt * histosResoNpt[FractionV0][l_ind - 4]->GetBinContent(i) / dns[l_ind - 4]; + const double value = mpt * histosResoNpt[FractionV0][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex]; const double weight = cfgMethod.cfgUsePtCorrWeights ? profileWeight : 1.0; - fillCombinedFractionProfile
(FractionV0, l_ind - SpeciesCount, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][l_ind - SpeciesCount]->GetBinContent(i) / dns[l_ind - SpeciesCount], flowPtContainer.corrDen[1] * dns[l_ind - SpeciesCount]); + fillCombinedFractionProfile
(FractionV0, resonanceIndex, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], flowPtContainer.corrDen[1] * dns[resonanceIndex]); flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centmult, value, weight, rndm); } } From a183d02949f43490a7ac44fce1acf31c7e170619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 21 Sep 2026 23:40:45 +0200 Subject: [PATCH 024/648] [PWGLF] Fix code check warnings in previous PR (#17985) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 6eea1a825d2..cead75239f5 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -620,9 +620,8 @@ struct HadronNucleiCorrelation { return; } - float deltaEta = part0.eta() - part1.eta(); - float deltaPhi = part0.phi() - part1.phi(); - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -1 * o2::constants::math::PIHalf); + const float deltaEta = part0.eta() - part1.eta(); + const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); for (int k = 0; k < nBinspT; k++) { @@ -691,9 +690,8 @@ struct HadronNucleiCorrelation { void fillHistogramsGen(T1 const& part0, T1 const& part1, const bool ME) { - float deltaEta = part0.eta() - part1.eta(); - float deltaPhi = part0.phi() - part1.phi(); - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -1 * o2::constants::math::PIHalf); + const float deltaEta = part0.eta() - part1.eta(); + const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); // Here we have to use doRapidity const float deltaRapidity = part0.rapidityForPdg(pdgPart0) - part1.rapidityForPdg(pdgPart1); for (int k = 0; k < nBinspT; k++) { From c9c2b5491130471729e07a341a568e9cc5a5624f Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Tue, 22 Sep 2026 08:42:36 +0200 Subject: [PATCH 025/648] [Common] pidTPCModule::createNetworkPrediction(): refactor and add NN version 5 (#17912) --- Common/Tools/PID/pidTPCModule.h | 199 ++++++++++++++++++-------------- 1 file changed, 112 insertions(+), 87 deletions(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 292b3317075..b38bc532c44 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -24,12 +24,15 @@ #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/PID/TPCPIDResponse.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/TableProducer/PID/pidTPCBase.h" // IWYU pragma: keep #include "Tools/ML/model.h" +#include +#include #include #include #include @@ -46,6 +49,7 @@ #include #include +#include #include #include #include @@ -54,6 +58,7 @@ #include #include #include +#include #include #include @@ -139,6 +144,9 @@ struct pidTPCConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable useNetworkAl{"useNetworkAl", 1, {"Switch for applying neural network on the alpha mass hypothesis (if network enabled) (set to 0 to disable)"}}; o2::framework::Configurable networkBetaGammaCutoff{"networkBetaGammaCutoff", 0.45, {"Lower value of beta-gamma to override the NN application"}}; o2::framework::Configurable cfgPathGrpLhcIf{"ccdb-path-grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + + o2::framework::Configurable phiEntranceCoeff1{"phiEntranceCoeff1", 1.026f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"}; + o2::framework::Configurable phiEntranceCoeff2{"phiEntranceCoeff2", 85.f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"}; }; // helper getter - FIXME should be separate @@ -445,7 +453,48 @@ class pidTPCModule template std::unique_ptr createNetworkPrediction(TCCDB& ccdb, soa::Join const& collisions, M const& mults, T const& tracks, B const& bcs, const size_t size) { - auto start_network_total = std::chrono::high_resolution_clock::now(); + constexpr int NParticleTypes = 9; + constexpr double OneToKilo = 1.e-3; + constexpr int NanoToOne = 1000000000; + constexpr double MultiplicityNorm = 11000.; + constexpr double HadronicRateNormPp = 1500.; + constexpr double HadronicRateNormAa = 50.; + constexpr double Ft0cOccupancyNorm = 60000.; + constexpr float LightSpeedDm2PS = o2::constants::physics::LightSpeedCm2PS / 10.f; + + struct NNVersionEntry { + std::string_view versionName{}; + int numberOfFeatures{}; + int versionNumber{}; + }; + + constexpr std::array NNVersionsDictionary{ + {{"", 6, 1}, + {"1", 6, 1}, + {"2", 7, 2}, + {"3", 8, 3}, + {"4", 9, 4}, + {"5", 9, 5}}}; + + enum IndexNnFeature : int { + IdxTpcInnerParam = 0, + IdxTgl, + IdxSigned1Pt, + IdxMass, + IdxMultiplicity, + IdxNClusters, + IdxFt0cOcc, + IdxHadronicRate, + IdxModPhi + }; + + constexpr int OldestNNVersionWithFt0c{2}; + constexpr int OldestNNVersionWithHadronicRate{3}; + constexpr int OldestNNVersionWithModPhi{4}; + constexpr int NNVersionWithModPhiEntrance{5}; + + const auto startNetworkTotal = std::chrono::high_resolution_clock::now(); + if (pidTPCopts.autofetchNetworks) { const auto& bc = bcs.begin(); // Initialise correct TPC response object before NN setup (for NCl normalisation) @@ -483,7 +532,7 @@ class pidTPCModule if (bc.timestamp() < network.getValidityFrom() || bc.timestamp() > network.getValidityUntil()) { // fetches network only if the runnumbers change LOG(info) << "Fetching network for timestamp: " << bc.timestamp(); - bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers); + const bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers); networkVersion = headers["NN-Version"]; if (retrieveSuccess) { network.initModel(pidTPCopts.networkPathLocally.value, pidTPCopts.enableNetworkOptimizations.value, pidTPCopts.networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0)); @@ -497,52 +546,52 @@ class pidTPCModule } // Defining some network parameters - int input_dimensions = network.getNumInputNodes(); - int output_dimensions = network.getNumOutputNodes(); - const uint64_t track_prop_size = input_dimensions * size; - const uint64_t prediction_size = output_dimensions * size; + const int inputDimensions = network.getNumInputNodes(); + const int outputDimensions = network.getNumOutputNodes(); + const uint64_t trackPropSize = inputDimensions * size; + const uint64_t predictionSize = outputDimensions * size; + + int nnVersion{0}; + for (const auto& nnVersionEntry : NNVersionsDictionary) { + if (networkVersion == nnVersionEntry.versionName && inputDimensions == nnVersionEntry.numberOfFeatures) { + nnVersion = nnVersionEntry.versionNumber; + break; + } + } + if (nnVersion == 0) { + LOG(fatal) << "createNetworkPrediction(): networkVersion '" << networkVersion << "' and number of features " << inputDimensions << " are not compatible according to NNVersionsDictionary"; + } + + const int hadronicRateNorm = collsys == CollisionSystemType::kCollSyspp ? HadronicRateNormPp : HadronicRateNormAa; // Deliberately uninitialised: the evaluation loop below writes every element // (one block per mass hypothesis), so zero-initialising would only touch // every page of an O(100 MB) buffer twice. - std::unique_ptr network_prediction(new float[prediction_size * 9]); // For each mass hypotheses + std::unique_ptr networkPrediction(new float[predictionSize * NParticleTypes]); // For each mass hypotheses + const float nNclNormalization = response->GetNClNormalization(); - float duration_network = 0; + float durationNetwork = 0; - std::vector track_properties(track_prop_size); - uint64_t counter_track_props = 0; - int loop_counter = 0; + std::vector trackProperties(trackPropSize); + uint64_t counterTrackProps = 0; + int loopCounter = 0; // To load the Hadronic rate once for each collision - float hadronicRateBegin = 0.; std::vector hadronicRateForCollision(collisions.size(), 0.0f); - size_t i = 0; + size_t iCollision = 0; for (const auto& collision : collisions) { const auto& bc = collision.template bc_as(); if (irSource.compare("") != 0) { - hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; - } else { - hadronicRateForCollision[i] = 0.0f; + hadronicRateForCollision[iCollision] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo; } - i++; - } - auto bc = bcs.begin(); - if (irSource.compare("") != 0) { - hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz - } else { - hadronicRateBegin = 0.0f; + ++iCollision; } + const auto bc = bcs.begin(); + const float hadronicRateBegin = irSource.compare("") != 0 ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.f; // Filling a std::vector to be evaluated by the network // Evaluation on single tracks brings huge overhead: Thus evaluation is done on one large vector - static constexpr int NParticleTypes = 9; - constexpr int ExpectedInputDimensionsNNV2 = 7; - constexpr int ExpectedInputDimensionsNNV3 = 8; - constexpr int ExpectedInputDimensionsNNV4 = 9; - constexpr auto NetworkVersionV2 = "2"; - constexpr auto NetworkVersionV3 = "3"; - constexpr auto NetworkVersionV4 = "4"; - for (int j = 0; j < NParticleTypes; j++) { // Loop over particle number for which network correction is used + for (int jParticleType = 0; jParticleType < NParticleTypes; ++jParticleType) { // Loop over particle number for which network correction is used for (auto const& trk : tracks) { if (!trk.hasTPC()) { continue; @@ -552,74 +601,50 @@ class pidTPCModule continue; } } - track_properties[counter_track_props] = trk.tpcInnerParam(); - track_properties[counter_track_props + 1] = trk.tgl(); - track_properties[counter_track_props + 2] = trk.signed1Pt(); - track_properties[counter_track_props + 3] = o2::track::pid_constants::sMasses[j]; - track_properties[counter_track_props + 4] = (trk.has_collision() && mults.size() > 0) ? mults[trk.collisionId()] / 11000. : 1.; - track_properties[counter_track_props + 5] = std::sqrt(nNclNormalization / trk.tpcNClsFound()); - if (input_dimensions == ExpectedInputDimensionsNNV2 && networkVersion == NetworkVersionV2) { - track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; + const bool isGoodTrack = trk.has_collision() && mults.size() > 0; + trackProperties[counterTrackProps + IdxTpcInnerParam] = trk.tpcInnerParam(); + trackProperties[counterTrackProps + IdxTgl] = trk.tgl(); + trackProperties[counterTrackProps + IdxSigned1Pt] = trk.signed1Pt(); + trackProperties[counterTrackProps + IdxMass] = o2::track::pid_constants::sMasses[jParticleType]; + trackProperties[counterTrackProps + IdxMultiplicity] = isGoodTrack ? mults[trk.collisionId()] / MultiplicityNorm : 1.; + trackProperties[counterTrackProps + IdxNClusters] = std::sqrt(nNclNormalization / trk.tpcNClsFound()); + if (nnVersion >= OldestNNVersionWithFt0c) { + trackProperties[counterTrackProps + IdxFt0cOcc] = isGoodTrack ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / Ft0cOccupancyNorm : 1.; } - if (input_dimensions == ExpectedInputDimensionsNNV3 && networkVersion == NetworkVersionV3) { - track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - if (trk.has_collision() && mults.size() > 0) { - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.; - } - } else { - // asign Hadronic Rate at beginning of run if track does not belong to a collision - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateBegin / 50.; - } - } + if (nnVersion >= OldestNNVersionWithHadronicRate) { + const float hadronicRate = isGoodTrack ? hadronicRateForCollision[trk.collisionId()] : hadronicRateBegin; + trackProperties[counterTrackProps + IdxHadronicRate] = hadronicRate / hadronicRateNorm; } - - if (input_dimensions == ExpectedInputDimensionsNNV4 && networkVersion == NetworkVersionV4) { - track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - if (trk.has_collision() && mults.size() > 0) { - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.; - } - } else { - // asign Hadronic Rate at beginning of run if track does not belong to a collision - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateBegin / 50.; - } + if (nnVersion >= OldestNNVersionWithModPhi) { + float phi = trk.phi(); + if (nnVersion == NNVersionWithModPhiEntrance) { + phi += pidTPCopts.phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * pidTPCopts.phiEntranceCoeff2 * trk.signed1Pt(); } - track_properties[counter_track_props + 8] = std::fmod(std::fmod(trk.phi(), 2 * M_PI) + 2 * M_PI, M_PI / 9.0); + trackProperties[counterTrackProps + IdxModPhi] = RecoDecay::constrainAngle(phi, 0.f, o2::constants::math::NSectors); } - counter_track_props += input_dimensions; + counterTrackProps += inputDimensions; } - auto start_network_eval = std::chrono::high_resolution_clock::now(); - float* output_network = network.evalModel(track_properties); - auto stop_network_eval = std::chrono::high_resolution_clock::now(); - duration_network += std::chrono::duration>(stop_network_eval - start_network_eval).count(); - for (uint64_t k = 0; k < prediction_size; k += output_dimensions) { - for (int l = 0; l < output_dimensions; l++) { - network_prediction[k + l + prediction_size * loop_counter] = output_network[k + l]; + const auto startNetworkEval = std::chrono::high_resolution_clock::now(); + const float* const outputNetwork = network.evalModel(trackProperties); + const auto stopNetworkEval = std::chrono::high_resolution_clock::now(); + durationNetwork += std::chrono::duration>(stopNetworkEval - startNetworkEval).count(); + for (uint64_t kPrediction = 0; kPrediction < predictionSize; kPrediction += outputDimensions) { + for (int lOutputDim = 0; lOutputDim < outputDimensions; ++lOutputDim) { + networkPrediction[kPrediction + lOutputDim + predictionSize * loopCounter] = outputNetwork[kPrediction + lOutputDim]; } } - counter_track_props = 0; - loop_counter += 1; + counterTrackProps = 0; + ++loopCounter; } - track_properties.clear(); + trackProperties.clear(); - auto stop_network_total = std::chrono::high_resolution_clock::now(); - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << duration_network / (size * 9) << "ns ; Total time (eval ONNX): " << duration_network / 1000000000 << " s"; - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / (size * 9) << "ns ; Total time (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / 1000000000 << " s"; + const auto stopNetworkTotal = std::chrono::high_resolution_clock::now(); + LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << durationNetwork / (size * NParticleTypes) << "ns ; Total time (eval ONNX): " << durationNetwork / NanoToOne << " s"; + LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration>(stopNetworkTotal - startNetworkTotal).count() / (size * NParticleTypes) << "ns ; Total time (eval + overhead): " << std::chrono::duration>(stopNetworkTotal - startNetworkTotal).count() / NanoToOne << " s"; - return network_prediction; + return networkPrediction; } //__________________________________________________ From 9982cad036c408daafd79a79c05d20544b9a3f80 Mon Sep 17 00:00:00 2001 From: sashingo Date: Tue, 22 Sep 2026 16:43:38 +0900 Subject: [PATCH 026/648] [PWGHF] Add MC efficiency and improve Z-h correlation (#17992) --- PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx | 588 ++++++++++++++++------ 1 file changed, 424 insertions(+), 164 deletions(-) diff --git a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx index ac168cf5d3e..78f9ac10578 100644 --- a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx +++ b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx @@ -44,9 +44,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -84,7 +86,6 @@ struct HfTaskElectronWeakBoson { Configurable chi2ItsMax{"chi2ItsMax", 15.0f, "its chi2 cut"}; Configurable ptMin{"ptMin", 3.0f, "minimum pT cut"}; Configurable ptAssMin{"ptAssMin", 0.15, "minimum pT cut for associated hadrons"}; - Configurable ptMatch{"ptMatch", 0.001, "pT match in Z->ee and associated tracks"}; Configurable ptZeeMin{"ptZeeMin", 20.0f, "minimum pT cut for Zee"}; Configurable chi2TpcMax{"chi2TpcMax", 4.0f, "tpc chi2 cut"}; Configurable nclItsMin{"nclItsMin", 2.0f, "its # of cluster cut"}; @@ -140,24 +141,46 @@ struct HfTaskElectronWeakBoson { Configurable centralityMax{"centralityMax", 101, "maximum cut on centrality selection"}; Configurable> centralityBins{"centralityBins", {0, 20, 60, 100}, "centrality bins"}; - // QA for Z->ee - Configurable enableZeeTrkRecoQA{"enableZeeTrkRecoQA", false, "Enable QA for track info Z->ee reconstruction"}; - Configurable enableZeeEmcRecoQA{"enableZeeEmcRecoQA", false, "Enable QA for Emc info Z->ee reconstruction"}; - Configurable massZMinQA{"massZMinQA", 0.1, "minimum mass cut for Zee Reco QA"}; - // UE - Configurable nRandomCones{"nRandomCones", 100, "number of random cones"}; - Configurable rcHardE{"rcHardE", 5.0, "hard cluster veto energy"}; - Configurable rcVetoR{"rcVetoR", 0.4, "veto radius"}; - Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; + // QA for Z->ee. + // Grouped in a ConfigurableGroup to stay below the O2 limit of 100 task members. + struct : ConfigurableGroup { + Configurable enableZeeTrkRecoQA{"enableZeeTrkRecoQA", false, "Enable QA for track info Z->ee reconstruction"}; + Configurable enableZeeEmcRecoQA{"enableZeeEmcRecoQA", false, "Enable QA for Emc info Z->ee reconstruction"}; + Configurable massZMinQA{"massZMinQA", 0.1, "minimum mass cut for Zee Reco QA"}; + } zeeQaCfg; + + // UE (same remark: grouped, no prefix, option names unchanged) + struct : ConfigurableGroup { + Configurable nRandomCones{"nRandomCones", 100, "number of random cones"}; + Configurable rcHardE{"rcHardE", 5.0, "hard cluster veto energy"}; + Configurable rcVetoR{"rcVetoR", 0.4, "veto radius"}; + Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; + } ueCfg; + + // MC : efficiency of the associated hadrons in the Z-h correlation. + struct : ConfigurableGroup { + std::string prefix = "mcEfficiency"; // JSON group name + Configurable requirePhysicalPrimary{"requirePhysicalPrimary", true, "require isPhysicalPrimary for the generated associated hadrons"}; + Configurable requireSameMcCollision{"requireSameMcCollision", true, "require the matched MC particle to belong to the MC collision of the reconstructed one"}; + ConfigurableAxis confaxisPtHadron{"confaxisPtHadron", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 20.0, 30.0, 50.0, 100.0}, "p_{T,hadron} (GeV/c)"}; + ConfigurableAxis confaxisEtaHadron{"confaxisEtaHadron", {20, -1.0, 1.0}, "#eta_{hadron}"}; + } mcCfg; // CCDB service object Service ccdb{}; + // PDG database, used to get the charge of the generated particles + Service pdg{}; + + // TParticlePDG::Charge() is given in units of |e|/3, a charged particle has |Charge()| >= 1 + static constexpr double ChargeTolerance = 0.001; + struct HfElectronCandidate { - float pt, eta, phi, dcaxyTrk, dcazTrk, eop, energyIso, momIso, dedxTrk, m02Emc; - int ntrackIso, nclusterTPC, nclusterITS; - HfElectronCandidate(float ptr, float e, float ph, float dcaxy, float dcaz, float ep, float eiso, float piso, float dedx, float m02, int ntrkiso, int nclstpc, int nclsits) - : pt(ptr), eta(e), phi(ph), dcaxyTrk(dcaxy), dcazTrk(dcaz), eop(ep), energyIso(eiso), momIso(piso), dedxTrk(dedx), m02Emc(m02), ntrackIso(ntrkiso), nclusterTPC(nclstpc), nclusterITS(nclsits) {} + float pt, eta, phi, dcaxyTrk, dcazTrk, eop, energyIso, momIso, nSigmaTpc, m02Emc; + int ntrackIso, nclusterTPC, nclusterITS, sign; + int64_t indexTrk; // globalIndex of the track, used to remove the Z daughters exactly + HfElectronCandidate(float ptr, float e, float ph, float dcaxy, float dcaz, float ep, float eiso, float piso, float nsig, float m02, int ntrkiso, int nclstpc, int nclsits, int sgn, int64_t idxtrk) + : pt(ptr), eta(e), phi(ph), dcaxyTrk(dcaxy), dcazTrk(dcaz), eop(ep), energyIso(eiso), momIso(piso), nSigmaTpc(nsig), m02Emc(m02), ntrackIso(ntrkiso), nclusterTPC(nclstpc), nclusterITS(nclsits), sign(sgn), indexTrk(idxtrk) {} }; std::vector selectedElectronsIso; std::vector selectedPositronsIso; @@ -166,16 +189,23 @@ struct HfTaskElectronWeakBoson { struct HfZeeCandidate { float pt, eta, phi, mass, ptchild0, ptchild1; int charge; - HfZeeCandidate(float ptr, float e, float ph, float m, int ch, float ptzee0, float ptzee1) - : pt(ptr), eta(e), phi(ph), mass(m), ptchild0(ptzee0), ptchild1(ptzee1), charge(ch) {} + int64_t indexChild0, indexChild1; // globalIndex of the two tracks used to build the Z + HfZeeCandidate(float ptr, float e, float ph, float m, int ch, float ptzee0, float ptzee1, int64_t idxzee0, int64_t idxzee1) + : pt(ptr), eta(e), phi(ph), mass(m), ptchild0(ptzee0), ptchild1(ptzee1), charge(ch), indexChild0(idxzee0), indexChild1(idxzee1) {} }; std::vector reconstructedZ; - using CollisionsWithCent = soa::Join; + using CollisionsWithCent = soa::Join; using SelectedClusters = o2::aod::EMCALClusters; // PbPb // using TrackEle = o2::soa::Join; using TrackEle = o2::soa::Join; + // MC : same collisions/tracks as above, plus the MC labels. aod::EvSels already comes with + // CollisionsWithCent (joining it again would duplicate the table), so the data and the MC + // paths see exactly the same event selection. + using CollisionsWithCentMc = soa::Join; + using TrackEleMc = o2::soa::Join; + // pp // using TrackEle = o2::soa::Filtered>; @@ -202,7 +232,10 @@ struct HfTaskElectronWeakBoson { ConfigurableAxis confaxisIsoEnergy{"confaxisIsoEnergy", {255, 0, 2.0}, "E_{iso}"}; ConfigurableAxis confaxisIsoMomentum{"confaxisIsoMomentum", {255, 0, 2.0}, "p_{iso}"}; ConfigurableAxis confaxisIsoTrack{"confaxisIsoTrack", {25, -0.5, 24.5}, "Isolation Track"}; - ConfigurableAxis confaxisDedxTrack{"confaxisDedxTrack", {200, -10, 10}, "dEdx"}; + // raw TPC dE/dx (arbitrary units, MIP around 50) -- used by hTHnElectrons + ConfigurableAxis confaxisDedxTrack{"confaxisDedxTrack", {200, 0, 200}, "dE/dx"}; + // TPC n-sigma of the associated hadrons -- used by hZHadronDphi + ConfigurableAxis confaxisNsigmaHadron{"confaxisNsigmaHadron", {200, -10, 10}, "n#sigma^{#pi}_{TPC}"}; ConfigurableAxis confaxisInvMassZgamma{"confaxisInvMassZgamma", {150, 0, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisInvMassZ{"confaxisInvMassZ", {130, 20, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisZfrag{"confaxisZfrag", {200, 0, 2.0}, "p_{T,h}/p_{T,Z}"}; @@ -261,7 +294,10 @@ struct HfTaskElectronWeakBoson { const AxisSpec axisNsigma{100, -5, 5, "N#sigma"}; const AxisSpec axisNsigmaZneg{100, -5, 5, "N#sigma_{pos}"}; const AxisSpec axisNsigmaZpos{100, -5, 5, "N#sigma_{neg}"}; - const AxisSpec axisDedx{confaxisDedxTrack, "dEdx"}; + const AxisSpec axisDedx{confaxisDedxTrack, "dE/dx"}; + const AxisSpec axisNsigmaHadron{confaxisNsigmaHadron, "n#sigma^{#pi}_{TPC}"}; + const AxisSpec axisSignHadron{2, -2, 2, "charge sign (hadron)"}; + const AxisSpec axisNMatch{10, -0.5, 9.5, "N_{cluster} within rMatchMax"}; const AxisSpec axisE{nBinsE, 0, binEmax, "Energy"}; const AxisSpec axisM02{100, 0, 1, "M02"}; const AxisSpec axisM02neg{100, 0, 1, "M02(neg)"}; @@ -343,9 +379,10 @@ struct HfTaskElectronWeakBoson { registry.add("hInvMassZeeEmcQAbg", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisDedx}); registry.add("hTHnTrMatch", "Track EMC Match", HistType::kTHnSparseF, {axisPt, axisdPhi, axisdEta}); + registry.add("hNMatchPerTrack", "number of matched clusters within rMatchMax per track", kTH2F, {{axisPt}, {axisNMatch}}); // Z-hadron correlation histograms - registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisDedx}); + registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron}); registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH2F, {{axisSign}, {axisPtZ}}); // hisotgram for EMCal trigger @@ -355,6 +392,112 @@ struct HfTaskElectronWeakBoson { registry.add("hRho", "rho UE density", kTH1F, {axisE}); registry.add("hSumERC", "RC sumE", kTH1F, {axisE}); registry.add("hEnergyUE", "UE vs. centrality", kTH2F, {{axisCentrality}, {axisE}}); + + // MC : associated-hadron efficiency + const AxisSpec axisPtHadronMc{mcCfg.confaxisPtHadron, "p_{T,hadron} (GeV/c)"}; + const AxisSpec axisEtaHadronMc{mcCfg.confaxisEtaHadron, "#eta_{hadron}"}; + const AxisSpec axisMcCollCounter{4, -0.5, 3.5, "MC collision selection step"}; + + if (doprocessMcGen) { + registry.add("hMcGenCollCounter", "generated collisions (0: all, 1: |zvtx| cut, 2: reconstructed & selected)", kTH1D, {axisMcCollCounter}); + registry.add("hMcGenZvtx", "generated Z vertex", kTH1D, {axisZvtx}); + // denominator of the efficiency + registry.add("hMcGenHadron", "generated primary charged hadrons", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + } + + if (doprocessMcRec) { + registry.add("hMcRecCollCounter", "reconstructed collisions in MC", kTH1D, {axisCounter}); + // numerator of the efficiency, in generated kinematics (use with hMcGenHadron) + registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + // same tracks, but in reconstructed kinematics (use to correct in measured bins) + registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + // purity : all selected tracks and the non-primary / unlabelled sub-samples + registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + // momentum resolution + registry.add("hMcPtGenRec", "generated vs. reconstructed p_{T}", kTH2F, {{axisPtHadronMc}, {axisPtHadronMc}}); + } + } + + // Kinematic + quality selection shared by the data and the MC track loops, + // so that the efficiency is evaluated with exactly the same cuts. + template + bool isSelectedTrackKine(TTrack const& track) + { + if (!track.isGlobalTrackWoPtEta()) { + return false; + } + if (std::abs(track.eta()) > etaTrMax) { + return false; + } + if (applyTrackSys) { + if (track.tpcNClsCrossedRows() < nclcrossTpcMin) { + return false; + } + if (std::abs(track.dcaXY()) > dcaxyMax) { + return false; + } + if (track.itsChi2NCl() > chi2ItsMax) { + return false; + } + if (track.tpcChi2NCl() > chi2TpcMax) { + return false; + } + if (track.tpcNClsFound() < nclTpcMin) { + return false; + } + if (track.itsNCls() < nclItsMin) { + return false; + } + } + return true; + } + + // Centrality selection, applied on the centrality estimator itself (as in processData, + // where the cut is done before the variable is possibly overwritten by the multiplicity) + template + bool isSelectedCentrality(TCollision const& collision) + { + if (!enableCentralityAnalysis) { + return true; + } + float const centrality = o2::hf_centrality::getCentralityColl(collision, centralityEstimator); + return (centrality >= centralityMin && centrality <= centralityMax); + } + + // Value filled on the centrality axis, following the same priority as in processData + template + float getCentralityValue(TCollision const& collision) + { + float centrality = 1.0; + if (enableCentralityAnalysis) { + centrality = o2::hf_centrality::getCentralityColl(collision, centralityEstimator); + } + if (enableMultiplicityFT0MAnalysis) { + centrality = collision.multFT0M(); + } + if (enableMultiplicityPVAnalysis) { + centrality = collision.multNTracksPV(); + } + return centrality; + } + + // Generated particle counted as an associated hadron + template + bool isChargedPrimary(TParticle const& particle) + { + if (mcCfg.requirePhysicalPrimary && !particle.isPhysicalPrimary()) { + return false; + } + const TParticlePDG* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle) { + return false; + } + if (std::abs(pdgParticle->Charge()) < ChargeTolerance) { + return false; + } + return true; } double getIsolatedCluster(const o2::aod::EMCALCluster& cluster, @@ -431,9 +574,9 @@ struct HfTaskElectronWeakBoson { const float randomConeArea = o2::constants::math::PI * randomConeR * randomConeR; std::vector sumErc; - sumErc.reserve(nRandomCones); + sumErc.reserve(ueCfg.nRandomCones); - for (int i = 0; i < nRandomCones; i++) { + for (int i = 0; i < ueCfg.nRandomCones; i++) { float etarc = rnd->Uniform(-etaEmcMax, etaEmcMax); // in EMCal acceptance float phirc = rnd->Uniform(phiEmcMin, phiEmcMax); // in EMCal acceptance @@ -441,7 +584,7 @@ struct HfTaskElectronWeakBoson { float energySumRC = 0; for (const auto& c : clusters) { - if (c.energy() > rcHardE) { + if (c.energy() > ueCfg.rcHardE) { continue; } double dEtarc = etarc - c.eta(); @@ -471,15 +614,69 @@ struct HfTaskElectronWeakBoson { return median / randomConeArea; } + // Outcome of the track - EMCal cluster matching for one track + struct HfEmcMatchResult { + int64_t posBestMatch = -1; // cursor position of the closest match inside the slice, -1 if none + double rMin = 999.9; // smallest sqrt(dPhi^2 + dEta^2) over the matched clusters + double dPhiMin = 999.9; + double dEtaMin = 999.9; + int nMatchInRadius = 0; // number of matched clusters inside rMatchMax + bool isMatched = false; // the closest cluster is inside rMatchMax + }; + + template + HfEmcMatchResult findBestEmcMatch(TMatchedTracks const& matchesOfTrack, float centrality) + { + HfEmcMatchResult result; + int64_t posMatch = -1; + + for (const auto& match : matchesOfTrack) { + ++posMatch; + const auto& cluster = match.template emcalcluster_as(); + if (cluster.time() < timeEmcMin || cluster.time() > timeEmcMax) { + continue; + } + + double const dEta = match.template track_as().trackEtaEmcal() - cluster.eta(); + double dPhi = match.template track_as().trackPhiEmcal() - cluster.phi(); + dPhi = RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI); + double const r = RecoDecay::sqrtSumOfSquares(dPhi, dEta); + + registry.fill(HIST("hMatchPhi"), cluster.phi(), match.template track_as().trackPhiEmcal()); + registry.fill(HIST("hMatchEta"), cluster.eta(), match.template track_as().trackEtaEmcal()); + registry.fill(HIST("hTHnTrMatch"), match.template track_as().pt(), dPhi, dEta); + registry.fill(HIST("hEMCtime"), cluster.time()); + registry.fill(HIST("hEnergy"), cluster.energy()); + registry.fill(HIST("hEnergyMult"), centrality, cluster.energy()); + + if (std::abs(dPhi) <= rMatchMax && std::abs(dEta) <= rMatchMax) { + result.nMatchInRadius++; + } + + // LOG(info) << "r match = " << r; + if (r < result.rMin) { + result.rMin = r; + result.dPhiMin = dPhi; + result.dEtaMin = dEta; + result.posBestMatch = posMatch; + // LOG(info) << "match cl info : " << r << " ; " << cluster.energy(); + } + } + + result.isMatched = (result.posBestMatch >= 0) && (std::abs(result.dPhiMin) <= rMatchMax) && (std::abs(result.dEtaMin) <= rMatchMax); + return result; + } + void recoMassZee(const KFParticle& kfpIsoEle, int charge, + int64_t indexIsoEle, float centrality, TrackEle const& tracks) { // LOG(info) << "Invarimass cal by KF particle "; for (const auto& track : tracks) { - if (std::abs(track.pt() - kfpIsoEle.GetPt()) < ptMatch) { + if (track.globalIndex() == indexIsoEle) { continue; } if (track.pt() < ptZeeMin) { @@ -537,16 +734,17 @@ struct HfTaskElectronWeakBoson { massZee, track.sign() * charge, kfpIsoEle.GetPt(), - kfpAssEle.GetPt()); + kfpAssEle.GetPt(), + indexIsoEle, + track.globalIndex()); } } - // void process(soa::Filtered::iterator const& collision, - void process(soa::Filtered::iterator const& collision, - aod::BCsWithTimestamps const&, - SelectedClusters const& emcClusters, - TrackEle const& tracks, - o2::aod::EMCALMatchedTracks const& matchedtracks) + void processData(soa::Filtered::iterator const& collision, + aod::BCsWithTimestamps const&, + SelectedClusters const& emcClusters, + TrackEle const& tracks, + o2::aod::EMCALMatchedTracks const& matchedtracks) { registry.fill(HIST("hEventCounterInit"), 0.5); @@ -638,7 +836,7 @@ struct HfTaskElectronWeakBoson { float rho = 0.f; float energyUE = 0.f; - if (useUEsub) { + if (ueCfg.useUEsub) { rho = estimateRhoRC(emcClusters); energyUE = rho * static_cast(o2::constants::math::PI * rIsolation * rIsolation); registry.fill(HIST("hEnergyUE"), centrality, energyUE); @@ -648,32 +846,9 @@ struct HfTaskElectronWeakBoson { // track loop for (const auto& track : tracks) { - if (!track.isGlobalTrackWoPtEta()) { - continue; - } - if (std::abs(track.eta()) > etaTrMax) { + if (!isSelectedTrackKine(track)) { continue; } - if (applyTrackSys) { - if (track.tpcNClsCrossedRows() < nclcrossTpcMin) { - continue; - } - if (std::abs(track.dcaXY()) > dcaxyMax) { - continue; - } - if (track.itsChi2NCl() > chi2ItsMax) { - continue; - } - if (track.tpcChi2NCl() > chi2TpcMax) { - continue; - } - if (track.tpcNClsFound() < nclTpcMin) { - continue; - } - if (track.itsNCls() < nclItsMin) { - continue; - } - } registry.fill(HIST("hEta"), track.eta()); registry.fill(HIST("hITSchi2"), track.itsChi2NCl()); registry.fill(HIST("hTPCchi2"), track.tpcChi2NCl()); @@ -700,11 +875,13 @@ struct HfTaskElectronWeakBoson { eop, isoEnergy, isoMomentum, - track.tpcNSigmaPi() * track.sign(), + track.tpcNSigmaPi(), m02, trackCount, track.tpcNClsCrossedRows(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } if (track.pt() < ptMin) { @@ -730,102 +907,71 @@ struct HfTaskElectronWeakBoson { bool isIsolatedTr = false; if ((tracksofcluster.size() != 0) && isEMCacceptance) { - int nMatch = 0; - for (const auto& match : tracksofcluster) { - if (match.emcalcluster_as().time() < timeEmcMin || match.emcalcluster_as().time() > timeEmcMax) { - continue; - } - float const m02Emc = match.emcalcluster_as().m02(); - float const energyEmc = match.emcalcluster_as().energy(); - double const phiEmc = match.emcalcluster_as().phi(); - double const etaEmc = match.emcalcluster_as().eta(); - double const timeEmc = match.emcalcluster_as().time(); - // LOG(info) << "tr phi0 = " << match.track_as().phi(); - // LOG(info) << "tr phi1 = " << track.phi(); - // LOG(info) << "emc phi = " << phiEmc; - - if (nMatch == 0) { - double const dEta = match.track_as().trackEtaEmcal() - etaEmc; - double dPhi = match.track_as().trackPhiEmcal() - phiEmc; - dPhi = RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI); - - registry.fill(HIST("hMatchPhi"), phiEmc, match.track_as().trackPhiEmcal()); - registry.fill(HIST("hMatchEta"), etaEmc, match.track_as().trackEtaEmcal()); - - double const r = RecoDecay::sqrtSumOfSquares(dPhi, dEta); - // LOG(info) << "r match = " << r; - if (r < rMin) { - rMin = r; - dPhiMin = dPhi; - dEtaMin = dEta; + // Pass 1 : QA over every matched cluster, and find the closest one + auto const emcMatch = findBestEmcMatch(tracksofcluster, centrality); + rMin = emcMatch.rMin; + dPhiMin = emcMatch.dPhiMin; + dEtaMin = emcMatch.dEtaMin; + registry.fill(HIST("hNMatchPerTrack"), track.pt(), emcMatch.nMatchInRadius); + + // Pass 2 : only the closest cluster is used for E/p, isolation and the Z reconstruction + if (emcMatch.isMatched) { + auto const match = tracksofcluster.iteratorAt(emcMatch.posBestMatch); + const auto& cluster = match.emcalcluster_as(); + float const m02Emc = cluster.m02(); + float const energyEmc = cluster.energy(); + + registry.fill(HIST("hTrMatchR"), match.track_as().pt(), rMin); + registry.fill(HIST("hEnergyNcell"), energyEmc, cluster.nCells()); + + eop = energyEmc / match.track_as().p(); + m02 = m02Emc; + // LOG(info) << "check match cl info : " << cluster.energy(); + isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); + + if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { + registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), track.tpcSignal()); + } + // LOG(info) << "E/p" << eop; + registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); + + if (m02Emc >= m02Min && m02Emc <= m02Max && + match.track_as().tpcNSigmaEl() > nsigTpcMin && match.track_as().tpcNSigmaEl() < nsigTpcMax) { + registry.fill(HIST("hEop"), match.track_as().pt(), eop); + if (eop > eopMin && eop < eopMax && isoEnergy < energyIsolationMax) { + isIsolated = true; } - registry.fill(HIST("hTHnTrMatch"), match.track_as().pt(), dPhi, dEta); - registry.fill(HIST("hEMCtime"), timeEmc); - registry.fill(HIST("hEnergy"), energyEmc); - registry.fill(HIST("hEnergyMult"), centrality, energyEmc); - - if (std::abs(dPhi) > rMatchMax || std::abs(dEta) > rMatchMax) { - continue; + if (eop > eopMin && eop < eopMax && trackCount < trackIsolationMax && isoMomentum < momentumIsolationMax) { + isIsolatedTr = true; } - registry.fill(HIST("hTrMatchR"), match.track_as().pt(), r); - registry.fill(HIST("hEnergyNcell"), energyEmc, match.emcalcluster_as().nCells()); - - const auto& cluster = match.emcalcluster_as(); - - eop = energyEmc / match.track_as().p(); - m02 = m02Emc; - // LOG(info) << "eop = " << eop; - - isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); - - if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { - registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), track.tpcSignal()); - } - // LOG(info) << "E/p" << eop; - registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); - if (match.emcalcluster_as().m02() < m02Min || match.emcalcluster_as().m02() > m02Max) { - continue; + if (isIsolated && isIsolatedTr) { + registry.fill(HIST("hEopIsolation"), match.track_as().pt(), eop); + + if (match.track_as().pt() > ptZeeMin) { + int pdgIso = kElectron; + if (match.track_as().sign() > 0) { + pdgIso = kPositron; + } + KFPTrack const kfpTrackIsoEle = createKFPTrackFromTrack(match.track_as()); + KFParticle const kfpIsoEle(kfpTrackIsoEle, pdgIso); + recoMassZee(kfpIsoEle, match.track_as().sign(), match.track_as().globalIndex(), centrality, tracks); + + } // end of pt cut for e from Z + } // end if isolation cut + if (isIsolatedTr) { + registry.fill(HIST("hEopIsolationTr"), match.track_as().pt(), eop); } - - if (match.track_as().tpcNSigmaEl() > nsigTpcMin && match.track_as().tpcNSigmaEl() < nsigTpcMax) { - registry.fill(HIST("hEop"), match.track_as().pt(), eop); - if (eop > eopMin && eop < eopMax && isoEnergy < energyIsolationMax) { - isIsolated = true; - } - if (eop > eopMin && eop < eopMax && trackCount < trackIsolationMax && isoMomentum < momentumIsolationMax) { - isIsolatedTr = true; - } - - if (isIsolated && isIsolatedTr) { - registry.fill(HIST("hEopIsolation"), match.track_as().pt(), eop); - - if (match.track_as().pt() > ptZeeMin) { - int pdgIso = kElectron; - if (match.track_as().sign() > 0) { - pdgIso = kPositron; - } - KFPTrack const kfpTrackIsoEle = createKFPTrackFromTrack(match.track_as()); - KFParticle const kfpIsoEle(kfpTrackIsoEle, pdgIso); - recoMassZee(kfpIsoEle, match.track_as().sign(), centrality, tracks); - - } // end of pt cut for e from Z - } // end if isolation cut - if (isIsolatedTr) { - registry.fill(HIST("hEopIsolationTr"), match.track_as().pt(), eop); - } - } // end of PID cut - } // end of nmatch == 0 - nMatch++; - } // end of cluster match + } // end of M02 + PID cut + } // end of isMatched } // end of cluster if (rMin < rMatchMax) { // LOG(info) << "R mim = " << rMin; registry.fill(HIST("hTrMatch_mim"), dPhiMin, dEtaMin); } - if ((enableZeeTrkRecoQA || enableZeeEmcRecoQA) && track.pt() > ptZeeMin) { + if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) && track.pt() > ptZeeMin) { if (track.sign() < 0) { selectedElectronsIso.emplace_back( track.pt(), @@ -840,7 +986,9 @@ struct HfTaskElectronWeakBoson { m02, trackCount, track.tpcNClsFound(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } else { selectedPositronsIso.emplace_back( track.pt(), @@ -855,7 +1003,9 @@ struct HfTaskElectronWeakBoson { m02, trackCount, track.tpcNClsFound(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } } @@ -869,22 +1019,20 @@ struct HfTaskElectronWeakBoson { } registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt); for (const auto& trackAss : selectedElectronsAss) { - if (std::abs(trackAss.pt - zBoson.ptchild0) < ptMatch) { - continue; - } - if (std::abs(trackAss.pt - zBoson.ptchild1) < ptMatch) { + // remove the two tracks used to build this Z, matched exactly by globalIndex + if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { continue; } // calculate Z-h correlation double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); double const ptRatio = trackAss.pt / zBoson.pt; double const deltaEta = zBoson.eta - trackAss.eta; - registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, ptRatio, trackAss.pt, trackAss.dedxTrk); + registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign); } } } // end of Z-hadron correlation // Z->ee QA - if (enableZeeTrkRecoQA || enableZeeEmcRecoQA) { + if (zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) { if (!selectedElectronsIso.empty() && !selectedPositronsIso.empty()) { // signal for (const auto& trackEle : selectedElectronsIso) { @@ -894,12 +1042,12 @@ struct HfTaskElectronWeakBoson { auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; - if (invMass > massZMinQA) { - if (enableZeeTrkRecoQA) { + if (invMass > zeeQaCfg.massZMinQA) { + if (zeeQaCfg.enableZeeTrkRecoQA) { registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackEle.pt, trackPos.pt, trackEle.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackEle.nclusterTPC, trackPos.nclusterTPC, trackEle.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackPos.eop, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); } - if (enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackEle.pt, trackPos.pt, trackEle.eop, trackPos.eop, trackEle.m02Emc, trackPos.m02Emc, trackEle.dedxTrk, trackPos.dedxTrk, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); + if (zeeQaCfg.enableZeeEmcRecoQA) { + registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackEle.pt, trackPos.pt, trackEle.eop, trackPos.eop, trackEle.m02Emc, trackPos.m02Emc, trackEle.nSigmaTpc, trackPos.nSigmaTpc, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); } } } @@ -914,12 +1062,12 @@ struct HfTaskElectronWeakBoson { auto child2 = RecoDecayPtEtaPhi::pVector(trackEle2.pt, trackEle2.eta, trackEle2.phi); float const sectorpos = trackEle2.phi / o2::constants::math::SectorSpanRad; double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > massZMinQA) { - if (enableZeeTrkRecoQA) { + if (invMass > zeeQaCfg.massZMinQA) { + if (zeeQaCfg.enableZeeTrkRecoQA) { registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.dcaxyTrk, trackEle2.dcaxyTrk, trackEle2.dcazTrk, trackEle.nclusterTPC, trackEle2.nclusterTPC, trackEle.nclusterITS, trackEle2.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackEle2.eop, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); } - if (enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.eop, trackEle2.eop, trackEle.m02Emc, trackEle2.m02Emc, trackEle.dedxTrk, trackEle2.dedxTrk, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); + if (zeeQaCfg.enableZeeEmcRecoQA) { + registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.eop, trackEle2.eop, trackEle.m02Emc, trackEle2.m02Emc, trackEle.nSigmaTpc, trackEle2.nSigmaTpc, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); } } } @@ -934,19 +1082,131 @@ struct HfTaskElectronWeakBoson { auto child2 = RecoDecayPtEtaPhi::pVector(trackPos2.pt, trackPos2.eta, trackPos2.phi); float const sectorpos = trackPos2.phi / o2::constants::math::SectorSpanRad; double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > massZMinQA) { - if (enableZeeTrkRecoQA) { + if (invMass > zeeQaCfg.massZMinQA) { + if (zeeQaCfg.enableZeeTrkRecoQA) { registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.dcaxyTrk, trackPos2.dcaxyTrk, trackPos2.dcazTrk, trackPos.nclusterTPC, trackPos2.nclusterTPC, trackPos.nclusterITS, trackPos2.nclusterITS, sectorneg, sectorpos, trackPos.eop, trackPos2.eop, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); } - if (enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.eop, trackPos2.eop, trackPos.m02Emc, trackPos2.m02Emc, trackPos.dedxTrk, trackPos2.dedxTrk, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); + if (zeeQaCfg.enableZeeEmcRecoQA) { + registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.eop, trackPos2.eop, trackPos.m02Emc, trackPos2.m02Emc, trackPos.nSigmaTpc, trackPos2.nSigmaTpc, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); } } } } // end of pos-pos paring } // check arrays aren't empty } // end of Z->ee QA - } // process + } // processData + PROCESS_SWITCH(HfTaskElectronWeakBoson, processData, "process data (Z->ee and Z-h correlation)", true); + + // ------------------------------------------------------------------------- + // MC : denominator of the associated-hadron efficiency. + // Looping over the generated collisions (with the reconstructed ones collected by + // soa::SmallGroups) also gives access to the event-loss / split-collision effects + // through hMcGenCollCounter. + // ------------------------------------------------------------------------- + void processMcGen(aod::McCollision const& mcCollision, + soa::SmallGroups const& collisions, + aod::McParticles const& mcParticles) + { + registry.fill(HIST("hMcGenCollCounter"), 0.5); + + if (std::abs(mcCollision.posZ()) > vtxZ) { + return; + } + registry.fill(HIST("hMcGenCollCounter"), 1.5); + registry.fill(HIST("hMcGenZvtx"), mcCollision.posZ()); + + // look for a reconstructed collision passing the same event selection as in processData + bool isReconstructed = false; + float centrality = 1.0; + for (const auto& collision : collisions) { + if (applySel8 && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > vtxZ) { + continue; + } + if (!isSelectedCentrality(collision)) { + continue; + } + isReconstructed = true; + centrality = getCentralityValue(collision); + break; + } + if (!isReconstructed) { + return; + } + registry.fill(HIST("hMcGenCollCounter"), 2.5); + + for (const auto& particle : mcParticles) { + if (!isChargedPrimary(particle)) { + continue; + } + // same acceptance as isSelectedTrackKine() applied to the reconstructed tracks + if (std::abs(particle.eta()) > etaTrMax) { + continue; + } + if (particle.pt() < ptAssMin) { + continue; + } + registry.fill(HIST("hMcGenHadron"), centrality, particle.pt(), particle.eta()); + } + } // processMcGen + PROCESS_SWITCH(HfTaskElectronWeakBoson, processMcGen, "process MC generated level for the associated-hadron efficiency", false); + + // ------------------------------------------------------------------------- + // MC : numerator of the associated-hadron efficiency (and purity). + // The track selection is identical to the one applied to selectedElectronsAss + // in processData. + // ------------------------------------------------------------------------- + void processMcRec(soa::Filtered::iterator const& collision, + TrackEleMc const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelectedCentrality(collision)) { + return; + } + float const centrality = getCentralityValue(collision); + registry.fill(HIST("hMcRecCollCounter"), 0.5); + + for (const auto& track : tracks) { + if (!isSelectedTrackKine(track)) { + continue; + } + if (track.pt() < ptAssMin) { + continue; + } + + registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta()); + + if (!track.has_mcParticle()) { + registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta()); + continue; + } + + auto const mcParticle = track.mcParticle(); + + // reject pileup : the matched particle must come from this MC collision + if (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) { + registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); + continue; + } + if (!isChargedPrimary(mcParticle)) { + registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); + continue; + } + + registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta()); + registry.fill(HIST("hMcPtGenRec"), mcParticle.pt(), track.pt()); + } + } // processMcRec + PROCESS_SWITCH(HfTaskElectronWeakBoson, processMcRec, "process MC reconstructed level for the associated-hadron efficiency", false); + }; // struct HfTaskElectronWeakBoson WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 54b18f97c125393e0ba2902e08f1b42024aa887b Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Tue, 22 Sep 2026 13:41:58 +0530 Subject: [PATCH 027/648] [PWGCF] upgraded Lambda-Proton Balance Function Code (#17981) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 909 +++++++++++++----- 1 file changed, 646 insertions(+), 263 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 9d503bdc453..bb09321931a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file lambdaProtonBalanceFunction.cxx -/// \brief Single- and two-particle density correlations (rho1, rho2) and R2 correlations for Lambda-proton pairs to measure the balance function. +/// \brief Single- and two-particle density correlations (rho1, rho2) and R2 correlations for Lambda-proton, proton-proton and Lambda-Lambda pairs (plus antiparticle combinations) to measure the balance function. /// \author Anoop Poruthiyil #include "PWGLF/DataModel/LFStrangenessTables.h" @@ -61,10 +61,26 @@ using MyTracks = soa::Join; +// Per-centrality-bin histogram set. Defined at file scope (NOT inside the task struct), +// otherwise O2's struct reflection in adaptAnalysisTask fails to compile. +struct CentRho2Set { + // eta space (only booked if cFillEtaSpace && cFillCentEtaSpace) + std::shared_ptr Lp, LAp, ALp, ALAp, pp, pAp, App, ApAp, LL, LAL, ALL, ALAL; + // y space + std::shared_ptr Lp_y, LAp_y, ALp_y, ALAp_y, pp_y, pAp_y, App_y, ApAp_y, LL_y, LAL_y, ALL_y, ALAL_y; + // rho1 eta + std::shared_ptr rho1_Proton, rho1_AntiProton, rho1_Lambda, rho1_AntiLambda; + // rho1 y + std::shared_ptr rho1_Proton_y, rho1_AntiProton_y, rho1_Lambda_y, rho1_AntiLambda_y; + // event counter + std::shared_ptr hEvents; +}; + struct LambdaProtonBalanceFunction { HistogramRegistry registryLambda{"Lambda_invMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryLambdaExtended{"Lambda_invMassExtended", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryRho{"rho1ANDrho2_LP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryCent{"rho1ANDrho2_LP_CentralityBased", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryOther{"Other_Hists", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryPid{"PID", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryQaDetector{"QA_Detec", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -75,7 +91,7 @@ struct LambdaProtonBalanceFunction { // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // pT bins 0.6 → 6.0 in steps of 0.2 (used for per-bin rho2 / mass histograms) + // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin rho2 / mass histograms) std::vector ptEdges; // ════════════════════════════════════════════════════════════════════════ @@ -87,11 +103,11 @@ struct LambdaProtonBalanceFunction { std::shared_ptr hMassAntiLambdaMerged; // ════════════════════════════════════════════════════════════════════════ - // SECTION A2 — Extended invariant-mass histograms (y × pT grid) + // SECTION A2— Extended invariant-mass histograms (y × pT grid) // hMassLambdaExtended[iY][iPt] → Lambda_invMassExtended/Lambda/ // hMassAntiLambdaExtended[iY][iPt] → Lambda_invMassExtended/AntiLambda/ - // Rapidity bins: [-1.0,-0.9), [-0.9,-0.8), [-0.8,-0.7), [-0.7,-0.6] - // pT bins: [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] + // Rapidity bins (kExtNyBins = 2): [-0.8,-0.7), [-0.7,-0.6] + // pT bins (kExtNptBins = 25): [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] // ════════════════════════════════════════════════════════════════════════ static constexpr int kExtNyBins = 2; static constexpr int kExtNptBins = 25; @@ -108,7 +124,9 @@ struct LambdaProtonBalanceFunction { // ════════════════════════════════════════════════════════════════════════ // SECTION B — rho2 eta-space histograms // Naming: hRho2_ = per-pT-bin vector - // hRho2__pT015toMaxDefined = merged (full pT range) + // hRho2__pT015toMaxDefined = merged (full pT range as + // set by the pT cuts: protons 0.5-3.6, Lambdas 0.6-3.6 GeV/c; + // "015" is a legacy name and does NOT reflect the actual lower cut) // ════════════════════════════════════════════════════════════════════════ // Lambda–proton family std::shared_ptr hRho2_Lp_pT015toMaxDefined; @@ -147,6 +165,10 @@ struct LambdaProtonBalanceFunction { std::shared_ptr hRho2_ALL_y_pT015toMaxDefined; std::shared_ptr hRho2_ALAL_y_pT015toMaxDefined; + // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) + std::vector centSets; + std::vector centEdgesLocal; + // ════════════════════════════════════════════════════════════════════════ // SECTION D — pT-spectra and pair-count monitoring histograms // ════════════════════════════════════════════════════════════════════════ @@ -172,11 +194,16 @@ struct LambdaProtonBalanceFunction { // sample before constructing future Balance Functions. int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events + int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto + int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto // ───────────────────────────────────────────────────────────────────── // ── Configurables ───────────────────────────────────────────────────── - Configurable cPPandV0NBins{"cPPandV0NBins", 100, "N bins in all histos"}; + Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cFillEtaSpace{"cFillEtaSpace", false, "Fill eta-space rho1/rho2 histograms"}; + Configurable cUseQinvCut{"cUseQinvCut", true, "Apply q_inv > 0.01 GeV/c pair cut (false = no q_inv rejection)"}; + Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; // Track quality cuts Configurable pProtonTPCMinRows{"pProtonTPCMinRows", 70, "Minimum TPC crossed rows"}; @@ -186,7 +213,7 @@ struct LambdaProtonBalanceFunction { Configurable pProtonITSMaxChi2PerCluster{"pProtonITSMaxChi2PerCluster", 36.0f, "Max ITS chi2 / Ncls"}; Configurable cPPMinTpcSignal{"cPPMinTpcSignal", 10.0f, "Min raw TPC signal (sanity cut)"}; // DCA cuts - Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // changed 0.1-->0.05-->0.02 (tightened for cleanest primary-proton sample) + Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // tight cut for cleanest primary-proton sample Configurable pProtonMaxDCAz{"pProtonMaxDCAz", 0.1f, "Max |DCAz| (cm)"}; Configurable pProtonTPCTOFSwitchP{"pProtonTPCTOFSwitchP", 0.700000238f, "p threshold (GeV/c) above which TOF is used if available"}; @@ -214,16 +241,17 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // Proton Kinematic Cuts (from strange9) - Configurable pProtonMinP{"pProtonMinP", 0.5f, "Min p_{TPC} for primary protons"}; - Configurable pProtonMaxP{"pProtonMaxP", 3.5999999f, "Max p for primary protons"}; - Configurable pProtonMaxEta{"pProtonMaxEta", 0.5f, "Max |eta| for primary protons"}; + Configurable pProtonMinP{"pProtonMinP", 0.5f, "Min p for primary protons (GeV/c)"}; + Configurable pProtonMaxP{"pProtonMaxP", 3.6f, "Max p for primary protons (GeV/c)"}; + Configurable pProtonMinPt{"pProtonMinPt", 0.5f, "Min pT for primary protons (GeV/c)"}; + Configurable pProtonMaxPt{"pProtonMaxPt", 3.6f, "Max pT for primary protons (GeV/c)"}; + Configurable pProtonMaxY{"pProtonMaxY", 0.5f, "Max |y| for primary protons"}; // Lambda Kinematic Cuts (from strange9) Configurable lambdaV0MinPt{"lambdaV0MinPt", 0.60f, "Min pT for Lambdas"}; Configurable lambdaV0MaxPt{"lambdaV0MaxPt", 3.60f, "Max pT for Lambdas"}; Configurable lambdaV0MaxY{"lambdaV0MaxY", 0.5f, "Max |y| for Lambdas"}; - Configurable lambdaV0MaxEta{"lambdaV0MaxEta", 0.5f, "Max |eta| for Lambdas"}; // V0 Mass and Topology Cuts (from strange9) Configurable lambdaV0MaxCTau{"lambdaV0MaxCTau", 30.0f, "Max V0 ctau (cm)"}; @@ -235,13 +263,11 @@ struct LambdaProtonBalanceFunction { Configurable cV0DauMinPt{"cV0DauMinPt", 0.1f, "Daughter pT minimum"}; Configurable cV0DauMaxEta{"cV0DauMaxEta", 0.8f, "Daughter |eta| cut"}; Configurable cV0DauMinTpcCrossedRows{"cV0DauMinTpcCrossedRows", 70, "Daughter TPC min crossed rows"}; - // Rapidity-specific constants matched to PProtonMaxY's default value (0.8). + // Rapidity-specific constants matched to pProtonMaxY's default value (0.5). // Note: Since these are compile-time constexpr, the axis will NOT auto-update - // if PProtonMaxY is changed via configurable at runtime. The axis just needs to + // if pProtonMaxY is changed via configurable at runtime. The axis just needs to // stay >= the cut, not exactly equal. - // When u change JSON, adjust this also (not needed is my new finding) - // Eta static constexpr int kRhoEtaBins = 40; static constexpr float kRhoMin = -0.5f; @@ -254,13 +280,16 @@ struct LambdaProtonBalanceFunction { static constexpr int kRhoPhiBins = 72; static constexpr int kRhoUnrolledBins = kRhoEtaBins * kRhoPhiBins; + static constexpr int kRhoUnrolledBinsY = kRhoYBins * kRhoPhiBins; void buildPtBins() { ptEdges.clear(); - static constexpr float kPtEdgeMax = 6.0001f; - for (float pt = 0.6f; pt <= kPtEdgeMax; pt += 0.2f) { - ptEdges.push_back(pt); + static constexpr int kNPtEdges = 16; // 0.6, 0.8, ..., 3.6 GeV/c (15 bins) + static constexpr float kPtEdgeMin = 0.6f; + static constexpr float kPtEdgeStep = 0.2f; + for (int i = 0; i < kNPtEdges; ++i) { + ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); } } @@ -317,7 +346,7 @@ struct LambdaProtonBalanceFunction { static constexpr float kMassProton = o2::constants::physics::MassProton; static constexpr float kMassLambda = o2::constants::physics::MassLambda; - // Femtoscopic q_inv cut (applied only to QA Before/After plots, not to physics rho2 histograms): + // Femtoscopic q_inv cut: pairs with q_inv <= kQinvCutLP are rejected from all rho2 histograms (pp, pAp, App, ApAp, LP-family, LL-family): static constexpr float kQinvCutLP = 0.01f; template @@ -325,25 +354,32 @@ struct LambdaProtonBalanceFunction { { return RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, kMassProton); } - // Fills both eta-space and rapidity-space rho2 histograms in one call. - // Separated guards prevent filling when either index is out of acceptance. - void fillRho2Pair( - std::shared_ptr& histEta, std::shared_ptr& histY, - int idxEta1, int idxEta2, int idxY1, int idxY2) const - { - if (idxEta1 >= 0 && idxEta2 >= 0) { - histEta->Fill(idxEta1, idxEta2); - } - if (idxY1 >= 0 && idxY2 >= 0) { - histY->Fill(idxY1, idxY2); - } - } float displayDeltaPhi(float dPhi) const { return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); } + // k* = |p*| of one particle in the pair rest frame, from four-momenta. + float computeKstar(float px1, float py1, float pz1, float m1, + float px2, float py2, float pz2, float m2) const + { + const float E1 = std::sqrt(px1 * px1 + py1 * py1 + pz1 * pz1 + m1 * m1); + const float E2 = std::sqrt(px2 * px2 + py2 * py2 + pz2 * pz2 + m2 * m2); + const float sx = px1 + px2; + const float sy = py1 + py2; + const float sz = pz1 + pz2; + const float sE = E1 + E2; + const float s = sE * sE - sx * sx - sy * sy - sz * sz; + if (s <= 0.f) { + return 0.f; + } + // k*^2 = [ (s - (m1+m2)^2)(s - (m1-m2)^2) ] / (4 s) + const float a = s - (m1 + m2) * (m1 + m2); + const float b = s - (m1 - m2) * (m1 - m2); + return std::sqrt(std::max(a * b, 0.f) / (4.f * s)); + } + // ── Femtoscopic q_inv computation ──────────────────────────────────────── // Returns q_inv = sqrt( -(p1 - p2)^2 ) in natural units (GeV/c). // E1 and E2 must be computed by the caller using the appropriate mass @@ -361,6 +397,31 @@ struct LambdaProtonBalanceFunction { // q2 should be <= 0 for physical pairs; protect sqrt from numerical noise return std::sqrt(std::max(-q2, 0.0f)); } + + // true => pair is rejected by the q_inv cut (never true when the cut is switched off) + bool failsQinvCut(float qinv) const + { + return cUseQinvCut.value && (qinv <= kQinvCutLP); + } + + // Returns index of centrality bin for cent, or -1 if outside all bins. + int centBinIndex(float cent) const + { + for (size_t i = 0; i + 1 < centEdgesLocal.size(); ++i) { + if (cent >= centEdgesLocal[i] && cent < centEdgesLocal[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + // Fill helper: fills h (if valid) at (a,b) only when both indices are in range. + static void fillIdx(std::shared_ptr const& h, int a, int b) + { + if (h && a >= 0 && b >= 0) { + h->Fill(a, b); + } + } // ───────────────────────────────────────────────────────────────────── // ── Two-region PID selection for (anti)protons ──────────────────────── @@ -370,23 +431,16 @@ struct LambdaProtonBalanceFunction { const float pTPC = trk.tpcInnerParam(); // NOTE: pTPC, not trk.p() — matches reference exactly const float nsPr = trk.tpcNSigmaPr(); + if (std::abs(nsPr) >= pProtonTPCNsigma.value) { + return false; + } if (pTPC < pProtonTPCTOFSwitchP.value) { - if (std::abs(nsPr) >= pProtonTPCNsigma.value) { - return false; - } - return true; - } else { - if (std::abs(nsPr) >= pProtonTPCNsigma.value) { - return false; - } - if (!trk.hasTOF()) { - return false; - } - if (std::abs(trk.tofNSigmaPr()) >= pProtonTOFNsigma.value) { - return false; - } return true; } + if (!trk.hasTOF()) { + return false; + } + return std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value; } // ───────────────────────────────────────────────────────────────────── @@ -396,9 +450,9 @@ struct LambdaProtonBalanceFunction { /// Canonical analysis stages for (anti)proton QA fills. enum class ProtonQAStage : int { - RawAfterTrackSel = 0, ///< truly RAW — before any track selection, DCA, or PID - TpcPid = 1, ///< after DCA + TPC PID cut passes - TpcTofPid = 2, ///< after DCA + TPC PID + TOF PID all pass + RawAfterTrackSel = 0, ///< after track-quality selection (TrackSelection table), before DCA cuts and PID + TpcPid = 1, ///< after DCA + TPC PID cut passes (all momenta) + TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchP FinalSelected = 3 ///< entered selectedPrimProtons / selectedPrimAntiProtons }; @@ -476,10 +530,10 @@ struct LambdaProtonBalanceFunction { void fillProtonDetectorQuality(ProtonQAStage stage, TTrack const& trk) { const float pt = trk.pt(); - const float tpcCR = static_cast(trk.tpcNClsCrossedRows()); + const auto tpcCR = static_cast(trk.tpcNClsCrossedRows()); const float tpcCRoF = trk.tpcCrossedRowsOverFindableCls(); const float tpcChi2 = trk.tpcChi2NCl(); - const float itsN = static_cast(trk.itsNCls()); + const auto itsN = static_cast(trk.itsNCls()); const float itsChi2 = trk.itsChi2NCl(); const float dcaXY = trk.dcaXY(); const float dcaZ = trk.dcaZ(); @@ -1072,15 +1126,41 @@ struct LambdaProtonBalanceFunction { } } + // Per-particle cache used by the pair loops (avoids recomputing per pair). + struct PartInfo { + int64_t gid; + float px, py, pz, E; + float eta, phi, y, pt; + int idxEta; + int idxY; + }; + + template + PartInfo makeProtonInfo(TTrack const& t) const + { + PartInfo p; + p.gid = t.globalIndex(); + p.px = t.px(); + p.py = t.py(); + p.pz = t.pz(); + p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + kMassProton * kMassProton); + p.eta = t.eta(); + p.phi = t.phi(); + p.pt = t.pt(); + p.y = protonRapidity(t); + p.idxEta = unrolledIndex(p.eta, p.phi); + p.idxY = unrolledIndexY(p.y, p.phi); + return p; + } + void init(InitContext const&) { buildPtBins(); // ── Common axis definitions ────────────────────────────────────────── AxisSpec lambdaMassAxis = {200, 1.08f, 1.15f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; - AxisSpec vertexZAxis = {cPPandV0NBins, -15.f, 15.f, "vrtx_{Z} [cm]"}; + AxisSpec vertexZAxis = {100, -15.f, 15.f, "vrtx_{Z} [cm]"}; AxisSpec ptAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec pzAxis = {200, -10.0f, 10.0f, "p_{z} (GeV/#it{c})"}; AxisSpec axisP = {200, 0.0f, 6.0f, "p (GeV/c)"}; // axisPTPC: TPC inner momentum — the variable that drives the proton PID selection logic. // Used alongside axisP so both total-p and pTPC views are available for every diagnostic plot. @@ -1094,13 +1174,11 @@ struct LambdaProtonBalanceFunction { AxisSpec nsigmaAxis = {100, -10.0f, 10.0f, "n#sigma"}; AxisSpec dcaAxis = {100, -1.0f, 1.0f, "DCAxy (cm)"}; AxisSpec dcaZAxis = {100, -1.0f, 1.0f, "DCAz (cm)"}; - AxisSpec massAxis = {200, 1.08f, 1.15f, "M (GeV/#it{c}^{2})"}; AxisSpec dcaAxisWide = {200, 0.0f, 5.0f, "DCA (cm)"}; AxisSpec multAxis = {100, 0.0f, 100.0f, "FT0M Percentile (%)"}; AxisSpec etaAxis = {kRhoEtaBins, kRhoMin, kRhoMax, "#eta"}; AxisSpec phiAxis = {kRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; AxisSpec unrolledAxis = {kRhoUnrolledBins, 0.0f, static_cast(kRhoUnrolledBins), "index(#eta,#varphi)"}; - static constexpr int kRhoUnrolledBinsY = kRhoYBins * kRhoPhiBins; AxisSpec unrolledAxisY = {kRhoUnrolledBinsY, 0.0f, static_cast(kRhoUnrolledBinsY), "index(y,#varphi)"}; AxisSpec rapidityAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; AxisSpec yAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; @@ -1126,16 +1204,14 @@ struct LambdaProtonBalanceFunction { registryOther.add("hNPrimAntiProtonRemovedByVeto", "AntiProton candidates removed by V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); registryOther.add("hNPrimAntiProtonAfterVeto", "AntiProton candidates after V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); // ───────────────────────────────────────────────────────────────────── - registryOther.add("hPtSelLambda", "#Lambda p_{T} yield", {HistType::kTH1F, {ptAxis}}); - registryOther.add("hPtSelAntiLambda", "#bar{#Lambda} p_{T} yield", {HistType::kTH1F, {ptAxis}}); - registryOther.add("hPtSelectedPrimProton", "Selected primary proton p_{T} yield", {HistType::kTH1F, {ptAxis}}); - registryOther.add("hPtSelectedPrimAntiProton", "Selected primary antiproton p_{T} yield", {HistType::kTH1F, {ptAxis}}); - registryOther.add("hNPairs_Lambda_PrimProton", "#Lambda-p pairs per event", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); - registryOther.add("hNPairs_Lambda_PrimAntiProton", "#Lambda-#bar{p} pairs per event", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); - registryOther.add("hNPairs_AntiLambda_PrimProton", "#bar{#Lambda}-p pairs per event", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); - registryOther.add("hNPairs_AntiLambda_PrimAntiProton", "#bar{#Lambda}-#bar{p} pairs per event", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); - - // ── Event cutflow ───────────────────────────────────────── + registryOther.add("hPtSelLambda", "Selected #Lambda p_{T} spectrum (raw counts);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelAntiLambda", "Selected #bar{#Lambda} p_{T} spectrum (raw counts);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelectedPrimProton", "Selected primary proton p_{T} spectrum (raw counts, after V0-daughter veto);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelectedPrimAntiProton", "Selected primary antiproton p_{T} spectrum (raw counts, after V0-daughter veto);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hNPairs_Lambda_PrimProton", "#Lambda-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_Lambda_PrimAntiProton", "#Lambda-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_AntiLambda_PrimProton", "#bar{#Lambda}-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_AntiLambda_PrimAntiProton", "#bar{#Lambda}-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); // ══════════════════════════════════════════════════════════════════════════ // NEW STAGED QA_Detec REGISTRATION @@ -1147,8 +1223,7 @@ struct LambdaProtonBalanceFunction { AxisSpec axisCosPA_zoom = {200, 0.99f, 1.0f, "Cos(PA)"}; AxisSpec axisDecayR = {100, 0.0f, 50.0f, "cm"}; AxisSpec axisCtau = {100, 0.0f, 50.0f, "ctau (cm)"}; - AxisSpec axisCutFlow = {5, 0.5f, 5.5f, "Cut stage"}; - AxisSpec axisTOFMatchFrac = {50, 0.0f, 1.0f, "TOF matching fraction"}; + AxisSpec axisCutFlow = {14, 0.5f, 14.5f, "Cut stage"}; auto addProtonQAForStage = [&](const char* species, const char* stage) { // Kinematics @@ -1197,16 +1272,16 @@ struct LambdaProtonBalanceFunction { auto addLambdaQAForStage = [&](const char* species, const char* stage) { // Kinematics - registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_pt", species, stage), "p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {axisPtProton}}); - registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_eta", species, stage), "#eta;#eta;Counts", {HistType::kTH1F, {qaEtaAxis}}); - registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_phi", species, stage), "#phi;#phi;Counts", {HistType::kTH1F, {qaPhiAxis}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_pt", species, stage), "V0 p_{T} (RawCandidate = all V0s before any cut, both hypotheses);p_{T} (GeV/c);Counts", {HistType::kTH1F, {axisPtProton}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_eta", species, stage), "V0 #eta (RawCandidate = all V0s before any cut, both hypotheses);#eta;Counts", {HistType::kTH1F, {qaEtaAxis}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_phi", species, stage), "V0 #varphi (RawCandidate = all V0s before any cut, both hypotheses);#varphi;Counts", {HistType::kTH1F, {qaPhiAxis}}); if (std::string(stage) == "02_Final") { registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_rapidity", species, stage), "y;y;Counts", {HistType::kTH1F, {rapidityAxis}}); } // Topology - registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa", species, stage), "Cos(PA);Cos(PA);Counts", {HistType::kTH1F, {{100, 0.9f, 1.0f, "Cos(PA)"}}}); - registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa_zoom", species, stage), "Cos(PA);Cos(PA);Counts", {HistType::kTH1F, {axisCosPA_zoom}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa", species, stage), "V0 cos(PA), full range;cos(PA);Counts", {HistType::kTH1F, {{100, 0.9f, 1.0f, "cos(PA)"}}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa_zoom", species, stage), "V0 cos(PA), zoom 0.99-1.0;cos(PA);Counts", {HistType::kTH1F, {axisCosPA_zoom}}); registryQaDetector.add(Form("%s/02_Topology/%s/h1f_dcaV0toPV", species, stage), "DCA V0 to PV;DCA (cm);Counts", {HistType::kTH1F, {dcaAxisWide}}); registryQaDetector.add(Form("%s/02_Topology/%s/h1f_ctau", species, stage), "ctau;ctau (cm);Counts", {HistType::kTH1F, {axisCtau}}); if (std::string(stage) == "01_RawCandidate" || std::string(stage) == "02_Final") { @@ -1280,21 +1355,45 @@ struct LambdaProtonBalanceFunction { addDaughterQA("02_AntiLambda", "02_AntiPion", stage); } - registryQaDetector.add("Selection/TOF_Matching/hTOFMatchedFractionVsPt", "TOF matched fraction vs p_{T};p_{T};Fraction", {HistType::kTProfile, {axisPtProton}}); + registryQaDetector.add("Selection/TOF_Matching/hTOFMatchedFractionVsPt", "TOF-matched fraction of selected (anti)protons (before V0-daughter veto) vs p_{T};p_{T} (GeV/c);TOF-matched fraction", {HistType::kTProfile, {axisPtProton}}); // ── Item 4: 2D TOF-matching efficiency maps (eta × phi) ────────────────── registryQaDetector.add("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi", - "Proton TOF matched fraction vs (#eta, #varphi);#eta;#varphi;Fraction", + "TOF-matched fraction of selected (anti)protons (before V0-daughter veto) vs (#eta, #varphi);#eta;#varphi;TOF-matched fraction", {HistType::kTProfile2D, {qaEtaAxis, qaPhiAxis}}); // ───────────────────────────────────────────────────────────────────────── - registryQaDetector.add("Selection/TOF_Matching/hCutFlow", "Proton Track Cutflow;Stage;Counts", {HistType::kTH1F, {axisCutFlow}}); + registryQaDetector.add("Selection/TOF_Matching/hCutFlow", "(Anti)proton track cutflow (both charges);Stage;Counts", {HistType::kTH1F, {axisCutFlow}}); + { + auto hCF = registryQaDetector.get(HIST("Selection/TOF_Matching/hCutFlow")); + hCF->GetXaxis()->SetBinLabel(1, "All tracks"); + hCF->GetXaxis()->SetBinLabel(2, "hasTPC"); + hCF->GetXaxis()->SetBinLabel(3, "TPC crossed rows"); + hCF->GetXaxis()->SetBinLabel(4, "TPC rows/findable"); + hCF->GetXaxis()->SetBinLabel(5, "TPC chi2"); + hCF->GetXaxis()->SetBinLabel(6, "ITS Ncls"); + hCF->GetXaxis()->SetBinLabel(7, "ITS chi2"); + hCF->GetXaxis()->SetBinLabel(8, "TPC signal"); + hCF->GetXaxis()->SetBinLabel(9, "p_TPC & pT window"); + hCF->GetXaxis()->SetBinLabel(10, "|y| window"); + hCF->GetXaxis()->SetBinLabel(11, "DCA xy,z"); + hCF->GetXaxis()->SetBinLabel(12, "PID (TPC/TOF)"); + hCF->GetXaxis()->SetBinLabel(13, "V0-daughter veto"); + hCF->GetXaxis()->SetBinLabel(14, "Selected"); + } // ══════════════════════════════════════════════════════════════════════════ // EXISTING QA_Detec REGISTRATION (Preserved for backward compatibility) // ══════════════════════════════════════════════════════════════════════════ - // -- Proton (existing) --r INEL > 0 + // -- Event cutflow -- registryOther.add("hEventCutflow", "Event Cutflow;Step;Counts", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + { + auto hEC = registryOther.get(HIST("hEventCutflow")); + hEC->GetXaxis()->SetBinLabel(1, "sel8 + |z|GetXaxis()->SetBinLabel(2, "numContrib >= 1"); + hEC->GetXaxis()->SetBinLabel(3, "centFT0M < 80"); + hEC->GetXaxis()->SetBinLabel(4, "INEL > 0"); + } // ── ProtonVetoQA histograms ─────────────────────────────────────────── // Purpose: quantify the importance of the V0-daughter veto for the @@ -1330,12 +1429,21 @@ struct LambdaProtonBalanceFunction { // hPileupFlags: each bin corresponds to a different event-selection bit. // Bin 1 = kNoSameBunchPileup, Bin 2 = kIsGoodZvtxFT0vsPV, // Bin 3 = kIsVertexITSTPC, Bin 4 = kIsVertexTOFmatched, - // Bin 5 = kIsVertexTRDmatched, Bin 6 = any pileup-rejection bit fired. + // Bin 5 = kIsVertexTRDmatched, Bin 6 = kNoSameBunchPileup && kIsGoodZvtxFT0vsPV && kIsVertexITSTPC all set. registryOther.add("hPileupFlags", - "Event-level pile-up rejection flags (Bin 6: All 3 passed (AND));Flag;Events passing", + "Event-level pile-up rejection flags;Flag;Events passing", {HistType::kTH1F, {{6, 0.5f, 6.5f, "Pileup flag"}}}); + { + auto hPU = registryOther.get(HIST("hPileupFlags")); + hPU->GetXaxis()->SetBinLabel(1, "NoSameBunchPileup"); + hPU->GetXaxis()->SetBinLabel(2, "GoodZvtxFT0vsPV"); + hPU->GetXaxis()->SetBinLabel(3, "VertexITSTPC"); + hPU->GetXaxis()->SetBinLabel(4, "VertexTOFmatched"); + hPU->GetXaxis()->SetBinLabel(5, "VertexTRDmatched"); + hPU->GetXaxis()->SetBinLabel(6, "NoSBPU&&GoodZvtx&&ITSTPC"); + } registryOther.add("h2f_NTracks_vs_Cent", - "N_{tracks}(|#eta|<0.8) vs FT0M cent;FT0M (%);N_{tracks}(|#eta|<0.8)", + "N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs FT0M cent;FT0M (%);N_{tracks}(|#eta|<0.8)", {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{tracks}"}}}); registryOther.add("h2f_NumContrib_vs_Cent", "N_{contribs} vs FT0M percentile;FT0M (%);N_{contribs}", @@ -1362,15 +1470,15 @@ struct LambdaProtonBalanceFunction { // ── Rho histograms ─────────────────────────────────────────────────── registryRho.add("hEventCounter", "Events", {HistType::kTH1F, {{1, 0.0, 1.0}}}); // eta-based rho1 - registryRho.add("h2_rho1_Proton", "rho1 proton", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiProton", "rho1 pbar", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_Lambda", "rho1 lambda", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiLambda", "rho1 lambdabar", {HistType::kTH2F, {etaAxis, phiAxis}}); + registryRho.add("h2_rho1_Proton", "#rho_{1}(p) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiProton", "#rho_{1}(#bar{p}) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); + registryRho.add("h2_rho1_Lambda", "#rho_{1}(#Lambda) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiLambda", "#rho_{1}(#bar{#Lambda}) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); // rapidity-based rho1 - registryRho.add("h2_rho1_Proton_y", "rho1 proton (y)", {HistType::kTH2F, {yAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiProton_y", "rho1 pbar (y)", {HistType::kTH2F, {yAxis, phiAxis}}); - registryRho.add("h2_rho1_Lambda_y", "rho1 lambda (y)", {HistType::kTH2F, {yAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiLambda_y", "rho1 lambdabar (y)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_Proton_y", "#rho_{1}(p) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiProton_y", "#rho_{1}(#bar{p}) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_Lambda_y", "#rho_{1}(#Lambda) in (y,#varphi)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiLambda_y", "#rho_{1}(#bar{#Lambda}) in (y,#varphi)", {HistType::kTH2F, {yAxis, phiAxis}}); // ───────────────────────────────────────────────────────────────────── // ── Autocorrelation / feed-down diagnostic histograms ──────────────── @@ -1379,28 +1487,30 @@ struct LambdaProtonBalanceFunction { // Folder: CorrelationQA/PrimaryProton_vs_LambdaDaughter/ // // h2f_dEta_dPhi : Δη vs Δφ for every (selected Lambda, selected - // primary proton) pair. No veto on shared tracks. - // A peak at (0,0) indicates track sharing / feed-down. + // primary proton) pair. The primary-proton list is + // ALREADY cleaned by the V0-daughter veto, so the same + // track cannot appear; this checks for residual + // proximity correlation (e.g. undetected feed-down). // h2f_dEta_dPhi_nonSameTrack : same, restricted to pairs where the primary // proton track is NOT the Lambda positive daughter. - AxisSpec axisCorDEta = {64, -1.6f, 1.6f}; + AxisSpec axisCorDEta = {64, -1.6f, 1.6f, "#Delta#eta"}; AxisSpec axisCorDPhi = {72, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#varphi"}; AxisSpec axisCorDPhiDisplay = {72, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; registryCorrelationQA.add( "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi", - "Primary Proton vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_display", - "Primary Proton vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); registryCorrelationQA.add( "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack", - "Primary Proton vs #Lambda Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display", - "Primary Proton vs #Lambda Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); // ───────────────────────────────────────────────────────────────────── @@ -1410,11 +1520,11 @@ struct LambdaProtonBalanceFunction { // Same-track matching is impossible here (opposite charges). registryCorrelationQA.add( "PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi", - "Primary AntiProton vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + "Primary AntiProton (after V0-daughter veto) vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi_display", - "Primary AntiProton vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + "Primary AntiProton (after V0-daughter veto) vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); // ───────────────────────────────────────────────────────────────────── @@ -1424,11 +1534,11 @@ struct LambdaProtonBalanceFunction { // Same-track matching is impossible here (opposite charges). registryCorrelationQA.add( "PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi", - "Primary Proton vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + "Primary Proton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display", - "Primary Proton vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + "Primary Proton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); // ───────────────────────────────────────────────────────────────────── @@ -1440,19 +1550,19 @@ struct LambdaProtonBalanceFunction { // the AntiLambda neg-daughter (i.e. genuine different-track pairs). registryCorrelationQA.add( "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi", - "Primary AntiProton vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display", - "Primary AntiProton vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); registryCorrelationQA.add( "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack", - "Primary AntiProton vs #bar{#Lambda} Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); registryCorrelationQA.add( "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display", - "Primary AntiProton vs #bar{#Lambda} Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); // ───────────────────────────────────────────────────────────────────── @@ -1478,13 +1588,14 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.add( "QA3/PairCleaning/h1f_pairCleaning", "p-p pair cleaning;Step;Counts", - {HistType::kTH1F, {{3, 0.5f, 3.5f}}}); + {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); // Bin labels are set after histogram registration { auto h = registryCorrelationQA.get(HIST("QA3/PairCleaning/h1f_pairCleaning")); h->GetXaxis()->SetBinLabel(1, "All Candidate Pairs"); h->GetXaxis()->SetBinLabel(2, "Rejected Same Track"); - h->GetXaxis()->SetBinLabel(3, "Accepted"); + h->GetXaxis()->SetBinLabel(3, "Rejected q_inv"); + h->GetXaxis()->SetBinLabel(4, "Accepted"); } // ───────────────────────────────────────────────────────────────────── @@ -1512,33 +1623,62 @@ struct LambdaProtonBalanceFunction { {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut", - "Split-track QA: q_{inv} for #Lambda-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut", - "Split-track QA: q_{inv} for #Lambda-#Lambda pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); // "After_qinvCut": filled only for pairs surviving the q_inv cut - // (q_inv > kQinvCutLP), applied uniformly to PP, LP, and LL pairs. + // (q_inv > kQinvCutLP), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut", - "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut;q_{inv} (GeV/c);Counts", + "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut", - "Split-track QA: q_{inv} for #Lambda-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut", - "Split-track QA: q_{inv} for #Lambda-#Lambda pairs, after q_{inv} cut;q_{inv} (GeV/c);Counts", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + // q_inv Before/After QA for p-pbar, pbar-p and pbar-pbar pairs + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut", + "Split-track QA: q_{inv} for p-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut", + "Split-track QA: q_{inv} for p-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_sharedClsFraction_t1", - "Split-track QA: TPC shared-cluster fraction (track 1 in close p-p pairs);Fraction;Counts", + "Split-track QA: TPC shared-cluster fraction, track 1 (p-p: first proton; #Lambda-p: V0 daughter) in close pairs;Fraction;Counts", {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_sharedClsFraction_t2", - "Split-track QA: TPC shared-cluster fraction (track 2 in close p-p pairs);Fraction;Counts", + "Split-track QA: TPC shared-cluster fraction, track 2 (p-p: second proton; #Lambda-p: primary (anti)proton) in close pairs;Fraction;Counts", {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); // ───────────────────────────────────────────────────────────────────── @@ -1553,58 +1693,58 @@ struct LambdaProtonBalanceFunction { {HistType::kTH1F, {pidCheckPtAxis}}); h_TPCorTOF = registryProtonPidCheck.add("h_TPCorTOF", - "Proton candidates: TPC mandatory, TOF optional if present;#it{p}_{T} (GeV/#it{c});Counts", + "Proton candidates: TPC required; if TOF present at any p, TOF n#sigma also required (differs from final selection, which requires TOF only above p_{TPC} switch);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); // ───────────────────────────────────────────────────────────────────── // ── Merged / pT015toMaxDefined histograms ─────────────────────────────────── hRho2_Lp_pT015toMaxDefined = registryRho.add("h2_rho2_Lp_pT015toMaxDefined", - "Lambda-p correlation (integrated pT range)", + "#rho_{2}(#Lambda, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_LAp_pT015toMaxDefined = registryRho.add("h2_rho2_LAp_pT015toMaxDefined", - "Lambda-pbar correlation (integrated pT range)", + "#rho_{2}(#Lambda, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_ALp_pT015toMaxDefined = registryRho.add("h2_rho2_ALp_pT015toMaxDefined", - "AntiLambda-p correlation (integrated pT range)", + "#rho_{2}(#bar{#Lambda}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_ALAp_pT015toMaxDefined = registryRho.add("h2_rho2_ALAp_pT015toMaxDefined", - "AntiLambda-pbar correlation (integrated pT range)", + "#rho_{2}(#bar{#Lambda}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_pp_pT015toMaxDefined = registryRho.add("h2_rho2_pp_pT015toMaxDefined", - "pp correlation", + "#rho_{2}(p, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_pAp_pT015toMaxDefined = registryRho.add("h2_rho2_pAp_pT015toMaxDefined", - "p-pbar correlation", + "#rho_{2}(p, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_App_pT015toMaxDefined = registryRho.add("h2_rho2_App_pT015toMaxDefined", - "pbar-p correlation", + "#rho_{2}(#bar{p}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_ApAp_pT015toMaxDefined = registryRho.add("h2_rho2_ApAp_pT015toMaxDefined", - "pbar-pbar correlation", + "#rho_{2}(#bar{p}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_LL_pT015toMaxDefined = registryRho.add("h2_rho2_LL_pT015toMaxDefined", - "Lambda-Lambda correlation", + "#rho_{2}(#Lambda, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_LAL_pT015toMaxDefined = registryRho.add("h2_rho2_LAL_pT015toMaxDefined", - "Lambda-AntiLambda correlation", + "#rho_{2}(#Lambda, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_ALL_pT015toMaxDefined = registryRho.add("h2_rho2_ALL_pT015toMaxDefined", - "AntiLambda-Lambda correlation", + "#rho_{2}(#bar{#Lambda}, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); hRho2_ALAL_pT015toMaxDefined = registryRho.add("h2_rho2_ALAL_pT015toMaxDefined", - "AntiLambda-AntiLambda correlation", + "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); // ── Rapidity merged rho2 histograms ───────────────────────────────────── @@ -1614,55 +1754,55 @@ struct LambdaProtonBalanceFunction { // so the booked axis must match that range or downstream R2/BF code that // expects y-space-sized histograms will see a dimension mismatch. hRho2_Lp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_Lp_y_pT015toMaxDefined", "Lambda-p (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_LAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAp_y_pT015toMaxDefined", "Lambda-pbar (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_ALp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALp_y_pT015toMaxDefined", "AntiLambda-p (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_ALAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAp_y_pT015toMaxDefined", "AntiLambda-pbar (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_pp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pp_y_pT015toMaxDefined", "pp (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_pAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pAp_y_pT015toMaxDefined", "p-pbar (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_App_y_pT015toMaxDefined = - registryRho.add("h2_rho2_App_y_pT015toMaxDefined", "pbar-p (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_ApAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ApAp_y_pT015toMaxDefined", "pbar-pbar (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_LL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LL_y_pT015toMaxDefined", "Lambda-Lambda (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_LAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAL_y_pT015toMaxDefined", "Lambda-AntiLambda (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_ALL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALL_y_pT015toMaxDefined", "AntiLambda-Lambda (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); hRho2_ALAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAL_y_pT015toMaxDefined", "AntiLambda-AntiLambda (y)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + registryRho.add("h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); // ───────────────────────────────────────────────────────────────────── hPtSelectedPrimProton_pT015toMaxDefined = registryOther.add("hPtSelectedPrimProton_pT015toMaxDefined", - "Proton p_{T} yield (integrated pT range)", + "Selected primary proton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); hPtSelectedPrimAntiProton_pT015toMaxDefined = registryOther.add("hPtSelectedPrimAntiProton_pT015toMaxDefined", - "Antiproton p_{T} yield (integrated pT range)", + "Selected primary antiproton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); hPtSelLambda_pT015toMaxDefined = registryOther.add("hPtSelLambda_pT015toMaxDefined", - "#Lambda p_{T} yield (integrated pT range)", + "Selected #Lambda p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); hPtSelAntiLambda_pT015toMaxDefined = registryOther.add("hPtSelAntiLambda_pT015toMaxDefined", - "#bar{#Lambda} p_{T} yield (integrated pT range)", + "Selected #bar{#Lambda} p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); hMassLambdaMerged = registryLambda.add("hMassLambdaNoMassCut_pT015toMaxDefined", - "Lambda Mass No Mass Cut (integrated pT range)", + "#Lambda mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); hMassAntiLambdaMerged = registryLambda.add("hMassAntiLambdaNoMassCut_pT015toMaxDefined", - "AntiLambda Mass No Mass Cut (integrated pT range)", + "#bar{#Lambda} mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); // ───────────────────────────────────────────────────────────────────── @@ -1681,39 +1821,39 @@ struct LambdaProtonBalanceFunction { hPtPrimProton_Lambda.push_back( registryOther.add( Form("hPtPrimProton_Lambda_ptBin%02zu", i), - Form("Proton pT in #Lambda-p pairs %.1f < pT < %.1f", ptLo, ptHi), + Form("Proton #it{p}_{T} in #Lambda-p pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); hPtPrimAntiProton_Lambda.push_back( registryOther.add( Form("hPtPrimAntiProton_Lambda_ptBin%02zu", i), - Form("Antiproton pT in #Lambda-#bar{p} pairs %.1f < pT < %.1f", ptLo, ptHi), + Form("Antiproton #it{p}_{T} in #Lambda-#bar{p} pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); hPtPrimProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimProton_AntiLambda_ptBin%02zu", i), - Form("Proton pT in #bar{#Lambda}-p pairs %.1f < pT < %.1f", ptLo, ptHi), + Form("Proton #it{p}_{T} in #bar{#Lambda}-p pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); hPtPrimAntiProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimAntiProton_AntiLambda_ptBin%02zu", i), - Form("Antiproton pT in #bar{#Lambda}-#bar{p} pairs %.1f < pT < %.1f", ptLo, ptHi), + Form("Antiproton #it{p}_{T} in #bar{#Lambda}-#bar{p} pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); hMassLambdaPtBins.push_back( registryLambda.add( Form("hMassLambdaNoMassCut_ptBin%02zu", i), - Form("Lambda Mass No Mass Cut %.1f < pT < %.1f", ptLo, ptHi), + Form("#Lambda mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), {HistType::kTH1F, {lambdaMassAxis}})); hMassAntiLambdaPtBins.push_back( registryLambda.add( Form("hMassAntiLambdaNoMassCut_ptBin%02zu", i), - Form("AntiLambda Mass No Mass Cut %.1f < pT < %.1f", ptLo, ptHi), + Form("#bar{#Lambda} mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), {HistType::kTH1F, {lambdaMassAxis}})); } // ───────────────────────────────────────────────────────────────────── // ── Extended y × pT invariant-mass histograms ───────────────────────── - // Rapidity bins: 4 bins from -1.0 to -0.6 in steps of 0.1 + // Rapidity bins: 2 bins from -0.8 to -0.6 in steps of 0.1 // pT bins: 25 bins from 0.0 to 2.5 in steps of 0.1 // Folder structure: // Lambda_invMassExtended/Lambda/hMassLambda_yBinXX_ptBinYY @@ -1744,7 +1884,7 @@ struct LambdaProtonBalanceFunction { hMassLambdaExtended[iY][iPt] = registryLambdaExtended.add( Form("Lambda/hMassLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("Lambda Invariant Mass | %.1f <= y %s %.1f | %.1f <= pT %s %.1f GeV/c", + Form("#Lambda mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), {HistType::kTH1F, {lambdaMassAxis}}); @@ -1752,12 +1892,81 @@ struct LambdaProtonBalanceFunction { hMassAntiLambdaExtended[iY][iPt] = registryLambdaExtended.add( Form("AntiLambda/hMassAntiLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("AntiLambda Invariant Mass | %.1f <= y %s %.1f | %.1f <= pT %s %.1f GeV/c", + Form("#bar{#Lambda} mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), {HistType::kTH1F, {lambdaMassAxis}}); } } // ───────────────────────────────────────────────────────────────────── + + // ══════════════════════════════════════════════════════════════════════ + // Centrality-differential histograms → top-level folder "rho1ANDrho2_LP_CentralityBased" + // ══════════════════════════════════════════════════════════════════════ + centSets.clear(); + centEdgesLocal = {0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}; + if (cFillCentHists.value) { + const size_t nCent = centEdgesLocal.size() - 1; + centSets.resize(nCent); + + for (size_t ic = 0; ic < nCent; ++ic) { + const float cLo = centEdgesLocal[ic]; + const float cHi = centEdgesLocal[ic + 1]; + const std::string dir = Form("Cent%02d_%02d", static_cast(cLo), static_cast(cHi)); + const std::string ctag = Form("FT0M %.0f-%.0f%%", cLo, cHi); + auto& S = centSets[ic]; + + S.hEvents = registryCent.add((dir + "/hEventCounter").c_str(), + ("Events, " + ctag).c_str(), + {HistType::kTH1F, {{1, 0.0, 1.0}}}); + + // ── rho1, y space (always) ── + S.rho1_Proton_y = registryCent.add((dir + "/h2_rho1_Proton_y").c_str(), ("#rho_{1}(p) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_AntiProton_y = registryCent.add((dir + "/h2_rho1_AntiProton_y").c_str(), ("#rho_{1}(#bar{p}) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_Lambda_y = registryCent.add((dir + "/h2_rho1_Lambda_y").c_str(), ("#rho_{1}(#Lambda) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_AntiLambda_y = registryCent.add((dir + "/h2_rho1_AntiLambda_y").c_str(), ("#rho_{1}(#bar{#Lambda}) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + + // ── rho2, y space (always) ── + auto addY = [&](std::shared_ptr& h, const char* name, const char* title) { + h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + }; + addY(S.Lp_y, "h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (y,#varphi)"); + addY(S.LAp_y, "h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); + addY(S.ALp_y, "h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); + addY(S.ALAp_y, "h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); + addY(S.pp_y, "h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p,p) (y,#varphi)"); + addY(S.pAp_y, "h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (y,#varphi)"); + addY(S.App_y, "h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (y,#varphi)"); + addY(S.ApAp_y, "h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); + addY(S.LL_y, "h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); + addY(S.LAL_y, "h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); + addY(S.ALL_y, "h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); + addY(S.ALAL_y, "h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); + + // ── eta space (only if BOTH switches on: memory heavy) ── + if (cFillEtaSpace.value) { + S.rho1_Proton = registryCent.add((dir + "/h2_rho1_Proton").c_str(), ("#rho_{1}(p) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); + S.rho1_AntiProton = registryCent.add((dir + "/h2_rho1_AntiProton").c_str(), ("#rho_{1}(#bar{p}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); + S.rho1_Lambda = registryCent.add((dir + "/h2_rho1_Lambda").c_str(), ("#rho_{1}(#Lambda) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); + S.rho1_AntiLambda = registryCent.add((dir + "/h2_rho1_AntiLambda").c_str(), ("#rho_{1}(#bar{#Lambda}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); + + auto addEta = [&](std::shared_ptr& h, const char* name, const char* title) { + h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); + }; + addEta(S.Lp, "h2_rho2_Lp_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (#eta,#varphi)"); + addEta(S.LAp, "h2_rho2_LAp_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (#eta,#varphi)"); + addEta(S.ALp, "h2_rho2_ALp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (#eta,#varphi)"); + addEta(S.ALAp, "h2_rho2_ALAp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (#eta,#varphi)"); + addEta(S.pp, "h2_rho2_pp_pT015toMaxDefined", "#rho_{2}(p,p) (#eta,#varphi)"); + addEta(S.pAp, "h2_rho2_pAp_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (#eta,#varphi)"); + addEta(S.App, "h2_rho2_App_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (#eta,#varphi)"); + addEta(S.ApAp, "h2_rho2_ApAp_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (#eta,#varphi)"); + addEta(S.LL, "h2_rho2_LL_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (#eta,#varphi)"); + addEta(S.LAL, "h2_rho2_LAL_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (#eta,#varphi)"); + addEta(S.ALL, "h2_rho2_ALL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (#eta,#varphi)"); + addEta(S.ALAL, "h2_rho2_ALAL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (#eta,#varphi)"); + } + } + } } Filter eventFilter = (o2::aod::evsel::sel8 == true); @@ -1798,9 +2007,9 @@ struct LambdaProtonBalanceFunction { // ALAp : trigger=AntiLambda,assoc=antiproton // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) // - // Each channel has BOTH eta-space (unrolledIndex) and - // rapidity-space (unrolledIndexY) versions filled in the SAME loop. - // fillRho2Pair() handles both fills atomically. + // Each channel has both eta-space (unrolledIndex) and + // rapidity-space (unrolledIndexY) versions filled in the same loop; the + // eta-space fills are gated by cFillEtaSpace. // ───────────────────────────────────────────────────────────────────── // ── Event cutflow bin 1 — enters process (after sel8 + posZ filters) ── @@ -1819,7 +2028,7 @@ struct LambdaProtonBalanceFunction { // ── Event cutflow bin 3 — after centFT0M < 80 ──────────────────────── registryOther.fill(HIST("hEventCutflow"), 3.0f); - // INEL > 0: require at least one charged track in |eta| < 1.0 + // INEL > 0: require at least one charged track in |eta| < 0.8 int nTracksINEL = 0; static constexpr float kMaxInelEta = 0.8f; for (auto const& trk : tracks) { @@ -1834,6 +2043,17 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("hEventCutflow"), 4.0f); registryRho.fill(HIST("hEventCounter"), 0.5); + + // ── Centrality bin for the centrality-differential histograms ───────── + CentRho2Set* cs = nullptr; + if (cFillCentHists.value) { + const int iCent = centBinIndex(collision.centFT0M()); + if (iCent >= 0 && iCent < static_cast(centSets.size())) { + cs = ¢Sets[iCent]; + cs->hEvents->Fill(0.5); + } + } + registryOther.fill(HIST("hVertexZRec"), collision.posZ()); registryOther.fill(HIST("hFT0MPercentile"), collision.centFT0M()); @@ -1869,6 +2089,7 @@ struct LambdaProtonBalanceFunction { // ====== Step 1: Initialise containers ====================================== std::unordered_set v0DaughterTrackIds; + v0DaughterTrackIds.reserve(64); std::vector selectedPrimProtons; std::vector selectedPrimAntiProtons; std::vector selectedLambdas; @@ -1896,6 +2117,8 @@ struct LambdaProtonBalanceFunction { constexpr float mK0PDG = o2::constants::physics::MassK0Short; for (const auto& v0 : V0s) { + const auto& v0PosDauCached = v0.posTrack_as(); + const auto& v0NegDauCached = v0.negTrack_as(); // ── TRULY RAW QA Fills — before any V0 selection cut ───────────── // Placed here at the very top of the loop: before v0Type, daughter @@ -1905,8 +2128,8 @@ struct LambdaProtonBalanceFunction { // and the AntiLambda hypothesis (pos=pion, neg=proton) since no PID // decision has been made yet. This gives the true combinatorial view. { - const auto& rawPosDau = v0.posTrack_as(); - const auto& rawNegDau = v0.negTrack_as(); + const auto& rawPosDau = v0PosDauCached; + const auto& rawNegDau = v0NegDauCached; const float rawCtau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; const float rawBgctau = rawCtau * v0.p() / mLambdaPDG; // Lambda hypothesis (positive daughter assumed proton) @@ -1932,8 +2155,8 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────── - const auto& v0PosDau = v0.posTrack_as(); - const auto& v0NegDau = v0.negTrack_as(); + const auto& v0PosDau = v0PosDauCached; + const auto& v0NegDau = v0NegDauCached; // ── Daughter kinematic quality cuts (from strange9) ─────────────── if (v0PosDau.pt() <= cV0DauMinPt.value || v0NegDau.pt() <= cV0DauMinPt.value || @@ -2075,7 +2298,7 @@ struct LambdaProtonBalanceFunction { // ── Extended y × pT Lambda invariant-mass fill ──────────────────── // Use the same rapidity variable `y` computed above // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -1.0 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. + // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { // Determine rapidity bin index int iYext = static_cast((y - kExtYMin) / kExtYStep); @@ -2110,7 +2333,7 @@ struct LambdaProtonBalanceFunction { // ── Extended y × pT AntiLambda invariant-mass fill ──────────────── // Use the same rapidity variable `y` computed above // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -1.0 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. + // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { int iYext = static_cast((y - kExtYMin) / kExtYStep); if (iYext >= kExtNyBins) { @@ -2165,8 +2388,16 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, v0PosDau); fillDaughterQA(DaughterQAStage::Final, v0NegDau); - registryRho.fill(HIST("h2_rho1_Lambda"), v0.eta(), v0.phi()); + if (cFillEtaSpace.value) { + registryRho.fill(HIST("h2_rho1_Lambda"), v0.eta(), v0.phi()); + } registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); + if (cs) { + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); + if (cs->rho1_Lambda) { + cs->rho1_Lambda->Fill(v0.eta(), v0.phi()); + } + } registryOther.fill(HIST("hPtSelLambda"), pt); hPtSelLambda_pT015toMaxDefined->Fill(pt); } @@ -2181,8 +2412,16 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, v0NegDau); fillDaughterQA(DaughterQAStage::Final, v0PosDau); - registryRho.fill(HIST("h2_rho1_AntiLambda"), v0.eta(), v0.phi()); + if (cFillEtaSpace.value) { + registryRho.fill(HIST("h2_rho1_AntiLambda"), v0.eta(), v0.phi()); + } registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); + if (cs) { + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); + if (cs->rho1_AntiLambda) { + cs->rho1_AntiLambda->Fill(v0.eta(), v0.phi()); + } + } registryOther.fill(HIST("hPtSelAntiLambda"), pt); hPtSelAntiLambda_pT015toMaxDefined->Fill(pt); } @@ -2199,47 +2438,54 @@ struct LambdaProtonBalanceFunction { if (trk.sign() > 0) { fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) + if (trk.hasTPC()) { fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTOF()) + } + if (trk.hasTOF()) { fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } } else { fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) + if (trk.hasTPC()) { fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTOF()) + } + if (trk.hasTOF()) { fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } } // ───────────────────────────────────────────────────────────────── // ── Track quality cuts ──────────────────────────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 1.0f); // Quality cut starting point (after kinematics) + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 1.0f); if (!trk.hasTPC()) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 2.0f); // passed hasTPC + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 2.0f); if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 3.0f); // passed tpcNClsCrossedRows + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 3.0f); if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 4.0f); // passed tpcCrossedRowsOverFindableCls + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 4.0f); if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 5.0f); // passed tpcChi2NCl + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 5.0f); if (trk.itsNCls() < pProtonITSMinClusters.value) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 6.0f); if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 7.0f); if (trk.tpcSignal() < cPPMinTpcSignal.value) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 8.0f); // ───────────────────────────────────────────────────────────────── // ── p_TPC and pT acceptance cuts ───────────────────────────────────────────── @@ -2247,9 +2493,10 @@ struct LambdaProtonBalanceFunction { if (pTPC < pProtonMinP.value || pTPC > pProtonMaxP.value) { continue; } - if (trk.pt() < pProtonMinP.value || trk.pt() > pProtonMaxP.value) { + if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 9.0f); // ───────────────────────────────────────────────────────────────── // ── Proton kinematic acceptance ─────────────────────────────────── @@ -2259,6 +2506,7 @@ struct LambdaProtonBalanceFunction { if (std::abs(y) > pProtonMaxY.value) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 10.0f); // ───────────────────────────────────────────────────────────────── // ── DCA cuts ───────────────────────────────────────────────────── const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && @@ -2266,6 +2514,7 @@ struct LambdaProtonBalanceFunction { if (!passDCA) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 11.0f); // ───────────────────────────────────────────────────────────────── // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) @@ -2294,14 +2543,16 @@ struct LambdaProtonBalanceFunction { fillProtonKinematics(ProtonQAStage::TpcPid, trk); fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) + if (trk.hasTOF()) { fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } } else { fillProtonKinematics(ProtonQAStage::TpcPid, trk); fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) + if (trk.hasTOF()) { fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } } } @@ -2324,6 +2575,7 @@ struct LambdaProtonBalanceFunction { if (!passesPrimProtonPid(trk)) { continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 12.0f); // TOF Matching Fraction check on selected protons registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); @@ -2363,7 +2615,7 @@ struct LambdaProtonBalanceFunction { // This MUST appear before push_back so that every pp histogram // (which iterates selectedPrimProtons / selectedPrimAntiProtons) // automatically uses the cleaned collection. - const bool isV0Daughter = v0DaughterTrackIds.count(trk.globalIndex()) > 0; + const bool isV0Daughter = v0DaughterTrackIds.contains(trk.globalIndex()); if (trk.sign() > 0) { ++nProtonBeforeVeto; } else { @@ -2378,9 +2630,11 @@ struct LambdaProtonBalanceFunction { } continue; } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 13.0f); // ───────────────────────────────────────────────────────────────── // ── Classify into proton / antiproton lists ─────────────────────── + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 14.0f); // VS Code: commented out unused variables. // const float pz = trk.pt() * std::sinh(trk.eta()); // const float eta = trk.eta(); @@ -2391,8 +2645,9 @@ struct LambdaProtonBalanceFunction { fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); // TOF QA only when track was accepted through the TPC+TOF branch: // p > PProtonTPC_TOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC > pProtonTPCTOFSwitchP.value) + if (pTPC >= pProtonTPCTOFSwitchP.value) { fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); hPtSelectedPrimProton_pT015toMaxDefined->Fill(trk.pt()); @@ -2403,8 +2658,9 @@ struct LambdaProtonBalanceFunction { fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); // TOF QA only when track was accepted through the TPC+TOF branch: // p > pProtonTPCTOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC > pProtonTPCTOFSwitchP.value) + if (pTPC >= pProtonTPCTOFSwitchP.value) { fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); hPtSelectedPrimAntiProton_pT015toMaxDefined->Fill(trk.pt()); @@ -2442,25 +2698,55 @@ struct LambdaProtonBalanceFunction { // Accumulate run-level totals for endOfStream() summary. runTotalProtonBeforeVeto += nBefore; runTotalProtonRemovedVeto += nRemoved; + runTotalAntiPBeforeVeto += nAntiPBeforeVeto; + runTotalAntiPRemovedVeto += nAntiPRemovedVeto; } // ───────────────────────────────────────────────────────────────────── // ── Single-particle density for protons (eta and y) ────────────────── for (auto const& primProton : selectedPrimProtons) { - registryRho.fill(HIST("h2_rho1_Proton"), primProton.eta(), primProton.phi()); + if (cFillEtaSpace.value) { + registryRho.fill(HIST("h2_rho1_Proton"), primProton.eta(), primProton.phi()); + } const float yp = protonRapidity(primProton); registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi()); + if (cs) { + cs->rho1_Proton_y->Fill(yp, primProton.phi()); + if (cs->rho1_Proton) { + cs->rho1_Proton->Fill(primProton.eta(), primProton.phi()); + } + } } for (auto const& primAntiProton : selectedPrimAntiProtons) { - registryRho.fill(HIST("h2_rho1_AntiProton"), primAntiProton.eta(), primAntiProton.phi()); + if (cFillEtaSpace.value) { + registryRho.fill(HIST("h2_rho1_AntiProton"), primAntiProton.eta(), primAntiProton.phi()); + } const float yap = protonRapidity(primAntiProton); registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi()); + if (cs) { + cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi()); + if (cs->rho1_AntiProton) { + cs->rho1_AntiProton->Fill(primAntiProton.eta(), primAntiProton.phi()); + } + } } // ───────────────────────────────────────────────────────────────────── registryOther.fill(HIST("hNSelectedPrimProtons"), static_cast(selectedPrimProtons.size())); registryOther.fill(HIST("hNSelectedPrimAntiProtons"), static_cast(selectedPrimAntiProtons.size())); + // ── Per-particle caches for the pair loops ─────────────────────────── + std::vector protonInfo; + std::vector antiProtonInfo; + protonInfo.reserve(selectedPrimProtons.size()); + antiProtonInfo.reserve(selectedPrimAntiProtons.size()); + for (auto const& t : selectedPrimProtons) { + protonInfo.push_back(makeProtonInfo(t)); + } + for (auto const& t : selectedPrimAntiProtons) { + antiProtonInfo.push_back(makeProtonInfo(t)); + } + // ── pp rho2 fills (eta and y) ─────────────────────────────────────────── for (auto const& p1 : selectedPrimProtons) { const float y1_pp = protonRapidity(p1); @@ -2472,8 +2758,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 2); continue; } // CHANGED from globalIndex() - // QA3/PairCleaning: accepted pair - registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 3); + // QA3/PairCleaning: bins 3 (Rejected q_inv) and 4 (Accepted) are filled at the q_inv cut below // QA3/RawPairDensity: fill Δη vs Δφ for every accepted p-p pair { @@ -2490,7 +2775,7 @@ struct LambdaProtonBalanceFunction { const float E2 = std::sqrt(p2mag * p2mag + kMassProton * kMassProton); const float qinvPP = computeQinv(p1.px(), p1.py(), p1.pz(), E1, p2.px(), p2.py(), p2.pz(), E2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut"), qinvPP); - if (qinvPP > kQinvCutLP) { + if (!failsQinvCut(qinvPP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut"), qinvPP); } } @@ -2512,68 +2797,94 @@ struct LambdaProtonBalanceFunction { } } + // q_inv cut (pp) + { + const float qE1 = std::sqrt(p1.px() * p1.px() + p1.py() * p1.py() + p1.pz() * p1.pz() + kMassProton * kMassProton); + const float qE2 = std::sqrt(p2.px() * p2.px() + p2.py() * p2.py() + p2.pz() * p2.pz() + kMassProton * kMassProton); + if (failsQinvCut(computeQinv(p1.px(), p1.py(), p1.pz(), qE1, p2.px(), p2.py(), p2.pz(), qE2))) { + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 3); + continue; + } + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 4); + } const int idx1 = unrolledIndex(p1.eta(), p1.phi()); const int idx2 = unrolledIndex(p2.eta(), p2.phi()); const float y2_pp = protonRapidity(p2); const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); - // Physics pair correlations (rho2): q_inv cut not applied to rho2 - if (idx1 >= 0 && idx2 >= 0) { + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { hRho2_pp_pT015toMaxDefined->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { hRho2_pp_y_pT015toMaxDefined->Fill(idxY1, idxY2); } + if (cs) { + fillIdx(cs->pp, idx1, idx2); + fillIdx(cs->pp_y, idxY1, idxY2); + } } } - for (auto const& p1 : selectedPrimProtons) { - const float y1_pAp = protonRapidity(p1); - for (auto const& p2 : selectedPrimAntiProtons) { - const int idx1 = unrolledIndex(p1.eta(), p1.phi()); - const int idx2 = unrolledIndex(p2.eta(), p2.phi()); - const float y2_pAp = protonRapidity(p2); - const int idxY1 = unrolledIndexY(y1_pAp, p1.phi()); - const int idxY2 = unrolledIndexY(y2_pAp, p2.phi()); - if (idx1 >= 0 && idx2 >= 0) { - hRho2_pAp_pT015toMaxDefined->Fill(idx1, idx2); + for (auto const& a : protonInfo) { + for (auto const& b : antiProtonInfo) { + const float qinvPAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut"), qinvPAp); + if (failsQinvCut(qinvPAp)) { + continue; } - if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_pAp_y_pT015toMaxDefined->Fill(idxY1, idxY2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); + if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { + hRho2_pAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + } + if (a.idxY >= 0 && b.idxY >= 0) { + hRho2_pAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + } + if (cs) { + fillIdx(cs->pAp, a.idxEta, b.idxEta); + fillIdx(cs->pAp_y, a.idxY, b.idxY); } } } - for (auto const& p1 : selectedPrimAntiProtons) { - const float y1_App = protonRapidity(p1); - for (auto const& p2 : selectedPrimProtons) { - const int idx1 = unrolledIndex(p1.eta(), p1.phi()); - const int idx2 = unrolledIndex(p2.eta(), p2.phi()); - const float y2_App = protonRapidity(p2); - const int idxY1 = unrolledIndexY(y1_App, p1.phi()); - const int idxY2 = unrolledIndexY(y2_App, p2.phi()); - if (idx1 >= 0 && idx2 >= 0) { - hRho2_App_pT015toMaxDefined->Fill(idx1, idx2); + for (auto const& a : antiProtonInfo) { + for (auto const& b : protonInfo) { + const float qinvApp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut"), qinvApp); + if (failsQinvCut(qinvApp)) { + continue; } - if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_App_y_pT015toMaxDefined->Fill(idxY1, idxY2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); + if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { + hRho2_App_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + } + if (a.idxY >= 0 && b.idxY >= 0) { + hRho2_App_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + } + if (cs) { + fillIdx(cs->App, a.idxEta, b.idxEta); + fillIdx(cs->App_y, a.idxY, b.idxY); } } } - for (auto const& p1 : selectedPrimAntiProtons) { - const float y1_ApAp = protonRapidity(p1); - for (auto const& p2 : selectedPrimAntiProtons) { - if (p1.index() == p2.index()) { + for (auto const& a : antiProtonInfo) { + for (auto const& b : antiProtonInfo) { + if (a.gid == b.gid) { continue; - } // CHANGED from globalIndex() - const int idx1 = unrolledIndex(p1.eta(), p1.phi()); - const int idx2 = unrolledIndex(p2.eta(), p2.phi()); - const float y2_ApAp = protonRapidity(p2); - const int idxY1 = unrolledIndexY(y1_ApAp, p1.phi()); - const int idxY2 = unrolledIndexY(y2_ApAp, p2.phi()); - if (idx1 >= 0 && idx2 >= 0) { - hRho2_ApAp_pT015toMaxDefined->Fill(idx1, idx2); } - if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_ApAp_y_pT015toMaxDefined->Fill(idxY1, idxY2); + const float qinvApAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut"), qinvApAp); + if (failsQinvCut(qinvApAp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); + if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { + hRho2_ApAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + } + if (a.idxY >= 0 && b.idxY >= 0) { + hRho2_ApAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + } + if (cs) { + fillIdx(cs->ApAp, a.idxEta, b.idxEta); + fillIdx(cs->ApAp_y, a.idxY, b.idxY); } } } @@ -2609,7 +2920,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); } // QA3/SplitTrackQA: q_inv Before and After cut - if (qinvLP > kQinvCutLP) { + if (!failsQinvCut(qinvLP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLP); } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks @@ -2633,16 +2944,28 @@ struct LambdaProtonBalanceFunction { const float yP = protonRapidity(primProton); const int idxYL = unrolledIndexY(yL, v0.phi()); const int idxYP = unrolledIndexY(yP, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut not applied to rho2 + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (failsQinvCut(qinvLP)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); if (idxLambda >= 0 && idxPrimP >= 0) { if (bin >= 0) { hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } - hRho2_Lp_pT015toMaxDefined->Fill(idxLambda, idxPrimP); + if (cFillEtaSpace.value) { + hRho2_Lp_pT015toMaxDefined->Fill(idxLambda, idxPrimP); + } } if (idxYL >= 0 && idxYP >= 0) { hRho2_Lp_y_pT015toMaxDefined->Fill(idxYL, idxYP); } + if (cs) { + fillIdx(cs->Lp, idxLambda, idxPrimP); + fillIdx(cs->Lp_y, idxYL, idxYP); + } ++nPairs_Lambda_PrimProton; } for (auto const& primAntiProton : selectedPrimAntiProtons) { @@ -2660,7 +2983,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (qinvLAp > kQinvCutLP) { + if (!failsQinvCut(qinvLAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLAp); } // QA3/RawPairDensity: Lambda-antiproton → same LP histogram @@ -2677,27 +3000,39 @@ struct LambdaProtonBalanceFunction { const float yAp = protonRapidity(primAntiProton); const int idxYL2 = unrolledIndexY(yL2, v0.phi()); const int idxYAp = unrolledIndexY(yAp, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut not applied to rho2 + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (failsQinvCut(qinvLAp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); if (idxLambda >= 0 && idxPrimPbar >= 0) { if (bin >= 0) { hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } - hRho2_LAp_pT015toMaxDefined->Fill(idxLambda, idxPrimPbar); + if (cFillEtaSpace.value) { + hRho2_LAp_pT015toMaxDefined->Fill(idxLambda, idxPrimPbar); + } } if (idxYL2 >= 0 && idxYAp >= 0) { hRho2_LAp_y_pT015toMaxDefined->Fill(idxYL2, idxYAp); } + if (cs) { + fillIdx(cs->LAp, idxLambda, idxPrimPbar); + fillIdx(cs->LAp_y, idxYL2, idxYAp); + } ++nPairs_Lambda_PrimAntiProton; } // ── Autocorrelation diagnostic: primary proton vs Lambda daughter proton── // Lambda proton daughter = v0PosDau (positive track, identified as proton // by |tpcNSigmaPr| < PProtonTPCNsigma, already in scope above). - // Loop over ALL selected primary protons — no daughter-track veto applied - // intentionally (the purpose is to DETECT the autocorrelation). + // Loop over ALL selected primary protons. NOTE: selectedPrimProtons is already + // cleaned by the V0-daughter veto, so the same-track case cannot occur here; + // this only checks for residual proximity correlations (e.g. undetected feed-down). // Fills inclusive h2f_dEta_dPhi for all pairs and - // h2f_dEta_dPhi_nonSameTrack for pairs where the primary proton is not - // the Lambda positive daughter. + // h2f_dEta_dPhi_nonSameTrack for every pair in this cleaned sample. { const float etaDau = v0PosDau.eta(); const float phiDau = v0PosDau.phi(); @@ -2776,7 +3111,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (qinvALp > kQinvCutLP) { + if (!failsQinvCut(qinvALp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALp); } // QA3/RawPairDensity: AntiLambda-proton → same LP histogram @@ -2793,16 +3128,28 @@ struct LambdaProtonBalanceFunction { const float yP2 = protonRapidity(primProton); const int idxYAL = unrolledIndexY(yAL, v0.phi()); const int idxYP2 = unrolledIndexY(yP2, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut not applied to rho2 + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (failsQinvCut(qinvALp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); if (idxAL >= 0 && idxPrimP >= 0) { if (bin >= 0) { hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } - hRho2_ALp_pT015toMaxDefined->Fill(idxAL, idxPrimP); + if (cFillEtaSpace.value) { + hRho2_ALp_pT015toMaxDefined->Fill(idxAL, idxPrimP); + } } if (idxYAL >= 0 && idxYP2 >= 0) { hRho2_ALp_y_pT015toMaxDefined->Fill(idxYAL, idxYP2); } + if (cs) { + fillIdx(cs->ALp, idxAL, idxPrimP); + fillIdx(cs->ALp_y, idxYAL, idxYP2); + } ++nPairs_AntiLambda_PrimProton; } for (auto const& primAntiProton : selectedPrimAntiProtons) { @@ -2820,7 +3167,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (qinvALAp > kQinvCutLP) { + if (!failsQinvCut(qinvALAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks @@ -2844,16 +3191,28 @@ struct LambdaProtonBalanceFunction { const float yAp2 = protonRapidity(primAntiProton); const int idxYAL2 = unrolledIndexY(yAL2, v0.phi()); const int idxYAp2 = unrolledIndexY(yAp2, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut not applied to rho2 + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (failsQinvCut(qinvALAp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); if (idxAL2 >= 0 && idxPrimPb >= 0) { if (bin >= 0) { hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } - hRho2_ALAp_pT015toMaxDefined->Fill(idxAL2, idxPrimPb); + if (cFillEtaSpace.value) { + hRho2_ALAp_pT015toMaxDefined->Fill(idxAL2, idxPrimPb); + } } if (idxYAL2 >= 0 && idxYAp2 >= 0) { hRho2_ALAp_y_pT015toMaxDefined->Fill(idxYAL2, idxYAp2); } + if (cs) { + fillIdx(cs->ALAp, idxAL2, idxPrimPb); + fillIdx(cs->ALAp_y, idxYAL2, idxYAp2); + } ++nPairs_AntiLambda_PrimAntiProton; } @@ -2881,9 +3240,10 @@ struct LambdaProtonBalanceFunction { // ── Combo 4: AntiLambda daughter antiproton ↔ primary antiproton ─ // AntiLambda neg-daughter = v0NegDau (charge −1, identified as // antiproton). Primary antiproton = selectedPrimAntiProtons (charge −1). - // Fills inclusive h2f_dEta_dPhi for all pairs and - // h2f_dEta_dPhi_nonSameTrack for pairs where the primary antiproton is - // not the AntiLambda negative daughter. + // Fills inclusive h2f_dEta_dPhi for all pairs and h2f_dEta_dPhi_nonSameTrack + // for every pair in this cleaned sample (selectedPrimAntiProtons is already + // veto-cleaned, so the primary antiproton can never be the AntiLambda + // negative daughter; the nonSameTrack histogram will equal the inclusive one). { const float etaDauAL4 = v0NegDau.eta(); const float phiDauAL4 = v0NegDau.phi(); @@ -2951,21 +3311,26 @@ struct LambdaProtonBalanceFunction { const float Ev2 = std::sqrt(v2pmag * v2pmag + kMassLambda * kMassLambda); const float qinvLL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1, v2.px(), v2.py(), v2.pz(), Ev2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLL); - if (qinvLL > kQinvCutLP) { - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); + if (failsQinvCut(qinvLL)) { + continue; } + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); } const int idx1 = unrolledIndex(v1.eta(), v1.phi()); const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1L, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut not applied to rho2 - if (idx1 >= 0 && idx2 >= 0) { + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { hRho2_LL_pT015toMaxDefined->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { hRho2_LL_y_pT015toMaxDefined->Fill(idxY1, idxY2); } + if (cs) { + fillIdx(cs->LL, idx1, idx2); + fillIdx(cs->LL_y, idxY1, idxY2); + } } } // LAL @@ -3000,21 +3365,26 @@ struct LambdaProtonBalanceFunction { const float Ev2L = std::sqrt(v2pmagL * v2pmagL + kMassLambda * kMassLambda); const float qinvLAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1L, v2.px(), v2.py(), v2.pz(), Ev2L); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLAL); - if (qinvLAL > kQinvCutLP) { - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); + if (failsQinvCut(qinvLAL)) { + continue; } + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); } const int idx1 = unrolledIndex(v1.eta(), v1.phi()); const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut not applied to rho2 - if (idx1 >= 0 && idx2 >= 0) { + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { hRho2_LAL_pT015toMaxDefined->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { hRho2_LAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); } + if (cs) { + fillIdx(cs->LAL, idx1, idx2); + fillIdx(cs->LAL_y, idxY1, idxY2); + } } } // ALL @@ -3049,21 +3419,26 @@ struct LambdaProtonBalanceFunction { const float Ev2AL = std::sqrt(v2pmagAL * v2pmagAL + kMassLambda * kMassLambda); const float qinvALL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL, v2.px(), v2.py(), v2.pz(), Ev2AL); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALL); - if (qinvALL > kQinvCutLP) { - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); + if (failsQinvCut(qinvALL)) { + continue; } + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); } const int idx1 = unrolledIndex(v1.eta(), v1.phi()); const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut not applied to rho2 - if (idx1 >= 0 && idx2 >= 0) { + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { hRho2_ALL_pT015toMaxDefined->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { hRho2_ALL_y_pT015toMaxDefined->Fill(idxY1, idxY2); } + if (cs) { + fillIdx(cs->ALL, idx1, idx2); + fillIdx(cs->ALL_y, idxY1, idxY2); + } } } // ALAL @@ -3101,21 +3476,26 @@ struct LambdaProtonBalanceFunction { const float Ev2AL2 = std::sqrt(v2pmagAL2 * v2pmagAL2 + kMassLambda * kMassLambda); const float qinvALAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL2, v2.px(), v2.py(), v2.pz(), Ev2AL2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALAL); - if (qinvALAL > kQinvCutLP) { - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); + if (failsQinvCut(qinvALAL)) { + continue; } + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); } const int idx1 = unrolledIndex(v1.eta(), v1.phi()); const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut not applied to rho2 - if (idx1 >= 0 && idx2 >= 0) { + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { hRho2_ALAL_pT015toMaxDefined->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { hRho2_ALAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); } + if (cs) { + fillIdx(cs->ALAL, idx1, idx2); + fillIdx(cs->ALAL_y, idxY1, idxY2); + } } } // ───────────────────────────────────────────────────────────────────── @@ -3145,6 +3525,12 @@ struct LambdaProtonBalanceFunction { LOG(info) << "Total proton candidates after veto : " << totalAfter; LOG(info) << "Total removed by veto : " << totalRemoved; LOG(info) << Form("Fraction removed : %.6f", fracRemoved); + const double fracRemovedAp = (runTotalAntiPBeforeVeto > 0) + ? static_cast(runTotalAntiPRemovedVeto) / static_cast(runTotalAntiPBeforeVeto) + : 0.0; + LOG(info) << "Total antiproton candidates before veto: " << runTotalAntiPBeforeVeto; + LOG(info) << "Total antiproton removed by veto : " << runTotalAntiPRemovedVeto; + LOG(info) << Form("Antiproton fraction removed : %.6f", fracRemovedAp); LOG(info) << "==============================================="; LOG(info) << ""; } @@ -3155,6 +3541,3 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } - -// What i did here is to change the eta acceptance from 0.8 to 1.0 for now for QA purposes, but later for final analysis we will change it back to 0.8. So please ignore the changes related to maxEta and qaEtaAxis for now. I have kept the maxEta as 1.0f so that it can be used for track acceptance in the code, but we will change it to 0.8f later for final analysis. The same goes for qaEtaAxis, I have set it to -1.0f to 1.0f for now, but we will change it to -0.8f to 0.8f later for final analysis. -// Also we previously gave 0.8 rapidity cut for lambda and anti-lambda, but now we have changed it to 1.0 for now for QA purposes, but later for final analysis we will change it back to 0.8. So please ignore the changes related to rapidity cuts for now. From 9254241bb3d4b4c5bc0b5bc789497634eb33f2c4 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Tue, 22 Sep 2026 12:40:26 +0200 Subject: [PATCH 028/648] [PWGLF] Fix forwardlambdakzeroanalysis (#18004) --- PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx index 4ea9d1ccb5a..1abe53f3258 100644 --- a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx @@ -2800,7 +2800,7 @@ struct forwardlambdakzeroanalysis { initCCDB(bcs, collision); if (!isEventAccepted(collision, bcs, true)) { - return; + continue; } float centrality = -1; From 40f376b5f47bae9b89ecd1c00a75e9303bf55d71 Mon Sep 17 00:00:00 2001 From: Subhadeep Mandal <147193283+5ub-Man@users.noreply.github.com> Date: Tue, 22 Sep 2026 18:26:39 +0530 Subject: [PATCH 029/648] [PWGUD] Added process functions for MC closure (#18003) --- PWGUD/Tasks/sginclusivePhiKstarSD.cxx | 813 ++++++++++++++++++++++---- 1 file changed, 701 insertions(+), 112 deletions(-) diff --git a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx index 80add1374fa..29a6fa3a46e 100644 --- a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx +++ b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx @@ -44,6 +44,9 @@ #include #include +#include + +#include #include #include #include @@ -65,7 +68,7 @@ using o2::aod::sgselector::SingleGapC; struct SginclusivePhiKstarSD { SGSelector sgSelector; - Service pdg; + Service pdg{}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry rQA{"QA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -514,7 +517,7 @@ struct SginclusivePhiKstarSD { //_____________________________________________________________________________ double cosThetaCollinsSoperFrame(const ROOT::Math::PxPyPzMVector& pair1, const ROOT::Math::PxPyPzMVector& pair2, - const ROOT::Math::PxPyPzMVector& fourpion) + const ROOT::Math::PxPyPzMVector& fourPion) { double halfSqrtSnn = 2680.; double massOfLead208 = 193.6823; @@ -525,7 +528,7 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector v1 = ROOT::Math::PxPyPzMVector(pair1.Px(), pair1.Py(), pair1.Pz(), pair1.M()); ROOT::Math::PxPyPzMVector v2 = ROOT::Math::PxPyPzMVector(pair2.Px(), pair2.Py(), pair2.Pz(), pair2.M()); - ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourpion.Px(), fourpion.Py(), fourpion.Pz(), fourpion.M()); + ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourPion.Px(), fourPion.Py(), fourPion.Pz(), fourPion.M()); // Boost to center of mass frame ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -540,7 +543,7 @@ struct SginclusivePhiKstarSD { return cosThetaCs; } - double phiCollinsSoperFrame(const ROOT::Math::PxPyPzMVector& pair1, const ROOT::Math::PxPyPzMVector& pair2, const ROOT::Math::PxPyPzMVector& fourpion) + double phiCollinsSoperFrame(const ROOT::Math::PxPyPzMVector& pair1, const ROOT::Math::PxPyPzMVector& pair2, const ROOT::Math::PxPyPzMVector& fourPion) { // Half of the energy per pair of the colliding nucleons. double halfSqrtSnn = 2680.; @@ -552,7 +555,7 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector v1 = ROOT::Math::PxPyPzMVector(pair1.Px(), pair1.Py(), pair1.Pz(), pair1.M()); ROOT::Math::PxPyPzMVector v2 = ROOT::Math::PxPyPzMVector(pair2.Px(), pair2.Py(), pair2.Pz(), pair2.M()); - ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourpion.Px(), fourpion.Py(), fourpion.Pz(), fourpion.M()); + ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourPion.Px(), fourPion.Py(), fourPion.Pz(), fourPion.M()); // Boost to center of mass frame ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -565,8 +568,8 @@ struct SginclusivePhiKstarSD { ROOT::Math::XYZVectorF yaxisCs{(beam1Cm.Cross(beam2Cm)).Unit()}; ROOT::Math::XYZVectorF xaxisCs{(yaxisCs.Cross(zaxisCs)).Unit()}; - double phi = std::atan2(yaxisCs.Dot(v1Cm), xaxisCs.Dot(v1Cm)); - return phi; + double phiAngle = std::atan2(yaxisCs.Dot(v1Cm), xaxisCs.Dot(v1Cm)); + return phiAngle; } template @@ -589,12 +592,13 @@ struct SginclusivePhiKstarSD { template std::pair selectionEvent(const C& collision, bool fillHist = false) { - if (fillHist) + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 0); + } // Gapside logic int gapSide = collision.gapSide(); - float fitCut[5] = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + std::array fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; int truegapSide = sgSelector.trueGap(collision, fitCut[0], fitCut[1], fitCut[2], zdcCut); if (fillHist) { @@ -604,80 +608,110 @@ struct SginclusivePhiKstarSD { gapSide = truegapSide; - if (gapSide < SingleGapA || gapSide > DoubleGap) + if (gapSide < SingleGapA || gapSide > DoubleGap) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 1); + } - if (upcflag != -1 && collision.flags() != upcflag) + if (upcflag != -1 && collision.flags() != upcflag) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 2); + } - if (std::abs(collision.posZ()) > vzCut) + if (std::abs(collision.posZ()) > vzCut) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 3); + } - if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) + if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 4); + } - if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) + if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 5); + } - if (useTrs && collision.trs() != 1) + if (useTrs && collision.trs() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 6); + } - if (useTrofs && collision.trofs() != 1) + if (useTrofs && collision.trofs() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 7); + } - if (useHmpr && collision.hmpr() != 1) + if (useHmpr && collision.hmpr() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 8); + } - if (useTfb && collision.tfb() != 1) + if (useTfb && collision.tfb() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 9); + } - if (useItsrofb && collision.itsROFb() != 1) + if (useItsrofb && collision.itsROFb() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 10); + } - if (useSbp && collision.sbp() != 1) + if (useSbp && collision.sbp() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 11); + } - if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) + if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 12); + } - if (useVtxItsTpc && collision.vtxITSTPC() != 1) + if (useVtxItsTpc && collision.vtxITSTPC() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 13); + } - if (!isGoodRCTflag(collision)) + if (!isGoodRCTflag(collision)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 14); + } - if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) + if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 15); + } return {true, gapSide}; } @@ -749,8 +783,9 @@ struct SginclusivePhiKstarSD { void process(UDCollisionFull const& collision, UDtracksfull const& tracks) { - if (qa) + if (qa) { rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + } ROOT::Math::PxPyPzMVector v0; ROOT::Math::PxPyPzMVector v1; @@ -771,8 +806,9 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; auto [eventSelected, gapSide] = selectionEvent(collision, true); - if (!eventSelected) + if (!eventSelected) { return; + } int mult = collision.numContrib(); @@ -786,8 +822,9 @@ struct SginclusivePhiKstarSD { registry.fill(HIST("gap_mult2"), mult); } - if (qa) + if (qa) { rQA.fill(HIST("hOcc_after"), collision.occupancyInTime()); + } int mult0 = 0; int mult1 = 0; @@ -804,8 +841,9 @@ struct SginclusivePhiKstarSD { rQA.fill(HIST("hDcaz_all_before"), track1.dcaZ()); } - if (!trackselector(track1, parameters)) + if (trackselector(track1, parameters) == 0) { continue; + } v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassPionCharged); @@ -871,13 +909,15 @@ struct SginclusivePhiKstarSD { } } if (gapSide == SingleGapA) { - if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) + if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) { return; + } registry.fill(HIST("mult_0"), mult0); } if (gapSide == SingleGapC) { - if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) + if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) { return; + } registry.fill(HIST("mult_1"), mult1); } if (qa) { @@ -897,16 +937,18 @@ struct SginclusivePhiKstarSD { } } for (const auto& [t0, t1] : combinations(tracks, tracks)) { - if (!trackselector(t0, parameters) || !trackselector(t1, parameters)) + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { continue; + } if (phi && selectionPIDKaon1(t0) && selectionPIDKaon1(t1)) { // Apply kaon hypothesis and create pairs v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -923,16 +965,18 @@ struct SginclusivePhiKstarSD { // samesignpair if (t0.sign() == t1.sign()) { if (gapSide == SingleGapA) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_KK_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_KK_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (gapSide == SingleGapC) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_KK_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_KK_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { registry.fill(HIST("os_KK_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); @@ -953,8 +997,9 @@ struct SginclusivePhiKstarSD { v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } if (t0.sign() != t1.sign()) { if (gapSide == SingleGapA) { @@ -972,16 +1017,19 @@ struct SginclusivePhiKstarSD { } } for (const auto& [t0, t1] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { - if (!trackselector(t0, parameters) || !trackselector(t1, parameters)) + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { continue; - if (t0.globalIndex() == t1.globalIndex()) + } + if (t0.globalIndex() == t1.globalIndex()) { continue; + } if (rho && selectionPIDProton(t0, useTof, nsigmaTpcCut, nsigmaTofCut) && selectionPIDKaon1(t1)) { v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassProton); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -1008,13 +1056,15 @@ struct SginclusivePhiKstarSD { } } if (kstar && selectionPIDKaon1(t0) && selectionPIDPion1(t1)) { - if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) + if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) { continue; + } v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -1030,16 +1080,18 @@ struct SginclusivePhiKstarSD { } // same sign pair if (t0.sign() == t1.sign()) { if (gapSide == SingleGapA) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_pk_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_pk_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (gapSide == SingleGapC) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_pk_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_pk_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { registry.fill(HIST("os_pk_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); @@ -1059,8 +1111,9 @@ struct SginclusivePhiKstarSD { v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } if (t0.sign() != t1.sign()) { if (gapSide == SingleGapA) { @@ -1107,8 +1160,9 @@ struct SginclusivePhiKstarSD { registry.fill(HIST("costheta_vs_phi1"), costhetaPair1, phiPair1); } if (static_cast(onlyPionTracksp.size()) != numTwoTracks && static_cast(onlyPionTracksn.size()) != numTwoTracks) { - if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) + if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) { return; + } const ROOT::Math::PxPyPzMVector& l1 = onlyPionTrackspm.at(0); const ROOT::Math::PxPyPzMVector& l2 = onlyPionTrackspm.at(1); const ROOT::Math::PxPyPzMVector& l3 = onlyPionTrackspm.at(2); @@ -1143,24 +1197,28 @@ struct SginclusivePhiKstarSD { auto [eventSelected1, gapSide1] = selectionEvent(collision1, false); auto [eventSelected2, gapSide2] = selectionEvent(collision2, false); - if (!eventSelected1 || !eventSelected2) + if (!eventSelected1 || !eventSelected2) { continue; + } - if (gapSide1 != gapSide2) + if (gapSide1 != gapSide2) { continue; + } auto posThisColl = posTracks->sliceByCached(aod::udtrack::udCollisionId, collision1.globalIndex(), cache); auto negThisColl = negTracks->sliceByCached(aod::udtrack::udCollisionId, collision2.globalIndex(), cache); // for (auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { for (const auto& [track1, track2] : o2::soa::combinations(posThisColl, negThisColl)) { - if (!trackselector(track1, parameters) || !trackselector(track2, parameters)) + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { continue; + } if (phi && selectionPIDKaon1(track1) && selectionPIDKaon1(track2)) { v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (track1.sign() != track2.sign()) { if (gapSide1 == SingleGapA) { @@ -1173,16 +1231,19 @@ struct SginclusivePhiKstarSD { } } for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { - if (!trackselector(track1, parameters) || !trackselector(track2, parameters)) + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { continue; - if (track1.globalIndex() == track2.globalIndex()) + } + if (track1.globalIndex() == track2.globalIndex()) { continue; + } if (kstar && selectionPIDKaon1(track1) && selectionPIDPion1(track2)) { v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (track1.sign() != track2.sign()) { if (gapSide1 == SingleGapA) { @@ -1218,14 +1279,16 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector vkstar; ROOT::Math::PxPyPzMVector vphi; for (const auto& mccollision : mccollisions) { - if (mccollision.generatorsID() != generatedId) + if (mccollision.generatorsID() != generatedId) { continue; + } registry.get(HIST("MC/Stat"))->Fill(0., 1.); // get reconstructed collision which belongs to mccollision auto colSlice = collisions.sliceBy(colPerMcCollision, mccollision.globalIndex()); registry.get(HIST("MC/recCols"))->Fill(colSlice.size(), 1.); - if (reconstruction && colSlice.size() < 1) + if (reconstruction && colSlice.size() < 1) { continue; + } // get McParticles which belong to mccollision auto partSlice = McParts.sliceBy(partPerMcCollision, mccollision.globalIndex()); registry.get(HIST("MC/nParts"))->Fill(partSlice.size(), 1.); @@ -1410,23 +1473,27 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector vkstar; ROOT::Math::PxPyPzMVector vphi; - if (!collision.has_udMcCollision()) + if (!collision.has_udMcCollision()) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 1); - if (qaMC) + if (qaMC) { rQA.fill(HIST("hOcc_before_mc"), collision.occupancyInTime()); + } auto mccoll = collision.udMcCollision(); - if (mccoll.generatorsID() != generatedId) + if (mccoll.generatorsID() != generatedId) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 2); - if (upcflag != -1 && collision.flags() != upcflag) + if (upcflag != -1 && collision.flags() != upcflag) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 3); - float fitCut[5] = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + std::array fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; int truegapSide = sgSelector.trueGap(collision, fitCut[0], fitCut[1], fitCut[2], zdcCut); registry.get(HIST("Reco/Stat"))->Fill(4.0, 1.); @@ -1496,60 +1563,74 @@ struct SginclusivePhiKstarSD { } } - if (truegapSide != gapsideMC) + if (truegapSide != gapsideMC) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 4); - if (std::abs(collision.posZ()) > vzCut) + if (std::abs(collision.posZ()) > vzCut) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 5); - if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) + if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 6); - if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) + if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 7); - if (useTrs && collision.trs() != 1) + if (useTrs && collision.trs() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 8); - if (useTrofs && collision.trofs() != 1) + if (useTrofs && collision.trofs() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 9); - if (useHmpr && collision.hmpr() != 1) + if (useHmpr && collision.hmpr() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 10); - if (useTfb && collision.tfb() != 1) + if (useTfb && collision.tfb() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 11); - if (useItsrofb && collision.itsROFb() != 1) + if (useItsrofb && collision.itsROFb() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 12); - if (useSbp && collision.sbp() != 1) + if (useSbp && collision.sbp() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 13); - if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) + if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 14); - if (useVtxItsTpc && collision.vtxITSTPC() != 1) + if (useVtxItsTpc && collision.vtxITSTPC() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 15); - if (!isGoodRCTflag(collision)) + if (!isGoodRCTflag(collision)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 16); - if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) + if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 17); if (qaMC) { @@ -1586,8 +1667,9 @@ struct SginclusivePhiKstarSD { } } for (const auto& tr1 : tracks) { - if (!tr1.has_udMcParticle()) + if (!tr1.has_udMcParticle()) { continue; + } auto mcPart1 = tr1.udMcParticle(); if (qaMC) { @@ -1601,8 +1683,9 @@ struct SginclusivePhiKstarSD { registry.get(HIST("Reco/tr_tpcnclfind_1"))->Fill(tr1.tpcNClsFindable(), 1.); registry.get(HIST("Reco/tr_itsChi2NCl_1"))->Fill(tr1.itsChi2NCl(), 1.); - if (!trackselector(tr1, parameters)) + if (trackselector(tr1, parameters) == 0) { continue; + } registry.get(HIST("Reco/tr_dcaz_2"))->Fill(tr1.dcaZ(), 1.); registry.get(HIST("Reco/tr_dcaxy_2"))->Fill(tr1.dcaXY(), 1.); @@ -1655,29 +1738,36 @@ struct SginclusivePhiKstarSD { vr0.SetCoordinates(tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassKaonCharged); registry.get(HIST("Reco/trpt"))->Fill(vr0.Pt(), 1.); registry.get(HIST("Reco/treta_k"))->Fill(vr0.Eta(), 1.); - if (!selectionPIDKaon1(tr1)) + if (!selectionPIDKaon1(tr1)) { continue; + } registry.get(HIST("Reco/trpt_k"))->Fill(vr0.Pt(), 1.); int t2 = 0; for (const auto& tr2 : tracks) { - if (!tr2.has_udMcParticle()) + if (!tr2.has_udMcParticle()) { continue; - if (!trackselector(tr2, parameters)) + } + if (trackselector(tr2, parameters) == 0) { continue; + } t2++; if (t2 > t1) { - if (!selectionPIDKaon1(tr2)) + if (!selectionPIDKaon1(tr2)) { continue; + } vr1.SetCoordinates(tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassKaonCharged); auto mcPart2 = tr2.udMcParticle(); // if (std::abs(mcPart2.globalIndex() - mcPart1.globalIndex()) != 1) // continue; - if (std::abs(mcPart1.pdgCode()) != kKPlus || std::abs(mcPart2.pdgCode()) != kKPlus) + if (std::abs(mcPart1.pdgCode()) != kKPlus || std::abs(mcPart2.pdgCode()) != kKPlus) { continue; - if (mcPart1.pdgCode() == mcPart2.pdgCode()) + } + if (mcPart1.pdgCode() == mcPart2.pdgCode()) { continue; - if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) + } + if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) { continue; + } bool flag = false; bool flag1 = false; int gIndex1 = 0; @@ -1689,8 +1779,9 @@ struct SginclusivePhiKstarSD { continue; } vphi.SetCoordinates(mother.px(), mother.py(), mother.pz(), o2::constants::physics::MassPhi); - if (std::abs(vphi.Rapidity()) > 0.5) + if (std::abs(vphi.Rapidity()) > 0.5) { continue; + } flag = true; gIndex1 = mother.globalIndex(); } @@ -1698,8 +1789,9 @@ struct SginclusivePhiKstarSD { for (const auto& mother1 : mcPart1.mothers_as()) { if (std::abs(mother1.pdgCode()) == o2::constants::physics::Pdg::kPhi) { vphi.SetCoordinates(mother1.px(), mother1.py(), mother1.pz(), o2::constants::physics::MassPhi); - if (std::abs(vphi.Rapidity()) > 0.5) + if (std::abs(vphi.Rapidity()) > 0.5) { continue; + } flag1 = true; gIndex2 = mother1.globalIndex(); } @@ -1729,23 +1821,28 @@ struct SginclusivePhiKstarSD { } // KStar for (const auto& [tr1, tr2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { - if (!tr1.has_udMcParticle() || !tr2.has_udMcParticle()) + if (!tr1.has_udMcParticle() || !tr2.has_udMcParticle()) { continue; - if (!selectionPIDPion1(tr1) || !selectionPIDKaon1(tr2)) + } + if (!selectionPIDPion1(tr1) || !selectionPIDKaon1(tr2)) { continue; + } // if (tr1.index() == tr2.index()) // continue; // We need to run (0,1), (1,0) pairs as well. but same id pairs are not needed. auto mcPart1 = tr1.udMcParticle(); auto mcPart2 = tr2.udMcParticle(); - if (std::abs(mcPart1.pdgCode()) != kPiPlus || std::abs(mcPart2.pdgCode()) != kKPlus) + if (std::abs(mcPart1.pdgCode()) != kPiPlus || std::abs(mcPart2.pdgCode()) != kKPlus) { continue; - if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) + } + if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) { continue; + } // if (std::abs(mcPart2.globalIndex() - mcPart1.globalIndex()) != 1) // continue; - if (tr1.sign() * tr2.sign() > 0) + if (tr1.sign() * tr2.sign() > 0) { continue; + } vr0.SetCoordinates(tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassPionCharged); vr1.SetCoordinates(tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassKaonCharged); @@ -1753,10 +1850,10 @@ struct SginclusivePhiKstarSD { vr1g.SetCoordinates(mcPart2.px(), mcPart2.py(), mcPart2.pz(), o2::constants::physics::MassKaonCharged); vr01g = vr0g + vr1g; vr01 = vr0 + vr1; - if (!trackselector(tr1, parameters) || !trackselector(tr2, parameters)) { + if ((trackselector(tr1, parameters) == 0) || (trackselector(tr2, parameters) == 0)) { registry.get(HIST("Reco/selM_k"))->Fill(vr01.M(), 1.); } - if (trackselector(tr1, parameters) && trackselector(tr2, parameters)) { + if ((trackselector(tr1, parameters) != 0) && (trackselector(tr2, parameters) != 0)) { bool flag = false; bool flag1 = false; int gIndex1 = 0; @@ -1766,8 +1863,9 @@ struct SginclusivePhiKstarSD { if (std::abs(mother.pdgCode()) == o2::constants::physics::Pdg::kK0Star892) { vkstar.SetCoordinates(mother.px(), mother.py(), mother.pz(), o2::constants::physics::MassK0Star892); // registry.get(HIST("MC/accMPtRap_kstar_T"))->Fill(vkstar.M(), vkstar.Pt(), vkstar.Rapidity(), 1.); - if (std::abs(vkstar.Rapidity()) > 0.5) + if (std::abs(vkstar.Rapidity()) > 0.5) { continue; + } flag = true; gIndex1 = mother.globalIndex(); } @@ -1775,8 +1873,9 @@ struct SginclusivePhiKstarSD { for (const auto& mother1 : mcPart2.mothers_as()) { if (std::abs(mother1.pdgCode()) == o2::constants::physics::Pdg::kK0Star892) { vkstar.SetCoordinates(mother1.px(), mother1.py(), mother1.pz(), o2::constants::physics::MassK0Star892); - if (std::abs(vkstar.Rapidity()) > 0.5) + if (std::abs(vkstar.Rapidity()) > 0.5) { continue; + } flag1 = true; gIndex2 = mother1.globalIndex(); } @@ -1815,13 +1914,503 @@ struct SginclusivePhiKstarSD { auto mcPart = track.udMcParticle(); auto pPart = std::sqrt(mcPart.px() * mcPart.px() + mcPart.py() * mcPart.py() + mcPart.pz() * mcPart.pz()); auto pDiff = pTrack - pPart; - registry.get(HIST("Reco/pDiff"))->Fill(pDiff, track.isPVContributor(), 1.); + registry.get(HIST("Reco/pDiff"))->Fill(pDiff, static_cast(track.isPVContributor()), 1.); } else { registry.get(HIST("Reco/pDiff"))->Fill(-5.9, -1, 1.); } } } PROCESS_SWITCH(SginclusivePhiKstarSD, processReco, "Process reconstructed data", true); + + void processSEMC(CC const& collision, TCs const& tracks, aod::UDMcCollisions const& /*mccollisions*/, aod::UDMcParticles const& /* McParts */) + { + + if (!collision.has_udMcCollision()) { + return; + } + + auto mccoll = collision.udMcCollision(); + if (mccoll.generatorsID() != generatedId) { + return; + } + + if (qa) { + rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + } + + ROOT::Math::PxPyPzMVector v0; + ROOT::Math::PxPyPzMVector v1; + ROOT::Math::PxPyPzMVector v01; + + ROOT::Math::PxPyPzMVector phiv; + ROOT::Math::PxPyPzMVector phiv1; + + std::vector onlyPionTracksp; + std::vector rawPionTracksp; + + std::vector onlyPionTrackspm; + std::vector rawPionTrackspm; + + std::vector onlyPionTracksn; + std::vector rawPionTracksn; + + std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + auto [eventSelected, gapSide] = selectionEvent(collision, true); + if (!eventSelected) { + return; + } + + int mult = collision.numContrib(); + + if (gapSide == SingleGapA) { + registry.fill(HIST("gap_mult0"), mult); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("gap_mult1"), mult); + } + if (gapSide == DoubleGap) { + registry.fill(HIST("gap_mult2"), mult); + } + + if (qa) { + rQA.fill(HIST("hOcc_after"), collision.occupancyInTime()); + } + + int mult0 = 0; + int mult1 = 0; + int mult2 = 0; + if (qa) { + rQA.fill(HIST("hVertexX"), collision.posX()); + rQA.fill(HIST("hVertexY"), collision.posY()); + rQA.fill(HIST("hVertexZ"), collision.posZ()); + } + + for (const auto& track1 : tracks) { + if (qa) { + rQA.fill(HIST("hDcaxy_all_before"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_all_before"), track1.dcaZ()); + } + + if (trackselector(track1, parameters) == 0) { + continue; + } + + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassPionCharged); + + if (qa) { + rQA.fill(HIST("hDcaxy_all_after"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_all_after"), track1.dcaZ()); + rQA.fill(HIST("hEta_all_after"), v0.Eta()); + } + + if (selectionPIDPion1(track1)) { + onlyPionTrackspm.push_back(v0); + rawPionTrackspm.push_back(track1); + if (track1.sign() == 1) { + onlyPionTracksp.push_back(v0); + rawPionTracksp.push_back(track1); + } + if (track1.sign() == -1) { + onlyPionTracksn.push_back(v0); + rawPionTracksn.push_back(track1); + } + } + if (gapSide == SingleGapA) { + mult0++; + } + if (gapSide == SingleGapC) { + mult1++; + } + if (gapSide == DoubleGap) { + mult2++; + } + + if (qa) { + rQA.fill(HIST("tpc_dedx"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta"), v0.P(), track1.beta()); + rQA.fill(HIST("tof_nsigma_kaon_all"), v0.Pt(), track1.tofNSigmaKa()); + rQA.fill(HIST("tof_nsigma_pion_all"), v0.Pt(), track1.tofNSigmaPi()); + rQA.fill(HIST("tpc_nsigma_kaon_all"), v0.Pt(), track1.tpcNSigmaKa()); + rQA.fill(HIST("tpc_nsigma_pion_all"), v0.Pt(), track1.tpcNSigmaPi()); + + if (selectionPIDKaon1(track1)) { + rQA.fill(HIST("tpc_dedx_kaon"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta_kaon"), v0.P(), track1.beta()); + rQA.fill(HIST("tpc_nsigma_kaon"), v0.Pt(), track1.tpcNSigmaKa()); + rQA.fill(HIST("tof_nsigma_kaon"), v0.Pt(), track1.tofNSigmaKa()); + rQA.fill(HIST("tpc_tof_nsigma_kaon"), track1.tpcNSigmaKa(), track1.tofNSigmaKa()); + rQA.fill(HIST("hEta_ka"), v0.Eta()); + rQA.fill(HIST("hRap_ka"), v0.Rapidity()); + rQA.fill(HIST("hDcaxy_ka"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_ka"), track1.dcaZ()); + } + + if (selectionPIDPion1(track1)) { + rQA.fill(HIST("tpc_dedx_pion"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta_pion"), v0.P(), track1.beta()); + rQA.fill(HIST("tpc_nsigma_pion"), v0.Pt(), track1.tpcNSigmaPi()); + rQA.fill(HIST("tof_nsigma_pion"), v0.Pt(), track1.tofNSigmaPi()); + rQA.fill(HIST("tpc_tof_nsigma_pion"), track1.tpcNSigmaPi(), track1.tofNSigmaPi()); + rQA.fill(HIST("hEta_pi"), v0.Eta()); + rQA.fill(HIST("hRap_pi"), v0.Rapidity()); + rQA.fill(HIST("hDcaxy_pi"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_pi"), track1.dcaZ()); + } + } + } + if (gapSide == SingleGapA) { + if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) { + return; + } + registry.fill(HIST("mult_0"), mult0); + } + if (gapSide == SingleGapC) { + if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) { + return; + } + registry.fill(HIST("mult_1"), mult1); + } + if (qa) { + if (gapSide == SingleGapA) { + rQA.fill(HIST("V0A_0"), collision.totalFV0AmplitudeA()); + rQA.fill(HIST("FT0A_0"), collision.totalFT0AmplitudeA()); + rQA.fill(HIST("FT0C_0"), collision.totalFT0AmplitudeC()); + rQA.fill(HIST("ZDC_A_0"), collision.energyCommonZNA()); + rQA.fill(HIST("ZDC_C_0"), collision.energyCommonZNC()); + } + if (gapSide == SingleGapC) { + rQA.fill(HIST("V0A_1"), collision.totalFV0AmplitudeA()); + rQA.fill(HIST("FT0A_1"), collision.totalFT0AmplitudeA()); + rQA.fill(HIST("FT0C_1"), collision.totalFT0AmplitudeC()); + rQA.fill(HIST("ZDC_A_1"), collision.energyCommonZNA()); + rQA.fill(HIST("ZDC_C_1"), collision.energyCommonZNC()); + } + } + for (const auto& [t0, t1] : combinations(tracks, tracks)) { + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { + continue; + } + + if (phi && selectionPIDKaon1(t0) && selectionPIDKaon1(t1)) { + // Apply kaon hypothesis and create pairs + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_KK_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_KK_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + // samesignpair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + if (t0.sign() < 0) { + registry.fill(HIST("os_KK_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_KK_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (gapSide == SingleGapC) { + if (t0.sign() < 0) { + registry.fill(HIST("os_KK_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_KK_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + + if (fillRotation) { + for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { + auto anglestart = confMinRot; + auto angleend = confMaxRot; + auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); + auto rotangle = anglestart + nrotbkg * anglestep; + registry.fill(HIST("hRotation"), rotangle); + + auto rotkaonPx = t0.px() * std::cos(rotangle) - t0.py() * std::sin(rotangle); + auto rotkaonPy = t0.px() * std::sin(rotangle) + t0.py() * std::cos(rotangle); + + v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_KK_rot_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_KK_rot_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_rot_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + for (const auto& [t0, t1] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { + continue; + } + if (t0.globalIndex() == t1.globalIndex()) { + continue; + } + if (rho && selectionPIDProton(t0, useTof, nsigmaTpcCut, nsigmaTofCut) && selectionPIDKaon1(t1)) { + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassProton); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pp_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pp_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pp_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } // same sign pair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pp_ls_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pp_ls_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pp_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + if (kstar && selectionPIDKaon1(t0) && selectionPIDPion1(t1)) { + if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) { + continue; + } + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pk_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pk_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } // same sign pair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + if (t0.sign() < 0) { + registry.fill(HIST("os_pk_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_pk_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (gapSide == SingleGapC) { + if (t0.sign() < 0) { + registry.fill(HIST("os_pk_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_pk_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (fillRotation) { + for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { + auto anglestart = confMinRot; + auto angleend = confMaxRot; + auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); + auto rotangle = anglestart + nrotbkg * anglestep; + registry.fill(HIST("hRotation"), rotangle); + + auto rotkaonPx = t0.px() * std::cos(rotangle) - t0.py() * std::sin(rotangle); + auto rotkaonPy = t0.px() * std::sin(rotangle) + t0.py() * std::cos(rotangle); + + v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pk_rot_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pk_rot_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_rot_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + if (fourpion) { + if (gapSide == DoubleGap && mult2 == numFourTracks) { + ROOT::Math::PxPyPzMVector pair1, pair2, pair3, pair4; + if (static_cast(onlyPionTracksp.size()) == numTwoTracks && static_cast(onlyPionTracksn.size()) == numTwoTracks) { + const ROOT::Math::PxPyPzMVector& k1 = onlyPionTracksp.at(0); + const ROOT::Math::PxPyPzMVector& k2 = onlyPionTracksp.at(1); + const ROOT::Math::PxPyPzMVector& k3 = onlyPionTracksn.at(0); + const ROOT::Math::PxPyPzMVector& k4 = onlyPionTracksn.at(1); + phiv = k1 + k2 + k3 + k4; + pair1 = k1 + k3; + pair2 = k2 + k4; + pair3 = k1 + k4; + pair4 = k2 + k3; + registry.fill(HIST("os_pppp_pT_2"), phiv.M(), phiv.Pt(), phiv.Rapidity()); + registry.fill(HIST("os_pp_vs_pp_mass"), pair1.M(), pair2.M()); + registry.fill(HIST("os_pp_vs_pp_pt"), pair1.Pt(), pair2.Pt()); + auto costhetaPair = cosThetaCollinsSoperFrame(pair1, pair2, phiv); + auto phiPair = 1. * o2::constants::math::PI + phiCollinsSoperFrame(pair1, pair2, phiv); + registry.fill(HIST("phi_dis"), phiPair); + registry.fill(HIST("costheta_dis"), costhetaPair); + registry.fill(HIST("costheta_vs_phi"), costhetaPair, phiPair); + registry.fill(HIST("os_pp_vs_pp_mass1"), pair3.M(), pair4.M()); + registry.fill(HIST("os_pp_vs_pp_pt1"), pair3.Pt(), pair4.Pt()); + auto costhetaPair1 = cosThetaCollinsSoperFrame(pair3, pair4, phiv); + auto phiPair1 = 1. * o2::constants::math::PI + phiCollinsSoperFrame(pair3, pair4, phiv); + registry.fill(HIST("phi_dis1"), phiPair1); + registry.fill(HIST("costheta_dis1"), costhetaPair1); + registry.fill(HIST("costheta_vs_phi1"), costhetaPair1, phiPair1); + } + if (static_cast(onlyPionTracksp.size()) != numTwoTracks && static_cast(onlyPionTracksn.size()) != numTwoTracks) { + if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) { + return; + } + const ROOT::Math::PxPyPzMVector& l1 = onlyPionTrackspm.at(0); + const ROOT::Math::PxPyPzMVector& l2 = onlyPionTrackspm.at(1); + const ROOT::Math::PxPyPzMVector& l3 = onlyPionTrackspm.at(2); + const ROOT::Math::PxPyPzMVector& l4 = onlyPionTrackspm.at(3); + phiv1 = l1 + l2 + l3 + l4; + registry.fill(HIST("os_pppp_pT_2_ls"), phiv1.M(), phiv1.Pt(), phiv1.Rapidity()); + } + } + } + } + PROCESS_SWITCH(SginclusivePhiKstarSD, processSEMC, "Process unlike event in MC", false); + + void mixprocessMC(CCs const& collisions, TCs const& /* tracks */, aod::UDMcCollisions const& /*mccollisions*/, aod::UDMcParticles const& /* McParts */) + { + ROOT::Math::PxPyPzMVector v0; + ROOT::Math::PxPyPzMVector v1; + ROOT::Math::PxPyPzMVector v01; + + std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + + for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { + + if (!collision1.has_udMcCollision() || !collision2.has_udMcCollision()) { + return; + } + + auto mccoll1 = collision1.udMcCollision(); + auto mccoll2 = collision2.udMcCollision(); + if (mccoll1.generatorsID() != generatedId || mccoll2.generatorsID() != generatedId) { + return; + } + + auto [eventSelected1, gapSide1] = selectionEvent(collision1, false); + auto [eventSelected2, gapSide2] = selectionEvent(collision2, false); + if (!eventSelected1 || !eventSelected2) { + continue; + } + + if (gapSide1 != gapSide2) { + continue; + } + + auto posThisColl = posTracks->sliceByCached(aod::udtrack::udCollisionId, collision1.globalIndex(), cache); + auto negThisColl = negTracks->sliceByCached(aod::udtrack::udCollisionId, collision2.globalIndex(), cache); + // for (auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { + for (const auto& [track1, track2] : o2::soa::combinations(posThisColl, negThisColl)) { + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { + continue; + } + if (phi && selectionPIDKaon1(track1) && selectionPIDKaon1(track2)) { + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + // Opposite sign pairs + if (track1.sign() != track2.sign()) { + if (gapSide1 == SingleGapA) { + registry.fill(HIST("os_KK_mix_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide1 == SingleGapC) { + registry.fill(HIST("os_KK_mix_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { + continue; + } + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + if (kstar && selectionPIDKaon1(track1) && selectionPIDPion1(track2)) { + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + // Opposite sign pairs + if (track1.sign() != track2.sign()) { + if (gapSide1 == SingleGapA) { + registry.fill(HIST("os_pk_mix_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide1 == SingleGapC) { + registry.fill(HIST("os_pk_mix_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + PROCESS_SWITCH(SginclusivePhiKstarSD, mixprocessMC, "Process Mixed event in MC", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 4498e22efda2a59846176e7ef1eff56ae1fa6a4e Mon Sep 17 00:00:00 2001 From: Christian Reckziegel <57670185+christianreckziegel@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:39:41 -0300 Subject: [PATCH 030/648] [PWGJE] Charm hadronisation: MC task now cleaner and more robust in terms of event selection monitoring (#17976) Co-authored-by: ALICE Action Bot --- PWGJE/Tasks/hfFragmentationFunction.cxx | 209 ++++++++++++++++++------ 1 file changed, 162 insertions(+), 47 deletions(-) diff --git a/PWGJE/Tasks/hfFragmentationFunction.cxx b/PWGJE/Tasks/hfFragmentationFunction.cxx index 50e1e2b0ef0..054cae8ea42 100644 --- a/PWGJE/Tasks/hfFragmentationFunction.cxx +++ b/PWGJE/Tasks/hfFragmentationFunction.cxx @@ -62,6 +62,25 @@ double deltaPhi(double phi1, double phi2) return std::abs(dphi); } +// +/// Collision counter selection indexes +/// +/// The collision selection is done and stored in multiple steps, for later QA analysis. +/// In order not to hard code which bins should be filled throughout different process +/// function, this namespace with enums is create +namespace collision_selections +{ +enum CollisionSelectionStep { + StepMcCollisions = 0, ///< raw mccollisions with no selection, starts with 0 + StepMcCollisionsZCut, ///< mccollisions with z vtx selection + StepMcCollisionsZCutSel8, ///< mccollisions with z vtx and sel8 mc emulated selections + StepMcCollisionsZCutSel8HasCollisions, ///< mccollisions with z vtx and sel8 mc emulated selections, with at least one reconstructed collisions + StepMcCollisionsZCutSel8NoSplitCollisions, ///< mccollisions with z vtx and sel8 mc emulated selections, with no split reconstructed collisions + StepRecoCollisions, ///< raw reconstructed collisions after previous mccollisions selection + StepRecoCollisionsZcut, ///< reconstructed collisions with z vtx selection after previous mccollisions selection + StepRecoCollisionsZcutSel8 ///< reconstructed collisions with z vtx and sel8 selections after previous mccollisions selection +}; +} // namespace collision_selections // creating table for storing distance data namespace o2::aod { @@ -84,6 +103,8 @@ DECLARE_SOA_COLUMN(HfMlScore1, hfMlScore1, float); DECLARE_SOA_COLUMN(HfMlScore2, hfMlScore2, float); DECLARE_SOA_COLUMN(HfMatchedFrom, hfMatchedFrom, int); DECLARE_SOA_COLUMN(HfSelectedAs, hfSelectedAs, int); +DECLARE_SOA_COLUMN(RecoCollZCut, recoCollZCut, bool); +DECLARE_SOA_COLUMN(RecoCollSel8, recoCollSel8, bool); DECLARE_SOA_COLUMN(McJetHfDist, mcJetHfDist, float); DECLARE_SOA_COLUMN(McJetPt, mcJetPt, float); DECLARE_SOA_COLUMN(McJetEta, mcJetEta, float); @@ -95,6 +116,10 @@ DECLARE_SOA_COLUMN(McHfPhi, mcHfPhi, float); DECLARE_SOA_COLUMN(McHfY, mcHfY, float); DECLARE_SOA_COLUMN(McHfPrompt, mcHfPrompt, bool); DECLARE_SOA_COLUMN(McHfMatch, mcHfMatch, bool); +DECLARE_SOA_COLUMN(McCollZCut, mcCollZCut, bool); +DECLARE_SOA_COLUMN(McCollSel8, mcCollSel8, bool); +DECLARE_SOA_COLUMN(McCollNumCollisions, mcCollNumCollisions, int); +DECLARE_SOA_COLUMN(McCollNumSelectedCollisions, mcCollNumSelectedCollisions, int); } // namespace jet_distance DECLARE_SOA_TABLE(JetDistanceTable, "AOD", "JETDISTTABLE", jet_distance::JetHfDist, @@ -151,6 +176,10 @@ DECLARE_SOA_TABLE(MatchJetDistanceTable, "AOD", "MATCHTABLE", jet_distance::McHfPhi, jet_distance::McHfY, jet_distance::McHfPrompt, + jet_distance::McCollZCut, + jet_distance::McCollSel8, + jet_distance::McCollNumCollisions, + jet_distance::McCollNumSelectedCollisions, jet_distance::JetHfDist, jet_distance::JetPt, jet_distance::JetEta, @@ -166,7 +195,9 @@ DECLARE_SOA_TABLE(MatchJetDistanceTable, "AOD", "MATCHTABLE", jet_distance::HfMlScore1, jet_distance::HfMlScore2, jet_distance::HfMatchedFrom, - jet_distance::HfSelectedAs); + jet_distance::HfSelectedAs, + jet_distance::RecoCollZCut, + jet_distance::RecoCollSel8); } // namespace o2::aod struct HfFragmentationFunction { @@ -194,6 +225,10 @@ struct HfFragmentationFunction { Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable applyMcEventSelection{"applyMcEventSelection", false, "Choose a boolean value"}; + Configurable applyRecoEventSelection{"applyRecoEventSelection", true, "data: apply z-vertex and event selection; MC: reject generated events whose reconstructed collisions all fail them"}; + Configurable rejectMCCollisionNoRecoCollision{"rejectMCCollisionNoRecoCollision", false, "reject generated events with no reconstructed collision"}; + Configurable rejectSplitCollisions{"rejectSplitCollisions", false, "reject generated events associated to more than one reconstructed collision"}; std::vector eventSelectionBits; @@ -203,12 +238,12 @@ struct HfFragmentationFunction { eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); // create histograms - // collision system histograms - std::vector histLabels = {"mccollisions", "z_cut", "collisions", "sel8"}; + // collision counter histograms + std::vector histLabels = {"mccollisions", "mccollisions+z_cut", "mccollisions+z_{cut}+sel8", "mccollisions+z_{cut}+sel8+HasCollisions", "mccollisions+z_{cut}+sel8+NoSplitVtx", "collisions", "collisions+z_{cut}", "collisions+z_{cut}+sel8"}; registry.add("h_collision_counter", ";# of collisions;", HistType::kTH1F, {{static_cast(histLabels.size()), 0.0, static_cast(histLabels.size())}}); - auto counter = registry.get(HIST("h_collision_counter")); + auto collCounter = registry.get(HIST("h_collision_counter")); for (std::vector::size_type iCounter = 0; iCounter < histLabels.size(); iCounter++) { - counter->GetXaxis()->SetBinLabel(iCounter + 1, histLabels[iCounter].data()); + collCounter->GetXaxis()->SetBinLabel(iCounter + 1, histLabels[iCounter].data()); } registry.add("h_jet_counter", ";# of jets;", {HistType::kTH1F, {{6, 0., 3.0}}}); auto jetCounter = registry.get(HIST("h_jet_counter")); @@ -240,11 +275,11 @@ struct HfFragmentationFunction { aod::JetTracks const&) { // apply event selection and fill histograms for sanity check - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (applyRecoEventSelection && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut))) { return; } - registry.fill(HIST("h_collision_counter"), 3.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); for (const auto& jet : jets) { // fill jet counter histogram @@ -315,22 +350,22 @@ struct HfFragmentationFunction { { for (const auto& mccollision : mccollisions) { - registry.fill(HIST("h_collision_counter"), 0.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisions); // skip collisions outside of |z| < vertexZCut - if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut)) { + if (applyMcEventSelection && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut))) { continue; } - registry.fill(HIST("h_collision_counter"), 1.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8); // reconstructed collisions associated to same mccollision const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); for (const auto& collision : collisionsPerMCCollision) { - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (applyRecoEventSelection && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut))) { continue; } - registry.fill(HIST("h_collision_counter"), 3.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); // d0 detector level jets associated to the current same collision const auto d0mcdJetsPerCollision = mcdjets.sliceBy(d0MCDJetsPerCollisionPreslice, collision.globalIndex()); @@ -349,9 +384,9 @@ struct HfFragmentationFunction { int selectedAs = 0; // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits - if (mcdd0cand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as D0 + if ((mcdd0cand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as D0 selectedAs = 1; - } else if (mcdd0cand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as D0bar + } else if ((mcdd0cand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as D0bar selectedAs = -1; } @@ -387,25 +422,66 @@ struct HfFragmentationFunction { } PROCESS_SWITCH(HfFragmentationFunction, processMcEfficiency, "non-matched and matched MC HF and jets", false); - template + template void analyzeMC(TMCPJetsPerMCCollisionPreslice const& MCPJetsPerMCCollisionPreslice, + TMCDJetsPerCollisionPreslice const& MCDJetsPerCollisionPreslice, aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - TJetsMCD const&, TJetsMCP const& mcpjets, - TCandidatesMCD const&, + TJetsMCD const& mcdjets, TCandidatesMCP const&, - aod::JetTracks const&, - aod::JetParticles const&) + TCandidatesMCD const&, + aod::JetParticles const&, + aod::JetTracks const&) { for (const auto& mccollision : mccollisions) { - registry.fill(HIST("h_collision_counter"), 0.0); + + // --- begin event selection + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisions); // skip collisions outside of |z| < vertexZCut - if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut)) { + if (applyMcEventSelection && !(std::abs(mccollision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCut); + if (applyMcEventSelection && !jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8); + + // reconstructed collisions associated to this mccollision + const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + // only consider events with at least one reconstructed collision + if (rejectMCCollisionNoRecoCollision && (collisionsPerMCCollision.size() == 0)) { + continue; + } + // only consider events with no split vertices (one mccollision-to-one collision) + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8HasCollisions); + if (rejectSplitCollisions && collisionsPerMCCollision.size() > 1) { continue; } - registry.fill(HIST("h_collision_counter"), 1.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8NoSplitCollisions); + int numSelectedCollisions = 0; + for (const auto& collision : collisionsPerMCCollision) { + + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (!(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcut); + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); + numSelectedCollisions++; + } // end of collisions loop + + // separate reconstructed collision selection from the case with no existing reconstructed collision per mccollision + if (applyRecoEventSelection && (numSelectedCollisions == 0) && (collisionsPerMCCollision.size() != 0)) { + continue; + } + + // --- begin particle level jets storage // hf particle level jets associated to same mccollision const auto mcpJetsPerMCCollision = mcpjets.sliceBy(MCPJetsPerMCCollisionPreslice, mccollision.globalIndex()); for (const auto& mcpjet : mcpJetsPerMCCollision) { @@ -422,70 +498,109 @@ struct HfFragmentationFunction { for (const auto& mcdjet : mcpjet.template matchedJetCand_as()) { registry.fill(HIST("h_jet_counter"), 2.0); - // apply collision sel8 selection on detector level jet's collision - const auto& collision = collisions.iteratorAt(mcdjet.collisionId()); - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { - continue; - } - registry.fill(HIST("h_collision_counter"), 3.0); - // obtain leading HF candidate in jet auto mcdcand = mcdjet.template candidates_first_as(); int selectedAs = 0; // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits - if (mcdcand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as HF + if ((mcdcand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as HF selectedAs = 1; - } else if (mcdcand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as HFbar + } else if ((mcdcand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as HFbar selectedAs = -1; } + // fetch collision associated to the detector level jet + const auto& collision = mcdjet.template collision_as(); + // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), mcpjet.pt(), mcpjet.eta(), mcpjet.phi(), mcpjet.template tracks_as().size() + mcpjet.template candidates_as().size(), // particle level jet mcpcand.pt(), mcpcand.eta(), mcpcand.phi(), mcpcand.y(), (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections jetutilities::deltaR(mcdjet, mcdcand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.template tracks_as().size() + mcdjet.template candidates_as().size(), // detector level jet mcdcand.pt(), mcdcand.eta(), mcdcand.phi(), mcdcand.m(), mcdcand.y(), (mcdcand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level HF mcdcand.mlScores()[0], mcdcand.mlScores()[1], mcdcand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt - static_cast(mcdcand.flagMcMatchRec()), selectedAs); // HF = +1, HFbar = -1, neither = 0 + static_cast(mcdcand.flagMcMatchRec()), selectedAs, // HF = +1, HFbar = -1, neither = 0 + std::abs(collision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(collision, eventSelectionBits)); // Reconstructed collision selections } } else { // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), mcpjet.pt(), mcpjet.eta(), mcpjet.phi(), mcpjet.template tracks_as().size() + mcpjet.template candidates_as().size(), // particle level jet mcpcand.pt(), mcpcand.eta(), mcpcand.phi(), mcpcand.y(), (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections -2, -2, -2, -2, -2, // no detector-level jet found - -2, -2, -2, -2, -2, -2, // no detector-level jet found + -2, -2, -2, -2, -2, false, // no detector-level jet found -2, -2, -2, // no detector-level jet found - -2, -2); // no detector-level jet found + -2, -2, // no detector-level jet found + false, false); // no detector-level jet found } } // end of mcpjets loop + + // --- begin non-matched detector level jets storage (fake candidates and correlated background if present) + // reconstructed collisions associated to same mccollision + for (const auto& collision : collisionsPerMCCollision) { + + // d0 detector level jets associated to the current same collision + const auto mcdJetsPerCollision = mcdjets.sliceBy(MCDJetsPerCollisionPreslice, collision.globalIndex()); + for (const auto& mcdjet : mcdJetsPerCollision) { + + registry.fill(HIST("h_jet_counter"), 0.5); + + // obtain leading HF candidate in jet + auto mcdcand = mcdjet.template candidates_first_as(); + + if (mcdjet.has_matchedJetCand()) { + registry.fill(HIST("h_jet_counter"), 1.5); + } else { // store the detector level non-matched candidates + + // reflection information for storage: D0 = +1, D0bar = -1, neither = 0 + int selectedAs = 0; + + // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits + if ((mcdcand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as D0 + selectedAs = 1; + } else if ((mcdcand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as D0bar + selectedAs = -1; + } + + // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) + matchJetTable(-2, -2, -2, -2, -2, // particle level jet + -2, -2, -2, -2, false, // particle level HF + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections + jetutilities::deltaR(mcdjet, mcdcand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.template tracks_as().size() + mcdjet.template candidates_as().size(), // detector level jet + mcdcand.pt(), mcdcand.eta(), mcdcand.phi(), mcdcand.m(), mcdcand.y(), (mcdcand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level HF + mcdcand.mlScores()[0], mcdcand.mlScores()[1], mcdcand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt + static_cast(mcdcand.flagMcMatchRec()), selectedAs, // HF = +1, HFbar = -1, neither = 0 + std::abs(collision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(collision, eventSelectionBits)); // Reconstructed collision selections + } + } // end of non-matched detector level jets loop + } // end of collisions loop } // end of mccollisions loop } // end of analyzeMC function void processD0MC(aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - JetD0MCDTable const& mcdjets, JetD0MCPTable const& mcpjets, - aod::CandidatesD0MCD const& mcdcands, + JetD0MCDTable const& mcdjets, aod::CandidatesD0MCP const& mcpcands, - aod::JetTracks const& jettracks, - aod::JetParticles const& jetparticles) + aod::CandidatesD0MCD const& mcdcands, + aod::JetParticles const& jetparticles, + aod::JetTracks const& jettracks) { - analyzeMC, JetD0MCDTable, JetD0MCPTable, aod::CandidatesD0MCD, aod::CandidatesD0MCP>(d0MCPJetsPerMCCollisionPreslice, mccollisions, collisions, mcdjets, mcpjets, mcdcands, mcpcands, jettracks, jetparticles); + analyzeMC, Preslice, JetD0MCPTable, JetD0MCDTable, aod::CandidatesD0MCP, aod::CandidatesD0MCD>(d0MCPJetsPerMCCollisionPreslice, d0MCDJetsPerCollisionPreslice, mccollisions, collisions, mcpjets, mcdjets, mcpcands, mcdcands, jetparticles, jettracks); } PROCESS_SWITCH(HfFragmentationFunction, processD0MC, "Store all simulated D0 jets information with matched candidate (if any found)", false); void processLcMC(aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - JetLcMCDTable const& mcdjets, JetLcMCPTable const& mcpjets, - aod::CandidatesLcMCD const& mcdcands, + JetLcMCDTable const& mcdjets, aod::CandidatesLcMCP const& mcpcands, - aod::JetTracks const& jettracks, - aod::JetParticles const& jetparticles) + aod::CandidatesLcMCD const& mcdcands, + aod::JetParticles const& jetparticles, + aod::JetTracks const& jettracks) { - analyzeMC, JetLcMCDTable, JetLcMCPTable, aod::CandidatesLcMCD, aod::CandidatesLcMCP>(lcMCPJetsPerMCCollisionPreslice, mccollisions, collisions, mcdjets, mcpjets, mcdcands, mcpcands, jettracks, jetparticles); + analyzeMC, Preslice, JetLcMCPTable, JetLcMCDTable, aod::CandidatesLcMCP, aod::CandidatesLcMCD>(lcMCPJetsPerMCCollisionPreslice, lcMCDJetsPerCollisionPreslice, mccollisions, collisions, mcpjets, mcdjets, mcpcands, mcdcands, jetparticles, jettracks); } PROCESS_SWITCH(HfFragmentationFunction, processLcMC, "Store all simulated Lc jets information with matched candidate (if any found)", false); }; From 0d3e70a09c808d753dac5701a5b603102fa4abfe Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Tue, 22 Sep 2026 19:02:31 +0200 Subject: [PATCH 031/648] [PWGLF,PWGMM] Cleanup following reassociation checks (#18011) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 3812 ++++++++++------------------ 1 file changed, 1297 insertions(+), 2515 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 06bc603dbfd..c5c51422118 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -44,16 +44,11 @@ #include #include #include -#include -#include -#include -#include #include #include #include #include -#include #include #include @@ -62,21 +57,14 @@ #include #include #include -#include #include -#include #include -#include #include #include #include #include -#include #include -using SMatrix55 = ROOT::Math::SMatrix>; -using SMatrix5 = ROOT::Math::SVector; - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -89,11 +77,7 @@ using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; auto static constexpr CintZero = 0; -auto static constexpr CintOne = 1; -auto static constexpr Czero = 0.f; -auto static constexpr CInvalid = -999.f; -auto static constexpr Cninety = 90.f; -auto static constexpr ConeHeighty = 180.f; +auto static constexpr CfloatFive = 5.f; auto static constexpr CminAccFT0A = 3.5f; auto static constexpr CmaxAccFT0A = 4.9f; auto static constexpr CminAccFT0C = -3.3f; @@ -130,14 +114,14 @@ enum class TrkSel { nTrkSel }; -enum class TrkTrkBestSel { - trkTrkBestSelAll = 0, - trkTrkBestSelCollID, - trkTrkBestSelOrphan, - trkTrkBestSelDCAxyCut, - trkTrkBestSelDCAzCut, - trkTrkBestSelNumReassoc, - nTrkTrkBestSel +enum class TrkBestSel { + trkBestSelAll = 0, + trkBestSelCollID, + trkBestSelOrphan, + trkBestSelDCAxyCut, + trkBestSelDCAzCut, + trkBestSelNumReassoc, + nTrkBestSel }; enum class AmbTrkType { @@ -151,185 +135,75 @@ enum class AmbTrkType { nAmbTrkType }; -enum AmbTrkTypeAssocFlag { - kSelAll = 0, - kSelGoodVtx, - kSelGoodVtxTrue, - kSelBadVtx, - kSelBadVtxTrue, - kSelNonAmb, - kSelNonAmbGoodVtx, - kSelNonAmbGoodVtxTrue, - kSelNonAmbBadVtx, - kSelNonAmbBadVtxTrue, - kSelNonAmbID, - kSelNonAmbIDGoodVtxTrue, - kSelNonAmbIDBadVtxTrue, - kSelAmbID, - kSelAmbIDGoodVtxTrue, - kSelAmbIDBadVtxTrue, - kSelNonAmbIDExtra, - kSelNonAmbIDExtraGoodVtxTrue, - kSelNonAmbIDExtraBadVtxTrue, - kSelAmb, - kSelAmbGoodVtxTrue, - kSelAmbBadVtxTrue, - kSelOrphanNull, - nSelAmbTrkTypeAssocFlag +enum class GenTrkType { + kGenAll = 0, + kGenRecEvt, + nGenTrkType }; -enum class TrackToCollEvtType { - kAllRecColl = 0, - kIsInelGt0wMft, - kEvtSel, - kBestCollIdx, - kIsMcColl, - nTrackToCollEvtType +enum class GenIdxTrkType { + kGenIdxAll = 0, + kGenIdxDupl, + kGenIdxFake, + nGenIdxTrkType }; -enum class VertexStatusMC { - kNull = 0, - kGood, - kBad +enum class RecTrkType { + kRecAll = 0, + kRecFake, + nRecTrkType }; -enum class AssocCheckVtxType { - kAllVtxTrue = 0, - kAllVtxFalse, - kAllGoodVtx, - kAllGoodVtxTrue, - kAllGoodVtxFalse, - kAllBadVtx, - kAllBadVtxTrue, - kAllBadVtxFalse, - nAssocVtxType +enum class RecIdxTrkType { + kRecIdxPrim = 0, + kRecIdxSec, + kRecIdxDupl, + nRecIdxTrkType }; -enum class ReassocCheckVtxType { - kIsTrueVtxAllTrue = 0, - kIsTrueVtxAllFalse, - kIsTrueVtxVsGoodVtxTrue, - kIsTrueVtxVsGoodVtxFalse, - kIsTrueVtxVsBadVtxTrue, - kIsTrueVtxVsBadVtxFalse, - kIsRecGoodAllTrue, - kIsRecGoodAllFalse, - kIsRecGoodMatchAllTrue, - kIsRecGoodMatchAllFalse, - kIsRecGoodVsGoodVtxTrue, - kIsRecGoodVsGoodVtxFalse, - kIsRecGoodMatchVsGoodVtxTrue, - kIsRecGoodMatchVsGoodVtxFalse, - kIsRecGoodVsBadVtxTrue, - kIsRecGoodVsBadVtxFalse, - kIsRecGoodMatchVsBadVtxTrue, - kIsRecGoodMatchVsBadVtxFalse, - nReassocVtxType +enum class EvtLossType { + kGenAll = 0, + kGenSel, + kGenSplit, + kGenRecEvt, + nEvtLossType }; -enum class ReAssocMCEventStatus { - kEvtReAsAll = 0, - kEvtReAsSelected, - kEvtReAsHasMcColl, - kEvtReAsSplitVtxRemoved, - kEvtReAsZVtxCutMC, - nEvtReAsReAssocMCEventStatus +enum class McEffStatus { + kMcEffAll = 0, + kMcEffSel, + kMcEffHasMcColl, + kMcEffNoSplitVtx, + nMcEfftStatus }; -enum class ReAssocMCTrackStatus { - kTrkReAssocAll = 0, - kTrkBestSel, - kTrkSel, - kTrkHasColl, - kTrkReassignedRemoved, - kTrkHasMcPart, - kTrkIdGt0, - kTrkNonAmb, - kTrkNonAmbGood, - kTrkNonAmbBad, - kTrkNonAmbID, - kTrkNonAmbIDGood, - kTrkNonAmbIDBad, - kTrkAmbID, - kTrkAmbIDGood, - kTrkAmbIDBad, - kTrkNonAmbIDExtra, - kTrkNonAmbIDExtraGood, - kTrkNonAmbIDExtraBad, - kReAssoc, - kReAssocGood, - kReAssocGoodIsCompTrue, - kReAssocGoodIsCompFalse, - kReAssocBad, - kReAssocBadIsCompTrue, - kReAssocBadIsCompFalse, - kAssoc, - kAssocGood, - kAssocGoodIsCompTrue, - kAssocGoodIsCompFalse, - kAssocBad, - kAssocBadIsCompTrue, - kAssocBadIsCompFalse, - nReAssocMCTrackStatusCheck +enum class McStatus { + kMcRecAll = 0, + kMcRecSel, + kMcRecHasMcColl, + kMcRecNoSplitVtx, + kMcGenAll, + nMcStatus }; -enum class HistStatusReAssocVtx { - kTrkRec = 0, - kTrkIdGt0, - kTrkNonAmb, - kTrkNonAmbGood, - kTrkNonAmbBad, - kTrkNonAmbID, - kTrkNonAmbIDGood, - kTrkNonAmbIDBad, - kTrkAmbID, - kTrkAmbIDGood, - kTrkAmbIDBad, - kTrkNonAmbIDExtra, - kTrkNonAmbIDExtraGood, - kTrkNonAmbIDExtraBad, - kReAssoc, - kReAssocGood, - kReAssocGoodIsCompTrue, - kReAssocGoodIsCompFalse, - kReAssocBad, - kReAssocBadIsCompTrue, - kReAssocBadIsCompFalse, - kAssoc, - kAssocGood, - kAssocGoodIsCompTrue, - kAssocGoodIsCompFalse, - kAssocBad, - kAssocBadIsCompTrue, - kAssocBadIsCompFalse, - nHistStatusReAssocVtx +enum class McTrackStatus { + kBestTrkAll = 0, + kBesTrktSel, + kTrkAsBestSel, + kTrkHasColl, + kTrkReassignedRemoved, + nMcTrackStatus }; struct DndetaMFTPbPb { SliceCache cache; - std::array, 4> hCollAssoc; - std::array, 28> hReAssocVtxRes; - std::array, 28> hReAssocDCA; - std::array, 21> hTimeAssocWithReassocMC; - enum OccupancyEst { TrkITS = 1, Ft0C }; - HistogramRegistry registry{ - "registry", - {}, - OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry qaregistry{ - "qaregistry", - {}, - OutputObjHandlingPolicy::AnalysisObject, - false, - true}; - - HistogramRegistry registryAlign{"registryAlign", {}}; - std::array mftQuadrant = {"Q0", "Q1", "Q2", "Q3"}; - std::array, 3>, 4>, 2> hAlignment; + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { Configurable cfgDoIR{"cfgDoIR", false, "Flag to retrieve Interaction rate from CCDB"}; @@ -340,16 +214,11 @@ struct DndetaMFTPbPb { Configurable cfgUseParticleSel{"cfgUseParticleSel", true, "Flag to apply particle selection"}; Configurable cfgUsePrimaries{"cfgUsePrimaries", true, "Select primary particles"}; Configurable cfgUseSecondaries{"cfgUseSecondaries", false, "Select secondary particles"}; - Configurable cfgRemoveTrivialAssoc{"cfgRemoveTrivialAssoc", false, "Skip trivial associations"}; - Configurable cfgRemoveAmbiguousTracks{"cfgRemoveAmbiguousTracks", false, "Remove ambiguous tracks"}; - Configurable cfgRemoveOrphanTracks{"cfgRemoveOrphanTracks", true, "Remove orphan tracks"}; Configurable cfgRemoveReassigned{"cfgRemoveReassigned", false, "Remove reassgined tracks"}; Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; Configurable cfgUseTrackParExtra{"cfgUseTrackParExtra", false, "Use table with refitted track parameters"}; - Configurable cfgUseInelgt0{"cfgUseInelgt0", false, "Use INEL > 0 condition"}; Configurable cfgUseInelgt0wMFT{"cfgUseInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable cfgDCAtype{"cfgDCAtype", 2, "DCA coordinate type [0: DCA-X, 1: DCA-Y, 2: DCA-XY]"}; } gConf; struct : ConfigurableGroup { @@ -392,8 +261,6 @@ struct DndetaMFTPbPb { Configurable maxPhi{"maxPhi", 6.2832, ""}; Configurable minEta{"minEta", -3.6f, ""}; Configurable maxEta{"maxEta", -2.5f, ""}; - Configurable minEtaGenMult{"minEtaGenMult", -0.5, "Min eta for generated mid-rapidity multiplicity"}; - Configurable maxEtaGenMult{"maxEtaGenMult", 0.5, "Max eta for generated mid-rapidity multiplicity"}; Configurable minNclusterMft{"minNclusterMft", 5, "minimum number of MFT clusters"}; Configurable useChi2Cut{"useChi2Cut", true, "use track chi2 cut"}; Configurable maxChi2NCl{"maxChi2NCl", 40.0f, "maximum chi2 per MFT clusters"}; @@ -428,15 +295,14 @@ struct DndetaMFTPbPb { Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; Configurable minIR{"minIR", -1, "minimum IR (kHz) collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR (kHz) collisions"}; + Configurable useInelgt0wTVX{"useInelgt0wTVX", false, "Use INEL > 0 condition with TVX trigger, i.e. FT0A and FT0C acceptance"}; + Configurable useGenMult{"useGenMult", false, "use MultMC for centrality"}; } eventCuts; Service pdg{}; Service ccdb{}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift from CCDB"}; - Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable cfgManualZShift{"cfgManualZShift", 0.0f, "manual z-shift for propagation of global muon to PV"}; int mRunNumber{-1}; uint64_t mSOR{0}; @@ -449,14 +315,10 @@ struct DndetaMFTPbPb { float bZ = 0; // Magnetic field for MFT std::array centerMFT{0, 0, -61.4}; // Field at center of MFT - float mZShift = 0; // z-vertex shift - o2::parameters::GRPMagField* grpmag = nullptr; std::vector ambiguousTrkIds; std::vector reassignedTrkIds; - std::vector ambiguousTrkIdsMC; - std::vector reassignedTrkIdsMC; /// @brief init function, definition of histograms void init(InitContext&) @@ -480,6 +342,11 @@ struct DndetaMFTPbPb { const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; const AxisSpec invQPtAxis{binOpt.invQPtBins, "q/p_{T} (1/GeV)"}; + const AxisSpec genEvtTypeAxis = {static_cast(GenTrkType::nGenTrkType) - 1, +0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; + const AxisSpec recEvtTypeAxis = {static_cast(RecTrkType::nRecTrkType) - 1, +0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; + const AxisSpec genEvtIdxTypeAxis = {static_cast(GenTrkType::nGenTrkType) - 1, +0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; + const AxisSpec recEvtIdxTypeAxis = {static_cast(RecTrkType::nRecTrkType) - 1, +0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; + const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType) - 1, +0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -489,68 +356,38 @@ struct DndetaMFTPbPb { ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); ccdb->setFatalWhenNull(false); - if (static_cast(doprocessDataInclusive) + - static_cast(doprocessDatawBestTracksInclusive) > - 1) { - LOGP(fatal, - "Either processDataInclusive OR " - "processDatawBestTracksInclusive should be enabled!"); - } - if ((static_cast(doprocessDataCentFT0C) + - static_cast(doprocessDatawBestTracksCentFT0C) > - 1) || - (static_cast(doprocessDataCentFT0CVariant1) + - static_cast(doprocessDatawBestTracksCentFT0CVariant1) > - 1) || - (static_cast(doprocessDataCentFT0M) + - static_cast(doprocessDatawBestTracksCentFT0M) > - 1) || - (static_cast(doprocessDataCentNGlobal) + - static_cast(doprocessDatawBestTracksCentNGlobal) > - 1) || - (static_cast(doprocessDataCentMFT) + - static_cast(doprocessDatawBestTracksCentMFT) > - 1)) { - LOGP(fatal, - "Either processDataCent[ESTIMATOR] OR " - "processDatawBestTracksCent[ESTIMATOR] should " - "be enabled!"); - } - if (static_cast(doprocessMCInclusive) + - static_cast(doprocessMCwBestTracksInclusive) > - 1) { - LOGP(fatal, - "Either processMCInclusive OR processMCwBestTracksInclusive " - "should be enabled!"); - } - if ((static_cast(doprocessMCCentFT0C) + - static_cast(doprocessMCwBestTracksCentFT0C) > - 1) || - (static_cast(doprocessMCCentFT0CVariant1) + - static_cast(doprocessMCwBestTracksCentFT0CVariant1) > - 1) || - (static_cast(doprocessMCCentFT0M) + - static_cast(doprocessMCwBestTracksCentFT0M) > - 1) || - (static_cast(doprocessMCCentNGlobal) + - static_cast(doprocessMCwBestTracksCentNGlobal) > - 1) || - (static_cast(doprocessMCCentMFT) + - static_cast(doprocessMCwBestTracksCentMFT) > - 1)) { - LOGP(fatal, - "Either processMCCent[ESTIMATOR] OR " - "processMCwBestTracksCent[ESTIMATOR] should " - "be enabled!"); - } - - // registry.add("Events/hRCTSel", "Event accepted if RCT not selected;RCT Status;", {HistType::kTH1F, {{2, 0.5, 2.5}}}); - // auto hrctSel = registry.get(HIST("Events/hRCTSel")); - // auto* x = hrctSel->GetXaxis(); - // x->SetBinLabel(1, "All"); - // x->SetBinLabel(2, "kFT0Bad"); - - registry.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); + if (static_cast(doprocessDataInclusive) + static_cast(doprocessDatawBestTracksInclusive) > 1) { + LOGP(fatal, "Either processDataInclusive OR processDatawBestTracksInclusive should be enabled!"); + } + if (static_cast(doprocessDataCentFT0C) + static_cast(doprocessDatawBestTracksCentFT0C) > 1) { + LOGP(fatal, "Either processDataCent[ESTIMATOR] OR processDatawBestTracksCent[ESTIMATOR] should be enabled!"); + } + if (static_cast(doprocessMCInclusive) + static_cast(doprocessMCbestInclusive) > 1) { + LOGP(fatal, "Either processMCInclusive OR processMCbestInclusive should be enabled!"); + } + if (static_cast(doprocessMCCentFT0C) + static_cast(doprocessMCbestCentFT0C) > 1) { + LOGP(fatal, "Either processMCCent[ESTIMATOR] OR processMCbestCent[ESTIMATOR] should be enabled!"); + } + if (static_cast(doprocessEfficiencyInclusive) + static_cast(doprocessEfficiencyBestInclusive) > 1) { + LOGP(fatal, "Either doprocessEfficiencyInclusive OR doprocessEfficiencyBestInclusive should be enabled!"); + } + if (static_cast(doprocessEfficiencyCentFT0C) + static_cast(doprocessEfficiencyBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessEfficiencyCentFT0C OR doprocessEfficiencyBestCentFT0C should be enabled!"); + } + if (static_cast(doprocessEfficiencyIdxInlusive) + static_cast(doprocessEfficiencyIdxBestInlusive) > 1) { + LOGP(fatal, "Either doprocessEfficiencyIdxInlusive OR doprocessEfficiencyIdxBestInlusive should be enabled!"); + } + if (static_cast(doprocessEfficiencyIdxCentFT0C) + static_cast(doprocessEfficiencyIdxBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessEfficiencyIdxCentFT0C OR doprocessEfficiencyIdxBestCentFT0C should be enabled!"); + } + + // General counters - QC + auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); + hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); + hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); + hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + + registryQC.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); std::array(EvtSel::nEvtSel)> labelEvtSel{ "All coll.", "Sel 8", @@ -568,30 +405,12 @@ struct DndetaMFTPbPb { "Above max occup.", "RCT Flag Checker", "RCT Flag Checker Extra"}; - registry.get(HIST("Events/hEvtSel"))->SetMinimum(0.1); + registryQC.get(HIST("Events/hEvtSel"))->SetMinimum(0.1); for (int iBin = 0; iBin < static_cast(EvtSel::nEvtSel); iBin++) { - registry.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); - } - - qaregistry.add("Events/hCentBest", "; centrality", HistType::kTH1F, {centralityAxis}); - qaregistry.add("Events/hGenMult", "Sel rec evt. vs generated mult; mult", HistType::kTH1F, {multAxis}); - qaregistry.add("Events/hCentRecVsGenMult", "Rec cent vs gen mid-rapidity multiplicity; centrality; mult", HistType::kTH2F, {centralityAxis, multAxis}); - qaregistry.add("Events/hCentRecFromGenMult", "Centrality from generated mult", HistType::kTH1F, {centralityAxis}); - - registry.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkTrkBestSel::nTrkTrkBestSel), -0.5, +static_cast(TrkTrkBestSel::nTrkTrkBestSel) - 0.5}}}); - std::array(TrkTrkBestSel::nTrkTrkBestSel)> labelTrkTrkBestSel{ - "All", - "Assigned (ID>=0)", - "No orphans", - "DCA xy cut", - "DCA z cut", - "#Reassoc"}; - registry.get(HIST("Tracks/hBestTrkSel"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrkTrkBestSel::nTrkTrkBestSel); iBin++) { - registry.get(HIST("Tracks/hBestTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkTrkBestSel[iBin].data()); + registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); } - registry.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); + registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); std::array(TrkSel::nTrkSel)> labelTrkSel{ "All", "Ncls", @@ -600,693 +419,256 @@ struct DndetaMFTPbPb { "Phi cut", "Pt", "CA"}; - registry.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); + registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { - registry.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); - } - - auto hBcSel = registry.add("hBcSel", "hBcSel", HistType::kTH1F, - {{3, -0.5f, +2.5f}}); - hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); - hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); - hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); - - if (doprocessDataInclusive || doprocessDatawBestTracksInclusive || - doprocessMCInclusive || doprocessMCwBestTracksInclusive) { - registry.add({"Events/Selection", - ";status;occupancy", - {HistType::kTH2F, {{2, 0.5, 2.5}, occupancyAxis}}}); - auto hstat = registry.get(HIST("Events/Selection")); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + } + + if (doprocessDatawBestTracksInclusive || doprocessDatawBestTracksCentFT0C || + doprocessMCbestInclusive || doprocessMCbestCentFT0C || + doprocessEfficiencyBestInclusive || doprocessEfficiencyBestCentFT0C || + doprocessEfficiencyIdxBestInlusive || doprocessEfficiencyIdxBestCentFT0C) { + registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); + std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ + "All", + "Assigned (ID>=0)", + "No orphans", + "DCA xy cut", + "DCA z cut", + "#Reassoc"}; + registryQC.get(HIST("Tracks/hBestTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkBestSel::nTrkBestSel); iBin++) { + registryQC.get(HIST("Tracks/hBestTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkTrkBestSel[iBin].data()); + } + } + if (doprocessDataInclusive || doprocessDatawBestTracksInclusive) { + registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); + registryData.add({"Events/Selection", ";status; occupancy", {HistType::kTH2F, {{2, 0.5, 2.5}, occupancyAxis}}}); + auto hstat = registryData.get(HIST("Events/Selection")); auto* x = hstat->GetXaxis(); x->SetBinLabel(1, "All"); x->SetBinLabel(2, "Selected"); - registry.add("Events/hInteractionRate", "; occupancy; IR (kHz)", kTH2F, {occupancyAxis, irAxis}); - - registry.add({"Events/NtrkZvtx", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtx", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - - qaregistry.add( - {"Tracks/Chi2Eta", - "; #chi^{2}; #it{#eta}; occupancy", - {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Chi2", - "; #chi^{2};", - {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/NclustersEta", - "; nClusters; #eta; occupancy", - {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NchSel", - "; N_{ch}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NchBestSel", - "; N_{ch}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - - qaregistry.add({"Tracks/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Eta", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Phi", "; #varphi; centrality; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); + registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/TanLambda", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); + registryData.add({"Tracks/InvQPt", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); if (doprocessDatawBestTracksInclusive) { - registry.add( - {"Events/NtrkZvtxBest", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/EtaZvtxBest", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEtaBest", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/NclustersEtaBest", - "; nClusters; #eta; occupancy", - {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/DCA3d", - "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", - {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/ReTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/ReTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/OrigTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/OrigTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/RestTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/RestTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/TrackAmbDegree", - "; N_{coll}^{comp}; occupancy", - {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); - qaregistry.add({"Tracks/TanLambdaExtra", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/InvQPtExtra", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/EtaExtra", "; #eta; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/PhiExtra", "; #varphi; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); - } - } - - if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C || - doprocessMCCentFT0C || doprocessMCwBestTracksCentFT0C || - doprocessDataCentFT0CVariant1 || - doprocessDatawBestTracksCentFT0CVariant1 || - doprocessMCCentFT0CVariant1 || doprocessMCwBestTracksCentFT0CVariant1 || - doprocessDataCentFT0M || doprocessDatawBestTracksCentFT0M || - doprocessMCCentFT0M || doprocessMCwBestTracksCentFT0M || - doprocessDataCentNGlobal || doprocessDatawBestTracksCentNGlobal || - doprocessMCCentNGlobal || doprocessMCwBestTracksCentNGlobal || - doprocessDataCentMFT || doprocessDatawBestTracksCentMFT || - doprocessMCCentMFT || doprocessMCwBestTracksCentMFT) { - registry.add({"Events/Centrality/Selection", - ";status;centrality;occupancy", - {HistType::kTHnSparseF, - {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); - auto hstat = registry.get(HIST("Events/Centrality/Selection")); - hstat->GetAxis(0)->SetBinLabel(1, "All"); - hstat->GetAxis(0)->SetBinLabel(2, "Selected"); - - registry.add("Events/Centrality/hInteractionRate", "; centrality; occupancy; IR (kHz)", kTHnSparseF, {centralityAxis, occupancyAxis, irAxis}); - - qaregistry.add({"Events/Centrality/hCent", - "; centrality; occupancy", - {HistType::kTH2F, {centralityAxis, occupancyAxis}}, - true}); - qaregistry.add( - {"Events/Centrality/hZvtxCent", - "; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Events/Centrality/NtrkZvtx", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtx", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEta", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - - qaregistry.add( - {"Tracks/Centrality/NchSel", - "; N_{ch}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NchBestSel", - "; N_{ch}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/Chi2Eta", - "; #chi^{2}; #it{#eta}; centrality; occupancy", - {HistType::kTHnSparseF, - {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Chi2", - "; #chi^{2}; centrality; occupancy", - {HistType::kTHnSparseF, - {chiSqAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/NclustersEta", - "; nClusters; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - - qaregistry.add({"Tracks/Centrality/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Eta", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Phi", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); - - if (doprocessDatawBestTracksCentFT0C || - doprocessDatawBestTracksCentFT0CVariant1 || - doprocessDatawBestTracksCentFT0M || - doprocessDatawBestTracksCentNGlobal || - doprocessDatawBestTracksCentMFT) { - registry.add({"Events/Centrality/NtrkZvtxBest", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxBest", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEtaBest", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NclustersEtaBest", - "; nClusters; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/TrackAmbDegree", - "; N_{coll}^{comp}; centrality; occupancy", - {HistType::kTHnSparseF, - {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/TanLambdaExtra", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/InvQPtExtra", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/EtaExtra", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/DCA3d", - "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/ReTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/ReTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/OrigTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/OrigTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/RestTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/RestTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - } - } - - if (doprocessMCInclusive || doprocessMCwBestTracksInclusive) { - registry.add({"Events/EvtEffGen", - ";status;occupancy", - {HistType::kTH2F, {{3, 0.5, 3.5}, occupancyAxis}}}); - auto heff = registry.get(HIST("Events/EvtEffGen")); - auto* h = heff->GetXaxis(); - h->SetBinLabel(1, "All reconstructed"); - h->SetBinLabel(2, "Selected reconstructed"); - h->SetBinLabel(3, "All generated"); - - registry.add({"Events/NtrkZvtxGen_t", - "; N_{trk}; Z_{vtx} (cm);", - {HistType::kTH2F, {multAxis, zAxis}}}); - registry.add({"Events/NtrkZvtxGen", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtxGen", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEtaGen", - "; #varphi; #eta;", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtxGen_t", - "; #eta; Z_{vtx} (cm);", - {HistType::kTH2F, {etaAxis, zAxis}}}); - registry.add({"Tracks/PhiEtaGen_t", - "; #varphi; #eta;", - {HistType::kTH2F, {phiAxis, etaAxis}}}); - qaregistry.add({"Events/NotFoundEventZvtx", - "; #it{z}_{vtx} (cm)", - {HistType::kTH1F, {zAxis}}}); - qaregistry.add({"Events/ZvtxDiff", - "; Z_{rec} - Z_{gen} (cm)", - {HistType::kTH1F, {deltaZAxis}}}); - qaregistry.add({"Events/SplitMult", - "; N_{gen}; #it{z}_{vtx} (cm)", - {HistType::kTH2F, {multAxis, zAxis}}}); - } - - if (doprocessMCCentFT0C || doprocessMCwBestTracksCentFT0C || - doprocessMCCentFT0CVariant1 || doprocessMCwBestTracksCentFT0CVariant1 || - doprocessMCCentFT0M || doprocessMCwBestTracksCentFT0M || - doprocessMCCentNGlobal || doprocessMCwBestTracksCentNGlobal || - doprocessMCCentMFT || doprocessMCwBestTracksCentMFT) { - registry.add({"Events/Centrality/EvtEffGen", - ";status;centrality;occupancy", - {HistType::kTHnSparseF, - {{3, 0.5, 3.5}, centralityAxis, occupancyAxis}}}); - auto heff = registry.get(HIST("Events/Centrality/EvtEffGen")); - heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); - heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); - heff->GetAxis(0)->SetBinLabel(3, "All generated"); - - registry.add( - {"Events/Centrality/NtrkZvtxGen_t", - "; N_{trk}; Z_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis}}}); - registry.add({"Events/Centrality/NtrkZvtxGen", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Events/Centrality/hRecCent", - "; centrality; occupancy", - {HistType::kTH2F, {centralityAxis, occupancyAxis}}}); - registry.add( - {"Events/Centrality/hRecZvtxCent", - "; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxGen", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEtaGen", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxGen_t", - "; #eta; Z_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis}}}); - registry.add( - {"Tracks/Centrality/PhiEtaGen_t", - "; #varphi; #eta; centrality", - {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis}}}); - qaregistry.add({"Events/Centrality/NotFoundEventZvtx", - "; #it{z}_{vtx} (cm); centrality", - {HistType::kTH2F, {zAxis, centralityAxis}}}); - qaregistry.add({"Events/Centrality/ZvtxDiff", - "; Z_{rec} - Z_{gen} (cm); centrality", - {HistType::kTH2F, {deltaZAxis, centralityAxis}}}); - qaregistry.add( - {"Events/Centrality/SplitMult", - "; N_{gen}; #it{z}_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis}}}); - } - - if (doprocessTrkEffIdxBestInlusive) { - qaregistry.add({"Tracks/hPtEtaEffGenFakeBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffPrimBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffSecBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenDuplBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffDuplBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/NmftTrkPerPartBest", - "; #it{N}_{mft tracks per particle}", - {HistType::kTH1F, {multAxis}}}); - } - - if (doprocessTrkEffIdxBestCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenFakeBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffPrimBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffSecBest", - "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenDuplBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffDuplBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NmftTrkPerPartBest", - "; #it{N}_{mft tracks per particle}; centrality", - {HistType::kTHnSparseF, {multAxis, centralityAxis}}}); - } - - if (doprocessTrkEffIdxInlusive) { - qaregistry.add({"Tracks/hPtEtaEffGen", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffPrim", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffSec", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenDupl", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffDupl", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NmftTrkPerPart", - "; #it{N}_{mft tracks per particle}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffIdxCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGen", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffPrim", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffSec", - "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenDupl", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffDupl", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NmftTrkPerPart", - "; #it{N}_{mft tracks per particle}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffBestInclusive) { - qaregistry.add({"Tracks/hPtPhiEtaZvtxEffBestGen", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtPhiEtaZvtxEffBestRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEffBestFakeRec", - " ; p_{T} (GeV/c); occupancy", - {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffBestCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtPhiEtaZvtxEffBestGen", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtPhiEtaZvtxEffBestRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEffBestFakeRec", - "; p_{T} (GeV/c); centrality; occupancy", - {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessMcQACentFT0C) { - qaregistry.add( - {"Events/Centrality/hRecPerGenColls", - "; #it{N}_{reco collisions} / #it{N}_{gen collisions}; centrality", - {HistType::kTHnSparseF, - {{200, 0., 2.}, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/hNmftTrks", - "; #it{N}_{mft tracks}; centrality", - {HistType::kTHnSparseF, - {{200, -0.5, 200.}, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hFracAmbiguousMftTrks", - "; #it{N}_{ambiguous tracks} / #it{N}_{tracks}; centrality", - {HistType::kTHnSparseF, - {{100, 0., 1.}, centralityAxis, occupancyAxis}}}); - } - - if (doprocessCheckAmbiguousMftTracks) { - qaregistry.add({"Tracks/hMftTracksAmbDegree", - " ; N_{coll}^{comp}", - {HistType::kTH1F, {{41, -0.5, 40.5}}}}); - qaregistry.add({"Tracks/hMftTracksAmbDegreeWithTrivial", - " ; N_{coll}^{comp}", - {HistType::kTH1F, {{41, -0.5, 40.5}}}}); - - qaregistry.add("Tracks/hAmbTrackType", "hAmbTrackType", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); - std::array(AmbTrkType::nAmbTrkType)> labelAmbiguity{ - "all", - "orphan", - "orphanNull", - "nonAmb", - "nonAmbSame", - "Amb", - "AmbGt1"}; - qaregistry.get(HIST("Tracks/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - qaregistry.get(HIST("Tracks/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - } - - if (doprocessEfficiencyInclusive) { - qaregistry.add({"Tracks/hEffRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hEffFake", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - } - - if (doprocessEfficiencyCentFT0C) { - qaregistry.add({"Tracks/Centrality/hEffRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/hEffFake", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessCorrelationwBestTracksInclusive) { - qaregistry.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); - qaregistry.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); - qaregistry.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); - qaregistry.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); - qaregistry.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); - } - - if (doprocessAlignmentInclusive) { - for (size_t j = 0; j < mftQuadrant.size(); j++) { - const auto& quadrant = mftQuadrant[j]; - std::string hPath = std::string("Alignment/") + quadrant + "/"; - hAlignment[0][j][0]["DCA_x_vs_z"] = registryAlign.add((hPath + "DCA_x_vs_z").c_str(), std::format("DCA(x) vs. z - {}", quadrant).c_str(), {HistType::kTH2F, {zAxis, dcaxyAxis}}); - hAlignment[0][j][0]["DCA_y_vs_z"] = registryAlign.add((hPath + "DCA_y_vs_z").c_str(), std::format("DCA(y) vs. z - {}", quadrant).c_str(), {HistType::kTH2F, {zAxis, dcaxyAxis}}); - } - } - - if (doprocessEventAndSignalLossCentFT0C) { - auto hNevt = registry.add("Events/Centrality/hNRecCollsSigEvtLoss", "Number of reconstructed MC collisions", HistType::kTH1F, {{1, 0.5, 1.5}}); - hNevt->GetXaxis()->SetBinLabel(1, "Reconstructed collisions"); - - registry.add("Events/Centrality/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); - auto hstat = registry.get(HIST("Events/Centrality/EvtSigLossStatus")); + registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/OrigTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/RestTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + registryData.add({"Tracks/TanLambdaExtra", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); + registryData.add({"Tracks/InvQPtExtra", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); + registryData.add({"Tracks/EtaExtra", "; #eta; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/PhiExtra", "; #varphi; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); + } + } + if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { + registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Events/Centrality/Selection", ";status; centrality; occupancy", {HistType::kTHnSparseF, {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); + auto hstat = registryData.get(HIST("Events/Centrality/Selection")); + hstat->GetXaxis()->SetBinLabel(1, "All"); + hstat->GetXaxis()->SetBinLabel(2, "Selected"); + + registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; #eta; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + + if (doprocessDatawBestTracksCentFT0C) { + registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/OrigTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/RestTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TanLambdaExtra", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/InvQPtExtra", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/EtaExtra", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); + } + } + if (doprocessMCInclusive || doprocessMCbestInclusive) { + registryMC.add("Events/McStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, occupancyAxis}}); + std::array(McStatus::nMcStatus)> labelMcStatus{ + "Rec all", + "Rec sel", + "Rec w/ mc coll", + "Rec w/o split vtx", + "Gen all"}; + for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { + registryMC.get(HIST("Events/McStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcStatus[iBin].data()); + } + if (doprocessMCbestInclusive) { + registryMC.add({"Tracks/hMcTrackStatus", "Number of tracks; Cut; occupancy", {HistType::kTH2F, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, occupancyAxis}}}); + std::array(McTrackStatus::nMcTrackStatus)> labelMcTrackStatus{ + "Best all", + "Best sel", + "Trk sel", + "Has coll", + "Reas rm"}; + for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { + registryMC.get(HIST("Tracks/hMcTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcTrackStatus[iBin].data()); + } + registryMC.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); + registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + } + if (doprocessMCCentFT0C || doprocessMCbestCentFT0C) { + registryMC.add("Events/Centrality/McStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McStatus::nMcStatus)> labelMcStatusCent{ + "Rec all", + "Rec sel", + "Rec w/ mc coll", + "Rec w/o split vtx", + "Gen all"}; + for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/McStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCent[iBin].data()); + } + if (doprocessMCbestCentFT0C) { + registryMC.add({"Tracks/Centrality/hMcTrackStatus", "Number of tracks; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, centralityAxis, occupancyAxis}}}); + std::array(McTrackStatus::nMcTrackStatus)> labelMcStatusCentBest{ + "Best all", + "Best sel", + "Trk sel", + "Has coll", + "Reas rm"}; + for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { + registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCentBest[iBin].data()); + } + registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen_t", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessEfficiencyInclusive || doprocessEfficiencyBestInclusive) { + registryMC.add("Events/hMcEffStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, occupancyAxis}}); + std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatus{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed"}; + for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { + registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); + } + registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); + } + if (doprocessEfficiencyCentFT0C || doprocessEfficiencyBestCentFT0C) { + registryMC.add("Events/Centrality/hMcEffStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatusCent{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed"}; + for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); + } + registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessEfficiencyIdxInlusive || doprocessEfficiencyIdxBestInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {multAxis, occupancyAxis}}}); + } + if (doprocessEfficiencyIdxCentFT0C || doprocessEfficiencyIdxBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTH2F, {multAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessMcSgnEvtLossCentFT0C) { + registryMC.add("Events/hEvtMcGen", "Events/hEvtMcGen", {HistType::kTH1F, {{4, 0.f, 4.f}}}); + registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(1, "all"); + registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(2, "z-vtx"); + registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(3, "isInelGt0wMft"); + registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(4, "TVX"); + // + registryMC.add("Events/EvtSigLossStatus", ";status;centrality", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + auto hstat = registryMC.get(HIST("Events/EvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); - - registry.add("Events/Centrality/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}); - registry.add("Events/Centrality/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRec", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRecNParticlesEta05", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add({"Tracks/Centrality/EtaCentVsMultGen_t", "; #eta; centrality; mult gen", {HistType::kTHnSparseF, {etaAxis, centralityAxis, multFT0cAxis}}}); - registry.add({"Tracks/Centrality/EtaCentVsMultGen", "; #eta; centrality; mult gen", {HistType::kTHnSparseF, {etaAxis, centralityAxis, multFT0cAxis}}}); - registry.add({"Tracks/Centrality/EtaGen_t", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/EtaGen", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, centralityAxis}}}); - } - - if (doprocessDCAReassocMc) { - registry.add({"Events/Centrality/EvtGenRecReassoc", ";status;centrality", {HistType::kTHnSparseF, {{4, 0.5, 4.5}, centralityAxis}}}); - auto heff = registry.get(HIST("Events/Centrality/EvtGenRecReassoc")); - heff->GetAxis(0)->SetBinLabel(1, "All generated"); - heff->GetAxis(0)->SetBinLabel(2, "All reconstructed"); - heff->GetAxis(0)->SetBinLabel(3, "Selected reconstructed"); - heff->GetAxis(0)->SetBinLabel(4, "Remove split vertices"); - - registry.add({"Tracks/Centrality/THnDCAxyBestRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestRecFake", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecWeak", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecMat", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + // + registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multFT0cAxis, evtLossTypeAxis}}}); + registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); + registryMC.add({"Events/hNchTVX", "; Nch; status", {HistType::kTH2F, {{2, 0, 2}, multAxis}}}); + registryMC.add({"Events/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); + registryMC.add({"Events/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCentRec", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecNParticlesEta05", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Tracks/hEtaVsNchGen", "; #eta; mult gen", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); + registryMC.add({"Tracks/hEtaVsNchGenRecEvt", "; #eta; mult gen w/ Rec evt", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); + } + if (doprocessCorrelationwBestTracksInclusive) { + registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); + registryData.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); + registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); + registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); + registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); } } - /// Filters - tracks - Filter filtTrkEta = (aod::fwdtrack::eta < trackCuts.maxEta) && - (aod::fwdtrack::eta > trackCuts.minEta); - Filter filtATrackID = (aod::fwdtrack::bestCollisionId >= CintZero); - Filter filtATrackDCAxy = (nabs(aod::fwdtrack::bestDCAXY) < trackCuts.maxDCAxy); - Filter filtATrackDCAz = (nabs(aod::fwdtrack::bestDCAZ) < trackCuts.maxDCAz); - + /// Filters - particles + Filter primaries = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > CfloatFive, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)); /// Joined tables using FullBCs = soa::Join; using CollBCs = soa::Join; using ExtBCs = soa::Join; - /// Collisions using Colls = soa::Join; using CollsCentFT0C = soa::Join; - using CollsCentFT0CVariant1 = - soa::Join; - using CollsCentFT0M = soa::Join; - using CollsCentNGlobal = - soa::Join; - using CollsCentMFT = soa::Join; - using CollsGenCentFT0C = soa::Join; - using CollsGenCentFT0CExtra = soa::Join; + using CollsGenCentFT0C = soa::Join; using CollsCorr = soa::Join; - using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; - /// Tracks - using MFTTracksLabeled = soa::Join; - using MftTracksWColls = soa::Join; - using BestTracksMC = soa::Join; - - /// Filtered tables - using FiltMftTracks = soa::Filtered; - using FiltBestTracks = soa::Filtered; - - Preslice perMCCol = aod::mcparticle::mcCollisionId; - PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; + using MftTracksLabeled = soa::Join; + using MftBestTracksLabeled = soa::Join; + /// Particles + using ParticlesIdx = soa::Join; + using FiltParticlesIdx = soa::Filtered; template float getDCAz(const T& track) @@ -1298,135 +680,21 @@ struct DndetaMFTPbPb { } } - /// \brief RMS calculation - /// \param vec vector of values to compute RMS - template - T computeRMS(std::vector& vec) - { - T sum = std::accumulate(vec.begin(), vec.end(), 0.0); - T mean = sum / vec.size(); - - std::vector diff(vec.size()); - std::transform(vec.begin(), vec.end(), diff.begin(), [mean](T x) { return x - mean; }); - T sqSum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); - T stdev = std::sqrt(sqSum / vec.size()); - - return stdev; - } - - std::unordered_set mcIds; - std::unordered_set setRecCollSel; - std::unordered_map refCentByMcId; - std::unordered_map genCentByMcId; - std::unordered_map mapMcCollIdPerRecColl; - std::unordered_map mapRecToMc; - std::unordered_map mapMcToRec; - - /// \brief check good rec vertex of corresponding mc coll is available in compatible rec coll - template - bool isRecInCompColl(AT const& atrack) - { - auto const& ids = atrack.compatibleCollIds(); - bool isInCoColl = false; - if (atrack.ambDegree() != 0) { - const int mcCollId = atrack.mcParticle().mcCollisionId(); - if (!ids.empty()) { - for (auto const& id : ids) { - auto itMcCollId = mapMcCollIdPerRecColl.find(id); - if (itMcCollId != mapMcCollIdPerRecColl.end()) { - if (itMcCollId->second == mcCollId) { - isInCoColl = true; - return isInCoColl; - } - } else { - return isInCoColl; - } - } - } - } - return isInCoColl; - } - - template - void createMCIds(MC const& mcCollisions, CollsGenCentFT0CExtra const& collisions, P const& particles) + std::unordered_map mapMcCollIdPerRecColl; + template + void buildLookupTable(C const& collisions) { - const auto& nMcColls = mcCollisions.size(); - LOG(info) << "MC collisions: " << nMcColls; const auto& nRecoColls = collisions.size(); - LOG(info) << "Reconstructed collisions: " << nRecoColls; - - mcIds.clear(); - mcIds.reserve(nMcColls); - setRecCollSel.clear(); - setRecCollSel.reserve(nRecoColls); - refCentByMcId.clear(); - refCentByMcId.reserve(nMcColls); - genCentByMcId.clear(); - genCentByMcId.reserve(nMcColls); mapMcCollIdPerRecColl.clear(); mapMcCollIdPerRecColl.reserve(nRecoColls); - mapRecToMc.clear(); - mapRecToMc.reserve(nRecoColls); - mapMcToRec.clear(); - mapMcToRec.reserve(nRecoColls); - - for (const auto& mcCollision : mcCollisions) { - const auto mcId = mcCollision.globalIndex(); - - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { - continue; - } - - bool atLeastOne = false; - int maxNcontributors = -1; - float bestCollCent = CInvalid; - - auto groupedColls = collisions.sliceBy(perCollMcRec, mcId); - for (auto const& collision : groupedColls) { - const float lCent = getRecoCent(collision); - if (!isGoodEvent(collision)) { - continue; - } - if (gConf.cfgUseInelgt0 && !collision.isInelGt0()) { - continue; - } - atLeastOne = true; - - // const int nContrib = collision.multPVTotalContributors(); - const int nContrib = collision.numContrib(); - if (maxNcontributors < nContrib) { - maxNcontributors = nContrib; - bestCollCent = lCent; - mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); - mapRecToMc.emplace(collision.globalIndex(), collision.mcCollisionId()); - mapMcToRec.emplace(collision.mcCollisionId(), collision.globalIndex()); - setRecCollSel.insert(collision.globalIndex()); - } - } - - if (!atLeastOne || bestCollCent == CInvalid) { - continue; - } - - auto partsPerMcColl = particles.sliceBy(perMCCol, mcId); - const int genMult = countPartMidRap(partsPerMcColl); - const float genCentClass = getCentGenMult(genMult); - if (genCentClass == CInvalid) { - continue; - } - if (genCentClass < eventCuts.minCentrality || genCentClass > eventCuts.maxCentrality) { - continue; + int maxNcontributors = -1; + for (auto const& collision : collisions) { + // const int nContrib = collision.multPVTotalContributors(); + const int nContrib = collision.numContrib(); + if (maxNcontributors < nContrib) { + maxNcontributors = nContrib; } - - qaregistry.fill(HIST("Events/hCentBest"), bestCollCent); - - mcIds.insert(mcId); - refCentByMcId.emplace(mcId, genCentClass); - genCentByMcId[mcId] = genMult; - - qaregistry.fill(HIST("Events/hGenMult"), genMult); - qaregistry.fill(HIST("Events/hCentRecFromGenMult"), genCentClass); - qaregistry.fill(HIST("Events/hCentRecVsGenMult"), bestCollCent, genMult); + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); } } @@ -1446,45 +714,31 @@ struct DndetaMFTPbPb { auto field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); bZ = field->getBz(centerMFT.data()); LOG(info) << "The field at the center of the MFT is bZ = " << bZ; - - if (cfgApplyZShiftFromCCDB) { - auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); - if (zShift != nullptr && !zShift->empty()) { - LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); - mZShift = (*zShift)[0]; - } else { - LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); - mZShift = 0; - } - } else { - LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); - mZShift = cfgManualZShift; - } } template bool isBestTrackSelected(const B& besttrack) { if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelAll)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelAll)); } if (besttrack.bestCollisionId() < CintZero) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelCollID)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelCollID)); } if (besttrack.ambDegree() == CintZero) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelOrphan)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelOrphan)); } if (std::abs(besttrack.bestDCAXY()) >= trackCuts.maxDCAxy) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAxyCut)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelDCAxyCut)); } if (trackCuts.useDCAzCut) { const float bestDcaZ = getDCAz(besttrack); @@ -1493,7 +747,7 @@ struct DndetaMFTPbPb { } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAzCut)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelDCAzCut)); } return true; } @@ -1502,13 +756,13 @@ struct DndetaMFTPbPb { bool isTrackSelected(const T& track) { if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelAll)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelAll)); } if (track.nClusters() < trackCuts.minNclusterMft) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelNCls)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelNCls)); } if (trackCuts.useChi2Cut) { float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); @@ -1518,20 +772,16 @@ struct DndetaMFTPbPb { } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelChi2Ncl)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelChi2Ncl)); } if (track.eta() < trackCuts.minEta || track.eta() > trackCuts.maxEta) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); } if (trackCuts.usephiCut) { float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { - return false; - } if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -1541,19 +791,19 @@ struct DndetaMFTPbPb { } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhiCut)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhiCut)); } if (trackCuts.usePtCut && track.pt() < trackCuts.minPt) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPt)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPt)); } if (trackCuts.requireCA && !track.isCA()) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelCA)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelCA)); } return true; } @@ -1565,49 +815,31 @@ struct DndetaMFTPbPb { for (auto const& track : tracks) { if (fillHis) { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Chi2"), track.chi2(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/Chi2Eta"), track.chi2(), track.eta(), occ); - qaregistry.fill(HIST("Tracks/Chi2"), track.chi2(), occ); - qaregistry.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); + registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), track.eta(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); } } if (!isTrackSelected(track)) { continue; } if (fillHis) { - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Eta"), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Phi"), phi, c, occ); + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); + registryData.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - registry.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); - qaregistry.fill(HIST("Tracks/TanLambda"), track.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Eta"), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Phi"), phi, c, occ); + registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occ); + registryData.fill(HIST("Tracks/TanLambda"), track.tgl(), occ); + registryData.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), occ); } } ++nTrk; } - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NchSel"), nTrk, c, occ); - } else { - qaregistry.fill(HIST("Tracks/NchSel"), nTrk, occ); - } - } return nTrk; } @@ -1616,24 +848,19 @@ struct DndetaMFTPbPb { { for (auto const& etrack : besttracksExtra) { if (fillHis) { - float phi = etrack.phis(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (has_reco_cent) { if (gConf.cfgUseTrackParExtra) { - qaregistry.fill(HIST("Tracks/Centrality/TanLambdaExtra"), etrack.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/InvQPtExtra"), etrack.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/EtaExtra"), etrack.etas(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/PhiExtra"), phi, c, occ); + registryData.fill(HIST("Tracks/Centrality/TanLambdaExtra"), etrack.tgl(), c, occ); + registryData.fill(HIST("Tracks/Centrality/InvQPtExtra"), etrack.signed1Pt(), c, occ); + registryData.fill(HIST("Tracks/Centrality/EtaExtra"), etrack.etas(), c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiExtra"), etrack.phis(), c, occ); } } else { if (gConf.cfgUseTrackParExtra) { - qaregistry.fill(HIST("Tracks/TanLambdaExtra"), etrack.tgl(), occ); - qaregistry.fill(HIST("Tracks/InvQPtExtra"), etrack.signed1Pt(), occ); - qaregistry.fill(HIST("Tracks/EtaExtra"), etrack.etas(), occ); - qaregistry.fill(HIST("Tracks/PhiExtra"), phi, occ); + registryData.fill(HIST("Tracks/TanLambdaExtra"), etrack.tgl(), occ); + registryData.fill(HIST("Tracks/InvQPtExtra"), etrack.signed1Pt(), occ); + registryData.fill(HIST("Tracks/EtaExtra"), etrack.etas(), occ); + registryData.fill(HIST("Tracks/PhiExtra"), etrack.phis(), occ); } } } @@ -1641,8 +868,7 @@ struct DndetaMFTPbPb { } template - int countBestTracks(T const& tracks, B const& besttracks, float z, - float c, float occ) + int countBestTracks(T const& tracks, B const& besttracks, float z, float c, float occ) { auto nATrk = 0; ambiguousTrkIds.reserve(besttracks.size()); @@ -1653,47 +879,45 @@ struct DndetaMFTPbPb { } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), itrack.eta(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occ); + } if (!isTrackSelected(itrack)) { continue; } ambiguousTrkIds.emplace_back(atrack.mfttrackId()); ++nATrk; if (fillHis) { - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxBest"), itrack.eta(), z, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEtaBest"), phi, itrack.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); - qaregistry.fill(HIST("Tracks/Centrality/NclustersEtaBest"), itrack.nClusters(), itrack.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/TanLambda"), itrack.tgl(), c, occ); + registryData.fill(HIST("Tracks/Centrality/InvQPt"), itrack.signed1Pt(), c, occ); + registryData.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); + registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtxBest"), itrack.eta(), z, occ); - registry.fill(HIST("Tracks/PhiEtaBest"), phi, itrack.eta(), occ); - qaregistry.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); - qaregistry.fill(HIST("Tracks/NclustersEtaBest"), itrack.nClusters(), itrack.eta(), occ); - qaregistry.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); + registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occ); + registryData.fill(HIST("Tracks/TanLambda"), itrack.tgl(), occ); + registryData.fill(HIST("Tracks/InvQPt"), itrack.signed1Pt(), occ); + registryData.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); + registryData.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); } } - if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { reassignedTrkIds.emplace_back(atrack.mfttrackId()); if (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelNumReassoc)); - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), phi, itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), itrack.phi(), itrack.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); - qaregistry.fill(HIST("Tracks/ReTracksPhiEta"), phi, itrack.eta(), occ); + registryData.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); + registryData.fill(HIST("Tracks/ReTracksPhiEta"), itrack.phi(), itrack.eta(), occ); } } } @@ -1703,18 +927,13 @@ struct DndetaMFTPbPb { if (!isTrackSelected(track)) { continue; } - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if (fillHis) { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/OrigTracksEtaZvtx"), track.eta(), z, c, occ); - qaregistry.fill(HIST("Tracks/Centrality/OrigTracksPhiEta"), phi, track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/OrigTracksEtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/OrigTracksPhiEta"), track.phi(), track.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/OrigTracksEtaZvtx"), track.eta(), z, occ); - qaregistry.fill(HIST("Tracks/OrigTracksPhiEta"), phi, track.eta(), occ); + registryData.fill(HIST("Tracks/OrigTracksEtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/OrigTracksPhiEta"), track.phi(), track.eta(), occ); } } if (std::find(ambiguousTrkIds.begin(), ambiguousTrkIds.end(), track.globalIndex()) != ambiguousTrkIds.end()) { @@ -1726,27 +945,20 @@ struct DndetaMFTPbPb { // ++nATrk; // use for testing purposes only! if (fillHis) { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/RestTracksEtaZvtx"), track.eta(), z, c, occ); - qaregistry.fill(HIST("Tracks/Centrality/RestTracksPhiEta"), phi, track.eta(), c, occ); - // registry.fill(HIST("Tracks/Centrality/EtaZvtxBest"), track.eta(), z, c, occ); - // registry.fill(HIST("Tracks/Centrality/PhiEtaBest"), phi, track.eta(), c, occ); - // qaregistry.fill(HIST("Tracks/Centrality/NclustersEtaBest"), track.nClusters(), track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/RestTracksEtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/RestTracksPhiEta"), track.phi(), track.eta(), c, occ); + // registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); + // registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); + // registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/RestTracksEtaZvtx"), track.eta(), z, occ); - qaregistry.fill(HIST("Tracks/RestTracksPhiEta"), phi, track.eta(), occ); - // registry.fill(HIST("Tracks/EtaZvtxBest"), track.eta(), z, occ); - // registry.fill(HIST("Tracks/PhiEtaBest"), phi, track.eta(), occ); - // qaregistry.fill(HIST("Tracks/NclustersEtaBest"), track.nClusters(), track.eta(), occ); + registryData.fill(HIST("Tracks/RestTracksEtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/RestTracksPhiEta"), track.phi(), track.eta(), occ); + // registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); + // registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); + // registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); } } } - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NchBestSel"), nATrk, c, occ); - } else { - qaregistry.fill(HIST("Tracks/NchBestSel"), nATrk, occ); - } - } ambiguousTrkIds.clear(); ambiguousTrkIds.shrink_to_fit(); reassignedTrkIds.clear(); @@ -1754,59 +966,6 @@ struct DndetaMFTPbPb { return nATrk; } - template - int countPartMidRap(P const& particles) - { - int nCh = 0; - for (auto const& part : particles) { - if (!isChrgParticle(part.pdgCode())) { - continue; - } - if (!part.isPhysicalPrimary()) { - continue; - } - if (part.eta() < trackCuts.minEtaGenMult || part.eta() > trackCuts.maxEtaGenMult) { - continue; - } - nCh++; - } - return nCh; - } - - float getCentGenMult(int nCharged) - { - const auto& genMultBinLimits = binOpt.genMultBins.value; - const auto& recCentBinVal = binOpt.recCentBinCenters.value; - if (genMultBinLimits.size() != recCentBinVal.size()) { - LOGF(fatal, "genMultBinLimits size (%zu) is different from recCentBinVal size (%zu)", genMultBinLimits.size(), recCentBinVal.size()); - } - for (size_t i = 0; i < genMultBinLimits.size(); ++i) { - if (nCharged >= genMultBinLimits[i]) { - return recCentBinVal[i]; - } - } - return CInvalid; - } - - template - int countPart(P const& particles) - { - auto nCharged = 0; - for (auto const& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { - continue; - } - nCharged++; - } - return nCharged; - } - template bool isInelGt0wMft(P const& particles) { @@ -1833,16 +992,34 @@ struct DndetaMFTPbPb { } nChrgMc++; } - if (nChrgFT0A == CintZero || nChrgFT0C == CintZero) { - registry.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); + registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); return false; } - registry.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); + registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); return nChrgMc != CintZero; } + template + int countPart(P const& particles) + { + auto nCharged = 0; + for (auto const& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { + continue; + } + nCharged++; + } + return nCharged; + } + template bool isParticleSelected(P const& particle) { @@ -1857,10 +1034,6 @@ struct DndetaMFTPbPb { } if (trackCuts.usephiCut) { float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { - return false; - } if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -1872,6 +1045,16 @@ struct DndetaMFTPbPb { return true; } + template + bool cutInPhi(P const& particle) + { + if (!trackCuts.usephiCut) { + return true; + } + auto phi = particle.phi(); + return (phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut)); + } + template float getOccupancy(C const& collision, uint occEstimator) { @@ -1900,7 +1083,7 @@ struct DndetaMFTPbPb { float maxSec = std::ceil(runDuration.second * 1.e-3); /// round tsEOR to the lowest integer higher than tsEOR const AxisSpec axisSeconds{static_cast((maxSec - mMinSeconds) / 20.f), 0, maxSec - mMinSeconds, "Seconds since SOR"}; int hadronicRateBins = static_cast(eventCuts.maxIR - eventCuts.minIR); - gHadronicRate[mRunNumber] = registry.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {hadronicRateBins, eventCuts.minIR, eventCuts.maxIR}}).get(); + gHadronicRate[mRunNumber] = registryMC.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {hadronicRateBins, eventCuts.minIR, eventCuts.maxIR}}).get(); } gCurrentHadronicRate = gHadronicRate[mRunNumber]; } @@ -1909,75 +1092,75 @@ struct DndetaMFTPbPb { bool isGoodEvent(C const& collision) { if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAll)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAll)); } if (!collision.sel8()) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtSel)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtSel)); } if (eventCuts.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtIsGoodZvtx)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtIsGoodZvtx)); } if (eventCuts.requireRejectSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoSameBunchPileup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoSameBunchPileup)); } if (collision.posZ() <= eventCuts.minZvtx || collision.posZ() >= eventCuts.maxZvtx) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtZvtxCut)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtZvtxCut)); } if (eventCuts.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStd)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStd)); } if (eventCuts.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeNarrow)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeNarrow)); } if (eventCuts.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStrict)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStrict)); } if (eventCuts.requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStrict)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStrict)); } if (eventCuts.requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStandard)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStandard)); } if (eventCuts.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoHighMultCollInPrevRof)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoHighMultCollInPrevRof)); } if (eventCuts.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtGoodITSLayersAll)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtGoodITSLayersAll)); } if (eventCuts.minOccupancy >= 0 && getOccupancy(collision, eventCuts.occupancyEstimator) < @@ -1985,7 +1168,7 @@ struct DndetaMFTPbPb { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtBelowMinOccup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtBelowMinOccup)); } if (eventCuts.maxOccupancy >= 0 && getOccupancy(collision, eventCuts.occupancyEstimator) > @@ -1993,19 +1176,19 @@ struct DndetaMFTPbPb { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAboveMaxOccup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAboveMaxOccup)); } if (rctCuts.requireRCTFlagChecker && !rctChecker(collision)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagChecker)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagChecker)); } if (rctCuts.requireRCTFlagCheckerExtra && !rctCheckerExtra(collision)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagCheckerExtra)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagCheckerExtra)); } return true; } @@ -2023,8 +1206,7 @@ struct DndetaMFTPbPb { } template - void fillHistMC(P const& particles, float c, float occ, float zvtx, - bool const gtZeroColl) + void fillHistMC(P const& particles, float zvtx, float c, float occ) { for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { @@ -2033,37 +1215,12 @@ struct DndetaMFTPbPb { if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { continue; } - - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (isCent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), - zvtx, c); - registry.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), phi, - particle.eta(), c); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), particle.phi(), particle.eta(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx); - registry.fill(HIST("Tracks/PhiEtaGen_t"), phi, particle.eta()); - } - - if (gtZeroColl) { - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } - if constexpr (isCent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), - zvtx, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEtaGen"), phi, - particle.eta(), c, occ); - } else { - registry.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); - registry.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); - } + registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen"), particle.phi(), particle.eta(), occ); } } } @@ -2075,7 +1232,7 @@ struct DndetaMFTPbPb { for (auto const& bc : bcs) { if (static_cast(bc.selection_bit(aod::evsel::kIsBBT0A) && bc.selection_bit(aod::evsel::kIsBBT0C)) != 0) { - registry.fill(HIST("hBcSel"), 0); + registryQC.fill(HIST("hBcSel"), 0); cols.clear(); for (auto const& collision : collisions) { if (collision.has_foundBC()) { @@ -2088,49 +1245,38 @@ struct DndetaMFTPbPb { } LOGP(debug, "BC {} has {} collisions", bc.globalBC(), cols.size()); if (!cols.empty()) { - registry.fill(HIST("hBcSel"), 1); + registryQC.fill(HIST("hBcSel"), 1); if (cols.size() > 1) { - registry.fill(HIST("hBcSel"), 2); + registryQC.fill(HIST("hBcSel"), 2); } } } } } - PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", - true); + PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", true); /// @brief process function for counting tracks template void processData(typename C::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& /*bcs*/) + aod::MFTTracks const& tracks, + CollBCs const& /*bcs*/) { - auto occ = getOccupancy(collision, eventCuts.occupancyEstimator); float c = getRecoCent(collision); + float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); } else { - registry.fill(HIST("Events/Selection"), 1., occ); + registryData.fill(HIST("Events/Selection"), 1., occ); } - - // const bool isFT0Bad = bc.rct_bit(kFT0Bad); - // registry.fill(HIST("Events/hRCTSel"), 1.0); - // if (!isFT0Bad) { - // registry.fill(HIST("Events/hRCTSel"), 2.0); - // } - - if (!isGoodEvent(collision)) { - return; - } - if (gConf.cfgDoIR) { initHadronicRate(bc); float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/hInteractionRate"), c, occ, ir); + registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { - registry.fill(HIST("Events/hInteractionRate"), occ, ir); + registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate @@ -2138,50 +1284,49 @@ struct DndetaMFTPbPb { } gCurrentHadronicRate->Fill(seconds, ir); } - + if (!isGoodEvent(collision)) { + return; + } auto z = collision.posZ(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 2., c, occ); + registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); } else { - registry.fill(HIST("Events/Selection"), 2., occ); + registryData.fill(HIST("Events/Selection"), 2., occ); } auto nTrk = countTracks(tracks, z, c, occ); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); } else { - registry.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); } } - /// @brief process function for counting tracks (based on BestCollisionsFwd3d - /// table) + /// @brief process function for counting tracks (based on BestCollisionsFwd3d table) template - void processDatawBestTracks( - typename C::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& /*bcs*/) + void processDatawBestTracks(typename C::iterator const& collision, + aod::MFTTracks const& tracks, + soa::SmallGroups const& besttracks, + aod::BestCollisionsFwd3dExtra const& besttracksExtra, + CollBCs const& /*bcs*/) { - auto occ = getOccupancy(collision, eventCuts.occupancyEstimator); float c = getRecoCent(collision); + float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); } else { - registry.fill(HIST("Events/Selection"), 1., occ); - } - - if (!isGoodEvent(collision)) { - return; + registryData.fill(HIST("Events/Selection"), 1., occ); } - if (gConf.cfgDoIR) { initHadronicRate(bc); float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/hInteractionRate"), c, occ, ir); + registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { - registry.fill(HIST("Events/hInteractionRate"), occ, ir); + registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate @@ -2189,169 +1334,81 @@ struct DndetaMFTPbPb { } gCurrentHadronicRate->Fill(seconds, ir); } - + if (!isGoodEvent(collision)) { + return; + } auto z = collision.posZ(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - qaregistry.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - qaregistry.fill(HIST("Events/Centrality/hCent"), c, occ); + registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); } else { - registry.fill(HIST("Events/Selection"), 2., occ); + registryData.fill(HIST("Events/Selection"), 2., occ); + registryData.fill(HIST("Events/hZvtxCent"), z, c, occ); } auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); countBestTracksExtra(besttracksExtra, c, occ); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxBest"), nBestTrks, z, c, - occ); + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); } else { - registry.fill(HIST("Events/NtrkZvtxBest"), nBestTrks, z, occ); + registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); } } - void processDataInclusive(Colls::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) + void processDataInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& bcs) { processData(collision, tracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDataInclusive, - "Count tracks (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processDataInclusive, "Count tracks (inclusive)", false); - void processDataCentFT0C(CollsCentFT0C::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) + void processDataCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& bcs) { processData(collision, tracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, - "Count tracks in FT0C centrality bins", false); - - void - processDataCentFT0CVariant1(CollsCentFT0CVariant1::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0CVariant1, - "Count tracks in FT0CVariant1 centrality bins", false); - - void processDataCentFT0M(CollsCentFT0M::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0M, - "Count tracks in FT0M centrality bins", false); - - void processDataCentNGlobal(CollsCentNGlobal::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentNGlobal, - "Count tracks in NGlobal centrality bins", false); - - void processDataCentMFT(CollsCentMFT::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentMFT, - "Count tracks in MFT centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, "Count tracks in FT0C centrality bins", false); - void processDatawBestTracksInclusive( - Colls::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) { processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, - "Count tracks based on BestCollisionsFwd3d table (inclusive)", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, "Count tracks based on BestCollisionsFwd3d table (inclusive)", false); - void processDatawBestTracksCentFT0C( - CollsCentFT0C::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) { processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, - "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentFT0CVariant1( - CollsCentFT0CVariant1::iterator const& collision, - FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0CVariant1, - "Count tracks in FT0CVariant1 centrality bins based on " - "BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentFT0M( - CollsCentFT0M::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0M, - "Count tracks in FT0M centrality bins based on BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentNGlobal( - CollsCentNGlobal::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentNGlobal, - "Count tracks in NGlobal centrality bins based on " - "BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentMFT( - CollsCentMFT::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentMFT, - "Count tracks in MFT centrality bins based on BestCollisionsFwd3d table", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", false); - Preslice perCol = o2::aod::fwdtrack::collisionId; - PresliceUnsorted recColPerMcCol = - aod::mccollisionlabel::mcCollisionId; + Preslice perCol = o2::aod::fwdtrack::collisionId; Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); - /// @brief process template function to run on MC gen template - void processMC( - typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) + void processMC(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - bool gtZeroColl = false; - int gtOneColl = 0; - + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + float occGen = -1.; + for (const auto& collision : collisions) { + if (isGoodEvent(collision)) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } + } + } float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } if constexpr (has_reco_cent) { - float crecMin = 105.f; + float crecMin = 999.; for (const auto& collision : collisions) { if (isGoodEvent(collision)) { float c = getRecoCent(collision); @@ -2365,197 +1422,150 @@ struct DndetaMFTPbPb { } } - float occGen = -1.; - for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; - } - } - } - for (auto const& collision : collisions) { - float occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - float crec = getRecoCent(collision); - + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + float cRec = getRecoCent(collision); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 1., crec, occrec); + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); } else { - registry.fill(HIST("Events/EvtEffGen"), 1., occrec); + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); + } + if (!isGoodEvent(collision)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); + } + if (!collision.has_mcCollision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); } - if (isGoodEvent(collision)) { - gtZeroColl = true; - ++gtOneColl; - auto z = collision.posZ(); - - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 2., crec, occrec); - registry.fill(HIST("Events/Centrality/hRecCent"), crec, occrec); - registry.fill(HIST("Events/Centrality/hRecZvtxCent"), z, crec, - occrec); - } else { - registry.fill(HIST("Events/EvtEffGen"), 2., occrec); - } - - auto perColSample = tracks.sliceBy(perCol, collision.globalIndex()); - auto nTrkRec = countTracks(perColSample, z, crec, occrec); - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/ZvtxDiff"), - collision.posZ() - mcCollision.posZ(), crec); - } else { - qaregistry.fill(HIST("Events/ZvtxDiff"), - collision.posZ() - mcCollision.posZ()); + auto nTrkRec = 0; + auto perColSample = tracks.sliceBy(perCol, collision.globalIndex()); + for (auto const& track : perColSample) { + if (!isTrackSelected(track)) { + continue; } - - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > - eventCuts.maxZvtxDiff) { + if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + const auto& particle = track.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occRec); + registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + } + ++nTrkRec; } - - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, - collision.posZ(), crec, occrec); - } else { - registry.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), - occrec); + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; } } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), occRec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } } if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 3., cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); } else { - registry.fill(HIST("Events/EvtEffGen"), 3., occGen); - } - - auto perCollMCsample = mcSample->sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto nchrg = countPart(perCollMCsample); - auto zvtxMC = mcCollision.posZ(); - - if (gtOneColl > 1) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/SplitMult"), nchrg, zvtxMC, cGen); - } else { - qaregistry.fill(HIST("Events/SplitMult"), nchrg, zvtxMC); - } + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); } auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, zvtxMC, - cGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registry.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, zvtxMC); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } - - fillHistMC>(particles, cGen, occGen, zvtxMC, gtZeroColl); - if (collisions.size() == 0) { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/NotFoundEventZvtx"), - mcCollision.posZ(), cGen); + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); } else { - qaregistry.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ()); + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); } } + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); } - void processMCInclusive( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) - { - processMC(mccollision, collisions, particles, - tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCInclusive, - "Count MC particles (inclusive)", false); - - void processMCCentFT0C( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0C, - "Count MC particles in FT0C centrality bins", false); - - void processMCCentFT0CVariant1( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0CVariant1, - "Count MC particles in FT0CVariant1 centrality bins", false); - - void processMCCentFT0M( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0M, - "Count MC particles in FT0M centrality bins", false); - - void processMCCentNGlobal( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) + void processMCInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processMC(mccollision, collisions, - particles, tracks); + processMC(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentNGlobal, - "Count MC particles in NGlobal centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMCInclusive, "Count MC particles (inclusive)", false); - void processMCCentMFT( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks) + void processMCCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processMC(mccollision, collisions, - particles, tracks); + processMC(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentMFT, - "Count MC particles in MFT centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0C, "Count MC particles in FT0C centrality bins", false); - PresliceUnsorted perColU = - aod::fwdtrack::bestCollisionId; + PresliceUnsorted perColU = aod::fwdtrack::bestCollisionId; - /// @brief process template function to run on MC truth using - /// aod::BestCollisionsFwd3d tracks template - void processMCwBestTracks( - typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) + void processMCbest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& /*tracks*/, + MftBestTracksLabeled const& besttracks) { - bool gtZeroColl = false; + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + float occGen = -1.; + for (const auto& collision : collisions) { + if (isGoodEvent(collision)) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } + } + } float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } if constexpr (has_reco_cent) { - float crecMin = 105.f; + float crecMin = 999.; for (const auto& collision : collisions) { if (isGoodEvent(collision)) { float c = getRecoCent(collision); @@ -2569,362 +1579,543 @@ struct DndetaMFTPbPb { } } - float occGen = -1.; - for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; - } - } - } + buildLookupTable(collisions); for (auto const& collision : collisions) { - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - float crec = getRecoCent(collision); - + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + float cRec = getRecoCent(collision); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 1., crec, occrec); + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); } else { - registry.fill(HIST("Events/EvtEffGen"), 1., occrec); + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); + } + if (!isGoodEvent(collision)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); + } + if (!collision.has_mcCollision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); } - if (isGoodEvent(collision)) { - gtZeroColl = true; - auto z = collision.posZ(); - + auto nATrk = 0; + auto perCollisionASample = besttracks.sliceBy(perColU, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 2., crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); } else { - registry.fill(HIST("Events/EvtEffGen"), 2., occrec); + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); + } + if (!isBestTrackSelected(atrack)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); + } + if (!itrack.has_collision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + } + if (gConf.cfgRemoveReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } } - - auto perCollisionSample = - tracks.sliceBy(perCol, collision.globalIndex()); - auto perCollisionASample = - besttracks.sliceBy(perColU, collision.globalIndex()); - auto nTrkRec = countBestTracks( - perCollisionSample, perCollisionASample, z, crec, - collision.trackOccupancyInTimeRange()); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, z, - crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); } else { - registry.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, z, occrec); + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + const auto& particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + // if (collision.mcCollisionId() != particle.mcCollisionId()) { + // continue; + // } + const int bestRecColl = atrack.bestCollisionId(); + if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + continue; + } + int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; + if (mcCollIdRec != particle.mcCollisionId()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); + registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + } + ++nATrk; } } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nATrk, collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nATrk, collision.posZ(), occRec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } } if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 3., cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); } else { - registry.fill(HIST("Events/EvtEffGen"), 3., occGen); + registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); } - auto zvtxMC = mcCollision.posZ(); auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, zvtxMC, - cGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registry.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, zvtxMC); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } - - fillHistMC>(particles, cGen, occGen, zvtxMC, gtZeroColl); - if (collisions.size() == 0) { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/NotFoundEventZvtx"), - mcCollision.posZ(), cGen); + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); } else { - qaregistry.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ()); + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); } } + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); } - void processMCwBestTracksInclusive( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksInclusive, - "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", - false); - - void processMCwBestTracksCentFT0C( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0C, - "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentFT0CVariant1( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0CVariant1, - "Count MC particles in FT0CVariant1 centrality bins using " - "aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentFT0M( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0M, - "Count MC particles in FT0M centrality bins using aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentNGlobal( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) + void processMCbestInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& besttracks) { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); + processMCbest(mccollision, collisions, particles, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentNGlobal, - "Count MC particles in NGlobal centrality bins using " - "aod::BestCollisionsFwd3d", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processMCbestInclusive, "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", false); - void processMCwBestTracksCentMFT( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, MFTTracksLabeled const& tracks, - FiltBestTracks const& besttracks) + void processMCbestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& besttracks) { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); + processMCbest(mccollision, collisions, particles, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentMFT, - "Count MC particles in MFT centrality bins using aod::BestCollisionsFwd3d", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processMCbestCentFT0C, "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", false); - using ParticlesI = soa::Join; - Partition primariesI = (aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); + Preslice filtMcTrkperCol = o2::aod::fwdtrack::collisionId; - // Preslice perMCCol = aod::mcparticle::mcCollisionId; - // - template - void processTrkEffIdxBest( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, ParticlesI const& /*particles*/, - BestTracksMC const& atracks) - { - for (auto const& collision : collisions) { + /// @brief process function to calculate tracking efficiency + template + void processEfficiency(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = -999.; + auto occRec = -999.; + bool gtOneRec = false; + for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (!collision.has_mcCollision()) { + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + gtOneRec = true; + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } - float crec = getRecoCent(collision); - auto mcCollision = collision.mcCollision(); - - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; - } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); } - - auto partsPerCol = primariesI->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - partsPerCol.bindExternalIndices(&atracks); - for (auto const& particle : partsPerCol) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - - // MC gen - if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenBest"), particle.pt(), particle.eta(), crec); - } + } else { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + } + } + for (const auto& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenBest"), particle.pt(), particle.eta()); - } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); } } - // MC rec - if (particle.has_mfttracks()) { - auto iscounted = false; - auto ncnt = 0; - auto relatedTracks = particle.template mfttracks_as(); - for (auto const& track : relatedTracks) { - if (!isBestTrackSelected(track)) { - continue; - } - ++ncnt; - - if constexpr (has_reco_cent) { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffPrimBest"), particle.pt(), particle.eta(), crec); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffSecBest"), particle.pt(), particle.eta(), crec); - } - } - } else { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffPrimBest"), particle.pt(), particle.eta()); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtEtaEffSecBest"), particle.pt(), particle.eta()); - } - } - } - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NmftTrkPerPartBest"), ncnt, crec); - } else { - qaregistry.fill(HIST("Tracks/NmftTrkPerPartBest"), ncnt); - } - - if (relatedTracks.size() > 1) { // duplicates - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenDuplBest"), particle.pt(), particle.eta(), crec); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffDuplBest"), track.pt(), track.eta(), crec); - } - } else { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenDuplBest"), particle.pt(), particle.eta()); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/hPtEtaEffDuplBest"), track.pt(), track.eta()); - } - } - } + } + } + for (const auto& collision : collisions) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + } + if (!isGoodEvent(collision)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + } + if (!collision.has_mcCollision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + } + auto perColTrks = tracks.sliceBy(filtMcTrkperCol, collision.globalIndex()); + for (auto const& track : perColTrks) { + if (!isTrackSelected(track)) { + continue; + } + if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + const auto& particle = track.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { - // MC FAKES if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenFakeBest"), particle.pt(), particle.eta(), crec); - } - } + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenFakeBest"), particle.pt(), particle.eta()); - } - } + registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } } } - void processTrkEffIdxBestInlusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - BestTracksMC const& atracks) + void processEfficiencyInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processTrkEffIdxBest(collisions, mccollisions, particles, atracks); + processEfficiency(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); - void processTrkEffIdxBestCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - BestTracksMC const& atracks) + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyInclusive, "Process efficiencies (inclusive)", false); + + void processEfficiencyCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processTrkEffIdxBest(collisions, mccollisions, particles, atracks); + processEfficiency(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); - - /// @brief process template function to calculate tracking efficiency (indexed - /// as particle-to-MFT-tracks) - template - void processTrkEffIdx( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, ParticlesI const& /*particles*/, - MFTTracksLabeled const& tracks) + + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyCentFT0C, "Process efficiencies in FT0C centrality bins", false); + + /// @brief process function to calculate tracking efficiency based on BestCollisionsFwd3d in FT0C bins + template + void processEfficiencyBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) { - for (auto const& collision : collisions) { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + + buildLookupTable(collisions); + + auto cRec = -999.; + auto occRec = -999.; + bool gtOneRec = false; + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + gtOneRec = true; + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } + + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + } + } else { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + } + } + + for (const auto& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + } + } + } + } + + for (const auto& collision : collisions) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + } if (!isGoodEvent(collision)) { continue; } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + } if (!collision.has_mcCollision()) { continue; } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + } else { + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + } - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); + auto perCollisionASample = besttracks.sliceBy(perColU, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if (!isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + if (!itrack.has_collision()) { + continue; + } + if (gConf.cfgRemoveReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + const auto& particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + const int bestRecColl = atrack.bestCollisionId(); + if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + continue; + } + int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; + if (mcCollIdRec != particle.mcCollisionId()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + } + } + } + } + + void processEfficiencyBestInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) + { + processEfficiencyBest(mccollision, collisions, particles, besttracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); + + void processEfficiencyBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) + { + processEfficiencyBest(mccollision, collisions, particles, besttracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); + + // Partition primariesIdx = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > 5.f, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)) && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); - auto partsPerCol = primariesI->sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - partsPerCol.bindExternalIndices(&tracks); + /// @brief process template function to calculate tracking efficiency (indexed as particle-to-MFT-tracks) + template + void processEfficiencyIdx(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftTracksLabeled const& /*tracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); for (auto const& particle : partsPerCol) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - // MC gen if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGen"), particle.pt(), particle.eta(), crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); } } } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGen"), particle.pt(), particle.eta(), occrec); + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } } } @@ -2932,56 +2123,72 @@ struct DndetaMFTPbPb { if (particle.has_mfttracks()) { auto iscounted = false; auto ncnt = 0; - auto relatedTracks = particle.template mfttracks_as(); + auto relatedTracks = particle.template mfttracks_as(); for (auto const& track : relatedTracks) { + if (!isTrackSelected(track)) { + continue; + } + if (track.mcMask() != trackCuts.selMcMask) { + continue; + } ++ncnt; if constexpr (has_reco_cent) { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffPrim"), particle.pt(), particle.eta(), crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); } iscounted = true; } } + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); if (ncnt > 1) { // secondaries if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffSec"), particle.pt(), particle.eta(), crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); } } } else { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffPrim"), particle.pt(), particle.eta(), occrec); + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); } iscounted = true; } } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occRec); if (ncnt > 1) { // secondaries if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtEtaEffSec"), particle.pt(), particle.eta(), occrec); + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), occRec); } } } } - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occrec); - } - if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenDupl"), particle.pt(), particle.eta(), crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffDupl"), track.pt(), track.eta(), crec, occrec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); } } else { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenDupl"), particle.pt(), particle.eta(), occrec); + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/hPtEtaEffDupl"), track.pt(), track.eta(), occrec); + registryMC.fill(HIST("Tracks/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), occRec); + } + } + } + } else { + // MC FAKES + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } } @@ -2990,374 +2197,197 @@ struct DndetaMFTPbPb { } } - void processTrkEffIdxInlusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - MFTTracksLabeled const& tracks) + void processEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftTracksLabeled const& tracks) { - processTrkEffIdx(collisions, mccollisions, - particles, tracks); + processEfficiencyIdx(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxInlusive, - "Process tracking efficiency (inclusive, indexed)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxInlusive, "Process tracking efficiency (inclusive, indexed)", false); - void processTrkEffIdxCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - MFTTracksLabeled const& tracks) + void processEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftTracksLabeled const& tracks) { - processTrkEffIdx(collisions, mccollisions, - particles, tracks); + processEfficiencyIdx(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxCentFT0C, - "Process tracking efficiency (in FT0C centrality bins, indexed)", false); - - /// @brief process function to calculate tracking efficiency based - /// on BestCollisionsFwd3d in FT0C bins - template - void processTrkEffBest( - typename soa::Join::iterator const& collision, - MC const& /*mccollisions*/, aod::McParticles const& particles, - MFTTracksLabeled const& tracks, - soa::SmallGroups const& besttracks) + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxCentFT0C, "Process tracking efficiency (in FT0C centrality bins, indexed)", false); + + template + void processEfficiencyIdxBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftBestTracksLabeled const& /*atracks*/) { - if (!isGoodEvent(collision)) { - return; - } - if (!collision.has_mcCollision()) { - return; - } + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); - auto partsPerCol = particles.sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + buildLookupTable(collisions); - for (auto const& particle : partsPerCol) { - if (!isChrgParticle(particle.pdgCode())) { + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), crec, occrec); - } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), occrec); - } - } - } + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - ambiguousTrkIdsMC.reserve(besttracks.size()); - reassignedTrkIdsMC.reserve(besttracks.size()); - - for (auto const& track : besttracks) { - ambiguousTrkIdsMC.emplace_back(track.mfttrackId()); - if (!isBestTrackSelected(track)) { - continue; - } - auto itrack = track.mfttrack_as(); - if (itrack.collisionId() != track.bestCollisionId()) { - reassignedTrkIdsMC.emplace_back(track.mfttrackId()); - } - if (!isTrackSelected(itrack)) { - continue; - } - if (itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { - auto particle = itrack.mcParticle_as(); - if (itrack.eta() > trackCuts.minEta && itrack.eta() < trackCuts.maxEta) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestRec"), - particle.pt(), particle.phi(), particle.eta(), - mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - occrec); - } + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { + continue; } - } else { + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + // MC gen if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEffBestFakeRec"), itrack.pt(), itrack.phi(), itrack.eta(), mcCollision.posZ(), crec, occrec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); + } + } } else { - qaregistry.fill(HIST("Tracks/hPtEffBestFakeRec"), itrack.pt(), itrack.phi(), itrack.eta(), mcCollision.posZ(), occrec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); + } + } } - } - } - - for (auto const& track : tracks) { - if (std::find(ambiguousTrkIdsMC.begin(), ambiguousTrkIdsMC.end(), track.globalIndex()) != ambiguousTrkIdsMC.end()) { - continue; - } - if (std::find(reassignedTrkIdsMC.begin(), reassignedTrkIdsMC.end(), track.globalIndex()) != reassignedTrkIdsMC.end()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { - auto particle = track.mcParticle_as(); - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestRec"), - particle.pt(), particle.phi(), particle.eta(), - mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - occrec); + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& atrack : relatedTracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + const int bestRecColl = atrack.bestCollisionId(); + if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + continue; + } + int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; + if (mcCollIdRec != particle.mcCollisionId()) { + continue; + } + ++ncnt; + if constexpr (has_reco_cent) { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); + } + } + } else { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), occRec); + } + } + } } - } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEffBestFakeRec"), track.pt(), track.phi(), track.eta(), mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtEffBestFakeRec"), track.pt(), track.phi(), track.eta(), mcCollision.posZ(), occrec); - } - } - } - ambiguousTrkIdsMC.clear(); - ambiguousTrkIdsMC.shrink_to_fit(); - reassignedTrkIdsMC.clear(); - reassignedTrkIdsMC.shrink_to_fit(); - } - - void processTrkEffBestInclusive( - soa::Join::iterator const& collision, - aod::McCollisions const& mccollisions, aod::McParticles const& particles, - MFTTracksLabeled const& tracks, - soa::SmallGroups const& besttracks) - { - processTrkEffBest(collision, mccollisions, - particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffBestInclusive, - "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", - false); - - void processTrkEffBestCentFT0C( - soa::Join::iterator const& collision, - aod::McCollisions const& mccollisions, aod::McParticles const& particles, - MFTTracksLabeled const& tracks, - soa::SmallGroups const& besttracks) - { - processTrkEffBest( - collision, mccollisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffBestCentFT0C, - "Process tracking efficiency (in FT0 centrality bins, based " - "on BestCollisionsFwd3d)", - false); - - Preslice filtMcTrkperCol = o2::aod::fwdtrack::collisionId; - - /// @brief process function to calculate MC efficiency and fraction of fake - /// tracks - template - void processEfficiency( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, aod::McParticles const& /*particles*/, - MFTTracksLabeled const& tracks) - { - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); - auto perColTrks = - tracks.sliceBy(filtMcTrkperCol, collision.globalIndex()); - - for (auto const& track : perColTrks) { - if (!isTrackSelected(track)) { - continue; - } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { - auto particle = track.template mcParticle_as(); - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hEffRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - crec, occrec); + if (relatedTracks.size() > 1) { // duplicates + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); + } + } else { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), occRec); + } + } } } else { + // MC FAKES if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hEffFake"), track.pt(), - track.phi(), track.eta(), mcCollision.posZ(), crec, - occrec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + } + } } else { - qaregistry.fill(HIST("Tracks/hEffFake"), track.pt(), track.phi(), - track.eta(), mcCollision.posZ(), crec, occrec); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); + } + } } } } } } - void processEfficiencyInclusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, aod::McParticles const& particles, - MFTTracksLabeled const& tracks) + void processEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) { - processEfficiency(collisions, mccollisions, - particles, tracks); + processEfficiencyIdxBest(mccollision, collisions, particles, atracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyInclusive, - "Process efficiencies (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); - void processEfficiencyCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, aod::McParticles const& particles, - MFTTracksLabeled const& tracks) + void processEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) { - processEfficiency( - collisions, mccollisions, particles, tracks); + processEfficiencyIdxBest(mccollision, collisions, particles, atracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyCentFT0C, - "Process efficiencies in FT0C centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); - template - void processCorrelationwBestTracks(typename C::iterator const& collision, FiltMftTracks const& /*tracks*/, soa::SmallGroups const& besttracks) + /// @brief process function to calculate signal loss based on MC + void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles) { - if (!isGoodEvent(collision)) { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenAll)); + registryMC.fill(HIST("Events/hEvtMcGen"), 0.5); + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { return; } - - auto nBestTrks = 0; - for (auto const& atrack : besttracks) { - if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { - continue; - } - auto itrack = atrack.template mfttrack_as(); - if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { - continue; - } - if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { - continue; - } - nBestTrks++; - } - qaregistry.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); - qaregistry.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); - qaregistry.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); - qaregistry.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); - qaregistry.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); - } - - void processCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, FiltMftTracks const& tracks, soa::SmallGroups const& besttracks) - { - processCorrelationwBestTracks(collision, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processCorrelationwBestTracksInclusive, "Do correlation study based on BestCollisionsFwd3d table", false); - - int getQuadrantPhi(float phi) - { - if (phi >= Czero && phi < Cninety) { - return 0; - } - if (phi >= Cninety && phi <= ConeHeighty) { - return 1; - } - if (phi >= -ConeHeighty && phi < -Cninety) { - return 2; - } - if (phi >= -Cninety && phi < Czero) { - return 3; - } - return -1; - } - - template - int getQuadrantTrack(T const& track) - { - float phi = static_cast(track.phi()) * ConeHeighty / PI; - return getQuadrantPhi(phi); - } - - /// @brief process function to check MFT alignment (based on BestCollisionsFwd3d table) - template - void processAlignment(typename C::iterator const& collision, - FiltMftTracks const& /*tracks*/, - soa::SmallGroups const& besttracks, - CollBCs const& /*bcs*/ - ) - { - auto bc = collision.template foundBC_as(); - if (!isGoodEvent(collision)) { + registryMC.fill(HIST("Events/hEvtMcGen"), 1.5); + // At least one generated primary in MFT acceptance + TVX triggered collisions + if (gConf.cfgUseInelgt0wMFT && !isInelGt0wMft(particles)) { return; } - - if (gConf.cfgDoIR) { - initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; - float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate - return; - } - gCurrentHadronicRate->Fill(seconds, ir); - } - - auto z = collision.posZ(); - for (auto const& atrack : besttracks) { - if (!isBestTrackSelected(atrack)) { - continue; - } - auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { - continue; - } - - int quadrant = getQuadrantTrack(itrack); - if (quadrant < 0) { - continue; - } - std::get>(hAlignment[0][quadrant][0]["DCA_x_vs_z"])->Fill(z, atrack.bestDCAXY()); - } - } - - void processAlignmentInclusive(Colls::iterator const& collision, - FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, - CollBCs const& bcs) - { - processAlignment(collision, tracks, besttracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processAlignmentInclusive, "Check MFT alignment using tracks based on BestCollisionsFwd3d table (inclusive)", false); - - /// @brief process function to calculate signal loss based on MC - void processEventAndSignalLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles) - { - registry.fill(HIST("Events/Centrality/hNRecCollsSigEvtLoss"), 1.f, collisions.size()); - - if (gConf.cfgUseInelgt0 && !mcCollision.isInelGt0()) { - return; - } - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + registryMC.fill(HIST("Events/hEvtMcGen"), 2.5); + if (eventCuts.useInelgt0wTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { return; } + registryMC.fill(HIST("Events/hEvtMcGen"), 3.5); + registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSel)); // Evt loss den bool gtZeroColl = false; auto maxNcontributors = -1; - float centrality = -999.f; + float cRec = -999.f; for (auto const& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -3365,9 +2395,11 @@ struct DndetaMFTPbPb { if (std::abs(collision.posZ()) >= eventCuts.maxZvtx) { continue; } + registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSplit)); + registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), mcCollision.multMCFT0C()); if (maxNcontributors < collision.numContrib()) { maxNcontributors = collision.numContrib(); - centrality = getRecoCent(collision); + cRec = getRecoCent(collision); } gtZeroColl = true; } @@ -3375,22 +2407,21 @@ struct DndetaMFTPbPb { auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 1., centrality); - registry.fill(HIST("Events/Centrality/hMultGenVsCent"), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentNParticlesEta05"), centrality, mcCollision.multMCNParticlesEta05()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentNParticlesEtaMFT"), centrality, multMCNParticlesEtaMFT); - - if (collisions.size() == 0) { - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 3., centrality); - } + registryMC.fill(HIST("Events/EvtSigLossStatus"), 1., cRec); // Evt split den + registryMC.fill(HIST("Events/hMultGenVsCent"), cRec, mcCollision.multMCFT0C()); + registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); + registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); if (gtZeroColl) { - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 2., centrality); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRec"), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRecNParticlesEta05"), centrality, mcCollision.multMCNParticlesEta05()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRecNParticlesEtaMFT"), centrality, multMCNParticlesEtaMFT); + registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenRecEvt)); // Evt loss num + registryMC.fill(HIST("Events/EvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/hMultGenVsCentRec"), cRec, mcCollision.multMCFT0C()); + registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); + registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); + } + if (collisions.size() == 0) { + registryMC.fill(HIST("Events/EvtSigLossStatus"), 3., cRec); } - for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; @@ -3398,302 +2429,53 @@ struct DndetaMFTPbPb { if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { continue; } - - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } - - registry.fill(HIST("Tracks/Centrality/EtaCentVsMultGen_t"), particle.eta(), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Tracks/Centrality/EtaGen_t"), particle.eta(), centrality); - + registryMC.fill(HIST("Tracks/hEtaVsNchGen"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss den if (gtZeroColl) { - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } - registry.fill(HIST("Tracks/Centrality/EtaCentVsMultGen"), particle.eta(), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Tracks/Centrality/EtaGen"), particle.eta(), centrality); - } - } - } - - PROCESS_SWITCH(DndetaMFTPbPb, processEventAndSignalLossCentFT0C, "Signal/event loss based on MC (in FT0C centrality bins)", false); - - Preslice filtTrkperCol = o2::aod::fwdtrack::collisionId; - - /// @brief process template function for MC QA checks - void processMcQACentFT0C(soa::Join const& collisions, - MFTTracksLabeled const& tracks, - aod::AmbiguousMFTTracks const& atracks, - aod::McCollisions const& mcCollisions, - aod::McParticles const& /*particles*/) - { - for (const auto& collision : collisions) { - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - qaregistry.fill(HIST("Events/Centrality/hRecPerGenColls"), static_cast(collisions.size()) / mcCollisions.size(), crec, occrec); - if (!isGoodEvent(collision)) { - return; - } - - auto trkPerColl = tracks.sliceBy(filtTrkperCol, collision.globalIndex()); - uint ntracks{0u}, nAtracks{0u}; - for (const auto& track : trkPerColl) { - ntracks++; - for (const auto& atrack : atracks) { - if (atrack.mfttrackId() == track.globalIndex()) { - nAtracks++; - break; - } - } + registryMC.fill(HIST("Tracks/hEtaVsNchGenRecEvt"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss num } - qaregistry.fill(HIST("Tracks/Centrality/hNmftTrks"), ntracks, crec, occrec); - qaregistry.fill(HIST("Tracks/Centrality/hFracAmbiguousMftTrks"), static_cast(nAtracks) / ntracks, crec, occrec); } } - PROCESS_SWITCH(DndetaMFTPbPb, processMcQACentFT0C, "Process MC QA checks (in FT0 centrality bins)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcSgnEvtLossCentFT0C, "Process Signal/event loss based on MC (in FT0C centrality bins)", false); - /// @brief process function to check ambiguous tracks - void processCheckAmbiguousMftTracks(aod::Collisions const&, MftTracksWColls const& tracks) - { - for (auto const& track : tracks) { - auto trkCollId = track.has_collision() ? track.collisionId() : -1; - auto ids = track.compatibleCollIds(); - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAll)); - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { - qaregistry.fill(HIST("Tracks/hMftTracksAmbDegreeWithTrivial"), track.compatibleCollIds().size()); - if (ids.empty()) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); - } - if (ids.size() == 1 && trkCollId == ids[0]) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); - } - continue; - } - qaregistry.fill(HIST("Tracks/hMftTracksAmbDegree"), track.compatibleCollIds().size()); - - if (!track.compatibleCollIds().empty()) { - if (track.compatibleCollIds().size() == 1) { - if (track.collisionId() != track.compatibleCollIds()[0]) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); - } else { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); - } - } else { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); - } - } - } - } - - PROCESS_SWITCH(DndetaMFTPbPb, processCheckAmbiguousMftTracks, "Process checks for Ambiguous MFT tracks (inclusive)", false); - - PresliceUnsorted perColBestTrks = aod::fwdtrack::bestCollisionId; - - void processDCAReassocMc( - CollsGenCentFT0CExtra const& collisions, - CollsMCExtra const& mcCollisions, - aod::McParticles const& particles, - BestTracksMC const& besttracks, - MFTTracksLabeled const& /*tracks*/ - ) + template + void processCorrelationwBestTracks(typename C::iterator const& collision, + aod::MFTTracks const& /*tracks*/, + soa::SmallGroups const& besttracks) { - float cGen = -1; - float crecMin = 105.f; - for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; - } - } - } - if (cGen < 0) { - cGen = crecMin; + if (!isGoodEvent(collision)) { + return; } - createMCIds(mcCollisions, collisions, particles); - - registry.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 1., cGen); - - int nNoMC{0}; - for (const auto& collision : collisions) { - auto crec = getRecoCent(collision); - registry.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); - if (!isGoodEvent(collision)) { - continue; - } - if (!collision.has_mcCollision()) { - continue; - } - - int64_t recCollId = collision.globalIndex(); - auto itMC = mapMcCollIdPerRecColl.find(recCollId); - if (itMC == mapMcCollIdPerRecColl.end()) { - nNoMC++; + auto nBestTrks = 0; + for (auto const& atrack : besttracks) { + if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { continue; } - registry.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); - if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + auto itrack = atrack.template mfttrack_as(); + if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { continue; } - auto mcColl = collision.mcCollision_as(); - if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { + if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { continue; } - registry.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 4., crec); - - auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); - for (auto const& atrack : perCollisionASample) { - if (!isBestTrackSelected(atrack)) { - continue; - } - const float bestDcaZ = getDCAz(atrack); - auto itrack = atrack.template mfttrack_as(); - - if (!isTrackSelected(itrack)) { - continue; - } - if (!itrack.has_collision()) { - continue; - } - if (gConf.cfgRemoveReassigned) { - if (itrack.collisionId() != atrack.bestCollisionId()) { - continue; - } - } - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestRec"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), atrack.bestDCAZ(), crec, crec); - - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { - auto particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { - continue; - } - } - const int bestRecColl = atrack.bestCollisionId(); - auto itMapMcCollIdPerRecColl = mapMcCollIdPerRecColl.find(bestRecColl); - if (itMapMcCollIdPerRecColl == mapMcCollIdPerRecColl.end()) { - continue; - } - const float mcCollIdRec = itMapMcCollIdPerRecColl->second; - // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - bestMCCol: {}", mcCollIdRec, bestMCCol); - const auto dcaXtruth(particle.vx() - mcColl.posX()); - const auto dcaYtruth(particle.vy() - mcColl.posY()); - const auto dcaZtruth(particle.vz() - mcColl.posZ()); - auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); - - const auto mcId = particle.mcCollisionId(); - if (!mcIds.contains(mcId)) { - continue; - } - auto itRefCent = refCentByMcId.find(mcId); - if (itRefCent == refCentByMcId.end()) { - continue; - } - const float refCent = itRefCent->second; - // auto itGenCent = genCentByMcId.find(mcId); - // if (itGenCent == genCentByMcId.end()) { - // continue; - // } - // const int genCent = itGenCent->second; - - if (atrack.ambDegree() > CintZero) { // all tracks - if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSec"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSec"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeak"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } else { // Particles from the material - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMat"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrim"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrim"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } else { // Wrong collision - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } - if (atrack.ambDegree() == CintOne) { // non-ambiguous - if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } else { // Particles from the material - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } else { // Wrong collision - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } - } else { // ambiguous - if (collision.has_mcCollision() && mcCollIdRec == mcId) { // good coll - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } else { // Particles from the material - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } else { // wrong collision - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } else { // Particles from the material - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } - } else { // Primaries - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); - } - } - } - } else { // no MC particle - LOGP(debug, "No MC particle for ambiguous itrack, skip..."); - registry.fill(HIST("Tracks/Centrality/THnDCAxyBestRecFake"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); - } - } - } + nBestTrks++; } - LOG(info) << "No MC: " << nNoMC; + registryData.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); + registryData.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); + registryData.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); + registryData.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); + registryData.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); + } + + void processCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, + aod::MFTTracks const& tracks, + soa::SmallGroups const& besttracks) + { + processCorrelationwBestTracks(collision, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processDCAReassocMc, "Process MC DCA checks using re-association information based on BestCollisionsFwd3d table (in FT0C centrality bins)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processCorrelationwBestTracksInclusive, "Process correlation QA based on BestCollisionsFwd3d table", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From d217352724dc91d1c052d6b40c96408f9b4537db Mon Sep 17 00:00:00 2001 From: "M. Monalisa Melo Paulino" <71565863+monamelop@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:41:15 +0200 Subject: [PATCH 032/648] [PWGJE] Add MC efficiency information to Ds jet task (#18009) --- PWGJE/Tasks/jetDsSpecSubs.cxx | 67 ++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/PWGJE/Tasks/jetDsSpecSubs.cxx b/PWGJE/Tasks/jetDsSpecSubs.cxx index 593fd0f63e5..d1a5a634fc1 100644 --- a/PWGJE/Tasks/jetDsSpecSubs.cxx +++ b/PWGJE/Tasks/jetDsSpecSubs.cxx @@ -52,10 +52,11 @@ consteval float getValFromBin(int bin) enum BinExpColCntr { AllCollisions = 1, Sel8ZCut = 2 }; -enum BinMCColCntr { AllMCCollisions = 1, - SelectedMCCollisions = 2, - AssociatedRecoCollisions = 3, - SelectedAssociatedRecoCollisions = 4 +enum BinMCColCntr { + AllMCCollisions = 1, + EventSelection = 2, + VertexZCut = 3, + HasMCPDsJet = 4 }; enum BinMCJetCntr { @@ -318,13 +319,20 @@ struct JetDsSpecSubs { // Detector-level sparse histograms registry.add( "hSparse_ds_mcd1", - ";m_{D_{S}}^{rec};#it{p}_{T,D_{S}}^{det};#it{p}_{T,jet}^{det};z^{D_{S},jet}_{||,det};Origin(D_{S});Matching status", + ";m_{D_{S}}^{rec};" + "#it{p}_{T,D_{S}}^{det};" + "#it{p}_{T,jet}^{det};" + "z^{D_{S},jet}_{||,det};" + "Origin(D_{S});" + "flagMcMatchRec;" + "Jet matching status", {HistType::kTHnSparseF, {{350, 1.6, 2.3}, {100, 0., 50.}, {100, 0., 100.}, {200, 0., 1.}, {3, -0.5, 2.5}, + {21, -10.5, 10.5}, {2, -0.5, 1.5}}}); registry.add( @@ -356,13 +364,20 @@ struct JetDsSpecSubs { // Particle-level sparse with origin and matching status registry.add( "hSparse_ds_mcp", - ";#it{p}_{T,D_{S}}^{part};#it{p}_{T,jet}^{part};z^{D_{S},jet}_{||,part};#DeltaR_{D_{S},jet}^{part};Origin(D_{S});Matching status", + ";#it{p}_{T,D_{S}}^{part};" + "#it{p}_{T,jet}^{part};" + "z^{D_{S},jet}_{||,part};" + "#DeltaR_{D_{S},jet}^{part};" + "Origin(D_{S});" + "flagMcMatchGen;" + "Jet matching status", {HistType::kTHnSparseF, {{100, 0., 50.}, {100, 0., 100.}, {200, 0., 1.}, {200, 0., 1.}, {3, -0.5, 2.5}, + {21, -10.5, 10.5}, {2, -0.5, 1.5}}}); // Matched detector-level Ds-tagged jets @@ -418,10 +433,10 @@ struct JetDsSpecSubs { // Counter labels auto mcCollisionCounter = registry.get(HIST("hMCColCounter")); - mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AllMCCollisions, "All MC coll."); - mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::SelectedMCCollisions, "Selected MC coll."); - mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AssociatedRecoCollisions, "MC-associated reco coll."); - mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::SelectedAssociatedRecoCollisions, "Selected MC-associated reco coll."); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AllMCCollisions, "All MC collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::EventSelection, "Event selection"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::VertexZCut, "|z_{vtx}| < cut"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::HasMCPDsJet, "Has MCP D_{s}-jet"); auto jetCounter = registry.get(HIST("hMCJetCounter")); jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MCPJets, "MCP Ds-jets"); @@ -432,12 +447,12 @@ struct JetDsSpecSubs { // Matching-status labels auto hSparseMCD = registry.get(HIST("hSparse_ds_mcd1")); - hSparseMCD->GetAxis(5)->SetBinLabel(1, "Unmatched"); - hSparseMCD->GetAxis(5)->SetBinLabel(2, "Matched"); + hSparseMCD->GetAxis(6)->SetBinLabel(1, "Unmatched"); + hSparseMCD->GetAxis(6)->SetBinLabel(2, "Matched"); auto hSparseMCP = registry.get(HIST("hSparse_ds_mcp")); - hSparseMCP->GetAxis(5)->SetBinLabel(1, "Unmatched"); - hSparseMCP->GetAxis(5)->SetBinLabel(2, "Matched"); + hSparseMCP->GetAxis(6)->SetBinLabel(1, "Unmatched"); + hSparseMCP->GetAxis(6)->SetBinLabel(2, "Matched"); } void addMCPOnTheFlyHistograms() @@ -755,17 +770,27 @@ struct JetDsSpecSubs { // All MC collisions registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::AllMCCollisions)); - // MC collision selection - if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || - !(std::abs(mccollision.posZ()) < vertexZCut)) { + // Event selection + if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits)) { continue; } - // Selected MC collisions - registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::SelectedMCCollisions)); + + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::EventSelection)); + + // Vertex-z selection + if (std::abs(mccollision.posZ()) >= vertexZCut) { + continue; + } + + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::VertexZCut)); // Particle-level jets belonging to the current MC collision const auto mcpJetsPerMCCollision = mcpjets.sliceBy(MCPJetsPerMCCollisionPreslice, mccollision.globalIndex()); + if (mcpJetsPerMCCollision.size() > 0) { + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::HasMCPDsJet)); + } + // Particle-level Ds-tagged jets for (const auto& mcpjet : mcpJetsPerMCCollision) { @@ -806,6 +831,7 @@ struct JetDsSpecSubs { zParallelMCP, deltaRMCP, originMCP, + flagMcMatchGen, static_cast(isMatchedMCP)); // Store all particle-level Ds-tagged jets @@ -984,7 +1010,7 @@ struct JetDsSpecSubs { registry.fill(HIST("h_ds_phi_mcd"), mcdcand.phi()); registry.fill(HIST("h_ds_mass_mcd"), mcdcand.m()); - // Detector-level sparse: mass, pT Ds, pT jet, z_parallel, origin, matching status + // Detector-level sparse: mass, pT Ds, pT jet, z_parallel, origin, flagMcMatchRec, matching status registry.fill( HIST("hSparse_ds_mcd1"), mcdcand.m(), @@ -992,6 +1018,7 @@ struct JetDsSpecSubs { mcdjet.pt(), zParallelMCD, originMCD, + flagMcMatchRec, static_cast(isMatchedMCD)); // Detector-level sparse: pT Ds, pT jet, DeltaR From 5591daaac59367ec77f5e701d6f5a75bd9f3c18d Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:55:14 +0200 Subject: [PATCH 033/648] [PWGLF] Add doClosureTestPureMC Configurable in hStrangeCorrelation.cxx (#18008) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 126 ++++++++++-------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 0f0a1220082..587d5ba73d5 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -125,6 +125,7 @@ struct HStrangeCorrelation { Configurable doCorrelationOmegaPlus{"doCorrelationOmegaPlus", false, "do OmegaPlus correlation"}; Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; + Configurable doClosureTestPureMC{"doClosureTestPureMC", false, "fill regular ClosureTest histograms without event or reconstructed-trigger selection, using MC vertex z and centrality 0.05; keep truth-particle selections"}; Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; Configurable selectINELgtONE{"selectINELgtONE", false, "select INEL>1 events (at least 2 charged particles in |eta| < 1)"}; Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; @@ -6181,71 +6182,82 @@ struct HStrangeCorrelation { float bestCollisionCentpercentile = -1; float bestCollisionVtxZ = 0.0f; - bool bestCollisionSel8 = false; - bool bestCollisionINELgtZERO = false; - bool bestCollisionINELgtONE = false; - bool bestCollisionNoSameBunchPileup = false; - bool bestCollisionGoodTriggerTVX = false; - bool bestCollisionGoodZvtxFT0vsPV = false; - bool isCollisionSelect = false; - int biggestNContribs = -1; - uint32_t bestCollisionTriggerPresenceMap = 0; + // Pure MC defines an inclusive generated-event reference, independent of + // reconstructed collisions and triggerPresenceMap. Particle/pair selections + // below still define the observable. PairLossK0 diagnostics above retain + // their own event selection. + if (masterConfigurations.doClosureTestPureMC) { + bestCollisionCentpercentile = 0.05f; + bestCollisionVtxZ = mcCollision.posZ(); + } else { + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + bool bestCollisionINELgtONE = false; + bool bestCollisionNoSameBunchPileup = false; + bool bestCollisionGoodTriggerTVX = false; + bool bestCollisionGoodZvtxFT0vsPV = false; + bool isCollisionSelect = false; + int biggestNContribs = -1; + uint32_t bestCollisionTriggerPresenceMap = 0; - for (auto const& recCollision : recCollisions) { - if (biggestNContribs < recCollision.numContrib()) { - biggestNContribs = recCollision.numContrib(); - bestCollisionCentpercentile = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); - if (masterConfigurations.applyNewMCSelection) { - isCollisionSelect = ((masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false))); - } else { - bestCollisionSel8 = recCollision.sel8(); + for (auto const& recCollision : recCollisions) { + if (biggestNContribs < recCollision.numContrib()) { + biggestNContribs = recCollision.numContrib(); + bestCollisionCentpercentile = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); + // Both branches fill the generated-level THns with this vertex position, so it + // is taken from the best collision regardless of which event selection is used. bestCollisionVtxZ = recCollision.posZ(); - bestCollisionINELgtZERO = recCollision.isInelGt0(); - bestCollisionINELgtONE = recCollision.isInelGt1(); - bestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); - bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); - bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); - } - if (triggerPresenceMap.size() > 0) { - bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + if (masterConfigurations.applyNewMCSelection) { + isCollisionSelect = ((masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false))); + } else { + bestCollisionSel8 = recCollision.sel8(); + bestCollisionINELgtZERO = recCollision.isInelGt0(); + bestCollisionINELgtONE = recCollision.isInelGt1(); + bestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); + bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + } + if (triggerPresenceMap.size() > 0) { + bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + } } } - } - // ________________________________________________ - // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { - return; - } - - if (masterConfigurations.applyNewMCSelection) { - if (!isCollisionSelect) { + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } - } else { - if (masterConfigurations.doGenEventSelection) { - if (!bestCollisionSel8) { - return; - } - if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) { - return; - } - if (!bestCollisionINELgtZERO) { - return; - } - if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { - return; - } - if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { - return; - } - if (masterConfigurations.requireGoodTriggerTVX && !bestCollisionGoodTriggerTVX) { - return; - } - if (masterConfigurations.requireGoodZvtxFT0vsPV && !bestCollisionGoodZvtxFT0vsPV) { + + if (masterConfigurations.applyNewMCSelection) { + if (!isCollisionSelect) { return; } - if (bestCollisionCentpercentile > axisRanges[5][1] || bestCollisionCentpercentile < axisRanges[5][0]) { - return; + } else { + if (masterConfigurations.doGenEventSelection) { + if (!bestCollisionSel8) { + return; + } + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) { + return; + } + if (!bestCollisionINELgtZERO) { + return; + } + if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { + return; + } + if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { + return; + } + if (masterConfigurations.requireGoodTriggerTVX && !bestCollisionGoodTriggerTVX) { + return; + } + if (masterConfigurations.requireGoodZvtxFT0vsPV && !bestCollisionGoodZvtxFT0vsPV) { + return; + } + if (bestCollisionCentpercentile > axisRanges[5][1] || bestCollisionCentpercentile < axisRanges[5][0]) { + return; + } } } } From 237c3ed7cefa25f0a2f79d81db1add5fc6c557d9 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Wed, 23 Sep 2026 02:00:43 +0800 Subject: [PATCH 034/648] [PWGCF] Fix impact parameter bug and other improvements according to code check (#18006) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index b676b66994d..9e32c7d2a4f 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -43,6 +43,7 @@ #include +#include #include #include #include @@ -187,37 +188,37 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { - bool fProcess[eProcess_N] = {false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. - bool fDryRun = false; // book all histos and run without filling and calculating anything - } tc; // you have to prepend "tc." for all objects name in this group later in the code + std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + bool fDryRun = false; // book all histos and run without filling and calculating anything + } tc; // you have to prepend "tc." for all objects name in this group later in the code // **) Particle histograms: struct ParticleHistograms { - TList* fParticleHistogramsList = NULL; //!, 2>, eParticleHistograms_N> fParticleHistograms{}; } pc; // you have to prepend "pc." for all objects name in this group later in the code // *) Event histograms: struct EventHistograms { - TList* fEventHistogramsList = NULL; //!, 2>, eEventHistograms_N> fEventHistograms{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] + } ec; // prepend "ec." for event counters // *) External histograms: struct ExternalHistograms { - TList* fExternalHistogramsList = NULL; - TH1D* fhistWeights = NULL; + TList* fExternalHistogramsList = nullptr; + TH1D* fhistWeights = nullptr; } ex; struct Observables { - TList* fObservablesList = NULL; - TProfile* fProfTwo[2][2] = {{NULL}}; //! [reco,sim][before, after event cuts] + TList* fObservablesList = nullptr; + std::array, 2> fProfTwo{}; //! [reco,sim][before, after event cuts] } obs; // *) Quality assurance histograms: struct QualityAssurance { - TList* fQualityAssuranceList = NULL; //!(tracks.size()); - auto impactParameter = thisMCCollision.impactParameter() * 1e15; + auto impactParameter = thisMCCollision.impactParameter(); LOGF(info, "Reco collision = %d, MC collision = %d, b = %f", collision.globalIndex(), From 018e1f172c386778a191cfaa3e0715f260744198 Mon Sep 17 00:00:00 2001 From: Fan Si <147278151+fsii@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:05:30 +0200 Subject: [PATCH 035/648] [PWGCF] Update histograms and tables (#17995) --- PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx | 1105 +++++++++--------- 1 file changed, 577 insertions(+), 528 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx index 7226779f377..fe441f712d1 100644 --- a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx @@ -47,21 +47,24 @@ #include #include #include -#include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -113,29 +116,29 @@ using MiniMcParticle = MiniMcParticles::iterator; namespace mini_track { DECLARE_SOA_INDEX_COLUMN(MiniCollision, miniCollision); -DECLARE_SOA_COLUMN(Code, code, std::uint32_t); +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint16_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); } // namespace mini_track -DECLARE_SOA_TABLE(MiniTracks, "AOD", "MINITRACK", soa::Index<>, mini_track::MiniCollisionId, mini_track::Code); +DECLARE_SOA_TABLE(MiniTracks, "AOD", "MINITRACK", soa::Index<>, mini_track::MiniCollisionId, mini_track::CodeLow, mini_track::CodeHigh); using MiniTrack = MiniTracks::iterator; namespace tiny_mc_collision { -DECLARE_SOA_COLUMN(NMcParticlesP, nMcParticlesP, std::uint16_t); -DECLARE_SOA_COLUMN(NMcParticlesM, nMcParticlesM, NMcParticlesP::type); +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint16_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); } // namespace tiny_mc_collision -DECLARE_SOA_TABLE(TinyMcCollisions, "AOD", "TINYMCCOLLISION", soa::Index<>, tiny_mc_collision::NMcParticlesP, tiny_mc_collision::NMcParticlesM); +DECLARE_SOA_TABLE(TinyMcCollisions, "AOD", "TINYMCCOLLISION", soa::Index<>, tiny_mc_collision::CodeLow, tiny_mc_collision::CodeHigh); using TinyMcCollision = TinyMcCollisions::iterator; namespace tiny_collision { -DECLARE_SOA_COLUMN(Code, code, mini_collision::Code::type); -DECLARE_SOA_COLUMN(NTracksP, nTracksP, std::uint16_t); -DECLARE_SOA_COLUMN(NTracksM, nTracksM, NTracksP::type); +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint32_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); } // namespace tiny_collision -DECLARE_SOA_TABLE(TinyCollisions, "AOD", "TINYCOLLISION", soa::Index<>, tiny_collision::Code, tiny_collision::NTracksP, tiny_collision::NTracksM); +DECLARE_SOA_TABLE(TinyCollisions, "AOD", "TINYCOLLISION", soa::Index<>, tiny_collision::CodeLow, tiny_collision::CodeHigh); using TinyCollision = TinyCollisions::iterator; namespace tiny_mc_particle @@ -161,72 +164,69 @@ namespace { namespace fluctuation_calculator_base { -inline constexpr std::int8_t MaxOrder{8}; -inline constexpr std::int32_t NExponentKeys{MaxOrder * (MaxOrder + 1) / 2}; -inline constexpr std::array, NExponentKeys> ExponentKeys{[]() consteval noexcept -> std::array, NExponentKeys> { +constexpr std::int8_t OrderMax{8}; +constexpr std::int32_t NExponentKeys{OrderMax * (OrderMax + 1) / 2}; +constexpr std::array, NExponentKeys> ExponentKeys{[]() consteval noexcept -> std::array, NExponentKeys> { std::array, NExponentKeys> result{}; std::int32_t index{}; - for (std::int32_t const& iExponent : std::views::iota(1, MaxOrder + 1)) { + for (std::int32_t const& iExponent : std::views::iota(1, OrderMax + 1)) { for (std::int32_t const& jExponent : std::views::iota(1, iExponent + 1)) { result[index++] = {static_cast(iExponent), static_cast(jExponent)}; } } return result; }()}; -inline constexpr std::int32_t NOrderKeys{[]() consteval noexcept -> std::int32_t { - std::array counts{1}; +constexpr std::int32_t NOrderKeys{[]() consteval noexcept -> std::int32_t { + std::array counts{1}; for (std::pair const& exponentKey /* o2-linter: disable=const-ref-in-for-loop */ : ExponentKeys) { const std::int32_t weight{exponentKey.first}; - for (std::int32_t const& sum : std::views::iota(weight, MaxOrder + 1)) { + for (std::int32_t const& sum : std::views::iota(weight, OrderMax + 1)) { counts[sum] += counts[sum - weight]; } } return std::accumulate(counts.begin(), counts.end(), 0); }()}; -inline constexpr std::array, NOrderKeys> OrderKeys{[]() consteval noexcept -> std::array, NOrderKeys> { - constexpr auto FillOrderKeys{[](const auto& self, std::array& current, std::int32_t& index, std::array, NOrderKeys>& result, const std::int32_t position, const std::int32_t sum, const std::int32_t target) consteval noexcept -> void { - if (position == NExponentKeys) { - if (sum == target) { - result[index++] = current; - } +constexpr std::array, NOrderKeys> OrderKeys{[]() consteval noexcept -> std::array, NOrderKeys> { + constexpr auto FillOrderKeys{[](const auto& self, const std::span& current, std::int32_t& index, const std::span, NOrderKeys>& output, const std::int32_t indexPosition, const std::int32_t sum, const std::int32_t target) consteval noexcept -> void { + if (std::cmp_equal(sum, target)) { + std::ranges::fill(std::views::drop(current, indexPosition), 0); + std::ranges::copy(current, output[index++].begin()); + return; + } + if (std::cmp_equal(indexPosition, NExponentKeys)) { return; } - const std::int32_t weight{ExponentKeys[position].first}; + const std::int32_t weight{ExponentKeys[indexPosition].first}; + if (std::cmp_greater(weight, target - sum)) { + return; + } for (std::int32_t const& power : std::views::iota(0, (target - sum) / weight + 1)) { - current[position] = static_cast(power); - self(self, current, index, result, position + 1, sum + power * weight, target); + current[indexPosition] = static_cast(power); + self(self, current, index, output, indexPosition + 1, sum + power * weight, target); } }}; std::array, NOrderKeys> result{}; std::array current{}; std::int32_t index{}; - for (std::int32_t const& target : std::views::iota(0, MaxOrder + 1)) { + for (std::int32_t const& target : std::views::iota(0, OrderMax + 1)) { FillOrderKeys(FillOrderKeys, current, index, result, 0, 0, target); } return result; }()}; -inline constexpr std::array>, NOrderKeys> OrderKeyProductLinks{[]() consteval noexcept -> std::array>, NOrderKeys> { - std::array>, NOrderKeys> result{}; +constexpr std::array, NOrderKeys> OrderKeyProductLinks{[]() consteval noexcept -> std::array, NOrderKeys> { + std::array, NOrderKeys> result{}; for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys)) { - result[iOrderKey].first = iOrderKey; + result[iOrderKey].first = std::inner_product(OrderKeys[iOrderKey].begin(), OrderKeys[iOrderKey].end(), ExponentKeys.begin(), 0, std::plus{}, [](const std::int8_t power, const std::pair& exponentKey) consteval noexcept -> std::int32_t { return power * exponentKey.first; }); + } + for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys) | std::views::reverse) { std::array orderKeyParent{OrderKeys[iOrderKey]}; - for (std::int32_t const& iExponentKey : std::views::iota(0, NExponentKeys) | std::views::reverse) { - const std::int8_t power{orderKeyParent[iExponentKey]}; - if (power <= 0) { - continue; - } - - orderKeyParent[iExponentKey] = {}; - for (std::int32_t const& iOrderKeyParent : std::views::iota(0, iOrderKey)) { - if (OrderKeys[iOrderKeyParent] == orderKeyParent) { - result[iOrderKey] = {iOrderKeyParent, {iExponentKey, power}}; - break; - } - } - break; - } + const std::int32_t indexExponentKey{NExponentKeys - 1 - static_cast(std::ranges::find_if_not(std::views::reverse(orderKeyParent), std::logical_not{}) - orderKeyParent.rbegin())}; + --orderKeyParent[indexExponentKey]; + const std::int32_t order{result[iOrderKey].first - ExponentKeys[indexExponentKey].first}; + const std::ranges::subrange, NOrderKeys>::iterator> orderKeyParents{std::ranges::equal_range(std::views::take(result, iOrderKey), order, {}, &std::pair::first)}; + result[iOrderKey] = {static_cast(std::ranges::lower_bound(std::views::take(OrderKeys, orderKeyParents.end() - result.begin()) | std::views::drop(orderKeyParents.begin() - result.begin()), orderKeyParent) - OrderKeys.begin()), indexExponentKey}; } return result; }()}; @@ -235,21 +235,17 @@ static_assert([]() consteval noexcept -> bool { return false; } for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys)) { - const auto& [iOrderKeyParent, factor]{OrderKeyProductLinks[iOrderKey]}; - const auto& [iExponentKey, power]{factor}; - if (iOrderKeyParent < 0 || iOrderKeyParent >= iOrderKey || iExponentKey < 0 || iExponentKey >= NExponentKeys || power <= 0) { + const auto& [indexOrderKeyParent, indexExponentKey]{OrderKeyProductLinks[iOrderKey]}; + if (std::cmp_less(indexOrderKeyParent, 0) || std::cmp_greater_equal(indexOrderKeyParent, iOrderKey) || std::cmp_less(indexExponentKey, 0) || std::cmp_greater_equal(indexExponentKey, NExponentKeys)) { return false; } - for (std::int32_t const& iExponentKeyAfter : std::views::iota(iExponentKey + 1, NExponentKeys)) { - if (OrderKeys[iOrderKey][iExponentKeyAfter] > 0) { + for (std::int32_t const& iExponentKeyAfter : std::views::iota(indexExponentKey + 1, NExponentKeys)) { + if (std::cmp_greater(OrderKeys[iOrderKey][iExponentKeyAfter], 0)) { return false; } } - std::array orderKeyReconstructed{OrderKeys[iOrderKeyParent]}; - if (orderKeyReconstructed[iExponentKey] != 0) { - return false; - } - orderKeyReconstructed[iExponentKey] = power; + std::array orderKeyReconstructed{OrderKeys[indexOrderKeyParent]}; + ++orderKeyReconstructed[indexExponentKey]; if (orderKeyReconstructed != OrderKeys[iOrderKey]) { return false; } @@ -261,49 +257,55 @@ static_assert([]() consteval noexcept -> bool { class FluctuationCalculatorTrack { public: - FluctuationCalculatorTrack() noexcept = default; - FluctuationCalculatorTrack(const FluctuationCalculatorTrack&) noexcept = default; - FluctuationCalculatorTrack(FluctuationCalculatorTrack&&) noexcept = default; - FluctuationCalculatorTrack& operator=(const FluctuationCalculatorTrack&) noexcept = default; - FluctuationCalculatorTrack& operator=(FluctuationCalculatorTrack&&) noexcept = default; - virtual ~FluctuationCalculatorTrack() noexcept = default; - - [[nodiscard]] std::array getProducts(const double weight = 1.) const noexcept + constexpr FluctuationCalculatorTrack() noexcept = default; + constexpr FluctuationCalculatorTrack(const FluctuationCalculatorTrack&) noexcept = default; + constexpr FluctuationCalculatorTrack(FluctuationCalculatorTrack&&) noexcept = default; + constexpr FluctuationCalculatorTrack& operator=(const FluctuationCalculatorTrack&) noexcept = default; + constexpr FluctuationCalculatorTrack& operator=(FluctuationCalculatorTrack&&) noexcept = default; + virtual constexpr ~FluctuationCalculatorTrack() noexcept = default; + + [[nodiscard]] constexpr double getQ(const std::int32_t indexExponentKey) const noexcept { return mQs[indexExponentKey]; } + [[nodiscard]] constexpr const std::array& getQs() const noexcept { return mQs; } + [[nodiscard]] constexpr std::array getProducts(const double weight = 1.) const noexcept { - std::array, fluctuation_calculator_base::NExponentKeys> powersQ{}; - for (std::int32_t const& iExponentKey : std::views::iota(0, fluctuation_calculator_base::NExponentKeys)) { - powersQ[iExponentKey][1] = mQs[iExponentKey]; - for (std::int32_t const& power : std::views::iota(2, fluctuation_calculator_base::MaxOrder / fluctuation_calculator_base::ExponentKeys[iExponentKey].first + 1)) { - powersQ[iExponentKey][power] = powersQ[iExponentKey][power - 1] * mQs[iExponentKey]; - } - } - std::array products{weight}; for (std::int32_t const& iOrderKey : std::views::iota(1, fluctuation_calculator_base::NOrderKeys)) { - const auto& [iOrderKeyParent, factor]{fluctuation_calculator_base::OrderKeyProductLinks[iOrderKey]}; - const auto& [iExponentKey, power]{factor}; - products[iOrderKey] = products[iOrderKeyParent] * powersQ[iExponentKey][power]; + const auto& [indexOrderKeyParent, indexExponentKey]{fluctuation_calculator_base::OrderKeyProductLinks[iOrderKey]}; + products[iOrderKey] = products[indexOrderKeyParent] * mQs[indexExponentKey]; } return products; } - void addQ(const std::int32_t exponentKeyIndex, const double q) noexcept { mQs[exponentKeyIndex] += q; } - void clear() noexcept { mQs.fill({}); } - void fill(const double charge, const double efficiency, const double weight = 1.) noexcept + constexpr void setQ(const std::int32_t indexExponentKey, const double q) noexcept { mQs[indexExponentKey] = q; } + constexpr void setQs(const std::span& qs) noexcept { - const double inverseEfficiency{1. / efficiency}; - std::array powersCharge{weight}; - std::array powersInverseEfficiency{1.}; - for (std::int32_t const& exponent : std::views::iota(1, fluctuation_calculator_base::MaxOrder + 1)) { - powersCharge[exponent] = powersCharge[exponent - 1] * charge; - powersInverseEfficiency[exponent] = powersInverseEfficiency[exponent - 1] * inverseEfficiency; + if (qs.data() != mQs.data()) { + std::ranges::copy(qs, mQs.begin()); } + } + constexpr void addQ(const std::int32_t indexExponentKey, const double q) noexcept { mQs[indexExponentKey] += q; } + constexpr void addQs(const std::span& qs) noexcept + { for (std::int32_t const& iExponentKey : std::views::iota(0, fluctuation_calculator_base::NExponentKeys)) { - const auto& [exponentCharge, exponentEfficiency]{fluctuation_calculator_base::ExponentKeys[iExponentKey]}; - addQ(iExponentKey, powersCharge[exponentCharge] * powersInverseEfficiency[exponentEfficiency]); + mQs[iExponentKey] += qs[iExponentKey]; + } + } + constexpr void clear() noexcept { mQs.fill({}); } + constexpr void fill(const double charge, const double efficiency, const double weight = 1.) noexcept + { + const double efficiencyInverse{1. / efficiency}; + double powerCharge{weight}; + std::int32_t indexExponentKey{}; + for (std::int32_t const& exponentCharge : std::views::iota(1, fluctuation_calculator_base::OrderMax + 1)) { + powerCharge *= charge; + double powerEfficiencyInverse{powerCharge}; + for ([[maybe_unused]] std::int32_t const& exponentEfficiency : std::views::iota(1, exponentCharge + 1)) { + powerEfficiencyInverse *= efficiencyInverse; + addQ(indexExponentKey++, powerEfficiencyInverse); + } } } - protected: + private: std::array mQs{}; }; @@ -514,7 +516,7 @@ constexpr std::string_view getName(const std::int32_t index) return EnumInfo>::DisplayNames.at(index); } else if constexpr (NameKindValue == NameKind::DisplayLower) { return EnumInfo>::DisplayNamesLower.at(index); - } else { + } else { // NameKindValue == NameKind::Default return EnumInfo>::Names.at(index); } } @@ -526,7 +528,7 @@ constexpr std::string_view getName(const E e) } template requires IsValidEnum -std::vector getDisplayNames() +constexpr std::vector getDisplayNames() { if constexpr (requires { EnumInfo>::DisplayNames; }) { return {EnumInfo>::DisplayNames.begin(), EnumInfo>::DisplayNames.end()}; @@ -567,12 +569,15 @@ constexpr double getMass(const ParticleSpecies particleSpecies) noexcept namespace mini_collision_codec { -inline constexpr std::int32_t NBinsVz{40}; -inline constexpr std::array> RangeVz{-10., 10.}; -inline constexpr std::int32_t NBinsCentrality{550}; -inline constexpr std::array BinEdgesCentrality{[]() consteval noexcept -> std::array { +constexpr std::int32_t NBinsVz{40}; +constexpr std::array> RangeVz{-10., 10.}; +constexpr std::int32_t NBinsCentrality{550}; +constexpr std::array BinEdgesCentrality{[]() consteval noexcept -> std::array { constexpr std::array EdgesSelection{0., 0.001, 0.01, 0.1, 1., 10., 100.}; + static_assert(std::ranges::adjacent_find(EdgesSelection, std::greater_equal{}) == EdgesSelection.end()); constexpr std::array NBinsPerSection{100, 90, 90, 90, 90, 90}; + static_assert(std::cmp_equal(std::accumulate(NBinsPerSection.begin(), NBinsPerSection.end(), 0), NBinsCentrality)); + std::array edges{}; std::int32_t index{}; edges[index++] = EdgesSelection.front(); @@ -584,78 +589,125 @@ inline constexpr std::array BinEdgesCentrality{[]() } return edges; }()}; -inline constexpr std::int32_t NStatesGoodNPvContributors{2}; -static_assert(static_cast(NBinsVz) * NBinsCentrality * NStatesGoodNPvContributors <= static_cast(std::numeric_limits::max()) + 1); +constexpr std::int32_t NStatesGoodNPvContributors{2}; +static_assert(std::cmp_less_equal(static_cast(NBinsVz) * NBinsCentrality * NStatesGoodNPvContributors, static_cast(std::numeric_limits::max()) + 1)); std::optional encode(const double vz, const double centrality, const bool isGoodNPvContributors) noexcept { - if (!std::isfinite(vz) || !std::isfinite(centrality) || vz < RangeVz[toI(RangeEdge::Min)] || RangeVz[toI(RangeEdge::Max)] <= vz || centrality < BinEdgesCentrality.front() || BinEdgesCentrality.back() <= centrality) { + if (!(RangeVz[toI(RangeEdge::Min)] <= vz) || !(vz < RangeVz[toI(RangeEdge::Max)]) || !(BinEdgesCentrality.front() <= centrality) || !(centrality < BinEdgesCentrality.back())) { return std::nullopt; } - const std::int32_t vzBinIndex{static_cast(std::floor((vz - RangeVz[toI(RangeEdge::Min)]) / ((RangeVz[toI(RangeEdge::Max)] - RangeVz[toI(RangeEdge::Min)]) / NBinsVz)))}; - if (vzBinIndex < 0 || NBinsVz <= vzBinIndex) { + const std::int32_t indexBinVz{static_cast(std::floor((vz - RangeVz[toI(RangeEdge::Min)]) / ((RangeVz[toI(RangeEdge::Max)] - RangeVz[toI(RangeEdge::Min)]) / NBinsVz)))}; + if (std::cmp_less(indexBinVz, 0) || std::cmp_greater_equal(indexBinVz, NBinsVz)) { return std::nullopt; } - aod::mini_collision::Code::type code{static_cast(vzBinIndex)}; - aod::mini_collision::Code::type codeSpan{NBinsVz}; - code += (static_cast(std::ranges::upper_bound(BinEdgesCentrality, centrality) - BinEdgesCentrality.begin()) - 1) * codeSpan; - codeSpan *= NBinsCentrality; - code += static_cast(isGoodNPvContributors) * codeSpan; + aod::mini_collision::Code::type code{static_cast(indexBinVz)}; + code = code * NBinsCentrality + static_cast(std::ranges::upper_bound(BinEdgesCentrality, centrality) - BinEdgesCentrality.begin()) - 1; + code = code * NStatesGoodNPvContributors + static_cast(isGoodNPvContributors); return code; } } // namespace mini_collision_codec +namespace tiny_collision_codec +{ +constexpr std::int32_t NStatesNTracks{4096}; +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(mini_collision_codec::NBinsVz) * mini_collision_codec::NBinsCentrality * NStatesNTracks * NStatesNTracks - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); + +std::optional> encode(const double vz, const double centrality, const std::span>& nsTracks) noexcept +{ + if (!(mini_collision_codec::RangeVz[toI(RangeEdge::Min)] <= vz) || !(vz < mini_collision_codec::RangeVz[toI(RangeEdge::Max)]) || !(mini_collision_codec::BinEdgesCentrality.front() <= centrality) || !(centrality < mini_collision_codec::BinEdgesCentrality.back())) { + return std::nullopt; + } + + const std::int32_t indexBinVz{static_cast(std::floor((vz - mini_collision_codec::RangeVz[toI(RangeEdge::Min)]) / ((mini_collision_codec::RangeVz[toI(RangeEdge::Max)] - mini_collision_codec::RangeVz[toI(RangeEdge::Min)]) / mini_collision_codec::NBinsVz)))}; + if (std::cmp_less(indexBinVz, 0) || std::cmp_greater_equal(indexBinVz, mini_collision_codec::NBinsVz)) { + return std::nullopt; + } + + std::uint64_t code{static_cast(indexBinVz)}; + code = code * mini_collision_codec::NBinsCentrality + static_cast(std::ranges::upper_bound(mini_collision_codec::BinEdgesCentrality, centrality) - mini_collision_codec::BinEdgesCentrality.begin()) - 1; + for (std::uint16_t const& nTracks : nsTracks) { + if (std::cmp_greater_equal(nTracks, NStatesNTracks)) { + LOG(fatal) << "Invalid number of tracks " << nTracks << ": must be less than " << NStatesNTracks << "!"; + } + + code = code * NStatesNTracks + nTracks; + } + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; +} +} // namespace tiny_collision_codec + +namespace tiny_mc_collision_codec +{ +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(tiny_collision_codec::NStatesNTracks) * tiny_collision_codec::NStatesNTracks - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); + +std::tuple encode(const std::span>& nsMcParticles) noexcept +{ + std::uint64_t code{}; + for (std::uint16_t const& nMcParticles : nsMcParticles) { + if (std::cmp_greater_equal(nMcParticles, tiny_collision_codec::NStatesNTracks)) { + LOG(fatal) << "Invalid number of MC particles " << nMcParticles << ": must be less than " << tiny_collision_codec::NStatesNTracks << "!"; + } + + code = code * tiny_collision_codec::NStatesNTracks + nMcParticles; + } + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; +} +} // namespace tiny_mc_collision_codec + namespace mini_track_codec { -inline constexpr std::int32_t NStatesPvContributor{2}; -inline constexpr std::int32_t NBinsPt{18}; -inline constexpr std::int32_t NBinsEta{16}; -inline constexpr std::array> RangePt{0.2, 2.}; -inline constexpr std::array> RangeEta{-0.8, 0.8}; -static_assert(static_cast(NStatesPvContributor) * NEs * NEs * NEs * NEs * NEs * NEs * NEs * NEs * NBinsPt * NBinsEta * NEs * NEs * NEs <= static_cast(std::numeric_limits::max()) + 1); +constexpr std::int32_t NStatesPvContributor{2}; +constexpr std::int32_t NBinsPt{18}; +constexpr std::int32_t NBinsEta{16}; +constexpr std::array> RangePt{0.2, 2.}; +constexpr std::array> RangeEta{-0.8, 0.8}; +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(NStatesPvContributor) * NEs * NEs * NEs * NEs * NEs * NEs * NBinsPt * NBinsEta * NEs * NEs * NEs - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); struct ConfigSelection { - std::array> minItsNCls{}; - std::array> maxItsChi2NCls{}; - std::array> minTpcNCls{}; - std::array> maxTpcChi2NCls{}; - std::array> minTpcNCrossedRows{}; - std::array> maxTpcNClsSharedRatio{}; - std::array>, NEs> maxAbsNSigmaDca{}; - std::array>, NEs> maxAbsNSigmaPid{}; + std::array> cutsMinItsNCls{}; + std::array> cutsMaxItsChi2NCls{}; + std::array> cutsMaxTpcChi2NCls{}; + std::array> cutsMaxTpcNClsSharedRatio{}; + std::array> cutsMinTpcNCrossedRows{}; + std::array>, NEs> cutsMaxAbsNSigmaDca{}; + std::array>, NEs> cutsMaxAbsNSigmaPid{}; }; template requires IsValidEnumValue -std::optional encode(const ConfigSelection& configSelection, const bool isPvContributor, const double itsNCls, const double itsChi2NCls, const double tpcNCls, const double tpcChi2NCls, const double tpcNCrossedRows, const double tpcNClsSharedRatio, const std::array>& absNSigmaDca, const double pt, const double eta, const std::int32_t sign, const double absNSigmaPid) noexcept +std::optional> encode(const ConfigSelection& configSelection, const bool isPvContributor, const double itsNCls, const double itsChi2NCls, const double tpcChi2NCls, const double tpcNClsSharedRatio, const double tpcNCrossedRows, const std::span>& absNsSigmaDca, const double pt, const double eta, const std::int32_t sign, const double absNSigmaPid) noexcept { - if (!std::isfinite(pt) || !std::isfinite(eta) || pt < RangePt[toI(RangeEdge::Min)] || RangePt[toI(RangeEdge::Max)] <= pt || eta < RangeEta[toI(RangeEdge::Min)] || RangeEta[toI(RangeEdge::Max)] <= eta) { + if (!(RangePt[toI(RangeEdge::Min)] <= pt) || !(pt < RangePt[toI(RangeEdge::Max)]) || !(RangeEta[toI(RangeEdge::Min)] <= eta) || !(eta < RangeEta[toI(RangeEdge::Max)])) { return std::nullopt; } - const std::int32_t ptBinIndex{static_cast(std::floor((pt - RangePt[toI(RangeEdge::Min)]) / ((RangePt[toI(RangeEdge::Max)] - RangePt[toI(RangeEdge::Min)]) / NBinsPt)))}; - if (ptBinIndex < 0 || NBinsPt <= ptBinIndex) { + const std::int32_t indexBinPt{static_cast(std::floor((pt - RangePt[toI(RangeEdge::Min)]) / ((RangePt[toI(RangeEdge::Max)] - RangePt[toI(RangeEdge::Min)]) / NBinsPt)))}; + if (std::cmp_less(indexBinPt, 0) || std::cmp_greater_equal(indexBinPt, NBinsPt)) { return std::nullopt; } - const std::int32_t etaBinIndex{static_cast(std::floor((eta - RangeEta[toI(RangeEdge::Min)]) / ((RangeEta[toI(RangeEdge::Max)] - RangeEta[toI(RangeEdge::Min)]) / NBinsEta)))}; - if (etaBinIndex < 0 || NBinsEta <= etaBinIndex) { + const std::int32_t indexBinEta{static_cast(std::floor((eta - RangeEta[toI(RangeEdge::Min)]) / ((RangeEta[toI(RangeEdge::Max)] - RangeEta[toI(RangeEdge::Min)]) / NBinsEta)))}; + if (std::cmp_less(indexBinEta, 0) || std::cmp_greater_equal(indexBinEta, NBinsEta)) { return std::nullopt; } static constexpr auto GetState{ [] requires IsValidEnumValue - (const double value, const std::array>& cuts) noexcept -> std::int32_t { + (const double input, const std::span>& cuts) constexpr noexcept -> std::int32_t { for (std::int32_t const& iSelection : std::views::iota(0, NEs) | std::views::reverse) { if constexpr (RangeEdgeValue == RangeEdge::Min) { - if (value > cuts[iSelection]) { + if (input > cuts[iSelection]) { return iSelection; } - } else { - if (value < cuts[iSelection]) { + } else { // RangeEdgeValue == RangeEdge::Max + if (input < cuts[iSelection]) { return iSelection; } } @@ -663,92 +715,81 @@ std::optional encode(const ConfigSelection& configS return -1; }}; - aod::mini_track::Code::type code{static_cast(isPvContributor)}; - aod::mini_track::Code::type codeSpan{NStatesPvContributor}; + std::uint64_t code{static_cast(isPvContributor)}; for (std::int32_t const& state : std::to_array( // cppcheck-suppress internalAstError - {GetState.template operator()(itsNCls, configSelection.minItsNCls), - GetState.template operator()(itsChi2NCls, configSelection.maxItsChi2NCls), - GetState.template operator()(tpcNCls, configSelection.minTpcNCls), - GetState.template operator()(tpcChi2NCls, configSelection.maxTpcChi2NCls), - GetState.template operator()(tpcNCrossedRows, configSelection.minTpcNCrossedRows), - GetState.template operator()(tpcNClsSharedRatio, configSelection.maxTpcNClsSharedRatio), - GetState.template operator()(absNSigmaDca[toI(DcaAxis::Xy)], configSelection.maxAbsNSigmaDca[toI(DcaAxis::Xy)]), - GetState.template operator()(absNSigmaDca[toI(DcaAxis::Z)], configSelection.maxAbsNSigmaDca[toI(DcaAxis::Z)])})) { - if (state < 0) { + {GetState.template operator()(itsNCls, configSelection.cutsMinItsNCls), + GetState.template operator()(itsChi2NCls, configSelection.cutsMaxItsChi2NCls), + GetState.template operator()(tpcChi2NCls, configSelection.cutsMaxTpcChi2NCls), + GetState.template operator()(tpcNClsSharedRatio, configSelection.cutsMaxTpcNClsSharedRatio), + GetState.template operator()(tpcNCrossedRows, configSelection.cutsMinTpcNCrossedRows), + std::ranges::min(std::views::iota(0, NEs) | std::views::transform([&absNsSigmaDca, &configSelection](const std::int32_t indexDcaAxis) constexpr noexcept -> std::int32_t { return GetState.template operator()(absNsSigmaDca[indexDcaAxis], configSelection.cutsMaxAbsNSigmaDca[indexDcaAxis]); }))})) { + if (std::cmp_less(state, 0)) { return std::nullopt; } - code += state * codeSpan; - codeSpan *= NEs; + code = code * NEs + state; } - code += ptBinIndex * codeSpan; - codeSpan *= NBinsPt; - code += etaBinIndex * codeSpan; - codeSpan *= NBinsEta; - code += (sign > 0 ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)) * codeSpan; - codeSpan *= NEs; - code += toI(ParticleSpeciesAllValue) * codeSpan; - codeSpan *= NEs; + code = code * NBinsPt + indexBinPt; + code = code * NBinsEta + indexBinEta; + code = code * NEs + (std::cmp_greater(sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)); + code = code * NEs + toI(ParticleSpeciesAllValue); if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { - // clang-format v20.1.3 - // clang-format off - const std::int32_t statePid{GetState.template operator()(absNSigmaPid, configSelection.maxAbsNSigmaPid[toI(getValue(ParticleSpeciesAllValue))])}; - // clang-format on - if (statePid < 0) { + const std::int32_t statePid = GetState.template operator()(absNSigmaPid, configSelection.cutsMaxAbsNSigmaPid[toI(getValue(ParticleSpeciesAllValue))]); + if (std::cmp_less(statePid, 0)) { return std::nullopt; } - code += statePid * codeSpan; + + code = code * NEs + statePid; + } else { // ParticleSpeciesAllValue == ParticleSpeciesAll::All + code *= NEs; } - return code; + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; } } // namespace mini_track_codec namespace mini_mc_particle_codec { -static_assert(static_cast(mini_track_codec::NBinsPt) * mini_track_codec::NBinsEta * NEs * NEs <= static_cast(std::numeric_limits::max()) + 1); +static_assert(std::cmp_less_equal(static_cast(mini_track_codec::NBinsPt) * mini_track_codec::NBinsEta * NEs * NEs, static_cast(std::numeric_limits::max()) + 1)); template requires IsValidEnumValue std::optional encode(const double pt, const double eta, const std::int32_t sign) noexcept { - if (!std::isfinite(pt) || !std::isfinite(eta) || pt < mini_track_codec::RangePt[toI(RangeEdge::Min)] || mini_track_codec::RangePt[toI(RangeEdge::Max)] <= pt || eta < mini_track_codec::RangeEta[toI(RangeEdge::Min)] || mini_track_codec::RangeEta[toI(RangeEdge::Max)] <= eta) { + if (!(mini_track_codec::RangePt[toI(RangeEdge::Min)] <= pt) || !(pt < mini_track_codec::RangePt[toI(RangeEdge::Max)]) || !(mini_track_codec::RangeEta[toI(RangeEdge::Min)] <= eta) || !(eta < mini_track_codec::RangeEta[toI(RangeEdge::Max)])) { return std::nullopt; } - const std::int32_t ptBinIndex{static_cast(std::floor((pt - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / ((mini_track_codec::RangePt[toI(RangeEdge::Max)] - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / mini_track_codec::NBinsPt)))}; - if (ptBinIndex < 0 || mini_track_codec::NBinsPt <= ptBinIndex) { + const std::int32_t indexBinPt{static_cast(std::floor((pt - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / ((mini_track_codec::RangePt[toI(RangeEdge::Max)] - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / mini_track_codec::NBinsPt)))}; + if (std::cmp_less(indexBinPt, 0) || std::cmp_greater_equal(indexBinPt, mini_track_codec::NBinsPt)) { return std::nullopt; } - const std::int32_t etaBinIndex{static_cast(std::floor((eta - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / ((mini_track_codec::RangeEta[toI(RangeEdge::Max)] - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / mini_track_codec::NBinsEta)))}; - if (etaBinIndex < 0 || mini_track_codec::NBinsEta <= etaBinIndex) { + const std::int32_t indexBinEta{static_cast(std::floor((eta - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / ((mini_track_codec::RangeEta[toI(RangeEdge::Max)] - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / mini_track_codec::NBinsEta)))}; + if (std::cmp_less(indexBinEta, 0) || std::cmp_greater_equal(indexBinEta, mini_track_codec::NBinsEta)) { return std::nullopt; } - aod::mini_mc_particle::Code::type code{static_cast(ptBinIndex)}; - aod::mini_mc_particle::Code::type codeSpan{mini_track_codec::NBinsPt}; - code += etaBinIndex * codeSpan; - codeSpan *= mini_track_codec::NBinsEta; - code += (sign > 0 ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)) * codeSpan; - codeSpan *= NEs; - code += toI(ParticleSpeciesValue) * codeSpan; + aod::mini_mc_particle::Code::type code{static_cast(indexBinPt)}; + code = code * mini_track_codec::NBinsEta + indexBinEta; + code = code * NEs + (std::cmp_greater(sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)); + code = code * NEs + toI(ParticleSpeciesValue); return code; } } // namespace mini_mc_particle_codec template requires std::is_arithmetic_v -constexpr std::int32_t nEnabled(const Configurable>& cfg) +constexpr std::int32_t nEnabled(const Configurable>& cfg) noexcept { const LabeledArray& la{cfg.value}; return std::count_if(la[0], la[0] + la.rows() * la.cols(), [](const T x) constexpr -> bool { return x != T{}; }); } template requires std::is_arithmetic_v -constexpr bool isEnabled(const Configurable>& cfg) +constexpr bool isEnabled(const Configurable>& cfg) noexcept { - return nEnabled(cfg) > 0; + return std::cmp_greater(nEnabled(cfg), 0); } double interpolate(const TH3* const h, const double x, const double y, const double z) @@ -757,35 +798,35 @@ double interpolate(const TH3* const h, const double x, const double y, const dou return 0.; } - static constexpr auto GetBinIndicesWeights{[](const TAxis* const axis, const double position) -> std::array, NEs> { - if (!axis) { + static constexpr auto GetBinIndicesWeights{[](const TAxis* const a, const double position) -> std::array, NEs> { + if (!a) { return {}; } - const std::int32_t nBins{axis->GetNbins()}; - if (nBins == 1 || position <= axis->GetBinCenter(1)) { + const std::int32_t nBins{a->GetNbins()}; + if (std::cmp_equal(nBins, 1) || position <= a->GetBinCenter(1)) { return {{{1, 1.}, {1, 0.}}}; } - if (position >= axis->GetBinCenter(nBins)) { + if (position >= a->GetBinCenter(nBins)) { return {{{nBins, 1.}, {nBins, 0.}}}; } - const std::int32_t binIndex{axis->FindFixBin(position)}; - const std::int32_t binIndexLower{position < axis->GetBinCenter(binIndex) ? binIndex - 1 : binIndex}; - const std::int32_t binIndexUpper{binIndexLower + 1}; - const double fraction{(position - axis->GetBinCenter(binIndexLower)) / (axis->GetBinCenter(binIndexUpper) - axis->GetBinCenter(binIndexLower))}; + const std::int32_t indexBin{a->FindFixBin(position)}; + const std::int32_t indexBinLower{position < a->GetBinCenter(indexBin) ? indexBin - 1 : indexBin}; + const std::int32_t indexBinUpper{indexBinLower + 1}; + const double fraction{(position - a->GetBinCenter(indexBinLower)) / (a->GetBinCenter(indexBinUpper) - a->GetBinCenter(indexBinLower))}; - return {{{binIndexLower, 1. - fraction}, {binIndexUpper, fraction}}}; + return {{{indexBinLower, 1. - fraction}, {indexBinUpper, fraction}}}; }}; - const std::array, NEs> binIndicesWeightsX{GetBinIndicesWeights(h->GetXaxis(), x)}; - const std::array, NEs> binIndicesWeightsY{GetBinIndicesWeights(h->GetYaxis(), y)}; - const std::array, NEs> binIndicesWeightsZ{GetBinIndicesWeights(h->GetZaxis(), z)}; + const std::array, NEs> indicesAndWeightsBinX{GetBinIndicesWeights(h->GetXaxis(), x)}; + const std::array, NEs> indicesAndWeightsBinY{GetBinIndicesWeights(h->GetYaxis(), y)}; + const std::array, NEs> indicesAndWeightsBinZ{GetBinIndicesWeights(h->GetZaxis(), z)}; double result{}; - for (const auto& [iBinX, weightX] : binIndicesWeightsX) { - for (const auto& [iBinY, weightY] : binIndicesWeightsY) { - for (const auto& [iBinZ, weightZ] : binIndicesWeightsZ) { + for (const auto& [iBinX, weightX] : indicesAndWeightsBinX) { + for (const auto& [iBinY, weightY] : indicesAndWeightsBinY) { + for (const auto& [iBinZ, weightZ] : indicesAndWeightsBinZ) { result += weightX * weightY * weightZ * h->GetBinContent(iBinX, iBinY, iBinZ); } } @@ -799,8 +840,8 @@ struct PartNumFluc { static constexpr std::int32_t NDimensionsEfficiency{4}; const TList* lCcdb{}; - std::map> runNumbersIndicesGroupIndices; - std::int32_t runGroupIndexCurrent{}; + std::map> runNumbersToIndicesRunAndGroup; + std::int32_t indexRunGroupCurrent{}; std::array, NEs>, NEs>, NEs> calibrationsPtMeasureDca{}; std::array>, NEs>, NEs> hsCentralityPtEtaShiftNSigmaPid{}; std::array>, NEs>, NEs> hsVzCentralityPtEtaEfficiency{}; @@ -811,35 +852,35 @@ struct PartNumFluc { std::array>, NEs> numbers{}; std::array>, NEs> numbersEff{}; - void clear() noexcept { *this = {}; } + constexpr void clear() noexcept { *this = {}; } } holderMcEvent{}; struct HolderEvent { static constexpr std::array> RangeCentrality{0., 100.}; - static constexpr bool isValidCentrality(const double value) noexcept { return RangeCentrality[toI(RangeEdge::Min)] <= value && value < RangeCentrality[toI(RangeEdge::Max)]; } + static constexpr bool isValidCentrality(const double centrality) noexcept { return RangeCentrality[toI(RangeEdge::Min)] <= centrality && centrality < RangeCentrality[toI(RangeEdge::Max)]; } std::int32_t runNumber{}; - std::int32_t runIndex{}; - std::int32_t runGroupIndex{}; + std::int32_t indexRun{}; + std::int32_t indexRunGroup{}; double vz{}; - std::array> nGlobalTracks{}; - std::array> nPvContributors{}; - std::array>, NEs>, NEs> measureDca{}; - std::array> nTofBeta{}; + std::array> nsGlobalTracks{}; + std::array> nsPvContributors{}; + std::array>, NEs>, NEs> measuresDca{}; + std::array> nsTofBeta{}; double centralityCalibration{}; double centrality{}; - std::int32_t subgroupIndex{}; + std::int32_t indexSubgroup{}; std::array>, NEs> numbers{}; - void clear() noexcept { *this = {}; } - [[nodiscard]] std::int32_t getNGlobalTracks() const noexcept { return std::accumulate(nGlobalTracks.begin(), nGlobalTracks.end(), 0); } - [[nodiscard]] std::int32_t getNPvContributors() const noexcept { return std::accumulate(nPvContributors.begin(), nPvContributors.end(), 0); } + constexpr void clear() noexcept { *this = {}; } + [[nodiscard]] constexpr std::int32_t getNGlobalTracks() const noexcept { return std::accumulate(nsGlobalTracks.begin(), nsGlobalTracks.end(), 0); } + [[nodiscard]] constexpr std::int32_t getNPvContributors() const noexcept { return std::accumulate(nsPvContributors.begin(), nsPvContributors.end(), 0); } template requires IsValidEnumValue [[nodiscard]] double getMeasureDca() const noexcept { const std::int32_t sumNGlobalTracks{getNGlobalTracks()}; - if (sumNGlobalTracks == 0) { + if (std::cmp_equal(sumNGlobalTracks, 0)) { return 0.; } @@ -847,17 +888,17 @@ struct PartNumFluc { if constexpr (DcaMeasureValue == DcaMeasure::Sigma) { const double meanDca{getMeasureDca()}; for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - sumDca += (std::pow(measureDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][iChargeSpecies], 2.) + std::pow(measureDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] - meanDca, 2.)) * nGlobalTracks[iChargeSpecies]; + sumDca += (std::pow(measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][iChargeSpecies], 2.) + std::pow(measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] - meanDca, 2.)) * nsGlobalTracks[iChargeSpecies]; } return std::sqrt(sumDca / sumNGlobalTracks); - } else { + } else { // DcaMeasureValue == DcaMeasure::Mean for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - sumDca += measureDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] * nGlobalTracks[iChargeSpecies]; + sumDca += measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] * nsGlobalTracks[iChargeSpecies]; } return sumDca / sumNGlobalTracks; } } - [[nodiscard]] std::int32_t getNTofBeta() const noexcept { return std::accumulate(nTofBeta.begin(), nTofBeta.end(), 0); } + [[nodiscard]] constexpr std::int32_t getNTofBeta() const noexcept { return std::accumulate(nsTofBeta.begin(), nsTofBeta.end(), 0); } } holderEvent{}; struct HolderMcParticle { @@ -866,63 +907,79 @@ struct PartNumFluc { double pt{}; double eta{}; - void clear() noexcept { *this = {}; } + constexpr void clear() noexcept { *this = {}; } } holderMcParticle{}; struct HolderTrack { static constexpr double TruncationAbsNSigmaPid{999.}; - static constexpr double truncateNSigmaPid(const double value, const double shift = {}) noexcept + static constexpr double truncateNSigmaPid(const double nSigmaPid, const double shift = {}) noexcept { - const double valueShifted{value - shift}; - return std::abs(value) < TruncationAbsNSigmaPid && std::abs(valueShifted) < TruncationAbsNSigmaPid ? valueShifted : -TruncationAbsNSigmaPid; + const double nSigmaPidShifted{nSigmaPid - shift}; + return std::abs(nSigmaPid) < TruncationAbsNSigmaPid && std::abs(nSigmaPidShifted) < TruncationAbsNSigmaPid ? nSigmaPidShifted : -TruncationAbsNSigmaPid; } - [[nodiscard]] double getNSigmaPidCombined(const std::int32_t particleSpeciesIndex) const noexcept + [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept { - return truncateNSigmaPid(std::copysign(std::hypot(nSigmaPid[toI(Detector::Tpc)][particleSpeciesIndex], nSigmaPid[toI(Detector::Tof)][particleSpeciesIndex]), nSigmaPid[toI(Detector::Tpc)][particleSpeciesIndex] + nSigmaPid[toI(Detector::Tof)][particleSpeciesIndex])); + return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); } - std::array> dca{}; + std::array> dcas{}; std::int32_t sign{}; double pt{}; double eta{}; double phi{}; - std::array> hasPid{}; - std::array>, NEs> nSigmaPid{[]() constexpr -> std::array>, NEs> { - std::array>, NEs> a{}; - std::array> b{}; - b.fill(-TruncationAbsNSigmaPid); - a.fill(b); - return a; + std::array> havePid{}; + std::array>, NEs> nsSigmaPid{[]() consteval -> std::array>, NEs> { + std::array>, NEs> result{}; + std::array> source{}; + source.fill(-TruncationAbsNSigmaPid); + result.fill(source); + return result; }()}; - void clear() noexcept { *this = {}; } + constexpr void clear() noexcept { *this = {}; } } holderTrack{}; struct HolderDerivedData { template - static constexpr T convert(const double value) noexcept + static constexpr T convert(const double input) noexcept { - return std::numeric_limits::lowest() <= value && value <= std::numeric_limits::max() ? static_cast(std::rint(value)) : (std::is_signed_v ? std::numeric_limits::lowest() : std::numeric_limits::max()); + return std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max() ? static_cast(std::rint(input)) : (std::is_signed_v ? std::numeric_limits::lowest() : std::numeric_limits::max()); } - std::array> nMcParticles{}; - std::array> nTracks{}; - std::vector signedEfficienciesMcParticle{[]() constexpr -> std::vector { std::vector v{}; v.reserve(256); return v; }()}; - std::vector signedEfficienciesTrack{[]() constexpr -> std::vector { std::vector v{}; v.reserve(256); return v; }()}; + std::array> nsMcParticles{}; + std::array> nsTracks{}; + std::vector signedEfficienciesMcParticle{[]() constexpr -> std::vector { std::vector result{}; result.reserve(256); return result; }()}; + std::vector signedEfficienciesTrack{[]() constexpr -> std::vector { std::vector result{}; result.reserve(256); return result; }()}; - void clear() noexcept + constexpr void clear() noexcept { - nMcParticles = {}; - nTracks = {}; + nsMcParticles = {}; + nsTracks = {}; signedEfficienciesMcParticle.clear(); signedEfficienciesTrack.clear(); } } holderDerivedData{}; + struct HolderMember { + bool doQaAcceptance{}; + bool doQaPhi{}; + bool doQaPid{}; + bool doCalculationYield{}; + bool doCalculationPurity{}; + bool doCalculationFractionPrimary{}; + bool doCalculationFluctuation{}; + bool doStorageMiniTable{}; + std::array> rangePtAll{std::numeric_limits::max(), std::numeric_limits::lowest()}; + std::optional configSelection; + std::mt19937_64 engineRandom{std::random_device{}()}; + std::array, NEs>, NEs> fluctuationCalculatorsTrackMcParticle{}; + std::array, NEs>, NEs> fluctuationCalculatorsTrackTrack{}; + } holderMember{}; + struct : ConfigurableGroup { std::string prefix{"cgCcdb"}; - Configurable cfgUrl{"cfgUrl", "http://ccdb-test.cern.ch:8080", "Url of CCDB"}; + Configurable cfgUrl{"cfgUrl", "https://alice-ccdb.cern.ch", "Url of CCDB"}; Configurable cfgPath{"cfgPath", "Users/f/fasi/test", "Path in CCDB"}; Configurable cfgTimestampLatest{"cfgTimestampLatest", -1, "Latest timestamp in CCDB"}; } cgCcdb{}; @@ -978,6 +1035,7 @@ struct PartNumFluc { Configurable cfgCutMinTpcChi2NCls{"cfgCutMinTpcChi2NCls", 0., "Minimum chi2 per cluster TPC"}; Configurable> cfgCutsMaxTpcChi2NCls{"cfgCutsMaxTpcChi2NCls", {std::array>{4., 3.5, 3.}.data(), NEs, getDisplayNames()}, "Maximum chi2 per cluster TPC"}; Configurable> cfgCutsMaxTpcNClsSharedRatio{"cfgCutsMaxTpcNClsSharedRatio", {std::array>{0.5, 0.4, 0.3}.data(), NEs, getDisplayNames()}, "Maximum ratios of shared clusters over clusters TPC"}; + Configurable cfgCutMinTpcNClsRatio{"cfgCutMinTpcNClsRatio", 0., "Minimum ratio of clusters over findable clusters TPC"}; Configurable> cfgCutsMinTpcNCrossedRows{"cfgCutsMinTpcNCrossedRows", {std::array>{70, 80, 90}.data(), NEs, getDisplayNames()}, "Minimum numbers of crossed rows TPC"}; Configurable cfgCutMinTpcNCrossedRowsRatio{"cfgCutMinTpcNCrossedRowsRatio", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable cfgFlagRecalibrationDca{"cfgFlagRecalibrationDca", false, "DCA recalibration flag"}; @@ -1001,58 +1059,46 @@ struct PartNumFluc { Filter filterTrack{requireQualityTracksInFilter() && requireTrackCutInFilter(TrackSelectionFlags::kGoldenChi2)}; Filter filterMcCollision{aod::mccollisionprop::numRecoCollision > 0}; - Preslice presliceTracksPerCollision{aod::track::collisionId}; - PresliceUnsorted presliceTracksPerMcParticle{aod::mctracklabel::mcParticleId}; - - Produces miniCollision{}; - Produces miniMcParticle{}; - Produces miniTrack{}; - Produces tinyMcCollision{}; - Produces tinyCollision{}; - Produces tinyMcParticle{}; - Produces tinyTrack{}; - - HistogramRegistry hrCalculationFluctuation{"CalculationFluctuation", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrCalculationFractionPrimary{"CalculationFractionPrimary", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrCalculationPurity{"CalculationPurity", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrCalculationYield{"CalculationYield", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaMc{"QaMc", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaPid{"QaPid", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaPhi{"QaPhi", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaAcceptance{"QaAcceptance", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaDca{"QaDca", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaTrack{"QaTrack", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaCentrality{"QaCentrality", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaEvent{"QaEvent", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrQaRun{"QaRun", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - HistogramRegistry hrCounter{"Counter", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - - bool doQaAcceptance{}; - bool doQaPhi{}; - bool doQaPid{}; - bool doCalculationYield{}; - bool doCalculationPurity{}; - bool doCalculationFractionPrimary{}; - bool doCalculationFluctuation{}; - bool doStorageMiniTable{}; - std::array> rangePtAll{std::numeric_limits::max(), std::numeric_limits::lowest()}; - std::unique_ptr configSelection; - - std::array, NEs>, NEs> fluctuationCalculatorsTrackMcParticle{}; - std::array, NEs>, NEs> fluctuationCalculatorsTrackTrack{}; + struct : PresliceGroup { + Preslice tracksPerCollision{aod::track::collisionId}; + PresliceUnsorted tracksPerMcParticle{aod::mctracklabel::mcParticleId}; + } psg{}; + + struct : ProducesGroup { + Produces miniCollision{}; + Produces miniMcParticle{}; + Produces miniTrack{}; + Produces tinyMcCollision{}; + Produces tinyCollision{}; + Produces tinyMcParticle{}; + Produces tinyTrack{}; + } pg{}; + + HistogramRegistry hrCalculationFluctuation{"hrCalculationFluctuation", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationFractionPrimary{"hrCalculationFractionPrimary", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationPurity{"hrCalculationPurity", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationYield{"hrCalculationYield", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaMc{"hrQaMc", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaPid{"hrQaPid", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaPhi{"hrQaPhi", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaAcceptance{"hrQaAcceptance", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaDca{"hrQaDca", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaTrack{"hrQaTrack", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaCentrality{"hrQaCentrality", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaEvent{"hrQaEvent", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaRun{"hrQaRun", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCounter{"hrCounter", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; void init(const InitContext&) { - gRandom->SetSeed(0); - - doQaAcceptance = isEnabled(cgAnalysis.cfgFlagsQaAcceptance); - doQaPhi = isEnabled(cgAnalysis.cfgFlagsQaPhi); - doQaPid = isEnabled(cgAnalysis.cfgFlagsQaPid); - doCalculationYield = isEnabled(cgAnalysis.cfgFlagsCalculationYield); - doCalculationPurity = isEnabled(cgAnalysis.cfgFlagsCalculationPurity); - doCalculationFractionPrimary = isEnabled(cgAnalysis.cfgFlagsCalculationFractionPrimary); - doCalculationFluctuation = isEnabled(cgAnalysis.cfgFlagsCalculationFluctuation); - doStorageMiniTable = isEnabled(cgAnalysis.cfgFlagsStorageMiniTable); + holderMember.doQaAcceptance = isEnabled(cgAnalysis.cfgFlagsQaAcceptance); + holderMember.doQaPhi = isEnabled(cgAnalysis.cfgFlagsQaPhi); + holderMember.doQaPid = isEnabled(cgAnalysis.cfgFlagsQaPid); + holderMember.doCalculationYield = isEnabled(cgAnalysis.cfgFlagsCalculationYield); + holderMember.doCalculationPurity = isEnabled(cgAnalysis.cfgFlagsCalculationPurity); + holderMember.doCalculationFractionPrimary = isEnabled(cgAnalysis.cfgFlagsCalculationFractionPrimary); + holderMember.doCalculationFluctuation = isEnabled(cgAnalysis.cfgFlagsCalculationFluctuation); + holderMember.doStorageMiniTable = isEnabled(cgAnalysis.cfgFlagsStorageMiniTable); if (doProcessRaw.value == doProcessMc.value) { LOG(fatal) << "Identical values of doProcessRaw and doProcessMc!"; @@ -1062,80 +1108,77 @@ struct PartNumFluc { } else { LOG(info) << "Enabling raw data process."; } - if (nEnabled(cgAnalysis.cfgFlagsCalculationFluctuation) > 1) { + if (std::cmp_greater(nEnabled(cgAnalysis.cfgFlagsCalculationFluctuation), 1)) { LOG(fatal) << "Invalid " << cgAnalysis.cfgFlagsCalculationFluctuation.name << "!"; } - if (nEnabled(cgAnalysis.cfgFlagsStorageMiniTable) > 1) { + if (std::cmp_greater(nEnabled(cgAnalysis.cfgFlagsStorageMiniTable), 1)) { LOG(fatal) << "Invalid " << cgAnalysis.cfgFlagsStorageMiniTable.name << "!"; } - if ((cgAnalysis.cfgFlagQaEvent.value || cgAnalysis.cfgFlagQaCentrality.value || doCalculationFluctuation) && cgEvent.cfgNMultiplicityBinsAxis.value <= 0) { + if ((cgAnalysis.cfgFlagQaEvent.value || cgAnalysis.cfgFlagQaCentrality.value || holderMember.doCalculationFluctuation) && std::cmp_less_equal(cgEvent.cfgNMultiplicityBinsAxis.value, 0)) { LOG(fatal) << "Invalid " << cgEvent.cfgNMultiplicityBinsAxis.name << "!"; } - if (doCalculationFluctuation && cgEvent.cfgNSubgroups.value <= 0) { + if (holderMember.doCalculationFluctuation && std::cmp_less_equal(cgEvent.cfgNSubgroups.value, 0)) { LOG(fatal) << "Invalid " << cgEvent.cfgNSubgroups.name << "!"; } - if (doStorageMiniTable) { - configSelection = std::make_unique(); + if (holderMember.doStorageMiniTable) { + holderMember.configSelection.emplace(); for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - configSelection->minItsNCls[iSelection] = cgTrack.cfgCutsMinItsNCls.value.get(iSelection); - configSelection->maxItsChi2NCls[iSelection] = cgTrack.cfgCutsMaxItsChi2NCls.value.get(iSelection); - configSelection->minTpcNCls[iSelection] = cgTrack.cfgCutsMinTpcNCls.value.get(iSelection); - configSelection->maxTpcChi2NCls[iSelection] = cgTrack.cfgCutsMaxTpcChi2NCls.value.get(iSelection); - configSelection->minTpcNCrossedRows[iSelection] = cgTrack.cfgCutsMinTpcNCrossedRows.value.get(iSelection); - configSelection->maxTpcNClsSharedRatio[iSelection] = cgTrack.cfgCutsMaxTpcNClsSharedRatio.value.get(iSelection); + holderMember.configSelection->cutsMinItsNCls[iSelection] = cgTrack.cfgCutsMinItsNCls.value.get(iSelection); + holderMember.configSelection->cutsMaxItsChi2NCls[iSelection] = cgTrack.cfgCutsMaxItsChi2NCls.value.get(iSelection); + holderMember.configSelection->cutsMaxTpcChi2NCls[iSelection] = cgTrack.cfgCutsMaxTpcChi2NCls.value.get(iSelection); + holderMember.configSelection->cutsMaxTpcNClsSharedRatio[iSelection] = cgTrack.cfgCutsMaxTpcNClsSharedRatio.value.get(iSelection); + holderMember.configSelection->cutsMinTpcNCrossedRows[iSelection] = cgTrack.cfgCutsMinTpcNCrossedRows.value.get(iSelection); } for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - configSelection->maxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(getName(iDcaAxis).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(getName(iDcaAxis).data(), getName(iSelection).data()); } } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - configSelection->maxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(getName(iParticleSpecies).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(getName(iParticleSpecies).data(), getName(iSelection).data()); } } static constexpr auto ValidateCuts{ [] requires IsValidEnumValue - (const auto& cuts, const auto& name) constexpr -> void { + (const std::span>& cuts, const std::string& name) constexpr -> void { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - if (!std::isfinite(cuts[iSelection])) { - LOG(fatal) << "Non-finite value in " << name << "!"; - } - if (iSelection == 0) { + if (std::cmp_equal(iSelection, 0)) { continue; } + if constexpr (RangeEdgeValue == RangeEdge::Min) { if (!(cuts[iSelection - 1] < cuts[iSelection])) { LOG(fatal) << "Values in " << name << " must satisfy " << getName(Selection::Loose) << " < " << getName(Selection::Default) << " < " << getName(Selection::Tight) << "!"; } - } else { + } else { // RangeEdgeValue == RangeEdge::Max if (!(cuts[iSelection - 1] > cuts[iSelection])) { LOG(fatal) << "Values in " << name << " must satisfy " << getName(Selection::Loose) << " > " << getName(Selection::Default) << " > " << getName(Selection::Tight) << "!"; } } } }}; - ValidateCuts.template operator()(configSelection->minItsNCls, cgTrack.cfgCutsMinItsNCls.name); - ValidateCuts.template operator()(configSelection->maxItsChi2NCls, cgTrack.cfgCutsMaxItsChi2NCls.name); - ValidateCuts.template operator()(configSelection->minTpcNCls, cgTrack.cfgCutsMinTpcNCls.name); - ValidateCuts.template operator()(configSelection->maxTpcChi2NCls, cgTrack.cfgCutsMaxTpcChi2NCls.name); - ValidateCuts.template operator()(configSelection->minTpcNCrossedRows, cgTrack.cfgCutsMinTpcNCrossedRows.name); - ValidateCuts.template operator()(configSelection->maxTpcNClsSharedRatio, cgTrack.cfgCutsMaxTpcNClsSharedRatio.name); + + ValidateCuts.template operator()(holderMember.configSelection->cutsMinItsNCls, cgTrack.cfgCutsMinItsNCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxItsChi2NCls, cgTrack.cfgCutsMaxItsChi2NCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxTpcChi2NCls, cgTrack.cfgCutsMaxTpcChi2NCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxTpcNClsSharedRatio, cgTrack.cfgCutsMaxTpcNClsSharedRatio.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMinTpcNCrossedRows, cgTrack.cfgCutsMinTpcNCrossedRows.name); for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { - ValidateCuts.template operator()(configSelection->maxAbsNSigmaDca[iDcaAxis], cgTrack.cfgCutsMaxAbsNSigmaDca.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis], cgTrack.cfgCutsMaxAbsNSigmaDca.name); } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { - ValidateCuts.template operator()(configSelection->maxAbsNSigmaPid[iParticleSpecies], cgTrack.cfgCutsMaxAbsNSigmaPid.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies], cgTrack.cfgCutsMaxAbsNSigmaPid.name); } } - if (doStorageMiniTable || static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge)))) { + if (holderMember.doStorageMiniTable || static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge)))) { for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { - rangePtAll[toI(RangeEdge::Min)] = std::min(rangePtAll[toI(RangeEdge::Min)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Min))); - rangePtAll[toI(RangeEdge::Max)] = std::max(rangePtAll[toI(RangeEdge::Max)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Max))); + holderMember.rangePtAll[toI(RangeEdge::Min)] = std::min(holderMember.rangePtAll[toI(RangeEdge::Min)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Min))); + holderMember.rangePtAll[toI(RangeEdge::Max)] = std::max(holderMember.rangePtAll[toI(RangeEdge::Max)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Max))); } } @@ -1145,33 +1188,33 @@ struct PartNumFluc { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(true); - if (cgCcdb.cfgTimestampLatest.value >= 0) { + if (std::cmp_greater_equal(cgCcdb.cfgTimestampLatest.value, 0)) { ccdb->setCreatedNotAfter(cgCcdb.cfgTimestampLatest.value); } readCcdb(); std::int32_t nRunsBad{}; - for (const auto& [runNumber, runIndexGroupIndex] : holderCcdb.runNumbersIndicesGroupIndices) { - const std::int32_t runGroupIndex{runIndexGroupIndex.second}; - if (runGroupIndex == 0 || (cgEvent.cfgFlagRejectionRunBad.value && runGroupIndex < 0)) { + for (const auto& [runNumber, indexRunAndGroup] : holderCcdb.runNumbersToIndicesRunAndGroup) { + const std::int32_t indexRunGroup{indexRunAndGroup.second}; + if (std::cmp_equal(indexRunGroup, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(indexRunGroup, 0))) { ++nRunsBad; } } - if (holderCcdb.runNumbersIndicesGroupIndices.empty()) { + if (holderCcdb.runNumbersToIndicesRunAndGroup.empty()) { LOG(info) << "No run process enabled."; } else { - LOG(info) << "Number of runs: " << holderCcdb.runNumbersIndicesGroupIndices.size(); - if (nRunsBad <= 0) { + LOG(info) << "Number of runs: " << holderCcdb.runNumbersToIndicesRunAndGroup.size(); + if (std::cmp_less_equal(nRunsBad, 0)) { LOG(info) << "No run rejection enabled."; } else { LOG(info) << "Number of bad runs: " << nRunsBad; } - for (const auto& [runNumber, runIndexGroupIndex] : holderCcdb.runNumbersIndicesGroupIndices) { - if (runIndexGroupIndex.second == 0 || (cgEvent.cfgFlagRejectionRunBad.value && runIndexGroupIndex.second < 0)) { - LOG(info) << "Enabling rejecting run: " << runNumber << " (" << runIndexGroupIndex.second << ")"; + for (const auto& [runNumber, indexRunAndGroup] : holderCcdb.runNumbersToIndicesRunAndGroup) { + if (std::cmp_equal(indexRunAndGroup.second, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(indexRunAndGroup.second, 0))) { + LOG(info) << "Enabling rejecting run: " << runNumber << " (" << indexRunAndGroup.second << ")"; } else { - LOG(info) << "Enabling processing run: " << runNumber << " (" << std::abs(runIndexGroupIndex.second) << ")"; + LOG(info) << "Enabling processing run: " << runNumber << " (" << std::abs(indexRunAndGroup.second) << ")"; } } } @@ -1191,7 +1234,7 @@ struct PartNumFluc { LOG(info) << "Enabling RCT flag: table"; } - if ((cgEvent.cfgBitsSelection.value & ((std::uint64_t{1} << aod::evsel::EventSelectionFlags::kNsel) - 1)) == 0) { + if (std::cmp_equal(cgEvent.cfgBitsSelection.value & ((std::uint64_t{1} << aod::evsel::EventSelectionFlags::kNsel) - 1), 0)) { LOG(info) << "No event selection bit enabled."; } else { for (std::int32_t const& iBit : std::views::iota(0, aod::evsel::EventSelectionFlags::kNsel)) { @@ -1221,7 +1264,7 @@ struct PartNumFluc { if (cgAnalysis.cfgFlagQaRun.value) { LOG(info) << "Enabling run QA."; - const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersIndicesGroupIndices.size()), -0.5, holderCcdb.runNumbersIndicesGroupIndices.size() - 0.5, "Run Index"}}}; + const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, holderCcdb.runNumbersToIndicesRunAndGroup.size() - 0.5, "Run Index"}}}; for (const auto& [name, title, isChargeSpeciesSeparated] : std::to_array>( {{"Vx", "#LT#it{V}_{#it{x}}#GT (cm)", false}, @@ -1245,6 +1288,7 @@ struct PartNumFluc { {"TpcNCls", "TPC #LTnClusters#GT", true}, {"TpcChi2NCls", "TPC #LT#it{#chi}^{2}/nClusters#GT", true}, {"TpcNClsSharedRatio", "TPC #LTnSharedClusters/nClusters#GT", true}, + {"TpcNClsRatio", "TPC #LTnClusters/nFindableClusters#GT", true}, {"TpcNCrossedRows", "TPC #LTnCrossedRows#GT", true}, {"TpcNCrossedRowsRatio", "TPC #LTnCrossedRows/nFindableClusters#GT", true}, {"Pt", "#LT#it{p}_{T}#GT (GeV/#it{c})", true}, @@ -1298,7 +1342,7 @@ struct PartNumFluc { {{"ItsNClsChi2NCls", {HistType::kTHnSparseD, {{10, -0.5, 9.5, "ITS nClusters"}, {80, 0., 40., "ITS #it{#chi}^{2}/nClusters"}}}}, {"TpcNClsChi2NCls", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {100, 0., 5., "TPC #it{#chi}^{2}/nClusters"}}}}, {"TpcNClsNClsShared", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nSharedClusters"}}}}, - {"TpcNClsFindableNCrossedRows", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nFindableClusters"}, {180, -0.5, 179.5, "TPC nCrossedRows"}}}}})) { + {"TpcNClsNClsFindableNCrossedRows", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nFindableClusters"}, {180, -0.5, 179.5, "TPC nCrossedRows"}}}}})) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { hrQaTrack.add(std::format("h{}_{}", name, getName(iChargeSpecies)).c_str(), "", hcs); } @@ -1331,7 +1375,7 @@ struct PartNumFluc { for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - hrQaAcceptance.add(std::format("h{}Pt_{}Edge{}{}", iParticleSpeciesAll == toI(ParticleSpeciesAll::All) ? "Eta" : "Rapidity", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, iParticleSpeciesAll == toI(ParticleSpeciesAll::All) ? "#it{#eta}" : "#it{y}"}, {250, 0., 2.5, "#it{p}_{T} (GeV/#it{c})"}}}); + hrQaAcceptance.add(std::format("h{}Pt_{}Edge{}{}", std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "Eta" : "Rapidity", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "#it{#eta}" : "#it{y}"}, {250, 0., 2.5, "#it{p}_{T} (GeV/#it{c})"}}}); } } } @@ -1361,7 +1405,7 @@ struct PartNumFluc { const AxisSpec asCentrality{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}; - if (iParticleSpeciesAll == toI(ParticleSpeciesAll::All)) { + if (std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All))) { const AxisSpec asPOverQ{350, -3.5, 3.5, "#it{p}/#it{q} (GeV/#it{c})"}; const AxisSpec asEta{48, -1.2, 1.2, "#it{#eta}"}; @@ -1489,9 +1533,9 @@ struct PartNumFluc { for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { if (doProcessMc.value) { - fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber] = std::make_unique(); + holderMember.fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber] = std::make_unique(); } - fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber] = std::make_unique(); + holderMember.fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber] = std::make_unique(); } if (doProcessMc.value) { @@ -1522,7 +1566,7 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndex->GetN())) { - holderCcdb.runNumbersIndicesGroupIndices[static_cast(std::rint(gRunNumberGroupIndex->GetX()[iRun]))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetY()[iRun]))}; + holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetX()[iRun]))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetY()[iRun]))}; } if (cgEvent.cfgFlagRejectionRunBadMc.value) { @@ -1531,28 +1575,28 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex_mc!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndexMc->GetN())) { - if (static_cast(std::rint(gRunNumberGroupIndexMc->GetY()[iRun])) <= 0) { - if (const auto iter{holderCcdb.runNumbersIndicesGroupIndices.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetX()[iRun])))}; iter != holderCcdb.runNumbersIndicesGroupIndices.end() && iter->second.second > 0) { + if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetY()[iRun])), 0)) { + if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetX()[iRun])))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { iter->second.second = -iter->second.second; } } } } } else { - const std::int32_t runGroupIndex{std::abs(holderEvent.runGroupIndex)}; - if (holderCcdb.runGroupIndexCurrent == runGroupIndex) { + const std::int32_t indexRunGroup{std::abs(holderEvent.indexRunGroup)}; + if (std::cmp_equal(holderCcdb.indexRunGroupCurrent, indexRunGroup)) { return; } - holderCcdb.runGroupIndexCurrent = runGroupIndex; + holderCcdb.indexRunGroupCurrent = indexRunGroup; holderCcdb.calibrationsPtMeasureDca = {}; holderCcdb.hsCentralityPtEtaShiftNSigmaPid = {}; holderCcdb.hsVzCentralityPtEtaEfficiency = {}; - if (!cgTrack.cfgFlagRecalibrationDca.value && !isEnabled(cgTrack.cfgFlagsRecalibrationNSigmaPid) && !doCalculationFluctuation) { + if (!cgTrack.cfgFlagRecalibrationDca.value && !isEnabled(cgTrack.cfgFlagsRecalibrationNSigmaPid) && !holderMember.doCalculationFluctuation) { return; } - const std::string nameList{std::format("lRunGroup_{}", runGroupIndex)}; + const std::string nameList{std::format("lRunGroup_{}", indexRunGroup)}; const TList* const lRunGroup{dynamic_cast(holderCcdb.lCcdb->FindObject(nameList.c_str()))}; if (!lRunGroup) { LOG(fatal) << "Invalid " << nameList << "!"; @@ -1563,17 +1607,17 @@ struct PartNumFluc { for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { std::pair& calibration{holderCcdb.calibrationsPtMeasureDca[iDcaMeasure][iDcaAxis][iChargeSpecies]}; - const std::string nameFormula{std::format("fPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", runGroupIndex)}; + const std::string nameFormula{std::format("fPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; calibration.first = dynamic_cast(lRunGroup->FindObject(nameFormula.c_str())); - if (!calibration.first || calibration.first->GetNdim() != 1 || calibration.first->GetNpar() <= 0) { + if (!calibration.first || std::cmp_not_equal(calibration.first->GetNdim(), 1) || std::cmp_less_equal(calibration.first->GetNpar(), 0)) { LOG(fatal) << "Invalid " << nameFormula << "!"; } LOG(info) << "Reading from CCDB: " << nameFormula << " \"" << calibration.first->GetExpFormula() << "\""; const std::int32_t nParameters{calibration.first->GetNpar()}; - const std::string nameHistogram{std::format("hCentralityEtaParameterPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", runGroupIndex)}; + const std::string nameHistogram{std::format("hCentralityEtaParameterPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; calibration.second = dynamic_cast(lRunGroup->FindObject(nameHistogram.c_str())); - if (calibration.second == nullptr || calibration.second->GetNbinsZ() != nParameters || std::ranges::any_of(std::views::iota(0, nParameters), [zAxis = calibration.second->GetZaxis()](const std::int32_t binIndex) -> bool { return zAxis->GetBinCenter(binIndex + 1) != binIndex; })) { + if (calibration.second == nullptr || std::cmp_not_equal(calibration.second->GetNbinsZ(), nParameters) || std::ranges::any_of(std::views::iota(0, nParameters), [aZ = calibration.second->GetZaxis()](const std::int32_t indexBin) -> bool { return aZ->GetBinCenter(indexBin + 1) != indexBin; })) { LOG(fatal) << "Invalid " << nameHistogram << "!"; } LOG(info) << "Reading from CCDB: " << nameHistogram; @@ -1589,10 +1633,10 @@ struct PartNumFluc { for (std::int32_t const& iDetector : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - const std::string name{std::format("hCentralityPtEtaShift{}NSigma{}{}{}_runGroup{}", getName(iDetector), getName(iParticleSpecies), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", runGroupIndex)}; - const TH3*& histogram{holderCcdb.hsCentralityPtEtaShiftNSigmaPid[iDetector][iParticleSpecies][iChargeSpecies]}; - histogram = dynamic_cast(lRunGroup->FindObject(name.c_str())); - if (!histogram) { + const std::string name{std::format("hCentralityPtEtaShift{}NSigma{}{}{}_runGroup{}", getName(iDetector), getName(iParticleSpecies), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; + const TH3*& h{holderCcdb.hsCentralityPtEtaShiftNSigmaPid[iDetector][iParticleSpecies][iChargeSpecies]}; + h = dynamic_cast(lRunGroup->FindObject(name.c_str())); + if (!h) { LOG(fatal) << "Invalid " << name << "!"; } LOG(info) << "Reading from CCDB: " << name; @@ -1601,16 +1645,16 @@ struct PartNumFluc { } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { - if (!static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge))) && (iParticleSpecies != toI(ParticleSpecies::Kaon) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Kaon)))) && (iParticleSpecies != toI(ParticleSpecies::Proton) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Proton))))) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge))) && (std::cmp_not_equal(iParticleSpecies, toI(ParticleSpecies::Kaon)) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Kaon)))) && (std::cmp_not_equal(iParticleSpecies, toI(ParticleSpecies::Proton)) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Proton))))) { continue; } for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - const std::string name{std::format("hVzCentralityPtEtaEfficiency{}{}{}_runGroup{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies), runGroupIndex)}; - const THnBase*& histogram{holderCcdb.hsVzCentralityPtEtaEfficiency[iPidStrategy][iParticleSpecies][iChargeSpecies]}; - histogram = dynamic_cast(lRunGroup->FindObject(name.c_str())); - if (!histogram || histogram->GetNdimensions() != HolderCcdb::NDimensionsEfficiency) { + const std::string name{std::format("hVzCentralityPtEtaEfficiency{}{}{}_runGroup{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies), indexRunGroup)}; + const THnBase*& h{holderCcdb.hsVzCentralityPtEtaEfficiency[iPidStrategy][iParticleSpecies][iChargeSpecies]}; + h = dynamic_cast(lRunGroup->FindObject(name.c_str())); + if (!h || std::cmp_not_equal(h->GetNdimensions(), HolderCcdb::NDimensionsEfficiency)) { LOG(fatal) << "Invalid " << name << "!"; } LOG(info) << "Reading from CCDB: " << name; @@ -1624,8 +1668,8 @@ struct PartNumFluc { requires IsValidEnumValue double getEfficiency(const bool doUseMcParticleMomentum) const { - const THnBase* const hsVzCentralityPtEtaEfficiency{holderCcdb.hsVzCentralityPtEtaEfficiency[toI(PidStrategyValue)][toI(ParticleSpeciesValue)][toI(ChargeSpeciesValue)]}; - return hsVzCentralityPtEtaEfficiency ? hsVzCentralityPtEtaEfficiency->GetBinContent(hsVzCentralityPtEtaEfficiency->GetBin(std::array{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta}.data())) : 0.; + const THnBase* const hVzCentralityPtEtaEfficiency{holderCcdb.hsVzCentralityPtEtaEfficiency[toI(PidStrategyValue)][toI(ParticleSpeciesValue)][toI(ChargeSpeciesValue)]}; + return hVzCentralityPtEtaEfficiency ? hVzCentralityPtEtaEfficiency->GetBinContent(hVzCentralityPtEtaEfficiency->GetBin(std::array{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta}.data())) : 0.; } template @@ -1634,7 +1678,7 @@ struct PartNumFluc { { if constexpr (DoRecalibrate) { if (cgTrack.cfgFlagsRecalibrationNSigmaPid.value.get(toI(ParticleSpeciesValue))) { - return interpolate(holderCcdb.hsCentralityPtEtaShiftNSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)][holderTrack.sign > 0 ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)], holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta); + return interpolate(holderCcdb.hsCentralityPtEtaShiftNSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)][std::cmp_greater(holderTrack.sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)], holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta); } } return 0.; @@ -1650,10 +1694,10 @@ struct PartNumFluc { if (std::cmp_less(parametersPtMeasureDcaScratch.size(), nParameters)) { parametersPtMeasureDcaScratch.resize(nParameters); } - const std::int32_t centralityBinIndex{std::clamp(hCentralityEtaParameterPtMeasureDca->GetXaxis()->FindFixBin(holderEvent.centralityCalibration), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsX())}; - const std::int32_t etaBinIndex{std::clamp(hCentralityEtaParameterPtMeasureDca->GetYaxis()->FindFixBin(holderTrack.eta), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsY())}; + const std::int32_t indexBinCentrality{std::clamp(hCentralityEtaParameterPtMeasureDca->GetXaxis()->FindFixBin(holderEvent.centralityCalibration), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsX())}; + const std::int32_t indexBinEta{std::clamp(hCentralityEtaParameterPtMeasureDca->GetYaxis()->FindFixBin(holderTrack.eta), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsY())}; for (std::int32_t const& iParameter : std::views::iota(0, nParameters)) { - parametersPtMeasureDcaScratch[iParameter] = hCentralityEtaParameterPtMeasureDca->GetBinContent(centralityBinIndex, etaBinIndex, iParameter + 1); + parametersPtMeasureDcaScratch[iParameter] = hCentralityEtaParameterPtMeasureDca->GetBinContent(indexBinCentrality, indexBinEta, iParameter + 1); } return fPtMeasureDca->EvalPar(&holderTrack.pt, parametersPtMeasureDcaScratch.data()); } @@ -1663,12 +1707,12 @@ struct PartNumFluc { double getAbsNSigmaDca() const { if (!cgTrack.cfgFlagRecalibrationDca.value) { - return std::abs(holderTrack.dca[toI(DcaAxisValue)]); + return std::abs(holderTrack.dcas[toI(DcaAxisValue)]); } - const std::int32_t chargeSpeciesIndex{holderTrack.sign > 0 ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)}; - const double sigma{getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][chargeSpeciesIndex])}; - return sigma > 0. ? std::abs((holderTrack.dca[toI(DcaAxisValue)] - getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][chargeSpeciesIndex])) / sigma) : std::numeric_limits::infinity(); + const std::int32_t indexChargeSpecies{std::cmp_greater(holderTrack.sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)}; + const double sigma{getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][indexChargeSpecies])}; + return sigma > 0. ? std::abs((holderTrack.dcas[toI(DcaAxisValue)] - getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][indexChargeSpecies])) / sigma) : std::numeric_limits::infinity(); } template @@ -1677,44 +1721,44 @@ struct PartNumFluc { { if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { if constexpr (PidStrategyAllValue == PidStrategyAll::Tpc) { - if (!holderTrack.hasPid[toI(Detector::Tpc)]) { + if (!holderTrack.havePid[toI(Detector::Tpc)]) { return false; } } else if constexpr (PidStrategyAllValue == PidStrategyAll::Tof) { - if (!holderTrack.hasPid[toI(Detector::Tof)]) { + if (!holderTrack.havePid[toI(Detector::Tof)]) { return false; } } else { - if (!holderTrack.hasPid[toI(Detector::Tpc)] || !holderTrack.hasPid[toI(Detector::Tof)]) { + if (!holderTrack.havePid[toI(Detector::Tpc)] || !holderTrack.havePid[toI(Detector::Tof)]) { return false; } } } else { - constexpr std::int32_t ParticleSpeciesIndex{toI(getValue(ParticleSpeciesAllValue))}; + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; if constexpr (PidStrategyAllValue == PidStrategyAll::TpcTofSeparated) { - if (!(std::abs(holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(ParticleSpeciesIndex, toI(SelectionValue)))) { + if (!(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { return false; } - if (!(std::abs(holderTrack.nSigmaPid[toI(Detector::Tof)][ParticleSpeciesIndex]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(ParticleSpeciesIndex, toI(SelectionValue)))) { + if (!(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { return false; } - if (doRejectOthers && !(std::abs(holderTrack.nSigmaPid[toI(Detector::Tof)][ParticleSpeciesIndex]) < std::min(std::abs(holderTrack.nSigmaPid[toI(Detector::Tof)][(ParticleSpeciesIndex + 1) % NEs]), std::abs(holderTrack.nSigmaPid[toI(Detector::Tof)][(ParticleSpeciesIndex + 2) % NEs])))) { + if (doRejectOthers && !(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]) < std::min(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][(IndexParticleSpecies + 1) % NEs]), std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][(IndexParticleSpecies + 2) % NEs])))) { return false; } } else if constexpr (PidStrategyAllValue == PidStrategyAll::TpcTofCombined) { - const double absNSigmaPidCombined{std::abs(holderTrack.getNSigmaPidCombined(ParticleSpeciesIndex))}; - if (!(absNSigmaPidCombined < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(ParticleSpeciesIndex, toI(SelectionValue)))) { + const double absNSigmaPidCombined{std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies))}; + if (!(absNSigmaPidCombined < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { return false; } - if (doRejectOthers && !(absNSigmaPidCombined < std::min(std::abs(holderTrack.getNSigmaPidCombined((ParticleSpeciesIndex + 1) % NEs)), std::abs(holderTrack.getNSigmaPidCombined((ParticleSpeciesIndex + 2) % NEs))))) { + if (doRejectOthers && !(absNSigmaPidCombined < std::min(std::abs(holderTrack.getNSigmaPidCombined((IndexParticleSpecies + 1) % NEs)), std::abs(holderTrack.getNSigmaPidCombined((IndexParticleSpecies + 2) % NEs))))) { return false; } } else { - constexpr std::int32_t DetectorIndex{toI(getValue(PidStrategyAllValue))}; - if (!(std::abs(holderTrack.nSigmaPid[DetectorIndex][ParticleSpeciesIndex]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(ParticleSpeciesIndex, toI(SelectionValue)))) { + constexpr std::int32_t IndexDetector{toI(getValue(PidStrategyAllValue))}; + if (!(std::abs(holderTrack.nsSigmaPid[IndexDetector][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { return false; } - if (doRejectOthers && !(std::abs(holderTrack.nSigmaPid[DetectorIndex][ParticleSpeciesIndex]) < std::min(std::abs(holderTrack.nSigmaPid[DetectorIndex][(ParticleSpeciesIndex + 1) % NEs]), std::abs(holderTrack.nSigmaPid[DetectorIndex][(ParticleSpeciesIndex + 2) % NEs])))) { + if (doRejectOthers && !(std::abs(holderTrack.nsSigmaPid[IndexDetector][IndexParticleSpecies]) < std::min(std::abs(holderTrack.nsSigmaPid[IndexDetector][(IndexParticleSpecies + 1) % NEs]), std::abs(holderTrack.nsSigmaPid[IndexDetector][(IndexParticleSpecies + 2) % NEs])))) { return false; } } @@ -1727,9 +1771,9 @@ struct PartNumFluc { bool isPid() const { if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { - return ChargeSpeciesValue == ChargeSpecies::Plus ? holderMcParticle.charge > 0 : holderMcParticle.charge < 0; + return ChargeSpeciesValue == ChargeSpecies::Plus ? std::cmp_greater(holderMcParticle.charge, 0) : std::cmp_less(holderMcParticle.charge, 0); } else { - return holderMcParticle.pdgCode == getPdgCode(getValue(ParticleSpeciesAllValue), ChargeSpeciesValue); + return std::cmp_equal(holderMcParticle.pdgCode, getPdgCode(getValue(ParticleSpeciesAllValue), ChargeSpeciesValue)); } } @@ -1739,12 +1783,12 @@ struct PartNumFluc { { const double pt{doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt}; if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { - if (!(rangePtAll[toI(RangeEdge::Min)] < pt) || !(pt < rangePtAll[toI(RangeEdge::Max)])) { + if (!(holderMember.rangePtAll[toI(RangeEdge::Min)] < pt) || !(pt < holderMember.rangePtAll[toI(RangeEdge::Max)])) { return false; } } else { - const std::int32_t particleSpeciesIndex{toI(getValue(ParticleSpeciesAllValue))}; - if (!(cgTrack.cfgCutsRangePt.value.get(particleSpeciesIndex, toI(RangeEdge::Min)) < pt) || !(pt < cgTrack.cfgCutsRangePt.value.get(particleSpeciesIndex, toI(RangeEdge::Max)))) { + const std::int32_t indexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + if (!(cgTrack.cfgCutsRangePt.value.get(indexParticleSpecies, toI(RangeEdge::Min)) < pt) || !(pt < cgTrack.cfgCutsRangePt.value.get(indexParticleSpecies, toI(RangeEdge::Max)))) { return false; } } @@ -1762,13 +1806,13 @@ struct PartNumFluc { if (cgTrack.cfgFlagPvContributor.value && !track.isPVContributor()) { return false; } - if (!(track.itsNCls() > cgTrack.cfgCutsMinItsNCls.value.get(toI(Selection::Default)))) { + if (!std::cmp_greater(track.itsNCls(), cgTrack.cfgCutsMinItsNCls.value.get(toI(Selection::Default)))) { return false; } if (!(track.itsChi2NCl() < cgTrack.cfgCutsMaxItsChi2NCls.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcNClsFound() > cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default)))) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default)))) { return false; } if (!(cgTrack.cfgCutMinTpcChi2NCls.value < track.tpcChi2NCl()) || !(track.tpcChi2NCl() < cgTrack.cfgCutsMaxTpcChi2NCls.value.get(toI(Selection::Default)))) { @@ -1777,7 +1821,10 @@ struct PartNumFluc { if (!(track.tpcFractionSharedCls() < cgTrack.cfgCutsMaxTpcNClsSharedRatio.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcNClsCrossedRows() > cgTrack.cfgCutsMinTpcNCrossedRows.value.get(toI(Selection::Default)))) { + if (!(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value)) { + return false; + } + if (!std::cmp_greater(track.tpcNClsCrossedRows(), cgTrack.cfgCutsMinTpcNCrossedRows.value.get(toI(Selection::Default)))) { return false; } if (!(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { @@ -1790,14 +1837,14 @@ struct PartNumFluc { requires IsValidEnumValue void fillQaRunByTrackByChargeSpecies(const T& track) { - const auto fill{[this](const auto& name, const auto value) -> void { - hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.runIndex, value); + const auto fill{[this](const auto& name, const auto input) -> void { + hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.indexRun, input); }}; const auto fillNSigmaPidByDetectorParticleSpecies{ [this, &fill] requires IsValidEnumValue () -> void { - const double nSigmaPid{holderTrack.nSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)]}; + const double nSigmaPid{holderTrack.nsSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)]}; if (std::abs(nSigmaPid) < HolderTrack::TruncationAbsNSigmaPid) { fill(C_SV(getName(DetectorValue)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesValue)), nSigmaPid); } @@ -1808,18 +1855,19 @@ struct PartNumFluc { fill(C_CS("TpcNCls"), track.tpcNClsFound()); fill(C_CS("TpcChi2NCls"), track.tpcChi2NCl()); fill(C_CS("TpcNClsSharedRatio"), track.tpcFractionSharedCls()); + fill(C_CS("TpcNClsRatio"), track.tpcFoundOverFindableCls()); fill(C_CS("TpcNCrossedRows"), track.tpcNClsCrossedRows()); fill(C_CS("TpcNCrossedRowsRatio"), track.tpcCrossedRowsOverFindableCls()); fill(C_CS("Pt"), holderTrack.pt); fill(C_CS("Eta"), holderTrack.eta); fill(C_CS("Phi"), holderTrack.phi); - if (holderTrack.hasPid[toI(Detector::Tpc)]) { + if (holderTrack.havePid[toI(Detector::Tpc)]) { fill(C_SV(getName(Detector::Tpc)) + C_CS("DeDx"), track.tpcSignal()); fillNSigmaPidByDetectorParticleSpecies.template operator()(); fillNSigmaPidByDetectorParticleSpecies.template operator()(); fillNSigmaPidByDetectorParticleSpecies.template operator()(); } - if (holderTrack.hasPid[toI(Detector::Tof)]) { + if (holderTrack.havePid[toI(Detector::Tof)]) { fill(C_SV(getName(Detector::Tof)) + C_CS("InverseBeta"), 1. / track.beta()); fillNSigmaPidByDetectorParticleSpecies.template operator()(); fillNSigmaPidByDetectorParticleSpecies.template operator()(); @@ -1831,19 +1879,19 @@ struct PartNumFluc { requires IsValidEnumValue void fillQaRunByEventByChargeSpecies() { - const auto fill{[this](const auto& name, const auto value) -> void { - hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.runIndex, value); + const auto fill{[this](const auto& name, const auto input) -> void { + hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.indexRun, input); }}; - fill(C_CS("NGlobalTracks"), holderEvent.nGlobalTracks[toI(ChargeSpeciesValue)]); - fill(C_CS("NPvContributors"), holderEvent.nPvContributors[toI(ChargeSpeciesValue)]); - if (holderEvent.nGlobalTracks[toI(ChargeSpeciesValue)] > 0) { - fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measureDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); - fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measureDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); - fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measureDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); - fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measureDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); + fill(C_CS("NGlobalTracks"), holderEvent.nsGlobalTracks[toI(ChargeSpeciesValue)]); + fill(C_CS("NPvContributors"), holderEvent.nsPvContributors[toI(ChargeSpeciesValue)]); + if (std::cmp_greater(holderEvent.nsGlobalTracks[toI(ChargeSpeciesValue)], 0)) { + fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); } - fill(C_CS("NTofBeta"), holderEvent.nTofBeta[toI(ChargeSpeciesValue)]); + fill(C_CS("NTofBeta"), holderEvent.nsTofBeta[toI(ChargeSpeciesValue)]); } template @@ -1857,7 +1905,7 @@ struct PartNumFluc { fill(C_CS("ItsNClsChi2NCls"), track.itsNCls(), track.itsChi2NCl()); fill(C_CS("TpcNClsChi2NCls"), track.tpcNClsFound(), track.tpcChi2NCl()); fill(C_CS("TpcNClsNClsShared"), track.tpcNClsFound(), track.tpcNClsShared()); - fill(C_CS("TpcNClsFindableNCrossedRows"), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); + fill(C_CS("TpcNClsNClsFindableNCrossedRows"), track.tpcNClsFound(), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); } template @@ -1868,8 +1916,8 @@ struct PartNumFluc { [this] requires IsValidEnumValue () -> void { - hrQaDca.fill(C_CS("hPtDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderTrack.pt, holderTrack.dca[toI(DcaAxisValue)]); - hrQaDca.fill(C_CS("pCentralityPtEtaDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.dca[toI(DcaAxisValue)]); + hrQaDca.fill(C_CS("hPtDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderTrack.pt, holderTrack.dcas[toI(DcaAxisValue)]); + hrQaDca.fill(C_CS("pCentralityPtEtaDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.dcas[toI(DcaAxisValue)]); }}; fillByDcaAxis.template operator()(); @@ -1887,13 +1935,13 @@ struct PartNumFluc { const auto fillByChargeSpecies{ [this] requires IsValidEnumValue - (const auto& name, const double value) -> void { + (const auto& name, const double input) -> void { const auto fillByPidStrategy{ - [this, &name, value] + [this, input, &name] requires IsValidEnumValue () -> void { if (isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { - hrQaAcceptance.fill(C_CS("h") + name + C_CS("Pt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), value, holderTrack.pt); // NOLINT(clang-analyzer-core.NonNullParamChecker) + hrQaAcceptance.fill(C_CS("h") + name + C_CS("Pt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), input, holderTrack.pt); // NOLINT(clang-analyzer-core.NonNullParamChecker) } }}; @@ -1902,13 +1950,13 @@ struct PartNumFluc { }}; if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); } else { fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); } } else { - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); } else { fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); @@ -1947,7 +1995,7 @@ struct PartNumFluc { fillByPidStrategy.template operator()(); }}; - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(); } else { fillByChargeSpecies.template operator()(); @@ -1970,29 +2018,29 @@ struct PartNumFluc { hrQaPid.fill(C_CS("hCentralityPOverQEtaTofInverseBeta"), holderEvent.centralityCalibration, track.p() / holderTrack.sign, holderTrack.eta, 1. / track.beta()); } } else { - constexpr std::int32_t ParticleSpeciesIndex{toI(getValue(ParticleSpeciesAllValue))}; + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; const auto fillByChargeSpecies{ [this] requires IsValidEnumValue () -> void { if constexpr (DataModeValue == DataMode::McTrack) { if (isPid()) { - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex]); - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nSigmaPid[toI(Detector::Tof)][ParticleSpeciesIndex]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]); } } else { // DataModeValue == DataMode::RawTrack - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); if (isPid(false)) { - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tof)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tof)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); } if (isPid(false)) { - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tpc)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nSigmaPid[toI(Detector::Tof)][ParticleSpeciesIndex]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tpc)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]); } - hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(PidStrategy::TpcTof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.getNSigmaPidCombined(ParticleSpeciesIndex)); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(PidStrategy::TpcTof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.getNSigmaPidCombined(IndexParticleSpecies)); } }}; - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(); } else { fillByChargeSpecies.template operator()(); @@ -2049,7 +2097,7 @@ struct PartNumFluc { } }}; - if (chargeSign > 0) { + if (std::cmp_greater(chargeSign, 0)) { fillByChargeSpecies.template operator()(); } else { fillByChargeSpecies.template operator()(); @@ -2081,7 +2129,7 @@ struct PartNumFluc { fillByPidStrategy.template operator()(); }}; - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(); } else { fillByChargeSpecies.template operator()(); @@ -2113,7 +2161,7 @@ struct PartNumFluc { fillByPidStrategy.template operator()(); }}; - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillByChargeSpecies.template operator()(); } else { fillByChargeSpecies.template operator()(); @@ -2126,9 +2174,9 @@ struct PartNumFluc { if (static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(iParticleNumber))) { for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { if (doProcessMc.value) { - fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber]->clear(); + holderMember.fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber]->clear(); } - fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber]->clear(); + holderMember.fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber]->clear(); } } } @@ -2155,16 +2203,16 @@ struct PartNumFluc { return true; } else if constexpr (DataModeValue == DataMode::McTrack) { return flagMcParticleMomentum; - } else { + } else { // DataModeValue == DataMode::RawTrack return false; } }(cgTrack.cfgFlagMcParticleMomentum.value)}; - const ChargeSpecies chargeSpecies{chargeSign > 0 ? ChargeSpecies::Plus : ChargeSpecies::Minus}; + const ChargeSpecies chargeSpecies{std::cmp_greater(chargeSign, 0) ? ChargeSpecies::Plus : ChargeSpecies::Minus}; if (isGoodMomentum(ParticleNumberValue)>(doUseMcParticleMomentum)) { if constexpr (DataModeValue == DataMode::McMcParticle) { - ++holderDerivedData.nMcParticles[toI(chargeSpecies)]; + ++holderDerivedData.nsMcParticles[toI(chargeSpecies)]; } else { - ++holderDerivedData.nTracks[toI(chargeSpecies)]; + ++holderDerivedData.nsTracks[toI(chargeSpecies)]; } } @@ -2172,7 +2220,7 @@ struct PartNumFluc { [this, chargeSign, doUseMcParticleMomentum] requires IsValidEnumValue && (getValue(ParticleNumberValue) == ParticleSpeciesAll::All || getValue(ParticleNumberValue) == getValue(ParticleSpeciesValue)) () -> bool { - if (!isGoodMomentum(ParticleSpeciesValue)>(doUseMcParticleMomentum) || (DataModeValue != DataMode::RawTrack && (chargeSign > 0 ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>()))) { + if (!isGoodMomentum(ParticleSpeciesValue)>(doUseMcParticleMomentum) || (DataModeValue != DataMode::RawTrack && (std::cmp_greater(chargeSign, 0) ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>()))) { return false; } @@ -2181,7 +2229,7 @@ struct PartNumFluc { return ptMcParticle >= thresholdPtTofPid; } else if constexpr (DataModeValue == DataMode::McTrack) { return (doUseMcParticleMomentumValue ? ptMcParticle : ptTrack) >= thresholdPtTofPid; - } else { + } else { // DataModeValue == DataMode::RawTrack return ptTrack >= thresholdPtTofPid; } }(doUseMcParticleMomentum, holderMcParticle.pt, holderTrack.pt, cgTrack.cfgThresholdsPtTofPid.value.get(toI(ParticleSpeciesValue)))}; @@ -2198,11 +2246,11 @@ struct PartNumFluc { const double efficiency{doUseTofPid ? getEfficiency(doUseMcParticleMomentum) : getEfficiency(doUseMcParticleMomentum)}; const auto fill{ [this, efficiency]() -> void { - std::array, NEs>, NEs>& fluctuationCalculatorsTrack{DataModeValue == DataMode::McMcParticle ? fluctuationCalculatorsTrackMcParticle : fluctuationCalculatorsTrackTrack}; + std::array, NEs>, NEs>& fluctuationCalculatorsTrack{DataModeValue == DataMode::McMcParticle ? holderMember.fluctuationCalculatorsTrackMcParticle : holderMember.fluctuationCalculatorsTrackTrack}; if constexpr (ChargeSpeciesValue == ChargeSpecies::Plus) { fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Plus)]->fill(1., efficiency); fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Net)]->fill(1., efficiency); - } else { + } else { // ChargeSpeciesValue == ChargeSpecies::Minus fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Minus)]->fill(1., efficiency); fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Net)]->fill(-1., efficiency); } @@ -2210,7 +2258,7 @@ struct PartNumFluc { }}; if constexpr (DataModeValue == DataMode::McMcParticle) { ++holderMcEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpeciesValue)]; - if (gRandom->Rndm() < efficiency) { + if (std::uniform_real_distribution{}(holderMember.engineRandom) < efficiency) { ++holderMcEvent.numbersEff[toI(ParticleNumberValue)][toI(ChargeSpeciesValue)]; fill(); } @@ -2222,7 +2270,7 @@ struct PartNumFluc { } }}; - if (chargeSign > 0) { + if (std::cmp_greater(chargeSign, 0)) { calculateByChargeSpecies.template operator()(); } else { calculateByChargeSpecies.template operator()(); @@ -2258,15 +2306,15 @@ struct PartNumFluc { requires IsValidEnumValue () -> void { if (doProcessMc.value) { - const std::array products{fluctuationCalculatorsTrackMcParticle[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; + const std::array products{holderMember.fluctuationCalculatorsTrackMcParticle[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; for (std::int32_t const& iOrderKey : std::views::iota(0, fluctuation_calculator_base::NOrderKeys)) { - hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)) + C_CS("_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderKey, products[iOrderKey]); + hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)) + C_CS("_mc"), holderEvent.centrality, holderEvent.indexSubgroup, iOrderKey, products[iOrderKey]); } } - const std::array products{fluctuationCalculatorsTrackTrack[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; + const std::array products{holderMember.fluctuationCalculatorsTrackTrack[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; for (std::int32_t const& iOrderKey : std::views::iota(0, fluctuation_calculator_base::NOrderKeys)) { - hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)), holderEvent.centrality, holderEvent.subgroupIndex, iOrderKey, products[iOrderKey]); + hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)), holderEvent.centrality, holderEvent.indexSubgroup, iOrderKey, products[iOrderKey]); } }}; @@ -2279,28 +2327,28 @@ struct PartNumFluc { template bool setTrack(const T& track) { - if (std::abs(track.sign()) != 1) { + if (std::cmp_not_equal(std::abs(track.sign()), 1)) { return false; } holderTrack.clear(); - holderTrack.dca[toI(DcaAxis::Xy)] = track.dcaXY(); - holderTrack.dca[toI(DcaAxis::Z)] = track.dcaZ(); + holderTrack.dcas[toI(DcaAxis::Xy)] = track.dcaXY(); + holderTrack.dcas[toI(DcaAxis::Z)] = track.dcaZ(); holderTrack.sign = track.sign(); holderTrack.pt = track.pt(); holderTrack.eta = track.eta(); holderTrack.phi = track.phi(); - holderTrack.hasPid[toI(Detector::Tpc)] = (track.hasTPC() && track.tpcSignal() > 0.); - holderTrack.hasPid[toI(Detector::Tof)] = (track.hasTOF() && track.beta() > 0.); - if (holderTrack.hasPid[toI(Detector::Tpc)]) { - holderTrack.nSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPi(), getShiftNSigmaPid()); - holderTrack.nSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaKa(), getShiftNSigmaPid()); - holderTrack.nSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPr(), getShiftNSigmaPid()); + holderTrack.havePid[toI(Detector::Tpc)] = (track.hasTPC() && track.tpcSignal() > 0.); + holderTrack.havePid[toI(Detector::Tof)] = (track.hasTOF() && track.beta() > 0.); + if (holderTrack.havePid[toI(Detector::Tpc)]) { + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPi(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaKa(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPr(), getShiftNSigmaPid()); } - if (holderTrack.hasPid[toI(Detector::Tof)]) { - holderTrack.nSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPi(), getShiftNSigmaPid()); - holderTrack.nSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaKa(), getShiftNSigmaPid()); - holderTrack.nSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPr(), getShiftNSigmaPid()); + if (holderTrack.havePid[toI(Detector::Tof)]) { + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPi(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaKa(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPr(), getShiftNSigmaPid()); } return true; @@ -2324,7 +2372,7 @@ struct PartNumFluc { } } - if (holderMcParticle.charge == 0) { + if (std::cmp_equal(holderMcParticle.charge, 0)) { return false; } @@ -2343,22 +2391,22 @@ struct PartNumFluc { if constexpr (DoInitEvent) { if (track.isPrimaryTrack()) { - const std::int32_t chargeSpeciesIndex{toI(holderTrack.sign > 0 ? ChargeSpecies::Plus : ChargeSpecies::Minus)}; - ++holderEvent.nGlobalTracks[chargeSpeciesIndex]; + const std::int32_t indexChargeSpecies{toI(std::cmp_greater(holderTrack.sign, 0) ? ChargeSpecies::Plus : ChargeSpecies::Minus)}; + ++holderEvent.nsGlobalTracks[indexChargeSpecies]; if (track.isPVContributor()) { - ++holderEvent.nPvContributors[chargeSpeciesIndex]; + ++holderEvent.nsPvContributors[indexChargeSpecies]; } for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { - holderEvent.measureDca[toI(DcaMeasure::Mean)][iDcaAxis][chargeSpeciesIndex] += holderTrack.dca[iDcaAxis]; - holderEvent.measureDca[toI(DcaMeasure::Sigma)][iDcaAxis][chargeSpeciesIndex] += std::pow(holderTrack.dca[iDcaAxis], 2.); + holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][indexChargeSpecies] += holderTrack.dcas[iDcaAxis]; + holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][indexChargeSpecies] += std::pow(holderTrack.dcas[iDcaAxis], 2.); } - if (holderTrack.hasPid[toI(Detector::Tof)]) { - ++holderEvent.nTofBeta[chargeSpeciesIndex]; + if (holderTrack.havePid[toI(Detector::Tof)]) { + ++holderEvent.nsTofBeta[indexChargeSpecies]; } } if (cgAnalysis.cfgFlagQaRun.value && track.isPrimaryTrack()) { - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillQaRunByTrackByChargeSpecies(track); } else { fillQaRunByTrackByChargeSpecies(track); @@ -2366,7 +2414,7 @@ struct PartNumFluc { } } else { if (cgAnalysis.cfgFlagQaTrack.value && track.isPrimaryTrack()) { - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillQaTrackByChargeSpecies(track); } else { fillQaTrackByChargeSpecies(track); @@ -2378,7 +2426,7 @@ struct PartNumFluc { } if (cgAnalysis.cfgFlagQaDca.value) { - if (holderTrack.sign > 0) { + if (std::cmp_greater(holderTrack.sign, 0)) { fillQaDcaByChargeSpecies(); } else { fillQaDcaByChargeSpecies(); @@ -2389,7 +2437,7 @@ struct PartNumFluc { return false; } - if (doQaAcceptance) { + if (holderMember.doQaAcceptance) { const double vz{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; if (holderTrack.eta * vz > 0. && std::abs(vz) > (doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { fillQaAcceptanceByParticleSpeciesAll(track); @@ -2416,7 +2464,7 @@ struct PartNumFluc { return; } - miniCollision(*codeCollision); + pg.miniCollision(*codeCollision); for (const auto& mcParticle : mcParticles) { if (!initMcParticle(mcParticle)) { @@ -2428,13 +2476,13 @@ struct PartNumFluc { [this] requires IsValidEnumValue && (getValue(ParticleNumberValue) == ParticleSpeciesAll::All || getValue(ParticleNumberValue) == getValue(ParticleSpeciesValue)) () -> bool { - if (!isGoodMomentum(ParticleSpeciesValue)>(true) || (holderMcParticle.charge > 0 ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>())) { + if (!isGoodMomentum(ParticleSpeciesValue)>(true) || (std::cmp_greater(holderMcParticle.charge, 0) ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>())) { return false; } const std::optional codeMcParticle{mini_mc_particle_codec::encode(holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.charge)}; if (codeMcParticle.has_value()) { - miniMcParticle(miniCollision.lastIndex(), *codeMcParticle); + pg.miniMcParticle(pg.miniCollision.lastIndex(), *codeMcParticle); } return true; }}; @@ -2449,9 +2497,9 @@ struct PartNumFluc { } if (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) { - const auto& tracksMatched{tracks.sliceBy(presliceTracksPerMcParticle, mcParticle.globalIndex())}; + const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; for (const auto& track : tracksMatched) { - if (!setTrack(track) || !(cgTrack.cfgCutMinTpcChi2NCls.value < track.tpcChi2NCl()) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default))) || !setTrack(track) || !(track.tpcChi2NCl() > cgTrack.cfgCutMinTpcChi2NCls.value) || !(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { continue; } @@ -2460,20 +2508,20 @@ struct PartNumFluc { requires IsValidEnumValue && (ParticleSpeciesAllValue != ParticleSpeciesAll::All) () -> bool { const bool doUseMcParticleMomentum{cgTrack.cfgFlagMcParticleMomentum.value}; - if (!isGoodMomentum(doUseMcParticleMomentum) || (holderTrack.sign > 0 ? !isPid() : !isPid())) { + if (!isGoodMomentum(doUseMcParticleMomentum) || (std::cmp_greater(holderTrack.sign, 0) ? !isPid() : !isPid())) { return false; } - constexpr std::int32_t ParticleSpeciesIndex{toI(getValue(ParticleSpeciesAllValue))}; + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; const double pt{doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt}; - const bool doUseTofPid{pt >= cgTrack.cfgThresholdsPtTofPid.value.get(ParticleSpeciesIndex)}; + const bool doUseTofPid{pt >= cgTrack.cfgThresholdsPtTofPid.value.get(IndexParticleSpecies)}; if (!(doUseTofPid ? isPid(PidStrategy::TpcTof), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value) : isPid(PidStrategy::Tpc), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value))) { return false; } - const std::optional codeTrack{mini_track_codec::encode(*configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcNClsFound(), track.tpcChi2NCl(), track.tpcNClsCrossedRows(), track.tpcFractionSharedCls(), {getAbsNSigmaDca(), getAbsNSigmaDca()}, pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta, holderTrack.sign, doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(ParticleSpeciesIndex)) : std::abs(holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex]))}; + const std::optional> codeTrack{mini_track_codec::encode(*holderMember.configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tpcFractionSharedCls(), track.tpcNClsCrossedRows(), std::array>{getAbsNSigmaDca(), getAbsNSigmaDca()}, pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta, holderTrack.sign, doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies)) : std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]))}; if (codeTrack.has_value()) { - miniTrack(miniCollision.lastIndex(), *codeTrack); + pg.miniTrack(pg.miniCollision.lastIndex(), std::get<0>(*codeTrack), std::get<1>(*codeTrack)); } return true; }}; @@ -2503,10 +2551,10 @@ struct PartNumFluc { return; } - miniCollision(*codeCollision); + pg.miniCollision(*codeCollision); for (const auto& track : tracks) { - if (!setTrack(track) || !(cgTrack.cfgCutMinTpcChi2NCls.value < track.tpcChi2NCl()) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default))) || !setTrack(track) || !(track.tpcChi2NCl() > cgTrack.cfgCutMinTpcChi2NCls.value) || !(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { continue; } @@ -2514,9 +2562,9 @@ struct PartNumFluc { [this, &track] requires IsValidEnumValue (const double absNSigmaPid) -> void { - const std::optional codeTrack{mini_track_codec::encode(*configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcNClsFound(), track.tpcChi2NCl(), track.tpcNClsCrossedRows(), track.tpcFractionSharedCls(), {getAbsNSigmaDca(), getAbsNSigmaDca()}, holderTrack.pt, holderTrack.eta, holderTrack.sign, absNSigmaPid)}; + const std::optional> codeTrack{mini_track_codec::encode(*holderMember.configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tpcFractionSharedCls(), track.tpcNClsCrossedRows(), std::array>{getAbsNSigmaDca(), getAbsNSigmaDca()}, holderTrack.pt, holderTrack.eta, holderTrack.sign, absNSigmaPid)}; if (codeTrack.has_value()) { - miniTrack(miniCollision.lastIndex(), *codeTrack); + pg.miniTrack(pg.miniCollision.lastIndex(), std::get<0>(*codeTrack), std::get<1>(*codeTrack)); } }}; const auto fillByParticleSpeciesAll{ @@ -2534,13 +2582,13 @@ struct PartNumFluc { return false; } - constexpr std::int32_t ParticleSpeciesIndex{toI(getValue(ParticleSpeciesAllValue))}; - const bool doUseTofPid{holderTrack.pt >= cgTrack.cfgThresholdsPtTofPid.value.get(ParticleSpeciesIndex)}; + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + const bool doUseTofPid{holderTrack.pt >= cgTrack.cfgThresholdsPtTofPid.value.get(IndexParticleSpecies)}; if (!(doUseTofPid ? isPid(PidStrategy::TpcTof), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value) : isPid(PidStrategy::Tpc), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value))) { return false; } - fill.template operator()(doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(ParticleSpeciesIndex)) : std::abs(holderTrack.nSigmaPid[toI(Detector::Tpc)][ParticleSpeciesIndex])); + fill.template operator()(doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies)) : std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies])); } return true; }}; @@ -2583,7 +2631,7 @@ struct PartNumFluc { hrQaMc.fill(C_CS("hNCollisionsPerMcCollision"), mcCollision.numRecoCollision()); } - if (cgEvent.cfgFlagSingleCollisionMc.value && mcCollision.numRecoCollision() != 1) { + if (cgEvent.cfgFlagSingleCollisionMc.value && std::cmp_not_equal(mcCollision.numRecoCollision(), 1)) { return fillMcEventSelection(McEventSelection::NRecoCollisions); } @@ -2631,14 +2679,14 @@ struct PartNumFluc { holderEvent.runNumber = foundBc.runNumber(); { - const auto iter{holderCcdb.runNumbersIndicesGroupIndices.find(holderEvent.runNumber)}; - if (iter == holderCcdb.runNumbersIndicesGroupIndices.end()) { + const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(holderEvent.runNumber)}; + if (iter == holderCcdb.runNumbersToIndicesRunAndGroup.end()) { return fillEventSelection(EventSelection::Run); } - std::tie(holderEvent.runIndex, holderEvent.runGroupIndex) = iter->second; + std::tie(holderEvent.indexRun, holderEvent.indexRunGroup) = iter->second; } - if (holderEvent.runGroupIndex == 0 || (cgEvent.cfgFlagRejectionRunBad.value && holderEvent.runGroupIndex < 0)) { + if (std::cmp_equal(holderEvent.indexRunGroup, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(holderEvent.indexRunGroup, 0))) { return fillEventSelection(EventSelection::Run); } @@ -2669,30 +2717,30 @@ struct PartNumFluc { return fillEventSelection(EventSelection::Vz); } - if (cgEvent.cfgCutMaxOccupancy.value >= 0) { + if (std::cmp_greater_equal(cgEvent.cfgCutMaxOccupancy.value, 0)) { const std::int32_t occupancy{collision.trackOccupancyInTimeRange()}; - if (occupancy < 0 || occupancy >= cgEvent.cfgCutMaxOccupancy.value) { + if (std::cmp_less(occupancy, 0) || std::cmp_greater_equal(occupancy, cgEvent.cfgCutMaxOccupancy.value)) { return fillEventSelection(EventSelection::Occupancy); } } if (cgAnalysis.cfgFlagQaRun.value) { - hrQaRun.fill(C_CS("pRunIndexVx"), holderEvent.runIndex, collision.posX()); - hrQaRun.fill(C_CS("pRunIndexVy"), holderEvent.runIndex, collision.posY()); - hrQaRun.fill(C_CS("pRunIndexVz"), holderEvent.runIndex, holderEvent.vz); - hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0a"), holderEvent.runIndex, collision.multZeqFT0A()); - hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0c"), holderEvent.runIndex, collision.multZeqFT0C()); + hrQaRun.fill(C_CS("pRunIndexVx"), holderEvent.indexRun, collision.posX()); + hrQaRun.fill(C_CS("pRunIndexVy"), holderEvent.indexRun, collision.posY()); + hrQaRun.fill(C_CS("pRunIndexVz"), holderEvent.indexRun, holderEvent.vz); + hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0a"), holderEvent.indexRun, collision.multZeqFT0A()); + hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0c"), holderEvent.indexRun, collision.multZeqFT0C()); if (const double centrality{collision.centNTPV()}; HolderEvent::isValidCentrality(centrality)) { - hrQaRun.fill(C_CS("pRunIndexCentralityNtpv"), holderEvent.runIndex, centrality); + hrQaRun.fill(C_CS("pRunIndexCentralityNtpv"), holderEvent.indexRun, centrality); } if (const double centrality{collision.centFT0A()}; HolderEvent::isValidCentrality(centrality)) { - hrQaRun.fill(C_CS("pRunIndexCentralityFt0a"), holderEvent.runIndex, centrality); + hrQaRun.fill(C_CS("pRunIndexCentralityFt0a"), holderEvent.indexRun, centrality); } if (const double centrality{collision.centFT0C()}; HolderEvent::isValidCentrality(centrality)) { - hrQaRun.fill(C_CS("pRunIndexCentralityFt0c"), holderEvent.runIndex, centrality); + hrQaRun.fill(C_CS("pRunIndexCentralityFt0c"), holderEvent.indexRun, centrality); } if (const double centrality{collision.centFT0M()}; HolderEvent::isValidCentrality(centrality)) { - hrQaRun.fill(C_CS("pRunIndexCentralityFt0m"), holderEvent.runIndex, centrality); + hrQaRun.fill(C_CS("pRunIndexCentralityFt0m"), holderEvent.indexRun, centrality); } } @@ -2700,10 +2748,10 @@ struct PartNumFluc { initTrack(track); } for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - if (holderEvent.nGlobalTracks[iChargeSpecies] > 0) { + if (std::cmp_greater(holderEvent.nsGlobalTracks[iChargeSpecies], 0)) { for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { - holderEvent.measureDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies] /= holderEvent.nGlobalTracks[iChargeSpecies]; - holderEvent.measureDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] = std::sqrt(std::max(0., holderEvent.measureDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] / holderEvent.nGlobalTracks[iChargeSpecies] - std::pow(holderEvent.measureDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies], 2.))); + holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies] /= holderEvent.nsGlobalTracks[iChargeSpecies]; + holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] = std::sqrt(std::max(0., holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] / holderEvent.nsGlobalTracks[iChargeSpecies] - std::pow(holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies], 2.))); } } } @@ -2715,21 +2763,21 @@ struct PartNumFluc { if (cgAnalysis.cfgFlagQaEvent.value) { hrQaEvent.fill(C_CS("hNPvContributorsNGlobalTracks"), holderEvent.getNPvContributors(), holderEvent.getNGlobalTracks()); - if (holderEvent.getNGlobalTracks() > 0) { + if (std::cmp_greater(holderEvent.getNGlobalTracks(), 0)) { hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); } hrQaEvent.fill(C_CS("hNTofBetaNGlobalTracks"), holderEvent.getNTofBeta(), holderEvent.getNGlobalTracks()); } - if (!(holderEvent.getNPvContributors() - holderEvent.getNGlobalTracks() > cgEvent.cfgCutMinDeviationNPvContributors.value)) { + if (!std::cmp_greater(holderEvent.getNPvContributors() - holderEvent.getNGlobalTracks(), cgEvent.cfgCutMinDeviationNPvContributors.value)) { return fillEventSelection(EventSelection::NPvContributors); } readCcdb(); if (cgAnalysis.cfgFlagQaEvent.value) { - if (holderEvent.getNGlobalTracks() > 0) { + if (std::cmp_greater(holderEvent.getNGlobalTracks(), 0)) { hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)) + C_CS("_nPvContributorsCut"), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)) + C_CS("_nPvContributorsCut"), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); } @@ -2750,14 +2798,14 @@ struct PartNumFluc { } for (const auto& collision : collisions) { - if (collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (std::cmp_not_equal(collision.globalIndex(), mcCollision.bestCollisionIndex())) { continue; } - const auto& tracks{tracksUngrouped.sliceBy(presliceTracksPerCollision, collision.globalIndex())}; + const auto& tracks{tracksUngrouped.sliceBy(psg.tracksPerCollision, collision.globalIndex())}; const EventSelection eventSelection{initEvent(collision, tracks)}; - if ((eventSelection == EventSelection::Good || toI(eventSelection) >= toI(EventSelection::NPvContributors)) && doStorageMiniTable && gRandom->Rndm() * cgEvent.cfgFactorStorageMiniTable.value < 1.) { + if ((eventSelection == EventSelection::Good || std::cmp_greater_equal(toI(eventSelection), toI(EventSelection::NPvContributors))) && holderMember.doStorageMiniTable && (std::uniform_real_distribution{}(holderMember.engineRandom)) * cgEvent.cfgFactorStorageMiniTable.value < 1.) { const bool isGoodNPvContributors{eventSelection != EventSelection::NPvContributors}; readCcdb(); fillMiniTableMc(mcParticles, tracks, isGoodNPvContributors); @@ -2772,9 +2820,9 @@ struct PartNumFluc { hrQaMc.fill(C_CS("hCentralityVzMcDeltaVz"), holderEvent.centralityCalibration, holderMcEvent.vz, holderEvent.vz - holderMcEvent.vz); } - if (cgAnalysis.cfgFlagQaTrack.value || cgAnalysis.cfgFlagQaDca.value || doQaAcceptance || doQaPhi || doQaPid || cgAnalysis.cfgFlagQaMc.value || doCalculationYield || doCalculationPurity || doCalculationFractionPrimary || doCalculationFluctuation) { - if (doCalculationFluctuation) { - holderEvent.subgroupIndex = gRandom->Integer(cgEvent.cfgNSubgroups.value); + if (cgAnalysis.cfgFlagQaTrack.value || cgAnalysis.cfgFlagQaDca.value || holderMember.doQaAcceptance || holderMember.doQaPhi || holderMember.doQaPid || cgAnalysis.cfgFlagQaMc.value || holderMember.doCalculationYield || holderMember.doCalculationPurity || holderMember.doCalculationFractionPrimary || holderMember.doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { + holderEvent.indexSubgroup = std::uniform_int_distribution{0, cgEvent.cfgNSubgroups.value - 1}(holderMember.engineRandom); initCalculationFluctuation(); holderDerivedData.clear(); } @@ -2784,20 +2832,20 @@ struct PartNumFluc { continue; } - const auto& tracksMatched{tracks.sliceBy(presliceTracksPerMcParticle, mcParticle.globalIndex())}; + const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; if (cgAnalysis.cfgFlagQaMc.value && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { hrQaMc.fill(C_CS("hCentralityNTracksPerMcParticle"), holderEvent.centralityCalibration, tracksMatched.size()); } if (mcParticle.isPhysicalPrimary()) { - if (doCalculationYield) { + if (holderMember.doCalculationYield) { fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); } - if (doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); @@ -2809,14 +2857,14 @@ struct PartNumFluc { continue; } - if (doQaPhi) { + if (holderMember.doQaPhi) { fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); } - if (doQaPid) { + if (holderMember.doQaPid) { fillQaPidByParticleSpeciesAll(track); fillQaPidByParticleSpeciesAll(track); fillQaPidByParticleSpeciesAll(track); @@ -2828,25 +2876,25 @@ struct PartNumFluc { hrQaMc.fill(C_CS("hCentralityPtMcEtaMcDeltaEta"), holderEvent.centralityCalibration, holderMcParticle.pt, holderMcParticle.eta, holderTrack.eta - holderMcParticle.eta); } - if (doCalculationYield && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + if (holderMember.doCalculationYield && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); } - if (doCalculationPurity && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + if (holderMember.doCalculationPurity && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { fillCalculationPurityByParticleSpecies(); fillCalculationPurityByParticleSpecies(); fillCalculationPurityByParticleSpecies(); } - if (doCalculationFractionPrimary) { + if (holderMember.doCalculationFractionPrimary) { fillCalculationFractionPrimaryByParticleSpecies(mcParticle); fillCalculationFractionPrimaryByParticleSpecies(mcParticle); fillCalculationFractionPrimaryByParticleSpecies(mcParticle); } - if (doCalculationFluctuation && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + if (holderMember.doCalculationFluctuation && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); @@ -2854,18 +2902,19 @@ struct PartNumFluc { } } - if (doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { fillCalculationFluctuationByParticleNumber(); fillCalculationFluctuationByParticleNumber(); fillCalculationFluctuationByParticleNumber(); - if (const std::optional codeCollision{mini_collision_codec::encode(cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, true)}; codeCollision.has_value()) { - tinyMcCollision(holderDerivedData.nMcParticles[toI(ChargeSpecies::Plus)], holderDerivedData.nMcParticles[toI(ChargeSpecies::Minus)]); - tinyCollision(*codeCollision, holderDerivedData.nTracks[toI(ChargeSpecies::Plus)], holderDerivedData.nTracks[toI(ChargeSpecies::Minus)]); + if (const std::optional> codeCollision{tiny_collision_codec::encode(cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, holderDerivedData.nsTracks)}; codeCollision.has_value()) { + const std::tuple codeMcCollision{tiny_mc_collision_codec::encode(holderDerivedData.nsMcParticles)}; + pg.tinyMcCollision(std::get<0>(codeMcCollision), std::get<1>(codeMcCollision)); + pg.tinyCollision(std::get<0>(*codeCollision), std::get<1>(*codeCollision)); for (aod::tiny_mc_particle::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesMcParticle) { - tinyMcParticle(tinyMcCollision.lastIndex(), signedEfficiency); + pg.tinyMcParticle(pg.tinyMcCollision.lastIndex(), signedEfficiency); } for (aod::tiny_track::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesTrack) { - tinyTrack(tinyCollision.lastIndex(), signedEfficiency); + pg.tinyTrack(pg.tinyCollision.lastIndex(), signedEfficiency); } } } @@ -2876,7 +2925,7 @@ struct PartNumFluc { void processRaw(const soa::Filtered::iterator& collision, const soa::Filtered& tracks, const aod::BCsWithTimestamps&) { const EventSelection eventSelection{initEvent(collision, tracks)}; - if ((eventSelection == EventSelection::Good || toI(eventSelection) >= toI(EventSelection::NPvContributors)) && doStorageMiniTable && gRandom->Rndm() * cgEvent.cfgFactorStorageMiniTable.value < 1.) { + if ((eventSelection == EventSelection::Good || std::cmp_greater_equal(toI(eventSelection), toI(EventSelection::NPvContributors))) && holderMember.doStorageMiniTable && (std::uniform_real_distribution{}(holderMember.engineRandom)) * cgEvent.cfgFactorStorageMiniTable.value < 1.) { const bool isGoodNPvContributors{eventSelection != EventSelection::NPvContributors}; readCcdb(); fillMiniTableRaw(tracks, isGoodNPvContributors); @@ -2887,12 +2936,12 @@ struct PartNumFluc { return; } - if (!cgAnalysis.cfgFlagQaTrack.value && !cgAnalysis.cfgFlagQaDca.value && !doQaAcceptance && !doQaPhi && !doQaPid && !doCalculationYield && !doCalculationFluctuation) { + if (!cgAnalysis.cfgFlagQaTrack.value && !cgAnalysis.cfgFlagQaDca.value && !holderMember.doQaAcceptance && !holderMember.doQaPhi && !holderMember.doQaPid && !holderMember.doCalculationYield && !holderMember.doCalculationFluctuation) { return; } - if (doCalculationFluctuation) { - holderEvent.subgroupIndex = gRandom->Integer(cgEvent.cfgNSubgroups.value); + if (holderMember.doCalculationFluctuation) { + holderEvent.indexSubgroup = std::uniform_int_distribution{0, cgEvent.cfgNSubgroups.value - 1}(holderMember.engineRandom); initCalculationFluctuation(); holderDerivedData.clear(); } @@ -2902,41 +2951,41 @@ struct PartNumFluc { continue; } - if (doQaPhi) { + if (holderMember.doQaPhi) { fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); } - if (doQaPid) { + if (holderMember.doQaPid) { fillQaPidByParticleSpeciesAll(track); fillQaPidByParticleSpeciesAll(track); fillQaPidByParticleSpeciesAll(track); fillQaPidByParticleSpeciesAll(track); } - if (doCalculationYield) { + if (holderMember.doCalculationYield) { fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); fillCalculationYieldByParticleSpecies(); } - if (doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); calculateFluctuationByParticleNumber(); } } - if (doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { fillCalculationFluctuationByParticleNumber(); fillCalculationFluctuationByParticleNumber(); fillCalculationFluctuationByParticleNumber(); - if (const std::optional codeCollision{mini_collision_codec::encode(holderEvent.vz, holderEvent.centrality, true)}; codeCollision.has_value()) { - tinyCollision(*codeCollision, holderDerivedData.nTracks[toI(ChargeSpecies::Plus)], holderDerivedData.nTracks[toI(ChargeSpecies::Minus)]); + if (const std::optional> codeCollision{tiny_collision_codec::encode(holderEvent.vz, holderEvent.centrality, holderDerivedData.nsTracks)}; codeCollision.has_value()) { + pg.tinyCollision(std::get<0>(*codeCollision), std::get<1>(*codeCollision)); for (aod::tiny_track::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesTrack) { - tinyTrack(tinyCollision.lastIndex(), signedEfficiency); + pg.tinyTrack(pg.tinyCollision.lastIndex(), signedEfficiency); } } } From bb18c4365f55ac2cc650737b6ce27ad046885db4 Mon Sep 17 00:00:00 2001 From: Rrantu <156880782+Rrantu@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:10:31 +0200 Subject: [PATCH 036/648] [PWGHF] Add generated MC outputs to UPC Lambda_c task (#18010) --- PWGHF/D2H/Tasks/taskUpcLc.cxx | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskUpcLc.cxx b/PWGHF/D2H/Tasks/taskUpcLc.cxx index bb4785cf4e3..d9be942e4e0 100644 --- a/PWGHF/D2H/Tasks/taskUpcLc.cxx +++ b/PWGHF/D2H/Tasks/taskUpcLc.cxx @@ -17,6 +17,7 @@ /// \author Ran Tu , Fudan University #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" @@ -28,10 +29,12 @@ #include "PWGUD/Core/SGSelector.h" #include "PWGUD/Core/UPCHelpers.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include +#include #include #include #include @@ -126,6 +129,10 @@ DECLARE_SOA_TABLE(HfUpcLcMcInfos, "AOD", "HFUPCLCMCINFO", full::FlagMcMatchRec, full::OriginMcRec, full::PtBhadMotherPart); +DECLARE_SOA_TABLE(HfUpcLcMcGen, "AOD", "HFUPCLCMCGEN", + full::Pt, + hf_cand_mc_flag::FlagMcMatchGen, + hf_cand_mc_flag::OriginMcGen); } // namespace o2::aod /// Λc± → p± K∓ π± analysis task @@ -134,12 +141,14 @@ struct HfTaskUpcLc { Produces rowCandUpc; Produces rowCandUpcMcBdt; Produces rowCandUpcMc; + Produces rowLcMcGen; Produces rowUpcQa; Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits"}; Configurable fillTreeOnlySingleGap{"fillTreeOnlySingleGap", false, "Only fill the tree for candidates that pass the single-gap UPC events"}; + Configurable fillMcGenLcTree{"fillMcGenLcTree", false, "Fill the generated Lc to p K pi tree"}; Configurable fillTreeUpcQa{"fillTreeUpcQa", false, "Fill Tree for UPC QA"}; Configurable fillHistQa{"fillHistQa", false, "Fill histograms for UPC detector QA"}; Configurable verticesWithUpc{"verticesWithUpc", false, "Consider vertices with UPC settings"}; @@ -211,10 +220,22 @@ struct HfTaskUpcLc { BCsType const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, - aod::FDDs const& fdds - - ) + aod::FDDs const& fdds, + soa::Join const* mcParticles = nullptr) { + if constexpr (IsMc) { + if (fillMcGenLcTree) { + for (const auto& particle : *mcParticles) { + if (std::abs(particle.flagMcMatchGen()) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)) > yCandRecoMax) { + continue; + } + rowLcMcGen(particle.pt(), particle.flagMcMatchGen(), particle.originMcGen()); + } + } + } for (const auto& collision : collisions) { float centrality{-1.f}; const auto rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc(collision, centrality, ccdb, registry, bcs); @@ -397,13 +418,14 @@ struct HfTaskUpcLc { aod::BcFullInfos const& bcs, LcCandidatesMlMc const& selectedLcCandidatesMlMc, aod::McCollisions const&, + soa::Join const& mcParticles, aod::TracksWMc const&, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds, aod::Zdcs const& /*zdcs*/) { - runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMlMc, bcs, ft0s, fv0as, fdds); + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMlMc, bcs, ft0s, fv0as, fdds, &mcParticles); } PROCESS_SWITCH(HfTaskUpcLc, processMcWithMlWithUpc, "Process MC with the ML method with UPC", false); @@ -411,13 +433,14 @@ struct HfTaskUpcLc { aod::BcFullInfos const& bcs, LcCandidatesMc const& selectedLcCandidatesMc, aod::McCollisions const&, + soa::Join const& mcParticles, aod::TracksWMc const&, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds, aod::Zdcs const& /*zdcs*/) { - runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMc, bcs, ft0s, fv0as, fdds); + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMc, bcs, ft0s, fv0as, fdds, &mcParticles); } PROCESS_SWITCH(HfTaskUpcLc, processMcStdWithUpc, "Process MC with the standard method with UPC", false); }; From 695a60211103f927f1dae8451489d875e246b5e2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 22 Sep 2026 22:02:00 +0200 Subject: [PATCH 037/648] Improve the CCDB migration skill (#18018) --- .claude/commands/migrate-ccdb.md | 103 ++++++++++++++++++------------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/.claude/commands/migrate-ccdb.md b/.claude/commands/migrate-ccdb.md index 4f6bce9ab5d..15670bd84fd 100644 --- a/.claude/commands/migrate-ccdb.md +++ b/.claude/commands/migrate-ccdb.md @@ -10,9 +10,10 @@ The old approach uses `Service` and calls `ccdb->get // In namespace o2::aod (or a sub-namespace): DECLARE_SOA_CCDB_COLUMN(StructName, getterName, ConcreteType, "CCDB/Object/Path"); -// ... or, when the object needs fixing up after deserialisation, the _FULL form, whose -// trailing argument is the finaliser (see "Objects needing post-deserialisation fixup"): -DECLARE_SOA_CCDB_COLUMN_FULL(StructName, "fStructName", getterName, ConcreteType, "CCDB/Object/Path", +// ... or, when the object needs fixing up after deserialisation, the _FULL form. The 0 is +// the run-dependence mode; the finaliser is the trailing argument (see the two sections +// "Objects needing post-deserialisation fixup" and "Run-dependent objects"): +DECLARE_SOA_CCDB_COLUMN_FULL(StructName, "fStructName", getterName, ConcreteType, "CCDB/Object/Path", 0, [](ConcreteType* o) { return fixUp(o); }); DECLARE_SOA_TIMESTAMPED_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TABLEDESC", @@ -35,21 +36,11 @@ void process(MyBCs const& bcs) { **Configurable CCDB paths** (`ConfigurableCCDBPath`): -If the original task used a `Configurable` to supply the CCDB path, the path can remain user-overridable after migration using `ConfigurableCCDBPath`. This is a typed `Configurable` whose option name is automatically set to `"ccdb:" + Column::mLabel` (where `mLabel = "f" + StructName`), defaulting to the compile-time path in the column declaration. The framework reads this option name when resolving CCDB URLs, so users can still redirect the path via JSON config. +`ConfigurableCCDBPath` is a `Configurable` named `"ccdb:" + Column::mLabel` (where `mLabel = "f" + StructName`), defaulting to `Column::query`. Declare it **only** when a task consumes a *shared* column and needs a different default path than the shared declaration carries. It does not make the path settable from the task's config block — see Known gaps. No O2Physics task currently declares one; the model migration explains why it deliberately does not (`propagationServiceV2.cxx:70-73`). -The Configurable is purely declarative for the path-override mechanism: declaring it is sufficient — you do **not** pass `.value` to a getter or fetcher. The accessor remains `bc.getterName()`. The `.value` member is still available if the task wants to log the resolved path. +Why it is a default and not an override: `ArrowSupport.cxx:641-666` copies the `ConfigParamSpec` — `opt.defaultValue`, not a runtime value — from the task onto `internal-dpl-aod-ccdb` at topology-build time, before any config JSON is read. The task's own `ccdb:fXxx` option is auto-registered (`AnalysisTask.h:575`) but nothing reads it for path resolution. -```cpp -struct MyTask { - // Replaces: Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "..."}; - ConfigurableCCDBPath grpMagFieldPath; // option name = "ccdb:fGRPMagField" - - void process(MyBCs const& bcs) { - auto const& grpmag = bcs.iteratorAt(0).grpMagField(); // path override is honoured automatically - LOGP(info, "Using GRPMagField path: {}", grpMagFieldPath.value); - } -}; -``` +It is purely declarative: declaring it is sufficient, and the accessor stays `bc.getterName()`. Do **not** log `.value` as "the path in use" — it reflects the task device's own option, which the fetcher never reads, so it prints exactly the value that is being ignored. Required headers (add if missing): ``, ``, `` Headers to remove (if no longer needed): `` @@ -66,20 +57,25 @@ Find every `ccdb->getForTimeStamp(path, ts)` call (and variants like `fCCDB-> - The timestamp source (BC timestamp, computed value, etc.) - Where the result is used +Also record the other call shapes, which each need a different decision: `getForRun` (see Known gaps — not the same query), `getSpecific(path, ts, metadata)` (only `runNumber` metadata is expressible, via run-dependence mode 1; anything else — skip), and `get(path)` relying on a previously-set manager timestamp (treat as global/init-time unless that timestamp came from a BC). + **Deduplicate**: for the same (type, path) pair, declare only one CCDB column. Multiple call sites collapse into multiple uses of the same getter. ### Step 2 — Identify scope Determine whether each fetch is: - **Per-BC/per-collision** (called inside `process()` with a timestamp from a BC) — these can be migrated -- **Per-run** (called once when `runNumber` changes, guarded by `mRunNumber == ...`) — these can be migrated; the framework caches per unique timestamp automatically +- **Per-run** (called once when `runNumber` changes, guarded by `mRunNumber == ...`) — migratable, but declare the table with `DECLARE_SOA_UNIFORM_TABLE(..., aod::BCs, o2::aod::bc::RunNumber, ...)` so the fetcher queries once per run (see "Uniformity"). The timestamp default queries once per distinct BC timestamp, which is the opposite of what the `mRunNumber` guard was doing. +- **Optional** (the task calls `ccdb->setFatalWhenNull(false)` and branches on a null result — common for MC without a calibration) — a column cannot express absence; a missing object aborts the job. Leave these as-is and note it. - **Global/init-time** (called in `init()` with a fixed timestamp, not keyed to a BC) — these **cannot** be migrated to CCDB tables; leave them as-is and note this Skip the migration for any global/init-time fetches. Skip the whole file if all fetches are global. ### Step 3 — Declare CCDB columns and table -In the `o2::aod` namespace (or a private sub-namespace inside the file, before the task struct), declare: +**First check whether a shared column already exists**, and join it instead of redeclaring: `GLO/Config/GRPMagField`, `GLO/Calib/MeanVertex`, `GLO/Config/GRPECS`, `GLO/Config/GRPLHCIF` are in `Common/DataModel/GloCCDBObjects.h` (`aod::GloCCDBObjects`, `aod::GrpLHCIFCCDBObjects`); `GLO/Param/MatLUT` is in `aod::GeomCCDBObjects` in the same header; `TPC/Calib/VDriftTgl` in `Common/DataModel/TpcCCDBObjects.h`; TrackTuner calibrations in `Common/DataModel/TrackTunerCCDBObjects.h`. Declare a per-task table only for objects with no shared column. + +Otherwise, in the `o2::aod` namespace (or a private sub-namespace inside the file, before the task struct), declare: ```cpp namespace o2::aod @@ -108,6 +104,7 @@ below, and getting them wrong is silent rather than loud: Rules for naming: - `StructName` / `getterName`: derive from the type name, e.g. `GRPMagField` / `grpMagField`, `MeanVertex` / `meanVertex` +- **The label `f` is a workflow-global key**, not a per-table one: it names the path option on the fetcher, and two columns sharing a `StructName` share one path — first-seen wins, no warning (`AnalysisCCDBHelpers.cxx:91-100`, `ArrowSupport.cxx:665`). Harmless while the paths agree, silent divergence otherwise. If your path differs from an existing column of that name, pick a distinct `StructName`. - Table name: `CCDBObjects`, e.g. `SkimmerDalitzEECCDBObjects` - `_Desc_` string: short ALL-CAPS string unique within the binary (≤ 16 chars to fit the AOD descriptor), e.g. `"DALZECC"`, `"TOFCALIB"` - Namespace: lowercase snake-case derived from the task name (avoid collisions with other CCDB column namespaces in the file) @@ -120,7 +117,7 @@ Rules for naming: 3. **Remove** `ccdb->setURL(...)`, `ccdb->setCaching(...)`, `ccdb->setLocalObjectValidityChecking()`, `ccdb->setCreatedNotAfter(...)`, `ccdb->setFatalWhenNull(...)` from `init()` 4. **Remove** the entire `initCCDB()`/`initMagField()` helper method if it only did CCDB fetching; otherwise remove just the CCDB lines from it 5. **Handle path Configurables** — for each `Configurable` that held a CCDB path: - - If the path was used as the sole argument to `getForTimeStamp` and the user may want to override it at runtime: **replace** it with `ConfigurableCCDBPath` (e.g. `ConfigurableCCDBPath grpMagFieldPath;`). The member name should match the getter for clarity. Keep a comment explaining what path it controls. + - If its default equals the compile-time path you put in the column (the normal case): **remove** it. `AnalysisTask.h:575` auto-registers `ccdb:fXxx` on every subscribing task with default `Column::query`, so a `ConfigurableCCDBPath` would only restate that default. Declare `ConfigurableCCDBPath` only when consuming a *shared* column whose declared path differs from this task's old default, and comment that it sets the fetcher's default, not a runtime value. - If the path was never intended to be user-facing (e.g. internal fixed paths): **remove** it outright; the compile-time path in `DECLARE_SOA_CCDB_COLUMN` is sufficient. - Always remove Configurables that were only used for CCDB manager setup and not for paths: `ccdb-url`, `ccdb-no-later-than`, `skipGRPOquery`, `d_bz_input` (if only used to bypass CCDB), etc. 6. **Remove** cached pointer member variables (e.g. `GRPMagField* grpmag = nullptr`) if they were only populated by CCDB fetches that are now replaced @@ -140,6 +137,7 @@ Then for each `process()` that used to call `getForTimeStamp`: - Replace every `ccdb->getForTimeStamp(path, ts)` call with `bc.getterName()`. The returned reference is to a cached deserialized object; treat it as immutable. - Null-pointer checks (`if (!grpmag)`) on the result become unnecessary — the framework guarantees the object is present (or the task fails early). Remove them. - If a helper template like `initCCDB(collision)` was called per-collision, inline its remaining (non-CCDB) work or drop it. +- When configuring from the first BC, guard the empty table first: `if (bcs.size() == 0) { return; }` (`StandardCCDBLoader.h:69`). Empty BC tables occur, and `bcs.begin()` / `iteratorAt(0)` on one is undefined. ### Step 6 — Fix includes @@ -159,11 +157,11 @@ After making changes: ## Important limitations — tell the user if any apply -- **Configurable paths**: CCDB column paths are compile-time constants in the macro. Add `ConfigurableCCDBPath` to allow runtime override; its default is `Column::query` so it always agrees with the macro by construction. +- **Configurable paths**: after migration a path is a compile-time constant plus one option on the fetcher device. It is **not** settable from the task's config block or from a Hyperloop wagon parameter — read the Known gaps entry before telling the user a path is configurable. - **`getRunDuration()` calls**: these use `BasicCCDBManager` statically and are unrelated to per-BC fetching — do not touch them. - **`ctpRateFetcher` / other helpers**: out of scope. - **Multiple tasks in one file**: tasks can share a single CCDB table declaration if they need the same objects; otherwise each task gets its own with a unique `_Desc_`. -- **Non-BC timestamps**: if the timestamp comes from something other than a BC, the migration is non-trivial — flag it instead of forcing it. This is the single most common blocker in practice. `Common/Tools/EventSelectionModule.h:243` computes `ts = sorTimestamp / 2 + eorTimestamp / 2` (mid-run, from `getRunDuration` / `AggregatedRunInfo`) and fetches `EventSelectionParams`, `ITS/Config/AlpideParam`, `TriggerAliases` and `ITS/Calib/TimeDeadMap` at it. A BC-keyed column fetches at each BC's own timestamp instead, so migrating these silently changes which object version is served whenever an object is revised mid-run. They need a run-keyed table before they can move. +- **Non-BC timestamps**: if the timestamp comes from something other than a BC, the migration is non-trivial — flag it instead of forcing it. This is the single most common blocker in practice. `Common/Tools/EventSelectionModule.h:244` computes `ts = sorTimestamp / 2 + eorTimestamp / 2` (mid-run, from `getRunDuration` / `AggregatedRunInfo`) and fetches `EventSelectionParams`, `ITS/Config/AlpideParam`, `TriggerAliases` and `ITS/Calib/TimeDeadMap` at it. A BC-keyed column fetches at each BC's own timestamp instead, so migrating these silently changes which object version is served whenever an object is revised mid-run. They need a run-uniform table before they can move — and note that one queries at the run's first BC, not mid-run, so it is equivalent only for objects with a single version per run. - **Global/init-time fetches** (e.g. `efficiencyGlobal.cxx` style): not migratable — the timestamped-table mechanism requires a row in a BC-keyed table. - **Magnetic-field side effects**: tasks that compute `d_bz` from a fetched `GRPMagField` and seed a propagator can keep that logic, just sourcing the object from `bc.grpMagField()` instead of `ccdb->getForTimeStamp(...)`. @@ -171,20 +169,22 @@ After making changes: ### Why this migration matters beyond tidiness -The per-task path Configurable is a silent-divergence trap. `propagationService` and `propagationServiceV2` share the identical `ccdb.lutPath` Configurable (`Common/Tools/StandardCCDBLoader.h:45`, default `GLO/Param/MatLUT`), but config JSONs key overrides by *device name*. Every config in the tree carries a `propagation-service` block setting `GLO/Param/MatLUTInner` and no `propagation-service-v2` block, so V2 silently fell back to the full LUT — different material corrections, no warning. After migration the path is one option on the fetcher device, and two tasks disagreeing produces a warning (`ArrowSupport.cxx:641-666`) instead of silence. +The old per-task path Configurable is device-name-scoped, which made it a silent-divergence trap: `propagationServiceV2` shared `ccdb.lutPath` with `propagationService` (`Common/Tools/StandardCCDBLoader.h:45`, default `GLO/Param/MatLUT`), but every config setting `GLO/Param/MatLUTInner` did so under a `propagation-service` block (8 of them, all under `Tutorials/`), so V2 silently used the full LUT — different material corrections, no warning. + +Migration makes the path one option on the fetcher device and turns two *tasks* declaring conflicting defaults into a warning (`ArrowSupport.cxx:641-666`). That is the real gain. It does not fix the scoping itself: **runtime** overrides remain device-scoped, just aimed at `internal-dpl-aod-ccdb` instead of the task — see Known gaps before telling a user a migrated path is "now configurable". ### Objects needing post-deserialisation fixup Some objects are not usable straight out of the ROOT streamer. `MatLayerCylSet` is a `FlatObject`: its internal pointers are unfixed and its voxel lookup unbuilt until `MatLayerCylSet::rectifyPtrFromFile()` runs. Use the `_FULL` form, which carries the finaliser (the plain `DECLARE_SOA_CCDB_COLUMN` passes an identity one): ```cpp -DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", //! +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", 0, //! [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); ``` -The finaliser must be the **last** macro argument (commas in a lambda body are absorbed by `__VA_ARGS__`), has signature `T* (*)(T*)`, and runs on the receiving device once per (re)deserialisation, before the object is ever handed out. Ownership contract: whatever it returns is what the column cache later `delete`s, so a finaliser returning a *different* instance must dispose of the one it was given. +The argument before the finaliser is the run-dependence mode (`0` = query by timestamp, the default the plain macro passes; see "Run-dependent objects"). The finaliser must be the **last** macro argument (commas in a lambda body are absorbed by `__VA_ARGS__`), has signature `T* (*)(T*)`, and runs on the receiving device once per (re)deserialisation, before the object is ever handed out. Ownership contract: whatever it returns is what the column cache later `delete`s, so a finaliser returning a *different* instance must dispose of the one it was given. -Do **not** put this fixup in the task. There is no `finaliseCCDB` hook on the analysis path (`adaptAnalysisTask` wires only `EndOfStream`, `AnalysisTask.h:610-619`; grep confirms zero uses of `finaliseCCDB` in O2Physics), and even if there were, an opt-in hook means a task that forgets it gets a silently broken object. +Do **not** put this fixup in the task. There is no `finaliseCCDB` hook on the analysis path (`adaptAnalysisTask` wires only `EndOfStream`, `AnalysisTask.h:610-619`; grep confirms zero uses of `finaliseCCDB` in O2Physics). ### Uniformity: how often the object can change @@ -204,15 +204,9 @@ Worked examples in the tree: `aod::TpcCalibCCDBObjects` keeps the timestamp defa the TPC drift velocity genuinely varies within a run; `aod::GeomCCDBObjects` and `aod::TrackTunerCCDBObjects` are run-uniform. -Two consequences worth knowing before choosing: - -- The uniformity column may live in a **different table** from the timestamp — the run number - is on `aod::BCs`, the timestamp on `aod::Timestamps`. Both are handed to the fetcher - automatically (the table's `generateSources()` merges their originals) and read positionally. -- Positional reading is only sound if the two sources are **row-aligned**. ASoA encodes no - type-level relation between tables that merely have equal row counts, so this cannot be a - `static_assert`; the fetcher compares the two column lengths and fatals on a mismatch. - Anything joinable with the BCs is fine. +The run number lives on `aod::BCs` and the timestamp on `aod::Timestamps`; the macro takes both, +and the fetcher fatals if they are not row-aligned (anything joinable with the BCs is fine). The +uniformity column must be an int32/int64/uint64 (`AnalysisCCDBHelpers.cxx:246-260`). ### Paths that vary by run: declare a mapping, not code @@ -228,10 +222,13 @@ is fatal**, deliberately — silently substituting another period's calibration mode this whole mechanism exists to prevent. A string with no `=` is a plain path, so existing columns are unaffected. -The mapping is *data*, carried in the schema metadata. That matters: the CCDB fetcher is a -separate device and must not depend on code from the task that declared the column, so a -resolver lambda would not do. It also means the run ranges stop being compiled in — the whole -mapping is replaceable at runtime through the `ccdb:fXxx` option. +**The key matched against the ranges is the table's uniformity value**, so a run-range mapping +requires `DECLARE_SOA_UNIFORM_TABLE` on `aod::bc::RunNumber`. On a timestamp-uniform table the +ranges match nothing: fatal if there is no fallback entry, and — worse — every row silently +takes the fallback if there is one. + +The mapping is *data* in the schema metadata, so the run ranges stop being compiled in: the +whole mapping is replaceable through the `ccdb:fXxx` option, subject to the Known gaps scoping. This replaces hand-written run-range tables. `TrackTuner::getPathInputFileAutomaticFromCCDB()` is the model case: ~50 lines of `else if (lo <= runNumber && runNumber <= hi)` became the @@ -277,7 +274,7 @@ can report whether an option was explicitly set. One table per **family of objects used together with similar validity intervals** — not one per consuming task. Geometry and material description (`GLO/Param/MatLUT`, and later `GLO/Config/GeometryAligned`, `GLO/Config/Geometry`, `/Calib/Align`; see `GRPGeomRequest` in `O2/Detectors/Base/src/GRPGeomHelper.cxx:44-60`) is one family with essentially static validity. The GRP family changes per run, and `GRPMagField` is requested per timeframe in O2 (`GRPGeomHelper.cxx:72`). Splitting on that boundary keeps a task from fetching a multi-hundred-MB LUT it never asked for. -**Several timestamped tables can be joined onto the same BCs.** `soa::Join` works: the duplicated `aod::Timestamps` is deduplicated when `originals` is merged (`ASoA.h:172-186`), giving 4 originals, and every accessor resolves. Do not invent per-use-case tables to work around a limitation that does not exist. +**Several timestamped tables can be joined onto the same BCs.** `soa::Join` works: the duplicated `aod::Timestamps` is deduplicated when `originals` is merged (`ASoA.h:131-140`), giving 4 originals, and every accessor resolves. Do not invent per-use-case tables to work around a limitation that does not exist. ### Global state is not a lookup @@ -296,18 +293,36 @@ If a shared module takes a `StandardCCDBLoader`, change it to take the values it ### What the migration does and does not buy -The fetcher downloads once into a shm cache and the column stores `(handle, segment, size)` (`AnalysisCCDBHelpers.cxx:213-222`). What is shared is the **serialised blob**; each consumer still streams its own heap copy in the column getter. So expect fewer downloads, one configuration point and cross-device consistency — but not a per-device RSS reduction. For a `FlatObject` like the LUT, real memory sharing needs a zero-copy path (`FlatObject::setActualBufferAddress`) that does not exist yet. +The fetcher downloads once into a shm cache and the column stores `(handle, segment, size)` (`AnalysisCCDBHelpers.cxx:320-330`). What is shared is the **serialised blob**; each consumer still streams its own heap copy in the column getter. So expect fewer downloads, one configuration point and cross-device consistency — but not a per-device RSS reduction. For a `FlatObject` like the LUT, real memory sharing needs a zero-copy path (`FlatObject::setActualBufferAddress`) that does not exist yet. + +Measured on `propagationServiceV2` (`HF_LHC23_pass4_Thin_small_2P3PDstar`, `daily-20260922-0000-1`), migrated wagon 61527 against un-migrated baseline wagon 61526: + +| | baseline 766059 | migrated 766456 | +| --- | --- | --- | +| propagation device `cpuUsedAbsolute` | 3,972,078 | 2,866,181 (**−28 %**) | +| propagation device peak PSS | 160 MB | 168 MB | +| train peak PSS | 1340 MB | 1366 MB | +| CCDB queries from the task device | 3.03 MB fetched | 0 — all via the fetcher | + +Quote the CPU number when justifying a migration; do not promise memory. ### Known gaps in the mechanism -- **Run-dependent objects are not served correctly.** The analysis fetcher still hardcodes `.runNumber = 1, .runDependent = 0` for every column, even though `CCDBFetcherHelper.cxx:189-195` implements the run-dependent query paths. `GLO/Config/GRPECS` is marked "Run dependent !!!" in O2 and already has a column — verify before relying on it. Now that a run-uniform table gives the fetcher a run number per row, wiring this through is small and worth doing. -- **`getForRun` is not the same query.** `BasicCCDBManager::getForRun` resolves the run duration and queries at *mid-run* (`BasicCCDBManager.h:364-374`); a column queries at each BC's timestamp. Identical for objects with one version per run, divergent otherwise. +- **Runtime path overrides are scoped to the fetcher device, so most existing config mechanisms cannot reach them.** `ccdb:fXxx` is registered as an option on `internal-dpl-aod-ccdb`, and DPL resolves options strictly per device: `DataProcessingDevice.cxx:2615` builds the retriever keyed on the device `name`, and `ConfigurationOptionsRetriever.cxx:47` does `getRecursive(mMainKey)` with no global or wildcard section. A value placed in any other device's block is unreachable. Hyperloop writes wagon parameters into the wagon's own device block, and the config JSONs in the tree are written the same way, so **the natural way to set a migrated path silently does nothing**. The four levers that do work today: pass `--ccdb:fXxx` on the workflow command line (DPL forwards CLI options to every device declaring them, which is what `propagationServiceV2.cxx:73` tells users to do); hand-write an `internal-dpl-aod-ccdb` block into the config JSON; edit the compile-time path in the column declaration; or declare `ConfigurableCCDBPath` in a task to change that default — but note that ArrowSupport takes the *first* device in workflow order carrying the option, and every subscriber carries it with the compile-time default, so a `ConfigurableCCDBPath` in a task that is not the first subscriber loses to that default with only a warning. Making this usable from Hyperloop needs a Hyperloop-side change (emit `ccdb:*` into an `internal-dpl-aod-ccdb` block); making it usable generally needs the fetcher to consult the declaring task's runtime value, which the build-time propagation in `ArrowSupport.cxx:641-666` cannot do. + + Observed end-to-end (Hyperloop test 766459): the wagon carried `ccdb:fMatLUT = GLO/Param/MatLUTInner` and `configuration.json` shipped it, inside the `propagation-service-v2` block; `dpl-config.json` then held the key **twice**, `MatLUTInner` under `propagation-service-v2` and `MatLUT` under `internal-dpl-aod-ccdb`. The job fetched the full LUT. + + **Verify a path override by payload, never by config** — reading it back from the wagon or from `configuration.json` only proves it was *stored*. Three checks prove it was *used*: the fetcher's `[internal-dpl-aod-ccdb] ccdb reads ` log lines, the object UUID in that URL, and `reader.ccdb-cache-fetched-bytes` in `performanceMetrics_processed.json`. Grepping for the path *name* is not one of them. + +- **Run-dependent objects need an explicit mode, and no in-tree column sets one.** The run-dependence argument of `DECLARE_SOA_CCDB_COLUMN_FULL` (`0` = by timestamp; `1` additionally sends the run as `runNumber` metadata; `2` queries by run instead of timestamp — `CCDBFetcherHelper.cxx:189-198`) *is* honoured: the fetcher reads it per field, takes the run from the uniformity value, and fatals if the table is not uniform in `fRunNumber` (`AnalysisCCDBHelpers.cxx:288-303`). What is missing is use: every in-tree column is mode 0. In particular `GLO/Config/GRPECS` is requested run-dependently in O2 (`GRPGeomHelper.cxx:66`) but `ccdbGlo::GRPECSObject` is mode 0 inside the timestamp-uniform `aod::GloCCDBObjects` — do not rely on it for run-keyed lookups until it moves to a run-uniform table with mode 1. +- **`getForRun` is not the same query.** `BasicCCDBManager::getForRun` resolves the run duration and queries at *mid-run* (`BasicCCDBManager.h:364-381`); a column queries at each BC's timestamp. Identical for objects with one version per run, divergent otherwise. - **Row cardinality, not query count.** The uniformity column already collapses the *queries* to one per distinct value, but the table still carries one row per BC per column — a `FixedSizeList`, 24 B, rebuilt every timeframe. Collapsing the rows too needs a non-extension table plus lookup by value at the consumer, which does not exist yet. So a run-uniform table costs the same arrow memory as before; what it saves is the fetching. -- **Multi-run dataframes.** Skimmed datasets can span runs. Every existing consumer configures from `bcs.begin()` and applies it to the whole DF (`propagationServiceV2.cxx`, `StandardCCDBLoader.h:70-77`, `strangenessBuilderModule.h:850`), which is wrong for such a DF. Migrating preserves this bug unless it is fixed deliberately — do not claim the migration fixes it. +- **Multi-run dataframes.** Skimmed datasets can span runs. Every existing consumer configures from `bcs.begin()` and applies it to the whole DF (`propagationServiceV2.cxx`, `StandardCCDBLoader.h:70-77`, `strangenessBuilderModule.h:884`), which is wrong for such a DF. Migrating preserves this bug unless it is fixed deliberately — do not claim the migration fixes it. ### Practical gotchas -- `DECLARE_SOA_CCDB_COLUMN` expands to code using `TClass` and `TBufferFile`, but `ASoA.h` only sees them forward-declared. A translation unit that includes the column header without otherwise pulling in `` and `` fails to compile. Include them if needed. +- `DECLARE_SOA_CCDB_COLUMN` expands to a non-template member using `TClass::GetClass` and `TBufferFile`, and `ASoA.h` neither includes nor forward-declares them. The header that expands the macro must have `` and `` in scope — include them explicitly (`// IWYU pragma: keep`) rather than relying on `AnalysisDataModel.h` to drag them in transitively. +- The getter holds one deserialised object per column *type*, replaced (and the previous one `delete`d) as soon as a row with a different payload is read (`ASoA.h:2413-2432`). A reference from `bc1.x()` dangles after `bc2.x()` when the two rows resolve to different objects — another run in a multi-run DF, or a mid-run revision. Read at the point of use; never hold the reference across rows or timeframes. This is also why a pointer stored into global state (`setMatLUT`) must be refreshed every timeframe. - A failed fetch is fatal, not silent: if `extractCCDBPayload` returns null the getter aborts naming the type, the path and the `ccdb:` option to check. A mistyped path therefore stops the job rather than dereferencing null. - Do not add a `sources` member to a table's metadata struct. It makes the struct satisfy both `soa::with_sources` and `soa::with_sources_generator`, and `getInputMetadata` becomes ambiguous. - Device options are matched by device *name*. Never look a task's own option up by a hardcoded name (`device.name == "propagation-service"` silently matched nothing in `propagation-service-v2`); take the running device from `initContext.services().get()`. Spell the type out rather than using `auto`, or the pre-existing `option.defaultValue.get()` becomes a dependent name and needs `template`. From fb96939b71946d71525daa818ddb25a7452f73e5 Mon Sep 17 00:00:00 2001 From: Zhengqing Wang Date: Tue, 22 Sep 2026 23:27:51 +0200 Subject: [PATCH 038/648] [PWGLF] Fix duplicate histogram name in pi-hypertriton femto (#18014) --- PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx index ce1433b873b..784304adcd8 100644 --- a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx @@ -355,7 +355,7 @@ struct PiHypertritonFemto { {"hPoolFlow", "Mixing pool selection;0=selected,1=in range;events", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, {"hDaughterHeTPC", "He3 daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, {"hDaughterPiTPC", "Pion daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, - {"hPionTPC", "Pair pion QA per selected track;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"hPairPionTPC", "Pair pion QA per selected track;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, {"MC/SE/hKstarRecVsGenHyperReco", "Pion-hypertriton k* response for reconstructed true hypertritons;generated k* (GeV/#it{c});reconstructed k* (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {300, 0., 3.}}}}, {"MC/SE/hKstarResolutionHyperReco", "Pion-hypertriton k* resolution for reconstructed true hypertritons;reconstructed k* (GeV/#it{c});k*_{reco}-k*_{gen} (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {200, -0.2, 0.2}}}}, {"MC/SE/hPrimaryPionVsKstarDen", "Truth-matched selected pion pair-weighted denominator;k* (GeV/#it{c});Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, @@ -788,7 +788,7 @@ struct PiHypertritonFemto { continue; } event.hadrons.push_back(makePion(track, primaryVertex)); - hadHyperRegistry.fill(HIST("hPionTPC"), track.tpcInnerParam(), track.tpcSignal()); + hadHyperRegistry.fill(HIST("hPairPionTPC"), track.tpcInnerParam(), track.tpcSignal()); } } From 834e08f85f45a98a4a4df30eac08f4faef637331 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Tue, 22 Sep 2026 23:40:47 +0200 Subject: [PATCH 039/648] [PWGLF] Add configurable for TOF nsigma selection (#18017) --- PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx b/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx index a97fedddd4c..e7d70cf152d 100644 --- a/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx @@ -328,7 +328,8 @@ struct FlattenictyPikp { Configurable nclPidTPCMin{"nclPidTPCMin", 130, "Minimum number of TPC PID clusters"}; Configurable phiCutPtMin{"phiCutPtMin", 2.0f, "Minimum pT for phi cut"}; Configurable tofBetaPion{"tofBetaPion", 1.0f, "Minimum beta for TOF pions"}; - Configurable useSelTOFBeta{"useSelTOFBeta", true, "Use selection of TOF Beta"}; + Configurable useSelTOFBeta{"useSelTOFBeta", true, "Apply selection of TOF Beta"}; + Configurable useSelTOFnSigma{"useSelTOFnSigma", false, "Apply Nsigma selection of TOF"}; Configurable tofBetaPiMax{"tofBetaPiMax", 5E-5, "Maximum beta for TOF pion selection"}; Configurable rejectTrkAtTPCSector{"rejectTrkAtTPCSector", true, "Reject tracks close to the TPC sector boundaries"}; Configurable geoTrkCutMin{"geoTrkCutMin", "0.06/x+pi/18.0-0.06", "ROOT TF1 formula for minimum phi cut in TPC"}; @@ -973,9 +974,9 @@ struct FlattenictyPikp { if (track.length() == Cnull) { return false; } - const float tTOF = track.tofSignal(); - const float tExpPiTOF = track.tofExpSignalPi(tTOF); - if (tTOF == Cnull) { + const float& tTOF = track.tofSignal(); + const float& tExpPiTOF = track.tofExpSignalPi(tTOF); + if (tTOF <= Cnull) { return false; } registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hTofExpPi"), track.p(), tExpPiTOF / tTOF); @@ -986,7 +987,7 @@ struct FlattenictyPikp { if (trkSelOpt.useSelTOFBeta && !isSelTOFBeta) { return false; } - if (!isSelNsigmaPi) { + if (trkSelOpt.useSelTOFnSigma && !isSelNsigmaPi) { return false; } registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hBetaVsP"), track.p(), track.beta()); From 7e385d45c7e496dcebf613a9b19c41291638a39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20=C5=A0erk=C5=A1nyt=C4=97?= <45659867+lauraser@users.noreply.github.com> Date: Wed, 23 Sep 2026 03:33:43 +0200 Subject: [PATCH 040/648] [Infrastructure] Add coordinator of EbyE to the code owners (#18007) Co-authored-by: Laura Serksnyte --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 55b281330f3..8faaf6abff2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -36,7 +36,7 @@ /PWGCF/DataModel @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser /PWGCF/TableProducer @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser /PWGCF/Tasks @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser -/PWGCF/EbyEFluctuations @alibuild @SwatiSaha-1997 @isputows @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/EbyEFluctuations @alibuild @SwatiSaha-1997 @isputows @victor-gonzalez @dsarkaralice @lauraser @fsii /PWGCF/Femto @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN /PWGCF/FemtoDream @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN /PWGCF/Femto3D @alibuild @glromane @sofiatomassini @lauraser @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN From 49936040c824d8b9255bfa223cc9944ee74f4ee0 Mon Sep 17 00:00:00 2001 From: prottayCMT <61418725+prottayCMT@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:59:40 +0200 Subject: [PATCH 041/648] [PWGLF] added process function to check effect of spatial calibration on v1 (#18015) Co-authored-by: Prottay Das --- PWGLF/Tasks/Strangeness/lambdapolsp.cxx | 212 ++++++++++++++++-------- 1 file changed, 139 insertions(+), 73 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx index 1eadddbbf48..d939c77b84c 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx @@ -14,6 +14,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/SPCalibrationTables.h" +#include "PWGLF/DataModel/ZDC2StageCalibrationTables.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" @@ -322,8 +323,6 @@ struct lambdapolsp { AxisSpec thnAxisres{binGrp.resNbins, binGrp.lbinres, binGrp.hbinres, "Reso"}; AxisSpec thnAxisInvMass{binGrp.IMNbins, binGrp.lbinIM, binGrp.hbinIM, "#it{M} (GeV/#it{c}^{2})"}; AxisSpec spAxis = {binGrp.spNbins, binGrp.lbinsp, binGrp.hbinsp, "Sp"}; - // AxisSpec qxZDCAxis = {binGrp.QxyNbins, binGrp.lbinQxy, binGrp.hbinQxy, "Qx"}; - // AxisSpec centAxis = {CentNbins, lbinCent, hbinCent, "V0M (%)"}; std::vector runaxes = {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}; if (needetaaxis) @@ -342,10 +341,6 @@ struct lambdapolsp { runaxesSyst.push_back(systIDAxis); - // if (needetaaxis) - // runaxes.insert(runaxes.end(), {axisGrp.configbinAxis}); - // std::vector runaxes2 = {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configcentAxis}; - if (checkwithpub) { if (useprofile == 2) { histos.add("hpuxQxpvscentpteta", "hpuxQxpvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); @@ -356,49 +351,8 @@ struct lambdapolsp { histos.add("hpuxyQxypvscentpteta", "hpuxyQxypvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpoddv1vscentpteta", "hpoddv1vscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpevenv1vscentpteta", "hpevenv1vscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - /*histos.add("hpv21", "hpv21", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpv22", "hpv22", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpv23", "hpv23", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tx1Ax1Cvscentpteta", "hpx2Tx1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Ay1Cvscentpteta", "hpx2Ty1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Ay1Cvscentpteta", "hpy2Tx1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Ax1Cvscentpteta", "hpy2Ty1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Ax1Cvscentpteta", "hpx1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Ay1Cvscentpteta", "hpy1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Avscentpteta", "hpx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Cvscentpteta", "hpx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Avscentpteta", "hpy1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Cvscentpteta", "hpy1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - - histos.add("hpx2Tx1Avscentpteta", "hpx2Tx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tx1Cvscentpteta", "hpx2Tx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Avscentpteta", "hpx2Ty1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Cvscentpteta", "hpx2Ty1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Avscentpteta", "hpy2Tx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Cvscentpteta", "hpy2Ty1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Avscentpteta", "hpy2Ty1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Cvscentpteta", "hpy2Tx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Ay1Cvscentpteta", "hpx1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Ax1Cvscentpteta", "hpy1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tvscentpteta", "hpx2Tvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tvscentpteta", "hpy2Tvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - */ histos.add("hpuxvscentpteta", "hpuxvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpuyvscentpteta", "hpuyvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - /* - histos.add("hpuxvscentptetaneg", "hpuxvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyvscentptetaneg", "hpuyvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - - histos.add("hpuxQxpvscentptetaneg", "hpuxQxpvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyQypvscentptetaneg", "hpuyQypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxQxtvscentptetaneg", "hpuxQxtvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyQytvscentptetaneg", "hpuyQytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxyQxytvscentptetaneg", "hpuxyQxytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxyQxypvscentptetaneg", "hpuxyQxypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpoddv1vscentptetaneg", "hpoddv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpevenv1vscentptetaneg", "hpevenv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - */ - histos.add("hpQxtQxpvscent", "hpQxtQxpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQytQypvscent", "hpQytQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQxytpvscent", "hpQxytpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); @@ -446,17 +400,6 @@ struct lambdapolsp { histos.add("hpuxvscentpteta", "hpuxvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpuyvscentpteta", "hpuyvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - /*histos.add("hpuxvscentptetaneg", "hpuxvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyvscentptetaneg", "hpuyvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - - histos.add("hpuxQxpvscentptetaneg", "hpuxQxpvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyQypvscentptetaneg", "hpuyQypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxQxtvscentptetaneg", "hpuxQxtvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyQytvscentptetaneg", "hpuyQytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxyQxytvscentptetaneg", "hpuxyQxytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxyQxypvscentptetaneg", "hpuxyQxypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpoddv1vscentptetaneg", "hpoddv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpevenv1vscentptetaneg", "hpevenv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true);*/ histos.add("hpQxtQxpvscent", "hpQxtQxpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQytQypvscent", "hpQytQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); @@ -472,14 +415,6 @@ struct lambdapolsp { } histos.add("hCentrality", "Centrality distribution", kTH1F, {{axisGrp.configcentAxis}}); - // histos.add("hpsiApsiC", "hpsiApsiC", kTHnSparseF, {psiACAxis, psiACAxis}); - // histos.add("hpsiApsiC", "hpsiApsiC", kTH2F, {psiACAxis, psiACAxis}); - // histos.add("hphiminuspsiA", "hphiminuspisA", kTH1F, {{50, 0, 6.28}}, true); - // histos.add("hphiminuspsiC", "hphiminuspisC", kTH1F, {{50, 0, 6.28}}, true); - // histos.add("hCentrality0", "Centrality distribution0", kTH1F, {{centAxis}}); - // histos.add("hCentrality1", "Centrality distribution1", kTH1F, {{centAxis}}); - // histos.add("hCentrality2", "Centrality distribution2", kTH1F, {{centAxis}}); - // histos.add("hCentrality3", "Centrality distribution3", kTH1F, {{centAxis}}); if (!checkwithpub) { // histos.add("hVtxZ", "Vertex distribution in Z;Z (cm)", kTH1F, {{20, -10.0, 10.0}}); @@ -1388,13 +1323,11 @@ struct lambdapolsp { } ROOT::Math::PxPyPzMVector Lambda, AntiLambda, Lambdadummy, AntiLambdadummy, Proton, Pion, AntiProton, AntiPion, fourVecDauCM, K0sdummy, K0s; - // double phiangle = 0.0; - // double angleLambda=0.0; - // double angleAntiLambda=0.0; - double massLambda = o2::constants::physics::MassLambda; - double massK0s = o2::constants::physics::MassK0Short; - double massPr = o2::constants::physics::MassProton; - double massPi = o2::constants::physics::MassPionCharged; + + static constexpr double massLambda = o2::constants::physics::MassLambda; + static constexpr double massK0s = o2::constants::physics::MassK0Short; + static constexpr double massPr = o2::constants::physics::MassProton; + static constexpr double massPi = o2::constants::physics::MassPionCharged; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); @@ -1402,6 +1335,7 @@ struct lambdapolsp { Filter dcaCutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); using EventCandidates = soa::Filtered>; + using EventCandidatescalibstage = soa::Filtered>; using EventCandidatesMC = soa::Filtered>; using AllTrackCandidates = soa::Filtered>; using ResoV0s = aod::V0Datas; @@ -1414,6 +1348,138 @@ struct lambdapolsp { using BCsRun3 = soa::Join; + void processDatav1(EventCandidatescalibstage::iterator const& collision, AllTrackCandidates const& tracks, BCsRun3 const&) + { + + if (!collision.sel8()) { + return; + } + double centrality = -999.; + if (centestim == 0) + centrality = collision.centFT0C(); + + if (!collision.triggerSP()) { + return; + } + + if (evselGrp.additionalEvSel && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + return; + } + if (evselGrp.additionalEvSel2 && (collision.trackOccupancyInTimeRange() > evselGrp.cfgMaxOccupancy || collision.trackOccupancyInTimeRange() < evselGrp.cfgMinOccupancy)) { + return; + } + if (evselGrp.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return; + } + + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { + return; + } + + auto qxZDCA = collision.qxZDCA(); + auto qxZDCC = collision.qxZDCC(); + auto qyZDCA = collision.qyZDCA(); + auto qyZDCC = collision.qyZDCC(); + + histos.fill(HIST("hCentrality"), centrality); + + auto QxtQxp = qxZDCA * qxZDCC; + auto QytQyp = qyZDCA * qyZDCC; + auto Qxytp = QxtQxp + QytQyp; + auto QxpQyt = qxZDCA * qyZDCC; + auto QxtQyp = qxZDCC * qyZDCA; + + histos.fill(HIST("hpQxtQxpvscent"), centrality, QxtQxp); + histos.fill(HIST("hpQytQypvscent"), centrality, QytQyp); + histos.fill(HIST("hpQxytpvscent"), centrality, Qxytp); + histos.fill(HIST("hpQxpQytvscent"), centrality, QxpQyt); + histos.fill(HIST("hpQxtQypvscent"), centrality, QxtQyp); + + int wNUA = 1; + + for (const auto& track : tracks) { + if (!selectionTrack(track)) { + continue; + } + + float sign = track.sign(); + if (sign == 0.0) // removing neutral particles + continue; + + auto ux = TMath::Cos(GetPhiInRange(track.phi())); + auto uy = TMath::Sin(GetPhiInRange(track.phi())); + + auto uxQxp = ux * qxZDCA; + auto uyQyp = uy * qyZDCA; + auto uxyQxyp = uxQxp + uyQyp; + auto uxQxt = ux * qxZDCC; + auto uyQyt = uy * qyZDCC; + auto uxyQxyt = uxQxt + uyQyt; + auto oddv1 = ux * (qxZDCA - qxZDCC) + uy * (qyZDCA - qyZDCC); + auto evenv1 = ux * (qxZDCA + qxZDCC) + uy * (qyZDCA + qyZDCC); + + if (globalpt) { + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.pt(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.pt(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.pt(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.pt(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); + } else { + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); + } + } + } + + PROCESS_SWITCH(lambdapolsp, processDatav1, "Process datav1", false); + void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& tracks, ResoV0s const& V0s, BCsRun3 const&) { From e128232d918085543b0991d0dc11b637f04c61b7 Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Wed, 23 Sep 2026 09:32:55 +0200 Subject: [PATCH 042/648] [PWGDQ] fixes to the dimuon processing in the global alignment task (#18012) --- PWGDQ/Tasks/muonGlobalAlignment.cxx | 40 +++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index 66f3b57240d..c045c1c05ff 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -2223,13 +2223,31 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc c.posX(), c.posY(), c.posZ(), c.covXX(), c.covYY(), c.covZZ()); + bool hasOppositeSignMuonPair = false; + std::vector muonPairs; + getMuonPairs(collisionInfo, muonPairs); + for (auto [mchIndex1, mchIndex2] : muonPairs) { + + int sign1 = muonTracks.rawIteratorAt(mchIndex1).sign(); + int sign2 = muonTracks.rawIteratorAt(mchIndex2).sign(); + + // only consider opposite-sign pairs + if ((sign1 * sign2) < 0) { + hasOppositeSignMuonPair = true; + break; + } + } + // loop over MCH tracks for (const auto& mchIndex : collisionInfo.mchTracks) { auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); - // only store good MCH tracks - if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { - continue; + // store all MCH tracks when the event contains a muon pair of opposite sign + if (!hasOppositeSignMuonPair) { + // only store good MCH tracks + if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { + continue; + } } fwdTable(collId, @@ -2809,18 +2827,24 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc std::vector muonPairs; getMuonPairs(collisionInfo, muonPairs); - for (const auto& [mchIndex1, mchIndex2] : muonPairs) { + for (auto [mchIndex1, mchIndex2] : muonPairs) { // o2-linter: disable=const-ref-in-for-loop (values are modified in loop) - auto const& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); - auto const& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); - int sign1 = muonTrack1.sign(); - int sign2 = muonTrack2.sign(); + int sign1 = muonTracks.rawIteratorAt(mchIndex1).sign(); + int sign2 = muonTracks.rawIteratorAt(mchIndex2).sign(); // only consider opposite-sign pairs if ((sign1 * sign2) >= 0) { continue; } + // make sure that index #1 corresponds to the positive track + if (sign1 < 0) { + std::swap(mchIndex1, mchIndex2); + } + + auto const& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); + auto const& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); + bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); From 6037ae9d9215c9c1b8d78a7f8b6e480280490df0 Mon Sep 17 00:00:00 2001 From: mcoquet642 <74600025+mcoquet642@users.noreply.github.com> Date: Wed, 23 Sep 2026 09:46:33 +0200 Subject: [PATCH 043/648] [PWGDQ] Improvements tableReader_withAssoc and dqEfficiency_withAssoc, and simplifying FillPairVertexing (#17999) Co-authored-by: ALICE Action Bot --- PWGDQ/Core/VarManager.h | 308 ++++------- .../TableProducer/tableMakerMC_withAssoc.cxx | 2 + PWGDQ/Tasks/dqEfficiency.cxx | 70 ++- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 249 ++++----- PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 22 +- PWGDQ/Tasks/tableReader.cxx | 43 +- PWGDQ/Tasks/tableReader_withAssoc.h | 485 ++++++++---------- PWGDQ/Tasks/tableReader_withAssoc_direct.cxx | 249 +++++---- 8 files changed, 637 insertions(+), 791 deletions(-) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index e1e1b96282e..f3f4d045b79 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -1514,7 +1513,7 @@ class VarManager : public TObject template static void FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& track2, float* values = nullptr); template - static void FillPairVertexing(C const& collision, T const& t1, T const& t2, bool propToSV = false, float* values = nullptr); + static void FillPairVertexing(C const& collision, T const& t1, T const& t2, float* values = nullptr); template static void FillPairVertexingRecomputePV(C const& /*collision*/, T const& t1, T const& t2, const o2::dataformats::VertexBase& pvRefitted, float* values = nullptr); template @@ -1873,7 +1872,7 @@ template void VarManager::FillPropagateMuon(const T& muon, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedMuonCov) > 0) { @@ -1915,7 +1914,7 @@ template void VarManager::FillGlobalMuonRefit(T1 const& muontrack, T2 const& mfttrack, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0) { float xShift = 0.f; @@ -1950,7 +1949,7 @@ template (fgValues); } if constexpr ((MuonfillMap & MuonCov) > 0) { if constexpr ((MFTfillMap & MFTCov) > 0) { @@ -1984,7 +1983,7 @@ template void VarManager::FillTimeFrame(T const& tf, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (T::template contains()) { values[kTFNBCs] = tf.size(); @@ -2010,7 +2009,7 @@ template void VarManager::FillBC(T const& bc, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kRunNo] = bc.runNumber(); values[kBC] = bc.globalBC(); @@ -2023,7 +2022,7 @@ template void VarManager::FillEvent(T const& event, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & CollisionTimestamp) > 0) { @@ -2690,7 +2689,7 @@ template void VarManager::FillEventTracks(T const& tracks, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // compute event properties based on DCAz of the tracks @@ -2826,7 +2825,7 @@ template void VarManager::FillEventFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (values[kCentFT0C] >= 0.) { @@ -2842,7 +2841,7 @@ template void VarManager::FillTwoMixEventsFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (values[kTwoEvCentFT0C1] >= 0.) { @@ -2866,7 +2865,7 @@ template void VarManager::FillTwoMixEventsCumulants(T const& h_v22ev1, T const& h_v24ev1, T const& h_v22ev2, T const& h_v24ev2, T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } int idx_v22ev1 = 0; @@ -2912,7 +2911,7 @@ template void VarManager::FillTwoEvents(T const& ev1, T const& ev2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // if constexpr (T::template contains()) { values[kTwoEvPosZ1] = ev1.posZ(); @@ -2932,7 +2931,7 @@ template void VarManager::FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& /*tracks1*/, T2 const& /*tracks2*/, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kTwoEvPosZ1] = ev1.posZ(); values[kTwoEvPosZ2] = ev2.posZ(); @@ -2990,7 +2989,7 @@ template void VarManager::FillTrack(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & TrackMFT) > 0) { @@ -3425,7 +3424,7 @@ void VarManager::FillTrackCollision(T const& track, C const& collision, float* v { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedTrackBarrel) > 0 || (fillMap & TrackDCA) > 0) { auto trackPar = getTrackPar(track); @@ -3476,7 +3475,7 @@ template void VarManager::FillTrackCollisionMatCorr(T const& track, C const& collision, M const& materialCorr, P const& propagator, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedTrackBarrel) > 0 || (fillMap & TrackDCA) > 0) { auto trackPar = getTrackPar(track); @@ -3504,7 +3503,7 @@ template void VarManager::FillPhoton(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Quantities based on the basic table (contains just kine information and filter bits) @@ -3540,7 +3539,7 @@ void VarManager::FillTrackEMCal(T const& cluster, float trackP, float deltaEta, // trackP: momentum of the matched track, used to compute E/p when called in a track-cluster matching context; // the default (negative) value leaves E/p at the -999 sentinel, as for tracks without a matched cluster. if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kEMCalEnergy] = cluster.energy(); @@ -3565,7 +3564,7 @@ template void VarManager::FillTrackMC(const U& mcStack, T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Quantities based on the mc particle table @@ -3599,7 +3598,7 @@ void VarManager::FillTrackCollisionMC(T1 const& track, T2 const& MotherTrack, C { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m = o2::constants::physics::MassBPlus; @@ -3653,7 +3652,7 @@ void VarManager::FillTrackCollisionMC(T1 const& track, const std::array(fgValues); } float m = o2::constants::physics::MassJPsi; @@ -3749,7 +3748,7 @@ template void VarManager::FillPairPropagateMuon(T1 const& muon1, T2 const& muon2, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } o2::dataformats::GlobalFwdTrack propmuon1 = PropagateMuon(muon1, collision); o2::dataformats::GlobalFwdTrack propmuon2 = PropagateMuon(muon2, collision); @@ -3774,7 +3773,7 @@ template void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4049,7 +4048,7 @@ template void VarManager::FillPairRotation(T1 const& t1, T2 const& t2, int rotation, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4120,7 +4119,7 @@ template void VarManager::FillPairCollision(const C& collision, T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((pairType == kDecayToEE) && ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { @@ -4172,7 +4171,7 @@ template (fgValues); } if constexpr ((pairType == kDecayToEE) && ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { @@ -4231,7 +4230,7 @@ void VarManager::FillTriple(T1 const& t1, T2 const& t2, T3 const& t3, float* val { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (pairType == kTripleCandidateToEEPhoton) { float m1 = o2::constants::physics::MassElectron; @@ -4305,7 +4304,7 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) // Lightweight fill function called from the innermost event mixing loop // if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4602,7 +4601,7 @@ template void VarManager::FillPairMEAcrossTFs(T const& t1, T const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4672,7 +4671,7 @@ template void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4847,7 +4846,7 @@ template void VarManager::FillTripleMC(T1 const& t1, T2 const& t2, T3 const& t3, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (candidateType == kTripleCandidateToEEPhoton) { @@ -4929,7 +4928,7 @@ void VarManager::FillTripleMC(T1 const& t1, T2 const& t2, T3 const& t3, float* v } template -void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, bool propToSV, float* values) +void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, float* values) { // check at compile time that the event and cov matrix have the cov matrix constexpr bool eventHasVtxCov = ((collFillMap & Collision) > 0 || (collFillMap & ReducedEventVtxCov) > 0); @@ -4937,7 +4936,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; float m2 = o2::constants::physics::MassElectron; @@ -4945,7 +4944,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, m1 = o2::constants::physics::MassKaonCharged; m2 = o2::constants::physics::MassPionCharged; } - if constexpr (pairType == kDecayToMuMu && muonHasCov) { + if constexpr (pairType == kDecayToMuMu) { m1 = o2::constants::physics::MassMuon; m2 = o2::constants::physics::MassMuon; } @@ -5010,22 +5009,16 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, std::array covMatrixPCA{}; // get track impact parameters // This modifies track momenta! - o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); - std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; - o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; // auto primaryVertex = getPrimaryVertex(collision); - auto covMatrixPV = primaryVertex.getCov(); + std::array covMatrixPV{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; if constexpr ((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) { secondaryVertex = fgFitterTwoProngBarrel.getPCACandidate(); // printf("secVtx (first) %f %f %f \n",secondaryVertex[0],secondaryVertex[1],secondaryVertex[2]); covMatrixPCA = fgFitterTwoProngBarrel.calcPCACovMatrixFlat(); - auto chi2PCA = fgFitterTwoProngBarrel.getChi2AtPCACandidate(); - auto trackParVar0 = fgFitterTwoProngBarrel.getTrack(0); - auto trackParVar1 = fgFitterTwoProngBarrel.getTrack(1); - values[kVertexingChi2PCA] = chi2PCA; - v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; - v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; + values[kVertexingChi2PCA] = fgFitterTwoProngBarrel.getChi2AtPCACandidate(); + v1 = {fgFitterTwoProngBarrel.getTrack(0).getPt(), fgFitterTwoProngBarrel.getTrack(0).getEta(), fgFitterTwoProngBarrel.getTrack(0).getPhi(), m1}; + v2 = {fgFitterTwoProngBarrel.getTrack(1).getPt(), fgFitterTwoProngBarrel.getTrack(1).getEta(), fgFitterTwoProngBarrel.getTrack(1).getPhi(), m2}; v12 = v1 + v2; if (fgPVrecalKF) { primaryVertexNew = RecalculatePrimaryVertex(t1, t2, collision); @@ -5035,40 +5028,29 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, // Get pca candidate from forward DCA fitter secondaryVertex = fgFitterTwoProngFwd.getPCACandidate(); covMatrixPCA = fgFitterTwoProngFwd.calcPCACovMatrixFlat(); - auto chi2PCA = fgFitterTwoProngFwd.getChi2AtPCACandidate(); - auto trackParVar0 = fgFitterTwoProngFwd.getTrack(0); - auto trackParVar1 = fgFitterTwoProngFwd.getTrack(1); - values[kVertexingChi2PCA] = chi2PCA; - v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; - v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; + values[kVertexingChi2PCA] = fgFitterTwoProngFwd.getChi2AtPCACandidate(); + v1 = {fgFitterTwoProngFwd.getTrack(0).getPt(), fgFitterTwoProngFwd.getTrack(0).getEta(), fgFitterTwoProngFwd.getTrack(0).getPhi(), m1}; + v2 = {fgFitterTwoProngFwd.getTrack(1).getPt(), fgFitterTwoProngFwd.getTrack(1).getEta(), fgFitterTwoProngFwd.getTrack(1).getPhi(), m2}; v12 = v1 + v2; - - values[kPt1] = trackParVar0.getPt(); - values[kEta1] = trackParVar0.getEta(); - values[kPhi1] = trackParVar0.getPhi(); - - values[kPt2] = trackParVar1.getPt(); - values[kEta2] = trackParVar1.getEta(); - values[kPhi2] = trackParVar1.getPhi(); } - double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); - double theta = std::atan2(secondaryVertex[2] - collision.posZ(), - std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + - (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + const double dx = secondaryVertex[0] - collision.posX(); + const double dy = secondaryVertex[1] - collision.posY(); + const double dz = secondaryVertex[2] - collision.posZ(); + double phi = std::atan2(dy, dx); + double theta = std::atan2(dz, std::sqrt(dx * dx + dy * dy)); values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); - values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + - (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); - values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); + values[kVertexingLxy] = dx * dx + dy * dy; + values[kVertexingLz] = dz * dz; values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); values[kVertexingLz] = std::sqrt(values[kVertexingLz]); values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); - values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauz] = -dz * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauxy] = values[kVertexingLxy] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); values[kVertexingPz] = TMath::Abs(v12.Pz()); @@ -5077,63 +5059,43 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingTauzErr] = values[kVertexingLzErr] * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v12.Px() + - (secondaryVertex[1] - collision.posY()) * v12.Py() + - (secondaryVertex[2] - collision.posZ()) * v12.Pz()) / + values[kCosPointingAngle] = (dx * v12.Px() + dy * v12.Py() + dz * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); // Decay length defined as in Run 2 - values[kVertexingLzProjected] = ((secondaryVertex[2] - collision.posZ()) * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); - values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()); + values[kVertexingLzProjected] = (dz * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); + values[kVertexingLxyProjected] = (dx * v12.Px()) + (dy * v12.Py()); values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py())); - values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()) + ((secondaryVertex[2] - collision.posZ()) * v12.Pz()); + values[kVertexingLxyzProjected] = (dx * v12.Px()) + (dy * v12.Py()) + (dz * v12.Pz()); values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py()) + (v12.Pz() * v12.Pz())); - if (fgPVrecalKF) { - values[kVertexingLxyProjectedRecalculatePV] = (secondaryVertex[0] - primaryVertexNew.getX()) * v12.Px() + (secondaryVertex[1] - primaryVertexNew.getY()) * v12.Py(); - values[kVertexingLxyProjectedRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] / v12.Pt(); - } values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v12.M() / (v12.Pt()); values[kVertexingTauxyProjectedPoleJPsiMass] = values[kVertexingLxyProjected] * o2::constants::physics::MassJPsi / (v12.Pt()); values[kVertexingTauxyProjectedNs] = values[kVertexingTauxyProjected] / o2::constants::physics::LightSpeedCm2NS; if (fgPVrecalKF) { + values[kVertexingLxyProjectedRecalculatePV] = (secondaryVertex[0] - primaryVertexNew.getX()) * v12.Px() + (secondaryVertex[1] - primaryVertexNew.getY()) * v12.Py(); + values[kVertexingLxyProjectedRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] / v12.Pt(); values[kVertexingTauxyProjectedPoleJPsiMassRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] * o2::constants::physics::MassJPsi / (v12.Pt()); } values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v12.M() / TMath::Abs(v12.Pz()); values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v12.M() / (v12.P()); } - } else { + } else if constexpr (((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) || ((pairType == kDecayToMuMu) && muonHasCov)) { KFParticle trk0KF; KFParticle trk1KF; KFParticle KFGeoTwoProng; - if constexpr ((pairType == kDecayToEE) && trackHasCov) { - KFPTrack kfpTrack0 = createKFPTrackFromTrack(t1); - trk0KF = KFParticle(kfpTrack0, -11 * t1.sign()); - KFPTrack kfpTrack1 = createKFPTrackFromTrack(t2); - trk1KF = KFParticle(kfpTrack1, -11 * t2.sign()); + if constexpr (pairType == kDecayToEE) { + trk0KF = KFParticle(createKFPTrackFromTrack(t1), -11 * t1.sign()); + trk1KF = KFParticle(createKFPTrackFromTrack(t2), -11 * t2.sign()); + } else if constexpr (pairType == kDecayToMuMu) { + trk0KF = KFParticle(createKFPFwdTrackFromFwdTrack(t1), -13 * t1.sign()); + trk1KF = KFParticle(createKFPFwdTrackFromFwdTrack(t2), -13 * t2.sign()); + } else if constexpr (pairType == kDecayToKPi) { + trk0KF = KFParticle(createKFPTrackFromTrack(t1), 321 * t1.sign()); + trk1KF = KFParticle(createKFPTrackFromTrack(t2), 211 * t2.sign()); + } + KFGeoTwoProng.SetConstructMethod(2); + KFGeoTwoProng.AddDaughter(trk0KF); + KFGeoTwoProng.AddDaughter(trk1KF); - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); - - } else if constexpr ((pairType == kDecayToMuMu) && muonHasCov) { - KFPTrack kfpTrack0 = createKFPFwdTrackFromFwdTrack(t1); - trk0KF = KFParticle(kfpTrack0, -13 * t1.sign()); - KFPTrack kfpTrack1 = createKFPFwdTrackFromFwdTrack(t2); - trk1KF = KFParticle(kfpTrack1, -13 * t2.sign()); - - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); - - } else if constexpr ((pairType == kDecayToKPi) && trackHasCov) { - KFPTrack kfpTrack0 = createKFPTrackFromTrack(t1); - trk0KF = KFParticle(kfpTrack0, 321 * t1.sign()); - KFPTrack kfpTrack1 = createKFPTrackFromTrack(t2); - trk1KF = KFParticle(kfpTrack1, 211 * t2.sign()); - - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); - } if (fgUsedVars[kKFMass]) { float mass = 0., massErr = 0.; if (!KFGeoTwoProng.GetMass(mass, massErr)) { @@ -5257,70 +5219,28 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kKFMassGeoTop] = -999.; } } - if (propToSV) { - if constexpr ((pairType == kDecayToMuMu) && muonHasCov) { - o2::track::TrackParCovFwd pars1 = FwdToTrackPar(t1, t1); - o2::track::TrackParCovFwd pars2 = FwdToTrackPar(t2, t2); - - auto geoMan1 = o2::base::GeometryManager::meanMaterialBudget(t1.x(), t1.y(), t1.z(), KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()); - auto geoMan2 = o2::base::GeometryManager::meanMaterialBudget(t2.x(), t2.y(), t2.z(), KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()); - auto x2x01 = static_cast(geoMan1.meanX2X0); - auto x2x02 = static_cast(geoMan2.meanX2X0); - std::array B{}; - std::array xyz = {0.f, 0.f, 0.f}; - KFGeoTwoProng.GetFieldValue(xyz.data(), B.data()); - // TODO: find better soluton to handle cases where KF outputs negative variances - /*float covXX = 0.1; - float covYY = 0.1; - if (KFGeoTwoProng.GetCovariance(0, 0) > 0) { - covXX = KFGeoTwoProng.GetCovariance(0, 0); - } - if (KFGeoTwoProng.GetCovariance(1, 1) > 0) { - covYY = KFGeoTwoProng.GetCovariance(0, 0); - }*/ - pars1.propagateToVtxhelixWithMCS(KFGeoTwoProng.GetZ(), {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY()}, {KFGeoTwoProng.GetCovariance(0, 0), KFGeoTwoProng.GetCovariance(1, 1)}, B[2], x2x01); - pars2.propagateToVtxhelixWithMCS(KFGeoTwoProng.GetZ(), {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY()}, {KFGeoTwoProng.GetCovariance(0, 0), KFGeoTwoProng.GetCovariance(1, 1)}, B[2], x2x02); - v1 = {pars1.getPt(), pars1.getEta(), pars1.getPhi(), m1}; - v2 = {pars2.getPt(), pars2.getEta(), pars2.getPhi(), m2}; - v12 = v1 + v2; - values[kMass] = v12.M(); - values[kPt] = v12.Pt(); - values[kEta] = v12.Eta(); - values[kPhi] = v12.Phi(); - values[kRap] = -v12.Rapidity(); - values[kVertexingTauxy] = KFGeoTwoProng.GetPseudoProperDecayTime(KFPV, v12.M()) / (o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauz] = -1 * dzPair2PV * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauzErr] = values[kVertexingLzErr] * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingPz] = TMath::Abs(v12.Pz()); - values[kVertexingSV] = KFGeoTwoProng.GetZ(); - - values[kPt1] = pars1.getPt(); - values[kEta1] = pars1.getEta(); - values[kPhi1] = pars1.getPhi(); - - values[kPt2] = pars2.getPt(); - values[kEta2] = pars2.getEta(); - values[kPhi2] = pars2.getPhi(); - } - } - } - } - if (propToSV) { - values[kMass] = v12.M(); - values[kPt] = v12.Pt(); - values[kEta] = v12.Eta(); - // values[kPhi] = v12.Phi(); - values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); - } else { - values[kPt1] = t1.pt(); - values[kEta1] = t1.eta(); - values[kPhi1] = t1.phi(); - values[kPt2] = t2.pt(); - values[kEta2] = t2.eta(); - values[kPhi2] = t2.phi(); + float sv[3] = {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()}; + KFParticle trk0AtSV = trk0KF; + KFParticle trk1AtSV = trk1KF; + trk0AtSV.TransportToPoint(sv); + trk1AtSV.TransportToPoint(sv); + + v1 = {trk0AtSV.GetPt(), trk0AtSV.GetEta(), trk0AtSV.GetPhi(), m1}; + v2 = {trk1AtSV.GetPt(), trk1AtSV.GetEta(), trk1AtSV.GetPhi(), m2}; + v12 = v1 + v2; + } } + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kPt1] = v1.Pt(); + values[kEta1] = v1.Eta(); + values[kPhi1] = v1.Phi(); + values[kPt2] = v2.Pt(); + values[kEta2] = v2.Eta(); + values[kPhi2] = v2.Phi(); } template @@ -5334,7 +5254,7 @@ void VarManager::FillPairVertexingRecomputePV(C const& /*collision*/, T const& t constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -5395,7 +5315,7 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& bool trackHasCov = ((fillMap & ReducedTrackBarrelCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassKaonCharged; @@ -5618,7 +5538,7 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton constexpr bool trackHasCov = ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0); constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float mtrack = o2::constants::physics::MassMuon; @@ -5882,7 +5802,7 @@ template void VarManager::FillQVectorFromGFW(C const& /*collision*/, A const& compA11, A const& compB11, A const& compC11, A const& compA21, A const& compB21, A const& compC21, A const& compA31, A const& compB31, A const& compC31, A const& compA41, A const& compB41, A const& compC41, A const& compA23, A const& compA42, float S10A, float S10B, float S10C, float S11A, float S11B, float S11C, float S12A, float S13A, float S14A, float S21A, float S22A, float S31A, float S41A, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Fill Qn vectors from generic flow framework for different eta gap A, B, C (n=1,2,3,4) with proper normalisation @@ -5987,7 +5907,7 @@ template void VarManager::FillQVectorFromCentralFW(C const& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float xQVecFT0a = collision.qvecFT0ARe(); // already normalised @@ -6083,7 +6003,7 @@ template void VarManager::FillSpectatorPlane(C const& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } auto zncEnergy = collision.energySectorZNC(); @@ -6191,7 +6111,7 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -6440,7 +6360,7 @@ template void VarManager::FillZDC(T const& zdc, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kEnergyCommonZNA] = (zdc.energyCommonZNA() > 0) ? zdc.energyCommonZNA() : -1.; @@ -6457,7 +6377,7 @@ template void VarManager::FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* values, float hadronMass) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (fgUsedVars[kPairMass] || fgUsedVars[kPairPt] || fgUsedVars[kPairEta] || fgUsedVars[kPairPhi] || fgUsedVars[kPairMassDau] || fgUsedVars[kPairPtDau] || fgUsedVars[kDileptonHadronKstar]) { @@ -6498,10 +6418,10 @@ template (fgValues); } if (fgUsedVars[kVertexingTauxyProjectedPoleJPsiMass] || fgUsedVars[kVertexingLxyProjected]) { - FillPairVertexing(collision, lepton1, lepton2, false, values); + FillPairVertexing(collision, lepton1, lepton2, values); } float m1 = o2::constants::physics::MassElectron; float m2 = o2::constants::physics::MassElectron; @@ -6629,7 +6549,7 @@ template void VarManager::FillDileptonPhoton(T1 const& dilepton, T2 const& photon, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (fgUsedVars[kPairMass] || fgUsedVars[kPairPt] || fgUsedVars[kPairEta] || fgUsedVars[kPairPhi]) { ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.mass()); @@ -6656,7 +6576,7 @@ template void VarManager::FillHadron(T const& hadron, float* values, float hadronMass) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } ROOT::Math::PtEtaPhiMVector vhadron(hadron.pt(), hadron.eta(), hadron.phi(), hadronMass); @@ -6671,7 +6591,7 @@ template void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (partType == kJPsi) { @@ -6714,7 +6634,7 @@ template void VarManager::FillDileptonTrackTrack(T1 const& dilepton, T2 const& hadron1, T3 const& hadron2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } double defaultDileptonMass = o2::constants::physics::MassJPsi; @@ -6776,7 +6696,7 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l } if (!values) { - values = fgValues; + values = static_cast(fgValues); } float mlepton1 = o2::constants::physics::MassElectron; @@ -7026,7 +6946,7 @@ template void VarManager::FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& track2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } double defaultDileptonMass = o2::constants::physics::MassJPsi; @@ -7177,7 +7097,7 @@ template void VarManager::FillBdtScore(T1 const& bdtScore, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (bdtScore.size() == 1) { @@ -7218,7 +7138,7 @@ template void VarManager::FillFIT(T1 const& bc, T2 const& bcs, T3 const& ft0s, T4 const& fv0as, T5 const& fdds, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Initialize FIT info structure @@ -7278,7 +7198,7 @@ template void VarManager::FillEventAlice3(T const& event, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & CollisionTimestamp) > 0) { values[kTimestamp] = event.timestamp(); @@ -7366,7 +7286,7 @@ template void VarManager::FillTrackAlice3(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & Track) > 0 || (fillMap & ReducedTrack) > 0) { @@ -7519,7 +7439,7 @@ template void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -7730,7 +7650,7 @@ template void VarManager::FillResolutions(M const& mcTrack, T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kDeltaPt] = track.pt() - mcTrack.pt(); @@ -7747,7 +7667,7 @@ void VarManager::FillPairVertexingAlice3(C const& collision, T const& t1, T cons constexpr bool trackHasCov = ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; float m2 = o2::constants::physics::MassElectron; @@ -7904,7 +7824,7 @@ void VarManager::FillTripletVertexingALICE3(C const& collision, T const& t1, T c bool trackHasCov = ((fillMap & ReducedTrackBarrelCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassKaonCharged; diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index ad9fdc91f8d..d4c7f32de57 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -1427,6 +1427,8 @@ struct TableMakerMC { if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { if (fConfigVariousOptions.fUseML.value) { skimBestMuonMatchesML(muons, mftTracks, mftCovs, collision); + } else { + skimBestMuonMatches(muons); } } else { skimBestMuonMatches(muons); diff --git a/PWGDQ/Tasks/dqEfficiency.cxx b/PWGDQ/Tasks/dqEfficiency.cxx index 32548b335d5..afcb0388c08 100644 --- a/PWGDQ/Tasks/dqEfficiency.cxx +++ b/PWGDQ/Tasks/dqEfficiency.cxx @@ -103,8 +103,8 @@ struct AnalysisEventSelection { Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; - AnalysisCompositeCut* fEventCut; + HistogramManager* fHistMan = nullptr; + AnalysisCompositeCut* fEventCut = nullptr; void init(o2::framework::InitContext& context) { @@ -174,7 +174,7 @@ struct AnalysisTrackSelection { Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; @@ -305,13 +305,13 @@ struct AnalysisTrackSelection { } // fill histograms - for (unsigned int i = 0; i < fMCSignals.size(); i++) { - if (!(mcDecision & (static_cast(1) << i))) { + for (unsigned int iMC = 0; iMC < fMCSignals.size(); iMC++) { + if (!(mcDecision & (static_cast(1) << iMC))) { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { if (filterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(fHistNamesMCMatched[j][i].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts } // end loop over MC signals @@ -343,7 +343,7 @@ struct AnalysisMuonSelection { Configurable fConfigMCSignals{"cfgMuonMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; @@ -478,13 +478,13 @@ struct AnalysisMuonSelection { } // fill histograms - for (unsigned int i = 0; i < fMCSignals.size(); i++) { - if (!(mcDecision & (static_cast(1) << i))) { + for (unsigned int iMC = 0; iMC < fMCSignals.size(); iMC++) { + if (!(mcDecision & (static_cast(1) << iMC))) { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { if (filterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(fHistNamesMCMatched[j][i].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts } // end loop over MC signals @@ -516,7 +516,7 @@ struct AnalysisSameEventPairing { float mMagField = 0.0; o2::parameters::GRPMagField* grpmag = nullptr; o2::base::MatLayerCylSet* lut = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; @@ -546,7 +546,7 @@ struct AnalysisSameEventPairing { // TODO: special AnalysisCuts to be prepared in this direction // TODO: cuts on the MC truth information to be added if needed - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector> fBarrelHistNames; std::vector> fBarrelHistNamesMCmatched; std::vector> fMuonHistNames; @@ -795,7 +795,7 @@ struct AnalysisSameEventPairing { VarManager::FillPair(t1, t2); // secondary vertexing is not implemented for e-mu pairs so we need to hide this function from the e-mu analysis for now if constexpr ((TPairType == VarManager::kDecayToEE) || (TPairType == VarManager::kDecayToMuMu)) { - VarManager::FillPairVertexing(event, t1, t2, fPropToPCA, VarManager::fgValues); + VarManager::FillPairVertexing(event, t1, t2, VarManager::fgValues); } // run MC matching for this pair @@ -816,7 +816,7 @@ struct AnalysisSameEventPairing { dileptonFilterMap = twoTrackFilter; dileptonMcDecision = mcDecision; - if (!fConfigSkimSignalOnly || (fConfigSkimSignalOnly && mcDecision > 0)) { + if (!fConfigSkimSignalOnly || mcDecision > 0) { if constexpr (TPairType == VarManager::kDecayToEE) { dielectronList(event, VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), dileptonFilterMap, dileptonMcDecision); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackCollInfo) > 0) { @@ -882,11 +882,11 @@ struct AnalysisSameEventPairing { if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { fHistMan->FillHistClass(Form("%s_unambiguous", histNames[icut][0].Data()), VarManager::fgValues); } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMCmatched[icut][isig].Data(), VarManager::fgValues); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(histNamesMCmatched[icut][iSig].Data(), VarManager::fgValues); if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { - fHistMan->FillHistClass(Form("%s_unambiguous", histNamesMCmatched[icut][isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("%s_unambiguous", histNamesMCmatched[icut][iSig].Data()), VarManager::fgValues); } } } @@ -1071,12 +1071,10 @@ struct AnalysisDileptonTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesTrack; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesTrack = nullptr; + HistogramManager* fHistMan = nullptr; - std::vector> fMuonHistNames; - std::vector> fMuonHistNamesMCmatched; std::vector fRecMCSignalsNames; std::vector fRecMCSignals; @@ -1085,7 +1083,7 @@ struct AnalysisDileptonTrack { // NOTE: the barrel track filter is shared between the filters for dilepton electron candidates (first n-bits) // and the associated hadrons (n+1 bit) --> see the barrel track selection task // The current condition should be replaced when bitwise operators will become available in Filter expressions - int fNHadronCutBit; + int fNHadronCutBit = 0; void init(o2::framework::InitContext& context) { @@ -1242,9 +1240,9 @@ struct AnalysisDileptonTrack { } } // end loop over MC signals - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonsSelected_matchedMC_%s", fRecMCSignalsNames[isig].Data()), fValuesDilepton); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("DileptonsSelected_matchedMC_%s", fRecMCSignalsNames[iSig].Data()), fValuesDilepton); } } @@ -1267,7 +1265,7 @@ struct AnalysisDileptonTrack { mcDecision = 0; isig = 0; for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if constexpr (TTrackFillMap & VarManager::ObjTypes::ReducedTrack || TTrackFillMap & VarManager::ObjTypes::ReducedMuon || TTrackFillMap & VarManager::ObjTypes::ReducedMuon) { // for skimmed DQ model + if constexpr (TTrackFillMap & VarManager::ObjTypes::ReducedTrack || TTrackFillMap & VarManager::ObjTypes::ReducedMuon) { // for skimmed DQ model if ((*sig).CheckSignal(false, lepton1MC, lepton2MC, trackMC)) { mcDecision |= (static_cast(1) << isig); } @@ -1278,9 +1276,9 @@ struct AnalysisDileptonTrack { dileptontrackcandidatesList(mcDecision, fValuesTrack[VarManager::kPairMass], fValuesTrack[VarManager::kPairPt], fValuesTrack[VarManager::kPairEta], fValuesTrack[VarManager::kVertexingTauz], fValuesTrack[VarManager::kVertexingTauxy], fValuesTrack[VarManager::kVertexingLz], fValuesTrack[VarManager::kVertexingLxy]); } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackInvMass_matchedMC_%s", fRecMCSignalsNames[isig].Data()), fValuesTrack); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("DileptonTrackInvMass_matchedMC_%s", fRecMCSignalsNames[iSig].Data()), fValuesTrack); } } } @@ -1357,7 +1355,7 @@ struct AnalysisDileptonTrackTrack { Configurable fConfigUseDCAVertexing{"cfgUseDCAVertexing", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; Produces DileptonTrackTrackTable; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fRecMCSignalsNames; std::vector fRecMCSignals; @@ -1368,7 +1366,7 @@ struct AnalysisDileptonTrackTrack { Filter dileptonFilter = aod::reducedpair::sign == 0; Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > 0; - float* fValuesQuadruplet; + float* fValuesQuadruplet = nullptr; std::vector fQuadrupletCutNames; AnalysisCompositeCut fDileptonCut; @@ -1585,9 +1583,9 @@ struct AnalysisDileptonTrackTrack { mcDecision |= (static_cast(1) << isig); } } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("MCTruthRecQuad_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignalsNames[isig].Data()), fValuesQuadruplet); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("MCTruthRecQuad_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignalsNames[iSig].Data()), fValuesQuadruplet); } } } diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 57b142eda98..8f5c01923aa 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -460,7 +460,9 @@ struct AnalysisEventSelection { collisionSplittingMap[*ev1It] = true; collisionSplittingMap[*ev2It] = true; } - fHistMan->FillHistClass("SameBunchCorrelations", dqefficiency_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass("SameBunchCorrelations", dqefficiency_helpers::varValues()); + } } // end second event loop } // end first event loop } // end if BC1 events > 1 @@ -485,7 +487,9 @@ struct AnalysisEventSelection { collisionSplittingMap[ev1It] = true; collisionSplittingMap[ev2It] = true; } - fHistMan->FillHistClass("OutOfBunchCorrelations", dqefficiency_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass("OutOfBunchCorrelations", dqefficiency_helpers::varValues()); + } } } } @@ -592,7 +596,7 @@ struct AnalysisTrackSelection { if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto const& t : addTrackCuts) { - fTrackCuts.push_back(static_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -873,15 +877,13 @@ struct AnalysisMuonSelection { Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fConfigMCSignals{"cfgMuonMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigMCSignalsJSON{"cfgMuonMCsignalsJSON", "", "Additional list of MC signals via JSON"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Service fCCDB{}; HistogramManager* fHistMan = nullptr; @@ -890,7 +892,7 @@ struct AnalysisMuonSelection { std::vector fHistNamesMCMatched; std::vector fMCSignals; // list of signals to be checked - int fCurrentRun = 0; // current run kept to detect run changes and trigger loading params from CCDB + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) @@ -915,7 +917,7 @@ struct AnalysisMuonSelection { if (addCutsStr != "") { std::vector addCuts = dqcuts::GetCutsFromJSON(addCutsStr.Data()); for (auto const& t : addCuts) { - fMuonCuts.push_back(static_cast(t)); + fMuonCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -979,29 +981,18 @@ struct AnalysisMuonSelection { fCCDB->setCaching(true); fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigGeoPath); - } } template void runMuonSelection(ReducedMuonsAssoc const& assocs, TEvents const& events, TMuons const& muons, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& muonsMC) { + fNAssocsInBunch.clear(); + fNAssocsOutOfBunch.clear(); + if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { - auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); - if (grpmag != nullptr) { - o2::base::Propagator::initFieldFromGRP(grpmag); - VarManager::SetMagneticField(grpmag->getNominalL3Field()); - } else { - // LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); - // If the magnetic field is not found it is configured by had by the user - VarManager::SetMagneticField(fConfigMagField.value); - } fCurrentRun = events.begin().runNumber(); } - fNAssocsInBunch.clear(); - fNAssocsOutOfBunch.clear(); muonSel.reserve(assocs.size()); for (auto const& assoc : assocs) { @@ -1116,7 +1107,9 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("Muon_AmbiguityInBunch", dqefficiency_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass("Muon_AmbiguityInBunch", dqefficiency_helpers::varValues()); + } } // end loop over in-bunch ambiguous tracks for (auto const& [trackIdx, evIndices] : fNAssocsOutOfBunch) { @@ -1127,7 +1120,9 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("Muon_AmbiguityOutOfBunch", dqefficiency_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass("Muon_AmbiguityOutOfBunch", dqefficiency_helpers::varValues()); + } } // end loop over out-of-bunch ambiguous tracks // publish the ambiguity table @@ -1610,18 +1605,13 @@ struct AnalysisSameEventPairing { Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; } fConfigCCDB; struct : ConfigurableGroup { - Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable magField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; Configurable polarTables{"cfgPolarTables", false, "Produce tables with dilepton polarization information"}; Configurable useKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; Configurable useAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable propToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - Configurable corrFullGeo{"cfgCorrFullGeo", false, "Use full geometry to correct for MCS effects in track propagation"}; Configurable noCorr{"cfgNoCorrFwdProp", false, "Do not correct for MCS effects in track propagation"}; Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; @@ -1700,7 +1690,7 @@ struct AnalysisSameEventPairing { VarManager::SetDefaultVarNames(); fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed"); - fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed"); + fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); fEnableBarrelMuonHistos = context.mOptions.get("processElectronMuonSkimmed"); fEnableBarrelMuonMixingHistos = context.mOptions.get("processMixingElectronMuonSkimmed"); @@ -2142,10 +2132,6 @@ struct AnalysisSameEventPairing { if (fConfigOptions.noCorr) { VarManager::SetupFwdDCAFitterNoCorr(); - } else if (fConfigOptions.corrFullGeo || (fConfigOptions.useKFVertexing && fConfigOptions.propToPCA)) { - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigCCDB.geoPath); - } } else { fLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); VarManager::SetupMatLUTFwdDCAFitter(fLUT); @@ -2165,35 +2151,22 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, bool withTwoProngFitter = true) { - if (fConfigOptions.useRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations - } + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigOptions.magField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(fConfigOptions.magField.value, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (withTwoProngFitter) { + if (fConfigOptions.useKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables + VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); } + } else { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } } @@ -2333,7 +2306,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -2425,7 +2398,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -2438,36 +2411,34 @@ struct AnalysisSameEventPairing { dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } - if constexpr (TTwoProngFitter) { - dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); - if (fConfigOptions.flatTables.value && t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { - dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), - event.selection_raw(), evSel, - event.reducedMCevent().mcPosX(), event.reducedMCevent().mcPosY(), event.reducedMCevent().mcPosZ(), - VarManager::fgValues[VarManager::kMass], - mcDecision, - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], - VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], - VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], - VarManager::fgValues[VarManager::kCosPointingAngle], - VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), - VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), - t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), - t1.mcMask(), t2.mcMask(), - t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), - t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), - t1.chi2(), t2.chi2(), - t1.reducedMCTrack().pt(), t1.reducedMCTrack().eta(), t1.reducedMCTrack().phi(), t1.reducedMCTrack().e(), - t2.reducedMCTrack().pt(), t2.reducedMCTrack().eta(), t2.reducedMCTrack().phi(), t2.reducedMCTrack().e(), - t1.reducedMCTrack().vx(), t1.reducedMCTrack().vy(), t1.reducedMCTrack().vz(), t1.reducedMCTrack().vt(), - t2.reducedMCTrack().vx(), t2.reducedMCTrack().vy(), t2.reducedMCTrack().vz(), t2.reducedMCTrack().vt(), - (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), - isCorrectAssoc_leg1, isCorrectAssoc_leg2, - -999.0, -999.0, -999.0, -999.0, -999.0, - -999.0, -999.0, -999.0, -999.0, -999.0, - -999.0, VarManager::fgValues[VarManager::kMultDimuons], - VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); - } + dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); + if (fConfigOptions.flatTables.value && t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { + dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), + event.selection_raw(), evSel, + event.reducedMCevent().mcPosX(), event.reducedMCevent().mcPosY(), event.reducedMCevent().mcPosZ(), + VarManager::fgValues[VarManager::kMass], + mcDecision, + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], + VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], + VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), + VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), + t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), + t1.mcMask(), t2.mcMask(), + t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), + t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), + t1.chi2(), t2.chi2(), + t1.reducedMCTrack().pt(), t1.reducedMCTrack().eta(), t1.reducedMCTrack().phi(), t1.reducedMCTrack().e(), + t2.reducedMCTrack().pt(), t2.reducedMCTrack().eta(), t2.reducedMCTrack().phi(), t2.reducedMCTrack().e(), + t1.reducedMCTrack().vx(), t1.reducedMCTrack().vy(), t1.reducedMCTrack().vz(), t1.reducedMCTrack().vt(), + t2.reducedMCTrack().vx(), t2.reducedMCTrack().vy(), t2.reducedMCTrack().vz(), t2.reducedMCTrack().vt(), + (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), + isCorrectAssoc_leg1, isCorrectAssoc_leg2, + -999.0, -999.0, -999.0, -999.0, -999.0, + -999.0, -999.0, -999.0, -999.0, -999.0, + -999.0, VarManager::fgValues[VarManager::kMultDimuons], + VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); } } // Fill histograms @@ -3083,13 +3054,19 @@ struct AnalysisSameEventPairing { void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); // Feature replaced by processMCGen /* if (fConfigMC.runMCGenPair) { runMCGen(mcEvents, mcTracks); }*/ } + void processMuonOnlyVertexingSkimmed(MyEventsVtxCovSelected const& events, + soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) + { + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); + } + PresliceUnsorted perReducedMcGenEvent = aod::reducedtrackMC::reducedMCeventId; void processMCGen(soa::Filtered const& events, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) @@ -3334,6 +3311,7 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmed, "Run barrel only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithCollSkimmed, "Run barrel only pairing, with skimmed tracks and with collision information", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlyVertexingSkimmed, "Run muon only pairing with two-prong vertexing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processElectronMuonSkimmed, "Run electron-muon pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMixingElectronMuonSkimmed, "Run electron-muon mixing pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); @@ -3372,12 +3350,9 @@ struct AnalysisAsymmetricPairing { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigGRPMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Choose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; Configurable fConfigUseAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable fConfigPropToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; Configurable fConfigLutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; @@ -3459,7 +3434,7 @@ struct AnalysisAsymmetricPairing { if (addPairCutsStr != "") { std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); for (auto const& t : addPairCuts) { - fPairCuts.push_back(static_cast(t)); + fPairCuts.push_back(dynamic_cast(t)); cutNamesStr += Form(",%s", t->GetName()); } } @@ -3780,40 +3755,24 @@ struct AnalysisAsymmetricPairing { void initParamsFromCCDB(uint64_t timestamp, bool isTriplets) { - if (fConfigUseRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (isTriplets) { + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); } } else { - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables } } } @@ -3926,7 +3885,7 @@ struct AnalysisAsymmetricPairing { VarManager::FillPair(t1, t2); if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } // Fill histograms @@ -4368,9 +4327,7 @@ struct AnalysisDileptonTrack { Configurable fConfigHistogramSubgroups{"cfgDileptonTrackHistogramsSubgroups", "invmass,vertexing", "Comma separated list of dilepton-track histogram subgroups"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -4744,25 +4701,17 @@ struct AnalysisDileptonTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); + } else { + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index d3a968e28a8..ba62d644f59 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -308,7 +308,7 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con template void PrintBitMap(TMap map, int nbits) { - std::string msg = ""; + std::string msg; for (int i = 0; i < nbits; i++) { msg += ((map & (TMap(1) << i)) > 0 ? "1" : "0"); } @@ -631,7 +631,7 @@ struct AnalysisTrackSelection { if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (const auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -826,11 +826,11 @@ struct AnalysisTrackSelection { // cout << " Cut matched, filling histograms" << endl; if (isCorrectAssoc) { // cout << " Correct association" << endl; - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), static_cast(VarManager::fgValues)); // cout << " Filled histogram dir: " << fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data() << endl; } else { // cout << " Incorrect association" << endl; - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), static_cast(VarManager::fgValues)); // cout << " Filled histogram dir: " << fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data() << endl; } } @@ -1062,7 +1062,7 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } @@ -1110,7 +1110,7 @@ struct AnalysisPrefilterSelection { // auto track = assoc.template track_as(); mymap = -1; // if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { - if (fPrefilterMap.find(assoc.trackId()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(assoc.trackId())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else // mymap = ~fPrefilterMap[track.globalIndex()]; mymap = ~fPrefilterMap[assoc.trackId()]; @@ -1828,7 +1828,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); if (fConfigOptions.recomputePV) { VarManager::SetPVrecalculationKF(false); VarManager::ResetValues(VarManager::kVertexingLxyProjectedRecalculatePV, VarManager::kVertexingLxyProjectedRecalculatePV + 1); @@ -1921,7 +1921,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1984,7 +1984,7 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), static_cast(VarManager::fgValues)); // matched signal /*if (fConfigOptions.fConfigMiniTree) { if constexpr (TPairType == VarManager::kDecayToMuMu) { twoTrackFilter = a1.isMuonSelected_raw() & a2.isMuonSelected_raw() & fMuonFilterMask; @@ -2372,7 +2372,7 @@ struct AnalysisDileptonTrack { std::vector fPairCutNames; std::vector fCommonPairCutNames; - Service fCCDB; + Service fCCDB{}; // TODO: The filter expressions seem to always use the default value of configurables, not the values from the actual configuration file Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); @@ -2648,7 +2648,7 @@ struct AnalysisDileptonTrack { for (int iCutTrack = 0; iCutTrack < fNCuts; iCutTrack++) { // here we check that this track cut is one of those required to associate with the dileptons - if (!(fTrackCutBitMap & (static_cast(1) << iCutTrack))) { + if ((fTrackCutBitMap & (static_cast(1) << iCutTrack)) == 0) { continue; } diff --git a/PWGDQ/Tasks/tableReader.cxx b/PWGDQ/Tasks/tableReader.cxx index ef3e6713b2f..e9deca68d91 100644 --- a/PWGDQ/Tasks/tableReader.cxx +++ b/PWGDQ/Tasks/tableReader.cxx @@ -177,8 +177,8 @@ struct AnalysisEventSelection { HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; - int fLastRun; + AnalysisCompositeCut* fEventCut = nullptr; + int fLastRun = -1; Service fCCDB; @@ -349,10 +349,10 @@ struct AnalysisTrackSelection { Service fCCDB; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. int64_t reserveSize = 0; @@ -479,7 +479,7 @@ struct AnalysisMuonSelection { Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; @@ -602,14 +602,14 @@ struct AnalysisPrefilterSelection { Service ccdb; o2::parameters::GRPMagField* grpmag = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. Filter barrelTracksSelectedPrefilter = aod::dqanalysisflags::isBarrelSelectedPrefilter > 0; Partition> barrelTracksSelected = aod::dqanalysisflags::isBarrelSelected > 0; std::map fPrefiltermap; - AnalysisCompositeCut* fPairCut; + AnalysisCompositeCut* fPairCut = nullptr; void init(o2::framework::InitContext& context) { @@ -719,13 +719,13 @@ struct AnalysisEventMixing { TH2D* SingleMuv24m = nullptr; // Single muon v24, loaded from CCDB TH2D* SingleMuv22p = nullptr; // Single antimuon v22, loaded from CCDB TH2D* SingleMuv24p = nullptr; // Single antimuon v24, loaded from CCDB - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; Filter filterTrackSelected = aod::dqanalysisflags::isBarrelSelected > 0; Filter filterMuonTrackSelected = aod::dqanalysisflags::isMuonSelected > 0; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // NOTE: The bit mask is required to run pairing just based on the desired electron/muon candidate cuts uint32_t fTwoTrackFilterMask = 0; uint32_t fTwoMuonFilterMask = 0; @@ -1046,7 +1046,7 @@ struct AnalysisSameEventPairing { o2::base::MatLayerCylSet* lut = nullptr; TH1D* ResoFlowSP = nullptr; // Resolution factors for flow analysis, this will be loaded from CCDB TH1D* ResoFlowEP = nullptr; // Resolution factors for flow analysis, this will be loaded from CCDB - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; Configurable fConfigTrackCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; @@ -1097,7 +1097,7 @@ struct AnalysisSameEventPairing { Filter filterMuonTrackSelected = aod::dqanalysisflags::isMuonSelected > 0; Filter prefilter = aod::dqanalysisflags::isPrefilterVetoed == 0; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; o2::analysis::DQMlResponse fDQMlResponse; std::vector fOutputMlPsi2ee = {}; // TODO: check this is needed or not @@ -1217,7 +1217,6 @@ struct AnalysisSameEventPairing { histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); fTrackHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts @@ -1255,7 +1254,6 @@ struct AnalysisSameEventPairing { } fMuonHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts @@ -1288,11 +1286,10 @@ struct AnalysisSameEventPairing { histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); fTrackMuonHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts - std::vector names = { + names = { Form("PairsEleMuSEPM_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName()), Form("PairsEleMuSEPP_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName()), Form("PairsEleMuSEMM_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName())}; @@ -1466,7 +1463,7 @@ struct AnalysisSameEventPairing { VarManager::FillPair(t1, t2); if constexpr ((TPairType == pairTypeEE) || (TPairType == pairTypeMuMu)) { // call this just for ee or mumu pairs if constexpr (TTwoProngFitter == true) { - VarManager::FillPairVertexing(event, t1, t2, fPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1876,7 +1873,7 @@ struct AnalysisDileptonHadron { // To be modified/adapted if new requirements appear float mMagField = 0.0; o2::parameters::GRPMagField* grpmag = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; // TODO: For now this is only used to determine the position in the filter bit map for the hadron cut @@ -1902,14 +1899,14 @@ struct AnalysisDileptonHadron { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesHadron; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesHadron = nullptr; + HistogramManager* fHistMan = nullptr; // NOTE: the barrel track filter is shared between the filters for dilepton electron candidates (first n-bits) // and the associated hadrons (n+1 bit) --> see the barrel track selection task // The current condition should be replaced when bitwise operators will become available in Filter expressions - int fNHadronCutBit; + int fNHadronCutBit = 0; NoBinningPolicy hashBin; @@ -2113,8 +2110,8 @@ struct AnalysisDileptonTrackTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use some values array to avoid mixing up the quantities - float* fValuesQuadruplet; - HistogramManager* fHistMan; + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; // cut name setting TString fTrackCutName1; diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index 927ce012626..3ade22e200f 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -716,7 +716,7 @@ struct AnalysisTrackSelection { if (addTrackCutsStr != "") { std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto const& t : addTrackCuts) { - fTrackCuts.push_back(static_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } @@ -951,9 +951,7 @@ struct AnalysisMuonSelection { o2::framework::Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - o2::framework::Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; o2::framework::Service fCCDB{}; @@ -985,7 +983,7 @@ struct AnalysisMuonSelection { if (addCutsStr != "") { std::vector addCuts = o2::aod::dqcuts::GetCutsFromJSON(addCutsStr.Data()); for (auto const& t : addCuts) { - fMuonCuts.push_back(static_cast(t)); + fMuonCuts.push_back(dynamic_cast(t)); } } @@ -1015,9 +1013,6 @@ struct AnalysisMuonSelection { fCCDB->setCaching(true); fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigGeoPath); - } } template @@ -1027,13 +1022,6 @@ struct AnalysisMuonSelection { fNAssocsOutOfBunch.clear(); if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { - auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); - if (grpmag != nullptr) { - o2::base::Propagator::initFieldFromGRP(grpmag); - VarManager::SetMagneticField(grpmag->getNominalL3Field()); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); - } fCurrentRun = events.begin().runNumber(); } @@ -1384,7 +1372,6 @@ struct AnalysisSameEventPairing { o2::framework::Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; o2::framework::Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; o2::framework::Configurable GrpLhcIfPath{"grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; o2::framework::Configurable efficiencyPath{"effHistPath", "Users/z/zhxiong/efficiency", "Path on the CCDB for the efficiency histograms"}; o2::framework::Configurable flowPath{"flowPath", "Users/y/yiping/FlowResolution", "Path to the flow resolution object"}; @@ -1392,14 +1379,10 @@ struct AnalysisSameEventPairing { } fConfigCCDB; struct : o2::framework::ConfigurableGroup { - o2::framework::Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - o2::framework::Configurable magField{"cfgMagField", 5.0f, "Manually set magnetic field"}; o2::framework::Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; o2::framework::Configurable polarTables{"cfgPolarTables", false, "Produce tables with dilepton polarization information"}; o2::framework::Configurable useKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; o2::framework::Configurable useAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - o2::framework::Configurable propToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - o2::framework::Configurable corrFullGeo{"cfgCorrFullGeo", false, "Use full geometry to correct for MCS effects in track propagation"}; o2::framework::Configurable noCorr{"cfgNoCorrFwdProp", false, "Do not correct for MCS effects in track propagation"}; o2::framework::Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; o2::framework::Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; @@ -1469,7 +1452,7 @@ struct AnalysisSameEventPairing { fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed"); fEnableBarrelMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingBarrelSkimmed") || context.mOptions.get("processMixingBarrelSkimmedFlow") || context.mOptions.get("processMixingBarrelWithQvectorCentrSkimmedNoCov"); fEnableBarrelMixingHistos |= fConfigRunMixingAcrossTFs; - fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow"); + fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); fEnableMuonMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingMuonSkimmed") || context.mOptions.get("processMixingMuonSkimmedFlow"); fEnableBarrelMuonHistos = context.mOptions.get("processElectronMuonSkimmed"); fEnableBarrelMuonMixingHistos = context.mOptions.get("processMixingElectronMuonSkimmed"); @@ -1749,10 +1732,6 @@ struct AnalysisSameEventPairing { if (fConfigOptions.noCorr) { VarManager::SetupFwdDCAFitterNoCorr(); - } else if (fConfigOptions.corrFullGeo || (fConfigOptions.useKFVertexing && fConfigOptions.propToPCA)) { - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigCCDB.geoPath); - } } else { fLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); VarManager::SetupMatLUTFwdDCAFitter(fLUT); @@ -1830,35 +1809,22 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, int runNumber, bool withTwoProngFitter = true) { - if (fConfigOptions.useRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations - } + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigOptions.magField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(fConfigOptions.magField.value, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (withTwoProngFitter) { + if (fConfigOptions.useKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables + VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); } + } else { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } std::map metadataRCT, header; @@ -1939,7 +1905,7 @@ struct AnalysisSameEventPairing { }*/ auto twoTrackFilter = static_cast(0); - uint32_t dileptonMcDecision = static_cast(0); // placeholder, copy of the dqEfficiency.cxx one + auto dileptonMcDecision = static_cast(0); // placeholder, copy of the dqEfficiency.cxx one int sign1 = 0; int sign2 = 0; // Reserve capacity for the output tables to avoid repeated reallocations @@ -2072,7 +2038,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -2200,7 +2166,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -2216,49 +2182,47 @@ struct AnalysisSameEventPairing { dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } - if constexpr (TTwoProngFitter) { - dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); - if (fConfigOptions.flatTables.value) { - dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), - event.selection_raw(), evSel, - -999., -999., -999., - VarManager::fgValues[VarManager::kMass], - false, - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], - VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], - VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], - VarManager::fgValues[VarManager::kCosPointingAngle], - VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), - VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), - t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), - 0., 0., - t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), - t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), - t1.chi2(), t2.chi2(), - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), - true, true, - VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kU3Q3], - VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kCentFT0C], - VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kCos3DeltaPhi], - VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], - VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); - } - if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { - if constexpr (eventHasQvector || eventHasQvectorCentr) { - dileptonFlowList(event.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), isFirst, - VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kR2SP_AC], VarManager::fgValues[VarManager::kR2SP_BC], - VarManager::fgValues[VarManager::kU3Q3], VarManager::fgValues[VarManager::kR3SP], - VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2EP_AC], VarManager::fgValues[VarManager::kR2EP_BC], - VarManager::fgValues[VarManager::kCos3DeltaPhi], VarManager::fgValues[VarManager::kR3EP], - VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], - VarManager::fgValues[VarManager::kCORR2REF], VarManager::fgValues[VarManager::kCORR4REF], VarManager::fgValues[VarManager::kM11REF], VarManager::fgValues[VarManager::kM1111REF], - VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kMultA]); - } + dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); + if (fConfigOptions.flatTables.value) { + dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), + event.selection_raw(), evSel, + -999., -999., -999., + VarManager::fgValues[VarManager::kMass], + false, + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], + VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], + VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), + VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), + t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), + 0., 0., + t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), + t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), + t1.chi2(), t2.chi2(), + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), + true, true, + VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kU3Q3], + VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kCentFT0C], + VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kCos3DeltaPhi], + VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], + VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); + } + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { + if constexpr (eventHasQvector || eventHasQvectorCentr) { + dileptonFlowList(event.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), isFirst, + VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kR2SP_AC], VarManager::fgValues[VarManager::kR2SP_BC], + VarManager::fgValues[VarManager::kU3Q3], VarManager::fgValues[VarManager::kR3SP], + VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2EP_AC], VarManager::fgValues[VarManager::kR2EP_BC], + VarManager::fgValues[VarManager::kCos3DeltaPhi], VarManager::fgValues[VarManager::kR3EP], + VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], + VarManager::fgValues[VarManager::kCORR2REF], VarManager::fgValues[VarManager::kCORR4REF], VarManager::fgValues[VarManager::kM11REF], VarManager::fgValues[VarManager::kM1111REF], + VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kMultA]); } } if (t1.sign() != t2.sign()) { @@ -2309,7 +2273,9 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); + } if (useMiniTree.fConfigMiniTree) { auto t1 = a1.template reducedmuon_as(); auto t2 = a2.template reducedmuon_as(); @@ -2329,7 +2295,7 @@ struct AnalysisSameEventPairing { } } } - if (fConfigAmbiguousMuonHistograms) { + if (fConfigQA && fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset].Data(), dqtablereader_helpers::varValues()); } @@ -2342,12 +2308,14 @@ struct AnalysisSameEventPairing { } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); + } } } - } else { + } else if (fConfigQA) { if (sign1 > 0) { if constexpr (TPairType == VarManager::kDecayToMuMu) { fHistMan->FillHistClass(histNames[icut][1].Data(), dqtablereader_helpers::varValues()); @@ -2392,21 +2360,23 @@ struct AnalysisSameEventPairing { } } } - for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { - AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!(cut.IsSelected(dqtablereader_helpers::varValues()))) { // apply pair cuts - continue; - } - if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), dqtablereader_helpers::varValues()); - } else { - if (sign1 > 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), dqtablereader_helpers::varValues()); + if (fConfigQA) { + for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { + AnalysisCompositeCut cut = fPairCuts.at(iPairCut); + if (!(cut.IsSelected(dqtablereader_helpers::varValues()))) { // apply pair cuts + continue; + } + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), dqtablereader_helpers::varValues()); + if (sign1 > 0) { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), dqtablereader_helpers::varValues()); + } } - } - } // end loop (pair cuts) + } // end loop (pair cuts) + } } } // end loop (cuts) @@ -2462,18 +2432,22 @@ struct AnalysisSameEventPairing { if (fConfigNRotations.value == 1) { VarManager::FillPairRotation(t1, t2, fConfigNRotations.value); if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } else if (fConfigNRotations.value == 3) { for (int irot = 1; irot <= fConfigNRotations.value; irot++) { VarManager::FillPairRotation(t1, t2, irot); if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } @@ -2532,7 +2506,9 @@ struct AnalysisSameEventPairing { VarManager::FillPairMEAcrossTFs(t1, t2); for (int icut = 0; icut < ncuts; icut++) { if (mixedTwoTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } if (fConfigTRPairs) { @@ -2560,7 +2536,9 @@ struct AnalysisSameEventPairing { VarManager::FillPairMEAcrossTFs(t1, t2); for (int icut = 0; icut < ncuts; icut++) { if (mixedTwoTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } @@ -2576,7 +2554,9 @@ struct AnalysisSameEventPairing { VarManager::FillPairMEAcrossTFs(t1, t2); for (int icut = 0; icut < ncuts; icut++) { if (mixedTwoTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } if (fConfigTRPairs) { @@ -2603,7 +2583,9 @@ struct AnalysisSameEventPairing { VarManager::FillPairMEAcrossTFs(t1, t2); for (int icut = 0; icut < ncuts; icut++) { if (mixedTwoTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } @@ -2724,60 +2706,62 @@ struct AnalysisSameEventPairing { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); isUnambiguous = !((twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)) || (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31))); - if (pairSign == 0) { - if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); - if (fConfigAmbiguousMuonHistograms) { - if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][15].Data(), dqtablereader_helpers::varValues()); - } - if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][18].Data(), dqtablereader_helpers::varValues()); - } - if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][21].Data(), dqtablereader_helpers::varValues()); - } - } - } - if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - } - } else { - if (pairSign > 0) { + if (fConfigQA) { + if (pairSign == 0) { if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); + fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); if (fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][16].Data(), dqtablereader_helpers::varValues()); + fHistMan->FillHistClass(histNames[icut][15].Data(), dqtablereader_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][19].Data(), dqtablereader_helpers::varValues()); + fHistMan->FillHistClass(histNames[icut][18].Data(), dqtablereader_helpers::varValues()); } if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][22].Data(), dqtablereader_helpers::varValues()); + fHistMan->FillHistClass(histNames[icut][21].Data(), dqtablereader_helpers::varValues()); } } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { - if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); - if (fConfigAmbiguousMuonHistograms) { - if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][17].Data(), dqtablereader_helpers::varValues()); - } - if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][20].Data(), dqtablereader_helpers::varValues()); + if (pairSign > 0) { + if constexpr (TPairType == VarManager::kDecayToMuMu) { + fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); + if (fConfigAmbiguousMuonHistograms) { + if (isAmbiInBunch) { + fHistMan->FillHistClass(histNames[icut][16].Data(), dqtablereader_helpers::varValues()); + } + if (isAmbiOutOfBunch) { + fHistMan->FillHistClass(histNames[icut][19].Data(), dqtablereader_helpers::varValues()); + } + if (isUnambiguous) { + fHistMan->FillHistClass(histNames[icut][22].Data(), dqtablereader_helpers::varValues()); + } } - if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][23].Data(), dqtablereader_helpers::varValues()); + } + if constexpr (TPairType == VarManager::kDecayToEE) { + fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } else { + if constexpr (TPairType == VarManager::kDecayToMuMu) { + fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); + if (fConfigAmbiguousMuonHistograms) { + if (isAmbiInBunch) { + fHistMan->FillHistClass(histNames[icut][17].Data(), dqtablereader_helpers::varValues()); + } + if (isAmbiOutOfBunch) { + fHistMan->FillHistClass(histNames[icut][20].Data(), dqtablereader_helpers::varValues()); + } + if (isUnambiguous) { + fHistMan->FillHistClass(histNames[icut][23].Data(), dqtablereader_helpers::varValues()); + } } } - } - if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if constexpr (TPairType == VarManager::kDecayToEE) { + fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } @@ -2935,13 +2919,15 @@ struct AnalysisSameEventPairing { if (itHist == histNames.end()) { continue; } - if (sign1 * sign2 < 0) { // Opposite Sign - fHistMan->FillHistClass(itHist->second[0].Data(), dqtablereader_helpers::varValues()); - } else { // Like Sign - if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[1].Data(), dqtablereader_helpers::varValues()); - } else { - fHistMan->FillHistClass(itHist->second[2].Data(), dqtablereader_helpers::varValues()); + if (fConfigQA) { + if (sign1 * sign2 < 0) { // Opposite Sign + fHistMan->FillHistClass(itHist->second[0].Data(), dqtablereader_helpers::varValues()); + } else { // Like Sign + if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[1].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[2].Data(), dqtablereader_helpers::varValues()); + } } } } // end pair cut loop @@ -3004,13 +2990,15 @@ struct AnalysisSameEventPairing { if (itHist == histNames.end() || itHist->second.size() < 6) { continue; } - if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(itHist->second[3].Data(), dqtablereader_helpers::varValues()); - } else { - if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[4].Data(), dqtablereader_helpers::varValues()); + if (fConfigQA) { + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHist->second[3].Data(), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(itHist->second[5].Data(), dqtablereader_helpers::varValues()); + if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[4].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[5].Data(), dqtablereader_helpers::varValues()); + } } } } // end pair cut loop @@ -3108,19 +3096,25 @@ struct AnalysisSameEventPairing { void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processMuonOnlySkimmedMultExtra(MyEventsVtxCovSelectedMultExtra const& events, o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processMuonOnlySkimmedFlow(MyEventsQvectorCentrSelected const& events, o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + } + + void processMuonOnlyVertexingSkimmed(MyEventsVtxCovSelected const& events, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + { + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processElectronMuonSkimmed(MyEventsVtxCovSelected const& events, @@ -3191,6 +3185,7 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedMultExtra, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedFlow, "Run muon only pairing, with skimmed tracks and flow", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlyVertexingSkimmed, "Run muon only pairing with two-prong vertexing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processElectronMuonSkimmed, "Run electron-muon pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMixingAllSkimmed, "Run all types of mixed pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMixingBarrelSkimmed, "Run barrel type mixing pairing, with skimmed tracks", false); @@ -3233,12 +3228,9 @@ struct AnalysisAsymmetricPairing { o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; o2::framework::Configurable fConfigGRPMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Choose whether to fetch the magnetic field from ccdb or set it manually"}; - o2::framework::Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; o2::framework::Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; o2::framework::Configurable fConfigUseAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - o2::framework::Configurable fConfigPropToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; o2::framework::Configurable fConfigLutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; o2::framework::Configurable fConfigFetchInteractionRate{"cfgFetchInteractionRate", false, "Fetch event-wise interaction rate from the CCDB"}; o2::framework::Configurable fConfigIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; @@ -3310,7 +3302,7 @@ struct AnalysisAsymmetricPairing { if (addPairCutsStr != "") { std::vector addPairCuts = o2::aod::dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); for (auto const& t : addPairCuts) { - fPairCuts.push_back(static_cast(t)); + fPairCuts.push_back(dynamic_cast(t)); cutNamesStr += Form(",%s", t->GetName()); } } @@ -3516,40 +3508,24 @@ struct AnalysisAsymmetricPairing { void initParamsFromCCDB(uint64_t timestamp, bool isTriplets) { - if (fConfigUseRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (isTriplets) { + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); } } else { - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables } } } @@ -3616,8 +3592,8 @@ struct AnalysisAsymmetricPairing { for (auto const& [a1, a2] : combinations(o2::soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { auto twoTrackFilter = static_cast(0); - uint32_t twoTrackCommonFilter = static_cast(0); - uint32_t pairFilter = static_cast(0); + auto twoTrackCommonFilter = static_cast(0); + auto pairFilter = static_cast(0); for (int icut = 0; icut < fNLegCuts; ++icut) { // Find leg pair definitions both candidates participate in if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut])) { @@ -3669,7 +3645,7 @@ struct AnalysisAsymmetricPairing { VarManager::FillPair(t1, t2); if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } // Fill histograms @@ -3816,8 +3792,8 @@ struct AnalysisAsymmetricPairing { template void readTriplet(TTrackAssoc const& a1, TTrackAssoc const& a2, TTrackAssoc const& a3, TTracks const& /*tracks*/, TEvent const& event, VarManager::PairCandidateType tripletType) { - uint32_t threeTrackFilter = static_cast(0); - uint32_t threeTrackCommonFilter = static_cast(0); + auto threeTrackFilter = static_cast(0); + auto threeTrackCommonFilter = static_cast(0); for (int icut = 0; icut < fNLegCuts; ++icut) { // Find out which leg cut combinations the triplet passes if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut]) && (a3.isBarrelSelected_raw() & fConstructedLegCFilterMasksMap[icut])) { @@ -3983,9 +3959,7 @@ struct AnalysisDileptonTrack { o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; o2::framework::Configurable fConfigMixingDepth{"cfgMixingDepth", 5, "Event mixing pool depth"}; - o2::framework::Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; o2::framework::Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -4274,31 +4248,23 @@ struct AnalysisDileptonTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); + } else { + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } void initAccFromCCDB(uint64_t timestamp) { - TList* listAccs = fCCDB->getForTimeStamp(fConfigAccCCDBPath, timestamp); + auto listAccs = fCCDB->getForTimeStamp(fConfigAccCCDBPath, timestamp); if (!listAccs) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } @@ -4667,7 +4633,7 @@ struct AnalysisDileptonTrack { continue; } for (uint32_t iTrackCut = 0; iTrackCut < fTrackCutNames.size(); iTrackCut++) { - if (trackSelection & (static_cast(1) << iTrackCut)) { + if ((trackSelection & (static_cast(1) << iTrackCut)) != 0) { fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); if (fConfigEnergycorrelator) { fHistMan->FillHistClass(Form("DileptonTrackECME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); @@ -4715,7 +4681,7 @@ struct AnalysisDileptonTrack { continue; } for (uint32_t iTrackCut = 0; iTrackCut < fTrackCutNames.size(); iTrackCut++) { - if (muonSelection & (static_cast(1) << iTrackCut)) { + if ((muonSelection & (static_cast(1) << iTrackCut)) != 0) { fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); } } @@ -4750,9 +4716,7 @@ struct AnalysisDileptonTrackTrack { o2::framework::Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; o2::framework::Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; - o2::framework::Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; o2::framework::Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; o2::framework::Produces DileptonTrackTrackTable; @@ -4833,30 +4797,19 @@ struct AnalysisDileptonTrackTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - VarManager::SetupFourProngKFParticle(magField); - } else if (fConfigSetupFourProngFitter.value) { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - VarManager::SetupFourProngKFParticle(fConfigMagField.value); - } else if (fConfigSetupFourProngFitter.value) { - LOGP(info, "Setting up DCA fitter for two and four prong candidates"); - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - VarManager::SetupFourProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); + VarManager::SetupFourProngKFParticle(magField); + } else if (fConfigSetupFourProngFitter.value) { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index 95fceb1e889..7bbff3763c9 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -82,8 +82,6 @@ using namespace o2::framework::expressions; using namespace o2::aod; using namespace o2::common::core; -Zorro zorro; - // Some definitions namespace o2::aod { @@ -325,20 +323,22 @@ struct AnalysisEventSelection { // RCT flag checker o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + Zorro zorro; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; o2::parameters::GRPObject* fGrpMagRun2 = nullptr; // for run 2, we access the GRPObject from GLO/GRP/GRP o2::parameters::GRPMagField* fGrpMag = nullptr; // for run 3, we access GRPMagField from GLO/Config/GRPMagField - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; std::map fSelMap; // key: reduced event global index, value: event selection decision std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices - int fCurrentRun; + int fCurrentRun = -1; void init(o2::framework::InitContext& context) { @@ -370,7 +370,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); DefineHistograms(fHistMan, "TimeFrameStats;Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); if (fConfigCheckSplitCollisions) { DefineHistograms(fHistMan, "OutOfBunchCorrelations;SameBunchCorrelations;", ""); @@ -449,7 +449,7 @@ struct AnalysisEventSelection { } VarManager::SetCalibrationType(fConfigPostCalibTPC.fConfigTPCpostCalibType, fConfigPostCalibTPC.fConfigTPCuseInterpolatedCalib); } - if (fIsRun2 == true) { + if (fIsRun2) { fGrpMagRun2 = fCCDB->getForTimeStamp(fConfigCCDB.fConfigGrpMagPathRun2, bcs.begin().timestamp()); if (fGrpMagRun2 != nullptr) { o2::base::Propagator::initFieldFromGRP(fGrpMagRun2); @@ -481,7 +481,7 @@ struct AnalysisEventSelection { VarManager::FillTimeFrame(bcs); VarManager::FillTimeFrame(events); if (fConfigQA) { - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); } fSelMap.clear(); @@ -496,7 +496,7 @@ struct AnalysisEventSelection { // the hash table is joined to the events by row order, so publish one row per event before any event selection if (fMixHandler != nullptr) { - int hh = fMixHandler->FindEventCategory(VarManager::fgValues); + int hh = fMixHandler->FindEventCategory(static_cast(VarManager::fgValues)); // events outside the mixing limits (-1) get a distinct negative hash so that they are not mixed with each other if (hh < 0) { hh = -1 - static_cast(event.globalIndex()); @@ -506,33 +506,33 @@ struct AnalysisEventSelection { bool decision = false; if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", static_cast(VarManager::fgValues)); } if (fConfigZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(fConfigZorro.fConfigCcdbPathZorro.value); zorro.setBCtolerance(fConfigZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), fConfigZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(kStatsZorroInfo)), reinterpret_cast(fStatsList->At(kStatsZorroSel))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(kStatsZorroInfo)), dynamic_cast(fStatsList->At(kStatsZorroSel))); - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { + if (!fEventCut->IsSelected(static_cast(VarManager::fgValues)) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { continue; } - bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, reinterpret_cast(fStatsList->At(kStatsZorroSel))); + bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, dynamic_cast(fStatsList->At(kStatsZorroSel))); if (fConfigZorro.fConfigRunZorroSel && (!zorroSel)) { continue; } } else { - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { + if (!fEventCut->IsSelected(static_cast(VarManager::fgValues)) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { continue; } } decision = true; if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", static_cast(VarManager::fgValues)); } fSelMap[event.globalIndex()] = decision; @@ -572,7 +572,7 @@ struct AnalysisEventSelection { collisionSplittingMap[*ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("SameBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("SameBunchCorrelations", static_cast(VarManager::fgValues)); } } // end second event loop } // end first event loop @@ -599,7 +599,7 @@ struct AnalysisEventSelection { collisionSplittingMap[ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("OutOfBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("OutOfBunchCorrelations", static_cast(VarManager::fgValues)); } } } @@ -607,7 +607,7 @@ struct AnalysisEventSelection { } // publish the table - uint32_t evSel = static_cast(0); + auto evSel = static_cast(0); for (auto& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts @@ -662,22 +662,23 @@ struct AnalysisTrackSelection { Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Service fCCDB; - Service fTofResponse; + Service fCCDB{}; + Service fTofResponse{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fHistNamesReco; - int fCurrentRun; // current run (needed to detect run changes for loading CCDB parameters) + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) void init(o2::framework::InitContext& context) { - if (context.mOptions.get("processDummy")) + if (context.mOptions.get("processDummy")) { return; + } VarManager::SetDefaultVarNames(); fCurrentRun = 0; @@ -696,7 +697,7 @@ struct AnalysisTrackSelection { if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } @@ -706,7 +707,7 @@ struct AnalysisTrackSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); // Configure histogram classes for each track cut; TString histClasses = "TimeFrameStats;AssocsBarrel_BeforeCuts;"; @@ -745,8 +746,9 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTimeFrame(events); VarManager::FillTimeFrame(tracks); - if (fConfigQA) - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); + } if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { if (fConfigComputeTPCpostCalib) { @@ -759,7 +761,7 @@ struct AnalysisTrackSelection { VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); } - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); if (grpmag != nullptr) { VarManager::SetMagneticField(grpmag->getNominalL3Field()); } else { @@ -792,19 +794,21 @@ struct AnalysisTrackSelection { VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA - if (track.collisionId() != event.globalIndex()) + if (track.collisionId() != event.globalIndex()) { VarManager::FillTrackCollision(track, event); + } - if (fConfigQA) - fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", static_cast(VarManager::fgValues)); + } int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(static_cast(VarManager::fgValues))) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut], VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesReco[iCut], static_cast(VarManager::fgValues)); } } } // end loop over cuts @@ -846,7 +850,7 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", static_cast(VarManager::fgValues)); } // end loop over in-bunch ambiguous tracks for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { @@ -857,7 +861,7 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", static_cast(VarManager::fgValues)); } // end loop over out-of-bunch ambiguous tracks } @@ -907,9 +911,9 @@ struct AnalysisPrefilterSelection { Configurable fPropTrack{"cfgPropTrack", false, "Propagate tracks to associated collision to recalculate DCA and momentum vector"}; std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = -1; Preslice trackAssocsPerCollision = aod::track_association::collisionId; @@ -1015,7 +1019,7 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } @@ -1025,7 +1029,7 @@ struct AnalysisPrefilterSelection { VarManager::FillPairCollision(event, track1, track2); } // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { + if (fPairCut->IsSelected(static_cast(VarManager::fgValues))) { if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } @@ -1062,7 +1066,7 @@ struct AnalysisPrefilterSelection { // auto track = assoc.template track_as(); mymap = -1; // if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { - if (fPrefilterMap.find(assoc.trackId()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(assoc.trackId())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else // mymap = ~fPrefilterMap[track.globalIndex()]; mymap = ~fPrefilterMap[assoc.trackId()]; @@ -1103,7 +1107,7 @@ struct AnalysisMuonSelection { Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; } fConfigCCDB; - Service fCCDB; + Service fCCDB{}; HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; @@ -1133,7 +1137,7 @@ struct AnalysisMuonSelection { if (addCutsStr != "") { std::vector addCuts = dqcuts::GetCutsFromJSON(addCutsStr.Data()); for (auto& t : addCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + fMuonCuts.push_back(dynamic_cast(t)); } } @@ -1143,7 +1147,7 @@ struct AnalysisMuonSelection { if (fHistMan == nullptr) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); TString histDirNames = "TrackMuon_BeforeCuts;"; for (auto& cut : fMuonCuts) { @@ -1177,7 +1181,7 @@ struct AnalysisMuonSelection { fNAssocsOutOfBunch.clear(); if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, bcs.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, bcs.begin().timestamp()); if (grpmag != nullptr) { o2::base::Propagator::initFieldFromGRP(grpmag); VarManager::SetMagneticField(grpmag->getNominalL3Field()); @@ -1191,7 +1195,7 @@ struct AnalysisMuonSelection { if (fConfigPublishAmbiguity) { muonAmbiguities.reserve(muons.size()); } - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); int iCut = 0; for (auto& assoc : assocs) { @@ -1207,14 +1211,14 @@ struct AnalysisMuonSelection { filterMap = static_cast(0); VarManager::FillTrack(track); if (fConfigQA) { - fHistMan->FillHistClass("TrackMuon_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_BeforeCuts", static_cast(VarManager::fgValues)); } iCut = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(static_cast(VarManager::fgValues))) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), static_cast(VarManager::fgValues)); } } } @@ -1240,22 +1244,24 @@ struct AnalysisMuonSelection { if (fConfigPublishAmbiguity) { if (fConfigQA) { for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { - if (evIndices.size() <= 1) + if (evIndices.size() <= 1) { continue; + } auto track = muons.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", static_cast(VarManager::fgValues)); } for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { - if (evIndices.size() <= 1) + if (evIndices.size() <= 1) { continue; + } auto track = muons.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", static_cast(VarManager::fgValues)); } } // publish ambiguity table (one row per FwdTrack) @@ -1298,7 +1304,7 @@ struct AnalysisSameEventPairing { o2::base::MatLayerCylSet* fLUT = nullptr; TH1D* ResoFlowSP = nullptr; TH1D* ResoFlowEP = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; @@ -1343,7 +1349,7 @@ struct AnalysisSameEventPairing { Configurable flowPath{"flowPath", "Users/y/yiping/FlowResolution", "Path to the flow resolution object"}; } fConfigCCDB; - Service fCCDB; + Service fCCDB{}; // vectors needed for PV recomputation std::vector pvContribGlobIDs; @@ -1352,22 +1358,22 @@ struct AnalysisSameEventPairing { // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; - std::map> fMuonHistNames; + // std::map> fMuonHistNames; std::map> fTrackMuonHistNames; // for electron-muon pairs: key = iTrack * fNCutsMuon + iMuon std::vector fPairCuts; AnalysisCompositeCut fMCGenAccCut; // bool fUseMCGenAccCut = false; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - uint32_t fMuonFilterMask; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream - int fNCutsBarrel; - int fNCutsMuon; - int fNPairCuts; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + uint32_t fMuonFilterMask = 0; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream + int fNCutsBarrel = 0; + int fNCutsMuon = 0; + int fNPairCuts = 0; bool fHasTwoProngGenMCsignals = false; - bool fEnableBarrelHistos; - bool fEnableBarrelMuonHistos; + bool fEnableBarrelHistos = false; + bool fEnableBarrelMuonHistos = false; std::vector fTrackCuts; // barrel cut names, used in EMu histogram filling std::vector fMuonCuts; // muon cut names, used in EMu histogram filling @@ -1459,7 +1465,6 @@ struct AnalysisSameEventPairing { // if there are pair cuts specified, assign hist directories for each barrel cut - pair cut combination // NOTE: This could possibly lead to large histogram outputs. It is strongly advised to use pair cuts only // if you know what you are doing. - TString cutNamesStr = fConfigOptions.pair.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); @@ -1506,8 +1511,9 @@ struct AnalysisSameEventPairing { int seqTrackIdx = 0; // sequential index into fTrackCuts (which contains only required cuts) for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { // skip barrel cuts not required in this task - if (!(fTrackFilterMask & (static_cast(1) << iTrack))) + if ((fTrackFilterMask & (static_cast(1) << iTrack)) == 0) { continue; + } TString trackCutName = fTrackCuts[seqTrackIdx]; seqTrackIdx++; std::vector names = { @@ -1543,7 +1549,7 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy @@ -1556,7 +1562,7 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, bool withTwoProngFitter = true) { if (fConfigOptions.useRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); float magField = 0.0; if (grpmag != nullptr) { @@ -1610,15 +1616,18 @@ struct AnalysisSameEventPairing { // int nMyPVContrib = 0; int nMyPVContribOrig = 0; for (auto const& trk : tracks) { // check if it is PV contributor - if (!trk.isPVContributor()) + if (!trk.isPVContributor()) { continue; + } // check if it contributes to the vtx of this collision - if (trk.collisionId() != collision.globalIndex()) + if (trk.collisionId() != collision.globalIndex()) { continue; + } // nMyPVContribOrig++; // --- remove t1 and t2 if they are PV contributors --- - if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) + if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) { continue; + } // add tracks and parameters to the list pvContribGlobIDs.push_back(trk.globalIndex()); pvContribTrackPars.push_back(getTrackParCov(trk)); @@ -1643,8 +1652,9 @@ struct AnalysisSameEventPairing { vertexer.init(); bool PVrefit_doable = vertexer.prepareVertexRefit(pvContribTrackPars, Pvtx); - if (!PVrefit_doable) + if (!PVrefit_doable) { return false; + } // --- do the refit --- pvRefitted = vertexer.refitVertex(vec_useTrk_PVrefit, Pvtx); @@ -1703,19 +1713,21 @@ struct AnalysisSameEventPairing { // constexpr bool fillFlowReso = eventHasQvector; for (auto& event : events) { - if (!event.isEventSelected_bit(0)) + if (!event.isEventSelected_bit(0)) { continue; + } if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) { continue; } VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, static_cast(VarManager::fgValues)); auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); - if (groupedAssocs.size() == 0) + if (groupedAssocs.size() == 0) { continue; + } if (fConfigOptions.useFlowReso) { if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { @@ -1729,8 +1741,9 @@ struct AnalysisSameEventPairing { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; - if (!twoTrackFilter) + if (!twoTrackFilter) { continue; + } auto t1 = a1.template track_as(); auto t2 = a2.template track_as(); @@ -1765,17 +1778,17 @@ struct AnalysisSameEventPairing { } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); o2::dataformats::VertexBase pvRefit; if (fConfigOptions.recomputePV) { VarManager::SetPVrecalculationKF(false); VarManager::ResetValues(VarManager::kVertexingLxyProjectedRecalculatePV, VarManager::kVertexingLxyProjectedRecalculatePV + 1); VarManager::ResetValues(VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV, VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV + 1); // cout << "primary vertex (before): x -> " << event.posX() << " y -> " << event.posY() << " z -> " << event.posZ() << endl; - o2::dataformats::VertexBase pvRefit; bool ok = refitPVWithPVertexer(event, tracks, t1, t2, pvRefit); - if (ok) + if (ok) { VarManager::FillPairVertexingRecomputePV(event, t1, t2, pvRefit); + } // cout << "primary vertex (after): ok -> " << ok << " x -> " << pvRefit.getX() << " y -> " << pvRefit.getY() << " z -> " << pvRefit.getZ() << endl; } } @@ -1803,43 +1816,43 @@ struct AnalysisSameEventPairing { } } // Fill normal histograms - bool isAmbiInBunch = (twoTrackFilter & (1 << 28)) || (twoTrackFilter & (1 << 29)); - bool isAmbiOutOfBunch = (twoTrackFilter & (1 << 30)) || (twoTrackFilter & (1 << 31)); + bool isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + bool isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); for (int icut = 0; icut < ncuts; icut++) { // loop over cut definitions if (twoTrackFilter & (static_cast(1) << icut)) { if (sign1 * sign2 < 0) { // opposite sign pairs - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][0].Data(), static_cast(VarManager::fgValues)); PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), static_cast(VarManager::fgValues)); } } } else if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), static_cast(VarManager::fgValues)); } } } else { // -- pairs - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), static_cast(VarManager::fgValues)); } } } @@ -1847,14 +1860,15 @@ struct AnalysisSameEventPairing { // Pair cuts for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!cut.IsSelected(VarManager::fgValues)) + if (!cut.IsSelected(static_cast(VarManager::fgValues))) { continue; // apply pair cuts + } if (sign1 * sign2 < 0) { // opposite sign pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), static_cast(VarManager::fgValues)); } else if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), static_cast(VarManager::fgValues)); } else { // -- pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), static_cast(VarManager::fgValues)); } } // end loop (pair cuts) } @@ -1890,26 +1904,32 @@ struct AnalysisSameEventPairing { constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); for (auto& event : events) { - if (!event.isEventSelected_bit(0)) + if (!event.isEventSelected_bit(0)) { continue; - if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) + } + if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) { continue; + } VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, static_cast(VarManager::fgValues)); auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); - if (groupedAssocs1.size() == 0) + if (groupedAssocs1.size() == 0) { continue; + } auto groupedAssocs2 = assocs2.sliceBy(preslice2, event.globalIndex()); - if (groupedAssocs2.size() == 0) + if (groupedAssocs2.size() == 0) { continue; + } for (auto& [a1, a2] : o2::soa::combinations(soa::CombinationsFullIndexPolicy(groupedAssocs1, groupedAssocs2))) { - if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { continue; - if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) + } + if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { continue; + } auto t1 = a1.template track_as(); auto t2 = a2.template fwdtrack_as(); @@ -1924,14 +1944,18 @@ struct AnalysisSameEventPairing { } } // store ambiguity flags in bits 28-31 - if (t1.barrelAmbiguityInBunch() > 1) + if (t1.barrelAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 28); - if (t2.muonAmbiguityInBunch() > 1) + } + if (t2.muonAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 29); - if (t1.barrelAmbiguityOutOfBunch() > 1) + } + if (t1.barrelAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 30); - if (t2.muonAmbiguityOutOfBunch() > 1) + } + if (t2.muonAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 31); + } VarManager::FillPair(t1, t2); if (fConfigOptions.fPropTrack) { @@ -1947,21 +1971,24 @@ struct AnalysisSameEventPairing { t1.sign() + t2.sign(), twoTrackFilter, 0); for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { - if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) + if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) { continue; + } for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { - if (!(a2.isMuonSelected_raw() & (1u << iMuon))) + if (!(a2.isMuonSelected_raw() & (1u << iMuon))) { continue; + } int index = iTrack * fNCutsMuon + iMuon; auto itHist = histNames.find(index); - if (itHist == histNames.end()) + if (itHist == histNames.end()) { continue; + } if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(itHist->second[0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[0].Data(), static_cast(VarManager::fgValues)); } else if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[1].Data(), static_cast(VarManager::fgValues)); } else { - fHistMan->FillHistClass(itHist->second[2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[2].Data(), static_cast(VarManager::fgValues)); } } // end muon cut loop } // end barrel cut loop From d80d8592984654e0cf6004d3b949abe93368a8eb Mon Sep 17 00:00:00 2001 From: arvindkhuntia <31609955+arvindkhuntia@users.noreply.github.com> Date: Wed, 23 Sep 2026 10:13:48 +0200 Subject: [PATCH 044/648] [PWGJE] add jet migration and event-normalization corrections (#18016) Co-authored-by: Arvind Khuntia --- PWGJE/Tasks/nucleiInJets.cxx | 178 ++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 1 deletion(-) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 82ce31ad6da..1891dc04b65 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -815,6 +815,17 @@ struct nucleiInJets { jetHist.add("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType", "Pt (gen, mcC, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/genSel/hLeadingJetPt", "particle-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{gen} (GeV/#it{c}); Entries", HistType::kTH1F, {{100, 0., 100.}}); + jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "matched detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); + jetHist.add("jetSelCorr/recoSel/hMatchedLeadingJetPt", "selected matched leading jet; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); #it{p}_{T,lead}^{gen} (GeV/#it{c})", HistType::kTH2F, {{120, -20., 100.}, {100, 0., 100.}}); + jetHist.add("jetSelCorr/eventNorm/hEventCounts", "event count for leading-jet event-normalization correction; event selection; Entries", HistType::kTH1D, {{2, 0., 2.}}); + jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(1, "gen lead #it{p}_{T} > cut"); + jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(2, "reco lead #it{p}_{T}^{corr} > cut"); + jetHist.add("jetSelCorr/genSel/jetCone/pt/PtParticleType", "generated primaries in particle-level leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/genSel/perpCone/pt/PtParticleType", "generated primaries in particle-level perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries in matched reco-selected leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in matched reco-selected perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("feeddown/antiProton/jetCone/PtOrigin", "reconstructed #bar{p} origin in jet cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); jetHist.add("feeddown/antiProton/jetCone/PtOriginTPC", "reconstructed #bar{p} origin in jet cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); jetHist.add("feeddown/antiProton/jetCone/PtOriginTOF", "reconstructed #bar{p} origin in jet cone, TOF matched; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); @@ -2291,13 +2302,17 @@ struct nucleiInJets { jetHist.fill(HIST("mcdJet/eventStat"), 2.5); int nJets = 0; + int nAcceptedJets = 0; std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; for (auto& mcdjet : mcdjets) { jetHist.fill(HIST("mcdJet/hJetPt"), mcdjet.pt()); jetHist.fill(HIST("mcdJet/hJetEta"), mcdjet.eta()); jetHist.fill(HIST("mcdJet/hJetPhi"), mcdjet.phi()); - if (mcdjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(mcdjet.eta())) { + nAcceptedJets++; + } + if (isConeAxisAccepted(mcdjet.eta()) && mcdjet.pt() > leadingJetPt) { leadingJetPt = mcdjet.pt(); leadingJetWithPtEtaPhi[0] = mcdjet.pt(); leadingJetWithPtEtaPhi[1] = mcdjet.eta(); @@ -2309,6 +2324,8 @@ struct nucleiInJets { jetHist.fill(HIST("mcdJet/nJetsPerEvent"), nJets); if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; for (const auto& track : tracks) { auto fullTrack = track.track_as(); if (!isTrackSelected(fullTrack)) @@ -2339,6 +2356,9 @@ struct nucleiInJets { jetFlagPerpCone = true; } else if (!isWithLeadingJet) { for (const auto& mcdjet : mcdjets) { + if (!isConeAxisAccepted(mcdjet.eta())) { + continue; + } double delPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - track.phi()); double delEta = mcdjet.eta() - track.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); @@ -2357,6 +2377,73 @@ struct nucleiInJets { } Preslice> perMCCol = aod::jmcparticle::mcCollisionId; + + template + bool isRecoCollisionSelectedForJetSelectionCorrection(const CollType& collision) + { + if (std::abs(collision.posZ()) > cfgMaxZVertex) { + return false; + } + if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) { + return false; + } + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) { + return false; + } + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) { + return false; + } + if (useOccupancy && !isOccupancyAccepted(collision)) { + return false; + } + return true; + } + + template + void fillJetSelectionCorrectionParticles(const ParticlesType& mcParticles, double jetEta, double jetPhi) + { + const auto perpConePhiJet = getPerpendicuarPhi(jetPhi); + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) { + continue; + } + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcParticle.y())) { + continue; + } + + const auto particleType = mapPDGToValue(mcParticle.pdgCode()); + if (particleType == 0) { + continue; + } + + const double delEta = jetEta - mcParticle.eta(); + const double delPhi = TVector2::Phi_mpi_pi(jetPhi - mcParticle.phi()); + const double rJet = RecoDecay::sqrtSumOfSquares(delEta, delPhi); + if (rJet < cfgjetR) { + if constexpr (RecoSelected) { + jetHist.fill(HIST("jetSelCorr/recoSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else { + jetHist.fill(HIST("jetSelCorr/genSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } + } + + const double delPhiPerpCone1 = TVector2::Phi_mpi_pi(perpConePhiJet[0] - mcParticle.phi()); + const double delPhiPerpCone2 = TVector2::Phi_mpi_pi(perpConePhiJet[1] - mcParticle.phi()); + const double rPerpCone1 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone1); + const double rPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); + if (rPerpCone1 < cfgjetR || rPerpCone2 < cfgjetR) { + if constexpr (RecoSelected) { + jetHist.fill(HIST("jetSelCorr/recoSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else { + jetHist.fill(HIST("jetSelCorr/genSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } + } + } + } + void processRecMatched(JetCollWithLabel const& collision, JetMCDetTable const& mcdjets, soa::Join const& tracks, JetMCPartTable const&, TrackCandidatesMC const&, aod::JetParticles const& particleTracks, aod::JMcCollisions const&) @@ -2687,6 +2774,94 @@ struct nucleiInJets { } // process int nprocessSimJEEvents = 0; + void processJetSelectionCorrection(aod::JetMcCollision const& collision, + soa::SmallGroups> const& recocolls, + JetMCDetTable const&, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) + { + if (std::abs(collision.posZ()) > cfgMaxZVertex) { + return; + } + + bool hasGenLeadingJet = false; + double genLeadingJetPt = -999.; + double genLeadingJetEta = -999.; + double genLeadingJetPhi = -999.; + + for (const auto& mcpjet : mcpjets) { + if (!isConeAxisAccepted(mcpjet.eta())) { + continue; + } + if (mcpjet.pt() > genLeadingJetPt) { + hasGenLeadingJet = true; + genLeadingJetPt = mcpjet.pt(); + genLeadingJetEta = mcpjet.eta(); + genLeadingJetPhi = mcpjet.phi(); + } + } + + if (hasGenLeadingJet && genLeadingJetPt > cfgjetPtBkgSubMinMC) { + jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 0.5); + jetHist.fill(HIST("jetSelCorr/genSel/hLeadingJetPt"), genLeadingJetPt); + fillJetSelectionCorrectionParticles(mcParticles, genLeadingJetEta, genLeadingJetPhi); + } + + bool hasRecoSelectedLeadingJet = false; + double recoSelectedLeadingJetPt = -999.; + double recoSelectedMatchedGenJetPt = -999.; + double recoSelectedMatchedGenJetEta = -999.; + double recoSelectedMatchedGenJetPhi = -999.; + + for (const auto& mcpjet : mcpjets) { + if (!mcpjet.has_matchedJetGeo()) { + continue; + } + if (!isConeAxisAccepted(mcpjet.eta())) { + continue; + } + for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { + if (!isConeAxisAccepted(mcdjet.eta())) { + continue; + } + double selectedRecoCollisionRho = -1.; + bool hasSelectedRecoCollision = false; + for (const auto& recocoll : recocolls) { + if (mcdjet.collisionId() != recocoll.globalIndex()) { + continue; + } + if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { + continue; + } + selectedRecoCollisionRho = recocoll.rho(); + hasSelectedRecoCollision = true; + break; + } + if (!hasSelectedRecoCollision) { + continue; + } + + const double jetArea = M_PI * cfgjetR * cfgjetR; + const double mcdJetPtBkgSub = usebkgSubractionMC ? mcdjet.pt() - selectedRecoCollisionRho * jetArea : mcdjet.pt(); + if (mcdJetPtBkgSub <= cfgjetPtBkgSubMinMC) { + continue; + } + if (mcdJetPtBkgSub > recoSelectedLeadingJetPt) { + hasRecoSelectedLeadingJet = true; + recoSelectedLeadingJetPt = mcdJetPtBkgSub; + recoSelectedMatchedGenJetPt = mcpjet.pt(); + recoSelectedMatchedGenJetEta = mcpjet.eta(); + recoSelectedMatchedGenJetPhi = mcpjet.phi(); + } + } + } + + if (hasRecoSelectedLeadingJet) { + jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 1.5); + jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), recoSelectedLeadingJetPt); + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), recoSelectedLeadingJetPt, recoSelectedMatchedGenJetPt); + fillJetSelectionCorrectionParticles(mcParticles, recoSelectedMatchedGenJetEta, recoSelectedMatchedGenJetPhi); + } + } + void processGenMatched(aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, JetMCDetTable const&, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) @@ -3096,6 +3271,7 @@ struct nucleiInJets { PROCESS_SWITCH(nucleiInJets, processMCRec, "nuclei in Jets for detectorlevel Jets", false); PROCESS_SWITCH(nucleiInJets, processMCGen, "nuclei in Jets MC particlelevel Jets", false); PROCESS_SWITCH(nucleiInJets, processRecMatched, "nuclei in Jets rec matched", false); + PROCESS_SWITCH(nucleiInJets, processJetSelectionCorrection, "nuclei in Jets jet-selection migration correction", false); PROCESS_SWITCH(nucleiInJets, processGenMatched, "nuclei in Jets gen matched", false); PROCESS_SWITCH(nucleiInJets, processEventSignalLoss, "Event and signal loss analysis (inclusive)", false); }; From 8e92d10faf78b58e86b17e673318e8644ca94297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 23 Sep 2026 12:24:49 +0200 Subject: [PATCH 045/648] [PWGLF] Simplify h-d pair selection logic (#18020) - enable vs Y correlation --- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 665 ++++++------------ 1 file changed, 200 insertions(+), 465 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index cead75239f5..120e5893138 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -188,6 +188,7 @@ struct HadronNucleiCorrelation { // Generated-level pairing: PDG codes of the two species selected by `mode` (set in init) int pdgPart0 = 0; int pdgPart1 = 0; + std::pair massPart0Part1; // masses of the two species selected by `mode` (set in init) // Lightweight copy of a generated particle, so that the generated-level pairing runs on // compact candidate vectors instead of re-reading the full MC particle table for every pair @@ -255,7 +256,9 @@ struct HadronNucleiCorrelation { const AxisSpec ptAxisSmall = {100, -5.f, 5.f, "#it{p}_{T} GeV/#it{c}"}; const AxisSpec deltaEtaAxis = {settingsAxes.axisDeltaEta, "#Delta#eta"}; + LOG(info) << "DeltaEta axis: from " << deltaEtaAxis.binEdges[0] << " to " << deltaEtaAxis.binEdges[1]; const AxisSpec deltaRapAxis = {settingsAxes.axisDeltaRap, "#Delta y"}; + LOG(info) << "DeltaRap axis: from " << deltaRapAxis.binEdges[0] << " to " << deltaRapAxis.binEdges[1]; if (doprocessSameEvent || doprocessSameEventEvSel) { registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{7, 0.f, 7.f}}}); @@ -313,6 +316,34 @@ struct HadronNucleiCorrelation { default: LOG(fatal) << "Unhandled case " << mode; } + // Set the masses of the two species selected by `mode` + switch (pdgPart0) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + massPart0Part1.first = o2::track::PID::getMass(o2::track::PID::Deuteron); + break; + case PDG_t::kProton: + case -PDG_t::kProton: + massPart0Part1.first = o2::track::PID::getMass(o2::track::PID::Proton); + break; + default: + LOG(fatal) << "Unhandled pdgPart0 " << pdgPart0; + } + switch (pdgPart1) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + massPart0Part1.second = o2::track::PID::getMass(o2::track::PID::Deuteron); + break; + case PDG_t::kProton: + case -PDG_t::kProton: + massPart0Part1.second = o2::track::PID::getMass(o2::track::PID::Proton); + break; + default: + LOG(fatal) << "Unhandled pdgPart1 " << pdgPart1; + } + if (massPart0Part1.first <= 0.f || massPart0Part1.second <= 0.f) { + LOG(fatal) << "Masses of the two species selected by `mode` are not set correctly: " << massPart0Part1.first << ", " << massPart0Part1.second; + } if (!isMC) { for (int i = 0; i < nBinspT; i++) { @@ -521,21 +552,21 @@ struct HadronNucleiCorrelation { Filter simvertexFilter = nabs(o2::aod::mccollision::posZ) <= cutzVertex; template - bool isProton(Type const& track, const int sign) + bool isProton(Type const& track, const int sign) const { - const bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC; - const bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF; - const bool isTPCElRejection = rejectionEl && track.beta() < BetahasTOFthr && track.pt() < pTthrprTPCEl && track.tpcNSigmaEl() >= nsigmaElPr; - const bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr; + const bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC.value; + const bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF.value; + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrprTPCEl.value && track.tpcNSigmaEl() >= nsigmaElPr.value; + const bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr.value; - const bool isQuadraticPID = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()) < nsigmaQuadratic; + const bool isQuadraticPID = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()) < nsigmaQuadratic.value; // Check if the sign of the track matches the expected sign for protons or antiprotons const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); - if (!doQuadraticPID) { + if (!doQuadraticPID.value) { if (isTPCPID) { - if (track.pt() < pTthrprTOF) { - if (!doITSPID || isITSPID) { + if (track.pt() < pTthrprTOF.value) { + if (!doITSPID.value || isITSPID) { return signCheck; } } else if (isTPCElRejection || isTOFPID) { @@ -543,9 +574,9 @@ struct HadronNucleiCorrelation { } } } else { - if (track.pt() < pTthrprTOF) { + if (track.pt() < pTthrprTOF.value) { if (isTPCPID) { - if (!doITSPID || isITSPID) { + if (!doITSPID.value || isITSPID) { return signCheck; } } @@ -557,21 +588,21 @@ struct HadronNucleiCorrelation { } template - bool isDeuteron(Type const& track, const int sign) + bool isDeuteron(Type const& track, const int sign) const { - const bool isTPCPID = std::abs(track.tpcNSigmaDe()) < nsigmaTPC; - const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF; - const bool isTPCElRejection = rejectionEl && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl && track.tpcNSigmaEl() >= nsigmaElDe; - const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe; + const bool isTPCPID = std::abs(track.tpcNSigmaDe()) < nsigmaTPC.value; + const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; + const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; - const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic; + const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; // Check if the sign of the track matches the expected sign for deuterons or antideuterons const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); - if (!doQuadraticPID) { + if (!doQuadraticPID.value) { if (isTPCPID) { - if (track.pt() < pTthrdeTOF) { - if (!doITSPID || isITSPID) { + if (track.pt() < pTthrdeTOF.value) { + if (!doITSPID.value || isITSPID) { return signCheck; } } else if (isTPCElRejection || isTOFPID) { @@ -579,9 +610,9 @@ struct HadronNucleiCorrelation { } } } else { - if (track.pt() < pTthrdeTOF) { + if (track.pt() < pTthrdeTOF.value) { if (isTPCPID) { - if (!doITSPID || isITSPID) { + if (!doITSPID.value || isITSPID) { return signCheck; } } @@ -593,13 +624,13 @@ struct HadronNucleiCorrelation { } template - bool applyDCAcut(const T1& track) + bool applyDCAcut(const T1& track) const { // pt-dependent selection - if (std::abs(track.dcaXY()) > (dcaPar0 + dcaPar1 / track.pt())) { + if (std::abs(track.dcaXY()) > (dcaPar0.value + dcaPar1.value / track.pt())) { return false; } - if (doDCAZ && std::abs(track.dcaZ()) > (dcaPar0 + dcaPar1 / track.pt())) { + if (doDCAZ.value && std::abs(track.dcaZ()) > (dcaPar0.value + dcaPar1.value / track.pt())) { return false; } return true; @@ -622,6 +653,9 @@ struct HadronNucleiCorrelation { const float deltaEta = part0.eta() - part1.eta(); const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); + const float deltaRapidity = part0.rapidity(massPart0Part1.first) - part1.rapidity(massPart0Part1.second); + // Use rapidity difference if doRapidity is true, otherwise use pseudorapidity difference + const float x = doRapidity ? deltaRapidity : deltaEta; for (int k = 0; k < nBinspT; k++) { @@ -670,14 +704,14 @@ struct HadronNucleiCorrelation { } if (ME) { - hDeltaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); if (corr0 != 0 && corr1 != 0) { - hCorrDeltaPhiMixdEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hCorrDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); } } else { - hDeltaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); if (corr0 != 0 && corr1 != 0) { - hCorrDeltaPhiSameEv[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hCorrDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); } } // SE } // pT condition @@ -692,16 +726,17 @@ struct HadronNucleiCorrelation { const float deltaEta = part0.eta() - part1.eta(); const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); - // Here we have to use doRapidity const float deltaRapidity = part0.rapidityForPdg(pdgPart0) - part1.rapidityForPdg(pdgPart1); + // Use rapidity difference if doRapidity is true, otherwise use pseudorapidity difference + const float x = doRapidity ? deltaRapidity : deltaEta; for (int k = 0; k < nBinspT; k++) { if (part0.pt() >= pTBins.value.at(k) && part0.pt() < pTBins.value.at(k + 1)) { if (ME) { - hDeltaPhiMixdEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); - hCorrDeltaPhiMixdEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); + hDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); + hCorrDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); } else { - hDeltaPhiSameEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); - hCorrDeltaPhiSameEv[k]->Fill(doRapidity ? deltaRapidity : deltaEta, deltaPhi, part1.pt()); + hDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); + hCorrDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); } // SE } // pT condition } // nBinspT loop @@ -791,6 +826,121 @@ struct HadronNucleiCorrelation { return colCache; } + template + bool selectPair(T const& part0, T const& part1) const + { + if constexpr (eventSelection) { + if (removeSameBunchPileup.value) { + if (!part0.template singleCollSel_as().isNoSameBunchPileup()) { + return false; + } + if (!part1.template singleCollSel_as().isNoSameBunchPileup()) { + return false; + } + } + } + if (part0.tpcFractionSharedCls() > maxtpcSharedCls.value) { + return false; + } + if (part0.itsNCls() < minitsNCls.value) { + return false; + } + if (part1.tpcFractionSharedCls() > maxtpcSharedCls.value) { + return false; + } + if (part1.itsNCls() < minitsNCls.value) { + return false; + } + if (!applyDCAcut(part0)) { + return false; + } + if (!applyDCAcut(part1)) { + return false; + } + // remove tracks outside pt bins + if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { + return false; + } + if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { + return false; + } + return true; + }; + + template + bool selectPairPid(const T& part0, const T& part1) const + { + switch (mode.value) { + case kPP: // mode 6 + if (!isProton(part0, +1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kPbarPbar: // mode 5 + if (!isProton(part0, -1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kDbarPbar: + if (!isDeuteron(part0, -1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kDP: + if (!isDeuteron(part0, +1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kDbarP: + if (!isDeuteron(part0, -1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kDPbar: + if (!isDeuteron(part0, +1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kPbarP: + if (!isProton(part0, -1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kPPbar: + if (!isProton(part0, +1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + default: + LOG(fatal) << "Unknown mode: " << mode; + } + return true; + } + void processSameEvent(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) { @@ -887,53 +1037,14 @@ struct HadronNucleiCorrelation { for (const auto& [part0, part1] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { + if (!selectPair(part0, part1)) { continue; } - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { + if (!selectPairPid(part0, part1)) { continue; } - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - - // mode 6 - if (mode == kPP) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - // mode 5 - if (mode == kPbarPbar) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - fillHistograms(part0, part1, false, true); } @@ -945,84 +1056,14 @@ struct HadronNucleiCorrelation { continue; } - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { - continue; - } - - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { + if (!selectPair(part0, part1)) { continue; } - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { + if (!selectPairPid(part0, part1)) { continue; } - // modes 0,1,2,3,4,7 - if (mode == kDbarPbar) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kDP) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDbarP) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDPbar) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPbarP) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPPbar) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - fillHistograms(part0, part1, false, false); } } @@ -1117,61 +1158,18 @@ struct HadronNucleiCorrelation { pair->SetMagField1(collision.magField()); pair->SetMagField2(collision.magField()); - if (mode == kPbarPbar || mode == kPP) { // Identical particle combinations + if (mode == kPbarPbar || mode == kPP) { // Identical particle combinations (only pp or pbarpbar) for (const auto& [part0, part1] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { - if (removeSameBunchPileup && !part0.template singleCollSel_as().isNoSameBunchPileup()) { - continue; - } - - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { - continue; - } - - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { + if (!selectPair(part0, part1)) { continue; } - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { + if (!selectPairPid(part0, part1)) { continue; } - // mode 6 - if (mode == kPP) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - // mode 5 - if (mode == kPbarPbar) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - fillHistograms(part0, part1, false, true); } @@ -1182,88 +1180,13 @@ struct HadronNucleiCorrelation { if (part0.globalIndex() == part1.globalIndex()) { continue; } - - if (removeSameBunchPileup && !part0.template singleCollSel_as().isNoSameBunchPileup()) { + if (!selectPair(part0, part1)) { continue; } - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { + if (!selectPairPid(part0, part1)) { continue; } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { - continue; - } - - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { - continue; - } - - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - - // modes 0,1,2,3,4,7 - if (mode == kDbarPbar) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kDP) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDbarP) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDPbar) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPbarP) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPPbar) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } fillHistograms(part0, part1, false, false); } @@ -1295,105 +1218,15 @@ struct HadronNucleiCorrelation { for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { + if (!selectPair(part0, part1)) { continue; } - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { - continue; - } - - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { + if (!selectPairPid(part0, part1)) { continue; } - //{"mode", 0, "0: antid-antip, 1: d-p, 2: antid-p, 3: d-antip, 4: antip-p, 5: antip-antip, 6: p-p, 7: p-antip"}; - if (mode == kDbarPbar) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kDP) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDbarP) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDPbar) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPbarP) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPbarPbar) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPP) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPPbar) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - - bool isIdentical = false; - if (mode == kPbarPbar || mode == kPP) { - isIdentical = true; - } - + const bool isIdentical = (mode == kPbarPbar || mode == kPP); fillHistograms(part0, part1, true, isIdentical); } } @@ -1413,7 +1246,7 @@ struct HadronNucleiCorrelation { const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); - const float limit = 1e-4; + constexpr float limit = 1e-4; if (std::abs(magFieldTesla1 - magFieldTesla2) > limit) { continue; @@ -1424,112 +1257,14 @@ struct HadronNucleiCorrelation { for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (removeSameBunchPileup && !part0.template singleCollSel_as().isNoSameBunchPileup()) { - continue; - } - if (removeSameBunchPileup && !part1.template singleCollSel_as().isNoSameBunchPileup()) { - continue; - } - - if (part0.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part0.itsNCls() < minitsNCls) { - continue; - } - if (part1.tpcFractionSharedCls() > maxtpcSharedCls) { - continue; - } - if (part1.itsNCls() < minitsNCls) { + if (!selectPair(part0, part1)) { continue; } - if (!applyDCAcut(part0)) { - continue; - } - if (!applyDCAcut(part1)) { + if (!selectPairPid(part0, part1)) { continue; } - - // remove tracks outside pt bins - if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { - continue; - } - - //{"mode", 0, "0: antid-antip, 1: d-p, 2: antid-p, 3: d-antip, 4: antip-p, 5: antip-antip, 6: p-p, 7: p-antip"}; - if (mode == kDbarPbar) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kDP) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDbarP) { - if (!isDeuteron(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kDPbar) { - if (!isDeuteron(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPbarP) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPbarPbar) { - if (!isProton(part0, -1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - if (mode == kPP) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, +1)) { - continue; - } - } - if (mode == kPPbar) { - if (!isProton(part0, +1)) { - continue; - } - if (!isProton(part1, -1)) { - continue; - } - } - - bool isIdentical = false; - if (mode == kPbarPbar || mode == kPP) { - isIdentical = true; - } - + const bool isIdentical = (mode == kPbarPbar || mode == kPP); fillHistograms(part0, part1, true, isIdentical); } } From a899ce098c06099e6b11adec857efac8d76936b3 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Wed, 23 Sep 2026 12:35:42 +0200 Subject: [PATCH 046/648] [DPG] Skip ambiguous tracks with an empty BC slice in the event selection QA (#18019) --- DPG/Tasks/AOTEvent/eventSelectionQa.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index 13278810e0c..c5da0ee0076 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -1009,8 +1009,12 @@ struct EventSelectionQaTask { // special check to avoid crashes (in particular, on some MC Pb-Pb datasets) // (related to shifts in ambiguous tracks association to bc slices (off by 1) - see https://mattermost.web.cern.ch/alice/pl/g9yaaf3tn3g4pgn7c1yex9copy - if (ambTrId >= 0 && (ambTracks.iteratorAt(ambTrId).bcIds()[0] >= bcs.size())) - continue; + // an empty slice (-1, -1) passes the upper check, so test it as well + if (ambTrId >= 0) { + const auto& ambTrack = ambTracks.iteratorAt(ambTrId); + if (!ambTrack.has_bc() || ambTrack.bcIds()[0] >= bcs.size() || ambTrack.bcIds()[1] >= bcs.size()) + continue; + } int indexBc = ambTrId < 0 ? track.collision_as().bc_as().globalIndex() : ambTracks.iteratorAt(ambTrId).bc_as().begin().globalIndex(); auto bc = bcs.iteratorAt(indexBc); From 59c27d1f7dfeec1e407024ca13a12f2551008870 Mon Sep 17 00:00:00 2001 From: fuchuncui <162277233+fuchuncui@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:29:12 +0800 Subject: [PATCH 047/648] [PWGCF] Add MC process for cascades' correlation (#18021) --- .../Tasks/cascDiHadronCorr.cxx | 351 ++++++++++++++---- 1 file changed, 270 insertions(+), 81 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx index b5e515105c6..7aa30d4f202 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx @@ -88,21 +88,10 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyCasc, std::string, "", "CCDB path to efficiency object") O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") O2_DEFINE_CONFIGURABLE(cfgVerbosity, bool, false, "Verbose output") @@ -115,6 +104,21 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgNSigmapid, std::vector, (std::vector{3, 3, 3}), "tpc NSigma for Pion Proton Kaon") O2_DEFINE_CONFIGURABLE(cfgOutputXi, bool, true, "Output Xi-charged correlation") O2_DEFINE_CONFIGURABLE(cfgOutputOmega, bool, false, "Output Omega-charged correlation") + struct : ConfigurableGroup { + std::string prefix = "cfgEvSel"; + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") + } cfgEvSel; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -215,6 +219,7 @@ struct CascDiHadronCorr { // Corrections TH3D* mEfficiency = nullptr; + TH3D* mEfficiencyCasc = nullptr; TH1D* mCentralityWeight = nullptr; bool correctionsLoaded = false; @@ -244,24 +249,14 @@ struct CascDiHadronCorr { float eta; float phi; float pt; - int region; - float efficiency; - float efficiencyError; - int type; }; - float pvz; - float mult; - std::vector trigParticles; + float pvz = 0; + float mult = 0; std::vector assocParticles; - void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type) + void addValidParticle(float eta, float phi, float pt) { - ValidParticle particle{eta, phi, pt, region, efficiency, efficiencyError, type}; - - if (type == -1) { - trigParticles.push_back(particle); - } else { - assocParticles.push_back(particle); - } + ValidParticle particle{eta, phi, pt}; + assocParticles.push_back(particle); } }; using ValidCollisions = std::vector>; @@ -296,7 +291,7 @@ struct CascDiHadronCorr { LOGF(info, "Starting init"); // Event Counter - if (doprocessSame && cfgUseAdditionalEventCut) { + if (doprocessSame && cfgEvSel.cfgUseAdditionalEventCut.value) { registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{12, 0, 12}}}); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(1, "after sel8"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(2, "kNoSameBunchPileup"); @@ -312,7 +307,7 @@ struct CascDiHadronCorr { registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(12, "cfgEvSelV0AT0ACut"); } - if (cfgEvSelMultCorrelation) { + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; @@ -381,7 +376,7 @@ struct CascDiHadronCorr { if (doprocessMCMixed && doprocessOntheflyMixed) { LOGF(fatal, "Full simulation and on-the-fly processing of mixed event not supported"); } - if (doprocessMCSame) { + if (doprocessMCSame || doprocessMCSameCasc) { registry.add("MCTrue/MCeventcount", "MCeventcount", {HistType::kTH1F, {{5, 0, 5, "bin"}}}); // histogram to see how many events are in the same and mixed event registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(2, "same all"); registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(3, "same reco"); @@ -404,6 +399,9 @@ struct CascDiHadronCorr { registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(3, "Reco Primary"); registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(4, "Reco All"); registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(5, "Fake"); + registry.add("MCGen", "MCGen", {HistType::kTH1D, {axisPtEfficiency}}); + registry.add("MCRec", "MCRec", {HistType::kTH1D, {axisPtEfficiency}}); + registry.add("MCRecPri", "MCRecPri", {HistType::kTH1D, {axisPtEfficiency}}); } LOGF(info, "Initializing correlation container"); @@ -663,33 +661,61 @@ struct CascDiHadronCorr { if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.value.c_str()); + } + if (cfgEfficiencyCasc.value.empty() == false) { + if (cfgLocalEfficiency > 0) { + TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyCasc.value.c_str(), "READ"); + mEfficiencyCasc = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { + mEfficiencyCasc = ccdb->getForTimeStamp(cfgEfficiencyCasc, timestamp); + } + if (mEfficiencyCasc == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyCasc.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiencyCasc.value.c_str()); } if (cfgCentralityWeight.value.empty() == false) { mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s", cfgCentralityWeight.value.c_str()); } correctionsLoaded = true; } - bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ, bool isCh = true) { - float eff = 1.; - if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); - int ptBin = mEfficiency->GetYaxis()->FindBin(pt); - int zBin = mEfficiency->GetZaxis()->FindBin(posZ); - eff = mEfficiency->GetBinContent(etaBin, ptBin, zBin); + if (isCh) { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int zBin = mEfficiency->GetZaxis()->FindBin(posZ); + eff = mEfficiency->GetBinContent(etaBin, ptBin, zBin); + } else { + eff = 1.0; + } + if (eff == 0) + return false; + weight_nue = 1. / eff; + return true; } else { - eff = 1.0; + float eff = 1.; + if (mEfficiencyCasc) { + int etaBin = mEfficiencyCasc->GetXaxis()->FindBin(eta); + int ptBin = mEfficiencyCasc->GetYaxis()->FindBin(pt); + int zBin = mEfficiencyCasc->GetZaxis()->FindBin(posZ); + eff = mEfficiencyCasc->GetBinContent(etaBin, ptBin, zBin); + } else { + eff = 1.0; + } + if (eff == 0) + return false; + weight_nue = 1. / eff; + return true; } - if (eff == 0) - return false; - weight_nue = 1. / eff; - return true; } bool getCentralityWeight(float& weightCent, const float centrality) @@ -822,7 +848,7 @@ struct CascDiHadronCorr { for (const auto& track1 : tracks1) { if (!cascSelected(track1, posX, posY, posZ)) continue; - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ, false)) continue; int index = 0; @@ -961,7 +987,7 @@ struct CascDiHadronCorr { if (!cascSelected(track1, posX, posY, posZ)) continue; - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ, false)) continue; if (system == SameEvent) { registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); @@ -976,15 +1002,15 @@ struct CascDiHadronCorr { auto negdau = track1.template negTrack_as(); for (auto const& track2 : tracks2) { - if (!trackSelected(track2)) continue; if (mEfficiency) { associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; } - if (!cfgUsePtOrder && bachelor.globalIndex() == track2.globalIndex()) + if (!cfgUsePtOrder && bachelor.globalIndex() == track2.globalIndex()) { continue; // For pt-differential correlations, skip if the trigger bachelor and associate are the same track + } if (!cfgUsePtOrder && posdau.globalIndex() == track2.globalIndex()) continue; // For pt-differential correlations, skip if the trigger posdau and associate are the same track if (!cfgUsePtOrder && negdau.globalIndex() == track2.globalIndex()) @@ -1181,67 +1207,120 @@ struct CascDiHadronCorr { } } + template + void fillMCCorrelationsCasc(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track1.isPhysicalPrimary()) + continue; + + if (!((cfgOutputXi && getSpecies(track1.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(track1.pdgCode()) == getSpecies(PDG_t::kOmegaMinus)))) + continue; + + if (system == SameEvent && (doprocessMCSameCasc)) + registry.fill(HIST("MCTrue/MCTrig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + + for (auto const& track2 : tracks2) { + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track2.isPhysicalPrimary()) + continue; + + if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + if (doprocessMCSameCasc || doprocessOntheflySame) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassXiMinus, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassOmegaMinus, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + if (doprocessMCMixedCasc || doprocessOntheflyMixed) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassXiMinus, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassOmegaMinus, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + template bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) { registry.fill(HIST("hEventCountSpecific"), 0.5); - if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (cfgEvSel.cfgEvSelkNoSameBunchPileup.value && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - if (fillCounter && cfgEvSelkNoSameBunchPileup) + if (fillCounter && cfgEvSel.cfgEvSelkNoSameBunchPileup.value) registry.fill(HIST("hEventCountSpecific"), 1.5); - if (cfgEvSelkNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + if (cfgEvSel.cfgEvSelkNoITSROFrameBorder.value && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return 0; } - if (fillCounter && cfgEvSelkNoITSROFrameBorder) + if (fillCounter && cfgEvSel.cfgEvSelkNoITSROFrameBorder.value) registry.fill(HIST("hEventCountSpecific"), 2.5); - if (cfgEvSelkNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + if (cfgEvSel.cfgEvSelkNoTimeFrameBorder.value && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return 0; } - if (fillCounter && cfgEvSelkNoTimeFrameBorder) + if (fillCounter && cfgEvSel.cfgEvSelkNoTimeFrameBorder.value) registry.fill(HIST("hEventCountSpecific"), 3.5); - if (cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (cfgEvSel.cfgEvSelkIsGoodZvtxFT0vsPV.value && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return 0; } - if (fillCounter && cfgEvSelkIsGoodZvtxFT0vsPV) + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodZvtxFT0vsPV.value) registry.fill(HIST("hEventCountSpecific"), 4.5); - if (cfgEvSelkNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (cfgEvSel.cfgEvSelkNoCollInTimeRangeStandard.value && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range return 0; } - if (fillCounter && cfgEvSelkNoCollInTimeRangeStandard) + if (fillCounter && cfgEvSel.cfgEvSelkNoCollInTimeRangeStandard.value) registry.fill(HIST("hEventCountSpecific"), 5.5); - if (cfgEvSelkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves return 0; } - if (fillCounter && cfgEvSelkIsGoodITSLayersAll) + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value) registry.fill(HIST("hEventCountSpecific"), 6.5); - if (cfgEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold return 0; } - if (fillCounter && cfgEvSelkNoCollInRofStandard) + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value) registry.fill(HIST("hEventCountSpecific"), 7.5); - if (cfgEvSelkNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold return 0; } - if (fillCounter && cfgEvSelkNoHighMultCollInPrevRof) + if (fillCounter && cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) registry.fill(HIST("hEventCountSpecific"), 8.5); auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgEvSelOccupancy && (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh)) + if (cfgEvSel.cfgEvSelOccupancy.value && (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh)) return 0; - if (fillCounter && cfgEvSelOccupancy) + if (fillCounter && cfgEvSel.cfgEvSelOccupancy.value) registry.fill(HIST("hEventCountSpecific"), 9.5); auto multNTracksPV = collision.multNTracksPV(); - if (cfgEvSelMultCorrelation) { + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) { if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(centrality)) return 0; @@ -1267,14 +1346,14 @@ struct CascDiHadronCorr { return 0; } } - if (fillCounter && cfgEvSelMultCorrelation) + if (fillCounter && cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) registry.fill(HIST("hEventCountSpecific"), 10.5); // V0A T0A 5 sigma cut float sigma = 5.0; - if (cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) + if (cfgEvSel.cfgEvSelV0AT0ACut.value && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) return 0; - if (fillCounter && cfgEvSelV0AT0ACut) + if (fillCounter && cfgEvSel.cfgEvSelV0AT0ACut.value) registry.fill(HIST("hEventCountSpecific"), 11.5); return 1; @@ -1329,7 +1408,7 @@ struct CascDiHadronCorr { if (!cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, true)) return; loadCorrection(bc.timestamp()); if (!cfgCentTableUnavailable) { @@ -1395,9 +1474,9 @@ struct CascDiHadronCorr { cent1 = getCentrality(collision1); cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision1, tracks1.size(), cent1, false)) continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision2, tracks2.size(), cent2, false)) continue; if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) @@ -1456,9 +1535,9 @@ struct CascDiHadronCorr { cent1 = getCentrality(collision1); cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision1, tracks1.size(), cent1, false)) continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision2, tracks2.size(), cent2, false)) continue; if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) @@ -1505,14 +1584,14 @@ struct CascDiHadronCorr { if (!collision.sel8()) return; - if (cfgSelCollByNch && tracks.size() < cfgCutMultMin) + if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) return; float cent = -1; if (!cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, false)) return; if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) @@ -1525,7 +1604,7 @@ struct CascDiHadronCorr { for (const auto& track : tracks) { if (!trackSelected(track)) continue; - currentCollision.addValidParticle(track.eta(), track.phi(), track.pt(), 0, 1, 1, 1); + currentCollision.addValidParticle(track.eta(), track.phi(), track.pt()); } fillCorrelations(tracks, currentCollision, collision.posZ(), bin, weightCent); @@ -1558,14 +1637,14 @@ struct CascDiHadronCorr { if (!collision.sel8()) return; - if (cfgSelCollByNch && tracks.size() < cfgCutMultMin) + if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) return; float cent = -1; if (!cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, false)) + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, false)) return; if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) @@ -1578,7 +1657,7 @@ struct CascDiHadronCorr { for (const auto& track : tracks) { if (!trackSelected(track)) continue; - currentCollision.addValidParticle(track.eta(), track.phi(), track.pt(), 0, 1, 1, 1); + currentCollision.addValidParticle(track.eta(), track.phi(), track.pt()); } fillCorrelationsCasc(cascades, currentCollision, collision.posX(), collision.posY(), collision.posZ(), bin, weightCent); @@ -1612,6 +1691,7 @@ struct CascDiHadronCorr { if (mcParticle.isPhysicalPrimary()) { if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) { registry.fill(HIST("MCEffeventcount"), 1.5); + registry.fill(HIST("MCGen"), mcParticle.pt()); same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), 0., mcCollision.posZ()); } } @@ -1630,9 +1710,13 @@ struct CascDiHadronCorr { auto mcParticle = casc.mcParticle_as(); if (mcParticle.isPhysicalPrimary()) { registry.fill(HIST("MCEffeventcount"), 2.5); + if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) + registry.fill(HIST("MCRecPri"), mcParticle.pt()); same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), 0., mcCollision.posZ()); } registry.fill(HIST("MCEffeventcount"), 3.5); + if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) + registry.fill(HIST("MCRec"), mcParticle.pt()); same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, mcParticle.eta(), mcParticle.pt(), (cfgOutputXi * getSpecies(PDG_t::kXiMinus) + cfgOutputOmega * getSpecies(PDG_t::kOmegaMinus)), 0., mcCollision.posZ()); } else { // fake casc @@ -1692,6 +1776,54 @@ struct CascDiHadronCorr { } PROCESS_SWITCH(CascDiHadronCorr, processMCSame, "Process MC same event", false); + void processMCSameCasc(FilteredMcCollisions::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + if (cfgVerbosity) { + LOGF(info, "processMCSame. MC collision: %d, particles: %d, collisions: %d", mcCollision.globalIndex(), mcParticles.size(), collisions.size()); + } + + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + } + + if (cfgSelCollByNch && (mcParticles.size() < cfgCutMultMin || mcParticles.size() >= cfgCutMultMax)) { + return; + } + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + + if (cfgUseCFStepAll) { + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + fillMCCorrelationsCasc(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + + if (collisions.size() == 0) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 2.5); + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelationsCasc(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + PROCESS_SWITCH(CascDiHadronCorr, processMCSameCasc, "Process MC same event", false); + void processMCMixed(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { @@ -1747,6 +1879,63 @@ struct CascDiHadronCorr { } } PROCESS_SWITCH(CascDiHadronCorr, processMCMixed, "Process MC mixed events", false); + + void processMCMixedCasc(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgVerbosity > 0) { + LOGF(info, "Found %d related collisions", groupedCollisions.size()); + } + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions) { + cent = getCentrality(collision); + } + } + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && groupedCollisions.size() != 0 && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgUseCFStepAll) + fillMCCorrelationsCasc(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + + if (groupedCollisions.size() == 0) { + continue; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + fillMCCorrelationsCasc(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processMCMixedCasc, "Process MC mixed events", false); + void processOntheflySame(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { if (cfgVerbosity) { From 5f871bf489172793f7f126506de1720de79f7449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:17:10 +0200 Subject: [PATCH 048/648] [PWGHF] Add charm D-D-meson femtoscopy workflows (#17998) Co-authored-by: ALICE Action Bot --- PWGHF/HFC/TableProducer/CMakeLists.txt | 5 + .../producerCharmHadronsCharmFemtoDream.cxx | 405 ++++++++++++++++ PWGHF/HFC/Tasks/CMakeLists.txt | 5 + .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 457 ++++++++++++++++++ 4 files changed, 872 insertions(+) create mode 100644 PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx create mode 100644 PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index d0c1fb800ff..299981d8c1f 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -98,3 +98,8 @@ o2physics_add_dpl_workflow(producer-charm-hadrons-v0-femto-dream SOURCES producerCharmHadronsV0FemtoDream.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(producer-charm-hadrons-charm-femto-dream + SOURCES producerCharmHadronsCharmFemtoDream.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..a0d8e189ae2 --- /dev/null +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,405 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file producerCharmHadronsCharmFemtoDream.cxx +/// \brief Produce reduced D-meson candidate tables for charm-charm femtoscopy +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfMlResponseD0ToKPi.h" +#include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct HfProducerCharmHadronsCharmFemtoDream { + enum MlMode : uint8_t { + NoMl = 0, + FillMlFromSelector = 1, + FillMlFromNewBDT = 2 + }; + static constexpr std::size_t NMlClasses = 3; + static constexpr std::size_t NMlPtEdgesMin = 2; + static constexpr int ND0Hypotheses = 2; + // Each species needs its own model, feature order and pT-dependent cuts. + struct HfMlConfig : ConfigurableGroup { + std::string prefix; + static inline const std::array defaultCuts{1., 0., 0.}; + Configurable mlApplicationMode{"mlApplicationMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; + Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; + Configurable> cutsMl{"cutsMl", {defaultCuts.data(), 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; + Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; + }; + + Produces collisions; + Produces rowMasks; + Produces d0Rows; + Produces dstarRows; + + Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + HfMlConfig mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; + + Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; + o2::hf_evsel::HfEventSelection hfEvSel; + o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; + o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; + + using Collisions = soa::Join; + using CollisionsWithFT0C = soa::Join; + using D0s = soa::Join; + using Dstars = soa::Join; + + using D0sMl = soa::Join; + using DstarsMl = soa::Join; + + Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + + HistogramRegistry registry{"registry"}; + OutputObj zorroSummary{"zorroSummary"}; + + bool hasD0 = false; + bool hasDstar = false; + int runNumber = -1; + + template + void initMl(HfMlConfig const& cfg, Response& response, bool withMl) + { + if (cfg.mlApplicationMode.value < NoMl || cfg.mlApplicationMode.value > FillMlFromNewBDT) { + LOGP(fatal, "{}: invalid mlApplicationMode", cfg.prefix); + } + if (cfg.mlApplicationMode.value != FillMlFromNewBDT) { + return; + } + if (!withMl) { + LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); + } + auto const& edges = cfg.binsPtMl.value; + const bool areEdgesFinite = std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }); + const bool areEdgesIncreasing = std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) == edges.end(); + if (edges.size() < NMlPtEdgesMin || !areEdgesFinite || !areEdgesIncreasing) { + LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); + } + const auto nBins = edges.size() - 1; + if (cfg.nClassesMl.value != NMlClasses || cfg.cutDirMl.value.size() != NMlClasses || cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != NMlClasses || cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || (cfg.loadModelsFromCCDB.value && cfg.modelPathsCCDB.value.size() != nBins)) { + LOGP(fatal, "{}: provide three classes, cuts/models for every pT bin, and input features", cfg.prefix); + } + for (const auto& direction : cfg.cutDirMl.value) { + if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { + LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); + } + } + for (unsigned int bin = 0; bin < nBins; ++bin) { + for (unsigned int score = 0; score < NMlClasses; ++score) { + if (!std::isfinite(cfg.cutsMl.value.get(bin, score))) { + LOGP(fatal, "{}: cutsMl must be finite", cfg.prefix); + } + } + } + response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, cfg.nClassesMl.value); + response.cacheInputFeaturesIndices(cfg.namesInputFeatures.value); + if (cfg.loadModelsFromCCDB.value) { + ccdbApi.init(ccdbUrl.value); + response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, cfg.modelPathsCCDB.value, cfg.timestampCCDB.value); + } else { + response.setModelPathsLocal(cfg.onnxFileNames.value); + } + response.init(); + } + + void init(InitContext const&) + { + const std::array processSwitches{doprocessD0D0, doprocessD0Dstar, doprocessD0D0Ml, doprocessD0DstarMl, doprocessD0D0WithFT0C, doprocessD0DstarWithFT0C, doprocessD0D0MlWithFT0C, doprocessD0DstarMlWithFT0C}; + if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { + LOGP(fatal, "Enable exactly one charm-charm producer process"); + } + if (selectionFlagD0 < 1) { + LOGP(fatal, "selectionFlagD0 must be positive"); + } + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; + initMl(mlD0, hfMlResponseD0, withMl); + if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || doprocessD0DstarMlWithFT0C) { + initMl(mlDstar, hfMlResponseDstar, withMl); + } + hfEvSel.init(registry, &zorroSummary); + const AxisSpec eventStage{2, -0.5, 1.5, "stage (0=all,1=accepted)"}; + const AxisSpec candidateSpecies{4, -0.5, 3.5, "species (0=D0,1=D0bar,2=Dstar+,3=Dstar-)"}; + registry.add("events", "Events", kTH1F, {eventStage}); + registry.add("candidates", "Candidates", kTH1F, {candidateSpecies}); + } + + template + bool acceptCollision(CollType const& col) + { + registry.fill(HIST("events"), 0); + float cent = -1.f; // No centrality for pp MB. + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(col, cent, ccdb, registry); + hfEvSel.fillHistograms(col, rejectionMask, cent); + if (rejectionMask != 0) { + return false; + } + hasD0 = false; + hasDstar = false; + auto bc = col.template bc_as(); + initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); + // Propagator field is in kG; FemtoDream uses Tesla. + const float fieldTesla = 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + // MultV0M is the common reduced percentile column: FT0C or -1 (no + // estimator). + collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); + registry.fill(HIST("events"), 1); + // Keep all accepted collisions, including zero/single-candidate events. + return true; + } + + template + std::array readScores(Scores const& scores) + { + if (scores.size() != NMlClasses) { + LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); + return {-1.f, -1.f, -1.f}; + } + for (const auto& score : scores) { + if (!std::isfinite(score)) { + LOGP(fatal, "Non-finite selector ML score"); + } + } + return {scores[0], scores[1], scores[2]}; + } + + template + void fillD0(CollType const& col, CandsType const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (const auto& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + // The OR filter accepts the row if either hypothesis passes. Write only + // the passing hypotheses, each with its own flavour and ML scores. + for (int hypothesis = 0; hypothesis < ND0Hypotheses; ++hypothesis) { + if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < selectionFlagD0) { + continue; + } + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlD0.mlApplicationMode != NoMl) { + scores = hypothesis == 0 ? readScores(cand.mlProbD0()) : readScores(cand.mlProbD0bar()); + } + if (mlD0.mlApplicationMode == FillMlFromNewBDT) { + // Do not call the ML response with an out-of-range model index. + if (!std::isfinite(cand.pt()) || cand.pt() < mlD0.binsPtMl.value.front() || cand.pt() >= mlD0.binsPtMl.value.back()) { + continue; + } + const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; + auto features = hfMlResponseD0.getInputFeatures(cand, pdgCode); + std::vector output; + if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { + continue; // Reject this hypothesis only. + } + scores = readScores(output); + } + } + d0Rows(collisions.lastIndex(), timestamp, hypothesis == 0 ? 1 : -1, p0.globalIndex(), p1.globalIndex(), p0.pt(), p1.pt(), p0.eta(), p1.eta(), p0.phi(), p1.phi(), 1 << hypothesis, scores[0], scores[1], scores[2]); + hasD0 = true; + registry.fill(HIST("candidates"), hypothesis); + } + } + } + + template + void fillDstar(CollType const& col, CandsType const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (const auto& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + auto soft = cand.template prongPi_as(); + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlDstar.mlApplicationMode != NoMl) { + scores = readScores(cand.mlProbDstarToD0Pi()); + } + if (mlDstar.mlApplicationMode == FillMlFromNewBDT) { + if (!std::isfinite(cand.pt()) || cand.pt() < mlDstar.binsPtMl.value.front() || cand.pt() >= mlDstar.binsPtMl.value.back()) { + continue; + } + // Match the unswapped daughter convention used by D+track. + auto features = hfMlResponseDstar.getInputFeatures(cand, false); + std::vector output; + if (!hfMlResponseDstar.isSelectedMl(features, cand.pt(), output)) { + continue; + } + scores = readScores(output); + } + } + dstarRows(collisions.lastIndex(), timestamp, soft.sign(), p0.globalIndex(), p1.globalIndex(), soft.globalIndex(), p0.pt(), p1.pt(), soft.pt(), p0.eta(), p1.eta(), soft.eta(), p0.phi(), p1.phi(), soft.phi(), 1, scores[0], scores[1], scores[2]); + hasDstar = true; + registry.fill(HIST("candidates"), soft.sign() > 0 ? 2 : 3); + } + } + + void processD0D0(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); + + void processD0Dstar(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); + + void processD0D0Ml(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); + + void processD0DstarMl(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); + + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); + + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); + + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); + + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 4fa2b6cd959..69e279424c9 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -70,3 +70,8 @@ o2physics_add_dpl_workflow(task-flow COMPONENT_NAME Analysis) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PWGCF/Core) + +o2physics_add_dpl_workflow(task-charm-hadrons-charm-femto-dream + SOURCES taskCharmHadronsCharmFemtoDream.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..264b4ac2d25 --- /dev/null +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,457 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskCharmHadronsCharmFemtoDream.cxx +/// \brief Build DD pair femtoscopy distributions +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGCF/FemtoDream/Core/femtoDreamMath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::analysis::femtoDream; + +struct HfTaskCharmHadronsCharmFemtoDream { + // Like/unlike sign refers to charm flavour; D0 is electrically neutral. + // Keep charge-conjugate channels separate so they can also be combined + // offline. + enum PairChannel : int { + D0D0LikeSign = 0, + D0barD0barLikeSign = 1, + D0D0barUnlikeSign = 2, + D0DstarPlusLikeSign = 3, + D0barDstarMinusLikeSign = 4, + D0DstarMinusUnlikeSign = 5, + D0barDstarPlusUnlikeSign = 6, + NPairChannels = 7 + }; + static constexpr int MixingBinPolicyMax = 2; + + Produces rowFemtoResultCharm2Prong; + Produces rowFemtoResultCharmDstar; + Produces rowFemtoResultColl; + + Configurable ptD0Min{"ptD0Min", 0.f, "Minimum D0 pT"}; + Configurable ptD0Max{"ptD0Max", 36.f, "Maximum D0 pT"}; + Configurable ptDstarMin{"ptDstarMin", 0.f, "Minimum Dstar pT"}; + Configurable ptDstarMax{"ptDstarMax", 36.f, "Maximum Dstar pT"}; + Configurable etaCandMax{"etaCandMax", 0.8f, "Maximum absolute candidate eta"}; + Configurable massD0Min{"massD0Min", 1.7f, "Minimum D0 mass (keep sidebands)"}; + Configurable massD0Max{"massD0Max", 2.0f, "Maximum D0 mass"}; + Configurable deltaMassDstarMin{"deltaMassDstarMin", MassPiPlus, "Minimum Dstar-D0 mass difference"}; + Configurable deltaMassDstarMax{"deltaMassDstarMax", 0.17f, "Maximum Dstar-D0 mass difference"}; + Configurable massD0DaughterMin{"massD0DaughterMin", 1.80f, "Minimum Dstar daughter D0 mass"}; + Configurable massD0DaughterMax{"massD0DaughterMax", 1.93f, "Maximum Dstar daughter D0 mass"}; + Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; + Configurable bkgBdtD0Max{"bkgBdtD0Max", 1.f, "Maximum D0 background score"}; + Configurable promptBdtD0Min{"promptBdtD0Min", 0.f, "Minimum D0 prompt score"}; + Configurable bkgBdtDstarMax{"bkgBdtDstarMax", 1.f, "Maximum Dstar background score"}; + Configurable promptBdtDstarMin{"promptBdtDstarMin", 0.f, "Minimum Dstar prompt score"}; + Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + struct : ConfigurableGroup { + std::string prefix = "eventSel"; + Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; + Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; + Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; + Configurable multPercentileMin{"multPercentileMin", 0.f, "Minimum multiplicity percentile"}; + Configurable multPercentileMax{"multPercentileMax", 100.f, "Maximum multiplicity percentile"}; + } eventSel; + + struct : ConfigurableGroup { + Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; + Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; + } mixSetting; + SliceCache cache; + using FilteredCollisions = soa::Filtered>; + using FilteredCharmCand2Prongs = soa::Filtered; + using FilteredCharmCandDstars = soa::Filtered; + + Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= eventSel.multMin && aod::femtodreamcollision::multNtr <= eventSel.multMax; + Filter eventMultiplicityPercentile = ifnode(eventSel.useCentrality, aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, Node{LiteralNode{true}}); + Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; + + // Full-table process inputs do not automatically register grouping caches. + // Register both keys explicitly for the sliceByCached calls below. + Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; + + Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= bkgBdtD0Max && aod::fdhf::bdtPrompt >= promptBdtD0Min, Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= bkgBdtDstarMax && aod::fdhf::bdtPrompt >= promptBdtDstarMin, Node{LiteralNode{true}}); + + ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; + ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; + ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; + HistogramRegistry registry{"registry"}; + + void init(InitContext const&) + { + if (doprocessD0D0 == doprocessD0Dstar) { + LOGP(fatal, "Enable exactly one charm-charm analysis process"); + } + if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptD0Min < 0 || ptD0Min >= ptD0Max || ptDstarMin < 0 || ptDstarMin >= ptDstarMax || etaCandMax <= 0 || massD0Min >= massD0Max || deltaMassDstarMin >= deltaMassDstarMax || massD0DaughterMin >= massD0DaughterMax || charmHadCandSel < 1 || eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { + LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); + } + colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; + const AxisSpec massD0{300, massD0Min.value, massD0Max.value, "M(Kpi) (GeV/c2)"}; + const AxisSpec deltaMass{310, deltaMassDstarMin.value, deltaMassDstarMax.value, "Delta M (GeV/c2)"}; + const AxisSpec pt{72, ptD0Min.value, ptD0Max.value, "D0 pT (GeV/c)"}; + const AxisSpec ptStar{72, ptDstarMin.value, ptDstarMax.value, "Dstar pT (GeV/c)"}; + const AxisSpec mult{mixingBinMult, "NTracksPV"}; + const AxisSpec channel{static_cast(NPairChannels), -0.5, static_cast(NPairChannels) - 0.5, "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; + registry.add("SE/D0D0", "Same event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("ME/D0D0", "Mixed event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("SE/D0Dstar", "Same event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("ME/D0Dstar", "Mixed event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, {{2, -0.5, 1.5}}); + registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", kTH1F, {{2, -0.5, 1.5}}); + registry.add("QA/d0MassPt", ";M(Kpi);pT", kTH2F, {massD0, pt}); + registry.add("QA/dstarMassPt", ";Delta M;pT", kTH2F, {deltaMass, ptStar}); + } + + template + static bool sharesDaughter(D0Row const& a, OtherRow const& b) + { + for (const auto& id : std::array{a.prong0Id(), a.prong1Id()}) { + if (id < 0) { + continue; + } + if (id == b.prong0Id() || id == b.prong1Id()) { + return true; + } + if constexpr (IsDstar) { + if (id == b.prong2Id()) { + return true; + } + } + } + return false; + } + + // D0 charge denotes flavour, not electric charge. + template + static PairChannel pairChannel(D0Row const& d0, OtherRow const& other) + { + if constexpr (!IsDstar) { + return d0.charge() != other.charge() ? D0D0barUnlikeSign : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + } + return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign : D0DstarMinusUnlikeSign) : (other.charge() < 0 ? D0barDstarMinusLikeSign : D0barDstarPlusUnlikeSign); + } + + // Canonical ordering keeps mass/pt axes independent of event ordering for + // D0D0. + template + static bool reverseD0Order(FirstRow const& first, SecondRow const& second) + { + if (first.charge() != second.charge()) { + return first.charge() < second.charge(); // D0 first for D0-D0bar + } + return first.pt() < second.pt(); // leading pT first for equal flavours + } + + template + void validateMlScores(CandsType const& candidates) + { + if (!useMl) { + return; + } + for (const auto& row : candidates) { + if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); + } + } + } + + template + bool select(Row const& row, float& mass) + { + if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaCandMax || row.pt() < (IsDstar ? ptDstarMin.value : ptD0Min.value) || row.pt() >= (IsDstar ? ptDstarMax.value : ptD0Max.value)) { + return false; + } + const std::array masses = row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + if constexpr (IsDstar) { + const float daughterMass = row.mDaughD0(masses); + if (!std::isfinite(daughterMass) || daughterMass < massD0DaughterMin || daughterMass >= massD0DaughterMax) { + return false; + } + mass = row.m(std::array{masses[0], masses[1], MassPiPlus}) - daughterMass; + } else { + mass = row.m(masses); + } + if (!std::isfinite(mass) || mass < (IsDstar ? deltaMassDstarMin.value : massD0Min.value) || mass >= (IsDstar ? deltaMassDstarMax.value : massD0Max.value)) { + return false; + } + if constexpr (IsDstar) { + if constexpr (FillQa) { + registry.fill(HIST("QA/dstarMassPt"), mass, row.pt()); + } + } else { + if constexpr (FillQa) { + registry.fill(HIST("QA/d0MassPt"), mass, row.pt()); + } + } + return true; + } + + template + void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, float secondMass, float mult) + { + if constexpr (!Mixed) { + if (sharesDaughter(first, second)) { + registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); + return; + } + } + const bool reverse = !IsDstar && reverseD0Order(first, second); + const float mass1 = reverse ? secondMass : firstMass; + const float mass2 = reverse ? firstMass : secondMass; + const float pt1 = reverse ? second.pt() : first.pt(); + const float pt2 = reverse ? first.pt() : second.pt(); + // Read kinematics directly from the candidate rows. Only D0D0 can reverse; + // its two nominal parent masses are equal. Never use Dstar delta mass here. + const float kstar = reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) : FemtoDreamMath::getkstar(first, MassD0, second, IsDstar ? MassDStar : MassD0); + if (!std::isfinite(kstar)) { + return; + } + const int channel = pairChannel(first, second); + if constexpr (IsDstar && Mixed) { + registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (IsDstar) { + registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (Mixed) { + registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else { + registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } + } + + template + int mixingBin(CollType const& col) + { + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + return colBinningMult.getBin({col.posZ(), col.multNtr()}); + case aod::femtodreamcollision::kMultPercentile: + return colBinningMultPercentile.getBin({col.posZ(), col.multV0M()}); + case aod::femtodreamcollision::kMultMultPercentile: + return colBinningMultMultPercentile.getBin({col.posZ(), col.multNtr(), col.multV0M()}); + default: + return -1; + } + } + + template + void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, OtherTable const& otherRows, Policy const& policy) + { + Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. + Partition partitionMaskedCol2 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + Partition partitionMaskedColDstar = (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + partitionMaskedCol1.bindTable(cols); + if constexpr (IsDstar) { + partitionMaskedColDstar.bindTable(cols); + } else { + partitionMaskedCol2.bindTable(cols); + } + auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; + for (const auto& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy(policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, secondCollisions))) { + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + if constexpr (!IsDstar) { + // Full policy returns both orientations for identical inputs. Count + // once. + if (collision1.globalIndex() > collision2.globalIndex()) { + continue; + } + } + if (!std::isfinite(collision1.magField()) || collision1.magField() != collision2.magField()) { + continue; + } + if constexpr (IsDstar) { + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(collision1)); + } else { + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(collision1)); + } + auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); + auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); + // Species roles remain fixed; the full policy supplies the reverse event + // orientation when eligible. Do not add a second manual reverse loop. + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, collision1.multNtr()); + } + } + } + } + + // Write candidates once per collision, independently of SE/ME pair counts. + // Reuse the same output tables and field conventions as the track/V0 tasks. + template + void fillTables(CollType const& col, D0Slice const& d0s, DstarSlice const& dstars) + { + int64_t timeStamp = -1; + bool hasCandidate = false; + auto recordTimeStamp = [&](int64_t value) { + if (hasCandidate && value != timeStamp) { + LOGP(fatal, "Inconsistent charm timestamps in one reduced collision"); + } + timeStamp = value; + hasCandidate = true; + }; + for (const auto& part : d0s) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + rowFemtoResultCharm2Prong(col.globalIndex(), timeStamp, mass, part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + if constexpr (WithDstar) { + for (const auto& part : dstars) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. + rowFemtoResultCharmDstar(col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + } + // Retain D0-only/Dstar-only events even when no clean SE pair exists. + // MC fields above are zero for these data processes, as in existing tasks. + if (hasCandidate) { + rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), col.multNtr()); + } + } + + template + void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, DstarTable const& dstarRows, Policy const& policy) + { + for (const auto& col : cols) { + auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + // Export before online mixing-bin cuts, so offline mixing can change bins. + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + fillTables(col, d0s, dstars); + } else { + fillTables(col, d0s, d0s); + } + const int bin = mixingBin(col); + if (bin < 0 || !std::isfinite(col.magField())) { + registry.fill(HIST("QA/events"), 1); + continue; + } + registry.fill(HIST("QA/events"), 0); + registry.fill(HIST("MixingQA/hSECollisionBins"), bin); + for (const auto& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + } + } + if (mixSetting.doMixEvent && mixSetting.mixingDepth > 0) { + doMixedEvent(cols, d0Rows, d0Rows, policy); + if constexpr (WithDstar) { + doMixedEvent(cols, d0Rows, dstarRows, policy); + } + } + } + + template + void run(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0Rows, DstarTable const& dstarRows) + { + validateMlScores(d0Rows); + partitionCharmHadron2Prong.bindTable(d0Rows); + if constexpr (WithDstar) { + validateMlScores(dstarRows); + partitionCharmHadronDstar.bindTable(dstarRows); + } + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); + } + } + + void processD0D0(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s) + { + run(cols, d0s, d0s); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); + + void processD0Dstar(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s, + FilteredCharmCandDstars const& dstars) + { + run(cols, d0s, dstars); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From c9cae50c8f4458d9259c5bb554e05672a42250e5 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:34:08 +0200 Subject: [PATCH 049/648] [PWGLF] Temporarily remove grouping of tracked cascades (#18022) Co-authored-by: ALICE Action Bot --- .../Strangeness/strangederivedbuilder.cxx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx index 8007e538f89..57ebfa938a0 100644 --- a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx @@ -249,7 +249,7 @@ struct strangederivedbuilder { Preslice V0perCollision = o2::aod::v0data::collisionId; Preslice CascperCollision = o2::aod::cascdata::collisionId; Preslice KFCascperCollision = o2::aod::cascdata::collisionId; - Preslice TraCascperCollision = o2::aod::cascdata::collisionId; + // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; @@ -576,11 +576,12 @@ struct strangederivedbuilder { auto V0Table_thisColl = V0s.sliceBy(V0perCollision, collIdx); auto CascTable_thisColl = Cascades.sliceBy(CascperCollision, collIdx); auto KFCascTable_thisColl = KFCascades.sliceBy(KFCascperCollision, collIdx); - auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); + // auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); bool strange = V0Table_thisColl.size() > 0 || CascTable_thisColl.size() > 0 || - KFCascTable_thisColl.size() > 0 || - TraCascTable_thisColl.size() > 0; + KFCascTable_thisColl.size() > 0 /* || + TraCascTable_thisColl.size() > 0*/ + ; auto bc = collision.template bc_as(); @@ -761,8 +762,8 @@ struct strangederivedbuilder { CascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); for (const auto& casc : KFCascTable_thisColl) KFCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); - for (const auto& casc : TraCascTable_thisColl) - TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); + // for (const auto& casc : TraCascTable_thisColl) + // TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); } // +-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+ From 1c9f91cb8daba0459b40c5d3c8cedea3e62fbb6f Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 23 Sep 2026 20:03:02 +0200 Subject: [PATCH 050/648] [PWGCF] Fixes in femto framework (#18026) --- PWGCF/Femto/Core/cascadeBuilder.h | 2 +- PWGCF/Femto/Core/cascadeHistManager.h | 2 +- PWGCF/Femto/Core/collisionBuilder.h | 3 + PWGCF/Femto/Core/mcBuilder.h | 24 +++---- PWGCF/Femto/Core/pairBuilder.h | 22 ++---- PWGCF/Femto/Core/pairProcessHelpers.h | 6 +- PWGCF/Femto/Core/tripletBuilder.h | 12 ++-- PWGCF/Femto/Core/tripletProcessHelpers.h | 47 ++++++++----- .../Femto/Core/twoTrackResonanceHistManager.h | 4 +- PWGCF/Femto/Core/v0HistManager.h | 2 +- PWGCF/Femto/DataModel/FemtoTables.h | 4 +- PWGCF/Femto/TableProducer/femtoProducer.cxx | 67 ++++++++++--------- PWGCF/Femto/Tasks/femtoDndetaPair.cxx | 12 +++- PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx | 12 +++- PWGCF/Femto/Tasks/femtoKinkQa.cxx | 2 +- PWGCF/Femto/Tasks/femtoMcParticleQa.cxx | 4 +- .../Tasks/femtoPairMcParticleMcParticle.cxx | 2 +- 17 files changed, 127 insertions(+), 100 deletions(-) diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 9b86483b61c..1248e0bfc8d 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -669,7 +669,7 @@ class CascadeBuilder } template - void fillCascade(T1& collisionBuilder, T2& cascadeProducts, T3 const& cascade, T4 const& col, int bachelorIndex, int posDaughterIndex, int negDaughterIndex) + void fillCascade(T1& collisionBuilder, T2& cascadeProducts, T3 const& cascade, T4 const& col, int64_t bachelorIndex, int64_t posDaughterIndex, int64_t negDaughterIndex) { float strangeTofBachelor = 0.f; float strangeTofPosDau = 0.f; diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index cbb8143feb5..5d60057b60a 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -475,7 +475,7 @@ class CascadeHistManager this->fillAnalysis(cascadeCandidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { - this->fillQa(cascadeCandidate, bachelor, negDaughter, posDaughter); + this->fillQa(cascadeCandidate, bachelor, posDaughter, negDaughter); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(cascadeCandidate, col, mcParticles, mcMothers, mcPartonicMothers); diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 7a425a0c2a2..1c4737bdb09 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -669,6 +669,9 @@ class CollisionBuilder template void initCollision(T1 const& bc, T2 const& col, T3 const& tracks, T4& ccdb, T5& histRegistry) { + if (!mFillAnyTable) { + return; // selection object was never configured (no collision table requested), applying selections would use an unset registry + } if (mRunNumber != bc.runNumber()) { mRunNumber = bc.runNumber(); if (mMagFieldForced == 0) { diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 7935ebbb2ad..4b9526d4ae9 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -149,18 +149,18 @@ class McBuilder mProduceMcParticles = utils::enableTable("FMcParticles_001", table.produceMcParticles.value, initContext); mProduceMcMothers = utils::enableTable("FMcMothers_001", table.produceMcMothers.value, initContext); mProduceMcPartonicMothers = utils::enableTable("FMcPartMoths_001", table.produceMcPartonicMothers.value, initContext); - mProduceMcMotherLabels = utils::enableTable("FMcMotherLabels", table.producedMcMotherLabels.value, initContext); - - mProduceCollisionLabels = utils::enableTable("FColLabels", table.producedCollisionLabels.value, initContext); - mProduceTrackLabels = utils::enableTable("FTrackLabels", table.producedTrackLabels.value, initContext); - mProduceLambdaLabels = utils::enableTable("FLambdaLabels", table.producedLambdaLabels.value, initContext); - mProduceK0shortLabels = utils::enableTable("FK0shortLabels", table.producedK0shortLabels.value, initContext); - mProduceD0Labels = utils::enableTable("FD0Labels", table.producedD0Labels.value, initContext); - mProduceLcLabels = utils::enableTable("FLcLabels", table.producedLcLabels.value, initContext); - mProduceSigmaLabels = utils::enableTable("FSigmaLabels", table.producedSigmaLabels.value, initContext); - mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels", table.producedSigmaPlusLabels.value, initContext); - mProduceXiLabels = utils::enableTable("FXiLabels", table.producedXiLabels.value, initContext); - mProduceOmegaLabels = utils::enableTable("FOmegaLabels", table.producedOmegaLabels.value, initContext); + mProduceMcMotherLabels = utils::enableTable("FMcMotherLabels_001", table.producedMcMotherLabels.value, initContext); + + mProduceCollisionLabels = utils::enableTable("FColLabels_001", table.producedCollisionLabels.value, initContext); + mProduceTrackLabels = utils::enableTable("FTrackLabels_001", table.producedTrackLabels.value, initContext); + mProduceLambdaLabels = utils::enableTable("FLambdaLabels_001", table.producedLambdaLabels.value, initContext); + mProduceK0shortLabels = utils::enableTable("FK0shortLabels_001", table.producedK0shortLabels.value, initContext); + mProduceD0Labels = utils::enableTable("FD0Labels_001", table.producedD0Labels.value, initContext); + mProduceLcLabels = utils::enableTable("FLcLabels_001", table.producedLcLabels.value, initContext); + mProduceSigmaLabels = utils::enableTable("FSigmaLabels_001", table.producedSigmaLabels.value, initContext); + mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels_001", table.producedSigmaPlusLabels.value, initContext); + mProduceXiLabels = utils::enableTable("FXiLabels_001", table.producedXiLabels.value, initContext); + mProduceOmegaLabels = utils::enableTable("FOmegaLabels_001", table.producedOmegaLabels.value, initContext); if (mProduceMcCollisions || mProduceCollisionLabels || mProduceMcParticles || mProduceMcMotherLabels || diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index e3e485eabb8..0b408b64487 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -398,14 +398,6 @@ class PairV0V0Builder << "). Overriding sameSpecies to false."; mSameSpecies = false; } - } else { - // same v0 species: both values are legitimate (e.g. lambda-lambda vs lambda-antilambda), - // but running as different species only makes sense if the two selections are disjoint - if (!mSameSpecies && confV0Selection1.sign.value == confV0Selection2.sign.value) { - LOG(warn) << "sameSpecies=false for identical v0 species with identical sign (" - << confV0Selection1.sign.value << "). If both selections match the same " - << "candidates, this produces self-pairs and double counting."; - } } mColHistManager.template init(registry, colHistSpec, confCollisionBinning); @@ -1910,17 +1902,17 @@ class PairTrackCascadeBuilder } template - void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& v0Partition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& cascadePartition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2067,7 +2059,7 @@ class PairMcParticleMcParticleBuilder bool processSameEvent(T1 const& col, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& cache) { if (mSameSpecies) { - auto mcParticleSlice = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); if (mcParticleSlice.size() < nLimitPartitionIdenticalParticles) { return false; @@ -2080,8 +2072,8 @@ class PairMcParticleMcParticleBuilder return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); } - auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); - auto mcParticleSlice2 = partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice2 = partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles) { return false; } diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 70c681c78a1..54323079df3 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -667,7 +667,7 @@ void processMixedEvent(T1 const& Collisions, int windowSizeRaw = 0; int windowSizeEffective = 0; - std::optionalsliceByCached(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { @@ -678,12 +678,12 @@ void processMixedEvent(T1 const& Collisions, windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); } ++windowSizeRaw; - auto sliceParticle2 = Partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache); + auto sliceParticle2 = Partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache); PairHistManager.resetTrackedParticlesPerEvent(); diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index 1f4c704a67b..ec68bd83de1 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -1076,7 +1076,7 @@ class TripletMcParticleMcParticleMcParticleBuilder tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mParticle1Particle2Particle3AreSameSpecies) { - auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); if (mcParticleSlice1.size() < nLimitPartitionIdenticalParticles123) { return false; } @@ -1088,8 +1088,8 @@ class TripletMcParticleMcParticleMcParticleBuilder } if (mParticle1Particle2AreSameSpecies) { - auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); - auto mcParticleSlice3 = partition3->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice3 = partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); if (mcParticleSlice1.size() < nLimitPartitionIdenticalParticles12 || mcParticleSlice3.size() < nLimitPartitionParticles) { return false; } @@ -1100,9 +1100,9 @@ class TripletMcParticleMcParticleMcParticleBuilder return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mMcParticleHistManager3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); } - auto mcParticleSlice1 = partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); - auto mcParticleSlice2 = partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); - auto mcParticleSlice3 = partition3->sliceByCached(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice2 = partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice3 = partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles || mcParticleSlice3.size() < nLimitPartitionParticles) { return false; } diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index 729ef2a9cc1..a5d7b812305 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -35,7 +35,9 @@ enum TripletOrder : uint8_t { kOrder123, // no swap kOrder213, // swap 1&2: for the case that particle 1 & 2 are the same species, particle 3 is something else kOrder132, // swap 2&3 - kOrder321, // reverse: swap 1&3 + kOrder231, // swap 1&2 & 1&3 + kOrder312, // swap 2&3 & 1&3 + kOrder321, // swap 1&3 }; // process same event for identical 3 particles @@ -76,6 +78,12 @@ bool processSameEvent(T1 const& SliceParticle, case kOrder132: TripletHistManager.setTriplet(p1, p3, p2, Collision); break; + case kOrder231: + TripletHistManager.setTriplet(p2, p3, p1, Collision); + break; + case kOrder312: + TripletHistManager.setTriplet(p3, p1, p2, Collision); + break; case kOrder321: TripletHistManager.setTriplet(p3, p2, p1, Collision); break; @@ -291,6 +299,12 @@ bool processSameEvent(T1 const& SliceParticle, case kOrder132: TripletHistManager.setTripletMc(p1, p3, p2, mcParticles, Collision, mcCollisions); break; + case kOrder231: + TripletHistManager.setTripletMc(p2, p3, p1, mcParticles, Collision, mcCollisions); + break; + case kOrder312: + TripletHistManager.setTripletMc(p3, p1, p2, mcParticles, Collision, mcCollisions); + break; case kOrder321: TripletHistManager.setTripletMc(p3, p2, p1, mcParticles, Collision, mcCollisions); break; @@ -403,7 +417,6 @@ bool processSameEvent(T1 const& SliceParticle1, } // process same event for 3 different particles with mc information -// NOTE: added `Cleaner1`, `Cleaner2`, `Cleaner3` (one per species) template sliceByCached(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; - std::optionalsliceByCached(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle2; + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle2; for (auto const& [collision1, collision2, collision3] : o2::soa::selfCombinations(policy, depth, -1, McCollisions, McCollisions, McCollisions)) { @@ -1019,18 +1030,18 @@ void processMixedEventMcTruth(T1 const& McCollisions, windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); } // inner sub-window if (collision2.globalIndex() != lastCollisionIndex2) { lastCollisionIndex2 = collision2.globalIndex(); - sliceParticle2.emplace(Partition2->sliceByCached(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache)); + sliceParticle2.emplace(Partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache)); } ++windowSizeRaw; - auto sliceParticle3 = Partition3->sliceByCached(o2::aod::femtomcparticle::fMcColId, collision3.globalIndex(), cache); + auto sliceParticle3 = Partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision3.globalIndex(), cache); TripletHistManager.resetTrackedParticlesPerEvent(); diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h index a683dbf1d4c..1f9149ddc26 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -216,8 +216,8 @@ class TwoTrackResonanceHistManager negDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); } else { mPdgCode = -1 * mPdgCode; - posDauPdgCodeAbs = std::abs(PDG_t::kKMinus); - negDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + posDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + negDauPdgCodeAbs = std::abs(PDG_t::kKMinus); } } else { LOG(fatal) << "PDG code for TwoTrackResonance has to be either Rho, Phi or K^0*(892)"; diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index ad9b56ed6ba..4f55c529358 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -618,7 +618,7 @@ class V0HistManager massLambda = v0candidate.massLambda(); massAntiLambda = v0candidate.massAntiLambda(); tofPosDau = posDau.tofNSigmaPi(); - tofNegDau = posDau.tofNSigmaPi(); + tofNegDau = negDau.tofNSigmaPi(); } mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassLambda, HistTable)), massLambda); diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index dd4af0506bb..958fb9cb99d 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -216,11 +216,11 @@ DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, //! transverse momentum }); DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! momentum in x [](float pt, float phi) -> float { - return std::fabs(pt) * std::sin(phi); + return std::fabs(pt) * std::cos(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! momentum in y [](float pt, float phi) -> float { - return std::fabs(pt) * std::cos(phi); + return std::fabs(pt) * std::sin(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! momentum in z [](float pt, float eta) -> float { diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 16bd0b3726e..0053724e5a9 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -372,15 +372,14 @@ struct FemtoProducer { return true; } - template - bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks, T5 const& mcParticles) + template + bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks) { collisionBuilder.reset(); - // in pass-through the maps are built once per timeframe - // resetting here would wipe them and duplicate every mc collision and particle - if (!mcBuilder.isPassThrough()) { - mcBuilder.reset(mcCols, mcParticles); - } + // mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function + // (both pass-through and lazy-fill mode); resetting here on every reco collision would + // wipe the dedup maps and duplicate every mc collision/particle referenced by more than + // one reco collision (e.g. split-vertex reconstruction under pileup) auto bc = col.template bc_as(); collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); if (!collisionBuilder.checkCollision(col, mcCols)) { @@ -678,8 +677,10 @@ struct FemtoProducer { // does no auto-grouping. Tables that are iterated over (tracks, v0s, cascades, // kinks) are sliced per collision; the full track table is passed alongside so // the v0/cascade/kink builders can resolve their daughter indices. - // These functions own the per-timeframe reset of the mc builder maps, which is - // why processMcCollisions skips its own reset in pass-through mode. + // These functions own the per-timeframe reset of the mc builder maps (once, before the + // reco-collision loop, in both pass-through and lazy-fill mode); processMcCollisions never + // resets them itself, since it runs once per reco collision and the dedup maps have to + // survive across all reco collisions in the timeframe. // ========================================================================== void processTracksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, @@ -688,14 +689,14 @@ struct FemtoProducer { rawinputs::Run3McRecoTracks const& tracks, rawinputs::Run3McGenParticles const& mcParticles) { + mcBuilder.reset(mcCols, mcParticles); if (mcBuilder.isPassThrough()) { - mcBuilder.reset(mcCols, mcParticles); mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach(mcCols, mcParticles, perMcCollision, mcProducts); } for (const auto& col : cols) { auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); - if (!processMcCollisions(col, mcCols, bcs, tracksThisCol, mcParticles)) { + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } auto tracksWithItsPid = o2::soa::Attach perMcColParticles = o2::aod::femtomcparticle::fMcColId; + // pass-through mode does not guarantee fMcColId is written in sorted order (reco-driven label + // rows can be created out of mc-collision order), so this must be an unsorted grouping + o2::framework::PresliceUnsorted perMcColParticles = o2::aod::femtomcparticle::fMcColId; // setup collisions (applied in the dndeta builder, no filter, so the cutflow sees every collision) collisionbuilder::ConfCollisionSelection collisionSelection; @@ -101,6 +103,12 @@ struct FemtoDndetaPair { o2::framework::Partition dndetaTrackWithLabelPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); o2::framework::Partition dndetaTrackWithLabelPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + // the *Mc process functions take the ungrouped tables (no ::iterator first argument), so DPL never + // auto-registers the fColId slicing cache for FemtoTracksWithLabel; this unused Preslice is the only + // thing that requests/enables it, otherwise sliceByCached() throws "Disabled cache ... is requested" + // once the first accepted reco collision actually reaches it + o2::framework::Preslice perColTracksWithLabel = o2::aod::femtobase::stored::fColId; + // setup tracks (trigger) trackbuilder::ConfTrackSelection1 confTrackSelection1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -127,6 +135,8 @@ struct FemtoDndetaPair { o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + // same reason as perColTracksWithLabel above, needed by processTrackV0Mc + o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; // setup strangeness yields (dedicated selections, independent of the trigger) v0builder::ConfLambdaSelectionStrangeness confStrangeLambdaSelection; diff --git a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx index 8bdf06a3459..4be3c0f0751 100644 --- a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx +++ b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx @@ -82,7 +82,9 @@ struct FemtoDndetaTriplet { dndetahistmanager::ConfDndetaBinning confDndetaBinning; dndetabuilder::DndetaBuilder dndetaBuilder; - o2::framework::Preslice perMcColParticles = o2::aod::femtomcparticle::fMcColId; + // pass-through mode does not guarantee fMcColId is written in sorted order (reco-driven label + // rows can be created out of mc-collision order), so this must be an unsorted grouping + o2::framework::PresliceUnsorted perMcColParticles = o2::aod::femtomcparticle::fMcColId; // setup collisions (applied in the dndeta builder, no filter, so the cutflow sees every collision) collisionbuilder::ConfCollisionSelection collisionSelection; @@ -103,6 +105,12 @@ struct FemtoDndetaTriplet { o2::framework::Partition dndetaTrackWithLabelPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); o2::framework::Partition dndetaTrackWithLabelPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + // the *Mc process functions take the ungrouped tables (no ::iterator first argument), so DPL never + // auto-registers the fColId slicing cache for FemtoTracksWithLabel; this unused Preslice is the only + // thing that requests/enables it, otherwise sliceByCached() throws "Disabled cache ... is requested" + // once the first accepted reco collision actually reaches it + o2::framework::Preslice perColTracksWithLabel = o2::aod::femtobase::stored::fColId; + // setup tracks (trigger) trackbuilder::ConfTrackSelection1 confTrackSelection1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -135,6 +143,8 @@ struct FemtoDndetaTriplet { o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + // same reason as perColTracksWithLabel above, needed by processTrackTrackV0Mc + o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; // setup strangeness yields (dedicated selections, independent of the trigger) v0builder::ConfLambdaSelectionStrangeness confStrangeLambdaSelection; diff --git a/PWGCF/Femto/Tasks/femtoKinkQa.cxx b/PWGCF/Femto/Tasks/femtoKinkQa.cxx index f1b456def84..e89ed4926dc 100644 --- a/PWGCF/Femto/Tasks/femtoKinkQa.cxx +++ b/PWGCF/Femto/Tasks/femtoKinkQa.cxx @@ -101,7 +101,7 @@ struct FemtoKinkQa { o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; - o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaSelection); + o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlussWithLabel = o2::aod::femtobase::stored::fColId; kinkhistmanager::ConfSigmaPlusBinning1 confSigmaPlusBinning; diff --git a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx index 22e96ddaedb..f7f0706e16a 100644 --- a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx +++ b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx @@ -59,7 +59,7 @@ struct FemtoMcParticleQa { mcparticlehistmanager::McParticleHistManager mcParticleHistManager1; o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); - o2::framework::Preslice perColReco = o2::aod::femtomcparticle::fMcColId; + o2::framework::PresliceUnsorted perColReco = o2::aod::femtomcparticle::fMcColId; particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; particlecleaner::ParticleCleaner mcParticleCleaner; @@ -83,7 +83,7 @@ struct FemtoMcParticleQa { void process(FilteredFemtoMcCollision const& col, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - auto mcParticleSlice = mcParticlesPartition1->sliceByCached(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); if (mcParticleSlice.size() == 0) { return; } diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index d7a36b69bf3..7be627972f8 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -76,7 +76,7 @@ struct FemtoPairMcParticleMcParticle { o2::framework::Partition mcParticlesPartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); - o2::framework::Preslice perColParticles = o2::aod::femtomcparticle::fMcColId; + o2::framework::PresliceUnsorted perColParticles = o2::aod::femtomcparticle::fMcColId; // setup pairs pairhistmanager::ConfPairBinning confPairBinning; From 3fa4b26c0d59863cbbebcc2c5697154c4ba3d809 Mon Sep 17 00:00:00 2001 From: Paola Vargas Torres <88360333+PaolaVT@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:29:02 -0600 Subject: [PATCH 051/648] [PWGLF] Add configurable flags for optional track cuts (#18032) Co-authored-by: PaolaVT --- PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx | 57 ++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx index aa012ec3dd6..b4f80254804 100644 --- a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx @@ -128,6 +128,11 @@ struct DedxPidAnalysis { }; + enum NDCAzSelectionMode : int { + DCAz1 = 1, + DCAzpT = 2, + }; + enum MomentumMode : int { TpcInnerParam = 1, TotalMomentum = 2 @@ -205,7 +210,11 @@ struct DedxPidAnalysis { "max z distance to IP"}; Configurable etaMin{"etaMin", -0.8f, "etaMin"}; Configurable etaMax{"etaMax", +0.8f, "etaMax"}; - // Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; + // Configurables para activar/desactivar cortes + Configurable requireITSRefit{"requireITSRefit", true, "Require ITS refit"}; + Configurable requireTPCRefit{"requireTPCRefit", true, "Require TPC refit"}; + Configurable requireMinNCrossedRowsOverFindableClustersTPC{"requireMinNCrossedRowsOverFindableClustersTPC", true, "Require min N crossed rows / findable clusters TPC"}; + Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; Configurable nSigmaDCAxy{"nSigmaDCAxy", 3.0, "nSigma DCAxy selection"}; Configurable dcaXYp0{"dcaXYp0", 0.0105f, "DCAxy formula: p0 + p1/pt^p2"}; Configurable dcaXYp1{"dcaXYp1", 0.0350f, "DCAxy p1 parameter"}; @@ -214,7 +223,7 @@ struct DedxPidAnalysis { Configurable dcaZp1{"dcaZp1", 0.0350f, "DCAz p1 parameter"}; Configurable dcaZp2{"dcaZp2", 1.1f, "DCAz p2 parameter"}; Configurable nSigmaDCAz{"nSigmaDCAz", 3.0, "nSigma DCAz selection"}; - // Configurable maxDCAz{"maxDCAz", 0.1f, "maxDCAz"}; + Configurable maxDCAz{"maxDCAz", 0.1f, "maxDCAz"}; Configurable pionMin{"pionMin", 0.35f, "pionMin"}; Configurable pionMax{"pionMax", 0.45f, "pionMax"}; Configurable elTofCut{"elTofCut", 0.1f, "elTofCut"}; @@ -252,6 +261,7 @@ struct DedxPidAnalysis { Configurable nPileUp{"nPileUp", true, "Rejects events with pileup in the same bunch crossing"}; Configurable nINELSelectionMode{"nINELSelectionMode", 2, "INEL event selection: 1 no sel, 2 INEL>0, 3 INEL>1"}; Configurable nGoodITS{"nGoodITS", true, "Numbers of inactive chips on all ITS layers are below maximum allowed values"}; + Configurable nDCAzOption{"nDCAzOption", 1, "DCA selection: 1: from -0.1 to 0.1, 2: pT dependent cut"}; Configurable v0SelectionMode{"v0SelectionMode", 3, "V0 Selection base on TPC: 1, TOF:2 ,Both:3"}; Configurable momentumMode{"momentumMode", 2, "1: TPC inner param, 2: Total momentum p"}; Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; @@ -283,10 +293,16 @@ struct DedxPidAnalysis { TrackSelection selectedTracks; selectedTracks.SetPtRange(0.1f, 1e10f); selectedTracks.SetEtaRange(etaMin, etaMax); - // selectedTracks.SetRequireITSRefit(true); - // selectedTracks.SetRequireTPCRefit(true); + if (requireITSRefit.value) { + selectedTracks.SetRequireITSRefit(true); + } + if (requireTPCRefit.value) { + selectedTracks.SetRequireTPCRefit(true); + } selectedTracks.SetMinNCrossedRowsTPC(static_cast(minNCrossedRowsTPC.value)); - // selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC); + if (requireMinNCrossedRowsOverFindableClustersTPC.value) { + selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + } selectedTracks.SetMaxChi2PerClusterTPC(maxChi2TPC); selectedTracks.SetRequireHitsInITSLayers(1, {0, 1, 2}); selectedTracks.SetMaxChi2PerClusterITS(maxChi2ITS); @@ -312,11 +328,33 @@ struct DedxPidAnalysis { } else { LOGF(info, "GoodZvtxFT0vsPV cut disabled"); } + if (requireITSRefit) { + LOGF(info, "Applying ITSRefit cut"); + } else { + LOGF(info, "ITSRefit cut disabled"); + } + + if (requireTPCRefit) { + LOGF(info, "Applying TPCRefit cut"); + } else { + LOGF(info, "TPCRefit cut disabled"); + } + if (requireMinNCrossedRowsOverFindableClustersTPC) { + LOGF(info, "Applying MinNCrossedRowsOverFindableClustersTPC cut"); + } else { + LOGF(info, "MinNCrossedRowsOverFindableClustersTPC cut disabled"); + } + if (nGoodITS) { LOGF(info, "Applying GoodITSLayersAll cut"); } else { LOGF(info, "GoodITSLayersAll cut disabled"); } + if (nDCAzOption == DCAz1) { + LOGF(info, "Applying -0.1 to 0.1 cut"); + } else if (nDCAzOption == DCAzpT) { + LOGF(info, "Applying pT-dependet cut"); + } if (nINELSelectionMode == NoSelINEL) { LOGF(info, "Applying just INEL"); label = "INEL"; @@ -856,8 +894,13 @@ struct DedxPidAnalysis { template bool passesDCAzCut(const T1& track) const { - const float maxiDcaZ = nSigmaDCAz.value * (dcaZp0.value + dcaZp1.value / std::pow(track.pt(), dcaZp2.value)) / 3.0; - return std::abs(track.dcaZ()) < maxiDcaZ; + if (nDCAzOption.value == DCAz1) { + return std::abs(track.dcaZ()) < maxDCAz.value; + } else if (nDCAzOption.value == DCAzpT) { + const float maxiDcaZ = nSigmaDCAz.value * (dcaZp0.value + dcaZp1.value / std::pow(track.pt(), dcaZp2.value)) / 3.0; + return std::abs(track.dcaZ()) < maxiDcaZ; + } + return true; } // Momentum template From b8c6555721f9db603bb09aad58ffe988597d4c3a Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:32:36 +0200 Subject: [PATCH 052/648] [PWGLF] Completed analysis task with (anti)Lambda and Omega cases + Introduced per-run efficiency fetching (#18033) --- .../DataModel/LFPhiStrangeCorrelationTables.h | 161 ++-- .../Strangeness/phiStrangeCorrelator.cxx | 441 ++++++--- .../Strangeness/phiStrangeCorrelation.cxx | 901 ++++++------------ 3 files changed, 700 insertions(+), 803 deletions(-) diff --git a/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h b/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h index d4fddd28771..44f552a0f6d 100644 --- a/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h +++ b/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h @@ -29,12 +29,6 @@ DECLARE_SOA_COLUMN(DefaultSel, defaultSel, bool); DECLARE_SOA_COLUMN(PhimesonSel, phimesonSel, bool); } // namespace lf_selection_event -/*DECLARE_SOA_TABLE(PhiStrangeDefEvtSelDataLike, "AOD", "DEFEVTSELDATA", - lf_selection_event::DefaultSel); - -DECLARE_SOA_TABLE(PhiStrangeDefEvtSelMcGen, "AOD", "DEFEVTSELMCGEN", - lf_selection_event::DefaultSel);*/ - DECLARE_SOA_TABLE(PhiStrangeEvtSelDataLike, "AOD", "EVTSELDATA", lf_selection_event::DefaultSel, lf_selection_event::PhimesonSel); @@ -43,28 +37,6 @@ DECLARE_SOA_TABLE(PhiStrangeEvtSelMcGen, "AOD", "EVTSELMCGEN", lf_selection_event::DefaultSel, lf_selection_event::PhimesonSel); -/*namespace lf_selection_default_collision -{ -DECLARE_SOA_COLUMN(DefaultSel, defaultSel, bool); -} // namespace lf_selection_default_collision - -DECLARE_SOA_TABLE(DefaultSelectionData, "AOD", "DEFSELDATA", - lf_selection_default_collision::DefaultSel); - -DECLARE_SOA_TABLE(DefaultSelectionMcGen, "AOD", "DEFSELMCGEN", - lf_selection_default_collision::DefaultSel); - -namespace lf_selection_phi_collision -{ -DECLARE_SOA_COLUMN(PhimesonSel, phimesonSel, bool); -} // namespace lf_selection_phi_collision - -DECLARE_SOA_TABLE(PhimesonSelectionData, "AOD", "PHIINCOLLDATA", - lf_selection_phi_collision::PhimesonSel); - -DECLARE_SOA_TABLE(PhimesonSelectionMcGen, "AOD", "PHIINCOLLMCGEN", - lf_selection_phi_collision::PhimesonSel);*/ - namespace lf_selection_phi_candidate { DECLARE_SOA_INDEX_COLUMN(Collision, collision); @@ -89,7 +61,7 @@ DECLARE_SOA_TABLE(PhimesonCandidatesData, "AOD", "PHICANDDATA", lf_selection_phi_candidate::Phi, lf_selection_phi_candidate::InMassRegion); -DECLARE_SOA_TABLE(PhimesonCandidatesMcReco, "AOD", "PHICANDMCRECO", +DECLARE_SOA_TABLE(PhimesonCandidatesMcReco, "AOD", "PHICANDMCREC", soa::Index<>, lf_selection_phi_candidate::CollisionId, lf_selection_phi_candidate::M, @@ -107,7 +79,7 @@ DECLARE_SOA_TABLE(PhimesonCandidatesMcGen, "AOD", "PHICANDMCGEN", lf_selection_phi_candidate::Phi, lf_selection_phi_candidate::InMassRegion); -namespace lf_selection_k0s_reduced +namespace lf_selection_strange_reduced { DECLARE_SOA_INDEX_COLUMN(Collision, collision); @@ -120,58 +92,97 @@ DECLARE_SOA_DYNAMIC_COLUMN(InMassRegion, inMassRegion, [](float m, float minM, float maxM) -> bool { return (m >= minM && m <= maxM); }); -} // namespace lf_selection_k0s_reduced +} // namespace lf_selection_strange_reduced DECLARE_SOA_TABLE(K0sReducedCandidatesData, "AOD", "K0SCANDDATA", soa::Index<>, - lf_selection_k0s_reduced::CollisionId, - lf_selection_k0s_reduced::M, - lf_selection_k0s_reduced::Pt, - lf_selection_k0s_reduced::Y, - lf_selection_k0s_reduced::Phi, - lf_selection_k0s_reduced::InMassRegion); - -DECLARE_SOA_TABLE(K0sReducedCandidatesMcReco, "AOD", "K0SCANDMCRECO", + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(K0sReducedCandidatesMcReco, "AOD", "K0SCANDMCREC", soa::Index<>, - lf_selection_k0s_reduced::CollisionId, - lf_selection_k0s_reduced::M, - lf_selection_k0s_reduced::Pt, - lf_selection_k0s_reduced::Y, - lf_selection_k0s_reduced::Phi, - lf_selection_k0s_reduced::InMassRegion); - -namespace lf_selection_xi_reduced -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); - -DECLARE_SOA_COLUMN(M, m, float); -DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(Y, y, float); -DECLARE_SOA_COLUMN(Phi, phi, float); - -DECLARE_SOA_DYNAMIC_COLUMN(InMassRegion, inMassRegion, - [](float m, float minM, float maxM) -> bool { - return (m >= minM && m <= maxM); - }); -} // namespace lf_selection_xi_reduced + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(LambdaReducedCandidatesData, "AOD", "LAMBDACANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(LambdaReducedCandidatesMcReco, "AOD", "LAMBDACANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(AntiLambdaReducedCandidatesData, "AOD", "ALAMBCANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(AntiLambdaReducedCandidatesMcReco, "AOD", "ALAMBCANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); DECLARE_SOA_TABLE(XiReducedCandidatesData, "AOD", "XICANDDATA", soa::Index<>, - lf_selection_xi_reduced::CollisionId, - lf_selection_xi_reduced::M, - lf_selection_xi_reduced::Pt, - lf_selection_xi_reduced::Y, - lf_selection_xi_reduced::Phi, - lf_selection_xi_reduced::InMassRegion); - -DECLARE_SOA_TABLE(XiReducedCandidatesMcReco, "AOD", "XICANDMCRECO", + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(XiReducedCandidatesMcReco, "AOD", "XICANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(OmegaReducedCandidatesData, "AOD", "OMEGACANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(OmegaReducedCandidatesMcReco, "AOD", "OMEGACANDMCREC", soa::Index<>, - lf_selection_xi_reduced::CollisionId, - lf_selection_xi_reduced::M, - lf_selection_xi_reduced::Pt, - lf_selection_xi_reduced::Y, - lf_selection_xi_reduced::Phi, - lf_selection_xi_reduced::InMassRegion); + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); namespace lf_selection_pion_track { @@ -209,7 +220,7 @@ DECLARE_SOA_TABLE(PionTracksData, "AOD", "PITRACKSDATA", lf_selection_pion_track::HasTOF, lf_selection_pion_track::NSigmaTOF>); -DECLARE_SOA_TABLE(PionTracksMcReco, "AOD", "PITRACKSMCRECO", +DECLARE_SOA_TABLE(PionTracksMcReco, "AOD", "PITRACKSMCREC", soa::Index<>, lf_selection_pion_track::CollisionId, lf_selection_pion_track::NSigmaTPC, diff --git a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx index 816c35150b5..907bfb71011 100644 --- a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx +++ b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx @@ -238,7 +238,7 @@ struct PhiMesonCandProducer { continue; } - const auto& mcParticles = mcParticlesOpt.value().get(); + const auto& mcParticles = mcParticlesOpt->get(); const auto track1McParticle = mcParticles.rawIteratorAt(track1.mcParticleId()); if (track1McParticle.pdgCode() != PDG_t::kKPlus || !track1McParticle.isPhysicalPrimary()) { continue; @@ -259,7 +259,7 @@ struct PhiMesonCandProducer { continue; } - const auto& mcParticles = mcParticlesOpt.value().get(); + const auto& mcParticles = mcParticlesOpt->get(); const auto track2McParticle = mcParticles.rawIteratorAt(track2.mcParticleId()); if (track2McParticle.pdgCode() != PDG_t::kKMinus || !track2McParticle.isPhysicalPrimary()) { continue; @@ -285,7 +285,7 @@ struct PhiMesonCandProducer { continue; } - const auto& mcParticles = mcParticlesOpt.value().get(); + const auto& mcParticles = mcParticlesOpt->get(); const auto track1McParticle = mcParticles.rawIteratorAt(track1.mcParticleId()); const auto track2McParticle = mcParticles.rawIteratorAt(track2.mcParticleId()); @@ -403,11 +403,21 @@ struct PhiMesonCandProducer { PROCESS_SWITCH(PhiMesonCandProducer, processMCGen, "Process function to select Phi meson candidates in MCGen", false); }; -struct K0sReducedCandProducer { - // Produce the table with the K0s candidates information +struct V0ReducedCandProducer { + enum V0Type { kK0S = 0, + kLambda, + kAntiLambda }; + + // Produce the table with the V0 candidates information Produces k0sReducedCandidatesData; Produces k0sReducedCandidatesMcReco; + Produces lambdaReducedCandidatesData; + Produces lambdaReducedCandidatesMcReco; + + Produces antilambdaReducedCandidatesData; + Produces antilambdaReducedCandidatesMcReco; + HistogramRegistry histos{"k0sReducedCandidates", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable on multiplicity bins @@ -417,6 +427,7 @@ struct K0sReducedCandProducer { struct : ConfigurableGroup { Configurable etaMax{"etaMax", 0.8f, "eta max"}; Configurable nSigmaCutTPCSecPion{"nSigmaCutTPCSecPion", 4.0f, "Value of the TPC Nsigma cut for secondary Pions"}; + Configurable nSigmaCutTPCSecProton{"nSigmaCutTPCSecProton", 4.0f, "Value of the TPC Nsigma cut for secondary Protons"}; Configurable minTPCnClsFound{"minTPCnClsFound", 70, "min number of found TPC clusters"}; Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "min number of TPC crossed rows"}; @@ -425,25 +436,37 @@ struct K0sReducedCandProducer { // Configurables for V0 selection struct : ConfigurableGroup { - Configurable v0SettingCosPA{"v0SettingCosPA", 0.98f, "V0 CosPA"}; - Configurable v0SettingRadius{"v0SettingRadius", 0.5f, "V0 decay radius"}; - Configurable v0SettingDCAV0Dau{"v0SettingDCAV0Dau", 1.0f, "DCA V0 Daughters"}; - Configurable v0SettingDCAPosToPV{"v0SettingDCAPosToPV", 0.1f, "DCA Pos To PV"}; - Configurable v0SettingDCANegToPV{"v0SettingDCANegToPV", 0.1f, "DCA Neg To PV"}; - Configurable v0SettingMinPt{"v0SettingMinPt", 0.1f, "V0 min pt"}; + Configurable decayRadius{"decayRadius", 0.5f, "V0 decay radius"}; + Configurable dcaV0Dau{"dcaV0Dau", 1.0f, "DCA V0 Daughters"}; + Configurable dcaPosToPV{"dcaPosToPV", 0.1f, "DCA Pos To PV"}; + Configurable dcaNegToPV{"dcaNegToPV", 0.1f, "DCA Neg To PV"}; - Configurable cfgFurtherV0Selection{"cfgFurtherV0Selection", false, "Further V0 selection"}; - Configurable ctauK0s{"ctauK0s", 20.0f, "C tau K0s(cm)"}; - Configurable paramArmenterosCut{"paramArmenterosCut", 0.2f, "parameter Armenteros Cut"}; - Configurable v0rejK0s{"v0rejK0s", 0.005f, "V0 rej K0s"}; + Configurable furtherV0Selection{"furtherV0Selection", false, "Further V0 selection"}; - Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; + Configurable yAcceptance{"yAcceptance", 0.5f, "Rapidity acceptance"}; - Configurable cfgYAcceptance{"cfgYAcceptance", 0.5f, "Rapidity acceptance"}; - } v0Configs; + struct : ConfigurableGroup { + Configurable cosPAK0S{"cosPAK0S", 0.98f, "K0S CosPA"}; + Configurable ctauK0S{"ctauK0S", 20.0f, "C tau K0s(cm)"}; + Configurable rejMassK0S{"rejMassK0S", 0.005f, "V0 rej K0s"}; + + Configurable paramArmenterosCutK0S{"paramArmenterosCutK0S", 0.2f, "Parameter for Armenteros Cut for K0S"}; - // Configurable on K0S pT bins - Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; + Configurable minPtK0S{"minPtK0S", 0.1f, "K0S min pt"}; + Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; + Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; + } k0sConfigs; + + struct : ConfigurableGroup { + Configurable cosPALambda{"cosPALambda", 0.98f, "Lambda CosPA"}; + Configurable ctauLambda{"ctauLambda", 7.89f, "C tau Lambda(cm)"}; + Configurable rejMassLambda{"rejMassLambda", 0.005f, "V0 rej Lambda"}; + + Configurable minPtLambda{"minPtLambda", 0.1f, "Lambda min pt"}; + Configurable> binspTLambda{"binspTLambda", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda"}; + Configurable> rangeMLambdaSignal{"rangeMLambdaSignal", {1.11f, 1.12f}, "Lambda mass range for signal extraction"}; + } lambdaConfigs; + } v0Configs; // Constants static constexpr double MassK0S = o2::constants::physics::MassK0Short; @@ -454,9 +477,9 @@ struct K0sReducedCandProducer { Filter collisionFilter = aod::lf_selection_event::defaultSel == true; // Defining filters on V0s (cannot filter on dynamic columns) - Filter v0PreFilter = (nabs(aod::v0data::dcapostopv) > v0Configs.v0SettingDCAPosToPV && - nabs(aod::v0data::dcanegtopv) > v0Configs.v0SettingDCANegToPV && - aod::v0data::dcaV0daughters < v0Configs.v0SettingDCAV0Dau); + Filter v0PreFilter = (nabs(aod::v0data::dcapostopv) > v0Configs.dcaPosToPV && + nabs(aod::v0data::dcanegtopv) > v0Configs.dcaNegToPV && + aod::v0data::dcaV0daughters < v0Configs.dcaV0Dau); // Defining the type of the collisions for data and MC using SelCollisions = soa::Join; @@ -469,16 +492,35 @@ struct K0sReducedCandProducer { using FullV0s = soa::Filtered; using FullMCV0s = soa::Filtered>; - using V0DauTracks = soa::Join; + using V0DauTracks = soa::Join; using V0DauMCTracks = soa::Join; void init(InitContext&) { AxisSpec binnedmultAxis{(std::vector)binsMult, "centFT0M"}; - AxisSpec binnedpTK0SAxis{(std::vector)binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTK0SAxis{(std::vector)v0Configs.k0sConfigs.binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxis{(std::vector)v0Configs.lambdaConfigs.binspTLambda, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec massK0SAxis = {200, 0.45f, 0.55f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec massLambdaAxis = {200, 1.08f, 1.15f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; histos.add("h3K0sCandidatesMass", "K^{0}_{S} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTK0SAxis, massK0SAxis}); + histos.add("h3LambdaCandidatesMass", "#Lambda candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); + histos.add("h3AntiLambdaCandidatesMass", "#bar{#Lambda} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); + } + + template + constexpr int getPdgCode() + { + if constexpr (v0Type == kK0S) { + return PDG_t::kK0Short; + } else if constexpr (v0Type == kLambda) { + return PDG_t::kLambda0; + } else if constexpr (v0Type == kAntiLambda) { + return PDG_t::kLambda0Bar; + } else { + static_assert(v0Type == kK0S || v0Type == kLambda || v0Type == kAntiLambda, "Unsupported particle type in getPdgCode"); + return 0; + } } // Single track selection for strangeness sector @@ -505,7 +547,7 @@ struct K0sReducedCandProducer { } // V0 selection - template + template bool selectionV0(const T1& v0, const T2& collision) { using V0DauTrackType = std::conditional_t; @@ -518,88 +560,154 @@ struct K0sReducedCandProducer { } if constexpr (!isMC) { - if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { - return false; - } - if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { - return false; + if constexpr (v0Type == kK0S) { + if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (v0Type == kLambda) { + if (std::abs(posDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (v0Type == kAntiLambda) { + if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } } } - if (v0.v0cosPA() < v0Configs.v0SettingCosPA) { + if (v0.v0radius() < v0Configs.decayRadius) { return false; } - if (v0.v0radius() < v0Configs.v0SettingRadius) { + if (std::abs(v0.rapidity(v0Type)) > v0Configs.yAcceptance) { return false; } - if (v0.pt() < v0Configs.v0SettingMinPt) { + + const float cutCosPA = (v0Type == kK0S) ? v0Configs.k0sConfigs.cosPAK0S : v0Configs.lambdaConfigs.cosPALambda; + const float cutMinPt = (v0Type == kK0S) ? v0Configs.k0sConfigs.minPtK0S : v0Configs.lambdaConfigs.minPtLambda; + const float cutCtau = (v0Type == kK0S) ? v0Configs.k0sConfigs.ctauK0S : v0Configs.lambdaConfigs.ctauLambda; + + if (v0.v0cosPA() < cutCosPA) { + return false; + } + if (v0.pt() < cutMinPt) { return false; } - if (v0Configs.cfgFurtherV0Selection) { - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassK0S > v0Configs.ctauK0s) { - return false; - } - if (v0.qtarm() < (v0Configs.paramArmenterosCut * std::abs(v0.alpha()))) { + if (v0Configs.furtherV0Selection) { + constexpr float assumedMass = v0Type == kK0S ? MassK0S : MassLambda; + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * assumedMass > cutCtau) { return false; } - if (std::abs(v0.mLambda() - MassLambda) < v0Configs.v0rejK0s) { - return false; + + if constexpr (v0Type == kK0S) { + if (std::abs(v0.mLambda() - MassLambda) < v0Configs.k0sConfigs.rejMassK0S || + std::abs(v0.mAntiLambda() - MassLambda) < v0Configs.k0sConfigs.rejMassK0S) { + return false; + } + if (v0.qtarm() < (v0Configs.k0sConfigs.paramArmenterosCutK0S * std::abs(v0.alpha()))) { + return false; + } + } else { + if (std::abs(v0.mK0Short() - MassK0S) < v0Configs.lambdaConfigs.rejMassLambda) { + return false; + } } } - if (std::abs(v0.yK0Short()) > v0Configs.cfgYAcceptance) { - return false; + return true; + } + + template + void processV0Type(TColl const& collision, TV0 const& v0, std::optional> mcParticlesOpt = std::nullopt) + { + // Cut on V0 dynamic columns + if (!selectionV0(v0, collision)) { + return; } - return true; + if constexpr (isMC) { + if (!v0.has_mcParticle() || !mcParticlesOpt) { + return; + } + + const auto& mcParticles = mcParticlesOpt->get(); + const auto& v0McParticle = mcParticles.rawIteratorAt(v0.mcParticleId()); + if (v0McParticle.pdgCode() != getPdgCode() || !v0McParticle.isPhysicalPrimary()) { + return; + } + + if constexpr (v0Type == kK0S) { + k0sReducedCandidatesMcReco(collision.globalIndex(), v0.mK0Short(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } else if constexpr (v0Type == kLambda) { + lambdaReducedCandidatesMcReco(collision.globalIndex(), v0.mLambda(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } else if constexpr (v0Type == kAntiLambda) { + antilambdaReducedCandidatesMcReco(collision.globalIndex(), v0.mAntiLambda(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } + } else { + if constexpr (v0Type == kK0S) { + histos.fill(HIST("h3K0sCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mK0Short()); + k0sReducedCandidatesData(collision.globalIndex(), v0.mK0Short(), v0.pt(), v0.yK0Short(), v0.phi()); + } else if constexpr (v0Type == kLambda) { + histos.fill(HIST("h3LambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mLambda()); + lambdaReducedCandidatesData(collision.globalIndex(), v0.mLambda(), v0.pt(), v0.yLambda(), v0.phi()); + } else if constexpr (v0Type == kAntiLambda) { + histos.fill(HIST("h3AntiLambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mAntiLambda()); + antilambdaReducedCandidatesData(collision.globalIndex(), v0.mAntiLambda(), v0.pt(), v0.yLambda(), v0.phi()); + // histos.fill(HIST("h3LambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mAntiLambda()); + // lambdaReducedCandidatesData(collision.globalIndex(), v0.mAntiLambda(), v0.pt(), v0.yLambda(), v0.phi()); + } + } + } + + template + void evaluateAllHypotheses(TColl const& collision, TV0 const& v0, std::optional> mcParticlesOpt = std::nullopt) + { + (processV0Type(collision, v0, mcParticlesOpt), ...); } // void processData(SelCollisions::iterator const& collision, FullV0s const& V0s, V0DauTracks const&) void processData(FilteredSelCollisions::iterator const& collision, FullV0s const& V0s, V0DauTracks const&) { for (const auto& v0 : V0s) { - // Cut on V0 dynamic columns - if (!selectionV0(v0, collision)) { - continue; - } - - histos.fill(HIST("h3K0sCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mK0Short()); - - k0sReducedCandidatesData(collision.globalIndex(), v0.mK0Short(), v0.pt(), v0.yK0Short(), v0.phi()); + evaluateAllHypotheses(collision, v0); } } - PROCESS_SWITCH(K0sReducedCandProducer, processData, "Process function to select reduced K0s candidates in Data or in McReco (w/o McTruth) analysis", true); + PROCESS_SWITCH(V0ReducedCandProducer, processData, "Process function to select reduced K0S candidates in Data or in McReco (w/o McTruth) analysis", true); // void processMCReco(SimCollisions::iterator const& collision, FullMCV0s const& V0s, V0DauMCTracks const&, aod::McParticles const& mcParticles) void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCV0s const& V0s, V0DauMCTracks const&, aod::McParticles const& mcParticles) { for (const auto& v0 : V0s) { - if (!selectionV0(v0, collision)) { - continue; - } - if (!v0.has_mcParticle()) { - continue; - } - - const auto& v0McParticle = mcParticles.rawIteratorAt(v0.mcParticleId()); - if (std::abs(v0McParticle.pdgCode()) != PDG_t::kK0Short || !v0McParticle.isPhysicalPrimary()) { - continue; - } - - k0sReducedCandidatesMcReco(collision.globalIndex(), v0.mK0Short(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + evaluateAllHypotheses(collision, v0, mcParticles); } } - PROCESS_SWITCH(K0sReducedCandProducer, processMCReco, "Process function to select reduced K0s candidates in MCReco w MC truth", false); + PROCESS_SWITCH(V0ReducedCandProducer, processMCReco, "Process function to select reduced K0s candidates in MCReco w MC truth", false); }; -struct XiReducedCandProducer { +struct CascadeReducedCandProducer { + enum CascadeType { kXi = 0, + kAntiXi, + kOmega, + kAntiOmega }; + // Produce the table with the Xi candidates information Produces xiReducedCandidatesData; Produces xiReducedCandidatesMcReco; + Produces omegaReducedCandidatesData; + Produces omegaReducedCandidatesMcReco; + HistogramRegistry histos{"xiReducedCandidates", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable on multiplicity bins @@ -609,6 +717,7 @@ struct XiReducedCandProducer { struct : ConfigurableGroup { Configurable etaMax{"etaMax", 0.8f, "eta max"}; Configurable nSigmaCutTPCSecPion{"nSigmaCutTPCSecPion", 4.0f, "Value of the TPC Nsigma cut for secondary Pions"}; + Configurable nSigmaCutTPCSecKaon{"nSigmaCutTPCSecKaon", 4.0f, "Value of the TPC Nsigma cut for secondary Kaons"}; Configurable nSigmaCutTPCSecProton{"nSigmaCutTPCSecProton", 4.0f, "Value of the TPC Nsigma cut for secondary Protons"}; Configurable minTPCnClsFound{"minTPCnClsFound", 70, "min number of found TPC clusters"}; @@ -618,35 +727,48 @@ struct XiReducedCandProducer { // Configurables for Cascade selection struct : ConfigurableGroup { - Configurable cascSettingV0CosPA{"cascSettingV0CosPA", 0.98f, "V0 CosPA"}; - Configurable cascSettingCosPA{"cascSettingCosPA", 0.98f, "Cascade CosPA"}; - Configurable cascSettingV0Radius{"cascSettingV0Radius", 0.5f, "V0 decay radius"}; - Configurable cascSettingRadius{"cascSettingRadius", 0.5f, "Cascade decay radius"}; - Configurable cascSettingDCAV0Dau{"cascSettingDCAV0Dau", 1.0f, "DCA V0 Daughters"}; - Configurable cascSettingDCACascDau{"cascSettingDCACascDau", 1.0f, "DCA Cascade Daughters"}; - Configurable cascSettingDCAPosToPV{"cascSettingDCAPosToPV", 0.1f, "DCA Pos To PV"}; - Configurable cascSettingDCANegToPV{"cascSettingDCANegToPV", 0.1f, "DCA Neg To PV"}; - Configurable cascSettingDCABachToPV{"cascSettingDCABachToPV", 0.1f, "DCA Bach To PV"}; - Configurable cascSettingMinPt{"cascSettingMinPt", 0.8f, "Cascade min pt"}; - - Configurable cfgYAcceptance{"cfgYAcceptance", 0.5f, "Rapidity acceptance"}; + Configurable dcaV0Dau{"dcaV0Dau", 1.0f, "DCA V0 Daughters"}; + Configurable dcaCascDau{"dcaCascDau", 1.0f, "DCA Cascade Daughters"}; + Configurable dcaPosToPV{"dcaPosToPV", 0.1f, "DCA Pos To PV"}; + Configurable dcaNegToPV{"dcaNegToPV", 0.1f, "DCA Neg To PV"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.1f, "DCA Bach To PV"}; + + Configurable yAcceptance{"yAcceptance", 0.5f, "Rapidity acceptance"}; + + struct : ConfigurableGroup { + Configurable v0CosPAXi{"v0CosPAXi", 0.98f, "Xi V0 CosPA"}; + Configurable cosPAXi{"cosPAXi", 0.98f, "Xi CosPA"}; + Configurable v0RadiusXi{"v0RadiusXi", 0.5f, "V0 decay radius"}; + Configurable radiusXi{"radiusXi", 0.5f, "Xi decay radius"}; + + Configurable minPtXi{"minPtXi", 0.8f, "Xi min pt"}; + Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; + } xiConfigs; + + struct : ConfigurableGroup { + Configurable v0CosPAOmega{"v0CosPAOmega", 0.98f, "Omega V0 CosPA"}; + Configurable cosPAOmega{"cosPAOmega", 0.98f, "Omega CosPA"}; + Configurable v0RadiusOmega{"v0RadiusOmega", 0.5f, "V0 decay radius"}; + Configurable radiusOmega{"radiusOmega", 0.5f, "Omega decay radius"}; + + Configurable minPtOmega{"minPtOmega", 0.8f, "Omega min pt"}; + Configurable> binspTOmega{"binspTOmega", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega"}; + } omegaConfigs; } cascadeConfigs; - // Configurables for Xi pT bins - Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; - // Constants static constexpr double MassXi = o2::constants::physics::MassXiMinus; + static constexpr double MassOmega = o2::constants::physics::MassOmegaMinus; // Filter on default selected collisions Filter collisionFilter = aod::lf_selection_event::defaultSel == true; // Defining filters on Cascades (cannot filter on dynamic columns) - Filter cascadePreFilter = (nabs(aod::cascdata::dcapostopv) > cascadeConfigs.cascSettingDCAPosToPV && - nabs(aod::cascdata::dcanegtopv) > cascadeConfigs.cascSettingDCANegToPV && - nabs(aod::cascdata::dcabachtopv) > cascadeConfigs.cascSettingDCABachToPV && - aod::cascdata::dcaV0daughters < cascadeConfigs.cascSettingDCAV0Dau && - aod::cascdata::dcacascdaughters < cascadeConfigs.cascSettingDCACascDau); + Filter cascadePreFilter = (nabs(aod::cascdata::dcapostopv) > cascadeConfigs.dcaPosToPV && + nabs(aod::cascdata::dcanegtopv) > cascadeConfigs.dcaNegToPV && + nabs(aod::cascdata::dcabachtopv) > cascadeConfigs.dcaBachToPV && + aod::cascdata::dcaV0daughters < cascadeConfigs.dcaV0Dau && + aod::cascdata::dcacascdaughters < cascadeConfigs.dcaCascDau); // Defining the type of the collisions for data and MC using SelCollisions = soa::Join; @@ -659,16 +781,36 @@ struct XiReducedCandProducer { using FullCascades = soa::Filtered; using FullMCCascades = soa::Filtered>; - using DauTracks = soa::Join; + using DauTracks = soa::Join; using DauMCTracks = soa::Join; void init(InitContext&) { AxisSpec binnedmultAxis{(std::vector)binsMult, "centFT0M"}; - AxisSpec binnedpTXiAxis{(std::vector)binspTXi, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTXiAxis{(std::vector)cascadeConfigs.xiConfigs.binspTXi, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxis{(std::vector)cascadeConfigs.omegaConfigs.binspTOmega, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec massXiAxis = {200, 1.2f, 1.4f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec massOmegaAxis = {200, 1.6f, 1.8f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; histos.add("h3XiCandidatesMass", "#Xi candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTXiAxis, massXiAxis}); + histos.add("h3OmegaCandidatesMass", "#Omega candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTOmegaAxis, massOmegaAxis}); + } + + template + constexpr int getPdgCode() + { + if constexpr (cascadeType == kXi) { + return PDG_t::kXiMinus; + } else if constexpr (cascadeType == kAntiXi) { + return PDG_t::kXiPlusBar; + } else if constexpr (cascadeType == kOmega) { + return PDG_t::kOmegaMinus; + } else if constexpr (cascadeType == kAntiOmega) { + return PDG_t::kOmegaPlusBar; + } else { + static_assert(cascadeType == kXi || cascadeType == kAntiXi || cascadeType == kOmega || cascadeType == kAntiOmega, "Unsupported particle type in getPdgCode"); + return 0; + } } // Single track selection for strangeness sector @@ -695,9 +837,19 @@ struct XiReducedCandProducer { } // Cascade selection - template + template bool selectionCascade(const T1& cascade, const T2& collision) { + if constexpr (cascadeType == kXi || cascadeType == kOmega) { + if (cascade.sign() > 0) { + return false; // Matter must have negative charge + } + } else { + if (cascade.sign() < 0) { + return false; // Anti-matter must have positive charge + } + } + using DauTrackType = std::conditional_t; const auto& posDaughterTrack = cascade.template posTrack_as(); @@ -709,14 +861,14 @@ struct XiReducedCandProducer { } if constexpr (!isMC) { - if (cascade.sign() < 0) { + if constexpr (cascadeType == kXi || cascadeType == kOmega) { if (std::abs(posDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { return false; } if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { return false; } - } else if (cascade.sign() > 0) { + } else { if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { return false; } @@ -724,73 +876,110 @@ struct XiReducedCandProducer { return false; } } - if (std::abs(bachDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { - return false; + + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + if (std::abs(bachDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + if (std::abs(bachDaughterTrack.tpcNSigmaKa()) > trackConfigs.nSigmaCutTPCSecKaon) { + return false; + } } } + if (std::abs(cascade.rapidity(cascadeType)) > cascadeConfigs.yAcceptance) { + return false; + } + + const float cutV0CosPA = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.v0CosPAXi : cascadeConfigs.omegaConfigs.v0CosPAOmega; + const float cutCosPA = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.cosPAXi : cascadeConfigs.omegaConfigs.cosPAOmega; + const float cutV0Radius = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.v0RadiusXi : cascadeConfigs.omegaConfigs.v0RadiusOmega; + const float cutRadius = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.radiusXi : cascadeConfigs.omegaConfigs.radiusOmega; + const float cutMinPt = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.minPtXi : cascadeConfigs.omegaConfigs.minPtOmega; + const auto& pvx = collision.posX(); const auto& pvy = collision.posY(); const auto& pvz = collision.posZ(); - if (cascade.v0cosPA(pvx, pvy, pvz) < cascadeConfigs.cascSettingV0CosPA) { + if (cascade.v0cosPA(pvx, pvy, pvz) < cutV0CosPA) { return false; } - if (cascade.casccosPA(pvx, pvy, pvz) < cascadeConfigs.cascSettingCosPA) { + if (cascade.casccosPA(pvx, pvy, pvz) < cutCosPA) { return false; } - if (cascade.v0radius() < cascadeConfigs.cascSettingV0Radius) { + if (cascade.v0radius() < cutV0Radius) { return false; } - if (cascade.cascradius() < cascadeConfigs.cascSettingRadius) { + if (cascade.cascradius() < cutRadius) { return false; } - if (cascade.pt() < cascadeConfigs.cascSettingMinPt) { + if (cascade.pt() < cutMinPt) { return false; } - if (std::abs(cascade.yXi()) > cascadeConfigs.cfgYAcceptance) { - return false; - } return true; } - void processData(FilteredSelCollisions::iterator const& collision, FullCascades const& cascades, DauTracks const&) + template + void processCascadeType(TColl const& collision, TCascade const& cascade, std::optional> mcParticlesOpt = std::nullopt) { - for (const auto& cascade : cascades) { - // Cut on cascade dynamic columns - if (!selectionCascade(cascade, collision)) { - continue; + // Cut on Cascade dynamic columns + if (!selectionCascade(cascade, collision)) { + return; + } + + if constexpr (isMC) { + if (!cascade.has_mcParticle() || !mcParticlesOpt) { + return; } - histos.fill(HIST("h3XiCandidatesMass"), collision.centFT0M(), cascade.pt(), cascade.mXi()); + const auto& mcParticles = mcParticlesOpt->get(); + const auto& cascadeMcParticle = mcParticles.rawIteratorAt(cascade.mcParticleId()); + if (cascadeMcParticle.pdgCode() != getPdgCode() || !cascadeMcParticle.isPhysicalPrimary()) { + return; + } - xiReducedCandidatesData(collision.globalIndex(), cascade.mXi(), cascade.pt(), cascade.yXi(), cascade.phi()); + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + xiReducedCandidatesMcReco(collision.globalIndex(), cascade.mXi(), cascadeMcParticle.pt(), cascadeMcParticle.y(), cascadeMcParticle.phi()); + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + omegaReducedCandidatesMcReco(collision.globalIndex(), cascade.mOmega(), cascadeMcParticle.pt(), cascadeMcParticle.y(), cascadeMcParticle.phi()); + } + } else { + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + histos.fill(HIST("h3XiCandidatesMass"), collision.centFT0M(), cascade.pt(), cascade.mXi()); + xiReducedCandidatesData(collision.globalIndex(), cascade.mXi(), cascade.pt(), cascade.yXi(), cascade.phi()); + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + histos.fill(HIST("h3OmegaCandidatesMass"), collision.centFT0M(), cascade.pt(), cascade.mOmega()); + omegaReducedCandidatesData(collision.globalIndex(), cascade.mOmega(), cascade.pt(), cascade.yOmega(), cascade.phi()); + } } } - PROCESS_SWITCH(XiReducedCandProducer, processData, "Process function to select reduced Xi candidates in Data or in McReco (w/o McTruth) analysis", true); + template + void evaluateAllHypotheses(TColl const& collision, TCascade const& cascade, std::optional> mcParticlesOpt = std::nullopt) + { + (processCascadeType(collision, cascade, mcParticlesOpt), ...); + } - void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCCascades const& cascades, DauMCTracks const&, aod::McParticles const& mcParticles) + void + processData(FilteredSelCollisions::iterator const& collision, FullCascades const& cascades, DauTracks const&) { for (const auto& cascade : cascades) { - if (!selectionCascade(cascade, collision)) { - continue; - } - if (!cascade.has_mcParticle()) { - continue; - } + evaluateAllHypotheses(collision, cascade); + } + } - const auto& cascadeMcParticle = mcParticles.rawIteratorAt(cascade.mcParticleId()); - if (std::abs(cascadeMcParticle.pdgCode()) != PDG_t::kXiMinus || !cascadeMcParticle.isPhysicalPrimary()) { - continue; - } + PROCESS_SWITCH(CascadeReducedCandProducer, processData, "Process function to select reduced Xi candidates in Data or in McReco (w/o McTruth) analysis", true); - xiReducedCandidatesMcReco(collision.globalIndex(), cascade.mXi(), cascadeMcParticle.pt(), cascadeMcParticle.y(), cascadeMcParticle.phi()); + void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCCascades const& cascades, DauMCTracks const&, aod::McParticles const& mcParticles) + { + for (const auto& cascade : cascades) { + evaluateAllHypotheses(collision, cascade, mcParticles); } } - PROCESS_SWITCH(XiReducedCandProducer, processMCReco, "Process function to select reduced Xi candidates in MCReco w MC truth", false); + PROCESS_SWITCH(CascadeReducedCandProducer, processMCReco, "Process function to select reduced Xi candidates in MCReco w MC truth", false); }; struct PionTrackProducer { @@ -1323,8 +1512,8 @@ struct EventSelectionProducer { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index 7f5b35b17da..3db46f77cd6 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,10 @@ enum AnalysisMode { enum AssociatedParticleType { kK0S = 0, + kLambda, + kAntiLambda, kXi, + kOmega, kPion, kAssocPartSize }; @@ -92,12 +96,20 @@ constexpr int getPdgCode() { if constexpr (PartType == kK0S) { return PDG_t::kK0Short; + } else if constexpr (PartType == kLambda) { + return PDG_t::kLambda0; + } else if constexpr (PartType == kAntiLambda) { + return PDG_t::kLambda0Bar; } else if constexpr (PartType == kXi) { return PDG_t::kXiMinus; + } else if constexpr (PartType == kOmega) { + return PDG_t::kOmegaMinus; } else if constexpr (PartType == kPion) { return PDG_t::kPiPlus; } else { - static_assert(PartType == kK0S || PartType == kXi || PartType == kPion, "Unsupported particle type in getPdgCode"); + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getPdgCode"); return 0; } } @@ -199,12 +211,24 @@ struct PhiStrangeCorrelation { Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; } k0sConfigs; + // Configurables for Lambda selection + struct : ConfigurableGroup { + Configurable selectLambdaInSigRegion{"selectLambdaInSigRegion", true, "Select Lambda candidates in signal region"}; + Configurable> rangeMLambdaSignal{"rangeMLambdaSignal", {1.11f, 1.12f}, "Lambda mass range for signal extraction"}; + } lambdaConfigs; + // Configurables for Xi selection struct : ConfigurableGroup { Configurable selectXiInSigRegion{"selectXiInSigRegion", true, "Select Xi candidates in signal region"}; Configurable> rangeMXiSignal{"rangeMXiSignal", {1.31f, 1.33f}, "Xi mass range for signal extraction"}; } xiConfigs; + // Configurables for Omega selection + struct : ConfigurableGroup { + Configurable selectOmegaInSigRegion{"selectOmegaInSigRegion", true, "Select Omega candidates in signal region"}; + Configurable> rangeMOmegaSignal{"rangeMOmegaSignal", {1.66f, 1.68f}, "Omega mass range for signal extraction"}; + } omegaConfigs; + // Configurables for Pions selection struct : ConfigurableGroup { Configurable selectPionInSigRegion{"selectPionInSigRegion", true, "Select Pion candidates in signal region"}; @@ -221,15 +245,23 @@ struct PhiStrangeCorrelation { Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; Configurable> binspTK0SExt{"binspTK0SExt", {0.0, 0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S extended for MC Gen"}; + // Configurable on Lambda pT bins + Configurable> binspTLambda{"binspTLambda", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda"}; + Configurable> binspTLambdaExt{"binspTLambdaExt", {0.0, 0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda extended for MC Gen"}; + // Configurable on Xi pT bins Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; Configurable> binspTXiExt{"binspTXiExt", {0.0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi extended for MC Gen"}; + // Configurable on Omega pT bins + Configurable> binspTOmega{"binspTOmega", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega"}; + Configurable> binspTOmegaExt{"binspTOmegaExt", {0.0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega extended for MC Gen"}; + // Configurable on pion pT bins Configurable> binspTPi{"binspTPi", {0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0}, "pT bin limits for Pions"}; Configurable> binspTPiExt{"binspTPiExt", {0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0}, "pT bin limits for Pions extended for MC Gen"}; - Configurable> activeCorrelationTypes{"activeCorrelationTypes", {true, false, true}, "Enable correlation types: 0 - K0S, 1 - Xi, 2 - Pion"}; + Configurable> activeCorrelationTypes{"activeCorrelationTypes", {true, false, false, false, false, true}, "Enable correlation types: 0 - K0S, 1 - Lambda, 2 - AntiLambda, 3 - Xi, 4 - Omega, 5 - Pion"}; // Configurables for delta y selection struct : ConfigurableGroup { @@ -245,6 +277,7 @@ struct PhiStrangeCorrelation { Configurable useEffInterpolation{"useEffInterpolation", false, "If true, interpolates efficiency map, else uses bin center"}; Configurable applyPhiEfficiency{"applyPhiEfficiency", false, "Apply efficiency for Phi candidates"}; Configurable propagateEffError{"propagateEffError", false, "Propagate efficiency error"}; + Configurable perPeriodEfficiency{"perPeriodEfficiency", false, "Use per period efficiency maps"}; } efficiencyConfigs; // Configurable for event mixing @@ -258,7 +291,9 @@ struct PhiStrangeCorrelation { struct : ConfigurableGroup { Configurable minPhiPt{"minPhiPt", 0.4f, "Minimum pT for Phi candidates"}; Configurable v0SettingMinPt{"v0SettingMinPt", 0.1f, "V0 min pt"}; + Configurable lambdaSettingMinPt{"lambdaSettingMinPt", 0.2f, "Lambda min pt"}; Configurable cascadeSettingMinPt{"cascadeSettingMinPt", 0.8f, "Cascade min pt"}; + Configurable omegaSettingMinPt{"omegaSettingMinPt", 0.8f, "Omega min pt"}; Configurable cMinPionPtcut{"cMinPionPtcut", 0.2f, "Track minimum pt cut"}; Configurable bypassPtCut{"bypassPtCut", false, "Bypass the minimum pt cut at MCGen level"}; } minPtMcGenConfigs; @@ -283,9 +318,18 @@ struct PhiStrangeCorrelation { Preslice k0sDataPerCollision = aod::v0::collisionId; Preslice k0sMcRecoPerCollision = aod::v0::collisionId; + Preslice lambdaDataPerCollision = aod::v0::collisionId; + Preslice lambdaMcRecoPerCollision = aod::v0::collisionId; + + Preslice antiLambdaDataPerCollision = aod::v0::collisionId; + Preslice antiLambdaMcRecoPerCollision = aod::v0::collisionId; + Preslice xiDataPerCollision = aod::cascade::collisionId; Preslice xiMcRecoPerCollision = aod::cascade::collisionId; + Preslice omegaDataPerCollision = aod::cascade::collisionId; + Preslice omegaMcRecoPerCollision = aod::cascade::collisionId; + Preslice pionTrackDataPerCollision = aod::track::collisionId; Preslice pionTrackMcRecoPerCollision = aod::track::collisionId; @@ -309,7 +353,7 @@ struct PhiStrangeCorrelation { BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; static constexpr std::array PhiMassRegionLabels{"Signal", "Sideband"}; - static constexpr std::array AssocParticleLabels{"K0S", "Xi", "Pi"}; + static constexpr std::array AssocParticleLabels{"K0S", "Lambda", "AntiLambda", "Xi", "Omega", "Pi"}; // Light structures to store only the necessary information for the correlation analysis at MCGen level struct MiniParticle { @@ -322,13 +366,18 @@ struct PhiStrangeCorrelation { float multiplicity{}; std::vector phiParticles; std::vector k0sParticles; + std::vector lambdaParticles; + std::vector antilambdaParticles; std::vector xiParticles; + std::vector omegaParticles; std::vector pionParticles; }; // Buffer for mixed event, organized as a vector of deques, one for each multiplicity bin, containing the past events with their particles of interest needed for mixing std::vector> eventBuffer; + int mLastRunNumber{-1}; + void init(InitContext&) { AxisSpec vertexZAxis = {100, -cutZVertex, cutZVertex, "vrtx_{Z} [cm]"}; // TO BE REMOVED @@ -345,10 +394,18 @@ struct PhiStrangeCorrelation { AxisSpec pTK0SAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTK0SAxis{(std::vector)binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTK0SAxisExt{(std::vector)binspTK0SExt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massLambdaAxis = {200, 1.1f, 1.13f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec pTLambdaAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxis{(std::vector)binspTLambda, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxisExt{(std::vector)binspTLambdaExt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec massXiAxis = {200, 1.3f, 1.4f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; AxisSpec pTXiAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTXiAxis{(std::vector)binspTXi, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTXiAxisExt{(std::vector)binspTXiExt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massOmegaAxis = {200, 1.65f, 1.7f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec pTOmegaAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxis{(std::vector)binspTOmega, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxisExt{(std::vector)binspTOmegaExt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec nSigmaPiAxis = {100, -10.0f, 10.0f, "N#sigma #pi"}; AxisSpec pTPiAxis = {50, 0.0f, 5.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTPiAxis{(std::vector)binspTPi, "#it{p}_{T} (GeV/#it{c})"}; @@ -357,22 +414,30 @@ struct PhiStrangeCorrelation { histos.add("phi/h3PhiData", "Invariant mass of Phi in Data", kTH3F, {binnedmultAxis, binnedpTPhiAxis, massPhiAxis}); histos.add("phiK0S/h6PhiK0SData", "Invariant mass of Phi vs Invariant mass of K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); + histos.add("phiLambda/h6PhiLambdaData", "Invariant mass of Phi vs Invariant mass of Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, massPhiAxis, massLambdaAxis}); histos.add("phiXi/h6PhiXiData", "Invariant mass of Phi vs Invariant mass of Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); + histos.add("phiOmega/h6PhiOmegaData", "Invariant mass of Phi vs Invariant mass of Omega in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); histos.add("phiPi/h6PhiPiTPCData", "Invariant mass of Phi vs nSigmaTPC Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); histos.add("phiPi/h6PhiPiTOFData", "Invariant mass of Phi vs nSigmaTOF Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); histos.add("phiK0S/h6PhiK0SDataME", "Invariant mass of Phi vs Invariant mass of K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); + histos.add("phiLambda/h6PhiLambdaDataME", "Invariant mass of Phi vs Invariant mass of Lambda in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, massPhiAxis, massLambdaAxis}); histos.add("phiXi/h6PhiXiDataME", "Invariant mass of Phi vs Invariant mass of Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); + histos.add("phiOmega/h6PhiOmegaDataME", "Invariant mass of Phi vs Invariant mass of Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); for (const auto& label : PhiMassRegionLabels) { histos.add(fmt::format("phiK0S/h5PhiK0SData{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiLambda/h5PhiLambdaData{}", label).c_str(), "Deltay vs deltaphi for Phi and Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiXi/h5PhiXiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiOmega/h5PhiOmegaData{}", label).c_str(), "Deltay vs deltaphi for Phi and Omega in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiPi/h5PhiPiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiK0S/h5PhiK0SDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiLambda/h5PhiLambdaDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Lambda in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiXi/h5PhiXiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiOmega/h5PhiOmegaDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiPi/h5PhiPiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); } @@ -391,10 +456,22 @@ struct PhiStrangeCorrelation { histos.add("k0s/h3K0SMCGen", "K0S in MC Gen", kTH3F, {binnedmultAxis, binnedpTK0SAxis, yAxis}); histos.add("k0s/h4K0SMCGenAssocReco", "K0S in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); + histos.add("lambda/h4LambdaMCReco", "Lambda in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("lambda/h3LambdaMCGen", "Lambda in MC Gen", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("lambda/h4LambdaMCGenAssocReco", "Lambda in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + + histos.add("antilambda/h4AntiLambdaMCReco", "AntiLambda in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("antilambda/h3AntiLambdaMCGen", "AntiLambda in MC Gen", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("antilambda/h4AntiLambdaMCGenAssocReco", "AntiLambda in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("xi/h4XiMCReco", "Xi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTXiAxis, yAxis}); histos.add("xi/h3XiMCGen", "Xi in MC Gen", kTH3F, {binnedmultAxis, binnedpTXiAxis, yAxis}); histos.add("xi/h4XiMCGenAssocReco", "Xi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTXiAxis, yAxis}); + histos.add("omega/h4OmegaMCReco", "Omega in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("omega/h3OmegaMCGen", "Omega in MC Gen", kTH3F, {binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("omega/h4OmegaMCGenAssocReco", "Omega in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("pi/h4PiMCReco", "Pion in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); histos.add("pi/h3PiMCGen", "Pion in MC Gen", kTH3F, {binnedmultAxis, binnedpTPiAxis, yAxis}); histos.add("pi/h4PiMCGenAssocReco", "Pion in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); @@ -402,11 +479,17 @@ struct PhiStrangeCorrelation { histos.add("phi/h3PhiMCClosureGen", "Phi in MC Gen for MC Closure Test", kTH3F, {binnedmultAxis, binnedpTPhiAxisExt, yAxis}); histos.add("phiK0S/h5PhiK0SClosureMCGen", "Deltay vs deltaphi for Phi and K0Short in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiLambda/h5PhiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and Lambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and AntiLambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiXi/h5PhiXiClosureMCGen", "Deltay vs deltaphi for Phi and Xi in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiOmega/h5PhiOmegaClosureMCGen", "Deltay vs deltaphi for Phi and Omega in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTOmegaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiPi/h5PhiPiClosureMCGen", "Deltay vs deltaphi for Phi and Pion in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiK0S/h5PhiK0SClosureMCGenME", "Deltay vs deltaphi for Phi and K0Short in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiLambda/h5PhiLambdaClosureMCGenME", "Deltay vs deltaphi for Phi and Lambda in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGenME", "Deltay vs deltaphi for Phi and AntiLambda in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiXi/h5PhiXiClosureMCGenME", "Deltay vs deltaphi for Phi and Xi in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiOmega/h5PhiOmegaClosureMCGenME", "Deltay vs deltaphi for Phi and Omega in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTOmegaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiPi/h5PhiPiClosureMCGenME", "Deltay vs deltaphi for Phi and Pion in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis}); // Load efficiency maps from CCDB @@ -419,44 +502,65 @@ struct PhiStrangeCorrelation { /*for (int i = 0; i < ParticleOfInterestSize; ++i) { loadEfficiencyMapFromCCDB(static_cast(i)); }*/ - loadEfficiencyMaps(); + if (!efficiencyConfigs.perPeriodEfficiency) { + loadEfficiencyMaps(); + } } eventBuffer.resize(binsMult->size() - 1); } - void fetchSingleEfficiencyMapFromCCDB(EffMapPtr& effMap, std::string_view particleName) + void fetchSingleEfficiencyMapFromCCDB(EffMapPtr& effMap, std::string_view particleName, std::optional runNumber = std::nullopt) { std::string path = fmt::format("{}{}", ccdbEfficiencyPath.value, particleName); - if (auto map3D = std::shared_ptr(ccdb->get(path))) { + auto fetchFromCCDB = [&]() -> std::shared_ptr { + auto mapPtr = runNumber ? ccdb->getForRun(path, *runNumber) : ccdb->get(path); + return std::shared_ptr(mapPtr); + }; + + if (auto map3D = fetchFromCCDB.operator()()) { effMap = map3D; - LOG(info) << "Efficiency map (TH3) for " << particleName << " loaded from CCDB"; + LOG(debug) << "Efficiency map (TH3) for " << particleName << " loaded from CCDB"; return; } - if (auto map2D = std::shared_ptr(ccdb->get(path))) { + if (auto map2D = fetchFromCCDB.operator()()) { effMap = map2D; - LOG(info) << "Efficiency map (TH2) for " << particleName << " loaded from CCDB"; + LOG(debug) << "Efficiency map (TH2) for " << particleName << " loaded from CCDB"; return; } - LOG(fatal) << "Could not load efficiency map (neither TH3 nor TH2) for " << particleName << " from CCDB!"; + std::string runSuffix = runNumber ? fmt::format(" for run {}", *runNumber) : ""; + LOG(fatal) << "Could not load efficiency map (neither TH3 nor TH2) for " << particleName << " from CCDB" << runSuffix << "!"; } - void loadEfficiencyMaps() + void loadEfficiencyMaps(std::optional runNumber = std::nullopt) { + if (runNumber) { + if (*runNumber == mLastRunNumber) { + return; + } + mLastRunNumber = *runNumber; + } + // Load the Trigger (Phi) map if requested by analysis method if (efficiencyConfigs.applyPhiEfficiency) { - fetchSingleEfficiencyMapFromCCDB(effMapPhi, "Phi"); + fetchSingleEfficiencyMapFromCCDB(effMapPhi, "Phi", runNumber); } // Only load the associated maps that are explicitly enabled for (size_t i = 0; i < kAssocPartSize; ++i) { if (activeCorrelationTypes->at(i)) { - fetchSingleEfficiencyMapFromCCDB(effMapsAssoc[i], AssocParticleLabels[i]); + fetchSingleEfficiencyMapFromCCDB(effMapsAssoc[i], AssocParticleLabels[i], runNumber); } } + + if (runNumber) { + LOG(info) << "Efficiency maps successfully updated from CCDB for run " << *runNumber; + } else { + LOG(info) << "Global efficiency maps successfully loaded from CCDB"; + } } // Compute weight based on efficiencies @@ -525,12 +629,20 @@ struct PhiStrangeCorrelation { { if constexpr (PartType == kK0S) { return preslices.k0sMcRecoPerCollision; + } else if constexpr (PartType == kLambda) { + return preslices.lambdaMcRecoPerCollision; + } else if constexpr (PartType == kAntiLambda) { + return preslices.antiLambdaMcRecoPerCollision; } else if constexpr (PartType == kXi) { return preslices.xiMcRecoPerCollision; + } else if constexpr (PartType == kOmega) { + return preslices.omegaMcRecoPerCollision; } else if constexpr (PartType == kPion) { return preslices.pionTrackMcRecoPerCollision; } else { - static_assert(PartType == kK0S || PartType == kXi || PartType == kPion, "Unsupported particle type in getPreslice"); + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getPreslice"); return preslices.k0sMcRecoPerCollision; } } @@ -540,12 +652,18 @@ struct PhiStrangeCorrelation { { if constexpr (PartType == kK0S) { return minPtMcGenConfigs.v0SettingMinPt.value; + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + return minPtMcGenConfigs.lambdaSettingMinPt.value; } else if constexpr (PartType == kXi) { return minPtMcGenConfigs.cascadeSettingMinPt.value; + } else if constexpr (PartType == kOmega) { + return minPtMcGenConfigs.omegaSettingMinPt.value; } else if constexpr (PartType == kPion) { return minPtMcGenConfigs.cMinPionPtcut.value; } else { - static_assert(PartType == kK0S || PartType == kXi || PartType == kPion, "Unsupported particle type in getMinGenPt"); + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getMinGenPt"); return 0.f; } } @@ -623,16 +741,26 @@ struct PhiStrangeCorrelation { const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; const auto& [minMass, maxMass] = k0sConfigs.rangeMK0sSignal.value; return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTK0S->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && lambdaConfigs.selectLambdaInSigRegion; + const auto& [minMass, maxMass] = lambdaConfigs.rangeMLambdaSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTLambda->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); } else if constexpr (PartType == kXi) { const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && xiConfigs.selectXiInSigRegion; const auto& [minMass, maxMass] = xiConfigs.rangeMXiSignal.value; return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTXi->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kOmega) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && omegaConfigs.selectOmegaInSigRegion; + const auto& [minMass, maxMass] = omegaConfigs.rangeMOmegaSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTOmega->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); } else if constexpr (PartType == kPion) { const bool applyNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTPi->back()) && (!applyNSigmaCut || assoc.inNSigmaRegion(pionConfigs.pidTPCMax, pionConfigs.pidTOFMax)); } else { - static_assert(PartType == kK0S || PartType == kXi || PartType == kPion, "Unsupported particle type in isAssocValid"); + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in isAssocValid"); return false; } } @@ -655,12 +783,24 @@ struct PhiStrangeCorrelation { } else { customFillTHn(HIST("phiK0S/h6PhiK0SData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + if constexpr (IsME) { + customFillTHn(HIST("phiLambda/h6PhiLambdaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiLambda/h6PhiLambdaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } } else if constexpr (PartType == kXi) { if constexpr (IsME) { customFillTHn(HIST("phiXi/h6PhiXiDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } else { customFillTHn(HIST("phiXi/h6PhiXiData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } + } else if constexpr (PartType == kOmega) { + if constexpr (IsME) { + customFillTHn(HIST("phiOmega/h6PhiOmegaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiOmega/h6PhiOmegaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } } else if constexpr (PartType == kPion) { if constexpr (IsME) { customFillTHn(HIST("phiPi/h6PhiPiTPCDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTPC()); @@ -682,6 +822,14 @@ struct PhiStrangeCorrelation { fillSignal = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; } + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } } else if constexpr (PartType == kXi) { if constexpr (IsME) { fillSignal = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; @@ -690,6 +838,14 @@ struct PhiStrangeCorrelation { fillSignal = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; } + } else if constexpr (PartType == kOmega) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } } else if constexpr (PartType == kPion) { if constexpr (IsME) { fillSignal = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; @@ -711,157 +867,7 @@ struct PhiStrangeCorrelation { else fillSideband(); }); - - /*static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) { - return; - } - - if constexpr (PartType == kK0S) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiK0S/h5PhiK0SDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiK0S/h5PhiK0SDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } else { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiK0S/h5PhiK0SDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiK0S/h5PhiK0SDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } - } else if constexpr (PartType == kXi) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiXi/h5PhiXiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiXi/h5PhiXiDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } else { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiXi/h5PhiXiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiXi/h5PhiXiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } - } else if constexpr (PartType == kPion) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiPi/h5PhiPiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiPi/h5PhiPiDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } else { - if constexpr (Idx == kSignalRegion) - customFillTHn(HIST("phiPi/h5PhiPiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - else - customFillTHn(HIST("phiPi/h5PhiPiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - } - } - });*/ } - - /*if (analysisMode == kMassvsMass) { - auto fillMass = [&](auto histID, auto assocVal) { - customFillTHn(histID, weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assocVal); - }; - - if constexpr (PartType == kK0S) { - if constexpr (IsME) { - fillMass(HIST("phiK0S/h6PhiK0SDataME"), assoc.m()); - } else { - fillMass(HIST("phiK0S/h6PhiK0SData"), assoc.m()); - } - } else if constexpr (PartType == kXi) { - if constexpr (IsME) { - fillMass(HIST("phiXi/h6PhiXiDataME"), assoc.m()); - } else { - fillMass(HIST("phiXi/h6PhiXiData"), assoc.m()); - } - } else if constexpr (PartType == kPion) { - if constexpr (IsME) { - fillMass(HIST("phiPi/h6PhiPiTPCDataME"), assoc.nSigmaTPC()); - fillMass(HIST("phiPi/h6PhiPiTOFDataME"), assoc.nSigmaTOF()); - } else { - fillMass(HIST("phiPi/h6PhiPiTPCData"), assoc.nSigmaTPC()); - fillMass(HIST("phiPi/h6PhiPiTOFData"), assoc.nSigmaTOF()); - } - } - } else if (analysisMode == kDeltaYvsDeltaPhi) { - static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) { - return; - } - - auto fillDelta = [&](auto histID) { - customFillTHn(histID, weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); - }; - - if constexpr (PartType == kK0S) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiK0S/h5PhiK0SDataMESignal")); - else - fillDelta(HIST("phiK0S/h5PhiK0SDataMESideband")); - } else { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiK0S/h5PhiK0SDataSignal")); - else - fillDelta(HIST("phiK0S/h5PhiK0SDataSideband")); - } - } else if constexpr (PartType == kXi) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiXi/h5PhiXiDataMESignal")); - else - fillDelta(HIST("phiXi/h5PhiXiDataMESideband")); - } else { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiXi/h5PhiXiDataSignal")); - else - fillDelta(HIST("phiXi/h5PhiXiDataSideband")); - } - } else if constexpr (PartType == kPion) { - if constexpr (IsME) { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiPi/h5PhiPiDataMESignal")); - else - fillDelta(HIST("phiPi/h5PhiPiDataMESideband")); - } else { - if constexpr (Idx == kSignalRegion) - fillDelta(HIST("phiPi/h5PhiPiDataSignal")); - else - fillDelta(HIST("phiPi/h5PhiPiDataSideband")); - } - }*/ - - /*if constexpr (PartType == kK0S) { - if constexpr (IsME) { - auto t = std::make_tuple(HIST("phiK0S/h5PhiK0SDataMESignal"), HIST("phiK0S/h5PhiK0SDataMESideband")); - fillDelta(std::get(t)); - } else { - auto t = std::make_tuple(HIST("phiK0S/h5PhiK0SDataSignal"), HIST("phiK0S/h5PhiK0SDataSideband")); - fillDelta(std::get(t)); - } - } else if constexpr (PartType == kXi) { - if constexpr (IsME) { - auto t = std::make_tuple(HIST("phiXi/h5PhiXiDataMESignal"), HIST("phiXi/h5PhiXiDataMESideband")); - fillDelta(std::get(t)); - } else { - auto t = std::make_tuple(HIST("phiXi/h5PhiXiDataSignal"), HIST("phiXi/h5PhiXiDataSideband")); - fillDelta(std::get(t)); - } - } else if constexpr (PartType == kPion) { - if constexpr (IsME) { - auto t = std::make_tuple(HIST("phiPi/h5PhiPiDataMESignal"), HIST("phiPi/h5PhiPiDataMESideband")); - fillDelta(std::get(t)); - } else { - auto t = std::make_tuple(HIST("phiPi/h5PhiPiDataSignal"), HIST("phiPi/h5PhiPiDataSideband")); - fillDelta(std::get(t)); - } - }*/ - //}); - //} } template @@ -892,6 +898,11 @@ struct PhiStrangeCorrelation { { float multiplicity = collision.centFT0M(); + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.perPeriodEfficiency) { + auto bc = collision.template bc_as(); + loadEfficiencyMaps(bc.runNumber()); + } + for (const auto& phiCand : phiCandidates) { if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) { continue; @@ -908,12 +919,19 @@ struct PhiStrangeCorrelation { void processPhiAssocSEDataLike(SelCollisions::iterator const& collision, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced, + aod::LambdaReducedCandidatesData const& lambdaReduced, + aod::AntiLambdaReducedCandidatesData const& antilambdaReduced, aod::XiReducedCandidatesData const& xiReduced, - aod::PionTracksData const& pionTracks) + aod::OmegaReducedCandidatesData const& omegaReduced, + aod::PionTracksData const& pionTracks, + aod::BCs const&) { processPhiAssocSE(collision, phiCandidates, makeAssocInput(k0sReduced), + makeAssocInput(lambdaReduced), + makeAssocInput(antilambdaReduced), makeAssocInput(xiReduced), + makeAssocInput(omegaReduced), makeAssocInput(pionTracks)); } @@ -922,12 +940,19 @@ struct PhiStrangeCorrelation { void processPhiAssocSEMCWithPDG(SimCollisions::iterator const& collision, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, aod::XiReducedCandidatesMcReco const& xiReduced, - aod::PionTracksMcReco const& pionTracks) + aod::OmegaReducedCandidatesMcReco const& omegaReduced, + aod::PionTracksMcReco const& pionTracks, + aod::BCs const&) { processPhiAssocSE(collision, phiCandidates, makeAssocInput(k0sReduced), + makeAssocInput(lambdaReduced), + makeAssocInput(antilambdaReduced), makeAssocInput(xiReduced), + makeAssocInput(omegaReduced), makeAssocInput(pionTracks)); } @@ -942,6 +967,11 @@ struct PhiStrangeCorrelation { for (const auto& [c1, phiCands, c2, assocRed] : pairPhiAssoc) { float multiplicity = c1.centFT0M(); + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.perPeriodEfficiency) { + auto bc = c1.template bc_as(); + loadEfficiencyMaps(bc.runNumber()); + } + for (const auto& [phiCand, assoc] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, assocRed))) { if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) { continue; @@ -954,7 +984,8 @@ struct PhiStrangeCorrelation { void processPhiK0SMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, - aod::K0sReducedCandidatesData const& k0sReduced) + aod::K0sReducedCandidatesData const& k0sReduced, + aod::BCs const&) { processPhiAssocME(collisions, phiCandidates, k0sReduced); } @@ -963,465 +994,124 @@ struct PhiStrangeCorrelation { void processPhiK0SMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, - aod::K0sReducedCandidatesMcReco const& k0sReduced) + aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::BCs const&) { processPhiAssocME(collisions, phiCandidates, k0sReduced); } PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SMEMCWithPDG, "Process function for Phi-K0S 2D Correlations in MC with PDG ME", true); - void processPhiXiMEDataLike(SelCollisions const& collisions, - aod::PhimesonCandidatesData const& phiCandidates, - aod::XiReducedCandidatesData const& xiReduced) + void processPhiLambdaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::LambdaReducedCandidatesData const& lambdaReduced, + aod::BCs const&) { - processPhiAssocME(collisions, phiCandidates, xiReduced); + processPhiAssocME(collisions, phiCandidates, lambdaReduced); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEDataLike, "Process function for Phi-Xi 2D Correlations in Data or MC w/o PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiLambdaMEDataLike, "Process function for Phi-Lambda 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiXiMEMCWithPDG(SimCollisions const& collisions, - aod::PhimesonCandidatesMcReco const& phiCandidates, - aod::XiReducedCandidatesMcReco const& xiReduced) + void processPhiLambdaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::BCs const&) { - processPhiAssocME(collisions, phiCandidates, xiReduced); + processPhiAssocME(collisions, phiCandidates, lambdaReduced); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEMCWithPDG, "Process function for Phi-Xi 2D Correlations in MC with PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiLambdaMEMCWithPDG, "Process function for Phi-Lambda 2D Correlations in MC with PDG ME", true); - void processPhiPionMEDataLike(SelCollisions const& collisions, - aod::PhimesonCandidatesData const& phiCandidates, - aod::PionTracksData const& pionTracks) + void processPhiAntiLambdaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::AntiLambdaReducedCandidatesData const& antilambdaReduced, + aod::BCs const&) { - processPhiAssocME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, antilambdaReduced); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEDataLike, "Process function for Phi-Pion 2D Correlations in Data or MC w/o PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAntiLambdaMEDataLike, "Process function for Phi-AntiLambda 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiPionMEMCWithPDG(SimCollisions const& collisions, - aod::PhimesonCandidatesMcReco const& phiCandidates, - aod::PionTracksMcReco const& pionTracks) + void processPhiAntiLambdaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, + aod::BCs const&) { - processPhiAssocME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, antilambdaReduced); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true); - - /*template - void processPhiAssocSE(TCollision const& collision, TPhiCands const& phiCandidates, TK0SCands const& k0sReduced, TXiCands const& xiReduced, TPionCands const& pionTracks) - { - float multiplicity = collision.centFT0M(); - - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - auto isK0sValid = [&](const auto& k0s) { - return (!efficiencyConfigs.applyEfficiency || k0s.pt() < binspTK0S->back()) && (!applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s)); - }; - - const bool applyXiMassCut = (analysisMode == kDeltaYvsDeltaPhi) && xiConfigs.selectXiInSigRegion; - const auto& [minMassXi, maxMassXi] = xiConfigs.rangeMXiSignal.value; - auto isXiValid = [&](const auto& xi) { - return (!efficiencyConfigs.applyEfficiency || xi.pt() < binspTXi->back()) && (!applyXiMassCut || xi.inMassRegion(minMassXi, maxMassXi)); - }; - - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; - - auto isPionValid = [&](const auto& pion) { - return (!efficiencyConfigs.applyEfficiency || pion.pt() < binspTPi->back()) && (!applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax)); - }; - - for (const auto& phiCand : phiCandidates) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - - auto weightPhi = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y())); - - // histos.fill(HIST("phi/h3PhiData"), multiplicity, phiCand.pt(), phiCand.m(), weightPhi); - customFillHist(HIST("phi/h3PhiData"), weightPhi, multiplicity, phiCand.pt(), phiCand.m()); - - auto processCorrelations = [&](auto fillK0S, auto fillXi, auto fillPion) { - if (activeCorrelationTypes->at(kK0S)) { - // Loop over all reduced K0S candidates - for (const auto& k0s : k0sReduced) { - if (!isK0sValid(k0s)) - continue; - - // auto weightPhiK0S = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kK0S], multiplicity, k0s.pt(), k0s.y())); - - fillK0S(k0s, computeAssocWeight(multiplicity, phiCand, k0s)); - } - } - - if (activeCorrelationTypes->at(kXi)) { - // Loop over all reduced Xi candidates - for (const auto& xi : xiReduced) { - if (!isXiValid(xi)) - continue; - - // auto weightPhiXi = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kXi], multiplicity, xi.pt(), xi.y())); - - fillXi(xi, computeAssocWeight(multiplicity, phiCand, xi)); - } - } - - if (activeCorrelationTypes->at(kPion)) { - // Loop over all primary pion candidates - for (const auto& pionTrack : pionTracks) { - if (!isPionValid(pionTrack)) - continue; - - // auto weightPhiPion = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kPion], multiplicity, pionTrack.pt(), pionTrack.y())); - - fillPion(pionTrack, computeAssocWeight(multiplicity, phiCand, pionTrack)); - } - } - }; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAntiLambdaMEMCWithPDG, "Process function for Phi-AntiLambda 2D Correlations in MC with PDG ME", true); - if (analysisMode == kMassvsMass) { - auto k0sHistID = HIST("phiK0S/h6PhiK0SData"); - auto xiHistID = HIST("phiXi/h6PhiXiData"); - auto piTPCHistID = HIST("phiPi/h6PhiPiTPCData"); - auto piTOFHistID = HIST("phiPi/h6PhiPiTOFData"); - - processCorrelations( - //[&](const auto& k0s, float w) { - // histos.fill(k0sHistID, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m(), w); - [&](const auto& k0s, const std::pair& w) { - customFillTHn(k0sHistID, w, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m()); - }, - //[&](const auto& xi, float w) { - [&](const auto& xi, const std::pair& w) { - customFillTHn(xiHistID, w, multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), phiCand.m(), xi.m()); - }, - //[&](const auto& pion, float w) { - // histos.fill(piTPCHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC(), w); - // histos.fill(piTOFHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF(), w); - [&](const auto& pion, const std::pair& w) { - customFillTHn(piTPCHistID, w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC()); - customFillTHn(piTOFHistID, w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF()); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto k0sHistID = std::make_tuple(HIST("phiK0S/h5PhiK0SDataSignal"), HIST("phiK0S/h5PhiK0SDataSideband")); - auto xiHistID = std::make_tuple(HIST("phiXi/h5PhiXiDataSignal"), HIST("phiXi/h5PhiXiDataSideband")); - auto piHistID = std::make_tuple(HIST("phiPi/h5PhiPiDataSignal"), HIST("phiPi/h5PhiPiDataSideband")); - - static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; - - // auto k0sHistID = HIST("phiK0S/h5PhiK0SData") + HIST(PhiMassRegionLabels[Idx]); - // auto piHistID = HIST("phiPi/h5PhiPiData") + HIST(PhiMassRegionLabels[Idx]); - - processCorrelations( - //[&](const auto& k0s, float w) { - // histos.fill(std::get(k0sHistID), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), w); - [&](const auto& k0s, const std::pair& w) { - customFillTHn(std::get(k0sHistID), w, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi())); - }, - //[&](const auto& xi, float w) { - // histos.fill(std::get(xiHistID), multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), getDeltaPhi(phiCand.phi(), xi.phi()), w); - [&](const auto& xi, const std::pair& w) { - customFillTHn(std::get(xiHistID), w, multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), getDeltaPhi(phiCand.phi(), xi.phi())); - }, - //[&](const auto& pion, float w) { - // histos.fill(std::get(piHistID), multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi()), w); - [&](const auto& pion, const std::pair& w) { - customFillTHn(std::get(piHistID), w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi())); - }); - }); - } - } - } - - void processPhiAssocSEDataLike(SelCollisions::iterator const& collision, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced, aod::XiReducedCandidatesData const& xiReduced, aod::PionTracksData const& pionTracks) - { - processPhiAssocSE(collision, phiCandidates, k0sReduced, xiReduced, pionTracks); - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAssocSEDataLike, "Process function for Phi-Associated 2D Correlations in Data or MC w/o PDG SE", true); - - void processPhiAssocSEMCWithPDG(SimCollisions::iterator const& collision, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced, aod::XiReducedCandidatesMcReco const& xiReduced, aod::PionTracksMcReco const& pionTracks) - { - processPhiAssocSE(collision, phiCandidates, k0sReduced, xiReduced, pionTracks); - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAssocSEMCWithPDG, "Process function for Phi-Associated 2D Correlations in MC with PDG SE", true); - - template - void processPhiK0SME(TCollisions const& collisions, TPhiCands const& phiCandidates, TK0SCands const& k0sReduced) - { - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - - auto isK0sValid = [&](const auto& k0s) { - return (!efficiencyConfigs.applyEfficiency || k0s.pt() < binspTK0S->back()) && (!applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s)); - }; - - auto tuplePhiK0S = std::make_tuple(phiCandidates, k0sReduced); - Pair pairPhiK0S{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiK0S, &cache}; - - for (const auto& [c1, phiCands, c2, k0sRed] : pairPhiK0S) { - - float multiplicity = c1.centFT0M(); - - for (const auto& [phiCand, k0s] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, k0sRed))) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - if (!isK0sValid(k0s)) - continue; - - auto processCorrelations = [&](auto fillK0S) { - // auto weightPhiK0S = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kK0S], multiplicity, k0s.pt(), k0s.y())); - fillK0S(k0s, computeAssocWeight(multiplicity, phiCand, k0s)); - }; - - if (analysisMode == kMassvsMass) { - auto k0sHistID = HIST("phiK0S/h6PhiK0SDataME"); - - processCorrelations( - //[&](const auto& k0s, float w) { - // histos.fill(k0sHistID, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m(), w); - [&](const auto& k0s, const std::pair& w) { - customFillTHn(k0sHistID, w, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m()); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto k0sHistID = std::make_tuple(HIST("phiK0S/h5PhiK0SDataMESignal"), HIST("phiK0S/h5PhiK0SDataMESideband")); - - static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; - - processCorrelations( - //[&](const auto& k0s, float w) { - // histos.fill(std::get(k0sHistID), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), w); - [&](const auto& k0s, const std::pair& w) { - customFillTHn(std::get(k0sHistID), w, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi())); - }); - }); - } - } - } - } - - // PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SME, "Process function for Phi-K0S and Deltay and Deltaphi 2D Correlations in Data ME", false); - - void processPhiK0SMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced) - { - processPhiK0SME(collisions, phiCandidates, k0sReduced); - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SMEDataLike, "Process function for Phi-K0S 2D Correlations in Data or MC w/o PDG ME", true); - - void processPhiK0SMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced) - { - processPhiK0SME(collisions, phiCandidates, k0sReduced); - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SMEMCWithPDG, "Process function for Phi-K0S 2D Correlations in MC with PDG ME", true); - - template - void processPhiXiME(TCollisions const& collisions, TPhiCands const& phiCandidates, TXiCands const& xiReduced) - { - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyXiMassCut = (analysisMode == kDeltaYvsDeltaPhi) && xiConfigs.selectXiInSigRegion; - const auto& [minMassXi, maxMassXi] = xiConfigs.rangeMXiSignal.value; - - auto isXiValid = [&](const auto& xi) { - return (!efficiencyConfigs.applyEfficiency || xi.pt() < binspTXi->back()) && (!applyXiMassCut || xi.inMassRegion(minMassXi, maxMassXi)); - }; - - auto tuplePhiXi = std::make_tuple(phiCandidates, xiReduced); - Pair pairPhiXi{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiXi, &cache}; - - for (const auto& [c1, phiCands, c2, xiRed] : pairPhiXi) { - - float multiplicity = c1.centFT0M(); - - for (const auto& [phiCand, xi] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, xiRed))) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - if (!isXiValid(xi)) - continue; - - auto processCorrelations = [&](auto fillXi) { - // auto weightPhiXi = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kXi], multiplicity, xi.pt(), xi.y())); - fillXi(xi, computeAssocWeight(multiplicity, phiCand, xi)); - }; - - if (analysisMode == kMassvsMass) { - auto xiHistID = HIST("phiXi/h6PhiXiDataME"); - - processCorrelations( - //[&](const auto& xi, float w) { - // histos.fill(xiHistID, multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), phiCand.m(), xi.m(), w); - [&](const auto& xi, const std::pair& w) { - customFillTHn(xiHistID, w, multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), phiCand.m(), xi.m()); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto xiHistID = std::make_tuple(HIST("phiXi/h5PhiXiDataMESignal"), HIST("phiXi/h5PhiXiDataMESideband")); - - static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; - - processCorrelations( - //[&](const auto& xi, float w) { - // histos.fill(std::get(xiHistID), multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), getDeltaPhi(phiCand.phi(), xi.phi()), w); - [&](const auto& xi, const std::pair& w) { - customFillTHn(std::get(xiHistID), w, multiplicity, phiCand.pt(), xi.pt(), phiCand.y() - xi.y(), getDeltaPhi(phiCand.phi(), xi.phi())); - }); - }); - } - } - } - } - - void processPhiXiMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::XiReducedCandidatesData const& xiReduced) + void processPhiXiMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::XiReducedCandidatesData const& xiReduced, + aod::BCs const&) { - processPhiXiME(collisions, phiCandidates, xiReduced); + processPhiAssocME(collisions, phiCandidates, xiReduced); } PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEDataLike, "Process function for Phi-Xi 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiXiMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::XiReducedCandidatesMcReco const& xiReduced) + void processPhiXiMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::XiReducedCandidatesMcReco const& xiReduced, + aod::BCs const&) { - processPhiXiME(collisions, phiCandidates, xiReduced); + processPhiAssocME(collisions, phiCandidates, xiReduced); } PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEMCWithPDG, "Process function for Phi-Xi 2D Correlations in MC with PDG ME", true); - template - void processPhiPionME(TCollisions const& collisions, TPhiCands const& phiCandidates, TPionCands const& pionTracks) + void processPhiOmegaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::OmegaReducedCandidatesData const& omegaReduced, + aod::BCs const&) { - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; - - auto isPionValid = [&](const auto& pion) { - return (!efficiencyConfigs.applyEfficiency || pion.pt() < binspTPi->back()) && (!applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax)); - }; - - auto tuplePhiPion = std::make_tuple(phiCandidates, pionTracks); - Pair pairPhiPion{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiPion, &cache}; - - for (const auto& [c1, phiCands, c2, piTracks] : pairPhiPion) { - - float multiplicity = c1.centFT0M(); - - for (const auto& [phiCand, piTrack] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, piTracks))) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - if (!isPionValid(piTrack)) - continue; - - auto processCorrelations = [&](auto fillPion) { - // auto weightPhiPion = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - // BoundEfficiencyMap(effMapsAssoc[kPion], multiplicity, piTrack.pt(), piTrack.y())); - fillPion(piTrack, computeAssocWeight(multiplicity, phiCand, piTrack)); - }; + processPhiAssocME(collisions, phiCandidates, omegaReduced); + } - if (analysisMode == kMassvsMass) { - auto piTPCHistID = HIST("phiPi/h6PhiPiTPCDataME"); - auto piTOFHistID = HIST("phiPi/h6PhiPiTOFDataME"); - - processCorrelations( - //[&](const auto& pion, float w) { - // histos.fill(piTPCHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC(), w); - // histos.fill(piTOFHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF(), w); - [&](const auto& pion, const std::pair& w) { - customFillTHn(piTPCHistID, w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC()); - customFillTHn(piTOFHistID, w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF()); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto piHistID = std::make_tuple(HIST("phiPi/h5PhiPiDataMESignal"), HIST("phiPi/h5PhiPiDataMESideband")); - - static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiOmegaMEDataLike, "Process function for Phi-Omega 2D Correlations in Data or MC w/o PDG ME", true); - processCorrelations( - //[&](const auto& pion, float w) { - // histos.fill(std::get(piHistID), multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi()), w); - [&](const auto& pion, const std::pair& w) { - customFillTHn(std::get(piHistID), w, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi())); - }); - }); - } - } - } + void processPhiOmegaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::OmegaReducedCandidatesMcReco const& omegaReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, omegaReduced); } - // PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionME, "Process function for Phi-Pion Deltay and Deltaphi 2D Correlations in Data ME", false); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiOmegaMEMCWithPDG, "Process function for Phi-Omega 2D Correlations in MC with PDG ME", true); - void processPhiPionMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::PionTracksData const& pionTracks) + void processPhiPionMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::PionTracksData const& pionTracks, + aod::BCs const&) { - processPhiPionME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, pionTracks); } PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEDataLike, "Process function for Phi-Pion 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiPionMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::PionTracksMcReco const& pionTracks) + void processPhiPionMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::PionTracksMcReco const& pionTracks, + aod::BCs const&) { - processPhiPionME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, pionTracks); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true);*/ + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true); void processParticleEfficiency(MCCollisions const& mcCollisions, SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, aod::XiReducedCandidatesMcReco const& xiReduced, + aod::OmegaReducedCandidatesMcReco const& omegaReduced, aod::PionTracksMcReco const& pionTracks, aod::McParticles const& mcParticles) { - /*const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - auto isK0sValid = [&](const auto& k0s) { - return !applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s); - }; - - const bool applyXiMassCut = (analysisMode == kDeltaYvsDeltaPhi) && xiConfigs.selectXiInSigRegion; - const auto& [minMassXi, maxMassXi] = xiConfigs.rangeMXiSignal.value; - auto isXiValid = [&](const auto& xi) { - return !applyXiMassCut || xi.inMassRegion(minMassXi, maxMassXi); - }; - - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; - - auto isPionValid = [&](const auto& pion) { - return !applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax); - };*/ - std::unordered_map> collsGrouped; collsGrouped.reserve(mcCollisions.size()); @@ -1477,34 +1167,12 @@ struct PhiStrangeCorrelation { }; fillRecoAssocSpecies(makeAssocInput(k0sReduced), HIST("k0s/h4K0SMCReco")); + fillRecoAssocSpecies(makeAssocInput(lambdaReduced), HIST("lambda/h4LambdaMCReco")); + fillRecoAssocSpecies(makeAssocInput(antilambdaReduced), HIST("antilambda/h4AntiLambdaMCReco")); fillRecoAssocSpecies(makeAssocInput(xiReduced), HIST("xi/h4XiMCReco")); + fillRecoAssocSpecies(makeAssocInput(omegaReduced), HIST("omega/h4OmegaMCReco")); fillRecoAssocSpecies(makeAssocInput(pionTracks), HIST("pi/h4PiMCReco")); - /*const auto k0sThisColl = k0sReduced.sliceBy(preslices.k0sMcRecoPerCollision, collision.globalIndex()); - const auto xiThisColl = xiReduced.sliceBy(preslices.xiMcRecoPerCollision, collision.globalIndex()); - const auto pionTracksThisColl = pionTracks.sliceBy(preslices.pionTrackMcRecoPerCollision, collision.globalIndex()); - - for (const auto& k0s : k0sThisColl) { - if (!isAssocValid(k0s)) - continue; - - histos.fill(HIST("k0s/h4K0SMCReco"), collision.posZ(), mcCollision.centFT0M(), k0s.pt(), k0s.y()); - } - - for (const auto& xi : xiThisColl) { - if (!isAssocValid(xi)) - continue; - - histos.fill(HIST("xi/h4XiMCReco"), collision.posZ(), mcCollision.centFT0M(), xi.pt(), xi.y()); - } - - for (const auto& pionTrack : pionTracksThisColl) { - if (!isAssocValid(pionTrack)) - continue; - - histos.fill(HIST("pi/h4PiMCReco"), collision.posZ(), mcCollision.centFT0M(), pionTrack.pt(), pionTrack.y()); - }*/ - numberAssocColls++; } } @@ -1572,9 +1240,18 @@ struct PhiStrangeCorrelation { case getPdgCode(): fillGenAssocSpecies.template operator()(HIST("k0s/h3K0SMCGen"), HIST("k0s/h4K0SMCGenAssocReco")); break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("lambda/h3LambdaMCGen"), HIST("lambda/h4LambdaMCGenAssocReco")); + break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("antilambda/h3AntiLambdaMCGen"), HIST("antilambda/h4AntiLambdaMCGenAssocReco")); + break; case getPdgCode(): fillGenAssocSpecies.template operator()(HIST("xi/h3XiMCGen"), HIST("xi/h4XiMCGenAssocReco")); break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("omega/h3OmegaMCGen"), HIST("omega/h4OmegaMCGenAssocReco")); + break; case getPdgCode(): fillGenAssocSpecies.template operator()(HIST("pi/h3PiMCGen"), HIST("pi/h4PiMCGenAssocReco")); break; @@ -1713,7 +1390,10 @@ struct PhiStrangeCorrelation { std::vector phiParticles; std::vector k0sParticles; + std::vector lambdaParticles; + std::vector antilambdaParticles; std::vector xiParticles; + std::vector omegaParticles; std::vector pionParticles; // Preliminary loop to fill vectors of particles of interest for the current event, applying pt and y cuts @@ -1755,9 +1435,18 @@ struct PhiStrangeCorrelation { case getPdgCode(): fillPartCollection.template operator()(k0sParticles); break; + case getPdgCode(): + fillPartCollection.template operator()(lambdaParticles); + break; + case getPdgCode(): + fillPartCollection.template operator()(antilambdaParticles); + break; case getPdgCode(): fillPartCollection.template operator()(xiParticles); break; + case getPdgCode(): + fillPartCollection.template operator()(omegaParticles); + break; case getPdgCode(): fillPartCollection.template operator()(pionParticles); break; @@ -1767,10 +1456,13 @@ struct PhiStrangeCorrelation { } const bool skipK0s = !activeCorrelationTypes->at(kK0S) || k0sParticles.empty(); + const bool skipLambda = !activeCorrelationTypes->at(kLambda) || lambdaParticles.empty(); + const bool skipAntiLambda = !activeCorrelationTypes->at(kAntiLambda) || antilambdaParticles.empty(); const bool skipXi = !activeCorrelationTypes->at(kXi) || xiParticles.empty(); + const bool skipOmega = !activeCorrelationTypes->at(kOmega) || omegaParticles.empty(); const bool skipPion = !activeCorrelationTypes->at(kPion) || pionParticles.empty(); - if (phiParticles.empty() && skipK0s && skipXi && skipPion) { + if (phiParticles.empty() && skipK0s && skipLambda && skipAntiLambda && skipXi && skipOmega && skipPion) { return; } @@ -1780,7 +1472,8 @@ struct PhiStrangeCorrelation { } // Same Event Correlations - std::array*, kAssocPartSize> currentAssocParticles{&k0sParticles, &xiParticles, &pionParticles}; + std::array*, kAssocPartSize> currentAssocParticles{&k0sParticles, &lambdaParticles, &antilambdaParticles, + &xiParticles, &omegaParticles, &pionParticles}; for (const auto& phiParticle : phiParticles) { histos.fill(HIST("phi/h3PhiMCClosureGen"), multiplicity, phiParticle.pt, phiParticle.y); @@ -1802,7 +1495,8 @@ struct PhiStrangeCorrelation { // Mixed Event Correlations for (const auto& pastEvent : eventBuffer[multBin]) { - std::array*, kAssocPartSize> pastAssocParticles{&pastEvent.k0sParticles, &pastEvent.xiParticles, &pastEvent.pionParticles}; + std::array*, kAssocPartSize> pastAssocParticles{&pastEvent.k0sParticles, &pastEvent.lambdaParticles, &pastEvent.antilambdaParticles, + &pastEvent.xiParticles, &pastEvent.omegaParticles, &pastEvent.pionParticles}; // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past associated particles for (const auto& phiParticle : phiParticles) { @@ -1826,7 +1520,10 @@ struct PhiStrangeCorrelation { currentEvent.multiplicity = multiplicity; currentEvent.phiParticles = std::move(phiParticles); currentEvent.k0sParticles = std::move(k0sParticles); + currentEvent.lambdaParticles = std::move(lambdaParticles); + currentEvent.antilambdaParticles = std::move(antilambdaParticles); currentEvent.xiParticles = std::move(xiParticles); + currentEvent.omegaParticles = std::move(omegaParticles); currentEvent.pionParticles = std::move(pionParticles); eventBuffer[multBin].push_front(std::move(currentEvent)); From a7f7ac103f2fbaa8fd0e9d8753c80c118ea1aec5 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Wed, 23 Sep 2026 23:09:26 +0200 Subject: [PATCH 053/648] [PWGLF,PWGMM] Fix histo dimension + add DCA MC and QA data processes (#18031) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 704 ++++++++++++++++++++++------- 1 file changed, 540 insertions(+), 164 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index c5c51422118..7b8ec127ee5 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -23,7 +23,6 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/Centrality.h" -#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" @@ -49,6 +48,7 @@ #include #include #include +#include #include #include @@ -57,12 +57,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include using namespace o2; @@ -77,7 +79,9 @@ using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; auto static constexpr CintZero = 0; +auto static constexpr CintOne = 1; auto static constexpr CfloatFive = 5.f; +auto static constexpr CInvalid = -999.f; auto static constexpr CminAccFT0A = 3.5f; auto static constexpr CmaxAccFT0A = 4.9f; auto static constexpr CminAccFT0C = -3.3f; @@ -195,11 +199,16 @@ enum class McTrackStatus { nMcTrackStatus }; +enum class OccupancyEst { + TrkITS = 1, + Ft0C +}; + struct DndetaMFTPbPb { SliceCache cache; - enum OccupancyEst { TrkITS = 1, - Ft0C }; + Preslice perMCCol = aod::mcparticle::mcCollisionId; + PresliceUnsorted perColBestTrks = aod::fwdtrack::bestCollisionId; HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -236,8 +245,8 @@ struct DndetaMFTPbPb { ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; ConfigurableAxis zvtxBins{"zvtxBins", {60, -30., 30.}, "Z-vtx binning (cm)"}; ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; - ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -1., 1.}, "DCAxy binning (cm)"}; - ConfigurableAxis dcaZBins{"dcaZBins", {800, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -2., 2.}, "DCAxy binning (cm)"}; + ConfigurableAxis dcaZBins{"dcaZBins", {800, -2., 2.}, "DCAz binning (cm)"}; ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; ConfigurableAxis chiSqPerNclBins{"chiSqPerNclBins", {100, 0, 100}, "#chi^{2} binning"}; @@ -261,15 +270,17 @@ struct DndetaMFTPbPb { Configurable maxPhi{"maxPhi", 6.2832, ""}; Configurable minEta{"minEta", -3.6f, ""}; Configurable maxEta{"maxEta", -2.5f, ""}; + Configurable minEtaGenMult{"minEtaGenMult", -0.5, "Min eta for generated mid-rapidity multiplicity"}; + Configurable maxEtaGenMult{"maxEtaGenMult", 0.5, "Max eta for generated mid-rapidity multiplicity"}; Configurable minNclusterMft{"minNclusterMft", 5, "minimum number of MFT clusters"}; Configurable useChi2Cut{"useChi2Cut", true, "use track chi2 cut"}; Configurable maxChi2NCl{"maxChi2NCl", 40.0f, "maximum chi2 per MFT clusters"}; Configurable usePtCut{"usePtCut", false, "use track pT cut"}; Configurable minPt{"minPt", 0., "minimum pT of the MFT tracks"}; Configurable requireCA{"requireCA", false, "Use Cellular Automaton track-finding algorithm"}; - Configurable maxDCAxy{"maxDCAxy", 1.0f, "Cut on dca XY"}; + Configurable maxDCAxy{"maxDCAxy", 2.0f, "Cut on dca XY"}; Configurable useDCAzCut{"useDCAzCut", true, "use dca Z cut"}; - Configurable maxDCAz{"maxDCAz", 1.0f, "Cut on dca Z"}; + Configurable maxDCAz{"maxDCAz", 2.0f, "Cut on dca Z"}; Configurable selMcMask{"selMcMask", 0, "McMask for correct match"}; } trackCuts; @@ -342,11 +353,11 @@ struct DndetaMFTPbPb { const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; const AxisSpec invQPtAxis{binOpt.invQPtBins, "q/p_{T} (1/GeV)"}; - const AxisSpec genEvtTypeAxis = {static_cast(GenTrkType::nGenTrkType) - 1, +0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; - const AxisSpec recEvtTypeAxis = {static_cast(RecTrkType::nRecTrkType) - 1, +0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; - const AxisSpec genEvtIdxTypeAxis = {static_cast(GenTrkType::nGenTrkType) - 1, +0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; - const AxisSpec recEvtIdxTypeAxis = {static_cast(RecTrkType::nRecTrkType) - 1, +0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; - const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType) - 1, +0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; + const AxisSpec genEvtTypeAxis = {static_cast(GenTrkType::nGenTrkType), -0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; + const AxisSpec recEvtTypeAxis = {static_cast(RecTrkType::nRecTrkType), -0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; + const AxisSpec genEvtIdxTypeAxis = {static_cast(GenIdxTrkType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Type axis"}; + const AxisSpec recEvtIdxTypeAxis = {static_cast(RecIdxTrkType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Type axis"}; + const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType), -0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -362,23 +373,23 @@ struct DndetaMFTPbPb { if (static_cast(doprocessDataCentFT0C) + static_cast(doprocessDatawBestTracksCentFT0C) > 1) { LOGP(fatal, "Either processDataCent[ESTIMATOR] OR processDatawBestTracksCent[ESTIMATOR] should be enabled!"); } - if (static_cast(doprocessMCInclusive) + static_cast(doprocessMCbestInclusive) > 1) { - LOGP(fatal, "Either processMCInclusive OR processMCbestInclusive should be enabled!"); + if (static_cast(doprocessMcInclusive) + static_cast(doprocessMcBestInclusive) > 1) { + LOGP(fatal, "Either processMcInclusive OR processMcBestInclusive should be enabled!"); } - if (static_cast(doprocessMCCentFT0C) + static_cast(doprocessMCbestCentFT0C) > 1) { + if (static_cast(doprocessMcCentFT0C) + static_cast(doprocessMcBestCentFT0C) > 1) { LOGP(fatal, "Either processMCCent[ESTIMATOR] OR processMCbestCent[ESTIMATOR] should be enabled!"); } - if (static_cast(doprocessEfficiencyInclusive) + static_cast(doprocessEfficiencyBestInclusive) > 1) { - LOGP(fatal, "Either doprocessEfficiencyInclusive OR doprocessEfficiencyBestInclusive should be enabled!"); + if (static_cast(doprocessMcEfficiencyInclusive) + static_cast(doprocessMcEfficiencyBestInclusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyInclusive OR doprocessMcEfficiencyBestInclusive should be enabled!"); } - if (static_cast(doprocessEfficiencyCentFT0C) + static_cast(doprocessEfficiencyBestCentFT0C) > 1) { - LOGP(fatal, "Either doprocessEfficiencyCentFT0C OR doprocessEfficiencyBestCentFT0C should be enabled!"); + if (static_cast(doprocessMcEfficiencyCentFT0C) + static_cast(doprocessMcEfficiencyBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyCentFT0C OR doprocessMcEfficiencyBestCentFT0C should be enabled!"); } - if (static_cast(doprocessEfficiencyIdxInlusive) + static_cast(doprocessEfficiencyIdxBestInlusive) > 1) { - LOGP(fatal, "Either doprocessEfficiencyIdxInlusive OR doprocessEfficiencyIdxBestInlusive should be enabled!"); + if (static_cast(doprocessMcEfficiencyIdxInlusive) + static_cast(doprocessMcEfficiencyIdxBestInlusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxInlusive OR doprocessMcEfficiencyIdxBestInlusive should be enabled!"); } - if (static_cast(doprocessEfficiencyIdxCentFT0C) + static_cast(doprocessEfficiencyIdxBestCentFT0C) > 1) { - LOGP(fatal, "Either doprocessEfficiencyIdxCentFT0C OR doprocessEfficiencyIdxBestCentFT0C should be enabled!"); + if (static_cast(doprocessMcEfficiencyIdxCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxCentFT0C OR doprocessMcEfficiencyIdxBestCentFT0C should be enabled!"); } // General counters - QC @@ -425,9 +436,10 @@ struct DndetaMFTPbPb { } if (doprocessDatawBestTracksInclusive || doprocessDatawBestTracksCentFT0C || - doprocessMCbestInclusive || doprocessMCbestCentFT0C || - doprocessEfficiencyBestInclusive || doprocessEfficiencyBestCentFT0C || - doprocessEfficiencyIdxBestInlusive || doprocessEfficiencyIdxBestCentFT0C) { + doprocessMcBestInclusive || doprocessMcBestCentFT0C || + doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyBestCentFT0C || + doprocessMcEfficiencyIdxBestInlusive || doprocessMcEfficiencyIdxBestCentFT0C || + doprocessDataCorrelationwBestTracksInclusive) { registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ "All", @@ -475,9 +487,9 @@ struct DndetaMFTPbPb { if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Events/Centrality/Selection", ";status; centrality; occupancy", {HistType::kTHnSparseF, {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); - auto hstat = registryData.get(HIST("Events/Centrality/Selection")); - hstat->GetXaxis()->SetBinLabel(1, "All"); - hstat->GetXaxis()->SetBinLabel(2, "Selected"); + auto hstat = registryData.get(HIST("Events/Centrality/Selection")); + hstat->GetAxis(0)->SetBinLabel(1, "All"); + hstat->GetAxis(0)->SetBinLabel(2, "Selected"); registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); @@ -503,7 +515,14 @@ struct DndetaMFTPbPb { registryData.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); } } - if (doprocessMCInclusive || doprocessMCbestInclusive) { + if (doprocessDataCorrelationwBestTracksInclusive) { + registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); + registryData.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); + registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); + registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); + registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); + } + if (doprocessMcInclusive || doprocessMcBestInclusive) { registryMC.add("Events/McStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, occupancyAxis}}); std::array(McStatus::nMcStatus)> labelMcStatus{ "Rec all", @@ -514,7 +533,7 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { registryMC.get(HIST("Events/McStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcStatus[iBin].data()); } - if (doprocessMCbestInclusive) { + if (doprocessMcBestInclusive) { registryMC.add({"Tracks/hMcTrackStatus", "Number of tracks; Cut; occupancy", {HistType::kTH2F, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, occupancyAxis}}}); std::array(McTrackStatus::nMcTrackStatus)> labelMcTrackStatus{ "Best all", @@ -538,7 +557,7 @@ struct DndetaMFTPbPb { registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); } - if (doprocessMCCentFT0C || doprocessMCbestCentFT0C) { + if (doprocessMcCentFT0C || doprocessMcBestCentFT0C) { registryMC.add("Events/Centrality/McStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, centralityAxis, occupancyAxis}}); std::array(McStatus::nMcStatus)> labelMcStatusCent{ "Rec all", @@ -549,7 +568,7 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { registryMC.get(HIST("Events/Centrality/McStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCent[iBin].data()); } - if (doprocessMCbestCentFT0C) { + if (doprocessMcBestCentFT0C) { registryMC.add({"Tracks/Centrality/hMcTrackStatus", "Number of tracks; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, centralityAxis, occupancyAxis}}}); std::array(McTrackStatus::nMcTrackStatus)> labelMcStatusCentBest{ "Best all", @@ -576,7 +595,7 @@ struct DndetaMFTPbPb { registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); } - if (doprocessEfficiencyInclusive || doprocessEfficiencyBestInclusive) { + if (doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive) { registryMC.add("Events/hMcEffStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, occupancyAxis}}); std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatus{ "All", @@ -592,7 +611,7 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); } - if (doprocessEfficiencyCentFT0C || doprocessEfficiencyBestCentFT0C) { + if (doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C) { registryMC.add("Events/Centrality/hMcEffStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, centralityAxis, occupancyAxis}}); std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatusCent{ "All", @@ -607,15 +626,15 @@ struct DndetaMFTPbPb { registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); } - if (doprocessEfficiencyIdxInlusive || doprocessEfficiencyIdxBestInlusive) { - registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {multAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxBestInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {{10, 0.5, 10.5}, occupancyAxis}}}); } - if (doprocessEfficiencyIdxCentFT0C || doprocessEfficiencyIdxBestCentFT0C) { - registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTH2F, {multAxis, centralityAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyIdxCentFT0C || doprocessMcEfficiencyIdxBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTHnSparseF, {{10, 0.5, 10.5}, centralityAxis, occupancyAxis}}}); } if (doprocessMcSgnEvtLossCentFT0C) { registryMC.add("Events/hEvtMcGen", "Events/hEvtMcGen", {HistType::kTH1F, {{4, 0.f, 4.f}}}); @@ -624,7 +643,7 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(3, "isInelGt0wMft"); registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(4, "TVX"); // - registryMC.add("Events/EvtSigLossStatus", ";status;centrality", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + registryMC.add("Events/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); auto hstat = registryMC.get(HIST("Events/EvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); @@ -642,12 +661,52 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/hEtaVsNchGen", "; #eta; mult gen", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); registryMC.add({"Tracks/hEtaVsNchGenRecEvt", "; #eta; mult gen w/ Rec evt", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); } - if (doprocessCorrelationwBestTracksInclusive) { - registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); - registryData.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); - registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); - registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); - registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); + if (doprocessMcReassocDCA) { + registryMC.add({"Events/Centrality/EvtGenRecReassoc", ";status;centrality", {HistType::kTHnSparseF, {{3, 0.5, 3.5}, centralityAxis}}}); + auto heff = registryMC.get(HIST("Events/Centrality/EvtGenRecReassoc")); + heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); + heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); + heff->GetAxis(0)->SetBinLabel(3, "Remove split vertices"); + + registryMC.add("Events/hCentBest", "; centrality", HistType::kTH1F, {centralityAxis}); + registryMC.add("Events/hGenMult", "Sel rec evt. vs generated mult; mult", HistType::kTH1F, {multAxis}); + registryMC.add("Events/hCentRecVsGenMult", "Rec cent vs gen mid-rapidity multiplicity; centrality; mult", HistType::kTH2F, {centralityAxis, multAxis}); + registryMC.add("Events/hCentRecFromGenMult", "Centrality from generated mult", HistType::kTH1F, {centralityAxis}); + + registryMC.add({"Tracks/Centrality/THnDCAxyBestRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestRecFake", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeak", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMat", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); } } @@ -661,7 +720,9 @@ struct DndetaMFTPbPb { using Colls = soa::Join; using CollsCentFT0C = soa::Join; using CollsGenCentFT0C = soa::Join; + using CollsGenCentFT0CExtra = soa::Join; using CollsCorr = soa::Join; + using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; /// Tracks using MftTracksLabeled = soa::Join; @@ -693,8 +754,96 @@ struct DndetaMFTPbPb { const int nContrib = collision.numContrib(); if (maxNcontributors < nContrib) { maxNcontributors = nContrib; + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); } - mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); + } + } + + std::unordered_set mcIds; + std::unordered_set setRecCollSel; + std::unordered_map refCentByMcId; + std::unordered_map genCentByMcId; + std::unordered_map mapRecToMc; + std::unordered_map mapMcToRec; + + PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; + + template + void createMCIds(MC const& mcCollisions, CollsGenCentFT0CExtra const& collisions, P const& particles) + { + const auto& nMcColls = mcCollisions.size(); + LOG(info) << "MC collisions: " << nMcColls; + const auto& nRecoColls = collisions.size(); + LOG(info) << "Reconstructed collisions: " << nRecoColls; + + mcIds.clear(); + mcIds.reserve(nMcColls); + setRecCollSel.clear(); + setRecCollSel.reserve(nRecoColls); + refCentByMcId.clear(); + refCentByMcId.reserve(nMcColls); + genCentByMcId.clear(); + genCentByMcId.reserve(nMcColls); + mapMcCollIdPerRecColl.clear(); + mapMcCollIdPerRecColl.reserve(nRecoColls); + mapRecToMc.clear(); + mapRecToMc.reserve(nRecoColls); + mapMcToRec.clear(); + mapMcToRec.reserve(nRecoColls); + + for (const auto& mcCollision : mcCollisions) { + const auto mcId = mcCollision.globalIndex(); + + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + + bool atLeastOne = false; + int maxNcontributors = -1; + float bestCollCent = CInvalid; + + auto groupedColls = collisions.sliceBy(perCollMcRec, mcId); + for (auto const& collision : groupedColls) { + const float lCent = getRecoCent(collision); + if (!isGoodEvent(collision)) { + continue; + } + atLeastOne = true; + + // const int nContrib = collision.multPVTotalContributors(); + const int nContrib = collision.numContrib(); + if (maxNcontributors < nContrib) { + maxNcontributors = nContrib; + bestCollCent = lCent; + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); + mapRecToMc.emplace(collision.globalIndex(), collision.mcCollisionId()); + mapMcToRec.emplace(collision.mcCollisionId(), collision.globalIndex()); + setRecCollSel.insert(collision.globalIndex()); + } + } + + if (!atLeastOne || bestCollCent == CInvalid) { + continue; + } + + auto partsPerMcColl = particles.sliceBy(perMCCol, mcId); + const int genMult = countPartMidRap(partsPerMcColl); + const float genCentClass = getCentGenMult(genMult); + if (genCentClass == CInvalid) { + continue; + } + if (genCentClass < eventCuts.minCentrality || genCentClass > eventCuts.maxCentrality) { + continue; + } + registryMC.fill(HIST("Events/hCentBest"), bestCollCent); + + mcIds.insert(mcId); + refCentByMcId.emplace(mcId, genCentClass); + genCentByMcId[mcId] = genMult; + + registryMC.fill(HIST("Events/hGenMult"), genMult); + registryMC.fill(HIST("Events/hCentRecFromGenMult"), genCentClass); + registryMC.fill(HIST("Events/hCentRecVsGenMult"), bestCollCent, genMult); } } @@ -1001,6 +1150,40 @@ struct DndetaMFTPbPb { return nChrgMc != CintZero; } + template + int countPartMidRap(P const& particles) + { + int nCh = 0; + for (auto const& part : particles) { + if (!isChrgParticle(part.pdgCode())) { + continue; + } + if (!part.isPhysicalPrimary()) { + continue; + } + if (part.eta() < trackCuts.minEtaGenMult || part.eta() > trackCuts.maxEtaGenMult) { + continue; + } + nCh++; + } + return nCh; + } + + float getCentGenMult(int nCharged) + { + const auto& genMultBinLimits = binOpt.genMultBins.value; + const auto& recCentBinVal = binOpt.recCentBinCenters.value; + if (genMultBinLimits.size() != recCentBinVal.size()) { + LOGF(fatal, "genMultBinLimits size (%zu) is different from recCentBinVal size (%zu)", genMultBinLimits.size(), recCentBinVal.size()); + } + for (size_t i = 0; i < genMultBinLimits.size(); ++i) { + if (nCharged >= genMultBinLimits[i]) { + return recCentBinVal[i]; + } + } + return CInvalid; + } + template int countPart(P const& particles) { @@ -1059,9 +1242,9 @@ struct DndetaMFTPbPb { float getOccupancy(C const& collision, uint occEstimator) { switch (occEstimator) { - case OccupancyEst::TrkITS: + case static_cast(OccupancyEst::TrkITS): return collision.trackOccupancyInTimeRange(); - case OccupancyEst::Ft0C: + case static_cast(OccupancyEst::Ft0C): return collision.ft0cOccupancyInTimeRange(); default: LOG(fatal) << "No valid occupancy estimator "; @@ -1343,7 +1526,6 @@ struct DndetaMFTPbPb { registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); } else { registryData.fill(HIST("Events/Selection"), 2., occ); - registryData.fill(HIST("Events/hZvtxCent"), z, c, occ); } auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); @@ -1384,11 +1566,50 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", false); - Preslice perCol = o2::aod::fwdtrack::collisionId; + template + void processDataCorrelationwBestTracks(typename C::iterator const& collision, + aod::MFTTracks const& /*tracks*/, + soa::SmallGroups const& besttracks) + { + if (!isGoodEvent(collision)) { + return; + } + + auto nBestTrks = 0; + for (auto const& atrack : besttracks) { + if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.template mfttrack_as(); + if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { + continue; + } + if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { + continue; + } + nBestTrks++; + } + registryData.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); + registryData.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); + registryData.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); + registryData.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); + registryData.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); + } + + void processDataCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, + aod::MFTTracks const& tracks, + soa::SmallGroups const& besttracks) + { + processDataCorrelationwBestTracks(collision, tracks, besttracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processDataCorrelationwBestTracksInclusive, "Process correlation QA based on BestCollisionsFwd3d table", false); + Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); + Preslice perColMcFiltTrk = o2::aod::fwdtrack::collisionId; template - void processMC(typename MC::iterator const& mcCollision, + void processMc(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks) @@ -1457,7 +1678,7 @@ struct DndetaMFTPbPb { } auto nTrkRec = 0; - auto perColSample = tracks.sliceBy(perCol, collision.globalIndex()); + auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); for (auto const& track : perColSample) { if (!isTrackSelected(track)) { continue; @@ -1521,30 +1742,28 @@ struct DndetaMFTPbPb { fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); } - void processMCInclusive(CollsMCExtraMult::iterator const& mccollision, + void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks) { - processMC(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCInclusive, "Count MC particles (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcInclusive, "Count MC particles (inclusive)", false); - void processMCCentFT0C(CollsMCExtraMult::iterator const& mccollision, + void processMcCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks) { - processMC(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0C, "Count MC particles in FT0C centrality bins", false); - - PresliceUnsorted perColU = aod::fwdtrack::bestCollisionId; + PROCESS_SWITCH(DndetaMFTPbPb, processMcCentFT0C, "Count MC particles in FT0C centrality bins", false); template - void processMCbest(typename MC::iterator const& mcCollision, + void processMcBest(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& /*tracks*/, @@ -1616,7 +1835,7 @@ struct DndetaMFTPbPb { } auto nATrk = 0; - auto perCollisionASample = besttracks.sliceBy(perColU, collision.globalIndex()); + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); @@ -1735,40 +1954,38 @@ struct DndetaMFTPbPb { fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); } - void processMCbestInclusive(CollsMCExtraMult::iterator const& mccollision, + void processMcBestInclusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks, MftBestTracksLabeled const& besttracks) { - processMCbest(mccollision, collisions, particles, tracks, besttracks); + processMcBest(mccollision, collisions, particles, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCbestInclusive, "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcBestInclusive, "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", false); - void processMCbestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + void processMcBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks, MftBestTracksLabeled const& besttracks) { - processMCbest(mccollision, collisions, particles, tracks, besttracks); + processMcBest(mccollision, collisions, particles, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCbestCentFT0C, "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", false); - - Preslice filtMcTrkperCol = o2::aod::fwdtrack::collisionId; + PROCESS_SWITCH(DndetaMFTPbPb, processMcBestCentFT0C, "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", false); /// @brief process function to calculate tracking efficiency template - void processEfficiency(typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftTracksLabeled const& tracks) + void processMcEfficiency(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - auto cRec = -999.; - auto occRec = -999.; + auto cRec = CInvalid; + auto occRec = CInvalid; bool gtOneRec = false; for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { @@ -1851,7 +2068,7 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); } - auto perColTrks = tracks.sliceBy(filtMcTrkperCol, collision.globalIndex()); + auto perColTrks = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); for (auto const& track : perColTrks) { if (!isTrackSelected(track)) { continue; @@ -1884,25 +2101,25 @@ struct DndetaMFTPbPb { } } - void processEfficiencyInclusive(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftTracksLabeled const& tracks) + void processMcEfficiencyInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processEfficiency(mccollision, collisions, particles, tracks); + processMcEfficiency(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyInclusive, "Process efficiencies (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyInclusive, "Process efficiencies (inclusive)", false); - void processEfficiencyCentFT0C(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftTracksLabeled const& tracks) + void processMcEfficiencyCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - processEfficiency(mccollision, collisions, particles, tracks); + processMcEfficiency(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyCentFT0C, "Process efficiencies in FT0C centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyCentFT0C, "Process efficiencies in FT0C centrality bins", false); /// @brief process function to calculate tracking efficiency based on BestCollisionsFwd3d in FT0C bins template @@ -1915,8 +2132,8 @@ struct DndetaMFTPbPb { buildLookupTable(collisions); - auto cRec = -999.; - auto occRec = -999.; + auto cRec = CInvalid; + auto occRec = CInvalid; bool gtOneRec = false; for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { @@ -1929,7 +2146,6 @@ struct DndetaMFTPbPb { cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } - if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); if (gtOneRec) { @@ -1941,7 +2157,6 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); } } - for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; @@ -1965,7 +2180,6 @@ struct DndetaMFTPbPb { } } } - for (const auto& collision : collisions) { if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); @@ -2002,7 +2216,7 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); } - auto perCollisionASample = besttracks.sliceBy(perColU, collision.globalIndex()); + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { if (!isBestTrackSelected(atrack)) { continue; @@ -2057,25 +2271,25 @@ struct DndetaMFTPbPb { } } - void processEfficiencyBestInclusive(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftBestTracksLabeled const& besttracks) + void processMcEfficiencyBestInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) { processEfficiencyBest(mccollision, collisions, particles, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); - void processEfficiencyBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftBestTracksLabeled const& besttracks) + void processMcEfficiencyBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) { processEfficiencyBest(mccollision, collisions, particles, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); // Partition primariesIdx = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > 5.f, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)) && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); @@ -2087,6 +2301,18 @@ struct DndetaMFTPbPb { MftTracksLabeled const& /*tracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + auto occRec = CInvalid; + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -2094,9 +2320,6 @@ struct DndetaMFTPbPb { if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - float cRec = getRecoCent(collision); - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); for (auto const& particle : partsPerCol) { if (!isChrgParticle(particle.pdgCode())) { @@ -2197,25 +2420,25 @@ struct DndetaMFTPbPb { } } - void processEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, - MftTracksLabeled const& tracks) + void processMcEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxInlusive, "Process tracking efficiency (inclusive, indexed)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxInlusive, "Process tracking efficiency (inclusive, indexed)", false); - void processEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, - MftTracksLabeled const& tracks) + void processMcEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxCentFT0C, "Process tracking efficiency (in FT0C centrality bins, indexed)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxCentFT0C, "Process tracking efficiency (in FT0C centrality bins, indexed)", false); template void processEfficiencyIdxBest(typename MC::iterator const& mcCollision, @@ -2224,6 +2447,18 @@ struct DndetaMFTPbPb { MftBestTracksLabeled const& /*atracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + auto occRec = CInvalid; + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } buildLookupTable(collisions); @@ -2234,9 +2469,6 @@ struct DndetaMFTPbPb { if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - float cRec = getRecoCent(collision); - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); for (auto const& particle : partsPerCol) { if (!isChrgParticle(particle.pdgCode())) { @@ -2342,25 +2574,25 @@ struct DndetaMFTPbPb { } } - void processEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, - MftBestTracksLabeled const& atracks) + void processMcEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); - void processEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, - MftBestTracksLabeled const& atracks) + void processMcEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + FiltParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); /// @brief process function to calculate signal loss based on MC void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, @@ -2387,7 +2619,7 @@ struct DndetaMFTPbPb { bool gtZeroColl = false; auto maxNcontributors = -1; - float cRec = -999.f; + float cRec = CInvalid; for (auto const& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -2438,44 +2670,188 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcSgnEvtLossCentFT0C, "Process Signal/event loss based on MC (in FT0C centrality bins)", false); - template - void processCorrelationwBestTracks(typename C::iterator const& collision, - aod::MFTTracks const& /*tracks*/, - soa::SmallGroups const& besttracks) + void processMcReassocDCA(CollsGenCentFT0CExtra const& collisions, + CollsMCExtra const& mcCollisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks, + MftTracksLabeled const& /*tracks*/) { - if (!isGoodEvent(collision)) { - return; - } + createMCIds(mcCollisions, collisions, particles); - auto nBestTrks = 0; - for (auto const& atrack : besttracks) { - if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { + int nNoMC{0}; + for (const auto& collision : collisions) { + auto crec = getRecoCent(collision); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); + if (!isGoodEvent(collision)) { continue; } - auto itrack = atrack.template mfttrack_as(); - if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { + if (!collision.has_mcCollision()) { continue; } - if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { + + int64_t recCollId = collision.globalIndex(); + auto itMC = mapMcCollIdPerRecColl.find(recCollId); + if (itMC == mapMcCollIdPerRecColl.end()) { + nNoMC++; continue; } - nBestTrks++; - } - registryData.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); - registryData.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); - registryData.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); - registryData.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); - registryData.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); - } + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); + if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; + } + auto mcColl = collision.mcCollision_as(); + if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 4., crec); - void processCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, - aod::MFTTracks const& tracks, - soa::SmallGroups const& besttracks) - { - processCorrelationwBestTracks(collision, tracks, besttracks); + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if (!isBestTrackSelected(atrack)) { + continue; + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + + if (!isTrackSelected(itrack)) { + continue; + } + if (!itrack.has_collision()) { + continue; + } + if (gConf.cfgRemoveReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } + } + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRec"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), atrack.bestDCAZ(), crec, crec); + + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + auto particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + const int bestRecColl = atrack.bestCollisionId(); + auto itMapMcCollIdPerRecColl = mapMcCollIdPerRecColl.find(bestRecColl); + if (itMapMcCollIdPerRecColl == mapMcCollIdPerRecColl.end()) { + continue; + } + const float mcCollIdRec = itMapMcCollIdPerRecColl->second; + // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - bestMCCol: {}", mcCollIdRec, bestMCCol); + const auto dcaXtruth(particle.vx() - mcColl.posX()); + const auto dcaYtruth(particle.vy() - mcColl.posY()); + const auto dcaZtruth(particle.vz() - mcColl.posZ()); + auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); + + const auto mcId = particle.mcCollisionId(); + if (!mcIds.contains(mcId)) { + continue; + } + auto itRefCent = refCentByMcId.find(mcId); + if (itRefCent == refCentByMcId.end()) { + continue; + } + const float refCent = itRefCent->second; + // auto itGenCent = genCentByMcId.find(mcId); + // if (itGenCent == genCentByMcId.end()) { + // continue; + // } + // const int genCent = itGenCent->second; + + if (atrack.ambDegree() > CintZero) { // all tracks + if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSec"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSec"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeak"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMat"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrim"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrim"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // Wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } + if (atrack.ambDegree() == CintOne) { // non-ambiguous + if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // Wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } + } else { // ambiguous + if (collision.has_mcCollision() && mcCollIdRec == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } + } + } else { // no MC particle + LOGP(debug, "No MC particle for ambiguous itrack, skip..."); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRecFake"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } + } + } + LOG(info) << "No MC: " << nNoMC; } - PROCESS_SWITCH(DndetaMFTPbPb, processCorrelationwBestTracksInclusive, "Process correlation QA based on BestCollisionsFwd3d table", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcReassocDCA, "Process MC DCA checks using re-association information based on BestCollisionsFwd3d table (in FT0C centrality bins)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 65cbc1d76a50bd1e901dcd63da726d2751bfac33 Mon Sep 17 00:00:00 2001 From: sarjeetagami <162087855+sarjeetagami@users.noreply.github.com> Date: Thu, 24 Sep 2026 12:15:07 +0530 Subject: [PATCH 054/648] =?UTF-8?q?[PWGLF]=20Add=20production=20plane=20an?= =?UTF-8?q?d=20random=20plane,=20plus=20the=20phi=20part=20for=20=E2=80=A6?= =?UTF-8?q?=20(#18025)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sarjeeta gami --- PWGLF/Tasks/Resonances/kstarpbpb.cxx | 851 ++++++++++++++---- .../Tasks/Resonances/phianalysisrun3pbpb.cxx | 682 +++----------- 2 files changed, 790 insertions(+), 743 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 0a36e123a3d..919065e8cd1 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -21,6 +21,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -39,17 +40,20 @@ #include #include #include +#include #include #include +#include #include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include #include +#include +#include #include #include #include @@ -132,6 +136,39 @@ struct Kstarpbpb { Configurable cfgOccupancyCut{"cfgOccupancyCut", 500, "Occupancy cut"}; Configurable useWeight{"useWeight", false, "use EP dep effi weight"}; Configurable useSP{"useSP", false, "use SP"}; + // spin-alignment quantization axis + Configurable cfgSAFrame{"cfgSAFrame", 0, "SA quantization axis: 0 = event plane (FT0C), 1 = production plane, 2 = random event plane"}; + Configurable cfgRndSeed{"cfgRndSeed", 0, "Seed for random event plane (0 = unique seed per job)"}; + Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 2.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 2 is the previous behaviour, n = 1 is the normal to the Psi2 plane"}; + + // phi(1020) spin alignment: event selection is the K* one; track/PID selections and defaults identical to PWGLF/Tasks/Resonances/phipbpb.cxx + // (JSON keys follow the O2 linter naming rules, so removeFakeTrack, confFakeKaonCut and nsigmaCutTPC differ from the phipbpb.cxx spelling) + struct : ConfigurableGroup { + std::string prefix = "phiSA"; + Configurable removeFakeTrack{"removeFakeTrack", true, "Remove fake track from momentum difference"}; + Configurable confFakeKaonCut{"confFakeKaonCut", 0.1, "Cut based on track from momentum difference"}; + Configurable useGlobalTrack{"useGlobalTrack", true, "use Global track"}; + Configurable useDcaSyst{"useDcaSyst", false, "useDcaSyst"}; + Configurable cfgCutTOFBeta{"cfgCutTOFBeta", 0.0, "cut TOF beta"}; + Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Value of the TPC Nsigma cut"}; + Configurable nsigmaCutTOF{"nsigmaCutTOF", 3.0, "Value of the TOF Nsigma cut"}; + Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Number of TPC cluster"}; + Configurable cfgTPCSharedcluster{"cfgTPCSharedcluster", 0.4, "Maximum Number of TPC shared cluster"}; + Configurable isDeepAngle{"isDeepAngle", false, "Deep Angle cut"}; + Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; + Configurable ispTdepPID{"ispTdepPID", true, "pT dependent PID"}; + Configurable isTOFOnly{"isTOFOnly", false, "TOF only PID"}; + Configurable confRapidity{"confRapidity", 0.5, "Rapidity cut"}; + Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; + Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; + ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {120, 0.98, 1.1}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisCosThetaStar{"configThnAxisCosThetaStar", {10, -1.0, 1.}, "cos(#vartheta)"}; + ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0., 80}, "Centrality"}; + ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {8, 0, 0.8}, "Rapidity"}; + ConfigurableAxis configThnAxisSA{"configThnAxisSA", {200, -1, 1}, "SA"}; + } phiSA; Configurable cfgMinTrackPt{"cfgMinTrackPt", 0.15f, "Minimum track pT"}; @@ -168,12 +205,12 @@ struct Kstarpbpb { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Event selection cuts - Alex - TF1* fMultPVCutLow = nullptr; - TF1* fMultPVCutHigh = nullptr; - TF1* fMultCutLow = nullptr; - TF1* fMultCutHigh = nullptr; - TF1* fMultMultPVCut = nullptr; + enum SAFrame : int { + kEventPlane = 0, + kProductionPlane = 1, + kRandomEventPlane = 2 + }; + TRandom3 rndGen; void init(o2::framework::InitContext&) { @@ -182,52 +219,77 @@ struct Kstarpbpb { rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + if (cfgSAFrame.value < kEventPlane || cfgSAFrame.value > kRandomEventPlane) { + LOGF(fatal, "cfgSAFrame = %d not supported (0 = event plane, 1 = production plane, 2 = random event plane)", cfgSAFrame.value); + } + rndGen.SetSeed(cfgRndSeed); + std::vector occupancyBinning = {0.0, 500.0, 1000.0, 1500.0, 2000.0, 3000.0, 4000.0, 5000.0, 50000.0}; AxisSpec phiAxis = {500, -6.28, 6.28, "phi"}; AxisSpec resAxis = {6000, -30, 30, "Res"}; AxisSpec centAxis = {8, 0, 80, "V0M (%)"}; AxisSpec occupancyAxis = {occupancyBinning, "Occupancy"}; - histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0, 10.0}}); - if (!fillSA) { - histos.add("hSparseV2SASameEvent_V2", "hSparseV2SASameEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); - } - if (fillRotation) { + AxisSpec vzAxis = {400, -20.0, 20.0, "Z_{vtx} (cm)"}; + AxisSpec qvecAxis = {200, 0, 20, "q"}; + if (doprocessSE) { + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0, 10.0}}); if (!fillSA) { + histos.add("hSparseV2SASameEvent_V2", "hSparseV2SASameEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + } + if (fillRotation && !fillSA) { histos.add("hRotation", "hRotation", kTH1F, {{360, 0.0, o2::constants::math::TwoPI}}); histos.add("hSparseV2SASameEventRotational_V2", "hSparseV2SASameEventRotational_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); } + if (fillSA) { + histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + if (fillRotation) { + histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + } + if (cfgSAFrame.value == kRandomEventPlane) { + histos.add("hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); + } + } } - if (fillSA) { - histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + if (doprocessMixedEvent) { + if (!fillSA) { + histos.add("hSparseV2SAMixedEvent_V2", "hSparseV2SAMixedEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + } + if (fillSA) { + histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + } } - histos.add("hSparseV2SAGen_V2", "hSparseV2SAGen_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); - histos.add("hSparseV2SARec_V2", "hSparseV2SARec_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); - histos.add("hpt", "hpt", kTH1F, {configThnAxisPt}); - histos.add("hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); - histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); - histos.add("CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); - histos.add("hSparseV2SAMixedEvent_V2", "hSparseV2SAMixedEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); - histos.add("h2PhiGen2", "Phi meson gen", kTH2F, {configThnAxisPt, configThnAxisCentrality}); - histos.add("h2PhiRec2", "Phi meson Rec", kTH2F, {configThnAxisPt, configThnAxisCentrality}); - histos.add("hImpactParameter", "Impact parameter", kTH1F, {{200, 0.0f, 20.0f}}); - histos.add("hEventPlaneAngle", "hEventPlaneAngle", kTH1F, {{200, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); - histos.add("hSparseKstarMCGenWeight", "hSparseKstarMCGenWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecWeight", "hSparseKstarMCRecWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCGenKaonWeight", "hSparseKstarMCGenKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecKaonWeight", "hSparseKstarMCRecKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecKaonMissMatchWeight", "hSparseKstarMCRecKaonMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCGenPionWeight", "hSparseKstarMCGenPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecPionWeight", "hSparseKstarMCRecPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecPionMissMatchWeight", "hSparseKstarMCRecPionMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCGenSA", "hSparseKstarMCGenSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); - histos.add("hSparseKstarMCGenCosThetaStar_effy", "hSparseKstarMCGenCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); - histos.add("hSparseKstarMCRecSA", "hSparseKstarMCRecSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); - histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); - if (additionalQAplots1) { + + if (doprocessMC) { + histos.add("hSparseV2SAGen_V2", "hSparseV2SAGen_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + histos.add("hSparseV2SARec_V2", "hSparseV2SARec_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + histos.add("hpt", "hpt", kTH1F, {configThnAxisPt}); + histos.add("hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); + histos.add("CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); + histos.add("h2PhiGen2", "Phi meson gen", kTH2F, {configThnAxisPt, configThnAxisCentrality}); + histos.add("h2PhiRec2", "Phi meson Rec", kTH2F, {configThnAxisPt, configThnAxisCentrality}); + histos.add("hSparseKstarMCGenSA", "hSparseKstarMCGenSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCGenCosThetaStar_effy", "hSparseKstarMCGenCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecSA", "hSparseKstarMCRecSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + } + + if (doprocessMCkstarWeight) { + histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); + histos.add("hImpactParameter", "Impact parameter", kTH1F, {{200, 0.0f, 20.0f}}); + histos.add("hEventPlaneAngle", "hEventPlaneAngle", kTH1F, {{200, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); + histos.add("hSparseKstarMCGenWeight", "hSparseKstarMCGenWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecWeight", "hSparseKstarMCRecWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCGenKaonWeight", "hSparseKstarMCGenKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecKaonWeight", "hSparseKstarMCRecKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecKaonMissMatchWeight", "hSparseKstarMCRecKaonMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCGenPionWeight", "hSparseKstarMCGenPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecPionWeight", "hSparseKstarMCRecPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecPionMissMatchWeight", "hSparseKstarMCRecPionMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + } + + if (doprocessSE && additionalQAplots1) { histos.add("hFTOCvsTPCSelected", "Mult correlation FT0C vs. TPC after selection", kTH2F, {{80, 0.0f, 80.0f}, {100, -0.5f, 5999.5f}}); histos.add("hCentrality", "Centrality distribution", kTH1F, {{200, 0.0, 200.0}}); histos.add("hOccupancy", "Occupancy distribution", kTH1F, {occupancyAxis}); @@ -244,8 +306,14 @@ struct Kstarpbpb { histos.add("ResTrackSPFT0CTPC", "ResTrackSPFT0CTPC", kTH3F, {centAxis, occupancyAxis, resAxis}); histos.add("ResTrackSPFT0CFT0A", "ResTrackSPFT0CFT0A", kTH3F, {centAxis, occupancyAxis, resAxis}); histos.add("ResTrackSPFT0ATPC", "ResTrackSPFT0ATPC", kTH3F, {centAxis, occupancyAxis, resAxis}); + histos.add("hQFT0CvsCent", "q_{FT0C} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQFT0CvsVz", "q_{FT0C} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); + histos.add("hQFT0AvsCent", "q_{FT0A} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQFT0AvsVz", "q_{FT0A} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); + histos.add("hQTPCvsCent", "q_{TPC} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQTPCvsVz", "q_{TPC} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); } - if (additionalQAplots) { + if (doprocessSE && additionalQAplots) { // DCA QA histos.add("QAbefore/trkDCAxyka", "DCAxy distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); histos.add("QAbefore/trkDCAzka", "DCAz distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); @@ -276,11 +344,45 @@ struct Kstarpbpb { histos.add("QAafter/TPC_Nsigma_allpi", "TPC NSigma for pion;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{pion};", {HistType::kTH3D, {{200, 0.0, 20.0}, {100, -6, 6}, {100, 0.0, 100.0}}}); } - histos.add("hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); - histos.add("hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); - histos.add("hMassMixedEventLikeNN", "Mixed-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); - histos.add("hMassMixedEventLikePP", "Mixed-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); - histos.add("hMassMixedEventUnlike", "Mixed-event unlike-sign mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + if (doprocessSE) { + histos.add("hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + } + if (doprocessMixedEvent) { + histos.add("hMassMixedEventLikeNN", "Mixed-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassMixedEventLikePP", "Mixed-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassMixedEventUnlike", "Mixed-event unlike-sign mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + } + + // phi(1020) spin alignment, same names and binning as phipbpb.cxx + if (doprocessSEPhi || doprocessMEPhi || doprocessMCPhi) { + const AxisSpec thnAxisInvMassPhi{phiSA.configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPtPhi{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisCosThetaStarPhi{phiSA.configThnAxisCosThetaStar, "cos(#vartheta_{OP})"}; + const AxisSpec thnAxisCentralityPhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + const AxisSpec thnAxisRapidityPhi{phiSA.configThnAxisRapidity, "Rapidity"}; + const AxisSpec thnAxisSAPhi{phiSA.configThnAxisSA, "SA"}; + if (cfgSAFrame.value == kRandomEventPlane) { + histos.add("phi/hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); + } + if (doprocessSEPhi) { + histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (doprocessMEPhi) { + histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MixedEventCosThetaStar", "hSparseV2MixedEventCosThetaStar", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (doprocessMCPhi) { + histos.add("phi/hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); + histos.add("phi/h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); + histos.add("phi/CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); + histos.add("phi/hSparseV2MCGenSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + } ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); @@ -289,8 +391,8 @@ struct Kstarpbpb { ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); } - double massKa = o2::constants::physics::MassKPlus; - double massPi = o2::constants::physics::MassPiMinus; + static constexpr double MassKa = o2::constants::physics::MassKPlus; + static constexpr double MassPi = o2::constants::physics::MassPiMinus; template bool selectionTrack(const T& candidate) @@ -403,6 +505,204 @@ struct Kstarpbpb { } return result; } + + // event-level plane angle for SA: reconstructed Psi2 (event plane) or a random angle (random event plane) + double getSAEventAngle(double psiEP) + { + if (cfgSAFrame.value == kRandomEventPlane) { + return rndGen.Uniform(-o2::constants::math::PIHalf, o2::constants::math::PIHalf); + } + return psiEP; + } + + // in-plane angle of the SA plane, used for the cos(2(phi* - Psi)) variable + double getSAPlaneAngle(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kProductionPlane) { + return mother.Phi(); // production plane is spanned by the beam axis and the mother momentum + } + return psiSA; + } + + // quantization axis = normal to the SA plane + ROOT::Math::XYZVector getSAAxis(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kProductionPlane) { + return ROOT::Math::XYZVector(0., 0., 1.).Cross(mother.Vect()).Unit(); // z x p + } + return {std::sin(cfgEPNormalHarmonic.value * psiSA), -std::cos(cfgEPNormalHarmonic.value * psiSA), 0.}; + } + + // ---------------- event selection, common to all K* and phi(1020) process functions ---------------- + template + bool selectionEventRCT(const TCollision& collision) + { + return !rctCut.requireRCTFlagChecker || rctCut.rctChecker(collision); + } + + // additionalEvSel1-4 (data and reconstructed MC) + template + bool selectionEventBits(const TCollision& collision) + { + if (additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + return true; + } + + // sel8, EP trigger and additionalEvSel1-4 + template + bool selectionEventCuts(const TCollision& collision) + { + return collision.sel8() && collision.triggereventep() && selectionEventBits(collision); + } + + // full data event selection without the occupancy cut + template + bool selectionEvent(const TCollision& collision) + { + return selectionEventRCT(collision) && selectionEventCuts(collision); + } + + template + bool selectionOccupancy(const TCollision& collision) + { + return !(fillOccupancy && collision.trackOccupancyInTimeRange() > cfgOccupancyCut); + } + + // mixed-event pair; occupancy rejects the pair only if both events are above the cut + template + bool selectionEventPairME(const TCollision& collision1, const TCollision& collision2) + { + if (!selectionEvent(collision1) || !selectionEvent(collision2)) { + return false; + } + if (collision1.bcId() == collision2.bcId()) { + return false; + } + return !(fillOccupancy && collision1.trackOccupancyInTimeRange() >= cfgOccupancyCut && collision2.trackOccupancyInTimeRange() >= cfgOccupancyCut); + } + + // ---------------- phi(1020) track and PID selections, identical to phipbpb.cxx ---------------- + + template + bool selectionTrackPhi(const T& candidate) + { + // table-level PIDcutFilter of phipbpb.cxx, applied here because this task also selects pions + if (!(std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC)) { + return false; + } + if (phiSA.useGlobalTrack && !phiSA.useDcaSyst && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > phiSA.cfgITScluster && candidate.tpcNClsCrossedRows() > phiSA.cfgTPCcluster && candidate.tpcFractionSharedCls() < phiSA.cfgTPCSharedcluster)) { + return false; + } + if (phiSA.useGlobalTrack && phiSA.useDcaSyst && !(candidate.itsNCls() > phiSA.cfgITScluster && candidate.tpcNClsCrossedRows() > phiSA.cfgTPCcluster && candidate.tpcFractionSharedCls() < phiSA.cfgTPCSharedcluster)) { + return false; + } + if (!phiSA.useGlobalTrack && !(candidate.tpcNClsFound() > phiSA.cfgTPCcluster)) { + return false; + } + return true; + } + + static constexpr float PhiTPCOnlyPt = 0.5f; + template + bool selectionPIDpTdependentPhi(const T& candidate) + { + if (candidate.pt() < PhiTPCOnlyPt && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { + return true; + } + if (candidate.pt() >= PhiTPCOnlyPt && candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF) { + return true; + } + if (!phiSA.useGlobalTrack && !candidate.hasTPC()) { + return true; + } + return false; + } + + template + bool selectionPIDPhi(const T& candidate) + { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { + return true; + } + if (candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF) { + return true; + } + return false; + } + + template + bool selectionPIDTOFOnlyPhi(const T& candidate) + { + return candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF; + } + + // data PID: pT-dependent, standard or TOF-only + template + bool selectionKaonPhi(const T& candidate) + { + if (phiSA.ispTdepPID && !phiSA.isTOFOnly && !selectionPIDpTdependentPhi(candidate)) { + return false; + } + if (!phiSA.ispTdepPID && !phiSA.isTOFOnly && !selectionPIDPhi(candidate)) { + return false; + } + if (phiSA.isTOFOnly && !selectionPIDTOFOnlyPhi(candidate)) { + return false; + } + return true; + } + + static constexpr float PhiITSMaxP = 1.0f; + static constexpr float PhiITSNSigma = 2.5f; + template + bool selectionITSPhi(const T& candidate) + { + if (phiSA.useGlobalTrack && candidate.p() < PhiITSMaxP) { + auto nSigmaITS = itsResponse.nSigmaITS(candidate); + if (!(nSigmaITS > -PhiITSNSigma && nSigmaITS < PhiITSNSigma)) { + return false; + } + } + return true; + } + + // deep angle cut on pair to remove photon conversion + template + bool selectionPairPhi(const T1& candidate1, const T2& candidate2) + { + double pt1 = candidate1.pt(), pt2 = candidate2.pt(); + double pz1 = candidate1.pz(), pz2 = candidate2.pz(); + double p1 = candidate1.p(), p2 = candidate2.p(); + double angle = std::acos(std::clamp((pt1 * pt2 + pz1 * pz2) / (p1 * p2), -1.0, 1.0)); // clamp = TMath::ACos behaviour + return !phiSA.isDeepAngle || angle >= phiSA.cfgDeepAngle; + } + + template + bool isFakeKaonPhi(T const& track) + { + return std::abs(track.p() - track.tpcInnerParam()) > phiSA.confFakeKaonCut; + } + + // phipbpb.cxx builds the EP-frame MC efficiency with the in-plane vector at Psi = 0 (x axis); kept for identical results + ROOT::Math::XYZVector getSAAxisPhiMC(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kEventPlane) { + return {std::cos(2.0 * psiSA), std::sin(2.0 * psiSA), 0.}; + } + return getSAAxis(mother, psiSA); + } + template bool isFakeKaon(T const& track, int /*PID*/) { @@ -418,45 +718,29 @@ struct Kstarpbpb { {6, -HalfPI, HalfPI}, "event plane angle"}; ConfigurableAxis axisOccup{"axisOccup", {20, -0.5, 40000.0}, "occupancy axis"}; - double v2 = 0.; - double v2Rot = 0.; using BinningTypeVertexContributor = ColumnBinningPolicy; - ROOT::Math::PxPyPzMVector kstarMother, fourVecDauCM, daughter1, daughter2, kaonrot, kstarrot, kaonPlus, pionMinus; - ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVec, eventplaneVecNorm; - ROOT::Math::PxPyPzMVector daughter2rot, fourVecDauCMrot; - ROOT::Math::XYZVector threeVecDauCMrot, threeVecDauCMXYrot; int currentRunNumber = -999; int lastRunNumber = -999; TH2D* hweight = nullptr; + o2::aod::ITSResponse itsResponse; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { + // scratch vectors: local to this call, never carry state across events/tracks + ROOT::Math::PxPyPzMVector kstarMother, fourVecDauCM, daughter1, daughter2, kaonrot, kstarrot; + ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVecNorm; + ROOT::Math::PxPyPzMVector fourVecDauCMrot; + ROOT::Math::XYZVector threeVecDauCMrot, threeVecDauCMXYrot; + double v2 = 0.; + double v2Rot = 0.; + histos.fill(HIST("hEvtSelInfo"), 0.5); - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision)) { + if (!selectionEventRCT(collision)) { return; } histos.fill(HIST("hEvtSelInfo"), 1.5); - if (!collision.sel8()) { - return; - } - if (!collision.triggereventep()) { - return; - } - if (additionalEvSel1 && - !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return; - } - if (additionalEvSel2 && - !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return; - } - if (additionalEvSel3 && - !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { - return; - } - if (additionalEvSel4 && - !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (!selectionEventCuts(collision)) { return; } histos.fill(HIST("hEvtSelInfo"), 2.5); @@ -469,7 +753,7 @@ struct Kstarpbpb { auto qFT0C = collision.qFT0C(); auto qFT0A = collision.qFT0A(); auto qTPC = collision.qTPC(); - if (fillOccupancy && occupancy > cfgOccupancyCut) { + if (!selectionOccupancy(collision)) { return; } histos.fill(HIST("hEvtSelInfo"), 3.5); @@ -487,6 +771,17 @@ struct Kstarpbpb { histos.fill(HIST("hCentrality"), centrality); histos.fill(HIST("hOccupancy"), occupancy); histos.fill(HIST("hVtxZ"), collision.posZ()); + histos.fill(HIST("hQFT0CvsCent"), centrality, qFT0C); + histos.fill(HIST("hQFT0CvsVz"), collision.posZ(), qFT0C); + histos.fill(HIST("hQFT0AvsCent"), centrality, qFT0A); + histos.fill(HIST("hQFT0AvsVz"), collision.posZ(), qFT0A); + histos.fill(HIST("hQTPCvsCent"), centrality, qTPC); + histos.fill(HIST("hQTPCvsVz"), collision.posZ(), qTPC); + } + // one angle per event: Psi2 from FT0C, or a random angle for the random-EP null test + auto psiSA = getSAEventAngle(psiFT0C); + if (fillSA && cfgSAFrame.value == kRandomEventPlane) { + histos.fill(HIST("hPsiRandom"), centrality, psiSA); } auto bc = collision.bc_as(); currentRunNumber = collision.bc_as().runNumber(); @@ -554,8 +849,8 @@ struct Kstarpbpb { weight2 = 1; } } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); kstarMother = daughter1 + daughter2; if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; @@ -603,9 +898,8 @@ struct Kstarpbpb { fourVecDauCM = boost(daughter1); threeVecDauCM = fourVecDauCM.Vect(); threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); - eventplaneVec = ROOT::Math::XYZVector(std::cos(2.0 * psiFT0C), std::sin(2.0 * psiFT0C), 0); - eventplaneVecNorm = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - psiFT0C); + eventplaneVecNorm = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA)); auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); @@ -634,7 +928,7 @@ struct Kstarpbpb { } auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); - kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track1.pz(), massKa); + kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track1.pz(), MassKa); kstarrot = kaonrot + daughter2; if (std::abs(kstarrot.Rapidity()) > confRapidity) { continue; @@ -656,9 +950,11 @@ struct Kstarpbpb { fourVecDauCMrot = boost(kaonrot); threeVecDauCMrot = fourVecDauCMrot.Vect(); threeVecDauCMXYrot = ROOT::Math::XYZVector(threeVecDauCMrot.X(), threeVecDauCMrot.Y(), 0.); - auto cosPhistarminuspsirot = getPhiInRange(fourVecDauCMrot.Phi() - psiFT0C); + // production-plane axis must follow the rotated candidate + auto saAxisRot = getSAAxis(kstarrot, psiSA); + auto cosPhistarminuspsirot = getPhiInRange(fourVecDauCMrot.Phi() - getSAPlaneAngle(kstarrot, psiSA)); auto sarot = std::cos(2.0 * cosPhistarminuspsirot); - auto cosThetaStarrot = eventplaneVecNorm.Dot(threeVecDauCMrot) / std::sqrt(threeVecDauCMrot.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + auto cosThetaStarrot = saAxisRot.Dot(threeVecDauCMrot) / std::sqrt(threeVecDauCMrot.Mag2()) / std::sqrt(saAxisRot.Mag2()); if (usepolar) { histos.fill(HIST("hSparseSAvsraprot"), kstarrot.M(), kstarrot.Pt(), cosThetaStarrot, kstarrot.Rapidity(), centrality); } else { @@ -675,48 +971,16 @@ struct Kstarpbpb { void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { + // scratch vectors: local to this call, never carry state across pairs + ROOT::Math::PxPyPzMVector kstarMother, fourVecDauCM, daughter1, daughter2; + ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVecNorm; + double v2 = 0.; auto tracksTuple = std::make_tuple(tracks); BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision2)) { - continue; - } - - if (!collision1.sel8() || - !collision2.sel8() || - - !collision1.triggereventep() || - !collision2.triggereventep() || - - (additionalEvSel1 && - (!collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || - !collision2.selection_bit(aod::evsel::kNoTimeFrameBorder))) || - - (additionalEvSel2 && - (!collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || - !collision2.selection_bit(aod::evsel::kNoITSROFrameBorder))) || - - (additionalEvSel3 && - (!collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || - !collision2.selection_bit(aod::evsel::kNoSameBunchPileup))) || - - (additionalEvSel4 && - (!collision1.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) || - !collision2.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)))) { - continue; - } - if (collision1.bcId() == collision2.bcId()) { - continue; - } - int occupancy1 = collision1.trackOccupancyInTimeRange(); - int occupancy2 = collision2.trackOccupancyInTimeRange(); - if (fillOccupancy && occupancy1 >= cfgOccupancyCut && occupancy2 >= cfgOccupancyCut) // occupancy info is available for this collision (*) - { + if (!selectionEventPairME(collision1, collision2)) { continue; } auto centrality = collision1.centFT0C(); @@ -724,6 +988,7 @@ struct Kstarpbpb { auto qFT0C1 = collision1.qFT0C(); auto psiFT0C2 = collision2.psiFT0C(); auto qFT0C2 = collision2.qFT0C(); + auto psiSA1 = getSAEventAngle(psiFT0C1); for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { @@ -743,8 +1008,8 @@ struct Kstarpbpb { continue; } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); kstarMother = daughter1 + daughter2; if (std::abs(kstarMother.Rapidity()) > confRapidity) { @@ -775,9 +1040,8 @@ struct Kstarpbpb { fourVecDauCM = boost(daughter1); threeVecDauCM = fourVecDauCM.Vect(); threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); - eventplaneVec = ROOT::Math::XYZVector(std::cos(2.0 * psiFT0C1), std::sin(2.0 * psiFT0C1), 0); - eventplaneVecNorm = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C1), -std::cos(2.0 * psiFT0C1), 0); - auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - psiFT0C1); + eventplaneVecNorm = getSAAxis(kstarMother, psiSA1); + auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA1)); auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); if (usepolar) { @@ -801,6 +1065,10 @@ struct Kstarpbpb { void processMC(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { + // scratch vectors: local to this call, never carry state across particles + ROOT::Math::PxPyPzMVector kstarMother, kaonPlus, pionMinus; + double v2 = 0.; + histos.fill(HIST("hMC"), 0); if (RecCollisions.size() == 0) { histos.fill(HIST("hMC"), 1); @@ -817,24 +1085,7 @@ struct Kstarpbpb { histos.fill(HIST("hMC"), 4); continue; } - - if (additionalEvSel1 && - !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - continue; - } - - if (additionalEvSel2 && - !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - continue; - } - - if (additionalEvSel3 && - !RecCollision.selection_bit(aod::evsel::kNoSameBunchPileup)) { - continue; - } - - if (additionalEvSel4 && - !RecCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (!selectionEventBits(RecCollision)) { continue; } histos.fill(HIST("hMC"), 5); @@ -845,6 +1096,7 @@ struct Kstarpbpb { histos.fill(HIST("hMC"), 7); auto centrality = RecCollision.centFT0C(); histos.fill(HIST("CentPercentileMCRecHist"), centrality); + auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event auto oldindex = -999; auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle @@ -906,7 +1158,7 @@ struct Kstarpbpb { if (std::abs(mothertrack1.y()) > confRapidity) { continue; } - if (pdgcheck && std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { + if (pdgcheck && mothertrack1.pdgCode() != o2::constants::physics::kK0Star892) { continue; } if (!isTOFOnly && !(strategySelectionPID(track1, 0, strategyPID) || strategySelectionPID(track2, 1, strategyPID))) { @@ -919,14 +1171,10 @@ struct Kstarpbpb { continue; } oldindex = mothertrack1.globalIndex(); - if (track1.sign() > 0 && track2.sign() < 0) { - kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - pionMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } - if (track1.sign() < 0 && track2.sign() > 0) { - pionMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } + // track1 is always the kaon-PID candidate and track2 always the pion-PID candidate (enforced above); + // assign unconditionally so the boost below always uses the true kaon, regardless of its charge sign + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pionMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); kstarMother = kaonPlus + pionMinus; if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; @@ -943,8 +1191,8 @@ struct Kstarpbpb { ROOT::Math::Boost boost{kstarMother.BoostToCM()}; auto fourVecDauCMRec = boost(kaonPlus); auto threeVecDauCMRec = fourVecDauCMRec.Vect(); - auto eventplaneVecNormRec = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosPhistarminuspsiRec = getPhiInRange(fourVecDauCMRec.Phi() - psiFT0C); + auto eventplaneVecNormRec = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsiRec = getPhiInRange(fourVecDauCMRec.Phi() - getSAPlaneAngle(kstarMother, psiSA)); auto saRec = std::cos(2.0 * cosPhistarminuspsiRec); auto cosThetaStarRec = eventplaneVecNormRec.Dot(threeVecDauCMRec) / std::sqrt(threeVecDauCMRec.Mag2()) / std::sqrt(eventplaneVecNormRec.Mag2()); @@ -982,7 +1230,7 @@ struct Kstarpbpb { if (!genacceptancecut) { daughtp = true; } - kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); + kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); } else if (kCurrentDaughter.pdgCode() == -PDG_t::kPiPlus) { if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtm = true; @@ -990,7 +1238,7 @@ struct Kstarpbpb { if (!genacceptancecut) { daughtm = true; } - pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massPi); + pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassPi); } } if (daughtp && daughtm) { @@ -1009,8 +1257,8 @@ struct Kstarpbpb { ROOT::Math::Boost boost{kstarMother.BoostToCM()}; auto fourVecDauCMGen = boost(kaonPlus); auto threeVecDauCMGen = fourVecDauCMGen.Vect(); - auto eventplaneVecNormGen = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosPhistarminuspsiGen = getPhiInRange(fourVecDauCMGen.Phi() - psiFT0C); + auto eventplaneVecNormGen = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsiGen = getPhiInRange(fourVecDauCMGen.Phi() - getSAPlaneAngle(kstarMother, psiSA)); auto saGen = std::cos(2.0 * cosPhistarminuspsiGen); auto cosThetaStarGen = eventplaneVecNormGen.Dot(threeVecDauCMGen) / std::sqrt(threeVecDauCMGen.Mag2()) / std::sqrt(eventplaneVecNormGen.Mag2()); @@ -1026,6 +1274,9 @@ struct Kstarpbpb { void processMCkstarWeight(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { + // scratch vectors: local to this call, never carry state across particles + ROOT::Math::PxPyPzMVector kaonPlus, pionMinus; + float imp = TrueCollision.impactParameter(); float evPhi = TrueCollision.eventPlaneAngle() / 2.0; static constexpr std::array CentEdges = { @@ -1107,16 +1358,6 @@ struct Kstarpbpb { if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus) { continue; } - if (!selectionTrack(track1) || !selectionTrack(track2) || track1.sign() * track2.sign() > 0) { - continue; - } - // PID check - if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { - continue; - } - if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { - continue; - } for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { @@ -1178,12 +1419,12 @@ struct Kstarpbpb { if (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtp = true; } - kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); + kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); } else if (kCurrentDaughter.pdgCode() == -PDG_t::kPiPlus) { if (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtm = true; } - pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massPi); + pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassPi); } } if (daughtp && daughtm) { @@ -1195,6 +1436,272 @@ struct Kstarpbpb { } // process MC PROCESS_SWITCH(Kstarpbpb, processMCkstarWeight, "Process MC kstar Weight", false); + + // ================= phi(1020) spin alignment: port of the SA part of phipbpb.cxx ================= + struct SAValues { + double cosThetaStar; + double sa; + }; + + // K- in the phi rest frame (as in phipbpb.cxx) w.r.t. the chosen quantization axis + SAValues getSAValuesPhi(const ROOT::Math::PxPyPzMVector& mother, const ROOT::Math::PxPyPzMVector& kaonMinus, const ROOT::Math::XYZVector& axis, double psiSA) + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto threeVecDau = boost(kaonMinus).Vect(); + auto cosThetaStar = axis.Dot(threeVecDau) / std::sqrt(threeVecDau.Mag2()) / std::sqrt(axis.Mag2()); + auto sa = std::cos(2.0 * getPhiInRange(threeVecDau.Phi() - getSAPlaneAngle(mother, psiSA))); + return {.cosThetaStar = cosThetaStar, .sa = sa}; + } + + void processSEPhi(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + if (!selectionEvent(collision) || !selectionOccupancy(collision)) { + return; + } + auto centrality = collision.centFT0C(); + auto psiSA = getSAEventAngle(collision.psiFT0C()); + if (cfgSAFrame.value == kRandomEventPlane) { + histos.fill(HIST("phi/hPsiRandom"), centrality, psiSA); + } + + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + for (const auto& track1 : tracks) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // positive kaon + continue; + } + if (!selectionTrackPhi(track1) || !selectionKaonPhi(track1) || !selectionITSPhi(track1)) { + continue; + } + for (const auto& track2 : tracks) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // negative kaon + continue; + } + if (!selectionTrackPhi(track2) || !selectionKaonPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (!selectionITSPhi(track2)) { + continue; + } + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phiMother = kaonPlusPhi + kaonMinusPhi; + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + auto absRapidity = std::abs(phiMother.Rapidity()); + histos.fill(HIST("phi/hSparseV2SameEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); + histos.fill(HIST("phi/hSparseV2SameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processSEPhi, "Process same event phi(1020) spin alignment", false); + + void processMEPhi(EventCandidates const& collisions, TrackCandidates const& tracks) + { + auto tracksTuple = std::make_tuple(tracks); + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionEventPairME(collision1, collision2)) { + continue; + } + auto centrality = collision1.centFT0C(); + auto psiSA = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionITSPhi(track1) || !selectionITSPhi(track2)) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionKaonPhi(track1) || !selectionKaonPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phiMother = kaonPlusPhi + kaonMinusPhi; + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + auto absRapidity = std::abs(phiMother.Rapidity()); + histos.fill(HIST("phi/hSparseV2MixedEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); + histos.fill(HIST("phi/hSparseV2MixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMEPhi, "Process mixed event phi(1020) spin alignment", false); + + void processMCPhi(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + { + histos.fill(HIST("phi/hMC"), 0); + if (RecCollisions.size() == 0) { + histos.fill(HIST("phi/hMC"), 1); + return; + } + if (RecCollisions.size() > 1) { + histos.fill(HIST("phi/hMC"), 2); + return; + } + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + for (const auto& RecCollision : RecCollisions) { + auto psiFT0C = 0.0; + histos.fill(HIST("phi/hMC"), 3); + if (!RecCollision.sel8()) { + histos.fill(HIST("phi/hMC"), 4); + continue; + } + if (!selectionEventBits(RecCollision)) { + continue; + } + histos.fill(HIST("phi/hMC"), 5); // same bin meaning as the K* hMC + if (std::abs(RecCollision.posZ()) > cfgCutVertex) { + histos.fill(HIST("phi/hMC"), 6); + continue; + } + histos.fill(HIST("phi/hMC"), 7); + auto centrality = RecCollision.centFT0C(); + histos.fill(HIST("phi/CentPercentileMCRecHist"), centrality); + auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + auto oldindex = -999; + auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); + // loop over reconstructed particle + for (const auto& track1 : rectrackspart) { + if (!selectionTrackPhi(track1)) { + continue; + } + if (phiSA.ispTdepPID && !selectionPIDpTdependentPhi(track1)) { + continue; + } + if (!phiSA.ispTdepPID && !selectionPIDPhi(track1)) { + continue; + } + if (!track1.has_mcParticle()) { + continue; + } + auto track1ID = track1.index(); + for (const auto& track2 : rectrackspart) { + auto track2ID = track2.index(); + if (track2ID <= track1ID) { + continue; + } + if (!selectionTrackPhi(track2)) { + continue; + } + if (phiSA.ispTdepPID && !selectionPIDpTdependentPhi(track2)) { + continue; + } + if (!phiSA.ispTdepPID && !selectionPIDPhi(track2)) { + continue; + } + if (!track2.has_mcParticle()) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + const auto mctrack1 = track1.mcParticle(); + const auto mctrack2 = track2.mcParticle(); + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { + continue; + } + if (std::abs(mctrack1.pdgCode()) != PDG_t::kKPlus || std::abs(mctrack2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + for (const auto& mothertrack1 : mctrack1.mothers_as()) { + for (const auto& mothertrack2 : mctrack2.mothers_as()) { + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { + continue; + } + if (mothertrack1 != mothertrack2) { + continue; + } + if (std::abs(mothertrack1.y()) > phiSA.confRapidity) { + continue; + } + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (phiSA.avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { + histos.fill(HIST("phi/h1PhiRecsplit"), mothertrack1.pt()); + continue; + } + oldindex = mothertrack1.globalIndex(); + if (track1.sign() > 0) { + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phiMother = kaonPlusPhi + kaonMinusPhi; + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + histos.fill(HIST("phi/hSparseV2MCRecSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); + } + } + } + } + // loop over generated particle + for (const auto& mcParticle : GenParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto kDaughters = mcParticle.daughters_as(); + static constexpr std::size_t NumberOfDaughters = 2; + if (kDaughters.size() != NumberOfDaughters) { + continue; + } + auto daughtp = false; + auto daughtm = false; + for (const auto& kCurrentDaughter : kDaughters) { + if (!kCurrentDaughter.isPhysicalPrimary()) { + continue; + } + bool inAcceptance = !phiSA.genacceptancecut || (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta); + if (kCurrentDaughter.pdgCode() == +PDG_t::kKPlus) { + daughtp = daughtp || inAcceptance; + kaonPlusPhi = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } else if (kCurrentDaughter.pdgCode() == -PDG_t::kKPlus) { + daughtm = daughtm || inAcceptance; + kaonMinusPhi = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } + } + if (daughtp && daughtm) { + phiMother = kaonPlusPhi + kaonMinusPhi; + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2MCGenCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + histos.fill(HIST("phi/hSparseV2MCGenSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCPhi, "Process MC phi(1020) spin alignment", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx index afbed255a6e..26184b5f79f 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx @@ -157,6 +157,11 @@ struct Phianalysisrun3pbpb { histos.add("h2PhiRapidity", "phi meson Rapidity", kTH2F, {ptAxisphi, {200, -4, 4}}); histos.add("hEta", "eta of kaon track candidates", HistType::kTH2F, {{200, -1.0f, 1.0f}, ptAxisphi}); histos.add("hPhi", "phi of kaon track candidates", HistType::kTH2F, {{65, 0, 6.5}, ptAxisphi}); + histos.add("QAbefore/TPC_Nsigma_all", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); + histos.add("QAbefore/TOF_Nsigma_all", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); + histos.add("QAbefore/trkDCAxy", "DCAxy distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); + histos.add("QAbefore/trkDCAz", "DCAz distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); + histos.add("QAbefore/TOF_TPC_Mapka_all", "TOF + TPC Combined PID for Kaon;#sigma_{TOF}^{Kaon};#sigma_{TPC}^{Kaon}", {HistType::kTH2D, {{100, -6, 6}, {100, -6, 6}}}); // DCA QA // DCA histograms: separate for positive and negative kaons, range [-1.0, 1.0] @@ -200,46 +205,35 @@ struct Phianalysisrun3pbpb { histos.add("QAafter/TPC_Nsigma_all_neg", "TPC NSigma for negative Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{K^{-}}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); } else { histos.add("hMC", "MC Event statistics", kTH1F, {{15, 0.0f, 15.0f}}); - histos.add("EL1", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("EL2", "MC Event statistics", kTH1F, {centAxis}); - histos.add("ES1", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("ES3", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("ES2", "MC Event statistics", kTH1F, {centAxis}); - histos.add("ES4", "MC Event statistics", kTH1F, {centAxis}); histos.add("h1PhiGen", "Phi meson Gen", kTH1F, {ptAxisphi}); histos.add("h1PhiGen1", "Phi meson Gen", kTH1F, {ptAxisphi}); histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {ptAxisphi}); histos.add("Centrec", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centgen", "MC Centrality", kTH1F, {centAxisphi}); - histos.add("hVtxZgen", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("hVtxZrec", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("h2PhiRec2", "Phi meson Rec", kTH2F, {ptAxisphi, centAxisphi}); histos.add("h3PhiRec3", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); - histos.add("h3Phi1Rec3", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); - histos.add("h3PhiGen3", "Phi meson Gen", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); histos.add("h3PhiInvMassMixedMC", "Invariant mass of Phi meson Mixed", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassSameMC", "Invariant mass of Phi meson same", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassSameMC1", "Invariant mass of Phi meson same", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassRotMC", "Invariant mass of Phi meson Rotation", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h2PhiGen2", "Phi meson gen", kTH2F, {ptAxisphi, centAxisphi}); - histos.add("h2PhiGen1", "Phi meson gen", kTH2F, {ptAxis, impactParAxis}); histos.add("h1PhiRec1", "Phi meson Rec", kTH1F, {ptAxisphi}); histos.add("h1Phimassgen", "Phi meson gen", kTH1F, {massAxisphi}); histos.add("h1Phimassrec", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimasssame", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimassmix", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimassrot", "Phi meson Rec", kTH1F, {massAxisphi}); - histos.add("h1Phi1massrec", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phipt", "Phi meson Rec", kTH1F, {ptAxisphi}); - histos.add("hOccupancy1", "Occupancy distribution", kTH1F, {{500, 0, 50000}}); histos.add("h1PhifinalRec", "Phi meson Rec", kTH1F, {ptAxisphi}); histos.add("h1Phifinalgenmass", "Phi meson gen mass", kTH1F, {massAxisphi}); histos.add("h3PhifinalRec", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); // Mass resolution: Rec-Gen invariant mass difference per truth-matched candidate, - // binned in (generated pT, centrality). Idea from h3PhiRec in - // https://github.com/sarjeetagami/O2Physics/blob/db08910960e93e188969354ae8925ca0c2a0a0a1/PWGLF/Tasks/Resonances/phianalysisrun3.cxx - // (there binned in genPt/recPt instead of genPt/centrality). Lets the - // resolution be obtained directly from mean/RMS or a Gaussian fit of the + // binned in (reconstructed pT, centrality) so it lines up with how real data + // (and every other Rec-level histogram in this file) is binned - resolution vs. + // pT is only comparable to the data-driven Voigtian-width proxy if both use the + // same, measurable (reconstructed) pT axis, not the MC-truth generated pT. + // Lets the resolution be obtained directly from mean/RMS or a Gaussian fit of the // projected deltaM distribution per pT bin, instead of only from the // Voigtian-sigma method (resv.cxx) that h3PhifinalRec alone supports. histos.add("h3PhiRecoResolution", "Phi meson Rec-Gen mass difference", kTH3F, {ptAxisphi, centAxisphi, deltaMassAxisphi}); @@ -252,12 +246,6 @@ struct Phianalysisrun3pbpb { histos.add("Centmix", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centgen1", "MC Centrality", kTH1F, {centAxisphi}); histos.add("h1PhiRecsplit1", "Phi meson Rec split", kTH1F, {ptAxisphi}); - histos.add("hImpactParameterGen", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); - histos.add("hImpactParameterRec", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); - histos.add("hImpactParameterGenCen", "Impact parameter of generated MC events", kTH2F, {impactParAxis, centAxis}); - histos.add("hImpactParameterRecCen", "Impact parameter of generated MC events", kTH2F, {impactParAxis, centAxis}); - histos.add("TOF_Nsigma_MC", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); - histos.add("TPC_Nsigma_MC", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("TOF_Nsigma1_MC", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("TPC_Nsigma1_MC", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("trkDCAxy", "DCAxy distribution of positive kaon track candidates", HistType::kTH3F, {{150, -1.0f, 1.0f}, centAxisphi, ptAxisphi}); @@ -422,7 +410,7 @@ struct Phianalysisrun3pbpb { return false; } int occupancy = collision.trackOccupancyInTimeRange(); - return !(selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)); + return !selectionConfig.fillOccupancy || (occupancy >= cfgMinOccupancy && occupancy <= cfgMaxOccupancy); } template float selColMultMC(CheckColCent const& col) @@ -460,7 +448,7 @@ struct Phianalysisrun3pbpb { double p1 = candidate1.p(); double p2 = candidate2.p(); double angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - return !(isDeepAngle && angle < cfgDeepAngle); + return !isDeepAngle || angle >= cfgDeepAngle; } template void fillinvMass(const T1& candidate1, const T2& candidate2, float multiplicity, bool unlike, bool mix, bool likesign, float massd1, float massd2) @@ -505,6 +493,32 @@ struct Phianalysisrun3pbpb { } } } + template + void fillMixedPairs(const T1& tracks1, const T2& tracks2, float multiplicity) + { + for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + bool unlike = false; + bool likesign = false; + bool mix = true; + if (!selectionTrack(t1)) { + continue; + } + if (!selectionTrack(t2)) { + continue; + } + if (!selectionPair(t1, t2)) { + continue; + } + if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { + fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); + } + if (ispTdepPID && + (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && + (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { + fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); + } + } + } Filter collisionFilter = (!selectionConfig.cutvz) || (nabs(aod::collision::posZ) < selectionConfig.cfgCutVertex); @@ -699,340 +713,71 @@ struct Phianalysisrun3pbpb { } PROCESS_SWITCH(Phianalysisrun3pbpb, processSameEvent, "Process Same event", false); - void processMixedEvent1(EventCandidates const& collisions, TrackCandidates const& tracks) + void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.selHasBC && (!c1.has_foundBC() || !c2.has_foundBC())) { - continue; - } - if (selectionConfig.selHasFT0 && (!c1.has_foundFT0() || !c2.has_foundFT0())) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity = c1.centFT0C(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool likesign = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + //////// currently mixing the event with similar multiplicity; estimator picked at runtime via centestimator //////// + const int kCentFT0C = 0; + const int kCentFT0A = 1; + const int kCentFT0M = 2; + const int kCentFV0A = 3; + if (centestimator == kCentFT0C) { + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - } - } - } - PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent1, "Process Mixed event", false); - void processMixedEvent2(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; + fillMixedPairs(tracks1, tracks2, c1.centFT0C()); } - if (selectionConfig.selHasBC && (!c1.has_foundBC() || !c2.has_foundBC())) { - continue; - } - if (selectionConfig.selHasFT0 && (!c1.has_foundFT0() || !c2.has_foundFT0())) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity = c1.centFT0A(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool likesign = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFT0A) { + BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - } - } - } - - PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent2, "Process Mixed event", false); - void processMixedEvent3(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.selHasBC && (!c1.has_foundBC() || !c2.has_foundBC())) { - continue; - } - if (selectionConfig.selHasFT0 && (!c1.has_foundFT0() || !c2.has_foundFT0())) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; + fillMixedPairs(tracks1, tracks2, c1.centFT0A()); } - float multiplicity = c1.centFT0M(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool likesign = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFT0M) { + BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - } - } - } - - PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent3, "Process Mixed event", false); - void processMixedEvent4(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; + fillMixedPairs(tracks1, tracks2, c1.centFT0M()); } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.selHasBC && (!c1.has_foundBC() || !c2.has_foundBC())) { - continue; - } - if (selectionConfig.selHasFT0 && (!c1.has_foundFT0() || !c2.has_foundFT0())) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity = c1.centFV0A(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool likesign = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFV0A) { + BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); - } + fillMixedPairs(tracks1, tracks2, c1.centFV0A()); } } } - - PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent4, "Process Mixed event", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent, "Process Mixed event", false); void processRotEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { - if (!collision.sel8()) { + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { + return; + } + if (cfgDoSel8 && !collision.sel8()) { return; } if (selectionConfig.selHasBC && !collision.has_foundBC()) { @@ -1041,19 +786,44 @@ struct Phianalysisrun3pbpb { if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { return; } - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return; + } + if (selectionConfig.additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return; + } + if (selectionConfig.additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return; } - if (selectionConfig.additionalEvSel3 && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { + if (selectionConfig.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return; + } + if (selectionConfig.additionalEvSel5 && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return; + } + if (selectionConfig.additionalEvSel6 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return; } int occupancy = collision.trackOccupancyInTimeRange(); if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { return; } + if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + return; + } float multiplicity{-1}; - if (cfgMultFT0) { + const int kCentFT0C = 0; + const int kCentFT0A = 1; + const int kCentFT0M = 2; + const int kCentFV0A = 3; + if (centestimator == kCentFT0C) { multiplicity = collision.centFT0C(); + } else if (centestimator == kCentFT0A) { + multiplicity = collision.centFT0A(); + } else if (centestimator == kCentFT0M) { + multiplicity = collision.centFT0M(); + } else if (centestimator == kCentFV0A) { + multiplicity = collision.centFV0A(); } histos.fill(HIST("hCentrality"), multiplicity); histos.fill(HIST("hVtxZ"), collision.posZ()); @@ -1107,8 +877,7 @@ struct Phianalysisrun3pbpb { for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { auto anglestart = confMinRot; auto angleend = confMaxRot; - auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); - auto rotangle = anglestart + nrotbkg * anglestep; + auto rotangle = (nBkgRotations > 1) ? (anglestart + nrotbkg * (angleend - anglestart) / (1.0 * (nBkgRotations - 1))) : (0.5 * (anglestart + angleend)); if (track1.sign() * track2.sign() < 0) { auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); @@ -1346,235 +1115,6 @@ struct Phianalysisrun3pbpb { } // process MC PROCESS_SWITCH(Phianalysisrun3pbpb, processMC, "Process Reconstructed", false); - void processGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) - { - - histos.fill(HIST("hMC"), 0.5); - if (std::abs(mcCollision.posZ()) < selectionConfig.cfgCutVertex) { - histos.fill(HIST("hMC"), 1.5); - } - float imp = mcCollision.impactParameter(); - histos.fill(HIST("hImpactParameterGen"), imp); - std::vector selectedEvents(collisions.size()); - int nevts = 0; - float multiplicity = 0.f; - for (const auto& collision : collisions) { - if (cfgDoSel8 && !collision.sel8()) { - continue; - } - if (selectionConfig.selHasBC && !collision.has_foundBC()) { - continue; - } - if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { - continue; - } - if (std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { - continue; - } - - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - int occupancy = collision.trackOccupancyInTimeRange(); - if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { - continue; - } - histos.fill(HIST("hOccupancy1"), occupancy); - multiplicity = collision.centFT0C(); - histos.fill(HIST("Centgen"), multiplicity); - histos.fill(HIST("hVtxZgen"), collision.mcCollision().posZ()); - histos.fill(HIST("hImpactParameterGenCen"), imp, multiplicity); - - selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); - histos.fill(HIST("hMC"), 2.5); - } - selectedEvents.resize(nevts); - - const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); - histos.fill(HIST("EL1"), imp); - histos.fill(HIST("EL2"), multiplicity); - if (reco && !evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection - return; - } - histos.fill(HIST("ES1"), imp); - histos.fill(HIST("ES2"), multiplicity); - for (const auto& mcParticle : mcParticles) { - const double kMaxAcceptedRapidity = 0.5; - - if (std::abs(mcParticle.y()) >= kMaxAcceptedRapidity) { - - continue; - } - if (pdgcheck && mcParticle.pdgCode() != o2::constants::physics::kPhi) { - continue; - } - auto kDaughters = mcParticle.daughters_as(); - const size_t kExpectedNumberOfDaughters = 2; - - if (kDaughters.size() != kExpectedNumberOfDaughters) { - - continue; - } - auto daughtp = false; - auto daughtm = false; - for (const auto& kCurrentDaughter : kDaughters) { - if (!kCurrentDaughter.isPhysicalPrimary()) { - continue; - } - if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { - daughtp = true; - kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { - daughtm = true; - kaonMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } - } - if (daughtp && daughtm) { - phiMesonMother = kaonPlus + kaonMinus; - histos.fill(HIST("h1PhiGen"), phiMesonMother.pt()); - histos.fill(HIST("h2PhiGen2"), phiMesonMother.pt(), multiplicity); - histos.fill(HIST("h2PhiGen1"), phiMesonMother.pt(), imp); - histos.fill(HIST("h1Phimassgen"), phiMesonMother.M()); - histos.fill(HIST("h3PhiGen3"), phiMesonMother.pt(), multiplicity, phiMesonMother.M()); - } - } - } - PROCESS_SWITCH(Phianalysisrun3pbpb, processGen, "Process Generated", false); - void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) - { - if (!collision.has_mcCollision()) { - return; - } - if (cfgDoSel8 && !collision.sel8()) { - return; - } - if (selectionConfig.selHasBC && !collision.has_foundBC()) { - return; - } - if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { - return; - } - if (std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { - return; - } - - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - return; - } - int occupancy = collision.trackOccupancyInTimeRange(); - if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { - return; - } - auto multiplicity = collision.centFT0C(); - histos.fill(HIST("Centrec"), multiplicity); - histos.fill(HIST("hVtxZrec"), collision.posZ()); - float imp = collision.mcCollision().impactParameter(); - histos.fill(HIST("hImpactParameterRec"), imp); - histos.fill(HIST("hImpactParameterRecCen"), imp, multiplicity); - histos.fill(HIST("ES3"), imp); - histos.fill(HIST("ES4"), multiplicity); - auto oldindex = -999; - for (const auto& track1 : tracks) { - if (!selectionTrack(track1)) { - continue; - } - if (!track1.has_mcParticle()) { - continue; - } - auto track1ID = track1.index(); - for (const auto& track2 : tracks) { - if (!track2.has_mcParticle()) { - continue; - } - if (!selectionTrack(track2)) { - continue; - } - auto track2ID = track2.index(); - if (track2ID <= track1ID) { - continue; - } - if (!selectionPair(track1, track2)) { - continue; - } - if (track1.sign() * track2.sign() > 0) { - continue; - } - const auto mctrack1 = track1.mcParticle(); - const auto mctrack2 = track2.mcParticle(); - int track1PDG = std::abs(mctrack1.pdgCode()); - int track2PDG = std::abs(mctrack2.pdgCode()); - if (!mctrack1.isPhysicalPrimary()) { - continue; - } - if (!mctrack2.isPhysicalPrimary()) { - continue; - } - if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kKPlus) { - continue; - } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - - phiMother = daughter1 + daughter2; - histos.fill(HIST("h1Phi1massrec"), phiMother.M()); - histos.fill(HIST("h3Phi1Rec3"), phiMother.pt(), multiplicity, phiMother.M()); - for (const auto& mothertrack1 : mctrack1.mothers_as()) { - for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { - continue; - } - if (mothertrack1.globalIndex() != mothertrack2.globalIndex()) { - continue; - } - if (!mothertrack1.producedByGenerator()) { - continue; - } - const double kMaxRapidityCut = 0.5; - - if (std::abs(mothertrack1.y()) >= kMaxRapidityCut) { - continue; - } - - if (pdgcheck && std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { - continue; - } - if (!ispTdepPID && (!selectionPID(track1) || !selectionPID(track2))) { - continue; - } - if (ispTdepPID && - (selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1)) && - (selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { - - continue; - } - - histos.fill(HIST("TPC_Nsigma_MC"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - histos.fill(HIST("TOF_Nsigma_MC"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { - histos.fill(HIST("h1PhiRecsplit"), mothertrack1.pt()); - continue; - } - oldindex = mothertrack1.globalIndex(); - if (track1.sign() * track2.sign() < 0) { - kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - } - phiMesonMother = kaonPlus + kaonMinus; - - if (std::abs(phiMesonMother.Rapidity()) > confRapidity) { - continue; - } - histos.fill(HIST("h1PhiRec1"), phiMesonMother.pt()); - histos.fill(HIST("h2PhiRec2"), phiMesonMother.pt(), multiplicity); - histos.fill(HIST("h1Phimassrec"), phiMesonMother.M()); - histos.fill(HIST("h3PhiRec3"), phiMesonMother.pt(), multiplicity, phiMesonMother.M()); - } - } - } - } - } - - PROCESS_SWITCH(Phianalysisrun3pbpb, processRec, "Process Reconstructed", false); void processSameEventMC(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) { if (!collision.sel8()) { @@ -1672,8 +1212,7 @@ struct Phianalysisrun3pbpb { for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { auto anglestart = confMinRot; auto angleend = confMaxRot; - auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); - auto rotangle = anglestart + nrotbkg * anglestep; + auto rotangle = (nBkgRotations > 1) ? (anglestart + nrotbkg * (angleend - anglestart) / (1.0 * (nBkgRotations - 1))) : (0.5 * (anglestart + angleend)); if (track1.sign() * track2.sign() < 0) { auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); @@ -2057,6 +1596,7 @@ struct Phianalysisrun3pbpb { std::array pvec1Rec = {track2.px(), track2.py(), track2.pz()}; std::array, 2> arrMomrec = {pvec0Rec, pvec1Rec}; recMass = RecoDecay::m(arrMomrec, std::array{massKa, massKa}); + auto recPt = RecoDecay::pt(std::array{track1.px() + track2.px(), track1.py() + track2.py()}); // reconstructed-pair rapidity, cut with the same rapiditycut1/rapiditycut2 that fillinvMass() applies to data auto recRapidity = RecoDecay::y(std::array{track1.px() + track2.px(), track1.py() + track2.py(), track1.pz() + track2.pz()}, recMass); @@ -2074,10 +1614,10 @@ struct Phianalysisrun3pbpb { auto motherE = mothertrack1.e(); genMass = std::sqrt(motherE * motherE - motherP * motherP); - histos.fill(HIST("h1PhifinalRec"), mothertrack1.pt()); - histos.fill(HIST("h3PhifinalRec"), mothertrack1.pt(), multiplicity, recMass); + histos.fill(HIST("h1PhifinalRec"), recPt); + histos.fill(HIST("h3PhifinalRec"), recPt, multiplicity, recMass); histos.fill(HIST("h1Phifinalgenmass"), genMass); - histos.fill(HIST("h3PhiRecoResolution"), mothertrack1.pt(), multiplicity, recMass - genMass); + histos.fill(HIST("h3PhiRecoResolution"), recPt, multiplicity, recMass - genMass); } } } From 3103678e90d098f856be398c6bbe87f59a84f9b7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bernardinis <95907752+lorber98@users.noreply.github.com> Date: Thu, 24 Sep 2026 10:08:13 +0200 Subject: [PATCH 055/648] [PWGLF] Fix MB histograms filling for cascade in MC reco (#18027) --- .../Strangeness/strangenessInJetsIons.cxx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx index 4542d3ed6ce..47769cced1e 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx @@ -1584,17 +1584,17 @@ struct StrangenessInJetsIons { case kXiMinus: if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiNeg_generated_MB"), genMultiplicity, particle.pt()); - if (hasReco) { - registryMC.fill(HIST("XiNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); - } + } + if (particleOfInterestDict[ParticleOfInterest::kCascades] && hasReco) { + registryMC.fill(HIST("XiNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); } break; case kXiPlusBar: if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiPos_generated_MB"), genMultiplicity, particle.pt()); - if (hasReco) { - registryMC.fill(HIST("XiPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); - } + } + if (particleOfInterestDict[ParticleOfInterest::kCascades] && hasReco) { + registryMC.fill(HIST("XiPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); } break; case kOmegaMinus: @@ -1775,30 +1775,30 @@ struct StrangenessInJetsIons { continue; if (std::abs(motherPos.pdgCode()) != kLambda0) continue; - if (std::abs(motherPos.eta()) > 0.8) + if (std::abs(motherBach.eta()) > 0.8) continue; // Xi+ if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && - motherPos.pdgCode() == kXiPlusBar && + motherBach.pdgCode() == kXiPlusBar && passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { registryMC.fill(HIST("XiPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Xi- if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && - motherPos.pdgCode() == kXiMinus && + motherBach.pdgCode() == kXiMinus && passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { registryMC.fill(HIST("XiNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega+ if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && - motherPos.pdgCode() == kOmegaPlusBar && + motherBach.pdgCode() == kOmegaPlusBar && passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { registryMC.fill(HIST("OmegaPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega- if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && - motherPos.pdgCode() == kOmegaMinus && + motherBach.pdgCode() == kOmegaMinus && passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { registryMC.fill(HIST("OmegaNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); } @@ -1807,11 +1807,11 @@ struct StrangenessInJetsIons { continue; // Rapidity generated particle - double yGen = motherPos.y(); + double yGen = motherBach.y(); // Histograms below are filled only for PhysicalPrimary particles // Xi+ (primary) - if (motherPos.pdgCode() == kXiPlusBar) { + if (motherBach.pdgCode() == kXiPlusBar) { if (passedRapidityCut(yGen, configV0.rapidityMax)) { registryMC.fill(HIST("XiPos_gen_recoEvent_MB"), multiplicity, casc.pt()); } From 3c23e7d1f537720ed08d0fd6fe512a23f3520347 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Thu, 24 Sep 2026 12:04:40 +0200 Subject: [PATCH 056/648] [PWGJE] added pvalue configurable, added safeguard for invalid EP values (#18034) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index cff612f536e..18d5701a0ea 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -121,6 +121,7 @@ struct JetSpectraEseTask { Configurable cfgrhoPhi{"cfgrhoPhi", true, "Flag for rho(phi)"}; Configurable cfgRhoPhiPvalCriteria{"cfgRhoPhiPvalCriteria", true, "Use instead of rho(phi) when the rho(phi) fit p-value is below 0.01"}; + Configurable cfgRhoPhiPvalCut{"cfgRhoPhiPvalCut", 0.01, "p-value cut for rho(phi) fit"}; Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number // look in Qvector table for this number"}; Configurable cfgnCorrLevel{"cfgnCorrLevel", 3, "QVector step: 0 = no corr, 1 = rect, 2 = twist, 3 = full"}; @@ -225,6 +226,7 @@ struct JetSpectraEseTask { struct EventPlane { float psi2; float psi3; + bool isValid; }; struct EventPlaneFiller { @@ -237,6 +239,7 @@ struct JetSpectraEseTask { kEventSel, kOccupancyCut, kCentCut, + kEPValid, kEse, kRhoLocal, kLeadJetCut @@ -382,6 +385,7 @@ struct JetSpectraEseTask { registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEventSel, "Event selection"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kOccupancyCut, "Occupancy cut"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kCentCut, "Centrality cut"); + registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEPValid, "EP valid"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEse, "ESE available"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kRhoLocal, "rho(#phi) available"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kLeadJetCut, "leading jet pT cut"); @@ -454,6 +458,7 @@ struct JetSpectraEseTask { registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEventSel, "Event selection"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kOccupancyCut, "Occupancy cut"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kCentCut, "Centrality cut"); + registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEPValid, "EP valid"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEse, "ESE available"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kRhoLocal, "rho(#phi) available"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kLeadJetCut, "leading jet pT cut"); @@ -697,6 +702,10 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hEventCounter"), kCentCut); const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + registry.fill(HIST("eventQA/hEventCounter"), kEPValid); const auto qPerc{collision.qPERCFT0C()}; if (qPerc[0] < 0) { return; @@ -853,6 +862,10 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hEventCounterMixed"), kCentCut); const auto psi{procEP(c1)}; + if (!psi.isValid) { + continue; + } + registry.fill(HIST("eventQA/hEventCounterMixed"), kEPValid); const auto qPerc{c1.qPERCFT0C()}; if (qPerc[0] < 0) { continue; @@ -1002,6 +1015,9 @@ struct JetSpectraEseTask { } [[maybe_unused]] const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } detCorrelation(collision); auto originalCollision = collision.collision_as(); @@ -1024,6 +1040,9 @@ struct JetSpectraEseTask { float count{0.5}; registry.fill(HIST("hEventCounterOcc"), count++); const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } const auto qPerc{collision.qPERCFT0C()}; auto occupancy{collision.trackOccupancyInTimeRange()}; @@ -1400,7 +1419,9 @@ struct JetSpectraEseTask { fillEPCos(vec, epCorrContainer22, epCorrContainer24, epCorrContainer44); } } - return {.psi2 = epMap.at(cfgEPRefA), .psi3 = ep3Map.at(cfgEPRefA)}; + // check A is valid + bool isValidA{epMap.at(cfgEPRefA) != InvalidValue}; + return {.psi2 = epMap.at(cfgEPRefA), .psi3 = ep3Map.at(cfgEPRefA), .isValid = isValidA}; } template void fillEPCos(const collision& col, const std::array& Corr22, const std::array& Corr42, const std::array& Corr44) @@ -1611,8 +1632,8 @@ struct JetSpectraEseTask { if constexpr (fillHist) { registry.fill(HIST("eventQA/hRhoPhiCheck"), 0.5); } - const float pValue = 0.01; - if (cfgRhoPhiPvalCriteria && cDF < pValue) { + + if (cfgRhoPhiPvalCriteria && cDF < cfgRhoPhiPvalCut) { const float noFlow = 0.0f; modulationFit->SetParameter(1, noFlow); // o2-linter: disable=magic-number (fit params) modulationFit->SetParameter(3, noFlow); // o2-linter: disable=magic-number (fit params) @@ -1701,6 +1722,9 @@ struct JetSpectraEseTask { } const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } auto qPerc{collision.qPERCFT0C()}; if (qPerc[0] < 0) { return; From 017dc0e7e68de61e0bf5edaaed63efe9ee4a9211 Mon Sep 17 00:00:00 2001 From: Nasir Mehdi Malik <89008506+nasirmehdimalik@users.noreply.github.com> Date: Thu, 24 Sep 2026 18:21:12 +0530 Subject: [PATCH 057/648] [PWGLF] Added effeciency calculation for polarisation study (#18028) --- PWGLF/Tasks/Resonances/lambda1520pbpb.cxx | 126 +++++++++++++++++++--- 1 file changed, 110 insertions(+), 16 deletions(-) diff --git a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx index 8cc51ed2294..abcfe683f88 100644 --- a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx @@ -269,21 +269,24 @@ struct Lambda1520pbpb { histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tof_p", " Kaons proton", kTH2F, {axisPpid, axisTOFNsigma}); histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_vs_tpc", "n#sigma(TOF) vs n#sigma(TPC) Kaons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); - // Analysis - if (!doprocessMC) { - int nActiveFrames = static_cast(cActivateHelicityFrame) + - static_cast(cActivateCollinsSoperFrame) + - static_cast(cActivateProductionFrame) + - static_cast(cActivateBeamAxisFrame) + - static_cast(cActivateRandomFrame); - - bool polActive = nActiveFrames > 0; + // ── Polarization-frame activation (shared by data and MC booking) ─────── + int nActiveFrames = static_cast(cActivateHelicityFrame) + + static_cast(cActivateCollinsSoperFrame) + + static_cast(cActivateProductionFrame) + + static_cast(cActivateBeamAxisFrame) + + static_cast(cActivateRandomFrame); + + bool polActive = nActiveFrames > 0; + + if (nActiveFrames > 1) { + LOG(fatal) << "Multiple polarization frames activated, but axisPolFrame was removed! " + << "Please activate only ONE frame at a time to prevent data mixing in the 4D sparse."; + } - if (nActiveFrames > 1) { - LOG(fatal) << "Multiple polarization frames activated, but axisPolFrame was removed! " - << "Please activate only ONE frame at a time to prevent data mixing in the 4D sparse."; - } + const AxisSpec axisCosTheta{cCosThetaBins, "cos#theta*"}; + // Analysis + if (!doprocessMC) { if (!polActive) { histos.add("Analysis/h4d_lstar_invm_US_PM", "THn #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); histos.add("Analysis/h4d_lstar_invm_US_MP", "THn #bar #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); @@ -305,8 +308,6 @@ struct Lambda1520pbpb { // ── 4D Polarization / cosThetaStar histograms ────────────────────────── if (polActive) { - const AxisSpec axisCosTheta{cCosThetaBins, "cos#theta*"}; - histos.add("Analysis/h4d_pol_US_PM", "cos#theta* #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); histos.add("Analysis/h4d_pol_US_MP", "cos#theta* #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); histos.add("Analysis/h4d_pol_LS_PP", "cos#theta* Like Signs p K^{+}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); @@ -340,6 +341,20 @@ struct Lambda1520pbpb { histos.add("Analysis/h3d_rec_lstar_MP", "Reconstructed #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); histos.add("Analysis/h3d_reso_lstar_PM", "Resolution #Lambda(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); histos.add("Analysis/h3d_reso_lstar_MP", "Resolution #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); + + if (polActive) { + const AxisSpec axisMassRes{200, -0.05, 0.05, "M_{rec} - M_{gen} (GeV/#it{c}^{2})"}; + + histos.add("Analysis/h4d_pol_rec_PM", "cos#theta* Reconstructed (truth-matched) #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_rec_MP", "cos#theta* Reconstructed (truth-matched) #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_reso_PM", "cos#theta* vs mass resolution #Lambda(1520)", kTHnSparseF, {axisMassRes, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_reso_MP", "cos#theta* vs mass resolution #bar{#Lambda}(1520)", kTHnSparseF, {axisMassRes, axisPt, axisCent, axisCosTheta}); + } + } + + if (doprocessMCGenPol && polActive) { + histos.add("Analysis/h4d_pol_gen_PM", "cos#theta* Generated (truth) #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_gen_MP", "cos#theta* Generated (truth) #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); } if (doprocessMCGen) { @@ -591,7 +606,10 @@ struct Lambda1520pbpb { Rotated, Mixed, LikeSign, - LikeSignMixed }; + LikeSignMixed, + McTrue, + McTrueRes, + GenTrue }; template void fillPolarization(float candMass, float candPt, @@ -629,6 +647,21 @@ struct Lambda1520pbpb { histos.fill(HIST("Analysis/h4d_pol_LS_PP_mix"), candMass, candPt, mult, cosTheta); else histos.fill(HIST("Analysis/h4d_pol_LS_MM_mix"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::McTrue) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_rec_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_rec_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::McTrueRes) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_reso_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_reso_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::GenTrue) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_gen_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_gen_MP"), candMass, candPt, mult, cosTheta); } else { if (protonIsPositive) histos.fill(HIST("Analysis/h4d_pol_US_PM"), candMass, candPt, mult, cosTheta); @@ -975,6 +1008,17 @@ struct Lambda1520pbpb { histos.fill(HIST("Analysis/h3d_rec_lstar_MP"), candMass, candPt, mult); histos.fill(HIST("Analysis/h3d_reso_lstar_MP"), candMassRes, candPt, mult); } + + if (polActive) { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.motherPDG() > 0); + fillPolarization(candMassRes, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.motherPDG() > 0); + } } } } @@ -1073,6 +1117,56 @@ struct Lambda1520pbpb { } PROCESS_SWITCH(Lambda1520pbpb, processMC, "Process Event for MC", false); + void processMCGenPol(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents, aod::McParticles const& mcParticles) + { + auto mult = collision.cent(); + bool polActive = static_cast(cActivateHelicityFrame) || + static_cast(cActivateCollinsSoperFrame) || + static_cast(cActivateProductionFrame) || + static_cast(cActivateBeamAxisFrame) || + static_cast(cActivateRandomFrame); + if (!polActive) + return; + + for (auto const& part : resoParents) { + if (std::abs(part.pdgCode()) != lambda1520id) + continue; + float yshift = std::abs(part.y()) - cfgRapidityShift; + if (std::abs(yshift) > cfgRapidityCut) + continue; + + bool pass1 = std::abs(part.daughterPDG1()) == kProton || std::abs(part.daughterPDG2()) == kProton; + bool pass2 = std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus; + if (!pass1 || !pass2) + continue; + + std::array pvec = {part.px(), part.py(), part.pz()}; + float mass = RecoDecay::m(pvec, part.e()); + + auto motherMc = mcParticles.iteratorAt(part.mcParticleId()); + bool foundProton = false, foundKaon = false; + std::array protonP{}, kaonP{}; + float protonE = 0.f, kaonE = 0.f; + for (auto const& dau : motherMc.daughters_as()) { + if (!foundProton && std::abs(dau.pdgCode()) == kProton) { + protonP = {dau.px(), dau.py(), dau.pz()}; + protonE = dau.e(); + foundProton = true; + } else if (!foundKaon && std::abs(dau.pdgCode()) == kKPlus) { + kaonP = {dau.px(), dau.py(), dau.pz()}; + kaonE = dau.e(); + foundKaon = true; + } + } + if (!foundProton || !foundKaon) + continue; + + std::array motherP = {part.px(), part.py(), part.pz()}; + fillPolarization(mass, part.pt(), motherP, part.e(), protonP, protonE, kaonP, kaonE, mult, part.pdgCode() > 0); + } + } + PROCESS_SWITCH(Lambda1520pbpb, processMCGenPol, "Standalone generator-level polarization pass (run separately from processMC)", false); + void processMCGen(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents) { float centrality = collision.cent(); From 6ce17cbf4ca2d7b60a88b43042a4b93c70aa42a8 Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:02:18 +0200 Subject: [PATCH 058/648] [PWGHF] Add ML processing for data in D+ tree creator (#18037) --- .../TableProducer/treeCreatorDplusToPiKPi.cxx | 74 +++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx index 5c77c684bc9..19460efb5df 100644 --- a/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx @@ -317,12 +317,18 @@ struct HfTreeCreatorDplusToPiKPi { std::vector outputMl = {-999., -999.}; if constexpr (DoMl) { - for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + if (candidate.mlProbDplusToPiKPi().empty()) { + rowCandidateMl( + -999., + -999.); + } else { + for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + } + rowCandidateMl( + outputMl[0], + outputMl[1]); } - rowCandidateMl( - outputMl[0], - outputMl[1]); } float cent{-1.}; @@ -494,6 +500,35 @@ struct HfTreeCreatorDplusToPiKPi { PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processData, "Process data", true); + void processDataWMl(aod::Collisions const& collisions, + soa::Filtered> const& candidates, + TracksWPid const&) + { + // Filling event properties + rowCandidateFullEvents.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision, 0, 1); + } + + // Filling candidate properties + if (fillCandidateLiteTable) { + rowCandidateLite.reserve(candidates.size()); + } else { + rowCandidateFull.reserve(candidates.size()); + } + for (const auto& candidate : candidates) { + if (downSampleBkgFactor < 1.) { + float const pseudoRndm = candidate.ptProng0() * 1000. - static_cast(candidate.ptProng0() * 1000); + if (candidate.pt() < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { + continue; + } + } + fillCandidateTable(candidate); + } + } + + PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWMl, "Process data with ML", false); + void processDataWCent(CollisionsCent const& collisions, soa::Filtered> const& candidates, TracksWPid const&) @@ -523,6 +558,35 @@ struct HfTreeCreatorDplusToPiKPi { PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWCent, "Process data with cent", false); + void processDataWCentMl(CollisionsCent const& collisions, + soa::Filtered> const& candidates, + TracksWPid const&) + { + // Filling event properties + rowCandidateFullEvents.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision, 0, 1); + } + + // Filling candidate properties + if (fillCandidateLiteTable) { + rowCandidateLite.reserve(candidates.size()); + } else { + rowCandidateFull.reserve(candidates.size()); + } + for (const auto& candidate : candidates) { + if (downSampleBkgFactor < 1.) { + float const pseudoRndm = candidate.ptProng0() * 1000. - static_cast(candidate.ptProng0() * 1000); + if (candidate.pt() < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { + continue; + } + } + fillCandidateTable(candidate); + } + } + + PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWCentMl, "Process data with cent and ML", false); + template void fillMcTables(CollType const& collisions, aod::McCollisions const&, From 7c89443d10c80926b8f97823c0798bc85eda75c7 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Thu, 24 Sep 2026 23:58:51 +0200 Subject: [PATCH 059/648] [PWGLF] First attempt at reconstructing Xi0 decays (#18048) Co-authored-by: ALICE Action Bot --- PWGLF/DataModel/LFSigmaTables.h | 524 +++++++++++++++ .../TableProducer/Strangeness/CMakeLists.txt | 2 +- .../Strangeness/sigma0builder.cxx | 625 +++++++++++++++++- 3 files changed, 1128 insertions(+), 23 deletions(-) diff --git a/PWGLF/DataModel/LFSigmaTables.h b/PWGLF/DataModel/LFSigmaTables.h index e11567b2d7a..de5d0f46acd 100644 --- a/PWGLF/DataModel/LFSigmaTables.h +++ b/PWGLF/DataModel/LFSigmaTables.h @@ -1395,6 +1395,530 @@ DECLARE_SOA_TABLE(Pi0Gens, "AOD", "PI0GENS", DECLARE_SOA_TABLE(Pi0GenCollRef, "AOD", "PI0GENCOLLREF", //! optional table to refer back to a collision o2::soa::Index<>, v0data::StraMCCollisionId); +// ___________________________________________________________________________ +// Xi0 +namespace Xi0Core +{ +DECLARE_SOA_COLUMN(Photon1Index, photon1Index, int); +DECLARE_SOA_COLUMN(Photon2Index, photon2Index, int); +DECLARE_SOA_COLUMN(LambdaIndex, lambdaIndex, int); +DECLARE_SOA_COLUMN(X, x, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(Z, z, float); +DECLARE_SOA_COLUMN(XPi0, xPi0, float); +DECLARE_SOA_COLUMN(YPi0, yPi0, float); +DECLARE_SOA_COLUMN(ZPi0, zPi0, float); +DECLARE_SOA_COLUMN(XLambda, xLambda, float); +DECLARE_SOA_COLUMN(YLambda, yLambda, float); +DECLARE_SOA_COLUMN(ZLambda, zLambda, float); +DECLARE_SOA_COLUMN(DCADaughters, dcadaughters, float); +DECLARE_SOA_COLUMN(DCADaughtersPi0, dcadaughtersPi0, float); +DECLARE_SOA_COLUMN(DCAXYCascToPV, dcaXYCascToPV, float); +DECLARE_SOA_COLUMN(DCAZCascToPV, dcaZCascToPV, float); + +DECLARE_SOA_COLUMN(Photon1Px, photon1Px, float); +DECLARE_SOA_COLUMN(Photon1Py, photon1Py, float); +DECLARE_SOA_COLUMN(Photon1Pz, photon1Pz, float); + +DECLARE_SOA_COLUMN(Photon2Px, photon2Px, float); +DECLARE_SOA_COLUMN(Photon2Py, photon2Py, float); +DECLARE_SOA_COLUMN(Photon2Pz, photon2Pz, float); + +DECLARE_SOA_COLUMN(LambdaPx, lambdaPx, float); +DECLARE_SOA_COLUMN(LambdaPy, lambdaPy, float); +DECLARE_SOA_COLUMN(LambdaPz, lambdaPz, float); + +//______________________________________________________ +// DYNAMIC COLUMNS +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! Pi0 px + [](float photon1Px, float photon2Px, float lambdaPx) -> float { return photon1Px + photon2Px + lambdaPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! Pi0 py + [](float photon1Py, float photon2Py, float lambdaPy) -> float { return photon1Py + photon2Py + lambdaPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! Pi0 pz + [](float photon1Pz, float photon2Pz, float lambdaPz) -> float { return photon1Pz + photon2Pz + lambdaPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, + [](float photon1Px, float photon1Py, float photon2Px, float photon2Py, float lambdaPx, float lambdaPy) -> float { + return RecoDecay::pt(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! Total momentum in GeV/c + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Xi0Mass, xi0Mass, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + std::array pVecPi0{photon1Px + photon2Px, photon1Py + photon2Py, photon1Pz + photon2Pz}; + std::array pVecLambda{lambdaPx, lambdaPy, lambdaPz}; + auto arrMom = std::array{pVecPi0, pVecLambda}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassXi0}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(PxPi0, pxPi0, //! Pi0 px + [](float photon1Px, float photon2Px) -> float { return photon1Px + photon2Px; }); +DECLARE_SOA_DYNAMIC_COLUMN(PyPi0, pyPi0, //! Pi0 py + [](float photon1Py, float photon2Py) -> float { return photon1Py + photon2Py; }); +DECLARE_SOA_DYNAMIC_COLUMN(PzPi0, pzPi0, //! Pi0 pz + [](float photon1Pz, float photon2Pz) -> float { return photon1Pz + photon2Pz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0Mass, pi0Mass, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz) -> float { + std::array pVecPhoton1{photon1Px, photon1Py, photon1Pz}; + std::array pVecPhoton2{photon2Px, photon2Py, photon2Pz}; + auto arrMom = std::array{pVecPhoton1, pVecPhoton2}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Rapidity, rapidity, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::y(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz}, o2::constants::physics::MassXi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, //! Phi in the range [0, 2pi) + [](float photon1Px, float photon1Py, float photon2Px, float photon2Py, float lambdaPx, float lambdaPy) -> float { return RecoDecay::phi(photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, //! Pseudorapidity + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::eta(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Radius, radius, //! Xi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(RadiusPi0, radiusPi0, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(RadiusLambda, radiusLambda, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +// CosPAs +DECLARE_SOA_DYNAMIC_COLUMN(Pi0CosPA, pi0CosPA, //! + [](float xPi0, float yPi0, float zPi0, float pxGamma1, float pyGamma1, float pzGamma1, float pxGamma2, float pyGamma2, float pzGamma2, float pvX, float pvY, float pvZ) -> float { + return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{xPi0, yPi0, zPi0}, std::array{pxGamma1 + pxGamma2, pyGamma1 + pyGamma2, pzGamma1 + pzGamma2}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaCosPA, lambdaCosPA, //! + [](float xLambda, float yLambda, float zLambda, float pxLambda, float pyLambda, float pzLambda, float pvX, float pvY, float pvZ) -> float { + return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{xLambda, yLambda, zLambda}, std::array{pxLambda, pyLambda, pzLambda}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(CascCosPA, cascCosPA, //! + [](float X, float Y, float Z, float PxGamma1, float PxGamma2, float PxLambda, float PyGamma1, float PyGamma2, float PyLambda, float PzGamma1, float PzGamma2, float PzLambda, float pvX, float pvY, float pvZ) -> float { return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{X, Y, Z}, std::array{PxGamma1 + PxGamma2 + PxLambda, PyGamma1 + PyGamma2 + PyLambda, PzGamma1 + PzGamma2 + PzLambda}); }); + +DECLARE_SOA_DYNAMIC_COLUMN(DCALambdaToPV, dcaLambdaToPV, //! + [](float X, float Y, float Z, float px, float py, float pz, float pvX, float pvY, float pvZ) -> float { + return std::sqrt((std::pow((pvY - Y) * pz - (pvZ - Z) * py, 2) + std::pow((pvX - X) * pz - (pvZ - Z) * px, 2) + std::pow((pvX - X) * py - (pvY - Y) * px, 2)) / (px * px + py * py + pz * pz)); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(DCAPi0ToPV, dcaPi0ToPV, //! + [](float X, float Y, float Z, float pxGamma1, float pyGamma1, float pzGamma1, float pxGamma2, float pyGamma2, float pzGamma2, float pvX, float pvY, float pvZ) -> float { + float px = pxGamma1 + pxGamma2; + float py = pyGamma1 + pyGamma2; + float pz = pzGamma1 + pzGamma2; + return std::sqrt((std::pow((pvY - Y) * pz - (pvZ - Z) * py, 2) + std::pow((pvX - X) * pz - (pvZ - Z) * px, 2) + std::pow((pvX - X) * py - (pvY - Y) * px, 2)) / (px * px + py * py + pz * pz)); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Pt, photon1Pt, //! Transverse momentum in GeV/c + [](float photon1Px, float photon1Py) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px, photon1Py); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1P, photon1p, //! Total momentum in GeV/c + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px, photon1Py, photon1Pz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Eta, photon1Eta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::eta(std::array{photon1Px, photon1Py, photon1Pz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Y, photon1Y, //! Rapidity + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::y(std::array{photon1Px, photon1Py, photon1Pz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Phi, photon1Phi, //! Phi in the range [0, 2pi) + [](float photon1Px, float photon1Py) -> float { return RecoDecay::phi(photon1Px, photon1Py); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Pt, photon2Pt, //! Transverse momentum in GeV/c + [](float photon2Px, float photon2Py) -> float { + return RecoDecay::sqrtSumOfSquares(photon2Px, photon2Py); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2P, photon2p, //! Total momentum in GeV/c + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::sqrtSumOfSquares(photon2Px, photon2Py, photon2Pz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Eta, photon2Eta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::eta(std::array{photon2Px, photon2Py, photon2Pz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Y, photon2Y, //! Rapidity + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::y(std::array{photon2Px, photon2Py, photon2Pz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Phi, photon2Phi, //! Phi in the range [0, 2pi) + [](float photon2Px, float photon2Py) -> float { return RecoDecay::phi(photon2Px, photon2Py); }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaPt, lambdaPt, //! Transverse momentum in GeV/c + [](float lambdaPx, float lambdaPy) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaPx, lambdaPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaP, lambdaP, //! Total momentum in GeV/c + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaPx, lambdaPy, lambdaPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaEta, lambdaEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::eta(std::array{lambdaPx, lambdaPy, lambdaPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaY, lambdaY, //! Rapidity + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::y(std::array{lambdaPx, lambdaPy, lambdaPz}, o2::constants::physics::MassLambda); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaPhi, lambdaPhi, //! Phi in the range [0, 2pi) + [](float lambdaPx, float lambdaPy) -> float { return RecoDecay::phi(lambdaPx, lambdaPy); }); + +} // namespace Xi0Core + +DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", + Xi0Core::X, Xi0Core::Y, Xi0Core::Z, + Xi0Core::XPi0, Xi0Core::YPi0, Xi0Core::ZPi0, + Xi0Core::XLambda, Xi0Core::YLambda, Xi0Core::ZLambda, + Xi0Core::DCADaughters, Xi0Core::DCADaughtersPi0, Xi0Core::DCAXYCascToPV, Xi0Core::DCAZCascToPV, + + // Xi0 daughter momenta + Xi0Core::Photon1Px, Xi0Core::Photon1Py, Xi0Core::Photon1Pz, + Xi0Core::Photon2Px, Xi0Core::Photon2Py, Xi0Core::Photon2Pz, + Xi0Core::LambdaPx, Xi0Core::LambdaPy, Xi0Core::LambdaPz, + + // Dynamic columns + Xi0Core::Px, + Xi0Core::Py, + Xi0Core::Pz, + Xi0Core::Pt, + Xi0Core::P, + Xi0Core::Rapidity, + Xi0Core::Phi, + Xi0Core::Eta, + Xi0Core::Radius, + Xi0Core::CascCosPA, + + // Dynamic columns for pi0 + Xi0Core::PxPi0, + Xi0Core::PyPi0, + Xi0Core::PzPi0, + Xi0Core::Pi0Mass, + Xi0Core::RadiusPi0, + Xi0Core::Pi0CosPA, + Xi0Core::DCAPi0ToPV, + + // Dynamic columns for Lambda + Xi0Core::RadiusLambda, + Xi0Core::LambdaCosPA, + Xi0Core::DCALambdaToPV, + Xi0Core::LambdaPt, + Xi0Core::LambdaP, + Xi0Core::LambdaEta, + Xi0Core::LambdaY, + Xi0Core::LambdaPhi, + + // Dynamic columns for Photon1 + Xi0Core::Photon1Pt, + Xi0Core::Photon1P, + Xi0Core::Photon1Eta, + Xi0Core::Photon1Y, + Xi0Core::Photon1Phi, + + // Dynamic columns for Photon2 + Xi0Core::Photon2Pt, + Xi0Core::Photon2P, + Xi0Core::Photon2Eta, + Xi0Core::Photon2Y, + Xi0Core::Photon2Phi); + +DECLARE_SOA_TABLE(Xi0CollRefs, "AOD", "XI0COLLREFS", //! optional table to refer back to a collision + o2::soa::Index<>, v0data::StraCollisionId); + +DECLARE_SOA_TABLE(Xi0Indices, "AOD", "XI0INDICES", //! optional table to refer back to V0Cores + o2::soa::Index<>, Xi0Core::Photon1Index, Xi0Core::Photon2Index, Xi0Core::LambdaIndex); + +// for MC +namespace Xi0MCCore +{ +DECLARE_SOA_COLUMN(Photon1Index, photon1Index, int); +DECLARE_SOA_COLUMN(Photon2Index, photon2Index, int); +DECLARE_SOA_COLUMN(LambdaIndex, lambdaIndex, int); + +DECLARE_SOA_COLUMN(Photon1MCPx, photon1mcpx, float); +DECLARE_SOA_COLUMN(Photon1MCPy, photon1mcpy, float); +DECLARE_SOA_COLUMN(Photon1MCPz, photon1mcpz, float); +DECLARE_SOA_COLUMN(IsPhoton1Primary, isPhoton1Primary, bool); +DECLARE_SOA_COLUMN(Photon1PDGCodePositive, photon1PDGCodePositive, int); +DECLARE_SOA_COLUMN(Photon1PDGCodeNegative, photon1PDGCodeNegative, int); +DECLARE_SOA_COLUMN(Photon1PDGCode, photon1PDGCode, int); +DECLARE_SOA_COLUMN(Photon1PDGCodeMother, photon1PDGCodeMother, int); + +DECLARE_SOA_COLUMN(Photon2MCPx, photon2mcpx, float); +DECLARE_SOA_COLUMN(Photon2MCPy, photon2mcpy, float); +DECLARE_SOA_COLUMN(Photon2MCPz, photon2mcpz, float); +DECLARE_SOA_COLUMN(IsPhoton2Primary, isPhoton2Primary, bool); +DECLARE_SOA_COLUMN(Photon2PDGCodePositive, photon2PDGCodePositive, int); +DECLARE_SOA_COLUMN(Photon2PDGCodeNegative, photon2PDGCodeNegative, int); +DECLARE_SOA_COLUMN(Photon2PDGCode, photon2PDGCode, int); +DECLARE_SOA_COLUMN(Photon2PDGCodeMother, photon2PDGCodeMother, int); + +DECLARE_SOA_COLUMN(LambdaMCPx, lambdamcpx, float); +DECLARE_SOA_COLUMN(LambdaMCPy, lambdamcpy, float); +DECLARE_SOA_COLUMN(LambdaMCPz, lambdamcpz, float); +DECLARE_SOA_COLUMN(IsLambdaPrimary, isLambdaPrimary, bool); +DECLARE_SOA_COLUMN(LambdaPDGCodePositive, lambdaPDGCodePositive, int); +DECLARE_SOA_COLUMN(LambdaPDGCodeNegative, lambdaPDGCodeNegative, int); +DECLARE_SOA_COLUMN(LambdaPDGCode, lambdaPDGCode, int); +DECLARE_SOA_COLUMN(LambdaPDGCodeMother, lambdaPDGCodeMother, int); + +DECLARE_SOA_COLUMN(Pi0MCx, pi0mcx, float); +DECLARE_SOA_COLUMN(Pi0MCy, pi0mcy, float); +DECLARE_SOA_COLUMN(Pi0MCz, pi0mcz, float); +DECLARE_SOA_COLUMN(IsPi0Primary, isPi0Primary, bool); +DECLARE_SOA_COLUMN(Pi0MCprocess, pi0mcprocess, int); +DECLARE_SOA_COLUMN(Pi0PDGCode, pi0PDGCode, int); + +DECLARE_SOA_COLUMN(MCx, mcx, float); +DECLARE_SOA_COLUMN(MCy, mcy, float); +DECLARE_SOA_COLUMN(MCz, mcz, float); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(MCprocess, mcprocess, int); +DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); + +DECLARE_SOA_COLUMN(PDGCodeMother, pdgCodeMother, int); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPx, mcpx, //! Xi0 MC px + [](float photon1MCPx, float photon2MCPx, float lambdaMCPx) -> float { return photon1MCPx + photon2MCPx + lambdaMCPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(MCPy, mcpy, //! Xi0 MC py + [](float photon1MCPy, float photon2MCPy, float lambdaMCPy) -> float { return photon1MCPy + photon2MCPy + lambdaMCPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(MCPz, mcpz, //! Xi0 MC pz + [](float photon1MCPz, float photon2MCPz, float lambdaMCPz) -> float { return photon1MCPz + photon2MCPz + lambdaMCPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPt, mcpt, + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy, float lambdaMCPx, float lambdaMCPy) -> float { + return RecoDecay::pt(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCP, mcp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(RapidityMC, rapidityMC, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz}, o2::constants::physics::MassXi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPhi, mcPhi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy, float lambdaMCPx, float lambdaMCPy) -> float { return RecoDecay::phi(photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCEta, mcEta, //! Pseudorapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCRadius, mcRadius, //! Xi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCRadiusPi0, mcRadiusPi0, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPx, pi0mcpx, //! Pi0 MC px + [](float photon1MCPx, float photon2MCPx) -> float { return photon1MCPx + photon2MCPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPy, pi0mcpy, //! Pi0 MC py + [](float photon1MCPy, float photon2MCPy) -> float { return photon1MCPy + photon2MCPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPz, pi0mcpz, //! Pi0 MC pz + [](float photon1MCPz, float photon2MCPz) -> float { return photon1MCPz + photon2MCPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPt, pi0mcpt, + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy) -> float { + return RecoDecay::pt(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCP, pi0mcp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCMass, pi0MCMass, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + std::array pVecPhoton1{photon1MCPx, photon1MCPy, photon1MCPz}; + std::array pVecPhoton2{photon2MCPx, photon2MCPy, photon2MCPz}; + auto arrMom = std::array{pVecPhoton1, pVecPhoton2}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0RapidityMC, pi0RapidityMC, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz}, o2::constants::physics::MassPi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPhi, pi0mcphi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy) -> float { return RecoDecay::phi(photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCEta, pi0mceta, //! Pseudorapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCOPAngle, pi0mcopAngle, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) { + TVector3 v1(photon1MCPx, photon1MCPy, photon1MCPz); + TVector3 v2(photon2MCPx, photon2MCPy, photon2MCPz); + return v1.Angle(v2); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCPt, photon1MCPt, //! Transverse momentum in GeV/c + [](float photon1MCPx, float photon1MCPy) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx, photon1MCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCP, photon1MCp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx, photon1MCPy, photon1MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCEta, photon1MCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx, photon1MCPy, photon1MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCY, photon1MCY, //! Rapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx, photon1MCPy, photon1MCPz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCPhi, photon1MCPhi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy) -> float { return RecoDecay::phi(photon1MCPx, photon1MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCPt, photon2MCPt, //! Transverse momentum in GeV/c + [](float photon2MCPx, float photon2MCPy) -> float { + return RecoDecay::sqrtSumOfSquares(photon2MCPx, photon2MCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCP, photon2MCp, //! Total momentum in GeV/c + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon2MCPx, photon2MCPy, photon2MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCEta, photon2MCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::eta(std::array{photon2MCPx, photon2MCPy, photon2MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCY, photon2MCY, //! Rapidity + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::y(std::array{photon2MCPx, photon2MCPy, photon2MCPz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCPhi, photon2MCPhi, //! Phi in the range [0, 2pi) + [](float photon2MCPx, float photon2MCPy) -> float { return RecoDecay::phi(photon2MCPx, photon2MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCPt, lambdaMCPt, //! Transverse momentum in GeV/c + [](float lambdaMCPx, float lambdaMCPy) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaMCPx, lambdaMCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCP, lambdaMCp, //! Total momentum in GeV/c + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaMCPx, lambdaMCPy, lambdaMCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCEta, lambdaMCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::eta(std::array{lambdaMCPx, lambdaMCPy, lambdaMCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCY, lambdaMCY, //! Rapidity + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::y(std::array{lambdaMCPx, lambdaMCPy, lambdaMCPz}, o2::constants::physics::MassLambda); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCPhi, lambdaMCPhi, //! Phi in the range [0, 2pi) + [](float lambdaMCPx, float lambdaMCPy) -> float { return RecoDecay::phi(lambdaMCPx, lambdaMCPy); }); + +} // namespace Xi0MCCore + +DECLARE_SOA_TABLE(Xi0MCCores, "AOD", "XI0MCCORES", + // Basic properties + Xi0MCCore::Photon1MCPx, Xi0MCCore::Photon1MCPy, Xi0MCCore::Photon1MCPz, + Xi0MCCore::IsPhoton1Primary, Xi0MCCore::Photon1PDGCodePositive, Xi0MCCore::Photon1PDGCodeNegative, Xi0MCCore::Photon1PDGCode, Xi0MCCore::Photon1PDGCodeMother, + + Xi0MCCore::Photon2MCPx, Xi0MCCore::Photon2MCPy, Xi0MCCore::Photon2MCPz, + Xi0MCCore::IsPhoton2Primary, Xi0MCCore::Photon2PDGCodePositive, Xi0MCCore::Photon2PDGCodeNegative, Xi0MCCore::Photon2PDGCode, Xi0MCCore::Photon2PDGCodeMother, + + Xi0MCCore::LambdaMCPx, Xi0MCCore::LambdaMCPy, Xi0MCCore::LambdaMCPz, + Xi0MCCore::IsLambdaPrimary, Xi0MCCore::LambdaPDGCodePositive, Xi0MCCore::LambdaPDGCodeNegative, Xi0MCCore::LambdaPDGCode, Xi0MCCore::LambdaPDGCodeMother, + + Xi0MCCore::Pi0MCx, Xi0MCCore::Pi0MCy, Xi0MCCore::Pi0MCz, + Xi0MCCore::IsPi0Primary, Xi0MCCore::Pi0MCprocess, Xi0MCCore::Pi0PDGCode, + + Xi0MCCore::MCx, Xi0MCCore::MCy, Xi0MCCore::MCz, + Xi0MCCore::IsPhysicalPrimary, Xi0MCCore::MCprocess, Xi0MCCore::PDGCode, Xi0MCCore::PDGCodeMother, + + // Dynamic columns + Xi0MCCore::MCPx, + Xi0MCCore::MCPy, + Xi0MCCore::MCPz, + Xi0MCCore::MCPt, + Xi0MCCore::MCP, + Xi0MCCore::RapidityMC, + Xi0MCCore::MCPhi, + Xi0MCCore::MCEta, + + Xi0MCCore::MCRadius, + Xi0MCCore::MCRadiusPi0, + + Xi0MCCore::MCPx, + Xi0MCCore::MCPy, + Xi0MCCore::MCPz, + Xi0MCCore::MCPt, + Xi0MCCore::MCP, + Xi0MCCore::Pi0MCMass, + Xi0MCCore::Pi0RapidityMC, + Xi0MCCore::Pi0MCPhi, + Xi0MCCore::Pi0MCEta, + Xi0MCCore::Pi0MCOPAngle, + + Xi0MCCore::Photon1MCPt, + Xi0MCCore::Photon1MCP, + Xi0MCCore::Photon1MCEta, + Xi0MCCore::Photon1MCY, + Xi0MCCore::Photon1MCPhi, + + Xi0MCCore::Photon2MCPt, + Xi0MCCore::Photon2MCP, + Xi0MCCore::Photon2MCEta, + Xi0MCCore::Photon2MCY, + Xi0MCCore::Photon2MCPhi, + + Xi0MCCore::LambdaMCPt, + Xi0MCCore::LambdaMCP, + Xi0MCCore::LambdaMCEta, + Xi0MCCore::LambdaMCY, + Xi0MCCore::LambdaMCPhi); + +DECLARE_SOA_TABLE(Xi0MCCollRefs, "AOD", "XI0MCCOLLREFS", //! optional table to refer back to a collision + o2::soa::Index<>, v0data::StraMCCollisionId); + +DECLARE_SOA_TABLE(Xi0MCIndices, "AOD", "XI0MCINDICES", //! optional table to refer back to V0MCCores + o2::soa::Index<>, Xi0MCCore::Photon1Index, Xi0MCCore::Photon2Index, Xi0MCCore::LambdaIndex); + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFSIGMATABLES_H_ diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 643c63aa9c0..a61a01e6a56 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -149,7 +149,7 @@ o2physics_add_dpl_workflow(cascademlselection o2physics_add_dpl_workflow(sigma0builder SOURCES sigma0builder.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationbuilder # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) diff --git a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx index 214259f75c4..090103f41db 100644 --- a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx @@ -25,10 +25,13 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" +#include "Common/Tools/StandardCCDBLoader.h" #include #include #include +#include +#include #include #include #include @@ -40,6 +43,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include @@ -73,7 +77,10 @@ static const std::vector DirList = {"V0BeforeSel", "PhotonSel", "La static const std::vector DirList2 = {"EMCalPhotonBeforeSel", "EMCalPhotonSel"}; struct sigma0builder { + o2::common::StandardCCDBLoaderConfigurables standardCCDBLoaderConfigurables; + o2::common::StandardCCDBLoader ccdbLoader; Service ccdb; + int mRunNumber = 0; ctpRateFetcher rateFetcher; //___________________________________________________ @@ -105,6 +112,15 @@ struct sigma0builder { Produces pi0Gens; // Generated pi0s Produces pi0GenCollRefs; // references collisions from pi0Gens + //__________________________________________________ + // Xi0 specific + Produces xi0cores; // xi0 candidates info for analysis + Produces xi0collRefs; // references to straCollision + Produces xi0Indices; // references to V0Cores + Produces xi0mccores; // Reco xi0 MC properties + Produces xi0mccollRefs; // references to straMCCollision + Produces xi0mcIndices; // references to V0MCCores + //__________________________________________________ // pack track quality but separte also afterburner // dynamic range: 0-31 @@ -176,6 +192,7 @@ struct sigma0builder { Configurable fillPi0Tables{"fillPi0Tables", false, "fill pi0 tables for QA"}; Configurable fillSigma0Tables{"fillSigma0Tables", true, "fill sigma0 tables for analysis"}; Configurable fillKStarTables{"fillKStarTables", true, "fill kstar tables for analysis"}; + Configurable fillXi0Tables{"fillXi0Tables", false, "fill xi0 tables for analysis"}; // For ML Selection Configurable useMLScores{"useMLScores", false, "use ML scores to select candidates"}; @@ -284,6 +301,19 @@ struct sigma0builder { Configurable KShortMaxTPCNSigmas{"KShortMaxTPCNSigmas", 1e+9, "Max |TPC NSigma| (pion hypothesis) for K0S daughters"}; } kshortSelections; + // Xi0 criteria: + struct : ConfigurableGroup { + std::string prefix = "cascadeSelections"; // JSON group name + Configurable maxPi0Rapidity{"maxPi0Rapidity", 1.0, "Maximum rapidity of pi0"}; + Configurable massPi0Window{"massPi0Window", 0.115, "Maximum pi0 mass (GeV/c)"}; + Configurable dcaPi0daughters{"dcaPi0daughters", 1., "Maximum DCA between pi0 daughters"}; + Configurable radiusPi0{"radiusPi0", 2.5, "Max DCA V0 Daughters (cm)"}; + Configurable dcaPi0ToPV{"dcaPi0ToPV", 0.0, "Minimum Pi0 DCA to PV(cm)"}; + Configurable dcaCascadeDaughters{"dcaCascadeDaughters", 1., "Maximum DCA between cascade daughters"}; + Configurable radiusCascade{"radiusCascade", 2.5, "Minimum cascade decay radius (cm)"}; + Configurable cosPACascade{"cosPACascade", 0.95, "Min cascade CosPA"}; + } cascadeSelections; + // KStar criteria: Configurable KStarWindow{"KStarWindow", 0.1, "Mass window around expected (in GeV/c2)"}; Configurable KStarMaxRap{"KStarMaxRap", 0.8, "Max kstar rapidity"}; @@ -315,11 +345,13 @@ struct sigma0builder { ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "N_{ch}"}; // Invariant Mass - ConfigurableAxis axisSigmaMass{"axisSigmaMass", {500, 1.10f, 1.30f}, "M_{#Sigma^{0}} (GeV/c^{2})"}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "M_{#Lambda} (GeV/c^{2})"}; - ConfigurableAxis axisPhotonMass{"axisPhotonMass", {200, 0.0f, 0.3f}, "M_{#Gamma}"}; - ConfigurableAxis axisK0SMass{"axisK0SMass", {200, 0.4f, 0.6f}, "M_{K^{0}}"}; - ConfigurableAxis axisKStarMass{"axisKStarMass", {500, 0.6f, 1.6f}, "M_{K^{*}} (GeV/c^{2})"}; + ConfigurableAxis axisSigmaMass{"axisSigmaMass", {500, 1.10f, 1.30f}, "#it{M}_{#Sigma^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "#it{M}_{#Lambda} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPhotonMass{"axisPhotonMass", {200, 0.0f, 0.3f}, "#it{M}_{#Gamma} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisK0SMass{"axisK0SMass", {200, 0.4f, 0.6f}, "#it{M}_{K^{0}_{S}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisKStarMass{"axisKStarMass", {500, 0.6f, 1.6f}, "#it{M}_{K^{*}} (GeV/#it{c}}^{2})"}; + ConfigurableAxis axisPi0Mass{"axisPi0Mass", {100, 0.08f, 0.18f}, "#it{M}_{#pi^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisXi0Mass{"axisXi0Mass", {100, 1.27f, 1.37f}, "#it{M}_{#Xi^{0}} (GeV/#it{c}^{2})"}; // AP plot axes ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; @@ -350,6 +382,30 @@ struct sigma0builder { ConfigurableAxis axisClrShape{"axisClrShape", {100, 0.0, 1.0}, "cluster shape"}; } axisConfig; + struct : ConfigurableGroup { + std::string prefix = "fitterConfiguration"; // JSON group name + Configurable propagateToPCA{"propagateToPCA", true, "Propagate to PCA?"}; + Configurable minParamChange{"minParamChange", 4., "Stop minimization iterations if largest change of any X is smaller than this."}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "Stop iterations is chi2/chi2old > this"}; + Configurable maxR{"maxR", 200., "Don't consider as a seed (circles intersection) if its R exceeds this"}; + Configurable maxDXYIni{"maxDXYIni", 4.0f, "Don't consider as a seed (circles intersection) if XY distance exceeds this"}; + Configurable maxDZIni{"maxDZIni", 1e9, "Don't consider as a seed (circles intersection) if Z distance exceeds this"}; + Configurable maxChi2{"maxChi2", 1e9, "Max dca from prongs to vertex"}; + Configurable useAbsDCA{"useAbsDCA", true, "Use abs dca minimization"}; + Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Max generated particle rapidity"}; + } fitterConfiguration; + + // CCDB options + struct : ConfigurableGroup { + // manual + Configurable useCustomRunNumber{"ccdbConfigurations.useCustomRunNumber", false, "Use custom run number"}; + Configurable customRunNumber{"ccdbConfigurations.customRunNumber", 544122, "Manually set the run number "}; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + } ccdbConfigurations; + + o2::vertexing::DCAFitterN<2> fitter2Prongs; // 2-prong o2 dca fitter + o2::vertexing::DCAFitterN<3> fitter3Prongs; // 3-prong o2 dca fitter + void init(InitContext const&) { LOGF(info, "Initializing now: cross-checking correctness..."); @@ -366,9 +422,9 @@ struct sigma0builder { } // setting CCDB service - ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); - ccdb->setFatalWhenNull(false); + ccdb->setLocalObjectValidityChecking(); + ccdb->setURL(ccdbConfigurations.ccdburl.value); histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); @@ -562,6 +618,11 @@ struct sigma0builder { histos.add("KStarSel/hKStarMassSelected", "hKStarMassSelected", kTH1F, {axisConfig.axisKStarMass}); } + if (fillXi0Tables) { + histos.add("Xi0Sel/hPi0Mass", "hPi0Mass", kTH1F, {axisConfig.axisPi0Mass}); + histos.add("Xi0Sel/hXi0Mass", "hXi0Mass", kTH1F, {axisConfig.axisXi0Mass}); + } + if (doAssocStudy && (doprocessMonteCarlo || doprocessMonteCarloWithTOF)) { histos.add("V0AssoQA/h2dIRVsPt_TrueGamma", "h2dIRVsPt_TrueGamma", kTH2F, {axisConfig.axisIRBinning, axisConfig.axisPt}); histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueGamma", "h3dPAVsIRVsPt_TrueGamma", kTH3F, {axisConfig.axisPA, axisConfig.axisIRBinning, axisConfig.axisPt}); @@ -782,18 +843,66 @@ struct sigma0builder { histos.add("V0QA/h2dGenKShortVsMultMC", "h2dGenKShortVsMultMC", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); } + fitter2Prongs.setPropagateToPCA(fitterConfiguration.propagateToPCA); + fitter2Prongs.setMaxR(fitterConfiguration.maxR); + fitter2Prongs.setMinParamChange(fitterConfiguration.minParamChange); + fitter2Prongs.setMinRelChi2Change(fitterConfiguration.minRelChi2Change); + fitter2Prongs.setMaxDZIni(fitterConfiguration.maxDZIni); + fitter2Prongs.setMaxDXYIni(fitterConfiguration.maxDXYIni); + fitter2Prongs.setMaxChi2(fitterConfiguration.maxChi2); + fitter2Prongs.setUseAbsDCA(fitterConfiguration.useAbsDCA); + fitter2Prongs.setWeightedFinalPCA(fitterConfiguration.useWeightedFinalPCA); + + fitter3Prongs.setPropagateToPCA(fitterConfiguration.propagateToPCA); + fitter3Prongs.setMaxR(fitterConfiguration.maxR); + fitter3Prongs.setMinParamChange(fitterConfiguration.minParamChange); + fitter3Prongs.setMinRelChi2Change(fitterConfiguration.minRelChi2Change); + fitter3Prongs.setMaxDZIni(fitterConfiguration.maxDZIni); + fitter3Prongs.setMaxDXYIni(fitterConfiguration.maxDXYIni); + fitter3Prongs.setMaxChi2(fitterConfiguration.maxChi2); + fitter3Prongs.setUseAbsDCA(fitterConfiguration.useAbsDCA); + fitter3Prongs.setWeightedFinalPCA(fitterConfiguration.useWeightedFinalPCA); + + fitter2Prongs.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + fitter3Prongs.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + + // mag field has to be set later + fitter2Prongs.setBz(-999.9f); // will NOT make sense if not changed + fitter3Prongs.setBz(-999.9f); // will NOT make sense if not changed + // inspect histogram sizes, please histos.print(); } + template // TCollision should be of the type: soa::Join::iterator or so + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber() || (ccdbConfigurations.useCustomRunNumber && mRunNumber == ccdbConfigurations.customRunNumber)) { + return; + } + + mRunNumber = ccdbConfigurations.useCustomRunNumber ? ccdbConfigurations.customRunNumber : collision.runNumber(); + + ccdbLoader.initCCDB(standardCCDBLoaderConfigurables, ccdb, mRunNumber); + + auto magneticField = o2::base::Propagator::Instance()->getNominalBz(); + // Set magnetic field value once known + fitter2Prongs.setBz(magneticField); + fitter3Prongs.setBz(magneticField); + } + // ______________________________________________________ // Struct to store V0Pair properties struct V0PairTopoInfo { - float X = -999.f; - float Y = -999.f; - float Z = -999.f; - float DCADau = -999.f; - float CosPA = -1.f; + std::array position{-999.f, -999.f, -999.f}; + float dcaPi0ToPV = 999.f; + float daughterDCA = 999.f; + float cosPA = -1.f; + float pi0Mass = 999.f; + float pi0Y = 999.f; + + int v01Index = 0; // index to de-reference V0Cores table + int v02Index = 0; // index to de-reference V0Cores table }; // ______________________________________________________ @@ -856,6 +965,79 @@ struct sigma0builder { float MCvy = 999.f; }; + // ______________________________________________________ + // Struct to store V0Pair properties + struct Xi0Info { + std::array gamma1Momentum{999.f, 999.f, 999.f}; + std::array gamma2Momentum{999.f, 999.f, 999.f}; + std::array lambdaMomentum{999.f, 999.f, 999.f}; + std::array pi0Position{0., 0., 0.}; + std::array cascadePosition{0., 0., 0.}; + float xi0Mass = 999.f; + float dcaPi0ToPV = 999.f; + float pi0DaughterDCA = 999.f; + float cascadeDaughterDCA = 999.f; + float cascadeDCAxy = 999.f; + float cascadeDCAz = 999.f; + + int collisionId = 0; + int gamma1Index = 0; // index to de-reference V0Cores table + int gamma2Index = 0; // index to de-reference V0Cores table + int lambdaIndex = 0; // index to de-reference V0Cores table + + std::array covariance{0.}; + }; + + struct Xi0MCInfo { + int gamma1Index = 0; // index to de-reference V0MCCores table + int gamma2Index = 0; // index to de-reference V0MCCores table + int lambdaIndex = 0; // index to de-reference V0MCCores table + + // Basic kinematic info + float gamma1MCpx = -999.f; + float gamma1MCpy = -999.f; + float gamma1MCpz = -999.f; + float gamma2MCpx = -999.f; + float gamma2MCpy = -999.f; + float gamma2MCpz = -999.f; + float lambdaMCpx = -999.f; + float lambdaMCpy = -999.f; + float lambdaMCpz = -999.f; + + // MC association info + bool gamma1IsPhysicalPrimary = false; + bool gamma2IsPhysicalPrimary = false; + bool lambdaIsPhysicalPrimary = false; + int gamma1PDGCodePos = 0; + int gamma1PDGCodeNeg = 0; + int gamma2PDGCodePos = 0; + int gamma2PDGCodeNeg = 0; + int lambdaPDGCodePos = 0; + int lambdaPDGCodeNeg = 0; + int gamma1PDGCode = 0; + int gamma2PDGCode = 0; + int lambdaPDGCode = 0; + int gamma1PDGCodeMother = 0; + int gamma2PDGCodeMother = 0; + int lambdaPDGCodeMother = 0; + + int pi0PDGCode = 0; + int pi0MCProcess = 0; + bool pi0IsPhysicalPrimary = false; + float pi0X = -999.f; + float pi0Y = -999.f; + float pi0Z = -999.f; + + int xi0PDGCode = 0; + int xi0MCProcess = 0; + bool xi0IsPhysicalPrimary = false; + float xi0X = -999.f; + float xi0Y = -999.f; + float xi0Z = -999.f; + + int xi0PDGCodeMother = 0; + }; + template V0PairTopoInfo propagateV0PairToDCA(TV01 const& v01, TV02 const& v02) { @@ -886,18 +1068,18 @@ struct sigma0builder { ROOT::Math::XYZVector PCA = 0.5 * (pointOn1 + pointOn2); // Calculate properties and fill struct - info.DCADau = (cross.Mag2() > 0) ? std::abs(posdiff.Dot(cross)) / cross.R() : 999.f; - info.CosPA = v01momentumNorm.Dot(v02momentumNorm); + info.daughterDCA = (cross.Mag2() > 0) ? std::abs(posdiff.Dot(cross)) / cross.R() : 999.f; + info.cosPA = v01momentumNorm.Dot(v02momentumNorm); float Min_threshold = 1e-5f; // Threshold to consider lines as parallel, can be tuned if (d < Min_threshold) { // Parallel or nearly parallel lines - info.X = info.Y = info.Z = 0.f; // should we use another dummy value? Perhaps 999.f? + info.position[0] = info.position[1] = info.position[2] = 0.f; // should we use another dummy value? Perhaps 999.f? return info; } - info.X = PCA.X(); - info.Y = PCA.Y(); - info.Z = PCA.Z(); + info.position[0] = PCA.X(); + info.position[1] = PCA.Y(); + info.position[2] = PCA.Z(); return info; } @@ -1249,6 +1431,105 @@ struct sigma0builder { return MCinfo; } + template + Xi0MCInfo getXi0MCInfo(TV0 const& gamma1, TV0 const& gamma2, TV0 const& lambda, TMCParticles const& mcparticles) + { + Xi0MCInfo MCinfo; + + if (!gamma1.has_v0MCCore() || !gamma2.has_v0MCCore() || !lambda.has_v0MCCore()) { + return MCinfo; + } + + auto gamma1MC = gamma1.template v0MCCore_as>(); + auto gamma2MC = gamma2.template v0MCCore_as>(); + auto lambdaMC = lambda.template v0MCCore_as>(); + + // Basic kinematic info + MCinfo.gamma1MCpx = gamma1MC.pxMC(); + MCinfo.gamma1MCpy = gamma1MC.pyMC(); + MCinfo.gamma1MCpz = gamma1MC.pzMC(); + MCinfo.gamma2MCpx = gamma2MC.pxMC(); + MCinfo.gamma2MCpy = gamma2MC.pyMC(); + MCinfo.gamma2MCpz = gamma2MC.pzMC(); + MCinfo.lambdaMCpx = lambdaMC.pxMC(); + MCinfo.lambdaMCpy = lambdaMC.pyMC(); + MCinfo.lambdaMCpz = lambdaMC.pzMC(); + + // MC association info + MCinfo.gamma1Index = gamma1MC.globalIndex(); + MCinfo.gamma2Index = gamma2MC.globalIndex(); + MCinfo.lambdaIndex = lambdaMC.globalIndex(); + MCinfo.gamma1IsPhysicalPrimary = gamma1MC.isPhysicalPrimary(); + MCinfo.gamma2IsPhysicalPrimary = gamma2MC.isPhysicalPrimary(); + MCinfo.lambdaIsPhysicalPrimary = lambdaMC.isPhysicalPrimary(); + MCinfo.gamma1PDGCodePos = gamma1MC.pdgCodePositive(); + MCinfo.gamma1PDGCodeNeg = gamma1MC.pdgCodeNegative(); + MCinfo.gamma2PDGCodePos = gamma2MC.pdgCodePositive(); + MCinfo.gamma2PDGCodeNeg = gamma2MC.pdgCodeNegative(); + MCinfo.lambdaPDGCodePos = lambdaMC.pdgCodePositive(); + MCinfo.lambdaPDGCodeNeg = lambdaMC.pdgCodeNegative(); + MCinfo.gamma1PDGCode = gamma1MC.pdgCode(); + MCinfo.gamma2PDGCode = gamma2MC.pdgCode(); + MCinfo.lambdaPDGCode = lambdaMC.pdgCode(); + + // Get corresponding entries in MCParticles table + auto MCParticle_gamma1 = mcparticles.rawIteratorAt(gamma1MC.particleIdMC()); + auto MCParticle_gamma2 = mcparticles.rawIteratorAt(gamma2MC.particleIdMC()); + auto MCParticle_lambda = mcparticles.rawIteratorAt(lambdaMC.particleIdMC()); + + if ((std::abs(MCParticle_gamma1.pdgCode()) == PDG_t::kGamma) && + (std::abs(MCParticle_gamma2.pdgCode()) == PDG_t::kGamma) && + (std::abs(MCParticle_lambda.pdgCode()) == PDG_t::kLambda0)) { + + // Get MC Mothers + auto const& MCMothersList_gamma1 = MCParticle_gamma1.template mothers_as(); + auto const& MCMothersList_gamma2 = MCParticle_gamma2.template mothers_as(); + auto const& MCMothersList_lambda = MCParticle_lambda.template mothers_as(); + + if (!MCMothersList_gamma1.empty() && !MCMothersList_gamma2.empty() && !MCMothersList_lambda.empty()) { // Are there mothers? + auto const& MCMother_gamma1 = MCMothersList_gamma1.front(); // First mother + auto const& MCMother_gamma2 = MCMothersList_gamma2.front(); // First mother + auto const& MCMother_lambda = MCMothersList_lambda.front(); // First mother + + MCinfo.gamma1PDGCodeMother = MCMother_gamma1.pdgCode(); + MCinfo.gamma2PDGCodeMother = MCMother_gamma2.pdgCode(); + MCinfo.lambdaPDGCodeMother = MCMother_lambda.pdgCode(); + + if (MCMother_gamma1.globalIndex() == MCMother_gamma2.globalIndex() && std::abs(MCinfo.gamma2PDGCodeMother) == PDG_t::kPi0) { // check that gamma1 and gamma2 have the same pi0 mother + + MCinfo.pi0PDGCode = MCMother_gamma1.pdgCode(); + MCinfo.pi0MCProcess = MCMother_gamma1.getProcess(); + MCinfo.pi0IsPhysicalPrimary = MCMother_gamma1.isPhysicalPrimary(); + MCinfo.pi0X = MCMother_gamma1.vx(); + MCinfo.pi0Y = MCMother_gamma1.vy(); + MCinfo.pi0Z = MCMother_gamma1.vz(); + + auto const& MCMothersList_pi0 = MCMother_gamma1.template mothers_as(); // get pi0 mother list + if (!MCMothersList_pi0.empty()) { // Are there mothers? + auto const& MCMother_pi0 = MCMothersList_pi0.front(); // get pi0 mother + + if (MCMother_pi0.globalIndex() == MCMother_lambda.globalIndex()) { // check that lambda and pi0 have the same mother. + MCinfo.xi0PDGCode = MCMother_pi0.pdgCode(); + MCinfo.xi0MCProcess = MCMother_pi0.getProcess(); + MCinfo.pi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); + MCinfo.xi0X = MCMother_pi0.vx(); + MCinfo.xi0Y = MCMother_pi0.vy(); + MCinfo.xi0Z = MCMother_pi0.vz(); + + auto const& v0pairmothers = MCMother_pi0.template mothers_as(); // Get mothers + if (!v0pairmothers.empty()) { + auto& v0PairMother = v0pairmothers.front(); // V0Pair mother, V0s grandmother + MCinfo.xi0PDGCodeMother = v0PairMother.pdgCode(); + } + } + } + } + } + } + + return MCinfo; + } + // ______________________________________________________ // Check whether the collision passes our collision selections // Should work with collisions, mccollisions, stracollisions and stramccollisions tables! @@ -1629,9 +1910,9 @@ struct sigma0builder { GenInfo.IsPi0 = mcParticle.pdgCode() == PDG_t::kPi0; // 111; GenInfo.IsSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0; // PDG_t::kSigma0 GenInfo.IsAntiSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0Bar; //-3212 - GenInfo.IsKStar = std::abs(mcParticle.pdgCode()) == o2::constants::physics::Pdg::kK0Star892; // 313; GenInfo.IsLambdaStar = mcParticle.pdgCode() == 3124; // 102134 (PYTHIA8) GenInfo.IsAntiLambdaStar = mcParticle.pdgCode() == -3124; // -102134 + GenInfo.IsKStar = std::abs(mcParticle.pdgCode()) == o2::constants::physics::Pdg::kK0Star892; // 313; GenInfo.IsProducedByGenerator = mcParticle.producedByGenerator(); GenInfo.MCProcess = mcParticle.getProcess(); GenInfo.MCPt = mcParticle.pt(); @@ -2310,7 +2591,7 @@ struct sigma0builder { pi0MCInfo.fIsV02Primary, pi0MCInfo.V02PDGCode, pi0MCInfo.V02PDGCodeMother, pi0MCInfo.fIsV02CorrectlyAssign); } - pi0cores(pi0TopoInfo.X, pi0TopoInfo.Y, pi0TopoInfo.Z, pi0TopoInfo.DCADau, pi0TopoInfo.CosPA, + pi0cores(pi0TopoInfo.position[0], pi0TopoInfo.position[1], pi0TopoInfo.position[2], pi0TopoInfo.daughterDCA, pi0TopoInfo.cosPA, gamma1.px(), gamma1.py(), gamma1.pz(), gamma1.mGamma(), gamma1.qtarm(), gamma1.alpha(), gamma1.dcapostopv(), gamma1.dcanegtopv(), gamma1.dcaV0daughters(), gamma1.negativeeta(), gamma1.positiveeta(), gamma1.v0cosPA(), gamma1.v0radius(), gamma1.z(), @@ -2325,6 +2606,274 @@ struct sigma0builder { return true; } + //_______________________________________________ + // Build pi0 candidate for QA + template + bool buildPi0ForXi0(TV0Object const& gamma1, TV0Object const& gamma2, TCollision const& collision, V0PairTopoInfo& info) + { + //_______________________________________________ + // Check if both V0s are made of the same tracks + if (gamma1.posTrackExtraId() == gamma2.posTrackExtraId() || + gamma1.negTrackExtraId() == gamma2.negTrackExtraId()) { + return false; + } + + //_______________________________________________ + // Calculate pi0 properties + const std::array vtxGamma1 = {gamma1.x(), gamma1.y(), gamma1.z()}; + const std::array vtxGamma2 = {gamma2.x(), gamma2.y(), gamma2.z()}; + + const std::array momGamma1 = {gamma1.px(), gamma1.py(), gamma1.pz()}; + const std::array momGamma2 = {gamma2.px(), gamma2.py(), gamma2.pz()}; + + const std::array covGamma1 = {999.}; + const std::array covGamma2 = {999.}; + + std::array pVecGamma1{gamma1.px(), gamma1.py(), gamma1.pz()}; + std::array pVecGamma2{gamma2.px(), gamma2.py(), gamma2.pz()}; + std::array arrpi0{pVecGamma1, pVecGamma2}; + info.pi0Mass = RecoDecay::m(arrpi0, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + info.pi0Y = RecoDecay::y(std::array{gamma1.px() + gamma2.px(), gamma1.py() + gamma2.py(), gamma1.pz() + gamma2.pz()}, o2::constants::physics::MassPi0); + + info.v01Index = gamma1.globalIndex(); + info.v02Index = gamma2.globalIndex(); + + //_______________________________________________ + // Pi0-specific selections: + if (std::abs(info.pi0Y) > cascadeSelections.maxPi0Rapidity) + return false; + + if (std::abs(info.pi0Mass - o2::constants::physics::MassPi0) > cascadeSelections.massPi0Window) + return false; + + o2::track::TrackParCov gammaTrack1(vtxGamma1, momGamma1, covGamma1, 0, true); + gammaTrack1.setAbsCharge(0); + gammaTrack1.setPID(o2::track::PID::Photon); + o2::track::TrackParCov gammaTrack2(vtxGamma2, momGamma2, covGamma2, 0, true); + gammaTrack2.setAbsCharge(0); + gammaTrack2.setPID(o2::track::PID::Photon); + + // First build Pi0 to select secondary pi0s + int nCandPi0 = 0; + try { + nCandPi0 = fitter2Prongs.process(gammaTrack1, gammaTrack2); + } catch (...) { + return false; + } + if (nCandPi0 == 0) { + return false; + } + + fitter2Prongs.propagateTracksToVertex(); // propagate e and K to D vertex + if (!fitter2Prongs.isPropagateTracksToVertexDone()) { + return false; + } + + info.daughterDCA = TMath::Sqrt(fitter2Prongs.getChi2AtPCACandidate()); + + if (info.daughterDCA > cascadeSelections.dcaPi0daughters) { + return false; + } + + // get decay vertex coordinates + const auto& vtxPi0 = fitter2Prongs.getPCACandidate(); + for (int i = 0; i < 3; i++) { + info.position[i] = vtxPi0[i]; + } + if (std::hypot(info.position[0], info.position[1]) < cascadeSelections.radiusPi0) { + return false; + } + + float x = vtxPi0[0]; + float y = vtxPi0[1]; + float z = vtxPi0[2]; + float px = pVecGamma1[0] + pVecGamma2[0]; + float py = pVecGamma1[1] + pVecGamma2[1]; + float pz = pVecGamma1[2] + pVecGamma2[2]; + info.dcaPi0ToPV = std::sqrt((std::pow((collision.posY() - y) * pz - (collision.posZ() - z) * py, 2) + std::pow((collision.posX() - x) * pz - (collision.posZ() - z) * px, 2) + std::pow((collision.posX() - x) * py - (collision.posY() - y) * px, 2)) / (px * px + py * py + pz * pz)); + if (info.dcaPi0ToPV < cascadeSelections.dcaPi0ToPV) { + return false; + } + + histos.fill(HIST("Xi0Sel/hPi0Mass"), info.pi0Mass); + + return true; + } + + //_______________________________________________ + // Build Xi0 candidate for analysis + template + bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, TCollision const& collision, TMCParticles const& mcparticles) + { + Xi0Info cascade; + //_______________________________________________ + // Check if the 3 V0s are made of the same tracks + if (gamma1.posTrackExtraId() == gamma2.posTrackExtraId() || + gamma1.negTrackExtraId() == gamma2.negTrackExtraId() || + gamma1.posTrackExtraId() == lambda.posTrackExtraId() || + gamma1.negTrackExtraId() == lambda.negTrackExtraId() || + gamma2.posTrackExtraId() == lambda.posTrackExtraId() || + gamma2.negTrackExtraId() == lambda.negTrackExtraId()) { + return false; + } + + const std::array vtxGamma1 = {gamma1.x(), gamma1.y(), gamma1.z()}; + const std::array vtxGamma2 = {gamma2.x(), gamma2.y(), gamma2.z()}; + const std::array vtxLambda = {lambda.x(), lambda.y(), lambda.z()}; + + const std::array momGamma1 = {gamma1.px(), gamma1.py(), gamma1.pz()}; + const std::array momGamma2 = {gamma2.px(), gamma2.py(), gamma2.pz()}; + const std::array momLambda = {lambda.px(), lambda.py(), lambda.pz()}; + + const std::array covGamma1 = {999.}; + const std::array covGamma2 = {999.}; + const std::array covLambda = {999.}; + + o2::track::TrackParCov gammaTrack1(vtxGamma1, momGamma1, covGamma1, 0, true); + gammaTrack1.setAbsCharge(0); + gammaTrack1.setPID(o2::track::PID::Photon); + o2::track::TrackParCov gammaTrack2(vtxGamma2, momGamma2, covGamma2, 0, true); + gammaTrack2.setAbsCharge(0); + gammaTrack2.setPID(o2::track::PID::Photon); + o2::track::TrackParCov lambdaTrack(vtxLambda, momLambda, covLambda, 0, true); + lambdaTrack.setAbsCharge(0); + lambdaTrack.setPID(o2::track::PID::Lambda); + + // build Xi0 --> pi0 Lambda --> gamma gamma Lambda + int nCandXi0 = 0; + try { + nCandXi0 = fitter3Prongs.process(gammaTrack1, gammaTrack2, lambdaTrack); + } catch (...) { + return false; + } + if (nCandXi0 == 0) { + return false; + } + + fitter3Prongs.propagateTracksToVertex(); // propagate e and K to D vertex + if (!fitter3Prongs.isPropagateTracksToVertexDone()) { + return false; + } + + auto lGamma1Track = fitter3Prongs.getTrack(0); + auto lGamma2Track = fitter3Prongs.getTrack(1); + auto lLambdaTrack = fitter3Prongs.getTrack(2); + + cascade.cascadeDaughterDCA = TMath::Sqrt(fitter3Prongs.getChi2AtPCACandidate()); + if (cascade.cascadeDaughterDCA > cascadeSelections.dcaCascadeDaughters) { + return false; + } + + lGamma1Track.getPxPyPzGlo(cascade.gamma1Momentum); + lGamma2Track.getPxPyPzGlo(cascade.gamma2Momentum); + lLambdaTrack.getPxPyPzGlo(cascade.lambdaMomentum); + // get decay vertex coordinates + const auto& vtx = fitter3Prongs.getPCACandidate(); + for (int i = 0; i < 3; i++) { + cascade.cascadePosition[i] = vtx[i]; + } + if (std::hypot(cascade.cascadePosition[0], cascade.cascadePosition[1]) < cascadeSelections.radiusCascade) { + return false; + } + + double cosPA = RecoDecay::cpa( + std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2]}, + std::array{cascade.gamma1Momentum[0] + cascade.gamma2Momentum[0] + cascade.lambdaMomentum[0], + cascade.gamma1Momentum[1] + cascade.gamma2Momentum[1] + cascade.lambdaMomentum[1], + cascade.gamma1Momentum[2] + cascade.gamma2Momentum[2] + cascade.lambdaMomentum[2]}); + if (cosPA < cascadeSelections.cosPACascade) { + return false; + } + + // Calculate DCAxy of the cascade (with bending) + auto lCascadeTrack = fitter3Prongs.createParentTrackParCov(); + lCascadeTrack.setAbsCharge(0); // to be sure + lCascadeTrack.setPID(o2::track::PID::XiMinus); // FIXME: not OK for omegas + std::array dcaInfo{999.f, 999.f}; + + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, lCascadeTrack, 2.f, fitter3Prongs.getMatCorrType(), &dcaInfo); + cascade.cascadeDCAxy = dcaInfo[0]; + cascade.cascadeDCAz = dcaInfo[1]; + + // Populate information + // cascadecandidate.v0Id = v0index.globalIndex(); + cascade.collisionId = collision.globalIndex(); + cascade.gamma1Index = gamma1.globalIndex(); + cascade.gamma2Index = gamma2.globalIndex(); + cascade.lambdaIndex = lambda.globalIndex(); + + std::array momPi0{gamma1.px() + gamma2.px(), gamma1.py() + gamma2.py(), gamma1.pz() + gamma2.pz()}; + auto arrMom = std::array{momPi0, momLambda}; + cascade.xi0Mass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassXi0}); + + // Calculate position covariance matrix + auto covVtxV = fitter3Prongs.calcPCACovMatrix(0); + // std::array positionCovariance; + float positionCovariance[6]; + positionCovariance[0] = covVtxV(0, 0); + positionCovariance[1] = covVtxV(1, 0); + positionCovariance[2] = covVtxV(1, 1); + positionCovariance[3] = covVtxV(2, 0); + positionCovariance[4] = covVtxV(2, 1); + positionCovariance[5] = covVtxV(2, 2); + // store momentum covariance matrix + std::array covTgamma1 = {0.}; + std::array covTgamma2 = {0.}; + std::array covTlambda = {0.}; + // std::array momentumCovariance; + lGamma1Track.getCovXYZPxPyPzGlo(covTgamma1); + lGamma2Track.getCovXYZPxPyPzGlo(covTgamma2); + lLambdaTrack.getCovXYZPxPyPzGlo(covTlambda); + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 21; i++) { + cascade.covariance[i] = 0.0f; + } + for (int i = 0; i < 6; i++) { + cascade.covariance[i] = positionCovariance[i]; + cascade.covariance[MomInd[i]] = covTgamma1[MomInd[i]] + covTgamma2[MomInd[i]] + covTlambda[MomInd[i]]; + } + + // Check if MC data and populate corresponding table + if constexpr (requires { collision.straMCCollisionId(); gamma1.motherMCPartId(); gamma2.motherMCPartId(); lambda.motherMCPartId(); }) { + auto xi0MCInfo = getXi0MCInfo(gamma1, gamma2, lambda, mcparticles); + + xi0mccores(xi0MCInfo.gamma1MCpx, xi0MCInfo.gamma1MCpy, xi0MCInfo.gamma1MCpz, + xi0MCInfo.gamma1IsPhysicalPrimary, xi0MCInfo.gamma1PDGCodePos, xi0MCInfo.gamma1PDGCodeNeg, xi0MCInfo.gamma1PDGCode, xi0MCInfo.gamma1PDGCodeMother, + xi0MCInfo.gamma2MCpx, xi0MCInfo.gamma2MCpy, xi0MCInfo.gamma2MCpz, + xi0MCInfo.gamma2IsPhysicalPrimary, xi0MCInfo.gamma2PDGCodePos, xi0MCInfo.gamma2PDGCodeNeg, xi0MCInfo.gamma2PDGCode, xi0MCInfo.gamma2PDGCodeMother, + xi0MCInfo.lambdaMCpx, xi0MCInfo.lambdaMCpy, xi0MCInfo.lambdaMCpz, + xi0MCInfo.lambdaIsPhysicalPrimary, xi0MCInfo.lambdaPDGCodePos, xi0MCInfo.lambdaPDGCodeNeg, xi0MCInfo.lambdaPDGCode, xi0MCInfo.lambdaPDGCodeMother, + xi0MCInfo.pi0X, xi0MCInfo.pi0Y, xi0MCInfo.pi0Z, xi0MCInfo.pi0IsPhysicalPrimary, xi0MCInfo.pi0MCProcess, xi0MCInfo.pi0PDGCode, + xi0MCInfo.xi0X, xi0MCInfo.xi0Y, xi0MCInfo.xi0Z, xi0MCInfo.xi0IsPhysicalPrimary, xi0MCInfo.xi0MCProcess, xi0MCInfo.xi0PDGCode, xi0MCInfo.xi0PDGCodeMother); + + xi0mcIndices(xi0MCInfo.gamma1Index, xi0MCInfo.gamma2Index, xi0MCInfo.lambdaIndex); + + int mcCollisionIndex = -1; + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as>(); + mcCollisionIndex = mcCollision.globalIndex(); + } + xi0mccollRefs(mcCollisionIndex); + } + + xi0cores(cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2], + cascade.pi0Position[0], cascade.pi0Position[1], cascade.pi0Position[2], + vtxLambda[0], vtxLambda[1], vtxLambda[2], + cascade.cascadeDaughterDCA, cascade.pi0DaughterDCA, cascade.cascadeDCAxy, cascade.cascadeDCAz, + cascade.gamma1Momentum[0], cascade.gamma1Momentum[1], cascade.gamma1Momentum[2], + cascade.gamma2Momentum[0], cascade.gamma2Momentum[1], cascade.gamma2Momentum[2], + cascade.lambdaMomentum[0], cascade.lambdaMomentum[1], cascade.lambdaMomentum[2]); + + xi0Indices(cascade.gamma1Index, cascade.gamma2Index, cascade.lambdaIndex); + + xi0collRefs(collision.globalIndex()); + + histos.fill(HIST("Xi0Sel/hXi0Mass"), cascade.xi0Mass); + + return true; + } + //_______________________________________________ // Build sigma0 candidate with PCM photons template @@ -2374,7 +2923,7 @@ struct sigma0builder { // Sigma0 topological info auto sigma0TopoInfo = propagateV0PairToDCA(gamma, lambda); - sigma0cores(gamma.globalIndex(), lambda.globalIndex(), sigma0TopoInfo.X, sigma0TopoInfo.Y, sigma0TopoInfo.Z, sigma0TopoInfo.DCADau, + sigma0cores(gamma.globalIndex(), lambda.globalIndex(), sigma0TopoInfo.position[0], sigma0TopoInfo.position[1], sigma0TopoInfo.position[2], sigma0TopoInfo.daughterDCA, gamma.px(), gamma.py(), gamma.pz(), gamma.mGamma(), lambda.px(), lambda.py(), lambda.pz(), lambda.mLambda(), lambda.mAntiLambda()); // MC properties @@ -2601,7 +3150,7 @@ struct sigma0builder { auto kstarTopoInfo = propagateV0PairToDCA(gamma, kshort); - kstarcores(gamma.globalIndex(), kshort.globalIndex(), kstarTopoInfo.X, kstarTopoInfo.Y, kstarTopoInfo.Z, kstarTopoInfo.DCADau, + kstarcores(gamma.globalIndex(), kshort.globalIndex(), kstarTopoInfo.position[0], kstarTopoInfo.position[1], kstarTopoInfo.position[2], kstarTopoInfo.daughterDCA, gamma.px(), gamma.py(), gamma.pz(), gamma.mGamma(), kshort.px(), kshort.py(), kshort.pz(), kshort.mK0Short()); // MC properties @@ -2720,6 +3269,8 @@ struct sigma0builder { //_______________________________________________ // Collisions loop for (const auto& coll : collisions) { + initCCDB(coll); + // Event selection if (eventSelections.fUseEventSelection) { if (!IsEventAccepted(coll, true)) @@ -2842,6 +3393,36 @@ struct sigma0builder { } } } + + //_______________________________________________ + // Xi0 loop + if (fillXi0Tables) { + // First find pi0 candidates + std::vector pi0Candidates; + for (size_t i = 0; i < bestGammasArray.size(); ++i) { // loop over photons + auto gamma1 = fullV0s.rawIteratorAt(bestGammasArray[i]); + for (size_t j = i + 1; j < bestGammasArray.size(); ++j) { // loop over photons + auto gamma2 = fullV0s.rawIteratorAt(bestGammasArray[j]); + V0PairTopoInfo pi0Info; + if (!buildPi0ForXi0(gamma1, gamma2, coll, pi0Info)) { + continue; + } + pi0Candidates.push_back(pi0Info); + } + } + + // second build Xi0 candidates + for (size_t i = 0; i < bestLambdasArray.size(); ++i) { // loop over Lambda + auto lambda = fullV0s.rawIteratorAt(bestLambdasArray[i]); + for (size_t j = 0; j < pi0Candidates.size(); ++j) { // loop over pi0 + auto gamma1 = fullV0s.rawIteratorAt(pi0Candidates[j].v01Index); + auto gamma2 = fullV0s.rawIteratorAt(pi0Candidates[j].v02Index); + // Building pi0 candidate & filling tables + if (!buildXi0(gamma1, gamma2, lambda, coll, mcparticles)) + continue; + } + } + } } } From 4a71345bdfefe0cedab53c2956683fbe038a8f30 Mon Sep 17 00:00:00 2001 From: mcoquet642 <74600025+mcoquet642@users.noreply.github.com> Date: Fri, 25 Sep 2026 00:46:59 +0200 Subject: [PATCH 060/648] [PWGDQ] Fixing ZDC table dependency of processes in tableMaker_withAssoc (#18041) --- PWGDQ/TableProducer/tableMaker_withAssoc.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx index 4bf71f23791..8ba2ee20340 100644 --- a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx @@ -150,8 +150,10 @@ constexpr static uint32_t gkEventFillMapWithMultsEventFilterZdc = VarManager::Ob // constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilterZdc = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter | VarManager::ObjTypes::Zdc; constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilterZdcFit = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter | VarManager::ObjTypes::Zdc | VarManager::ObjTypes::Fit; // constexpr static uint32_t gkEventFillMapWithCent = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; -constexpr static uint32_t gkEventFillMapWithCentAndMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::Zdc; -constexpr static uint32_t gkEventFillMapWithCentAndMultsQvect = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::CollisionQvectCentr | VarManager::ObjTypes::Zdc; +constexpr static uint32_t gkEventFillMapWithCentAndMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; +constexpr static uint32_t gkEventFillMapWithCentAndMultsZdc = gkEventFillMapWithCentAndMults | VarManager::ObjTypes::Zdc; +constexpr static uint32_t gkEventFillMapWithCentAndMultsQvect = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::CollisionQvectCentr; +constexpr static uint32_t gkEventFillMapWithCentAndMultsQvectZdc = gkEventFillMapWithCentAndMultsQvect | VarManager::ObjTypes::Zdc; constexpr static uint32_t gkEventFillMapWithMultsExtra = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; // constexpr static uint32_t gkEventFillMapWithCentRun2 = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCentRun2; // Unused variable // constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackPID | VarManager::ObjTypes::TrackPIDExtra; @@ -2125,7 +2127,7 @@ struct TableMaker { TrackAssoc const& trackAssocs, FwdTrackAssoc const& fwdTrackAssocs, MFTTrackAssoc const& mftAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, zdcs, tracksBarrel, muons, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, nullptr, ft0s, fv0as, fdds); + fullSkimming(collisions, bcs, zdcs, tracksBarrel, muons, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, nullptr, ft0s, fv0as, fdds); } // produce the barrel only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter @@ -2182,7 +2184,7 @@ struct TableMaker { void processPbPbMuonOnly(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyMuonsWithCov const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); + fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the muon-only DQ skimmed data model typically for UPC Pb-Pb (no centrality), subscribe to the DQ rapidity gap event filter (filter-PbPb) @@ -2197,7 +2199,7 @@ struct TableMaker { void processPbPbStreamMuonOnly(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyMuonsNoDca const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); + fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the realigned muon only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter From ddb1f9f65c438d6439f11f0becb5fdd20dddf2a2 Mon Sep 17 00:00:00 2001 From: Sigurd Nese <32108009+sigurdnese@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:43:47 +0200 Subject: [PATCH 061/648] [PWGDQ] Fill FIT variables in tableMakerMC_withAssoc (#18045) --- .../TableProducer/tableMakerMC_withAssoc.cxx | 88 ++++++++++++++----- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index d4c7f32de57..d31a296d0ac 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -112,12 +112,14 @@ using MyEventsWithCentAndMults = soa::Join; using MyEventsMcWithMults = soa::Join; +using MyBCs = soa::Join; + // Declare bit maps containing information on the table joins content (used as argument in templated functions) constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision; constexpr static uint32_t gkEventFillMapWithMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; // constexpr static uint32_t gkEventFillMapWithCent = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; constexpr static uint32_t gkEventFillMapWithCentAndMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::CollisionMultExtra; -constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilter = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter; +constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilterFit = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter | VarManager::ObjTypes::Fit; constexpr static uint32_t gkEventMcFillMap = VarManager::ObjTypes::CollisionMC; constexpr static uint32_t gkEventMcFillMapWithCent = VarManager::ObjTypes::CollisionMC | VarManager::ObjTypes::CollisionCent; // constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackPID; @@ -158,6 +160,7 @@ struct TableMakerMC { Produces eventExtended; Produces eventVtxCov; Produces eventInfo; + Produces fit; Produces multPV; Produces multAll; Produces eventMClabels; @@ -696,8 +699,10 @@ struct TableMakerMC { } } - template - void skimCollisions(TEvents const& collisions, BCsWithTimestamps const& /*bcs*/) + template + void skimCollisions(TEvents const& collisions, TBCs const& bcs, + TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds) { // Skim reconstructed collisions which are selected by the user specified cuts @@ -729,12 +734,12 @@ struct TableMakerMC { } (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); - auto bc = collision.template bc_as(); + auto bc = collision.template bc_as(); // store the selection decisions auto tag = static_cast(0); // store some more information in the tag // if the BC found by event selection does not coincide with the collision.bc(), toggle the first bit - auto bcEvSel = collision.template foundBC_as(); + auto bcEvSel = collision.template foundBC_as(); if (bcEvSel.globalIndex() != bc.globalIndex()) { tag |= (static_cast(1) << 0); } @@ -750,6 +755,19 @@ struct TableMakerMC { if (collision.has_mcCollision()) { auto mcCollision = collision.template mcCollision_as(); VarManager::FillEvent(mcCollision); + // Fill FIT info using newbc pattern for UPC events (similar to ZDC) + if constexpr ((TEventFillMap & VarManager::ObjTypes::Fit) > 0) { + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + if constexpr ((TEventFillMap & VarManager::ObjTypes::RapidityGapFilter) > 0) { + auto newbc = bcs.rawIteratorAt(collision.newBcIndex()); + VarManager::FillFIT(newbc, bcs, ft0s, fv0as, fdds); + } else { + VarManager::FillFIT(bcEvSel, bcs, ft0s, fv0as, fdds); + } + } + } } if (fDoDetailedQA) { fHistMan->FillHistClass("Event_BeforeCuts", dqtablemakermc_helpers::varValues()); @@ -804,6 +822,30 @@ struct TableMakerMC { eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); eventMClabels(collision.mcCollisionId(), collision.mcMask()); eventInfo(collision.globalIndex()); + // Fill FIT table if requested + if constexpr ((TEventFillMap & VarManager::ObjTypes::Fit) > 0) { + fit(VarManager::fgValues[VarManager::kAmplitudeFT0A], VarManager::fgValues[VarManager::kAmplitudeFT0C], + VarManager::fgValues[VarManager::kTimeFT0A], VarManager::fgValues[VarManager::kTimeFT0C], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFT0]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0C]), + VarManager::fgValues[VarManager::kAmplitudeFDDA], VarManager::fgValues[VarManager::kAmplitudeFDDC], + VarManager::fgValues[VarManager::kTimeFDDA], VarManager::fgValues[VarManager::kTimeFDDC], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFDD]), + VarManager::fgValues[VarManager::kAmplitudeFV0A], VarManager::fgValues[VarManager::kTimeFV0A], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFV0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFV0A]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBBFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDCpf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDCpf])); + } if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMultExtra) > 0) { multPV(collision.multNTracksHasITS(), collision.multNTracksHasTPC(), collision.multNTracksHasTOF(), collision.multNTracksHasTRD(), collision.multNTracksITSOnly(), collision.multNTracksTPCOnly(), collision.multNTracksITSTPC(), @@ -1313,12 +1355,13 @@ struct TableMakerMC { } // end loop over selected muons } // end skimMuons - template - void fullSkimming(TEvents const& collisions, BCsWithTimestamps const& bcs, + template + void fullSkimming(TEvents const& collisions, TBCs const& bcs, TTracks const& tracksBarrel, TMuons const& muons, TMFTTracks const& mftTracks, TTrackAssoc const& trackAssocs, TFwdTrackAssoc const& fwdTrackAssocs, TMFTTrackAssoc const& mftAssocs, - MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, TMFTCov const& mftCovs) + MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, TMFTCov const& mftCovs, + TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds) { // Check whether the run changed and update CCDB if it did if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { @@ -1356,7 +1399,7 @@ struct TableMakerMC { eventVtxCov.reserve(collisions.size()); eventMClabels.reserve(collisions.size()); eventInfo.reserve(collisions.size()); - skimCollisions(collisions, bcs); + skimCollisions(collisions, bcs, ft0s, fv0as, fdds); if (fCollIndexMap.empty()) { return; } @@ -1591,14 +1634,14 @@ struct TableMakerMC { aod::TrackAssoc const& trackAssocs, aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPBarrelOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, MyBarrelTracksWithCov const& tracksBarrel, aod::TrackAssoc const& trackAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonOnlyBasic(MyEvents const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1606,7 +1649,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1614,7 +1657,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonRefit(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1622,7 +1665,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, aod::MFTTracksCov const& mftCovs) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, mftCovs); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, mftCovs, nullptr, nullptr, nullptr); } void processPPRealignedMuonOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1630,7 +1673,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPbPb(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1638,7 +1681,7 @@ struct TableMakerMC { aod::TrackAssoc const& trackAssocs, aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPbPbBarrelOnly(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1646,15 +1689,16 @@ struct TableMakerMC { MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { computeCollMergingTag(collisions, tracksBarrel, presliceBarrelTracksWithCov); - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } - void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, aod::BCsWithTimestamps const& bcs, + void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, MyBCs const& bcs, MyBarrelTracksWithCov const& tracksBarrel, aod::TrackAssoc const& trackAssocs, + aod::FT0s& ft0s, aod::FV0As& fv0as, aod::FDDs& fdds, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { computeCollMergingTag(collisions, tracksBarrel, presliceBarrelTracksWithCov); - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, ft0s, fv0as, fdds); } void processPbPbMuonOnly(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1662,7 +1706,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); /*LOGP(info, "---------------------------"); for (const auto& mcCollision : mcCollisions) { LOGP(info, "Gen. FT0C centrality = {}", mcCollision.bestCollisionCentFT0C()); @@ -1686,7 +1730,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } // Process the BCs and store stats for luminosity retrieval ----------------------------------------------------------------------------------- From b2a6622d7829cd7ad0446d2809d7ca55681059c5 Mon Sep 17 00:00:00 2001 From: Laszlo Gyulai <50623881+laszlogyulai@users.noreply.github.com> Date: Fri, 25 Sep 2026 09:03:07 +0200 Subject: [PATCH 062/648] [PWGHF] Adding D0/Lc analysis task (#17808) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ALICE Action Bot Co-authored-by: Vít Kučera <26327373+vkucera@users.noreply.github.com> --- PWGHF/D2H/Tasks/CMakeLists.txt | 5 + PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx | 1128 ++++++++++++++++++++++ 2 files changed, 1133 insertions(+) create mode 100644 PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 19a61637c19..4d6a60998fc 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -99,6 +99,11 @@ o2physics_add_dpl_workflow(task-dstar-to-d0-pi PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-flattenicity-d0-lc + SOURCES taskFlattenicityD0Lc.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-flow-charm-hadrons SOURCES taskFlowCharmHadrons.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils diff --git a/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx b/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx new file mode 100644 index 00000000000..dc18bbd9087 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx @@ -0,0 +1,1128 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file taskFlattenicityD0Lc.cxx +/// \brief Analysis of D0/Lambda_c yield as a function of flattenicity +/// \author Laszlo Gyulai, laszlo.gyulai@cern.ch + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; + +namespace +{ +enum CandTypeSel { + SigD0 = 0, // Signal D0 + SigD0bar, // Signal D0bar + ReflectedD0, // Reflected D0 + ReflectedD0bar, // Reflected D0bar + PureSigD0, // Signal D0 exclude Reflected D0bar + PureSigD0bar // Signal D0bar exclude Reflected D0 +}; +} // namespace + +static const int nCellsFV0 = 48; +static const int nInnerCellsFV0 = 32; +std::array rhoLatticeFV0{}; +std::array rhoLatticeFV0Calibrated{}; +std::array calib = {1.01697, 1.122, 1.03854, 1.108, 1.11634, 1.14971, 1.19321, 1.06866, 0.954675, 0.952695, 0.969853, 0.957557, 0.989784, 1.01549, 1.02182, 0.976005, 1.01865, 1.06871, 1.06264, 1.02969, 1.07378, 1.06622, 1.15057, 1.0433, 0.83654, 0.847178, 0.890027, 0.920814, 0.888271, 1.04662, 0.8869, 0.856348, 0.863181, 0.906312, 0.902166, 1.00122, 1.03303, 0.887866, 0.892437, 0.906278, 0.884976, 0.864251, 0.917221, 1.10618, 1.04028, 0.893184, 0.915734, 0.892676}; +std::map channelsToRings = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 7}, {5, 6}, {6, 5}, {7, 4}, {8, 8}, {9, 9}, {10, 10}, {11, 11}, {12, 15}, {13, 14}, {14, 13}, {15, 12}, {16, 16}, {17, 17}, {18, 18}, {19, 19}, {20, 23}, {21, 22}, {22, 21}, {23, 20}, {24, 24}, {25, 25}, {26, 26}, {27, 27}, {28, 31}, {29, 30}, {30, 29}, {31, 28}, {32, 32}, {33, 34}, {34, 36}, {35, 38}, {36, 47}, {37, 45}, {38, 43}, {39, 41}, {40, 33}, {41, 35}, {42, 37}, {43, 39}, {44, 46}, {45, 44}, {46, 42}, {47, 40}}; + +struct HfTaskFlattenicityD0Lc { + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable selectionTopol{"selectionTopol", 1, "Selection Flag for topologically selected candidates"}; + Configurable selectionCand{"selectionCand", 1, "Selection Flag for conj. topol. selected candidates"}; + 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 ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable selectionFlagHf{"selectionFlagHf", 1, "Selection Flag for HF flagged candidates"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + + HfEventSelection hfEvSel; + Service ccdb{}; + + using Collisions = soa::Join; + using CollisionsWithMcLabels = soa::Join; + + using TracksWPid = soa::Join; + using TracksSelQuality = soa::Join; + + using D0Candidates = soa::Filtered>; + using D0CandidatesMc = soa::Filtered>; + + using LcCandidates = soa::Filtered>; + using LcCandidatesMc = soa::Filtered>; + + Filter filterD0 = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_2prong::DecayType::D0ToPiK))) != static_cast(0); + Filter filterLc = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc; + + Preslice candD0PerCollision = aod::hf_cand::collisionId; + Preslice candLcPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted colPerMcCollisionLc = aod::mcparticle::mcCollisionId; + + Partition selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isRecoHfFlag >= selectionFlagHf; + + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {120, 1.5848, 2.1848}, "Cand. inv-mass bins"}; + ConfigurableAxis thnConfigAxisFlat{"thnConfigAxisFlat", {100, 0, 1}, "Flattenicity in event associated with candidate"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {500, 0, 50}, "Cand. pT bins"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1, 1}, "Cand. rapidity bins"}; + ConfigurableAxis thnConfigAxisCandType{"thnConfigAxisCandType", {6, -0.5, 5.5}, "D0 type"}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {1000, 0, 100}, "Cand. beauty mother pTB bins"}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, "Cand. origin type"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; + ConfigurableAxis thnConfigAxisGenPtD{"thnConfigAxisGenPtD", {500, 0, 50}, "Gen Pt D"}; + ConfigurableAxis thnConfigAxisGenPtB{"thnConfigAxisGenPtB", {1000, 0, 100}, "Gen Pt B"}; + ConfigurableAxis thnConfigAxisMassLc{"thnConfigAxisMassLc", {300, 1.98, 2.58}, ""}; + ConfigurableAxis thnConfigAxisCanType{"thnConfigAxisCanType", {5, 0., 5.}, ""}; + + HistogramRegistry registry{"registry"}; + + void init(InitContext const&) + { + hfEvSel.addHistograms(registry); + registry.add("Flattenicity", "Number of events; 1-#rho; Counter", {kTH1F, {{100, 0, 1}}}); + registry.add("Flattenicity_calibrated", "Number of events; 1-#rho; Counter", {kTH1F, {{100, 0, 1}}}); + + auto vbins = binsPt.value; + + const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (#pi K) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisFlat{thnConfigAxisFlat, "#it{#rho}"}; + const AxisSpec thnAxisY{thnConfigAxisY, "y"}; + const AxisSpec thnAxisCandType{thnConfigAxisCandType, "D0 type"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "Origin"}; + const AxisSpec thnAxisGenPtD{thnConfigAxisGenPtD, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisGenPtB{thnConfigAxisGenPtB, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisMassLc{thnConfigAxisMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisCanType{thnConfigAxisCanType, "candidates type"}; + + std::vector axesD0 = {thnAxisMass, thnAxisPt, thnAxisFlat, thnAxisY, thnAxisCandType}; + std::vector axesD0Gen = {thnAxisGenPtD, thnAxisFlat, thnAxisGenPtB, thnAxisY, thnAxisOrigin}; + std::vector axesLcData = {thnAxisMassLc, thnAxisPt, thnAxisFlat}; + std::vector axesLcMc = {thnAxisMassLc, thnAxisPt, thnAxisFlat, thnAxisPtB, thnAxisCanType}; + std::vector axesGenLc = {thnAxisPt, thnAxisFlat, thnAxisPtB, thnAxisCanType}; + + if (doprocessData) { + registry.add("Data/D0/hMass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0., 5.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hMassFinerBinning", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hMassVsPhi", "2-prong candidates vs phi;inv. mass (#pi K) (GeV/#it{c}^{2});phi (rad);entries", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {32, 0, o2::constants::math::TwoPI}}}); + registry.add("Data/D0/hPtCand", "2-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hPtProng0", "2-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hPtProng1", "2-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthxy", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLenErr", "2-prong candidates;decay length error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLenXYErr", "2-prong candidates;decay length xy error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hNormalisedDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hNormalisedDecLengthxy", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong0", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong1", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0ErrProng0", "2-prong candidates;prong 0 DCAxy to prim. vertex error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0ErrProng1", "2-prong candidates;prong 1 DCAxy to prim. vertex error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0d0", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2});entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCTS", "2-prong candidates;cos #it{#theta}* (D^{0});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCt", "2-prong candidates;proper lifetime (D^{0}) * #it{c} (cm);entries", {HistType::kTH2F, {{120, -20., 100.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPA", "2-prong candidates;cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hEta", "2-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hSelectionStatus", "2-prong candidates;selection status;entries", {HistType::kTH2F, {{5, -0.5, 4.5}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthFinerBinning", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{400, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthxyFinerBinning", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{400, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong0FinerBinning", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong1FinerBinning", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0d0FinerBinning", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2});entries", {HistType::kTH2F, {{500, -0.1, 0.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCTSFinerBinning", "2-prong candidates;cos #it{#theta}* (D^{0});entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCtFinerBinning", "2-prong candidates;proper lifetime (D^{0}) * #it{c} (cm);entries", {HistType::kTH2F, {{500, -0., 100.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPAFinerBinning", "2-prong candidates;cosine of pointing angle;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPAXYFinerBinning", "2-prong candidates;cosine of pointing angle xy;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("Data/D0/hMassVsPtVsFlatVsYVsD0Type", "Thn for D0 candidates", HistType::kTHnSparseD, axesD0); + + registry.add("Data/Lc/hMass", ";inv. mass (p K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{600, 1.98, 2.58}}}); + registry.add("Data/Lc/hMassVsPt", "Inv.mass vs pt;inv. mass (p K #pi) (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, 1.98, 2.58}, {vbins}}}); + registry.add("Data/Lc/hPt", "#pT of candidates;it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng0", "pT of prong 0;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng1", "pT of prong 1;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng2", "pT of prong 2;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hd0Prong0", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0Prong1", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0Prong2", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0VsPtProng0", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hd0VsPtProng1", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hd0VsPtProng2", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hDecLength", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("Data/Lc/hDecLengthVsPt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("Data/Lc/hDecLengthxy", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("Data/Lc/hDecLengthxyVsPt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("Data/Lc/hCt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("Data/Lc/hCtVsPt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("Data/Lc/hCPA", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("Data/Lc/hCPAVsPt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("Data/Lc/hCPAxy", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("Data/Lc/hCPAxyVsPt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("Data/Lc/hEta", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("Data/Lc/hEtaVsPt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("Data/Lc/hPhi", "#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("Data/Lc/hPhiVsPt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("Data/Lc/hSelectionStatus", "3-prong candidates;selection status;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{5, -0.5, 4.5}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/Lc/hImpParErrProng0VsPt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hImpParErrProng1VsPt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hImpParErrProng2VsPt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hDecLenErrVsPt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + + registry.add("Data/Lc/hLcVsPtVsFlat", "THn for Reconstructed Lambdac candidates for data", HistType::kTHnSparseF, axesLcData); + } + + if (doprocessMCD0) { + registry.add("MC/D0/hPtGenSig", "2-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoHFFlag", "2-prong candidates (RecoHFFlag - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenVsPtRecSig", "2-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH2F, {{360, 0., 36.}, {360, 0., 36.}}}); + registry.add("MC/D0/hYGenVsYRecSig", "2-prong candidates (matched);#it{y}^{gen.} ;#it{y}^{rec.} ;entries", {HistType::kTH2F, {{300, -1.5, 1.5}, {300, -1.5, 1.5}}}); + registry.add("MC/D0/hMassVsPtGenVsPtRecSig", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2});#it{p}_{T}^{gen.} (GeV/#it{c});#it{p}_{T}^{rec.} (GeV/#it{c})", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {150, 0., 30.}}}); + registry.add("MC/D0/hCPARecSig", "2-prong candidates (matched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/D0/hEtaRecSig", "2-prong candidates (matched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hPtRecBg", "2-prong candidates (unmatched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hCPARecBg", "2-prong candidates (unmatched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/D0/hEtaRecBg", "2-prong candidates (unmatched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hMassSigBkgD0", "2-prong candidates (not checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtProng0Sig", "prong0 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hPtProng1Sig", "prong1 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthSig", "2-prong candidates (matched);decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthXYSig", "2-prong candidates (matched);decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthSig", "2-prong candidates (matched);normalised decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthXYSig", "2-prong candidates (matched);normalised decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0Sig", "2-prong candidates (matched);prong 0 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong1Sig", "2-prong candidates (matched);prong 1 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0d0Sig", "2-prong candidates (matched);product of DCAxy to prim. vertex (cm^{2}); #it{y}", {HistType::kTH2F, {{500, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCTSSig", "2-prong candidates (matched);cos #it{#theta}* (D^{0}); #it{y}", {HistType::kTH2F, {{110, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCtSig", "2-prong candidates (matched);proper lifetime (D^{0}) * #it{c} (cm); #it{y}", {HistType::kTH2F, {{120, -20., 100.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPASig", "2-prong candidates (matched);cosine of pointing angle; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPAxySig", "2-prong candidates (matched);cosine of pointing angle xy; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0VsPtSig", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hd0Prong1VsPtSig", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hd0d0VsPtSig", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2}) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -0.1, 0.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCTSVsPtSig", "2-prong candidates;cos #it{#theta}* (D^{0}) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCPAVsPtSig", "2-prong candidates;cosine of pointing angle vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCPAXYVsPtSig", "2-prong candidates;cosine of pointing angle xy vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hNormalisedDecLengthxyVsPtSig", "2-prong candidates;decay length xy (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hDecLengthVsPtSig", "2-prong candidates;decay length (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hDecLengthxyVsPtSig", "2-prong candidates;decay length xy (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hMassSigD0", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtProng0Bkg", "prong0 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hPtProng1Bkg", "prong1 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthBkg", "2-prong candidates (checked);decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthXYBkg", "2-prong candidates (checked);decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthBkg", "2-prong candidates (checked);normalised decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthXYBkg", "2-prong candidates (checked);normalised decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0Bkg", "2-prong candidates (checked);prong 0 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong1Bkg", "2-prong candidates (checked);prong 1 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0d0Bkg", "2-prong candidates (checked);product of DCAxy to prim. vertex (cm^{2}); #it{y}", {HistType::kTH2F, {{500, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCTSBkg", "2-prong candidates (checked);cos #it{#theta}* (D^{0}); #it{y}", {HistType::kTH2F, {{110, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCtBkg", "2-prong candidates (checked);proper lifetime (D^{0}) * #it{c} (cm); #it{y}", {HistType::kTH2F, {{120, -20., 100.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPABkg", "2-prong candidates (checked);cosine of pointing angle; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPAxyBkg", "2-prong candidates (checked);cosine of pointing angle xy; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hMassBkgD0", "2-prong candidates (checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassReflBkgD0", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassSigBkgD0bar", "2-prong candidates (not checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassSigD0bar", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassReflBkgD0bar", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtGen", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYGen", "2-prong candidates (matched);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hYGenPrompt", "MC particles (matched, prompt);#it{y}^{gen.};entries", {HistType::kTH1F, {{300, -1.5, 1.5}}}); + registry.add("MC/D0/hPtVsYGenPrompt", "2-prong candidates (matched, prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hYGenNonPrompt", "MC particles (matched, non-prompt);#it{y}^{gen.};entries", {HistType::kTH1F, {{300, -1.5, 1.5}}}); + registry.add("MC/D0/hPtVsYGenNonPrompt", "2-prong candidates (matched, non-prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hEtaGen", "MC particles (matched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hMassBkgD0bar", "2-prong candidates (checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoTopol", "2-prong candidates (RecoTopol - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoCand", "2-prong candidates (RecoCand - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSig_RecoPID", "2-prong candidates (RecoPID - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSig", "2-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoHFFlag", "2-prong candidates (RecoHFFlag - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoTopol", "2-prong candidates (RecoTopol - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoCand", "2-prong candidates (RecoCand - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoPID", "2-prong candidates (RecoPID - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSigPrompt", "2-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoHFFlag", "2-prong candidates (RecoHFFlag - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoTopol", "2-prong candidates (RecoTopol - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoCand", "2-prong candidates (RecoCand - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoPID", "2-prong candidates (RecoPID - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSigNonPrompt", "2-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + + axesD0.push_back(thnAxisPtB); + axesD0.push_back(thnAxisOrigin); + registry.add("MC/D0/hMassVsPtVsFlatVsYVsD0Type", "Thn for D0 candidates", HistType::kTHnSparseD, axesD0); + registry.add("MC/D0/hD0Gen", "Thn for generated D0 from charm and beauty", HistType::kTHnSparseD, axesD0Gen); + } + + if (doprocessMCLc) { + registry.add("MC/Lc/hPtGenSig", "3-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hMassRecSig", "3-prong candidates (matched);inv. mass (p K #pi) (GeV/#it{c}^{2});", {HistType::kTH1F, {{600, 1.98, 2.58}}}); + registry.add("MC/Lc/hMassVsPtRecSig", "3-prong candidates (matched);inv. mass (p K #pi) (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, 1.98, 2.58}, {vbins}}}); + registry.add("MC/Lc/hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSig", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSig", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSig", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSig", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSig", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSig", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSig", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSig", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSig", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSig", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSig", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSig", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSig", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSig", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSig", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSig", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSig", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSig", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSig", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSig", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSig", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSig", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSig", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSig", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSig", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSig", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSig", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtRecSigPrompt", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSigPrompt", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSigPrompt", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSigPrompt", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSigPrompt", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSigPrompt", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSigPrompt", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSigPrompt", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSigPrompt", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSigPrompt", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSigPrompt", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSigPrompt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSigPrompt", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSigPrompt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSigPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSigPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSigPrompt", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSigPrompt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSigPrompt", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSigPrompt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSigPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSigPrompt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSigPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSigPrompt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSigPrompt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSigPrompt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSigPrompt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSigPrompt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtRecSigNonPrompt", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSigNonPrompt", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSigNonPrompt", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSigNonPrompt", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSigNonPrompt", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSigNonPrompt", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSigNonPrompt", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSigNonPrompt", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSigNonPrompt", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSigNonPrompt", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSigNonPrompt", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSigNonPrompt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSigNonPrompt", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSigNonPrompt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSigNonPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSigNonPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSigNonPrompt", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSigNonPrompt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSigNonPrompt", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSigNonPrompt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSigNonPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSigNonPrompt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSigNonPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSigNonPrompt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSigNonPrompt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSigNonPrompt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSigNonPrompt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSigNonPrompt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtGen", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGen", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGen", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGen", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGen", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGen", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGen", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hPtGenPrompt", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGenPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGenPrompt", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGenPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGenPrompt", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGenPrompt", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGenPrompt", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hPtGenNonPrompt", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGenNonPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGenNonPrompt", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGenNonPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGenNonPrompt", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGenNonPrompt", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGenNonPrompt", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + + registry.add("MC/Lc/hnLcVars", "THn for Reconstructed Lambdac candidates for MC", HistType::kTHnSparseF, axesLcMc); + registry.add("MC/Lc/hnLcVarsGen", "THn for Generated Lambdac", HistType::kTHnSparseF, axesGenLc); + } + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + void processData(Collisions const& collisions, + D0Candidates const&, + LcCandidates const& selectedLcCandidates, + aod::TracksWExtra const&, + aod::BcFullInfos const& bcs, + aod::FV0As const&, + TracksWPid const& tracks) + { + runAnalysisData(collisions, selectedD0Candidates, selectedLcCandidates, bcs, tracks); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processData, "Process data", true); + + void processMCD0(D0CandidatesMc const&, + soa::Join const& mcParticles2prong, + TracksSelQuality const& tracks, + CollisionsWithMcLabels const& collisions, + aod::McCollisions const& mcCollisions, + aod::FV0As const&, + aod::BcFullInfos const& bcs) + { + runAnalysisMCD0(selectedD0CandidatesMc, mcParticles2prong, tracks, collisions, mcCollisions, bcs); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processMCD0, "Process MC D0 with DCAFitter", false); + + void processMCLc(soa::Join const& mcParticles3prong, + TracksSelQuality const& tracks, + CollisionsWithMcLabels const& collisions, + aod::McCollisions const& mcCollisions, + aod::FV0As const&, + LcCandidatesMc const& selectedLcCandidatesMc, + aod::BcFullInfos const& bcs) + { + runAnalysisMCLc(selectedLcCandidatesMc, mcParticles3prong, tracks, collisions, mcCollisions, bcs); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processMCLc, "Process MC Lc with DCAFitter", false); + + template + void runAnalysisData(CollType const& collisions, + CandTypeD0 const& candidatesD0, + CandTypeLc const& candidatesLc, + aod::BcFullInfos const&, + TracksWPid const&) + { + for (const auto& collision : collisions) { + + float centrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + hfEvSel.fillHistograms(collision, rejectionMask, centrality); + if (rejectionMask != 0) { + continue; + } + + const float flat = fillFlat(collision); + const auto thisCollId = collision.globalIndex(); + + // D0 + const auto& groupedD0Candidates = candidatesD0.sliceBy(candD0PerCollision, thisCollId); + for (const auto& candidate : groupedD0Candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yD0(candidate)) > yCandRecoMax) { + continue; + } + + const float massD0 = HfHelper::invMassD0ToPiK(candidate); + const float massD0bar = HfHelper::invMassD0barToKPi(candidate); + const auto ptCandidate = candidate.pt(); + + registry.fill(HIST("Data/D0/hPtCand"), ptCandidate); + registry.fill(HIST("Data/D0/hPtProng0"), candidate.ptProng0()); + registry.fill(HIST("Data/D0/hPtProng1"), candidate.ptProng1()); + registry.fill(HIST("Data/D0/hDecLength"), candidate.decayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthxy"), candidate.decayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLenErr"), candidate.errorDecayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLenXYErr"), candidate.errorDecayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hNormalisedDecLength"), candidate.decayLengthNormalised(), ptCandidate); + registry.fill(HIST("Data/D0/hNormalisedDecLengthxy"), candidate.decayLengthXYNormalised(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong0"), candidate.impactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong1"), candidate.impactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0ErrProng0"), candidate.errorImpactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0ErrProng1"), candidate.errorImpactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0d0"), candidate.impactParameterProduct(), ptCandidate); + registry.fill(HIST("Data/D0/hCTS"), HfHelper::cosThetaStarD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCt"), HfHelper::ctD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCPA"), candidate.cpa(), ptCandidate); + registry.fill(HIST("Data/D0/hEta"), candidate.eta(), ptCandidate); + registry.fill(HIST("Data/D0/hSelectionStatus"), candidate.isSelD0() + (candidate.isSelD0bar() * 2), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthFinerBinning"), candidate.decayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthxyFinerBinning"), candidate.decayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong0FinerBinning"), candidate.impactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong1FinerBinning"), candidate.impactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0d0FinerBinning"), candidate.impactParameterProduct(), ptCandidate); + registry.fill(HIST("Data/D0/hCTSFinerBinning"), HfHelper::cosThetaStarD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCtFinerBinning"), HfHelper::ctD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCPAFinerBinning"), candidate.cpa(), ptCandidate); + registry.fill(HIST("Data/D0/hCPAXYFinerBinning"), candidate.cpaXY(), ptCandidate); + + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("Data/D0/hMass"), massD0, ptCandidate); + registry.fill(HIST("Data/D0/hMassFinerBinning"), massD0, ptCandidate); + registry.fill(HIST("Data/D0/hMassVsPhi"), massD0, ptCandidate, candidate.phi()); + + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, HfHelper::yD0(candidate), SigD0); + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0); + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("Data/D0/hMass"), massD0bar, ptCandidate); + registry.fill(HIST("Data/D0/hMassFinerBinning"), massD0bar, ptCandidate); + registry.fill(HIST("Data/D0/hMassVsPhi"), massD0bar, ptCandidate, candidate.phi()); + + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, HfHelper::yD0(candidate), SigD0bar); + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar); + } + } + + // Lc + const auto& groupedLcCandidates = candidatesLc.sliceBy(candLcPerCollision, thisCollId); + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + const auto pt = candidate.pt(); + const auto ptProng0 = candidate.ptProng0(); + const auto ptProng1 = candidate.ptProng1(); + const auto ptProng2 = candidate.ptProng2(); + const auto decayLength = candidate.decayLength(); + const auto decayLengthXY = candidate.decayLengthXY(); + const auto cpa = candidate.cpa(); + const auto cpaXY = candidate.cpaXY(); + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + registry.fill(HIST("Data/Lc/hMass"), HfHelper::invMassLcToPKPi(candidate)); + registry.fill(HIST("Data/Lc/hMassVsPt"), HfHelper::invMassLcToPKPi(candidate), pt); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + registry.fill(HIST("Data/Lc/hMass"), HfHelper::invMassLcToPiKP(candidate)); + registry.fill(HIST("Data/Lc/hMassVsPt"), HfHelper::invMassLcToPiKP(candidate), pt); + } + registry.fill(HIST("Data/Lc/hPt"), pt); + registry.fill(HIST("Data/Lc/hPtProng0"), ptProng0); + registry.fill(HIST("Data/Lc/hPtProng1"), ptProng1); + registry.fill(HIST("Data/Lc/hPtProng2"), ptProng2); + registry.fill(HIST("Data/Lc/hd0Prong0"), candidate.impactParameter0()); + registry.fill(HIST("Data/Lc/hd0Prong1"), candidate.impactParameter1()); + registry.fill(HIST("Data/Lc/hd0Prong2"), candidate.impactParameter2()); + registry.fill(HIST("Data/Lc/hd0VsPtProng0"), candidate.impactParameter0(), pt); + registry.fill(HIST("Data/Lc/hd0VsPtProng1"), candidate.impactParameter1(), pt); + registry.fill(HIST("Data/Lc/hd0VsPtProng2"), candidate.impactParameter2(), pt); + registry.fill(HIST("Data/Lc/hDecLength"), decayLength); + registry.fill(HIST("Data/Lc/hDecLengthVsPt"), decayLength, pt); + registry.fill(HIST("Data/Lc/hDecLengthxy"), decayLengthXY); + registry.fill(HIST("Data/Lc/hDecLengthxyVsPt"), decayLengthXY, pt); + registry.fill(HIST("Data/Lc/hCt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("Data/Lc/hCtVsPt"), HfHelper::ctLc(candidate), pt); + registry.fill(HIST("Data/Lc/hCPA"), cpa); + registry.fill(HIST("Data/Lc/hCPAVsPt"), cpa, pt); + registry.fill(HIST("Data/Lc/hCPAxy"), cpaXY); + registry.fill(HIST("Data/Lc/hCPAxyVsPt"), cpaXY, pt); + registry.fill(HIST("Data/Lc/hEta"), candidate.eta()); + registry.fill(HIST("Data/Lc/hEtaVsPt"), candidate.eta(), pt); + registry.fill(HIST("Data/Lc/hPhi"), candidate.phi()); + registry.fill(HIST("Data/Lc/hPhiVsPt"), candidate.phi(), pt); + registry.fill(HIST("Data/Lc/hSelectionStatus"), candidate.isSelLcToPKPi(), pt); + registry.fill(HIST("Data/Lc/hSelectionStatus"), candidate.isSelLcToPiKP(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng0VsPt"), candidate.errorImpactParameter0(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng1VsPt"), candidate.errorImpactParameter1(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng2VsPt"), candidate.errorImpactParameter2(), pt); + registry.fill(HIST("Data/Lc/hDecLenErrVsPt"), candidate.errorDecayLength(), pt); + + auto fillTHnData = [&](bool isPKPi) { + const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("Data/Lc/hLcVsPtVsFlat"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat}); + + registry.get(HIST("Data/Lc/hLcVsPtVsFlat"))->Fill(valuesToFill.data()); + }; + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + fillTHnData(true); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + fillTHnData(false); + } + } + } + } + + template + void runAnalysisMCD0(CandTypeD0 const& candidatesD0, + soa::Join const& mcParticles2prong, + TracksSelQuality const&, + CollType const& collisions, + aod::McCollisions const&, + aod::BcFullInfos const&) + { + // MC rec. + for (const auto& candidate : candidatesD0) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yD0(candidate)) > yCandRecoMax) { + continue; + } + + auto collision = candidate.template collision_as(); + + const float flat = fillFlat(collision); + + float massD0{0.f}, massD0bar{0.f}; + massD0 = HfHelper::invMassD0ToPiK(candidate); + massD0bar = HfHelper::invMassD0barToKPi(candidate); + + auto trackPos = candidate.template prong0_as(); + if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + auto indexMother = RecoDecay::getMother(mcParticles2prong, trackPos.template mcParticle_as>(), o2::constants::physics::Pdg::kD0, true); + auto particleMother = mcParticles2prong.rawIteratorAt(indexMother); + auto ptGen = particleMother.pt(); + auto yGen = RecoDecay::y(particleMother.pVector(), o2::constants::physics::MassD0); + registry.fill(HIST("MC/D0/hPtGenSig"), ptGen); + auto ptRec = candidate.pt(); + auto yRec = HfHelper::yD0(candidate); + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoHFFlag"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtGenVsPtRecSig"), ptGen, ptRec); + registry.fill(HIST("MC/D0/hYGenVsYRecSig"), yGen, yRec); + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("MC/D0/hMassVsPtGenVsPtRecSig"), massD0, ptGen, ptRec); + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hMassVsPtGenVsPtRecSig"), massD0bar, ptGen, ptRec); + } + } + + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSig_RecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSig"), ptRec); + } + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoHFFlag"), ptRec, yRec); + } + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSigPrompt"), ptRec); + } + } else { + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoHFFlag"), ptRec, yRec); + } + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSigNonPrompt"), ptRec); + } + } + registry.fill(HIST("MC/D0/hCPARecSig"), candidate.cpa()); + registry.fill(HIST("MC/D0/hEtaRecSig"), candidate.eta()); + } else { + registry.fill(HIST("MC/D0/hPtRecBg"), candidate.pt()); + registry.fill(HIST("MC/D0/hCPARecBg"), candidate.cpa()); + registry.fill(HIST("MC/D0/hEtaRecBg"), candidate.eta()); + } + auto ptCandidate = candidate.pt(); + auto ptProng0 = candidate.ptProng0(); + auto ptProng1 = candidate.ptProng1(); + auto rapidityCandidate = HfHelper::yD0(candidate); + auto declengthCandidate = candidate.decayLength(); + auto declengthxyCandidate = candidate.decayLengthXY(); + auto normaliseddeclengthCandidate = candidate.decayLengthNormalised(); + auto normaliseddeclengthxyCandidate = candidate.decayLengthXYNormalised(); + auto d0Prong0 = candidate.impactParameter0(); + auto d0Prong1 = candidate.impactParameter1(); + auto d0d0Candidate = candidate.impactParameterProduct(); + auto ctsCandidate = HfHelper::cosThetaStarD0(candidate); + auto ctCandidate = HfHelper::ctD0(candidate); + auto cpaCandidate = candidate.cpa(); + auto cpaxyCandidate = candidate.cpaXY(); + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("MC/D0/hMassSigBkgD0"), massD0, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hPtProng0Sig"), ptProng0, rapidityCandidate); + registry.fill(HIST("MC/D0/hPtProng1Sig"), ptProng1, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthSig"), declengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthXYSig"), declengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthSig"), normaliseddeclengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthXYSig"), normaliseddeclengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0Sig"), d0Prong0, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong1Sig"), d0Prong1, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0d0Sig"), d0d0Candidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCTSSig"), ctsCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCtSig"), ctCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPASig"), cpaCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPAxySig"), cpaxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0VsPtSig"), d0Prong0, ptCandidate); + registry.fill(HIST("MC/D0/hd0Prong1VsPtSig"), d0Prong1, ptCandidate); + registry.fill(HIST("MC/D0/hd0d0VsPtSig"), d0d0Candidate, ptCandidate); + registry.fill(HIST("MC/D0/hCTSVsPtSig"), ctsCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hCPAVsPtSig"), cpaCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hCPAXYVsPtSig"), cpaxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthxyVsPtSig"), normaliseddeclengthxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hDecLengthVsPtSig"), declengthCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hDecLengthxyVsPtSig"), declengthxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hMassSigD0"), massD0, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } else { + registry.fill(HIST("MC/D0/hPtProng0Bkg"), ptProng0, rapidityCandidate); + registry.fill(HIST("MC/D0/hPtProng1Bkg"), ptProng1, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthBkg"), declengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthXYBkg"), declengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthBkg"), normaliseddeclengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthXYBkg"), normaliseddeclengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0Bkg"), d0Prong0, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong1Bkg"), d0Prong1, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0d0Bkg"), d0d0Candidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCTSBkg"), ctsCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCtBkg"), ctCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPABkg"), cpaCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPAxyBkg"), cpaxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassBkgD0"), massD0, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassReflBkgD0"), massD0, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } + } + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hMassSigBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassSigD0bar"), massD0bar, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } else { + registry.fill(HIST("MC/D0/hMassBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassReflBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } + } + } + } + + // MC gen. + for (const auto& particle : mcParticles2prong) { + 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) { + continue; + } + + float flat{-1.f}; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, particle.mcCollision().globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + flat = fillFlat(recCol); + } + + float ptGenB = -1; + auto ptGen = particle.pt(); + auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassD0); + registry.fill(HIST("MC/D0/hPtGen"), ptGen); + registry.fill(HIST("MC/D0/hPtVsYGen"), ptGen, yGen); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("MC/D0/hPtGenPrompt"), ptGen); + registry.fill(HIST("MC/D0/hYGenPrompt"), yGen); + registry.fill(HIST("MC/D0/hPtVsYGenPrompt"), ptGen, yGen); + registry.fill(HIST("MC/D0/hD0Gen"), ptGen, flat, ptGenB, yGen, 1); + } else { + ptGenB = mcParticles2prong.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("MC/D0/hPtGenNonPrompt"), ptGen); + registry.fill(HIST("MC/D0/hYGenNonPrompt"), yGen); + registry.fill(HIST("MC/D0/hPtVsYGenNonPrompt"), ptGen, yGen); + registry.fill(HIST("MC/D0/hD0Gen"), ptGen, flat, ptGenB, yGen, 2); + } + registry.fill(HIST("MC/D0/hEtaGen"), particle.eta()); + } + } + } + + template + void runAnalysisMCLc(CandTypeLc const& candidatesLc, + soa::Join const& mcParticles3prong, + TracksSelQuality const&, + CollType const& collisions, + aod::McCollisions const&, + aod::BcFullInfos const&) + { + for (const auto& collision : collisions) { + // MC Rec. + const auto thisCollId = collision.globalIndex(); + const auto& groupedLcCandidates = candidatesLc.sliceBy(candLcPerCollision, thisCollId); + + const float flat = fillFlat(collision); + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + + if (std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as>(); + const auto pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); + const auto indexMother = RecoDecay::getMother(mcParticles3prong, mcParticleProng0, o2::constants::physics::Pdg::kLambdaCPlus, true); + const auto particleMother = mcParticles3prong.rawIteratorAt(indexMother); + registry.fill(HIST("MC/Lc/hPtGenSig"), particleMother.pt()); + + const auto pt = candidate.pt(); + const auto originType = candidate.originMcRec(); + const auto ptRecB = candidate.ptBhadMotherPart(); + + if ((candidate.isSelLcToPKPi() >= selectionFlagLc) && pdgCodeProng0 == kProton) { + registry.fill(HIST("MC/Lc/hMassRecSig"), HfHelper::invMassLcToPKPi(candidate)); + registry.fill(HIST("MC/Lc/hMassVsPtRecSig"), HfHelper::invMassLcToPKPi(candidate), candidate.pt()); + } + if ((candidate.isSelLcToPiKP() >= selectionFlagLc) && pdgCodeProng0 == kPiPlus) { + registry.fill(HIST("MC/Lc/hMassRecSig"), HfHelper::invMassLcToPiKP(candidate)); + registry.fill(HIST("MC/Lc/hMassVsPtRecSig"), HfHelper::invMassLcToPiKP(candidate), candidate.pt()); + } + registry.fill(HIST("MC/Lc/hPtRecSig"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSig"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSig"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSig"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSig"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSig"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSig"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSig"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSig"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSig"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSig"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSig"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSig"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSig"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSig"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSig"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSig"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSig"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSig"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSig"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSig"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSig"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSig"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSig"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSig"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSig"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSig"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSig"), candidate.errorDecayLength(), candidate.pt()); + + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("MC/Lc/hPtRecSigPrompt"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSigPrompt"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSigPrompt"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSigPrompt"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSigPrompt"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSigPrompt"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSigPrompt"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSigPrompt"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSigPrompt"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSigPrompt"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSigPrompt"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSigPrompt"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSigPrompt"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSigPrompt"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSigPrompt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSigPrompt"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSigPrompt"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSigPrompt"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSigPrompt"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSigPrompt"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSigPrompt"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSigPrompt"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSigPrompt"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSigPrompt"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSigPrompt"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSigPrompt"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSigPrompt"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSigPrompt"), candidate.errorDecayLength(), candidate.pt()); + } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("MC/Lc/hPtRecSigNonPrompt"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSigNonPrompt"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSigNonPrompt"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSigNonPrompt"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSigNonPrompt"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSigNonPrompt"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSigNonPrompt"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSigNonPrompt"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSigNonPrompt"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSigNonPrompt"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSigNonPrompt"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSigNonPrompt"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSigNonPrompt"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSigNonPrompt"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSigNonPrompt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSigNonPrompt"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSigNonPrompt"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSigNonPrompt"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSigNonPrompt"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSigNonPrompt"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSigNonPrompt"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSigNonPrompt"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSigNonPrompt"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSigNonPrompt"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSigNonPrompt"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSigNonPrompt"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSigNonPrompt"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSigNonPrompt"), candidate.errorDecayLength(), candidate.pt()); + } + + if ((candidate.isSelLcToPKPi() >= selectionFlagLc) && pdgCodeProng0 == kProton) { + const auto massLc = HfHelper::invMassLcToPKPi(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("MC/Lc/hnLcVars"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat, ptRecB, static_cast(originType)}); + registry.get(HIST("MC/Lc/hnLcVars"))->Fill(valuesToFill.data()); + } + if ((candidate.isSelLcToPiKP() >= selectionFlagLc) && pdgCodeProng0 == kPiPlus) { + const auto massLc = HfHelper::invMassLcToPiKP(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("MC/Lc/hnLcVars"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat, ptRecB, static_cast(originType)}); + registry.get(HIST("MC/Lc/hnLcVars"))->Fill(valuesToFill.data()); + } + } + } + } + + // MC gen. + for (const auto& particle : mcParticles3prong) { + if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus); + if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { + continue; + } + + float flat{-1.f}; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollisionLc, particle.mcCollision().globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + flat = fillFlat(recCol); + } + + const auto ptGen = particle.pt(); + const auto originType = particle.originMcGen(); + float ptGenB = -1.; + + registry.fill(HIST("MC/Lc/hPtGen"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGen"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGen"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGen"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGen"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGen"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGen"), particle.phi(), particle.pt()); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + ptGenB = -1.; + std::vector valuesToFill{ptGen, flat, ptGenB, static_cast(originType)}; + registry.get(HIST("MC/Lc/hnLcVarsGen"))->Fill(valuesToFill.data()); + + registry.fill(HIST("MC/Lc/hPtGenPrompt"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGenPrompt"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGenPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGenPrompt"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGenPrompt"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGenPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGenPrompt"), particle.phi(), particle.pt()); + } else if (particle.originMcGen() == RecoDecay::OriginType::NonPrompt) { + ptGenB = mcParticles3prong.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + std::vector valuesToFill{ptGen, flat, ptGenB, static_cast(originType)}; + registry.get(HIST("MC/Lc/hnLcVarsGen"))->Fill(valuesToFill.data()); + + registry.fill(HIST("MC/Lc/hPtGenNonPrompt"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGenNonPrompt"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGenNonPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGenNonPrompt"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGenNonPrompt"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGenNonPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGenNonPrompt"), particle.phi(), particle.pt()); + } + } + } + } + + template + float fillFlat(CollType const& collision) + { + rhoLatticeFV0.fill(0); + rhoLatticeFV0Calibrated.fill(0); + float flat = 9999; + if (collision.has_foundFV0()) { + auto fv0 = collision.foundFV0(); + std::bitset<8> fV0Triggers = fv0.triggerMask(); + bool isOkFV0OrA = fV0Triggers[o2::fit::Triggers::bitA]; + if (isOkFV0OrA) { + for (std::size_t ich = 0; ich < fv0.channel().size(); ich++) { + float amplCh = fv0.amplitude()[ich]; + float amplChCalibrated = fv0.amplitude()[ich]; + int chv0 = fv0.channel()[ich]; + int chv0phi = channelsToRings.at(chv0); + if (amplCh > 0.0) { + if (chv0phi > 0.0) { + amplChCalibrated *= calib[chv0phi]; + if (chv0 < nInnerCellsFV0) { + rhoLatticeFV0[chv0phi] += amplCh; + rhoLatticeFV0Calibrated[chv0phi] += amplChCalibrated; + } else { + rhoLatticeFV0[chv0phi] += amplCh / 2.; + rhoLatticeFV0Calibrated[chv0phi] += amplChCalibrated / 2.; + } + } + } + } + float flattenicityFV0 = calcFlatenicity(rhoLatticeFV0); + float flattenicityFV0Calibrated = calcFlatenicity(rhoLatticeFV0Calibrated); + if constexpr (fillHist) { + registry.fill(HIST("Flattenicity_calibrated"), 1 - flattenicityFV0Calibrated); + registry.fill(HIST("Flattenicity"), 1 - flattenicityFV0); + } + flat = 1. - flattenicityFV0; + } + } + return flat; + } + + template + float calcFlatenicity(std::array const& signals) + { + static_assert(S != 0); + + int entries = signals.size(); + float flat{-1}; + float mRho{0}; + for (int iCell = 0; iCell < entries; ++iCell) { + if (signals[iCell] > 0.0) { + mRho += 1.0 * signals[iCell]; + } + } + mRho /= (1.0 * entries); + if (mRho <= 0) { + return -1; + } + float sRhoTmp{0}; + float sRho{0}; + for (int iCell = 0; iCell < entries; ++iCell) { + if (signals[iCell] > 0.0) { + sRhoTmp += std::pow(1.0 * signals[iCell] - mRho, 2); + } + } + sRhoTmp /= (1.0 * entries * entries); + sRho = std::sqrt(sRhoTmp); + if (mRho > 0.0) { + flat = sRho / mRho; + } else { + flat = -1; + } + return flat; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 0fc735b89e50d0bc0968603e0834348fe18b5d75 Mon Sep 17 00:00:00 2001 From: lucamicheletti93 <38209984+lucamicheletti93@users.noreply.github.com> Date: Fri, 25 Sep 2026 09:27:46 +0200 Subject: [PATCH 063/648] [PWGDQ] Fixing reserve issue and adding extra cut (#17844) Co-authored-by: Lucamicheletti92 --- PWGDQ/Core/CutsLibrary.cxx | 10 ++++++++++ PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx | 3 --- PWGDQ/Tasks/taskFwdTrackPid.cxx | 2 -- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 56b283503f8..0974eab27a4 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -3060,6 +3060,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "muonEtaCut") { + cut->AddCut(GetAnalysisCut("muonEtaCut")); + return cut; + } + if (nameStr == "muonMinimalCuts") { cut->AddCut(GetAnalysisCut("muonMinimalCuts")); return cut; @@ -6575,6 +6580,11 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "muonEtaCut") { + cut->AddCut(VarManager::kEta, -4.0, -2.5); + return cut; + } + if (nameStr == "muonMinimalCuts") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); diff --git a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx index a499141f60d..6d2d711491f 100644 --- a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx +++ b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx @@ -420,7 +420,6 @@ struct tableMakerMuonMchTrkEfficiency { for (int i = 0; i < ncuts; i++) nselmuons.push_back(0); - rowCandidateBase.reserve(tracksMuon.size()); for (auto& muon : tracksMuon) { VarManager::FillTrack(muon); @@ -486,8 +485,6 @@ struct tableMakerMuonMchTrkEfficiency { for (int i = 0; i < ncuts; i++) nselmuons.push_back(0); - rowCandidateBase.reserve(tracksMuon.size()); - rowCandidateGen.reserve(tracksMuon.size()); for (auto& muon : tracksMuon) { /// /// First compute MC matched quantities using either the DQ skimmed or the Framework data models diff --git a/PWGDQ/Tasks/taskFwdTrackPid.cxx b/PWGDQ/Tasks/taskFwdTrackPid.cxx index 4947a37a942..d79c5c3c9e7 100644 --- a/PWGDQ/Tasks/taskFwdTrackPid.cxx +++ b/PWGDQ/Tasks/taskFwdTrackPid.cxx @@ -131,7 +131,6 @@ struct taskFwdTrackPid { template void runFwdTrackPid(TEvent const& event, Muons const& muons, MftTracks const& mftTracks) { - fwdPidAllList.reserve(1); for (const auto& muon : muons) { if (muon.has_matchMFTTrack() && muon.trackType() == 0 && TMath::Abs(muon.fwdDcaX()) < fConfigMaxDCA && TMath::Abs(muon.fwdDcaY()) < fConfigMaxDCA) { auto mftTrack = muon.template matchMFTTrack_as(); @@ -157,7 +156,6 @@ struct taskFwdTrackPid { template void runFwdTrackPidMC(TEvent const& event, Muons const& muons, MftTracks const& mftTracks, TEventsMC const& /*eventsMC*/, TTracksMC const& /*tracksMC*/) { - fwdPidAllList.reserve(1); for (const auto& muon : muons) { if (muon.has_matchMFTTrack() && muon.trackType() == 0 && TMath::Abs(muon.fwdDcaX()) < fConfigMaxDCA && TMath::Abs(muon.fwdDcaY()) < fConfigMaxDCA) { auto mftTrack = muon.template matchMFTTrack_as(); From ab94ead35ea5679d8950ac4bf795c27479b76fe6 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:11:21 +0200 Subject: [PATCH 064/648] =?UTF-8?q?[PWGCF]=20fix=20initialisation=20in=20s?= =?UTF-8?q?ome=20process=20paths=20in=20twoParticleCorrelationsMp=E2=80=A6?= =?UTF-8?q?=20(#18040)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx | 7 ++++++- .../Tasks/twoParticleCorrelationsMpi.cxx | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx index 629804bbbbe..da76264b369 100644 --- a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -939,7 +940,7 @@ struct FlowGenericFramework { if (corrconfigsV0.empty()) { LOGF(error, "Radial (V0) configuration contains vectors of different size - check the GFWCorrConfig configurable"); } - const size_t requiredChargedConfigs = static_cast(SpeciesCount); + const auto requiredChargedConfigs = static_cast(SpeciesCount); const size_t requiredResonanceConfigs = requiredChargedConfigs + static_cast(ResonanceCount); if (cfgFill.cfgAnalyseChargedHadrons && (corrconfigsV02.size() < requiredChargedConfigs || corrconfigsV0.size() < requiredChargedConfigs)) { LOGF(fatal, "Charged-hadron analysis requires at least %zu V02 and V0 correlation configurations", requiredChargedConfigs); @@ -2362,6 +2363,10 @@ struct FlowGenericFramework { if (!track.isPhysicalPrimary()) { return; } + auto pdgParticle = pdgDB->GetParticle(track.pdgCode()); + if (!pdgParticle || pdgParticle->Charge() == 0.) { + return; + } if (cfgFill.cfgFillQA) { fillTrackQA(track, vtxz); } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index 39988d92474..0d681481ffb 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -367,7 +367,7 @@ struct TwoParticleCorrelationsMpi { if (cfgFillAcceptanceWeights && !cfgAcceptance.value.empty()) { LOGF(fatal, "cfgFillAcceptanceWeights and cfgAcceptance are mutually exclusive: produce and apply acceptance weights in separate jobs"); } - if (cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedMultSet || doprocessMCSameDerived)) { + if (cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { LOGF(fatal, "cfgFillAcceptanceWeights requires a reconstructed derived same-event process"); } if (cfgAcceptancePhiBins < 1 || cfgAcceptanceEtaBins < 1) { @@ -655,7 +655,7 @@ struct TwoParticleCorrelationsMpi { ++nPtPtSubevents; } - if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedMultSet)) { + if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { auto recoProfiles = std::make_unique(); addConfigObjectsToObjArray(recoProfiles.get(), mCorrConfigs, cfgUserAxis == EventSeedAxis && eventClassifierPercentileAxisEnabled); fFC.setObject(new FlowContainer("FlowContainer")); From 3a2e6b6035766aa4d5f9250443653b32fbdf2c86 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Fri, 25 Sep 2026 12:45:22 +0200 Subject: [PATCH 065/648] [PWGCF] enable Tracktuner and change ME binning in HadNucleiFemto.cxx (#18044) --- .../TableProducer/HadNucleiFemto.cxx | 86 +++++++++++++------ 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 09163954b50..fc838b5f3b3 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -84,11 +84,20 @@ using CollBracket = o2::math_utils::Bracket; using HyperCandidates = aod::DataHypCandsWColl; using HyperCandidatesMC = aod::MCHypCandsWColl; using CollisionsFull = soa::Join; +// The standard Femto producer stores multNTracksPV as its collision +// multiplicity. Subscribe to PVMults only in the mixed-event process so the +// same vertex--multiplicity pool definition can optionally be reproduced. +using CollisionsFullWithPVMult = soa::Join; using CollisionsFullMC = soa::Join; using HadHyperCollisionsFull = soa::Join; using HadHyperCollisionsFullMC = soa::Join; using TrackCandidates = soa::Join; using TrackCandidatesMC = soa::Join; +// The DCA-template process explicitly subscribes to the propagated covariance +// table. This makes the propagation service enter the branch where TrackTuner +// corrections can be applied when enabled, without adding that dependency to +// unrelated MC process switches. +using TrackCandidatesMCDca = soa::Join; namespace { @@ -328,6 +337,7 @@ struct HadNucleiFemto { // Event selection and mixing configuration Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable settingNoMixedEvents{"settingNoMixedEvents", 5, "Number of mixed events per event"}; + Configurable settingUseMultiplicityBinning{"settingUseMultiplicityBinning", false, "Use vertex and multNTracksPV for event mixing instead of vertex and centrality"}; Configurable settingRequireBothSpeciesForMixing{"settingRequireBothSpeciesForMixing", false, "Use only events containing at least one selected nucleus and one selected hadron for mixed-event pairing"}; Configurable settingEnableBkgUS{"settingEnableBkgUS", false, "Enable US background"}; Configurable settingSaveUSandLS{"settingSaveUSandLS", true, "Save All Pairs"}; @@ -356,7 +366,9 @@ struct HadNucleiFemto { Configurable settingCutNsigTOFPrMin{"settingCutNsigTOFPrMin", 3.0f, "Minimum TOF Pr Nsigma cut for rejection"}; Configurable settingCutNsigTOFPiMin{"settingCutNsigTOFPiMin", 3.0f, "Minimum TOF Pi Nsigma cut for rejection"}; Configurable settingHadptMin{"settingHadptMin", 0.14f, "Minimum pT for the reference pion track cuts"}; - Configurable settingHadptMax{"settingHadptMax", 2.5f, "Maximum pT for the reference pion track cuts"}; + Configurable settingHadptMax{"settingHadptMax", 4.0f, "Maximum pT for the reference pion track cuts"}; + Configurable settingProtonPtMin{"settingProtonPtMin", 0.5f, "Minimum pT for proton candidates"}; + Configurable settingProtonPtMax{"settingProtonPtMax", 3.0f, "Maximum pT for proton candidates"}; Configurable settingPionITSInnerBarrelMin{"settingPionITSInnerBarrelMin", 3, "Minimum ITS inner barrel clusters for the reference pion track cuts"}; Configurable settingPionITSNClsMin{"settingPionITSNClsMin", 7, "Minimum ITS clusters for the reference pion track cuts"}; Configurable settingPionTPCNClsFoundMin{"settingPionTPCNClsFoundMin", 80, "Minimum found TPC clusters for the reference pion track cuts"}; @@ -388,7 +400,7 @@ struct HadNucleiFemto { struct : o2::framework::ConfigurableGroup { // cppcheck-suppress unusedStructMember std::string prefix{"helium3Pid"}; - Configurable settingRigidityMinHe3{"settingRigidityMinHe3", 0.8f, "Minimum He3 TPC rigidity"}; + Configurable settingRigidityMinHe3{"settingRigidityMinHe3", 0.0f, "Minimum He3 TPC rigidity"}; Configurable settingTPCNClsFoundMinHe3{"settingTPCNClsFoundMinHe3", 110, "Minimum found TPC clusters for He3"}; Configurable settingTPCCrossedRowsMinHe3{"settingTPCCrossedRowsMinHe3", 70, "Minimum crossed TPC rows for He3"}; Configurable settingTPCNSigmaMaxHe3{"settingTPCNSigmaMaxHe3", 3.f, "Maximum absolute TPC n-sigma for He3"}; @@ -435,7 +447,7 @@ struct HadNucleiFemto { struct : o2::framework::ConfigurableGroup { // cppcheck-suppress unusedStructMember std::string prefix{"fractionPurity"}; - Configurable settingHadronDcaFitAbsMax{"settingHadronDcaFitAbsMax", 2.4f, "Maximum absolute pion DCAxy and DCAz stored for the fraction fit"}; + Configurable settingHadronDcaFitAbsMax{"settingHadronDcaFitAbsMax", 2.4f, "Maximum absolute hadron DCAxy and DCAz stored for the fraction fit"}; Configurable settingNucleusDcaFitAbsMax{"settingNucleusDcaFitAbsMax", 1.0f, "Maximum absolute nucleus DCAxy and DCAz stored for the fraction fit"}; Configurable settingRequireRecoMCCollisionMatch{"settingRequireRecoMCCollisionMatch", true, "For the base DCA templates, require the truth particle to belong to the reconstructed collision MC label; detailed templates always store both association classes"}; } fractionPurity; @@ -620,7 +632,9 @@ struct HadNucleiFemto { // binning for EM background ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for vtxz"}; ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for centrality"}; - using BinningType = ColumnBinningPolicy; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0., 4., 8., 12., 16., 20., 24., 28., 32., 36., 40., 44., 48., 52., 56., 60., 64., 68., 72., 76., 80., 84., 88., 92., 96., 100., 200.}, "Variable-width multNTracksPV bins for event mixing"}; + using CentralityBinningType = ColumnBinningPolicy; + using MultiplicityBinningType = ColumnBinningPolicy; std::array mBBparamsNucleus{}; float mMassHad{0.f}; @@ -688,36 +702,36 @@ struct HadNucleiFemto { // Base inputs for Giorgio-style offline DCA fits. The origin axis is: // 0 primary, 1 weak decay, 2 material (every non-primary, non-decay process). - {"fraction/hDcaDataHad", "Pion DCA-fit input;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + {"fraction/hDcaDataHad", "Hadron DCA-fit input;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, {"fraction/hDcaDataNu", "Nucleus DCA-fit input;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}}}}, - {"fraction/hDcaTemplateHad", "Truth pion DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}}}}, + {"fraction/hDcaTemplateHad", "Truth hadron DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}}}}, {"fraction/hDcaTemplateNu", "Truth nucleus DCA templates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}}}}, // DCA shapes of selected candidates that are not the requested truth // species. They are kept separate from primary/weak/material so that // their normalisation can be fixed or constrained by the PID purity. - {"fraction/hDcaMisidentifiedHad", "Misidentified pion candidates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;collision association", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}}}}, + {"fraction/hDcaMisidentifiedHad", "Misidentified hadron candidates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;collision association", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}}}}, {"fraction/hDcaMisidentifiedNu", "Misidentified nucleus candidates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;collision association", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}}}}, - {"fraction/hDcaNoMCLabelHad", "Selected pion candidates without an MC label;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + {"fraction/hDcaNoMCLabelHad", "Selected hadron candidates without an MC label;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, {"fraction/hDcaNoMCLabelNu", "Selected nucleus candidates without an MC label;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}}}}, // Superset templates for optional offline refinements. They retain the // same base origin plus collision association, direct-parent category, // and the particle production radius. Projecting away the extra axes // recovers the base shapes; selecting them enables species-specific fits. - {"fraction/hDcaTemplateDetailHad", "Detailed truth pion DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;parent category;production R_{xy} (cm)", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {NParentCategories, -0.5f, static_cast(NParentCategories) - 0.5f}, {200, 0.f, 100.f}}}}, + {"fraction/hDcaTemplateDetailHad", "Detailed truth hadron DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;parent category;production R_{xy} (cm)", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {NParentCategories, -0.5f, static_cast(NParentCategories) - 0.5f}, {200, 0.f, 100.f}}}}, {"fraction/hDcaTemplateDetailNu", "Detailed truth nucleus DCA templates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;parent category;production R_{xy} (cm)", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {NParentCategories, -0.5f, static_cast(NParentCategories) - 0.5f}, {200, 0.f, 100.f}}}}, // Mother-resolved templates. There is one entry per direct mother (or a // single zero-code entry when no mother is stored). Reconstruct the exact // PDG code offline as sign * (PDG high * 10000 + PDG low). - {"fraction/hDcaMotherPdgHad", "Pion DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + {"fraction/hDcaMotherPdgHad", "Hadron DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, {"fraction/hDcaMotherPdgNu", "Nucleus DCA by exact direct-mother PDG; signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, // Hierarchical MC truth purity counters: all = correct species + mis-ID // + no label; correct species = correct collision + wrong collision; // correct collision = primary + weak + material. - {"purityMC/hHadron", "Selected pion truth composition;signed reconstructed p_{T} (GeV/c);category;centrality", {HistType::kTH3F, {{280, -7.f, 7.f}, {NPurityCategories, -0.5f, static_cast(NPurityCategories) - 0.5f}, {40, 0.f, 100.f}}}}, + {"purityMC/hHadron", "Selected hadron truth composition;signed reconstructed p_{T} (GeV/c);category;centrality", {HistType::kTH3F, {{280, -7.f, 7.f}, {NPurityCategories, -0.5f, static_cast(NPurityCategories) - 0.5f}, {40, 0.f, 100.f}}}}, {"purityMC/hNucleus", "Selected nucleus truth composition;signed physical reconstructed p_{T} (GeV/c);category;centrality", {HistType::kTH3F, {{280, -7.f, 7.f}, {NPurityCategories, -0.5f, static_cast(NPurityCategories) - 0.5f}, {40, 0.f, 100.f}}}}, // dE/dx @@ -753,12 +767,12 @@ struct HadNucleiFemto { // Purity {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -3.0f, 3.0f}}}}, + {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -5.0f, 5.0f}}}}, {"purity/h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, {"purity/h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, // Hypertriton {"hHe3TPCnsigma", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(He3)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, @@ -1247,6 +1261,18 @@ struct HadNucleiFemto { candidate.tpcNClsCrossedRows() >= hadronPid.settingPionTPCCrossedRowsMin.value; } + template + bool selectProtonTrackForDcaFit(const Ttrack& candidate) const + { + constexpr float protonEtaMax = 0.8f; + constexpr int protonTPCNClsFoundMin = 90; + constexpr int protonTPCCrossedRowsMin = 80; + return std::abs(candidate.eta()) < protonEtaMax && + std::abs(candidate.pt()) > 0.f && + candidate.tpcNClsFound() > protonTPCNClsFoundMin && + candidate.tpcNClsCrossedRows() > protonTPCCrossedRowsMin; + } + template bool selectTritonTrackForDcaFit(const Ttrack& candidate) const { @@ -1270,9 +1296,11 @@ struct HadNucleiFemto { if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { return selectPionTrackForDcaFit(candidate); } - // The fraction feature is intended for pion-nucleus configurations. Keep - // the nominal selection for other supported hadrons instead of silently - // changing their established cuts. + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return selectProtonTrackForDcaFit(candidate); + } + // Keep the nominal selection for other supported hadrons instead of + // silently changing their established cuts. return selectTrackHadron(candidate); } @@ -1459,8 +1487,6 @@ struct HadNucleiFemto { template bool selectionPIDProton(const Ttrack& candidate) { - constexpr float protonPtMin = 0.5f; - constexpr float protonPtMax = 3.0f; constexpr float protonPCombMin = 0.75f; constexpr float protonTPCNsigmaMax = 3.0f; constexpr float protonCombNsigmaMax = 3.0f; @@ -1468,7 +1494,7 @@ struct HadNucleiFemto { const float tpcNSigmaPr = candidate.tpcNSigmaPr(); mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.sign() * candidate.tpcInnerParam(), tpcNSigmaPr); - if (std::abs(candidate.pt()) <= protonPtMin || std::abs(candidate.pt()) >= protonPtMax) { + if (std::abs(candidate.pt()) <= hadronPid.settingProtonPtMin || std::abs(candidate.pt()) >= hadronPid.settingProtonPtMax) { return false; } @@ -3065,7 +3091,7 @@ struct HadNucleiFemto { std::unordered_map>& mixingPools, int& mixingRunNumber) { - const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; @@ -3217,10 +3243,11 @@ struct HadNucleiFemto { } PROCESS_SWITCH(HadNucleiFemto, processSameEvent, "Process Same event", false); - void processMixedEvent(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) + void processMixedEvent(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { LOG(debug) << "Processing mixed event"; - const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; for (const auto& collision : collisions) { mQaRegistry.fill(HIST("hMixedEventSelections"), 0); @@ -3266,7 +3293,12 @@ struct HadNucleiFemto { continue; } - const int poolBin = configuredBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + const int poolBin = eventMixing.settingUseMultiplicityBinning.value + ? multiplicityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.multNTracksPV())) + : centralityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (poolBin < 0) { + continue; + } auto& pool = mMixingPools[poolBin]; mQaRegistry.fill(HIST("hMixingPoolOccupancy"), pool.size()); @@ -3453,7 +3485,7 @@ struct HadNucleiFemto { // MC DCA templates use the relaxed-DCA selection. MC purity uses the full // nominal candidate selection, including its DCA requirement. - void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMC& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) { for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { @@ -3602,10 +3634,12 @@ struct HadNucleiFemto { } else if (passTrackNu && useHelium3Nucleus()) { const float tpcNSigmaHe3 = computeNSigmaHe3(track); const float signedPtHe3 = track.sign() * 2.f * track.pt(); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtHe3, tpcNSigmaHe3); o2::aod::ITSResponse itsResponse; const float itsNSigmaHe3 = itsResponse.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtHe3, itsNSigmaHe3); + if (itsNSigmaHe3 >= helium3Pid.settingITSNSigmaMinHe3) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtHe3, tpcNSigmaHe3); + } } else if (passTrackNu && useTritonNucleus()) { const float tpcNSigmaTr = track.tpcNSigmaTr(); o2::aod::ITSResponse itsResponse; From 49d45a3dbb49ac161e9bb7b39444033d10a6910b Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Fri, 25 Sep 2026 13:40:01 +0200 Subject: [PATCH 066/648] [PWGCF] Update cutculator in femto framework (#18056) --- PWGCF/Femto/Macros/cutculator.py | 90 +++++++++++++++++++++++++++- PWGCF/Femto/Macros/cutculator_gui.py | 78 ++++++++++++++++++++++-- 2 files changed, 161 insertions(+), 7 deletions(-) diff --git a/PWGCF/Femto/Macros/cutculator.py b/PWGCF/Femto/Macros/cutculator.py index 7ce759182dc..e2d534b64e2 100644 --- a/PWGCF/Femto/Macros/cutculator.py +++ b/PWGCF/Femto/Macros/cutculator.py @@ -181,7 +181,77 @@ def ask_user_selection(group): return selected_bins -def main(rootfile_path, tdir_path="femto-producer"): +def parse_bitmask(text): + """Parse a bitmask given as decimal, hex (0x...) or binary (0b...).""" + text = text.strip() + try: + value = int(text, 0) + except ValueError: + # int(x, 0) rejects decimals with leading zeros, e.g. "010" + value = int(text, 10) + if value < 0: + raise ValueError("bitmask must be non-negative") + return value + + +def is_minimal_bin(b): + return b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0" + + +def print_decoded_bitmask(groups, bitmask): + """ + Print all cuts selected by the given bitmask, grouped by selection. Minimal + cuts with BitPosition X (always applied) are listed as well for every + selection where no stricter minimal bit is set. + """ + print("\n=======================================") + print(f"Cuts selected by bitmask {bitmask} ({hex(bitmask)}):") + print("=======================================\n") + + known_bits = set() + for sel_name, group in groups.items(): + selected = [] + has_minimal_bit = False + for b in group: + pos = b.get("BitPosition", "X") + if pos.upper() == "X": + continue + known_bits.add(int(pos)) + if bitmask & (1 << int(pos)): + selected.append(b) + if is_minimal_bin(b): + has_minimal_bit = True + + entries = [] + if not has_minimal_bit: + for b in group: + if is_minimal_bin(b) and b.get("BitPosition", "X").upper() == "X": + entries.append(f"{format_value_with_comment(b)} [minimal, no bit]") + for b in selected: + if is_minimal_bin(b): + kind = "minimal" + elif b.get("OptionalCut", "0") == "1": + kind = "optional" + else: + kind = "neutral" + entries.append(f"{format_value_with_comment(b)} [{kind}, bit {b.get('BitPosition')}]") + + if entries: + print(f" {sel_name}:") + for e in entries: + print(f" {e}") + + unknown_bits = [i for i in range(bitmask.bit_length()) if bitmask & (1 << i) and i not in known_bits] + if unknown_bits: + print(f"\nWarning: bit(s) {', '.join(map(str, unknown_bits))} are set but not defined in this histogram!") + + print("\nBitmask:") + print(f" Decimal: {bitmask}") + print(f" Binary: {bin(bitmask)}") + print(f" Hex: {hex(bitmask)}") + + +def main(rootfile_path, tdir_path="femto-producer", decode=False): print(f"Opening ROOT file: {rootfile_path}") f = ROOT.TFile.Open(rootfile_path) if not f: @@ -243,6 +313,17 @@ def main(rootfile_path, tdir_path="femto-producer"): sel_name = b.get("SelectionName", f"unknown_{b['_bin_index']}") groups.setdefault(sel_name, []).append(b) + # decode an existing bitmask instead of building a new one + if decode: + while True: + try: + bitmask = parse_bitmask(input("\nEnter bitmask to decode (dec, 0x hex or 0b bin): ")) + break + except ValueError: + print("Invalid bitmask.") + print_decoded_bitmask(groups, bitmask) + return + selected_bins = [] for group in groups.values(): @@ -280,5 +361,10 @@ def main(rootfile_path, tdir_path="femto-producer"): parser = argparse.ArgumentParser() parser.add_argument("rootfile", help="Path to ROOT file") parser.add_argument("--dir", default="femto-producer", help="TDirectory path in ROOT file") + parser.add_argument( + "--bitmask", + action="store_true", + help="Ask for a bitmask after selecting the histogram and print the cuts it selects", + ) args = parser.parse_args() - main(args.rootfile, args.dir) + main(args.rootfile, args.dir, args.bitmask) diff --git a/PWGCF/Femto/Macros/cutculator_gui.py b/PWGCF/Femto/Macros/cutculator_gui.py index 039908fa158..9f0393895c6 100644 --- a/PWGCF/Femto/Macros/cutculator_gui.py +++ b/PWGCF/Femto/Macros/cutculator_gui.py @@ -231,6 +231,7 @@ def __init__(self, rootfile=None, tdir="femto-producer"): self._is_filter_hist = False self._vars = {} # (SelectionName, idx) → BooleanVar self._check_labels = {} # (SelectionName, idx) → Label (custom checkbox glyph) + self._setters = {} # (SelectionName, idx) → callable(bool) setting the checkbox state + glyph self._build_ui() @@ -264,6 +265,26 @@ def _build_ui(self): self._hist_combo.pack(side="left", padx=6) self._hist_combo.bind("<>", self._on_hist_selected) + # ── bitmask input: check all cuts corresponding to a given bitmask ── + tk.Label(bar, text="Bitmask:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left", padx=(20, 0)) + self._mask_var = tk.StringVar() + mask_entry = tk.Entry( + bar, + textvariable=self._mask_var, + width=16, + font=FONT_BODY, + bg=BG, + fg=FG, + insertbackground=FG, + relief="flat", + highlightthickness=1, + highlightbackground=BORDER, + highlightcolor=ACCENT, + ) + mask_entry.pack(side="left", padx=6) + mask_entry.bind("", lambda _e: self._apply_bitmask()) + self._make_button(bar, "Set Bitmask", self._apply_bitmask, ACCENT).pack(side="left", padx=2) + self._style_combobox() # ── legend ── @@ -470,6 +491,7 @@ def _on_hist_selected(self, _e=None): self._hist = hist self._vars = {} self._check_labels = {} + self._setters = {} # Always start from a clean summary panel — forget()-ing the pane only # hides it, it does not destroy previously built rows. @@ -714,10 +736,15 @@ def _build_loosest_row(self, parent, sel_name, idx, b): self._vars[(sel_name, idx)] = var self._check_labels[(sel_name, idx)] = check_lbl + def set_state(state): + var.set(state) + check_lbl.config(text="[x]" if state else "[ ]", fg=ACCENT_ALWAYS if state else FG_DIM) + text_lbl.config(fg=FG if state else FG_DIM) + + self._setters[(sel_name, idx)] = set_state + def toggle(_e=None): - var.set(not var.get()) - check_lbl.config(text="[x]" if var.get() else "[ ]", fg=ACCENT_ALWAYS if var.get() else FG_DIM) - text_lbl.config(fg=FG if var.get() else FG_DIM) + set_state(not var.get()) self._update_bitmask() for w in (row, check_lbl, text_lbl): @@ -752,14 +779,55 @@ def _build_bin_row(self, parent, sel_name, idx, b, kind): if pos.upper() != "X": tk.Label(row, text=f"bit {pos}", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM, width=8).pack(side="right", padx=4) + def set_state(state): + var.set(state) + check_lbl.config(text="[x]" if state else "[ ]", fg=color if state else FG_DIM) + + self._setters[(sel_name, idx)] = set_state + def toggle(_e=None): - var.set(not var.get()) - check_lbl.config(text="[x]" if var.get() else "[ ]", fg=color if var.get() else FG_DIM) + set_state(not var.get()) self._update_bitmask() for w in (row, check_lbl, text_lbl): w.bind("", toggle) + # ── Bitmask input ───────────────────────────────────────────────────────── + def _apply_bitmask(self): + """Check exactly the cuts whose bit is set in the entered bitmask. The + always-applied minimal floors are added to the summary automatically.""" + if self._is_filter_hist or not self._groups: + messagebox.showinfo("Not applicable", "Load a selection histogram first.") + return + + text = self._mask_var.get().strip() + try: + bitmask = int(text, 0) + except ValueError: + try: + # int(x, 0) rejects decimals with leading zeros, e.g. "010" + bitmask = int(text, 10) + except ValueError: + bitmask = -1 + if bitmask < 0: + messagebox.showerror("Invalid bitmask", f"Cannot parse '{text}'.\nUse decimal, 0x hex or 0b binary.") + return + + known_bits = set() + for (sel_name, idx), set_state in self._setters.items(): + pos = bit_position_int(self._groups[sel_name][idx]) + if pos >= 0: + known_bits.add(pos) + set_state(pos >= 0 and bool(bitmask & (1 << pos))) + self._update_bitmask() + + unknown_bits = [i for i in range(bitmask.bit_length()) if bitmask & (1 << i) and i not in known_bits] + if unknown_bits: + messagebox.showwarning( + "Unknown bits", + f"Bit(s) {', '.join(map(str, unknown_bits))} are set but not defined in this histogram.", + ) + # ── Bitmask computation + summary update ────────────────────────────────── def _update_bitmask(self): if self._is_filter_hist: From a4a3857b66cc3577da3cb03f6eaba32bbf34be00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:18:59 +0200 Subject: [PATCH 067/648] [Infrastructure] Bump oxsecurity/megalinter from 10.0.0 to 10.1.0 (#17902) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/mega-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index 00ae50f1d00..3e0aebe587f 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -39,7 +39,7 @@ jobs: id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.io/flavors/ - uses: oxsecurity/megalinter@v10.0.0 + uses: oxsecurity/megalinter@v10.1.0 env: # All available variables are described in documentation: # https://megalinter.io/configuration/ From 4aeb9f37e93bb2460b8278e324bc7a1715ee9397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:21:12 +0200 Subject: [PATCH 068/648] [Infrastructure] Add code-check in the PR cleanup list (#18058) --- .github/workflows/clean-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index 7ec1b0355a6..be471ce2287 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -39,6 +39,11 @@ name: Clean PR checks type: boolean default: true + 'check_build/O2Physics/code-check': + description: build/O2Physics/code-check + type: boolean + default: true + permissions: {} jobs: From 8f09b7b6bfa236ce3c93d82730801c1665bf359e Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Fri, 25 Sep 2026 23:57:00 +0800 Subject: [PATCH 069/648] [PWGCF] Add efficiency corrections and rename and redesign some features (#18049) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 733 +++++++++++------- 1 file changed, 442 insertions(+), 291 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 9e32c7d2a4f..ef6af20e12d 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,6 @@ #include #include -#include #include #include #include @@ -72,7 +72,8 @@ enum ECentralityEstimator { eFT0C = 0, eFT0M, eFV0A, - eNTPV + eNTPV, + eCentralityEstimator_N }; enum EMultiplicityTables { @@ -80,7 +81,8 @@ enum EMultiplicityTables { eMultFV0M, eMultFT0C, eMultFT0M, - eMultNTracksPV + eMultNTracksPV, + eMultiplicityTables_N }; enum ERecSim { @@ -90,8 +92,9 @@ enum ERecSim { }; enum ECuts { - eNoCuts = 0, - eWithCuts + eBefore = 0, + eAfter, + eCuts_N }; enum ERealMC { @@ -100,9 +103,9 @@ enum ERealMC { }; enum EProcess { - eProcessRec = 0, // Run 3, only reconstructed - eProcessRecSim, // Run 3, both reconstructed and simulated - eProcessSim, // Run 3, only simulated + eProcessRec = 0, + eProcessRecSim, + eProcessSim, eProcess_N }; @@ -110,6 +113,7 @@ enum EParticleHistograms { eHistPt = 0, eHistPhi, eHistEta, + eHistCharge, eParticleHistograms_N }; @@ -124,8 +128,16 @@ enum EEventHistograms { eEventHistograms_N }; +enum EPhiAndPtHistograms { + ePhiRec = 0, + ePtRec, + ePtMC, + ePhiAndPtHistograms_N +}; + enum EExternalHistograms { - eHistWeights = 0, + ePhi = 0, + ePt, eExternalHistograms_N }; @@ -134,9 +146,34 @@ enum EObservables { eObservablesHist_N }; +// *) Lists of names: +static constexpr std::array CentralityEstimatorNames = { + "FT0C", + "FT0M", + "FV0A", + "NTPV"}; + +static constexpr std::array MultiplicityTablesNames = { + "multTPC", + "multFV0M", + "multFT0C", + "multFT0M", + "multNTracksPV"}; + +static constexpr std::array WeightsNames = { + "ePhi", + "ePt"}; + +static constexpr std::array CutsNames = { + "eBefore", + "eAfter"}; + // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root { + // *) Misc: + bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwiese, a crash + // *) Base TList to hold all output objects: TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function OutputObj fBaseList{sBaseListName.Data(), @@ -145,6 +182,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) CCDB: Service ccdb{}; // support for offline callibration data base, not needed for the time being... + Service pdg{}; // *) Define configurables: Configurable centralityEstimator{"centralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; @@ -156,19 +194,19 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/O2challenge-", "path to external ROOT file which holds all particle weights"}; // *) binnings - Configurable> cfPtBins{"cfPtBins", {2000, 0., 5.}, "nPtBins, ptMin, ptMax"}; // example for an array - Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; // example for an array - Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; // example for an array - - Configurable> cfMultBinsRec{"cfMultBinsRec", {400, 0., 40000.}, "nMultBins, multMin, multMax"}; - Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "nMultBins, multMin, multMax"}; - Configurable> cfMultBinsSim{"cfMultBinsSim", {400, 0., 40000.}, "nMultBins, multMin, multMax"}; + Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; + Configurable> cfMultBins{"cfMultBins", {400, 0., 40000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "Reference mult bins: nMultBins, multMin, multMax"}; Configurable> cfVxBins{"cfVxBins", {300, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; Configurable> cfVyBins{"cfVyBins", {300, -0.01, 0.01}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; Configurable> cfVzBins{"cfVzBins", {300, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; - Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; Configurable> cfIpBins{"cfIpBins", {100, 0., 20.}, "Impact parameters hist (MC only): nIPBins, ipMin, ipMax"}; + Configurable> cfPtBins{"cfPtBins", {2000, 0., 5.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; + // *) Cuts // event level cuts Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; @@ -180,6 +218,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}, with convention: min <= pt < max"}; Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "switch to apply pt cut"}; Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta < max"}; + Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; // *) misc Configurable sigmaInel{"sigmaInel", 7.71, "inelastic cross section in mb"}; @@ -196,18 +235,23 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct ParticleHistograms { TList* fParticleHistogramsList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHistograms{}; - } pc; // you have to prepend "pc." for all objects name in this group later in the code + } pc; // *) Event histograms: struct EventHistograms { - TList* fEventHistogramsList = nullptr; //!, 2>, eEventHistograms_N> fEventHistograms{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] - } ec; // prepend "ec." for event counters + } ec; + + struct PhiAndPtHistograms { + TList* fPhiAndPtHistogramsList = nullptr; + std::array, ePhiAndPtHistograms_N> fPhiAndPtHistograms{}; //! [type][before, after cuts] + } pph; // *) External histograms: struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - TH1D* fhistWeights = nullptr; + std::array, eExternalHistograms_N> fWeights{}; //! [type][before, after cuts] } ex; struct Observables { @@ -219,7 +263,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct QualityAssurance { TList* fQualityAssuranceList = nullptr; //!Get("hist-name"). - // Usage: TH1D *hist = (TH1D*) getObjectFromList("some-valid-TList-pointer","some-object-name"); + // Usage: TH1F *hist = (TH1F*) getObjectFromList("some-valid-TList-pointer","some-object-name"); // Insanity checks: if (!list) { @@ -372,10 +416,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return nullptr; } // TObject* getObjectFromList(TList *list, char *objectName) - TH1D* getHistogramWithWeights(const char* filePath, const char* runNumber) + TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) { // *) Return value: - TH1D* hist = nullptr; + TH1F* hist = nullptr; TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights @@ -406,7 +450,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } weightsFile->GetObject("ccdb_object", baseList); if (!baseList) { - // weightsFile->ls(); LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -428,7 +471,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // File you want to access is in your home dir in CCDB: // Remember that here I do not access the file; instead, I directly access the object in that file. // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case - ccdb->setURL("https://alice-ccdb.cern.ch"); // to be able to use "ccdb" this object in your analysis task, see 4b/ below + ccdb->setURL("https://alice-ccdb.cern.ch"); baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); if (!baseList) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); @@ -457,13 +500,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TFile* weightsFile = TFile::Open(filePath, "READ"); if (!weightsFile) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m can't open file", __FUNCTION__, __LINE__); } - weightsFile->GetObject("ccdb_object", baseList); - if (!baseList) { - // weightsFile->ls(); LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -480,28 +520,64 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: - // listWithRuns->ls(); - if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s: listWithRuns is null for run %s\033[0m", __FUNCTION__, runNumber); } - hist = dynamic_cast(listWithRuns->FindObject("h_invert")); + hist = dynamic_cast(listWithRuns->FindObject(histName)); if (!hist) { LOGF(fatal, "%s: histogram 'hist1' not found in run list", __FUNCTION__); } hist->SetDirectory(nullptr); - auto histClone = dynamic_cast(hist->Clone()); + auto histClone = dynamic_cast(hist->Clone()); histClone->SetDirectory(nullptr); - delete baseList; // release back the memory + delete baseList; return histClone; } // templates + template + float chooseCent(T1 const& collision) + { + switch (centralityEstimator) { + case eFT0C: + return static_cast(collision.centFT0C()); + case eFT0M: + return static_cast(collision.centFT0M()); + case eFV0A: + return static_cast(collision.centFV0A()); + case eNTPV: + return static_cast(collision.centNTPV()); + default: + LOG(warning) << "Unknown centrality estimator. Using FT0C as default."; + return static_cast(collision.centFT0C()); + } + } + + template + float chooseMult(T1 const& collision) + { + switch (multiplicityTables) { + case eMultTPC: + return static_cast(collision.multTPC()); + case eMultFV0M: + return static_cast(collision.multFV0M()); + case eMultFT0C: + return static_cast(collision.multFT0C()); + case eMultFT0M: + return static_cast(collision.multFT0M()); + case eMultNTracksPV: + return static_cast(collision.multNTracksPV()); + default: + LOG(warning) << "Unknown multiplicity. Using multTPC as default."; + return static_cast(collision.multTPC()); + } + } + template bool eventCuts(T1 const& collision) { @@ -534,72 +610,28 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void eventHistFill(T1 const& collision, T2 const& tracks) { - auto thisCent = collision.centFT0C(); // use auto to determine the type - switch (centralityEstimator) { - case eFT0C: - thisCent = collision.centFT0C(); - break; - case eFT0M: - thisCent = collision.centFT0M(); - break; - case eFV0A: - thisCent = collision.centFV0A(); - break; - case eNTPV: - thisCent = collision.centNTPV(); - break; - default: - LOG(warning) << "Unknown centrality estimator. Using FT0C as default."; - break; // thisCent is already FT0C - } - - LOGF(info, - "centFT0C=%.6f centFT0M=%.6f centFV0A=%.6f", - collision.centFT0C(), - collision.centFT0M(), - collision.centFV0A()); - - auto thisRefMult = collision.multTPC(); // use auto to determine the type - switch (multiplicityTables) { - case eMultTPC: - thisRefMult = collision.multTPC(); - break; - case eMultFV0M: - thisRefMult = collision.multFV0M(); - break; - case eMultFT0C: - thisRefMult = collision.multFT0C(); - break; - case eMultFT0M: - thisRefMult = collision.multFT0M(); - break; - case eMultNTracksPV: - thisRefMult = collision.multNTracksPV(); - break; - default: - LOG(warning) << "Unknown multiplicity. Using multTPC as default."; - break; // thisRefMult is already multTPC - } + auto thisCent = chooseCent(collision); + auto thisRefMult = chooseMult(collision); if constexpr (rs == eRec || rs == eRecAndSim) { // Fill reconstructed-level event histograms if (rm == eReal) { int multiplicityRec = static_cast(tracks.size()); - if constexpr (cuts == eNoCuts) { - ec.fEventHistograms[eHistMultiplicity][eRec][eNoCuts]->Fill(multiplicityRec); - ec.fEventHistograms[eHistCentrality][eRec][eNoCuts]->Fill(thisCent); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eNoCuts]->Fill(thisRefMult); - ec.fEventHistograms[eHistVertexX][eRec][eNoCuts]->Fill(collision.posX()); - ec.fEventHistograms[eHistVertexY][eRec][eNoCuts]->Fill(collision.posY()); - ec.fEventHistograms[eHistVertexZ][eRec][eNoCuts]->Fill(collision.posZ()); + if constexpr (cuts == eBefore) { + ec.fEventHistograms[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHistograms[eHistCentrality][eRec][eBefore]->Fill(thisCent); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHistograms[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); + ec.fEventHistograms[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); + ec.fEventHistograms[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); } - if constexpr (cuts == eWithCuts) { - ec.fEventHistograms[eHistMultiplicity][eRec][eWithCuts]->Fill(multiplicityRec); - ec.fEventHistograms[eHistCentrality][eRec][eWithCuts]->Fill(thisCent); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eWithCuts]->Fill(thisRefMult); - ec.fEventHistograms[eHistVertexX][eRec][eWithCuts]->Fill(collision.posX()); - ec.fEventHistograms[eHistVertexY][eRec][eWithCuts]->Fill(collision.posY()); - ec.fEventHistograms[eHistVertexZ][eRec][eWithCuts]->Fill(collision.posZ()); + if constexpr (cuts == eAfter) { + ec.fEventHistograms[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHistograms[eHistCentrality][eRec][eAfter]->Fill(thisCent); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHistograms[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); + ec.fEventHistograms[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); + ec.fEventHistograms[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); } } @@ -613,28 +645,23 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto thisMCCollision = collision.mcCollision(); // corresponding MC truth simulated particle int multiplicitySim = static_cast(tracks.size()); auto impactParameter = thisMCCollision.impactParameter(); - LOGF(info, - "Reco collision = %d, MC collision = %d, b = %f", - collision.globalIndex(), - thisMCCollision.globalIndex(), - impactParameter); auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter - if constexpr (cuts == eNoCuts) { - ec.fEventHistograms[eHistMultiplicity][eSim][eNoCuts]->Fill(multiplicitySim); - ec.fEventHistograms[eHistCentrality][eSim][eNoCuts]->Fill(centralityMC); - ec.fEventHistograms[eHistImpactParameter][eSim][eNoCuts]->Fill(impactParameter); - ec.fEventHistograms[eHistVertexX][eSim][eNoCuts]->Fill(thisMCCollision.posX()); - ec.fEventHistograms[eHistVertexY][eSim][eNoCuts]->Fill(thisMCCollision.posY()); - ec.fEventHistograms[eHistVertexZ][eSim][eNoCuts]->Fill(thisMCCollision.posZ()); + if constexpr (cuts == eBefore) { + ec.fEventHistograms[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHistograms[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHistograms[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHistograms[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHistograms[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHistograms[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); } - if constexpr (cuts == eWithCuts) { - ec.fEventHistograms[eHistMultiplicity][eSim][eWithCuts]->Fill(multiplicitySim); - ec.fEventHistograms[eHistCentrality][eSim][eWithCuts]->Fill(centralityMC); - ec.fEventHistograms[eHistImpactParameter][eSim][eWithCuts]->Fill(impactParameter); - ec.fEventHistograms[eHistVertexX][eSim][eWithCuts]->Fill(thisMCCollision.posX()); - ec.fEventHistograms[eHistVertexY][eSim][eWithCuts]->Fill(thisMCCollision.posY()); - ec.fEventHistograms[eHistVertexZ][eSim][eWithCuts]->Fill(thisMCCollision.posZ()); + if constexpr (cuts == eAfter) { + ec.fEventHistograms[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHistograms[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHistograms[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHistograms[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHistograms[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHistograms[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); } } // end of if (rm == eMC) { } @@ -644,7 +671,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool particleCuts(T const& track) { - // add eta cuts!!!!!! if constexpr (rs == eRec || rs == eRecAndSim) { if (cfPtCutSwitch) // pt cuts for Rec { @@ -686,6 +712,27 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // end of if (rm == eMC) { } } + if (cfChargeCutSwitch) // charge cuts for Rec + { + if (rm == eReal) { + if (track.sign() == 0) { + return false; + } + } + if constexpr (rs == eRecAndSim) // charge cuts for Sim + { + if (rm == eMC) { + if (!track.has_mcParticle()) { + return false; + } + auto mcParticle = track.mcParticle(); + auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); + if (chargeMC == 0) { + return false; + } + } // end of if (rm == eMC) { + } + } } return true; @@ -696,16 +743,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to { if constexpr (rs == eRec || rs == eRecAndSim) { if (rm == eReal) { - if constexpr (cuts == eNoCuts) { - pc.fParticleHistograms[eHistPt][eRec][eNoCuts]->Fill(track.pt()); - pc.fParticleHistograms[eHistPhi][eRec][eNoCuts]->Fill(track.phi()); - pc.fParticleHistograms[eHistEta][eRec][eNoCuts]->Fill(track.eta()); + if constexpr (cuts == eBefore) { + pc.fParticleHistograms[eHistPt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHistograms[eHistPhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHistograms[eHistEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHistograms[eHistCharge][eRec][eBefore]->Fill(track.sign()); } - if constexpr (cuts == eWithCuts) { - pc.fParticleHistograms[eHistPt][eRec][eWithCuts]->Fill(track.pt()); - pc.fParticleHistograms[eHistPhi][eRec][eWithCuts]->Fill(track.phi()); - pc.fParticleHistograms[eHistEta][eRec][eWithCuts]->Fill(track.eta()); + if constexpr (cuts == eAfter) { + pc.fParticleHistograms[eHistPt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHistograms[eHistPhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHistograms[eHistEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHistograms[eHistCharge][eRec][eAfter]->Fill(track.sign()); } } @@ -713,49 +762,68 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo if constexpr (rs == eRecAndSim) { + if (rm == eReal) { + if constexpr (cuts == eBefore) { + pc.fParticleHistograms[eHistCharge][eSim][eBefore]->Fill(track.sign()); + pph.fPhiAndPtHistograms[ePhiRec][eBefore]->Fill(track.phi()); + pph.fPhiAndPtHistograms[ePtRec][eBefore]->Fill(track.pt()); + } + if constexpr (cuts == eAfter) { + pc.fParticleHistograms[eHistCharge][eSim][eAfter]->Fill(track.sign()); + pph.fPhiAndPtHistograms[ePhiRec][eAfter]->Fill(track.phi()); + pph.fPhiAndPtHistograms[ePtRec][eAfter]->Fill(track.pt()); + } + } + if (rm == eMC) { if (!track.has_mcParticle()) { LOGF(warning, " No MC particle for this track, skip..."); return; } - auto mcParticle = track.mcParticle(); // corresponding MC truth simulated particle - if constexpr (cuts == eNoCuts) { - pc.fParticleHistograms[eHistPt][eSim][eNoCuts]->Fill(mcParticle.pt()); - pc.fParticleHistograms[eHistPhi][eSim][eNoCuts]->Fill(mcParticle.phi()); - pc.fParticleHistograms[eHistEta][eSim][eNoCuts]->Fill(mcParticle.eta()); + auto mcParticle = track.mcParticle(); + auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); + const int chargeMCUnit = 3; + if (std::abs(chargeMC) == chargeMCUnit) { + chargeMC /= chargeMCUnit; + } + if constexpr (cuts == eBefore) { + pc.fParticleHistograms[eHistPt][eSim][eBefore]->Fill(mcParticle.pt()); + pc.fParticleHistograms[eHistPhi][eSim][eBefore]->Fill(mcParticle.phi()); + pc.fParticleHistograms[eHistEta][eSim][eBefore]->Fill(mcParticle.eta()); + pc.fParticleHistograms[eHistCharge][eSim][eBefore]->Fill(chargeMC); + pph.fPhiAndPtHistograms[ePtMC][eBefore]->Fill(mcParticle.pt()); } - if constexpr (cuts == eWithCuts) { - pc.fParticleHistograms[eHistPt][eSim][eWithCuts]->Fill(mcParticle.pt()); - pc.fParticleHistograms[eHistPhi][eSim][eWithCuts]->Fill(mcParticle.phi()); - pc.fParticleHistograms[eHistEta][eSim][eWithCuts]->Fill(mcParticle.eta()); + if constexpr (cuts == eAfter) { + pc.fParticleHistograms[eHistPt][eSim][eAfter]->Fill(mcParticle.pt()); + pc.fParticleHistograms[eHistPhi][eSim][eAfter]->Fill(mcParticle.phi()); + pc.fParticleHistograms[eHistEta][eSim][eAfter]->Fill(mcParticle.eta()); + pc.fParticleHistograms[eHistCharge][eSim][eAfter]->Fill(chargeMC); + pph.fPhiAndPtHistograms[ePtMC][eAfter]->Fill(mcParticle.pt()); } } // end of if (rm == eMC) { } } } + void loadWeights(int runNumber) + { + for (int i = 0; i < eExternalHistograms_N; ++i) { + for (int j = 0; j < eCuts_N; ++j) { + ex.fWeights[i][j] = + getHistogramWithWeights( + cfFileWithWeights.value.c_str(), + Form("%d", runNumber), + Form("[%s][%s]", WeightsNames[i], CutsNames[j])); + ex.fExternalHistogramsList->Add(ex.fWeights[i][j]); + } + } + } + template void qaFill(T1 const& collision) { - auto thisCent = collision.centFT0C(); // use auto to determine the type - switch (centralityEstimator) { - case eFT0C: - thisCent = collision.centFT0C(); - break; - case eFT0M: - thisCent = collision.centFT0M(); - break; - case eFV0A: - thisCent = collision.centFV0A(); - break; - case eNTPV: - thisCent = collision.centNTPV(); - break; - default: - LOG(warning) << "Unknown centrality estimator. Using FT0C as default."; - break; // thisCent is already FT0C - } + auto thisCent = chooseCent(collision); if constexpr (rs == eRecAndSim || rs == eSim) { if (!collision.has_mcCollision()) { return; @@ -775,6 +843,22 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fDryRun) { return; } + int thisRunNumber = collision.bc().runNumber(); + + // Set titles for all PhiAndPtHistograms to the runNumber + if (rs == eRecAndSim && isFirstCollision) { + for (int i = 0; i < eCuts_N; ++i) { + for (int j = 0; j < ePhiAndPtHistograms_N; ++j) { + pph.fPhiAndPtHistograms[j][i]->SetTitle(Form("%d", thisRunNumber)); + } + } + } + + // Get weights + if (cfExternalFileSwitch && isFirstCollision) { + loadWeights(thisRunNumber); + } + isFirstCollision = false; // Fill Quality Assurance if (qualityAssuranceSwitch) { @@ -785,64 +869,68 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const bool passesEventCutsMC = eventCuts(collision); // Fill Event Hist - eventHistFill(collision, tracks); - eventHistFill(collision, tracks); + eventHistFill(collision, tracks); + eventHistFill(collision, tracks); if (cfMasterCutSwitch) { if (passesEventCutsReal) { - eventHistFill(collision, tracks); + eventHistFill(collision, tracks); } if (passesEventCutsMC) { - eventHistFill(collision, tracks); + eventHistFill(collision, tracks); } } // Print current run number: - LOGF(info, "Run number: %d", collision.bc().runNumber()); - - // Print centrality estimated with the selected estimator: - // LOGF(info, "Centrality: %f", thisCent); - + LOGF(info, "Run number: %d", thisRunNumber); // Print vertex X position: LOGF(info, "Vertex X position: %f", collision.posX()); std::vector n2 = {-2, 2}; - auto qVectorsTableNoCutsReal = initQVectorsTable(2, n2); - auto qVectorsTableWithCutsReal = initQVectorsTable(2, n2); + auto qVectorsTableBeforeCutsReal = initQVectorsTable(2, n2); + auto qVectorsTableAfterCutsReal = initQVectorsTable(2, n2); // Main loop over particles: auto track = tracks.iteratorAt(0); // set the type and scope from one instance for (int64_t i = 0; i < tracks.size(); i++) { - // Print track azimuthal angle: - // LOGF(info, "Track azimuthal angle: %f", track.phi()); - track = tracks.iteratorAt(i); + const float thisPhi = track.phi(); + const float thisPt = track.pt(); + float wPhiBefore = 1.f; + float wPhiAfter = 1.f; + float wPtBefore = 1.f; + float wPtAfter = 1.f; + if (cfExternalFileSwitch) { + wPhiBefore = ex.fWeights[ePhi][eBefore]->GetBinContent(ex.fWeights[ePhi][eBefore]->FindBin(thisPhi)); + wPhiAfter = ex.fWeights[ePhi][eAfter]->GetBinContent(ex.fWeights[ePhi][eAfter]->FindBin(thisPhi)); + wPtBefore = ex.fWeights[ePt][eBefore]->GetBinContent(ex.fWeights[ePt][eBefore]->FindBin(thisPt)); + wPtAfter = ex.fWeights[ePt][eAfter]->GetBinContent(ex.fWeights[ePt][eAfter]->FindBin(thisPt)); + } - particleHistFill(track); - updateQVectorsTable(qVectorsTableNoCutsReal, track.phi()); - - particleHistFill(track); + particleHistFill(track); + particleHistFill(track); + updateQVectorsTable(qVectorsTableBeforeCutsReal, thisPhi, wPhiBefore * wPtBefore); if (cfMasterCutSwitch) { if (passesEventCutsReal && particleCuts(track)) { - particleHistFill(track); - updateQVectorsTable(qVectorsTableWithCutsReal, track.phi()); + particleHistFill(track); + updateQVectorsTable(qVectorsTableAfterCutsReal, thisPhi, wPhiAfter * wPtAfter); } if (passesEventCutsMC && particleCuts(track)) { - particleHistFill(track); + particleHistFill(track); } } } // end of for (int64_t i = 0; i < tracks.size(); i++) { std::vector resultMultCorr(2, TComplex(0., 0.)); - resultMultCorr = two(qVectorsTableNoCutsReal, n2); + resultMultCorr = two(qVectorsTableBeforeCutsReal, n2); if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eNoCuts]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); + obs.fProfTwo[eRec][eBefore]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); } if (cfMasterCutSwitch && passesEventCutsReal) { - resultMultCorr = two(qVectorsTableWithCutsReal, n2); + resultMultCorr = two(qVectorsTableAfterCutsReal, n2); if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eWithCuts]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); + obs.fProfTwo[eRec][eAfter]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); } } @@ -852,10 +940,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to void init(InitContext&) { // ... code to book and initialize all analysis objects ... - const int withCutFillColor = kGreen - 10; - const int withCutLineColor = kGreen; - const int noCutFillColor = kRed - 10; - const int noCutLineColor = kRed; + const int afterCutFillColor = kGreen - 10; + const int afterCutLineColor = kGreen; + const int beforeCutFillColor = kRed - 10; + const int beforeCutLineColor = kRed; // *) Set automatically what to process, from an implicit variable "doprocessSomEProcessName" within a PROCESS_SWITCH clause: tc.fProcess[eProcessRec] = doprocessRec; @@ -877,11 +965,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ex.fExternalHistogramsList->SetName("ExternalHistograms"); ex.fExternalHistogramsList->SetOwner(true); fBaseList->Add(ex.fExternalHistogramsList); - - // *) Book and Fill external hist - ex.fhistWeights = getHistogramWithWeights(cfFileWithWeights.value.c_str(), "000123456"); - ex.fhistWeights->SetTitle(cfFileWithWeights.value.c_str()); - ex.fExternalHistogramsList->Add(ex.fhistWeights); } // *) Book particle TLists: @@ -891,81 +974,97 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file // *) Book pt and phi distribution with binning defined through configurables in the json file: - std::vector lPtBins = cfPtBins.value; // define local array and initialize it from an array set in the configurables + std::vector lPtBins = cfPtBins.value; int nBinsPt = static_cast(lPtBins[0]); float minPt = lPtBins[1]; float maxPt = lPtBins[2]; - std::vector lPhiBins = cfPhiBins.value; // define local array and initialize it from an array set in the configurables + std::vector lPhiBins = cfPhiBins.value; int nBinsPhi = static_cast(lPhiBins[0]); float minPhi = lPhiBins[1]; float maxPhi = lPhiBins[2]; - std::vector lEtaBins = cfEtaBins.value; // define local array and initialize it from an array set in the configurables + std::vector lEtaBins = cfEtaBins.value; int nBinsEta = static_cast(lEtaBins[0]); float minEta = lEtaBins[1]; float maxEta = lEtaBins[2]; + int nBinsCharge = 5; + float minCharge = -2.5; + float maxCharge = 2.5; + if (doprocessRec || doprocessRecSim) { - pc.fParticleHistograms[eHistPt][eRec][eNoCuts] = new TH1F("[eHistPt][eRec][eNoCuts]", "pt distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eRec][eNoCuts]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistograms[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); + pc.fParticleHistograms[eHistPt][eRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eRec][eNoCuts] = new TH1F("[eHistPhi][eRec][eNoCuts]", "phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eRec][eNoCuts]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHistograms[eHistPhi][eRec][eBefore] = new TH1F("[eHistPhi][eRec][eBefore]", "#phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHistograms[eHistPhi][eRec][eBefore]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eRec][eNoCuts] = new TH1F("[eHistEta][eRec][eNoCuts]", "eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eRec][eNoCuts]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHistograms[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); + pc.fParticleHistograms[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); + + pc.fParticleHistograms[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHistograms[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eRec][eNoCuts]->SetColors(noCutLineColor, -1, noCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eNoCuts]); + pc.fParticleHistograms[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eBefore]); } if (cfMasterCutSwitch) { - pc.fParticleHistograms[eHistPt][eRec][eWithCuts] = new TH1F("[eHistPt][eRec][eWithCuts]", "pt distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eRec][eWithCuts]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistograms[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); + pc.fParticleHistograms[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); + + pc.fParticleHistograms[eHistPhi][eRec][eAfter] = new TH1F("[eHistPhi][eRec][eAfter]", "#phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHistograms[eHistPhi][eRec][eAfter]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistPhi][eRec][eWithCuts] = new TH1F("[eHistPhi][eRec][eWithCuts]", "phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eRec][eWithCuts]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHistograms[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); + pc.fParticleHistograms[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistEta][eRec][eWithCuts] = new TH1F("[eHistEta][eRec][eWithCuts]", "eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eRec][eWithCuts]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHistograms[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHistograms[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eRec][eWithCuts]->SetColors(withCutLineColor, -1, withCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eWithCuts]); + pc.fParticleHistograms[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eAfter]); } } } if (doprocessSim || doprocessRecSim) { - pc.fParticleHistograms[eHistPt][eSim][eNoCuts] = new TH1F("[eHistPt][eSim][eNoCuts]", "pt distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eSim][eNoCuts]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistograms[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); + pc.fParticleHistograms[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eSim][eNoCuts] = new TH1F("[eHistPhi][eSim][eNoCuts]", "phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eSim][eNoCuts]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHistograms[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHistograms[eHistPhi][eSim][eBefore]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eSim][eNoCuts] = new TH1F("[eHistEta][eSim][eNoCuts]", "eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eSim][eNoCuts]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHistograms[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); + pc.fParticleHistograms[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); + + pc.fParticleHistograms[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHistograms[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eSim][eNoCuts]->SetColors(noCutLineColor, -1, noCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eNoCuts]); + pc.fParticleHistograms[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eBefore]); } if (cfMasterCutSwitch) { - pc.fParticleHistograms[eHistPt][eSim][eWithCuts] = new TH1F("[eHistPt][eSim][eWithCuts]", "pt distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eSim][eWithCuts]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistograms[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); + pc.fParticleHistograms[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); + + pc.fParticleHistograms[eHistPhi][eSim][eAfter] = new TH1F("[eHistPhi][eSim][eAfter]", "#phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHistograms[eHistPhi][eSim][eAfter]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistPhi][eSim][eWithCuts] = new TH1F("[eHistPhi][eSim][eWithCuts]", "phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eSim][eWithCuts]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHistograms[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); + pc.fParticleHistograms[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistEta][eSim][eWithCuts] = new TH1F("[eHistEta][eSim][eWithCuts]", "eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eSim][eWithCuts]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHistograms[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHistograms[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eSim][eWithCuts]->SetColors(withCutLineColor, -1, withCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eWithCuts]); + pc.fParticleHistograms[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eAfter]); } } } @@ -976,26 +1075,24 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHistogramsList->SetOwner(true); fBaseList->Add(ec.fEventHistogramsList); + float defaultBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; + const int nDefaultBins = sizeof(defaultBoundaries) / sizeof(defaultBoundaries[0]) - 1; + std::vector lCent = cfCentBins.value; int nBinsCent = static_cast(lCent[0]); float minCent = lCent[1]; float maxCent = lCent[2]; - std::vector lMultRec = cfMultBinsRec.value; - int nBinsMultRec = static_cast(lMultRec[0]); - float minMultRec = lMultRec[1]; - float maxMultRec = lMultRec[2]; + std::vector lMult = cfMultBins.value; + int nBinsMult = static_cast(lMult[0]); + float minMult = lMult[1]; + float maxMult = lMult[2]; std::vector lMultRef = cfMultBinsRef.value; int nBinsMultRef = static_cast(lMultRef[0]); float minMultRef = lMultRef[1]; float maxMultRef = lMultRef[2]; - std::vector lMultSim = cfMultBinsSim.value; - int nBinsMultSim = static_cast(lMultSim[0]); - float minMultSim = lMultSim[1]; - float maxMultSim = lMultSim[2]; - std::vector lVx = cfVxBins.value; int nBinsVx = static_cast(lVx[0]); float minVx = lVx[1]; @@ -1019,108 +1116,124 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // eEventHistograms_N if (doprocessRec || doprocessRecSim) { - ec.fEventHistograms[eHistCentrality][eRec][eNoCuts] = new TH1F("[eHistCentrality][eRec][eNoCuts]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); - ec.fEventHistograms[eHistCentrality][eRec][eNoCuts]->GetXaxis()->SetTitle("Centrality"); + if (cfALICECentBinSwitch) { + ec.fEventHistograms[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultBins, defaultBoundaries); + } else { + ec.fEventHistograms[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); + } + ec.fEventHistograms[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eRec][eNoCuts] = new TH1F("[eHistMultiplicity][eRec][eNoCuts]", "Multiplicity (reconstructed) before cuts", nBinsMultRec, minMultRec, maxMultRec); - ec.fEventHistograms[eHistMultiplicity][eRec][eNoCuts]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHistograms[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); + ec.fEventHistograms[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eNoCuts] = new TH1F("[eHistReferenceMultiplicity][eRec][eNoCuts]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eNoCuts]->GetXaxis()->SetTitle("Reference Multiplicity"); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - ec.fEventHistograms[eHistVertexX][eRec][eNoCuts] = new TH1F("[eHistVertexX][eRec][eNoCuts]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eRec][eNoCuts]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHistograms[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); + ec.fEventHistograms[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eRec][eNoCuts] = new TH1F("[eHistVertexY][eRec][eNoCuts]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eRec][eNoCuts]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHistograms[eHistVertexY][eRec][eBefore] = new TH1F("[eHistVertexY][eRec][eBefore]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); + ec.fEventHistograms[eHistVertexY][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eRec][eNoCuts] = new TH1F("[eHistVertexZ][eRec][eNoCuts]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eRec][eNoCuts]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHistograms[eHistVertexZ][eRec][eBefore] = new TH1F("[eHistVertexZ][eRec][eBefore]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); + ec.fEventHistograms[eHistVertexZ][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Z"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistImpactParameter) { - ec.fEventHistograms[i][eRec][eNoCuts]->SetColors(noCutLineColor, -1, noCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eNoCuts]); + ec.fEventHistograms[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eBefore]); } } if (cfMasterCutSwitch) { - ec.fEventHistograms[eHistCentrality][eRec][eWithCuts] = new TH1F("[eHistCentrality][eRec][eWithCuts]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); - ec.fEventHistograms[eHistCentrality][eRec][eWithCuts]->GetXaxis()->SetTitle("Centrality"); + if (cfALICECentBinSwitch) { + ec.fEventHistograms[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultBins, defaultBoundaries); + } else { + ec.fEventHistograms[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); + } + ec.fEventHistograms[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eRec][eWithCuts] = new TH1F("[eHistMultiplicity][eRec][eWithCuts]", "Multiplicity (reconstructed) after cuts", nBinsMultRec, minMultRec, maxMultRec); - ec.fEventHistograms[eHistMultiplicity][eRec][eWithCuts]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHistograms[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); + ec.fEventHistograms[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eWithCuts] = new TH1F("[eHistReferenceMultiplicity][eRec][eWithCuts]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eWithCuts]->GetXaxis()->SetTitle("Reference Multiplicity"); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); + ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - ec.fEventHistograms[eHistVertexX][eRec][eWithCuts] = new TH1F("[eHistVertexX][eRec][eWithCuts]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eRec][eWithCuts]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHistograms[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); + ec.fEventHistograms[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eRec][eWithCuts] = new TH1F("[eHistVertexY][eRec][eWithCuts]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eRec][eWithCuts]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHistograms[eHistVertexY][eRec][eAfter] = new TH1F("[eHistVertexY][eRec][eAfter]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); + ec.fEventHistograms[eHistVertexY][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eRec][eWithCuts] = new TH1F("[eHistVertexZ][eRec][eWithCuts]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eRec][eWithCuts]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHistograms[eHistVertexZ][eRec][eAfter] = new TH1F("[eHistVertexZ][eRec][eAfter]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); + ec.fEventHistograms[eHistVertexZ][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Z"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistImpactParameter) { - ec.fEventHistograms[i][eRec][eWithCuts]->SetColors(withCutLineColor, -1, withCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eWithCuts]); + ec.fEventHistograms[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eAfter]); } } } } if (doprocessSim || doprocessRecSim) { - ec.fEventHistograms[eHistCentrality][eSim][eNoCuts] = new TH1F("[eHistCentrality][eSim][eNoCuts]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); - ec.fEventHistograms[eHistCentrality][eSim][eNoCuts]->GetXaxis()->SetTitle("Centrality"); + if (cfALICECentBinSwitch) { + ec.fEventHistograms[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultBins, defaultBoundaries); + } else { + ec.fEventHistograms[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); + } + ec.fEventHistograms[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eSim][eNoCuts] = new TH1F("[eHistMultiplicity][eSim][eNoCuts]", "Multiplicity (simulated) before cuts", nBinsMultSim, minMultSim, maxMultSim); - ec.fEventHistograms[eHistMultiplicity][eSim][eNoCuts]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHistograms[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); + ec.fEventHistograms[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistVertexX][eSim][eNoCuts] = new TH1F("[eHistVertexX][eSim][eNoCuts]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eSim][eNoCuts]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHistograms[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); + ec.fEventHistograms[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eSim][eNoCuts] = new TH1F("[eHistVertexY][eSim][eNoCuts]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eSim][eNoCuts]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHistograms[eHistVertexY][eSim][eBefore] = new TH1F("[eHistVertexY][eSim][eBefore]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); + ec.fEventHistograms[eHistVertexY][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eSim][eNoCuts] = new TH1F("[eHistVertexZ][eSim][eNoCuts]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eSim][eNoCuts]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHistograms[eHistVertexZ][eSim][eBefore] = new TH1F("[eHistVertexZ][eSim][eBefore]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); + ec.fEventHistograms[eHistVertexZ][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - ec.fEventHistograms[eHistImpactParameter][eSim][eNoCuts] = new TH1F("[eHistImpactParameter][eSim][eNoCuts]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); - ec.fEventHistograms[eHistImpactParameter][eSim][eNoCuts]->GetXaxis()->SetTitle("Impact Parameter"); + ec.fEventHistograms[eHistImpactParameter][eSim][eBefore] = new TH1F("[eHistImpactParameter][eSim][eBefore]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); + ec.fEventHistograms[eHistImpactParameter][eSim][eBefore]->GetXaxis()->SetTitle("Impact Parameter"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistReferenceMultiplicity) { - ec.fEventHistograms[i][eSim][eNoCuts]->SetColors(noCutLineColor, -1, noCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eNoCuts]); + ec.fEventHistograms[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eBefore]); } } if (cfMasterCutSwitch) { - ec.fEventHistograms[eHistCentrality][eSim][eWithCuts] = new TH1F("[eHistCentrality][eSim][eWithCuts]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); - ec.fEventHistograms[eHistCentrality][eSim][eWithCuts]->GetXaxis()->SetTitle("Centrality"); + if (cfALICECentBinSwitch) { + ec.fEventHistograms[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultBins, defaultBoundaries); + } else { + ec.fEventHistograms[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); + } + ec.fEventHistograms[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eSim][eWithCuts] = new TH1F("[eHistMultiplicity][eSim][eWithCuts]", "Multiplicity (simulated) after cuts", nBinsMultSim, minMultSim, maxMultSim); - ec.fEventHistograms[eHistMultiplicity][eSim][eWithCuts]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHistograms[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); + ec.fEventHistograms[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistVertexX][eSim][eWithCuts] = new TH1F("[eHistVertexX][eSim][eWithCuts]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eSim][eWithCuts]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHistograms[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); + ec.fEventHistograms[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eSim][eWithCuts] = new TH1F("[eHistVertexY][eSim][eWithCuts]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eSim][eWithCuts]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHistograms[eHistVertexY][eSim][eAfter] = new TH1F("[eHistVertexY][eSim][eAfter]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); + ec.fEventHistograms[eHistVertexY][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eSim][eWithCuts] = new TH1F("[eHistVertexZ][eSim][eWithCuts]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eSim][eWithCuts]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHistograms[eHistVertexZ][eSim][eAfter] = new TH1F("[eHistVertexZ][eSim][eAfter]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); + ec.fEventHistograms[eHistVertexZ][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - ec.fEventHistograms[eHistImpactParameter][eSim][eWithCuts] = new TH1F("[eHistImpactParameter][eSim][eWithCuts]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); - ec.fEventHistograms[eHistImpactParameter][eSim][eWithCuts]->GetXaxis()->SetTitle("Impact Parameter"); + ec.fEventHistograms[eHistImpactParameter][eSim][eAfter] = new TH1F("[eHistImpactParameter][eSim][eAfter]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); + ec.fEventHistograms[eHistImpactParameter][eSim][eAfter]->GetXaxis()->SetTitle("Impact Parameter"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistReferenceMultiplicity) { - ec.fEventHistograms[i][eSim][eWithCuts]->SetColors(withCutLineColor, -1, withCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eWithCuts]); + ec.fEventHistograms[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eAfter]); } } } @@ -1133,14 +1246,52 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(obs.fObservablesList); if (doprocessRec || doprocessRecSim) { - obs.fProfTwo[eRec][eNoCuts] = new TProfile("obs.fProfTwo[eRec][eNoCuts]", "obs.fProfTwo[eRec][eNoCuts]", 1, 0., 1); - obs.fProfTwo[eRec][eNoCuts]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eNoCuts]); + obs.fProfTwo[eRec][eBefore] = new TProfile("obs.fProfTwo[eRec][eBefore]", "obs.fProfTwo[eRec][eBefore]", 1, 0., 1); + obs.fProfTwo[eRec][eBefore]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); + obs.fObservablesList->Add(obs.fProfTwo[eRec][eBefore]); + + if (cfMasterCutSwitch) { + obs.fProfTwo[eRec][eAfter] = new TProfile("obs.fProfTwo[eRec][eAfter]", "obs.fProfTwo[eRec][eAfter]", 1, 0., 1); + obs.fProfTwo[eRec][eAfter]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); + obs.fObservablesList->Add(obs.fProfTwo[eRec][eAfter]); + } + } + + if (doprocessRecSim) { + // *) Book PhiAndPtHistograms TLists: + pph.fPhiAndPtHistogramsList = new TList(); + pph.fPhiAndPtHistogramsList->SetName("PhiAndPtHistograms"); + pph.fPhiAndPtHistogramsList->SetOwner(true); + fBaseList->Add(pph.fPhiAndPtHistogramsList); + + pph.fPhiAndPtHistograms[ePhiRec][eBefore] = new TH1F("[ePhiRec][eBefore]", "phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); + pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("#varphi"); + + pph.fPhiAndPtHistograms[ePtRec][eBefore] = new TH1F("[ePtRec][eBefore]", "pt distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); + pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); + + pph.fPhiAndPtHistograms[ePtMC][eBefore] = new TH1F("[ePtMC][eBefore]", "pt distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); + pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); + + for (int i = 0; i < ePhiAndPtHistograms_N; ++i) { + pph.fPhiAndPtHistograms[i][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + pph.fPhiAndPtHistogramsList->Add(pph.fPhiAndPtHistograms[i][eBefore]); + } if (cfMasterCutSwitch) { - obs.fProfTwo[eRec][eWithCuts] = new TProfile("obs.fProfTwo[eRec][eWithCuts]", "obs.fProfTwo[eRec][eWithCuts]", 1, 0., 1); - obs.fProfTwo[eRec][eWithCuts]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eWithCuts]); + pph.fPhiAndPtHistograms[ePhiRec][eAfter] = new TH1F("[ePhiRec][eAfter]", "phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); + pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("#varphi"); + + pph.fPhiAndPtHistograms[ePtRec][eAfter] = new TH1F("[ePtRec][eAfter]", "pt distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); + pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); + + pph.fPhiAndPtHistograms[ePtMC][eAfter] = new TH1F("[ePtMC][eAfter]", "pt distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); + pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); + + for (int i = 0; i < ePhiAndPtHistograms_N; ++i) { + pph.fPhiAndPtHistograms[i][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + pph.fPhiAndPtHistogramsList->Add(pph.fPhiAndPtHistograms[i][eAfter]); + } } } From da6b53667b1189bbba156fea6a1d4139979ebe0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Jos=C3=A9?= <52935244+lucasjsilva@users.noreply.github.com> Date: Fri, 25 Sep 2026 13:05:37 -0300 Subject: [PATCH 070/648] [PWGLF] Include extra phi cut condition to INEL > 0 track validation (#18029) --- PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx b/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx index 504298e995c..eb51bf09b1b 100644 --- a/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx @@ -253,6 +253,9 @@ struct StudyPnch { if (std::abs(track.eta()) >= 1.0f) { return false; } + if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } return true; } From 2e545a359eba756fc046374bcb100caf8a18e799 Mon Sep 17 00:00:00 2001 From: Akash-Raj-285 <157659199+Akash-Raj-285@users.noreply.github.com> Date: Fri, 25 Sep 2026 12:36:20 -0400 Subject: [PATCH 071/648] [PWGCF] Lambda spin analysis (#18035) --- .../lambdaOrAntiLambdaProducerWithSpin.cxx | 232 +++++++++++++++--- 1 file changed, 204 insertions(+), 28 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx index a228a55a528..fb9decce085 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx @@ -480,9 +480,9 @@ struct LambdaOrAntiLambdaProducerWithSpin { using TracksWithPID = soa::Join; using CollisionsWithActivity = soa::Join; - void process(aod::V0Datas const& v0s, - TracksWithPID const&, - CollisionsWithActivity const&) + void processData(aod::V0Datas const& v0s, + TracksWithPID const&, + CollisionsWithActivity const&) { for (auto const& v0 : v0s) { @@ -621,6 +621,10 @@ struct LambdaOrAntiLambdaProducerWithSpin { rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlphaVsQt"), v0.alpha(), v0.qtarm()); } } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaOrAntiLambdaProducerWithSpin, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaOrAntiLambdaProducerWithSpin, processDummy, "Skip candidate production", false); }; //*********************************************************************************************************** @@ -870,7 +874,7 @@ struct LambdaAntiLambdaSelector { Partition selectedAntiLambdas = aod::lambdaorantilambda::isAntiLambdaHypothesis == true; - void process(SelectedLambdaOrAntiLambdas const&) + void processData(SelectedLambdaOrAntiLambdas const&) { // ============================================================ // Fill the final Lambda table @@ -958,6 +962,10 @@ struct LambdaAntiLambdaSelector { candidate.qtArm()); } } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaSelector, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaAntiLambdaSelector, processDummy, "Skip candidate production", false); }; //*********************************************************************************************************** @@ -1162,6 +1170,9 @@ struct LambdaAntiLambdaMcRecoTableProducer { nSel8Collisions); } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processDummy, "Do nothing on real data", true); PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processMcReco, "Produce MC reconstructed table", false); PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processEveSelPassedMcGenTable, "Produce MC generated table", false); }; @@ -1335,6 +1346,10 @@ struct LambdaAntiLambdaEfficiencyPlots { } } } + + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaEfficiencyPlots, processDummy, "Do nothing on real data", true); PROCESS_SWITCH(LambdaAntiLambdaEfficiencyPlots, processEfficiencyMcGen, "Fill MC-generated efficiency histograms", false); }; @@ -1474,7 +1489,9 @@ struct LambdaAntiLambdaSelectionCutFlow { fillStage(7.f); } } + void processDummy(aod::Collisions const&) {} + PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processDummy, "Do nothing on real data", true); PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processEventCutFlow, "Fill reconstructed-event cut flow", false); PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processLambdaCutFlow, "Fill MCReco Lambda cut flow", false); }; @@ -1502,9 +1519,6 @@ struct LambdaAntiLambdaPairAnalysis { ConfigurableAxis axisDeltaCent{"axisDeltaCent", {200, -100.f, 100.f}, "Delta Centrality axis"}; // Mixed-Event Compatablity variables - Configurable compatibilityDeltaPt{"compatibilityDeltaPt", 0.1f, "compatibility pT difference for mixed-candidate matching"}; - Configurable compatibilityDeltaPhi{"compatibilityDeltaPhi", 0.1f, "compatibility phi difference for mixed-candidate matching"}; - Configurable compatibilityDeltaRapidity{"compatibilityDeltaRapidity", 0.1f, "compatibility rapidity difference for mixed-candidate matching"}; Configurable mixedEventMaxDeltaMultiplicity{"mixedEventMaxDeltaMultiplicity", 10.f, "Maximum multiplicity difference between mixed events"}; // Short-range pairs @@ -1586,7 +1600,17 @@ struct LambdaAntiLambdaPairAnalysis { "pairs;" "MixedEvent", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/EventCount", "Mixed-event short-range " + candidate1 + candidate2 + + "pairs;" + "MixedEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/EventCount", "Mixed-event long-range " + candidate1 + candidate2 + + "pairs;" + "MixedEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/NumberOfPairs", "Mixed-event pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/NumberOfPairs", "Mixed-event short-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/NumberOfPairs", "Mixed-event long-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); // Delta Kinaematics histos rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaEtaDelatPhi", "Mixed-event" + candidate1 + candidate2 + @@ -1637,6 +1661,24 @@ struct LambdaAntiLambdaPairAnalysis { "Mult" + candidate2, HistType::kTH3F, {axisCos, axisMult, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/CosMult", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Mult" + + candidate1 + + ";" + "Mult" + + candidate2, + HistType::kTH3F, {axisCos, axisMult, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/CosMult", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Mult" + + candidate1 + + ";" + "Mult" + + candidate2, + HistType::kTH3F, {axisCos, axisMult, axisMult}); rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/CosCent", "Mixed-event" + candidate1 + candidate2 + "pairs;" "cos#theta;" @@ -1646,11 +1688,37 @@ struct LambdaAntiLambdaPairAnalysis { "Cent" + candidate2, HistType::kTH3F, {axisCos, axisCent, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/CosCent", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Cent" + + candidate1 + + ";" + "Cent" + + candidate2, + HistType::kTH3F, {axisCos, axisCent, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/CosCent", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Cent" + + candidate1 + + ";" + "Cent" + + candidate2, + HistType::kTH3F, {axisCos, axisCent, axisCent}); // 1d rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/Cos", "Mixed-event" + candidate1 + candidate2 + "pairs;" "cos#theta", HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/Cos", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta", + HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/Cos", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta", + HistType::kTH1F, {axisCos}); } void init(InitContext const&) @@ -1685,15 +1753,13 @@ struct LambdaAntiLambdaPairAnalysis { } template - bool isKinematicallyCompatible(Candidate1 const& candidate1, Candidate2 const& candidate2) + bool isSameEventLongRange(Candidate1 const& candidate1, Candidate2 const& candidate2) { - const float deltaPt = std::abs(candidate1.pt() - candidate2.pt()); const float deltaPhi = std::abs(std::remainder(candidate1.phi() - candidate2.phi(), o2::constants::math::TwoPI)); const float deltaRapidity = std::abs(candidate1.rapidity() - candidate2.rapidity()); - return deltaPt < compatibilityDeltaPt && - deltaPhi < compatibilityDeltaPhi && - deltaRapidity < compatibilityDeltaRapidity; + return deltaPhi > sameEventShortRangePairMaxDeltaPhi && + deltaRapidity > sameEventShortRangePairMaxDeltaRapidity; } template @@ -1742,6 +1808,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/Mass"), lambda.mass(), antiLambda.mass()); const bool isShortRange = isSameEventShortRange(lambda, antiLambda); + const bool isLongRange = isSameEventLongRange(lambda, antiLambda); if (isShortRange) { hasShortRangePair = true; @@ -1750,7 +1817,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity()); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality()); - } else { + } else if (isLongRange) { hasLongRangePair = true; rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/NumberOfPairs"), 0); @@ -1804,6 +1871,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/Mass"), lambda1.mass(), lambda2.mass()); const bool isShortRange = isSameEventShortRange(lambda1, lambda2); + const bool isLongRange = isSameEventLongRange(lambda1, lambda2); if (isShortRange) { hasShortRangePair = true; @@ -1812,7 +1880,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity()); rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda1.centrality()); - } else { + } else if (isLongRange) { hasLongRangePair = true; rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/NumberOfPairs"), 0); @@ -1865,6 +1933,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/Mass"), antilambda1.mass(), antilambda2.mass()); const bool isShortRange = isSameEventShortRange(antilambda1, antilambda2); + const bool isLongRange = isSameEventLongRange(antilambda1, antilambda2); if (isShortRange) { hasShortRangePair = true; @@ -1873,7 +1942,7 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, antilambda1.multiplicity()); rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, antilambda1.centrality()); - } else { + } else if (isLongRange) { hasLongRangePair = true; rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/NumberOfPairs"), 0); @@ -1893,13 +1962,18 @@ struct LambdaAntiLambdaPairAnalysis { template void fillLambdaAntiLambdaMixedEvent(LambdaSlice1 const& lambdas, AntiLambdaSlice2 const& antiLambdas) { + + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + bool hasAcceptedPair = false; + // Lambda-AntiLambda pairs for (auto const& [lambda, antiLambda] : combinations(CombinationsFullIndexPolicy(lambdas, antiLambdas))) { + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda, antiLambda); + const bool isLongRangeMixed = isSameEventLongRange(lambda, antiLambda); - if (!isKinematicallyCompatible(lambda, antiLambda)) { - continue; - } // Boost the proton into the Lambda rest frame. const auto protonStar = daughterInParentRestFrame(lambda.px(), lambda.py(), lambda.pz(), lambda.mass(), lambda.protonPx(), lambda.protonPy(), lambda.protonPz(), o2::constants::physics::MassProton); // Boost the antiproton into the anti-Lambda rest frame. @@ -1928,23 +2002,49 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } } if (hasAcceptedPair) { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/EventCount"), 0); } + + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } } template void fillAntiLambdaLambdaMixedEvent(AntiLambdaSlice1 const& antiLambdas, LambdaSlice2 const& lambdas) { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + bool hasAcceptedPair = false; // Lambda-AntiLambda pairs for (auto const& [lambda, antiLambda] : combinations(CombinationsFullIndexPolicy(lambdas, antiLambdas))) { - if (!isKinematicallyCompatible(lambda, antiLambda)) { - continue; - } + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda, antiLambda); + const bool isLongRangeMixed = isSameEventLongRange(lambda, antiLambda); + // Boost the proton into the Lambda rest frame. const auto protonStar = daughterInParentRestFrame(lambda.px(), lambda.py(), lambda.pz(), lambda.mass(), lambda.protonPx(), lambda.protonPy(), lambda.protonPz(), o2::constants::physics::MassProton); // Boost the antiproton into the anti-Lambda rest frame. @@ -1973,23 +2073,49 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } } if (hasAcceptedPair) { rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/EventCount"), 0); } + + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } } template void fillLambdaLambdaMixedEvent(LambdaSlice1 const& lambdas1, LambdaSlice2 const& lambdas2) { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + bool hasAcceptedPair = false; // Lambda-AntiLambda pairs for (auto const& [lambda1, lambda2] : combinations(CombinationsFullIndexPolicy(lambdas1, lambdas2))) { - if (!isKinematicallyCompatible(lambda1, lambda2)) { - continue; - } + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda1, lambda2); + const bool isLongRangeMixed = isSameEventLongRange(lambda1, lambda2); + // Boost the proton into the Lambda rest frame. const auto protonStar = daughterInParentRestFrame(lambda1.px(), lambda1.py(), lambda1.pz(), lambda1.mass(), lambda1.protonPx(), lambda1.protonPy(), lambda1.protonPz(), o2::constants::physics::MassProton); // Boost the antiproton into the anti-Lambda rest frame. @@ -2018,23 +2144,48 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); + } } if (hasAcceptedPair) { rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/EventCount"), 0); } + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/EventCount"), 0); + } } template void fillAntiLambdaAntiLambdaMixedEvent(AntiLambdaSlice1 const& antiLambdas1, AntiLambdaSlice2 const& antiLambdas2) { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + bool hasAcceptedPair = false; // Lambda-AntiLambda pairs for (auto const& [antiLambda1, antiLambda2] : combinations(CombinationsFullIndexPolicy(antiLambdas1, antiLambdas2))) { - if (!isKinematicallyCompatible(antiLambda1, antiLambda2)) { - continue; - } + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(antiLambda1, antiLambda2); + const bool isLongRangeMixed = isSameEventLongRange(antiLambda1, antiLambda2); + // Boost the proton into the Lambda rest frame. const auto protonStar = daughterInParentRestFrame(antiLambda1.px(), antiLambda1.py(), antiLambda1.pz(), antiLambda1.mass(), antiLambda1.protonPx(), antiLambda1.protonPy(), antiLambda1.protonPz(), o2::constants::physics::MassProton); // Boost the antiproton into the anti-Lambda rest frame. @@ -2063,11 +2214,32 @@ struct LambdaAntiLambdaPairAnalysis { rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); + } } if (hasAcceptedPair) { rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/EventCount"), 0); } + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } } // ================================================================ // Mass Filter @@ -2086,7 +2258,7 @@ struct LambdaAntiLambdaPairAnalysis { Preslice lambdasPerCollision = aod::lambdahyperon::collisionId; Preslice antiLambdasPerCollision = aod::lambdahyperon::collisionId; - void process(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + void processData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) { static constexpr int MinimumSameSpeciesCandidates = 2; @@ -2146,6 +2318,10 @@ struct LambdaAntiLambdaPairAnalysis { } } } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processDummy, "Skip candidate production", false); }; /*WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 2918b80663ae6d2ba895f94544086089ecde6a4b Mon Sep 17 00:00:00 2001 From: sofiatomassini <122356048+sofiatomassini@users.noreply.github.com> Date: Fri, 25 Sep 2026 20:37:05 +0200 Subject: [PATCH 072/648] [PWGCF] Adding task for multiplicity camculation in femto analysis (#18057) Co-authored-by: Sofia Tomassini --- PWGCF/Femto3D/DataModel/PIDutils.h | 66 ++++- PWGCF/Femto3D/Tasks/CMakeLists.txt | 5 + PWGCF/Femto3D/Tasks/multEstimator.cxx | 331 ++++++++++++++++++++++++++ 3 files changed, 395 insertions(+), 7 deletions(-) create mode 100644 PWGCF/Femto3D/Tasks/multEstimator.cxx diff --git a/PWGCF/Femto3D/DataModel/PIDutils.h b/PWGCF/Femto3D/DataModel/PIDutils.h index 8baed073633..f35737dfc6d 100644 --- a/PWGCF/Femto3D/DataModel/PIDutils.h +++ b/PWGCF/Femto3D/DataModel/PIDutils.h @@ -33,7 +33,7 @@ namespace o2::aod::singletrackselector namespace pidutils { -//========================================== SFINAE checks ========================================== +//========================================== SFINAE checks for TPC ========================================== template struct hasTPCPi : std::false_type { @@ -77,6 +77,8 @@ template struct hasTPCHe>> : std::true_type { }; +//========================================== SFINAE checks for TOF ========================================== + template struct hasTOFPi : std::false_type { }; @@ -119,6 +121,50 @@ template struct hasTOFHe>> : std::true_type { }; +//========================================== SFINAE checks for ITS ========================================== + +template +struct hasITSPi : std::false_type { +}; +template +struct hasITSPi().itsNSigmaPi())>> : std::true_type { +}; + +template +struct hasITSKa : std::false_type { +}; +template +struct hasITSKa().itsNSigmaKa())>> : std::true_type { +}; + +template +struct hasITSPr : std::false_type { +}; +template +struct hasITSPr().itsNSigmaPr())>> : std::true_type { +}; + +template +struct hasITSDe : std::false_type { +}; +template +struct hasITSDe().itsNSigmaDe())>> : std::true_type { +}; + +template +struct hasITSTr : std::false_type { +}; +template +struct hasITSTr().itsNSigmaTr())>> : std::true_type { +}; + +template +struct hasITSHe : std::false_type { +}; +template +struct hasITSHe().itsNSigmaHe())>> : std::true_type { +}; + } // namespace pidutils //========================================== ITS PID ========================================== @@ -128,17 +174,23 @@ inline float getITSNsigma(TrackType const& track, int const& PDG) { switch (PDG) { case 211: - return track.itsNSigmaPi(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSPi::value) + return track.itsNSigmaPi(); case 321: - return track.itsNSigmaKa(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSKa::value) + return track.itsNSigmaKa(); case 2212: - return track.itsNSigmaPr(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSPr::value) + return track.itsNSigmaPr(); case 1000010020: - return track.itsNSigmaDe(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSDe::value) + return track.itsNSigmaDe(); case 1000020030: - return track.itsNSigmaHe(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSHe::value) + return track.itsNSigmaHe(); case 1000010030: - return track.itsNSigmaTr(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSTr::value) + return track.itsNSigmaTr(); case 0: return -1000.0; default: diff --git a/PWGCF/Femto3D/Tasks/CMakeLists.txt b/PWGCF/Femto3D/Tasks/CMakeLists.txt index 4090fea6795..b7b8ad7c31f 100644 --- a/PWGCF/Femto3D/Tasks/CMakeLists.txt +++ b/PWGCF/Femto3D/Tasks/CMakeLists.txt @@ -28,3 +28,8 @@ o2physics_add_dpl_workflow(femto3d-pid-optimization SOURCES PIDoptimization.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto3d-mult-estimator + SOURCES multEstimator.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto3D/Tasks/multEstimator.cxx b/PWGCF/Femto3D/Tasks/multEstimator.cxx new file mode 100644 index 00000000000..91d4e14e59e --- /dev/null +++ b/PWGCF/Femto3D/Tasks/multEstimator.cxx @@ -0,0 +1,331 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \brief create a table applying some basic cuts on the ITS and DCA. +/// \author Sofia Tomassini, Gleb Romanenko, Nicolò Jacazio +/// \since 31 May 2023 + +#include "PWGCF/Femto3D/DataModel/PIDutils.h" +#include "PWGCF/Femto3D/DataModel/singletrackselector.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::track; +using namespace o2::aod; +//::singletrackselector; // the namespace defined in .h + +struct multEstimator { + Service ccdb; + + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable CBThadronPID{"CBThadronPID", false, "Apply ev. sel. based on RCT flag `hadronPID`"}; // more in Common/CCDB/RCTSelectionFlags.h + Configurable centTableToUse{"centTableToUse", 1, "Flag to choose cent./mult.perc. estimator (Run3 only [FTOC for PbPb; FTOM for pp], for Run2 the V0M is used): 0 -> CentFV0As, 1 -> CentFT0Ms, 2 -> CentFT0As, 3 -> CentFT0Cs, 4 -> CentFDDMs, 5 -> CentNTPVs"}; + + Configurable applyEvSel{"applyEvSel", 2, "Flag to apply rapidity cut: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"}; + Configurable _eta{"eta", 0.5, "abs eta value limit"}; + Configurable _vertexZ{"VertexZ", 10.0, "abs vertexZ value limit"}; + + // the cuts below are used to count and for the conditional mult ... + + Configurable _maxTofChi2{"maxTofChi2", 10.f, "Maximum TOF Chi2 value -> to remove mismatched tracks"}; + + Configurable rejectNotPropagatedTrks{"rejectNotPropagatedTrks", true, "rejects tracks that are not propagated to the primary vertex"}; + Configurable _removeSameBunchPileup{"removeSameBunchPileup", false, ""}; + Configurable _requestGoodZvtxFT0vsPV{"requestGoodZvtxFT0vsPV", false, ""}; + Configurable _requestVertexITSTPC{"requestVertexITSTPC", false, ""}; + Configurable _requestVertexTOForTRDmatched{"requestVertexTOFmatched", 0, "0 -> no selectio; 1 -> vertex is matched to TOF or TRD; 2 -> matched to both;"}; + Configurable _requestNoCollInTimeRangeStandard{"requestNoCollInTimeRangeStandard", false, ""}; + Configurable _requestIsGoodITSLayersAll{"requestIsGoodITSLayersAll", false, "cut time intervals with dead ITS staves"}; + // Configurable fetchRate{"fetchRate", true, "Fetch the hadronic rate from the CCDB"}; + Configurable> _IRcut{"IRcut", std::pair{0.f, 100.f}, "[min., max.] IR range to keep events within"}; + Configurable> _OccupancyCut{"OccupancyCut", std::pair{0, 10000}, "[min., max.] occupancy range to keep events within"}; + + Configurable _sign{"sign", 1, "sign of a track"}; + Configurable _min_P{"min_P", 0.0, "lower mometum limit"}; + Configurable _max_P{"max_P", 100.0, "upper mometum limit"}; + Configurable> _dcaXY{"dcaXY", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaXY value limit; formula: [0] + [1]*pT^[2]"}; + Configurable> _dcaZ{"dcaZ", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaZ value limit; formula: [0] + [1]*pT^[2]"}; + Configurable _tpcNClsFound{"minTpcNClsFound", 0, "minimum allowed number of TPC clasters"}; + Configurable _tpcChi2NCl{"tpcChi2NCl", 100.0, "upper limit for chi2 value of a fit over TPC clasters"}; + Configurable _tpcCrossedRowsOverFindableCls{"tpcCrossedRowsOverFindableCls", 0, "lower limit of TPC CrossedRows/FindableCls value"}; + Configurable _tpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; + Configurable _itsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; + Configurable _itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; + + Configurable> _dcaXYCond{"dcaXYCond", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaXY value limit for conditioned multiplicity; formula: [0] + [1]*pT^[2]"}; + Configurable> _dcaZCond{"dcaZCond", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaZ value limit for conditioned multiplicity; formula: [0] + [1]*pT^[2]"}; + Configurable _tpcNClsFoundCond{"minTpcNClsFoundCond", 0, "minimum allowed number of TPC clasters for conditioned multiplicity"}; + Configurable _tpcChi2NClCond{"tpcChi2NClCond", 100.0, "upper limit for chi2 value of a fit over TPC clasters for conditioned multiplicity"}; + Configurable _tpcCrossedRowsOverFindableClsCond{"tpcCrossedRowsOverFindableClsCond", 0, "lower limit of TPC CrossedRows/FindableCls value for conditioned multiplicity"}; + Configurable _itsNClsCond{"minItsNClsCond", 0, "minimum allowed number of ITS clasters for a track for conditioned multiplicity"}; + Configurable _itsChi2NClCond{"itsChi2NClCond", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track for conditioned multiplicity"}; + + Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for (only pion, kaon, proton and deurton are supported now)"}; + Configurable> _tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; + Configurable> _itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; + Configurable _PIDtrshld{"PIDtrshld", 10.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; + Configurable> _tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; + Configurable> _tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; + + Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of perticles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; + Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + + Configurable _minNcandidates{"minNcandidates", 1, "minimum namber of candidates for the conditional multiplicity"}; + + std::pair> TPCcuts; + std::pair> TOFcuts; + + std::shared_ptr ITShisto; + std::shared_ptr TPChisto; + std::shared_ptr TOFhisto; + + Configurable> _centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep events within"}; + + using Trks = soa::Join; + + using CollRun3 = soa::Join; + + Filter eventFilter = (applyEvSel.node() == 0) || + ((applyEvSel.node() == 1) && (aod::evsel::sel7 == true)) || + ((applyEvSel.node() == 2) && (aod::evsel::sel8 == true)); + Filter vertexFilter = nabs(o2::aod::collision::posZ) < _vertexZ; + + Filter etaFilter = nabs(o2::aod::track::eta) < _eta; + + ctpRateFetcher mRateFetcher; // inspired by zdcSP.cxx in PWGLF + int mRunNumber = 0; + + int tot_counter = 0; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + SliceCache cache; + + rctsel::RCTFlagsChecker myChecker{"CBT_hadronPID"}; + + std::shared_ptr Nch_vs_cent_vs_eta; + std::shared_ptr Nch_vs_cent_vs_eta_conditional; + std::shared_ptr tmp_histo_per_event; + std::shared_ptr Events_vs_cent; + std::shared_ptr Events_vs_cent_conditional; + + void init(InitContext&) + { + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + myChecker.init("CBT_hadronPID", true); + + TPCcuts = std::make_pair(_particlePDG.value, _tpcNSigma.value); + TOFcuts = std::make_pair(_particlePDG.value, _tofNSigma.value); + + Nch_vs_cent_vs_eta = registry.add("Nch_vs_cent_vs_eta", "Nch_vs_cent_vs_eta", kTH2F, {{100, 0.0, 100.0, "cent"}, {200, -1.0, 1.0, "deta"}}); + Nch_vs_cent_vs_eta_conditional = registry.add("Nch_vs_cent_vs_eta_conditional", "Nch_vs_cent_vs_eta_conditional", kTH2F, {{100, 0.0, 100.0, "cent"}, {200, -1.0, 1.0, "deta"}}); + tmp_histo_per_event = std::make_shared(TH2F("tmp_histo_per_event", "tmp_histo_per_event", 100, 0.0, 100.0, 200, -1.0, 1.0)); + + Events_vs_cent = std::make_shared(TH1F("Events_vs_cent", "Events_vs_cent", 100, 0.0, 100.)); + Events_vs_cent_conditional = std::make_shared(TH1F("Events_vs_cent_conditional", "Events_vs_cent_conditional", 100, 0.0, 100.)); + + ITShisto = registry.add(Form("nsigmaITS_PDG%i", _particlePDG.value), Form("nsigmaITS_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + TPChisto = registry.add(Form("nsigmaTPC_PDG%i", _particlePDG.value), Form("nsigmaTPC_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + TOFhisto = registry.add(Form("nsigmaTOF_PDG%i", _particlePDG.value), Form("nsigmaTOF_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) // inspired by PWGLF/TableProducer/lambdakzerobuilder.cxx + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + } + + void process(soa::Filtered::iterator const& collision, + soa::Filtered const& tracks, + aod::BCsWithTimestamps const&) + + { + + const auto& bc = collision.bc_as(); + initCCDB(bc); + + if (!myChecker(*collision) && CBThadronPID) + return; + + if (_removeSameBunchPileup && !collision.selection_bit(evsel::kNoSameBunchPileup)) + return; + if (_requestGoodZvtxFT0vsPV && !collision.selection_bit(evsel::kIsGoodZvtxFT0vsPV)) + return; + if (std::fabs(collision.posZ()) > _vertexZ) + return; + + int occupancy = collision.trackOccupancyInTimeRange(); + + float centValue = -100.0f; + + switch (centTableToUse) { + case 0: + centValue = collision.centFV0A(); + break; + case 1: + centValue = collision.centFT0M(); + break; + case 2: + centValue = collision.centFT0A(); + break; + case 3: + centValue = collision.centFT0C(); + break; + case 4: + centValue = collision.centFDDM(); + break; + case 5: + centValue = collision.centNTPV(); + break; + default: + LOGF(fatal, "Invalid flag for cent./mult.perc. estimator has been choosen. Please check."); + break; + } + + if (centValue < _centCut.value.first || centValue >= _centCut.value.second) + return; + + Events_vs_cent->Fill(centValue); + // ============================ dNch/deta no cuts ================================ + + int cadidates_counter = 0; + + // auto tracksWithPid = soa::Attach, aod::pidits::ITSNSigmaPi, aod::pidits::ITSNSigmaKa, aod::pidits::ITSNSigmaPr, aod::pidits::ITSNSigmaDe, aod::pidits::ITSNSigmaTr, aod::pidits::ITSNSigmaHe>(tracks); + auto tracksWithPid = soa::Attach, aod::pidits::ITSNSigmaPr>(tracks); + + for (const auto& track : tracksWithPid) { + + // ============================ dNch/deta normal ================================ + + if (rejectNotPropagatedTrks && track.trackType() != aod::track::Track) { + continue; + } + + if ((track.tpcNClsFound()) < _tpcNClsFound || (track.itsNCls()) < _itsNCls) + continue; + if ((track.itsChi2NCl()) > _itsChi2NCl || (track.tpcChi2NCl()) > _tpcChi2NCl) + continue; + if ((track.tpcCrossedRowsOverFindableCls()) < _tpcCrossedRowsOverFindableCls) + continue; + if (std::fabs(track.dcaXY()) > _dcaXY.value[0] + _dcaXY.value[1] * std::pow(track.pt(), _dcaXY.value[2]) || std::fabs(track.dcaZ()) > _dcaZ.value[0] + _dcaZ.value[1] * std::pow(track.pt(), _dcaZ.value[2])) + continue; + + if (track.sign() != 0) { + tmp_histo_per_event->Fill(centValue, track.eta()); + tot_counter++; + } + + // ============================ dNch/deta conditioned (additional cuts) ================================ + + // analisiys track cuts + if (std::fabs(track.eta()) > _eta) // because soa::Attach operation breaks the filtering + continue; + if (track.sign() != _sign) + continue; + if (track.p() < _min_P || track.p() > _max_P) + continue; + if (_requestVertexITSTPC && !collision.selection_bit(evsel::kIsVertexITSTPC)) + continue; + if (_requestVertexTOForTRDmatched > static_cast(collision.selection_bit(evsel::kIsVertexTOFmatched)) + static_cast(collision.selection_bit(evsel::kIsVertexTRDmatched))) + continue; + if (_requestNoCollInTimeRangeStandard && !collision.selection_bit(evsel::kNoCollInTimeRangeStandard)) + continue; + if (_requestIsGoodITSLayersAll && !collision.selection_bit(evsel::kIsGoodITSLayersAll)) + continue; + + if (occupancy < _OccupancyCut.value.first || occupancy >= _OccupancyCut.value.second) + continue; + if (track.tofChi2() > _maxTofChi2) + continue; + if ((track.tpcNClsFound()) < _tpcNClsFoundCond || (track.itsNCls()) < _itsNClsCond) + continue; + if ((track.itsChi2NCl()) > _itsChi2NClCond || (track.tpcChi2NCl()) > _tpcChi2NClCond) + continue; + if ((track.tpcCrossedRowsOverFindableCls()) < _tpcCrossedRowsOverFindableClsCond) + continue; + if (std::fabs(track.dcaXY()) > _dcaXYCond.value[0] + _dcaXYCond.value[1] * std::pow(track.pt(), _dcaXYCond.value[2]) || std::fabs(track.dcaZ()) > _dcaZCond.value[0] + _dcaZCond.value[1] * std::pow(track.pt(), _dcaZCond.value[2])) + continue; + // ========================== PID cuts ========================== + + if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF))) + continue; + + if (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value)) { + ITShisto->Fill(track.p(), o2::aod::singletrackselector::getITSNsigma(track, _particlePDG)); + TPChisto->Fill(track.p(), o2::aod::singletrackselector::getTPCNsigma(track, _particlePDG)); + TOFhisto->Fill(track.p(), o2::aod::singletrackselector::getTOFNsigma(track, _particlePDG)); + cadidates_counter++; + } + } + Nch_vs_cent_vs_eta->Add(tmp_histo_per_event.get()); + if (Nch_vs_cent_vs_eta->GetEntries() != tot_counter) + LOGF(fatal, "tot counter != entries"); + if (cadidates_counter >= _minNcandidates) { + Nch_vs_cent_vs_eta_conditional->Add(tmp_histo_per_event.get()); + Events_vs_cent_conditional->Fill(centValue); + } + + tmp_histo_per_event->Reset(); + if (tmp_histo_per_event->GetEntries()) + LOGF(fatal, "not cleared"); + cadidates_counter = 0; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From e78e09053a0465c04305ced2b7c80ec6ea920c7d Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 25 Sep 2026 20:39:00 +0200 Subject: [PATCH 073/648] [PWGLF] Added necessary histograms for Lambda feeddown + substituted boilerplate code with macros (#18061) --- .../Strangeness/phiStrangeCorrelator.cxx | 32 +- .../Strangeness/phiStrangeCorrelation.cxx | 461 +++++++++--------- 2 files changed, 252 insertions(+), 241 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx index 907bfb71011..7d3fca0718b 100644 --- a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx +++ b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx @@ -468,6 +468,8 @@ struct V0ReducedCandProducer { } lambdaConfigs; } v0Configs; + Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; + // Constants static constexpr double MassK0S = o2::constants::physics::MassK0Short; static constexpr double MassLambda = o2::constants::physics::MassLambda0; @@ -500,12 +502,16 @@ struct V0ReducedCandProducer { AxisSpec binnedmultAxis{(std::vector)binsMult, "centFT0M"}; AxisSpec binnedpTK0SAxis{(std::vector)v0Configs.k0sConfigs.binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTLambdaAxis{(std::vector)v0Configs.lambdaConfigs.binspTLambda, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTXiAxis{(std::vector)binspTXi, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec massK0SAxis = {200, 0.45f, 0.55f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; AxisSpec massLambdaAxis = {200, 1.08f, 1.15f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; histos.add("h3K0sCandidatesMass", "K^{0}_{S} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTK0SAxis, massK0SAxis}); histos.add("h3LambdaCandidatesMass", "#Lambda candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); histos.add("h3AntiLambdaCandidatesMass", "#bar{#Lambda} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); + + histos.add("h2LambdaFromXi", "#Lambda from #Xi", kTH2F, {binnedpTLambdaAxis, binnedpTXiAxis}); + histos.add("h2AntiLambdaFromXi", "#bar{#Lambda} from #Xi", kTH2F, {binnedpTLambdaAxis, binnedpTXiAxis}); } template @@ -641,7 +647,31 @@ struct V0ReducedCandProducer { const auto& mcParticles = mcParticlesOpt->get(); const auto& v0McParticle = mcParticles.rawIteratorAt(v0.mcParticleId()); - if (v0McParticle.pdgCode() != getPdgCode() || !v0McParticle.isPhysicalPrimary()) { + // if (v0McParticle.pdgCode() != getPdgCode() || !v0McParticle.isPhysicalPrimary()) { + // return; + //} + if (v0McParticle.pdgCode() != getPdgCode()) { + return; + } + + if constexpr ((v0Type == kLambda || v0Type == kAntiLambda)) { + if (v0.has_mcMotherParticle()) { + auto v0McMotherParticle = mcParticles.rawIteratorAt(v0.mcMotherParticleId()); + if (std::abs(v0McMotherParticle.y()) <= v0Configs.yAcceptance) { + if constexpr (v0Type == kLambda) { + if (v0McMotherParticle.pdgCode() == PDG_t::kXiMinus || v0McMotherParticle.pdgCode() == o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2LambdaFromXi"), v0McParticle.pt(), v0McMotherParticle.pt()); + } + } else if constexpr (v0Type == kAntiLambda) { + if (v0McMotherParticle.pdgCode() == PDG_t::kXiPlusBar || v0McMotherParticle.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2AntiLambdaFromXi"), v0McParticle.pt(), v0McMotherParticle.pt()); + } + } + } + } + } + + if (!v0McParticle.isPhysicalPrimary()) { return; } diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index 3db46f77cd6..81b6ec46eb1 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -289,12 +289,12 @@ struct PhiStrangeCorrelation { // Configurables for minimum pt selection in McGen struct : ConfigurableGroup { - Configurable minPhiPt{"minPhiPt", 0.4f, "Minimum pT for Phi candidates"}; - Configurable v0SettingMinPt{"v0SettingMinPt", 0.1f, "V0 min pt"}; - Configurable lambdaSettingMinPt{"lambdaSettingMinPt", 0.2f, "Lambda min pt"}; - Configurable cascadeSettingMinPt{"cascadeSettingMinPt", 0.8f, "Cascade min pt"}; - Configurable omegaSettingMinPt{"omegaSettingMinPt", 0.8f, "Omega min pt"}; - Configurable cMinPionPtcut{"cMinPionPtcut", 0.2f, "Track minimum pt cut"}; + Configurable minPtPhi{"minPtPhi", 0.4f, "Minimum pT for Phi candidates"}; + Configurable minPtK0S{"minPtK0S", 0.1f, "Minimum pT for K0S candidates"}; + Configurable minPtLambda{"minPtLambda", 0.2f, "Minimum pT for Lambda candidates"}; + Configurable minPtXi{"minPtXi", 0.8f, "Minimum pT for Xi candidates"}; + Configurable minPtOmega{"minPtOmega", 0.8f, "Minimum pT for Omega candidates"}; + Configurable minPtPion{"minPtPion", 0.2f, "Minimum pT for Pion candidates"}; Configurable bypassPtCut{"bypassPtCut", false, "Bypass the minimum pt cut at MCGen level"}; } minPtMcGenConfigs; @@ -311,8 +311,22 @@ struct PhiStrangeCorrelation { SliceCache cache; struct : PresliceGroup { - Preslice phiCandDataPerCollision = aod::lf_selection_phi_candidate::collisionId; - // PresliceUnsorted phiCandMcRecoPerCollision = aod::lf_selection_phi_candidate::collisionId; +#define DEFINE_PRESLICE(PREF, TAB_PREF, COL_ID) \ + Preslice PREF##DataPerCollision = aod::COL_ID::collisionId; \ + Preslice PREF##McRecoPerCollision = aod::COL_ID::collisionId; + + // Trigger + DEFINE_PRESLICE(phiCand, Phimeson, lf_selection_phi_candidate) + + // Associated Strangeness + DEFINE_PRESLICE(k0s, K0sReduced, v0) + DEFINE_PRESLICE(lambda, LambdaReduced, v0) + DEFINE_PRESLICE(antiLambda, AntiLambdaReduced, v0) + DEFINE_PRESLICE(xi, XiReduced, cascade) + DEFINE_PRESLICE(omega, OmegaReduced, cascade) +#undef DEFINE_PRESLICE + + /*Preslice phiCandDataPerCollision = aod::lf_selection_phi_candidate::collisionId; Preslice phiCandMcRecoPerCollision = aod::lf_selection_phi_candidate::collisionId; Preslice k0sDataPerCollision = aod::v0::collisionId; @@ -328,7 +342,7 @@ struct PhiStrangeCorrelation { Preslice xiMcRecoPerCollision = aod::cascade::collisionId; Preslice omegaDataPerCollision = aod::cascade::collisionId; - Preslice omegaMcRecoPerCollision = aod::cascade::collisionId; + Preslice omegaMcRecoPerCollision = aod::cascade::collisionId;*/ Preslice pionTrackDataPerCollision = aod::track::collisionId; Preslice pionTrackMcRecoPerCollision = aod::track::collisionId; @@ -339,9 +353,6 @@ struct PhiStrangeCorrelation { // Necessary service to retrieve efficiency maps from CCDB Service ccdb{}; - // std::shared_ptr effMapPhi{}; - // std::array, kAssocPartSize> effMapsAssoc{}; - EffMapPtr effMapPhi; std::array effMapsAssoc{}; @@ -365,12 +376,13 @@ struct PhiStrangeCorrelation { struct MiniEvent { float multiplicity{}; std::vector phiParticles; - std::vector k0sParticles; + std::array, kAssocPartSize> assocParticles; + /*std::vector k0sParticles; std::vector lambdaParticles; std::vector antilambdaParticles; std::vector xiParticles; std::vector omegaParticles; - std::vector pionParticles; + std::vector pionParticles;*/ }; // Buffer for mixed event, organized as a vector of deques, one for each multiplicity bin, containing the past events with their particles of interest needed for mixing @@ -411,9 +423,57 @@ struct PhiStrangeCorrelation { AxisSpec binnedpTPiAxis{(std::vector)binspTPi, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTPiAxisExt{(std::vector)binspTPiExt, "#it{p}_{T} (GeV/#it{c})"}; + std::array massAxes = {massK0SAxis, massLambdaAxis, massLambdaAxis, massXiAxis, massOmegaAxis, nSigmaPiAxis}; + std::array binnedpTAxes = {binnedpTK0SAxis, binnedpTLambdaAxis, binnedpTLambdaAxis, binnedpTXiAxis, binnedpTOmegaAxis, binnedpTPiAxis}; + std::array binnedpTExtAxes = {binnedpTK0SAxisExt, binnedpTLambdaAxisExt, binnedpTLambdaAxisExt, binnedpTXiAxisExt, binnedpTOmegaAxisExt, binnedpTPiAxisExt}; + + std::array assocTitles = {"K0Short", "Lambda", "AntiLambda", "Xi", "Omega", "Pion"}; + std::array dirNames = {"k0s", "lambda", "antilambda", "xi", "omega", "pi"}; + histos.add("phi/h3PhiData", "Invariant mass of Phi in Data", kTH3F, {binnedmultAxis, binnedpTPhiAxis, massPhiAxis}); - histos.add("phiK0S/h6PhiK0SData", "Invariant mass of Phi vs Invariant mass of K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); + histos.add("phi/h4PhiMCReco", "Phi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); + histos.add("phi/h3PhiMCGen", "Phi in MC Gen", kTH3F, {binnedmultAxis, binnedpTPhiAxis, yAxis}); + histos.add("phi/h4PhiMCGenAssocReco", "Phi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); + + histos.add("phi/h3PhiMCClosureGen", "Phi in MC Gen for MC Closure Test", kTH3F, {binnedmultAxis, binnedpTPhiAxisExt, yAxis}); + + for (size_t i = 0; i < kAssocPartSize; ++i) { + std::string pName(AssocParticleLabels[i]); + std::string pTitle = assocTitles[i]; + std::string dir = dirNames[i]; + + if (i == kPion) { + histos.add("phiPi/h6PhiPiTPCData", "Invariant mass of Phi vs nSigmaTPC of Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC of Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTOFData", "Invariant mass of Phi vs nSigmaTOF of Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF of Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + } else { + histos.add(fmt::format("phi{}/h6Phi{}Data", pName, pName).c_str(), fmt::format("Invariant mass of Phi vs Invariant mass of {} in Data", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add(fmt::format("phi{}/h6Phi{}DataME", pName, pName).c_str(), fmt::format("Invariant mass of Phi vs Invariant mass of {} in Data ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + } + + for (const auto& label : PhiMassRegionLabels) { + histos.add(fmt::format("phi{}/h5Phi{}Data{}", pName, pName, label).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in Data", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phi{}/h5Phi{}DataME{}", pName, pName, label).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in Data ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, deltaphiAxis}); + } + + histos.add(fmt::format("{}/h4{}MCReco", dir, pName).c_str(), fmt::format("{} in MC Reco", pTitle).c_str(), kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTAxes[i], yAxis}); + histos.add(fmt::format("{}/h3{}MCGen", dir, pName).c_str(), fmt::format("{} in MC Gen", pTitle).c_str(), kTH3F, {binnedmultAxis, binnedpTAxes[i], yAxis}); + histos.add(fmt::format("{}/h4{}MCGenAssocReco", dir, pName).c_str(), fmt::format("{} in MC Gen Assoc Reco", pTitle).c_str(), kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTAxes[i], yAxis}); + + histos.add(fmt::format("phi{}/h5Phi{}ClosureMCGen", pName, pName).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in MCGen", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTExtAxes[i], deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phi{}/h5Phi{}ClosureMCGenME", pName, pName).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in MCGen ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTExtAxes[i], deltayAxis, deltaphiAxis}); + } + + histos.add("event/hRecoMCMultiplicityPercent", "RecoMC Multiplicity Percentile", kTH1F, {binnedmultAxis}); + histos.add("event/h2RecoMCVertexZvsMult", "RecoMC Vertex Z vs Multiplicity Percentile", kTH2F, {vertexZAxis, binnedmultAxis}); + histos.add("event/hSplitVertexZ", "Split in z-vtx", kTH1F, {{100, -5.0f, 5.0f}}); + histos.add("event/hGenMCMultiplicityPercent", "Generated MC Multiplicity Percentile", kTH1F, {binnedmultAxis}); + histos.add("event/hGenMCAssocRecoMultiplicityPercent", "Generated MC associated Multiplicity Percentile", kTH1F, {binnedmultAxis}); + histos.add("event/h2GenMCAssocRecoVertexZvsMult", "Generated MC associated reco Vertex Z vs multiplicity", kTH2F, {vertexZAxis, binnedmultAxis}); + + /*histos.add("phiK0S/h6PhiK0SData", "Invariant mass of Phi vs Invariant mass of K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); histos.add("phiLambda/h6PhiLambdaData", "Invariant mass of Phi vs Invariant mass of Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, massPhiAxis, massLambdaAxis}); histos.add("phiXi/h6PhiXiData", "Invariant mass of Phi vs Invariant mass of Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); histos.add("phiOmega/h6PhiOmegaData", "Invariant mass of Phi vs Invariant mass of Omega in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); @@ -425,9 +485,9 @@ struct PhiStrangeCorrelation { histos.add("phiXi/h6PhiXiDataME", "Invariant mass of Phi vs Invariant mass of Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); histos.add("phiOmega/h6PhiOmegaDataME", "Invariant mass of Phi vs Invariant mass of Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); - histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); + histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis});*/ - for (const auto& label : PhiMassRegionLabels) { + /*for (const auto& label : PhiMassRegionLabels) { histos.add(fmt::format("phiK0S/h5PhiK0SData{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiLambda/h5PhiLambdaData{}", label).c_str(), "Deltay vs deltaphi for Phi and Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiXi/h5PhiXiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); @@ -439,20 +499,9 @@ struct PhiStrangeCorrelation { histos.add(fmt::format("phiXi/h5PhiXiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiOmega/h5PhiOmegaDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, deltaphiAxis}); histos.add(fmt::format("phiPi/h5PhiPiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); - } + }*/ - histos.add("event/hRecoMCMultiplicityPercent", "RecoMC Multiplicity Percentile", kTH1F, {binnedmultAxis}); - histos.add("event/h2RecoMCVertexZvsMult", "RecoMC Vertex Z vs Multiplicity Percentile", kTH2F, {vertexZAxis, binnedmultAxis}); - histos.add("event/hSplitVertexZ", "Split in z-vtx", kTH1F, {{100, -5.0f, 5.0f}}); - histos.add("event/hGenMCMultiplicityPercent", "Generated MC Multiplicity Percentile", kTH1F, {binnedmultAxis}); - histos.add("event/hGenMCAssocRecoMultiplicityPercent", "Generated MC associated Multiplicity Percentile", kTH1F, {binnedmultAxis}); - histos.add("event/h2GenMCAssocRecoVertexZvsMult", "Generated MC associated reco Vertex Z vs multiplicity", kTH2F, {vertexZAxis, binnedmultAxis}); - - histos.add("phi/h4PhiMCReco", "Phi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); - histos.add("phi/h3PhiMCGen", "Phi in MC Gen", kTH3F, {binnedmultAxis, binnedpTPhiAxis, yAxis}); - histos.add("phi/h4PhiMCGenAssocReco", "Phi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); - - histos.add("k0s/h4K0SMCReco", "K0S in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); + /*histos.add("k0s/h4K0SMCReco", "K0S in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); histos.add("k0s/h3K0SMCGen", "K0S in MC Gen", kTH3F, {binnedmultAxis, binnedpTK0SAxis, yAxis}); histos.add("k0s/h4K0SMCGenAssocReco", "K0S in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); @@ -474,11 +523,9 @@ struct PhiStrangeCorrelation { histos.add("pi/h4PiMCReco", "Pion in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); histos.add("pi/h3PiMCGen", "Pion in MC Gen", kTH3F, {binnedmultAxis, binnedpTPiAxis, yAxis}); - histos.add("pi/h4PiMCGenAssocReco", "Pion in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); - - histos.add("phi/h3PhiMCClosureGen", "Phi in MC Gen for MC Closure Test", kTH3F, {binnedmultAxis, binnedpTPhiAxisExt, yAxis}); + histos.add("pi/h4PiMCGenAssocReco", "Pion in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis});*/ - histos.add("phiK0S/h5PhiK0SClosureMCGen", "Deltay vs deltaphi for Phi and K0Short in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); + /*histos.add("phiK0S/h5PhiK0SClosureMCGen", "Deltay vs deltaphi for Phi and K0Short in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiLambda/h5PhiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and Lambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and AntiLambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiXi/h5PhiXiClosureMCGen", "Deltay vs deltaphi for Phi and Xi in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); @@ -490,7 +537,7 @@ struct PhiStrangeCorrelation { histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGenME", "Deltay vs deltaphi for Phi and AntiLambda in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiXi/h5PhiXiClosureMCGenME", "Deltay vs deltaphi for Phi and Xi in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiOmega/h5PhiOmegaClosureMCGenME", "Deltay vs deltaphi for Phi and Omega in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTOmegaAxisExt, deltayAxis, deltaphiAxis}); - histos.add("phiPi/h5PhiPiClosureMCGenME", "Deltay vs deltaphi for Phi and Pion in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiPi/h5PhiPiClosureMCGenME", "Deltay vs deltaphi for Phi and Pion in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis});*/ // Load efficiency maps from CCDB if (efficiencyConfigs.applyEfficiency) { @@ -499,9 +546,6 @@ struct PhiStrangeCorrelation { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - /*for (int i = 0; i < ParticleOfInterestSize; ++i) { - loadEfficiencyMapFromCCDB(static_cast(i)); - }*/ if (!efficiencyConfigs.perPeriodEfficiency) { loadEfficiencyMaps(); } @@ -651,15 +695,15 @@ struct PhiStrangeCorrelation { float getMinGenPt() const { if constexpr (PartType == kK0S) { - return minPtMcGenConfigs.v0SettingMinPt.value; + return minPtMcGenConfigs.minPtK0S.value; } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { - return minPtMcGenConfigs.lambdaSettingMinPt.value; + return minPtMcGenConfigs.minPtLambda.value; } else if constexpr (PartType == kXi) { - return minPtMcGenConfigs.cascadeSettingMinPt.value; + return minPtMcGenConfigs.minPtXi.value; } else if constexpr (PartType == kOmega) { - return minPtMcGenConfigs.omegaSettingMinPt.value; + return minPtMcGenConfigs.minPtOmega.value; } else if constexpr (PartType == kPion) { - return minPtMcGenConfigs.cMinPionPtcut.value; + return minPtMcGenConfigs.minPtPion.value; } else { static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || PartType == kXi || PartType == kOmega || PartType == kPion, @@ -776,19 +820,54 @@ struct PhiStrangeCorrelation { { const std::array, kPhiMassRegions> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; + float deltaY = phiCand.y() - assoc.y(); + float deltaPhi = getDeltaPhi(phiCand.phi(), assoc.phi()); + if (analysisMode == kMassvsMass) { - if constexpr (PartType == kK0S) { + +#define FILL_MASS_CASE(PART_ENUM, PART_STR) \ + if constexpr (PartType == PART_ENUM) { \ + if constexpr (IsME) { \ + customFillTHn(HIST("phi" PART_STR "/h6Phi" PART_STR "DataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.m()); \ + } else { \ + customFillTHn(HIST("phi" PART_STR "/h6Phi" PART_STR "Data"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.m()); \ + } \ + } + + FILL_MASS_CASE(kK0S, "K0S"); + FILL_MASS_CASE(kLambda, "Lambda"); + FILL_MASS_CASE(kAntiLambda, "AntiLambda"); + FILL_MASS_CASE(kXi, "Xi"); + FILL_MASS_CASE(kOmega, "Omega"); + if constexpr (PartType == kPion) { + if constexpr (IsME) { + customFillTHn(HIST("phiPi/h6PhiPiTPCDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTOF()); + } else { + customFillTHn(HIST("phiPi/h6PhiPiTPCData"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFData"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTOF()); + } + } +#undef FILL_MASS_CASE + + /*if constexpr (PartType == kK0S) { if constexpr (IsME) { customFillTHn(HIST("phiK0S/h6PhiK0SDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } else { customFillTHn(HIST("phiK0S/h6PhiK0SData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } - } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + } else if constexpr (PartType == kLambda) { if constexpr (IsME) { customFillTHn(HIST("phiLambda/h6PhiLambdaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } else { customFillTHn(HIST("phiLambda/h6PhiLambdaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); } + } else if constexpr (PartType == kAntiLambda) { + if constexpr (IsME) { + customFillTHn(HIST("phiAntiLambda/h6PhiAntiLambdaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiAntiLambda/h6PhiAntiLambdaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } } else if constexpr (PartType == kXi) { if constexpr (IsME) { customFillTHn(HIST("phiXi/h6PhiXiDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); @@ -809,12 +888,31 @@ struct PhiStrangeCorrelation { customFillTHn(HIST("phiPi/h6PhiPiTPCData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTPC()); customFillTHn(HIST("phiPi/h6PhiPiTOFData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTOF()); } - } + }*/ } else if (analysisMode == kDeltaYvsDeltaPhi) { std::function fillSignal; std::function fillSideband; - if constexpr (PartType == kK0S) { +#define FILL_DYDP_CASE(PART_ENUM, PART_STR) \ + if constexpr (PartType == PART_ENUM) { \ + if constexpr (IsME) { \ + fillSignal = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + fillSideband = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + } else { \ + fillSignal = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + fillSideband = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + } \ + } + + FILL_DYDP_CASE(kK0S, "K0S"); + FILL_DYDP_CASE(kLambda, "Lambda"); + FILL_DYDP_CASE(kAntiLambda, "AntiLambda"); + FILL_DYDP_CASE(kXi, "Xi"); + FILL_DYDP_CASE(kOmega, "Omega"); + FILL_DYDP_CASE(kPion, "Pi"); +#undef FILL_DYDP_CASE + + /*if constexpr (PartType == kK0S) { if constexpr (IsME) { fillSignal = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; @@ -822,7 +920,7 @@ struct PhiStrangeCorrelation { fillSignal = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; } - } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + } else if constexpr (PartType == kLambda) { if constexpr (IsME) { fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; @@ -830,6 +928,14 @@ struct PhiStrangeCorrelation { fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; } + } else if constexpr (PartType == kAntiLambda) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } } else if constexpr (PartType == kXi) { if constexpr (IsME) { fillSignal = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; @@ -854,7 +960,7 @@ struct PhiStrangeCorrelation { fillSignal = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; fillSideband = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; } - } + }*/ static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { constexpr unsigned int Idx = i_idx.value; @@ -982,7 +1088,40 @@ struct PhiStrangeCorrelation { } } - void processPhiK0SMEDataLike(SelCollisions const& collisions, +#define DO_PROCESS_SWITCH(CLASS, FUNC_NAME, DESC) \ + PROCESS_SWITCH(CLASS, FUNC_NAME, DESC, true) + +#define DEFINE_ME_PROCESSES(PART_NAME, PART_ENUM, TABLE_DATA, TABLE_MC) \ + void processPhi##PART_NAME##MEDataLike(SelCollisions const& collisions, \ + aod::PhimesonCandidatesData const& phiCandidates, \ + TABLE_DATA const& assocReduced, \ + aod::BCs const&) \ + { \ + processPhiAssocME(collisions, phiCandidates, assocReduced); \ + } \ + \ + DO_PROCESS_SWITCH(PhiStrangeCorrelation, processPhi##PART_NAME##MEDataLike, "Phi-" #PART_NAME " ME Data or MC w/o PDG"); \ + \ + void processPhi##PART_NAME##MEMCWithPDG(SimCollisions const& collisions, \ + aod::PhimesonCandidatesMcReco const& phiCandidates, \ + TABLE_MC const& assocReduced, \ + aod::BCs const&) \ + { \ + processPhiAssocME(collisions, phiCandidates, assocReduced); \ + } \ + \ + DO_PROCESS_SWITCH(PhiStrangeCorrelation, processPhi##PART_NAME##MEMCWithPDG, "Phi-" #PART_NAME " ME MC w/ PDG"); + + DEFINE_ME_PROCESSES(K0S, kK0S, aod::K0sReducedCandidatesData, aod::K0sReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Lambda, kLambda, aod::LambdaReducedCandidatesData, aod::LambdaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(AntiLambda, kAntiLambda, aod::AntiLambdaReducedCandidatesData, aod::AntiLambdaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Xi, kXi, aod::XiReducedCandidatesData, aod::XiReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Omega, kOmega, aod::OmegaReducedCandidatesData, aod::OmegaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Pion, kPion, aod::PionTracksData, aod::PionTracksMcReco) +#undef DEFINE_ME_PROCESSES +#undef DO_PROCESS_SWITCH + + /*void processPhiK0SMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced, aod::BCs const&) @@ -1100,7 +1239,7 @@ struct PhiStrangeCorrelation { processPhiAssocME(collisions, phiCandidates, pionTracks); } - PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true);*/ void processParticleEfficiency(MCCollisions const& mcCollisions, SimCollisions const& collisions, @@ -1196,8 +1335,8 @@ struct PhiStrangeCorrelation { const auto mcParticlesThisMcColl = mcParticles.sliceBy(preslices.mcPartPerMcCollision, mcCollision.globalIndex()); for (const auto& mcParticle : mcParticlesThisMcColl) { - auto inYAcceptance = [&]() { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; + if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { + continue; }; auto fillGenHistos = [&](auto h3Key, auto h4Key) { @@ -1215,28 +1354,12 @@ struct PhiStrangeCorrelation { fillGenHistos(h3Key, h4Key); }; - if (!inYAcceptance()) { - continue; - } - switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) { + if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPtPhi) { fillGenHistos(HIST("phi/h3PhiMCGen"), HIST("phi/h4PhiMCGenAssocReco")); } break; - /*case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - fillGenHistos(HIST("k0s/h3K0SMCGen"), HIST("k0s/h4K0SMCGenAssocReco")); - break; - case PDG_t::kXiMinus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cascadeSettingMinPt) - fillGenHistos(HIST("xi/h3XiMCGen"), HIST("xi/h4XiMCGenAssocReco")); - break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - fillGenHistos(HIST("pi/h3PiMCGen"), HIST("pi/h4PiMCGenAssocReco")); - break;*/ case getPdgCode(): fillGenAssocSpecies.template operator()(HIST("k0s/h3K0SMCGen"), HIST("k0s/h4K0SMCGenAssocReco")); break; @@ -1264,205 +1387,68 @@ struct PhiStrangeCorrelation { PROCESS_SWITCH(PhiStrangeCorrelation, processParticleEfficiency, "Process function for Efficiency Computation for Particles of Interest", false); - /*void processMCGenClosureSE(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) - { - float multiplicity = mcCollision.centFT0M(); - - std::vector phiIndices; - std::vector k0sIndices; - std::vector pionIndices; - std::vector> assocIndices; - - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; - - for (const auto& mcParticle : mcParticles) { - if (!inYAcceptance(mcParticle)) - continue; - - switch (std::abs(mcParticle.pdgCode())) { - case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) - phiIndices.push_back(mcParticle.globalIndex()); - break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - k0sIndices.push_back(mcParticle.globalIndex()); - break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - pionIndices.push_back(mcParticle.globalIndex()); - break; - default: - break; - } - } - - assocIndices.push_back(k0sIndices); - assocIndices.push_back(pionIndices); - - for (std::size_t iTrigg{0}; iTrigg < phiIndices.size(); ++iTrigg) { - auto& phiParticle = mcParticles.rawIteratorAt(phiIndices[iTrigg]); - - static_for<0, assocIndices.size() - 1>([&](auto i_idx) { - constexpr unsigned int Idx = i_idx.value; - - for (std::size_t iAssoc{0}; iAssoc < assocIndices[Idx].size(); ++iAssoc) { - auto& assocParticle = mcParticles.rawIteratorAt(assocIndices[Idx][iAssoc]); - - histos.fill(HIST("mcGenClosure/h5Phi") + HIST(AssocParticleLabels[Idx]) + HIST("ClosureGenSE"), multiplicity, phiParticle.pt(), assocParticle.pt(), phiParticle.y() - assocParticle.y(), getDeltaPhi(phiParticle.phi(), assocParticle.phi())); - } - }); - } - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processMCGenClosureSE, "Process function for MC Gen Closure Test in SE", false); - - void processMCGenClosureME(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) - { - float multiplicity = mcCollision.centFT0M(); - - std::vector phiParticles; - std::vector k0sParticles; - std::vector pionParticles; - - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; - - for (const auto& mcParticle : mcParticles) { - if (!inYAcceptance(mcParticle)) - continue; - - switch (std::abs(mcParticle.pdgCode())) { - case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) - phiParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - k0sParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - pionParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break; - default: - break; - } - } - - if (phiParticles.empty() && k0sParticles.empty() && pionParticles.empty()) - return; - - int multBin = getCentBin(multiplicity); - - // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past K0S and pion particles - for (const auto& pastEvent : eventBuffer[multBin]) { - for (const auto& phiParticle : phiParticles) { - for (const auto& k0sParticle : pastEvent.k0sParticles) { - histos.fill(HIST("mcGenClosure/h5PhiK0SClosureGenME"), multiplicity, phiParticle.pt, k0sParticle.pt, phiParticle.y - k0sParticle.y, getDeltaPhi(phiParticle.phi, k0sParticle.phi)); - } - for (const auto& pionParticle : pastEvent.pionParticles) { - histos.fill(HIST("mcGenClosure/h5PhiPiClosureGenME"), multiplicity, phiParticle.pt, pionParticle.pt, phiParticle.y - pionParticle.y, getDeltaPhi(phiParticle.phi, pionParticle.phi)); - } - } - } - - // Add current event to buffer - MiniEvent currentEvent; - currentEvent.multiplicity = multiplicity; - currentEvent.phiParticles = std::move(phiParticles); - currentEvent.k0sParticles = std::move(k0sParticles); - currentEvent.pionParticles = std::move(pionParticles); - - eventBuffer[multBin].push_front(std::move(currentEvent)); - if (eventBuffer[multBin].size() > static_cast(cfgNoMixedEvents.value)) - eventBuffer[multBin].pop_back(); - } - - PROCESS_SWITCH(PhiStrangeCorrelation, processMCGenClosureME, "Process function for MC Gen Closure Test in ME", false);*/ - void processMCGenClosure(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { float multiplicity = mcCollision.centFT0M(); std::vector phiParticles; - std::vector k0sParticles; + std::array, kAssocPartSize> assocParticles; + /*std::vector k0sParticles; std::vector lambdaParticles; std::vector antilambdaParticles; std::vector xiParticles; std::vector omegaParticles; - std::vector pionParticles; + std::vector pionParticles;*/ // Preliminary loop to fill vectors of particles of interest for the current event, applying pt and y cuts for (const auto& mcParticle : mcParticles) { - auto inYAcceptance = [&]() { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; + if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { + continue; }; - auto fillPartCollection = [&](auto& collection) { - if (!activeCorrelationTypes->at(PartType) || !isGenSpeciesValid(mcParticle, minPtMcGenConfigs.bypassPtCut.value)) { - return; + auto fillPartCollection = [&]() { + if (activeCorrelationTypes->at(PartType) && isGenSpeciesValid(mcParticle, minPtMcGenConfigs.bypassPtCut.value)) { + assocParticles[PartType].emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); } - - collection.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); }; - if (!inYAcceptance()) { - continue; - } - switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.minPhiPt)) { + if (eventSelectionType == 0 && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.minPtPhi)) { phiParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); } break; - /*case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt)) - k0sParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break; - case PDG_t::kXiMinus: - if (mcParticle.isPhysicalPrimary() && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.cascadeSettingMinPt)) - xiParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut)) - pionParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); - break;*/ case getPdgCode(): - fillPartCollection.template operator()(k0sParticles); + fillPartCollection.template operator()(); break; case getPdgCode(): - fillPartCollection.template operator()(lambdaParticles); + fillPartCollection.template operator()(); break; case getPdgCode(): - fillPartCollection.template operator()(antilambdaParticles); + fillPartCollection.template operator()(); break; case getPdgCode(): - fillPartCollection.template operator()(xiParticles); + fillPartCollection.template operator()(); break; case getPdgCode(): - fillPartCollection.template operator()(omegaParticles); + fillPartCollection.template operator()(); break; case getPdgCode(): - fillPartCollection.template operator()(pionParticles); + fillPartCollection.template operator()(); break; default: break; } } - const bool skipK0s = !activeCorrelationTypes->at(kK0S) || k0sParticles.empty(); - const bool skipLambda = !activeCorrelationTypes->at(kLambda) || lambdaParticles.empty(); - const bool skipAntiLambda = !activeCorrelationTypes->at(kAntiLambda) || antilambdaParticles.empty(); - const bool skipXi = !activeCorrelationTypes->at(kXi) || xiParticles.empty(); - const bool skipOmega = !activeCorrelationTypes->at(kOmega) || omegaParticles.empty(); - const bool skipPion = !activeCorrelationTypes->at(kPion) || pionParticles.empty(); + bool hasAnyAssoc = false; + for (size_t i = 0; i < kAssocPartSize; ++i) { + if (!assocParticles[i].empty()) { + hasAnyAssoc = true; + } + } - if (phiParticles.empty() && skipK0s && skipLambda && skipAntiLambda && skipXi && skipOmega && skipPion) { + if (phiParticles.empty() && !hasAnyAssoc) { return; } @@ -1472,9 +1458,6 @@ struct PhiStrangeCorrelation { } // Same Event Correlations - std::array*, kAssocPartSize> currentAssocParticles{&k0sParticles, &lambdaParticles, &antilambdaParticles, - &xiParticles, &omegaParticles, &pionParticles}; - for (const auto& phiParticle : phiParticles) { histos.fill(HIST("phi/h3PhiMCClosureGen"), multiplicity, phiParticle.pt, phiParticle.y); @@ -1484,7 +1467,7 @@ struct PhiStrangeCorrelation { return; } - for (const auto& assocParticle : *(currentAssocParticles[Idx])) { + for (const auto& assocParticle : assocParticles[Idx]) { histos.fill(HIST("phi") + HIST(AssocParticleLabels[Idx]) + HIST("/h5Phi") + HIST(AssocParticleLabels[Idx]) + HIST("ClosureMCGen"), multiplicity, phiParticle.pt, assocParticle.pt, phiParticle.y - assocParticle.y, @@ -1494,11 +1477,8 @@ struct PhiStrangeCorrelation { } // Mixed Event Correlations + // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past associated particles for (const auto& pastEvent : eventBuffer[multBin]) { - std::array*, kAssocPartSize> pastAssocParticles{&pastEvent.k0sParticles, &pastEvent.lambdaParticles, &pastEvent.antilambdaParticles, - &pastEvent.xiParticles, &pastEvent.omegaParticles, &pastEvent.pionParticles}; - - // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past associated particles for (const auto& phiParticle : phiParticles) { static_for<0, AssocParticleLabels.size() - 1>([&](auto i_idx) { constexpr unsigned int Idx = i_idx.value; @@ -1506,7 +1486,7 @@ struct PhiStrangeCorrelation { return; } - for (const auto& assocParticle : *(pastAssocParticles[Idx])) { + for (const auto& assocParticle : pastEvent.assocParticles[Idx]) { histos.fill(HIST("phi") + HIST(AssocParticleLabels[Idx]) + HIST("/h5Phi") + HIST(AssocParticleLabels[Idx]) + HIST("ClosureMCGenME"), multiplicity, phiParticle.pt, assocParticle.pt, phiParticle.y - assocParticle.y, @@ -1519,12 +1499,13 @@ struct PhiStrangeCorrelation { MiniEvent currentEvent; currentEvent.multiplicity = multiplicity; currentEvent.phiParticles = std::move(phiParticles); - currentEvent.k0sParticles = std::move(k0sParticles); + currentEvent.assocParticles = std::move(assocParticles); + /*currentEvent.k0sParticles = std::move(k0sParticles); currentEvent.lambdaParticles = std::move(lambdaParticles); currentEvent.antilambdaParticles = std::move(antilambdaParticles); currentEvent.xiParticles = std::move(xiParticles); currentEvent.omegaParticles = std::move(omegaParticles); - currentEvent.pionParticles = std::move(pionParticles); + currentEvent.pionParticles = std::move(pionParticles);*/ eventBuffer[multBin].push_front(std::move(currentEvent)); if (eventBuffer[multBin].size() > static_cast(cfgNoMixedEvents.value)) { From 20410e55c01c15bc85b683e821ff296dd8016bfe Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Fri, 25 Sep 2026 20:46:33 +0200 Subject: [PATCH 074/648] [PWGLF] Fix MC processing in forwardlambdakzeroanalysis task (#18051) --- PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx index 1abe53f3258..c73a5926a7a 100644 --- a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx @@ -268,7 +268,6 @@ struct forwardlambdakzeroanalysis { ctpRateFetcher rateFetcher; int mRunNumber = 0; float magField = 0.; - std::map metadata; o2::parameters::GRPMagField* grpmag = nullptr; // CCDB options @@ -2998,7 +2997,7 @@ struct forwardlambdakzeroanalysis { // Simulated processing in Run 3 (subscribes to MC information too) void processMonteCarlo(soa::Join const& collisions, soa::Join const& tracks, - soa::SmallGroups> const& besttracks, + soa::Join const& besttracks, aod::BCsWithTimestamps const& bcs, soa::Join const& mccollisions, aod::McParticles const& mcParticles) From fd4fa999e9ae61547c7dfb91afec595c93144b4f Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:45:39 +0200 Subject: [PATCH 075/648] [PWGLF] Add multi-dim ZDC recenter and new hist in spin correlation task (#18063) --- .../Common/zdc2stagecalibration.cxx | 131 +++++++++++-- .../Strangeness/lambdaspincorrderived.cxx | 174 +++++++++++++++++- 2 files changed, 288 insertions(+), 17 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 33dd31d91cd..6c686f336ec 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -78,7 +78,9 @@ struct zdc2stagecalibration { Configurable deriveSpatialCalib{"deriveSpatialCalib", false, "store spatial regression moments after gain correction"}; Configurable deriveQRecentering{"deriveQRecentering", false, "store Q-vector recentering regression moments after gain and spatial correction"}; Configurable useQRecentering{"useQRecentering", false, "apply Q-vector recentering calibration"}; - Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the run-wise Q-vector recentering coefficients"}; + Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression"}; + Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; + Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, @@ -103,10 +105,16 @@ struct zdc2stagecalibration { static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; static constexpr int kQRecenteringNComponents = 4; static constexpr int kQRecenteringNMoments = kQRecenteringNMatrixMoments + kQRecenteringNComponents * kQRecenteringNFeatures; + static constexpr int kQRecenteringMultidimNFeatures = 35; + static constexpr int kQRecenteringMultidimNMatrixMoments = kQRecenteringMultidimNFeatures * (kQRecenteringMultidimNFeatures + 1) / 2; + static constexpr int kQRecenteringMultidimNMoments = kQRecenteringMultidimNMatrixMoments + kQRecenteringNComponents * kQRecenteringMultidimNFeatures; void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + if (qRecenteringMethod.value < 0 || qRecenteringMethod.value > 1) { + LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression or 1 for the multidimensional cubic regression.", qRecenteringMethod.value); + } const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; @@ -119,6 +127,7 @@ struct zdc2stagecalibration { AxisSpec crossCoordAxis = {2, 0.0, 2.0, "coordinate"}; AxisSpec ratioAxis = {240, 0.0, 2.4, "#Sigma tower/common"}; AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; + AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments, 0.0, static_cast(kQRecenteringMultidimNMoments), "multidimensional regression moment"}; AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; AxisSpec vertexXYAxis = {100, -0.5, 0.5, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; @@ -187,6 +196,7 @@ struct zdc2stagecalibration { // For each 1% centrality bin, moments 0..230 are the upper triangle of sum(X_i X_j), // followed by 4 blocks of 21 sum(X_i Q): QxA, QyA, QxC, QyC. histos.add("QRecenteringCalibration/hRegressionMoments", "Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMomentAxis}); + histos.add("QRecenteringCalibration/hRegressionMomentsMultidim", "Multidimensional cubic Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMultidimMomentAxis}); histos.add("QRecenteringQA/pQBeforeVsCentrality", "Q before recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); histos.add("QRecenteringQA/pQBeforeVsTime", "Q before recentering vs time;time from SOR (h);Q component;", kTProfile2D, {timeAxis, qComponentAxis}); @@ -198,6 +208,22 @@ struct zdc2stagecalibration { histos.add("QRecenteringQA/pQAfterVsVx", "Q after recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVy", "Q after recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityTime", "QyC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVx", "QxA after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVx", "QyA after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVx", "QxC after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVx", "QyC after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVy", "QxA after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVy", "QyA after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVy", "QxC after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVy", "QyC after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVz", "QxA after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVz", "QyA after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVz", "QxC after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVz", "QyC after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); // A-C Q-vector correlation QA. These histograms are diagnostic only; no Qx/Qy mixing is applied. histos.add("QCorrelationQA/hQxAQxCBefore", "QxA vs QxC before recentering;Q_{x}^{A};Q_{x}^{C}", kTH2F, {qValueAxis, qValueAxis}); @@ -252,8 +278,11 @@ struct zdc2stagecalibration { LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); } } - if (calibrationStage.value == 2 && useQRecentering.value && !confQRecenteringPath.value.empty()) { - qRecenteringProfile = ccdb->getForTimeStamp(confQRecenteringPath.value, timestamp); + if (calibrationStage.value == 2 && useQRecentering.value) { + const std::string qRecenteringPath = qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value; + if (!qRecenteringPath.empty()) { + qRecenteringProfile = ccdb->getForTimeStamp(qRecenteringPath, timestamp); + } if (!qRecenteringProfile) { LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); } @@ -307,6 +336,26 @@ struct zdc2stagecalibration { x * y, x * z, y * z}; } + std::array makeQRecenteringMultidimFeatures(float timeFromSOR, float vx, float vy, float vz) const + { + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + const double t2 = t * t; + const double x2 = x * x; + const double y2 = y * y; + const double z2 = z * z; + + return {1.0, t, x, y, z, + t2, x2, y2, z2, t * x, t * y, t * z, x * y, x * z, y * z, + t2 * t, x2 * x, y2 * y, z2 * z, + t2 * x, t2 * y, t2 * z, x2 * t, x2 * y, x2 * z, + y2 * t, y2 * x, y2 * z, z2 * t, z2 * x, z2 * y, + t * x * y, t * x * z, t * y * z, x * y * z}; + } + using MyCollisions = o2::soa::Join; void process(MyCollisions::iterator const& collision, o2::aod::FT0s const& /*ft0s*/, o2::aod::FV0As const& /*fv0s*/, BCsRun3 const& /*bcs*/, o2::aod::Zdcs const& /*zdcs*/) @@ -632,7 +681,6 @@ struct zdc2stagecalibration { return; } - const auto qFeatures = makeQRecenteringFeatures(centrality, timeFromSOR, vx, vy, vz); std::array qValues = {qxZDCA, qyZDCA, qxZDCC, qyZDCC}; for (int i = 0; i < 4; ++i) { @@ -654,17 +702,35 @@ struct zdc2stagecalibration { histos.fill(HIST("QCorrelationQA/pQACorrelationBeforeVsCentrality"), centrality, 3.5, qValues[1] * qValues[2]); if (calibrationStage.value == 2 && useGainCallib.value && useSpatialCalib.value && deriveQRecentering.value && !useQRecentering.value) { - int moment = 0; - for (int i = 0; i < kQRecenteringNFeatures; ++i) { - for (int j = i; j < kQRecenteringNFeatures; ++j) { - histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, moment + 0.5, qFeatures[i] * qFeatures[j]); - ++moment; - } - } - for (int component = 0; component < kQRecenteringNComponents; ++component) { + if (qRecenteringMethod.value == 0) { + const auto qFeatures = makeQRecenteringFeatures(centrality, timeFromSOR, vx, vy, vz); + int moment = 0; for (int i = 0; i < kQRecenteringNFeatures; ++i) { - const int index = kQRecenteringNMatrixMoments + component * kQRecenteringNFeatures + i; - histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, index + 0.5, qFeatures[i] * qValues[component]); + for (int j = i; j < kQRecenteringNFeatures; ++j) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, moment + 0.5, qFeatures[i] * qFeatures[j]); + ++moment; + } + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringNFeatures; ++i) { + const int index = kQRecenteringNMatrixMoments + component * kQRecenteringNFeatures + i; + histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, index + 0.5, qFeatures[i] * qValues[component]); + } + } + } else { + const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + int moment = 0; + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + for (int j = i; j < kQRecenteringMultidimNFeatures; ++j) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, moment + 0.5, qFeatures[i] * qFeatures[j]); + ++moment; + } + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const int index = kQRecenteringMultidimNMatrixMoments + component * kQRecenteringMultidimNFeatures + i; + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, index + 0.5, qFeatures[i] * qValues[component]); + } } } } @@ -673,7 +739,8 @@ struct zdc2stagecalibration { if (calibrationStage.value == 2 && useQRecentering.value) { if (!qRecenteringProfile) { qRecenteringOK = false; - } else { + } else if (qRecenteringMethod.value == 0) { + const auto qFeatures = makeQRecenteringFeatures(centrality, timeFromSOR, vx, vy, vz); for (int component = 0; component < kQRecenteringNComponents; ++component) { double predictedBias = 0.0; for (int i = 0; i < kQRecenteringNFeatures; ++i) { @@ -690,6 +757,24 @@ struct zdc2stagecalibration { } qValues[component] -= predictedBias; } + } else { + const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } } } if (!qRecenteringOK) { @@ -705,6 +790,22 @@ struct zdc2stagecalibration { histos.fill(HIST("QRecenteringQA/pQAfterVsVy"), vy, component, qValues[i]); histos.fill(HIST("QRecenteringQA/pQAfterVsVz"), vz, component, qValues[i]); } + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityTime"), centrality, timeFromSOR, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityTime"), centrality, timeFromSOR, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityTime"), centrality, timeFromSOR, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityTime"), centrality, timeFromSOR, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVx"), centrality, vx, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVx"), centrality, vx, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVx"), centrality, vx, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVx"), centrality, vx, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVy"), centrality, vy, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVy"), centrality, vy, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVy"), centrality, vy, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVy"), centrality, vy, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVz"), centrality, vz, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVz"), centrality, vz, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVz"), centrality, vz, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVz"), centrality, vz, qValues[3]); histos.fill(HIST("QCorrelationQA/hQxAQxCAfter"), qValues[0], qValues[2]); histos.fill(HIST("QCorrelationQA/hQyAQyCAfter"), qValues[1], qValues[3]); diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index b814f51feab..2f75bd83291 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -656,7 +656,129 @@ struct lambdaspincorrderived { histos.add("hSparseLambdaAntiLambda", "hSparseLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaLambda", "hSparseAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaAntiLambda", "hSparseAntiLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - + // ============================================================ + // cos^2(theta*) sparses + // ============================================================ + + // Same event + histos.add("hSparseLambdaLambdaCos2", + "hSparseLambdaLambdaCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseLambdaAntiLambdaCos2", + "hSparseLambdaAntiLambdaCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaLambdaCos2", + "hSparseAntiLambdaLambdaCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaAntiLambdaCos2", + "hSparseAntiLambdaAntiLambdaCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + // Mixed event + histos.add("hSparseLambdaLambdaMixedCos2", + "hSparseLambdaLambdaMixedCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseLambdaAntiLambdaMixedCos2", + "hSparseLambdaAntiLambdaMixedCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaLambdaMixedCos2", + "hSparseAntiLambdaLambdaMixedCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaAntiLambdaMixedCos2", + "hSparseAntiLambdaAntiLambdaMixedCos2", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + // ============================================================ + // cos^3(theta*) sparses + // ============================================================ + + // Same event -- useful QA / future higher moment + histos.add("hSparseLambdaLambdaCos3", + "hSparseLambdaLambdaCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseLambdaAntiLambdaCos3", + "hSparseLambdaAntiLambdaCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaLambdaCos3", + "hSparseAntiLambdaLambdaCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaAntiLambdaCos3", + "hSparseAntiLambdaAntiLambdaCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + // Mixed event -- needed for linearity test + histos.add("hSparseLambdaLambdaMixedCos3", + "hSparseLambdaLambdaMixedCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseLambdaAntiLambdaMixedCos3", + "hSparseLambdaAntiLambdaMixedCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaLambdaMixedCos3", + "hSparseAntiLambdaLambdaMixedCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); + + histos.add("hSparseAntiLambdaAntiLambdaMixedCos3", + "hSparseAntiLambdaAntiLambdaMixedCos3", + HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisInvMass, + configThnAxisPol, configThnAxisR}, + true); // Original same-event pairs split by whether the ME replacement search // succeeds. These use the original SE pair variables and the ordinary // SE NUA weight; no mixing or replacement-efficiency weight is applied. @@ -1142,7 +1264,8 @@ struct lambdaspincorrderived { cosDeltaTheta_hel = -111.0; double cosThetaDiff = (cosDef == 0) ? cosDeltaTheta_STAR_naive : cosDeltaTheta_hel; - + const double cosThetaDiff2 = cosThetaDiff * cosThetaDiff; + const double cosThetaDiff3 = cosThetaDiff2 * cosThetaDiff; double pt1 = particle1.Pt(); double dphi1 = RecoDecay::constrainAngle(particle1.Phi(), 0.0F, harmonic); double deta1 = particle1.Eta(); @@ -1250,6 +1373,8 @@ struct lambdaspincorrderived { if (fillBasicQAHistos) histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); histos.fill(HIST("hSparseLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseLambdaLambdaCos2"), particle1.M(), particle2.M(), cosThetaDiff2, deltaR, weight); + histos.fill(HIST("hSparseLambdaLambdaCos3"), particle1.M(), particle2.M(), cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1260,6 +1385,8 @@ struct lambdaspincorrderived { } } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseLambdaAntiLambdaCos2"), particle1.M(), particle2.M(), cosThetaDiff2, deltaR, weight); + histos.fill(HIST("hSparseLambdaAntiLambdaCos3"), particle1.M(), particle2.M(), cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1270,6 +1397,13 @@ struct lambdaspincorrderived { } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseAntiLambdaLambdaCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseAntiLambdaLambdaCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1280,6 +1414,13 @@ struct lambdaspincorrderived { } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseAntiLambdaAntiLambdaCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseAntiLambdaAntiLambdaCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1323,6 +1464,14 @@ struct lambdaspincorrderived { histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); } histos.fill(HIST("hSparseLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseLambdaLambdaMixedCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseLambdaLambdaMixedCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); + if (fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1334,6 +1483,13 @@ struct lambdaspincorrderived { } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseLambdaAntiLambdaMixedCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseLambdaAntiLambdaMixedCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1344,6 +1500,13 @@ struct lambdaspincorrderived { } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseAntiLambdaLambdaMixedCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseAntiLambdaLambdaMixedCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } @@ -1354,6 +1517,13 @@ struct lambdaspincorrderived { } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedCos2"), + particle1.M(), particle2.M(), + cosThetaDiff2, deltaR, weight); + + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedCos3"), + particle1.M(), particle2.M(), + cosThetaDiff3, deltaR, weight); if (fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } From f2cb248902a606a0ca1e94d3740a9c492d74f5f6 Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Fri, 25 Sep 2026 22:19:06 +0200 Subject: [PATCH 076/648] [DPG] use metadata info to set isMC and NumberOfOrbitsPerTF (#18054) --- DPG/Tasks/AOTEvent/eventSelectionQa.cxx | 38 +++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index c5da0ee0076..56a4c9a28ac 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -16,6 +16,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/MetadataHelper.h" #include "Common/DataModel/EventSelection.h" #include @@ -59,6 +60,8 @@ using namespace o2::framework; using namespace o2; using namespace o2::aod::evsel; +o2::common::core::MetadataHelper metadataInfo; + using BCsRun2 = soa::Join; using BCsRun3 = soa::Join; using ColEvSels = soa::Join; @@ -66,7 +69,6 @@ using FullTracksIU = soa::Join; using FullTracksIUwithLabels = soa::Join; struct EventSelectionQaTask { - Configurable isMC{"isMC", 0, "0 - data, 1 - MC"}; Configurable nGlobalBCs{"nGlobalBCs", 100000, "number of global bcs for detailed monitoring"}; Configurable isLowFlux{"isLowFlux", 1, "1 - low flux (pp, pPb), 0 - high flux (PbPb)"}; Configurable fillITSdeadStaveHists{"fillITSdeadStaveHists", 0, "0 - no, 1 - yes"}; @@ -85,6 +87,10 @@ struct EventSelectionQaTask { int rofOffset = -1; // ITS ROF offset, in bc int rofLength = -1; // ITS ROF length, in bc + std::string strLPMProductionTag = ""; // MC production tag to be retrieved from AO2D metadata + std::string strPassName = ""; // RecoPassName (for data) or AnchorPassName (for MC) from metadata + bool isMC = false; + std::bitset bcPatternA; std::bitset bcPatternC; std::bitset bcPatternB; @@ -111,9 +117,13 @@ struct EventSelectionQaTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - const AxisSpec axisMultV0M{1000, 0., isLowFlux ? 40000. : 40000., "V0M multiplicity"}; + isMC = metadataInfo.isMC(); + strLPMProductionTag = metadataInfo.get("LPMProductionTag"); // to extract info from ccdb by the tag + strPassName = metadataInfo.get(isMC ? "AnchorPassName" : "RecoPassName"); + + const AxisSpec axisMultV0M{1000, 0., 40000., "V0M multiplicity"}; const AxisSpec axisMultV0A{1000, 0., isLowFlux ? 40000. : 200000., "V0A multiplicity"}; - const AxisSpec axisMultV0C{1000, 0., isLowFlux ? 30000. : 30000., "V0C multiplicity"}; + const AxisSpec axisMultV0C{1000, 0., 30000., "V0C multiplicity"}; const AxisSpec axisMultT0A{1000, 0., isLowFlux ? 10000. : 200000., "T0A multiplicity"}; const AxisSpec axisMultT0C{1000, 0., isLowFlux ? 2000. : 70000., "T0C multiplicity"}; const AxisSpec axisMultT0M{1000, 0., isLowFlux ? 12000. : 270000., "T0M multiplicity"}; @@ -568,9 +578,7 @@ struct EventSelectionQaTask { histos.fill(HIST("hV0C012vsTklCol"), nTracklets, multRingV0C012); // filling plots for accepted events - bool accepted = 0; - accepted |= !isINT1period & col.sel7(); - accepted |= isINT1period & sel1; + bool accepted = (!isINT1period && col.sel7()) || (isINT1period && sel1); if (!accepted) { continue; } @@ -622,8 +630,13 @@ struct EventSelectionQaTask { lastRun = run; int64_t tsSOR = 0; // dummy start-of-run timestamp for unanchored MC int64_t tsEOR = 1; // dummy end-of-run timestamp for unanchored MC - if (run >= 500000) { - auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run); + const int run3min = 500000; + if (run >= run3min) { + auto runInfo = (!isMC) ? o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run) + : o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run, strLPMProductionTag); + LOGP(info, "eventSelectionQA: isMC = {}, NumberOfOrbitsPerTF extracted from AggregatedRunInfo = {}", isMC, runInfo.orbitsPerTF); + LOGP(info, "eventSelectionQA: strLPMProductionTag = {}, strPassName = {}", strLPMProductionTag, strPassName); + // first bc of the first orbit bcSOR = runInfo.orbitSOR * nBCsPerOrbit; // number of orbits per TF @@ -786,13 +799,13 @@ struct EventSelectionQaTask { const auto& bcPast = bcs.iteratorAt(bc.globalIndex() - deltaIndex); deltaBC = globalBC - bcPast.globalBC(); if (deltaBC < maxDeltaBC) { - pastActivityFT0 |= bcPast.has_ft0(); - pastActivityFV0 |= bcPast.has_fv0a(); - pastActivityFDD |= bcPast.has_fdd(); + pastActivityFT0 = pastActivityFT0 || bcPast.has_ft0(); + pastActivityFV0 = pastActivityFV0 || bcPast.has_fv0a(); + pastActivityFDD = pastActivityFDD || bcPast.has_fdd(); } } - bool pastActivity = pastActivityFT0 | pastActivityFV0 | pastActivityFDD; + bool pastActivity = pastActivityFT0 || pastActivityFV0 || pastActivityFDD; int localBC = bc.globalBC() % nBCsPerOrbit; float timeV0A = bc.has_fv0a() ? bc.fv0a().time() : -999.f; @@ -1508,6 +1521,7 @@ struct EventSelectionQaTask { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + metadataInfo.initMetadata(cfgc); return WorkflowSpec{ adaptAnalysisTask(cfgc)}; } From b7924a64f111fc7f2144078a96e12d1005bbe7bb Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Fri, 25 Sep 2026 22:58:24 +0200 Subject: [PATCH 077/648] [PWGLF] Fix buildTrackedCascades in strangenessBuilderModule.h (#18062) --- PWGLF/Utils/strangenessBuilderModule.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGLF/Utils/strangenessBuilderModule.h b/PWGLF/Utils/strangenessBuilderModule.h index 82363d970c0..45b28701685 100644 --- a/PWGLF/Utils/strangenessBuilderModule.h +++ b/PWGLF/Utils/strangenessBuilderModule.h @@ -1265,7 +1265,7 @@ class BuilderModule sorted_v0 = sort_indices(v0List, (baseOpts.mc_findableMode.value > 0)); // Cascade part if cores are requested, skip otherwise - if (baseOpts.mEnabledTables[kStoredCascCores] || baseOpts.mEnabledTables[kStoredKFCascCores]) { + if (baseOpts.mEnabledTables[kStoredCascCores] || baseOpts.mEnabledTables[kStoredKFCascCores] || baseOpts.mEnabledTables[kStoredTraCascCores]) { if (baseOpts.mc_findableMode.value < 2) { // simple passthrough: copy existing cascades to build list for (const auto& cascade : cascades) { @@ -2810,7 +2810,7 @@ class BuilderModule // interlink always produced if base core table generated traCascIndices[cascade.globalIndex()] = products.tracascdata.lastIndex(); } - if (baseOpts.mEnabledTables[kCascCovs]) { + if (baseOpts.mEnabledTables[kTraCascCovs]) { std::array traCovMat = {0.}; strangeTrackParCov.getCovXYZPxPyPzGlo(traCovMat); float traCovMatArray[o2::track::kLabCovMatSize]; @@ -2818,7 +2818,7 @@ class BuilderModule traCovMatArray[ii] = traCovMat[ii]; } products.tracasccovs(traCovMatArray); - histos.fill(HIST("hTableBuildingStatistics"), kCascCovs); + histos.fill(HIST("hTableBuildingStatistics"), kTraCascCovs); } //_________________________________________________________ From 305fb1702014ae926b05e35d5f985b5f7bf9e164 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Fri, 25 Sep 2026 23:29:57 +0200 Subject: [PATCH 078/648] [PWGHF] Correlated backgrounds in derived data creators (#18043) --- PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx | 11 ++++++----- .../TableProducer/derivedDataCreatorDplusToPiKPi.cxx | 7 ++++--- PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx | 7 ++++--- PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx | 7 ++++--- PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx | 7 ++++--- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx index 603316670a1..6c8dd98f3c9 100644 --- a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx @@ -83,6 +83,7 @@ struct HfDerivedDataCreatorD0ToKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; @@ -106,7 +107,7 @@ struct HfDerivedDataCreatorD0ToKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_d0::isSelD0 >= 1 || aod::hf_sel_candidate_d0::isSelD0bar >= 1; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesKfPerCollision = aod::hf_cand::collisionId; @@ -128,13 +129,13 @@ struct HfDerivedDataCreatorD0ToKPi { Partition candidatesMcMlAll = aod::hf_sel_candidate_d0::isSelD0 >= 0; Partition candidatesMcKfMlAll = aod::hf_sel_candidate_d0::isSelD0 >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcKfSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcKfSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcKfBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcKfMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcKfMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcKfMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); void init(InitContext const&) diff --git a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx index 74da79ae04c..b1cad301ba6 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx @@ -84,6 +84,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; @@ -103,7 +104,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = (aod::hf_sel_candidate_dplus::isSelDplusToPiKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -117,9 +118,9 @@ struct HfDerivedDataCreatorDplusToPiKPi { Partition candidatesMlAll = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); void init(InitContext const&) diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index 165dcadd758..7ed944efb1c 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -82,6 +82,7 @@ struct HfDerivedDataCreatorDsToKKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; @@ -101,7 +102,7 @@ struct HfDerivedDataCreatorDsToKKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -115,9 +116,9 @@ struct HfDerivedDataCreatorDsToKKPi { Partition candidatesMlAll = aod::hf_sel_candidate_ds::isSelDsToKKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_ds::isSelDsToKKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); void init(InitContext const&) diff --git a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx index a69bdea3f2c..5f20c81e4a5 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx @@ -80,6 +80,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; @@ -99,7 +100,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -113,9 +114,9 @@ struct HfDerivedDataCreatorDstarToD0Pi { Partition candidatesMlAll = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Partition candidatesMcMlAll = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); void init(InitContext const&) diff --git a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx index 7b37995e3a3..d543b4605f0 100644 --- a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx @@ -82,6 +82,7 @@ struct HfDerivedDataCreatorLcToPKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; @@ -101,7 +102,7 @@ struct HfDerivedDataCreatorLcToPKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 1 || aod::hf_sel_candidate_lc::isSelLcToPiKP >= 1; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -115,9 +116,9 @@ struct HfDerivedDataCreatorLcToPKPi { Partition candidatesMlAll = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec)) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec)) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); void init(InitContext const&) From 8741b741854b73818739cc5e3e8c935b9e438838 Mon Sep 17 00:00:00 2001 From: Hirak Koley Date: Sat, 26 Sep 2026 03:39:13 +0530 Subject: [PATCH 079/648] [PWGLF] Added first commit for h-resonance correlation (#18036) Co-authored-by: ALICE Action Bot --- .../DataModel/LFHResonanceCorrelationTables.h | 151 + PWGLF/TableProducer/Resonances/CMakeLists.txt | 5 + .../hResonanceCorrelationFilter.cxx | 1483 ++++++++ PWGLF/Tasks/Resonances/CMakeLists.txt | 5 + .../Resonances/hResonanceCorrelation.cxx | 3295 +++++++++++++++++ 5 files changed, 4939 insertions(+) create mode 100644 PWGLF/DataModel/LFHResonanceCorrelationTables.h create mode 100644 PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx create mode 100644 PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx diff --git a/PWGLF/DataModel/LFHResonanceCorrelationTables.h b/PWGLF/DataModel/LFHResonanceCorrelationTables.h new file mode 100644 index 00000000000..2bc281daaf6 --- /dev/null +++ b/PWGLF/DataModel/LFHResonanceCorrelationTables.h @@ -0,0 +1,151 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief This task serves to do hadron-resonance correlation studies. +/// The yield will be calculated using the two-particle correlation method. +/// Trigger particle : Hadrons +/// Associated Particles : Phi, K*0 +/// +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#ifndef PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ +#define PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ + +#include +#include + +// Simple checker +#define bitcheck(var, nbit) ((var) & (1 << (nbit))) + +namespace o2::aod +{ +/// _________________________________________ +/// Table for storing trigger track indices +namespace triggerTracks +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true physical primary flag +DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Trigger"); //! +DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt +DECLARE_SOA_COLUMN(IsLeading, isLeading, bool); // is leading track in the event +} // namespace triggerTracks +DECLARE_SOA_TABLE(TriggerTracks, "AOD", "TRIGGERTRACKS", o2::soa::Index<>, triggerTracks::CollisionId, triggerTracks::MCPhysicalPrimary, triggerTracks::TrackId, triggerTracks::MCOriginalPt, triggerTracks::IsLeading); +namespace triggerTrackExtras +{ +DECLARE_SOA_COLUMN(Extra, extra, int); // true physical primary flag +} // namespace triggerTrackExtras +DECLARE_SOA_TABLE(TriggerTrackExtras, "AOD", "TRIGGERTRACKEXTRAs", triggerTrackExtras::Extra); + +/// _________________________________________ +/// Table for storing assoc track indices +namespace assocHadrons +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true physical primary flag +DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! +DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt +DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); // pdg code of the MC particle +} // namespace assocHadrons +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode); +/// _________________________________________ +/// Table for storing assoc track PID +namespace assocPID +{ +DECLARE_SOA_COLUMN(NSigmaTPCPi, nSigmaTPCPi, float); +DECLARE_SOA_COLUMN(NSigmaTPCKa, nSigmaTPCKa, float); +DECLARE_SOA_COLUMN(NSigmaTPCPr, nSigmaTPCPr, float); +DECLARE_SOA_COLUMN(NSigmaTPCEl, nSigmaTPCEl, float); +DECLARE_SOA_COLUMN(NSigmaTOFPi, nSigmaTOFPi, float); +DECLARE_SOA_COLUMN(NSigmaTOFKa, nSigmaTOFKa, float); +DECLARE_SOA_COLUMN(NSigmaTOFPr, nSigmaTOFPr, float); +DECLARE_SOA_COLUMN(NSigmaTOFEl, nSigmaTOFEl, float); +} // namespace assocPID +DECLARE_SOA_TABLE(AssocPID, "AOD", "ASSOCPID", assocPID::NSigmaTPCPi, assocPID::NSigmaTPCKa, assocPID::NSigmaTPCPr, assocPID::NSigmaTPCEl, assocPID::NSigmaTOFPi, assocPID::NSigmaTOFKa, assocPID::NSigmaTOFPr, assocPID::NSigmaTOFEl); + +/// _________________________________________ +/// Table for storing associated phi candidate indices +namespace assocPhis +{ + +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! + +// MC information +DECLARE_SOA_COLUMN(MCTruePhi, mcTruePhi, bool); +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); + +// Quality variable +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Mass, mass, float); + +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_PhiPosDaughter"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_PhiNegDaughter"); + +} // namespace assocPhis + +DECLARE_SOA_TABLE( + AssocPhis, + "AOD", + "ASSOCPHIS", + o2::soa::Index<>, + assocPhis::CollisionId, + assocPhis::MCTruePhi, + assocPhis::MCPhysicalPrimary, + assocPhis::Pt, + assocPhis::Eta, + assocPhis::Phi, + assocPhis::Mass, + assocPhis::PosTrackDaughterId, + assocPhis::NegTrackDaughterId); + +/// _________________________________________ +/// Table for storing associated K*0 candidate indices +/// K*0 -> K+ pi- and anti-K*0 -> K- pi+ are stored together (sign-agnostic, +/// same convention as AssocPhis): PosTrackDaughter/NegTrackDaughter identify +/// the positively/negatively charged daughter, irrespective of which one is +/// the kaon and which one is the pion for that candidate. +namespace assocKstars +{ + +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! + +// MC information +DECLARE_SOA_COLUMN(MCTrueKstar, mcTrueKstar, bool); +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); + +// Quality variable +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Mass, mass, float); + +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_KstarPosDaughter"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_KstarNegDaughter"); + +} // namespace assocKstars + +DECLARE_SOA_TABLE( + AssocKstars, + "AOD", + "ASSOCKSTARS", + o2::soa::Index<>, + assocKstars::CollisionId, + assocKstars::MCTrueKstar, + assocKstars::MCPhysicalPrimary, + assocKstars::Pt, + assocKstars::Eta, + assocKstars::Phi, + assocKstars::Mass, + assocKstars::PosTrackDaughterId, + assocKstars::NegTrackDaughterId); +} // namespace o2::aod +#endif // PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ diff --git a/PWGLF/TableProducer/Resonances/CMakeLists.txt b/PWGLF/TableProducer/Resonances/CMakeLists.txt index 5ce14867844..bafd0b12ea0 100644 --- a/PWGLF/TableProducer/Resonances/CMakeLists.txt +++ b/PWGLF/TableProducer/Resonances/CMakeLists.txt @@ -69,3 +69,8 @@ o2physics_add_dpl_workflow(xi1530kaonreducedtable SOURCES xi1530kaonreducedtable.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hresonancecorrelationfilter + SOURCES hResonanceCorrelationFilter.cxx + PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx new file mode 100644 index 00000000000..959014c61ce --- /dev/null +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -0,0 +1,1483 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \brief This task pre-filters tracks to do h-resonance (phi, K*0) +/// correlations with an analysis task. +/// +/// \file hResonanceCorrelationFilter.cxx +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::constants::math; +using namespace o2::aod::rctsel; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using LorentzVectorPtEtaPhiMass = ROOT::Math::PtEtaPhiMVector; + +enum PIDCutType { + SquareType = 1, + CircularType, +}; + +#define BIT_SET(var, nbit) ((var) |= (1 << (nbit))) +#define BIT_CHECK(var, nbit) ((var) & (1 << (nbit))) + +struct HResonanceCorrelationFilter { + + Service ccdb; + + RCTFlagsChecker rctChecker; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // master analysis switches + Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; + + // Operational + Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; + + Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; + Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; + + Configurable cfgRapidity{"cfgRapidity", 0.5f, "Rapidity cut"}; + + // used for event selections in Pb-Pb + Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; + Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; + // event filtering + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; + Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; + Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; + Configurable minCentPercent{"minCentPercent", 0, "minimum centrality percentage"}; + Configurable maxCentPercent{"maxCentPercent", 100, "maximum centrality percentage"}; + } eventSelections; + + struct : ConfigurableGroup { + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + } configEvents; + + struct : ConfigurableGroup { + // Pre-selection Track cuts + Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; + Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; + Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; + Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + + // DCA Selections + // DCAr to PV + Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; + + // Track selections + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; + Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; + } configTracks; + + struct : ConfigurableGroup { + std::string prefix = "generalSelections"; + + // Associated particle selections in phase space + Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; + Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; + Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; + Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; + + // Trigger particle selections in phase space + Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; + Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; + Configurable triggerPtCutMin{"triggerPtCutMin", 3, "triggerptmin"}; + Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; + } generalSelections; + + struct : ConfigurableGroup { + std::string prefix = "trackSelections"; + // Track quality + Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; + Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; + Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; + Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for phi daughter tracks"}; + Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for phi daughter tracks"}; + + // Associated pion identification + Configurable pionMinBayesProb{"pionMinBayesProb", 0.95, "minimal Bayesian probability for pion ID"}; + Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; + Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; + + // Associated kaon identification (mirrors the pion selection above, with + // the accept/reject roles swapped: accept kaon-consistent tracks, reject + // pion-/proton-consistent ones). Selected via the Species template tag on + // isValidAssocTrack() below -- production path (which table rows + // get made) is still separate per process function, only the PID gate + // logic itself is shared. + Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; + + // primary particle DCAxy selections + // formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + } trackSelections; + + struct : ConfigurableGroup { + /// PID Selections + Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; + Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection + Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection + Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + + // Kaon + Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; + Configurable> kaonTPCPIDcuts{"kaonTPCPIDcuts", {2}, "nSigma list for Kaon TPC PID cuts"}; + Configurable> kaonTOFPIDpTintv{"kaonTOFPIDpTintv", {999.0f}, "pT intervals for Kaon TOF PID cuts"}; + Configurable> kaonTOFPIDcuts{"kaonTOFPIDcuts", {2}, "nSigma list for Kaon TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedpTintv{"kaonTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Kaon TPC-TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedPIDcuts{"kaonTPCTOFCombinedPIDcuts", {2}, "nSigma list for Kaon TPC-TOF PID cuts"}; + + // Pion (K*0 daughter) + Configurable> pionTPCPIDpTintv{"pionTPCPIDpTintv", {0.5f}, "pT intervals for Pion TPC PID cuts"}; + Configurable> pionTPCPIDcuts{"pionTPCPIDcuts", {2}, "nSigma list for Pion TPC PID cuts"}; + Configurable> pionTOFPIDpTintv{"pionTOFPIDpTintv", {999.0f}, "pT intervals for Pion TOF PID cuts"}; + Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; + Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; + Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; + } configPID; + + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + + // must include windows for background and peak + // + // This filter-level cut is an acceptance pre-cut only -- it decides what + // ever reaches AssocPhis/AssocKstars, not what counts as signal. It must + // stay wider than whatever signal+background region hResonanceCorrelation.cxx + // wants downstream (its massWindowConfigurationsPhi/Kstar go out to + // maxBgNSigma=6 by default), or that background region gets silently + // truncated before it ever reaches the analysis task. peakMass/sigma here + // mirror the analysis task's own massWindowConfigurationsPhi/Kstar + // (sigma = PDG Gamma/2.355 placeholder -- refit from your own peak and + // update both files together, they must agree on what "sigma" means). + Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsPhi"; + Configurable peakMass{"peakMass", 1.019455f, "Phi(1020) PDG mass (GeV)"}; + Configurable sigma{"sigma", 0.0018f, "effective width (GeV) for the maxMassNSigma pre-cut -- keep in sync with hResonanceCorrelation.cxx's massWindowConfigurationsPhi.sigma"}; + } massWindowConfigurationsPhi; + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsKstar"; + Configurable peakMass{"peakMass", 0.89555f, "K*0(892) PDG mass (GeV)"}; + Configurable sigma{"sigma", 0.0201f, "effective width (GeV) for the maxMassNSigma pre-cut -- keep in sync with hResonanceCorrelation.cxx's massWindowConfigurationsKstar.sigma"}; + } massWindowConfigurationsKstar; + + // Derived acceptance-window bounds, computed once in init() from the + // Configurables above (peakMass +/- maxMassNSigma * sigma) rather than + // hand-entered GeV numbers, so there is exactly one place to widen the + // pre-cut instead of four independent literals that can drift apart. + float mMinMassPhi = 0.f; + float mMaxMassPhi = 0.f; + float mMinMassKstar = 0.f; + float mMaxMassKstar = 0.f; + + // For extracting strangeness mass QA plots + struct : ConfigurableGroup { + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + ConfigurableAxis axisPhiMass{"axisPhiMass", {200, 0.99f, 1.08f}, "M(K^{+}K^{-})"}; + ConfigurableAxis axisKstarMass{"axisKstarMass", {200, 0.75f, 1.05f}, "M(K#pi)"}; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Centrality percentile bins"}; + } axesConfigurations; + + // QA + Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; + // Do declarative selections for DCAs, if possible + Filter preFilterTracks = nabs(aod::track::dcaXY) < trackSelections.dcaXYconstant + trackSelections.dcaXYpTdep * nabs(aod::track::signed1Pt); + + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; + using DauTracks = soa::Join; + using DauTracksMC = soa::Join; + // using IDTracks= soa::Join; // prepared for Bayesian PID + using IDTracks = soa::Join; + using IDTracksMC = soa::Join; + + Produces triggerTrack; + Produces triggerTrackExtra; + Produces assocPhis; + Produces assocKstars; + Produces assocHadrons; + Produces assocPID; + + using EventCandidates = soa::Join; + using TrackCandidates = soa::Filtered>; + + // for MC reco + using MCEventCandidates = soa::Join; + using MCTrackCandidates = soa::Filtered>; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + int mRunNumber = -1; + + struct TriggCandidate { + float pt = 0.f; + int collisionId = -1; + int trackId = -1; + bool isPhysicalPrimary = false; + float origPt = 0.f; + }; + TriggCandidate thisTrigg; + + std::vector triggerCandidates; + + void init(InitContext const&) + { + rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); + + zorroSummary.setObject(zorro.getZorroSummary()); + mRunNumber = -1; + + mMinMassPhi = massWindowConfigurationsPhi.peakMass - maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMaxMassPhi = massWindowConfigurationsPhi.peakMass + maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMinMassKstar = massWindowConfigurationsKstar.peakMass - maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMaxMassKstar = massWindowConfigurationsKstar.peakMass + maxMassNSigma * massWindowConfigurationsKstar.sigma; + LOGF(info, "Assoc mass pre-cut windows: Phi [%.4f, %.4f] GeV, K*0 [%.4f, %.4f] GeV", + mMinMassPhi, mMaxMassPhi, mMinMassKstar, mMaxMassKstar); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); + zorro.populateHistRegistry(histos, bc.runNumber()); + + mRunNumber = bc.runNumber(); + } + + template + bool isSelected(const Coll& collision, bool fillHist = true) + { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { + return false; + } + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); + } + + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { + return false; + } + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + } + + return true; + } + + // this function allows for all event selections to be done in a modular way + template + bool isCollisionSelected(TCollision const& collision) + { + // ________________________________________________ + // Perform basic event selection + if (!collision.sel8()) { + return false; + } + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return false; + } + if (collision.centFT0M() > eventSelections.maxCentPercent || collision.centFT0M() < eventSelections.minCentPercent) { + return false; + } + if (!collision.isInelGt0() && eventSelections.selectINELgtZERO) { + return false; + } + if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) { + return false; + } + if (zorroMask.value != "") { + auto bc = collision.template bc_as(); + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return false; + } + } + return true; + } + + // more event selections in Pb-Pb + template + bool isCollisionSelectedPbPb(TCollision const& collision) + { + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ + return false; + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) // cut time intervals with dead ITS staves + return false; + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + return false; + auto occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) /* Below min occupancy and Above max occupancy*/ + return false; + if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) // reject collisions close to Time Frame borders + return false; + if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) // reject events affected by the ITS ROF border + return false; + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) // rejects collisions which are associated with the same "found-by-T0" bunch crossing + return false; + return true; + } + + template + bool trackCut(const TrackType& track) + { + // basic track cuts + if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut + { + return false; + } + if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { + return false; + } + if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { + return false; + } + if (configTracks.cfgHasTOF && !track.hasTOF()) { + return false; + } + if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { + return false; + } + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { + return false; + } + if (configTracks.cfgPVContributor && !track.isPVContributor()) { + return false; + } + if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { + return false; + } + + return true; + } + + template + bool ptDependentPidKaon(const T& candidate) + { + auto vKaonTPCPIDpTintv = configPID.kaonTPCPIDpTintv.value; + vKaonTPCPIDpTintv.insert(vKaonTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTOFPIDpTintv = configPID.kaonTOFPIDpTintv.value; + auto vKaonTPCTOFCombinedpTintv = configPID.kaonTPCTOFCombinedpTintv.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + auto vKaonTOFPIDcuts = configPID.kaonTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vKaonTPCPIDpTintv.back(); + float tpcNsigmaKa = candidate.tpcNSigmaKa(); + float tofNsigmaKa = candidate.tofNSigmaKa(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vKaonTPCPIDpTintv.size() - 1; ++i) { + if (pt > vKaonTPCPIDpTintv[i] && pt < vKaonTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } + + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back(); + } + + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vKaonTOFPIDpTintv.size(); ++i) { + if (pt < vKaonTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaKa) < vKaonTOFPIDcuts[i] && + std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back()) { + return true; + } + } + } + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vKaonTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vKaonTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaKa * tpcNsigmaKa + + tofNsigmaKa * tofNsigmaKa; + if (combinedSigma2 < vKaonTPCTOFCombinedPIDcuts[i] * vKaonTPCTOFCombinedPIDcuts[i]) { + return true; + } + } + } + } + } + + return false; + } + + template + bool selectionPID(const T& candidate) + { + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (vKaonTPCTOFCombinedPIDcuts[0] * vKaonTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + return false; + } + + template + bool ptDependentPidPion(const T& candidate) + { + auto vPionTPCPIDpTintv = configPID.pionTPCPIDpTintv.value; + vPionTPCPIDpTintv.insert(vPionTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTOFPIDpTintv = configPID.pionTOFPIDpTintv.value; + auto vPionTPCTOFCombinedpTintv = configPID.pionTPCTOFCombinedpTintv.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + auto vPionTOFPIDcuts = configPID.pionTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vPionTPCPIDpTintv.back(); + float tpcNsigmaPi = candidate.tpcNSigmaPi(); + float tofNsigmaPi = candidate.tofNSigmaPi(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vPionTPCPIDpTintv.size() - 1; ++i) { + if (pt > vPionTPCPIDpTintv[i] && pt < vPionTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaPi) < vPionTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } + + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back(); + } + + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vPionTOFPIDpTintv.size(); ++i) { + if (pt < vPionTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaPi) < vPionTOFPIDcuts[i] && + std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back()) { + return true; + } + } + } + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vPionTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vPionTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaPi * tpcNsigmaPi + + tofNsigmaPi * tofNsigmaPi; + if (combinedSigma2 < vPionTPCTOFCombinedPIDcuts[i] * vPionTPCTOFCombinedPIDcuts[i]) { + return true; + } + } + } + } + } + + return false; + } + + template + bool selectionPIDPion(const T& candidate) + { + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (vPionTPCTOFCombinedPIDcuts[0] * vPionTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { + return true; + } + return false; + } + + // reco-level trigger quality checks (N.B.: DCA is filtered, not selected) + template + bool isValidTrigger(TTrack const& track) + { + if (track.eta() > generalSelections.triggerEtaMax || track.eta() < generalSelections.triggerEtaMin) { + return false; + } + // if (track.sign()= 1 ) {continue;} + if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { + return false; + } + if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + return false; // crossed rows + } + if (!track.hasITS() && trackSelections.triggerRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > trackSelections.triggerMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + return true; + } + + // Species tag for isValidAssocTrack()'s PID gate below. AssocPion + // and AssocKaon select which nSigma cut direction applies (accept that + // species, reject the other two); AssocHadron carries no PID logic of its + // own -- passed for clarity at Hadron call sites, but never actually reaches + // the `if constexpr (Species == AssocKaon) ... else ...` branch, since the + // outer `requires { assoc.tofSignal(); }` is already false for FullTracks + // (Hadron's track type, which has no PID columns at all). + enum AssocSpecies { AssocPion = 0, + AssocKaon = 1, + AssocHadron = 2 }; + + // Merged predicate for the Pion/Kaon/Hadron associated-track pools. Pion and + // Kaon both run over IDTracks/IDTracksMC (identical C++ type), so the two + // PID directions cannot be told apart by a `requires{}` type check the way + // Hadron (FullTracks, no PID columns) is told apart from the other two -- + // hence the explicit non-type template parameter instead. Everything except + // the PID nSigma cut direction (phase-space cuts, MC-truth lookup, table + // fills) is identical across all three species, so it stays unduplicated + // here; only the accept/reject block branches on Species. + template + bool isValidAssocTrack(TTrack const& assoc) + { + static_assert(Species == AssocPion || Species == AssocKaon || Species == AssocHadron, + "isValidAssocTrack: unknown species tag"); + if (assoc.eta() > generalSelections.assocEtaMax || assoc.eta() < generalSelections.assocEtaMin) { + return false; + } + if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { + return false; + } + if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + return false; // crossed rows + } + if (!assoc.hasITS() && trackSelections.assocRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + + // do this only if information is available + float nSigmaTPCTOF[8] = {-10, -10, -10, -10, -10, -10, -10, -10}; + if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { + if (assoc.tofSignal() > 0) { + if constexpr (Species == AssocKaon) { + if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > trackSelections.assocKaonNSigmaTPCFOF) + return false; + if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tofNSigmaPi() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + return false; + } else { + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) + return false; + if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + return false; + } + nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); + nSigmaTPCTOF[5] = assoc.tofNSigmaKa(); + nSigmaTPCTOF[6] = assoc.tofNSigmaPr(); + nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); + } else { + if constexpr (Species == AssocKaon) { + if (assoc.tpcNSigmaKa() > trackSelections.assocKaonNSigmaTPCFOF) + return false; + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + return false; + } else { + if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) + return false; + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + return false; + if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + return false; + } + } + nSigmaTPCTOF[0] = assoc.tpcNSigmaPi(); + nSigmaTPCTOF[1] = assoc.tpcNSigmaKa(); + nSigmaTPCTOF[2] = assoc.tpcNSigmaPr(); + nSigmaTPCTOF[3] = assoc.tpcNSigmaEl(); + } + + bool physicalPrimary = false; + float origPt = -1; + float code = -9999; + if constexpr (requires { assoc.mcParticle(); }) { + if (assoc.has_mcParticle()) { + auto mcParticle = assoc.mcParticle(); + physicalPrimary = mcParticle.isPhysicalPrimary(); + origPt = mcParticle.pt(); + code = mcParticle.pdgCode(); + } + } + + assocHadrons( + assoc.collisionId(), + physicalPrimary, + assoc.globalIndex(), + origPt, + code); + assocPID( + nSigmaTPCTOF[0], + nSigmaTPCTOF[1], + nSigmaTPCTOF[2], + nSigmaTPCTOF[3], + nSigmaTPCTOF[4], + nSigmaTPCTOF[5], + nSigmaTPCTOF[6], + nSigmaTPCTOF[7]); + return true; + } + + // for real data processing + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + { + triggerCandidates.clear(); + if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + double leadingPt = -1.; + int leadingId = -1; + for (auto const& track : tracks) { + if (!isValidTrigger(track)) + continue; + thisTrigg.pt = track.pt(); + thisTrigg.trackId = track.globalIndex(); + thisTrigg.collisionId = track.collisionId(); + thisTrigg.isPhysicalPrimary = false; // if you decide to check real data for primaries, you'll have a hard time + thisTrigg.origPt = 0; + triggerCandidates.push_back(thisTrigg); + if (track.pt() > leadingPt) { + leadingPt = track.pt(); + leadingId = track.globalIndex(); + } + } + for (auto const& TriggCandidate : triggerCandidates) { + bool isLeading = (leadingId == TriggCandidate.trackId); + triggerTrack( + TriggCandidate.collisionId, + TriggCandidate.isPhysicalPrimary, + TriggCandidate.trackId, + TriggCandidate.origPt, + isLeading); + triggerTrackExtra(1); + } + } + + // for MC processing + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + triggerCandidates.clear(); + if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + double leadingPt = -1.; + int leadingId = -1; + for (auto const& track : tracks) { + if (!isValidTrigger(track)) + continue; + thisTrigg.pt = track.pt(); + thisTrigg.trackId = track.globalIndex(); + thisTrigg.collisionId = track.collisionId(); + if (track.has_mcParticle()) { + auto mcParticle = track.mcParticle(); + thisTrigg.isPhysicalPrimary = mcParticle.isPhysicalPrimary(); + thisTrigg.origPt = mcParticle.pt(); + } + triggerCandidates.push_back(thisTrigg); + if (track.pt() > leadingPt) { + leadingPt = track.pt(); + leadingId = track.globalIndex(); + } + } + + for (auto const& TriggCandidate : triggerCandidates) { + bool isLeading = (leadingId == TriggCandidate.trackId); + triggerTrack( + TriggCandidate.collisionId, + TriggCandidate.isPhysicalPrimary, + TriggCandidate.trackId, + TriggCandidate.origPt, + isLeading); + triggerTrackExtra(1); + } + } + + void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + // Load parameters for sideband subtraction + auto bc = collision.bc_as(); + // Perform basic event selection + if (!collision.sel8()) { + return; + } + // No need to correlate stuff that's in far collisions + if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + return; + } + if (zorroMask.value != "") { + initCCDB(bc); + bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return; + } + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processPhis(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelected(collision)) { + return; + } + + LorentzVectorPtEtaPhiMass kplus; + LorentzVectorPtEtaPhiMass kminus; + LorentzVectorPtEtaPhiMass phi; + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (trk2.index() <= trk1.index()) { + continue; + } + + // same collision + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + // quality cuts + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + // unlike-sign only + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + // kaon PID + if (configPID.ispTdepPID) { + if (!ptDependentPidKaon(trk1) || + !ptDependentPidKaon(trk2)) { + continue; + } + } else { + if (!selectionPID(trk1) || + !selectionPID(trk2)) { + continue; + } + } + + // assign K+ and K- + if (trk1.sign() > 0) { + kplus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + } else { + kplus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + } + + phi = kplus + kminus; + + float invMass = phi.M(); + + // rapidity cut + if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + continue; + } + + // optional mass window + if (invMass < mMinMassPhi || invMass > mMaxMassPhi) { + continue; + } + + assocPhis( + collision.globalIndex(), + false, + false, + phi.Pt(), + phi.Eta(), + phi.Phi(), + invMass, + trk1.globalIndex(), + trk2.globalIndex()); + } + } + + void processPhisMC( + MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, + aod::McParticles const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelected(collision)) { + return; + } + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + // ----------------------------- + // Same cuts as phi1020analysis + // ----------------------------- + + if (trk2.index() <= trk1.index()) { + continue; + } + + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + if (!selectionPID(trk1) || !selectionPID(trk2)) { + continue; + } + + LorentzVectorPtEtaPhiMass kplus; + LorentzVectorPtEtaPhiMass kminus; + LorentzVectorPtEtaPhiMass phi; + + // assign K+ and K- + if (trk1.sign() > 0) { + kplus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + } else { + kplus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + } + + phi = kplus + kminus; + + float invMass = phi.M(); + + // rapidity cut + if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + continue; + } + + // optional mass window + if (invMass < mMinMassPhi || invMass > mMaxMassPhi) { + continue; + } + + bool mcTruePhi = false; + bool mcPhysicalPrimary = false; + + // ----------------------------- + // MC matching + // ----------------------------- + + if (trk1.has_mcParticle() && trk2.has_mcParticle()) { + + auto mc1 = trk1.mcParticle(); + auto mc2 = trk2.mcParticle(); + + if (mc1.has_mothers() && mc2.has_mothers()) { + + for (auto const& mother1 : mc1.mothers_as()) { + for (auto const& mother2 : mc2.mothers_as()) { + + if (mother1.globalIndex() != mother2.globalIndex()) { + continue; + } + + if (std::abs(mother1.pdgCode()) != 333) { + continue; + } + + mcTruePhi = true; + mcPhysicalPrimary = mother1.isPhysicalPrimary(); + } + } + } + } + + assocPhis( + collision.globalIndex(), + mcTruePhi, + mcPhysicalPrimary, + phi.Pt(), + phi.Eta(), + phi.Phi(), + invMass, + trk1.globalIndex(), + trk2.globalIndex()); + } + } + + // K*0 -> K+ pi- (and c.c.) reconstruction. Unlike phi (K+K-, symmetric daughter + // mass), the two daughter tracks are different species, so both mass hypotheses + // (trk1=kaon,trk2=pion) and (trk1=pion,trk2=kaon) are tried independently; either, + // both, or neither may pass PID for a given unlike-sign pair. + void processKstars(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelected(collision)) { + return; + } + + LorentzVectorPtEtaPhiMass kaon; + LorentzVectorPtEtaPhiMass pion; + LorentzVectorPtEtaPhiMass kstar; + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (trk2.index() <= trk1.index()) { + continue; + } + + // same collision + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + // quality cuts + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + // unlike-sign only + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + for (int hypothesis = 0; hypothesis < 2; hypothesis++) { + auto const& kaonTrack = (hypothesis == 0) ? trk1 : trk2; + auto const& pionTrack = (hypothesis == 0) ? trk2 : trk1; + + // kaon + pion PID + bool passKaonPID = configPID.ispTdepPID ? ptDependentPidKaon(kaonTrack) : selectionPID(kaonTrack); + bool passPionPID = configPID.ispTdepPID ? ptDependentPidPion(pionTrack) : selectionPIDPion(pionTrack); + if (!passKaonPID || !passPionPID) { + continue; + } + + kaon = LorentzVectorPtEtaPhiMass(kaonTrack.pt(), kaonTrack.eta(), kaonTrack.phi(), + o2::constants::physics::MassKPlus); + pion = LorentzVectorPtEtaPhiMass(pionTrack.pt(), pionTrack.eta(), pionTrack.phi(), + o2::constants::physics::MassPiPlus); + + kstar = kaon + pion; + + float invMass = kstar.M(); + + // rapidity cut + if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + continue; + } + + // mass window + if (invMass < mMinMassKstar || invMass > mMaxMassKstar) { + continue; + } + + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; + auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + + assocKstars( + collision.globalIndex(), + false, + false, + kstar.Pt(), + kstar.Eta(), + kstar.Phi(), + invMass, + posTrack.globalIndex(), + negTrack.globalIndex()); + } + } + } + + void processKstarsMC( + MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, + aod::McParticles const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelected(collision)) { + return; + } + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + // ----------------------------- + // Same cuts as processKstars + // ----------------------------- + + if (trk2.index() <= trk1.index()) { + continue; + } + + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + for (int hypothesis = 0; hypothesis < 2; hypothesis++) { + auto const& kaonTrack = (hypothesis == 0) ? trk1 : trk2; + auto const& pionTrack = (hypothesis == 0) ? trk2 : trk1; + + if (!selectionPID(kaonTrack) || !selectionPIDPion(pionTrack)) { + continue; + } + + LorentzVectorPtEtaPhiMass kaon = LorentzVectorPtEtaPhiMass(kaonTrack.pt(), kaonTrack.eta(), kaonTrack.phi(), + o2::constants::physics::MassKPlus); + LorentzVectorPtEtaPhiMass pion = LorentzVectorPtEtaPhiMass(pionTrack.pt(), pionTrack.eta(), pionTrack.phi(), + o2::constants::physics::MassPiPlus); + LorentzVectorPtEtaPhiMass kstar = kaon + pion; + + float invMass = kstar.M(); + + // rapidity cut + if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + continue; + } + + // mass window + if (invMass < mMinMassKstar || invMass > mMaxMassKstar) { + continue; + } + + bool mcTrueKstar = false; + bool mcPhysicalPrimary = false; + + // ----------------------------- + // MC matching: common mother with |pdgCode| == 313 (K*0/anti-K*0) + // ----------------------------- + + if (kaonTrack.has_mcParticle() && pionTrack.has_mcParticle()) { + + auto mcKaon = kaonTrack.mcParticle(); + auto mcPion = pionTrack.mcParticle(); + + if (mcKaon.has_mothers() && mcPion.has_mothers()) { + + for (auto const& motherKaon : mcKaon.mothers_as()) { + for (auto const& motherPion : mcPion.mothers_as()) { + + if (motherKaon.globalIndex() != motherPion.globalIndex()) { + continue; + } + + if (std::abs(motherKaon.pdgCode()) != 313) { + continue; + } + + mcTrueKstar = true; + mcPhysicalPrimary = motherKaon.isPhysicalPrimary(); + } + } + } + } + + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; + auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + + assocKstars( + collision.globalIndex(), + mcTrueKstar, + mcPhysicalPrimary, + kstar.Pt(), + kstar.Eta(), + kstar.Phi(), + invMass, + posTrack.globalIndex(), + negTrack.globalIndex()); + } + } + } + + PROCESS_SWITCH(HResonanceCorrelationFilter, processTriggers, "Produce trigger tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processTriggersMC, "Produce trigger tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocPions, "Produce associated Pion tables", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocPionsMC, "Produce associated Pion tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocKaons, "Produce associated Kaon tables", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocKaonsMC, "Produce associated Kaon tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocHadrons, "Produce associated Hadron tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocHadronsMC, "Produce associated Hadron tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processPhis, "Produce associated phi tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processPhisMC, "Produce associated phi tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processKstars, "Produce associated K*0 tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processKstarsMC, "Produce associated K*0 tables for MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 6eb48ce242f..06d226e887c 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -348,3 +348,8 @@ o2physics_add_dpl_workflow(lambdastarproxy SOURCES lambdastarproxy.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hresonancecorrelation + SOURCES hResonanceCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx new file mode 100644 index 00000000000..35f54137039 --- /dev/null +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -0,0 +1,3295 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file hResonanceCorrelation.cxx +/// \brief This task serves to do hadron-resonance correlation studies. +/// The yield will be calculated using the two-particle correlation method. +/// Trigger particle : Hadrons +/// Associated Particles : Phi, K*0 +/// this task requires the hResonanceCorrelationFilter to have been run before. +/// +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::constants::math; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +using TracksComplete = soa::Join; +using AssocPhis = soa::Join; +using AssocKstars = soa::Join; + +struct HResonanceCorrelation { + // for efficiency corrections if requested + Service ccdb; + + Service pdgDB; + o2::pwglf::ParticleCounter mCounter; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // event filtering + Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + struct : ConfigurableGroup { + std::string prefix = "masterConfigurations"; + Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; + Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; + Configurable doFullCorrelationStudy{"doFullCorrelationStudy", true, "if true, do full correlation study by creating all THnSparse histograms for the correlation function"}; + Configurable doCorrelationHadron{"doCorrelationHadron", false, "do Hadron correlation"}; + Configurable doCorrelationPhi{"doCorrelationPhi", false, "do Phi correlation"}; + Configurable doCorrelationKstar{"doCorrelationKstar", false, "do K*0 correlation"}; + Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; + Configurable doCorrelationKaon{"doCorrelationKaon", false, "do Kaon correlation"}; + Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; + Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; + Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; + Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; + Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; + Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; + Configurable doTriggPhysicalPrimary{"doTriggPhysicalPrimary", false, "require physical primary for trigger particles"}; + Configurable applyNewMCSelection{"applyNewMCSelection", false, "apply new MC Generated selection"}; + Configurable doSeparateFT0Prediction{"doSeparateFT0Prediction", false, "separate FT0M to FT0A and FT0C in prediction process"}; + Configurable useCentralityinPrediction{"useCentralityinPrediction", false, "if true, use centrality instead of multiplisity"}; + Configurable doMirroringInDelataEta{"doMirroringInDelataEta", false, "if true, fill only positive delta eta and mirror the negative side in post processing, Adjust the delta axis!"}; + Configurable fillCorrelationHistWithMass{"fillCorrelationHistWithMass", false, "if true, fill correlation histograms with particle mass"}; + } masterConfigurations; + + // master analysis switches + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; + Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; + Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; + Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; + + Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; + Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; + Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; + Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + + Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; + // used for event selections in Pb-Pb + Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; + Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; + + // Axes - configurable for smaller sizes + struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Mixing bins - multiplicity"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "#phi"}; + ConfigurableAxis axisEta{"axisEta", {80, -0.8, +0.8}, "#eta"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta #varphi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.6, 1.6}, "delta eta axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 1.0, 2.0, 3.0, 100}, "pt associated axis for histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + ConfigurableAxis axisMassNSigma{"axisMassNSigma", {40, -2, 2}, "Axis for mass Nsigma"}; + // Bounds must stay >= the filter task's own acceptance pre-cut + // (hResonanceCorrelationFilter.cxx: peakMass +/- maxMassNSigma*sigma, + // ~[0.998, 1.041] GeV for Phi and ~[0.654, 1.137] GeV for K*0 by + // default) -- anything narrower here silently underflows/overflows + // candidates the filter already let through, before fillCorrelationHistWithMass + // ever gets a chance to keep them. Bin count trimmed from the previous + // 300 (which spanned mostly-empty ranges, e.g. Phi out to 1.31 GeV) down + // to what the narrower, physically-relevant range actually needs while + // keeping comparable or finer MeV/bin resolution for the post-processing fit. + ConfigurableAxis axisPhiMass{"axisPhiMass", {100, 0.99f, 1.05f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisKstarMass{"axisKstarMass", {150, 0.65f, 1.15f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Multiplicity axis in model prediction process"}; + ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; + } axesConfigurations; + + // for topo var QA + // Per-species, peak-relative mass windows: a single shared + // `massWindowConfigurations` comparing raw assocCandidate.mass() directly + // against these bounds would break isLeftBg structurally (it requires a + // NEGATIVE mass, which raw mass never is), and sharing one window between + // Phi and K*0 leaves one species' tuned range (Phi's, ~1.0-1.04 GeV) + // entirely outside the other's candidate mass range (K*0's filter-level + // cut is 0.796-0.996 GeV), making K*0's RightBg structurally unreachable. + // + // Instead, classify using delta = mass - peakMass (a real signed quantity, + // so isLeftBg is reachable), with each species' own peak mass and its own + // effective sigma, in its own struct. maxPeakNSigma/minBgNSigma/maxBgNSigma + // are genuinely "number of sigma" (2, 3, 6 by default), multiplied by + // `sigma` (in GeV) to get the actual mass-offset bounds. + // + // `sigma` defaults here are PDG Gamma/2.355 (treating the natural + // Breit-Wigner width as if it were a Gaussian FWHM) -- a reasonable + // placeholder for K*0, where the ~47 MeV natural width likely dominates + // over detector resolution, but LIKELY AN UNDERESTIMATE for Phi, whose + // ~4.2 MeV natural width is probably smaller than your actual track + // momentum resolution. Refit both from your own reconstructed invariant + // mass peak (Voigt / BW (x) Gaussian) once you have statistics, and update + // `sigma` here -- everything else (the Nsigma multipliers) stays valid. + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsPhi"; + Configurable peakMass{"peakMass", 1.019455f, "Phi(1020) PDG mass (GeV), used as delta = mass - peakMass"}; + Configurable sigma{"sigma", 0.0018f, "effective width (GeV) for the Nsigma windows below -- PDG Gamma/2.355 placeholder, refit from your own peak"}; + Configurable maxPeakNSigma{"maxPeakNSigma", 2, "Signal region half-width, in units of sigma"}; + Configurable minBgNSigma{"minBgNSigma", 3, "Bg region edge closest to peak, in units of sigma"}; + Configurable maxBgNSigma{"maxBgNSigma", 6, "Bg region edge furthest from peak, in units of sigma"}; + } massWindowConfigurationsPhi; // allows for gap between peak and bg in case someone wants to + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsKstar"; + Configurable peakMass{"peakMass", 0.89555f, "K*0(892) PDG mass (GeV), used as delta = mass - peakMass"}; + Configurable sigma{"sigma", 0.0201f, "effective width (GeV) for the Nsigma windows below -- PDG Gamma/2.355 placeholder, refit from your own peak"}; + Configurable maxPeakNSigma{"maxPeakNSigma", 2, "Signal region half-width, in units of sigma"}; + Configurable minBgNSigma{"minBgNSigma", 3, "Bg region edge closest to peak, in units of sigma"}; + Configurable maxBgNSigma{"maxBgNSigma", 6, "Bg region edge furthest from peak, in units of sigma"}; + } massWindowConfigurationsKstar; // allows for gap between peak and bg in case someone wants to + + // Implementation of on-the-spot efficiency correction + struct : ConfigurableGroup { + std::string prefix = "efficiencyFlags"; + Configurable applyEfficiencyCorrection{"applyEfficiencyCorrection", false, "apply efficiency correction"}; + Configurable applyEfficiencyForTrigger{"applyEfficiencyForTrigger", false, "apply efficiency correction for the trigger particle"}; + Configurable applyEfficiencyPropagation{"applyEfficiencyPropagation", false, "propagate also the efficiency uncertainty"}; + Configurable applyPurityHadron{"applyPurityHadron", false, "apply the purity correction for associated hadrons"}; + Configurable applyPurityTrigger{"applyPurityTrigger", false, "apply the purity correction for trigger particle"}; + Configurable applyEffAsFunctionOfMult{"applyEffAsFunctionOfMult", false, "apply efficiency as a function of multiplicity as well"}; + Configurable applyEffAsFunctionOfMultAndPhi{"applyEffAsFunctionOfMultAndPhi", false, "apply efficiency as a function of multiplicity and phi"}; + } efficiencyFlags; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; + + // Configurables for doing subwagon systematics + struct : ConfigurableGroup { + std::string prefix = "trackSelection"; + // --- Track quality variations (single track, both trigger and assoc daughters) + Configurable minTPCNCrossedRowsTrigger{"minTPCNCrossedRowsTrigger", 70, "Minimum TPC crossed rows (trigger)"}; + Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; + Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; + Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; + Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; + Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; + Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; + + // --- Trigger: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + // --- Assoc track: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + + Configurable dcaZconstant{"dcaZconstant", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdep{"dcaZpTdep", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZconstantAssoc{"dcaZconstantAssoc", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdepAssoc{"dcaZpTdepAssoc", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + } trackSelection; + + struct : ConfigurableGroup { + std::string prefix = "checks"; + + // on the fly correction instead of mixingParameter + Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", 0, "enable an on-the-fly correction instead of using mixing"}; + } checks; + + struct ValidCollision { + struct ValidParticle { + float eta = 0.f; + float phi = 0.f; + float pt = 0.f; + int region = -1; + float efficiency = 1.f; + float efficiencyError = 0.f; + float mass = 0.f; // only meaningful for Phi/K*0 buffered candidates when + // masterConfigurations.fillCorrelationHistWithMass is + // set; unused (stays 0) for trigger particles and for + // Pion/Kaon/Hadron, which never carry a mass axis. + // no species tag stored here: the `type` argument to addValidParticle + // below (-1 = trigger, else Phi/K*0) only decides which vector this + // particle goes into; once stored, nothing reads it back. + }; + float pvz = 0.f; + float mult = 0.f; + std::vector trigParticles; + std::vector assocParticles; + void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type, float mass = 0.f) + { + ValidParticle particle{eta, phi, pt, region, efficiency, efficiencyError, mass}; + + if (type == -1) { + trigParticles.push_back(particle); + } else { + assocParticles.push_back(particle); + } + } + }; + + using ValidCollisions = std::vector>; + ValidCollisions validCollisions; + + // objects to use for efficiency corrections + TH2F* hEfficiencyTrigger = nullptr; + TH3F* hEfficiencyTriggerMult = nullptr; + THnF* hEfficiencyTriggerMultVsPhi = nullptr; + TH2F* hEfficiencyPion = nullptr; + TH2F* hEfficiencyKaon = nullptr; + TH2F* hEfficiencyPhi = nullptr; + THnF* hEfficiencyPhiMultVsPhi = nullptr; + TH2F* hEfficiencyKstar = nullptr; + THnF* hEfficiencyKstarMultVsPhi = nullptr; + TH2F* hEfficiencyHadron = nullptr; + TH3F* hEfficiencyHadronMult = nullptr; + TH1F* hPurityHadron = nullptr; + TH2F* hPurityHadronMult = nullptr; + + // objects to propagate the efficiency uncertainty + TH2F* hEfficiencyUncertaintyTrigger = nullptr; + TH3F* hEfficiencyUncertaintyTriggerMult = nullptr; + TH2F* hEfficiencyUncertaintyPion = nullptr; + TH2F* hEfficiencyUncertaintyKaon = nullptr; + TH2F* hEfficiencyUncertaintyPhi = nullptr; + TH2F* hEfficiencyUncertaintyKstar = nullptr; + TH2F* hEfficiencyUncertaintyHadron = nullptr; + TH3F* hEfficiencyUncertaintyHadronMult = nullptr; + TH1F* hPurityUncertaintyHadron = nullptr; + TH2F* hPurityUncertaintyHadronMult = nullptr; + + using BinningTypePP = ColumnBinningPolicy; + using BinningTypePbPb = ColumnBinningPolicy; + + // collision slicing for mixed events + Preslice collisionSliceTracks = aod::triggerTracks::collisionId; + // Preslice collisionSlicePions = aod::assocHadrons::collisionId; + Preslice collisionSliceHadrons = aod::assocHadrons::collisionId; + Preslice perCollision = aod::mcparticle::mcCollisionId; + Preslice collisionSlicePhis = aod::assocPhis::collisionId; + Preslice collisionSliceKstars = aod::assocKstars::collisionId; + + // Kaon sits after Pion, taking the slot Hadron used to occupy (Hadron + // moves to IndexHadron below) -- same reason Hadron itself sits outside + // AssocParticleTypesNoHadron: the generator-level MC-truth matching used by + // the closure test/prediction blocks further down is "intentionally + // restricted to Pion only" (see the NOTE comments at their static_for<...> + // calls) and was never extended to Kstar0 either, so Kaon joins Hadron + // outside that group rather than shifting Pion's/Kstar0's/Phi's indices or + // AssocParticleTypesNoHadron's boundary. + static constexpr std::string_view Particlenames[] = {"Phi", "Kstar0", "Pion", "Kaon", "Hadron"}; + static constexpr int PdgCodes[] = {333, 313, 211, 321, 0}; // Hadron has no single PDG code; 0 is a harmless placeholder + + static constexpr int IndexPhi = 0; + static constexpr int IndexKstar = 1; + static constexpr int IndexPion = 2; + static constexpr int IndexKaon = 3; + static constexpr int IndexHadron = 4; + + uint16_t doCorrelation = 0; + int mRunNumber = 0; + int mRunNumberZorro = 0; + + std::vector> axisRanges; + + static constexpr float MinRadiusTPC = 0.8; + static constexpr float MaxRadiusTPC = 2.5; + + static constexpr float Neutral = 0.0; + + static constexpr int AssocParticleTypes = 5; // Phi, Kstar0, Pion, Kaon, Hadron + static constexpr int AssocParticleTypesNoHadron = 3; // Phi, Kstar0, Pion (Kaon excluded too -- see the comment above IndexKaon) + + /// Function to aid in calculating delta-phi + /// \param phi1 first phi value + /// \param phi2 second phi value + double computeDeltaPhi(double phi1, double phi2) + { + double deltaPhi = phi1 - phi2; + double shiftedDeltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); + return shiftedDeltaPhi; + } + + /// Function to load zorro + /// \param bc provided such that the run number + timestamp can be used + void initZorro(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumberZorro == bc.runNumber()) { + return; + } + + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); + zorro.populateHistRegistry(histos, bc.runNumber()); + + mRunNumberZorro = bc.runNumber(); + } + + /// Function to load efficiencies to memory from CCDB + /// \param bc provided such that the run number can be used + void initEfficiencyFromCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + LOG(info) << "Loading efficiencies from CCDB for run " << mRunNumber << " now..."; + auto timeStamp = bc.timestamp(); + + TList* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); + + if (!listEfficiencies) { + LOG(fatal) << "Problem getting TList object with efficiencies!"; + } + + hEfficiencyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyTrigger")); + hEfficiencyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMult")); + hEfficiencyTriggerMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMultVsPhi")); + hEfficiencyPhi = static_cast(listEfficiencies->FindObject("hEfficiencyPhi")); + hEfficiencyPhiMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyPhiMultVsPhi")); + hEfficiencyKstar = static_cast(listEfficiencies->FindObject("hEfficiencyKstar")); + hEfficiencyKstarMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyKstarMultVsPhi")); + hEfficiencyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyHadron")); + hEfficiencyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyHadronMult")); + hEfficiencyPion = static_cast(listEfficiencies->FindObject("hEfficiencyPion")); + hEfficiencyKaon = static_cast(listEfficiencies->FindObject("hEfficiencyKaon")); + hPurityHadron = static_cast(listEfficiencies->FindObject("hPurityHadron")); + hPurityHadronMult = static_cast(listEfficiencies->FindObject("hPurityHadronMult")); + hEfficiencyUncertaintyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTrigger")); + hEfficiencyUncertaintyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTriggerMult")); + hEfficiencyUncertaintyPhi = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPhi")); + hEfficiencyUncertaintyKstar = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyKstar")); + hEfficiencyUncertaintyPion = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPion")); + hEfficiencyUncertaintyKaon = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyKaon")); + hEfficiencyUncertaintyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadron")); + hEfficiencyUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadronMult")); + hPurityUncertaintyHadron = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadron")); + hPurityUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadronMult")); + if (efficiencyFlags.applyEfficiencyPropagation && !efficiencyFlags.applyEffAsFunctionOfMultAndPhi && !hEfficiencyUncertaintyTrigger) + LOG(fatal) << "Problem getting hEfficiencyUncertaintyTrigger!"; + if (efficiencyFlags.applyEffAsFunctionOfMult && !hEfficiencyTriggerMult) + LOG(fatal) << "Problem getting hEfficiencyTriggerMult!"; + LOG(info) << "Efficiencies now loaded for " << mRunNumber; + } + + void init(InitContext const&) + { + zorroSummary.setObject(zorro.getZorroSummary()); + mRunNumber = 0; + mRunNumberZorro = 0; + hEfficiencyPion = 0x0; + hEfficiencyKaon = 0x0; + hEfficiencyPhi = 0x0; + hEfficiencyKstar = 0x0; + hEfficiencyUncertaintyTrigger = 0x0; + hEfficiencyUncertaintyPion = 0x0; + hEfficiencyUncertaintyKaon = 0x0; + hEfficiencyUncertaintyPhi = 0x0; + hEfficiencyUncertaintyKstar = 0x0; + + hEfficiencyHadron = 0x0; + hPurityHadron = 0x0; + hPurityUncertaintyHadron = 0x0; + hEfficiencyUncertaintyHadron = 0x0; + + // set bitmap for convenience + doCorrelation = 0; + if (masterConfigurations.doCorrelationPhi) + SETBIT(doCorrelation, IndexPhi); + if (masterConfigurations.doCorrelationKstar) + SETBIT(doCorrelation, IndexKstar); + if (masterConfigurations.doCorrelationPion) + SETBIT(doCorrelation, IndexPion); + if (masterConfigurations.doCorrelationKaon) + SETBIT(doCorrelation, IndexKaon); + if (masterConfigurations.doCorrelationHadron) + SETBIT(doCorrelation, IndexHadron); + + // Store axis ranges to prevent spurious filling + // axis status: + // --- Delta-phi is safe -> math forbids insanity + // --- Delta-eta depends on pre-filter -> check + // --- pT assoc depends on binning -> check + // --- vertex Z is safe -> skipped at evsel level + // --- multiplicity -> check + + // grab axis edge from ConfigurableAxes + const AxisSpec preAxisDeltaPhi{axesConfigurations.axisDeltaPhi, "#Delta#varphi"}; + const AxisSpec preAxisDeltaEta{axesConfigurations.axisDeltaEta, "#Delta#eta"}; + const AxisSpec preAxisPtAssoc{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{assoc} (GeV/c)"}; + const AxisSpec preAxisPtTrigger{axesConfigurations.axisPtTrigger, "#it{p}_{T}^{trigger} (GeV/c)"}; + const AxisSpec preAxisVtxZ{axesConfigurations.axisVtxZ, "vertex Z (cm)"}; + const AxisSpec preAxisMult{axesConfigurations.axisMult, "mult percentile"}; + const AxisSpec axisPtPhi{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{#phi} (GeV/c)"}; + const AxisSpec preAxisMultiplicity{axesConfigurations.axisMultiplicity, "multiplicity"}; + + // store the original axes in specific TH1Cs for completeness + histos.add("axes/hDeltaPhiAxis", "", kTH1C, {preAxisDeltaPhi}); + histos.add("axes/hDeltaEtaAxis", "", kTH1C, {preAxisDeltaEta}); + histos.add("axes/hPtAssocAxis", "", kTH1C, {preAxisPtAssoc}); + histos.add("axes/hPtTriggerAxis", "", kTH1C, {preAxisPtTrigger}); + histos.add("axes/hVertexZAxis", "", kTH1C, {preAxisVtxZ}); + histos.add("axes/hMultAxis", "", kTH1C, {preAxisMult}); + histos.add("axes/hMultiplicityAxis", "", kTH1C, {preAxisMultiplicity}); + + std::vector edgesDeltaPhiOrig = preAxisDeltaPhi.binEdges; + std::vector edgesDeltaEtaOrig = preAxisDeltaEta.binEdges; + std::vector edgesPtAssocOrig = preAxisPtAssoc.binEdges; + std::vector edgesPtTriggerOrig = preAxisPtTrigger.binEdges; + std::vector edgesVtxZOrig = preAxisVtxZ.binEdges; + std::vector edgesMultOrig = preAxisMult.binEdges; + std::vector edgesMultiplicityOrig = preAxisMultiplicity.binEdges; + + std::vector rangesDeltaPhi = {static_cast(edgesDeltaPhiOrig[0]), static_cast(edgesDeltaPhiOrig[edgesDeltaPhiOrig.size() - 1])}; + std::vector rangesDeltaEta = {static_cast(edgesDeltaEtaOrig[0]), static_cast(edgesDeltaEtaOrig[edgesDeltaEtaOrig.size() - 1])}; + std::vector rangesPtAssoc = {static_cast(edgesPtAssocOrig[0]), static_cast(edgesPtAssocOrig[edgesPtAssocOrig.size() - 1])}; + std::vector rangesPtTrigger = {static_cast(edgesPtTriggerOrig[0]), static_cast(edgesPtTriggerOrig[edgesPtTriggerOrig.size() - 1])}; + std::vector rangesVtxZ = {static_cast(edgesVtxZOrig[0]), static_cast(edgesVtxZOrig[edgesVtxZOrig.size() - 1])}; + std::vector rangesMult = {static_cast(edgesMultOrig[0]), static_cast(edgesMultOrig[edgesMultOrig.size() - 1])}; + std::vector rangesMultiplicity = {static_cast(edgesMultiplicityOrig[0]), static_cast(edgesMultiplicityOrig[edgesMultiplicityOrig.size() - 1])}; + + axisRanges.emplace_back(rangesDeltaPhi); + axisRanges.emplace_back(rangesDeltaEta); + axisRanges.emplace_back(rangesPtAssoc); + axisRanges.emplace_back(rangesPtTrigger); + axisRanges.emplace_back(rangesVtxZ); + axisRanges.emplace_back(rangesMult); + axisRanges.emplace_back(rangesMultiplicity); + + std::vector edgesDeltaPhi; + std::vector edgesDeltaEta; + std::vector edgesPtAssoc; + std::vector edgesPtTrigger; + std::vector edgesVtxZ; + std::vector edgesMult; + std::vector edgesMultiplicity; + + // v--- skipUnderOverflowInTHn ---v + // + // if enabled, this will change the axes such that they will solely cover the interval from + // edge[1] to edge[n-1]; this will mean that the bin 1 and bin N will be stored in + // under / overflow bins and will have to be manually unpacked. Do not forget to do the manual + // unpacking a posteriori! + // + // this feature is meant to save memory conveniently. + // it should actually be implemented centrally in ROOT but ok, this will do it for now. + + int offset = masterConfigurations.skipUnderOverflowInTHn ? 1 : 0; + // ===] delta-phi [=== + if (!preAxisDeltaPhi.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesDeltaPhiOrig.size()) - offset; i++) + edgesDeltaPhi.emplace_back(edgesDeltaPhiOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesDeltaPhiOrig[0]; + double delta = (edgesDeltaPhiOrig[1] - edgesDeltaPhiOrig[0]) / preAxisDeltaPhi.nBins.value(); + for (int i = offset; i < preAxisDeltaPhi.nBins.value() + 1 - offset; i++) + edgesDeltaPhi.emplace_back(min + static_cast(i) * delta); + } + // ===] delta-eta [=== + if (!preAxisDeltaEta.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesDeltaEtaOrig.size()) - offset; i++) + edgesDeltaEta.emplace_back(edgesDeltaEtaOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesDeltaEtaOrig[0]; + double delta = (edgesDeltaEtaOrig[1] - edgesDeltaEtaOrig[0]) / preAxisDeltaEta.nBins.value(); + for (int i = offset; i < preAxisDeltaEta.nBins.value() + 1 - offset; i++) + edgesDeltaEta.emplace_back(min + static_cast(i) * delta); + } + // ===] pt assoc [=== + if (!preAxisPtAssoc.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesPtAssocOrig.size()) - offset; i++) + edgesPtAssoc.emplace_back(edgesPtAssocOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesPtAssocOrig[0]; + double delta = (edgesPtAssocOrig[1] - edgesPtAssocOrig[0]) / preAxisPtAssoc.nBins.value(); + for (int i = offset; i < preAxisPtAssoc.nBins.value() + 1 - offset; i++) + edgesPtAssoc.emplace_back(min + static_cast(i) * delta); + } + // ===] pt trigger [=== + if (!preAxisPtTrigger.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesPtTriggerOrig.size()) - offset; i++) + edgesPtTrigger.emplace_back(edgesPtTriggerOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesPtTriggerOrig[0]; + double delta = (edgesPtTriggerOrig[1] - edgesPtTriggerOrig[0]) / preAxisPtTrigger.nBins.value(); + for (int i = offset; i < preAxisPtTrigger.nBins.value() + 1 - offset; i++) + edgesPtTrigger.emplace_back(min + static_cast(i) * delta); + } + // ===] vtx Z [=== + if (!preAxisVtxZ.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesVtxZOrig.size()) - offset; i++) + edgesVtxZ.emplace_back(edgesVtxZOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesVtxZOrig[0]; + double delta = (edgesVtxZOrig[1] - edgesVtxZOrig[0]) / preAxisVtxZ.nBins.value(); + for (int i = offset; i < preAxisVtxZ.nBins.value() + 1 - offset; i++) + edgesVtxZ.emplace_back(min + static_cast(i) * delta); + } + // ===] mult percentile [=== + if (!preAxisMult.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesMultOrig.size()) - offset; i++) + edgesMult.emplace_back(edgesMultOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesMultOrig[0]; + double delta = (edgesMultOrig[1] - edgesMultOrig[0]) / preAxisMult.nBins.value(); + for (int i = offset; i < preAxisMult.nBins.value() + 1 - offset; i++) + edgesMult.emplace_back(min + static_cast(i) * delta); + } + // ===] multiplicity count [=== + if (!preAxisMultiplicity.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesMultiplicityOrig.size()) - offset; i++) + edgesMultiplicity.emplace_back(edgesMultiplicityOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesMultiplicityOrig[0]; + double delta = (edgesMultiplicityOrig[1] - edgesMultiplicityOrig[0]) / preAxisMultiplicity.nBins.value(); + for (int i = offset; i < preAxisMultiplicity.nBins.value() + 1 - offset; i++) + edgesMultiplicity.emplace_back(min + static_cast(i) * delta); + } + + LOGF(info, "Initialized THnF axis delta-phi with %i bins.", edgesDeltaPhi.size() - 1); + LOGF(info, "Initialized THnF axis delta-eta with %i bins.", edgesDeltaEta.size() - 1); + LOGF(info, "Initialized THnF axis pTassoc with %i bins.", edgesPtAssoc.size() - 1); + LOGF(info, "Initialized THnF axis pTtrigger with %i bins.", edgesPtTrigger.size() - 1); + LOGF(info, "Initialized THnF axis vertex-Z with %i bins.", edgesVtxZ.size() - 1); + LOGF(info, "Initialized THnF axis mult cent with %i bins.", edgesMult.size() - 1); + LOGF(info, "Initialized THnF axis multiplicity with %i bins.", edgesMultiplicity.size() - 1); + + const AxisSpec axisDeltaPhiNDim{edgesDeltaPhi, "#Delta#varphi"}; + const AxisSpec axisDeltaEtaNDim{edgesDeltaEta, "#Delta#eta"}; + const AxisSpec axisPtAssocNDim{edgesPtAssoc, "#it{p}_{T}^{assoc} (GeV/c)"}; + const AxisSpec axisPtTriggerNDim{edgesPtTrigger, "#it{p}_{T}^{trigger} (GeV/c)"}; + const AxisSpec axisVtxZNDim{edgesVtxZ, "vertex Z (cm)"}; + const AxisSpec axisMultNDim{edgesMult, "mult percentile"}; + const AxisSpec axisMultiplicityNDim{edgesMultiplicity, "Multiplicity"}; + + if (doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstarsInBuffer) { + validCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); + for (size_t i = 0; i < validCollisions.size(); ++i) { + validCollisions[i].reserve(masterConfigurations.mixingParameter); + } + } + if (!masterConfigurations.doPPAnalysis) { + // event selections in Pb-Pb + histos.add("hEventSelection", "hEventSelection", kTH1F, {{10, 0, 10}}); + TString eventSelLabel[] = {"all", "sel8", "kIsTriggerTVX", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup "}; + for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); + } + } + + // ======================================================================== + // SHARED QA HISTOGRAMS + // ======================================================================== + bool anySameEvent = doprocessSameEventHPhis || doprocessSameEventHKstars || doprocessSameEventHPions || doprocessSameEventHKaons || doprocessSameEventHHadrons; + bool anyMixedEvent = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer || doprocessMixedEventHPions || doprocessMixedEventHKaons || doprocessMixedEventHHadrons; + + if (doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { + if (anySameEvent) + histos.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); + if (anyMixedEvent) { + histos.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); + histos.add("MixingQA/hMEpvz1", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + histos.add("MixingQA/hMEpvz2", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + histos.add("MixingQA/hMixingQA", "mixing QA", kTH1F, {{2, -0.5, 1.5}}); + } + histos.add("EventQA/hMult", "Multiplicity", kTH1F, {axesConfigurations.axisMult}); + histos.add("EventQA/hPvz", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + } + + if (anySameEvent) { + histos.add("hTriggerAllSelectedEtaVsPt", "hTriggerAllSelectedEtaVsPt", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hTriggerPtResolution", ";p_{T}^{reconstructed} (GeV/c); p_{T}^{generated} (GeV/c)", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisPtQA}); + histos.add("hTrackEtaVsPtVsPhi", "hTrackEtaVsPtVsPhi", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add("hTriggerPrimaryEtaVsPt", "hTriggerPrimaryEtaVsPt", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add("hTriggerPrimaryEtaVsPt", "hTriggerPrimaryEtaVsPt", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + } + + // ======================================================================== + // MAIN CORRELATION LOOP (SE & ME) + // ======================================================================== + bool needsSignalBgCloneSE = false; + bool needsSignalBgCloneME = false; + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + + bool doSE = false; + bool doME = false; + if (i == IndexPhi) { // Phi + doSE = doprocessSameEventHPhis; + doME = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer; + } else if (i == IndexKstar) { // K*0 + doSE = doprocessSameEventHKstars; + doME = doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer; + } else if (i == IndexPion) { // Pion + doSE = doprocessSameEventHPions; + doME = doprocessMixedEventHPions; + } else if (i == IndexKaon) { // Kaon + doSE = doprocessSameEventHKaons; + doME = doprocessMixedEventHKaons; + } else { // Hadron + doSE = doprocessSameEventHHadrons; + doME = doprocessMixedEventHHadrons; + } + + // Kinematic QA (Only for Phi) + if (i < IndexPion && (doSE || doME || doprocessPrediction || doprocessClosureTest)) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add(fmt::format("h{}EtaVsPtVsPhi", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add(fmt::format("h{}EtaVsPtVsPhiBg", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } else { + histos.add(fmt::format("h{}EtaVsPtVsPhiVsCent", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + histos.add(fmt::format("h{}EtaVsPtVsPhiVsCentBg", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + histos.add(fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); + histos.add(fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); + + // REMOVED (2026-09-24): hITSClusters{Phi,Kstar0}{Positive,Negative}Daughter{Toward,Transverse}. + // These were registered but never filled anywhere in this file (confirmed by + // exhaustive grep for any fill using these names). The V0/cascade analog in + // hStrangeCorrelation.cxx fills the equivalent histograms with the DAUGHTER's + // ITS cluster count vs the V0's DECAY RADIUS (assoc.v0radius()), split into + // "Toward"/"Transverse" via a delta-phi cut using checks.towardDeltaEtaRange / + // checks.transwerseDeltaEtaRangeMin/Max -- but neither of those two things has + // an equivalent here: Phi/K*0 candidates are reconstructed at the primary + // vertex from track pairs, not displaced V0 decays, so there is no "decay + // radius" analog on AssocPhis/AssocKstars, and the towardDeltaEtaRange / + // transwerseDeltaEtaRangeMin/Max configurables that drive the region split + // don't exist in this task's `checks` struct at all (only doOnTheFlyFlattening + // does). Implementing this would mean inventing both a physical observable for + // the third axis and the region-cut configurables, so it's removed rather than + // guessed. If you want it back, tell me what the third axis should represent + // for a non-displaced daughter pair (e.g. a DCA quantity, or drop to a TH2), + // and I'll add the config fields and fill it properly. + } + + // Core Correlation THnFs + if (masterConfigurations.doFullCorrelationStudy) { + if (doSE) { + if (masterConfigurations.fillCorrelationHistWithMass && (i == IndexPhi || i == IndexKstar)) { + // Mass is ADDED as its own axis here, not swapped in for + // Delta-eta: the mixed-event acceptance correction needs + // Delta-eta (pair acceptance isn't flat in it), so it has to + // stay present even when mass is also kept for post-processing + // signal/sideband extraction. 7 axes total; see + // fillCorrelationsPhi/fillCorrelationsKstar's binFillThnMass + // for the matching fill order. + histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, (i == IndexPhi ? axesConfigurations.axisPhiMass : axesConfigurations.axisKstarMass), axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (doDeltaPhiStarCheck) + histos.add(fmt::format("sameEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); + if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { + needsSignalBgCloneSE = true; + } + } + + if (doME) { + if (masterConfigurations.fillCorrelationHistWithMass && (i == IndexPhi || i == IndexKstar)) { + // Same 7-axis layout as the sameEvent/Signal booking above -- + // must match, since CorrelationBuilder-style same/mixed + // acceptance correction requires identical axis definitions on + // both sides. + histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, (i == IndexPhi ? axesConfigurations.axisPhiMass : axesConfigurations.axisKstarMass), axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (doDeltaPhiStarCheck) + histos.add(fmt::format("mixedEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); + if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { + needsSignalBgCloneME = true; + } + } + } + } + } + // Clone Signal/ into LeftBg/ and RightBg/ once, after all species for this + // loop have booked their Signal/ histograms (Phi and/or K*0 use sideband + // subtraction; avoids cloning the same prefix twice, which the histogram + // registry does not allow). + if (needsSignalBgCloneSE) { + histos.addClone("sameEvent/Signal/", "sameEvent/LeftBg/"); + histos.addClone("sameEvent/Signal/", "sameEvent/RightBg/"); + } + if (needsSignalBgCloneME) { + histos.addClone("mixedEvent/Signal/", "mixedEvent/LeftBg/"); + histos.addClone("mixedEvent/Signal/", "mixedEvent/RightBg/"); + } + + // ======================================================================== + // TRIGGER AND ASSOC SPECIFIC QA + // ======================================================================== + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis && masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesPhi", "TriggersPhi", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add("hNumberOfRejectedPairsPhi", "hNumberOfRejectedPairsPhi", kTH1F, {{1, 0, 1}}); + } + + if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars && masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesKstar0", "TriggersKstar0", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add("hNumberOfRejectedPairsKstar0", "hNumberOfRejectedPairsKstar0", kTH1F, {{1, 0, 1}}); + } + + if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesPion", "TriggersPion", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsPion", "hNumberOfRejectedPairsPion", kTH1F, {{1, 0, 1}}); + histos.add("hPionEtaVsPtAllSelected", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPositivePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hNegativePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesKaon", "TriggersKaon", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsKaon", "hNumberOfRejectedPairsKaon", kTH1F, {{1, 0, 1}}); + histos.add("hKaonEtaVsPtAllSelected", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPositiveKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hNegativeKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesHadron", "TriggersHadron", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsHadron", "hNumberOfRejectedPairsHadron", kTH1F, {{1, 0, 1}}); + histos.add("hDCAzTriggerHadron", "hDCAzTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyTriggerHadron", "hDCAxyTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAzAssociatedHadron", "hDCAzAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyAssociatedHadron", "hDCAxyAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hAsssocTrackEtaVsPtVsPhi", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("hAssocPrimaryEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hAssocHadronsAllSelectedEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hAssocPtResolution", ";p_{T}^{reconstructed} (GeV/c); p_{T}^{generated} (GeV/c)", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisPtQA}); + } + + LOGF(info, "Init THnFs done"); + + // ======================================================================== + // MC GENERATED, CLOSURE TEST, AND PREDICTION + // ======================================================================== + if (doprocessMCGenerated) { + histos.add("hGeneratedQAPtTrigger", "hGeneratedQAPtTrigger", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hGeneratedQAPtAssociatedPhi", "hGeneratedQAPtAssociatedPhi", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hClosureTestEventCounter", "hClosureTestEventCounter", kTH1F, {{10, 0, 10}}); + + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add("Generated/hTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add("Generated/hTrigger", "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + histos.add("Generated/hPositiveTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("Generated/hNegativeTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { // Fixed missing check here + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add(fmt::format("Generated/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add(fmt::format("Generated/h{}", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + if (i == IndexPion) { + histos.add(fmt::format("Generated/hPositive{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add(fmt::format("Generated/hNegative{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + } + } + histos.addClone("Generated/", "GeneratedWithPV/"); + + for (int i = 0; i < AssocParticleTypesNoHadron; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("GeneratedWithPV/h{}_MidYVsMult", Particlenames[i]).c_str(), "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add(fmt::format("GeneratedWithPV/h{}_MidYVsMult_TwoPVsOrMore", Particlenames[i]).c_str(), "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + } + } + + if (doprocessClosureTest) { + if (!doprocessMCGenerated) + histos.add("hClosureTestEventCounter", "hClosureTestEventCounter", kTH1F, {{10, 0, 10}}); + histos.add("hClosureQAPtTrigger", "hClosureQAPtTrigger", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hClosureQAPtAssociatedPhi", "hClosureQAPtAssociatedPhi", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add(fmt::format("ClosureTest/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } + } + histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (doprocessPrediction) { + mCounter.mPdgDatabase = pdgDB.service; + mCounter.mSelectPrimaries = doAssocPhysicalPrimary.value; + histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); + TString eventSelLabel[] = {"Read", "INELgt0", "|Z|<10"}; + for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { + histos.get(HIST("Prediction/hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); + } + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta08", "Nch in 0.8 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta05", "Nch in 0.5 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta08", "Nch in 0.8 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta05", "Nch in 0.5 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta08", "Nch in 0.8 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta05", "Nch in 0.5 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("Prediction/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (masterConfigurations.useCentralityinPrediction) { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultiplicityNDim}); + } + } + } + if (masterConfigurations.doSeparateFT0Prediction) { + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0A/"); + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0C/"); + } + } + + // visual inspection of sizes + histos.print(); + + // initialize CCDB *only* if efficiency correction requested + // skip if not requested, saves a bit of time + if (efficiencyFlags.applyEfficiencyCorrection) { + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + } + } + + // this function allows for all event selections to be done in a modular way + template + bool isisCollisionSelect(TCollision const& collision) + { + // ________________________________________________ + // Perform basic event selection + if (!collision.sel8()) { + return false; + } + if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { + return false; + } + if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { + return false; + } + if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { + return false; + } + if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { + return false; + } + if (zorroMask.value != "") { + auto bc = collision.template bc_as(); + initZorro(bc); + bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return false; + } + } + return true; + } + + // event selections in Pb-Pb + template + bool isisCollisionSelectPbPb(TCollision const& collision, bool fillHists) + { + if (fillHists) + histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + + // Perform basic event selection + if (!collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); + + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2.5 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + + if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); + + if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { + // cut time intervals with dead ITS staves + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4.5 /* collisions after cut time intervals with dead ITS staves*/); + + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) + return false; + if (fillHists) + histos.fill(HIST("hEventSelection"), 6.5 /* Below min occupancy and Above max occupancy*/); + + /* + if (collision.alias_bit(kTVXinTRD)) { + // TRD triggered + return false; + } + */ + + if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + // reject collisions close to Time Frame borders + // O2-4623 + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7.5 /* reject collisions close to Time Frame borders*/); + + if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + // reject events affected by the ITS ROF border + // O2-4309 + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8.5 /* reject events affected by the ITS ROF border*/); + + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + return true; + } + + template + bool isValidTrigger(TTrack const& track, bool isLeading) + { + if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { + return false; // crossed rows + } + if (!track.hasITS() && trackSelection.triggerRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > trackSelection.triggerMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.triggerRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + // systematic variations: trigger DCAxy + if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstant + trackSelection.dcaXYpTdep * std::abs(track.signed1Pt())) { + return false; + } + // systematic variations: trigger DCAz + if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstant + trackSelection.dcaZpTdep * std::abs(track.signed1Pt())) { + return false; + } + if (track.pt() > axisRanges[3][1] || track.pt() < axisRanges[3][0]) { + return false; + } + if (triggerParticleCharge > 0 && track.sign() < 0) { + return false; + } + if (triggerParticleCharge < 0 && track.sign() > 0) { + return false; + } + if (useTheLeadingParticleAsTrigger && !isLeading) { + return false; + } + return true; + } + template + bool isValidAssocHadron(TTrack const& track) + { + if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + return false; // crossed rows + } + if (!track.hasITS() && trackSelection.assocRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > trackSelection.assocMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.assocRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + // systematic variations: trigger DCAxy + if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstantAssoc + trackSelection.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { + return false; + } + // systematic variations: trigger DCAz + if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstantAssoc + trackSelection.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { + return false; + } + if (track.pt() > axisRanges[2][1] || track.pt() < axisRanges[2][0]) { + return false; + } + return true; + } + + double calculateAverageDeltaPhiStar(const double* trigg, const double* assoc, double B) + { + double dPhiStar = 0; + double dPhiStarMean = 0; + + double dPhi = assoc[0] - trigg[0]; + double phaseProton = (-0.3 * B * assoc[2]) / (2 * assoc[1]); + double phaseTrack = (-0.3 * B * trigg[2]) / (2 * trigg[1]); + + constexpr double kRadiusStep = 0.05; + int nSteps = static_cast((MaxRadiusTPC - MinRadiusTPC) / kRadiusStep); + for (int iR = 0; iR <= nSteps; ++iR) { + double r = MinRadiusTPC + iR * kRadiusStep; + dPhiStar = dPhi + std::asin(phaseProton * r) - std::asin(phaseTrack * r); + dPhiStarMean += (dPhiStar / 34); + } + + return dPhiStarMean; + } + void fillTriggerHistogram(std::shared_ptr const& hist, double pt, double mult, float eff, float effUncert, float purity, float purityErr) + { + int binx = hist->GetXaxis()->FindBin(pt); + int biny = hist->GetYaxis()->FindBin(mult); + float previousContent = hist->GetBinContent(binx, biny); + float previousUncert = hist->GetBinError(binx, biny); + float newContent = previousContent + purity / eff; + float newUncert = std::sqrt(previousUncert * previousUncert + std::pow(purity / eff, 2) + std::pow(purityErr / eff, 2) + std::pow(effUncert, 2) / std::pow(eff, 4)); + hist->SetBinContent(binx, biny, newContent); + hist->SetBinError(binx, biny, newUncert); + } + void fillCorrelationHistogram(std::shared_ptr const& hist, double binFillThn[], float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) + { + float previousContent, previousError2, currentContent, currentError2; + int bin = hist->GetBin(binFillThn); + previousContent = hist->GetBinContent(bin); + previousError2 = hist->GetBinError2(bin); + currentContent = previousContent + etaWeight * purity / (efficiency); + currentError2 = previousError2 + std::pow(etaWeight * purity / (efficiency), 2) + std::pow(etaWeight * totalPurityUncert / (efficiency), 2) + std::pow(totalEffUncert * purity * etaWeight, 2) / std::pow(efficiency, 4); + hist->SetBinContent(bin, currentContent); + hist->SetBinError2(bin, currentError2); + } + + // Species is an explicit compile-time tag (IndexPion, IndexKaon or + // IndexHadron) rather than inferred from column presence on THadrons: + // Pion's and Kaon's assoc tables are both Join + // (AssocPID carries all four species' nSigmas generically), so a + // `requires { assocTrack.nSigmaTPCPi(); }` check can no longer tell them + // apart the way it could tell Pion apart from the plain (no-PID) Hadron + // table. Only Hadron's THadrons type lacks PID columns at all. + template + void fillCorrelationsHadron(TTriggers const& triggers, THadrons const& assocs, bool mixing, float pvz, float mult, double bField) + { + static_assert(Species == IndexPion || Species == IndexKaon || Species == IndexHadron, + "fillCorrelationsHadron: Species must be IndexPion, IndexKaon or IndexHadron"); + + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.template track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigger = 1.0f; + float efficiencyTriggerError = 0.0f; + float purityTrigger = 1.0f; + float purityTriggerError = 0.0f; + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyTrigger = hEfficiencyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); + else + efficiencyTrigger = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityTrigger = hPurityHadronMult->Interpolate(trigg.pt(), mult); + else + purityTrigger = hPurityHadron->Interpolate(trigg.pt()); + } + if (efficiencyFlags.applyEfficiencyPropagation) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyTriggerError = hEfficiencyUncertaintyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); + else + efficiencyTriggerError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityTriggerError = hPurityUncertaintyHadronMult->Interpolate(trigg.pt(), mult); + else + purityTriggerError = hPurityUncertaintyHadron->Interpolate(trigg.pt()); + } + } + if (efficiencyTrigger == 0) { // check for zero efficiency, do not apply if the case + efficiencyTrigger = 1; + efficiencyTriggerError = 0; + } + } + if (!mixing) { + if constexpr (Species == IndexPion) + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesPion")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + else if constexpr (Species == IndexKaon) + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesKaon")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + else + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesHadron")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + } + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + for (auto const& assocTrack : assocs) { + auto assoc = assocTrack.template track_as(); + + //---] removing autocorrelations [--- + if (doAutocorrelationRejection) { + if (trigg.globalIndex() == assoc.globalIndex()) { + if constexpr (Species == IndexPion) + histos.fill(HIST("hNumberOfRejectedPairsPion"), 0.5); + else if constexpr (Species == IndexKaon) + histos.fill(HIST("hNumberOfRejectedPairsKaon"), 0.5); + else + histos.fill(HIST("hNumberOfRejectedPairsHadron"), 0.5); + continue; + } + } + //---] track quality check [--- + if (!isValidAssocHadron(assoc)) + continue; + if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + continue; + } + float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); + float deltaeta = trigg.eta() - assoc.eta(); + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = assoc.pt(); + float pttrigger = trigg.pt(); + + double assocSign = assoc.sign(); + const double assocForDeltaPhiStar[] = {assoc.phi(), assoc.pt(), assocSign}; + + float etaWeight = 1.; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float efficiency = 1; + float purity = 1.0f; + float purityUncertainty = 0.0f; + float totalEffUncert = 0.0; + float efficiencyUncertainty = 0.0f; + float totalPurityUncert = 0.0; + if (efficiencyFlags.applyEfficiencyCorrection) { + if constexpr (Species == IndexPion) { + efficiency = hEfficiencyPion->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyEfficiencyPropagation) + efficiencyUncertainty = hEfficiencyUncertaintyPion->Interpolate(ptassoc, assoc.eta()); + } else if constexpr (Species == IndexKaon) { + efficiency = hEfficiencyKaon->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyEfficiencyPropagation) + efficiencyUncertainty = hEfficiencyUncertaintyKaon->Interpolate(ptassoc, assoc.eta()); + } else { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiency = hEfficiencyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); + else + efficiency = hEfficiencyHadron->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyPurityHadron) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purity = hPurityHadronMult->Interpolate(ptassoc, mult); + else + purity = hPurityHadron->Interpolate(ptassoc); + } + if (efficiencyFlags.applyEfficiencyPropagation) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyUncertainty = hEfficiencyUncertaintyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); + else + efficiencyUncertainty = hEfficiencyUncertaintyHadron->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyPurityHadron) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityUncertainty = hPurityUncertaintyHadronMult->Interpolate(ptassoc, mult); + else + purityUncertainty = hPurityUncertaintyHadron->Interpolate(ptassoc); + } + } + } + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + efficiencyUncertainty = 0.0; + } + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigger * efficiencyUncertainty, 2) + std::pow(efficiencyTriggerError * efficiency, 2)); + totalPurityUncert = std::sqrt(std::pow(purityTrigger * purityUncertainty, 2) + std::pow(purity * purityTriggerError, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); + if (!mixing) { + if constexpr (Species == IndexPion) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Pion")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + if (triggSign == assocSign && doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + } else if constexpr (Species == IndexKaon) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kaon")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + if (triggSign == assocSign && doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + } else { + if (triggSign == assocSign && doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Hadron")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } + } else { + if constexpr (Species == IndexPion) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Pion")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } else if constexpr (Species == IndexKaon) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kaon")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Hadron")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } + } + } + } + } + + // ---- OPTIMIZED (2026-09-24): daughter-track resolution, efficiency lookup and + // mass-window classification hoisted out of the trigger loop; see comment block + // originally delivered in fillCorrelationsPhi_Kstar_optimized.cxx for rationale ---- + void fillCorrelationsPhi(aod::TriggerTracks const& triggers, aod::AssocPhis const& assocs, bool mixing, bool mixingInBf, float pvz, float mult, double bField) + { + ValidCollision currentCollision; + int binMult = 0; + int nBinsMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = pvz; + currentCollision.mult = mult; + if (mixingInBf) { + nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); + binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; + nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); + binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; + } + + // --------------------------------------------------------------------- + // Pre-resolve everything that does NOT depend on the trigger, once per + // assoc candidate instead of once per (trigger, assoc) pair. + // --------------------------------------------------------------------- + struct CachedPhi { + float phi, eta, pt, mass; + float phiKplus, etaKplus, ptKplus, signKplus; + float phiKminus, etaKminus, ptKminus, signKminus; + int32_t globalIndexPos, globalIndexNeg; + float efficiency, efficiencyError; + bool isLeftBg, isSignal, isRightBg; + bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + }; + std::vector cache; + cache.reserve(assocs.size()); + for (auto const& assocCandidate : assocs) { + auto postrack = assocCandidate.posTrackDaughter_as(); + auto negtrack = assocCandidate.negTrackDaughter_as(); + + CachedPhi c; + c.phi = assocCandidate.phi(); + c.eta = assocCandidate.eta(); + c.pt = assocCandidate.pt(); + c.mass = assocCandidate.mass(); + + c.phiKplus = postrack.phi(); + c.etaKplus = postrack.eta(); + c.ptKplus = postrack.pt(); + c.signKplus = postrack.sign(); + c.phiKminus = negtrack.phi(); + c.etaKminus = negtrack.eta(); + c.ptKminus = negtrack.pt(); + c.signKminus = negtrack.sign(); + c.globalIndexPos = postrack.globalIndex(); + c.globalIndexNeg = negtrack.globalIndex(); + + c.efficiency = 1.0f; + c.efficiencyError = 0.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + c.efficiency = hEfficiencyPhi->Interpolate(c.pt, c.eta); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyUncertaintyPhi->Interpolate(c.pt, c.eta); + } else { + double bin[4] = {c.pt, c.eta, c.phi, mult}; + c.efficiency = hEfficiencyPhiMultVsPhi->GetBinContent(hEfficiencyPhiMultVsPhi->GetBin(bin)); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyPhiMultVsPhi->GetBinError(hEfficiencyPhiMultVsPhi->GetBin(bin)); + } + } + + // Peak-relative classification using Phi's own mass window. + float delta = c.mass - massWindowConfigurationsPhi.peakMass; + float sig = massWindowConfigurationsPhi.sigma; + c.isLeftBg = (-massWindowConfigurationsPhi.maxBgNSigma * sig < delta && delta < -massWindowConfigurationsPhi.minBgNSigma * sig); + c.isSignal = (-massWindowConfigurationsPhi.maxPeakNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxPeakNSigma * sig); + c.isRightBg = (massWindowConfigurationsPhi.minBgNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxBgNSigma * sig); + + c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTruePhi()) && + (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + + // Candidate-level kinematic/spectrum QA, ported from the equivalent + // block in hStrangeCorrelation.cxx (h3dSpectrum[Y], + // hEtaVsPtVsPhi[Bg]). These are registered (histos.add(...) + // above) but only filled here. Filled once per candidate per + // SAME EVENT only (never during mixing -- these are candidate QA, not + // correlation-pair QA, so filling them again per mixed pair would + // overcount each candidate by the mixing-pool depth). + // "SpectrumY" mirrors the V0 analog's rapidity cut (std::abs(rapidity) < + // ySel); AssocPhis has no exposed rapidity() accessor in this file, so + // pseudorapidity (c.eta) is used as a stand-in -- swap in a real + // rapidity() call here if/when the table exposes one. + if (!mixing) { + float nSigmaVal = delta / sig; + histos.fill(HIST("h3dPhiSpectrum"), c.pt, mult, nSigmaVal); + if (std::abs(c.eta) < ySel) { + histos.fill(HIST("h3dPhiSpectrumY"), c.pt, mult, nSigmaVal); + } + // matches the two registration branches above (TH3F vs THnF-with-mult, + // selected by efficiencyFlags.applyEffAsFunctionOfMultAndPhi) + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + if (c.isSignal) { + histos.fill(HIST("hPhiEtaVsPtVsPhi"), c.pt, c.eta, c.phi); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hPhiEtaVsPtVsPhiBg"), c.pt, c.eta, c.phi); + } + } else { + if (c.isSignal) { + histos.fill(HIST("hPhiEtaVsPtVsPhiVsCent"), c.pt, c.eta, c.phi, mult); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hPhiEtaVsPtVsPhiVsCentBg"), c.pt, c.eta, c.phi, mult); + } + } + } + + cache.push_back(c); + } + // --------------------------------------------------------------------- + + bool firstLoop = false; + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigg = 1.0f; + float efficiencyTriggError = 0.0f; + float purityTrigg = 1.0f; + float purityTriggErr = 0.0; + double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTriggErr = hPurityHadron->Interpolate(trigg.pt()); + } + } else { + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + } + } + if (efficiencyTrigg == 0) { + efficiencyTrigg = 1; + efficiencyTriggError = 0; + } + } + + if (!mixing) { + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesPhi")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } + + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + + if (mixingInBf) { + currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); + if (firstLoop) + continue; + } + + for (auto const& c : cache) { + firstLoop = true; + + //---] removing autocorrelations [--- + if (doAutocorrelationRejection) { + if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { + histos.fill(HIST("hNumberOfRejectedPairsPhi"), 0.5); + continue; + } + } + + float deltaphi = computeDeltaPhi(trigg.phi(), c.phi); + float deltaeta = trigg.eta() - c.eta; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = c.pt; + float pttrigger = trigg.pt(); + float massassoc = c.mass; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float etaWeight = 1; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + float totalEffUncert = 0.0f; + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * c.efficiencyError, 2) + std::pow(efficiencyTriggError * c.efficiency, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + // 7-slot layout matching the mass-mode Signal booking above: mass + // ADDED after Delta-eta, everything else shifted one slot right. + double binFillThnMass[7] = {deltaphi, deltaeta, massassoc, ptassoc, pttrigger, pvz, mult}; + + const double assocForDeltaPhiStarPlus[] = {c.phiKplus, c.ptKplus, c.signKplus}; + const double assocForDeltaPhiStarMinus[] = {c.phiKminus, c.ptKminus, c.signKminus}; + + if (!c.passesMcSelection) + continue; + + if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + + // Mixed Event Logic buffered in ValidCollision. LeftBg/RightBg are + // only booked when !fillCorrelationHistWithMass (see booking above), + // so both are skipped entirely in mass mode -- filling/replaying into + // an unbooked histogram would abort. Signal carries a real mass + // value through the buffer (ValidParticle::mass) so the replay loop + // below can build the correct 7-slot fill array in mass mode too. + if (mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 0, c.efficiency, c.efficiencyError, 0 /* 0 = Phi type */); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Phi")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 1, c.efficiency, c.efficiencyError, 0, c.mass); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 2, c.efficiency, c.efficiencyError, 0); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Phi")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + } + } + + // Process Buffer for Mixed Events -- unchanged from original. + if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + + int binnumb = binMult * nBinsVtxZ + binVtxZ; + int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; + if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || + (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + return; + + for (const auto& collision : validCollisions[binnumb]) { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + + for (const auto& trigger : collision.trigParticles) { + for (const auto& assoc : currentCollision.assocParticles) { + float deltaeta = trigger.eta - assoc.eta; + float deltaphi = computeDeltaPhi(trigger.phi, assoc.phi); + float efficiencyTrigg = trigger.efficiency; + float efficiencyAssoc = assoc.efficiency; + float efficiencyTriggError = trigger.efficiencyError; + float efficiencyAssocError = assoc.efficiencyError; + float totalEffUncert = 0.0; + float ptassoc = assoc.pt; + float pttrigger = trigger.pt; + + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); + } + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double binFillThnMass[7] = {deltaphi, deltaeta, assoc.mass, ptassoc, pttrigger, pvz, mult}; + + // region 0/2 (LeftBg/RightBg) are never buffered in mass mode (see + // the buffering guard above), so no separate guard is needed here + // beyond region itself never being 0/2 in that case; region 1 + // (Signal) picks the 7-slot mass array when mass mode is on, to + // match the booking above. + if (assoc.region == 0) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Phi")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 1) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 2) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Phi")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + } + } + } + + if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { + validCollisions[binnumb].erase(validCollisions[binnumb].begin()); + } + if (!currentCollision.trigParticles.empty()) + validCollisions[binnumb].push_back(currentCollision); + } + + // ============================================================================ + // fillCorrelationsKstar -- identical restructuring, mirrors fillCorrelationsPhi + // exactly (K*0 has no separate LeftBg/Signal/RightBg physics difference from + // Phi in this file -- same three-region sideband pattern, same daughter-pair + // caching opportunity). mcTruePhi() -> mcTrueKstar() is the only selection + // difference, and hEfficiencyPhi* -> hEfficiencyKstar* for the efficiency + // histograms. + // ============================================================================ + + void fillCorrelationsKstar(aod::TriggerTracks const& triggers, aod::AssocKstars const& assocs, bool mixing, bool mixingInBf, float pvz, float mult, double bField) + { + ValidCollision currentCollision; + int binMult = 0; + int nBinsMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = pvz; + currentCollision.mult = mult; + if (mixingInBf) { + nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); + binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; + nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); + binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; + } + + // --------------------------------------------------------------------- + // Same pre-pass as fillCorrelationsPhi -- this matters most for K*0, + // since assocKstars typically has far more candidates per event than + // assocPhis, so the N_trig-fold redundant daughter-track resolution is + // multiplied by a much bigger candidate count here than for Phi. + // --------------------------------------------------------------------- + struct CachedKstar { + float phi, eta, pt, mass; + float phiPos, etaPos, ptPos, signPos; + float phiNeg, etaNeg, ptNeg, signNeg; + int32_t globalIndexPos, globalIndexNeg; + float efficiency, efficiencyError; + bool isLeftBg, isSignal, isRightBg; + bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + }; + std::vector cache; + cache.reserve(assocs.size()); + for (auto const& assocCandidate : assocs) { + auto postrack = assocCandidate.posTrackDaughter_as(); + auto negtrack = assocCandidate.negTrackDaughter_as(); + + CachedKstar c; + c.phi = assocCandidate.phi(); + c.eta = assocCandidate.eta(); + c.pt = assocCandidate.pt(); + c.mass = assocCandidate.mass(); + + c.phiPos = postrack.phi(); + c.etaPos = postrack.eta(); + c.ptPos = postrack.pt(); + c.signPos = postrack.sign(); + c.phiNeg = negtrack.phi(); + c.etaNeg = negtrack.eta(); + c.ptNeg = negtrack.pt(); + c.signNeg = negtrack.sign(); + c.globalIndexPos = postrack.globalIndex(); + c.globalIndexNeg = negtrack.globalIndex(); + + c.efficiency = 1.0f; + c.efficiencyError = 0.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + c.efficiency = hEfficiencyKstar->Interpolate(c.pt, c.eta); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyUncertaintyKstar->Interpolate(c.pt, c.eta); + } else { + double bin[4] = {c.pt, c.eta, c.phi, mult}; + c.efficiency = hEfficiencyKstarMultVsPhi->GetBinContent(hEfficiencyKstarMultVsPhi->GetBin(bin)); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyKstarMultVsPhi->GetBinError(hEfficiencyKstarMultVsPhi->GetBin(bin)); + } + } + + // Peak-relative classification using K*0's own mass window. + float delta = c.mass - massWindowConfigurationsKstar.peakMass; + float sig = massWindowConfigurationsKstar.sigma; + c.isLeftBg = (-massWindowConfigurationsKstar.maxBgNSigma * sig < delta && delta < -massWindowConfigurationsKstar.minBgNSigma * sig); + c.isSignal = (-massWindowConfigurationsKstar.maxPeakNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxPeakNSigma * sig); + c.isRightBg = (massWindowConfigurationsKstar.minBgNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxBgNSigma * sig); + + c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTrueKstar()) && + (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + + // Same candidate-level Spectrum/EtaVsPtVsPhi QA as fillCorrelationsPhi + // above -- see the comment there for rationale. + if (!mixing) { + float nSigmaVal = delta / sig; + histos.fill(HIST("h3dKstar0Spectrum"), c.pt, mult, nSigmaVal); + if (std::abs(c.eta) < ySel) { + histos.fill(HIST("h3dKstar0SpectrumY"), c.pt, mult, nSigmaVal); + } + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + if (c.isSignal) { + histos.fill(HIST("hKstar0EtaVsPtVsPhi"), c.pt, c.eta, c.phi); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiBg"), c.pt, c.eta, c.phi); + } + } else { + if (c.isSignal) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiVsCent"), c.pt, c.eta, c.phi, mult); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiVsCentBg"), c.pt, c.eta, c.phi, mult); + } + } + } + + cache.push_back(c); + } + // --------------------------------------------------------------------- + + bool firstLoop = false; + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigg = 1.0f; + float efficiencyTriggError = 0.0f; + float purityTrigg = 1.0f; + float purityTriggErr = 0.0; + double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTriggErr = hPurityHadron->Interpolate(trigg.pt()); + } + } else { + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + } + } + if (efficiencyTrigg == 0) { + efficiencyTrigg = 1; + efficiencyTriggError = 0; + } + } + + if (!mixing) { + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesKstar0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } + + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + + if (mixingInBf) { + currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); + if (firstLoop) + continue; + } + + for (auto const& c : cache) { + firstLoop = true; + + //---] removing autocorrelations [--- + if (doAutocorrelationRejection) { + if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { + histos.fill(HIST("hNumberOfRejectedPairsKstar0"), 0.5); + continue; + } + } + + float deltaphi = computeDeltaPhi(trigg.phi(), c.phi); + float deltaeta = trigg.eta() - c.eta; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = c.pt; + float pttrigger = trigg.pt(); + float massassoc = c.mass; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float etaWeight = 1; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + float totalEffUncert = 0.0f; + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * c.efficiencyError, 2) + std::pow(efficiencyTriggError * c.efficiency, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + // 7-slot layout matching the mass-mode Signal booking above: mass + // ADDED after Delta-eta, everything else shifted one slot right. + double binFillThnMass[7] = {deltaphi, deltaeta, massassoc, ptassoc, pttrigger, pvz, mult}; + + const double assocForDeltaPhiStarPlus[] = {c.phiPos, c.ptPos, c.signPos}; + const double assocForDeltaPhiStarMinus[] = {c.phiNeg, c.ptNeg, c.signNeg}; + + if (!c.passesMcSelection) + continue; + + if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + + // Mixed-event logic: buffered into ValidCollision when mixing via the + // collision pool (processMixedEventHKstarsInBuffer), filled directly + // otherwise (processMixedEventHKstars). LeftBg/RightBg are only + // booked when !fillCorrelationHistWithMass (see booking above), so + // both are skipped entirely in mass mode -- filling/replaying into an + // unbooked histogram would abort. Signal carries a real mass value + // through the buffer (ValidParticle::mass) so the replay loop below + // can build the correct 7-slot fill array in mass mode too. + if (mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 0, c.efficiency, c.efficiencyError, 1 /* 1 = Kstar0 type */); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Kstar0")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 1, c.efficiency, c.efficiencyError, 1, c.mass); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 2, c.efficiency, c.efficiencyError, 1); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Kstar0")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + } + } + + // Process buffer for mixed events (mirrors fillCorrelationsPhi; reached + // when called from processMixedEventHKstarsInBuffer with mixingInBf=true) + if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + + int binnumb = binMult * nBinsVtxZ + binVtxZ; + int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; + if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || + (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + return; + + for (const auto& collision : validCollisions[binnumb]) { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + + for (const auto& trigger : collision.trigParticles) { + for (const auto& assoc : currentCollision.assocParticles) { + float deltaeta = trigger.eta - assoc.eta; + float deltaphi = computeDeltaPhi(trigger.phi, assoc.phi); + float efficiencyTrigg = trigger.efficiency; + float efficiencyAssoc = assoc.efficiency; + float efficiencyTriggError = trigger.efficiencyError; + float efficiencyAssocError = assoc.efficiencyError; + float totalEffUncert = 0.0; + float ptassoc = assoc.pt; + float pttrigger = trigger.pt; + + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); + } + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double binFillThnMass[7] = {deltaphi, deltaeta, assoc.mass, ptassoc, pttrigger, pvz, mult}; + + // region 0/2 (LeftBg/RightBg) are never buffered in mass mode (see + // the buffering guard above); region 1 (Signal) picks the 7-slot + // mass array when mass mode is on, to match the booking above. + if (assoc.region == 0) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Kstar0")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 1) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 2) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Kstar0")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + } + } + } + + if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { + validCollisions[binnumb].erase(validCollisions[binnumb].begin()); + } + if (!currentCollision.trigParticles.empty()) + validCollisions[binnumb].push_back(currentCollision); + } + + double getMagneticField(uint64_t timestamp) + { + static parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + + return 0.1 * (grpo->getNominalL3Field()); // 1 T = 10 kG + } + // if this process function is enabled, it will be such that only events with trigger particles within a given + // trigger pt bin are taken for the entire processing. This allows for the calculation of e.g. efficiencies + // within an event class that has a trigger (which may differ with respect to other cases, to be checked) + + // for map determining which trigger bins are present and which aren't + std::vector triggerPresenceMap; + + void processSelectEventWithTrigger(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // setup + triggerPresenceMap.clear(); + triggerPresenceMap.resize(collisions.size(), 0); + + for (auto const& collision : collisions) { + // ________________________________________________ + // Perform basic event selection + if (!isisCollisionSelect(collision)) { + continue; + } + + // do not forget to re-group ... + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + + for (auto const& triggerTrack : slicedTriggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + auto binNumber = histos.get(HIST("axes/hPtTriggerAxis"))->FindFixBin(track.pt()) - 1; + SETBIT(triggerPresenceMap[collision.globalIndex()], binNumber); + } + } + } + + void processSameEventHHadrons(soa::Join::iterator const& collision, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection + if (!isisCollisionSelect(collision)) { + return; + } + // ________________________________________________ + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons && doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + + // Do basic QA + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hDCAzTriggerHadron"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxyTriggerHadron"), track.dcaXY(), track.pt()); + float efficiency = 1.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + efficiency = hEfficiencyTrigger->Interpolate(track.pt(), track.eta()); + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + } + float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi(), weight); + } + } + for (auto const& assocTrack : assocHadrons) { + auto assoc = assocTrack.track_as(); + if (!isValidAssocHadron(assoc)) + continue; + float efficiency = 1.0f; + float purity = 1.0f; + histos.fill(HIST("hDCAzAssociatedHadron"), assoc.dcaZ(), assoc.pt()); + histos.fill(HIST("hDCAxyAssociatedHadron"), assoc.dcaXY(), assoc.pt()); + if (efficiencyFlags.applyEfficiencyCorrection) { + efficiency = hEfficiencyHadron->Interpolate(assoc.pt(), assoc.eta()); + if (efficiencyFlags.applyPurityHadron) + purity = hPurityHadron->Interpolate(assoc.pt()); + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + } + float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; + histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); + histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); + if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); + histos.fill(HIST("hAsssocTrackEtaVsPtVsPhi"), assoc.pt(), assoc.eta(), assoc.phi(), weight); + } + + // ________________________________________________ + // Do hadron - hadron correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, assocHadrons, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection + if (!isisCollisionSelect(collision)) { + return; + } + // ________________________________________________ + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + // Do basic QA + for (auto const& pion : associatedPions) { + auto pionTrack = pion.track_as(); + if (!isValidAssocHadron(pionTrack)) + continue; + + histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) + continue; + if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) + continue; + histos.fill(HIST("hPionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + if (pionTrack.sign() > 0) + histos.fill(HIST("hPositivePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + else + histos.fill(HIST("hNegativePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + } + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + } + + // ________________________________________________ + // Do hadron - Pion correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, associatedPions, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHKaons(soa::Join::iterator const& collision, soa::Join const& associatedKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection + if (!isisCollisionSelect(collision)) { + return; + } + // ________________________________________________ + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + // Do basic QA + for (auto const& kaon : associatedKaons) { + auto kaonTrack = kaon.track_as(); + if (!isValidAssocHadron(kaonTrack)) + continue; + + histos.fill(HIST("hKaonEtaVsPtAllSelected"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + if (doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) + continue; + if (masterConfigurations.doMCassociation && std::abs(kaon.pdgCode()) != PdgCodes[IndexKaon]) + continue; + histos.fill(HIST("hKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + if (kaonTrack.sign() > 0) + histos.fill(HIST("hPositiveKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + else + histos.fill(HIST("hNegativeKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + } + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + } + + // ________________________________________________ + // Do hadron - Kaon correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, associatedKaons, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHPhis(soa::Join::iterator const& collision, aod::AssocPhis const& associatedPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + + // Skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (!isisCollisionSelect(collision)) { + return; + } + + if (doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + + // ----------------------------- + // Trigger QA + // ----------------------------- + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { + continue; + } + + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + + // ----------------------------- + // h-phi correlation + // ----------------------------- + fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHKstars(soa::Join::iterator const& collision, aod::AssocKstars const& associatedKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + + // Skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (!isisCollisionSelect(collision)) { + return; + } + + if (doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + + // ----------------------------- + // Trigger QA + // ----------------------------- + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { + continue; + } + + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + + // ----------------------------- + // h-K*0 correlation + // ----------------------------- + fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + } + + void + processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + return; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocHadrons = assocHadrons.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - hadron correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocHadrons, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHPions(soa::Join const& collisions, soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + continue; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocPions = assocPions.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - phi correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocPions, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHKaons(soa::Join const& collisions, soa::Join const& assocKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + continue; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocKaons = assocKaons.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - Kaon correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocKaons, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHPhis(soa::Join const& collisions, aod::AssocPhis const& assocPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + continue; + } + + if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + continue; + } + + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + + auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision2.globalIndex()); + + if (masterConfigurations.doFullCorrelationStudy) { + // Use the per-collision slices computed above, not the full unsliced + // triggerTracks/assocPhis tables -- passing the whole dataframe here + // would mix every trigger with every candidate in the TF regardless + // of which two collisions are actually being paired. + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, false, collision1.posZ(), collision1.centFT0M(), bField); + } + } + } + + void processMixedEventHKstars(soa::Join const& collisions, aod::AssocKstars const& assocKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + continue; + } + + if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + continue; + } + + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + + auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision2.globalIndex()); + + if (masterConfigurations.doFullCorrelationStudy) { + // Same reasoning as processMixedEventHPhis above -- use this collision + // pair's own slices, not the full unsliced triggerTracks/assocKstars. + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, false, collision1.posZ(), collision1.centFT0M(), bField); + } + } + } + + void processMCGenerated(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) + { + histos.fill(HIST("hClosureTestEventCounter"), 2.5f); + + //--------------------------------------------- + // Generated particles before event selection + //--------------------------------------------- + + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.eta()) > etaSel) + continue; + + float pt = mcParticle.pt(); + + // trigger particles + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hGeneratedQAPtTrigger"), pt, 0.0f); + } + } + + // generated phi + + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + + // if (!doAssocPhysicalPrimaryInGen || + // mcParticle.isPhysicalPrimary()) { + + histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 0.0f); + + //} + } + } + + for (auto const& mcParticle : mcParticles) { + + // phi + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("Generated/hPhi"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), 1.0); + } else { + histos.fill(HIST("Generated/hPhi"), mcParticle.pt(), mcParticle.eta(), 1.0); + } + } + + if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + continue; + // NOTE: intentionally restricted to Pion only (IndexPion..IndexPion); Kstar0 is + // not extended to processMCGenerated/ClosureTest/Prediction in this version. + static_for([&](auto i) { + constexpr int Index = i.value; + if (i == IndexPion && mcParticle.pdgCode() > Neutral) { + histos.fill(HIST("Generated/hPositive") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } else if (i == IndexPion && mcParticle.pdgCode() < Neutral) { + histos.fill(HIST("Generated/hNegative") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } else if (mcParticle.pdgCode() == PdgCodes[i]) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), 1); + } else { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } + } + }); + } + + //--------------------------------------------- + // Require reconstructed collision + //--------------------------------------------- + + if (collisions.size() < 1) + return; + + int biggestNContribs = -1; + float bestCollisionVtxZ = 0.f; + float bestCollisionFT0Mpercentile = -1.f; + float bestCollisionFT0Cpercentile = -1.f; + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + bool isCollisionSelect = false; + uint32_t bestCollisionTriggerPresenceMap = 0; + + for (auto const& collision : collisions) { + + if (collision.numContrib() <= biggestNContribs) + continue; + + biggestNContribs = collision.numContrib(); + + bestCollisionFT0Mpercentile = collision.centFT0M(); + bestCollisionFT0Cpercentile = collision.centFT0C(); + + if (masterConfigurations.applyNewMCSelection) { + isCollisionSelect = (masterConfigurations.doPPAnalysis && isisCollisionSelect(collision)) || (!masterConfigurations.doPPAnalysis && isisCollisionSelectPbPb(collision, false)); + } else { + bestCollisionSel8 = collision.sel8(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionINELgtZERO = collision.isInelGt0(); + } + + if (triggerPresenceMap.size() > 0) + bestCollisionTriggerPresenceMap = triggerPresenceMap[collision.globalIndex()]; + } + + if (collisions.size() > 1) { + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.y()) > ySel) + continue; + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); + } + + if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + continue; + + // NOTE: Pion only, see note above; Kstar0 not extended here. + static_for([&](auto i) { + constexpr int Index = i.value; + + if (std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i])) + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); + }); + } + } + + //--------------------------------------------- + // Collision selection + //--------------------------------------------- + + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) + return; + + if (masterConfigurations.applyNewMCSelection) { + + if (!isCollisionSelect) + return; + + } else { + + if (!bestCollisionSel8) + return; + + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) + return; + + if (!bestCollisionINELgtZERO) + return; + } + + histos.fill(HIST("hClosureTestEventCounter"), 3.5f); + + //--------------------------------------------- + // Generated particles after event selection + //--------------------------------------------- + + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.eta()) > etaSel) + continue; + + float pt = mcParticle.pt(); + + // trigger QA + + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hGeneratedQAPtTrigger"), pt, 1.0f); + } + } + + // generated phi + + if (std::abs(mcParticle.pdgCode()) != Pdg::kPhi) + continue; + + // if (doAssocPhysicalPrimaryInGen && + // !mcParticle.isPhysicalPrimary()) + // continue; + + histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 1.0f); + } + + for (auto const& mcParticle : mcParticles) { + + double eta = mcParticle.eta(); + double pt = mcParticle.pt(); + + // generated phi + + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + + histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), mcParticle.phi(), bestCollisionFT0Cpercentile); + + } else { + histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), bestCollisionFT0Mpercentile); + } + if (std::abs(mcParticle.y()) < ySel) + histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); + } + + if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("GeneratedWithPV/hTrigger"), pt, eta, mcParticle.phi(), bestCollisionFT0Cpercentile); + } else { + histos.fill(HIST("GeneratedWithPV/hTrigger"), pt, eta, bestCollisionFT0Mpercentile); + } + if (mcParticle.pdgCode() > 0) + histos.fill(HIST("GeneratedWithPV/hPositiveTrigger"), pt, eta, bestCollisionFT0Mpercentile); + else + histos.fill(HIST("GeneratedWithPV/hNegativeTrigger"), pt, eta, bestCollisionFT0Mpercentile); + } + + // NOTE: Pion only, see note above; Kstar0 not extended here. + static_for([&](auto i) { + constexpr int Index = i.value; + if (i == IndexPion && std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i]) && mcParticle.pdgCode() > Neutral) { + histos.fill(HIST("GeneratedWithPV/hPositive") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), bestCollisionFT0Mpercentile); + } else if (i == IndexPion && std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i]) && mcParticle.pdgCode() < Neutral) { + histos.fill(HIST("GeneratedWithPV/hNegative") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), bestCollisionFT0Mpercentile); + } + + if (std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i])) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, mcParticle.phi(), bestCollisionFT0Cpercentile); + } else { + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, bestCollisionFT0Mpercentile); + } + if (std::abs(mcParticle.y()) < ySel) + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); + } + }); + } + } + + void processClosureTest(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& recCollisions, aod::McParticles const& mcParticles) + { + + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector phiIndices; + std::vector kstarIndices; // intentionally left unpopulated: Kstar0 is not + // extended to this process function in this version + + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 0.0f); // step 1: no event selection whatsoever + } + } + + // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 0.0f); // step 1: no event selection whatsoever + } + // } + } + + histos.fill(HIST("hClosureTestEventCounter"), 0.5f); + + int bestCollisionFT0Mpercentile = -1; + float bestCollisionVtxZ = 0.0f; + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + int biggestNContribs = -1; + uint32_t bestCollisionTriggerPresenceMap = 0; + + for (auto const& recCollision : recCollisions) { + if (biggestNContribs < recCollision.numContrib()) { + biggestNContribs = recCollision.numContrib(); + bestCollisionFT0Mpercentile = recCollision.centFT0M(); + bestCollisionSel8 = recCollision.sel8(); + bestCollisionVtxZ = recCollision.posZ(); + bestCollisionINELgtZERO = recCollision.isInelGt0(); + if (triggerPresenceMap.size() > 0) + bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + } + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + return; + } + + if (masterConfigurations.doGenEventSelection) { + if (!bestCollisionSel8) + return; + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) + return; + if (!bestCollisionINELgtZERO) + return; + if (bestCollisionFT0Mpercentile > axisRanges[5][1] || bestCollisionFT0Mpercentile < axisRanges[5][0]) { + return; + } + } + + histos.fill(HIST("hClosureTestEventCounter"), 1.5f); + + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 1.0f); // step 2: after event selection + } + } + + // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 1.0f); // step 2: after event selection + } + // } + } + + int iteratorNum = -1; + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); + } + if (masterConfigurations.doCorrelationHadron) { + if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); + } + } + } + if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); + } + } + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + phiIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hPhi"), gpt, geta, gphi); + } + } + + associatedIndices.emplace_back(phiIndices); // IndexPhi = 0 + associatedIndices.emplace_back(kstarIndices); // IndexKstar = 1 (always empty here) + associatedIndices.emplace_back(piIndices); // IndexPion = 2 + associatedIndices.emplace_back(assocHadronIndices); // Hadron = 3 + + for (std::size_t iTrigger = 0; iTrigger < triggerIndices.size(); iTrigger++) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndices[iTrigger]); + // check range of trigger particle + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + double getatrigger = triggerParticle.eta(); + double gphitrigger = triggerParticle.phi(); + double pttrigger = triggerParticle.pt(); + + auto mothers = triggerParticle.mothers_as(); + auto globalIndex = (mothers.size() > 0) ? triggerParticle.mothers_first_as().globalIndex() : -1; + + static_for<0, 3>([&](auto i) { // associated loop (Kstar0 slot is always empty, see above) + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + if (triggerIndices[iTrigger] != associatedIndices[Index][iassoc] && globalIndex != assocParticle.globalIndex()) { // avoid self + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaphi = computeDeltaPhi(gphitrigger, gphiassoc); + double deltaeta = getatrigger - getaassoc; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + if (TESTBIT(doCorrelation, i)) + histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionFT0Mpercentile); + } + } + }); + } + } + + void processMixedEventHPhisInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocPhis const& assocPhis, TracksComplete const&, aod::BCsWithTimestamps const&) + { + for (auto const& collision : collisions) { + + auto bc = collision.bc_as(); + auto dBz = getMagneticField(bc.timestamp()); + + // Slice down to this collision's own triggers/candidates before + // buffering into ValidCollision -- without this, every collision would + // buffer the entire dataframe's triggers/candidates instead of just + // its own, defeating the point of per-collision buffering. + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision.globalIndex()); + + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + } + } + + void processMixedEventHKstarsInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocKstars const& assocKstars, TracksComplete const&, aod::BCsWithTimestamps const&) + { + for (auto const& collision : collisions) { + + auto bc = collision.bc_as(); + auto dBz = getMagneticField(bc.timestamp()); + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision.globalIndex()); + + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + } + } + + void processPrediction(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector phiIndices; + std::vector kstarIndices; // intentionally left unpopulated: Kstar0 is not + // extended to this process function in this version + float centMultFT0M = -1; + float centMultFT0A = -1; + float centMultFT0C = -1; + float multFT0M = -1; + float multFT0A = -1; + float multFT0C = -1; + float multEta08 = -1; + float multEta05 = -1; + histos.fill(HIST("Prediction/hEventSelection"), 0.5); + if (masterConfigurations.selectINELgtZERO && !o2::pwglf::isINELgt0mc(mcParticles, pdgDB)) { + return; + } + histos.fill(HIST("Prediction/hEventSelection"), 1.5); + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("Prediction/hEventSelection"), 2.5); + if (masterConfigurations.useCentralityinPrediction) { + centMultFT0M = mcCollision.centFT0M(); + centMultFT0A = mcCollision.centFT0A(); + centMultFT0C = mcCollision.centFT0C(); + } else { + multFT0M = mCounter.countFT0A(mcParticles) + mCounter.countFT0C(mcParticles); + multFT0A = mCounter.countFT0A(mcParticles); + multFT0C = mCounter.countFT0C(mcParticles); + multEta08 = mCounter.countEta08(mcParticles); + multEta05 = mCounter.countEta05(mcParticles); + } + if (!masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hFT0AvsNchEta08"), multFT0A, multEta08); + histos.fill(HIST("Prediction/hFT0AvsNchEta05"), multFT0A, multEta05); + histos.fill(HIST("Prediction/hFT0CvsNchEta08"), multFT0C, multEta08); + histos.fill(HIST("Prediction/hFT0CvsNchEta05"), multFT0C, multEta05); + } + histos.fill(HIST("Prediction/hFT0MvsNchEta08"), multFT0M, multEta08); + histos.fill(HIST("Prediction/hFT0MvsNchEta05"), multFT0M, multEta05); + } + int iteratorNum = -1; + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, centMultFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, centMultFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, centMultFT0M); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, multFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, multFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, multFT0M); + } + } + if (masterConfigurations.doCorrelationHadron) { + if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); + } + } + } + if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); + } + } + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + phiIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hPhi"), gpt, geta, gphi); + } + } + + associatedIndices.emplace_back(phiIndices); // IndexPhi = 0 + associatedIndices.emplace_back(kstarIndices); // IndexKstar = 1 (always empty here) + associatedIndices.emplace_back(piIndices); // IndexPion = 2 + associatedIndices.emplace_back(assocHadronIndices); // Hadron = 3 + for (std::size_t iTrigger = 0; iTrigger < triggerIndices.size(); iTrigger++) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndices[iTrigger]); + // check range of trigger particle + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + double getatrigger = triggerParticle.eta(); + double gphitrigger = triggerParticle.phi(); + double pttrigger = triggerParticle.pt(); + auto mothers = triggerParticle.mothers_as(); + auto globalIndex = (mothers.size() > 0) ? triggerParticle.mothers_first_as().globalIndex() : -1; + static_for<0, 3>([&](auto i) { // associated loop (Kstar0 slot is always empty, see above) + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + if (triggerIndices[iTrigger] != associatedIndices[Index][iassoc] && globalIndex != assocParticle.globalIndex()) { // avoid self + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaphi = computeDeltaPhi(gphitrigger, gphiassoc); + double deltaeta = getatrigger - getaassoc; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + if (TESTBIT(doCorrelation, i)) { + if (masterConfigurations.useCentralityinPrediction) { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0C); + } + } else { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0C); + } + } + } + } + } + }); + } + } + + PROCESS_SWITCH(HResonanceCorrelation, processSelectEventWithTrigger, "Select events with trigger only", true); + + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHPions, "Process same events, h-Pion", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHKaons, "Process same events, h-Kaon", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHHadrons, "Process same events, h-h", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHPhis, "Process same events, h-phi", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHKstars, "Process same events, h-K*0", true); + + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPions, "Process mixed events, h-Pion", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKaons, "Process mixed events, h-Kaon", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHHadrons, "Process mixed events, h-h", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPhis, "Process mixed events, h-phi", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPhisInBuffer, "Process mixed-event, h-phi using collision buffer", false); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKstars, "Process mixed events, h-K*0", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKstarsInBuffer, "Process mixed-event, h-K*0 using collision buffer", false); + + PROCESS_SWITCH(HResonanceCorrelation, processMCGenerated, "Process MC generated", false); + PROCESS_SWITCH(HResonanceCorrelation, processClosureTest, "Process Closure Test", false); + PROCESS_SWITCH(HResonanceCorrelation, processPrediction, "process model prediction", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From c98eb61caac09e50e7fafc9e26f3b14a46a0a83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Sat, 26 Sep 2026 08:33:21 +0200 Subject: [PATCH 080/648] [PWGHF] Refactor ML configuration with new cut parameters (#18055) --- .../producerCharmHadronsCharmFemtoDream.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx index a0d8e189ae2..6ac7c687352 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -17,6 +17,7 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/HfMlResponseD0ToKPi.h" #include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" +#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/Utils/utilsBfieldCCDB.h" @@ -54,6 +55,7 @@ using namespace o2; using namespace o2::framework; +using namespace o2::analysis; using namespace o2::framework::expressions; struct HfProducerCharmHadronsCharmFemtoDream { @@ -68,12 +70,11 @@ struct HfProducerCharmHadronsCharmFemtoDream { // Each species needs its own model, feature order and pT-dependent cuts. struct HfMlConfig : ConfigurableGroup { std::string prefix; - static inline const std::array defaultCuts{1., 0., 0.}; Configurable mlApplicationMode{"mlApplicationMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; - Configurable> binsPtMl{"binsPtMl", std::vector{0., 36.}, "pT bin limits for new BDT"}; - Configurable> cutsMl{"cutsMl", {defaultCuts.data(), 1, 3}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; - Configurable> cutDirMl{"cutDirMl", std::vector{0, 1, 1}, "Reject scores above (0), below (1), or do not cut (2)"}; - Configurable nClassesMl{"nClassesMl", 3, "Three output classes: background, prompt, nonprompt"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for new BDT"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsDmesCutScore}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{"nClassesMl", int{hf_cuts_ml::NCutScores}, "Three output classes: background, prompt, nonprompt"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; From 87c9c233eebf750705e118bb36304acc38ab8a40 Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Sat, 26 Sep 2026 03:59:23 -0500 Subject: [PATCH 081/648] [PWGHF] Fix KFParticle reconstruction in Xic0/Omegac0 candidate creator (#18047) --- PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx index 89185af0de5..4f1c007d20a 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx @@ -880,7 +880,7 @@ struct HfCandidateCreatorXic0Omegac0 { KFParticle kfV0MassConstrained = kfV0; kfV0MassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassLambda); // set mass constrain to Lambda if (kfUseV0MassConstraint) { - KFParticle const kfV0 = kfV0MassConstrained; + kfV0 = kfV0MassConstrained; } kfV0.TransportToDecayVertex(); @@ -921,7 +921,7 @@ struct HfCandidateCreatorXic0Omegac0 { kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to OmegaMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested - KFParticle const kfOmega = kfOmegaMassConstrained; + kfOmega = kfOmegaMassConstrained; } registry.fill(HIST("hInvMassOmegaMinus"), massCasc); kfOmega.TransportToDecayVertex(); @@ -931,7 +931,7 @@ struct HfCandidateCreatorXic0Omegac0 { //*>~<* step 3 : reconstruc Omegac0 with KF // Create KF charm bach Pion from track KFPTrack const kfTrackBachPion = createKFPTrackFromTrack(trackCharmBachelor); - KFParticle const kfBachPion(kfTrackBachPion, kPiPlus); + KFParticle const kfBachPion(kfTrackBachPion, bachCharge < 0 ? kPiPlus : kPiMinus); const KFParticle* omegaC0Daugthers[2] = {&kfBachPion, &kfOmega}; // construct OmegaC0 @@ -1414,7 +1414,7 @@ struct HfCandidateCreatorXic0Omegac0 { kfXiMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassXiMinus); // set mass constrain to XiMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested - KFParticle const kfXi = kfXiMassConstrained; + kfXi = kfXiMassConstrained; } registry.fill(HIST("hInvMassXiMinus"), massCasc); kfXi.TransportToDecayVertex(); @@ -1848,7 +1848,7 @@ struct HfCandidateCreatorXic0Omegac0 { } registry.fill(HIST("hInvMassXiMinus_rej"), massCascrej); // rej: Add competing rejection to minimize misidentified Xi impact. Reject if kfBachPionRej is Pion and the constructed cascade has Xi's invariant mass. KFParticle kfOmegaMassConstrained = kfOmega; - kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to XiMinus + kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to OmegaMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested kfOmega = kfOmegaMassConstrained; From ebc24712a60457d269de2e1c0fa6553b350269be Mon Sep 17 00:00:00 2001 From: Rashi gupta <167059733+rashigupt@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:42:12 +0530 Subject: [PATCH 082/648] [PWGHF] Add separate event fractions for electron and hadron event mixing (#18053) Co-authored-by: rashi.gupta@cern.ch --- .../TableProducer/correlatorHfeHadrons.cxx | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx index 43468843ebf..b832a897cbf 100644 --- a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx @@ -16,7 +16,6 @@ #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFL/DataModel/ElectronSelectionTable.h" -#include "PWGJE/DataModel/EMCALClusters.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" @@ -92,7 +91,8 @@ struct HfCorrelatorHfeHadrons { // Electron hadron correlation condition Configurable ptCondition{"ptCondition", true, "Electron pT should be greater than associate particle pT"}; - Configurable eventFractionToAnalyze{"eventFractionToAnalyze", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + Configurable electronEventFraction{"electronEventFraction", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + Configurable hadronEventFraction{"hadronEventFraction", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; TRandom3 rnd{0}; @@ -212,41 +212,45 @@ struct HfCorrelatorHfeHadrons { } // Electron-hadron Correlation - template - void fillCorrelation(CollisionType const& collision, ElectronType const& electrons, TracksType const& tracks, EmcClusterType const& emcClusters, BcType const&, McParticlesType const&) + template + void fillCorrelation(CollisionType const& collision, ElectronType const& electrons, TracksType const& tracks, BcType const&, McParticlesType const&) { if (!(isRun3 ? collision.sel8() : (collision.sel7() && collision.alias_bit(kINT7)))) { return; } - // skip events with no clusters - if (emcClusters.size() == 0 && skipNoEmcClusters) { - return; - } int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); auto bc = collision.template bc_as(); int gCollisionId = collision.globalIndex(); int64_t timeStamp = bc.timestamp(); - bool skipEventTableFilling = false; - if (eventFractionToAnalyze > 0) { - if (rnd.Uniform(0, 1) > eventFractionToAnalyze) { - skipEventTableFilling = true; + bool skipEventElectronTableFilling = false; + bool skipEventHadronTableFilling = false; + if (electronEventFraction > 0) { + if (rnd.Uniform(0, 1) > electronEventFraction) { + skipEventElectronTableFilling = true; } } - registry.fill(HIST("hNevents"), 1); + if (hadronEventFraction > 0) { + if (rnd.Uniform(0, 1) > hadronEventFraction) { + skipEventHadronTableFilling = true; + } + } + registry.fill(HIST("hZvertex"), collision.posZ()); // fraction of event which used for Event mixing - if (!skipEventTableFilling) { + if (!skipEventElectronTableFilling) { registry.fill(HIST("hTracksBin"), poolBin); - registry.fill(HIST("hZvertex"), collision.posZ()); + } + if (!skipEventHadronTableFilling) { + registry.fill(HIST("hNevents"), 1); } for (const auto& hTrack : tracks) { if (!selAssoHadron(hTrack)) { continue; } - if (!skipEventTableFilling) { + if (!skipEventHadronTableFilling) { registry.fill(HIST("hptHadron"), hTrack.pt()); entryHadron(hTrack.phi(), hTrack.eta(), hTrack.pt(), poolBin, gCollisionId, timeStamp); @@ -340,7 +344,7 @@ struct HfCorrelatorHfeHadrons { } } - if (!skipEventTableFilling) { + if (!skipEventElectronTableFilling) { registry.fill(HIST("hElectronBin"), poolBin); entryElectron(phiElectron, etaElectron, ptElectron, nElectronLS, nElectronUS, poolBin, gCollisionId, timeStamp); } @@ -452,10 +456,10 @@ struct HfCorrelatorHfeHadrons { void processData(TableCollision const& collision, aod::HfCorrSelEl const& electrons, - TableTracks const& tracks, aod::EMCALClusters const& emcClusters, + TableTracks const& tracks, aod::BCsWithTimestamps const& bcs) { - fillCorrelation(collision, electrons, tracks, emcClusters, bcs, 0); + fillCorrelation(collision, electrons, tracks, bcs, 0); } PROCESS_SWITCH(HfCorrelatorHfeHadrons, processData, "Process for Data", false); @@ -464,10 +468,10 @@ struct HfCorrelatorHfeHadrons { void processMcRec(McTableCollision const& mcCollision, aod::HfCorrSelEl const& mcElectrons, - McTableTracks const& mcTracks, aod::EMCALClusters const& emcClusters, + McTableTracks const& mcTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticle) { - fillCorrelation(mcCollision, mcElectrons, mcTracks, emcClusters, bcs, mcParticle); + fillCorrelation(mcCollision, mcElectrons, mcTracks, bcs, mcParticle); } PROCESS_SWITCH(HfCorrelatorHfeHadrons, processMcRec, "Process MC Reco mode", true); From f4355176bf5d6d08fe834369db7fbdecc7fb8983 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:25:49 +0800 Subject: [PATCH 083/648] [PWGLF] Added histograms to store k* information(analysis k* star cut influence on spin correlation for like-sign pairs) (#18066) --- .../Strangeness/lambdaspincorrderived.cxx | 953 ++++++------------ 1 file changed, 284 insertions(+), 669 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 2f75bd83291..408b6be942e 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +59,61 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; +static inline double getKStar( + const ROOT::Math::PtEtaPhiMVector& p1, + const ROOT::Math::PtEtaPhiMVector& p2) +{ + const auto pair = p1 + p2; + + ROOT::Math::Boost boostToPairCM(pair.BoostToCM()); + const auto p1CM = boostToPairCM(p1); + + return std::sqrt( + p1CM.Px() * p1CM.Px() + + p1CM.Py() * p1CM.Py() + + p1CM.Pz() * p1CM.Pz()); +} + +struct PionKin { + double pt, eta, phi; +}; + +static inline PionKin getPionKinematics( + double lambdaPt, double lambdaEta, double lambdaPhi, + double protonPt, double protonEta, double protonPhi) +{ + // const double mP = o2::constants::physics::MassProton; + + // Λ momentum energy + const double pxL = lambdaPt * std::cos(lambdaPhi); + const double pyL = lambdaPt * std::sin(lambdaPhi); + const double pzL = lambdaPt * std::sinh(lambdaEta); + // const double EL = std::sqrt(pxL*pxL + pyL*pyL + pzL*pzL + lambdaMass*lambdaMass); + + // proton momentum and energy + const double pxP = protonPt * std::cos(protonPhi); + const double pyP = protonPt * std::sin(protonPhi); + const double pzP = protonPt * std::sinh(protonEta); + // const double EP = std::sqrt(pxP*pxP + pyP*pyP + pzP*pzP + mP*mP); + + // π momentum and energy + const double pxPi = pxL - pxP; + const double pyPi = pyL - pyP; + const double pzPi = pzL - pzP; + + PionKin out; + out.pt = std::sqrt(pxPi * pxPi + pyPi * pyPi); + + if (out.pt < 1e-6) { + out.eta = 0.0; + out.phi = 0.0; + return out; + } + out.eta = std::asinh(pzPi / out.pt); + out.phi = std::atan2(pyPi, pxPi); + return out; +} + namespace mcacc { // event @@ -199,11 +253,6 @@ struct lambdaspincorrderived { "ConfWeightPaths", std::vector{}, "Replaced-leg CCDB paths in order: REP_LL_leg1,REP_UL_leg1,REP_ALAL_leg1,REP_LL_leg2,REP_UL_leg2,REP_ALAL_leg2"}; - Configurable useConditionedSEWeight{"useConditionedSEWeight", false, "Use replacement weights derived from successful SE targets only"}; - Configurable> ConfConditionedWeightPaths{ - "ConfConditionedWeightPaths", - std::vector{}, - "Conditioned CCDB paths in order: REP/SUC_TGT for LL_leg1,UL_leg1,ALAL_leg1,LL_leg2,UL_leg2,ALAL_leg2"}; Configurable> ConfFixedWeightPaths{ "ConfFixedWeightPaths", std::vector{}, @@ -270,19 +319,25 @@ struct lambdaspincorrderived { // Output-control switches. Keep the final mass-mass-costheta sparse on by default, // but allow heavy QA/additional sparses to be disabled in production. - Configurable fillBasicQAHistos{"fillBasicQAHistos", true, "Fill light QA histograms: pT-y, phi-eta, centrality, dphi, pt/eta vs cent"}; - Configurable fillReplacementQAHistos{"fillReplacementQAHistos", true, "Fill raw TGT/REP single-candidate replacement QA maps"}; - Configurable fillFixedLegQAHistos{"fillFixedLegQAHistos", false, "Fill fixed-leg TGT/SUC QA maps"}; - Configurable fillWeightQAHistos{"fillWeightQAHistos", false, "Fill weighted/final-weighted REP/FIX QA maps"}; - Configurable fillAnalysisSparses{"fillAnalysisSparses", false, "Fill extra deltaR/deltaRap/deltaPhi Analysis THnSparse objects"}; - Configurable fillAdditionalSparses{"fillAdditionalSparses", false, "Fill extra rapidity/dphi/pair-mass THnSparse objects"}; - Configurable checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"}; + + struct : ConfigurableGroup { + Configurable fillBasicQAHistos{"fillBasicQAHistos", true, "Fill light QA histograms: pT-y, phi-eta, centrality, dphi, pt/eta vs cent"}; + Configurable fillReplacementQAHistos{"fillReplacementQAHistos", true, "Fill raw TGT/REP single-candidate replacement QA maps"}; + Configurable fillFixedLegQAHistos{"fillFixedLegQAHistos", false, "Fill fixed-leg TGT/SUC QA maps"}; + Configurable fillWeightQAHistos{"fillWeightQAHistos", true, "Fill weighted/final-weighted REP/FIX QA maps"}; + Configurable fillAnalysisSparses{"fillAnalysisSparses", false, "Fill extra deltaR/deltaRap/deltaPhi Analysis THnSparse objects"}; + Configurable fillAdditionalSparses{"fillAdditionalSparses", false, "Fill extra rapidity/dphi/pair-mass THnSparse objects"}; + Configurable checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"}; + + Configurable fillkStarSparses{"fillkStarSparses", true, "Fill extra deltaR/deltaRap/deltaPhi kStar THnSparse objects"}; + + } fillHistConfig; Configurable ptMin{"ptMin", 0.5, "V0 Pt minimum"}; Configurable ptMax{"ptMax", 3.0, "V0 Pt maximum"}; Configurable MassMin{"MassMin", 1.09, "V0 Mass minimum"}; Configurable MassMax{"MassMax", 1.14, "V0 Mass maximum"}; - Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; + Configurable rapidity{"rapidity", 0.5, "Rapidity cut on lambda"}; Configurable v0eta{"v0eta", 0.8, "Eta cut on lambda"}; @@ -295,28 +350,10 @@ struct lambdaspincorrderived { Configurable dcaPion{"dcaPion", 0.2, "DCA Pion"}; Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; + Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; } v0Configurations; - struct : ConfigurableGroup { - std::string prefix = "closePairQA"; - - Configurable fillClosePairQA{ - "fillClosePairQA", - true, - "Fill daughter deta-dphi vs Lambda-Lambda deltaR close-pair QA"}; - - ConfigurableAxis axClosePairDEta{ - "axClosePairDEta", - {100, -0.2, 0.2}, - "#Delta#eta daughter"}; - - ConfigurableAxis axClosePairDPhi{ - "axClosePairDPhi", - {100, -0.2, 0.2}, - "#Delta#varphi daughter"}; - } cfgClosePairQA; - // Event Mixing Configurable cosDef{"cosDef", 1, "Defination of cos"}; @@ -336,27 +373,18 @@ struct lambdaspincorrderived { ConfigurableAxis configThnAxisPhi{"configThnAxisPhi", {VARIABLE_WIDTH, 0.0, 2.0 * TMath::Pi()}, "Phi"}; ConfigurableAxis configThnAxisDeltaPhi{"configThnAxisDeltaPhi", {VARIABLE_WIDTH, 0.0, TMath::Pi() / 6, 2.0 * TMath::Pi() / 6, 3.0 * TMath::Pi() / 6, 4.0 * TMath::Pi() / 6, 5.0 * TMath::Pi() / 6, TMath::Pi()}, "Delta Phi"}; - ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 2.0, 3.1, 4.0}, "Delta R"}; + ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {VARIABLE_WIDTH, 0.0, 0.5, 1.2, 2.0, 3.1, 4.0}, "Delta R"}; + ConfigurableAxis configThnAxisDeltaRnew{"configThnAxisDeltaRnew", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1}, "Delta R"}; ConfigurableAxis configThnAxisDeltaRap{"configThnAxisDeltaRap", {VARIABLE_WIDTH, 0.0, 0.2, 0.5, 1.0, 1.6}, "Delta Rap"}; + ConfigurableAxis configThnAxisKStar{"configThnAxisKStar", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 1.0}, "#it{k}^{*} (GeV/#it{c})"}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // ------------------------- // Systematics control // ------------------------- - struct SysFromDeltaPtDeltaEtaDeltaPhi { - // Mixed -event kinematic cut - float minDeltaPt; - float maxDelatPt; - - float minDeltaEta; - float maxDeltaEta; - - float minDeltaPhi; - float maxDeltaPhi; - }; - struct SysCuts { // Topology cut float maxDcaDaughters; @@ -456,13 +484,55 @@ struct lambdaspincorrderived { void init(o2::framework::InitContext&) { + + // Laura: Δη–Δφ for same-charge ΛΛ in the first ΔR bin + const int nBinsDEta = 80; + const double dEtaMin = -0.15; + const double dEtaMax = 0.15; + + const int nBinsDPhi = 80; + const double dPhiMin = -0.15; + const double dPhiMax = 0.15; + + histos.add("hDEtaDPhiKStar_pp_SE", + "p-p #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_pipi_SE", + "#pi-#pi #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_ppi_SE", + "p-#pi #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_pip_SE", + "#pi-p #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + buildSystematicCuts(); nSysTotal = (int)sysCuts.size(); // or whatever vector you fill LOGF(info, "sysCuts.size()=%zu nSysTotal=%d", sysCuts.size(), nSysTotal); const AxisSpec thnAxisSys{nSysTotal, -0.5f, float(nSysTotal) - 0.5f, "sysId"}; - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.add("hPtRadiusV0", "V0 QA;#it{p}_{T}^{V0} (GeV/#it{c});V0 decay radius (cm)", kTH2F, {{100, 0.0, 10.0}, {120, 0.0, 45.0}}); histos.add("hPtYSame", "hPtYSame", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); histos.add("hPtYMix", "hPtYMix", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); @@ -475,59 +545,7 @@ struct lambdaspincorrderived { histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); } - if (cfgClosePairQA.fillClosePairQA) { - histos.add( - "hClosePairPP_kStar020", - "p_{1}-p_{2}, k* > 0.2 GeV/c;#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPiPi_kStar020", - "#pi_{1}-#pi_{2}, k* > 0.2 GeV/c;#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPPi_kStar020", - "p_{1}-#pi_{2}, k* > 0.2 GeV/c;#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPiP_kStar020", - "#pi_{1}-p_{2}, k* > 0.2 GeV/c;#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - - histos.add( - "hClosePairPP", - "p_{1}-p_{2};#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPiPi", - "#pi_{1}-#pi_{2};#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPPi", - "p_{1}-#pi_{2};#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - histos.add( - "hClosePairPiP", - "#pi_{1}-p_{2};#Delta#eta;#Delta#varphi;#DeltaR_{#Lambda#Lambda}", - HistType::kTH3D, - {cfgClosePairQA.axClosePairDEta, cfgClosePairQA.axClosePairDPhi, configThnAxisDeltaR}, - true); - } - - if (fillWeightQAHistos) { + if (fillHistConfig.fillWeightQAHistos) { // QA for weighting histos.add("REP_LL_leg1_weighted", "Weighted repl LL leg1;#phi;y/#eta;p_{T}", @@ -561,7 +579,7 @@ struct lambdaspincorrderived { "Weighted repl ALAL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); } - if (fillFixedLegQAHistos) { + if (fillHistConfig.fillFixedLegQAHistos) { // Fixed-leg QA for replacement-leg bias check. // For repLeg1: leg1 is replaced, fixed leg is original leg2. // For repLeg2: leg2 is replaced, fixed leg is original leg1. @@ -597,7 +615,7 @@ struct lambdaspincorrderived { histos.add("hNUAWeightLambda", "Lambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); histos.add("hNUAWeightAntiLambda", "AntiLambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); - if (fillReplacementQAHistos) { + if (fillHistConfig.fillReplacementQAHistos) { // --- target/replacement single-leg occupancy maps for replacement correction histos.add("TGT_LL_leg1", "Target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); histos.add("TGT_LAL_leg1", "Target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); @@ -614,25 +632,12 @@ struct lambdaspincorrderived { histos.add("TGT_ALL_leg2", "Target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); histos.add("TGT_ALAL_leg2", "Target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - // Target candidates restricted to branches for which at least one valid - // replacement was found. Use REP/SUC_TGT as the conditioned matching - // efficiency when SE itself is restricted to replacement-found pairs. - histos.add("SUC_TGT_LL_leg1", "Successful target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_LAL_leg1", "Successful target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_ALL_leg1", "Successful target ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_ALAL_leg1", "Successful target ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("SUC_TGT_LL_leg2", "Successful target LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_LAL_leg2", "Successful target LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_ALL_leg2", "Successful target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("SUC_TGT_ALAL_leg2", "Successful target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_LL_leg2", "Repl LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); histos.add("REP_LAL_leg2", "Repl LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); histos.add("REP_ALL_leg2", "Repl ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); histos.add("REP_ALAL_leg2", "Repl ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); } - if (fillWeightQAHistos) { + if (fillHistConfig.fillWeightQAHistos) { // Final-weighted QA: filled with the exact same mixing correction weight // used for the mixed-event sparse, excluding NUA. // Correction categories are LL, UL=(LAL+ALL), ALAL. @@ -656,141 +661,6 @@ struct lambdaspincorrderived { histos.add("hSparseLambdaAntiLambda", "hSparseLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaLambda", "hSparseAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaAntiLambda", "hSparseAntiLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - // ============================================================ - // cos^2(theta*) sparses - // ============================================================ - - // Same event - histos.add("hSparseLambdaLambdaCos2", - "hSparseLambdaLambdaCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseLambdaAntiLambdaCos2", - "hSparseLambdaAntiLambdaCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaLambdaCos2", - "hSparseAntiLambdaLambdaCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaAntiLambdaCos2", - "hSparseAntiLambdaAntiLambdaCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - // Mixed event - histos.add("hSparseLambdaLambdaMixedCos2", - "hSparseLambdaLambdaMixedCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseLambdaAntiLambdaMixedCos2", - "hSparseLambdaAntiLambdaMixedCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaLambdaMixedCos2", - "hSparseAntiLambdaLambdaMixedCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaAntiLambdaMixedCos2", - "hSparseAntiLambdaAntiLambdaMixedCos2", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - // ============================================================ - // cos^3(theta*) sparses - // ============================================================ - - // Same event -- useful QA / future higher moment - histos.add("hSparseLambdaLambdaCos3", - "hSparseLambdaLambdaCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseLambdaAntiLambdaCos3", - "hSparseLambdaAntiLambdaCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaLambdaCos3", - "hSparseAntiLambdaLambdaCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaAntiLambdaCos3", - "hSparseAntiLambdaAntiLambdaCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - // Mixed event -- needed for linearity test - histos.add("hSparseLambdaLambdaMixedCos3", - "hSparseLambdaLambdaMixedCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseLambdaAntiLambdaMixedCos3", - "hSparseLambdaAntiLambdaMixedCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaLambdaMixedCos3", - "hSparseAntiLambdaLambdaMixedCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - - histos.add("hSparseAntiLambdaAntiLambdaMixedCos3", - "hSparseAntiLambdaAntiLambdaMixedCos3", - HistType::kTHnSparseF, - {configThnAxisInvMass, configThnAxisInvMass, - configThnAxisPol, configThnAxisR}, - true); - // Original same-event pairs split by whether the ME replacement search - // succeeds. These use the original SE pair variables and the ordinary - // SE NUA weight; no mixing or replacement-efficiency weight is applied. - histos.add("hSparseLambdaLambdaSEReplacementFound", "hSparseLambdaLambdaSEReplacementFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseLambdaAntiLambdaSEReplacementFound", "hSparseLambdaAntiLambdaSEReplacementFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseAntiLambdaLambdaSEReplacementFound", "hSparseAntiLambdaLambdaSEReplacementFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseAntiLambdaAntiLambdaSEReplacementFound", "hSparseAntiLambdaAntiLambdaSEReplacementFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - - histos.add("hSparseLambdaLambdaSEReplacementNotFound", "hSparseLambdaLambdaSEReplacementNotFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseLambdaAntiLambdaSEReplacementNotFound", "hSparseLambdaAntiLambdaSEReplacementNotFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseAntiLambdaLambdaSEReplacementNotFound", "hSparseAntiLambdaLambdaSEReplacementNotFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseAntiLambdaAntiLambdaSEReplacementNotFound", "hSparseAntiLambdaAntiLambdaSEReplacementNotFound", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseLambdaLambdaMixed", "hSparseLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseLambdaAntiLambdaMixed", "hSparseLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); @@ -808,7 +678,20 @@ struct lambdaspincorrderived { histos.add("hSparseAntiLambdaLambdaMixedSys", "hSparseAntiLambdaLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); histos.add("hSparseAntiLambdaAntiLambdaMixedSys", "hSparseAntiLambdaAntiLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); - if (fillAnalysisSparses) { + if (fillHistConfig.fillkStarSparses) { + + histos.add("hSparseLambdaLambda_kStar", "hSparseLambdaLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseLambdaAntiLambda_kStar", "hSparseLambdaAntiLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaLambda_kStar", "hSparseAntiLambdaLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaAntiLambda_kStar", "hSparseAntiLambdaAntiLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + + histos.add("hSparseLambdaLambdaMixed_kStar", "hSparseLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + // histos.add("hSparseLambdaAntiLambdaMixed_kStar", "hSparseLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR,configThnAxisKStar}, true); + // histos.add("hSparseAntiLambdaLambdaMixed_kStar", "hSparseAntiLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR,configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaAntiLambdaMixed_kStar", "hSparseAntiLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + } + + if (fillHistConfig.fillAnalysisSparses) { histos.add("hSparseLambdaLambdaAnalysis", "hSparseLambdaLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); histos.add("hSparseLambdaAntiLambdaAnalysis", "hSparseLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); histos.add("hSparseAntiLambdaLambdaAnalysis", "hSparseAntiLambdLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); @@ -820,7 +703,7 @@ struct lambdaspincorrderived { histos.add("hSparseAntiLambdaAntiLambdaMixedAnalysis", "hSparseAntiLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.add("hSparseRapLambdaLambda", "hSparseRapLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapLambdaAntiLambda", "hSparseRapLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapAntiLambdaLambda", "hSparseRapAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); @@ -858,18 +741,12 @@ struct lambdaspincorrderived { ccdb->setCreatedNotAfter(cfgCcdbParam.nolaterthan.value); LOGF(info, "Getting alignment offsets from the CCDB (check carefully)..."); if (useweight) { - if (cfgCcdbParam.useConditionedSEWeight && cfgCcdbParam.useFixedWeight) { - LOGF(fatal, "useFixedWeight must be false with useConditionedSEWeight: the fixed leg already belongs to the successful SE subset"); - } - const auto& repPaths = cfgCcdbParam.useConditionedSEWeight - ? cfgCcdbParam.ConfConditionedWeightPaths.value - : cfgCcdbParam.ConfWeightPaths.value; - const char* activePathName = cfgCcdbParam.useConditionedSEWeight ? "ConfConditionedWeightPaths" : "ConfWeightPaths"; + const auto& repPaths = cfgCcdbParam.ConfWeightPaths.value; if (repPaths.size() < 6) { - LOGF(fatal, "%s must contain 6 paths: LL_leg1, UL_leg1, ALAL_leg1, LL_leg2, UL_leg2, ALAL_leg2. Found %zu", activePathName, repPaths.size()); + LOGF(fatal, "ConfWeightPaths must contain 6 paths: REP_LL_leg1, REP_UL_leg1, REP_ALAL_leg1, REP_LL_leg2, REP_UL_leg2, REP_ALAL_leg2. Found %zu", repPaths.size()); } for (size_t i = 0; i < repPaths.size(); ++i) { - LOGF(info, "%s[%zu] = '%s'", activePathName, i, repPaths[i].c_str()); + LOGF(info, "ConfWeightPaths[%zu] = '%s'", i, repPaths[i].c_str()); } auto loadRep = [&](int idx, const char* name) -> TH3D* { @@ -952,7 +829,7 @@ struct lambdaspincorrderived { if (candidate.v0Cospa() < v0Configurations.cosPA) { return false; } - if (checkDoubleStatus && candidate.doubleStatus()) { + if (fillHistConfig.checkDoubleStatus && candidate.doubleStatus()) { return false; } if (candidate.v0Radius() > v0Configurations.radiusMax) { @@ -997,7 +874,7 @@ struct lambdaspincorrderived { if (candidate.v0Cospa() < SysCuts.mincosPA) { return false; } - if (checkDoubleStatus && candidate.doubleStatus()) { + if (fillHistConfig.checkDoubleStatus && candidate.doubleStatus()) { return false; } if (candidate.v0Radius() > SysCuts.maxRadius) { @@ -1083,20 +960,6 @@ struct lambdaspincorrderived { } return w; } - static inline double getKStar( - const ROOT::Math::PtEtaPhiMVector& p1, - const ROOT::Math::PtEtaPhiMVector& p2) - { - const auto pair = p1 + p2; - - ROOT::Math::Boost boostToPairCM{pair.BoostToCM()}; - const auto p1CM = boostToPairCM(p1); - - return std::sqrt( - p1CM.Px() * p1CM.Px() + - p1CM.Py() * p1CM.Py() + - p1CM.Pz() * p1CM.Pz()); - } int getWeightCategory(int tag1, int tag2) const { @@ -1123,7 +986,7 @@ struct lambdaspincorrderived { // for the mixed-event sparse, before the NUA factor is applied. // mapLeg = physical replacement branch used for the CCDB map (1 or 2) // replacedPos = position of the replaced candidate after possible L#bar{L} reordering (1 or 2) - if (!fillWeightQAHistos) { + if (!fillHistConfig.fillWeightQAHistos) { return; } if (!std::isfinite(weight) || weight <= 0.0) { @@ -1215,6 +1078,24 @@ struct lambdaspincorrderived { lambda2Mass = o2::constants::physics::MassLambda; } + const auto pion1 = getPionKinematics( + particle1.Pt(), particle1.Eta(), particle1.Phi(), + daughpart1.Pt(), daughpart1.Eta(), daughpart1.Phi()); + + const auto pion2 = getPionKinematics( + particle2.Pt(), particle2.Eta(), particle2.Phi(), + daughpart2.Pt(), daughpart2.Eta(), daughpart2.Phi()); + + const auto pion1Vec = ROOT::Math::PtEtaPhiMVector( + pion1.pt, pion1.eta, pion1.phi, o2::constants::physics::MassPionCharged); + const auto pion2Vec = ROOT::Math::PtEtaPhiMVector( + pion2.pt, pion2.eta, pion2.phi, o2::constants::physics::MassPionCharged); + + const double kStar_pp = getKStar(daughpart1, daughpart2); + const double kStar_pipi = getKStar(pion1Vec, pion2Vec); + const double kStar_ppi = getKStar(daughpart1, pion2Vec); + const double kStar_pip = getKStar(pion1Vec, daughpart2); + auto particle1Dummy = ROOT::Math::PtEtaPhiMVector(particle1.Pt(), particle1.Eta(), particle1.Phi(), lambda1Mass); auto particle2Dummy = ROOT::Math::PtEtaPhiMVector(particle2.Pt(), particle2.Eta(), particle2.Phi(), lambda2Mass); auto pairDummy = particle1Dummy + particle2Dummy; @@ -1264,8 +1145,7 @@ struct lambdaspincorrderived { cosDeltaTheta_hel = -111.0; double cosThetaDiff = (cosDef == 0) ? cosDeltaTheta_STAR_naive : cosDeltaTheta_hel; - const double cosThetaDiff2 = cosThetaDiff * cosThetaDiff; - const double cosThetaDiff3 = cosThetaDiff2 * cosThetaDiff; + double pt1 = particle1.Pt(); double dphi1 = RecoDecay::constrainAngle(particle1.Phi(), 0.0F, harmonic); double deta1 = particle1.Eta(); @@ -1367,68 +1247,95 @@ struct lambdaspincorrderived { if (datatype == 0) { const double weight = pairNUAWeight; + + // ---- Δη–Δφ for same-charge ΛΛ, first ΔR bin ---- + if (tag1 == tag2) { + if (particle1.M() > 1.11 && particle1.M() < 1.12 && + particle2.M() > 1.11 && particle2.M() < 1.12) { + + if (deltaR >= 0.0 && deltaR < 3.1) { + + histos.fill(HIST("hDEtaDPhiKStar_pp_SE"), + daughpart1.Eta() - daughpart2.Eta(), + deltaPhiMinusPiToPi(daughpart1.Phi(), daughpart2.Phi()), + deltaR, + kStar_pp); + + histos.fill(HIST("hDEtaDPhiKStar_pipi_SE"), + pion1.eta - pion2.eta, + deltaPhiMinusPiToPi(pion1.phi, pion2.phi), + deltaR, + kStar_pipi); + + histos.fill(HIST("hDEtaDPhiKStar_ppi_SE"), + daughpart1.Eta() - pion2.eta, + deltaPhiMinusPiToPi(daughpart1.Phi(), pion2.phi), + deltaR, + kStar_ppi); + + histos.fill(HIST("hDEtaDPhiKStar_pip_SE"), + pion1.eta - daughpart2.Eta(), + deltaPhiMinusPiToPi(pion1.phi, daughpart2.Phi()), + deltaR, + kStar_pip); + } + } + } + if (tag1 == 0 && tag2 == 0) { - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("hPtYSame"), particle1.Pt(), particle1.Rapidity(), nuaWeight1); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); histos.fill(HIST("hSparseLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaCos2"), particle1.M(), particle2.M(), cosThetaDiff2, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaCos3"), particle1.M(), particle2.M(), cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } + + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaCos2"), particle1.M(), particle2.M(), cosThetaDiff2, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaCos3"), particle1.M(), particle2.M(), cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - - histos.fill(HIST("hSparseAntiLambdaLambdaCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - - histos.fill(HIST("hSparseAntiLambdaAntiLambdaCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } } else if (datatype == 1) { @@ -1458,24 +1365,21 @@ struct lambdaspincorrderived { if (tag1 == 0 && tag2 == 0) { if (replacedLeg == 1 || replacedLeg == 2) { - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("hPtYMix"), particle1.Pt(), particle1.Rapidity(), nuaWeight1 * mixpairweight); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); } histos.fill(HIST("hSparseLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaMixedCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaMixedCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); @@ -1483,156 +1387,41 @@ struct lambdaspincorrderived { } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaMixedCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - - histos.fill(HIST("hSparseLambdaAntiLambdaMixedCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaMixedCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - - histos.fill(HIST("hSparseAntiLambdaLambdaMixedCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedCos2"), - particle1.M(), particle2.M(), - cosThetaDiff2, deltaR, weight); - - histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedCos3"), - particle1.M(), particle2.M(), - cosThetaDiff3, deltaR, weight); - if (fillAnalysisSparses) { + if (fillHistConfig.fillAnalysisSparses) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); } - if (useAdditionalHisto || fillAdditionalSparses) { + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } } } - void fillSEReplacementOutcomeSparse( - int tag1, int tag2, - const ROOT::Math::PtEtaPhiMVector& particle1, - const ROOT::Math::PtEtaPhiMVector& particle2, - const ROOT::Math::PtEtaPhiMVector& daughter1, - const ROOT::Math::PtEtaPhiMVector& daughter2, - bool replacementFound) - { - const double lambda1Mass = usePDGM ? o2::constants::physics::MassLambda : particle1.M(); - const double lambda2Mass = usePDGM ? o2::constants::physics::MassLambda : particle2.M(); - - const auto particle1Dummy = ROOT::Math::PtEtaPhiMVector(particle1.Pt(), particle1.Eta(), particle1.Phi(), lambda1Mass); - const auto particle2Dummy = ROOT::Math::PtEtaPhiMVector(particle2.Pt(), particle2.Eta(), particle2.Phi(), lambda2Mass); - const auto pairDummy = particle1Dummy + particle2Dummy; - ROOT::Math::Boost boostPairToCM{pairDummy.BoostToCM()}; - - const auto lambda1CM = boostPairToCM(particle1Dummy); - const auto lambda2CM = boostPairToCM(particle2Dummy); - ROOT::Math::Boost boostLambda1ToCM{lambda1CM.BoostToCM()}; - ROOT::Math::Boost boostLambda2ToCM{lambda2CM.BoostToCM()}; - - const auto proton1PairCM = boostPairToCM(daughter1); - const auto proton2PairCM = boostPairToCM(daughter2); - const auto proton1LambdaRF = boostLambda1ToCM(proton1PairCM); - const auto proton2LambdaRF = boostLambda2ToCM(proton2PairCM); - - ROOT::Math::Boost boostL1LabToRF{particle1Dummy.BoostToCM()}; - ROOT::Math::Boost boostL2LabToRF{particle2Dummy.BoostToCM()}; - const auto proton1LRF = boostL1LabToRF(daughter1); - const auto proton2LRF = boostL2LabToRF(daughter2); - - const TVector3 u1 = TVector3(proton1LRF.Px(), proton1LRF.Py(), proton1LRF.Pz()).Unit(); - const TVector3 u2 = TVector3(proton2LRF.Px(), proton2LRF.Py(), proton2LRF.Pz()).Unit(); - const TVector3 k1 = TVector3(proton1LambdaRF.Px(), proton1LambdaRF.Py(), proton1LambdaRF.Pz()).Unit(); - const TVector3 k2 = TVector3(proton2LambdaRF.Px(), proton2LambdaRF.Py(), proton2LambdaRF.Pz()).Unit(); - - double cosThetaDiff = (cosDef == 0) ? u1.Dot(u2) : k1.Dot(k2); - if (cosThetaDiff > 1.0) { - cosThetaDiff = 111.0; - } else if (cosThetaDiff < -1.0) { - cosThetaDiff = -111.0; - } - - const double dphi1 = RecoDecay::constrainAngle(particle1.Phi(), 0.0F, harmonic); - const double dphi2 = RecoDecay::constrainAngle(particle2.Phi(), 0.0F, harmonic); - const double dphiPair = RecoDecay::constrainAngle(dphi1 - dphi2, -TMath::Pi(), harmonicDphi); - const double deltaRap = std::abs(particle1.Rapidity() - particle2.Rapidity()); - const double deltaR = TMath::Sqrt(deltaRap * deltaRap + dphiPair * dphiPair); - - // Match the ordinary SE sparse exactly. This is only the established SE - // NUA correction, not an ME-availability or replacement-candidate weight. - const double pairNUAWeight = getNUAWeight(tag1, particle1.Phi(), particle1.Eta()) * - getNUAWeight(tag2, particle2.Phi(), particle2.Eta()); - - if (replacementFound) { - if (tag1 == 0 && tag2 == 0) { - histos.fill(HIST("hSparseLambdaLambdaSEReplacementFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 0 && tag2 == 1) { - histos.fill(HIST("hSparseLambdaAntiLambdaSEReplacementFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 1 && tag2 == 0) { - histos.fill(HIST("hSparseAntiLambdaLambdaSEReplacementFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 1 && tag2 == 1) { - histos.fill(HIST("hSparseAntiLambdaAntiLambdaSEReplacementFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } - return; - } - - if (tag1 == 0 && tag2 == 0) { - histos.fill(HIST("hSparseLambdaLambdaSEReplacementNotFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 0 && tag2 == 1) { - histos.fill(HIST("hSparseLambdaAntiLambdaSEReplacementNotFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 1 && tag2 == 0) { - histos.fill(HIST("hSparseAntiLambdaLambdaSEReplacementNotFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } else if (tag1 == 1 && tag2 == 1) { - histos.fill(HIST("hSparseAntiLambdaAntiLambdaSEReplacementNotFound"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, pairNUAWeight); - } - } - - template - void fillSEReplacementOutcome(T1 const& first, T2 const& second, bool replacementFound) - { - const auto firstLambda = ROOT::Math::PtEtaPhiMVector(first.lambdaPt(), first.lambdaEta(), first.lambdaPhi(), first.lambdaMass()); - const auto firstProton = ROOT::Math::PtEtaPhiMVector(first.protonPt(), first.protonEta(), first.protonPhi(), o2::constants::physics::MassProton); - const auto secondLambda = ROOT::Math::PtEtaPhiMVector(second.lambdaPt(), second.lambdaEta(), second.lambdaPhi(), second.lambdaMass()); - const auto secondProton = ROOT::Math::PtEtaPhiMVector(second.protonPt(), second.protonEta(), second.protonPhi(), o2::constants::physics::MassProton); - const int firstStatus = first.v0Status(); - const int secondStatus = second.v0Status(); - - // Keep the same unlike-sign ordering used by processData: Lambda first, - // AntiLambda second. - if (firstStatus == 1 && secondStatus == 0) { - fillSEReplacementOutcomeSparse(0, 1, secondLambda, firstLambda, secondProton, firstProton, replacementFound); - return; - } - fillSEReplacementOutcomeSparse(firstStatus, secondStatus, firstLambda, secondLambda, firstProton, secondProton, replacementFound); - } - void fillHistogramsSys(int tag1, int tag2, const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2, const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2, @@ -1894,13 +1683,13 @@ struct lambdaspincorrderived { if (!selectionV0(v0)) { continue; } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); } proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), o2::constants::physics::MassProton); @@ -1922,56 +1711,14 @@ struct lambdaspincorrderived { } if (hasSharedDaughters(v0, v02)) continue; - proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), o2::constants::physics::MassProton); lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), v02.lambdaMass()); - const double kStar = getKStar(lambda, lambda2); - - // Close-pair QA for same-sign Lambda pairs in the signal mass window. - if (cfgClosePairQA.fillClosePairQA && - v0.v0Status() == v02.v0Status() && - v0.lambdaMass() > 1.11f && v0.lambdaMass() < 1.12f && - v02.lambdaMass() > 1.11f && v02.lambdaMass() < 1.12f) { - // Keep the parent DeltaR definition identical to the main analysis. - const double dPhiLL = deltaPhiMinusPiToPi(lambda.Phi(), lambda2.Phi()); - const double dYLL = lambda.Rapidity() - lambda2.Rapidity(); - const double deltaRLL = std::sqrt(dYLL * dYLL + dPhiLL * dPhiLL); - - // Lambda = proton + pion. - const auto pion1 = lambda - proton; - const auto pion2 = lambda2 - proton2; - - const double dEtaPP = proton.Eta() - proton2.Eta(); - const double dPhiPP = deltaPhiMinusPiToPi(proton.Phi(), proton2.Phi()); - histos.fill(HIST("hClosePairPP"), dEtaPP, dPhiPP, deltaRLL); - if (kStar > 0.20) { - histos.fill(HIST("hClosePairPP_kStar020"), dEtaPP, dPhiPP, deltaRLL); - } - - const double dEtaPiPi = pion1.Eta() - pion2.Eta(); - const double dPhiPiPi = deltaPhiMinusPiToPi(pion1.Phi(), pion2.Phi()); - histos.fill(HIST("hClosePairPiPi"), dEtaPiPi, dPhiPiPi, deltaRLL); - if (kStar > 0.20) { - histos.fill(HIST("hClosePairPiPi_kStar020"), dEtaPiPi, dPhiPiPi, deltaRLL); - } - - const double dEtaPPi = proton.Eta() - pion2.Eta(); - const double dPhiPPi = deltaPhiMinusPiToPi(proton.Phi(), pion2.Phi()); - histos.fill(HIST("hClosePairPPi"), dEtaPPi, dPhiPPi, deltaRLL); - if (kStar > 0.20) { - histos.fill(HIST("hClosePairPPi_kStar020"), dEtaPPi, dPhiPPi, deltaRLL); - } - - const double dEtaPiP = pion1.Eta() - proton2.Eta(); - const double dPhiPiP = deltaPhiMinusPiToPi(pion1.Phi(), proton2.Phi()); - histos.fill(HIST("hClosePairPiP"), dEtaPiP, dPhiPiP, deltaRLL); - if (kStar > 0.20) { - histos.fill(HIST("hClosePairPiP_kStar020"), dEtaPiP, dPhiPiP, deltaRLL); + if ((v0.v0Status() == 0 && v02.v0Status() == 1) || (v0.v0Status() == 1 && v02.v0Status() == 0)) { + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); } } - if ((v0.v0Status() == 0 && v02.v0Status() == 1) || (v0.v0Status() == 1 && v02.v0Status() == 0)) - if (fillBasicQAHistos) - histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); + // const int ptype = pairTypeCode(v0.v0Status(), v02.v0Status()); if (v0.v0Status() == 0 && v02.v0Status() == 0) { fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0); @@ -2058,7 +1805,7 @@ struct lambdaspincorrderived { void fillReplacementControlMap(int tag1, int tag2, int leg, bool isTarget, LV const& particle, float weight) { - if (!fillReplacementQAHistos) { + if (!fillHistConfig.fillReplacementQAHistos) { return; } const double pt = particle.Pt(); @@ -2123,41 +1870,6 @@ struct lambdaspincorrderived { return; } } - - template - void fillSuccessfulTargetControlMap(int tag1, int tag2, int leg, LV const& targetParticle, float weight) - { - if (!fillReplacementQAHistos) { - return; - } - const double pt = targetParticle.Pt(); - const double phi = RecoDecay::constrainAngle(targetParticle.Phi(), 0.0F, harmonic); - const double etaOrY = userapidity ? targetParticle.Rapidity() : targetParticle.Eta(); - - if (leg == 1) { - if (tag1 == 0 && tag2 == 0) - histos.fill(HIST("SUC_TGT_LL_leg1"), phi, etaOrY, pt, weight); - else if (tag1 == 0 && tag2 == 1) - histos.fill(HIST("SUC_TGT_LAL_leg1"), phi, etaOrY, pt, weight); - else if (tag1 == 1 && tag2 == 0) - histos.fill(HIST("SUC_TGT_ALL_leg1"), phi, etaOrY, pt, weight); - else if (tag1 == 1 && tag2 == 1) - histos.fill(HIST("SUC_TGT_ALAL_leg1"), phi, etaOrY, pt, weight); - return; - } - - if (leg == 2) { - if (tag1 == 0 && tag2 == 0) - histos.fill(HIST("SUC_TGT_LL_leg2"), phi, etaOrY, pt, weight); - else if (tag1 == 0 && tag2 == 1) - histos.fill(HIST("SUC_TGT_LAL_leg2"), phi, etaOrY, pt, weight); - else if (tag1 == 1 && tag2 == 0) - histos.fill(HIST("SUC_TGT_ALL_leg2"), phi, etaOrY, pt, weight); - else if (tag1 == 1 && tag2 == 1) - histos.fill(HIST("SUC_TGT_ALAL_leg2"), phi, etaOrY, pt, weight); - } - } - template void fillFixedLegControlMap(int tag1, int tag2, int repLeg, @@ -2165,7 +1877,7 @@ struct lambdaspincorrderived { LV const& fixedParticle, float weight) { - if (!fillFixedLegQAHistos) { + if (!fillHistConfig.fillFixedLegQAHistos) { return; } const double pt = fixedParticle.Pt(); @@ -2243,6 +1955,15 @@ struct lambdaspincorrderived { auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); + // if pool empty, push and continue + if (eventPools[bin].empty()) { + eventPools[bin].emplace_back(collision1.index(), std::move(poolA)); + if ((int)eventPools[bin].size() > nEvtMixing) { + eventPools[bin].pop_front(); + } + continue; + } + for (const auto& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0(t1) || !selectionV0(t2)) { continue; @@ -2307,12 +2028,9 @@ struct lambdaspincorrderived { } if (totalRepl <= 0) { - fillSEReplacementOutcome(t1, t2, false); continue; } - fillSEReplacementOutcome(t1, t2, true); - const float wBase = 1.0f / static_cast(totalRepl); // Single-track replacement target must use the same branch normalization @@ -2348,9 +2066,6 @@ struct lambdaspincorrderived { // -------- leg-1 replacement: (tX, t2) if (doMixLeg1) { if (tX.v0Status() == t1.v0Status() && checkKinematics(t1, tX)) { - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 1, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - wBase); fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -2368,7 +2083,7 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(tX.v0Status(), t2.v0Status(), lambda, lambda2, proton, proton2, @@ -2379,9 +2094,6 @@ struct lambdaspincorrderived { // -------- leg-2 replacement: (t1, tX) if (doMixLeg2) { if (tX.v0Status() == t2.v0Status() && checkKinematics(t2, tX)) { - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 2, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - wBase); fillReplacementControlMap(t1.v0Status(), tX.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -2399,7 +2111,7 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(t1.v0Status(), tX.v0Status(), lambda, lambda2, proton, proton2, @@ -2600,14 +2312,6 @@ struct lambdaspincorrderived { int replacedLeg = 1; int age = 0; uint64_t seed = 0; - uint64_t outcomeId = 0; - }; - - struct PendingSEReplacementOutcome { - StoredV6Candidate first; - StoredV6Candidate second; - int remainingBranches = 0; - bool replacementFound = false; }; template @@ -2620,23 +2324,21 @@ struct lambdaspincorrderived { static_cast(t.protonIndex()), static_cast(t.pionIndex())}; } - template - void fillV6MixedBranch(TRep const& replacement, TTarget const& target, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + template + void fillV6MixedBranch(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) { const auto repProton = ROOT::Math::PtEtaPhiMVector(replacement.protonPt(), replacement.protonEta(), replacement.protonPhi(), o2::constants::physics::MassProton); const auto repLambda = ROOT::Math::PtEtaPhiMVector(replacement.lambdaPt(), replacement.lambdaEta(), replacement.lambdaPhi(), replacement.lambdaMass()); const auto fixedProton = ROOT::Math::PtEtaPhiMVector(fixed.protonPt(), fixed.protonEta(), fixed.protonPhi(), o2::constants::physics::MassProton); const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(fixed.lambdaPt(), fixed.lambdaEta(), fixed.lambdaPhi(), fixed.lambdaMass()); - const auto targetLambda = ROOT::Math::PtEtaPhiMVector(target.lambdaPt(), target.lambdaEta(), target.lambdaPhi(), target.lambdaMass()); const int repStatus = replacement.v0Status(); const int fixedStatus = fixed.v0Status(); if (replacedLeg == 1) { - fillSuccessfulTargetControlMap(target.v0Status(), fixedStatus, 1, targetLambda, controlWeight); fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); if ((repStatus == 0 && fixedStatus == 1) || (repStatus == 1 && fixedStatus == 0)) { - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); } } @@ -2650,10 +2352,9 @@ struct lambdaspincorrderived { return; } - fillSuccessfulTargetControlMap(fixedStatus, target.v0Status(), 2, targetLambda, controlWeight); fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); } if (fixedStatus == 0 && repStatus == 1) { @@ -2715,38 +2416,10 @@ struct lambdaspincorrderived { struct V6PendingState { std::deque data; std::deque mc; - std::unordered_map dataOutcomes; - uint64_t nextOutcomeId = 1; }; V6PendingState v6Pending; - void finalizePendingSEReplacementOutcome(uint64_t outcomeId, bool replacementFound) - { - if (outcomeId == 0) { - return; - } - auto outcomeIt = v6Pending.dataOutcomes.find(outcomeId); - if (outcomeIt == v6Pending.dataOutcomes.end()) { - return; - } - - auto& outcome = outcomeIt->second; - if (replacementFound && !outcome.replacementFound) { - fillSEReplacementOutcome(outcome.first, outcome.second, true); - outcome.replacementFound = true; - } - - --outcome.remainingBranches; - if (outcome.remainingBranches > 0) { - return; - } - if (!outcome.replacementFound) { - fillSEReplacementOutcome(outcome.first, outcome.second, false); - } - v6Pending.dataOutcomes.erase(outcomeIt); - } - template StoredV6CandidateMC storeV6CandidateMC(T const& t) const { @@ -2757,22 +2430,20 @@ struct lambdaspincorrderived { static_cast(mcacc::prIdx(t)), static_cast(mcacc::piIdx(t))}; } - template - void fillV6MixedBranchMC(TRep const& replacement, TTarget const& target, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + template + void fillV6MixedBranchMC(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) { const auto repProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(replacement), mcacc::prEta(replacement), mcacc::prPhi(replacement), o2::constants::physics::MassProton); const auto repLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(replacement), mcacc::lamEta(replacement), mcacc::lamPhi(replacement), mcacc::lamMass(replacement)); const auto fixedProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(fixed), mcacc::prEta(fixed), mcacc::prPhi(fixed), o2::constants::physics::MassProton); const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(fixed), mcacc::lamEta(fixed), mcacc::lamPhi(fixed), mcacc::lamMass(fixed)); - const auto targetLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(target), mcacc::lamEta(target), mcacc::lamPhi(target), mcacc::lamMass(target)); const int repStatus = mcacc::v0Status(replacement); const int fixedStatus = mcacc::v0Status(fixed); if (replacedLeg == 1) { - fillSuccessfulTargetControlMap(mcacc::v0Status(target), fixedStatus, 1, targetLambda, controlWeight); fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); } if (repStatus == 0 && fixedStatus == 1) { @@ -2785,10 +2456,9 @@ struct lambdaspincorrderived { return; } - fillSuccessfulTargetControlMap(fixedStatus, mcacc::v0Status(target), 2, targetLambda, controlWeight); fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); } if (fixedStatus == 0 && repStatus == 1) { @@ -2826,7 +2496,7 @@ struct lambdaspincorrderived { if (mcacc::v0CosPA(candidate) < v0Configurations.cosPA) { return false; } - if (checkDoubleStatus && mcacc::doubleStatus(candidate)) { + if (fillHistConfig.checkDoubleStatus && mcacc::doubleStatus(candidate)) { return false; } if (mcacc::v0Radius(candidate) > v0Configurations.radiusMax) { @@ -2923,13 +2593,13 @@ struct lambdaspincorrderived { if (!selectionV0MC(v0)) { continue; } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("hPtRadiusV0"), mcacc::lamPt(v0), mcacc::v0Radius(v0)); } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("ptCent"), mcacc::lamPt(v0), centrality); } - if (fillBasicQAHistos) { + if (fillHistConfig.fillBasicQAHistos) { histos.fill(HIST("etaCent"), mcacc::lamEta(v0), centrality); } @@ -3104,7 +2774,7 @@ struct lambdaspincorrderived { ptMin.value, ptMax.value, ptMix.value, - v0etaMixBuffer.value, + v0Configurations.v0etaMixBuffer.value, etaMix.value, phiMix.value, MassMin.value, @@ -3331,13 +3001,11 @@ struct lambdaspincorrderived { size_t pendingAdded = 0; if (!cfgV6CarryUnmatched) { v6Pending.data.clear(); - v6Pending.dataOutcomes.clear(); } else { for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { auto& pending = *it; ++pending.age; if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { - finalizePendingSEReplacementOutcome(pending.outcomeId, false); ++pendingExpired; it = v6Pending.data.erase(it); continue; @@ -3382,10 +3050,9 @@ struct lambdaspincorrderived { if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { continue; } - fillV6MixedBranch(replacement, pending.target, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); } ++pendingMatched; - finalizePendingSEReplacementOutcome(pending.outcomeId, true); it = v6Pending.data.erase(it); } } @@ -3496,50 +3163,23 @@ struct lambdaspincorrderived { } } - const bool replacementFoundNow = nFill1 > 0 || nFill2 > 0; - std::vector branchesToCarry; - branchesToCarry.reserve(2); if (cfgV6CarryUnmatched) { const auto hasPendingSpace = [&]() { - return cfgV6MaxPendingBranches.value <= 0 || - static_cast(v6Pending.data.size() + branchesToCarry.size()) < cfgV6MaxPendingBranches.value; + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; }; if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { - branchesToCarry.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, - static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx)), 0}); + v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; } if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { - branchesToCarry.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, - static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx)), 0}); - } - } - - if (replacementFoundNow) { - fillSEReplacementOutcome(t1, t2, true); - } else if (branchesToCarry.empty()) { - // No accepted replacement exists and there is no pending branch that - // could still find one in a later data frame. - fillSEReplacementOutcome(t1, t2, false); - } else { - uint64_t outcomeId = v6Pending.nextOutcomeId++; - if (outcomeId == 0) { - outcomeId = v6Pending.nextOutcomeId++; - } - v6Pending.dataOutcomes.emplace( - outcomeId, - PendingSEReplacementOutcome{storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), - static_cast(branchesToCarry.size()), false}); - for (auto& branch : branchesToCarry) { - branch.outcomeId = outcomeId; + v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; } } - for (auto& branch : branchesToCarry) { - v6Pending.data.push_back(std::move(branch)); - ++pendingAdded; - } - - if (!replacementFoundNow) { + if (nFill1 <= 0 && nFill2 <= 0) { continue; } // Residual-weight QA needs a leg-specific normalization: @@ -3579,9 +3219,6 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughters(tX, t2)) continue; - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 1, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - wSELeg1); fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wSELeg1); @@ -3600,7 +3237,7 @@ struct lambdaspincorrderived { const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = tX.v0Status(); const int s2 = t2.v0Status(); @@ -3633,9 +3270,6 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughters(tY, t2)) continue; - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 2, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - wSELeg2); fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), wSELeg2); @@ -3652,7 +3286,7 @@ struct lambdaspincorrderived { // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = t1.v0Status(); const int s2 = tY.v0Status(); @@ -3680,7 +3314,7 @@ struct lambdaspincorrderived { ptMin.value, ptMax.value, ptMix.value, - v0etaMixBuffer.value, + v0Configurations.v0etaMixBuffer.value, etaMix.value, phiMix.value, MassMin.value, @@ -3979,7 +3613,7 @@ struct lambdaspincorrderived { if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { continue; } - fillV6MixedBranch(replacement, pending.target, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); } ++pendingMatched; it = v6Pending.data.erase(it); @@ -4160,9 +3794,6 @@ struct lambdaspincorrderived { if (hasSharedDaughters(tX, t2)) continue; - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 1, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - wSELeg1); fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wSELeg1); @@ -4178,7 +3809,7 @@ struct lambdaspincorrderived { const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = tX.v0Status(); @@ -4216,9 +3847,6 @@ struct lambdaspincorrderived { if (hasSharedDaughters(tY, t2)) continue; - fillSuccessfulTargetControlMap(t1.v0Status(), t2.v0Status(), 2, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - wSELeg2); fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), wSELeg2); @@ -4233,7 +3861,7 @@ struct lambdaspincorrderived { const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = t1.v0Status(); @@ -4267,7 +3895,7 @@ struct lambdaspincorrderived { ptMin.value, ptMax.value, ptMix.value, - v0etaMixBuffer.value, + v0Configurations.v0etaMixBuffer.value, etaMix.value, phiMix.value, MassMin.value, @@ -4549,7 +4177,7 @@ struct lambdaspincorrderived { if (hasSharedDaughtersMC(replacement, pending.target) || hasSharedDaughtersMC(replacement, pending.fixed)) { continue; } - fillV6MixedBranchMC(replacement, pending.target, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + fillV6MixedBranchMC(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); } ++pendingMatchedMC; it = v6Pending.mc.erase(it); @@ -4713,9 +4341,6 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughtersMC(tX, t2)) continue; - fillSuccessfulTargetControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), - wSELeg1); fillReplacementControlMap(mcacc::v0Status(tX), mcacc::v0Status(t2), 1, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tX), mcacc::lamEta(tX), mcacc::lamPhi(tX), mcacc::lamMass(tX)), wSELeg1); @@ -4739,7 +4364,7 @@ struct lambdaspincorrderived { // const int ptype = pairTypeCode(mcacc::v0Status(tX), mcacc::v0Status(t2)); const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lX.Phi(), (float)l2.Phi()); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = mcacc::v0Status(tX); const int s2 = mcacc::v0Status(t2); @@ -4771,9 +4396,6 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughtersMC(tY, t2)) continue; - fillSuccessfulTargetControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), - wSELeg2); fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(tY), 2, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tY), mcacc::lamEta(tY), mcacc::lamPhi(tY), mcacc::lamMass(tY)), wSELeg2); @@ -4797,7 +4419,7 @@ struct lambdaspincorrderived { // const int ptype = pairTypeCode(mcacc::v0Status(t1), mcacc::v0Status(tY)); const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)l1.Phi(), (float)lY.Phi()); - if (fillBasicQAHistos) + if (fillHistConfig.fillBasicQAHistos) histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = mcacc::v0Status(t1); const int s2 = mcacc::v0Status(tY); @@ -4825,13 +4447,6 @@ struct lambdaspincorrderived { if (cfgV6LogPending) { LOGF(info, "MEV6 end of stream: unmatchedData=%zu unmatchedMC=%zu", v6Pending.data.size(), v6Pending.mc.size()); } - for (auto& [outcomeId, outcome] : v6Pending.dataOutcomes) { - static_cast(outcomeId); - if (!outcome.replacementFound) { - fillSEReplacementOutcome(outcome.first, outcome.second, false); - } - } - v6Pending.dataOutcomes.clear(); v6Pending.data.clear(); v6Pending.mc.clear(); } From 651558e779e63704e3e3109a2572d38311e05a36 Mon Sep 17 00:00:00 2001 From: sarjeetagami <162087855+sarjeetagami@users.noreply.github.com> Date: Sun, 27 Sep 2026 01:02:18 +0530 Subject: [PATCH 084/648] [PWGLF] Add new PID and QA checks to kstarpbpb (#18065) Co-authored-by: sarjeeta gami --- PWGLF/Tasks/Resonances/kstarpbpb.cxx | 406 +++++++++++++++++---------- 1 file changed, 259 insertions(+), 147 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 919065e8cd1..4211976dc32 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -21,7 +21,6 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -43,12 +42,12 @@ #include #include #include -#include #include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include #include #include @@ -132,33 +131,46 @@ struct Kstarpbpb { Configurable nBkgRotations{"nBkgRotations", 9, "Number of rotated copies (background) per each original candidate"}; Configurable fillRotation{"fillRotation", true, "fill rotation"}; Configurable fillSA{"fillSA", true, "same event SA"}; - Configurable fillOccupancy{"fillOccupancy", false, "fill Occupancy"}; - Configurable cfgOccupancyCut{"cfgOccupancyCut", 500, "Occupancy cut"}; + Configurable fillLikeSignSA{"fillLikeSignSA", false, "fill the same-event like-sign SA sparse (needs fillSA)"}; Configurable useWeight{"useWeight", false, "use EP dep effi weight"}; Configurable useSP{"useSP", false, "use SP"}; // spin-alignment quantization axis Configurable cfgSAFrame{"cfgSAFrame", 0, "SA quantization axis: 0 = event plane (FT0C), 1 = production plane, 2 = random event plane"}; Configurable cfgRndSeed{"cfgRndSeed", 0, "Seed for random event plane (0 = unique seed per job)"}; Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 2.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 2 is the previous behaviour, n = 1 is the normal to the Psi2 plane"}; + // phi(1020) -> K+K- reflection veto for K* (off by default: the task then behaves exactly as before) + Configurable cfgPhiVeto{"cfgPhiVeto", false, "Reject K pi pairs whose KK mass (pion candidate given the kaon mass) is near the phi(1020) mass"}; + Configurable cfgPhiVetoWindow{"cfgPhiVetoWindow", 0.010f, "Half-width of the phi(1020) veto window (GeV/c^2)"}; - // phi(1020) spin alignment: event selection is the K* one; track/PID selections and defaults identical to PWGLF/Tasks/Resonances/phipbpb.cxx - // (JSON keys follow the O2 linter naming rules, so removeFakeTrack, confFakeKaonCut and nsigmaCutTPC differ from the phipbpb.cxx spelling) + // event loss and signal loss for K* and phi(1020) (processEvtLossSigLossMC), adapted from processEvtLossSigLossMC1 in phianalysisrun3pbpb.cxx + struct : ConfigurableGroup { + std::string prefix = "evtSigLoss"; + Configurable cutVzGen{"cutVzGen", true, "Cut |vz| < cfgCutVertex on the generated collision"}; + Configurable isApplyInelgt0{"isApplyInelgt0", false, "Require INEL > 0 for the generated collision"}; + Configurable isApplyTVX{"isApplyTVX", false, "Require TVX (FT0A and FT0C MC multiplicity > 0) for the generated collision"}; + Configurable requireSingleReco{"requireSingleReco", true, "Accepted MC event: exactly one reconstructed collision passing the selection, as in processMC / processMCPhi (false: at least one, as in phianalysisrun3pbpb.cxx)"}; + ConfigurableAxis axisImpactPar{"axisImpactPar", {200, 0.0, 20.0}, "Impact parameter (fm)"}; + ConfigurableAxis axisMultMC{"axisMultMC", {500, 0.0, 2000.0}, "Generated charged particles in |eta| < 0.5"}; + } evtSigLoss; + + // phi(1020) spin alignment: event selection is the K* one; track and PID selections identical to PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx + // (JSON keys follow the O2 linter naming rules) struct : ConfigurableGroup { std::string prefix = "phiSA"; Configurable removeFakeTrack{"removeFakeTrack", true, "Remove fake track from momentum difference"}; Configurable confFakeKaonCut{"confFakeKaonCut", 0.1, "Cut based on track from momentum difference"}; - Configurable useGlobalTrack{"useGlobalTrack", true, "use Global track"}; - Configurable useDcaSyst{"useDcaSyst", false, "useDcaSyst"}; - Configurable cfgCutTOFBeta{"cfgCutTOFBeta", 0.0, "cut TOF beta"}; - Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Value of the TPC Nsigma cut"}; - Configurable nsigmaCutTOF{"nsigmaCutTOF", 3.0, "Value of the TOF Nsigma cut"}; + // track selection (phianalysisrun3pbpb.cxx) + Configurable iscustomDCAcut{"iscustomDCAcut", false, "Global track (with DCA) + PV contributor + ITS clusters"}; + Configurable ismanualDCAcut{"ismanualDCAcut", true, "Global track w/o DCA + PV contributor + manual DCAxy/DCAz + ITS clusters"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 2.0f, "DCAxy range for kaon tracks (manual DCA cut)"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for kaon tracks (manual DCA cut)"}; Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Number of TPC cluster"}; - Configurable cfgTPCSharedcluster{"cfgTPCSharedcluster", 0.4, "Maximum Number of TPC shared cluster"}; + // PID (phianalysisrun3pbpb.cxx) + Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Value of the TPC Nsigma cut"}; + Configurable nsigmaCutCombined{"nsigmaCutCombined", 3.0, "Value of the combined TPC-TOF Nsigma cut"}; + Configurable isNoTOF{"isNoTOF", false, "TPC-only PID for all tracks"}; Configurable isDeepAngle{"isDeepAngle", false, "Deep Angle cut"}; Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; - Configurable ispTdepPID{"ispTdepPID", true, "pT dependent PID"}; - Configurable isTOFOnly{"isTOFOnly", false, "TOF only PID"}; Configurable confRapidity{"confRapidity", 0.5, "Rapidity cut"}; Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; @@ -192,6 +204,7 @@ struct Kstarpbpb { using TrackCandidates = soa::Filtered>; using CollisionMCTrueTable = aod::McCollisions; + using McCollisionMults = soa::Join; using TrackMCTrueTable = aod::McParticles; using CollisionMCRecTableCentFT0C = soa::SmallGroups>; using TrackMCRecTable = soa::Join; @@ -224,11 +237,9 @@ struct Kstarpbpb { } rndGen.SetSeed(cfgRndSeed); - std::vector occupancyBinning = {0.0, 500.0, 1000.0, 1500.0, 2000.0, 3000.0, 4000.0, 5000.0, 50000.0}; AxisSpec phiAxis = {500, -6.28, 6.28, "phi"}; AxisSpec resAxis = {6000, -30, 30, "Res"}; AxisSpec centAxis = {8, 0, 80, "V0M (%)"}; - AxisSpec occupancyAxis = {occupancyBinning, "Occupancy"}; AxisSpec vzAxis = {400, -20.0, 20.0, "Z_{vtx} (cm)"}; AxisSpec qvecAxis = {200, 0, 20, "q"}; if (doprocessSE) { @@ -241,10 +252,12 @@ struct Kstarpbpb { histos.add("hSparseV2SASameEventRotational_V2", "hSparseV2SASameEventRotational_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); } if (fillSA) { - histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + if (fillLikeSignSA) { + histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + } if (fillRotation) { - histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); } if (cfgSAFrame.value == kRandomEventPlane) { histos.add("hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); @@ -257,7 +270,7 @@ struct Kstarpbpb { histos.add("hSparseV2SAMixedEvent_V2", "hSparseV2SAMixedEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); } if (fillSA) { - histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); + histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); } } @@ -292,7 +305,6 @@ struct Kstarpbpb { if (doprocessSE && additionalQAplots1) { histos.add("hFTOCvsTPCSelected", "Mult correlation FT0C vs. TPC after selection", kTH2F, {{80, 0.0f, 80.0f}, {100, -0.5f, 5999.5f}}); histos.add("hCentrality", "Centrality distribution", kTH1F, {{200, 0.0, 200.0}}); - histos.add("hOccupancy", "Occupancy distribution", kTH1F, {occupancyAxis}); histos.add("hVtxZ", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("hPsiFT0C", "PsiFT0C", kTH2F, {centAxis, phiAxis}); histos.add("hPsiFT0A", "PsiFT0A", kTH2F, {centAxis, phiAxis}); @@ -303,9 +315,9 @@ struct Kstarpbpb { histos.add("ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); histos.add("ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); histos.add("ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); - histos.add("ResTrackSPFT0CTPC", "ResTrackSPFT0CTPC", kTH3F, {centAxis, occupancyAxis, resAxis}); - histos.add("ResTrackSPFT0CFT0A", "ResTrackSPFT0CFT0A", kTH3F, {centAxis, occupancyAxis, resAxis}); - histos.add("ResTrackSPFT0ATPC", "ResTrackSPFT0ATPC", kTH3F, {centAxis, occupancyAxis, resAxis}); + histos.add("ResTrackSPFT0CTPC", "ResTrackSPFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("ResTrackSPFT0CFT0A", "ResTrackSPFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("ResTrackSPFT0ATPC", "ResTrackSPFT0ATPC", kTH2F, {centAxis, resAxis}); histos.add("hQFT0CvsCent", "q_{FT0C} vs centrality", kTH2F, {centAxis, qvecAxis}); histos.add("hQFT0CvsVz", "q_{FT0C} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); histos.add("hQFT0AvsCent", "q_{FT0A} vs centrality", kTH2F, {centAxis, qvecAxis}); @@ -366,24 +378,59 @@ struct Kstarpbpb { histos.add("phi/hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); } if (doprocessSEPhi) { - histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); - histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); } if (doprocessMEPhi) { - histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); - histos.add("phi/hSparseV2MixedEventCosThetaStar", "hSparseV2MixedEventCosThetaStar", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MixedEventCosThetaStar", "hSparseV2MixedEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); } if (doprocessMCPhi) { histos.add("phi/hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); histos.add("phi/h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); histos.add("phi/CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); - histos.add("phi/hSparseV2MCGenSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); - histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); - histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); - histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseD, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCGenSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); } } + if (doprocessEvtLossSigLossMC) { + const AxisSpec impactParAxis{evtSigLoss.axisImpactPar, "Impact parameter (fm)"}; + const AxisSpec multMCAxis{evtSigLoss.axisMultMC, "N_{ch}^{gen} (|#eta| < 0.5)"}; + const AxisSpec centLossAxis{100, 0.0, 100.0, "Centrality FT0C (%)"}; + const AxisSpec ptKstarAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec ptPhiAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + histos.add("evtSigLoss/MCEventHist", "MC event statistics", kTH1F, {{4, 0.5, 4.5}}); + auto hstat = histos.get(HIST("evtSigLoss/MCEventHist")); + hstat->GetXaxis()->SetBinLabel(1, "All MC events"); + hstat->GetXaxis()->SetBinLabel(2, "MC events with selected reco event"); + hstat->GetXaxis()->SetBinLabel(3, "MC events with no reco event"); + hstat->GetXaxis()->SetBinLabel(4, "MC events with >1 reco event"); + // event loss + histos.add("evtSigLoss/hImpactParameterGen", "Impact parameter, all generated events", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParameterRec", "Impact parameter, generated events with selected reco event", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParameterGenNoReco", "Impact parameter, generated events with no reco event", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParVsCentRec", "Impact parameter vs centrality of the selected reco event", kTH2F, {centLossAxis, impactParAxis}); + histos.add("evtSigLoss/hMultEta05Gen", "N_{ch}^{gen}, all generated events", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05Rec", "N_{ch}^{gen}, generated events with selected reco event", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05GenNoReco", "N_{ch}^{gen}, generated events with no reco event", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05VsCentRec", "N_{ch}^{gen} vs centrality of the selected reco event", kTH2F, {centLossAxis, multMCAxis}); + // signal loss: K*0 + anti-K*0 + histos.add("evtSigLoss/hKstarGenBeforeEvtSel", "Generated K*0, all events", kTH2F, {ptKstarAxis, impactParAxis}); + histos.add("evtSigLoss/hKstarGenAfterEvtSel", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, impactParAxis}); + histos.add("evtSigLoss/hKstarGenVsMultBeforeEvtSel", "Generated K*0, all events", kTH2F, {ptKstarAxis, multMCAxis}); + histos.add("evtSigLoss/hKstarGenVsMultAfterEvtSel", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, multMCAxis}); + histos.add("evtSigLoss/hKstarGenAfterEvtSelVsCent", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, centLossAxis}); + // signal loss: phi(1020) + histos.add("evtSigLoss/hPhiGenBeforeEvtSel", "Generated phi, all events", kTH2F, {ptPhiAxis, impactParAxis}); + histos.add("evtSigLoss/hPhiGenAfterEvtSel", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, impactParAxis}); + histos.add("evtSigLoss/hPhiGenVsMultBeforeEvtSel", "Generated phi, all events", kTH2F, {ptPhiAxis, multMCAxis}); + histos.add("evtSigLoss/hPhiGenVsMultAfterEvtSel", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, multMCAxis}); + histos.add("evtSigLoss/hPhiGenAfterEvtSelVsCent", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, centLossAxis}); + } + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -524,6 +571,17 @@ struct Kstarpbpb { return psiSA; } + // phi(1020) -> K+K- with one kaon taken as the pion: true if the KK mass is inside the veto window (always false if cfgPhiVeto is off) + bool isPhiReflection(const ROOT::Math::PxPyPzMVector& kaon, const ROOT::Math::PxPyPzMVector& pion) + { + if (!cfgPhiVeto) { + return false; + } + ROOT::Math::PxPyPzMVector kaonAsKaon(kaon.Px(), kaon.Py(), kaon.Pz(), MassKa); + ROOT::Math::PxPyPzMVector pionAsKaon(pion.Px(), pion.Py(), pion.Pz(), MassKa); + return std::abs((kaonAsKaon + pionAsKaon).M() - o2::constants::physics::MassPhi) < cfgPhiVetoWindow; + } + // quantization axis = normal to the SA plane ROOT::Math::XYZVector getSAAxis(const ROOT::Math::PxPyPzMVector& mother, double psiSA) { @@ -566,117 +624,53 @@ struct Kstarpbpb { return collision.sel8() && collision.triggereventep() && selectionEventBits(collision); } - // full data event selection without the occupancy cut + // full data event selection template bool selectionEvent(const TCollision& collision) { return selectionEventRCT(collision) && selectionEventCuts(collision); } - template - bool selectionOccupancy(const TCollision& collision) - { - return !(fillOccupancy && collision.trackOccupancyInTimeRange() > cfgOccupancyCut); - } - - // mixed-event pair; occupancy rejects the pair only if both events are above the cut + // mixed-event pair: both events selected and not from the same bunch crossing template bool selectionEventPairME(const TCollision& collision1, const TCollision& collision2) { if (!selectionEvent(collision1) || !selectionEvent(collision2)) { return false; } - if (collision1.bcId() == collision2.bcId()) { - return false; - } - return !(fillOccupancy && collision1.trackOccupancyInTimeRange() >= cfgOccupancyCut && collision2.trackOccupancyInTimeRange() >= cfgOccupancyCut); + return collision1.bcId() != collision2.bcId(); } - // ---------------- phi(1020) track and PID selections, identical to phipbpb.cxx ---------------- + // ---------------- phi(1020) track and PID selections, identical to phianalysisrun3pbpb.cxx ---------------- template bool selectionTrackPhi(const T& candidate) { - // table-level PIDcutFilter of phipbpb.cxx, applied here because this task also selects pions - if (!(std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC)) { - return false; - } - if (phiSA.useGlobalTrack && !phiSA.useDcaSyst && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > phiSA.cfgITScluster && candidate.tpcNClsCrossedRows() > phiSA.cfgTPCcluster && candidate.tpcFractionSharedCls() < phiSA.cfgTPCSharedcluster)) { - return false; - } - if (phiSA.useGlobalTrack && phiSA.useDcaSyst && !(candidate.itsNCls() > phiSA.cfgITScluster && candidate.tpcNClsCrossedRows() > phiSA.cfgTPCcluster && candidate.tpcFractionSharedCls() < phiSA.cfgTPCSharedcluster)) { + if (phiSA.iscustomDCAcut && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > phiSA.cfgITScluster)) { return false; } - if (!phiSA.useGlobalTrack && !(candidate.tpcNClsFound() > phiSA.cfgTPCcluster)) { + if (phiSA.ismanualDCAcut && !(candidate.isGlobalTrackWoDCA() && candidate.isPVContributor() && std::abs(candidate.dcaXY()) < phiSA.cfgCutDCAxy && std::abs(candidate.dcaZ()) < phiSA.cfgCutDCAz && candidate.itsNCls() > phiSA.cfgITScluster)) { return false; } return true; } - static constexpr float PhiTPCOnlyPt = 0.5f; + // kaon PID: circular TPC-TOF cut if TOF is present, TPC-only otherwise (or always, if isNoTOF) template - bool selectionPIDpTdependentPhi(const T& candidate) + bool selectionPIDPhi(const T& candidate) { - if (candidate.pt() < PhiTPCOnlyPt && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { - return true; - } - if (candidate.pt() >= PhiTPCOnlyPt && candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF) { + if (!phiSA.isNoTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (phiSA.nsigmaCutCombined * phiSA.nsigmaCutCombined)) { return true; } - if (!phiSA.useGlobalTrack && !candidate.hasTPC()) { - return true; - } - return false; - } - - template - bool selectionPIDPhi(const T& candidate) - { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { + if (!phiSA.isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { return true; } - if (candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF) { + if (phiSA.isNoTOF && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { return true; } return false; } - template - bool selectionPIDTOFOnlyPhi(const T& candidate) - { - return candidate.hasTOF() && candidate.beta() > phiSA.cfgCutTOFBeta && std::abs(candidate.tofNSigmaKa()) < phiSA.nsigmaCutTOF; - } - - // data PID: pT-dependent, standard or TOF-only - template - bool selectionKaonPhi(const T& candidate) - { - if (phiSA.ispTdepPID && !phiSA.isTOFOnly && !selectionPIDpTdependentPhi(candidate)) { - return false; - } - if (!phiSA.ispTdepPID && !phiSA.isTOFOnly && !selectionPIDPhi(candidate)) { - return false; - } - if (phiSA.isTOFOnly && !selectionPIDTOFOnlyPhi(candidate)) { - return false; - } - return true; - } - - static constexpr float PhiITSMaxP = 1.0f; - static constexpr float PhiITSNSigma = 2.5f; - template - bool selectionITSPhi(const T& candidate) - { - if (phiSA.useGlobalTrack && candidate.p() < PhiITSMaxP) { - auto nSigmaITS = itsResponse.nSigmaITS(candidate); - if (!(nSigmaITS > -PhiITSNSigma && nSigmaITS < PhiITSNSigma)) { - return false; - } - } - return true; - } - // deep angle cut on pair to remove photon conversion template bool selectionPairPhi(const T1& candidate1, const T2& candidate2) @@ -717,14 +711,12 @@ struct Kstarpbpb { ConfigurableAxis axisEPAngle{"axisEPAngle", {6, -HalfPI, HalfPI}, "event plane angle"}; - ConfigurableAxis axisOccup{"axisOccup", {20, -0.5, 40000.0}, "occupancy axis"}; using BinningTypeVertexContributor = ColumnBinningPolicy; int currentRunNumber = -999; int lastRunNumber = -999; TH2D* hweight = nullptr; - o2::aod::ITSResponse itsResponse; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { // scratch vectors: local to this call, never carry state across events/tracks @@ -746,16 +738,12 @@ struct Kstarpbpb { histos.fill(HIST("hEvtSelInfo"), 2.5); auto centrality = collision.centFT0C(); auto multTPC = collision.multNTracksPV(); - int occupancy = collision.trackOccupancyInTimeRange(); auto psiFT0C = collision.psiFT0C(); auto psiFT0A = collision.psiFT0A(); auto psiTPC = collision.psiTPC(); auto qFT0C = collision.qFT0C(); auto qFT0A = collision.qFT0A(); auto qTPC = collision.qTPC(); - if (!selectionOccupancy(collision)) { - return; - } histos.fill(HIST("hEvtSelInfo"), 3.5); if (additionalQAplots1) { histos.fill(HIST("hFTOCvsTPCSelected"), centrality, multTPC); @@ -769,7 +757,6 @@ struct Kstarpbpb { histos.fill(HIST("ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (psiFT0C - psiFT0A))); histos.fill(HIST("ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); histos.fill(HIST("hCentrality"), centrality); - histos.fill(HIST("hOccupancy"), occupancy); histos.fill(HIST("hVtxZ"), collision.posZ()); histos.fill(HIST("hQFT0CvsCent"), centrality, qFT0C); histos.fill(HIST("hQFT0CvsVz"), collision.posZ(), qFT0C); @@ -855,6 +842,9 @@ struct Kstarpbpb { if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } + if (isPhiReflection(daughter1, daughter2)) { // phi(1020) veto (cfgPhiVeto); also skips the rotations of this pair + continue; + } auto phiMinusPsi = getPhiInRange(kstarMother.Phi() - psiFT0C); if (useSP) { @@ -869,9 +859,9 @@ struct Kstarpbpb { totalweight = 1.0; } if (additionalQAplots1) { - histos.fill(HIST("ResTrackSPFT0CTPC"), centrality, occupancy, qFT0C * qTPC * std::cos(2.0 * (psiFT0C - psiTPC))); - histos.fill(HIST("ResTrackSPFT0CFT0A"), centrality, occupancy, qFT0C * qFT0A * std::cos(2.0 * (psiFT0C - psiFT0A))); - histos.fill(HIST("ResTrackSPFT0ATPC"), centrality, occupancy, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + histos.fill(HIST("ResTrackSPFT0CTPC"), centrality, qFT0C * qTPC * std::cos(2.0 * (psiFT0C - psiTPC))); + histos.fill(HIST("ResTrackSPFT0CFT0A"), centrality, qFT0C * qFT0A * std::cos(2.0 * (psiFT0C - psiFT0A))); + histos.fill(HIST("ResTrackSPFT0ATPC"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); } if (!fillSA) { @@ -909,7 +899,7 @@ struct Kstarpbpb { } else { histos.fill(HIST("hSparseSAvsrapsameunlike"), kstarMother.M(), kstarMother.Pt(), sa, kstarMother.Rapidity(), centrality); } - } else if (track1Sign * track2Sign > 0) { + } else if (fillLikeSignSA && track1Sign * track2Sign > 0) { if (usepolar) { histos.fill(HIST("hSparseSAvsrapsamelike"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); } else { @@ -933,6 +923,9 @@ struct Kstarpbpb { if (std::abs(kstarrot.Rapidity()) > confRapidity) { continue; } + if (isPhiReflection(kaonrot, daughter2)) { // phi(1020) veto (cfgPhiVeto) on the rotated pair + continue; + } auto phiMinusPsiRot = getPhiInRange(kstarrot.Phi() - psiFT0C); if (useSP) { @@ -1015,6 +1008,9 @@ struct Kstarpbpb { if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } + if (isPhiReflection(daughter1, daughter2)) { // phi(1020) veto (cfgPhiVeto), same as in the same event + continue; + } int s1 = track1.sign(); int s2 = track2.sign(); @@ -1116,7 +1112,9 @@ struct Kstarpbpb { auto track1ID = track1.index(); for (const auto& track2 : rectrackspart) { auto track2ID = track2.index(); - if (track2ID <= track1ID) { + // roles are fixed (track1 = kaon candidate, track2 = pion candidate, checked against the MC PDG below), + // so every true K pi pair is found exactly once whatever the track order; only skip the same track + if (track2ID == track1ID) { continue; } if (!selectionTrack(track2)) { @@ -1158,7 +1156,8 @@ struct Kstarpbpb { if (std::abs(mothertrack1.y()) > confRapidity) { continue; } - if (pdgcheck && mothertrack1.pdgCode() != o2::constants::physics::kK0Star892) { + // K*0 and anti-K*0, as in data (all unlike-sign K pi pairs) + if (pdgcheck && std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } if (!isTOFOnly && !(strategySelectionPID(track1, 0, strategyPID) || strategySelectionPID(track2, 1, strategyPID))) { @@ -1179,6 +1178,9 @@ struct Kstarpbpb { if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } + if (isPhiReflection(kaonPlus, pionMinus)) { // phi(1020) veto (cfgPhiVeto), so the efficiency includes the true K* it removes + continue; + } auto phiMinusPsi = getPhiInRange(kstarMother.Phi() - psiFT0C); v2 = std::cos(2.0 * phiMinusPsi); @@ -1208,7 +1210,8 @@ struct Kstarpbpb { if (std::abs(mcParticle.y()) > confRapidity) { continue; } - if (pdgcheck && mcParticle.pdgCode() != o2::constants::physics::kK0Star892) { + // K*0 and anti-K*0, as in data and in the reconstructed loop above + if (pdgcheck && std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } auto kDaughters = mcParticle.daughters_as(); @@ -1217,13 +1220,15 @@ struct Kstarpbpb { if (kDaughters.size() != NumberOfDaughters) { continue; } + // daughtp = kaon daughter found, daughtm = pion daughter found (either charge: K+ pi- or K- pi+); + // kaonPlus / pionMinus hold the kaon / pion daughter, so the boost uses the kaon as in the reconstructed loop auto daughtp = false; auto daughtm = false; for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } - if (kCurrentDaughter.pdgCode() == +PDG_t::kKPlus) { + if (std::abs(kCurrentDaughter.pdgCode()) == PDG_t::kKPlus) { if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtp = true; } @@ -1231,7 +1236,7 @@ struct Kstarpbpb { daughtp = true; } kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); - } else if (kCurrentDaughter.pdgCode() == -PDG_t::kPiPlus) { + } else if (std::abs(kCurrentDaughter.pdgCode()) == PDG_t::kPiPlus) { if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtm = true; } @@ -1455,7 +1460,7 @@ struct Kstarpbpb { void processSEPhi(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!selectionEvent(collision) || !selectionOccupancy(collision)) { + if (!selectionEvent(collision)) { return; } auto centrality = collision.centFT0C(); @@ -1469,14 +1474,14 @@ struct Kstarpbpb { if (!(track1.signed1Pt() > cfgCutCharge.value)) { // positive kaon continue; } - if (!selectionTrackPhi(track1) || !selectionKaonPhi(track1) || !selectionITSPhi(track1)) { + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1)) { continue; } for (const auto& track2 : tracks) { if (!(track2.signed1Pt() < cfgCutCharge.value)) { // negative kaon continue; } - if (!selectionTrackPhi(track2) || !selectionKaonPhi(track2)) { + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2)) { continue; } if (!selectionPairPhi(track1, track2)) { @@ -1485,14 +1490,14 @@ struct Kstarpbpb { if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { continue; } - if (!selectionITSPhi(track2)) { - continue; - } kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); phiMother = kaonPlusPhi + kaonMinusPhi; - auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); auto absRapidity = std::abs(phiMother.Rapidity()); + if (absRapidity > phiSA.confRapidity) { + continue; + } + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2SameEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2SameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); } @@ -1516,13 +1521,10 @@ struct Kstarpbpb { if (track1.sign() * track2.sign() > 0) { continue; } - if (!selectionITSPhi(track1) || !selectionITSPhi(track2)) { - continue; - } if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { continue; } - if (!selectionKaonPhi(track1) || !selectionKaonPhi(track2)) { + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { continue; } if (!selectionPairPhi(track1, track2)) { @@ -1539,8 +1541,11 @@ struct Kstarpbpb { kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); } phiMother = kaonPlusPhi + kaonMinusPhi; - auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); auto absRapidity = std::abs(phiMother.Rapidity()); + if (absRapidity > phiSA.confRapidity) { + continue; + } + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MixedEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2MixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); } @@ -1586,10 +1591,7 @@ struct Kstarpbpb { if (!selectionTrackPhi(track1)) { continue; } - if (phiSA.ispTdepPID && !selectionPIDpTdependentPhi(track1)) { - continue; - } - if (!phiSA.ispTdepPID && !selectionPIDPhi(track1)) { + if (!selectionPIDPhi(track1)) { continue; } if (!track1.has_mcParticle()) { @@ -1604,10 +1606,7 @@ struct Kstarpbpb { if (!selectionTrackPhi(track2)) { continue; } - if (phiSA.ispTdepPID && !selectionPIDpTdependentPhi(track2)) { - continue; - } - if (!phiSA.ispTdepPID && !selectionPIDPhi(track2)) { + if (!selectionPIDPhi(track2)) { continue; } if (!track2.has_mcParticle()) { @@ -1641,9 +1640,6 @@ struct Kstarpbpb { if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { continue; } - if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { - continue; - } if (phiSA.avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { histos.fill(HIST("phi/h1PhiRecsplit"), mothertrack1.pt()); continue; @@ -1657,6 +1653,10 @@ struct Kstarpbpb { kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); } phiMother = kaonPlusPhi + kaonMinusPhi; + // reconstructed-pair rapidity, same cut as in processSEPhi / processMEPhi (true-mother y is cut above) + if (std::abs(phiMother.Rapidity()) > phiSA.confRapidity) { + continue; + } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); histos.fill(HIST("phi/hSparseV2MCRecSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); @@ -1702,6 +1702,118 @@ struct Kstarpbpb { } // rec collision loop } PROCESS_SWITCH(Kstarpbpb, processMCPhi, "Process MC phi(1020) spin alignment", false); + + // ================= event loss and signal loss for K* and phi(1020) ================= + // adapted from processEvtLossSigLossMC1 in phianalysisrun3pbpb.cxx. The accepted MC event uses the same reconstructed-event + // selection as processMC / processMCPhi (sel8, additionalEvSel1-4, |vz|) and, by default, the same single-reco-collision + // requirement; the generated K* and phi use the same rapidity and daughter conditions as the generated loops there. + void processEvtLossSigLossMC(McCollisionMults::iterator const& mcCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles) + { + if (evtSigLoss.cutVzGen && std::abs(mcCollision.posZ()) > cfgCutVertex) { + return; + } + if (evtSigLoss.isApplyInelgt0 && !mcCollision.isInelGt0()) { + return; + } + if (evtSigLoss.isApplyTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { + return; + } + const float impactPar = mcCollision.impactParameter(); + const float multMC = mcCollision.multMCNParticlesEta05(); + + // all generated events + histos.fill(HIST("evtSigLoss/MCEventHist"), 1); + histos.fill(HIST("evtSigLoss/hImpactParameterGen"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05Gen"), multMC); + if (RecCollisions.size() == 0) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 3); + histos.fill(HIST("evtSigLoss/hImpactParameterGenNoReco"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05GenNoReco"), multMC); + } + if (RecCollisions.size() > 1) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 4); + } + + // generated events with a selected reconstructed collision (event loss) + bool selected = false; + float centrality = -999.f; + if (!evtSigLoss.requireSingleReco || RecCollisions.size() == 1) { + for (const auto& RecCollision : RecCollisions) { + if (!RecCollision.sel8() || !selectionEventBits(RecCollision) || std::abs(RecCollision.posZ()) > cfgCutVertex) { + continue; + } + selected = true; + centrality = RecCollision.centFT0C(); + } + } + if (selected) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 2); + histos.fill(HIST("evtSigLoss/hImpactParameterRec"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05Rec"), multMC); + histos.fill(HIST("evtSigLoss/hImpactParVsCentRec"), centrality, impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05VsCentRec"), centrality, multMC); + } + + // generated K*0 / anti-K*0 and phi(1020) (signal loss) + static constexpr std::size_t NumberOfDaughters = 2; + for (const auto& mcParticle : GenParticles) { + const int pdgMother = std::abs(mcParticle.pdgCode()); + if (pdgMother != o2::constants::physics::kK0Star892 && mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + const bool isKstar = (pdgMother == o2::constants::physics::kK0Star892); + if (std::abs(mcParticle.y()) > (isKstar ? confRapidity.value : phiSA.confRapidity.value)) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false, hasPion = false, hasKPlus = false, hasKMinus = false; + ROOT::Math::PxPyPzMVector dau1, dau2; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + if (isKstar) { + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = true; + dau1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = true; + dau2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } else { + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = true; + dau1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = true; + dau2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + } + const double pt = (dau1 + dau2).Pt(); + if (isKstar && hasKaon && hasPion) { + histos.fill(HIST("evtSigLoss/hKstarGenBeforeEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hKstarGenVsMultBeforeEvtSel"), pt, multMC); + if (selected) { + histos.fill(HIST("evtSigLoss/hKstarGenAfterEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hKstarGenVsMultAfterEvtSel"), pt, multMC); + histos.fill(HIST("evtSigLoss/hKstarGenAfterEvtSelVsCent"), pt, centrality); + } + } else if (!isKstar && hasKPlus && hasKMinus) { + histos.fill(HIST("evtSigLoss/hPhiGenBeforeEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hPhiGenVsMultBeforeEvtSel"), pt, multMC); + if (selected) { + histos.fill(HIST("evtSigLoss/hPhiGenAfterEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hPhiGenVsMultAfterEvtSel"), pt, multMC); + histos.fill(HIST("evtSigLoss/hPhiGenAfterEvtSelVsCent"), pt, centrality); + } + } + } + } + PROCESS_SWITCH(Kstarpbpb, processEvtLossSigLossMC, "Process event loss and signal loss for K* and phi(1020)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { From e2a75a1a92d77c5634f091520bcde85376a3950a Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Sat, 26 Sep 2026 23:23:57 +0200 Subject: [PATCH 085/648] [PWGHF] Add possibility to run more loops on MC particles in dataCreatorCharmHadPiReduced (#18067) --- .../dataCreatorCharmHadPiReduced.cxx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 07f0dae9c3d..6d09d96b998 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -177,6 +177,7 @@ struct HfDataCreatorCharmHadPiReduced { Configurable invMassWindowCharmHadPi{"invMassWindowCharmHadPi", 0.3, "invariant-mass window for CharmHad-Pi pair preselections (GeV/c2)"}; // MC extra Configurable checkDecayTypeMc{"checkDecayTypeMc", false, "flag to enable MC checks on decay type"}; + Configurable nMcLoops{"nMcLoops", 0, "number of extra loops over generated particles per MC collision"}; } configs; // vertexing struct : o2::framework::ConfigurableGroup { @@ -394,6 +395,9 @@ struct HfDataCreatorCharmHadPiReduced { hCandidatesLc = registry.add("hCandidatesLc", "Lc candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesD0FromDstar = registry.add("hCandidatesD0FromDstar", "D0 from D* candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesBHadron = registry.add("hCandidatesBHadron", "B hadron candidate counter", {HistType::kTH1D, {axisCands}}); + if (configs.nMcLoops > 0) { + registry.add("hNMatchedMcLoops", "matches found in extra MC loops;matches per MC collision;entries", {HistType::kTH1D, {{100, -0.5, 99.5}}}); + } setLabelHistoCands(hCandidatesD0); setLabelHistoCands(hCandidatesDPlus); @@ -1793,6 +1797,20 @@ struct HfDataCreatorCharmHadPiReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } } // gen + + // extra loops over generated particles (increases CPU time) + if (configs.nMcLoops > 0) { + int nMatched{0}; + for (int iLoop = 0; iLoop < configs.nMcLoops; ++iLoop) { + for (const auto& particle : mcParticlesPerMcColl) { + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kB0, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBPlus, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBS, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kLambdaB0, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + } + } + registry.fill(HIST("hNMatchedMcLoops"), nMatched); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// From 1e7970b34a6f282bec27e19c8912fa790e178317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Sun, 27 Sep 2026 00:53:58 +0200 Subject: [PATCH 086/648] [PWGLF] Update hadronnucleicorrelation.cxx (#18042) - streamline axes - add possibility to select on Y --- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 147 ++++++++++-------- 1 file changed, 81 insertions(+), 66 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 120e5893138..50afd592225 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -145,13 +145,12 @@ struct HadronNucleiCorrelation { // pT/A bins Configurable> pTBins{"pTBins", {0.6f, 1.0f, 1.2f, 2.f}, "p_{T} bins"}; - ConfigurableAxis axisNSigma{"axisNSigma", {35, -7.f, 7.f}, "n#sigma"}; - ConfigurableAxis deltaPhiAxis = {"deltaPhiAxis", {46, -1 * o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}, "#Delta#phi (rad)"}; - struct : ConfigurableGroup { std::string prefix = "axes"; // JSON group name ConfigurableAxis axisDeltaEta{"axisDeltaEta", {300, -1.5, 1.5}, "#Delta#eta"}; ConfigurableAxis axisDeltaRap{"axisDeltaRap", {300, -1.5, 1.5}, "#Delta y"}; + ConfigurableAxis axisDeltaPhi{"deltaPhiAxis", {46, -1 * o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}, "#Delta #phi"}; + ConfigurableAxis axisNSigma{"axisNSigma", {35, -7.f, 7.f}, "n#sigma"}; } settingsAxes; using FilteredCollisions = soa::Filtered; @@ -259,6 +258,7 @@ struct HadronNucleiCorrelation { LOG(info) << "DeltaEta axis: from " << deltaEtaAxis.binEdges[0] << " to " << deltaEtaAxis.binEdges[1]; const AxisSpec deltaRapAxis = {settingsAxes.axisDeltaRap, "#Delta y"}; LOG(info) << "DeltaRap axis: from " << deltaRapAxis.binEdges[0] << " to " << deltaRapAxis.binEdges[1]; + const AxisSpec deltaPhiAxis = {settingsAxes.axisDeltaPhi, "#Delta#phi"}; if (doprocessSameEvent || doprocessSameEventEvSel) { registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{7, 0.f, 7.f}}}); @@ -365,15 +365,24 @@ struct HadronNucleiCorrelation { } } + const AxisSpec dcaPtAxis = {100, 0.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; if (doprocessSameEvent || doprocessSameEventEvSel || doprocessMC) { - registry.add("hPrDCAxy", "DCAxy p", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hDeDCAxy", "DCAxy d", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); + const AxisSpec dcaXyAxis = {600, -3.f, 3.f, "DCA xy (cm)"}; + registry.add("hPrDCAxy", "DCAxy p", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hDeDCAxy", "DCAxy d", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); } registry.add("hMult", "multiplicity", {HistType::kTH1D, {{200, 0.f, 200.f, "N_{ch}"}}}); + const AxisSpec tofNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma TOF"}; + const AxisSpec tpcNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma TPC"}; + const AxisSpec itsNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma ITS"}; + if (doQA && (doprocessSameEvent || doprocessSameEventEvSel || doprocessMC)) { + const AxisSpec dcaXyAxis = {200, -0.2f, 0.2f, "DCA xy (cm)"}; + const AxisSpec dcaZAxis = {200, -0.2f, 0.2f, "DCA z (cm)"}; + // Track QA registryQa.add("QA/hVtxZ_trk", "#it{z}_{vtx}", {HistType::kTH1D, {{150, -15.f, 15.f, "#it{z}_{vtx} (cm)"}}}); registryQa.add("QA/hTPCnClusters", "N TPC Clusters; N TPC Clusters", {HistType::kTH1D, {{200, 0.f, 200.f}}}); @@ -381,16 +390,13 @@ struct HadronNucleiCorrelation { registryQa.add("QA/hTPCchi2", "TPC chi2/Ncls; TPC chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 10.f}}}); registryQa.add("QA/hTPCcrossedRowsOverFindableCls", "TPC crossed Rows Over Findable Cls; TPC Crossed Rows Over Findable Cls", {HistType::kTH1D, {{100, 0.f, 2.f}}}); registryQa.add("QA/hITSchi2", "ITS chi2/Ncls; ITS chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 20.f}}}); - registryQa.add("QA/hDCAxy", "DCAxy", {HistType::kTH2D, {{200, -0.2f, 0.2f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registryQa.add("QA/hDCAz", "DCAz", {HistType::kTH2D, {{200, -0.2f, 0.2f, "DCA z (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); + registryQa.add("QA/hDCAxy", "DCAxy", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registryQa.add("QA/hDCAz", "DCAz", {HistType::kTH2D, {dcaZAxis, dcaPtAxis}}); registryQa.add("QA/TPCChi2VsPZ", "TPCChi2VsPZ", {HistType::kTH2D, {{100, 0.f, 10.f, "p_{TPC}/Z (GeV/c)"}, {120, 0.f, 6.f, "TPC Chi2"}}}); - const AxisSpec tofNSigmaAxis = {axisNSigma, "n#sigma TOF"}; - const AxisSpec tpcNSigmaAxis = {axisNSigma, "n#sigma TPC"}; - const AxisSpec itsNSigmaAxis = {axisNSigma, "n#sigma ITS"}; - registryQa.add("QA/h3dTPCTOF_Pr", "n#sigma TPC vs n#sigma TOF; n#sigma TPC;n#sigma TOF;p_{T} (GeV/c)", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); - registryQa.add("QA/h3dTPCTOF_AntiPr", "n#sigma TPC vs n#sigma TOF; n#sigma TPC;n#sigma TOF;p_{T} (GeV/c)", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); - registryQa.add("QA/h3dTPCTOF_De", "n#sigma TPC vs n#sigma TOF; n#sigma TPC;n#sigma TOF;p_{T} (GeV/c)", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); - registryQa.add("QA/h3dTPCTOF_AntiDe", "n#sigma TPC vs n#sigma TOF; n#sigma TPC;n#sigma TOF;p_{T} (GeV/c)", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_Pr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiPr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_De", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiDe", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_El", "n#sigma TPC vs p_{T} for e hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_Pr", "n#sigma TPC vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_De", "n#sigma TPC vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); @@ -447,52 +453,59 @@ struct HadronNucleiCorrelation { registry.add("hSec_EtaPhiPt_Proton", "Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); registry.add("hPrimSec_EtaPhiPt_Proton", "Primary + Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); - registry.add("hnSigmaTPCVsPt_Pr_MC", "n#sigma TPC vs p_{T} for p hypothesis true MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2F, {ptAxis, axisNSigma}}); - registry.add("hnSigmaTPCVsPt_De_MC", "n#sigma TPC vs p_{T} for d hypothesis true MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2F, {ptAxis, axisNSigma}}); - registry.add("hnSigmaTOFVsPt_Pr_MC", "n#sigma TOF vs p_{T} for p hypothesis true MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2F, {ptAxis, axisNSigma}}); - registry.add("hnSigmaTOFVsPt_De_MC", "n#sigma TOF vs p_{T} for d hypothesis true MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2F, {ptAxis, axisNSigma}}); + registry.add("hnSigmaTPCVsPt_Pr_MC", "n#sigma TPC vs p_{T} for p hypothesis true MC", {HistType::kTH2F, {ptAxis, tpcNSigmaAxis}}); + registry.add("hnSigmaTPCVsPt_De_MC", "n#sigma TPC vs p_{T} for d hypothesis true MC", {HistType::kTH2F, {ptAxis, tpcNSigmaAxis}}); + registry.add("hnSigmaTOFVsPt_Pr_MC", "n#sigma TOF vs p_{T} for p hypothesis true MC", {HistType::kTH2F, {ptAxis, tofNSigmaAxis}}); + registry.add("hnSigmaTOFVsPt_De_MC", "n#sigma TOF vs p_{T} for d hypothesis true MC", {HistType::kTH2F, {ptAxis, tofNSigmaAxis}}); - registry.add("hResPt_Proton", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_Deuteron", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_AntiProton", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_AntiDeuteron", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); + const AxisSpec genPtAxis = {100, 0.f, 10.f, "p_{T}(gen) (GeV/c)"}; + const AxisSpec resPtAxis = {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) (GeV/c)"}; + registry.add("hResPt_Proton", "p pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_Deuteron", "d pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_AntiProton", "#bar{p} pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_AntiDeuteron", "#bar{d} pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); - registry.add("hNumeratorPurity_Proton", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton", " p(#bar{p}); p_{T} (GeV/c);(S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); if (doMCQA) { - registry.add("hResEta_Proton", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResEta_Deuteron", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResPhi_Proton", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResPhi_Deuteron", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResEta_AntiProton", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResEta_AntiDeuteron", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResPhi_AntiProton", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResPhi_AntiDeuteron", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - - registry.add("hNumeratorPurity_Proton_TPC", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron_TPC", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Proton_TPCTOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron_TPCTOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Proton_TPC_or_TOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron_TPC_or_TOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Proton_TPCEl_or_TOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Proton_TPCEl", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron_TPCEl", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hNumeratorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton_TPC", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron_TPC", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton_TPCTOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron_TPCTOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton_TPC_or_TOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron_TPC_or_TOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton_TPCEl", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Proton_TPCEl_or_TOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron_TPCEl", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hDenominatorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {ptAxisSmall}}); + const AxisSpec genEtaAxis = {100, -1.f, 1.f, "#eta(gen)"}; + const AxisSpec resEtaAxis = {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}; + + const AxisSpec genPhiAxis = {100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}; + const AxisSpec resPhiAxis = {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}; + registry.add("hResEta_Proton", "p eta residual", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResEta_Deuteron", "d eta residual ", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResPhi_Proton", "p phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResPhi_Deuteron", "d phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResEta_AntiProton", "p eta residual", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResEta_AntiDeuteron", "p eta residual ", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResPhi_AntiProton", "p phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResPhi_AntiDeuteron", "d phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + + registry.add("hNumeratorPurity_Proton_TPC", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPC", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCTOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCTOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPC_or_TOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPC_or_TOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCEl_or_TOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCEl", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCEl", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPC", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPC", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCTOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCTOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPC_or_TOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPC_or_TOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCEl", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCEl_or_TOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCEl", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Proton_TPC", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Deuteron_TPC", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); @@ -784,8 +797,14 @@ struct HadronNucleiCorrelation { if (isPrim && !particle.isPhysicalPrimary()) { return; } - if (std::abs(particle.eta()) > etaCut) { - return; + if (doRapidity) { + if (std::abs(particle.y()) > etaCut) { + return; + } + } else { + if (std::abs(particle.eta()) > etaCut) { + return; + } } const int pdg = particle.pdgCode(); if (pdg != pdgPart0 && pdg != pdgPart1) { @@ -1732,10 +1751,9 @@ struct HadronNucleiCorrelation { } } - if (std::abs(particle.eta()) > etaCut) { - continue; + if (std::abs(particle.eta()) < etaCut) { + fillGeneratedQa(2.5); } - fillGeneratedQa(2.5); // (anti)neutrons are accepted by fillGeneratedQa for QA counting only: they have no // eta-phi-pt histogram and must not reach the switch below (whose default is fatal) @@ -1758,9 +1776,6 @@ struct HadronNucleiCorrelation { case -o2::constants::physics::Pdg::kDeuteron: registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), -1. * particle.pt()); break; - case PDG_t::kNeutron: - case -PDG_t::kNeutron: - break; default: LOG(fatal) << "Unhandled PDG code, should not happen, check the code! " << particle.pdgCode(); break; @@ -1809,7 +1824,7 @@ struct HadronNucleiCorrelation { using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}, true}; - for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, 5, -1, mcCollisions, mcCollisions)) { + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, maxmixcollsGen.value, -1, mcCollisions, mcCollisions)) { const auto& cache1 = genCaches.at(collision1.globalIndex()); const auto& cache2 = genCaches.at(collision2.globalIndex()); From c302af9eae829870324efa6f96c83c6db5fd5b70 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:24:42 +0200 Subject: [PATCH 087/648] [PWGLF] Fix xi0 mass calculation in sigma0builder (#18068) --- PWGLF/TableProducer/Strangeness/sigma0builder.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx index 090103f41db..2808e65d893 100644 --- a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include @@ -2805,7 +2805,7 @@ struct sigma0builder { std::array momPi0{gamma1.px() + gamma2.px(), gamma1.py() + gamma2.py(), gamma1.pz() + gamma2.pz()}; auto arrMom = std::array{momPi0, momLambda}; - cascade.xi0Mass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassXi0}); + cascade.xi0Mass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassLambda0}); // Calculate position covariance matrix auto covVtxV = fitter3Prongs.calcPCACovMatrix(0); From 7ffc10c8bfb34940b73daa24761a391252589590 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sun, 27 Sep 2026 21:07:07 +0800 Subject: [PATCH 088/648] [PWGCF] Fix bugs in charge hist and optimization (#18069) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 571 ++++++++---------- 1 file changed, 247 insertions(+), 324 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index ef6af20e12d..9fb3bed9a13 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -88,7 +88,8 @@ enum EMultiplicityTables { enum ERecSim { eRec = 0, eSim, - eRecAndSim + eRecAndSim, + eRecSim_N }; enum ECuts { @@ -128,11 +129,9 @@ enum EEventHistograms { eEventHistograms_N }; -enum EPhiAndPtHistograms { - ePhiRec = 0, - ePtRec, - ePtMC, - ePhiAndPtHistograms_N +enum EMiscHistograms { + eRunNumber = 0, + eMiscHistograms_N }; enum EExternalHistograms { @@ -171,9 +170,6 @@ static constexpr std::array CutsNames = { // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root { - // *) Misc: - bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwiese, a crash - // *) Base TList to hold all output objects: TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function OutputObj fBaseList{sBaseListName.Data(), @@ -233,30 +229,30 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // **) Particle histograms: struct ParticleHistograms { - TList* fParticleHistogramsList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHistograms{}; + TList* fParticleHistList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHist{}; } pc; // *) Event histograms: struct EventHistograms { - TList* fEventHistogramsList = nullptr; - std::array, 2>, eEventHistograms_N> fEventHistograms{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] + TList* fEventHistList = nullptr; + std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] } ec; - struct PhiAndPtHistograms { - TList* fPhiAndPtHistogramsList = nullptr; - std::array, ePhiAndPtHistograms_N> fPhiAndPtHistograms{}; //! [type][before, after cuts] - } pph; + struct MiscHistograms { + TList* fMiscHistList = nullptr; + TH1D* fMiscHistRunNumber = nullptr; + } misc; // *) External histograms: struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - std::array, eExternalHistograms_N> fWeights{}; //! [type][before, after cuts] + std::array, eExternalHistograms_N> fWeights{}; //! [type][before, after cuts] } ex; struct Observables { TList* fObservablesList = nullptr; - std::array, 2> fProfTwo{}; //! [reco,sim][before, after event cuts] + std::array, 2> fProfTwo{}; //! [reco,sim][before, after event cuts] } obs; // *) Quality assurance histograms: @@ -266,11 +262,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } qa; // *) functions - // how to calculate multiparticle correlation (an example): - // auto QVectorsTable = initQVectorsTable(8, N8); - // (within loop over tracks) updateQVectorsTable(QVectorsTable, phi, weight); - // std::vector resultMultCorr(2, TComplex(0., 0.)); - // resultMultCorr = recursion(8, QVectorsTable, N8); profile_run1->Fill(6.5, (resultMultCorr[0]/resultMultCorr[1].Re()).Re()/(1e-8)); std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { int sum = 0; @@ -288,14 +279,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to { const int maxHarmonic = QVectorsTable.size(); const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); - for (int h = 0; h < maxHarmonic; ++h) { - const auto cosH = std::cos(h * phi); - const auto sinH = std::sin(h * phi); - for (int p = 0; p < maxPower; ++p) { const auto wp = std::pow(weight, p); - QVectorsTable[h][p] += TComplex(wp * cosH, wp * sinH); + QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); } } } @@ -329,7 +316,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } return TComplex::Conjugate(Qvector[-n][p]); }; - std::vector c = {q(harmonic[m - 1], mult), q(0, mult)}; if ((m - 1) == 0) { return c; @@ -424,9 +410,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: - // Algorithm: If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; - // If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; - // for local files both abs and relative paths are just fine. + // Algorithm: If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; for local files both abs and relative paths are just fine. bool bFileIsInAliEn = false; bool bFileIsInCCDB = false; @@ -453,24 +437,22 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, - // for that there is a helper utility function getObjectFromList(...) , see its implementation further below + // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, for that there is a helper utility function getObjectFromList(...) , see its implementation further below listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + return nullptr; } } // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) - } else if (bFileIsInCCDB) { // File you want to access is in your home dir in CCDB: // Remember that here I do not access the file; instead, I directly access the object in that file. - // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case ccdb->setURL("https://alice-ccdb.cern.ch"); baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); if (!baseList) { @@ -483,13 +465,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + return nullptr; } } - // OK, we got the desired TList with efficiency corrections, after that we - // can use the common code for all 3 cases (local, AliEn, CCDB, that - // common code is below) + // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) } else { // this is the local case: // Check if the external ROOT file exists at the specified path: @@ -513,31 +494,35 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { - // baseList->ls(); - // LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, tc.fRunNumber.Data()); + LOGF(error, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + return nullptr; } } } // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: if (!listWithRuns) { - LOGF(fatal, - "\033[1;31m%s: listWithRuns is null for run %s\033[0m", - __FUNCTION__, runNumber); + LOGF(fatal, "\033[1;31m%s: listWithRuns is null for run %s\033[0m", __FUNCTION__, runNumber); + return nullptr; } hist = dynamic_cast(listWithRuns->FindObject(histName)); if (!hist) { - LOGF(fatal, "%s: histogram 'hist1' not found in run list", __FUNCTION__); + LOGF(info, "%s: histogram 'hist' not found in run list", __FUNCTION__); + return nullptr; } - hist->SetDirectory(nullptr); + auto histClone = dynamic_cast(hist->Clone()); + if (!histClone) { + LOGF(error, "%s: histogram 'histClone' failed to be cloned", __FUNCTION__); + return nullptr; + } histClone->SetDirectory(nullptr); delete baseList; return histClone; - } + } // end of TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) // templates template @@ -610,35 +595,34 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void eventHistFill(T1 const& collision, T2 const& tracks) { - auto thisCent = chooseCent(collision); - auto thisRefMult = chooseMult(collision); - if constexpr (rs == eRec || rs == eRecAndSim) { // Fill reconstructed-level event histograms if (rm == eReal) { + auto thisCent = chooseCent(collision); + auto thisRefMult = chooseMult(collision); int multiplicityRec = static_cast(tracks.size()); if constexpr (cuts == eBefore) { - ec.fEventHistograms[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); - ec.fEventHistograms[eHistCentrality][eRec][eBefore]->Fill(thisCent); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); - ec.fEventHistograms[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); - ec.fEventHistograms[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); - ec.fEventHistograms[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); + ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); } if constexpr (cuts == eAfter) { - ec.fEventHistograms[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); - ec.fEventHistograms[eHistCentrality][eRec][eAfter]->Fill(thisCent); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); - ec.fEventHistograms[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); - ec.fEventHistograms[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); - ec.fEventHistograms[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); + ec.fEventHist[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); } } // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed if constexpr (rs == eRecAndSim) { if (!collision.has_mcCollision()) { - LOGF(warning, "No MC collision for this collision, skip..."); + // LOGF(warning, "No MC collision for this collision, skip..."); return; } if (rm == eMC) { @@ -647,21 +631,21 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto impactParameter = thisMCCollision.impactParameter(); auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter if constexpr (cuts == eBefore) { - ec.fEventHistograms[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); - ec.fEventHistograms[eHistCentrality][eSim][eBefore]->Fill(centralityMC); - ec.fEventHistograms[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); - ec.fEventHistograms[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); - ec.fEventHistograms[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); - ec.fEventHistograms[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); + ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); } if constexpr (cuts == eAfter) { - ec.fEventHistograms[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); - ec.fEventHistograms[eHistCentrality][eSim][eAfter]->Fill(centralityMC); - ec.fEventHistograms[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); - ec.fEventHistograms[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); - ec.fEventHistograms[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); - ec.fEventHistograms[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); + ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); } } // end of if (rm == eMC) { } @@ -732,8 +716,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } // end of if (rm == eMC) { } - } - } + } // end of if (cfChargeCutSwitch) // charge cuts for Rec + } // end of if constexpr (rs == eRec || rs == eRecAndSim) { return true; } @@ -744,62 +728,42 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr (rs == eRec || rs == eRecAndSim) { if (rm == eReal) { if constexpr (cuts == eBefore) { - pc.fParticleHistograms[eHistPt][eRec][eBefore]->Fill(track.pt()); - pc.fParticleHistograms[eHistPhi][eRec][eBefore]->Fill(track.phi()); - pc.fParticleHistograms[eHistEta][eRec][eBefore]->Fill(track.eta()); - pc.fParticleHistograms[eHistCharge][eRec][eBefore]->Fill(track.sign()); + pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); } if constexpr (cuts == eAfter) { - pc.fParticleHistograms[eHistPt][eRec][eAfter]->Fill(track.pt()); - pc.fParticleHistograms[eHistPhi][eRec][eAfter]->Fill(track.phi()); - pc.fParticleHistograms[eHistEta][eRec][eAfter]->Fill(track.eta()); - pc.fParticleHistograms[eHistCharge][eRec][eAfter]->Fill(track.sign()); + pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); } } - // ... and corresponding MC truth simulated: - // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx - // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo if constexpr (rs == eRecAndSim) { - if (rm == eReal) { - if constexpr (cuts == eBefore) { - pc.fParticleHistograms[eHistCharge][eSim][eBefore]->Fill(track.sign()); - pph.fPhiAndPtHistograms[ePhiRec][eBefore]->Fill(track.phi()); - pph.fPhiAndPtHistograms[ePtRec][eBefore]->Fill(track.pt()); - } - if constexpr (cuts == eAfter) { - pc.fParticleHistograms[eHistCharge][eSim][eAfter]->Fill(track.sign()); - pph.fPhiAndPtHistograms[ePhiRec][eAfter]->Fill(track.phi()); - pph.fPhiAndPtHistograms[ePtRec][eAfter]->Fill(track.pt()); - } - } - if (rm == eMC) { if (!track.has_mcParticle()) { - LOGF(warning, " No MC particle for this track, skip..."); + // LOGF(warning, " No MC particle for this track, skip..."); return; } auto mcParticle = track.mcParticle(); auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); const int chargeMCUnit = 3; - if (std::abs(chargeMC) == chargeMCUnit) { - chargeMC /= chargeMCUnit; - } + chargeMC /= chargeMCUnit; if constexpr (cuts == eBefore) { - pc.fParticleHistograms[eHistPt][eSim][eBefore]->Fill(mcParticle.pt()); - pc.fParticleHistograms[eHistPhi][eSim][eBefore]->Fill(mcParticle.phi()); - pc.fParticleHistograms[eHistEta][eSim][eBefore]->Fill(mcParticle.eta()); - pc.fParticleHistograms[eHistCharge][eSim][eBefore]->Fill(chargeMC); - pph.fPhiAndPtHistograms[ePtMC][eBefore]->Fill(mcParticle.pt()); + pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(mcParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(mcParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(mcParticle.eta()); + pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); } if constexpr (cuts == eAfter) { - pc.fParticleHistograms[eHistPt][eSim][eAfter]->Fill(mcParticle.pt()); - pc.fParticleHistograms[eHistPhi][eSim][eAfter]->Fill(mcParticle.phi()); - pc.fParticleHistograms[eHistEta][eSim][eAfter]->Fill(mcParticle.eta()); - pc.fParticleHistograms[eHistCharge][eSim][eAfter]->Fill(chargeMC); - pph.fPhiAndPtHistograms[ePtMC][eAfter]->Fill(mcParticle.pt()); + pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(mcParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(mcParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(mcParticle.eta()); + pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); } } // end of if (rm == eMC) { } @@ -811,10 +775,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to for (int i = 0; i < eExternalHistograms_N; ++i) { for (int j = 0; j < eCuts_N; ++j) { ex.fWeights[i][j] = - getHistogramWithWeights( - cfFileWithWeights.value.c_str(), - Form("%d", runNumber), - Form("[%s][%s]", WeightsNames[i], CutsNames[j])); + getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s][%s]", WeightsNames[i], CutsNames[j])); ex.fExternalHistogramsList->Add(ex.fWeights[i][j]); } } @@ -835,6 +796,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + // *) Misc: + bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash + // *) Define all member functions to be called in the main process* functions: template void steer(T1 const& collision, T2 const& tracks) @@ -845,21 +809,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } int thisRunNumber = collision.bc().runNumber(); - // Set titles for all PhiAndPtHistograms to the runNumber - if (rs == eRecAndSim && isFirstCollision) { - for (int i = 0; i < eCuts_N; ++i) { - for (int j = 0; j < ePhiAndPtHistograms_N; ++j) { - pph.fPhiAndPtHistograms[j][i]->SetTitle(Form("%d", thisRunNumber)); - } + if (isFirstCollision) { + // Get run number + misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); + misc.fMiscHistRunNumber->SetBinContent(1, thisRunNumber); + // Get weights + if (cfExternalFileSwitch) { + loadWeights(thisRunNumber); } } - // Get weights - if (cfExternalFileSwitch && isFirstCollision) { - loadWeights(thisRunNumber); - } - isFirstCollision = false; - // Fill Quality Assurance if (qualityAssuranceSwitch) { qaFill(collision); @@ -881,11 +840,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - // Print current run number: - LOGF(info, "Run number: %d", thisRunNumber); - // Print vertex X position: - LOGF(info, "Vertex X position: %f", collision.posX()); - std::vector n2 = {-2, 2}; auto qVectorsTableBeforeCutsReal = initQVectorsTable(2, n2); auto qVectorsTableAfterCutsReal = initQVectorsTable(2, n2); @@ -900,7 +854,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to float wPhiAfter = 1.f; float wPtBefore = 1.f; float wPtAfter = 1.f; - if (cfExternalFileSwitch) { + if (cfExternalFileSwitch && ex.fWeights[ePhi][eBefore]) { wPhiBefore = ex.fWeights[ePhi][eBefore]->GetBinContent(ex.fWeights[ePhi][eBefore]->FindBin(thisPhi)); wPhiAfter = ex.fWeights[ePhi][eAfter]->GetBinContent(ex.fWeights[ePhi][eAfter]->FindBin(thisPhi)); wPtBefore = ex.fWeights[ePt][eBefore]->GetBinContent(ex.fWeights[ePt][eBefore]->FindBin(thisPt)); @@ -934,12 +888,14 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + // Now the first collision ends + isFirstCollision = false; } // end of template void steer(T1 const& collision, T2 const& tracks) { // *) Initialize and book all objects: void init(InitContext&) { - // ... code to book and initialize all analysis objects ... + // *) Set colors const int afterCutFillColor = kGreen - 10; const int afterCutLineColor = kGreen; const int beforeCutFillColor = kRed - 10; @@ -967,211 +923,220 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(ex.fExternalHistogramsList); } + // *) Book MiscHistograms TLists: + misc.fMiscHistList = new TList(); + misc.fMiscHistList->SetName("MiscHistograms"); + misc.fMiscHistList->SetOwner(true); + fBaseList->Add(misc.fMiscHistList); + + misc.fMiscHistRunNumber = new TH1D("eRunNumber", "to be set", 1, 0, 1); + misc.fMiscHistList->Add(misc.fMiscHistRunNumber); + // *) Book particle TLists: - pc.fParticleHistogramsList = new TList(); - pc.fParticleHistogramsList->SetName("ParticleHistograms"); - pc.fParticleHistogramsList->SetOwner(true); - fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + pc.fParticleHistList = new TList(); + pc.fParticleHistList->SetName("ParticleHistograms"); + pc.fParticleHistList->SetOwner(true); + fBaseList->Add(pc.fParticleHistList); // any nested TList in the base TList appears as a subdir in the output ROOT file // *) Book pt and phi distribution with binning defined through configurables in the json file: std::vector lPtBins = cfPtBins.value; - int nBinsPt = static_cast(lPtBins[0]); - float minPt = lPtBins[1]; - float maxPt = lPtBins[2]; + const int nBinsPt = static_cast(lPtBins[0]); + const float minPt = lPtBins[1]; + const float maxPt = lPtBins[2]; std::vector lPhiBins = cfPhiBins.value; - int nBinsPhi = static_cast(lPhiBins[0]); - float minPhi = lPhiBins[1]; - float maxPhi = lPhiBins[2]; + const int nBinsPhi = static_cast(lPhiBins[0]); + const float minPhi = lPhiBins[1]; + const float maxPhi = lPhiBins[2]; std::vector lEtaBins = cfEtaBins.value; - int nBinsEta = static_cast(lEtaBins[0]); - float minEta = lEtaBins[1]; - float maxEta = lEtaBins[2]; + const int nBinsEta = static_cast(lEtaBins[0]); + const float minEta = lEtaBins[1]; + const float maxEta = lEtaBins[2]; - int nBinsCharge = 5; - float minCharge = -2.5; - float maxCharge = 2.5; + const int nBinsCharge = 5; + const float minCharge = -2.5; + const float maxCharge = 2.5; if (doprocessRec || doprocessRecSim) { - pc.fParticleHistograms[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHist[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); + pc.fParticleHist[eHistPt][eRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eRec][eBefore] = new TH1F("[eHistPhi][eRec][eBefore]", "#phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eRec][eBefore]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHist[eHistPhi][eRec][eBefore] = new TH1F("[eHistPhi][eRec][eBefore]", "#phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHist[eHistPhi][eRec][eBefore]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHist[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); + pc.fParticleHist[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHistograms[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); + pc.fParticleHist[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eBefore]); + pc.fParticleHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eBefore]); } if (cfMasterCutSwitch) { - pc.fParticleHistograms[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHist[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); + pc.fParticleHist[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eRec][eAfter] = new TH1F("[eHistPhi][eRec][eAfter]", "#phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eRec][eAfter]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHist[eHistPhi][eRec][eAfter] = new TH1F("[eHistPhi][eRec][eAfter]", "#phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHist[eHistPhi][eRec][eAfter]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHist[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); + pc.fParticleHist[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHistograms[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); + pc.fParticleHist[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eRec][eAfter]); + pc.fParticleHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eAfter]); } } } if (doprocessSim || doprocessRecSim) { - pc.fParticleHistograms[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHist[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); + pc.fParticleHist[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eSim][eBefore]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHist[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHist[eHistPhi][eSim][eBefore]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHist[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); + pc.fParticleHist[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHistograms[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); + pc.fParticleHist[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eBefore]); + pc.fParticleHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eBefore]); } if (cfMasterCutSwitch) { - pc.fParticleHistograms[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHistograms[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHist[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); + pc.fParticleHist[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistograms[eHistPhi][eSim][eAfter] = new TH1F("[eHistPhi][eSim][eAfter]", "#phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHistograms[eHistPhi][eSim][eAfter]->GetXaxis()->SetTitle("#varphi"); + pc.fParticleHist[eHistPhi][eSim][eAfter] = new TH1F("[eHistPhi][eSim][eAfter]", "#phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); + pc.fParticleHist[eHistPhi][eSim][eAfter]->GetXaxis()->SetTitle("#varphi"); - pc.fParticleHistograms[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHistograms[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); + pc.fParticleHist[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); + pc.fParticleHist[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHistograms[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", nBinsCharge, minCharge, maxCharge); - pc.fParticleHistograms[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); + pc.fParticleHist[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHistograms[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[i][eSim][eAfter]); + pc.fParticleHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eAfter]); } } } // Book event-level histograms - ec.fEventHistogramsList = new TList(); - ec.fEventHistogramsList->SetName("EventHistograms"); - ec.fEventHistogramsList->SetOwner(true); - fBaseList->Add(ec.fEventHistogramsList); + ec.fEventHistList = new TList(); + ec.fEventHistList->SetName("EventHistograms"); + ec.fEventHistList->SetOwner(true); + fBaseList->Add(ec.fEventHistList); float defaultBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; const int nDefaultBins = sizeof(defaultBoundaries) / sizeof(defaultBoundaries[0]) - 1; std::vector lCent = cfCentBins.value; - int nBinsCent = static_cast(lCent[0]); - float minCent = lCent[1]; - float maxCent = lCent[2]; + const int nBinsCent = static_cast(lCent[0]); + const float minCent = lCent[1]; + const float maxCent = lCent[2]; std::vector lMult = cfMultBins.value; - int nBinsMult = static_cast(lMult[0]); - float minMult = lMult[1]; - float maxMult = lMult[2]; + const int nBinsMult = static_cast(lMult[0]); + const float minMult = lMult[1]; + const float maxMult = lMult[2]; std::vector lMultRef = cfMultBinsRef.value; - int nBinsMultRef = static_cast(lMultRef[0]); - float minMultRef = lMultRef[1]; - float maxMultRef = lMultRef[2]; + const int nBinsMultRef = static_cast(lMultRef[0]); + const float minMultRef = lMultRef[1]; + const float maxMultRef = lMultRef[2]; std::vector lVx = cfVxBins.value; - int nBinsVx = static_cast(lVx[0]); - float minVx = lVx[1]; - float maxVx = lVx[2]; + const int nBinsVx = static_cast(lVx[0]); + const float minVx = lVx[1]; + const float maxVx = lVx[2]; std::vector lVy = cfVyBins.value; - int nBinsVy = static_cast(lVy[0]); - float minVy = lVy[1]; - float maxVy = lVy[2]; + const int nBinsVy = static_cast(lVy[0]); + const float minVy = lVy[1]; + const float maxVy = lVy[2]; std::vector lVz = cfVzBins.value; - int nBinsVz = static_cast(lVz[0]); - float minVz = lVz[1]; - float maxVz = lVz[2]; + const int nBinsVz = static_cast(lVz[0]); + const float minVz = lVz[1]; + const float maxVz = lVz[2]; std::vector lIp = cfIpBins.value; - int nBinsIp = static_cast(lIp[0]); - float minIp = lIp[1]; - float maxIp = lIp[2]; + const int nBinsIp = static_cast(lIp[0]); + const float minIp = lIp[1]; + const float maxIp = lIp[2]; // eEventHistograms_N if (doprocessRec || doprocessRecSim) { if (cfALICECentBinSwitch) { - ec.fEventHistograms[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultBins, defaultBoundaries); } else { - ec.fEventHistograms[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); + ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); } - ec.fEventHistograms[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHistograms[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); + ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - ec.fEventHistograms[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHist[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); + ec.fEventHist[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eRec][eBefore] = new TH1F("[eHistVertexY][eRec][eBefore]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHist[eHistVertexY][eRec][eBefore] = new TH1F("[eHistVertexY][eRec][eBefore]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); + ec.fEventHist[eHistVertexY][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eRec][eBefore] = new TH1F("[eHistVertexZ][eRec][eBefore]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHist[eHistVertexZ][eRec][eBefore] = new TH1F("[eHistVertexZ][eRec][eBefore]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); + ec.fEventHist[eHistVertexZ][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Z"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistImpactParameter) { - ec.fEventHistograms[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eBefore]); + ec.fEventHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + ec.fEventHistList->Add(ec.fEventHist[i][eRec][eBefore]); } } if (cfMasterCutSwitch) { if (cfALICECentBinSwitch) { - ec.fEventHistograms[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultBins, defaultBoundaries); } else { - ec.fEventHistograms[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); + ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); } - ec.fEventHistograms[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHistograms[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); + ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHistograms[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); - ec.fEventHistograms[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHist[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); + ec.fEventHist[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eRec][eAfter] = new TH1F("[eHistVertexY][eRec][eAfter]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHist[eHistVertexY][eRec][eAfter] = new TH1F("[eHistVertexY][eRec][eAfter]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); + ec.fEventHist[eHistVertexY][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eRec][eAfter] = new TH1F("[eHistVertexZ][eRec][eAfter]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHist[eHistVertexZ][eRec][eAfter] = new TH1F("[eHistVertexZ][eRec][eAfter]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); + ec.fEventHist[eHistVertexZ][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Z"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistImpactParameter) { - ec.fEventHistograms[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eRec][eAfter]); + ec.fEventHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + ec.fEventHistList->Add(ec.fEventHist[i][eRec][eAfter]); } } } @@ -1179,61 +1144,61 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessSim || doprocessRecSim) { if (cfALICECentBinSwitch) { - ec.fEventHistograms[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultBins, defaultBoundaries); } else { - ec.fEventHistograms[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); + ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); } - ec.fEventHistograms[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHistograms[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); + ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHist[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); + ec.fEventHist[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eSim][eBefore] = new TH1F("[eHistVertexY][eSim][eBefore]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHist[eHistVertexY][eSim][eBefore] = new TH1F("[eHistVertexY][eSim][eBefore]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); + ec.fEventHist[eHistVertexY][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eSim][eBefore] = new TH1F("[eHistVertexZ][eSim][eBefore]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHist[eHistVertexZ][eSim][eBefore] = new TH1F("[eHistVertexZ][eSim][eBefore]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - ec.fEventHistograms[eHistImpactParameter][eSim][eBefore] = new TH1F("[eHistImpactParameter][eSim][eBefore]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); - ec.fEventHistograms[eHistImpactParameter][eSim][eBefore]->GetXaxis()->SetTitle("Impact Parameter"); + ec.fEventHist[eHistImpactParameter][eSim][eBefore] = new TH1F("[eHistImpactParameter][eSim][eBefore]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->GetXaxis()->SetTitle("Impact Parameter"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistReferenceMultiplicity) { - ec.fEventHistograms[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eBefore]); + ec.fEventHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); + ec.fEventHistList->Add(ec.fEventHist[i][eSim][eBefore]); } } if (cfMasterCutSwitch) { if (cfALICECentBinSwitch) { - ec.fEventHistograms[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultBins, defaultBoundaries); } else { - ec.fEventHistograms[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); + ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); } - ec.fEventHistograms[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); - ec.fEventHistograms[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHistograms[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); + ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); - ec.fEventHistograms[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHistograms[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); + ec.fEventHist[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); + ec.fEventHist[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); - ec.fEventHistograms[eHistVertexY][eSim][eAfter] = new TH1F("[eHistVertexY][eSim][eAfter]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHistograms[eHistVertexY][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Y"); + ec.fEventHist[eHistVertexY][eSim][eAfter] = new TH1F("[eHistVertexY][eSim][eAfter]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); + ec.fEventHist[eHistVertexY][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - ec.fEventHistograms[eHistVertexZ][eSim][eAfter] = new TH1F("[eHistVertexZ][eSim][eAfter]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHistograms[eHistVertexZ][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Z"); + ec.fEventHist[eHistVertexZ][eSim][eAfter] = new TH1F("[eHistVertexZ][eSim][eAfter]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - ec.fEventHistograms[eHistImpactParameter][eSim][eAfter] = new TH1F("[eHistImpactParameter][eSim][eAfter]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); - ec.fEventHistograms[eHistImpactParameter][eSim][eAfter]->GetXaxis()->SetTitle("Impact Parameter"); + ec.fEventHist[eHistImpactParameter][eSim][eAfter] = new TH1F("[eHistImpactParameter][eSim][eAfter]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->GetXaxis()->SetTitle("Impact Parameter"); for (int i = 0; i < eEventHistograms_N; ++i) { if (i != eHistReferenceMultiplicity) { - ec.fEventHistograms[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistogramsList->Add(ec.fEventHistograms[i][eSim][eAfter]); + ec.fEventHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); + ec.fEventHistList->Add(ec.fEventHist[i][eSim][eAfter]); } } } @@ -1257,44 +1222,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - if (doprocessRecSim) { - // *) Book PhiAndPtHistograms TLists: - pph.fPhiAndPtHistogramsList = new TList(); - pph.fPhiAndPtHistogramsList->SetName("PhiAndPtHistograms"); - pph.fPhiAndPtHistogramsList->SetOwner(true); - fBaseList->Add(pph.fPhiAndPtHistogramsList); - - pph.fPhiAndPtHistograms[ePhiRec][eBefore] = new TH1F("[ePhiRec][eBefore]", "phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); - pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("#varphi"); - - pph.fPhiAndPtHistograms[ePtRec][eBefore] = new TH1F("[ePtRec][eBefore]", "pt distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); - pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - pph.fPhiAndPtHistograms[ePtMC][eBefore] = new TH1F("[ePtMC][eBefore]", "pt distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); - pph.fPhiAndPtHistograms[ePhiRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - for (int i = 0; i < ePhiAndPtHistograms_N; ++i) { - pph.fPhiAndPtHistograms[i][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pph.fPhiAndPtHistogramsList->Add(pph.fPhiAndPtHistograms[i][eBefore]); - } - - if (cfMasterCutSwitch) { - pph.fPhiAndPtHistograms[ePhiRec][eAfter] = new TH1F("[ePhiRec][eAfter]", "phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); - pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("#varphi"); - - pph.fPhiAndPtHistograms[ePtRec][eAfter] = new TH1F("[ePtRec][eAfter]", "pt distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); - pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); - - pph.fPhiAndPtHistograms[ePtMC][eAfter] = new TH1F("[ePtMC][eAfter]", "pt distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); - pph.fPhiAndPtHistograms[ePhiRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); - - for (int i = 0; i < ePhiAndPtHistograms_N; ++i) { - pph.fPhiAndPtHistograms[i][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pph.fPhiAndPtHistogramsList->Add(pph.fPhiAndPtHistograms[i][eAfter]); - } - } - } - // *) Book and QA TLists: if (qualityAssuranceSwitch && doprocessRecSim) { qa.fQualityAssuranceList = new TList(); @@ -1317,8 +1244,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } PROCESS_SWITCH(MultiparticleCorrelationsMei, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version - // ------------------------------------------- - // B) Process both reconstructed and corresponding MC truth simulated data: void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) { @@ -1326,8 +1251,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } PROCESS_SWITCH(MultiparticleCorrelationsMei, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); - // ------------------------------------------- - // C) Process only simulated data: void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) { From a83063c85c14bc180808adce610c5cba5e1ec9c1 Mon Sep 17 00:00:00 2001 From: MRazza <118839113+MRazza879@users.noreply.github.com> Date: Mon, 28 Sep 2026 10:14:45 +0200 Subject: [PATCH 089/648] [PWGHF] add TPC pi/K/p veto study for TOF mismatch in DeuteronFromLbtask (#18030) Co-authored-by: Marta Razza --- PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index c54acf42d0e..d50ee5c5c1b 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -88,7 +88,7 @@ struct HfTaskDeuteronFromLb { using CollisionCandidates = o2::soa::Join; using MCTrackCandidates = o2::soa::Join; using MCCollisionCandidates = o2::soa::Join; - using TrackCandidates = o2::soa::Join; + using TrackCandidates = o2::soa::Join; Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; @@ -139,6 +139,7 @@ struct HfTaskDeuteronFromLb { qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); qaHistos.add("Data/hnSigmaTOFVsPtPurity", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); // MC generated-level histograms @@ -295,6 +296,11 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); + const bool isTPCDeClean = isTPCDe && + std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma; + if (track.pt() < ptThresholdforPID) { if (isITSDe) { qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); @@ -303,6 +309,9 @@ struct HfTaskDeuteronFromLb { if (isTPCDe && track.hasTOF()) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurity"), track.pt(), track.tofNSigmaDe()); } + if (isTPCDeClean && track.hasTOF()) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); + } } if (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) { From bdfef1f8a1cc37fbc8240fcec670c498b6934a90 Mon Sep 17 00:00:00 2001 From: hyungjun <109190387+hyungjun-lee@users.noreply.github.com> Date: Mon, 28 Sep 2026 18:19:17 +0900 Subject: [PATCH 090/648] =?UTF-8?q?[PWGDQ]=20Add=20optional=20barrel=20ass?= =?UTF-8?q?ociation=20time=20margin=20cut=20to=20data=20and=20M=E2=80=A6?= =?UTF-8?q?=20(#18052)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGDQ/Core/VarManager.cxx | 71 ++++++++++++++++++++++++++ PWGDQ/Core/VarManager.h | 31 +++++++++++ PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 25 +++++++++ PWGDQ/Tasks/tableReader_withAssoc.h | 25 +++++++++ 4 files changed, 152 insertions(+) diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index 84e6d055ce9..d1b193ec12c 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1041,6 +1042,12 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kTrackTimeRes] = "ns"; fgVariableNames[kTrackTimeResRelative] = "Relative resolution of the track time"; fgVariableUnits[kTrackTimeResRelative] = ""; + fgVariableNames[kTrackAssocDeltaTime] = "Track-collision association #Deltat"; + fgVariableUnits[kTrackAssocDeltaTime] = "ns"; + fgVariableNames[kTrackAssocTimeThreshold] = "Track-collision association time threshold"; + fgVariableUnits[kTrackAssocTimeThreshold] = "ns"; + fgVariableNames[kTrackAssocDeltaTimeNorm] = "|#Deltat| / threshold"; + fgVariableUnits[kTrackAssocDeltaTimeNorm] = ""; fgVariableNames[kDetectorMap] = "DetectorMap"; fgVariableUnits[kDetectorMap] = ""; fgVariableNames[kHasITS] = "HasITS"; @@ -2369,6 +2376,9 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kTrackTime"] = kTrackTime; fgVarNamesMap["kTrackTimeRes"] = kTrackTimeRes; fgVarNamesMap["kTrackTimeResRelative"] = kTrackTimeResRelative; + fgVarNamesMap["kTrackAssocDeltaTime"] = kTrackAssocDeltaTime; + fgVarNamesMap["kTrackAssocTimeThreshold"] = kTrackAssocTimeThreshold; + fgVarNamesMap["kTrackAssocDeltaTimeNorm"] = kTrackAssocDeltaTimeNorm; fgVarNamesMap["kDetectorMap"] = kDetectorMap; fgVarNamesMap["kHasITS"] = kHasITS; fgVarNamesMap["kHasTRD"] = kHasTRD; @@ -2918,3 +2928,64 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kInnerTOFnSigmaHe3"] = kInnerTOFnSigmaHe3; fgVarNamesMap["kInnerTOFnSigmaAl"] = kInnerTOFnSigmaAl; } + +//__________________________________________________________________ +bool VarManager::computeBarrelAssocTimeCompat(float trackTime, float trackTimeRes, bool timeResIsRange, bool isPVContributor, + int64_t origBC, float origCollTime, int origNumContrib, + int64_t collBC, float collTime, float collTimeRes, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values) +{ + // + // Mirrors the time-based association of Common/Core/CollisionAssociation.h::runAssocWithTime for central barrel tracks. + // Every arithmetic step is kept identical to the associator (including the double->int64 truncations), + // so that re-applying it on skimmed associations with a smaller timeMargin reproduces a skim produced directly with that margin. + // Only tracks assigned to a collision in the AO2D are handled (this is always the case for tracks written by the DQ table makers). + // + if (!values) { + values = fgValues; + } + values[kTrackAssocDeltaTime] = -9999.f; + values[kTrackAssocTimeThreshold] = 0.f; + values[kTrackAssocDeltaTimeNorm] = 9999.f; + + constexpr double BunchSpacingNS = o2::constants::lhc::LHCBunchSpacingNS; + + // (1) BC pre-window (CollisionAssociation.h: trackBCCache, bcOffsetMax, bcOffsetWindow) + // NOTE: the associator uses the raw trackTime here also for PV contributors + const int64_t bcOffsetMax = static_cast(bcWindowForOneSigma * nSigma + timeMargin / BunchSpacingNS); + const int64_t trackBCCache = static_cast(static_cast(origBC) + static_cast(trackTime) / BunchSpacingNS); + if (std::abs(trackBCCache - collBC) > bcOffsetMax) { + return false; + } + + // (2) PV-contributor handling (usePVAssociation: 0 off, 1 OnlySameBc, 2 SameBcAndLowMult) + constexpr int OnlySameBc = 1; + constexpr int SameBcAndLowMult = 2; + const bool pvMode = (usePVAssociation == OnlySameBc && isPVContributor) || + (usePVAssociation == SameBcAndLowMult && isPVContributor && origNumContrib > maxPvContribLowMult); + float tTrack = trackTime; + float tTrackRes = trackTimeRes; + if (pvMode) { + tTrack = origCollTime; // time of the ORIGINAL collision + tTrackRes = static_cast(BunchSpacingNS); // 1 BC + } + + // (3) time difference and threshold + const int64_t bcOffset = origBC - collBC; + const float collTimeRes2 = collTimeRes * collTimeRes; + const float deltaTime = tTrack - collTime + bcOffset * static_cast(BunchSpacingNS); + float threshold = 0.f; + if (pvMode) { + threshold = tTrackRes; // margin NOT applied + } else if (timeResIsRange) { + threshold = tTrackRes + nSigma * std::sqrt(collTimeRes2) + timeMargin; + } else { + threshold = nSigma * std::sqrt(collTimeRes2 + tTrackRes * tTrackRes) + timeMargin; + } + + values[kTrackAssocDeltaTime] = deltaTime; + values[kTrackAssocTimeThreshold] = threshold; + values[kTrackAssocDeltaTimeNorm] = (threshold > 0.f) ? std::abs(deltaTime) / threshold : 9999.f; + return std::abs(deltaTime) < threshold; +} diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index f3f4d045b79..fa9ae2468a8 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -578,6 +578,9 @@ class VarManager : public TObject kTrackTime, kTrackTimeRes, kTrackTimeResRelative, + kTrackAssocDeltaTime, // track time - time of the associated collision (BC offset included), ns + kTrackAssocTimeThreshold, // time-compatibility threshold of the track-to-collision associator (margin included), ns + kTrackAssocDeltaTimeNorm, // |kTrackAssocDeltaTime| / kTrackAssocTimeThreshold; < 1 means the association is time compatible kDetectorMap, kHasITS, kHasTRD, @@ -1474,6 +1477,22 @@ class VarManager : public TObject static void FillTrackEMCal(T const& cluster, float trackP = -1.0f, float deltaEta = -999.0f, float deltaPhi = -999.0f, float* values = nullptr); template static void FillTrackCollision(T const& track, C const& collision, float* values = nullptr); + // Re-evaluation of the time compatibility of a (barrel track, collision) association, mirroring + // Common/Core/CollisionAssociation.h::runAssocWithTime. Fills kTrackAssocDeltaTime, kTrackAssocTimeThreshold, kTrackAssocDeltaTimeNorm. + // orig* : collision originally assigned to the track in the AO2D (reference of trackTime) + // coll* : collision of the association under test + // The last 5 parameters must match the configuration of track-to-collision-associator used at skimming time + // (timeMargin and nSigma may be smaller in order to tighten the association at analysis level). + static bool computeBarrelAssocTimeCompat(float trackTime, float trackTimeRes, bool timeResIsRange, bool isPVContributor, + int64_t origBC, float origCollTime, int origNumContrib, + int64_t collBC, float collTime, float collTimeRes, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values = nullptr); + // Same as above for DQ skimmed tables: track = ReducedTracks+ReducedTracksBarrel, collision/origCollision = ReducedEvents+ReducedEventsExtended + template + static bool isBarrelAssocTimeCompatible(T const& track, C const& collision, C const& origCollision, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values = nullptr); template static void FillTrackCollisionMC(T1 const& track, T2 const& MotherTrack, C const& collision, float* values = nullptr); template @@ -3471,6 +3490,18 @@ void VarManager::FillTrackCollision(T const& track, C const& collision, float* v } } +template +bool VarManager::isBarrelAssocTimeCompatible(T const& track, C const& collision, C const& origCollision, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values) +{ + return computeBarrelAssocTimeCompat(track.trackTime(), track.trackTimeRes(), + (track.flags() & o2::aod::track::TrackTimeResIsRange) > 0, track.isPVContributor(), + static_cast(origCollision.globalBC()), origCollision.collisionTime(), origCollision.numContrib(), + static_cast(collision.globalBC()), collision.collisionTime(), collision.collisionTimeRes(), + nSigma, timeMargin, bcWindowForOneSigma, usePVAssociation, maxPvContribLowMult, values); +} + template void VarManager::FillTrackCollisionMatCorr(T const& track, C const& collision, M const& materialCorr, P const& propagator, float* values) { diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 8f5c01923aa..176d7ce64b2 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -554,6 +554,17 @@ struct AnalysisTrackSelection { Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + // Re-application of the track-to-collision time compatibility on the skimmed associations. + // timeMargin and nSigma must be <= the values used by track-to-collision-associator at skimming time; + // bcWindow, usePVAssociation and maxPvContributors must be identical to those values. + struct : ConfigurableGroup { + Configurable cfgAssocTimeCut{"cfgAssocTimeCut", false, "If true, reject associations failing the time compatibility computed with the parameters below"}; + Configurable cfgAssocTimeMargin{"cfgAssocTimeMargin", 0.f, "time margin (ns); must be <= skimming timeMargin"}; + Configurable cfgAssocNSigma{"cfgAssocNSigma", 4.f, "nSigmaForTimeCompat; must be <= skimming value"}; + Configurable cfgAssocBcWindowForOneSigma{"cfgAssocBcWindowForOneSigma", 60, "bcWindowForOneSigma; must be equal to the skimming value"}; + Configurable cfgAssocUsePVAssociation{"cfgAssocUsePVAssociation", 1, "usePVAssociation; must be equal to the skimming value"}; + Configurable cfgAssocMaxPvContributorsForLowMultReassoc{"cfgAssocMaxPvContributorsForLowMultReassoc", 10, "maxPvContributorsForLowMultReassoc; must be equal to the skimming value"}; + } fConfigAssocTime; Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -711,6 +722,20 @@ struct AnalysisTrackSelection { } auto track = assoc.template reducedtrack_as(); + + // Reject incompatible associations while preserving the row alignment of trackSel. + // The track time is referenced to its original collision, not the associated one. + if (fConfigAssocTime.cfgAssocTimeCut) { + auto origEvent = track.template reducedevent_as(); + if (!VarManager::isBarrelAssocTimeCompatible(track, event, origEvent, + fConfigAssocTime.cfgAssocNSigma, fConfigAssocTime.cfgAssocTimeMargin, + fConfigAssocTime.cfgAssocBcWindowForOneSigma, fConfigAssocTime.cfgAssocUsePVAssociation, + fConfigAssocTime.cfgAssocMaxPvContributorsForLowMultReassoc)) { + trackSel(0); + continue; + } + } + VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA VarManager::FillTrackCollision(track, event); diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index 3ade22e200f..e7952a1fdad 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -673,6 +673,17 @@ struct AnalysisTrackSelection { o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; o2::framework::Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; o2::framework::Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + // Re-application of the track-to-collision time compatibility on the skimmed associations. + // timeMargin and nSigma must be <= the values used by track-to-collision-associator at skimming time; + // bcWindow, usePVAssociation and maxPvContributors must be identical to those values. + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable cfgAssocTimeCut{"cfgAssocTimeCut", false, "If true, reject associations failing the time compatibility computed with the parameters below"}; + o2::framework::Configurable cfgAssocTimeMargin{"cfgAssocTimeMargin", 0.f, "time margin (ns); must be <= skimming timeMargin"}; + o2::framework::Configurable cfgAssocNSigma{"cfgAssocNSigma", 4.f, "nSigmaForTimeCompat; must be <= skimming value"}; + o2::framework::Configurable cfgAssocBcWindowForOneSigma{"cfgAssocBcWindowForOneSigma", 60, "bcWindowForOneSigma; must be equal to the skimming value"}; + o2::framework::Configurable cfgAssocUsePVAssociation{"cfgAssocUsePVAssociation", 1, "usePVAssociation; must be equal to the skimming value"}; + o2::framework::Configurable cfgAssocMaxPvContributorsForLowMultReassoc{"cfgAssocMaxPvContributorsForLowMultReassoc", 10, "maxPvContributorsForLowMultReassoc; must be equal to the skimming value"}; + } fConfigAssocTime; o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; o2::framework::Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; @@ -806,6 +817,20 @@ struct AnalysisTrackSelection { VarManager::FillEvent(event); auto track = assoc.template reducedtrack_as(); + + // Reject incompatible associations while preserving the row alignment of trackSel. + // The track time is referenced to its original collision, not the associated one. + if (fConfigAssocTime.cfgAssocTimeCut) { + auto origEvent = track.template reducedevent_as(); + if (!VarManager::isBarrelAssocTimeCompatible(track, event, origEvent, + fConfigAssocTime.cfgAssocNSigma, fConfigAssocTime.cfgAssocTimeMargin, + fConfigAssocTime.cfgAssocBcWindowForOneSigma, fConfigAssocTime.cfgAssocUsePVAssociation, + fConfigAssocTime.cfgAssocMaxPvContributorsForLowMultReassoc)) { + trackSel(0); + continue; + } + } + filterMap = static_cast(0); VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA From bef1958e8c5179086d3c86c1cde6471e21a552ff Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:07:43 +0800 Subject: [PATCH 091/648] [PWGLF] Added histograms to store k* information(analysis k* star cut influence on spin correlation for like-sign pairs) (#18072) --- .../Tasks/Strangeness/lambdaspincorrderived.cxx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 408b6be942e..b1546805464 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -686,8 +686,8 @@ struct lambdaspincorrderived { histos.add("hSparseAntiLambdaAntiLambda_kStar", "hSparseAntiLambdaAntiLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); histos.add("hSparseLambdaLambdaMixed_kStar", "hSparseLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); - // histos.add("hSparseLambdaAntiLambdaMixed_kStar", "hSparseLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR,configThnAxisKStar}, true); - // histos.add("hSparseAntiLambdaLambdaMixed_kStar", "hSparseAntiLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR,configThnAxisKStar}, true); + histos.add("hSparseLambdaAntiLambdaMixed_kStar", "hSparseLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaLambdaMixed_kStar", "hSparseAntiLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); histos.add("hSparseAntiLambdaAntiLambdaMixed_kStar", "hSparseAntiLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); } @@ -1311,6 +1311,9 @@ struct lambdaspincorrderived { histos.fill(HIST("hSparsePhiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaAntiLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); @@ -1322,6 +1325,9 @@ struct lambdaspincorrderived { histos.fill(HIST("hSparsePhiAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); if (fillHistConfig.fillAnalysisSparses) { @@ -1395,6 +1401,9 @@ struct lambdaspincorrderived { histos.fill(HIST("hSparsePhiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); if (fillHistConfig.fillAnalysisSparses) { @@ -1405,6 +1414,9 @@ struct lambdaspincorrderived { histos.fill(HIST("hSparsePhiAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); if (fillHistConfig.fillAnalysisSparses) { From e88e6b76fa8e9c22cf8684081ee9c2b2271cd7aa Mon Sep 17 00:00:00 2001 From: sofiatomassini <122356048+sofiatomassini@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:13:39 +0200 Subject: [PATCH 092/648] [PWGCF] Fix Event histogram (#18075) Co-authored-by: Sofia Tomassini --- PWGCF/Femto3D/Tasks/multEstimator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto3D/Tasks/multEstimator.cxx b/PWGCF/Femto3D/Tasks/multEstimator.cxx index 91d4e14e59e..fc284861c28 100644 --- a/PWGCF/Femto3D/Tasks/multEstimator.cxx +++ b/PWGCF/Femto3D/Tasks/multEstimator.cxx @@ -172,8 +172,8 @@ struct multEstimator { Nch_vs_cent_vs_eta_conditional = registry.add("Nch_vs_cent_vs_eta_conditional", "Nch_vs_cent_vs_eta_conditional", kTH2F, {{100, 0.0, 100.0, "cent"}, {200, -1.0, 1.0, "deta"}}); tmp_histo_per_event = std::make_shared(TH2F("tmp_histo_per_event", "tmp_histo_per_event", 100, 0.0, 100.0, 200, -1.0, 1.0)); - Events_vs_cent = std::make_shared(TH1F("Events_vs_cent", "Events_vs_cent", 100, 0.0, 100.)); - Events_vs_cent_conditional = std::make_shared(TH1F("Events_vs_cent_conditional", "Events_vs_cent_conditional", 100, 0.0, 100.)); + Events_vs_cent = registry.add("Events_vs_cent", "Events_vs_cent", kTH1F, {{100, 0.0, 100., "cent"}}); + Events_vs_cent_conditional = registry.add("Events_vs_cent_conditional", "Events_vs_cent_conditional", kTH1F, {{100, 0.0, 100.0, "cent"}}); ITShisto = registry.add(Form("nsigmaITS_PDG%i", _particlePDG.value), Form("nsigmaITS_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); TPChisto = registry.add(Form("nsigmaTPC_PDG%i", _particlePDG.value), Form("nsigmaTPC_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); From cb2bcbca696a063510e2e6e67c2ea4b00fa6af93 Mon Sep 17 00:00:00 2001 From: hernasab Date: Mon, 28 Sep 2026 10:59:33 -0500 Subject: [PATCH 093/648] [PWGCF] added pZDC correlations with ft0c and glb tracks as cent estimators (#17928) Co-authored-by: Sabrina Hernandez --- PWGCF/Flow/Tasks/flowZdcTask.cxx | 51 +++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowZdcTask.cxx b/PWGCF/Flow/Tasks/flowZdcTask.cxx index 9402dbb8c01..21cacbd92ea 100644 --- a/PWGCF/Flow/Tasks/flowZdcTask.cxx +++ b/PWGCF/Flow/Tasks/flowZdcTask.cxx @@ -54,6 +54,9 @@ using namespace o2::aod::evsel; struct FlowZdcTask { SliceCache cache; + Configurable useFT0M{"useFT0M", true, "FT0M for centrality"}; + Configurable useFT0C{"useFT0C", false, "FT0C for centrality"}; + Configurable useGlbTracks{"useGlbTracks", false, "GlbTracks for centrality"}; Configurable cfgCutVertex{"cfgCutVertex", 10.0, "Accepted z-vertex range"}; Configurable maxZem{"maxZem", 3099.5, "Max ZEM signal"}; // for ZDC info and analysis @@ -65,6 +68,8 @@ struct FlowZdcTask { Configurable maxAmpFT0{"maxAmpFT0", 2500, "Max FT0 amp"}; Configurable maxAmpFT0M{"maxAmpFT0M", 2500, "Max FT0M amp"}; Configurable nBinsAmpFT0M{"nBinsAmpFT0M", 100, "N bins FT0M amp"}; + Configurable nBinsAmpFT0C{"nBinsAmpFT0C", 100, "N bins FT0C amp"}; + Configurable nBinsGlbTracks{"nBinsGlbTracks", 100, "N bins global tracks"}; Configurable nBinsZDC{"nBinsZDC", 400, "nBinsZDC"}; Configurable nBinsZP{"nBinsZP", 50, "nBinsZP"}; Configurable nBinsZN{"nBinsZN", 50, "nBinsZN"}; @@ -199,6 +204,18 @@ struct FlowZdcTask { xAxis->SetBinLabel(16, "Within TDC cut?"); if (doprocessQA) { + if (useFT0M) { + histos.add("ZPAZNAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } + if (useFT0C) { + histos.add("ZPAZNAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0C, 0., maxAmpFT0}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0C, 0., maxAmpFT0}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } + if (useGlbTracks) { + histos.add("ZPAZNAVsGlbTracks", ";N_{global tracks} (|#eta|<0.8);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsGlbTracks, minNch, maxNch}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsGlbTracks", ";N_{global tracks} (|#eta|<0.8);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsGlbTracks, minNch, maxNch}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } histos.add("ZNAVsFT0C", ";T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNAVsFT0M", ";T0A+T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0C", ";T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); @@ -545,12 +562,28 @@ struct FlowZdcTask { if (((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) { histos.fill(HIST("ZNAVsZPA"), zpA, znA); histos.fill(HIST("CommonZNAVsZPA"), commonSumZpa, commonSumZna); - histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); + if (useFT0M) { + histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); + } + if (useFT0C) { + histos.fill(HIST("ZPAZNAVsFT0C"), aT0C / 100., zpA, znA); + } + if (useGlbTracks) { + histos.fill(HIST("ZPAZNAVsGlbTracks"), glbTracks, zpA, znA); + } } if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZPC >= minTdcZp) && (tZPC <= maxTdcZp))) { histos.fill(HIST("ZNCVsZPC"), zpC, znC); histos.fill(HIST("CommonZNCVsZPC"), commonSumZpc, commonSumZnc); - histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + if (useFT0M) { + histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + } + if (useFT0C) { + histos.fill(HIST("ZPCZNCVsFT0C"), aT0C / 100., zpC, znC); + } + if (useGlbTracks) { + histos.fill(HIST("ZPCZNCVsGlbTracks"), glbTracks, zpC, znC); + } } } else { histos.fill(HIST("ZNA"), znA); @@ -583,8 +616,18 @@ struct FlowZdcTask { histos.fill(HIST("ZPCVsNch"), glbTracks, zpC); histos.fill(HIST("ZNCVsNch"), glbTracks, znC); histos.fill(HIST("ZNAVsNch"), glbTracks, znA); - histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); - histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + if (useFT0M) { + histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); + histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + } + if (useFT0C) { + histos.fill(HIST("ZPAZNAVsFT0C"), aT0C / 100., zpA, znA); + histos.fill(HIST("ZPCZNCVsFT0C"), aT0C / 100., zpC, znC); + } + if (useGlbTracks) { + histos.fill(HIST("ZPAZNAVsGlbTracks"), glbTracks, zpA, znA); + histos.fill(HIST("ZPCZNCVsGlbTracks"), glbTracks, zpC, znC); + } } histos.fill(HIST("ZEM1"), aZEM1); histos.fill(HIST("ZEM2"), aZEM2); From 26d8ca6ff24d9376c66c2a55e1c9633957a187a7 Mon Sep 17 00:00:00 2001 From: Katarzyna <116073883+kgwizdzi@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:53:00 +0200 Subject: [PATCH 094/648] [PWGCF] FU D0 task: adding y histogram and non-prompt cut for reco D0s (#18074) --- PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index c0cd656cab1..0e54930d4df 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -314,6 +314,7 @@ struct FemtoUniversePairTaskTrackD0 { {"hEtaD0D0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"hYD0D0bar", ";y ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hPtDaughters", ";#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{300, 0., 12.}}}}, {"hSignDaughters", ";sign ;counts", {HistType::kTH1F, {{10, -2.5, 2.5}}}}}}; @@ -675,6 +676,7 @@ struct FemtoUniversePairTaskTrackD0 { registry.fill(HIST("hPtD0D0bar"), d0d0bar.pt()); registry.fill(HIST("hPhiD0D0bar"), d0d0bar.phi()); registry.fill(HIST("hEtaD0D0bar"), d0d0bar.eta()); + registry.fill(HIST("hYD0D0bar"), d0d0bar.transRadius()); // BDT score classes registry.fill(HIST("DebugBdt/hBdtScore0"), d0d0bar.decayVtxX()); registry.fill(HIST("DebugBdt/hBdtScore1"), d0d0bar.decayVtxY()); @@ -1388,7 +1390,7 @@ struct FemtoUniversePairTaskTrackD0 { { for (auto const& part : recoParts) { // filling the histograms for identified hadrons - if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco)) { + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco) && (part.decayVtxZ() < ConfMlProb.confMlProbNonPromptMax)) { // getting the efficiency value if (doEfficiencyCorr) { weight = efficiencyCalculator.getWeight(ParticleNo::TWO, part.pt()); From 553ea24b9d1cbf6b876f0641923b563f3553b6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:08:39 +0200 Subject: [PATCH 095/648] [Infrastructure] Update cpplint configuration (#18080) --- CPPLINT.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 1613014234b..53dade3df6c 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1 +1,2 @@ -filter=-build/c++11,-build/c++17,-build/include_order,-build/namespaces,-readability/fn_size,-readability/nolint,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/semicolon,-whitespace/todo +filter=-build/c++11,-build/c++17,-build/include_order,-build/namespaces,-readability/alt_tokens,-readability/fn_size,-readability/nolint,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/newline,-whitespace/parens,-whitespace/semicolon,-whitespace/todo +extensions=C,c,c++,cc,cl,cpp,cu,cuh,cxx,cxx.in,h,h++,hh,h.in,hpp,hxx,inc,inl,macro From 84dd4a87f396749ba3085b3449a930c7e9ffd5ce Mon Sep 17 00:00:00 2001 From: Tucker Hwang <108156178+mhwang285@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:13:01 -0700 Subject: [PATCH 096/648] [PWGJE] berkeleyTreeProducer: add run number to events (#18078) --- PWGJE/TableProducer/berkeleyTreeProducer.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PWGJE/TableProducer/berkeleyTreeProducer.cxx b/PWGJE/TableProducer/berkeleyTreeProducer.cxx index fe49ffdef91..669ba295b7f 100644 --- a/PWGJE/TableProducer/berkeleyTreeProducer.cxx +++ b/PWGJE/TableProducer/berkeleyTreeProducer.cxx @@ -39,6 +39,7 @@ namespace o2::aod { namespace berkeleytree { +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(VtxZ, vtxZ, float); DECLARE_SOA_COLUMN(Weight, weight, float); DECLARE_SOA_COLUMN(PtHat, ptHat, float); @@ -63,6 +64,7 @@ DECLARE_SOA_COLUMN(PdgId, pdgId, std::vector); } // namespace berkeleytree DECLARE_SOA_TABLE(BerkeleyTree, "AOD", "BERKELEYTREE", + berkeleytree::RunNumber, berkeleytree::VtxZ, berkeleytree::Weight, berkeleytree::PtHat, @@ -133,7 +135,7 @@ struct BerkeleyTreeProducer { } using JetParticlesWithOriginal = soa::Join; - void processMCJJ(aod::JetCollisionsMCD::iterator const& collision, aod::JetTracksMCD const& tracks, JetParticlesWithOriginal const& mcParticles, aod::JetMcCollisions const&) + void processMCJJ(aod::JetCollisionsMCD::iterator const& collision, aod::JetTracksMCD const& tracks, JetParticlesWithOriginal const& mcParticles, aod::JetMcCollisions const&, aod::JBCs const&) { // do not do any RCT selections, will be done on analysis level if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, false, "", false, false)) @@ -143,6 +145,7 @@ struct BerkeleyTreeProducer { float weight = collision.has_mcCollision() ? collision.mcCollision().weight() : 1.f; float pthat = collision.has_mcCollision() ? collision.mcCollision().ptHard() : 1.f; + int runNumber = collision.bc_as().runNumber(); std::vector detPt, detEta, detPhi; std::vector detTrackSel; @@ -196,7 +199,7 @@ struct BerkeleyTreeProducer { } } - tree(collision.posZ(), weight, pthat, collision.multFT0C(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.rct_raw(), detPt, detEta, detPhi, detTrackSel, detMcId, genPt, genEta, genPhi, genE, genCharge, genMcId, pdgId); + tree(runNumber, collision.posZ(), weight, pthat, collision.multFT0C(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.rct_raw(), detPt, detEta, detPhi, detTrackSel, detMcId, genPt, genEta, genPhi, genE, genCharge, genMcId, pdgId); } PROCESS_SWITCH(BerkeleyTreeProducer, processMCJJ, "MC processing for JJ simulations", false); From caed5574f20b08e0494a4e41ebdafdd184491218 Mon Sep 17 00:00:00 2001 From: Yongxi Du <1260971129@qq.com> Date: Tue, 29 Sep 2026 16:34:51 +0800 Subject: [PATCH 097/648] [PWGUD] fix ZDCtiming gapside selection (#18089) --- PWGUD/Tasks/flowCumulantsUpc.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGUD/Tasks/flowCumulantsUpc.cxx b/PWGUD/Tasks/flowCumulantsUpc.cxx index e7ccfdb51e4..19f29620a21 100644 --- a/PWGUD/Tasks/flowCumulantsUpc.cxx +++ b/PWGUD/Tasks/flowCumulantsUpc.cxx @@ -738,13 +738,13 @@ struct FlowCumulantsUpc { registry.fill(HIST("neutronClass"), 0, 0); } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { - neutronClass = 1; - registry.fill(HIST("neutronClass"), 0, 1); - } - if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 2; registry.fill(HIST("neutronClass"), 1, 0); } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 3; registry.fill(HIST("neutronClass"), 1, 1); From 5f03bc3c830bb401c19af17f32527d639d76d876 Mon Sep 17 00:00:00 2001 From: Ayan Kumar Manna Date: Tue, 29 Sep 2026 14:25:55 +0530 Subject: [PATCH 098/648] [PWGHF] Add centrality axis for secondary track contamination (#18082) --- PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx | 7 +++++++ PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx | 16 ++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx index 496df5d5b51..e9a4ccd4282 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx @@ -239,6 +239,7 @@ struct HfCorrelatorD0Hadrons { Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its D-meson mother instead of skipping it"}; Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analyses"}; + Configurable enableCentralityQA{"enableCentralityQA", false, "Enable FT0M centrality vs multiplicity QA in processData"}; int leadingIndex = 0; double softPiMass = 0.14543; // pion mass + Q-value of the D*->D0pi decay @@ -366,6 +367,9 @@ struct HfCorrelatorD0Hadrons { registry.get(HIST("hTrackCounter"))->GetXaxis()->SetBinLabel(5, "fake tracks"); registry.add("hZvtx", "z vertex", {HistType::kTH1F, {axisPosZ}}); registry.add("hMultFT0M", "Multiplicity FT0M", {HistType::kTH1F, {axisMultFT0M}}); + if (enableCentralityQA) { + registry.add("hCentFT0MVsMultFT0M", "Centrality FT0M vs multiplicity FT0M", {HistType::kTH2D, {axisMultFT0M, axisCent}}); + } registry.add("hCollisionPoolBin", "collision pool bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hD0PoolBin", "D0 selected in pool Bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hTracksPoolBin", "Particles associated pool bin", {HistType::kTH1F, {axisPoolBin}}); @@ -398,6 +402,9 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hCollisionPoolBin"), poolBin); registry.fill(HIST("hZvtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + if (enableCentralityQA) { + registry.fill(HIST("hCentFT0MVsMultFT0M"), collision.multFT0M(), collision.centFT0M()); + } int nTracks = 0; if (collision.numContrib() > 1) { diff --git a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx index 334b2878346..e9510c42a1c 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx @@ -207,8 +207,8 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hDeltaEtaPtIntSignalRegionRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSignalRegionRecSig", "D0-h deltaPhi MC reco signal region, signal", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSignalRegionRecSig", "D0-h deltaPhi vs deltaEta MC reco signal region, signal", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); + registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); registry.add("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig", "D0-h correlations signal region Prompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionNonPromptD0NonPromptHadronRecSig", "D0-h correlations signal region NonPrompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSignalRegionSoftPiRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); @@ -601,6 +601,10 @@ struct HfTaskCorrelationD0Hadrons { } for (const auto& pairEntry : pairEntries) { + float cent = 0.; + if (useCentrality) { + cent = pairEntry.cent(); + } // define variables for widely used quantities double const deltaPhi = pairEntry.deltaPhi(); double const deltaEta = pairEntry.deltaEta(); @@ -685,12 +689,12 @@ struct HfTaskCorrelationD0Hadrons { // ---------------------- Fill plots for signal case, D0 ->1, D0bar ->8 --------------------------------------------- if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig) && passesD0Ml) { // in signal region, tests bit ParticleTypeMcRec::D0Sig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { @@ -701,12 +705,12 @@ struct HfTaskCorrelationD0Hadrons { if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig) && passesD0barMl) { // in signal region, tests bit ParticleTypeMcRec::D0barSig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { From 7ec2f8695d22d37fa7d4824047172a2ef9c86919 Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:54:25 +0200 Subject: [PATCH 099/648] [PWGHF] Add eta selection in mlBasedTrackSelector (#18087) --- PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx index fd69e2bc816..32a7ba5ad8c 100644 --- a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx +++ b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx @@ -312,6 +312,7 @@ struct HfTrackSelectorTagSelTracks { Configurable testAcknowledgement{"testAcknowledgement", false, "test acknowledgement"}; Configurable fillHistograms{"fillHistograms", true, "fill histograms"}; Configurable ptMinTrack{"ptMinTrack", 0.3f, "min. track pT entering the charm combinatorics"}; + Configurable etaMaxTrack{"etaMaxTrack", 0.8f, "max. track eta entering the charm combinatorics"}; Configurable enableTiming{"enableTiming", false, "fill hTiming with the CPU of the feature building and of each model evaluation (adds two clock reads per call)"}; // D+ model Configurable applyMlDplus{"applyMlDplus", true, "evaluate the D+ track model"}; @@ -589,7 +590,7 @@ struct HfTrackSelectorTagSelTracks { registry.fill(HIST("hPtNoCuts"), track.pt()); } - const bool passesQuality = track.pt() >= config.ptMinTrack && track.isGlobalTrackWoDCA(); + const bool passesQuality = track.pt() >= config.ptMinTrack && std::abs(track.eta()) <= config.etaMaxTrack && track.isGlobalTrackWoDCA(); if (passesQuality && !scoreCollision && config.fillHistograms) { registry.fill(HIST("hPtQualityRejColl"), track.pt()); } From 4c87d0e4eb1f64ce868581c6d6c22c0544cdf862 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Tue, 29 Sep 2026 12:41:17 +0200 Subject: [PATCH 100/648] [PWGLF] Add derivedXi0analysis task (#18081) Co-authored-by: ALICE Action Bot --- PWGLF/DataModel/LFSigmaTables.h | 17 +- .../Strangeness/sigma0builder.cxx | 13 +- PWGLF/Tasks/Strangeness/CMakeLists.txt | 5 + .../Tasks/Strangeness/derivedXi0analysis.cxx | 1274 +++++++++++++++++ 4 files changed, 1298 insertions(+), 11 deletions(-) create mode 100644 PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx diff --git a/PWGLF/DataModel/LFSigmaTables.h b/PWGLF/DataModel/LFSigmaTables.h index de5d0f46acd..4428b3b2dff 100644 --- a/PWGLF/DataModel/LFSigmaTables.h +++ b/PWGLF/DataModel/LFSigmaTables.h @@ -1452,7 +1452,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(Xi0Mass, xi0Mass, std::array pVecPi0{photon1Px + photon2Px, photon1Py + photon2Py, photon1Pz + photon2Pz}; std::array pVecLambda{lambdaPx, lambdaPy, lambdaPz}; auto arrMom = std::array{pVecPi0, pVecLambda}; - return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassXi0}); + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassLambda0}); }); DECLARE_SOA_DYNAMIC_COLUMN(PxPi0, pxPi0, //! Pi0 px @@ -1607,11 +1607,12 @@ DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", Xi0Core::Pz, Xi0Core::Pt, Xi0Core::P, + Xi0Core::Xi0Mass, Xi0Core::Rapidity, Xi0Core::Phi, Xi0Core::Eta, Xi0Core::Radius, - Xi0Core::CascCosPA, + Xi0Core::CascCosPA, // Dynamic columns for pi0 Xi0Core::PxPi0, @@ -1620,7 +1621,7 @@ DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", Xi0Core::Pi0Mass, Xi0Core::RadiusPi0, Xi0Core::Pi0CosPA, - Xi0Core::DCAPi0ToPV, + Xi0Core::DCAPi0ToPV, // Dynamic columns for Lambda Xi0Core::RadiusLambda, @@ -1884,11 +1885,11 @@ DECLARE_SOA_TABLE(Xi0MCCores, "AOD", "XI0MCCORES", Xi0MCCore::MCRadius, Xi0MCCore::MCRadiusPi0, - Xi0MCCore::MCPx, - Xi0MCCore::MCPy, - Xi0MCCore::MCPz, - Xi0MCCore::MCPt, - Xi0MCCore::MCP, + Xi0MCCore::Pi0MCPx, + Xi0MCCore::Pi0MCPy, + Xi0MCCore::Pi0MCPz, + Xi0MCCore::Pi0MCPt, + Xi0MCCore::Pi0MCP, Xi0MCCore::Pi0MCMass, Xi0MCCore::Pi0RapidityMC, Xi0MCCore::Pi0MCPhi, diff --git a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx index 2808e65d893..cb51f93b6d3 100644 --- a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) @@ -1511,7 +1512,7 @@ struct sigma0builder { if (MCMother_pi0.globalIndex() == MCMother_lambda.globalIndex()) { // check that lambda and pi0 have the same mother. MCinfo.xi0PDGCode = MCMother_pi0.pdgCode(); MCinfo.xi0MCProcess = MCMother_pi0.getProcess(); - MCinfo.pi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); + MCinfo.xi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); MCinfo.xi0X = MCMother_pi0.vx(); MCinfo.xi0Y = MCMother_pi0.vy(); MCinfo.xi0Z = MCMother_pi0.vz(); @@ -2703,7 +2704,7 @@ struct sigma0builder { //_______________________________________________ // Build Xi0 candidate for analysis template - bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, TCollision const& collision, TMCParticles const& mcparticles) + bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, V0PairTopoInfo& pi0Info, TCollision const& collision, TMCParticles const& mcparticles) { Xi0Info cascade; //_______________________________________________ @@ -2764,6 +2765,8 @@ struct sigma0builder { return false; } + cascade.pi0DaughterDCA = pi0Info.daughterDCA; + lGamma1Track.getPxPyPzGlo(cascade.gamma1Momentum); lGamma2Track.getPxPyPzGlo(cascade.gamma2Momentum); lLambdaTrack.getPxPyPzGlo(cascade.lambdaMomentum); @@ -2776,6 +2779,10 @@ struct sigma0builder { return false; } + cascade.pi0Position[0] = pi0Info.position[0]; + cascade.pi0Position[1] = pi0Info.position[1]; + cascade.pi0Position[2] = pi0Info.position[2]; + double cosPA = RecoDecay::cpa( std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2]}, @@ -3418,7 +3425,7 @@ struct sigma0builder { auto gamma1 = fullV0s.rawIteratorAt(pi0Candidates[j].v01Index); auto gamma2 = fullV0s.rawIteratorAt(pi0Candidates[j].v02Index); // Building pi0 candidate & filling tables - if (!buildXi0(gamma1, gamma2, lambda, coll, mcparticles)) + if (!buildXi0(gamma1, gamma2, lambda, pi0Candidates[j], coll, mcparticles)) continue; } } diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 9b08a271008..7a757d05e88 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -69,6 +69,11 @@ o2physics_add_dpl_workflow(derivedcascadeanalysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(derivedxi0analysis + SOURCES derivedXi0analysis.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(cascpostprocessing SOURCES cascpostprocessing.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx new file mode 100644 index 00000000000..cfc54fd89fa --- /dev/null +++ b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx @@ -0,0 +1,1274 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file derivedXi0Analysis.cxx +/// \brief Xi0 (--> Lambda pi0 --> Lambda gamma gamma) analysis task using strangeness derived data (produced with the Xi0 builder) +/// +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// +// Xi0 analysis task +// ================= +// +// This code loops over the Xi0Cores table produced by the sigma0builder +// (PWGLF/TableProducer/Strangeness/sigma0builder.cxx, fillXi0Tables = true) +// and produces some standard analysis output. It is meant to be run over +// strangeness derived data. +// +// The Xi0 candidate is built out of three V0s: two photon conversions +// (forming the pi0) and one Lambda. The daughter V0s are reached by +// de-referencing the V0Cores table through the Xi0Indices table. +// +// Three process functions are provided: +// - processRealData : real data +// - processMonteCarlo : reconstructed information in MC +// - processGenerated : pure generated information in MC (from CascMCCores) +// +// N.B.: when running over reconstructed MC information, the rapidity and the +// pT used both in the selections and in the histograms are the generated +// ones, so that the numerator and the denominator of the +// acceptance x efficiency share exactly the same definition. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// romain.schotter@cern.ch +// + +#include "PWGLF/DataModel/LFSigmaTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// simple checkers, but ensure 64 bit integers +#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) + +// Xi0 candidates: cores + collision reference + indices to the daughter V0s. +// N.B.: aod::Xi0Indices and aod::Xi0MCIndices must never be joined together, +// since they share the very same column names (photon1Index, ...). +using Xi0Candidates = soa::Join; +using Xi0McCandidates = soa::Join; + +using V0Candidates = soa::Join; +using DauTracks = soa::Join; + +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + +struct DerivedXi0Analysis { + Service ccdb; + ctpRateFetcher rateFetcher; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Xi0 proper decay length, in cm (PDG: tau = 2.90e-10 s) + static constexpr float CtauXi0 = 8.71f; + // number of QA histogram sets: before and after the candidate selections + static constexpr int NSelectionStages = 2; + + //__________________________________________________ + // Selection bits: one per selection criterion, so that the full selection is + // a single mask operation and the bookkeeping fits in a single histogram + enum Selectionbits : int { // o2-linter: disable=name/enum (bit names follow derivedlambdakzeroanalysis) + // photon selections (both photons of the pi0 have to pass) + selPhotonV0Type = 0, + selPhotonMass, + selPhotonRapidity, + selPhotonDauEta, + selPhotonDCADauToPV, + selPhotonDCADau, + selPhotonRadius, + selPhotonCosPA, + selPhotonArmenteros, + selPhotonPt, + selPhotonTPCCrossedRows, + selPhotonTPCPID, + // Lambda selections, common to Lambda and anti-Lambda + selLambdaV0Type, + selLambdaRapidity, + selLambdaDauEta, + selLambdaDCAPosToPV, + selLambdaDCANegToPV, + selLambdaDCADau, + selLambdaRadius, + selLambdaCosPA, + selLambdaLifetime, + selLambdaTPCCrossedRows, + selLambdaITSClusters, + // Lambda selections, specific to one of the two mass hypotheses + selLambdaMass, + selAntiLambdaMass, + selLambdaArmenteros, // Armenteros alpha > 0 --> the positive daughter is the baryon + selAntiLambdaArmenteros, // Armenteros alpha < 0 --> the negative daughter is the baryon + selTPCPIDPositiveProton, + selTPCPIDNegativePion, + selTPCPIDNegativeProton, + selTPCPIDPositivePion, + // pi0 and Xi0 (cascade) selections + selPi0Mass, + selPi0Radius, + selPi0CosPA, + selPi0DCADau, + selXi0Radius, + selXi0CosPA, + selXi0DCADau, + selXi0DCAxyToPV, + selXi0DCAzToPV, + selXi0Lifetime, + selXi0Rapidity, + selXi0Pt, + // MC tagging + selConsiderXi0, + selConsiderAntiXi0, + selPhysPrimXi0, + selPhysPrimAntiXi0, + }; + + uint64_t maskTopological = 0; + uint64_t maskXi0Specific = 0; + uint64_t maskAntiXi0Specific = 0; + uint64_t maskSelectionXi0 = 0; + uint64_t maskSelectionAntiXi0 = 0; + + //__________________________________________________ + // Event level + Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; + Configurable fGetIR{"fGetIR", false, "Flag to retrieve the IR info."}; + Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; + Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; + Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + //__________________________________________________ + // Photon (from the pi0) selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "photonSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 7, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable maxMass{"maxMass", 0.1, "Max photon mass (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min photon rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max photon rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCADauToPV{"minDCADauToPV", 0.0, "Min DCA of the daughter tracks to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 3.5, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.0, "Min photon conversion radius (cm)"}; + Configurable maxRadius{"maxRadius", 240.0, "Max photon conversion radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.8, "Min photon cosine of pointing angle"}; + Configurable maxQt{"maxQt", 0.05, "Max qt (Armenteros-Podolanski) (GeV/c)"}; + Configurable maxAlpha{"maxAlpha", 0.95, "Max |alpha| (Armenteros-Podolanski)"}; + Configurable minPt{"minPt", 0.0, "Min photon pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max photon pT (GeV/c)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 30, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -7, "Min TPC NSigma (electron) of the daughter tracks"}; + Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +7, "Max TPC NSigma (electron) of the daughter tracks"}; + } photonSelections; + + //__________________________________________________ + // Lambda selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable massWindow{"massWindow", 0.015, "Lambda mass window around the PDG value (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min Lambda rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max Lambda rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05, "Min DCA of the positive daughter to PV (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05, "Min DCA of the negative daughter to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 1.0, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.5, "Min Lambda decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.0, "Max Lambda decay radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.95, "Min Lambda cosine of pointing angle (w.r.t. the PV)"}; + Configurable maxLifetime{"maxLifetime", 30.0, "Max Lambda proper lifetime, m*L/p (cm)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minITSclusters{"minITSclusters", -1, "Min number of ITS clusters of the daughter tracks (leave negative if no selection desired)"}; + Configurable maxTPCNSigmaPr{"maxTPCNSigmaPr", 5, "Max |TPC NSigma| (proton) of the baryon daughter"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5, "Max |TPC NSigma| (pion) of the meson daughter"}; + // Lambda / anti-Lambda are told apart with the Armenteros-Podolanski alpha: + // the baryon takes most of the momentum, so alpha > 0 for Lambda (positive + // daughter = proton) and alpha < 0 for anti-Lambda (negative daughter = anti-proton) + Configurable minAbsAlpha{"minAbsAlpha", 0.25, "Min |alpha| (Armenteros-Podolanski) to tag the Lambda charge"}; + Configurable maxAbsAlpha{"maxAbsAlpha", 1.0, "Max |alpha| (Armenteros-Podolanski)"}; + } lambdaSelections; + + //__________________________________________________ + // Xi0 selections, applied on the cascade itself + struct : ConfigurableGroup { + std::string prefix = "xi0Selections"; // JSON group name + Configurable minRapidity{"minRapidity", -0.5, "Min Xi0 rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.5, "Max Xi0 rapidity"}; + Configurable minPt{"minPt", 0.0, "Min Xi0 pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max Xi0 pT (GeV/c)"}; + Configurable pi0MassWindow{"pi0MassWindow", 0.035, "pi0 mass window around the PDG value (GeV/c^2)"}; + Configurable minPi0Radius{"minPi0Radius", -1, "Min pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable maxPi0Radius{"maxPi0Radius", -1, "Max pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable minPi0CosPA{"minPi0CosPA", -2, "Min pi0 cosine of pointing angle, leave below -1 if no selection desired"}; + Configurable maxDCAPi0Daughters{"maxDCAPi0Daughters", -1, "Max DCA between the two photons (cm), leave negative if no selection desired"}; + Configurable minCascRadius{"minCascRadius", 0.5, "Min Xi0 decay radius (cm)"}; + Configurable maxCascRadius{"maxCascRadius", 200.0, "Max Xi0 decay radius (cm)"}; + Configurable minCascCosPA{"minCascCosPA", 0.98, "Min Xi0 cosine of pointing angle"}; + Configurable maxDCACascDaughters{"maxDCACascDaughters", 1.0, "Max DCA between the Xi0 daughters (cm)"}; + Configurable maxDCAxyCascToPV{"maxDCAxyCascToPV", 1.0, "Max |DCAxy| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxDCAzCascToPV{"maxDCAzCascToPV", -1, "Max |DCAz| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxLifetime{"maxLifetime", 3.0, "Max Xi0 proper lifetime, in units of c*tau"}; + } xi0Selections; + + //__________________________________________________ + // MC-specific selections + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; // JSON group name + Configurable doMCAssociation{"doMCAssociation", false, "Keep only candidates whose full decay chain is matched to a true Xi0/anti-Xi0"}; + Configurable requirePhysicalPrimary{"requirePhysicalPrimary", true, "Keep only physical primary Xi0, at reconstructed and generated level"}; + } mcSelections; + + //__________________________________________________ + // QA switches + Configurable doEventQA{"doEventQA", true, "do event QA histograms"}; + Configurable doCandidateQA{"doCandidateQA", true, "do candidate-level QA histograms"}; + Configurable doMCQA{"doMCQA", true, "do MC-specific QA histograms (pT resolution, background composition)"}; + + //__________________________________________________ + // Axes + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality (%)"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisXi0Mass{"axisXi0Mass", {200, 1.2f, 1.5f}, "#it{M}_{#Lambda#pi^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPi0Mass{"axisPi0Mass", {200, 0.0f, 0.4f}, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.07f, 1.17f}, "#it{M}_{p#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisRadius{"axisRadius", {200, 0.0f, 100.0f}, "Decay radius (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.9f, 1.0f}, "cos(#theta_{PA})"}; + ConfigurableAxis axisDCADau{"axisDCADau", {200, 0.0f, 5.0f}, "DCA between daughters (cm)"}; + ConfigurableAxis axisDCAToPV{"axisDCAToPV", {200, -5.0f, 5.0f}, "DCA to PV (cm)"}; + ConfigurableAxis axisLifetime{"axisLifetime", {200, 0.0f, 50.0f}, "Proper lifetime (cm)"}; + ConfigurableAxis axisRapidity{"axisRapidity", {200, -1.0f, 1.0f}, "#it{y}"}; + ConfigurableAxis axisArmAlpha{"axisArmAlpha", {200, -1.0f, 1.0f}, "#alpha (Armenteros-Podolanski)"}; + ConfigurableAxis axisArmQt{"axisArmQt", {200, 0.0f, 0.5f}, "#it{q}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "#it{N}_{ch}"}; + ConfigurableAxis axisPtResolution{"axisPtResolution", {200, -1.0f, 1.0f}, "(#it{p}_{T}^{rec} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20.0f, 20.0f}, "PV #it{z} (cm)"}; + ConfigurableAxis axisPDGCode{"axisPDGCode", {10001, -5000.5f, 5000.5f}, "PDG code"}; + ConfigurableAxis axisNCandidates{"axisNCandidates", {10, -0.5f, 9.5f}, "Number of candidates"}; + + void init(InitContext const&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + //_______________________________________________ + // Assemble the selection masks once and for all + maskTopological = 0; + BITSET(maskTopological, selPhotonV0Type); + BITSET(maskTopological, selPhotonMass); + BITSET(maskTopological, selPhotonRapidity); + BITSET(maskTopological, selPhotonDauEta); + BITSET(maskTopological, selPhotonDCADauToPV); + BITSET(maskTopological, selPhotonDCADau); + BITSET(maskTopological, selPhotonRadius); + BITSET(maskTopological, selPhotonCosPA); + BITSET(maskTopological, selPhotonArmenteros); + BITSET(maskTopological, selPhotonPt); + BITSET(maskTopological, selPhotonTPCCrossedRows); + BITSET(maskTopological, selPhotonTPCPID); + BITSET(maskTopological, selLambdaV0Type); + BITSET(maskTopological, selLambdaRapidity); + BITSET(maskTopological, selLambdaDauEta); + BITSET(maskTopological, selLambdaDCAPosToPV); + BITSET(maskTopological, selLambdaDCANegToPV); + BITSET(maskTopological, selLambdaDCADau); + BITSET(maskTopological, selLambdaRadius); + BITSET(maskTopological, selLambdaCosPA); + BITSET(maskTopological, selLambdaLifetime); + BITSET(maskTopological, selLambdaTPCCrossedRows); + BITSET(maskTopological, selLambdaITSClusters); + BITSET(maskTopological, selPi0Mass); + BITSET(maskTopological, selPi0Radius); + BITSET(maskTopological, selPi0CosPA); + BITSET(maskTopological, selPi0DCADau); + BITSET(maskTopological, selXi0Radius); + BITSET(maskTopological, selXi0CosPA); + BITSET(maskTopological, selXi0DCADau); + BITSET(maskTopological, selXi0DCAxyToPV); + BITSET(maskTopological, selXi0DCAzToPV); + BITSET(maskTopological, selXi0Lifetime); + BITSET(maskTopological, selXi0Rapidity); + BITSET(maskTopological, selXi0Pt); + + maskXi0Specific = 0; + BITSET(maskXi0Specific, selLambdaMass); + BITSET(maskXi0Specific, selLambdaArmenteros); + BITSET(maskXi0Specific, selTPCPIDPositiveProton); + BITSET(maskXi0Specific, selTPCPIDNegativePion); + BITSET(maskXi0Specific, selConsiderXi0); + + maskAntiXi0Specific = 0; + BITSET(maskAntiXi0Specific, selAntiLambdaMass); + BITSET(maskAntiXi0Specific, selAntiLambdaArmenteros); + BITSET(maskAntiXi0Specific, selTPCPIDNegativeProton); + BITSET(maskAntiXi0Specific, selTPCPIDPositivePion); + BITSET(maskAntiXi0Specific, selConsiderAntiXi0); + + if (mcSelections.requirePhysicalPrimary) { + BITSET(maskXi0Specific, selPhysPrimXi0); + BITSET(maskAntiXi0Specific, selPhysPrimAntiXi0); + } + + maskSelectionXi0 = maskTopological | maskXi0Specific; + maskSelectionAntiXi0 = maskTopological | maskAntiXi0Specific; + + //_______________________________________________ + // Event selection bookkeeping + auto hEventSelection = histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +22.5f}}); + hEventSelection->GetXaxis()->SetBinLabel(1, "All collisions"); + hEventSelection->GetXaxis()->SetBinLabel(2, "sel8 cut"); + hEventSelection->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + hEventSelection->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(6, "posZ cut"); + hEventSelection->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); + hEventSelection->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + hEventSelection->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); + hEventSelection->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); + hEventSelection->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); + hEventSelection->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); + hEventSelection->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); + hEventSelection->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); + hEventSelection->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); + hEventSelection->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + hEventSelection->GetXaxis()->SetBinLabel(17, "INEL>0"); + hEventSelection->GetXaxis()->SetBinLabel(18, "INEL>1"); + hEventSelection->GetXaxis()->SetBinLabel(19, "Below min occup."); + hEventSelection->GetXaxis()->SetBinLabel(20, "Above max occup."); + hEventSelection->GetXaxis()->SetBinLabel(21, "Below min IR"); + hEventSelection->GetXaxis()->SetBinLabel(22, "Above max IR"); + hEventSelection->GetXaxis()->SetBinLabel(23, "Selected collisions"); + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); + histos.add("hEventPVz", "hEventPVz", kTH1D, {axisVtxZ}); + if (doEventQA) { + histos.add("EventQA/hCentralityVsNch", "hCentralityVsNch", kTH2D, {axisCentrality, axisNch}); + histos.add("EventQA/hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {axisCentrality, axisVtxZ}); + } + + //_______________________________________________ + // Single candidate-selection bookkeeping histogram + auto hSelections = histos.add("hSelections", "hSelections", kTH1D, {{static_cast(selPhysPrimAntiXi0) + 3, -0.5f, static_cast(selPhysPrimAntiXi0) + 2.5f}}); + hSelections->GetXaxis()->SetBinLabel(1, "All"); + hSelections->GetXaxis()->SetBinLabel(selPhotonV0Type + 2, "#gamma V0 type"); + hSelections->GetXaxis()->SetBinLabel(selPhotonMass + 2, "#gamma mass"); + hSelections->GetXaxis()->SetBinLabel(selPhotonRapidity + 2, "#gamma rapidity"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDauEta + 2, "#gamma dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADauToPV + 2, "#gamma DCA dau. to PV"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADau + 2, "#gamma DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selPhotonRadius + 2, "#gamma radius"); + hSelections->GetXaxis()->SetBinLabel(selPhotonCosPA + 2, "#gamma cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPhotonArmenteros + 2, "#gamma Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selPhotonPt + 2, "#gamma #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCCrossedRows + 2, "#gamma TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCPID + 2, "#gamma TPC PID"); + hSelections->GetXaxis()->SetBinLabel(selLambdaV0Type + 2, "#Lambda V0 type"); + hSelections->GetXaxis()->SetBinLabel(selLambdaRapidity + 2, "#Lambda rapidity"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDauEta + 2, "#Lambda dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCAPosToPV + 2, "#Lambda DCA pos. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCANegToPV + 2, "#Lambda DCA neg. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCADau + 2, "#Lambda DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selLambdaRadius + 2, "#Lambda radius"); + hSelections->GetXaxis()->SetBinLabel(selLambdaCosPA + 2, "#Lambda cosPA"); + hSelections->GetXaxis()->SetBinLabel(selLambdaLifetime + 2, "#Lambda lifetime"); + hSelections->GetXaxis()->SetBinLabel(selLambdaTPCCrossedRows + 2, "#Lambda TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selLambdaITSClusters + 2, "#Lambda ITS clusters"); + hSelections->GetXaxis()->SetBinLabel(selLambdaMass + 2, "#Lambda mass"); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaMass + 2, "#bar{#Lambda} mass"); + hSelections->GetXaxis()->SetBinLabel(selLambdaArmenteros + 2, "#Lambda Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaArmenteros + 2, "#bar{#Lambda} Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositiveProton + 2, "TPC PID p"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativePion + 2, "TPC PID #pi^{-}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativeProton + 2, "TPC PID #bar{p}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositivePion + 2, "TPC PID #pi^{+}"); + hSelections->GetXaxis()->SetBinLabel(selPi0Mass + 2, "#pi^{0} mass"); + hSelections->GetXaxis()->SetBinLabel(selPi0Radius + 2, "#pi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selPi0CosPA + 2, "#pi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPi0DCADau + 2, "#pi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0Radius + 2, "#Xi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selXi0CosPA + 2, "#Xi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCADau + 2, "#Xi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAxyToPV + 2, "#Xi^{0} DCA_{xy} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAzToPV + 2, "#Xi^{0} DCA_{z} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0Lifetime + 2, "#Xi^{0} lifetime"); + hSelections->GetXaxis()->SetBinLabel(selXi0Rapidity + 2, "#Xi^{0} rapidity"); + hSelections->GetXaxis()->SetBinLabel(selXi0Pt + 2, "#Xi^{0} #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderXi0 + 2, "True #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderAntiXi0 + 2, "True #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimXi0 + 2, "Phys. prim. #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 2, "Phys. prim. #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 3, "Cand. selected"); + + //_______________________________________________ + // Main analysis output + histos.add("h3dMassXi0", "h3dMassXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h3dMassAntiXi0", "h3dMassAntiXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h2dNbrOfXi0VsCentrality", "h2dNbrOfXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + histos.add("h2dNbrOfAntiXi0VsCentrality", "h2dNbrOfAntiXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + + //_______________________________________________ + // Candidate QA, before and after the selections + if (doCandidateQA) { + for (int mode = 0; mode < NSelectionStages; mode++) { + const std::string dir = (mode == 0) ? "QA/BeforeSel/" : "QA/AfterSel/"; + histos.add(dir + "h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add(dir + "hMassPi0", "hMassPi0", kTH1D, {axisPi0Mass}); + histos.add(dir + "hMassLambda", "hMassLambda", kTH1D, {axisLambdaMass}); + histos.add(dir + "hPt", "hPt", kTH1D, {axisPt}); + histos.add(dir + "hRapidity", "hRapidity", kTH1D, {axisRapidity}); + histos.add(dir + "hCascRadius", "hCascRadius", kTH1D, {axisRadius}); + histos.add(dir + "hCascCosPA", "hCascCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCACascDaughters", "hDCACascDaughters", kTH1D, {axisDCADau}); + histos.add(dir + "hDCAxyCascToPV", "hDCAxyCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hDCAzCascToPV", "hDCAzCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hLifetime", "hLifetime", kTH1D, {axisLifetime}); + histos.add(dir + "hLambdaRadius", "hLambdaRadius", kTH1D, {axisRadius}); + histos.add(dir + "hLambdaCosPA", "hLambdaCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hPi0Radius", "hPi0Radius", kTH1D, {axisRadius}); + histos.add(dir + "hPi0CosPA", "hPi0CosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCAPi0Daughters", "hDCAPi0Daughters", kTH1D, {axisDCADau}); + histos.add(dir + "h2dArmenterosLambda", "h2dArmenterosLambda", kTH2D, {axisArmAlpha, axisArmQt}); + } + } + + //_______________________________________________ + // MC-specific histograms + if (doprocessMonteCarlo && doMCQA) { + histos.add("MCQA/h2dPtResolution", "h2dPtResolution", kTH2D, {axisPt, axisPtResolution}); + histos.add("MCQA/h2dPtRecoVsPtGen", "h2dPtRecoVsPtGen", kTH2D, {axisPt, axisPt}); + histos.add("MCQA/h3dMassXi0VsPtReco", "h3dMassXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/h3dMassAntiXi0VsPtReco", "h3dMassAntiXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/hXi0PDGCode", "hXi0PDGCode", kTH1D, {axisPDGCode}); + histos.add("MCQA/hXi0PDGCodeMother", "hXi0PDGCodeMother", kTH1D, {axisPDGCode}); + histos.add("MCQA/h2dPhoton1VsPhoton2PDGCode", "h2dPhoton1VsPhoton2PDGCode", kTH2D, {axisPDGCode, axisPDGCode}); + histos.add("MCQA/hLambdaPDGCode", "hLambdaPDGCode", kTH1D, {axisPDGCode}); + } + + //_______________________________________________ + // Generated-level histograms + if (doprocessGenerated) { + histos.add("Gen/hGenEvents", "hGenEvents", kTH2D, {axisNch, {2, -0.5f, +1.5f}}); + histos.add("Gen/hGenEventCentrality", "hGenEventCentrality", kTH1D, {axisCentrality}); + histos.add("Gen/hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {axisCentrality, axisNch}); + + histos.add("Gen/h2dGenXi0", "h2dGenXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0", "h2dGenAntiXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenXi0VsMultMC", "h2dGenXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenAntiXi0VsMultMC", "h2dGenAntiXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenXi0_RecoedEvt", "h2dGenXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0_RecoedEvt", "h2dGenAntiXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/hGenXi0Rapidity", "hGenXi0Rapidity", kTH1D, {axisRapidity}); + } + } + + //_______________________________________________ + // Check that all the bits of a mask are set in a bitmap + bool verifyMask(uint64_t bitmap, uint64_t mask) + { + return (bitmap & mask) == mask; + } + + //_______________________________________________ + // Centrality getter (Run 3) + template + float getCentralityRun3(TCollision const& collision) + { + if (centralityEstimator == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimator == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimator == kCentFT0CVariant1) + return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) + return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) + return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) + return collision.centFV0A(); + + return -1.f; + } + + //_______________________________________________ + // Check whether the collision passes our collision selections + template + bool isEventAccepted(TCollision const& collision, bool fillHists) + { + if (fillHists) + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds */); + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 18 /* Below min occupancy */); + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 19 /* Above max occupancy */); + + // Fetch interaction rate only if required (in order to limit ccdb calls) + float interactionRate = (fGetIR) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource, fIRCrashOnNull) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 20 /* Below min IR */); + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 21 /* Above max IR */); + + if (fillHists) { + histos.fill(HIST("hEventSelection"), 22 /* selected collisions */); + float centrality = getCentralityRun3(collision); + histos.fill(HIST("hEventCentrality"), centrality); + histos.fill(HIST("hEventPVz"), collision.posZ()); + if (doEventQA) { + histos.fill(HIST("EventQA/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + histos.fill(HIST("EventQA/hCentralityVsPVz"), centrality, collision.posZ()); + } + } + + return true; + } + + //_______________________________________________ + // Photon (pi0 daughter) selection bitmap, computed on the de-referenced V0Cores entry. + // The bitmaps of the two photons are AND-ed, so that a bit is set only if + // both photons of the pi0 satisfy the corresponding criterion. + template + uint64_t computePhotonBitmap(TV0Object const& gamma) + { + uint64_t bitMap = 0; + + if (photonSelections.v0TypeSelection < 0 || gamma.v0Type() == photonSelections.v0TypeSelection) + BITSET(bitMap, selPhotonV0Type); + + if (gamma.mGamma() > 0 && gamma.mGamma() < photonSelections.maxMass) + BITSET(bitMap, selPhotonMass); + + const float photonY = RecoDecay::y(std::array{gamma.px(), gamma.py(), gamma.pz()}, o2::constants::physics::MassGamma); + if (photonY > photonSelections.minRapidity && photonY < photonSelections.maxRapidity) + BITSET(bitMap, selPhotonRapidity); + + if (std::abs(gamma.positiveeta()) < photonSelections.maxDauEta && std::abs(gamma.negativeeta()) < photonSelections.maxDauEta) + BITSET(bitMap, selPhotonDauEta); + + if (std::abs(gamma.dcapostopv()) > photonSelections.minDCADauToPV && std::abs(gamma.dcanegtopv()) > photonSelections.minDCADauToPV) + BITSET(bitMap, selPhotonDCADauToPV); + + if (std::abs(gamma.dcaV0daughters()) < photonSelections.maxDCADau) + BITSET(bitMap, selPhotonDCADau); + + if (gamma.v0radius() > photonSelections.minRadius && gamma.v0radius() < photonSelections.maxRadius) + BITSET(bitMap, selPhotonRadius); + + if (gamma.v0cosPA() > photonSelections.minCosPA) + BITSET(bitMap, selPhotonCosPA); + + if (gamma.qtarm() < photonSelections.maxQt && std::abs(gamma.alpha()) < photonSelections.maxAlpha) + BITSET(bitMap, selPhotonArmenteros); + + if (gamma.pt() > photonSelections.minPt && gamma.pt() < photonSelections.maxPt) + BITSET(bitMap, selPhotonPt); + + auto posTrack = gamma.template posTrackExtra_as(); + auto negTrack = gamma.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows) + BITSET(bitMap, selPhotonTPCCrossedRows); + + if (posTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && posTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl && + negTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && negTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl) + BITSET(bitMap, selPhotonTPCPID); + + return bitMap; + } + + //_______________________________________________ + // Lambda selection bitmap, computed on the de-referenced V0Cores entry + template + uint64_t computeLambdaBitmap(TV0Object const& lambda, TCollision const& collision) + { + uint64_t bitMap = 0; + + if (lambdaSelections.v0TypeSelection < 0 || lambda.v0Type() == lambdaSelections.v0TypeSelection) + BITSET(bitMap, selLambdaV0Type); + + if (lambda.yLambda() > lambdaSelections.minRapidity && lambda.yLambda() < lambdaSelections.maxRapidity) + BITSET(bitMap, selLambdaRapidity); + + if (std::abs(lambda.positiveeta()) < lambdaSelections.maxDauEta && std::abs(lambda.negativeeta()) < lambdaSelections.maxDauEta) + BITSET(bitMap, selLambdaDauEta); + + if (std::abs(lambda.dcapostopv()) > lambdaSelections.minDCAPosToPV) + BITSET(bitMap, selLambdaDCAPosToPV); + + if (std::abs(lambda.dcanegtopv()) > lambdaSelections.minDCANegToPV) + BITSET(bitMap, selLambdaDCANegToPV); + + if (std::abs(lambda.dcaV0daughters()) < lambdaSelections.maxDCADau) + BITSET(bitMap, selLambdaDCADau); + + if (lambda.v0radius() > lambdaSelections.minRadius && lambda.v0radius() < lambdaSelections.maxRadius) + BITSET(bitMap, selLambdaRadius); + + if (lambda.v0cosPA() > lambdaSelections.minCosPA) + BITSET(bitMap, selLambdaCosPA); + + const float lambdaLifetime = lambda.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (lambdaSelections.maxLifetime < 0 || lambdaLifetime < lambdaSelections.maxLifetime) + BITSET(bitMap, selLambdaLifetime); + + // Lambda / anti-Lambda separation via the Armenteros-Podolanski alpha: + // in the Lambda decay the baryon carries most of the longitudinal momentum + if (lambda.alpha() > lambdaSelections.minAbsAlpha && lambda.alpha() < lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selLambdaArmenteros); + if (lambda.alpha() < -lambdaSelections.minAbsAlpha && lambda.alpha() > -lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selAntiLambdaArmenteros); + + if (std::abs(lambda.mLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selLambdaMass); + if (std::abs(lambda.mAntiLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selAntiLambdaMass); + + auto posTrack = lambda.template posTrackExtra_as(); + auto negTrack = lambda.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows) + BITSET(bitMap, selLambdaTPCCrossedRows); + + if (lambdaSelections.minITSclusters < 0 || + (posTrack.itsNCls() >= lambdaSelections.minITSclusters && negTrack.itsNCls() >= lambdaSelections.minITSclusters)) + BITSET(bitMap, selLambdaITSClusters); + + // TPC PID, for both mass hypotheses + if (std::abs(posTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDPositiveProton); + if (std::abs(negTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDNegativePion); + if (std::abs(negTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDNegativeProton); + if (std::abs(posTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDPositivePion); + + return bitMap; + } + + //_______________________________________________ + // pi0 and Xi0 (cascade) selection bitmap. + // rapidity and pt are the generated ones when running over MC. + template + uint64_t computeCascadeBitmap(TXi0Object const& xi0, TCollision const& collision, float rapidity, float pt) + { + uint64_t bitMap = 0; + + if (std::abs(xi0.pi0Mass() - o2::constants::physics::MassPi0) < xi0Selections.pi0MassWindow) + BITSET(bitMap, selPi0Mass); + + if ((xi0Selections.minPi0Radius < 0 || xi0.radiusPi0() > xi0Selections.minPi0Radius) && + (xi0Selections.maxPi0Radius < 0 || xi0.radiusPi0() < xi0Selections.maxPi0Radius)) + BITSET(bitMap, selPi0Radius); + + if (xi0Selections.minPi0CosPA < -1 || xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minPi0CosPA) + BITSET(bitMap, selPi0CosPA); + + if (xi0Selections.maxDCAPi0Daughters < 0 || xi0.dcadaughtersPi0() < xi0Selections.maxDCAPi0Daughters) + BITSET(bitMap, selPi0DCADau); + + if (xi0.radius() > xi0Selections.minCascRadius && xi0.radius() < xi0Selections.maxCascRadius) + BITSET(bitMap, selXi0Radius); + + if (xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minCascCosPA) + BITSET(bitMap, selXi0CosPA); + + if (xi0.dcadaughters() < xi0Selections.maxDCACascDaughters) + BITSET(bitMap, selXi0DCADau); + + if (xi0Selections.maxDCAxyCascToPV < 0 || std::abs(xi0.dcaXYCascToPV()) < xi0Selections.maxDCAxyCascToPV) + BITSET(bitMap, selXi0DCAxyToPV); + + if (xi0Selections.maxDCAzCascToPV < 0 || std::abs(xi0.dcaZCascToPV()) < xi0Selections.maxDCAzCascToPV) + BITSET(bitMap, selXi0DCAzToPV); + + if (xi0Selections.maxLifetime < 0 || getProperLifetime(xi0, collision) < xi0Selections.maxLifetime * CtauXi0) + BITSET(bitMap, selXi0Lifetime); + + if (rapidity > xi0Selections.minRapidity && rapidity < xi0Selections.maxRapidity) + BITSET(bitMap, selXi0Rapidity); + + if (pt > xi0Selections.minPt && pt < xi0Selections.maxPt) + BITSET(bitMap, selXi0Pt); + + return bitMap; + } + + //_______________________________________________ + // MC association bitmap. + // The builder sets the Xi0 PDG code only if the full decay chain is matched, + // i.e. two true photons from the same true pi0 and a true Lambda sharing the + // same true Xi0 mother, so a single PDG check is enough here. + template + uint64_t computeMCAssociation(TXi0Object const& xi0) + { + uint64_t bitMap = 0; + + if (xi0.pdgCode() == o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimXi0); + } + if (xi0.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderAntiXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimAntiXi0); + } + + return bitMap; + } + + //_______________________________________________ + // Xi0 proper lifetime, m*L/p, in cm + template + float getProperLifetime(TXi0Object const& xi0, TCollision const& collision) + { + const float decayLength = RecoDecay::distance(std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{xi0.x(), xi0.y(), xi0.z()}); + const float totalMomentum = xi0.p(); + return (totalMomentum > 0) ? o2::constants::physics::MassXi0 * decayLength / totalMomentum : 1e6f; + } + + //_______________________________________________ + // Fill the candidate QA histograms. mode = 0: before selections, 1: after selections + template + void fillCandidateQA(TXi0Object const& xi0, TV0Object const& lambda, TCollision const& collision, float pt, float rapidity, float centrality, float massLambda) + { + static constexpr std::string_view MainDir[] = {"QA/BeforeSel", "QA/AfterSel"}; + + histos.fill(HIST(MainDir[mode]) + HIST("/h3dMass"), centrality, pt, xi0.xi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassPi0"), xi0.pi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassLambda"), massLambda); + histos.fill(HIST(MainDir[mode]) + HIST("/hPt"), pt); + histos.fill(HIST(MainDir[mode]) + HIST("/hRapidity"), rapidity); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascRadius"), xi0.radius()); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascCosPA"), xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCACascDaughters"), xi0.dcadaughters()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAxyCascToPV"), xi0.dcaXYCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAzCascToPV"), xi0.dcaZCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLifetime"), getProperLifetime(xi0, collision)); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaRadius"), xi0.radiusLambda()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaCosPA"), xi0.lambdaCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0Radius"), xi0.radiusPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0CosPA"), xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAPi0Daughters"), xi0.dcadaughtersPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/h2dArmenterosLambda"), lambda.alpha(), lambda.qtarm()); + } + + //_______________________________________________ + // Main reconstructed-level analysis function, common to data and MC + template + void analyseRecoedXi0s(TCollisions const& collisions, TXi0s const& fullXi0s, TV0s const& fullV0s) + { + // Custom grouping: the Xi0 candidates are not sorted per collision + std::vector> xi0grouped(collisions.size()); + for (const auto& xi0 : fullXi0s) { + xi0grouped[xi0.straCollisionId()].push_back(xi0.globalIndex()); + } + + const int64_t nV0s = fullV0s.size(); + + for (const auto& coll : collisions) { + // Event selection + if (!isEventAccepted(coll, true)) { + continue; + } + const float centrality = getCentralityRun3(coll); + + int nXi0s = 0; + int nAntiXi0s = 0; + + // Xi0 candidates loop + for (size_t i = 0; i < xi0grouped[coll.globalIndex()].size(); i++) { + auto xi0 = fullXi0s.rawIteratorAt(xi0grouped[coll.globalIndex()][i]); + + histos.fill(HIST("hSelections"), 0 /* all candidates */); + + //_______________________________________________ + // De-reference the three daughter V0s + if (xi0.photon1Index() < 0 || xi0.photon1Index() >= nV0s || + xi0.photon2Index() < 0 || xi0.photon2Index() >= nV0s || + xi0.lambdaIndex() < 0 || xi0.lambdaIndex() >= nV0s) { + continue; + } + auto photon1 = fullV0s.rawIteratorAt(xi0.photon1Index()); + auto photon2 = fullV0s.rawIteratorAt(xi0.photon2Index()); + auto lambda = fullV0s.rawIteratorAt(xi0.lambdaIndex()); + + //_______________________________________________ + // Kinematics entering both the selections and the histograms. + // Over MC, the generated rapidity and pT are used instead of the + // reconstructed ones, so that the acceptance x efficiency numerator + // and denominator are defined identically. + const float ptRecoed = xi0.pt(); + float pt = ptRecoed; + float rapidity = xi0.rapidity(); + bool hasMCInfo = false; + if constexpr (requires { xi0.pdgCode(); }) { + // the MC information is available only if the three daughter V0s + // could be associated to a V0MCCore entry by the builder + hasMCInfo = (xi0.photon1PDGCode() != 0 && xi0.photon2PDGCode() != 0 && xi0.lambdaPDGCode() != 0); + if (hasMCInfo) { + pt = xi0.mcpt(); + rapidity = xi0.rapidityMC(); + } + } + + //_______________________________________________ + // Assemble the selection bitmap + uint64_t selMap = computePhotonBitmap(photon1) & computePhotonBitmap(photon2); + selMap |= computeLambdaBitmap(lambda, coll); + selMap |= computeCascadeBitmap(xi0, coll, rapidity, pt); + + if constexpr (requires { xi0.pdgCode(); }) { + selMap |= computeMCAssociation(xi0); + + if (doMCQA) { + histos.fill(HIST("MCQA/hXi0PDGCode"), xi0.pdgCode()); + histos.fill(HIST("MCQA/hXi0PDGCodeMother"), xi0.pdgCodeMother()); + histos.fill(HIST("MCQA/h2dPhoton1VsPhoton2PDGCode"), xi0.photon1PDGCode(), xi0.photon2PDGCode()); + histos.fill(HIST("MCQA/hLambdaPDGCode"), xi0.lambdaPDGCode()); + } + + // disregard the MC association if not explicitly asked for + if (!mcSelections.doMCAssociation) { + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + } else { + // real data: every candidate is considered for both hypotheses, + // the Armenteros bits alone tell Xi0 and anti-Xi0 apart + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + + // Selection bookkeeping: one bin per criterion + for (int bit = 0; bit <= static_cast(selPhysPrimAntiXi0); bit++) { + if (BITCHECK(selMap, bit)) { + histos.fill(HIST("hSelections"), bit + 1); + } + } + + const bool passXi0 = verifyMask(selMap, maskSelectionXi0); + const bool passAntiXi0 = verifyMask(selMap, maskSelectionAntiXi0); + // the sign of the Armenteros alpha already tells the two hypotheses apart + const float massLambda = (lambda.alpha() < 0) ? lambda.mAntiLambda() : lambda.mLambda(); + + if (doCandidateQA) { + fillCandidateQA<0>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (!passXi0 && !passAntiXi0) { + continue; + } + histos.fill(HIST("hSelections"), static_cast(selPhysPrimAntiXi0) + 2 /* candidate selected */); + + if (doCandidateQA) { + fillCandidateQA<1>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (passXi0) { + nXi0s++; + histos.fill(HIST("h3dMassXi0"), centrality, pt, xi0.xi0Mass()); + } + if (passAntiXi0) { + nAntiXi0s++; + histos.fill(HIST("h3dMassAntiXi0"), centrality, pt, xi0.xi0Mass()); + } + + //_______________________________________________ + // MC-specific QA + if constexpr (requires { xi0.pdgCode(); }) { + if (doMCQA) { + if (passXi0) { + histos.fill(HIST("MCQA/h3dMassXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (passAntiXi0) { + histos.fill(HIST("MCQA/h3dMassAntiXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (hasMCInfo && pt > 0) { + histos.fill(HIST("MCQA/h2dPtResolution"), pt, (ptRecoed - pt) / pt); + histos.fill(HIST("MCQA/h2dPtRecoVsPtGen"), pt, ptRecoed); + } + } + } + } + + histos.fill(HIST("h2dNbrOfXi0VsCentrality"), centrality, nXi0s); + histos.fill(HIST("h2dNbrOfAntiXi0VsCentrality"), centrality, nAntiXi0s); + } + } + + //_______________________________________________ + // Generated-level processing + // Return the list of indices of the reconstructed collision associated to each MC collision + template + std::vector getListOfRecoCollIndices(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector listBestCollisionIdx(mcCollisions.size(), -1); + std::vector> groupedCollisions(mcCollisions.size()); + + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + int biggestNContribs = -1; + int bestCollisionIndex = -1; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (eventSelections.useEvtSelInDenomEff && !isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + bestCollisionIndex = collision.globalIndex(); + } + } + listBestCollisionIdx[mcCollision.globalIndex()] = bestCollisionIndex; + } + return listBestCollisionIdx; + } + + //_______________________________________________ + // Event selection applied at generated level + template + bool isGeneratedEventAccepted(TMCCollision const& mcCollision) + { + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + return false; + } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + return false; + } + return true; + } + + //_______________________________________________ + // Generated-level processing: generated event properties (event loss/splitting) + template + void fillGeneratedEventProperties(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector> groupedCollisions(mcCollisions.size()); + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events */); + + bool atLeastOne = false; + int biggestNContribs = -1; + float centrality = 100.5f; + int nCollisions = 0; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (!isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + centrality = getCentralityRun3(collision); + } + nCollisions++; + atLeastOne = true; + } + + histos.fill(HIST("Gen/hCentralityVsNcoll_beforeEvSel"), centrality, static_cast(groupedCollisions[mcCollision.globalIndex()].size())); + histos.fill(HIST("Gen/hCentralityVsNcoll_afterEvSel"), centrality, nCollisions); + histos.fill(HIST("Gen/hCentralityVsMultMC"), centrality, mcCollision.multMCNParticlesEta05()); + + if (atLeastOne) { + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event */); + histos.fill(HIST("Gen/hGenEventCentrality"), centrality); + } + } + } + + //_______________________________________________ + // Main generated-level analysis function. + // The generated Xi0 are read from the CascMCCores table. + template + void analyseGeneratedXi0s(TMCCollisions const& mcCollisions, TCollisions const& collisions, TCascMCs const& cascMCCores) + { + fillGeneratedEventProperties(mcCollisions, collisions); + std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); + + for (auto const& cascMC : cascMCCores) { + if (std::abs(cascMC.pdgCode()) != o2::constants::physics::Pdg::kXi0) { + continue; + } + if (!cascMC.has_straMCCollision()) { + continue; + } + if (mcSelections.requirePhysicalPrimary && !cascMC.isPhysicalPrimary()) { + continue; + } + + // N.B.: cascdata::RapidityMC only knows about the Xi- and Omega- masses, + // so the Xi0 rapidity is computed here explicitly + const float ptMC = cascMC.ptMC(); + const float rapidityMC = RecoDecay::y(std::array{cascMC.pxMC(), cascMC.pyMC(), cascMC.pzMC()}, o2::constants::physics::MassXi0); + + histos.fill(HIST("Gen/hGenXi0Rapidity"), rapidityMC); + + // same rapidity window as at reconstructed level + if (rapidityMC < xi0Selections.minRapidity || rapidityMC > xi0Selections.maxRapidity) { + continue; + } + + auto mcCollision = cascMC.template straMCCollision_as(); + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + float centrality = 100.5f; + const int bestCollisionIdx = listBestCollisionIdx[mcCollision.globalIndex()]; + if (bestCollisionIdx > -1) { + auto collision = collisions.rawIteratorAt(bestCollisionIdx); + centrality = getCentralityRun3(collision); + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0_RecoedEvt"), centrality, ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0_RecoedEvt"), centrality, ptMC); + } + } + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenAntiXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } + } + } + + //_______________________________________________ + // Process functions + void processRealData(soa::Join const& collisions, + Xi0Candidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processMonteCarlo(soa::Join const& collisions, + Xi0McCandidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processGenerated(soa::Join const& mcCollisions, + soa::Join const& collisions, + soa::Join const& cascMCCores) + { + analyseGeneratedXi0s(mcCollisions, collisions, cascMCCores); + } + + PROCESS_SWITCH(DerivedXi0Analysis, processRealData, "process as if real data", true); + PROCESS_SWITCH(DerivedXi0Analysis, processMonteCarlo, "process reconstructed information in MC", false); + PROCESS_SWITCH(DerivedXi0Analysis, processGenerated, "process pure generated information in MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 3f9bbcebcf4944728ee46af85fcfc3558ed08723 Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Tue, 29 Sep 2026 14:08:44 +0300 Subject: [PATCH 101/648] [PWGCF] Add Fox-Wolfram correlation analysis task (#18085) Co-authored-by: Iulian --- .../Tasks/CMakeLists.txt | 5 + .../Tasks/FoxWolframCorrelation.cxx | 1290 +++++++++++++++++ 2 files changed, 1295 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index d38f6e57f9b..b6fd881bf0e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -137,3 +137,8 @@ o2physics_add_dpl_workflow(lambda-or-anti-lambda-producer-with-spin SOURCES lambdaOrAntiLambdaProducerWithSpin.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fox-wolfram-correlation + SOURCES FoxWolframCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx new file mode 100644 index 00000000000..cdef44721e6 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -0,0 +1,1290 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FoxWolframCorrelation.cxx +/// \author Andreicovici Iulian Florin +/// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. +/// Classifies events by topology (triangular), fills correlation histograms, +/// and produces custom SOA tables for mixed-event background estimation. +/// \since 25 Sept 2026 + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::soa; + +// Define compact event and track tables for mixed-event processing. +namespace o2::aod +{ +namespace collision +{ +DECLARE_SOA_COLUMN(Multip, multip, int); +} + +DECLARE_SOA_TABLE(TriangleCollisions, "AOD", "TRIANGLECOLLISION", + o2::soa::Index<>, + collision::PosZ, + collision::Multip); + +using TriangleCollision = TriangleCollisions::iterator; + +namespace track_tri +{ +DECLARE_SOA_INDEX_COLUMN(TriangleCollision, triangleCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace track_tri + +DECLARE_SOA_TABLE(TriangleTracks, "AOD", "TRITRACK", + o2::soa::Index<>, + track_tri::TriangleCollisionId, + track_tri::Pt, track_tri::Eta, track_tri::Phi); + +} // namespace o2::aod + +// Wrap the azimuthal difference to the correlation interval. +double computeDeltaPhi(double phi1, double phi2) +{ + return RecoDecay::constrainAngle(phi1 - phi2, -o2::constants::math::PIHalf); +} + +// Name the available pair-selection modes. +enum LeadAssocMode : int { + kAllPairs = 0, + kAllPairsEtaConstrain = 1, + kpTLeadConstrain = 2, + kpTLeadFree = 3 +}; + +// Name the available event-topology selections. +enum TopologyMode : int { + kAllEvents = 0, + kTriEvents = 1, + kIsoEvents = 2 +}; + +template +// Apply the same pair-selection rules to signal and background. +bool passesLeadAssoc(int mode, bool foundPart, Track const& track1, Track const& track2, + TLead const& triggTrk, TLead const& triggTrkPart) +{ + if (mode == kAllPairs) { + return (track1.pt() < 3.f) && (track2.pt() < 3.f); + } + if (mode == kAllPairsEtaConstrain) { + + const bool firstPositiveSecondNegative = + track1.eta() > 0.1f && track1.eta() < 0.8f && + track2.eta() > -0.8f && track2.eta() < -0.1f; + + const bool firstNegativeSecondPositive = + track1.eta() > -0.8f && track1.eta() < -0.1f && + track2.eta() > 0.1f && track2.eta() < 0.8f; + + return track1.pt() < 3.f && track2.pt() < 3.f && + (firstPositiveSecondNegative || firstNegativeSecondPositive); + } + if (mode == kpTLeadConstrain) { + return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < 2.f); + } + if (mode == kpTLeadFree) { + return (track1.globalIndex() == triggTrk.globalIndex()) && (track2.pt() < track1.pt()); + } + + return false; +} + +template +// Find the overall leading track and the leading track in the selected momentum window. +auto findLeadingPart(T const& tracks) +{ + int leadingAllId = 0, leadingPartId = 0; + int i = 0; + + double ptMaxAll = -1., ptMaxPart = -1.; + bool foundPart = false; + + for (const auto& track : tracks) { + if (track.pt() > ptMaxAll) { + ptMaxAll = track.pt(); + leadingAllId = i; + } + if (track.pt() > 2.f && track.pt() < 3.f && track.pt() > ptMaxPart) { + ptMaxPart = track.pt(); + leadingPartId = i; + foundPart = true; + } + i++; + } + + return std::make_tuple(tracks.iteratorAt(leadingAllId), + tracks.iteratorAt(leadingPartId), + foundPart); +} +// Store selected tracks and collisions across data frames. +using TrackTuple = std::tuple; +using CollisionTuple = std::tuple>; + +// Define the reconstructed collision and track views. +using RecCollisions = soa::Join; +using CollisionRecTable = RecCollisions::iterator; +using RecTracks = soa::Join; + +// Select events, compute moments, and fill same-event observables. +struct FoxWolframCorrelation { + + SliceCache cache; + Preslice perCollision = aod::track::collisionId; + + Produces triCollisions; + Produces triTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Configure histogram axes, event selection, and topology. + Configurable nBins{"nBins", 100, "no. bins in all histos"}; + Configurable nSelEv{"nSelEv", 200, "no. of mixing Ev."}; + Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; + Configurable etaRange{"etaRange", 0.8f, "eta range to consider"}; + Configurable dcaZ{"dcaZ", 0.2f, "custom DCA Z cut (ignored if negative)"}; + Configurable doMultipSampling{"doMultipSampling", false, "doMultipSampling"}; + Configurable doSameEvent{"doSameEvent", true, "Fill same-event TRI correlations"}; + Configurable doFWMCorrelations{"doFWMCorrelations", false, + "Fill H_i-H_j correlation histograms for RANGE1-RANGE3"}; + Configurable topologyMode{"topologyMode", kTriEvents, + "Topology used for selected-event outputs: 0 ALL, 1 TRI, 2 ISO"}; + + // Choose the pair selection for same-event correlations. + Configurable leadAssocMode{"leadAssocMode", 0, + "0: ALL i-j pairs (pT<3); 1: ALL i-j pairs (pT < 3, Eta constrain) 2: pTLead in (2,3) & pTAssoc < 2; 3: pTAssoc < pTLead"}; + + Configurable isVtxRange{"isVtxRange", true, "isVtxRange"}; + Configurable isSel8{"isSel8", true, "isSel8"}; + Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "isNoSameBunchPileup"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "isGoodZvtxFT0vsPV"}; + Configurable isVertexITSTPC{"isVertexITSTPC", false, "isVertexITSTPC"}; + Configurable isVertexTOFmatched{"isVertexTOFmatched", false, "isVertexTOFmatched"}; + Configurable kIsGoodITSLayer0123{"kIsGoodITSLayer0123", false, "kIsGoodITSLayer0123"}; + Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "isNoCollInTimeRangeNarrow"}; + + // Set the isotropic event selection thresholds. + Configurable h1IsoMax{"h1IsoMax", 0.02, "h1IsoMax"}; + Configurable h2IsoMax{"h2IsoMax", 0.27, "h2IsoMax"}; + Configurable h3IsoMax{"h3IsoMax", 0.02, "h3IsoMax"}; + Configurable h4IsoMax{"h4IsoMax", 0.16, "h4IsoMax"}; + Configurable h5IsoMax{"h5IsoMax", 0.02, "h5IsoMax"}; + Configurable h6IsoMax{"h6IsoMax", 0.12, "h6IsoMax"}; + Configurable h7IsoMax{"h7IsoMax", 0.02, "h7IsoMax"}; + Configurable h8IsoMax{"h8IsoMax", 0.10, "h8IsoMax"}; + + // Define the three broad multiplicity ranges. + Configurable range1Min{"range1Min", 40, "minRANGE1 "}; + Configurable range1Max{"range1Max", 85, "maxRANGE1"}; + Configurable range2Min{"range2Min", 86, "minRANGE2"}; + Configurable range2Max{"range2Max", 140, "maxRANGE2"}; + Configurable range3Min{"range3Min", 141, "minRANGE3"}; + Configurable range3Max{"range3Max", 280, "maxRANGE3"}; + + // Define the seven finer multiplicity intervals. + Configurable bin4Min{"bin4Min", 40, "minBIN4"}; + Configurable bin4Max{"bin4Max", 60, "maxBIN4"}; + Configurable bin5Min{"bin5Min", 61, "minBIN5"}; + Configurable bin5Max{"bin5Max", 80, "maxBIN5"}; + Configurable bin6Min{"bin6Min", 81, "minBIN6"}; + Configurable bin6Max{"bin6Max", 100, "maxBIN6"}; + Configurable bin7Min{"bin7Min", 101, "minBIN7"}; + Configurable bin7Max{"bin7Max", 120, "maxBIN7"}; + Configurable bin8Min{"bin8Min", 121, "minBIN8"}; + Configurable bin8Max{"bin8Max", 140, "maxBIN8"}; + Configurable bin9Min{"bin9Min", 141, "minBIN9"}; + Configurable bin9Max{"bin9Max", 160, "maxBIN9"}; + Configurable bin10Min{"bin10Min", 161, "minBIN10"}; + Configurable bin10Max{"bin10Max", 280, "maxBIN10"}; + + Configurable noOfMultipBins{"noOfMultipBins", 10, "noOfMultipBins"}; + + // Provide reference multiplicity weights for optional sampling. + std::array limMultip{ + 7058, 43307, 85345, 110727, 118857, 118652, 115912, 112011, 109254, 106044, + 102421, 100599, 97952, 95908, 93883, 91890, 90264, 88097, 86388, 84582, + 82542, 81212, 79240, 77903, 75562, 74099, 71973, 70777, 69100, 67880, + 66162, 64827, 63513, 62101, 61110, 59307, 58419, 57719, 56344, 55228, + 54615, 53847, 52387, 51925, 51560, 50217, 49962, 48766, 48005, 47651, + 46758, 46046, 45531, 45063, 44271, 43530, 43151, 42489, 42347, 41576, + 40991, 40166, 39959, 39494, 39367, 38830, 38225, 37781, 37435, 36934, + 36501, 35980, 35846, 35312, 34926, 34640, 33693, 33721, 33296, 32570, + 32272, 32122, 31690, 31390, 31125, 30532, 29855, 29623, 29486, 29032, + 28534, 28140, 27491, 27430, 26760, 26601, 26217, 25625, 25464, 25067, + 24395, 24271, 23722, 23240, 22953, 22474, 22166, 21621, 21312, 20935, + 20444, 20157, 19714, 19077, 18774, 18284, 18005, 17391, 16963, 16864, + 16184, 15722, 15456, 14956, 14386, 14248, 13853, 13419, 13132, 12602, + 12192, 11667, 11397, 10967, 10701, 10316, 9831, 9758, 9371, 8905, + 8759, 8255, 7992, 7603, 7363, 7150, 6816, 6477, 6168, 5940, + 5638, 5502, 5154, 5014, 4686, 4434, 4263, 3998, 3959, 3623, + 3479, 3323, 3196, 3005, 2923, 2770, 2607, 2354, 2226, 2187, + 2058, 1980, 1794, 1739, 1601, 1515, 1419, 1321, 1292, 1175, + 1144, 1048, 995, 909, 860, 783, 783, 745, 680, 571, + 566, 524, 533, 462, 444, 427, 367, 320, 303, 317, + 270, 250, 227, 221, 207, 193, 188, 163, 131, 151, + 139, 104, 105, 70, 94, 83, 68, 62, 85, 70, + 46, 38, 43, 47, 39, 33, 38, 23, 28, 27, + 24, 17, 24, 17, 14, 27, 22, 14, 11, 11, + 10, 11, 7, 6, 5, 6, 6, 7, 2, 5, + 6, 4, 2, 5, 3, 2, 3, 1, 0, 0, + 2, 0, 1, 3, 2, 0, 1, 3, 1, 1, + 2, 0, 1, 1, 0, 0, 0, 1, 2, 0, + 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::array countMultip{}; + + static constexpr std::array binNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", + "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + + // Map each unique pair of Fox-Wolfram moments to its histogram. + static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; + static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; + + // Register event, track, moment, and correlation histograms. + void init(InitContext const&) + { + // Reject unsupported topology modes at startup. + if (topologyMode.value < kAllEvents || topologyMode.value > kIsoEvents) { + LOGF(fatal, "Invalid topologyMode=%d. Allowed values: 0 ALL, 1 TRI, 2 ISO", topologyMode.value); + } + + // Define axes shared by the registered histograms. + const AxisSpec axispT{100, 0.15, 10, "p_{T}"}; + const AxisSpec axispTL{nBins, 2, 3, "p_{T}^{Lead}"}; + const AxisSpec axispTA{nBins, 0.15, 2, "p_{T}^{Assoc}"}; + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisPhi{100, 0, constants::math::TwoPI, "#phi"}; + const AxisSpec axisEta{40, -0.8, 0.8, "#eta"}; + const AxisSpec axisMultip{350, 0, 350, "N_{ch}"}; + const AxisSpec axisHlrec{nBins / 2, 0, 1, "H_{l}^{rec}"}; + const AxisSpec axisHlfine{nBins, 0, 1, "H_{l}^{rec}"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "", "EventAxis"}; + const AxisSpec axisRecoil{nBins, 0., 1, "Recoil"}; + + // Label same-event pair counts by multiplicity interval. + histos.add("SamePairs/SamePairsNoLA", "SamePairsNoLA", kTH1D, {axisEvent}, false); + auto hstatLA = histos.get(HIST("SamePairs/SamePairsNoLA")); + auto* hLA = hstatLA->GetXaxis(); + hLA->SetBinLabel(1, "sPairsLA_RANGE1"); + hLA->SetBinLabel(2, "sPairsLA_RANGE2"); + hLA->SetBinLabel(3, "sPairsLA_RANGE3"); + hLA->SetBinLabel(4, "sPairsLA_BIN4"); + hLA->SetBinLabel(5, "sPairsLA_BIN5"); + hLA->SetBinLabel(6, "sPairsLA_BIN6"); + hLA->SetBinLabel(7, "sPairsLA_BIN7"); + hLA->SetBinLabel(8, "sPairsLA_BIN8"); + hLA->SetBinLabel(9, "sPairsLA_BIN9"); + hLA->SetBinLabel(10, "sPairsLA_BIN10"); + + // Label event counts for all multiplicity intervals. + histos.add("Events_Details/eventsNo", "No of EVENTS", kTH1D, {axisEvent}, false); + auto hstat2 = histos.get(HIST("Events_Details/eventsNo")); + auto* h2 = hstat2->GetXaxis(); + h2->SetBinLabel(1, "RANGE1"); + h2->SetBinLabel(2, "RANGE2"); + h2->SetBinLabel(3, "RANGE3"); + h2->SetBinLabel(4, "BIN4"); + h2->SetBinLabel(5, "BIN5"); + h2->SetBinLabel(6, "BIN6"); + h2->SetBinLabel(7, "BIN7"); + h2->SetBinLabel(8, "BIN8"); + h2->SetBinLabel(9, "BIN9"); + h2->SetBinLabel(10, "BIN10"); + + // Count events passing the nominal triangular selection. + histos.add("Events_Details/eventsNoTRI", "No of TRIANGLE EVENTS", kTH1D, {axisEvent}, false); + auto hstat3 = histos.get(HIST("Events_Details/eventsNoTRI")); + auto* h3 = hstat3->GetXaxis(); + h3->SetBinLabel(1, "RANGE1"); + h3->SetBinLabel(2, "RANGE2"); + h3->SetBinLabel(3, "RANGE3"); + h3->SetBinLabel(4, "BIN4"); + h3->SetBinLabel(5, "BIN5"); + h3->SetBinLabel(6, "BIN6"); + h3->SetBinLabel(7, "BIN7"); + h3->SetBinLabel(8, "BIN8"); + h3->SetBinLabel(9, "BIN9"); + h3->SetBinLabel(10, "BIN10"); + + // Count events passing the first alternate triangular selection. + histos.add("Events_Details/eventsNoTRI1", "No of TRI events selected by SET1", kTH1D, {axisEvent}, false); + auto hstatTRI1 = histos.get(HIST("Events_Details/eventsNoTRI1")); + auto* hTRI1 = hstatTRI1->GetXaxis(); + hTRI1->SetBinLabel(1, "RANGE1"); + hTRI1->SetBinLabel(2, "RANGE2"); + hTRI1->SetBinLabel(3, "RANGE3"); + hTRI1->SetBinLabel(4, "BIN4"); + hTRI1->SetBinLabel(5, "BIN5"); + hTRI1->SetBinLabel(6, "BIN6"); + hTRI1->SetBinLabel(7, "BIN7"); + hTRI1->SetBinLabel(8, "BIN8"); + hTRI1->SetBinLabel(9, "BIN9"); + hTRI1->SetBinLabel(10, "BIN10"); + + // Count events passing the second alternate triangular selection. + histos.add("Events_Details/eventsNoTRI2", "No of TRI events selected by SET2", kTH1D, {axisEvent}, false); + auto hstatTRI2 = histos.get(HIST("Events_Details/eventsNoTRI2")); + auto* hTRI2 = hstatTRI2->GetXaxis(); + hTRI2->SetBinLabel(1, "RANGE1"); + hTRI2->SetBinLabel(2, "RANGE2"); + hTRI2->SetBinLabel(3, "RANGE3"); + hTRI2->SetBinLabel(4, "BIN4"); + hTRI2->SetBinLabel(5, "BIN5"); + hTRI2->SetBinLabel(6, "BIN6"); + hTRI2->SetBinLabel(7, "BIN7"); + hTRI2->SetBinLabel(8, "BIN8"); + hTRI2->SetBinLabel(9, "BIN9"); + hTRI2->SetBinLabel(10, "BIN10"); + + // Count events passing the third alternate triangular selection. + histos.add("Events_Details/eventsNoTRI3", "No of TRI events selected by SET3", kTH1D, {axisEvent}, false); + auto hstatTRI3 = histos.get(HIST("Events_Details/eventsNoTRI3")); + auto* hTRI3 = hstatTRI3->GetXaxis(); + hTRI3->SetBinLabel(1, "RANGE1"); + hTRI3->SetBinLabel(2, "RANGE2"); + hTRI3->SetBinLabel(3, "RANGE3"); + hTRI3->SetBinLabel(4, "BIN4"); + hTRI3->SetBinLabel(5, "BIN5"); + hTRI3->SetBinLabel(6, "BIN6"); + hTRI3->SetBinLabel(7, "BIN7"); + hTRI3->SetBinLabel(8, "BIN8"); + hTRI3->SetBinLabel(9, "BIN9"); + hTRI3->SetBinLabel(10, "BIN10"); + + // Count events passing the tighter auxiliary triangular selection. + histos.add("Events_Details/eventsNoTRIAux", "No of TRI events selected by auxiliary SET", kTH1D, {axisEvent}, false); + auto hstatTRIAux = histos.get(HIST("Events_Details/eventsNoTRIAux")); + auto* hTRIAux = hstatTRIAux->GetXaxis(); + hTRIAux->SetBinLabel(1, "RANGE1"); + hTRIAux->SetBinLabel(2, "RANGE2"); + hTRIAux->SetBinLabel(3, "RANGE3"); + hTRIAux->SetBinLabel(4, "BIN4"); + hTRIAux->SetBinLabel(5, "BIN5"); + hTRIAux->SetBinLabel(6, "BIN6"); + hTRIAux->SetBinLabel(7, "BIN7"); + hTRIAux->SetBinLabel(8, "BIN8"); + hTRIAux->SetBinLabel(9, "BIN9"); + hTRIAux->SetBinLabel(10, "BIN10"); + + // Count events passing the isotropic selection. + histos.add("Events_Details/eventsNoISO", "No of ISO EVENTS", kTH1D, {axisEvent}, false); + auto hstat4 = histos.get(HIST("Events_Details/eventsNoISO")); + auto* h4 = hstat4->GetXaxis(); + h4->SetBinLabel(1, "RANGE1"); + h4->SetBinLabel(2, "RANGE2"); + h4->SetBinLabel(3, "RANGE3"); + h4->SetBinLabel(4, "BIN4"); + h4->SetBinLabel(5, "BIN5"); + h4->SetBinLabel(6, "BIN6"); + h4->SetBinLabel(7, "BIN7"); + h4->SetBinLabel(8, "BIN8"); + h4->SetBinLabel(9, "BIN9"); + h4->SetBinLabel(10, "BIN10"); + + // Register inclusive multiplicity, track spectra, and recoil histograms. + histos.add("Multip/ITSTPC_Multiplicity", "Reconstructed ITS-TPC Multiplicity; trks; Events", kTH1D, {axisMultip}); + + histos.add("pTSpectra/pT", "pT", kTH1D, {axispT}); + histos.add("Spectra/phi", "phi", kTH1D, {axisPhi}); + + histos.add("pTSpectra/RecoilALL", "RecoilALL", kTH2D, {axisMultip, axisRecoil}); + histos.add("pTSpectra/RecoilTRI", "RecoilTRI", kTH2D, {axisMultip, axisRecoil}); + + // Register track spectra for each multiplicity interval. + for (int i = 1; i <= noOfMultipBins; i++) { + histos.add(fmt::format("Multip/GlobalTrk_BIN{}", i).c_str(), fmt::format("GlobalTrk_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/GlobalTrkTri_BIN{}", i).c_str(), fmt::format("GlobalTrkTri_BIN{}", i).c_str(), kTH1D, {axisMultip}); + + histos.add(fmt::format("pTSpectra/pT_BIN{}", i).c_str(), fmt::format("pT_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("pTSpectra/pTTri_BIN{}", i).c_str(), fmt::format("pTTri_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("Spectra/phi_BIN{}", i).c_str(), fmt::format("phi_BIN{}", i).c_str(), kTH1D, {axisPhi}); + histos.add(fmt::format("Spectra/phiTri_BIN{}", i).c_str(), fmt::format("phiTri_BIN{}", i).c_str(), kTH1D, {axisPhi}); + + histos.add(fmt::format("Spectra/etaphi_BIN{}", i).c_str(), fmt::format("etaphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/etaphiTri_BIN{}", i).c_str(), fmt::format("etaphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + + histos.add(fmt::format("Spectra/ptphi_BIN{}", i).c_str(), fmt::format("ptphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + histos.add(fmt::format("Spectra/ptphiTri_BIN{}", i).c_str(), fmt::format("ptphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + } + + // Register leading and associated track spectra in the broad ranges. + for (int i = 1; i <= 3; i++) { + histos.add(fmt::format("pTSpectra/pT_LA_BIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); + histos.add(fmt::format("pTSpectra/pT_LA_TriBIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); + + histos.add(fmt::format("pTSpectra/fpT_LA_BIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); + histos.add(fmt::format("pTSpectra/fpT_LA_TriBIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); + + histos.add(fmt::format("pTSpectra/peakPT_BIN{}", i).c_str(), fmt::format("peakPT_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("pTSpectra/valleyPT_BIN{}", i).c_str(), fmt::format("valleyPT_BIN{}", i).c_str(), kTH1D, {axispT}); + } + + // Register same-event angular correlations. + for (int i = 1; i <= noOfMultipBins; ++i) { + histos.add(fmt::format("Corelations/sTRI_BIN{}", i).c_str(), fmt::format("sTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/sProjTRI_BIN{}", i).c_str(), fmt::format("sProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + + // Register moment distributions in the broad ranges. + const std::array bins = {1, 2, 3}; + for (int j = 1; j <= 8; j++) { + for (const auto& i : bins) { + histos.add(fmt::format("FWM_BIN{}/H{}", i, j).c_str(), fmt::format("H_{{{}}}^{{rec}}", j).c_str(), kTH1F, {axisHlrec}); + histos.add(fmt::format("FWM_BIN{}/Multip_H{}", i, j).c_str(), fmt::format("H_{{{}}} vs Multip", j).c_str(), kTH2D, {axisMultip, axisHlfine}); + } + } + + // Register optional pairwise moment correlations. + if (doFWMCorrelations) { + + for (size_t k = 0; k < fwmCorrelationNames.size(); ++k) { + + const int first = fwmCorrelationFirst[k] + 1; + const int second = fwmCorrelationSecond[k] + 1; + + histos.add(fmt::format("FWM_BIN1/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN2/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN3/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + } + } + } + + // Apply reconstructed collision quality requirements. + template + bool isEventSelected(const TCollision& RecCollision) + { + if (isVtxRange && std::abs(RecCollision.posZ()) >= vtxRange) { + return false; + } + + if (isSel8 && !RecCollision.sel8()) { + return false; + } + + if (isNoSameBunchPileup && !RecCollision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (isGoodZvtxFT0vsPV && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + + if (isVertexITSTPC && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + + if (isVertexTOFmatched && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + + if (kIsGoodITSLayer0123 && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + + if (isNoCollInTimeRangeNarrow && !RecCollision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + + return true; + } + + // Compute normalized Fox-Wolfram moments through order eight. + template + std::array computeFWM(T const& tracks) + { + std::array hSN{}; + std::array hS{}; + + // Accumulate transverse-momentum-weighted contributions from track pairs. + for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + double c = std::cos(t1.phi() - t2.phi()); + double w = t1.pt() * t2.pt(); + double p0 = 1.0, p1 = c; + + hSN[0] += w; + hSN[1] += w * p1; + + // Evaluate higher Legendre orders with the recurrence relation. + for (int l = 2; l < 9; ++l) { + + double p = ((2 * l - 1) * c * p1 - (l - 1) * p0) / l; + + hSN[l] += w * p; + p0 = p1; + p1 = p; + } + } + + // Normalize each moment by the zeroth moment. + for (int l = 0; l < 9; ++l) { + hS[l] = hSN[l] / hSN[0]; + } + + return hS; + } + + // Calculate normalized transverse recoil. + template + double computeRecoil(const Tracks& tracks) + { + double sumPx = 0.0; + double sumPy = 0.0; + double sumPt = 0.0; + + // Sum track momenta to form the transverse recoil. + for (const auto& track : tracks) { + sumPx += track.px(); + sumPy += track.py(); + sumPt += track.pt(); + } + + if (sumPt == 0.0) { + return 0.0; + } + + return std::sqrt(sumPx * sumPx + sumPy * sumPy) / sumPt; + } + + // Fill inclusive and topology-selected observables in one multiplicity interval. + template + void fillHistoMult(int binIndex, int minMult, int maxMult, + bool isSelectedTopology, bool isTRI, bool isTRI1, + bool isTRI2, bool isTRI3, bool isTRIAux, bool isISO, + const T& tracks) + { + if (tracks.size() < minMult || tracks.size() > maxMult) { + return; + } + + // Count all events and each topology selection in this interval. + histos.fill(HIST("Events_Details/eventsNo"), binIndex); + + if (isISO) { + histos.fill(HIST("Events_Details/eventsNoISO"), binIndex); + } + + if (isTRI) { + histos.fill(HIST("Events_Details/eventsNoTRI"), binIndex); + } + + if (isTRI1) { + histos.fill(HIST("Events_Details/eventsNoTRI1"), binIndex); + } + if (isTRI2) { + histos.fill(HIST("Events_Details/eventsNoTRI2"), binIndex); + } + if (isTRI3) { + histos.fill(HIST("Events_Details/eventsNoTRI3"), binIndex); + } + if (isTRIAux) { + histos.fill(HIST("Events_Details/eventsNoTRIAux"), binIndex); + } + + // Fill inclusive spectra and selected-topology spectra for this interval. + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex == idx + 1) { + + histos.fill(HIST("Multip/GlobalTrk_") + HIST(binNames[idx]), tracks.size()); + + for (const auto& track : tracks) { + + histos.fill(HIST("pTSpectra/pT_") + HIST(binNames[idx]), track.pt()); + histos.fill(HIST("Spectra/phi_") + HIST(binNames[idx]), track.phi()); + + histos.fill(HIST("Spectra/etaphi_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphi_") + HIST(binNames[idx]), track.phi(), track.pt()); + } + + // Fill spectra for the configured topology selection. + if (isSelectedTopology) { + + histos.fill(HIST("Multip/GlobalTrkTri_") + HIST(binNames[idx]), tracks.size()); + + for (const auto& track : tracks) { + histos.fill(HIST("pTSpectra/pTTri_") + HIST(binNames[idx]), track.pt()); + histos.fill(HIST("Spectra/phiTri_") + HIST(binNames[idx]), track.phi()); + + histos.fill(HIST("Spectra/etaphiTri_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiTri_") + HIST(binNames[idx]), track.phi(), track.pt()); + } + } + } + }); + } + + // Fill same-event pair distributions for one multiplicity interval. + template + void fillSameEventHist(int binIndex, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool onPeaks, + bool onValleys, + bool isLeadAssocPair) + { + + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + if (!isLeadAssocPair) { + return; + } + + // Record peak and valley track spectra in the broad ranges. + if constexpr (idx < 3) { + if (leadAssocMode.value == 3) { + if (onPeaks) { + histos.fill(HIST("pTSpectra/peakPT_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (onValleys) { + histos.fill(HIST("pTSpectra/valleyPT_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + } + + // Count accepted pairs and fill their angular distributions. + histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); + histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); + }); + } + + std::array, 3> triTuples; + + Filter ITSTPCTracks = requireGlobalTrackInFilter(); + using GlobalTracks = soa::Filtered; + + // Process reconstructed events and buffer selected tracks for mixing. + void process(CollisionRecTable const& collision, + GlobalTracks const& tracks) + { + + // Stop before analysis when the collision fails quality cuts. + if (!isEventSelected(collision)) { + return; + } + + if (tracks.size() == 0) { + return; + } + + // Determine track multiplicity, recoil, and leading tracks. + int nGlobalTracks = tracks.size(); + auto nRecoil = computeRecoil(tracks); + + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks); + + // Assign the event to broad and fine multiplicity intervals. + bool multRANGE1 = (nGlobalTracks >= range1Min && nGlobalTracks <= range1Max); + bool multRANGE2 = (nGlobalTracks >= range2Min && nGlobalTracks <= range2Max); + bool multRANGE3 = (nGlobalTracks >= range3Min && nGlobalTracks <= range3Max); + bool multBIN4 = (nGlobalTracks >= bin4Min && nGlobalTracks <= bin4Max); + bool multBIN5 = (nGlobalTracks >= bin5Min && nGlobalTracks <= bin5Max); + bool multBIN6 = (nGlobalTracks >= bin6Min && nGlobalTracks <= bin6Max); + bool multBIN7 = (nGlobalTracks >= bin7Min && nGlobalTracks <= bin7Max); + bool multBIN8 = (nGlobalTracks >= bin8Min && nGlobalTracks <= bin8Max); + bool multBIN9 = (nGlobalTracks >= bin9Min && nGlobalTracks <= bin9Max); + bool multBIN10 = (nGlobalTracks >= bin10Min && nGlobalTracks <= bin10Max); + + // Compute event moments and evaluate topology selections. + auto fwH = computeFWM(tracks); + auto [H0, H1, H2, H3, H4, H5, H6, H7, H8] = fwH; + + // Use tighter moment limits for the auxiliary triangular sample. + const bool isTRIAux = (H1 < 0.0475) && (H2 < 0.2850) && (H3 > 0.0420) && (H4 < 0.1805) && (H5 > 0.0210) && (H6 < 0.1425) && (H7 > 0.0168) && (H8 < 0.1140); + + // Apply the nominal triangular moment limits. + const bool isTRI = (H1 < 0.05) && (H2 < 0.30) && (H3 > 0.040) && (H4 < 0.190) && (H5 > 0.020) && (H6 < 0.150) && (H7 > 0.016) && (H8 < 0.120); + + // Apply three progressively looser triangular selections. + const bool isTRI1 = (H1 < 0.0525) && (H2 < 0.315) && (H3 > 0.0380) && (H4 < 0.1995) && (H5 > 0.0190) && (H6 < 0.1575) && (H7 > 0.0152) && (H8 < 0.1260); + + const bool isTRI2 = (H1 < 0.0550) && (H2 < 0.330) && (H3 > 0.0360) && (H4 < 0.2090) && (H5 > 0.0180) && (H6 < 0.1650) && (H7 > 0.0144) && (H8 < 0.1320); + + const bool isTRI3 = (H1 < 0.0575) && (H2 < 0.345) && (H3 > 0.0340) && (H4 < 0.2185) && (H5 > 0.0170) && (H6 < 0.1725) && (H7 > 0.0136) && (H8 < 0.1380); + + // Apply the configurable isotropic moment limits. + const bool isISO = (H1 < h1IsoMax) && (H2 < h2IsoMax) && (H3 < h3IsoMax) && (H4 < h4IsoMax) && (H5 < h5IsoMax) && (H6 < h6IsoMax) && (H7 < h7IsoMax) && (H8 < h8IsoMax); + + // Select all, triangular, or isotropic events for correlations. + const bool isSelectedTopology = + (topologyMode.value == kAllEvents) || + (topologyMode.value == kTriEvents && isTRI) || + (topologyMode.value == kIsoEvents && isISO); + + // Optionally cap the sampled events at each multiplicity. + int bin = std::clamp(nGlobalTracks, 0, 499); + if (doMultipSampling) { + if (countMultip[bin] >= limMultip[bin]) { + return; + } + + ++countMultip[bin]; + } + + // Fill inclusive event multiplicity and recoil. + histos.fill(HIST("Multip/ITSTPC_Multiplicity"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilALL"), nGlobalTracks, nRecoil); + + if (isSelectedTopology) { + histos.fill(HIST("pTSpectra/RecoilTRI"), nGlobalTracks, nRecoil); + } + + // Fill moments and optional moment correlations in the first broad range. + if (multRANGE1) { + + histos.fill(HIST("FWM_BIN1/H1"), H1); + histos.fill(HIST("FWM_BIN1/H2"), H2); + histos.fill(HIST("FWM_BIN1/H3"), H3); + histos.fill(HIST("FWM_BIN1/H4"), H4); + histos.fill(HIST("FWM_BIN1/H5"), H5); + histos.fill(HIST("FWM_BIN1/H6"), H6); + histos.fill(HIST("FWM_BIN1/H7"), H7); + histos.fill(HIST("FWM_BIN1/H8"), H8); + histos.fill(HIST("FWM_BIN1/Multip_H1"), nGlobalTracks, H1); + histos.fill(HIST("FWM_BIN1/Multip_H2"), nGlobalTracks, H2); + histos.fill(HIST("FWM_BIN1/Multip_H3"), nGlobalTracks, H3); + histos.fill(HIST("FWM_BIN1/Multip_H4"), nGlobalTracks, H4); + histos.fill(HIST("FWM_BIN1/Multip_H5"), nGlobalTracks, H5); + histos.fill(HIST("FWM_BIN1/Multip_H6"), nGlobalTracks, H6); + histos.fill(HIST("FWM_BIN1/Multip_H7"), nGlobalTracks, H7); + histos.fill(HIST("FWM_BIN1/Multip_H8"), nGlobalTracks, H8); + if (doFWMCorrelations) { + histos.fill(HIST("FWM_BIN1/H1_H2"), H1, H2); + histos.fill(HIST("FWM_BIN1/H1_H3"), H1, H3); + histos.fill(HIST("FWM_BIN1/H1_H4"), H1, H4); + histos.fill(HIST("FWM_BIN1/H1_H5"), H1, H5); + histos.fill(HIST("FWM_BIN1/H1_H6"), H1, H6); + histos.fill(HIST("FWM_BIN1/H1_H7"), H1, H7); + histos.fill(HIST("FWM_BIN1/H1_H8"), H1, H8); + histos.fill(HIST("FWM_BIN1/H2_H3"), H2, H3); + histos.fill(HIST("FWM_BIN1/H2_H4"), H2, H4); + histos.fill(HIST("FWM_BIN1/H2_H5"), H2, H5); + histos.fill(HIST("FWM_BIN1/H2_H6"), H2, H6); + histos.fill(HIST("FWM_BIN1/H2_H7"), H2, H7); + histos.fill(HIST("FWM_BIN1/H2_H8"), H2, H8); + histos.fill(HIST("FWM_BIN1/H3_H4"), H3, H4); + histos.fill(HIST("FWM_BIN1/H3_H5"), H3, H5); + histos.fill(HIST("FWM_BIN1/H3_H6"), H3, H6); + histos.fill(HIST("FWM_BIN1/H3_H7"), H3, H7); + histos.fill(HIST("FWM_BIN1/H3_H8"), H3, H8); + histos.fill(HIST("FWM_BIN1/H4_H5"), H4, H5); + histos.fill(HIST("FWM_BIN1/H4_H6"), H4, H6); + histos.fill(HIST("FWM_BIN1/H4_H7"), H4, H7); + histos.fill(HIST("FWM_BIN1/H4_H8"), H4, H8); + histos.fill(HIST("FWM_BIN1/H5_H6"), H5, H6); + histos.fill(HIST("FWM_BIN1/H5_H7"), H5, H7); + histos.fill(HIST("FWM_BIN1/H5_H8"), H5, H8); + histos.fill(HIST("FWM_BIN1/H6_H7"), H6, H7); + histos.fill(HIST("FWM_BIN1/H6_H8"), H6, H8); + histos.fill(HIST("FWM_BIN1/H7_H8"), H7, H8); + } + } + + // Fill moments and optional moment correlations in the second broad range. + if (multRANGE2) { + + histos.fill(HIST("FWM_BIN2/H1"), H1); + histos.fill(HIST("FWM_BIN2/H2"), H2); + histos.fill(HIST("FWM_BIN2/H3"), H3); + histos.fill(HIST("FWM_BIN2/H4"), H4); + histos.fill(HIST("FWM_BIN2/H5"), H5); + histos.fill(HIST("FWM_BIN2/H6"), H6); + histos.fill(HIST("FWM_BIN2/H7"), H7); + histos.fill(HIST("FWM_BIN2/H8"), H8); + histos.fill(HIST("FWM_BIN2/Multip_H1"), nGlobalTracks, H1); + histos.fill(HIST("FWM_BIN2/Multip_H2"), nGlobalTracks, H2); + histos.fill(HIST("FWM_BIN2/Multip_H3"), nGlobalTracks, H3); + histos.fill(HIST("FWM_BIN2/Multip_H4"), nGlobalTracks, H4); + histos.fill(HIST("FWM_BIN2/Multip_H5"), nGlobalTracks, H5); + histos.fill(HIST("FWM_BIN2/Multip_H6"), nGlobalTracks, H6); + histos.fill(HIST("FWM_BIN2/Multip_H7"), nGlobalTracks, H7); + histos.fill(HIST("FWM_BIN2/Multip_H8"), nGlobalTracks, H8); + if (doFWMCorrelations) { + histos.fill(HIST("FWM_BIN2/H1_H2"), H1, H2); + histos.fill(HIST("FWM_BIN2/H1_H3"), H1, H3); + histos.fill(HIST("FWM_BIN2/H1_H4"), H1, H4); + histos.fill(HIST("FWM_BIN2/H1_H5"), H1, H5); + histos.fill(HIST("FWM_BIN2/H1_H6"), H1, H6); + histos.fill(HIST("FWM_BIN2/H1_H7"), H1, H7); + histos.fill(HIST("FWM_BIN2/H1_H8"), H1, H8); + histos.fill(HIST("FWM_BIN2/H2_H3"), H2, H3); + histos.fill(HIST("FWM_BIN2/H2_H4"), H2, H4); + histos.fill(HIST("FWM_BIN2/H2_H5"), H2, H5); + histos.fill(HIST("FWM_BIN2/H2_H6"), H2, H6); + histos.fill(HIST("FWM_BIN2/H2_H7"), H2, H7); + histos.fill(HIST("FWM_BIN2/H2_H8"), H2, H8); + histos.fill(HIST("FWM_BIN2/H3_H4"), H3, H4); + histos.fill(HIST("FWM_BIN2/H3_H5"), H3, H5); + histos.fill(HIST("FWM_BIN2/H3_H6"), H3, H6); + histos.fill(HIST("FWM_BIN2/H3_H7"), H3, H7); + histos.fill(HIST("FWM_BIN2/H3_H8"), H3, H8); + histos.fill(HIST("FWM_BIN2/H4_H5"), H4, H5); + histos.fill(HIST("FWM_BIN2/H4_H6"), H4, H6); + histos.fill(HIST("FWM_BIN2/H4_H7"), H4, H7); + histos.fill(HIST("FWM_BIN2/H4_H8"), H4, H8); + histos.fill(HIST("FWM_BIN2/H5_H6"), H5, H6); + histos.fill(HIST("FWM_BIN2/H5_H7"), H5, H7); + histos.fill(HIST("FWM_BIN2/H5_H8"), H5, H8); + histos.fill(HIST("FWM_BIN2/H6_H7"), H6, H7); + histos.fill(HIST("FWM_BIN2/H6_H8"), H6, H8); + histos.fill(HIST("FWM_BIN2/H7_H8"), H7, H8); + } + } + + // Fill moments and optional moment correlations in the third broad range. + if (multRANGE3) { + + histos.fill(HIST("FWM_BIN3/H1"), H1); + histos.fill(HIST("FWM_BIN3/H2"), H2); + histos.fill(HIST("FWM_BIN3/H3"), H3); + histos.fill(HIST("FWM_BIN3/H4"), H4); + histos.fill(HIST("FWM_BIN3/H5"), H5); + histos.fill(HIST("FWM_BIN3/H6"), H6); + histos.fill(HIST("FWM_BIN3/H7"), H7); + histos.fill(HIST("FWM_BIN3/H8"), H8); + histos.fill(HIST("FWM_BIN3/Multip_H1"), nGlobalTracks, H1); + histos.fill(HIST("FWM_BIN3/Multip_H2"), nGlobalTracks, H2); + histos.fill(HIST("FWM_BIN3/Multip_H3"), nGlobalTracks, H3); + histos.fill(HIST("FWM_BIN3/Multip_H4"), nGlobalTracks, H4); + histos.fill(HIST("FWM_BIN3/Multip_H5"), nGlobalTracks, H5); + histos.fill(HIST("FWM_BIN3/Multip_H6"), nGlobalTracks, H6); + histos.fill(HIST("FWM_BIN3/Multip_H7"), nGlobalTracks, H7); + histos.fill(HIST("FWM_BIN3/Multip_H8"), nGlobalTracks, H8); + if (doFWMCorrelations) { + histos.fill(HIST("FWM_BIN3/H1_H2"), H1, H2); + histos.fill(HIST("FWM_BIN3/H1_H3"), H1, H3); + histos.fill(HIST("FWM_BIN3/H1_H4"), H1, H4); + histos.fill(HIST("FWM_BIN3/H1_H5"), H1, H5); + histos.fill(HIST("FWM_BIN3/H1_H6"), H1, H6); + histos.fill(HIST("FWM_BIN3/H1_H7"), H1, H7); + histos.fill(HIST("FWM_BIN3/H1_H8"), H1, H8); + histos.fill(HIST("FWM_BIN3/H2_H3"), H2, H3); + histos.fill(HIST("FWM_BIN3/H2_H4"), H2, H4); + histos.fill(HIST("FWM_BIN3/H2_H5"), H2, H5); + histos.fill(HIST("FWM_BIN3/H2_H6"), H2, H6); + histos.fill(HIST("FWM_BIN3/H2_H7"), H2, H7); + histos.fill(HIST("FWM_BIN3/H2_H8"), H2, H8); + histos.fill(HIST("FWM_BIN3/H3_H4"), H3, H4); + histos.fill(HIST("FWM_BIN3/H3_H5"), H3, H5); + histos.fill(HIST("FWM_BIN3/H3_H6"), H3, H6); + histos.fill(HIST("FWM_BIN3/H3_H7"), H3, H7); + histos.fill(HIST("FWM_BIN3/H3_H8"), H3, H8); + histos.fill(HIST("FWM_BIN3/H4_H5"), H4, H5); + histos.fill(HIST("FWM_BIN3/H4_H6"), H4, H6); + histos.fill(HIST("FWM_BIN3/H4_H7"), H4, H7); + histos.fill(HIST("FWM_BIN3/H4_H8"), H4, H8); + histos.fill(HIST("FWM_BIN3/H5_H6"), H5, H6); + histos.fill(HIST("FWM_BIN3/H5_H7"), H5, H7); + histos.fill(HIST("FWM_BIN3/H5_H8"), H5, H8); + histos.fill(HIST("FWM_BIN3/H6_H7"), H6, H7); + histos.fill(HIST("FWM_BIN3/H6_H8"), H6, H8); + histos.fill(HIST("FWM_BIN3/H7_H8"), H7, H8); + } + } + + // Fill inclusive and topology-selected leading-track spectra. + for (const auto& track : tracks) { + + histos.fill(HIST("pTSpectra/pT"), track.pt()); + histos.fill(HIST("Spectra/phi"), track.phi()); + + if (track.globalIndex() == triggTrk.globalIndex()) { + continue; + } + + if (multRANGE1) { + histos.fill(HIST("pTSpectra/pT_LA_BIN1"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN1"), triggTrk.pt(), track.pt()); + } + if (multRANGE2) { + histos.fill(HIST("pTSpectra/pT_LA_BIN2"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN2"), triggTrk.pt(), track.pt()); + } + if (multRANGE3) { + histos.fill(HIST("pTSpectra/pT_LA_BIN3"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN3"), triggTrk.pt(), track.pt()); + } + + if (!isSelectedTopology) { + continue; + } + + if (multRANGE1) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN1"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN1"), triggTrk.pt(), track.pt()); + } + if (multRANGE2) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN2"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN2"), triggTrk.pt(), track.pt()); + } + if (multRANGE3) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN3"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN3"), triggTrk.pt(), track.pt()); + } + } + + // Build same-event angular correlations for the selected topology. + if (doSameEvent && isSelectedTopology) { + + // Form distinct track pairs within the selected event. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + + // Calculate pair separations and classify azimuthal windows. + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + const bool onPeaks = (deltaPhi > -0.4 && deltaPhi < 0.4) || + (deltaPhi > 1.6 && deltaPhi < 2.4) || + (deltaPhi > 3.6 && deltaPhi < 4.4); + + const bool onValleys = (deltaPhi > -2.6 && deltaPhi < -1.4) || + (deltaPhi > 0.6 && deltaPhi < 1.4) || + (deltaPhi > 2.6 && deltaPhi < 3.4); + + // Apply the configured pair selection to each interval. + const bool isLeadAssocPair = passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart); + + if (multRANGE1) { + fillSameEventHist(1, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + + if (multRANGE2) { + fillSameEventHist(2, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + + if (multRANGE3) { + fillSameEventHist(3, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + + if (multBIN4) { + fillSameEventHist(4, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN5) { + fillSameEventHist(5, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN6) { + fillSameEventHist(6, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN7) { + fillSameEventHist(7, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN8) { + fillSameEventHist(8, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN9) { + fillSameEventHist(9, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + if (multBIN10) { + fillSameEventHist(10, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + } + } + } + + // Map the event to a broad range for mixed-event buffering. + int range = -1; + + if (multRANGE1) { + range = 0; + } else if (multRANGE2) { + range = 1; + } else if (multRANGE3) { + range = 2; + } + + // Buffer up to the requested number of selected events per range. + if (isSelectedTopology && range >= 0 && triTuples[range].size() < static_cast(nSelEv)) { + + std::vector tracksTRI; + tracksTRI.reserve(nGlobalTracks); + + for (const auto& track : tracks) { + tracksTRI.emplace_back(track.pt(), track.eta(), track.phi()); + } + + triTuples[range].emplace_back(collision.posZ(), nGlobalTracks, std::move(tracksTRI)); + } + + // Fill event counts and spectra for all matching multiplicity intervals. + fillHistoMult(1, range1Min, range1Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(2, range2Min, range2Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(3, range3Min, range3Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(4, bin4Min, bin4Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(5, bin5Min, bin5Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(6, bin6Min, bin6Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(7, bin7Min, bin7Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(8, bin8Min, bin8Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(9, bin9Min, bin9Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + fillHistoMult(10, bin10Min, bin10Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + } + + int countDF = 0; + + // Write buffered events to the tables consumed by the mixing task. + void processFILL(aod::Collisions const& /*collisions*/, aod::Tracks const& /*tracks*/) + { + // Track data frames and report buffered event counts. + countDF++; + + // LOGF(info, "Input data - ALL Collisions %d, Tracks %d -> DF no %d", collisions.size(), tracks.size(), countDF); + + for (int range = 0; range < 3; ++range) { + + auto& events = triTuples[range]; + // LOGF(info, "Selected events buffered in RANGE%d: %zu/%d", range + 1, events.size(), static_cast(nSelEv)); + + // Wait until a range reaches its event quota. + if (events.size() < static_cast(nSelEv)) { + continue; + } + + // LOGF(info, "-> FILL TABLES FOR RANGE%d <-", range + 1); + + // Write each buffered collision and its tracks to the output tables. + for (const auto& col : events) { + + float posZ = std::get<0>(col); + int multip = std::get<1>(col); + triCollisions(posZ, multip); + + for (const auto& track : std::get<2>(col)) { + triTracks(triCollisions.lastIndex(), std::get<0>(track), std::get<1>(track), std::get<2>(track)); + } + } + + // Release events after their tables are filled. + events.clear(); + } + } + + PROCESS_SWITCH(FoxWolframCorrelation, processFILL, "Fill the triTables", true); +}; + +// Build mixed-event backgrounds from the selected event tables. +struct FoxWolframCorrelationMixing { + + SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6; + SliceCache cache_bin7, cache_bin8, cache_bin9, cache_bin10; + Preslice triCol = aod::track_tri::triangleCollisionId; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + static constexpr std::array mixedBinNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", + "BIN7", "BIN8", "BIN9", "BIN10"}; + + Configurable nBin{"nBin", 100, "No of bis per axis"}; + ConfigurableAxis axisVtx{"axisVtx", {VARIABLE_WIDTH, -10, 10}, "vertex axis for mixed event histograms"}; + + // Use half-integer edges for inclusive integer multiplicity bins. + ConfigurableAxis axisMult1{"axisMult1", {VARIABLE_WIDTH, 39.5, 85.5}, "axisMult1"}; + ConfigurableAxis axisMult2{"axisMult2", {VARIABLE_WIDTH, 85.5, 140.5}, "axisMult2"}; + ConfigurableAxis axisMult3{"axisMult3", {VARIABLE_WIDTH, 140.5, 280.5}, "axisMult3"}; + ConfigurableAxis axisMult4{"axisMult4", {VARIABLE_WIDTH, 39.5, 60.5}, "axisMult4"}; + ConfigurableAxis axisMult5{"axisMult5", {VARIABLE_WIDTH, 60.5, 80.5}, "axisMult5"}; + ConfigurableAxis axisMult6{"axisMult6", {VARIABLE_WIDTH, 80.5, 100.5}, "axisMult6"}; + ConfigurableAxis axisMult7{"axisMult7", {VARIABLE_WIDTH, 100.5, 120.5}, "axisMult7"}; + ConfigurableAxis axisMult8{"axisMult8", {VARIABLE_WIDTH, 120.5, 140.5}, "axisMult8"}; + ConfigurableAxis axisMult9{"axisMult9", {VARIABLE_WIDTH, 140.5, 160.5}, "axisMult9"}; + ConfigurableAxis axisMult10{"axisMult10", {VARIABLE_WIDTH, 160.5, 280.5}, "axisMult10"}; + + Configurable doMixedEvent{"doMixedEvent", true, "Fill mixed-event TRI correlations"}; + Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per trigger event"}; + + // Choose the pair selection for mixed-event correlations. + Configurable leadAssocMode{"leadAssocMode", 0, + "0: ALL i-j pairs (pT<3); 1: ALL i-j pairs (pT < 3, Eta constrain) 2: pTLead in (2,3) & pTAssoc < 2; 3: pTAssoc < pTLead"}; + + // Register mixed-event pair counts and angular distributions. + void init(InitContext const&) + { + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "#Multip. Bin", "EventAxis"}; + + // Label mixed-event pair counts by multiplicity interval. + histos.add("MixingPairs/MixingPairsNoLA", "MixingPairsNoLA", kTH1D, {axisEvent}, false); + auto hstatLA = histos.get(HIST("MixingPairs/MixingPairsNoLA")); + auto* hLA = hstatLA->GetXaxis(); + hLA->SetBinLabel(1, "bPairsLA_BIN1"); + hLA->SetBinLabel(2, "bPairsLA_BIN2"); + hLA->SetBinLabel(3, "bPairsLA_BIN3"); + hLA->SetBinLabel(4, "bPairsLA_BIN4"); + hLA->SetBinLabel(5, "bPairsLA_BIN5"); + hLA->SetBinLabel(6, "bPairsLA_BIN6"); + hLA->SetBinLabel(7, "bPairsLA_BIN7"); + hLA->SetBinLabel(8, "bPairsLA_BIN8"); + hLA->SetBinLabel(9, "bPairsLA_BIN9"); + hLA->SetBinLabel(10, "bPairsLA_BIN10"); + + // Register mixed-event correlation histograms for all intervals. + for (int i = 1; i <= 10; i++) { + histos.add(fmt::format("Corelations/bTRI_BIN{}", i).c_str(), fmt::format("bTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/bProjTRI_BIN{}", i).c_str(), fmt::format("bProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + } + + // Mix events in vertex and multiplicity intervals. + using BinningType = ColumnBinningPolicy; + BinningType binningOnPosMultip1{{axisVtx, axisMult1}, true}; + BinningType binningOnPosMultip2{{axisVtx, axisMult2}, true}; + BinningType binningOnPosMultip3{{axisVtx, axisMult3}, true}; + BinningType binningOnPosMultip4{{axisVtx, axisMult4}, true}; + BinningType binningOnPosMultip5{{axisVtx, axisMult5}, true}; + BinningType binningOnPosMultip6{{axisVtx, axisMult6}, true}; + BinningType binningOnPosMultip7{{axisVtx, axisMult7}, true}; + BinningType binningOnPosMultip8{{axisVtx, axisMult8}, true}; + BinningType binningOnPosMultip9{{axisVtx, axisMult9}, true}; + BinningType binningOnPosMultip10{{axisVtx, axisMult10}, true}; + + // Create independent mixing pools for the ten multiplicity intervals. + SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cache_bin1}; + SameKindPair pairBIN2{binningOnPosMultip2, nMixedEvents, -1, &cache_bin2}; + SameKindPair pairBIN3{binningOnPosMultip3, nMixedEvents, -1, &cache_bin3}; + SameKindPair pairBIN4{binningOnPosMultip4, nMixedEvents, -1, &cache_bin4}; + SameKindPair pairBIN5{binningOnPosMultip5, nMixedEvents, -1, &cache_bin5}; + SameKindPair pairBIN6{binningOnPosMultip6, nMixedEvents, -1, &cache_bin6}; + SameKindPair pairBIN7{binningOnPosMultip7, nMixedEvents, -1, &cache_bin7}; + SameKindPair pairBIN8{binningOnPosMultip8, nMixedEvents, -1, &cache_bin8}; + SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cache_bin9}; + SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cache_bin10}; + + // Fill mixed-event distributions for a selected multiplicity interval. + template + void fillMixedEventBin(PairType& pair, int binIndex) + { + // Mix populated event pairs within the selected pool. + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (tracks1.size() == 0 || tracks2.size() == 0) { + continue; + } + + // Find leading tracks in the first mixed event. + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks1); + + // LOGF(debug, "Mix TRI Ev (BIN%d): (%d, %d), z=(%.3f, %.3f), mult=(%d, %d)", + // binIndex, c1.globalIndex(), c2.globalIndex(), c1.posZ(), c2.posZ(), tracks1.size(), tracks2.size()); + + // Select tracks from different events using the configured pair rules. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart)) { + continue; + } + + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + // Count accepted pairs in the corresponding multiplicity interval. + histos.fill(HIST("MixingPairs/MixingPairsNoLA"), binIndex); + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + if (binIndex == idx + 1) { + histos.fill(HIST("Corelations/bProjTRI_") + HIST(mixedBinNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/bTRI_") + HIST(mixedBinNames[idx]), deltaPhi, deltaEta); + } + }); + } + } + } + + // Run mixed-event pairing for all multiplicity intervals. + void process(aod::TriangleCollisions const& collisions, + aod::TriangleTracks const& tracks) + { + // Skip mixing when disabled or when the input tables are empty. + if (!doMixedEvent || collisions.size() == 0 || tracks.size() == 0) { + return; + } + + // Process all ten independent mixing pools. + fillMixedEventBin(pairBIN1, 1); + fillMixedEventBin(pairBIN2, 2); + fillMixedEventBin(pairBIN3, 3); + fillMixedEventBin(pairBIN4, 4); + fillMixedEventBin(pairBIN5, 5); + fillMixedEventBin(pairBIN6, 6); + fillMixedEventBin(pairBIN7, 7); + fillMixedEventBin(pairBIN8, 8); + fillMixedEventBin(pairBIN9, 9); + fillMixedEventBin(pairBIN10, 10); + } +}; + +// Expose the event analysis and mixed-event tasks as one workflow. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + }; +} From 64fee980a9a6c51b6ac19f5a11ae23d9468ea797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:41:44 +0200 Subject: [PATCH 102/648] [Infrastructure] Remove obsolete macOS build from clean-test (#18093) --- .github/workflows/clean-test.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index be471ce2287..fcaa7084f8d 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -24,11 +24,6 @@ name: Clean PR checks type: boolean default: true - 'check_build/O2Physics/o2/macOS': - description: build/O2Physics/o2/macOS - type: boolean - default: true - 'check_build/O2Physics/o2': description: build/O2Physics/o2 type: boolean From 153c6fb2630ae0e37022c661468aeef90f3bfd97 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:45:17 +0200 Subject: [PATCH 103/648] [PWGJE] Used wrong subsample histogram, changed to triggers (#18090) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 18d5701a0ea..3407543e60b 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -252,7 +252,7 @@ struct JetSpectraEseTask { static constexpr int NumSubSmpl = 5; static constexpr int NumSavedRhoFitEvents = 5; std::array, NumSubSmpl> hSameSub; - std::array, NumSubSmpl> hCentralityAnalyzedSub; + std::array, NumSubSmpl> hNtrigSub; void applySystematicPreset() { @@ -428,8 +428,8 @@ struct JetSpectraEseTask { for (int i = 0; i < NumSubSmpl; ++i) { std::string n = fmt::format("subsamples/thn_jethad_corr_same_subsample{}", i); hSameSub[i] = registry.add(n, "same;...", o2::framework::HistType::kTHnSparseF, axes); - std::string n2 = fmt::format("subsamples/hCentralityAnalyzed_subsample{}", i); - hCentralityAnalyzedSub[i] = registry.add(n2, ";Centrality;entries", o2::framework::HistType::kTH1F, {{centAxis}}); + std::string n2 = fmt::format("subsamples/hNtrig_subsample{}", i); + hNtrigSub[i] = registry.add(n2, "", o2::framework::HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}); } registry.add("hNtrig", "", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}}); @@ -730,7 +730,6 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/hRho"), centrality, collision.rho()); registry.fill(HIST("eventQA/hCentralityAnalyzed"), centrality); int lRndInd = fRndm->Integer(NumSubSmpl); - hCentralityAnalyzedSub[lRndInd]->Fill(centrality); auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; for (const auto& jet : jets) { @@ -763,6 +762,7 @@ struct JetSpectraEseTask { } registry.fill(HIST("hNtrig"), centrality, vCorrL, dPhi, qPerc[0]); + hNtrigSub[lRndInd]->Fill(centrality, vCorrL, dPhi, qPerc[0]); for (const auto& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; From 1d577c5a901be73cd7b1528b52bb1dfc25866436 Mon Sep 17 00:00:00 2001 From: Hamwooseok <168405791+Hamwooseok@users.noreply.github.com> Date: Tue, 29 Sep 2026 20:53:41 +0900 Subject: [PATCH 104/648] [PWGJE] Add hasColl and collision-environment QA to jet cross-section efficiency (#18083) Co-authored-by: Wooseok Ham --- PWGJE/Tasks/jetCrossSectionEfficiency.cxx | 735 +++++++++++++++++++++- 1 file changed, 709 insertions(+), 26 deletions(-) diff --git a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx index 9bcc1195454..cfc984b2c50 100644 --- a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx +++ b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -43,13 +44,19 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include +#include +#include +#include #include using namespace o2; @@ -101,6 +108,7 @@ struct JetCrossSectionEfficiency { Configurable truthFT0CActivityThresholdTimeRange{"truthFT0CActivityThresholdTimeRange", 8000.0f, "multFT0C truth proxy threshold for NoCollInTimeRangeStandard; reco uses FT0C amplitude, so validate or tune this value"}; Configurable truthFT0CActivityThresholdROF{"truthFT0CActivityThresholdROF", 5000.0f, "multFT0C truth proxy threshold for NoCollInRofStandard; reco uses FT0C amplitude, so validate or tune this value"}; Configurable truthRofCloseVzMax{"truthRofCloseVzMax", 0.3f, "maximum truth |delta z| in cm for the NoCollInRofStandard track-presence proxy"}; + Configurable tvxEnvironmentRequireOtherBcBaseline{"tvxEnvironmentRequireOtherBcBaseline", false, "require RCT, TVX, NoTFB and NoITSROFB for other BCs in the TVX-BC environment QA"}; o2::aod::rctsel::RCTFlagsChecker rctChecker; uint64_t rctMask = 0; @@ -143,6 +151,15 @@ struct JetCrossSectionEfficiency { static constexpr float ConfigSwitchHigh = 9998.0f; static constexpr float BrokenPtHardSentinel = 1.0f; static constexpr int MinITSClustersForOccupancy = 5; + static constexpr int HasCollQaMaxCount = 500; + static constexpr int HasCollQaMaxDeltaGlobalBC = 100000; + static constexpr int64_t NoOtherTruthCollision = -1; + + static std::vector getNearestAbsDeltaGlobalBCEdges() + { + // Keep the no-other-BC sentinel separate, then resolve the near-BC region most relevant for reconstruction losses. + return {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 7.5, 15.5, 31.5, 63.5, 127.5, 255.5, 511.5, 1000.5, 2000.5, 4000.5, 8000.5, 16000.5, 32000.5, 64000.5, static_cast(HasCollQaMaxDeltaGlobalBC) + 0.5}; + } struct TruthPbPbSelections { bool valid = false; @@ -150,12 +167,44 @@ struct JetCrossSectionEfficiency { bool noCollInRofStandard = false; }; + struct TruthHasCollEnvironment { + bool valid = false; + int nOtherSameTruthRof = 0; + int64_t nearestAbsDeltaGlobalBC = NoOtherTruthCollision; + }; + + struct VisibleBCEnvironment { + bool valid = false; + int nOtherTVXSameRof = 0; + int64_t nearestAbsDeltaTVXGlobalBC = NoOtherTruthCollision; + int64_t originalBCIndex = -1; + }; + + struct VisibleBCEnvironmentCache { + std::map byBCIndex; + std::map, VisibleBCEnvironment> byRunAndGlobalBC; + }; + using JetCollisionsMCDWithParent = soa::Join; + using JMcCollisionsWithParent = soa::Join; + using JetCollisionsWithParent = soa::Join; using CollisionsWithEvSels = soa::Join; + using OriginalBCsWithSels = soa::Join; + using OriginalCollisionsWithMcLabels = soa::Join; using FullTracksIU = soa::Join; + using FullTracksIUWithMcLabels = soa::Join; + using ChargedMCDJetsWithConstituents = soa::Join; + using ChargedMCPJetsWithConstituents = soa::Join; Partition pvTracks = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); Preslice pvTracksPerCollision = aod::track::collisionId; + PresliceUnsorted collisionsPerFoundBC = aod::evsel::foundBCId; + PresliceUnsorted jetCollisionsPerMcCollision = aod::jmccollisionlb::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoTracksPerMcParticle = aod::mctracklabel::mcParticleId; + PresliceUnsorted jetCollisionsPerOriginalCollision = aod::jcollision::collisionId; + Preslice detectorJetsPerJetCollision = aod::jet::collisionId; enum EventSelectionPreset { PresetSelTvx = 0, @@ -242,7 +291,24 @@ struct JetCrossSectionEfficiency { break; } - if (doprocessCrossSectionEfficiencyBcBitsFirst && isSel8FullPbPb && + const bool isDataTVXBCEnvironmentQAEnabled = doprocessDataTVXBCEnvironmentQA; + const bool isMCQAEnabled = doprocessMCTruthMultFT0CvsRecoAmplitude || + doprocessMCTruthMultFT0CvsRecoITS567 || + doprocessMCEnvironmentQA || + doprocessMCHasCollTrackFateQA || + doprocessMCHasCollJetSurvivalQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessCrossSectionEfficiency || + doprocessCrossSectionEfficiencyBcBitsFirst; + if (isDataTVXBCEnvironmentQAEnabled && isMCQAEnabled) { + LOGF(fatal, "processDataTVXBCEnvironmentQA cannot run with MC-only QA or cross-section-efficiency processes; enable the data process alone for data AO2Ds"); + } + + const bool needsRofParameters = (doprocessCrossSectionEfficiencyBcBitsFirst && isSel8FullPbPb) || + doprocessMCEnvironmentQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessDataTVXBCEnvironmentQA; + if (needsRofParameters && (truthRofOffsetInBC < 0 || truthRofLengthInBC <= 0)) { ccdb->setURL(static_cast(truthRofCcdbUrl)); ccdb->setCaching(true); @@ -327,7 +393,7 @@ struct JetCrossSectionEfficiency { } } - if (doprocessTruthMultFT0CvsRecoAmplitude) { + if (doprocessMCTruthMultFT0CvsRecoAmplitude) { AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; AxisSpec recoFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C}^{reco}"}; registry.add("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c", @@ -335,7 +401,7 @@ struct JetCrossSectionEfficiency { {HistType::kTH2F, {truthMultFT0CAxis, recoFT0CAmplitudeAxis}}); } - if (doprocessTruthMultFT0CvsRecoITS567) { + if (doprocessMCTruthMultFT0CvsRecoITS567) { AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; AxisSpec recoNITS567Axis = {1000, -0.5, 999.5, "N_{ITS567}^{reco}"}; AxisSpec truthMultFT0CPositiveAxis = {2, -0.5, 1.5, "truth multFT0C > 0"}; @@ -352,6 +418,170 @@ struct JetCrossSectionEfficiency { binaryActivity->GetYaxis()->SetBinLabel(1, "N_{ITS567} = 0"); binaryActivity->GetYaxis()->SetBinLabel(2, "N_{ITS567} > 0"); } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other same truth ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec targetMultFT0CAxis = {500, -0.5, 499.5, "target truth multFT0C"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_same_truth_rof_vs_has_coll", + "other MC collisions in the same truth ITS ROF vs hasColl;N_{other same truth ROF};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nOtherSameTruthRofAxis, hasCollAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_vs_has_coll", + "nearest other MC collision |#Delta global BC| vs hasColl;nearest |#Delta global BC|;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_has_coll", + "target truth multFT0C vs hasColl;target truth multFT0C;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF for hasColl;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all", + "target truth multFT0C vs nearest other MC collision distance;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll", + "target truth multFT0C vs nearest other MC collision distance for hasColl;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + + auto nOtherSameTruthRof = registry.get(HIST("h2_n_other_same_truth_rof_vs_has_coll")); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(1, "noColl"); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(2, "hasColl"); + auto nearestAbsDeltaGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll")); + nearestAbsDeltaGlobalBC->GetXaxis()->SetBinLabel(1, "no other valid MC collision"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(1, "noColl"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(2, "hasColl"); + auto targetMultFT0C = registry.get(HIST("h2_target_mult_ft0c_vs_has_coll")); + targetMultFT0C->GetYaxis()->SetBinLabel(1, "noColl"); + targetMultFT0C->GetYaxis()->SetBinLabel(2, "hasColl"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + } + + if (doprocessDataTVXBCEnvironmentQA || doprocessMCEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec targetFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C} at target TVX BC"}; + + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx", + "TVX-BC environment;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco", + "TVX-BC environment for target BCs with a reconstructed collision;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;N_{other TVX BCs in same ITS ROF};reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;nearest other TVX |#Delta global BC|;reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof", + "FT0C amplitude vs TVX-BC environment;A_{FT0C} at target TVX BC;N_{other TVX BCs in same ITS ROF};BCs", + {HistType::kTH2F, {targetFT0CAmplitudeAxis, nOtherTVXSameRofAxis}}); + + auto nOtherTVXSameRof = registry.get(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision")); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(1, "no reco collision"); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(2, "reco collision exists"); + auto nearestAbsDeltaTVXGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision")); + nearestAbsDeltaTVXGlobalBC->GetXaxis()->SetBinLabel(1, "no other TVX BC in run"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(1, "no reco collision"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(2, "reco collision exists"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other MC collisions in same truth ITS ROF}"}; + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof", + "truth and TVX-BC same-ROF environment;N_{other MC collisions in same truth ITS ROF};N_{other TVX BCs in same ITS ROF};weighted MC collisions", + {HistType::kTH2F, {nOtherSameTruthRofAxis, nOtherTVXSameRofAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx", + "truth and TVX-BC nearest collision environment;truth nearest |#Delta global BC|;TVX nearest |#Delta global BC|;weighted MC collisions", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCEnvironmentAxis, nearestAbsDeltaGlobalBCEnvironmentAxis}}); + registry.add("h2_reco_collision_proxy_vs_has_coll", + "reconstructed-collision proxy vs JetDerived hasColl;reconstructed collision at target TVX BC;JetDerived hasColl;weighted MC collisions", + {HistType::kTH2F, {hasRecoCollisionAtTargetBCAxis, hasCollAxis}}); + auto recoCollisionProxy = registry.get(HIST("h2_reco_collision_proxy_vs_has_coll")); + recoCollisionProxy->GetXaxis()->SetBinLabel(1, "no reco collision"); + recoCollisionProxy->GetXaxis()->SetBinLabel(2, "reco collision exists"); + recoCollisionProxy->GetYaxis()->SetBinLabel(1, "noColl"); + recoCollisionProxy->GetYaxis()->SetBinLabel(2, "hasColl"); + } + + if (doprocessMCHasCollJetEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("hs_mcp_jet_pt_tvx_environment_has_coll", + "selected MCP jets vs TVX-BC environment and hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;JetDerived hasColl;weighted jets", + {HistType::kTHnSparseF, {jetPtAxis, nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasCollAxis}}, true); + auto jetEnvironment = registry.get(HIST("hs_mcp_jet_pt_tvx_environment_has_coll")); + jetEnvironment->GetAxis(3)->SetBinLabel(1, "noColl"); + jetEnvironment->GetAxis(3)->SetBinLabel(2, "hasColl"); + jetEnvironment->GetAxis(2)->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCHasCollTrackFateQA) { + AxisSpec trackFateCountAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{target-origin reconstructed tracks}"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("h2_n_reco_tracks_from_target_vs_has_coll", + "reconstructed tracks from target MC collision vs hasColl;N_{target-origin reconstructed tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_unassigned_tracks_from_target_vs_has_coll", + "unassigned tracks from target MC collision vs hasColl;N_{target-origin unassigned tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll", + "target-origin tracks attached to other-labeled collision vs hasColl;N_{tracks attached to other-labeled collision};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + + auto setHasCollAxisLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "noColl"); + histogram->GetYaxis()->SetBinLabel(2, "hasColl"); + }; + setHasCollAxisLabels(registry.get(HIST("h2_n_reco_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"))); + } + + if (doprocessMCHasCollJetSurvivalQA) { + AxisSpec detectorJetExistsAxis = {2, -0.5, 1.5, "detector jet with target-origin reco track"}; + AxisSpec constituentPtFractionAxis = {300, 0., 3., "p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents}"}; + + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_hasColl", + "MCP jet pT vs detector-level jet survival for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_noColl", + "MCP jet pT vs detector-level jet survival for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl", + "MCP jet pT vs best detector-jet target-track capture fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl", + "MCP jet pT vs best detector-jet target-track capture fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + + auto setDetectorJetExistsLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "no detector jet"); + histogram->GetYaxis()->SetBinLabel(2, "detector jet exists"); + }; + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"))); + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"))); + } } template @@ -369,7 +599,7 @@ struct JetCrossSectionEfficiency { } template - bool configureTruthRofParameters(TBC const& truthBC) + bool configureRofParameters(TBC const& bc) { if (truthRofOffsetInBC >= 0 && truthRofLengthInBC > 0) { cachedTruthRofOffsetInBC = truthRofOffsetInBC; @@ -377,28 +607,28 @@ struct JetCrossSectionEfficiency { return true; } - if (cachedTruthRofRun == truthBC.runNumber() && cachedTruthRofLengthInBC > 0) { + if (cachedTruthRofRun == bc.runNumber() && cachedTruthRofLengthInBC > 0) { return true; } - auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", truthBC.timestamp()); + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", bc.timestamp()); if (alppar == nullptr) { - LOGF(fatal, "Could not retrieve ITS/Config/AlpideParam for sel8FullPbPb truth selections (run %d, timestamp %" PRIu64 ")", truthBC.runNumber(), static_cast(truthBC.timestamp())); + LOGF(fatal, "Could not retrieve ITS/Config/AlpideParam for ITS ROF calculations (run %d, timestamp %" PRIu64 ")", bc.runNumber(), static_cast(bc.timestamp())); return false; } - cachedTruthRofRun = truthBC.runNumber(); + cachedTruthRofRun = bc.runNumber(); cachedTruthRofOffsetInBC = truthRofOffsetInBC >= 0 ? truthRofOffsetInBC : alppar->roFrameBiasInBC; cachedTruthRofLengthInBC = truthRofLengthInBC > 0 ? truthRofLengthInBC : alppar->roFrameLengthInBC; if (cachedTruthRofLengthInBC <= 0) { - LOGF(fatal, "Invalid ITS ROF length %" PRId64 " BC for sel8FullPbPb truth selections", static_cast(cachedTruthRofLengthInBC)); + LOGF(fatal, "Invalid ITS ROF length %" PRId64 " BC", static_cast(cachedTruthRofLengthInBC)); return false; } - LOGF(info, "sel8FullPbPb truth selections use ITS ROF offset %" PRId64 " and length %" PRId64 " BC for run %d", static_cast(cachedTruthRofOffsetInBC), static_cast(cachedTruthRofLengthInBC), cachedTruthRofRun); + LOGF(info, "ITS ROF calculations use offset %" PRId64 " and length %" PRId64 " BC for run %d", static_cast(cachedTruthRofOffsetInBC), static_cast(cachedTruthRofLengthInBC), cachedTruthRofRun); return true; } - int64_t truthRofId(uint64_t globalBC) const + int64_t rofId(uint64_t globalBC) const { // Match EventSelectionModule: use the ITS ROF bias and length from DPLAlpideParam, with one orbit added to avoid a negative numerator. return (static_cast(globalBC) + o2::constants::lhc::LHCMaxBunches - cachedTruthRofOffsetInBC) / cachedTruthRofLengthInBC; @@ -426,11 +656,11 @@ struct JetCrossSectionEfficiency { requireValidTruthFT0CActivity(mccollision); auto truthBC = mccollision.template bc_as(); const uint64_t currentGlobalBC = truthBC.globalBC(); - if (currentGlobalBC == std::numeric_limits::max() || !configureTruthRofParameters(truthBC)) { + if (currentGlobalBC == std::numeric_limits::max() || !configureRofParameters(truthBC)) { return selections; } - const int64_t currentRof = truthRofId(currentGlobalBC); + const int64_t currentRof = rofId(currentGlobalBC); bool hasNarrowActivity = false; bool hasHighActivityInTimeRange = false; bool hasHighActivityInSameRof = false; @@ -455,7 +685,7 @@ struct JetCrossSectionEfficiency { const float deltaTimeUs = static_cast(deltaGlobalBC) * o2::constants::lhc::LHCBunchSpacingNS / 1000.0f; const bool inNarrowWindow = std::abs(deltaTimeUs) < truthTimeRangeNarrowUs; const bool inStandardTimeWindow = deltaTimeUs > truthTimeRangeStandardMinUs && deltaTimeUs < truthTimeRangeStandardMaxUs; - const bool isSameRof = truthRofId(otherGlobalBC) == currentRof; + const bool isSameRof = rofId(otherGlobalBC) == currentRof; if (!inNarrowWindow && !inStandardTimeWindow && !isSameRof) { continue; } @@ -488,6 +718,177 @@ struct JetCrossSectionEfficiency { return selections; } + template + bool passesTVXBCEnvironmentBaseline(TBC const& bc) + { + const bool passesRct = !static_cast(applyRCT) || (bc.rct_raw() & rctMask) == 0; + return passesRct && + bc.selection_bit(aod::evsel::kIsTriggerTVX) && + bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && + bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + } + + template + bool isOtherTVXBCForEnvironment(TBC const& bc) + { + if (!bc.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + return !static_cast(tvxEnvironmentRequireOtherBcBaseline) || passesTVXBCEnvironmentBaseline(bc); + } + + template + VisibleBCEnvironmentCache buildVisibleBCEnvironmentCache(TBCs const& bcs) + { + struct TVXBCInfo { + int64_t globalIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + bool passesTargetBaseline = false; + }; + + std::map> tvxBCsPerRun; + for (const auto& bc : bcs) { + if (!isOtherTVXBCForEnvironment(bc) || bc.globalBC() == std::numeric_limits::max()) { + continue; + } + if (!configureRofParameters(bc)) { + continue; + } + tvxBCsPerRun[bc.runNumber()].push_back({static_cast(bc.globalIndex()), bc.runNumber(), bc.globalBC(), rofId(bc.globalBC()), passesTVXBCEnvironmentBaseline(bc)}); + } + + VisibleBCEnvironmentCache cache; + for (auto& [runNumber, tvxBCs] : tvxBCsPerRun) { + std::sort(tvxBCs.begin(), tvxBCs.end(), [](const TVXBCInfo& lhs, const TVXBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); + + std::unordered_map nTVXBCsPerRof; + for (const auto& tvxBC : tvxBCs) { + ++nTVXBCsPerRof[tvxBC.rof]; + } + + for (size_t i = 0; i < tvxBCs.size(); ++i) { + const auto& tvxBC = tvxBCs[i]; + if (!tvxBC.passesTargetBaseline) { + continue; + } + + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = tvxBC.globalBC - tvxBCs[i - 1].globalBC; + } + if (i + 1 < tvxBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = tvxBCs[i + 1].globalBC - tvxBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); + } + + VisibleBCEnvironment environment; + environment.valid = true; + environment.nOtherTVXSameRof = nTVXBCsPerRof[tvxBC.rof] - 1; + environment.originalBCIndex = tvxBC.globalIndex; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaTVXGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache.byBCIndex[tvxBC.globalIndex] = environment; + cache.byRunAndGlobalBC[{runNumber, tvxBC.globalBC}] = environment; + } + } + return cache; + } + + template + std::map buildTruthHasCollEnvironmentCache(TMcCollisions const& mcCollisions) + { + struct TruthBCInfo { + int64_t mcCollisionIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + }; + + std::map> truthBCsPerRun; + for (const auto& mccollision : mcCollisions) { + const auto truthBC = mccollision.template bc_as(); + if (truthBC.globalBC() == std::numeric_limits::max() || !configureRofParameters(truthBC)) { + continue; + } + truthBCsPerRun[truthBC.runNumber()].push_back({static_cast(mccollision.globalIndex()), truthBC.runNumber(), truthBC.globalBC(), rofId(truthBC.globalBC())}); + } + + std::map cache; + for (auto& [runNumber, truthBCs] : truthBCsPerRun) { + (void)runNumber; + std::sort(truthBCs.begin(), truthBCs.end(), [](const TruthBCInfo& lhs, const TruthBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); + + std::unordered_map nTruthBCsPerRof; + for (const auto& truthBC : truthBCs) { + ++nTruthBCsPerRof[truthBC.rof]; + } + + for (size_t i = 0; i < truthBCs.size(); ++i) { + const auto& truthBC = truthBCs[i]; + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = truthBC.globalBC - truthBCs[i - 1].globalBC; + } + if (i + 1 < truthBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = truthBCs[i + 1].globalBC - truthBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); + } + + TruthHasCollEnvironment environment; + environment.valid = true; + environment.nOtherSameTruthRof = nTruthBCsPerRof[truthBC.rof] - 1; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache[truthBC.mcCollisionIndex] = environment; + } + } + return cache; + } + + template + void fillTVXBCEnvironmentHistogramsFromCache(VisibleBCEnvironmentCache const& environments, + TBCs const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + for (const auto& bc : bcs) { + const auto environment = environments.byBCIndex.find(static_cast(bc.globalIndex())); + if (environment == environments.byBCIndex.end()) { + continue; + } + + // This is a data-level TVX-BC to reconstructed-collision proxy, not the MC truth hasColl definition. + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + const auto& visibleEnvironment = environment->second; + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + if (hasRecoCollisionAtTargetBC) { + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + } + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision"), visibleEnvironment.nOtherTVXSameRof, hasRecoCollisionAtTargetBC); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision"), visibleEnvironment.nearestAbsDeltaTVXGlobalBC, hasRecoCollisionAtTargetBC); + if (bc.has_foundFT0()) { + const auto foundFT0 = ft0s.rawIteratorAt(bc.foundFT0Id()); + registry.fill(HIST("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof"), foundFT0.sumAmpC(), visibleEnvironment.nOtherTVXSameRof); + } + } + } + + template + void fillTVXBCEnvironmentHistograms(TBCs const& bcs, CollisionsWithEvSels const& collisions, aod::FT0s const& ft0s) + { + const auto environments = buildVisibleBCEnvironmentCache(bcs); + fillTVXBCEnvironmentHistogramsFromCache(environments, bcs, collisions, ft0s); + } + template bool isAcceptedJet(TJets const& jet) { @@ -519,10 +920,10 @@ struct JetCrossSectionEfficiency { return true; } - void processTruthMultFT0CvsRecoAmplitude(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - CollisionsWithEvSels const&, - aod::FT0s const& ft0s) + void processMCTruthMultFT0CvsRecoAmplitude(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + aod::FT0s const& ft0s) { if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return; @@ -543,13 +944,13 @@ struct JetCrossSectionEfficiency { auto foundFT0 = ft0s.rawIteratorAt(originalCollision.foundFT0Id()); registry.fill(HIST("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c"), mccollision.multFT0C(), foundFT0.sumAmpC(), mccollision.weight()); } - PROCESS_SWITCH(JetCrossSectionEfficiency, processTruthMultFT0CvsRecoAmplitude, - "truth multFT0C vs reconstructed foundFT0 A_FT0C for one-to-one MC/reco collision associations", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoAmplitude, + "[MC ONLY] truth multFT0C vs reconstructed foundFT0 A_FT0C for one-to-one MC/reco collision associations", false); - void processTruthMultFT0CvsRecoITS567(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - CollisionsWithEvSels const&, - FullTracksIU const&) + void processMCTruthMultFT0CvsRecoITS567(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + FullTracksIU const&) { if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return; @@ -576,8 +977,290 @@ struct JetCrossSectionEfficiency { registry.fill(HIST("h2_mccollision_mult_ft0c_positive_vs_reco_its567_positive"), mccollision.multFT0C() > 0.0f, nITS567 > 0, weight); } - PROCESS_SWITCH(JetCrossSectionEfficiency, processTruthMultFT0CvsRecoITS567, - "truth multFT0C activity vs reconstructed PV-contributor ITS567 activity for one-to-one MC/reco collision associations", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoITS567, + "[MC ONLY] truth multFT0C activity vs reconstructed PV-contributor ITS567 activity for one-to-one MC/reco collision associations", false); + + void processMCEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + aod::JBCs const&, + OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + const auto truthEnvironments = buildTruthHasCollEnvironmentCache(mcCollisions); + // The visible-BC QA is deliberately unweighted so its definition remains directly comparable to data. + fillTVXBCEnvironmentHistogramsFromCache(visibleEnvironments, bcs, collisions, ft0s); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + continue; + } + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { + continue; + } + + const auto environment = truthEnvironments.find(mccollision.globalIndex()); + if (environment == truthEnvironments.end() || !environment->second.valid) { + continue; + } + + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_other_same_truth_rof_vs_has_coll"), environment->second.nOtherSameTruthRof, hasRecoColl, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll"), environment->second.nearestAbsDeltaGlobalBC, hasRecoColl, weight); + + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment != visibleEnvironments.byRunAndGlobalBC.end() && visibleEnvironment->second.valid) { + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, visibleEnvironment->second.originalBCIndex); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + registry.fill(HIST("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof"), environment->second.nOtherSameTruthRof, visibleEnvironment->second.nOtherTVXSameRof, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx"), environment->second.nearestAbsDeltaGlobalBC, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, weight); + registry.fill(HIST("h2_reco_collision_proxy_vs_has_coll"), hasRecoCollisionAtTargetBC, hasRecoColl, weight); + } + + if (mccollision.multFT0C() < 0.0f) { + continue; + } + + registry.fill(HIST("h2_target_mult_ft0c_vs_has_coll"), mccollision.multFT0C(), hasRecoColl, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + if (hasRecoColl) { + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCEnvironmentQA, + "[MC ONLY] truth and visible TVX collision-environment QA and JetDerived hasColl validation", false); + + void processMCHasCollTrackFateQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + OriginalCollisionsWithMcLabels const&, + FullTracksIUWithMcLabels const& recoTracks, + aod::McCollisions const&, + aod::McParticles const& mcParticles, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const auto originalMcCollision = mccollision.template mcCollision_as(); + const auto targetMcParticles = mcParticles.sliceBy(mcParticlesPerMcCollision, originalMcCollision.globalIndex()); + int nRecoTracksFromTarget = 0; + int nUnassignedTracksFromTarget = 0; + int nTracksAttachedToOtherLabeledRecoCollision = 0; + + for (const auto& mcParticle : targetMcParticles) { + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, mcParticle.globalIndex()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; + } + + ++nRecoTracksFromTarget; + if (!recoTrack.has_collision()) { + ++nUnassignedTracksFromTarget; + continue; + } + + const auto recoCollision = recoTrack.template collision_as(); + if (recoCollision.has_mcCollision() && recoCollision.mcCollisionId() != originalMcCollision.globalIndex()) { + ++nTracksAttachedToOtherLabeledRecoCollision; + } + } + } + + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_reco_tracks_from_target_vs_has_coll"), nRecoTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"), nUnassignedTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"), nTracksAttachedToOtherLabeledRecoCollision, hasRecoColl, weight); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollTrackFateQA, + "[MC ONLY] track fate for target MC collisions before JetDerived hasColl", false); + + void processMCHasCollJetSurvivalQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + ChargedMCPJetsWithConstituents const& mcpJets, + ChargedMCDJetsWithConstituents const& detectorJets, + JetCollisionsWithParent const& jetCollisions, + FullTracksIUWithMcLabels const& recoTracks, + aod::JetTracks const&, + aod::JTrackPIs const& jTrackParents, + aod::JetParticles const&, + aod::JMcParticlePIs const& jMcParticleParents, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + return; + } + + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + for (const auto& mcpJet : mcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; + } + + float sumTruthConstituentPt = 0.0f; + float sumRecoTrackPtFromTruthJet = 0.0f; + std::unordered_map recoTrackPtByOriginalTrackId; + std::unordered_set jetCollisionIdsReceivingTargetTracks; + for (const auto& constituent : mcpJet.template tracks_as()) { + sumTruthConstituentPt += constituent.pt(); + const auto originalMcParticle = jMcParticleParents.rawIteratorAt(constituent.globalIndex()); + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, originalMcParticle.mcParticleId()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; + } + + const int64_t originalTrackId = recoTrack.globalIndex(); + if (!recoTrackPtByOriginalTrackId.emplace(originalTrackId, recoTrack.pt()).second) { + continue; + } + sumRecoTrackPtFromTruthJet += recoTrack.pt(); + + if (!recoTrack.has_collision()) { + continue; + } + const auto associatedJetCollisions = jetCollisions.sliceBy(jetCollisionsPerOriginalCollision, recoTrack.collisionId()); + for (const auto& jetCollision : associatedJetCollisions) { + jetCollisionIdsReceivingTargetTracks.insert(jetCollision.globalIndex()); + } + } + } + + if (sumTruthConstituentPt <= 0.0f) { + continue; + } + + std::unordered_map capturedRecoPtByDetectorJetId; + for (const int64_t jetCollisionId : jetCollisionIdsReceivingTargetTracks) { + const auto associatedDetectorJets = detectorJets.sliceBy(detectorJetsPerJetCollision, jetCollisionId); + for (const auto& detectorJet : associatedDetectorJets) { + float capturedRecoPt = 0.0f; + for (const auto& detectorConstituent : detectorJet.template tracks_as()) { + const auto originalTrack = jTrackParents.rawIteratorAt(detectorConstituent.globalIndex()); + const auto recoTrack = recoTrackPtByOriginalTrackId.find(originalTrack.trackId()); + if (recoTrack != recoTrackPtByOriginalTrackId.end()) { + capturedRecoPt += recoTrack->second; + } + } + if (capturedRecoPt > 0.0f) { + capturedRecoPtByDetectorJetId[detectorJet.globalIndex()] += capturedRecoPt; + } + } + } + + float bestDetectorJetCapturedTargetRecoPt = 0.0f; + for (const auto& [detectorJetId, capturedRecoPt] : capturedRecoPtByDetectorJetId) { + (void)detectorJetId; + if (capturedRecoPt > bestDetectorJetCapturedTargetRecoPt) { + bestDetectorJetCapturedTargetRecoPt = capturedRecoPt; + } + } + + const bool hasDetectorJetWithTargetTrack = !capturedRecoPtByDetectorJetId.empty(); + const float recoConstituentPtFraction = sumRecoTrackPtFromTruthJet / sumTruthConstituentPt; + const float bestDetectorJetCaptureFraction = bestDetectorJetCapturedTargetRecoPt / sumTruthConstituentPt; + if (hasRecoColl) { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); + } else { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetSurvivalQA, + "[MC ONLY] truth-constituent overlap QA for detector-level charged jet survival before JetDerived hasColl", false); + + void processMCHasCollJetEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + ChargedMCPJetsWithConstituents const& mcpJets, + aod::JetParticles const&, + aod::JBCs const&, + OriginalBCsWithSels const& bcs) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + continue; + } + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { + continue; + } + + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment == visibleEnvironments.byRunAndGlobalBC.end() || !visibleEnvironment->second.valid) { + continue; + } + + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + continue; + } + + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + const auto targetMcpJets = mcpJets.sliceBy(mcpJetsPerMcCollision, mccollision.globalIndex()); + for (const auto& mcpJet : targetMcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; + } + + // hasRecoColl remains a histogram axis rather than a selection, so noColl MCP jets are part of the denominator. + registry.fill(HIST("hs_mcp_jet_pt_tvx_environment_has_coll"), mcpJet.pt(), visibleEnvironment->second.nOtherTVXSameRof, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, hasRecoColl, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetEnvironmentQA, + "[MC ONLY] selected MCP jets vs original-TVX-BC environment and JetDerived hasColl", false); + + void processDataTVXBCEnvironmentQA(OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + fillTVXBCEnvironmentHistograms(bcs, collisions, ft0s); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processDataTVXBCEnvironmentQA, + "[DATA ONLY] TVX-BC collision-environment QA using original BC and reconstructed-collision tables", false); void processCrossSectionEfficiency(aod::JetMcCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, From 7de6b9e2d03829eb0c9b0b982856a5f7995dc5af Mon Sep 17 00:00:00 2001 From: Fan Si <147278151+fsii@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:18:07 +0200 Subject: [PATCH 105/648] [PWGCF] Add QA histograms (#18079) --- PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx | 209 +++++++++++-------- 1 file changed, 119 insertions(+), 90 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx index fe441f712d1..5f2ee67ac07 100644 --- a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx @@ -15,6 +15,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -72,16 +73,16 @@ #include #include -#define C_CS(cs) /* NOLINT(cppcoreguidelines-macro-usage) */ \ - [](const std::index_sequence) constexpr -> ConstStr<(cs)[indices]...> { \ - static_assert(std::is_array_v> && \ - std::same_as>, char>); \ - return ConstStr<(cs)[indices]...>{}; \ +#define C_CS(cs) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(cs)[indices]...> { \ + static_assert(std::is_array_v> && \ + std::same_as>, char>); \ + return ConstStr<(cs)[indices]...>{}; \ }(std::make_index_sequence{}) -#define C_SV(sv) /* NOLINT(cppcoreguidelines-macro-usage) */ \ - [](const std::index_sequence) constexpr -> ConstStr<(sv)[indices]...> { \ - static_assert(std::same_as, std::string_view>); \ - return ConstStr<(sv)[indices]...>{}; \ +#define C_SV(sv) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(sv)[indices]...> { \ + static_assert(std::same_as, std::string_view>); \ + return ConstStr<(sv)[indices]...>{}; \ }(std::make_index_sequence<(sv).size()>{}) using namespace o2; @@ -324,31 +325,31 @@ template requires IsValidEnum constexpr std::int32_t NEs{toI(E::N)}; -enum class NameKind { +enum class NameKind : std::int8_t { Default = 0, Lower, Display, DisplayLower, N }; -enum class DataMode { +enum class DataMode : std::int8_t { McMcParticle = 0, McTrack, RawTrack, N }; -enum class Selection { +enum class Selection : std::int8_t { Loose = 0, Default, Tight, N }; -enum class RangeEdge { +enum class RangeEdge : std::int8_t { Min = 0, Max, N }; -enum class McEventSelection { +enum class McEventSelection : std::int8_t { All = 0, Good, Inel, @@ -356,7 +357,7 @@ enum class McEventSelection { NRecoCollisions, N }; -enum class EventSelection { +enum class EventSelection : std::int8_t { All = 0, Good, Run, @@ -369,64 +370,64 @@ enum class EventSelection { NPvContributors, N }; -enum class CentralityDefinition { +enum class CentralityDefinition : std::int8_t { Ft0a = 0, Ft0c, Ft0m, N }; -enum class DcaMeasure { +enum class DcaMeasure : std::int8_t { Mean = 0, Sigma, N }; -enum class DcaAxis { +enum class DcaAxis : std::int8_t { Xy = 0, Z, N }; -enum class Detector { +enum class Detector : std::int8_t { Tpc = 0, Tof, N }; -enum class PidStrategy { +enum class PidStrategy : std::int8_t { Tpc = 0, TpcTof, N }; -enum class PidStrategyAll { +enum class PidStrategyAll : std::int8_t { Tpc = 0, Tof, TpcTofSeparated, TpcTofCombined, N }; -enum class ParticleSpecies { +enum class ParticleSpecies : std::int8_t { Pion = 0, Kaon, Proton, N }; -enum class ParticleSpeciesAll { +enum class ParticleSpeciesAll : std::int8_t { All = 0, Pion, Kaon, Proton, N }; -enum class ChargeSpecies { +enum class ChargeSpecies : std::int8_t { Plus = 0, Minus, N }; -enum class ParticleNumber { +enum class ParticleNumber : std::int8_t { Charge = 0, Kaon, Proton, N }; -enum class ChargeNumber { +enum class ChargeNumber : std::int8_t { Plus = 0, Minus, Total, @@ -783,7 +784,7 @@ template constexpr std::int32_t nEnabled(const Configurable>& cfg) noexcept { const LabeledArray& la{cfg.value}; - return std::count_if(la[0], la[0] + la.rows() * la.cols(), [](const T x) constexpr -> bool { return x != T{}; }); + return std::ranges::count_if(std::span{la[0], static_cast(la.rows()) * static_cast(la.cols())}, [](const T input) constexpr noexcept -> bool { return input != T{}; }); } template requires std::is_arithmetic_v @@ -794,7 +795,7 @@ constexpr bool isEnabled(const Configurable>& cfg) noexcept double interpolate(const TH3* const h, const double x, const double y, const double z) { - if (!h) { + if (h == nullptr) { return 0.; } @@ -918,18 +919,13 @@ struct PartNumFluc { return std::abs(nSigmaPid) < TruncationAbsNSigmaPid && std::abs(nSigmaPidShifted) < TruncationAbsNSigmaPid ? nSigmaPidShifted : -TruncationAbsNSigmaPid; } - [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept - { - return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); - } - std::array> dcas{}; std::int32_t sign{}; double pt{}; double eta{}; double phi{}; std::array> havePid{}; - std::array>, NEs> nsSigmaPid{[]() consteval -> std::array>, NEs> { + std::array>, NEs> nsSigmaPid{[]() consteval noexcept -> std::array>, NEs> { std::array>, NEs> result{}; std::array> source{}; source.fill(-TruncationAbsNSigmaPid); @@ -937,6 +933,10 @@ struct PartNumFluc { return result; }()}; + [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept + { + return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); + } constexpr void clear() noexcept { *this = {}; } } holderTrack{}; @@ -944,7 +944,14 @@ struct PartNumFluc { template static constexpr T convert(const double input) noexcept { - return std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max() ? static_cast(std::rint(input)) : (std::is_signed_v ? std::numeric_limits::lowest() : std::numeric_limits::max()); + if (std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max()) { + return static_cast(std::rint(input)); + } + if constexpr (std::is_signed_v) { + return std::numeric_limits::lowest(); + } else { + return std::numeric_limits::max(); + } } std::array> nsMcParticles{}; @@ -1039,13 +1046,13 @@ struct PartNumFluc { Configurable> cfgCutsMinTpcNCrossedRows{"cfgCutsMinTpcNCrossedRows", {std::array>{70, 80, 90}.data(), NEs, getDisplayNames()}, "Minimum numbers of crossed rows TPC"}; Configurable cfgCutMinTpcNCrossedRowsRatio{"cfgCutMinTpcNCrossedRowsRatio", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable cfgFlagRecalibrationDca{"cfgFlagRecalibrationDca", false, "DCA recalibration flag"}; - Configurable> cfgCutsMaxAbsNSigmaDca{"cfgCutsMaxAbsNSigmaDca", {std::array * NEs>{3., 2.5, 2., 3., 2.5, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values of DCA (cm)"}; - Configurable> cfgCutsRangePt{"cfgCutsRangePt", {std::array * NEs>{0.2, 2., 0.3, 2., 0.4, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "pT ranges (GeV/c)"}; + Configurable> cfgCutsMaxAbsNSigmaDca{"cfgCutsMaxAbsNSigmaDca", {std::array(NEs) * static_cast(NEs)>{3., 2.5, 2., 3., 2.5, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values of DCA (cm)"}; + Configurable> cfgCutsRangePt{"cfgCutsRangePt", {std::array(NEs) * static_cast(NEs)>{0.2, 2., 0.3, 2., 0.4, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "pT ranges (GeV/c)"}; Configurable cfgCutMaxAbsEta{"cfgCutMaxAbsEta", 0.8, "Maximum absolute eta"}; Configurable> cfgThresholdsPtTofPid{"cfgThresholdsPtTofPid", {std::array>{0.4, 0.4, 0.8}.data(), NEs, getDisplayNames()}, "pT (GeV/c) thresholds for TOF PID"}; Configurable> cfgFlagsRecalibrationNSigmaPid{"cfgFlagsRecalibrationNSigmaPid", {std::array>{}.data(), NEs, getDisplayNames()}, "nSigma PID recalibration flags"}; Configurable cfgFlagRejectionOthers{"cfgFlagRejectionOthers", false, "Other particle species rejection flag"}; - Configurable> cfgCutsMaxAbsNSigmaPid{"cfgCutsMaxAbsNSigmaPid", {std::array * NEs>{2.5, 2., 1.5, 2.5, 2., 1.5, 2.5, 2., 1.5}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values for PID"}; + Configurable> cfgCutsMaxAbsNSigmaPid{"cfgCutsMaxAbsNSigmaPid", {std::array(NEs) * static_cast(NEs)>{2.5, 2., 1.5, 2.5, 2., 1.5, 2.5, 2., 1.5}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values for PID"}; Configurable cfgFlagMcParticlePhysicalPrimary{"cfgFlagMcParticlePhysicalPrimary", true, "Flag of requiring physical primary MC particle"}; Configurable cfgFlagMcParticleMomentum{"cfgFlagMcParticleMomentum", true, "Flag of using momentum of MC particle"}; } cgTrack{}; @@ -1132,12 +1139,12 @@ struct PartNumFluc { } for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(getName(iDcaAxis).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(std::string{getName(iDcaAxis)}.c_str(), std::string{getName(iSelection)}.c_str()); } } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { - holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(getName(iParticleSpecies).data(), getName(iSelection).data()); + holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(std::string{getName(iParticleSpecies)}.c_str(), std::string{getName(iSelection)}.c_str()); } } @@ -1264,7 +1271,7 @@ struct PartNumFluc { if (cgAnalysis.cfgFlagQaRun.value) { LOG(info) << "Enabling run QA."; - const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, holderCcdb.runNumbersToIndicesRunAndGroup.size() - 0.5, "Run Index"}}}; + const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()) - 0.5, "Run Index"}}}; for (const auto& [name, title, isChargeSpeciesSeparated] : std::to_array>( {{"Vx", "#LT#it{V}_{#it{x}}#GT (cm)", false}, @@ -1361,7 +1368,7 @@ struct PartNumFluc { {"hPtDcaZ", "", {HistType::kTHnSparseD, {asPt, {250, -0.5, 0.5, "DCA_{#it{z}} (cm)"}}}}, {"pCentralityPtEtaDcaZ", ";;#LTDCA_{#it{z}}#GT (cm)", hcsQaDcaProfile}})) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - hrQaDca.add(std::format("{}_{}", name, getName(iChargeSpecies)).c_str(), title.data(), hcs); + hrQaDca.add(std::format("{}_{}", name, getName(iChargeSpecies)).c_str(), std::string{title}.c_str(), hcs); } } } @@ -1375,7 +1382,10 @@ struct PartNumFluc { for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { - hrQaAcceptance.add(std::format("h{}Pt_{}Edge{}{}", std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "Eta" : "Rapidity", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All)) ? "#it{#eta}" : "#it{y}"}, {250, 0., 2.5, "#it{p}_{T} (GeV/#it{c})"}}}); + hrQaAcceptance.add(std::format("hEta{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{#eta}_{Gen}" : "#it{#eta}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + if (!std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All))) { + hrQaAcceptance.add(std::format("hRapidity{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{y}_{Gen}" : "#it{y}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + } } } } @@ -1437,19 +1447,17 @@ struct PartNumFluc { } } - if (doProcessMc.value) { - if (cgAnalysis.cfgFlagQaMc.value) { - LOG(info) << "Enabling MC QA."; + if (doProcessMc.value && cgAnalysis.cfgFlagQaMc.value) { + LOG(info) << "Enabling MC QA."; - const double maxAbsVz{std::ceil(cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; - const AxisSpec asCentrality{20, 0., 100., "Centrality (%)"}; + const double maxAbsVz{std::ceil(cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; + const AxisSpec asCentrality{20, 0., 100., "Centrality (%)"}; - hrQaMc.add("hNCollisionsPerMcCollision", "", {HistType::kTH1D, {{20, -0.5, 19.5, "No. of collisions per MC collision"}}}); - hrQaMc.add("hCentralityNTracksPerMcParticle", "", {HistType::kTHnSparseF, {asCentrality, {20, -0.5, 19.5, "No. of tracks per MC particle"}}}); - hrQaMc.add("hCentralityVzMcDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {static_cast(maxAbsVz) * 20, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}}^{Gen} (cm)"}, {200, -0.2, 0.2, "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); - hrQaMc.add("hCentralityPtMcEtaMcDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {200, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}_{Gen}"}, {320, -0.8, 0.8, "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); - hrQaMc.add("hCentralityPtMcEtaMcDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {20, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {240, -1.2, 1.2, "#it{#eta}_{Gen}"}, {160, -0.4, 0.4, "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); - } + hrQaMc.add("hNCollisionsPerMcCollision", "", {HistType::kTH1D, {{20, -0.5, 19.5, "No. of collisions per MC collision"}}}); + hrQaMc.add("hCentralityNTracksPerMcParticle", "", {HistType::kTHnSparseF, {asCentrality, {20, -0.5, 19.5, "No. of tracks per MC particle"}}}); + hrQaMc.add("hCentralityVzMcDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {static_cast(maxAbsVz) * 20, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}}^{Gen} (cm)"}, {200, -0.2, 0.2, "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {200, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}_{Gen}"}, {320, -0.8, 0.8, "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {20, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {240, -1.2, 1.2, "#it{#eta}_{Gen}"}, {160, -0.4, 0.4, "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); } for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { @@ -1566,7 +1574,7 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndex->GetN())) { - holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetX()[iRun]))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetY()[iRun]))}; + holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetPointX(iRun)))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetPointY(iRun)))}; } if (cgEvent.cfgFlagRejectionRunBadMc.value) { @@ -1575,8 +1583,8 @@ struct PartNumFluc { LOG(fatal) << "Invalid gRunNumberGroupIndex_mc!"; } for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndexMc->GetN())) { - if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetY()[iRun])), 0)) { - if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetX()[iRun])))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { + if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointY(iRun))), 0)) { + if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointX(iRun))))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { iter->second.second = -iter->second.second; } } @@ -1768,7 +1776,7 @@ struct PartNumFluc { template requires IsValidEnumValue - bool isPid() const + bool isPid() const noexcept { if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { return ChargeSpeciesValue == ChargeSpecies::Plus ? std::cmp_greater(holderMcParticle.charge, 0) : std::cmp_less(holderMcParticle.charge, 0); @@ -1924,9 +1932,9 @@ struct PartNumFluc { fillByDcaAxis.template operator()(); } - template - requires IsValidEnumValue - void fillQaAcceptanceByParticleSpeciesAll(const T& track) + template + requires IsValidEnumValue && (DataModeValue == DataMode::McMcParticle || DataModeValue == DataMode::RawTrack) + void fillQaAcceptanceByParticleSpeciesAll() { if (!cgAnalysis.cfgFlagsQaAcceptance.value.get(toI(ParticleSpeciesAllValue))) { return; @@ -1935,13 +1943,29 @@ struct PartNumFluc { const auto fillByChargeSpecies{ [this] requires IsValidEnumValue - (const auto& name, const double input) -> void { + () -> void { const auto fillByPidStrategy{ - [this, input, &name] + [this] requires IsValidEnumValue () -> void { - if (isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { - hrQaAcceptance.fill(C_CS("h") + name + C_CS("Pt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), input, holderTrack.pt); // NOLINT(clang-analyzer-core.NonNullParamChecker) + if constexpr (DataModeValue == DataMode::McMcParticle) { + if (!isPid(PidStrategyValue), ParticleSpeciesAll::All>(false) || !isPid()) { + return; + } + + hrQaAcceptance.fill(C_CS("hEtaMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderMcParticle.eta, holderMcParticle.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderMcParticle.pt, holderMcParticle.eta, getMass(getValue(ParticleSpeciesAllValue))), holderMcParticle.pt); + } + } else { // DataModeValue == DataMode::RawTrack + if (!isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { + return; + } + + hrQaAcceptance.fill(C_CS("hEtaPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderTrack.eta, holderTrack.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderTrack.pt, holderTrack.eta, getMass(getValue(ParticleSpeciesAllValue))), holderTrack.pt); + } } }}; @@ -1949,18 +1973,10 @@ struct PartNumFluc { fillByPidStrategy.template operator()(); }}; - if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { - if (std::cmp_greater(holderTrack.sign, 0)) { - fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); - } else { - fillByChargeSpecies.template operator()(C_CS("Eta"), holderTrack.eta); - } + if (std::cmp_greater(DataModeValue == DataMode::McMcParticle ? holderMcParticle.charge : holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); } else { - if (std::cmp_greater(holderTrack.sign, 0)) { - fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); - } else { - fillByChargeSpecies.template operator()(C_CS("Rapidity"), track.rapidity(getMass(getValue(ParticleSpeciesAllValue)))); - } + fillByChargeSpecies.template operator()(); } } @@ -2056,7 +2072,7 @@ struct PartNumFluc { return; } - const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr -> std::int32_t { + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { if constexpr (DataModeValue == DataMode::McMcParticle) { return chargeMcParticle; } else { @@ -2190,7 +2206,7 @@ struct PartNumFluc { return; } - const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr -> std::int32_t { + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { if constexpr (DataModeValue == DataMode::McMcParticle) { return chargeMcParticle; } else { @@ -2198,7 +2214,7 @@ struct PartNumFluc { } }(holderMcParticle.charge, holderTrack.sign)}; - const bool doUseMcParticleMomentum{[](const bool flagMcParticleMomentum) constexpr -> bool { + const bool doUseMcParticleMomentum{[](const bool flagMcParticleMomentum) constexpr noexcept -> bool { if constexpr (DataModeValue == DataMode::McMcParticle) { return true; } else if constexpr (DataModeValue == DataMode::McTrack) { @@ -2224,7 +2240,7 @@ struct PartNumFluc { return false; } - const bool doUseTofPid{[](const bool doUseMcParticleMomentumValue, const double ptMcParticle, const double ptTrack, const double thresholdPtTofPid) constexpr -> bool { + const bool doUseTofPid{[](const bool doUseMcParticleMomentumValue, const double ptMcParticle, const double ptTrack, const double thresholdPtTofPid) constexpr noexcept -> bool { if constexpr (DataModeValue == DataMode::McMcParticle) { return ptMcParticle >= thresholdPtTofPid; } else if constexpr (DataModeValue == DataMode::McTrack) { @@ -2436,16 +2452,6 @@ struct PartNumFluc { if (!isGoodDca()) { return false; } - - if (holderMember.doQaAcceptance) { - const double vz{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; - if (holderTrack.eta * vz > 0. && std::abs(vz) > (doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - fillQaAcceptanceByParticleSpeciesAll(track); - } - } } return true; @@ -2834,6 +2840,22 @@ struct PartNumFluc { const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; + if (holderMember.doQaAcceptance && mcParticle.isPhysicalPrimary() && std::cmp_equal(tracksMatched.size(), 1)) { + const double vz{cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; + if (holderMcParticle.eta * vz > 0. && std::abs(vz) > (cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { + for (const auto& track : tracksMatched) { + if (!setTrack(track)) { + continue; + } + + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } + } + } + if (cgAnalysis.cfgFlagQaMc.value && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { hrQaMc.fill(C_CS("hCentralityNTracksPerMcParticle"), holderEvent.centralityCalibration, tracksMatched.size()); } @@ -2951,6 +2973,13 @@ struct PartNumFluc { continue; } + if (holderMember.doQaAcceptance && holderTrack.eta * holderEvent.vz > 0. && std::abs(holderEvent.vz) > cgEvent.cfgCutMaxAbsVz.value - 1.) { + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } + if (holderMember.doQaPhi) { fillQaPhiByParticleSpeciesAll(); fillQaPhiByParticleSpeciesAll(); From 2e27a7b298c0952a3d92bccbe5aab168d1255089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 29 Sep 2026 14:21:31 +0200 Subject: [PATCH 106/648] [PWGLF] Add on-the-fly coal to h-d corr (#18091) --- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 263 +++++++++++++++++- 1 file changed, 259 insertions(+), 4 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 50afd592225..db3a2218882 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -91,11 +92,20 @@ struct HadronNucleiCorrelation { Configurable doQA{"doQA", true, "save QA histograms"}; Configurable doMCQA{"doMCQA", false, "save MC QA histograms"}; Configurable isMC{"isMC", false, "is MC"}; - Configurable isMCGen{"isMCGen", false, "is isMCGen"}; Configurable isPrim{"isPrim", true, "is isPrim"}; Configurable doCorrection{"doCorrection", false, "do efficiency correction"}; Configurable doQuadraticPID{"doQuadraticPID", false, "do PID with sum in quadrature of TOF and TPC"}; + struct : ConfigurableGroup { + std::string prefix = "Coalescence"; // JSON group name + Configurable doMCGenCoalescence{"doMCGenCoalescence", false, "do a simple coalescence on the generated level"}; + // Coalescence parameters used in Eur. Phys. J. A 59 (2023) 72: + // p_max = 0.148 GeV/c + // r_max = 2 fm + Configurable pMax{"pMax", 0.148, "maximum momentum for coalescence"}; + Configurable rMax{"rMax", 2.0, "maximum radius for coalescence"}; + } settingsCoalescence; + Configurable fCorrectionPath{"fCorrectionPath", "", "Correction path to file"}; Configurable fCorrectionHisto{"fCorrectionHisto", "", "Correction histogram"}; Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -516,11 +526,11 @@ struct HadronNucleiCorrelation { registry.add("hReco_Pt_Proton_TPCEl", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Proton_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); registry.add("hReco_Pt_Deuteron_TPCEl", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); - registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); } } - if (isMCGen) { + if (doprocessMixedEventGen || doprocessSameEventGen) { registry.add("Generated/hNEventsMC", "hNEventsMC", {HistType::kTH1D, {{1, 0.f, 1.f}}}); registry.get(HIST("Generated/hNEventsMC"))->GetXaxis()->SetBinLabel(1, "All"); @@ -549,6 +559,8 @@ struct HadronNucleiCorrelation { registry.add("Generated/hAntiDeuteronsVsPt", "hAntiDeuteronsVsPt", {HistType::kTH1D, {ptAxisGen}}); registry.add("Generated/hProtonsVsPt", "hProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); registry.add("Generated/hAntiProtonsVsPt", "hAntiProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); + } else if (settingsCoalescence.doMCGenCoalescence.value) { + LOG(fatal) << "This is not a Gen Run but the Gen coalescence is required. Turn off doMCGenCoalescence"; } } @@ -1695,10 +1707,253 @@ struct HadronNucleiCorrelation { registry.fill(HIST("Generated/hNEventsMC"), 0.5); + // Local representation of a generated particle. + struct GenCoalescenceCandidate { + GenCoalescenceCandidate(float pt, + float eta, + float phi, + int pdg, + float vx, + float vy, + float vz, + float vt, + bool physicalPrimary) + : mKinematics{pt, eta, phi}, + mPdg{pdg}, + mVx{vx}, + mVy{vy}, + mVz{vz}, + mVt{vt}, + mPhysicalPrimary{physicalPrimary} + { + } + [[nodiscard]] float pt() const { return mKinematics.pt(); } + [[nodiscard]] float eta() const { return mKinematics.eta(); } + [[nodiscard]] float phi() const { return mKinematics.phi(); } + + [[nodiscard]] float px() const { return pt() * std::cos(phi()); } + [[nodiscard]] float py() const { return pt() * std::sin(phi()); } + [[nodiscard]] float pz() const { return mKinematics.pz(); } + + [[nodiscard]] float vx() const { return mVx; } + [[nodiscard]] float vy() const { return mVy; } + [[nodiscard]] float vz() const { return mVz; } + [[nodiscard]] float vt() const { return mVt; } + + [[nodiscard]] float mass() const + { + switch (std::abs(mPdg)) { + case PDG_t::kProton: + return o2::track::PID::getMass(o2::track::PID::Proton); + case PDG_t::kNeutron: + return o2::constants::physics::MassNeutron; + case o2::constants::physics::Pdg::kDeuteron: + return o2::track::PID::getMass(o2::track::PID::Deuteron); + default: + LOG(fatal) << "Unhandled pdg " << mPdg; + return 0.f; + } + } + [[nodiscard]] float energy() const { return std::hypot(mKinematics.p(), mass()); } + [[nodiscard]] float rapidity() const { return mKinematics.rapidityForMass(mass()); } + [[nodiscard]] float y() const { return rapidity(); } + [[nodiscard]] bool isPhysicalPrimary() const { return mPhysicalPrimary; } + [[nodiscard]] int pdgCode() const { return mPdg; } + + private: + GenCandidate mKinematics; + + int mPdg = 0; + float mVx = 0.f; + float mVy = 0.f; + float mVz = 0.f; + float mVt = 0.f; + + bool mPhysicalPrimary = false; + }; + + std::vector particlesToProcess; + particlesToProcess.reserve(mcParticles.size()); + + for (const auto& particle : mcParticles) { + switch (particle.pdgCode()) { + case PDG_t::kProton: + case PDG_t::kProtonBar: + case PDG_t::kNeutron: + case PDG_t::kNeutronBar: + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + particlesToProcess.emplace_back(particle.pt(), + particle.eta(), + particle.phi(), + particle.pdgCode(), + particle.vx(), + particle.vy(), + particle.vz(), + particle.vt(), + particle.isPhysicalPrimary()); + break; + default: + break; + } + } + + if (settingsCoalescence.doMCGenCoalescence.value) { + std::vector consumed(particlesToProcess.size(), false); + std::vector deuterons; + + // Try p+n -> d and pbar+nbar -> dbar independently. + for (const int sign : {+1, -1}) { + const int protonPDG = sign * PDG_t::kProton; + const int neutronPDG = sign * PDG_t::kNeutron; + const int deuteronPDG = sign * o2::constants::physics::Pdg::kDeuteron; + + for (size_t ip = 0; ip < particlesToProcess.size(); ++ip) { + if (consumed[ip] || particlesToProcess[ip].pdgCode() != protonPDG) { + continue; + } + + const auto& proton = particlesToProcess[ip]; + + for (size_t in = 0; in < particlesToProcess.size(); ++in) { + if (consumed[in] || particlesToProcess[in].pdgCode() != neutronPDG) { + continue; + } + + const auto& neutron = particlesToProcess[in]; + + const float ep = proton.energy(); + const float en = neutron.energy(); + + // Propagate the particle freezing out first to the freeze-out time of the other particle + float xp = proton.vx(); + float yp = proton.vy(); + float zp = proton.vz(); + const float tp = proton.vt(); + + float xn = neutron.vx(); + float yn = neutron.vy(); + float zn = neutron.vz(); + const float tn = neutron.vt(); + + const float commonTime = std::max(static_cast(tp), static_cast(tn)); + + if (tp < commonTime) { + const float dt = commonTime - tp; + xp += proton.px() / ep * dt; + yp += proton.py() / ep * dt; + zp += proton.pz() / ep * dt; + } + + if (tn < commonTime) { + const float dt = commonTime - tn; + xn += neutron.px() / en * dt; + yn += neutron.py() / en * dt; + zn += neutron.pz() / en * dt; + } + + // Velocity of the p-n centre-of-mass frame. + const float totalE = ep + en; + const float totalPx = proton.px() + neutron.px(); + const float totalPy = proton.py() + neutron.py(); + const float totalPz = proton.pz() + neutron.pz(); + + const float bx = totalPx / totalE; + const float by = totalPy / totalE; + const float bz = totalPz / totalE; + + const float beta2 = bx * bx + by * by + bz * bz; + if (beta2 >= 1.) { + continue; + } + + const float gamma = 1. / std::sqrt(1. - beta2); + + // Boost the proton momentum into the p-n rest frame. + // In that frame p_p* = -p_n*, therefore |p_p*| is the relative momentum entering the coalescence cut. + float pxStar = proton.px(); + float pyStar = proton.py(); + float pzStar = proton.pz(); + + if (beta2 > 0.) { + const float betaDotP = bx * proton.px() + by * proton.py() + bz * proton.pz(); + + const float factor = ((gamma - 1.) * betaDotP / beta2) - gamma * ep; + + pxStar += factor * bx; + pyStar += factor * by; + pzStar += factor * bz; + } + + const float pRelative = std::sqrt(pxStar * pxStar + pyStar * pyStar + pzStar * pzStar); + + if (pRelative >= settingsCoalescence.pMax.value) { + continue; + } + + // The two particles have already been propagated to equal time in the lab. Transform their spatial separation to the pair CM frame. + float dx = xp - xn; + float dy = yp - yn; + float dz = zp - zn; + + if (beta2 > 0.) { + const float betaDotR = bx * dx + by * dy + bz * dz; + const float factor = (gamma - 1.) * betaDotR / beta2; + + dx += factor * bx; + dy += factor * by; + dz += factor * bz; + } + + const float rRelative = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (rRelative >= settingsCoalescence.rMax.value) { + continue; + } + + // Successful coalescence: remove the constituent proton and neutron and replace them by a deuteron carrying their total three-momentum + consumed[ip] = true; + consumed[in] = true; + + const float deuteronPt = std::hypot(totalPx, totalPy); + const float deuteronPhi = std::atan2(totalPy, totalPx); + const float deuteronEta = std::asinh(totalPz / std::max(deuteronPt, 1.e-12f)); + + deuterons.emplace_back(deuteronPt, + deuteronEta, + deuteronPhi, + deuteronPDG, + 0.5f * (xp + xn), + 0.5f * (yp + yn), + 0.5f * (zp + zn), + commonTime, + proton.isPhysicalPrimary() && neutron.isPhysicalPrimary()); + break; + } + } + } + + // Construct the post-coalescence particle list. + std::vector coalescedParticles; + coalescedParticles.reserve(particlesToProcess.size() + deuterons.size()); + + for (size_t i = 0; i < particlesToProcess.size(); ++i) { + if (!consumed[i]) { + coalescedParticles.push_back(std::move(particlesToProcess[i])); + } + } + + for (auto& deuteron : deuterons) { + coalescedParticles.push_back(std::move(deuteron)); + } + + particlesToProcess = std::move(coalescedParticles); + } + // Pairing candidates are collected during the QA loop below (which already visits every particle) GenCollisionCache genCache; - for (const auto& particle : mcParticles) { + for (const auto& particle : particlesToProcess) { auto fillGeneratedQa = [this, &particle](const float binPosition) { switch (particle.pdgCode()) { case PDG_t::kProton: From e65210dab6787944f1fa0eb716cdf9fc0850ef04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:55:36 +0200 Subject: [PATCH 107/648] [ALICE3] Fix cpplint errors (#18094) --- ALICE3/Core/ALICE3CoreLinkDef.h | 2 ++ ALICE3/Core/FastTrackerLinkDef.h | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ALICE3/Core/ALICE3CoreLinkDef.h b/ALICE3/Core/ALICE3CoreLinkDef.h index bdd3c955d51..10706fa14cc 100644 --- a/ALICE3/Core/ALICE3CoreLinkDef.h +++ b/ALICE3/Core/ALICE3CoreLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/ALICE3/Core/FastTrackerLinkDef.h b/ALICE3/Core/FastTrackerLinkDef.h index 9885d281a4d..03e3a3c3f86 100644 --- a/ALICE3/Core/FastTrackerLinkDef.h +++ b/ALICE3/Core/FastTrackerLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef ALICE3_CORE_FASTTRACKERLINKDEF_H_ -#define ALICE3_CORE_FASTTRACKERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -19,5 +18,3 @@ #pragma link C++ class o2::fastsim::GeometryContainer + ; #pragma link C++ class o2::fastsim::FastTracker + ; #pragma link C++ class o2::fastsim::FlatLutWriter + ; - -#endif // ALICE3_CORE_FASTTRACKERLINKDEF_H_ From dd8959c7f16b88320455794dfa146f5b46f41909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:45:32 +0200 Subject: [PATCH 108/648] [PWGHF] Fix cpplint errors (#18100) --- PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h index cc755de478f..f5ddd506b55 100644 --- a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h +++ b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h @@ -18,8 +18,7 @@ /// \author Biao Zhang /// \author Oleksii Lubynets -#ifndef PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ -#define PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ +// NOLINT(build/header_guard) #ifdef __CINT__ #pragma link off all globals; @@ -27,5 +26,3 @@ #pragma link off all functions; #pragma link C++ class HFInvMassFitter + ; #endif - -#endif // PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ From db8aaf596fa088a974dd7e22c19bd77a4b00eb50 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Tue, 29 Sep 2026 19:30:37 +0530 Subject: [PATCH 109/648] [PWGCF] updated lambda-proton balance function (#18073) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 826 ++++++++++-------- 1 file changed, 463 insertions(+), 363 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index bb09321931a..a039d4f3da0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -38,8 +38,10 @@ #include #include +#include #include #include +#include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +64,14 @@ using MyTracks = soa::Join; +namespace +{ +float displayDeltaPhi(float dPhi) +{ + return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); +} +} // namespace + // Per-centrality-bin histogram set. Defined at file scope (NOT inside the task struct), // otherwise O2's struct reflection in adaptAnalysisTask fails to compile. struct CentRho2Set { @@ -91,23 +102,9 @@ struct LambdaProtonBalanceFunction { // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin rho2 / mass histograms) - std::vector ptEdges; - - // ════════════════════════════════════════════════════════════════════════ - // SECTION A — Invariant mass histograms // ════════════════════════════════════════════════════════════════════════ - std::vector> hMassLambdaPtBins; - std::vector> hMassAntiLambdaPtBins; - std::shared_ptr hMassLambdaMerged; - std::shared_ptr hMassAntiLambdaMerged; - - // ════════════════════════════════════════════════════════════════════════ - // SECTION A2— Extended invariant-mass histograms (y × pT grid) - // hMassLambdaExtended[iY][iPt] → Lambda_invMassExtended/Lambda/ - // hMassAntiLambdaExtended[iY][iPt] → Lambda_invMassExtended/AntiLambda/ - // Rapidity bins (kExtNyBins = 2): [-0.8,-0.7), [-0.7,-0.6] - // pT bins (kExtNptBins = 25): [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] + // Static compile-time constants (not non-static data members: do not count + // toward the O2 StructToTuple arity). // ════════════════════════════════════════════════════════════════════════ static constexpr int kExtNyBins = 2; static constexpr int kExtNptBins = 25; @@ -118,92 +115,140 @@ struct LambdaProtonBalanceFunction { static constexpr float kExtPtMax = 2.5f; static constexpr float kExtPtStep = 0.1f; - std::vector>> hMassLambdaExtended; - std::vector>> hMassAntiLambdaExtended; - // ════════════════════════════════════════════════════════════════════════ - // SECTION B — rho2 eta-space histograms - // Naming: hRho2_ = per-pT-bin vector - // hRho2__pT015toMaxDefined = merged (full pT range as - // set by the pT cuts: protons 0.5-3.6, Lambdas 0.6-3.6 GeV/c; - // "015" is a legacy name and does NOT reflect the actual lower cut) + // TaskState — ordinary C++ runtime/implementation state. + // Grouped into ONE nested object so that the number of direct data members of + // this task struct stays well below the O2 StructToTuple / structured-binding + // reflection limit. Contains NO O2 Framework objects (no Configurable, + // ConfigurableAxis, Filter, HistogramRegistry): those must remain top-level. // ════════════════════════════════════════════════════════════════════════ - // Lambda–proton family - std::shared_ptr hRho2_Lp_pT015toMaxDefined; - std::shared_ptr hRho2_LAp_pT015toMaxDefined; - std::shared_ptr hRho2_ALp_pT015toMaxDefined; - std::shared_ptr hRho2_ALAp_pT015toMaxDefined; - // Proton–proton family - std::shared_ptr hRho2_pp_pT015toMaxDefined; - std::shared_ptr hRho2_pAp_pT015toMaxDefined; - std::shared_ptr hRho2_App_pT015toMaxDefined; - std::shared_ptr hRho2_ApAp_pT015toMaxDefined; - // Lambda–Lambda family - std::shared_ptr hRho2_LL_pT015toMaxDefined; - std::shared_ptr hRho2_LAL_pT015toMaxDefined; - std::shared_ptr hRho2_ALL_pT015toMaxDefined; - std::shared_ptr hRho2_ALAL_pT015toMaxDefined; + struct TaskState { + // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin rho2 / mass histograms) + std::vector ptEdges; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION A — Invariant mass histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hMassLambdaPtBins; + std::vector> hMassAntiLambdaPtBins; + std::shared_ptr hMassLambdaMerged; + std::shared_ptr hMassAntiLambdaMerged; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION A2— Extended invariant-mass histograms (y × pT grid) + // hMassLambdaExtended[iY][iPt] → Lambda_invMassExtended/Lambda/ + // hMassAntiLambdaExtended[iY][iPt] → Lambda_invMassExtended/AntiLambda/ + // Rapidity bins (kExtNyBins = 2): [-0.8,-0.7), [-0.7,-0.6] + // pT bins (kExtNptBins = 25): [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] + // ════════════════════════════════════════════════════════════════════════ + + std::vector>> hMassLambdaExtended; + std::vector>> hMassAntiLambdaExtended; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION B — rho2 eta-space histograms + // Naming: hRho2_ = per-pT-bin vector + // hRho2__AllPt = merged (full pT range as + // set by the pT cuts: protons 0.5-3.6, Lambdas 0.6-3.6 GeV/c; + // "015" is a legacy name and does NOT reflect the actual lower cut) + // ════════════════════════════════════════════════════════════════════════ + // Lambda–proton family + std::shared_ptr hRho2_Lp_AllPt; + std::shared_ptr hRho2_LAp_AllPt; + std::shared_ptr hRho2_ALp_AllPt; + std::shared_ptr hRho2_ALAp_AllPt; + // Proton–proton family + std::shared_ptr hRho2_pp_AllPt; + std::shared_ptr hRho2_pAp_AllPt; + std::shared_ptr hRho2_App_AllPt; + std::shared_ptr hRho2_ApAp_AllPt; + // Lambda–Lambda family + std::shared_ptr hRho2_LL_AllPt; + std::shared_ptr hRho2_LAL_AllPt; + std::shared_ptr hRho2_ALL_AllPt; + std::shared_ptr hRho2_ALAL_AllPt; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION C — rho2 rapidity-space histograms + // Same channels as Section B but filled with unrolledIndexY(y, phi) + // Naming: hRho2__y / hRho2__y_AllPt + // ════════════════════════════════════════════════════════════════════════ + // Lambda–proton family (y) + std::shared_ptr hRho2_Lp_y_AllPt; + std::shared_ptr hRho2_LAp_y_AllPt; + std::shared_ptr hRho2_ALp_y_AllPt; + std::shared_ptr hRho2_ALAp_y_AllPt; + // Proton–proton family (y) + std::shared_ptr hRho2_pp_y_AllPt; + std::shared_ptr hRho2_pAp_y_AllPt; + std::shared_ptr hRho2_App_y_AllPt; + std::shared_ptr hRho2_ApAp_y_AllPt; + // Lambda–Lambda family (y) + std::shared_ptr hRho2_LL_y_AllPt; + std::shared_ptr hRho2_LAL_y_AllPt; + std::shared_ptr hRho2_ALL_y_AllPt; + std::shared_ptr hRho2_ALAL_y_AllPt; + + // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) + std::vector centSets; + std::vector centEdgesLocal; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION D — pT-spectra and pair-count monitoring histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hPtPrimProton_Lambda; + std::vector> hPtPrimAntiProton_Lambda; + std::vector> hPtPrimProton_AntiLambda; + std::vector> hPtPrimAntiProton_AntiLambda; + + std::shared_ptr hPtSelectedPrimProton_AllPt; + std::shared_ptr hPtSelectedPrimAntiProton_AllPt; + std::shared_ptr hPtSelLambda_AllPt; + std::shared_ptr hPtSelAntiLambda_AllPt; + + // ── PID-check histograms (ProtonCounts_byPID_Check) ─────────────────── + std::shared_ptr h_TPC; + std::shared_ptr h_TPCandTOF; + std::shared_ptr h_TPCorTOF; + // ───────────────────────────────────────────────────────────────────── - // ════════════════════════════════════════════════════════════════════════ - // SECTION C — rho2 rapidity-space histograms - // Same channels as Section B but filled with unrolledIndexY(y, phi) - // Naming: hRho2__y / hRho2__y_pT015toMaxDefined - // ════════════════════════════════════════════════════════════════════════ - // Lambda–proton family (y) - std::shared_ptr hRho2_Lp_y_pT015toMaxDefined; - std::shared_ptr hRho2_LAp_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALp_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALAp_y_pT015toMaxDefined; - // Proton–proton family (y) - std::shared_ptr hRho2_pp_y_pT015toMaxDefined; - std::shared_ptr hRho2_pAp_y_pT015toMaxDefined; - std::shared_ptr hRho2_App_y_pT015toMaxDefined; - std::shared_ptr hRho2_ApAp_y_pT015toMaxDefined; - // Lambda–Lambda family (y) - std::shared_ptr hRho2_LL_y_pT015toMaxDefined; - std::shared_ptr hRho2_LAL_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALL_y_pT015toMaxDefined; - std::shared_ptr hRho2_ALAL_y_pT015toMaxDefined; - - // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) - std::vector centSets; - std::vector centEdgesLocal; + // ── Run-level accumulators for proton-veto importance QA ───────────── + // These are summed over all processed events and printed in endOfStream(). + // They quantify how much Lambda feed-down was removed from the proton + // sample before constructing future Balance Functions. + int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events + int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events + int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto + int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto + + // Runtime copies populated from Configurables in init(). + // Not static, not constexpr — values are known only after O2 delivers the config. + int mRhoEtaBins = 0; + int mRhoPhiBins = 0; + int mRhoYBins = 0; + int mRhoUnrolledBins = 0; // mRhoEtaBins * mRhoPhiBins + int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins + float mRhoEtaMin = 0.f; + float mRhoEtaMax = 0.f; + float mRhoYMin = 0.f; + float mRhoYMax = 0.f; + }; - // ════════════════════════════════════════════════════════════════════════ - // SECTION D — pT-spectra and pair-count monitoring histograms - // ════════════════════════════════════════════════════════════════════════ - std::vector> hPtPrimProton_Lambda; - std::vector> hPtPrimAntiProton_Lambda; - std::vector> hPtPrimProton_AntiLambda; - std::vector> hPtPrimAntiProton_AntiLambda; - - std::shared_ptr hPtSelectedPrimProton_pT015toMaxDefined; - std::shared_ptr hPtSelectedPrimAntiProton_pT015toMaxDefined; - std::shared_ptr hPtSelLambda_pT015toMaxDefined; - std::shared_ptr hPtSelAntiLambda_pT015toMaxDefined; - - // ── PID-check histograms (ProtonCounts_byPID_Check) ─────────────────── - std::shared_ptr h_TPC; - std::shared_ptr h_TPCandTOF; - std::shared_ptr h_TPCorTOF; - // ───────────────────────────────────────────────────────────────────── + TaskState state; - // ── Run-level accumulators for proton-veto importance QA ───────────── - // These are summed over all processed events and printed in endOfStream(). - // They quantify how much Lambda feed-down was removed from the proton - // sample before constructing future Balance Functions. - int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events - int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events - int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto - int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto // ───────────────────────────────────────────────────────────────────── // ── Configurables ───────────────────────────────────────────────────── Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; Configurable cFillEtaSpace{"cFillEtaSpace", false, "Fill eta-space rho1/rho2 histograms"}; - Configurable cUseQinvCut{"cUseQinvCut", true, "Apply q_inv > 0.01 GeV/c pair cut (false = no q_inv rejection)"}; + Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; + Configurable cQinvCutValue{"cQinvCutValue", 0.01f, "q_inv cut value (GeV/c)"}; Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; + Configurable cUseFT0C{"cUseFT0C", false, "Use FT0C instead of FT0M as centrality estimator"}; + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; + Configurable cMinCentrality{"cMinCentrality", 0.0f, "Reject events with centrality below this value (%)"}; + Configurable cMaxCentrality{"cMaxCentrality", 80.0f, "Reject events with centrality above this value (%)"}; // Track quality cuts Configurable pProtonTPCMinRows{"pProtonTPCMinRows", 70, "Minimum TPC crossed rows"}; @@ -263,43 +308,36 @@ struct LambdaProtonBalanceFunction { Configurable cV0DauMinPt{"cV0DauMinPt", 0.1f, "Daughter pT minimum"}; Configurable cV0DauMaxEta{"cV0DauMaxEta", 0.8f, "Daughter |eta| cut"}; Configurable cV0DauMinTpcCrossedRows{"cV0DauMinTpcCrossedRows", 70, "Daughter TPC min crossed rows"}; - // Rapidity-specific constants matched to pProtonMaxY's default value (0.5). - // Note: Since these are compile-time constexpr, the axis will NOT auto-update - // if pProtonMaxY is changed via configurable at runtime. The axis just needs to - // stay >= the cut, not exactly equal. - - // Eta - static constexpr int kRhoEtaBins = 40; - static constexpr float kRhoMin = -0.5f; - static constexpr float kRhoMax = 0.5f; - - // Rapidity - static constexpr int kRhoYBins = 10; - static constexpr float kRhoYMin = -0.5f; - static constexpr float kRhoYMax = 0.5f; - - static constexpr int kRhoPhiBins = 72; - static constexpr int kRhoUnrolledBins = kRhoEtaBins * kRhoPhiBins; - static constexpr int kRhoUnrolledBinsY = kRhoYBins * kRhoPhiBins; + // ── Rho1/Rho2 grid — configurable at runtime (Hyperloop-friendly) ──────── + // These replace the former static constexpr k* constants. Default values + // reproduce the original hardcoded grid (eta: 40×72, y: 10×72). + Configurable cRhoEtaBins{"cRhoEtaBins", 40, "Number of eta bins for rho1/rho2"}; + Configurable cRhoPhiBins{"cRhoPhiBins", 72, "Number of phi bins for rho1/rho2"}; + Configurable cRhoYBins{"cRhoYBins", 10, "Number of y bins for rho1/rho2"}; + Configurable cRhoEtaMin{"cRhoEtaMin", -0.5f, "Min eta for rho1/rho2"}; + Configurable cRhoEtaMax{"cRhoEtaMax", 0.5f, "Max eta for rho1/rho2"}; + Configurable cRhoYMin{"cRhoYMin", -0.5f, "Min y for rho1/rho2"}; + Configurable cRhoYMax{"cRhoYMax", 0.5f, "Max y for rho1/rho2"}; + // ───────────────────────────────────────────────────────────────────────── void buildPtBins() { - ptEdges.clear(); + state.ptEdges.clear(); static constexpr int kNPtEdges = 16; // 0.6, 0.8, ..., 3.6 GeV/c (15 bins) static constexpr float kPtEdgeMin = 0.6f; static constexpr float kPtEdgeStep = 0.2f; for (int i = 0; i < kNPtEdges; ++i) { - ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); + state.ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); } } int etaBinIndex(float eta) const { - if (eta < kRhoMin || eta >= kRhoMax) { + if (eta < state.mRhoEtaMin || eta >= state.mRhoEtaMax) { return -1; } - const float binWidth = (kRhoMax - kRhoMin) / kRhoEtaBins; - return static_cast((eta - kRhoMin) / binWidth); + const float binWidth = (state.mRhoEtaMax - state.mRhoEtaMin) / state.mRhoEtaBins; + return static_cast((eta - state.mRhoEtaMin) / binWidth); } int phiBinIndex(float phi) const @@ -309,7 +347,7 @@ struct LambdaProtonBalanceFunction { if (phi < phiMin || phi >= phiMax) { return -1; } - const float binWidth = (phiMax - phiMin) / kRhoPhiBins; + const float binWidth = (phiMax - phiMin) / state.mRhoPhiBins; return static_cast((phi - phiMin) / binWidth); } @@ -320,16 +358,16 @@ struct LambdaProtonBalanceFunction { if (iEta < 0 || iPhi < 0) { return -1; } - return iEta * kRhoPhiBins + iPhi; + return iEta * state.mRhoPhiBins + iPhi; } int yBinIndex(float y) const { - if (y < kRhoYMin || y >= kRhoYMax) { + if (y < state.mRhoYMin || y >= state.mRhoYMax) { return -1; } - const float binWidth = (kRhoYMax - kRhoYMin) / kRhoYBins; - return static_cast((y - kRhoYMin) / binWidth); + const float binWidth = (state.mRhoYMax - state.mRhoYMin) / state.mRhoYBins; + return static_cast((y - state.mRhoYMin) / binWidth); } int unrolledIndexY(float y, float phi) const @@ -339,27 +377,19 @@ struct LambdaProtonBalanceFunction { if (iY < 0 || iPhi < 0) { return -1; } - return iY * kRhoPhiBins + iPhi; + return iY * state.mRhoPhiBins + iPhi; } // Convenience wrappers with particle-specific masses static constexpr float kMassProton = o2::constants::physics::MassProton; static constexpr float kMassLambda = o2::constants::physics::MassLambda; - // Femtoscopic q_inv cut: pairs with q_inv <= kQinvCutLP are rejected from all rho2 histograms (pp, pAp, App, ApAp, LP-family, LL-family): - static constexpr float kQinvCutLP = 0.01f; - template float protonRapidity(T const& track) const { return RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, kMassProton); } - float displayDeltaPhi(float dPhi) const - { - return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); - } - // k* = |p*| of one particle in the pair rest frame, from four-momenta. float computeKstar(float px1, float py1, float pz1, float m1, float px2, float py2, float pz2, float m2) const @@ -401,14 +431,22 @@ struct LambdaProtonBalanceFunction { // true => pair is rejected by the q_inv cut (never true when the cut is switched off) bool failsQinvCut(float qinv) const { - return cUseQinvCut.value && (qinv <= kQinvCutLP); + return cUseQinvCut.value && (qinv <= cQinvCutValue.value); + } + + // Returns the active centrality estimator's percentile for this collision, + // selected at runtime via cUseFT0C (false = FT0M, true = FT0C). + template + float getCentrality(TCollision const& col) const + { + return cUseFT0C.value ? col.centFT0C() : col.centFT0M(); } // Returns index of centrality bin for cent, or -1 if outside all bins. int centBinIndex(float cent) const { - for (size_t i = 0; i + 1 < centEdgesLocal.size(); ++i) { - if (cent >= centEdgesLocal[i] && cent < centEdgesLocal[i + 1]) { + for (size_t i = 0; i + 1 < state.centEdgesLocal.size(); ++i) { + if (cent >= state.centEdgesLocal[i] && cent < state.centEdgesLocal[i + 1]) { return static_cast(i); } } @@ -1128,11 +1166,11 @@ struct LambdaProtonBalanceFunction { // Per-particle cache used by the pair loops (avoids recomputing per pair). struct PartInfo { - int64_t gid; - float px, py, pz, E; - float eta, phi, y, pt; - int idxEta; - int idxY; + int64_t gid = 0; + float px = 0.f, py = 0.f, pz = 0.f, E = 0.f; + float eta = 0.f, phi = 0.f, y = 0.f, pt = 0.f; + int idxEta = -1; + int idxY = -1; }; template @@ -1155,6 +1193,18 @@ struct LambdaProtonBalanceFunction { void init(InitContext const&) { + // ── Populate rho1/rho2 grid members from Configurables ────────────────── + state.mRhoEtaBins = cRhoEtaBins.value; + state.mRhoPhiBins = cRhoPhiBins.value; + state.mRhoYBins = cRhoYBins.value; + state.mRhoEtaMin = cRhoEtaMin.value; + state.mRhoEtaMax = cRhoEtaMax.value; + state.mRhoYMin = cRhoYMin.value; + state.mRhoYMax = cRhoYMax.value; + state.mRhoUnrolledBins = state.mRhoEtaBins * state.mRhoPhiBins; + state.mRhoUnrolledBinsY = state.mRhoYBins * state.mRhoPhiBins; + // ──────────────────────────────────────────────────────────────────────── + buildPtBins(); // ── Common axis definitions ────────────────────────────────────────── @@ -1175,13 +1225,15 @@ struct LambdaProtonBalanceFunction { AxisSpec dcaAxis = {100, -1.0f, 1.0f, "DCAxy (cm)"}; AxisSpec dcaZAxis = {100, -1.0f, 1.0f, "DCAz (cm)"}; AxisSpec dcaAxisWide = {200, 0.0f, 5.0f, "DCA (cm)"}; - AxisSpec multAxis = {100, 0.0f, 100.0f, "FT0M Percentile (%)"}; - AxisSpec etaAxis = {kRhoEtaBins, kRhoMin, kRhoMax, "#eta"}; - AxisSpec phiAxis = {kRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; - AxisSpec unrolledAxis = {kRhoUnrolledBins, 0.0f, static_cast(kRhoUnrolledBins), "index(#eta,#varphi)"}; - AxisSpec unrolledAxisY = {kRhoUnrolledBinsY, 0.0f, static_cast(kRhoUnrolledBinsY), "index(y,#varphi)"}; - AxisSpec rapidityAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; - AxisSpec yAxis = {kRhoYBins, kRhoYMin, kRhoYMax, "y"}; + const std::string centLabel = cUseFT0C.value ? "FT0C" : "FT0M"; + const std::string centAxisTitle = centLabel + " Percentile (%)"; + AxisSpec multAxis = {100, 0.0f, 100.0f, centAxisTitle}; + AxisSpec etaAxis = {state.mRhoEtaBins, state.mRhoEtaMin, state.mRhoEtaMax, "#eta"}; + AxisSpec phiAxis = {state.mRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; + AxisSpec unrolledAxis = {state.mRhoUnrolledBins, 0.0f, static_cast(state.mRhoUnrolledBins), "index(#eta,#varphi)"}; + AxisSpec unrolledAxisY = {state.mRhoUnrolledBinsY, 0.0f, static_cast(state.mRhoUnrolledBinsY), "index(y,#varphi)"}; + AxisSpec rapidityAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; + AxisSpec yAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; // pT axis for ProtonCounts_byPID_Check: 0.15 → 6.0, 118 bins of 0.05 width AxisSpec pidCheckPtAxis = {118, 0.15f, 6.05f, "#it{p}_{T} (GeV/#it{c})"}; @@ -1189,7 +1241,7 @@ struct LambdaProtonBalanceFunction { // ── Other / event-level histograms ─────────────────────────────────── registryOther.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); - registryOther.add("hFT0MPercentile", "hFT0MPercentile", {HistType::kTH1F, {multAxis}}); + registryOther.add("hFT0MPercentile", ("Centrality percentile (" + centLabel + ")").c_str(), {HistType::kTH1F, {multAxis}}); registryOther.add("hNSelectedPrimProtons", "Number of selected primary protons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelectedPrimAntiProtons", "Number of selected primary antiprotons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelLambda", "Number of selected Lambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); @@ -1391,7 +1443,7 @@ struct LambdaProtonBalanceFunction { auto hEC = registryOther.get(HIST("hEventCutflow")); hEC->GetXaxis()->SetBinLabel(1, "sel8 + |z|GetXaxis()->SetBinLabel(2, "numContrib >= 1"); - hEC->GetXaxis()->SetBinLabel(3, "centFT0M < 80"); + hEC->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f]", cUseFT0C.value ? "centFT0C" : "centFT0M", cMinCentrality.value, cMaxCentrality.value)); hEC->GetXaxis()->SetBinLabel(4, "INEL > 0"); } @@ -1443,10 +1495,10 @@ struct LambdaProtonBalanceFunction { hPU->GetXaxis()->SetBinLabel(6, "NoSBPU&&GoodZvtx&&ITSTPC"); } registryOther.add("h2f_NTracks_vs_Cent", - "N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs FT0M cent;FT0M (%);N_{tracks}(|#eta|<0.8)", + ("N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs " + centLabel + ";" + centLabel + " (%);N_{tracks}(|#eta|<0.8)").c_str(), {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{tracks}"}}}); registryOther.add("h2f_NumContrib_vs_Cent", - "N_{contribs} vs FT0M percentile;FT0M (%);N_{contribs}", + ("N_{contribs} vs " + centLabel + " percentile;" + centLabel + " (%);N_{contribs}").c_str(), {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{contribs}"}}}); // ───────────────────────────────────────────────────────────────────── @@ -1616,7 +1668,7 @@ struct LambdaProtonBalanceFunction { // ── QA3: SplitTrackQA — q_inv and shared-cluster fraction ───────────── // "Before_qinvCut": filled for every accepted pair, prior to the q_inv - // cut (kQinvCutLP), so they show the complete original q_inv distributions. + // cut (kQinvCut), so they show the complete original q_inv distributions. registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut", "Split-track QA: q_{inv} for p-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", @@ -1630,7 +1682,7 @@ struct LambdaProtonBalanceFunction { "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); // "After_qinvCut": filled only for pairs surviving the q_inv cut - // (q_inv > kQinvCutLP), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). + // (q_inv > kQinvCut), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut", "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", @@ -1668,10 +1720,14 @@ struct LambdaProtonBalanceFunction { "QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut", "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); - registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs (after q_{inv} cut);k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut", "k* of #Lambda-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut", "k* of #Lambda-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut", "k* of #bar{#Lambda}-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut", "k* of #bar{#Lambda}-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_sharedClsFraction_t1", "Split-track QA: TPC shared-cluster fraction, track 1 (p-p: first proton; #Lambda-p: V0 daughter) in close pairs;Fraction;Counts", @@ -1680,171 +1736,175 @@ struct LambdaProtonBalanceFunction { "QA3/SplitTrackQA/h1f_sharedClsFraction_t2", "Split-track QA: TPC shared-cluster fraction, track 2 (p-p: second proton; #Lambda-p: primary (anti)proton) in close pairs;Fraction;Counts", {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); + registryCorrelationQA.add( + "h2_qinv_dPhi_Lp", + "Lambda-Proton pairs;#Delta#varphi;q_{inv} (GeV/c)", + {HistType::kTH2F, {{72, -static_cast(TMath::Pi()), static_cast(TMath::Pi())}, {100, 0.0f, 0.10f}}}); // ───────────────────────────────────────────────────────────────────── // ── ProtonCounts_byPID_Check histograms (protons only, sign-guarded in process) ── - h_TPC = + state.h_TPC = registryProtonPidCheck.add("h_TPC", "Proton candidates: TPC PID only (no TOF condition);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); - h_TPCandTOF = + state.h_TPCandTOF = registryProtonPidCheck.add("h_TPCandTOF", "Proton candidates: TPC AND TOF both required;#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); - h_TPCorTOF = + state.h_TPCorTOF = registryProtonPidCheck.add("h_TPCorTOF", "Proton candidates: TPC required; if TOF present at any p, TOF n#sigma also required (differs from final selection, which requires TOF only above p_{TPC} switch);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {pidCheckPtAxis}}); // ───────────────────────────────────────────────────────────────────── - // ── Merged / pT015toMaxDefined histograms ─────────────────────────────────── - hRho2_Lp_pT015toMaxDefined = - registryRho.add("h2_rho2_Lp_pT015toMaxDefined", + // ── Merged / AllPt histograms ─────────────────────────────────── + state.hRho2_Lp_AllPt = + registryRho.add("h2_rho2_Lp_AllPt", "#rho_{2}(#Lambda, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LAp_pT015toMaxDefined = - registryRho.add("h2_rho2_LAp_pT015toMaxDefined", + state.hRho2_LAp_AllPt = + registryRho.add("h2_rho2_LAp_AllPt", "#rho_{2}(#Lambda, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALp_pT015toMaxDefined = - registryRho.add("h2_rho2_ALp_pT015toMaxDefined", + state.hRho2_ALp_AllPt = + registryRho.add("h2_rho2_ALp_AllPt", "#rho_{2}(#bar{#Lambda}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALAp_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAp_pT015toMaxDefined", + state.hRho2_ALAp_AllPt = + registryRho.add("h2_rho2_ALAp_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_pp_pT015toMaxDefined = - registryRho.add("h2_rho2_pp_pT015toMaxDefined", + state.hRho2_pp_AllPt = + registryRho.add("h2_rho2_pp_AllPt", "#rho_{2}(p, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_pAp_pT015toMaxDefined = - registryRho.add("h2_rho2_pAp_pT015toMaxDefined", + state.hRho2_pAp_AllPt = + registryRho.add("h2_rho2_pAp_AllPt", "#rho_{2}(p, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_App_pT015toMaxDefined = - registryRho.add("h2_rho2_App_pT015toMaxDefined", + state.hRho2_App_AllPt = + registryRho.add("h2_rho2_App_AllPt", "#rho_{2}(#bar{p}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ApAp_pT015toMaxDefined = - registryRho.add("h2_rho2_ApAp_pT015toMaxDefined", + state.hRho2_ApAp_AllPt = + registryRho.add("h2_rho2_ApAp_AllPt", "#rho_{2}(#bar{p}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LL_pT015toMaxDefined = - registryRho.add("h2_rho2_LL_pT015toMaxDefined", + state.hRho2_LL_AllPt = + registryRho.add("h2_rho2_LL_AllPt", "#rho_{2}(#Lambda, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_LAL_pT015toMaxDefined = - registryRho.add("h2_rho2_LAL_pT015toMaxDefined", + state.hRho2_LAL_AllPt = + registryRho.add("h2_rho2_LAL_AllPt", "#rho_{2}(#Lambda, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALL_pT015toMaxDefined = - registryRho.add("h2_rho2_ALL_pT015toMaxDefined", + state.hRho2_ALL_AllPt = + registryRho.add("h2_rho2_ALL_AllPt", "#rho_{2}(#bar{#Lambda}, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - hRho2_ALAL_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAL_pT015toMaxDefined", + state.hRho2_ALAL_AllPt = + registryRho.add("h2_rho2_ALAL_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); // ── Rapidity merged rho2 histograms ───────────────────────────────────── - // NOTE: these must use unrolledAxisY (kRhoYBins*kRhoPhiBins bins), - // NOT the eta-space unrolledAxis (kRhoEtaBins*kRhoPhiBins bins). - // GetUnrolledIndexY() below produces indices in [0, kRhoUnrolledBinsY), + // NOTE: these must use unrolledAxisY (mRhoYBins*mRhoPhiBins bins), + // NOT the eta-space unrolledAxis (mRhoEtaBins*mRhoPhiBins bins). + // GetUnrolledIndexY() below produces indices in [0, mRhoUnrolledBinsY), // so the booked axis must match that range or downstream R2/BF code that // expects y-space-sized histograms will see a dimension mismatch. - hRho2_Lp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_pp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_pAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_App_y_pT015toMaxDefined = - registryRho.add("h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ApAp_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_LAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); - hRho2_ALAL_y_pT015toMaxDefined = - registryRho.add("h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_Lp_y_AllPt = + registryRho.add("h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAp_y_AllPt = + registryRho.add("h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALp_y_AllPt = + registryRho.add("h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAp_y_AllPt = + registryRho.add("h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pp_y_AllPt = + registryRho.add("h2_rho2_pp_y_AllPt", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pAp_y_AllPt = + registryRho.add("h2_rho2_pAp_y_AllPt", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_App_y_AllPt = + registryRho.add("h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ApAp_y_AllPt = + registryRho.add("h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LL_y_AllPt = + registryRho.add("h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAL_y_AllPt = + registryRho.add("h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALL_y_AllPt = + registryRho.add("h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAL_y_AllPt = + registryRho.add("h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); // ───────────────────────────────────────────────────────────────────── - hPtSelectedPrimProton_pT015toMaxDefined = - registryOther.add("hPtSelectedPrimProton_pT015toMaxDefined", + state.hPtSelectedPrimProton_AllPt = + registryOther.add("hPtSelectedPrimProton_AllPt", "Selected primary proton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelectedPrimAntiProton_pT015toMaxDefined = - registryOther.add("hPtSelectedPrimAntiProton_pT015toMaxDefined", + state.hPtSelectedPrimAntiProton_AllPt = + registryOther.add("hPtSelectedPrimAntiProton_AllPt", "Selected primary antiproton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelLambda_pT015toMaxDefined = - registryOther.add("hPtSelLambda_pT015toMaxDefined", + state.hPtSelLambda_AllPt = + registryOther.add("hPtSelLambda_AllPt", "Selected #Lambda p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hPtSelAntiLambda_pT015toMaxDefined = - registryOther.add("hPtSelAntiLambda_pT015toMaxDefined", + state.hPtSelAntiLambda_AllPt = + registryOther.add("hPtSelAntiLambda_AllPt", "Selected #bar{#Lambda} p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); - hMassLambdaMerged = - registryLambda.add("hMassLambdaNoMassCut_pT015toMaxDefined", + state.hMassLambdaMerged = + registryLambda.add("hMassLambdaNoMassCut_AllPt", "#Lambda mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); - hMassAntiLambdaMerged = - registryLambda.add("hMassAntiLambdaNoMassCut_pT015toMaxDefined", + state.hMassAntiLambdaMerged = + registryLambda.add("hMassAntiLambdaNoMassCut_AllPt", "#bar{#Lambda} mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", {HistType::kTH1F, {lambdaMassAxis}}); // ───────────────────────────────────────────────────────────────────── // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 6.0) ───────────────── - hMassLambdaPtBins.clear(); - hMassAntiLambdaPtBins.clear(); - hPtPrimProton_Lambda.clear(); - hPtPrimAntiProton_Lambda.clear(); - hPtPrimProton_AntiLambda.clear(); - hPtPrimAntiProton_AntiLambda.clear(); - - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - const float ptLo = ptEdges[i]; - const float ptHi = ptEdges[i + 1]; - - hPtPrimProton_Lambda.push_back( + state.hMassLambdaPtBins.clear(); + state.hMassAntiLambdaPtBins.clear(); + state.hPtPrimProton_Lambda.clear(); + state.hPtPrimAntiProton_Lambda.clear(); + state.hPtPrimProton_AntiLambda.clear(); + state.hPtPrimAntiProton_AntiLambda.clear(); + + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + const float ptLo = state.ptEdges[i]; + const float ptHi = state.ptEdges[i + 1]; + + state.hPtPrimProton_Lambda.push_back( registryOther.add( Form("hPtPrimProton_Lambda_ptBin%02zu", i), Form("Proton #it{p}_{T} in #Lambda-p pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimAntiProton_Lambda.push_back( + state.hPtPrimAntiProton_Lambda.push_back( registryOther.add( Form("hPtPrimAntiProton_Lambda_ptBin%02zu", i), Form("Antiproton #it{p}_{T} in #Lambda-#bar{p} pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimProton_AntiLambda.push_back( + state.hPtPrimProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimProton_AntiLambda_ptBin%02zu", i), Form("Proton #it{p}_{T} in #bar{#Lambda}-p pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hPtPrimAntiProton_AntiLambda.push_back( + state.hPtPrimAntiProton_AntiLambda.push_back( registryOther.add( Form("hPtPrimAntiProton_AntiLambda_ptBin%02zu", i), Form("Antiproton #it{p}_{T} in #bar{#Lambda}-#bar{p} pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), {HistType::kTH1F, {ptAxis}})); - hMassLambdaPtBins.push_back( + state.hMassLambdaPtBins.push_back( registryLambda.add( Form("hMassLambdaNoMassCut_ptBin%02zu", i), Form("#Lambda mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), {HistType::kTH1F, {lambdaMassAxis}})); - hMassAntiLambdaPtBins.push_back( + state.hMassAntiLambdaPtBins.push_back( registryLambda.add( Form("hMassAntiLambdaNoMassCut_ptBin%02zu", i), Form("#bar{#Lambda} mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), @@ -1858,10 +1918,10 @@ struct LambdaProtonBalanceFunction { // Folder structure: // Lambda_invMassExtended/Lambda/hMassLambda_yBinXX_ptBinYY // Lambda_invMassExtended/AntiLambda/hMassAntiLambda_yBinXX_ptBinYY - hMassLambdaExtended.clear(); - hMassAntiLambdaExtended.clear(); - hMassLambdaExtended.resize(kExtNyBins); - hMassAntiLambdaExtended.resize(kExtNyBins); + state.hMassLambdaExtended.clear(); + state.hMassAntiLambdaExtended.clear(); + state.hMassLambdaExtended.resize(kExtNyBins); + state.hMassAntiLambdaExtended.resize(kExtNyBins); for (int iY = 0; iY < kExtNyBins; ++iY) { const float yLow = kExtYMin + iY * kExtYStep; @@ -1870,8 +1930,8 @@ struct LambdaProtonBalanceFunction { const bool lastYBin = (iY == kExtNyBins - 1); const char* yUpBracket = lastYBin ? "<=" : "<"; - hMassLambdaExtended[iY].resize(kExtNptBins); - hMassAntiLambdaExtended[iY].resize(kExtNptBins); + state.hMassLambdaExtended[iY].resize(kExtNptBins); + state.hMassAntiLambdaExtended[iY].resize(kExtNptBins); for (int iPt = 0; iPt < kExtNptBins; ++iPt) { const float ptLow = kExtPtMin + iPt * kExtPtStep; @@ -1881,7 +1941,7 @@ struct LambdaProtonBalanceFunction { const char* ptUpBracket = lastPtBin ? "<=" : "<"; // Lambda - hMassLambdaExtended[iY][iPt] = + state.hMassLambdaExtended[iY][iPt] = registryLambdaExtended.add( Form("Lambda/hMassLambda_yBin%02d_ptBin%02d", iY, iPt), Form("#Lambda mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", @@ -1889,7 +1949,7 @@ struct LambdaProtonBalanceFunction { {HistType::kTH1F, {lambdaMassAxis}}); // AntiLambda - hMassAntiLambdaExtended[iY][iPt] = + state.hMassAntiLambdaExtended[iY][iPt] = registryLambdaExtended.add( Form("AntiLambda/hMassAntiLambda_yBin%02d_ptBin%02d", iY, iPt), Form("#bar{#Lambda} mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", @@ -1902,18 +1962,38 @@ struct LambdaProtonBalanceFunction { // ══════════════════════════════════════════════════════════════════════ // Centrality-differential histograms → top-level folder "rho1ANDrho2_LP_CentralityBased" // ══════════════════════════════════════════════════════════════════════ - centSets.clear(); - centEdgesLocal = {0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}; + state.centSets.clear(); + // Single source of truth: centrality class edges come from the cCentBins ConfigurableAxis. + // Edges are read back from a temporary ROOT histogram built from the AxisSpec. + { + const AxisSpec centCfgAxis(cCentBins, "centrality"); + HistogramRegistry tmpReg{"tmpCentEdges", {}, OutputObjHandlingPolicy::AnalysisObject}; + auto hTmp = tmpReg.add("hTmp", "", {HistType::kTH1F, {centCfgAxis}}); + state.centEdgesLocal.clear(); + const TAxis* ax = hTmp->GetXaxis(); + for (int i = 1; i <= ax->GetNbins(); ++i) { + state.centEdgesLocal.push_back(static_cast(ax->GetBinLowEdge(i))); + } + state.centEdgesLocal.push_back(static_cast(ax->GetBinUpEdge(ax->GetNbins()))); + } + if (state.centEdgesLocal.size() < 2) { + LOGF(fatal, "cCentBins needs at least 2 edges"); + } + for (const float e : state.centEdgesLocal) { + if (e != std::floor(e)) { + LOGF(fatal, "cCentBins edges must be integers (folder names use integer edges)"); + } + } if (cFillCentHists.value) { - const size_t nCent = centEdgesLocal.size() - 1; - centSets.resize(nCent); + const size_t nCent = state.centEdgesLocal.size() - 1; + state.centSets.resize(nCent); for (size_t ic = 0; ic < nCent; ++ic) { - const float cLo = centEdgesLocal[ic]; - const float cHi = centEdgesLocal[ic + 1]; + const float cLo = state.centEdgesLocal[ic]; + const float cHi = state.centEdgesLocal[ic + 1]; const std::string dir = Form("Cent%02d_%02d", static_cast(cLo), static_cast(cHi)); - const std::string ctag = Form("FT0M %.0f-%.0f%%", cLo, cHi); - auto& S = centSets[ic]; + const std::string ctag = Form("%s %.0f-%.0f%%", cUseFT0C.value ? "FT0C" : "FT0M", cLo, cHi); + auto& S = state.centSets[ic]; S.hEvents = registryCent.add((dir + "/hEventCounter").c_str(), ("Events, " + ctag).c_str(), @@ -1929,18 +2009,18 @@ struct LambdaProtonBalanceFunction { auto addY = [&](std::shared_ptr& h, const char* name, const char* title) { h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); }; - addY(S.Lp_y, "h2_rho2_Lp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (y,#varphi)"); - addY(S.LAp_y, "h2_rho2_LAp_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); - addY(S.ALp_y, "h2_rho2_ALp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); - addY(S.ALAp_y, "h2_rho2_ALAp_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); - addY(S.pp_y, "h2_rho2_pp_y_pT015toMaxDefined", "#rho_{2}(p,p) (y,#varphi)"); - addY(S.pAp_y, "h2_rho2_pAp_y_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (y,#varphi)"); - addY(S.App_y, "h2_rho2_App_y_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (y,#varphi)"); - addY(S.ApAp_y, "h2_rho2_ApAp_y_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); - addY(S.LL_y, "h2_rho2_LL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); - addY(S.LAL_y, "h2_rho2_LAL_y_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); - addY(S.ALL_y, "h2_rho2_ALL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); - addY(S.ALAL_y, "h2_rho2_ALAL_y_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); + addY(S.Lp_y, "h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda,p) (y,#varphi)"); + addY(S.LAp_y, "h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); + addY(S.ALp_y, "h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); + addY(S.ALAp_y, "h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); + addY(S.pp_y, "h2_rho2_pp_y_AllPt", "#rho_{2}(p,p) (y,#varphi)"); + addY(S.pAp_y, "h2_rho2_pAp_y_AllPt", "#rho_{2}(p,#bar{p}) (y,#varphi)"); + addY(S.App_y, "h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p},p) (y,#varphi)"); + addY(S.ApAp_y, "h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); + addY(S.LL_y, "h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); + addY(S.LAL_y, "h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); + addY(S.ALL_y, "h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); + addY(S.ALAL_y, "h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); // ── eta space (only if BOTH switches on: memory heavy) ── if (cFillEtaSpace.value) { @@ -1952,18 +2032,18 @@ struct LambdaProtonBalanceFunction { auto addEta = [&](std::shared_ptr& h, const char* name, const char* title) { h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); }; - addEta(S.Lp, "h2_rho2_Lp_pT015toMaxDefined", "#rho_{2}(#Lambda,p) (#eta,#varphi)"); - addEta(S.LAp, "h2_rho2_LAp_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{p}) (#eta,#varphi)"); - addEta(S.ALp, "h2_rho2_ALp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},p) (#eta,#varphi)"); - addEta(S.ALAp, "h2_rho2_ALAp_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{p}) (#eta,#varphi)"); - addEta(S.pp, "h2_rho2_pp_pT015toMaxDefined", "#rho_{2}(p,p) (#eta,#varphi)"); - addEta(S.pAp, "h2_rho2_pAp_pT015toMaxDefined", "#rho_{2}(p,#bar{p}) (#eta,#varphi)"); - addEta(S.App, "h2_rho2_App_pT015toMaxDefined", "#rho_{2}(#bar{p},p) (#eta,#varphi)"); - addEta(S.ApAp, "h2_rho2_ApAp_pT015toMaxDefined", "#rho_{2}(#bar{p},#bar{p}) (#eta,#varphi)"); - addEta(S.LL, "h2_rho2_LL_pT015toMaxDefined", "#rho_{2}(#Lambda,#Lambda) (#eta,#varphi)"); - addEta(S.LAL, "h2_rho2_LAL_pT015toMaxDefined", "#rho_{2}(#Lambda,#bar{#Lambda}) (#eta,#varphi)"); - addEta(S.ALL, "h2_rho2_ALL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#Lambda) (#eta,#varphi)"); - addEta(S.ALAL, "h2_rho2_ALAL_pT015toMaxDefined", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (#eta,#varphi)"); + addEta(S.Lp, "h2_rho2_Lp_AllPt", "#rho_{2}(#Lambda,p) (#eta,#varphi)"); + addEta(S.LAp, "h2_rho2_LAp_AllPt", "#rho_{2}(#Lambda,#bar{p}) (#eta,#varphi)"); + addEta(S.ALp, "h2_rho2_ALp_AllPt", "#rho_{2}(#bar{#Lambda},p) (#eta,#varphi)"); + addEta(S.ALAp, "h2_rho2_ALAp_AllPt", "#rho_{2}(#bar{#Lambda},#bar{p}) (#eta,#varphi)"); + addEta(S.pp, "h2_rho2_pp_AllPt", "#rho_{2}(p,p) (#eta,#varphi)"); + addEta(S.pAp, "h2_rho2_pAp_AllPt", "#rho_{2}(p,#bar{p}) (#eta,#varphi)"); + addEta(S.App, "h2_rho2_App_AllPt", "#rho_{2}(#bar{p},p) (#eta,#varphi)"); + addEta(S.ApAp, "h2_rho2_ApAp_AllPt", "#rho_{2}(#bar{p},#bar{p}) (#eta,#varphi)"); + addEta(S.LL, "h2_rho2_LL_AllPt", "#rho_{2}(#Lambda,#Lambda) (#eta,#varphi)"); + addEta(S.LAL, "h2_rho2_LAL_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (#eta,#varphi)"); + addEta(S.ALL, "h2_rho2_ALL_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (#eta,#varphi)"); + addEta(S.ALAL, "h2_rho2_ALAL_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (#eta,#varphi)"); } } } @@ -1992,7 +2072,7 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - void process(soa::Filtered>::iterator const& collision, + void process(soa::Filtered>::iterator const& collision, aod::V0Datas const& V0s, MyTracks const& tracks) { @@ -2021,19 +2101,20 @@ struct LambdaProtonBalanceFunction { // ── Event cutflow bin 2 — after numContrib >= 1 ────────────────────── registryOther.fill(HIST("hEventCutflow"), 2.0f); - static constexpr float kMaxCentFT0M = 80.0f; - if (collision.centFT0M() > kMaxCentFT0M) { + const float cent = getCentrality(collision); + if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { return; } - // ── Event cutflow bin 3 — after centFT0M < 80 ──────────────────────── + // ── Event cutflow bin 3 — after centrality in [cMinCentrality, cMaxCentrality) ── registryOther.fill(HIST("hEventCutflow"), 3.0f); // INEL > 0: require at least one charged track in |eta| < 0.8 - int nTracksINEL = 0; static constexpr float kMaxInelEta = 0.8f; + int nTracksINEL = 0; for (auto const& trk : tracks) { if (std::abs(trk.eta()) < kMaxInelEta) { ++nTracksINEL; + continue; } } if (nTracksINEL < 1) { @@ -2047,15 +2128,15 @@ struct LambdaProtonBalanceFunction { // ── Centrality bin for the centrality-differential histograms ───────── CentRho2Set* cs = nullptr; if (cFillCentHists.value) { - const int iCent = centBinIndex(collision.centFT0M()); - if (iCent >= 0 && iCent < static_cast(centSets.size())) { - cs = ¢Sets[iCent]; + const int iCent = centBinIndex(cent); + if (iCent >= 0 && iCent < static_cast(state.centSets.size())) { + cs = &state.centSets[iCent]; cs->hEvents->Fill(0.5); } } registryOther.fill(HIST("hVertexZRec"), collision.posZ()); - registryOther.fill(HIST("hFT0MPercentile"), collision.centFT0M()); + registryOther.fill(HIST("hFT0MPercentile"), cent); // ── Item 5: Event-level pileup rejection metrics ────────────────────── // Fill hPileupFlags: one bin per evsel bit, filled if the bit is set. @@ -2079,12 +2160,9 @@ struct LambdaProtonBalanceFunction { collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { registryOther.fill(HIST("hPileupFlags"), 6.0f); } - // N_{tracks} and N_{contribs} vs FT0M centrality — filled once per event - { - const float cent = collision.centFT0M(); - registryOther.fill(HIST("h2f_NTracks_vs_Cent"), cent, static_cast(nTracksINEL)); - registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); - } + // N_{tracks} and N_{contribs} vs centrality — filled once per event + registryOther.fill(HIST("h2f_NTracks_vs_Cent"), cent, static_cast(nTracksINEL)); + registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); // ───────────────────────────────────────────────────────────────────── // ====== Step 1: Initialise containers ====================================== @@ -2270,8 +2348,8 @@ struct LambdaProtonBalanceFunction { // const float pz = pt * std::sinh(eta); const float y = v0DauPID_AsLambda ? v0.rapidity(1) : v0.rapidity(2); int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { bin = static_cast(i); break; } @@ -2283,9 +2361,9 @@ struct LambdaProtonBalanceFunction { // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. if (v0DauPID_AsLambda) { if (bin >= 0) { - hMassLambdaPtBins[bin]->Fill(v0.mLambda()); + state.hMassLambdaPtBins[bin]->Fill(v0.mLambda()); } - hMassLambdaMerged->Fill(v0.mLambda()); + state.hMassLambdaMerged->Fill(v0.mLambda()); registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); // ── Item 2: mass vs eta/phi acceptance uniformity ── registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); @@ -2311,16 +2389,16 @@ struct LambdaProtonBalanceFunction { iPtext = kExtNptBins - 1; } // clamp upper edge of last bin if (iYext >= 0 && iPtext >= 0) { - hMassLambdaExtended[iYext][iPtext]->Fill(v0.mLambda()); + state.hMassLambdaExtended[iYext][iPtext]->Fill(v0.mLambda()); } } // ───────────────────────────────────────────────────────────────── } if (v0DauPID_AsAntiLambda) { if (bin >= 0) { - hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); + state.hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); } - hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); + state.hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); // ── Item 2: mass vs eta/phi acceptance uniformity ── registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); @@ -2344,7 +2422,7 @@ struct LambdaProtonBalanceFunction { iPtext = kExtNptBins - 1; } // clamp upper edge of last bin if (iYext >= 0 && iPtext >= 0) { - hMassAntiLambdaExtended[iYext][iPtext]->Fill(v0.mAntiLambda()); + state.hMassAntiLambdaExtended[iYext][iPtext]->Fill(v0.mAntiLambda()); } } // ───────────────────────────────────────────────────────────────── @@ -2399,7 +2477,7 @@ struct LambdaProtonBalanceFunction { } } registryOther.fill(HIST("hPtSelLambda"), pt); - hPtSelLambda_pT015toMaxDefined->Fill(pt); + state.hPtSelLambda_AllPt->Fill(pt); } if (selAntiLambda) { @@ -2423,7 +2501,7 @@ struct LambdaProtonBalanceFunction { } } registryOther.fill(HIST("hPtSelAntiLambda"), pt); - hPtSelAntiLambda_pT015toMaxDefined->Fill(pt); + state.hPtSelAntiLambda_AllPt->Fill(pt); } // ───────────────────────────────────────────────────────────────── } @@ -2523,15 +2601,15 @@ struct LambdaProtonBalanceFunction { const float nsTPC = std::abs(trk.tpcNSigmaPr()); if (nsTPC < pProtonTPCNsigma.value) { - h_TPC->Fill(pt); + state.h_TPC->Fill(pt); } if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - h_TPCandTOF->Fill(pt); + state.h_TPCandTOF->Fill(pt); } if (nsTPC < pProtonTPCNsigma.value) { if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - h_TPCorTOF->Fill(pt); + state.h_TPCorTOF->Fill(pt); } } } @@ -2650,7 +2728,7 @@ struct LambdaProtonBalanceFunction { } registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); - hPtSelectedPrimProton_pT015toMaxDefined->Fill(trk.pt()); + state.hPtSelectedPrimProton_AllPt->Fill(trk.pt()); } else { selectedPrimAntiProtons.push_back(trk); fillProtonKinematics(ProtonQAStage::FinalSelected, trk); @@ -2663,7 +2741,7 @@ struct LambdaProtonBalanceFunction { } registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); - hPtSelectedPrimAntiProton_pT015toMaxDefined->Fill(trk.pt()); + state.hPtSelectedPrimAntiProton_AllPt->Fill(trk.pt()); } // ───────────────────────────────────────────────────────────────── } @@ -2696,10 +2774,10 @@ struct LambdaProtonBalanceFunction { registryProtonVetoQA.fill(HIST("h1f_nAfterVeto"), static_cast(nAfter)); registryProtonVetoQA.fill(HIST("h1f_fractionRemoved"), fraction); // Accumulate run-level totals for endOfStream() summary. - runTotalProtonBeforeVeto += nBefore; - runTotalProtonRemovedVeto += nRemoved; - runTotalAntiPBeforeVeto += nAntiPBeforeVeto; - runTotalAntiPRemovedVeto += nAntiPRemovedVeto; + state.runTotalProtonBeforeVeto += nBefore; + state.runTotalProtonRemovedVeto += nRemoved; + state.runTotalAntiPBeforeVeto += nAntiPBeforeVeto; + state.runTotalAntiPRemovedVeto += nAntiPRemovedVeto; } // ───────────────────────────────────────────────────────────────────── @@ -2740,12 +2818,12 @@ struct LambdaProtonBalanceFunction { std::vector antiProtonInfo; protonInfo.reserve(selectedPrimProtons.size()); antiProtonInfo.reserve(selectedPrimAntiProtons.size()); - for (auto const& t : selectedPrimProtons) { - protonInfo.push_back(makeProtonInfo(t)); - } - for (auto const& t : selectedPrimAntiProtons) { - antiProtonInfo.push_back(makeProtonInfo(t)); - } + std::transform(selectedPrimProtons.begin(), selectedPrimProtons.end(), + std::back_inserter(protonInfo), + [this](auto const& t) { return makeProtonInfo(t); }); + std::transform(selectedPrimAntiProtons.begin(), selectedPrimAntiProtons.end(), + std::back_inserter(antiProtonInfo), + [this](auto const& t) { return makeProtonInfo(t); }); // ── pp rho2 fills (eta and y) ─────────────────────────────────────────── for (auto const& p1 : selectedPrimProtons) { @@ -2812,12 +2890,12 @@ struct LambdaProtonBalanceFunction { const float y2_pp = protonRapidity(p2); const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_pp_pT015toMaxDefined->Fill(idx1, idx2); + state.hRho2_pp_AllPt->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_pp_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_pp_y_AllPt->Fill(idxY1, idxY2); } if (cs) { fillIdx(cs->pp, idx1, idx2); @@ -2834,10 +2912,10 @@ struct LambdaProtonBalanceFunction { } registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_pAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + state.hRho2_pAp_AllPt->Fill(a.idxEta, b.idxEta); } if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_pAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_pAp_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { fillIdx(cs->pAp, a.idxEta, b.idxEta); @@ -2854,10 +2932,10 @@ struct LambdaProtonBalanceFunction { } registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_App_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + state.hRho2_App_AllPt->Fill(a.idxEta, b.idxEta); } if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_App_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_App_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { fillIdx(cs->App, a.idxEta, b.idxEta); @@ -2877,10 +2955,10 @@ struct LambdaProtonBalanceFunction { } registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - hRho2_ApAp_pT015toMaxDefined->Fill(a.idxEta, b.idxEta); + state.hRho2_ApAp_AllPt->Fill(a.idxEta, b.idxEta); } if (a.idxY >= 0 && b.idxY >= 0) { - hRho2_ApAp_y_pT015toMaxDefined->Fill(a.idxY, b.idxY); + state.hRho2_ApAp_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { fillIdx(cs->ApAp, a.idxEta, b.idxEta); @@ -2898,8 +2976,8 @@ struct LambdaProtonBalanceFunction { const auto& v0PosDau = v0.posTrack_as(); const float pt = v0.pt(); int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { bin = static_cast(i); break; } @@ -2944,7 +3022,20 @@ struct LambdaProtonBalanceFunction { const float yP = protonRapidity(primProton); const int idxYL = unrolledIndexY(yL, v0.phi()); const int idxYP = unrolledIndexY(yP, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // q_inv vs DeltaPhi diagnostic (filled BEFORE applying q_inv rejection) + float dPhi_Lp = primProton.phi() - v0.phi(); + while (dPhi_Lp > TMath::Pi()) { + dPhi_Lp -= 2.0f * TMath::Pi(); + } + while (dPhi_Lp < -TMath::Pi()) { + dPhi_Lp += 2.0f * TMath::Pi(); + } + registryCorrelationQA.fill(HIST("h2_qinv_dPhi_Lp"), dPhi_Lp, qinvLP); + + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvLP)) { continue; } @@ -2953,14 +3044,14 @@ struct LambdaProtonBalanceFunction { primProton.px(), primProton.py(), primProton.pz(), kMassProton)); if (idxLambda >= 0 && idxPrimP >= 0) { if (bin >= 0) { - hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); + state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } if (cFillEtaSpace.value) { - hRho2_Lp_pT015toMaxDefined->Fill(idxLambda, idxPrimP); + state.hRho2_Lp_AllPt->Fill(idxLambda, idxPrimP); } } if (idxYL >= 0 && idxYP >= 0) { - hRho2_Lp_y_pT015toMaxDefined->Fill(idxYL, idxYP); + state.hRho2_Lp_y_AllPt->Fill(idxYL, idxYP); } if (cs) { fillIdx(cs->Lp, idxLambda, idxPrimP); @@ -3000,7 +3091,10 @@ struct LambdaProtonBalanceFunction { const float yAp = protonRapidity(primAntiProton); const int idxYL2 = unrolledIndexY(yL2, v0.phi()); const int idxYAp = unrolledIndexY(yAp, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvLAp)) { continue; } @@ -3009,14 +3103,14 @@ struct LambdaProtonBalanceFunction { primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); if (idxLambda >= 0 && idxPrimPbar >= 0) { if (bin >= 0) { - hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); + state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } if (cFillEtaSpace.value) { - hRho2_LAp_pT015toMaxDefined->Fill(idxLambda, idxPrimPbar); + state.hRho2_LAp_AllPt->Fill(idxLambda, idxPrimPbar); } } if (idxYL2 >= 0 && idxYAp >= 0) { - hRho2_LAp_y_pT015toMaxDefined->Fill(idxYL2, idxYAp); + state.hRho2_LAp_y_AllPt->Fill(idxYL2, idxYAp); } if (cs) { fillIdx(cs->LAp, idxLambda, idxPrimPbar); @@ -3089,8 +3183,8 @@ struct LambdaProtonBalanceFunction { const auto& v0NegDau = v0.negTrack_as(); const float pt = v0.pt(); int bin = -1; - for (size_t i = 0; i < ptEdges.size() - 1; ++i) { - if (pt >= ptEdges[i] && pt < ptEdges[i + 1]) { + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { bin = static_cast(i); break; } @@ -3128,7 +3222,10 @@ struct LambdaProtonBalanceFunction { const float yP2 = protonRapidity(primProton); const int idxYAL = unrolledIndexY(yAL, v0.phi()); const int idxYP2 = unrolledIndexY(yP2, primProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvALp)) { continue; } @@ -3137,14 +3234,14 @@ struct LambdaProtonBalanceFunction { primProton.px(), primProton.py(), primProton.pz(), kMassProton)); if (idxAL >= 0 && idxPrimP >= 0) { if (bin >= 0) { - hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); + state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } if (cFillEtaSpace.value) { - hRho2_ALp_pT015toMaxDefined->Fill(idxAL, idxPrimP); + state.hRho2_ALp_AllPt->Fill(idxAL, idxPrimP); } } if (idxYAL >= 0 && idxYP2 >= 0) { - hRho2_ALp_y_pT015toMaxDefined->Fill(idxYAL, idxYP2); + state.hRho2_ALp_y_AllPt->Fill(idxYAL, idxYP2); } if (cs) { fillIdx(cs->ALp, idxAL, idxPrimP); @@ -3191,7 +3288,10 @@ struct LambdaProtonBalanceFunction { const float yAp2 = protonRapidity(primAntiProton); const int idxYAL2 = unrolledIndexY(yAL2, v0.phi()); const int idxYAp2 = unrolledIndexY(yAp2, primAntiProton.phi()); - // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (failsQinvCut(qinvALAp)) { continue; } @@ -3200,14 +3300,14 @@ struct LambdaProtonBalanceFunction { primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); if (idxAL2 >= 0 && idxPrimPb >= 0) { if (bin >= 0) { - hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); + state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } if (cFillEtaSpace.value) { - hRho2_ALAp_pT015toMaxDefined->Fill(idxAL2, idxPrimPb); + state.hRho2_ALAp_AllPt->Fill(idxAL2, idxPrimPb); } } if (idxYAL2 >= 0 && idxYAp2 >= 0) { - hRho2_ALAp_y_pT015toMaxDefined->Fill(idxYAL2, idxYAp2); + state.hRho2_ALAp_y_AllPt->Fill(idxYAL2, idxYAp2); } if (cs) { fillIdx(cs->ALAp, idxAL2, idxPrimPb); @@ -3320,12 +3420,12 @@ struct LambdaProtonBalanceFunction { const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1L, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_LL_pT015toMaxDefined->Fill(idx1, idx2); + state.hRho2_LL_AllPt->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_LL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_LL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { fillIdx(cs->LL, idx1, idx2); @@ -3374,12 +3474,12 @@ struct LambdaProtonBalanceFunction { const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_LAL_pT015toMaxDefined->Fill(idx1, idx2); + state.hRho2_LAL_AllPt->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_LAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_LAL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { fillIdx(cs->LAL, idx1, idx2); @@ -3428,12 +3528,12 @@ struct LambdaProtonBalanceFunction { const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_ALL_pT015toMaxDefined->Fill(idx1, idx2); + state.hRho2_ALL_AllPt->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_ALL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_ALL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { fillIdx(cs->ALL, idx1, idx2); @@ -3485,12 +3585,12 @@ struct LambdaProtonBalanceFunction { const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); - // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCutLP are rejected above) + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - hRho2_ALAL_pT015toMaxDefined->Fill(idx1, idx2); + state.hRho2_ALAL_AllPt->Fill(idx1, idx2); } if (idxY1 >= 0 && idxY2 >= 0) { - hRho2_ALAL_y_pT015toMaxDefined->Fill(idxY1, idxY2); + state.hRho2_ALAL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { fillIdx(cs->ALAL, idx1, idx2); @@ -3512,8 +3612,8 @@ struct LambdaProtonBalanceFunction { // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. void endOfStream(EndOfStreamContext const&) { - const int64_t totalBefore = runTotalProtonBeforeVeto; - const int64_t totalRemoved = runTotalProtonRemovedVeto; + const int64_t totalBefore = state.runTotalProtonBeforeVeto; + const int64_t totalRemoved = state.runTotalProtonRemovedVeto; const int64_t totalAfter = totalBefore - totalRemoved; const double fracRemoved = (totalBefore > 0) ? static_cast(totalRemoved) / static_cast(totalBefore) @@ -3525,11 +3625,11 @@ struct LambdaProtonBalanceFunction { LOG(info) << "Total proton candidates after veto : " << totalAfter; LOG(info) << "Total removed by veto : " << totalRemoved; LOG(info) << Form("Fraction removed : %.6f", fracRemoved); - const double fracRemovedAp = (runTotalAntiPBeforeVeto > 0) - ? static_cast(runTotalAntiPRemovedVeto) / static_cast(runTotalAntiPBeforeVeto) + const double fracRemovedAp = (state.runTotalAntiPBeforeVeto > 0) + ? static_cast(state.runTotalAntiPRemovedVeto) / static_cast(state.runTotalAntiPBeforeVeto) : 0.0; - LOG(info) << "Total antiproton candidates before veto: " << runTotalAntiPBeforeVeto; - LOG(info) << "Total antiproton removed by veto : " << runTotalAntiPRemovedVeto; + LOG(info) << "Total antiproton candidates before veto: " << state.runTotalAntiPBeforeVeto; + LOG(info) << "Total antiproton removed by veto : " << state.runTotalAntiPRemovedVeto; LOG(info) << Form("Antiproton fraction removed : %.6f", fracRemovedAp); LOG(info) << "==============================================="; LOG(info) << ""; From abafa83f7a57d9908789133532058669ae66d568 Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Tue, 29 Sep 2026 16:13:55 +0200 Subject: [PATCH 110/648] [Infrastructure,Tools,Common,Tutorial,PWGDQ,PWGHF,PWGLF] [ML] Fix for memory issue in ML headers (#17836) Co-authored-by: ALICE Action Bot --- CODEOWNERS | 2 +- Common/Tools/PID/pidTPCModule.h | 15 +- PWGDQ/Tasks/quarkoniaToHyperons.cxx | 6 +- .../candidateSelectorLcPidMl.cxx | 4 +- .../Strangeness/lambdakzeromlselection.cxx | 8 +- .../Strangeness/strangenessbuilder.cxx | 2 +- .../derivedlambdakzeroanalysis.cxx | 6 +- Tools/ML/MlResponse.h | 25 ++- Tools/ML/model.cxx | 126 ++++++++++++ Tools/ML/model.h | 189 ++++++++++++------ Tutorials/ML/applyOnnxModel.cxx | 8 +- 11 files changed, 296 insertions(+), 95 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 8faaf6abff2..ee6d5b59469 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -78,7 +78,7 @@ /PWGUD @alibuild @amatyja @rolavick /PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman @mhemmer-cern /Tools/PIDML @alibuild @saganatt -/Tools/ML @alibuild @fcatalan92 @fmazzasc +/Tools/ML @alibuild @fcatalan92 @fmazzasc @ChSonnabend /Tutorials/PWGCF @alibuild @jgrosseo @victor-gonzalez @zchochul /Tutorials/PWGDQ @alibuild @iarsene @mcoquet642 @XiaozhiBai @mguilbau /Tutorials/PWGEM @alibuild @mikesas @rbailhac @dsekihat @ivorobye @feisenhu diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index b38bc532c44..802d17a9b06 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -570,11 +571,12 @@ class pidTPCModule std::unique_ptr networkPrediction(new float[predictionSize * NParticleTypes]); // For each mass hypotheses const float nNclNormalization = response->GetNClNormalization(); - float durationNetwork = 0; + float durationNetwork = 0.f; std::vector trackProperties(trackPropSize); + std::vector outputNetwork; // output buffer, allocation is reused for all mass hypotheses uint64_t counterTrackProps = 0; - int loopCounter = 0; + uint64_t loopCounter = 0; // To load the Hadronic rate once for each collision std::vector hadronicRateForCollision(collisions.size(), 0.0f); @@ -626,14 +628,13 @@ class pidTPCModule } const auto startNetworkEval = std::chrono::high_resolution_clock::now(); - const float* const outputNetwork = network.evalModel(trackProperties); + network.evalModel(trackProperties, outputNetwork); const auto stopNetworkEval = std::chrono::high_resolution_clock::now(); durationNetwork += std::chrono::duration>(stopNetworkEval - startNetworkEval).count(); - for (uint64_t kPrediction = 0; kPrediction < predictionSize; kPrediction += outputDimensions) { - for (int lOutputDim = 0; lOutputDim < outputDimensions; ++lOutputDim) { - networkPrediction[kPrediction + lOutputDim + predictionSize * loopCounter] = outputNetwork[kPrediction + lOutputDim]; - } + if (outputNetwork.size() != predictionSize) { + LOG(fatal) << "Network output size (" << outputNetwork.size() << ") does not match the expected prediction size (" << predictionSize << ")"; } + std::copy(outputNetwork.begin(), outputNetwork.end(), networkPrediction.get() + predictionSize * loopCounter); counterTrackProps = 0; ++loopCounter; diff --git a/PWGDQ/Tasks/quarkoniaToHyperons.cxx b/PWGDQ/Tasks/quarkoniaToHyperons.cxx index 9c1fcb459f8..84606f59fac 100644 --- a/PWGDQ/Tasks/quarkoniaToHyperons.cxx +++ b/PWGDQ/Tasks/quarkoniaToHyperons.cxx @@ -1773,7 +1773,7 @@ struct QuarkoniaToHyperons { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1788,7 +1788,7 @@ struct QuarkoniaToHyperons { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1803,7 +1803,7 @@ struct QuarkoniaToHyperons { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); diff --git a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx index f27e5edc3cd..efccd81c0fd 100644 --- a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx @@ -307,12 +307,12 @@ struct HfCandidateSelectorLcPidMl { std::vector inputFeaturesD{trackParPos1.getPt(), trackPos1.dcaXY(), trackPos1.dcaZ(), trackParNeg.getPt(), trackNeg.dcaXY(), trackNeg.dcaZ(), trackParPos2.getPt(), trackPos2.dcaXY(), trackPos2.dcaZ()}; float scores[3] = {-1.f, -1.f, -1.f}; if (dataTypeML == 1) { - auto* scoresRaw = model.evalModel(inputFeaturesF); + const auto scoresRaw = model.evalModel(inputFeaturesF); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } } else if (dataTypeML == 11) { - auto* scoresRaw = model.evalModel(inputFeaturesD); + const auto scoresRaw = model.evalModel(inputFeaturesD); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } diff --git a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx index b27cb24c68c..3fd13a7aa95 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx @@ -210,19 +210,19 @@ struct lambdakzeromlselection { // calculate classifier output if (PredictLambda) { - float* LambdaProbability = lambda_bdt.evalModel(inputFeatures); + const std::vector LambdaProbability = lambda_bdt.evalModel(inputFeatures); lambdaMLSelections(LambdaProbability[1]); } if (PredictGamma) { - float* GammaProbability = gamma_bdt.evalModel(inputFeatures); + const std::vector GammaProbability = gamma_bdt.evalModel(inputFeatures); gammaMLSelections(GammaProbability[1]); } if (PredictAntiLambda) { - float* AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); + const std::vector AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); antiLambdaMLSelections(AntiLambdaProbability[1]); } if (PredictKZeroShort) { - float* KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); + const std::vector KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); kzeroShortMLSelections(KZeroShortProbability[1]); } } diff --git a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx index 14f37772f44..0d034c5b1a8 100644 --- a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx @@ -1006,7 +1006,7 @@ struct StrangenessBuilder { AvgPA, // 6. Avg Pointing Angle static_cast(v0zRanks[ic])}; // 7. V0 Vtx z Rank - float* BDTProbability = deduplication_bdt.evalModel(inputFeatures); + const std::vector BDTProbability = deduplication_bdt.evalModel(inputFeatures); if (BDTProbability[1] > bestMLScore) { bestMLScore = BDTProbability[1]; diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 9638d3954f6..141945bd687 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -1893,7 +1893,7 @@ struct derivedlambdakzeroanalysis { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1908,7 +1908,7 @@ struct derivedlambdakzeroanalysis { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1923,7 +1923,7 @@ struct derivedlambdakzeroanalysis { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); diff --git a/Tools/ML/MlResponse.h b/Tools/ML/MlResponse.h index 393eecba293..413534f48cb 100644 --- a/Tools/ML/MlResponse.h +++ b/Tools/ML/MlResponse.h @@ -190,8 +190,16 @@ class MlResponse LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " is different from the number of input features to be tested (" << numInputNodes << " vs " << numInputFeatures << ")"; } - TypeOutputScore* outputPtr = mModels[nModel].template evalModel(input); - return std::vector{outputPtr, outputPtr + mNClasses}; + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = mModels[nModel].template evalModel(input); + if (output.size() < mNClasses) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(mNClasses) << " classes are expected. Please check your configurables."; + } + if (output.size() > mNClasses) { + // keep only the first mNClasses scores (e.g. single-candidate probabilities of a multi-output model) + output.resize(mNClasses); + } + return output; } /// Get vector with model predictions for a batch of candidates @@ -221,11 +229,16 @@ class MlResponse LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " differs from features per row (" << numInputNodes << " vs " << featuresPerRow << ")"; } - TypeOutputScore* outputPtr = mModels[nModel].template evalModel(input); - if (outputPtr == nullptr) { - LOG(fatal) << "Batched model evaluation failed for model " << mPaths[nModel]; + std::vector output = mModels[nModel].template evalModel(input); + const std::size_t expectedOutputSize = nRows * mNClasses; + if (output.size() < expectedOutputSize) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << expectedOutputSize << " scores are expected for " << nRows << " rows and " << static_cast(mNClasses) << " classes. Please check your configurables."; + } + if (output.size() > expectedOutputSize) { + // keep only the first scores (e.g. batched probabilities of a multi-output model) + output.resize(expectedOutputSize); } - return std::vector{outputPtr, outputPtr + nRows * mNClasses}; + return output; } /// ML selections diff --git a/Tools/ML/model.cxx b/Tools/ML/model.cxx index 6d2098068aa..ebf3919a891 100644 --- a/Tools/ML/model.cxx +++ b/Tools/ML/model.cxx @@ -42,6 +42,9 @@ namespace ml std::string OnnxModel::printShape(const std::vector& v) { + if (v.empty()) { + return "[]"; + } std::stringstream ss(""); for (std::size_t i = 0; i < v.size() - 1; i++) ss << v[i] << "x"; @@ -90,7 +93,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz mEnv = std::make_shared(ORT_LOGGING_LEVEL_WARNING, "onnx-model"); mSession = std::make_shared(*mEnv, modelPath.c_str(), sessionOptions); + mMemInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + mInputNamesChar.clear(); + mOutputNamesChar.clear(); + mInputNames.clear(); + mInputShapes.clear(); + mOutputNames.clear(); + mOutputShapes.clear(); Ort::AllocatorWithDefaultOptions const tmpAllocator; for (std::size_t i = 0; i < mSession->GetInputCount(); ++i) { mInputNames.push_back(mSession->GetInputNameAllocated(i, tmpAllocator).get()); @@ -104,6 +114,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz for (std::size_t i = 0; i < mSession->GetOutputCount(); ++i) { mOutputShapes.emplace_back(mSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); } + mInputNamesChar.reserve(mInputNames.size()); + for (const auto& name : mInputNames) { + mInputNamesChar.push_back(name.c_str()); + } + mOutputNamesChar.reserve(mOutputNames.size()); + for (const auto& name : mOutputNames) { + mOutputNamesChar.push_back(name.c_str()); + } LOG(info) << "Input Nodes:"; for (std::size_t i = 0; i < mInputNames.size(); i++) { LOG(info) << "\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); @@ -122,6 +140,114 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz LOG(info) << "--- Model initialized! ---"; } +std::vector OnnxModel::inferInputShape(const std::size_t iinput, const int64_t size) const +{ + const std::vector& modelShape = mInputShapes[iinput]; + + // Rank-1 input: the whole vector is the tensor + if (modelShape.size() < 2) { + return {size}; + } + + // Product of all non-batch dimensions; dynamic dimensions (< 0) cannot be inferred + int64_t totalSize = 1; + bool hasDynamicDim = false; + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + if (modelShape[idim] < 0) { + hasDynamicDim = true; + } else { + totalSize *= modelShape[idim]; + } + } + + if (hasDynamicDim) { + if (modelShape.size() == 2) { + // [batch, features] with dynamic feature dimension: interpret the vector as a single sample + return {1, size}; + } + LOG(fatal) << "Input " << iinput << " (" << mInputNames[iinput] << ") has dynamic non-batch dimensions (" << printShape(modelShape) << "), the tensor shape cannot be inferred from a flat vector. Please provide std::vector inputs instead."; + } + + if (totalSize <= 0 || size % totalSize != 0) { + LOG(fatal) << "Size of the input vector (" << size << ") is not a multiple of the model input size (" << totalSize << ") for input " << iinput << " (" << mInputNames[iinput] << ", shape " << printShape(modelShape) << ")"; + } + + std::vector inputShape; + inputShape.reserve(modelShape.size()); + inputShape.push_back(size / totalSize); + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + inputShape.push_back(modelShape[idim]); + } + return inputShape; +} + +void OnnxModel::checkInput(const std::vector& input) const +{ + if (!mSession) { + LOG(fatal) << "OnnxModel::evalModel called before initModel()"; + } + if (input.size() != mInputNames.size()) { + LOG(fatal) << "Number of input tensors (" << input.size() << ") does not agree with the number of model inputs (" << mInputNames.size() << ")"; + } + for (std::size_t i = 0; i < input.size(); i++) { + LOG(debug) << "Input tensor " << i << " shape: " << printShape(input[i].GetTensorTypeAndShapeInfo().GetShape()); + } +} + +std::vector OnnxModel::evalModelRaw(std::vector& input) +{ + checkInput(input); + std::vector outputTensors; + try { + const Ort::RunOptions runOptions{nullptr}; + outputTensors = mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), mOutputNamesChar.data(), mOutputNamesChar.size()); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + + LOG(debug) << "Number of output tensors: " << outputTensors.size(); + if (outputTensors.size() != mOutputNames.size()) { + LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); + } + for (std::size_t i = 0; i < outputTensors.size(); i++) { + checkOutput(outputTensors[i], i); + } + + return outputTensors; +} + +Ort::Value OnnxModel::evalModelLast(std::vector& input) +{ + checkInput(input); + if (mOutputNamesChar.empty()) { + LOG(fatal) << "Model has no outputs"; + } + Ort::Value output{nullptr}; + try { + // A null RunOptions uses the runtime defaults without allocating options per call. + const Ort::RunOptions runOptions{nullptr}; + mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), &mOutputNamesChar.back(), &output, 1); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + checkOutput(output, mOutputShapes.size() - 1); + return output; +} + +void OnnxModel::checkOutput(const Ort::Value& tensor, const std::size_t index) const +{ + const std::vector shape = tensor.GetTensorTypeAndShapeInfo().GetShape(); + LOG(debug) << "Output tensor " << index << " shape: " << printShape(shape); + bool shapeOk = (shape.size() == mOutputShapes[index].size()); + for (std::size_t idim = 0; shapeOk && idim < shape.size(); idim++) { + // Dynamic dimensions of the model (< 0) can take any value. + shapeOk = (mOutputShapes[index][idim] < 0) || (shape[idim] == mOutputShapes[index][idim]); + } + if (!shapeOk) { + LOG(fatal) << "Shape of output tensor " << index << " does not agree with model specification! Output: " << printShape(shape) << " model: " << printShape(mOutputShapes[index]); + } +} + void OnnxModel::setActiveThreads(const int threads) { activeThreads = threads; diff --git a/Tools/ML/model.h b/Tools/ML/model.h index e9997c1501e..d94491f8c38 100644 --- a/Tools/ML/model.h +++ b/Tools/ML/model.h @@ -22,14 +22,10 @@ #include -#include #include -#include -#include #include #include -#include #include #include #include @@ -40,91 +36,112 @@ namespace o2 namespace ml { +/// \brief Thin wrapper around an ONNX Runtime session (CPU only) +/// +/// Inference entry points: +/// - evalModelRaw(): runs the session and returns the owning output tensors (one Ort::Value per model output). +/// This is the zero-copy path: the caller owns the tensors and may read them via GetTensorData() +/// for as long as the returned vector is alive. +/// - evalModel(input): convenience wrapper returning a copy of the *last* model output as std::vector. +/// - evalModel(input, output): same as above but writes into a caller-provided vector. The vector is +/// resized as needed and its capacity is reused across calls, which avoids repeated allocations of the result buffer. +/// Only the last model output is requested from ONNX Runtime. +/// +/// Inputs given as std::vector are wrapped in an Ort::Value without copying. The input vector must therefore +/// stay alive until the call returns (which is always the case for the synchronous calls provided here). class OnnxModel { public: OnnxModel() = default; ~OnnxModel() = default; + // The ONNX session options are not copyable, so the model is move-only + OnnxModel(const OnnxModel&) = delete; + OnnxModel& operator=(const OnnxModel&) = delete; + OnnxModel(OnnxModel&&) = default; + OnnxModel& operator=(OnnxModel&&) = default; // Inferencing void initModel(const std::string&, const bool = false, const int = 0, const uint64_t = 0, const uint64_t = 0); - // template methods -- best to define them in header + /// Run the model on already prepared input tensors + /// \param input one Ort::Value per model input + /// \return the output tensors of the model (owning). Access to the data via output[i].GetTensorData() + std::vector evalModelRaw(std::vector& input); + + /// Run the model and copy the last output tensor into a vector + /// \param input one Ort::Value per model input + /// \return flattened content of the last output tensor template - T* evalModel(std::vector& input) + std::vector evalModel(std::vector& input) { - LOG(debug) << "Input tensor shape: " << printShape(input[0].GetTensorTypeAndShapeInfo().GetShape()); - // assert(input[0].GetTensorTypeAndShapeInfo().GetShape() == getNumInputNodes()); --> Fails build in debug mode, TODO: assertion should be checked somehow - - try { - const Ort::RunOptions runOptions; - std::vector inputNamesChar(mInputNames.size(), nullptr); - std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(inputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - - std::vector outputNamesChar(mOutputNames.size(), nullptr); - std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(outputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - auto outputTensors = mSession->Run(runOptions, inputNamesChar.data(), input.data(), input.size(), outputNamesChar.data(), outputNamesChar.size()); - LOG(debug) << "Number of output tensors: " << outputTensors.size(); - if (outputTensors.size() != mOutputNames.size()) { - LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); - } - for (std::size_t i = 0; i < outputTensors.size(); i++) { - LOG(debug) << "Output tensor shape: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()); - if ((outputTensors[i].GetTensorTypeAndShapeInfo().GetShape() != mOutputShapes[i]) && (mOutputShapes[i][0] != -1)) { - LOG(fatal) << "Shape of tensor " << i << " does not agree with model specification! Output: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()) << " model: " << printShape(mOutputShapes[i]); - } - } - // TODO: isn't outputTensors freed after exiting this function thus making the returned pointer pointing to garbage memory resulting in undefined beaviour? - T* outputValues = outputTensors.back().GetTensorMutableData(); - return outputValues; - } catch (const Ort::Exception& exception) { - LOG(error) << "Error running model inference: " << exception.what(); - } - return nullptr; + std::vector output; + evalModel(input, output); + return output; } + /// Run the model and copy the last output tensor into the provided vector (allocation is reused between calls) + /// \param input one Ort::Value per model input + /// \param output vector which is filled with the flattened content of the last output tensor template - T* evalModel(std::vector& input) + void evalModel(std::vector& input, std::vector& output) { - const int64_t size = input.size(); - assert(size % mInputShapes[0][1] == 0); - std::vector inputShape{size / mInputShapes[0][1], mInputShapes[0][1]}; - std::vector inputTensors; - Ort::MemoryInfo memInfo = - Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input.data(), size, inputShape.data(), inputShape.size())); - LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); - return evalModel(inputTensors); + const Ort::Value tensor = evalModelLast(input); + copyOutput(tensor, output); } - // For 2D inputs + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \return flattened content of the last output tensor template - T* evalModel(std::vector>& input) + std::vector evalModel(std::vector& input) { + std::vector output; + evalModel(input, output); + return output; + } + + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector& input, std::vector& output) + { + if (mInputShapes.size() != 1) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but a single input vector was provided. Use the std::vector> or std::vector overload."; + } std::vector inputTensors; + inputTensors.reserve(1); + addInputTensor(inputTensors, input, 0); + evalModel(inputTensors, output); + } - Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \return flattened content of the last output tensor + template + std::vector evalModel(std::vector>& input) + { + std::vector output; + evalModel(input, output); + return output; + } + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector>& input, std::vector& output) + { + if (input.size() != mInputShapes.size()) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but " << input.size() << " input vectors were provided."; + } + std::vector inputTensors; + inputTensors.reserve(input.size()); for (std::size_t iinput = 0; iinput < input.size(); iinput++) { - [[maybe_unused]] int totalSize = 1; - int64_t size = input[iinput].size(); - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - totalSize *= mInputShapes[iinput][idim]; - } - assert(size % totalSize == 0); - - std::vector inputShape{static_cast(size / totalSize)}; - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - inputShape.push_back(mInputShapes[iinput][idim]); - } - - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input[iinput].data(), size, inputShape.data(), inputShape.size())); + addInputTensor(inputTensors, input[iinput], iinput); } - - return evalModel(inputTensors); + evalModel(inputTensors, output); } // Reset session @@ -142,6 +159,7 @@ class OnnxModel int getNumInputNodes() const { return mInputShapes[0][1]; } std::vector> getInputShapes() const { return mInputShapes; } int getNumOutputNodes() const { return mOutputShapes[0][1]; } + std::vector> getOutputShapes() const { return mOutputShapes; } uint64_t getValidityFrom() const { return validFrom; } uint64_t getValidityUntil() const { return validUntil; } void setActiveThreads(const int); @@ -151,6 +169,7 @@ class OnnxModel std::shared_ptr mEnv = nullptr; std::shared_ptr mSession = nullptr; Ort::SessionOptions sessionOptions; + Ort::MemoryInfo mMemInfo{nullptr}; // CPU memory info, created once in initModel // Input & Output specifications of the loaded network std::vector mInputNames; @@ -158,14 +177,54 @@ class OnnxModel std::vector mOutputNames; std::vector> mOutputShapes; + // Pointers into the name vectors above, populated only after all names are loaded. + // Moving the model transfers the backing vectors without invalidating these pointers. + std::vector mInputNamesChar; + std::vector mOutputNamesChar; + // Environment settings std::string modelPath; int activeThreads = 0; uint64_t validFrom = 0; uint64_t validUntil = 0; + /// Derive the tensor shape for input number iinput from the number of provided values + /// The first dimension is treated as batch dimension; all other dimensions are taken from the model + std::vector inferInputShape(const std::size_t iinput, const int64_t size) const; + + /// Wrap a flat data vector (no copy) into an Ort::Value and append it to tensors + template + void addInputTensor(std::vector& tensors, std::vector& data, const std::size_t iinput) const + { + const std::vector inputShape = inferInputShape(iinput, static_cast(data.size())); + LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); + tensors.emplace_back(Ort::Value::CreateTensor(mMemInfo, data.data(), data.size(), inputShape.data(), inputShape.size())); + } + + /// Request only the last output, keeping its tensor alive until the caller has copied it + Ort::Value evalModelLast(std::vector& input); + void checkInput(const std::vector& input) const; + void checkOutput(const Ort::Value& tensor, std::size_t index) const; + + /// Copy the content of an output tensor into output (reusing its allocation) + template + void copyOutput(const Ort::Value& tensor, std::vector& output) const + { + const auto info = tensor.GetTensorTypeAndShapeInfo(); + if (info.GetElementType() != Ort::TypeToTensorType::type) { + LOG(fatal) << "Requested output type (ONNX type id " << static_cast(Ort::TypeToTensorType::type) << ") does not match the model output tensor type (ONNX type id " << static_cast(info.GetElementType()) << ")"; + } + const T* data = tensor.GetTensorData(); + const auto size = info.GetElementCount(); + if (size == 0) { + output.clear(); + } else { + output.assign(data, data + size); + } + } + // Internal function for printing the shape of tensors - std::string printShape(const std::vector&); + static std::string printShape(const std::vector&); bool checkHyperloop(const bool = true); }; diff --git a/Tutorials/ML/applyOnnxModel.cxx b/Tutorials/ML/applyOnnxModel.cxx index 16151511df0..560e3f204bd 100644 --- a/Tutorials/ML/applyOnnxModel.cxx +++ b/Tutorials/ML/applyOnnxModel.cxx @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include @@ -51,11 +53,11 @@ struct applyModel { void run(ProcessingContext& pc) { - // Here we evaluate the model - float* modelOutput = network.evalModel(modelInput); + // Here we evaluate the model. The output tensor is copied into a std::vector owned by us + const std::vector modelOutput = network.evalModel(modelInput); // And now we print the output - for (int i = 0; i < 5; i++) { + for (std::size_t i = 0; i < std::min(modelInput.size(), modelOutput.size()); i++) { LOG(info) << "Input: " << modelInput[i] << ", Output: " << modelOutput[i]; } pc.services().get().endOfStream(); From 6e44589a99e61f0bfd329099c61f67f3f4be6e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:39:32 +0200 Subject: [PATCH 111/648] [PWGJE] Fix cpplint errors (#18101) --- PWGJE/Core/PWGJECoreLinkDef.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/PWGJE/Core/PWGJECoreLinkDef.h b/PWGJE/Core/PWGJECoreLinkDef.h index 869be064e38..cd25d7b94be 100644 --- a/PWGJE/Core/PWGJECoreLinkDef.h +++ b/PWGJE/Core/PWGJECoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGJE_CORE_PWGJECORELINKDEF_H_ -#define PWGJE_CORE_PWGJECORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ namespace jetutilities + ; #pragma link C++ namespace fastjetutilities + ; #pragma link C++ namespace jettaggingutilities + ; - -#endif // PWGJE_CORE_PWGJECORELINKDEF_H_ From 7c7ccbefdd930a2135bd5de3118eec138332cec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:50:42 +0200 Subject: [PATCH 112/648] [PWGUD] Fix cpplint errors (#18104) --- PWGUD/Core/DGCutparHolderLinkDef.h | 6 ++---- PWGUD/Core/DGPIDSelectorLinkDef.h | 2 ++ PWGUD/Core/FITCutParHolderLinkDef.h | 5 +---- PWGUD/Core/SGCutParHolderLinkDef.h | 5 +---- PWGUD/Core/UDFSParserLinkDef.h | 6 ++---- PWGUD/Core/UDGoodRunSelectorLinkDef.h | 2 ++ PWGUD/Core/UPCCutparHolderLinkDef.h | 2 ++ PWGUD/Core/decayTreeLinkDef.h | 6 ++---- PWGUD/Tasks/sgFITAnalyzer.cxx | 1 + PWGUD/Tasks/sgSpectraAnalyzer.cxx | 1 + 10 files changed, 16 insertions(+), 20 deletions(-) diff --git a/PWGUD/Core/DGCutparHolderLinkDef.h b/PWGUD/Core/DGCutparHolderLinkDef.h index b6b7c63efca..722796d015f 100644 --- a/PWGUD/Core/DGCutparHolderLinkDef.h +++ b/PWGUD/Core/DGCutparHolderLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class DGCutparHolder + ; - -#endif // PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/DGPIDSelectorLinkDef.h b/PWGUD/Core/DGPIDSelectorLinkDef.h index 2d0b181ba19..837079914e1 100644 --- a/PWGUD/Core/DGPIDSelectorLinkDef.h +++ b/PWGUD/Core/DGPIDSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/FITCutParHolderLinkDef.h b/PWGUD/Core/FITCutParHolderLinkDef.h index 3194a6754b2..5e0644779cb 100644 --- a/PWGUD/Core/FITCutParHolderLinkDef.h +++ b/PWGUD/Core/FITCutParHolderLinkDef.h @@ -13,12 +13,9 @@ // \author Sandor Lokos, sandor.lokos@cern.ch // \since March 2026 -#ifndef PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class FITCutParHolder + ; - -#endif // PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/SGCutParHolderLinkDef.h b/PWGUD/Core/SGCutParHolderLinkDef.h index 5dcadadc69e..f5d3a6f5a82 100644 --- a/PWGUD/Core/SGCutParHolderLinkDef.h +++ b/PWGUD/Core/SGCutParHolderLinkDef.h @@ -9,12 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class SGCutParHolder + ; - -#endif // PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/UDFSParserLinkDef.h b/PWGUD/Core/UDFSParserLinkDef.h index f7caf1a0ab0..8f6e10eabd6 100644 --- a/PWGUD/Core/UDFSParserLinkDef.h +++ b/PWGUD/Core/UDFSParserLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_UDFSPARSERLINKDEF_H_ -#define PWGUD_CORE_UDFSPARSERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class UDFSParser + ; - -#endif // PWGUD_CORE_UDFSPARSERLINKDEF_H_ diff --git a/PWGUD/Core/UDGoodRunSelectorLinkDef.h b/PWGUD/Core/UDGoodRunSelectorLinkDef.h index c26b4622809..c439e2e1094 100644 --- a/PWGUD/Core/UDGoodRunSelectorLinkDef.h +++ b/PWGUD/Core/UDGoodRunSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/UPCCutparHolderLinkDef.h b/PWGUD/Core/UPCCutparHolderLinkDef.h index 49d4802838d..6391b991669 100644 --- a/PWGUD/Core/UPCCutparHolderLinkDef.h +++ b/PWGUD/Core/UPCCutparHolderLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/decayTreeLinkDef.h b/PWGUD/Core/decayTreeLinkDef.h index 92d99ccf1bc..6ed7643f91b 100644 --- a/PWGUD/Core/decayTreeLinkDef.h +++ b/PWGUD/Core/decayTreeLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_DECAYTREELINKDEF_H_ -#define PWGUD_CORE_DECAYTREELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class decayTree + ; - -#endif // PWGUD_CORE_DECAYTREELINKDEF_H_ diff --git a/PWGUD/Tasks/sgFITAnalyzer.cxx b/PWGUD/Tasks/sgFITAnalyzer.cxx index de44924b0a2..39aa9c2486a 100644 --- a/PWGUD/Tasks/sgFITAnalyzer.cxx +++ b/PWGUD/Tasks/sgFITAnalyzer.cxx @@ -37,6 +37,7 @@ #include #include +#include #include using namespace o2; diff --git a/PWGUD/Tasks/sgSpectraAnalyzer.cxx b/PWGUD/Tasks/sgSpectraAnalyzer.cxx index b17c699970f..2d0d577bb84 100644 --- a/PWGUD/Tasks/sgSpectraAnalyzer.cxx +++ b/PWGUD/Tasks/sgSpectraAnalyzer.cxx @@ -33,6 +33,7 @@ #include #include +#include #include #include From a60e5a5f62161907478aa6708490fc8b483ce680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:09:15 +0200 Subject: [PATCH 113/648] [Tutorial] Fix cpplint errors (#18105) --- Tutorials/ML/applyMlSelection.cxx | 1 + Tutorials/src/ConfigurableCutLinkDef.h | 2 ++ Tutorials/src/associatedExample.cxx | 2 +- Tutorials/src/preprocess.cxx | 1 + Tutorials/src/reweighting.cxx | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Tutorials/ML/applyMlSelection.cxx b/Tutorials/ML/applyMlSelection.cxx index 869abd7a62c..2bee237ab3e 100644 --- a/Tutorials/ML/applyMlSelection.cxx +++ b/Tutorials/ML/applyMlSelection.cxx @@ -36,6 +36,7 @@ #include #include +#include #include using namespace o2; diff --git a/Tutorials/src/ConfigurableCutLinkDef.h b/Tutorials/src/ConfigurableCutLinkDef.h index f2667e6b6cc..397ee540c36 100644 --- a/Tutorials/src/ConfigurableCutLinkDef.h +++ b/Tutorials/src/ConfigurableCutLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Tutorials/src/associatedExample.cxx b/Tutorials/src/associatedExample.cxx index c8c87debff1..1aba1e01905 100644 --- a/Tutorials/src/associatedExample.cxx +++ b/Tutorials/src/associatedExample.cxx @@ -150,7 +150,7 @@ struct PartitionColExtra { struct BuildHmpidIndex { // build the index table HMPIDTracksIndex Builds idx; - void init(InitContext const&) {}; + void init(InitContext const&) {} }; struct ConsumeHmpidIndex { diff --git a/Tutorials/src/preprocess.cxx b/Tutorials/src/preprocess.cxx index 8a3bc43be05..14513311028 100644 --- a/Tutorials/src/preprocess.cxx +++ b/Tutorials/src/preprocess.cxx @@ -28,6 +28,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/Tutorials/src/reweighting.cxx b/Tutorials/src/reweighting.cxx index 48e90a9560d..b2622385cb3 100644 --- a/Tutorials/src/reweighting.cxx +++ b/Tutorials/src/reweighting.cxx @@ -51,6 +51,7 @@ #include #include #include +#include #include using namespace o2; From 5bb72a96b7934a2c99fca540c75f6d6869932cd4 Mon Sep 17 00:00:00 2001 From: rbailhac Date: Tue, 29 Sep 2026 18:59:09 +0200 Subject: [PATCH 114/648] [PWGEM] Add the possibility to check ITS-TPC matching (#17962) --- PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx index a608e07d79a..a322e7b0d97 100644 --- a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx +++ b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx @@ -142,6 +142,7 @@ struct createResolutionMapDerived { Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. + Configurable acceptOnlyPhysicalPrimary{"acceptOnlyPhysicalPrimary", false, "flag to accept only Physical Primary electrons"}; Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -456,6 +457,9 @@ struct createResolutionMapDerived { if (electroncuts.acceptOnlyWrongMatch && !o2::aod::pwgem::dilepton::utils::mcutil::hasFakeMatchITSTPC(track)) { continue; } + if (electroncuts.acceptOnlyPhysicalPrimary && !mcParticle.isPhysicalPrimary()) { + continue; + } registry.fill(HIST("Electron/hPIDForTracking"), track.pidForTracking()); if (electroncuts.checkPIDForTracking && track.pidForTracking() != static_cast(electroncuts.PartIdentifier)) { From 9632e6099cafffbd70718fe3142c47d2332570c0 Mon Sep 17 00:00:00 2001 From: Florian Eisenhut <53603353+feisenhu@users.noreply.github.com> Date: Tue, 29 Sep 2026 22:02:55 +0200 Subject: [PATCH 115/648] =?UTF-8?q?[PWGEM]=20Implement=20pt=20axis=20for?= =?UTF-8?q?=20each=20mother=20of=20electrons=20from=20charm=20had=E2=80=A6?= =?UTF-8?q?=20(#18110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx | 576 ++++++++++--------- 1 file changed, 304 insertions(+), 272 deletions(-) diff --git a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx index 0e2d7b2968d..62ef21abaab 100644 --- a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx +++ b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx @@ -376,6 +376,8 @@ struct checkMCPairTemplate { std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; std::string pair_y_axis_title = "y_{ll}"; std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; + std::string mother1_pt_axis_title = "p_{T,Mother1} (GeV/c)"; + std::string mother2_pt_axis_title = "p_{T,Mother2} (GeV/c)"; if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { mass_axis_title = "m_{ee} (GeV/c^{2})"; pair_pt_axis_title = "p_{T,ee} (GeV/c)"; @@ -396,6 +398,8 @@ struct checkMCPairTemplate { // pair info const AxisSpec axis_mass{ConfMllBins, mass_axis_title}; const AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; + const AxisSpec axis_M1_pt{ConfPtllBins, mother1_pt_axis_title}; + const AxisSpec axis_M2_pt{ConfPtllBins, mother2_pt_axis_title}; const AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; const AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; const AxisSpec axis_pt_meson{ConfPtllBins, "p_{T}^{VM} (GeV/c)"}; // for omega, phi meson pT spectra @@ -461,11 +465,11 @@ struct checkMCPairTemplate { static constexpr std::string_view charmed_mesons[] = {"Dplus", "D0", "Dsplus"}; // 411, 421, 431 static constexpr std::string_view anti_charmed_mesons[] = {"Dminus", "D0bar", "Dsminus"}; const int nm_c = sizeof(charmed_mesons) / sizeof(charmed_mesons[0]); - static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xicplus", "Xic0", "Omegac0"}; // 4122, 4232, 4132, 4332 - static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xicminus", "Xic0bar", "Omegac0bar"}; + static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xic0", "Xicplus", "Omegac0"}; // 4122, 4132, 4232, 4332 + static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xic0bar", "Xicminus", "Omegac0bar"}; const int nb_c = sizeof(charmed_baryons) / sizeof(charmed_baryons[0]); static constexpr std::string_view sum_charmed_mesons[] = {"Dpm", "D0", "Dspm"}; - static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xicpm", "Xic0", "Omegac0"}; + static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xic0", "Xicpm", "Omegac0"}; if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -546,7 +550,7 @@ struct checkMCPairTemplate { fRegistry.addClone("Generated/VM/All/", "Generated/VM/Acc/"); // reconstructed pair info - fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lspp/"); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lsmm/"); @@ -586,17 +590,20 @@ struct checkMCPairTemplate { } } - fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lspp/"); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lsmm/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/"); + fRegistry.add("Pair/bbbar/b2l_b2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lspp/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lsmm/"); + // fRegistry.addClone("Pair/bbbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/"); if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -625,57 +632,57 @@ struct checkMCPairTemplate { if (cfgFillSeparateBeautyHadronPairs) { // // (Hb->l) (Hb->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); // } // for (int im1 = 0; im1 < nm_b - 1; im1++) { // for (int im2 = im1 + 1; im2 < nm_b; im2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); // } // } // for (int ib1 = 0; ib1 < nb_b - 1; ib1++) { // for (int ib2 = ib1 + 1; ib2 < nb_b; ib2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); // } // } // for (int im = 0; im < nm_b; im++) { // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); // } // } // // (Hb->Hc->l) (Hb->Hc->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); // } - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); } // for correlated bkg due to mis-identified hadrons, and true combinatorial bkg @@ -1139,11 +1146,11 @@ struct checkMCPairTemplate { } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS @@ -1421,240 +1428,265 @@ struct checkMCPairTemplate { } template - void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight) + void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight, const float ptMotherC1, const float ptMotherC2) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, weight); - } + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); + } + + // Keep the mother-pt axes in the same order as the hadron names in the histogram path. + const auto motherOrder = [](int pdg) { + switch (std::abs(pdg)) { + case 411: + return 0; // Dpm + case 421: + return 1; // D0 + case 431: + return 2; // Dspm + case 4122: + return 3; // Lcpm + case 4132: + return 4; // Xic0 + case 4232: + return 5; // Xicpm + case 4332: + return 6; // Omegac0 + default: + return 7; + } + }; + const bool motherOrderMatches = motherOrder(pdgMotherC1) <= motherOrder(pdgMotherC2); + const float ptMotherFirst = motherOrderMatches ? ptMotherC1 : ptMotherC2; + const float ptMotherSecond = motherOrderMatches ? ptMotherC2 : ptMotherC1; if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -1663,308 +1695,308 @@ struct checkMCPairTemplate { // (Hb->l) (Hb->l)combinations // if (std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kBS) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaB0 && std::abs(pdgMotherC2) == kLambdaB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBS && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kD0 && std::abs(pdgMotherC2) == kD0) { // (Hb->Hc->l) (Hb->Hc->l) combinations // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaCPlus && std::abs(pdgMotherC2) == kLambdaCPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } if (sourceId == 17) { // (Hb->Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 20) { // (Hb->Hc+Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 21) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 22) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -2182,8 +2214,8 @@ struct checkMCPairTemplate { float deltaPt2 = t2mc.pt() - t2.pt(); switch (std::abs(mcmother.pdgCode())) { case 111: - if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt pi0 + if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 + fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2199,8 +2231,8 @@ struct checkMCPairTemplate { fRegistry.fill(HIST("Pair/sm/PromptPi0/lsmm/hDCAz1vsDCAz2"), dcaZinSigma(t1), dcaZinSigma(t2)); } } - } else { // non-prompt pi0 - fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt pi0 + } else { // non-prompt pi0 + fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2219,33 +2251,33 @@ struct checkMCPairTemplate { } break; case 221: - fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta + fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta break; case 331: - fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta' + fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta' break; case 113: - fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // rho + fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // rho break; case 223: - fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // omega + fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omega if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // omeag->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omeag->ee } } break; case 333: - fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // phi + fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // phi->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi->ee } } break; case 443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt J/psi + fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2259,7 +2291,7 @@ struct checkMCPairTemplate { } } } else { - fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt J/psi + fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2276,9 +2308,9 @@ struct checkMCPairTemplate { break; case 100443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt psi2S + fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt psi2S } else { - fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt psi2S + fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt psi2S } break; default: @@ -2287,7 +2319,7 @@ struct checkMCPairTemplate { } else if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && !(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { switch (std::abs(mcmother.pdgCode())) { case 22: - fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // photon conversion + fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // photon conversion if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsPhiV"), phiv, v12.M()); float rxy_gen = std::sqrt(std::pow(t1mc.vx(), 2) + std::pow(t1mc.vy(), 2)); @@ -2305,28 +2337,28 @@ struct checkMCPairTemplate { auto mp2 = mcparticles.iteratorAt(t2mc.mothersIds()[0]); switch (hfee_type) { case static_cast(EM_HFeeType::kCe_Ce): - fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // c2l_c2l + fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // c2l_c2l break; case static_cast(EM_HFeeType::kBe_Be): - fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2l_b2l + fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2l_b2l break; case static_cast(EM_HFeeType::kBCe_BCe): - fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2c2l + fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2c2l break; case static_cast(EM_HFeeType::kBCe_Be_SameB): - fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_sameb + fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_sameb break; case static_cast(EM_HFeeType::kBCe_Be_DiffB): - fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_diffb + fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_diffb break; case static_cast(EM_HFeeType::kBCCe_BCe): - fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2c2l + fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2c2l break; case static_cast(EM_HFeeType::kBCCe_BCCe): - fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2cc2l + fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2cc2l break; case static_cast(EM_HFeeType::kBCCee): - fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2ll + fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2ll break; default: break; From c896be8f33836a60d9e39ccddfddcedfbf6ac072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 29 Sep 2026 22:49:14 +0200 Subject: [PATCH 116/648] [PWGHF] derivedDataCreatorDsToKKPi: Fix PiKK selection (#18108) --- .../derivedDataCreatorDsToKKPi.cxx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index 7ed944efb1c..b1a2562c47a 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -89,7 +89,7 @@ struct HfDerivedDataCreatorDsToKKPi { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassDS}; - static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. + static constexpr int NHypothesesCand{2}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -101,7 +101,7 @@ struct HfDerivedDataCreatorDsToKKPi { using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; - Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 + Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0 || (aod::hf_sel_candidate_ds::isSelDsToPiKK & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; @@ -266,7 +266,7 @@ struct HfDerivedDataCreatorDsToKKPi { int8_t flagMcRec = 0, origin = 0, swapping = 0, flagDecayChanRec = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { - if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl)) { + if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl) && !TESTBIT(candidate.isSelDsToPiKK(), aod::SelectionStep::RecoMl)) { continue; } } @@ -295,11 +295,20 @@ struct HfDerivedDataCreatorDsToKKPi { double const ct = HfHelper::ctDs(candidate); double const y = HfHelper::yDs(candidate); float const massDsToKKPi = HfHelper::invMassDsToKKPi(candidate); - std::vector mlScoresDsToKKPi; + float const massDsToPiKK = HfHelper::invMassDsToPiKK(candidate); + std::vector mlScoresDsToKKPi, mlScoresDsToPiKK; + auto selectionStep = aod::SelectionStep::RecoMl - 1; if constexpr (IsMl) { + selectionStep = aod::SelectionStep::RecoMl; std::copy(candidate.mlProbDsToKKPi().begin(), candidate.mlProbDsToKKPi().end(), std::back_inserter(mlScoresDsToKKPi)); + std::copy(candidate.mlProbDsToPiKK().begin(), candidate.mlProbDsToPiKK().end(), std::back_inserter(mlScoresDsToPiKK)); + } + if (TESTBIT(candidate.isSelDsToKKPi(), selectionStep)) { + fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); + } + if (TESTBIT(candidate.isSelDsToPiKK(), selectionStep)) { + fillTablesCandidate(candidate, 1, massDsToPiKK, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToPiKK); } - fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); } } } From 0897bd73c185e2d88ea538673a992e0a5efc5735 Mon Sep 17 00:00:00 2001 From: rbailhac Date: Wed, 30 Sep 2026 00:29:39 +0200 Subject: [PATCH 117/648] [PWGEM] Add the possibility to look at ITS standalone tracks (#18112) --- PWGEM/Dilepton/Core/DielectronCut.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGEM/Dilepton/Core/DielectronCut.h b/PWGEM/Dilepton/Core/DielectronCut.h index 8ac890a6783..8a41649e992 100644 --- a/PWGEM/Dilepton/Core/DielectronCut.h +++ b/PWGEM/Dilepton/Core/DielectronCut.h @@ -177,9 +177,9 @@ class DielectronCut : public TNamed template bool IsSelectedTrack(TTrack const& track) const { - if (!track.hasITS() || !track.hasTPC()) { - return false; - } + // if (!track.hasITS() || !track.hasTPC()) { + // return false; + // } if (!dont_require_pteta) { if (!IsSelectedTrack(track, DielectronCuts::kTrackPtRange)) { From a54a5fdf8257542157546425892f1c3f4ef48f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:54:17 +0200 Subject: [PATCH 118/648] [Common,PWGCF,PWGDQ,PWGEM,PWGJE,PWGLF] Fix includes (#18113) --- Common/TableProducer/propagationServiceV2.cxx | 2 +- PWGCF/Femto3D/Tasks/multEstimator.cxx | 9 +++++---- .../TableProducer/longrangeMaker.cxx | 1 - PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx | 1 + .../TwoParticleCorrelations/Tasks/flowDecorrelation.cxx | 1 + PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx | 5 +++-- .../TableProducer/photonconversionbuilder.cxx | 4 ++++ .../TableProducer/photonconversionbuilderTmpTable.cxx | 4 ++++ .../TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx | 6 ++++++ .../TableProducer/skimmerPrimaryElectronFromDalitzEE.h | 1 - .../skimmerPrimaryElectronFromDalitzEETmpTable.cxx | 6 ++++++ PWGEM/PhotonMeson/Utils/AnalyticV0.h | 3 ++- PWGEM/PhotonMeson/Utils/PairUtilities.h | 4 ++-- PWGJE/TableProducer/derivedDataSelector.cxx | 1 + PWGJE/TableProducer/derivedDataWriter.cxx | 1 + PWGLF/TableProducer/Common/zdcvector.cxx | 1 - .../TableProducer/Resonances/xi1530kaonreducedtable.cxx | 6 +++++- .../TableProducer/Strangeness/doubleOmegaTreeCreator.cxx | 1 - .../GlobalEventProperties/PseudorapidityDensityMFT.cxx | 1 + PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx | 3 --- 20 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Common/TableProducer/propagationServiceV2.cxx b/Common/TableProducer/propagationServiceV2.cxx index 0fb3f09b6b4..c22544027fa 100644 --- a/Common/TableProducer/propagationServiceV2.cxx +++ b/Common/TableProducer/propagationServiceV2.cxx @@ -37,8 +37,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/PWGCF/Femto3D/Tasks/multEstimator.cxx b/PWGCF/Femto3D/Tasks/multEstimator.cxx index fc284861c28..1d6b9e845eb 100644 --- a/PWGCF/Femto3D/Tasks/multEstimator.cxx +++ b/PWGCF/Femto3D/Tasks/multEstimator.cxx @@ -14,20 +14,19 @@ /// \since 31 May 2023 #include "PWGCF/Femto3D/DataModel/PIDutils.h" -#include "PWGCF/Femto3D/DataModel/singletrackselector.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include -#include -#include #include #include #include @@ -42,9 +41,11 @@ #include #include +#include -#include #include +#include +#include #include #include #include diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx index b49e5a6f554..d9d528c64b0 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx @@ -62,7 +62,6 @@ #include #include -#include #include #include #include diff --git a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx index 54e9e909adf..0458b99b65a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include diff --git a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx index 7ab6ca2f262..5780cffa5e6 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index d31a296d0ac..5d63e2e5e17 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -66,6 +66,7 @@ #include #include +#include #include #include #include @@ -985,7 +986,7 @@ struct TableMakerMC { mcflags = 0; int isig = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { @@ -1239,7 +1240,7 @@ struct TableMakerMC { mcflags = 0; int isig = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx index 714a3ca3f72..6ee033f2817 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx @@ -16,6 +16,10 @@ #include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" +#include +#include +#include + o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx index 6b7927b6340..0346617358c 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx @@ -16,6 +16,10 @@ #include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" +#include +#include +#include + o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx index 97fe86ece34..1efd61d1ab7 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx @@ -15,6 +15,12 @@ #include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" +#include "PID/PIDTOFParamService.h" + +#include +#include +#include + using namespace o2::framework; using namespace o2::aod; using namespace o2::framework::expressions; diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h index ed671564446..d89f3b002a1 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx index 4587e8d6975..32ba2e5f287 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx @@ -15,6 +15,12 @@ #include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" +#include "PID/PIDTOFParamService.h" + +#include +#include +#include + using namespace o2::framework; using namespace o2::aod; using namespace o2::framework::expressions; diff --git a/PWGEM/PhotonMeson/Utils/AnalyticV0.h b/PWGEM/PhotonMeson/Utils/AnalyticV0.h index 90bfb05b272..ede542509cf 100644 --- a/PWGEM/PhotonMeson/Utils/AnalyticV0.h +++ b/PWGEM/PhotonMeson/Utils/AnalyticV0.h @@ -22,10 +22,11 @@ #include #include -#include #include #include +#include + #include #include #include diff --git a/PWGEM/PhotonMeson/Utils/PairUtilities.h b/PWGEM/PhotonMeson/Utils/PairUtilities.h index e4deb542747..87c45f4ad99 100644 --- a/PWGEM/PhotonMeson/Utils/PairUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PairUtilities.h @@ -21,9 +21,9 @@ #include #include -#include +#include // IWYU pragma: keep #include -#include +#include // IWYU pragma: keep #include #include diff --git a/PWGJE/TableProducer/derivedDataSelector.cxx b/PWGJE/TableProducer/derivedDataSelector.cxx index 25d4e69d3ba..b82419f7079 100644 --- a/PWGJE/TableProducer/derivedDataSelector.cxx +++ b/PWGJE/TableProducer/derivedDataSelector.cxx @@ -21,6 +21,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetSubtraction.h" #include #include diff --git a/PWGJE/TableProducer/derivedDataWriter.cxx b/PWGJE/TableProducer/derivedDataWriter.cxx index 5cb9b9c8f10..d0146f7c294 100644 --- a/PWGJE/TableProducer/derivedDataWriter.cxx +++ b/PWGJE/TableProducer/derivedDataWriter.cxx @@ -28,6 +28,7 @@ #include "PWGJE/DataModel/JetReducedDataDQ.h" #include "PWGJE/DataModel/JetReducedDataHF.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/V0SelectorTables.h" diff --git a/PWGLF/TableProducer/Common/zdcvector.cxx b/PWGLF/TableProducer/Common/zdcvector.cxx index d1c5eb20766..852834dcb8b 100644 --- a/PWGLF/TableProducer/Common/zdcvector.cxx +++ b/PWGLF/TableProducer/Common/zdcvector.cxx @@ -46,7 +46,6 @@ #include #include #include -#include #include using namespace o2; diff --git a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx index 149879b75d5..9565711b486 100644 --- a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx +++ b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx @@ -27,14 +27,18 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx index 9ca484967f6..8c081656ba7 100644 --- a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -21,7 +21,6 @@ #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" #include #include diff --git a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx index 83d6ef873c2..0fddd6d62d9 100644 --- a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx @@ -50,6 +50,7 @@ #include #include #include +#include #include #include diff --git a/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx index cfc54fd89fa..bf84dd619ba 100644 --- a/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx @@ -50,7 +50,6 @@ #include "Common/Core/RecoDecay.h" #include -#include #include #include #include @@ -64,8 +63,6 @@ #include #include -#include -#include #include #include From 1163fd6d22302a837a3c0a4e4af88962114ec7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:14:57 +0200 Subject: [PATCH 119/648] [Common] Fix cpplint errors (#18095) --- Common/CCDB/AnalysisCCDBLinkDef.h | 5 +---- Common/Core/AnalysisCoreLinkDef.h | 5 +---- Common/Core/EventFilteringUtilsLinkDef.h | 2 ++ Common/Core/FFitWeightsLinkDef.h | 5 +---- Common/DataModel/Centrality.h | 4 ++++ Common/Tools/Multiplicity/multToolsLinkDef.h | 2 ++ Common/Tools/trackSelectionRequestLinkDef.h | 2 ++ 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Common/CCDB/AnalysisCCDBLinkDef.h b/Common/CCDB/AnalysisCCDBLinkDef.h index bb2da231284..b8253021f88 100644 --- a/Common/CCDB/AnalysisCCDBLinkDef.h +++ b/Common/CCDB/AnalysisCCDBLinkDef.h @@ -14,8 +14,7 @@ /// /// \author Evgeny Kryshen -#ifndef COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ -#define COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -24,5 +23,3 @@ #pragma link C++ class EventSelectionParams + ; #pragma link C++ class TriggerAliases + ; #pragma link C++ class std::map < uint64_t, uint32_t> + ; - -#endif // COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ diff --git a/Common/Core/AnalysisCoreLinkDef.h b/Common/Core/AnalysisCoreLinkDef.h index b376ccc7f10..21eb52ac586 100644 --- a/Common/Core/AnalysisCoreLinkDef.h +++ b/Common/Core/AnalysisCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef COMMON_CORE_ANALYSISCORELINKDEF_H_ -#define COMMON_CORE_ANALYSISCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -25,5 +24,3 @@ #pragma link C++ class o2::pid::tpc::Response + ; #pragma link C++ class OrbitRange + ; - -#endif // COMMON_CORE_ANALYSISCORELINKDEF_H_ diff --git a/Common/Core/EventFilteringUtilsLinkDef.h b/Common/Core/EventFilteringUtilsLinkDef.h index 3f029b8aa9a..394c051784f 100644 --- a/Common/Core/EventFilteringUtilsLinkDef.h +++ b/Common/Core/EventFilteringUtilsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Core/FFitWeightsLinkDef.h b/Common/Core/FFitWeightsLinkDef.h index 1960be8f4df..3c604376da9 100644 --- a/Common/Core/FFitWeightsLinkDef.h +++ b/Common/Core/FFitWeightsLinkDef.h @@ -14,9 +14,6 @@ /// /// \author Joachim C. K. B. Hansen -#ifndef COMMON_CORE_FFITWEIGHTSLINKDEF_H_ -#define COMMON_CORE_FFITWEIGHTSLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link C++ class FFitWeights + ; - -#endif // COMMON_CORE_FFITWEIGHTSLINKDEF_H_ diff --git a/Common/DataModel/Centrality.h b/Common/DataModel/Centrality.h index 3bdf839eed8..a7f073fde92 100644 --- a/Common/DataModel/Centrality.h +++ b/Common/DataModel/Centrality.h @@ -100,18 +100,22 @@ using BCCentFT0C = BCCentFT0Cs::iterator; template concept HasRun2Centrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centRun2V0M() }; { t.centRun2CL0() }; { t.centRun2CL1() }; + // NOLINTEND(readability/braces) }; template concept HasCentrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centFV0A() }; { t.centFT0M() }; { t.centFT0A() }; { t.centFT0C() }; { t.centNTPV() }; + // NOLINTEND(readability/braces) }; } // namespace o2::aod diff --git a/Common/Tools/Multiplicity/multToolsLinkDef.h b/Common/Tools/Multiplicity/multToolsLinkDef.h index f8740f5afcb..2f2cd969c20 100644 --- a/Common/Tools/Multiplicity/multToolsLinkDef.h +++ b/Common/Tools/Multiplicity/multToolsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Tools/trackSelectionRequestLinkDef.h b/Common/Tools/trackSelectionRequestLinkDef.h index ae9e95b415b..93b201ce15a 100644 --- a/Common/Tools/trackSelectionRequestLinkDef.h +++ b/Common/Tools/trackSelectionRequestLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; From 27beb07a4738bbd4f7dc7014d00738c5fd4c4e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:27:21 +0200 Subject: [PATCH 120/648] [PWGLF] Fix cpplint errors (#18102) --- PWGLF/TableProducer/Nuspex/hyhe4builder.cxx | 1 + PWGLF/TableProducer/Resonances/cksspinalignment.cxx | 9 ++++----- PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx | 1 + .../Strangeness/strangederivedbuilder.cxx | 4 ++-- PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx | 1 + PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx | 1 + PWGLF/Tasks/Resonances/lambdav2.cxx | 9 +++------ PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx | 10 +++++----- PWGLF/Utils/collisionCutsGroupLinkDef.h | 5 +---- PWGLF/Utils/v0SelectionGroupLinkDef.h | 5 +---- 10 files changed, 20 insertions(+), 26 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx index f4ddba0fdbe..50e450b7936 100644 --- a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx +++ b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx @@ -47,6 +47,7 @@ #include #include #include +#include using namespace std; using namespace o2; diff --git a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx index c52483f28fe..8a62bb13ad4 100644 --- a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx +++ b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx @@ -139,11 +139,10 @@ struct cksspinalignment { } bool keepEvent(uint64_t eventIndex) const - { /* - if (cfgKeepFraction >= 1.0f) { - return true; - } - */ + { + // if (cfgKeepFraction >= 1.0f) { + // return true; + // } // SplitMix64 pseudo-random hash uint64_t x = eventIndex + 0x9e3779b97f4a7c15ULL; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; diff --git a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx index 6c20a736eda..e1329901149 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx @@ -61,6 +61,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx index 57ebfa938a0..e4f22fddc9f 100644 --- a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx @@ -579,8 +579,8 @@ struct strangederivedbuilder { // auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); bool strange = V0Table_thisColl.size() > 0 || CascTable_thisColl.size() > 0 || - KFCascTable_thisColl.size() > 0 /* || - TraCascTable_thisColl.size() > 0*/ + KFCascTable_thisColl.size() > 0 + // ||TraCascTable_thisColl.size() > 0 ; auto bc = collision.template bc_as(); diff --git a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx index 50dd28efbdc..ce410a7dd57 100644 --- a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx +++ b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx @@ -44,6 +44,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx index 1d2d5c11ec0..6ed90f63f57 100644 --- a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx @@ -47,6 +47,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/Tasks/Resonances/lambdav2.cxx b/PWGLF/Tasks/Resonances/lambdav2.cxx index 59d6e11bd2c..cf1cdfb8ef3 100644 --- a/PWGLF/Tasks/Resonances/lambdav2.cxx +++ b/PWGLF/Tasks/Resonances/lambdav2.cxx @@ -209,8 +209,7 @@ struct lambdav2 { template bool SelectionPID(const T& candidate, int PID) { - if (PID == 0) // pion - { + if (PID == 0) { // pion auto combPIDPi = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPCPi) { return true; @@ -218,8 +217,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDPi < nsigmaCutTOFPi) { return true; } - } else if (PID == 1) // kaon - { + } else if (PID == 1) { // kaon auto combPIDKa = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.45 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPCKa) { return true; @@ -227,8 +225,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDKa < nsigmaCutTOFKa) { return true; } - } else // proton - { + } else { // proton auto combPIDPr = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCPr) { return true; diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index 81b6ec46eb1..48162411c03 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -1303,7 +1303,7 @@ struct PhiStrangeCorrelation { histos.fill(histoKey, collision.posZ(), mcCollision.centFT0M(), assoc.pt(), assoc.y()); } - }; + }; // NOLINT(readability/braces) fillRecoAssocSpecies(makeAssocInput(k0sReduced), HIST("k0s/h4K0SMCReco")); fillRecoAssocSpecies(makeAssocInput(lambdaReduced), HIST("lambda/h4LambdaMCReco")); @@ -1337,7 +1337,7 @@ struct PhiStrangeCorrelation { for (const auto& mcParticle : mcParticlesThisMcColl) { if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { continue; - }; + } auto fillGenHistos = [&](auto h3Key, auto h4Key) { histos.fill(h3Key, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); @@ -1352,7 +1352,7 @@ struct PhiStrangeCorrelation { } fillGenHistos(h3Key, h4Key); - }; + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: @@ -1404,13 +1404,13 @@ struct PhiStrangeCorrelation { for (const auto& mcParticle : mcParticles) { if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { continue; - }; + } auto fillPartCollection = [&]() { if (activeCorrelationTypes->at(PartType) && isGenSpeciesValid(mcParticle, minPtMcGenConfigs.bypassPtCut.value)) { assocParticles[PartType].emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); } - }; + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: diff --git a/PWGLF/Utils/collisionCutsGroupLinkDef.h b/PWGLF/Utils/collisionCutsGroupLinkDef.h index e327556fbd2..a6aa000a66e 100644 --- a/PWGLF/Utils/collisionCutsGroupLinkDef.h +++ b/PWGLF/Utils/collisionCutsGroupLinkDef.h @@ -17,8 +17,7 @@ /// /// \author Bong-Hwi Lim -#ifndef PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ -#define PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -30,5 +29,3 @@ #pragma link C++ class std::vector < int> + ; #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ diff --git a/PWGLF/Utils/v0SelectionGroupLinkDef.h b/PWGLF/Utils/v0SelectionGroupLinkDef.h index 3c81add2fc2..c40840a0625 100644 --- a/PWGLF/Utils/v0SelectionGroupLinkDef.h +++ b/PWGLF/Utils/v0SelectionGroupLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ -#define PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < double> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ From 64150b1da43f2ead703ac955621c09ad66132625 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Wed, 30 Sep 2026 19:11:21 +0900 Subject: [PATCH 121/648] [PWGLF] Resonance: Add constraint PDG values and MC functionalities in Omega2012 (#17978) Co-authored-by: Bong-Hwi Lim --- .../Resonances/resonanceModuleInitializer.cxx | 8 +- PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 94 ++++++++++++++----- PWGLF/Tasks/Resonances/xi1820Analysis.cxx | 16 ++-- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx index d5a43cf1028..8a07e7365b3 100644 --- a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx @@ -110,9 +110,9 @@ struct ResonanceModuleInitializer { static constexpr int PdgLambda1520 = o2::constants::physics::Pdg::kLambda1520_Py; static constexpr int Xi1530Code = 3324; static constexpr int PdgK1Plus1270 = o2::constants::physics::Pdg::kK1_1270Plus; - static constexpr int Xi1820NeutralCode = 123314; - static constexpr int Xi1820MinusCode = 123324; - static constexpr int Omega2012MinusCode = 123334; + static constexpr int Xi1820MinusCode = o2::constants::physics::Pdg::kXi1820Minus; + static constexpr int Xi1820ZeroCode = o2::constants::physics::Pdg::kXi1820Zero; + static constexpr int Omega2012MinusCode = o2::constants::physics::Pdg::kOmega2012Minus; static constexpr int PdgProton = PDG_t::kProton; static constexpr int PdgLambda0 = PDG_t::kLambda0; static constexpr int PdgXiMinus = PDG_t::kXiMinus; @@ -232,7 +232,7 @@ struct ResonanceModuleInitializer { || (nabs(aod::mcparticle::pdgCode) == PdgLambda1520) // Lambda(1520) || (nabs(aod::mcparticle::pdgCode) == Xi1530Code) // Xi(1530)0 || (nabs(aod::mcparticle::pdgCode) == PdgK1Plus1270) // K1(1270)+ - || (nabs(aod::mcparticle::pdgCode) == Xi1820NeutralCode) // Xi(1820)0 + || (nabs(aod::mcparticle::pdgCode) == Xi1820ZeroCode) // Xi(1820)0 || (nabs(aod::mcparticle::pdgCode) == Xi1820MinusCode) // Xi(1820)- || (nabs(aod::mcparticle::pdgCode) == Omega2012MinusCode) // Omega(2012)- || (nabs(aod::mcparticle::pdgCode) == PdgProton) // proton diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 2f9db446f51..bd1a01cfa8b 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -50,7 +50,6 @@ struct Omega2012Analysis { static constexpr float kSmallNumber = 1e-10f; // Small number to avoid division by zero static constexpr float kMaxDCAV0ToPV = 1.0f; // Maximum DCA of V0 to PV static constexpr int kNumExpectedDaughters = 2; // Expected number of daughters for 2-body decay - static constexpr int kPlaceholderPdgCode = 3335; SliceCache cache; Preslice perResoCollisionCasc = aod::resodaughter::resoCollisionId; Preslice perResoCollisionV0 = aod::resodaughter::resoCollisionId; @@ -73,6 +72,8 @@ struct Omega2012Analysis { Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply Reco INEL>0 event selection"}; + Configurable cMCINELgt0{"cMCINELgt0", false, "Require generator INEL>0 in MC processes"}; + Configurable cMCVtxIn10{"cMCVtxIn10", false, "Require generator |vertex z| < 10 cm in MC processes"}; Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; @@ -157,6 +158,10 @@ struct Omega2012Analysis { // PDG masses double massK0 = MassK0Short; + // Module-initializer collision tables (matches the resonance-module-initializer producer) + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCollisions = soa::Join; + using BinningTypeVertexContributor = ColumnBinningPolicy; BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; @@ -686,13 +691,13 @@ struct Omega2012Analysis { template struct SelectedXiCandidate { CandidateT candidate; - std::array daughterIds; + std::array daughterIds{}; }; template struct SelectedK0sCandidate { CandidateT candidate; - std::array daughterIds; + std::array daughterIds{}; }; template @@ -888,7 +893,7 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processDummy, "Process Dummy", true); - void processData(const aod::ResoCollision& collision, + void processData(ResoCollisions::iterator const& collision, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { @@ -899,13 +904,13 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processData, "Process Event for data", false); - void processMixedEvent(const aod::ResoCollisions& collisions, + void processMixedEvent(ResoCollisions const& collisions, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { auto cascV0sTuple = std::make_tuple(resocasc, resov0s); - Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; for (const auto& [collision1, casc1, collision2, v0s2] : pairs) { if (cRecoINELgt0 && (!collision1.isRecINELgt0() || !collision2.isRecINELgt0())) @@ -947,20 +952,56 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processMixedEvent, "Process Mixed Event", false); - // MC processes - placeholder for future implementation - void processMC(const aod::ResoCollision& /*collision*/, - aod::ResoCascades const& /*resocasc*/, - aod::ResoV0s const& /*resov0s*/, - aod::McParticles const& /*mcParticles*/) + // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother + void processMC(ResoMCCollisions::iterator const& collision, + soa::Join const& resocasc, + soa::Join const& resov0s) { - // TODO: Implement MC truth matching for Xi + K0s - // - Match reconstructed Xi to MC Xi - // - Match reconstructed K0s to MC K0s - // - Fill MC truth histograms - // - Fill reconstruction efficiency histograms - // - Check if the Xi and K0s come from same Omega(2012) mother + if (cRecoINELgt0 && !collision.isRecINELgt0()) + return; + if (cMCINELgt0 && !collision.isINELgt0()) + return; + if (cMCVtxIn10 && !collision.isVtxIn10()) + return; + + int nCascAfterCuts = 0; + int nV0sAfterCuts = 0; + auto selectedXis = selectXiCandidates(resocasc, nCascAfterCuts); + auto selectedK0s = selectK0sCandidates(collision, resov0s, nV0sAfterCuts); + + for (const auto& selectedXi : selectedXis) { + const auto& xi = selectedXi.candidate; + if (std::abs(xi.pdgCode()) != kXiMinus) + continue; + + for (const auto& selectedK0 : selectedK0s) { + const auto& v0 = selectedK0.candidate; + + if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) + continue; + if (std::abs(v0.pdgCode()) != kK0Short) + continue; + if (xi.motherId() < 0 || xi.motherId() != v0.motherId()) + continue; + if (std::abs(xi.motherPDG()) != kOmega2012Minus || xi.motherPDG() != v0.motherPDG()) + continue; + + ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; + pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); + pRes = pXi + pK0s; + + if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) + continue; + + histos.fill(HIST("MC/hMCRecOmega2012Pt"), pRes.Pt()); + histos.fill(HIST("MC/hMCRecOmega2012PtEta"), pRes.Pt(), pRes.Eta()); + histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + } + } } - PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); void processMCGenerated(aod::McParticles const& mcParticles) { @@ -968,11 +1009,10 @@ struct Omega2012Analysis { // This resonance decays to Xi + K0s for (const auto& mcParticle : mcParticles) { - // Look for Omega(2012) - PDG code may vary by generator + // Look for Omega(2012) int pdg = mcParticle.pdgCode(); - // TODO: Update the PDG code library to include Omega(2012) codes - if (std::abs(pdg) != kPlaceholderPdgCode) + if (std::abs(pdg) != kOmega2012Minus) continue; // Fill generated level histograms @@ -1015,10 +1055,16 @@ struct Omega2012Analysis { (std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) { histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM); histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + + const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus; + const auto& pXiTruth = isDaughter1Xi ? p1 : p2; + const auto& pK0sTruth = isDaughter1Xi ? p2 : p1; + histos.fill(HIST("MC/hMCTrueXiPt"), pXiTruth.Pt()); + histos.fill(HIST("MC/hMCTrueK0sPt"), pK0sTruth.Pt()); } } } - PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles", false); // Fill function for 3-body decay analysis template @@ -1122,7 +1168,7 @@ struct Omega2012Analysis { } // 3-body decay analysis: Xi + pi + K0s with ResoTracks - void processThreeBodyWithTracks(const aod::ResoCollision& collision, + void processThreeBodyWithTracks(ResoCollisions::iterator const& collision, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s, aod::ResoTracks const& resotracks, @@ -1136,7 +1182,7 @@ struct Omega2012Analysis { PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithTracks, "Process 3-body decay with ResoTracks", false); // 3-body decay analysis: Xi + pi + K0s with ResoMicroTracks - void processThreeBodyWithMicroTracks(const aod::ResoCollision& collision, + void processThreeBodyWithMicroTracks(ResoCollisions::iterator const& collision, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s, aod::ResoMicroTracks const& resomicrotracks, diff --git a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx index 12b89e89024..e4bb98d8663 100644 --- a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx +++ b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx @@ -103,8 +103,6 @@ struct Xi1820Analysis { // Constants static constexpr float SmallMomentumDenominator = 1e-10f; // Small value to avoid division by zero - static constexpr int PdgChargedXi1820 = 123314; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) - static constexpr int PdgXi1820Zero = 123324; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) static constexpr int ExpectedDaughters = 2; // Expected number of daughters for two-body decay // Axes @@ -1116,7 +1114,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (v0.motherPDG() != -PdgChargedXi1820 || kaon.motherPDG() != v0.motherPDG()) { + if (v0.motherPDG() != -Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { continue; } if (kaon.pdgCode() != PDG_t::kKPlus || v0.pdgCode() != PDG_t::kLambda0Bar) { @@ -1193,7 +1191,7 @@ struct Xi1820Analysis { } } if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (v0.motherPDG() != PdgChargedXi1820 || kaon.motherPDG() != v0.motherPDG()) { + if (v0.motherPDG() != Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { continue; } if (kaon.pdgCode() != PDG_t::kKMinus || v0.pdgCode() != PDG_t::kLambda0) { @@ -1398,7 +1396,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (lambda.motherPDG() != PdgXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + if (lambda.motherPDG() != Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { continue; } if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0) { @@ -1468,7 +1466,7 @@ struct Xi1820Analysis { } if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (lambda.motherPDG() != -PdgXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + if (lambda.motherPDG() != -Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { continue; } if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0Bar) { @@ -1770,7 +1768,7 @@ struct Xi1820Analysis { histos.fill(HIST("multQA/h2MultCentMC"), inCent, multiplicity); for (const auto& part : resoParents) { // loop over all pre-filtered Gen particle on selected events auto pdgMother = part.pdgCode(); - if (std::abs(pdgMother) != PdgChargedXi1820 && std::abs(pdgMother) != PdgXi1820Zero) { + if (std::abs(pdgMother) != Pdg::kXi1820Minus && std::abs(pdgMother) != Pdg::kXi1820Zero) { continue; } if (!passesRapidity(part.y())) { @@ -1780,7 +1778,7 @@ struct Xi1820Analysis { auto daughter1PDG = part.daughterPDG1(); auto daughter2PDG = part.daughterPDG2(); - if (std::abs(pdgMother) == PdgChargedXi1820) { // Explicity check for the safety. + if (std::abs(pdgMother) == Pdg::kXi1820Minus) { // Explicity check for the safety. // K- + Anti-Lambda, K+ + Anti-Lambda if ((daughter1PDG == PDG_t::kKMinus && daughter2PDG == PDG_t::kLambda0) || (daughter1PDG == PDG_t::kLambda0 && daughter2PDG == PDG_t::kKMinus)) { @@ -1813,7 +1811,7 @@ struct Xi1820Analysis { int pdg = mcParticle.pdgCode(); // Select only the configured charged and neutral Xi(1820) states. - if (std::abs(pdg) != PdgChargedXi1820 && std::abs(pdg) != PdgXi1820Zero) { + if (std::abs(pdg) != Pdg::kXi1820Minus && std::abs(pdg) != Pdg::kXi1820Zero) { continue; } From ee54091eaf516da731d395025beb42b270407817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:31:36 +0200 Subject: [PATCH 122/648] [PWGDQ] Fix cpplint errors (#18098) --- PWGDQ/Core/MCSignal.h | 39 ++++++++++++++------------- PWGDQ/Macros/fit_library/CB2Pdf.h | 2 +- PWGDQ/Macros/fit_library/ExpPdf.h | 2 +- PWGDQ/Macros/fit_library/GausPdf.h | 2 +- PWGDQ/Macros/fit_library/Pol4ExpPdf.h | 2 +- PWGDQ/Macros/fit_library/VWGPdf.h | 2 +- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/PWGDQ/Core/MCSignal.h b/PWGDQ/Core/MCSignal.h index 476fb4b8751..89f98c27d6f 100644 --- a/PWGDQ/Core/MCSignal.h +++ b/PWGDQ/Core/MCSignal.h @@ -331,25 +331,26 @@ bool MCSignal::CheckProng(int i, bool checkSources, const T& track) break; } } - } /*else { // check generation in time - if (!currentMCParticle.has_daughters()) { - return false; - } - const auto& daughtersSlice = currentMCParticle.template daughters_as

(); - for (auto& d : daughtersSlice) { - if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - pdgInHistory.emplace_back(d.pdgCode()); - break; - } - if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - return false; - } - ith++; - if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. - break; - } - } - }*/ + } + // else { // check generation in time + // if (!currentMCParticle.has_daughters()) { + // return false; + // } + // const auto& daughtersSlice = currentMCParticle.template daughters_as

(); + // for (auto& d : daughtersSlice) { + // if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // pdgInHistory.emplace_back(d.pdgCode()); + // break; + // } + // if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // return false; + // } + // ith++; + // if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. + // break; + // } + // } + // } } if (pdgInHistory.size() != nIncludedPDG) { // vector has as many entries as mothers (daughters) defined for prong return false; diff --git a/PWGDQ/Macros/fit_library/CB2Pdf.h b/PWGDQ/Macros/fit_library/CB2Pdf.h index 23cb291dcdd..d5b5326813e 100644 --- a/PWGDQ/Macros/fit_library/CB2Pdf.h +++ b/PWGDQ/Macros/fit_library/CB2Pdf.h @@ -25,7 +25,7 @@ class CB2Pdf : public RooAbsPdf { public: - CB2Pdf(){}; + CB2Pdf() {} CB2Pdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/ExpPdf.h b/PWGDQ/Macros/fit_library/ExpPdf.h index 306b0aea210..7495176d942 100644 --- a/PWGDQ/Macros/fit_library/ExpPdf.h +++ b/PWGDQ/Macros/fit_library/ExpPdf.h @@ -25,7 +25,7 @@ class ExpPdf : public RooAbsPdf { public: - ExpPdf(){}; + ExpPdf() {} ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/GausPdf.h b/PWGDQ/Macros/fit_library/GausPdf.h index 80a1e956817..22e07a2d522 100644 --- a/PWGDQ/Macros/fit_library/GausPdf.h +++ b/PWGDQ/Macros/fit_library/GausPdf.h @@ -25,7 +25,7 @@ class GausPdf : public RooAbsPdf { public: - GausPdf(){}; + GausPdf() {} GausPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h index 67202dbe5ac..baca326c1f2 100644 --- a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h +++ b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h @@ -28,7 +28,7 @@ TString nameParameters[] = {"p0", "p1", "p2", "p3", "p4", "p5", "N_bkg", "N_sig" class Pol4ExpPdf : public RooAbsPdf { public: - Pol4ExpPdf(){}; + Pol4ExpPdf() {} Pol4ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _P0, diff --git a/PWGDQ/Macros/fit_library/VWGPdf.h b/PWGDQ/Macros/fit_library/VWGPdf.h index 3ed4f2f81e6..af8c6cba9eb 100644 --- a/PWGDQ/Macros/fit_library/VWGPdf.h +++ b/PWGDQ/Macros/fit_library/VWGPdf.h @@ -25,7 +25,7 @@ class VWGPdf : public RooAbsPdf { public: - VWGPdf(){}; + VWGPdf() {} VWGPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, From 74699d960cf12f75db8d0dfc657394dd0d56ebc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:35:14 +0200 Subject: [PATCH 123/648] [PWGEM] Fix cpplint errors (#18099) --- PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h | 5 +---- PWGEM/Dilepton/DataModel/EvSelFlags.h | 6 +++--- PWGEM/Tasks/phosCalibration.cxx | 1 + 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h index fe78534478e..a0ba6d2b630 100644 --- a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h +++ b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ -#define PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -20,5 +19,3 @@ #pragma link C++ class DielectronCut + ; #pragma link C++ class DimuonCut + ; #pragma link C++ class EMTrackCut + ; - -#endif // PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ diff --git a/PWGEM/Dilepton/DataModel/EvSelFlags.h b/PWGEM/Dilepton/DataModel/EvSelFlags.h index 0b05ff288eb..ae4d65a548b 100644 --- a/PWGEM/Dilepton/DataModel/EvSelFlags.h +++ b/PWGEM/Dilepton/DataModel/EvSelFlags.h @@ -9,8 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGEM_DILEPTON_CORE_EVSELFLAGS_H -#define PWGEM_DILEPTON_CORE_EVSELFLAGS_H +#ifndef PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ +#define PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ namespace o2::aod::emevsel { @@ -37,4 +37,4 @@ enum EventSelectionFlags { }; } // namespace o2::aod::emevsel -#endif // PWGEM_DILEPTON_CORE_EVSELFLAGS_H +#endif // PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ diff --git a/PWGEM/Tasks/phosCalibration.cxx b/PWGEM/Tasks/phosCalibration.cxx index 5e62addf7dc..0f244127a0e 100644 --- a/PWGEM/Tasks/phosCalibration.cxx +++ b/PWGEM/Tasks/phosCalibration.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include using namespace o2; From e7bbacacb3cf892adffe9626898c52d9ceb75e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:35:46 +0200 Subject: [PATCH 124/648] [PWGLF,PWGMM] Fix cpplint errors (#18103) --- PWGMM/Mult/TableProducer/percentiles.cxx | 1 + PWGMM/Mult/Tasks/dndeta.cxx | 1 + PWGMM/Mult/Tasks/puremc-dndeta.cxx | 1 + 3 files changed, 3 insertions(+) diff --git a/PWGMM/Mult/TableProducer/percentiles.cxx b/PWGMM/Mult/TableProducer/percentiles.cxx index d34802ab2ae..bba44ad0243 100644 --- a/PWGMM/Mult/TableProducer/percentiles.cxx +++ b/PWGMM/Mult/TableProducer/percentiles.cxx @@ -32,6 +32,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGMM/Mult/Tasks/dndeta.cxx b/PWGMM/Mult/Tasks/dndeta.cxx index ae99e5f8205..6f69f60e42c 100644 --- a/PWGMM/Mult/Tasks/dndeta.cxx +++ b/PWGMM/Mult/Tasks/dndeta.cxx @@ -42,6 +42,7 @@ #include #include +#include #include using namespace o2; diff --git a/PWGMM/Mult/Tasks/puremc-dndeta.cxx b/PWGMM/Mult/Tasks/puremc-dndeta.cxx index f634755a3dd..1891459aa4f 100644 --- a/PWGMM/Mult/Tasks/puremc-dndeta.cxx +++ b/PWGMM/Mult/Tasks/puremc-dndeta.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include using namespace o2; From 20c5eef44a7d94688881e32a43cfa8c5259acba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:40:27 +0200 Subject: [PATCH 125/648] [DPG] Fix cpplint errors (#18096) --- DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx | 25 +++++++++------------- DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx | 4 ++-- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx index 5e6cffea36d..acb75778be9 100644 --- a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx +++ b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx @@ -1087,7 +1087,6 @@ struct DetectorOccupancyQaTask { // July 2025: more for data vs MC: if (track.hasTPC() && occupancy >= 0 && confAddBasicQAhistos) { float pt = track.pt(); - // pt 0.2-0.5 if (pt > 0.2 && pt < 0.5) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_02_05"), track.tpcNClsFindable(), occupancy); @@ -1098,9 +1097,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_02_05"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_02_05"), track.tpcChi2NCl(), occupancy); } - } - // pt 0.5-1.0 - else if (pt > 0.5 && pt < 1.0) { + } else if (pt > 0.5 && pt < 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_05_10"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); @@ -1110,9 +1107,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_05_10"), track.tpcChi2NCl(), occupancy); } - } - // pt > 1.0 - else if (pt > 1.0) { + } else if (pt > 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_above1_0"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_above1_0"), track.tpcNClsFound(), occupancy); @@ -1383,8 +1378,8 @@ struct DetectorOccupancyQaTask { int tpcNClsFound = track.tpcNClsFound(); int tpcNClsCrossedRows = track.tpcNClsCrossedRows(); - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1401,8 +1396,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsFound_pos"), tpcNClsFound); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsCrossedRows_pos"), tpcNClsCrossedRows); } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); @@ -1449,8 +1444,8 @@ struct DetectorOccupancyQaTask { } // end of TPC good global // July 2025: for data vs MC kine distr comparison - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1463,8 +1458,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/hPhi_posModifiedQA"), phi); } } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); diff --git a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx index b85b1d3b42c..3a5579ebe51 100644 --- a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx +++ b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx @@ -991,9 +991,9 @@ struct LightIonsEvSelQa { // vZ diff (FT0 vs by tracks) bool badVzDiff = 0; - if (confUseDiffVzCutFromEvSel) + if (confUseDiffVzCutFromEvSel) { badVzDiff = !col.selection_bit(kIsGoodZvtxFT0vsPV); - else { // tune by hand + } else { // tune by hand float meanDiff = 0.0; // cm // O-O if (lastRunNumber == 564356) From 51b8cc7439e47c3cd9f3284ffc086de3213b6251 Mon Sep 17 00:00:00 2001 From: Jinhyun Park <125851562+jinhyunni@users.noreply.github.com> Date: Wed, 30 Sep 2026 22:07:57 +0900 Subject: [PATCH 126/648] [PWGHF] Omega0: Added appropriate kfBachRej pid (#18084) Co-authored-by: Jinhyun Park --- PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx index 6f37217b7f6..4ee67bd2ec1 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx @@ -234,7 +234,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { Service ccdb{}; Service pdgdb{}; - o2::base::MatLayerCylSet* lut; + o2::base::MatLayerCylSet* lut{}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; // DCAFitter @@ -259,7 +259,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { HfEventSelection hfEvSel; // PDG Id of daughter tracks & V0s & cascades & charm baryons - Used in KFParticle - int pdgIdOfV0DauPos{}, pdgIdOfV0DauNeg{}, pdgIdOfBach{}, pdgIdOfCharmBach{}; + int pdgIdOfV0DauPos{}, pdgIdOfV0DauNeg{}, pdgIdOfBach{}, pdgIdOfBachRej{}, pdgIdOfCharmBach{}; int pdgIdOfV0{}, pdgIdOfCascade{}, pdgIdOfCharmBaryon{}; // Track PID: PID value of tracks defined under o2::track::PID namespace @@ -315,6 +315,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kPiMinus; + pdgIdOfBachRej = 0; // -> Not used in XiPi channel pdgIdOfCharmBach = kPiPlus; pdgIdOfV0 = kLambda0; @@ -332,6 +333,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kPiPlus; pdgIdOfV0 = kLambda0; @@ -349,6 +351,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kKPlus; pdgIdOfV0 = kLambda0; @@ -370,6 +373,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { LOGF(info, " PDG ID of V0 negative daughter: %d", pdgIdOfV0DauNeg); LOGF(info, " PDG ID of V0: %d", pdgIdOfV0); LOGF(info, " PDG ID of Bachelor: %d", pdgIdOfBach); + LOGF(info, " PDG ID of Bachelor Rej: %d", pdgIdOfBachRej); LOGF(info, " PDG ID of Cascade: %d", pdgIdOfCascade); LOGF(info, " PDG ID of Charm Bachelor: %d", pdgIdOfCharmBach); LOGF(info, " PDG ID of Charm Baryon: %d", pdgIdOfCharmBaryon); @@ -965,7 +969,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { KFParticle kfPos(kfTrack0, (isAnti ? -pdgIdOfV0DauNeg : pdgIdOfV0DauPos)); KFParticle kfNeg(kfTrack1, (isAnti ? -pdgIdOfV0DauPos : pdgIdOfV0DauNeg)); KFParticle kfBach(kfTrackBach, (isAnti ? -pdgIdOfBach : pdgIdOfBach)); - KFParticle kfBachRej(kfTrackBach, (isAnti ? -pdgIdOfBach : pdgIdOfBach)); // Rej -> Used for Omegac0->OmegaPi only + KFParticle kfBachRej(kfTrackBach, (isAnti ? -pdgIdOfBachRej : pdgIdOfBachRej)); // Rej -> Used for Omegac0->OmegaPi only // ~~~~~~~Construct V0 with KF~~~~~~~ const KFParticle* v0Daughters[2] = {&kfPos, &kfNeg}; From cd5617108328b8aca9aeadda3dbb1f02eee4d66a Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Wed, 30 Sep 2026 08:11:56 -0500 Subject: [PATCH 127/648] [PWGHF] Fix redundant initialization in charm baryon candidate creator (#18109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vít Kučera <26327373+vkucera@users.noreply.github.com> --- PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx index 4f1c007d20a..1030e695c4b 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx @@ -617,7 +617,7 @@ struct HfCandidateCreatorXic0Omegac0 { } else { mCasc = casc.mOmega(); } - auto arrMassCharmBaryon = std::array{0., 0.}; + std::array arrMassCharmBaryon{}; if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::XiczeroOmegaczeroToXiPi) { arrMassCharmBaryon = {MassXiMinus, MassPiPlus}; } else if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi) { From 0a241e613abd7b8ee8aeb85ed85aaa97af4d8bfc Mon Sep 17 00:00:00 2001 From: Yongxi Du <1260971129@qq.com> Date: Thu, 1 Oct 2026 01:33:39 +0800 Subject: [PATCH 128/648] [PWGUD] fix zdc gapside selection (#18122) --- PWGUD/Tasks/flowCorrelationsUpc.cxx | 33 +++++++++-------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/PWGUD/Tasks/flowCorrelationsUpc.cxx b/PWGUD/Tasks/flowCorrelationsUpc.cxx index d708c17e5ae..a1dae010e98 100644 --- a/PWGUD/Tasks/flowCorrelationsUpc.cxx +++ b/PWGUD/Tasks/flowCorrelationsUpc.cxx @@ -78,7 +78,6 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 2.0, "dcaz cut") O2_DEFINE_CONFIGURABLE(cfgMaxTPCChi2NCl, int, 4, "tpcchi2") O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 1, "choose one side 0:A; 1:C") - O2_DEFINE_CONFIGURABLE(cfgGapSideMerge, bool, true, "merge A and C side") O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") @@ -122,11 +121,7 @@ struct FlowCorrelationsUpc { // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); - Filter collisionFilter = cfgGapSideMerge - ? ((aod::udcollision::gapSide == (uint8_t)0 || aod::udcollision::gapSide == (uint8_t)1) && - (aod::upcservice::truegapside == 0 || aod::upcservice::truegapside == 1)) - : ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && - (aod::upcservice::truegapside == cfgGapSide)); + Filter collisionFilter = ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && (aod::upcservice::truegapside == cfgGapSide)); // Connect to ccdb Service ccdb{}; @@ -264,13 +259,13 @@ struct FlowCorrelationsUpc { registry.fill(HIST("neutronClass"), 0, 0); } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { - neutronClass = 1; - registry.fill(HIST("neutronClass"), 0, 1); - } - if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 2; registry.fill(HIST("neutronClass"), 1, 0); } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { neutronClass = 3; registry.fill(HIST("neutronClass"), 1, 1); @@ -281,15 +276,11 @@ struct FlowCorrelationsUpc { return false; } // if A or C gap is requested, keep corresponding neutron class - if (cfgGapSideMerge) { - // accepted - } else { - if (cfgGapSide == 0 || cfgGapSide == 1) { - if ((cfgGapSide == 0 && neutronClass == 1) || (cfgGapSide == 1 && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) - // accepted - } else { - return false; - } + if (cfgGapSide == 0 || cfgGapSide == 1) { + if ((cfgGapSide == 0 && neutronClass == 1) || (cfgGapSide == 1 && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) + // accepted + } else { + return false; } } } @@ -332,10 +323,6 @@ struct FlowCorrelationsUpc { return false; } - if (!zdcTimeCut(collision)) { - return false; - } - return true; } From d668cc0d5d9cc6461befb19d8b43e32328f0c9f4 Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Wed, 30 Sep 2026 23:52:29 +0200 Subject: [PATCH 129/648] [PWGLF] Add selectINELgtN in hStrangeCorrelation.cxx (#18123) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 72 +++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 587d5ba73d5..9d8a80ffdbb 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -127,7 +127,8 @@ struct HStrangeCorrelation { Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; Configurable doClosureTestPureMC{"doClosureTestPureMC", false, "fill regular ClosureTest histograms without event or reconstructed-trigger selection, using MC vertex z and centrality 0.05; keep truth-particle selections"}; Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable selectINELgtONE{"selectINELgtONE", false, "select INEL>1 events (at least 2 charged particles in |eta| < 1)"}; + Configurable selectINELgtN{"selectINELgtN", false, "select INEL>N events (more than N charged particles in |eta| < 1), N = inelGtNThreshold"}; + Configurable inelGtNThreshold{"inelGtNThreshold", 1, "N of the INEL>N selection (reco: PV contributors, gen: charged physical primaries, both in |eta| < 1)"}; Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions associated with the same found-by-T0 bunch crossing"}; @@ -3266,6 +3267,37 @@ struct HStrangeCorrelation { } } + // INEL>N at reconstructed level: more than N PV contributors in |eta| < 1. + // multNTracksPVeta1 is exactly this count (filled by multcenttable), so this is + // collision.isInelGt0() / isInelGt1() with a free N and needs no track table + template + bool isInelGtNReco(TCollision const& collision, int threshold) + { + return collision.multNTracksPVeta1() > threshold; + } + + // INEL>N at generated level: more than N charged physical primaries in |eta| < 1, + // counted the same way as multcenttable fills multMCNParticlesEta10 + template + bool isInelGtNGen(TMcParticles const& mcParticles, int threshold) + { + constexpr double ChargeTolerance = 1e-3; // |charge| below this counts as neutral + int nChEta1 = 0; + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; + } + if (std::abs(mcParticle.eta()) < 1.0f) { + ++nChEta1; + } + } + return nChEta1 > threshold; + } + // this function allows for all event selections to be done in a modular way template bool isCollisionSelected(TCollision const& collision) @@ -3288,7 +3320,7 @@ struct HStrangeCorrelation { if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { return false; } - if (!collision.isInelGt1() && masterConfigurations.selectINELgtONE) { + if (!isInelGtNReco(collision, masterConfigurations.inelGtNThreshold) && masterConfigurations.selectINELgtN) { return false; } if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { @@ -4262,7 +4294,7 @@ struct HStrangeCorrelation { float bestCollisionVtxZ = 0.0f; bool bestCollisionSel8 = false; bool bestCollisionINELgtZERO = false; - bool bestCollisionINELgtONE = false; + bool bestCollisionINELgtN = false; bool bestCollisionNoSameBunchPileup = false; bool bestCollisionGoodTriggerTVX = false; bool bestCollisionGoodZvtxFT0vsPV = false; @@ -4281,7 +4313,7 @@ struct HStrangeCorrelation { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); bestCollisionINELgtZERO = collision.isInelGt0(); - bestCollisionINELgtONE = collision.isInelGt1(); + bestCollisionINELgtN = isInelGtNReco(collision, masterConfigurations.inelGtNThreshold); bestCollisionNoSameBunchPileup = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bestCollisionGoodTriggerTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); @@ -4333,7 +4365,7 @@ struct HStrangeCorrelation { if (!bestCollisionINELgtZERO) { return; } - if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { return; } if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { @@ -4472,9 +4504,9 @@ struct HStrangeCorrelation { { // Part 3: a self-contained generator-level study. It deliberately touches no // reconstructed quantity in its event selection or in any of its axes: the - // event is selected on generated INEL>0 and the generated vertex only, the - // multiplicity is counted from generated particles, and every object is - // filled with generated coordinates. + // event is selected on generated INEL>0 (INEL>N if enabled) and the generated + // vertex only, the multiplicity is counted from generated particles, and every + // object is filled with generated coordinates. // // Reconstruction enters in exactly one place -- whether a generated object // has a reconstructed counterpart at all -- and that splits the very same @@ -4495,9 +4527,9 @@ struct HStrangeCorrelation { histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 0.0f); // Generated-level event selection. No reconstructed variable is used. - // INEL>1 implies INEL>0, so only the tighter enabled selection has to be evaluated - if (masterConfigurations.selectINELgtONE) { - if (!o2::pwglf::isINELgt1mc(mcParticles, pdgDB)) { + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { return; } } else if (masterConfigurations.selectINELgtZERO) { @@ -4788,7 +4820,7 @@ struct HStrangeCorrelation { float genBestCollisionVtxZ = 0.0f; bool genBestCollisionSel8 = false; bool genBestCollisionINELgtZERO = false; - bool genBestCollisionINELgtONE = false; + bool genBestCollisionINELgtN = false; bool genBestCollisionNoSameBunchPileup = false; bool genBestCollisionGoodTriggerTVX = false; bool genBestCollisionGoodZvtxFT0vsPV = false; @@ -4808,7 +4840,7 @@ struct HStrangeCorrelation { genBestCollisionSel8 = recCollision.sel8(); genBestCollisionVtxZ = recCollision.posZ(); genBestCollisionINELgtZERO = recCollision.isInelGt0(); - genBestCollisionINELgtONE = recCollision.isInelGt1(); + genBestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); genBestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); genBestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); genBestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); @@ -4822,7 +4854,7 @@ struct HStrangeCorrelation { genEventSelected = genEventSelected && genCollisionSelected; } else if (masterConfigurations.doGenEventSelection) { genEventSelected = genEventSelected && genBestCollisionSel8 && std::abs(genBestCollisionVtxZ) <= masterConfigurations.zVertexCut && - genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtONE || genBestCollisionINELgtONE) && + genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtN || genBestCollisionINELgtN) && (!masterConfigurations.rejectSameBunchPileup || genBestCollisionNoSameBunchPileup) && (!masterConfigurations.requireGoodTriggerTVX || genBestCollisionGoodTriggerTVX) && (!masterConfigurations.requireGoodZvtxFT0vsPV || genBestCollisionGoodZvtxFT0vsPV) && @@ -6192,7 +6224,7 @@ struct HStrangeCorrelation { } else { bool bestCollisionSel8 = false; bool bestCollisionINELgtZERO = false; - bool bestCollisionINELgtONE = false; + bool bestCollisionINELgtN = false; bool bestCollisionNoSameBunchPileup = false; bool bestCollisionGoodTriggerTVX = false; bool bestCollisionGoodZvtxFT0vsPV = false; @@ -6212,7 +6244,7 @@ struct HStrangeCorrelation { } else { bestCollisionSel8 = recCollision.sel8(); bestCollisionINELgtZERO = recCollision.isInelGt0(); - bestCollisionINELgtONE = recCollision.isInelGt1(); + bestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); bestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); @@ -6243,7 +6275,7 @@ struct HStrangeCorrelation { if (!bestCollisionINELgtZERO) { return; } - if (masterConfigurations.selectINELgtONE && !bestCollisionINELgtONE) { + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { return; } if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { @@ -6587,9 +6619,9 @@ struct HStrangeCorrelation { float multEta08 = -1; float multEta05 = -1; histos.fill(HIST("Prediction/hEventSelection"), 0.5); - // INEL>1 implies INEL>0, so only the tighter enabled selection has to be evaluated - if (masterConfigurations.selectINELgtONE) { - if (!o2::pwglf::isINELgt1mc(mcParticles, pdgDB)) { + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { return; } } else if (masterConfigurations.selectINELgtZERO) { From a3e0adb7505cb4bfb2feb5a54d4c6c68ef7633ac Mon Sep 17 00:00:00 2001 From: rolavick Date: Thu, 1 Oct 2026 04:18:14 +0200 Subject: [PATCH 130/648] Update CODEOWNERS of UD code (#18119) --- CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index ee6d5b59469..b2560d073fd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -75,7 +75,7 @@ /PWGMM/Lumi @alibuild @omvazque @mpuccio @skundu692 @aalkin @jgcn @gbencedi @abmodak /PWGMM/UE @alibuild @omvazque @mpuccio @skundu692 @aalkin @aortizve @jgcn -/PWGUD @alibuild @amatyja @rolavick +/PWGUD @alibuild @amatyja @ekryshen /PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman @mhemmer-cern /Tools/PIDML @alibuild @saganatt /Tools/ML @alibuild @fcatalan92 @fmazzasc @ChSonnabend @@ -86,4 +86,4 @@ /Tutorials/PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman /Tutorials/PWGLF @alibuild @alcaliva @lbariogl @BongHwi @lbarnby @ercolessi @iravasen @njacazio @romainschotter @skundu692 @lhusova /Tutorials/PWGMM @alibuild @aalkin @ddobrigk -/Tutorials/PWGUD @alibuild @pbuehler @amatyja +/Tutorials/PWGUD @alibuild @amatyja @ekryshen From 7ec53ef9a40e77b34e9dfcd42135f0894f29fece Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Thu, 1 Oct 2026 04:31:51 +0200 Subject: [PATCH 131/648] [PWGLF] Refactor recoed V0 analysis in derivedlambdakzeroanalysis (#18128) --- .../derivedlambdakzeroanalysis.cxx | 148 ++++++------------ 1 file changed, 48 insertions(+), 100 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 141945bd687..e07590e4e81 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -1432,7 +1432,7 @@ struct derivedlambdakzeroanalysis { } template - uint64_t computeReconstructionBitmap(TV0 const& v0, TCollision const& collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) + uint64_t computeReconstructionBitmap(TV0 const& v0, TCollision const& collision, float rapidityLambda, float rapidityK0Short) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -2926,9 +2926,9 @@ struct derivedlambdakzeroanalysis { } // ______________________________________________________ - // Real data processing - no MC subscription - template - void analyzeRecoedV0sInRealData(TCollision const& collision, TV0s const& fullV0s) + // Recoed data and MC processing (can use MC info if subscribed to it) + template + void analyzeRecoedV0s(TCollision const& collision, TV0s const& fullV0s, TMCCollisions const& /*mcCollisions*/) { // Fire up CCDB if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || @@ -2951,8 +2951,6 @@ struct derivedlambdakzeroanalysis { // Fill recoed event properties fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - // __________________________________________ // perform main analysis int nK0Shorts = 0; @@ -2965,96 +2963,51 @@ struct derivedlambdakzeroanalysis { if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) continue; // skip V0s that are not standard - // fill AP plot for all V0s - histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.yK0Short(), v0.pt()); - - // consider for histograms for all species - BITSET(selMap, selConsiderK0Short); - BITSET(selMap, selConsiderLambda); - BITSET(selMap, selConsiderAntiLambda); - - BITSET(selMap, selPhysPrimK0Short); - BITSET(selMap, selPhysPrimLambda); - BITSET(selMap, selPhysPrimAntiLambda); - - analyseCandidate(v0, v0.pt(), v0.yLambda(), v0.yK0Short(), centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - } // end v0 loop - - // fill the histograms with the number of reconstructed K0s/Lambda/antiLambda per collision - if (analyseK0Short) { - histos.fill(HIST("h2dNbrOfK0ShortVsCentrality"), centrality, nK0Shorts); - } - if (analyseLambda) { - histos.fill(HIST("h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); - } - if (analyseAntiLambda) { - histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, nAntiLambdas); - } - } - - // ______________________________________________________ - // Simulated processing (subscribes to MC information too) - template - void analyzeRecoedV0sInMonteCarlo(TCollision const& collision, TV0s const& fullV0s, TMCCollisions const& /*mcCollisions*/) - { - // Fire up CCDB - if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || - (mlConfigurations.useLambdaScores && mlConfigurations.calculateLambdaScores) || - (mlConfigurations.useAntiLambdaScores && mlConfigurations.calculateAntiLambdaScores) || - v0Selections.rejectTPCsectorBoundary) { - initCCDB(collision); - } - - if (!isEventAccepted(collision, true)) { - return; - } - - float centrality = -1; - float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated - double interactionRate = -1; - // gap side - int gapSide = -1; - int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides - // Fill recoed event properties - fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + float pt = v0.pt(); + float ptMC = -1.f; + float yK0Short = v0.yK0Short(); + float yLambda = v0.yLambda(); + bool correctCollision = false; + int mcNch = -1; + uint64_t selMapMCassociation = 0; + + if constexpr (requires { v0.v0MCCoreId(); }) { + if (doMCAssociation) { + if (!v0.has_v0MCCore()) + continue; - // __________________________________________ - // perform main analysis - int nK0Shorts = 0; - int nLambdas = 0; - int nAntiLambdas = 0; - for (auto const& v0 : fullV0s) { - if (std::abs(v0.negativeeta()) > v0Selections.daughterEtaCut || std::abs(v0.positiveeta()) > v0Selections.daughterEtaCut) - continue; // remove acceptance that's badly reproduced by MC / superfluous in future + auto v0MC = v0.template v0MCCore_as>(); - if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) - continue; // skip V0s that are not standard + ptMC = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); + pt = ptMC; + yK0Short = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); + yLambda = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - if (!v0.has_v0MCCore()) - continue; + selMapMCassociation = computeMCAssociation(v0MC); - auto v0MC = v0.template v0MCCore_as>(); + if constexpr (requires { collision.straMCCollisionId(); }) { + // check collision association explicitly + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as(); + mcNch = mcCollision.multMCNParticlesEta05(); + correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + } + } + } + } // fill AP plot for all V0s histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - float ptmc = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); - float ymc = 1e3; - if (v0MC.pdgCode() == PDG_t::kK0Short) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); - else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, ymc, ymc, ptmc); - selMap = selMap | computeMCAssociation(v0MC); + uint64_t selMap = computeReconstructionBitmap(v0, collision, yK0Short, yLambda); + BITSET(selMap, selMapMCassociation); // feeddown matrix always with association - if (calculateFeeddownMatrix) - fillFeeddownMatrix(v0, ptmc, centrality, selMap); + if constexpr (requires { v0.motherMCPartId(); }) { + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(v0, ptMC, centrality, selMap); + } + } // consider only associated candidates if asked to do so, disregard association if (!doMCAssociation) { @@ -3067,18 +3020,13 @@ struct derivedlambdakzeroanalysis { BITSET(selMap, selPhysPrimAntiLambda); } - analyseCandidate(v0, ptmc, ymc, ymc, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); + analyseCandidate(v0, pt, yLambda, yK0Short, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - if (doCollisionAssociationQA) { - // check collision association explicitly - bool correctCollision = false; - int mcNch = -1; - if (collision.has_straMCCollision()) { - auto mcCollision = collision.template straMCCollision_as(); - mcNch = mcCollision.multMCNParticlesEta05(); - correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + if constexpr (requires { collision.straMCCollisionId(); }) { + if (doCollisionAssociationQA) { + // check collision association explicitly + analyseCollisionAssociation(v0, ptMC, mcNch, correctCollision, selMap); } - analyseCollisionAssociation(v0, ptmc, mcNch, correctCollision, selMap); } } // end v0 loop @@ -3535,28 +3483,28 @@ struct derivedlambdakzeroanalysis { // Real data processing in Run 3 - no MC subscription void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Real data processing in Run 2 - no MC subscription void processRealDataRun2(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Simulated processing in Run 3 (subscribes to MC information too) void processMonteCarloRun3(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s, mccollisions); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ // Simulated processing in Run 2 (subscribes to MC information too) void processMonteCarloRun2(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s, mccollisions); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ From 572b95634bb4ffb81cb2b82a80e532641efa96e2 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Thu, 1 Oct 2026 05:05:28 +0200 Subject: [PATCH 132/648] [PWGLF] Restore slicing on tracked cascades in strangederivedbuilder (#18127) Co-authored-by: ALICE Action Bot --- .../Strangeness/strangederivedbuilder.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx index e4f22fddc9f..8007e538f89 100644 --- a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx @@ -249,7 +249,7 @@ struct strangederivedbuilder { Preslice V0perCollision = o2::aod::v0data::collisionId; Preslice CascperCollision = o2::aod::cascdata::collisionId; Preslice KFCascperCollision = o2::aod::cascdata::collisionId; - // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; + Preslice TraCascperCollision = o2::aod::cascdata::collisionId; Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; @@ -576,12 +576,11 @@ struct strangederivedbuilder { auto V0Table_thisColl = V0s.sliceBy(V0perCollision, collIdx); auto CascTable_thisColl = Cascades.sliceBy(CascperCollision, collIdx); auto KFCascTable_thisColl = KFCascades.sliceBy(KFCascperCollision, collIdx); - // auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); + auto TraCascTable_thisColl = TraCascades.sliceBy(TraCascperCollision, collIdx); bool strange = V0Table_thisColl.size() > 0 || CascTable_thisColl.size() > 0 || - KFCascTable_thisColl.size() > 0 - // ||TraCascTable_thisColl.size() > 0 - ; + KFCascTable_thisColl.size() > 0 || + TraCascTable_thisColl.size() > 0; auto bc = collision.template bc_as(); @@ -762,8 +761,8 @@ struct strangederivedbuilder { CascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); for (const auto& casc : KFCascTable_thisColl) KFCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); - // for (const auto& casc : TraCascTable_thisColl) - // TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); + for (const auto& casc : TraCascTable_thisColl) + TraCascadeCollIndices[casc.globalIndex()] = products.strangeColl.lastIndex(); } // +-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+ From e86c4d707ad29e84ade7b55aaa05ff773cc9df83 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Thu, 1 Oct 2026 14:18:53 +0800 Subject: [PATCH 133/648] [PWGCF] Improvements on efficiency correction (#18120) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 9fb3bed9a13..5282c3146ce 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -134,10 +134,10 @@ enum EMiscHistograms { eMiscHistograms_N }; -enum EExternalHistograms { +enum EWeightsHistograms { ePhi = 0, ePt, - eExternalHistograms_N + eWeightsHistograms_N }; enum EObservables { @@ -159,7 +159,7 @@ static constexpr std::array MultiplicityTabl "multFT0M", "multNTracksPV"}; -static constexpr std::array WeightsNames = { +static constexpr std::array WeightsNames = { "ePhi", "ePt"}; @@ -187,7 +187,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) external root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; - Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/O2challenge-", "path to external ROOT file which holds all particle weights"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; // *) binnings Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; @@ -219,6 +219,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) misc Configurable sigmaInel{"sigmaInel", 7.71, "inelastic cross section in mb"}; Configurable qualityAssuranceSwitch{"qualityAssuranceSwitch", false, "quality assurance switch"}; + Configurable runMessageSwitch{"runMessageSwitch", false, "run message switch"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: @@ -247,7 +248,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) External histograms: struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - std::array, eExternalHistograms_N> fWeights{}; //! [type][before, after cuts] + std::array, eWeightsHistograms_N> fWeights{}; //! [type][before, after cuts] } ex; struct Observables { @@ -275,7 +276,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } template - void updateQVectorsTable(std::vector>& QVectorsTable, T1 phi, T1 weight = T1(1)) + void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 weight = T1(1)) { const int maxHarmonic = QVectorsTable.size(); const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); @@ -526,9 +527,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // templates template - float chooseCent(T1 const& collision) + float chooseCent(T1 const& collision, int const& whichEstimator) { - switch (centralityEstimator) { + switch (whichEstimator) { case eFT0C: return static_cast(collision.centFT0C()); case eFT0M: @@ -544,9 +545,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } template - float chooseMult(T1 const& collision) + float chooseMult(T1 const& collision, int const& whichTable) { - switch (multiplicityTables) { + switch (whichTable) { case eMultTPC: return static_cast(collision.multTPC()); case eMultFV0M: @@ -598,8 +599,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr (rs == eRec || rs == eRecAndSim) { // Fill reconstructed-level event histograms if (rm == eReal) { - auto thisCent = chooseCent(collision); - auto thisRefMult = chooseMult(collision); + auto thisCent = chooseCent(collision, centralityEstimator); + auto thisRefMult = chooseMult(collision, multiplicityTables); int multiplicityRec = static_cast(tracks.size()); if constexpr (cuts == eBefore) { ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); @@ -772,10 +773,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to void loadWeights(int runNumber) { - for (int i = 0; i < eExternalHistograms_N; ++i) { + for (int i = 0; i < eWeightsHistograms_N; ++i) { for (int j = 0; j < eCuts_N; ++j) { - ex.fWeights[i][j] = - getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s][%s]", WeightsNames[i], CutsNames[j])); + ex.fWeights[i][j] = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s][%s]", WeightsNames[i], CutsNames[j])); + if (!ex.fWeights[i][j]) { + LOGF(info, "[%s][%s] not found", WeightsNames[i], CutsNames[j]); + continue; + } ex.fExternalHistogramsList->Add(ex.fWeights[i][j]); } } @@ -784,7 +788,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void qaFill(T1 const& collision) { - auto thisCent = chooseCent(collision); + auto thisCent = chooseCent(collision, centralityEstimator); if constexpr (rs == eRecAndSim || rs == eSim) { if (!collision.has_mcCollision()) { return; @@ -807,7 +811,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fDryRun) { return; } - int thisRunNumber = collision.bc().runNumber(); + const int thisRunNumber = collision.bc().runNumber(); + if (runMessageSwitch) { + LOGF(info, "Successfully running, run number is %d", thisRunNumber); + } if (isFirstCollision) { // Get run number @@ -848,27 +855,30 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto track = tracks.iteratorAt(0); // set the type and scope from one instance for (int64_t i = 0; i < tracks.size(); i++) { track = tracks.iteratorAt(i); - const float thisPhi = track.phi(); - const float thisPt = track.pt(); - float wPhiBefore = 1.f; - float wPhiAfter = 1.f; - float wPtBefore = 1.f; - float wPtAfter = 1.f; - if (cfExternalFileSwitch && ex.fWeights[ePhi][eBefore]) { - wPhiBefore = ex.fWeights[ePhi][eBefore]->GetBinContent(ex.fWeights[ePhi][eBefore]->FindBin(thisPhi)); - wPhiAfter = ex.fWeights[ePhi][eAfter]->GetBinContent(ex.fWeights[ePhi][eAfter]->FindBin(thisPhi)); - wPtBefore = ex.fWeights[ePt][eBefore]->GetBinContent(ex.fWeights[ePt][eBefore]->FindBin(thisPt)); - wPtAfter = ex.fWeights[ePt][eAfter]->GetBinContent(ex.fWeights[ePt][eAfter]->FindBin(thisPt)); + float thisPhi = track.phi(); + float thisPt = track.pt(); + std::array thisPhiAndPt = {thisPhi, thisPt}; + std::array, 2> thisWeights = {{{1.f, 1.f}, {1.f, 1.f}}}; // {{wPhiBefore, wPhiAfter},{wPtBefore, wPtAfter}} + + // eWeightsHistograms_N + if (cfExternalFileSwitch) { + for (int k = 0; k < eWeightsHistograms_N; ++k) { + for (int j = 0; j < eCuts_N; ++j) { + if (ex.fWeights[k][j]) { + thisWeights[k][j] = ex.fWeights[k][j]->GetBinContent(ex.fWeights[k][j]->FindBin(thisPhiAndPt[k])); + } + } + } } particleHistFill(track); particleHistFill(track); - updateQVectorsTable(qVectorsTableBeforeCutsReal, thisPhi, wPhiBefore * wPtBefore); + updateQVectorsTable(qVectorsTableBeforeCutsReal, thisPhi, thisWeights[ePhi][eBefore] * thisWeights[ePt][eBefore]); if (cfMasterCutSwitch) { if (passesEventCutsReal && particleCuts(track)) { particleHistFill(track); - updateQVectorsTable(qVectorsTableAfterCutsReal, thisPhi, wPhiAfter * wPtAfter); + updateQVectorsTable(qVectorsTableAfterCutsReal, thisPhi, thisWeights[ePhi][eAfter] * thisWeights[ePt][eAfter]); } if (passesEventCutsMC && particleCuts(track)) { particleHistFill(track); From bfddaaecd627b41c9269d0dbe3c2fe17630cd4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:38:12 +0200 Subject: [PATCH 134/648] [PWGCF] Fix ClassDef compilation warning (#18076) --- PWGCF/Core/AnalysisConfigurableCuts.h | 2 +- PWGCF/GenericFramework/Core/FlowPtContainer.h | 2 +- .../Core/EventSelectionFilterAndAnalysis.h | 2 +- .../Core/PIDSelectionFilterAndAnalysis.h | 2 +- .../Core/SkimmingConfigurableCuts.h | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PWGCF/Core/AnalysisConfigurableCuts.h b/PWGCF/Core/AnalysisConfigurableCuts.h index 76b7b759f07..aebb6b2f407 100644 --- a/PWGCF/Core/AnalysisConfigurableCuts.h +++ b/PWGCF/Core/AnalysisConfigurableCuts.h @@ -151,7 +151,7 @@ class SimpleInclusiveCut : public TNamed SimpleInclusiveCut& operator=(const SimpleInclusiveCut&); private: - ClassDef(SimpleInclusiveCut, 1); + ClassDefOverride(SimpleInclusiveCut, 1); }; } // namespace analysis diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.h b/PWGCF/GenericFramework/Core/FlowPtContainer.h index 96ada820053..904153b7887 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.h +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.h @@ -233,6 +233,6 @@ class FlowPtContainer : public TNamed void getSubevents(int k, int n, std::vector& current, std::vector>& subevents); static constexpr std::array FactorialArray = {1., 1., 2., 6., 24., 120., 720., 5040., 40320.}; static constexpr std::array SignArray = {1, -1, 1, -1, 1, -1, 1, -1, 1}; - ClassDef(FlowPtContainer, 2); + ClassDefOverride(FlowPtContainer, 2); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index 5030f011be7..0a0a6aa3e41 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -119,7 +119,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis CutBrick* mZVertex; //! the z vertex selection cuts PileUpRejBrick* mPileUpRejection; //! the pile-up rejection criteria - ClassDef(EventSelectionFilterAndAnalysis, 1) + ClassDefOverride(EventSelectionFilterAndAnalysis, 1) }; /// \brief Stores the different multiplicities needed for proper collision filtering diff --git a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h index 9c6d4d5ead0..8d24e4348c8 100644 --- a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h @@ -145,7 +145,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis std::vector*> mCloseNsigmasTOF; std::vector*> mBayesProbability; - ClassDef(PIDSelectionFilterAndAnalysis, 1); + ClassDefOverride(PIDSelectionFilterAndAnalysis, 1); }; /// \brief Fills the filter cuts mask diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 8c3ca4f2094..885b646e906 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -98,7 +98,7 @@ class CutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(CutBrick, 1); + ClassDefOverride(CutBrick, 1); }; /// \class CutBrickLimit @@ -452,7 +452,7 @@ class CutWithVariations : public CutBrick bool mAllowSeveralDefaults; ///< true if allows to store several cut default values TList mDefaultBricks; ///< the list with the cut default values bricks TList mVariationBricks; ///< the list with the cut variation values bricks - ClassDef(CutWithVariations, 1); + ClassDefOverride(CutWithVariations, 1); }; /// \class SpecialCutBrick @@ -503,7 +503,7 @@ class SpecialCutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(SpecialCutBrick, 1); + ClassDefOverride(SpecialCutBrick, 1); }; class TrackSelectionBrick : public SpecialCutBrick @@ -626,7 +626,7 @@ class TrackSelectionBrick : public SpecialCutBrick // vector of ITS requirements (minNRequiredHits in specific requiredLayers) std::vector>> mRequiredITSHits{}; - ClassDef(TrackSelectionBrick, 1); + ClassDefOverride(TrackSelectionBrick, 1); }; } // namespace PWGCF From be9230c28c2343603373c53447692cac4c802830 Mon Sep 17 00:00:00 2001 From: CesarOmarRA <35703156+CesarOmarRA@users.noreply.github.com> Date: Thu, 1 Oct 2026 04:32:30 -0600 Subject: [PATCH 135/648] [PWGUD] update upcRhoPrimeAnalysis - RD and MC (#18115) --- PWGUD/Tasks/upcRhoPrimeAnalysis.cxx | 851 ++++++++++++++++++++++------ PWGUD/Tasks/upcVmRof.cxx | 642 +++++++++++++++++++-- 2 files changed, 1283 insertions(+), 210 deletions(-) diff --git a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx index 703f0989283..b8bfdcb6d81 100644 --- a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \brief Task for analysis of rho' in UPCs using UD tables (from SG producer). -/// \author Cesar Ramirez, cesar.ramirez@cern.ch +/// \file upcRhoPrimeAnalysis.cxx +/// \brief Task for analysis of rho prime in UPCs using UD tables (from SG producer). +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla -#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" #include "PWGUD/DataModel/UDTables.h" #include @@ -26,62 +26,66 @@ #include #include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include // IWYU pragma: keep #include #include +#include #include #include #include -#include -#include #include using namespace o2; using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace ROOT::Math; // Define UD tables using UDtracks = soa::Join; -using UDCollisions = soa::Join; +using UDCollisions = soa::Join; +using UDMcCollisions = aod::UDMcCollisions; +using UDMcParticles = aod::UDMcParticles; +using UDtracksMC = soa::Join; +using UDCollisionsMC = soa::Join; namespace o2::aod { namespace fourpi { -// Declare columns -DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number for event identification -DECLARE_SOA_COLUMN(M, m, double); // Invariant mass of the system -DECLARE_SOA_COLUMN(Pt, pt, double); // Transverse momentum of the system -DECLARE_SOA_COLUMN(Eta, eta, double); // Pseudorapidity of the system -DECLARE_SOA_COLUMN(Phi, phi, double); // Azimuthal angle of the system +// Columns RD / MC reco +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number +DECLARE_SOA_COLUMN(M, m, double); // System invariant mass +DECLARE_SOA_COLUMN(Pt, pt, double); // System pT +DECLARE_SOA_COLUMN(Eta, eta, double); // System pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, double); // System azimuthal angle DECLARE_SOA_COLUMN(PosX, posX, double); // Vertex X position DECLARE_SOA_COLUMN(PosY, posY, double); // Vertex Y position DECLARE_SOA_COLUMN(PosZ, posZ, double); // Vertex Z position -DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Total charge of selected tracks +DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Real total charge of the 4 selected tracks DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); // FT0A amplitude DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); // FT0C amplitude DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); // FV0A amplitude -DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of primary vertex contributors +DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of PV contributors DECLARE_SOA_COLUMN(Sign, sign, std::vector); // Track charges -DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT values -DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta values -DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi values -DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nσ for electrons -DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nσ for pions -DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nσ for kaons -DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nσ for protons -DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track identifiers -DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC mode reconstruction flag -DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA timing -DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC timing +DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT +DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta +DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi +DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nSigma electron +DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nSigma pion +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nSigma kaon +DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nSigma proton +DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track index within the system +DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC reconstruction mode flag +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA time +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC time DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); // ZNA energy DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); // ZNC energy -DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // Neutral system flag -DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy in time +DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // TotalCharge == 0 +DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, double); // Hadronic interaction rate } // namespace fourpi -// Define the output DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::RunNumber, fourpi::M, fourpi::Pt, fourpi::Eta, fourpi::Phi, fourpi::PosX, fourpi::PosY, fourpi::PosZ, fourpi::TotalCharge, @@ -92,10 +96,63 @@ DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::TrackID, fourpi::IsReconstructedWithUPC, fourpi::TimeZNA, fourpi::TimeZNC, fourpi::EnergyCommonZNA, fourpi::EnergyCommonZNC, fourpi::IsChargeZero, fourpi::OccupancyInTime, fourpi::HadronicRate); + +namespace mcgen4pi +{ +// Columns MC gen +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); // Common mother PDG +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); // Mother pT +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); // Mother phi +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); // Mother invariant mass +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); // Mother rapidity +DECLARE_SOA_COLUMN(McTotalCharge, mcTotalCharge, int); // Real total charge of the 4 generated particles + +// Per-particle info +DECLARE_SOA_COLUMN(McTrackPdg, mcTrackPdg, int[4]); +DECLARE_SOA_COLUMN(McTrackPt, mcTrackPt, float[4]); +DECLARE_SOA_COLUMN(McTrackEta, mcTrackEta, float[4]); +DECLARE_SOA_COLUMN(McTrackPhi, mcTrackPhi, float[4]); +DECLARE_SOA_COLUMN(McTrackSign, mcTrackSign, int[4]); +DECLARE_SOA_COLUMN(McTrackIsPrimary, mcTrackIsPrimary, int[4]); + +// Generated vertex +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); + +// Generated collision index +DECLARE_SOA_COLUMN(McCollisionIndex, mcCollisionIndex, int); + +// Real run number for MC gen +DECLARE_SOA_COLUMN(McRunNumber, mcRunNumber, int); + +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen4pi + +// MC reco<->gen match table +DECLARE_SOA_TABLE(FourPiMcMatchTree, "AOD", "FOURPIMCMATCH", + fourpi::IsReconstructedWithUPC, + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber, mcgen4pi::RecoIndex); + +// MC All generated collisions +DECLARE_SOA_TABLE(FourPiMcGenAllTree, "AOD", "FOURPIMCGALL", + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber); } // namespace o2::aod struct upcRhoPrimeAnalysis { Produces systemTree; + Produces fourPiMcMatchTree; + Produces fourPiMcGenAllTree; // System selection configuration Configurable systemYCut{"systemYCut", 0.5, "Max Rapidity of rho prime"}; @@ -127,11 +184,16 @@ struct upcRhoPrimeAnalysis { Configurable dcaZcut{"dcaZcut", 2, "dcaZ cut"}; Configurable minTPCFindableClusters{"minTPCFindableClusters", 70, "Minimum number of findable TPC clusters"}; - // Define histogram registry + // Optional generatorId filter + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + + // Define histogram registry for RD / MC reco HistogramRegistry registry{ "registry", {// Event flow histograms - {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1F, {{9, 0, 9}}}}, + {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1D, {{11, 0, 11}}}}, + {"Events/FlowDetailed", "Detailed event flow;Cut;Counts", {HistType::kTH1D, {{13, 0, 13}}}}, + {"Events/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, {"Events/VertexZ", "Vertex Z;z (cm);Counts", {HistType::kTH1F, {{200, -20, 20}}}}, {"Events/NumContrib", "Number of contributors;N_{contrib};Counts", {HistType::kTH1F, {{100, 0, 100}}}}, {"Events/FV0Amplitude", "FV0 amplitude;Amplitude;Counts", {HistType::kTH1F, {{200, 0, 200}}}}, @@ -145,23 +207,148 @@ struct upcRhoPrimeAnalysis { {"Tracks/TPCNSigmaPi", "TPC n#sigma for #pi;n#sigma;Counts", {HistType::kTH1F, {{200, -10, 10}}}}, {"Tracks/TPCChi2NCl", "TPC #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 20}}}}, {"Tracks/ITSChi2NCl", "ITS #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 50}}}}, - {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{12, 0, 12}}}}, + {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{15, 0, 15}}}}, {"Tracks/DCASpectrum", "Track DCA spectrum;DCA (cm);Counts", {HistType::kTH1F, {{100, 0, 5}}}}, {"Tracks/ChargeDistribution", "Track charge distribution;Charge;Counts", {HistType::kTH1F, {{3, -1.5, 1.5}}}}, {"Tracks/TPCClusters", "TPC clusters findable;N_{clusters};Counts", {HistType::kTH1F, {{100, 0, 200}}}}, + {"Tracks/NGoodTracksPerEvent", "Good tracks per event (before the ==4 cut);N;Counts", {HistType::kTH1F, {{11, -0.5, 10.5}}}}, // System kinematics histograms {"System/hM", ";m (GeV/#it{c}^{2});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, - {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, {"System/hEta", ";#eta;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, {"System/hPhi", ";#phi;counts", {HistType::kTH1F, {{180, 0.0, 6.28}}}}, {"System/hY", ";y;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"System/hTotalChargeBefore", "Total charge before M/Pt/Y cuts;Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + // -4=----, -2=---+ (3-,1+), 0=+-+- (2+,2-), +2=+++- (3+,1-), +4=++++ + {"System/hTotalCharge", "System total charge (4 tracks, after M/Pt/Y cuts);Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + {"System/hMVsTotalChargeBefore", "Invariant mass vs charge combination (before M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, + {"System/hMVsTotalCharge", "Invariant mass vs charge combination (after M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, // Comparison histograms {"Cuts/MBefore", "Mass before cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, {"Cuts/MAfter", "Mass after cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, - {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1}}}}, - {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 10}}}}}}; + {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}, + {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}}}; + + // Define histogram registry for MC analysis + HistogramRegistry mcRegistry{ + "mcRegistry", + {// Event-level MC histograms + {"MC/Events/hAllEvents", "All MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hAccepted", "Accepted MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hVertexZ", "MC Vertex Z;z (cm);Counts", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, + {"MC/Events/hNPrimaries", "Number of primary particles;N;Counts", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + + // Track-level MC histograms + {"MC/Tracks/hPt", "MC Track p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0, 2.0}}}}, + {"MC/Tracks/hEta", "MC Track #eta;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, + {"MC/Tracks/hPhi", "MC Track #phi;#phi;Counts", {HistType::kTH1F, {{200, 0.0, 6.28}}}}, + {"MC/Tracks/hPdgCode", "PDG codes;PDG code;Counts", {HistType::kTH1F, {{2000, -1000, 1000}}}}, + + // System-level (mother) MC histograms + {"MC/System/hM", "MC Invariant Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/System/hPt", "MC p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/System/hY", "MC Rapidity;y;Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/System/hMvsPt", "MC Mass vs p_{T};m (GeV/c^{2});p_{T} (GeV/c)", {HistType::kTH2F, {{1000, 0.0, 10.0}, {1000, 0.0, 1.1}}}}, + {"MC/System/hMvsY", "MC Mass vs Rapidity;m (GeV/c^{2});y", {HistType::kTH2F, {{1000, 0.0, 10.0}, {200, -2.0, 2.0}}}}, + {"MC/System/hTotalCharge", "Generated total charge;Q;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + + // Basic summary + {"MC/Summary/hEventCounter", "Generated vs reconstructed summary;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hMatchStatus", "Reco<->Gen match status;;Counts", {HistType::kTH1D, {{3, 0, 3}}}}, + {"MC/Summary/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hRecoTotalCharge", "Reco total charge (MC events, after M/Pt/Y cuts);Q;Counts", {HistType::kTH1D, {{9, -4.5, 4.5}}}}, + + // Rho prime specific histograms + {"MC/RhoPrime/hFound", "Rho Prime Found;Found;Counts", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"MC/RhoPrime/hMass", "Rho Prime Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassUPC", "Rho Prime Mass UPC;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassSTD", "Rho Prime Mass STD;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hPt", "Rho Prime p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + + // Control histograms + {"MC/Control/hNDaughtersOfMother", "Number of daughters of the common mother;N;Counts", {HistType::kTH1F, {{10, 0, 10}}}}, + {"MC/Control/hMotherPdg", "PDG of the common mother found;PDG;Counts", {HistType::kTH1F, {{200, 0, 40000}}}}, + {"MC/Control/hTracksWithMcParticle", "Reco tracks with an associated mcParticle (of 4);N;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, + + // MC reco<->gen match histograms + {"MC/Match/hRecoEvents", "Reco events entering the match check;;Counts", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Match/hMatchedGenM", "Matched candidates - generated M;m_{gen} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/Match/hMatchedGenPt", "Matched candidates - generated p_{T};p_{T,gen} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/Match/hMatchedGenY", "Matched candidates - generated y;y_{gen};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/Match/hRecoVsGenM", "Reco vs Gen M;m_{gen} (GeV/c^{2});m_{reco} (GeV/c^{2})", {HistType::kTH2F, {{500, 0.0, 5.0}, {500, 0.0, 5.0}}}}}}; + + // Helper functions for kinematic calculations + static float pt(float px, float py) { return std::sqrt(px * px + py * py); } + + static float eta(float px, float py, float pz) + { + if (std::abs(pz) > 1e-10) { + float p = std::sqrt(px * px + py * py + pz * pz); + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + static float phi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + // Generic "common mother" + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0; + }; + + // 4 particles share the same direct mother + template + MotherInfo findCommonMotherGeneric(const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + return info; + } + + // Charge sign from PDG code + static int signFromPdg(int pdgCode) + { + if (pdgCode == 211) { + return 1; + } + if (pdgCode == -211) { + return -1; + } + return (pdgCode > 0) ? 1 : (pdgCode < 0) ? -1 + : 0; + } void init(InitContext&) { @@ -173,32 +360,135 @@ struct upcRhoPrimeAnalysis { hFlow->GetXaxis()->SetBinLabel(4, "ITS ROFb cut"); hFlow->GetXaxis()->SetBinLabel(5, "TFB cut"); hFlow->GetXaxis()->SetBinLabel(6, "Gap Side cut"); - hFlow->GetXaxis()->SetBinLabel(7, "PV contrib cut"); - hFlow->GetXaxis()->SetBinLabel(8, "Z vtx cut"); - hFlow->GetXaxis()->SetBinLabel(9, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(7, "ZDC energy cut"); + hFlow->GetXaxis()->SetBinLabel(8, "PV contrib cut"); + hFlow->GetXaxis()->SetBinLabel(9, "Z vtx cut"); + hFlow->GetXaxis()->SetBinLabel(10, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(11, "System cuts (M,Pt,Y)"); + + auto hFlowDetailed = registry.get(HIST("Events/FlowDetailed")); + hFlowDetailed->GetXaxis()->SetBinLabel(1, "All events"); + hFlowDetailed->GetXaxis()->SetBinLabel(2, "vtxITSTPC"); + hFlowDetailed->GetXaxis()->SetBinLabel(3, "sbp"); + hFlowDetailed->GetXaxis()->SetBinLabel(4, "itsROFb"); + hFlowDetailed->GetXaxis()->SetBinLabel(5, "tfb"); + hFlowDetailed->GetXaxis()->SetBinLabel(6, "gapSide"); + hFlowDetailed->GetXaxis()->SetBinLabel(7, "FV0 < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(8, "FT0A < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(9, "FT0C < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(10, "ZDC energy < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(11, "numContrib == 4"); + hFlowDetailed->GetXaxis()->SetBinLabel(12, "posZ < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(13, "4 tracks"); + + // Readable labels for Events/hRecoMode + auto hRecoMode = registry.get(HIST("Events/hRecoMode")); + hRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hChargeBefore = registry.get(HIST("System/hTotalChargeBefore")); + hChargeBefore->GetXaxis()->SetBinLabel(1, "----"); + hChargeBefore->GetXaxis()->SetBinLabel(3, "---+"); + hChargeBefore->GetXaxis()->SetBinLabel(5, "+-+-"); + hChargeBefore->GetXaxis()->SetBinLabel(7, "+++-"); + hChargeBefore->GetXaxis()->SetBinLabel(9, "++++"); + + auto hCharge = registry.get(HIST("System/hTotalCharge")); + hCharge->GetXaxis()->SetBinLabel(1, "----"); + hCharge->GetXaxis()->SetBinLabel(3, "---+"); + hCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMChargeBefore = registry.get(HIST("System/hMVsTotalChargeBefore")); + hMChargeBefore->GetYaxis()->SetBinLabel(1, "----"); + hMChargeBefore->GetYaxis()->SetBinLabel(3, "---+"); + hMChargeBefore->GetYaxis()->SetBinLabel(5, "+-+-"); + hMChargeBefore->GetYaxis()->SetBinLabel(7, "+++-"); + hMChargeBefore->GetYaxis()->SetBinLabel(9, "++++"); + + auto hMCharge = registry.get(HIST("System/hMVsTotalCharge")); + hMCharge->GetYaxis()->SetBinLabel(1, "----"); + hMCharge->GetYaxis()->SetBinLabel(3, "---+"); + hMCharge->GetYaxis()->SetBinLabel(5, "+-+-"); + hMCharge->GetYaxis()->SetBinLabel(7, "+++-"); + hMCharge->GetYaxis()->SetBinLabel(9, "++++"); - // Configure track rejection reasons histogram labels auto hReject = registry.get(HIST("Tracks/RejectionReasons")); - hReject->GetXaxis()->SetBinLabel(1, "All Tracks"); - hReject->GetXaxis()->SetBinLabel(2, "PV Contributor"); - hReject->GetXaxis()->SetBinLabel(3, "Has ITS+TPC"); - hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1 GeV/c"); - hReject->GetXaxis()->SetBinLabel(5, "TPC chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(6, "ITS chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(7, "TPC clusters findable"); - hReject->GetXaxis()->SetBinLabel(8, "TPC nSigmaPi"); - hReject->GetXaxis()->SetBinLabel(9, "Eta acceptance"); - hReject->GetXaxis()->SetBinLabel(10, "DCAz cut"); - hReject->GetXaxis()->SetBinLabel(11, "DCAxy cut"); - hReject->GetXaxis()->SetBinLabel(12, "Accepted Tracks"); + hReject->GetXaxis()->SetBinLabel(1, "All tracks"); + hReject->GetXaxis()->SetBinLabel(2, "isPVContributor"); + hReject->GetXaxis()->SetBinLabel(3, "hasITS && hasTPC"); + hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1"); + hReject->GetXaxis()->SetBinLabel(5, "tpcChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(6, "itsChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(7, "tpcNClsFindable > cut"); + hReject->GetXaxis()->SetBinLabel(8, "|tpcNSigmaPi| < cut"); + hReject->GetXaxis()->SetBinLabel(9, "|eta| < cut"); + hReject->GetXaxis()->SetBinLabel(10, "|dcaZ| < cut"); + hReject->GetXaxis()->SetBinLabel(11, "|dcaXY| < cut"); + hReject->GetXaxis()->SetBinLabel(12, "Accepted tracks"); + + auto hPdg = mcRegistry.get(HIST("MC/Tracks/hPdgCode")); + if (hPdg) { + int bin211 = hPdg->GetXaxis()->FindBin(211); + int binM211 = hPdg->GetXaxis()->FindBin(-211); + int bin30113 = hPdg->GetXaxis()->FindBin(30113); + if (bin211 > 0 && bin211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin211, "#pi^{+}"); + if (binM211 > 0 && binM211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(binM211, "#pi^{-}"); + if (bin30113 > 0 && bin30113 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin30113, "rho'"); + } + + auto hRhoFound = mcRegistry.get(HIST("MC/RhoPrime/hFound")); + hRhoFound->GetXaxis()->SetBinLabel(1, "Not Found"); + hRhoFound->GetXaxis()->SetBinLabel(2, "Found"); + + auto hMcCharge = mcRegistry.get(HIST("MC/System/hTotalCharge")); + hMcCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMcSummary = mcRegistry.get(HIST("MC/Summary/hEventCounter")); + hMcSummary->GetXaxis()->SetBinLabel(1, "Generated (denominator, gen all)"); + hMcSummary->GetXaxis()->SetBinLabel(2, "Reconstructed + matched (numerator)"); + + auto hMatchStatus = mcRegistry.get(HIST("MC/Summary/hMatchStatus")); + hMatchStatus->GetXaxis()->SetBinLabel(1, "No mcParticle on some track"); + hMatchStatus->GetXaxis()->SetBinLabel(2, "Has mcParticle but no common mother"); + hMatchStatus->GetXaxis()->SetBinLabel(3, "Match found"); + + auto hMcRecoMode = mcRegistry.get(HIST("MC/Summary/hRecoMode")); + hMcRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hMcRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hMcRecoCharge = mcRegistry.get(HIST("MC/Summary/hRecoTotalCharge")); + hMcRecoCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcRecoCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcRecoCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(9, "++++"); + + if (doprocessDataCols && (doprocessMcCols || doprocessMcGenAll)) { + LOGP(fatal, + "Invalid config: processDataCols must not run together with processMcCols/processMcGenAll " + "(it would duplicate SystemTree rows). Disable one of the two groups in your configuration.json."); + } } - void process(UDCollisions::iterator const& collision, UDtracks const& tracks) + // Collision + track selection + template + std::vector selectAndFillSystemTree(ColType const& collision, TracksType const& tracks, bool& outFilled) { - // Count all processed events + outFilled = false; + std::vector goodTracks; + registry.fill(HIST("Events/Flow"), 0); + registry.fill(HIST("Events/FlowDetailed"), 0); - // Fill basic event diagnostics registry.fill(HIST("Events/VertexZ"), collision.posZ()); registry.fill(HIST("Events/NumContrib"), collision.numContrib()); registry.fill(HIST("Events/FV0Amplitude"), collision.totalFV0AmplitudeA()); @@ -207,64 +497,84 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNA()); registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNC()); - // Apply event selection cuts in sequence - if (collision.vtxITSTPC() != vtxITSTPCcut) - return; + if (collision.vtxITSTPC() != vtxITSTPCcut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 1); + registry.fill(HIST("Events/FlowDetailed"), 1); - if (collision.sbp() != sbpCut) - return; + if (collision.sbp() != sbpCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 2); + registry.fill(HIST("Events/FlowDetailed"), 2); - if (collision.itsROFb() != itsROFbCut) - return; + if (collision.itsROFb() != itsROFbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 3); + registry.fill(HIST("Events/FlowDetailed"), 3); - if (collision.tfb() != tfbCut) - return; + if (collision.tfb() != tfbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 4); + registry.fill(HIST("Events/FlowDetailed"), 4); + + if (specifyGapSide && collision.gapSide() != gapSide) { + return goodTracks; + } - if (specifyGapSide && collision.gapSide() != gapSide) - return; - if (collision.totalFV0AmplitudeA() > fv0Cut) - return; - if (collision.totalFT0AmplitudeA() > ft0aCut) - return; - if (collision.totalFT0AmplitudeC() > ft0cCut) - return; - if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) - return; registry.fill(HIST("Events/Flow"), 5); + registry.fill(HIST("Events/FlowDetailed"), 5); - if (collision.numContrib() != numPVContrib) - return; + if (collision.totalFV0AmplitudeA() > fv0Cut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 6); + + if (collision.totalFT0AmplitudeA() > ft0aCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 7); + + if (collision.totalFT0AmplitudeC() > ft0cCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 8); + + if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 6); + registry.fill(HIST("Events/FlowDetailed"), 9); - if (std::abs(collision.posZ()) > vZCut) - return; + if (collision.numContrib() != numPVContrib) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 7); + registry.fill(HIST("Events/FlowDetailed"), 10); - std::vector posPions; - std::vector negPions; - posPions.reserve(2); - negPions.reserve(2); + if (std::abs(collision.posZ()) > vZCut) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 8); + registry.fill(HIST("Events/FlowDetailed"), 11); - // Loop over all tracks in the event + // --- Track selection: up to 4 good tracks, charge combination --- + goodTracks.reserve(4); for (const auto& track : tracks) { - registry.fill(HIST("Tracks/RejectionReasons"), 0); // Count all tracks + registry.fill(HIST("Tracks/RejectionReasons"), 0); - // Track selection criteria applied in sequence: if (useOnlyPVtracks && !track.isPVContributor()) { registry.fill(HIST("Tracks/RejectionReasons"), 1); continue; } - if (!track.hasITS() || !track.hasTPC()) { registry.fill(HIST("Tracks/RejectionReasons"), 2); continue; } - // Fill track spectra registry.fill(HIST("Tracks/Pt"), track.pt()); registry.fill(HIST("Tracks/Eta"), eta(track.px(), track.py(), track.pz())); registry.fill(HIST("Tracks/TPCNSigmaPi"), track.tpcNSigmaPi()); @@ -278,7 +588,6 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 3); continue; } - if (track.tpcChi2NCl() > tpcChi2NClsCut) { registry.fill(HIST("Tracks/RejectionReasons"), 4); continue; @@ -287,84 +596,71 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 5); continue; } - if (track.tpcNClsFindable() < minTPCFindableClusters) { registry.fill(HIST("Tracks/RejectionReasons"), 6); continue; } - if (std::abs(track.tpcNSigmaPi()) > nSigmaTPCcut) { registry.fill(HIST("Tracks/RejectionReasons"), 7); continue; } - - float trackEta = eta(track.px(), track.py(), track.pz()); - if (std::abs(trackEta) > etaCut) { + if (std::abs(eta(track.px(), track.py(), track.pz())) > etaCut) { registry.fill(HIST("Tracks/RejectionReasons"), 8); continue; } - if (std::abs(track.dcaZ()) > dcaZcut) { registry.fill(HIST("Tracks/RejectionReasons"), 9); continue; } - float maxDCAxy = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (dcaXYcut == 0 && (std::fabs(track.dcaXY()) > maxDCAxy)) { - registry.fill(HIST("Tracks/RejectionReasons"), 10); - continue; - } else if (dcaXYcut != 0 && (std::fabs(track.dcaXY()) > dcaXYcut)) { + if (dcaXYcut == 0 && std::fabs(track.dcaXY()) > maxDCAxy) { registry.fill(HIST("Tracks/RejectionReasons"), 10); continue; } - // Track passed all selection criteria registry.fill(HIST("Tracks/RejectionReasons"), 11); - - if (track.sign() > 0 && posPions.size() < 2) { - posPions.push_back(track); - } else if (track.sign() < 0 && negPions.size() < 2) { - negPions.push_back(track); - } - - if (posPions.size() == 2 && negPions.size() == 2) + goodTracks.push_back(track); + if (goodTracks.size() == 4) { break; + } } - if (posPions.size() != 2 || negPions.size() != 2) { - return; - } - registry.fill(HIST("Events/Flow"), 8); + // Basic control + registry.fill(HIST("Tracks/NGoodTracksPerEvent"), goodTracks.size()); - std::vector selectedTracks; - selectedTracks.insert(selectedTracks.end(), posPions.begin(), posPions.end()); - selectedTracks.insert(selectedTracks.end(), negPions.begin(), negPions.end()); + if (goodTracks.size() != 4) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 9); + registry.fill(HIST("Events/FlowDetailed"), 12); - // Reconstruct the 4-pion system - ROOT::Math::PxPyPzMVector fourPionSystem; - std::vector pionFourVectors; + // Real total charge + int totalCharge = 0; + for (const auto& track : goodTracks) { + totalCharge += track.sign(); + } + bool isChargeZero = (totalCharge == 0); + registry.fill(HIST("System/hTotalChargeBefore"), totalCharge); - for (const auto& track : selectedTracks) { - ROOT::Math::PxPyPzMVector pionVec( - track.px(), track.py(), track.pz(), - o2::constants::physics::MassPionCharged); - fourPionSystem += pionVec; - pionFourVectors.push_back(pionVec); + PxPyPzMVector fourPionSystem; + for (const auto& track : goodTracks) { + fourPionSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); } - // Fill pre-cut system histograms registry.fill(HIST("Cuts/MBefore"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtBefore"), fourPionSystem.Pt()); + registry.fill(HIST("System/hMVsTotalChargeBefore"), fourPionSystem.M(), totalCharge); - // Apply system-level kinematic cuts - if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) - return; - if (fourPionSystem.Pt() > systemPtCut) - return; - if (std::abs(fourPionSystem.Rapidity()) > systemYCut) - return; + if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) { + return goodTracks; + } + if (fourPionSystem.Pt() > systemPtCut) { + return goodTracks; + } + if (std::abs(fourPionSystem.Rapidity()) > systemYCut) { + return goodTracks; + } - // Fill post-cut system histograms registry.fill(HIST("Cuts/MAfter"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtAfter"), fourPionSystem.Pt()); registry.fill(HIST("System/hM"), fourPionSystem.M()); @@ -372,13 +668,14 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("System/hEta"), fourPionSystem.Eta()); registry.fill(HIST("System/hPhi"), fourPionSystem.Phi() + o2::constants::math::PI); registry.fill(HIST("System/hY"), fourPionSystem.Rapidity()); + registry.fill(HIST("System/hTotalCharge"), totalCharge); + registry.fill(HIST("System/hMVsTotalCharge"), fourPionSystem.M(), totalCharge); std::vector trackPts, trackEtas, trackPhis; std::vector trackSigns, trackIDs; std::vector tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr; - - for (size_t i = 0; i < selectedTracks.size(); i++) { - const auto& track = selectedTracks[i]; + for (size_t i = 0; i < goodTracks.size(); i++) { + const auto& track = goodTracks[i]; trackPts.push_back(track.pt()); trackEtas.push_back(eta(track.px(), track.py(), track.pz())); trackPhis.push_back(phi(track.px(), track.py())); @@ -387,48 +684,272 @@ struct upcRhoPrimeAnalysis { tpcNSigmasPi.push_back(track.tpcNSigmaPi()); tpcNSigmasKa.push_back(track.tpcNSigmaKa()); tpcNSigmasPr.push_back(track.tpcNSigmaPr()); - trackIDs.push_back(i); + trackIDs.push_back(static_cast(i)); } bool isReconstructedWithUPC = (collision.flags() == 1); - // Fill the output + registry.fill(HIST("Events/Flow"), 10); + registry.fill(HIST("Events/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + systemTree( collision.runNumber(), - fourPionSystem.M(), - fourPionSystem.Pt(), - fourPionSystem.Rapidity(), - fourPionSystem.Phi(), - collision.posX(), - collision.posY(), - collision.posZ(), - 0, // Total charge = 0 - collision.totalFT0AmplitudeA(), - collision.totalFT0AmplitudeC(), - collision.totalFV0AmplitudeA(), + fourPionSystem.M(), fourPionSystem.Pt(), fourPionSystem.Rapidity(), fourPionSystem.Phi(), + collision.posX(), collision.posY(), collision.posZ(), + totalCharge, + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.numContrib(), - trackSigns, - trackPts, - trackEtas, - trackPhis, - tpcNSigmasEl, - tpcNSigmasPi, - tpcNSigmasKa, - tpcNSigmasPr, - trackIDs, + trackSigns, trackPts, trackEtas, trackPhis, + tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr, + trackIDs, isReconstructedWithUPC, + collision.timeZNA(), collision.timeZNC(), collision.energyCommonZNA(), collision.energyCommonZNC(), + isChargeZero, collision.occupancyInTime(), collision.hadronicRate()); + + outFilled = true; + return goodTracks; + } // end selectAndFillSystemTree + + int getMcRunNumber(aod::BCs const& bcs) + { + if (bcs.size() == 0) { + return -1; + } + auto bc = bcs.begin(); + return bc.runNumber(); + } + + // processDataCols: RD or MC reco + void processDataCols(UDCollisions::iterator const& collision, UDtracks const& tracks) + { + bool filled = false; + selectAndFillSystemTree(collision, tracks, filled); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processDataCols, "Process real data or MC reco", true); + + // processMcCols: MC only + void processMcCols(UDCollisionsMC::iterator const& collision, UDtracksMC const& tracks, UDMcParticles const&, UDMcCollisions const&, aod::BCs const& bcs) + { + if (genId != -1) { + if (!collision.has_udMcCollision() || collision.template udMcCollision_as().generatorsID() != genId) { + return; + } + } + + bool filled = false; + auto selTrks = selectAndFillSystemTree(collision, tracks, filled); + if (!filled) { + return; + } + + bool isReconstructedWithUPC = (collision.flags() == 1); + + mcRegistry.fill(HIST("MC/Match/hRecoEvents"), 0); + mcRegistry.fill(HIST("MC/Summary/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + + int recoTotalCharge = 0; + for (const auto& track : selTrks) { + recoTotalCharge += track.sign(); + } + mcRegistry.fill(HIST("MC/Summary/hRecoTotalCharge"), recoTotalCharge); + + // Defaults (pdg=0 / kinematics=-999 => "no match") + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + int mcTotalCharge = 0; + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {-999, -999, -999, -999}; + float trackEtas[4] = {-999, -999, -999, -999}; + float trackPhis[4] = {-999, -999, -999, -999}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {0, 0, 0, 0}; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcCollisionIndex = -1; + int mcRunNumber = getMcRunNumber(bcs); + + // Basic control + int nTracksWithMcParticle = 0; + for (const auto& track : selTrks) { + if (track.has_udMcParticle()) { + nTracksWithMcParticle++; + } + } + mcRegistry.fill(HIST("MC/Control/hTracksWithMcParticle"), nTracksWithMcParticle); + + // All 4 reco tracks need an associated mcParticle to look for the mother + std::vector mcParts; + bool allHaveMcParticle = true; + for (const auto& track : selTrks) { + if (!track.has_udMcParticle()) { + allHaveMcParticle = false; + break; + } + mcParts.push_back(track.template udMcParticle_as()); + } + + MotherInfo mi; + if (allHaveMcParticle) { + mi = findCommonMotherGeneric(mcParts); + } + + if (!allHaveMcParticle) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 0); + } else if (!mi.found) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 1); + } else { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 2); + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 1); + } + + if (allHaveMcParticle) { + for (size_t i = 0; i < mcParts.size() && i < 4; i++) { + trackPdgs[i] = mcParts[i].pdgCode(); + trackPts[i] = pt(mcParts[i].px(), mcParts[i].py()); + trackEtas[i] = eta(mcParts[i].px(), mcParts[i].py(), mcParts[i].pz()); + trackPhis[i] = phi(mcParts[i].px(), mcParts[i].py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + isPrimary[i] = mcParts[i].isPhysicalPrimary() ? 1 : 0; + mcTotalCharge += trackSigns[i]; + } + + auto mcCollision = mcParts[0].template udMcCollision_as(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcCollisionIndex = static_cast(mcCollision.globalIndex()); + } + + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + PxPyPzMVector genSystem; + for (const auto& p : mcParts) { + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + mcRegistry.fill(HIST("MC/Match/hMatchedGenM"), motherMass); + mcRegistry.fill(HIST("MC/Match/hMatchedGenPt"), motherPt); + mcRegistry.fill(HIST("MC/Match/hMatchedGenY"), motherRap); + + PxPyPzMVector recoSystem; + for (const auto& track : selTrks) { + recoSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); + } + mcRegistry.fill(HIST("MC/Match/hRecoVsGenM"), motherMass, recoSystem.M()); + if (motherPdg == 30113) { // only when the mother found is actually the rho prime + if (isReconstructedWithUPC) { + mcRegistry.fill(HIST("MC/RhoPrime/hMassUPC"), recoSystem.M()); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hMassSTD"), recoSystem.M()); + } + } + } + + fourPiMcMatchTree( isReconstructedWithUPC, - collision.timeZNA(), - collision.timeZNC(), - collision.energyCommonZNA(), - collision.energyCommonZNC(), - true, // Always charge zero for our selection - collision.occupancyInTime(), - collision.hadronicRate()); + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcPosX, mcPosY, mcPosZ, mcCollisionIndex, mcRunNumber, + systemTree.lastIndex()); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcCols, "Match MC reco<->gen (MC only)", false); + + // processMcGenAll: MC All generated collisions + void processMcGenAll(UDMcCollisions::iterator const& mcCollision, UDMcParticles const& mcParticles, aod::BCs const& bcs) + { + if (genId != -1 && mcCollision.generatorsID() != genId) { + return; + } + + int mcRunNumber = getMcRunNumber(bcs); + + mcRegistry.fill(HIST("MC/Events/hAllEvents"), 0); + mcRegistry.fill(HIST("MC/Events/hVertexZ"), mcCollision.posZ()); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + mcRegistry.fill(HIST("MC/Events/hNPrimaries"), primaries.size()); + + if (primaries.size() != 4) { + return; + } + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 0); + + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {0, 0, 0, 0}; + float trackEtas[4] = {0, 0, 0, 0}; + float trackPhis[4] = {0, 0, 0, 0}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {1, 1, 1, 1}; + int mcTotalCharge = 0; + + PxPyPzMVector genSystem; + for (size_t i = 0; i < 4; i++) { + const auto& p = primaries[i]; + trackPdgs[i] = p.pdgCode(); + trackPts[i] = pt(p.px(), p.py()); + trackEtas[i] = eta(p.px(), p.py(), p.pz()); + trackPhis[i] = phi(p.px(), p.py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + mcTotalCharge += trackSigns[i]; + + mcRegistry.fill(HIST("MC/Tracks/hPt"), trackPts[i]); + mcRegistry.fill(HIST("MC/Tracks/hEta"), trackEtas[i]); + mcRegistry.fill(HIST("MC/Tracks/hPhi"), trackPhis[i] + o2::constants::math::PI); // same 0-2pi convention as System/hPhi + mcRegistry.fill(HIST("MC/Tracks/hPdgCode"), trackPdgs[i]); + + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + + // Generic common-mother search + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + MotherInfo mi = findCommonMotherGeneric(primaries); + mcRegistry.fill(HIST("MC/Control/hNDaughtersOfMother"), mi.found ? 4 : 0); + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + } + + mcRegistry.fill(HIST("MC/System/hM"), genSystem.M()); + mcRegistry.fill(HIST("MC/System/hPt"), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hY"), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hMvsPt"), genSystem.M(), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hMvsY"), genSystem.M(), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hTotalCharge"), mcTotalCharge); + + if (motherPdg == 30113) { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 1); + mcRegistry.fill(HIST("MC/RhoPrime/hMass"), motherMass); + mcRegistry.fill(HIST("MC/RhoPrime/hPt"), motherPt); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 0); + } + + fourPiMcGenAllTree( + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + static_cast(mcCollision.globalIndex()), mcRunNumber); + + mcRegistry.fill(HIST("MC/Events/hAccepted"), 0); } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcGenAll, "All generated collisions (MC only)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc, TaskName{"upc-rho-prime-analysis"})}; } diff --git a/PWGUD/Tasks/upcVmRof.cxx b/PWGUD/Tasks/upcVmRof.cxx index 5d112ad9ecb..00e3dc6ac7c 100644 --- a/PWGUD/Tasks/upcVmRof.cxx +++ b/PWGUD/Tasks/upcVmRof.cxx @@ -13,6 +13,7 @@ /// \brief analysis of UPC vector meson production ROF by ROF /// /// \author Guillermo Contreras (jesus.guillermo.contreras.nuno@cern.ch), Czech Technical University in Prague +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/EventSelection.h" @@ -41,6 +42,7 @@ #include #include +#include #include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include #include #include @@ -57,11 +60,14 @@ using namespace o2::framework::expressions; using BCsTSsSels = soa::Join; using ColSels = soa::Join; +using ColSelsMc = soa::Join; using TRKs = soa::Join; +using TRKsMc = soa::Join; using ColSel = ColSels::iterator; +using ColSelMc = ColSelsMc::iterator; namespace o2::aod { @@ -74,6 +80,7 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(Chi2, chi2, float); DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(NearestBCB, nearestBCB, int); DECLARE_SOA_COLUMN(LocalTF, localTF, int); DECLARE_SOA_COLUMN(LocalROF, localROF, int); DECLARE_SOA_COLUMN(UpcFlag, upcFlag, int); @@ -140,9 +147,49 @@ DECLARE_SOA_COLUMN(HasTof3, hasTof3, int); DECLARE_SOA_COLUMN(HasTof4, hasTof4, int); } // namespace datarows +namespace mcgen +{ +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); + +DECLARE_SOA_COLUMN(McPdg1, mcPdg1, int); +DECLARE_SOA_COLUMN(McPt1, mcPt1, float); +DECLARE_SOA_COLUMN(McEta1, mcEta1, float); +DECLARE_SOA_COLUMN(McPhi1, mcPhi1, float); +DECLARE_SOA_COLUMN(McQ1, mcQ1, int); +DECLARE_SOA_COLUMN(IsPrimary1, isPrimary1, int); +DECLARE_SOA_COLUMN(McPdg2, mcPdg2, int); +DECLARE_SOA_COLUMN(McPt2, mcPt2, float); +DECLARE_SOA_COLUMN(McEta2, mcEta2, float); +DECLARE_SOA_COLUMN(McPhi2, mcPhi2, float); +DECLARE_SOA_COLUMN(McQ2, mcQ2, int); +DECLARE_SOA_COLUMN(IsPrimary2, isPrimary2, int); +DECLARE_SOA_COLUMN(McPdg3, mcPdg3, int); +DECLARE_SOA_COLUMN(McPt3, mcPt3, float); +DECLARE_SOA_COLUMN(McEta3, mcEta3, float); +DECLARE_SOA_COLUMN(McPhi3, mcPhi3, float); +DECLARE_SOA_COLUMN(McQ3, mcQ3, int); +DECLARE_SOA_COLUMN(IsPrimary3, isPrimary3, int); +DECLARE_SOA_COLUMN(McPdg4, mcPdg4, int); +DECLARE_SOA_COLUMN(McPt4, mcPt4, float); +DECLARE_SOA_COLUMN(McEta4, mcEta4, float); +DECLARE_SOA_COLUMN(McPhi4, mcPhi4, float); +DECLARE_SOA_COLUMN(McQ4, mcQ4, int); +DECLARE_SOA_COLUMN(IsPrimary4, isPrimary4, int); + +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); +DECLARE_SOA_COLUMN(McLocalBC, mcLocalBC, int); +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen + DECLARE_SOA_TABLE(TwoTrkTable, "AOD", "TWOTRKTABLE", datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, - datarows::LocalBC, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, @@ -152,7 +199,7 @@ DECLARE_SOA_TABLE(TwoTrkTable, "AOD", "TWOTRKTABLE", datarows::HasTof1, datarows::HasTof2); DECLARE_SOA_TABLE(FourTrkTable, "AOD", "FOURTRKTABLE", datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, - datarows::LocalBC, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, @@ -162,6 +209,32 @@ DECLARE_SOA_TABLE(FourTrkTable, "AOD", "FOURTRKTABLE", datarows::Pt3, datarows::Eta3, datarows::Phi3, datarows::Q3, datarows::PidPion3, datarows::PidElectron3, datarows::PidKaon3, datarows::PidProton3, datarows::Pt4, datarows::Eta4, datarows::Phi4, datarows::Q4, datarows::PidPion4, datarows::PidElectron4, datarows::PidKaon4, datarows::PidProton4, datarows::HasTof1, datarows::HasTof2, datarows::HasTof3, datarows::HasTof4); + +DECLARE_SOA_TABLE(TwoTrkRecGenTable, "AOD", "TWOTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); +DECLARE_SOA_TABLE(FourTrkRecGenTable, "AOD", "FOURTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); + +DECLARE_SOA_TABLE(TwoTrkGenTable, "AOD", "TWOTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); +DECLARE_SOA_TABLE(FourTrkGenTable, "AOD", "FOURTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); } // namespace o2::aod struct UpcVmRof { @@ -169,6 +242,10 @@ struct UpcVmRof { // output Produces twoTrkTable; Produces fourTrkTable; + Produces twoTrkRecGenTable; + Produces fourTrkRecGenTable; + Produces twoTrkGenTable; + Produces fourTrkGenTable; // services Service ccdb{}; // access to database @@ -188,6 +265,9 @@ struct UpcVmRof { std::bitset bcPatternB; std::bitset bcPatternC; std::vector bcbIdx; + std::vector vecNearestBCB; + std::vector vecDistNearestBCB; + int nbcB = 0; // variables to store ITS ROF info @@ -230,6 +310,7 @@ struct UpcVmRof { Configurable minTrkTpcClusters{"minTrkTpcClusters", 70.0, "minimum number of TPC clusters associated to the track"}; Configurable maxTrkDcaZ{"maxTrkDcaZ", 2.0, "max DCA in z of track to vtx (cm)"}; Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin 1e4 means some 28 s"}; + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; //-------------------------------------------------------------------------------- // get ITS ROF info @@ -242,6 +323,63 @@ struct UpcVmRof { rofPerOrbit = static_cast(o2::constants::lhc::LHCMaxBunches / rofLength); } + //-------------------------------------------------------------------------------- + // find nearest bcb for this filling scheme + void setNearestBCB() + { + vecNearestBCB.clear(); + vecDistNearestBCB.clear(); + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + // extend vector + vecNearestBCB.push_back(-1); + vecDistNearestBCB.push_back(-1); + // are we in a bc-b? + if (bcPatternB.test(i)) { + vecNearestBCB[i] = i; + vecDistNearestBCB[i] = 0; + continue; + } + // find nearest previous bc-b + int nearestLeft = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i - j; + if (k < 0) + k = o2::constants::lhc::LHCMaxBunches + k; + if (bcPatternB.test(k)) { + nearestLeft = k; + break; + } + } // end nearestLeft loop + + // find nearest next bc-b + int nearesRight = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i + j; + if (k > (o2::constants::lhc::LHCMaxBunches - 1)) + k = k - o2::constants::lhc::LHCMaxBunches; + if (bcPatternB.test(k)) { + nearesRight = k; + break; + } + } // end nearesRight loop + + // find nearest bc-b + int dLeft = i - nearestLeft; + // cppcheck-suppress knownConditionTrueFalse + if (dLeft < 0) + dLeft += o2::constants::lhc::LHCMaxBunches; + int dRight = nearesRight - i; + // cppcheck-suppress knownConditionTrueFalse + if (dRight < 0) + dRight += o2::constants::lhc::LHCMaxBunches; + int dMin = std::min(dLeft, dRight); + int nearest = ((dMin == dLeft) ? nearestLeft : nearesRight); + vecNearestBCB[i] = nearest; + int dist = ((dMin == dLeft) ? -dLeft : dRight); + vecDistNearestBCB[i] = dist; + } // end loop over bc + } + //-------------------------------------------------------------------------------- // get filling scheme void getFillingScheme() @@ -460,6 +598,109 @@ struct UpcVmRof { ccdb->setFatalWhenNull(false); } // end init() + //-------------------------------------------------------------------------------- + // helper function for MC: pt + static float mcPt(float px, float py) { return std::sqrt(px * px + py * py); } + + //-------------------------------------------------------------------------------- + // helper function for MC: pseudorapidity + static float mcEta(float px, float py, float pz) + { + float p = std::sqrt(px * px + py * py + pz * pz); + if (std::abs(p - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: azimuth + static float mcPhi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: rapidity + static float mcRapidity(float px, float py, float pz, float mass) + { + float energy = std::sqrt(px * px + py * py + pz * pz + mass * mass); + if (std::abs(energy - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((energy + pz) / (energy - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // mother search + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0, pz = 0, mass = 0; + }; + + //-------------------------------------------------------------------------------- + // checks if all given daughters share the same direct mother: found=false if not + template + MotherInfo findCommonMother(aod::McParticles const& mcParticles, const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + int64_t motherIdx = daughters[0].mothersIds()[0]; + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers() || daughters[i].mothersIds()[0] != motherIdx) { + return info; + } + } + auto mother = mcParticles.rawIteratorAt(motherIdx); + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + + //-------------------------------------------------------------------------------- + // for processMcGenAll: same mother search. + template + MotherInfo findCommonMotherFromIters(/* aod::McParticles const& mcParticles, */ const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + //-------------------------------------------------------------------------------- // process BCs to get trigger information void processBCs(BCsTSsSels const& bcs) @@ -528,11 +769,17 @@ struct UpcVmRof { PROCESS_SWITCH(UpcVmRof, processBCs, "get BCs and trigger information", true); //-------------------------------------------------------------------------------- - // get collision information - void processCols(ColSel const& col, BCsTSsSels const&, TRKs const& tracks, - aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, - aod::Zdcs const&) + // shared collision+track selection; used by both real data and MC + template + std::vector fillCollisionTables( + ColType const& col, BCsTSsSels const& bcs, TracksType const& tracks, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, aod::Zdcs const&, + bool& outIsTwoBody, bool& outIsFourBody) { + outIsTwoBody = false; + outIsFourBody = false; + std::vector selTrks; + // get info for this bc auto bc = col.template foundBC_as(); if (runNumberCol != bc.runNumber()) { // new run @@ -540,6 +787,7 @@ struct UpcVmRof { getRunInfo(runNumberCol); getFillingScheme(); addColHistos(runNumberCol); + setNearestBCB(); } int64_t thisBC = getBcWithinOrbit(bc.globalBC()); int64_t thisTF = getTimeFrame(bc.globalBC()); @@ -547,18 +795,14 @@ struct UpcVmRof { // select collision if (!checkColFlags(col, runNumberCol)) { - return; + return selTrks; } - // accept only -B bcs - if (!bcPatternB.test(thisBC)) { - return; - } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(10); // select on zVtx if (std::abs(col.posZ()) > maxAbsPosZ) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(11); @@ -566,7 +810,7 @@ struct UpcVmRof { bool isTwoContributors = (col.numContrib() == NTrksTwoBody); bool isFourContributors = (col.numContrib() == NTrksFourBody); if (!isTwoContributors && !isFourContributors) { - return; + return selTrks; } if (isTwoContributors) { colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(16); @@ -576,7 +820,6 @@ struct UpcVmRof { } // select tracks - std::vector selTrks; colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(0); for (const auto& track : tracks) { if (!track.isPVContributor()) { @@ -623,7 +866,7 @@ struct UpcVmRof { bool isTwoBody = isTwoContributors && (selTrks.size() == NTrksTwoBody); bool isFourBody = isFourContributors && (selTrks.size() == NTrksFourBody); if (!isTwoBody && !isFourBody) { - return; + return selTrks; } // selected events @@ -634,6 +877,40 @@ struct UpcVmRof { colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(19); } + // find the bc row for the nearest bc if different from the current one + bool foundBCB = true; + auto nearbcb = bcs.iteratorAt(bc.globalIndex()); + if (vecDistNearestBCB[thisBC] > 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB <= bcs.iteratorAt(bcs.size() - 1).globalIndex()) { + while (nearbcb != bcs.end()) { + ++nearbcb; + if (nearbcb.globalIndex() > gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in future direction + if (vecDistNearestBCB[thisBC] < 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB >= bcs.iteratorAt(0).globalIndex()) { + while (nearbcb != bcs.iteratorAt(0)) { + --nearbcb; + if (nearbcb.globalIndex() < gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in past direction + // FT0 selection float aFT0A = 0; float aFT0C = 0; @@ -641,34 +918,34 @@ struct UpcVmRof { float tFT0C = 33; // default time to mark events without FT0 info int nFT0A = 0; int nFT0C = 0; - if (bc.has_foundFT0()) { + if (foundBCB && nearbcb.has_foundFT0()) { // a side - if (bc.foundFT0().isValidTimeA()) { // valid time - tFT0A = bc.foundFT0().timeA(); + if (nearbcb.foundFT0().isValidTimeA()) { // valid time + tFT0A = nearbcb.foundFT0().timeA(); if (std::abs(tFT0A) > maxAbsTimeFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(12); - aFT0A = bc.foundFT0().sumAmpA(); + aFT0A = nearbcb.foundFT0().sumAmpA(); if (aFT0A > maxAmpFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(13); - nFT0A = (bc.foundFT0().amplitudeA()).size(); + nFT0A = (nearbcb.foundFT0().amplitudeA()).size(); } // a side // c side - if (bc.foundFT0().isValidTimeC()) { // valid time - tFT0C = bc.foundFT0().timeC(); + if (nearbcb.foundFT0().isValidTimeC()) { // valid time + tFT0C = nearbcb.foundFT0().timeC(); if (std::abs(tFT0C) > maxAbsTimeFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(14); - aFT0C = bc.foundFT0().sumAmpC(); + aFT0C = nearbcb.foundFT0().sumAmpC(); if (aFT0C > maxAmpFT0) { - return; + return selTrks; } colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(15); - nFT0C = (bc.foundFT0().amplitudeC()).size(); + nFT0C = (nearbcb.foundFT0().amplitudeC()).size(); } // c side } // FT0 selection @@ -684,9 +961,9 @@ struct UpcVmRof { float aFV0A = 0; float tFV0A = 33; // default time to mark events without FV0 info int nFV0A = 0; - if (bc.has_foundFV0()) { - tFV0A = bc.foundFV0().time(); - auto v = bc.foundFV0().amplitude(); + if (foundBCB && nearbcb.has_foundFV0()) { + tFV0A = nearbcb.foundFV0().time(); + auto v = nearbcb.foundFV0().amplitude(); aFV0A = std::accumulate(v.begin(), v.end(), 0.f); nFV0A = v.size(); } // FV0A info @@ -698,9 +975,9 @@ struct UpcVmRof { float aFDDC = 0; float tFDDC = 33; // default time to mark events without FDD info int nFDDC = 0; - if (bc.has_foundFDD()) { - tFDDA = bc.foundFDD().timeA(); - auto vA = bc.foundFDD().chargeA(); + if (foundBCB && nearbcb.has_foundFDD()) { + tFDDA = nearbcb.foundFDD().timeA(); + auto vA = nearbcb.foundFDD().chargeA(); // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; if (vA[0] > 0 && vA[4] > 0) { aFDDA += 0.5 * (vA[0] + vA[4]); @@ -718,8 +995,8 @@ struct UpcVmRof { aFDDA += 0.5 * (vA[3] + vA[7]); nFDDA++; } - tFDDC = bc.foundFDD().timeC(); - auto vC = bc.foundFDD().chargeC(); + tFDDC = nearbcb.foundFDD().timeC(); + auto vC = nearbcb.foundFDD().chargeC(); // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; if (vC[0] > 0 && vC[4] > 0) { aFDDC += 0.5 * (vC[0] + vC[4]); @@ -744,11 +1021,11 @@ struct UpcVmRof { float tZNC = -999; // default time to mark events without ZN info float eZNA = -999; float eZNC = -999; - if (bc.has_zdc()) { - tZNA = (bc.zdc()).timeZNA(); - tZNC = (bc.zdc()).timeZNC(); - eZNA = (bc.zdc()).energyCommonZNA(); - eZNC = (bc.zdc()).energyCommonZNC(); + if (foundBCB && nearbcb.has_zdc()) { + tZNA = (nearbcb.zdc()).timeZNA(); + tZNC = (nearbcb.zdc()).timeZNC(); + eZNA = (nearbcb.zdc()).energyCommonZNA(); + eZNC = (nearbcb.zdc()).energyCommonZNC(); if (!std::isfinite(tZNA)) { tZNA = -999; } @@ -774,7 +1051,7 @@ struct UpcVmRof { tof[1] = 1; } colTH1Pointers[Form("col/%d/twoTrkTF_H", runNumberCol)]->Fill(thisTF); - twoTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, thisTF, thisROF, recoFlag, + twoTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, eZNA, eZNC, tZNA, tZNC, selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), @@ -797,7 +1074,7 @@ struct UpcVmRof { tof[3] = 1; } colTH1Pointers[Form("col/%d/fourTrkTF_H", runNumberCol)]->Fill(thisTF); - fourTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, thisTF, thisROF, recoFlag, + fourTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, eZNA, eZNC, tZNA, tZNC, selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), @@ -811,12 +1088,287 @@ struct UpcVmRof { tof[0], tof[1], tof[2], tof[3]); } - } // end processCol - PROCESS_SWITCH(UpcVmRof, processCols, "get collisions and track information", true); + outIsTwoBody = isTwoBody; + outIsFourBody = isFourBody; + return selTrks; + } // end fillCollisionTables + + //-------------------------------------------------------------------------------- + // process real data or MC at reconstruction level + void processDataCols(ColSel const& col, BCsTSsSels const& bcs, TRKs const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs) + { + bool isTwoBody, isFourBody; + fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + } // end processDataCols + PROCESS_SWITCH(UpcVmRof, processDataCols, "process real data or mc reco, no mc truth", true); + + //-------------------------------------------------------------------------------- + // MC only: fills the same reconstruction tables and the generated information of the reconstructed event + void processMcCols(ColSelMc const& col, BCsTSsSels const& bcs, TRKsMc const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs, + aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (genId != -1) { + if (!col.has_mcCollision() || col.mcCollision().getGeneratorId() != genId) { + return; + } + } + + bool isTwoBody, isFourBody; + auto selTrks = fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + if (!isTwoBody && !isFourBody) { + return; // event was not written to the reco tables either + } + + // MC gen + if (isTwoBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, sign1 = 0, sign2 = 0, isPrim1 = 0, isPrim2 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999, pt2 = -999, eta2 = -999, phi2 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px(); + float sumPy = mcPart1->py() + mcPart2->py(); + float sumPz = mcPart1->pz() + mcPart2->pz(); + float sumE = mcPart1->e() + mcPart2->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + twoTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + twoTrkTable.lastIndex() // row of TwoTrkTable this mc info belongs to + ); + } + if (isFourBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + auto mcPart3 = selTrks[2].has_mcParticle() ? std::optional(selTrks[2].mcParticle()) : std::nullopt; + auto mcPart4 = selTrks[3].has_mcParticle() ? std::optional(selTrks[3].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, pdg3 = 0, pdg4 = 0; + int sign1 = 0, sign2 = 0, sign3 = 0, sign4 = 0; + int isPrim1 = 0, isPrim2 = 0, isPrim3 = 0, isPrim4 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999; + float pt2 = -999, eta2 = -999, phi2 = -999; + float pt3 = -999, eta3 = -999, phi3 = -999; + float pt4 = -999, eta4 = -999, phi4 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2 && mcPart3 && mcPart4) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + pdg3 = mcPart3->pdgCode(); + pt3 = mcPt(mcPart3->px(), mcPart3->py()); + eta3 = mcEta(mcPart3->px(), mcPart3->py(), mcPart3->pz()); + phi3 = mcPhi(mcPart3->px(), mcPart3->py()); + sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + isPrim3 = mcPart3->isPhysicalPrimary() ? 1 : 0; + + pdg4 = mcPart4->pdgCode(); + pt4 = mcPt(mcPart4->px(), mcPart4->py()); + eta4 = mcEta(mcPart4->px(), mcPart4->py(), mcPart4->pz()); + phi4 = mcPhi(mcPart4->px(), mcPart4->py()); + sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + isPrim4 = mcPart4->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px() + mcPart3->px() + mcPart4->px(); + float sumPy = mcPart1->py() + mcPart2->py() + mcPart3->py() + mcPart4->py(); + float sumPz = mcPart1->pz() + mcPart2->pz() + mcPart3->pz() + mcPart4->pz(); + float sumE = mcPart1->e() + mcPart2->e() + mcPart3->e() + mcPart4->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2, *mcPart3, *mcPart4}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + fourTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + pdg3, pt3, eta3, phi3, sign3, isPrim3, + pdg4, pt4, eta4, phi4, sign4, isPrim4, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + fourTrkTable.lastIndex() // row of FourTrkTable this mc info belongs to + ); + } + } // end processMcCols + PROCESS_SWITCH(UpcVmRof, processMcCols, "process mc truth matched to reco, mc only", false); + + //-------------------------------------------------------------------------------- + // Fill MC information for all generated events + void processMcGen(aod::McCollision const& mcCollision, BCsTSsSels const&, aod::McParticles const& mcParticles) + { + if (genId != -1 && mcCollision.getGeneratorId() != genId) { + return; + } + + // event counter + if (!colTH1Pointers["mcGen/genSel_H"]) { + colTH1Pointers["mcGen/genSel_H"] = colTH1Registry.add("mcGen/genSel_H", + "pure generator event counter; selID; Counter", + {HistType::kTH1D, {{3, -0.5, 2.5}}}); + } + colTH1Pointers["mcGen/genSel_H"]->Fill(0); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + + // local BC of the generated collision + int localBc = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + if (primaries.size() == NTrksTwoBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + + // mother mass/momentum always from summed daughters + float sumPx = p1.px() + p2.px(); + float sumPy = p1.py() + p2.py(); + float sumPz = p1.pz() + p2.pz(); + float sumE = p1.e() + p2.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; // 0 = no common mother confirmed in the gen tree + MotherInfo mi = findCommonMotherFromIters(/* mcParticles,*/ std::vector{p1, p2}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(1); + twoTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + + if (primaries.size() == NTrksFourBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + auto p3 = primaries[2]; + auto p4 = primaries[3]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int pdg3 = p3.pdgCode(); + int pdg4 = p4.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + int sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + int sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + + float sumPx = p1.px() + p2.px() + p3.px() + p4.px(); + float sumPy = p1.py() + p2.py() + p3.py() + p4.py(); + float sumPz = p1.pz() + p2.pz() + p3.pz() + p4.pz(); + float sumE = p1.e() + p2.e() + p3.e() + p4.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; + MotherInfo mi = findCommonMotherFromIters(/* mcParticles, */ std::vector{p1, p2, p3, p4}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(2); + fourTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + pdg3, mcPt(p3.px(), p3.py()), mcEta(p3.px(), p3.py(), p3.pz()), mcPhi(p3.px(), p3.py()), sign3, 1, + pdg4, mcPt(p4.px(), p4.py()), mcEta(p4.px(), p4.py(), p4.pz()), mcPhi(p4.px(), p4.py()), sign4, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + } // end processMcGenAll + PROCESS_SWITCH(UpcVmRof, processMcGen, "process all generated collisions, mc only", false); }; // end of struct UpcVmRof WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; From e8b8c72f7f30a4947b177667ed6933825ce72a15 Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Thu, 1 Oct 2026 13:25:04 +0200 Subject: [PATCH 136/648] [PWGCF] radialFlowDecorr: remove FT0 and Nch-[pT] covariance, clarify C3 pair/single maps, fix cfgSystType help (#18130) --- .../Tasks/radialFlowDecorr.cxx | 439 ++---------------- 1 file changed, 40 insertions(+), 399 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 60955d61098..37774bc9a35 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -16,14 +16,11 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include -#include -#include #include #include #include @@ -66,7 +63,6 @@ using namespace constants::math; struct RadialFlowDecorr { // --- fixed constants --------------------------------------------------------- - static constexpr int KnFt0cCell = 96; static constexpr int KIntM = 4; // pT-moment order used in the sums (need m up to 3 for c3) static constexpr int KIntK = 4; // weight-power order used in the sums (need k up to 3 for c3) @@ -185,7 +181,7 @@ struct RadialFlowDecorr { Configurable cfgNchOMax{"cfgNchOMax", 800, "Max Nch range for OO collisions"}; Configurable cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; - Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systDCA,2=systEff,3=systFlat,4=systNEta,5=systNITS,6=systNTPC,7=systPileup,8=systVz,9=systEtaBinning"}; + Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systDCA,2=systEff,3=systFlat,4=systNITS,5=systNTPC,6=systPileup,7=systVz,8=systEtaBinning"}; Configurable cfgNBootstrap{"cfgNBootstrap", 16, "Number of Poisson bootstrap samples (base data run only)"}; Configurable cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"}; @@ -254,25 +250,14 @@ struct RadialFlowDecorr { TProfile2D* pmeanTruNchEtabinStep2 = nullptr; TProfile2D* pmeanRecoNchEtabinStep2 = nullptr; TProfile2D* pmeanRecoEffcorrNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultTruNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultRecoNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultRecoEffcorrNchEtabinStep2 = nullptr; // Data mean maps (per eta bin) TProfile2D* pmeanNchEtabinStep2 = nullptr; - TProfile2D* pmeanMultNchEtabinStep2 = nullptr; - TProfile* pmeanFT0AmultpvStep2 = nullptr; - TProfile* pmeanFT0CmultpvStep2 = nullptr; } state; - o2::ft0::Geometry ft0Det; // --- bootstrap replica storage (base data fluc only) ------------------------- struct BootstrapHists { - std::array, KMaxBoot> amplFT0ACent{}; - std::array, KMaxBoot> amplFT0AMult{}; - std::array, KMaxBoot> amplFT0CCent{}; - std::array, KMaxBoot> amplFT0CMult{}; std::array, KMaxBoot> multCent{}; std::array, KMaxBoot> multMult{}; std::array, KMaxBoot> meanpTCent{}; @@ -285,19 +270,10 @@ struct RadialFlowDecorr { std::array, KMaxBoot> c2SubMult{}; std::array, KMaxBoot> c3SubCent{}; std::array, KMaxBoot> c3SubMult{}; - std::array, KMaxBoot> covCent{}; - std::array, KMaxBoot> covMult{}; - std::array, KMaxBoot> covFT0ACent{}; - std::array, KMaxBoot> covFT0AMult{}; - std::array, KMaxBoot> covFT0CCent{}; - std::array, KMaxBoot> covFT0CMult{}; std::array, KMaxBoot> c2Sub2D{}; std::array, KMaxBoot> c3Sub2D{}; std::array, KMaxBoot> gapSum2D{}; std::array, KMaxBoot> c3GapSum2D{}; - std::array, KMaxBoot> cov2D{}; - std::array, KMaxBoot> covFT0A2D{}; - std::array, KMaxBoot> covFT0C2D{}; } bs; // =========================================================================== @@ -677,41 +653,6 @@ struct RadialFlowDecorr { return rebinned; } - std::vector* offsetFT0 = nullptr; - uint64_t mLastTimestamp = 0; - double getEtaFT0(uint64_t globalChno, int i) - { - if (i > 1 || i < 0) { - LOGF(fatal, "kFIT Index %d out of range", i); - } - auto chPos = ft0Det.getChannelCenter(globalChno); - auto x = chPos.X() + (*offsetFT0)[i].getX(); - auto y = chPos.Y() + (*offsetFT0)[i].getY(); - auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - if (i == 1) { - z = -std::abs(z); - } else if (i == 0) { - z = std::abs(z); - } - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - return -std::log(std::tan(0.5 * theta)); - } - - void loadAlignParam(uint64_t timestamp) - { - if (timestamp == mLastTimestamp && offsetFT0 != nullptr) { - return; - } - offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); - if (!offsetFT0) { - LOGF(fatal, "Failed to load valid FT0 alignment from CCDB!"); - return; - } - mLastTimestamp = timestamp; - LOGF(info, "Loaded FT0 alignment for timestamp %llu", timestamp); - } - // Per-event two- and three-particle pT correlators (standard method) from the // power sums, following arXiv:2112.03397, Eqs. 2-3. struct C2C3Result { @@ -761,7 +702,6 @@ struct RadialFlowDecorr { // Table joins // =========================================================================== using GeneralCollisions = soa::Join; @@ -863,13 +803,6 @@ struct RadialFlowDecorr { histos.add("wgt_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); histos.add("wgt_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("pmeanFT0Amultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0Cmultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0C_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmean_cent_id_eta_FT0", ";cent;id;#eta", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;id;#eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("MCGen/Prof_Cent_Nchrec", ";cent;#LT N#GT", kTProfile, {centAxis1Per}); histos.add("MCGen/Prof_Mult_Nchrec", ";mult;#LT N#GT", kTProfile, {nChAxis}); histos.add("MCGen/Prof_Cent_MeanpT", ";cent;#LT p_{T}#GT", kTProfile, {centAxis1Per}); @@ -901,21 +834,15 @@ struct RadialFlowDecorr { histos.add("MCGen/Prof_MeanpT_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("MCGen/Prof_C2_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("MCGen/Prof_C2Sub_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cov_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("MCGen/Prof_MeanpT_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("MCGen/Prof_C2_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("MCGen/Prof_C2Sub_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cov_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("MCGen/Prof_C3_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("MCGen/Prof_C3_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + histos.add("MCGen/Prof_C3Sub_Cent_etabin", ";cent;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + histos.add("MCGen/Prof_C3Sub_Mult_etabin", ";mult;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); @@ -923,11 +850,8 @@ struct RadialFlowDecorr { histos.add("MCGen/Prof_C2Sub2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); histos.add("MCGen/Prof_GapSum2D", ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("MCGen/Prof_C3Sub2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_C3GapSum2D", ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("MCGen/Prof_Cov2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + histos.add("MCGen/Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + histos.add("MCGen/Prof_C3GapSum2D", ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); } void declareDataGetFlatHists() @@ -941,13 +865,6 @@ struct RadialFlowDecorr { void declareDataMeanHists() { - histos.add("pmeanFT0Amultpv", "N_{PV}; AmplitudeA", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", "cent; AmplitudeA", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0Cmultpv", "N_{PV}; AmplitudeC", kTProfile, {nChAxis}); - histos.add("pmeanFT0C_cent", "cent; AmplitudeC", kTProfile, {centAxis1Per}); - - histos.add("pmean_cent_id_eta_FT0", ";cent;channel id; #eta;amplitude", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;channel id; #eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); histos.add("Prof_Cent_Nchrec", ";cent;#LT N_{PV}#GT", kTProfile, {centAxis1Per}); histos.add("Prof_Mult_Nchrec", ";N_{PV};#LT N_{PV}#GT", kTProfile, {nChAxis}); @@ -978,29 +895,20 @@ struct RadialFlowDecorr { histos.add("Prof_C2_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("Prof_C2Sub_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("Prof_C2Sub_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_Cov_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_Cov_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_CovFT0A_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_CovFT0A_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_CovFT0C_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_CovFT0C_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("Prof_C3_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); histos.add("Prof_C3_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + histos.add("Prof_C3Sub_Cent_etabin", ";Centrality;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + histos.add("Prof_C3Sub_Mult_etabin", ";N_{PV};#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); histos.add("Prof_C2Sub2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_GapSum2D", ";cent;#Delta#eta (Gap);#Sigma#eta/2 (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_C3GapSum2D", ";cent;#Delta#eta (Gap);#Sigma#eta/2 (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_Cov2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0A2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0C2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + histos.add("Prof_GapSum2D", ";cent;#eta_{A} #minus #eta_{C} (Gap);#eta_{A} + #eta_{C} (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); + histos.add("Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + histos.add("Prof_C3GapSum2D", ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); } // 30 Poisson-bootstrap replicas of every final fluctuation observable @@ -1011,10 +919,6 @@ struct RadialFlowDecorr { for (int s = 0; s < nBoot; ++s) { bs.multCent[s] = histos.add(Form("Bootstrap/Prof_Mult_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); bs.multMult[s] = histos.add(Form("Bootstrap/Prof_Mult_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.amplFT0ACent[s] = histos.add(Form("Bootstrap/Prof_AmplFT0A_Cent_sample%d", s), ";cent;AmplitudeA", kTProfile, {centAxis1Per}); - bs.amplFT0AMult[s] = histos.add(Form("Bootstrap/Prof_AmplFT0A_Mult_sample%d", s), ";N_{PV};AmplitudeA", kTProfile, {nChAxis}); - bs.amplFT0CCent[s] = histos.add(Form("Bootstrap/Prof_AmplFT0C_Cent_sample%d", s), ";cent;AmplitudeC", kTProfile, {centAxis1Per}); - bs.amplFT0CMult[s] = histos.add(Form("Bootstrap/Prof_AmplFT0C_Mult_sample%d", s), ";N_{PV};AmplitudeC", kTProfile, {nChAxis}); bs.meanpTCent[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); bs.meanpTMult[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); bs.c2Cent[s] = histos.add(Form("Bootstrap/Prof_C2_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); @@ -1023,21 +927,12 @@ struct RadialFlowDecorr { bs.c2SubMult[s] = histos.add(Form("Bootstrap/Prof_C2Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); bs.c3Cent[s] = histos.add(Form("Bootstrap/Prof_C3_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); bs.c3Mult[s] = histos.add(Form("Bootstrap/Prof_C3_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c3SubCent[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c3SubMult[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covCent[s] = histos.add(Form("Bootstrap/Prof_Cov_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covMult[s] = histos.add(Form("Bootstrap/Prof_Cov_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covFT0ACent[s] = histos.add(Form("Bootstrap/Prof_CovFT0A_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covFT0AMult[s] = histos.add(Form("Bootstrap/Prof_CovFT0A_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.covFT0CCent[s] = histos.add(Form("Bootstrap/Prof_CovFT0C_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.covFT0CMult[s] = histos.add(Form("Bootstrap/Prof_CovFT0C_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + bs.c3SubCent[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + bs.c3SubMult[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); bs.c2Sub2D[s] = histos.add(Form("Bootstrap/Prof_C2Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); bs.gapSum2D[s] = histos.add(Form("Bootstrap/Prof_GapSum2D_sample%d", s), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - bs.c3Sub2D[s] = histos.add(Form("Bootstrap/Prof_C3Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.c3GapSum2D[s] = histos.add(Form("Bootstrap/Prof_C3GapSum2D_sample%d", s), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - bs.cov2D[s] = histos.add(Form("Bootstrap/Prof_Cov2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.covFT0A2D[s] = histos.add(Form("Bootstrap/Prof_CovFT0A2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.covFT0C2D[s] = histos.add(Form("Bootstrap/Prof_CovFT0C2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + bs.c3Sub2D[s] = histos.add(Form("Bootstrap/Prof_C3Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + bs.c3GapSum2D[s] = histos.add(Form("Bootstrap/Prof_C3GapSum2D_sample%d", s), ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); } } @@ -1194,9 +1089,6 @@ struct RadialFlowDecorr { int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - loadAlignParam(now); - ft0Det.calculateChannelCenter(); - std::string sysDir; switch (cfgSys) { case kPbPb: @@ -1376,14 +1268,9 @@ struct RadialFlowDecorr { LOGF(info, "Loading MC Mean profiles from CCDB path: %s", pathMCMean.c_str()); auto* lstMCMean = ccdb->getForTimeStamp(pathMCMean, now); if (lstMCMean) { - loadProfileFromList(lstMCMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadProfileFromList(lstMCMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); loadProfileFromList(lstMCMean, "pmeanTru_nch_etabin", state.pmeanTruNchEtabinStep2); loadProfileFromList(lstMCMean, "pmeanReco_nch_etabin", state.pmeanRecoNchEtabinStep2); loadProfileFromList(lstMCMean, "pmeanRecoEffcorr_nch_etabin", state.pmeanRecoEffcorrNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultTru_nch_etabin", state.pmeanMultTruNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultReco_nch_etabin", state.pmeanMultRecoNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanMultRecoEffcorr_nch_etabin", state.pmeanMultRecoEffcorrNchEtabinStep2); // MC is pinned to 0.2-wide bins; guard against a stale MC-mean object built // at a different width (its y-axis would then not have nEta+1 bins). if (state.pmeanTruNchEtabinStep2 && @@ -1406,10 +1293,7 @@ struct RadialFlowDecorr { (st == kSystBase ? "Base" : systSuffix[st].c_str()), meanPath.c_str()); auto* lstDataMean = ccdb->getForTimeStamp(meanPath, now); if (lstDataMean) { - loadProfileFromList(lstDataMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadProfileFromList(lstDataMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); loadProfileFromList(lstDataMean, "pmean_nch_etabin", state.pmeanNchEtabinStep2); - loadProfileFromList(lstDataMean, "pmeanMult_nch_etabin", state.pmeanMultNchEtabinStep2); // The DataMean and DataFluc passes must share cfgEtaBinWidth, else the // (ibx, ieta+1) read in processDataFluc maps a fluc bin index onto a // different physical eta slice, silently. The mean profile y-axis has @@ -1553,7 +1437,7 @@ struct RadialFlowDecorr { // =========================================================================== // MC: mean pT (truth / reco / reco-eff-corrected) // =========================================================================== - void processMCMean(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) + void processMCMean(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) { std::array sumWiTruth{}, sumWiptiTruth{}; std::array sumWiReco{}, sumWiptiReco{}; @@ -1694,44 +1578,16 @@ struct RadialFlowDecorr { histos.fill(HIST("pmeanMultRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiRecoEffCorr[ietaA]); } } - - // FT0 - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - auto eta = getEtaFT0(globalId, 1); - amplFT0C += ampl; - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); - } - } - histos.fill(HIST("pmeanFT0Amultpv"), multPV, amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), multPV, amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); } PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); // =========================================================================== // MC: fluctuations (C2, subevent) at three levels // =========================================================================== - void processMCFluc(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) + void processMCFluc(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) { - if (!state.pmeanTruNchEtabinStep2 || !state.pmeanRecoNchEtabinStep2 || !state.pmeanRecoEffcorrNchEtabinStep2 || - !state.pmeanMultTruNchEtabinStep2 || !state.pmeanMultRecoNchEtabinStep2 || !state.pmeanMultRecoEffcorrNchEtabinStep2) { - LOGF(warning, "MC fluc: mean pT or mult map missing"); + if (!state.pmeanTruNchEtabinStep2 || !state.pmeanRecoNchEtabinStep2 || !state.pmeanRecoEffcorrNchEtabinStep2) { + LOGF(warning, "MC fluc: mean pT map missing"); return; } @@ -1746,9 +1602,7 @@ struct RadialFlowDecorr { std::array meanReco{}, c2Reco{}, c3Reco{}; std::array meanRecoEffCor{}, c2RecoEffCor{}, c3RecoEffCor{}; - std::array meanTruMult{}, meanRecoMult{}, meanRecoEffCorMult{}; std::array p1kBarTru{}, p1kBarReco{}, p1kBarRecoEffCor{}; - std::array p1kBarTruMult{}, p1kBarRecoMult{}, p1kBarRecoEffCorMult{}; if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { return; @@ -1770,8 +1624,6 @@ struct RadialFlowDecorr { histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - double p1kBarFt0A = 0.0, p1kBarFt0C = 0.0; - // --- truth sums --- for (const auto& particle : mcParticles) { if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { @@ -1849,24 +1701,10 @@ struct RadialFlowDecorr { const int ibx = state.pmeanTruNchEtabinStep2->GetXaxis()->FindBin(multPV); const int iby = ieta + 1; - meanTruMult[ieta] = sumWkTru[ieta][1]; - meanRecoMult[ieta] = sumWkReco[ieta][1]; - meanRecoEffCorMult[ieta] = sumWkRecoEffCor[ieta][1]; - float mmptTru = state.pmeanTruNchEtabinStep2->GetBinContent(ibx, iby); float mmptReco = state.pmeanRecoNchEtabinStep2->GetBinContent(ibx, iby); float mmptRecoEffCor = state.pmeanRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); - float mmMultTru = state.pmeanMultTruNchEtabinStep2->GetBinContent(ibx, iby); - float mmMultReco = state.pmeanMultRecoNchEtabinStep2->GetBinContent(ibx, iby); - float mmMultRecoEffCor = state.pmeanMultRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); - - // covariance requires both bins populated: gate each multiplicity deviation on - // the same >=1-track condition as the mean, so cov skips unless both bins qualify - p1kBarTruMult[ieta] = (sumWkTru[ieta][1] >= 1.0) ? (meanTruMult[ieta] - mmMultTru) : std::numeric_limits::quiet_NaN(); - p1kBarRecoMult[ieta] = (sumWkReco[ieta][1] >= 1.0) ? (meanRecoMult[ieta] - mmMultReco) : std::numeric_limits::quiet_NaN(); - p1kBarRecoEffCorMult[ieta] = (sumWkRecoEffCor[ieta][1] >= 1.0) ? (meanRecoEffCorMult[ieta] - mmMultRecoEffCor) : std::numeric_limits::quiet_NaN(); - // truth meanTru[ieta] = (sumWkTru[ieta][1] >= 1.0) ? (sumPmwkTru[ieta][1][1] / sumWkTru[ieta][1]) : std::numeric_limits::quiet_NaN(); c2Tru[ieta] = std::numeric_limits::quiet_NaN(); @@ -1908,19 +1746,6 @@ struct RadialFlowDecorr { } } - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - amplFT0A += ft0.amplitudeA()[iCh]; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - amplFT0C += ft0.amplitudeC()[iCh]; - } - } - p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(multPV)); - p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(multPV)); - // per-eta counts & means for (int ieta = 0; ieta < nEta; ++ieta) { histos.fill(HIST("MCGen/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkTru[ieta][1]); @@ -1984,7 +1809,7 @@ struct RadialFlowDecorr { } } - // mirror-pair subevent (C2Sub) & covariances vs eta bin + // mirror-pair subevent (C2Sub, C3Sub) vs eta bin for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { int ietaC = nEta - ietaA; @@ -1999,10 +1824,6 @@ struct RadialFlowDecorr { float c3SubRecoEffCorA = c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; float c3SubRecoEffCorC = c2RecoEffCor[ietaC] * p1kBarRecoEffCor[ietaA]; - float covTru = p1kBarTruMult[ietaA] * p1kBarTru[ietaC]; - float covReco = p1kBarRecoMult[ietaA] * p1kBarReco[ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[ietaA] * p1kBarRecoEffCor[ietaC]; - if (std::isfinite(c2SubTru)) { histos.fill(HIST("MCGen/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubTru); histos.fill(HIST("MCGen/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubTru); @@ -2039,53 +1860,6 @@ struct RadialFlowDecorr { histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubRecoEffCorC); histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubRecoEffCorC); } - if (std::isfinite(covTru)) { - histos.fill(HIST("MCGen/Prof_Cov_Cent_etabin"), cent, ietaA, covTru); - histos.fill(HIST("MCGen/Prof_Cov_Mult_etabin"), multPV, ietaA, covTru); - } - if (std::isfinite(covReco)) { - histos.fill(HIST("MCReco/Prof_Cov_Cent_etabin"), cent, ietaA, covReco); - histos.fill(HIST("MCReco/Prof_Cov_Mult_etabin"), multPV, ietaA, covReco); - } - if (std::isfinite(covRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Cent_etabin"), cent, ietaA, covRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Mult_etabin"), multPV, ietaA, covRecoEffCor); - } - } - - // FT0 covariance vs narrow eta bin (full range, indexed by the actual pT bin) - for (int ieta = 1; ieta < nEta; ++ieta) { - float covFT0ATru = p1kBarFt0A * p1kBarTru[ieta]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[ieta]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[ieta]; - float covFT0CTru = p1kBarFt0C * p1kBarTru[ieta]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[ieta]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[ieta]; - - if (std::isfinite(covFT0ATru)) { - histos.fill(HIST("MCGen/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0ATru); - histos.fill(HIST("MCGen/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0ATru); - } - if (std::isfinite(covFT0AReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0AReco); - histos.fill(HIST("MCReco/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0AReco); - } - if (std::isfinite(covFT0ARecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0ARecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Mult_etabin"), multPV, ieta, covFT0ARecoEffCor); - } - if (std::isfinite(covFT0CTru)) { - histos.fill(HIST("MCGen/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CTru); - histos.fill(HIST("MCGen/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CTru); - } - if (std::isfinite(covFT0CReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CReco); - histos.fill(HIST("MCReco/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CReco); - } - if (std::isfinite(covFT0CRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0CRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Mult_etabin"), multPV, ieta, covFT0CRecoEffCor); - } } // full 2D subevent map @@ -2104,18 +1878,6 @@ struct RadialFlowDecorr { float c3Sub2DReco = (ietaA == ietaC) ? static_cast(c3Reco[ietaA]) : c2Reco[ietaA] * p1kBarReco[ietaC]; float c3Sub2DRecoEffCor = (ietaA == ietaC) ? static_cast(c3RecoEffCor[ietaA]) : c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; - float covTru = p1kBarTruMult[ietaA] * p1kBarTru[ietaC]; - float covReco = p1kBarRecoMult[ietaA] * p1kBarReco[ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[ietaA] * p1kBarRecoEffCor[ietaC]; - - float covFT0ATru = p1kBarFt0A * p1kBarTru[ietaC]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[ietaC]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[ietaC]; - - float covFT0CTru = p1kBarFt0C * p1kBarTru[ietaA]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[ietaA]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[ietaA]; - if (std::isfinite(c2SubTru)) { histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubTru); histos.fill(HIST("MCGen/Prof_GapSum2D"), cent, gap, sum, c2SubTru); @@ -2141,36 +1903,6 @@ struct RadialFlowDecorr { histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DRecoEffCor); histos.fill(HIST("MCRecoEffCorr/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DRecoEffCor); } - - if (std::isfinite(covTru)) { - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covTru); - } - if (std::isfinite(covReco)) { - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covReco); - } - if (std::isfinite(covRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covRecoEffCor); - } - - if (std::isfinite(covFT0ATru)) { - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ATru); - } - if (std::isfinite(covFT0AReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0AReco); - } - if (std::isfinite(covFT0ARecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ARecoEffCor); - } - - if (std::isfinite(covFT0CTru)) { - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CTru); - } - if (std::isfinite(covFT0CReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CReco); - } - if (std::isfinite(covFT0CRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CRecoEffCor); - } } } } @@ -2251,7 +1983,7 @@ struct RadialFlowDecorr { // =========================================================================== // DATA: mean pT // =========================================================================== - void processDataMean(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + void processDataMean(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) { std::array sumWi{}, sumWipti{}; @@ -2355,39 +2087,13 @@ struct RadialFlowDecorr { } } } - - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - amplFT0C += ampl; - auto eta = getEtaFT0(globalId, 1); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); - } - } - histos.fill(HIST("pmeanFT0Amultpv"), coll.multNTracksPV(), amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), coll.multNTracksPV(), amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); } PROCESS_SWITCH(RadialFlowDecorr, processDataMean, "process data to calculate mean pT", cfgRunDataMean); // =========================================================================== // DATA: fluctuations (C2, subevent) + Poisson bootstrap for the base run // =========================================================================== - void processDataFluc(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + void processDataFluc(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) { if (!isEventSelected(coll)) { return; @@ -2407,8 +2113,8 @@ struct RadialFlowDecorr { histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - if (!state.pmeanNchEtabinStep2 || !state.pmeanMultNchEtabinStep2) { - LOGF(warning, "Data fluc: mean pT or mult map missing"); + if (!state.pmeanNchEtabinStep2) { + LOGF(warning, "Data fluc: mean pT map missing"); return; } if (cfgEff && (!state.hEff || !state.hFake)) { @@ -2424,7 +2130,7 @@ struct RadialFlowDecorr { std::array, KIntM>, KNEtaMax> sumpmwk{}; std::array, KNEtaMax> sumwk{}; std::array mean{}, c2{}, c3{}, p1kBar{}; - std::array meanMult{}, p1kBarMult{}; + std::array meanMult{}; // --- Poisson bootstrap: one weight per sample per event --- std::array poisW{}; @@ -2433,14 +2139,6 @@ struct RadialFlowDecorr { poisW[s] = rng.Poisson(1.0); } } - auto fillBS1D = [&](std::array, KMaxBoot>& arr, double x, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, val, poisW[s]); - } - }; auto fillBS2D = [&](std::array, KMaxBoot>& arr, double x, double y, double val) { if (!doBoot) { return; @@ -2498,35 +2196,13 @@ struct RadialFlowDecorr { } } - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - amplFT0A += ft0.amplitudeA()[iCh]; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - amplFT0C += ft0.amplitudeC()[iCh]; - } - } - fillBS1D(bs.amplFT0ACent, cent, amplFT0A); - fillBS1D(bs.amplFT0AMult, coll.multNTracksPV(), amplFT0A); - fillBS1D(bs.amplFT0CCent, cent, amplFT0C); - fillBS1D(bs.amplFT0CMult, coll.multNTracksPV(), amplFT0C); - - double p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - double p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - for (int ieta = 0; ieta < nEta; ++ieta) { const int ibx = state.pmeanNchEtabinStep2->GetXaxis()->FindBin(coll.multNTracksPV()); const int iby = ieta + 1; float mmpt = state.pmeanNchEtabinStep2->GetBinContent(ibx, iby); - float mmMult = state.pmeanMultNchEtabinStep2->GetBinContent(ibx, iby); meanMult[ieta] = sumwk[ieta][1]; - // covariance requires both bins populated: gate the multiplicity deviation on - // the same >=1-track condition as the mean, so cov skips unless both bins qualify - p1kBarMult[ieta] = (sumwk[ieta][1] >= 1.0) ? (meanMult[ieta] - mmMult) : std::numeric_limits::quiet_NaN(); // mean pT of the bin: valid only with >=1 (weighted) track mean[ieta] = (sumwk[ieta][1] >= 1.0) ? (sumpmwk[ieta][1][1] / sumwk[ieta][1]) : std::numeric_limits::quiet_NaN(); @@ -2572,14 +2248,17 @@ struct RadialFlowDecorr { } } - // mirror-pair subevent (C2Sub) & covariances vs eta bin + // mirror-pair subevent (C2Sub, C3Sub) vs eta bin for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { int ietaC = nEta - ietaA; float c2Sub = p1kBar[ietaA] * p1kBar[ietaC]; - float c3SubA = c2[ietaA] * p1kBar[ietaC]; // 2 particles from A, 1 from C - float c3SubC = c2[ietaC] * p1kBar[ietaA]; // 2 particles from C, 1 from A - float covAC = p1kBarMult[ietaA] * p1kBar[ietaC]; - float covCA = p1kBar[ietaA] * p1kBarMult[ietaC]; + // Mirror pairs (A, C = -A): both orderings are filled, each at the bin of its pair, + // so bins A < 0 hold AAB and bins C > 0 hold ABB of the same mirror pair. For a + // symmetric collision system the two agree in expectation: eta -> -eta maps + // AAB(a, -a) onto AAB(-a, a) = ABB(a, -a). Comparing bin A with bin nEta - A is + // therefore a forward/backward check, not two independent observables. + float c3SubA = c2[ietaA] * p1kBar[ietaC]; // AAB: 2 particles from A, 1 from C + float c3SubC = c2[ietaC] * p1kBar[ietaA]; // ABB: 2 particles from C, 1 from A if (std::isfinite(c2Sub)) { histos.fill(HIST("Prof_C2Sub_Cent_etabin"), cent, ietaA, c2Sub); @@ -2599,36 +2278,6 @@ struct RadialFlowDecorr { fillBS2D(bs.c3SubCent, cent, ietaC, c3SubC); fillBS2D(bs.c3SubMult, coll.multNTracksPV(), ietaC, c3SubC); } - if (std::isfinite(covAC)) { - histos.fill(HIST("Prof_Cov_Cent_etabin"), cent, ietaA, covAC); - histos.fill(HIST("Prof_Cov_Mult_etabin"), coll.multNTracksPV(), ietaA, covAC); - fillBS2D(bs.covCent, cent, ietaA, covAC); - fillBS2D(bs.covMult, coll.multNTracksPV(), ietaA, covAC); - } - if (std::isfinite(covCA)) { - histos.fill(HIST("Prof_Cov_Cent_etabin"), cent, ietaC, covCA); - histos.fill(HIST("Prof_Cov_Mult_etabin"), coll.multNTracksPV(), ietaC, covCA); - fillBS2D(bs.covCent, cent, ietaC, covCA); - fillBS2D(bs.covMult, coll.multNTracksPV(), ietaC, covCA); - } - } - - // FT0 covariance vs narrow eta bin (full range, indexed by the actual pT bin) - for (int ieta = 1; ieta < nEta; ++ieta) { - float covFT0Aeta = p1kBarFt0A * p1kBar[ieta]; - float covFT0Ceta = p1kBarFt0C * p1kBar[ieta]; - if (std::isfinite(covFT0Aeta)) { - histos.fill(HIST("Prof_CovFT0A_Cent_etabin"), cent, ieta, covFT0Aeta); - histos.fill(HIST("Prof_CovFT0A_Mult_etabin"), coll.multNTracksPV(), ieta, covFT0Aeta); - fillBS2D(bs.covFT0ACent, cent, ieta, covFT0Aeta); - fillBS2D(bs.covFT0AMult, coll.multNTracksPV(), ieta, covFT0Aeta); - } - if (std::isfinite(covFT0Ceta)) { - histos.fill(HIST("Prof_CovFT0C_Cent_etabin"), cent, ieta, covFT0Ceta); - histos.fill(HIST("Prof_CovFT0C_Mult_etabin"), coll.multNTracksPV(), ieta, covFT0Ceta); - fillBS2D(bs.covFT0CCent, cent, ieta, covFT0Ceta); - fillBS2D(bs.covFT0CMult, coll.multNTracksPV(), ieta, covFT0Ceta); - } } // full 2D subevent map @@ -2640,10 +2289,14 @@ struct RadialFlowDecorr { float sum = (etaValA + etaValB); float c2Sub = (ietaA == ietaC) ? static_cast(c2[ietaA]) : p1kBar[ietaA] * p1kBar[ietaC]; - float c3Sub2D = (ietaA == ietaC) ? static_cast(c3[ietaA]) : c2[ietaA] * p1kBar[ietaC]; // diag: within-bin c3; off-diag: 2 from A, 1 from C - float cov = p1kBarMult[ietaA] * p1kBar[ietaC]; - float covFT0A = p1kBarFt0A * p1kBar[ietaC]; - float covFT0C = p1kBarFt0C * p1kBar[ietaA]; + // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, + // two particles from A and one from C ("AAB"). The same pair of bins read the + // other way round, cell (C,A) = c2[C] p1kBar[A], is "ABB" (one from A, two from C). + // Both are filled, so the map carries AAB and ABB of every bin pair: the row at + // eta_A = eta_ref is AAB with the pair at eta_ref, the column is ABB. Unlike C2, + // this map is NOT symmetric under A <-> C, and the two triangles must not be + // averaged or treated as duplicates. Diagonal: the within-bin c3 ("AAA"). + float c3Sub2D = (ietaA == ietaC) ? static_cast(c3[ietaA]) : c2[ietaA] * p1kBar[ietaC]; if (std::isfinite(c2Sub)) { histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2Sub); @@ -2657,18 +2310,6 @@ struct RadialFlowDecorr { fillBS3D(bs.c3Sub2D, cent, etaValA, etaValB, c3Sub2D); fillBS3D(bs.c3GapSum2D, cent, gap, sum, c3Sub2D); } - if (std::isfinite(cov)) { - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, cov); - fillBS3D(bs.cov2D, cent, etaValA, etaValB, cov); - } - if (std::isfinite(covFT0A)) { - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0A); - fillBS3D(bs.covFT0A2D, cent, etaValA, etaValB, covFT0A); - } - if (std::isfinite(covFT0C)) { - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0C); - fillBS3D(bs.covFT0C2D, cent, etaValA, etaValB, covFT0C); - } } } } From cd50e54b514bb9e64a6665e33948184c082c94b4 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Thu, 1 Oct 2026 13:49:00 +0200 Subject: [PATCH 137/648] [PWGCF] Add high-multiplicity triggers to CF table producer (#18124) --- PWGCF/TableProducer/CMakeLists.txt | 2 +- PWGCF/TableProducer/filterCorrelations.cxx | 37 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/PWGCF/TableProducer/CMakeLists.txt b/PWGCF/TableProducer/CMakeLists.txt index 6438d45a5fe..7d893f3a156 100644 --- a/PWGCF/TableProducer/CMakeLists.txt +++ b/PWGCF/TableProducer/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(filter-correlations SOURCES filterCorrelations.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-correlations-2prong diff --git a/PWGCF/TableProducer/filterCorrelations.cxx b/PWGCF/TableProducer/filterCorrelations.cxx index 8f246c14653..d31f482fa9b 100644 --- a/PWGCF/TableProducer/filterCorrelations.cxx +++ b/PWGCF/TableProducer/filterCorrelations.cxx @@ -15,6 +15,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/Core/TableHelper.h" +#include "Common/Core/Zorro.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -92,7 +93,7 @@ struct FilterCF { O2_DEFINE_CONFIGURABLE(cfgCutMCPt, float, 0.5f, "Minimal pT for particles"); O2_DEFINE_CONFIGURABLE(cfgCutMCEta, float, 0.8f, "Eta range for particles"); O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)"); - O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe) "); + O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe, 14 = fHighTrackMult (pp), 15 = fHighMultFv0 (pp), 16 = fHighTrackMult || fHighMultFv0 (pp))"); O2_DEFINE_CONFIGURABLE(cfgMinOcc, int, 0, "minimum occupancy selection"); O2_DEFINE_CONFIGURABLE(cfgMaxOcc, int, 3000, "maximum occupancy selection"); O2_DEFINE_CONFIGURABLE(cfgCollisionFlags, uint16_t, aod::collision::CollisionFlagsRun2::Run2VertexerTracks, "Request collision flags if non-zero (0 = off, 1 = Run2VertexerTracks)"); @@ -135,6 +136,9 @@ struct FilterCF { HistogramRegistry registrytrackQA{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + Zorro zorro; + int zorroRun = 0; + Produces outputCollisions; Produces outputCollisionsExtra; Produces outputTracks; @@ -194,12 +198,13 @@ struct FilterCF { return; } localMultiplicityEfficiency.reset(dynamic_cast(efficiency->Clone())); - } else { - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); } } + if ((cfgTrigger >= 14 && cfgTrigger <= 16) || (!cfgEfficiencyMultiplicity.value.empty() && cfgLocalEfficiency == 0)) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } if (doprocessTrackQA) { registrytrackQA.add("zvtx", "Z Vertex position; posz (cm); Events", HistType::kTH1F, {{100, -12, 12}}); registrytrackQA.add("eta", "eta distribution; eta; arb. units", HistType::kTH1F, {{100, -2, 2}}); @@ -249,6 +254,26 @@ struct FilterCF { if (cfgTrigger == 13) { // relevant for pO/OO/NeNe, recommended by Physics Board on 27.01.2026; o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); } + + // Zorro-based trigger selection + if (cfgTrigger >= 14 && cfgTrigger <= 16) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + auto bc = collision.template bc_as(); + if (zorroRun != bc.runNumber()) { + std::string zorroMask; + if (cfgTrigger == 14) { // High-multiplicity pp trigger based on tracks (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult"; + } + if (cfgTrigger == 15) { // High-multiplicity pp trigger based on FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighMultFv0"; + } + if (cfgTrigger == 16) { // High-multiplicity pp trigger based on tracks or FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult,fHighMultFv0"; + } + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask); + zorroRun = bc.runNumber(); + } + return isMultSelected && zorro.isSelected(bc.globalBC(), 0); // Set BC threshold to zero to not admit nearby low-multiplicity collisions + } return false; } @@ -488,7 +513,7 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processDataMults, "Process data with multiplicity sets", false); - void processTrackQA(soa::Filtered>::iterator const& collision, soa::Filtered> const& tracks) + void processTrackQA(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered> const& tracks) { if (!keepCollision(collision)) { return; From 424e128b4e75291b3a2541f3635d14a04907887d Mon Sep 17 00:00:00 2001 From: Cristian-Moscatelli Date: Thu, 1 Oct 2026 17:09:19 +0200 Subject: [PATCH 138/648] [PWGLF] Add processMCasData in PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx (#18134) Co-authored-by: Cristian Moscatelli --- .../Nuspex/deuteronInTriggeredEvents.cxx | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx index 9d855c71fda..22cca32068a 100644 --- a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx +++ b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx @@ -266,6 +266,7 @@ struct DeuteronInTriggeredEvents { Produces nucleiTable; // For data Produces nucleiTableMCExtension; // For MC analysis Produces genEventMCSel; // For MC reco events + Produces nucleiTableMC; // For MC analysis as data Service ccdb; Service pdgDB; // For INELgt0 gen MC selection Zorro zorro; // Definition of Zorro: helpful for skimmed data @@ -1111,6 +1112,77 @@ struct DeuteronInTriggeredEvents { } } PROCESS_SWITCH(DeuteronInTriggeredEvents, processMC, "MC analysis", false); + + void processMCasData(soa::Join const& collisions, aod::McCollisions const& mcCollisions, soa::Join const& tracks, aod::McParticles const& particlesMC, aod::BCsWithTimestamps const&) + { + nuclei::candidates.clear(); + std::vector goodCollisions(mcCollisions.size(), false); + + for (const auto& collision : collisions) { + if (!eventSelectionWithHisto(collision)) { + continue; + } + + // Avoid unwanted memory leaks + if (!collision.has_mcCollision()) + continue; + + int mcId = collision.mcCollisionId(); + if (mcId < 0 || mcId >= static_cast(mcCollisions.size())) + continue; + + goodCollisions[collision.mcCollisionId()] = true; + const auto& slicedTracks = tracks.sliceBy(tracksPerCollisions, collision.globalIndex()); + fillDataInfo(collision, slicedTracks); + } + + std::vector isReconstructed(particlesMC.size(), false); + for (size_t i{0}; i < nuclei::candidates.size(); ++i) { + auto& c = nuclei::candidates[i]; + if (c.fillTree) { + auto label = tracks.iteratorAt(c.globalIndex); + + if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + continue; + } + + auto particle = particlesMC.iteratorAt(label.mcParticleId()); + + int motherPdgCode = 0; + float motherDecRadius = -1; + isReconstructed[particle.globalIndex()] = true; + + if (particle.isPhysicalPrimary()) { + c.flags |= kIsPhysicalPrimary; + if (particle.has_mothers()) { + for (const auto& motherparticle : particle.mothers_as()) { + if (std::find(nuclei::hfMothCodes.begin(), nuclei::hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != nuclei::hfMothCodes.end()) { + c.flags |= kIsSecondaryFromWeakDecay; + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + break; + } + } + } + } else if (particle.getProcess() == TMCProcess::kPDecay) { + c.flags |= kIsSecondaryFromWeakDecay; + for (const auto& motherparticle : particle.mothers_as()) { + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + } + } else { + c.flags |= kIsSecondaryFromMaterial; + } + + isReconstructed[particle.globalIndex()] = true; + float absoDecL = computeAbsoDecL(particle); + + nucleiTableMC(c.pt, c.eta, c.phi, c.tpcInnerParam, c.beta, c.zVertex, c.nContrib, c.dcaXY, c.dcaZ, c.tpcSignal, c.itsChi2, c.tpcChi2, c.tofChi2, c.flags, c.tpcFindableCls, c.tpcCrossedRows, c.itsClsMap, c.tpcNCls, c.tpcNClsShared, c.clusterSizesITS, goodCollisions[particle.mcCollisionId()], particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), motherPdgCode, motherDecRadius, absoDecL); + } + } + } + + PROCESS_SWITCH(DeuteronInTriggeredEvents, processMCasData, "MC as data analysis", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 15c66ee19c3336d544bcfecdd0d373b80aa362e1 Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Thu, 1 Oct 2026 19:40:48 +0200 Subject: [PATCH 139/648] [PWGLF] Deuteron proton correlation: add ITS cut for purity studies (#18140) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index db3a2218882..3c0c6b0ee6b 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -1022,12 +1022,20 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); registryQa.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); if (track.sign() > 0) { - registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } } if (track.sign() < 0) { - registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } } if (isProton(track, -1)) { From d5b6d3e376ca28a76fa731bfec64f5e9358c87f5 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:46:38 +0200 Subject: [PATCH 140/648] [PWGLF] ZDC correlation and improved recentering (#18142) --- .../Common/zdc2stagecalibration.cxx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 6c686f336ec..95bf7d01141 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -108,6 +108,10 @@ struct zdc2stagecalibration { static constexpr int kQRecenteringMultidimNFeatures = 35; static constexpr int kQRecenteringMultidimNMatrixMoments = kQRecenteringMultidimNFeatures * (kQRecenteringMultidimNFeatures + 1) / 2; static constexpr int kQRecenteringMultidimNMoments = kQRecenteringMultidimNMatrixMoments + kQRecenteringNComponents * kQRecenteringMultidimNFeatures; + // Additional sufficient statistics for the flatness-constrained multidimensional fit. + // Per conditioning bin store 35 sums of the cubic features followed by + // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N. + static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents; void init(o2::framework::InitContext&) { @@ -128,7 +132,9 @@ struct zdc2stagecalibration { AxisSpec ratioAxis = {240, 0.0, 2.4, "#Sigma tower/common"}; AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments, 0.0, static_cast(kQRecenteringMultidimNMoments), "multidimensional regression moment"}; + AxisSpec qRecenteringFlatnessMomentAxis = {kQRecenteringFlatnessNMoments, 0.0, static_cast(kQRecenteringFlatnessNMoments), "flatness sufficient-statistic index"}; AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; + AxisSpec resolutionCentralityAxis = {50, 0.0, 50.0, "centrality (%)"}; AxisSpec vertexXYAxis = {100, -0.5, 0.5, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; @@ -198,6 +204,17 @@ struct zdc2stagecalibration { histos.add("QRecenteringCalibration/hRegressionMoments", "Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMomentAxis}); histos.add("QRecenteringCalibration/hRegressionMomentsMultidim", "Multidimensional cubic Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMultidimMomentAxis}); + // Sufficient statistics used only when deriving the multidimensional map with + // an additional flatness penalty. They do not alter the 35-feature model or + // the application-side CCDB format. For each (centrality,conditioning) bin: + // indices 0..34 = sum(X_i), indices 35..38 = sum(QxA,QyA,QxC,QyC). + if (deriveQRecentering.value && qRecenteringMethod.value == 1) { + histos.add("QRecenteringCalibration/hFlatnessMomentsTime", "Flatness sufficient statistics vs time;centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVx", "Flatness sufficient statistics vs v_{x};centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVy", "Flatness sufficient statistics vs v_{y};centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVz", "Flatness sufficient statistics vs v_{z};centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringFlatnessMomentAxis}); + } + histos.add("QRecenteringQA/pQBeforeVsCentrality", "Q before recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); histos.add("QRecenteringQA/pQBeforeVsTime", "Q before recentering vs time;time from SOR (h);Q component;", kTProfile2D, {timeAxis, qComponentAxis}); histos.add("QRecenteringQA/pQBeforeVsVx", "Q before recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); @@ -208,6 +225,7 @@ struct zdc2stagecalibration { histos.add("QRecenteringQA/pQAfterVsVx", "Q after recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVy", "Q after recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); @@ -732,6 +750,25 @@ struct zdc2stagecalibration { histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, index + 0.5, qFeatures[i] * qValues[component]); } } + + // Store only the compact per-bin sums needed to add conditional-mean + // flatness penalties in the post-processing builder. The first feature + // is 1, therefore sum(X_0) is the number of events in that bin. + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const double value = qFeatures[i]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, i + 0.5, value); + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int index = kQRecenteringMultidimNFeatures + component; + const double value = qValues[component]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, index + 0.5, value); + } } } @@ -818,6 +855,12 @@ struct zdc2stagecalibration { const double phiRecenteringA = std::atan2(qValues[1], qValues[0]); const double phiRecenteringC = std::atan2(qValues[3], qValues[2]); + // ZDC resolution + const double qMagA = std::hypot(qValues[0], qValues[1]); + const double qMagC = std::hypot(qValues[2], qValues[3]); + if (centrality >= 0.f && centrality < 50.f && qMagA > 0. && qMagC > 0.) { + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentrality"), centrality, std::cos(phiRecenteringA - phiRecenteringC)); + } histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNA"), phiRecenteringA); histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNC"), phiRecenteringC); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNA"), centrality, phiRecenteringA); From b2754ccd14d4e1863321cbdaec1d7332532affe4 Mon Sep 17 00:00:00 2001 From: bhattdurgesh921-sys Date: Fri, 2 Oct 2026 03:14:35 +0530 Subject: [PATCH 141/648] =?UTF-8?q?[PWGLF]=20added=20the=20constant=20shif?= =?UTF-8?q?t=20for=20the=20TOF=20species=20and=20also=20pt=20dependent=20D?= =?UTF-8?q?C=E2=80=A6=20(#18143)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGLF/Tasks/Resonances/deltaAnalysis.cxx | 72 ++++++++++++++---------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx index 0d39c167ce7..23e2378ac41 100644 --- a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -94,6 +94,7 @@ struct DeltaAnalysis { Configurable cfgRequireRecoINELgt0{"cfgRequireRecoINELgt0", true, "Require reconstructed INEL>0 event class (at least one PV track in |eta|<1)"}; Configurable cfgUseNoSameBunchPileupCut{"cfgUseNoSameBunchPileupCut", false, "Apply kNoSameBunchPileup event selection"}; Configurable cfgUseGoodZvtxFT0vsPVCut{"cfgUseGoodZvtxFT0vsPVCut", false, "Apply kIsGoodZvtxFT0vsPV event selection"}; + Configurable cfgUseGoodITSLayersAll{"cfgUseGoodITSLayersAll", false, "Apply kIsGoodITSLayersAll event selection"}; Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "Apply occupancy-in-time-range cut"}; Configurable cfgOccupancyMin{"cfgOccupancyMin", 0, "Minimum track occupancy in time range"}; Configurable cfgOccupancyMax{"cfgOccupancyMax", 9999, "Maximum track occupancy in time range"}; @@ -101,6 +102,7 @@ struct DeltaAnalysis { Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality percentile"}; Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum centrality percentile"}; Configurable cfgUseMCTruthCentrality{"cfgUseMCTruthCentrality", false, "Use MC truth centrality for generated Delta histograms"}; + Configurable cfgApplyMCTruthINELgt0{"cfgApplyMCTruthINELgt0", true, "Apply truth-level INEL>0 selection to MC generated events"}; } evSel; struct : ConfigurableGroup { @@ -125,7 +127,7 @@ struct DeltaAnalysis { Configurable applyTPCChi2NClCut{"applyTPCChi2NClCut", true, "Enable/disable maximum TPC chi2/NCl cut"}; Configurable applyITSChi2NClCut{"applyITSChi2NClCut", true, "Enable/disable maximum ITS chi2/NCl cut"}; Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require isPrimaryTrack flag"}; - Configurable requireGlobalTrackNoDCA{"requireGlobalTrackNoDCA", true, "Require isGlobalTrackWoDCA flag"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track"}; Configurable requirePVContributor{"requirePVContributor", true, "Require PV-contributor flag"}; } trackCuts; @@ -135,6 +137,10 @@ struct DeltaAnalysis { Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.10f, 0.08f, 0.05f, 0.05f}, "Max |DCAxy| for proton per pT bin [cm]"}; Configurable> pionDCAPtBinEdges{"pionDCAPtBinEdges", {0.0f, 0.5f, 1.0f, 2.0f, 1000.f}, "Pion pT bin edges for DCAxy cut [GeV/c]"}; Configurable> pionMaxDCAxyPerPtBin{"pionMaxDCAxyPerPtBin", {0.20f, 0.15f, 0.10f, 0.08f}, "Max |DCAxy| for pion per pT bin [cm]"}; + Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Use continuous pT-dependent DCAxy cut"}; + Configurable dcaXYConst{"dcaXYConst", 0.0105f, "Constant term of pT-dependent DCAxy cut [cm]"}; + Configurable dcaXYPtCoeff{"dcaXYPtCoeff", 0.035f, "pT-dependent coefficient of DCAxy cut [cm]"}; + Configurable dcaXYPtExponent{"dcaXYPtExponent", 1.1f, "pT exponent of DCAxy cut"}; } dcaCuts; struct : ConfigurableGroup { @@ -155,6 +161,7 @@ struct DeltaAnalysis { Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for proton per momentum bin"}; Configurable> protonTOFPIDMomentumBins{"protonTOFPIDMomentumBins", {0.f, 999.f}, "Proton TOF PID momentum bin edges [GeV/c]"}; Configurable> protonTOFNSigmaCutPerBin{"protonTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for proton per momentum bin"}; + Configurable shiftInNsigmaTOFProton{"shiftInNsigmaTOFProton", 0.0f, "Shift applied to proton TOF nSigma"}; } protonPID; struct : ConfigurableGroup { @@ -169,6 +176,7 @@ struct DeltaAnalysis { Configurable> pionTPCNSigmaCutPerBin{"pionTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for pion per momentum bin"}; Configurable> pionTOFPIDMomentumBins{"pionTOFPIDMomentumBins", {0.f, 999.f}, "Pion TOF PID momentum bin edges [GeV/c]"}; Configurable> pionTOFNSigmaCutPerBin{"pionTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for pion per momentum bin"}; + Configurable shiftInNsigmaTOFPion{"shiftInNsigmaTOFPion", 0.0f, "Shift applied to pion TOF nSigma"}; } pionPID; struct : ConfigurableGroup { @@ -432,10 +440,7 @@ struct DeltaAnalysis { histos.add("MCRecoEvent/centralitydistribution", "Centrality distribution (MC);vCentFT0M;Entries", kTH1F, {centAxis}); } - // ── MC reconstructed event mixing: histograms (gated by the dedicated MC mixing switch, - // independent from doprocessMC / doprocessMixedEvent). Mirrors the DATA EM histograms - // one-to-one but lives under its own AnalysisMCReco / THnSparseMCReco keys so downstream - // scripts can never confuse DATA-EM with MCReco-EM. ───────────────────────────────────── + // ── MC reconstructed event mixing: histograms (gated by the dedicated MC mixing switch) if (mixingCfg.enableMCEventMixing) { histos.add("AnalysisMCReco/hDeltaPlusPlusInvMassEM", "#Delta^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); histos.add("AnalysisMCReco/hAntiDeltaPlusPlusInvMassEM", "#bar{#Delta}^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); @@ -447,7 +452,6 @@ struct DeltaAnalysis { histos.add("THnSparseMCReco/hDeltaZeroEM", "THnSparse #Delta^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); histos.add("THnSparseMCReco/hAntiDeltaZeroEM", "THnSparse #bar{#Delta}^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); - // Minimal debug QA: verifies MC mixing actually ran (non-zero when enabled and MC data flows through it). histos.add("QAMC/EventMixing/hMixedEventPairs", "Number of MC mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); histos.add("QAMC/EventMixing/hMixedEventPairsByCentrality", "MC mixed-event collision pairs vs centrality", kTH1F, {centAxis}); } @@ -472,9 +476,6 @@ struct DeltaAnalysis { histos.add("MCGenQA/hGenPionAntiDeltaPlusPlus", "Gen pion from #bar{#Delta}^{++}", kTH1F, {ptAxis}); histos.add("MCGenQA/hGenPionDeltaZero", "Gen pion from #Delta^{0}", kTH1F, {ptAxis}); histos.add("MCGenQA/hGenPionAntiDeltaZero", "Gen pion from #bar{#Delta}^{0}", kTH1F, {ptAxis}); - - // Generated-Delta cut flow. 0=All generated, 1=producedByGenerator, 2=truth vertex-z, - // 3=truth INEL>0, 4=PDG, 5=rapidity, 6=decay channel, 7=final generated histogram. histos.add("MCGenQA/hGenDeltaCutFlow", "Generated #Delta cut flow", kTH1F, {{8, -0.5f, 7.5f}}); histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusInvMass", "#Delta^{++} generated, event-accepted", kTH2F, {ptAxis, massAxis}); @@ -497,9 +498,6 @@ struct DeltaAnalysis { } // ── Event-level cut-flow (QA-only) histograms ─────────────────────────────────────────── - // These are pure bookkeeping: they record how many events survive each existing selection - // step (already implemented in passesEventSelectionImpl(), isTruthInelGt0(), and - // processEventFactor()) and do not themselves apply, add, or alter any selection. { histos.add("CutFlow/Data/hEventCutFlow", "Data event cut flow", kTH1F, {{9, -0.5f, 8.5f}}); auto hDataCutFlow = histos.get(HIST("CutFlow/Data/hEventCutFlow")); @@ -622,8 +620,6 @@ struct DeltaAnalysis { return collision.multNTracksPVeta1() > 0; } - // Truth-level charged-primary identification, used only to build the truth-level INEL>0 event - // class (isTruthInelGt0 below). template bool isChargedPrimaryMC(McParticleType const& mcPart) { @@ -637,8 +633,6 @@ struct DeltaAnalysis { return std::abs(pdgParticle->Charge()) > MinAbsCharge; } - // Truth-level analogue of isRecoInelGt0() above - at least one charged primary within - // |eta| < 1 among the generated particles of a given MC collision. template bool isTruthInelGt0(McParticleTableType const& mcParticlesInCollision) { @@ -710,6 +704,11 @@ struct DeltaAnalysis { } fillEventCutFlowBin(7.f); // GoodZvtxFT0vsPV + if (evSel.cfgUseGoodITSLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + fillEventCutFlowBin(8.f); // Final accepted event return true; } @@ -755,7 +754,7 @@ struct DeltaAnalysis { if (trackCuts.requirePrimaryTrack && !track.isPrimaryTrack()) { return false; } - if (trackCuts.requireGlobalTrackNoDCA && !track.isGlobalTrackWoDCA()) { + if (trackCuts.requireGlobalTrack && !track.isGlobalTrack()) { return false; } if (trackCuts.requirePVContributor && !track.isPVContributor()) { @@ -767,6 +766,14 @@ struct DeltaAnalysis { template bool passesProtonDCASelection(TrackType const& track) { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + const int nBins = static_cast(mProtonDCAPtEdges.size()) - 1; const float pt = track.pt(); bool passed = false; @@ -783,6 +790,14 @@ struct DeltaAnalysis { template bool passesPionDCASelection(TrackType const& track) { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + const int nBins = static_cast(mPionDCAPtEdges.size()) - 1; const float pt = track.pt(); bool passed = false; @@ -831,7 +846,8 @@ struct DeltaAnalysis { } // Circular mode: joint TPC+TOF nSigma radius. - const float combinedNSigPr = tpcNSigPr * tpcNSigPr + tofNSigPr * tofNSigPr; + const float shiftedTofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); + const float combinedNSigPr = tpcNSigPr * tpcNSigPr + shiftedTofNSigPr * shiftedTofNSigPr; const float combinedNSigPi = tpcNSigPi * tpcNSigPi + tofNSigPi * tofNSigPi; const auto circularCutSq = static_cast(protonPID.combinedNSigmaCutProton * protonPID.combinedNSigmaCutProton); const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + @@ -888,7 +904,8 @@ struct DeltaAnalysis { } // Circular mode: joint TPC+TOF nSigma radius. - const float combinedNSigPi = tpcNSigPi * tpcNSigPi + tofNSigPi * tofNSigPi; + const float shiftedTofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); + const float combinedNSigPi = tpcNSigPi * tpcNSigPi + shiftedTofNSigPi * shiftedTofNSigPi; const float combinedNSigPr = tpcNSigPr * tpcNSigPr + tofNSigPr * tofNSigPr; const auto circularCutSq = static_cast(pionPID.combinedNSigmaCutPion * pionPID.combinedNSigmaCutPion); const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + @@ -928,7 +945,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPr = track.tofNSigmaPr(); + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); histos.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); histos.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), pt, tofNSigPr); histos.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); @@ -954,7 +971,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAafter/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAafter/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPi = track.tofNSigmaPi(); + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); histos.fill(HIST("QAafter/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); histos.fill(HIST("QAafter/Pion/tofNSigmaVsPt"), pt, tofNSigPi); histos.fill(HIST("QAafter/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); @@ -980,7 +997,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAMC/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAMC/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPr = track.tofNSigmaPr(); + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); histos.fill(HIST("QAMC/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); histos.fill(HIST("QAMC/Proton/tofNSigmaVsPt"), pt, tofNSigPr); histos.fill(HIST("QAMC/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); @@ -1006,7 +1023,7 @@ struct DeltaAnalysis { histos.fill(HIST("QAMC/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); histos.fill(HIST("QAMC/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); if (!pidShared.useTPCOnlyPID && track.hasTOF()) { - const float tofNSigPi = track.tofNSigmaPi(); + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); histos.fill(HIST("QAMC/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); histos.fill(HIST("QAMC/Pion/tofNSigmaVsPt"), pt, tofNSigPi); histos.fill(HIST("QAMC/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); @@ -1889,7 +1906,7 @@ struct DeltaAnalysis { histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 1.f); // |Vz| accepted const bool truthInelGt0 = isTruthInelGt0(mcParticles); - if (evSel.cfgRequireRecoINELgt0 && !truthInelGt0) { + if (evSel.cfgApplyMCTruthINELgt0 && !truthInelGt0) { return; } histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 2.f); // Truth INEL>0 @@ -1898,8 +1915,6 @@ struct DeltaAnalysis { histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 4.f); // Final generated event histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 3.f); // Final generated event - // ── MODIFIED BLOCK (per user request): centrality-source switch for generated Delta ──── - // Added: evSel.cfgUseMCTruthCentrality (see Configurable added in evSel group above). bool hasAcceptedReco = false; float genCentrality = mcCollision.centFT0M(); @@ -2062,7 +2077,7 @@ struct DeltaAnalysis { histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 1.f); // |Vz| < cfgCutVertex auto mcPartsThisColl = mcParticles.sliceBy(perMcCollisionDelta, mcCollision.globalIndex()); - if (evSel.cfgRequireRecoINELgt0 && !isTruthInelGt0(mcPartsThisColl)) { + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcPartsThisColl)) { continue; } histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 2.f); // Truth INEL>0 @@ -2083,10 +2098,7 @@ struct DeltaAnalysis { if (hasAcceptedReco) { histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 4.f); // Associated reco collision passes event selection histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 5.f); // Final EventAccepted - // Truth-binned: denominator for event_loss = hEventsGenAccepted / hEventsGenAll (unchanged). histos.fill(HIST("EventFactor/hEventsGenAccepted"), truthCentrality); - // NEW - reco-binned: numerator for event_splitting = hEventsGenAcceptedReco / hRecoEvents, - // now on the same (reconstructed) centrality axis as hRecoEvents. histos.fill(HIST("EventFactor/hEventsGenAcceptedReco"), itCent->second); } } From 9054f4059c2e7083d4441f7f0e713bb571ec988a Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Fri, 2 Oct 2026 02:22:41 +0200 Subject: [PATCH 142/648] [PWGHF] Fix correlated bkgs filling in derived data creators (#18139) Co-authored-by: Marcello Di Costanzo --- PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx | 2 +- PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx | 2 +- PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx | 2 +- PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx | 2 +- PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx index 6c8dd98f3c9..ad8e52744e1 100644 --- a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx @@ -299,7 +299,7 @@ struct HfDerivedDataCreatorD0ToKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx index b1cad301ba6..bf66abd0420 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx @@ -304,7 +304,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index b1a2562c47a..c30f037d976 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -287,7 +287,7 @@ struct HfDerivedDataCreatorDsToKKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx index 5f20c81e4a5..c73262f6572 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx @@ -279,7 +279,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx index d543b4605f0..c308143988c 100644 --- a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx @@ -280,7 +280,7 @@ struct HfDerivedDataCreatorLcToPKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)) { continue; } } From 334ea69a9c95c5a52037a03b608181853743cde2 Mon Sep 17 00:00:00 2001 From: sarjeetagami <162087855+sarjeetagami@users.noreply.github.com> Date: Fri, 2 Oct 2026 10:27:01 +0530 Subject: [PATCH 143/648] [PWGLF] Add MC closure part to kstarpbpb task (#18121) Co-authored-by: sarjeeta gami --- PWGLF/Tasks/Resonances/kstarpbpb.cxx | 1322 +++++++++++++++++++++++++- 1 file changed, 1320 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 4211976dc32..0072c90e197 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -131,13 +131,14 @@ struct Kstarpbpb { Configurable nBkgRotations{"nBkgRotations", 9, "Number of rotated copies (background) per each original candidate"}; Configurable fillRotation{"fillRotation", true, "fill rotation"}; Configurable fillSA{"fillSA", true, "same event SA"}; + Configurable doClosureTest{"doClosureTest", false, "Fill dedicated MC closure histograms for spin alignment"}; Configurable fillLikeSignSA{"fillLikeSignSA", false, "fill the same-event like-sign SA sparse (needs fillSA)"}; Configurable useWeight{"useWeight", false, "use EP dep effi weight"}; Configurable useSP{"useSP", false, "use SP"}; // spin-alignment quantization axis Configurable cfgSAFrame{"cfgSAFrame", 0, "SA quantization axis: 0 = event plane (FT0C), 1 = production plane, 2 = random event plane"}; Configurable cfgRndSeed{"cfgRndSeed", 0, "Seed for random event plane (0 = unique seed per job)"}; - Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 2.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 2 is the previous behaviour, n = 1 is the normal to the Psi2 plane"}; + Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 1.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 1 is the normal to the Psi2 plane (default), n = 2 is the old behaviour (axis at 2*Psi2 - pi/2, not the normal; the R2 resolution correction does not apply)"}; // phi(1020) -> K+K- reflection veto for K* (off by default: the task then behaves exactly as before) Configurable cfgPhiVeto{"cfgPhiVeto", false, "Reject K pi pairs whose KK mass (pion candidate given the kaon mass) is near the phi(1020) mass"}; Configurable cfgPhiVetoWindow{"cfgPhiVetoWindow", 0.010f, "Half-width of the phi(1020) veto window (GeV/c^2)"}; @@ -174,6 +175,9 @@ struct Kstarpbpb { Configurable confRapidity{"confRapidity", 0.5, "Rapidity cut"}; Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; + // same-event backgrounds, as for K* (off by default: processSEPhi then behaves exactly as before) + Configurable fillRotation{"fillRotation", false, "Fill the same-event rotated K+K- background (K+ rotated, with confMinRot, confMaxRot and nBkgRotations as for K*)"}; + Configurable fillLikeSign{"fillLikeSign", false, "Fill the same-event like-sign (K+K+ and K-K-) background"}; ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {120, 0.98, 1.1}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisCosThetaStar{"configThnAxisCosThetaStar", {10, -1.0, 1.}, "cos(#vartheta)"}; @@ -181,6 +185,24 @@ struct Kstarpbpb { ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {8, 0, 0.8}, "Rapidity"}; ConfigurableAxis configThnAxisSA{"configThnAxisSA", {200, -1, 1}, "SA"}; } phiSA; + + // MC closure test of the cos(theta*) chain (processMCClosure, processMCClosureME, processMCClosurePhi, processMCClosureMEPhi) + // Step 1: SE unlike-sign minus normalised ME (same cuts as data) -> raw yield vs cos(theta*) + // Step 2: raw yield compared with the true reconstructed yield (hSparseRecTrue) + // Step 3: raw yield / (Acc x Eff)(cos theta*), with Acc x Eff = hSparseRecTrueEff / hSparseGenEff + // Step 4: corrected yield and rho00 compared with the generated input (hSparseGen) + struct : ConfigurableGroup { + std::string prefix = "closure"; + Configurable injectPolarization{"injectPolarization", false, "Reweight the true K*/phi with W(cos theta*) so that the sample has rho00 = rho00Inj"}; + Configurable rho00Inj{"rho00Inj", 1.0f / 3.0f, "Injected rho00, in [0,1] (1/3 = unpolarized)"}; + Configurable useTrueEP{"useTrueEP", true, "Use the MC true event plane angle as Psi (false: Psi = 0, fixed axis as in processMC)"}; + Configurable splitSample{"splitSample", true, "Even MC events build Acc x Eff, odd MC events are the test sample (false: same events for both)"}; + Configurable fillRotBkg{"fillRotBkg", false, "Fill the rotated-pair background (weight 1), cross-check of the ME background"}; + Configurable genAcceptanceCut{"genAcceptanceCut", false, "Daughter pT/eta cut on the generated K*/phi (false: denominator is the full |y| < cut sample, so the correction is the full Acc x Eff in cos theta*)"}; + Configurable requirePrimaryTracks{"requirePrimaryTracks", false, "SE, ME and rotated pairs only from tracks matched to physical primaries (as in phianalysisrun3pbpb / kstar892LightIon); false: all tracks, as in data"}; + Configurable meRequireSingleReco{"meRequireSingleReco", true, "ME: both events must come from an MC collision with exactly one reco collision, as required in the SE (processMCClosure / processMCClosurePhi)"}; + ConfigurableAxis axisCosThetaStar{"axisCosThetaStar", {10, -1.0, 1.0}, "cos(#vartheta*)"}; + } closure; Configurable cfgMinTrackPt{"cfgMinTrackPt", 0.15f, "Minimum track pT"}; @@ -211,6 +233,16 @@ struct Kstarpbpb { using FilTrackMCRecTable = soa::Filtered; Preslice perCollision = aod::track::collisionId; + // reconstructed MC collisions for the closure: adds EPCalibrationTables (psiFT0C etc.) on top of CollisionMCRecTableCentFT0C, + // so the closure can optionally use the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane + using CollisionMCRecTableClosure = soa::SmallGroups>; + // reconstructed MC collisions for the closure mixed event (posZ and centrality filters as in data) + using CollisionMCRecMixTable = soa::Filtered>; + // same as CollisionMCRecMixTable, plus EPCalibrationTables (psiFT0C etc.) for the reconstructed-event-plane closure + using CollisionMCRecMixTableRecoEP = soa::Filtered>; + using BinningTypeMCClosure = ColumnBinningPolicy; + // same as BinningTypeMCClosure, plus Psi_FT0C (as in data's BinningTypeVertexContributor), for the reconstructed-EP closure ME + using BinningTypeMCClosureRecoEP = ColumnBinningPolicy; SliceCache cache; // Partition posTracks = aod::track::signed1Pt > cfgCutCharge; @@ -235,6 +267,9 @@ struct Kstarpbpb { if (cfgSAFrame.value < kEventPlane || cfgSAFrame.value > kRandomEventPlane) { LOGF(fatal, "cfgSAFrame = %d not supported (0 = event plane, 1 = production plane, 2 = random event plane)", cfgSAFrame.value); } + if (cfgSAFrame.value != kProductionPlane && cfgEPNormalHarmonic.value != 1.0f) { + LOGF(warning, "cfgEPNormalHarmonic = %.1f: the SA axis is not the normal to the Psi2 plane, so the R2 = resolution correction does not close", cfgEPNormalHarmonic.value); + } rndGen.SetSeed(cfgRndSeed); AxisSpec phiAxis = {500, -6.28, 6.28, "phi"}; @@ -288,6 +323,31 @@ struct Kstarpbpb { histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); } + if (doClosureTest) { + histos.add("closure/KstarSameEventCosThetaStar", "K*0 closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMixedEventCosThetaStar", "K*0 closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCRecTrueCosThetaStar", "K*0 closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCGenCosThetaStar", "K*0 closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + + const AxisSpec closurePhiMass{phiSA.configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec closurePhiPt{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec closurePhiCos{20, -1.0, 1.0, "cos(#vartheta)"}; + const AxisSpec closurePhiRap{phiSA.configThnAxisRapidity, "|y|"}; + const AxisSpec closurePhiCent{phiSA.configThnAxisCentrality, "Centrality (%)"}; + histos.add("closure/PhiSameEventCosThetaStar", "phi closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMixedEventCosThetaStar", "phi closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCRecTrueCosThetaStar", "phi closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCGenCosThetaStar", "phi closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + } + if (doprocessMCkstarWeight) { histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); histos.add("hImpactParameter", "Impact parameter", kTH1F, {{200, 0.0f, 20.0f}}); @@ -380,6 +440,16 @@ struct Kstarpbpb { if (doprocessSEPhi) { histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + if (phiSA.fillRotation) { + histos.add("phi/hSparseV2RotationSA", "hSparseV2RotationSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2RotationCosThetaStar", "hSparseV2RotationCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (phiSA.fillLikeSign) { + histos.add("phi/hSparseV2SameEventLikeSA", "hSparseV2SameEventLikeSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventLikeCosThetaStar", "hSparseV2SameEventLikeCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + } } if (doprocessMEPhi) { histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); @@ -431,6 +501,128 @@ struct Kstarpbpb { histos.add("evtSigLoss/hPhiGenAfterEvtSelVsCent", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, centLossAxis}); } + // MC closure test of the cos(theta*) chain + if (doprocessMCClosure || doprocessMCClosureME || doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosure || doprocessMCClosureME) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosure) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstar/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstar/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstar/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstar/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstar/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureME) { + histos.add("closureKstar/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosurePhi) { + histos.add("closurePhi/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhi/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhi/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhi/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureMEPhi) { + histos.add("closurePhi/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } + } + + // MC closure test of the cos(theta*) chain, using the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane. + // Needs EPCalibrationTables on the MC sample (i.e. the epvector producer run on it), unlike the true-EP closure above. + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP || doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEP) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstarRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstarRecoEP/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstarRecoEP/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstarRecoEP/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstarRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + // sub-event resolution of the closure sample, needed to derive R2 and un-smear rho00 downstream; same definition as the data ResFT0C*SP + histos.add("closureKstarRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.add("closureKstarRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.add("closureKstarRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEP) { + histos.add("closureKstarRecoEP/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEPPhi) { + histos.add("closurePhiRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhiRecoEP/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhiRecoEP/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhiRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEPPhi) { + histos.add("closurePhiRecoEP/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } + } + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -893,6 +1085,10 @@ struct Kstarpbpb { auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest && track1Sign * track2Sign < 0) { + histos.fill(HIST("closure/KstarSameEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); + } + if (track1Sign * track2Sign < 0) { if (usepolar) { histos.fill(HIST("hSparseSAvsrapsameunlike"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); @@ -1040,6 +1236,9 @@ struct Kstarpbpb { auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA1)); auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest) { + histos.fill(HIST("closure/KstarMixedEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); + } if (usepolar) { histos.fill(HIST("hSparseSAvsrapmix"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); } else { @@ -1200,6 +1399,9 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCRecSA"), kstarMother.M(), kstarMother.Pt(), saRec, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCRecCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCRecTrueCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + } } } } @@ -1269,6 +1471,9 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCGenSA"), kstarMother.M(), kstarMother.Pt(), saGen, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCGenCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCGenCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + } } } } @@ -1469,7 +1674,7 @@ struct Kstarpbpb { histos.fill(HIST("phi/hPsiRandom"), centrality, psiSA); } - ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother, kaonPlusRot, phiRot; for (const auto& track1 : tracks) { if (!(track1.signed1Pt() > cfgCutCharge.value)) { // positive kaon continue; @@ -1498,8 +1703,73 @@ struct Kstarpbpb { continue; } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiSameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } histos.fill(HIST("phi/hSparseV2SameEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2SameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + + // rotated background, same rotation as for K* in processSE: rotate the K+, cos(theta*) of the K- in the rotated pair + if (phiSA.fillRotation && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinusPhi; + auto absRapidityRot = std::abs(phiRot.Rapidity()); + if (absRapidityRot > phiSA.confRapidity) { + continue; + } + // production-plane axis must follow the rotated candidate + auto [cosThetaStarRot, saRot] = getSAValuesPhi(phiRot, kaonMinusPhi, getSAAxis(phiRot, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2RotationSA"), phiRot.M(), phiRot.Pt(), saRot, absRapidityRot, centrality); + histos.fill(HIST("phi/hSparseV2RotationCosThetaStar"), phiRot.M(), phiRot.Pt(), cosThetaStarRot, absRapidityRot, centrality); + } + } + } + } + + // like-sign background (K+K+ and K-K-): same track, PID and pair cuts as the unlike-sign pairs, each pair taken once; + // cos(theta*) of the first kaon of the pair (the two kaons are back to back in the pair rest frame, so the choice only flips the sign) + if (phiSA.fillLikeSign) { + ROOT::Math::PxPyPzMVector kaonLike1, kaonLike2, pairLike; + for (const auto& track1 : tracks) { + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1)) { + continue; + } + for (const auto& track2 : tracks) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() <= 0) { + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonLike1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonLike2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + pairLike = kaonLike1 + kaonLike2; + auto absRapidityLike = std::abs(pairLike.Rapidity()); + if (absRapidityLike > phiSA.confRapidity) { + continue; + } + auto [cosThetaStarLike, saLike] = getSAValuesPhi(pairLike, kaonLike1, getSAAxis(pairLike, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2SameEventLikeSA"), pairLike.M(), pairLike.Pt(), saLike, absRapidityLike, centrality); + histos.fill(HIST("phi/hSparseV2SameEventLikeCosThetaStar"), pairLike.M(), pairLike.Pt(), cosThetaStarLike, absRapidityLike, centrality); + if (track1.sign() > 0) { + histos.fill(HIST("phi/hMassSameEventLikePP"), pairLike.M(), centrality); + } else { + histos.fill(HIST("phi/hMassSameEventLikeNN"), pairLike.M(), centrality); + } + } } } } @@ -1546,6 +1816,9 @@ struct Kstarpbpb { continue; } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiMixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } histos.fill(HIST("phi/hSparseV2MixedEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); histos.fill(HIST("phi/hSparseV2MixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); } @@ -1659,6 +1932,9 @@ struct Kstarpbpb { } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCRecTrueCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } histos.fill(HIST("phi/hSparseV2MCRecSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); } } @@ -1696,6 +1972,9 @@ struct Kstarpbpb { phiMother = kaonPlusPhi + kaonMinusPhi; auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCGenCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCGenCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } histos.fill(HIST("phi/hSparseV2MCGenSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); } } @@ -1814,6 +2093,1045 @@ struct Kstarpbpb { } } PROCESS_SWITCH(Kstarpbpb, processEvtLossSigLossMC, "Process event loss and signal loss for K* and phi(1020)", false); + + // ================= MC closure test of the cos(theta*) chain ================= + // Test sample (reconstructed MC analysed like data): + // hSparseSE all same-event unlike-sign pairs (signal + background), true pairs weighted by W + // hSparseME mixed-event unlike-sign pairs (weight 1) -> step 1: SE - norm * ME + // hSparseRecTrue reconstructed true K* (phi), weighted by W -> step 2: truth for the raw yield + // hSparseGen generated K* (phi), daughters in acceptance, weighted -> step 4: input yield and rho00 + // Efficiency sample (independent events if closure.splitSample): + // hSparseRecTrueEff / hSparseGenEff = Acc x Eff(cos theta*) -> step 3 + // W(cos) = 3/2 [(1 - rho00) + (3 rho00 - 1) cos^2] with the GENERATED cos(theta*) (W = 1 if injectPolarization is off) + + // W(cos) = 3/2 [(1 - rho) + (3 rho - 1) cos^2]; = 1 over cos in [-1,1]; W = 1 for rho = 1/3 + double getInjectionWeight(double cosThetaStar) + { + if (!closure.injectPolarization) { + return 1.0; + } + const double rho = closure.rho00Inj.value; + return 1.5 * ((1.0 - rho) + (3.0 * rho - 1.0) * cosThetaStar * cosThetaStar); + } + + // cos(theta*) of 'dau' in the rest frame of 'mother' with respect to 'axis' + double getCosThetaStar(const ROOT::Math::PxPyPzMVector& mother, const ROOT::Math::PxPyPzMVector& dau, const ROOT::Math::XYZVector& axis) + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto threeVecDau = boost(dau).Vect(); + return axis.Dot(threeVecDau) / std::sqrt(threeVecDau.Mag2()) / std::sqrt(axis.Mag2()); + } + + // one Psi per MC event (call once per event: the random-EP option draws a new angle at each call) + double getClosurePsi(double trueEventPlaneAngle) + { + return getSAEventAngle(closure.useTrueEP ? trueEventPlaneAngle : 0.0); + } + + // even MC events -> Acc x Eff sample, odd MC events -> test sample (both if splitSample is off) + static constexpr int64_t SplitSampleModulus = 2; + bool isClosureEffSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 0); + } + bool isClosureTestSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 1); + } + + // track enters the SE / ME / rotated pairs: always if requirePrimaryTracks is off, else only if matched to a physical primary + template + bool selectionClosurePrimary(const TTrack& track) + { + return !closure.requirePrimaryTracks || (track.has_mcParticle() && track.mcParticle().isPhysicalPrimary()); + } + + // number of reco collisions per MC collision (for the single-reco requirement of the ME, same as the SE) + std::vector countRecoPerMcCollision(aod::McCollisionLabels const& mcLabels, int64_t nMcCollisions) + { + std::vector nRecPerMc(nMcCollisions, 0); + for (const auto& label : mcLabels) { + if (label.mcCollisionId() >= 0 && label.mcCollisionId() < nMcCollisions) { + nRecPerMc[label.mcCollisionId()]++; + } + } + return nRecPerMc; + } + + // mixed-event pair of the closure test sample: same event class as the SE + template + bool selectionClosurePairME(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMC(collision1) || !selectionEventMC(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // reco-event selection of processMC / processMCPhi, plus the data centrality cut (identical for SE and ME) + template + bool selectionEventMC(const TCollision& collision) + { + return collision.sel8() && selectionEventBits(collision) && std::abs(collision.posZ()) < cfgCutVertex && std::abs(collision.centFT0C()) < cfgCutCentrality; + } + + // reco-event selection of the reconstructed-Psi_FT0C closure: selectionEventMC plus the EP-calibration trigger required + // on data (processSE / processSEPhi via selectionEventCuts), only available once EPCalibrationTables is joined + template + bool selectionEventMCRecoEP(const TCollision& collision) + { + return selectionEventMC(collision) && collision.triggereventep(); + } + + // mixed-event pair of the reconstructed-Psi_FT0C closure test sample: same as selectionClosurePairME, with the EP trigger + template + bool selectionClosurePairMERecoEP(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMCRecoEP(collision1) || !selectionEventMCRecoEP(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // K pi pair from the decay of one primary K*0 / anti-K*0 (|y| < confRapidity) + template + bool isTrueKstarPair(const TTrack& trackKaon, const TTrack& trackPion) + { + if (!trackKaon.has_mcParticle() || !trackPion.has_mcParticle()) { + return false; + } + const auto mcKaon = trackKaon.mcParticle(); + const auto mcPion = trackPion.mcParticle(); + if (!mcKaon.isPhysicalPrimary() || !mcPion.isPhysicalPrimary()) { + return false; + } + if (std::abs(mcKaon.pdgCode()) != PDG_t::kKPlus || std::abs(mcPion.pdgCode()) != PDG_t::kPiPlus) { + return false; + } + for (const auto& motherKaon : mcKaon.template mothers_as()) { + for (const auto& motherPion : mcPion.template mothers_as()) { + if (motherKaon != motherPion) { + continue; + } + if (std::abs(motherKaon.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + if (std::abs(motherKaon.y()) > confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // K+ K- pair from the decay of one primary phi(1020) (|y| < phiSA.confRapidity) + template + bool isTruePhiPair(const TTrack& trackPlus, const TTrack& trackMinus) + { + if (!trackPlus.has_mcParticle() || !trackMinus.has_mcParticle()) { + return false; + } + const auto mcPlus = trackPlus.mcParticle(); + const auto mcMinus = trackMinus.mcParticle(); + if (!mcPlus.isPhysicalPrimary() || !mcMinus.isPhysicalPrimary()) { + return false; + } + if (mcPlus.pdgCode() != PDG_t::kKPlus || mcMinus.pdgCode() != PDG_t::kKMinus) { + return false; + } + for (const auto& motherPlus : mcPlus.template mothers_as()) { + for (const auto& motherMinus : mcMinus.template mothers_as()) { + if (motherPlus != motherMinus) { + continue; + } + if (motherPlus.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + if (std::abs(motherPlus.y()) > phiSA.confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // ---------------- K*0: same event, true reco, generated ---------------- + // track1 = kaon candidate, track2 = pion candidate, unlike-sign, same cuts as processSE. + // The axis is built with getSAAxis, as in processMC (efficiency) and processSE (data). + void processMCClosure(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstar/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstar/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstar/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); // same angle for the rec and the gen part of this event + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstar/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstar/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstar/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstar/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstar/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosure, "Process MC closure of cos(theta*) for K*: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample ---------------- + // Same pairing as processMixedEvent (kaon from event 1, pion from event 2, unlike-sign), axis from event 1. + void processMCClosureME(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstar/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureME, "Process MC closure of cos(theta*) for K*: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated ---------------- + // track1 = K+, track2 = K-, as in processSEPhi; cos(theta*) of the K-. + // The axis is built with getSAAxis, as in processSEPhi/processMEPhi (the real data analysis). + void processMCClosurePhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhi/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhi/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhi/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhi/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhi/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhi/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhi/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhi/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosurePhi, "Process MC closure of cos(theta*) for phi(1020): SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample ---------------- + // Same pairing as processMEPhi (K+K- from two events), cos(theta*) of the K-, axis from event 1. + void processMCClosureMEPhi(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhi/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMEPhi, "Process MC closure of cos(theta*) for phi(1020): mixed event", false); + + // ================= MC closure test of the cos(theta*) chain, reconstructed (finite-resolution) Psi_FT0C ================= + // Same as processMCClosure(Phi)/processMCClosureME(Phi) above, but Psi is the reconstructed Psi_FT0C (as on data) instead of + // the MC true event plane, and the sub-event resolution of the sample is filled too (ResFT0C*SP, needed to derive R2 and + // un-smear the fitted raw rho00 downstream). Kept as separate process functions (own histogram directories, own collision + // tables joined with EPCalibrationTables) so the true-EP closure above keeps zero dependency on the EP-calibration producer. + + // ---------------- K*0: same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEP(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstarRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closureKstarRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstarRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstarRecoEP/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstarRecoEP/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEP(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEPPhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhiRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closurePhiRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhiRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhiRecoEP/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhiRecoEP/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEPPhi(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: mixed event", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { From 7df39f529fc04f2be929c7108a7970723ea03d4b Mon Sep 17 00:00:00 2001 From: vbarbaso <146095385+vbarbaso@users.noreply.github.com> Date: Fri, 2 Oct 2026 08:59:47 +0200 Subject: [PATCH 144/648] [PWGLF] TOF shift, Flags for Gen and pt dependent DCA cut (#18141) Signed-off-by: Veronika Barbasova Co-authored-by: Veronika Barbasova --- .../Tasks/Resonances/phianalysisTHnSparse.cxx | 119 ++++++++++++++---- 1 file changed, 95 insertions(+), 24 deletions(-) diff --git a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx index 85a1bcdb45e..40cbd0b51cf 100644 --- a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx @@ -18,6 +18,7 @@ #include "PWGLF/Utils/rsnOutput.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -62,6 +63,7 @@ using namespace o2; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; struct PhianalysisTHnSparse { @@ -98,11 +100,15 @@ struct PhianalysisTHnSparse { Configurable etatrack{"etatrack", 1.0f, "Cut: Maximal value of tracks eta."}; Configurable dcaXY{"dcaXY", 1.0f, "Cut: Maximal value of tracks DCA XY."}; Configurable dcaZ{"dcaZ", 1.0f, "Cut: Maximal value of tracks DCA Z."}; + Configurable ptDependentDCA{"ptDependentDCA", false, "Use pT dependent DCA cut according to formula dcaXY = n_sigma * (0.0105 + 0.0350 * pow(pt, -1.1))."}; + Configurable dcaNSigma{"dcaNSigma", 7.0f, "Cut: Maximal value of tracks DCA NSigma."}; Configurable tpcnSigmaPos{"tpcnSigmaPos", 10.0f, "Cut: Maximal value of TPC NSigma of the positive particle."}; Configurable tpcnSigmaNeg{"tpcnSigmaNeg", 10.0f, "Cut: Maximal value of TPC NSigma of the negative particle."}; Configurable tpcPidOnly{"tpcPidOnly", false, "Use TPC only for PID."}; Configurable combinedNSigma{"combinedNSigma", 3.0f, "Cut: Maximal value of NSigma for combined TPC and TOF NSigma cut."}; Configurable ptTOFThreshold{"ptTOFThreshold", 0.5f, "Cut: Minimal value of tracks pt for using TOF PID."}; + Configurable tofNSigmaPosShift{"tofNSigmaPosShift", 0.0f, "Shift of TOF NSigma of the positive particle."}; + Configurable tofNSigmaNegShift{"tofNSigmaNegShift", 0.0f, "Shift of TOF NSigma of the negative particle."}; Configurable tpcNClsFound{"tpcNClsFound", 0, "Cut: Minimal value of found TPC clusters"}; Configurable tpcNClsCrossedRows{"tpcNClsCrossedRows", 0, "Cut: Minimal value of crossed rows in TPC"}; Configurable globalTrack{"globalTrack", false, "Use isGlobalTrack track selection."}; @@ -111,6 +117,17 @@ struct PhianalysisTHnSparse { Configurable rapidity{"rapidity", 0.5f, "Cut: Maximal value of particle rapidity."}; } trackCuts; + struct : ConfigurableGroup { + Configurable bcBorderCutsGen{"bcBorderCutsGen", false, "Use BC TF/ITS ROF border cuts on Gen events."}; + Configurable rctCheckGen{"rctCheckGen", false, "Use RCT flag cut on Gen events."}; + Configurable rctCheckerLabel{"rctCheckerLabel", "CBT_hadronPID", "RCT flag checker label"}; + Configurable rctCheckerZDC{"rctCheckerZDC", false, "RCT flag checker ZDC check"}; + Configurable rctCheckerLimitAcceptAsBad{"rctCheckerLimitAcceptAsBad", false, "RCT flag checker treat Limited Acceptance As Bad"}; + + } mcCuts; + + RCTFlagsChecker rctChecker; + Configurable> sparseAxes{"sparseAxes", std::vector{o2::analysis::rsn::pair_axis::names}, "Axes."}; Configurable> sysAxes{"sysAxes", std::vector{o2::analysis::rsn::systematic_axis::names}, "Axes."}; @@ -148,8 +165,8 @@ struct PhianalysisTHnSparse { // Axes specifications AxisSpec vzQAaxis = {200, -20., 20., "V_{z} (cm)"}; - AxisSpec dcaXYQAaxis = {200, -0.5, 0.5, "DCA_{xy} (cm)"}; - AxisSpec dcaZQAaxis = {200, -0.5, 0.5, "DCA_{z} (cm)"}; + AxisSpec dcaXYQAaxis = {2000, -0.2, 0.2, "DCA_{xy} (cm)"}; + AxisSpec dcaZQAaxis = {2000, -0.2, 0.2, "DCA_{z} (cm)"}; AxisSpec etaQAaxis = {200, -1.0, 1.0, "#eta"}; AxisSpec rapidityQAaxis = {200, -1.0, 1.0, "y"}; AxisSpec tpcNClsQAaxis = {200, 0., 200., "TPC NClusters"}; @@ -238,6 +255,8 @@ struct PhianalysisTHnSparse { rsnOutput = new o2::analysis::rsn::OutputSparse(); rsnOutput->init(sparseAxes, allAxes, sysAxes, allAxesSys, static_cast(produce.produceMC), mixingType, static_cast(produce.produceLikesign), static_cast(produce.produceRotational), static_cast(eventCuts.inelGt0), ®istry); + rctChecker.init(mcCuts.rctCheckerLabel, mcCuts.rctCheckerZDC, mcCuts.rctCheckerLimitAcceptAsBad, mcCuts.rctCheckGen); + // Print summary of configuration LOGF(info, "=== PhianalysisTHnSparse configuration summary ==="); LOGF(info, "produceMC: %s", static_cast(produce.produceMC) ? "true" : "false"); @@ -261,6 +280,8 @@ struct PhianalysisTHnSparse { LOGF(info, "tpcPidOnly: %s", static_cast(trackCuts.tpcPidOnly) ? "true" : "false"); LOGF(info, "combinedNSigma: %.2f", static_cast(trackCuts.combinedNSigma)); LOGF(info, "ptTOFThreshold: %.2f", static_cast(trackCuts.ptTOFThreshold)); + LOGF(info, "tofNSigmaPosShift: %.2f", static_cast(trackCuts.tofNSigmaPosShift)); + LOGF(info, "tofNSigmaNegShift: %.2f", static_cast(trackCuts.tofNSigmaNegShift)); LOGF(info, "tpcNClsFound: %d", static_cast(trackCuts.tpcNClsFound)); LOGF(info, "tpcNClsCrossedRows: %d", static_cast(trackCuts.tpcNClsCrossedRows)); LOGF(info, "globalTrack: %s", static_cast(trackCuts.globalTrack) ? "true" : "false"); @@ -446,24 +467,28 @@ struct PhianalysisTHnSparse { // ----------------------- MC Corrections ----------------------- - registry.add("QAMC/Gen/hNEvents", "Number of MC Gen Events", kTH1F, {{6, 0.0f, 6.0f}}); + registry.add("QAMC/Gen/hNEvents", "Number of MC Gen Events", kTH1F, {{8, 0.0f, 8.0f}}); auto hNEventsGen = registry.get(HIST("QAMC/Gen/hNEvents")); - hNEventsGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events - hNEventsGen->GetXaxis()->SetBinLabel(2, "V_z cut"); // Generated events passing Vz cut - hNEventsGen->GetXaxis()->SetBinLabel(3, "INEL"); // Generated events passing INEL cut - hNEventsGen->GetXaxis()->SetBinLabel(4, "INEL>0"); // Passing INELgt0 cut --> EL numerator for INEL>0 - hNEventsGen->GetXaxis()->SetBinLabel(5, "ALORE INEL"); // Passing ALORE INEL cut - hNEventsGen->GetXaxis()->SetBinLabel(6, "ALORE INEL>0"); // Passing ALORE INEL>0 cut + hNEventsGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events + hNEventsGen->GetXaxis()->SetBinLabel(2, "V_z cut"); // Generated events passing Vz cut + hNEventsGen->GetXaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); // Passing BC TF/ITS ROF border cut + hNEventsGen->GetXaxis()->SetBinLabel(4, "RCTFlagsChecker"); // Passing RCTFlagsChecker cut + hNEventsGen->GetXaxis()->SetBinLabel(5, "INEL"); // Generated events passing INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(6, "INEL>0"); // Passing INELgt0 cut --> EL numerator for INEL>0 + hNEventsGen->GetXaxis()->SetBinLabel(7, "ALORE INEL"); // Passing ALORE INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(8, "ALORE INEL>0"); // Passing ALORE INEL>0 cut hNEventsGen->SetMinimum(0.1); - registry.add("QAMC/Gen/hNEventsCent", "Generated events", HistType::kTH2F, {centQAAxis, {6, 0, 6}}); + registry.add("QAMC/Gen/hNEventsCent", "Generated events", HistType::kTH2F, {centQAAxis, {8, 0, 8}}); auto hNEventsGenCent = registry.get(HIST("QAMC/Gen/hNEventsCent")); hNEventsGenCent->GetYaxis()->SetBinLabel(1, "all"); hNEventsGenCent->GetYaxis()->SetBinLabel(2, "V_z cut"); - hNEventsGenCent->GetYaxis()->SetBinLabel(3, "INEL"); - hNEventsGenCent->GetYaxis()->SetBinLabel(4, "INEL>0"); - hNEventsGenCent->GetYaxis()->SetBinLabel(5, "ALORE INEL"); - hNEventsGenCent->GetYaxis()->SetBinLabel(6, "ALORE INEL>0"); + hNEventsGenCent->GetYaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); + hNEventsGenCent->GetYaxis()->SetBinLabel(4, "RCTFlagsChecker"); + hNEventsGenCent->GetYaxis()->SetBinLabel(5, "INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(6, "INEL>0"); + hNEventsGenCent->GetYaxis()->SetBinLabel(7, "ALORE INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(8, "ALORE INEL>0"); registry.add("QAMC/Rec/hNEvents", "Number of MC Rec Events", kTH1F, {{3, 0.0f, 3.0f}}); auto hNEventsRec = registry.get(HIST("QAMC/Rec/hNEvents")); @@ -600,13 +625,14 @@ struct PhianalysisTHnSparse { { float tofNsigma = 0.0f; int particleType = (track.sign() > 0) ? static_cast(daughterPos) : static_cast(daughterNeg); + float tofNSigmaShift = (track.sign() > 0) ? static_cast(trackCuts.tofNSigmaPosShift) : static_cast(trackCuts.tofNSigmaNegShift); if (particleType == pion) { - tofNsigma = track.tofNSigmaPi(); + tofNsigma = track.tofNSigmaPi() + tofNSigmaShift; } else if (particleType == kaon) { - tofNsigma = track.tofNSigmaKa(); + tofNsigma = track.tofNSigmaKa() + tofNSigmaShift; } else if (particleType == proton) { - tofNsigma = track.tofNSigmaPr(); + tofNsigma = track.tofNSigmaPr() + tofNSigmaShift; } return tofNsigma; } @@ -631,7 +657,13 @@ struct PhianalysisTHnSparse { registry.fill(HIST("QA/Track/hSelection"), 2.5); } - if (std::abs(track.dcaXY()) >= static_cast(trackCuts.dcaXY) || + float dcaXY = static_cast(trackCuts.dcaXY); + if (static_cast(trackCuts.ptDependentDCA)) { + double dcaNSigma = static_cast(trackCuts.dcaNSigma); + dcaXY = (dcaNSigma / 7) * (0.0105 + 0.0350 / std::pow(track.pt(), 1.1)); + } + + if (std::abs(track.dcaXY()) >= dcaXY || std::abs(track.dcaZ()) >= static_cast(trackCuts.dcaZ)) { return false; } @@ -731,6 +763,30 @@ struct PhianalysisTHnSparse { return pointPair; } + template + bool selectedGenEventBcBorderCuts(T const& bc) + { + if (!static_cast(mcCuts.bcBorderCutsGen)) { + return true; + } + if (static_cast(eventCuts.noTimeFrameBorder) && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (static_cast(eventCuts.noITSROFrameBorder) && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + return true; + } + + template + bool selectedGenEventRCT(TBC const& bc) + { + if (!static_cast(mcCuts.rctCheckGen)) { + return true; + } + return !static_cast(mcCuts.rctCheckGen) || rctChecker(bc); + } + void processQA(EventCandidate const& collision, TrackCandidates const& tracks) { if (!static_cast(produce.produceQA)) { @@ -1569,14 +1625,29 @@ struct PhianalysisTHnSparse { registry.fill(HIST("QAMC/Gen/hNEvents"), 1.5); registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 1.5); + // Accepted generated border cuts + const auto bc = mcCollision.bc_as>(); + if (!selectedGenEventBcBorderCuts(bc)) { + return; + } registry.fill(HIST("QAMC/Gen/hNEvents"), 2.5); registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 2.5); + // Accepted generated event RCT flags + if (!selectedGenEventRCT(bc)) { + return; + } + registry.fill(HIST("QAMC/Gen/hNEvents"), 3.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 3.5); + + registry.fill(HIST("QAMC/Gen/hNEvents"), 4.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 4.5); + bool isINELgt0 = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdg)) { isINELgt0 = true; - registry.fill(HIST("QAMC/Gen/hNEvents"), 3.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 3.5); // Event Loss Denominator + registry.fill(HIST("QAMC/Gen/hNEvents"), 5.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 5.5); // Event Loss Denominator } for (const auto& mcParticle : mcParticles) { @@ -1641,12 +1712,12 @@ struct PhianalysisTHnSparse { return; } - registry.fill(HIST("QAMC/Gen/hNEvents"), 4.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 4.5); + registry.fill(HIST("QAMC/Gen/hNEvents"), 6.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 6.5); if (nRecoInelGt0Coll > 0) { - registry.fill(HIST("QAMC/Gen/hNEvents"), 5.5); - registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 5.5); // Event Loss Numerator + registry.fill(HIST("QAMC/Gen/hNEvents"), 7.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 7.5); // Event Loss Numerator } for (const auto& mcParticle : mcParticles) { From 372a0a918b278bc29524f901b9396fe4788e6b16 Mon Sep 17 00:00:00 2001 From: Florian Jonas Date: Fri, 2 Oct 2026 09:38:00 +0200 Subject: [PATCH 145/648] [PWGJE] gammaJetTree changes to merged definition and event selection (#18135) --- PWGJE/Tasks/gammaJetTreeProducer.cxx | 74 +++++++++++++++------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/PWGJE/Tasks/gammaJetTreeProducer.cxx b/PWGJE/Tasks/gammaJetTreeProducer.cxx index 8096c260dc3..9cf316f118e 100644 --- a/PWGJE/Tasks/gammaJetTreeProducer.cxx +++ b/PWGJE/Tasks/gammaJetTreeProducer.cxx @@ -410,7 +410,7 @@ struct GammaJetTreeProducer { return false; } mHistograms.fill(HIST("eventQA"), 1); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, true, rctLabel)) { return false; } mHistograms.fill(HIST("eventQA"), 2); @@ -809,9 +809,10 @@ struct GammaJetTreeProducer { // return recursive list of all daughter IDs /// \brief Gets all daughter particle IDs in the decay chain /// \param particle The particle to start from + /// \param mcParticles The MC particles collection /// \return Vector of daughter particle IDs - template - void getDaughtersInChain(const T& particle, std::vector& daughters, int depth = 0) + template + void getDaughtersInChain(const T& particle, U const& mcParticles, std::vector& daughters, int depth = 0) { // Limit recursion depth to avoid infinite loops if (depth > MaxRecursionDepth) { // 100 generations should be more than enough @@ -822,10 +823,12 @@ struct GammaJetTreeProducer { return; } - const auto& daughterParticles = particle.template daughters_as(); - for (const auto& daughter : daughterParticles) { - daughters.push_back(daughter.globalIndex()); - getDaughtersInChain(daughter, daughters, depth + 1); + // Daughter indices are a contiguous inclusive range. Resolve them on the full table + // so the IDs match cluster MC labels. + const auto daughterIds = particle.daughtersIds(); + for (int daughterId = daughterIds[0]; daughterId <= daughterIds[1]; ++daughterId) { + daughters.push_back(daughterId); + getDaughtersInChain(mcParticles.iteratorAt(daughterId), mcParticles, daughters, depth + 1); } } /// \brief Finds the first physical primary particle in the decay chain (upwards) @@ -885,34 +888,37 @@ struct GammaJetTreeProducer { if (motherIndex != -1) { const auto& mother = mcParticles.iteratorAt(motherIndex); - // get daughters of pi0 mother - auto daughtersMother = mother.template daughters_as(); - // check if there are two daughters that are both photons - if (daughtersMother.size() == 2) { // o2-linter: disable=magic-number (it is just counting number of daughters) - const auto& daughter1 = daughtersMother.iteratorAt(0); - const auto& daughter2 = daughtersMother.iteratorAt(1); - if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { - // get the full stack of particles that these daughters create - std::vector fullDecayChain1; - std::vector fullDecayChain2; - getDaughtersInChain(daughter1, fullDecayChain1); - getDaughtersInChain(daughter2, fullDecayChain2); - bool photon1Found = false; - bool photon2Found = false; - - // check if any of the particles in the fullDecayChain are leading or subleading in the cluster - for (const auto& particleID : fullDecayChain1) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon1Found = true; + // Daughter indices are the inclusive [first, last] range stored on the mother. + // Resolve them on the full MC table, same index space as the cluster labels. + if (mother.has_daughters()) { + const auto daughterIds = mother.daughtersIds(); + const bool hasTwoDaughters = daughterIds[1] == daughterIds[0] + 1; // o2-linter: disable=magic-number (two-body decay) + if (hasTwoDaughters) { + const auto& daughter1 = mcParticles.iteratorAt(daughterIds[0]); + const auto& daughter2 = mcParticles.iteratorAt(daughterIds[1]); + if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { + // include the decay photons themselves, since unconverted photons are usually the cluster inducers + std::vector fullDecayChain1{daughterIds[0]}; + std::vector fullDecayChain2{daughterIds[1]}; + getDaughtersInChain(daughter1, mcParticles, fullDecayChain1); + getDaughtersInChain(daughter2, mcParticles, fullDecayChain2); + bool photon1Found = false; + bool photon2Found = false; + + // check if any of the particles in the fullDecayChain are leading or subleading in the cluster + for (const auto& particleID : fullDecayChain1) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon1Found = true; + } } - } - for (const auto& particleID : fullDecayChain2) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon2Found = true; + for (const auto& particleID : fullDecayChain2) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon2Found = true; + } + } + if (photon1Found && photon2Found) { + isMerged = true; } - } - if (photon1Found && photon2Found) { - isMerged = true; } } } @@ -1242,7 +1248,7 @@ struct GammaJetTreeProducer { for (const auto& mcCluster : mcClusters) { mHistograms.fill(HIST("clusterMC_E_All"), mcCluster.energy()); auto [origin, mcIndex] = getClusterOrigin(mcCluster, mcParticles); - float leadingEnergyFraction = mcCluster.amplitudeA()[0] / mcCluster.energy(); + float leadingEnergyFraction = mcCluster.amplitudeA()[0]; // amplitudes are already stored as energy fractions // Fill MC origin QA histograms if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kPhoton))) { mHistograms.fill(HIST("clusterMC_E_Photon"), mcCluster.energy()); From 915acaef18731791eaec4bf6ac9cd9f70ec92ce5 Mon Sep 17 00:00:00 2001 From: arvindkhuntia <31609955+arvindkhuntia@users.noreply.github.com> Date: Fri, 2 Oct 2026 09:38:40 +0200 Subject: [PATCH 146/648] [PWGJE] Fix leading-jet selection (Rec) and event normalization (#18050) Co-authored-by: Arvind Khuntia --- PWGJE/Tasks/nucleiInJets.cxx | 187 ++++++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 56 deletions(-) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 1891dc04b65..156020d18b3 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -258,6 +258,8 @@ struct nucleiInJets { using JetMCPartTable = soa::Filtered>; using JetMCDetTable = soa::Filtered>; + Preslice> detectorJetsPerCollision = aod::jet::collisionId; + SliceCache cache; HistogramRegistry jetHist{"jetHist", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -328,7 +330,8 @@ struct nucleiInJets { jetHist.add("jet/h2JetPtVsBkgRho", "jet_{p_{T}} vs background #rho; jet_{p_{T}} (GeV/c); #rho (GeV/c/area)", kTH2F, {PtJetAxis, {100, 0, 20}}); jetHist.add("jet/h1BkgRho", "Background #rho; #rho (GeV/c/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEvents", "NumbeOfJetEvents", kTH1F, {{1, 0, 1}}); - jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEventsBeforeRhoSelection", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin before rho selection", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin and rho > 0", kTH1F, {{1, 0, 1}}); jetHist.add("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut", "Background #rho for events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; #rho (GeV/#it{c}/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEta", "jet_{#eta}", kTH1F, {{100, -1.0, 1.0}}); jetHist.add("jet/h1JetPhi", "jet_{#phi}", kTH1F, {{80, -1.0, 7.}}); @@ -816,15 +819,23 @@ struct nucleiInJets { jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("jetSelCorr/genSel/hLeadingJetPt", "particle-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{gen} (GeV/#it{c}); Entries", HistType::kTH1F, {{100, 0., 100.}}); - jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "matched detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); + jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); jetHist.add("jetSelCorr/recoSel/hMatchedLeadingJetPt", "selected matched leading jet; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); #it{p}_{T,lead}^{gen} (GeV/#it{c})", HistType::kTH2F, {{120, -20., 100.}, {100, 0., 100.}}); jetHist.add("jetSelCorr/eventNorm/hEventCounts", "event count for leading-jet event-normalization correction; event selection; Entries", HistType::kTH1D, {{2, 0., 2.}}); jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(1, "gen lead #it{p}_{T} > cut"); jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(2, "reco lead #it{p}_{T}^{corr} > cut"); + jetHist.add("jetSelCorr/recoSel/hMatchStatus", "selected detector leading-jet matching; status; reconstructed collisions", HistType::kTH1D, {{3, 0., 3.}}); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(1, "no truth match in MC collision"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(2, "truth axis outside acceptance"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(3, "accepted truth axis"); + jetHist.add("jetSelCorr/eventNorm/hSelectedRecoCollisions", "selected reconstructed collisions per MC collision; selected reconstructed collisions; MC collisions", HistType::kTH1D, {{11, -0.5, 10.5}}); + jetHist.add("jetSelCorr/matchedTruthAxis/hEventCount", "selected reconstructed collisions with accepted matched truth axis; selection; reconstructed collisions", HistType::kTH1D, {{1, 0., 1.}}); + jetHist.add("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType", "generated primaries around the matched truth axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the matched truth jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("jetSelCorr/genSel/jetCone/pt/PtParticleType", "generated primaries in particle-level leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("jetSelCorr/genSel/perpCone/pt/PtParticleType", "generated primaries in particle-level perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); - jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries in matched reco-selected leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); - jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in matched reco-selected perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries around the selected detector leading-jet axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the selected detector leading jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("feeddown/antiProton/jetCone/PtOrigin", "reconstructed #bar{p} origin in jet cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); jetHist.add("feeddown/antiProton/jetCone/PtOriginTPC", "reconstructed #bar{p} origin in jet cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); @@ -1818,7 +1829,7 @@ struct nucleiInJets { jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); } - if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); jetHist.fill(HIST("hNEvents"), 8.5); @@ -1829,7 +1840,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { - jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } } for (const auto& track : tracks) { auto trk = track.track_as(); @@ -1920,7 +1934,7 @@ struct nucleiInJets { jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); } - if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); jetHist.fill(HIST("hNEvents"), 8.5); @@ -1931,7 +1945,10 @@ struct nucleiInJets { return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { - jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } } for (auto& track : tracks) { auto trk = track.track_as(); @@ -2399,7 +2416,7 @@ struct nucleiInJets { return true; } - template + template void fillJetSelectionCorrectionParticles(const ParticlesType& mcParticles, double jetEta, double jetPhi) { const auto perpConePhiJet = getPerpendicuarPhi(jetPhi); @@ -2423,7 +2440,9 @@ struct nucleiInJets { const double delPhi = TVector2::Phi_mpi_pi(jetPhi - mcParticle.phi()); const double rJet = RecoDecay::sqrtSumOfSquares(delEta, delPhi); if (rJet < cfgjetR) { - if constexpr (RecoSelected) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { jetHist.fill(HIST("jetSelCorr/recoSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("jetSelCorr/genSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2435,7 +2454,9 @@ struct nucleiInJets { const double rPerpCone1 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone1); const double rPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (rPerpCone1 < cfgjetR || rPerpCone2 < cfgjetR) { - if constexpr (RecoSelected) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { jetHist.fill(HIST("jetSelCorr/recoSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("jetSelCorr/genSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2453,16 +2474,43 @@ struct nucleiInJets { if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) return; + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) + return; + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) + return; + if (useOccupancy && !isOccupancyAccepted(collision)) + return; + jetHist.fill(HIST("recmatched/vertexZ"), collision.posZ()); // Event-wise random splitting for closure test: decide once per event bool useDataLikeHist = (randUniform.Uniform(0, 1) < 0.5); const float backgroundRho = collision.rho(); + if (usebkgSubractionMC && backgroundRho <= 0.f) { + return; + } const float jetArea = M_PI * cfgjetR * cfgjetR; if (usebkgSubractionMC) { jetHist.fill(HIST("jet/h1BkgRho"), backgroundRho); } + // Select the accepted detector-level leading jet before considering matches. + int32_t leadingDetJetId = -1; + float leadingDetJetPt = -1.f; + if (isWithLeadingJet) { + for (const auto& mcdjet : mcdjets) { + const float mcdJetPtForResponseSel = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); + if (isConeAxisAccepted(mcdjet.eta()) && mcdJetPtForResponseSel > leadingDetJetPt) { + leadingDetJetPt = mcdJetPtForResponseSel; + leadingDetJetId = mcdjet.globalIndex(); + } + } + if (leadingDetJetId < 0) { + return; + } + } + + std::vector mcdJetIds{}; std::vector mcdJetPt{}; std::vector mcdJetPhi{}; std::vector mcdJetEta{}; @@ -2483,7 +2531,7 @@ struct nucleiInJets { continue; const float mcdJetPtForResponse = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); - if (mcdJetPtForResponse < cfgjetPtBkgSubMinMC) { + if (mcdJetPtForResponse <= cfgjetPtBkgSubMinMC) { continue; } const double jetAxisEtaForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.eta() : mcpjet.eta()) : mcdjet.eta(); @@ -2496,6 +2544,7 @@ struct nucleiInJets { jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[0]); jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[1]); + mcdJetIds.push_back(mcdjet.globalIndex()); mcdJetPt.push_back(mcdJetPtForResponse); mcdJetPhi.push_back(mcdjet.phi()); mcdJetEta.push_back(mcdjet.eta()); @@ -2523,6 +2572,13 @@ struct nucleiInJets { } else { LOGP(fatal, "Error: Index {} is out of range for vectors!", indexJet); } + if (isWithLeadingJet) { + const auto leadingMatch = std::find(mcdJetIds.begin(), mcdJetIds.end(), leadingDetJetId); + if (leadingMatch == mcdJetIds.end()) { + return; // Do not replace an unmatched or rejected leading jet with a subleading jet. + } + indexJet = std::distance(mcdJetIds.begin(), leadingMatch); + } if (useMcC) { if (useDataLikeHist) jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); @@ -2776,7 +2832,7 @@ struct nucleiInJets { int nprocessSimJEEvents = 0; void processJetSelectionCorrection(aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, - JetMCDetTable const&, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) + JetMCDetTable const& mcdjets, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) { if (std::abs(collision.posZ()) > cfgMaxZVertex) { return; @@ -2805,61 +2861,80 @@ struct nucleiInJets { fillJetSelectionCorrectionParticles(mcParticles, genLeadingJetEta, genLeadingJetPhi); } - bool hasRecoSelectedLeadingJet = false; - double recoSelectedLeadingJetPt = -999.; - double recoSelectedMatchedGenJetPt = -999.; - double recoSelectedMatchedGenJetEta = -999.; - double recoSelectedMatchedGenJetPhi = -999.; - - for (const auto& mcpjet : mcpjets) { - if (!mcpjet.has_matchedJetGeo()) { + // Count each reconstructed collision, as in data. The generated selection + // above is independent: neither branch requires the other to pass the cut. + int nSelectedRecoCollisions = 0; + const double jetArea = M_PI * cfgjetR * cfgjetR; + for (const auto& recocoll : recocolls) { + if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { continue; } - if (!isConeAxisAccepted(mcpjet.eta())) { + if (usebkgSubractionMC && !(recocoll.rho() > 0.)) { continue; } - for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { - if (!isConeAxisAccepted(mcdjet.eta())) { - continue; - } - double selectedRecoCollisionRho = -1.; - bool hasSelectedRecoCollision = false; - for (const auto& recocoll : recocolls) { - if (mcdjet.collisionId() != recocoll.globalIndex()) { - continue; - } - if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { - continue; - } - selectedRecoCollisionRho = recocoll.rho(); - hasSelectedRecoCollision = true; - break; - } - if (!hasSelectedRecoCollision) { - continue; - } - const double jetArea = M_PI * cfgjetR * cfgjetR; - const double mcdJetPtBkgSub = usebkgSubractionMC ? mcdjet.pt() - selectedRecoCollisionRho * jetArea : mcdjet.pt(); - if (mcdJetPtBkgSub <= cfgjetPtBkgSubMinMC) { + const auto jetsInCollision = mcdjets.sliceBy(detectorJetsPerCollision, recocoll.globalIndex()); + auto leadingJet = jetsInCollision.begin(); + bool hasLeadingJet = false; + double leadingJetPt = cfgjetPtBkgSubMinMC; + for (auto jet = jetsInCollision.begin(); jet != jetsInCollision.end(); ++jet) { + if (!isConeAxisAccepted(jet.eta())) { continue; } - if (mcdJetPtBkgSub > recoSelectedLeadingJetPt) { - hasRecoSelectedLeadingJet = true; - recoSelectedLeadingJetPt = mcdJetPtBkgSub; - recoSelectedMatchedGenJetPt = mcpjet.pt(); - recoSelectedMatchedGenJetEta = mcpjet.eta(); - recoSelectedMatchedGenJetPhi = mcpjet.phi(); + const double correctedPt = usebkgSubractionMC ? jet.pt() - recocoll.rho() * jetArea : jet.pt(); + if (correctedPt > leadingJetPt) { + leadingJet = jet; + leadingJetPt = correctedPt; + hasLeadingJet = true; } } - } + if (!hasLeadingJet) { + continue; + } - if (hasRecoSelectedLeadingJet) { + ++nSelectedRecoCollisions; jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 1.5); - jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), recoSelectedLeadingJetPt); - jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), recoSelectedLeadingJetPt, recoSelectedMatchedGenJetPt); - fillJetSelectionCorrectionParticles(mcParticles, recoSelectedMatchedGenJetEta, recoSelectedMatchedGenJetPhi); + jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), leadingJetPt); + // All selected detector jets, including unmatched jets, use detector axes + // Never substitute a matched subleading jet or mix truth and detector axes + fillJetSelectionCorrectionParticles(mcParticles, leadingJet.eta(), leadingJet.phi()); + + // Preserve a separate matched-truth-axis sample for efficiency comparisons + // Resolve multiple geometrical matches by the smallest angular distance + bool hasTruthMatch = false; + double closestDistance = 1.e9; + double truthPt = 0.; + double truthEta = 0.; + double truthPhi = 0.; + if (leadingJet.has_matchedJetGeo()) { + for (const auto& truthJet : leadingJet.template matchedJetGeo_as()) { + if (truthJet.mcCollisionId() != collision.globalIndex()) { + continue; + } + const double distance = RecoDecay::sqrtSumOfSquares(leadingJet.eta() - truthJet.eta(), TVector2::Phi_mpi_pi(leadingJet.phi() - truthJet.phi())); + if (distance < closestDistance) { + closestDistance = distance; + hasTruthMatch = true; + truthPt = truthJet.pt(); + truthEta = truthJet.eta(); + truthPhi = truthJet.phi(); + } + } + } + if (!hasTruthMatch) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 0.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), leadingJetPt, truthPt); + if (!isConeAxisAccepted(truthEta)) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 1.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 2.5); + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/hEventCount"), 0.5); + fillJetSelectionCorrectionParticles(mcParticles, truthEta, truthPhi); } + jetHist.fill(HIST("jetSelCorr/eventNorm/hSelectedRecoCollisions"), nSelectedRecoCollisions); } void processGenMatched(aod::JetMcCollision const& collision, From b4cc6ecda6a03d31720ee6ab89b328fb965dcb33 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:16:35 +0200 Subject: [PATCH 147/648] [ALICE3] Refactor alice3 configuration parser (#18145) --- ALICE3/Core/CMakeLists.txt | 1 + ALICE3/Core/ConfigurationParser.cxx | 211 ++++++++++++++++++++++++++++ ALICE3/Core/ConfigurationParser.h | 53 +++++++ ALICE3/Core/FlatTrackSmearer.cxx | 3 +- ALICE3/Core/GeometryContainer.cxx | 185 +----------------------- ALICE3/Core/GeometryContainer.h | 24 +--- 6 files changed, 272 insertions(+), 205 deletions(-) create mode 100644 ALICE3/Core/ConfigurationParser.cxx create mode 100644 ALICE3/Core/ConfigurationParser.h diff --git a/ALICE3/Core/CMakeLists.txt b/ALICE3/Core/CMakeLists.txt index 55f916861a6..4bfb95fba00 100644 --- a/ALICE3/Core/CMakeLists.txt +++ b/ALICE3/Core/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_library(ALICE3Core SOURCES TrackUtilities.cxx + ConfigurationParser.cxx FlatLutEntry.cxx FlatTrackSmearer.cxx GeometryContainer.cxx diff --git a/ALICE3/Core/ConfigurationParser.cxx b/ALICE3/Core/ConfigurationParser.cxx new file mode 100644 index 00000000000..0682090d08f --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.cxx @@ -0,0 +1,211 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.cxx +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#include "ConfigurationParser.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace o2::fastsim +{ + +std::map> ConfigurationParser::parseTEnvConfiguration(std::string& filename, std::vector& entries) +{ + std::map> configMap; + filename = gSystem->ExpandPathName(filename.c_str()); + LOG(info) << "Parsing TEnv configuration file: " << filename; + TEnv env(filename.c_str()); + THashList* table = env.GetTable(); + entries.clear(); + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + // key should contain exactly one dot + if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { + LOG(fatal) << "Key " << key << " does not contain exactly one dot"; + continue; + } + const std::string firstPart = key.substr(0, key.find('.')); + if (std::find(entries.begin(), entries.end(), firstPart) == entries.end()) { + entries.push_back(firstPart); + } + } + env.Print(); + // Entries + for (const auto& entry : entries) { + LOG(info) << " Reading entry " << entry; + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + if (key.find(entry + ".") == 0) { + const std::string paramName = key.substr(key.find('.') + 1); + const std::string value = env.GetValue(key.c_str(), ""); + configMap[entry][paramName] = value; + } + } + } + return configMap; +} + +std::string ConfigurationParser::accessFile(const std::string& path, const std::string& downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) +{ + if (path.rfind("ccdb:", 0) == 0) { + const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix + const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); + const std::string lockFile = localPath + ".lock"; + const std::string doneFile = localPath + ".done"; + + // Create directory structure if it doesn't exist + std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); + gSystem->mkdir(dirPath.c_str(), true); + + // Check if file is already fully downloaded + struct stat buffer; + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; + return localPath; + } + + // Acquire file lock for inter-process synchronization + int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); + if (lockFd == -1) { + LOG(error) << " --- Failed to create lock file: " << lockFile; + return localPath; + } + + // Try to acquire exclusive lock (non-blocking) + LOG(info) << " --- Attempting to acquire lock for: " << localPath; + int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); + + if (lockResult == -1 && errno == EWOULDBLOCK) { + // Lock is held by another process - wait up to 10 minutes for download to complete + LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; + close(lockFd); + + const auto startTime = std::chrono::steady_clock::now(); + const auto timeout = std::chrono::minutes(10); + const auto checkInterval = std::chrono::seconds(5); + + while (true) { + // Check if download is complete + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + return localPath; + } + + // Check timeout + auto elapsed = std::chrono::steady_clock::now() - startTime; + if (elapsed >= timeout) { + LOG(fatal) << " --- Timeout waiting for file download: " << localPath << ". Waited for 10 minutes."; + return localPath; + } + + // Wait before checking again + std::this_thread::sleep_for(checkInterval); + } + } else if (lockResult == -1) { + LOG(error) << " --- Failed to acquire lock for: " << lockFile; + close(lockFd); + return localPath; + } + + // Lock acquired successfully - double-check if file was downloaded while we were trying + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + flock(lockFd, LOCK_UN); + close(lockFd); + return localPath; + } + + // File does not exist, retrieve from CCDB + LOG(info) << " --- CCDB source detected for " << path; + std::map metadata; + bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); + if (!status) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Failed to retrieve configuration from CCDB for path: " << ccdbPath; + return ""; + } + LOG(info) << " --- Retrieved configuration from CCDB to: " << localPath; + + // Verify the integrity of the downloaded file + if (stat(localPath.c_str(), &buffer) != 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; + return ""; + } + if (buffer.st_size == 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file is empty: " << localPath; + return ""; + } + LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; + + // Create done marker file to indicate successful download + std::ofstream doneMarker(doneFile); + doneMarker.close(); + + // Release lock + flock(lockFd, LOCK_UN); + close(lockFd); + + // If timeout is specified, schedule file deletion after timeout + if (timeoutSeconds > 0) { + LOG(info) << " --- Deleting configuration file after timeout: " << localPath; + std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { + LOG(info) << " --- Operating deletion of configuration file after timeout: " << localPath; + std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); + if (std::remove(localPath.c_str()) == 0) { + LOG(info) << " --- File deleted successfully: " << localPath; + } else { + LOG(warning) << " --- Failed to delete file: " << localPath; + } + // Also remove the done marker file + if (std::remove(doneFile.c_str()) == 0) { + LOG(info) << " --- Done marker deleted: " << doneFile; + } + }); + deletionThread.detach(); + } + + return localPath; + } + return path; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/ConfigurationParser.h b/ALICE3/Core/ConfigurationParser.h new file mode 100644 index 00000000000..b92a02e59ec --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.h @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.h +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#ifndef ALICE3_CORE_CONFIGURATIONPARSER_H_ +#define ALICE3_CORE_CONFIGURATIONPARSER_H_ + +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +class ConfigurationParser +{ + public: + /** + * @brief Parses a TEnv configuration file with keys of the form "." and returns the key-value pairs split per entry + * @param filename Path to the TEnv configuration file + * @param entries Vector to store the order of the entries as they appear in the file + * @return A map where each key is an entry name and the value is another map of key-value pairs for that entry + */ + static std::map> parseTEnvConfiguration(std::string& filename, std::vector& entries); + + /** + * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. + * @param path The path to the file, either local or ccdb (starting with "ccdb:") + * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" + * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. Must be set when path is a ccdb path. + * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. + * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb + */ + static std::string accessFile(const std::string& path, const std::string& downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_CONFIGURATIONPARSER_H_ diff --git a/ALICE3/Core/FlatTrackSmearer.cxx b/ALICE3/Core/FlatTrackSmearer.cxx index 64c370d52a1..1bd79f088a8 100644 --- a/ALICE3/Core/FlatTrackSmearer.cxx +++ b/ALICE3/Core/FlatTrackSmearer.cxx @@ -11,6 +11,7 @@ #include "FlatTrackSmearer.h" +#include "ALICE3/Core/ConfigurationParser.h" #include "ALICE3/Core/FlatLutEntry.h" #include "ALICE3/Core/GeometryContainer.h" @@ -96,7 +97,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) } LOGF(info, "Loading %s LUT file: '%s'", getParticleName(pdg), filename); - const std::string localFilename = o2::fastsim::GeometryEntry::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, 10); + const std::string localFilename = o2::fastsim::ConfigurationParser::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, o2::fastsim::GeometryContainer::cleanLutWhenLoaded() ? 10 : 0); std::ifstream lutFile(localFilename, std::ifstream::binary); if (!lutFile.is_open()) { diff --git a/ALICE3/Core/GeometryContainer.cxx b/ALICE3/Core/GeometryContainer.cxx index 79f1a2d67c5..7936e66b629 100644 --- a/ALICE3/Core/GeometryContainer.cxx +++ b/ALICE3/Core/GeometryContainer.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file GeometryContainer.h +/// \file GeometryContainer.cxx /// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) /// \brief Set of utilities for the ALICE3 geometry handling /// \since February 13, 2026 @@ -23,66 +23,13 @@ #include #include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include - namespace o2::fastsim { -std::map> GeometryEntry::parseTEnvConfiguration(std::string& filename, std::vector& layers) -{ - std::map> configMap; - filename = gSystem->ExpandPathName(filename.c_str()); - LOG(info) << "Parsing TEnv configuration file: " << filename; - TEnv env(filename.c_str()); - THashList* table = env.GetTable(); - layers.clear(); - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - // key should contain exactly one dot - if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { - LOG(fatal) << "Key " << key << " does not contain exactly one dot"; - continue; - } - const std::string firstPart = key.substr(0, key.find('.')); - if (std::find(layers.begin(), layers.end(), firstPart) == layers.end()) { - layers.push_back(firstPart); - } - } - env.Print(); - // Layers - for (const auto& layer : layers) { - LOG(info) << " Reading layer " << layer; - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - if (key.find(layer + ".") == 0) { - const std::string paramName = key.substr(key.find('.') + 1); - const std::string value = env.GetValue(key.c_str(), ""); - configMap[layer][paramName] = value; - } - } - } - return configMap; -} - bool GeometryContainer::mCleanLutWhenLoaded = true; void GeometryContainer::init(o2::framework::InitContext& initContext) { @@ -159,134 +106,4 @@ void GeometryEntry::replaceValue(const std::string& layerName, const std::string setValue(layerName, key, value); } -std::string GeometryEntry::accessFile(const std::string& path, const std::string& downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) -{ - - if (path.rfind("ccdb:", 0) == 0) { - const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix - const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); - const std::string lockFile = localPath + ".lock"; - const std::string doneFile = localPath + ".done"; - - // Create directory structure if it doesn't exist - std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); - gSystem->mkdir(dirPath.c_str(), true); - - // Check if file is already fully downloaded - struct stat buffer; - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; - return localPath; - } - - // Acquire file lock for inter-process synchronization - int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); - if (lockFd == -1) { - LOG(error) << " --- Failed to create lock file: " << lockFile; - return localPath; - } - - // Try to acquire exclusive lock (non-blocking) - LOG(info) << " --- Attempting to acquire lock for: " << localPath; - int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); - - if (lockResult == -1 && errno == EWOULDBLOCK) { - // Lock is held by another process - wait up to 10 minutes for download to complete - LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; - close(lockFd); - - const auto startTime = std::chrono::steady_clock::now(); - const auto timeout = std::chrono::minutes(10); - const auto checkInterval = std::chrono::seconds(5); - - while (true) { - // Check if download is complete - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - return localPath; - } - - // Check timeout - auto elapsed = std::chrono::steady_clock::now() - startTime; - if (elapsed >= timeout) { - LOG(fatal) << " --- Timeout waiting for geometry file download: " << localPath << ". Waited for 10 minutes."; - return localPath; - } - - // Wait before checking again - std::this_thread::sleep_for(checkInterval); - } - } else if (lockResult == -1) { - LOG(error) << " --- Failed to acquire lock for: " << lockFile; - close(lockFd); - return localPath; - } - - // Lock acquired successfully - double-check if file was downloaded while we were trying - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - flock(lockFd, LOCK_UN); - close(lockFd); - return localPath; - } - - // File does not exist, retrieve from CCDB - LOG(info) << " --- CCDB source detected for detector geometry " << path; - std::map metadata; - bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); - if (!status) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Failed to retrieve geometry configuration from CCDB for path: " << ccdbPath; - return ""; - } - LOG(info) << " --- Retrieved geometry configuration from CCDB to: " << localPath; - - // Verify the integrity of the downloaded file - if (stat(localPath.c_str(), &buffer) != 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; - return ""; - } - if (buffer.st_size == 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file is empty: " << localPath; - return ""; - } - LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; - - // Create done marker file to indicate successful download - std::ofstream doneMarker(doneFile); - doneMarker.close(); - - // Release lock - flock(lockFd, LOCK_UN); - close(lockFd); - - // If timeout is specified, schedule file deletion after timeout - if (timeoutSeconds > 0 && GeometryContainer::cleanLutWhenLoaded()) { - LOG(info) << " --- Deleting geometry configuration file after timeout: " << localPath; - std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { - LOG(info) << " --- Operating deletion of geometry configuration file after timeout: " << localPath; - std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); - if (std::remove(localPath.c_str()) == 0) { - LOG(info) << " --- File deleted successfully: " << localPath; - } else { - LOG(warning) << " --- Failed to delete file: " << localPath; - } - // Also remove the done marker file - if (std::remove(doneFile.c_str()) == 0) { - LOG(info) << " --- Done marker deleted: " << doneFile; - } - }); - deletionThread.detach(); - } - - return localPath; - } - return path; -} - } // namespace o2::fastsim diff --git a/ALICE3/Core/GeometryContainer.h b/ALICE3/Core/GeometryContainer.h index 67037c1b183..c343ecedbb0 100644 --- a/ALICE3/Core/GeometryContainer.h +++ b/ALICE3/Core/GeometryContainer.h @@ -18,6 +18,8 @@ #ifndef ALICE3_CORE_GEOMETRYCONTAINER_H_ #define ALICE3_CORE_GEOMETRYCONTAINER_H_ +#include "ALICE3/Core/ConfigurationParser.h" + #include #include #include @@ -35,32 +37,14 @@ struct GeometryEntry { GeometryEntry() = default; explicit GeometryEntry(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdb = nullptr) { - mFileName = accessFile(filename, "./.ALICE3/Configuration/", ccdb); - mConfigurations = GeometryEntry::parseTEnvConfiguration(mFileName, mLayerNames); + mFileName = ConfigurationParser::accessFile(filename, "./.ALICE3/Configuration/", ccdb); + mConfigurations = ConfigurationParser::parseTEnvConfiguration(mFileName, mLayerNames); LOG(info) << "Loaded geometry configuration from file: " << mFileName << " with " << mLayerNames.size() << " layers."; if (mLayerNames.empty()) { LOG(warning) << "No layers found in geometry configuration file: " << filename; } } - /** - * @brief Parses a TEnv configuration file and returns the key-value pairs split per entry - * @param filename Path to the TEnv configuration file - * @param layers Vector to store the order of the layers as they appear in the file - * @return A map where each key is a layer name and the value is another map of key-value pairs for that layer - */ - static std::map> parseTEnvConfiguration(std::string& filename, std::vector& layers); - - /** - * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. - * @param path The path to the file, either local or ccdb (starting with "ccdb:") - * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" - * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. If nullptr, the function will create a temporary CCDB manager instance. Default is nullptr. - * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. - * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb - */ - static std::string accessFile(const std::string& path, const std::string& downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); - std::map> getConfigurations() const { return mConfigurations; } std::map getConfiguration(const std::string& layerName) const; std::vector getLayerNames() const { return mLayerNames; } From 89b39d7276dc249305ba5bf0a3aa87eae7338e1e Mon Sep 17 00:00:00 2001 From: Oussama3030 <59825001+Oussama3030@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:19:32 +0200 Subject: [PATCH 148/648] [PWGLF] Added ML framework for the Lambdastar (#18138) --- PWGLF/Tasks/Resonances/CMakeLists.txt | 2 +- .../Tasks/Resonances/k892hadronphotonBkg.cxx | 186 ++++++++++- PWGLF/Tasks/Strangeness/CMakeLists.txt | 2 +- PWGLF/Tasks/Strangeness/sigmaanalysis.cxx | 161 ++++++++- PWGLF/Utils/ResonanceMlResponse.h | 316 ++++++++++++++++++ 5 files changed, 661 insertions(+), 6 deletions(-) create mode 100644 PWGLF/Utils/ResonanceMlResponse.h diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 06d226e887c..f8a6669861e 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -316,7 +316,7 @@ o2physics_add_dpl_workflow(k892hadronphoton o2physics_add_dpl_workflow(k892hadronphotonbkg SOURCES k892hadronphotonBkg.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(chk892li diff --git a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx index 07e7490a3a4..0a3ab731da2 100644 --- a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx +++ b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx @@ -16,19 +16,23 @@ #include "PWGLF/DataModel/LFStrangenessMLTables.h" #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -50,6 +54,7 @@ #include using namespace o2; +using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; using std::array; @@ -73,7 +78,10 @@ enum BkgResonance { struct k892hadronphotonBkg { Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; + TRandom3 rotRng{12345}; // struct member; fixed seed for reproducibility across grid jobs // Histogram registry @@ -119,6 +127,22 @@ struct k892hadronphotonBkg { Configurable rotGamma{"rotGamma", false, "Flag to rotate the photon direction"}; } lstarBkgConfig; + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Pair-pT bin edges of the BDT models, one model per bin (pairs outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + } bdt; + ConfigurableAxis axisVertexMixBkg{"axisVertexMixBkg", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "z-vertex bins for mixing"}; ConfigurableAxis axisCentralityMixBkg{"axisCentralityMixBkg", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "centrality bins for mixing"}; @@ -250,6 +274,14 @@ struct k892hadronphotonBkg { ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 1.1f}, "Resonance AP q_{T} (GeV/c)"}; ConfigurableAxis axisCandSel{"axisCandSel", {15, 0.5f, +15.5f}, "Candidate Selection"}; ConfigurableAxis axisOPAngle{"axisOPAngle", {140, 0.0f, 7.0f}, "Opening angle (rad)"}; + // BDT QA axes + ConfigurableAxis mlProb{"mlProb", {100, 0.0f, 1.0f}, "BDT signal score"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisV0APQt{"axisV0APQt", {220, 0.0f, 0.5f}, "V0 AP q_{T} (GeV/c)"}; + ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; } axisConfig; void init(InitContext const&) @@ -259,6 +291,58 @@ struct k892hadronphotonBkg { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per pair-pT bin. MlResponse checks the model files and cutDir, not the rows of scoreCuts + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // It is applied to the Lambda(1520) MIXED background only (for now!!) + if (!lstarBkgConfig.doSameEvtRotation && !lstarBkgConfig.doEvtMixing) { + LOG(warning) << "bdt.enableML is set but no Lambda(1520) background is requested: the BDT will not be applied."; + } + if (kstarBkgConfig.doSameEvtRotation || kstarBkgConfig.doEvtMixing) { + LOG(info) << "The BDT (gamma + Lambda features) is not applied to the K*(892) background."; + } + + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dLambdaDCADaughters", "h2dLambdaDCADaughters", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {axisConfig.mlProb, axisConfig.axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {axisConfig.mlProb, axisConfig.axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {axisConfig.mlProb, axisConfig.axisOPAngle}); + } + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); if (eventSelections.fUseEventSelection) { @@ -351,6 +435,8 @@ struct k892hadronphotonBkg { histos.add("LambdaStarBkg/h4dMixedLambdaStarPtVsAPAlphaVsAPQt", "h4dMixedLambdaStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); } } + + histos.print(); } //_______________________________________________ @@ -793,6 +879,73 @@ struct k892hadronphotonBkg { return std::sqrt(RecoDecay::p2(hadronP) - dp * dp / momTot2); } + //_______________________________________________ + // The two V0s share a daughter track (or are the same V0): rejected by the builder + template + static bool shareDaughters(TV0Object const& photon, TV0Object const& hadron) + { + return photon.globalIndex() == hadron.globalIndex() || + photon.posTrackExtraId() == hadron.posTrackExtraId() || + photon.negTrackExtraId() == hadron.negTrackExtraId() || + photon.posTrackExtraId() == hadron.negTrackExtraId() || + photon.negTrackExtraId() == hadron.posTrackExtraId(); + } + + //_______________________________________________ + // Fill BDT performance QA + template + void fillBDTPerformance(TV0Object const& lambda, TV0Object const& photon, float openAngle, float score, float pt, float mass) + { + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dLambdaDCADaughters"), score, lambda.dcaV0daughters()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, lambda.alpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, lambda.dcanegtopv()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, lambda.dcapostopv()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, lambda.qtarm()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, photon.alpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, photon.v0cosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, photon.dcaV0daughters()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, photon.dcanegtopv()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, photon.dcapostopv()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, photon.qtarm()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, photon.v0radius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, openAngle); + } + + //_______________________________________________ + // BDT selection of a Lambda + photon pair + template + bool selectML(TV0Object const& lambda, TV0Object const& photon, + float openAngle, float pt, float mass) + { + // No model outside the bdt.ptBinEdges range + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(lambda, photon, openAngle); + std::vector outputMl; + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(lambda, photon, openAngle, outputMl[1], pt, mass); + + return isSelected; + } + //_______________________________________________ // Compute same-event rotational background within a single collision. template @@ -820,6 +973,10 @@ struct k892hadronphotonBkg { for (const int& pIdx : photonIndices) { const auto& photon = fullV0s.rawIteratorAt(pIdx); + // Same pair rejection + if (shareDaughters(photon, hadron)) + continue; + // photon as a massless 4-vector ROOT::Math::PtEtaPhiMVector pGamma(photon.pt(), photon.eta(), @@ -853,6 +1010,16 @@ struct k892hadronphotonBkg { // Opening angle between photon and hadron double cosOA = gammaLeg.Vect().Dot(hadronLeg.Vect()) / (gammaLeg.P() * hadronLeg.P()); double openAngle = std::acos(cosOA); + // double pt = reso.Pt(); + // double mass = reso.M(); + + // // To:Do BDT selection (Lambda(1520)) + // if constexpr (resonance == kResoLambdaStar) { + // if (bdt.enableML) { + // if (!selectML(hadron, photon, openAngle, pt, mass)) + // continue; + // } + // } // Armenteros-Podolanski of the rotated pair const std::array gammaMom{static_cast(gammaLeg.Px()), static_cast(gammaLeg.Py()), static_cast(gammaLeg.Pz())}; @@ -882,9 +1049,9 @@ struct k892hadronphotonBkg { //_______________________________________________ // Mixed-event pairing: hadrons and photons come from two different collisions. - // Centrality is taken from the reference collision (the first of the pair) - template - void calculateMixedBackground(TCollision const& refColl, + // Centrality is taken from the reference collision (the first of the pair). + template + void calculateMixedBackground(TRefColl const& refColl, std::vector const& hadronIndices, std::vector const& photonIndices, TV0s const& fullV0s) @@ -909,6 +1076,11 @@ struct k892hadronphotonBkg { for (const int& pIdx : photonIndices) { const auto& photon = fullV0s.rawIteratorAt(pIdx); + + // Same pair rejection as the builder + if (shareDaughters(photon, hadron)) + continue; + float pP = std::hypot(photon.px(), photon.py(), photon.pz()); ROOT::Math::PxPyPzEVector fourMomPhoton( photon.px(), photon.py(), photon.pz(), pP); @@ -931,6 +1103,14 @@ struct k892hadronphotonBkg { if (std::abs(rapidity) > maxRap) continue; + // BDT selection (Lambda(1520) only) + if constexpr (resonance == kResoLambdaStar) { + if (bdt.enableML) { + if (!selectML(hadron, photon, openAngle, pt, mass)) + continue; + } + } + // Armenteros-Podolanski of the mixed pair const std::array gammaMom{photon.px(), photon.py(), photon.pz()}; const std::array hadronMom{hadron.px(), hadron.py(), hadron.pz()}; diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 7a757d05e88..5d3711f586a 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -86,7 +86,7 @@ o2physics_add_dpl_workflow(hstrangecorrelation o2physics_add_dpl_workflow(sigmaanalysis SOURCES sigmaanalysis.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phik0shortanalysis diff --git a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx index fae9617f013..1e94d5e785c 100644 --- a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx @@ -21,17 +21,21 @@ #include "PWGLF/DataModel/LFSigmaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -47,6 +51,7 @@ #include #include +#include #include #include #include @@ -54,6 +59,7 @@ #include using namespace o2; +using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; @@ -84,7 +90,9 @@ enum CentEstimator { struct sigmaanalysis { Service ccdb; + o2::ccdb::CcdbApi ccdbApi; ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; //__________________________________________________ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -100,6 +108,24 @@ struct sigmaanalysis { Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Candidate-pT bin edges of the BDT models, one model per bin (candidates outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + // Configurable mlCorrThreshold{"mlCorrThreshold", 0.5, "Threshold for correlated K* score"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + + } bdt; + struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; @@ -287,6 +313,8 @@ struct sigmaanalysis { ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; ConfigurableAxis axisV0PairRadius{"axisV0PairRadius", {200, 0.0f, 20.0f}, "V0Pair radius (cm)"}; ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {500, 0.0f, 50.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; @@ -305,7 +333,7 @@ struct sigmaanalysis { ConfigurableAxis axisCandSel{"axisCandSel", {20, 0.5f, +20.5f}, "Candidate Selection"}; // ML - ConfigurableAxis MLProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; + ConfigurableAxis mlProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; int NSigma0Cand = 0; void init(InitContext const&) @@ -321,6 +349,65 @@ struct sigmaanalysis { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per candidate-pT bin + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // The model is trained on PCM (V0) photon features, which EMCal clusters do not have + if (doprocessRealDataWithEMCal || doprocessMonteCarloWithEMCal) { + LOG(fatal) << "BDT selection is only available for PCM photons: disable bdt.enableML or use processRealData/processMonteCarlo."; + } + + // BDT performance QA + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisPt, axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisPt, axisSigmaMass, mlProb}); + + // Signal score vs the main topological variables + histos.add("BDT/h2dDCADaughters", "h2dDCADaughters", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {mlProb, axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {mlProb, axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {mlProb, axisOPAngle}); + + // MC-truth-based score + if (doprocessMonteCarlo) { + histos.add("BDT/hScoreTrueSignal", "hScoreTrueSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreTrueBackground", "hScoreTrueBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsPtTrueSignal", "h2dScoreVsPtTrueSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h2dScoreVsPtTrueBackground", "h2dScoreVsPtTrueBackground", kTH2D, {axisPt, mlProb}); + } + } + // Event Counters histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisNch}); @@ -1652,10 +1739,82 @@ struct sigmaanalysis { return true; } + // Fill BDT performance QA + template + void fillBDTPerformance(TSigma0Object const& cand, float score) + { + float pt = cand.pt(); + float mass = cand.sigma0Mass(); + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dDCADaughters"), score, cand.lambdaDCADau()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, cand.lambdaAlpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, cand.lambdaDCANegPV()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, cand.lambdaDCAPosPV()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, cand.lambdaQt()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, cand.photonAlpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, cand.photonCosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, cand.photonDCADau()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, cand.photonDCANegPV()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, cand.photonDCAPosPV()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, cand.photonQt()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, cand.photonRadius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, cand.opAngle()); + + // MC-truth-based separation (signal = particle + antiparticle) + if constexpr (requires { cand.isSigma0(); cand.isLambdaStar(); }) { + bool isTrueSignal = doLambdaStar ? (cand.isLambdaStar() || cand.isAntiLambdaStar()) : (cand.isSigma0() || cand.isAntiSigma0()); + if (isTrueSignal) { + histos.fill(HIST("BDT/hScoreTrueSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueSignal"), pt, score); + } else { + histos.fill(HIST("BDT/hScoreTrueBackground"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueBackground"), pt, score); + } + } + } + + template + bool selectML(TSigma0Object const& cand) + { + // No model outside the bdt.ptBinEdges range + const float pt = cand.pt(); + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(cand, cand, cand.opAngle()); + std::vector outputMl; // [background, signal] + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(cand, outputMl[1]); + + return isSelected; + } + // Apply selections in sigma0 candidates template bool processSigma0Candidate(TSigma0Object const& cand) { + // BDT selection: model trained on PCM photon features only + if constexpr (requires { cand.photonV0Type(); }) { + if (bdt.enableML && !selectML(cand)) + return false; + } + // Photon specific selections if constexpr (requires { cand.photonV0Type(); }) { // Processing PCM photon if (!selectPhoton(cand)) diff --git a/PWGLF/Utils/ResonanceMlResponse.h b/PWGLF/Utils/ResonanceMlResponse.h new file mode 100644 index 00000000000..8a7932bc42f --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.h @@ -0,0 +1,316 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.h +/// \brief Class to compute the ML response for Lambda + photon resonance selections (Sigma0, Lambda(1520)), +/// either from sigma0builder candidates or from a Lambda and a photon V0 of the strangeness derived data +/// \author Oussama Benchikhi + +#ifndef PWGLF_UTILS_RESONANCEMLRESPONSE_H_ +#define PWGLF_UTILS_RESONANCEMLRESPONSE_H_ + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include + +namespace o2::analysis +{ +// list of input features that can be requested via the namesInputFeatures configurable (sigma0builder getter names) +enum class InputFeaturesResonance : uint8_t { + // Lambda + lambdaQt = 0, + lambdaAlpha, + lambdaRadius, + lambdaCosPA, + lambdaDCADau, + lambdaDCANegPV, + lambdaDCAPosPV, + lambdaPosEta, + lambdaNegEta, + lambdaPosPrTPCNSigma, + lambdaPosPiTPCNSigma, + lambdaNegPrTPCNSigma, + lambdaNegPiTPCNSigma, + // Photon + photonQt, + photonAlpha, + photonCosPA, + photonDCADau, + photonDCANegPV, + photonDCAPosPV, + photonRadius, + photonZconv, + photonPsiPair, + photonPosEta, + photonNegEta, + photonPosTPCNSigmaEl, + photonNegTPCNSigmaEl, + // Photon-Lambda pair + opAngle +}; + +template +class ResonanceMlResponse : public MlResponse +{ + public: + ResonanceMlResponse() = default; + ~ResonanceMlResponse() override = default; + + /// Input features of a photon-Lambda pair + template , typename TLambda, typename TPhoton> + std::vector getInputFeatures(TLambda const& lambda, TPhoton const& photon, float opAngle) + { + // sigma0builder candidates carry prefixed columns (lambdaQt(), photonQt(), ...), derived V0s the plain V0 getters + constexpr bool LambdaFromSigma0 = requires(TLambda const& cand) { cand.lambdaQt(); }; + constexpr bool PhotonFromSigma0 = requires(TPhoton const& cand) { cand.photonQt(); }; + + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (static_cast(idx)) { + // Lambda + case InputFeaturesResonance::lambdaQt: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaQt()); + } else { + inputFeatures.emplace_back(lambda.qtarm()); + } + break; + case InputFeaturesResonance::lambdaAlpha: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaAlpha()); + } else { + inputFeatures.emplace_back(lambda.alpha()); + } + break; + case InputFeaturesResonance::lambdaRadius: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaRadius()); + } else { + inputFeatures.emplace_back(lambda.v0radius()); + } + break; + case InputFeaturesResonance::lambdaCosPA: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaCosPA()); + } else { + inputFeatures.emplace_back(lambda.v0cosPA()); + } + break; + case InputFeaturesResonance::lambdaDCADau: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCADau()); + } else { + inputFeatures.emplace_back(lambda.dcaV0daughters()); + } + break; + case InputFeaturesResonance::lambdaDCANegPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCANegPV()); + } else { + inputFeatures.emplace_back(lambda.dcanegtopv()); + } + break; + case InputFeaturesResonance::lambdaDCAPosPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCAPosPV()); + } else { + inputFeatures.emplace_back(lambda.dcapostopv()); + } + break; + case InputFeaturesResonance::lambdaPosEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosEta()); + } else { + inputFeatures.emplace_back(lambda.positiveeta()); + } + break; + case InputFeaturesResonance::lambdaNegEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegEta()); + } else { + inputFeatures.emplace_back(lambda.negativeeta()); + } + break; + case InputFeaturesResonance::lambdaPosPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaPosPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPi()); + } + break; + case InputFeaturesResonance::lambdaNegPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaNegPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPi()); + } + break; + // Photon + case InputFeaturesResonance::photonQt: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonQt()); + } else { + inputFeatures.emplace_back(photon.qtarm()); + } + break; + case InputFeaturesResonance::photonAlpha: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonAlpha()); + } else { + inputFeatures.emplace_back(photon.alpha()); + } + break; + case InputFeaturesResonance::photonCosPA: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonCosPA()); + } else { + inputFeatures.emplace_back(photon.v0cosPA()); + } + break; + case InputFeaturesResonance::photonDCADau: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCADau()); + } else { + inputFeatures.emplace_back(photon.dcaV0daughters()); + } + break; + case InputFeaturesResonance::photonDCANegPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCANegPV()); + } else { + inputFeatures.emplace_back(photon.dcanegtopv()); + } + break; + case InputFeaturesResonance::photonDCAPosPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCAPosPV()); + } else { + inputFeatures.emplace_back(photon.dcapostopv()); + } + break; + case InputFeaturesResonance::photonRadius: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonRadius()); + } else { + inputFeatures.emplace_back(photon.v0radius()); + } + break; + case InputFeaturesResonance::photonZconv: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonZconv()); + } else { + inputFeatures.emplace_back(photon.z()); + } + break; + case InputFeaturesResonance::photonPsiPair: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPsiPair()); + } else { + inputFeatures.emplace_back(photon.psipair()); + } + break; + case InputFeaturesResonance::photonPosEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosEta()); + } else { + inputFeatures.emplace_back(photon.positiveeta()); + } + break; + case InputFeaturesResonance::photonNegEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegEta()); + } else { + inputFeatures.emplace_back(photon.negativeeta()); + } + break; + case InputFeaturesResonance::photonPosTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template posTrackExtra_as().tpcNSigmaEl()); + } + break; + case InputFeaturesResonance::photonNegTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template negTrackExtra_as().tpcNSigmaEl()); + } + break; + // Photon-Lambda pair + case InputFeaturesResonance::opAngle: + inputFeatures.emplace_back(opAngle); + break; + } + } + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() override + { + MlResponse::mAvailableInputFeatures = { + {"lambdaQt", static_cast(InputFeaturesResonance::lambdaQt)}, + {"lambdaAlpha", static_cast(InputFeaturesResonance::lambdaAlpha)}, + {"lambdaRadius", static_cast(InputFeaturesResonance::lambdaRadius)}, + {"lambdaCosPA", static_cast(InputFeaturesResonance::lambdaCosPA)}, + {"lambdaDCADau", static_cast(InputFeaturesResonance::lambdaDCADau)}, + {"lambdaDCANegPV", static_cast(InputFeaturesResonance::lambdaDCANegPV)}, + {"lambdaDCAPosPV", static_cast(InputFeaturesResonance::lambdaDCAPosPV)}, + {"lambdaPosEta", static_cast(InputFeaturesResonance::lambdaPosEta)}, + {"lambdaNegEta", static_cast(InputFeaturesResonance::lambdaNegEta)}, + {"lambdaPosPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPrTPCNSigma)}, + {"lambdaPosPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPiTPCNSigma)}, + {"lambdaNegPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPrTPCNSigma)}, + {"lambdaNegPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPiTPCNSigma)}, + {"photonQt", static_cast(InputFeaturesResonance::photonQt)}, + {"photonAlpha", static_cast(InputFeaturesResonance::photonAlpha)}, + {"photonCosPA", static_cast(InputFeaturesResonance::photonCosPA)}, + {"photonDCADau", static_cast(InputFeaturesResonance::photonDCADau)}, + {"photonDCANegPV", static_cast(InputFeaturesResonance::photonDCANegPV)}, + {"photonDCAPosPV", static_cast(InputFeaturesResonance::photonDCAPosPV)}, + {"photonRadius", static_cast(InputFeaturesResonance::photonRadius)}, + {"photonZconv", static_cast(InputFeaturesResonance::photonZconv)}, + {"photonPsiPair", static_cast(InputFeaturesResonance::photonPsiPair)}, + {"photonPosEta", static_cast(InputFeaturesResonance::photonPosEta)}, + {"photonNegEta", static_cast(InputFeaturesResonance::photonNegEta)}, + {"photonPosTPCNSigmaEl", static_cast(InputFeaturesResonance::photonPosTPCNSigmaEl)}, + {"photonNegTPCNSigmaEl", static_cast(InputFeaturesResonance::photonNegTPCNSigmaEl)}, + {"opAngle", static_cast(InputFeaturesResonance::opAngle)}}; + } +}; + +} // namespace o2::analysis + +#endif // PWGLF_UTILS_RESONANCEMLRESPONSE_H_ From 8bce36d39671d006ca034c1a0e4f3827cb4057f8 Mon Sep 17 00:00:00 2001 From: rolavick Date: Fri, 2 Oct 2026 15:11:26 +0200 Subject: [PATCH 149/648] [PWGUD] new two tracks table producer for global muon tracks (#18144) --- PWGUD/DataModel/TwoFwdTracksEventTables.h | 164 +++++ PWGUD/TableProducer/CMakeLists.txt | 5 + .../twoFwdTracksEventTableProducer.cxx | 609 ++++++++++++++++++ 3 files changed, 778 insertions(+) create mode 100644 PWGUD/DataModel/TwoFwdTracksEventTables.h create mode 100644 PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx diff --git a/PWGUD/DataModel/TwoFwdTracksEventTables.h b/PWGUD/DataModel/TwoFwdTracksEventTables.h new file mode 100644 index 00000000000..93a0309d7a8 --- /dev/null +++ b/PWGUD/DataModel/TwoFwdTracksEventTables.h @@ -0,0 +1,164 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file TwoFwdTracksEventTables.h +/// \author Roman Lavička +/// \since 2026-10-02 +/// \brief A table to store information about forward UPC candidates preselected to have exactly two muon tracks. +/// \brief Forward counterpart of TwoTracksEventTables.h, filled from the output of upcCandProducerGlobalMuon +/// \brief (MCH/MID/MFT tracks, FV0A and ZDC instead of ITS/TPC/TOF tracks, FT0 and event-selection bits). +/// + +#ifndef PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ +#define PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace two_fwd_tracks_tree +{ +// event info +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(Bc, bc, uint64_t); +DECLARE_SOA_COLUMN(NumContrib, numContrib, int); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +// FV0A and ZDC info +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(AmplitudesV0A, amplitudesV0A, std::vector); +DECLARE_SOA_COLUMN(AmpRelBCsV0A, ampRelBCsV0A, std::vector); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +// tracks +DECLARE_SOA_COLUMN(TrkPx, trkPx, float[2]); +DECLARE_SOA_COLUMN(TrkPy, trkPy, float[2]); +DECLARE_SOA_COLUMN(TrkPz, trkPz, float[2]); +DECLARE_SOA_COLUMN(TrkSign, trkSign, int[2]); +DECLARE_SOA_COLUMN(TrkTime, trkTime, float[2]); +DECLARE_SOA_COLUMN(TrkTimeRes, trkTimeRes, float[2]); +DECLARE_SOA_COLUMN(TrkType, trkType, int[2]); +DECLARE_SOA_COLUMN(TrkNClusters, trkNClusters, int[2]); +DECLARE_SOA_COLUMN(TrkPDca, trkPDca, float[2]); +DECLARE_SOA_COLUMN(TrkRAtAbsorberEnd, trkRAtAbsorberEnd, float[2]); +DECLARE_SOA_COLUMN(TrkChi2, trkChi2, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMID, trkChi2MatchMCHMID, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMFT, trkChi2MatchMCHMFT, float[2]); +DECLARE_SOA_COLUMN(TrkMCHBitMap, trkMCHBitMap, int[2]); +DECLARE_SOA_COLUMN(TrkMIDBitMap, trkMIDBitMap, int[2]); +DECLARE_SOA_COLUMN(Trk1MIDBoards, trk1MIDBoards, uint32_t); +DECLARE_SOA_COLUMN(Trk2MIDBoards, trk2MIDBoards, uint32_t); +// truth event +DECLARE_SOA_COLUMN(TrueChannel, trueChannel, int); +DECLARE_SOA_COLUMN(TrueHasRecoColl, trueHasRecoColl, bool); +DECLARE_SOA_COLUMN(TruePosX, truePosX, float); +DECLARE_SOA_COLUMN(TruePosY, truePosY, float); +DECLARE_SOA_COLUMN(TruePosZ, truePosZ, float); +// truth particles +DECLARE_SOA_COLUMN(TrueMotherPx, trueMotherPx, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPy, trueMotherPy, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPz, trueMotherPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPx, trueDaugPx, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPy, trueDaugPy, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPz, trueDaugPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPdgCode, trueDaugPdgCode, int[2]); +// additional info +DECLARE_SOA_COLUMN(ProblematicEvent, problematicEvent, bool); + +} // namespace two_fwd_tracks_tree + +DECLARE_SOA_TABLE(TwoFwdTracks, "AOD", "TWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards); + +DECLARE_SOA_TABLE(TrueTwoFwdTracks, "AOD", "TRUETWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards, + two_fwd_tracks_tree::TrueChannel, + two_fwd_tracks_tree::TrueHasRecoColl, + two_fwd_tracks_tree::TruePosX, + two_fwd_tracks_tree::TruePosY, + two_fwd_tracks_tree::TruePosZ, + two_fwd_tracks_tree::TrueMotherPx, + two_fwd_tracks_tree::TrueMotherPy, + two_fwd_tracks_tree::TrueMotherPz, + two_fwd_tracks_tree::TrueDaugPx, + two_fwd_tracks_tree::TrueDaugPy, + two_fwd_tracks_tree::TrueDaugPz, + two_fwd_tracks_tree::TrueDaugPdgCode, + two_fwd_tracks_tree::ProblematicEvent); + +} // namespace o2::aod + +#endif // PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index ecc22560552..41ee7f57d8d 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -41,6 +41,11 @@ o2physics_add_dpl_workflow(two-tracks-event-table-producer PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(two-fwd-tracks-event-table-producer + SOURCES twoFwdTracksEventTableProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(fwd-track-propagation SOURCES fwdTrackPropagation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking diff --git a/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx new file mode 100644 index 00000000000..1950842a7cf --- /dev/null +++ b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx @@ -0,0 +1,609 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoFwdTracksEventTableProducer.cxx +/// \brief Produces derived table with two forward tracks per event from UD tables created by upcCandProducerGlobalMuon +/// +/// \author Roman Lavicka , Austrian Academy of Sciences & SMI +/// \since 02.10.2026 +// + +#include "PWGUD/Core/UPCHelpers.h" +#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" +#include "PWGUD/DataModel/TwoFwdTracksEventTables.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +namespace +{ +constexpr int NumTracks = 2; // the table stores exactly two forward tracks per event +constexpr int DefaultInt = -999; // value of integer columns without information +constexpr float DefaultFloat = -999.f; // value of float columns without information +constexpr uint32_t DefaultMIDBoards = 0; // value of MID-board columns without information + +enum EventSelection { + EvSelAll = 0, + EvSelFV0Veto, + EvSelNumContrib, + EvSelTwoTracks, + EvSelInvariantMass, + EvSelSystemPt, + EvSelOneTrackMomentum, + EvSelBothTracksMomentum, + NEvSels +}; + +enum FwdTrackSelection { + TrkSelAll = 0, + TrkSelType, + TrkSelPt, + TrkSelEta, + TrkSelRabs, + TrkSelPDca, + TrkSelChi2, + TrkSelChi2MatchMCHMFT, + NTrkSels +}; + +enum TruthTrouble { + TroubleTooManyMothers = 1, + TroubleTooManyDaughters, + TroubleNoTrack, + TroubleMoreTracks, + TroubleDifferentCandidates, + NTroubles +}; + +// reco-level information of one row, shared by the measured and the simulated table +struct RecoInfo { + int32_t runNumber{DefaultInt}; + uint64_t bc{0}; + int numContrib{DefaultInt}; + float posX{DefaultFloat}; + float posY{DefaultFloat}; + float posZ{DefaultFloat}; + float totalFV0AmplitudeA{DefaultFloat}; + std::vector amplitudesV0A; + std::vector ampRelBCsV0A; + float energyCommonZNA{DefaultFloat}; + float energyCommonZNC{DefaultFloat}; + float timeZNA{DefaultFloat}; + float timeZNC{DefaultFloat}; + float px[NumTracks] = {DefaultFloat, DefaultFloat}; + float py[NumTracks] = {DefaultFloat, DefaultFloat}; + float pz[NumTracks] = {DefaultFloat, DefaultFloat}; + int sign[NumTracks] = {DefaultInt, DefaultInt}; + float time[NumTracks] = {DefaultFloat, DefaultFloat}; + float timeRes[NumTracks] = {DefaultFloat, DefaultFloat}; + int type[NumTracks] = {DefaultInt, DefaultInt}; + int nClusters[NumTracks] = {DefaultInt, DefaultInt}; + float pDca[NumTracks] = {DefaultFloat, DefaultFloat}; + float rAbs[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMID[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMFT[NumTracks] = {DefaultFloat, DefaultFloat}; + int mchBitMap[NumTracks] = {DefaultInt, DefaultInt}; + int midBitMap[NumTracks] = {DefaultInt, DefaultInt}; + uint32_t midBoards[NumTracks] = {DefaultMIDBoards, DefaultMIDBoards}; +}; + +// truth-level information of one row of the simulated table +struct TruthInfo { + int channel{-1}; + bool hasRecoColl{false}; + float motherPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPz[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPz[NumTracks] = {DefaultFloat, DefaultFloat}; + int daugPdgCode[NumTracks] = {DefaultInt, DefaultInt}; + bool problem{false}; +}; +} // namespace + +struct TwoFwdTracksEventTableProducer { + Produces twoFwdTracks; + Produces trueTwoFwdTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // declare configurables + Configurable verboseInfo{"verboseInfo", false, {"Print general info to terminal; default it false."}}; + + struct : ConfigurableGroup { + Configurable useFV0Veto{"useFV0Veto", true, {"Reject candidates with FV0A activity"}}; + Configurable cutMaxFV0Amp{"cutMaxFV0Amp", 100.f, {"Maximum FV0A amplitude allowed"}}; + Configurable cutFV0RelBcRange{"cutFV0RelBcRange", 0, {"FV0A veto applied in BCs [-range, +range] around the candidate BC; 0 = candidate BC only"}}; + Configurable useNumContrib{"useNumContrib", false, {"Require given number of forward tracks in the candidate (before track selection)"}}; + Configurable cutNumContrib{"cutNumContrib", 2, {"Number of forward tracks required in the candidate"}}; + } cutSample; + + struct : ConfigurableGroup { + Configurable acceptGlobalMuonTrack{"acceptGlobalMuonTrack", true, {"Accept MCH-MID-MFT tracks"}}; + Configurable acceptGlobalForwardTrack{"acceptGlobalForwardTrack", true, {"Accept MCH-MFT tracks"}}; + Configurable acceptMuonStandaloneTrack{"acceptMuonStandaloneTrack", true, {"Accept MCH-MID tracks"}}; + Configurable acceptMCHStandaloneTrack{"acceptMCHStandaloneTrack", true, {"Accept MCH-only tracks"}}; + Configurable applyFwdTrackSelection{"applyFwdTrackSelection", true, {"Apply kinematic and quality cuts defined below on forward tracks"}}; + Configurable cutMinPt{"cutMinPt", 0.f, {"Forward track cut"}}; + Configurable cutMaxPt{"cutMaxPt", 1e10f, {"Forward track cut"}}; + Configurable cutMinEta{"cutMinEta", -4.f, {"Forward track cut"}}; + Configurable cutMaxEta{"cutMaxEta", -2.5f, {"Forward track cut"}}; + Configurable cutMinRabs{"cutMinRabs", 17.6f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxRabs{"cutMaxRabs", 89.5f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxPDcaLowRabs{"cutMaxPDcaLowRabs", 594.f, {"Forward track pDCA cut for rAbs below 26.5 cm"}}; + Configurable cutMaxPDcaHighRabs{"cutMaxPDcaHighRabs", 324.f, {"Forward track pDCA cut for rAbs above 26.5 cm"}}; + Configurable cutMaxChi2{"cutMaxChi2", 1e10f, {"Forward track cut on track chi2"}}; + Configurable cutMaxChi2MatchMCHMFT{"cutMaxChi2MatchMCHMFT", 1e10f, {"Forward track cut on MCH-MFT matching chi2 (MFT tracks only)"}}; + } cutFwdTrack; + + struct : ConfigurableGroup { + Configurable preselUseMinMomentumOnBothTracks{"preselUseMinMomentumOnBothTracks", false, {"Both tracks are required to fill requirement on minimum momentum."}}; + Configurable preselMinTrackMomentum{"preselMinTrackMomentum", 0.1, {"Requirement on minimum momentum of the track."}}; + Configurable preselSystemPtCut{"preselSystemPtCut", 2.0, {"By default, cut on maximum system pT."}}; + Configurable preselUseOppositeSystemPtCut{"preselUseOppositeSystemPtCut", false, {"Negates the system pT cut (cut on minimum system pT)."}}; + Configurable preselMinInvariantMass{"preselMinInvariantMass", 2.0, {"Requirement on minimum system invariant mass."}}; + Configurable preselMaxInvariantMass{"preselMaxInvariantMass", 5.0, {"Requirement on maximum system invariant mass."}}; + } cutPreselect; + + using CandidatesFwd = soa::Join; + using CandidateFwd = CandidatesFwd::iterator; + using FwdTracks = soa::Join; + using FwdMcTracks = soa::Join; + + // init + void init(InitContext&) + { + if (verboseInfo) + printMediumMessage("INIT METHOD"); + + histos.add("Reco/hSelections", "Effect of selections;;Number of events (-)", HistType::kTH1D, {{NEvSels, -0.5, static_cast(NEvSels) - 0.5}}); + auto hSel = histos.get(HIST("Reco/hSelections")); + hSel->GetXaxis()->SetBinLabel(EvSelAll + 1, "All"); + hSel->GetXaxis()->SetBinLabel(EvSelFV0Veto + 1, "FV0A veto"); + hSel->GetXaxis()->SetBinLabel(EvSelNumContrib + 1, "N contrib."); + hSel->GetXaxis()->SetBinLabel(EvSelTwoTracks + 1, "2 sel. tracks"); + hSel->GetXaxis()->SetBinLabel(EvSelInvariantMass + 1, "Inv. mass"); + hSel->GetXaxis()->SetBinLabel(EvSelSystemPt + 1, "System pT"); + hSel->GetXaxis()->SetBinLabel(EvSelOneTrackMomentum + 1, "One trk p"); + hSel->GetXaxis()->SetBinLabel(EvSelBothTracksMomentum + 1, "Both trks p"); + + histos.add("Reco/hFwdTrackSelections", "Effect of forward track selections;;Number of tracks (-)", HistType::kTH1D, {{NTrkSels, -0.5, static_cast(NTrkSels) - 0.5}}); + auto hTrkSel = histos.get(HIST("Reco/hFwdTrackSelections")); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelAll + 1, "All"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelType + 1, "Track type"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPt + 1, "pT"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelEta + 1, "#eta"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelRabs + 1, "R_{abs}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPDca + 1, "pDCA"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2 + 1, "#chi^{2}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2MatchMCHMFT + 1, "#chi^{2}_{MCH-MFT}"); + + histos.add("Reco/hNanalyzedPerRun", "N analyzed events per run;Run number (-);Number of analyzed events (-)", HistType::kTH1D, {{1, 0., 1.}}); + histos.add("Reco/hNselectedPerRun", "N selected events per run;Run number (-);Number of selected events (-)", HistType::kTH1D, {{1, 0., 1.}}); + + histos.add("Truth/hTroubles", "Counter of unwanted issues;;Number of troubles (-)", HistType::kTH1D, {{NTroubles - 1, 0.5, static_cast(NTroubles) - 0.5}}); + auto hTroubles = histos.get(HIST("Truth/hTroubles")); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyMothers + 1, "> 2 mothers"); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyDaughters + 1, "> 2 charged daughters"); + hTroubles->GetXaxis()->SetBinLabel(TroubleNoTrack + 1, "Daughter without track"); + hTroubles->GetXaxis()->SetBinLabel(TroubleMoreTracks + 1, "Daughter in > 1 cand."); + hTroubles->GetXaxis()->SetBinLabel(TroubleDifferentCandidates + 1, "Daughters in diff. cand."); + } // end init + + bool isAcceptedTrackType(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + switch (trackType) { + case ForwardTrackTypeEnum::GlobalMuonTrack: + return cutFwdTrack.acceptGlobalMuonTrack; + case ForwardTrackTypeEnum::GlobalForwardTrack: + return cutFwdTrack.acceptGlobalForwardTrack; + case ForwardTrackTypeEnum::MuonStandaloneTrack: + return cutFwdTrack.acceptMuonStandaloneTrack; + case ForwardTrackTypeEnum::MCHStandaloneTrack: + return cutFwdTrack.acceptMCHStandaloneTrack; + default: + return false; + } + } + + static bool hasMFT(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + return trackType == ForwardTrackTypeEnum::GlobalMuonTrack || trackType == ForwardTrackTypeEnum::GlobalForwardTrack; + } + + template + static ROOT::Math::PxPyPzMVector muonVector(T const& track) + { + return ROOT::Math::PxPyPzMVector(track.px(), track.py(), track.pz(), MassMuon); + } + + template + bool isSelectedFwdTrack(T const& track, bool fillHistos = true) + { + auto countPassed = [&](FwdTrackSelection step) { + if (fillHistos) + histos.fill(HIST("Reco/hFwdTrackSelections"), step); + }; + countPassed(TrkSelAll); + + if (!isAcceptedTrackType(track.trackType())) + return false; + countPassed(TrkSelType); + + if (!cutFwdTrack.applyFwdTrackSelection) + return true; + + const auto vec = muonVector(track); + if (vec.Pt() < cutFwdTrack.cutMinPt || vec.Pt() > cutFwdTrack.cutMaxPt) + return false; + countPassed(TrkSelPt); + + if (vec.Eta() < cutFwdTrack.cutMinEta || vec.Eta() > cutFwdTrack.cutMaxEta) + return false; + countPassed(TrkSelEta); + + if (track.rAtAbsorberEnd() < cutFwdTrack.cutMinRabs || track.rAtAbsorberEnd() > cutFwdTrack.cutMaxRabs) + return false; + countPassed(TrkSelRabs); + + float maxPDca = track.rAtAbsorberEnd() < upchelpers::AbsorberMid ? cutFwdTrack.cutMaxPDcaLowRabs : cutFwdTrack.cutMaxPDcaHighRabs; + if (track.pDca() > maxPDca) + return false; + countPassed(TrkSelPDca); + + if (track.chi2() > cutFwdTrack.cutMaxChi2) + return false; + countPassed(TrkSelChi2); + + if (hasMFT(track.trackType()) && track.chi2MatchMCHMFT() > cutFwdTrack.cutMaxChi2MatchMCHMFT) + return false; + countPassed(TrkSelChi2MatchMCHMFT); + + return true; + } + + // largest FV0A amplitude within [-maxRelBc, +maxRelBc] around the candidate BC + template + static float getMaxFV0Amplitude(C const& cand, int maxRelBc) + { + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + float maxAmp = 0.f; + for (std::size_t i = 0; i < amps.size(); ++i) { + if (std::abs(relBCs[i]) <= maxRelBc) + maxAmp = std::max(maxAmp, amps[i]); + } + return maxAmp; + } + + static float finiteOrDefault(float value) + { + return std::isfinite(value) ? value : DefaultFloat; + } + + template + static void fillEventInfo(RecoInfo& info, C const& cand) + { + info.runNumber = cand.runNumber(); + info.bc = cand.globalBC(); + info.numContrib = cand.numContrib(); + info.posX = cand.posX(); + info.posY = cand.posY(); + info.posZ = cand.posZ(); + info.totalFV0AmplitudeA = getMaxFV0Amplitude(cand, 0); + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + info.amplitudesV0A.assign(amps.begin(), amps.end()); + info.ampRelBCsV0A.assign(relBCs.begin(), relBCs.end()); + } + + // the ZDC table is filled by the producer only for candidates with ZDC activity, i.e. it holds at most one row per candidate + static void fillZdcInfo(RecoInfo& info, aod::UDZdcsReduced const& zdcs) + { + for (const auto& zdc : zdcs) { + info.energyCommonZNA = finiteOrDefault(zdc.energyCommonZNA()); + info.energyCommonZNC = finiteOrDefault(zdc.energyCommonZNC()); + info.timeZNA = finiteOrDefault(zdc.timeZNA()); + info.timeZNC = finiteOrDefault(zdc.timeZNC()); + } + } + + template + static void fillTrackInfo(RecoInfo& info, int iTrk, T const& track) + { + info.px[iTrk] = track.px(); + info.py[iTrk] = track.py(); + info.pz[iTrk] = track.pz(); + info.sign[iTrk] = track.sign(); + info.time[iTrk] = track.trackTime(); + info.timeRes[iTrk] = track.trackTimeRes(); + info.type[iTrk] = track.trackType(); + info.nClusters[iTrk] = track.nClusters(); + info.pDca[iTrk] = track.pDca(); + info.rAbs[iTrk] = track.rAtAbsorberEnd(); + info.chi2[iTrk] = track.chi2(); + info.chi2MatchMCHMID[iTrk] = track.chi2MatchMCHMID(); + info.chi2MatchMCHMFT[iTrk] = track.chi2MatchMCHMFT(); + info.mchBitMap[iTrk] = track.mchBitMap(); + info.midBitMap[iTrk] = track.midBitMap(); + info.midBoards[iTrk] = track.midBoards(); + } + + // writes reco-level columns, followed by optional truth-level columns of the simulated table + template + static void writeRow(TCursor& cursor, RecoInfo& info, TTruth&&... truth) + { + cursor(info.runNumber, info.bc, info.numContrib, info.posX, info.posY, info.posZ, + info.totalFV0AmplitudeA, info.amplitudesV0A, info.ampRelBCsV0A, + info.energyCommonZNA, info.energyCommonZNC, info.timeZNA, info.timeZNC, + info.px, info.py, info.pz, info.sign, info.time, info.timeRes, + info.type, info.nClusters, info.pDca, info.rAbs, info.chi2, info.chi2MatchMCHMID, info.chi2MatchMCHMFT, + info.mchBitMap, info.midBitMap, info.midBoards[0], info.midBoards[1], + std::forward(truth)...); + } + + static int getTrueChannel(const int pdgCodes[NumTracks]) + { + const int part1 = enumMyParticle(pdgCodes[0]); + const int part2 = enumMyParticle(pdgCodes[1]); + if (part1 == P_ELECTRON && part2 == P_ELECTRON) + return CH_EE; + if (part1 == P_MUON && part2 == P_MUON) + return CH_MUMU; + if ((part1 == P_ELECTRON && (part2 == P_PION || part2 == P_MUON)) || (part2 == P_ELECTRON && (part1 == P_PION || part1 == P_MUON))) + return CH_EMUPI; + return -1; + } + + void processDataFwd(CandidateFwd const& cand, + FwdTracks const& tracks, + aod::UDZdcsReduced const& zdcs) + { + const char* srun = Form("%d", cand.runNumber()); + histos.get(HIST("Reco/hNanalyzedPerRun"))->Fill(srun, 1); + + histos.fill(HIST("Reco/hSelections"), EvSelAll); + + if (cutSample.useFV0Veto && getMaxFV0Amplitude(cand, cutSample.cutFV0RelBcRange) > cutSample.cutMaxFV0Amp) + return; + histos.fill(HIST("Reco/hSelections"), EvSelFV0Veto); + + if (cutSample.useNumContrib && cand.numContrib() != cutSample.cutNumContrib) + return; + histos.fill(HIST("Reco/hSelections"), EvSelNumContrib); + + RecoInfo info; + std::array daug; + int nSelected = 0; + for (const auto& track : tracks) { + if (!isSelectedFwdTrack(track)) + continue; + if (nSelected < NumTracks) { + fillTrackInfo(info, nSelected, track); + daug[nSelected] = muonVector(track); + } + nSelected++; + } + + // Critical selection, without it the rest of the process function will fail + if (nSelected != NumTracks) + return; + histos.fill(HIST("Reco/hSelections"), EvSelTwoTracks); + + const auto mother = daug[0] + daug[1]; + + // Apply system selections + // invariant mass + if (mother.M() < cutPreselect.preselMinInvariantMass || mother.M() > cutPreselect.preselMaxInvariantMass) + return; + histos.fill(HIST("Reco/hSelections"), EvSelInvariantMass); + + // system pt + if (cutPreselect.preselUseOppositeSystemPtCut ? mother.Pt() < cutPreselect.preselSystemPtCut : mother.Pt() > cutPreselect.preselSystemPtCut) + return; + histos.fill(HIST("Reco/hSelections"), EvSelSystemPt); + + // one track momentum + if (daug[0].P() < cutPreselect.preselMinTrackMomentum && daug[1].P() < cutPreselect.preselMinTrackMomentum) + return; + histos.fill(HIST("Reco/hSelections"), EvSelOneTrackMomentum); + + // both tracks momentum + if (cutPreselect.preselUseMinMomentumOnBothTracks && (daug[0].P() < cutPreselect.preselMinTrackMomentum || daug[1].P() < cutPreselect.preselMinTrackMomentum)) + return; + histos.fill(HIST("Reco/hSelections"), EvSelBothTracksMomentum); + + histos.get(HIST("Reco/hNselectedPerRun"))->Fill(srun, 1); + + fillEventInfo(info, cand); + fillZdcInfo(info, zdcs); + writeRow(twoFwdTracks, info); + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processDataFwd, "Iterate UD tables with measured data created by upcCandProducerGlobalMuon.", true); + + PresliceUnsorted partPerMcCollision = aod::udmcparticle::udMcCollisionId; + PresliceUnsorted trackPerMcParticle = aod::udmcfwdtracklabel::udMcParticleId; + + void processMonteCarlo(aod::UDMcCollisions const& mcCollisions, + aod::UDMcParticles const& parts, + CandidatesFwd const& candidates, + FwdMcTracks const& tracks) + { + // start loop over generated collisions + for (const auto& mcColl : mcCollisions) { + RecoInfo info; + TruthInfo truth; + std::vector daughterIds; + + // store a charged particle as truth daughter; returns false if there are already too many of them + auto addTrueDaughter = [&](auto const& daughter) { + // check if it is the charged particle (= no pi0, photon or neutrino) + if (enumMyParticle(daughter.pdgCode()) == -1) + return true; + // check we do not have more than 2 charged daughters in total + if (static_cast(daughterIds.size()) >= NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 total charged daughters. Breaking the daughter loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyDaughters); + truth.problem = true; + return false; + } + const auto iDaug = daughterIds.size(); + truth.daugPx[iDaug] = daughter.px(); + truth.daugPy[iDaug] = daughter.py(); + truth.daugPz[iDaug] = daughter.pz(); + truth.daugPdgCode[iDaug] = daughter.pdgCode(); + daughterIds.push_back(daughter.globalIndex()); + return true; + }; + + // get particles associated to generated collision + const auto& partsFromMcColl = parts.sliceBy(partPerMcCollision, mcColl.globalIndex()); + int countMothers = 0; + for (const auto& particle : partsFromMcColl) { + // select only mothers with checking if particle has no mother + if (particle.has_mothers()) + continue; + countMothers++; + // check the generated collision does not have more than 2 mothers + if (countMothers > NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 no mother particles. Breaking the particle loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyMothers); + truth.problem = true; + break; + } + // fill info for each mother + truth.motherPx[countMothers - 1] = particle.px(); + truth.motherPy[countMothers - 1] = particle.py(); + truth.motherPz[countMothers - 1] = particle.pz(); + + if (particle.has_daughters()) { + for (const auto& daughter : particle.daughters_as()) { + if (!addTrueDaughter(daughter)) + break; + } + } else { + // motherless final-state particle, e.g. muon from gamma gamma -> mu mu, is its own daughter + addTrueDaughter(particle); + } + if (truth.problem) + break; + } // particles + + // get the reconstructed tracks of each daughter (how well the daughter was reconstructed). + // One muon can be stored in several candidates (as MCH-MID-MFT track in an anchor candidate and as MCH-MID track + // in an MCH-MID candidate), so collect per daughter the selected track in each candidate, preferring the lowest track type. + std::array, NumTracks> trackIdPerCand; // candidate id -> track id, per daughter + int nRecoDaughters = 0; + for (std::size_t iDaug = 0; iDaug < daughterIds.size(); ++iDaug) { + auto& candTracks = trackIdPerCand[iDaug]; + for (const auto& trk : tracks.sliceBy(trackPerMcParticle, daughterIds[iDaug])) { + if (!isSelectedFwdTrack(trk, false)) + continue; + auto it = candTracks.find(trk.udCollisionId()); + if (it == candTracks.end() || trk.trackType() < tracks.iteratorAt(it->second).trackType()) + candTracks[trk.udCollisionId()] = trk.globalIndex(); + } + if (candTracks.empty()) { + if (verboseInfo) + printLargeMessage("Daughter has no associated track. Skipping this daughter."); + histos.fill(HIST("Truth/hTroubles"), TroubleNoTrack); + truth.problem = true; + continue; + } + if (candTracks.size() > 1) + histos.fill(HIST("Truth/hTroubles"), TroubleMoreTracks); + nRecoDaughters++; + } // daughters + + // choose the candidate holding most daughters; on a tie the one with the lowest summed track type (i.e. with MFT) + std::map> candScore; // candidate id -> (number of daughters, summed track type) + for (const auto& candTracks : trackIdPerCand) { + for (const auto& [cand, trkId] : candTracks) { + candScore[cand].first++; + candScore[cand].second += tracks.iteratorAt(trkId).trackType(); + } + } + int32_t candId = -1; + std::pair bestScore{0, 0}; + for (const auto& [cand, score] : candScore) { + if (candId < 0 || score.first > bestScore.first || (score.first == bestScore.first && score.second < bestScore.second)) { + candId = cand; + bestScore = score; + } + } + + // fill info for reconstructed collision and tracks of the chosen candidate + if (candId >= 0) { + if (bestScore.first < nRecoDaughters) { + if (verboseInfo) + printLargeMessage("Daughters are reconstructed in different candidates."); + histos.fill(HIST("Truth/hTroubles"), TroubleDifferentCandidates); + truth.problem = true; + } + truth.hasRecoColl = true; + fillEventInfo(info, candidates.iteratorAt(candId)); + for (int iDaug = 0; iDaug < NumTracks; ++iDaug) { + auto it = trackIdPerCand[iDaug].find(candId); + if (it != trackIdPerCand[iDaug].end()) + fillTrackInfo(info, iDaug, tracks.iteratorAt(it->second)); + } + } + + truth.channel = getTrueChannel(truth.daugPdgCode); + + writeRow(trueTwoFwdTracks, info, // no ZDC info in MC + truth.channel, truth.hasRecoColl, mcColl.posX(), mcColl.posY(), mcColl.posZ(), + truth.motherPx, truth.motherPy, truth.motherPz, truth.daugPx, truth.daugPy, truth.daugPz, truth.daugPdgCode, truth.problem); + } // mccollisions + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by upcCandProducerGlobalMuon.", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From 5d0755d5a8bfebf8b8f88ed591c50110d1074443 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Fri, 2 Oct 2026 15:17:06 +0200 Subject: [PATCH 150/648] [ALICE3] Add option to modify the decay table in the otf decayer (#18150) --- ALICE3/Core/Decayer.h | 106 ++++++++++++++++--- ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx | 7 ++ 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/ALICE3/Core/Decayer.h b/ALICE3/Core/Decayer.h index 8f88e4e311b..fb39a372cb2 100644 --- a/ALICE3/Core/Decayer.h +++ b/ALICE3/Core/Decayer.h @@ -19,10 +19,13 @@ #ifndef ALICE3_CORE_DECAYER_H_ #define ALICE3_CORE_DECAYER_H_ +#include "ALICE3/Core/ConfigurationParser.h" #include "ALICE3/Core/OTFParticle.h" #include "ALICE3/Core/TrackUtilities.h" +#include #include +#include #include #include @@ -34,6 +37,10 @@ #include #include #include +#include +#include +#include +#include #include namespace o2::upgrade @@ -45,6 +52,53 @@ class Decayer // Default constructor Decayer() = default; + static constexpr double BranchingRatioTolerance = 0.01; + + struct DecayChannel { + double branchingRatio{}; + std::vector daughters; + }; + + template + void loadDecayTable(const std::string& path, const TDatabase& pdgDB, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + { + std::string fileName = o2::fastsim::ConfigurationParser::accessFile(path, "./.ALICE3/Configuration/", ccdb); + std::vector mothers; + const std::map> config = o2::fastsim::ConfigurationParser::parseTEnvConfiguration(fileName, mothers); + + mDecayTable.clear(); + for (const auto& [motherName, channels] : config) { + const int motherPdg = std::stoi(motherName); + if (!pdgDB->GetParticle(motherPdg)) { + LOG(fatal) << "Decay table: unknown mother PDG code " << motherPdg; + } + for (const auto& [channelName, value] : channels) { + DecayChannel channel; + std::istringstream stream(value); + stream >> channel.branchingRatio; + int daughterPdg{}; + while (stream >> daughterPdg) { + if (!pdgDB->GetParticle(daughterPdg)) { + LOG(fatal) << "Decay table: unknown daughter PDG code " << daughterPdg << " in " << motherName << "." << channelName; + } + channel.daughters.push_back(daughterPdg); + } + if (!stream.eof() || channel.branchingRatio <= 0. || channel.daughters.size() < 2) { + LOG(fatal) << "Decay table: invalid channel " << motherName << "." << channelName << ": \"" << value << "\""; + } + mDecayTable[motherPdg].push_back(channel); + } + double brTotal = 0.; + for (const auto& channel : mDecayTable[motherPdg]) { + brTotal += channel.branchingRatio; + } + if (std::abs(brTotal - 1.) > BranchingRatioTolerance) { + LOG(fatal) << "Decay table: branching ratios of PDG " << motherPdg << " sum to " << brTotal << ", expected 1 within " << BranchingRatioTolerance; + } + LOG(info) << "Decay table: overriding " << mDecayTable[motherPdg].size() << " decay channel(s) of PDG " << motherPdg; + } + } + template std::vector decayParticle(const OTFParticle& particle, const TDatabase& pdgDB) { @@ -69,21 +123,22 @@ class Decayer py = particle.py() * std::cos(mTheta) + particle.px() * std::sin(mTheta); } - double brTotal = 0.; e = std::sqrt(mass * mass + px * px + py * py + particle.pz() * particle.pz()); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brTotal += particleInfo->DecayChannel(ch)->BranchingRatio(); + const std::vector channels = getDecayChannels(particle.pdgCode(), particleInfo); + + double brTotal = 0.; + for (const auto& channel : channels) { + brTotal += channel.branchingRatio; } double brSum = 0.; std::vector dauMasses; std::vector pdgCodesDaughters; const double randomChannel = mRand3.Uniform(0., brTotal); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brSum += particleInfo->DecayChannel(ch)->BranchingRatio(); + for (const auto& channel : channels) { + brSum += channel.branchingRatio; if (randomChannel < brSum) { - for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { - const int pdgDau = particleInfo->DecayChannel(ch)->DaughterPdgCode(dau); + for (const int pdgDau : channel.daughters) { pdgCodesDaughters.push_back(pdgDau); const auto& dauInfo = pdgDB->GetParticle(pdgDau); dauMasses.push_back(dauInfo->Mass()); @@ -102,16 +157,16 @@ class Decayer decay.Generate(); std::vector decayProducts; + decayProducts.reserve(dauMasses.size()); for (size_t i = 0; i < dauMasses.size(); ++i) { - o2::upgrade::OTFParticle particle; - TLorentzVector dau = *decay.GetDecay(i); - particle.setPDG(pdgCodesDaughters[i]); - particle.setVxVyVz(mVx, mVy, mVz); - particle.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); - particle.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); - decayProducts.push_back(particle); + o2::upgrade::OTFParticle daughter; + const TLorentzVector& dau = *decay.GetDecay(i); + daughter.setPDG(pdgCodesDaughters[i]); + daughter.setVxVyVz(mVx, mVy, mVz); + daughter.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); + daughter.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); + decayProducts.push_back(daughter); } - return decayProducts; } @@ -166,6 +221,27 @@ class Decayer [[nodiscard]] float getDecayRadius() const { return static_cast(std::hypot(mVx, mVy)); } private: + /// Decay channels from the loaded decay table if the mother is listed there, otherwise from TDatabasePDG + template + std::vector getDecayChannels(const int pdgCode, const TParticleInfo& particleInfo) const + { + if (const auto it = mDecayTable.find(pdgCode); it != mDecayTable.end()) { + return it->second; + } + + std::vector channels; + for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { + DecayChannel channel; + channel.branchingRatio = particleInfo->DecayChannel(ch)->BranchingRatio(); + for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { + channel.daughters.push_back(particleInfo->DecayChannel(ch)->DaughterPdgCode(dau)); + } + channels.push_back(channel); + } + return channels; + } + + std::unordered_map> mDecayTable; double mBz{20.}; // kG double mVx{-1.}, mVy{-1.}, mVz{-1.}; double mTheta{}; diff --git a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx index 2c8c4dea0c9..c6f587fdd3c 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx @@ -20,6 +20,7 @@ #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/tracksAlice3.h" +#include #include #include #include @@ -92,10 +93,12 @@ struct OnTheFlyDecayer { o2::upgrade::Decayer decayer; Service pdgDB{}; + Service ccdb{}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Configurable seed{"seed", 0, "Set seed for particle decayer"}; Configurable magneticField{"magneticField", 20., "Magnetic field (kG)"}; + Configurable decayTable{"decayTable", "", "Decay table overriding the TDatabasePDG decay channels of the listed particles (local path or ccdb:), empty to disable"}; Configurable> enabledDecays{"enabledDecays", {DefaultParameters[0].data(), NumDecays, NumParameters, particleNames, parameterNames}, "Enable option for particle to be decayed: 0 - no, 1 - yes"}; @@ -111,6 +114,10 @@ struct OnTheFlyDecayer { LOG(info) << " --- Using magnetic field: " << magneticField; decayer.setSeed(seed); decayer.setBField(magneticField); + if (!decayTable.value.empty()) { + LOG(info) << " --- Using decay table: " << decayTable.value; + decayer.loadDecayTable(decayTable.value, pdgDB, ccdb.operator->()); + } for (int i = 0; i < NumDecays; ++i) { if (enabledDecays->get(particleNames[i].c_str(), "enable") != 0) { LOG(info) << " --- Decay enabled: " << pdgCodes[i]; From 80f67fd5f1868a567a8e8d93d8e5ea23ca7f2580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Fri, 2 Oct 2026 15:22:16 +0200 Subject: [PATCH 151/648] [PWGHF] Add TPC cluster count for deuteron prong in the cd task (#18146) Co-authored-by: ALICE Action Bot --- PWGHF/D2H/Tasks/taskCd.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskCd.cxx b/PWGHF/D2H/Tasks/taskCd.cxx index 8282a7eecf5..6e35eb255c3 100644 --- a/PWGHF/D2H/Tasks/taskCd.cxx +++ b/PWGHF/D2H/Tasks/taskCd.cxx @@ -109,6 +109,7 @@ DECLARE_SOA_COLUMN(NSigmaTofPi, nSigmaTofPi, float); //! TOF nσ DECLARE_SOA_COLUMN(NItsClusters, nItsClusters, float); //! Number of ITS clusters used in the track fit DECLARE_SOA_COLUMN(NItsNClusterSize, nItsNClusterSize, float); //! Number of ITS clusters size used in the track fit DECLARE_SOA_COLUMN(NTpcClusters, nTpcClusters, float); //! Number of TPC clusters used in the track fit +DECLARE_SOA_COLUMN(TpcNClsFoundDe, tpcNClsFoundDe, uint16_t); //! Number of found TPC clusters for the selected deuteron prong DECLARE_SOA_COLUMN(NTpcSignalsDe, nTpcSignalsDe, float); //! Number of TPC signas for deuteron DECLARE_SOA_COLUMN(NTpcSignalsPi, nTpcSignalsPi, float); //! Number of TPC signas for pion DECLARE_SOA_COLUMN(NTpcSignalsKa, nTpcSignalsKa, float); //! Number of TPC signas for kaon @@ -210,7 +211,8 @@ DECLARE_SOA_TABLE(HfCandCdFull, "AOD", "HFCANDCDFULL", full::Cent, full::VtxZ, full::GIndexCol, - full::TimeStamp); + full::TimeStamp, + full::TpcNClsFoundDe); DECLARE_SOA_TABLE(HfCandCdGen, "AOD", "HFCANDCDGEN", full::Pt, @@ -624,7 +626,7 @@ struct HfTaskCd { candidate.ct(o2::constants::physics::MassCDeuteron) * CmToMum, candFlag, candSign, candidate.flagMcMatchRec(), isCandidateSwapped, hypothesisMask, candidateGlobalIndex, candidate.originMcRec(), candidate.flagMcDecayChanRec(), ctGen, collision.numContrib(), o2::hf_centrality::getCentralityColl(collision), - collision.posZ(), collision.globalIndex(), timeStamp); + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); } }; @@ -905,7 +907,7 @@ struct HfTaskCd { nSigmaTpcPi, nSigmaTofPi, nSigmaTpcKa, nSigmaTofKa, candidate.ct(o2::constants::physics::MassCDeuteron), candFlag, candSign, 0, -1, hypothesisMask, candidateGlobalIndex, 0, 0, -1.f, collision.numContrib(), cent, - collision.posZ(), collision.globalIndex(), timeStamp); + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); } }; From 69700d698e5ca448a615c4637ac0d97b72af22ae Mon Sep 17 00:00:00 2001 From: mj525 <99000728+mjkim525@users.noreply.github.com> Date: Fri, 2 Oct 2026 23:22:30 +0900 Subject: [PATCH 152/648] [PWGLF] Update `resonanceModuleInitializer` for `mcCentrality` compatibility (#18151) --- .../Resonances/resonanceModuleInitializer.cxx | 93 ++++++++++++++++--- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx index 8a07e7365b3..57640b2d3fc 100644 --- a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx @@ -273,9 +273,25 @@ struct ResonanceModuleInitializer { } LOGF(info, "Stored collision multiplicity estimator: %d", EventConfig.cfgMultiplicityEstimator.value); + const int enabledGenProcesses = static_cast(doprocessMCgen) + + static_cast(doprocessMCgenFT0M) + + static_cast(doprocessMCgenFT0C) + + static_cast(doprocessMCgenFV0A) + + static_cast(doprocessMCgenNoCent); + const bool anyGenProcess = enabledGenProcesses > 0; + if (enabledGenProcesses > 1) { + LOGF(fatal, "Enable only one generator process: processMCgen, processMCgenFT0M, processMCgenFT0C, processMCgenFV0A, or processMCgenNoCent"); + } + if (doprocessMCgenNoCent && GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "processMCgenNoCent requires cfgGenMultPercentile=false"); + } + if ((doprocessMCgenFT0M || doprocessMCgenFT0C || doprocessMCgenFV0A) && !GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "The estimator-specific generator processes require cfgGenMultPercentile=true; use processMCgenNoCent otherwise"); + } + // Run 2 and Run 3 callbacks require different event-selection semantics. const bool anyRun2Process = doprocessRun2 || doprocessRun2MC; - const bool anyRun3Process = doprocessRun3 || doprocessRun3MC || doprocessMCgen; + const bool anyRun3Process = doprocessRun3 || doprocessRun3MC || anyGenProcess; if (anyRun2Process && anyRun3Process) { LOG(fatal) << "Run 2 and Run 3 processes cannot be enabled in the same ResonanceModuleInitializer"; } @@ -290,13 +306,13 @@ struct ResonanceModuleInitializer { static_cast(GenCuts.cfgGenMultFT0M.value) + static_cast(GenCuts.cfgGenMultFT0C.value) + static_cast(GenCuts.cfgGenMultFV0A.value); - if ((doprocessMCgen || doprocessRun2MC || doprocessRun3MC) && enabledGenMultiplicityEstimators > 1) { + if ((anyGenProcess || doprocessRun2MC || doprocessRun3MC) && enabledGenMultiplicityEstimators > 1) { LOG(fatal) << "Only one generator multiplicity estimator can be enabled: cfgGenMult05, cfgGenMult10, cfgGenMultFT0M, cfgGenMultFT0C, or cfgGenMultFV0A"; } - if (doprocessMCgen) { - if (GenCuts.cfgGenMultPercentile && multEstimator != CentralityFT0M && + if (anyGenProcess) { + if (doprocessMCgen && GenCuts.cfgGenMultPercentile && multEstimator != CentralityFT0M && multEstimator != CentralityFT0C && multEstimator != CentralityFV0A) { - LOGF(fatal, "cfgGenMultPercentile supports cfgMultName=FT0M, FT0C, or FV0A"); + LOGF(fatal, "processMCgen with cfgGenMultPercentile supports cfgMultName=FT0M, FT0C, or FV0A"); } if (GenCuts.isZvtxcutGen && (!std::isfinite(GenCuts.cutzvertexGen.value) || GenCuts.cutzvertexGen.value <= 0.f)) { @@ -362,7 +378,7 @@ struct ResonanceModuleInitializer { ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } - if (doprocessMCgen) { + if (anyGenProcess) { constexpr std::array MCEventLabels{"All", "z vertex", "BC RCT", "INEL", "INEL>0"}; AxisSpec centAxisGen = {GenCuts.binsCentGen, "Centrality (%)"}; AxisSpec eventTypeAxis = {2, 0.f, 2.f, "Event type"}; @@ -533,7 +549,14 @@ struct ResonanceModuleInitializer { fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kAllpassed); } + // Keep the original input for existing processMCgen configurations. The + // dedicated callbacks select the MC percentile independently of cfgMultName + // (reconstructed centrality) and cfgGenMult* (generator multiplicity). using GenMCCollisions = soa::Join; + using GenMCCollisionsFT0M = soa::Join; + using GenMCCollisionsFT0C = soa::Join; + using GenMCCollisionsFV0A = soa::Join; + using GenMCCollisionsNoCent = soa::Join; using Run3MCCollisions = soa::Join; using Run2MCCollisions = soa::Join; using GenRecoCollisions = soa::Join; @@ -846,19 +869,19 @@ struct ResonanceModuleInitializer { * reduced AOD tables. RCT quality is evaluated through the generator * collision's associated BC because it is a run-condition property. */ - void processMCgen(GenMCCollisions::iterator const& mcCollision, - aod::McParticles const& mcParticles, - soa::SmallGroups const& collisions, - BCsWithRCT const&) + template + void processMCgenQA(MCCollision const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + float generatorCentrality) { - auto bc = mcCollision.bc_as(); + auto bc = mcCollision.template bc_as(); initCCDB(bc); const auto getReconstructedCentrality = [&](auto const& collision) { return centEst(collision); }; - const float generatorCentrality = getMCCentrality(mcCollision); const float impactParameter = mcCollision.impactParameter(); const float multiplicity = getMCMultiplicity(mcCollision); @@ -910,7 +933,51 @@ struct ResonanceModuleInitializer { qaRegistry.fill(HIST("EventGen/h4MultCent_recMC"), eventType, reconstructedCentrality, multiplicity, impactParameter); } } - PROCESS_SWITCH(ResonanceModuleInitializer, processMCgen, "Process generator-level MC QA", false); + + void processMCgen(GenMCCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, getMCCentrality(mcCollision)); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgen, "Process generator-level MC QA with all MC percentile inputs (legacy)", false); + + void processMCgenFT0M(GenMCCollisionsFT0M::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0M()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0M, "Process generator-level MC QA with only the FT0M MC percentile input", false); + + void processMCgenFT0C(GenMCCollisionsFT0C::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0C()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0C, "Process generator-level MC QA with only the FT0C MC percentile input", false); + + void processMCgenFV0A(GenMCCollisionsFV0A::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFV0A()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFV0A, "Process generator-level MC QA with only the FV0A MC percentile input", false); + + void processMCgenNoCent(GenMCCollisionsNoCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, 100.5f); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenNoCent, "Process generator-level MC QA using reconstructed centrality without MC percentile inputs", false); /** * @brief Processes Run3 MC data From df9fccb1e1d36b8ab37df02c4cf4fae6620540a2 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:16:49 +0200 Subject: [PATCH 153/648] [ALICE3] Add example configuration for modifying the otf decay table (#18154) --- .../Macros/Configuration/decayTable_example.ini | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 ALICE3/Macros/Configuration/decayTable_example.ini diff --git a/ALICE3/Macros/Configuration/decayTable_example.ini b/ALICE3/Macros/Configuration/decayTable_example.ini new file mode 100644 index 00000000000..777defd3570 --- /dev/null +++ b/ALICE3/Macros/Configuration/decayTable_example.ini @@ -0,0 +1,16 @@ +# Example decay table for the on-the-fly decayer (configurable "decayTable") +# Overrides the TDatabasePDG decay channels of the listed mothers; unlisted particles are unaffected. +# Format: .: ... +# Branching ratios of each mother should sum to 1 (within 0.01); small deviations are renormalised. + +# Lambda -> p pi- only +3122.ch0: 0.639 2212 -211 +-3122.ch0: 0.639 -2112 111 + +# Lambda -> n pi0 only +3122.ch1: 0.361 2112 111 +-3122.ch1: 0.361 -2112 111 + +# Xi- -> Lambda pi- +3312.ch0: 1.0 3122 -211 +-3312.ch0: 1.0 -3122 211 From 9f8d90f673fdad23198ef2286648361b25a9b341 Mon Sep 17 00:00:00 2001 From: nkaratze Date: Fri, 2 Oct 2026 17:35:38 +0100 Subject: [PATCH 154/648] [PWGLF] Added and upgraded derived data processes and mc closure process (#18147) --- PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx | 499 ++++++++++++++----- 1 file changed, 365 insertions(+), 134 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx index e8f028b5f3c..fb66f7e07e8 100644 --- a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx +++ b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx @@ -104,57 +104,75 @@ struct V0PtInvMassPlots { Configurable itsMinHits{"itsMinHits", 1.0, "Minimum Hits of Daughter Tracks in the ITS"}; // Configurables switches for event selection - Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; - Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; - Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; - Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; - Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; - Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; - Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; - Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; - Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + struct EventSelectionConfig : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; + Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; + Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; + Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; + Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; + Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; + Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; + Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; + Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + Configurable doZvertexGenCut{"doZvertexGenCut", false, "Enable Generated Z vertex cut"}; + }; // Configurables switches for v0 selection - Configurable doRapidityCut{"doRapidityCut", true, "Enable rapidity v0 selection"}; - Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; - Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; - Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + struct V0SelectionConfig : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; + Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; + Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + }; // Configurables switches for K0sh selection - Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; - Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; - Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; - Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; - Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; - Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; - Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; - Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; - Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; - Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + struct K0shSelectionConfig : ConfigurableGroup { + std::string prefix = "k0shSelections"; // JSON group name + Configurable doK0shRapidityCut{"doK0shRapidityCut", true, "Enable rapidity K0sh selection"}; + Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; + Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; + Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; + Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; + Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; + Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; + Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; + Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; + Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; + Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for Lambda selection - Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; - Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; - Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; - Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; - Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; - Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; - Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; - Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; - Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; - Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + struct LambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable doLambdaRapidityCut{"doLambdaRapidityCut", true, "Enable rapidity Lambda selection"}; + Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; + Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; + Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; + Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; + Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; + Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; + Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; + Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; + Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; + Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for AntiLambda selection - Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; - Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; - Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; - Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; - Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; - Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; - Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; - Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; - Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; - Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + struct AntiLambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "antilambdaSelections"; // JSON group name + Configurable doAntiLambdaRapidityCut{"doAntiLambdaRapidityCut", true, "Enable rapidity AntiLambda selection"}; + Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; + Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; + Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; + Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; + Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; + Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; + Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; + Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; + Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; + Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + }; // Configurable K0sh Cuts (best cuts determined by v0topologicalcuts task) Configurable k0shSettingdcav0dau{"k0shSettingdcav0dau", 0.3, "DCA V0 Daughters"}; @@ -346,9 +364,6 @@ struct V0PtInvMassPlots { rMCCorrections.add("hK0shBeforeEventSelectionPtSpectrum", "hK0shBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); // not filled rMCCorrections.add("hLambdaBeforeEventSelectionPtSpectrum", "hLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hAntiLambdaBeforeEventSelectionPtSpectrum", "hAntiLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); - rMCCorrections.add("hK0shAfterEventSelectionPtSpectrum", "hK0shAfterEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); - rMCCorrections.add("hLambdaAfterEventSelectionPtSpectrum", "hLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); - rMCCorrections.add("hAntiLambdaAfterEventSelectionPtSpectrum", "hAntiLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); // Event and V0s Corrections rMCCorrections.add("hNEvents_Corrections", "hNEvents_Corrections", {HistType::kTH2D, {{10, 0.f, 10.f}, centAxis}}); @@ -356,8 +371,11 @@ struct V0PtInvMassPlots { // Generated Level Pt Spectrums (with rapidity cut) rMCCorrections.add("GenParticleRapidity", "GenParticleRapidity", {HistType::kTH1F, {{nBins, -10.0f, 10.0f}}}); rMCCorrections.add("hK0shGeneratedPtSpectrum", "hK0shGeneratedPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); + rMCCorrections.add("hK0shGeneratedPtSpectrumMult", "hK0shGeneratedPtSpectrumMult", {HistType::kTH2D, {k0ShortPtAxis, nchAxis}}); rMCCorrections.add("hLambdaGeneratedPtSpectrum", "hLambdaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hLambdaGeneratedPtSpectrumMult", "hLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {lambdaPtAxis, nchAxis}}); rMCCorrections.add("hAntiLambdaGeneratedPtSpectrum", "hAntiLambdaGeneratedPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiLambdaGeneratedPtSpectrumMult", "hAntiLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {antilambdaPtAxis, nchAxis}}); rMCCorrections.add("hXiMinusGeneratedPtSpectrum", "hXiMinusGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hXiZeroGeneratedPtSpectrum", "hXiZeroGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hOmegaGeneratedPtSpectrum", "hOmegaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); @@ -383,42 +401,50 @@ struct V0PtInvMassPlots { rNchAnalysis.add("hNchCentralityGenerated", "hNchCentralityGenerated", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated rNchAnalysis.add("hNchCentralityGeneratedAfterEventSelection", "hNchCentralityGeneratedAfterEventSelection", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated After Event Selection rNchAnalysis.add("hNchCentrality", "hNchCentrality", {HistType::kTH2D, {centAxis, nchAxis}}); - rNchAnalysis.add("hNchCentralityEtaHalf", "hNchCentralityEtaHalf", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality EtaHalfCut + rNchAnalysis.add("hNchCentralityEtaHalf", "hNchCentralityEtaHalf", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality EtaHalfCut + rNchAnalysis.add("hNchCentralityEtaHalfGen", "hNchCentralityEtaHalfGen", {HistType::kTH2D, {nchAxis, centAxis}}); // Nch vs Centrality EtaHalfCut } + // Grouped Configurables + EventSelectionConfig eventSelection; + V0SelectionConfig v0Selection; + K0shSelectionConfig k0shSelection; + LambdaSelectionConfig lambdaSelection; + AntiLambdaSelectionConfig antilambdaSelection; + // Event selection function template bool acceptEvent(TCollision const& collision) { rPtAnalysis.fill(HIST("hNEvents"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "All"); - if (dosel8 && !collision.sel8()) { + if (eventSelection.dosel8 && !collision.sel8()) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "sel 8"); - if (doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + if (eventSelection.doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(3, "NoTimeFrameBorder"); - if (doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + if (eventSelection.doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(4, "NoITSROFrameBorder"); - if (doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + if (eventSelection.doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(5, "IsTriggerTVX"); - if (docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { + if (eventSelection.docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(6, "cutZVertex"); - if (doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { - // if (doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS + if (eventSelection.doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { + // if (eventSelection.doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS return false; } rPtAnalysis.fill(HIST("hNEvents"), 6.5, collision.centFT0M()); @@ -476,17 +502,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -506,61 +532,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 0.5, v0.mK0Short()); - if (doRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut + if (k0shSelection.doK0shRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 1.5, v0.mK0Short()); - if (doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions + if (k0shSelection.doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions return false; } rPtAnalysis.fill(HIST("hNK0sh"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 2.5, v0.mK0Short()); - if (doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) + if (k0shSelection.doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) return false; } rPtAnalysis.fill(HIST("hNK0sh"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 3.5, v0.mK0Short()); - if (doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct + if (k0shSelection.doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct return false; } rPtAnalysis.fill(HIST("hNK0sh"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 4.5, v0.mK0Short()); - if (doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut + if (k0shSelection.doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 5.5, v0.mK0Short()); - if (doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut + if (k0shSelection.doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 6.5, v0.mK0Short()); - if (doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut + if (k0shSelection.doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 7.5, v0.mK0Short()); - if (doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut + if (k0shSelection.doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 8.5, v0.mK0Short()); - if (doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut + if (k0shSelection.doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 9.5, v0.mK0Short()); - if (doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut + if (k0shSelection.doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 10.5, collision.centFT0M()); @@ -588,61 +614,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 0.5, v0.mLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut + if (lambdaSelection.doLambdaRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 1.5, v0.mLambda()); - if (doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda + if (lambdaSelection.doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda return false; } rPtAnalysis.fill(HIST("hNLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 2.5, v0.mLambda()); - if (doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) + if (lambdaSelection.doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 3.5, v0.mLambda()); - if (doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct + if (lambdaSelection.doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct return false; } rPtAnalysis.fill(HIST("hNLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 4.5, v0.mLambda()); - if (doLambdaArmenterosCut && (v0.alpha() <= 0 || v0.qtarm() > lambdaparamArmenterosCut * v0.alpha())) { // Lambda Armenteros Cut + if (lambdaSelection.doLambdaArmenterosCut && (v0.alpha() <= 0 || v0.qtarm() > lambdaparamArmenterosCut * v0.alpha())) { // Lambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 5.5, v0.mLambda()); - if (doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut + if (lambdaSelection.doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 6.5, v0.mLambda()); - if (doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut + if (lambdaSelection.doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 7.5, v0.mLambda()); - if (doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut + if (lambdaSelection.doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 8.5, v0.mLambda()); - if (doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 9.5, v0.mLambda()); - if (doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 10.5, collision.centFT0M()); @@ -670,61 +696,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 0.5, v0.mAntiLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut + if (antilambdaSelection.doAntiLambdaRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 1.5, v0.mAntiLambda()); - if (doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda + if (antilambdaSelection.doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 2.5, v0.mAntiLambda()); - if (doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) + if (antilambdaSelection.doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 3.5, v0.mAntiLambda()); - if (doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct + if (antilambdaSelection.doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 4.5, v0.mAntiLambda()); - if (doAntilambdaArmenterosCut && (v0.alpha() > 0 || v0.qtarm() > antilambdaparamArmenterosCut * std::abs(v0.alpha()))) { // AntiLambda Armenteros Cut + if (antilambdaSelection.doAntilambdaArmenterosCut && (v0.alpha() > 0 || v0.qtarm() > antilambdaparamArmenterosCut * std::abs(v0.alpha()))) { // AntiLambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 5.5, v0.mAntiLambda()); - if (doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut + if (antilambdaSelection.doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 6.5, v0.mAntiLambda()); - if (doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut + if (antilambdaSelection.doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 7.5, v0.mAntiLambda()); - if (doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut + if (antilambdaSelection.doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 8.5, v0.mAntiLambda()); - if (doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 9.5, v0.mAntiLambda()); - if (doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 10.5, collision.centFT0M()); @@ -750,17 +776,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -782,8 +808,8 @@ struct V0PtInvMassPlots { soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) { - rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events - if (std::abs(mcCollision.posZ()) > cutZVertexGen) { // Generated Z vertex cut + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (eventSelection.doZvertexGenCut && std::abs(mcCollision.posZ()) > cutZVertexGen) { // Generated Z vertex cut return; } // if (!(mcCollision.multMCNParticlesEta10() > 0)) { // TRY TO CHANGE TO THIS @@ -846,10 +872,10 @@ struct V0PtInvMassPlots { nParticlesPerCollision++; } } - } // End of MCParticle Loop + } // End of MCParticle Loop For Signal Loss Denominator rNchAnalysis.fill(HIST("hNchCentralityGenerated"), mcCollision.centFT0M(), nParticlesPerCollision); - // Signal Loss Numenator Loop + // Signal Loss Start, Make sure we have INEL Collisions int recoCollINEL = 0; int recoCollINELgt0 = 0; @@ -938,7 +964,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -965,6 +990,9 @@ struct V0PtInvMassPlots { if (!acceptEvent(collision)) { // Event Selection return; } + if (!collision.has_mcCollision()) { + return; + } rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut @@ -987,7 +1015,7 @@ struct V0PtInvMassPlots { // K0sh Signla Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges @@ -1021,7 +1049,7 @@ struct V0PtInvMassPlots { // Lambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { @@ -1061,7 +1089,7 @@ struct V0PtInvMassPlots { // AntiLambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { @@ -1100,7 +1128,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1121,6 +1148,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } + if (!acceptEvent(collision)) { // Event Selection return; } @@ -1158,7 +1186,7 @@ struct V0PtInvMassPlots { if (antiLambdaAnalysis == true) { if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection for (int i = 0; i < nAntilambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); } } @@ -1175,7 +1203,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1197,10 +1224,12 @@ struct V0PtInvMassPlots { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), collision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), collision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track @@ -1241,7 +1270,7 @@ struct V0PtInvMassPlots { } } void recMCProcessDerived(soa::Join::iterator const& collision, - // To add McCentFT0Ms + soa::Join const& /*mcCollisions*/, soa::Join const& V0s, DaughterTracksDerived const&) { @@ -1249,7 +1278,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1270,10 +1298,16 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Event Split Numenator + + // For centrality estimation + const auto& mcCollision = collision.straMCCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track @@ -1287,25 +1321,25 @@ struct V0PtInvMassPlots { // K0sh Signal Split Numerator Start for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator - pthistos::kaonSplit[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator + pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } // K0sh SignaL Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges - pthistos::kaonPt[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shPhiFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kPhi) { // Phi Mother Matched - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1318,31 +1352,31 @@ struct V0PtInvMassPlots { // Lambda Signal Split Numerator Start for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaSplit[i]->Fill(v0.mLambda(), collision.centFT0M()); + pthistos::lambdaSplit[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } // Lambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaPt[i]->Fill(v0.mLambda(), collision.centFT0M()); + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiMinus) { // Xi Minus Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kXi0) { // Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaMinus) { // Omega Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1355,31 +1389,31 @@ struct V0PtInvMassPlots { // AntiLambda Signal Split Numerator End for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } // AntiLambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiPlusBar) { // Xi Plus Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == -kXi0) { // Anti-Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaPlusBar) { // Anti-Omega (minus) Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1388,12 +1422,209 @@ struct V0PtInvMassPlots { } } } + // This ii the process for the MC generated derived data + void genMCProcessDerived( + soa::Join::iterator const& mcCollision, + // soa::SmallGroups> const& collision, + soa::SmallGroups> const& collisions, + aod::V0MCCores const& V0s, + aod::CascMCCores const& cascs, + DaughterTracksDerived const&) + { + // Event Efficiency, Event Split and V0 Signal Loss Corrections + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (std::abs(mcCollision.posZ()) > cutZVertex) { + return; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 1.5, mcCollision.centFT0M()); // Event Efficiency Denominator + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalfGen"), collisions.multNGlobalTracksPVetaHalf(), mcCollision.centFT0M()); // Nch vs Centrality EtaHalfCut + // Particles (of interest) Generated Pt Spectrum and Signal Loss Denominator Loop + for (const auto& v0 : V0s) { + if (v0.isPhysicalPrimary()) { + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multMCNParticlesEta05()); // K0sh vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Lambda vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Anti-Lambda vs Centrality EtaHalfCut + } + } + } + } + for (const auto& casc : cascs) { + if (casc.isPhysicalPrimary()) { + // Make rapidity cuts, from O2's documentation: rapidity (0, 1: Xi; 2, 3: Omega) + if (casc.pdgCode() == kXiMinus) // Xi Minus matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiMinusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXi0) // Xi Zero matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaMinus) // Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXiPlusBar) // Xi Plus matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiPlusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == -kXi0) // Anti-Xi Zero matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hAntiXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaPlusBar) // Anti-Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + } + } // End of MCParticle Loop For Signal Loss Denominator + + // Signal Loss Numenator Loop + for (const auto& collision : collisions) { + rMCCorrections.fill(HIST("hNEvents_Corrections"), 2.5, mcCollision.centFT0M()); // Number of Events Reconsctructed + if (!acceptEvent(collision)) { // Event Selection + continue; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Event Split Denomimator and Event Efficiency Numenator + for (const auto& v0 : V0s) { + if (!v0.isPhysicalPrimary()) { + continue; + } + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + } + } + // End of Signal Loss Numenator Loop + } + // This is the process for Real Data MC Closure Test + void dataProcessClosureTest(soa::Join::iterator const& collision, + soa::Join const& /*mcCollisions*/, + aod::V0Datas const& V0s, + DaughterTracks const&) + { + // tokenise strings into individual values + pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); + pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); + pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); + + // Calculate number of histograms for each particle type + int nKaonHistograms = pthistos::kaonPtBins.size() - 1; + int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; + int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; + + // initialize and convert tokenized strings into vector of doubles for Pt Bin Edges + std::vector kaonptedgevalues(nKaonHistograms + 1); + std::vector lambdaptedgevalues(nLambdaHistograms + 1); + std::vector antilambdaptedgevalues(nAntilambdaHistograms + 1); + + for (int i = 0; i < nKaonHistograms + 1; i++) { + kaonptedgevalues[i] = std::stod(pthistos::kaonPtBins[i]); + } + for (int i = 0; i < nLambdaHistograms + 1; i++) { + lambdaptedgevalues[i] = std::stod(pthistos::lambdaPtBins[i]); + } + for (int i = 0; i < nAntilambdaHistograms + 1; i++) { + antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); + } + + // For centrality estimation + const auto& mcCollision = collision.mcCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut + for (const auto& v0 : V0s) { + // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) + const auto& posDaughterTrack = v0.template posTrack_as(); + const auto& negDaughterTrack = v0.template negTrack_as(); + if (!acceptV0(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selection + continue; + } + // K0sh analysis + if (kzeroAnalysis == true) { + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms + } + } + } + } + // Lambda analysis + if (lambdaAnalysis == true) { + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selection + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + } + } + // Anti-Lambda analysis + if (antiLambdaAnalysis == true) { + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + } + } + } + } PROCESS_SWITCH(V0PtInvMassPlots, genMCProcess, "Process Run 3 MC Generated", false); PROCESS_SWITCH(V0PtInvMassPlots, recMCProcess, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data,", false); - // PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated", false); - PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data,", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data", false); + PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessClosureTest, "Process Run 3 Data Closure Test", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From ab3803eb9532d7c77a494a456b070546370821e4 Mon Sep 17 00:00:00 2001 From: amatyja Date: Fri, 2 Oct 2026 20:58:43 +0200 Subject: [PATCH 155/648] [PWGUD] Add generator ID check to MC (#18149) --- .../tauThreeProngEventTableProducer.cxx | 102 ++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index e0967520da8..0a5f76df134 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -171,6 +171,7 @@ struct TauThreeProngEventTableProducer { // ConfigurableAxis minvAxis{"minvAxis", {100, 0.5, 5.0}, "M_{inv} (GeV/#it{c}^{2})"}; // ConfigurableAxis phiAxis{"phiAxis", {120, 0., 3.2}, "#phi"}; Configurable verbose{"verbose", {}, "Additional print outs"}; + Configurable> generatorIds{"generatorIds", std::vector{31}, "MC generatorIds to process"}; // cut selection configurables // Configurable zvertexcut{"zvertexcut", 10., "Z vertex cut"}; @@ -262,21 +263,22 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/nTof", ";N_{TOFtrk};events", {HistType::kTH1F, {{10, -1., 9.}}}); } if (doprocessMonteCarlo) { - registrySkim.add("skim/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{15, 0., 15.}}}); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "1: All"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "2: N^{#tau}=2"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "3: |y^{#tau}| <= 0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "4: |#eta^{ch}|<0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "5: 4 or 6 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "e+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "#mu+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#pi+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "6#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "rec"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec e+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec #mu+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #pi+3#pi"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec 6#pi"); + registrySkim.add("skim/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <= 0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); registrySkim.add("skim/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); @@ -294,23 +296,24 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/daughterPtMC", ";p_{T}^{daughter};events", {HistType::kTH1F, {{100, 0, 5.0}}}); } if (doprocessGenerated) { - registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{15, 0., 15.}}}); + registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "N^{#tau}=2"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "|y^{#tau}| <=0.9"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|#eta^{ch}|<=0.9"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "4 or 6 trk"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "6: 4 trk"); // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "7: 6 trk"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "e+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "#mu+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#pi+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "6#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "rec"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec e+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec #mu+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #pi+3#pi"); - registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec 6#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); registrySkim.add("gen/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(1, "1: NoProblem"); @@ -1149,9 +1152,27 @@ struct TauThreeProngEventTableProducer { // start loop over generated collisions for (const auto& mccoll : mcCollisions) { + // all collisions if (verbose) LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // all MC collisions + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } // set up default values per colission trueTauX[0] = -999.; @@ -1719,7 +1740,6 @@ struct TauThreeProngEventTableProducer { PROCESS_SWITCH(TauThreeProngEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by SG-Candidate-Producer.", false); // only MC information and what events are reconstructed - void processGenerated(aod::UDMcCollisions const& mcCollisions, aod::UDMcParticles const& mcParticles, FullMCSGUDCollisions const& collisions) @@ -1750,9 +1770,27 @@ struct TauThreeProngEventTableProducer { // start loop over generated collisions for (const auto& mccoll : mcCollisions) { + // all collisions if (verbose) LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); - registrySkim.get(HIST("gen/efficiencyMC"))->Fill(0., 1.); // all MC collisions + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } // set up default values per colission trueTauX[0] = -999.; From 102bab2d6501ddebdea828662ce8c603b6b225a8 Mon Sep 17 00:00:00 2001 From: rbailhac Date: Fri, 2 Oct 2026 22:25:55 +0200 Subject: [PATCH 156/648] [ALICE3] Add kaon and proton contamination in the combinatorial background (#18156) --- .../TableProducer/alice3-dileptonsmearing.cxx | 254 ++++++++++++++---- ALICE3/Tasks/alice3-dilepton.cxx | 33 ++- 2 files changed, 239 insertions(+), 48 deletions(-) diff --git a/ALICE3/TableProducer/alice3-dileptonsmearing.cxx b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx index cca45f14c38..4136e34da82 100644 --- a/ALICE3/TableProducer/alice3-dileptonsmearing.cxx +++ b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx @@ -89,8 +89,42 @@ struct alice3dileptonsmearer { Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; } pion_filenames; + struct : ConfigurableGroup { + std::string prefix = "kaon_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } kaon_filenames; + + struct : ConfigurableGroup { + std::string prefix = "proton_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } proton_filenames; + MomentumSmearer smearer_Electron; MomentumSmearer smearer_Pion; + MomentumSmearer smearer_Kaon; + MomentumSmearer smearer_Proton; Service ccdb; void init(InitContext&) @@ -129,6 +163,32 @@ struct alice3dileptonsmearer { smearer_Pion.setDCAHistName(""); smearer_Pion.setMinPt(pion_filenames.cfgMinPt); + smearer_Kaon.setNDSmearing(kaon_filenames.cfgNDSmearing.value); + smearer_Kaon.setResFileName(TString(kaon_filenames.cfgResFileName)); + smearer_Kaon.setResNDHistName(TString(kaon_filenames.cfgResNDHistName)); + smearer_Kaon.setResPtHistName(TString(kaon_filenames.cfgResPtHistName)); + smearer_Kaon.setResEtaHistName(TString(kaon_filenames.cfgResEtaHistName)); + smearer_Kaon.setResPhiPosHistName(TString(kaon_filenames.cfgResPhiPosHistName)); + smearer_Kaon.setResPhiNegHistName(TString(kaon_filenames.cfgResPhiNegHistName)); + smearer_Kaon.setEffFileName(TString(kaon_filenames.cfgEffFileName)); + smearer_Kaon.setEffHistName(TString(kaon_filenames.cfgEffHistName)); + smearer_Kaon.setDCAFileName(""); + smearer_Kaon.setDCAHistName(""); + smearer_Kaon.setMinPt(kaon_filenames.cfgMinPt); + + smearer_Proton.setNDSmearing(proton_filenames.cfgNDSmearing.value); + smearer_Proton.setResFileName(TString(proton_filenames.cfgResFileName)); + smearer_Proton.setResNDHistName(TString(proton_filenames.cfgResNDHistName)); + smearer_Proton.setResPtHistName(TString(proton_filenames.cfgResPtHistName)); + smearer_Proton.setResEtaHistName(TString(proton_filenames.cfgResEtaHistName)); + smearer_Proton.setResPhiPosHistName(TString(proton_filenames.cfgResPhiPosHistName)); + smearer_Proton.setResPhiNegHistName(TString(proton_filenames.cfgResPhiNegHistName)); + smearer_Proton.setEffFileName(TString(proton_filenames.cfgEffFileName)); + smearer_Proton.setEffHistName(TString(proton_filenames.cfgEffHistName)); + smearer_Proton.setDCAFileName(""); + smearer_Proton.setDCAHistName(""); + smearer_Proton.setMinPt(proton_filenames.cfgMinPt); + if (cfgFromCcdb) { ccdb->setURL(cfgCcdbUrl); ccdb->setCaching(true); @@ -148,10 +208,26 @@ struct alice3dileptonsmearer { smearer_Pion.setCcdbPathDCA(""); smearer_Pion.setTimestamp(timestamp); smearer_Pion.setCcdb(ccdb); + + smearer_Kaon.setCcdbPathRes(TString(kaon_filenames.cfgCcdbPathRes)); + smearer_Kaon.setCcdbPathEff(TString(kaon_filenames.cfgCcdbPathEff)); + // smearer_Kaon.setCcdbPathDCA(TString(kaon_filenames.fConfigCcdbPathDCA)); + smearer_Kaon.setCcdbPathDCA(""); + smearer_Kaon.setTimestamp(timestamp); + smearer_Kaon.setCcdb(ccdb); + + smearer_Proton.setCcdbPathRes(TString(proton_filenames.cfgCcdbPathRes)); + smearer_Proton.setCcdbPathEff(TString(proton_filenames.cfgCcdbPathEff)); + // smearer_Proton.setCcdbPathDCA(TString(proton_filenames.fConfigCcdbPathDCA)); + smearer_Proton.setCcdbPathDCA(""); + smearer_Proton.setTimestamp(timestamp); + smearer_Proton.setCcdb(ccdb); } smearer_Electron.init(); smearer_Pion.init(); + smearer_Kaon.init(); + smearer_Proton.init(); } void processACTSHybrid(MyTracks const& tracks, const aod::McParticles& /*mcParticles*/) @@ -211,6 +287,54 @@ struct alice3dileptonsmearer { } // fill the table smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Kaon.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(kaon_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(kaon_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Kaon.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Proton.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(proton_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(proton_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Proton.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); } else { // don't apply smearing and reject completely smearedelectron(ptgen, etagen, phigen, false); @@ -247,36 +371,40 @@ struct alice3dileptonchecksmearer { void init(o2::framework::InitContext&) { - registry.add("Electron/hCorrelation_Pt", "pT correlation;p_{T,l}^{gen} (GeV/c);p_{T,l}^{smeared} (GeV/c)", {HistType::kTH2F, {{1000, 0.0f, 10.0f}, {1000, 0.0f, 10.0f}}}); - registry.add("Electron/hCorrelation_Eta", "eta correlation;#eta_{l}^{gen};#eta_{l}^{smeared}", {HistType::kTH2F, {{200, -1.0f, +1.0f}, {200, -1.0f, +1.0f}}}); - registry.add("Electron/hCorrelation_Phi", "phi correlation;#varphi_{l}^{gen} (rad.);#varphi_{l}^{smeared} (rad.)", {HistType::kTH2F, {{100, 0.0f, TMath::TwoPi()}, {100, 0.0f, TMath::TwoPi()}}}); - - // Binning for resolution - AxisSpec axisPtRes{ptResBins, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}; - AxisSpec axisDeltaptRes{deltaptResBins, "(p^{gen}_{T} - p^{rec}_{T}) / p^{gen}_{T}"}; - AxisSpec axisDeltaetaRes{deltaetaResBins, "#eta^{gen} - #eta^{rec}"}; - AxisSpec axisDeltaphiRes{deltaphiResBins, "#varphi^{gen} - #varphi^{rec} (rad.)"}; - // Binning for efficiency - AxisSpec axiseta{etaBins, "#eta"}; - - if (!cfgUsePtVecRes) { - registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {axisPtRes, axisDeltaptRes}, true); - registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {axisPtRes, axisDeltaetaRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); - registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {axisPtRes, axiseta}, true); - registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {axisPtRes, axiseta}, true); - } else { - registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaptRes}, true); - registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaetaRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); - registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); - registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); - registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); - } + if (doprocessCheckACTSHybrid) { + registry.add("Electron/hCorrelation_Pt", "pT correlation;p_{T,l}^{gen} (GeV/c);p_{T,l}^{smeared} (GeV/c)", {HistType::kTH2F, {{1000, 0.0f, 10.0f}, {1000, 0.0f, 10.0f}}}); + registry.add("Electron/hCorrelation_Eta", "eta correlation;#eta_{l}^{gen};#eta_{l}^{smeared}", {HistType::kTH2F, {{200, -1.0f, +1.0f}, {200, -1.0f, +1.0f}}}); + registry.add("Electron/hCorrelation_Phi", "phi correlation;#varphi_{l}^{gen} (rad.);#varphi_{l}^{smeared} (rad.)", {HistType::kTH2F, {{100, 0.0f, TMath::TwoPi()}, {100, 0.0f, TMath::TwoPi()}}}); + + // Binning for resolution + AxisSpec axisPtRes{ptResBins, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}; + AxisSpec axisDeltaptRes{deltaptResBins, "(p^{gen}_{T} - p^{rec}_{T}) / p^{gen}_{T}"}; + AxisSpec axisDeltaetaRes{deltaetaResBins, "#eta^{gen} - #eta^{rec}"}; + AxisSpec axisDeltaphiRes{deltaphiResBins, "#varphi^{gen} - #varphi^{rec} (rad.)"}; + // Binning for efficiency + AxisSpec axiseta{etaBins, "#eta"}; + + if (!cfgUsePtVecRes) { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {axisPtRes, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {axisPtRes, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + } else { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + } - registry.addClone("Electron/", "Others/"); - registry.addClone("Electron/", "Pions/"); + registry.addClone("Electron/", "Other/"); + registry.addClone("Electron/", "Pion/"); + registry.addClone("Electron/", "Kaon/"); + registry.addClone("Electron/", "Proton/"); + } } void processCheckACTSHybrid(MyTracksWithSmearing const& tracks, const aod::McParticles& /*mcParticles*/) @@ -309,36 +437,68 @@ struct alice3dileptonchecksmearer { registry.fill(HIST("Electron/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { - registry.fill(HIST("Pions/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); - registry.fill(HIST("Pions/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + registry.fill(HIST("Pion/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Pion/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Pion/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Pion/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Pion/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Pion/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Pion/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Pion/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Pion/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + registry.fill(HIST("Kaon/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Kaon/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Kaon/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Kaon/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Kaon/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + registry.fill(HIST("Proton/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Proton/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); if (mcParticle.pdgCode() < 0) { // e+ - registry.fill(HIST("Pions/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Proton/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); } else { // e- - registry.fill(HIST("Pions/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Proton/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); } - registry.fill(HIST("Pions/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); - registry.fill(HIST("Pions/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); - registry.fill(HIST("Pions/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + registry.fill(HIST("Proton/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Proton/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Proton/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); // efficiency - registry.fill(HIST("Pions/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Proton/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); if (track.selected()) { - registry.fill(HIST("Pions/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Proton/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } else { - registry.fill(HIST("Others/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); - registry.fill(HIST("Others/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + registry.fill(HIST("Other/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Other/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); if (mcParticle.pdgCode() < 0) { // e+ - registry.fill(HIST("Others/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Other/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); } else { // e- - registry.fill(HIST("Others/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + registry.fill(HIST("Other/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); } - registry.fill(HIST("Others/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); - registry.fill(HIST("Others/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); - registry.fill(HIST("Others/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + registry.fill(HIST("Other/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Other/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Other/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); // efficiency - registry.fill(HIST("Others/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Other/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); if (track.selected()) { - registry.fill(HIST("Others/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + registry.fill(HIST("Other/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); } } } // end of loop diff --git a/ALICE3/Tasks/alice3-dilepton.cxx b/ALICE3/Tasks/alice3-dilepton.cxx index f9b3d5e0d5f..e6fa5079cc3 100644 --- a/ALICE3/Tasks/alice3-dilepton.cxx +++ b/ALICE3/Tasks/alice3-dilepton.cxx @@ -528,7 +528,8 @@ struct Alice3Dilepton { SliceCache cache_rec; Configurable pdg{"pdg", 11, "pdg code for analysis. dielectron:11, dimuon:13"}; - Configurable requireHFEid{"requireHFEid", true, "Require HFE identification"}; + Configurable requireDCid{"requireDCid", false, "Require pi0 or photon as mother"}; + Configurable requireHFEid{"requireHFEid", false, "Require HFE identification"}; Configurable contamination{"contamination", false, "Fill only pairs with one misidentifixed electrons"}; Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; @@ -630,6 +631,26 @@ struct Alice3Dilepton { } } + template + bool IsDC(TTrack const& track, TMCParticles const& mcparticles) + { + if (!track.has_mcParticle()) { + return false; + } + const auto p1 = track.template mcParticle_as(); + if (!p1.has_mothers()) { + return false; + } + int motherId = p1.mothersIds()[0]; + if (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if ((std::abs(mp.pdgCode()) == PDG_t::kPi0) || (std::abs(mp.pdgCode()) == PDG_t::kGamma)) { + return true; + } + } + return false; + } + template bool IsHF(TTrack const& track, TMCParticles const& mcparticles) { @@ -843,6 +864,11 @@ struct Alice3Dilepton { continue; } } + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } + } float pair_dca_xy = 999.f; ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); @@ -873,6 +899,11 @@ struct Alice3Dilepton { continue; } } + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } + } float pair_dca_xy = 999.f; ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); From 9888df8d1f4599dc40a53be666c084436f7ccca0 Mon Sep 17 00:00:00 2001 From: Subhadeep Roy Date: Sat, 3 Oct 2026 02:47:56 +0530 Subject: [PATCH 157/648] [PWGCF] Updated the pair constructions in lambdaSpinPolarization.cxx (#18157) --- .../Tasks/lambdaSpinPolarization.cxx | 2305 ++++++++--------- 1 file changed, 1082 insertions(+), 1223 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index f5d26c6fbaa..67a58ab98ec 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -43,18 +43,27 @@ #include #include +#include #include +#include +#include #include +#include #include #include #include +#include #include +#include #include #include #include +#include +#include #include #include +#include #include using namespace o2; @@ -70,7 +79,6 @@ namespace lambdacollision DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Mult, mult, float); DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); -// DECALRE_SOA_ } // namespace lambdacollision DECLARE_SOA_TABLE(LambdaCollisions, "AOD", "LAMBDACOLS", o2::soa::Index<>, @@ -172,7 +180,6 @@ using LambdaMixEventMcGenCollision = LambdaMixEventMcGenCollisions::iterator; namespace lambdamixeventtracks { -// DECLARE_SOA_INDEX_COLUMN(LambdaMixEventCollision, lambdaMixEventCollision); DECLARE_SOA_COLUMN(LambdaMixEventCollisionIdx, lambdaMixEventCollisionIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTrackIdx, lambdaMixEventTrackIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTimeStamp, lambdaMixEventTimeStamp, uint64_t); @@ -180,7 +187,6 @@ DECLARE_SOA_COLUMN(LambdaMixEventTimeStamp, lambdaMixEventTimeStamp, uint64_t); DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", o2::soa::Index<>, - // lambdamixeventtracks::LambdaMixEventCollisionId, lambdamixeventtracks::LambdaMixEventCollisionIdx, lambdamixeventtracks::LambdaMixEventTrackIdx, lambdatrack::Px, lambdatrack::Py, lambdatrack::Pz, lambdatrack::Mass, @@ -207,6 +213,21 @@ enum CollisionLabels { kTotColBeforeHasMcCollision = 1, kTotCol, kPassSelCol }; +enum EventCutFlow { kEvAll = 1, + kEvTrigger, + kEvTvx, + kEvTFBorder, + kEvItsRofBorder, + kEvItsTpcVtx, + kEvNoSameBunchPileup, + kEvGoodZvtxFT0vsPV, + kEvGoodItsLayers, + kEvCentrality, + kEvVz, + kEvOneV0, + kEvTwoV0, + kNEventCutFlow }; + enum TrackLabels { kTracksBeforeHasMcParticle = 1, kAllV0Tracks, @@ -245,23 +266,32 @@ enum RunType { kRun3 = 0, enum ParticleType { kLambda = 0, kAntiLambda }; -enum ParticlePairType { - - kLambdaAntiLambda = 0, - kAntiLambdaLambda = 1, - kLambdaLambda = 2, - kAntiLambdaAntiLambda = 3, - - kLambdaSBAntiLambda = 4, - kAntiLambdaSBLambda = 5, - kLambdaSBLambda = 6, - kAntiLambdaSBAntiLambda = 7, - - kLambdaSBSBAntiLambda = 8, - kAntiLambdaSBSBLambda = 9, - kLambdaSBSBLambda = 10, - kAntiLambdaSBSBAntiLambda = 11 -}; +enum ParticlePairType { kLambdaAntiLambda = 0, + kAntiLambdaLambda, + kLambdaLambda, + kAntiLambdaAntiLambda }; + +enum PairTier { kSameEvent = 0, + kSameEventInMixing, + kMixedEvent, + kMcGenSameEvent, + kMcGenSameEventInMixing, + kMcGenMixedEvent }; + +enum PairEventCount { kPeAll = 1, + kPeOneCand, + kPeTwoCand, + kPeUnlikeSign, + kPeLambdaLambda, + kPeAntiLambdaAntiLambda, + kNPairEventCount }; + +enum MEEventStatus { kMEEventSeen = 1, + kMEEventOutsidePools, + kMEEventWarmUp, + kMEEventMixed, + kMEEventDonated, + kNMEEventStatus }; enum ShareDauLambda { kUniqueLambda = 0, kLambdaShareDau }; @@ -284,9 +314,10 @@ enum PrmScdPairType { kPP = 0, kSP, kSS }; -// Number of daughters of the two-body decay Lambda -> p + pi static constexpr std::size_t NDaughtersTwoBody = 2; +static constexpr std::size_t NCandidatesForPair = 2; + struct LambdaTableProducer { Produces lambdaCollisionTable; @@ -294,8 +325,7 @@ struct LambdaTableProducer { Produces lambdaMCGenCollisionTable; Produces lambdaMCGenTrackTable; - Configurable cCentEstimator{"cCentEstimator", 0, - "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; @@ -303,130 +333,83 @@ struct LambdaTableProducer { Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; Configurable cInt7Trig{"cInt7Trig", false, "kINT7 MB Trigger"}; Configurable cSel7Trig{"cSel7Trig", false, "Sel7 (V0A+V0C) Run2"}; - Configurable cTriggerTvxSel{"cTriggerTvxSel", false, - "TVX Trigger Selection"}; - Configurable cTFBorder{"cTFBorder", false, - "Timeframe Border Selection"}; - Configurable cNoItsROBorder{"cNoItsROBorder", false, - "No ITSRO Border Cut"}; - Configurable cItsTpcVtx{"cItsTpcVtx", false, - "ITS+TPC Vertex Selection"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; + Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; + Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; + Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; - Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, - "z-vtx time diff selection"}; - Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, - "Good ITS Layers All"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; - // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; - Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, - "TPC Min Crossed Rows"}; - Configurable cMinTpcCROverCls{"cMinTpcCROverCls", 0.8, - "TPC Min CR/Findable Cls"}; - Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 0.4, - "TPC Max Shared Clusters"}; - Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 4, "Max TPC Chi2/ndf"}; - Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 3.0, - "TPC nSigma PID cut"}; - Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, - "Remove Ambiguous Tracks"}; - - Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.02, - "Min proton DCA to PV (cm)"}; - Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.06, - "Min pion DCA to PV (cm)"}; - Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., - "Min DCA between V0 daughters"}; - Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1., - "Max DCA between V0 daughters"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, "TPC Min Crossed Rows"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut"}; + Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; - Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 999.0, - "Max DCA V0 to PV"}; - Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, - "Min V0 decay radius (cm)"}; - Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, - "Max V0 decay radius (cm)"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.1, "Max DCA V0 to PV"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; Configurable cMaxV0CTau{"cMaxV0CTau", 30.0, "Max cTau (cm)"}; Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Min V0 cos(PA)"}; - Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.01, - "K0s mass rejection window"}; - Configurable cKshortRejFlag{"cKshortRejFlag", true, - "K0s mass rejection flag"}; - - // V0s kinmatic acceptance - Configurable cMinV0Mass{"cMinV0Mass", 1.10, "V0 Mass Min"}; - Configurable cMaxV0Mass{"cMaxV0Mass", 1.12, "V0 Mass Max"}; - Configurable cMinV0Pt{"cMinV0Pt", 0.8, "Minimum V0 pT"}; - Configurable cMaxV0Pt{"cMaxV0Pt", 4.2, "Minimum V0 pT"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; - Configurable cV0TypeSelFlag{"cV0TypeSelFlag", false, - "V0 Type Selection Flag"}; - Configurable cV0TypeSelection{"cV0TypeSelection", 1, - "V0 Type Selection"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; - // V0s MC Configurable cHasMcFlag{"cHasMcFlag", true, "Has Mc Tag"}; - Configurable cSelectTrueLambda{"cSelectTrueLambda", true, - "Select True Lambda"}; - Configurable cSelMCPSV0{"cSelMCPSV0", true, - "Select Primary/Secondary V0"}; - Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, - "Check for reco daughter PID"}; - Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, - "Primary Generated Lambda"}; - Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, - "Secondary Generated Lambda"}; - Configurable cGenDecayChannel{"cGenDecayChannel", true, - "Gen Level Decay Channel Flag"}; + Configurable cSelectTrueLambda{"cSelectTrueLambda", false, "Select True Lambda"}; + Configurable cSelMCPSV0{"cSelMCPSV0", false, "Select Primary/Secondary V0"}; + Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, "Check for reco daughter PID"}; + Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, "Primary Generated Lambda"}; + Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, "Secondary Generated Lambda"}; + Configurable cGenDecayChannel{"cGenDecayChannel", false, "Gen Level Decay Channel Flag"}; Configurable cRecoMomResoFlag{"cRecoMomResoFlag", false, "Check effect of momentum space smearing on balance function"}; - // Efficiency Correction - Configurable cCorrectionFlag{"cCorrectionFlag", false, - "Correction Flag"}; - Configurable cGetEffFact{"cGetEffFact", false, - "Get Efficiency Factor Flag"}; - Configurable cGetPrimFrac{"cGetPrimFrac", false, - "Get Primary Fraction Flag"}; - Configurable cCorrFactHist{"cCorrFactHist", 0, - "Efficiency Factor Histogram"}; - Configurable cPrimFracHist{"cPrimFracHist", 0, - "Primary Fraction Histogram"}; - - // CCDB - Configurable cUrlCCDB{"cUrlCCDB", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; - Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/lambda_corr_fact", "Path for ccdb-object"}; - - // Initialize CCDB Service + Configurable cCorrectionFlag{"cCorrectionFlag", false, "Correction Flag"}; + Configurable cGetEffFact{"cGetEffFact", true, "Get Efficiency Factor Flag"}; + Configurable cGetPrimFrac{"cGetPrimFrac", false, "Get Primary Fraction Flag"}; + Configurable cCorrFactHist{"cCorrFactHist", 0, "Efficiency Factor Histogram"}; + Configurable cPrimFracHist{"cPrimFracHist", 0, "Primary Fraction Histogram"}; + + Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "url of ccdb"}; + Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/SpinCorr/RecoEfficiency", "Path for ccdb-object"}; + Service ccdb{}; - // Histogram Registry. - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // initialize corr_factor objects std::vector> vCorrFactStrings = { {"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, {"hEffVsPtEtaCentLambda", "hEffVsPtEtaCentAntiLambda"}}; - // initialize corr_factor objects std::vector> vPrimFracStrings = { {"hPrimFracVsPtCentLambda", "hPrimFracVsPtCentAntiLambda"}, {"hPrimFracVsPtYCentLambda", "hPrimFracVsPtYCentAntiLambda"}, {"hPrimFracVsPtEtaCentLambda", "hPrimFracVsPtEtaCentAntiLambda"}}; - // Initialize Global Variables float cent = 0., mult = 0.; void init(InitContext const&) { - // Set CCDB url ccdb->setURL(cUrlCCDB.value); ccdb->setCaching(true); @@ -437,7 +420,7 @@ struct LambdaTableProducer { const AxisSpec axisVz(220, -11, 11, "V_{z} (cm)"); const AxisSpec axisPID(8000, -4000, 4000, "PdgCode"); - const AxisSpec axisV0Mass(120, 1.08, 1.20, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); const AxisSpec axisV0Rap(48, -1.2, 1.2, "y"); const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); @@ -458,102 +441,77 @@ struct LambdaTableProducer { const AxisSpec axisNsigma(401, -10.025, 10.025, "n#sigma"); const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); - // Create Histograms. - // Event histograms - histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, - {axisCols}); - histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, - {axisVz}); + histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, {axisCols}); + histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, {axisVz}); + histos.add("Events/h1f_collision_cent", "Centrality of the selected collisions", kTH1F, {axisCent}); + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNEventCutFlow - 1, 0.5, static_cast(kNEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kEvAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kEvNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kEvCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvOneV0, "#geq 1 selected #Lambda/#bar{#Lambda}"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTwoV0, "#geq 2 selected #Lambda/#bar{#Lambda}"); histos.add("Tracks/h1f_tracks_info", "# of tracks", kTH1F, {axisTrks}); - histos.add("Tracks/h2f_armpod_before_sel", "Armenteros-Podolanski (before)", - kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h2f_armpod_after_sel", "Armenteros-Podolanski (after)", - kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, - {axisV0Mass, axisV0Pt}); - histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", - kTH2F, {axisV0Mass, axisV0Pt}); - - histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armenteros-Podolanski", kTH2F, - {axisAlpha, axisQtarm}); - histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA V0 daughters", kTH1F, - {axisDcaDau}); - histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA pos-prong to PV", kTH1F, - {axisDcaProngPV}); - histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA neg-prong to PV", kTH1F, - {axisDcaProngPV}); - histos.add("QA/Lambda/h1f_dca_V0_to_PV", "DCA V0 to PV", kTH1F, - {axisDcaV0PV}); - histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, - {axisCosPA}); - histos.add("QA/Lambda/h1f_V0_radius", "V0 decay radius", kTH1F, - {axisRadius}); + histos.add("Tracks/h2f_armpod_before_sel", "Armenteros-Podolanski (before)", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h2f_armpod_after_sel", "Armenteros-Podolanski (after)", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", kTH2F, {axisV0Mass, axisV0Pt}); + + histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armenteros-Podolanski", kTH2F, {axisAlpha, axisQtarm}); + histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA V0 daughters", kTH1F, {axisDcaDau}); + histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA pos-prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA neg-prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h1f_dca_V0_to_PV", "DCA V0 to PV", kTH1F, {axisDcaV0PV}); + histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, {axisCosPA}); + histos.add("QA/Lambda/h1f_V0_radius", "V0 decay radius", kTH1F, {axisRadius}); histos.add("QA/Lambda/h1f_V0_ctau", "c#tau", kTH1F, {axisCTau}); histos.add("QA/Lambda/h1f_V0_gctau", "#gammac#tau", kTH1F, {axisGCTau}); - histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-prong p_{T}", kTH1F, - {axisTrackPt}); - histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-prong p_{T}", kTH1F, - {axisTrackPt}); - histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-prong #eta", kTH1F, - {axisV0Eta}); - histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-prong #eta", kTH1F, - {axisV0Eta}); - histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-prong #phi", kTH1F, - {axisV0Phi}); - histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-prong #phi", kTH1F, - {axisV0Phi}); - histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, - {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, - {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC dE/dx pos", kTH2F, - {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC dE/dx neg", kTH2F, - {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", - "TPC n#sigma_{p} pos", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", - "TPC n#sigma_{p} neg", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", - "TPC n#sigma_{#pi} pos", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", - "TPC n#sigma_{#pi} neg", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-prong #phi", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-prong #phi", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); + histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); + histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC dE/dx pos", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC dE/dx neg", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} neg", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} neg", kTH2F, {axisMomPID, axisNsigma}); histos.add("McRec/Lambda/hPt", "p_{T}", kTH1F, {axisV0Pt}); histos.add("McRec/Lambda/hEta", "#eta", kTH1F, {axisV0Eta}); histos.add("McRec/Lambda/hRap", "y", kTH1F, {axisV0Rap}); histos.add("McRec/Lambda/hPhi", "#phi", kTH1F, {axisV0Phi}); - // QA Anti-Lambda histos.addClone("QA/Lambda/", "QA/AntiLambda/"); histos.addClone("McRec/Lambda/", "McRec/AntiLambda/"); if (doprocessMCRun3) { - histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs before sel", kTH2F, - {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs after sel", kTH2F, - {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_lambda_mothers_pdg", "Lambda mothers", kTH2F, - {axisPID, axisV0Pt}); - - histos.add("McGen/h1f_collision_recgen", "RecGen collisions", kTH1F, - {axisMult}); - histos.add("McGen/h1f_collisions_info", "Collisions info", kTH1F, - {axisCols}); - histos.add("McGen/h2f_collision_posZ", "V_{z} rec vs gen", kTH2F, - {axisVz, axisVz}); - histos.add("McGen/h2f_collision_cent", "Centrality rec vs gen", kTH2F, - {axisCent, axisCent}); - histos.add("McGen/h1f_lambda_daughter_PDG", "Lambda dau PDG", kTH1F, - {axisPID}); - histos.add("McGen/h1f_antilambda_daughter_PDG", "AntiLambda dau PDG", - kTH1F, {axisPID}); + histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs before sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs after sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_lambda_mothers_pdg", "Lambda mothers", kTH2F, {axisPID, axisV0Pt}); + + histos.add("McGen/h1f_collision_recgen", "RecGen collisions", kTH1F, {axisMult}); + histos.add("McGen/h1f_collisions_info", "Collisions info", kTH1F, {axisCols}); + histos.add("McGen/h2f_collision_posZ", "V_{z} rec vs gen", kTH2F, {axisVz, axisVz}); + histos.add("McGen/h2f_collision_cent", "Centrality rec vs gen", kTH2F, {axisCent, axisCent}); + histos.add("McGen/h1f_lambda_daughter_PDG", "Lambda dau PDG", kTH1F, {axisPID}); + histos.add("McGen/h1f_antilambda_daughter_PDG", "AntiLambda dau PDG", kTH1F, {axisPID}); histos.addClone("McRec/", "McGen/"); - histos.add("McGen/Lambda/Proton/hPt", "Proton p_{T}", kTH1F, - {axisTrackPt}); + histos.add("McGen/Lambda/Proton/hPt", "Proton p_{T}", kTH1F, {axisTrackPt}); histos.add("McGen/Lambda/Proton/hEta", "Proton #eta", kTH1F, {axisV0Eta}); histos.add("McGen/Lambda/Proton/hRap", "Proton y", kTH1F, {axisV0Rap}); histos.add("McGen/Lambda/Proton/hPhi", "Proton #phi", kTH1F, {axisV0Phi}); @@ -561,120 +519,45 @@ struct LambdaTableProducer { histos.addClone("McGen/Lambda/Proton/", "McGen/AntiLambda/Proton/"); histos.addClone("McGen/Lambda/Pion/", "McGen/AntiLambda/Pion/"); - // set bin lables specific to MC - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, - "kTotColBeforeHasMcCollision"); - histos.get(HIST("McGen/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); - histos.get(HIST("McGen/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kTracksBeforeHasMcParticle, - "kTracksBeforeHasMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPrimaryLambda, "kPrimaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kSecondaryLambda, "kSecondaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kLambdaDauNotMcParticle, - "kLambdaDauNotMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kLambdaNotPrPiMinus, - "kLambdaNotPrPiMinus"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAntiLambdaNotAntiPrPiPlus, - "kAntiLambdaNotAntiPrPiPlus"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassTrueLambdaSel, "kPassTrueLambdaSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenTotAccLambda, "kGenTotAccLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); - } - - // set bin labels - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); - histos.get(HIST("Events/h1f_collisions_info")) - ->GetXaxis() - ->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0KShortMassRej, "kV0KShortMassRej"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNotLambdaNotAntiLambda, - "kNotLambdaNotAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0IsBothLambdaAntiLambda, - "kV0IsBothLambdaAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNotLambdaAfterSel, "kNotLambdaAfterSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kV0IsLambdaOrAntiLambda, - "kV0IsLambdaOrAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0KinCuts, "kPassV0KinCuts"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPassV0TopoSel, "kPassV0TopoSel"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kAllSelPassed, "kAllSelPassed"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kEffCorrPtCent, "kEffCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kEffCorrPtRapCent, "kEffCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNoEffCorr, "kNoEffCorr"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info")) - ->GetXaxis() - ->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, "kTotColBeforeHasMcCollision"); + histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kTracksBeforeHasMcParticle, "kTracksBeforeHasMcParticle"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPrimaryLambda, "kPrimaryLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kSecondaryLambda, "kSecondaryLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaDauNotMcParticle, "kLambdaDauNotMcParticle"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaNotPrPiMinus, "kLambdaNotPrPiMinus"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAntiLambdaNotAntiPrPiPlus, "kAntiLambdaNotAntiPrPiPlus"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassTrueLambdaSel, "kPassTrueLambdaSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenTotAccLambda, "kGenTotAccLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); + } + + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0KShortMassRej, "kV0KShortMassRej"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaNotAntiLambda, "kNotLambdaNotAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsBothLambdaAntiLambda, "kV0IsBothLambdaAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaAfterSel, "kNotLambdaAfterSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsLambdaOrAntiLambda, "kV0IsLambdaOrAntiLambda"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0KinCuts, "kPassV0KinCuts"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0TopoSel, "kPassV0TopoSel"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllSelPassed, "kAllSelPassed"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtCent, "kEffCorrPtCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtRapCent, "kEffCorrPtRapCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoEffCorr, "kNoEffCorr"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); + histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); } template bool selCollision(C const& col) { - if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { - return false; - } - + histos.fill(HIST("Events/hEventCutFlow"), kEvAll); if constexpr (run == kRun3) { if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); @@ -693,50 +576,59 @@ struct LambdaTableProducer { return false; } } + histos.fill(HIST("Events/hEventCutFlow"), kEvTrigger); - if (cent <= cMinMult || cent >= cMaxMult) { - return false; - } if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvTvx); if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvTFBorder); if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvItsRofBorder); if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvItsTpcVtx); if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvNoSameBunchPileup); if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (cIsGoodITSLayers && - !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodZvtxFT0vsPV); + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodItsLayers); + + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); mult = col.multNTracksPV(); return true; } - // Kinematic Selection - bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, - float const& ptMax, float const& rapMax) + bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) { return pt > ptMin && pt < ptMax && rap < rapMax; } - // Track Selection template bool selTrack(T const& track) { - if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, - cTrackMaxPt, cTrackEtaCut)) { + if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { return false; } if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { @@ -754,7 +646,6 @@ struct LambdaTableProducer { return true; } - // Daughter Track Selection template bool selDaughterTracks(V const& v0, T const&, ParticleType const& v0Type) { @@ -764,8 +655,6 @@ struct LambdaTableProducer { return false; } - // Apply DCA Selection on Daughter Tracks Based on Lambda/AntiLambda - // daughters float dcaProton = 0., dcaPion = 0.; if (v0Type == kLambda) { dcaProton = std::abs(v0.dcapostopv()); @@ -783,20 +672,17 @@ struct LambdaTableProducer { template bool topoCutSelection(C const& col, V const& v0, T const&) { - if (v0.dcaV0daughters() <= cMinV0DcaDaughters || - v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { return false; } if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { return false; } - if (v0.v0radius() <= cMinV0TransRadius || - v0.v0radius() >= cMaxV0TransRadius) { + if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { return false; } - float ctau = - v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { return false; } @@ -817,34 +703,27 @@ struct LambdaTableProducer { tpcNSigmaPr = negtrack.tpcNSigmaPr(); tpcNSigmaPi = postrack.tpcNSigmaPi(); } - return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && - std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); } template bool selLambdaMassWindow(V const& v0, T const&, ParticleType& v0type) { - // Kshort mass rejection hypothesis - if (cKshortRejFlag && - (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); return false; } - // initialize daughter tracks auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - // initialize selection flags bool lambdaFlag = false, antiLambdaFlag = false; - if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && - selLambdaDauWithTpcPid(postrack, negtrack)) { + if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { lambdaFlag = true; v0type = kLambda; } - if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && - selLambdaDauWithTpcPid(postrack, negtrack)) { + if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { antiLambdaFlag = true; v0type = kAntiLambda; } @@ -861,8 +740,7 @@ struct LambdaTableProducer { } template - bool selV0Particle(C const& col, V const& v0, T const& tracks, - ParticleType& v0Type) + bool selV0Particle(C const& col, V const& v0, T const& tracks, ParticleType& v0Type) { if (!selLambdaMassWindow(v0, tracks, v0Type)) { return false; @@ -885,7 +763,6 @@ struct LambdaTableProducer { } histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); - // All Selection Criterion Passed return true; } @@ -960,7 +837,6 @@ struct LambdaTableProducer { return 1.; } - // Get from CCDB auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, 1); if (!ccdbObj) { LOGF(warning, "CCDB OBJECT NOT FOUND"); @@ -970,7 +846,6 @@ struct LambdaTableProducer { float effCorrFact = 1., primFrac = 1.; float rap = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); - // Get Efficiency Factor if (cGetEffFact) { auto* objEff = ccdbObj->FindObject( Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str())); @@ -1021,26 +896,20 @@ struct LambdaTableProducer { template void fillLambdaQAHistos(C const& col, V const& v0, T const&) { - static constexpr std::array SubDir = { - "QA/Lambda/", "QA/AntiLambda/"}; + static constexpr std::array SubDir = {"QA/Lambda/", "QA/AntiLambda/"}; auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); float mass = (part == kLambda) ? v0.mLambda() : v0.mAntiLambda(); float e = RecoDecay::e(v0.px(), v0.py(), v0.pz(), mass); float gamma = e / mass; - float ctau = - v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; float gctau = ctau * gamma; - histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), - v0.qtarm()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), - v0.dcaV0daughters()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), - v0.dcapostopv()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_neg_to_PV"), - v0.dcanegtopv()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), v0.qtarm()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), v0.dcaV0daughters()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), v0.dcapostopv()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_neg_to_PV"), v0.dcanegtopv()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_to_PV"), v0.dcav0topv()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_cospa"), v0.v0cosPA()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_radius"), v0.v0radius()); @@ -1052,45 +921,31 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_eta"), negtrack.eta()); histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_phi"), postrack.phi()); histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_phi"), negtrack.phi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), - postrack.pt(), postrack.dcaXY()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), - negtrack.pt(), negtrack.dcaXY()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), - postrack.tpcInnerParam(), postrack.tpcSignal()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dEdx_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcSignal()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pr_vs_p"), - postrack.tpcInnerParam(), postrack.tpcNSigmaPr()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pr_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcNSigmaPr()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pi_vs_p"), - postrack.tpcInnerParam(), postrack.tpcNSigmaPi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), - negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), postrack.pt(), postrack.dcaXY()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), negtrack.pt(), negtrack.dcaXY()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), postrack.tpcInnerParam(), postrack.tpcSignal()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dEdx_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcSignal()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pr_vs_p"), postrack.tpcInnerParam(), postrack.tpcNSigmaPr()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pr_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPr()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_tpc_nsigma_pi_vs_p"), postrack.tpcInnerParam(), postrack.tpcNSigmaPi()); + histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); } template - void fillKinematicHists(float const& pt, float const& eta, float const& y, - float const& phi) + void fillKinematicHists(float const& pt, float const& eta, float const& y, float const& phi) { - static constexpr std::array SubDirRG = {"McRec/", - "McGen/"}; - static constexpr std::array SubDirPart = { - "Lambda/", "AntiLambda/"}; + static constexpr std::array SubDirRG = {"McRec/", "McGen/"}; + static constexpr std::array SubDirPart = {"Lambda/", "AntiLambda/"}; histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), pt); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), - eta); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), eta); histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hRap"), y); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), - phi); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phi); } template - void fillLambdaRecoTables(C const& collision, B const& bc, V const& v0tracks, - T const& tracks) + void fillLambdaRecoTables(C const& collision, B const& bc, V const& v0tracks, T const& tracks) { histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); @@ -1102,17 +957,16 @@ struct LambdaTableProducer { histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); - // Fill Collision Table - lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), - collision.posZ(), bc.timestamp()); + lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); - // initialize v0track objects ParticleType v0Type = kLambda; PrmScdType v0PrmScdType = kPrimary; float mass = 0., corr_fact = 1.; float prPx = 0., prPy = 0., prPz = 0.; float pt = 0., eta = 0., rap = 0., phi = 0.; + std::size_t nWritten = 0; for (auto const& v0 : v0tracks) { if constexpr (dmc == kMC) { @@ -1147,8 +1001,7 @@ struct LambdaTableProducer { phi = v0.phi(); if constexpr (dmc == kMC) { - histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), - v0.mcParticle().pdgCode(), v0.pt()); + histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), v0.mcParticle().pdgCode(), v0.pt()); if (cSelMCPSV0) { v0PrmScdType = isPrimaryV0(v0); } @@ -1159,8 +1012,7 @@ struct LambdaTableProducer { fillLambdaMothers(v0, tracks); } histos.fill(HIST("Tracks/h1f_tracks_info"), kPassTrueLambdaSel); - histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), - v0.mcParticle().pdgCode(), v0.pt()); + histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), v0.mcParticle().pdgCode(), v0.pt()); if (cRecoMomResoFlag) { auto mc = v0.template mcParticle_as(); pt = mc.pt(); @@ -1168,16 +1020,14 @@ struct LambdaTableProducer { rap = mc.y(); phi = mc.phi(); float y = cDoEtaAnalysis ? eta : rap; - if (!kinCutSelection(pt, std::abs(y), cMinV0Pt, cMaxV0Pt, - cMaxV0Rap)) { + if (!kinCutSelection(pt, std::abs(y), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { continue; } } } histos.fill(HIST("Tracks/h2f_armpod_after_sel"), v0.alpha(), v0.qtarm()); - corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0) - : getCorrectionFactors(v0); + corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0) : getCorrectionFactors(v0); if (v0Type == kLambda) { prPx = v0.pxpos(); @@ -1185,33 +1035,39 @@ struct LambdaTableProducer { prPz = v0.pzpos(); histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), - v0.phi()); + fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } else { prPx = v0.pxneg(); prPy = v0.pyneg(); prPz = v0.pzneg(); histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), - v0.phi()); + fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), v0.pz(), pt, eta, phi, rap, mass, prPx, prPy, prPz, v0.template posTrack_as().index(), - v0.template negTrack_as().index(), v0.v0cosPA(), - v0.dcaV0daughters(), (int8_t)v0Type, v0PrmScdType, + v0.template negTrack_as().index(), + v0.v0cosPA(), + v0.dcaV0daughters(), (int8_t)v0Type, + v0PrmScdType, corr_fact); + ++nWritten; + } + + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); + } + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); } } template - void fillLambdaMcGenTables(B const& bc, C const& mcCollision, - M const& mcParticles) + void fillLambdaMcGenTables(B const& bc, C const& mcCollision, M const& mcParticles) { - lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), - mcCollision.posY(), mcCollision.posZ(), + lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), bc.timestamp()); ParticleType v0Type = kLambda; @@ -1293,15 +1149,11 @@ struct LambdaTableProducer { histos.fill(HIST("McGen/Lambda/Pion/hEta"), vDauEta[piIdx]); histos.fill(HIST("McGen/Lambda/Pion/hRap"), vDauRap[piIdx]); histos.fill(HIST("McGen/Lambda/Pion/hPhi"), vDauPhi[piIdx]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), - mcpart.phi()); + fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } else { - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - daughterPDGs[prIdx]); - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - daughterPDGs[piIdx]); - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), - mcpart.pdgCode()); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[prIdx]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[piIdx]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), mcpart.pdgCode()); histos.fill(HIST("McGen/AntiLambda/Proton/hPt"), vDauPt[prIdx]); histos.fill(HIST("McGen/AntiLambda/Proton/hEta"), vDauEta[prIdx]); histos.fill(HIST("McGen/AntiLambda/Proton/hRap"), vDauRap[prIdx]); @@ -1310,8 +1162,7 @@ struct LambdaTableProducer { histos.fill(HIST("McGen/AntiLambda/Pion/hEta"), vDauEta[piIdx]); histos.fill(HIST("McGen/AntiLambda/Pion/hRap"), vDauRap[piIdx]); histos.fill(HIST("McGen/AntiLambda/Pion/hPhi"), vDauPhi[piIdx]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), - mcpart.y(), mcpart.phi()); + fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } lambdaMCGenTrackTable( @@ -1323,10 +1174,8 @@ struct LambdaTableProducer { } } - template - void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const&, - V const& V0s, T const& tracks, P const& mcParticles) + template + void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const&, V const& V0s, T const& tracks, P const& mcParticles) { int nRecCols = collisions.size(); if (nRecCols != 0) { @@ -1356,32 +1205,25 @@ struct LambdaTableProducer { } SliceCache cache; - Preslice> perCollision = - aod::v0data::collisionId; + Preslice> perCollision = aod::v0data::collisionId; - using CollisionsRun3 = soa::Join; - using CollisionsRun2 = - soa::Join; + using CollisionsRun3 = soa::Join; + using CollisionsRun2 = soa::Join; using Tracks = soa::Join; - using TracksRun2 = - soa::Join; + using TracksRun2 = soa::Join; using TracksMC = soa::Join; using TracksMCRun2 = soa::Join; using McV0Tracks = soa::Join; - void processDataRun3(CollisionsRun3::iterator const& collision, - aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, - Tracks const& tracks) + void processDataRun3(CollisionsRun3::iterator const& collision, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, Tracks const& tracks) { auto bc = collision.bc_as(); fillLambdaRecoTables(collision, bc, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", - true); + PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", true); void processMCRun3( aod::McCollisions::iterator const& mcCollision, @@ -1389,38 +1231,29 @@ struct LambdaTableProducer { aod::BCsWithTimestamps const& bcts, McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const& mcParticles) { - analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, - mcParticles); + analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, mcParticles); } - PROCESS_SWITCH(LambdaTableProducer, processMCRun3, - "Process for Run3 MC RecoGen", false); + PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); }; struct LambdaTracksExtProducer { Produces lambdaTrackExtTable; - Configurable cAcceptAllLambda{"cAcceptAllLambda", false, - "Accept all lambda (ignore sharing)"}; - Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, - "Reject lambda sharing daughters"}; - Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, - "Select lambda closest to PDG mass"}; - Configurable cSelLambdaTScore{"cSelLambdaTScore", false, - "Select lambda by t-score"}; + Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all lambda (ignore sharing)"}; + Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject lambda sharing daughters"}; + Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, "Select lambda closest to PDG mass"}; + Configurable cSelLambdaTScore{"cSelLambdaTScore", false, "Select lambda by t-score"}; Configurable cA{"cA", 0.6, "t-score weight: |mass - PDGmass|"}; Configurable cB{"cB", 0.6, "t-score weight: DCA daughters"}; Configurable cC{"cC", 0.6, "t-score weight: |cosPA - 1|"}; - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) { const AxisSpec axisMult(10, 0, 10); - const AxisSpec axisMass(120, 1.08, 1.20, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisMass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); const AxisSpec axisCPA(100, 0.995, 1.0, "cos(#theta_{PA})"); const AxisSpec axisDcaDau(75, 0., 1.5, "Daughter DCA (#sigma)"); const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); @@ -1430,48 +1263,36 @@ struct LambdaTracksExtProducer { histos.add("h1i_totantilambda_mult", "Multiplicity", kTH1I, {axisMult}); histos.add("h1i_lambda_mult", "Multiplicity", kTH1I, {axisMult}); histos.add("h1i_antilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", - kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaM", - "#rho_{2}^{Share} #bar{#Lambda}#bar{#Lambda}", kTH2D, - {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{Share} #bar{#Lambda}#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); histos.add("Reco/h1f_lambda_invmass", "M_{#Lambda}", kTH1F, {axisMass}); histos.add("Reco/h1f_lambda_cospa", "cos(PA)", kTH1F, {axisCPA}); histos.add("Reco/h1f_lambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); - histos.add("Reco/h1f_antilambda_invmass", "M_{#bar{#Lambda}}", kTH1F, - {axisMass}); + histos.add("Reco/h1f_antilambda_invmass", "M_{#bar{#Lambda}}", kTH1F, {axisMass}); histos.add("Reco/h1f_antilambda_cospa", "cos(PA)", kTH1F, {axisCPA}); - histos.add("Reco/h1f_antilambda_dcadau", "DCA daughters", kTH1F, - {axisDcaDau}); + histos.add("Reco/h1f_antilambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); histos.addClone("Reco/", "SharingDau/"); } template void fillHistos(T const& track) { - static constexpr std::array SubDir = {"Reco/", - "SharingDau/"}; + static constexpr std::array SubDir = {"Reco/", "SharingDau/"}; if (track.v0Type() == kLambda) { histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_invmass"), track.mass()); histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_dcadau"), track.dcaDau()); histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_cospa"), track.cosPA()); } else { - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_invmass"), - track.mass()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_dcadau"), - track.dcaDau()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_cospa"), - track.cosPA()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_invmass"), track.mass()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_dcadau"), track.dcaDau()); + histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_cospa"), track.cosPA()); } } - void process(aod::LambdaCollisions::iterator const&, - aod::LambdaTracks const& tracks) + void process(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) { int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; @@ -1487,50 +1308,32 @@ struct LambdaTracksExtProducer { ++nTotAntiLambda; } - tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + - (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); + tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); for (auto const& track : tracks) { if (lambda.index() == track.index()) { continue; } - if (lambda.posTrackId() == track.posTrackId() || - lambda.negTrackId() == track.negTrackId()) { + if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; if (lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && - track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); + histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && - track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), - lambda.eta() - track.eta(), - RecoDecay::constrainAngle((lambda.phi() - track.phi()), - -PIHalf)); + histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } - if (std::abs(lambda.mass() - MassLambda0) > - std::abs(track.mass() - MassLambda0)) { + if (std::abs(lambda.mass() - MassLambda0) > std::abs(track.mass() - MassLambda0)) { lambdaMinDeltaMassFlag = false; } - tTrack = (cA * std::abs(track.mass() - MassLambda0)) + - (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); + tTrack = (cA * std::abs(track.mass() - MassLambda0)) + (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); if (tLambda > tTrack) { lambdaMinTScoreFlag = false; } @@ -1543,9 +1346,7 @@ struct LambdaTracksExtProducer { fillHistos(lambda); } - if (cAcceptAllLambda || (cRejAllLambdaShaDau && !lambdaSharingDauFlag) || - (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) || - (cSelLambdaTScore && lambdaMinTScoreFlag)) { + if (cAcceptAllLambda || (cRejAllLambdaShaDau && !lambdaSharingDauFlag) || (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) || (cSelLambdaTScore && lambdaMinTScoreFlag)) { trueLambdaFlag = true; } @@ -1557,8 +1358,7 @@ struct LambdaTracksExtProducer { } } - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, - trueLambdaFlag); + lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag); } if (nTotLambda != 0) { @@ -1577,299 +1377,438 @@ struct LambdaTracksExtProducer { }; struct LambdaSpinPolarization { - // Table producer Produces lambdaMixEvtCol; Produces lambdaMixEvtTrk; Produces lambdaMixEvtMGCol; Produces lambdaMixEvtMGTrk; - Configurable cNPtBins{"cNPtBins", 30, "N pT bins"}; - Configurable cMinPt{"cMinPt", 0.5f, "pT min (GeV/c)"}; - Configurable cMaxPt{"cMaxPt", 4.5f, "pT max (GeV/c)"}; - Configurable cNRapBins{"cNRapBins", 10, "N rapidity bins"}; - Configurable cMinRap{"cMinRap", -0.5f, "Rapidity min"}; - Configurable cMaxRap{"cMaxRap", 0.5f, "Rapidity max"}; - Configurable cNPhiBins{"cNPhiBins", 36, "N phi bins"}; + Configurable cMassAccMin{"cMassAccMin", 1.0956f, "Accepted mass min (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassAccMax{"cMassAccMax", 1.1356f, "Accepted mass max (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassHistMin{"cMassHistMin", 1.0806f, "Mass axes min (GeV/c2), below cMassAccMin"}; + Configurable cMassHistMax{"cMassHistMax", 1.1806f, "Mass axes max (GeV/c2), above cMassAccMax"}; + Configurable cNMassBins{"cNMassBins", 200, "Mass axes N bins"}; + ConfigurableAxis axisMEReplacedMass{"axisMEReplacedMass", {VARIABLE_WIDTH, 1.0956, 1.1031, 1.1081, 1.1231, 1.1281, 1.1356}, "ME: mass of the replaced leg, binned by the analysis windows LSB | gap | Sig | gap | RSB (add edges, e.g. of the 3-sigma windows, to keep more window sets)"}; + ConfigurableAxis axisDeltaR{"axisDeltaR", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1, 3.5}, "DeltaR bins (last bin: outside the analysis windows)"}; Configurable cNBinsCosTS{"cNBinsCosTS", 10, "N costheta* bins"}; - Configurable cNBinsDeltaR{"cNBinsDeltaR", 20, "N DeltaR bins"}; - - Configurable cMassHistMin{"cMassHistMin", 1.08f, - "Mass histogram min (GeV/c2)"}; - Configurable cMassHistMax{"cMassHistMax", 1.20f, - "Mass histogram max (GeV/c2)"}; - Configurable cNMassBins{"cNMassBins", 120, "Mass histogram N bins"}; - - Configurable cSigMinLambda{"cSigMinLambda", 1.108f, - "Signal region min (GeV/c2)"}; - Configurable cSigMaxLambda{"cSigMaxLambda", 1.123f, - "Signal region max (GeV/c2)"}; - Configurable cSbLeftMin{"cSbLeftMin", 1.080f, - "Left sideband min (GeV/c2)"}; - Configurable cSbLeftMax{"cSbLeftMax", 1.100f, - "Left sideband max (GeV/c2)"}; - Configurable cSbRightMin{"cSbRightMin", 1.135f, - "Right sideband min (GeV/c2)"}; - Configurable cSbRightMax{"cSbRightMax", 1.155f, - "Right sideband max (GeV/c2)"}; - - Configurable cInvBoostFlag{"cInvBoostFlag", true, "Inverse boost flag"}; - Configurable cDoAtlasMethod{"cDoAtlasMethod", false, - "Fill pair-boost (ATLAS) histograms"}; - Configurable cDoStarMethod{"cDoStarMethod", true, - "Fill lab-boost (STAR) histograms"}; - Configurable mixingParameter{"mixingParameter", 5, "ME pool depth"}; - Configurable cMEMode{"cMEMode", 1, - "ME mode: 0=standard, 1=kinematicConstrained"}; - - ConfigurableAxis cMultBins{"cMultBins", {VARIABLE_WIDTH, 0.f, 10.f, 30.f, 50.f, 80.f, 100.f}, "Multiplicity bins"}; - ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 30, 50, 100}, "ME centrality bins"}; - ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -7, -3, 0, 3, 7}, "ME vtxZ bins"}; + + Configurable cMEPoolDepth{"cMEPoolDepth", 100000, "Rolling ME pool: depth in donor events per (centrality, Vz) bin"}; + Configurable cMEPoolMinEvents{"cMEPoolMinEvents", 20, "Rolling ME pool: events a pool must hold before its bin is mixed (warm-up)"}; + Configurable cMEPoolMinCand{"cMEPoolMinCand", 2, "Rolling ME pool: only events with at least this many accepted candidates donate"}; + ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 20, 40, 100}, "ME pool centrality bins"}; + ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -10., -7., -4., -2., 0, 2., 4., 7., 10.}, "ME pool vtxZ bins"}; Configurable cMaxDeltaPt{"cMaxDeltaPt", 0.1f, "Kinematic ME: max |deltaPt(SE)-deltaPt(ME)| (GeV/c)"}; - Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.1f, "Kinematic ME: max |deltaPhi(SE)-deltaPhi(ME)| (rad)"}; - Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.1f, "Kinematic ME: max |deltaRap(SE)-deltaRap(ME)|"}; + Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.02f, "Kinematic ME: max |deltaPhi(SE)-deltaPhi(ME)| (rad)"}; + Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.03f, "Kinematic ME: max |deltaRap(SE)-deltaRap(ME)|"}; + Configurable cFillSEInMixing{"cFillSEInMixing", true, "Kinematic ME: fill every same-event pair the mixing processes, matched or not (SEproc)"}; + + Configurable cFillKinWSpectra{"cFillKinWSpectra", true, "Kinematic ME: fill the leg-2 spectra of SEproc and ME (weight-map input, closure)"}; + Configurable cMinPt{"cMinPt", 0.6f, "Leg spectra and ME pool grid: pT min (GeV/c)"}; + Configurable cMaxPt{"cMaxPt", 3.0f, "Leg spectra and ME pool grid: pT max (GeV/c)"}; + Configurable cMinRap{"cMinRap", -0.5f, "Leg spectra and ME pool grid: rapidity min"}; + Configurable cMaxRap{"cMaxRap", 0.5f, "Leg spectra and ME pool grid: rapidity max"}; + Configurable cNKinWPtBins{"cNKinWPtBins", 0, "Leg spectra: pT bins (0: no wider than cMaxDeltaPt)"}; + Configurable cNKinWRapBins{"cNKinWRapBins", 0, "Leg spectra: rapidity bins (0: no wider than cMaxDeltaRap)"}; + Configurable cNKinWPhiBins{"cNKinWPhiBins", 0, "Leg spectra: phi bins (0: no wider than cMaxDeltaPhi)"}; + Configurable cApplyKinWeights{"cApplyKinWeights", false, "Kinematic ME: weight the leg-2 replacements with the maps from CCDB"}; + Configurable cKinWeightCcdbUrl{"cKinWeightCcdbUrl", "http://alice-ccdb.cern.ch", "Kinematic weights: CCDB url"}; + Configurable cKinWeightCcdbPath{"cKinWeightCcdbPath", "", "Kinematic weights: CCDB path of a TList with hKinW_ (axes of KinW/*/hLeg2_)"}; + Configurable cKinWeightCcdbTimestamp{"cKinWeightCcdbTimestamp", -1, "Kinematic weights: CCDB timestamp (-1: timestamp of the first mixed collision)"}; + + Configurable cFillClosePairQA{"cFillClosePairQA", true, "Close-pair QA of the same-event pairs: SE (processDataReco) and SEproc (processDataRecoMixed)"}; + Configurable cClosePairMassWindow{"cClosePairMassWindow", 0.004f, "Close-pair QA: both legs within |m - m_Lambda| < this (GeV/c2); <= 0: all pairs"}; + ConfigurableAxis axisClosePair{"axisClosePair", {100, -0.15, 0.15}, "Close-pair QA: bins of the daughter Delta eta, Delta y and Delta phi"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service ccdb{}; - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; + uint64_t pairOrderSeed = 0; + std::array, 4> kinWeightMaps{}; + std::vector legAxesRef{}; + TAxis replacedMassAxis{}; + bool kinWeightsLoaded = false; - float cent = 0.; struct PoolTrack { - float _px, _py, _pz, _pt, _rap, _phi, _mass; - float _prPx, _prPy, _prPz; - [[nodiscard]] float px() const { return _px; } - [[nodiscard]] float py() const { return _py; } - [[nodiscard]] float pz() const { return _pz; } - [[nodiscard]] float pt() const { return _pt; } - [[nodiscard]] float rap() const { return _rap; } - [[nodiscard]] float phi() const { return _phi; } - [[nodiscard]] float mass() const { return _mass; } - [[nodiscard]] float prPx() const { return _prPx; } - [[nodiscard]] float prPy() const { return _prPy; } - [[nodiscard]] float prPz() const { return _prPz; } + float mPx, mPy, mPz, mPt, mRap, mPhi, mMass; + float mPrPx, mPrPy, mPrPz; + int8_t mSpecies; + [[nodiscard]] float px() const { return mPx; } + [[nodiscard]] float py() const { return mPy; } + [[nodiscard]] float pz() const { return mPz; } + [[nodiscard]] float pt() const { return mPt; } + [[nodiscard]] float rap() const { return mRap; } + [[nodiscard]] float phi() const { return mPhi; } + [[nodiscard]] float mass() const { return mMass; } + [[nodiscard]] float prPx() const { return mPrPx; } + [[nodiscard]] float prPy() const { return mPrPy; } + [[nodiscard]] float prPz() const { return mPrPz; } + [[nodiscard]] int8_t species() const { return mSpecies; } }; template PoolTrack toPoolTrack(T const& trk) { - return PoolTrack{trk.px(), trk.py(), trk.pz(), trk.pt(), trk.rap(), - trk.phi(), trk.mass(), trk.prPx(), trk.prPy(), trk.prPz()}; + return PoolTrack{trk.px(), trk.py(), trk.pz(), trk.pt(), trk.rap(), trk.phi(), trk.mass(), trk.prPx(), trk.prPy(), trk.prPz(), static_cast(trk.v0Type())}; } + class RollingPool + { + public: + struct Grid { + float ptMin = 0.f; + float rapMin = 0.f; + float cellPt = 1.f; + float cellRap = 1.f; + float cellPhi = TwoPI; + int nPt = 1; + int nRap = 1; + int nPhi = 1; + }; + + RollingPool() = default; + RollingPool(Grid const& g, int depth) : grid(g), maxEvents(depth) {} + + [[nodiscard]] std::size_t nEvents() const { return nPerEvent.size(); } + + void push(std::vector const& event) + { + for (auto const& cand : event) { + const int64_t seq = seqBase + static_cast(slots.size()); + slots.push_back(Slot{cand, NoSeq}); + auto [it, isNew] = cells[cand.species()].try_emplace(cellKey(cand), Cell{seq, seq}); + if (!isNew) { + slot(it->second.tail).next = seq; + it->second.tail = seq; + } + } + nPerEvent.push_back(static_cast(event.size())); + while (static_cast(nPerEvent.size()) > maxEvents) { + const int n = nPerEvent.front(); + for (int i = 0; i < n; ++i) { + retireOldest(); + } + nPerEvent.pop_front(); + } + } + + template + void findMatches(PoolTrack const& leg, F const& isMatch, std::vector& out) const + { + out.clear(); + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(leg, iPt, iRap, iPhi); + std::array phiCells{}; + int nPhiCells = 0; + for (int d = -1; d <= 1; ++d) { + const int k = ((iPhi + d) % grid.nPhi + grid.nPhi) % grid.nPhi; + if (std::find(phiCells.begin(), phiCells.begin() + nPhiCells, k) == phiCells.begin() + nPhiCells) { + phiCells[nPhiCells++] = k; + } + } + auto const& speciesCells = cells[leg.species()]; + for (int jPt = std::max(iPt - 1, 0); jPt <= std::min(iPt + 1, grid.nPt - 1); ++jPt) { + for (int jRap = std::max(iRap - 1, 0); jRap <= std::min(iRap + 1, grid.nRap - 1); ++jRap) { + for (int ip = 0; ip < nPhiCells; ++ip) { + const auto it = speciesCells.find(key(jPt, jRap, phiCells[ip])); + if (it == speciesCells.end()) { + continue; + } + for (int64_t seq = it->second.head; seq != NoSeq;) { + Slot const& sl = slot(seq); + if (isMatch(sl.cand)) { + out.push_back(&sl.cand); + } + seq = sl.next; + } + } + } + } + } + + private: + static constexpr int64_t NoSeq = -1; + struct Slot { + PoolTrack cand; + int64_t next; + }; + struct Cell { + int64_t head; + int64_t tail; + }; + + Slot& slot(int64_t seq) { return slots[static_cast(seq - seqBase)]; } + [[nodiscard]] Slot const& slot(int64_t seq) const { return slots[static_cast(seq - seqBase)]; } + + void retireOldest() + { + Slot const& old = slots.front(); + auto& speciesCells = cells[old.cand.species()]; + auto it = speciesCells.find(cellKey(old.cand)); + if (it == speciesCells.end() || it->second.head != seqBase) { + LOGF(fatal, "RollingPool: grid index out of step with the candidate queue"); + return; + } + if (old.next == NoSeq) { + speciesCells.erase(it); + } else { + it->second.head = old.next; + } + slots.pop_front(); + ++seqBase; + } + + [[nodiscard]] int64_t key(int iPt, int iRap, int iPhi) const + { + return (static_cast(iPt) * grid.nRap + iRap) * grid.nPhi + iPhi; + } + + void cellCoords(PoolTrack const& c, int& iPt, int& iRap, int& iPhi) const + { + iPt = std::clamp(static_cast(std::floor((c.pt() - grid.ptMin) / grid.cellPt)), 0, grid.nPt - 1); + iRap = std::clamp(static_cast(std::floor((c.rap() - grid.rapMin) / grid.cellRap)), 0, grid.nRap - 1); + iPhi = std::clamp(static_cast(std::floor(RecoDecay::constrainAngle(c.phi(), 0.f) / grid.cellPhi)), 0, grid.nPhi - 1); + } + + [[nodiscard]] int64_t cellKey(PoolTrack const& c) const + { + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(c, iPt, iRap, iPhi); + return key(iPt, iRap, iPhi); + } + + Grid grid{}; + int maxEvents = 0; + std::deque slots{}; + std::deque nPerEvent{}; + int64_t seqBase = 0; + std::array, 2> cells{}; + }; + + std::vector mePools{}; + std::vector mePoolsGen{}; + TAxis mePoolAxisCent{}; + TAxis mePoolAxisVz{}; + void init(InitContext const&) { - const AxisSpec axisCheck(1, 0, 1, ""); - const AxisSpec axisCent(cMultBins, "FT0M (%)"); - - const AxisSpec axisMass(cNMassBins, cMassHistMin, cMassHistMax, - "M_{#Lambda} (GeV/#it{c}^{2})"); - const AxisSpec axisPt(cNPtBins, cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); - const AxisSpec axisDRap(2 * cNRapBins, cMinRap - cMaxRap, cMaxRap - cMinRap, - "#Deltay"); - const AxisSpec axisDPhi(cNPhiBins, -PI, PI, "#Delta#varphi"); + const AxisSpec axisM1(cNMassBins, cMassHistMin, cMassHistMax, "M_{1} (GeV/#it{c}^{2})"); + const AxisSpec axisM2(cNMassBins, cMassHistMin, cMassHistMax, "M_{2} (GeV/#it{c}^{2})"); + const AxisSpec axisDR(axisDeltaR, "#DeltaR"); const AxisSpec axisCosTS(cNBinsCosTS, -1, 1, "cos(#theta*)"); - const AxisSpec axisDR(cNBinsDeltaR, 0, 3.5, "#DeltaR"); - const AxisSpec axisPosZ(200, -10, 10, "V_{z} (cm)"); - const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); + const AxisSpec axisReplacedMass(axisMEReplacedMass, "M_{replaced leg} (GeV/#it{c}^{2})"); + const std::vector pairAxes = {axisM1, axisM2, axisDR, axisCosTS}; + const std::vector meAxes = {axisM1, axisM2, axisDR, axisCosTS, axisReplacedMass}; + + auto nBins = [](int configured, float range, float window) { + if (configured > 0) { + return configured; + } + return window > 0.f ? static_cast(std::ceil(range / window)) : 1; + }; + const AxisSpec axisLegPt(nBins(cNKinWPtBins, cMaxPt - cMinPt, cMaxDeltaPt), cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); + const AxisSpec axisLegRap(nBins(cNKinWRapBins, cMaxRap - cMinRap, cMaxDeltaRap), cMinRap, cMaxRap, "y"); + const AxisSpec axisLegPhi(nBins(cNKinWPhiBins, TwoPI, cMaxDeltaPhi), 0., TwoPI, "#varphi (rad)"); + const std::vector legAxes = {axisLegPt, axisLegRap, axisLegPhi, axisReplacedMass}; + legAxesRef.clear(); + for (auto const& spec : legAxes) { + const std::vector edges = axisEdges(spec); + legAxesRef.emplace_back(static_cast(edges.size()) - 1, edges.data()); + } + replacedMassAxis = legAxesRef.back(); + + if (!(cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value)) { + LOGF(fatal, "The mass axes [%.4f, %.4f] must be wider than the accepted mass window [%.4f, %.4f]", cMassHistMin.value, cMassHistMax.value, cMassAccMin.value, cMassAccMax.value); + } + std::vector massEdges = axisEdges(axisReplacedMass); + massEdges.push_back(cMassAccMin.value); + massEdges.push_back(cMassAccMax.value); + for (auto const& edge : massEdges) { + if (!isOnMassBinEdge(edge)) { + LOGF(warning, "Mass %.5f (accepted window or axisMEReplacedMass) is not a bin edge of the mass axes [%.4f, %.4f] / %d", edge, cMassHistMin.value, cMassHistMax.value, cNMassBins.value); + } + } + + if (doprocessDataRecoMixed || doprocessMcGenMixed) { + const std::vector centEdges = axisEdges(AxisSpec(axisCentME)); + const std::vector vzEdges = axisEdges(AxisSpec(axisVtxZME)); + mePoolAxisCent.Set(static_cast(centEdges.size()) - 1, centEdges.data()); + mePoolAxisVz.Set(static_cast(vzEdges.size()) - 1, vzEdges.data()); + + static constexpr std::array PoolSizeSteps = {1., 2., 5.}; + static constexpr double PoolSizeDecade = 10.; + const double depth = std::max(1, cMEPoolDepth.value); + std::vector poolSizeEdges = {0.}; + for (double decade = 1.; decade < depth; decade *= PoolSizeDecade) { + for (auto const& step : PoolSizeSteps) { + if (step * decade < depth) { + poolSizeEdges.push_back(step * decade); + } + } + } + poolSizeEdges.push_back(depth); + const AxisSpec axisPoolSize(poolSizeEdges, "events in the pool"); + + std::vector mixQaDirs; + if (doprocessDataRecoMixed) { + mixQaDirs.emplace_back("QA/ME/"); + } + if (doprocessMcGenMixed) { + mixQaDirs.emplace_back("McGen/QA/ME/"); + } + for (auto const& dir : mixQaDirs) { + histos.add((dir + "hMixedCentVz").c_str(), "events mixed;cent (%);V_{z} (cm)", kTH2F, {axisCentME, axisVtxZME}); + auto hStatus = histos.add((dir + "hEventStatus").c_str(), "rolling-pool mixing;;collisions", kTH1D, {{kNMEEventStatus - 1, 0.5, static_cast(kNMEEventStatus) - 0.5}}); + hStatus->GetXaxis()->SetBinLabel(kMEEventSeen, "seen"); + hStatus->GetXaxis()->SetBinLabel(kMEEventOutsidePools, "outside the pool bins"); + hStatus->GetXaxis()->SetBinLabel(kMEEventWarmUp, "pairs, pool in warm-up"); + hStatus->GetXaxis()->SetBinLabel(kMEEventMixed, "mixed"); + hStatus->GetXaxis()->SetBinLabel(kMEEventDonated, "donated to the pool"); + histos.add((dir + "hPoolEventsAtMixing").c_str(), "pool size when a collision is mixed;events in the pool;collisions", kTH1F, {axisPoolSize}); + histos.add((dir + "hMatchesLeg2").c_str(), "matches per replaced leg 2;N matches;pair type", kTH2F, {{100, -0.5, 99.5}, {4, -0.5, 3.5}}); + histos.add((dir + "hDeltaRShift").c_str(), "ME entries are filed at the #DeltaR of their seed;#DeltaR_{seed};#DeltaR_{mixed pair} - #DeltaR_{seed}", kTH2F, {{35, 0., 3.5}, {100, -0.2, 0.2}}); + histos.add((dir + "hLambdaMultVsCent").c_str(), "ME #Lambda mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + histos.add((dir + "hAntiLambdaMultVsCent").c_str(), "ME #bar{#Lambda} mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + } + + if (cMaxDeltaPt.value <= 0.f || cMaxDeltaRap.value <= 0.f || cMaxDeltaPhi.value <= 0.f) { + LOGF(fatal, "Rolling ME pool: the matching windows cMaxDeltaPt, cMaxDeltaRap and cMaxDeltaPhi must be positive"); + } + RollingPool::Grid grid; + grid.ptMin = cMinPt.value; + grid.rapMin = cMinRap.value; + grid.cellPt = cMaxDeltaPt.value; + grid.cellRap = cMaxDeltaRap.value; + grid.nPt = std::max(1, static_cast(std::ceil((cMaxPt.value - cMinPt.value) / grid.cellPt))); + grid.nRap = std::max(1, static_cast(std::ceil((cMaxRap.value - cMinRap.value) / grid.cellRap))); + grid.nPhi = std::max(1, static_cast(std::floor(TwoPI / cMaxDeltaPhi.value))); + grid.cellPhi = TwoPI / static_cast(grid.nPhi); + const auto nPools = static_cast(mePoolAxisCent.GetNbins() * mePoolAxisVz.GetNbins()); + if (doprocessDataRecoMixed) { + mePools.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + if (doprocessMcGenMixed) { + mePoolsGen.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + LOGF(info, "Rolling ME pools: %d (centrality) x %d (Vz) bins, depth %d donor events, grid %d x %d x %d (pT, y, phi)", + mePoolAxisCent.GetNbins(), mePoolAxisVz.GetNbins(), cMEPoolDepth.value, grid.nPt, grid.nRap, grid.nPhi); + } + + std::vector evDirs; + if (doprocessDataReco || doprocessDataRecoMixed) { + evDirs.emplace_back(""); + } + if (doprocessMcGen || doprocessMcGenMixed) { + evDirs.emplace_back("McGen/"); + } + const AxisSpec axisCandMass(cNMassBins, cMassHistMin, cMassHistMax, "M_{p#pi} (GeV/#it{c}^{2})"); + for (auto const& dir : evDirs) { + auto hCount = histos.add((dir + "Events/hEventCount").c_str(), "collisions of the pair analysis;;collisions", kTH1D, {{kNPairEventCount - 1, 0.5, static_cast(kNPairEventCount) - 0.5}}); + hCount->GetXaxis()->SetBinLabel(kPeAll, "all"); + hCount->GetXaxis()->SetBinLabel(kPeOneCand, "#geq 1 accepted #Lambda/#bar{#Lambda}"); + hCount->GetXaxis()->SetBinLabel(kPeTwoCand, "#geq 2 accepted (enter pairs)"); + hCount->GetXaxis()->SetBinLabel(kPeUnlikeSign, "#geq 1 #Lambda#bar{#Lambda} pair"); + hCount->GetXaxis()->SetBinLabel(kPeLambdaLambda, "#geq 1 #Lambda#Lambda pair"); + hCount->GetXaxis()->SetBinLabel(kPeAntiLambdaAntiLambda, "#geq 1 #bar{#Lambda}#bar{#Lambda} pair"); + histos.add((dir + "Events/hNCandAccepted").c_str(), "accepted candidates per collision;N_{#Lambda};N_{#bar{#Lambda}}", kTH2D, {{21, -0.5, 20.5}, {21, -0.5, 20.5}}); + histos.add((dir + "Events/hCentPairEvents").c_str(), "collisions with a pair;cent (%);collisions", kTH1D, {{100, 0., 100.}}); + for (auto const& species : {"Lambda/", "AntiLambda/"}) { + const std::string cand = dir + "QA/PairCand/" + species; + histos.add((cand + "hMassVsPt").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisCandMass, axisLegPt}); + histos.add((cand + "hRapVsPhi").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisLegRap, axisLegPhi}); + } + } + + static constexpr std::array PairTags = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + for (auto const& tag : PairTags) { + const std::string pair{tag}; + if (doprocessDataReco) { + histos.add(("SE/hPair_" + pair).c_str(), ("SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed) { + histos.add(("ME/hPair_" + pair).c_str(), ("ME " + pair).c_str(), kTHnSparseD, meAxes, true); + } + if (doprocessDataRecoMixed && cFillSEInMixing) { + histos.add(("SEproc/hPair_" + pair).c_str(), ("SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed && cFillKinWSpectra) { + histos.add(("KinW/SEproc/hLeg2_" + pair).c_str(), ("leg 2 of SEproc " + pair).c_str(), kTHnSparseD, legAxes); + histos.add(("KinW/ME/hLeg2_" + pair).c_str(), ("candidates replacing leg 2 " + pair).c_str(), kTHnSparseD, legAxes, true); + } + if (doprocessMcGen) { + histos.add(("McGen/SE/hPair_" + pair).c_str(), ("MC generated SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessMcGenMixed) { + histos.add(("McGen/ME/hPair_" + pair).c_str(), ("MC generated ME " + pair).c_str(), kTHnSparseD, meAxes, true); + } + if (doprocessMcGenMixed && cFillSEInMixing) { + histos.add(("McGen/SEproc/hPair_" + pair).c_str(), ("MC generated SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + } + + if (cFillClosePairQA && (doprocessDataReco || doprocessDataRecoMixed)) { + const AxisSpec axisCPdEta(axisClosePair, "#Delta#eta"); + const AxisSpec axisCPdY(axisClosePair, "#Deltay"); + const AxisSpec axisCPdPhi(axisClosePair, "#Delta#varphi (rad)"); + std::vector> cpDirs; + if (doprocessDataReco) { + cpDirs.emplace_back("QA/ClosePair/", ""); + } + if (doprocessDataRecoMixed) { + cpDirs.emplace_back("QA/ClosePairSEproc/", "SEproc, "); + } + for (auto const& [dir, tag] : cpDirs) { + for (auto const& cls : {"LamLam", "ALamALam", "UnlikeSign"}) { + for (auto const& combo : {"pp", "pipi", "p1pi2", "pi1p2"}) { + const std::string name = dir + cls + "/h_" + combo; + const std::string title = tag + cls + ", daughters " + combo; + histos.add((name + "_dEta").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdEta, axisCPdPhi}); + histos.add((name + "_dY").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdY, axisCPdPhi}); + } + } + } + } - histos.add("QA/ME/hPoolCentVz", "ME pool;cent (%);V_{z}", kTH2F, - {axisCentME, axisVtxZME}); - histos.add("QA/ME/hLambdaMultVsCent", "ME #Lambda mult;cent;N", kTH2F, - {axisCentME, {50, 0, 50}}); - histos.add("QA/ME/hAntiLambdaMultVsCent", "ME #bar{#Lambda} mult;cent;N", - kTH2F, {axisCentME, {50, 0, 50}}); - - const std::vector massSparseAxes = { - axisCent, axisMass, axisMass, axisPt, axisPt, - axisDRap, axisDPhi, axisDR, axisCosTS}; - - histos.add("SE/Reco/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda inclusive", kTHnSparseF, - massSparseAxes); - histos.add("SE/Reco/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} inclusive", kTHnSparseF, - massSparseAxes); - - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda Sig#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSigSB/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} Sig#timesSB", kTHnSparseF, - massSparseAxes); - - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaPLaM", - "M_{inv}: #Lambda#bar{#Lambda} SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaMLaP", - "M_{inv}: #bar{#Lambda}#Lambda SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaPLaP", - "M_{inv}: #Lambda#Lambda SB#timesSB", kTHnSparseF, - massSparseAxes); - histos.add("SE/RecoBkgSBSB/Star/h2f_n2_mass_LaMLaM", - "M_{inv}: #bar{#Lambda}#bar{#Lambda} SB#timesSB", kTHnSparseF, - massSparseAxes); - - histos.addClone("SE/Reco/Star/", "SE/Reco/Atlas/"); - histos.addClone("SE/RecoBkgSigSB/Star/", "SE/RecoBkgSigSB/Atlas/"); - histos.addClone("SE/RecoBkgSBSB/Star/", "SE/RecoBkgSBSB/Atlas/"); - - histos.addClone("SE/Reco/", "ME/Reco/"); - histos.addClone("SE/RecoBkgSigSB/", "ME/RecoBkgSigSB/"); - histos.addClone("SE/RecoBkgSBSB/", "ME/RecoBkgSBSB/"); - - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Star/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("SE/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Star/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Star]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_dltaR_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDR, axisCosTS}); - - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaM", - "#rho_{2} #Lambda#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaP", - "#rho_{2} #bar{#Lambda}#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaPLaP", - "#rho_{2} #Lambda#Lambda [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("ME/RecoCorr/Atlas/h2f_n2_ctheta_LaMLaM", - "#rho_{2} #bar{#Lambda}#bar{#Lambda} [Atlas]", kTHnSparseF, - {axisCent, axisDRap, axisDPhi, axisCosTS}); - - histos.addClone("SE/RecoCorr/Star/", "SE/RecoCorrBkgSigSB/Star/"); - histos.addClone("SE/RecoCorr/Atlas/", "SE/RecoCorrBkgSigSB/Atlas/"); - histos.addClone("SE/RecoCorr/Star/", "SE/RecoCorrBkgSBSB/Star/"); - histos.addClone("SE/RecoCorr/Atlas/", "SE/RecoCorrBkgSBSB/Atlas/"); - - histos.addClone("ME/RecoCorr/Star/", "ME/RecoCorrBkgSigSB/Star/"); - histos.addClone("ME/RecoCorr/Atlas/", "ME/RecoCorrBkgSigSB/Atlas/"); - histos.addClone("ME/RecoCorr/Star/", "ME/RecoCorrBkgSBSB/Star/"); - histos.addClone("ME/RecoCorr/Atlas/", "ME/RecoCorrBkgSBSB/Atlas/"); + if (cApplyKinWeights) { + if (cKinWeightCcdbPath.value.empty()) { + LOGF(fatal, "cApplyKinWeights is set but cKinWeightCcdbPath is empty"); + } + ccdb->setURL(cKinWeightCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + } } - bool isSignal(float m) const + bool isAcceptedMass(float m) const { - return (m >= cSigMinLambda.value && m <= cSigMaxLambda.value); + return m >= cMassAccMin.value && m < cMassAccMax.value; } - bool isSideband(float m) const + + static std::vector axisEdges(AxisSpec const& axis) { - return ((m >= cSbLeftMin.value && m <= cSbLeftMax.value) || - (m >= cSbRightMin.value && m <= cSbRightMax.value)); + if (!axis.nBins.has_value()) { + return axis.binEdges; + } + const int n = axis.nBins.value(); + std::vector edges(n + 1); + for (int i = 0; i <= n; ++i) { + edges[i] = axis.binEdges[0] + (axis.binEdges[1] - axis.binEdges[0]) * i / n; + } + return edges; } - bool isInclusive(float m) const + + static constexpr double MassEdgeTolerance = 1e-3; + [[nodiscard]] bool isOnMassBinEdge(double m) const { - return (m >= cMassHistMin.value && m <= cMassHistMax.value); + const double width = (static_cast(cMassHistMax.value) - cMassHistMin.value) / cNMassBins.value; + const double k = (m - cMassHistMin.value) / width; + return std::abs(k - std::round(k)) < MassEdgeTolerance; } - void getBoostVector(std::array const& p, std::array& v, - bool inverseBoostFlag = true) + void getBoostVector(std::array const& p, std::array& v) { int n = p.size(); for (int i = 0; i < n - 1; ++i) { - if (inverseBoostFlag) { - v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } else { - v[i] = p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } + v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); } } @@ -1885,9 +1824,7 @@ struct LambdaSpinPolarization { p[2] = p[2] + gam2 * bp * b[2] + gam * b[2] * e; } - // cos of the opening angle between the two proton three-momenta. - static float cosOpeningAngle(std::array const& a, - std::array const& b) + static float cosOpeningAngle(std::array const& a, std::array const& b) { std::array n1 = {a[0], a[1], a[2]}, n2 = {b[0], b[1], b[2]}; return RecoDecay::dotProd(n1, n2) / @@ -1895,563 +1832,492 @@ struct LambdaSpinPolarization { RecoDecay::sqrtSumOfSquares(n2[0], n2[1], n2[2])); } - float cosThetaStarAtlas(std::array const& l1, - std::array const& l2, - std::array const& pr1, - std::array const& pr2) + float cosThetaStarAtlas(std::array const& l1, std::array const& l2, std::array const& pr1, std::array const& pr2) { auto l1a = l1; auto l2a = l2; auto pr1a = pr1; auto pr2a = pr2; - const float mPair = - RecoDecay::m(std::array{std::array{l1a[0], l1a[1], l1a[2]}, - std::array{l2a[0], l2a[1], l2a[2]}}, - std::array{l1a[3], l2a[3]}); - std::array llpair = {l1a[0] + l2a[0], l1a[1] + l2a[1], - l1a[2] + l2a[2], mPair}; + const float mPair = RecoDecay::m(std::array{std::array{l1a[0], l1a[1], l1a[2]}, std::array{l2a[0], l2a[1], l2a[2]}}, std::array{l1a[3], l2a[3]}); + std::array llpair = {l1a[0] + l2a[0], l1a[1] + l2a[1], l1a[2] + l2a[2], mPair}; std::array vPair{}; - getBoostVector(llpair, vPair, cInvBoostFlag); + getBoostVector(llpair, vPair); boost(l1a, vPair); boost(l2a, vPair); boost(pr1a, vPair); boost(pr2a, vPair); std::array v1p{}, v2p{}; - getBoostVector(l1a, v1p, cInvBoostFlag); - getBoostVector(l2a, v2p, cInvBoostFlag); + getBoostVector(l1a, v1p); + getBoostVector(l2a, v2p); boost(pr1a, v1p); boost(pr2a, v2p); return cosOpeningAngle(pr1a, pr2a); } - float cosThetaStarStar(std::array const& l1, - std::array const& l2, - std::array const& pr1, - std::array const& pr2) + static float deltaR(PoolTrack const& p1, PoolTrack const& p2) { - auto pr1s = pr1; - auto pr2s = pr2; - std::array v1lab{}, v2lab{}; - getBoostVector(l1, v1lab, cInvBoostFlag); - getBoostVector(l2, v2lab, cInvBoostFlag); - boost(pr1s, v1lab); - boost(pr2s, v2lab); - return cosOpeningAngle(pr1s, pr2s); + const float drap = p1.rap() - p2.rap(); + const float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); + return std::sqrt(drap * drap + dphi * dphi); } - template - void fillPairHistos(U const& p1, U const& p2) + template + void fillPair(PoolTrack const& p1, PoolTrack const& p2, float dR, float w, float mReplaced = 0.f) { - static constexpr std::array SubDir = { - "LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - - constexpr bool IsSigSB = - (part_pair == kLambdaSBAntiLambda || part_pair == kAntiLambdaSBLambda || - part_pair == kLambdaSBLambda || part_pair == kAntiLambdaSBAntiLambda); - constexpr bool IsSBSB = - (part_pair == kLambdaSBSBAntiLambda || - part_pair == kAntiLambdaSBSBLambda || part_pair == kLambdaSBSBLambda || - part_pair == kAntiLambdaSBSBAntiLambda); - - constexpr int Idx = IsSigSB ? static_cast(part_pair) - 4 - : IsSBSB ? static_cast(part_pair) - 8 - : static_cast(part_pair); - - float drap = p1.rap() - p2.rap(); - float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); - float dR = std::sqrt(drap * drap + dphi * dphi); - - std::array l1 = {p1.px(), p1.py(), p1.pz(), MassLambda0}; - std::array l2 = {p2.px(), p2.py(), p2.pz(), MassLambda0}; + static constexpr std::array TierDir = {"SE/hPair_", "SEproc/hPair_", "ME/hPair_", + "McGen/SE/hPair_", "McGen/SEproc/hPair_", "McGen/ME/hPair_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + + std::array l1 = {p1.px(), p1.py(), p1.pz(), p1.mass()}; + std::array l2 = {p2.px(), p2.py(), p2.pz(), p2.mass()}; std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; - if (cDoAtlasMethod) { - float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("SE/Reco/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta); - histos.fill(HIST("SE/RecoCorr/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorr/Atlas/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta); - } else if constexpr (IsSigSB) { - histos.fill(HIST("SE/RecoBkgSigSB/Atlas/h2f_n2_mass_") + - HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } else { - histos.fill( - HIST("SE/RecoBkgSBSB/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } - } - - if (cDoStarMethod) { - float ctheta = cosThetaStarStar(l1, l2, pr1, pr2); - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("SE/Reco/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, - ctheta); - histos.fill(HIST("SE/RecoCorr/Star/h2f_n2_ctheta_") + HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorr/Star/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta); - } else if constexpr (IsSigSB) { - histos.fill( - HIST("SE/RecoBkgSigSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSigSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } else { - histos.fill( - HIST("SE/RecoBkgSBSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta); - histos.fill(HIST("SE/RecoCorrBkgSBSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta); - } + const float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); + if constexpr (tier == kMixedEvent || tier == kMcGenMixedEvent) { + histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, mReplaced, w); + } else { + histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, w); } } - template - void fillPairHistosWeighted(PoolTrack const& p1, PoolTrack const& p2, - float w) + template + void fillLeg2Spectrum(PoolTrack const& leg, float mReplaced, float w) { - static constexpr std::array SubDir = { - "LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - - constexpr bool IsSigSB = - (part_pair == kLambdaSBAntiLambda || part_pair == kAntiLambdaSBLambda || - part_pair == kLambdaSBLambda || part_pair == kAntiLambdaSBAntiLambda); - constexpr bool IsSBSB = - (part_pair == kLambdaSBSBAntiLambda || - part_pair == kAntiLambdaSBSBLambda || part_pair == kLambdaSBSBLambda || - part_pair == kAntiLambdaSBSBAntiLambda); - - constexpr int Idx = IsSigSB ? static_cast(part_pair) - 4 - : IsSBSB ? static_cast(part_pair) - 8 - : static_cast(part_pair); - - float drap = p1.rap() - p2.rap(); - float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); - float dR = std::sqrt(drap * drap + dphi * dphi); - - std::array l1 = {p1.px(), p1.py(), p1.pz(), MassLambda0}; - std::array l2 = {p2.px(), p2.py(), p2.pz(), MassLambda0}; - std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; - std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; + static constexpr std::array Dir = {"KinW/SEproc/hLeg2_", "KinW/ME/hLeg2_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + histos.fill(HIST(Dir[IsME]) + HIST(PairTag[part_pair]), leg.pt(), leg.rap(), leg.phi(), mReplaced, w); + } - if (cDoAtlasMethod) { - float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); - - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("ME/Reco/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Atlas/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else if constexpr (IsSigSB) { - histos.fill(HIST("ME/RecoBkgSigSB/Atlas/h2f_n2_mass_") + - HIST(SubDir[Idx]), - cent, p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, - dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else { - histos.fill( - HIST("ME/RecoBkgSBSB/Atlas/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Atlas/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Atlas/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } - } - - if (cDoStarMethod) { - float ctheta = cosThetaStarStar(l1, l2, pr1, pr2); - - if constexpr (!IsSigSB && !IsSBSB) { - histos.fill(HIST("ME/Reco/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, - ctheta, w); - histos.fill(HIST("ME/RecoCorr/Star/h2f_n2_ctheta_") + HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorr/Star/h2f_n2_dltaR_") + HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else if constexpr (IsSigSB) { - histos.fill( - HIST("ME/RecoBkgSigSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSigSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); - } else { - histos.fill( - HIST("ME/RecoBkgSBSB/Star/h2f_n2_mass_") + HIST(SubDir[Idx]), cent, - p1.mass(), p2.mass(), p1.pt(), p2.pt(), drap, dphi, dR, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Star/h2f_n2_ctheta_") + - HIST(SubDir[Idx]), - cent, drap, dphi, ctheta, w); - histos.fill(HIST("ME/RecoCorrBkgSBSB/Star/h2f_n2_dltaR_") + - HIST(SubDir[Idx]), - cent, dR, ctheta, w); + [[nodiscard]] bool isSameWindow(float m1, float m2) const + { + const int b = replacedMassAxis.FindFixBin(m1); + return b >= 1 && b <= replacedMassAxis.GetNbins() && b == replacedMassAxis.FindFixBin(m2); + } + + template + void fillDaughterSeparation(std::array const& a, double ma, std::array const& b, double mb, float dR) + { + static_assert(tier == kSameEvent || tier == kSameEventInMixing, "close-pair QA is filled for SE and SEproc pairs only"); + static constexpr std::array TierDir = {"QA/ClosePair/", "QA/ClosePairSEproc/"}; + static constexpr std::array ClsDir = {"LamLam/", "ALamALam/", "UnlikeSign/"}; + static constexpr std::array ComboName = {"h_pp_", "h_pipi_", "h_p1pi2_", "h_pi1p2_"}; + const float dEta = std::asinh(a[2] / std::hypot(a[0], a[1])) - std::asinh(b[2] / std::hypot(b[0], b[1])); + const float dY = RecoDecay::y(a, ma) - RecoDecay::y(b, mb); + const float dPhi = RecoDecay::constrainAngle(std::atan2(a[1], a[0]) - std::atan2(b[1], b[0]), -PI); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dEta"), dR, dEta, dPhi); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dY"), dR, dY, dPhi); + } + + template + void fillClosePairQA(PoolTrack const& l1, PoolTrack const& l2, float dR) + { + constexpr int Cls = part_pair == kLambdaLambda ? 0 : (part_pair == kAntiLambdaAntiLambda ? 1 : 2); + const std::array pr1 = {l1.prPx(), l1.prPy(), l1.prPz()}; + const std::array pr2 = {l2.prPx(), l2.prPy(), l2.prPz()}; + const std::array pi1 = {l1.px() - l1.prPx(), l1.py() - l1.prPy(), l1.pz() - l1.prPz()}; + const std::array pi2 = {l2.px() - l2.prPx(), l2.py() - l2.prPy(), l2.pz() - l2.prPz()}; + fillDaughterSeparation(pr1, MassProton, pr2, MassProton, dR); + fillDaughterSeparation(pi1, MassPionCharged, pi2, MassPionCharged, dR); + fillDaughterSeparation(pr1, MassProton, pi2, MassPionCharged, dR); + fillDaughterSeparation(pi1, MassPionCharged, pr2, MassProton, dR); + } + + bool isClosePairQAMass(float m) const + { + return cClosePairMassWindow.value <= 0.f || std::abs(m - MassLambda0) < cClosePairMassWindow.value; + } + + bool isKinematicMatch(PoolTrack const& cand, PoolTrack const& leg) const + { + return std::abs(cand.pt() - leg.pt()) < cMaxDeltaPt.value && + std::abs(cand.rap() - leg.rap()) < cMaxDeltaRap.value && + std::abs(RecoDecay::constrainAngle(cand.phi() - leg.phi(), -PI)) < cMaxDeltaPhi.value; + } + + static constexpr std::size_t NLegAxes = 4; + template + float kinWeight(PoolTrack const& cand, float mReplaced) const + { + THnBase const* map = kinWeightMaps[part_pair].get(); + if (!map) { + return 1.f; + } + const std::array x = {cand.pt(), cand.rap(), cand.phi(), mReplaced}; + std::array idx{}; + for (std::size_t i = 0; i < NLegAxes; ++i) { + TAxis const* axis = map->GetAxis(i); + idx[i] = axis->FindFixBin(x[i]); + if (idx[i] < 1 || idx[i] > axis->GetNbins()) { + return 1.f; } } + const int64_t bin = map->GetBin(idx.data()); + if (bin < 0) { + return 1.f; + } + const double w = map->GetBinContent(bin); + return w > 0. ? static_cast(w) : 1.f; } - template - void analyzePairsWithMassWindow(T const& trks_1, T const& trks_2) + static constexpr double AxisEdgeTolerance = 1e-6; + [[nodiscard]] bool hasLegAxes(THnBase const& map) const { - for (auto const& trk_1 : trks_1) { - if (!isInclusive(trk_1.mass())) { - continue; + if (map.GetNdimensions() != static_cast(legAxesRef.size())) { + return false; + } + for (std::size_t i = 0; i < legAxesRef.size(); ++i) { + TAxis const* axis = map.GetAxis(i); + TAxis const& ref = legAxesRef[i]; + if (axis->GetNbins() != ref.GetNbins()) { + return false; } - bool t1sig = isSignal(trk_1.mass()); - bool t1sb = isSideband(trk_1.mass()); - - for (auto const& trk_2 : trks_2) { - if constexpr (samelambda) { - if (trk_1.index() == trk_2.index()) { - continue; - } - } - if (!isInclusive(trk_2.mass())) { - continue; + for (int b = 1; b <= ref.GetNbins() + 1; ++b) { + if (std::abs(axis->GetBinLowEdge(b) - ref.GetBinLowEdge(b)) > AxisEdgeTolerance) { + return false; } - bool t2sig = isSignal(trk_2.mass()); - bool t2sb = isSideband(trk_2.mass()); + } + } + return true; + } - fillPairHistos(trk_1, trk_2); - if ((t1sig && t2sb) || (t1sb && t2sig)) { - fillPairHistos(trk_1, trk_2); - } - if (t1sb && t2sb) { - fillPairHistos(trk_1, trk_2); - } + void loadKinWeights(uint64_t collisionTimestamp) + { + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + const int64_t ts = cKinWeightCcdbTimestamp.value >= 0 ? cKinWeightCcdbTimestamp.value : static_cast(collisionTimestamp); + auto* list = ccdb->getForTimeStamp(cKinWeightCcdbPath.value, ts); + if (!list) { + LOGP(fatal, "Kinematic weights: no object at {} for timestamp {}", cKinWeightCcdbPath.value, ts); + return; + } + auto const* guard = dynamic_cast(list->FindObject("kinWeightGuardStatus")); + if (guard && !TString(guard->GetTitle()).BeginsWith("OK")) { + LOGF(fatal, "Kinematic weights at %s are flagged by their builder: %s", cKinWeightCcdbPath.value.c_str(), guard->GetTitle()); + return; + } + for (std::size_t i = 0; i < PairTag.size(); ++i) { + const std::string name = "hKinW_" + std::string(PairTag[i]); + auto* map = dynamic_cast(list->FindObject(name.c_str())); + if (!map || !hasLegAxes(*map)) { + LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi, window of the replaced leg) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); + return; } + kinWeightMaps[i].reset(static_cast(map->Clone())); } + auto const* form = dynamic_cast(list->FindObject("kinWeightForm")); + kinWeightsLoaded = true; + LOGF(info, "Kinematic weights loaded from %s (form: %s)", cKinWeightCcdbPath.value.c_str(), form ? form->GetTitle() : "not stated"); } - static constexpr int MEModeStandard = 0; - static constexpr int MEModeKinematic = 1; + static uint64_t splitMix(uint64_t x) + { + x += 0x9E3779B97F4A7C15ULL; + x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL; + x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL; + return x ^ (x >> 31); + } - template - void analyzePairsME(T const& trks_1, T const& trks_2) + template + void setPairOrderSeed(C const& col) { + const uint64_t vz = std::bit_cast(col.posZ()); + pairOrderSeed = splitMix(((vz << 32) | static_cast(col.globalIndex())) ^ (col.timeStamp() * 0xD6E8FEB86659FD93ULL)); + } + + template + bool isCountedOrder(T const& trk_1, T const& trk_2) const + { + const int64_t g1 = trk_1.globalIndex(), g2 = trk_2.globalIndex(); + if (g1 == g2) { + return false; + } + const bool firstIsLo = g1 < g2; + const uint64_t lo = static_cast(firstIsLo ? g1 : g2); + const uint64_t hi = static_cast(firstIsLo ? g2 : g1); + const bool swapped = splitMix(pairOrderSeed ^ ((lo << 32) | hi)) & 1ULL; + return firstIsLo != swapped; + } + template + void analyzePairsSE(T const& trks_1, T const& trks_2) + { for (auto const& trk_1 : trks_1) { - if (!isInclusive(trk_1.mass())) { + if (!isAcceptedMass(trk_1.mass())) { continue; } - const bool t1sig = isSignal(trk_1.mass()); - const bool t1sb = isSideband(trk_1.mass()); - PoolTrack p1 = toPoolTrack(trk_1); + const PoolTrack p1 = toPoolTrack(trk_1); for (auto const& trk_2 : trks_2) { - if constexpr (samelambda) { - if (trk_1.index() == trk_2.index()) { - continue; - } - } - if (!isInclusive(trk_2.mass())) { + if (!isCountedOrder(trk_1, trk_2) || !isAcceptedMass(trk_2.mass())) { continue; } - const bool t2sig = isSignal(trk_2.mass()); - const bool t2sb = isSideband(trk_2.mass()); - PoolTrack p2 = toPoolTrack(trk_2); - - fillPairHistosWeighted(p1, p2, 1.0f); - - if ((t1sig && t2sb) || (t1sb && t2sig)) { - fillPairHistosWeighted(p1, p2, 1.0f); - } - - if (t1sb && t2sb) { - fillPairHistosWeighted(p1, p2, 1.0f); + const PoolTrack p2 = toPoolTrack(trk_2); + const float dR = deltaR(p1, p2); + fillPair(p1, p2, dR, 1.f); + if constexpr (tier == kSameEvent) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dR); + } } } } } - template - void analyzePairsMEKinematic(T const& se_trks_1, T const& se_trks_2, - T const& me_pool_1, T const& me_pool_2) + template + void fillEventQA(float centrality, T const& lTrks, T const& alTrks) { - std::vector meVec1, meVec2; - meVec1.reserve(me_pool_1.size()); - meVec2.reserve(me_pool_2.size()); - for (auto const& me : me_pool_1) { - if (isInclusive(me.mass())) { - meVec1.push_back(toPoolTrack(me)); + static constexpr std::array EvDir = {"Events/", "McGen/Events/"}; + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isAcceptedMass(trk.mass())) { + ++nLambda; } } - for (auto const& me : me_pool_2) { - if (isInclusive(me.mass())) { - meVec2.push_back(toPoolTrack(me)); + for (auto const& trk : alTrks) { + if (isAcceptedMass(trk.mass())) { + ++nAntiLambda; } } - - if (meVec1.empty() && meVec2.empty()) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAll); + histos.fill(HIST(EvDir[Gen]) + HIST("hNCandAccepted"), nLambda, nAntiLambda); + if (nLambda + nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeOneCand); + } + if (nLambda + nAntiLambda < NCandidatesForPair) { return; } + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeTwoCand); + histos.fill(HIST(EvDir[Gen]) + HIST("hCentPairEvents"), centrality); + if (nLambda >= 1 && nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeUnlikeSign); + } + if (nLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeLambdaLambda); + } + if (nAntiLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAntiLambdaAntiLambda); + } + for (auto const& trk : lTrks) { + if (isAcceptedMass(trk.mass())) { + fillPairCandQA(trk); + } + } + for (auto const& trk : alTrks) { + if (isAcceptedMass(trk.mass())) { + fillPairCandQA(trk); + } + } + } + template + void fillPairCandQA(T const& trk) + { + static constexpr std::array CandDir = {"QA/PairCand/", "McGen/QA/PairCand/"}; + static constexpr std::array Species = {"Lambda/", "AntiLambda/"}; + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hMassVsPt"), trk.mass(), trk.pt()); + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hRapVsPhi"), trk.rap(), trk.phi()); + } + + [[nodiscard]] int poolBin(float centrality, float vz) const + { + const int iCent = mePoolAxisCent.FindFixBin(centrality); + const int iVz = mePoolAxisVz.FindFixBin(vz); + if (iCent < 1 || iCent > mePoolAxisCent.GetNbins() || iVz < 1 || iVz > mePoolAxisVz.GetNbins()) { + return -1; + } + return (iCent - 1) * mePoolAxisVz.GetNbins() + (iVz - 1); + } + + template + void analyzePairsMEKinematic(T const& se_trks_1, T const& se_trks_2, RollingPool const& pool) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + constexpr PairTier SeTier = Gen ? kMcGenSameEventInMixing : kSameEventInMixing; + constexpr PairTier MeTier = Gen ? kMcGenMixedEvent : kMixedEvent; + std::vector matches; for (auto const& trk1 : se_trks_1) { - if (!isInclusive(trk1.mass())) { + if (!isAcceptedMass(trk1.mass())) { continue; } - const bool se1sig = isSignal(trk1.mass()); - const bool se1sb = isSideband(trk1.mass()); - PoolTrack p1 = toPoolTrack(trk1); + const PoolTrack p1 = toPoolTrack(trk1); for (auto const& trk2 : se_trks_2) { - if constexpr (samelambda) { - if (trk1.index() == trk2.index()) { - continue; - } - } - if (!isInclusive(trk2.mass())) { + if (!isCountedOrder(trk1, trk2) || !isAcceptedMass(trk2.mass())) { continue; } - const bool se2sig = isSignal(trk2.mass()); - const bool se2sb = isSideband(trk2.mass()); - PoolTrack p2 = toPoolTrack(trk2); - - { - std::vector matchA; - for (auto const& meP : meVec2) { - if (std::abs(meP.pt() - p2.pt()) < cMaxDeltaPt && - std::abs(meP.rap() - p2.rap()) < cMaxDeltaRap && - std::abs(RecoDecay::constrainAngle(meP.phi() - p2.phi(), -PI)) < - cMaxDeltaPhi) { - matchA.push_back(meP); - } - } - if (!matchA.empty()) { - const float wA = 1.0f / static_cast(matchA.size()); - for (auto const& meP2 : matchA) { - const bool me2sig = isSignal(meP2.mass()); - const bool me2sb = isSideband(meP2.mass()); - - fillPairHistosWeighted(p1, meP2, wA); - - if ((se1sig && me2sb) || (se1sb && me2sig)) { - fillPairHistosWeighted(p1, meP2, wA); - } + const PoolTrack p2 = toPoolTrack(trk2); + const float dRSeed = deltaR(p1, p2); - if (se1sb && me2sb) { - fillPairHistosWeighted(p1, meP2, wA); - } - } + if (cFillSEInMixing) { + fillPair(p1, p2, dRSeed, 1.f); + } + if constexpr (!Gen) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dRSeed); + } + if (cFillKinWSpectra) { + fillLeg2Spectrum(p2, p2.mass(), 1.f); } } - { - std::vector matchB; - for (auto const& meP : meVec1) { - if (std::abs(meP.pt() - p1.pt()) < cMaxDeltaPt && - std::abs(meP.rap() - p1.rap()) < cMaxDeltaRap && - std::abs(RecoDecay::constrainAngle(meP.phi() - p1.phi(), -PI)) < - cMaxDeltaPhi) { - matchB.push_back(meP); + pool.findMatches(p2, [this, &p2](PoolTrack const& cand) { return isKinematicMatch(cand, p2); }, matches); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMatchesLeg2"), matches.size(), part_pair); + for (auto const& meP2 : matches) { + float kinW = 1.f; + if constexpr (!Gen) { + kinW = kinWeight(*meP2, p2.mass()); + } + const float w = kinW / static_cast(matches.size()); + fillPair(p1, *meP2, dRSeed, w, p2.mass()); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hDeltaRShift"), dRSeed, deltaR(p1, *meP2) - dRSeed, w); + if constexpr (!Gen) { + if (cFillKinWSpectra && isSameWindow(meP2->mass(), p2.mass())) { + fillLeg2Spectrum(*meP2, p2.mass(), w); } } - if (!matchB.empty()) { - const float wB = 1.0f / static_cast(matchB.size()); - for (auto const& meP1 : matchB) { - const bool me1sig = isSignal(meP1.mass()); - const bool me1sb = isSideband(meP1.mass()); + } + } + } + } - fillPairHistosWeighted(meP1, p2, wB); + template + void mixCollision(C const& col, T const& lTrks, T const& alTrks, std::vector& pools, std::vector& eventCands) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventSeen); + const int bin = poolBin(col.cent(), col.posZ()); + if (bin < 0) { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventOutsidePools); + return; + } + histos.fill(HIST(MixQaDir[Gen]) + HIST("hLambdaMultVsCent"), col.cent(), lTrks.size()); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hAntiLambdaMultVsCent"), col.cent(), alTrks.size()); - if ((me1sig && se2sb) || (me1sb && se2sig)) { - fillPairHistosWeighted(meP1, p2, wB); - } + eventCands.clear(); + for (auto const& trk : lTrks) { + if (isAcceptedMass(trk.mass())) { + eventCands.push_back(toPoolTrack(trk)); + } + } + for (auto const& trk : alTrks) { + if (isAcceptedMass(trk.mass())) { + eventCands.push_back(toPoolTrack(trk)); + } + } - if (me1sb && se2sb) { - fillPairHistosWeighted(meP1, p2, wB); - } - } + RollingPool& pool = pools[bin]; + if (eventCands.size() >= NCandidatesForPair) { + if (static_cast(pool.nEvents()) >= cMEPoolMinEvents.value) { + if constexpr (!Gen) { + if (cApplyKinWeights && !kinWeightsLoaded) { + loadKinWeights(col.timeStamp()); } } + setPairOrderSeed(col); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventMixed); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMixedCentVz"), col.cent(), col.posZ()); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hPoolEventsAtMixing"), pool.nEvents()); + analyzePairsMEKinematic(lTrks, alTrks, pool); + analyzePairsMEKinematic(alTrks, lTrks, pool); + analyzePairsMEKinematic(lTrks, lTrks, pool); + analyzePairsMEKinematic(alTrks, alTrks, pool); + } else { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventWarmUp); + } + } + if (static_cast(eventCands.size()) >= cMEPoolMinCand.value) { + pool.push(eventCands); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventDonated); + } + } - } // trk2 - } // trk1 - } // analyzePairsMEKinematic - - // ========================================================================= - // O2 framework declarations - // ========================================================================= using LambdaCollisions = aod::LambdaCollisions; using LambdaMcGenCollisions = aod::LambdaMcGenCollisions; using LambdaTracks = soa::Join; - Preslice perCollisionLambda = - aod::lambdatrack::lambdaCollisionId; + Preslice perCollisionLambda = aod::lambdatrack::lambdaCollisionId; SliceCache cache; - Partition partLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kLambda) && - (aod::lambdatrackext::trueLambdaFlag == true) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partAntiLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && - (aod::lambdatrackext::trueLambdaFlag == true) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); SliceCache cachemc; - Partition partMcLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kLambda) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partMcLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcAntiLambdaTracks = - (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && - (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); void processDummy(LambdaCollisions::iterator const&) {} PROCESS_SWITCH(LambdaSpinPolarization, processDummy, "Dummy", false); - void processDataReco(LambdaCollisions::iterator const& collision, - LambdaTracks const&) + void processDataReco(LambdaCollisions::iterator const& collision, LambdaTracks const&) { - cent = collision.cent(); - auto lTrks = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto alTrks = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - - analyzePairsWithMassWindow(lTrks, alTrks); - analyzePairsWithMassWindow(alTrks, lTrks); - analyzePairsWithMassWindow(lTrks, lTrks); - analyzePairsWithMassWindow(alTrks, alTrks); + setPairOrderSeed(collision); + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + if (!doprocessDataRecoMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); } - PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, - "SE only (data/MCReco)", false); + PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, "SE only (data/MCReco)", false); - struct GetMultiplicity { - float operator()(auto const& col) const { return col.cent(); } - }; - using MixedBinning = - FlexibleBinningPolicy, - o2::aod::collision::PosZ, GetMultiplicity>; - MixedBinning binningOnVtxAndMult{ - {GetMultiplicity{}}, - {axisVtxZME, axisCentME}, - true}; - - void processDataRecoMixed(LambdaCollisions const& col, LambdaTracks const&) + void processDataRecoMixed(LambdaCollisions const& cols, LambdaTracks const&) { - for (auto const& [col1, col2] : soa::selfCombinations( - binningOnVtxAndMult, mixingParameter, -1, col, col)) { - if (col1.globalIndex() == col2.globalIndex()) { - continue; - } - cent = col1.cent(); - histos.fill(HIST("QA/ME/hPoolCentVz"), col1.cent(), col1.posZ()); - - auto lTrks1 = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto lTrks2 = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - auto alTrks1 = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto alTrks2 = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - - histos.fill(HIST("QA/ME/hLambdaMultVsCent"), col1.cent(), lTrks1.size()); - histos.fill(HIST("QA/ME/hAntiLambdaMultVsCent"), col1.cent(), - alTrks1.size()); - - if (cMEMode == MEModeStandard) { - analyzePairsME(lTrks1, alTrks2); - analyzePairsME(alTrks1, lTrks2); - analyzePairsME(lTrks1, lTrks2); - analyzePairsME(alTrks1, alTrks2); - - } else if (cMEMode == MEModeKinematic) { - analyzePairsMEKinematic(lTrks1, alTrks1, - lTrks2, alTrks2); - analyzePairsMEKinematic(alTrks1, lTrks1, - alTrks2, lTrks2); - analyzePairsMEKinematic(lTrks1, lTrks1, lTrks2, - lTrks2); - analyzePairsMEKinematic( - alTrks1, alTrks1, alTrks2, alTrks2); - } + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + fillEventQA(col.cent(), lTrks, alTrks); + mixCollision(col, lTrks, alTrks, mePools, eventCands); + } + } + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, "ME: kinematic mixing against rolling pools that cross DataFrames", false); + + void processMcGen(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) + { + setPairOrderSeed(collision); + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + if (!doprocessMcGenMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); + } + PROCESS_SWITCH(LambdaSpinPolarization, processMcGen, "MC generated: SE pairs (McGen/SE), as processDataReco", false); + + void processMcGenMixed(LambdaMcGenCollisions const& cols, aod::LambdaMcGenTracks const&) + { + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + fillEventQA(col.cent(), lTrks, alTrks); + mixCollision(col, lTrks, alTrks, mePoolsGen, eventCands); } } - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, - "ME (modes 0=standard, 1=kinematicConstrained)", false); + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixed, "MC generated: kinematic mixing against rolling pools, as processDataRecoMixed", false); - void processDataRecoMixEvent(LambdaCollisions::iterator const& collision, - LambdaTracks const&) + void processDataRecoMixEvent(LambdaCollisions::iterator const& collision, LambdaTracks const&) { - auto lTrks = partLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto alTrks = partAntiLambdaTracks->sliceByCached( - aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); if (lTrks.size() == 0 && alTrks.size() == 0) { return; } - lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), - collision.timeStamp()); + lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), collision.timeStamp()); for (auto const& track : lTrks) { lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), @@ -2466,18 +2332,12 @@ struct LambdaSpinPolarization { collision.timeStamp()); } } - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, - "Mix-event table filling", false); + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, "Mix-event table filling", false); - void processMcGenMixEvent(LambdaMcGenCollisions::iterator const& collision, - aod::LambdaMcGenTracks const&) + void processMcGenMixEvent(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) { - auto lTrks = partMcLambdaTracks->sliceByCached( - aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), - cachemc); - auto alTrks = partMcAntiLambdaTracks->sliceByCached( - aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), - cachemc); + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); if (lTrks.size() == 0 && alTrks.size() == 0) { return; @@ -2499,8 +2359,7 @@ struct LambdaSpinPolarization { track.v0Type(), collision.timeStamp()); } } - PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixEvent, - "Mix-event McGen table filling", false); + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixEvent, "Mix-event McGen table filling", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { From d67e1e06eb0f7904a3b51cb5a063d3676c802c5a Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Sat, 3 Oct 2026 00:03:13 +0200 Subject: [PATCH 158/648] [PWGLF,PWGMM] Fix histo definitions and filling (#18155) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 619 +++++++++++++++-------------- 1 file changed, 311 insertions(+), 308 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 7b8ec127ee5..a985de4ba7d 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -82,10 +82,6 @@ auto static constexpr CintZero = 0; auto static constexpr CintOne = 1; auto static constexpr CfloatFive = 5.f; auto static constexpr CInvalid = -999.f; -auto static constexpr CminAccFT0A = 3.5f; -auto static constexpr CmaxAccFT0A = 4.9f; -auto static constexpr CminAccFT0C = -3.3f; -auto static constexpr CmaxAccFT0C = -2.1f; enum class EvtSel { evtAll = 0, @@ -173,21 +169,38 @@ enum class EvtLossType { nEvtLossType }; -enum class McEffStatus { - kMcEffAll = 0, - kMcEffSel, - kMcEffHasMcColl, - kMcEffNoSplitVtx, - nMcEfftStatus +enum class DataEvtStatus { + kDataAll = 0, + kDataSel, + kDataSelGt0, + kDataRej, + nDataEvtStatus }; -enum class McStatus { +enum class McEvtStatus { kMcRecAll = 0, kMcRecSel, kMcRecHasMcColl, kMcRecNoSplitVtx, kMcGenAll, - nMcStatus + nMcEvtStatus +}; + +enum class McEvtLossStatus { + kMcEvtAll = 0, + kMcEvtVtxZ, + kMcEvtTVX, + kMcEvtInelGt0, + kMcEvtInelGt0wMft, + kMcEvtStatus +}; + +enum class McEffStatus { + kMcEffAll = 0, + kMcEffSel, + kMcEffHasMcColl, + kMcEffNoSplitVtx, + nMcEfftStatus }; enum class McTrackStatus { @@ -215,18 +228,19 @@ struct DndetaMFTPbPb { HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { - Configurable cfgDoIR{"cfgDoIR", false, "Flag to retrieve Interaction rate from CCDB"}; - Configurable cfgUseIRCut{"cfgUseIRCut", false, "Flag to cut on IR rate"}; - Configurable cfgIRCrashOnNull{"cfgIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; - Configurable cfgIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; - Configurable cfgUseTrackSel{"cfgUseTrackSel", false, "Flag to apply track selection"}; - Configurable cfgUseParticleSel{"cfgUseParticleSel", true, "Flag to apply particle selection"}; - Configurable cfgUsePrimaries{"cfgUsePrimaries", true, "Select primary particles"}; - Configurable cfgUseSecondaries{"cfgUseSecondaries", false, "Select secondary particles"}; - Configurable cfgRemoveReassigned{"cfgRemoveReassigned", false, "Remove reassgined tracks"}; - Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; - Configurable cfgUseTrackParExtra{"cfgUseTrackParExtra", false, "Use table with refitted track parameters"}; - Configurable cfgUseInelgt0wMFT{"cfgUseInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; + Configurable useEvtSel{"useEvtSel", true, "Use event selection"}; + Configurable useIRFromCCDB{"useIRFromCCDB", false, "Flag to retrieve Interaction rate from CCDB"}; + Configurable useIRCut{"useIRCut", false, "Flag to cut on IR rate"}; + Configurable intRateCrashOnNull{"intRateCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; + Configurable intRateSource{"intRateSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; + Configurable useTrackSel{"useTrackSel", false, "Flag to apply track selection"}; + Configurable useParticleSel{"useParticleSel", true, "Flag to apply particle selection"}; + Configurable usePrimaries{"usePrimaries", true, "Select primary particles"}; + Configurable useSecondaries{"useSecondaries", false, "Select secondary particles"}; + Configurable rmReassigned{"rmReassigned", false, "Remove reassgined tracks"}; + Configurable rmSplitVertex{"rmSplitVertex", true, "Remove split vertices"}; + Configurable useInelgt0{"useInelgt0", false, "Use INEL > 0 condition"}; + Configurable useInelgt0wMFT{"useInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; } gConf; @@ -306,7 +320,7 @@ struct DndetaMFTPbPb { Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; Configurable minIR{"minIR", -1, "minimum IR (kHz) collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR (kHz) collisions"}; - Configurable useInelgt0wTVX{"useInelgt0wTVX", false, "Use INEL > 0 condition with TVX trigger, i.e. FT0A and FT0C acceptance"}; + Configurable requireTVX{"requireTVX", false, "Use TVX trigger, i.e. FT0A and FT0C acceptance"}; Configurable useGenMult{"useGenMult", false, "use MultMC for centrality"}; } eventCuts; @@ -328,9 +342,6 @@ struct DndetaMFTPbPb { std::array centerMFT{0, 0, -61.4}; // Field at center of MFT o2::parameters::GRPMagField* grpmag = nullptr; - std::vector ambiguousTrkIds; - std::vector reassignedTrkIds; - /// @brief init function, definition of histograms void init(InitContext&) { @@ -392,12 +403,6 @@ struct DndetaMFTPbPb { LOGP(fatal, "Either doprocessMcEfficiencyIdxCentFT0C OR doprocessMcEfficiencyIdxBestCentFT0C should be enabled!"); } - // General counters - QC - auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); - hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); - hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); - hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); - registryQC.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); std::array(EvtSel::nEvtSel)> labelEvtSel{ "All coll.", @@ -421,18 +426,34 @@ struct DndetaMFTPbPb { registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); } - registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); - std::array(TrkSel::nTrkSel)> labelTrkSel{ - "All", - "Ncls", - "Chi2", - "Eta", - "Phi cut", - "Pt", - "CA"}; - registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { - registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + if (doprocessTagging) { + auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); + hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); + hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); + hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + } + + if (doprocessDataInclusive || doprocessDatawBestTracksInclusive || + doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C || + doprocessMcInclusive || doprocessMcBestInclusive || + doprocessMcCentFT0C || doprocessMcBestCentFT0C || + doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive || + doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C || + doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxCentFT0C || + doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { + registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); + std::array(TrkSel::nTrkSel)> labelTrkSel{ + "All", + "Ncls", + "Chi2", + "Eta", + "Phi cut", + "Pt", + "CA"}; + registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + } } if (doprocessDatawBestTracksInclusive || doprocessDatawBestTracksCentFT0C || @@ -455,13 +476,17 @@ struct DndetaMFTPbPb { } if (doprocessDataInclusive || doprocessDatawBestTracksInclusive) { registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); - registryData.add({"Events/Selection", ";status; occupancy", {HistType::kTH2F, {{2, 0.5, 2.5}, occupancyAxis}}}); - auto hstat = registryData.get(HIST("Events/Selection")); - auto* x = hstat->GetXaxis(); - x->SetBinLabel(1, "All"); - x->SetBinLabel(2, "Selected"); - + registryData.add("Events/DataEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatus{ + "Data all", + "Data sel", + "Data sel gt0", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/DataEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelDataEvtStatus[iBin].data()); + } registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/EtaZvtxGt0", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); @@ -473,25 +498,24 @@ struct DndetaMFTPbPb { registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/OrigTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/RestTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); - registryData.add({"Tracks/TanLambdaExtra", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - registryData.add({"Tracks/InvQPtExtra", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - registryData.add({"Tracks/EtaExtra", "; #eta; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/PhiExtra", "; #varphi; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); } + LOG(info) << "doprocessData[BestTrack]Inclusive -> Size of the Data histograms:"; + registryData.print(); } if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Events/Centrality/Selection", ";status; centrality; occupancy", {HistType::kTHnSparseF, {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); - auto hstat = registryData.get(HIST("Events/Centrality/Selection")); - hstat->GetAxis(0)->SetBinLabel(1, "All"); - hstat->GetAxis(0)->SetBinLabel(2, "Selected"); - + registryData.add("Events/Centrality/DataEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatusCent{ + "Data all", + "Data sel", + "Data sel gt0", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/Centrality/DataEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelDataEvtStatusCent[iBin].data()); + } registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/EtaZvtxGt0", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; #eta; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); @@ -504,16 +528,10 @@ struct DndetaMFTPbPb { registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/OrigTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/OrigTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/RestTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/RestTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TanLambdaExtra", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/InvQPtExtra", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/EtaExtra", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); } + LOG(info) << "doprocessData[BestTrack]CentFT0C -> Size of the Data histograms:"; + registryData.print(); } if (doprocessDataCorrelationwBestTracksInclusive) { registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); @@ -521,17 +539,19 @@ struct DndetaMFTPbPb { registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); + LOG(info) << "doprocessDataCorrelationwBestTracksInclusive -> Size of the Data histograms:"; + registryData.print(); } if (doprocessMcInclusive || doprocessMcBestInclusive) { - registryMC.add("Events/McStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, occupancyAxis}}); - std::array(McStatus::nMcStatus)> labelMcStatus{ + registryMC.add("Events/McEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatus{ "Rec all", "Rec sel", "Rec w/ mc coll", "Rec w/o split vtx", "Gen all"}; - for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { - registryMC.get(HIST("Events/McStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcStatus[iBin].data()); + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/McEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtStatus[iBin].data()); } if (doprocessMcBestInclusive) { registryMC.add({"Tracks/hMcTrackStatus", "Number of tracks; Cut; occupancy", {HistType::kTH2F, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, occupancyAxis}}}); @@ -556,28 +576,30 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcCentFT0C || doprocessMcBestCentFT0C) { - registryMC.add("Events/Centrality/McStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McStatus::nMcStatus), -0.5, +static_cast(McStatus::nMcStatus) - 0.5}, centralityAxis, occupancyAxis}}); - std::array(McStatus::nMcStatus)> labelMcStatusCent{ + registryMC.add("Events/Centrality/McEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatusCent{ "Rec all", "Rec sel", "Rec w/ mc coll", "Rec w/o split vtx", "Gen all"}; - for (int iBin = 0; iBin < static_cast(McStatus::nMcStatus); iBin++) { - registryMC.get(HIST("Events/Centrality/McStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCent[iBin].data()); + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/McEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCent[iBin].data()); } if (doprocessMcBestCentFT0C) { registryMC.add({"Tracks/Centrality/hMcTrackStatus", "Number of tracks; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, centralityAxis, occupancyAxis}}}); - std::array(McTrackStatus::nMcTrackStatus)> labelMcStatusCentBest{ + std::array(McTrackStatus::nMcTrackStatus)> labelMcEvtStatusCentBest{ "Best all", "Best sel", "Trk sel", "Has coll", "Reas rm"}; for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { - registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcStatusCentBest[iBin].data()); + registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCentBest[iBin].data()); } registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); @@ -594,6 +616,8 @@ struct DndetaMFTPbPb { registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive) { registryMC.add("Events/hMcEffStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, occupancyAxis}}); @@ -610,6 +634,8 @@ struct DndetaMFTPbPb { registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C) { registryMC.add("Events/Centrality/hMcEffStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, centralityAxis, occupancyAxis}}); @@ -625,33 +651,41 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxBestInlusive) { registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {{10, 0.5, 10.5}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]Inlusive -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcEfficiencyIdxCentFT0C || doprocessMcEfficiencyIdxBestCentFT0C) { registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTHnSparseF, {{10, 0.5, 10.5}, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcSgnEvtLossCentFT0C) { - registryMC.add("Events/hEvtMcGen", "Events/hEvtMcGen", {HistType::kTH1F, {{4, 0.f, 4.f}}}); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(1, "all"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(2, "z-vtx"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(3, "isInelGt0wMft"); - registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(4, "TVX"); - // - registryMC.add("Events/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); - auto hstat = registryMC.get(HIST("Events/EvtSigLossStatus")); + registryMC.add("Events/hMcEvtLossStatus", "Number of events; Cut", {HistType::kTH1F, {{static_cast(McEvtLossStatus::kMcEvtStatus), -0.5, +static_cast(McEvtLossStatus::kMcEvtStatus) - 0.5}}}); + std::array(McEvtLossStatus::kMcEvtStatus)> labelMcEvtLossStatus{ + "All", + "Z-vtx", + "TVX", + "InelGt0 coll", + "InelGt0 in MFT"}; + for (int iBin = 0; iBin < static_cast(McEvtLossStatus::kMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/hMcEvtLossStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtLossStatus[iBin].data()); + } + registryMC.add("Events/Centrality/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); + auto hstat = registryMC.get(HIST("Events/Centrality/EvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); - // registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multFT0cAxis, evtLossTypeAxis}}}); registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hNchTVX", "; Nch; status", {HistType::kTH2F, {{2, 0, 2}, multAxis}}}); registryMC.add({"Events/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); registryMC.add({"Events/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); registryMC.add({"Events/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); @@ -660,6 +694,8 @@ struct DndetaMFTPbPb { registryMC.add({"Events/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); registryMC.add({"Tracks/hEtaVsNchGen", "; #eta; mult gen", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); registryMC.add({"Tracks/hEtaVsNchGenRecEvt", "; #eta; mult gen w/ Rec evt", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); + LOG(info) << "doprocessMcSgnEvtLossCentFT0C -> Size of the MC histograms:"; + registryMC.print(); } if (doprocessMcReassocDCA) { registryMC.add({"Events/Centrality/EvtGenRecReassoc", ";status;centrality", {HistType::kTHnSparseF, {{3, 0.5, 3.5}, centralityAxis}}}); @@ -707,6 +743,8 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + LOG(info) << "doprocessMcReassocDCA -> Size of the MC histograms:"; + registryMC.print(); } } @@ -992,36 +1030,10 @@ struct DndetaMFTPbPb { return nTrk; } - template - void countBestTracksExtra(B const& besttracksExtra, float c, float occ) - { - for (auto const& etrack : besttracksExtra) { - if (fillHis) { - if constexpr (has_reco_cent) { - if (gConf.cfgUseTrackParExtra) { - registryData.fill(HIST("Tracks/Centrality/TanLambdaExtra"), etrack.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPtExtra"), etrack.signed1Pt(), c, occ); - registryData.fill(HIST("Tracks/Centrality/EtaExtra"), etrack.etas(), c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiExtra"), etrack.phis(), c, occ); - } - } else { - if (gConf.cfgUseTrackParExtra) { - registryData.fill(HIST("Tracks/TanLambdaExtra"), etrack.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPtExtra"), etrack.signed1Pt(), occ); - registryData.fill(HIST("Tracks/EtaExtra"), etrack.etas(), occ); - registryData.fill(HIST("Tracks/PhiExtra"), etrack.phis(), occ); - } - } - } - } - } - template - int countBestTracks(T const& tracks, B const& besttracks, float z, float c, float occ) + int countBestTracks(T const& /*tracks*/, B const& besttracks, float z, float c, float occ) { auto nATrk = 0; - ambiguousTrkIds.reserve(besttracks.size()); - reassignedTrkIds.reserve(besttracks.size()); for (auto const& atrack : besttracks) { if (!isBestTrackSelected(atrack)) { continue; @@ -1038,7 +1050,6 @@ struct DndetaMFTPbPb { if (!isTrackSelected(itrack)) { continue; } - ambiguousTrkIds.emplace_back(atrack.mfttrackId()); ++nATrk; if (fillHis) { if constexpr (has_reco_cent) { @@ -1058,7 +1069,6 @@ struct DndetaMFTPbPb { } } if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { - reassignedTrkIds.emplace_back(atrack.mfttrackId()); if (fillHis) { registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { @@ -1071,47 +1081,6 @@ struct DndetaMFTPbPb { } } } - - for (auto const& track : tracks) { - if (!isTrackSelected(track)) { - continue; - } - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/OrigTracksEtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/OrigTracksPhiEta"), track.phi(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/OrigTracksEtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/OrigTracksPhiEta"), track.phi(), track.eta(), occ); - } - } - if (std::find(ambiguousTrkIds.begin(), ambiguousTrkIds.end(), track.globalIndex()) != ambiguousTrkIds.end()) { - continue; - } - if (std::find(reassignedTrkIds.begin(), reassignedTrkIds.end(), track.globalIndex()) != reassignedTrkIds.end()) { - continue; - } - // ++nATrk; // use for testing purposes only! - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/RestTracksEtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/RestTracksPhiEta"), track.phi(), track.eta(), c, occ); - // registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - // registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); - // registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/RestTracksEtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/RestTracksPhiEta"), track.phi(), track.eta(), occ); - // registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - // registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); - // registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); - } - } - } - ambiguousTrkIds.clear(); - ambiguousTrkIds.shrink_to_fit(); - reassignedTrkIds.clear(); - reassignedTrkIds.shrink_to_fit(); return nATrk; } @@ -1119,34 +1088,15 @@ struct DndetaMFTPbPb { bool isInelGt0wMft(P const& particles) { int nChrgMc = 0; - int nChrgFT0A = 0; - int nChrgFT0C = 0; for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (!particle.isPhysicalPrimary()) { - continue; - } - // trigger TVX - if (particle.eta() > CminAccFT0A && particle.eta() < CmaxAccFT0A) { - nChrgFT0A++; - } - if (particle.eta() > CminAccFT0C && particle.eta() < CmaxAccFT0C) { - nChrgFT0C++; - } - // acceptance MFT - if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } nChrgMc++; } - if (nChrgFT0A == CintZero || nChrgFT0C == CintZero) { - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); - return false; - } - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); - return nChrgMc != CintZero; } @@ -1192,7 +1142,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { @@ -1206,10 +1156,10 @@ struct DndetaMFTPbPb { template bool isParticleSelected(P const& particle) { - if (gConf.cfgUsePrimaries && !particle.isPhysicalPrimary()) { + if (gConf.usePrimaries && !particle.isPhysicalPrimary()) { return false; } - if (!gConf.cfgUsePrimaries && (gConf.cfgUseSecondaries && particle.isPhysicalPrimary())) { + if (!gConf.usePrimaries && (gConf.useSecondaries && particle.isPhysicalPrimary())) { return false; } if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { @@ -1395,7 +1345,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if constexpr (isCent) { @@ -1449,41 +1399,59 @@ struct DndetaMFTPbPb { float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registryData.fill(HIST("Events/Selection"), 1., occ); + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - if (!isGoodEvent(collision)) { - return; - } - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - } else { - registryData.fill(HIST("Events/Selection"), 2., occ); - } - auto nTrk = countTracks(tracks, z, c, occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + + auto nTrk = countTracks(tracks, z, c, occ); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); + if constexpr (has_reco_cent) { + if (nTrk > 0) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), c, occ); + for (auto const& track : tracks) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtxGt0"), track.eta(), z, c, occ); + } + } + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); + } else { + if (nTrk > 0) { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), occ); + for (auto const& track : tracks) { + registryData.fill(HIST("Tracks/EtaZvtxGt0"), track.eta(), z, occ); + } + } + registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + } } else { - registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } @@ -1492,49 +1460,79 @@ struct DndetaMFTPbPb { void processDatawBestTracks(typename C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, - aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& /*bcs*/) { float c = getRecoCent(collision); float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registryData.fill(HIST("Events/Selection"), 1., occ); + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - if (!isGoodEvent(collision)) { - return; - } - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - } else { - registryData.fill(HIST("Events/Selection"), 2., occ); - } - auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); - countBestTracksExtra(besttracksExtra, c, occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + + auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); + if constexpr (has_reco_cent) { + if (nBestTrks > 0) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), c, occ); + for (auto const& atrack : besttracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + registryData.fill(HIST("Tracks/Centrality/EtaZvtxGt0"), itrack.eta(), z, c, occ); + } + } + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); + } else { + if (nBestTrks > 0) { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), occ); + for (auto const& atrack : besttracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + registryData.fill(HIST("Tracks/EtaZvtxGt0"), itrack.eta(), z, occ); + } + } + registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); + } } else { - registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } @@ -1552,16 +1550,16 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, "Count tracks in FT0C centrality bins", false); - void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, "Count tracks based on BestCollisionsFwd3d table (inclusive)", false); - void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", false); @@ -1577,14 +1575,14 @@ struct DndetaMFTPbPb { auto nBestTrks = 0; for (auto const& atrack : besttracks) { - if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { + if (gConf.useTrackSel && !isBestTrackSelected(atrack)) { continue; } auto itrack = atrack.template mfttrack_as(); if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { continue; } - if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { + if (gConf.useTrackSel && !isTrackSelected(itrack)) { continue; } nBestTrks++; @@ -1647,34 +1645,34 @@ struct DndetaMFTPbPb { float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } if (!isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } auto nTrkRec = 0; @@ -1688,7 +1686,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (collision.mcCollisionId() != particle.mcCollisionId()) { @@ -1721,9 +1719,9 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } auto nCharged = countPart(particles); @@ -1804,34 +1802,34 @@ struct DndetaMFTPbPb { float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecAll), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } if (!isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecHasMcColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcRecNoSplitVtx), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } auto nATrk = 0; @@ -1868,7 +1866,7 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); } - if (gConf.cfgRemoveReassigned) { + if (gConf.rmReassigned) { if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } @@ -1883,7 +1881,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (eventCuts.useZDiffCut) { @@ -1933,9 +1931,9 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McStatus"), static_cast(McStatus::kMcGenAll), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { - registryMC.fill(HIST("Events/McStatus"), static_cast(McStatus::kMcGenAll), occGen); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } auto nCharged = countPart(particles); @@ -1991,7 +1989,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } gtOneRec = true; @@ -2007,14 +2005,14 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if constexpr (has_reco_cent) { @@ -2035,7 +2033,7 @@ struct DndetaMFTPbPb { } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); } @@ -2043,7 +2041,7 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); } @@ -2051,20 +2049,20 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), cRec, occRec); } else { registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); } @@ -2078,7 +2076,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (collision.mcCollisionId() != particle.mcCollisionId()) { @@ -2123,10 +2121,10 @@ struct DndetaMFTPbPb { /// @brief process function to calculate tracking efficiency based on BestCollisionsFwd3d in FT0C bins template - void processEfficiencyBest(typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - aod::McParticles const& particles, - MftBestTracksLabeled const& besttracks) + void processMcEfficiencyBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); @@ -2139,7 +2137,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } gtOneRec = true; @@ -2147,21 +2145,21 @@ struct DndetaMFTPbPb { occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); } } else { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if constexpr (has_reco_cent) { @@ -2182,7 +2180,7 @@ struct DndetaMFTPbPb { } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); } @@ -2190,7 +2188,7 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); } @@ -2198,20 +2196,20 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), cRec, occRec); } else { registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), cRec, occRec); } else { registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); } @@ -2228,7 +2226,7 @@ struct DndetaMFTPbPb { if (!itrack.has_collision()) { continue; } - if (gConf.cfgRemoveReassigned) { + if (gConf.rmReassigned) { if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } @@ -2238,7 +2236,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (eventCuts.useZDiffCut) { @@ -2276,7 +2274,7 @@ struct DndetaMFTPbPb { aod::McParticles const& particles, MftBestTracksLabeled const& besttracks) { - processEfficiencyBest(mccollision, collisions, particles, besttracks); + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); @@ -2286,7 +2284,7 @@ struct DndetaMFTPbPb { aod::McParticles const& particles, MftBestTracksLabeled const& besttracks) { - processEfficiencyBest(mccollision, collisions, particles, besttracks); + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); @@ -2307,7 +2305,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } cRec = getRecoCent(collision); @@ -2317,7 +2315,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); @@ -2453,7 +2451,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } cRec = getRecoCent(collision); @@ -2466,7 +2464,7 @@ struct DndetaMFTPbPb { if (!isGoodEvent(collision)) { continue; } - if (gConf.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); @@ -2594,34 +2592,36 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); - /// @brief process function to calculate signal loss based on MC void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenAll)); - registryMC.fill(HIST("Events/hEvtMcGen"), 0.5); + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtAll)); if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 1.5); - // At least one generated primary in MFT acceptance + TVX triggered collisions - if (gConf.cfgUseInelgt0wMFT && !isInelGt0wMft(particles)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtVtxZ)); + if (eventCuts.requireTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + return; + } + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtTVX)); + if (gConf.useInelgt0 && !mcCollision.isInelGt0()) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 2.5); - if (eventCuts.useInelgt0wTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0)); + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { return; } - registryMC.fill(HIST("Events/hEvtMcGen"), 3.5); + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0wMft)); registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSel)); // Evt loss den bool gtZeroColl = false; auto maxNcontributors = -1; float cRec = CInvalid; for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { + if (!isGoodEvent(collision)) { continue; } if (std::abs(collision.posZ()) >= eventCuts.maxZvtx) { @@ -2639,26 +2639,29 @@ struct DndetaMFTPbPb { auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - registryMC.fill(HIST("Events/EvtSigLossStatus"), 1., cRec); // Evt split den + registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 1., cRec); // Evt split den registryMC.fill(HIST("Events/hMultGenVsCent"), cRec, mcCollision.multMCFT0C()); registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); if (gtZeroColl) { + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { + return; + } registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenRecEvt)); // Evt loss num - registryMC.fill(HIST("Events/EvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 2., cRec); // Evt split num registryMC.fill(HIST("Events/hMultGenVsCentRec"), cRec, mcCollision.multMCFT0C()); registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); } if (collisions.size() == 0) { - registryMC.fill(HIST("Events/EvtSigLossStatus"), 3., cRec); + registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 3., cRec); } for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } registryMC.fill(HIST("Tracks/hEtaVsNchGen"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss den @@ -2696,7 +2699,7 @@ struct DndetaMFTPbPb { continue; } registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); - if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + if (gConf.rmSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { continue; } auto mcColl = collision.mcCollision_as(); @@ -2719,7 +2722,7 @@ struct DndetaMFTPbPb { if (!itrack.has_collision()) { continue; } - if (gConf.cfgRemoveReassigned) { + if (gConf.rmReassigned) { if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } @@ -2731,7 +2734,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (eventCuts.useZDiffCut) { @@ -2749,7 +2752,7 @@ struct DndetaMFTPbPb { const auto dcaXtruth(particle.vx() - mcColl.posX()); const auto dcaYtruth(particle.vy() - mcColl.posY()); const auto dcaZtruth(particle.vz() - mcColl.posZ()); - auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); + const auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); const auto mcId = particle.mcCollisionId(); if (!mcIds.contains(mcId)) { From d374d67fccfd2be83a18b79d1897f7aee3df68f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Sat, 3 Oct 2026 02:53:40 +0200 Subject: [PATCH 159/648] [Common,Tutorial,PWGEM] Include headers needed by `DECLARE_SOA_CCDB_COLUMN...` (#18129) --- Common/DataModel/GloCCDBObjects.h | 3 +++ Common/DataModel/TpcCCDBObjects.h | 3 +++ Common/DataModel/TrackTunerCCDBObjects.h | 2 ++ PWGEM/PhotonMeson/DataModel/EventTables.h | 4 ++-- PWGEM/PhotonMeson/DataModel/bcWiseTables.h | 4 ++-- Tutorials/src/ccdbtableaccess.cxx | 3 +++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Common/DataModel/GloCCDBObjects.h b/Common/DataModel/GloCCDBObjects.h index 4612f8ba02a..21b8bb7d610 100644 --- a/Common/DataModel/GloCCDBObjects.h +++ b/Common/DataModel/GloCCDBObjects.h @@ -59,6 +59,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + #include #include #include diff --git a/Common/DataModel/TpcCCDBObjects.h b/Common/DataModel/TpcCCDBObjects.h index 19b455e0234..9449946ff6c 100644 --- a/Common/DataModel/TpcCCDBObjects.h +++ b/Common/DataModel/TpcCCDBObjects.h @@ -29,6 +29,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + namespace o2::aod { namespace ccdbTpc diff --git a/Common/DataModel/TrackTunerCCDBObjects.h b/Common/DataModel/TrackTunerCCDBObjects.h index 2e4817efff3..5c27ea2f9eb 100644 --- a/Common/DataModel/TrackTunerCCDBObjects.h +++ b/Common/DataModel/TrackTunerCCDBObjects.h @@ -28,6 +28,8 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include namespace o2::aod diff --git a/PWGEM/PhotonMeson/DataModel/EventTables.h b/PWGEM/PhotonMeson/DataModel/EventTables.h index 93630002fc5..0d58611d8a5 100644 --- a/PWGEM/PhotonMeson/DataModel/EventTables.h +++ b/PWGEM/PhotonMeson/DataModel/EventTables.h @@ -23,8 +23,8 @@ #include #include -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include // for BIT diff --git a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h index e1effd5871f..d2eed4b2f58 100644 --- a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h +++ b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h @@ -19,8 +19,8 @@ #include #include -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) #include #include diff --git a/Tutorials/src/ccdbtableaccess.cxx b/Tutorials/src/ccdbtableaccess.cxx index 5c703e4d1dd..3cd86168e8f 100644 --- a/Tutorials/src/ccdbtableaccess.cxx +++ b/Tutorials/src/ccdbtableaccess.cxx @@ -21,6 +21,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + using namespace o2::framework; using namespace o2::header; using namespace o2; From 94ef0712e6c57a80be22665fbc23f3b29505c3c8 Mon Sep 17 00:00:00 2001 From: omassen <55696099+omassen@users.noreply.github.com> Date: Sat, 3 Oct 2026 06:53:44 +0200 Subject: [PATCH 160/648] [PWGEM] Add Flag for creating and filling NUA hist. (#18118) Co-authored-by: ALICE Action Bot --- PWGEM/Dilepton/Core/Dilepton.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/PWGEM/Dilepton/Core/Dilepton.h b/PWGEM/Dilepton/Core/Dilepton.h index 5cd306812cb..a63c927c422 100644 --- a/PWGEM/Dilepton/Core/Dilepton.h +++ b/PWGEM/Dilepton/Core/Dilepton.h @@ -326,6 +326,7 @@ struct Dilepton { struct : o2::framework::ConfigurableGroup { std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; @@ -669,7 +670,9 @@ struct Dilepton { const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); - fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); + } fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); @@ -1099,13 +1102,19 @@ struct Dilepton { float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } } else if constexpr (ev_id == 1) { if (t1.sign() * t2.sign() < 0) { // ULS From 07644bd0af7f91f68be7e56a24a93e8ba014e863 Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:06:12 +0200 Subject: [PATCH 161/648] [PWGCF] Fix MC histogram paths in radial flow decorrelation (#18158) --- PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 37774bc9a35..66dd110b560 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -1542,9 +1542,9 @@ struct RadialFlowDecorr { histos.fill(HIST("Fake_eta"), eta, fake); histos.fill(HIST("wgt_eta"), eta, w); - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); + histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi); + histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); + histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); } // subevent mean-pT maps @@ -1692,9 +1692,9 @@ struct RadialFlowDecorr { } } - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); + histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi); + histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); + histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); } for (int ieta = 0; ieta < nEta; ++ieta) { From 1d75cb00b7cf5b89351bb5b6d23d59647ca54ee5 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sat, 3 Oct 2026 22:18:22 +0800 Subject: [PATCH 162/648] [PWGCF] Add new QA and technical cuts and other improvements (#18160) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 499 +++++++++++------- 1 file changed, 308 insertions(+), 191 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 5282c3146ce..b508cd577b4 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -13,6 +13,7 @@ /// \brief Multiparticle correlation in O2 Framework /// \author yuanjun.mei@cern.ch +#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +94,17 @@ enum ERecSim { eRecSim_N }; +enum ETechnicalCuts { + eNoCollInTimeRangeStandard = 0, + eNoCollInRofStandard, + eNoSameBunchPileUp, + eIsVertexITSTPC, + eIsGoodITSLayersAll, + eIsGoodZvtxFT0vsPV, + eNoHighMultCollInPrevRof, + ETechnicalCuts_N +}; + enum ECuts { eBefore = 0, eAfter, @@ -135,7 +148,8 @@ enum EMiscHistograms { }; enum EWeightsHistograms { - ePhi = 0, + ePhiRec = 0, + ePhiSim, ePt, eWeightsHistograms_N }; @@ -160,7 +174,8 @@ static constexpr std::array MultiplicityTabl "multNTracksPV"}; static constexpr std::array WeightsNames = { - "ePhi", + "ePhiRec", + "ePhiSim", "ePt"}; static constexpr std::array CutsNames = { @@ -185,38 +200,43 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable multiplicityTables{"multiplicityTables", 0, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; - // *) external root files + // *) External root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; - // *) binnings + // *) Binnings Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; Configurable> cfMultBins{"cfMultBins", {400, 0., 40000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "Reference mult bins: nMultBins, multMin, multMax"}; - Configurable> cfVxBins{"cfVxBins", {300, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; - Configurable> cfVyBins{"cfVyBins", {300, -0.01, 0.01}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; - Configurable> cfVzBins{"cfVzBins", {300, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; + Configurable> cfContribBins{"cfContribBins", {400, 0., 6500.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; + Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; + Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; + Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; Configurable> cfIpBins{"cfIpBins", {100, 0., 20.}, "Impact parameters hist (MC only): nIPBins, ipMin, ipMax"}; - Configurable> cfPtBins{"cfPtBins", {2000, 0., 5.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPtBins{"cfPtBins", {2000, 0., 6.}, "nPtBins, ptMin, ptMax"}; Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; // *) Cuts - // event level cuts Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; + // technical cuts + Configurable> cfTechnicalCutSwitch{"cfTechnicalCutSwitch", {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}, "technical cuts switch, on and off by the first number before name"}; + + // event level cuts Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply vertex z position cut"}; - Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10, 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz < max"}; + Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; + Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}[cm], with convention: min <= cent <= max"}; // particle level cuts Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "switch to apply pt cut"}; - Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}, with convention: min <= pt < max"}; + Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}, with convention: min <= pt <= max"}; Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "switch to apply pt cut"}; - Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta < max"}; + Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; - // *) misc + // *) Misc Configurable sigmaInel{"sigmaInel", 7.71, "inelastic cross section in mb"}; Configurable qualityAssuranceSwitch{"qualityAssuranceSwitch", false, "quality assurance switch"}; Configurable runMessageSwitch{"runMessageSwitch", false, "run message switch"}; @@ -228,13 +248,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool fDryRun = false; // book all histos and run without filling and calculating anything } tc; // you have to prepend "tc." for all objects name in this group later in the code - // **) Particle histograms: struct ParticleHistograms { TList* fParticleHistList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHist{}; } pc; - // *) Event histograms: struct EventHistograms { TList* fEventHistList = nullptr; std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] @@ -245,24 +263,23 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TH1D* fMiscHistRunNumber = nullptr; } misc; - // *) External histograms: struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - std::array, eWeightsHistograms_N> fWeights{}; //! [type][before, after cuts] + std::array fWeights{}; //! [type] } ex; struct Observables { TList* fObservablesList = nullptr; - std::array, 2> fProfTwo{}; //! [reco,sim][before, after event cuts] + std::array fProfTwo{}; //! [reco,sim] } obs; - // *) Quality assurance histograms: struct QualityAssurance { TList* fQualityAssuranceList = nullptr; //! fHistCentralityRecSim{}; + std::array fHistMultNContrib{}; } qa; - // *) functions + // *) functions and templates std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { int sum = 0; @@ -300,7 +317,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto corr = [&](int n1, int n2) -> TComplex { return q(n1, 1) * q(n2, 1) - q(n1 + n2, 2); }; - TComplex n = corr(n1, n2); TComplex d = corr(0, 0); return {n, d}; @@ -327,7 +343,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if ((m - 1) == skip) { return c; } - int counter1 = 0; int hhold = harmonic[counter1]; harmonic[counter1] = harmonic[m - 2]; @@ -348,7 +363,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } harmonic[m - 2] = harmonic[counter1]; harmonic[counter1] = hhold; - if (mult == 1) { return {c[0] - c2[0], c[1] - c2[1]}; } @@ -509,7 +523,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to hist = dynamic_cast(listWithRuns->FindObject(histName)); if (!hist) { - LOGF(info, "%s: histogram 'hist' not found in run list", __FUNCTION__); + LOGF(info, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, histName, runNumber); return nullptr; } @@ -564,28 +578,65 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + template + bool technicalCuts(T1 const& collision) + { + if (cfTechnicalCutSwitch.value[eNoCollInTimeRangeStandard] == "1NoCollInTimeRangeStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (cfTechnicalCutSwitch.value[eNoCollInRofStandard] == "1NoCollInRofStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (cfTechnicalCutSwitch.value[eNoSameBunchPileUp] == "1NoSameBunchPileUp" && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (cfTechnicalCutSwitch.value[eIsVertexITSTPC] == "1IsVertexITSTPC" && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (cfTechnicalCutSwitch.value[eIsGoodITSLayersAll] == "1IsGoodITSLayersAll" && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + if (cfTechnicalCutSwitch.value[eIsGoodZvtxFT0vsPV] == "1IsGoodZvtxFT0vsPV" && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (cfTechnicalCutSwitch.value[eNoHighMultCollInPrevRof] == "1NoHighMultCollInPrevRof" && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + + return true; + } + template bool eventCuts(T1 const& collision) { + if constexpr (rs == eRecAndSim && rm == eMC) { + if (!collision.has_mcCollision()) { + return false; + } + } if constexpr (rs == eRec || rs == eRecAndSim) { if (cfEventCutSwitch) // event level cuts for Rec { - if (rm == eReal) { + if constexpr (rm == eReal) { if (collision.posZ() > cfVertexZCutRange.value[1] || collision.posZ() < cfVertexZCutRange.value[0]) { return false; } // vertex z cut + auto thisCent = chooseCent(collision, centralityEstimator); + if (thisCent > cfCentCutRange.value[1] || thisCent < cfCentCutRange.value[0]) { + return false; + } // centrality cut } - if constexpr (rs == eRecAndSim) // event level cuts for Sim + if constexpr (rs == eRecAndSim && rm == eMC) // event level cuts for Sim { - if (rm == eMC) { - if (!collision.has_mcCollision()) { - return false; - } - auto thisMCCollision = collision.mcCollision(); // corresponding MC truth simulated particle - if (thisMCCollision.posZ() > cfVertexZCutRange.value[1] || thisMCCollision.posZ() < cfVertexZCutRange.value[0]) { - return false; - } // vertex z cut - } + auto thisMCCollision = collision.mcCollision(); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; + if (thisMCCollision.posZ() > cfVertexZCutRange.value[1] || thisMCCollision.posZ() < cfVertexZCutRange.value[0]) { + return false; + } // vertex z cut + if (centralityMC > cfCentCutRange.value[1] || centralityMC < cfCentCutRange.value[0]) { + return false; + } // centrality cut } } } @@ -598,7 +649,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to { if constexpr (rs == eRec || rs == eRecAndSim) { // Fill reconstructed-level event histograms - if (rm == eReal) { + if constexpr (rm == eReal) { auto thisCent = chooseCent(collision, centralityEstimator); auto thisRefMult = chooseMult(collision, multiplicityTables); int multiplicityRec = static_cast(tracks.size()); @@ -621,34 +672,31 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed - if constexpr (rs == eRecAndSim) { + if constexpr (rs == eRecAndSim && rm == eMC) { if (!collision.has_mcCollision()) { - // LOGF(warning, "No MC collision for this collision, skip..."); return; } - if (rm == eMC) { - auto thisMCCollision = collision.mcCollision(); // corresponding MC truth simulated particle - int multiplicitySim = static_cast(tracks.size()); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter - if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); - } + auto thisMCCollision = collision.mcCollision(); + int multiplicitySim = static_cast(tracks.size()); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); + } - if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); - } - } // end of if (rm == eMC) { + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); + } } } } @@ -656,67 +704,63 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool particleCuts(T const& track) { + if constexpr (rs == eRecAndSim && rm == eMC) { + if (!track.has_mcParticle()) { + return false; + } + } if constexpr (rs == eRec || rs == eRecAndSim) { if (cfPtCutSwitch) // pt cuts for Rec { - if (rm == eReal) { + if constexpr (rm == eReal) { if (track.pt() < cfPtCutRange.value[0] || track.pt() > cfPtCutRange.value[1]) { return false; } } - if constexpr (rs == eRecAndSim) // pt cuts for Sim + if constexpr (rs == eRecAndSim && rm == eMC) // pt cuts for Sim { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - if (mcParticle.pt() < cfPtCutRange.value[0] || mcParticle.pt() > cfPtCutRange.value[1]) { - return false; - } - } // end of if (rm == eMC) { + auto thisMCParticle = track.mcParticle(); + if (thisMCParticle.pt() < cfPtCutRange.value[0] || thisMCParticle.pt() > cfPtCutRange.value[1]) { + return false; + } } } if (cfEtaCutSwitch) // eta cuts for Rec { - if (rm == eReal) { + if constexpr (rm == eReal) { if (track.eta() < cfEtaCutRange.value[0] || track.eta() > cfEtaCutRange.value[1]) { return false; } } - if constexpr (rs == eRecAndSim) // eta cuts for Sim + if constexpr (rs == eRecAndSim && rm == eMC) // eta cuts for Sim { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - if (mcParticle.eta() < cfEtaCutRange.value[0] || mcParticle.eta() > cfEtaCutRange.value[1]) { - return false; - } - } // end of if (rm == eMC) { + auto thisMCParticle = track.mcParticle(); + if (thisMCParticle.eta() < cfEtaCutRange.value[0] || thisMCParticle.eta() > cfEtaCutRange.value[1]) { + return false; + } } } if (cfChargeCutSwitch) // charge cuts for Rec { - if (rm == eReal) { - if (track.sign() == 0) { + if constexpr (rm == eReal) { + if (track.sign() != 1 && track.sign() != -1) { return false; } } - if constexpr (rs == eRecAndSim) // charge cuts for Sim + if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim { - if (rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto mcParticle = track.mcParticle(); - auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); - if (chargeMC == 0) { + auto thisMCParticle = track.mcParticle(); + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + if (particlePDG) { + const int chargeMCUnit = 3; + auto chargeMC = particlePDG->Charge() / chargeMCUnit; + if (chargeMC != 1 && chargeMC != -1) { return false; } - } // end of if (rm == eMC) { - } + } else { + return false; + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim } // end of if (cfChargeCutSwitch) // charge cuts for Rec } // end of if constexpr (rs == eRec || rs == eRecAndSim) { @@ -727,7 +771,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to void particleHistFill(T1 const& track) { if constexpr (rs == eRec || rs == eRecAndSim) { - if (rm == eReal) { + if constexpr (rm == eReal) { if constexpr (cuts == eBefore) { pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); @@ -743,30 +787,35 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - if constexpr (rs == eRecAndSim) { - if (rm == eMC) { - if (!track.has_mcParticle()) { - // LOGF(warning, " No MC particle for this track, skip..."); - return; - } - auto mcParticle = track.mcParticle(); - auto chargeMC = pdg->GetParticle(mcParticle.pdgCode())->Charge(); - const int chargeMCUnit = 3; + if constexpr (rs == eRecAndSim && rm == eMC) { + if (!track.has_mcParticle()) { + return; + } + auto thisMCParticle = track.mcParticle(); + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + float chargeMC = 0.f; + const int chargeMCUnit = 3; + if (particlePDG) { + chargeMC = particlePDG->Charge(); chargeMC /= chargeMCUnit; - if constexpr (cuts == eBefore) { - pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(mcParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(mcParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(mcParticle.eta()); + } + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(thisMCParticle.eta()); + if (particlePDG) { pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); } + } - if constexpr (cuts == eAfter) { - pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(mcParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(mcParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(mcParticle.eta()); + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(thisMCParticle.eta()); + if (particlePDG) { pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); } - } // end of if (rm == eMC) { + } } } } @@ -774,34 +823,49 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to void loadWeights(int runNumber) { for (int i = 0; i < eWeightsHistograms_N; ++i) { - for (int j = 0; j < eCuts_N; ++j) { - ex.fWeights[i][j] = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s][%s]", WeightsNames[i], CutsNames[j])); - if (!ex.fWeights[i][j]) { - LOGF(info, "[%s][%s] not found", WeightsNames[i], CutsNames[j]); - continue; - } - ex.fExternalHistogramsList->Add(ex.fWeights[i][j]); + ex.fWeights[i] = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s]", WeightsNames[i])); + if (!ex.fWeights[i]) { + continue; } + ex.fExternalHistogramsList->Add(ex.fWeights[i]); } } - template + template void qaFill(T1 const& collision) { auto thisCent = chooseCent(collision, centralityEstimator); - if constexpr (rs == eRecAndSim || rs == eSim) { + auto thisRefMult = chooseMult(collision, multiplicityTables); + if constexpr (rm == eReal) { + if constexpr (cuts == eBefore) { + qa.fHistMultNContrib[eBefore]->Fill(thisRefMult, collision.numContrib()); + } + if constexpr (cuts == eAfter) { + qa.fHistMultNContrib[eAfter]->Fill(thisRefMult, collision.numContrib()); + } + } + + if constexpr (rs == eRecAndSim && rm == eMC) { if (!collision.has_mcCollision()) { return; } auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter - qa.fHistCentralityRecSim->Fill(thisCent, centralityMC); + auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; + if constexpr (cuts == eBefore) { + qa.fHistCentralityRecSim[eBefore]->Fill(thisCent, centralityMC); + } + if constexpr (cuts == eAfter) { + qa.fHistCentralityRecSim[eAfter]->Fill(thisCent, centralityMC); + } } } // *) Misc: bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash + int collisionCounter = 0; + int thisRunNumber = 0; + const int runMessagePeriod = 100; // *) Define all member functions to be called in the main process* functions: template @@ -811,95 +875,123 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fDryRun) { return; } - const int thisRunNumber = collision.bc().runNumber(); - if (runMessageSwitch) { - LOGF(info, "Successfully running, run number is %d", thisRunNumber); - } + auto thisCollCent = chooseCent(collision, centralityEstimator); if (isFirstCollision) { - // Get run number - misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); - misc.fMiscHistRunNumber->SetBinContent(1, thisRunNumber); - // Get weights + thisRunNumber = collision.bc().runNumber(); + misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); // Get run number if (cfExternalFileSwitch) { - loadWeights(thisRunNumber); + loadWeights(thisRunNumber); // Get weights } } - // Fill Quality Assurance - if (qualityAssuranceSwitch) { - qaFill(collision); + if (runMessageSwitch) { + collisionCounter++; + if (collisionCounter % runMessagePeriod == 0) { + LOGF(info, "Successfully running, run number is %d", thisRunNumber); + collisionCounter = 0; + } } - const bool passesEventCutsReal = eventCuts(collision); - const bool passesEventCutsMC = eventCuts(collision); + // Fill Quality Assurance before cuts + if (qualityAssuranceSwitch) { + qaFill(collision); + qaFill(collision); + } - // Fill Event Hist + // Fill Event Hist before cuts eventHistFill(collision, tracks); eventHistFill(collision, tracks); + bool passEventCutsReal = eventCuts(collision); + bool passEventCutsMC = eventCuts(collision); + bool passTechnicalCut = technicalCuts(collision); + + // Fill event hist and qa hist after cuts if (cfMasterCutSwitch) { - if (passesEventCutsReal) { + if (passEventCutsReal && passTechnicalCut) { eventHistFill(collision, tracks); + if (qualityAssuranceSwitch) { + qaFill(collision); + } } - if (passesEventCutsMC) { + if (passEventCutsMC) { eventHistFill(collision, tracks); + if (qualityAssuranceSwitch) { + qaFill(collision); + } } } std::vector n2 = {-2, 2}; - auto qVectorsTableBeforeCutsReal = initQVectorsTable(2, n2); - auto qVectorsTableAfterCutsReal = initQVectorsTable(2, n2); + auto qVectorsTableMC = initQVectorsTable(2, n2); + auto qVectorsTableReal = initQVectorsTable(2, n2); // Main loop over particles: auto track = tracks.iteratorAt(0); // set the type and scope from one instance - for (int64_t i = 0; i < tracks.size(); i++) { - track = tracks.iteratorAt(i); - float thisPhi = track.phi(); + for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { + track = tracks.iteratorAt(nTrack); + + particleHistFill(track); + particleHistFill(track); + + float thisPhiRec = track.phi(); + float thisPhiSim = 0.f; float thisPt = track.pt(); - std::array thisPhiAndPt = {thisPhi, thisPt}; - std::array, 2> thisWeights = {{{1.f, 1.f}, {1.f, 1.f}}}; // {{wPhiBefore, wPhiAfter},{wPtBefore, wPtAfter}} - // eWeightsHistograms_N + bool hasMCP = false; + if constexpr (rs == eRecAndSim) { + hasMCP = track.has_mcParticle(); + if (hasMCP) { + thisPhiSim = track.mcParticle().phi(); + } + } + + std::array thisPhiAndPt = {thisPhiRec, thisPhiSim, thisPt}; + std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} + if (cfExternalFileSwitch) { for (int k = 0; k < eWeightsHistograms_N; ++k) { - for (int j = 0; j < eCuts_N; ++j) { - if (ex.fWeights[k][j]) { - thisWeights[k][j] = ex.fWeights[k][j]->GetBinContent(ex.fWeights[k][j]->FindBin(thisPhiAndPt[k])); - } + if (ex.fWeights[k]) { + thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); } } } - particleHistFill(track); - particleHistFill(track); - updateQVectorsTable(qVectorsTableBeforeCutsReal, thisPhi, thisWeights[ePhi][eBefore] * thisWeights[ePt][eBefore]); - if (cfMasterCutSwitch) { - if (passesEventCutsReal && particleCuts(track)) { + if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { particleHistFill(track); - updateQVectorsTable(qVectorsTableAfterCutsReal, thisPhi, thisWeights[ePhi][eAfter] * thisWeights[ePt][eAfter]); + updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeights[ePhiRec] * thisWeights[ePt]); } - if (passesEventCutsMC && particleCuts(track)) { + if (passEventCutsMC && particleCuts(track)) { particleHistFill(track); + if (hasMCP) { + updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeights[ePhiSim] * thisWeights[ePt]); + } } } - } // end of for (int64_t i = 0; i < tracks.size(); i++) { + } // end of for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { std::vector resultMultCorr(2, TComplex(0., 0.)); - resultMultCorr = two(qVectorsTableBeforeCutsReal, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eBefore]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); - } - if (cfMasterCutSwitch && passesEventCutsReal) { - resultMultCorr = two(qVectorsTableAfterCutsReal, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec][eAfter]->Fill(0.5, (resultMultCorr[0] / resultMultCorr[1].Re()).Re() / (1e-2)); + if (cfMasterCutSwitch) { + if (passEventCutsReal && passTechnicalCut) { + resultMultCorr = two(qVectorsTableReal, n2); + if (noneZeroDenom(resultMultCorr)) { + obs.fProfTwo[eRec]->Fill(thisCollCent, (resultMultCorr[0] / resultMultCorr[1].Re()).Re()); + } } - } - // Now the first collision ends - isFirstCollision = false; + if constexpr (rs == eRecAndSim) { + if (passEventCutsMC) { + resultMultCorr = two(qVectorsTableMC, n2); + if (noneZeroDenom(resultMultCorr)) { + obs.fProfTwo[eSim]->Fill(thisCollCent, (resultMultCorr[0] / resultMultCorr[1].Re()).Re()); + } + } // end of if (passEventCutsMC) { + } // end of if constexpr (rs == eRecAndSim) { + } // end of if (cfMasterCutSwitch) { + + isFirstCollision = false; // Now the first collision ends } // end of template void steer(T1 const& collision, T2 const& tracks) { // *) Initialize and book all objects: @@ -1050,8 +1142,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHistList->SetOwner(true); fBaseList->Add(ec.fEventHistList); - float defaultBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; - const int nDefaultBins = sizeof(defaultBoundaries) / sizeof(defaultBoundaries[0]) - 1; + float defaultCentBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; + const int nDefaultCentBins = sizeof(defaultCentBoundaries) / sizeof(defaultCentBoundaries[0]) - 1; std::vector lCent = cfCentBins.value; const int nBinsCent = static_cast(lCent[0]); @@ -1092,7 +1184,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessRec || doprocessRecSim) { if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); } @@ -1122,7 +1214,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (cfMasterCutSwitch) { if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); } @@ -1154,7 +1246,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessSim || doprocessRecSim) { if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); } @@ -1184,7 +1276,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (cfMasterCutSwitch) { if (cfALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultBins, defaultBoundaries); + ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); } @@ -1221,28 +1313,53 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(obs.fObservablesList); if (doprocessRec || doprocessRecSim) { - obs.fProfTwo[eRec][eBefore] = new TProfile("obs.fProfTwo[eRec][eBefore]", "obs.fProfTwo[eRec][eBefore]", 1, 0., 1); - obs.fProfTwo[eRec][eBefore]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eBefore]); + if (cfALICECentBinSwitch) { + obs.fProfTwo[eRec] = new TProfile("obs.fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nDefaultCentBins, defaultCentBoundaries); + } else { + obs.fProfTwo[eRec] = new TProfile("obs.fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nBinsCent, minCent, maxCent); + } + obs.fProfTwo[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fObservablesList->Add(obs.fProfTwo[eRec]); - if (cfMasterCutSwitch) { - obs.fProfTwo[eRec][eAfter] = new TProfile("obs.fProfTwo[eRec][eAfter]", "obs.fProfTwo[eRec][eAfter]", 1, 0., 1); - obs.fProfTwo[eRec][eAfter]->GetYaxis()->SetTitle("#LT#LTk#GT#GT / 10^{-k}"); - obs.fObservablesList->Add(obs.fProfTwo[eRec][eAfter]); + if (doprocessRecSim) { + if (cfALICECentBinSwitch) { + obs.fProfTwo[eSim] = new TProfile("obs.fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nDefaultCentBins, defaultCentBoundaries); + } else { + obs.fProfTwo[eSim] = new TProfile("obs.fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nBinsCent, minCent, maxCent); + } + obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fObservablesList->Add(obs.fProfTwo[eSim]); } } // *) Book and QA TLists: - if (qualityAssuranceSwitch && doprocessRecSim) { + if (qualityAssuranceSwitch) { + std::vector lContrib = cfContribBins.value; + const int nBinsContrib = static_cast(lContrib[0]); + const float minContrib = lContrib[1]; + const float maxContrib = lContrib[2]; qa.fQualityAssuranceList = new TList(); qa.fQualityAssuranceList->SetName("QualityAssurance"); qa.fQualityAssuranceList->SetOwner(true); fBaseList->Add(qa.fQualityAssuranceList); - qa.fHistCentralityRecSim = new TH2F("fHistCentralityRecSim", "Centrality Rec vs Sim", nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); - qa.fHistCentralityRecSim->GetXaxis()->SetTitle("Centrality (reconstructed)"); - qa.fHistCentralityRecSim->GetYaxis()->SetTitle("Centrality (simulated)"); - qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim); + if (doprocessRec || doprocessRecSim) { + for (int i = 0; i < eCuts_N; ++i) { + qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), nBinsMultRef, minMultRef, maxMultRef, nBinsContrib, minContrib, maxContrib); + qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); + qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); + } + } + + if (doprocessRecSim) { + for (int i = 0; i < eCuts_N; ++i) { + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); + qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); + qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); + qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); + } + } } } // end of void init(InitContext&) { From 14e3866f74d19a61ea8f5c8de8fd209de2ce1b20 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:35:00 +0200 Subject: [PATCH 163/648] [PWGCF] Add dca cuts in twoParticleCorrelationsMpi.cxx (#18152) --- .../Tasks/twoParticleCorrelationsMpi.cxx | 92 +++++++++++-------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index 0d681481ffb..f98d37b2c6d 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -116,6 +116,13 @@ struct TwoParticleCorrelationsMpi { Configurable cfgDropStepRECO{"cfgDropStepRECO", false, "choice to drop step RECO if efficiency correction is used"}; Configurable cfgCentBinsForMC{"cfgCentBinsForMC", 0, "0 = generated multiplicity; 1 = reconstructed multiplicity and all associated collisions"}; } cfgGeneral; + struct : ConfigurableGroup { + Configurable dcaxymax{"dcaxymax", 999.f, "maximum dcaxy of tracks"}; + Configurable dcazmax{"dcazmax", 999.f, "maximum dcaz of tracks"}; + Configurable enablePtDepDCAxy{"enablePtDepDCAxy", false, "Enable pT-dependent DCAxy cut: |DCAxy| < a + b/pT"}; + Configurable dcaXyConst{"dcaXyConst", 0.004f, "Constant term 'a' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm)"}; + Configurable dcaXySlope{"dcaXySlope", 0.013f, "Slope term 'b' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm x GeV/c)"}; + } cfgDCA; Configurable cfgTrackBitMask{"cfgTrackBitMask", 0, "BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task"}; Configurable cfgMultCorrelationsMask{"cfgMultCorrelationsMask", 0, "Selection bitmask for the multiplicity correlations. This should match the filter selection cfgEstimatorBitMask."}; Configurable cfgMultCutFormula{"cfgMultCutFormula", "", "Multiplicity correlations cut formula. A result greater than zero results in accepted event. Parameters: [cFT0C] FT0C centrality, [mFV0A] V0A multiplicity, [mGlob] global track multiplicity, [mPV] PV track multiplicity, [cFT0M] FT0M centrality"}; @@ -123,13 +130,16 @@ struct TwoParticleCorrelationsMpi { // Suggested values: Photon: 0.004; K0 and Lambda: 0.005 Configurable> cfgPairCut{"cfgPairCut", {CfgPairCutDefaults.front().data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; - Configurable cfgEfficiencyTrigger{"cfgEfficiencyTrigger", "", "CCDB path to efficiency object for trigger particles"}; - Configurable cfgEfficiencyAssociated{"cfgEfficiencyAssociated", "", "CCDB path to efficiency object for associated particles"}; - Configurable cfgAcceptance{"cfgAcceptance", "", "CCDB path to the GFW acceptance object"}; - Configurable cfgFillAcceptanceWeights{"cfgFillAcceptanceWeights", false, "Fill acceptance maps instead of physics outputs"}; - Configurable cfgAcceptanceRunByRun{"cfgAcceptanceRunByRun", false, "Fill or load run-by-run acceptance weights"}; - Configurable cfgAcceptancePhiBins{"cfgAcceptancePhiBins", int{DefaultAcceptancePhiBins}, "Number of phi bins in acceptance maps"}; - Configurable cfgAcceptanceEtaBins{"cfgAcceptanceEtaBins", int{DefaultAcceptanceEtaBins}, "Number of eta bins in acceptance maps"}; + struct : ConfigurableGroup { + Configurable cfgEfficiencyTrigger{"cfgEfficiencyTrigger", "", "CCDB path to efficiency object for trigger particles"}; + Configurable cfgEfficiencyAssociated{"cfgEfficiencyAssociated", "", "CCDB path to efficiency object for associated particles"}; + Configurable cfgAcceptance{"cfgAcceptance", "", "CCDB path to the GFW acceptance object"}; + Configurable cfgFillAcceptanceWeights{"cfgFillAcceptanceWeights", false, "Fill acceptance maps instead of physics outputs"}; + Configurable cfgAcceptanceRunByRun{"cfgAcceptanceRunByRun", false, "Fill or load run-by-run acceptance weights"}; + Configurable cfgAcceptancePhiBins{"cfgAcceptancePhiBins", int{DefaultAcceptancePhiBins}, "Number of phi bins in acceptance maps"}; + Configurable cfgAcceptanceEtaBins{"cfgAcceptanceEtaBins", int{DefaultAcceptanceEtaBins}, "Number of eta bins in acceptance maps"}; + } cfgCorrections; + Configurable cfgNuncSeedsTemplateFile{"cfgNuncSeedsTemplateFile", "", "Local ROOT file containing ensembleYieldTemplates"}; Configurable cfgNuncSeedsTemplate{"cfgNuncSeedsTemplate", "", "CCDB path to the ensemble-yield template ccdb_object"}; Configurable cfgMinPairAcceptance{"cfgMinPairAcceptance", 0.05f, "Minimum pair acceptance used by the event seed estimator"}; @@ -182,7 +192,11 @@ struct TwoParticleCorrelationsMpi { Filter collisionVertexTypeFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); // Track filters - Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && + (nabs(aod::track::dcaZ) < cfgDCA.dcazmax) && + ifnode(cfgDCA.enablePtDepDCAxy.node() == true, + nabs(aod::track::dcaXY) < (cfgDCA.dcaXyConst + cfgDCA.dcaXySlope / aod::track::pt), + nabs(aod::track::dcaXY) < cfgDCA.dcaxymax); Filter cfTrackFilter = (nabs(aod::cftrack::eta) < cfgGeneral.cfgCutEta) && (aod::cftrack::pt > cfgGeneral.cfgCutPt) && ncheckbit(aod::track::trackType, as(cfgTrackBitMask)); // MC filters @@ -327,7 +341,7 @@ struct TwoParticleCorrelationsMpi { Service pdg{}; using AodCollisions = soa::Filtered>; - using AodTracks = soa::Filtered>; + using AodTracks = soa::Filtered>; using DerivedCollisions = soa::Filtered; using DerivedCollisionsCorrected = soa::Filtered; @@ -361,16 +375,16 @@ struct TwoParticleCorrelationsMpi { (!(doprocessSameGenMC || doprocessMCSameDerived) || doprocessSameAOD || enabledDerivedSameProcesses > 0 || doprocessMixedAOD || enabledDerivedMixedProcesses > 0 || doprocessMCMixedDerived || - cfgGeneral.cfgCentBinsForMC != 0 || cfgFillAcceptanceWeights)) { + cfgGeneral.cfgCentBinsForMC != 0 || cfgCorrections.cfgFillAcceptanceWeights)) { LOGF(fatal, "cfgUserAxis=3 supports generated MC same events only: enable processSameGenMC or processMCSameDerived and disable all other same/mixed processes"); } - if (cfgFillAcceptanceWeights && !cfgAcceptance.value.empty()) { + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptance.value.empty()) { LOGF(fatal, "cfgFillAcceptanceWeights and cfgAcceptance are mutually exclusive: produce and apply acceptance weights in separate jobs"); } - if (cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { + if (cfgCorrections.cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { LOGF(fatal, "cfgFillAcceptanceWeights requires a reconstructed derived same-event process"); } - if (cfgAcceptancePhiBins < 1 || cfgAcceptanceEtaBins < 1) { + if (cfgCorrections.cfgAcceptancePhiBins < 1 || cfgCorrections.cfgAcceptanceEtaBins < 1) { LOGF(fatal, "Acceptance-map bin counts must be positive"); } if (!cfgNuncSeedsTemplateFile.value.empty() && !cfgNuncSeedsTemplate.value.empty()) { @@ -531,9 +545,9 @@ struct TwoParticleCorrelationsMpi { registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}}}); registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}, {10, -0.5, 9.5}}}); - if (cfgFillAcceptanceWeights && !cfgAcceptanceRunByRun) { - AxisSpec phiAxis{cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec etaAxis{cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptanceRunByRun) { + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; mAcceptanceWeights = registry.add("phi_eta_vtxz_ref", "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); } @@ -606,7 +620,7 @@ struct TwoParticleCorrelationsMpi { same->setTrackEtaCut(cfgGeneral.cfgCutEta); mixed->setTrackEtaCut(cfgGeneral.cfgCutEta); - if (!cfgEfficiencyAssociated.value.empty()) { + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { efficiencyAssociatedCache.reserve(512); } @@ -655,7 +669,7 @@ struct TwoParticleCorrelationsMpi { ++nPtPtSubevents; } - if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { auto recoProfiles = std::make_unique(); addConfigObjectsToObjArray(recoProfiles.get(), mCorrConfigs, cfgUserAxis == EventSeedAxis && eventClassifierPercentileAxisEnabled); fFC.setObject(new FlowContainer("FlowContainer")); @@ -669,7 +683,7 @@ struct TwoParticleCorrelationsMpi { fFCpt->initialise(axisMultiplicity, MaxPtCorrelationOrder, cfgCorrConfig.value, FlowNBootstrap); fFCpt->initialiseSubevent(axisMultiplicity, MaxPtCorrelationOrder, nPtPtSubevents, FlowNBootstrap); } - if (!cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameGenMC)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameGenMC)) { auto generatedProfiles = std::make_unique(); addConfigObjectsToObjArray(generatedProfiles.get(), mCorrConfigs, cfgUserAxis == NMPIAxis && eventClassifierPercentileAxisEnabled); fFCGen.setObject(new FlowContainer("FlowContainer_gen")); @@ -862,8 +876,8 @@ struct TwoParticleCorrelationsMpi { return existing->second; } - AxisSpec phiAxis{cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec etaAxis{cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; const std::string histogramName = Form("%d/phi_eta_vtxz_ref", runNumber); const auto histogram = registry.add(histogramName.c_str(), "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); mAcceptanceWeightsByRun.emplace(runNumber, histogram); @@ -876,7 +890,7 @@ struct TwoParticleCorrelationsMpi { if (track.pt() <= fPtAxis->GetXmin() || track.pt() >= fPtAxis->GetXmax()) { return; } - const auto histogram = cfgAcceptanceRunByRun ? getAcceptanceWeightsForRun(runNumber) : mAcceptanceWeights; + const auto histogram = cfgCorrections.cfgAcceptanceRunByRun ? getAcceptanceWeightsForRun(runNumber) : mAcceptanceWeights; histogram->Fill(track.phi(), track.eta(), posZ); } @@ -1983,46 +1997,46 @@ struct TwoParticleCorrelationsMpi { if (cfg.efficiencyLoaded) { return; } - if (!cfgEfficiencyTrigger.value.empty()) { + if (!cfgCorrections.cfgEfficiencyTrigger.value.empty()) { if (cfgGeneral.cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); + TFile* fEfficiencyTrigger = TFile::Open(cfgCorrections.cfgEfficiencyTrigger.value.c_str(), "READ"); cfg.mEfficiencyTrigger = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyTrigger, timestamp); } if (cfg.mEfficiencyTrigger == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); } - if (!cfgEfficiencyAssociated.value.empty()) { + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { if (cfgGeneral.cfgLocalEfficiency > 0) { - TFile* fEfficiencyAssociated = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); + TFile* fEfficiencyAssociated = TFile::Open(cfgCorrections.cfgEfficiencyAssociated.value.c_str(), "READ"); cfg.mEfficiencyAssociated = dynamic_cast(fEfficiencyAssociated->Get("ccdb_object")); } else { - cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyAssociated, timestamp); } if (cfg.mEfficiencyAssociated == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); + LOGF(info, "Loaded efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); } cfg.efficiencyLoaded = true; } void loadAcceptance(const uint64_t timestamp, const int runNumber) { - if (cfgFillAcceptanceWeights || cfgAcceptance.value.empty()) { + if (cfgCorrections.cfgFillAcceptanceWeights || cfgCorrections.cfgAcceptance.value.empty()) { cfg.mAcceptance = nullptr; cfg.acceptanceLoaded = true; return; } - if (cfg.acceptanceLoaded && (!cfgAcceptanceRunByRun || cfg.acceptanceRunNumber == runNumber)) { + if (cfg.acceptanceLoaded && (!cfgCorrections.cfgAcceptanceRunByRun || cfg.acceptanceRunNumber == runNumber)) { return; } - std::string path = cfgAcceptance.value; - if (cfgAcceptanceRunByRun) { + std::string path = cfgCorrections.cfgAcceptance.value; + if (cfgCorrections.cfgAcceptanceRunByRun) { if (path.back() != '/') { path += '/'; } @@ -2099,7 +2113,7 @@ struct TwoParticleCorrelationsMpi { soa::Filtered const& mcParticles, soa::SmallGroups const& collisions) { - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { return; } if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { @@ -2168,7 +2182,7 @@ struct TwoParticleCorrelationsMpi { if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), multiplicity); } - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { for (const auto& track : tracks2) { fillAcceptanceWeights(track, collision.posZ(), collision.runNumber()); } @@ -2433,7 +2447,7 @@ struct TwoParticleCorrelationsMpi { template void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, soa::SmallGroups const& collisions) { - if (cfgFillAcceptanceWeights) { + if (cfgCorrections.cfgFillAcceptanceWeights) { return; } if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { From ca1c3d8905de183b6a4c7652802642ac21e2763b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:54:43 +0200 Subject: [PATCH 164/648] [Tutorial,PWGCF,PWGHF,PWGLF] Remove default value for ignoring overflow when binning (#18163) --- PWGCF/Femto/Tasks/femtoPairD0D0.cxx | 6 ++-- .../Tasks/femtoPairMcParticleMcParticle.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 6 ++-- .../Femto/Tasks/femtoPairTrackCharmHadron.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 8 ++--- .../Tasks/femtoPairTrackTwoTrackResonance.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 6 ++-- .../Tasks/femtoPairV0TwoTrackResonance.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairV0V0.cxx | 6 ++-- ...oTripletMcParticleMcParticleMcParticle.cxx | 6 ++-- .../Tasks/femtoTripletTrackTrackCascade.cxx | 6 ++-- .../Tasks/femtoTripletTrackTrackTrack.cxx | 6 ++-- .../Femto/Tasks/femtoTripletTrackTrackV0.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskTrackCascade.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskTrackTrack.cxx | 10 +++--- .../Tasks/femtoDreamPairTaskTrackV0.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskV0Reso.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskV0V0.cxx | 6 ++-- ...femtoDreamTripletTaskTrackTrackCascade.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackTrack.cxx | 2 +- ...mtoDreamTripletTaskTrackTrackTrackPbPb.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackV0.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackV0PbPb.cxx | 2 +- .../Tasks/femtodreamPairCascadeCascade.cxx | 6 ++-- ...toUniversePairTaskTrackCascadeExtended.cxx | 12 +++---- .../Tasks/femtoUniversePairTaskTrackD0.cxx | 2 +- .../femtoUniversePairTaskTrackNucleus.cxx | 2 +- .../Tasks/femtoUniversePairTaskTrackPhi.cxx | 2 +- .../Tasks/femtoUniversePairTaskTrackTrack.cxx | 2 +- ...ersePairTaskTrackTrack3DMultKtExtended.cxx | 4 +-- ...emtoUniversePairTaskTrackTrackExtended.cxx | 2 +- .../femtoUniversePairTaskTrackTrackMC.cxx | 2 +- ...femtoUniversePairTaskTrackTrackMcTruth.cxx | 2 +- ...iversePairTaskTrackTrackMultKtExtended.cxx | 4 +-- ...irTaskTrackTrackSpherHarMultKtExtended.cxx | 8 ++--- ...TrackTrackSpherHarMultKtExtendedItsPid.cxx | 8 ++--- .../femtoUniversePairTaskTrackV0Extended.cxx | 32 +++++++++---------- .../femtoUniversePairTaskTrackV0Helicity.cxx | 16 +++++----- ...femtoUniversePairTaskV0CascadeExtended.cxx | 2 +- .../Tasks/femtoPairLambdaAntilambda.cxx | 6 ++-- .../Tasks/femtoWorldPairTaskPionPion.cxx | 2 +- .../femtoWorldPairTaskProtonAntiproton.cxx | 2 +- .../Tasks/femtoWorldPairTaskTrackD0.cxx | 2 +- .../Tasks/femtoWorldPairTaskTrackPhi.cxx | 2 +- .../Tasks/femtoWorldPairTaskTrackTrack.cxx | 2 +- .../Tasks/femtoWorldPionAllPairTask.cxx | 4 +-- .../femtoWorldPionPairTaskTrackTrack.cxx | 2 +- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 6 ++-- .../Tasks/taskCharmHadronsTrackFemtoDream.cxx | 6 ++-- .../Tasks/taskCharmHadronsV0FemtoDream.cxx | 6 ++-- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 2 +- .../forwardlambdakzeroanalysis.cxx | 2 +- Tutorials/src/tracksCombinations.cxx | 4 +-- 54 files changed, 142 insertions(+), 142 deletions(-) diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 66c7decf0f4..675c567a08b 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -124,9 +124,9 @@ struct FemtoPairD0D0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoD0D0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index 7be627972f8..4d449c44446 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -98,9 +98,9 @@ struct FemtoPairMcParticleMcParticle { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 4e70a7ab76f..78eb2da5dd5 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -150,9 +150,9 @@ struct FemtoPairTrackCascade { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 600c3c3c033..8c52006cd68 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -152,9 +152,9 @@ struct FemtoPairTrackCharmHadron { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackD0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index bbf0da77024..66b343d1729 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -141,9 +141,9 @@ struct FemtoPairTrackKink { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackKink", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 818db97e7eb..d9368a03308 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -118,10 +118,10 @@ struct FemtoPairTrackTrack { std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; std::vector defaultEventPlaneAngleBins{10, 0, o2::constants::math::TwoPI}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCentEventPlaneAngle{{defaultVtxBins, defaultCentBins, defaultEventPlaneAngleBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCentEventPlaneAngle{{defaultVtxBins, defaultCentBins, defaultEventPlaneAngleBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 5197f851bcb..661c3400c3e 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -140,9 +140,9 @@ struct FemtoPairTrackTwoTrackResonance { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 224567da38e..062ca3dd1df 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -143,9 +143,9 @@ struct FemtoPairTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index 68ddc7d277f..52401a050a4 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -229,9 +229,9 @@ struct FemtoPairV0TwoTrackResonance { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index dd676c5e43c..ce35f66d2a8 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -179,9 +179,9 @@ struct FemtoPairV0V0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx index fc4c36b8ed6..4200daf64a5 100644 --- a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -101,9 +101,9 @@ struct FemtoTripletMcParticleMcParticleMcParticle { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index b84c7418248..03054d45c57 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -170,9 +170,9 @@ struct FemtoTripletTrackTrackCascade { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 199038f6372..6a89df0a620 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -116,9 +116,9 @@ struct FemtoTripletTrackTrackTrack { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index aa1610d48e1..ee0d984245d 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -136,9 +136,9 @@ struct FemtoTripletTrackTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx index 1c793e973e3..7bfe2261732 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx @@ -211,9 +211,9 @@ struct femtoDreamPairTaskTrackCascade { Configurable Depth{"Depth", 5, "Number of events for mixing"}; Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index 22e69c80e49..70a83dc2dac 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -258,11 +258,11 @@ struct femtoDreamPairTaskTrackTrack { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both, 3: multipliciy percentile and qn value, 4: multipliciy percentile and event plane"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx index da2559e82ba..f58c5e48363 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx @@ -224,9 +224,9 @@ struct femtoDreamPairTaskTrackV0 { Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx index 290682891c7..95e1a8a106e 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx @@ -223,9 +223,9 @@ struct FemtoDreamPairTaskV0Reso { (aod::femtodreamparticle::mLambda > Reso2.invMassMin) && (aod::femtodreamparticle::mLambda < Reso2.invMassMax); - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= EventSel.multMin && aod::femtodreamcollision::multNtr <= EventSel.multMax; Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= EventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= EventSel.multPercentileMax; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx index 82ba66ed43e..01cdfbee150 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx @@ -208,9 +208,9 @@ struct femtoDreamPairTaskV0V0 { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx index d1e127065f0..8b7c1ffff6f 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx @@ -131,7 +131,7 @@ struct femtoDreamTripletTaskTrackTrackCascade { ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; Configurable ConfNEventsMix{"ConfNEventsMix", 5, "Number of events for mixing"}; Configurable ConfIsCPR{"ConfIsCPR", true, "Close Pair Rejection"}; Configurable ConfFillCPRQA{"ConfFillCPRQA", false, "Fill Close Pair Rejection plots as a function of eta and phi"}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx index 1b5bf143300..57480dd85c3 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx @@ -122,7 +122,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx index 8be1593da40..0377a32daee 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx @@ -129,7 +129,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx index 84bcb611cae..dede75a4906 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx @@ -167,7 +167,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx index e74ec599d5c..a2ab0840f08 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx @@ -179,7 +179,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 23.0f, 38.0f, 53.0f, 81.0f, 110.0f, 157.0f, 205.0f, 278.0f, 351.0f, 455.0f, 559.0f, 703.0f, 848.0f, 1050.0f, 1253.0f, 1530.0f, 1668.0f, 1857.0f, 2047.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confQ3Bins{"confQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis confQ3BinsFor4D{"confQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; diff --git a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx index e68ed83f7d3..11edf4705cd 100644 --- a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx @@ -200,9 +200,9 @@ struct FemtoDreamPairCascadeCascade { Configurable depth{"depth", 5, "Number of events for mixing"}; Configurable binPolicy{"binPolicy", 0, "Binning binPolicy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx index bd7362e67b0..b9a740f4874 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx @@ -1033,8 +1033,8 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1132,7 +1132,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEventCasc(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1229,7 +1229,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p template void doMixedEventTrack(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1379,7 +1379,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p // MC truth for track - cascade void processMixedEventMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); @@ -1408,7 +1408,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-p // MC truth for cascade - cascade void processMixedEventCascMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index 0e54930d4df..fce3a57e78c 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -249,7 +249,7 @@ struct FemtoUniversePairTaskTrackD0 { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx index 51f8ac5fd31..037c88b9837 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx @@ -191,7 +191,7 @@ struct FemtoUniversePairTaskTrackNucleus { ConfigurableAxis confMtBins3D{"confMtBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "Multiplicity binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confKstarBins{"confKstarBins", {1500, 0., 6.}, "Binning kstar"}; ConfigurableAxis confKtBins{"confKtBins", {150, 0., 9.}, "Binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index 3cd734acc13..aa6e8bb7cc8 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -217,7 +217,7 @@ struct FemtoUniversePairTaskTrackPhi { HistogramRegistry registryDCA{"registryDCA", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registryMCpT{"registryMCpT", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - ColumnBinningPolicy colBinning{{confBinsVtx, confBinsMult}, true}; + ColumnBinningPolicy colBinning{{confBinsVtx, confBinsMult}}; HistogramRegistry effCorrRegistry{"EfficiencyCorrection", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx index 6874668ca40..4b410fb8fca 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx @@ -121,7 +121,7 @@ struct FemtoUniversePairTaskTrackTrack { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx index 70f923f112b..def18f1deb1 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx @@ -176,8 +176,8 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {300, -1.5, 1.5}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx index 324bf757704..fe90355be5b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx @@ -166,7 +166,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx index 4b4fa24fff5..b0802f6521b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx @@ -157,7 +157,7 @@ struct femtoUniversePairTaskTrackTrackMC { ConfigurableAxis ConfmTBins3D{"ConfmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis ConfmultBins3D{"ConfmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfkstarBins{"ConfkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis ConfkTBins{"ConfkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx index 0d3634683c1..297cc03cfbd 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx @@ -106,7 +106,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx index 19b0e9255da..e1f2f92f773 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx @@ -208,7 +208,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; struct : o2::framework::ConfigurableGroup { Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; @@ -487,7 +487,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { void init(InitContext&) { - colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}, true}; + colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}}; eventHisto.init(&qaRegistry); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx index 465df0a76a6..3f622883254 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx @@ -264,8 +264,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -482,8 +482,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended { void init(InitContext&) { - colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}, true}; - colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}, true}; + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; eventHisto.init(&qaRegistry); trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx index 1c0c0cd0472..10ababfb0a5 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx @@ -253,8 +253,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -477,8 +477,8 @@ struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid { void init(InitContext&) { - colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}, true}; - colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}, true}; + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; eventHisto.init(&qaRegistry); trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx index a3ec2c9f607..1e3738446d9 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx @@ -1116,8 +1116,8 @@ struct FemtoUniversePairTaskTrackV0Extended { template void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1221,8 +1221,8 @@ struct FemtoUniversePairTaskTrackV0Extended { template void doMixedEventV0(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1403,8 +1403,8 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); @@ -1453,8 +1453,8 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); @@ -1545,8 +1545,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractions(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1621,8 +1621,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsV0(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; @@ -1737,8 +1737,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruth(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1774,8 +1774,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruthV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx index e59dfd95745..f7501d0388c 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx @@ -866,8 +866,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { template void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partitionOne, PartitionType& partitionTwo, int helRange, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1000,8 +1000,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes the mixed event for V0 - V0 void processMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1068,8 +1068,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1116,8 +1116,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx index 45d430a626a..47666260743 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx @@ -381,7 +381,7 @@ struct FemtoUniversePairTaskV0CascadeExtended { template void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = collision1.multNtr(); diff --git a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx index 97f8b7ec190..affd2abea83 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx @@ -149,9 +149,9 @@ struct FemtoPairLambdaAntilambda { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx index 5dd434d6b97..40ddbac7a6d 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx @@ -157,7 +157,7 @@ struct femtoWorldPairTaskPionPion { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx index 6ded175155f..44e36a37e98 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx @@ -153,7 +153,7 @@ struct femtoWorldPairTaskProtonAntiproton { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx index 5ca9109e6e7..1357578f0fa 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx @@ -367,7 +367,7 @@ struct femtoWorldPairTaskTrackD0 { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx index 426ca4af935..c339defa68e 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx @@ -327,7 +327,7 @@ struct femtoWorldPairTaskTrackPhi { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx index 1711a4fdc48..f5f84aff409 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx @@ -156,7 +156,7 @@ struct femtoWorldPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx index 248225ecd46..95bd3db42ec 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx @@ -115,8 +115,8 @@ struct FemtoWorldIdenticalPionPair { // Configurable> CfgVtxBins{"CfgVtxBins",{-10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f},"Mixing bins - z-vertex"}; // Configurable> CfgCentBins{"CfgCentBins",{5.0f,10.0f,20.0f,30.0f,40.0f,50.0f},"Centrality Bins"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; - ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; + ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx index fdedf6e0870..200ea018be9 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx @@ -157,7 +157,7 @@ struct femtoWorldPionPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index 264b4ac2d25..bf417b861a7 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -113,9 +113,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; HistogramRegistry registry{"registry"}; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx index aeb4e57e88d..f2e942ca582 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx @@ -263,9 +263,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.0f, 100.f}, "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx index 4ffdd61583c..44145c8f499 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx @@ -279,9 +279,9 @@ struct HfTaskCharmHadronsV0FemtoDream { ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; } AxisBinning; - ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 3c0c6b0ee6b..0bd761901d2 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -150,7 +150,7 @@ struct HadronNucleiCorrelation { // Mixing parameters ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 50.0f, 100.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; // pT/A bins Configurable> pTBins{"pTBins", {0.6f, 1.0f, 1.2f, 2.f}, "p_{T} bins"}; diff --git a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx index c73a5926a7a..f524b7dfa8e 100644 --- a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx @@ -359,7 +359,7 @@ struct forwardlambdakzeroanalysis { // Build the mixing binning locally: a struct member initialized from a // ConfigurableAxis captures the default bins at task construction time - ColumnBinningPolicy bkgColBinning{{eventMixingConfigurations.axisVertexMixing, eventMixingConfigurations.axisCentralityMixing}, true}; + ColumnBinningPolicy bkgColBinning{{eventMixingConfigurations.axisVertexMixing, eventMixingConfigurations.axisCentralityMixing}}; // Taken from https://github.com/AliceO2Group/O2Physics/blob/master/PWGLF/TableProducer/Strangeness/sigma0builder.cxx#L319 // Thanks Gianni! diff --git a/Tutorials/src/tracksCombinations.cxx b/Tutorials/src/tracksCombinations.cxx index a4ecbf1047e..6d73be618ab 100644 --- a/Tutorials/src/tracksCombinations.cxx +++ b/Tutorials/src/tracksCombinations.cxx @@ -60,7 +60,7 @@ struct TrackCombinations { struct BinnedTrackCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; void process(aod::Tracks const& tracks) { @@ -100,7 +100,7 @@ struct ConfigurableBinnedCollisionCombinations { struct BinnedTrackPartitionsCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; Configurable philow{"phiLow", 1.0f, "lowest phi"}; void process(aod::Tracks const& tracks) From dd11b07f32b5d394e0e228745fcbb39a9a211310 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Sat, 3 Oct 2026 17:34:28 +0200 Subject: [PATCH 165/648] [PWGLF] Fix bug + add feeddown QA histograms (#18162) --- .../derivedlambdakzeroanalysis.cxx | 78 +++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index e07590e4e81..04029b5d54d 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -131,9 +131,10 @@ struct derivedlambdakzeroanalysis { // for MC Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; - Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + Configurable doTreatPiToMuon{"doTreatPiToMuon", true, "Take pi decay into muon into account in MC"}; Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; Configurable doSecondaryV0s{"doSecondaryV0s", 0, "Look at secondary V0s? 0: No; 1: yes via a loop on V0MCCores; 2: yes via a loop on CascMCCores"}; + Configurable doQAfeeddown{"doQAfeeddown", true, "if MC, fill feeddown QA histograms"}; struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name @@ -374,6 +375,8 @@ struct derivedlambdakzeroanalysis { // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + + ConfigurableAxis axisPDGCodeMothers{"axisPDGCodeMothers", {7001, -3500.5, 3500.5}, "PDG code of mother particle"}; } axisConfigurations; // UPC selections @@ -457,6 +460,7 @@ struct derivedlambdakzeroanalysis { uint64_t maskSelectionLambda = 0; uint64_t maskSelectionAntiLambda = 0; + uint64_t secondaryMaskSelectionK0Short = 0; uint64_t secondaryMaskSelectionLambda = 0; uint64_t secondaryMaskSelectionAntiLambda = 0; @@ -669,6 +673,7 @@ struct derivedlambdakzeroanalysis { BITSET(maskSelectionAntiLambda, selPhysPrimAntiLambda); // No primary requirement for feeddown matrix + secondaryMaskSelectionK0Short = maskTopological | maskTrackProperties | maskK0ShortSpecific; secondaryMaskSelectionLambda = maskTopological | maskTrackProperties | maskLambdaSpecific; secondaryMaskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; @@ -1115,6 +1120,25 @@ struct derivedlambdakzeroanalysis { } } + if (analyseK0Short && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dK0ShortMothers", "h2dK0ShortMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dK0ShortMothersIsPrimary", "h2dK0ShortMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothers", "h3dK0ShortMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothersIsPrimary", "h3dK0ShortMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dLambdaMothers", "h2dLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dLambdaMothersIsPrimary", "h2dLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothers", "h3dLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothersIsPrimary", "h3dLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseAntiLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dAntiLambdaMothers", "h2dAntiLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dAntiLambdaMothersIsPrimary", "h2dAntiLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothers", "h3dAntiLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothersIsPrimary", "h3dAntiLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseK0Short) histos.add("hMassK0Short", "hMassK0Short", kTH1D, {axisConfigurations.axisK0Mass}); if (analyseLambda) @@ -1706,18 +1730,22 @@ struct derivedlambdakzeroanalysis { if (v0.pdgCode() == PDG_t::kK0Short && isPositivePion && isNegativePion) { BITSET(bitMap, selConsiderK0Short); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimK0Short); + LOG(info) << "coucou1"; + } } if (v0.pdgCode() == PDG_t::kLambda0 && isPositiveProton && isNegativePion) { BITSET(bitMap, selConsiderLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimLambda); + } } if (v0.pdgCode() == PDG_t::kLambda0Bar && isPositivePion && isNegativeProton) { BITSET(bitMap, selConsiderAntiLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimAntiLambda); + } } return bitMap; } @@ -2375,6 +2403,43 @@ struct derivedlambdakzeroanalysis { } } + template + void fillFeeddownQA(TV0 const& v0, float pt, float centrality, uint64_t selMap) + // fill feeddown matrix for Lambdas or AntiLambdas + // fixme: a potential improvement would be to consider mass windows for the l/al + { + if (!v0.has_motherMCPart()) + return; // does not have mother particle in record, skip + + auto v0mother = v0.motherMCPart(); + + // __________________________________________ + if (verifyMask(selMap, secondaryMaskSelectionK0Short) && analyseK0Short) { + histos.fill(HIST("h2dK0ShortMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dK0ShortMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionLambda) && analyseLambda) { + histos.fill(HIST("h2dLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionAntiLambda) && analyseAntiLambda) { + histos.fill(HIST("h2dAntiLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dAntiLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + } + template bool isEventAccepted(TCollision const& collision, bool fillHists) // check whether the collision passes our collision selections @@ -3000,13 +3065,16 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); uint64_t selMap = computeReconstructionBitmap(v0, collision, yK0Short, yLambda); - BITSET(selMap, selMapMCassociation); + selMap |= selMapMCassociation; // feeddown matrix always with association if constexpr (requires { v0.motherMCPartId(); }) { if (calculateFeeddownMatrix) { fillFeeddownMatrix(v0, ptMC, centrality, selMap); } + if (doQAfeeddown) { + fillFeeddownQA(v0, ptMC, centrality, selMap); + } } // consider only associated candidates if asked to do so, disregard association From 03a25b66f60f1fc4c9482d28d29b1ccfde020a48 Mon Sep 17 00:00:00 2001 From: Akash-Raj-285 <157659199+Akash-Raj-285@users.noreply.github.com> Date: Sat, 3 Oct 2026 17:07:29 -0400 Subject: [PATCH 166/648] [PWGCF] Lambda spin analysis (#18159) --- .../lambdaOrAntiLambdaProducerWithSpin.cxx | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx index fb9decce085..13e8add711b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx @@ -1520,6 +1520,7 @@ struct LambdaAntiLambdaPairAnalysis { // Mixed-Event Compatablity variables Configurable mixedEventMaxDeltaMultiplicity{"mixedEventMaxDeltaMultiplicity", 10.f, "Maximum multiplicity difference between mixed events"}; + Configurable maxMixedEvents{"maxMixedEvents", 5, "Maximum multiplicity-compatible collision combinations with selected candidates in both collisions per dataframe"}; // Short-range pairs Configurable sameEventShortRangePairMaxDeltaRapidity{"sameEventShortRangePairMaxDeltaRapidity", 0.5f, "ShortRangePai Maximum absolute rapidity difference for same-event pairs"}; @@ -2258,7 +2259,7 @@ struct LambdaAntiLambdaPairAnalysis { Preslice lambdasPerCollision = aod::lambdahyperon::collisionId; Preslice antiLambdasPerCollision = aod::lambdahyperon::collisionId; - void processData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + void processSameEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) { static constexpr int MinimumSameSpeciesCandidates = 2; @@ -2282,15 +2283,20 @@ struct LambdaAntiLambdaPairAnalysis { fillAntiLambdaAntiLambdaSameEvent(antiLambdasThisCollision); } } + } - // ============================================================ - // Mixed events - // ============================================================ + void processMixedEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + { + int mixedEventCounter = 0; for (auto const& [collision1, collision2] : combinations(CombinationsStrictlyUpperIndexPolicy(collisions, collisions))) { const float deltaMultiplicity = std::abs(static_cast(collision1.multNTracksPV()) - static_cast(collision2.multNTracksPV())); + if (mixedEventCounter >= maxMixedEvents) { + break; + } + if (deltaMultiplicity > mixedEventMaxDeltaMultiplicity) { continue; } @@ -2301,6 +2307,12 @@ struct LambdaAntiLambdaPairAnalysis { const auto antiLambdas1 = antiLambdas.sliceBy(antiLambdasPerCollision, collision1.globalIndex()); const auto antiLambdas2 = antiLambdas.sliceBy(antiLambdasPerCollision, collision2.globalIndex()); + // Empty collisions must not consume the mixing allowance. + if ((lambdas1.size() == 0 && antiLambdas1.size() == 0) || + (lambdas2.size() == 0 && antiLambdas2.size() == 0)) { + continue; + } + // Lambda–anti-Lambda requires at least one of each. if (lambdas1.size() > 0 && antiLambdas2.size() > 0) { fillLambdaAntiLambdaMixedEvent(lambdas1, antiLambdas2); @@ -2316,33 +2328,18 @@ struct LambdaAntiLambdaPairAnalysis { if (antiLambdas1.size() > 0 && antiLambdas2.size() > 0) { fillAntiLambdaAntiLambdaMixedEvent(antiLambdas1, antiLambdas2); } + + ++mixedEventCounter; } } + void processDummy(aod::Collisions const&) {} - PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processSameEventData, "Run same-event pair analysis", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processMixedEventData, "Run mixed-event pair analysis", false); PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processDummy, "Skip candidate production", false); }; -/*WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - const bool isMC = cfgc.options().get("isMC"); - - WorkflowSpec workflow; - - if (isMC) { - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - } else { - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - workflow.push_back(adaptAnalysisTask(cfgc)); - } - - return workflow; -}*/ - WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ From 7ca8667a70ae4b270305bc00ccf650360b62d727 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Sun, 4 Oct 2026 11:22:51 +0530 Subject: [PATCH 167/648] [PWGCF] Update of lambda proton bf 4 (#18164) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 278 ++++++++++++++++-- 1 file changed, 248 insertions(+), 30 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index a039d4f3da0..f6b7b71026f 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -61,7 +61,8 @@ using namespace o2::framework; using namespace o2::framework::expressions; using MyTracks = soa::Join; namespace @@ -244,6 +245,8 @@ struct LambdaProtonBalanceFunction { Configurable cFillEtaSpace{"cFillEtaSpace", false, "Fill eta-space rho1/rho2 histograms"}; Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; Configurable cQinvCutValue{"cQinvCutValue", 0.01f, "q_inv cut value (GeV/c)"}; + Configurable cQinvQABins{"cQinvQABins", 6000, "Number of bins for QA q_inv histograms"}; + Configurable cQinvQAMax{"cQinvQAMax", 6.0f, "Max q_inv for QA histograms (GeV/c)"}; Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; Configurable cUseFT0C{"cUseFT0C", false, "Use FT0C instead of FT0M as centrality estimator"}; ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; @@ -279,6 +282,13 @@ struct LambdaProtonBalanceFunction { Configurable lambdaV0DaughterProtonTPCNsigma{"lambdaV0DaughterProtonTPCNsigma", 2.0f, "lambdaV0DaughterProtonTPCNsigma"}; Configurable pProtonTOFNsigma{"pProtonTOFNsigma", 2.0f, "pProtonTOFNsigma"}; + // Contaminant veto for primary (anti)protons: reject track if compatible with pi/K/e + Configurable cUseContaminantVeto{"cUseContaminantVeto", true, "Reject primary (anti)protons compatible with pi/K/e hypotheses"}; + Configurable cVetoUseTOF{"cVetoUseTOF", true, "Also apply TOF veto whenever the track has TOF"}; + Configurable pProtonVetoPionNsigma{"pProtonVetoPionNsigma", 3.0f, "Reject if |nSigma_pi| < this"}; + Configurable pProtonVetoKaonNsigma{"pProtonVetoKaonNsigma", 3.0f, "Reject if |nSigma_K| < this"}; + Configurable pProtonVetoElectronNsigma{"pProtonVetoElectronNsigma", 3.0f, "Reject if |nSigma_e| < this"}; + // // V0 daughter beta cuts (particle-dependent) //BETACUTCommented // Configurable cV0protonDauMinBeta{"cV0protonDauMinBeta", 0.4f, "Min TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented // Configurable cV0protonDauMaxBeta{"cV0protonDauMaxBeta", 0.98f, "Max TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented @@ -480,6 +490,28 @@ struct LambdaProtonBalanceFunction { } return std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value; } + + // true => track survives the pi/K/e contaminant veto + template + bool passesContaminantVeto(TTrack const& trk) const + { + if (!cUseContaminantVeto.value) { + return true; + } + if (std::abs(trk.tpcNSigmaPi()) < pProtonVetoPionNsigma.value || + std::abs(trk.tpcNSigmaKa()) < pProtonVetoKaonNsigma.value || + std::abs(trk.tpcNSigmaEl()) < pProtonVetoElectronNsigma.value) { + return false; + } + if (cVetoUseTOF.value && trk.hasTOF()) { + if (std::abs(trk.tofNSigmaPi()) < pProtonVetoPionNsigma.value || + std::abs(trk.tofNSigmaKa()) < pProtonVetoKaonNsigma.value || + std::abs(trk.tofNSigmaEl()) < pProtonVetoElectronNsigma.value) { + return false; + } + } + return true; + } // ───────────────────────────────────────────────────────────────────── // ══════════════════════════════════════════════════════════════════════════ @@ -1275,7 +1307,7 @@ struct LambdaProtonBalanceFunction { AxisSpec axisCosPA_zoom = {200, 0.99f, 1.0f, "Cos(PA)"}; AxisSpec axisDecayR = {100, 0.0f, 50.0f, "cm"}; AxisSpec axisCtau = {100, 0.0f, 50.0f, "ctau (cm)"}; - AxisSpec axisCutFlow = {14, 0.5f, 14.5f, "Cut stage"}; + AxisSpec axisCutFlow = {15, 0.5f, 15.5f, "Cut stage"}; auto addProtonQAForStage = [&](const char* species, const char* stage) { // Kinematics @@ -1428,8 +1460,9 @@ struct LambdaProtonBalanceFunction { hCF->GetXaxis()->SetBinLabel(10, "|y| window"); hCF->GetXaxis()->SetBinLabel(11, "DCA xy,z"); hCF->GetXaxis()->SetBinLabel(12, "PID (TPC/TOF)"); - hCF->GetXaxis()->SetBinLabel(13, "V0-daughter veto"); - hCF->GetXaxis()->SetBinLabel(14, "Selected"); + hCF->GetXaxis()->SetBinLabel(13, "pi/K/e veto"); + hCF->GetXaxis()->SetBinLabel(14, "V0-daughter veto"); + hCF->GetXaxis()->SetBinLabel(15, "Selected"); } // ══════════════════════════════════════════════════════════════════════════ @@ -1667,59 +1700,65 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // ── QA3: SplitTrackQA — q_inv and shared-cluster fraction ───────────── + float qinvMax = cQinvQAMax.value; + int qinvBins = cQinvQABins.value; + if (qinvMax / qinvBins < 0.001f) { + qinvBins = static_cast(qinvMax / 0.001f); + } + // "Before_qinvCut": filled for every accepted pair, prior to the q_inv // cut (kQinvCut), so they show the complete original q_inv distributions. registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut", "Split-track QA: q_{inv} for p-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); // "After_qinvCut": filled only for pairs surviving the q_inv cut // (q_inv > kQinvCut), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut", "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut", "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); // q_inv Before/After QA for p-pbar, pbar-p and pbar-pbar pairs registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut", "Split-track QA: q_{inv} for p-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut", "Split-track QA: q_{inv} for p-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut", "Split-track QA: q_{inv} for #bar{p}-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut", "Split-track QA: q_{inv} for #bar{p}-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut", "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut", "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", - {HistType::kTH1F, {{600, 0.0f, 6.0f, "q_{inv} (GeV/c)"}}}); + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut", "k* of #Lambda-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut", "k* of #Lambda-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); @@ -1728,6 +1767,71 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut", "k* of #bar{#Lambda}-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + + // ── QA3: 3D q_inv vs ΔΦ vs Δη and q_inv vs ΔΦ vs ΔY ───────────────── + // Filled BEFORE the q_inv cut so the full distribution is visible. + // Nomenclature: "Lp" = Lambda(V0) vs primary proton + // "Lp_dau" = Lambda daughter proton vs primary proton + // "LAp"/"LAp_dau" = Lambda (V0/dau) vs primary antiproton + // "ALp"/"ALp_dau" = AntiLambda (V0/dau) vs primary proton + // "ALAp"/"ALAp_dau" = AntiLambda (V0/dau) vs primary antiproton + { + AxisSpec axisQinv3D = {100, 0.0f, 2.0f, "q_{inv} (GeV/c)"}; + AxisSpec axisDPhi3D = {64, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; + AxisSpec axisDEta3D = {40, -2.0f, 2.0f, "#Delta#eta"}; + AxisSpec axisDY3D = {40, -2.0f, 2.0f, "#Deltay"}; + // ── eta space ───────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + // ── y space ─────────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + } + // ───────────────────────────────────────────────────────────────────── registryCorrelationQA.add( "QA3/SplitTrackQA/h1f_sharedClsFraction_t1", "Split-track QA: TPC shared-cluster fraction, track 1 (p-p: first proton; #Lambda-p: V0 daughter) in close pairs;Fraction;Counts", @@ -2655,6 +2759,12 @@ struct LambdaProtonBalanceFunction { } registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 12.0f); + // pi/K/e contaminant veto + if (!passesContaminantVeto(trk)) { + continue; + } + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 13.0f); + // TOF Matching Fraction check on selected protons registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── @@ -2708,11 +2818,11 @@ struct LambdaProtonBalanceFunction { } continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 13.0f); + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 14.0f); // ───────────────────────────────────────────────────────────────── // ── Classify into proton / antiproton lists ─────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 14.0f); + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 15.0f); // VS Code: commented out unused variables. // const float pz = trk.pt() * std::sinh(trk.eta()); // const float eta = trk.eta(); @@ -2990,17 +3100,44 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted Lambda-proton pair float qinvLP = 0.0f; { - const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float dauPmag = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); const float pprmag = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); - const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); + const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); const float Eppr = std::sqrt(pprmag * pprmag + kMassProton * kMassProton); - qinvLP = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr); + qinvLP = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau, primProton.px(), primProton.py(), primProton.pz(), Eppr); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); } // QA3/SplitTrackQA: q_inv Before and After cut if (!failsQinvCut(qinvLP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLP); } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primProton and Lambda dau proton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_V0_p = v0.eta() - primProton.eta(); + const float yLambda = v0.rapidity(1); + const float yPrimP = protonRapidity(primProton); + const float dY_V0_p = yLambda - yPrimP; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3d = std::sqrt(v0pmag_3d * v0pmag_3d + kMassLambda * kMassLambda); + const float pprmag_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr_3d = std::sqrt(pprmag_3d * pprmag_3d + kMassProton * kMassProton); + const float qinvLP_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_p = RecoDecay::constrainAngle((v0PosDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dau_p = v0PosDau.eta() - primProton.eta(); + const float yDauP = protonRapidity(v0PosDau); + const float dY_dau_p = yDauP - yPrimP; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp"), qinvLP_V0, dPhi_V0_p, dEta_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau"), qinvLP, dPhi_dau_p, dEta_dau_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp"), qinvLP_V0, dPhi_V0_p, dY_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau"), qinvLP, dPhi_dau_p, dY_dau_p); + } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -3066,17 +3203,44 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted Lambda-antiproton pair float qinvLAp = 0.0f; { - const float v0pmag2 = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float dauPmag = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); const float paprmag = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float Ev02 = std::sqrt(v0pmag2 * v0pmag2 + kMassLambda * kMassLambda); + const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); const float Epapr = std::sqrt(paprmag * paprmag + kMassProton * kMassProton); - qinvLAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev02, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); + qinvLAp = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); } // QA3/SplitTrackQA: q_inv Before and After cut if (!failsQinvCut(qinvLAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLAp); } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primAntiProton and Lambda dau proton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_V0_ap = v0.eta() - primAntiProton.eta(); + const float yLambdaL = v0.rapidity(1); + const float yPrimAp = protonRapidity(primAntiProton); + const float dY_V0_ap = yLambdaL - yPrimAp; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3dL = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3dL = std::sqrt(v0pmag_3dL * v0pmag_3dL + kMassLambda * kMassLambda); + const float paprmag_3dL = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr_3dL = std::sqrt(paprmag_3dL * paprmag_3dL + kMassProton * kMassProton); + const float qinvLAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_ap = RecoDecay::constrainAngle((v0PosDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dau_ap = v0PosDau.eta() - primAntiProton.eta(); + const float yDauPL = protonRapidity(v0PosDau); + const float dY_dau_ap = yDauPL - yPrimAp; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp"), qinvLAp_V0, dPhi_V0_ap, dEta_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau"), qinvLAp, dPhi_dau_ap, dEta_dau_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp"), qinvLAp_V0, dPhi_V0_ap, dY_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau"), qinvLAp, dPhi_dau_ap, dY_dau_ap); + } // QA3/RawPairDensity: Lambda-antiproton → same LP histogram { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -3197,17 +3361,44 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-proton pair float qinvALp = 0.0f; { - const float alpmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float dauPmag = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); const float pprmag3 = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); - const float EalL = std::sqrt(alpmag * alpmag + kMassLambda * kMassLambda); + const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); const float Eppr3 = std::sqrt(pprmag3 * pprmag3 + kMassProton * kMassProton); - qinvALp = computeQinv(v0.px(), v0.py(), v0.pz(), EalL, primProton.px(), primProton.py(), primProton.pz(), Eppr3); + qinvALp = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), Edau, primProton.px(), primProton.py(), primProton.pz(), Eppr3); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); } // QA3/SplitTrackQA: q_inv Before and After cut if (!failsQinvCut(qinvALp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALp); } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primProton and AntiLambda dau antiproton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_AL_p = v0.eta() - primProton.eta(); + const float yAL = v0.rapidity(2); + const float yPrimP3 = protonRapidity(primProton); + const float dY_AL_p = yAL - yPrimP3; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL_3d = std::sqrt(almag_3d * almag_3d + kMassLambda * kMassLambda); + const float pprmag3_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr3_3d = std::sqrt(pprmag3_3d * pprmag3_3d + kMassProton * kMassProton); + const float qinvALp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_p = RecoDecay::constrainAngle((v0NegDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dauAL_p = v0NegDau.eta() - primProton.eta(); + const float yDauAL = protonRapidity(v0NegDau); + const float dY_dauAL_p = yDauAL - yPrimP3; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp"), qinvALp_V0, dPhi_AL_p, dEta_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau"), qinvALp, dPhi_dauAL_p, dEta_dauAL_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp"), qinvALp_V0, dPhi_AL_p, dY_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau"), qinvALp, dPhi_dauAL_p, dY_dauAL_p); + } // QA3/RawPairDensity: AntiLambda-proton → same LP histogram { constexpr float PIHalf = o2::constants::math::PI / 2.0f; @@ -3256,17 +3447,44 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-antiproton pair float qinvALAp = 0.0f; { - const float alpmag2 = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float dauPmag = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); const float paprmag2 = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float EalL2 = std::sqrt(alpmag2 * alpmag2 + kMassLambda * kMassLambda); + const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); const float Epapr2 = std::sqrt(paprmag2 * paprmag2 + kMassProton * kMassProton); - qinvALAp = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); + qinvALAp = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), Edau, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: q_inv Before and After cut if (!failsQinvCut(qinvALAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALAp); } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primAntiProton and AntiLambda dau antiproton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_AL_ap = v0.eta() - primAntiProton.eta(); + const float yAL2 = v0.rapidity(2); + const float yPrimAp2 = protonRapidity(primAntiProton); + const float dY_AL_ap = yAL2 - yPrimAp2; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag2_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL2_3d = std::sqrt(almag2_3d * almag2_3d + kMassLambda * kMassLambda); + const float paprmag2_3d = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr2_3d = std::sqrt(paprmag2_3d * paprmag2_3d + kMassProton * kMassProton); + const float qinvALAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_ap = RecoDecay::constrainAngle((v0NegDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dauAL_ap = v0NegDau.eta() - primAntiProton.eta(); + const float yDauAL2 = protonRapidity(v0NegDau); + const float dY_dauAL_ap = yDauAL2 - yPrimAp2; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp"), qinvALAp_V0, dPhi_AL_ap, dEta_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau"), qinvALAp, dPhi_dauAL_ap, dEta_dauAL_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp"), qinvALAp_V0, dPhi_AL_ap, dY_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau"), qinvALAp, dPhi_dauAL_ap, dY_dauAL_ap); + } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { constexpr float PIHalf = o2::constants::math::PI / 2.0f; From 46423fda49f814009d93281886161ec8f2fd261b Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Mon, 5 Oct 2026 01:38:14 +0800 Subject: [PATCH 168/648] [PWGCF] Fix fetching CCDB data bug and improvements (#18166) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 258 ++++++++++-------- 1 file changed, 149 insertions(+), 109 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index b508cd577b4..64137643dc9 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -196,9 +196,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Service pdg{}; // *) Define configurables: - Configurable centralityEstimator{"centralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; - Configurable multiplicityTables{"multiplicityTables", 0, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfCentralityEstimator{"cfCentralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfMultiplicityTables{"cfMultiplicityTables", 0, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; // *) External root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; @@ -206,7 +206,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Binnings Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; - Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; Configurable> cfMultBins{"cfMultBins", {400, 0., 40000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "Reference mult bins: nMultBins, multMin, multMax"}; Configurable> cfContribBins{"cfContribBins", {400, 0., 6500.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; @@ -225,7 +225,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable> cfTechnicalCutSwitch{"cfTechnicalCutSwitch", {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}, "technical cuts switch, on and off by the first number before name"}; // event level cuts - Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply vertex z position cut"}; + Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply event level cut"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}[cm], with convention: min <= cent <= max"}; @@ -237,16 +237,40 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; // *) Misc - Configurable sigmaInel{"sigmaInel", 7.71, "inelastic cross section in mb"}; - Configurable qualityAssuranceSwitch{"qualityAssuranceSwitch", false, "quality assurance switch"}; - Configurable runMessageSwitch{"runMessageSwitch", false, "run message switch"}; + Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; + Configurable cfQualityAssuranceSwitch{"cfQualityAssuranceSwitch", false, "quality assurance switch"}; + Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. bool fDryRun = false; // book all histos and run without filling and calculating anything - } tc; // you have to prepend "tc." for all objects name in this group later in the code + int fCentralityEstimator = 0; + int fMultiplicityTables = 0; + + bool fExternalFileSwitch = false; + std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; + bool fALICECentBinSwitch = true; + + bool fMasterCutSwitch = true; + std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; + + bool fEventCutSwitch = true; + std::vector fVertexZCutRange = {-10., 10.}; + std::vector fCentCutRange = {0., 80.}; + + bool fPtCutSwitch = true; + std::vector fPtCutRange = {0.2, 5.}; + bool fEtaCutSwitch = true; + std::vector fEtaCutRange = {-0.8, 0.8}; + + bool fChargeCutSwitch = true; + + double fSigmaInel = 7.71; + bool fQualityAssuranceSwitch = false; + bool fRunMessageSwitch = false; + } tc; // you have to prepend "tc." for all objects name in this group later in the code struct ParticleHistograms { TList* fParticleHistList = nullptr; //! histW{}; TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights @@ -460,7 +484,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return nullptr; + return; } } @@ -481,7 +505,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return nullptr; + return; } } @@ -510,7 +534,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(error, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); - return nullptr; + return; } } } @@ -518,25 +542,26 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s: listWithRuns is null for run %s\033[0m", __FUNCTION__, runNumber); - return nullptr; - } - - hist = dynamic_cast(listWithRuns->FindObject(histName)); - if (!hist) { - LOGF(info, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, histName, runNumber); - return nullptr; + return; } - auto histClone = dynamic_cast(hist->Clone()); - if (!histClone) { - LOGF(error, "%s: histogram 'histClone' failed to be cloned", __FUNCTION__); - return nullptr; + for (int i = 0; i < eWeightsHistograms_N; ++i) { + histW[i] = dynamic_cast(listWithRuns->FindObject(Form("[%s]", WeightsNames[i]))); + if (!histW[i]) { + LOGF(warning, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + histW[i]->SetDirectory(nullptr); + ex.fWeights[i] = dynamic_cast(histW[i]->Clone()); + if (!ex.fWeights[i]) { + LOGF(warning, "%s: failed to clone histogram '%s' with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + ex.fWeights[i]->SetTitle(tc.fFileWithWeights.c_str()); + ex.fExternalHistogramsList->Add(ex.fWeights[i]); } - histClone->SetDirectory(nullptr); delete baseList; - - return histClone; } // end of TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) // templates @@ -581,25 +606,25 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool technicalCuts(T1 const& collision) { - if (cfTechnicalCutSwitch.value[eNoCollInTimeRangeStandard] == "1NoCollInTimeRangeStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (tc.fTechnicalCutSwitch[eNoCollInTimeRangeStandard] == "1NoCollInTimeRangeStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (cfTechnicalCutSwitch.value[eNoCollInRofStandard] == "1NoCollInRofStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (tc.fTechnicalCutSwitch[eNoCollInRofStandard] == "1NoCollInRofStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (cfTechnicalCutSwitch.value[eNoSameBunchPileUp] == "1NoSameBunchPileUp" && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (tc.fTechnicalCutSwitch[eNoSameBunchPileUp] == "1NoSameBunchPileUp" && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (cfTechnicalCutSwitch.value[eIsVertexITSTPC] == "1IsVertexITSTPC" && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + if (tc.fTechnicalCutSwitch[eIsVertexITSTPC] == "1IsVertexITSTPC" && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - if (cfTechnicalCutSwitch.value[eIsGoodITSLayersAll] == "1IsGoodITSLayersAll" && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (tc.fTechnicalCutSwitch[eIsGoodITSLayersAll] == "1IsGoodITSLayersAll" && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (cfTechnicalCutSwitch.value[eIsGoodZvtxFT0vsPV] == "1IsGoodZvtxFT0vsPV" && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (tc.fTechnicalCutSwitch[eIsGoodZvtxFT0vsPV] == "1IsGoodZvtxFT0vsPV" && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (cfTechnicalCutSwitch.value[eNoHighMultCollInPrevRof] == "1NoHighMultCollInPrevRof" && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof] == "1NoHighMultCollInPrevRof" && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } @@ -615,14 +640,14 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (cfEventCutSwitch) // event level cuts for Rec + if (tc.fEventCutSwitch) // event level cuts for Rec { if constexpr (rm == eReal) { - if (collision.posZ() > cfVertexZCutRange.value[1] || collision.posZ() < cfVertexZCutRange.value[0]) { + if (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]) { return false; } // vertex z cut - auto thisCent = chooseCent(collision, centralityEstimator); - if (thisCent > cfCentCutRange.value[1] || thisCent < cfCentCutRange.value[0]) { + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + if (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]) { return false; } // centrality cut } @@ -630,11 +655,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to { auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; - if (thisMCCollision.posZ() > cfVertexZCutRange.value[1] || thisMCCollision.posZ() < cfVertexZCutRange.value[0]) { + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { return false; } // vertex z cut - if (centralityMC > cfCentCutRange.value[1] || centralityMC < cfCentCutRange.value[0]) { + if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { return false; } // centrality cut } @@ -650,8 +675,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr (rs == eRec || rs == eRecAndSim) { // Fill reconstructed-level event histograms if constexpr (rm == eReal) { - auto thisCent = chooseCent(collision, centralityEstimator); - auto thisRefMult = chooseMult(collision, multiplicityTables); + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); int multiplicityRec = static_cast(tracks.size()); if constexpr (cuts == eBefore) { ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); @@ -679,7 +704,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto thisMCCollision = collision.mcCollision(); int multiplicitySim = static_cast(tracks.size()); auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; // centrality for sim derived from impact parameter + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter if constexpr (cuts == eBefore) { ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); @@ -710,37 +735,37 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (cfPtCutSwitch) // pt cuts for Rec + if (tc.fPtCutSwitch) // pt cuts for Rec { if constexpr (rm == eReal) { - if (track.pt() < cfPtCutRange.value[0] || track.pt() > cfPtCutRange.value[1]) { + if (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]) { return false; } } if constexpr (rs == eRecAndSim && rm == eMC) // pt cuts for Sim { auto thisMCParticle = track.mcParticle(); - if (thisMCParticle.pt() < cfPtCutRange.value[0] || thisMCParticle.pt() > cfPtCutRange.value[1]) { + if (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]) { return false; } } } - if (cfEtaCutSwitch) // eta cuts for Rec + if (tc.fEtaCutSwitch) // eta cuts for Rec { if constexpr (rm == eReal) { - if (track.eta() < cfEtaCutRange.value[0] || track.eta() > cfEtaCutRange.value[1]) { + if (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]) { return false; } } if constexpr (rs == eRecAndSim && rm == eMC) // eta cuts for Sim { auto thisMCParticle = track.mcParticle(); - if (thisMCParticle.eta() < cfEtaCutRange.value[0] || thisMCParticle.eta() > cfEtaCutRange.value[1]) { + if (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]) { return false; } } } - if (cfChargeCutSwitch) // charge cuts for Rec + if (tc.fChargeCutSwitch) // charge cuts for Rec { if constexpr (rm == eReal) { if (track.sign() != 1 && track.sign() != -1) { @@ -761,7 +786,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return false; } } // end of if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim - } // end of if (cfChargeCutSwitch) // charge cuts for Rec + } // end of if (tc.fChargeCutSwitch) // charge cuts for Rec } // end of if constexpr (rs == eRec || rs == eRecAndSim) { return true; @@ -820,22 +845,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - void loadWeights(int runNumber) - { - for (int i = 0; i < eWeightsHistograms_N; ++i) { - ex.fWeights[i] = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), Form("[%s]", WeightsNames[i])); - if (!ex.fWeights[i]) { - continue; - } - ex.fExternalHistogramsList->Add(ex.fWeights[i]); - } - } - template void qaFill(T1 const& collision) { - auto thisCent = chooseCent(collision, centralityEstimator); - auto thisRefMult = chooseMult(collision, multiplicityTables); + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); if constexpr (rm == eReal) { if constexpr (cuts == eBefore) { qa.fHistMultNContrib[eBefore]->Fill(thisRefMult, collision.numContrib()); @@ -851,7 +865,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / sigmaInel; + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; if constexpr (cuts == eBefore) { qa.fHistCentralityRecSim[eBefore]->Fill(thisCent, centralityMC); } @@ -875,26 +889,26 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fDryRun) { return; } - auto thisCollCent = chooseCent(collision, centralityEstimator); + auto thisCollCentReal = chooseCent(collision, tc.fCentralityEstimator); if (isFirstCollision) { thisRunNumber = collision.bc().runNumber(); misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); // Get run number - if (cfExternalFileSwitch) { - loadWeights(thisRunNumber); // Get weights + if (tc.fExternalFileSwitch) { + getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", thisRunNumber)); } } - if (runMessageSwitch) { - collisionCounter++; + if (tc.fRunMessageSwitch) { if (collisionCounter % runMessagePeriod == 0) { LOGF(info, "Successfully running, run number is %d", thisRunNumber); collisionCounter = 0; } + ++collisionCounter; } // Fill Quality Assurance before cuts - if (qualityAssuranceSwitch) { + if (tc.fQualityAssuranceSwitch) { qaFill(collision); qaFill(collision); } @@ -908,16 +922,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool passTechnicalCut = technicalCuts(collision); // Fill event hist and qa hist after cuts - if (cfMasterCutSwitch) { + if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut) { eventHistFill(collision, tracks); - if (qualityAssuranceSwitch) { + if (tc.fQualityAssuranceSwitch) { qaFill(collision); } } if (passEventCutsMC) { eventHistFill(collision, tracks); - if (qualityAssuranceSwitch) { + if (tc.fQualityAssuranceSwitch) { qaFill(collision); } } @@ -950,7 +964,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::array thisPhiAndPt = {thisPhiRec, thisPhiSim, thisPt}; std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} - if (cfExternalFileSwitch) { + if (tc.fExternalFileSwitch) { for (int k = 0; k < eWeightsHistograms_N; ++k) { if (ex.fWeights[k]) { thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); @@ -958,7 +972,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - if (cfMasterCutSwitch) { + if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { particleHistFill(track); updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeights[ePhiRec] * thisWeights[ePt]); @@ -972,24 +986,26 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } // end of for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { - std::vector resultMultCorr(2, TComplex(0., 0.)); - if (cfMasterCutSwitch) { + std::vector resultTwo(2, TComplex(0., 0.)); + if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut) { - resultMultCorr = two(qVectorsTableReal, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eRec]->Fill(thisCollCent, (resultMultCorr[0] / resultMultCorr[1].Re()).Re()); + resultTwo = two(qVectorsTableReal, n2); + if (noneZeroDenom(resultTwo)) { + obs.fProfTwo[eRec]->Fill(thisCollCentReal, (resultTwo[0] / resultTwo[1].Re()).Re()); } } if constexpr (rs == eRecAndSim) { if (passEventCutsMC) { - resultMultCorr = two(qVectorsTableMC, n2); - if (noneZeroDenom(resultMultCorr)) { - obs.fProfTwo[eSim]->Fill(thisCollCent, (resultMultCorr[0] / resultMultCorr[1].Re()).Re()); + auto impactParam = collision.mcCollision().impactParameter(); + auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; + resultTwo = two(qVectorsTableMC, n2); + if (noneZeroDenom(resultTwo)) { + obs.fProfTwo[eSim]->Fill(thisCollCentMC, (resultTwo[0] / resultTwo[1].Re()).Re()); } } // end of if (passEventCutsMC) { } // end of if constexpr (rs == eRecAndSim) { - } // end of if (cfMasterCutSwitch) { + } // end of if (tc.fMasterCutSwitch) { isFirstCollision = false; // Now the first collision ends } // end of template void steer(T1 const& collision, T2 const& tracks) { @@ -1010,6 +1026,28 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Configure your task using configurables in the json file: tc.fDryRun = cfDryRun; + tc.fCentralityEstimator = cfCentralityEstimator; + tc.fMultiplicityTables = cfMultiplicityTables; + + tc.fExternalFileSwitch = cfExternalFileSwitch; + tc.fFileWithWeights = cfFileWithWeights; + tc.fALICECentBinSwitch = cfALICECentBinSwitch; + + tc.fMasterCutSwitch = cfMasterCutSwitch; + tc.fTechnicalCutSwitch = cfTechnicalCutSwitch.value; + tc.fEventCutSwitch = cfEventCutSwitch; + tc.fVertexZCutRange = cfVertexZCutRange.value; + tc.fCentCutRange = cfCentCutRange.value; + + tc.fPtCutSwitch = cfPtCutSwitch; + tc.fPtCutRange = cfPtCutRange.value; + tc.fEtaCutSwitch = cfEtaCutSwitch; + tc.fEtaCutRange = cfEtaCutRange.value; + tc.fChargeCutSwitch = cfChargeCutSwitch; + + tc.fSigmaInel = cfSigmaInel; + tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; + tc.fRunMessageSwitch = cfRunMessageSwitch; // *) Book base list: auto* temp = new TList(); @@ -1017,7 +1055,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList.setObject(temp); // *) Book and nest all other TLists: - if (cfExternalFileSwitch) { + if (tc.fExternalFileSwitch) { // *) Book External Hist List ex.fExternalHistogramsList = new TList(); ex.fExternalHistogramsList->SetName("ExternalHistograms"); @@ -1078,7 +1116,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eBefore]); } - if (cfMasterCutSwitch) { + if (tc.fMasterCutSwitch) { pc.fParticleHist[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); pc.fParticleHist[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); @@ -1116,7 +1154,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eBefore]); } - if (cfMasterCutSwitch) { + if (tc.fMasterCutSwitch) { pc.fParticleHist[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); pc.fParticleHist[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); @@ -1183,18 +1221,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // eEventHistograms_N if (doprocessRec || doprocessRecSim) { - if (cfALICECentBinSwitch) { + if (tc.fALICECentBinSwitch) { ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); } - ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); ec.fEventHist[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); ec.fEventHist[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); @@ -1212,19 +1250,19 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - if (cfMasterCutSwitch) { - if (cfALICECentBinSwitch) { + if (tc.fMasterCutSwitch) { + if (tc.fALICECentBinSwitch) { ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); } - ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); ec.fEventHist[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); ec.fEventHist[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); @@ -1245,12 +1283,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (doprocessSim || doprocessRecSim) { - if (cfALICECentBinSwitch) { + if (tc.fALICECentBinSwitch) { ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); } - ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); @@ -1274,13 +1312,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - if (cfMasterCutSwitch) { - if (cfALICECentBinSwitch) { + if (tc.fMasterCutSwitch) { + if (tc.fALICECentBinSwitch) { ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultCentBins, defaultCentBoundaries); } else { ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); } - ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[centralityEstimator])); + ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); @@ -1313,27 +1351,29 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(obs.fObservablesList); if (doprocessRec || doprocessRecSim) { - if (cfALICECentBinSwitch) { - obs.fProfTwo[eRec] = new TProfile("obs.fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nDefaultCentBins, defaultCentBoundaries); + if (tc.fALICECentBinSwitch) { + obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nDefaultCentBins, defaultCentBoundaries); } else { - obs.fProfTwo[eRec] = new TProfile("obs.fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nBinsCent, minCent, maxCent); + obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nBinsCent, minCent, maxCent); } obs.fProfTwo[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfTwo[eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eRec]); if (doprocessRecSim) { - if (cfALICECentBinSwitch) { - obs.fProfTwo[eSim] = new TProfile("obs.fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nDefaultCentBins, defaultCentBoundaries); + if (tc.fALICECentBinSwitch) { + obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nDefaultCentBins, defaultCentBoundaries); } else { - obs.fProfTwo[eSim] = new TProfile("obs.fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nBinsCent, minCent, maxCent); + obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nBinsCent, minCent, maxCent); } obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfTwo[eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eSim]); } } // *) Book and QA TLists: - if (qualityAssuranceSwitch) { + if (tc.fQualityAssuranceSwitch) { std::vector lContrib = cfContribBins.value; const int nBinsContrib = static_cast(lContrib[0]); const float minContrib = lContrib[1]; @@ -1346,7 +1386,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessRec || doprocessRecSim) { for (int i = 0; i < eCuts_N; ++i) { qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), nBinsMultRef, minMultRef, maxMultRef, nBinsContrib, minContrib, maxContrib); - qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[multiplicityTables])); + qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); } From 1e3614d02140e508d1b05a7bd8b1abec0fd63fec Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Sun, 4 Oct 2026 21:20:48 +0200 Subject: [PATCH 169/648] [PWGLF] Remove debug log in derivedlambdakzeroanalysis (#18165) Co-authored-by: ALICE Action Bot --- PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 04029b5d54d..4f201c3c439 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -60,6 +60,7 @@ #include #include +#include #include #include @@ -1732,7 +1733,6 @@ struct derivedlambdakzeroanalysis { BITSET(bitMap, selConsiderK0Short); if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimK0Short); - LOG(info) << "coucou1"; } } if (v0.pdgCode() == PDG_t::kLambda0 && isPositiveProton && isNegativePion) { From 4a0886b6964a054c88b96af51bdca8d077f324c4 Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Sun, 4 Oct 2026 22:29:17 +0200 Subject: [PATCH 170/648] [PWGCF] Add photons and pi0s to femto framework (#18169) Co-authored-by: ALICE Action Bot --- PWGCF/Femto/Core/dataTypes.h | 6 + PWGCF/Femto/Core/modes.h | 6 + PWGCF/Femto/Core/partitions.h | 27 ++ PWGCF/Femto/Core/photonBuilder.h | 404 ++++++++++++++++++ PWGCF/Femto/Core/photonHistManager.h | 295 +++++++++++++ PWGCF/Femto/Core/twoPhotonResonanceBuilder.h | 241 +++++++++++ .../Core/twoPhotonResonanceHistManager.h | 207 +++++++++ PWGCF/Femto/Core/v0Builder.h | 8 +- PWGCF/Femto/Core/v0HistManager.h | 9 +- PWGCF/Femto/DataModel/FemtoTables.h | 148 +++++++ PWGCF/Femto/TableProducer/CMakeLists.txt | 7 +- PWGCF/Femto/TableProducer/femtoProducer.cxx | 61 +++ .../femtoProducerTwoPhotonResonances.cxx | 88 ++++ PWGCF/Femto/Tasks/CMakeLists.txt | 10 + PWGCF/Femto/Tasks/femtoPhotonQa.cxx | 101 +++++ .../Femto/Tasks/femtoTwoPhotonResonanceQa.cxx | 143 +++++++ 16 files changed, 1757 insertions(+), 4 deletions(-) create mode 100644 PWGCF/Femto/Core/photonBuilder.h create mode 100644 PWGCF/Femto/Core/photonHistManager.h create mode 100644 PWGCF/Femto/Core/twoPhotonResonanceBuilder.h create mode 100644 PWGCF/Femto/Core/twoPhotonResonanceHistManager.h create mode 100644 PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx create mode 100644 PWGCF/Femto/Tasks/femtoPhotonQa.cxx create mode 100644 PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index 25c5d84147c..bbda8a76e93 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -35,6 +35,9 @@ using V0MaskType = uint32_t; using V0MaskType001 = uint16_t; // old data type, was too narrow using V0Type = uint16_t; +// datatypes for photons +using PhotonMaskType = uint16_t; + // datatypes for kinks using KinkMaskType = uint32_t; using KinkType = uint8_t; @@ -44,6 +47,9 @@ using TwoTrackResonanceMaskType = uint32_t; // two track resonance types using TwoTrackResonanceType = uint16_t; +// datatype for resonances built from two photons (pi0, eta, ...) +using TwoPhotonResonanceType = uint16_t; + // datatypes for cascades using CascadeMaskType = uint32_t; using CascadeMaskType001 = uint16_t; // old data type, was too narrow diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index ca55b122c80..609effb965b 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -234,6 +234,12 @@ enum class TwoTrackResonance : o2::analysis::femto::datatypes::TwoTrackResonance kKstar0Bar }; +// resonances reconstructed from two photons (PCM), e.g. pi0 -> gamma gamma, eta -> gamma gamma +enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonanceType { + kPi0, + kEta +}; + enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType { kD0, kD0Bar, diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index fac0704c71f..ad82cde98ea 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -94,6 +94,21 @@ ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) +// partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON +// there is no sign and no momentum-threshold PID switch, just a plain mask check per (unordered) daughter +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau1, (selection).dau1Mask) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau2, (selection).dau2Mask) + // partition for lambdas // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_LAMBDA_PARTITION(selection) \ @@ -123,6 +138,18 @@ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ ncheckbit(o2::aod::femtov0s::mask, (selection).mask) +// partition for photons (PCM) +// no sign/mass handling: photons are their own antiparticle and have no mass window +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_PHOTON_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ncheckbit(o2::aod::femtophotons::mask, (selection).mask) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_CASCADE_PARTITION(selection) \ ifnode((selection).sign.node() != 0, \ diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h new file mode 100644 index 00000000000..38943d09c27 --- /dev/null +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -0,0 +1,404 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonBuilder.h +/// \brief photon (PCM) builder +/// +/// Default selection values for the mandatory cuts (dauTpcNSigmaElAbsMax, keepDaughtersWithoutTpc) +/// are taken from the PCM branch of EventFiltering/PWGEM/EMPhotonFilter.cxx (isSelectedSecondary() +/// and the min_pt_pcm_photon cut in runFilter()): both V0 daughters must have |TPC nSigma_e| < 3.5 +/// whenever a TPC signal is available, and a daughter without TPC is not rejected. Note that +/// EMPhotonFilter.cxx also declares minpt_v0, maxeta_v0, max_dcatopv_xy_v0 and max_dcatopv_z_v0, +/// but never applies them -- they are dead configurables in that task. The additional geometric +/// cuts here (cosPA, DCA to PV, chi2/NDF, radius) are standard PCM conversion-quality cuts, not +/// used by that filter task, so they default to permissive (no-op) values. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ +#define PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonbuilder +{ + +// filters applied in the producer task (loose pre-selection, applied before quality/PID cuts) +struct ConfPhotonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT (matches EMPhotonFilter min_pt_pcm_photon default)"}; + o2::framework::Configurable ptMax{"ptMax", 99.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mGammaAbsMax{"mGammaAbsMax", 1e10f, "Maximum |m_ee| of the conversion (KF) at the secondary vertex"}; +}; + +// selection bits for photons (PCM) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all photons are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cosPaMin{"cosPaMin", {-1.f}, "Minimum cosine of pointing angle (not used by EMPhotonFilter, default is permissive)"}; \ + o2::framework::Configurable> dcaToPvXYAbsMax{"dcaToPvXYAbsMax", {1e10f}, "Maximum |DCAxy| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> dcaToPvZAbsMax{"dcaToPvZAbsMax", {1e10f}, "Maximum |DCAz| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> chi2NdfMax{"chi2NdfMax", {1e10f}, "Maximum chi2/NDF of the KF conversion vertex"}; \ + o2::framework::Configurable> v0RadiusMin{"v0RadiusMin", {0.f}, "Minimum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> v0RadiusMax{"v0RadiusMax", {1e10f}, "Maximum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.9f}, "Maximum |eta| for daughter tracks (matches EMPhotonFilter maxeta_v0, though unused there)"}; \ + o2::framework::Configurable> dauTpcNSigmaElAbsMax{"dauTpcNSigmaElAbsMax", {3.5f}, "Maximum |TPC nSigma_e| for V0 daughters (from EMPhotonFilter isSelectedSecondary)"}; \ + o2::framework::Configurable keepDaughtersWithoutTpc{"keepDaughtersWithoutTpc", true, "If true, a daughter without a TPC signal is not rejected by the TPC electron PID cut (matches EMPhotonFilter behaviour)"}; + +struct ConfPhotonBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBits"); + PHOTON_DEFAULT_BITS +}; + +#undef PHOTON_DEFAULT_BITS + +// base selection for QA/analysis tasks, used to re-select already produced photons by mask +// (analogous to ConfLambdaSelection/ConfK0shortSelection in spirit, but photons have neither +// a PDG hypothesis nor a mass window) +template +struct ConfPhotonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for photon selection"}; +}; + +constexpr const char PrefixPhotonSelection1[] = "PhotonSelection1"; +using ConfPhotonSelection1 = ConfPhotonSelection; + +/// The different selections for photons (PCM) +enum PhotonSels { + kCosPaMin, ///< Min. CPA (cosine pointing angle) of the conversion to the PV + kDcaToPvXYAbsMax, ///< Max. |DCAxy| of the photon to the PV + kDcaToPvZAbsMax, ///< Max. |DCAz| of the photon to the PV + kChi2NdfMax, ///< Max. chi2/NDF of the KF conversion vertex + kV0RadiusMin, ///< Min. transverse radius of the conversion point + kV0RadiusMax, ///< Max. transverse radius of the conversion point + kDauAbsEtaMax, ///< Max. absolute pseudorapidity of the daughters + kPosDauTpcNSigmaEl, ///< TPC electron PID for positive daughter + kNegDauTpcNSigmaEl, ///< TPC electron PID for negative daughter + kPhotonSelsMax +}; + +constexpr char PhotonSelHistName[] = "hPhotonSelection"; +constexpr char PhotonSelsName[] = "Photon selection object"; +const std::unordered_map photonSelectionNames = { + {kCosPaMin, "Min. CPA (cosine pointing angle)"}, + {kDcaToPvXYAbsMax, "Max. |DCAxy| to PV"}, + {kDcaToPvZAbsMax, "Max. |DCAz| to PV"}, + {kChi2NdfMax, "Max. chi2/NDF of conversion vertex"}, + {kV0RadiusMin, "Min. transverse radius"}, + {kV0RadiusMax, "Max. transverse radius"}, + {kDauAbsEtaMax, "Max. absolute pseudorapidity of daughters"}, + {kPosDauTpcNSigmaEl, "TPC electron PID for positive daughter"}, + {kNegDauTpcNSigmaEl, "TPC electron PID for negative daughter"}, +}; + +// enum for all photon (pre)filters (loose pre-selection, applied before quality/PID cuts) +enum PhotonFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMGammaAbsMax, + kPhotonFiltersMax +}; + +constexpr char PhotonFilterHistName[] = "hPhotonFilters"; +const std::unordered_map photonFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMGammaAbsMax, "mGammaAbsMax"}, +}; + +/// \brief Cut class to contain and execute all cuts applied to PCM photons (V0PhotonsKF) +template +class PhotonSelection : public baseselection::BaseSelection +{ + public: + PhotonSelection() = default; + ~PhotonSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMGammaAbsMax = filter.mGammaAbsMax.value; + mKeepDaughtersWithoutTpc = config.keepDaughtersWithoutTpc.value; + + this->addSelection(kCosPaMin, photonSelectionNames.at(kCosPaMin), config.cosPaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDcaToPvXYAbsMax, photonSelectionNames.at(kDcaToPvXYAbsMax), config.dcaToPvXYAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDcaToPvZAbsMax, photonSelectionNames.at(kDcaToPvZAbsMax), config.dcaToPvZAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChi2NdfMax, photonSelectionNames.at(kChi2NdfMax), config.chi2NdfMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kV0RadiusMin, photonSelectionNames.at(kV0RadiusMin), config.v0RadiusMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {photonFilterNames.at(kPtMin), mPtMin}, + {photonFilterNames.at(kPtMax), mPtMax}, + {photonFilterNames.at(kEtaMin), mEtaMin}, + {photonFilterNames.at(kEtaMax), mEtaMax}, + {photonFilterNames.at(kPhiMin), mPhiMin}, + {photonFilterNames.at(kPhiMax), mPhiMax}, + {photonFilterNames.at(kMGammaAbsMax), mMGammaAbsMax}, + }); + } + + template + void applySelections(T1 const& photon, T2 const& /*v0legs*/) + { + this->reset(); + this->evaluateObservable(kCosPaMin, photon.cospa()); + this->evaluateObservable(kDcaToPvXYAbsMax, photon.dcaXYtopv()); + this->evaluateObservable(kDcaToPvZAbsMax, photon.dcaZtopv()); + this->evaluateObservable(kChi2NdfMax, photon.chiSquareNDF()); + this->evaluateObservable(kV0RadiusMin, photon.v0radius()); + this->evaluateObservable(kV0RadiusMax, photon.v0radius()); + + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + + std::array etaAbsDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end())); + + // TPC electron PID: evaluate only when a TPC signal is available, matching + // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC + auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) { + if (hasTpc) { + this->evaluateObservable(bit, tpcNSigmaEl); + } else if (mKeepDaughtersWithoutTpc) { + this->evaluateObservable(bit, 0.f); + } + }; + evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl()); + evaluateDaughterTpcEl(kNegDauTpcNSigmaEl, negDaughter.hasTPC(), negDaughter.tpcNSigmaEl()); + + this->assembleBitmask(); + } + + template + bool checkFilters(const T& photon) const + { + bool pass = true; + bool p = false; + + p = photon.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = photon.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = photon.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = photon.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = photon.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = photon.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = std::fabs(photon.mGamma()) < mMGammaAbsMax; + this->template fillFilter(kMGammaAbsMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + protected: + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 99.f; + float mEtaMin = -10.f; + float mEtaMax = 10.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mMGammaAbsMax = 1e10f; + + bool mKeepDaughtersWithoutTpc = true; +}; + +struct PhotonBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPhotons; + o2::framework::Produces producedLitePhotons; + o2::framework::Produces producedPhotonMasks; + o2::framework::Produces producedPhotonExtras; +}; + +struct ConfPhotonTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonTables"); + o2::framework::Configurable producePhotons{"producePhotons", -1, "Produce Photons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLitePhotons{"produceLitePhotons", -1, "Produce LitePhotons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonMasks{"producePhotonMasks", -1, "Produce PhotonMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonExtras{"producePhotonExtras", -1, "Produce PhotonExtras (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for PCM photons (V0PhotonsKF + V0Legs, from PWGEM/PhotonMeson) +/// +/// Unlike the strangeness V0 builder, daughter tracks are not indexed into FTracks: the +/// conversion legs (V0Legs) carry a reduced set of columns and are not meant to be paired +/// individually in femtoscopy, so only photon-level kinematics plus daughter PID/quality +/// diagnostics (FPhotonExtras) are stored. +class PhotonBuilder +{ + public: + PhotonBuilder() = default; + ~PhotonBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + { + LOG(info) << "Initialize femto photon (PCM) builder..."; + mProducePhotons = utils::enableTable("FPhotons_001", table.producePhotons.value, initContext); + mProduceLitePhotons = utils::enableTable("FLitePhotons_001", table.produceLitePhotons.value, initContext); + mProducePhotonMasks = utils::enableTable("FPhotonMasks_001", table.producePhotonMasks.value, initContext); + mProducePhotonExtras = utils::enableTable("FPhotonExtras_001", table.producePhotonExtras.value, initContext); + + if (mProducePhotons && mProduceLitePhotons) { + LOG(fatal) << "FPhotons and FLitePhotons are mutually exclusive -- enable only one. " + << "FLitePhotons is meant to replace FPhotons at the producer stage (for better compression in derived data)."; + } + if (mProducePhotons || mProduceLitePhotons || mProducePhotonMasks || mProducePhotonExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mPhotonSelection.configure(registry, config, filter); + mPhotonSelection.printSelections(PhotonSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillPhotons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& photonProducts, T5 const& photons, T6 const& v0legs) + { + if (!mFillAnyTable) { + return; + } + for (const auto& photon : photons) { + if (!mPhotonSelection.checkFilters(photon)) { + continue; + } + mPhotonSelection.applySelections(photon, v0legs); + if (!mPhotonSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + if (mProducePhotons) { + photonProducts.producedPhotons(collisionBuilder.collisionIndex(), + photon.pt(), + photon.eta(), + photon.phi()); + } + if (mProduceLitePhotons) { + photonProducts.producedLitePhotons(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binUnsignedPt(photon.pt()), + o2::aod::femtobase::lite::binEta(photon.eta()), + o2::aod::femtobase::lite::binPhi(photon.phi())); + } + if (mProducePhotonMasks) { + photonProducts.producedPhotonMasks(mPhotonSelection.getBitmask()); + } + if (mProducePhotonExtras) { + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + photonProducts.producedPhotonExtras( + photon.cospa(), + photon.dcaXYtopv(), + photon.dcaZtopv(), + photon.chiSquareNDF(), + photon.v0radius(), + posDaughter.pt(), + negDaughter.pt(), + posDaughter.tpcInnerParam(), + negDaughter.tpcInnerParam(), + posDaughter.tpcSignal(), + negDaughter.tpcSignal(), + posDaughter.tpcNSigmaEl(), + negDaughter.tpcNSigmaEl(), + photon.vx(), + photon.vy(), + photon.vz()); + } + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mPhotonSelection.isPassThrough(); } + + private: + PhotonSelection mPhotonSelection; + bool mFillAnyTable = false; + bool mProducePhotons = false; + bool mProduceLitePhotons = false; + bool mProducePhotonMasks = false; + bool mProducePhotonExtras = false; +}; + +} // namespace o2::analysis::femto::photonbuilder +#endif // PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h new file mode 100644 index 00000000000..8c010060eb8 --- /dev/null +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -0,0 +1,295 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonHistManager.h +/// \brief histogram manager for photon (PCM) histograms +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::photonhistmanager +{ +// enum for photon (PCM) histograms +enum PhotonHist { + // analysis + kPt, + kEta, + kPhi, + // qa variables + kCosPa, + kPa, + kDcaToPvXY, + kDcaToPvZ, + kChi2Ndf, + kV0Radius, + kDecayVtxX, + kDecayVtxY, + kDecayVtxZ, + kDecayVtx, + kPosDauTpcNSigmaEl, + kNegDauTpcNSigmaEl, + kPosDauPt, + kNegDauPt, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPosDauVsNegDauTpcNSigmaEl, + kPosDauTpcSignalVsP, + kNegDauTpcSignalVsP, + + kPhotonHistLast +}; + +struct ConfPhotonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBinning"); + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; +}; + +struct ConfPhotonQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonQaBinning"); + o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.8, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; + o2::framework::ConfigurableAxis dcaToPv{"dcaToPv", {{200, -1, 1}}, "DCA of the photon to the PV (cm)"}; + o2::framework::ConfigurableAxis chi2Ndf{"chi2Ndf", {{100, 0, 50}}, "Chi2/NDF of the KF conversion vertex"}; + o2::framework::ConfigurableAxis v0Radius{"v0Radius", {{200, 0, 200}}, "Transverse radius of the conversion point (cm)"}; + o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{200, 0, 200}}, "Distance of the conversion point from the nominal interaction point (cm)"}; + o2::framework::ConfigurableAxis tpcNSigmaEl{"tpcNSigmaEl", {{200, -10, 10}}, "TPC n sigma electron for daughters"}; + o2::framework::ConfigurableAxis dauPt{"dauPt", {{300, 0, 3}}, "Pt of the daughters"}; + o2::framework::ConfigurableAxis dauTpcInnerParam{"dauTpcInnerParam", {{200, 0, 4}}, "Momentum of the daughters at the inner wall of the TPC (GeV/#it{c})"}; + o2::framework::ConfigurableAxis dauTpcSignal{"dauTpcSignal", {{200, 0, 200}}, "TPC dE/dx of the daughters"}; +}; + +// must be in sync with enum PhotonHist +constexpr std::array, kPhotonHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, + {kDcaToPvXY, o2::framework::HistType::kTH1F, "hDcaToPvXY", "DCA_{xy} to PV; DCA_{xy} (cm); Entries"}, + {kDcaToPvZ, o2::framework::HistType::kTH1F, "hDcaToPvZ", "DCA_{z} to PV; DCA_{z} (cm); Entries"}, + {kChi2Ndf, o2::framework::HistType::kTH1F, "hChi2Ndf", "Chi2/NDF of KF conversion vertex; #chi^{2}/NDF; Entries"}, + {kV0Radius, o2::framework::HistType::kTH1F, "hV0Radius", "Transverse radius; r_{xy} (cm); Entries"}, + {kDecayVtxX, o2::framework::HistType::kTH1F, "hDecayVtxX", "X coordinate of conversion point; DV_{X} (cm); Entries"}, + {kDecayVtxY, o2::framework::HistType::kTH1F, "hDecayVtxY", "Y coordinate of conversion point; DV_{Y} (cm); Entries"}, + {kDecayVtxZ, o2::framework::HistType::kTH1F, "hDecayVtxZ", "Z coordinate of conversion point; DV_{Z} (cm); Entries"}, + {kDecayVtx, o2::framework::HistType::kTH1F, "hDecayVtx", "Distance of conversion point from primary vertex; DV (cm); Entries"}, + {kPosDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hPosDauTpcNSigmaEl", "TPC n#sigma_{e} of positive daughter; n#sigma_{TPC,e}; Entries"}, + {kNegDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hNegDauTpcNSigmaEl", "TPC n#sigma_{e} of negative daughter; n#sigma_{TPC,e}; Entries"}, + {kPosDauPt, o2::framework::HistType::kTH1F, "hPosDauPt", "p_{T} of positive daughter; p_{T} (GeV/#it{c}); Entries"}, + {kNegDauPt, o2::framework::HistType::kTH1F, "hNegDauPt", "p_{T} of negative daughter; p_{T} (GeV/#it{c}); Entries"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kPosDauVsNegDauTpcNSigmaEl, o2::framework::HistType::kTH2F, "hPosDauVsNegDauTpcNSigmaEl", "TPC n#sigma_{e} of positive vs negative daughter; n#sigma_{TPC,e} (pos.); n#sigma_{TPC,e} (neg.)"}, + {kPosDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hPosDauTpcSignalVsP", "TPC dE/dx vs p of positive daughter; p_{TPC} (GeV/#it{c}); dE/dx"}, + {kNegDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hNegDauTpcSignalVsP", "TPC dE/dx vs p of negative daughter; p_{TPC} (GeV/#it{c}); dE/dx"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDcaToPvXY, {(confQa).dcaToPv}}, \ + {kDcaToPvZ, {(confQa).dcaToPv}}, \ + {kChi2Ndf, {(confQa).chi2Ndf}}, \ + {kV0Radius, {(confQa).v0Radius}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kPosDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kPosDauPt, {(confQa).dauPt}}, \ + {kNegDauPt, {(confQa).dauPt}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ + {kPosDauVsNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl, (confQa).tpcNSigmaEl}}, \ + {kPosDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, \ + {kNegDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, + +template +auto makePhotonHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +std::map> makePhotonQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis) + PHOTON_HIST_QA_MAP(confBinningAnalysis, confBinningQa)}; +} + +#undef PHOTON_HIST_ANALYSIS_MAP +#undef PHOTON_HIST_QA_MAP + +constexpr char PrefixPhotonQa[] = "PhotonQA/"; +constexpr char PrefixTwoPhotonResonanceDau1Qa[] = "TwoPhotonResonanceDau1Qa/"; +constexpr char PrefixTwoPhotonResonanceDau2Qa[] = "TwoPhotonResonanceDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class PhotonHistManager +/// \brief Histogram manager for PCM photon candidates (reco + QA only, no MC) +template +class PhotonHistManager +{ + public: + PhotonHistManager() = default; + ~PhotonHistManager() = default; + + // init for analysis only + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs) + { + mHistogramRegistry = registry; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + } + + // init for analysis + qa + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, T const& confQaBinning) + { + mHistogramRegistry = registry; + mPlot2d = confQaBinning.plot2d.value; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(specs); + } + } + + template + void fill(T const& photon) + { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(photon); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(photon); + } + } + + private: + void initAnalysis(std::map> const& specs) + { + std::string analysisDir = std::string(Prefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {specs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {specs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {specs.at(kPhi)}); + } + + void initQa(std::map> const& specs) + { + std::string qaDir = std::string(Prefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {specs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {specs.at(kPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvXY, HistTable), getHistDesc(kDcaToPvXY, HistTable), getHistType(kDcaToPvXY, HistTable), {specs.at(kDcaToPvXY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvZ, HistTable), getHistDesc(kDcaToPvZ, HistTable), getHistType(kDcaToPvZ, HistTable), {specs.at(kDcaToPvZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kChi2Ndf, HistTable), getHistDesc(kChi2Ndf, HistTable), getHistType(kChi2Ndf, HistTable), {specs.at(kChi2Ndf)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kV0Radius, HistTable), getHistDesc(kV0Radius, HistTable), getHistType(kV0Radius, HistTable), {specs.at(kV0Radius)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxX, HistTable), getHistDesc(kDecayVtxX, HistTable), getHistType(kDecayVtxX, HistTable), {specs.at(kDecayVtxX)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxY, HistTable), getHistDesc(kDecayVtxY, HistTable), getHistType(kDecayVtxY, HistTable), {specs.at(kDecayVtxY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxZ, HistTable), getHistDesc(kDecayVtxZ, HistTable), getHistType(kDecayVtxZ, HistTable), {specs.at(kDecayVtxZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtx, HistTable), getHistDesc(kDecayVtx, HistTable), getHistType(kDecayVtx, HistTable), {specs.at(kDecayVtx)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauTpcNSigmaEl, HistTable), getHistType(kPosDauTpcNSigmaEl, HistTable), {specs.at(kPosDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcNSigmaEl, HistTable), getHistDesc(kNegDauTpcNSigmaEl, HistTable), getHistType(kNegDauTpcNSigmaEl, HistTable), {specs.at(kNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauPt, HistTable), getHistDesc(kPosDauPt, HistTable), getHistType(kPosDauPt, HistTable), {specs.at(kPosDauPt)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauPt, HistTable), getHistDesc(kNegDauPt, HistTable), getHistType(kNegDauPt, HistTable), {specs.at(kNegDauPt)}); + + if (mPlot2d) { + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {specs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {specs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {specs.at(kPhiVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {specs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistType(kPosDauVsNegDauTpcNSigmaEl, HistTable), {specs.at(kPosDauVsNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcSignalVsP, HistTable), getHistDesc(kPosDauTpcSignalVsP, HistTable), getHistType(kPosDauTpcSignalVsP, HistTable), {specs.at(kPosDauTpcSignalVsP)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcSignalVsP, HistTable), getHistDesc(kNegDauTpcSignalVsP, HistTable), getHistType(kNegDauTpcSignalVsP, HistTable), {specs.at(kNegDauTpcSignalVsP)}); + } + } + + template + void fillAnalysis(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), photon.pt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), photon.phi()); + } + + template + void fillQa(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), photon.pa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvXY, HistTable)), photon.dcaToPvXY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvZ, HistTable)), photon.dcaToPvZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kChi2Ndf, HistTable)), photon.chi2Ndf()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kV0Radius, HistTable)), photon.v0Radius()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxX, HistTable)), photon.decayVtxX()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxY, HistTable)), photon.decayVtxY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxZ, HistTable)), photon.decayVtxZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtx, HistTable)), photon.decayVtx()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcNSigmaEl, HistTable)), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauPt, HistTable)), photon.posDauPt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauPt, HistTable)), photon.negDauPt()); + + if (mPlot2d) { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), photon.pt(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), photon.pt(), photon.phi()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), photon.phi(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), photon.pt(), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauVsNegDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl(), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcSignalVsP, HistTable)), photon.posDauTpcInnerParam(), photon.posDauTpcSignal()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcSignalVsP, HistTable)), photon.negDauTpcInnerParam(), photon.negDauTpcSignal()); + } + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlot2d = false; +}; +} // namespace o2::analysis::femto::photonhistmanager +#endif // PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..484fa18e098 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -0,0 +1,241 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceBuilder.h +/// \brief two photon resonance builder (pi0, eta, ... -> gamma gamma) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::twophotonresonancebuilder +{ +template +struct ConfTwoPhotonResonanceFilters : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.f, "Minimum invariant mass for the resonance"}; + o2::framework::Configurable massMax{"massMax", 1.f, "Maximum invariant mass for the resonance"}; +}; +constexpr const char PrefixPi0Filters[] = "Pi0Filters1"; +constexpr const char PrefixEtaFilters[] = "EtaFilters1"; + +using ConfPi0Filters = ConfTwoPhotonResonanceFilters; +using ConfEtaFilters = ConfTwoPhotonResonanceFilters; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + daughter mask. +// unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no momentum-threshold PID +// switch: photon daughters are unordered and have no momentum-dependent PID regime. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ + o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ + o2::framework::Configurable dau2Mask{"dau2Mask", 0, "Bitmask required for second photon daughter"}; + +struct ConfPi0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Selection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f) +}; + +struct ConfEtaSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaSelection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f) +}; + +#undef TWOPHOTONRESONANCE_DEFAULT_SELECTION + +struct TwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPi0s; + o2::framework::Produces producedPi0Masks; + o2::framework::Produces producedEtas; + o2::framework::Produces producedEtaMasks; +}; + +struct ConfTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoPhotonResonanceTables"); + o2::framework::Configurable producePi0s{"producePi0s", -1, "Produce Pi0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePi0Masks{"producePi0Masks", -1, "Produce Pi0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtas{"produceEtas", -1, "Produce Etas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtaMasks{"produceEtaMasks", -1, "Produce EtaMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two PCM photons (pi0, eta, ... -> gamma gamma) +/// +/// Unlike TwoTrackResonanceBuilder, there is no pos/neg daughter distinction (photons are their own +/// antiparticle) and no momentum-threshold PID switch (photon selection has no momentum-dependent PID +/// regime the way track PID does), so daughter combinatorics run over a single photon partition using +/// CombinationsStrictlyUpperIndexPolicy to avoid self-pairing/double-counting. +template +class TwoPhotonResonanceBuilder +{ + public: + TwoPhotonResonanceBuilder() = default; + ~TwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confTable, T3& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + LOG(info) << "Initialize femto Pi0 builder..."; + mProducePi0s = utils::enableTable("FPi0s_001", confTable.producePi0s.value, initContext); + mProducePi0Masks = utils::enableTable("FPi0Masks_001", confTable.producePi0Masks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + LOG(info) << "Initialize femto Eta builder..."; + mProduceEtas = utils::enableTable("FEtas_001", confTable.produceEtas.value, initContext); + mProduceEtaMasks = utils::enableTable("FEtaMasks_001", confTable.produceEtaMasks.value, initContext); + } + + if (mProducePi0s || mProducePi0Masks || mProduceEtas || mProduceEtaMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& photonPartition, T4& cache) + { + if (!mFillAnyTable) { + return; + } + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [dau1, dau2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(photonSlice, photonSlice))) { + this->fillResonance(col, dau1, dau2, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& dau1, T2 const& dau2) + { + ROOT::Math::PtEtaPhiMVector vecDau1{dau1.pt(), dau1.eta(), dau1.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecDau2{dau2.pt(), dau2.eta(), dau2.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecDau1 + vecDau2; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& dau1, T3 const& dau2, T4& resonanceProducts) + { + reconstructResonance(dau1, dau2); + if (!checkFilters()) { + return; + } + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + if (mProducePi0s) { + resonanceProducts.producedPi0s(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProducePi0Masks) { + resonanceProducts.producedPi0Masks(dau1.mask(), dau2.mask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + if (mProduceEtas) { + resonanceProducts.producedEtas(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProduceEtaMasks) { + resonanceProducts.producedEtaMasks(dau1.mask(), dau2.mask()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0; + float mEta = 0; + float mPhi = 0; + float mMass = 0; + + float mMassMin = 0.f; + float mMassMax = 0.f; + float mPtMin = 0.f; + float mPtMax = 0.f; + float mEtaMin = 0.f; + float mEtaMax = 0.f; + float mPhiMin = 0.f; + float mPhiMax = 0.f; + + bool mFillAnyTable = false; + bool mProducePi0s = false; + bool mProducePi0Masks = false; + bool mProduceEtas = false; + bool mProduceEtaMasks = false; +}; + +} // namespace o2::analysis::femto::twophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..834317c1b03 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -0,0 +1,207 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::twophotonresonancehistmanager +{ +enum TwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfPi0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Binning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.f, 0.3f) +}; + +struct ConfEtaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaBinning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.2f, 0.9f) +}; +#undef TWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m_{#gamma#gamma} (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m_{#gamma#gamma} (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +template +std::map> makeTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixPi0[] = "Pi0/"; +constexpr char PrefixEta[] = "Eta/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two PCM photons (reco + QA only, no MC). +/// Both daughters share the same PhotonHistManager-based sub-manager treatment: there is no +/// pos/neg distinction, nor a PDG-dependent daughter lookup the way TwoTrackResonanceHistManager +/// needs (photon daughters are always massless, regardless of the mother species). +template +class TwoPhotonResonanceHistManager +{ + public: + TwoPhotonResonanceHistManager() = default; + ~TwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& DauSpecs, + T1 const& ConfDauQaBinning) + { + mHistogramRegistry = registry; + mDau1Manager.template init(registry, DauSpecs, ConfDauQaBinning); + mDau2Manager.template init(registry, DauSpecs, ConfDauQaBinning); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& photons) + { + auto dau1 = photons.rawIteratorAt(resonance.dau1Id() - photons.offset()); + mDau1Manager.template fill(dau1); + auto dau2 = photons.rawIteratorAt(resonance.dau2Id() - photons.offset()); + mDau2Manager.template fill(dau2); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + photonhistmanager::PhotonHistManager mDau1Manager; + photonhistmanager::PhotonHistManager mDau2Manager; +}; +} // namespace o2::analysis::femto::twophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 6792d395071..bc894a8600b 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -715,7 +715,9 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); } } @@ -754,7 +756,9 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); } } diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 4f55c529358..331d426893a 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -75,6 +75,7 @@ enum V0Hist { kK0shortMassVsAntiLambdaMass, kStrangeTofVsTofPosDau, kStrangeTofVsTofNegDau, + kArmenterosQtVsAlpha, // mc kOrigin, kPdg, @@ -145,6 +146,8 @@ struct ConfV0QaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis massAntiLambda{"massAntiLambda", {{100, 1, 1.2}}, "mass for antiparticle hypothesis"}; o2::framework::ConfigurableAxis massK0short{"massK0short", {{200, 0.45, 0.55}}, "Mass for k0short hypothesis"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for daughters"}; + o2::framework::ConfigurableAxis armenterosAlpha{"armenterosAlpha", {{200, -1, 1}}, "Armenteros-Podolanski alpha"}; + o2::framework::ConfigurableAxis armenterosQt{"armenterosQt", {{150, 0, 0.3}}, "Armenteros-Podolanski qt (GeV/#it{c})"}; }; constexpr const char PrefixLambdaQaBinning1[] = "LambdaQaBinning1"; @@ -188,6 +191,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kLambdaMassVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hLambdaMassVsAntiLambdaMass", "#Lambda mass vs #bar{#Lambda}; m_{p#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kArmenterosQtVsAlpha, o2::framework::HistType::kTH2F, "hArmenterosPodolanski", "Armenteros-Podolanski; #alpha; q_{T} (GeV/#it{c})"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, @@ -251,7 +255,8 @@ constexpr std::array, kV0HistLast> HistTable = { {kK0shortMassVsLambdaMass, {(confQa).massK0short, (confQa).massLambda}}, \ {kK0shortMassVsAntiLambdaMass, {(confQa).massK0short, (confQa).massAntiLambda}}, \ {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ - {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, + {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kArmenterosQtVsAlpha, {(confQa).armenterosAlpha, (confQa).armenterosQt}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define V0_HIST_MC_QA_MAP(confAnalysis, confQa) \ @@ -518,6 +523,7 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kK0shortMassVsAntiLambdaMass, HistTable), getHistDesc(kK0shortMassVsAntiLambdaMass, HistTable), getHistType(kK0shortMassVsAntiLambdaMass, HistTable), {V0Specs.at(kK0shortMassVsAntiLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofPosDau, HistTable), getHistDesc(kStrangeTofVsTofPosDau, HistTable), getHistType(kStrangeTofVsTofPosDau, HistTable), {V0Specs.at(kStrangeTofVsTofPosDau)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofNegDau, HistTable), getHistDesc(kStrangeTofVsTofNegDau, HistTable), getHistType(kStrangeTofVsTofNegDau, HistTable), {V0Specs.at(kStrangeTofVsTofNegDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kArmenterosQtVsAlpha, HistTable), getHistDesc(kArmenterosQtVsAlpha, HistTable), getHistType(kArmenterosQtVsAlpha, HistTable), {V0Specs.at(kArmenterosQtVsAlpha)}); } } @@ -640,6 +646,7 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), massK0short, massAntiLambda); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofPosDau, HistTable)), v0candidate.strangeTofPosDau(), tofPosDau); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofNegDau, HistTable)), v0candidate.strangeTofNegDau(), tofNegDau); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kArmenterosQtVsAlpha, HistTable)), v0candidate.alpha(), v0candidate.qtArm()); } } diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 958fb9cb99d..89841e1b412 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -637,6 +637,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of d [](float vtxX, float vtxY, float vtxZ) -> float { return std::hypot(vtxX, vtxY, vtxZ); }); +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! Armenteros-Podolanski alpha +DECLARE_SOA_COLUMN(QtArm, qtArm, float); //! Armenteros-Podolanski qt // id columns for Lambda daughter tracks DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track @@ -740,6 +742,8 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lam femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); @@ -800,11 +804,155 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); using FK0shortExtras = FK0shortExtras_001; +namespace femtophotons +{ +// columns for bit masks +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for photon (PCM) selections + +// columns for debug information +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Cosine of pointing angle of the V0 (conversion) to the primary vertex +DECLARE_SOA_DYNAMIC_COLUMN(Pa, pa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); +DECLARE_SOA_COLUMN(DcaToPvXY, dcaToPvXY, float); //! DCAxy of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(DcaToPvZ, dcaToPvZ, float); //! DCAz of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); //! Chi2 / NDF of the reconstructed conversion vertex (KF) +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); //! Transverse radius of the conversion point +DECLARE_SOA_COLUMN(PosDauTpcNSigmaEl, posDauTpcNSigmaEl, float); //! TPC electron nSigma of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcNSigmaEl, negDauTpcNSigmaEl, float); //! TPC electron nSigma of the negative daughter +DECLARE_SOA_COLUMN(PosDauPt, posDauPt, float); //! pT of the positive daughter (at the conversion point) +DECLARE_SOA_COLUMN(NegDauPt, negDauPt, float); //! pT of the negative daughter (at the conversion point) +DECLARE_SOA_COLUMN(PosDauTpcInnerParam, posDauTpcInnerParam, float); //! momentum of the positive daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(NegDauTpcInnerParam, negDauTpcInnerParam, float); //! momentum of the negative daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(PosDauTpcSignal, posDauTpcSignal, float); //! TPC dE/dx of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcSignal, negDauTpcSignal, float); //! TPC dE/dx of the negative daughter +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of conversion point +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of conversion point from nominal interaction point + [](float vtxX, float vtxY, float vtxZ) -> float { + return std::hypot(vtxX, vtxY, vtxZ); + }); +} // namespace femtophotons + +// table for basic PCM photon information (mass is fixed to 0 by construction, not stored) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotons_001, "FPHOTON", 1, //! femto PCM photons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPhotons = FPhotons_001; +using StoredFPhotons = StoredFPhotons_001; + +// table for basic PCM photon information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLitePhotons_001, "FLITEPHOTON", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::UnsignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtobase::lite::unsignedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi); +using FLitePhotons = FLitePhotons_001; +using FLitePhoton = FLitePhotons::iterator; +using StoredFLitePhotons = StoredFLitePhotons_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonMasks_001, "FPHOTONMASK", 1, //! photon (PCM) masks + femtophotons::Mask); +using FPhotonMasks = FPhotonMasks_001; +using StoredFPhotonMasks = StoredFPhotonMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonExtras_001, "FPHOTONEXTRA", 1, //! photon (PCM) extra information + femtophotons::CosPa, + femtophotons::DcaToPvXY, + femtophotons::DcaToPvZ, + femtophotons::Chi2Ndf, + femtophotons::V0Radius, + femtophotons::PosDauPt, + femtophotons::NegDauPt, + femtophotons::PosDauTpcInnerParam, + femtophotons::NegDauTpcInnerParam, + femtophotons::PosDauTpcSignal, + femtophotons::NegDauTpcSignal, + femtophotons::PosDauTpcNSigmaEl, + femtophotons::NegDauTpcNSigmaEl, + femtophotons::DecayVtxX, + femtophotons::DecayVtxY, + femtophotons::DecayVtxZ, + femtophotons::DecayVtx, + femtophotons::Pa); +using FPhotonExtras = FPhotonExtras_001; +using StoredFPhotonExtras = StoredFPhotonExtras_001; + +namespace femtotwophotonresonances +{ +// columns for daughter (photon) bit masks -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances +DECLARE_SOA_COLUMN(MaskDau1, maskDau1, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for first photon daughter +DECLARE_SOA_COLUMN(MaskDau2, maskDau2, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for second photon daughter + +// id columns for the two photon daughters (unordered, no pos/neg distinction) +DECLARE_SOA_INDEX_COLUMN_FULL(Dau1, dau1, int32_t, FPhotons, "_Dau1"); //! index column for first photon daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Dau2, dau2, int32_t, FPhotons, "_Dau2"); //! index column for second photon daughter +} // namespace femtotwophotonresonances + +// table for pi0s (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPi0s = FPi0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s + femtotwophotonresonances::MaskDau1, + femtotwophotonresonances::MaskDau2); +using FPi0Masks = FPi0Masks_001; + +// table for etas (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FEtas = FEtas_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas + femtotwophotonresonances::MaskDau1, + femtotwophotonresonances::MaskDau2); +using FEtaMasks = FEtaMasks_001; + namespace femtokinks { // columns for bit masks diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 70caef3c49c..91add294624 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(femto-producer SOURCES ./femtoProducer.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(femto-producer-derived-to-derived @@ -38,3 +38,8 @@ o2physics_add_dpl_workflow(femto-producer-resonances SOURCES ./femtoProducerResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-two-photon-resonances + SOURCES ./femtoProducerTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 0053724e5a9..6fde51df1f2 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -19,8 +19,10 @@ #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonBuilder.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" @@ -101,6 +103,7 @@ struct FemtoProducer { o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; @@ -134,6 +137,13 @@ struct FemtoProducer { v0builder::V0Builder lambdaBuilder; v0builder::V0Builder antilambdaBuilder; + // photon (PCM) builder + photonbuilder::PhotonBuilderProducts photonBuilderProducts; + photonbuilder::ConfPhotonTables confPhotonTables; + photonbuilder::ConfPhotonFilters confPhotonFilters; + photonbuilder::ConfPhotonBits confPhotonBits; + photonbuilder::PhotonBuilder photonBuilder; + // charm hadron builder charmhadronbuilder::CharmHadronBuilderProducts charmHadronBuilderProducts; charmhadronbuilder::ConfCharmHadronFilters confCharmHadronFilters; @@ -198,6 +208,8 @@ struct FemtoProducer { lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); + photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); + d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); @@ -229,6 +241,9 @@ struct FemtoProducer { !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } + if (photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb)) { + LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; + } if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { @@ -269,6 +284,7 @@ struct FemtoProducer { add("k0short", k0shortBuilder.fillAnyTable(), k0shortBuilder.isPassThrough()); add("lambda", lambdaBuilder.fillAnyTable(), lambdaBuilder.isPassThrough()); add("antilambda", antilambdaBuilder.fillAnyTable(), antilambdaBuilder.isPassThrough()); + add("photon", photonBuilder.fillAnyTable(), photonBuilder.isPassThrough()); add("d0", d0Builder.fillAnyTable(), d0Builder.isPassThrough()); add("d0bar", d0barBuilder.fillAnyTable(), d0barBuilder.isPassThrough()); add("lc", lcBuilder.fillAnyTable(), lcBuilder.isPassThrough()); @@ -322,6 +338,8 @@ struct FemtoProducer { static_cast(doprocessTracksRun3PbPb) + static_cast(doprocessTracksRun3PbPbWithEventShape) + static_cast(doprocessTracksV0sRun3pp) + + static_cast(doprocessTracksPhotonsRun3pp) + + static_cast(doprocessTracksPhotonsRun3PbPb) + static_cast(doprocessTracksV0sRun3PbPb) + static_cast(doprocessTracksV0sCascadesRun3pp) + static_cast(doprocessTracksV0sCascadesRun3PbPb) + @@ -424,6 +442,13 @@ struct FemtoProducer { k0shortBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); } + // processing photons (PCM) + template + void processPhotons(T1 const& col, T2 const& photons, T3 const& v0legs) + { + photonBuilder.fillPhotons(col, collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); + } + // processing kinks template void processKinks(T1 const& col, T2 const& tracks, T3 const& kinks) @@ -540,6 +565,42 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Provide tracks and v0s", false); + void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); + + void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); + void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, diff --git a/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx new file mode 100644 index 00000000000..4752d5f3a54 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx @@ -0,0 +1,88 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTwoPhotonResonances.cxx +/// \brief Tasks that produces femto tables for resonances built from two PCM photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for resonance daughter photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + // resonance filters + twophotonresonancebuilder::ConfPi0Filters confPi0Filter; + twophotonresonancebuilder::ConfEtaFilters confEtaFilter; + + // resonance builders + twophotonresonancebuilder::ConfTwoPhotonResonanceTables confTwoPhotonResonanceTables; + twophotonresonancebuilder::TwoPhotonResonanceBuilderProducts twoPhotonResonanceBuilderProducts; + twophotonresonancebuilder::TwoPhotonResonanceBuilder pi0Builder; + twophotonresonancebuilder::TwoPhotonResonanceBuilder etaBuilder; + + void init(o2::framework::InitContext& context) + { + // init builders + pi0Builder.init(confPi0Filter, confTwoPhotonResonanceTables, context); + etaBuilder.init(confEtaFilter, confTwoPhotonResonanceTables, context); + } + + // process functions + void processPi0(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + pi0Builder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processPi0, "Build Pi0 candidates", true); + + void processEta(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + etaBuilder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processEta, "Build Eta candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index 0aa26cf38d7..6d4bb461d62 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -19,11 +19,21 @@ o2physics_add_dpl_workflow(femto-twotrackresonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-two-photon-resonance-qa + SOURCES femtoTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-v0-qa SOURCES femtoV0Qa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-photon-qa + SOURCES femtoPhotonQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-kink-qa SOURCES femtoKinkQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx new file mode 100644 index 00000000000..ad6adbbeebf --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx @@ -0,0 +1,101 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPhotonQa.cxx +/// \brief QA task for photons (PCM) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPhotonQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + photonhistmanager::ConfPhotonBinning confPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonQaBinning; + photonhistmanager::PhotonHistManager photonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoPhotonQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto photonHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonBinning, confPhotonQaBinning); + photonHistManager.init(&hRegistry, photonHistSpec, confPhotonQaBinning); + + hRegistry.print(); + } + + void processPhotons(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (photonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& photon : photonSlice) { + photonHistManager.fill(photon); + } + } + PROCESS_SWITCH(FemtoPhotonQa, processPhotons, "Process photons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..afcf432c8e4 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx @@ -0,0 +1,143 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for pi0s + twophotonresonancebuilder::ConfPi0Selection confPi0Selection; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(confPi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixPi0, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kPi0> + pi0HistManager; + + // setup for etas + twophotonresonancebuilder::ConfEtaSelection confEtaSelection; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(confEtaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixEta, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kEta> + etaHistManager; + + // setup for daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + o2::framework::HistogramRegistry hRegistry{"FemtoTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto dauHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonDauBinning, confPhotonDauQaBinning); + + if ((static_cast(doprocessPi0s) + static_cast(doprocessEtas)) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessPi0s) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confPi0Binning); + pi0HistManager.init(&hRegistry, pi0HistSpec, dauHistSpec, confPhotonDauQaBinning); + } + if (doprocessEtas) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confEtaBinning); + etaHistManager.init(&hRegistry, etaHistSpec, dauHistSpec, confPhotonDauQaBinning); + } + } + + void processPi0s(FilteredFemtoCollision const& col, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + auto pi0Slice = pi0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (pi0Slice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& pi0 : pi0Slice) { + pi0HistManager.fill(pi0, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processPi0s, "Process Pi0s", true); + + void processEtas(FilteredFemtoCollision const& col, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + auto etaSlice = etaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (etaSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& eta : etaSlice) { + etaHistManager.fill(eta, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processEtas, "Process Etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} From fe255edb6ade22276a2b11af8acf45363253ea60 Mon Sep 17 00:00:00 2001 From: Hirak Koley Date: Mon, 5 Oct 2026 04:39:44 +0530 Subject: [PATCH 171/648] [PWGLF] Updated event, track selections, a new rapidity column, more improvements (#18170) --- .../DataModel/LFHResonanceCorrelationTables.h | 10 +- .../hResonanceCorrelationFilter.cxx | 613 +++++++++--------- .../Resonances/hResonanceCorrelation.cxx | 419 ++++++++---- 3 files changed, 599 insertions(+), 443 deletions(-) diff --git a/PWGLF/DataModel/LFHResonanceCorrelationTables.h b/PWGLF/DataModel/LFHResonanceCorrelationTables.h index 2bc281daaf6..3caad772493 100644 --- a/PWGLF/DataModel/LFHResonanceCorrelationTables.h +++ b/PWGLF/DataModel/LFHResonanceCorrelationTables.h @@ -53,8 +53,12 @@ DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true phys DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); // pdg code of the MC particle +// Rapidity under the mass hypothesis of the species this entry was actually +// selected as (pion or kaon, from isValidAssocTrack()); -999 for +// the species-agnostic AssocHadron pool, which has no mass hypothesis to use. +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); } // namespace assocHadrons -DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode); +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode, assocHadrons::Rapidity); /// _________________________________________ /// Table for storing assoc track PID namespace assocPID @@ -86,6 +90,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // phi candidate's own rapidity (kaon-mass-pair system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_PhiPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_PhiNegDaughter"); @@ -104,6 +109,7 @@ DECLARE_SOA_TABLE( assocPhis::Eta, assocPhis::Phi, assocPhis::Mass, + assocPhis::Rapidity, assocPhis::PosTrackDaughterId, assocPhis::NegTrackDaughterId); @@ -127,6 +133,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // K*0 candidate's own rapidity (kaon+pion system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_KstarPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_KstarNegDaughter"); @@ -145,6 +152,7 @@ DECLARE_SOA_TABLE( assocKstars::Eta, assocKstars::Phi, assocKstars::Mass, + assocKstars::Rapidity, assocKstars::PosTrackDaughterId, assocKstars::NegTrackDaughterId); } // namespace o2::aod diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx index 959014c61ce..f35fa0fd078 100644 --- a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -18,9 +18,6 @@ #include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -28,7 +25,6 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -56,7 +52,6 @@ using namespace o2; using namespace o2::soa; using namespace o2::constants::math; -using namespace o2::aod::rctsel; using namespace o2::framework; using namespace o2::framework::expressions; @@ -72,64 +67,43 @@ enum PIDCutType { struct HResonanceCorrelationFilter { - Service ccdb; - - RCTFlagsChecker rctChecker; - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // master analysis switches - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; - - // Operational - Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; - - Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; - Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; - - Configurable cfgRapidity{"cfgRapidity", 0.5f, "Rapidity cut"}; - - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; - - struct : ConfigurableGroup { - std::string prefix = "eventSelections"; - // event filtering - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; - Configurable minCentPercent{"minCentPercent", 0, "minimum centrality percentage"}; - Configurable maxCentPercent{"maxCentPercent", 100, "maximum centrality percentage"}; - } eventSelections; - + // Single, shared event-selection cut list used by every process function + // (processTriggers, processAssocPions/Kaons/Hadrons, processPhis, + // processKstars, and their MC counterparts) via isSelectedEvents(), so the + // trigger table and every associated-track/resonance table are always + // built over the exact same set of selected collisions. struct : ConfigurableGroup { + std::string prefix = "configEvents"; Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; - Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; - Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; - Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; - Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; - Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; } configEvents; + // Merged from the formerly separate configTracks/generalSelections/ + // trackSelections groups -- all three were "cuts applied to a track or + // track pair", just split by who last touched them. No member names + // collided, so every Configurable below keeps its original key; only the + // "." prefix at each call site changed, to configTracks. struct : ConfigurableGroup { + std::string prefix = "configTracks"; // Pre-selection Track cuts - Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "max TPC chi2/clusters for good track"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "max ITS chi2/clusters for good track"}; + Configurable cfgMinTPCCrossedRowsOverFindableCls{"cfgMinTPCCrossedRowsOverFindableCls", 0.8f, "min ratio of TPC crossed rows over findable clusters for good track"}; + Configurable cfgMinITSNCls{"cfgMinITSNCls", 5, "min number of ITS hits (clusters) for good track"}; // DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; // DCAz to PV Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; @@ -139,64 +113,44 @@ struct HResonanceCorrelationFilter { Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; - Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; - } configTracks; - struct : ConfigurableGroup { - std::string prefix = "generalSelections"; - - // Associated particle selections in phase space + // Associated particle selections in phase space (formerly generalSelections) Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; - // Trigger particle selections in phase space + // Trigger particle selections in phase space (formerly generalSelections) Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; Configurable triggerPtCutMin{"triggerPtCutMin", 3, "triggerptmin"}; Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; - } generalSelections; - struct : ConfigurableGroup { - std::string prefix = "trackSelections"; - // Track quality + // Track quality (formerly trackSelections) Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; - Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for phi daughter tracks"}; - Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for phi daughter tracks"}; - - // Associated pion identification - Configurable pionMinBayesProb{"pionMinBayesProb", 0.95, "minimal Bayesian probability for pion ID"}; - Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; - Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; - - // Associated kaon identification (mirrors the pion selection above, with - // the accept/reject roles swapped: accept kaon-consistent tracks, reject - // pion-/proton-consistent ones). Selected via the Species template tag on - // isValidAssocTrack() below -- production path (which table rows - // get made) is still separate per process function, only the PID gate - // logic itself is shared. - Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; - // primary particle DCAxy selections + // primary particle DCAxy selections (formerly trackSelections) // formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - } trackSelections; + } configTracks; struct : ConfigurableGroup { + std::string prefix = "configPID"; /// PID Selections - Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + // Were loose top-level Configurables; folded in here as unused (no call + // sites reference them) and logically the same "Kaon PID" kind as the + // kaonTPCPIDcuts/kaonTOFPIDcuts group below. + Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; + Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; // Kaon Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; @@ -213,10 +167,28 @@ struct HResonanceCorrelationFilter { Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; + + // Associated kaon identification (formerly trackSelections; mirrors the + // pion selection above, with the accept/reject roles swapped: accept + // kaon-consistent tracks, reject pion-/proton-consistent ones). Selected + // via the Species template tag on isValidAssocTrack() below -- + // production path (which table rows get made) is still separate per + // process function, only the PID gate logic itself is shared. + Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; + + // Associated pion identification (formerly trackSelections) + Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; + Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; } configPID; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + // Unused in this file today -- nothing here reads from CCDB any more + // (Zorro was the only consumer and has been removed). Kept (grouped, + // rather than removed) since that's outside today's ask. + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + } ccdbConfigurations; // must include windows for background and peak // @@ -229,7 +201,10 @@ struct HResonanceCorrelationFilter { // mirror the analysis task's own massWindowConfigurationsPhi/Kstar // (sigma = PDG Gamma/2.355 placeholder -- refit from your own peak and // update both files together, they must agree on what "sigma" means). - Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurations"; + Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + } massWindowConfigurations; struct : ConfigurableGroup { std::string prefix = "massWindowConfigurationsPhi"; @@ -254,24 +229,40 @@ struct HResonanceCorrelationFilter { // For extracting strangeness mass QA plots struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - ConfigurableAxis axisPhiMass{"axisPhiMass", {200, 0.99f, 1.08f}, "M(K^{+}K^{-})"}; - ConfigurableAxis axisKstarMass{"axisKstarMass", {200, 0.75f, 1.05f}, "M(K#pi)"}; - ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Centrality percentile bins"}; + // Daughter/trigger-track QA axes (Phi->KK, K*0->Kpi, trigger hadrons): + // each is a 1D QA histogram's own axis, booked in init(). + ConfigurableAxis axisEtaQA{"axisEtaQA", {100, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisNSigmaQA{"axisNSigmaQA", {100, -10.0f, 10.0f}, "n#sigma"}; + ConfigurableAxis axisDCAxyQA{"axisDCAxyQA", {200, -0.5f, 0.5f}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDCAzQA{"axisDCAzQA", {200, -0.5f, 0.5f}, "DCA_{z} (cm)"}; + ConfigurableAxis axisTPCCrossedRowsQA{"axisTPCCrossedRowsQA", {160, 0, 160}, "TPC crossed rows"}; } axesConfigurations; // QA - Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; - // Do declarative selections for DCAs, if possible - Filter preFilterTracks = nabs(aod::track::dcaXY) < trackSelections.dcaXYconstant + trackSelections.dcaXYpTdep * nabs(aod::track::signed1Pt); + struct : ConfigurableGroup { + std::string prefix = "qaConfigurations"; + Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; + } qaConfigurations; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; + // Do declarative selections for DCAs, if possible + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && aod::track::pt > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.dcaXYconstant + configTracks.dcaXYpTdep * nabs(aod::track::signed1Pt)) && + (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + // All four aliases below join TrackSelection + TrackSelectionExtension so + // that trackCut() -- the shared "is this a good, well-reconstructed track" + // baseline -- is callable (and applied) on trigger and associated-particle + // tracks exactly as it already is on Phi/K*0 daughters (TrackCandidates), + // not just on the latter. + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; using DauTracks = soa::Join; using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID - using IDTracks = soa::Join; - using IDTracksMC = soa::Join; + using IDTracks = soa::Join; + using IDTracksMC = soa::Join; Produces triggerTrack; Produces triggerTrackExtra; @@ -287,10 +278,6 @@ struct HResonanceCorrelationFilter { using MCEventCandidates = soa::Join; using MCTrackCandidates = soa::Filtered>; - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - int mRunNumber = -1; - struct TriggCandidate { float pt = 0.f; int collisionId = -1; @@ -304,44 +291,125 @@ struct HResonanceCorrelationFilter { void init(InitContext const&) { - rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); - zorroSummary.setObject(zorro.getZorroSummary()); - mRunNumber = -1; - - mMinMassPhi = massWindowConfigurationsPhi.peakMass - maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMaxMassPhi = massWindowConfigurationsPhi.peakMass + maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMinMassKstar = massWindowConfigurationsKstar.peakMass - maxMassNSigma * massWindowConfigurationsKstar.sigma; - mMaxMassKstar = massWindowConfigurationsKstar.peakMass + maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMinMassPhi = massWindowConfigurationsPhi.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMaxMassPhi = massWindowConfigurationsPhi.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMinMassKstar = massWindowConfigurationsKstar.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMaxMassKstar = massWindowConfigurationsKstar.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; LOGF(info, "Assoc mass pre-cut windows: Phi [%.4f, %.4f] GeV, K*0 [%.4f, %.4f] GeV", mMinMassPhi, mMaxMassPhi, mMinMassKstar, mMaxMassKstar); + + // QA histograms for Phi (K+K-) and K*0 (K-pi) daughter tracks: booked once + // per daughter species (Phi's kaon, Kstar's kaon, Kstar's pion), filled at + // the point each candidate passes all cuts (including the mass window) in + // processPhis/processKstars (and their MC counterparts). + auto bookDaughterQA = [&](const std::string& dir) { + histos.add((dir + "/hPt").c_str(), "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add((dir + "/hEta").c_str(), "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add((dir + "/hTPCNSigma").c_str(), "TPC n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hTOFNSigma").c_str(), "TOF n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hDCAxy").c_str(), "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hDCAz").c_str(), "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hTPCCrossedRows").c_str(), "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + // 2D pT-binned QA, same variables as above, to see pT dependence + histos.add((dir + "/hPtVsDCAxy").c_str(), "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hPtVsDCAz").c_str(), "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hPtVsTPCNSigma").c_str(), "p_{T} vs TPC n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hPtVsTOFNSigma").c_str(), "p_{T} vs TOF n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + }; + bookDaughterQA("QA/Phi/Kaon"); + bookDaughterQA("QA/Kstar/Kaon"); + bookDaughterQA("QA/Kstar/Pion"); + + // Trigger hadron QA: no PID applied to trigger tracks, so only the 5 + // non-PID variables are booked (pt, eta, dcaXY, dcaZ, TPC crossed rows), + // plus the pT-binned 2D versions of DCAxy/DCAz (no nSigma -- no PID here). + histos.add("QA/TriggerHadron/hPt", "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add("QA/TriggerHadron/hEta", "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add("QA/TriggerHadron/hDCAxy", "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hDCAz", "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add("QA/TriggerHadron/hTPCCrossedRows", "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + histos.add("QA/TriggerHadron/hPtVsDCAxy", "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hPtVsDCAz", "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); } - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + // HIST() needs a compile-time literal path, so each daughter species gets + // its own explicit fill function rather than one generic/parameterized + // helper. Kaon QA uses the Ka PID accessors, pion QA uses the Pi ones. + template + void fillPhiKaonQA(const Track& track) { - if (mRunNumber == bc.runNumber()) { - return; - } + histos.fill(HIST("QA/Phi/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Phi/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); - zorro.populateHistRegistry(histos, bc.runNumber()); + template + void fillKstarKaonQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + + template + void fillKstarPionQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Pion/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Pion/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigma"), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hTOFNSigma"), track.tofNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaPi()); + } - mRunNumber = bc.runNumber(); + // Trigger hadrons carry no PID selection, so only the 5 non-PID variables + // are filled here. + template + void fillTriggerHadronQA(const Track& track) + { + histos.fill(HIST("QA/TriggerHadron/hPt"), track.pt()); + histos.fill(HIST("QA/TriggerHadron/hEta"), track.eta()); + histos.fill(HIST("QA/TriggerHadron/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/TriggerHadron/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAz"), track.pt(), track.dcaZ()); } template - bool isSelected(const Coll& collision, bool fillHist = true) + bool isSelectedEvents(const Coll& collision, bool fillHist = true) { auto applyCut = [&](bool enabled, bool condition, int bin) { if (!enabled) { @@ -379,15 +447,17 @@ struct HResonanceCorrelationFilter { return false; } - if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; } - if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } @@ -398,87 +468,35 @@ struct HResonanceCorrelationFilter { return true; } - // this function allows for all event selections to be done in a modular way - template - bool isCollisionSelected(TCollision const& collision) - { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return false; - } - if (collision.centFT0M() > eventSelections.maxCentPercent || collision.centFT0M() < eventSelections.minCentPercent) { - return false; - } - if (!collision.isInelGt0() && eventSelections.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return false; - } - } - return true; - } - - // more event selections in Pb-Pb - template - bool isCollisionSelectedPbPb(TCollision const& collision) - { - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) // cut time intervals with dead ITS staves - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - return false; - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) /* Below min occupancy and Above max occupancy*/ - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) // reject collisions close to Time Frame borders - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) // reject events affected by the ITS ROF border - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) // rejects collisions which are associated with the same "found-by-T0" bunch crossing - return false; - return true; - } - template bool trackCut(const TrackType& track) { // basic track cuts - if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut - { + // NOTE: pT-dependent |DCAxy| cut is NOT re-applied here -- it's already + // enforced unconditionally, for every track in every process function, + // by acceptanceFilter (Filter on aod::track::dcaXY using the live + // configTracks.dcaXYconstant/dcaXYpTdep). A second check here would be + // either a no-op (same cut) or a stale one (if those configurables are + // ever retuned and this duplicate doesn't track them). + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; } - if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { + if (configTracks.cfgPVContributor && !track.isPVContributor()) { return false; } if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { return false; } - if (configTracks.cfgHasTOF && !track.hasTOF()) { + if (track.tpcChi2NCl() > configTracks.cfgMaxTPCChi2NCl) { return false; } - if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { + if (track.itsChi2NCl() > configTracks.cfgMaxITSChi2NCl) { return false; } - if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { - return false; - } - if (configTracks.cfgPVContributor && !track.isPVContributor()) { + if (track.tpcCrossedRowsOverFindableCls() < configTracks.cfgMinTPCCrossedRowsOverFindableCls) { return false; } - if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { + if (track.itsNCls() < configTracks.cfgMinITSNCls) { return false; } @@ -669,23 +687,28 @@ struct HResonanceCorrelationFilter { template bool isValidTrigger(TTrack const& track) { - if (track.eta() > generalSelections.triggerEtaMax || track.eta() < generalSelections.triggerEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(track)) { + return false; + } + if (track.eta() > configTracks.triggerEtaMax || track.eta() < configTracks.triggerEtaMin) { return false; } // if (track.sign()= 1 ) {continue;} - if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { + if (track.pt() > configTracks.triggerPtCutMax || track.pt() < configTracks.triggerPtCutMin) { return false; } - if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (track.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!track.hasITS() && trackSelections.triggerRequireITS) { + if (!track.hasITS() && configTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelections.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(BIT_CHECK(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } return true; @@ -715,16 +738,21 @@ struct HResonanceCorrelationFilter { { static_assert(Species == AssocPion || Species == AssocKaon || Species == AssocHadron, "isValidAssocTrack: unknown species tag"); - if (assoc.eta() > generalSelections.assocEtaMax || assoc.eta() < generalSelections.assocEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(assoc)) { + return false; + } + if (assoc.eta() > configTracks.assocEtaMax || assoc.eta() < configTracks.assocEtaMin) { return false; } - if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { + if (assoc.pt() > configTracks.assocPtCutMax || assoc.pt() < configTracks.assocPtCutMin) { return false; } - if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (assoc.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!assoc.hasITS() && trackSelections.assocRequireITS) { + if (!assoc.hasITS() && configTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } @@ -733,26 +761,26 @@ struct HResonanceCorrelationFilter { if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { if (assoc.tofSignal() > 0) { if constexpr (Species == AssocKaon) { - if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > trackSelections.assocKaonNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPi() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tofNSigmaKa() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); @@ -761,18 +789,18 @@ struct HResonanceCorrelationFilter { nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); } else { if constexpr (Species == AssocKaon) { - if (assoc.tpcNSigmaKa() > trackSelections.assocKaonNSigmaTPCFOF) + if (assoc.tpcNSigmaKa() > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) + if (assoc.tpcNSigmaPi() > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } } @@ -794,12 +822,26 @@ struct HResonanceCorrelationFilter { } } + // Rapidity under the species mass hypothesis; AssocHadron has no mass + // hypothesis to assume, so it's filled with a dummy value instead. + float rapidity = -999.f; + if constexpr (Species == AssocKaon) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassKPlus) + .Rapidity(); + } else if constexpr (Species == AssocPion) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassPiPlus) + .Rapidity(); + } + assocHadrons( assoc.collisionId(), physicalPrimary, assoc.globalIndex(), origPt, - code); + code, + rapidity); assocPID( nSigmaTPCTOF[0], nSigmaTPCTOF[1], @@ -813,10 +855,10 @@ struct HResonanceCorrelationFilter { } // for real data processing - void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -827,6 +869,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -851,10 +894,10 @@ struct HResonanceCorrelationFilter { } // for MC processing - void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -865,6 +908,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -892,25 +936,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -920,25 +951,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -948,25 +966,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -976,25 +981,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1004,25 +996,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { - return; - } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1031,25 +1010,12 @@ struct HResonanceCorrelationFilter { continue; } } - void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { - return; - } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1062,7 +1028,7 @@ struct HResonanceCorrelationFilter { void processPhis(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1133,6 +1099,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + assocPhis( collision.globalIndex(), false, @@ -1141,6 +1111,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1156,7 +1127,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1219,6 +1190,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + bool mcTruePhi = false; bool mcPhysicalPrimary = false; @@ -1259,6 +1234,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1271,7 +1247,7 @@ struct HResonanceCorrelationFilter { void processKstars(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1332,8 +1308,15 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + // Rapidity under whichever mass hypothesis (kaon or pion) was + // actually assigned to that daughter for this hypothesis -- matches + // posTrack/negTrack above, not a fixed species per pos/neg slot. assocKstars( collision.globalIndex(), @@ -1343,6 +1326,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } @@ -1359,7 +1343,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1413,6 +1397,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + bool mcTrueKstar = false; bool mcPhysicalPrimary = false; @@ -1456,6 +1444,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx index 35f54137039..c631ee15932 100644 --- a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -97,7 +97,6 @@ struct HResonanceCorrelation { struct : ConfigurableGroup { std::string prefix = "masterConfigurations"; - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; Configurable doFullCorrelationStudy{"doFullCorrelationStudy", true, "if true, do full correlation study by creating all THnSparse histograms for the correlation function"}; Configurable doCorrelationHadron{"doCorrelationHadron", false, "do Hadron correlation"}; @@ -106,11 +105,14 @@ struct HResonanceCorrelation { Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; Configurable doCorrelationKaon{"doCorrelationKaon", false, "do Kaon correlation"}; Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; + // Used by the generator-level (MC-truth) selection in processPrediction, + // not by isSelectedEvents() below -- that's a different, reconstructed- + // level selection now driven entirely by configEvents.* to match the + // filter task (hResonanceCorrelationFilter.cxx) exactly. + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events (generator level, see processPrediction)"}; + // Still used directly by the closure-test/prediction blocks (reconstructed + // vs. generated z-vertex checks), independent of isSelectedEvents(). + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position (closure test / prediction only)"}; Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; @@ -127,7 +129,6 @@ struct HResonanceCorrelation { Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; - Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; @@ -136,9 +137,23 @@ struct HResonanceCorrelation { Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; + + // Reconstructed-event selection cuts -- deliberately the same cut set and + // names as configEvents in hResonanceCorrelationFilter.cxx, so a collision + // that was selected to produce the trigger/associated/candidate tables is + // selected identically here (previously this task re-derived its own, + // different cut list via isisCollisionSelect()/isisCollisionSelectPbPb(), + // which could disagree with the filter task's selection). + struct : ConfigurableGroup { + std::string prefix = "configEvents"; + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + } configEvents; // Axes - configurable for smaller sizes struct : ConfigurableGroup { @@ -348,6 +363,11 @@ struct HResonanceCorrelation { static constexpr int IndexHadron = 4; uint16_t doCorrelation = 0; + // Which species' processSameEventH() is responsible for filling the + // shared CollCutCounts/EventQA histograms for a given collision, computed + // once in init() -- see the comment there. -1 means none of the same-event + // process functions are actually enabled (bit + switch), so nothing fills them. + int eventQAOwnerIndex = -1; int mRunNumber = 0; int mRunNumberZorro = 0; @@ -465,6 +485,27 @@ struct HResonanceCorrelation { if (masterConfigurations.doCorrelationHadron) SETBIT(doCorrelation, IndexHadron); + // Pick exactly one "owner" species for the shared CollCutCounts/EventQA + // fills: the highest-priority species (Phi > Kstar > Pion > Kaon > + // Hadron) that will actually run its processSameEventH() (both its + // doCorrelation bit AND its PROCESS_SWITCH on). Every processSameEventH + // calls isSelectedEvents(collision, eventQAOwnerIndex == IndexX) -- only + // the owner's call passes fillHist=true, so a collision examined by + // several same-event process functions in the same run (e.g. Phi and + // K*0 both enabled) is still only counted once, instead of once per + // enabled species. + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis) { + eventQAOwnerIndex = IndexPhi; + } else if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars) { + eventQAOwnerIndex = IndexKstar; + } else if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + eventQAOwnerIndex = IndexPion; + } else if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + eventQAOwnerIndex = IndexKaon; + } else if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + eventQAOwnerIndex = IndexHadron; + } + // Store axis ranges to prevent spurious filling // axis status: // --- Delta-phi is safe -> math forbids insanity @@ -642,13 +683,20 @@ struct HResonanceCorrelation { validCollisions[i].reserve(masterConfigurations.mixingParameter); } } - if (!masterConfigurations.doPPAnalysis) { - // event selections in Pb-Pb - histos.add("hEventSelection", "hEventSelection", kTH1F, {{10, 0, 10}}); - TString eventSelLabel[] = {"all", "sel8", "kIsTriggerTVX", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup "}; - for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); - } + // Event-selection cut-flow, same cut set/order/labels as CollCutCounts in + // hResonanceCorrelationFilter.cxx, filled by isSelectedEvents() -- see + // eventQAOwnerIndex above for why only one process function fills it. + if (eventQAOwnerIndex != -1) { + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); } // ======================================================================== @@ -708,6 +756,24 @@ struct HResonanceCorrelation { doME = doprocessMixedEventHHadrons; } + // Same-/mixed-event invariant-mass cross-check (Phi/K*0 only). Booked + // independent of doFullCorrelationStudy/fillCorrelationHistWithMass + // since these are standalone QA histograms, not part of the + // Signal/LeftBg/RightBg THnF family -- just need doSE/doME (the + // process switch(es) for this species) to actually get filled. + if (i == IndexPhi && doSE) { + histos.add("hPhiInvMass", "Phi candidate invariant mass, same event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doSE) { + histos.add("hKstarInvMass", "K*0 candidate invariant mass, same event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexPhi && doME) { + histos.add("hPhiMixedEventInvMass", "Phi candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doME) { + histos.add("hKstarMixedEventInvMass", "K*0 candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + // Kinematic QA (Only for Phi) if (i < IndexPion && (doSE || doME || doprocessPrediction || doprocessClosureTest)) { if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { @@ -955,115 +1021,92 @@ struct HResonanceCorrelation { } } - // this function allows for all event selections to be done in a modular way + // Single event-selection predicate, cut-for-cut and bin-for-bin the same as + // isSelectedEvents() in hResonanceCorrelationFilter.cxx, so a collision is + // selected identically whether the filter task is producing its tables or + // this task is correlating them. Replaces the former isisCollisionSelect() + // (pp) / isisCollisionSelectPbPb() (Pb-Pb) pair, which carried their own, + // different cut lists (centrality-range, INEL>0, ITS-good-layers, TPC + // occupancy) that could disagree with the filter task's selection. + // + // fillHist controls the CollCutCounts cut-flow and EventQA/hMult+hPvz + // fills: pass true only from the one process function that "owns" this + // collision for QA purposes (see eventQAOwnerIndex in init()) so a + // collision examined by several enabled same-event process functions is + // still only counted once. Mixed-event callers always pass false, since ME + // revisits the same collisions many times per mixing pool and has its own + // separate MixingQA histograms. template - bool isisCollisionSelect(TCollision const& collision) + bool isSelectedEvents(TCollision const& collision, bool fillHist = true) { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { - return false; - } - if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { - return false; - } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initZorro(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { return false; } - } - return true; - } - - // event selections in Pb-Pb - template - bool isisCollisionSelectPbPb(TCollision const& collision, bool fillHists) - { - if (fillHists) - histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; - // Perform basic event selection - if (!collision.sel8()) { - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 2.5 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - // cut time intervals with dead ITS staves + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 4.5 /* collisions after cut time intervals with dead ITS staves*/); - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { - // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - // use this cut at low multiplicities with caution + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 5.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; - if (fillHists) - histos.fill(HIST("hEventSelection"), 6.5 /* Below min occupancy and Above max occupancy*/); + } - /* - if (collision.alias_bit(kTVXinTRD)) { - // TRD triggered + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - */ - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - // reject collisions close to Time Frame borders - // O2-4623 + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 7.5 /* reject collisions close to Time Frame borders*/); - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - // reject events affected by the ITS ROF border - // O2-4309 - return false; + // Zorro trigger-skim accounting: this task's own feature, not part of the + // filter task's cut set (which has no skim awareness) -- opt-in via + // zorroMask, off by default, so it doesn't change the baseline cut set. + if (zorroMask.value != "") { + auto bc = collision.template bc_as(); + initZorro(bc); + bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return false; + } } - if (fillHists) - histos.fill(HIST("hEventSelection"), 8.5 /* reject events affected by the ITS ROF border*/); - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - // rejects collisions which are associated with the same "found-by-T0" bunch crossing - // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 9.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + return true; } @@ -2106,7 +2149,7 @@ struct HResonanceCorrelation { for (auto const& collision : collisions) { // ________________________________________________ // Perform basic event selection - if (!isisCollisionSelect(collision)) { + if (!isSelectedEvents(collision, false)) { continue; } @@ -2126,6 +2169,15 @@ struct HResonanceCorrelation { void processSameEventHHadrons(soa::Join::iterator const& collision, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // Guard against the fatal "histogram not found" crash: the per-species + // histograms this function fills are only booked in init() when + // TESTBIT(doCorrelation, IndexHadron) is also set (see init(), ~line 831) + // -- doCorrelation defaults to 0 (masterConfigurations.doCorrelationHadron + // defaults false) while this PROCESS_SWITCH defaults true, so without this + // guard the default config fills unbooked histograms and crashes. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. @@ -2144,15 +2196,15 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. fillHist=true only from the owning + // species (see eventQAOwnerIndex in init()) so CollCutCounts/EventQA + // aren't multi-counted when several same-event species run together. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexHadron)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexHadron && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA @@ -2212,6 +2264,12 @@ struct HResonanceCorrelation { void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPion=false, this switch=true) fills + // hPionEtaVsPtAllSelected etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2228,15 +2286,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPion)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPion && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& pion : associatedPions) { @@ -2277,6 +2334,13 @@ struct HResonanceCorrelation { void processSameEventHKaons(soa::Join::iterator const& collision, soa::Join const& associatedKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKaon=false, this switch=true) fills + // hKaonEtaVsPtAllSelected etc. before they're booked -> fatal (this is + // the exact FATAL reported: "hKaonEtaVsPtAllSelected" not found). + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2293,15 +2357,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKaon)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKaon && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& kaon : associatedKaons) { @@ -2342,6 +2405,12 @@ struct HResonanceCorrelation { void processSameEventHPhis(soa::Join::iterator const& collision, aod::AssocPhis const& associatedPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPhi=false, this switch=true) fills + // sameEvent/Signal/Phi etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2358,14 +2427,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPhi)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPhi && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2391,14 +2460,34 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check: raw per-candidate mass, read + // straight off the already-computed Mass column -- no kinematics + // recomputed here. See the matching comment at hPhiMixedEventInvMass's + // booking in init() for why this is the mixed-event counterpart. + for (auto const& cand : associatedPhis) { + histos.fill(HIST("hPhiInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-phi correlation // ----------------------------- - fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy to match how sameEvent/Signal/Phi is + // booked in init() (~line 753) -- this call used to be unconditional, + // which would fill that THnF even when doFullCorrelationStudy=false (and + // it was therefore never booked). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processSameEventHKstars(soa::Join::iterator const& collision, aod::AssocKstars const& associatedKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKstar=false, this switch=true) fills + // sameEvent/Signal/Kstar0 etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2415,14 +2504,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKstar)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKstar && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2448,15 +2537,30 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check -- see the matching comment in + // processSameEventHPhis. + for (auto const& cand : associatedKstars) { + histos.fill(HIST("hKstarInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-K*0 correlation // ----------------------------- - fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy for the same reason as the Phi call + // above -- matches how sameEvent/Signal/Kstar0 is booked in init(). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Hadron + // is booked only when TESTBIT(doCorrelation, IndexHadron) is set. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2473,7 +2577,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2501,6 +2605,11 @@ struct HResonanceCorrelation { void processMixedEventHPions(soa::Join const& collisions, soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Pion + // is booked only when TESTBIT(doCorrelation, IndexPion) is set. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2517,7 +2626,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2545,6 +2654,11 @@ struct HResonanceCorrelation { void processMixedEventHKaons(soa::Join const& collisions, soa::Join const& assocKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kaon + // is booked only when TESTBIT(doCorrelation, IndexKaon) is set. + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2561,7 +2675,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2589,6 +2703,11 @@ struct HResonanceCorrelation { void processMixedEventHPhis(soa::Join const& collisions, aod::AssocPhis const& assocPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2604,7 +2723,7 @@ struct HResonanceCorrelation { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2626,6 +2745,14 @@ struct HResonanceCorrelation { auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check: reuses the Mass/Rapidity + // columns already stored by the filter task (no kinematics + // recomputed here) and this same mixing pool, so it's directly + // comparable to the same-event mass QA and to the real ME background. + for (auto const& cand : slicedAssocPhis) { + histos.fill(HIST("hPhiMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Use the per-collision slices computed above, not the full unsliced // triggerTracks/assocPhis tables -- passing the whole dataframe here @@ -2638,6 +2765,11 @@ struct HResonanceCorrelation { void processMixedEventHKstars(soa::Join const& collisions, aod::AssocKstars const& assocKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2653,7 +2785,7 @@ struct HResonanceCorrelation { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2675,6 +2807,12 @@ struct HResonanceCorrelation { auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check -- see the matching comment + // in processMixedEventHPhis. + for (auto const& cand : slicedAssocKstars) { + histos.fill(HIST("hKstarMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Same reasoning as processMixedEventHPhis above -- use this collision // pair's own slices, not the full unsliced triggerTracks/assocKstars. @@ -2776,7 +2914,11 @@ struct HResonanceCorrelation { bestCollisionFT0Cpercentile = collision.centFT0C(); if (masterConfigurations.applyNewMCSelection) { - isCollisionSelect = (masterConfigurations.doPPAnalysis && isisCollisionSelect(collision)) || (!masterConfigurations.doPPAnalysis && isisCollisionSelectPbPb(collision, false)); + // isSelectedEvents() is the one selection used everywhere in this + // task now (pp and Pb-Pb alike), matching the filter task; see its + // own comment for why -- no more doPPAnalysis branch to a different + // cut set here. + isCollisionSelect = isSelectedEvents(collision, false); } else { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); @@ -3092,6 +3234,11 @@ struct HResonanceCorrelation { void processMixedEventHPhisInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocPhis const& assocPhis, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3104,12 +3251,21 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision.globalIndex()); - fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment in + // processSameEventHPhis; this call used to be unconditional. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } } void processMixedEventHKstarsInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocKstars const& assocKstars, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3118,7 +3274,10 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision.globalIndex()); - fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment above. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } } From 356dccfa844cd92cf49931448a076674a582e304 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Mon, 5 Oct 2026 13:54:12 +0900 Subject: [PATCH 172/648] [PWGLF] K1(1270): shared selection core and ML training-table exporter (#18167) --- PWGLF/Core/K1AnalysisMicroCore.h | 741 +++++++++++++++++ PWGLF/Core/K1MlFeatures.h | 873 +++++++++++++++++++++ PWGLF/Core/ResoAnalysisSelectionCore.h | 462 +++++++++++ PWGLF/DataModel/LFK1MlTables.h | 125 +++ PWGLF/Tasks/Resonances/CMakeLists.txt | 5 + PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx | 861 +++++++++----------- PWGLF/Tasks/Resonances/k1TrainingTable.cxx | 281 +++++++ 7 files changed, 2839 insertions(+), 509 deletions(-) create mode 100644 PWGLF/Core/K1AnalysisMicroCore.h create mode 100644 PWGLF/Core/K1MlFeatures.h create mode 100644 PWGLF/Core/ResoAnalysisSelectionCore.h create mode 100644 PWGLF/DataModel/LFK1MlTables.h create mode 100644 PWGLF/Tasks/Resonances/k1TrainingTable.cxx diff --git a/PWGLF/Core/K1AnalysisMicroCore.h b/PWGLF/Core/K1AnalysisMicroCore.h new file mode 100644 index 00000000000..4e2cd0e720c --- /dev/null +++ b/PWGLF/Core/K1AnalysisMicroCore.h @@ -0,0 +1,741 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1AnalysisMicroCore.h +/// \brief Shared K1(1270) selection, truth classification and candidate enumeration of the K1 resonance tasks +/// \author Su-Jeong Ji , Bong-Hwi Lim +/// +/// The core owns the selection and its cut-flow instrumentation (CutFlow/*, ML/*). The tasks own their +/// output histograms and fill them from the pair and candidate hooks of forEachCandidate(). + +#ifndef PWGLF_CORE_K1ANALYSISMICROCORE_H_ +#define PWGLF_CORE_K1ANALYSISMICROCORE_H_ + +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1micro +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::isCutEnabled; +using o2::analysis::resonance::isInRange; +using o2::analysis::resonance::isInWindow; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +enum class K1TruthChannel { + None = 0, + RhoK = 1, + KStarPi = 2 +}; + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative selection bits of an unlike-sign candidate handed to the export hook. +enum CandidatePassBit : uint16_t { + kPassLoose = 1, // valid canonical candidate inside the K1 rapidity window + kPassQuality = 2, // track quality of all three tracks + kPassPID = 4, // TOF requirement and PID of all three tracks + kPassPair = 8, // pion-pair pT and secondary mass window + kPassCandidate = 16 // candidate cuts +}; +inline constexpr uint16_t PassBitsSelected = kPassLoose | kPassQuality | kPassPID | kPassPair | kPassCandidate; + +inline constexpr double MassRho770 = 0.77526; // PDG 2024, not available in o2::constants::physics +inline constexpr int NCandidateStages = 12; +inline constexpr int NTruthChannels = 3; + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Pion PID selection +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID selection +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Secondary selection (-999 switches a cut off; the values it needs are then not computed) +struct SecondaryCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; + o2::framework::Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; + o2::framework::Configurable cSecondaryMasswindow{"cSecondaryMasswindow", -999, "Secondary inv mass selection window"}; + o2::framework::Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", -999, "Min inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", -999, "MAx inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMinPiKaMassCut{"cMinPiKaMassCut", -999, "bPion-Kaon pair inv mass selection minimum"}; + o2::framework::Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", -999, "bPion-Kaon pair inv mass selection maximum"}; + o2::framework::Configurable cMinAngle{"cMinAngle", -999, "Minimum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMaxAngle{"cMaxAngle", -999, "Maximum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMinPairAsym{"cMinPairAsym", -999, "Minimum pair asymmetry"}; + o2::framework::Configurable cMaxPairAsym{"cMaxPairAsym", -999, "Maximum pair asymmetry"}; +}; + +/// Common TOF switch and K1 selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection"}; + o2::framework::Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; + o2::framework::Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; +}; + +/// Process functions enabled in the task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool microTracks = false; // any process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC with full or micro tracks + bool mcRecoMicro = false; // reconstructed MC with micro tracks + bool mcGen = false; // generated K1 parents in selected reconstructed events +}; + +/// Loose-stage traversal of unlike-sign micro candidates for the export hook. +/// With the defaults and without an export hook, the candidate loop applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML/looseCutflow and ML/looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Selected (pion, pion, kaon) triplet handed to the candidate hook. +/// mass13, mass23, angle and pairAsym are computed only inside the K1 rapidity window, and there only +/// when a candidate cut needs them or forEachCandidate() was asked for them; otherwise they are 0. +struct K1CandidateValues { + ROOT::Math::PxPyPzMVector k1; // pion1 + pion2 + kaon + ROOT::Math::PxPyPzMVector secondary; // pion1 + pion2 + double mass13 = 0.; // pion1 + kaon (K*0 candidate in the unlike-sign case) + double mass23 = 0.; // pion2 + kaon + double angle = 0.; // opening angle between the secondary resonance and the bachelor + double pairAsym = 0.; // energy asymmetry between the secondary resonance and the bachelor + bool isUnlikeSign = false; // opposite-sign pion pair + bool inRapidity = false; // K1 rapidity window + bool passesCandidateCuts = false; // secondary mass, pi-K mass, angle and asymmetry cuts (inside the rapidity window) +}; + +// Truth classification from the immediate mothers and the sibling IDs of the reconstructed daughters. +template +bool hasSibling(const Track& directDaughter, int resonanceId) +{ + if (resonanceId < 0) { + return false; + } + const auto siblings = directDaughter.siblingIds(); + return siblings[0] == resonanceId || siblings[1] == resonanceId; +} + +template +bool matchesKStarPi(const Track& resonancePion, const Track& directPion, const Kaon& kaon) +{ + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + if (resonancePion.motherId() != kaon.motherId() || + resonancePion.motherId() == directPion.motherId()) { + return false; + } + if (resonancePion.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892 || kaon.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892) { + return false; + } + if (resonancePion.pdgCode() != -charge * kPiPlus || directPion.pdgCode() != charge * kPiPlus || + directPion.motherPDG() != charge * o2::constants::physics::Pdg::kK1_1270Plus) { + return false; + } + return hasSibling(directPion, kaon.motherId()); +} + +template +K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kaon& kaon) +{ + if (std::abs(pion1.pdgCode()) != kPiPlus || std::abs(pion2.pdgCode()) != kPiPlus || + std::abs(kaon.pdgCode()) != kKPlus) { + return K1TruthChannel::None; + } + if (pion1.motherId() < 0 || pion2.motherId() < 0 || kaon.motherId() < 0) { + return K1TruthChannel::None; + } + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const bool rhoPions = pion1.motherId() == pion2.motherId() && + pion1.motherPDG() == kRho770_0 && pion2.motherPDG() == kRho770_0 && + pion1.pdgCode() == -pion2.pdgCode(); + if (rhoPions && kaon.motherPDG() == charge * o2::constants::physics::Pdg::kK1_1270Plus && + kaon.motherId() != pion1.motherId() && hasSibling(kaon, pion1.motherId())) { + return K1TruthChannel::RhoK; + } + if (matchesKStarPi(pion1, pion2, kaon) || matchesKStarPi(pion2, pion1, kaon)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +// Immediate decay channel of a generated K1 from the PDG codes of its two daughters. +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2) +{ + if ((daughter1 == kRho770_0 && daughter2 == charge * kKPlus) || + (daughter2 == kRho770_0 && daughter1 == charge * kKPlus)) { + return K1TruthChannel::RhoK; + } + if ((daughter1 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter2 == charge * kPiPlus) || + (daughter2 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter1 == charge * kPiPlus)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +/// K1 selection and candidate enumeration shared by the K1 tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class K1AnalysisMicroCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + SecondaryCuts const& secondaryCuts, CandidateCuts const& candidateCuts, + ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mSecondaryCuts = secondaryCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cMaxTPCnSigmaPion.value; + pion.maxTOFnSigma = pionPidCuts.cMaxTOFnSigmaPion.value; + pion.combinedNSigma = pionPidCuts.nsigmaCutCombinedPion.value; + pion.onlyTOFTracks = pionPidCuts.cUseOnlyTOFTrackPi.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cMaxTPCnSigmaPion"; + pion.maxTOFName = "cMaxTOFnSigmaPion"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cMaxTPCnSigmaKaon.value; + kaon.maxTOFnSigma = kaonPidCuts.cMaxTOFnSigmaKaon.value; + kaon.combinedNSigma = kaonPidCuts.nsigmaCutCombinedKaon.value; + kaon.onlyTOFTracks = kaonPidCuts.cUseOnlyTOFTrackKa.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cMaxTPCnSigmaKaon"; + kaon.maxTOFName = "cMaxTOFnSigmaKaon"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mCandidateCuts.cByPassTOF, modes.microTracks); + + mSecondaryWindowOn = isCutEnabled(mSecondaryCuts.cSecondaryMasswindow); + mAnotherMassCutOn = isCutEnabled(mSecondaryCuts.cMinAnotherSecondaryMassCut) || isCutEnabled(mSecondaryCuts.cMaxAnotherSecondaryMassCut); + mPiKaMassCutOn = isCutEnabled(mSecondaryCuts.cMinPiKaMassCut) || isCutEnabled(mSecondaryCuts.cMaxPiKaMassCut); + mAngleCutOn = isCutEnabled(mSecondaryCuts.cMinAngle) || isCutEnabled(mSecondaryCuts.cMaxAngle); + mPairAsymCutOn = isCutEnabled(mSecondaryCuts.cMinPairAsym) || isCutEnabled(mSecondaryCuts.cMaxPairAsym); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + // Full selection stage of a track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + const int qualityStage = mSelection.trackQualityStage(track); + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if constexpr (S == Species::Pion) { + if (hasTOF) { + tofNSigma = track.tofNSigmaPi(); + } + } else { + if (hasTOF) { + tofNSigma = track.tofNSigmaKa(); + } + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + // Unordered (pion, pion, kaon) candidate enumeration of one collision (or one mixed pair of collisions). + // dTracks1: bachelor kaons, dTracks2: pions. The core fills the cut-flow instrumentation; the hooks + // (nullptr to skip) receive + // - onPair(trk1, trk2, secondary, passesPairPt): every pion pair passing the pion selection, + // trk1 being the pion with the lower index, + // - onCandidate(kaon, pion1, pion2, K1CandidateValues): every triplet passing the pion, pair and kaon + // selection, with the canonical pion roles (pion1: opposite sign to the kaon in the unlike-sign case), + // - onExport(collision, kaon, same-sign pion, opposite-sign pion, truth channel, pass bits): the unlike-sign + // micro same-event candidates at the loose or selected stage configured by LooseStageOptions. + // computeValues requests mass13, mass23, angle and pairAsym for every candidate in the rapidity window. + template + void forEachCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + bool computeValues, PairHook onPair = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + if (dTracks1.size() == 0 || dTracks2.size() == 0) { + return; + } + constexpr bool HasPairHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + // Sets are local to this reconstructed collision: IDs cannot leak across DFs. + // Source-file/DF deduplication across split collisions belongs in the audit. + std::array, NTruthChannels> matchedMothers; + + // Selection cache: every track is selected once, not once per pair x bachelor. + // dTracks1: bachelor kaons, dTracks2: pions (different collisions in mixed events). + constexpr bool FillCutFlow = IsResoMicrotrack && !IsMix; + const int64_t firstKaonIndex = dTracks1.begin().index(); + const int64_t firstPionIndex = dTracks2.begin().index(); + const auto kaonSelected = buildSelectionCache(histos, dTracks1, firstKaonIndex); + const auto pionSelected = buildSelectionCache(histos, dTracks2, firstPionIndex); + + // Only micro same-event ML work needs traversal before conventional cuts. + // The canonical-candidate validity is also required before a selected-stage export. + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + std::vector kaonQuality(dTracks1.size(), 0), pionQuality(dTracks2.size(), 0); + if (visitLoose) { + for (const auto& track : dTracks1) { + kaonQuality[getCacheIndex(track, firstKaonIndex, kaonQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + for (const auto& track : dTracks2) { + pionQuality[getCacheIndex(track, firstPionIndex, pionQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + } + + // Values needed only by switched-on cuts or by the task are computed only then + const bool isK892Mode = mSecondaryCuts.cfgModeK892orRho; + const bool needAngle = computeValues || mAngleCutOn; + const bool needPairAsym = computeValues || mPairAsymCutOn; + // K892 mode: the K* candidate is (trk1, K), rho mode: the rho is (trk1, trk2) + const bool needMass13 = computeValues || (isK892Mode ? mSecondaryWindowOn : mAnotherMassCutOn) || (isK892Mode && (needAngle || needPairAsym)); + const bool needMass23 = computeValues || mPiKaMassCutOn; + const bool rhoWindowOn = mSecondaryWindowOn && !isK892Mode; + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1, lPair13, lPair23; + // Unordered pion pairs: each (pion, pion, kaon) triplet is visited once. + // Here trk1 is the pion with the lower index; the roles are assigned once the bachelor is known. + for (const auto& [trk1, trk2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(dTracks2, dTracks2))) { + // trk1: pion, trk2: pion, bTrack: kaon + const bool pionsSelected = pionSelected[getCacheIndex(trk1, firstPionIndex, pionSelected.size())] && pionSelected[getCacheIndex(trk2, firstPionIndex, pionSelected.size())]; + bool pairPt = false; + bool rhoWindow = true; + if (pionsSelected || visitLoose) { + // Resonance reconstruction + lDecayDaughter1.SetCoordinates(trk1.px(), trk1.py(), trk1.pz(), o2::constants::physics::MassPionCharged); + lDecayDaughter2.SetCoordinates(trk2.px(), trk2.py(), trk2.pz(), o2::constants::physics::MassPionCharged); + lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + pairPt = !(lResonanceSecondary.Pt() < mSecondaryCuts.cMinSecondaryPtCut); + rhoWindow = !rhoWindowOn || isInWindow(lResonanceSecondary.M(), MassRho770, mSecondaryCuts.cSecondaryMasswindow); + } + if constexpr (FillCutFlow) { + // Early stages count potential triplets: each pair carries N bachelor trials. + // This preserves the pair-first reconstruction and avoids a new cubic data loop. + // Distinct pion IDs are guaranteed by the strictly upper index policy (stage 1 is always passed). + const int lastStage = !pionsSelected ? 1 : !pairPt ? 3 + : !rhoWindow ? 4 + : 5; + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, 0, static_cast(dTracks1.size())); + } + if constexpr (IsMC) { + // Match before rejecting quality/pT so both channels have an upstream numerator. + if (std::abs(trk1.pdgCode()) == kPiPlus && trk1.pdgCode() == -trk2.pdgCode()) { + for (const auto& bachelor : dTracks1) { + const auto channel = classifyK1Truth(trk1, trk2, bachelor); + if (channel != K1TruthChannel::None) { + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(channel)); + } + } + } + } + } + } + if (!pionsSelected && !visitLoose) { + continue; + } + if constexpr (HasPairHook) { + if (pionsSelected) { + onPair(trk1, trk2, lResonanceSecondary, pairPt); + } + } + if (!pairPt && !visitLoose) { + continue; + } + // Secondary mass window (rho mode): the bachelor loop is skipped for rejected pairs + if (!rhoWindow && !visitLoose) { + continue; + } + + for (const auto& bTrack : dTracks1) { + if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) { + continue; + } + K1TruthChannel flowChannel = K1TruthChannel::None; + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + flowChannel = classifyK1Truth(trk1, trk2, bTrack); + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + histos.fill(HIST("CutFlow/candidates"), stage, 0); + if constexpr (IsMC) { + if (flowChannel != K1TruthChannel::None) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(flowChannel)); + } + } + } + }; + const bool pairSelected = pionsSelected && pairPt && rhoWindow; + const bool bachelorSelected = kaonSelected[getCacheIndex(bTrack, firstKaonIndex, kaonSelected.size())]; + if (pairSelected) { + countCandidate(6); + } + if ((!pairSelected || !bachelorSelected) && !visitLoose) { + continue; + } + const bool tripletSelected = pairSelected && bachelorSelected; + if (tripletSelected) { + countCandidate(7); + } + + // Canonical assignment of the pion roles, once the bachelor is known. + // Unlike-sign pair: the pion with the sign opposite to the kaon is pion 1 (K*0 partner), the other is pion 2. + // Like-sign pair (the rule is ambiguous): the pion with the lower index is pion 1. + const bool isUnlikeSign = trk1.sign() * trk2.sign() < 0; + const bool swapPions = isUnlikeSign && trk1.sign() == bTrack.sign(); + const auto& pion1 = swapPions ? trk2 : trk1; + const auto& pion2 = swapPions ? trk1 : trk2; + const auto& lPion1 = swapPions ? lDecayDaughter2 : lDecayDaughter1; + const auto& lPion2 = swapPions ? lDecayDaughter1 : lDecayDaughter2; + + // K1 reconstruction + lDecayDaughter_bach.SetCoordinates(bTrack.px(), bTrack.py(), bTrack.pz(), o2::constants::physics::MassKaonCharged); + lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; + K1CandidateValues values; + values.k1 = lResonanceK1; + values.secondary = lResonanceSecondary; + values.isUnlikeSign = isUnlikeSign; + + auto countMl = [&](int stage) { + if (mLooseOptions.audit && isUnlikeSign) { + histos.fill(HIST("ML/looseCutflow"), stage, 0); + if (flowChannel != K1TruthChannel::None) { + const int stratum = 2 * static_cast(flowChannel) - (bTrack.sign() > 0 ? 1 : 0); + histos.fill(HIST("ML/looseCutflow"), stage, stratum); + } + } + }; + bool validLoose = false; + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity && isUnlikeSign) { + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(bTrack), o2::analysis::k1ml::makeTrackSnapshot(pion2), o2::analysis::k1ml::makeTrackSnapshot(pion1)); + validLoose = canonical.status == o2::analysis::k1ml::BuildStatus::Ok && + std::isfinite(lResonanceK1.M()) && std::isfinite(lResonanceK1.Pt()) && + std::isfinite(lResonanceK1.Rapidity()) && std::isfinite(lResonanceK1.Eta()) && + std::isfinite(lResonanceK1.Phi()) && + o2::analysis::k1ml::buildMasterFeatures(canonical.candidate).status == o2::analysis::k1ml::BuildStatus::Ok; + if (validLoose) { + countMl(0); + } + } + } + + // Stage L common acceptance uses the existing inclusive rapidity window. + values.inRapidity = lResonanceK1.Rapidity() >= mCandidateCuts.cK1MinRap && lResonanceK1.Rapidity() <= mCandidateCuts.cK1MaxRap; + if (!values.inRapidity) { + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(bTrack, pion1, pion2, values); + } + } + continue; + } + if (tripletSelected) { + countCandidate(8); + } + + if (needMass13) { + lPair13 = lPion1 + lDecayDaughter_bach; + values.mass13 = lPair13.M(); + } + if (needMass23) { + lPair23 = lPion2 + lDecayDaughter_bach; + values.mass23 = lPair23.M(); + } + // Rho mode: secondary = (trk1, trk2) against the bachelor. K892 mode: secondary = (trk1, K) against trk2. + if (needAngle) { + values.angle = isK892Mode ? ROOT::Math::VectorUtil::Angle(lPair13, lPion2) : ROOT::Math::VectorUtil::Angle(lResonanceSecondary, lDecayDaughter_bach); + } + if (needPairAsym) { + values.pairAsym = isK892Mode ? (lPair13.E() - lPion2.E()) / (lPair13.E() + lPion2.E()) + : (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); + } + + // Candidate cuts (each one is evaluated only if switched on) + values.passesCandidateCuts = + (!isK892Mode || !mSecondaryWindowOn || (isInWindow(values.mass13, o2::constants::physics::MassK0Star892, mSecondaryCuts.cSecondaryMasswindow) && pion1.sign() != bTrack.sign())) && + (!mAnotherMassCutOn || isInRange(isK892Mode ? lResonanceSecondary.M() : values.mass13, mSecondaryCuts.cMinAnotherSecondaryMassCut, mSecondaryCuts.cMaxAnotherSecondaryMassCut)) && + (!mPiKaMassCutOn || isInRange(values.mass23, mSecondaryCuts.cMinPiKaMassCut, mSecondaryCuts.cMaxPiKaMassCut)) && + (!mAngleCutOn || isInRange(values.angle, mSecondaryCuts.cMinAngle, mSecondaryCuts.cMaxAngle)) && + (!mPairAsymCutOn || isInRange(values.pairAsym, mSecondaryCuts.cMinPairAsym, mSecondaryCuts.cMaxPairAsym)); + auto exportCandidate = [&](uint16_t passBits) { + if constexpr (IsResoMicrotrack && !IsMix && HasExportHook) { + onExport(collision, bTrack, pion2, pion1, flowChannel, passBits); + } else { + static_cast(passBits); + } + }; + if (visitLoose && validLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/looseMassPtActivity"), lResonanceK1.M(), lResonanceK1.Pt(), collision.cent()); + } + const bool qualityPass = kaonQuality[getCacheIndex(bTrack, firstKaonIndex, kaonQuality.size())] && + pionQuality[getCacheIndex(trk1, firstPionIndex, pionQuality.size())] && + pionQuality[getCacheIndex(trk2, firstPionIndex, pionQuality.size())]; + uint16_t passBits = kPassLoose; + if (qualityPass) { + passBits |= kPassQuality; + countMl(2); + if (pionsSelected && bachelorSelected) { + passBits |= kPassPID; + countMl(3); + if (pairPt && rhoWindow) { + passBits |= kPassPair; + countMl(4); + if (values.passesCandidateCuts) { + passBits |= kPassCandidate; + countMl(5); + } + } + } + } + if (!mLooseOptions.exportSelected) { + exportCandidate(passBits); + } + } + // Stage C retains the frozen conventional selections. + if (!tripletSelected) { + continue; + } + if (values.passesCandidateCuts) { + countCandidate(9); + countCandidate(isUnlikeSign ? 10 : 11); + if (isUnlikeSign && mLooseOptions.exportSelected && validLoose) { + exportCandidate(PassBitsSelected); + } + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + if (flowChannel != K1TruthChannel::None) { + const int mother = flowChannel == K1TruthChannel::RhoK ? bTrack.motherId() : std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() + : pion2.motherId(); + if (matchedMothers[static_cast(flowChannel)].insert(mother).second) { + histos.fill(HIST("CutFlow/uniqueMothersPerCollision"), static_cast(flowChannel)); + } + } + } + } + if constexpr (HasCandidateHook) { + onCandidate(bTrack, pion1, pion2, values); + } + } // bTrack + } + } // forEachCandidate + + // Generated K1 parents of a selected reconstructed MC collision. The optional callback receives + // (parent, immediate channel) for the parents inside the K1 rapidity window. + // Parents belong to selected reconstructed events; split reco collisions + // repeat parent sets. This is not an unconditional generated denominator. + template + void forEachGeneratedK1(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus) { + continue; + } + const int charge = part.pdgCode() > 0 ? 1 : -1; + const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + private: + // Selection cache of one track slice. The row number of a grouped slice is global, hence the offset. + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + template + std::vector buildSelectionCache(o2::framework::HistogramRegistry& histos, const TracksType& tracks, int64_t firstIndex) + { + std::vector selected(tracks.size(), 0); + for (const auto& track : tracks) { + const int stage = trackSelectionStage(track); + selected[getCacheIndex(track, firstIndex, selected.size())] = (stage == TrackStage::kTrkPID) ? 1 : 0; + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/tracks"), i, static_cast(S)); + } + } + } + return selected; + } + + // Cut-flow instrumentation of the selection; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + if (mLooseOptions.audit) { + auto flow = histos.add("ML/looseCutflow", "US triplets;stage;signal stratum", HistType::kTH2D, {{6, -0.5, 5.5}, {5, -0.5, 4.5}}); + const std::array labels{"structural US", "loose acceptance", "track quality", "TOF + PID", "pair requirements", "selected US"}; + const std::array strata{"all US", "rhoK+", "rhoK-", "KstarPi+", "KstarPi-"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + for (std::size_t i = 0; i < strata.size(); ++i) { + flow->GetYaxis()->SetBinLabel(i + 1, strata[i]); + } + histos.add("ML/looseMassPtActivity", "Loose US;mass (GeV/c^{2});pT (GeV/c);FT0M percentile", HistType::kTH3D, + {{300, 0.7, 3.7}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "FT0M percentile"}}); + } + + // Micro-only instrumentation: category 0 includes all combinations, not just unmatched. + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/tracks", "Micro tracks, once per selected collision;stage;species", HistType::kTH2D, {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/candidates", "Unordered micro triplets;stage;category", HistType::kTH2D, {{NCandidateStages, -0.5, NCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"input unordered triplets", "distinct pion IDs", "pion selection (quality+PID)", "pion pair constructed", "pair pT", "secondary mass window (rho mode)", "three distinct IDs", "kaon selection (quality+PID)", "K1 rapidity", "candidate cuts", "final US", "final LS"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "rho K"); + candidateFlow->GetYaxis()->SetBinLabel(3, "K* pi"); + if (modes.mcRecoMicro) { + auto mothers = histos.add("CutFlow/uniqueMothersPerCollision", "Final unique K1 IDs summed over reconstructed collisions (not globally deduplicated)", HistType::kTH1D, {{2, 0.5, 2.5}}); + mothers->GetXaxis()->SetBinLabel(1, "rho K"); + mothers->GetXaxis()->SetBinLabel(2, "K* pi"); + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "K1 parent rows conditional on selected reconstructed events;stage;immediate channel", HistType::kTH2D, {{2, -0.5, 1.5}, {3, -0.5, 2.5}}); + generated->GetXaxis()->SetBinLabel(1, "all K1 parent rows"); + generated->GetXaxis()->SetBinLabel(2, "K1 rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "rho K"); + generated->GetYaxis()->SetBinLabel(3, "K* pi"); + } + } + + ResoAnalysisSelectionCore mSelection; + SecondaryCuts mSecondaryCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; + + // Derived once in init(): which candidate cuts are switched on. + bool mSecondaryWindowOn = false; + bool mAnotherMassCutOn = false; + bool mPiKaMassCutOn = false; + bool mAngleCutOn = false; + bool mPairAsymCutOn = false; +}; + +} // namespace o2::analysis::k1micro + +#endif // PWGLF_CORE_K1ANALYSISMICROCORE_H_ diff --git a/PWGLF/Core/K1MlFeatures.h b/PWGLF/Core/K1MlFeatures.h new file mode 100644 index 00000000000..262fb0feff7 --- /dev/null +++ b/PWGLF/Core/K1MlFeatures.h @@ -0,0 +1,873 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1MlFeatures.h +/// \brief Canonical micro001 K1 candidate and feature contract helpers +/// \author Bong-Hwi Lim +/// + +#ifndef PWGLF_CORE_K1MLFEATURES_H_ +#define PWGLF_CORE_K1MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCandidateTracks = 3; +inline constexpr std::size_t NMasterFeatures = 125; + +enum class Role : uint8_t { Kaon, + PionSame, + PionOpp }; +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + SubstructureV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedTrack, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct CandidateSnapshot { + std::array tracks{}; // K, same-charge pion, opposite-charge pion +}; + +struct CanonicalizationResult { + CandidateSnapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +struct KinematicAudit { + float massKPiPi = 0.f; + float massPiPi = 0.f; + float massKaonPiSame = 0.f; + float massKaonPiOpp = 0.f; + float candidatePt = 0.f; + float candidateEta = 0.f; + float candidatePhi = 0.f; + float scalarSumPt = 0.f; +}; + +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; + +// Adapt the real ResoMicroTracks_001 public columns and dynamic accessors. +// Keeping the raw bytes alongside decoded accessors makes the encoding auditable. +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(TrackSnapshot const& track) +{ + return std::isfinite(track.px) && std::isfinite(track.py) && std::isfinite(track.pz) && + std::hypot(track.px, track.py) > 0.f; +} + +inline CanonicalizationResult canonicalizeUS(TrackSnapshot const& kaon, + TrackSnapshot const& pionA, + TrackSnapshot const& pionB) +{ + CanonicalizationResult result; + if (kaon.sourceTrackId == pionA.sourceTrackId || kaon.sourceTrackId == pionB.sourceTrackId || + pionA.sourceTrackId == pionB.sourceTrackId) { + result.status = BuildStatus::ReusedTrack; + return result; + } + if ((kaon.charge != 1 && kaon.charge != -1) || (pionA.charge != 1 && pionA.charge != -1) || + (pionB.charge != 1 && pionB.charge != -1) || pionA.charge == pionB.charge || + (pionA.charge != kaon.charge && pionB.charge != kaon.charge)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(kaon) || !hasFiniteMomentum(pionA) || !hasFiniteMomentum(pionB)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + const auto& same = pionA.charge == kaon.charge ? pionA : pionB; + const auto& opposite = pionA.charge == kaon.charge ? pionB : pionA; + result.candidate.tracks = {kaon, same, opposite}; + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -3.5f : 3.5f, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +struct Kinematics { + double pt; + double eta; + double phi; + double energy; +}; + +inline bool getKinematics(TrackSnapshot const& track, double mass, Kinematics& out) +{ + if (!hasFiniteMomentum(track)) { + return false; + } + const double px = track.px, py = track.py, pz = track.pz; + out.pt = std::hypot(px, py); + const double p = std::hypot(out.pt, pz); + out.eta = std::asinh(pz / out.pt); + out.phi = std::atan2(py, px); + out.energy = std::sqrt(p * p + mass * mass); + return std::isfinite(out.pt) && std::isfinite(out.eta) && std::isfinite(out.phi) && std::isfinite(out.energy); +} + +inline float invariantMass(Kinematics const& a, Kinematics const& b) +{ + const auto pxa = a.pt * std::cos(a.phi), pya = a.pt * std::sin(a.phi); + const auto pxb = b.pt * std::cos(b.phi), pyb = b.pt * std::sin(b.phi); + const double e = a.energy + b.energy; + const double px = pxa + pxb, py = pya + pyb; + // pz is recovered from pt*sinh(eta), matching the stored three-momentum. + const double pz = a.pt * std::sinh(a.eta) + b.pt * std::sinh(b.eta); + const double m2 = e * e - px * px - py * py - pz * pz; + return static_cast(std::sqrt(std::max(0.0, m2))); +} + +inline float invariantMass(std::array const& tracks, + std::array const& indices) +{ + double e = 0., px = 0., py = 0., pz = 0.; + for (const auto& i : indices) { + const auto& track = tracks[i]; + e += track.energy; + px += track.pt * std::cos(track.phi); + py += track.pt * std::sin(track.phi); + pz += track.pt * std::sinh(track.eta); + } + return static_cast(std::sqrt(std::max(0.0, e * e - px * px - py * py - pz * pz))); +} + +inline void append(std::array& out, std::size_t& index, EncodedValue value) +{ + out[index++] = value.value; + out[index++] = value.valid; + out[index++] = value.overflow; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names and projection indices are generated from feature_contract_v1.json. +inline constexpr std::array MasterFeatureNames{ + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "kaon.pt_fraction", + "pion_same.tpc_nsigma_pi", + "pion_same.tpc_nsigma_pi_valid", + "pion_same.tpc_nsigma_pi_overflow", + "pion_same.tpc_nsigma_ka", + "pion_same.tpc_nsigma_ka_valid", + "pion_same.tpc_nsigma_ka_overflow", + "pion_same.tpc_nsigma_pr", + "pion_same.tpc_nsigma_pr_valid", + "pion_same.tpc_nsigma_pr_overflow", + "pion_same.tof_nsigma_pi", + "pion_same.tof_nsigma_pi_valid", + "pion_same.tof_nsigma_pi_overflow", + "pion_same.tof_nsigma_ka", + "pion_same.tof_nsigma_ka_valid", + "pion_same.tof_nsigma_ka_overflow", + "pion_same.tof_nsigma_pr", + "pion_same.tof_nsigma_pr_valid", + "pion_same.tof_nsigma_pr_overflow", + "pion_same.abs_dca_xy", + "pion_same.abs_dca_xy_valid", + "pion_same.abs_dca_xy_overflow", + "pion_same.abs_dca_z", + "pion_same.abs_dca_z_valid", + "pion_same.abs_dca_z_overflow", + "pion_same.passed_ptdep_dca_xy", + "pion_same.passed_ptdep_dca_z", + "pion_same.has_tof", + "pion_same.tpc_crossed_rows", + "pion_same.its_hit_l0", + "pion_same.its_hit_l1", + "pion_same.its_hit_l2", + "pion_same.its_hit_l3", + "pion_same.its_hit_l4", + "pion_same.its_hit_l5", + "pion_same.its_hit_l6", + "pion_same.is_pv_contributor", + "pion_same.pt_fraction", + "pion_opp.tpc_nsigma_pi", + "pion_opp.tpc_nsigma_pi_valid", + "pion_opp.tpc_nsigma_pi_overflow", + "pion_opp.tpc_nsigma_ka", + "pion_opp.tpc_nsigma_ka_valid", + "pion_opp.tpc_nsigma_ka_overflow", + "pion_opp.tpc_nsigma_pr", + "pion_opp.tpc_nsigma_pr_valid", + "pion_opp.tpc_nsigma_pr_overflow", + "pion_opp.tof_nsigma_pi", + "pion_opp.tof_nsigma_pi_valid", + "pion_opp.tof_nsigma_pi_overflow", + "pion_opp.tof_nsigma_ka", + "pion_opp.tof_nsigma_ka_valid", + "pion_opp.tof_nsigma_ka_overflow", + "pion_opp.tof_nsigma_pr", + "pion_opp.tof_nsigma_pr_valid", + "pion_opp.tof_nsigma_pr_overflow", + "pion_opp.abs_dca_xy", + "pion_opp.abs_dca_xy_valid", + "pion_opp.abs_dca_xy_overflow", + "pion_opp.abs_dca_z", + "pion_opp.abs_dca_z_valid", + "pion_opp.abs_dca_z_overflow", + "pion_opp.passed_ptdep_dca_xy", + "pion_opp.passed_ptdep_dca_z", + "pion_opp.has_tof", + "pion_opp.tpc_crossed_rows", + "pion_opp.its_hit_l0", + "pion_opp.its_hit_l1", + "pion_opp.its_hit_l2", + "pion_opp.its_hit_l3", + "pion_opp.its_hit_l4", + "pion_opp.its_hit_l5", + "pion_opp.its_hit_l6", + "pion_opp.is_pv_contributor", + "pion_opp.pt_fraction", + "kaon__pion_same.delta_eta", + "kaon__pion_same.sin_delta_phi", + "kaon__pion_same.cos_delta_phi", + "kaon__pion_same.z_pt", + "kaon__pion_opp.delta_eta", + "kaon__pion_opp.sin_delta_phi", + "kaon__pion_opp.cos_delta_phi", + "kaon__pion_opp.z_pt", + "pion_same__pion_opp.delta_eta", + "pion_same__pion_opp.sin_delta_phi", + "pion_same__pion_opp.cos_delta_phi", + "pion_same__pion_opp.z_pt", + "mass_pi_pi", + "mass_kaon_pion_opp"}; +inline constexpr std::array DetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109}; +inline constexpr std::array RelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122}; +inline constexpr std::array SubstructureV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124}; +static_assert(MasterFeatureNames.size() == NMasterFeatures, "master feature name count must match NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(DetectorV1Projection, NMasterFeatures), "DetectorV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(RelationalV1Projection, NMasterFeatures), "RelationalV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(SubstructureV1Projection, NMasterFeatures), "SubstructureV1 projection indices must be strictly increasing and below NMasterFeatures"); +inline constexpr std::string_view FeatureContractSha256 = "39f38ece001581d8ebf57392fad045759a63ba49f57c411b9b566d7c1cc58b8a"; + +inline FeaturePack buildMasterFeatures(CandidateSnapshot const& candidate) +{ + FeaturePack pack; + for (auto const& track : candidate.tracks) { + if ((track.charge != 1 && track.charge != -1) || !hasFiniteMomentum(track)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + } + if (candidate.tracks[0].sourceTrackId == candidate.tracks[1].sourceTrackId || + candidate.tracks[0].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[1].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[0].charge != candidate.tracks[1].charge || + candidate.tracks[0].charge == candidate.tracks[2].charge) { + pack.status = BuildStatus::InvalidChargePattern; + return pack; + } + + std::array kin{}; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + if (!detail::getKinematics(candidate.tracks[i], i == 0 ? o2::constants::physics::MassKaonCharged : o2::constants::physics::MassPionCharged, kin[i])) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + } + const double sumPt = kin[0].pt + kin[1].pt + kin[2].pt; + if (!std::isfinite(sumPt) || sumPt <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + const double totalPx = static_cast(candidate.tracks[0].px) + candidate.tracks[1].px + candidate.tracks[2].px; + const double totalPy = static_cast(candidate.tracks[0].py) + candidate.tracks[1].py + candidate.tracks[2].py; + const double totalPz = static_cast(candidate.tracks[0].pz) + candidate.tracks[1].pz + candidate.tracks[2].pz; + const double candidatePt = std::hypot(totalPx, totalPy); + if (!std::isfinite(candidatePt)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.kinematics.scalarSumPt = static_cast(sumPt); + pack.kinematics.candidatePt = static_cast(candidatePt); + pack.kinematics.candidateEta = candidatePt > 0. ? static_cast(std::asinh(totalPz / candidatePt)) : 0.f; + pack.kinematics.candidatePhi = static_cast(std::atan2(totalPy, totalPx)); + pack.kinematics.massKPiPi = detail::invariantMass(kin, std::array{0, 1, 2}); + pack.kinematics.massPiPi = detail::invariantMass(kin[1], kin[2]); + pack.kinematics.massKaonPiSame = detail::invariantMass(kin[0], kin[1]); + pack.kinematics.massKaonPiOpp = detail::invariantMass(kin[0], kin[2]); + + std::size_t index = 0; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + const auto& track = candidate.tracks[i]; + for (const float& decoded : track.tpcNSigma) { + detail::append(pack.master, index, detail::encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + detail::append(pack.master, index, track.hasTOF ? detail::encodePID(decoded) : detail::EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + detail::append(pack.master, index, detail::encodeDCA(track.dcaXY)); + detail::append(pack.master, index, detail::encodeDCA(track.dcaZ)); + pack.master[index++] = track.passedPtDependentDCAxy ? 1.f : 0.f; + pack.master[index++] = track.passedPtDependentDCAz ? 1.f : 0.f; + pack.master[index++] = track.hasTOF ? 1.f : 0.f; + pack.master[index++] = static_cast(track.tpcNClsCrossedRows); + for (const bool& hit : track.itsHit) { + pack.master[index++] = hit ? 1.f : 0.f; + } + pack.master[index++] = track.isPVContributor ? 1.f : 0.f; + pack.master[index++] = static_cast(kin[i].pt / sumPt); + } + + constexpr std::array, 3> PairIndices{{{{0, 1}}, {{0, 2}}, {{1, 2}}}}; + for (auto const& pair : PairIndices) { + const auto i = pair[0], j = pair[1]; + const double dphi = kin[i].phi - kin[j].phi; + const double zDenominator = kin[i].pt + kin[j].pt; + if (!std::isfinite(dphi) || zDenominator <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.master[index++] = static_cast(kin[i].eta - kin[j].eta); + pack.master[index++] = static_cast(std::sin(dphi)); + pack.master[index++] = static_cast(std::cos(dphi)); + pack.master[index++] = static_cast(std::min(kin[i].pt, kin[j].pt) / zDenominator); + } + pack.master[index++] = pack.kinematics.massPiPi; + pack.master[index++] = pack.kinematics.massKaonPiOpp; + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != pack.master.size() || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return pack; + } + pack.status = BuildStatus::Ok; + return pack; +} + +inline std::vector projectFeatures(FeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + std::vector projected; + switch (profile) { + case Profile::DetectorV1: + projected.reserve(DetectorV1Projection.size()); + for (const auto& i : DetectorV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::RelationalV1: + projected.reserve(RelationalV1Projection.size()); + for (const auto& i : RelationalV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::SubstructureV1: + projected.reserve(SubstructureV1Projection.size()); + for (const auto& i : SubstructureV1Projection) { + projected.push_back(pack.master[i]); + } + break; + default: + return {}; + } + return projected; +} +} // namespace o2::analysis::k1ml + +#endif // PWGLF_CORE_K1MLFEATURES_H_ diff --git a/PWGLF/Core/ResoAnalysisSelectionCore.h b/PWGLF/Core/ResoAnalysisSelectionCore.h new file mode 100644 index 00000000000..225f65124b7 --- /dev/null +++ b/PWGLF/Core/ResoAnalysisSelectionCore.h @@ -0,0 +1,462 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ResoAnalysisSelectionCore.h +/// \brief Event, track-quality and PID selection of resonance daughters from the reduced v001 resonance tables +/// \author Bong-Hwi Lim +/// +/// The same selection code serves full ResoTracks (exact values) and ResoMicroTracks (quantised DCA and nSigma, +/// see LFResonanceTables.h); only the comparisons are quantisation aware. A cut set to DisabledCut is off. + +#ifndef PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ +#define PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::resonance +{ + +inline constexpr float DisabledCut = -999.f; // an optional cut with this value is off and not evaluated +inline constexpr double DCAGridStep = 0.025; // v001 micro DCA encoding, lower-inclusive bins up to DCAGridMax +inline constexpr double DCAGridMax = 0.15; +inline constexpr double PIDGridStart = 2.0; // v001 micro nSigma encoding: 0.25 bins in [2.0, 3.5] +inline constexpr double PIDGridStep = 0.25; +inline constexpr double PIDGridMax = 3.5; +inline constexpr double GridTolerance = 1e-4; +inline constexpr std::size_t MinPtBinEdges = 2; // a pT dependent PID table needs at least one bin +inline constexpr float ProducerDCAPtP0 = 0.004f; // resonanceModuleInitializer cfgTightDCAOffset default +inline constexpr float ProducerDCAPtCoeff = 0.013f; // resonanceModuleInitializer cfgTightDCAPtCoefficient default +inline constexpr float ProducerDCAPtPower = 1.f; // resonanceModuleInitializer cfgTightDCAPtPower default +inline constexpr float ConfigTolerance = 1e-6f; + +// Last stage passed by a track; a cut-flow histogram can be filled directly from this value. +enum TrackStage : int { + kTrkInput = 0, + kTrkPt, + kTrkEta, + kTrkDCAxy, + kTrkDCAz, + kTrkFlags, + kTrkClusters, + kTrkTOFRequired, + kTrkPID, + kTrkNStages +}; + +// Resolved PID cut of one species at a given pT. +struct PIDCut { + double tpcMax = 0.; + double tofMax = 0.; + double combined = 0.; + bool tofRequired = false; +}; + +// A cut is on unless it carries the disabled value (tolerant to the float parsing of the JSON value). +inline bool isCutEnabled(float value) +{ + return value > DisabledCut + 1.f; +} + +// v001 micro values are lower-inclusive bin edges: a maximum cut on the grid keeps bins below it. +inline bool passesBinnedMax(double decoded, double cut) +{ + return decoded < cut - o2::constants::math::Epsilon; +} + +// Minimum cut on the grid keeps the bin starting at the cut. +inline bool passesBinnedMin(double decoded, double cut) +{ + return decoded >= cut - o2::constants::math::Epsilon; +} + +template +bool passesMax(double value, double cut) +{ + if constexpr (IsResoMicrotrack) { + return passesBinnedMax(value, cut); + } else { + return value < cut; + } +} + +inline bool isInRange(double value, double minimum, double maximum) +{ + if (isCutEnabled(minimum) && value < minimum) { + return false; + } + if (isCutEnabled(maximum) && value > maximum) { + return false; + } + return true; +} + +inline bool isInWindow(double value, double center, double width) +{ + return std::abs(value - center) < width; +} + +// Preserve pT-bin membership [low, high). +inline int getPtBinIndex(float pt, const std::vector& ptBins) +{ + for (std::size_t i = 1; i < ptBins.size(); ++i) { + if (pt >= ptBins[i - 1] && pt < ptBins[i]) { + return static_cast(i - 1); + } + } + return -1; +} + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Event selection +struct EventCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply reconstructed INEL>0 selection"}; + o2::framework::Configurable cMCINELgt0{"cMCINELgt0", false, "Require generator INEL>0 in MC processes"}; + o2::framework::Configurable cMCVtxIn10{"cMCVtxIn10", false, "Require generator |vz| < 10 cm in MC processes"}; +}; + +/// Track selections (common for all daughter species, -999 switches an optional cut off) +struct TrackCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; + o2::framework::Configurable cMaxEtacut{"cMaxEtacut", -999.f, "Track maximum |eta| cut (-999: off)"}; + // DCAr to PV + o2::framework::Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + o2::framework::Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; + o2::framework::Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; + o2::framework::Configurable cfgUsePtDepDCA{"cfgUsePtDepDCA", false, "Use pT dependent DCA cut instead of the fixed maximum"}; + o2::framework::Configurable cDCAToPVByPtP0{"cDCAToPVByPtP0", 0.004f, "pT dependent DCA cut = P0 + coefficient / pT^power (cm)"}; + o2::framework::Configurable cDCAToPVByPtCoeff{"cDCAToPVByPtCoeff", 0.013f, "Coefficient in the pT dependent DCA cut"}; + o2::framework::Configurable cDCAToPVByPtPower{"cDCAToPVByPtPower", 1.f, "Power in the pT dependent DCA cut"}; + o2::framework::Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + o2::framework::Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + o2::framework::Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + o2::framework::Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + o2::framework::Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster (found clusters, ResoTracks only)"}; + o2::framework::Configurable cfgTPCCrossedRowsMin{"cfgTPCCrossedRowsMin", 0, "Minimum number of TPC crossed rows"}; + o2::framework::Configurable cfgITSNClsMin{"cfgITSNClsMin", 0, "Minimum number of ITS clusters (ResoMicroTracks only)"}; + o2::framework::Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; +}; + +/// PID selection of one daughter species, filled by the task from its own (species-named) configurables. +/// The configurable names are used only in the configuration messages. +struct PIDCutConfig { + std::string species; // e.g. "Pion", used in messages + double maxTPCnSigma = DisabledCut; + double maxTOFnSigma = DisabledCut; + double combinedNSigma = DisabledCut; // combined TPC-TOF cut, on when > 0 + bool onlyTOFTracks = false; // require a TOF signal + bool usePtDependent = false; // use the pT binned cuts below instead of the fixed maxima + std::vector ptBins; // bin edges; the other vectors have one entry per bin + std::vector tpcNSigmaCuts; + std::vector tofNSigmaCuts; + std::vector tofRequired; + std::string maxTPCName; // configurable names for the messages + std::string maxTOFName; + std::string tpcCutsName; + std::string tofCutsName; +}; + +/// Event, track-quality, TOF-requirement and PID selection of resonance daughters. +/// The species index is the position of its PIDCutConfig in init(). +class ResoAnalysisSelectionCore +{ + public: + // byPassTOF skips the TOF nSigma cut and the pT binned TOF requirement; microTracks enables the grid checks. + void init(EventCuts const& eventCuts, TrackCuts const& trackCuts, std::vector pidCuts, bool byPassTOF, bool microTracks) + { + mEventCuts = eventCuts; + mTrackCuts = trackCuts; + mPID = std::move(pidCuts); + mByPassTOF = byPassTOF; + checkConfiguration(microTracks); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return !(mEventCuts.cRecoINELgt0 && !collision.isRecINELgt0()); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + if (mEventCuts.cMCINELgt0 && !collision.isINELgt0()) { + return false; + } + if (mEventCuts.cMCVtxIn10 && !collision.isVtxIn10()) { + return false; + } + return true; + } + + // Resolve the PID cut of one species at a given pT; false if the pT is outside all pT-dependent bins. + bool getPIDCut(int species, float pt, PIDCut& cut) + { + const auto& config = mPID[species]; + cut.tpcMax = config.maxTPCnSigma; + cut.tofMax = config.maxTOFnSigma; + cut.combined = config.combinedNSigma; + cut.tofRequired = false; + if (config.usePtDependent) { + const int ptBin = getPtBinIndex(pt, config.ptBins); + if (ptBin < 0) { + return false; + } + const auto bin = static_cast(ptBin); + cut.tpcMax = config.tpcNSigmaCuts[bin]; + cut.tofMax = config.tofNSigmaCuts[bin]; + cut.tofRequired = config.tofRequired[bin] != 0; + } + return true; + } + + // Track quality selection shared by all species. Returns the last stage that was passed. + // Full tracks store exact values; micro tracks store quantised DCA (see LFResonanceTables.h). + template + int trackQualityStage(const TrackType& track) + { + const double pt = track.pt(); + const double dcaXY = track.dcaXY(); + const double dcaZ = track.dcaZ(); + // Invalid micro DCA codes decode to NaN + if (!std::isfinite(pt) || !std::isfinite(track.eta()) || !std::isfinite(dcaXY) || !std::isfinite(dcaZ)) { + return kTrkInput; + } + if (std::abs(pt) < mTrackCuts.cMinPtcut) { + return kTrkInput; + } + if (isCutEnabled(mTrackCuts.cMaxEtacut) && !(std::abs(track.eta()) < mTrackCuts.cMaxEtacut)) { + return kTrkPt; + } + + if (mTrackCuts.cfgUsePtDepDCA) { + if constexpr (IsResoMicrotrack) { + if (!track.passedPtDependentDCAxy()) { + return kTrkEta; + } + if (!track.passedPtDependentDCAz()) { + return kTrkDCAxy; + } + } else { + const double dcaPtCut = mTrackCuts.cDCAToPVByPtP0 + mTrackCuts.cDCAToPVByPtCoeff * std::pow(pt, -static_cast(mTrackCuts.cDCAToPVByPtPower)); + if (!(std::abs(dcaXY) < dcaPtCut)) { + return kTrkEta; + } + if (!(std::abs(dcaZ) < dcaPtCut)) { + return kTrkDCAxy; + } + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaXY, mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } else { + if (!(std::abs(dcaXY) <= mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaZ, mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) <= mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMin(dcaZ, mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) >= mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + + // Track flags + if ((mTrackCuts.cfgPrimaryTrack && !track.isPrimaryTrack()) || + (mTrackCuts.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) || + (mTrackCuts.cfgGlobalTrack && !track.isGlobalTrack()) || + (mTrackCuts.cfgPVContributor && !track.isPVContributor()) || + (mTrackCuts.cfgUseITSRefit && !track.passedITSRefit()) || + (mTrackCuts.cfgUseTPCRefit && !track.passedTPCRefit())) { + return kTrkDCAz; + } + + // Clusters: found clusters exist only in ResoTracks, ITS clusters only in ResoMicroTracks + if constexpr (!IsResoMicrotrack) { + if constexpr (requires { track.tpcNClsFound(); }) { + if (track.tpcNClsFound() < mTrackCuts.cfgTPCcluster) { + return kTrkFlags; + } + } + } + if constexpr (requires { track.tpcNClsCrossedRows(); }) { + if (track.tpcNClsCrossedRows() < mTrackCuts.cfgTPCCrossedRowsMin) { + return kTrkFlags; + } + } + if constexpr (IsResoMicrotrack) { + if constexpr (requires { track.itsNCls(); }) { + if (track.itsNCls() < mTrackCuts.cfgITSNClsMin) { + return kTrkFlags; + } + } + } + return kTrkClusters; + } + + // TOF signal requirement of the track (global, per species, or per pT bin) + template + bool passesTOFRequired(int species, const TrackType& track) + { + bool required = mTrackCuts.cfgHasTOF || mPID[species].onlyTOFTracks; + PIDCut cut; + // A pT outside all bins is rejected by passesPID + if (!mByPassTOF && getPIDCut(species, track.pt(), cut) && cut.tofRequired) { + required = true; + } + return !required || track.hasTOF(); + } + + // PID selection from the nSigma values of the species; tofNSigma is used only with hasTOF. + template + bool passesPID(int species, float pt, bool hasTOF, double tpcNSigma, double tofNSigma) + { + PIDCut cut; + if (!getPIDCut(species, pt, cut)) { + return false; + } + if (isCutEnabled(cut.tpcMax) && !passesMax(std::abs(tpcNSigma), cut.tpcMax)) { + return false; + } + // Missing TOF is handled by passesTOFRequired; here the TPC alone decides + if (mByPassTOF || !hasTOF) { + return true; + } + bool tofPassed = !isCutEnabled(cut.tofMax) || passesMax(std::abs(tofNSigma), cut.tofMax); + if (!tofPassed && cut.combined > 0 && tpcNSigma * tpcNSigma + tofNSigma * tofNSigma < cut.combined * cut.combined) { + tofPassed = true; + } + return tofPassed; + } + + private: + void checkConfiguration(bool microTracks) + { + // Consistency of the pT dependent PID configuration + bool anyOnlyTOF = false; + for (const auto& config : mPID) { + if (config.usePtDependent) { + const auto& bins = config.ptBins; + if (bins.size() < MinPtBinEdges || config.tpcNSigmaCuts.size() != bins.size() - 1 || + config.tofNSigmaCuts.size() != bins.size() - 1 || config.tofRequired.size() != bins.size() - 1) { + LOG(fatal) << config.species << " pT dependent PID vectors must have (number of pT bin edges - 1) entries"; + } + } + anyOnlyTOF = anyOnlyTOF || config.onlyTOFTracks; + } + if (mByPassTOF && anyOnlyTOF) { + LOG(warning) << "cByPassTOF skips the TOF nSigma selection, but cUseOnlyTOFTrack* still requires a TOF signal"; + } + + // Micro tracks store quantised DCA and nSigma: a cut off the grid would silently act as a different cut. + if (!microTracks) { + return; + } + auto checkDCAGrid = [](const char* name, double cut) { + const double nearest = std::min(std::max(std::round(cut / DCAGridStep) * DCAGridStep, 0.), DCAGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised DCA grid (multiples of " << DCAGridStep << " up to " << DCAGridMax << "); nearest value: " << nearest; + } + }; + auto checkPIDGrid = [](const std::string& name, double cut) { + const double nearest = std::min(std::max(PIDGridStart + std::round((cut - PIDGridStart) / PIDGridStep) * PIDGridStep, PIDGridStart), PIDGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised nSigma grid ({2.0, 2.25, ..., 3.5}); nearest value: " << nearest; + } + }; + if (mTrackCuts.cfgUsePtDepDCA) { + LOG(info) << "Micro tracks use the producer pT dependent DCA flags (0.004 + 0.013 / pT); cDCAToPVByPt* are ignored"; + if (std::abs(mTrackCuts.cDCAToPVByPtP0 - ProducerDCAPtP0) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtCoeff - ProducerDCAPtCoeff) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtPower - ProducerDCAPtPower) > ConfigTolerance) { + LOG(warning) << "cDCAToPVByPt* differ from the producer defaults, but micro tracks always use the producer formula"; + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + checkDCAGrid("cMaxDCArToPVcut", mTrackCuts.cMaxDCArToPVcut); + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + checkDCAGrid("cMaxDCAzToPVcut", mTrackCuts.cMaxDCAzToPVcut); + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + checkDCAGrid("cMinDCAzToPVcut", mTrackCuts.cMinDCAzToPVcut); + } + bool anyCombined = false; + for (const auto& config : mPID) { + if (isCutEnabled(config.maxTPCnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTPCName, config.maxTPCnSigma); + } + if (isCutEnabled(config.maxTOFnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTOFName, config.maxTOFnSigma); + } + anyCombined = anyCombined || config.combinedNSigma > 0; + } + for (const auto& config : mPID) { + if (!config.usePtDependent) { + continue; + } + for (const auto& cut : config.tpcNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tpcCutsName, cut); + } + } + for (const auto& cut : config.tofNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tofCutsName, cut); + } + } + } + if (anyCombined) { + LOG(warning) << "nsigmaCutCombined* on micro tracks uses quantised nSigma values (approximate)"; + } + } + + EventCuts mEventCuts; + TrackCuts mTrackCuts; + std::vector mPID; + bool mByPassTOF = false; +}; + +} // namespace o2::analysis::resonance + +#endif // PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h new file mode 100644 index 00000000000..892d2adefe0 --- /dev/null +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -0,0 +1,125 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFK1MlTables.h +/// \brief Derived K1 microtrack training and audit tables +/// \author Bong-Hwi Lim +/// +#ifndef PWGLF_DATAMODEL_LFK1MLTABLES_H_ +#define PWGLF_DATAMODEL_LFK1MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace k1ml +{ +// Persisted relations target only these derived tables, so AO2D merge tools +// can relocate them. Source AO2D row numbers remain scalar audit values. +DECLARE_SOA_COLUMN(K1RecoCollisionId, k1RecoCollisionId, int64_t); //! row index of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(K1PosZ, k1PosZ, float); +DECLARE_SOA_COLUMN(K1BField, k1BField, float); +DECLARE_SOA_COLUMN(K1Centrality, k1Centrality, float); +DECLARE_SOA_COLUMN(K1Multiplicity, k1Multiplicity, float); +DECLARE_SOA_COLUMN(K1RecINELgt0, k1RecINELgt0, bool); +DECLARE_SOA_COLUMN(K1SourceTrackId, k1SourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(K1Px, k1Px, float); +DECLARE_SOA_COLUMN(K1Py, k1Py, float); +DECLARE_SOA_COLUMN(K1Pz, k1Pz, float); +DECLARE_SOA_COLUMN(K1PidPi, k1PidPi, uint8_t); +DECLARE_SOA_COLUMN(K1PidKa, k1PidKa, uint8_t); +DECLARE_SOA_COLUMN(K1PidPr, k1PidPr, uint8_t); +DECLARE_SOA_COLUMN(K1SelectionFlags, k1SelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(K1TrackFlags, k1TrackFlags, uint8_t); +DECLARE_SOA_COLUMN(K1CrossedRows, k1CrossedRows, uint8_t); +DECLARE_SOA_COLUMN(K1ItsClusterMap, k1ItsClusterMap, uint8_t); +DECLARE_SOA_COLUMN(K1Mass, k1Mass, float); +DECLARE_SOA_COLUMN(K1MassPiPi, k1MassPiPi, float); +DECLARE_SOA_COLUMN(K1MassKaPiSame, k1MassKaPiSame, float); +DECLARE_SOA_COLUMN(K1MassKaPiOpp, k1MassKaPiOpp, float); +DECLARE_SOA_COLUMN(K1ScalarSumPt, k1ScalarSumPt, float); +DECLARE_SOA_COLUMN(K1PiPiPt, k1PiPiPt, float); +DECLARE_SOA_COLUMN(K1Pt, k1Pt, float); +DECLARE_SOA_COLUMN(K1Y, k1Y, float); +DECLARE_SOA_COLUMN(K1Eta, k1Eta, float); +DECLARE_SOA_COLUMN(K1Phi, k1Phi, float); +DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); +// Cumulative selection bits of the unlike-sign candidate: +// 1 = valid canonical candidate inside the K1 rapidity window (loose stage), +// 2 = track quality of all three tracks, +// 4 = TOF requirement and PID of all three tracks, +// 8 = pion-pair pT and secondary mass window, +// 16 = candidate cuts. +// Candidates written at the "selected" export stage carry 31. +DECLARE_SOA_COLUMN(K1BaselinePassBits, k1BaselinePassBits, uint16_t); +DECLARE_SOA_COLUMN(K1MasterFeatures, k1MasterFeatures, float[125]); //! 125 master features of the K1 ML feature contract (PWGLF/Core/K1MlFeatures.h) +DECLARE_SOA_COLUMN(K1FeatureStatus, k1FeatureStatus, uint8_t); //! o2::analysis::k1ml::BuildStatus; only Ok (0) rows are written +DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi +DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t); +DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t); +DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! NaN in K1MlTruth: not filled for reconstructed candidates +DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! NaN in K1MlTruth: not filled for reconstructed candidates +DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(K1GenSelectedRecoEvent, k1GenSelectedRecoEvent, bool); //! true: parents are taken from selected reconstructed events +} // namespace k1ml + +DECLARE_SOA_TABLE(K1MlEvents, "AOD", "K1MLEVENT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1PosZ, k1ml::K1BField, + k1ml::K1Centrality, k1ml::K1Multiplicity, k1ml::K1RecINELgt0); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(K1MlEvent, k1MlEvent, int, K1MlEvents, ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlTracks, "AOD", "K1MLTRACK", + o2::soa::Index<>, k1ml::K1MlEventId, k1ml::K1SourceTrackId, + k1ml::K1Px, k1ml::K1Py, k1ml::K1Pz, + k1ml::K1PidPi, k1ml::K1PidKa, k1ml::K1PidPr, + k1ml::K1SelectionFlags, k1ml::K1TrackFlags, + k1ml::K1CrossedRows, k1ml::K1ItsClusterMap); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(K1MlKaonTrack, k1MlKaonTrack, int, K1MlTracks, "_Kaon"); +DECLARE_SOA_INDEX_COLUMN_FULL(K1MlSamePionTrack, k1MlSamePionTrack, int, K1MlTracks, "_Same"); +DECLARE_SOA_INDEX_COLUMN_FULL(K1MlOppPionTrack, k1MlOppPionTrack, int, K1MlTracks, "_Opp"); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlCandidates, "AOD", "K1MLCANDIDATE", + o2::soa::Index<>, k1ml::K1MlEventId, + k1ml::K1MlKaonTrackId, k1ml::K1MlSamePionTrackId, k1ml::K1MlOppPionTrackId, + k1ml::K1Mass, k1ml::K1MassPiPi, k1ml::K1MassKaPiSame, k1ml::K1MassKaPiOpp, + k1ml::K1ScalarSumPt, k1ml::K1PiPiPt, + k1ml::K1Pt, k1ml::K1Y, k1ml::K1Eta, k1ml::K1Phi, k1ml::K1Charge, + k1ml::K1BaselinePassBits); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(K1MlCandidate, k1MlCandidate, int, K1MlCandidates, ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlInputs, "AOD", "K1MLINPUT", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1MasterFeatures, k1ml::K1FeatureStatus); +DECLARE_SOA_TABLE(K1MlTruth, "AOD", "K1MLTRUTH", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1TruthStatus, k1ml::K1TruthChannel, + k1ml::K1MotherPdg, k1ml::K1MotherId, + k1ml::K1GeneratedPt, k1ml::K1GeneratedY); +DECLARE_SOA_TABLE(K1MlGenAudit, "AOD", "K1MLGENAUDIT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1OriginalMcParticleId, + k1ml::K1MotherPdg, k1ml::K1DaughterPdg1, k1ml::K1DaughterPdg2, + k1ml::K1TruthChannel, k1ml::K1GeneratedPt, k1ml::K1GeneratedY, + k1ml::K1GenSelectedRecoEvent); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFK1MLTABLES_H_ diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index f8a6669861e..b1f09db8d9a 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -79,6 +79,11 @@ o2physics_add_dpl_workflow(k1analysismicro PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k1-training-table + SOURCES k1TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phianalysisrun3 SOURCES phianalysisrun3.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index 0cbab0d427c..9d6877ca7e5 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -14,12 +14,11 @@ /// \author Su-Jeong Ji , Bong-Hwi Lim /// +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" -#include -#include #include -#include #include #include #include @@ -28,129 +27,134 @@ #include #include #include +#include #include #include #include -#include -#include // FIXME +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -using namespace o2::constants::physics; -using namespace o2::constants::math; -; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +/// Histogram binning, QA and debug output +struct HistogramOptions : ConfigurableGroup { + Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; + Configurable cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"}; +}; + +enum BinAnti : unsigned int { + kNormal = 0, + kAnti, + kNAEnd +}; + +enum BinType : unsigned int { + kK1P = 0, + kK1N, + kK1P_Mix, + kK1N_Mix, + kK1P_GenINEL10, + kK1N_GenINEL10, + kK1P_GenINELgt10, + kK1N_GenINELgt10, + kK1P_GenTrig10, + kK1N_GenTrig10, + kK1P_GenEvtSel, + kK1N_GenEvtSel, + kK1P_Rec, + kK1N_Rec, + kTYEnd +}; + +enum class QAFolder { + Before, // QA/*: before the candidate cuts + After, // QAcut/*: after the candidate cuts + MC // QAMC/*: matched K1 truth candidates +}; struct K1AnalysisMicro { - enum BinAnti : unsigned int { - kNormal = 0, - kAnti, - kNAEnd - }; - enum BinType : unsigned int { - kK1P = 0, - kK1N, - kK1P_Mix, - kK1N_Mix, - kK1P_GenINEL10, - kK1N_GenINEL10, - kK1P_GenINELgt10, - kK1N_GenINELgt10, - kK1P_GenTrig10, - kK1N_GenTrig10, - kK1P_GenEvtSel, - kK1N_GenEvtSel, - kK1P_Rec, - kK1N_Rec, - kTYEnd - }; + // Module-initializer v001 tables; full tracks keep their unversioned schema as a fallback. + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoTracks = aod::ResoTracks; // no v001 exists; K1 does not need ResoTrackTracks (trackId unused) + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCTracks = soa::Join; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + SliceCache cache; - Preslice perRCol = aod::resodaughter::resoCollisionId; - Preslice perCollision = aod::track::collisionId; + // Registered only to enable the slice cache that SameKindPair (event mixing) needs, as in Xi1820Analysis + Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - using ResoMCCols = soa::Join; - //// Configurables - Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + // Selection shared with the K1 training-table task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + HistogramOptions histogramOptions; + /// Event Mixing Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; - /// Pre-selection cuts - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - - /// DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; - // DCAz to PV - Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; - Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; - - /// PID Selections - Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion"}; // TPC - Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion"}; // TOF - Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined - Configurable cTOFVeto{"cTOFVeto", true, "TOF Veto, if false, TOF is nessessary for PID selection"}; // TOF Veto - Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection - // Kaon - Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon"}; // TPC - Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon"}; // TOF - Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined - Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection - // Track selections - Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor - Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; - Configurable additionalEvsel{"additionalEvsel", true, "Additional event selcection"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; - Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - - // Secondary selection - Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; - /* - Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; - Configurable cSecondaryMasswindow{"cSecondaryMasswindow", 0.1, "Secondary inv mass selection window"}; - Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", 0, "Min inv. mass selection of another secondary scenario"}; - Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", 999, "MAx inv. mass selection of another secondary scenario"}; - Configurable cMinPiKaMassCut{"cMinPiKaMassCut", 0, "bPion-Kaon pair inv mass selection minimum"}; - Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", 999, "bPion-Kaon pair inv mass selection maximum"}; - Configurable cMinAngle{"cMinAngle", 0, "Minimum angle between K(892)0 and bachelor pion"}; - Configurable cMaxAngle{"cMaxAngle", 4, "Maximum angle between K(892)0 and bachelor pion"}; - Configurable cMinPairAsym{"cMinPairAsym", -1, "Minimum pair asymmetry"}; - Configurable cMaxPairAsym{"cMaxPairAsym", 1, "Maximum pair asymmetry"}; -*/ - - // K1 selection - Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; - Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; - - void init(o2::framework::InitContext&) + + K1AnalysisMicroCore core; + std::array truthDebugCounts{}; + + void init(InitContext&) { + const int sameEventModes = static_cast(doprocessResoTracks) + static_cast(doprocessResoMicroTracks) + + static_cast(doprocessMC) + static_cast(doprocessMCMicro); + const int mixedEventModes = static_cast(doprocessME) + static_cast(doprocessMEMicro); + if (sameEventModes > 1 || mixedEventModes > 1) { + LOG(fatal) << "Enable at most one same-event mode and one mixing mode"; + } + + ProcessModes modes; + modes.microTracks = doprocessResoMicroTracks || doprocessMCMicro || doprocessMEMicro; + modes.mcReco = doprocessMC || doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes); + registerHistograms(modes); + + // Print output histograms statistics + LOG(info) << "Size of the histograms in K1 Analysis Task"; + histos.print(); + } + + void registerHistograms(ProcessModes const& modes) + { + const int nBinsDiv = histogramOptions.cNbinsDiv; std::vector centBinning = {0., 1., 5., 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 80., 90., 100., 200.}; AxisSpec centAxis = {centBinning, "T0M (%)"}; AxisSpec ptAxis = {150, 0, 15, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec dcaxyAxis = {300, 0, 3, "DCA_{#it{xy}} (cm)"}; AxisSpec dcazAxis = {500, 0, 5, "DCA_{#it{z}} (cm)"}; - AxisSpec invMassAxisK892 = {1400 / cNbinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 - AxisSpec invMassAxisRho = {2000 / cNbinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho - AxisSpec invMassAxisReso = {1600 / cNbinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 - AxisSpec invMassAxisScan = {250, 0, 2.5, "Invariant Mass (GeV/#it{c}^2)"}; // For selection + AxisSpec invMassAxisK892 = {1400 / nBinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 + AxisSpec invMassAxisRho = {2000 / nBinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho + AxisSpec invMassAxisReso = {1600 / nBinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 AxisSpec pidQAAxis = {130, -6.5, 6.5}; - AxisSpec dataTypeAxis = {9, 0, 9, "Histogram types"}; - AxisSpec mcTypeAxis = {4, 0, 4, "Histogram types"}; // THnSparse AxisSpec axisAnti = {BinAnti::kNAEnd, 0, BinAnti::kNAEnd, "Type of bin: Normal or Anti"}; AxisSpec axisType = {BinType::kTYEnd, 0, BinType::kTYEnd, "Type of bin with charge and mix"}; - AxisSpec mcLabelAxis = {5, -0.5, 4.5, "MC Label"}; // DCA QA // Primary pion @@ -223,7 +227,16 @@ struct K1AnalysisMicro { histos.add("k1invmass_Mix", "Invariant mass of K1(1270) (ME)", HistType::kTH1F, {invMassAxisReso}); // MC - if (doprocessMC) { + if (modes.mcReco) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"}; + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/channel", "All selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); + histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso}); + histos.add("MCReco/pt", "Reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho}); + histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); @@ -254,494 +267,324 @@ struct K1AnalysisMicro { histos.add("QAMC/hInvmassSecon_PiKa", "Invariant mass of secondary resonance vs pion-kaon", HistType::kTH2F, {invMassAxisRho, invMassAxisK892}); histos.add("QAMC/hInvmassSecon", "Invariant mass of secondary resonance", HistType::kTH1F, {invMassAxisRho}); histos.add("QAMC/hpT_Secondary", "pT distribution of secondary resonance", HistType::kTH1F, {ptAxis}); - } // doprocessMC - // Print output histograms statistics - LOG(info) << "Size of the histograms in K1 Analysis Task"; - histos.print(); - } // init - - // PDG code - int kPDGRho770 = 113; - int kK1Plus = 10323; - - template - bool trackCut(const TrackType& track) - { - if constexpr (!IsResoMicrotrack) { - // basic track cuts - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (std::abs(track.dcaXY()) > cMaxDCArToPVcut) - return false; - if (std::abs(track.dcaZ()) > cMaxDCAzToPVcut) - return false; - if (track.tpcNClsFound() < cfgTPCcluster) - return false; - if (cfgHasTOF && !track.hasTOF()) - return false; - if (cfgUseITSRefit && !track.passedITSRefit()) - return false; - if (cfgUseTPCRefit && !track.passedTPCRefit()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgGlobalTrack && !track.isGlobalTrack()) - return false; - } else { - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cMaxDCArToPVcut - Epsilon) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cMaxDCAzToPVcut - Epsilon) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; + } // mcReco + if (modes.mcGen) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"}; + histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis}); + histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis}); } - return true; } - // Pion PID selection tools - template - bool selectionPIDpion(const T& candidate) + // Track QA of a pion; isPrimary selects the trkppion (first) or trkspion (second) histograms + template + void fillPionQA(const TrackType& track, bool isPrimary) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaPi()) < cMaxTPCnSigmaPion) { - tpcPIDPassed = true; - } else { - return false; - } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaPi()) < cMaxTOFnSigmaPion) { - tofPIDPassed = true; + const bool hasTOF = track.hasTOF(); + if (isPrimary) { + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - if ((nsigmaCutCombinedPion > 0) && (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi() < nsigmaCutCombinedPion * nsigmaCutCombinedPion)) { - tofPIDPassed = true; + histos.fill(HIST("QA/trkppionpT"), track.pt()); + histos.fill(HIST("QA/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkppionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } + histos.fill(HIST("QAcut/trkppionpT"), track.pt()); + histos.fill(HIST("QAcut/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkppionDCAz"), track.dcaZ()); } else { - if (!cTOFVeto) { - return false; + histos.fill(HIST("QAMC/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkppionpT"), track.pt()); + histos.fill(HIST("QAMC/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkppionDCAz"), track.dcaZ()); } } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaPiFlag())) < cMaxTPCnSigmaPion + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaPiFlag())) < cMaxTOFnSigmaPion + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QA/trkspionpT"), track.pt()); + histos.fill(HIST("QA/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkspionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAcut/trkspionpT"), track.pt()); + histos.fill(HIST("QAcut/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkspionDCAz"), track.dcaZ()); + } else { + histos.fill(HIST("QAMC/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAMC/trkspionpT"), track.pt()); + histos.fill(HIST("QAMC/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkspionDCAz"), track.dcaZ()); } } - return false; } - // Kaon PID selection tools - template - bool selectionPIDkaon(const T& candidate) + // Track QA of the bachelor kaon + template + void fillKaonQA(const TrackType& track) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaKa()) < cMaxTPCnSigmaKaon) { - tpcPIDPassed = true; - } else { - return false; + const bool hasTOF = track.hasTOF(); + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QA/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QA/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cMaxTOFnSigmaKaon) { - tofPIDPassed = true; - } - if ((nsigmaCutCombinedKaon > 0) && (candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa() + candidate.tofNSigmaKa() * candidate.tofNSigmaKa() < nsigmaCutCombinedKaon * nsigmaCutCombinedKaon)) { - tofPIDPassed = true; - } - } else { - if (!cTOFVeto) { - return false; - } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QA/trkkaonpT"), track.pt()); + histos.fill(HIST("QA/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkkaonDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAcut/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAcut/trkkaonpT"), track.pt()); + histos.fill(HIST("QAcut/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkkaonDCAz"), track.dcaZ()); } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaKaFlag())) < cMaxTPCnSigmaKaon + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaKaFlag())) < cMaxTOFnSigmaKaon + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAMC/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAMC/trkkaonpT"), track.pt()); + histos.fill(HIST("QAMC/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkkaonDCAz"), track.dcaZ()); } - return false; } - template - bool isTrueK1(const T& trk1, const T& trk2, const T2& bTrack) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - if (std::abs(bTrack.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (((std::abs(trk1.motherPDG()) && std::abs(trk2.motherPDG()) != kPDGRho770) && (std::abs(bTrack.motherPDG()) != kK1Plus)) || (std::abs(trk1.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk2.motherPDG()) != kK1Plus)) || (std::abs(trk2.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk1.motherPDG()) != kK1Plus))) - return false; - auto siblings = bTrack.siblingIds(); - if (siblings[0] != mother1 && siblings[1] != mother2) - return false; - return true; - } // isTrueK1 - - template - bool isTrueK892(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kK0Star892) - return false; - return true; - } - - template - bool isTrueRho(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kPDGRho770) - return false; - return true; - } + // Histograms of the selected pion pairs and (pion, pion, kaon) candidates of one collision + // (or one mixed pair of collisions). The selection itself is the shared K1 core. + // dTracks1: bachelor kaons, dTracks2: pions. template void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) { - auto multiplicity = collision.cent(); - TLorentzVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1; - for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks2, dTracks2))) { - // Full index policy is needed to consider all possible combinations - if (trk1.index() == trk2.index()) - continue; // We need to run (0,1), (1,0) pairs too. But the same id pairs are not needed. - // trk1: pion, trk2: pion, bTrack: kaon - if (!trackCut(trk1) || !trackCut(trk2)) - continue; + const bool fillQA = !IsMix && histogramOptions.additionalQAplots; + const auto multiplicity = collision.cent(); + + // Pion pair passing the pion selection; trk1 is the pion with the lower index + auto onPair = [&](auto const& trk1, auto const& trk2, ROOT::Math::PxPyPzMVector const& secondary, bool passesPairPt) { + if (fillQA) { + fillPionQA(trk1, true); + fillPionQA(trk2, false); + } + if (!passesPairPt) { + return; + } + if (fillQA) { + histos.fill(HIST("QA/hInvmassSecon"), secondary.M()); + } + if constexpr (IsMC) { + histos.fill(HIST("QAMC/hpT_Secondary"), secondary.Pt()); + } + }; - auto trk1pt = trk1.pt(); - auto trk2pt = trk2.pt(); - auto isTrk1hasTOF = trk1.hasTOF(); - auto isTrk2hasTOF = trk2.hasTOF(); - - if constexpr (!IsResoMicrotrack) { - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - if (cUseOnlyTOFTrackPi && !isTrk1hasTOF) - continue; - if (!selectionPIDpion(trk1) || !selectionPIDpion(trk2)) - continue; - - if constexpr (!IsMix) { - - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QA/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QA/trkppionDCAz"), trk1.dcaZ()); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QA/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QA/trkspionDCAz"), trk2.dcaZ()); - } - } else { - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag())); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QA/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag())); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QA/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); + // Candidate passing the pion, pair and kaon selection; pion1 is the K*0 partner in the unlike-sign case + auto onCandidate = [&](auto const& kaon, auto const& pion1, auto const& pion2, K1CandidateValues const& c) { + if (fillQA) { + fillKaonQA(kaon); + } + if (!c.inRapidity) { + return; } - // Resonance reconstruction - lDecayDaughter1.SetXYZM(trk1.px(), trk1.py(), trk1.pz(), MassPionCharged); - lDecayDaughter2.SetXYZM(trk2.px(), trk2.py(), trk2.pz(), MassPionCharged); - lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + // QA histogram before the candidate cuts + if (fillQA) { + histos.fill(HIST("QA/K1OA"), c.angle); + histos.fill(HIST("QA/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QA/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QA/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QA/hpT_Secondary"), c.secondary.Pt()); + } + if (!c.passesCandidateCuts) { + return; + } - if (lResonanceSecondary.Pt() < cMinSecondaryPtCut) - continue; + // QA histograms after the candidate cuts + if (fillQA) { + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + histos.fill(HIST("QAcut/K1OA"), c.angle); + histos.fill(HIST("QAcut/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAcut/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAcut/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAcut/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAcut/hpT_Secondary"), c.secondary.Pt()); + } - if constexpr (!IsMix) { - histos.fill(HIST("QA/hInvmassSecon"), lResonanceSecondary.M()); + const unsigned int typeNormal = BinAnti::kNormal; + if constexpr (IsMix) { + const unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; + histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_Mix"), c.k1.M()); + return; } - if constexpr (IsMC) { - /* - if (isTrueK892(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } else { - if (isTrueRho(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } - */ - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); + unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P : BinType::kK1N; + if (c.isUnlikeSign) { + histos.fill(HIST("k1invmass"), c.k1.M()); + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + } else { + histos.fill(HIST("k1invmass_LS"), c.k1.M()); + histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); } - // Mass Window cut is removed - - for (const auto& bTrack : dTracks1) { - if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) - continue; - if (!trackCut(bTrack)) - continue; - if (!selectionPIDkaon(bTrack)) - continue; - - // K1 reconstruction - lDecayDaughter_bach.SetXYZM(bTrack.px(), bTrack.py(), bTrack.pz(), MassKaonCharged); - lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; - - // Cuts - if (lResonanceK1.Rapidity() > cK1MaxRap || lResonanceK1.Rapidity() < cK1MinRap) - continue; - - auto lK1Angle = lResonanceSecondary.Angle(lDecayDaughter_bach.Vect()); - auto lPairAsym = (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); - - TLorentzVector temp13 = lDecayDaughter1 + lDecayDaughter_bach; - TLorentzVector temp23 = lDecayDaughter2 + lDecayDaughter_bach; - - // QA histogram - if constexpr (!IsMix) { - histos.fill(HIST("QA/K1OA"), lK1Angle); - histos.fill(HIST("QA/K1PairAsym"), lPairAsym); - histos.fill(HIST("QA/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QA/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QA/hpT_Secondary"), lResonanceSecondary.Pt()); + + if constexpr (IsMC) { + const auto channel = classifyK1Truth(pion1, pion2, kaon); + const int channelBin = static_cast(channel); + histos.fill(HIST("MCReco/channel"), channelBin); + histos.fill(HIST("MCReco/mass"), channelBin, c.k1.M()); + histos.fill(HIST("MCReco/pt"), channelBin, c.k1.Pt()); + histos.fill(HIST("MCReco/piPiMass"), channelBin, c.secondary.M()); + histos.fill(HIST("MCReco/pi1KMass"), channelBin, c.mass13); + histos.fill(HIST("MCReco/pi2KMass"), channelBin, c.mass23); + if (channel == K1TruthChannel::None) { + histos.fill(HIST("k1invmass_MC_noK1"), c.k1.M()); + return; } - // Selection cuts are removed - // QA histograms after the cuts are removed as no cuts are applied - - if constexpr (!IsMix) { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P : BinType::kK1N; - unsigned int typeNormal = BinAnti::kNormal; - if (trk1.sign() * trk2.sign() < 0) { - histos.fill(HIST("k1invmass"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } else { - histos.fill(HIST("k1invmass_LS"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } - - if constexpr (IsMC) { - if (isTrueK1(trk1, trk2, bTrack)) { - typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_MC"), lResonanceK1.M()); - histos.fill(HIST("QAMC/K1OA"), lK1Angle); - histos.fill(HIST("QAMC/K1PairAsym"), lPairAsym); - histos.fill(HIST("QAMC/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QAMC/hInvmassSecon"), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hpT_Seocondary"), lResonanceSecondary.Pt()); - - if constexpr (!IsResoMicrotrack) { - - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - // PID QA primary pion - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QAMC/trkppionDCAz"), trk1.dcaZ()); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QAMC/trkspionDCAz"), trk2.dcaZ()); - - } else { - - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags())); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags())); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); - } - } else { - histos.fill(HIST("k1invmass_MC_noK1"), lResonanceK1.M()); - } - } // IsMC - } else { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; - unsigned int typeNormal = BinAnti::kNormal; - histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_Mix"), lResonanceK1.M()); + if (truthDebugCounts[channelBin] < histogramOptions.cfgTruthDebug) { + ++truthDebugCounts[channelBin]; + LOGP(info, "K1Truth channel={} collision={} tracks=({},{},{}) pdg=({},{},{}) mothers=({},{},{}) motherPDG=({},{},{}) siblings=(({},{}),({},{}),({},{}))", + channelBin, collision.globalIndex(), + pion1.globalIndex(), pion2.globalIndex(), kaon.globalIndex(), + pion1.pdgCode(), pion2.pdgCode(), kaon.pdgCode(), pion1.motherId(), pion2.motherId(), kaon.motherId(), + pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(), + pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]); } - } // bTrack - } - } // fillHistograms + typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_MC"), c.k1.M()); + histos.fill(HIST("QAMC/K1OA"), c.angle); + histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAMC/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAMC/hpT_Secondary"), c.secondary.Pt()); + + // PID QA primary and secondary pion + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + } + }; + + core.forEachCandidate(histos, collision, dTracks1, dTracks2, IsMC || fillQA, onPair, onCandidate); + } - void processResoTracks(aod::ResoCollision const& collision, - aod::ResoTracks const& resotracks) + void processResoTracks(ResoCollisions::iterator const& collision, + ResoTracks const& resotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resotracks, resotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoTracks, "Process ResoTracks", false); - void processResoMicroTracks(aod::ResoCollision const& collision, - aod::ResoMicroTracks const& resomicrotracks) + void processResoMicroTracks(ResoCollisions::iterator const& collision, + ResoMicroTracks const& resomicrotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resomicrotracks, resomicrotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true); - void processMC(aod::ResoCollision const& collision, - soa::Join const& resotracks) + void processMC(ResoMCCols::iterator const& collision, + ResoMCTracks const& resotracks) { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); fillHistograms(collision, resotracks, resotracks); } PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false); - void processMCTrue(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) { - auto multiplicity = collision.cent(); - for (const auto& part : resoParents) { - if (std::abs(part.pdgCode()) != kK1Plus) - continue; - if (std::abs(part.y()) > 0.5) { - continue; - } - bool pass1 = false; - bool pass2 = false; - bool pass3 = false; - bool pass4 = false; - if (std::abs(part.daughterPDG1()) == 313 || std::abs(part.daughterPDG2()) == 313) { // At least one decay into K892 - pass2 = true; - } - if (std::abs(part.daughterPDG1()) == kPiPlus || std::abs(part.daughterPDG2()) == kPiPlus) { // At lest one decay into pion - pass1 = true; - } - if (std::abs(part.daughterPDG1()) == kPDGRho770 || std::abs(part.daughterPDG2()) == kPDGRho770) { - pass4 = true; - } - if (std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus) { - pass3 = true; - } - if (!pass1 || !pass2 || !pass3 || !pass4) // If we have both decay products - continue; - auto typeNormal = part.pdgCode() > 0 ? BinAnti::kNormal : BinAnti::kAnti; - if (collision.isVtxIn10()) // INEL>10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINEL10 : BinType::kK1N_GenINEL10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isInSel8()) // INEL>10, vtx10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINELgt10 : BinType::kK1N_GenINELgt10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isTriggerTVX()) // vtx10, TriggerTVX - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenTrig10 : BinType::kK1N_GenTrig10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isInAfterAllCuts()) // after all event selection - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenEvtSel : BinType::kK1N_GenEvtSel; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } + // The modular producer already selected these reconstructed collisions. + // Apply precisely the same reconstruction loop as the data baseline. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + fillHistograms(collision, tracks, tracks); } - PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process Event for MC", false); + PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + // Keep other/unresolved immediate decays too; never require both pairs. + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + const int charge = part.pdgCode() > 0 ? 1 : -1; + histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast(channel)); + histos.fill(HIST("MCGen/ptChannel"), static_cast(channel), part.pt()); + }); + } + PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false); // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) + void processME(ResoCollisions const& collisions, ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; PROCESS_SWITCH(K1AnalysisMicro, processME, "Process EventMixing light without partition", false); // Processing Event Mixing -- Micro - // using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processMEMicro(o2::aod::ResoCollisions const& collisions, aod::ResoMicroTracks const& resomicrotracks) + void processMEMicro(ResoCollisions const& collisions, ResoMicroTracks const& resomicrotracks) { auto tracksTuple = std::make_tuple(resomicrotracks); BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; - PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", true); -}; // struct + PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", false); +}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx new file mode 100644 index 00000000000..8b070a97be1 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx @@ -0,0 +1,281 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file k1TrainingTable.cxx +/// \brief Derived K1 training table workflow +/// \author Bong-Hwi Lim +/// + +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFK1MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +static_assert(std::extent_v == o2::analysis::k1ml::NMasterFeatures, + "K1MasterFeatures column size must match the K1 ML feature contract"); + +/// Writes the unlike-sign K1 micro candidates of the shared K1 selection, with the +/// canonical (kaon, same-sign pion, opposite-sign pion) tracks and the master features. +struct K1TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + + Produces k1MlEvents; + Produces k1MlTracks; + Produces k1MlCandidates; + Produces k1MlInputs; + Produces k1MlTruth; + Produces k1MlGenAudit; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the K1 histogram task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + + Configurable k1MlExportStage{"k1MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (pass bits = 31)"}; + Configurable k1MlLooseAudit{"k1MlLooseAudit", true, "Record loose US cutflow and mass/pT/activity spectrum"}; + Configurable k1MlParityRows{"k1MlParityRows", 0, "Log feature hashes of the first N exported candidates"}; + + K1AnalysisMicroCore core; + int64_t k1MlEventRow = -1; + std::unordered_map k1MlTrackRows; + int k1MlParityLogged = 0; + + void init(InitContext&) + { + if (static_cast(doprocessResoMicroTracks) + static_cast(doprocessMCMicro) != 1 || + (doprocessMCTrue && !doprocessMCMicro)) { + LOG(fatal) << "K1 training table requires exactly one of processResoMicroTracks and processMCMicro; processMCTrue requires processMCMicro"; + } + if (k1MlParityRows < 0) { + LOG(fatal) << "k1MlParityRows must not be negative"; + } + if (k1MlExportStage.value != "loose" && k1MlExportStage.value != "selected") { + LOG(fatal) << "k1MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.microTracks = true; + modes.mcReco = doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = k1MlLooseAudit; + looseOptions.exportSelected = k1MlExportStage.value == "selected"; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes, looseOptions); + if (doprocessMCMicro) { + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + } + + // Candidates that violate the canonical or feature contract are skipped, never written. + auto skipped = histos.add("ML/exportSkipped", "Skipped candidates;K1 ML build status;candidates", HistType::kTH1D, {{6, -0.5, 5.5}}); + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedTrack", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + + LOG(info) << "Size of the histograms in K1 training table task"; + histos.print(); + } + + template + int64_t writeK1MlTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = k1MlTrackRows.find(id); it != k1MlTrackRows.end()) { + return it->second; + } + k1MlTracks(k1MlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const int64_t row = k1MlTracks.lastIndex(); + k1MlTrackRows.emplace(id, row); + return row; + } + + template + void writeK1MlEvent(Collision const& collision) + { + k1MlTrackRows.clear(); + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + k1MlEvents(collision.globalIndex(), + collision.posZ(), collision.bMagField(), collision.cent(), collision.multiplicity(), collision.isRecINELgt0()); + k1MlEventRow = k1MlEvents.lastIndex(); + } + + template + void logParity(Collision const& collision, o2::analysis::k1ml::CandidateSnapshot const& candidate, o2::analysis::k1ml::FeaturePack const& pack) + { + if (k1MlParityLogged >= k1MlParityRows) { + return; + } + uint64_t hash = FnvOffsetBasis; + for (const auto& value : pack.master) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + LOGP(info, "K1MLPARITY collision={} tracks={},{},{} featureHash={}", + collision.globalIndex(), candidate.tracks[0].sourceTrackId, candidate.tracks[1].sourceTrackId, + candidate.tracks[2].sourceTrackId, hash); + ++k1MlParityLogged; + } + + template + void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion, + K1TruthChannel channel, uint16_t passBits) + { + using o2::analysis::k1ml::BuildStatus; + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion)); + if (canonical.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = o2::analysis::k1ml::buildMasterFeatures(canonical.candidate); + if (pack.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(pack.status)); + return; + } + logParity(collision, canonical.candidate, pack); + const auto kaonRow = writeK1MlTrack(kaon); + const auto sameRow = writeK1MlTrack(samePion); + const auto oppRow = writeK1MlTrack(oppPion); + ROOT::Math::PxPyPzMVector k{kaon.px(), kaon.py(), kaon.pz(), MassKaonCharged}; + ROOT::Math::PxPyPzMVector s{samePion.px(), samePion.py(), samePion.pz(), MassPionCharged}; + ROOT::Math::PxPyPzMVector o{oppPion.px(), oppPion.py(), oppPion.pz(), MassPionCharged}; + const auto mother = k + s + o; + k1MlCandidates(k1MlEventRow, kaonRow, sameRow, oppRow, + static_cast(mother.M()), static_cast((s + o).M()), + static_cast((k + s).M()), static_cast((k + o).M()), + pack.kinematics.scalarSumPt, static_cast((s + o).Pt()), + static_cast(mother.Pt()), static_cast(mother.Rapidity()), + static_cast(mother.Eta()), static_cast(mother.Phi()), + static_cast(kaon.sign()), passBits); + const int64_t row = k1MlCandidates.lastIndex(); + k1MlInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = channel != K1TruthChannel::None; + int motherId = -1; + if (matched) { + motherId = channel == K1TruthChannel::RhoK ? kaon.motherId() : std::abs(samePion.motherPDG()) == Pdg::kK1_1270Plus ? samePion.motherId() + : oppPion.motherId(); + } + // Immediate-mother information alone does not prove a physical UID. + k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast(channel), + matched ? kaon.sign() * Pdg::kK1_1270Plus : 0, static_cast(motherId), + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + } else { + k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1}, + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + } + } + + void processResoMicroTracks(ResoCollisions::iterator const& collision, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + writeK1MlEvent(collision); + // Selection only: the K1 analysis histograms belong to the K1 analysis task + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + }); + } + PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true); + + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) + { + // The modular producer already selected these reconstructed collisions. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + writeK1MlEvent(collision); + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + k1MlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), + part.pt(), part.y(), true); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCTrue, "Write generated K1 parents of selected reconstructed MC events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 791f2b95f361c986d6bd89ddf73e8b41e065291c Mon Sep 17 00:00:00 2001 From: Neelkamal Mallick <104082831+nmallick19@users.noreply.github.com> Date: Mon, 5 Oct 2026 10:39:09 +0300 Subject: [PATCH 173/648] =?UTF-8?q?[PWGCF]=20JCorran:=20Add=20nonflow=20SP?= =?UTF-8?q?C=20correlators=20and=20a=20smaller=20Q-vector=20f=E2=80=A6=20(?= =?UTF-8?q?#18116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Neelkamal Mallick Co-authored-by: ALICE Action Bot --- PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx | 16 ++++++--- PWGCF/JCorran/Core/FlowJSPCAnalysis.h | 36 +++++++++++++++++--- PWGCF/JCorran/Core/FlowJSPCObservables.h | 43 +++++++++++++++--------- PWGCF/JCorran/Core/JQVectors.h | 30 +++++++++++++---- PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx | 6 +++- 5 files changed, 98 insertions(+), 33 deletions(-) diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx index 0c1927d9579..2fc35810957 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx @@ -119,11 +119,19 @@ void FlowJSPCAnalysis::calculateCorrelators(const int fCentBin) // Histogram filling fillHistograms(fCentBin, j, correlationNum, correlationDenom, weightCorrelationNum, weightCorrelationDenom); + } - correlationNum = 0.; - weightCorrelationNum = 0.; - correlationDenom = 0.; - weightCorrelationDenom = 0.; + // N_m = Re(Q_{0,1}); weight M_m. 3SPC → fN3, 4SPC → fN4. + if (mHistRegistry && qvecs) { + const double nSel = qvecs->QvectorQC[0][1].Re(); + const float centX = static_cast(fCentBin) + 0.5f; + if (nSel > 0.0 && std::isfinite(nSel)) { + if (mWhichSPC == 0 && fCorrelDenoms[2] > 0.0) { + mHistRegistry->fill(HIST("fN3"), centX, nSel, fCorrelDenoms[2]); + } else if (mWhichSPC == 1 && fCorrelDenoms[3] > 0.0) { + mHistRegistry->fill(HIST("fN4"), centX, nSel, fCorrelDenoms[3]); + } + } } } diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h index 59a677dccd7..97ddedc1af2 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -42,6 +43,27 @@ class FlowJSPCAnalysis int getCentBin(float cValue); using JQVectorsT = JQVectors; + /// Fill-grid size for Q_{n,p}: (v8 * nPartDen)+1 harmonics, nPartDen+1 powers. + static constexpr uint32_t Nh3p = 49; // 3-particle SPC, 6-particle denominator + static constexpr uint32_t Nk3p = 7; + static constexpr uint32_t Nh4p = 65; // 4-particle SPC, 8-particle denominator + static constexpr uint32_t Nk4p = 9; + static constexpr uint32_t NhFull = 113; + static constexpr uint32_t NkFull = 15; + void qVectorGrid(int whichSPC, uint32_t& nhUse, uint32_t& nkUse) + { + mWhichSPC = whichSPC; + if (whichSPC == 0) { + nhUse = Nh3p; + nkUse = Nk3p; + } else if (whichSPC == 1) { + nhUse = Nh4p; + nkUse = Nk4p; + } else { + nhUse = NhFull; + nkUse = NkFull; + } + } inline void setQvectors(const JQVectorsT* _qvecs) { qvecs = _qvecs; } void correlation(int c_nPart, int c_nHarmo, int* harmo, double* correlData); void calculateCorrelators(const int fCentBin); @@ -57,6 +79,9 @@ class FlowJSPCAnalysis return; } mHistRegistry->add("FullCentrality", "FullCentrality", o2::framework::HistType::kTH1D, {{100, 0., 100.}}, true); + // Effective N_m per centrality class, weighted by M_m (arXiv:2606.10258 c0). + mHistRegistry->add("fN3", "Effective N_{3};centrality class;N_{3}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); + mHistRegistry->add("fN4", "Effective N_{4};centrality class;N_{4}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); mHistRegistry->add("Centrality_0/fResults", "Numerators and denominators", {o2::framework::HistType::kTProfile, {{24, 0., 24.}}}, true); mHistRegistry->add("Centrality_0/fCovResults", "Covariance N*D", {o2::framework::HistType::kTProfile, {{48, 0., 48.}}}, true); mHistRegistry->add("Centrality_0/phiBefore", "Phi before", {o2::framework::HistType::kTH1D, {{100, 0., o2::constants::math::TwoPI}}}, true); @@ -67,13 +92,13 @@ class FlowJSPCAnalysis } } - void setCorrSet(int obsInd, int harmo[8]) + void setCorrSet(int obsInd, int const harmo[8]) { for (int i = 0; i < 8; i++) { fHarmosArray[obsInd][i] = harmo[i]; } } - void setFullCorrSet(int harmo[12][8]) + void setFullCorrSet(int const harmo[12][8]) { memcpy(fHarmosArray, harmo, sizeof(int) * 12 * 8); } @@ -92,13 +117,14 @@ class FlowJSPCAnalysis private: const int mNqHarmos = 113; ///< Highest harmo for Q(n,p): (v8*14part)+1. const int mNqPowers = 15; ///< Max power for Q(n,p): 14part+1. - const JQVectorsT* qvecs; + const JQVectorsT* qvecs = nullptr; o2::framework::HistogramRegistry* mHistRegistry = nullptr; - int fHarmosArray[12][8]; + int fHarmosArray[12][8] = {{0}}; - double fCorrelDenoms[14]; + double fCorrelDenoms[14] = {0}; + int mWhichSPC = 0; ClassDefNV(FlowJSPCAnalysis, 1); }; diff --git a/PWGCF/JCorran/Core/FlowJSPCObservables.h b/PWGCF/JCorran/Core/FlowJSPCObservables.h index 440d70c8bf2..e5db30a94e7 100644 --- a/PWGCF/JCorran/Core/FlowJSPCObservables.h +++ b/PWGCF/JCorran/Core/FlowJSPCObservables.h @@ -38,42 +38,53 @@ class FlowJSPCObservables switch (index) { case 0: { LOGF(info, "Computing three harmonic SPC"); - int harmonicArray01[maxNrComb][8] = { - {3, 6, -3, -3, 0, 0, 0, 0}, - {3, 4, -2, -2, 0, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258 nonflow refs in unused slots. + int const harmonicArray01[maxNrComb][8] = { + {3, 6, -3, -3, 0, 0, 0, 0}, // 0: C633 = + {3, 4, -2, -2, 0, 0, 0, 0}, // 1: C422 = {3, 8, -4, -4, 0, 0, 0, 0}, - {3, 2, 4, -6, 0, 0, 0, 0}, - {3, 2, 3, -5, 0, 0, 0, 0}, + {3, 2, 4, -6, 0, 0, 0, 0}, // 3: C246 = + {3, 2, 3, -5, 0, 0, 0, 0}, // 4: C235 = {3, 3, 4, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 2, 5, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 3, 5, -8, 0, 0, 0, 0}, // These are three harmonic SPC!! - {0, 6, -2, -2, -2, 0, 0, 0}, - {0, 2, -3, -4, 5, 0, 0, 0}, - {0, 2, -3, -3, 4, 0, 0, 0}, - {0, 3, 3, -2, -2, -2, 0, 0}}; + // {0, 6, -2, -2, -2, 0, 0, 0}, + // {0, 2, -3, -4, 5, 0, 0, 0}, + // {0, 2, -3, -3, 4, 0, 0, 0}, + // {0, 3, 3, -2, -2, -2, 0, 0}, + {3, 1, 1, -2, 0, 0, 0, 0}, // 8: C112 = , Eqs. (IV.7), (IV.18) + {3, 1, 2, -3, 0, 0, 0, 0}, // 9: C123 = , Eqs. (IV.8), (IV.18) + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray01, sizeof(int) * maxNrComb * 8); } break; case 1: { LOGF(info, "Computing four harmonic SPC"); - int harmonicArray02[maxNrComb][8] = { - {4, 6, -2, -2, -2, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258: c1{4}=<<4>>-2<<2>>^2 after averaging, Eq. (IV.6). + int const harmonicArray02[maxNrComb][8] = { + {4, 6, -2, -2, -2, 0, 0, 0}, // 0: C6222 = {4, 2, -3, -4, 5, 0, 0, 0}, {4, 2, -3, -3, 4, 0, 0, 0}, {4, 2, 2, 3, -7, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 2, 4, -8, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 7, -4, -5, 0, 0, 0}, {4, 3, -4, -4, 5, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + {4, 1, 1, -1, -1, 0, 0, 0}, // 7: <<4>>_{1,1,-1,-1} for c1{4} + {2, 1, -1, 0, 0, 0, 0, 0}, // 8: <<2>>_{1,-1} = + {3, 1, 1, -2, 0, 0, 0, 0}, // 9: C112 on the 4-particle sample (mixed-order Eq. (IV.19)) {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray02, sizeof(int) * maxNrComb * 8); } break; case 2: { LOGF(info, "Computing five and six harmonic SPC"); - int harmonicArray03[maxNrComb][8] = { + int const harmonicArray03[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {5, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, @@ -90,7 +101,7 @@ class FlowJSPCObservables } break; case 3: { LOGF(info, "Computing slected five harmonic SPC"); - int harmonicArray04[maxNrComb][8] = { + int const harmonicArray04[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {0, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, diff --git a/PWGCF/JCorran/Core/JQVectors.h b/PWGCF/JCorran/Core/JQVectors.h index d0ba705c32f..aadb821fc2b 100644 --- a/PWGCF/JCorran/Core/JQVectors.h +++ b/PWGCF/JCorran/Core/JQVectors.h @@ -19,6 +19,8 @@ #include +#include +#include #include #include @@ -52,11 +54,16 @@ class JQVectors : public std::conditional_t, JQ using hasInvMass = decltype(std::declval().invMass()); template - inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f) + inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f, + uint32_t nhUse = nh, uint32_t nkUse = nk) { + // nhUse/nkUse limit the filled (harmonic, power) grid. Defaults keep the full template size. + const uint32_t nH = std::min(nhUse, nh); + const uint32_t nK = std::min(nkUse, nk); + // calculate Q-vector for QC method ( no subgroup ) - for (UInt_t ih = 0; ih < nh; ++ih) { - for (UInt_t ik = 0; ik < nk; ++ik) { + for (UInt_t ih = 0; ih < nH; ++ih) { + for (UInt_t ik = 0; ik < nK; ++ik) { QvectorQC[ih][ik] = Q(0, 0); if constexpr (gap) { for (UInt_t isub = 0; isub < 2; ++isub) @@ -64,7 +71,7 @@ class JQVectors : public std::conditional_t, JQ } } } - for (auto& track : inputInst) { + for (auto const& track : inputInst) { if (track.eta() < -etamax || track.eta() > etamax) continue; using JInputClassIter = typename JInputClass::iterator; @@ -74,10 +81,15 @@ class JQVectors : public std::conditional_t, JQ } UInt_t isub = (UInt_t)(track.eta() > 0.0); - for (UInt_t ih = 0; ih < nh; ++ih) { + const Double_t phi = track.phi(); + const Double_t c1 = TMath::Cos(phi); + const Double_t s1 = TMath::Sin(phi); + Double_t cn = 1.0; // cos(ih * phi), ih = 0 + Double_t sn = 0.0; // sin(ih * phi) + for (UInt_t ih = 0; ih < nH; ++ih) { Double_t tf = 1.0; - for (UInt_t ik = 0; ik < nk; ++ik) { - Q q(tf * TMath::Cos(ih * track.phi()), tf * TMath::Sin(ih * track.phi())); + for (UInt_t ik = 0; ik < nK; ++ik) { + Q q(tf * cn, tf * sn); QvectorQC[ih][ik] += q; if constexpr (gap) { @@ -90,6 +102,10 @@ class JQVectors : public std::conditional_t, JQ if constexpr (std::experimental::is_detected::value) tf *= track.weightEff(); } + const Double_t cnNext = cn * c1 - sn * s1; + const Double_t snNext = cn * s1 + sn * c1; + cn = cnNext; + sn = snNext; } } } diff --git a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx index 6892ab58658..cfe7f931a39 100644 --- a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx @@ -109,6 +109,8 @@ struct flowJSPCAnalysis { std::unique_ptr multCutFormula; std::array multCutFormulaParamIndex; + uint32_t mNhUse = FlowJSPCAnalysis::NhFull; + uint32_t mNkUse = FlowJSPCAnalysis::NkFull; void init(InitContext const&) { @@ -118,6 +120,8 @@ struct flowJSPCAnalysis { spcObservables.setSPCObservables(cfgWhichSPC); spcAnalysis.setFullCorrSet(spcObservables.harmonicArray); + spcAnalysis.qVectorGrid(cfgWhichSPC, mNhUse, mNkUse); + LOGF(info, "Q-vector fill grid: nh=%u nk=%u (cfgWhichSPC=%d)", mNhUse, mNkUse, cfgWhichSPC.value); histManager.setHistRegistryQA(&qaHistRegistry); histManager.setDebugLog(false); @@ -186,7 +190,7 @@ struct flowJSPCAnalysis { if (cfgFillQA) histManager.fillEventQA<1>(collision, cBin, cent, nTracks); - jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax); + jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax, 0.0f, 999.9f, mNhUse, mNkUse); spcAnalysis.setQvectors(&jqvecs); spcAnalysis.calculateCorrelators(cBin); } From e030cfab7d4ea0a5ff943730d8365647e7672fe5 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Mon, 5 Oct 2026 10:22:35 +0200 Subject: [PATCH 174/648] [PWGCF] Add independent mixed-event diagnostics to HadNucleiFemto (#18168) --- .../DataModel/HadronNucleiTables.h | 58 ++++ .../TableProducer/HadNucleiFemto.cxx | 296 +++++++++++++++++- 2 files changed, 350 insertions(+), 4 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h index 161be5544ab..250fb7f2308 100644 --- a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h +++ b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h @@ -24,6 +24,64 @@ namespace o2::aod { +// Independent mixed-event audit output. IDs are unique only together with the +// two UUID words identifying the task instance. Not an extension of pair rows. +namespace hnmediag +{ +DECLARE_SOA_COLUMN(InstanceHi, instanceHi, uint64_t); +DECLARE_SOA_COLUMN(InstanceLo, instanceLo, uint64_t); +DECLARE_SOA_COLUMN(CallId, callId, uint64_t); +DECLARE_SOA_COLUMN(Timeslice, timeslice, uint64_t); +DECLARE_SOA_COLUMN(TfCounter, tfCounter, uint32_t); +DECLARE_SOA_COLUMN(TfRun, tfRun, uint32_t); +DECLARE_SOA_COLUMN(Kind, kind, uint8_t); +DECLARE_SOA_COLUMN(Status, status, uint8_t); +DECLARE_SOA_COLUMN(PoolEpoch, poolEpoch, uint64_t); +DECLARE_SOA_COLUMN(Run, run, int32_t); +DECLARE_SOA_COLUMN(PreviousRun, previousRun, int32_t); +DECLARE_SOA_COLUMN(EventId, eventId, int64_t); +DECLARE_SOA_COLUMN(PartnerId, partnerId, int64_t); +DECLARE_SOA_COLUMN(InputCollisionId, inputCollisionId, int64_t); +DECLARE_SOA_COLUMN(GlobalBc, globalBc, uint64_t); +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(ZVertex, zVertex, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(PvMultiplicity, pvMultiplicity, float); +DECLARE_SOA_COLUMN(NumContrib, numContrib, uint32_t); +DECLARE_SOA_COLUMN(NNuclei, nNuclei, uint32_t); +DECLARE_SOA_COLUMN(NHadrons, nHadrons, uint32_t); +DECLARE_SOA_COLUMN(PoolBin, poolBin, int32_t); +DECLARE_SOA_COLUMN(PoolBefore, poolBefore, uint32_t); +DECLARE_SOA_COLUMN(PoolAfter, poolAfter, uint32_t); +DECLARE_SOA_COLUMN(BufferedEvents, bufferedEvents, uint64_t); +DECLARE_SOA_COLUMN(OccupiedBins, occupiedBins, uint32_t); +DECLARE_SOA_COLUMN(ExpectedPairs, expectedPairs, uint64_t); +DECLARE_SOA_COLUMN(AcceptedPairs, acceptedPairs, uint64_t); +DECLARE_SOA_COLUMN(RejectedPairs, rejectedPairs, uint64_t); +DECLARE_SOA_COLUMN(Combinations, combinations, uint64_t); +DECLARE_SOA_COLUMN(InputCollisions, inputCollisions, uint64_t); +DECLARE_SOA_COLUMN(PassedEventSelection, passedEventSelection, uint64_t); +DECLARE_SOA_COLUMN(PassedZorro, passedZorro, uint64_t); +} // namespace hnmediag +DECLARE_SOA_TABLE(HadNucleiMEDiag, "AOD", "HNMEDIAG", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::Timeslice, hnmediag::TfCounter, hnmediag::TfRun, + hnmediag::Kind, hnmediag::Status, hnmediag::PoolEpoch, + hnmediag::Run, hnmediag::PreviousRun, hnmediag::EventId, + hnmediag::InputCollisionId, hnmediag::GlobalBc, hnmediag::Timestamp, + hnmediag::ZVertex, hnmediag::Centrality, hnmediag::PvMultiplicity, + hnmediag::NumContrib, hnmediag::NNuclei, hnmediag::NHadrons, + hnmediag::PoolBin, hnmediag::PoolBefore, hnmediag::PoolAfter, + hnmediag::BufferedEvents, hnmediag::OccupiedBins, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs, hnmediag::RejectedPairs, + hnmediag::Combinations, hnmediag::InputCollisions, + hnmediag::PassedEventSelection, hnmediag::PassedZorro); +DECLARE_SOA_TABLE(HadNucleiMEDiagLinks, "AOD", "HNMEDIAGLINK", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::PoolEpoch, hnmediag::Run, hnmediag::EventId, + hnmediag::PartnerId, hnmediag::PoolBin, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs); + namespace hadron_nuclei_tables { diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index fc838b5f3b3..cafbb4acc3a 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -43,11 +43,16 @@ #include #include #include +#include #include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -62,6 +67,7 @@ #include #include #include +#include #include #include @@ -322,6 +328,17 @@ struct HadNucleiFemto { Produces mOutputHadHyperDataTable; Produces mOutputHadHyperMCTable; Produces mOutputMultiplicityTable; + Produces mOutputMEDiagnostic; + Produces mOutputMEDiagnosticLinks; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"meDiagnostic"}; + Configurable saveRejectedEvents{"saveRejectedEvents", false, "Save rejected collision rows too (potentially very large); rejection counters are always recorded"}; + Configurable savePartnerLinks{"savePartnerLinks", true, "Save one diagnostic row per undirected mixed-event combination"}; + Configurable productionTag{"productionTag", "unspecified", "Production label recorded with diagnostic instance UUID"}; + Configurable codeTag{"codeTag", "unspecified", "Exact source/build revision label for the diagnostic run"}; + } meDiagnostic; struct : o2::framework::ConfigurableGroup { // cppcheck-suppress unusedStructMember @@ -647,6 +664,29 @@ struct HadNucleiFemto { int mMixingRunNumber{-1}; int mHyperMixingRunNumber{-1}; int mHyperMCMixingRunNumber{-1}; + // A separate state machine: the ordinary ME pools and counters are untouched. + std::unordered_map> mDiagnosticPools; + TimingInfo mDiagnosticTiming; + std::string mDiagnosticUUID; + uint64_t mDiagnosticInstanceHi{0}, mDiagnosticInstanceLo{0}; + uint64_t mDiagnosticCall{0}, mDiagnosticEpoch{0}, mDiagnosticBuffered{0}; + uint64_t mDiagnosticExpected{0}, mDiagnosticAccepted{0}, mDiagnosticCombinations{0}; + int64_t mNextDiagnosticEventId{0}; + int mDiagnosticRun{-1}; + static constexpr uint8_t DiagnosticEventKind = 3; + struct MEDiagnosticRow { + uint8_t kind{DiagnosticEventKind}, status{0}; + int run{-1}, previousRun{-1}; + int64_t eventId{-1}, inputCollisionId{-1}; + uint64_t globalBC{0}, timestamp{0}; + float z{0.f}, centrality{0.f}, multiplicity{0.f}; + uint32_t numContrib{0}, nNuclei{0}, nHadrons{0}; + int poolBin{-1}; + uint32_t before{0}, after{0}; + uint64_t expected{0}, accepted{0}, combinations{0}; + uint64_t inputCollisions{0}, passedEventSelection{0}, passedZorro{0}; + }; + HistogramRegistry mDiagnosticRegistry{"MEDiagnostic", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; o2::vertexing::DCAFitterN<2> mFitter; int mRunNumber{0}; @@ -769,10 +809,10 @@ struct HadNucleiFemto { {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -5.0f, 5.0f}}}}, {"purity/h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, + {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, {"purity/h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 7.0f}}}}, + {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, // Hypertriton {"hHe3TPCnsigma", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(He3)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, @@ -800,8 +840,44 @@ struct HadNucleiFemto { false, true}; - void init(o2::framework::InitContext&) - { + void init(o2::framework::InitContext& initContext) + { + if (doprocessMixedEventDiagnostic) { + // AnalysisTask does not automatically invoke a task endOfStream method. + // CallbackService appends this callback without replacing output flushing. + initContext.services().get().set( + [this](EndOfStreamContext& context) { endOfStream(context); }); + if (doprocessMixedEvent || doprocessSameEvent || doprocessMC || doprocessHyper || doprocessMCHyper || doprocessPurity || doprocessDcaFractionData || doprocessDcaFractionPurityMC) { + LOG(fatal) << "processMixedEventDiagnostic must run alone; disable the other HadNucleiFemto processes to avoid shared selection-QA contamination"; + } + if (eventMixing.settingNoMixedEvents.value <= 0) { + LOG(fatal) << "ME diagnostic requires a positive mixing depth"; + } + mDiagnosticUUID = TUUID().AsString(); + std::string uuidWords = mDiagnosticUUID; + uuidWords.erase(std::remove(uuidWords.begin(), uuidWords.end(), '-'), uuidWords.end()); + mDiagnosticInstanceHi = std::stoull(uuidWords.substr(0, 16), nullptr, 16); + mDiagnosticInstanceLo = std::stoull(uuidWords.substr(16, 16), nullptr, 16); + const auto& device = initContext.services().get(); + const char* alienJob = std::getenv("ALIEN_PROC_ID"); + LOG(info) << "ME_DIAG INIT instance=" << mDiagnosticUUID << " device=" << device.id + << " rank=" << device.rank << " inputTimesliceId=" << device.inputTimesliceId + << " job=" << (alienJob ? alienJob : "unknown") + << " production=" << meDiagnostic.productionTag.value << " code=" << meDiagnostic.codeTag.value + << " depth=" << eventMixing.settingNoMixedEvents.value + << " requireBoth=" << eventMixing.settingRequireBothSpeciesForMixing.value + << " multiplicityBinning=" << eventMixing.settingUseMultiplicityBinning.value + << " applyCPR=" << CPR.settingApplyClosePairRejection.value; + for (const auto& name : {"hLifecycle", "hSelection"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{6, -0.5, 5.5}}); + } + mDiagnosticRegistry.add("hPoolOccupancy", "Prior partners;partners;events", HistType::kTH1D, {{101, -0.5, 100.5}}); + mDiagnosticRegistry.add("hPairs", "0=expected,1=accepted,2=CPR rejected;counter;pairs", HistType::kTH1D, {{3, -0.5, 2.5}}); + // The global sink keys by task + histogram name, not registry directory. + for (const auto& name : {"hMEDiagKStar_LS_M", "hMEDiagKStar_LS_A", "hMEDiagKStar_US_M", "hMEDiagKStar_US_A"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{300, 0., 3.}}); + } + } const bool processHyperPairs = doprocessHyper || doprocessMCHyper; if (processHyperPairs && hadHyper.maxOutputKstar.value == 0.f) { LOG(fatal) << "Hadron-hypertriton mode requires a nonzero output k* range"; @@ -3332,6 +3408,218 @@ struct HadNucleiFemto { } PROCESS_SWITCH(HadNucleiFemto, processMixedEvent, "Process Mixed event", false); + void run(ProcessingContext& context) + { + if (doprocessMixedEventDiagnostic) { + // TimingInfo is stream-scoped; access it through the active context. + mDiagnosticTiming = context.services().get(); + } + } + + void writeMEDiagnostic(const MEDiagnosticRow& row) + { + mOutputMEDiagnostic(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + static_cast(mDiagnosticTiming.timeslice), mDiagnosticTiming.tfCounter, mDiagnosticTiming.runNumber, + row.kind, row.status, mDiagnosticEpoch, row.run, row.previousRun, row.eventId, + row.inputCollisionId, row.globalBC, row.timestamp, row.z, row.centrality, row.multiplicity, + row.numContrib, row.nNuclei, row.nHadrons, row.poolBin, row.before, row.after, + mDiagnosticBuffered, static_cast(mDiagnosticPools.size()), + row.expected, row.accepted, row.expected - row.accepted, row.combinations, + row.inputCollisions, row.passedEventSelection, row.passedZorro); + if (row.kind != DiagnosticEventKind) { + mDiagnosticRegistry.fill(HIST("hLifecycle"), row.kind); + } + } + + bool fillDiagnosticMixedPair(const BufferedTrack& nucleus, const BufferedTrack& hadron) + { + // Same CPR predicate and kinematics as fillMixedPair; separate final QA, + // and never write the ordinary pair/multiplicity tables from this process. + if (isClosePair(nucleus, hadron, /*fillQA*/ true)) { + return false; + } + HadNucandidate candidate; + fillBufferedCandidateInfo(nucleus, hadron, candidate); + if (candidate.recoPtNu() > 0) { + if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_M"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_M"), candidate.kstar); + } + } else if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_A"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_A"), candidate.kstar); + } + return true; + } + + void processMixedEventDiagnostic(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) + { + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; + ++mDiagnosticCall; + MEDiagnosticRow call; + call.run = mDiagnosticRun; + call.inputCollisions = collisions.size(); + if (mDiagnosticCall == 1) { + call.kind = 0; // Instance initialization, emitted in a valid table context. + writeMEDiagnostic(call); + } + call.kind = 1; + writeMEDiagnostic(call); + LOG(info) << "ME_DIAG BEGIN instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " timeslice=" << mDiagnosticTiming.timeslice << " tfCounter=" << mDiagnosticTiming.tfCounter + << " run=" << mDiagnosticRun << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size(); + + for (const auto& collision : collisions) { + const auto bc = collision.template bc_as(); + MEDiagnosticRow row; + row.run = bc.runNumber(); + row.inputCollisionId = collision.globalIndex(); + row.globalBC = bc.globalBC(); + row.timestamp = bc.timestamp(); + row.z = collision.posZ(); + row.centrality = collision.centFT0C(); + row.multiplicity = collision.multNTracksPV(); + row.numContrib = collision.numContrib(); + mDiagnosticRegistry.fill(HIST("hSelection"), 0); + if (!passesEventSelection(collision)) { + row.status = 1; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedEventSelection; + mDiagnosticRegistry.fill(HIST("hSelection"), 1); + if (!passesZorroSelection(collision)) { + row.status = 2; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedZorro; + mDiagnosticRegistry.fill(HIST("hSelection"), 2); + // Match the ordinary ME ordering: observe the run before species/bin cuts. + if (mDiagnosticRun != bc.runNumber()) { + MEDiagnosticRow reset = row; + reset.kind = 2; + reset.status = mDiagnosticRun < 0 ? 0 : 1; + reset.previousRun = mDiagnosticRun; + ++mDiagnosticEpoch; + writeMEDiagnostic(reset); // Contains the pre-clear buffered totals. + LOG(info) << "ME_DIAG CLEAR instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " reason=" << (mDiagnosticRun < 0 ? "first-run" : "run-change") + << " oldRun=" << mDiagnosticRun << " newRun=" << bc.runNumber() + << " epoch=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size(); + mDiagnosticPools.clear(); + mDiagnosticBuffered = 0; + mDiagnosticRun = bc.runNumber(); + } + BufferedCollision current; + current.eventId = mNextDiagnosticEventId++; + current.posZ = collision.posZ(); + current.numContrib = collision.numContrib(); + current.centFT0C = collision.centFT0C(); + current.multFT0C = collision.multFT0C(); + row.eventId = current.eventId; + auto tracksThisCollision = tracks.sliceBy(mPerCol, collision.globalIndex()); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + if (selectTrackNu(track) && selectionPIDNu(track)) { + current.nuclei.push_back(makeBufferedTrack(track, true, current.eventId)); + } + if (selectTrackHadron(track) && selectionPIDHadron(track)) { + current.hadrons.push_back(makeBufferedTrack(track, false, current.eventId)); + } + } + row.nNuclei = current.nuclei.size(); + row.nHadrons = current.hadrons.size(); + if (eventMixing.settingRequireBothSpeciesForMixing.value && (current.nuclei.empty() || current.hadrons.empty())) { + row.status = 3; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 3); + row.poolBin = eventMixing.settingUseMultiplicityBinning.value + ? multiplicityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.multNTracksPV())) + : centralityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (row.poolBin < 0) { + row.status = 4; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 4); + auto& pool = mDiagnosticPools[row.poolBin]; + row.before = pool.size(); + row.combinations = pool.size(); + mDiagnosticRegistry.fill(HIST("hPoolOccupancy"), row.before); + for (const auto& partner : pool) { + const uint64_t expected = static_cast(current.nuclei.size()) * partner.hadrons.size() + + static_cast(partner.nuclei.size()) * current.hadrons.size(); + uint64_t accepted = 0; + for (const auto& nucleus : current.nuclei) { + for (const auto& hadron : partner.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + for (const auto& nucleus : partner.nuclei) { + for (const auto& hadron : current.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + row.expected += expected; + row.accepted += accepted; + if (meDiagnostic.savePartnerLinks) { + mOutputMEDiagnosticLinks(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + mDiagnosticEpoch, mDiagnosticRun, current.eventId, partner.eventId, row.poolBin, expected, accepted); + } + } + if (pool.size() >= static_cast(eventMixing.settingNoMixedEvents.value)) { + pool.pop_front(); + } else { + ++mDiagnosticBuffered; + } + pool.push_back(std::move(current)); + row.after = pool.size(); + writeMEDiagnostic(row); + call.expected += row.expected; + call.accepted += row.accepted; + call.combinations += row.combinations; + mDiagnosticRegistry.fill(HIST("hPairs"), 0., static_cast(row.expected)); + mDiagnosticRegistry.fill(HIST("hPairs"), 1., static_cast(row.accepted)); + mDiagnosticRegistry.fill(HIST("hPairs"), 2., static_cast(row.expected - row.accepted)); + } + call.kind = 4; + call.run = mDiagnosticRun; + writeMEDiagnostic(call); + mDiagnosticExpected += call.expected; + mDiagnosticAccepted += call.accepted; + mDiagnosticCombinations += call.combinations; + LOG(info) << "ME_DIAG END instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size() + << " combinations=" << call.combinations << " expected=" << call.expected << " accepted=" << call.accepted; + } + PROCESS_SWITCH(HadNucleiFemto, processMixedEventDiagnostic, "Independent ME pool lifecycle diagnostic (run alone)", false); + + void endOfStream(EndOfStreamContext&) + { + if (doprocessMixedEventDiagnostic) { + // EOS table writes are not guaranteed to be flushed; final snapshot in log. + LOG(info) << "ME_DIAG EOS instance=" << mDiagnosticUUID << " calls=" << mDiagnosticCall + << " epochs=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size() << " combinations=" << mDiagnosticCombinations + << " expected=" << mDiagnosticExpected << " accepted=" << mDiagnosticAccepted; + } + } + // Produce the data distributions fitted offline with the MC templates. All // nominal quality and PID selections are applied; only DCA is relaxed. void processDcaFractionData(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) From dd604f6a0656b9e63f8dc3781f704b115238bdf4 Mon Sep 17 00:00:00 2001 From: Junlee Kim Date: Mon, 5 Oct 2026 19:03:43 +0900 Subject: [PATCH 175/648] [PWGLF] cha01710analysis.cxx: adding QA plots (#18132) --- PWGLF/Tasks/Resonances/cha01710analysis.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx index 8a75cd96a48..52900b00422 100644 --- a/PWGLF/Tasks/Resonances/cha01710analysis.cxx +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -191,6 +191,8 @@ struct Cha01710analysis { histos.add("Kaon/hTPCNSigma", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Kaon/hTOFNSigma", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTPCNSigmaSelected", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTOFNSigmaSelected", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Pair/hSignalPlus", "K0S K+ same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); histos.add("Pair/hSignalMinus", "K0S K- same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); @@ -339,6 +341,9 @@ struct Cha01710analysis { if (track.globalIndex() == pos.globalIndex() || track.globalIndex() == neg.globalIndex() || !selectKaon(track)) { continue; } + histos.fill(HIST("Kaon/hTPCNSigmaSelected"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("Kaon/hTOFNSigmaSelected"), track.pt(), track.tofNSigmaKa()); + ROOT::Math::PxPyPzMVector kaon(track.px(), track.py(), track.pz(), constants::physics::MassKaonCharged); auto mother = k0 + kaon; if (std::abs(mother.Rapidity()) > cfgMotherRapidityMax) { From 1163dd7b0d5939d544c169ca74a055d8b4304f53 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Mon, 5 Oct 2026 12:58:08 +0200 Subject: [PATCH 176/648] [Common] Add outer FT0C and FV0, and combined fit signals to centrality study (#18039) --- Common/DataModel/Multiplicity.h | 21 +++++++- .../TableProducer/Converters/CMakeLists.txt | 5 ++ .../Converters/multBCs002Converter.cxx | 51 +++++++++++++++++++ .../TableProducer/multiplicityExtraTable.cxx | 24 +++++---- Common/Tasks/centralityStudy.cxx | 42 ++++++++++++--- 5 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 Common/TableProducer/Converters/multBCs002Converter.cxx diff --git a/Common/DataModel/Multiplicity.h b/Common/DataModel/Multiplicity.h index c258fd0c426..d9b15e4f85e 100644 --- a/Common/DataModel/Multiplicity.h +++ b/Common/DataModel/Multiplicity.h @@ -103,6 +103,7 @@ DECLARE_SOA_COLUMN(TimeToNeNext, timeToNeNext, float); //! DECLARE_SOA_COLUMN(FT0TriggerMask, ft0TriggerMask, uint8_t); //! DECLARE_SOA_COLUMN(MultFV0AOuter, multFV0AOuter, float); //! FV0 without innermost ring DECLARE_SOA_COLUMN(MultFT0AOuter, multFT0AOuter, float); //! FT0A without innermost ring +DECLARE_SOA_COLUMN(MultFT0COuter, multFT0COuter, float); //! FT0C without innermost ring // Timing information DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); //! @@ -144,7 +145,7 @@ DECLARE_SOA_TABLE(MFTMults, "AOD", "MFTMULT", //! Multiplicity with MFT DECLARE_SOA_TABLE(FITExtraMults, "AOD", "FITEXTRAMULT", //! Extra information from FIT detectors mult::MultFV0AOuter, mult::MultFT0AOuter, - mult::FT0TriggerMask); + mult::MultFT0COuter); using BarrelMults = soa::Join; using Mults = soa::Join; @@ -311,6 +312,22 @@ DECLARE_SOA_TABLE_VERSIONED(MultBCs_001, "AOD", "MULTBC", 1, //! mult::MultFV0AOuter, mult::MultFT0AOuter); +DECLARE_SOA_TABLE_VERSIONED(MultBCs_002, "AOD", "MULTBC", 2, //! + mult::MultFT0A, + mult::MultFT0C, + mult::MultFV0A, + mult::MultFDDA, + mult::MultFDDC, + mult::MultZNA, + mult::MultZNC, + mult::MultZEM1, + mult::MultZEM2, + mult::MultZPA, + mult::MultZPC, + mult::MultFV0AOuter, + mult::MultFT0AOuter, + mult::MultFT0COuter); + DECLARE_SOA_TABLE(MultBcSel_000, "AOD", "MULTBCSEL", //! BC selection bits joinable with multBCs evsel::Selection); @@ -339,7 +356,7 @@ DECLARE_SOA_TABLE(TimeBCs, "AOD", "TIMEBC", //! mult::TimeZPA, mult::TimeZPC) -using MultBCs = MultBCs_001; +using MultBCs = MultBCs_002; using MultBcSel = MultBcSel_001; using MultBC = MultBCs::iterator; diff --git a/Common/TableProducer/Converters/CMakeLists.txt b/Common/TableProducer/Converters/CMakeLists.txt index a8b14efaca3..600dfea3d9b 100644 --- a/Common/TableProducer/Converters/CMakeLists.txt +++ b/Common/TableProducer/Converters/CMakeLists.txt @@ -118,3 +118,8 @@ o2physics_add_dpl_workflow(mult-bcs-converter SOURCES multBCsConverter.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(mult-bcs-002-converter + SOURCES multBCs002Converter.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/Common/TableProducer/Converters/multBCs002Converter.cxx b/Common/TableProducer/Converters/multBCs002Converter.cxx new file mode 100644 index 00000000000..dfed7182c1a --- /dev/null +++ b/Common/TableProducer/Converters/multBCs002Converter.cxx @@ -0,0 +1,51 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multBCs002Converter.cxx +/// \brief Converts MultBCs table from version 001 to 002 +/// \author Jesper Karlsson Gumrpecht + +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include + +struct MultBCs002Converter { + o2::framework::Produces multBC; + static constexpr float DummyValue = -1.f; + void process(o2::aod::MultBCs_001 const& multBCs) + { + multBC.reserve(multBCs.size()); + for (const auto& multbc : multBCs) { + multBC(multbc.multFT0A(), + multbc.multFT0C(), + multbc.multFV0A(), + multbc.multFDDA(), + multbc.multFDDC(), + multbc.multZNA(), + multbc.multZNC(), + multbc.multZEM1(), + multbc.multZEM2(), + multbc.multZPA(), + multbc.multZPC(), + multbc.multFV0AOuter(), + multbc.multFT0AOuter(), + DummyValue /* dummy amplitude for FT0C Outer */); + } + } +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + return o2::framework::WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Common/TableProducer/multiplicityExtraTable.cxx b/Common/TableProducer/multiplicityExtraTable.cxx index 3211b23a346..19a1280f60c 100644 --- a/Common/TableProducer/multiplicityExtraTable.cxx +++ b/Common/TableProducer/multiplicityExtraTable.cxx @@ -44,7 +44,6 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using BCPattern = std::bitset; -const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; struct MultiplicityExtraTable { Produces multBC; @@ -62,6 +61,7 @@ struct MultiplicityExtraTable { Configurable bcDownscaleFactor{"bcDownscaleFactor", 2, "Downscale factor for BC table (0: save nothing, 1: save all)"}; Configurable minFT0CforBCTable{"minFT0CforBCTable", 25.0f, "Minimum FT0C amplitude to fill BC table to reduce data"}; Configurable saveOnlyBCsWithCollisions{"saveOnlyBCsWithCollisions", true, "save only BCs with collisions in them"}; + Configurable requestMinSignalForFT0C{"requestMinSignalForFT0C", true, "save only BCs with minimum FT0C signal"}; Configurable bcTableFloatPrecision{"bcTableFloatPrecision", 0.1, "float precision in bc table for data reduction"}; @@ -126,14 +126,14 @@ struct MultiplicityExtraTable { float multFT0C = 0.f; if (bc.has_ft0()) { auto ft0 = bc.ft0(); - for (const auto amplitude : ft0.amplitudeC()) { + for (const auto amplitude : ft0.amplitudeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFT0C += amplitude; } } else { multFT0C = -999.0f; } - if (multFT0C < minFT0CforBCTable) { + if (requestMinSignalForFT0C && multFT0C < minFT0CforBCTable) { continue; // skip this event } @@ -166,6 +166,7 @@ struct MultiplicityExtraTable { float multFDDA = 0.f; float multFDDC = 0.f; float multFT0AOuter = 0.f; + float multFT0COuter = 0.f; float multFV0AOuter = 0.f; // ZDC amplitudes @@ -196,7 +197,7 @@ struct MultiplicityExtraTable { // initialize - from Arvind newRunNumber = bc.runNumber(); - int localBC = bc.globalBC() % nBCsPerOrbit; + int localBC = bc.globalBC() % o2::constants::lhc::LHCMaxBunches; if (newRunNumber != oldRunNumber) { auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, newRunNumber); @@ -224,8 +225,12 @@ struct MultiplicityExtraTable { multFT0AOuter += ft0.amplitudeA()[ii]; } } - for (const auto amplitude : ft0.amplitudeC()) { - multFT0C += amplitude; + for (size_t ii = 0; ii < ft0.amplitudeC().size(); ++ii) { + multFT0C += ft0.amplitudeC()[ii]; + static constexpr int MaxChannelIdInnerRingFT0C = 48; + if (ft0.channelC()[ii] > MaxChannelIdInnerRingFT0C) { + multFT0COuter += ft0.amplitudeC()[ii]; + } } posZFT0 = ft0.posZ(); posZFT0valid = ft0.isValidTime(); @@ -261,10 +266,10 @@ struct MultiplicityExtraTable { std::bitset<8> fFDDTriggers = fdd.triggerMask(); multFDDTriggerBits = static_cast(fFDDTriggers.to_ulong()); - for (const auto amplitude : fdd.chargeA()) { + for (const auto amplitude : fdd.chargeA()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDA += amplitude; } - for (const auto amplitude : fdd.chargeC()) { + for (const auto amplitude : fdd.chargeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDC += amplitude; } @@ -313,7 +318,8 @@ struct MultiplicityExtraTable { tru(multZPA), tru(multZPC), tru(multFV0AOuter), - tru(multFT0AOuter)); + tru(multFT0AOuter), + tru(multFT0COuter)); multBcSel( bc.selection_raw(), diff --git a/Common/Tasks/centralityStudy.cxx b/Common/Tasks/centralityStudy.cxx index fab358689b1..3746eb622d5 100644 --- a/Common/Tasks/centralityStudy.cxx +++ b/Common/Tasks/centralityStudy.cxx @@ -185,7 +185,7 @@ struct CentralityStudy { Configurable factorFT0C{"factorFT0C", 1.00f, "scale the raw FT0C signal for convenience"}; Configurable factorFV0A{"factorFV0A", 1.00f, "scale the raw FV0A signal for convenience"}; Configurable factorFT0M{"factorFT0M", 1.00f, "scale the raw FT0M signal for convenience"}; - Configurable factorFV0AT0C{"factorFV0AT0C", 1.00f, "scale the raw FV0A+FT0C signal for convenience"}; + Configurable factorFIT{"factorFIT", 1.00f, "scale the raw FV0A+FT0C signal for convenience"}; Configurable normFT0A{"normFT0A", 1.00f, "scale the self-normalised FT0A signal; scale.normFT0A * FT0A / "}; Configurable normFT0C{"normFT0C", 1.00f, "scale the self-normalised FT0C signal; scale.normFT0C * FT0C / "}; Configurable normFV0A{"normFV0A", 1.00f, "scale the self-normalised FV0A signal; scale.normFV0A * FV0A / "}; @@ -206,6 +206,7 @@ struct CentralityStudy { ConfigurableAxis axisFT0COccupancy{"axisFT0COccupancy", {50, 0, 80000}, "FT0C occupancy"}; // For one-dimensional plots, where binning is no issue + ConfigurableAxis axisMultUltraFineFIT{"axisMultUltraFineFIT", {60000, 0, 60000}, "FT0M + FV0 amplitude"}; ConfigurableAxis axisMultUltraFineFV0A{"axisMultUltraFineFV0A", {60000, 0, 60000}, "FV0A amplitude"}; ConfigurableAxis axisMultUltraFineFT0C{"axisMultUltraFineFT0C", {60000, 0, 60000}, "FT0C amplitude"}; ConfigurableAxis axisMultUltraFineFT0A{"axisMultUltraFineFT0A", {60000, 0, 60000}, "FT0A amplitude"}; @@ -273,8 +274,13 @@ struct CentralityStudy { histos.add("hFT0C_Collisions", "hFT0C_Collisions", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0M_Collisions", "hFT0M_Collisions", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_Collisions", "hFV0A_Collisions", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_Collisions", "hFV0AOuter_Collisions", kTH1D, {axisMultUltraFineFV0A}); histos.add("hFT0AOuter_Collisions", "hFT0AOuter_Collisions", kTH1D, {axisMultUltraFineFT0A}); + histos.add("hFT0COuter_Collisions", "hFT0COuter_Collisions", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0MOuterA_Collisions", "hFT0MOuterA_Collisions", kTH1D, {axisMultUltraFineFT0M}); + histos.add("hFIT_Collisions", "hFIT_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_Collisions", "hFIT_FullV0_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_Collisions", "hFIT_All_Collisions", kTH1D, {axisMultUltraFineFIT}); histos.add("hNGlobalTracks", "hNGlobalTracks", kTH1D, {axisMultUltraFineGlobalTracks}); histos.add("hNMFTTracks", "hNMFTTracks", kTH1D, {axisMultUltraFineMFTTracks}); @@ -407,14 +413,20 @@ struct CentralityStudy { histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(8, "zpac time"); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(9, "isFlangeEvent"); + histos.add("hFIT_BCs", "hFIT_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_BCs", "hFIT_FullV0_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_BCs", "hFIT_All_BCs", kTH1D, {axisMultUltraFineFIT}); histos.add("hFT0C_BCs", "hFT0C_BCs", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0COuter_BCs", "hFT0COuter_BCs", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0A_BCs", "hFT0A_BCs", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0AOuter_BCs", "hFT0AOuter_BCs", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0M_BCs", "hFT0M_BCs", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFT0MOuterA_BCs", "hFT0MOuterA_BCs", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_BCs", "hFV0A_BCs", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_BCs", "hFV0AOuter_BCs", kTH1D, {axisMultUltraFineFV0A}); histos.add("hFV0AT0C_BCs", "hFV0AT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); + histos.add("hFV0AOuterT0C_BCs", "hFV0AOuterT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); histos.add("hScaledFT0M_BCs", "hScaledFT0M_BCs", kTH1D, {axisMultUltraFineScaledFT0M}); histos.add("hScaledFV0AT0C_BCs", "hScaledFV0AT0C_BCs", kTH1D, {axisMultUltraFineScaledFV0AT0C}); @@ -541,11 +553,17 @@ struct CentralityStudy { getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(19, "bcsel"); histPointers.insert({histPath + "hFT0C_Collisions", histos.add((histPath + "hFT0C_Collisions").c_str(), "hFT0C_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0COuter_Collisions", histos.add((histPath + "hFT0COuter_Collisions").c_str(), "hFT0COuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); histPointers.insert({histPath + "hFT0A_Collisions", histos.add((histPath + "hFT0A_Collisions").c_str(), "hFT0A_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFV0AOuter_Collisions", histos.add((histPath + "hFV0AOuter_Collisions").c_str(), "hFV0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); histPointers.insert({histPath + "hFT0M_Collisions", histos.add((histPath + "hFT0M_Collisions").c_str(), "hFT0M_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFV0A_Collisions", histos.add((histPath + "hFV0A_Collisions").c_str(), "hFV0A_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hFIT_Collisions", histos.add((histPath + "hFIT_Collisions").c_str(), "hFIT_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_FullV0_Collisions", histos.add((histPath + "hFIT_FullV0_Collisions").c_str(), "hFIT_FullV0_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_All_Collisions", histos.add((histPath + "hFIT_All_Collisions").c_str(), "hFIT_All_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hNGlobalTracks", histos.add((histPath + "hNGlobalTracks").c_str(), "hNGlobalTracks", {kTH1D, {{axisMultUltraFineGlobalTracks}}})}); histPointers.insert({histPath + "hNMFTTracks", histos.add((histPath + "hNMFTTracks").c_str(), "hNMFTTracks", {kTH1D, {{axisMultUltraFineMFTTracks}}})}); histPointers.insert({histPath + "hNPVContributors", histos.add((histPath + "hNPVContributors").c_str(), "hNPVContributors", {kTH1D, {{axisMultUltraFinePVContributors}}})}); @@ -1174,8 +1192,14 @@ struct CentralityStudy { if constexpr (requires { collision.has_multBC(); }) { if (collision.has_multBC()) { auto multbc = collision.template multBC_as>(); + histos.fill(HIST("hFV0AOuter_Collisions"), multbc.multFV0AOuter() * scale.factorFV0A); histos.fill(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); histos.fill(HIST("hFT0MOuterA_Collisions"), multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); + + histos.fill(HIST("hFT0COuter_Collisions"), multbc.multFT0COuter() * scale.factorFT0C); + histos.fill(HIST("hFIT_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + histos.fill(HIST("hFIT_FullV0_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + histos.fill(HIST("hFIT_All_Collisions"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); if (studies.do2DPlots) { histos.fill(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); } @@ -1192,6 +1216,7 @@ struct CentralityStudy { if (studies.doRunByRunHistograms) { getHist(histPath + "hInteractionRate")->Fill(interactionRate); getHist(histPath + "hFT0AOuter_Collisions")->Fill(multbc.multFT0AOuter() * scale.factorFT0A); + getHist(histPath + "hFV0AOuter_Collisions")->Fill(multbc.multFV0AOuter() * scale.factorFV0A); getHist(histPath + "hFT0MOuterA_Collisions")->Fill(multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); if (studies.do2DPlots) { getHist(histPath + "hFT0AOuterVsFT0C")->Fill(multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); @@ -1348,13 +1373,18 @@ struct CentralityStudy { // if we got here, we also finally fill the FT0C histogram, please histos.fill(HIST("hFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C); + histos.fill(HIST("hFT0COuter_BCs"), multbc.multFT0COuter() * scale.factorFT0C); histos.fill(HIST("hFT0A_BCs"), multbc.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFT0AOuter_BCs"), multbc.multFT0AOuter() * scale.factorFT0A); + histos.fill(HIST("hFV0AOuter_BCs"), multbc.multFV0AOuter() * scale.factorFV0A); histos.fill(HIST("hFT0M_BCs"), (multbc.multFT0A() + multbc.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFT0MOuterA_BCs"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFV0A_BCs"), multbc.multFV0A() * scale.factorFV0A); - histos.fill(HIST("hFV0AT0C_BCs"), (multbc.multFV0A() + multbc.multFT0C()) * scale.factorFV0AT0C); - + histos.fill(HIST("hFV0AT0C_BCs"), (multbc.multFV0A() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFV0AOuterT0C_BCs"), (multbc.multFV0AOuter() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFIT_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + histos.fill(HIST("hFIT_FullV0_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + histos.fill(HIST("hFIT_All_BCs"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); if (studies.do2DPlots) { histos.fill(HIST("hFT0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFV0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFV0A() * scale.factorFV0A); @@ -1384,9 +1414,9 @@ struct CentralityStudy { continue; } - const float selfNormFV0A = scale.normFT0A * multbc.multFV0A() / histos.get(HIST("hFT0A_BCs"))->GetMean(); - const float selfNormFT0A = scale.normFV0A * multbc.multFT0A() / histos.get(HIST("hFT0C_BCs"))->GetMean(); - const float selfNormFT0C = scale.normFT0C * multbc.multFT0C() / histos.get(HIST("hFV0A_BCs"))->GetMean(); + const float selfNormFV0A = scale.normFV0A * multbc.multFV0A() / histos.get(HIST("hFV0A_BCs"))->GetMean(); + const float selfNormFT0A = scale.normFT0A * multbc.multFT0A() / histos.get(HIST("hFT0A_BCs"))->GetMean(); + const float selfNormFT0C = scale.normFT0C * multbc.multFT0C() / histos.get(HIST("hFT0C_BCs"))->GetMean(); histos.fill(HIST("hScaledFT0M_BCs"), selfNormFT0A + selfNormFT0C); histos.fill(HIST("hScaledFV0AT0C_BCs"), selfNormFV0A + selfNormFT0C); } From d7617af9bdfdcd78f035334608516bcb500b7faa Mon Sep 17 00:00:00 2001 From: Cristian Andrei Date: Mon, 5 Oct 2026 14:20:01 +0300 Subject: [PATCH 177/648] [PWGLF] eventShapeCoex: Add forward/backward sub-event producer (#18086) --- PWGLF/DataModel/LFEventTopologyTables.h | 120 +++++++++ PWGLF/TableProducer/CMakeLists.txt | 1 + .../GlobalEventProperties/CMakeLists.txt | 15 ++ .../GlobalEventProperties/eventShapeCoex.cxx | 247 ++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 PWGLF/DataModel/LFEventTopologyTables.h create mode 100644 PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt create mode 100644 PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx diff --git a/PWGLF/DataModel/LFEventTopologyTables.h b/PWGLF/DataModel/LFEventTopologyTables.h new file mode 100644 index 00000000000..a42baa8c94d --- /dev/null +++ b/PWGLF/DataModel/LFEventTopologyTables.h @@ -0,0 +1,120 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LFEventTopologyTables.h +/// \brief Per-collision forward/backward sub-event tables, reconstructed and generated level. +/// \author Cristian Andrei + +#ifndef PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ +#define PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ + +#include + +#include + +namespace o2::aod +{ + +namespace evshapecoex +{ +DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! source collision global index (DF-local; not an index column) +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); //! run number +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! global bunch crossing +DECLARE_SOA_COLUMN(PosZ, posZ, float); //! primary-vertex z (cm) +DECLARE_SOA_COLUMN(MultFT0A, multFT0A, float); //! FT0-A amplitude +DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); //! FT0-C amplitude +DECLARE_SOA_COLUMN(NF, nF, uint16_t); //! track count, forward sub-event +DECLARE_SOA_COLUMN(NB, nB, uint16_t); //! track count, backward sub-event +DECLARE_SOA_COLUMN(NGap, nGap, uint16_t); //! track count, central gap +DECLARE_SOA_COLUMN(AF, aF, float); //! mean pT, forward (GeV/c); NaN if empty +DECLARE_SOA_COLUMN(AB, aB, float); //! mean pT, backward (GeV/c); NaN if empty +DECLARE_SOA_COLUMN(SumPt2F, sumPt2F, float); //! sum of pT^2, forward (GeV^2/c^2) +DECLARE_SOA_COLUMN(SumPt2B, sumPt2B, float); //! sum of pT^2, backward (GeV^2/c^2) +DECLARE_SOA_COLUMN(QxF, qxF, float); //! sum of cos(2 phi), forward +DECLARE_SOA_COLUMN(QyF, qyF, float); //! sum of sin(2 phi), forward +DECLARE_SOA_COLUMN(QxB, qxB, float); //! sum of cos(2 phi), backward +DECLARE_SOA_COLUMN(QyB, qyB, float); //! sum of sin(2 phi), backward +DECLARE_SOA_COLUMN(Qx4F, qx4F, float); //! sum of cos(4 phi), forward +DECLARE_SOA_COLUMN(Qy4F, qy4F, float); //! sum of sin(4 phi), forward +DECLARE_SOA_COLUMN(Qx4B, qx4B, float); //! sum of cos(4 phi), backward +DECLARE_SOA_COLUMN(Qy4B, qy4B, float); //! sum of sin(4 phi), backward +DECLARE_SOA_COLUMN(NPlus, nPlus, uint16_t); //! positive tracks, forward + backward +DECLARE_SOA_COLUMN(NMinus, nMinus, uint16_t); //! negative tracks, forward + backward +DECLARE_SOA_COLUMN(QaBits, qaBits, uint16_t); //! event-selection bits, order as QaBitOrder in eventShapeCoex.cxx +DECLARE_SOA_COLUMN(OccTracks, occTracks, int); //! track occupancy in time range +DECLARE_SOA_COLUMN(OccFT0C, occFT0C, float); //! FT0C occupancy in time range +DECLARE_SOA_COLUMN(NumContrib, numContrib, uint16_t); //! number of PV contributors +DECLARE_SOA_COLUMN(CollTimeRes, collTimeRes, float); //! collision time resolution (ns) +DECLARE_SOA_COLUMN(NPVC, nPVC, uint16_t); //! PV contributors among the selected tracks +} // namespace evshapecoex + +DECLARE_SOA_TABLE(EvShapeCoex, "AOD", "EVSHAPECOEX", //! per-collision forward/backward sub-events, reconstructed level + o2::soa::Index<>, + evshapecoex::CollisionId, evshapecoex::RunNumber, evshapecoex::GlobalBC, evshapecoex::PosZ, + evshapecoex::MultFT0A, evshapecoex::MultFT0C, + evshapecoex::NF, evshapecoex::NB, evshapecoex::NGap, + evshapecoex::AF, evshapecoex::AB, evshapecoex::SumPt2F, evshapecoex::SumPt2B, + evshapecoex::QxF, evshapecoex::QyF, evshapecoex::QxB, evshapecoex::QyB, + evshapecoex::Qx4F, evshapecoex::Qy4F, evshapecoex::Qx4B, evshapecoex::Qy4B, + evshapecoex::NPlus, evshapecoex::NMinus, + evshapecoex::QaBits, evshapecoex::OccTracks, evshapecoex::OccFT0C, + evshapecoex::NumContrib, evshapecoex::CollTimeRes, evshapecoex::NPVC); +using EvShapeCoexRow = EvShapeCoex::iterator; + +// Generator level: charged physical primaries, one row per McCollision (no event selection). +namespace evshapecoexgen +{ +DECLARE_SOA_COLUMN(McCollisionId, mcCollisionId, int); //! source McCollision global index (DF-local; not an index column) +DECLARE_SOA_COLUMN(PosZ, posZ, float); //! generated primary-vertex z (cm) +DECLARE_SOA_COLUMN(NFwdA, nFwdA, uint16_t); //! charged primaries in the FT0-A acceptance +DECLARE_SOA_COLUMN(NFwdC, nFwdC, uint16_t); //! charged primaries in the FT0-C acceptance +DECLARE_SOA_COLUMN(NF, nF, uint16_t); //! particle count, forward sub-event +DECLARE_SOA_COLUMN(NB, nB, uint16_t); //! particle count, backward sub-event +DECLARE_SOA_COLUMN(NGap, nGap, uint16_t); //! particle count, central gap +DECLARE_SOA_COLUMN(AF, aF, float); //! mean pT, forward (GeV/c); NaN if empty +DECLARE_SOA_COLUMN(AB, aB, float); //! mean pT, backward (GeV/c); NaN if empty +DECLARE_SOA_COLUMN(SumPt2F, sumPt2F, float); //! sum of pT^2, forward (GeV^2/c^2) +DECLARE_SOA_COLUMN(SumPt2B, sumPt2B, float); //! sum of pT^2, backward (GeV^2/c^2) +DECLARE_SOA_COLUMN(QxF, qxF, float); //! sum of cos(2 phi), forward +DECLARE_SOA_COLUMN(QyF, qyF, float); //! sum of sin(2 phi), forward +DECLARE_SOA_COLUMN(QxB, qxB, float); //! sum of cos(2 phi), backward +DECLARE_SOA_COLUMN(QyB, qyB, float); //! sum of sin(2 phi), backward +DECLARE_SOA_COLUMN(Qx4F, qx4F, float); //! sum of cos(4 phi), forward +DECLARE_SOA_COLUMN(Qy4F, qy4F, float); //! sum of sin(4 phi), forward +DECLARE_SOA_COLUMN(Qx4B, qx4B, float); //! sum of cos(4 phi), backward +DECLARE_SOA_COLUMN(Qy4B, qy4B, float); //! sum of sin(4 phi), backward +DECLARE_SOA_COLUMN(NPlus, nPlus, uint16_t); //! positive particles, forward + backward +DECLARE_SOA_COLUMN(NMinus, nMinus, uint16_t); //! negative particles, forward + backward +} // namespace evshapecoexgen + +DECLARE_SOA_TABLE(EvShapeCoexGen, "AOD", "EVSHAPECOEXGEN", //! per-McCollision forward/backward sub-events, generator level + o2::soa::Index<>, + evshapecoexgen::McCollisionId, evshapecoexgen::PosZ, + evshapecoexgen::NFwdA, evshapecoexgen::NFwdC, + evshapecoexgen::NF, evshapecoexgen::NB, evshapecoexgen::NGap, + evshapecoexgen::AF, evshapecoexgen::AB, evshapecoexgen::SumPt2F, evshapecoexgen::SumPt2B, + evshapecoexgen::QxF, evshapecoexgen::QyF, evshapecoexgen::QxB, evshapecoexgen::QyB, + evshapecoexgen::Qx4F, evshapecoexgen::Qy4F, evshapecoexgen::Qx4B, evshapecoexgen::Qy4B, + evshapecoexgen::NPlus, evshapecoexgen::NMinus); +using EvShapeCoexGenRow = EvShapeCoexGen::iterator; + +namespace evshapecoexmclabel +{ +DECLARE_SOA_INDEX_COLUMN_FULL(EvShapeCoexGen, evShapeCoexGen, int, EvShapeCoexGen, ""); //! matching EvShapeCoexGen row; negative if unlabelled +} // namespace evshapecoexmclabel + +DECLARE_SOA_TABLE(EvShapeCoexMcLabels, "AOD", "EVSHAPECOEXLBL", //! MC label, joinable with EvShapeCoex + o2::soa::Index<>, evshapecoexmclabel::EvShapeCoexGenId); +using EvShapeCoexMcLabel = EvShapeCoexMcLabels::iterator; + +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ diff --git a/PWGLF/TableProducer/CMakeLists.txt b/PWGLF/TableProducer/CMakeLists.txt index ec21b553563..745d984c81d 100644 --- a/PWGLF/TableProducer/CMakeLists.txt +++ b/PWGLF/TableProducer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(QC) add_subdirectory(Common) +add_subdirectory(GlobalEventProperties) add_subdirectory(Nuspex) add_subdirectory(Strangeness) add_subdirectory(Resonances) diff --git a/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt new file mode 100644 index 00000000000..6161ead0a4b --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(event-shape-coex + SOURCES eventShapeCoex.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx new file mode 100644 index 00000000000..1dfd790903e --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx @@ -0,0 +1,247 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file eventShapeCoex.cxx +/// \brief Per-collision forward/backward sub-event quantities of the mid-rapidity tracks. +/// \author Cristian Andrei +/// +/// First O2Physics task of the AliPhysics PWGLF/SPECTRA/MultEvShape analyses. + +#include "PWGLF/DataModel/LFEventTopologyTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +/// Sums over one sub-event, shared by the reconstructed and generated levels. +struct SubEventSums { + uint32_t n = 0; + double sumPt = 0.; + double sumPt2 = 0.; + double qx2 = 0., qy2 = 0.; + double qx4 = 0., qy4 = 0.; + + void add(double pt, float phi) + { + // phi stays float, as stored in the AOD + const double c = std::cos(phi); + const double s = std::sin(phi); + const double cos2 = c * c - s * s; + const double sin2 = 2. * s * c; + ++n; + sumPt += pt; + sumPt2 += pt * pt; + qx2 += cos2; + qy2 += sin2; + qx4 += cos2 * cos2 - sin2 * sin2; + qy4 += 2. * sin2 * cos2; + } + + [[nodiscard]] float meanPt() const { return (n > 0) ? static_cast(sumPt / n) : std::nanf(""); } +}; +} // namespace + +struct EventShapeCoex { + Produces coex; + Produces coexGen; + Produces coexMcLabels; + + Service pdg{}; + + Configurable vtxZCut{"vtxZCut", 10.0f, "Max |PV z| (cm)"}; + Configurable ptMin{"ptMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable ptMax{"ptMax", 2.0f, "Maximum track pT (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Max |eta| (mid-rapidity measurement region)"}; + Configurable gapHalf{"gapHalf", 0.2f, "Central-gap half-width: |eta| <= gapHalf excluded from F/B"}; + Configurable etaFwdAMin{"etaFwdAMin", 3.5f, "FT0-A proxy: lower eta edge (generator level)"}; + Configurable etaFwdAMax{"etaFwdAMax", 4.9f, "FT0-A proxy: upper eta edge (generator level)"}; + Configurable etaFwdCMin{"etaFwdCMin", -3.3f, "FT0-C proxy: lower eta edge (generator level)"}; + Configurable etaFwdCMax{"etaFwdCMax", -2.1f, "FT0-C proxy: upper eta edge (generator level)"}; + + Filter trackFilter = (aod::track::pt > ptMin) && (aod::track::pt < ptMax) && (nabs(aod::track::eta) < etaMax) && requireGlobalTrackInFilter(); + + using MyCollisions = soa::Join; + using MyCollisionsMc = soa::Join; + using MyTracks = soa::Filtered>; + + void init(InitContext const&) + { + if (doprocessReco && doprocessRecoMC) { + LOGF(fatal, "processReco and processRecoMC both fill EvShapeCoex; enable only one of them."); + } + if (doprocessRecoMC && !doprocessMC) { + LOGF(fatal, "processRecoMC links to EvShapeCoexGen rows and requires processMC in the same job."); + } + } + + /// Fills one EvShapeCoex row; returns false if the collision is rejected. + template + bool fillReco(TCollision const& coll, MyTracks const& tracks) + { + if (!coll.sel8() || std::abs(coll.posZ()) > vtxZCut) { + return false; + } + + const float g = gapHalf; + SubEventSums fwd, bwd; + uint32_t nGap = 0, nPlus = 0, nMinus = 0, nPVC = 0; + for (const auto& track : tracks) { + if (track.isPVContributor()) { + ++nPVC; + } + const float eta = track.eta(); + if (eta > g) { + fwd.add(track.pt(), track.phi()); + } else if (eta < -g) { + bwd.add(track.pt(), track.phi()); + } else { + ++nGap; + continue; + } + if (track.sign() > 0) { + ++nPlus; + } else if (track.sign() < 0) { + ++nMinus; + } + } + + // bit i of qaBits = QaBitOrder[i] + static constexpr int NQaBits = 12; + static constexpr std::array QaBitOrder = { + o2::aod::evsel::kNoSameBunchPileup, o2::aod::evsel::kIsGoodZvtxFT0vsPV, + o2::aod::evsel::kIsVertexITSTPC, o2::aod::evsel::kIsVertexTOFmatched, + o2::aod::evsel::kNoCollInTimeRangeNarrow, o2::aod::evsel::kNoCollInTimeRangeStrict, + o2::aod::evsel::kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInRofStrict, + o2::aod::evsel::kNoCollInRofStandard, o2::aod::evsel::kNoHighMultCollInPrevRof, + o2::aod::evsel::kNoITSROFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}; + uint16_t qaBits = 0; + for (int i = 0; i < NQaBits; ++i) { + if (coll.selection_bit(QaBitOrder[i])) { + qaBits |= static_cast(1u << i); + } + } + + const auto& bc = coll.template bc_as(); + + coex(static_cast(coll.globalIndex()), bc.runNumber(), bc.globalBC(), coll.posZ(), + coll.multFT0A(), coll.multFT0C(), + static_cast(fwd.n), static_cast(bwd.n), static_cast(nGap), + fwd.meanPt(), bwd.meanPt(), static_cast(fwd.sumPt2), static_cast(bwd.sumPt2), + static_cast(fwd.qx2), static_cast(fwd.qy2), + static_cast(bwd.qx2), static_cast(bwd.qy2), + static_cast(fwd.qx4), static_cast(fwd.qy4), + static_cast(bwd.qx4), static_cast(bwd.qy4), + static_cast(nPlus), static_cast(nMinus), + qaBits, coll.trackOccupancyInTimeRange(), coll.ft0cOccupancyInTimeRange(), + coll.numContrib(), coll.collisionTimeRes(), static_cast(nPVC)); + return true; + } + + void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCs const&) + { + fillReco(coll, tracks); + } + PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level reduction (EvShapeCoex)", true); + + void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::BCs const&) + { + if (fillReco(coll, tracks)) { + // EvShapeCoexGen has one row per McCollision, in order: the McCollision index is its row index + coexMcLabels(coll.mcCollisionId()); + } + } + PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level reduction with MC labels (EvShapeCoex + EvShapeCoexMcLabels)", false); + + void processMC(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { + static constexpr double ChargeTolerance = 1.e-3; + + const float g = gapHalf; + const float em = etaMax; + SubEventSums fwd, bwd; + uint32_t nFwdA = 0, nFwdC = 0, nGap = 0, nPlus = 0, nMinus = 0; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle == nullptr) { + continue; // unknown PDG code, charge undetermined + } + const double charge = pdgParticle->Charge(); + if (std::abs(charge) < ChargeTolerance) { + continue; + } + const float eta = particle.eta(); + // forward counts: no pT window + if (eta > etaFwdAMin && eta < etaFwdAMax) { + ++nFwdA; + } + if (eta > etaFwdCMin && eta < etaFwdCMax) { + ++nFwdC; + } + const float pt = particle.pt(); + if (pt <= ptMin || pt >= ptMax || std::abs(eta) >= em) { + continue; + } + if (eta > g) { + fwd.add(pt, particle.phi()); + } else if (eta < -g) { + bwd.add(pt, particle.phi()); + } else { + ++nGap; + continue; + } + if (charge > 0.) { + ++nPlus; + } else { + ++nMinus; + } + } + + coexGen(static_cast(mcCollision.globalIndex()), mcCollision.posZ(), + static_cast(nFwdA), static_cast(nFwdC), + static_cast(fwd.n), static_cast(bwd.n), static_cast(nGap), + fwd.meanPt(), bwd.meanPt(), static_cast(fwd.sumPt2), static_cast(bwd.sumPt2), + static_cast(fwd.qx2), static_cast(fwd.qy2), + static_cast(bwd.qx2), static_cast(bwd.qy2), + static_cast(fwd.qx4), static_cast(fwd.qy4), + static_cast(bwd.qx4), static_cast(bwd.qy4), + static_cast(nPlus), static_cast(nMinus)); + } + PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level reduction (EvShapeCoexGen)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From cebe65753a2df9aa0963458e1b9dce78a4bfcee0 Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:11:01 +0200 Subject: [PATCH 178/648] [PWGCF] Fix code-check errors in femto framework (#18171) --- PWGCF/Femto/Core/baseSelection.h | 2 +- PWGCF/Femto/Core/photonBuilder.h | 1 - PWGCF/Femto/Core/photonHistManager.h | 1 + PWGCF/Femto/Tasks/femtoPhotonQa.cxx | 2 -- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 30fe70231e5..2cd6071f36a 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -377,7 +377,7 @@ class BaseSelection /// \brief Print the full configuration of all selections to the log. /// \param objectName Name of the object owning this selection (used as label in the log output). - void printSelections(const std::string& objectName) const + void printSelections(const char* objectName) const { LOG(info) << "Printing Configuration of " << objectName; for (size_t idx = 0; idx < mSelectionContainers.size(); ++idx) { diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h index 38943d09c27..d64f86c56d6 100644 --- a/PWGCF/Femto/Core/photonBuilder.h +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -40,7 +40,6 @@ #include #include -#include #include #include #include diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h index 8c010060eb8..23b8688dfa5 100644 --- a/PWGCF/Femto/Core/photonHistManager.h +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx index ad6adbbeebf..2e074fb1ff3 100644 --- a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx +++ b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx @@ -27,12 +27,10 @@ #include #include #include -#include #include #include #include -#include #include using namespace o2::analysis::femto; From a450be186183ddcde8c82aaa15f0c3f7e45fb98a Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Mon, 5 Oct 2026 15:41:47 +0300 Subject: [PATCH 179/648] [PWGCF] Remove local multiplicity sampling (#18133) Co-authored-by: Iulian --- .../Tasks/FoxWolframCorrelation.cxx | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index cdef44721e6..04d3f8461c1 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -42,7 +42,6 @@ #include -#include #include #include #include @@ -192,7 +191,6 @@ struct FoxWolframCorrelation { Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; Configurable etaRange{"etaRange", 0.8f, "eta range to consider"}; Configurable dcaZ{"dcaZ", 0.2f, "custom DCA Z cut (ignored if negative)"}; - Configurable doMultipSampling{"doMultipSampling", false, "doMultipSampling"}; Configurable doSameEvent{"doSameEvent", true, "Fill same-event TRI correlations"}; Configurable doFWMCorrelations{"doFWMCorrelations", false, "Fill H_i-H_j correlation histograms for RANGE1-RANGE3"}; @@ -248,45 +246,6 @@ struct FoxWolframCorrelation { Configurable noOfMultipBins{"noOfMultipBins", 10, "noOfMultipBins"}; - // Provide reference multiplicity weights for optional sampling. - std::array limMultip{ - 7058, 43307, 85345, 110727, 118857, 118652, 115912, 112011, 109254, 106044, - 102421, 100599, 97952, 95908, 93883, 91890, 90264, 88097, 86388, 84582, - 82542, 81212, 79240, 77903, 75562, 74099, 71973, 70777, 69100, 67880, - 66162, 64827, 63513, 62101, 61110, 59307, 58419, 57719, 56344, 55228, - 54615, 53847, 52387, 51925, 51560, 50217, 49962, 48766, 48005, 47651, - 46758, 46046, 45531, 45063, 44271, 43530, 43151, 42489, 42347, 41576, - 40991, 40166, 39959, 39494, 39367, 38830, 38225, 37781, 37435, 36934, - 36501, 35980, 35846, 35312, 34926, 34640, 33693, 33721, 33296, 32570, - 32272, 32122, 31690, 31390, 31125, 30532, 29855, 29623, 29486, 29032, - 28534, 28140, 27491, 27430, 26760, 26601, 26217, 25625, 25464, 25067, - 24395, 24271, 23722, 23240, 22953, 22474, 22166, 21621, 21312, 20935, - 20444, 20157, 19714, 19077, 18774, 18284, 18005, 17391, 16963, 16864, - 16184, 15722, 15456, 14956, 14386, 14248, 13853, 13419, 13132, 12602, - 12192, 11667, 11397, 10967, 10701, 10316, 9831, 9758, 9371, 8905, - 8759, 8255, 7992, 7603, 7363, 7150, 6816, 6477, 6168, 5940, - 5638, 5502, 5154, 5014, 4686, 4434, 4263, 3998, 3959, 3623, - 3479, 3323, 3196, 3005, 2923, 2770, 2607, 2354, 2226, 2187, - 2058, 1980, 1794, 1739, 1601, 1515, 1419, 1321, 1292, 1175, - 1144, 1048, 995, 909, 860, 783, 783, 745, 680, 571, - 566, 524, 533, 462, 444, 427, 367, 320, 303, 317, - 270, 250, 227, 221, 207, 193, 188, 163, 131, 151, - 139, 104, 105, 70, 94, 83, 68, 62, 85, 70, - 46, 38, 43, 47, 39, 33, 38, 23, 28, 27, - 24, 17, 24, 17, 14, 27, 22, 14, 11, 11, - 10, 11, 7, 6, 5, 6, 6, 7, 2, 5, - 6, 4, 2, 5, 3, 2, 3, 1, 0, 0, - 2, 0, 1, 3, 2, 0, 1, 3, 1, 1, - 2, 0, 1, 1, 0, 0, 0, 1, 2, 0, - 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - std::array countMultip{}; - static constexpr std::array binNames = { "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; @@ -777,16 +736,6 @@ struct FoxWolframCorrelation { (topologyMode.value == kTriEvents && isTRI) || (topologyMode.value == kIsoEvents && isISO); - // Optionally cap the sampled events at each multiplicity. - int bin = std::clamp(nGlobalTracks, 0, 499); - if (doMultipSampling) { - if (countMultip[bin] >= limMultip[bin]) { - return; - } - - ++countMultip[bin]; - } - // Fill inclusive event multiplicity and recoil. histos.fill(HIST("Multip/ITSTPC_Multiplicity"), nGlobalTracks); histos.fill(HIST("pTSpectra/RecoilALL"), nGlobalTracks, nRecoil); From cd20e0a5f4cbe5d20534eafc26ac8bacd0124df8 Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Mon, 5 Oct 2026 15:31:24 +0200 Subject: [PATCH 180/648] [PWGLF] Nuclei hadron correlation: fix purity histograms and add rejection of protons (#18173) --- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 0bd761901d2..f4b1ef4083c 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -95,6 +95,7 @@ struct HadronNucleiCorrelation { Configurable isPrim{"isPrim", true, "is isPrim"}; Configurable doCorrection{"doCorrection", false, "do efficiency correction"}; Configurable doQuadraticPID{"doQuadraticPID", false, "do PID with sum in quadrature of TOF and TPC"}; + Configurable doPIDRej{"doPIDRej", false, "do PID rejection of competing species"}; struct : ConfigurableGroup { std::string prefix = "Coalescence"; // JSON group name @@ -130,12 +131,13 @@ struct HadronNucleiCorrelation { Configurable nsigmaElPr{"nsigmaElPr", 1.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaElDe{"nsigmaElDe", 3.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaTOF{"nsigmaTOF", 3.5f, "cut nsigma TOF"}; + Configurable nsigmaTOFPrRej{"nsigmaTOFPrRej", 3.f, "cut nsigma TOF for proton rejection"}; + Configurable doPrRej{"doPrRej", true, "do TOF proton rejection"}; Configurable nsigmaQuadratic{"nsigmaQuadratic", 3.0f, "cut on sqrt(nsigmaTPC^2 + nsigmaTOF^2), used above the TOF pT threshold when doQuadraticPID is on"}; Configurable nsigmaITSPr{"nsigmaITSPr", -2.0f, "cut nsigma ITS Pr"}; Configurable nsigmaITSDe{"nsigmaITSDe", -2.0f, "cut nsigma ITS De"}; Configurable doITSPID{"doITSPID", true, "do ITS PID"}; Configurable pTthrprTOF{"pTthrprTOF", 0.8f, "threshold pT proton to use TOF"}; - Configurable pTthrprTPCEl{"pTthrprTPCEl", 1.0f, "threshold pT proton to use TPC El rejection"}; Configurable pTthrdeTOF{"pTthrdeTOF", 1.0f, "threshold pT deuteron to use TOF"}; Configurable pTthrdeTPCEl{"pTthrdeTPCEl", 1.0f, "threshold pT deuteron to use TPC El rejection"}; Configurable rejectionEl{"rejectionEl", true, "use TPC El rejection"}; @@ -407,6 +409,8 @@ struct HadronNucleiCorrelation { registryQa.add("QA/h3dTPCTOF_AntiPr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/h3dTPCTOF_De", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/h3dTPCTOF_AntiDe", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_De_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiDe_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_El", "n#sigma TPC vs p_{T} for e hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_Pr", "n#sigma TPC vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); registryQa.add("QA/hnSigmaTPCVsPt_De", "n#sigma TPC vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); @@ -581,7 +585,6 @@ struct HadronNucleiCorrelation { { const bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC.value; const bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF.value; - const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrprTPCEl.value && track.tpcNSigmaEl() >= nsigmaElPr.value; const bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()) < nsigmaQuadratic.value; @@ -594,7 +597,7 @@ struct HadronNucleiCorrelation { if (!doITSPID.value || isITSPID) { return signCheck; } - } else if (isTPCElRejection || isTOFPID) { + } else if (isTOFPID) { return signCheck; } } @@ -619,11 +622,15 @@ struct HadronNucleiCorrelation { const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; + const bool isNotPr = std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value || !doPrRej.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; // Check if the sign of the track matches the expected sign for deuterons or antideuterons const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); + if (!isNotPr) { + return false; + } if (!doQuadraticPID.value) { if (isTPCPID) { if (track.pt() < pTthrdeTOF.value) { @@ -1021,12 +1028,23 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); registryQa.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); + + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; + const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; + const bool isNotPr = std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value || !doPrRej.value; + if (track.sign() > 0) { + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { - registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isNotPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTPCElRejection || isTOFPID) { + registryQa.fill(HIST("QA/h3dTPCTOF_De_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } } } if (track.sign() < 0) { @@ -1034,7 +1052,12 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { - registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isNotPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTPCElRejection || isTOFPID) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } } } From 8a9a9dcf3c853cbb7c2a4191cc652d8992be94a7 Mon Sep 17 00:00:00 2001 From: Hirak Koley Date: Mon, 5 Oct 2026 19:23:54 +0530 Subject: [PATCH 181/648] [PWGLF] Refactor track selection and update rapidity cuts, commented long LOGF entries (#18172) --- .../hResonanceCorrelationFilter.cxx | 128 ++++--- .../Resonances/hResonanceCorrelation.cxx | 316 +++++++++--------- 2 files changed, 243 insertions(+), 201 deletions(-) diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx index f35fa0fd078..bca62f39f87 100644 --- a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -83,6 +83,11 @@ struct HResonanceCorrelationFilter { Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + // Centrality source switch for hCentralitySelected: O-O collisions have a + // well-defined FT0C centrality estimator; p-O (asymmetric system) does + // not, so FV0A is used there instead. Only affects which estimator is + // read for that one QA histogram -- doesn't change any selection cut. + Configurable cfgUseFV0ACentrality{"cfgUseFV0ACentrality", false, "Centrality estimator for hCentralitySelected: false = O-O (FT0C), true = p-O (FV0A)"}; } configEvents; // Merged from the formerly separate configTracks/generalSelections/ @@ -90,13 +95,19 @@ struct HResonanceCorrelationFilter { // track pair", just split by who last touched them. No member names // collided, so every Configurable below keeps its original key; only the // "." prefix at each call site changed, to configTracks. + // General, species-agnostic track-quality cuts: applied identically to + // every track that reaches trackCut() -- trigger, associated hadron, AND + // Phi/K*0 daughter candidates alike (see trackCut() below, and its call + // sites at isValidTrigger(), isValidAssocTrack(), and both + // processPhis()/processKstars()). cfgCutEta/cMinPtcut additionally gate + // the framework-level `acceptanceFilter` Filter, applied to every track + // in every process function before any of the above ever sees it. struct : ConfigurableGroup { std::string prefix = "configTracks"; // Pre-selection Track cuts Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; - Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "max TPC chi2/clusters for good track"}; Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "max ITS chi2/clusters for good track"}; @@ -115,30 +126,49 @@ struct HResonanceCorrelationFilter { Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; - // Associated particle selections in phase space (formerly generalSelections) - Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; - Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; - Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; - Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; + // Track quality shared by trigger AND assoc selection (isValidTrigger() + // and isValidAssocTrack() both read this same configurable -- + // it is NOT split into trigger-/assoc-only copies below). + Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; - // Trigger particle selections in phase space (formerly generalSelections) + // primary particle DCAxy selections (formerly trackSelections) + // formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + } configTracks; + + // Phi/K*0 resonance-candidate cuts: applied to the RECONSTRUCTED candidate + // (the K+K- or K-pi 4-vector), not to the daughter tracks themselves -- + // the daughters go through the shared configTracks/trackCut() cuts above + // like every other track. Currently holds just the one configurable; kept + // as its own group rather than folded into configTracks so it reads as + // "resonance-level", not "track-level". + struct : ConfigurableGroup { + std::string prefix = "configResoDauTracks"; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for the reconstructed Phi/K*0 candidate"}; + } configResoDauTracks; + + // Trigger-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; - Configurable triggerPtCutMin{"triggerPtCutMin", 3, "triggerptmin"}; + Configurable triggerPtCutMin{"triggerPtCutMin", 2, "triggerptmin"}; Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; - - // Track quality (formerly trackSelections) - Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; - Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + } configTriggerTracks; - // primary particle DCAxy selections (formerly trackSelections) - // formula: |DCAxy| < 0.004f + (0.013f / pt) - Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - } configTracks; + // Associated-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; + Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; + Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; + Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; + } configAssocTracks; struct : ConfigurableGroup { std::string prefix = "configPID"; @@ -181,15 +211,6 @@ struct HResonanceCorrelationFilter { Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; } configPID; - // Unused in this file today -- nothing here reads from CCDB any more - // (Zorro was the only consumer and has been removed). Kept (grouped, - // rather than removed) since that's outside today's ask. - struct : ConfigurableGroup { - std::string prefix = "ccdbConfigurations"; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; - } ccdbConfigurations; - // must include windows for background and peak // // This filter-level cut is an acceptance pre-cut only -- it decides what @@ -238,6 +259,8 @@ struct HResonanceCorrelationFilter { ConfigurableAxis axisDCAxyQA{"axisDCAxyQA", {200, -0.5f, 0.5f}, "DCA_{xy} (cm)"}; ConfigurableAxis axisDCAzQA{"axisDCAzQA", {200, -0.5f, 0.5f}, "DCA_{z} (cm)"}; ConfigurableAxis axisTPCCrossedRowsQA{"axisTPCCrossedRowsQA", {160, 0, 160}, "TPC crossed rows"}; + ConfigurableAxis axisVertexZQA{"axisVertexZQA", {60, -15.0f, 15.0f}, "Vertex Z (cm)"}; + ConfigurableAxis axisCentralityQA{"axisCentralityQA", {100, 0.0f, 100.0f}, "Centrality (%)"}; } axesConfigurations; // QA @@ -292,6 +315,12 @@ struct HResonanceCorrelationFilter { void init(InitContext const&) { histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + // Z-vertex and centrality of selected events (filled in isSelectedEvents() + // once a collision passes every cut, i.e. the same "All Passed Events" + // point as CollCutCounts bin 8). Centrality source for the latter is + // configEvents.cfgUseFV0ACentrality: FT0C for O-O, FV0A for p-O. + histos.add("hVertexZSelected", "Vertex Z of selected events", kTH1F, {axesConfigurations.axisVertexZQA}); + histos.add("hCentralitySelected", "Centrality of selected events", kTH1F, {axesConfigurations.axisCentralityQA}); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); @@ -326,6 +355,8 @@ struct HResonanceCorrelationFilter { histos.add((dir + "/hPtVsDCAz").c_str(), "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); histos.add((dir + "/hPtVsTPCNSigma").c_str(), "p_{T} vs TPC n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); histos.add((dir + "/hPtVsTOFNSigma").c_str(), "p_{T} vs TOF n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + // TPC vs TOF nSigma correlation, for the usual PID "banana plot" check + histos.add((dir + "/hTPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma", kTH2F, {axesConfigurations.axisNSigmaQA, axesConfigurations.axisNSigmaQA}); }; bookDaughterQA("QA/Phi/Kaon"); bookDaughterQA("QA/Kstar/Kaon"); @@ -360,6 +391,9 @@ struct HResonanceCorrelationFilter { histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); histos.fill(HIST("QA/Phi/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); histos.fill(HIST("QA/Phi/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } } template @@ -376,6 +410,9 @@ struct HResonanceCorrelationFilter { histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); histos.fill(HIST("QA/Kstar/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); histos.fill(HIST("QA/Kstar/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } } template @@ -392,6 +429,9 @@ struct HResonanceCorrelationFilter { histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAz"), track.pt(), track.dcaZ()); histos.fill(HIST("QA/Kstar/Pion/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaPi()); histos.fill(HIST("QA/Kstar/Pion/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaPi()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } } // Trigger hadrons carry no PID selection, so only the 5 non-PID variables @@ -463,6 +503,14 @@ struct HResonanceCorrelationFilter { if (fillHist) { histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("hVertexZSelected"), collision.posZ()); + // Guarded with if constexpr: isSelectedEvents() is called with several + // different collision join types (some callers pass fillHist=false and + // don't join centrality tables at all), so this only compiles/fills for + // callers whose collision type actually has both estimators joined. + if constexpr (requires { collision.centFT0C(); collision.centFV0A(); }) { + histos.fill(HIST("hCentralitySelected"), configEvents.cfgUseFV0ACentrality ? collision.centFV0A() : collision.centFT0C()); + } } return true; @@ -692,23 +740,23 @@ struct HResonanceCorrelationFilter { if (!trackCut(track)) { return false; } - if (track.eta() > configTracks.triggerEtaMax || track.eta() < configTracks.triggerEtaMin) { + if (track.eta() > configTriggerTracks.triggerEtaMax || track.eta() < configTriggerTracks.triggerEtaMin) { return false; } // if (track.sign()= 1 ) {continue;} - if (track.pt() > configTracks.triggerPtCutMax || track.pt() < configTracks.triggerPtCutMin) { + if (track.pt() > configTriggerTracks.triggerPtCutMax || track.pt() < configTriggerTracks.triggerPtCutMin) { return false; } if (track.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!track.hasITS() && configTracks.triggerRequireITS) { + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > configTracks.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTracks.triggerRequireL0) { + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } return true; @@ -743,16 +791,16 @@ struct HResonanceCorrelationFilter { if (!trackCut(assoc)) { return false; } - if (assoc.eta() > configTracks.assocEtaMax || assoc.eta() < configTracks.assocEtaMin) { + if (assoc.eta() > configAssocTracks.assocEtaMax || assoc.eta() < configAssocTracks.assocEtaMin) { return false; } - if (assoc.pt() > configTracks.assocPtCutMax || assoc.pt() < configTracks.assocPtCutMin) { + if (assoc.pt() > configAssocTracks.assocPtCutMax || assoc.pt() < configAssocTracks.assocPtCutMin) { return false; } if (assoc.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!assoc.hasITS() && configTracks.assocRequireITS) { + if (!assoc.hasITS() && configAssocTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } @@ -855,7 +903,7 @@ struct HResonanceCorrelationFilter { } // for real data processing - void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) { triggerCandidates.clear(); if (!isSelectedEvents(collision)) { @@ -894,7 +942,7 @@ struct HResonanceCorrelationFilter { } // for MC processing - void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { triggerCandidates.clear(); if (!isSelectedEvents(collision)) { @@ -1090,7 +1138,7 @@ struct HResonanceCorrelationFilter { float invMass = phi.M(); // rapidity cut - if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1181,7 +1229,7 @@ struct HResonanceCorrelationFilter { float invMass = phi.M(); // rapidity cut - if (std::abs(phi.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1299,7 +1347,7 @@ struct HResonanceCorrelationFilter { float invMass = kstar.M(); // rapidity cut - if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } @@ -1388,7 +1436,7 @@ struct HResonanceCorrelationFilter { float invMass = kstar.M(); // rapidity cut - if (std::abs(kstar.Rapidity()) > configTracks.cfgCutRapidity) { + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { continue; } diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx index c631ee15932..ae5b8a0f061 100644 --- a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -24,8 +24,6 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -89,12 +87,6 @@ struct HResonanceCorrelation { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // event filtering - Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; - - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - struct : ConfigurableGroup { std::string prefix = "masterConfigurations"; Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; @@ -124,19 +116,40 @@ struct HResonanceCorrelation { Configurable fillCorrelationHistWithMass{"fillCorrelationHistWithMass", false, "if true, fill correlation histograms with particle mass"}; } masterConfigurations; - // master analysis switches - Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; - Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; - Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; - Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; - Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; - - Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; - Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; - Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; - Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + // Trigger-selection-mode flags: which trigger-pT bin to require (if + // processSelectEventWithTrigger is enabled), a charge cut on the trigger + // particle, and whether to use the event's leading particle as the sole + // trigger instead of every trigger candidate. + struct : ConfigurableGroup { + std::string prefix = "triggerSelection"; + Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; + Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; + Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; + } triggerSelection; + + // MC-truth physical-primary requirements, reconstructed-level and + // generator-level respectively. + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + } mcSelections; - Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; + // Histogram-booking/filling toggles, not physics cuts. + struct : ConfigurableGroup { + std::string prefix = "qaSwitches"; + Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; + Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; + } qaSwitches; + + // Generic phase-space cuts used across trigger, associated AND gen-level + // (MC-truth) particles alike -- not species- or reco/gen-specific, so not + // folded into configTriggerTracks/configAssocTracks. + struct : ConfigurableGroup { + std::string prefix = "phaseSpaceSelections"; + Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; + Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + } phaseSpaceSelections; // Reconstructed-event selection cuts -- deliberately the same cut set and // names as configEvents in hResonanceCorrelationFilter.cxx, so a collision @@ -235,41 +248,51 @@ struct HResonanceCorrelation { Configurable applyEffAsFunctionOfMult{"applyEffAsFunctionOfMult", false, "apply efficiency as a function of multiplicity as well"}; Configurable applyEffAsFunctionOfMultAndPhi{"applyEffAsFunctionOfMultAndPhi", false, "apply efficiency as a function of multiplicity and phi"}; } efficiencyFlags; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; - - // Configurables for doing subwagon systematics struct : ConfigurableGroup { - std::string prefix = "trackSelection"; - // --- Track quality variations (single track, both trigger and assoc daughters) + std::string prefix = "ccdbConfigurations"; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; + } ccdbConfigurations; + + // Configurables for doing subwagon systematics. + // Split by who they apply to (trigger hadron vs. associated particle). + // requireDCAzCut is the one genuinely shared switch (it toggles the DCAz + // cut for BOTH isValidTrigger() and the assoc selection); rather than its + // own single-entry group, it lives in `checks` below alongside the other + // small behavioral toggles. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; Configurable minTPCNCrossedRowsTrigger{"minTPCNCrossedRowsTrigger", 70, "Minimum TPC crossed rows (trigger)"}; - Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; - Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; - Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; - Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; - Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; - - // --- Trigger: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - // --- Assoc track: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) - Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - Configurable dcaZconstant{"dcaZconstant", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; Configurable dcaZpTdep{"dcaZpTdep", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + } configTriggerTracks; + + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; + Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; + Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaZconstantAssoc{"dcaZconstantAssoc", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; Configurable dcaZpTdepAssoc{"dcaZpTdepAssoc", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; - } trackSelection; + } configAssocTracks; struct : ConfigurableGroup { std::string prefix = "checks"; // on the fly correction instead of mixingParameter Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", 0, "enable an on-the-fly correction instead of using mixing"}; + Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; + Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; } checks; struct ValidCollision { @@ -369,7 +392,6 @@ struct HResonanceCorrelation { // process functions are actually enabled (bit + switch), so nothing fills them. int eventQAOwnerIndex = -1; int mRunNumber = 0; - int mRunNumberZorro = 0; std::vector> axisRanges; @@ -391,20 +413,6 @@ struct HResonanceCorrelation { return shiftedDeltaPhi; } - /// Function to load zorro - /// \param bc provided such that the run number + timestamp can be used - void initZorro(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumberZorro == bc.runNumber()) { - return; - } - - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); - zorro.populateHistRegistry(histos, bc.runNumber()); - - mRunNumberZorro = bc.runNumber(); - } - /// Function to load efficiencies to memory from CCDB /// \param bc provided such that the run number can be used void initEfficiencyFromCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -416,7 +424,7 @@ struct HResonanceCorrelation { LOG(info) << "Loading efficiencies from CCDB for run " << mRunNumber << " now..."; auto timeStamp = bc.timestamp(); - TList* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); + TList* listEfficiencies = ccdb->getForTimeStamp(ccdbConfigurations.efficiencyCCDBPath, timeStamp); if (!listEfficiencies) { LOG(fatal) << "Problem getting TList object with efficiencies!"; @@ -454,9 +462,7 @@ struct HResonanceCorrelation { void init(InitContext const&) { - zorroSummary.setObject(zorro.getZorroSummary()); mRunNumber = 0; - mRunNumberZorro = 0; hEfficiencyPion = 0x0; hEfficiencyKaon = 0x0; hEfficiencyPhi = 0x0; @@ -705,7 +711,7 @@ struct HResonanceCorrelation { bool anySameEvent = doprocessSameEventHPhis || doprocessSameEventHKstars || doprocessSameEventHPions || doprocessSameEventHKaons || doprocessSameEventHHadrons; bool anyMixedEvent = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer || doprocessMixedEventHPions || doprocessMixedEventHKaons || doprocessMixedEventHHadrons; - if (doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { + if (qaSwitches.doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { if (anySameEvent) histos.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); if (anyMixedEvent) { @@ -820,7 +826,7 @@ struct HResonanceCorrelation { } else { histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } - if (doDeltaPhiStarCheck) + if (qaSwitches.doDeltaPhiStarCheck) histos.add(fmt::format("sameEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { needsSignalBgCloneSE = true; @@ -837,7 +843,7 @@ struct HResonanceCorrelation { } else { histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } - if (doDeltaPhiStarCheck) + if (qaSwitches.doDeltaPhiStarCheck) histos.add(fmt::format("mixedEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { needsSignalBgCloneME = true; @@ -967,7 +973,7 @@ struct HResonanceCorrelation { if (doprocessPrediction) { mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = doAssocPhysicalPrimary.value; + mCounter.mSelectPrimaries = mcSelections.doAssocPhysicalPrimary.value; histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); TString eventSelLabel[] = {"Read", "INELgt0", "|Z|<10"}; for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { @@ -1014,7 +1020,7 @@ struct HResonanceCorrelation { // initialize CCDB *only* if efficiency correction requested // skip if not requested, saves a bit of time if (efficiencyFlags.applyEfficiencyCorrection) { - ccdb->setURL(ccdburl); + ccdb->setURL(ccdbConfigurations.ccdburl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); @@ -1089,18 +1095,6 @@ struct HResonanceCorrelation { return false; } - // Zorro trigger-skim accounting: this task's own feature, not part of the - // filter task's cut set (which has no skim awareness) -- opt-in via - // zorroMask, off by default, so it doesn't change the baseline cut set. - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initZorro(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return false; - } - } - if (fillHist) { histos.fill(HIST("CollCutCounts"), 8); histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); @@ -1113,36 +1107,36 @@ struct HResonanceCorrelation { template bool isValidTrigger(TTrack const& track, bool isLeading) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { + if (track.tpcNClsCrossedRows() < configTriggerTracks.minTPCNCrossedRowsTrigger) { return false; // crossed rows } - if (!track.hasITS() && trackSelection.triggerRequireITS) { + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelection.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.triggerRequireL0) { + if (!(TESTBIT(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } // systematic variations: trigger DCAxy - if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstant + trackSelection.dcaXYpTdep * std::abs(track.signed1Pt())) { + if (std::abs(track.dcaXY()) > configTriggerTracks.dcaXYconstant + configTriggerTracks.dcaXYpTdep * std::abs(track.signed1Pt())) { return false; } // systematic variations: trigger DCAz - if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstant + trackSelection.dcaZpTdep * std::abs(track.signed1Pt())) { + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configTriggerTracks.dcaZconstant + configTriggerTracks.dcaZpTdep * std::abs(track.signed1Pt())) { return false; } if (track.pt() > axisRanges[3][1] || track.pt() < axisRanges[3][0]) { return false; } - if (triggerParticleCharge > 0 && track.sign() < 0) { + if (triggerSelection.triggerParticleCharge > 0 && track.sign() < 0) { return false; } - if (triggerParticleCharge < 0 && track.sign() > 0) { + if (triggerSelection.triggerParticleCharge < 0 && track.sign() > 0) { return false; } - if (useTheLeadingParticleAsTrigger && !isLeading) { + if (triggerSelection.useTheLeadingParticleAsTrigger && !isLeading) { return false; } return true; @@ -1150,24 +1144,24 @@ struct HResonanceCorrelation { template bool isValidAssocHadron(TTrack const& track) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (track.tpcNClsCrossedRows() < configAssocTracks.minTPCNCrossedRowsAssociated) { return false; // crossed rows } - if (!track.hasITS() && trackSelection.assocRequireITS) { + if (!track.hasITS() && configAssocTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelection.assocMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configAssocTracks.assocMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelection.assocRequireL0) { + if (!(TESTBIT(track.itsClusterMap(), 0)) && configAssocTracks.assocRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } // systematic variations: trigger DCAxy - if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstantAssoc + trackSelection.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { + if (std::abs(track.dcaXY()) > configAssocTracks.dcaXYconstantAssoc + configAssocTracks.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { return false; } // systematic variations: trigger DCAz - if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstantAssoc + trackSelection.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configAssocTracks.dcaZconstantAssoc + configAssocTracks.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { return false; } if (track.pt() > axisRanges[2][1] || track.pt() < axisRanges[2][0]) { @@ -1284,7 +1278,7 @@ struct HResonanceCorrelation { auto assoc = assocTrack.template track_as(); //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == assoc.globalIndex()) { if constexpr (Species == IndexPion) histos.fill(HIST("hNumberOfRejectedPairsPion"), 0.5); @@ -1298,7 +1292,7 @@ struct HResonanceCorrelation { //---] track quality check [--- if (!isValidAssocHadron(assoc)) continue; - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); @@ -1380,22 +1374,22 @@ struct HResonanceCorrelation { if (!mixing) { if constexpr (Species == IndexPion) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Pion")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } } else if constexpr (Species == IndexKaon) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kaon")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } } else { - if (triggSign == assocSign && doDeltaPhiStarCheck) { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); - } else if (doDeltaPhiStarCheck) { + } else if (qaSwitches.doDeltaPhiStarCheck) { histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); } fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Hadron")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); @@ -1443,7 +1437,7 @@ struct HResonanceCorrelation { int32_t globalIndexPos, globalIndexNeg; float efficiency, efficiencyError; bool isLeftBg, isSignal, isRightBg; - bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) }; std::vector cache; cache.reserve(assocs.size()); @@ -1491,7 +1485,7 @@ struct HResonanceCorrelation { c.isRightBg = (massWindowConfigurationsPhi.minBgNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxBgNSigma * sig); c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTruePhi()) && - (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); // Candidate-level kinematic/spectrum QA, ported from the equivalent // block in hStrangeCorrelation.cxx (h3dSpectrum[Y], @@ -1501,13 +1495,13 @@ struct HResonanceCorrelation { // correlation-pair QA, so filling them again per mixed pair would // overcount each candidate by the mixing-pool depth). // "SpectrumY" mirrors the V0 analog's rapidity cut (std::abs(rapidity) < - // ySel); AssocPhis has no exposed rapidity() accessor in this file, so + // phaseSpaceSelections.ySel); AssocPhis has no exposed rapidity() accessor in this file, so // pseudorapidity (c.eta) is used as a stand-in -- swap in a real // rapidity() call here if/when the table exposes one. if (!mixing) { float nSigmaVal = delta / sig; histos.fill(HIST("h3dPhiSpectrum"), c.pt, mult, nSigmaVal); - if (std::abs(c.eta) < ySel) { + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { histos.fill(HIST("h3dPhiSpectrumY"), c.pt, mult, nSigmaVal); } // matches the two registration branches above (TH3F vs THnF-with-mult, @@ -1586,7 +1580,7 @@ struct HResonanceCorrelation { firstLoop = true; //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { histos.fill(HIST("hNumberOfRejectedPairsPhi"), 0.5); continue; @@ -1634,7 +1628,7 @@ struct HResonanceCorrelation { if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1648,7 +1642,7 @@ struct HResonanceCorrelation { } if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1662,7 +1656,7 @@ struct HResonanceCorrelation { } if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1805,7 +1799,7 @@ struct HResonanceCorrelation { int32_t globalIndexPos, globalIndexNeg; float efficiency, efficiencyError; bool isLeftBg, isSignal, isRightBg; - bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!doAssocPhysicalPrimary || mcPhysicalPrimary()) + bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) }; std::vector cache; cache.reserve(assocs.size()); @@ -1853,14 +1847,14 @@ struct HResonanceCorrelation { c.isRightBg = (massWindowConfigurationsKstar.minBgNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxBgNSigma * sig); c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTrueKstar()) && - (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); // Same candidate-level Spectrum/EtaVsPtVsPhi QA as fillCorrelationsPhi // above -- see the comment there for rationale. if (!mixing) { float nSigmaVal = delta / sig; histos.fill(HIST("h3dKstar0Spectrum"), c.pt, mult, nSigmaVal); - if (std::abs(c.eta) < ySel) { + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { histos.fill(HIST("h3dKstar0SpectrumY"), c.pt, mult, nSigmaVal); } if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { @@ -1937,7 +1931,7 @@ struct HResonanceCorrelation { firstLoop = true; //---] removing autocorrelations [--- - if (doAutocorrelationRejection) { + if (checks.doAutocorrelationRejection) { if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { histos.fill(HIST("hNumberOfRejectedPairsKstar0"), 0.5); continue; @@ -1985,7 +1979,7 @@ struct HResonanceCorrelation { if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -1999,7 +1993,7 @@ struct HResonanceCorrelation { } if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -2013,7 +2007,7 @@ struct HResonanceCorrelation { } if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); - if (doDeltaPhiStarCheck) { + if (qaSwitches.doDeltaPhiStarCheck) { double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); if (triggSign > 0) { @@ -2178,13 +2172,13 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexHadron)) { return; } - LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); + // LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2203,7 +2197,7 @@ struct HResonanceCorrelation { return; } // ________________________________________________ - if (eventQAOwnerIndex == IndexHadron && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexHadron && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); } @@ -2250,7 +2244,7 @@ struct HResonanceCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) continue; histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); histos.fill(HIST("hAsssocTrackEtaVsPtVsPhi"), assoc.pt(), assoc.eta(), assoc.phi(), weight); @@ -2270,12 +2264,12 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexPion)) { return; } - LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); + // LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -2292,7 +2286,7 @@ struct HResonanceCorrelation { return; } // ________________________________________________ - if (eventQAOwnerIndex == IndexPion && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPion && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); } // Do basic QA @@ -2302,7 +2296,7 @@ struct HResonanceCorrelation { continue; histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) continue; if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) continue; @@ -2341,12 +2335,12 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexKaon)) { return; } - LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); + // LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -2363,7 +2357,7 @@ struct HResonanceCorrelation { return; } // ________________________________________________ - if (eventQAOwnerIndex == IndexKaon && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKaon && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); } // Do basic QA @@ -2373,7 +2367,7 @@ struct HResonanceCorrelation { continue; histos.fill(HIST("hKaonEtaVsPtAllSelected"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) continue; if (masterConfigurations.doMCassociation && std::abs(kaon.pdgCode()) != PdgCodes[IndexKaon]) continue; @@ -2411,12 +2405,12 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexPhi)) { return; } - LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); + // LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // Skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2433,7 +2427,7 @@ struct HResonanceCorrelation { return; } - if (eventQAOwnerIndex == IndexPhi && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPhi && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); } @@ -2488,12 +2482,12 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexKstar)) { return; } - LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); + // LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // Skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { return; } @@ -2510,7 +2504,7 @@ struct HResonanceCorrelation { return; } - if (eventQAOwnerIndex == IndexKstar && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKstar && qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); } @@ -2571,7 +2565,7 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { return; } @@ -2584,7 +2578,7 @@ struct HResonanceCorrelation { continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2620,7 +2614,7 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } @@ -2633,7 +2627,7 @@ struct HResonanceCorrelation { continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2669,7 +2663,7 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } @@ -2682,7 +2676,7 @@ struct HResonanceCorrelation { continue; if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) continue; - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting } @@ -2719,7 +2713,7 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } @@ -2735,7 +2729,7 @@ struct HResonanceCorrelation { continue; } - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); @@ -2781,7 +2775,7 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { continue; } @@ -2797,7 +2791,7 @@ struct HResonanceCorrelation { continue; } - if (doMixingQAandEventQA) { + if (qaSwitches.doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); @@ -2831,7 +2825,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > etaSel) + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) continue; float pt = mcParticle.pt(); @@ -2847,7 +2841,7 @@ struct HResonanceCorrelation { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { - // if (!doAssocPhysicalPrimaryInGen || + // if (!mcSelections.doAssocPhysicalPrimaryInGen || // mcParticle.isPhysicalPrimary()) { histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 0.0f); @@ -2867,7 +2861,7 @@ struct HResonanceCorrelation { } } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) continue; // NOTE: intentionally restricted to Pion only (IndexPion..IndexPion); Kstar0 is // not extended to processMCGenerated/ClosureTest/Prediction in this version. @@ -2932,13 +2926,13 @@ struct HResonanceCorrelation { if (collisions.size() > 1) { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) > ySel) + if (std::abs(mcParticle.y()) > phaseSpaceSelections.ySel) continue; if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) continue; // NOTE: Pion only, see note above; Kstar0 not extended here. @@ -2955,7 +2949,7 @@ struct HResonanceCorrelation { // Collision selection //--------------------------------------------- - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) return; if (masterConfigurations.applyNewMCSelection) { @@ -2983,7 +2977,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > etaSel) + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) continue; float pt = mcParticle.pt(); @@ -3002,7 +2996,7 @@ struct HResonanceCorrelation { if (std::abs(mcParticle.pdgCode()) != Pdg::kPhi) continue; - // if (doAssocPhysicalPrimaryInGen && + // if (mcSelections.doAssocPhysicalPrimaryInGen && // !mcParticle.isPhysicalPrimary()) // continue; @@ -3025,11 +3019,11 @@ struct HResonanceCorrelation { } else { histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), bestCollisionFT0Mpercentile); } - if (std::abs(mcParticle.y()) < ySel) + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); } - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -3059,7 +3053,7 @@ struct HResonanceCorrelation { } else { histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, bestCollisionFT0Mpercentile); } - if (std::abs(mcParticle.y()) < ySel) + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); } }); @@ -3079,7 +3073,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } double gpt = mcParticle.pt(); @@ -3089,7 +3083,7 @@ struct HResonanceCorrelation { } } - // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 0.0f); // step 1: no event selection whatsoever } @@ -3118,7 +3112,7 @@ struct HResonanceCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) { return; } @@ -3138,7 +3132,7 @@ struct HResonanceCorrelation { for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } double gpt = mcParticle.pt(); @@ -3148,7 +3142,7 @@ struct HResonanceCorrelation { } } - // if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 1.0f); // step 2: after event selection } @@ -3161,7 +3155,7 @@ struct HResonanceCorrelation { double geta = mcParticle.eta(); double gpt = mcParticle.pt(); double gphi = mcParticle.phi(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -3170,13 +3164,13 @@ struct HResonanceCorrelation { histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); @@ -3334,7 +3328,7 @@ struct HResonanceCorrelation { double geta = mcParticle.eta(); double gpt = mcParticle.pt(); double gphi = mcParticle.phi(); - if (std::abs(geta) > etaSel) { + if (std::abs(geta) > phaseSpaceSelections.etaSel) { continue; } if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { @@ -3355,13 +3349,13 @@ struct HResonanceCorrelation { } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); From 93b9a0e33bb7642102ea6086a89723b2006303e5 Mon Sep 17 00:00:00 2001 From: spolitan <59452587+stefanopolitano@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:58:17 +0200 Subject: [PATCH 182/648] [PWGHF] Add bdt information in 2pc sparse (#18178) --- .../TableProducer/correlatorFlowCharmHadronsReduced.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index eb62101c165..816270c38a0 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -197,8 +197,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } else if (doprocessMixedEventHadHadWCentMix || doprocessMixedEventHadHadWMultMix) { registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); } else { - axes.insert(axes.end(), {axisInvMass}); - // axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); + // axes.insert(axes.end(), {axisInvMass}); + axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); if (doprocessSameEventCharmHadWCentMix || doprocessSameEventCharmHadWMultMix || doprocessSameEventCharmHadWCentMixBase) { registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventCharmHadWMultMix || doprocessMixedEventCharmHadWCentMixBase) { @@ -298,7 +298,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { if constexpr (FillSparses) { if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), - pair.deltaPhi(), trigCand.invMassTrig()); // , trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + pair.deltaPhi(), trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); } else { registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()); } @@ -366,7 +366,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { if constexpr (FillSparses) { if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, - deltaPhi, trigCand.invMassTrig()); //, trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + deltaPhi, trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); } else { registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi); } From b3d3e66bf17cef53f05d384aac85b0d3bb1be722 Mon Sep 17 00:00:00 2001 From: Ana Marin Date: Mon, 5 Oct 2026 21:25:53 +0200 Subject: [PATCH 183/648] [PWGEM] Store the mixing categories if flag is true (#18111) Co-authored-by: Ana Marin Garcia --- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 40 +++++++++++++++++---- PWGEM/PhotonMeson/Utils/NMHistograms.h | 21 +++++++++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 45e4f7c26d7..9623f795ea6 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -98,6 +98,7 @@ struct Pi0EtaToGammaGamma { o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {o2::framework::VARIABLE_WIDTH, -o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0.0f, +o2::constants::math::PIQuarter, +o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + o2::framework::Configurable cfgSparseFullAxes{"cfgSparseFullAxes", false, "add event plane, occupancy and z-vertex axes to Pair/same/hs and Pair/mix/hs"}; o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; @@ -423,10 +424,13 @@ struct Pi0EtaToGammaGamma { emh2 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + std::vector occBinsForHs(occ_bin_edges.begin(), occ_bin_edges.end()); + std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); + std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, "ee#gamma"); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, "#gamma#gamma"); } DefineEMEventCut(); DefinePCMCut(); @@ -800,6 +804,8 @@ struct Pi0EtaToGammaGamma { occbin = static_cast(occ_bin_edges.size()) - 2; } + float occupancy = (cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) : collision.ft0cOccupancyInTimeRange(); + // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); @@ -859,7 +865,11 @@ struct Pi0EtaToGammaGamma { continue; } - fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + } std::pair tuple_tmp_id2 = std::make_pair(pos2.trackId(), ele2.trackId()); if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { @@ -953,7 +963,11 @@ struct Pi0EtaToGammaGamma { wpair *= g2.omegaMBWeight(); } - fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), wpair); + } else { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + } if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { auto emphoton1 = o2::aod::pwgem::photonmeson::utils::EMPhoton(g1.pt(), g1.eta(), g1.phi(), 0); @@ -1047,7 +1061,11 @@ struct Pi0EtaToGammaGamma { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1082,7 +1100,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1115,7 +1137,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool diff --git a/PWGEM/PhotonMeson/Utils/NMHistograms.h b/PWGEM/PhotonMeson/Utils/NMHistograms.h index ac361d9783e..ab69f458636 100644 --- a/PWGEM/PhotonMeson/Utils/NMHistograms.h +++ b/PWGEM/PhotonMeson/Utils/NMHistograms.h @@ -18,6 +18,7 @@ #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" +#include #include #include #include @@ -32,7 +33,12 @@ namespace o2::aod::pwgem::photonmeson::utils::nmhistogram { -inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, const char* pairname = "#gamma#gamma") +inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, + bool sparseFullAxes = false, + std::vector const& occBins = {0, 500, 1000, 2000, 3000, 5000, 10000}, + std::vector const& epBins = {-o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf}, + std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, + const char* pairname = "#gamma#gamma") { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at skimming.!! std::vector ptbins; @@ -51,6 +57,9 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is } const o2::framework::AxisSpec axis_pt{ptbins, Form("p_{T,%s} (GeV/c)", pairname)}; const o2::framework::AxisSpec axis_mass{400, 0, 0.8, Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; + const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; + const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; if (isMC) { fRegistry->add("Pair/Pi0/hs_Primary", "rec. true pi0", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); @@ -74,7 +83,15 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is fRegistry->get(HIST("Generated/Eta/hPtY"))->SetXTitle("p_{T} (GeV/c)"); fRegistry->get(HIST("Generated/Eta/hPtY"))->SetYTitle("rapidity |y|"); } else { - fRegistry->add("Pair/same/hs", "diphoton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); + if (sparseFullAxes) { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt, axis_ep, axis_occ, axis_vtx}, true); + } else { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt}, true); + } fRegistry->addClone("Pair/same/", "Pair/mix/"); } } From 706404cdc59d025091dac4fb42e28046132a19e7 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:48:28 +0200 Subject: [PATCH 184/648] [PWGLF] Fix rapidity selection in derivedlambdakzeroanalysis (#18177) --- PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 4f201c3c439..1befc65defe 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -3064,7 +3064,7 @@ struct derivedlambdakzeroanalysis { // fill AP plot for all V0s histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - uint64_t selMap = computeReconstructionBitmap(v0, collision, yK0Short, yLambda); + uint64_t selMap = computeReconstructionBitmap(v0, collision, yLambda, yK0Short); selMap |= selMapMCassociation; // feeddown matrix always with association From b5075e011bc31f1dcb4e557edb8e18045f816fa6 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:26:32 +0200 Subject: [PATCH 185/648] [PWGCF] initial task commit (#18174) Co-authored-by: ALICE Action Bot --- PWGCF/GenericFramework/Tasks/CMakeLists.txt | 5 + .../GenericFramework/Tasks/energyFlowGfw.cxx | 782 ++++++++++++++++++ 2 files changed, 787 insertions(+) create mode 100644 PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx diff --git a/PWGCF/GenericFramework/Tasks/CMakeLists.txt b/PWGCF/GenericFramework/Tasks/CMakeLists.txt index 73a9f7a5396..bc5feb0779c 100644 --- a/PWGCF/GenericFramework/Tasks/CMakeLists.txt +++ b/PWGCF/GenericFramework/Tasks/CMakeLists.txt @@ -28,3 +28,8 @@ o2physics_add_dpl_workflow(flow-gfw-nonflow SOURCES flowGfwNonflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(energy-flow-gfw + SOURCES energyFlowGfw.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx new file mode 100644 index 00000000000..4fa7d5d96b0 --- /dev/null +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -0,0 +1,782 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file enegyFlowGfw.cxx +/// \brief Charged-particle transverse-energy correlators in Xe-Xe and Pb-Pb collisions +/// \author Emil Gorm Dahlbæk Nielsen + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::genericframework; + +struct EnergyFlowGfw { + enum EnergyWeightMode : int { + PtWeight = 0, + MtWeight, + FullEtWeight + }; + + enum ParticleId : int { + Unidentified = 0, + Pion, + Kaon, + Proton + }; + + enum ActivityAxisMode : int { + CentralityAxis = 0, + NGlobalTracksAxis, + ZdcSpectatorAxis + }; + + enum AodSelectionStep : uint8_t { + AllAodEvents = 0, + TriggerSelected, + CentralitySelected, + HasValidZdc, + SpectatorSelected + }; + + enum ZdcQaStep : uint8_t { + MatchedZdc = 0, + FiniteCommonEnergy, + NonnegativeCommonEnergy, + HasZnTiming, + HasZpTiming, + HasAllTiming + }; + + struct : ConfigurableGroup { + Configurable vertexZ{"vertexZ", 10.f, "Accepted |z-vertex| (cm)"}; + Configurable> ptCuts{"ptCuts", {0.2f, 5.f}, "Track pT cuts (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute track pseudorapidity"}; + } cuts; + + struct : ConfigurableGroup { + Configurable weightMode{"weightMode", FullEtWeight, "Energy weight: 0=pT, 1=mT, 2=E/cosh(eta)"}; + Configurable nSigmaCut{"nSigmaCut", 3.f, "TPC or combined TPC+TOF PID selection in n-sigma"}; + Configurable tofPtCut{"tofPtCut", 0.5f, "Require TOF PID above this pT (GeV/c)"}; + Configurable strictPid{"strictPid", true, "Mark tracks compatible with multiple species as unidentified"}; + Configurable unidentifiedMass{"unidentifiedMass", o2::constants::physics::MassPionCharged, "Mass assigned to unidentified charged tracks (GeV/c^2)"}; + } energy; + + Configurable cfgRegions{"cfgRegions", {{"refA", "refB"}, {-0.8, 0.4}, {-0.4, 0.8}, {0, 0}, {1, 1}}, "GFW region definitions"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refA {0}", "refB {0}", "refA {0} refB {0}", "refA {1} refB {-1}", "refA {2} refB {-2}", "refA {3} refB {-3}", "refA {4} refB {-4}", "refA {5} refB {-5}", "refA {6} refB {-6}"}, {"F0A", "F0B", "F0AF0B", "EECGap12", "EECGap22", "EECGap32", "EECGap42", "EECGap52", "EECGap62"}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}}, "GFW correlations to calculate"}; + Configurable cfgBootstrapSamples{"cfgBootstrapSamples", 10, "Number of FlowContainer bootstrap subsamples; zero disables bootstrapping"}; + Configurable cfgActivityAxis{"cfgActivityAxis", CentralityAxis, "FlowContainer x-axis: 0=centrality, 1=number of filtered global tracks, 2=corrected ZDC spectator estimate"}; + Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"}; + + Configurable cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"}; + Configurable cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"}; + Configurable cfgZdcResponseZNA{"cfgZdcResponseZNA", 1.f, "ZNA efficiency/acceptance response factor alpha; corrected energy is E_ZNA/alpha_ZNA"}; + Configurable cfgZdcResponseZNC{"cfgZdcResponseZNC", 1.f, "ZNC efficiency/acceptance response factor alpha; corrected energy is E_ZNC/alpha_ZNC"}; + Configurable cfgZdcResponseZPA{"cfgZdcResponseZPA", 1.f, "ZPA efficiency/acceptance response factor alpha; corrected energy is E_ZPA/alpha_ZPA"}; + Configurable cfgZdcResponseZPC{"cfgZdcResponseZPC", 1.f, "ZPC efficiency/acceptance response factor alpha; corrected energy is E_ZPC/alpha_ZPC"}; + Configurable cfgZdcMaxAbsTimeForQa{"cfgZdcMaxAbsTimeForQa", 100.f, "Maximum absolute ZDC time considered available in QA; excludes missing-time sentinels"}; + + Configurable efficiencyPath{"efficiencyPath", "", "CCDB path or local ROOT file containing a 4D efficiency-correction THn"}; + Configurable efficiencyFromLocalFile{"efficiencyFromLocalFile", false, "Load efficiencyPath from a local ROOT file"}; + Configurable acceptancePath{"acceptancePath", "", "CCDB path to GFW acceptance weights"}; + Configurable acceptanceRunByRun{"acceptanceRunByRun", false, "Load acceptance weights from the RunByRun subdirectory"}; + + ConfigurableAxis axisVertex{"axisVertex", {10, -10., 10.}, "z-vertex (cm)"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.1}, "centrality"}; + ConfigurableAxis axisNGlobalTracks{"axisNGlobalTracks", {500, 0., 5000.}, "number of filtered global tracks"}; + ConfigurableAxis axisPt{"axisPt", {100, 0., 5.}, "pT (GeV/c)"}; + ConfigurableAxis axisEnergy{"axisEnergy", {200, 0., 20.}, "transverse-energy weight (GeV)"}; + ConfigurableAxis axisTotalSpectators{"axisTotalSpectators", {500, 0., 500.}, "total spectator count"}; + ConfigurableAxis axisSpectatorActivity{"axisSpectatorActivity", {500, 0., 500.}, "spectator count used by the FlowContainer"}; + ConfigurableAxis axisZdcNeutronEnergy{"axisZdcNeutronEnergy", {410, -2000., 80000.}, "ZDC neutron common energy"}; + ConfigurableAxis axisZdcProtonEnergy{"axisZdcProtonEnergy", {310, -1000., 30000.}, "ZDC proton common energy"}; + ConfigurableAxis axisZdcTotalEnergy{"axisZdcTotalEnergy", {410, -5000., 200000.}, "summed ZDC common energy"}; + ConfigurableAxis axisZdcZemEnergy{"axisZdcZemEnergy", {210, -100., 2000.}, "summed ZEM energy"}; + ConfigurableAxis axisZdcTime{"axisZdcTime", {600, -15., 15.}, "ZDC time (ns)"}; + ConfigurableAxis axisZdcTimeCombination{"axisZdcTimeCombination", {400, -20., 20.}, "ZDC time sum or difference (ns)"}; + ConfigurableAxis axisZdcTrackMultiplicity{"axisZdcTrackMultiplicity", {400, 0., 4000.}, "number of filtered global tracks"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cuts.vertexZ; + Filter trackFilter = (nabs(aod::track::eta) < cuts.etaMax) && (aod::track::pt > cuts.ptCuts->first) && (aod::track::pt < cuts.ptCuts->second) && + ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); + + using CollisionsRun2 = soa::Filtered>; + using CollisionsRun3 = soa::Filtered>; + using Tracks = soa::Filtered>; + using BCsRun2 = soa::Join; + using BCsRun3 = soa::Join; + + OutputObj flowContainer{"FlowContainer"}; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + + std::unique_ptr gfw{std::make_unique()}; + std::unique_ptr random{std::make_unique(0)}; + std::vector corrConfigs; + std::vector zeroHarmonicConfigs; + int gfwMask = 0; + + std::unique_ptr localEfficiency; + THn* efficiency = nullptr; + GFWWeights* acceptance = nullptr; + int acceptanceRunNumber = -1; + + void init(InitContext const&) + { + if (doprocessRun2 && doprocessRun3) { + LOGF(fatal, "Enable only one input format: Run 2 AOD or Run 3 AOD"); + } + if (cuts.ptCuts->first >= cuts.ptCuts->second || cuts.etaMax <= 0.f) { + LOGF(fatal, "Require ptMin < ptMax and etaMax > 0"); + } + if (energy.weightMode < PtWeight || energy.weightMode > FullEtWeight) { + LOGF(fatal, "weightMode must be 0 (pT), 1 (mT), or 2 (full ET)"); + } + if (energy.nSigmaCut <= 0.f || energy.tofPtCut < 0.f || energy.unidentifiedMass < 0.f) { + LOGF(fatal, "PID n-sigma and mass settings must be non-negative, with nSigmaCut > 0"); + } + if (cfgBootstrapSamples < 0) { + LOGF(fatal, "cfgBootstrapSamples must not be negative"); + } + if (cfgActivityAxis < CentralityAxis || cfgActivityAxis > ZdcSpectatorAxis) { + LOGF(fatal, "cfgActivityAxis must be 0 (centrality), 1 (filtered global tracks), or 2 (corrected ZDC spectators)"); + } + if (cfgMaxCentrality < 0.f || cfgMaxCentrality > 100.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) { + LOGF(fatal, "The maximum centrality must be in [0, 100], the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative"); + } + + const int numberOfRegions = cfgRegions->GetSize(); + if (numberOfRegions <= 0 || cfgCorrConfig->GetSize() <= 0) { + LOGF(fatal, "GFW regions and correlations must be non-empty and internally consistent"); + } + + float largestRegionEta = 0.f; + for (int index = 0; index < numberOfRegions; ++index) { + const float etaMin = cfgRegions->GetEtaMin()[index]; + const float etaMax = cfgRegions->GetEtaMax()[index]; + if (!std::isfinite(etaMin) || !std::isfinite(etaMax) || etaMin >= etaMax) { + LOGF(fatal, "GFW region %s must have finite etaMin < etaMax", cfgRegions->GetNames()[index].c_str()); + } + if (cfgRegions->GetpTDifs()[index] != 0 || cfgRegions->GetBitmasks()[index] == 0) { + LOGF(fatal, "This task supports only pT-integrated regions with non-zero bit masks"); + } + largestRegionEta = std::max({largestRegionEta, std::abs(etaMin), std::abs(etaMax)}); + gfwMask |= cfgRegions->GetBitmasks()[index]; + gfw->AddRegion(cfgRegions->GetNames()[index], etaMin, etaMax, 1, cfgRegions->GetBitmasks()[index]); + } + if (largestRegionEta > cuts.etaMax) { + LOGF(fatal, "All GFW regions must be contained within the selected track eta range"); + } + + auto profileNames = std::make_unique(); + profileNames->SetOwner(true); + corrConfigs.reserve(cfgCorrConfig->GetSize()); + zeroHarmonicConfigs.reserve(cfgCorrConfig->GetSize()); + for (int index = 0; index < cfgCorrConfig->GetSize(); ++index) { + if (cfgCorrConfig->GetpTDifs()[index] != 0) { + LOGF(fatal, "This task supports only pT-integrated GFW correlations"); + } + auto config = gfw->GetCorrelatorConfig(cfgCorrConfig->GetCorrs()[index], cfgCorrConfig->GetHeads()[index], false); + if (config.Regs.empty() || config.Head.empty()) { + LOGF(fatal, "Invalid GFW correlation configuration at index %d", index); + } + const bool zeroHarmonic = std::all_of(config.Hars.begin(), config.Hars.end(), [](const auto& harmonics) { + return std::all_of(harmonics.begin(), harmonics.end(), [](int harmonic) { return harmonic == 0; }); + }); + zeroHarmonicConfigs.push_back(zeroHarmonic); + if (!zeroHarmonic) { + profileNames->Add(new TNamed(config.Head.c_str(), config.Head.c_str())); + } + const std::string rawName = config.Head + "_raw"; + profileNames->Add(new TNamed(rawName.c_str(), rawName.c_str())); + corrConfigs.push_back(std::move(config)); + } + gfw->CreateRegions(); + flowContainer.setObject(new FlowContainer("FlowContainer")); + const AxisSpec activityAxis = [this]() { + if (cfgActivityAxis.value == NGlobalTracksAxis) { + return AxisSpec{axisNGlobalTracks, "N_{global tracks}"}; + } + if (cfgActivityAxis.value == ZdcSpectatorAxis) { + return AxisSpec{axisSpectatorActivity, "N_{spectators}"}; + } + return AxisSpec{axisCentrality, "centrality (%)"}; + }(); + if (activityAxis.nBins.has_value()) { + flowContainer->Initialize(profileNames.get(), activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgBootstrapSamples); + } else { + flowContainer->Initialize(profileNames.get(), activityAxis, cfgBootstrapSamples); + } + + const AxisSpec etaAxis{32, -cuts.etaMax.value, cuts.etaMax.value, "#eta"}; + registry.add("event/centrality", "Accepted events;centrality;events", HistType::kTH1F, {axisCentrality}); + registry.add("event/activity", "Accepted events;event activity;events", HistType::kTH1F, {activityAxis}); + registry.add("event/vertex", "Accepted events;z-vtx (cm);events", HistType::kTH1F, {axisVertex}); + registry.add("event/aodSelection", "AOD event selection;selection;events", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("event/totalSpectators", "ZDC spectator estimate;N_{spectators};events", HistType::kTH1F, {axisTotalSpectators}); + registry.add("track/ptEta", "Selected tracks;p_{T} (GeV/c);#eta", HistType::kTH2F, {axisPt, etaAxis}); + registry.add("track/energy", "Energy entering the GFW;E_{T} (GeV);tracks", HistType::kTH1F, {axisEnergy}); + registry.add("track/pid", "PID mass assignment;species;tracks", HistType::kTH1F, {{4, -0.5, 3.5}}); + + registry.add("zdc/qaSelection", "ZDC QA availability;condition;events", HistType::kTH1F, {{6, -0.5, 5.5}}); + registry.add("zdc/energyZNA", "Triggered events with matched ZDC;E_{ZNA};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZNC", "Triggered events with matched ZDC;E_{ZNC};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZPA", "Triggered events with matched ZDC;E_{ZPA};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZPC", "Triggered events with matched ZDC;E_{ZPC};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZEM", "Triggered events with matched ZDC;E_{ZEM1}+E_{ZEM2};events", HistType::kTH1F, {axisZdcZemEnergy}); + registry.add("zdc/timeZNA", "Triggered events with matched ZDC;t_{ZNA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZNC", "Triggered events with matched ZDC;t_{ZNC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPA", "Triggered events with matched ZDC;t_{ZPA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPC", "Triggered events with matched ZDC;t_{ZPC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZnSumVsDiff", "Triggered events with ZNA and ZNC timing;t_{ZNA}-t_{ZNC} (ns);t_{ZNA}+t_{ZNC} (ns)", HistType::kTH2F, {axisZdcTimeCombination, axisZdcTimeCombination}); + registry.add("zdc/energyZnaVsZnc", "Triggered events with finite ZN energies;E_{ZNC};E_{ZNA}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsZpc", "Triggered events with finite ZP energies;E_{ZPC};E_{ZPA}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsZem", "Triggered events with finite ZN and ZEM energies;E_{ZEM1}+E_{ZEM2};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcZemEnergy, axisZdcTotalEnergy}); + registry.add("zdc/energyZnaVsTime", "Triggered events with matched ZDC;t_{ZNA} (ns);E_{ZNA}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZncVsTime", "Triggered events with matched ZDC;t_{ZNC} (ns);E_{ZNC}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsTime", "Triggered events with matched ZDC;t_{ZPA} (ns);E_{ZPA}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/energyZpcVsTime", "Triggered events with matched ZDC;t_{ZPC} (ns);E_{ZPC}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZNA", "Triggered events with finite ZNA energies;E_{ZNA}^{common};#Sigma E_{ZNA}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZNC", "Triggered events with finite ZNC energies;E_{ZNC}^{common};#Sigma E_{ZNC}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZPA", "Triggered events with finite ZPA energies;E_{ZPA}^{common};#Sigma E_{ZPA}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZPC", "Triggered events with finite ZPC energies;E_{ZPC}^{common};#Sigma E_{ZPC}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsNGlobalTracks", "Triggered events with finite ZN energies;N_{global tracks};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsNGlobalTracks", "Triggered events with finite ZP energies;N_{global tracks};E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsNGlobalTracks", "Triggered events passing current nonnegative-energy requirement;N_{global tracks};current N_{spectators} estimate", HistType::kTH2F, {axisZdcTrackMultiplicity, axisTotalSpectators}); + registry.add("zdc/energyZnVsCentrality", "Triggered events with finite ZN energies;centrality (%);E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsCentrality", "Triggered events with finite ZP energies;centrality (%);E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsCentrality", "Triggered events with finite common energies;centrality (%);#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsCentrality", "Triggered events passing current nonnegative-energy requirement;centrality (%);current N_{spectators} estimate", HistType::kTH2F, {axisCentrality, axisTotalSpectators}); + registry.add("zdc/meanZNAByNGlobalTracks", "Mean ZNA response;N_{global tracks};#LT E_{ZNA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNCByNGlobalTracks", "Mean ZNC response;N_{global tracks};#LT E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPAByNGlobalTracks", "Mean ZPA response;N_{global tracks};#LT E_{ZPA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPCByNGlobalTracks", "Mean ZPC response;N_{global tracks};#LT E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZnByNGlobalTracks", "Mean neutron-ZDC response;N_{global tracks};#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZpByNGlobalTracks", "Mean proton-ZDC response;N_{global tracks};#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanTotalByNGlobalTracks", "Mean total ZDC response;N_{global tracks};#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNAByCentrality", "Mean ZNA response;centrality (%);#LT E_{ZNA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZNCByCentrality", "Mean ZNC response;centrality (%);#LT E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPAByCentrality", "Mean ZPA response;centrality (%);#LT E_{ZPA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPCByCentrality", "Mean ZPC response;centrality (%);#LT E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZnByCentrality", "Mean neutron-ZDC response;centrality (%);#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZpByCentrality", "Mean proton-ZDC response;centrality (%);#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanTotalByCentrality", "Mean total ZDC response;centrality (%);#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisCentrality}); + + auto selectionHistogram = registry.get(HIST("event/aodSelection")); + selectionHistogram->GetXaxis()->SetBinLabel(AllAodEvents + 1, "all"); + selectionHistogram->GetXaxis()->SetBinLabel(TriggerSelected + 1, "event selection"); + selectionHistogram->GetXaxis()->SetBinLabel(CentralitySelected + 1, "centrality threshold"); + selectionHistogram->GetXaxis()->SetBinLabel(HasValidZdc + 1, "valid ZDC"); + selectionHistogram->GetXaxis()->SetBinLabel(SpectatorSelected + 1, "spectator threshold"); + auto zdcQaHistogram = registry.get(HIST("zdc/qaSelection")); + zdcQaHistogram->GetXaxis()->SetBinLabel(MatchedZdc + 1, "matched ZDC"); + zdcQaHistogram->GetXaxis()->SetBinLabel(FiniteCommonEnergy + 1, "finite common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(NonnegativeCommonEnergy + 1, "nonnegative common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZnTiming + 1, "ZNA and ZNC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZpTiming + 1, "ZPA and ZPC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasAllTiming + 1, "all timing"); + auto pidHistogram = registry.get(HIST("track/pid")); + pidHistogram->GetXaxis()->SetBinLabel(Unidentified + 1, "unidentified"); + pidHistogram->GetXaxis()->SetBinLabel(Pion + 1, "pion"); + pidHistogram->GetXaxis()->SetBinLabel(Kaon + 1, "kaon"); + pidHistogram->GetXaxis()->SetBinLabel(Proton + 1, "proton"); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + template + float totalSpectators(TZdc const& zdc) const + { + const std::array energies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array responseFactors{cfgZdcResponseZNA.value, cfgZdcResponseZNC.value, cfgZdcResponseZPA.value, cfgZdcResponseZPC.value}; + float totalEnergy = 0.f; + for (int index = 0; index < 4; ++index) { + const float value = energies[index]; + if (!std::isfinite(value) || value < 0.f) { + return -1.f; + } + totalEnergy += value / responseFactors[index]; + } + return totalEnergy / cfgSpectatorEnergyPerNucleon.value; + } + + template + void fillZdcQa(TZdc const& zdc, float numberOfGlobalTracks, float centrality) + { + registry.fill(HIST("zdc/qaSelection"), MatchedZdc); + + const std::array commonEnergies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array times{zdc.timeZNA(), zdc.timeZNC(), zdc.timeZPA(), zdc.timeZPC()}; + const bool validCentrality = std::isfinite(centrality) && centrality >= 0.f && centrality <= 100.f; + const bool allFinite = std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return std::isfinite(value); }); + const bool allNonnegative = allFinite && std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return value >= 0.f; }); + if (allFinite) { + registry.fill(HIST("zdc/qaSelection"), FiniteCommonEnergy); + } + if (allNonnegative) { + registry.fill(HIST("zdc/qaSelection"), NonnegativeCommonEnergy); + } + + const auto hasTiming = [this](float value) { return std::isfinite(value) && std::abs(value) < cfgZdcMaxAbsTimeForQa.value; }; + const bool hasZnTiming = hasTiming(times[0]) && hasTiming(times[1]); + const bool hasZpTiming = hasTiming(times[2]) && hasTiming(times[3]); + if (hasZnTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZnTiming); + registry.fill(HIST("zdc/timeZnSumVsDiff"), times[0] - times[1], times[0] + times[1]); + } + if (hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZpTiming); + } + if (hasZnTiming && hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasAllTiming); + } + + if (std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZNA"), commonEnergies[0]); + registry.fill(HIST("zdc/meanZNAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[0]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNAByCentrality"), centrality, commonEnergies[0]); + } + } + if (std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZNC"), commonEnergies[1]); + registry.fill(HIST("zdc/meanZNCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[1]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNCByCentrality"), centrality, commonEnergies[1]); + } + } + if (std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZPA"), commonEnergies[2]); + registry.fill(HIST("zdc/meanZPAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[2]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPAByCentrality"), centrality, commonEnergies[2]); + } + } + if (std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZPC"), commonEnergies[3]); + registry.fill(HIST("zdc/meanZPCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[3]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPCByCentrality"), centrality, commonEnergies[3]); + } + } + + if (hasTiming(times[0])) { + registry.fill(HIST("zdc/timeZNA"), times[0]); + } + if (hasTiming(times[1])) { + registry.fill(HIST("zdc/timeZNC"), times[1]); + } + if (hasTiming(times[2])) { + registry.fill(HIST("zdc/timeZPA"), times[2]); + } + if (hasTiming(times[3])) { + registry.fill(HIST("zdc/timeZPC"), times[3]); + } + if (hasTiming(times[0]) && std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZnaVsTime"), times[0], commonEnergies[0]); + } + if (hasTiming(times[1]) && std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZncVsTime"), times[1], commonEnergies[1]); + } + if (hasTiming(times[2]) && std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZpaVsTime"), times[2], commonEnergies[2]); + } + if (hasTiming(times[3]) && std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZpcVsTime"), times[3], commonEnergies[3]); + } + + const auto sumSectors = [](const auto& sectors) { + float sum = 0.f; + for (const float value : sectors) { + if (!std::isfinite(value)) { + return std::numeric_limits::quiet_NaN(); + } + sum += value; + } + return sum; + }; + const std::array sectorSums{sumSectors(zdc.energySectorZNA()), sumSectors(zdc.energySectorZNC()), sumSectors(zdc.energySectorZPA()), sumSectors(zdc.energySectorZPC())}; + if (std::isfinite(commonEnergies[0]) && std::isfinite(sectorSums[0])) { + registry.fill(HIST("zdc/commonVsSectorZNA"), commonEnergies[0], sectorSums[0]); + } + if (std::isfinite(commonEnergies[1]) && std::isfinite(sectorSums[1])) { + registry.fill(HIST("zdc/commonVsSectorZNC"), commonEnergies[1], sectorSums[1]); + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(sectorSums[2])) { + registry.fill(HIST("zdc/commonVsSectorZPA"), commonEnergies[2], sectorSums[2]); + } + if (std::isfinite(commonEnergies[3]) && std::isfinite(sectorSums[3])) { + registry.fill(HIST("zdc/commonVsSectorZPC"), commonEnergies[3], sectorSums[3]); + } + + const float zem1 = zdc.energyZEM1(); + const float zem2 = zdc.energyZEM2(); + const bool finiteZem = std::isfinite(zem1) && std::isfinite(zem2); + const float zemSum = finiteZem ? zem1 + zem2 : std::numeric_limits::quiet_NaN(); + if (finiteZem) { + registry.fill(HIST("zdc/energyZEM"), zemSum); + } + if (std::isfinite(commonEnergies[0]) && std::isfinite(commonEnergies[1])) { + const float znSum = commonEnergies[0] + commonEnergies[1]; + registry.fill(HIST("zdc/energyZnaVsZnc"), commonEnergies[1], commonEnergies[0]); + registry.fill(HIST("zdc/energyZnVsNGlobalTracks"), numberOfGlobalTracks, znSum); + registry.fill(HIST("zdc/meanZnByNGlobalTracks"), numberOfGlobalTracks, znSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZnVsCentrality"), centrality, znSum); + registry.fill(HIST("zdc/meanZnByCentrality"), centrality, znSum); + } + if (finiteZem) { + registry.fill(HIST("zdc/energyZnVsZem"), zemSum, znSum); + } + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(commonEnergies[3])) { + const float zpSum = commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyZpaVsZpc"), commonEnergies[3], commonEnergies[2]); + registry.fill(HIST("zdc/energyZpVsNGlobalTracks"), numberOfGlobalTracks, zpSum); + registry.fill(HIST("zdc/meanZpByNGlobalTracks"), numberOfGlobalTracks, zpSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZpVsCentrality"), centrality, zpSum); + registry.fill(HIST("zdc/meanZpByCentrality"), centrality, zpSum); + } + } + if (allFinite) { + const float totalEnergy = commonEnergies[0] + commonEnergies[1] + commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyTotalVsNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + registry.fill(HIST("zdc/meanTotalByNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + if (validCentrality) { + registry.fill(HIST("zdc/energyTotalVsCentrality"), centrality, totalEnergy); + registry.fill(HIST("zdc/meanTotalByCentrality"), centrality, totalEnergy); + } + if (allNonnegative) { + const float spectators = totalSpectators(zdc); + registry.fill(HIST("zdc/spectatorEstimateVsNGlobalTracks"), numberOfGlobalTracks, spectators); + if (validCentrality) { + registry.fill(HIST("zdc/spectatorEstimateVsCentrality"), centrality, spectators); + } + } + } + } + + template + bool eventSelected(TCollision const& collision, bool triggerSelected, float numberOfGlobalTracks, float centrality, float& spectators) + { + spectators = -1.f; + registry.fill(HIST("event/aodSelection"), AllAodEvents); + if (!triggerSelected) { + return false; + } + registry.fill(HIST("event/aodSelection"), TriggerSelected); + + if (!collision.has_foundBC()) { + return false; + } + const auto& bc = collision.template foundBC_as(); + if (!bc.has_zdc()) { + return false; + } + const auto& zdc = bc.zdc(); + fillZdcQa(zdc, numberOfGlobalTracks, centrality); + + if (!std::isfinite(centrality) || centrality < 0.f || centrality > cfgMaxCentrality) { + return false; + } + registry.fill(HIST("event/aodSelection"), CentralitySelected); + + spectators = totalSpectators(zdc); + if (spectators < 0.f) { + return false; + } + registry.fill(HIST("event/aodSelection"), HasValidZdc); + registry.fill(HIST("event/totalSpectators"), spectators); + if (spectators > cfgMaxTotalSpectators) { + return false; + } + registry.fill(HIST("event/aodSelection"), SpectatorSelected); + return true; + } + + void loadEfficiency(uint64_t timestamp) + { + if (efficiencyPath.value.empty()) { + efficiency = nullptr; + return; + } + if (efficiencyFromLocalFile) { + if (localEfficiency) { + efficiency = localEfficiency.get(); + return; + } + std::unique_ptr input(TFile::Open(efficiencyPath.value.c_str(), "READ")); + if (!input || input->IsZombie()) { + LOGF(fatal, "Could not open efficiency file %s", efficiencyPath.value.c_str()); + return; + } + auto* source = dynamic_cast(input->Get("ccdb_object")); + if (!source || source->GetNdimensions() != 4) { + LOGF(fatal, "Efficiency object must be a 4D THn with axes eta, pT, centrality, z-vtx"); + } + localEfficiency.reset(dynamic_cast(source->Clone("eecEfficiency"))); + if (!localEfficiency) { + LOGF(fatal, "Could not clone the efficiency object from %s", efficiencyPath.value.c_str()); + } + efficiency = localEfficiency.get(); + return; + } + if (!efficiency || !ccdb->isCachedObjectValid(efficiencyPath.value, timestamp)) { + efficiency = ccdb->getForTimeStamp>(efficiencyPath.value, timestamp); + if (!efficiency || efficiency->GetNdimensions() != 4) { + LOGF(fatal, "Could not load a 4D efficiency correction from %s", efficiencyPath.value.c_str()); + } + } + } + + void loadAcceptance(uint64_t timestamp, int runNumber) + { + if (acceptancePath.value.empty()) { + acceptance = nullptr; + return; + } + if (acceptance && (!acceptanceRunByRun || acceptanceRunNumber == runNumber)) { + return; + } + std::string path = acceptancePath.value; + if (acceptanceRunByRun) { + if (path.back() != '/') { + path += '/'; + } + path += "RunByRun/"; + } + acceptance = ccdb->getForTimeStamp(path, timestamp); + if (!acceptance || !acceptance->isDataFilled()) { + LOGF(fatal, "Could not load populated GFW acceptance weights from %s", path.c_str()); + } + acceptanceRunNumber = runNumber; + } + + double efficiencyWeight(float eta, float pt, float centrality, float posZ) const + { + if (!efficiency) { + return 1.; + } + std::array bins{ + efficiency->GetAxis(0)->FindBin(eta), + efficiency->GetAxis(1)->FindBin(pt), + efficiency->GetAxis(2)->FindBin(centrality), + efficiency->GetAxis(3)->FindBin(posZ)}; + return efficiency->GetBinContent(bins.data()); + } + + template + int particleId(TTrack const& track) const + { + const std::array tpc{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array selected = tpc; + if (track.pt() > energy.tofPtCut.value) { + if (!track.hasTOF()) { + return Unidentified; + } + selected = {std::hypot(tpc[0], track.tofNSigmaPi()), + std::hypot(tpc[1], track.tofNSigmaKa()), + std::hypot(tpc[2], track.tofNSigmaPr())}; + } + + int bestSpecies = Unidentified; + int compatibleSpecies = 0; + float bestNSigma = energy.nSigmaCut.value; + for (int species = 0; species < 3; ++species) { + const float absoluteNSigma = std::abs(selected[species]); + if (absoluteNSigma >= energy.nSigmaCut.value) { + continue; + } + ++compatibleSpecies; + if (absoluteNSigma < bestNSigma) { + bestNSigma = absoluteNSigma; + bestSpecies = species + 1; + } + } + if (energy.strictPid.value && compatibleSpecies > 1) { + return Unidentified; + } + return bestSpecies; + } + + double massForParticle(int pid) const + { + switch (pid) { + case Pion: + return o2::constants::physics::MassPionCharged; + case Kaon: + return o2::constants::physics::MassKPlus; + case Proton: + return o2::constants::physics::MassProton; + default: + return energy.unidentifiedMass.value; + } + } + + template + double transverseEnergy(TTrack const& track, int pid) const + { + if (energy.weightMode.value == PtWeight) { + return track.pt(); + } + const double mass = massForParticle(pid); + if (energy.weightMode.value == MtWeight) { + return std::hypot(track.pt(), mass); + } + return std::hypot(track.pt(), mass / std::cosh(track.eta())); + } + + template + void processCollision(TCollision const& collision, TTracks const& tracks, float centrality, float spectators) + { + float activity = centrality; + if (cfgActivityAxis.value == NGlobalTracksAxis) { + activity = static_cast(tracks.size()); + } else if (cfgActivityAxis.value == ZdcSpectatorAxis) { + activity = spectators; + } + if (!std::isfinite(activity)) { + return; + } + + gfw->Clear(); + for (const auto& track : tracks) { + const int pid = energy.weightMode.value == PtWeight ? Unidentified : particleId(track); + const double et = transverseEnergy(track, pid); + const double weff = efficiencyWeight(track.eta(), track.pt(), centrality, collision.posZ()); + const double wacc = acceptance ? acceptance->getNUA(track.phi(), track.eta(), collision.posZ()) : 1.; + if (!std::isfinite(et) || !std::isfinite(weff) || !std::isfinite(wacc) || et <= 0. || weff <= 0. || wacc <= 0.) { + continue; + } + gfw->Fill(track.eta(), 0, track.phi(), et * weff * wacc, gfwMask); + registry.fill(HIST("track/ptEta"), track.pt(), track.eta()); + registry.fill(HIST("track/energy"), et); + if (energy.weightMode.value != PtWeight) { + registry.fill(HIST("track/pid"), pid); + } + } + + registry.fill(HIST("event/centrality"), centrality); + registry.fill(HIST("event/activity"), activity); + registry.fill(HIST("event/vertex"), collision.posZ()); + fillFlowContainer(activity); + } + + void fillFlowContainer(float activity) + { + const double randomNumber = random->Rndm(); + for (std::size_t index = 0; index < corrConfigs.size(); ++index) { + const auto& config = corrConfigs[index]; + if (zeroHarmonicConfigs[index]) { + const double raw = gfw->Calculate(config, 0, false).real(); + if (std::isfinite(raw) && raw > 0.) { + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + } + continue; + } + + const double denominator = gfw->Calculate(config, 0, true).real(); + if (!std::isfinite(denominator) || denominator <= 0.) { + continue; + } + const double raw = gfw->Calculate(config, 0, false).real(); + if (!std::isfinite(raw)) { + continue; + } + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + flowContainer->FillProfile(config.Head.c_str(), activity, raw / denominator, denominator, randomNumber); + } + } + + void processRun2(CollisionsRun2::iterator const& collision, Tracks const& tracks, BCsRun2 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centRun2V0M(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2, "Process Run 2 AOD data", false); + + void processRun3(CollisionsRun3::iterator const& collision, Tracks const& tracks, BCsRun3 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(tracks.size()), collision.centFT0C(), spectators)) { + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centFT0C(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3, "Process Run 3 AOD data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 4889c684d8838bb7567f9de5168e3fc9c88d96d9 Mon Sep 17 00:00:00 2001 From: Noor Koster <82090643+cnkoster@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:09:43 +0200 Subject: [PATCH 186/648] [PWGCF] flowSP: add extra qa in MC + cleanup task (#18107) Co-authored-by: ALICE Action Bot --- PWGCF/Flow/TableProducer/zdcQVectors.cxx | 210 ++++++--- PWGCF/Flow/Tasks/flowSP.cxx | 576 +++++++++++++++-------- 2 files changed, 536 insertions(+), 250 deletions(-) diff --git a/PWGCF/Flow/TableProducer/zdcQVectors.cxx b/PWGCF/Flow/TableProducer/zdcQVectors.cxx index 9c1cce63a0b..0aade4713d6 100644 --- a/PWGCF/Flow/TableProducer/zdcQVectors.cxx +++ b/PWGCF/Flow/TableProducer/zdcQVectors.cxx @@ -111,7 +111,7 @@ struct ZdcQVectors { } EvSel; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgRecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_RecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Timestamp, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Timestamp", "CCDB directory for Timestamp recentering"); } extraTS; @@ -131,14 +131,12 @@ struct ZdcQVectors { O2_DEFINE_CONFIGURABLE(cfgVtxZ, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried") - O2_DEFINE_CONFIGURABLE(cfgEnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") - O2_DEFINE_CONFIGURABLE(cfgMeanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") O2_DEFINE_CONFIGURABLE(cfgMinEntriesSparseBin, int, 1000, "Minimal number of entries allowed in 4D recentering histogram to use for recentering.") - O2_DEFINE_CONFIGURABLE(cfgRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); O2_DEFINE_CONFIGURABLE(cfgFillHistRegistry, bool, true, "Fill common registry with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillCutAnalysis, bool, true, "Fill cut analysis with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillNothing, bool, false, "Disable ALL Histograms -> ONLY use to reduce memory"); O2_DEFINE_CONFIGURABLE(cfgNoGain, bool, true, "Do not apply gain correction to ZDC energy calibration"); + O2_DEFINE_CONFIGURABLE(cfgNIterationsAfterShift, int, 1, "Number of iterations to perform after shift"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAxy, float, 0.2, "Cut on DCA in the transverse direction (cm)"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAz, float, 0.2, "Cut on DCA in the longitudinal direction (cm)"); @@ -146,11 +144,19 @@ struct ZdcQVectors { O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmax, float, 10, "maximum pt (GeV/c)"); O2_DEFINE_CONFIGURABLE(cfgTrackSelsEta, float, 0.8, "eta cut"); + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_EnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Meanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Rec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Shift, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Shift", "CCDB directory for Shift ZDC"); + O2_DEFINE_CONFIGURABLE(cfgCCDBdir_ShiftRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/noGain/AfterShift", "CCDB directory for recentering after Shift ZDC"); + Configurable> cfgSelVec{"cfgSelVec", std::vector{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, "Put 1 for every event selection from SelectionCriteria that is used in flowSP"}; Configurable> cfgEvSelsMultPv{"cfgEvSelsMultPv", std::vector{2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}, "Multiplicity cuts (PV) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; Configurable> cfgEvSelsMult{"cfgEvSelsMult", std::vector{1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}, "Multiplicity cuts (Global) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", "0.0105 + 0.035 / TMath::Power(x,1.1)", 0, 100); + // define my..... // Filter collisionFilter = nabs(aod::collision::posZ) <; @@ -185,6 +191,7 @@ struct ZdcQVectors { kMeanv, kRec, kTimestamp, + kRecShift, nCalibModes }; @@ -204,10 +211,8 @@ struct ZdcQVectors { // keep track of calibration histos for each given step and iteration struct Calib { - std::vector calibList = std::vector(4, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp - std::vector calibfilesLoaded = std::vector(4, false); - int atStep = 0; - int atIteration = 0; + std::vector calibList = std::vector(5, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp, [4] recenter after shift Correction + std::vector calibfilesLoaded = std::vector(5, false); TProfile3D* shiftprofileC = nullptr; TProfile3D* shiftprofileA = nullptr; @@ -220,11 +225,13 @@ struct ZdcQVectors { uint64_t timestamp = 0; std::vector v = {0, 0, 0}; bool isSelected = 0; + int atIteration = 0; } cal; enum FillType { kBefore, - kAfter + kAfter, + kAfterShift }; void init(InitContext const&) @@ -270,6 +277,9 @@ struct ZdcQVectors { registry.add(Form("QA/before/hSPplaneA"), "hSPplaneA", kTH2D, {axisPsiA, axisCent}); registry.add(Form("QA/before/hSPplaneC"), "hSPplaneC", kTH2D, {axisPsiC, axisCent}); registry.add(Form("QA/before/hSPplaneFull"), "hSPplaneFull", kTH2D, {{100, -PI, PI}, axisCent}); + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("QA/before/", "QA/afterShift/"); + } for (const auto& side : sides) { registry.add(Form("recentering/before/hZN%s_Qx_vs_Qy", side), Form("hZN%s_Qx_vs_Qy", side), kTH2F, {axisQ, axisQ}); } @@ -294,7 +304,7 @@ struct ZdcQVectors { registry.add(Form("recentering/before/hQ%s%s_vs_vy", coord, side), Form("hQ%s%s_vs_vy", coord, side), {HistType::kTProfile, {axisVy}}); registry.add(Form("recentering/before/hQ%s%s_vs_vz", coord, side), Form("hQ%s%s_vs_vz", coord, side), {HistType::kTProfile, {axisVz}}); registry.add(Form("recentering/before/hQ%s%s_vs_timestamp", coord, side), Form("hQ%s%s_vs_timestamp", coord, side), {HistType::kTProfile, {axisTimestamp}}); - registry.add(Form("recentering/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{35, 0, 35}, axisQ}}); + registry.add(Form("recentering/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{100, 0, 100}, axisQ}}); } // end of capCOORDS } // end of sides @@ -377,6 +387,10 @@ struct ZdcQVectors { registry.add("CutAnalysis/hvertex_vy", "hvertex_vy", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); registry.add("CutAnalysis/hvertex_vz", "hvertex_vz", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); } + + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("recentering/before/", "recentering/afterShift/"); + } registry.addClone("recentering/before/", "recentering/after/"); registry.addClone("QA/before/", "QA/after/"); } @@ -614,7 +628,7 @@ struct ZdcQVectors { if (cfgFillNothing) { return; } - static constexpr std::array Time = {"before", "after"}; // todo move to struct like in flowSP + static constexpr std::array Time = {"before", "after", "afterShift"}; registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNA_Qx_vs_Qy"), qxa, qya); registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNC_Qx_vs_Qy"), qxc, qyc); @@ -675,11 +689,11 @@ struct ZdcQVectors { registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNC_Qy_vs_Centrality"), centrality, qyc); // add psi!! - double psiA = 1.0 * std::atan2(qxc, qxa); + double psiA = 1.0 * std::atan2(qya, qxa); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneA"), psiA, centrality, 1); - double psiC = 1.0 * std::atan2(qyc, qya); + double psiC = 1.0 * std::atan2(qyc, qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneC"), psiC, centrality, 1); - double psiFull = 1.0 * std::atan2(qxc + qyc, qxa + qya); + double psiFull = 1.0 * std::atan2(qya + qyc, qxa + qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneFull"), psiFull, centrality, 1); } @@ -698,7 +712,6 @@ struct ZdcQVectors { cal.calibfilesLoaded[cm] = true; LOGF(info, "Loaded calibration histos from %s", ccdb_dir.c_str()); if (cm == kRec) { - cal.atStep = 5; cal.atIteration = 5; } } @@ -722,7 +735,7 @@ struct ZdcQVectors { if (!hist) { LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); } - } else if (cm == kRec) { + } else if (cm == kRec || cm == kRecShift) { auto list = dynamic_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); if (!list) { LOGF(fatal, "No calibration list for iteration %i and step %i", iteration, step); @@ -731,8 +744,6 @@ struct ZdcQVectors { if (!hist) { LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); } - cal.atStep = step; - cal.atIteration = iteration; } if (!hist) { LOGF(fatal, "%s not available.. Abort..", objName); @@ -768,7 +779,7 @@ struct ZdcQVectors { bin = h->GetXaxis()->FindBin(TString::Format("%i", cal.runnumber)); } if (name.Contains("timestamp")) { - bin = h->GetXaxis()->FindBin(cal.timestamp); + bin = h->GetXaxis()->FindBin(cal.rsTimestamp); } calibConstant = h->GetBinContent(bin); } @@ -817,7 +828,6 @@ struct ZdcQVectors { std::vector cents; auto cent = collision.centFT0C(); - cents.push_back(collision.centFT0C()); if (cfgFT0Cvariant1) { @@ -853,6 +863,30 @@ struct ZdcQVectors { cal.runnumber = runnumber; cal.centrality = cent; + // load new calibrations for new runs only + if (runnumber != cal.lastRunNumber) { + cal.calibfilesLoaded[kEnergyCal] = false; + cal.calibList[kEnergyCal] = nullptr; + + cal.calibfilesLoaded[kMeanv] = false; + cal.calibList[kMeanv] = nullptr; + + cal.calibfilesLoaded[kRec] = false; + cal.calibList[kRec] = nullptr; + + cal.calibfilesLoaded[kTimestamp] = false; + cal.calibList[kTimestamp] = nullptr; + + cal.calibfilesLoaded[kRecShift] = false; + cal.calibList[kRecShift] = nullptr; + + cal.isShiftProfileFound = false; + cal.shiftprofileC = nullptr; + cal.shiftprofileA = nullptr; + + cal.atIteration = 0; + } + if (cfgFillHistRegistry && !cfgFillNothing) { registry.fill(HIST("QA/centrality_before"), cent); } @@ -891,16 +925,20 @@ struct ZdcQVectors { bool isZNChit = true; for (int i = 0; i < nTowers; ++i) { - if (i < nTowersPerSide && eZN[i] <= 0) + if (i < nTowersPerSide && eZN[i] <= 0) { isZNAhit = false; - if (i >= nTowersPerSide && eZN[i] <= 0) + } + if (i >= nTowersPerSide && eZN[i] <= 0) { isZNChit = false; + } } - if (zdcCol.energyCommonZNA() <= 0) + if (zdcCol.energyCommonZNA() <= 0) { isZNAhit = false; - if (zdcCol.energyCommonZNC() <= 0) + } + if (zdcCol.energyCommonZNC() <= 0) { isZNChit = false; + } // if ZNA or ZNC not hit correctly.. do not use event in q-vector calculation if (!isZNAhit || !isZNChit) { @@ -927,32 +965,13 @@ struct ZdcQVectors { } registry.fill(HIST("hEventCount"), evSel_CentCuts); - // load new calibrations for new runs only - if (runnumber != cal.lastRunNumber) { - cal.calibfilesLoaded[kEnergyCal] = false; - cal.calibList[kEnergyCal] = nullptr; - - cal.calibfilesLoaded[kMeanv] = false; - cal.calibList[kMeanv] = nullptr; - - cal.calibfilesLoaded[kRec] = false; - cal.calibList[kRec] = nullptr; - - cal.calibfilesLoaded[kTimestamp] = false; - cal.calibList[kTimestamp] = nullptr; - - cal.isShiftProfileFound = false; - cal.shiftprofileC = nullptr; - cal.shiftprofileA = nullptr; - } - // load the calibration histos for iteration 0 step 0 (Energy Calibration) if (!cfgNoGain) { - loadCalibrations(cfgEnergyCal.value, timestamp); + loadCalibrations(cfgCCDBDir_EnergyCal.value, timestamp); } // load the calibrations for the mean v - loadCalibrations(cfgMeanv.value, timestamp); + loadCalibrations(cfgCCDBDir_Meanv.value, timestamp); if (!cfgFillNothing && isEventSelected) { registry.get(HIST("vmean/hvertex_vx"))->Fill(Form("%d", runnumber), v[0]); @@ -1074,17 +1093,25 @@ struct ZdcQVectors { return; } - loadCalibrations(cfgRec.value, timestamp); + // Load in TList with all recentering histos + loadCalibrations(cfgCCDBDir_Rec.value, timestamp); - if (extraTS.cfgRecenterForTimestamp) { + // If CCDB dir given load recentering for extra step Timestamp + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { loadCalibrations(extraTS.cfgCCDBdir_Timestamp.value, timestamp); } + // If CCDB dir given load recentering after shift correction. + if (!cfgCCDBdir_ShiftRec.value.empty()) { + loadCalibrations(cfgCCDBdir_ShiftRec.value, timestamp); + } + std::array qRec(q); if (cal.atIteration == 0) { - if (cal.isSelected && cfgFillHistRegistry && isEventSelected) + if (cal.isSelected && cfgFillHistRegistry && isEventSelected) { fillCommonRegistry(q[0], q[1], q[2], q[3], cal.v, cent, rsTimestamp); + } spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), q[0], q[1], q[2], q[3], cal.isSelected, eventSelectionFlags); cal.lastRunNumber = runnumber; @@ -1136,7 +1163,7 @@ struct ZdcQVectors { pb++; } - if (extraTS.cfgRecenterForTimestamp) { + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { corrQxA.push_back(getCorrection(namesTS[0].Data(), it, 6)); corrQyA.push_back(getCorrection(namesTS[1].Data(), it, 6)); corrQxC.push_back(getCorrection(namesTS[2].Data(), it, 6)); @@ -1257,20 +1284,87 @@ struct ZdcQVectors { double qXcShift = std::hypot(qRec[2], qRec[3]) * std::cos(psiZDCCshift); double qYcShift = std::hypot(qRec[2], qRec[3]) * std::sin(psiZDCCshift); + if (!cal.calibfilesLoaded[kRecShift]) { + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; + } + // vector of 4 + corrQxA.clear(); + corrQyA.clear(); + corrQxC.clear(); + corrQyC.clear(); + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); - registry.fill(HIST("QA/centrality_after"), cent); - registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); - registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); - registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); - registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); } - spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); - qRec = {0, 0, 0, 0}; + for (int it = 1; it <= cfgNIterationsAfterShift; it++) { + corrQxA.push_back(getCorrection(names[0][0].Data(), it, 1)); + corrQyA.push_back(getCorrection(names[0][1].Data(), it, 1)); + corrQxC.push_back(getCorrection(names[0][2].Data(), it, 1)); + corrQyC.push_back(getCorrection(names[0][3].Data(), it, 1)); - cal.lastRunNumber = runnumber; - return; + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, qXaShift - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, qYaShift - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, qXcShift - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, qYcShift - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + pb++; + + for (int step = 2; step <= nSteps; step++) { + corrQxA.push_back(getCorrection(names[step - 1][0].Data(), it, step)); + corrQyA.push_back(getCorrection(names[step - 1][1].Data(), it, step)); + corrQxC.push_back(getCorrection(names[step - 1][2].Data(), it, step)); + corrQyC.push_back(getCorrection(names[step - 1][3].Data(), it, step)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + + pb++; + } + + double totalCorrectionQxAshift = std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0); + double totalCorrectionQyAshift = std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0); + double totalCorrectionQxCshift = std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0); + double totalCorrectionQyCshift = std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0); + + qXaShift -= totalCorrectionQxAshift; + qYaShift -= totalCorrectionQyAshift; + qXcShift -= totalCorrectionQxCshift; + qYcShift -= totalCorrectionQyCshift; + + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; + } LOGF(warning, "We return without saving table... -> THis is a problem"); cal.lastRunNumber = runnumber; diff --git a/PWGCF/Flow/Tasks/flowSP.cxx b/PWGCF/Flow/Tasks/flowSP.cxx index 9e04b351488..26b96ec9c80 100644 --- a/PWGCF/Flow/Tasks/flowSP.cxx +++ b/PWGCF/Flow/Tasks/flowSP.cxx @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ using namespace o2::framework::expressions; using namespace o2::aod::rctsel; // using namespace o2::analysis; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) struct FlowSP { @@ -130,6 +131,7 @@ struct FlowSP { O2_DEFINE_CONFIGURABLE(cCentMin, float, 0, "Minimum cenrality for selected events"); O2_DEFINE_CONFIGURABLE(cCentMax, float, 90, "Maximum cenrality for selected events"); O2_DEFINE_CONFIGURABLE(cFilterLeptons, bool, true, "Filter out leptons from MCGenerated by requiring |pdgCode| > 100"); + O2_DEFINE_CONFIGURABLE(cDoGeneratedInReco, bool, true, "Do MC Generated inside the MCReco process function on mcCollisions"); // NUA and NUE weights O2_DEFINE_CONFIGURABLE(cFillWeights, bool, true, "Fill NUA weights"); O2_DEFINE_CONFIGURABLE(cFillWeightsPOS, bool, true, "Fill NUA weights only for positive charges"); @@ -142,9 +144,7 @@ struct FlowSP { // Additional track Selections O2_DEFINE_CONFIGURABLE(cTrackSelsUseAdditionalTrackCut, bool, false, "Bool to enable Additional Track Cut"); O2_DEFINE_CONFIGURABLE(cTrackSelsDoDCApt, bool, true, "Apply Pt dependent DCAz cut"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCApt1, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets a"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCApt2, float, 0.035, "DcaZ < const + (a * b) / pt^1.1 -> this sets b"); - O2_DEFINE_CONFIGURABLE(cTrackSelsDCAptConsMin, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets const"); + O2_DEFINE_CONFIGURABLE(cTrackSelsDCAfunc, std::string, "0.0105 + 0.035 / TMath::Power(x,1.1)", "Function for pT-dependent DCA-cut (Default y = 0.0105 + 0.035 / TMath::Power(x,1.1))"); O2_DEFINE_CONFIGURABLE(cTrackSelsPIDNsigma, float, 2.0, "nSigma cut for PID"); O2_DEFINE_CONFIGURABLE(cTrackSelDoTrackQAvsCent, bool, true, "Do track selection QA plots as function of centrality"); // harmonics for v coefficients @@ -176,8 +176,8 @@ struct FlowSP { Filter trackFilter = nabs(aod::track::eta) < cfg.cTrackSelsEta && aod::track::pt > cfg.cTrackSelsPtmin&& aod::track::pt < cfg.cTrackSelsPtmax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true) || cfg.cIsMCReco) && nabs(aod::track::dcaXY) < cfg.cTrackSelsDCAxy&& nabs(aod::track::dcaZ) < cfg.cTrackSelsDCAz; Filter trackFilterMC = nabs(aod::mcparticle::eta) < cfg.cTrackSelsEta && aod::mcparticle::pt > cfg.cTrackSelsPtmin&& aod::mcparticle::pt < cfg.cTrackSelsPtmax; using GeneralCollisions = soa::Join; - using UnfilteredTracksPID = soa::Join; - using UnfilteredTracks = soa::Join; + using UnfilteredTracksPID = soa::Join; + using UnfilteredTracks = soa::Join; using UsedTracks = soa::Filtered; using UsedTracksPID = soa::Filtered; @@ -197,16 +197,16 @@ struct FlowSP { Preslice trackPerCollision = aod::track::collisionId; // Connect to ccdb - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; // struct to hold the correction histos/ struct Config { - std::vector mEfficiency = {}; - std::vector mEfficiency2D = {}; - std::vector mEfficiency3D = {}; - std::vector mAcceptance = {}; - std::vector mAcceptance2D = {}; + std::vector mEfficiency; + std::vector mEfficiency2D; + std::vector mEfficiency3D; + std::vector mAcceptance; + std::vector mAcceptance2D; bool correctionsLoaded = false; int lastRunNumber = 0; @@ -290,6 +290,9 @@ struct FlowSP { std::unique_ptr fMultCutHigh = nullptr; std::unique_ptr fMultMultPVCut = nullptr; + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", cfg.cTrackSelsDCAfunc.value.c_str(), 0, 100); + enum SelectionCriteria { evSel_FilteredEvent, evSel_sel8, @@ -312,12 +315,12 @@ struct FlowSP { trackSel_ZeroCharge, trackSel_Eta, trackSel_Pt, + trackSel_NCls, + trackSel_TPCBoundary, trackSel_DCAxy, trackSel_DCAz, - trackSel_GlobalTracks, - trackSel_NCls, trackSel_FshCls, - trackSel_TPCBoundary, + trackSel_GlobalTracks, trackSel_ParticleWeights, nTrackSelections }; @@ -349,9 +352,9 @@ struct FlowSP { nParticleTypes }; - static constexpr std::string_view Charge[] = {"incl/", "pos/", "neg/"}; - static constexpr std::string_view Species[] = {"", "pion/", "kaon/", "proton/"}; - static constexpr std::string_view Time[] = {"before/", "after/"}; + static constexpr std::array Charge = {"incl/", "pos/", "neg/"}; + static constexpr std::array Species = {"", "pion/", "kaon/", "proton/"}; + static constexpr std::array Time = {"before/", "after/"}; void init(InitContext const&) { @@ -499,8 +502,9 @@ struct FlowSP { histos.add("incl/QA/after/hPhi_Eta_Pt_corrected", "", kTH3D, {axisPhi, axisEta, axisPt}); } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { histos.addClone("incl/QA/after/", "incl/QA/before/"); + } } if (cfg.cFillPIDQA && doprocessDataPID) { @@ -548,9 +552,22 @@ struct FlowSP { } if (doprocessMCReco) { - registry.add("trackMCReco/after/incl/hIsPhysicalPrimary", "", {HistType::kTH3D, {{2, 0, 2}, axisCentrality, axisPt}}); - registry.get(HIST("trackMCReco/after/incl/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(1, "Secondary"); - registry.get(HIST("trackMCReco/after/incl/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(2, "Primary"); + registry.add("trackMCReco/after/incl/hIsPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/after/incl/hIsNotPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/incl/hPtMCPtTrack", "", {HistType::kTH2D, {axisPt, axisPt}}); + registry.add("trackMCReco/incl/hEtaMCEtaTrack", "", {HistType::kTH2D, {axisEta, axisEta}}); + registry.add("trackMCReco/incl/hPtPerTrackSelection", "", {HistType::kTH2D, {axisPt, {nTrackSelections, 0, nTrackSelections}}}); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Eta + 1, "Eta"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Pt + 1, "Pt"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAxy + 1, "DCAxy"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAz + 1, "DCAz"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_GlobalTracks + 1, "GlobalTracks"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_NCls + 1, "nClusters TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_FshCls + 1, "Frac. sh. Cls TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_TPCBoundary + 1, "TPC Boundary"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ZeroCharge + 1, "Only charged"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ParticleWeights + 1, "Apply weights"); + registry.add("trackMCReco/hTrackSize_unFiltered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); registry.add("trackMCReco/hTrackSize_Filtered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); registry.add("trackMCReco/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); @@ -560,6 +577,8 @@ struct FlowSP { // Clone into particles and before/after registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/pos/"); registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/pos/"); registry.addClone("trackMCReco/after/", "trackMCReco/before/"); } @@ -681,14 +700,15 @@ struct FlowSP { registry.addClone("incl/", "neg/"); } } - - } else if (doprocessMCGen) { + } + if (doprocessMCGen || doprocessMCReco) { registry.add("trackMCGen/nCollReconstructedPerMcCollision", "", {HistType::kTH1D, {{10, -5, 5}}}); registry.add("trackMCGen/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_proton", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_pion", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_kaon", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/phi_eta_vtxZ_gen", "", {HistType::kTH3D, {axisPhi, axisEta, axisVz}}); + registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/pos/"); registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/neg/"); registry.addClone("trackMCGen/after/", "trackMCGen/before/"); @@ -768,8 +788,9 @@ struct FlowSP { int etaind = hNUA->GetYaxis()->FindBin(eta); int vzind = hNUA->GetZaxis()->FindBin(vtxz); float weight = hNUA->GetBinContent(xind, etaind, vzind); - if (weight != 0) + if (weight != 0) { return 1. / weight; + } return 1; } @@ -809,13 +830,11 @@ struct FlowSP { } } - if (nIdentified == 0) { - return kUnidentified; // No PID match found - } else if (nIdentified == 1) { + if (nIdentified == 1) { return valPID; - } else { - return kUnidentified; // Multiple PID matches found } + + return kUnidentified; // Multiple PID matches found } int getMagneticField(uint64_t timestamp) @@ -850,28 +869,30 @@ struct FlowSP { void loadCorrections(uint64_t timestamp) { // corrections saved on CCDB as TList {incl, pos, neg} of GFWWeights (acc) TH1D (eff) objects! - if (conf.correctionsLoaded) + if (conf.correctionsLoaded) { return; + } int nWeights = 3; if (cfg.cUseNUA1D) { - if (cfg.cCCDB_NUA.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights"))); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_positive"))); - conf.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_negative"))); + if (!cfg.cCCDB_NUA.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_positive"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_negative"))); int sizeAcc = conf.mAcceptance.size(); - if (sizeAcc < nWeights) + if (sizeAcc < nWeights) { LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); - else + } else { LOGF(info, "Loaded acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUA empty! No corrections loaded"); } } else if (cfg.cUseNUA2D) { - if (cfg.cCCDB_NUA.value.empty() == false) { - TH3D* hNUA2D = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); + if (!cfg.cCCDB_NUA.value.empty()) { + auto* hNUA2D = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); if (!hNUA2D) { LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); } else { @@ -883,39 +904,41 @@ struct FlowSP { } } // Get Efficiency correction - if (cfg.cCCDB_NUE.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE, timestamp); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE, timestamp); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency.size(); - if (sizeEff < nWeights) + if (sizeEff < nWeights) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); - else + } else { LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUE empty! No corrections loaded"); } // Get Efficiency correction - if (cfg.cCCDB_NUE2D.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE2D, timestamp); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE2D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE2D, timestamp); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency2D.size(); - if (sizeEff < nWeights) + if (sizeEff < nWeights) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); - else + } else { LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } } else { LOGF(info, "cfg.cCCDB_NUE2 empty! No corrections loaded"); } - if (cfg.cCCDB_NUE3D.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE3D, timestamp); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - conf.mEfficiency3D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); + if (!cfg.cCCDB_NUE3D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE3D, timestamp); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); int sizeEff = conf.mEfficiency3D.size(); if (sizeEff < nWeights) LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); @@ -977,15 +1000,28 @@ struct FlowSP { return true; } + template + inline void fillPrimaryHistos(const McParticleObject& mcparticle) + { + + if (mcparticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } else { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsNotPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } + } + template bool eventSelected(const TCollision& collision, const int& multTrk) { - if (!collision.sel8()) + if (!collision.sel8()) { return 0; + } histos.fill(HIST("hEventCount"), evSel_sel8); - if (cfg.cEvtUseRCTFlagChecker && !rctChecker(collision)) + if (cfg.cEvtUseRCTFlagChecker && !rctChecker(collision)) { return 0; + } histos.fill(HIST("hEventCount"), evSel_RCTFlagsZDC); // Occupancy @@ -994,8 +1030,8 @@ struct FlowSP { if (occupancy > cfg.cEvSelsMaxOccupancy || occupancy < cfg.cEvSelsMinOccupancy) { return 0; } - histos.fill(HIST("hEventCount"), evSel_occupancy); } + histos.fill(HIST("hEventCount"), evSel_occupancy); if (cfg.cEvSelsNoSameBunchPileupCut) { if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -1003,37 +1039,37 @@ struct FlowSP { // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } + histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); if (cfg.cEvSelsIsGoodZvtxFT0vsPV) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); if (cfg.cEvSelsNoCollInTimeRangeStandard) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); if (cfg.cEvSelsNoCollInTimeRangeNarrow) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); } + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); if (cfg.cEvSelsIsVertexITSTPC) { if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { // selects collisions with at least one ITS-TPC track, and thus rejects vertices built from ITS-only tracks return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); } + histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); if (cfg.cEvSelsIsGoodITSLayersAll) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { @@ -1041,14 +1077,14 @@ struct FlowSP { // https://indico.cern.ch/event/1493023/ (09-01-2025) return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); if (cfg.cEvSelsIsGoodITSLayer0123) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); if (cfg.cEvSelsUseAdditionalEventCut) { float vtxz = -999; @@ -1057,25 +1093,30 @@ struct FlowSP { float zRes = std::sqrt(collision.covZZ()); float minzRes = 0.25; int maxNumContrib = 20; - if (zRes > minzRes && collision.numContrib() < maxNumContrib) + if (zRes > minzRes && collision.numContrib() < maxNumContrib) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); - if (vtxz > cfg.cEvSelsVtxZ || vtxz < -cfg.cEvSelsVtxZ) + if (vtxz > cfg.cEvSelsVtxZ || vtxz < -cfg.cEvSelsVtxZ) { return 0; - if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) + } + if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) { return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) + } + if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) { return 0; - if (multTrk < fMultCutLow->Eval(collision.centFT0C())) + } + if (multTrk < fMultCutLow->Eval(collision.centFT0C())) { return 0; - if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) + } + if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) { return 0; - - histos.fill(HIST("hEventCount"), evSel_MultCuts); + } } + histos.fill(HIST("hEventCount"), evSel_MultCuts); return 1; } @@ -1083,64 +1124,131 @@ struct FlowSP { template bool trackSelected(const TrackObject& track, const int& field) { - if (std::fabs(track.eta()) > cfg.cTrackSelsEta) + auto fillSpectraStudyMCReco = [&](TrackSelections sel) { + if constexpr (o2::framework::has_type_v) { + if (!track.has_mcParticle()) { + return; + } + const auto mcParticle = track.template mcParticle_as(); + if (doprocessMCReco && mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/incl/hPtPerTrackSelection"), mcParticle.pt(), sel); + if (track.sign() > 0) { + registry.fill(HIST("trackMCReco/pos/hPtPerTrackSelection"), mcParticle.pt(), sel); + } else if (track.sign() < 0) { + registry.fill(HIST("trackMCReco/neg/hPtPerTrackSelection"), mcParticle.pt(), sel); + } + } + } + }; + + if (std::fabs(track.eta()) > cfg.cTrackSelsEta) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_Eta); + fillSpectraStudyMCReco(trackSel_Eta); - if (track.pt() < cfg.cTrackSelsPtmin || track.pt() > cfg.cTrackSelsPtmax) + if (track.pt() < cfg.cTrackSelsPtmin || track.pt() > cfg.cTrackSelsPtmax) { return false; - + } histos.fill(HIST("hTrackCount"), trackSel_Pt); + fillSpectraStudyMCReco(trackSel_Pt); - if (track.dcaXY() > cfg.cTrackSelsDCAxy) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAxy); - - if (track.dcaZ() > cfg.cTrackSelsDCAz) - return false; - - if (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > (cfg.cTrackSelsDCAptConsMin + (cfg.cTrackSelsDCApt1 * cfg.cTrackSelsDCApt2) / (std::pow(track.pt(), 1.1)))) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAz); - - if (track.tpcNClsCrossedRows() < cfg.cTrackSelsNcls) + if (track.tpcNClsCrossedRows() < cfg.cTrackSelsNcls) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_NCls); - - if (track.tpcFractionSharedCls() > cfg.cTrackSelsFshcls) - return false; - histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_NCls); double phimodn = track.phi(); - if (field < 0) // for negative polarity field + if (field < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (track.sign() < 0) // for negative charge + } + if (track.sign() < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (phimodn < 0) + } + if (phimodn < 0) { LOGF(warning, "phi < 0: %g", phimodn); + } phimodn += o2::constants::math::PI / 18.0; // to center gap in the middle phimodn = fmod(phimodn, o2::constants::math::PI / 9.0); - if (cfg.cFillTrackQA && cfg.cFillQABefore) + if (cfg.cFillTrackQA && cfg.cFillQABefore) { histos.fill(HIST("incl/QA/before/pt_phi"), track.pt(), phimodn); + } if (cfg.cTrackSelsUseAdditionalTrackCut) { - if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) + if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) { return false; // reject track + } } - if (cfg.cFillTrackQA) + if (cfg.cFillTrackQA) { histos.fill(HIST("incl/QA/after/pt_phi"), track.pt(), phimodn); + } histos.fill(HIST("hTrackCount"), trackSel_TPCBoundary); + fillSpectraStudyMCReco(trackSel_TPCBoundary); + + // Only fill primary/secondary histos for MC data. + if constexpr (o2::framework::has_type_v) { + auto mcParticle = track.template mcParticle_as(); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + fillSpectraStudyMCReco(trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + fillSpectraStudyMCReco(trackSel_DCAz); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + } else { // Only apply DCA-cuts for data + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + } + + if (track.tpcFractionSharedCls() > cfg.cTrackSelsFshcls) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_FshCls); + + if (!track.isGlobalTrack()) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_GlobalTracks); + fillSpectraStudyMCReco(trackSel_GlobalTracks); + return true; } template inline void fillEventQA(const CollisionObject& collision, const TracksObject& tracks) { - if (!cfg.cFillEventQA) + if (!cfg.cFillEventQA) { return; + } histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentFT0C"), collision.centFT0C(), spm.centWeight); histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentNGlobal"), collision.centNGlobal(), spm.centWeight); @@ -1253,8 +1361,9 @@ struct FlowSP { template inline void fillTrackQA(const TrackObject& track) { - if (!cfg.cFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } double weight = spm.wacc[ct][par] * spm.weff[ct][par] * spm.centWeight; @@ -1282,8 +1391,9 @@ struct FlowSP { template inline void fillPIDQA(const TrackObject& track) { - if (!cfg.cFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } if constexpr (framework::has_type_v) { histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTOF_pt"), track.pt(), track.tofNSigmaPi()); histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTPC_pt"), track.pt(), track.tpcNSigmaPi()); @@ -1332,17 +1442,6 @@ struct FlowSP { } } - template - inline void fillPrimaryHistos(const McParticleObject& mcparticle) - { - - if (!mcparticle.isPhysicalPrimary()) { - registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), 0, spm.centrality, mcparticle.pt()); - } else { - registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), 1, spm.centrality, mcparticle.pt()); - } - } - template void fillAllQA(const TrackObject& track) { @@ -1381,27 +1480,34 @@ struct FlowSP { LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)conf.mAcceptance.size()); } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, tracks); + } loadCorrections(bc.timestamp()); spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1450,8 +1556,9 @@ struct FlowSP { histos.fill(HIST("QA/hFullEvPlaneRes"), spm.centrality, -1 * std::cos(spm.psiA - spm.psiC)); } - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); @@ -1459,12 +1566,12 @@ struct FlowSP { // Only load once! // If not loaded set to 1 - if (cfg.cCCDBdir_QQ.value.empty() == false) { + if (!cfg.cCCDBdir_QQ.value.empty()) { if (!conf.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); - conf.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - conf.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - conf.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); conf.clQQ = true; } spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); @@ -1473,19 +1580,20 @@ struct FlowSP { } double evPlaneRes = 1.; - if (cfg.cCCDBdir_SP.value.empty() == false) { + if (!cfg.cCCDBdir_SP.value.empty()) { if (!conf.clEvPlaneRes) { conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); conf.clEvPlaneRes = true; } evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfg.cCCDBdir_centrality.value.empty() == false) { + if (!cfg.cCCDBdir_centrality.value.empty()) { if (!conf.clCentrality) { conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); conf.clCentrality = true; @@ -1523,8 +1631,9 @@ struct FlowSP { for (const auto& track : tracks) { - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); @@ -1534,11 +1643,12 @@ struct FlowSP { fillAllQA(track); } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } spm.meanPtWeight = 1.0; - if (cfg.cCCDBdir_meanPt.value.empty() == false) { + if (!cfg.cCCDBdir_meanPt.value.empty()) { if (!conf.clMeanPt) { conf.hMeanPt = ccdb->getForTimeStamp(cfg.cCCDBdir_meanPt.value, bc.timestamp()); conf.clMeanPt = true; @@ -1573,10 +1683,12 @@ struct FlowSP { } // Set weff and wacc for inclusive, negative and positive hadrons - if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; - if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + } + if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ParticleWeights); @@ -1713,20 +1825,26 @@ struct FlowSP { spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1745,19 +1863,20 @@ struct FlowSP { // https://twiki.cern.ch/twiki/pub/ALICE/DirectedFlowAnalysisNote/vn_ZDC_ALICE_INT_NOTE_version02.pdf spm.psiFull = 1.0 * std::atan2(spm.qyA + spm.qyC, spm.qxA + spm.qxC); - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } // Load correlations and SP resolution needed for Scalar Product and event plane methods. // Only load once! // If not loaded set to 1 - if (cfg.cCCDBdir_QQ.value.empty() == false) { + if (!cfg.cCCDBdir_QQ.value.empty()) { if (!conf.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); - conf.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - conf.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - conf.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); conf.clQQ = true; } spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); @@ -1766,19 +1885,20 @@ struct FlowSP { } double evPlaneRes = 1.; - if (cfg.cCCDBdir_SP.value.empty() == false) { + if (!cfg.cCCDBdir_SP.value.empty()) { if (!conf.clEvPlaneRes) { conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); conf.clEvPlaneRes = true; } evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfg.cCCDBdir_centrality.value.empty() == false) { + if (!cfg.cCCDBdir_centrality.value.empty()) { if (!conf.clCentrality) { conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); conf.clCentrality = true; @@ -1799,8 +1919,9 @@ struct FlowSP { histos.fill(HIST("hPIDcounts"), trackPID, track.pt()); - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); @@ -1822,8 +1943,9 @@ struct FlowSP { } } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } // constrain angle to 0 -> [0,0+2pi] auto phi = RecoDecay::constrainAngle(track.phi(), 0); @@ -1913,7 +2035,7 @@ struct FlowSP { PROCESS_SWITCH(FlowSP, processDataPID, "Process analysis for non-derived data with PID", false); - void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, aod::McParticles const&) + void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, MCs const& McParts, aod::McCollisions const&) { auto bc = collision.template bc_as(); int standardMagField = 99999; @@ -1921,28 +2043,34 @@ struct FlowSP { spm.vz = collision.posZ(); spm.centrality = collision.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, filteredTracks); + } - if (!eventSelected(collision, filteredTracks.size())) + if (!eventSelected(collision, filteredTracks.size())) { return; + } - if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); if (!collision.has_mcCollision()) { - LOGF(info, "No mccollision found for this collision"); return; } @@ -1951,15 +2079,17 @@ struct FlowSP { registry.fill(HIST("trackMCReco/hTrackSize_unFiltered"), tracks.size(), spm.centrality); registry.fill(HIST("trackMCReco/hTrackSize_Filtered"), filteredTracks.size(), spm.centrality); - for (const auto& track : filteredTracks) { + for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } - auto mcParticle = track.mcParticle(); + auto mcParticle = track.mcParticle_as(); - if (track.sign() == 0.0) + if (track.sign() == 0.0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); if (cfg.cFillWithMCParticle) { @@ -1993,24 +2123,23 @@ struct FlowSP { } } - fillPrimaryHistos(mcParticle); - if (spm.charge == kPositive) { - fillPrimaryHistos(mcParticle); - } else { - fillPrimaryHistos(mcParticle); + if (!trackSelected(track, field)) { + continue; } - if (!trackSelected(track, field)) + if (!mcParticle.isPhysicalPrimary()) { continue; + } - fillPrimaryHistos(mcParticle); + registry.fill(HIST("trackMCReco/incl/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/incl/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); if (spm.charge == kPositive) { - fillPrimaryHistos(mcParticle); - } else { - fillPrimaryHistos(mcParticle); + registry.fill(HIST("trackMCReco/pos/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/pos/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); + } else if (spm.charge == kNegative) { + registry.fill(HIST("trackMCReco/neg/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/neg/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); } - if (!mcParticle.isPhysicalPrimary()) - continue; if (cfg.cFillWithMCParticle) { fillMCPtHistos(mcParticle, mcParticle.pdgCode()); @@ -2035,6 +2164,60 @@ struct FlowSP { } } // end of track loop + + if (cfg.cDoGeneratedInReco) { + + auto mcCollision = collision.mcCollision(); + float vtxz = mcCollision.posZ(); + + // get McParticles which belong to mccollision + auto partSlice = McParts.sliceBy(partPerMcCollision, mcCollision.globalIndex()); + + for (const auto& particle : partSlice) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + auto pdgCode = particle.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + + if (std::abs(pdgInfo->Charge()) < 1) { + continue; + } + + spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; + + int minVal = 100; + if (cfg.cFilterLeptons && std::abs(pdgCode) < minVal) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/before/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/before/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/after/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/after/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + } + } } PROCESS_SWITCH(FlowSP, processMCReco, "Process analysis for MC reconstructed events", false); @@ -2064,17 +2247,22 @@ struct FlowSP { auto filteredTrackSlice = filteredTracks.sliceBy(trackPerCollision, col.globalIndex()); spm.centrality = col.centFT0C(); - if (cfg.cCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = col.centFT0CVariant1(); - if (cfg.cCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = col.centFT0M(); - if (cfg.cCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = col.centFV0A(); - if (cfg.cCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = col.centNGlobal(); + } - if (cfg.cFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(col, filteredTrackSlice); + } if (trackSlice.size() < 1) { colSelected = false; @@ -2095,20 +2283,23 @@ struct FlowSP { } // leave reconstructed collision loop - if (!colSelected) + if (!colSelected) { continue; + } float vtxz = mcCollision.posZ(); for (const auto& particle : partSlice) { - if (!particle.isPhysicalPrimary()) + if (!particle.isPhysicalPrimary()) { continue; + } auto pdgCode = particle.pdgCode(); auto pdgInfo = pdg->GetParticle(pdgCode); - if (std::abs(pdgInfo->Charge()) < 1) + if (std::abs(pdgInfo->Charge()) < 1) { continue; + } spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; @@ -2127,8 +2318,9 @@ struct FlowSP { registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { continue; + } fillMCPtHistos(particle, pdgCode); @@ -2139,7 +2331,7 @@ struct FlowSP { } else { registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - } + } // end of parts } } PROCESS_SWITCH(FlowSP, processMCGen, "Process analysis for MC generated events", false); From 9391c67b3d79422e24c366df33dae4de0d802e0d Mon Sep 17 00:00:00 2001 From: Yiping Wang <142303052+ypwangg@users.noreply.github.com> Date: Tue, 6 Oct 2026 17:45:47 +0800 Subject: [PATCH 187/648] [PWGDQ] code development for dq-eff-direct for dilepton track track (#18176) Co-authored-by: ALICE Action Bot --- PWGDQ/Core/VarManager.h | 68 ++- PWGDQ/DataModel/ReducedInfoTables.h | 2 + PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 538 +++++++++++++++++- 3 files changed, 578 insertions(+), 30 deletions(-) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index fa9ae2468a8..c7eaf3b6edf 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -6734,6 +6734,11 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l float mlepton2 = o2::constants::physics::MassElectron; float mtrack1 = o2::constants::physics::MassPionCharged; float mtrack2 = o2::constants::physics::MassPionCharged; + float mMother = 3.686; + + if constexpr (candidateType == kXtoJpsiPiPi) { + mMother = o2::constants::physics::MassX3872; + } ROOT::Math::PtEtaPhiMVector v1(lepton1.pt(), lepton1.eta(), lepton1.phi(), mlepton1); ROOT::Math::PtEtaPhiMVector v2(lepton2.pt(), lepton2.eta(), lepton2.phi(), mlepton2); @@ -6747,26 +6752,30 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { // create covariance matrix - std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; - std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), - lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), - lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; - o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; - std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; - std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), - lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), - lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; - o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; - std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; - std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), - track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), - track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; - o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; - std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; - std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), - track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), - track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; - o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; + o2::track::TrackParCov pars1 = getTrackParCov(lepton1); + o2::track::TrackParCov pars2 = getTrackParCov(lepton2); + o2::track::TrackParCov pars3 = getTrackParCov(track1); + o2::track::TrackParCov pars4 = getTrackParCov(track2); + // std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; + // std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), + // lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), + // lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; + // std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; + // std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), + // lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), + // lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; + // std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; + // std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), + // track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), + // track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; + // std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; + // std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), + // track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), + // track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; procCodeDilepton = VarManager::fgFitterTwoProngBarrel.process(pars1, pars2); // create dilepton track @@ -6775,7 +6784,7 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l procCodeDileptonTrackTrack = VarManager::fgFitterFourProngBarrel.process(pars1, pars2, pars3, pars4); // fill values - if (procCodeDilepton == 0 && procCodeDileptonTrackTrack == 0) { + if (procCodeDilepton == 0 || procCodeDileptonTrackTrack == 0) { // TODO: set the other variables to appropriate values and return values[kVertexingLxy] = -999.; values[kVertexingLxyz] = -999.; @@ -6811,6 +6820,7 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + values[kVertexingChi2PCA] = fgFitterFourProngBarrel.getChi2AtPCACandidate(); values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); @@ -6823,11 +6833,11 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); - values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxy] = values[kVertexingLxy] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxy] = values[kVertexingLxy] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauzErr] = values[kVertexingLzErr] * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauzErr] = values[kVertexingLzErr] * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxyErr] = values[kVertexingLxyErr] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v1234.Px() + (secondaryVertex[1] - collision.posY()) * v1234.Py() + @@ -6841,9 +6851,9 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()) + ((secondaryVertex[2] - collision.posZ()) * v1234.Pz()); values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py()) + (v1234.Pz() * v1234.Pz())); - values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v1234.M() / TMath::Abs(v1234.Pz()); - values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v1234.M() / (v1234.Pt()); - values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v1234.M() / (v1234.P()); + values[kVertexingTauzProjected] = values[kVertexingLzProjected] * mMother / TMath::Abs(v1234.Pz()); + values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * mMother / (v1234.Pt()); + values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * mMother / (v1234.P()); } else { KFParticle lepton1KF; // lepton1 KFParticle lepton2KF; // lepton2 @@ -6983,7 +6993,7 @@ void VarManager::FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& trac double defaultDileptonMass = o2::constants::physics::MassJPsi; double hadronMass1 = o2::constants::physics::MassPionCharged; double hadronMass2 = o2::constants::physics::MassPionCharged; - if (candidateType == kXtoJpsiPiPi) { + if (candidateType == kXtoJpsiPiPi || candidateType == kPsi2StoJpsiPiPi) { defaultDileptonMass = o2::constants::physics::MassJPsi; hadronMass1 = o2::constants::physics::MassPionCharged; hadronMass2 = o2::constants::physics::MassPionCharged; diff --git a/PWGDQ/DataModel/ReducedInfoTables.h b/PWGDQ/DataModel/ReducedInfoTables.h index ffd96c9b267..eeed55d4886 100644 --- a/PWGDQ/DataModel/ReducedInfoTables.h +++ b/PWGDQ/DataModel/ReducedInfoTables.h @@ -1198,6 +1198,8 @@ DECLARE_SOA_COLUMN(VertexingLxyProjected, vertexingLxyProjected, float); //! DECLARE_SOA_COLUMN(VertexingLxyzProjected, vertexingLxyzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauzProjected, vertexingTauzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauxyProjected, vertexingTauxyProjected, float); //! +DECLARE_SOA_BITMAP_COLUMN(DileptonFilterMap, dileptonFilterMap, 32); //! +DECLARE_SOA_BITMAP_COLUMN(QuadFilterMap, quadFilterMap, 32); //! } // namespace dileptonTrackTrackCandidate DECLARE_SOA_TABLE(DileptonTrackTrackCandidates, "AOD", "RTDQUADPLET", //! diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index ba62d644f59..54e22436ad8 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -3189,6 +3189,541 @@ struct AnalysisDileptonTrack { PROCESS_SWITCH(AnalysisDileptonTrack, processDummy, "Dummy function", true); }; +struct AnalysisDileptonTrackTrack { + OutputObj fOutputList{"output"}; + + Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigDileptonCut{"cfgDileptonCut", "pairJpsi2", "Dilepton cut"}; + Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; + Configurable fConfigMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable fConfigDileptonMCRecSignal{"cfgDileptonMCRecSignal", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigTrackPtMin{"cfgTrackPtMin", 0.15f, "Minimum pt of tracks to be used in the quadruplet"}; + Configurable fConfigTrackEtaAbs{"cfgTrackEtaAbs", 0.9f, "Maximum absolute eta of tracks to be used in the quadruplet"}; + Configurable fConfigDileptonPtMin{"cfgDileptonPtMin", 1.0f, "Minimum pt of dileptons to be used in the quadruplet"}; + Configurable fConfigDileptonEtaAbs{"cfgDileptonEtaAbs", 0.9f, "Maximum absolute eta of dileptons to be used in the quadruplet"}; + Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; + Configurable fConfigAddQuadrupletHistogram{"cfgAddQuadrupletHistogram", "xtojpsipipi", "Comma separated list of histograms"}; + + Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; + // Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + // Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + // Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigDileptonLxyCut{"cfgDileptonLxyCut", -999.f, "Dilepton Lxy cut for secondary vertex"}; + + struct : ConfigurableGroup { + Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + } fConfigCCDBOptions; + + Produces DileptonTrackTrackTable; + + int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + // cut name setting + TString fTrackCutName1; + TString fTrackCutName2; + std::vector fTrackCutNames; + uint32_t fTrackCutBitMap1; + uint32_t fTrackCutBitMap2; + bool fIsSameTrackCut = false; + AnalysisCompositeCut fDileptonCut; + std::vector fQuadrupletCutNames; + std::vector fQuadrupletCuts; + + Service fCCDB; + o2::ccdb::CcdbApi fCCDBApi; + + Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); + Filter dileptonFilter = aod::reducedpair::sign == 0 && aod::reducedpair::lxy > fConfigDileptonLxyCut; + Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > static_cast(0); + + constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map + + float* fValuesQuadruplet; + HistogramManager* fHistMan; + + // o2::analysis::QuadMlResponse fQuadMlResponse; + // std::vector fOutputMlQuad; + + std::vector fRecMCSignals; + std::vector fGenMCSignals; + + void init(o2::framework::InitContext& context) + { + bool isX3872 = context.mOptions.get("processX3872"); + bool isPsi2S = context.mOptions.get("processPsi2S"); + bool isMCGen = context.mOptions.get("processMCGen") || context.mOptions.get("processMCGenWithEventSelection"); + bool isDummy = context.mOptions.get("processDummy"); + + if (isDummy) { + if (isPsi2S || isX3872 || isMCGen) { + LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!"; + return; + } else { + LOG(info) << "Dummy function is enabled. Skipping the rest of the init function"; + return; + } + } + + fCurrentRun = 0; + + fCCDB->setURL(fConfigCCDBOptions.fConfigCcdbUrl.value); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + fCCDB->setCreatedNotAfter(fConfigCCDBOptions.fConfigNoLaterThan.value); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + fCCDB->get(fConfigCCDBOptions.fConfigGeoPath); + } + + fValuesQuadruplet = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(true); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString sigNamesStr = fConfigMCRecSignals.value; + std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); + if (!sigNamesStr.IsNull()) { + for (int isig = 0; isig < objRecSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objRecSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 4) { + LOG(fatal) << "Signal at reconstructed level requested (" << sig->GetName() << ") " << "does not have 4 prongs! Fix it"; + } + fRecMCSignals.push_back(sig); + } else { + LOG(fatal) << "Signal at reconstructed level requested (" << objRecSigArray->At(isig)->GetName() << ") " << "could not be retrieved from the library! -> skipped"; + } + } + } + + TString sigGenNamesStr = fConfigMCGenSignals.value; + std::unique_ptr objGenSigArray(sigGenNamesStr.Tokenize(",")); + for (int isig = 0; isig < objGenSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() == 1) { // NOTE: 1-prong signals required + fGenMCSignals.push_back(sig); + } + } + } + + string cfgTrackSelection_TrackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); + TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; + if (!cfgTrackSelection_TrackCuts.empty()) { + cfgTrackSelection_objArrayTrackCuts = TString(cfgTrackSelection_TrackCuts).Tokenize(","); + } + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + TString cutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + fTrackCutNames.push_back(cutName); + } + + fTrackCutName1 = fConfigTrackCut1.value; + fTrackCutName2 = fConfigTrackCut2.value; + if (fTrackCutName1 == fTrackCutName2) { + fIsSameTrackCut = true; + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName1.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut1 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName1.Data()); + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName2.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut2 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName2.Data()); + } + LOGP(info, "track cut name 1: {}", fTrackCutName1.Data()); + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + LOGP(info, "Available track cut: {}", cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName()); + TString fTrackSelectionCutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + if (fTrackSelectionCutName.CompareTo(fTrackCutName1) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName1.Data()); + fTrackCutBitMap1 |= (static_cast(1) << icut); + } + if (fTrackSelectionCutName.CompareTo(fTrackCutName2) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName2.Data()); + fTrackCutBitMap2 |= (static_cast(1) << icut); + } + } + LOGP(info, "Track cut bitmap 1: {}", fTrackCutBitMap1); + + TString configDileptonCutNamesStr = fConfigDileptonCut.value; + fDileptonCut = *dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; + std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); + for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { + TString cutName = objArray->At(icut)->GetName(); + fQuadrupletCutNames.push_back(cutName); + fQuadrupletCuts.push_back(*dqcuts::GetCompositeCut(cutName.Data())); + } + + if (isPsi2S || isX3872) { + DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); + if (!configQuadruletCutNamesStr.IsNull()) { + for (std::size_t icut = 0; icut < fQuadrupletCutNames.size(); ++icut) { + if (fIsSameTrackCut) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } else { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + for (auto& sig : fRecMCSignals) { + DefineHistograms(fHistMan, Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + // DefineHistograms(fHistMan, Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + } + } + } + } + + if (isMCGen) { + for (auto& sig : fGenMCSignals) { + DefineHistograms(fHistMan, Form("MCTruthGenQuad_%s", sig->GetName()), ""); + DefineHistograms(fHistMan, Form("MCTruthGenQuadAccepted_%s", sig->GetName()), ""); + } + // DefineHistograms(fHistMan, "MCTruthGenQuadAccepted", ""); + } + + VarManager::SetUseVars(fHistMan->GetUsedVars()); + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + // init parameters from CCDB + void initParamsFromCCDB(uint64_t timestamp) + { + if (fConfigCCDBOptions.fConfigUseRemoteField.value) { + o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDBOptions.fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); + VarManager::SetupFourProngKFParticle(magField); + } else if (fConfigSetupFourProngFitter.value) { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } else { + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + VarManager::SetupFourProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + } else if (fConfigSetupFourProngFitter.value) { + LOGP(info, "Setting up DCA fitter for two and four prong candidates"); + VarManager::SetupTwoProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } + } + + // Template function to run pair - hadron combinations + template + void runDileptonTrackTrack(TEvent const& event, TBCs const& /*bcs*/, soa::Join const& assocs, TTracks const& tracks, TDileptons const& dileptons, McCollisions const& /*mcEvents*/, McParticles const& /*mcTracks*/) + { + VarManager::ResetValues(0, VarManager::kNVars, fValuesQuadruplet); + VarManager::FillEvent(event, fValuesQuadruplet); + // VarManager::FillEvent(event.reducedMCevent(), fValuesQuadruplet); + if (event.has_mcCollision()) { + VarManager::FillEvent(event.mcCollision(), fValuesQuadruplet); + } + + uint32_t mcDecision = static_cast(0); + size_t isig = 0; + + for (auto dilepton : dileptons) { + // get full track info of tracks based on the index + + int indexLepton1 = dilepton.index0Id(); + int indexLepton2 = dilepton.index1Id(); + auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); + auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); + auto lepton1MC = lepton1.mcParticle(); + auto lepton2MC = lepton2.mcParticle(); + // Check that the dilepton has zero charge + if (dilepton.sign() != 0) { + continue; + } + VarManager::FillTrack(dilepton, fValuesQuadruplet); + + // bool isAmbiguousLepton = (dilepton.filterMap_raw() & (static_cast(1) << 28)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 29)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 30)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 31)); + // // if (isAmbi && isAmbiguousLepton) + // // continue; // skip ambiguous dileptons + // if constexpr ((TTrackFillMap & VarManager::ObjTypes::AmbiTrack) > 0) { + // if (isAmbiguousLepton) { + // // LOGP(info, "Dilepton {} is ambiguous, skipping", dilepton.index0Id()); + // continue; // skip ambiguous dileptons + // } + // } + + // apply the dilepton cut + if (!fDileptonCut.IsSelected(fValuesQuadruplet)) + continue; + + fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + + // loop over hadrons pairs + for (auto& [a1, a2] : o2::soa::combinations(assocs, assocs)) { + uint32_t trackSelection = 0; + trackSelection = ((a1.isBarrelSelected_raw() & fTrackCutBitMap1) && (a2.isBarrelSelected_raw() & fTrackCutBitMap2)); + // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); + if (!trackSelection) { + continue; + } + + // get the track from this association + auto track1 = tracks.rawIteratorAt(a1.trackId()); + auto track2 = tracks.rawIteratorAt(a2.trackId()); + if (track1.sign() < 0 && track2.sign() > 0) { + std::swap(track1, track2); + } + // avoid self combinations + if (track1.globalIndex() == indexLepton1 || track1.globalIndex() == indexLepton2 || track2.globalIndex() == indexLepton1 || track2.globalIndex() == indexLepton2) { + continue; + } + + // fill variables + VarManager::FillDileptonTrackTrack(dilepton, track1, track2, fValuesQuadruplet); + // fValuesQuadruplet[VarManager::kWeight] = 1.0f; + if (fConfigSetupFourProngFitter || fConfigUseKFVertexing) { + // LOGP(info, "Using KF or DCA fitter for secondary vertexing"); + VarManager::FillDileptonTrackTrackVertexing(event, lepton1, lepton2, track1, track2, fValuesQuadruplet); + } + + // if (fConfigML.applyBDT) { + // bool isSelectedBDT = false; + // auto pT = fValuesQuadruplet[VarManager::kQuadPt]; + // if (pT < fConfigML.ptBins.value.front() || pT > fConfigML.ptBins.value.back()) { + // continue; // skip quadruplets outside of the pt range of the BDT models + // } + // // TODO: move some variables into varManager and reduce the number of input parameters + // std::vector inputFeatures = fQuadMlResponse.getInputFeatures(lepton1, lepton2, fValuesQuadruplet); + // isSelectedBDT = fQuadMlResponse.isSelectedMl(inputFeatures, fValuesQuadruplet[VarManager::kQuadPt], fOutputMlQuad); + // if (!isSelectedBDT) + // continue; + // VarManager::FillBdtScore(fOutputMlQuad, fValuesQuadruplet); + // } + + auto track1MC = track1.mcParticle(); + auto track2MC = track2.mcParticle(); + mcDecision = 0; + isig = 0; + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + if ((*sig)->CheckSignal(true, lepton1MC, lepton2MC, track1MC, track2MC)) { + mcDecision |= (static_cast(1) << isig); + } + } + + int iCut = 0; + uint32_t CutDecision = 0; + for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { + // apply dilepton-track-track cut + if (fQuadrupletCuts[iCut].IsSelected(fValuesQuadruplet)) { + CutDecision |= (1 << iCut); + if (fIsSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { + if (mcDecision & (static_cast(1) << isig)) { + fHistMan->FillHistClass(Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // if (isAmbiguousLepton || (track1.barrelAmbiguityInBunch() > 1) || (track2.barrelAmbiguityInBunch() > 1) || + // (track1.barrelAmbiguityOutOfBunch() > 1) || (track2.barrelAmbiguityOutOfBunch() > 1)) { + // fHistMan->FillHistClass(Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // } + } + } + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + } // end loop over quadruplet cuts + + // fill table + if (!CutDecision) + continue; + if (!mcDecision) + continue; + // if (fConfigML.applyBDT) + // continue; // skip filling table when BDT is applied for now + DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadDefaultDileptonMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], + fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], + dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), + lepton1.tpcNSigmaEl(), lepton1.tpcNSigmaPi(), lepton1.tpcNSigmaPr(), lepton1.tpcNClsFound(), + lepton2.tpcNSigmaEl(), lepton2.tpcNSigmaPi(), lepton2.tpcNSigmaPr(), lepton2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kDitrackMass], fValuesQuadruplet[VarManager::kDitrackPt], track1.pt(), track2.pt(), track1.eta(), track2.eta(), track1.phi(), track2.phi(), track1.sign(), track2.sign(), track1.tpcNSigmaPi(), track2.tpcNSigmaPi(), track1.tpcNSigmaKa(), track2.tpcNSigmaKa(), track1.tpcNSigmaPr(), track1.tpcNSigmaPr(), track1.tpcNClsFound(), track2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kKFMass], fValuesQuadruplet[VarManager::kVertexingProcCode], fValuesQuadruplet[VarManager::kVertexingChi2PCA], fValuesQuadruplet[VarManager::kCosPointingAngle], fValuesQuadruplet[VarManager::kKFDCAxyzBetweenProngs], fValuesQuadruplet[VarManager::kKFChi2OverNDFGeo], + fValuesQuadruplet[VarManager::kVertexingLz], fValuesQuadruplet[VarManager::kVertexingLxy], fValuesQuadruplet[VarManager::kVertexingLxyz], fValuesQuadruplet[VarManager::kVertexingTauz], fValuesQuadruplet[VarManager::kVertexingTauxy], fValuesQuadruplet[VarManager::kVertexingLzErr], fValuesQuadruplet[VarManager::kVertexingLxyzErr], + fValuesQuadruplet[VarManager::kVertexingTauzErr], fValuesQuadruplet[VarManager::kVertexingLzProjected], fValuesQuadruplet[VarManager::kVertexingLxyProjected], fValuesQuadruplet[VarManager::kVertexingLxyzProjected], fValuesQuadruplet[VarManager::kVertexingTauzProjected], fValuesQuadruplet[VarManager::kVertexingTauxyProjected]); + } // end loop over associations + } // end loop over dileptons + } + + Preslice> trackAssocsPerCollision = aod::track_association::collisionId; + Preslice dielectronsPerCollision = aod::reducedpair::reducedeventId; + // Preslice ditracksPerCollision = aod::reducedpair::reducedeventId; + + void processX3872(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (const auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + void processPsi2S(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + PresliceUnsorted perReducedMcEvent = aod::mcparticle::mcCollisionId; + + void processMCGen(McParticles const& mcTracks) + { + // loop over mc stack and fill histograms for pure MC truth signals + // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event + // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedtrackMC::reducedMCeventId, event.reducedMCevent().globalIndex()); + for (auto& mctrack : mcTracks) { + VarManager::FillTrackMC(mcTracks, mctrack); + // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. + // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. + // TODO: Use the mcReducedFlags to select signals + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, mctrack)) { + int daughterIdFirst = mctrack.daughtersIds()[0]; + int daughterIdEnd = mctrack.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + // LOGP(info, "PDG of dilepton: {}, track1: {}, track2: {}", dilepton.pdgCode(), track1.pdgCode(), track2.pdgCode()); + VarManager::FillQuadMC(dilepton, track1, track2); + int daughterIdFirst2 = dilepton.daughtersIds()[0]; + int daughterIdEnd2 = dilepton.daughtersIds()[1]; + int Ndaughters2 = daughterIdEnd2 - daughterIdFirst2 + 1; + if (Ndaughters2 == 2) { + auto lepton1 = mcTracks.rawIteratorAt(daughterIdFirst2); + auto lepton2 = mcTracks.rawIteratorAt(daughterIdFirst2 + 1); + VarManager::FillPairMC(lepton1, lepton2); + // LOGP(info, "PDG of lepton1: {}, lepton2: {}", lepton1.pdgCode(), lepton2.pdgCode()); + if (lepton1.pt() > fConfigDileptonPtMin && lepton2.pt() > fConfigDileptonPtMin) { + if (std::abs(lepton1.eta()) < fConfigDileptonEtaAbs && std::abs(lepton2.eta()) < fConfigDileptonEtaAbs) { + if (track1.pt() > fConfigTrackPtMin && track2.pt() > fConfigTrackPtMin) { + if (std::abs(track1.eta()) < fConfigTrackEtaAbs && std::abs(track2.eta()) < fConfigTrackEtaAbs) { + fHistMan->FillHistClass(Form("MCTruthGenQuadAccepted_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + } + } + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + + void processMCGenWithEventSelection(soa::Filtered const& events, + McCollisions const& /*mcEvents*/, McParticles const& mcTracks) + { + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + if (!event.has_mcCollision()) { + continue; + } + + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.mcCollisionId()); + groupedMCTracks.bindInternalIndicesTo(&mcTracks); + for (auto& track : groupedMCTracks) { + + VarManager::FillTrackMC(mcTracks, track); + + auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, track_raw)) { + int daughterIdFirst = track_raw.daughtersIds()[0]; + int daughterIdEnd = track_raw.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + VarManager::FillQuadMC(dilepton, track1, track2); + } + fHistMan->FillHistClass(Form("MCTruthGenQuad_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } // end loop over events + } + + void processDummy(MyEvents&) + { + // do nothing + } + + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processPsi2S, "Run psi(2S) -> e+ e- + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processX3872, "Run X(3872) -> J/psi + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms with event selection", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", false); +}; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { // Initialize metadata for TOF response @@ -3198,7 +3733,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) From 1d9e723b54ffe6dc9c360513e8a1873618b69a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 6 Oct 2026 12:34:32 +0200 Subject: [PATCH 188/648] [ALICE3] Refactor ParticleInfo struct member access (#18184) Fix histo name --- ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index e50656494fc..9bfc03a70a2 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -166,8 +166,8 @@ struct OnTheFlyTofPid { std::array, NParticles>, NParticles> h2dOuterDeltaTrue; struct ParticleInfo { - std::string_view texName{}; - std::string_view name{}; + std::string_view texName; + std::string_view name; ParticleId type{}; int pdgCode{}; double mass{}; @@ -347,7 +347,7 @@ struct OnTheFlyTofPid { if (!doQaForParticle(Particles[iHyp].pdgCode)) { continue; } - const auto& pNameHypo = Particles[iHyp].texName; + const auto& pNameHypo = Particles[iHyp].name; const std::string nameTitleInner = std::format("h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); const std::string nameTitleOuter = std::format("h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); From dc6f0e6c2e5ba8fff74d1af2dc04cb461cd0366b Mon Sep 17 00:00:00 2001 From: YubiaoWang Date: Tue, 6 Oct 2026 13:21:07 +0200 Subject: [PATCH 189/648] [PWGJE] jetChargedV2 task: unify in-/out-of-plane selection for jet pT and background fluctuations (#18183) --- PWGJE/Tasks/jetChargedV2.cxx | 353 +++++++++++++++++++++++------------ 1 file changed, 230 insertions(+), 123 deletions(-) diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 05d897db26f..51e31090ffc 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -15,6 +15,7 @@ #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetSubtraction.h" @@ -90,25 +91,30 @@ struct JetChargedV2 { Configurable nBinsEta{"nBinsEta", 200, "number of bins for eta axes"}; Configurable randomConeLeadJetDeltaR{"randomConeLeadJetDeltaR", -99.0, "min distance between leading jet axis and random cone (RC) axis; if negative, min distance is set to automatic value of R_leadJet+R_RC "}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; + // Fluctuation histogram axes, kept identical to jetBackgroundAnalysis (h2_centrality_rhorandomcone*) + // so that the in-plane / out-of-plane fluctuations can be combined into the response matrix + // by the local unfolding tools (AnalysisToolsO2-evtpln). + Configurable nBinsFluct{"nBinsFluct", 1000, "number of bins for fluctuations axes"}; + Configurable deltaPtMinMaxFluct{"deltaPtMinMaxFluct", 200, "maximum and minimum delta pt value for fluctuation axes"}; //=====================< tracking efficiency >=====================// Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; //=====================< evt pln >=====================// - Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; - Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; + Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; + Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; Configurable detCheck{"detCheck", 4, "total qvector number"}; - + Configurable rhoFitRangeMargin{"rhoFitRangeMargin", 0.5, "extension of the rho modulation fit function range"}; + Configurable checkAbsDeltaPhi{"checkAbsDeltaPhi", false, "use 0-pi event-plane region for in/out-plane"}; ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; ConfigurableAxis cfgAxisVnCent{"cfgAxisVnCent", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 50, 70, 100}, " % "}; - ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; EventPlaneHelper helperEP; int detId = 0; @@ -129,6 +135,8 @@ struct JetChargedV2 { Configurable useMedianRho{"useMedianRho", false, "use median rho for subtract MCP Background"}; Configurable useLocalRho{"useLocalRho", false, "use local rho for subtract MCP Background"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + Configurable kappa{"kappa", 1.0, "angularity kappa"}; + Configurable alpha{"alpha", 1.0, "angularity alpha"}; enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, @@ -139,9 +147,6 @@ struct JetChargedV2 { template int getDetId(const T& name) { - if (name.value == "BPos" || name.value == "BNeg" || name.value == "BTot") { - LOGF(warning, "Using deprecated label: %s. Please use TPCpos, TPCneg, TPCall instead.", name.value); - } if (name.value == "FT0C") { return 0; } @@ -171,6 +176,7 @@ struct JetChargedV2 { std::vector eventSelectionBits; int trackSelection = -1; + bool doSumw2 = false; double evtnum = 0; float collQvecAmpDetId = 1e-8; TH1F* hPtsumSumptFit = nullptr; @@ -183,12 +189,12 @@ struct JetChargedV2 { void init(o2::framework::InitContext&) { + doSumw2 = skipMBGapEvents; detId = getDetId(cfgDetName); detIdCheck = getDetId(cfgDetCheckName); refAId = getDetId(cfgRefAName); refBId = getDetId(cfgRefBName); if (detId == refAId || detId == refBId || refAId == refBId || detId == detIdCheck) { - LOGF(info, "Wrong detector configuration \n The FT0C will be used to get Q-Vector \n The TPCpos and TPCneg will be used as reference systems"); detId = 0; detIdCheck = 0; refAId = 4; @@ -229,6 +235,9 @@ struct JetChargedV2 { AxisSpec jetPtAxis = {200, 0., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetPtAxisRhoAreaSub = {400, -200., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetEtaAxis = {nBinsEta, -1.0, 1.0, "#eta"}; + // fluctuation axes, same as jetBackgroundAnalysis's h2_centrality_rhorandomcone* + AxisSpec fluctCentralityAxis = {1100, 0.0, 110.0, "centrality (%)"}; + AxisSpec fluctDeltaPtAxis = {nBinsFluct, -deltaPtMinMaxFluct, deltaPtMinMaxFluct, "#delta #it{p}_{T} (GeV/#it{c})"}; AxisSpec axisPt = {40, 0.0, 4.0}; AxisSpec axisEta = {32, -0.8, 0.8}; @@ -342,7 +351,6 @@ struct JetChargedV2 { registry.add("h_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h2_phi_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); registry.add("h2_centrality_pT", "centrality vs p_{T}; p_{T}; centrality ", {HistType::kTH2F, {{210, -10.0, 200.0}, {100, 0., 100}}}); registry.add("h2_rholocal_cent", "#centrality vs #rho(#varphi); #centrality; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); @@ -353,24 +361,11 @@ struct JetChargedV2 { registry.add("h2_rholocal_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_rholocal_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_averagerho_absDelta", "#varphi vs #rho(0)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_raw_pt", "#varphi vs #it{p}_{T}; #it{p}_{T} - <#rho>Area; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_low", "#varphi vs #rho(#varphi)absDeltaLow, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium", "#varphi vs #rho(#varphi)absDeltaMediun, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high", "#varphi vs #rho(#varphi)absDeltahigh, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - - registry.add("h2_phi_rholocal_absDelta_low_inplane", "#varphi vs #rho(#varphi)absDeltaLow inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium_inplane", "#varphi vs #rho(#varphi)absDeltaMediun inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high_inplane", "#varphi vs #rho(#varphi)absDeltahigh inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - - registry.add("h2_phi_rholocal_absDelta_low_outplane", "#varphi vs #rho(#varphi)absDeltaLow outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_medium_outplane", "#varphi vs #rho(#varphi)absDeltaMediun outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta_high_outplane", "#varphi vs #rho(#varphi)absDeltahigh outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h_good_bad_ugly", "local rho large than 0, less than 0 and total", {HistType::kTH1F, {{5, 0.0, 5.0}}}); registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(1, "#rho_{local} > 0"); registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(2, "#rho_{local} <= 0"); @@ -387,27 +382,48 @@ struct JetChargedV2 { registry.add("leadJetEta", "leadJet constituent #eta ", {HistType::kTH1F, {{100, -1.0, 1.0}}}); //< RC test plots >// - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - // registry.add("h1_distribution_RC", "RC #phi-#Psi_{2}", {HistType::kTH1F, {{72, 0.0, o2::constants::math::TwoPI}}}); - + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (checkAbsDeltaPhi) { + registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_averagerho_absDelta", "#varphi vs #rho(0)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_absDelta_low", "#varphi vs #rho(#varphi)absDeltaLow, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_medium", "#varphi vs #rho(#varphi)absDeltaMediun, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_high", "#varphi vs #rho(#varphi)absDeltahigh, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h_jet_pt_in_plane_v2_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_in_plane_v2_rho_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_rho_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + } //< bkg sub plot | end >// //< median rho >// registry.add("h_jet_pt_in_plane_v2", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v2", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_phi_rholocal_Delta_low_inplane", "#varphi vs #rho(#varphi)absDeltaLow inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_inplane", "#varphi vs #rho(#varphi)absDeltaMediun inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_inplane", "#varphi vs #rho(#varphi)absDeltahigh inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_Delta_low_outplane", "#varphi vs #rho(#varphi)absDeltaLow outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_outplane", "#varphi vs #rho(#varphi)absDeltaMediun outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_outplane", "#varphi vs #rho(#varphi)absDeltahigh outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h_jet_pt_in_plane_v3", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v3", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); @@ -427,6 +443,15 @@ struct JetChargedV2 { registry.add("h2_centrality_jet_eta_rhoareasubtracted", "centrality vs. jet eta;centrality; #eta; counts", {HistType::kTH2F, {centralityAxis, jetEtaAxis}}); registry.add("h2_centrality_jet_phi_rhoareasubtracted", "centrality vs. jet phi;centrality; #varphi; counts", {HistType::kTH2F, {centralityAxis, phiAxis}}); registry.add("h2_jet_pt_track_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}); + + registry.add("h_jet_pt_rhoareasubtracted", "jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h_jet_eta_rhoareasubtracted", "jet eta;#eta; counts", {HistType::kTH1F, {jetEtaAxis}}, doSumw2); + registry.add("h_jet_phi_rhoareasubtracted", "jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_area_rhoareasubtracted", "jet #it{p}_{T,jet} vs. Area_{jet}; #it{p}_{T,jet} (GeV/#it{c}); Area_{jet}", {HistType::kTH2F, {jetPtAxis, {150, 0., 1.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_ntracks_rhoareasubtracted", "jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet} (GeV/#it{c}); N_{jet, tracks}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_corr_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,corr}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,corr} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity_rhoareasubtracted", "jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted", "jet_pt_eta_phi_rhoareasubtracted", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetEtaAxis, phiAxis}}, doSumw2); } if (doprocessSigmaPtMCP || doprocessSigmaPtAreaSubMCP) { @@ -800,6 +825,38 @@ struct JetChargedV2 { } } + template + void fillJetAreaSubHistograms(TJets const& jet, float centrality, float rho, float weight = 1.0, float pTHat = 999.0) + { + if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) { + return; + } + double jetcorrpt = jet.pt() - (rho * jet.area()); + if (jet.r() == round(selectedJetsRadius * 100.0f)) { + // fill jet histograms after area-based subtraction + registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jetcorrpt, weight); + registry.fill(HIST("h2_centrality_jet_pt_rhoareasubtracted"), centrality, jetcorrpt, weight); + registry.fill(HIST("h2_jet_pt_jet_corr_pt_rhoareasubtracted"), jet.pt(), jetcorrpt, weight); + registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted"), jetcorrpt, jet.eta(), jet.phi(), weight); + if (jetcorrpt > 0) { + registry.fill(HIST("h_jet_eta_rhoareasubtracted"), jet.eta(), weight); + registry.fill(HIST("h_jet_phi_rhoareasubtracted"), jet.phi(), weight); + registry.fill(HIST("h2_centrality_jet_eta_rhoareasubtracted"), centrality, jet.eta(), weight); + registry.fill(HIST("h2_centrality_jet_phi_rhoareasubtracted"), centrality, jet.phi(), weight); + registry.fill(HIST("h2_jet_pt_jet_area_rhoareasubtracted"), jetcorrpt, jet.area(), weight); + registry.fill(HIST("h2_jet_pt_jet_ntracks_rhoareasubtracted"), jetcorrpt, jet.tracksIds().size(), weight); + } + } + + float angularity = 0.; + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_track_pt_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + } + angularity /= (jet.pt() * (jet.r() / 100.f)); + registry.fill(HIST("h2_jet_pt_jet_angularity_rhoareasubtracted"), jetcorrpt, angularity, weight); + } + double chiSquareCDF(int nDF, double x) { return TMath::Gamma(nDF / 2., x / 2.); @@ -879,6 +936,38 @@ struct JetChargedV2 { registry.fill(HIST("leadJetEtaMCP"), leadingJetEta); } + // Integrate the rho modulation fit over [phi - radius, phi + radius]. The integration + // limits are kept inside [0, 2pi) by splitting the window into two pieces whenever it + double getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin) + { + if (!fitFunc) { + return 0.0; + } + if (radius > fitRangeMargin) { + static bool warnOnce = true; // called once per jet / random cone, so warn only once + if (warnOnce) { + LOGF(warning, "Rho fit range margin (%.3f) is smaller than integration radius (%.3f)", fitRangeMargin, radius); + warnOnce = false; + } + } + phi = RecoDecay::constrainAngle(phi, 0.); + const double twoPi = o2::constants::math::TwoPI; + const double low = phi - radius; + const double high = phi + radius; + if (low < 0.) { // window crosses 0: split into two pieces + return fitFunc->Integral(low + twoPi, twoPi) + fitFunc->Integral(0., high); + } + if (high > twoPi) { // window crosses 2*pi: split into two pieces + return fitFunc->Integral(low, twoPi) + fitFunc->Integral(0., high - twoPi); + } + return fitFunc->Integral(low, high); + } + + void getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin, double& integralValue) + { + integralValue = getRhoLocalIntegral(fitFunc, phi, radius, fitRangeMargin); + } + // Run General_Purpose MC MCP template void fitFncAreaSubMCP(U const& collision, J const& jets, TH1F* hPtsumSumptFitMCPFill, bool mcLevelIsParticleLevel, float weight = 1.0) @@ -969,7 +1058,7 @@ struct JetChargedV2 { continue; } - double integralValue = fFitModulationV2v3P->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3P, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_mcp_phi_rholocal"), jet.phi() - ep2, rholocal, weight); registry.fill(HIST("h2_mcp_centrality_rholocal"), centrality, rholocal, weight); @@ -981,7 +1070,7 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); float absDelta = std::abs(phiMinusPsi2); if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_mcp_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), weight); @@ -1131,12 +1220,12 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - double integralValue = fFitModulationRMFill->Integral(jetMCD.phi() - selectedJetsRadius, jetMCD.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationRMFill, jetMCD.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = rho / (2 * selectedJetsRadius * tempparaA) * integralValue; double corrBasejetpt = jetMCD.pt() - (rholocal * jetMCD.area()); double corrTagjetpt = 0.0; if (subtractMCPBackgroundBool) { - double integralValueMCP = fFitModulationRMFill->Integral(jetMCP.phi() - selectedJetsRadius, jetMCP.phi() + selectedJetsRadius); + double integralValueMCP = getRhoLocalIntegral(fFitModulationRMFill, jetMCP.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocalMCP = mcrho / (2 * selectedJetsRadius * tempparaA) * integralValueMCP; corrTagjetpt = jetMCP.pt() - (rholocalMCP * jetMCP.area()); } else { @@ -1310,7 +1399,6 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlTwistV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlFinalV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); - // mark histosQA.fill(HIST("h_ep2_FT0CV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); histosQA.fill(HIST("h_ep2_FT0MV2"), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); histosQA.fill(HIST("h2_ep2_FT0C_FT0M"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); @@ -1426,6 +1514,7 @@ struct JetChargedV2 { if (!isAcceptedJet(jet)) { continue; } + fillJetAreaSubHistograms(jet, centrality, collision.rho()); } double leadingJetPt = -1; @@ -1437,7 +1526,8 @@ struct JetChargedV2 { int nTrk = 0; getNtrk(tracks, jets, nTrk, evtnum, leadingJetEta); if (nTrk <= 0) { - return; + registry.fill(HIST("h_collisions"), 4.5); + return; // otherwise the fit histogram below would be created with 0 bins } hPtsumSumptFit = new TH1F("h_ptsum_sumpt_fit", "h_ptsum_sumpt fit use", TMath::CeilNint(std::sqrt(nTrk)), 0., o2::constants::math::TwoPI); @@ -1447,9 +1537,16 @@ struct JetChargedV2 { double ep3 = 0.; int cfgNmodA = 2; int cfgNmodB = 3; - int evtPlnAngleA = 3; - // int evtPlnAngleB = 3; - // int evtPlnAngleC = 5; + // In-plane / out-of-plane windows, identical to the downstream QA and unfolding + // macros (analysisCode_headFiles.cpp::GetInOutPlane2D), which cut + // Delta phi = phi - Psi_2 in [0, 2pi) at pi/4, 3pi/4, 5pi/4 and 7pi/4: + // in-plane : [0, pi/4) U [3pi/4, 5pi/4) U [7pi/4, 2pi) + // out-of-plane : [pi/4, 3pi/4) U [5pi/4, 7pi/4) + // The same composite expression also gives the correct region for a folded + // |Delta phi| in [0, pi], namely [0, pi/4) U [3pi/4, pi]. + int evtPlnAngleA = 7; + int evtPlnAngleB = 3; + int evtPlnAngleC = 5; for (uint i = 0; i < cfgnMods->size(); i++) { int nmode = cfgnMods->at(i); int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); @@ -1584,24 +1681,7 @@ struct JetChargedV2 { if (jet.r() != round(selectedJetsRadius * 100.0f)) { continue; } - double twoPi = o2::constants::math::TwoPI; - double phi = std::fmod(jet.phi(), twoPi); - if (phi < 0) { - phi += twoPi; - } - - double low = phi - selectedJetsRadius; - double high = phi + selectedJetsRadius; - double integralValue = 0.0; - if (low < 0) { - integralValue += fFitModulationV2v3->Integral(low + twoPi, twoPi); - integralValue += fFitModulationV2v3->Integral(0, high); - } else if (high > twoPi) { - integralValue += fFitModulationV2v3->Integral(low, twoPi); - integralValue += fFitModulationV2v3->Integral(0, high - twoPi); - } else { - integralValue += fFitModulationV2v3->Integral(low, high); - } + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = 0.0; if (integralValue <= 0) { @@ -1624,32 +1704,32 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + float absDelta = std::abs(RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI)); registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); registry.fill(HIST("h2_averagerho_cent"), centrality, collision.rho(), 1.0); registry.fill(HIST("h2_centrality_pT"), jet.pt(), centrality, 1.0); - - registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_averagerho_absDelta"), absDelta, collision.rho(), 1.0); - + registry.fill(HIST("h2_phi_rholocal"), phiMinusPsi2, rholocal, 1.0); registry.fill(HIST("h2_rholocal_pt"), jet.pt(), rholocal, 1.0); registry.fill(HIST("h2_rholocal_raw_pt"), jet.pt() - rholocal * jet.area(), rholocal, 1.0); registry.fill(HIST("h2_averagerho_pt"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_averagerho_raw_pt"), jet.pt() - collision.rho() * jet.area(), collision.rho(), 1.0); - int lowPtCut = 20; - int mediumPtCut = 40; - int highPtCut = 70; - int highPtCutEnd = 100; - - if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { - registry.fill(HIST("h2_phi_rholocal_absDelta_low"), absDelta, rholocal, 1.0); - } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { - registry.fill(HIST("h2_phi_rholocal_absDelta_medium"), absDelta, rholocal, 1.0); - } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { - registry.fill(HIST("h2_phi_rholocal_absDelta_high"), absDelta, rholocal, 1.0); + if (checkAbsDeltaPhi) { + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); + registry.fill(HIST("h2_phi_averagerho_absDelta"), absDelta, collision.rho(), 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_low"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_medium"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_absDelta_high"), absDelta, rholocal, 1.0); + } } registry.fill(HIST("h_jet_pt_inclusive_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); @@ -1658,17 +1738,23 @@ struct JetChargedV2 { histosQA.fill(HIST("h2_ep2_FT0C_FT0M_bumpRegion"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); } - // if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { - if (absDelta < o2::constants::math::PIQuarter || absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) { + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_low_inplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_medium_inplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_high_inplane"), absDelta, rholocal, 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_inplane"), phiMinusPsi2, rholocal, 1.0); + } registry.fill(HIST("h2_averagerho_pt_inplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_inplane"), jet.pt(), rholocal, 1.0); @@ -1679,25 +1765,36 @@ struct JetChargedV2 { registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_low_outplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_medium_outplane"), absDelta, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta_high_outplane"), absDelta, rholocal, 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_outplane"), phiMinusPsi2, rholocal, 1.0); + } registry.fill(HIST("h2_averagerho_pt_outplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_outplane"), jet.pt(), rholocal, 1.0); } + if (checkAbsDeltaPhi) { + if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h_jet_pt_in_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_in_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } else { + registry.fill(HIST("h_jet_pt_out_of_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } + } } else if (nmode == cfgNmodB) { double phiMinusPsi3 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } ep3 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - // phiMinusPsi3 = jet.phi() - ep3; phiMinusPsi3 = RecoDecay::constrainAngle(jet.phi() - ep3, -o2::constants::math::PI); float absDelta3 = std::abs(phiMinusPsi3); - // if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { - if (absDelta3 < o2::constants::math::PIQuarter || absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) { + if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_in_plane_v3_rho"), jet.pt() - (rholocal * jet.area()), 1.0); } else { @@ -1714,18 +1811,16 @@ struct JetChargedV2 { float randomConePt = 0; double integralValueRC = 0.0; double rholocalRC = 0.0; - integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; int nmode = cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; - // double rcPhiPsi2Rand = 0.0; float absRcPhiPsi2 = 0.0; - - // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); - absRcPhiPsi2 = std::abs(rcPhiPsi2); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { @@ -1736,14 +1831,21 @@ struct JetChargedV2 { } } } - // registry.fill(HIST("h1_distribution_RC"), rcPhiPsi2); registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - if (absRcPhiPsi2 < o2::constants::math::PIQuarter || absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); + + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); } else { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + } + // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); @@ -1759,10 +1861,10 @@ struct JetChargedV2 { } if (jetWasInCone) { randomConePt = 0.0; - integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); - absRcPhiPsi2 = std::abs(rcPhiPsi2); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here @@ -1777,17 +1879,22 @@ struct JetChargedV2 { } registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); - - if (absRcPhiPsi2 < o2::constants::math::PIQuarter || absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); } else { - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, absRcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_abs_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), absRcPhiPsi2, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } } - } else if (nmode == cfgNmodB) { continue; } @@ -1992,7 +2099,7 @@ struct JetChargedV2 { registry.fill(HIST("h_mcd_pt_before_matching_mcdprocess"), corrBasejetpt); } - double integralValue = fFitModulationV2v3->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); @@ -2002,7 +2109,7 @@ struct JetChargedV2 { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); float absDelta = std::abs(phiMinusPsi2); registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); @@ -2037,14 +2144,14 @@ struct JetChargedV2 { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; - double integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + double integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); double rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; int nmode = cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { From 4a91854181b99e068b4ca6c0caa80c6bbcb1cfe8 Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Tue, 6 Oct 2026 14:20:49 +0200 Subject: [PATCH 190/648] [DPG] add time dependency monitoring for Vx and Vy (#18187) Co-authored-by: Igor Altsybeev --- DPG/Tasks/AOTEvent/timeDependentQa.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/DPG/Tasks/AOTEvent/timeDependentQa.cxx b/DPG/Tasks/AOTEvent/timeDependentQa.cxx index 03f9850a1b5..2599f74018b 100644 --- a/DPG/Tasks/AOTEvent/timeDependentQa.cxx +++ b/DPG/Tasks/AOTEvent/timeDependentQa.cxx @@ -227,7 +227,12 @@ struct TimeDependentQaTask { histos.add("hSecondsCollisions", "", kTH1D, {axisSeconds}); histos.add("hSecondsCollisionsNoPileup", "", kTH1D, {axisSeconds}); histos.add("hSecondsIR", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVzBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVxBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVyBeforeAllCuts", "", kTH1D, {axisSeconds}); histos.add("hSecondsVz", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVx", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVy", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0Camlp", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0CamlpByColMult", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0AamlpByColMult", "", kTH1D, {axisSeconds}); @@ -527,6 +532,10 @@ struct TimeDependentQaTask { histos.fill(HIST("hSecondsUPCverticesBeforeAllCuts"), secFromSOR, isVertexUPC ? 1 : 0); + histos.fill(HIST("hSecondsVzBeforeAllCuts"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVxBeforeAllCuts"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVyBeforeAllCuts"), secFromSOR, col.posY()); + if (std::fabs(col.posZ()) > 10) continue; @@ -546,6 +555,8 @@ struct TimeDependentQaTask { if (col.selection_bit(kNoSameBunchPileup)) histos.fill(HIST("hSecondsCollisionsNoPileup"), secFromSOR); histos.fill(HIST("hSecondsVz"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVx"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVy"), secFromSOR, col.posY()); histos.fill(HIST("hSecondsFT0Camlp"), secFromSOR, bc.foundFT0().sumAmpC()); histos.fill(HIST("hSecondsFT0CamlpByColMult"), secFromSOR, col.multFT0C()); histos.fill(HIST("hSecondsFT0AamlpByColMult"), secFromSOR, col.multFT0A()); From 9939e9f6cc4875fa85f2e618e86082855ba6deec Mon Sep 17 00:00:00 2001 From: hfribert Date: Tue, 6 Oct 2026 14:40:09 +0200 Subject: [PATCH 191/648] [PWGLF] Add customV0 pairing for Sigma+ PCM photon (#18175) Co-authored-by: Henrik Fribert --- PWGLF/DataModel/LFKinkDecayTables.h | 48 +- .../Strangeness/sigmaplusbuilder.cxx | 1780 ++++++++++------- 2 files changed, 1141 insertions(+), 687 deletions(-) diff --git a/PWGLF/DataModel/LFKinkDecayTables.h b/PWGLF/DataModel/LFKinkDecayTables.h index a8a5cffa8a3..db5e3825d43 100644 --- a/PWGLF/DataModel/LFKinkDecayTables.h +++ b/PWGLF/DataModel/LFKinkDecayTables.h @@ -191,11 +191,11 @@ DECLARE_SOA_COLUMN(PhotonQt, photonQt, float); //! Armente DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the measured photon (cm) DECLARE_SOA_COLUMN(PhotonOpeningAngle, photonOpeningAngle, float); //! Opening angle between the photon's e+e- daughters (rad) DECLARE_SOA_COLUMN(PhotonPointingAngle, photonPointingAngle, float); //! Angle between the photon momentum and the line from its conversion point to the candidate decay vertex (rad) -DECLARE_SOA_COLUMN(PhotonDcaToPV, photonDcaToPV, float); //! DCA of the photon's flight line to the primary vertex (cm) DECLARE_SOA_COLUMN(RootCenter, rootCenter, float); //! -coefB/(2*coefA) of the missing-photon quadratic solve: negative flags an unphysical phase-space point DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the field-unbent proton momentum (from its original reference point) and the PV->decay-vertex direction DECLARE_SOA_COLUMN(CandDcaToPV, candDcaToPV, float); //! DCA of the candidate's total reconstructed momentum line to the PV (cm) +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the PV->decay-vertex direction needed for the missing photon to have a real solution (rad), 0 if none needed DECLARE_SOA_COLUMN(ProtonSign, protonSign, int); //! Charge sign of the proton track (= sign of the whole candidate, since the photon is neutral) DECLARE_SOA_COLUMN(ProtonItsNCls, protonItsNCls, uint8_t); //! Number of ITS clusters of the proton track @@ -208,10 +208,26 @@ DECLARE_SOA_COLUMN(PhotonPosTpcNCls, photonPosTpcNCls, int16_t); //! Number of f DECLARE_SOA_COLUMN(PhotonNegItsNCls, photonNegItsNCls, uint8_t); //! Number of ITS clusters of the photon's negative daughter DECLARE_SOA_COLUMN(PhotonNegTpcNCls, photonNegTpcNCls, int16_t); //! Number of found TPC clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonDcaDau, photonDcaDau, float); //! DCA between the photon's e+e- daughters at the conversion point (cm) +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the angle between the photon momentum and the PV->conversion-point direction +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon's flight line to the PV (cm) +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) +DECLARE_SOA_COLUMN(PhotonPsiPair, photonPsiPair, float); //! psi_pair of the e+e- legs, evaluated after propagating them outward, 999 if the propagation failed +DECLARE_SOA_COLUMN(PhotonPosDcaXY, photonPosDcaXY, float); //! DCAxy of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosDcaZ, photonPosDcaZ, float); //! DCAz of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaXY, photonNegDcaXY, float); //! DCAxy of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaZ, photonNegDcaZ, float); //! DCAz of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosTpcNClsFindable, photonPosTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcNClsFindable, photonNegTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonPosTpcChi2NCl, photonPosTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcChi2NCl, photonNegTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's negative daughter + // MC columns DECLARE_SOA_COLUMN(CollisionIdCheck, collisionIdCheck, bool); //! True if the proton's collision ID matches the reconstructed collision ID -DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! True if the proton and photon share the same true Sigma+ mother +DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! True if the proton and photon share the same true Sigma+ mother +DECLARE_SOA_COLUMN(IsProtonFromSigma, isProtonFromSigma, bool); //! True if the proton is the daughter of a true Sigma+ -> p pi0 +DECLARE_SOA_COLUMN(IsPhotonFromSigma, isPhotonFromSigma, bool); //! True if the photon is from the pi0 of a true Sigma+ -> p pi0 DECLARE_SOA_COLUMN(XDecVtxMC, xDecVtxMC, float); //! MC-truth Sigma+ decay vertex (x direction) DECLARE_SOA_COLUMN(YDecVtxMC, yDecVtxMC, float); //! MC-truth Sigma+ decay vertex (y direction) @@ -273,11 +289,14 @@ DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, - sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, sigmapluscand::PhotonDcaToPV, - sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::ProtonSign, sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, // dynamic columns sigmapluscand::Radius, @@ -297,13 +316,16 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, - sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, sigmapluscand::PhotonDcaToPV, - sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::ProtonSign, sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, sigmapluscand::CollisionIdCheck, - sigmapluscand::IsSignal, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::XDecVtxMC, sigmapluscand::YDecVtxMC, sigmapluscand::ZDecVtxMC, sigmapluscand::PxProtonMC, sigmapluscand::PyProtonMC, sigmapluscand::PzProtonMC, sigmapluscand::PxGammaMC, sigmapluscand::PyGammaMC, sigmapluscand::PzGammaMC, @@ -323,7 +345,7 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::TransDecayRadius, - sigmapluscand::CandDcaToPV, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, sigmapluscand::ProtonSign, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, @@ -333,6 +355,9 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, // dynamic columns sigmapluscand::PxSigmaPlus, @@ -343,7 +368,7 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", sigmapluscand::TransDecayRadius, - sigmapluscand::CandDcaToPV, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, sigmapluscand::ProtonSign, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, @@ -353,8 +378,11 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, sigmapluscand::CollisionIdCheck, - sigmapluscand::IsSignal, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::DecayRadiusMC, sigmapluscand::MassMC, sigmapluscand::PxSigmaPlusMC, sigmapluscand::PySigmaPlusMC, sigmapluscand::PzSigmaPlusMC, diff --git a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx index 7969042b519..e9df7b7e324 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx @@ -13,10 +13,14 @@ /// \brief Task for Sigma+ -> p + pi0 reconstruction, pi0 reconstructed via one converted photon (PCM) /// \author Henrik Fribert (TUM) +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/svPoolCreator.h" #include "Common/Core/RecoDecay.h" +#include "Common/Core/TPCVDriftManager.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -24,32 +28,42 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include -#include +#include +#include +#include #include #include -#include #include #include #include +#include +#include +#include #include +#include +#include #include using namespace o2; @@ -67,27 +81,33 @@ struct Sigmaplusbuilder { Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; // photon (PCM) selection - Configurable photonMaxMass{"photonMaxMass", 0.20, "Max photon mass (GeV/c^2)"}; - Configurable photonMinRapidity{"photonMinRapidity", -0.8, "Min photon rapidity"}; - Configurable photonMaxRapidity{"photonMaxRapidity", 0.8, "Max photon rapidity"}; - Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma+ in MC"}; - Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma+ in MC"}; - Configurable photonDauEtaMin{"photonDauEtaMin", -0.8, "Min eta of photon daughter tracks"}; - Configurable photonDauEtaMax{"photonDauEtaMax", 0.8, "Max eta of photon daughter tracks"}; + Configurable photonMaxMass{"photonMaxMass", 0.05, "Max photon mass (GeV/c^2)"}; + Configurable photonMinRapidity{"photonMinRapidity", -1.0, "Min photon rapidity"}; + Configurable photonMaxRapidity{"photonMaxRapidity", 1.0, "Max photon rapidity"}; + Configurable photonDauEtaMin{"photonDauEtaMin", -1.0, "Min eta of photon daughter tracks"}; + Configurable photonDauEtaMax{"photonDauEtaMax", 1.0, "Max eta of photon daughter tracks"}; Configurable photonMinRadius{"photonMinRadius", 3.0, "Min photon conversion radius (cm)"}; Configurable photonMaxRadius{"photonMaxRadius", 115., "Max photon conversion radius (cm)"}; - Configurable photonMinV0cospa{"photonMinV0cospa", 0.80, "Min V0 CosPA"}; - Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 3.5, "Max DCA between photon daughters (cm)"}; + Configurable photonMinV0cospa{"photonMinV0cospa", 0.9, "Min photon cosine of pointing angle to the PV"}; + Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 1.5, "Max DCA between photon daughters (cm)"}; Configurable photonMaxOpeningAngle{"photonMaxOpeningAngle", 0.4, "Max opening angle between the photon's e+/e- daughter momenta (rad)"}; Configurable photonMaxDeltaTheta{"photonMaxDeltaTheta", 0.15, "Max |theta_pos - theta_neg| of the photon's daughter tracks (rad)"}; - Configurable photonMaxQt{"photonMaxQt", 0.15, "Max Armenteros qT for photons (GeV/c)"}; - Configurable photonMaxAlpha{"photonMaxAlpha", 1.0, "Max |Armenteros alpha| for photons"}; - Configurable photonDauMinTPCNSigmaEl{"photonDauMinTPCNSigmaEl", -5., "Min TPC nSigma_el of the photon daughters"}; - Configurable photonDauMaxTPCNSigmaEl{"photonDauMaxTPCNSigmaEl", 5., "Max TPC nSigma_el of the photon daughters"}; - Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon (V0) daughter tracks"}; + Configurable photonMaxQt{"photonMaxQt", 0.05, "Max Armenteros qT for photons (GeV/c)"}; + Configurable photonMaxAlpha{"photonMaxAlpha", 0.95, "Max |Armenteros alpha| for photons"}; + Configurable photonDauMinTPCNSigmaEl{"photonDauMinTPCNSigmaEl", -3., "Min TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxTPCNSigmaEl{"photonDauMaxTPCNSigmaEl", 3., "Max TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxPt{"photonDauMaxPt", 0.5, "Max pT of the photon daughters (GeV/c)"}; + Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon daughter tracks"}; + + // photon daughters paired by this task instead of taken from V0Datas (V0Datas assumes photons from primary vertex) + Configurable useCustomVertexer{"useCustomVertexer", false, "Pair the photon's e+/e- daughter tracks in this task instead of reading V0Datas"}; + Configurable photonSkipAmbiTracks{"photonSkipAmbiTracks", false, "Skip ambiguous tracks when pairing photon daughters"}; + Configurable photonPoolTimeMarginNS{"photonPoolTimeMarginNS", 800., "Time margin (ns) added to a daughter track's time range when matching it to collisions"}; + Configurable photonMaxDXYIni{"photonMaxDXYIni", 4., "Max xy distance (cm) between the two daughter tracks at the start of the photon vertex fit"}; + Configurable photonMaxCircleTouchDist{"photonMaxCircleTouchDist", 4., "Max |circle centre distance - (R1 + R2)| (cm) of the two daughter tracks, applied before the fit"}; // proton selection - Configurable protonMinPt{"protonMinPt", 0.3, "Minimum proton pT (GeV/c)"}; + Configurable protonMinPt{"protonMinPt", 0.4, "Minimum proton pT (GeV/c)"}; Configurable protonMaxEta{"protonMaxEta", 0.9, "Maximum |eta| for proton track"}; Configurable protonMinTpcNCls{"protonMinTpcNCls", 80, "Min number of found TPC clusters for the proton track"}; Configurable protonMaxTPCNSigma{"protonMaxTPCNSigma", 4, "Max |TPC nSigma_pr| for proton"}; @@ -98,36 +118,39 @@ struct Sigmaplusbuilder { Configurable protonMaxDcaToPV{"protonMaxDcaToPV", 5.0, "Max DCAxy of the proton track to the PV (cm)"}; // proton-photon candidate selection - Configurable candMaxDcaProtonGamma{"candMaxDcaProtonGamma", 0.5, "Max DCA between proton and photon at the fitted vertex (cm)"}; - Configurable candMaxDcaToPV{"candMaxDcaToPV", 0.1, "Max DCA of the candidate's total (reconstructed) momentum line to the PV (cm)"}; + Configurable candVertexProtonWeight{"candVertexProtonWeight", 1.0, "Sigma+ vertex between the proton's (1) and the photon's (0) point of closest approach"}; + Configurable candMaxDcaProtonGamma{"candMaxDcaProtonGamma", 1.5, "Max DCA between proton and photon at the fitted vertex (cm)"}; + Configurable candMaxDcaToPV{"candMaxDcaToPV", 20., "Max DCA of the candidate's total (reconstructed) momentum line to the PV (cm)"}; Configurable candRejectNegRootCenter{"candRejectNegRootCenter", true, "Reject candidates with rootCenter<0"}; Configurable candMaxRootCenter{"candMaxRootCenter", 15, "Max rootCenter=-coefB/(2*coefA) (GeV/c)"}; Configurable candMinAntiSigmaPointingAngle{"candMinAntiSigmaPointingAngle", 0.0, "Min AntiSigmaPointingAngle (rad)"}; - Configurable candMaxAntiSigmaPointingAngle{"candMaxAntiSigmaPointingAngle", 0.3, "Max AntiSigmaPointingAngle (rad)"}; + Configurable candMaxAntiSigmaPointingAngle{"candMaxAntiSigmaPointingAngle", 10., "Max AntiSigmaPointingAngle (rad)"}; Configurable candMaxSigmaMass{"candMaxSigmaMass", 1.35, "Max reconstructed Sigma+ candidate mass (GeV/c^2)"}; Configurable candMaxRapidity{"candMaxRapidity", 0.9, "Max |rapidity| of the reconstructed Sigma+ candidate"}; Configurable candMinRadius{"candMinRadius", 1.0, "Min candidate decay radius (cm)"}; Configurable candMaxRadius{"candMaxRadius", 100., "Max candidate decay radius (cm)"}; Configurable candMinFlightDistance{"candMinFlightDistance", 0.0, "Min 3D distance from PV to candidate decay vertex (cm)"}; Configurable candMaxFlightDistance{"candMaxFlightDistance", 250.0, "Max 3D distance from PV to candidate decay vertex (cm)"}; - Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' raw track momenta"}; - Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon's fitted momentum and the decay-vertex-to-conversion-point line (rad)"}; + Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' track momenta"}; + Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon momentum and the decay-vertex-to-conversion-point line (rad)"}; Configurable candMaxPhotonDcaToPV{"candMaxPhotonDcaToPV", 250.0, "Max DCA of the photon's flight line to the PV (cm)"}; + Configurable candDeduplicatePhotons{"candDeduplicatePhotons", true, "Per timeframe, write only the candidate with the smallest DCA to PV among candidates sharing the same photon"}; - // missing-photon discriminant retry - // resolution-shaped function and tuned parameters from Run 2 used - Configurable discrRetryMaxIter{"discrRetryMaxIter", 10, "Max attempts to recover a negative discriminant by perturbing the flight direction"}; - Configurable discrRetryThetaRange{"discrRetryThetaRange", 0.02, "Max |delta-theta| perturbation of the flight direction per retry (rad)"}; - Configurable discrRetryThetaPar0{"discrRetryThetaPar0", 0.000133299, "par0 of the delta-theta resolution function"}; - Configurable discrRetryThetaPar1{"discrRetryThetaPar1", 0.0016761, "par1 of the delta-theta resolution function"}; - Configurable discrRetryPhiRange{"discrRetryPhiRange", 0.02, "Max |delta-phi| perturbation of the flight direction per retry (rad)"}; - Configurable discrRetryPhiPar0{"discrRetryPhiPar0", 0.000129845, "par0 of the delta-phi resolution function"}; - Configurable discrRetryPhiPar1{"discrRetryPhiPar1", 0.00199688, "par1 of the delta-phi resolution function"}; + // tilt search: if the measured flight direction gives no real solution for the missing photon, + // tilted flight directions are tried starting with the smallest tilt + Configurable candMaxTilt{"candMaxTilt", 0.3, "Max tilt of the flight direction searched for a real root (rad)"}; + Configurable candTiltStep{"candTiltStep", 0.005, "Tilt step between the rings of tried flight directions (rad)"}; + Configurable candTiltNAzimuth{"candTiltNAzimuth", 36, "Flight directions tried per ring"}; + + // MC + Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma+ in MC"}; + Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma+ in MC"}; Configurable fillSlimTables{"fillSlimTables", false, "write the slim candidate tables instead of the full ones"}; Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "CCDB path of the material budget LUT"}; Produces sigmaPlusCands; Produces sigmaPlusCandsMC; @@ -138,8 +161,79 @@ struct Sigmaplusbuilder { o2::vertexing::DCAFitterN<2> fitter; int mRunNumber = 0; float mBz = 0; - TF1 mThetaResoFunc; - TF1 mPhiResoFunc; + o2::base::MatLayerCylSet* mLut = nullptr; + o2::aod::common::TPCVDriftManager mVDriftMgr; + + // daughter pairing (electrons and positrons with the range of collisions each is compatible with) + svPoolCreator svPhotonPoolCreator{PDG_t::kElectron, PDG_t::kPositron}; + std::vector mElectronPool; + std::vector mPositronPool; + std::vector mGoodCollision; // collisions passing the event selection + + // values of a candidate, kept until the end of the timeframe and then written to the tables + struct SigmaPlusCandidate { + uint64_t photonId = 0; + bool isSignal = false; + int photonLegsWithoutIts = 0; + int matchedSigmaId = -1; + std::array decVtx{}; + float radius = 0.f; + float flightDistance = 0.f; + float dcaProtonGamma = 0.f; + float dcaToPV = 0.f; + float tiltAngle = 0.f; + float rootCenter = 0.f; + float antiSigmaPointingAngle = 0.f; + std::array pProton{}; + std::array pGamma1{}; + std::array pGamma2{}; + float protonTpcNSigma = 0.f; + float protonTofNSigma = 0.f; + int protonSign = 0; + uint8_t protonItsNCls = 0; + int16_t protonTpcNCls = 0; + float protonDcaXY = 0.f; + float protonDcaZ = 0.f; + float photonMass = 0.f; + float photonAlpha = 0.f; + float photonQt = 0.f; + float photonRadius = 0.f; + float photonOpeningAngle = 0.f; + float photonPointingAngle = 0.f; + float photonDcaDau = 0.f; + float photonCosPAToPV = 0.f; + float photonDcaXYToPV = 0.f; + float photonDcaZToPV = 0.f; + float photonPsiPair = 0.f; + float posTpcNSigmaEl = 0.f; + float negTpcNSigmaEl = 0.f; + uint8_t posItsNCls = 0; + uint8_t negItsNCls = 0; + int16_t posTpcNCls = 0; + int16_t negTpcNCls = 0; + int16_t posTpcNClsFindable = 0; + int16_t negTpcNClsFindable = 0; + float posTpcChi2NCl = 0.f; + float negTpcChi2NCl = 0.f; + float posDcaXY = 0.f; + float posDcaZ = 0.f; + float negDcaXY = 0.f; + float negDcaZ = 0.f; + // MC + bool collisionIdCheck = false; + bool protonIsSignal = false; + bool photonIsSignal = false; + std::array decVtxMC{}; + std::array pProtonMC{}; + std::array pGammaMC{}; + std::array pSigmaPlusMC{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; + }; + std::vector mCandidatesOfTimeframe; + + // MC: generated Sigma+ -> p pi0 in acceptance of current timeframe and whether a true candidate of it was written + std::unordered_map mGenSigmaWritten; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -162,10 +256,11 @@ struct Sigmaplusbuilder { fitter.setMaxChi2(1e9); fitter.setUseAbsDCA(true); - mThetaResoFunc = TF1("thetaResoFunc", "[0]/(abs(x)+[1])", -discrRetryThetaRange, discrRetryThetaRange); - mThetaResoFunc.SetParameters(discrRetryThetaPar0.value, discrRetryThetaPar1.value); - mPhiResoFunc = TF1("phiResoFunc", "[0]/(abs(x)+[1])", -discrRetryPhiRange, discrRetryPhiRange); - mPhiResoFunc.SetParameters(discrRetryPhiPar0.value, discrRetryPhiPar1.value); + mVDriftMgr.init(&ccdb->instance()); + svPhotonPoolCreator.setTimeMargin(photonPoolTimeMarginNS); + if (photonSkipAmbiTracks) { + svPhotonPoolCreator.setSkipAmbiTracks(); + } const AxisSpec axisVertexZ{100, -15., 15., "vrtx_{Z} (cm)"}; @@ -180,221 +275,102 @@ struct Sigmaplusbuilder { const AxisSpec axisTpcNCls{160, -0.5, 159.5, "TPC clusters"}; const AxisSpec axisPhotonOpeningAngle{180, 0., 3.15, "opening angle (rad)"}; const AxisSpec axisPhotonDeltaTheta{200, -1., 1., "#Delta#theta (rad)"}; + const AxisSpec axisPhotonDcaV0Dau{200, 0., 10., "DCA between photon daughters (cm)"}; + const AxisSpec axisCollisionsPerPhoton{100, 0.5, 100.5, "collisions a photon is kept in"}; + const AxisSpec axisTpcTimeRangeNColl{200, 0.5, 200.5, "collisions in the TPC time range of a TPC-only daughter"}; const AxisSpec axisProtonSel{7, -0.5, 6.5, "selection step"}; const AxisSpec axisProtonPt{100, 0., 5., "#it{p}_{T,p} (GeV/c)"}; const AxisSpec axisNSigma{100, -5., 5., "n#sigma"}; const AxisSpec axisProtonDcaToPV{1000, 0., 5., "DCA_{xy,p} to PV (cm)"}; - histos.add("hVertexZ", "hVertexZ", kTH1F, {axisVertexZ}); - - histos.add("Photon/hSelectionCounter", "Photon/hSelectionCounter", kTH1F, {axisPhotonSel}); - auto hPhotonSel = histos.get(HIST("Photon/hSelectionCounter")); - hPhotonSel->GetXaxis()->SetBinLabel(1, "All"); - hPhotonSel->GetXaxis()->SetBinLabel(2, "Neg eta"); - hPhotonSel->GetXaxis()->SetBinLabel(3, "Pos eta"); - hPhotonSel->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hPhotonSel->GetXaxis()->SetBinLabel(5, "TPC nSigma_{el}"); - hPhotonSel->GetXaxis()->SetBinLabel(6, "DCA daughters"); - hPhotonSel->GetXaxis()->SetBinLabel(7, "Radius"); - hPhotonSel->GetXaxis()->SetBinLabel(8, "Opening angle"); - hPhotonSel->GetXaxis()->SetBinLabel(9, "Delta theta"); - hPhotonSel->GetXaxis()->SetBinLabel(10, "CosPA"); - hPhotonSel->GetXaxis()->SetBinLabel(11, "Rapidity"); - hPhotonSel->GetXaxis()->SetBinLabel(12, "Qt"); - hPhotonSel->GetXaxis()->SetBinLabel(13, "Alpha"); - hPhotonSel->GetXaxis()->SetBinLabel(14, "Mass"); - - histos.add("Photon/hMass", "Photon/hMass", kTH1F, {axisPhotonMass}); - histos.add("Photon/hPt", "Photon/hPt", kTH1F, {axisPhotonPt}); - histos.add("Photon/hRadius", "Photon/hRadius", kTH1F, {axisPhotonRadius}); - histos.add("Photon/h2ArmenterosPodolanski", "Photon/h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); - histos.add("Photon/h2ConvPointXY", "Photon/h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); - histos.add("Photon/h2TPCNSigmaElPosVsPt", "Photon/h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/h2TPCNSigmaElNegVsPt", "Photon/h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/h2TPCNClsPosVsPt", "Photon/h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/h2TPCNClsNegVsPt", "Photon/h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/hOpeningAngle", "Photon/hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Photon/hDeltaTheta", "Photon/hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); - - histos.add("Proton/hSelectionCounter", "Proton/hSelectionCounter", kTH1F, {axisProtonSel}); - auto hProtonSel = histos.get(HIST("Proton/hSelectionCounter")); - hProtonSel->GetXaxis()->SetBinLabel(1, "All"); - hProtonSel->GetXaxis()->SetBinLabel(2, "Pt"); - hProtonSel->GetXaxis()->SetBinLabel(3, "Eta"); - hProtonSel->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hProtonSel->GetXaxis()->SetBinLabel(5, "TPC nSigma"); - hProtonSel->GetXaxis()->SetBinLabel(6, "TOF nSigma"); - hProtonSel->GetXaxis()->SetBinLabel(7, "DCA to PV"); - - histos.add("Proton/hPt", "Proton/hPt", kTH1F, {axisProtonPt}); - histos.add("Proton/h2TPCNSigmaVsPt", "Proton/h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/h2TOFNSigmaVsPt", "Proton/h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/h2TPCNClsVsPt", "Proton/h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); - histos.add("Proton/h2DcaToPVVsPt", "Proton/h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); - const AxisSpec axisCandSel{15, -0.5, 14.5, "selection step"}; + const AxisSpec axisPhotonLegType{3, -0.5, 2.5, "photon daughters without ITS"}; const AxisSpec axisCandPhotonDcaToPV{250, 0., 250., "DCA_{#gamma-line} to PV (cm)"}; const AxisSpec axisDca{100, 0., 10., "DCA(p,#gamma) (cm)"}; const AxisSpec axisDcaToPV{500, 0., 0.5, "DCA_{cand} to PV (cm)"}; const AxisSpec axisCandRadius{200, 0., 200., "R_{dec} (cm)"}; const AxisSpec axisFlightDistance{250, 0., 250., "|SV-PV| (cm)"}; - const AxisSpec axisDiscriminant{200, -1., 1., "discriminant (GeV^{4}/#it{c}^{4})"}; + const AxisSpec axisTilt{200, 0., 0.5, "flight direction tilt for a real root (rad)"}; const AxisSpec axisRootCenter{400, -10., 10., "-b/(2a) (GeV/#it{c})"}; const AxisSpec axisAntiSigmaPA{180, 0., 3.15, "AntiPA (rad)"}; const AxisSpec axisMassSigma{200, 1.0, 1.4, "m_{p#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec axisRapidity{200, -2., 2., "y_{#Sigma^{+}}"}; const AxisSpec axisSigmaPt{100, 0., 6., "#it{p}_{T,#Sigma^{+}} (GeV/#it{c})"}; - const AxisSpec axisMomentum{100, 0., 10., "#it{p} (GeV/#it{c})"}; - - histos.add("Candidate/hSelectionCounter", "Candidate/hSelectionCounter", kTH1F, {axisCandSel}); - auto hCandSel = histos.get(HIST("Candidate/hSelectionCounter")); - hCandSel->GetXaxis()->SetBinLabel(1, "All pairs"); - hCandSel->GetXaxis()->SetBinLabel(2, "Autocorrelation"); - hCandSel->GetXaxis()->SetBinLabel(3, "Vertex fit"); - hCandSel->GetXaxis()->SetBinLabel(4, "DCA(p,#gamma)"); - hCandSel->GetXaxis()->SetBinLabel(5, "Radius"); - hCandSel->GetXaxis()->SetBinLabel(6, "Flight distance"); - hCandSel->GetXaxis()->SetBinLabel(7, "Real root"); - hCandSel->GetXaxis()->SetBinLabel(8, "Valid root"); - hCandSel->GetXaxis()->SetBinLabel(9, "DCA to PV"); - hCandSel->GetXaxis()->SetBinLabel(10, "Mass"); - hCandSel->GetXaxis()->SetBinLabel(11, "Rapidity"); - hCandSel->GetXaxis()->SetBinLabel(12, "Photon opening angle"); - hCandSel->GetXaxis()->SetBinLabel(13, "Photon pointing angle"); - hCandSel->GetXaxis()->SetBinLabel(14, "Photon DCA to PV"); - hCandSel->GetXaxis()->SetBinLabel(15, "Filled"); - - histos.add("Candidate/hDcaProtonGamma", "Candidate/hDcaProtonGamma", kTH1F, {axisDca}); - histos.add("Candidate/hDcaToPV", "Candidate/hDcaToPV", kTH1F, {axisDcaToPV}); - histos.add("Candidate/hRadius", "Candidate/hRadius", kTH1F, {axisCandRadius}); - histos.add("Candidate/hFlightDistance", "Candidate/hFlightDistance", kTH1F, {axisFlightDistance}); - histos.add("Candidate/hPhotonOpeningAngle", "Candidate/hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/hPhotonPointingAngle", "Candidate/hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/hPhotonDcaToPV", "Candidate/hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); - histos.add("Candidate/hDiscriminant", "Candidate/hDiscriminant", kTH1F, {axisDiscriminant}); - histos.add("Candidate/hRootCenter", "Candidate/hRootCenter", kTH1F, {axisRootCenter}); - histos.add("Candidate/hMassSigmaPlus", "Candidate/hMassSigmaPlus", kTH1F, {axisMassSigma}); - histos.add("Candidate/hRapidity", "Candidate/hRapidity", kTH1F, {axisRapidity}); - histos.add("Candidate/h2MassVsPt", "Candidate/h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); - histos.add("Candidate/h2MassVsRootCenter", "Candidate/h2MassVsRootCenter", kTH2F, {axisRootCenter, axisMassSigma}); - histos.add("Candidate/hAntiSigmaPointingAngle", "Candidate/hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); - histos.add("Candidate/h2MassVsAntiSigmaPointingAngle", "Candidate/h2MassVsAntiSigmaPointingAngle", kTH2F, {axisAntiSigmaPA, axisMassSigma}); - - const AxisSpec axisDiscrIter{discrRetryMaxIter + 2, -0.5, discrRetryMaxIter + 1.5, "discriminant retry iteration"}; - histos.add("Candidate/hDiscriminantRetryIter", "Candidate/hDiscriminantRetryIter", kTH1F, {axisDiscrIter}); + const AxisSpec axisCandidatesPerPhoton{50, 0.5, 50.5, "candidates sharing one photon"}; + + const AxisSpec axisSigmaSel{6, -0.5, 5.5, "step"}; + + const std::vector photonSteps{"All", "Neg eta", "Pos eta", "TPC clusters", "TPC nSigma_{el}, p_{T}", "DCA daughters", "Radius", + "Opening angle", "Delta theta", "CosPA", "Rapidity", "Qt", "Alpha", "Mass"}; + const std::vector protonSteps{"All", "Pt", "Eta", "TPC clusters", "TPC nSigma", "TOF nSigma", "DCA to PV"}; + const std::vector candSteps{"All pairs", "Autocorrelation", "Vertex fit", "DCA(p,#gamma)", "Radius", "Flight distance", "Real root", + "Valid root", "DCA to PV", "Mass", "Rapidity", "Photon opening angle", "Photon pointing angle", "Photon DCA to PV", "Written"}; + const std::vector sigmaSteps{"Generated", "Proton reconstructed", "Proton selected", "Photon daughters reconstructed", "Photon selected", "Candidate written"}; + auto setBinLabels = [](TAxis* axis, const std::vector& labels) { + for (size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(i + 1, labels[i].c_str()); + } + }; + histos.add("hVertexZ", "hVertexZ", kTH1F, {axisVertexZ}); + + setBinLabels(histos.add("Photon/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisPhotonSel})->GetXaxis(), photonSteps); + histos.add("Photon/Inclusive/h2TPCNClsPosVsPt", "h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNClsNegVsPt", "h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNSigmaElPosVsPt", "h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/h2TPCNSigmaElNegVsPt", "h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/hDcaV0Daughters", "hDcaV0Daughters", kTH1F, {axisPhotonDcaV0Dau}); + histos.add("Photon/Inclusive/hOpeningAngle", "hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Photon/Inclusive/hDeltaTheta", "hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); + histos.add("Photon/Inclusive/hMass", "hMass", kTH1F, {axisPhotonMass}); + histos.add("Photon/Inclusive/hPt", "hPt", kTH1F, {axisPhotonPt}); + histos.add("Photon/Inclusive/hRadius", "hRadius", kTH1F, {axisPhotonRadius}); + histos.add("Photon/Inclusive/h2ArmenterosPodolanski", "h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); + histos.add("Photon/Inclusive/h2ConvPointXY", "h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); + histos.add("Photon/Inclusive/hCollisionsPerPhoton", "hCollisionsPerPhoton", kTH1F, {axisCollisionsPerPhoton}); + + setBinLabels(histos.add("Proton/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisProtonSel})->GetXaxis(), protonSteps); + histos.add("Proton/Inclusive/hPt", "hPt", kTH1F, {axisProtonPt}); + histos.add("Proton/Inclusive/h2TPCNSigmaVsPt", "h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TOFNSigmaVsPt", "h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TPCNClsVsPt", "h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); + histos.add("Proton/Inclusive/h2DcaToPVVsPt", "h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); + + setBinLabels(histos.add("Candidate/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisCandSel})->GetXaxis(), candSteps); + setBinLabels(histos.add("Candidate/Inclusive/h2SelectionCounterVsLegType", "h2SelectionCounterVsLegType", kTH2D, {axisCandSel, axisPhotonLegType})->GetXaxis(), candSteps); + histos.add("Candidate/Inclusive/hDcaProtonGamma", "hDcaProtonGamma", kTH1F, {axisDca}); + histos.add("Candidate/Inclusive/hRadius", "hRadius", kTH1F, {axisCandRadius}); + histos.add("Candidate/Inclusive/hFlightDistance", "hFlightDistance", kTH1F, {axisFlightDistance}); + histos.add("Candidate/Inclusive/hTiltAngle", "hTiltAngle", kTH1F, {axisTilt}); + histos.add("Candidate/Inclusive/hRootCenter", "hRootCenter", kTH1F, {axisRootCenter}); + histos.add("Candidate/Inclusive/hDcaToPV", "hDcaToPV", kTH1F, {axisDcaToPV}); + histos.add("Candidate/Inclusive/hAntiSigmaPointingAngle", "hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); + histos.add("Candidate/Inclusive/hMassSigmaPlus", "hMassSigmaPlus", kTH1F, {axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsPt", "h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsTilt", "h2MassVsTilt", kTH2F, {axisTilt, axisMassSigma}); + histos.add("Candidate/Inclusive/hRapidity", "hRapidity", kTH1F, {axisRapidity}); + histos.add("Candidate/Inclusive/hPhotonOpeningAngle", "hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonPointingAngle", "hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonDcaToPV", "hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); if (doprocessMc || doprocessFindable) { - histos.add("Photon/True/hSelectionCounter", "Photon/True/hSelectionCounter", kTH1F, {axisPhotonSel}); - auto hPhotonSelSignal = histos.get(HIST("Photon/True/hSelectionCounter")); - hPhotonSelSignal->GetXaxis()->SetBinLabel(1, "All"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(2, "Neg eta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(3, "Pos eta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(5, "TPC nSigma_{el}"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(6, "DCA daughters"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(7, "Radius"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(8, "Opening angle"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(9, "Delta theta"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(10, "CosPA"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(11, "Rapidity"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(12, "Qt"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(13, "Alpha"); - hPhotonSelSignal->GetXaxis()->SetBinLabel(14, "Mass"); - - histos.add("Photon/True/h2TPCNSigmaElPosVsPt", "Photon/True/h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/True/h2TPCNSigmaElNegVsPt", "Photon/True/h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); - histos.add("Photon/True/h2TPCNClsPosVsPt", "Photon/True/h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); - histos.add("Photon/True/h2TPCNClsNegVsPt", "Photon/True/h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.addClone("Photon/Inclusive/", "Photon/True/"); } - if (doprocessMc) { - histos.add("Photon/True/hMass", "Photon/True/hMass", kTH1F, {axisPhotonMass}); - histos.add("Photon/True/hPt", "Photon/True/hPt", kTH1F, {axisPhotonPt}); - histos.add("Photon/True/hRadius", "Photon/True/hRadius", kTH1F, {axisPhotonRadius}); - histos.add("Photon/True/h2ArmenterosPodolanski", "Photon/True/h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); - histos.add("Photon/True/h2ConvPointXY", "Photon/True/h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); - histos.add("Photon/True/hOpeningAngle", "Photon/True/hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Photon/True/hDeltaTheta", "Photon/True/hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); - - histos.add("Proton/True/hPt", "Proton/True/hPt", kTH1F, {axisProtonPt}); - histos.add("Proton/True/h2TPCNSigmaVsPt", "Proton/True/h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/True/h2TOFNSigmaVsPt", "Proton/True/h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); - histos.add("Proton/True/h2TPCNClsVsPt", "Proton/True/h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); - histos.add("Proton/True/h2DcaToPVVsPt", "Proton/True/h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); - - histos.add("Proton/True/hSelectionCounter", "Proton/True/hSelectionCounter", kTH1F, {axisProtonSel}); - auto hProtonSelSignal = histos.get(HIST("Proton/True/hSelectionCounter")); - hProtonSelSignal->GetXaxis()->SetBinLabel(1, "All"); - hProtonSelSignal->GetXaxis()->SetBinLabel(2, "Pt"); - hProtonSelSignal->GetXaxis()->SetBinLabel(3, "Eta"); - hProtonSelSignal->GetXaxis()->SetBinLabel(4, "TPC clusters"); - hProtonSelSignal->GetXaxis()->SetBinLabel(5, "TPC nSigma"); - hProtonSelSignal->GetXaxis()->SetBinLabel(6, "TOF nSigma"); - hProtonSelSignal->GetXaxis()->SetBinLabel(7, "DCA to PV"); - - histos.add("Candidate/True/hSelectionCounter", "Candidate/True/hSelectionCounter", kTH1F, {axisCandSel}); - auto hCandSelSignal = histos.get(HIST("Candidate/True/hSelectionCounter")); - hCandSelSignal->GetXaxis()->SetBinLabel(1, "All pairs"); - hCandSelSignal->GetXaxis()->SetBinLabel(2, "Autocorrelation"); - hCandSelSignal->GetXaxis()->SetBinLabel(3, "Vertex fit"); - hCandSelSignal->GetXaxis()->SetBinLabel(4, "DCA(p,#gamma)"); - hCandSelSignal->GetXaxis()->SetBinLabel(5, "Radius"); - hCandSelSignal->GetXaxis()->SetBinLabel(6, "Flight distance"); - hCandSelSignal->GetXaxis()->SetBinLabel(7, "Real root"); - hCandSelSignal->GetXaxis()->SetBinLabel(8, "Valid root"); - hCandSelSignal->GetXaxis()->SetBinLabel(9, "DCA to PV"); - hCandSelSignal->GetXaxis()->SetBinLabel(10, "Mass"); - hCandSelSignal->GetXaxis()->SetBinLabel(11, "Rapidity"); - hCandSelSignal->GetXaxis()->SetBinLabel(12, "Photon opening angle"); - hCandSelSignal->GetXaxis()->SetBinLabel(13, "Photon pointing angle"); - hCandSelSignal->GetXaxis()->SetBinLabel(14, "Photon DCA to PV"); - hCandSelSignal->GetXaxis()->SetBinLabel(15, "Filled"); - - histos.add("Candidate/True/hDcaProtonGamma", "Candidate/True/hDcaProtonGamma", kTH1F, {axisDca}); - histos.add("Candidate/True/hDcaToPV", "Candidate/True/hDcaToPV", kTH1F, {axisDcaToPV}); - histos.add("Candidate/True/hRadius", "Candidate/True/hRadius", kTH1F, {axisCandRadius}); - histos.add("Candidate/True/hFlightDistance", "Candidate/True/hFlightDistance", kTH1F, {axisFlightDistance}); - histos.add("Candidate/True/hPhotonOpeningAngle", "Candidate/True/hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/True/hPhotonPointingAngle", "Candidate/True/hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); - histos.add("Candidate/True/hPhotonDcaToPV", "Candidate/True/hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); - const AxisSpec axisVtxRes{200, 0., 20., "|vtx_{fit} - vtx_{MC}| (cm)"}; - histos.add("Candidate/True/hVertexResFromMcTruth", "Candidate/True/hVertexResFromMcTruth", kTH1F, {axisVtxRes}); - const AxisSpec axisMomRes{200, -1., 1., "(p_{fit} - p_{MC}) / p_{MC}"}; - histos.add("Candidate/True/hProtonMomResFromMcTruth", "Candidate/True/hProtonMomResFromMcTruth", kTH1F, {axisMomRes}); - histos.add("Candidate/True/hPhotonMomResFromMcTruth", "Candidate/True/hPhotonMomResFromMcTruth", kTH1F, {axisMomRes}); - const AxisSpec axisProtonFlightAngle{180, 0., 3.15, "angle(p_{proton}, n) (rad)"}; - histos.add("Candidate/True/hProtonFlightAngle", "Candidate/True/hProtonFlightAngle", kTH1F, {axisProtonFlightAngle}); - histos.add("Candidate/True/hDiscriminant", "Candidate/True/hDiscriminant", kTH1F, {axisDiscriminant}); - histos.add("Candidate/True/hDiscriminantRetryIter", "Candidate/True/hDiscriminantRetryIter", kTH1F, {axisDiscrIter}); - histos.add("Candidate/True/hRootCenter", "Candidate/True/hRootCenter", kTH1F, {axisRootCenter}); - histos.add("Candidate/True/hMassSigmaPlus", "Candidate/True/hMassSigmaPlus", kTH1F, {axisMassSigma}); - histos.add("Candidate/True/hRapidity", "Candidate/True/hRapidity", kTH1F, {axisRapidity}); - histos.add("Candidate/True/h2MassVsPt", "Candidate/True/h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); - histos.add("Candidate/True/h2MassVsRootCenter", "Candidate/True/h2MassVsRootCenter", kTH2F, {axisRootCenter, axisMassSigma}); - histos.add("Candidate/True/hAntiSigmaPointingAngle", "Candidate/True/hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); - histos.add("Candidate/True/h2MassVsAntiSigmaPointingAngle", "Candidate/True/h2MassVsAntiSigmaPointingAngle", kTH2F, {axisAntiSigmaPA, axisMassSigma}); - - histos.add("MC/hGenSigmaPlusPt", "MC/hGenSigmaPlusPt", kTH1F, {axisSigmaPt}); + histos.addClone("Proton/Inclusive/", "Proton/True/"); + histos.addClone("Candidate/Inclusive/", "Candidate/True/"); + } - const AxisSpec axisProtonTruthQA{3, -0.5, 2.5, "step"}; - histos.add("MC/hProtonTruthQA", "MC/hProtonTruthQA", kTH1F, {axisProtonTruthQA}); - auto hProtonTruthQA = histos.get(HIST("MC/hProtonTruthQA")); - hProtonTruthQA->GetXaxis()->SetBinLabel(1, "Accepted"); - hProtonTruthQA->GetXaxis()->SetBinLabel(2, "Has mcParticle"); - hProtonTruthQA->GetXaxis()->SetBinLabel(3, "Sigma+ mother"); + histos.add("Photon/Inclusive/hTpcTimeRangeNColl", "hTpcTimeRangeNColl", kTH1F, {axisTpcTimeRangeNColl}); + histos.add("Candidate/Inclusive/hCandidatesPerPhoton", "hCandidatesPerPhoton", kTH1F, {axisCandidatesPerPhoton}); - const AxisSpec axisPhotonTruthQA{5, -0.5, 4.5, "step"}; - histos.add("MC/hPhotonTruthQA", "MC/hPhotonTruthQA", kTH1F, {axisPhotonTruthQA}); - auto hPhotonTruthQA = histos.get(HIST("MC/hPhotonTruthQA")); - hPhotonTruthQA->GetXaxis()->SetBinLabel(1, "Accepted"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(2, "Both legs have mcParticle"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(3, "Shared real gamma mother"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(4, "Gamma's mother is pi0"); - hPhotonTruthQA->GetXaxis()->SetBinLabel(5, "Pi0's mother is Sigma+"); + if (doprocessMc) { + histos.add("MC/hGenSigmaPlusPt", "MC/hGenSigmaPlusPt", kTH1F, {axisSigmaPt}); + setBinLabels(histos.add("MC/hSigmaPlusCounter", "MC/hSigmaPlusCounter", kTH1D, {axisSigmaSel})->GetXaxis(), sigmaSteps); } if (doprocessFindable) { + const AxisSpec axisMomentum{100, 0., 10., "#it{p} (GeV/#it{c})"}; const AxisSpec axisDetectorPresence{3, -0.5, 2.5, "detector"}; const AxisSpec axisDuplicateTrack{2, -0.5, 1.5, "track"}; const AxisSpec axisTPCClusters{160, -0.5, 159.5, "TPC clusters"}; @@ -418,23 +394,11 @@ struct Sigmaplusbuilder { histos.add("Findable/hElectronDetectorPresence", "Findable/hElectronDetectorPresence", kTH1F, {axisDetectorPresence}); histos.add("Findable/hPositronDetectorPresence", "Findable/hPositronDetectorPresence", kTH1F, {axisDetectorPresence}); - auto hConversionPairV0Presence = histos.get(HIST("Findable/hConversionPairV0Presence")); - hConversionPairV0Presence->GetXaxis()->SetBinLabel(1, "valid pair"); - hConversionPairV0Presence->GetXaxis()->SetBinLabel(2, "in V0"); - auto hPhotonSearchPresence = histos.get(HIST("Findable/hPhotonSearchPresence")); - hPhotonSearchPresence->GetXaxis()->SetBinLabel(1, "valid pair"); - hPhotonSearchPresence->GetXaxis()->SetBinLabel(2, "passed photon selection"); - auto hDuplicateConversionTrackCounter = histos.get(HIST("Findable/hDuplicateConversionTrackCounter")); - hDuplicateConversionTrackCounter->GetXaxis()->SetBinLabel(1, "e^{-}"); - hDuplicateConversionTrackCounter->GetXaxis()->SetBinLabel(2, "e^{+}"); - auto hElectronDetectorPresence = histos.get(HIST("Findable/hElectronDetectorPresence")); - auto hPositronDetectorPresence = histos.get(HIST("Findable/hPositronDetectorPresence")); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(1, "ITS"); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(2, "TPC"); - hElectronDetectorPresence->GetXaxis()->SetBinLabel(3, "TOF"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(1, "ITS"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(2, "TPC"); - hPositronDetectorPresence->GetXaxis()->SetBinLabel(3, "TOF"); + setBinLabels(histos.get(HIST("Findable/hConversionPairV0Presence"))->GetXaxis(), {"valid pair", "in V0"}); + setBinLabels(histos.get(HIST("Findable/hPhotonSearchPresence"))->GetXaxis(), {"valid pair", "passed photon selection"}); + setBinLabels(histos.get(HIST("Findable/hDuplicateConversionTrackCounter"))->GetXaxis(), {"e^{-}", "e^{+}"}); + setBinLabels(histos.get(HIST("Findable/hElectronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); + setBinLabels(histos.get(HIST("Findable/hPositronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); } } @@ -447,11 +411,48 @@ struct Sigmaplusbuilder { float alpha = 0.f; float qtarm = 0.f; float radius = 0.f; + float dcaDau = 0.f; + float cosPAToPV = 0.f; TTrack negTrack; TTrack posTrack; }; - // photon candidates + // MC counter of generated Sigma+ -> p pi0 in acceptance + void fillSigmaCounter(int sigmaId, int step) + { + if (mGenSigmaWritten.contains(sigmaId)) { + histos.fill(HIST("MC/hSigmaPlusCounter"), step); + } + } + + template + void fillPhotonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Photon/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Photon/True/") + name, values...); + } + } + + template + void fillProtonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Proton/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Proton/True/") + name, values...); + } + } + + template + void fillCandHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Candidate/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Candidate/True/") + name, values...); + } + } + + // photon candidates from V0Datas template std::vector> findPhotonsFromV0s(const TV0s& v0s, const TTracks&, const std::array& pv) { @@ -461,22 +462,16 @@ struct Sigmaplusbuilder { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); - bool isSignal = false; + int sigmaId = -1; if constexpr (IsMC) { if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { - auto mcPos = posTrack.template mcParticle_as(); - auto mcNeg = negTrack.template mcParticle_as(); - isSignal = findSigmaPlusMotherOfPhoton(mcPos, mcNeg) >= 0; + sigmaId = findSigmaPlusMotherOfPhoton(posTrack.template mcParticle_as(), negTrack.template mcParticle_as()); } } + bool isSignal = sigmaId >= 0; auto fillPhotonStep = [&](int step) { - histos.fill(HIST("Photon/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hSelectionCounter"), step); - } - } + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); }; fillPhotonStep(0); @@ -490,33 +485,25 @@ struct Sigmaplusbuilder { } fillPhotonStep(2); - histos.fill(HIST("Photon/h2TPCNClsPosVsPt"), posTrack.pt(), posTrack.tpcNClsFound()); - histos.fill(HIST("Photon/h2TPCNClsNegVsPt"), negTrack.pt(), negTrack.tpcNClsFound()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/h2TPCNClsPosVsPt"), posTrack.pt(), posTrack.tpcNClsFound()); - histos.fill(HIST("Photon/True/h2TPCNClsNegVsPt"), negTrack.pt(), negTrack.tpcNClsFound()); - } - } + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNClsFound()); + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNClsFound()); if (posTrack.tpcNClsFound() < photonDauMinTpcNCls || negTrack.tpcNClsFound() < photonDauMinTpcNCls) { continue; } fillPhotonStep(3); - histos.fill(HIST("Photon/h2TPCNSigmaElPosVsPt"), posTrack.pt(), posTrack.tpcNSigmaEl()); - histos.fill(HIST("Photon/h2TPCNSigmaElNegVsPt"), negTrack.pt(), negTrack.tpcNSigmaEl()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/h2TPCNSigmaElPosVsPt"), posTrack.pt(), posTrack.tpcNSigmaEl()); - histos.fill(HIST("Photon/True/h2TPCNSigmaElNegVsPt"), negTrack.pt(), negTrack.tpcNSigmaEl()); - } - } + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNSigmaEl()); + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNSigmaEl()); if (posTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || posTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl || negTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || negTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { continue; } + if (posTrack.pt() > photonDauMaxPt || negTrack.pt() > photonDauMaxPt) { + continue; + } fillPhotonStep(4); + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, v0.dcaV0daughters()); if (v0.dcaV0daughters() > photonMaxDCAV0Dau) { continue; } @@ -532,40 +519,23 @@ struct Sigmaplusbuilder { std::array pNeg{v0.pxneg(), v0.pyneg(), v0.pzneg()}; std::array pPos{v0.pxpos(), v0.pypos(), v0.pzpos()}; - // opening angle between the daughter momenta - float photonOpeningAngleV0 = std::acos(std::clamp(dot3(pPos, pNeg) / std::sqrt(dot3(pPos, pPos) * dot3(pNeg, pNeg)), -1.f, 1.f)); - histos.fill(HIST("Photon/hOpeningAngle"), photonOpeningAngleV0); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hOpeningAngle"), photonOpeningAngleV0); - } - } - if (photonOpeningAngleV0 > photonMaxOpeningAngle) { + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + if (photonOpeningAngle > photonMaxOpeningAngle) { continue; } fillPhotonStep(7); - // delta theta between the daughter tracks' own polar angles - float posTheta = 2.f * std::atan(std::exp(-posTrack.eta())); - float negTheta = 2.f * std::atan(std::exp(-negTrack.eta())); - float photonDeltaTheta = posTheta - negTheta; - histos.fill(HIST("Photon/hDeltaTheta"), photonDeltaTheta); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Photon/True/hDeltaTheta"), photonDeltaTheta); - } - } + // difference of the daughter tracks' polar angles + float photonDeltaTheta = 2.f * std::atan(std::exp(-posTrack.eta())) - 2.f * std::atan(std::exp(-negTrack.eta())); + fillPhotonHist(HIST("hDeltaTheta"), isSignal, photonDeltaTheta); if (std::abs(photonDeltaTheta) > photonMaxDeltaTheta) { continue; } fillPhotonStep(8); std::array pGamma{pNeg[0] + pPos[0], pNeg[1] + pPos[1], pNeg[2] + pPos[2]}; - float gammaP = std::sqrt(dot3(pGamma, pGamma)); - - std::array flightVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; - float flightNorm = std::sqrt(dot3(flightVec, flightVec)); - float cosPA = dot3(flightVec, pGamma) / (flightNorm * gammaP); + float cosPA = RecoDecay::cpa(pv, secVtx, pGamma); if (cosPA < photonMinV0cospa) { continue; } @@ -594,19 +564,428 @@ struct Sigmaplusbuilder { continue; } fillPhotonStep(13); + fillSigmaCounter(sigmaId, 4); - histos.fill(HIST("Photon/hMass"), mGamma); - histos.fill(HIST("Photon/hPt"), std::hypot(pGamma[0], pGamma[1])); - histos.fill(HIST("Photon/hRadius"), radius); - histos.fill(HIST("Photon/h2ArmenterosPodolanski"), alpha, qtarm); - histos.fill(HIST("Photon/h2ConvPointXY"), secVtx[0], secVtx[1]); + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, secVtx[0], secVtx[1]); - photons.push_back({secVtx[0], secVtx[1], secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, negTrack, posTrack}); + photons.push_back({secVtx[0], secVtx[1], secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, v0.dcaV0daughters(), cosPA, negTrack, posTrack}); } return photons; } + static uint64_t photonId(int64_t posTrackId, int64_t negTrackId) + { + return (static_cast(posTrackId) << 32) | static_cast(negTrackId); + } + + // psi_pair of the photon daughters (similar to PsiPair in PWGEM) + template + float photonPsiPair(const TTrack& posTrack, const TTrack& negTrack, float convRadius) + { + for (const float& offsetR : {60.f, 30.f, 10.f}) { + auto posTrackPar = getTrackParCov(posTrack); + auto negTrackPar = getTrackParCov(negTrack); + posTrackPar.setPID(o2::track::PID::Electron); + negTrackPar.setPID(o2::track::PID::Electron); + if (!o2::base::Propagator::Instance()->propagateToR(posTrackPar, convRadius + offsetR) || !o2::base::Propagator::Instance()->propagateToR(negTrackPar, convRadius + offsetR)) { + continue; + } + std::array pPos{}; + std::array pNeg{}; + posTrackPar.getPxPyPzGlo(pPos); + negTrackPar.getPxPyPzGlo(pNeg); + return o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + } + return 999.f; + } + + template + void markGoodCollisions(const TCollisions& collisions) + { + mGoodCollision.assign(collisions.size(), false); + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + mGoodCollision[collision.globalIndex()] = true; + } + } + + // photon daughter selection of the self-pairing (TPC histograms filled before cuts) + template + bool selectPhotonDaughter(const TTrack& track, bool isSignalLeg) + { + if (track.eta() < photonDauEtaMin || track.eta() > photonDauEtaMax) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } else { + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } + if (track.tpcNClsFound() < photonDauMinTpcNCls) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } else { + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } + if (track.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || track.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { + return false; + } + return track.pt() <= photonDauMaxPt; + } + + // collisions of a track with only TPC time (all inside time range [t - backward, t + forward] plus a margin) + template + TrackCand tpcOnlyTrackCand(const TTrack& track, const std::vector& collisionBcNS, const std::vector& collisionTimeNS) + { + o2::aod::track::extensions::TPCTimeErrEncoding timeEncoding{}; + timeEncoding.encoding.timeErr = track.trackTimeRes(); + double trackTimeNS = collisionBcNS[track.collisionId()] + track.trackTime(); // trackTime() is relative to the BC of its collision + double timeMin = trackTimeNS - timeEncoding.getDeltaTBwd() - photonPoolTimeMarginNS; + double timeMax = trackTimeNS + timeEncoding.getDeltaTFwd() + photonPoolTimeMarginNS; + int firstCollIdx = track.collisionId(); + int lastCollIdx = firstCollIdx; + for (int collIdx = 0; collIdx < static_cast(collisionTimeNS.size()); ++collIdx) { + if (collisionTimeNS[collIdx] >= timeMin && collisionTimeNS[collIdx] <= timeMax) { + firstCollIdx = std::min(firstCollIdx, collIdx); + lastCollIdx = std::max(lastCollIdx, collIdx); + } + } + histos.fill(HIST("Photon/Inclusive/hTpcTimeRangeNColl"), lastCollIdx - firstCollIdx + 1); + return TrackCand{.Idxtr = static_cast(track.globalIndex()), .collBracket = {firstCollIdx, lastCollIdx}}; + } + + // electron-positron pairs with |theta+ - theta-| <= photonMaxDeltaTheta that share at least one collision (theta should be similar) + std::vector findPairsByPolarAngle(const std::vector& thetaOfTrack, int nCollisions) + { + // electrons of each collision sorted by polar angle, so a positron only looks at those inside its theta window + std::vector>> electronsByCollision(nCollisions); + for (int iElectron = 0; iElectron < static_cast(mElectronPool.size()); ++iElectron) { + const auto& electron = mElectronPool[iElectron]; + for (int collIdx = electron.collBracket.getMin(); collIdx <= electron.collBracket.getMax(); ++collIdx) { + electronsByCollision[collIdx].push_back({thetaOfTrack[electron.Idxtr], iElectron}); + } + } + for (size_t collIdx = 0; collIdx < electronsByCollision.size(); ++collIdx) { + std::sort(electronsByCollision[collIdx].begin(), electronsByCollision[collIdx].end()); + } + + std::vector pairs; + std::vector lastPositronPaired(mElectronPool.size(), -1); // an electron in several collisions is paired once per positron + for (int iPositron = 0; iPositron < static_cast(mPositronPool.size()); ++iPositron) { + const auto& positron = mPositronPool[iPositron]; + float thetaMin = thetaOfTrack[positron.Idxtr] - photonMaxDeltaTheta; + float thetaMax = thetaOfTrack[positron.Idxtr] + photonMaxDeltaTheta; + for (int collIdx = positron.collBracket.getMin(); collIdx <= positron.collBracket.getMax(); ++collIdx) { + const auto& electronsOfCollision = electronsByCollision[collIdx]; + auto electronIt = std::lower_bound(electronsOfCollision.begin(), electronsOfCollision.end(), std::make_pair(thetaMin, -1)); + for (; electronIt != electronsOfCollision.end() && electronIt->first <= thetaMax; ++electronIt) { + int iElectron = electronIt->second; + if (lastPositronPaired[iElectron] == iPositron) { + continue; + } + lastPositronPaired[iElectron] = iPositron; + const auto& electron = mElectronPool[iElectron]; + pairs.push_back(SVCand{.tr0Idx = electron.Idxtr, .tr1Idx = positron.Idxtr, .collBracket = electron.collBracket.getOverlap(positron.collBracket)}); + } + } + } + return pairs; + } + + // cuts before the fit (similar polar angles, and the circles of the two tracks in xy touching at the conversion point) + template + bool passesPreFitCuts(const TTrack& posTrack, const TTrack& negTrack, float deltaTheta, bool isSignal) + { + fillPhotonHist(HIST("hDeltaTheta"), isSignal, deltaTheta); + if (std::abs(deltaTheta) > photonMaxDeltaTheta) { + return false; + } + float sna = 0.f; + float csa = 0.f; + o2::math_utils::CircleXYf_t posCircle; + o2::math_utils::CircleXYf_t negCircle; + getTrackParCov(posTrack).getCircleParams(mBz, posCircle, sna, csa); + getTrackParCov(negTrack).getCircleParams(mBz, negCircle, sna, csa); + float circleTouchDist = std::abs(std::hypot(posCircle.xC - negCircle.xC, posCircle.yC - negCircle.yC) - (posCircle.rC + negCircle.rC)); + return circleTouchDist <= photonMaxCircleTouchDist; + } + + static constexpr int LastDaughterCutStep = 4; + + // photon vertex fitted in one collision + struct PhotonFit { + float dca = 0.f; + std::array secVtx{}; + std::array pPos{}; + std::array pNeg{}; + float posTrackZ = 0.f; // z of the positive daughter at the fit, after moving it to the collision's time + }; + + // moves a track with only a TPC time to the time of the collision + template + bool moveToCollision(const TCollision& collision, const TTrack& track, o2::track::TrackParCov& trackParCov) + { + if (!(track.flags() & o2::aod::track::TrackTimeAsym)) { + return true; + } + return mVDriftMgr.moveTPCTrack(collision, track, trackParCov); + } + + // vertex fit of the photon daughters, in the collinear mode if a daughter has no ITS + bool fitPhoton(o2::track::TrackParCov posTrackParCov, o2::track::TrackParCov negTrackParCov, bool collinear, PhotonFit& fit) + { + posTrackParCov.setPID(o2::track::PID::Electron); + negTrackParCov.setPID(o2::track::PID::Electron); + + // photon fit settings, reset afterwards since the fitter is shared with the Sigma+ vertex fit + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + fitter.setMaxDXYIni(photonMaxDXYIni); + fitter.setCollinear(collinear); + int nCand = 0; + try { + nCand = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + nCand = 0; + } + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + fitter.setMaxDXYIni(1e9); + fitter.setCollinear(false); + if (nCand == 0 || !fitter.propagateTracksToVertex()) { + return false; + } + + fit.dca = std::sqrt(fitter.getChi2AtPCACandidate()); + fit.secVtx = fitter.getPCACandidatePos(); + fitter.getTrack(0).getPxPyPzGlo(fit.pPos); + fitter.getTrack(1).getPxPyPzGlo(fit.pNeg); + fit.posTrackZ = posTrackParCov.getZ(); + return true; + } + + // a photon is kept in every compatible collision where it passes the cuts, and the proton of that collision then decides + template + std::vector>> findPhotonsSelfPaired(const TCollisions& collisions, const TTracks& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + std::vector>> photonsByCollision(collisions.size()); + + svPhotonPoolCreator.clearPools(); + svPhotonPoolCreator.fillBC2Coll(collisions, bcs); + mElectronPool.clear(); + mPositronPool.clear(); + + // BC start and time of every collision, in ns relative to the BC of the first collision + std::vector collisionBcNS(collisions.size()); + std::vector collisionTimeNS(collisions.size()); + uint64_t firstGlobalBC = collisions.begin().template bc_as().globalBC(); + for (const auto& collision : collisions) { + int64_t bcDiff = static_cast(collision.template bc_as().globalBC()) - static_cast(firstGlobalBC); + collisionBcNS[collision.globalIndex()] = bcDiff * o2::constants::lhc::LHCBunchSpacingNS; + collisionTimeNS[collision.globalIndex()] = collisionBcNS[collision.globalIndex()] + collision.collisionTime(); + } + + // select the daughter tracks and find the collisions each is compatible with + std::vector thetaOfTrack(tracks.size(), 0.f); + std::vector mcPhotonOfTrack(tracks.size(), -1); // MC: Sigma+ photon the track comes from + std::vector mcSigmaOfTrack(tracks.size(), -1); // MC: Sigma+ the track comes from + for (const auto& track : tracks) { + int mcPhoton = -1; + int mcSigma = -1; + if constexpr (IsMC) { + if (track.has_mcParticle()) { + mcSigma = findSigmaPlusAncestorOfPhotonDaughter(track.template mcParticle_as(), mcPhoton); + } + } + if (!selectPhotonDaughter(track, mcSigma >= 0)) { + continue; + } + thetaOfTrack[track.globalIndex()] = 2.f * std::atan(std::exp(-track.eta())); + mcPhotonOfTrack[track.globalIndex()] = mcPhoton; + mcSigmaOfTrack[track.globalIndex()] = mcSigma; + + if (track.flags() & o2::aod::track::TrackTimeAsym) { + // track with only a TPC time + if (!track.has_collision()) { + continue; + } + if (track.sign() < 0) { + mElectronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } else { + mPositronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } + } else { + // track with an ITS time (matched to collisions by svPoolCreator) + svPhotonPoolCreator.appendTrackCand(track, collisions, track.sign() < 0 ? PDG_t::kElectron : PDG_t::kPositron, ambiguousTracks, bcs); + } + } + + // svPoolCreator pools are ordered dau0 pos, dau0 neg, dau1 pos, dau1 neg: electrons are in 1, positrons are in 2 + auto timeMatchedPools = svPhotonPoolCreator.getTrackCandPool(); + mElectronPool.insert(mElectronPool.end(), timeMatchedPools[1].begin(), timeMatchedPools[1].end()); + mPositronPool.insert(mPositronPool.end(), timeMatchedPools[2].begin(), timeMatchedPools[2].end()); + + auto pairs = findPairsByPolarAngle(thetaOfTrack, collisions.size()); + + // fit and select each pair in every good collision it is compatible with + uint64_t nTruePairs = 0; + for (const auto& pair : pairs) { + int electronIdx = pair.tr0Idx; + int positronIdx = pair.tr1Idx; + int sigmaId = -1; + if constexpr (IsMC) { + if (mcPhotonOfTrack[electronIdx] >= 0 && mcPhotonOfTrack[electronIdx] == mcPhotonOfTrack[positronIdx]) { + sigmaId = mcSigmaOfTrack[electronIdx]; + nTruePairs++; + } + } + bool isSignal = sigmaId >= 0; + + auto negTrack = tracks.rawIteratorAt(electronIdx); + auto posTrack = tracks.rawIteratorAt(positronIdx); + if (!passesPreFitCuts(posTrack, negTrack, thetaOfTrack[positronIdx] - thetaOfTrack[electronIdx], isSignal)) { + continue; + } + + // both daughters with only a TPC time on the same TPC side: moving to another collision shifts both by the same z, + // so the pair is fitted once and the vertex is only shifted in z for the other collisions + bool posTpcTime = posTrack.flags() & o2::aod::track::TrackTimeAsym; + bool negTpcTime = negTrack.flags() & o2::aod::track::TrackTimeAsym; + bool sameShiftForAllCollisions = posTpcTime && negTpcTime && ((posTrack.tgl() > 0.f) == (negTrack.tgl() > 0.f)); + bool collinearFit = !posTrack.hasITS() || !negTrack.hasITS(); + std::optional referenceFit; + + int maxStepReached = LastDaughterCutStep; // furthest selection step reached in any collision (for selection counter histograms) + auto passStep = [&](int step) { maxStepReached = std::max(maxStepReached, step); }; + bool anyFitOk = false; + float smallestDca = 1e9f; + bool openingAngleFilled = false; + int nCopies = 0; + + for (int collIdx = pair.collBracket.getMin(); collIdx <= pair.collBracket.getMax(); ++collIdx) { + if (!mGoodCollision[collIdx]) { + continue; + } + auto collision = collisions.rawIteratorAt(collIdx); + + PhotonFit fit; + if (sameShiftForAllCollisions && referenceFit) { + auto posTrackParCov = getTrackParCov(posTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov)) { + continue; + } + fit = *referenceFit; + fit.secVtx[2] += posTrackParCov.getZ() - referenceFit->posTrackZ; + } else { + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov) || !moveToCollision(collision, negTrack, negTrackParCov)) { + continue; + } + bool fitOk = fitPhoton(posTrackParCov, negTrackParCov, collinearFit, fit); + if (fitOk) { + anyFitOk = true; + smallestDca = std::min(smallestDca, fit.dca); + } + if (!fitOk || fit.dca > photonMaxDCAV0Dau) { + if (sameShiftForAllCollisions) { + break; // same result in every collision + } + continue; + } + if (sameShiftForAllCollisions) { + referenceFit = fit; + } + } + passStep(5); + + float radius = std::hypot(fit.secVtx[0], fit.secVtx[1]); + if (radius < photonMinRadius || radius > photonMaxRadius) { + continue; + } + passStep(6); + + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (!openingAngleFilled) { // once per pair + openingAngleFilled = true; + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + } + if (photonOpeningAngle > photonMaxOpeningAngle) { + continue; + } + passStep(8); // opening angle, and delta theta that is cut before the fit + + std::array pGamma{fit.pNeg[0] + fit.pPos[0], fit.pNeg[1] + fit.pPos[1], fit.pNeg[2] + fit.pPos[2]}; + float cosPA = RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, fit.secVtx, pGamma); + if (cosPA < photonMinV0cospa) { + continue; + } + passStep(9); + + float photonY = RecoDecay::y(pGamma, o2::constants::physics::MassGamma); + if (photonY < photonMinRapidity || photonY > photonMaxRapidity) { + continue; + } + passStep(10); + + float qtarm = v0Qt(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + float alpha = v0Alpha(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (qtarm > photonMaxQt) { + continue; + } + passStep(11); + + if (std::abs(alpha) > photonMaxAlpha) { + continue; + } + passStep(12); + + float mGamma = RecoDecay::m(std::array{fit.pPos, fit.pNeg}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (mGamma > photonMaxMass) { + continue; + } + passStep(13); + + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, fit.secVtx[0], fit.secVtx[1]); + + nCopies++; + photonsByCollision[collIdx].push_back({fit.secVtx[0], fit.secVtx[1], fit.secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, fit.dca, cosPA, negTrack, posTrack}); + } + + if (anyFitOk) { + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, smallestDca); + } + if (nCopies > 0) { + fillPhotonHist(HIST("hCollisionsPerPhoton"), isSignal, nCopies); + fillSigmaCounter(sigmaId, 4); + } + for (int step = 5; step <= maxStepReached; ++step) { + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); + } + } + + // steps 0-4 are the daughter-track cuts applied before the pairing, so they count all pairs + for (int step = 0; step <= LastDaughterCutStep; ++step) { + histos.fill(HIST("Photon/Inclusive/hSelectionCounter"), step, static_cast(pairs.size())); + if constexpr (IsMC) { + histos.fill(HIST("Photon/True/hSelectionCounter"), step, static_cast(nTruePairs)); + } + } + + return photonsByCollision; + } + // proton candidate selection template bool selectProton(const TTrack& track) @@ -614,17 +993,11 @@ struct Sigmaplusbuilder { bool isSignal = false; if constexpr (IsMC) { if (track.has_mcParticle()) { - auto mcProton = track.template mcParticle_as(); - isSignal = findSigmaPlusMotherOfProton(mcProton) >= 0; + isSignal = findSigmaPlusMotherOfProton(track.template mcParticle_as()) >= 0; } } auto fillProtonStep = [&](int step) { - histos.fill(HIST("Proton/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/hSelectionCounter"), step); - } - } + fillProtonHist(HIST("hSelectionCounter"), isSignal, step); }; fillProtonStep(0); @@ -638,12 +1011,7 @@ struct Sigmaplusbuilder { } fillProtonStep(2); - histos.fill(HIST("Proton/h2TPCNClsVsPt"), track.pt(), track.tpcNClsFound()); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/h2TPCNClsVsPt"), track.pt(), track.tpcNClsFound()); - } - } + fillProtonHist(HIST("h2TPCNClsVsPt"), isSignal, track.pt(), track.tpcNClsFound()); if (track.tpcNClsFound() < protonMinTpcNCls) { return false; } @@ -654,8 +1022,6 @@ struct Sigmaplusbuilder { } fillProtonStep(4); - // A track with no TOF hit at all is tolerated even above protonPtMinRequireTOF - only an existing-but-failing - // TOF nSigma gets rejected. Set protonRequireTofHit=true to instead mandate a TOF hit above the threshold. if (track.pt() > protonPtMinRequireTOF) { if (track.hasTOF()) { if (std::abs(track.tofNSigmaPr()) > protonMaxTOFNSigma) { @@ -667,37 +1033,21 @@ struct Sigmaplusbuilder { } fillProtonStep(5); - // proton DCA to PV - histos.fill(HIST("Proton/h2DcaToPVVsPt"), track.pt(), std::abs(track.dcaXY())); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/h2DcaToPVVsPt"), track.pt(), std::abs(track.dcaXY())); - } - } + fillProtonHist(HIST("h2DcaToPVVsPt"), isSignal, track.pt(), std::abs(track.dcaXY())); if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV) { return false; } fillProtonStep(6); - histos.fill(HIST("Proton/hPt"), track.pt()); - histos.fill(HIST("Proton/h2TPCNSigmaVsPt"), track.pt(), track.tpcNSigmaPr()); + fillProtonHist(HIST("hPt"), isSignal, track.pt()); + fillProtonHist(HIST("h2TPCNSigmaVsPt"), isSignal, track.pt(), track.tpcNSigmaPr()); if (track.hasTOF()) { - histos.fill(HIST("Proton/h2TOFNSigmaVsPt"), track.pt(), track.tofNSigmaPr()); - } - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Proton/True/hPt"), track.pt()); - histos.fill(HIST("Proton/True/h2TPCNSigmaVsPt"), track.pt(), track.tpcNSigmaPr()); - if (track.hasTOF()) { - histos.fill(HIST("Proton/True/h2TOFNSigmaVsPt"), track.pt(), track.tofNSigmaPr()); - } - } + fillProtonHist(HIST("h2TOFNSigmaVsPt"), isSignal, track.pt(), track.tofNSigmaPr()); } return true; } - // small vector helpers used in the Sigma+ reconstruction static float dot3(const std::array& a, const std::array& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; @@ -726,31 +1076,38 @@ struct Sigmaplusbuilder { return mothers.front().globalIndex(); } + // MC: for an e+ or e- from Sigma+ -> pi0 -> photon -> e+e-, returns the Sigma+ index and sets photonIndex (-1 otherwise) template - int findSigmaPlusMotherOfPhoton(const TMcPart& mcPos, const TMcPart& mcNeg) + int findSigmaPlusAncestorOfPhotonDaughter(const TMcPart& mcDaughter, int& photonIndex) { - auto const& posMothers = mcPos.template mothers_as(); - auto const& negMothers = mcNeg.template mothers_as(); - if (posMothers.empty() || negMothers.empty()) { - return -1; - } - auto mcGamma = posMothers.front(); - if (mcGamma.globalIndex() != negMothers.front().globalIndex() || mcGamma.pdgCode() != PDG_t::kGamma) { + auto const& mothers = mcDaughter.template mothers_as(); + if (mothers.empty() || mothers.front().pdgCode() != PDG_t::kGamma) { return -1; } - - auto const& pi0Mothers = mcGamma.template mothers_as(); + auto const& pi0Mothers = mothers.front().template mothers_as(); if (pi0Mothers.empty() || std::abs(pi0Mothers.front().pdgCode()) != PDG_t::kPi0) { return -1; } - auto const& sigmaMothers = pi0Mothers.front().template mothers_as(); if (sigmaMothers.empty() || std::abs(sigmaMothers.front().pdgCode()) != PDG_t::kSigmaPlus) { return -1; } + photonIndex = mothers.front().globalIndex(); return sigmaMothers.front().globalIndex(); } + template + int findSigmaPlusMotherOfPhoton(const TMcPart& mcPos, const TMcPart& mcNeg) + { + int posPhoton = -1; + int negPhoton = -1; + int sigmaId = findSigmaPlusAncestorOfPhotonDaughter(mcPos, posPhoton); + if (sigmaId < 0 || findSigmaPlusAncestorOfPhotonDaughter(mcNeg, negPhoton) < 0 || posPhoton != negPhoton) { + return -1; + } + return sigmaId; + } + template bool isSigmaPlusToProtonPi0(const TMcPart& mcPart) { @@ -823,21 +1180,23 @@ struct Sigmaplusbuilder { } // Build a Sigma+ -> p pi0 candidate from a proton track and a PCM photon - // Returns the MC index of the matched true Sigma+ mother if a signal candidate was built, -1 otherwise template - int buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, const std::array& pv) + void buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, const std::array& pv) { auto posTrack = photon.posTrack; auto negTrack = photon.negTrack; + int photonLegsWithoutIts = (posTrack.hasITS() ? 0 : 1) + (negTrack.hasITS() ? 0 : 1); bool isSignal = false; - bool collisionIdCheck = false; // MC only: true if the proton's true MC collision matches the reconstructed collision - std::array mcTrueVtx{}; // Sigma+ decay vertex - std::array mcTrueMomProton{}; // true MC proton momentum - std::array mcTrueMomGamma{}; // true MC momentum of the measured photon - std::array mcTrueMomSigmaPlus{}; // true MC momentum of the Sigma+ mother - float decayRadiusMC = -999.f; // MC-truth decay radius - float massMC = -999.f; // MC-truth Sigma+ mass + bool protonIsSignal = false; + bool photonIsSignal = false; + bool collisionIdCheck = false; // MC only: true if the proton's MC collision matches the reconstructed collision + std::array mcTrueVtx{}; + std::array mcTrueMomProton{}; + std::array mcTrueMomGamma{}; + std::array mcTrueMomSigmaPlus{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; int matchedSigmaId = -1; if constexpr (IsMC) { if (protonTrack.has_mcParticle()) { @@ -847,9 +1206,9 @@ struct Sigmaplusbuilder { if (protonCollision.has_mcCollision()) { collisionIdCheck = protonCollision.mcCollision().globalIndex() == mcProton.mcCollisionId(); } - auto const& protonMothers = mcProton.template mothers_as(); int protonSigmaIdx = findSigmaPlusMotherOfProton(mcProton); + protonIsSignal = protonSigmaIdx >= 0; if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { auto mcPos = posTrack.template mcParticle_as(); @@ -862,13 +1221,14 @@ struct Sigmaplusbuilder { } int photonSigmaIdx = findSigmaPlusMotherOfPhoton(mcPos, mcNeg); - isSignal = (protonSigmaIdx >= 0 && photonSigmaIdx >= 0 && photonSigmaIdx == protonSigmaIdx); + photonIsSignal = photonSigmaIdx >= 0; + isSignal = protonIsSignal && photonIsSignal && photonSigmaIdx == protonSigmaIdx; } if (isSignal) { + auto mcSigmaPlusMother = mcProton.template mothers_as().front(); mcTrueVtx = {mcProton.vx(), mcProton.vy(), mcProton.vz()}; mcTrueMomProton = {mcProton.px(), mcProton.py(), mcProton.pz()}; - auto mcSigmaPlusMother = protonMothers.front(); mcTrueMomSigmaPlus = {mcSigmaPlusMother.px(), mcSigmaPlusMother.py(), mcSigmaPlusMother.pz()}; matchedSigmaId = mcSigmaPlusMother.globalIndex(); decayRadiusMC = std::hypot(mcTrueVtx[0] - mcSigmaPlusMother.vx(), mcTrueVtx[1] - mcSigmaPlusMother.vy()); @@ -878,18 +1238,14 @@ struct Sigmaplusbuilder { } auto fillCandStep = [&](int step) { - histos.fill(HIST("Candidate/hSelectionCounter"), step); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hSelectionCounter"), step); - } - } + fillCandHist(HIST("hSelectionCounter"), isSignal, step); + fillCandHist(HIST("h2SelectionCounterVsLegType"), isSignal, step, photonLegsWithoutIts); }; fillCandStep(0); // all pairs - // Reject the pair if the proton track is one of the photon's own e+/e- daughter tracks. + // reject the pair if the proton track is one of the photon's own daughter tracks if (protonTrack.globalIndex() == posTrack.globalIndex() || protonTrack.globalIndex() == negTrack.globalIndex()) { - return -1; + return; } fillCandStep(1); // autocorrelation @@ -906,54 +1262,43 @@ struct Sigmaplusbuilder { try { nCand = fitter.process(protonTrackParCov, photonTrackParCov); } catch (...) { - return -1; + return; } if (nCand == 0 || !fitter.propagateTracksToVertex()) { - return -1; + return; } - fillCandStep(2); // Vertex fit + fillCandStep(2); // vertex fit + std::array protonAtPca{}; + std::array photonAtPca{}; + fitter.getTrack(0).getXYZGlo(protonAtPca); + fitter.getTrack(1).getXYZGlo(photonAtPca); - float fitChi2 = fitter.getChi2AtPCACandidate(); - float dcaProtonGamma = std::sqrt(fitChi2); - histos.fill(HIST("Candidate/hDcaProtonGamma"), dcaProtonGamma); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDcaProtonGamma"), dcaProtonGamma); - } - } + float dcaProtonGamma = std::sqrt(fitter.getChi2AtPCACandidate()); + fillCandHist(HIST("hDcaProtonGamma"), isSignal, dcaProtonGamma); if (dcaProtonGamma > candMaxDcaProtonGamma) { - return -1; + return; } fillCandStep(3); // DCA(p,gamma) - std::array secVtx = fitter.getPCACandidatePos(); - float radius = std::hypot(secVtx[0], secVtx[1]); - histos.fill(HIST("Candidate/hRadius"), radius); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRadius"), radius); - float vtxResFromMc = std::hypot(secVtx[0] - mcTrueVtx[0], secVtx[1] - mcTrueVtx[1], secVtx[2] - mcTrueVtx[2]); - histos.fill(HIST("Candidate/True/hVertexResFromMcTruth"), vtxResFromMc); - } + // decay vertex between the proton's and the photon's point of closest approach (relative weight given by candVertexProtonWeight) + std::array secVtx{}; + for (size_t i = 0; i < secVtx.size(); ++i) { + secVtx[i] = candVertexProtonWeight * protonAtPca[i] + (1.f - candVertexProtonWeight) * photonAtPca[i]; } + float radius = std::hypot(secVtx[0], secVtx[1]); + fillCandHist(HIST("hRadius"), isSignal, radius); if (radius < candMinRadius || radius > candMaxRadius) { - return -1; + return; } fillCandStep(4); // radius - // flight direction n and the decay-plane basis n, eIn, eOut std::array flightVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; - std::array nHat = normalize3(flightVec); + std::array nHatOrig = normalize3(flightVec); float flightDistance = std::sqrt(dot3(flightVec, flightVec)); - histos.fill(HIST("Candidate/hFlightDistance"), flightDistance); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hFlightDistance"), flightDistance); - } - } + fillCandHist(HIST("hFlightDistance"), isSignal, flightDistance); if (flightDistance < candMinFlightDistance || flightDistance > candMaxFlightDistance) { - return -1; + return; } fillCandStep(5); // flight distance @@ -962,110 +1307,93 @@ struct Sigmaplusbuilder { fitter.getTrack(0).getPxPyPzGlo(pProton); fitter.getTrack(1).getPxPyPzGlo(pGamma1); - if constexpr (IsMC) { - if (isSignal) { - float trueProtonP = std::sqrt(dot3(mcTrueMomProton, mcTrueMomProton)); - float fitProtonP = std::sqrt(dot3(pProton, pProton)); - histos.fill(HIST("Candidate/True/hProtonMomResFromMcTruth"), (fitProtonP - trueProtonP) / trueProtonP); - - float trueGammaP = std::sqrt(dot3(mcTrueMomGamma, mcTrueMomGamma)); - float fitGammaP = std::sqrt(dot3(pGamma1, pGamma1)); - histos.fill(HIST("Candidate/True/hPhotonMomResFromMcTruth"), (fitGammaP - trueGammaP) / trueGammaP); - } - } - - float e1 = std::sqrt(dot3(pGamma1, pGamma1)); // |p_gamma1| + float e1 = std::sqrt(dot3(pGamma1, pGamma1)); float massPi0 = o2::constants::physics::MassPionNeutral; + constexpr float CoefADegenerateThreshold = 1e-6f; + std::array nHat{}; std::array eOut{}; std::array eIn{}; - float coefA = 0.f, coefB = 0.f, coefK = 0.f, coefL = 0.f; + float coefA = 0.f, coefB = 0.f; float pGamma2In = 0.f, pGamma2Out = 0.f, tPerp2 = 0.f; - float discriminant = -1.f; - std::array nHatOrig = nHat; // each retry perturbs around this - constexpr float CoefADegenerateThreshold = 1e-6f; // guards against dividing by a near-zero quadratic coefficient - - if constexpr (IsMC) { - if (isSignal) { - float cosProtonFlight = dot3(pProton, nHatOrig) / std::sqrt(dot3(pProton, pProton)); - float protonFlightAngle = std::acos(std::clamp(cosProtonFlight, -1.f, 1.f)); - histos.fill(HIST("Candidate/True/hProtonFlightAngle"), protonFlightAngle); - } - } - - int discrIter = 0; - for (; discrIter <= discrRetryMaxIter; ++discrIter) { - if (discrIter > 0) { - TVector3 nHatVec(nHatOrig[0], nHatOrig[1], nHatOrig[2]); - float dTheta = mThetaResoFunc.GetRandom(); - float dPhi = mPhiResoFunc.GetRandom(); - nHatVec.SetMagThetaPhi(1., nHatVec.Theta() + dTheta, nHatVec.Phi() + dPhi); - nHat = {static_cast(nHatVec.X()), static_cast(nHatVec.Y()), static_cast(nHatVec.Z())}; - } + auto solveForFlightDir = [&](const std::array& nHatUse) { + nHat = nHatUse; eOut = normalize3(cross3(pProton, nHat)); // normal to the decay plane - eIn = cross3(eOut, nHat); // in-plane, transverse to n + eIn = cross3(eOut, nHat); // in the decay plane, transverse to n - float pProtonIn = dot3(pProton, eIn); // proton has no out-of-plane component, by construction + float pProtonIn = dot3(pProton, eIn); float pGamma1Long = dot3(pGamma1, nHat); float pGamma1In = dot3(pGamma1, eIn); float pGamma1Out = dot3(pGamma1, eOut); - // in-plane transverse momentum cancels between p, gamma1, gamma2 pGamma2In = -(pProtonIn + pGamma1In); - // out-of-plane momentum cancels between gamma1 and gamma2 alone pGamma2Out = -pGamma1Out; tPerp2 = pGamma2In * pGamma2In + pGamma2Out * pGamma2Out; - // m_pi0^2 = 2*(|p_gamma1||p_gamma2| - p_gamma1.p_gamma2), solved for - // the remaining unknown (p_gamma2 along n) -> quadratic coefA*x^2+coefB*x+coefC=0 - float dot1 = pGamma1In * pGamma2In + pGamma1Out * pGamma2Out; - coefK = massPi0 * massPi0 + 2.f * dot1; - coefL = 2.f * pGamma1Long; - + // m_pi0^2 = 2*(|p_gamma1||p_gamma2| - p_gamma1.p_gamma2) as coefA*x^2 + coefB*x + coefC = 0 + float coefK = massPi0 * massPi0 + 2.f * (pGamma1In * pGamma2In + pGamma1Out * pGamma2Out); + float coefL = 2.f * pGamma1Long; coefA = 4.f * e1 * e1 - coefL * coefL; if (std::abs(coefA) < CoefADegenerateThreshold) { - continue; + return -1.f; } coefB = -2.f * coefK * coefL; float coefC = 4.f * e1 * e1 * tPerp2 - coefK * coefK; + return coefB * coefB - 4.f * coefA * coefC; + }; - discriminant = coefB * coefB - 4.f * coefA * coefC; - if (discriminant >= 0.f) { - break; + // if the measured flight direction gives no real solution, flight directions on rings of increasing + // tilt around it are tried (the first ring with a real solution is taken, and on it the direction with the largest discriminant) + float tiltAngle = 0.f; + float discriminant = solveForFlightDir(nHatOrig); + if (discriminant < 0.f) { + constexpr float MaxAbsNzForZHelperAxis = 0.9f; + std::array helperAxis = std::abs(nHatOrig[2]) < MaxAbsNzForZHelperAxis ? std::array{0.f, 0.f, 1.f} : std::array{1.f, 0.f, 0.f}; + std::array tiltAxisU = normalize3(cross3(nHatOrig, helperAxis)); + std::array tiltAxisV = cross3(nHatOrig, tiltAxisU); + int nRings = static_cast(std::lround(candMaxTilt / candTiltStep)); + for (int ring = 1; ring <= nRings && discriminant < 0.f; ++ring) { + float tilt = ring * candTiltStep; + float bestRingDisc = -1.f; + std::array bestRingDir{}; + for (int iAzimuth = 0; iAzimuth < candTiltNAzimuth; ++iAzimuth) { + float azimuth = o2::constants::math::TwoPI * iAzimuth / candTiltNAzimuth; + std::array tiltDir{}; + for (size_t i = 0; i < tiltDir.size(); ++i) { + tiltDir[i] = std::cos(tilt) * nHatOrig[i] + std::sin(tilt) * (std::cos(azimuth) * tiltAxisU[i] + std::sin(azimuth) * tiltAxisV[i]); + } + float disc = solveForFlightDir(tiltDir); + if (disc >= 0.f && disc > bestRingDisc) { + bestRingDisc = disc; + bestRingDir = tiltDir; + } + } + if (bestRingDisc >= 0.f) { + discriminant = solveForFlightDir(bestRingDir); + tiltAngle = tilt; + } } } - histos.fill(HIST("Candidate/hDiscriminantRetryIter"), discrIter); - histos.fill(HIST("Candidate/hDiscriminant"), discriminant); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDiscriminantRetryIter"), discrIter); - histos.fill(HIST("Candidate/True/hDiscriminant"), discriminant); - } - } + float tiltForHist = discriminant >= 0.f ? tiltAngle : 0.4999f; // no real solution + fillCandHist(HIST("hTiltAngle"), isSignal, tiltForHist); if (discriminant < 0.f) { - return -1; + return; } fillCandStep(6); // real root - // two roots from the quadratic, among both we keep the mass closest to the nominal Sigma+ mass + // of the two roots, keep the one giving the mass closest to the nominal Sigma+ mass float sqrtDisc = std::sqrt(discriminant); std::array roots{(-coefB + sqrtDisc) / (2.f * coefA), (-coefB - sqrtDisc) / (2.f * coefA)}; - // perturbation-free center of the two roots float rootCenter = -coefB / (2.f * coefA); - histos.fill(HIST("Candidate/hRootCenter"), rootCenter); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRootCenter"), rootCenter); - } - } + fillCandHist(HIST("hRootCenter"), isSignal, rootCenter); if (candRejectNegRootCenter && rootCenter < 0.f) { - return -1; + return; } if (rootCenter > candMaxRootCenter) { - return -1; + return; } bool haveCandidate = false; @@ -1092,31 +1420,26 @@ struct Sigmaplusbuilder { } } if (!haveCandidate) { - return -1; + return; } fillCandStep(7); // valid root std::array pSigma{pProton[0] + pGamma1[0] + bestMomGamma2[0], pProton[1] + pGamma1[1] + bestMomGamma2[1], pProton[2] + pGamma1[2] + bestMomGamma2[2]}; float ptSigma = std::hypot(pSigma[0], pSigma[1]); - // candidate DCA to PV o2::track::TrackPar sigmaTrackPar({secVtx[0], secVtx[1], secVtx[2]}, {pSigma[0], pSigma[1], pSigma[2]}, protonTrack.sign(), true); std::array dcaSigmaToPv{}; o2::base::Propagator::Instance()->propagateToDCA(o2::math_utils::Point3D{pv[0], pv[1], pv[2]}, sigmaTrackPar, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dcaSigmaToPv); float candDcaToPV = std::abs(dcaSigmaToPv[0]); - histos.fill(HIST("Candidate/hDcaToPV"), candDcaToPV); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hDcaToPV"), candDcaToPV); - } - } + fillCandHist(HIST("hDcaToPV"), isSignal, candDcaToPV); if (candDcaToPV > candMaxDcaToPV) { - return -1; + return; } fillCandStep(8); // DCA to PV - // AntiSigmaPointingAngle - Fake pointing angle + // angle between the proton momentum and the flight direction, both rotated back to undo the bending + // of the proton between its reference point and the decay vertex std::array protonPath{secVtx[0] - protonOrigPos[0], secVtx[1] - protonOrigPos[1], secVtx[2] - protonOrigPos[2]}; float protonPathLength = std::sqrt(dot3(protonPath, protonPath)); float svRadiusFromPv = std::hypot(secVtx[0] - pv[0], secVtx[1] - pv[1]); @@ -1138,196 +1461,295 @@ struct Sigmaplusbuilder { if (flightDistance / (2.f * rCurve) < 1.f) { alphaVtxRot = -propDir * qProton * bzSign * std::asin(flightDistance / (2.f * rCurve)); } - std::array sigmaVertexVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; std::array sigmaVertexVecRot{ - sigmaVertexVec[0] * std::cos(alphaVtxRot) - sigmaVertexVec[1] * std::sin(alphaVtxRot), - sigmaVertexVec[0] * std::sin(alphaVtxRot) + sigmaVertexVec[1] * std::cos(alphaVtxRot), - sigmaVertexVec[2]}; + flightVec[0] * std::cos(alphaVtxRot) - flightVec[1] * std::sin(alphaVtxRot), + flightVec[0] * std::sin(alphaVtxRot) + flightVec[1] * std::cos(alphaVtxRot), + flightVec[2]}; float antiSigmaPointingAngle = std::acos(std::clamp(dot3(pProtonRot, sigmaVertexVecRot) / std::sqrt(dot3(pProtonRot, pProtonRot) * dot3(sigmaVertexVecRot, sigmaVertexVecRot)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hAntiSigmaPointingAngle"), antiSigmaPointingAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hAntiSigmaPointingAngle"), antiSigmaPointingAngle); - } - } - // window cut + fillCandHist(HIST("hAntiSigmaPointingAngle"), isSignal, antiSigmaPointingAngle); if (antiSigmaPointingAngle < candMinAntiSigmaPointingAngle || antiSigmaPointingAngle > candMaxAntiSigmaPointingAngle) { - return -1; - } - - histos.fill(HIST("Candidate/hMassSigmaPlus"), bestMass); - histos.fill(HIST("Candidate/h2MassVsPt"), ptSigma, bestMass); - histos.fill(HIST("Candidate/h2MassVsRootCenter"), rootCenter, bestMass); - histos.fill(HIST("Candidate/h2MassVsAntiSigmaPointingAngle"), antiSigmaPointingAngle, bestMass); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hMassSigmaPlus"), bestMass); - histos.fill(HIST("Candidate/True/h2MassVsPt"), ptSigma, bestMass); - histos.fill(HIST("Candidate/True/h2MassVsAntiSigmaPointingAngle"), antiSigmaPointingAngle, bestMass); - histos.fill(HIST("Candidate/True/h2MassVsRootCenter"), rootCenter, bestMass); - } + return; } + fillCandHist(HIST("hMassSigmaPlus"), isSignal, bestMass); + fillCandHist(HIST("h2MassVsPt"), isSignal, ptSigma, bestMass); + fillCandHist(HIST("h2MassVsTilt"), isSignal, tiltAngle, bestMass); if (bestMass > candMaxSigmaMass) { - return -1; + return; } fillCandStep(9); // mass float candRapidity = RecoDecay::y(pSigma, o2::constants::physics::MassSigmaPlus); - histos.fill(HIST("Candidate/hRapidity"), candRapidity); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hRapidity"), candRapidity); - } - } + fillCandHist(HIST("hRapidity"), isSignal, candRapidity); if (std::abs(candRapidity) > candMaxRapidity) { - return -1; + return; } fillCandStep(10); // rapidity - // photon (V0) opening angle, recomputed here from the daughters' own raw track momenta - std::array pPosDau{posTrack.px(), posTrack.py(), posTrack.pz()}; - std::array pNegDau{negTrack.px(), negTrack.py(), negTrack.pz()}; - float photonOpeningAngle = std::acos(std::clamp(dot3(pPosDau, pNegDau) / std::sqrt(dot3(pPosDau, pPosDau) * dot3(pNegDau, pNegDau)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hPhotonOpeningAngle"), photonOpeningAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonOpeningAngle"), photonOpeningAngle); - } - } + // photon opening angle from the daughters' track momenta + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(posTrack.px(), posTrack.py(), posTrack.pz(), negTrack.px(), negTrack.py(), negTrack.pz()); + fillCandHist(HIST("hPhotonOpeningAngle"), isSignal, photonOpeningAngle); if (photonOpeningAngle > candMaxPhotonOpeningAngle) { - return -1; + return; } fillCandStep(11); // photon opening angle - // photon pointing angle + // angle between the photon momentum and the line from the decay vertex to the conversion point std::array decVtxToConv{photon.x - secVtx[0], photon.y - secVtx[1], photon.z - secVtx[2]}; float photonPointingAngle = std::acos(std::clamp(dot3(pGamma1, decVtxToConv) / std::sqrt(dot3(pGamma1, pGamma1) * dot3(decVtxToConv, decVtxToConv)), -1.f, 1.f)); - histos.fill(HIST("Candidate/hPhotonPointingAngle"), photonPointingAngle); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonPointingAngle"), photonPointingAngle); - } - } + fillCandHist(HIST("hPhotonPointingAngle"), isSignal, photonPointingAngle); if (photonPointingAngle > candMaxPhotonPointingAngle) { - return -1; + return; } fillCandStep(12); // photon pointing angle - // photon DCA to PV - std::array convPoint{photon.x, photon.y, photon.z}; std::array photonDir = normalize3({photon.px, photon.py, photon.pz}); - std::array pvToConv{pv[0] - convPoint[0], pv[1] - convPoint[1], pv[2] - convPoint[2]}; + std::array pvToConv{pv[0] - photon.x, pv[1] - photon.y, pv[2] - photon.z}; std::array pvToConvCrossDir = cross3(pvToConv, photonDir); float photonDcaToPV = std::sqrt(dot3(pvToConvCrossDir, pvToConvCrossDir)); - histos.fill(HIST("Candidate/hPhotonDcaToPV"), photonDcaToPV); - if constexpr (IsMC) { - if (isSignal) { - histos.fill(HIST("Candidate/True/hPhotonDcaToPV"), photonDcaToPV); - } - } + fillCandHist(HIST("hPhotonDcaToPV"), isSignal, photonDcaToPV); if (photonDcaToPV > candMaxPhotonDcaToPV) { - return -1; + return; } fillCandStep(13); // photon DCA to PV - fillCandStep(14); // filled + float photonPt2 = photon.px * photon.px + photon.py * photon.py; + float tAtDcaXY = photonPt2 > 0.f ? ((pv[0] - photon.x) * photon.px + (pv[1] - photon.y) * photon.py) / photonPt2 : 0.f; + float photonDcaXYToPV = std::hypot(photon.x + tAtDcaXY * photon.px - pv[0], photon.y + tAtDcaXY * photon.py - pv[1]); + float photonDcaZToPV = photon.z + tAtDcaXY * photon.pz - pv[2]; + float psiPair = photonPsiPair(posTrack, negTrack, photon.radius); + + SigmaPlusCandidate cand; + cand.photonId = photonId(posTrack.globalIndex(), negTrack.globalIndex()); + cand.isSignal = isSignal; + cand.photonLegsWithoutIts = photonLegsWithoutIts; + cand.matchedSigmaId = matchedSigmaId; + cand.decVtx = secVtx; + cand.radius = radius; + cand.flightDistance = flightDistance; + cand.dcaProtonGamma = dcaProtonGamma; + cand.dcaToPV = candDcaToPV; + cand.tiltAngle = tiltAngle; + cand.rootCenter = rootCenter; + cand.antiSigmaPointingAngle = antiSigmaPointingAngle; + cand.pProton = pProton; + cand.pGamma1 = pGamma1; + cand.pGamma2 = bestMomGamma2; + cand.protonTpcNSigma = protonTrack.tpcNSigmaPr(); + cand.protonTofNSigma = protonTrack.tofNSigmaPr(); + cand.protonSign = protonTrack.sign(); + cand.protonItsNCls = protonTrack.itsNCls(); + cand.protonTpcNCls = protonTrack.tpcNClsFound(); + cand.protonDcaXY = protonTrack.dcaXY(); + cand.protonDcaZ = protonTrack.dcaZ(); + cand.photonMass = photon.mGamma; + cand.photonAlpha = photon.alpha; + cand.photonQt = photon.qtarm; + cand.photonRadius = photon.radius; + cand.photonOpeningAngle = photonOpeningAngle; + cand.photonPointingAngle = photonPointingAngle; + cand.photonDcaDau = photon.dcaDau; + cand.photonCosPAToPV = photon.cosPAToPV; + cand.photonDcaXYToPV = photonDcaXYToPV; + cand.photonDcaZToPV = photonDcaZToPV; + cand.photonPsiPair = psiPair; + cand.posTpcNSigmaEl = posTrack.tpcNSigmaEl(); + cand.negTpcNSigmaEl = negTrack.tpcNSigmaEl(); + cand.posItsNCls = posTrack.itsNCls(); + cand.negItsNCls = negTrack.itsNCls(); + cand.posTpcNCls = posTrack.tpcNClsFound(); + cand.negTpcNCls = negTrack.tpcNClsFound(); + cand.posTpcNClsFindable = posTrack.tpcNClsFindable(); + cand.negTpcNClsFindable = negTrack.tpcNClsFindable(); + cand.posTpcChi2NCl = posTrack.tpcChi2NCl(); + cand.negTpcChi2NCl = negTrack.tpcChi2NCl(); + cand.posDcaXY = posTrack.dcaXY(); + cand.posDcaZ = posTrack.dcaZ(); + cand.negDcaXY = negTrack.dcaXY(); + cand.negDcaZ = negTrack.dcaZ(); + cand.collisionIdCheck = collisionIdCheck; + cand.protonIsSignal = protonIsSignal; + cand.photonIsSignal = photonIsSignal; + cand.decVtxMC = mcTrueVtx; + cand.pProtonMC = mcTrueMomProton; + cand.pGammaMC = mcTrueMomGamma; + cand.pSigmaPlusMC = mcTrueMomSigmaPlus; + cand.decayRadiusMC = decayRadiusMC; + cand.massMC = massMC; + mCandidatesOfTimeframe.push_back(cand); + } + + template + void fillCandidateTables(const SigmaPlusCandidate& cand) + { if constexpr (IsMC) { if (fillSlimTables) { - slimSigmaPlusCandsMC(radius, - candDcaToPV, dcaProtonGamma, - protonTrack.sign(), - protonTrack.dcaXY(), protonTrack.dcaZ(), - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, - collisionIdCheck, - isSignal, - decayRadiusMC, massMC, - mcTrueMomSigmaPlus[0], mcTrueMomSigmaPlus[1], mcTrueMomSigmaPlus[2]); - return matchedSigmaId; + slimSigmaPlusCandsMC(cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decayRadiusMC, cand.massMC, + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2]); + return; } - sigmaPlusCandsMC(secVtx[0], secVtx[1], secVtx[2], - flightDistance, dcaProtonGamma, - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, photon.alpha, photon.qtarm, photon.radius, - photonOpeningAngle, photonPointingAngle, photonDcaToPV, - rootCenter, antiSigmaPointingAngle, candDcaToPV, - protonTrack.sign(), - protonTrack.itsNCls(), protonTrack.tpcNClsFound(), protonTrack.dcaXY(), protonTrack.dcaZ(), - posTrack.itsNCls(), posTrack.tpcNClsFound(), negTrack.itsNCls(), negTrack.tpcNClsFound(), - collisionIdCheck, - isSignal, - mcTrueVtx[0], mcTrueVtx[1], mcTrueVtx[2], - mcTrueMomProton[0], mcTrueMomProton[1], mcTrueMomProton[2], - mcTrueMomGamma[0], mcTrueMomGamma[1], mcTrueMomGamma[2], - mcTrueMomSigmaPlus[0], mcTrueMomSigmaPlus[1], mcTrueMomSigmaPlus[2], - decayRadiusMC, massMC); + sigmaPlusCandsMC(cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decVtxMC[0], cand.decVtxMC[1], cand.decVtxMC[2], + cand.pProtonMC[0], cand.pProtonMC[1], cand.pProtonMC[2], + cand.pGammaMC[0], cand.pGammaMC[1], cand.pGammaMC[2], + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2], + cand.decayRadiusMC, cand.massMC); } else { if (fillSlimTables) { - slimSigmaPlusCands(radius, - candDcaToPV, dcaProtonGamma, - protonTrack.sign(), - protonTrack.dcaXY(), protonTrack.dcaZ(), - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma); - return matchedSigmaId; + slimSigmaPlusCands(cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl); + return; + } + sigmaPlusCands(cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl); + } + } + + // The same photon can be in candidates of several collisions (with candDeduplicatePhotons only the one with the smallest DCA to PV is written) + template + void deduplicateAndWriteCandidates() + { + std::unordered_map> candsByPhoton; + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + candsByPhoton[mCandidatesOfTimeframe[iCand].photonId].push_back(iCand); + } + std::vector keep(mCandidatesOfTimeframe.size(), !candDeduplicatePhotons); + for (const auto& [photon, cands] : candsByPhoton) { + histos.fill(HIST("Candidate/Inclusive/hCandidatesPerPhoton"), cands.size()); + if (candDeduplicatePhotons) { + int best = cands[0]; + for (const int& iCand : cands) { + if (mCandidatesOfTimeframe[iCand].dcaToPV < mCandidatesOfTimeframe[best].dcaToPV) { + best = iCand; + } + } + keep[best] = true; } - sigmaPlusCands(secVtx[0], secVtx[1], secVtx[2], - flightDistance, dcaProtonGamma, - pProton[0], pProton[1], pProton[2], - pGamma1[0], pGamma1[1], pGamma1[2], - bestMomGamma2[0], bestMomGamma2[1], bestMomGamma2[2], - protonTrack.tpcNSigmaPr(), protonTrack.tofNSigmaPr(), - posTrack.tpcNSigmaEl(), negTrack.tpcNSigmaEl(), - photon.mGamma, photon.alpha, photon.qtarm, photon.radius, - photonOpeningAngle, photonPointingAngle, photonDcaToPV, - rootCenter, antiSigmaPointingAngle, candDcaToPV, - protonTrack.sign(), - protonTrack.itsNCls(), protonTrack.tpcNClsFound(), protonTrack.dcaXY(), protonTrack.dcaZ(), - posTrack.itsNCls(), posTrack.tpcNClsFound(), negTrack.itsNCls(), negTrack.tpcNClsFound()); - } - return matchedSigmaId; + } + + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + if (!keep[iCand]) { + continue; + } + const auto& cand = mCandidatesOfTimeframe[iCand]; + fillCandHist(HIST("hSelectionCounter"), cand.isSignal, 14); + fillCandHist(HIST("h2SelectionCounterVsLegType"), cand.isSignal, 14, cand.photonLegsWithoutIts); + if constexpr (IsMC) { + if (cand.isSignal) { + fillSigmaCounter(cand.matchedSigmaId, 5); + auto genIt = mGenSigmaWritten.find(cand.matchedSigmaId); + if (genIt != mGenSigmaWritten.end()) { + genIt->second = true; + } + } + } + fillCandidateTables(cand); + } + mCandidatesOfTimeframe.clear(); } - void initCCDB(aod::BCs::iterator const& bc) + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) { if (mRunNumber == bc.runNumber()) { return; } mRunNumber = bc.runNumber(); - o2::parameters::GRPMagField* grpmag = ccdb->getForRun(grpmagPath, mRunNumber); + auto* grpmag = ccdb->getForRun(grpmagPath, mRunNumber); o2::base::Propagator::initFieldFromGRP(grpmag); mBz = grpmag->getNominalL3Field(); fitter.setBz(mBz); + if (!mLut) { + mLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + } + o2::base::Propagator::Instance()->setMatLUT(mLut); LOG(info) << "Task initialized for run " << mRunNumber << " with magnetic field " << mBz << " kZG"; } - void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::BCs const&) + void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) { + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if (useCustomVertexer && collisions.size() > 0) { + auto firstBC = collisions.begin().bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } + for (const auto& collision : collisions) { if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } - initCCDB(collision.bc_as()); + initCCDB(collision.bc_as()); histos.fill(HIST("hVertexZ"), collision.posZ()); std::array pv{collision.posX(), collision.posY(), collision.posZ()}; auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - auto acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + std::vector> acceptedPhotons; + if (useCustomVertexer) { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } else { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + } std::vector acceptedProtons; for (const auto& track : tracksThisCollision) { @@ -1342,105 +1764,102 @@ struct Sigmaplusbuilder { } } } + deduplicateAndWriteCandidates(); } PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data", true); - void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::BCs const&, aod::McParticles const& mcParticles, aod::McCollisions const&) + void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) { - std::vector matchedSigmaPlusMcIds; // Sigma+ MC indices that got at least one signal candidate + // generated Sigma+ -> p pi0 in acceptance + mGenSigmaWritten.clear(); + for (const auto& mcPart : mcParticles) { + if (!isSigmaPlusToProtonPi0(mcPart) || std::abs(mcPart.y()) > cutRapMotherMC || mcPart.pt() < cutPtGenMC) { + continue; + } + mGenSigmaWritten[mcPart.globalIndex()] = false; + histos.fill(HIST("MC/hGenSigmaPlusPt"), mcPart.pt()); + histos.fill(HIST("MC/hSigmaPlusCounter"), 0); + } + + constexpr int ElectronLeg = 1; + constexpr int PositronLeg = 2; + std::unordered_map> legsOfPhoton; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + auto mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + fillSigmaCounter(findSigmaPlusMotherOfProton(mcParticle), 1); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kElectron) { + int photonIndex = -1; + std::array conversionVertex{}; + int sigmaId = findSigmaPlusMotherOfConversionElectron(mcParticle, photonIndex, conversionVertex); + if (sigmaId >= 0) { + auto& legs = legsOfPhoton[photonIndex]; + legs.first = sigmaId; + legs.second |= (mcParticle.pdgCode() == PDG_t::kElectron ? ElectronLeg : PositronLeg); + } + } + } + for (const auto& [photonIndex, legs] : legsOfPhoton) { + if (legs.second == (ElectronLeg | PositronLeg)) { + fillSigmaCounter(legs.first, 3); + } + } + + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if (useCustomVertexer && collisions.size() > 0) { + auto firstBC = collisions.begin().bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } for (const auto& collision : collisions) { if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } - initCCDB(collision.bc_as()); + initCCDB(collision.bc_as()); histos.fill(HIST("hVertexZ"), collision.posZ()); std::array pv{collision.posX(), collision.posY(), collision.posZ()}; auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - auto acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); - for (const auto& photon : acceptedPhotons) { - histos.fill(HIST("MC/hPhotonTruthQA"), 0); - auto posTrack = photon.posTrack; - auto negTrack = photon.negTrack; - if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { - histos.fill(HIST("MC/hPhotonTruthQA"), 1); - auto mcPos = posTrack.template mcParticle_as(); - auto mcNeg = negTrack.template mcParticle_as(); - - auto const& posMothers = mcPos.template mothers_as(); - auto const& negMothers = mcNeg.template mothers_as(); - if (!posMothers.empty() && !negMothers.empty()) { - auto mcGamma = posMothers.front(); - if (mcGamma.globalIndex() == negMothers.front().globalIndex() && mcGamma.pdgCode() == PDG_t::kGamma) { - histos.fill(HIST("MC/hPhotonTruthQA"), 2); - auto const& pi0Mothers = mcGamma.template mothers_as(); - if (!pi0Mothers.empty() && std::abs(pi0Mothers.front().pdgCode()) == PDG_t::kPi0) { - histos.fill(HIST("MC/hPhotonTruthQA"), 3); - auto const& sigmaMothers = pi0Mothers.front().template mothers_as(); - if (!sigmaMothers.empty() && std::abs(sigmaMothers.front().pdgCode()) == PDG_t::kSigmaPlus) { - histos.fill(HIST("MC/hPhotonTruthQA"), 4); - - histos.fill(HIST("Photon/True/hMass"), photon.mGamma); - histos.fill(HIST("Photon/True/hPt"), std::hypot(photon.px, photon.py)); - histos.fill(HIST("Photon/True/hRadius"), photon.radius); - histos.fill(HIST("Photon/True/h2ArmenterosPodolanski"), photon.alpha, photon.qtarm); - histos.fill(HIST("Photon/True/h2ConvPointXY"), photon.x, photon.y); - } - } - } - } - } + std::vector> acceptedPhotons; + if (useCustomVertexer) { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } else { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); } std::vector acceptedProtons; for (const auto& track : tracksThisCollision) { if (selectProton(track)) { acceptedProtons.push_back(track); - - histos.fill(HIST("MC/hProtonTruthQA"), 0); if (track.has_mcParticle()) { - histos.fill(HIST("MC/hProtonTruthQA"), 1); - auto mcProton = track.template mcParticle_as(); - if (findSigmaPlusMotherOfProton(mcProton) >= 0) { - histos.fill(HIST("MC/hProtonTruthQA"), 2); - } + fillSigmaCounter(findSigmaPlusMotherOfProton(track.template mcParticle_as()), 2); } } } for (const auto& photon : acceptedPhotons) { for (const auto& proton : acceptedProtons) { - int matchedId = buildSigmaPlusCandidate(proton, photon, pv); - if (matchedId >= 0) { - matchedSigmaPlusMcIds.push_back(matchedId); - } + buildSigmaPlusCandidate(proton, photon, pv); } } } + deduplicateAndWriteCandidates(); - // all generated Sigma+ -> p pi0 decays, regardless of reconstruction + // a table row with the true values for each generated Sigma+ in acceptance without a written candidate for (const auto& mcPart : mcParticles) { - if (!isSigmaPlusToProtonPi0(mcPart)) { - continue; - } - if (std::abs(mcPart.y()) > cutRapMotherMC) { + auto genIt = mGenSigmaWritten.find(mcPart.globalIndex()); + if (genIt == mGenSigmaWritten.end() || genIt->second) { continue; } - if (mcPart.pt() < cutPtGenMC) { - continue; - } - histos.fill(HIST("MC/hGenSigmaPlusPt"), mcPart.pt()); - bool wasReconstructed = std::find(matchedSigmaPlusMcIds.begin(), matchedSigmaPlusMcIds.end(), mcPart.globalIndex()) != matchedSigmaPlusMcIds.end(); - if (wasReconstructed) { - continue; - } - - // this true Sigma+ never made it into any signal candidate: still record its truth info, - // with the reconstructed-side columns set to -999 int pdgProton = mcPart.pdgCode() > 0 ? PDG_t::kProton : PDG_t::kProtonBar; std::array genDecVtx{-999.f, -999.f, -999.f}; std::array genMomProton{-999.f, -999.f, -999.f}; @@ -1456,7 +1875,7 @@ struct Sigmaplusbuilder { if (fillSlimTables) { slimSigmaPlusCandsMC(-999.f, - -999.f, -999.f, + -999.f, -999.f, -999.f, 0, -999.f, -999.f, -999.f, -999.f, -999.f, @@ -1465,8 +1884,11 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, false, - false, + false, false, false, genDecayRadiusMC, genMassMC, mcPart.px(), mcPart.py(), mcPart.pz()); continue; @@ -1480,13 +1902,16 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, - -999.f, -999.f, -999.f, - -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, 0, 0, -999, -999.f, -999.f, 0, -999, 0, -999, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, false, - false, + false, false, false, genDecVtx[0], genDecVtx[1], genDecVtx[2], genMomProton[0], genMomProton[1], genMomProton[2], -999.f, -999.f, -999.f, @@ -1496,14 +1921,15 @@ struct Sigmaplusbuilder { } PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC", false); - void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCs const&) + void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) { + mGenSigmaWritten.clear(); constexpr int MinDauTpcCls = 90; for (const auto& collision : collisions) { if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } - initCCDB(collision.bc_as()); + initCCDB(collision.bc_as()); auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); std::array pv{collision.posX(), collision.posY(), collision.posZ()}; From 869d3a452a5845d958dbd4bf5c4cb2dfebd62023 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:34:41 +0200 Subject: [PATCH 192/648] [PWGCF] Add efficiency and acceptance methods for Run2/3 (#18189) --- .../GenericFramework/Tasks/energyFlowGfw.cxx | 230 +++++++++++++++++- 1 file changed, 220 insertions(+), 10 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx index 4fa7d5d96b0..e1d2ea659cc 100644 --- a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include #include #include @@ -78,7 +80,7 @@ struct EnergyFlowGfw { enum ActivityAxisMode : int { CentralityAxis = 0, - NGlobalTracksAxis, + NchAxis, ZdcSpectatorAxis }; @@ -99,6 +101,20 @@ struct EnergyFlowGfw { HasAllTiming }; + enum EfficiencyStep : uint8_t { + EfficiencyGenerated = 0, + EfficiencyReconstructed, + EfficiencyStepCount + }; + + enum EfficiencySpecies : uint8_t { + EfficiencyCharged = 0, + EfficiencyPion, + EfficiencyKaon, + EfficiencyProton, + EfficiencySpeciesCount + }; + struct : ConfigurableGroup { Configurable vertexZ{"vertexZ", 10.f, "Accepted |z-vertex| (cm)"}; Configurable> ptCuts{"ptCuts", {0.2f, 5.f}, "Track pT cuts (GeV/c)"}; @@ -118,6 +134,8 @@ struct EnergyFlowGfw { Configurable cfgBootstrapSamples{"cfgBootstrapSamples", 10, "Number of FlowContainer bootstrap subsamples; zero disables bootstrapping"}; Configurable cfgActivityAxis{"cfgActivityAxis", CentralityAxis, "FlowContainer x-axis: 0=centrality, 1=number of filtered global tracks, 2=corrected ZDC spectator estimate"}; Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"}; + Configurable cfgFillEfficiency{"fillEfficiency", false, "Fill MC tracking efficiency; ignored by data processes"}; + Configurable cfgFillAcceptance{"fillAcceptance", false, "Fill GFW acceptance weights instead of running the reconstructed analysis"}; Configurable cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"}; Configurable cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"}; @@ -152,16 +170,26 @@ struct EnergyFlowGfw { ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); using CollisionsRun2 = soa::Filtered>; + using CollisionsRun2MC = soa::Join; using CollisionsRun3 = soa::Filtered>; + using CollisionsRun3MC = soa::Join; using Tracks = soa::Filtered>; + aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>>; + using TracksMC = soa::Filtered>; using BCsRun2 = soa::Join; using BCsRun3 = soa::Join; + Preslice tracksPerCollision = aod::track::collisionId; + OutputObj flowContainer{"FlowContainer"}; + OutputObj acceptanceWeights{GFWWeights("weights")}; HistogramRegistry registry{"registry"}; Service ccdb{}; + Service pdgDB{}; std::unique_ptr gfw{std::make_unique()}; std::unique_ptr random{std::make_unique(0)}; @@ -176,8 +204,9 @@ struct EnergyFlowGfw { void init(InitContext const&) { - if (doprocessRun2 && doprocessRun3) { - LOGF(fatal, "Enable only one input format: Run 2 AOD or Run 3 AOD"); + const int enabledProcesses = static_cast(doprocessRun2) + static_cast(doprocessRun2MC) + static_cast(doprocessRun3) + static_cast(doprocessRun3MC); + if (enabledProcesses > 1) { + LOGF(fatal, "Enable only one input process: Run 2 data, Run 2 MC, Run 3 data, or Run 3 MC"); } if (cuts.ptCuts->first >= cuts.ptCuts->second || cuts.etaMax <= 0.f) { LOGF(fatal, "Require ptMin < ptMax and etaMax > 0"); @@ -194,8 +223,11 @@ struct EnergyFlowGfw { if (cfgActivityAxis < CentralityAxis || cfgActivityAxis > ZdcSpectatorAxis) { LOGF(fatal, "cfgActivityAxis must be 0 (centrality), 1 (filtered global tracks), or 2 (corrected ZDC spectators)"); } - if (cfgMaxCentrality < 0.f || cfgMaxCentrality > 100.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) { - LOGF(fatal, "The maximum centrality must be in [0, 100], the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative"); + if (!std::isfinite(cfgMaxCentrality.value) || cfgMaxCentrality < 0.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) { + LOGF(fatal, "The maximum centrality must be finite and non-negative, the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative"); + } + if (cfgMaxCentrality > 100.f) { + LOGF(warning, "cfgMaxCentrality is %.1f (> 100); dummy centrality values will be accepted. Use the Nch or spectator activity axis when calibrated centrality is unavailable", cfgMaxCentrality.value); } const int numberOfRegions = cfgRegions->GetSize(); @@ -247,7 +279,7 @@ struct EnergyFlowGfw { gfw->CreateRegions(); flowContainer.setObject(new FlowContainer("FlowContainer")); const AxisSpec activityAxis = [this]() { - if (cfgActivityAxis.value == NGlobalTracksAxis) { + if (cfgActivityAxis.value == NchAxis) { return AxisSpec{axisNGlobalTracks, "N_{global tracks}"}; } if (cfgActivityAxis.value == ZdcSpectatorAxis) { @@ -260,6 +292,9 @@ struct EnergyFlowGfw { } else { flowContainer->Initialize(profileNames.get(), activityAxis, cfgBootstrapSamples); } + if (cfgFillAcceptance) { + acceptanceWeights->init(true, false); + } const AxisSpec etaAxis{32, -cuts.etaMax.value, cuts.etaMax.value, "#eta"}; registry.add("event/centrality", "Accepted events;centrality;events", HistType::kTH1F, {axisCentrality}); @@ -271,6 +306,19 @@ struct EnergyFlowGfw { registry.add("track/energy", "Energy entering the GFW;E_{T} (GeV);tracks", HistType::kTH1F, {axisEnergy}); registry.add("track/pid", "PID mass assignment;species;tracks", HistType::kTH1F, {{4, -0.5, 3.5}}); + if (cfgFillEfficiency && (doprocessRun2MC || doprocessRun3MC)) { + const AxisSpec efficiencySpeciesAxis{EfficiencySpeciesCount, -0.5, static_cast(EfficiencySpeciesCount) - 0.5, "particle"}; + const AxisSpec efficiencyStepAxis{EfficiencyStepCount, -0.5, static_cast(EfficiencyStepCount) - 0.5, "generated/reconstructed"}; + registry.add("Efficiency/efficiencyHist", "MC tracking efficiency;#it{p}_{T}^{MC};centrality (%);particle;step", HistType::kTHnSparseF, {axisPt, axisCentrality, efficiencySpeciesAxis, efficiencyStepAxis}); + auto efficiencyHistogram = registry.get(HIST("Efficiency/efficiencyHist")); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyCharged + 1, "charged"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyPion + 1, "pion"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyKaon + 1, "kaon"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyProton + 1, "proton"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyGenerated + 1, "generated"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyReconstructed + 1, "reconstructed"); + } + registry.add("zdc/qaSelection", "ZDC QA availability;condition;events", HistType::kTH1F, {{6, -0.5, 5.5}}); registry.add("zdc/energyZNA", "Triggered events with matched ZDC;E_{ZNA};events", HistType::kTH1F, {axisZdcNeutronEnergy}); registry.add("zdc/energyZNC", "Triggered events with matched ZDC;E_{ZNC};events", HistType::kTH1F, {axisZdcNeutronEnergy}); @@ -685,11 +733,79 @@ struct EnergyFlowGfw { return std::hypot(track.pt(), mass / std::cosh(track.eta())); } + template + bool isEfficiencyParticle(TParticle const& particle) const + { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= cuts.etaMax.value || particle.pt() <= cuts.ptCuts.value.first || particle.pt() >= cuts.ptCuts.value.second) { + return false; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0.; + } + + template + int efficiencySpecies(TParticle const& particle) const + { + const int absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kPiPlus) { + return EfficiencyPion; + } + if (absolutePdg == PDG_t::kKPlus) { + return EfficiencyKaon; + } + if (absolutePdg == PDG_t::kProton) { + return EfficiencyProton; + } + return EfficiencyCharged; + } + + template + void fillEfficiencyParticle(TParticle const& particle, float centrality, EfficiencyStep step) + { + if (!isEfficiencyParticle(particle)) { + return; + } + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, EfficiencyCharged, step); + const int species = efficiencySpecies(particle); + if (species != EfficiencyCharged) { + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, species, step); + } + } + + template + void fillTrackingEfficiency(int mcCollisionIndex, TParticles const& particles, TTracks const& tracks, float centrality) + { + for (const auto& particle : particles) { + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyGenerated); + } + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyReconstructed); + } + } + + template + void fillAcceptanceWeights(TCollision const& collision, TTracks const& tracks, float centrality) + { + for (const auto& track : tracks) { + acceptanceWeights->fill(track.phi(), track.eta(), collision.posZ(), track.pt(), centrality, 0); + } + } + template void processCollision(TCollision const& collision, TTracks const& tracks, float centrality, float spectators) { float activity = centrality; - if (cfgActivityAxis.value == NGlobalTracksAxis) { + if (cfgActivityAxis.value == NchAxis) { activity = static_cast(tracks.size()); } else if (cfgActivityAxis.value == ZdcSpectatorAxis) { activity = spectators; @@ -755,6 +871,10 @@ struct EnergyFlowGfw { if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { return; } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centRun2V0M()); + return; + } const auto& bc = collision.foundBC_as(); loadEfficiency(bc.timestamp()); loadAcceptance(bc.timestamp(), bc.runNumber()); @@ -762,21 +882,111 @@ struct EnergyFlowGfw { } PROCESS_SWITCH(EnergyFlowGfw, processRun2, "Process Run 2 AOD data", false); + void processRun2MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun2 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(groupedTracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centRun2V0M(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2MC, "Process matched Run 2 MC for efficiency, acceptance, or reconstructed closure", false); + void processRun3(CollisionsRun3::iterator const& collision, Tracks const& tracks, BCsRun3 const&, aod::Zdcs const&) { float spectators = -1.f; if (!eventSelected(collision, collision.sel8(), static_cast(tracks.size()), collision.centFT0C(), spectators)) { return; } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centFT0C()); + return; + } const auto& bc = collision.foundBC_as(); loadEfficiency(bc.timestamp()); loadAcceptance(bc.timestamp(), bc.runNumber()); processCollision(collision, tracks, collision.centFT0C(), spectators); } PROCESS_SWITCH(EnergyFlowGfw, processRun3, "Process Run 3 AOD data", true); + + void processRun3MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun3 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(groupedTracks.size()), collision.centFT0C(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centFT0C(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3MC, "Process matched Run 3 MC for efficiency, acceptance, or reconstructed closure", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } From 824dbc8fb386959c87271c1bd593b2b04bc31192 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Tue, 6 Oct 2026 15:57:14 +0200 Subject: [PATCH 193/648] [PWGCF] Add DCA fraction and purity QA to HadNucleiFemto (#18182) Co-authored-by: ALICE Action Bot --- .../TableProducer/HadNucleiFemto.cxx | 153 +++++++++++++++++- 1 file changed, 151 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index cafbb4acc3a..bb266f0bdf8 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs +/// \brief Nuclei-hadron femtoscopy task /// \author CMY /// \date 2025-04-10 @@ -768,6 +768,28 @@ struct HadNucleiFemto { {"fraction/hDcaMotherPdgHad", "Hadron DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, {"fraction/hDcaMotherPdgNu", "Nucleus DCA by exact direct-mother PDG; signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + // Generator denominators and source-resolved response matrices for the + // hypertriton-to-primary He3 and triton constraints. Generated momenta + // are physical particle momenta and must never receive the reconstructed + // He3-track x2 rigidity conversion. The two-body histograms are + // conditional on their generated decay channel, so the physical + // branching ratios remain independent offline inputs. + {"fractionMCGen/hPrimaryHe3", "Physical-primary generated He3 in selected MC collisions;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertriton", "Physical-primary generated hypertritons in selected MC collisions;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToHe3Pi", "Physical-primary generated hypertritons in the He3-pion channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterHe3", "Generated direct He3 daughters in the hypertriton-He3-pion channel;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterHe3Pt", "Generated hypertriton-to-He3 decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hPrimaryTriton", "Physical-primary generated tritons in selected MC collisions;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToTritonPi0", "Physical-primary generated hypertritons in the triton-pi0 channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterTriton", "Generated direct triton daughters in the hypertriton-triton-pi0 channel;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterTritonPt", "Generated hypertriton-to-triton decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryHe3GenPtVsRecoPt", "DCA-selected physical-primary He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt", "DCA-selected hypertriton-daughter He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterRecoPt", "DCA-selected hypertriton parent-to-He3 response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryTritonGenPtVsRecoPt", "DCA-selected physical-primary triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt", "DCA-selected hypertriton-daughter triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt", "DCA-selected hypertriton parent-to-triton response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + // Hierarchical MC truth purity counters: all = correct species + mis-ID // + no label; correct species = correct collision + wrong collision; // correct collision = primary + weak + material. @@ -3687,6 +3709,54 @@ struct HadNucleiFemto { const float productionRadius = std::hypot(particle.vx(), particle.vy()); const float signedPt = signedPhysicalPt(track, isNucleus); + // These response matrices use the relaxed DCA-fit selection under which + // this function is called. Keep only correct-collision He3 or triton so + // the reconstructed numerators match the selected-MC-collision generator + // denominators filled below. Hypertriton responses are conditional on the + // explicit He3-pion or triton-pi0 channel; physical branching ratios are + // deliberately not folded into these histograms. + const int absoluteParticlePdg = std::abs(particle.pdgCode()); + if (isNucleus && matchesRecoCollision && (absoluteParticlePdg == He3PDG || absoluteParticlePdg == TritonPDG)) { + const float signedGeneratedPt = particle.pdgCode() > 0 ? particle.pt() : -particle.pt(); + const float generatedRapidity = particle.y(); + if (origin == Primary) { + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } + } else if (origin == WeakDecay && particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + if (std::abs(mother.pdgCode()) != HyperTritonPDG || !mother.isPhysicalPrimary() || + ((mother.pdgCode() > 0) != (particle.pdgCode() > 0))) { + continue; + } + const int expectedPionPdg = absoluteParticlePdg == He3PDG + ? (mother.pdgCode() > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus) + : PDG_t::kPi0; + bool hasExpectedPion = false; + for (const auto& daughter : mother.template daughters_as()) { + if (daughter.pdgCode() == expectedPionPdg) { + hasExpectedPion = true; + break; + } + } + if (!hasExpectedPion) { + continue; + } + const float signedParentPt = mother.pdgCode() > 0 ? mother.pt() : -mother.pt(); + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } + break; + } + } + } + // The detailed histogram is always filled for a truth-PDG-matched track, // including wrong-collision associations, so tighter choices can be made // offline without rerunning the table producer. @@ -3771,15 +3841,93 @@ struct HadNucleiFemto { } } + using SelectedMcCollisionInfo = std::unordered_map>; + + void fillMCGeneratorNucleiFeedDown(const aod::McParticles& mcParticles, const SelectedMcCollisionInfo& selectedMcCollisions) + { + for (const auto& particle : mcParticles) { + const auto collision = selectedMcCollisions.find(particle.mcCollisionId()); + if (collision == selectedMcCollisions.end()) { + continue; + } + const float centrality = collision->second.second; + const int particlePdg = particle.pdgCode(); + const int absolutePdg = std::abs(particlePdg); + const float signedPt = particlePdg > 0 ? particle.pt() : -particle.pt(); + + if (absolutePdg == He3PDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryHe3"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg == TritonPDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryTriton"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg != HyperTritonPDG || !particle.isPhysicalPrimary()) { + continue; + } + + mQaRegistry.fill(HIST("fractionMCGen/hHypertriton"), signedPt, particle.y(), centrality); + + const int expectedHe3Pdg = particlePdg > 0 ? He3PDG : -He3PDG; + const int expectedTritonPdg = particlePdg > 0 ? TritonPDG : -TritonPDG; + const int expectedChargedPionPdg = particlePdg > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus; + bool hasExpectedHe3 = false; + bool hasExpectedTriton = false; + bool hasExpectedChargedPion = false; + bool hasPi0 = false; + float signedHe3Pt = 0.f; + float he3Rapidity = 0.f; + float signedTritonPt = 0.f; + float tritonRapidity = 0.f; + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.pdgCode() == expectedHe3Pdg) { + hasExpectedHe3 = true; + signedHe3Pt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + he3Rapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedTritonPdg) { + hasExpectedTriton = true; + signedTritonPt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + tritonRapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedChargedPionPdg) { + hasExpectedChargedPion = true; + } else if (daughter.pdgCode() == PDG_t::kPi0) { + hasPi0 = true; + } + } + + if (hasExpectedHe3 && hasExpectedChargedPion) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToHe3Pi"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterHe3"), signedHe3Pt, he3Rapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterHe3Pt"), signedPt, signedHe3Pt, particle.y(), he3Rapidity, centrality); + } + if (hasExpectedTriton && hasPi0) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToTritonPi0"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterTriton"), signedTritonPt, tritonRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterTritonPt"), signedPt, signedTritonPt, particle.y(), tritonRapidity, centrality); + } + } + } + // MC DCA templates use the relaxed-DCA selection. MC purity uses the full // nominal candidate selection, including its DCA requirement. - void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles& mcParticles, const aod::BCsWithTimestamps& bcs) { + SelectedMcCollisionInfo selectedMcCollisions; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; } + if (collision.has_mcCollision()) { + const int64_t mcCollisionId = collision.mcCollisionId(); + const int numberOfContributors = collision.numContrib(); + const auto storedCollision = selectedMcCollisions.find(mcCollisionId); + if (storedCollision == selectedMcCollisions.end() || numberOfContributors > storedCollision->second.first) { + selectedMcCollisions[mcCollisionId] = {numberOfContributors, collision.centFT0C()}; + } + } + const uint64_t collIdx = collision.globalIndex(); auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); tracksThisCollision.bindExternalIndices(&tracks); @@ -3840,6 +3988,7 @@ struct HadNucleiFemto { } } } + fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions); } PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false); From 63b25d583fb039ae78b0366ebbe6c607f9d74e3d Mon Sep 17 00:00:00 2001 From: Oussama3030 <59825001+Oussama3030@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:08:07 +0200 Subject: [PATCH 194/648] [PWGLF] added two new variables for ML use for lambda star analysis (#18185) --- .../Tasks/Resonances/k892hadronphotonBkg.cxx | 26 +++++++++++-------- PWGLF/Tasks/Strangeness/sigmaanalysis.cxx | 6 ++++- PWGLF/Utils/ResonanceMlResponse.h | 18 ++++++++++--- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx index 0a3ab731da2..2dbfcfce7be 100644 --- a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx +++ b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx @@ -341,6 +341,8 @@ struct k892hadronphotonBkg { histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {axisConfig.mlProb, axisConfig.axisV0Radius}); histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {axisConfig.mlProb, axisConfig.axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {axisConfig.mlProb, axisConfig.axisAPQt}); } histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); @@ -894,7 +896,7 @@ struct k892hadronphotonBkg { //_______________________________________________ // Fill BDT performance QA template - void fillBDTPerformance(TV0Object const& lambda, TV0Object const& photon, float openAngle, float score, float pt, float mass) + void fillBDTPerformance(TV0Object const& lambda, TV0Object const& photon, float openAngle, float apAlpha, float apQt, float score, float pt, float mass) { float bkgScore = 1.0f - score; @@ -924,24 +926,26 @@ struct k892hadronphotonBkg { histos.fill(HIST("BDT/h2dPhotonQt"), score, photon.qtarm()); histos.fill(HIST("BDT/h2dPhotonRadius"), score, photon.v0radius()); histos.fill(HIST("BDT/h2dOPAngle"), score, openAngle); + histos.fill(HIST("BDT/h2dAPAlpha"), score, apAlpha); + histos.fill(HIST("BDT/h2dAPQt"), score, apQt); } //_______________________________________________ // BDT selection of a Lambda + photon pair template bool selectML(TV0Object const& lambda, TV0Object const& photon, - float openAngle, float pt, float mass) + float openAngle, float apAlpha, float apQt, float pt, float mass) { // No model outside the bdt.ptBinEdges range if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) return false; // Features in the order of bdt.namesInputFeatures - auto inputFeatures = mlResponse.getInputFeatures(lambda, photon, openAngle); + auto inputFeatures = mlResponse.getInputFeatures(lambda, photon, openAngle, apAlpha, apQt); std::vector outputMl; const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin - fillBDTPerformance(lambda, photon, openAngle, outputMl[1], pt, mass); + fillBDTPerformance(lambda, photon, openAngle, apAlpha, apQt, outputMl[1], pt, mass); return isSelected; } @@ -1103,20 +1107,20 @@ struct k892hadronphotonBkg { if (std::abs(rapidity) > maxRap) continue; + // Armenteros-Podolanski of the mixed pair + const std::array gammaMom{photon.px(), photon.py(), photon.pz()}; + const std::array hadronMom{hadron.px(), hadron.py(), hadron.pz()}; + const float apAlpha = armenterosAlpha(gammaMom, hadronMom); + const float apQt = armenterosQt(gammaMom, hadronMom); + // BDT selection (Lambda(1520) only) if constexpr (resonance == kResoLambdaStar) { if (bdt.enableML) { - if (!selectML(hadron, photon, openAngle, pt, mass)) + if (!selectML(hadron, photon, openAngle, apAlpha, apQt, pt, mass)) continue; } } - // Armenteros-Podolanski of the mixed pair - const std::array gammaMom{photon.px(), photon.py(), photon.pz()}; - const std::array hadronMom{hadron.px(), hadron.py(), hadron.pz()}; - const float apAlpha = armenterosAlpha(gammaMom, hadronMom); - const float apQt = armenterosQt(gammaMom, hadronMom); - if constexpr (resonance == kResoKStar) { histos.fill(HIST("KStarBkg/h2dMixedKStarMassVsPt"), mass, pt); histos.fill(HIST("KStarBkg/h3dMixedKStarMassVsPt"), centrality, pt, mass); diff --git a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx index 1e94d5e785c..b7205446f78 100644 --- a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx @@ -398,6 +398,8 @@ struct sigmaanalysis { histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {mlProb, axisAPQt}); histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {mlProb, axisV0Radius}); histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {mlProb, axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {mlProb, axisAPQt}); // MC-truth-based score if (doprocessMonteCarlo) { @@ -1773,6 +1775,8 @@ struct sigmaanalysis { histos.fill(HIST("BDT/h2dPhotonQt"), score, cand.photonQt()); histos.fill(HIST("BDT/h2dPhotonRadius"), score, cand.photonRadius()); histos.fill(HIST("BDT/h2dOPAngle"), score, cand.opAngle()); + histos.fill(HIST("BDT/h2dAPAlpha"), score, cand.lStarAlpha()); + histos.fill(HIST("BDT/h2dAPQt"), score, cand.lStarQtarm()); // MC-truth-based separation (signal = particle + antiparticle) if constexpr (requires { cand.isSigma0(); cand.isLambdaStar(); }) { @@ -1796,7 +1800,7 @@ struct sigmaanalysis { return false; // Features in the order of bdt.namesInputFeatures - auto inputFeatures = mlResponse.getInputFeatures(cand, cand, cand.opAngle()); + auto inputFeatures = mlResponse.getInputFeatures(cand, cand, cand.opAngle(), cand.lStarAlpha(), cand.lStarQtarm()); std::vector outputMl; // [background, signal] const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin diff --git a/PWGLF/Utils/ResonanceMlResponse.h b/PWGLF/Utils/ResonanceMlResponse.h index 8a7932bc42f..e3051ef81c6 100644 --- a/PWGLF/Utils/ResonanceMlResponse.h +++ b/PWGLF/Utils/ResonanceMlResponse.h @@ -60,7 +60,9 @@ enum class InputFeaturesResonance : uint8_t { photonPosTPCNSigmaEl, photonNegTPCNSigmaEl, // Photon-Lambda pair - opAngle + opAngle, + apAlpha, + apQt }; template @@ -72,7 +74,7 @@ class ResonanceMlResponse : public MlResponse /// Input features of a photon-Lambda pair template , typename TLambda, typename TPhoton> - std::vector getInputFeatures(TLambda const& lambda, TPhoton const& photon, float opAngle) + std::vector getInputFeatures(TLambda const& lambda, TPhoton const& photon, float opAngle, float apAlpha, float apQt) { // sigma0builder candidates carry prefixed columns (lambdaQt(), photonQt(), ...), derived V0s the plain V0 getters constexpr bool LambdaFromSigma0 = requires(TLambda const& cand) { cand.lambdaQt(); }; @@ -271,6 +273,14 @@ class ResonanceMlResponse : public MlResponse case InputFeaturesResonance::opAngle: inputFeatures.emplace_back(opAngle); break; + + case InputFeaturesResonance::apAlpha: + inputFeatures.emplace_back(apAlpha); + break; + + case InputFeaturesResonance::apQt: + inputFeatures.emplace_back(apQt); + break; } } return inputFeatures; @@ -307,7 +317,9 @@ class ResonanceMlResponse : public MlResponse {"photonNegEta", static_cast(InputFeaturesResonance::photonNegEta)}, {"photonPosTPCNSigmaEl", static_cast(InputFeaturesResonance::photonPosTPCNSigmaEl)}, {"photonNegTPCNSigmaEl", static_cast(InputFeaturesResonance::photonNegTPCNSigmaEl)}, - {"opAngle", static_cast(InputFeaturesResonance::opAngle)}}; + {"opAngle", static_cast(InputFeaturesResonance::opAngle)}, + {"apAlpha", static_cast(InputFeaturesResonance::apAlpha)}, + {"apQt", static_cast(InputFeaturesResonance::apQt)}}; } }; From fa6afb26d26b00d2d98c9475d2073a5725dbae59 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:39:03 +0200 Subject: [PATCH 195/648] [Tutorial,PWGCF,PWGDQ,PWGEM,PWGHF,PWGJE,PWGLF,PWGUD] BinningPolicyBase: drop default value (#18179) --- .../TableProducer/HadNucleiFemto.cxx | 10 +++---- .../Tasks/piDeFemtoSystematics.cxx | 2 +- PWGCF/Femto/Tasks/femtoPairD0D0.cxx | 6 ++-- .../Tasks/femtoPairMcParticleMcParticle.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 6 ++-- .../Femto/Tasks/femtoPairTrackCharmHadron.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 8 +++--- .../Tasks/femtoPairTrackTwoTrackResonance.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 6 ++-- .../Tasks/femtoPairV0TwoTrackResonance.cxx | 6 ++-- PWGCF/Femto/Tasks/femtoPairV0V0.cxx | 6 ++-- ...oTripletMcParticleMcParticleMcParticle.cxx | 6 ++-- .../Tasks/femtoTripletTrackTrackCascade.cxx | 6 ++-- .../Tasks/femtoTripletTrackTrackTrack.cxx | 6 ++-- .../Femto/Tasks/femtoTripletTrackTrackV0.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskTrackCascade.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskTrackTrack.cxx | 10 +++---- .../Tasks/femtoDreamPairTaskTrackV0.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskV0Reso.cxx | 6 ++-- .../Tasks/femtoDreamPairTaskV0V0.cxx | 6 ++-- ...femtoDreamTripletTaskTrackTrackCascade.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackTrack.cxx | 2 +- ...mtoDreamTripletTaskTrackTrackTrackPbPb.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackV0.cxx | 2 +- .../femtoDreamTripletTaskTrackTrackV0PbPb.cxx | 2 +- .../Tasks/femtodreamPairCascadeCascade.cxx | 6 ++-- .../Tasks/femtoPairLambdaAntilambda.cxx | 6 ++-- .../Tasks/threeParticleCorrelations.cxx | 4 +-- PWGCF/TableProducer/filter2Prong.cxx | 2 +- PWGCF/Tasks/correlations.cxx | 8 +++--- PWGCF/Tasks/dptDptCorrelations.cxx | 4 +-- PWGCF/Tutorial/CFTutorialTask5.cxx | 4 +-- .../Tasks/FoxWolframCorrelation.cxx | 20 ++++++------- .../Tasks/cascDiHadronCorr.cxx | 10 +++---- .../TwoParticleCorrelations/Tasks/corrFit.cxx | 8 +++--- .../Tasks/corrReso.cxx | 4 +-- .../Tasks/diHadronCor.cxx | 6 ++-- .../Tasks/etaDihadron.cxx | 6 ++-- .../Tasks/flowDecorrelation.cxx | 14 +++++----- .../Tasks/longRangeDihadronCor.cxx | 14 +++++----- .../Tasks/longrangeCorrelation.cxx | 10 +++---- .../Tasks/longrangecorrDerived.cxx | 6 ++-- .../Tasks/nucleibalance.cxx | 10 +++---- .../Tasks/pidDiHadron.cxx | 4 +-- .../Tasks/twoParticleCorrelationPp.cxx | 2 +- .../Tasks/twoParticleCorrelationsMpi.cxx | 8 +++--- PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx | 2 +- PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx | 6 ++-- PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx | 6 ++-- PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx | 6 ++-- .../candidateCreatorCharmResoReduced.cxx | 2 +- .../HFC/TableProducer/correlatorD0Hadrons.cxx | 12 ++++---- .../TableProducer/correlatorDMesonPairs.cxx | 2 +- .../TableProducer/correlatorDplusHadrons.cxx | 10 +++---- .../HFC/TableProducer/correlatorDsHadrons.cxx | 12 ++++---- .../correlatorDsHadronsReduced.cxx | 4 +-- .../TableProducer/correlatorDstarHadrons.cxx | 4 +-- .../correlatorFlowCharmHadronsReduced.cxx | 20 ++++++------- .../TableProducer/correlatorHfeHadrons.cxx | 6 ++-- .../HFC/TableProducer/correlatorLcHadrons.cxx | 10 +++---- .../TableProducer/correlatorLcScHadrons.cxx | 8 +++--- .../TableProducer/correlatorXicHadrons.cxx | 8 +++--- .../Tasks/taskCharmHadronsCharmFemtoDream.cxx | 6 ++-- .../Tasks/taskCharmHadronsTrackFemtoDream.cxx | 6 ++-- .../Tasks/taskCharmHadronsV0FemtoDream.cxx | 6 ++-- PWGHF/HFC/Tasks/taskFlow.cxx | 12 ++++---- PWGJE/Tasks/chargedJetHadron.cxx | 6 ++-- PWGJE/Tasks/jetSpectraEseTask.cxx | 6 ++-- .../Tasks/photonChargedTriggerCorrelation.cxx | 2 +- .../Nuspex/decay3bodybuilder.cxx | 4 +-- PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx | 2 +- .../Nuspex/piHypertritonFemto.cxx | 2 +- .../Strangeness/sigmaHadCorr.cxx | 8 +++--- .../Tasks/Nuspex/hadronnucleicorrelation.cxx | 2 +- PWGLF/Tasks/QC/resonanceqa.cxx | 2 +- .../Tasks/Resonances/chargedkstaranalysis.cxx | 2 +- PWGLF/Tasks/Resonances/cksspinalignder.cxx | 2 +- PWGLF/Tasks/Resonances/deltaAnalysis.cxx | 10 +++---- PWGLF/Tasks/Resonances/doublephimeson.cxx | 4 +-- PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx | 2 +- .../Tasks/Resonances/f1protoncorrelation.cxx | 2 +- .../Resonances/hResonanceCorrelation.cxx | 24 ++++++++-------- .../Tasks/Resonances/higherMassResonances.cxx | 10 +++---- PWGLF/Tasks/Resonances/highmasslambda.cxx | 2 +- PWGLF/Tasks/Resonances/highmasslambdasvx.cxx | 2 +- PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx | 4 +-- .../Resonances/k892SpherocityAnalysis.cxx | 4 +-- PWGLF/Tasks/Resonances/k892analysis.cxx | 2 +- PWGLF/Tasks/Resonances/k892analysispbpb.cxx | 8 +++--- .../Tasks/Resonances/k892hadronphotonBkg.cxx | 2 +- PWGLF/Tasks/Resonances/k892pmanalysis.cxx | 2 +- PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx | 4 +-- PWGLF/Tasks/Resonances/kshortlambda.cxx | 4 +-- PWGLF/Tasks/Resonances/kstar0analysis.cxx | 2 +- PWGLF/Tasks/Resonances/kstar892LightIon.cxx | 16 +++++------ PWGLF/Tasks/Resonances/kstar892analysis.cxx | 8 +++--- PWGLF/Tasks/Resonances/kstarInOO.cxx | 4 +-- PWGLF/Tasks/Resonances/kstarpbpb.cxx | 12 ++++---- PWGLF/Tasks/Resonances/kstarqa.cxx | 18 ++++++------ PWGLF/Tasks/Resonances/lambda1405analysis.cxx | 12 ++++---- .../lambda1520SpherocityAnalysis.cxx | 4 +-- PWGLF/Tasks/Resonances/lambda1520analysis.cxx | 2 +- .../Resonances/lambda1520analysisinOO.cxx | 2 +- .../Resonances/lambda1520analysisinpp.cxx | 2 +- .../Tasks/Resonances/lambda1520analysispo.cxx | 6 ++-- PWGLF/Tasks/Resonances/lambda1520pbpb.cxx | 6 ++-- PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 2 +- .../Resonances/phi1020SpherocityAnalysis.cxx | 4 +-- PWGLF/Tasks/Resonances/phi1020analysis.cxx | 2 +- PWGLF/Tasks/Resonances/phianalysis.cxx | 2 +- .../Tasks/Resonances/phianalysisTHnSparse.cxx | 4 +-- PWGLF/Tasks/Resonances/phianalysisrun3.cxx | 6 ++-- .../Tasks/Resonances/phianalysisrun3pbpb.cxx | 10 +++---- PWGLF/Tasks/Resonances/phiflowder.cxx | 7 ++--- PWGLF/Tasks/Resonances/phipbpb.cxx | 8 +++--- PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx | 2 +- PWGLF/Tasks/Resonances/phitutorial.cxx | 2 +- PWGLF/Tasks/Resonances/phitutorial_step3.cxx | 2 +- PWGLF/Tasks/Resonances/rhoanalysis.cxx | 2 +- PWGLF/Tasks/Resonances/sigma.cxx | 2 +- PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx | 4 +-- .../Resonances/xi1530kaoncorrelation.cxx | 2 +- PWGLF/Tasks/Resonances/xi1820Analysis.cxx | 10 +++---- .../Tasks/Strangeness/cascadecorrelations.cxx | 4 +-- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 28 +++++++++---------- .../lambdaJetPolarizationIonsDerived.cxx | 9 ++---- .../Strangeness/lambdaTwoPartPolarization.cxx | 4 +-- PWGLF/Tasks/Strangeness/lambdalambda.cxx | 2 +- PWGLF/Tasks/Strangeness/lambdapolsp.cxx | 2 +- .../Strangeness/lambdaspincorrderived.cxx | 2 +- .../Strangeness/phiStrangeCorrelation.cxx | 2 +- .../Tasks/Strangeness/phik0shortanalysis.cxx | 2 +- .../Tasks/Strangeness/taskLambdaSpinCorr.cxx | 2 +- PWGUD/Tasks/flowCorrelationsUpc.cxx | 2 +- PWGUD/Tasks/flowLongRangeCorrUpc.cxx | 6 ++-- PWGUD/Tasks/sginclusivePhiKstarSD.cxx | 4 +-- PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx | 8 +++--- .../PWGLF/Resonance/resonances_step2.cxx | 2 +- Tutorials/src/eventMixing.cxx | 24 ++++++++-------- Tutorials/src/eventMixingValidation.cxx | 6 ++-- Tutorials/src/tracksCombinations.cxx | 2 +- 142 files changed, 421 insertions(+), 425 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index bb266f0bdf8..3af64a1e310 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -3189,7 +3189,7 @@ struct HadNucleiFemto { std::unordered_map>& mixingPools, int& mixingRunNumber) { - const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}}; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; @@ -3344,8 +3344,8 @@ struct HadNucleiFemto { void processMixedEvent(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { LOG(debug) << "Processing mixed event"; - const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; - const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}}; for (const auto& collision : collisions) { mQaRegistry.fill(HIST("hMixedEventSelections"), 0); @@ -3478,8 +3478,8 @@ struct HadNucleiFemto { void processMixedEventDiagnostic(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { - const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; - const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}}; ++mDiagnosticCall; MEDiagnosticRow call; call.run = mDiagnosticRun; diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx index 515dbde5ba7..08328b97ca9 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -202,7 +202,7 @@ struct PiDeFemtoSystematics { ConfigurableAxis centralityAxis{"centralityAxis", {100, 0., 100.}, "centrality axis"}; using MixingBinning = ColumnBinningPolicy; - MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}, true}; + MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}}; SliceCache cache; SameKindPair mixedEventPairs{ mixingBinning, eventCuts.numberOfMixedEvents, -1, &cache}; diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 675c567a08b..267e8f2c343 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -149,9 +149,9 @@ struct FemtoPairD0D0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index 4d449c44446..e49b4e5557a 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -109,9 +109,9 @@ struct FemtoPairMcParticleMcParticle { { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 78eb2da5dd5..99994021ecb 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -179,9 +179,9 @@ struct FemtoPairTrackCascade { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 8c52006cd68..4a09a585caf 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -180,9 +180,9 @@ struct FemtoPairTrackCharmHadron { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index 66b343d1729..3352ec015ca 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -169,9 +169,9 @@ struct FemtoPairTrackKink { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index d9368a03308..6653b0c6612 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -140,10 +140,10 @@ struct FemtoPairTrackTrack { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; - mixBinsVtxCentEventPlaneAngle = {{confMixing.vtxBins.value, confMixing.centBins.value, confMixing.eventPlaneAngle.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + mixBinsVtxCentEventPlaneAngle = {{confMixing.vtxBins.value, confMixing.centBins.value, confMixing.eventPlaneAngle.value}}; // setup histogram specs std::map> colHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 661c3400c3e..fa6927b65fa 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -159,9 +159,9 @@ struct FemtoPairTrackTwoTrackResonance { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 062ca3dd1df..6466c67b139 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -171,9 +171,9 @@ struct FemtoPairTrackV0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index 52401a050a4..c56750af9f1 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -257,9 +257,9 @@ struct FemtoPairV0TwoTrackResonance { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index ce35f66d2a8..8dd3b8559ef 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -219,9 +219,9 @@ struct FemtoPairV0V0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx index 4200daf64a5..866eaae259f 100644 --- a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -112,9 +112,9 @@ struct FemtoTripletMcParticleMcParticleMcParticle { { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index 03054d45c57..a4addee1ce1 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -200,9 +200,9 @@ struct FemtoTripletTrackTrackCascade { } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 6a89df0a620..6cd97fc7262 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -136,9 +136,9 @@ struct FemtoTripletTrackTrackTrack { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index ee0d984245d..0f67c500627 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -156,9 +156,9 @@ struct FemtoTripletTrackTrackV0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx index 7bfe2261732..3b8164861ee 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx @@ -227,9 +227,9 @@ struct femtoDreamPairTaskTrackCascade { void init(InitContext&) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); trackHistoPartTwo.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTCascade, Option.Dummy, Option.Dummy, Binning.TempFitVarCascade, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Binning.InvMass, Option.Dummy, Option.IsMC, Cascade2.PDGCode); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index 70a83dc2dac..d0afc20d35e 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -283,11 +283,11 @@ struct femtoDreamPairTaskTrackTrack { { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx index f58c5e48363..ccf630b78a8 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx @@ -240,9 +240,9 @@ struct femtoDreamPairTaskTrackV0 { void init(InitContext& context) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx index 95e1a8a106e..5bd1511f013 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx @@ -251,9 +251,9 @@ struct FemtoDreamPairTaskV0Reso { { // setup binnnig policy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx index 01cdfbee150..bc5191724c6 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx @@ -225,9 +225,9 @@ struct femtoDreamPairTaskV0V0 { void init(InitContext&) { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx index 8b7c1ffff6f..1599bedb0bb 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx @@ -157,7 +157,7 @@ struct femtoDreamTripletTaskTrackTrackCascade { void init(InitContext& context) { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); particleHistoSelectedCascades.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Bins, ConfDummy, ConfDummy, ConfTempFitVarBinsCascade, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfInvMassBins, ConfDummy, ConfIsMC, ConfPDGCodeCascade); particleHistoPosChild.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Child, ConfDummy, ConfDummy, ConfTempFitVarBinsCascadeChild, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, 0); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx index 57480dd85c3..7456409d1bc 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx @@ -150,7 +150,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx index 0377a32daee..9a5087d9f6b 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx @@ -157,7 +157,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx index dede75a4906..8702d8d5c77 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx @@ -200,7 +200,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx index a2ab0840f08..1eecc066d27 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx @@ -212,7 +212,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{confVtxBins, confMultBins}, true}; + colBinning = {{confVtxBins, confMultBins}}; trackHistoselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); trackHistoALLselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); diff --git a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx index 11edf4705cd..3c4ebd17713 100644 --- a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx @@ -216,9 +216,9 @@ struct FemtoDreamPairCascadeCascade { void init(InitContext&) { // setup binnnig binPolicy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); cascHistoPartOne.init(®istry, Binning.multTempFit, Option.dummy, Binning.pTCascade, Option.dummy, Option.dummy, Binning.tempFitVarCascade, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.isMC, Cascade1.pdgCode); diff --git a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx index affd2abea83..74e881296e3 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx @@ -173,9 +173,9 @@ struct FemtoPairLambdaAntilambda { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; diff --git a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx index d1aca1a5aab..b78325f9d01 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx @@ -182,8 +182,8 @@ struct ThreeParticleCorrelations { using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType collBinning{{confCentBins, confZvtxBins}, true}; - BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}, true}; + BinningType collBinning{{confCentBins, confZvtxBins}}; + BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}}; Pair pairData{collBinning, 5, -1, &cache}; SameKindPair pairMC{collBinningMC, 5, -1, &cache}; diff --git a/PWGCF/TableProducer/filter2Prong.cxx b/PWGCF/TableProducer/filter2Prong.cxx index bff2a81ee2c..2c90a269e9e 100644 --- a/PWGCF/TableProducer/filter2Prong.cxx +++ b/PWGCF/TableProducer/filter2Prong.cxx @@ -830,7 +830,7 @@ struct Filter2Prong { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}}; auto tracksTuple = std::make_tuple(cftracks, cftracks); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; using TB = std::tuple_element - 1, decltype(tracksTuple)>::type; diff --git a/PWGCF/Tasks/correlations.cxx b/PWGCF/Tasks/correlations.cxx index 46d372b88ae..5a5f41d3de2 100644 --- a/PWGCF/Tasks/correlations.cxx +++ b/PWGCF/Tasks/correlations.cxx @@ -970,7 +970,7 @@ struct CorrelationTask { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -1049,7 +1049,7 @@ struct CorrelationTask { // NOTE legacy function for O2 integration tests. Full version needs derived data // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 - BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{configurableBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1102,7 +1102,7 @@ struct CorrelationTask { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1544,7 +1544,7 @@ struct CorrelationTask { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/Tasks/dptDptCorrelations.cxx b/PWGCF/Tasks/dptDptCorrelations.cxx index 99602575f1d..9f7713c221c 100644 --- a/PWGCF/Tasks/dptDptCorrelations.cxx +++ b/PWGCF/Tasks/dptDptCorrelations.cxx @@ -1489,7 +1489,7 @@ struct DptDptCorrelations { std::vector multBinsEdges{VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0, 100.1f}; SliceCache cache; using BinningZVtxMultRec = ColumnBinningPolicy; - BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) + BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}}; static constexpr int KNoOfLoggingCombinations = 10; void processRecLevelMixed(soa::Filtered const& collisions, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) @@ -1569,7 +1569,7 @@ struct DptDptCorrelations { PROCESS_SWITCH(DptDptCorrelations, processRecLevelMixedNotStored, "Process reco level mixed events correlations for not stored derived data", false); using BinningZVtxMultGen = ColumnBinningPolicy; - BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) + BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}}; void processGenLevelMixed(soa::Filtered const& collisions, soa::Filtered const& tracks) { diff --git a/PWGCF/Tutorial/CFTutorialTask5.cxx b/PWGCF/Tutorial/CFTutorialTask5.cxx index 065fd3da043..6e5d78cdf1c 100644 --- a/PWGCF/Tutorial/CFTutorialTask5.cxx +++ b/PWGCF/Tutorial/CFTutorialTask5.cxx @@ -150,7 +150,7 @@ struct CFTutorialTask5 { void processMixed(MyFilteredCollisions const& colls, MyFilteredTracks const&) { - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, colls, colls)) { auto groupPositive = positive->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); auto groupNegative = negative->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); @@ -175,7 +175,7 @@ struct CFTutorialTask5 { void processMixedEventInterface(MyFilteredCollisions& colls, MyFilteredTracks& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; SameKindPair pair{colBinning, 5, -1, colls, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { Partition groupPositive = aod::track::signed1Pt > ConfChargeCut; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index 04d3f8461c1..217bbcf6872 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -1144,16 +1144,16 @@ struct FoxWolframCorrelationMixing { // Mix events in vertex and multiplicity intervals. using BinningType = ColumnBinningPolicy; - BinningType binningOnPosMultip1{{axisVtx, axisMult1}, true}; - BinningType binningOnPosMultip2{{axisVtx, axisMult2}, true}; - BinningType binningOnPosMultip3{{axisVtx, axisMult3}, true}; - BinningType binningOnPosMultip4{{axisVtx, axisMult4}, true}; - BinningType binningOnPosMultip5{{axisVtx, axisMult5}, true}; - BinningType binningOnPosMultip6{{axisVtx, axisMult6}, true}; - BinningType binningOnPosMultip7{{axisVtx, axisMult7}, true}; - BinningType binningOnPosMultip8{{axisVtx, axisMult8}, true}; - BinningType binningOnPosMultip9{{axisVtx, axisMult9}, true}; - BinningType binningOnPosMultip10{{axisVtx, axisMult10}, true}; + BinningType binningOnPosMultip1{{axisVtx, axisMult1}}; + BinningType binningOnPosMultip2{{axisVtx, axisMult2}}; + BinningType binningOnPosMultip3{{axisVtx, axisMult3}}; + BinningType binningOnPosMultip4{{axisVtx, axisMult4}}; + BinningType binningOnPosMultip5{{axisVtx, axisMult5}}; + BinningType binningOnPosMultip6{{axisVtx, axisMult6}}; + BinningType binningOnPosMultip7{{axisVtx, axisMult7}}; + BinningType binningOnPosMultip8{{axisVtx, axisMult8}}; + BinningType binningOnPosMultip9{{axisVtx, axisMult9}}; + BinningType binningOnPosMultip10{{axisVtx, axisMult10}}; // Create independent mixing pools for the ten multiplicity intervals. SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cache_bin1}; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx index 7aa30d4f202..79f8efb4720 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx @@ -1453,7 +1453,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1514,7 +1514,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(cascades, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1834,7 +1834,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1890,7 +1890,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1977,7 +1977,7 @@ struct CascDiHadronCorr { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx index 27eb0d6fdf5..9376c2fe182 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx @@ -1461,7 +1461,7 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1578,7 +1578,7 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1696,7 +1696,7 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1808,7 +1808,7 @@ struct CorrFit { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx index 338aa2406bf..1f75e51f647 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx @@ -1452,7 +1452,7 @@ struct CorrReso { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1585,7 +1585,7 @@ struct CorrReso { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx index 0458b99b65a..61dfa8787c3 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx @@ -948,7 +948,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1108,7 +1108,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1195,7 +1195,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx index ea041d44f09..24cf76f210a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx @@ -932,7 +932,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1090,7 +1090,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1174,7 +1174,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx index 5780cffa5e6..9a2251ed1f5 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx @@ -1193,7 +1193,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1298,7 +1298,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1400,7 +1400,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; @@ -1552,7 +1552,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1665,7 +1665,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1838,7 +1838,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1953,7 +1953,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx index 5baea7d44b4..8f66d3bfb27 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx @@ -1592,7 +1592,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1746,7 +1746,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1857,7 +1857,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2098,7 +2098,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2168,7 +2168,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2367,7 +2367,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2443,7 +2443,7 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx index ccb73d62d14..40c09df3d8e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx @@ -867,7 +867,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -902,7 +902,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -937,7 +937,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; auto tracksTuple = std::make_tuple(tracks, mfttracks); Pair pairs{binningOnVtxAndMult, mixingParameter, -1, col, tracksTuple, &cache}; for (auto const& [col1, tracks1, col2, tracks2] : pairs) { @@ -967,7 +967,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -1002,7 +1002,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index 54014f8f0e7..9745762a695 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -648,7 +648,7 @@ struct LongrangecorrDerived { return multiplicity; }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; @@ -717,7 +717,7 @@ struct LongrangecorrDerived { }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; @@ -749,7 +749,7 @@ struct LongrangecorrDerived { return collision.multiplicity(); }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx index dc2fc35780c..640352fff2f 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx @@ -857,7 +857,7 @@ struct Nucleibalance { const auto multiplicity = eventMultiplicityFT0MOrFallback(collision, collision.multiplicity()); using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; int bin = configurableBinningDerived.getBin({collision.posZ(), multiplicity}); registry.fill(HIST("eventcount_same"), bin); registry.fill(HIST("trackcount_same"), bin, tracks.size()); @@ -1256,7 +1256,7 @@ struct Nucleibalance { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -1305,7 +1305,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1369,7 +1369,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1602,7 +1602,7 @@ struct Nucleibalance { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx index 40ce655fb4e..af48e2069a7 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx @@ -1276,7 +1276,7 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1512,7 +1512,7 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx index 5a94ee3811a..73b8b8eecfc 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx @@ -360,7 +360,7 @@ struct TwoParticleCorrelationPp { void processMixed(FullCollisions const& collisions, FullTracks const& tracks) { - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; const auto multiplicity = getMultiplicity(collision); if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), multiplicity); @@ -2300,7 +2300,7 @@ struct TwoParticleCorrelationsMpi { // NOTE legacy function for O2 integration tests. Full version needs derived data // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 - BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{configurableBinning, cfgNumMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -2359,7 +2359,7 @@ struct TwoParticleCorrelationsMpi { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -2558,7 +2558,7 @@ struct TwoParticleCorrelationsMpi { }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx index 3fc4da628f5..1396420b61a 100644 --- a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx +++ b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx @@ -195,7 +195,7 @@ struct AnalysisEnergyCorrelator { std::vector multBins = fConfigEventOptions.fConfigMultBins.value; multBins.insert(multBins.begin(), VARIABLE_WIDTH); - fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}, true); + fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}); bool isBarrelME = context.mOptions.get("processBarrelMixedEvent"); bool isMCGen_energycorrelators = context.mOptions.get("processMCGenEnergyCorrelators") || context.mOptions.get("processMCGenEnergyCorrelatorsPion"); diff --git a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx index 6bb206de9dc..f43011ad92c 100644 --- a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx +++ b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx @@ -349,9 +349,9 @@ struct TagAndProbe { using BinningType_M = ColumnBinningPolicy; using BinningType_A = ColumnBinningPolicy; using BinningType_C = ColumnBinningPolicy; - BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}, true}; - BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}, true}; - BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}, true}; + BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}}; + BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}}; + BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}}; template void MixedEventPairing(TEvents const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TPreslice1 const& perCollision1, TPreslice2 const& perCollision2, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts, TLegs const& /*legs*/, TMixedBinning const& colBinning) diff --git a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx index f6a6bad2c2e..b2395f2fe65 100644 --- a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx +++ b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx @@ -704,7 +704,7 @@ struct CalibTaskEmc { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -866,7 +866,7 @@ struct CalibTaskEmc { return; } using BinningTypeMixed = ColumnBinningPolicy; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); @@ -1020,7 +1020,7 @@ struct CalibTaskEmc { return; } using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index df0c4ac6456..ecb1fe06214 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -1173,7 +1173,7 @@ struct TaskPi0FlowEMC { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1367,7 +1367,7 @@ struct TaskPi0FlowEMC { } using BinningTypeMixed = ColumnBinningPolicy>; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); @@ -1576,7 +1576,7 @@ struct TaskPi0FlowEMC { } using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored diff --git a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx index b1affb1cbac..bd7e4f64cc4 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx @@ -765,7 +765,7 @@ struct HfCandidateCreatorCharmResoReduced { V0TrRedTable const& candsV0Tr) { using BinningType = ColumnBinningPolicy; - BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}, true}; + BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}}; auto bachTuple = std::make_tuple(candsD, candsV0Tr); Pair const pairs{corrBinning, cfgMixedEvent.numberEventsMixed, cfgMixedEvent.numberEventsToSkip, collisions, bachTuple, &cache}; for (const auto& [collision1, bachDs, collision2, bachV0Trs] : pairs) { diff --git a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx index e9a4ccd4282..eabbaa2fcbd 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx @@ -383,7 +383,7 @@ struct HfCorrelatorD0Hadrons { SelectedCandidatesDataMl const& candidates, aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto bc = collision.bc_as(); int gCollisionId = collision.globalIndex(); @@ -605,7 +605,7 @@ struct HfCorrelatorD0Hadrons { aod::McParticles const& mcParticles, aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto bc = collision.bc_as(); int gCollisionId = collision.globalIndex(); @@ -935,7 +935,7 @@ struct HfCorrelatorD0Hadrons { void processMcGen(SelectedCollisionsMcGen::iterator const& mcCollision, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); int gCollisionId = mcCollision.globalIndex(); int64_t timeStamp = 0; @@ -1102,7 +1102,7 @@ struct HfCorrelatorD0Hadrons { SelectedCandidatesDataMl const& candidates, SelectedTracks const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZvtx"), collision.posZ()); @@ -1191,7 +1191,7 @@ struct HfCorrelatorD0Hadrons { SelectedTracksMcRec const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(candidates, tracks); Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; bool isD0Prompt = false; @@ -1326,7 +1326,7 @@ struct HfCorrelatorD0Hadrons { void processMcGenMixedEvent(SelectedCollisionsMcGen const& collisions, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx index 19130525213..76c9767de64 100644 --- a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx @@ -415,7 +415,7 @@ struct HfCorrelatorDMesonPairs { AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; AxisSpec const axisDcaXY = {binsDcaXY, "DCA xy"}; - corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}, true}); + corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}}); if (applyMixedEvent) { registry.add("hMultiplicityPreSelection", "multiplicity prior to selection;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index 334e473114c..8c3ec22ea1f 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -255,7 +255,7 @@ struct HfCorrelatorDplusHadrons { ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsMassD{"binsMassD", {200, 1.7, 2.10}, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; ConfigurableAxis binsDcaXY{"binsDcaXY", {128, -0.2, 0.2}, "DCA xy"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -330,7 +330,7 @@ struct HfCorrelatorDplusHadrons { registry.add("hPhiMcGen", "D+,Hadron particles - MC Gen", {HistType::kTH1F, {axisPhi}}); registry.add("hMultFT0AMcGen", "D+,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1F, {axisMultiplicity}}); registry.add("hFakeCollision", "Fake collision counter", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake coll"}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -721,7 +721,7 @@ struct HfCorrelatorDplusHadrons { soa::Join const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; for (const auto& mcCollision : mcCollisions) { @@ -806,7 +806,7 @@ struct HfCorrelatorDplusHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -880,7 +880,7 @@ struct HfCorrelatorDplusHadrons { void processMcGenMixedEvent(SelCollisionsWithDplusMc const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx index eabedd61556..f0d4194148b 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx @@ -443,7 +443,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -529,7 +529,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -706,7 +706,7 @@ struct HfCorrelatorDsHadrons { soa::Join const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; for (const auto& mcCollision : mcCollisions) { @@ -918,7 +918,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZVtx"), collision.posZ()); @@ -994,7 +994,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDs(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1085,7 +1085,7 @@ struct HfCorrelatorDsHadrons { void processMcGenME(SelCollisionsMc const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx index 4365220fa88..9808b2c627a 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx @@ -110,7 +110,7 @@ struct HfCorrelatorDsHadronsReduced { soa::Join const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); @@ -157,7 +157,7 @@ struct HfCorrelatorDsHadronsReduced { aod::AssocTrackReds const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int const poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); diff --git a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx index ff27fa3f3e9..aedb3fadca9 100644 --- a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx @@ -179,7 +179,7 @@ struct HfCorrelatorDstarHadrons { ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; - BinningType binningScheme{{binsZVtx, binsMultiplicity}, true}; + BinningType binningScheme{{binsZVtx, binsMultiplicity}}; // Eta Phi Axes ConfigurableAxis axisEta{"axisEta", {16, -1.0, 1.0}, "Eta Axis"}; ConfigurableAxis axisPhi{"axisPhi", {64, 0.0, 3.14}, "Phi Axis"}; @@ -203,7 +203,7 @@ struct HfCorrelatorDstarHadrons { invMassD0Particle = -999.0; binNumber = -2; - binningScheme = {{binsZVtx, binsMultiplicity}, true}; + binningScheme = {{binsZVtx, binsMultiplicity}}; registry.add("QA/hMultFT0M", "Multiplicity distribution in FT0M", {HistType::kTH1D, {axisSpecMultFT0M}}); registry.add("QA/hCandsPerCol", "Candidates per Collision", {HistType::kTH1D, {{100, 0.0, 100.0}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index 816270c38a0..911ae2a2b0d 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -466,7 +466,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigCharms const&, aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto trigCand = pair.template hfcRedTrigCharm_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZMult); @@ -482,7 +482,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigTracks const&, aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto trigCand = pair.template hfcRedTrigTrack_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZMult); @@ -498,7 +498,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigCharms const&, aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto trigCand = pair.template hfcRedTrigCharm_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZCent); @@ -514,7 +514,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigTracks const&, aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto trigCand = pair.template hfcRedTrigTrack_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZCent); @@ -530,7 +530,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; if (fillSparses && fillTables) { @@ -547,7 +547,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; if (fillSparses && fillTables) { @@ -563,7 +563,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processMixedEventHadHadWCentMix(aod::HfcRedCorrColls const& collisions, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, tracksTuple, &cache}; if (fillSparses && fillTables) { @@ -579,7 +579,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processMixedEventHadHadWMultMix(aod::HfcRedCorrColls const& collisions, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, tracksTuple, &cache}; if (fillSparses && fillTables) { @@ -612,7 +612,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, aod::HfcRedAssBases const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; for (const auto& collision : collisions) { if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { @@ -633,7 +633,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, aod::HfcRedAssBases const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; doCorrelationsMixedEvent(collisions, candidates, tracks, binPolicyPosZCent); } diff --git a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx index b832a897cbf..5b5722562d3 100644 --- a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx @@ -122,7 +122,7 @@ struct HfCorrelatorHfeHadrons { ConfigurableAxis binsNSigma{"binsNSigma", {30, -15., 15.}, "#it{#sigma_{TPC}}"}; ConfigurableAxis binsMass{"binsMass", {100, 0.0, 2.0}, "Mass (GeV/#it{c}^{2}); entries"}; - BinningType corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType corrBinning{{zPoolBins, multPoolBins}}; HistogramRegistry registry{ "registry", {}}; @@ -479,7 +479,7 @@ struct HfCorrelatorHfeHadrons { void processMcGen(McGenTableCollision const& mcCollision, aod::McParticles const& mcParticles, aod::HfMcGenSelEl const& electrons) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); for (const auto& particleMc : mcParticles) { @@ -598,7 +598,7 @@ struct HfCorrelatorHfeHadrons { void processMcGenMixedEvent(McGenTableCollisions const& mcCollision, aod::HfMcGenSelEl const& electrons, aod::McParticles const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(electrons, mcParticles); Pair const pairMcGen{corrBinningMcGen, 5, -1, mcCollision, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx index 879981fec67..1781e20ac59 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx @@ -282,7 +282,7 @@ struct HfCorrelatorLcHadrons { ConfigurableAxis binsMassLc{"binsMassLc", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsCentFt0m{"binsCentFt0m", {100, 0., 100.}, "Centrality percentile (FT0M)"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -362,7 +362,7 @@ struct HfCorrelatorLcHadrons { registry.add("hEtaMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisEta}}); registry.add("hPhiMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisPhi}}); registry.add("hMultFT0AMcGen", "Lc,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1D, {axisMultiplicity}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } /// Lc-hadron correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) @@ -825,7 +825,7 @@ struct HfCorrelatorLcHadrons { int counterLcHadron = 0; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1011,7 +1011,7 @@ struct HfCorrelatorLcHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yLc(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1132,7 +1132,7 @@ struct HfCorrelatorLcHadrons { void processMcGenMixedEvent(SelCollisionsWithLcMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx index da60d9982c2..3b2b29b1f8b 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx @@ -417,7 +417,7 @@ struct HfCorrelatorLcScHadrons { ConfigurableAxis binsCandMass{"binsCandMass", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; int8_t chargeCand = 3; @@ -536,7 +536,7 @@ struct HfCorrelatorLcScHadrons { registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -1603,7 +1603,7 @@ struct HfCorrelatorLcScHadrons { static constexpr std::size_t PDGChargeScale{3u}; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1834,7 +1834,7 @@ struct HfCorrelatorLcScHadrons { void processMcGenMixedEvent(SelCollisionsMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, cfgCharmCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { diff --git a/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx index 249b4eea990..5c0d11af9d5 100644 --- a/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx @@ -459,7 +459,7 @@ struct HfCorrelatorXicHadrons { ConfigurableAxis binsCandMassXic0{"binsCandMassXic0", {200, 2.4, 2.8}, "inv. mass (Xi pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry"}; int8_t chargeCand = 3; @@ -584,7 +584,7 @@ struct HfCorrelatorXicHadrons { registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -1525,7 +1525,7 @@ struct HfCorrelatorXicHadrons { int8_t candSign = 0; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -1865,7 +1865,7 @@ struct HfCorrelatorXicHadrons { void processMcGenMixedEvent(SelCollisionsMc const& collisions, CandidatesXicPlusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, cfgXicCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx index bf417b861a7..74ab959c959 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -127,9 +127,9 @@ struct HfTaskCharmHadronsCharmFemtoDream { if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptD0Min < 0 || ptD0Min >= ptD0Max || ptDstarMin < 0 || ptDstarMin >= ptDstarMax || etaCandMax <= 0 || massD0Min >= massD0Max || deltaMassDstarMin >= deltaMassDstarMax || massD0DaughterMin >= massD0DaughterMax || charmHadCandSel < 1 || eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); } - colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; - colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; const AxisSpec massD0{300, massD0Min.value, massD0Max.value, "M(Kpi) (GeV/c2)"}; const AxisSpec deltaMass{310, deltaMassDstarMin.value, deltaMassDstarMax.value, "Delta M (GeV/c2)"}; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx index f2e942ca582..d4012fbdba8 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx @@ -312,9 +312,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { massTwo = o2::analysis::femtoDream::getMass(charmSel.charmHadPDGCode.value); // setup columnpolicy for binning - colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; - colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; eventHisto.init(®istry); allTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); selectedTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx index 44145c8f499..8dc31b37116 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx @@ -330,9 +330,9 @@ struct HfTaskCharmHadronsV0FemtoDream { bool processDstar = doprocessDataDstarV0; // setup columnpolicy for binning - colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; eventHisto.init(®istry); v0HistoPartOne.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); v0HistoPartOneSelected.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 1337864b67b..77aa48b5244 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -2640,7 +2640,7 @@ struct HfTaskFlow { // The first one that I call "Data" should work for data and mc rec using BinningTypeData = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; auto tracksTuple = std::make_tuple(tracksTrigger, tracksAssoc); Pair pair{binningWithTracksSize, configTask.nMixedEvents, -1, collisions, tracksTuple, &cache}; @@ -2700,7 +2700,7 @@ struct HfTaskFlow { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2764,7 +2764,7 @@ struct HfTaskFlow { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2835,7 +2835,7 @@ struct HfTaskFlow { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -2927,7 +2927,7 @@ struct HfTaskFlow { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { @@ -4567,7 +4567,7 @@ struct HfTaskFlow { }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning const binningOnVtxAndMult{{getTracksSize}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getTracksSize}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); diff --git a/PWGJE/Tasks/chargedJetHadron.cxx b/PWGJE/Tasks/chargedJetHadron.cxx index 6984a46a8e0..12440d3bad2 100644 --- a/PWGJE/Tasks/chargedJetHadron.cxx +++ b/PWGJE/Tasks/chargedJetHadron.cxx @@ -116,9 +116,9 @@ struct ChargedJetHadron { using BinningTypePP = ColumnBinningPolicy; using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType corrBinningPP{{binsZVtx, binsCentrality}, true}; - BinningType corrBinning{{binsZVtx, binsCentrality}, true}; - BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}, true}; + BinningType corrBinningPP{{binsZVtx, binsCentrality}}; + BinningType corrBinning{{binsZVtx, binsCentrality}}; + BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}}; HistogramRegistry registry; // histogram registry std::vector eventSelectionBits; diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 3407543e60b..57b89a93a34 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -204,9 +204,9 @@ struct JetSpectraEseTask { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFT0M = ColumnBinningPolicy; using BinningTypeFT0CVariant1 = ColumnBinningPolicy; - BinningTypeFT0C corrBinningFT0C{{binsZVtx, binsCentrality}, true}; - BinningTypeFT0M corrBinningFT0M{{binsZVtx, binsCentrality}, true}; - BinningTypeFT0CVariant1 corrBinningFT0CVariant1{{binsZVtx, binsCentrality}, true}; + BinningTypeFT0C corrBinningFT0C{{binsZVtx, binsCentrality}}; + BinningTypeFT0M corrBinningFT0M{{binsZVtx, binsCentrality}}; + BinningTypeFT0CVariant1 corrBinningFT0CVariant1{{binsZVtx, binsCentrality}}; Service pdg{}; enum class DetID { FT0C, diff --git a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx index 9ab84d0638e..943bc742517 100644 --- a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx +++ b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx @@ -222,7 +222,7 @@ struct PhotonChargedTriggerCorrelation { resultVec.insert(resultVec.end(), vec.begin(), vec.end()); return resultVec; }; - BinningZPvMult binningZPvMult{{prependValueToVector(binsZPvBinning.value, VARIABLE_WIDTH), prependValueToVector(binsMultBinning.value, VARIABLE_WIDTH)}, true}; + BinningZPvMult binningZPvMult{{prependValueToVector(binsZPvBinning.value, VARIABLE_WIDTH), prependValueToVector(binsMultBinning.value, VARIABLE_WIDTH)}}; // declare analysis variables diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index 7466a973bcb..3e58c303ed4 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -1356,10 +1356,10 @@ struct decay3bodyBuilder { } if (decay3bodyBuilderOpts.useKFParticle) { - Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiKF); } else { - Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiDCA); } } diff --git a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx index d30182f35dc..c90bc73c6cb 100644 --- a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx @@ -386,7 +386,7 @@ struct he3HadronFemto { ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for multiplicity"}; ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for centrality"}; using BinningType = ColumnBinningPolicy; - BinningType binningPolicy{{axisVertex, axisCentrality}, true}; + BinningType binningPolicy{{axisVertex, axisCentrality}}; SliceCache cache; SameKindPair mPair{binningPolicy, settingNoMixedEvents, -1, &cache}; diff --git a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx index 784304adcd8..77de9f213f7 100644 --- a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx @@ -1164,7 +1164,7 @@ void PiHypertritonFemto::processPairs(const Tcollisions& collisions, const Ttrac { // Each invocation receives a new input-table scope; row indices can repeat in later frames. const uint64_t sourceFrameId = ++mSourceFrameId; - const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}}; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; diff --git a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx index 6f2a8a2336c..782f8c68bfc 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx @@ -663,7 +663,7 @@ struct SigmaHadCorr { { if (useMultNTracksPV.value) { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -685,7 +685,7 @@ struct SigmaHadCorr { } } else { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -835,7 +835,7 @@ struct SigmaHadCorr { { if (useMultNTracksPV.value) { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) @@ -883,7 +883,7 @@ struct SigmaHadCorr { } } else { for (auto const& [collision1, collision2] : - selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}, true}, nEvtMixingBkg, -1, collisions, collisions)) { + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) continue; if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index f4b1ef4083c..9f906a15541 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -2108,7 +2108,7 @@ struct HadronNucleiCorrelation { }; using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}, true}; + BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, maxmixcollsGen.value, -1, mcCollisions, mcCollisions)) { const auto& cache1 = genCaches.at(collision1.globalIndex()); diff --git a/PWGLF/Tasks/QC/resonanceqa.cxx b/PWGLF/Tasks/QC/resonanceqa.cxx index c30eba9fb9b..aae477086e9 100644 --- a/PWGLF/Tasks/QC/resonanceqa.cxx +++ b/PWGLF/Tasks/QC/resonanceqa.cxx @@ -264,7 +264,7 @@ struct resonanceqa { ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for bin"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0.0, 2.750, 5.250, 7.750, 12.750, 17.750, 22.750, 27.750, 32.750, 37.750, 42.750, 47.750, 52.750, 57.750, 62.750, 67.750, 72.750, 77.750, 82.750, 87.750, 92.750, 97.750, 250.1}, "multiplicity axis for histograms"}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { diff --git a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx index 2020bacdaa4..364b677d2a0 100644 --- a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx +++ b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx @@ -1328,7 +1328,7 @@ struct Chargedkstaranalysis { PROCESS_SWITCH(Chargedkstaranalysis, processDataSE, "Process Event for data without Partitioning", true); using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}, true}; + BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}}; Pair pair{binningOnPositions, nEvtMixing, -1, &cache}; void processDataME(EventCandidates const& /*collisions*/, TrackCandidates const& /*tracks*/, V0Candidates const& /*V0s*/) diff --git a/PWGLF/Tasks/Resonances/cksspinalignder.cxx b/PWGLF/Tasks/Resonances/cksspinalignder.cxx index 0570217b7e0..55456c551a1 100644 --- a/PWGLF/Tasks/Resonances/cksspinalignder.cxx +++ b/PWGLF/Tasks/Resonances/cksspinalignder.cxx @@ -384,7 +384,7 @@ struct cksspinalignder { aod::KShortTracks const& k0sTracks, aod::PionTracks const& pionTracks) { - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx index 23e2378ac41..edcaa3b35ff 100644 --- a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -1429,11 +1429,11 @@ struct DeltaAnalysis { using BinningTypeFV0A = ColumnBinningPolicy; using BinningTypeNTPV = ColumnBinningPolicy; - BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeFV0A binningFV0A{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; - BinningTypeNTPV binningNTPV{{axes.cfgVtxAxis, axes.cfgCentAxis}, true}; + BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFV0A binningFV0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeNTPV binningNTPV{{axes.cfgVtxAxis, axes.cfgCentAxis}}; SameKindPair pairFT0M{binningFT0M, mixingCfg.cfgNoMixedEvents, -1, &cache}; SameKindPair pairFT0A{binningFT0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index b027bfdd2d3..585d6a92173 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -2329,7 +2329,7 @@ struct doublephimeson { { auto tracksTuple = std::make_tuple(phitracks); - BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; SameKindPair pair{ binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; @@ -2587,7 +2587,7 @@ struct doublephimeson { void processMixedEvent(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) { auto tracksTuple = std::make_tuple(phitracks); - BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; SameKindPair pair{ binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx index 832b4e47a1a..82b711ede8e 100644 --- a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx +++ b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx @@ -706,7 +706,7 @@ struct F0980pbpbanalysis { qVecDetInd = detId * 4 + 3 + (nmode - 2) * FlowConfig.cfgQvecNum * 4; auto trackTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}, true}; + BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}}; SameKindPair pair{binningOnPositions, BkgMethodConfig.cfgBkgMixedNum, -1, collisions, trackTuple, &cache}; ROOT::Math::PxPyPzMVector ptl1, ptl2, recoPtl; for (const auto& [c1, t1, c2, t2] : pair) { diff --git a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx index 193cc5e105b..ead7c9b294f 100644 --- a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx +++ b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx @@ -668,7 +668,7 @@ struct f1protoncorrelation { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}}; Preslice tracksPerCollisionPresliceF1 = aod::f1protondaughter::redF1PEventId; Preslice tracksPerCollisionPresliceP = aod::f1protondaughter::redF1PEventId; void processME(aod::RedF1PEvents& collisions, diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx index ae5b8a0f061..5d8989567fe 100644 --- a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -1712,7 +1712,7 @@ struct HResonanceCorrelation { return; for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); for (const auto& trigger : collision.trigParticles) { @@ -2066,7 +2066,7 @@ struct HResonanceCorrelation { return; for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); for (const auto& trigger : collision.trigParticles) { @@ -2174,7 +2174,7 @@ struct HResonanceCorrelation { } // LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found @@ -2266,7 +2266,7 @@ struct HResonanceCorrelation { } // LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { @@ -2337,7 +2337,7 @@ struct HResonanceCorrelation { } // LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { @@ -2407,7 +2407,7 @@ struct HResonanceCorrelation { } // LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // Skip if desired trigger not found if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { @@ -2484,7 +2484,7 @@ struct HResonanceCorrelation { } // LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // Skip if desired trigger not found if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { @@ -2555,7 +2555,7 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexHadron)) { return; } - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2604,7 +2604,7 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexPion)) { return; } - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2653,7 +2653,7 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexKaon)) { return; } - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2702,7 +2702,7 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexPhi)) { return; } - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2764,7 +2764,7 @@ struct HResonanceCorrelation { if (!TESTBIT(doCorrelation, IndexKstar)) { return; } - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { diff --git a/PWGLF/Tasks/Resonances/higherMassResonances.cxx b/PWGLF/Tasks/Resonances/higherMassResonances.cxx index 342a8dfa83d..30c22f10d14 100644 --- a/PWGLF/Tasks/Resonances/higherMassResonances.cxx +++ b/PWGLF/Tasks/Resonances/higherMassResonances.cxx @@ -1335,13 +1335,13 @@ struct HigherMassResonances { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; // for derived data only + BinningType colBinning{{axisVertex, axisMultiplicity}}; // for derived data only Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) diff --git a/PWGLF/Tasks/Resonances/highmasslambda.cxx b/PWGLF/Tasks/Resonances/highmasslambda.cxx index cf2df918881..dae9c6c0d82 100644 --- a/PWGLF/Tasks/Resonances/highmasslambda.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambda.cxx @@ -714,7 +714,7 @@ struct highmasslambda { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder)) { diff --git a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx index c3ece46c3c8..33d5b697660 100644 --- a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx @@ -808,7 +808,7 @@ struct highmasslambdasvx { /* void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision2.sel8()) { diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index 9d6877ca7e5..a84ed359767 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -557,7 +557,7 @@ struct K1AnalysisMicro { void processME(ResoCollisions const& collisions, ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -573,7 +573,7 @@ struct K1AnalysisMicro { void processMEMicro(ResoCollisions const& collisions, ResoMicroTracks const& resomicrotracks) { auto tracksTuple = std::make_tuple(resomicrotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx index 5508bb7cf9a..5af74594dc6 100644 --- a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx @@ -511,8 +511,8 @@ struct k892Analysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/k892analysis.cxx b/PWGLF/Tasks/Resonances/k892analysis.cxx index ee26e771a1e..f80fbb3b5d7 100644 --- a/PWGLF/Tasks/Resonances/k892analysis.cxx +++ b/PWGLF/Tasks/Resonances/k892analysis.cxx @@ -862,7 +862,7 @@ struct K892analysis { void processMELight(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx index 05d1d4e0c30..a49fd403148 100644 --- a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx +++ b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx @@ -982,7 +982,7 @@ struct K892analysispbpb { void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1009,7 +1009,7 @@ struct K892analysispbpb { void processMixedEventRun2(Run2Events const& collisions, TrackCandidates const& tracks, BCsWithRun2Info const& bcs) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1034,7 +1034,7 @@ struct K892analysispbpb { void processMixedEventMC(EventCandidatesMCrec const& recCollisions, TrackCandidatesMCrec const& RecTracks, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1059,7 +1059,7 @@ struct K892analysispbpb { void processMixedEventMCRun2(EventCandidatesMCrecRun2 const& recCollisions, TrackCandidatesMCrec const& RecTracks, BCsWithRun2Info const& bcs, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx index 2dbfcfce7be..206f8861590 100644 --- a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx +++ b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx @@ -1208,7 +1208,7 @@ struct k892hadronphotonBkg { // Build the mixing binning locally: a struct member initialized from a // ConfigurableAxis captures the default bins at task construction time - BkgBinningType bkgColBinning{{axisVertexMixBkg, axisCentralityMixBkg}, true}; + BkgBinningType bkgColBinning{{axisVertexMixBkg, axisCentralityMixBkg}}; for (const auto& [coll1, coll2] : selfCombinations(bkgColBinning, kstarBkgConfig.nMix, -1, collisions, collisions)) { diff --git a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx index f30d7c1ae28..abe0ae5f7f3 100644 --- a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx @@ -492,7 +492,7 @@ struct k892pmanalysis { { auto tracksV0sTuple = std::make_tuple(resotracks, resov0s); // auto V0sTuple = std::make_tuple(resov0s); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; Pair pairs{colBinning, nEvtMixing, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, resotracks1, collision2, resov0s2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx index 19abecac1e1..c746e7c082f 100644 --- a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx +++ b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx @@ -504,8 +504,8 @@ struct kaonkaonAnalysisRun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}, true}; // for pp - BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}, true}; // for PbPb + BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}}; // for pp + BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}}; // for PbPb SameKindPair pair1{binningOnPositions1, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; SameKindPair pair2{binningOnPositions2, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/kshortlambda.cxx b/PWGLF/Tasks/Resonances/kshortlambda.cxx index 0ddf5ade8bf..64b0278d835 100644 --- a/PWGLF/Tasks/Resonances/kshortlambda.cxx +++ b/PWGLF/Tasks/Resonances/kshortlambda.cxx @@ -714,8 +714,8 @@ struct Kshortlambda { void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) { auto tracksTuple = std::make_tuple(v0s); - BinningTypeVertexContributor binningOnPositions1{{mevz, memult}, true}; - BinningTypeCentralityM binningOnPositions2{{mevz, memult}, true}; + BinningTypeVertexContributor binningOnPositions1{{mevz, memult}}; + BinningTypeCentralityM binningOnPositions2{{mevz, memult}}; SameKindPair pair1{binningOnPositions1, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for PbPb SameKindPair pair2{binningOnPositions2, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for pp diff --git a/PWGLF/Tasks/Resonances/kstar0analysis.cxx b/PWGLF/Tasks/Resonances/kstar0analysis.cxx index 4755b2e0a0b..ad544f85608 100644 --- a/PWGLF/Tasks/Resonances/kstar0analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar0analysis.cxx @@ -1133,7 +1133,7 @@ struct Kstar0analysis { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx index cdfb55fc3ba..24ad4ba2a94 100644 --- a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx +++ b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx @@ -1539,10 +1539,10 @@ struct Kstar892LightIon { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}}; SameKindPair pair1{binningOnFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pair2{binningOnFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; @@ -1612,10 +1612,10 @@ struct Kstar892LightIon { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}}; SameKindPair pairmc1{binningOnMCFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pairmc2{binningOnMCFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; diff --git a/PWGLF/Tasks/Resonances/kstar892analysis.cxx b/PWGLF/Tasks/Resonances/kstar892analysis.cxx index 3e7626203df..88fd873b977 100644 --- a/PWGLF/Tasks/Resonances/kstar892analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar892analysis.cxx @@ -886,7 +886,7 @@ struct kstar892analysis { void processMELight(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -904,7 +904,7 @@ struct kstar892analysis { void processMELightWithTof(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -922,7 +922,7 @@ struct kstar892analysis { void processMELightTPCLowPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -940,7 +940,7 @@ struct kstar892analysis { void processMELightTOFHighPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kstarInOO.cxx b/PWGLF/Tasks/Resonances/kstarInOO.cxx index d16f1be1818..7126483b17c 100644 --- a/PWGLF/Tasks/Resonances/kstarInOO.cxx +++ b/PWGLF/Tasks/Resonances/kstarInOO.cxx @@ -1464,7 +1464,7 @@ struct kstarInOO { void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) + BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}}; SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (cDebugLevel > 0) { @@ -1556,7 +1556,7 @@ struct kstarInOO { void processMixedEventMC(EventCandidates const& collisions, TrackCandidatesMC const& tracks, aod::McParticles const&) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) + BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}}; SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (cDebugLevel > 0) { diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 0072c90e197..ab2c7fb4d4d 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -1166,7 +1166,7 @@ struct Kstarpbpb { double v2 = 0.; auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionEventPairME(collision1, collision2)) { @@ -1778,7 +1778,7 @@ struct Kstarpbpb { void processMEPhi(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { @@ -2445,7 +2445,7 @@ struct Kstarpbpb { ROOT::Math::PxPyPzMVector kaon, pion, kstar; const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); auto tracksTuple = std::make_tuple(tracks); - BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { @@ -2640,7 +2640,7 @@ struct Kstarpbpb { ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); auto tracksTuple = std::make_tuple(tracks); - BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { @@ -2877,7 +2877,7 @@ struct Kstarpbpb { ROOT::Math::PxPyPzMVector kaon, pion, kstar; const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); auto tracksTuple = std::make_tuple(tracks); - BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { @@ -3088,7 +3088,7 @@ struct Kstarpbpb { ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); auto tracksTuple = std::make_tuple(tracks); - BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 042672d9c41..89bda99b335 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -1337,15 +1337,15 @@ struct Kstarqa { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; - - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; + + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}}; SameKindPair pair1{binningOnFT0M, configGp.cfgNoMixedEvents, -1, &cache}; SameKindPair pair2{binningOnFT0A, configGp.cfgNoMixedEvents, -1, &cache}; diff --git a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx index 06385d1a370..87b5655c48c 100644 --- a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx @@ -1182,10 +1182,10 @@ struct lambda1405analysis { const aod::BCs&) { if (useCentrality) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } else { - BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } } @@ -1196,7 +1196,7 @@ struct lambda1405analysis { TracksFull const& tracks, const aod::BCs&) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; fillOutputData(collision, kinkCands, tracks, binPolicy); } PROCESS_SWITCH(lambda1405analysis, processDataWQVecsSameEvent, "Data processing with centrality and Q vectors info", false); @@ -1208,7 +1208,7 @@ struct lambda1405analysis { { auto pairsTuple = std::make_tuple(kinkCands, tracks); if (useCentrality) { - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { @@ -1223,7 +1223,7 @@ struct lambda1405analysis { fillOutputData(sigmasColl, sigmas, bachPis, binPolicy); } } else { - BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { @@ -1247,7 +1247,7 @@ struct lambda1405analysis { const aod::BCs&) { auto pairsTuple = std::make_tuple(kinkCands, tracks); - BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}, true}; + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { if (sigmas.size() == 0 || bachPis.size() == 0) { diff --git a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx index d5543744f89..d84c295aa29 100644 --- a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx @@ -653,8 +653,8 @@ struct lambdaAnalysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx index b45c29e6070..b59bbf3a1d2 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx @@ -1107,7 +1107,7 @@ struct Lambda1520analysis { void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx index 1aa84ce8085..413690fc58c 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx @@ -1154,7 +1154,7 @@ struct Lstaranalysis { // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; void processME(EventCandidates const& collision, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx index a9ffd4f6082..20f2d5dd2ca 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx @@ -1248,7 +1248,7 @@ struct Lambda1520analysisinpp { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx index 435a104dbdb..8cbe7eb227c 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx @@ -1067,7 +1067,7 @@ struct lambda1520analysispo { { LOGF(debug, "Event mixing started"); MixingBinningVtxZAndCentrality mixingBins{ - {vertexZMixingBins, centralityMixingBins}, true}; + {vertexZMixingBins, centralityMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair @@ -1131,7 +1131,7 @@ struct lambda1520analysispo { LOG(fatal) << "Disable processMix() first when using processMixDF()!"; LOGF(debug, "Event mixing (DF format) started"); - MixingBinningDF mixingBins{{vertexZMixingBins, centralityMixingBins}, true}; + MixingBinningDF mixingBins{{vertexZMixingBins, centralityMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair eventPairs{mixingBins, numberOfEventsToMix, -1, @@ -1171,7 +1171,7 @@ struct lambda1520analysispo { LOGF(debug, "Event-plane-dependent event mixing (DF format) started"); MixingBinningWithEventPlane mixingBins{ - {vertexZMixingBins, centralityMixingBins, eventPlaneMixingBins}, true}; + {vertexZMixingBins, centralityMixingBins, eventPlaneMixingBins}}; auto trackPool = std::make_tuple(tracks); SameKindPair diff --git a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx index abcfe683f88..8e06ab022dd 100644 --- a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx @@ -1253,7 +1253,7 @@ struct Lambda1520pbpb { void processMix(ResoCols const& collisions, ResoTracks const& tracks) { LOGF(debug, "Event Mixing Started"); - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; @@ -1295,7 +1295,7 @@ struct Lambda1520pbpb { LOG(fatal) << "Disable processMix() first!"; LOGF(debug, "Event Mixing Started"); - BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; @@ -1320,7 +1320,7 @@ struct Lambda1520pbpb { if (doprocessMix || doprocessMixDF) LOG(fatal) << "Disable processMix() or processMixDF() first!"; LOGF(debug, "Event Mixing Started"); - BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}, true}; + BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index bd1a01cfa8b..cfbbab3d7d6 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -163,7 +163,7 @@ struct Omega2012Analysis { using ResoMCCollisions = soa::Join; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; void init(InitContext&) { diff --git a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx index d1aae772d1b..29f2a75f55f 100644 --- a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx @@ -453,8 +453,8 @@ struct Phi1020SpherocityAnalysis { void processMix(ResoCols const& collisions, ResoTracks const& tracks) { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}, true}; - BinningType2 binningPositions2{{mixVtxBins, mixMultBins}, true}; + BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}}; + BinningType2 binningPositions2{{mixVtxBins, mixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (mixSph) { SameKindPair pairs{binningPositions1, numMixEv, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/phi1020analysis.cxx b/PWGLF/Tasks/Resonances/phi1020analysis.cxx index 9aee9ebd3e1..672e84c15b7 100644 --- a/PWGLF/Tasks/Resonances/phi1020analysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020analysis.cxx @@ -1028,7 +1028,7 @@ struct Phi1020analysis { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phianalysis.cxx b/PWGLF/Tasks/Resonances/phianalysis.cxx index 43a2c49027a..1363e182fa6 100644 --- a/PWGLF/Tasks/Resonances/phianalysis.cxx +++ b/PWGLF/Tasks/Resonances/phianalysis.cxx @@ -328,7 +328,7 @@ struct phianalysis { void processMELight(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx index 40cbd0b51cf..04bfa017916 100644 --- a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx @@ -1421,10 +1421,10 @@ struct PhianalysisTHnSparse { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}}; SameKindPair pairVzCe{binningVzCe, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; - BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}, true}; + BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}}; SameKindPair pairVzMu{binningVzMu, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; if (mixingType == rsn::MixingType::ce) { diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx index 2d0218171ae..084f4a5aae4 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx @@ -241,14 +241,14 @@ struct phianalysisrun3 { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {2000, 0, 10000}, "TPC multiplicity for bin"}; // using BinningType = BinningPolicy>; - // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}}; // using BinningTypeTPCMultiplicity = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; // using BinningTypeCentrality = ColumnBinningPolicy; // using BinningType = ColumnBinningPolicy; - // BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { @@ -306,7 +306,7 @@ struct phianalysisrun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { if (applyEvsel && !c1.sel8()) { diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx index 26184b5f79f..c73ae59c466 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx @@ -722,7 +722,7 @@ struct Phianalysisrun3pbpb { const int kCentFT0M = 2; const int kCentFV0A = 3; if (centestimator == kCentFT0C) { - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -734,7 +734,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0C()); } } else if (centestimator == kCentFT0A) { - BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -746,7 +746,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0A()); } } else if (centestimator == kCentFT0M) { - BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -758,7 +758,7 @@ struct Phianalysisrun3pbpb { fillMixedPairs(tracks1, tracks2, c1.centFT0M()); } } else if (centestimator == kCentFV0A) { - BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { @@ -1236,7 +1236,7 @@ struct Phianalysisrun3pbpb { { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pairs{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phiflowder.cxx b/PWGLF/Tasks/Resonances/phiflowder.cxx index a76e1dc7fdc..8a855a5c1c2 100644 --- a/PWGLF/Tasks/Resonances/phiflowder.cxx +++ b/PWGLF/Tasks/Resonances/phiflowder.cxx @@ -430,12 +430,12 @@ struct phiflowder { // Processing Event Mixing using BinningType = ColumnBinningPolicy; - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; void processMixedData(EventCandidates const& collisions, aod::KaonTracks const& /*kaontracks*/) { - BinningType colBinning{{cfgVtxBins, cfgCentBins}, true}; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { if (collision1.globalIndex() == collision2.globalIndex()) { @@ -524,8 +524,7 @@ struct phiflowder { {getSPAngle}, {cfgVtxBins, cfgCentBins, - cfgSPAngleBins}, - true}; + cfgSPAngleBins}}; for (const auto& [collision1, collision2] : selfCombinations(binningOnSPAngle, diff --git a/PWGLF/Tasks/Resonances/phipbpb.cxx b/PWGLF/Tasks/Resonances/phipbpb.cxx index ca124250f26..e02230387f5 100644 --- a/PWGLF/Tasks/Resonances/phipbpb.cxx +++ b/PWGLF/Tasks/Resonances/phipbpb.cxx @@ -941,8 +941,8 @@ struct phipbpb { } PROCESS_SWITCH(phipbpb, processSameEventv1, "Process Same event for v1", false); - BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; - BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}}; Preslice tracksPerCollision = aod::track::collisionId; void processMEAcc(EventCandidates const& collisions, TrackCandidates const& tracks) { @@ -1188,7 +1188,7 @@ struct phipbpb { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { // if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { @@ -1310,7 +1310,7 @@ struct phipbpb { void processMixedEventOptiv1(EventCandidatesv1 const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.triggereventsp() || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup)) { diff --git a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx index d26a5b66045..27d024611de 100644 --- a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx +++ b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx @@ -494,7 +494,7 @@ struct phispectrapbpbqa { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { diff --git a/PWGLF/Tasks/Resonances/phitutorial.cxx b/PWGLF/Tasks/Resonances/phitutorial.cxx index d60c63819e6..84c591b9edb 100644 --- a/PWGLF/Tasks/Resonances/phitutorial.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial.cxx @@ -187,7 +187,7 @@ struct phitutorial { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx index c2f072e37d0..739c339f90b 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx @@ -188,7 +188,7 @@ struct phitutorial_step3 { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) // notice the collisions subscrition, it is not an iterator here! diff --git a/PWGLF/Tasks/Resonances/rhoanalysis.cxx b/PWGLF/Tasks/Resonances/rhoanalysis.cxx index 77f4bd5c1e0..47367bd4ee4 100644 --- a/PWGLF/Tasks/Resonances/rhoanalysis.cxx +++ b/PWGLF/Tasks/Resonances/rhoanalysis.cxx @@ -267,7 +267,7 @@ struct rhoanalysis { void processMixedEvent(Event const& events, TrackPi const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, events, tracksTuple, &cache}; float massPiplus = Ipdg->Mass(kPiPlus); float massPiminus = Ipdg->Mass(kPiMinus); diff --git a/PWGLF/Tasks/Resonances/sigma.cxx b/PWGLF/Tasks/Resonances/sigma.cxx index 487a967e93d..61639e5f506 100644 --- a/PWGLF/Tasks/Resonances/sigma.cxx +++ b/PWGLF/Tasks/Resonances/sigma.cxx @@ -257,7 +257,7 @@ struct sigma { using BinningTypeCentralityM = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; Pair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::V0Datas const& V0s, aod::BCs const&) diff --git a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx index 3ed15c1328d..5d7f09bee53 100644 --- a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx +++ b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx @@ -1445,7 +1445,7 @@ struct Xi1530Analysisqa { } auto tracksTuple = std::make_tuple(resomicrotracks, cascTracks); - BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; Pair pairs{colBinning, mixingConfig.nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1493,7 +1493,7 @@ struct Xi1530Analysisqa { mixingPools.clear(); mixingBField = collision.bMagField(); } - BinningTypeVtxZT0M binning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; + BinningTypeVtxZT0M binning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; diff --git a/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx index b48a6b13948..347058612ad 100644 --- a/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx +++ b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx @@ -177,7 +177,7 @@ struct xi1530kaoncorrelation { using MixingBinning = ColumnBinningPolicy; - MixingBinning mixingBinning{{cfgMixVtxBins, cfgMixFT0MBins, cfgMixBzBins}, true}; + MixingBinning mixingBinning{{cfgMixVtxBins, cfgMixFT0MBins, cfgMixBzBins}}; Preslice xiStarsPerEvent = aod::redxistarcandidate::redXiStarKEventId; Preslice kaonsPerEvent = aod::redxistarkaon::redXiStarKEventId; diff --git a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx index e4bb98d8663..e922020b6d8 100644 --- a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx +++ b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx @@ -1554,7 +1554,7 @@ struct Xi1820Analysis { return; } auto tracksTuple = std::make_tuple(resoTracks, resoV0s); - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) if (!acceptsMixedCollisions(collision1, collision2)) { @@ -1573,7 +1573,7 @@ struct Xi1820Analysis { return; } auto tracksTuple = std::make_tuple(resoMicroTracks, resoV0s); - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) if (!acceptsMixedCollisions(collision1, collision2)) { @@ -1613,7 +1613,7 @@ struct Xi1820Analysis { mixingPools.clear(); mixingBField = collision.bMagField(); } - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; @@ -1654,7 +1654,7 @@ struct Xi1820Analysis { return; } auto v0sV0sTuple = std::make_tuple(resoV0s, resoV0s); - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sV0sTuple, &cache}; for (auto& [collision1, k0s1, collision2, lambda2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) @@ -1678,7 +1678,7 @@ struct Xi1820Analysis { neutralMixingPools.clear(); neutralMixingBField = collision.bMagField(); } - BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); if (bin < 0) { return; diff --git a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx index 48f11561b4f..d8fbf39ca88 100644 --- a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx +++ b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx @@ -932,9 +932,9 @@ struct CascadeCorrelations { ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; // ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100, 1000}, "Mixing bins - multiplicity"}; // using BinningType = ColumnBinningPolicy; - // BinningType colBinning{{axisVtxZ, axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + // BinningType colBinning{{axisVtxZ, axisMult}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVtxZ}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningType colBinning{{axisVtxZ}}; void processSameEvent(MyCollisionsMult::iterator const& collision, MyCascades const& Cascades, FullTracksExtIU const&, aod::BCsWithTimestamps const&) { diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 9d8a80ffdbb..23286d1e870 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -1784,7 +1784,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // When 'collisionHasTriggOrAssoc' = 0: // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] // When 'collisionHasTriggOrAssoc' = 3 @@ -2122,7 +2122,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { @@ -3516,7 +3516,7 @@ struct HStrangeCorrelation { aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found @@ -3616,8 +3616,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; if (!doprocessSameEventHCascades && doMixingQAandEventQA) { std::visit([&](auto const& binning) { @@ -3793,8 +3793,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; double cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // ________________________________________________ @@ -3951,7 +3951,7 @@ struct HStrangeCorrelation { soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { @@ -4029,7 +4029,7 @@ struct HStrangeCorrelation { aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -4081,8 +4081,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -4140,8 +4140,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -4195,7 +4195,7 @@ struct HStrangeCorrelation { soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 72a6465ced8..3eb74ce6662 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1487,8 +1487,7 @@ struct lambdajetpolarizationionsderived { using LeadPBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; LeadPBinningType leadPBinning{{getMixLeadPPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; // Ignore overflows true + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; // Ignore overflows true // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: //(Already filtered by Zvtx even though we call by aod::RingCollisions, so no need to access the filtered table) @@ -1532,8 +1531,7 @@ struct lambdajetpolarizationionsderived { using LeadJetBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadJetPt), decltype(getMixCentrality)>; LeadJetBinningType leadJetBinning{{getMixLeadJetPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; // RingJets is still the associated table (SameKindPair requires one), but its sliced content goes unused here: // the borrowed direction comes from jetProxyByCollision, which already knows which jet is the leading one. @@ -1568,8 +1566,7 @@ struct lambdajetpolarizationionsderived { using SubJetBinningType = FlexibleBinningPolicy, o2::aod::lambdajetpol::Zvtx, decltype(getMixSubJetPt), decltype(getMixCentrality)>; SubJetBinningType subJetBinning{{getMixSubJetPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; SameKindPair subJetPair{ subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; diff --git a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx index 1859c3f0782..2eeee1ccae9 100644 --- a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx @@ -601,7 +601,7 @@ struct LfLambdaTwoPartPolarization { Preslice tracksPerCollisionV0 = aod::v0data::collisionId; using BinningTypeT0C = ColumnBinningPolicy; - BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}, true}; + BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}}; void processDataMixedT0C(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) @@ -633,7 +633,7 @@ struct LfLambdaTwoPartPolarization { PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataMixedT0C, "Process event for mixed data in PbPb", false); using BinningTypeT0M = ColumnBinningPolicy; - BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}, true}; + BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}}; void processDataMixedT0M(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) diff --git a/PWGLF/Tasks/Strangeness/lambdalambda.cxx b/PWGLF/Tasks/Strangeness/lambdalambda.cxx index d3659397339..91ddb9c66ae 100644 --- a/PWGLF/Tasks/Strangeness/lambdalambda.cxx +++ b/PWGLF/Tasks/Strangeness/lambdalambda.cxx @@ -585,7 +585,7 @@ struct lambdalambda { SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{vertexAxis, centAxis}, true}; + BinningType colBinning{{vertexAxis, centAxis}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processDataMixed(EventCandidates const& collisions, diff --git a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx index d939c77b84c..45d7fd49b01 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx @@ -2445,7 +2445,7 @@ struct lambdapolsp { // Processing Event Mixing /* using BinningType = ColumnBinningPolicy; - BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}, true}; + BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}}; Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only void processDerivedDataMixed(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index b1546805464..04e4687225c 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -1951,7 +1951,7 @@ struct lambdaspincorrderived { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}}; Preslice tracksPerCollisionV0 = aod::lambdapair::lambdaeventId; void processMEV3(EventCandidates const& collisions, AllTrackCandidates const& V0s) diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index 48162411c03..4f95c222ce5 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -361,7 +361,7 @@ struct PhiStrangeCorrelation { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 15.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Multiplicity percentage binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; static constexpr std::array PhiMassRegionLabels{"Signal", "Sideband"}; static constexpr std::array AssocParticleLabels{"K0S", "Lambda", "AntiLambda", "Xi", "Omega", "Pi"}; diff --git a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx index 51975c776f2..40b9cc698cb 100644 --- a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx @@ -290,7 +290,7 @@ struct Phik0shortanalysis { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {20, 0, 100}, "Multiplicity percentil binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; // Cache for manual slicing SliceCache cache; diff --git a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx index 9fcac3ab845..e0c175fae1e 100644 --- a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx +++ b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx @@ -575,7 +575,7 @@ struct LfTaskLambdaSpinCorr { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicityClass}, true}; + BinningType colBinning{{axisVertex, axisMultiplicityClass}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processME(EventCandidates const& collisions, AllTrackCandidates const&, ResoV0s const& V0s) { diff --git a/PWGUD/Tasks/flowCorrelationsUpc.cxx b/PWGUD/Tasks/flowCorrelationsUpc.cxx index a1dae010e98..623265a92d4 100644 --- a/PWGUD/Tasks/flowCorrelationsUpc.cxx +++ b/PWGUD/Tasks/flowCorrelationsUpc.cxx @@ -605,7 +605,7 @@ struct FlowCorrelationsUpc { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; diff --git a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx index 7fbca9da958..465285bd039 100644 --- a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx +++ b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx @@ -584,7 +584,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -652,7 +652,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -719,7 +719,7 @@ struct FlowLongRangeCorrUpc { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx index 29a6fa3a46e..e6059892c94 100644 --- a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx +++ b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx @@ -1191,7 +1191,7 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { @@ -2334,7 +2334,7 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { diff --git a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx index bc6408d3b39..16a473cc0b0 100644 --- a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx +++ b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx @@ -409,9 +409,9 @@ struct UpcPhotonuclearAnalysisJMG { // Binning only on PosZ without multiplicity // using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges}}; // SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, &cache}; template @@ -896,8 +896,8 @@ struct UpcPhotonuclearAnalysisJMG { // int sgSide = reconstructedCollision.gapSide(); // int sgSide = 0; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(reconstructedTracks); SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, reconstructedCollision, tracksTuple, &cache}; diff --git a/Tutorials/PWGLF/Resonance/resonances_step2.cxx b/Tutorials/PWGLF/Resonance/resonances_step2.cxx index 3d83bc39bb6..a0c43dd03ba 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step2.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step2.cxx @@ -202,7 +202,7 @@ struct resonances_tutorial { void processME(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { // loop over all pairs diff --git a/Tutorials/src/eventMixing.cxx b/Tutorials/src/eventMixing.cxx index 925cc9f03a7..379532105ab 100644 --- a/Tutorials/src/eventMixing.cxx +++ b/Tutorials/src/eventMixing.cxx @@ -51,7 +51,7 @@ struct MixedEvents { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) { @@ -86,7 +86,7 @@ struct MixedEventsInsideProcess { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -124,7 +124,7 @@ struct MixedEventsFilteredTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -157,7 +157,7 @@ struct MixedEventsJoinedCollisions { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks, aod::BCsWithTimestamps const&) @@ -190,7 +190,7 @@ struct MixedEventsDynamicColumns { std::vector zBins{7, -7, 7}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy>; - BinningType corrBinning{{zBins, multBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType corrBinning{{zBins, multBins}}; SameKindPair pair{corrBinning, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks) @@ -223,7 +223,7 @@ struct MixedEventsVariousKinds { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; Pair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -258,7 +258,7 @@ struct MixedEventsTriple { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; SameKindTriple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -292,7 +292,7 @@ struct MixedEventsTripleVariousKinds { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; Triple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -401,7 +401,7 @@ struct MixedEventsPartitionedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -453,7 +453,7 @@ struct MixedEventsLambdaBinning { }; using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}, true}; + BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pair{binningWithLambda, 5, -1, collisions, tracksTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -490,8 +490,8 @@ struct MixedEventsCounters { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; - // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; + SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under / overflow(-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/eventMixingValidation.cxx b/Tutorials/src/eventMixingValidation.cxx index f7cf702f5d0..3b026e2c48e 100644 --- a/Tutorials/src/eventMixingValidation.cxx +++ b/Tutorials/src/eventMixingValidation.cxx @@ -40,7 +40,7 @@ struct MixedEventsEmptyTables { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -72,7 +72,7 @@ struct MixedEventsJoinedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::FullTracks const& tracks) @@ -104,7 +104,7 @@ struct MixedEventsJoinedTracks { // std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; // std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; // using BinningType = ColumnBinningPolicy; -// BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) +// BinningType binningOnPositions{{xBins, yBins}}; // SameKindPair pair{binningOnPositions, 5, -1}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored // // void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/tracksCombinations.cxx b/Tutorials/src/tracksCombinations.cxx index 6d73be618ab..f3a22c03d73 100644 --- a/Tutorials/src/tracksCombinations.cxx +++ b/Tutorials/src/tracksCombinations.cxx @@ -83,7 +83,7 @@ struct ConfigurableBinnedCollisionCombinations { void process(soa::Join const& collisions) { using BinningType = ColumnBinningPolicy>; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; + BinningType colBinning{{axisVertex, axisMultiplicity}}; int count = 0; // Strictly upper tracks binned by x and y position From bab344887ec9894e696a022363aac6234e4dba6a Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:58:36 +0200 Subject: [PATCH 196/648] [PWGLF] Fix MC association check in derivedlambdakzeroanalysis (#18186) --- PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 1befc65defe..1d7a4877abd 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -3078,7 +3078,8 @@ struct derivedlambdakzeroanalysis { } // consider only associated candidates if asked to do so, disregard association - if (!doMCAssociation) { + // do not perform MC association when running over real data + if (doprocessRealDataRun3 || doprocessRealDataRun2 || !doMCAssociation) { BITSET(selMap, selConsiderK0Short); BITSET(selMap, selConsiderLambda); BITSET(selMap, selConsiderAntiLambda); From aa4fb228af1532827c2d664b1f7be7d803510edc Mon Sep 17 00:00:00 2001 From: samtaarora1154-art Date: Tue, 6 Oct 2026 22:52:16 +0530 Subject: [PATCH 197/648] [PWGCF] Added ZDC timing cut (#18195) --- PWGCF/Flow/Tasks/flowEventPlane.cxx | 113 +++++++++++++++++++++------- 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowEventPlane.cxx b/PWGCF/Flow/Tasks/flowEventPlane.cxx index 2ab9c8d97df..1605f2a0836 100644 --- a/PWGCF/Flow/Tasks/flowEventPlane.cxx +++ b/PWGCF/Flow/Tasks/flowEventPlane.cxx @@ -167,12 +167,16 @@ struct SpCalibTableProducer { Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -184,7 +188,7 @@ struct SpCalibTableProducer { void init(InitContext const&) { // Histogram collision - histos.add("hCent", "Centrality", kTH1F, {{100, 0., 100., "FT0C%"}}); + histos.add("hCent", "Centrality", kTH1F, {{cCentBins, 0., 100., "FT0C%"}}); histos.add("hVz", "V_{z}", kTH1F, {{100, -10., 10., "V_{z}(cm)"}}); } @@ -226,6 +230,20 @@ struct SpCalibTableProducer { return true; } + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } + return true; + } + template void analyzeCollision(B const& bc, C const& collision) { @@ -243,6 +261,10 @@ struct SpCalibTableProducer { posZ = collision.posZ(); auto zdc = bc.zdc(); + if (!selZdcHit(zdc)) { + return; + } + std::vector znaEnergy, zncEnergy; for (int i = 0; i < kXYAC; ++i) { znaEnergy.push_back(zdc.energySectorZNA()[i]); @@ -292,12 +314,18 @@ struct SpectatorPlaneTableProducer { Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; + + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; // Coarse binning factor Configurable cAxisCBF{"cAxisCBF", 10, "Coarse Bin Factor"}; @@ -313,6 +341,7 @@ struct SpectatorPlaneTableProducer { Configurable cRecentVxVy{"cRecentVxVy", true, "Vx / Vy recentering"}; // Corrections + Configurable cLoadCorrection{"cLoadCorrection", true, "Load corrections"}; Configurable cDoGainCalib{"cDoGainCalib", true, "Gain Calib Flag"}; Configurable cUseAlphaZDC{"cUseAlphaZDC", true, "Use Alpha ZDC"}; @@ -327,7 +356,6 @@ struct SpectatorPlaneTableProducer { // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.1, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 10.0, "p_{T} maximum"}; - Configurable cNEtaBins{"cNEtaBins", 7, "# of eta bins"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; Configurable cTrackGlobal{"cTrackGlobal", true, "Global Track"}; Configurable cTrackDcaXYCut{"cTrackDcaXYCut", 0.1, "DcaXY Cut"}; @@ -389,7 +417,7 @@ struct SpectatorPlaneTableProducer { // Define axes const AxisSpec axisZDCEnergy{500, 0, 500, "ZD[AC] Signal"}; - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + const AxisSpec axisCent{cCentBins, 0., 100, "FT0C%"}; const AxisSpec axisVx{cAxisVxyBins, cAxisVxMin, cAxisVxMax, "V_{X}(cm)"}; const AxisSpec axisVy{cAxisVxyBins, cAxisVyMin, cAxisVyMax, "V_{Y}(cm)"}; const AxisSpec axisVz{cAxisVzBins, cMinZVtx, cMaxZVtx, "V_{Z}(cm)"}; @@ -468,6 +496,7 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector histos.add("DF/hQaQc", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisCent}); + histos.add("DF/hQaQcVarCent", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisVarCent}); } // Select collsion @@ -509,6 +538,20 @@ struct SpectatorPlaneTableProducer { return true; } + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } + return true; + } + // Load Gain Calibrations and ZDC Q-Vector Recentering Corrections void loadCorrections() { @@ -699,11 +742,18 @@ struct SpectatorPlaneTableProducer { // Zdc information auto zdc = bc.zdc(); + + if (!selZdcHit(zdc)) { + return false; + } auto znaEnergy = zdc.energySectorZNA(); auto zncEnergy = zdc.energySectorZNC(); auto znaEnergyCommon = zdc.energyCommonZNA(); auto zncEnergyCommon = zdc.energyCommonZNC(); + // Change in 3rd tower energy due to spurious peaks + zncEnergy[2] = zncEnergyCommon - zncEnergy[0] - zncEnergy[1] - zncEnergy[3]; + // Check energy deposits if (znaEnergyCommon <= 0 || zncEnergyCommon <= 0 || znaEnergy[0] <= 0 || znaEnergy[1] <= 0 || znaEnergy[2] <= 0 || znaEnergy[3] <= 0 || zncEnergy[0] <= 0 || zncEnergy[1] <= 0 || zncEnergy[2] <= 0 || zncEnergy[3] <= 0) { return false; @@ -901,6 +951,7 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector float qac = (vSP[kXa] * vSP[kXc]) + (vSP[kYa] * vSP[kYc]); histos.fill(HIST("DF/hQaQc"), cent, qac); + histos.fill(HIST("DF/hQaQcVarCent"), cent, qac); } // Fill table @@ -925,7 +976,9 @@ struct SpectatorPlaneTableProducer { struct FlowEventPlane { // Tracks - Configurable cEtaBins{"cEtaBins", 5, "# of eta bins"}; + Configurable cEtaBinsCharged{"cEtaBinsCharged", 5, "# of eta bins"}; + Configurable cEtaBinsHadrons{"cEtaBinsHadrons", 5, "# of eta bins"}; + Configurable cEtaBinsStrange{"cEtaBinsStrange", 5, "# of eta bins"}; Configurable cEtaCut{"cEtaCut", 0.8, "Rapidity cut"}; // Pi,Ka,Pr @@ -960,6 +1013,8 @@ struct FlowEventPlane { Configurable cLambdaMinPt{"cLambdaMinPt", 0.6, "Lambda Min pT"}; Configurable cLambdaMaxPt{"cLambdaMaxPt", 6.0, "Lambda Max pT"}; + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; + // Histogram registry: an object to hold your histograms HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -970,13 +1025,15 @@ struct FlowEventPlane { void init(InitContext const&) { // Define axes - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + // const AxisSpec axisCent{100, 0., 100, "FT0C%"}; const AxisSpec axisXYac{600, -6, 6, "Q^{t}Q^{p}"}; const AxisSpec axisV1{400, -4, 4, "v_{1}"}; const AxisSpec axisTrackPt{100, 0., 10., "p_{T} (GeV/#it{c})"}; - const AxisSpec axisTrackEta{cEtaBins, -0.8, 0.8, "#eta"}; + const AxisSpec axisTrackEtaCharged{cEtaBinsCharged, -0.8, 0.8, "#etaCharged"}; + const AxisSpec axisTrackEtaHadrons{cEtaBinsHadrons, -0.8, 0.8, "#etaHadrons"}; + const AxisSpec axisTrackEtaStrange{cEtaBinsStrange, -0.8, 0.8, "#etaStrange"}; const AxisSpec axisTrackDcaXY{60, -0.15, 0.15, "DCA_{XY}"}; const AxisSpec axisTrackDcaZ{230, -1.15, 1.15, "DCA_{XY}"}; const AxisSpec axisTrackdEdx{360, 20, 200, "#frac{dE}{dx}"}; @@ -1004,12 +1061,12 @@ struct FlowEventPlane { histos.add("TrackQA/hPtDcaXY", "DCA_{XY} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaXY}); histos.add("TrackQA/hPtDcaZ", "DCA_{Z} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaZ}); histos.add("TrackQA/hTrackTPCdEdX", "hTrackTPCdEdX", kTH2F, {axisMomPID, axisdEdx}); - histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); } // Identified hadrons @@ -1018,22 +1075,22 @@ struct FlowEventPlane { histos.add("PartId/Pion/hTOFSignal", "#beta_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackTofSignal}); histos.add("PartId/Pion/hTPCNSigma", "n#sigma_{TPC} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); histos.add("PartId/Pion/hTOFNSigma", "n#sigma_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); - histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); histos.addClone("PartId/Pion/", "PartId/Kaon/"); histos.addClone("PartId/Pion/", "PartId/Proton/"); } // Resonance if (doprocessResoFlow) { - histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); + histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); } // Lambda @@ -1053,14 +1110,14 @@ struct FlowEventPlane { histos.add("V0/Lambda/QA/hPosNsigPiVsP", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); histos.add("V0/Lambda/QA/hNegNsigPiVsP", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); histos.addClone("V0/Lambda/", "V0/K0Short/"); - histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisTrackEta, axisLambdaInvMass}); - histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackEta, axisLambdaInvMass}); - histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackEta, axisLambdaInvMass}); + histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); histos.addClone("V0/Lambda/", "V0/AntiLambda/"); histos.addClone("V0/Lambda/", "V0/LambdaAntiLambda/"); - histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisTrackEta, axisK0ShortInvMass}); - histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackEta, axisK0ShortInvMass}); - histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackEta, axisK0ShortInvMass}); + histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); } } From 8d78807497271551cd5199e23119fd969d273d41 Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:18:31 +0200 Subject: [PATCH 198/648] [PWGCF] Updates in femto framework (#18194) Co-authored-by: ALICE Action Bot --- PWGCF/Femto/Core/cascadeBuilder.h | 24 +-- PWGCF/Femto/Core/cascadeHistManager.h | 26 ++-- PWGCF/Femto/Core/charmHadronBuilder.h | 2 +- PWGCF/Femto/Core/charmHadronHistManager.h | 18 +-- PWGCF/Femto/Core/closePairRejection.h | 8 +- PWGCF/Femto/Core/collisionBuilder.h | 46 +++--- PWGCF/Femto/Core/collisionHistManager.h | 2 +- PWGCF/Femto/Core/dndetaHistManager.h | 2 +- PWGCF/Femto/Core/kinkBuilder.h | 6 +- PWGCF/Femto/Core/kinkHistManager.h | 12 +- PWGCF/Femto/Core/mcBuilder.h | 6 +- PWGCF/Femto/Core/pairCleaner.h | 12 +- PWGCF/Femto/Core/pairHistManager.h | 154 +++++++++++++++++++- PWGCF/Femto/Core/particleCleaner.h | 10 +- PWGCF/Femto/Core/trackBuilder.h | 34 ++--- PWGCF/Femto/Core/trackHistManager.h | 20 +-- PWGCF/Femto/Core/tripletHistManager.h | 10 +- PWGCF/Femto/Core/twoTrackResonanceBuilder.h | 6 +- PWGCF/Femto/Core/v0Builder.h | 26 ++-- PWGCF/Femto/Core/v0HistManager.h | 12 +- PWGCF/Femto/TableProducer/femtoProducer.cxx | 35 ++++- 21 files changed, 320 insertions(+), 151 deletions(-) diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 1248e0bfc8d..84e3809d0f0 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -76,9 +76,9 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for negative daughter tracks"}; \ - o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsimga_Pion/Proton| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \ o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection"}; \ o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, candidates whose daughters have no TOF signal are kept"}; @@ -86,15 +86,15 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { struct ConfXiBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("XiBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for bachelor tracks"}; - o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsimga_Pion| TOF for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsigma_Pion| TOF for bachelor tracks"}; }; struct ConfOmegaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("OmegaBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsimga_Kaon| TPC for bachelor tracks"}; - o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsimga_Kaon| TOF for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsigma_Kaon| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsigma_Kaon| TOF for bachelor tracks"}; }; #undef CASCADE_DEFAULT_BITS @@ -130,19 +130,19 @@ enum CascadeSels { // selections for cascades kCascadeCpaMin, ///< Min. CPA (cosine pointing angle) kCascadePaMax, ///< Max. PA (pointing angle in rad); disabled by default - kCascadeDcaDaughMax, ///< Max. DCA of the daughers at decay vertex + kCascadeDcaDaughMax, ///< Max. DCA of the daughters at decay vertex kCascadeTransRadMin, ///< max. transverse radius // selection for lambda daughter kLambdaCpaMin, ///< Min. CPA of the lambda kLambdaDcaDauMax, ///< Max. DCA between the lambda daughters at lambda decay vertex - kLambdaTransRadMin, ///< Min. tranverse radius of the lambda + kLambdaTransRadMin, ///< Min. transverse radius of the lambda kLambdaDcaToPvMin, ///< Min. DCA of the lambda to the primary vertex - // selection for bachelor/daugthers + // selection for bachelor/daughters kDauAbsEtaMax, ///< Max. |eta| of daughter tracks kDauTpcClsMin, ///< Min. number of TPC clusters of daughters/bachelor - kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughers and bachelor from primary vertex + kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughters and bachelor from primary vertex // PID selection for cascade bachelor kBachelorTpcPion, ///< TPC Pion PID for bachelor @@ -150,7 +150,7 @@ enum CascadeSels { kBachelorTofPion, ///< TOF Pion PID for bachelor kBachelorTofKaon, ///< TOF Kaon PID for bachelor /// - // PID selection for lambda daughers + // PID selection for lambda daughters kPosDauTpc, ///< TPC PID for positive daughter kNegDauTpc, ///< TPC PID for negative daughter kPosDauTof, ///< TOF PID for positive daughter diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index 5d60057b60a..c9b6659678a 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -83,7 +83,7 @@ enum CascadeHist { kTruePtVsPt, kTrueEtaVsEta, kTruePhiVsPhi, - // histograms for fraction estimation of v0s + // histograms for fraction estimation of cascades kNoMcParticle, kPrimary, kFromWrongCollision, @@ -120,16 +120,16 @@ struct ConfCascadeQaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d Qa histograms"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; - o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Poiting angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "mass for antiparticle hypothesis"}; - o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "Mass under the Xi hypothesis"}; + o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "Mass under the Omega hypothesis"}; o2::framework::ConfigurableAxis lambdaMass{"lambdaMass", {{200, 1., 1.2}}, "Mass of daughter lambda"}; o2::framework::ConfigurableAxis lambdaCosPa{"lambdaCosPa", {{100, 0.9, 1}}, "Cosine of pointing angle of daughter lambda"}; o2::framework::ConfigurableAxis lambdaDauDca{"lambdaDauDca", {{150, 0, 1.0}}, "DCA of lambda daughters at lambda decay vertex"}; - o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "DCA of lambda daughters at lambda decay vertex"}; + o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "Transverse radius of daughter #Lambda decay vertex"}; o2::framework::ConfigurableAxis lambdaDcaToPv{"lambdaDcaToPv", {{100, 0, 200}}, "DCA of lambda daughter from primary vertex"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for bachelor/daughters"}; }; @@ -156,7 +156,7 @@ constexpr std::array, kCascadeHistLast> HistT {kDecayDauDca, o2::framework::HistType::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, {kTransRadius, o2::framework::HistType::kTH1F, "hTransRadius", "Transverse radius ; r_{xy} (cm); Entries"}, {kLambdaMass, o2::framework::HistType::kTH1F, "hLambdaMass", "Invariant mass of daughter lambda ; m_{#Lambda dau} (GeV/#it{c}^{2}); Entries"}, - {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of poiting angle of daughter lambda ; cos_{#Lambda dau}(#alpha); Entries"}, + {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of pointing angle of daughter lambda ; cos_{#Lambda dau}(#alpha); Entries"}, {kLambdaDauDca, o2::framework::HistType::kTH1F, "hLambdaDauDca", "Daughter DCA at #Lambda decay vertex ; DCA_{#Lambda dau decay vertex} (cm); Entries"}, {kLambdaTransRadius, o2::framework::HistType::kTH1F, "hLambdaTransRadius", "Transverse radius of daughter #Lambda ; r_{xy,#Lambda dau} (cm); Entries"}, {kLambdaDcaToPv, o2::framework::HistType::kTH1F, "hLambdaDcaToPv", "DCA to primary vertex of daughter #Lambda ; DCA_{#Lambda} (cm); Entries"}, @@ -166,8 +166,8 @@ constexpr std::array, kCascadeHistLast> HistT {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of poiting angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Poiting angle; p_{T} (GeV/#it{c}); #alpha"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle; p_{T} (GeV/#it{c}); #alpha"}, {kPtVsMassXi, o2::framework::HistType::kTH2F, "hPtVsMassXi", "p_{T} vs mass #Xi; p_{T} (GeV/#it{c}); m_{#Lambda#pi} (GeV/#it{c}^{2})"}, {kPtVsMassOmega, o2::framework::HistType::kTH2F, "hPtVsMassOmega", "p_{T} vs mass #Omega; p_{T} (GeV/#it{c}); m_{#LambdaK} (GeV/#it{c}^{2})"}, {kPtVsMassLambda, o2::framework::HistType::kTH2F, "hPtVsMassLambda", "p_{T} vs mass daughter #Lambda; p_{T} (GeV/#it{c}); m_{#Lambda dau} (GeV/#it{c}^{2})"}, @@ -176,9 +176,9 @@ constexpr std::array, kCascadeHistLast> HistT {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, - {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, - {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed v0; PDG Code; Entries"}, + {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed cascade; PDG Code; Entries"}, + {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed cascade; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed cascade; PDG Code; Entries"}, {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True transverse momentum vs transverse momentum; p_{T,True} (GeV/#it{c}); p_{T,True} (GeV/#it{c})"}, {kTrueEtaVsEta, o2::framework::HistType::kTH2F, "hTrueEtaVsEta", "True pseudorapdity vs pseudorapdity; #eta_{True}; #eta"}, {kTruePhiVsPhi, o2::framework::HistType::kTH2F, "hTruePhiVsPhi", "True azimuthal angle vs azimuthal angle; #varphi_{True}; #varphi"}, @@ -742,7 +742,7 @@ class CascadeHistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } diff --git a/PWGCF/Femto/Core/charmHadronBuilder.h b/PWGCF/Femto/Core/charmHadronBuilder.h index 62f235da485..a907062a9ad 100644 --- a/PWGCF/Femto/Core/charmHadronBuilder.h +++ b/PWGCF/Femto/Core/charmHadronBuilder.h @@ -92,7 +92,7 @@ struct ConfD0Selection : o2::framework::ConfigurableGroup { o2::framework::Configurable ptMin{"ptMin", 1.f, "Minimum pT"}; o2::framework::Configurable ptMax{"ptMax", 3.f, "Maximum pT"}; // acceptance is applied as a rapidity cut in the builder; the eta/phi windows - // are kept open and exist only to satisfy macro + // are kept open and exist only to satisfy the partition macro o2::framework::Configurable etaMin{"etaMin", -0.8f, "Minimum eta"}; o2::framework::Configurable etaMax{"etaMax", 0.8f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; diff --git a/PWGCF/Femto/Core/charmHadronHistManager.h b/PWGCF/Femto/Core/charmHadronHistManager.h index f5d81d93f5b..e0391aad0ae 100644 --- a/PWGCF/Femto/Core/charmHadronHistManager.h +++ b/PWGCF/Femto/Core/charmHadronHistManager.h @@ -88,15 +88,15 @@ enum CharmHadronHist { }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CHARMHADRON_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis charmHadrons{"charmHadrons", {{8001, -4000.5, 4000.5}}, "MC ONLY: CharmHadrons codes of reconstructed D0s"}; \ - o2::framework::ConfigurableAxis pt2d{"pt2d", {{240, 0, 6}}, "Pt for 2D QA"}; \ - o2::framework::ConfigurableAxis eta2d{"eta2d", {{200, -1.5, 1.5}}, "Eta for 2D QA"}; \ +#define CHARMHADRON_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis charmHadrons{"charmHadrons", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed charm hadrons"}; \ + o2::framework::ConfigurableAxis pt2d{"pt2d", {{240, 0, 6}}, "Pt for 2D QA"}; \ + o2::framework::ConfigurableAxis eta2d{"eta2d", {{200, -1.5, 1.5}}, "Eta for 2D QA"}; \ o2::framework::ConfigurableAxis phi2d{"phi2d", {{200, 0, 1.f * o2::constants::math::TwoPI}}, "Phi for 2D QA"}; template diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index a8db746c394..8e763b7affc 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -66,10 +66,10 @@ struct ConfCpr : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); o2::framework::Configurable cutAverage{"cutAverage", DefaultCutAverage, "Apply CPR if the average deta-dphistar is below the configured values"}; o2::framework::Configurable cutAnyRadius{"cutAnyRadius", false, "Apply CPR if the deta-dphistar is below the configured values at any radius"}; - o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as episoidal cut. If false use rectangluar cut."}; + o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as ellipsoidal cut. If false use rectangular cut."}; o2::framework::Configurable plotAllRadii{"plotAllRadii", true, "Plot deta-dphi distribution at all radii"}; o2::framework::Configurable plotAverage{"plotAverage", true, "Plot average deta dphi distribution"}; - o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2"}; + o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2)"}; o2::framework::Configurable plotKinematic{"plotKinematic", true, "Plot kinematic (kstar/Q3) distribution of blocked pairs/triplets"}; o2::framework::Configurable detaMax{"detaMax", 0.01f, "Maximium deta"}; o2::framework::Configurable dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"}; @@ -172,7 +172,7 @@ constexpr std::array, kCprHistogramLast> HistTabl {kRadius6, o2::framework::HistType::kTH2F, "hRadius6", "Radius 6: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius7, o2::framework::HistType::kTH2F, "hRadius7", "Radius 7: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius8, o2::framework::HistType::kTH2F, "hRadius8", "Radius 8: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, - {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic distribution of blocked pairs; kinematic Variable (GeV/c#it{c}); Entries"}, + {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic distribution of blocked pairs; kinematic Variable (GeV/#it{c}); Entries"}, {kPhi1VsPhi2, o2::framework::HistType::kTH2F, "hPhi1vsPhi2", "#phi_{1} vs #phi_{2}; #phi_{1}; #phi_{2}"}, {kEta1VsEta2, o2::framework::HistType::kTH2F, "hEta1VsEta2", "#eta_{1} vs #eta_{2}; #eta_{1}; #eta_{2}"}}}; @@ -334,7 +334,7 @@ class CloseTrackRejection count++; } } - // for small momemeta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii + // for small momenta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii if (count > 0) { mAverageDphistar = std::accumulate(mDphistar.begin(), mDphistar.end(), 0.f) / count; // only average values if phistar could be computed } else { diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 1c4737bdb09..2ae41c34b78 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -57,28 +57,28 @@ struct ConfCollisionFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable sphericityMax{"sphericityMax", 1.f, "Maximum sphericity"}; o2::framework::Configurable magFieldMin{"magFieldMin", -5, "Minimum magnetic field strength (kG)"}; o2::framework::Configurable magFieldMax{"magFieldMax", 5, "Maximum magnetic field strength (kG)"}; - o2::framework::Configurable subGeneratorId{"subGeneratorId", 0, "MC ONLY: If positive, keep 0 = MB, <0 triggered on something"}; + o2::framework::Configurable subGeneratorId{"subGeneratorId", 0, "MC ONLY: Sub-generator ID to keep (0 = MB). Only applied if >= 0; set to -1 (default) to disable and keep all sub-generators"}; }; struct ConfCollisionBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionBits"); o2::framework::Configurable passThrough{"passThrough", false, "If true, all collisions are passed through. Bits for all enabled (non-zero) selection flags are stored, disabled flags (0) are not."}; - o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayerAll{"isGoodItsLayerAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occpancy"}; - o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occpancy"}; + o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayerAll{"isGoodItsLayerAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occupancy"}; + o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occupancy"}; o2::framework::Configurable> sphericityMin{"sphericityMin", {}, "Minimum sphericity"}; o2::framework::Configurable> sphericityMax{"sphericityMax", {}, "Maximum sphericity"}; o2::framework::Configurable> triggers{"triggers", {}, "List of all triggers to be used"}; @@ -90,7 +90,7 @@ struct ConfCollisionBits : o2::framework::ConfigurableGroup { struct ConfCcdb : o2::framework::ConfigurableGroup { std::string prefix = std::string("ConfCcdb"); o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL to ccdb"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP)"}; o2::framework::Configurable triggerPath{"triggerPath", "EventFiltering/Zorro/", "CCDB path for trigger information"}; o2::framework::Configurable magFieldForced{"magFieldForced", 0, "Force value for magnetic field (kG). This will skip calls to the ccdb. Deactivate by setting value to 0"}; }; @@ -118,7 +118,7 @@ struct ConfCollisionSelection : o2::framework::ConfigurableGroup { /// enum for all collision selections enum CollisionSels { - // collsion selection flags + // collision selection flags kSel8, ///< Sel8 kRctFlags, ///< RCT flags ok kNoSameBunchPileUp, ///< Reject collisions in case of pileup with another collision in the same foundBC @@ -153,10 +153,10 @@ const std::unordered_map collisionSelectionNames = { {kIsGoodZvtxFt0VsPv, "Is good zvtx FT0 vs PV"}, {kNoCollInTimeRangeNarrow, "No collision in time range narrow"}, {kNoCollInTimeRangeStrict, "No collision in time range strict"}, - {kNoCollInTimeRangeStandard, "No collission in time range standard"}, - {kNoCollInRofStrict, "No collsion in ROF strict"}, + {kNoCollInTimeRangeStandard, "No collision in time range standard"}, + {kNoCollInRofStrict, "No collision in ROF strict"}, {kNoCollInRofStandard, "No collision in ROF standard"}, - {kNoHighMultCollInPrevRof, "No high mult collsions in previous ROF"}, + {kNoHighMultCollInPrevRof, "No high mult collisions in previous ROF"}, {kIsGoodItsLayer3, "Is good ITS layer 3"}, {kIsGoodItsLayer0123, "Is good ITS layer 0-3"}, {kIsGoodItsLayerAll, "Is good ITS layer all"}, @@ -462,7 +462,7 @@ class CollisionSelection : public baseselection::BaseSelectionreset(); - // casting bool to float gurantees false -> 0 and true -> 1 + // casting bool to float guarantees false -> 0 and true -> 1 // and we check for equality to 1, so evaluation succeeds if the selection bit is true this->evaluateObservable(kSel8, static_cast(col.sel8())); this->evaluateObservable(kNoSameBunchPileUp, static_cast(col.selection_bit(o2::aod::evsel::kNoSameBunchPileup))); diff --git a/PWGCF/Femto/Core/collisionHistManager.h b/PWGCF/Femto/Core/collisionHistManager.h index f233455fbbe..2c77591e0c4 100644 --- a/PWGCF/Femto/Core/collisionHistManager.h +++ b/PWGCF/Femto/Core/collisionHistManager.h @@ -87,7 +87,7 @@ constexpr std::array, kColHistLast> HistTable = { {kCentVsMult, o2::framework::HistType::kTH2F, "hCentVsMult", "Centrality vs Multiplicity; Centrality (%); Multiplicity"}, {kMultVsSphericity, o2::framework::HistType::kTH2F, "hMultVsSphericity", "Multiplicity vs Sphericity; Multiplicity; Sphericity"}, {kCentVsSphericity, o2::framework::HistType::kTH2F, "hCentVsSphericity", "Centrality vs Sphericity; Centrality (%); Sphericity"}, - {kFT0AvsFT0C, o2::framework::HistType::kTH2F, "hFT0AvsFT0C", "FT0A centrality vs FT0C centrality; Centrality_{FT0A} (%); Centrality_{FT0C}"}, + {kFT0AvsFT0C, o2::framework::HistType::kTH2F, "hFT0AvsFT0C", "FT0A centrality vs FT0C centrality; Centrality_{FT0A} (%); Centrality_{FT0C} (%)"}, // event shape {kQvector, o2::framework::HistType::kTH1F, "hQvector", "q-vector; q-vector; Entries"}, {kEventPlaneAngle, o2::framework::HistType::kTH1F, "hEventPlaneAngle", "Event Plane angle; #Psi_{EP}; Entries"}, diff --git a/PWGCF/Femto/Core/dndetaHistManager.h b/PWGCF/Femto/Core/dndetaHistManager.h index ed4f425aed6..69b2107d575 100644 --- a/PWGCF/Femto/Core/dndetaHistManager.h +++ b/PWGCF/Femto/Core/dndetaHistManager.h @@ -11,7 +11,7 @@ /// \file dndetaHistManager.h /// \brief histogram manager for charged-particle pseudorapidity density measurements on femto derived data -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 009be2f6dd4..4fd8670accb 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -95,15 +95,15 @@ struct ConfSigmaPlusBits : o2::framework::ConfigurableGroup { // base selection for analysis task for kinks // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Select antipartilce via sign"}; \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Select antiparticle via sign"}; \ o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Sigma"}; \ - o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Sigma"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass"}; \ o2::framework::Configurable mask{"mask", 0x0, "Bitmask for kink selection"}; // base selection for analysis task for sigmas diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index 3e98b8865e0..4cd68cb901a 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -93,7 +93,7 @@ constexpr std::size_t MaxSecondary = 3; o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected V0s"}; + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected kinks"}; template struct ConfSigmaBinning : o2::framework::ConfigurableGroup { @@ -157,7 +157,7 @@ constexpr std::array, kKinkHistLast> HistTable = {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed kinks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed kink; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother reconstructed knik; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed kink; PDG Code; Entries"}, {kTruePt, o2::framework::HistType::kTH1F, "hTruePt", "True transverse momentum; p_{T} (GeV/#it{c}); Entries"}, {kTrueEta, o2::framework::HistType::kTH1F, "hTrueEta", "True pseudorapdity; #eta; Entries"}, {kTruePhi, o2::framework::HistType::kTH1F, "hTruePhi", "True azimuthal angle; #varphi; Entries"}, @@ -167,9 +167,9 @@ constexpr std::array, kKinkHistLast> HistTable = {kFromMaterial, o2::framework::HistType::kTH2F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); kink angle"}, {kMissidentified, o2::framework::HistType::kTH2F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); kink angle"}, {kSecondary1, o2::framework::HistType::kTH2F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); kink angle"}}}; + {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); kink angle"}}}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_HIST_ANALYSIS_MAP(conf) \ @@ -618,7 +618,7 @@ class KinkHistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 4b9526d4ae9..9a76b65d0f9 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -84,8 +84,8 @@ struct ConfMcTables : o2::framework::ConfigurableGroup { o2::framework::Configurable producedK0shortLabels{"producedK0shortLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedD0Labels{"producedD0Labels", -1, "Produce D0 labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedLcLabels{"producedLcLabels", -1, "Produce Lc labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce sigma labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce sigma plus labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedXiLabels{"producedXiLabels", -1, "Produce xi labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedOmegaLabels{"producedOmegaLabels", -1, "Produce omega labels (-1: auto; 0 off; 1 on)"}; }; @@ -197,7 +197,7 @@ class McBuilder this->fillMcCollision(mcCol, mcProducts); } // Add label - mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collsions has been added so we can now safely retrieve the index + mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collision has been added so we can now safely retrieve the index } else { // If no MC collision associated, fill empty label mcProducts.producedCollisionLabels(-1); diff --git a/PWGCF/Femto/Core/pairCleaner.h b/PWGCF/Femto/Core/pairCleaner.h index 6bf859149b8..66368ed8a5d 100644 --- a/PWGCF/Femto/Core/pairCleaner.h +++ b/PWGCF/Femto/Core/pairCleaner.h @@ -158,13 +158,13 @@ class BasePairCleaner bool mcPairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& partonicMothers) const { if (mUseMotherAsAncestor) { - // if one of the two particles has no associated mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { return false; } return !this->mcPairHasCommonMother(particle1, particle2); } - // if one of the two particles has no associated partonic mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } @@ -175,7 +175,7 @@ class BasePairCleaner template bool pairHasCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const { - // if one of the two particles has no associated mc particle, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { return false; } @@ -190,7 +190,7 @@ class BasePairCleaner template bool pairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const { - // if one of the two particles has no associated mc particle, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { return false; } @@ -210,7 +210,7 @@ class BasePairCleaner template bool mcPairHasCommonPartonicMother(T1 const& particle1, T2 const& particle2, T3 const& /*partonicMothers*/) const { - // if one of the two particles has no associated partonic mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } @@ -227,7 +227,7 @@ class BasePairCleaner template bool mcPairHasCommonMother(T1 const& particle1, T2 const& particle2) const { - // if one of the two particles has no associated mother, we cannot know if they have a common anchestor, so we break out with false + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { return false; } diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index c2fef5d2659..75c3051cc45 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -11,7 +11,7 @@ /// \file pairHistManager.h /// \brief histogram manager for pair tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ @@ -29,6 +29,8 @@ #include #include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include @@ -105,6 +107,19 @@ enum PairHist { kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, // dalitz plots kDalitz, // between a track and pos/neg daughter of another particle + // angles + kAlpha, // angle between pT1 and pT2 of the paritcles in the lab frame + kAlphaVsKstar, + kAlphaVsKt, + kAlphaVsMt, + kBetaLab, // angle between kT and kstar in lab frame + kBetaLabVsKstar, + kBetaLabVsKt, + kBetaLabVsMt, + kKstarVsAlphaVsMtVsMult, + kKstarVsAlphaVsMtVsPt1VsPt2VsMult, + kKstarVsBetaLabVsMtVsMult, + kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, // reco-vs-mc-truth correlation (requires BOTH a reco pair and matched mc info) kTrueKstarVsKstar, kTrueKtVsKt, @@ -213,6 +228,14 @@ struct ConfPairBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent", false, "(Reco) Enable 8D histogram (Kstar Vs Mt Vs Minv Vs Kt Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; o2::framework::Configurable plotDalitz{"plotDalitz", false, "(Reco) Enable dalitz plot. Not supported for pure mc-truth pairs (no trackTable/daughter structure)"}; o2::framework::Configurable plotDeltaEtaDeltaPhi{"plotDeltaEtaDeltaPhi", false, "(Reco/Mc) Plot #Delta#phi vs #Delta#eta"}; + o2::framework::Configurable plotAlpha{"plotAlpha", false, "(Reco/Mc) Plot alpha (angle between pT1 and pT2) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis alpha{"alpha", {{180, -o2::constants::math::PI, o2::constants::math::PI}}, "alpha (angle between pT1 and pT2)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsMult{"plotKstarVsAlphaVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs Alpha Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsPt1VsPt2VsMult{"plotKstarVsAlphaVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs Alpha Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotBetaLab{"plotBetaLab", false, "(Reco/Mc) Plot betaLab (angle between kT and k* in lab frame) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis betaLab{"betaLab", {{90, 0.f, o2::constants::math::PI}}, "betaLab (angle between kT and k* in lab frame)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsMult{"plotKstarVsBetaLabVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs BetaLab Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult{"plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs BetaLab Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar"}; o2::framework::ConfigurableAxis kt{"kt", {{600, 0, 6}}, "kt"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; @@ -257,8 +280,8 @@ struct ConfPairCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; o2::framework::Configurable massInvMin{"massInvMin", -1, "Minimal invariant mass (set to -1 to deactivate)"}; o2::framework::Configurable massInvMax{"massInvMax", -1, "Maximal invariant mass (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; }; @@ -348,13 +371,26 @@ constexpr std::array, kPairHistogramLast> {kTrueKstarVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMult", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True};"}, {kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True}; Centrality_{True} (%);"}, // mixing qa - {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # partilce 1; # particle 2"}, - {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window Entries"}, + {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # particle 1; # particle 2"}, + {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window; Entries"}, {kMeMixingWindowEffective, o2::framework::HistType::kTH1F, "hMeMixingWindowEffective", "Effective Mixing Window; Effective Mixing Window; Entries"}, - {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique partilce 2 in each mixing bin; # partilce 1; # particle 2"}, + {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each mixing bin; # particle 1; # particle 2"}, {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, o2::framework::HistType::kTHnSparseF, "hVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2", "Mixing bins; V_{z,1} (cm); multiplicity_{1}; centrality_{1} (%); V_{z,2} (cm); multiplicity_{2}; centrality_{2} (%)"}, // angular {kDeltaEtaDeltaPhi, o2::framework::HistType::kTH2F, "hDeltaEtaDeltaPhi", "#Delta#phi vs #Delta#eta; #Delta#phi; #Delta#eta"}, + // angles + {kAlpha, o2::framework::HistType::kTH1F, "hAlpha", "angle between p_{T,1} and p_{T,2}; #alpha; Entries"}, + {kAlphaVsKstar, o2::framework::HistType::kTH2F, "hAlphaVsKstar", "#alpha vs k*; #alpha; k* (GeV/#it{c})"}, + {kAlphaVsKt, o2::framework::HistType::kTH2F, "hAlphaVsKt", "#alpha vs k_{T}; #alpha; k_{T} (GeV/#it{c})"}, + {kAlphaVsMt, o2::framework::HistType::kTH2F, "hAlphaVsMt", "#alpha vs m_{T}; #alpha; m_{T} (GeV/#it{c}^{2})"}, + {kBetaLab, o2::framework::HistType::kTH1F, "hBetaLab", "angle between k_{T} and k* (lab frame); #beta_{lab}; Entries"}, + {kBetaLabVsKstar, o2::framework::HistType::kTH2F, "hBetaLabVsKstar", "#beta_{lab} vs k*; #beta_{lab}; k* (GeV/#it{c})"}, + {kBetaLabVsKt, o2::framework::HistType::kTH2F, "hBetaLabVsKt", "#beta_{lab} vs k_{T}; #beta_{lab}; k_{T} (GeV/#it{c})"}, + {kBetaLabVsMt, o2::framework::HistType::kTH2F, "hBetaLabVsMt", "#beta_{lab} vs m_{T}; #beta_{lab}; m_{T} (GeV/#it{c}^{2})"}, + {kKstarVsAlphaVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsMult", "k* vs #alpha vs m_{T} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsPt1VsPt2VsMult", "k* vs #alpha vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsMult", "k* vs #beta_{lab} vs m_{T} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsPt1VsPt2VsMult", "k* vs #beta_{lab} vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, // Bertsch-Pratt 3D decomposition in LCMS {kQout, o2::framework::HistType::kTH1F, "hQout", "q_{out} in LCMS; q_{out} (GeV/#it{c}); Entries"}, {kQside, o2::framework::HistType::kTH1F, "hQside", "q_{side} in LCMS; q_{side} (GeV/#it{c}); Entries"}, @@ -410,6 +446,18 @@ constexpr std::array, kPairHistogramLast> {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ {kDalitz, {(confAnalysis).kstar, (confAnalysis).dalitzMtot, (confAnalysis).dalitzM12, (confAnalysis).dalitzM13}}, \ {kDeltaEtaDeltaPhi, {(confAnalysis).binningDeltaPhi, (confAnalysis).binningDeltaEta}}, \ + {kAlpha, {(confAnalysis).alpha}}, \ + {kAlphaVsKstar, {(confAnalysis).alpha, (confAnalysis).kstar}}, \ + {kAlphaVsKt, {(confAnalysis).alpha, (confAnalysis).kt}}, \ + {kAlphaVsMt, {(confAnalysis).alpha, (confAnalysis).mt}}, \ + {kBetaLab, {(confAnalysis).betaLab}}, \ + {kBetaLabVsKstar, {(confAnalysis).betaLab, (confAnalysis).kstar}}, \ + {kBetaLabVsKt, {(confAnalysis).betaLab, (confAnalysis).kt}}, \ + {kBetaLabVsMt, {(confAnalysis).betaLab, (confAnalysis).mt}}, \ + {kKstarVsAlphaVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ {kQout, {(confAnalysis).qout}}, \ {kQside, {(confAnalysis).qside}}, \ {kQlong, {(confAnalysis).qlong}}, \ @@ -588,6 +636,12 @@ class PairHistManager mPlotDalitz = ConfPairBinning.plotDalitz.value; mPlotDeltaEtaDeltaPhi = ConfPairBinning.plotDeltaEtaDeltaPhi.value; + mPlotAlpha = ConfPairBinning.plotAlpha.value; + mPlotKstarVsAlphaVsMtVsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsMult.value; + mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsPt1VsPt2VsMult.value; + mPlotBetaLab = ConfPairBinning.plotBetaLab.value; + mPlotKstarVsBetaLabVsMtVsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsMult.value; + mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult.value; mPlotBertschPratt = ConfPairBinning.plotBertschPratt.value; mPlotEventShape = ConfPairBinning.plotEventShape.value; @@ -1004,6 +1058,34 @@ class PairHistManager if (mPlotDeltaEtaDeltaPhi) { mHistogramRegistry->add(analysisDir + getHistNameV2(kDeltaEtaDeltaPhi, HistTable), getHistDesc(kDeltaEtaDeltaPhi, HistTable), getHistType(kDeltaEtaDeltaPhi, HistTable), {Specs.at(kDeltaEtaDeltaPhi)}); } + if (mPlotAlpha) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlpha, HistTable), getHistDesc(kAlpha, HistTable), getHistType(kAlpha, HistTable), {Specs.at(kAlpha)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKstar, HistTable), getHistDesc(kAlphaVsKstar, HistTable), getHistType(kAlphaVsKstar, HistTable), {Specs.at(kAlphaVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKt, HistTable), getHistDesc(kAlphaVsKt, HistTable), getHistType(kAlphaVsKt, HistTable), {Specs.at(kAlphaVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsMt, HistTable), getHistDesc(kAlphaVsMt, HistTable), getHistType(kAlphaVsMt, HistTable), {Specs.at(kAlphaVsMt)}); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsMult)}); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsPt1VsPt2VsMult)}); + } + if (mPlotBetaLab) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLab, HistTable), getHistDesc(kBetaLab, HistTable), getHistType(kBetaLab, HistTable), {Specs.at(kBetaLab)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKstar, HistTable), getHistDesc(kBetaLabVsKstar, HistTable), getHistType(kBetaLabVsKstar, HistTable), {Specs.at(kBetaLabVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKt, HistTable), getHistDesc(kBetaLabVsKt, HistTable), getHistType(kBetaLabVsKt, HistTable), {Specs.at(kBetaLabVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsMt, HistTable), getHistDesc(kBetaLabVsMt, HistTable), getHistType(kBetaLabVsMt, HistTable), {Specs.at(kBetaLabVsMt)}); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsMult)}); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult)}); + } if (mPlotBertschPratt) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQout, HistTable), getHistDesc(kQout, HistTable), getHistType(kQout, HistTable), {Specs.at(kQout)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kQside, HistTable), getHistDesc(kQside, HistTable), getHistType(kQside, HistTable), {Specs.at(kQside)}); @@ -1262,6 +1344,13 @@ class PairHistManager mDeltaPhi = RecoDecay::constrainAngle(particle1.phi() - particle2.phi(), -o2::constants::math::PIHalf); } + if (mPlotAlpha || mPlotKstarVsAlphaVsMtVsMult || mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mAlpha = getAlpha(mParticle1, mParticle2); + } + if (mPlotBetaLab || mPlotKstarVsBetaLabVsMtVsMult || mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mBetaLab = getBetaLab(mParticle1, mParticle2); + } + if (mPlotDalitz) { if constexpr (modes::isEqual(particleType1, modes::Particle::kTrack) && (modes::isEqual(particleType2, modes::Particle::kV0) || modes::isEqual(particleType2, modes::Particle::kTwoTrackResonance) || modes::isEqual(particleType2, modes::Particle::kCharmHadron)) && requires(T2 p) { p.posDauId(); p.negDauId(); }) { @@ -1372,6 +1461,34 @@ class PairHistManager if (mPlotDeltaEtaDeltaPhi) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kDeltaEtaDeltaPhi, HistTable)), mDeltaPhi, mDeltaEta); } + if (mPlotAlpha) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlpha, HistTable)), mAlpha); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKstar, HistTable)), mAlpha, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKt, HistTable)), mAlpha, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsMt, HistTable)), mAlpha, mMt); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsMult, HistTable)), mKstar, mAlpha, mMt, mMult); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mAlpha, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } + if (mPlotBetaLab) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLab, HistTable)), mBetaLab); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKstar, HistTable)), mBetaLab, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKt, HistTable)), mBetaLab, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsMt, HistTable)), mBetaLab, mMt); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsMult, HistTable)), mKstar, mBetaLab, mMt, mMult); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mBetaLab, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } if (mPlotBertschPratt) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQout, HistTable)), mQout); mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQside, HistTable)), mQside); @@ -1537,6 +1654,22 @@ class PairHistManager return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); } + float getAlpha(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) + { + return static_cast(RecoDecay::constrainAngle(part2.phi() - part1.phi(), -o2::constants::math::PI)); + } + + float getBetaLab(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) + { + ROOT::Math::XYZVector p1(part1.Px(), part1.Py(), part1.Pz()); + ROOT::Math::XYZVector p2(part2.Px(), part2.Py(), part2.Pz()); + + ROOT::Math::XYZVector kstar = p1 - p2; + ROOT::Math::XYZVector kt = p1 + p2; + + return static_cast(std::acos(kstar.Dot(kt) / std::sqrt(kstar.Mag2() * kt.Mag2()))); + } + std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) { const ROOT::Math::PxPyPzEVector p1(part1); @@ -1700,6 +1833,8 @@ class PairHistManager double mMass12 = 0.; double mMass13 = 0.; double mMassTot2 = 0.; + float mAlpha = 0.f; + float mBetaLab = 0.f; // mc (used for both reco-vs-truth correlation AND pure mc-truth-only pairs — // for the latter, these are simply the primary/only kinematic values, not a @@ -1762,6 +1897,13 @@ class PairHistManager bool mPlotDalitz = false; bool mPlotDeltaEtaDeltaPhi = false; + bool mPlotAlpha = false; + bool mPlotKstarVsAlphaVsMtVsMult = false; + bool mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = false; + bool mPlotBetaLab = false; + bool mPlotKstarVsBetaLabVsMtVsMult = false; + bool mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = false; + bool mPlotBertschPratt = false; bool mPlotEventShape = false; diff --git a/PWGCF/Femto/Core/particleCleaner.h b/PWGCF/Femto/Core/particleCleaner.h index 6c29b6ced89..b7387cc3ec8 100644 --- a/PWGCF/Femto/Core/particleCleaner.h +++ b/PWGCF/Femto/Core/particleCleaner.h @@ -31,11 +31,11 @@ struct ConfParticleCleaner : o2::framework::ConfigurableGroup { o2::framework::Configurable> rejectedPdgCodes{"rejectedPdgCodes", {}, "Reject particles with this exact pdg code (including the sign!)"}; o2::framework::Configurable rejectParticleWithoutMcParticle{"rejectParticleWithoutMcParticle", false, "If true, particles which have no associated MC information, are rejected"}; o2::framework::Configurable rejectParticleWithoutMcMother{"rejectParticleWithoutMcMother", false, "If true, particles which have no associated mother are rejected"}; - o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, all particles which have no associated partonic mother"}; - o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; + o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, particles which have no associated partonic mother are rejected"}; + o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers do not have one of the supplied pdg codes (including the sign!)"}; }; constexpr const char PrefixTrackCleaner1[] = "TrackCleaner1"; diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index e499cd26381..51861002449 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -59,13 +59,13 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; o2::framework::Configurable> itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; o2::framework::Configurable> itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; - o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TForumal, where x=pt"}; - o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TForumal, where x=pt"}; + o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TFormula, where x=pt"}; + o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TFormula, where x=pt"}; o2::framework::Configurable> itsChi2Max{"itsChi2Max", {}, "Maximum ITS chi2 per cluster"}; o2::framework::Configurable> tpcChi2Max{"tpcChi2Max", {}, "Maximum TPC chi2 per cluster"}; // Electron PID cuts - o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make electron PID optional"}; o2::framework::Configurable minMomTofElectron{"minMomTofElectron", 0.3, "Minimum momentum to required TOF PID for Electron"}; o2::framework::Configurable> itsElectron{"itsElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Electron PID"}; o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Electron PID"}; @@ -74,7 +74,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID"}; // Pion PID cuts - o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make election PID optional"}; + o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make pion PID optional"}; o2::framework::Configurable minMomTofPion{"minMomTofPion", 0.5, "Minimum momentum to required TOF PID for Pion"}; o2::framework::Configurable> itsPion{"itsPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Pion PID"}; o2::framework::Configurable> tpcPion{"tpcPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Pion PID"}; @@ -83,7 +83,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID"}; // Kaon PID cuts - o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make election PID optional"}; + o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make kaon PID optional"}; o2::framework::Configurable minMomTofKaon{"minMomTofKaon", 0.4, "Minimum momentum to required TOF PID for Kaon"}; o2::framework::Configurable> itsKaon{"itsKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Kaon PID"}; o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Kaon PID"}; @@ -92,7 +92,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID"}; // Proton PID cuts - o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make election PID optional"}; + o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make proton PID optional"}; o2::framework::Configurable minMomTofProton{"minMomTofProton", 0.75, "Minimum momentum to required TOF PID for Proton"}; o2::framework::Configurable> itsProton{"itsProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Proton PID"}; o2::framework::Configurable> tpcProton{"tpcProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Proton PID"}; @@ -101,7 +101,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID"}; // Deuteron PID cuts - o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make deuteron PID optional"}; o2::framework::Configurable minMomTofDeuteron{"minMomTofDeuteron", 1.2, "Minimum momentum to required TOF PID for Deuteron"}; o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Deuteron PID"}; o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Deuteron PID"}; @@ -110,7 +110,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID"}; // Triton PID cuts - o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make election PID optional"}; + o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make triton PID optional"}; o2::framework::Configurable minMomTofTriton{"minMomTofTriton", 1.4, "Minimum momentum to required TOF PID for Triton"}; o2::framework::Configurable> itsTriton{"itsTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Triton PID"}; o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Triton PID"}; @@ -119,7 +119,7 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID"}; // Helium PID cuts - o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make election PID optional"}; + o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make helium PID optional"}; o2::framework::Configurable minMomTofHelium{"minMomTofHelium", 1.6, "Minimum momentum to required TOF PID for Helium"}; o2::framework::Configurable> itsHelium{"itsHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Helium PID"}; o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Helium PID"}; @@ -200,7 +200,7 @@ enum TrackSels { kTPCchi2Max, ///< Max. TPC chi2 per cluster /// track pid cuts - kItsElectron, ///< ITS Electon PID + kItsElectron, ///< ITS Electron PID kItsPion, ///< ITS Pion PID kItsKaon, ///< ITS Kaon PID kItsProton, ///< ITS Proton PID @@ -208,7 +208,7 @@ enum TrackSels { kItsTriton, ///< ITS Triton PID kItsHelium, ///< ITS He3 PID - kTpcElectron, ///< TPC Electon PID + kTpcElectron, ///< TPC Electron PID kTpcPion, ///< TPC Pion PID kTpcKaon, ///< TPC Kaon PID kTpcProton, ///< TPC Proton PID @@ -216,7 +216,7 @@ enum TrackSels { kTpcTriton, ///< TPC Triton PID kTpcHelium, ///< TPC He3 PID - kTofElectron, ///< TOF Electon PID + kTofElectron, ///< TOF Electron PID kTofPion, ///< TOF Pion PID kTofKaon, ///< TOF Kaon PID kTofProton, ///< TOF Proton PID @@ -224,7 +224,7 @@ enum TrackSels { kTofTriton, ///< TOF Triton PID kTofHelium, ///< TOF He3 PID - kTpcitsElectron, ///< TPC+ITS Electon PID + kTpcitsElectron, ///< TPC+ITS Electron PID kTpcitsPion, ///< TPC+ITS Pion PID kTpcitsKaon, ///< TPC+ITS Kaon PID kTpcitsProton, ///< TPC+ITS Proton PID @@ -232,7 +232,7 @@ enum TrackSels { kTpcitsTriton, ///< TPC+ITS Triton PID kTpcitsHelium, ///< TPC+ITS He3 PID - kTpctofElectron, ///< TPC+TOF Electon PID + kTpctofElectron, ///< TPC+TOF Electron PID kTpctofPion, ///< TPC+TOF Pion PID kTpctofKaon, ///< TPC+TOF Kaon PID kTpctofProton, ///< TPC+TOF Proton PID @@ -289,7 +289,7 @@ const std::unordered_map trackSelectionNames = { {kTpcitsProton, "TPC+ITS Proton PID"}, {kTpcitsDeuteron, "TPC+ITS Deuteron PID"}, {kTpcitsTriton, "TPC+ITS Triton PID"}, - {kTpcitsHelium, "TPC+ITS He PID"}, + {kTpcitsHelium, "TPC+ITS He3 PID"}, {kTpctofElectron, "TPC+TOF Electron PID"}, {kTpctofPion, "TPC+TOF Pion PID"}, @@ -812,7 +812,7 @@ class TrackBuilder { auto result = utils::getIndex(daughter.globalIndex(), indexMap); if (result) { - // daugher already in track table + // daughter already in track table return result.value(); } if (!this->template fillMcTrack(daughter, daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts)) { @@ -853,7 +853,7 @@ class TrackBuilder bool mProduceTritonPids = false; bool mProduceHeliumPids = false; - std::unordered_map indexMap; // for mapping tracks to daughers of lambdas, cascades and resonances ... + std::unordered_map indexMap; // for mapping tracks to daughters of lambdas, cascades and resonances ... }; struct TrackBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 1a4312a8195..36cae95349b 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -208,9 +208,9 @@ using ConfKinkChaDauBinning = ConfTrackBinning; using ConfResonancePosDauBinning = ConfTrackBinning; using ConfResonanceNegDauBinning = ConfTrackBinning; using ConfPionPlusBinning = ConfTrackBinning; -using ConfPionMinusBinning = ConfTrackBinning; +using ConfPionMinusBinning = ConfTrackBinning; using ConfKaonPlusBinning = ConfTrackBinning; -using ConfKaonMinusBinning = ConfTrackBinning; +using ConfKaonMinusBinning = ConfTrackBinning; template struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { @@ -338,9 +338,9 @@ constexpr std::array, kTrackHistLast> {kItsClusterIb, o2::framework::HistType::kTH1F, "hItsClusterIb", "ITS cluster in inner barrel; ITS IB cluster; Entries"}, {kTpcCrossedRows, o2::framework::HistType::kTH1F, "hTpcCrossedRows", "TPC crossed rows; TPC crossed rows; Entries"}, {kTpcCluster, o2::framework::HistType::kTH1F, "hTpcCluster", "TPC cluster found; TPC cluster found; Entries"}, - {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / Tpc crossed rows; Entries"}, - {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared ; Entries"}, - {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found ; Entries"}, + {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / TPC crossed rows; Entries"}, + {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared; Entries"}, + {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found; Entries"}, {kItsChi2, o2::framework::HistType::kTH1F, "hItsChi2", "ITS #chi^{2} / cluster; #chi^{2}_{ITS} / N_{cls}; Entries"}, {kTpcChi2, o2::framework::HistType::kTH1F, "hTpcChi2", "TPC #chi^{2} / cluster; #chi^{2}_{TPC} / N_{cls}; Entries"}, {kTrackType, o2::framework::HistType::kTH1F, "hTrackType", "Type the track row was stored as; Track type; Entries"}, @@ -387,7 +387,7 @@ constexpr std::array, kTrackHistLast> {kTofDeuteron, o2::framework::HistType::kTH2F, "hTofPidDeuteron", "TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TOF,de}"}, {kTofTriton, o2::framework::HistType::kTH2F, "hTofPidTriton", "TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TOF,tr}"}, {kTofHelium, o2::framework::HistType::kTH2F, "hTofPidHelium", "TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TOF,he}"}, - {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{ITS,el}^{2}}"}, + {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{its,el}^{2}}"}, {kTpcitsPion, o2::framework::HistType::kTH2F, "hTpcitsPidPion", "TPC+ITS PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{its,pi}^{2}}"}, {kTpcitsKaon, o2::framework::HistType::kTH2F, "hTpcitsPidKaon", "TPC+ITS PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{its,ka}^{2}}"}, {kTpcitsProton, o2::framework::HistType::kTH2F, "hTpcitsPidProton", "TPC+ITS PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{its,pr}^{2}}"}, @@ -414,9 +414,9 @@ constexpr std::array, kTrackHistLast> {kFromMaterial, o2::framework::HistType::kTHnSparseF, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, {kMissidentified, o2::framework::HistType::kTHnSparseF, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, {kSecondary1, o2::framework::HistType::kTHnSparseF, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, - {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, }}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -1125,7 +1125,7 @@ class TrackHistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index b11e38905d0..7743971f40f 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -11,7 +11,7 @@ /// \file tripletHistManager.h /// \brief histogram manager for triplet tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ @@ -147,12 +147,12 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { struct ConfTripletCuts : o2::framework::ConfigurableGroup { std::string prefix = std::string("TripletCuts"); - o2::framework::Configurable q3Max{"q3Max", -1, "Maximal kstar (set to -1 to deactivate)"}; - o2::framework::Configurable q3Min{"q3Min", -1, "Minimal kstar (set to -1 to deactivate)"}; + o2::framework::Configurable q3Max{"q3Max", -1, "Maximal Q3 (set to -1 to deactivate)"}; + o2::framework::Configurable q3Min{"q3Min", -1, "Minimal Q3 (set to -1 to deactivate)"}; o2::framework::Configurable mtMax{"mtMax", -1, "Maximal mt (set to -1 to deactivate)"}; o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; }; diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index 126f447b7d0..379ea697b0a 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -76,13 +76,13 @@ using ConfKstarFilters = ConfTwoTrackResonanceFilters; struct ConfPhiSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("PhiSelection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(333, 0.95f, 1.05f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; struct ConfRho0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Rho0Selection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(113, 0.7f, 0.84f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; struct ConfKstar0Selection : o2::framework::ConfigurableGroup { @@ -106,7 +106,7 @@ struct ConfTwoTrackResonanceTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("TwoTrackResonanceTables"); o2::framework::Configurable producePhis{"producePhis", -1, "Produce Phis (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producePhiMasks{"producePhiMasks", -1, "Produce PhiMasks (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce K0stars (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce Kstar0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceKstar0Masks{"produceKstar0Masks", -1, "Produce Kstar0Masks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0s{"produceRho0s", -1, "Produce Rho0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0Masks{"produceRho0Masks", -1, "Produce Rho0Masks (-1: auto; 0 off; 1 on)"}; diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index bc894a8600b..f2b8f46440e 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -51,13 +51,13 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { o2::framework::Configurable massMinLambda{"massMinLambda", 1.f, "Minimum mass for Lambda hypothesis"}; o2::framework::Configurable massMaxLambda{"massMaxLambda", 1.2f, "Maximum mass for Lambda hypothesis"}; o2::framework::Configurable rejectHypothesisK0short{"rejectHypothesisK0short", true, "Rejection of K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.475f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.515f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.475f, "Minimum mass to reject K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.515f, "Maximum mass to reject K0short hypothesis for Lambda candidates"}; o2::framework::Configurable massMinK0short{"massMinK0short", 0.45f, "Minimum mass for K0Short hypothesis"}; o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.53f, "Maximum mass for K0Short hypothesis"}; o2::framework::Configurable rejectHypothesisLambda{"rejectHypothesisLambda", true, "Rejection of Lambda hypothesis for K0short candidates"}; - o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to reject Lambda hypothesis for K0short candidates"}; + o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to reject Lambda hypothesis for K0short candidates"}; }; // selections bits for all v0s @@ -72,17 +72,17 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is an optional"}; \ - o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be ture for all limits if the daugher track has no TOF"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection. If false, TOF PID is optional"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; // derived selection bits for lambda struct ConfLambdaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("LambdaBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; - o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC negative for daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for negative daughter tracks"}; o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; o2::framework::Configurable> posDauTofProton{"posDauTofProton", {}, "Maximum |nsigma_Proton| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; @@ -93,8 +93,8 @@ struct ConfLambdaBits : o2::framework::ConfigurableGroup { struct ConfK0shortBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("K0shortBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; }; @@ -128,7 +128,7 @@ template struct ConfK0shortSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; V0_DEFAULT_SELECTIONS(0.47, 0.51, 310) - o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatability with Lambda selection"}; + o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatibility with Lambda selection"}; }; #undef V0_DEFAULT_SELECTIONS diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 331d426893a..6301c1e8880 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -137,12 +137,12 @@ struct ConfV0QaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; - o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Poiting angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{100, 0, 100}}, "Decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for particle hypothesis"}; o2::framework::ConfigurableAxis massAntiLambda{"massAntiLambda", {{100, 1, 1.2}}, "mass for antiparticle hypothesis"}; o2::framework::ConfigurableAxis massK0short{"massK0short", {{200, 0.45, 0.55}}, "Mass for k0short hypothesis"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for daughters"}; @@ -181,8 +181,8 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of poiting angle;p_{T} (GeV/#it{c}); cos(#alpha);"}, - {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Poiting angle;p_{T} (GeV/#it{c}); #alpha"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of pointing angle;p_{T} (GeV/#it{c}); cos(#alpha);"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle;p_{T} (GeV/#it{c}); #alpha"}, {kPtVsLambdaMass, o2::framework::HistType::kTH2F, "hPtVsLambdaMass", "p_{T} vs #Lambda mass; p_{T} (GeV/#it{c}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, {kPtVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hPtVsAntiLambdaMass", "p_{T} vs #bar{#Lambda} mass; p_{T} (GeV/#it{c}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kPtVsK0shortMass, o2::framework::HistType::kTH2F, "hPtVsK0shortMass", "p_{T} vs K^{0}_{S} mass; p_{T} (GeV/#it{c}); m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2})"}, @@ -739,7 +739,7 @@ class V0HistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 6fde51df1f2..e10eb8a366c 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -228,24 +228,25 @@ struct FemtoProducer { if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && - !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesRun3ppMc && + !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc)) { LOG(fatal) << "At least one cascade table is enabled, but wrong process function is enabled. Breaking..."; } if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && - !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && + !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPb && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } - if (photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb)) { + if (photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && !doprocessTracksV0sCascadesKinksPhotonsRun3pp)) { LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; } if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && - (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && + (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one kink table is enabled, but wrong process function is enabled. Breaking..."; } @@ -346,6 +347,7 @@ struct FemtoProducer { static_cast(doprocessTracksV0sCascadesRun3PbPbWithEventShape) + static_cast(doprocessTracksKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksRun3pp) + + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + static_cast(doprocessTracksD0sRun3pp) + static_cast(doprocessTracksD0sRun3PbPb) + static_cast(doprocessTracksRun3ppMc) + @@ -701,6 +703,31 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksRun3pp, "Provide tracks, v0s, cascades and kinks", false); + void processTracksV0sCascadesKinksPhotonsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + rawinputs::Run3Kinks const& kinks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + processV0s(col, tracks, v0s); + processCascades(col, tracks, cascades); + processKinks(col, tracks, kinks); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksPhotonsRun3pp, "Provide tracks, v0s, cascades, kinks and PCM photons", false); + void processTracksD0sRun3pp(rawinputs::Run3PpCollisions::iterator const& col, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, From e97bbebb820ec10bbffa8cced75ee97cc5d6a581 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 6 Oct 2026 22:28:40 +0200 Subject: [PATCH 199/648] [PWGLF] Add K0s to the He3 correlation analysis (#18192) --- PWGLF/DataModel/LFSlimHeLambda.h | 19 +++ .../Nuspex/he3LambdaAnalysis.cxx | 112 ++++++++++++++---- .../Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx | 92 +++++++++++++- 3 files changed, 197 insertions(+), 26 deletions(-) diff --git a/PWGLF/DataModel/LFSlimHeLambda.h b/PWGLF/DataModel/LFSlimHeLambda.h index 6b3ae954cd9..a0737effbbb 100644 --- a/PWGLF/DataModel/LFSlimHeLambda.h +++ b/PWGLF/DataModel/LFSlimHeLambda.h @@ -54,12 +54,18 @@ DECLARE_SOA_COLUMN(DCAPVProton, dcaPVProton, float); DECLARE_SOA_COLUMN(DCAPVPion, dcaPVPion, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(Ct, ct, float); +DECLARE_SOA_COLUMN(DCAPVPos, dcaPVPos, float); +DECLARE_SOA_COLUMN(DCAPVNeg, dcaPVNeg, float); +DECLARE_SOA_COLUMN(NsigmaTPCPosPion, nSigmaTPCPosPion, float); +DECLARE_SOA_COLUMN(NsigmaTPCNegPion, nSigmaTPCNegPion, float); } // namespace lfv0he3 DECLARE_SOA_TABLE_VERSIONED(LFHe3_000, "AOD", "LFHE3V0", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_000, "AOD", "LFLAMBDA", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFHe3_001, "AOD", "LFHE3V0", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::TPCnClsPID, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_001, "AOD", "LFLAMBDA", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::NsigmaTPCProton, lfv0he3::NsigmaTPCPion, lfv0he3::Sign); +DECLARE_SOA_TABLE(LFK0s, "AOD", "LFK0S", lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::Ct, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVPos, lfv0he3::DCAPVNeg, lfv0he3::V0Radius, lfv0he3::NsigmaTPCPosPion, lfv0he3::NsigmaTPCNegPion); } // namespace o2::aod struct he3Candidate { @@ -87,4 +93,17 @@ struct lambdaCandidate { int8_t sign = 0; // Charge sign of the Lambda candidate }; +struct k0sCandidate { + ROOT::Math::LorentzVector> momentum; + float mass = -1.f; + float ct = -1.f; // Proper decay length in cm + float cosPA = -2.f; + float dcaV0Daughters = -999.f; + float dcaPosToPV = -999.f; + float dcaNegToPV = -999.f; + float v0Radius = -1.f; + float posPionNSigmaTPC = -999.f; + float negPionNSigmaTPC = -999.f; +}; + #endif // PWGLF_DATAMODEL_LFSLIMHELAMBDA_H_ diff --git a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx index 01411e4e566..293733ad9c5 100644 --- a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx +++ b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx @@ -56,7 +56,7 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; namespace { -constexpr double betheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; +constexpr std::array betheBlochDefault[1]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; static const std::vector particleName{"He3"}; o2::base::MatLayerCylSet* matLUT = nullptr; @@ -101,6 +101,9 @@ struct he3LambdaAnalysis { Produces lfHe3V0Collision; Produces lfHe3; Produces lfLambda; + Produces lfK0s; + + Configurable cfgStoreK0s{"cfgStoreK0s", false, "Select and store K0s candidates, including He3-K0s events without a selected Lambda"}; // Configurables for event selection struct : ConfigurableGroup { @@ -122,7 +125,7 @@ struct he3LambdaAnalysis { Configurable dcazMax{"dcazMax", 0.5f, "Maximum He3 DCA z"}; Configurable tpcClusMin{"tpcClusMin", 100, "Minimum He3 TPC clusters"}; Configurable itsClusMin{"itsClusMin", 5, "Minimum He3 ITS clusters"}; - Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0], 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; + Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0].data(), 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; } cfgHe3; // Lambda selection criteria @@ -131,6 +134,8 @@ struct he3LambdaAnalysis { Configurable ptMin{"ptMin", 0.5f, "Minimum Lambda pT"}; Configurable ptMax{"ptMax", 10.0f, "Maximum Lambda pT"}; Configurable massWindow{"massWindow", 0.015f, "Lambda mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum Lambda proper decay length (cm)"}; + Configurable ctMax{"ctMax", 30.f, "Maximum Lambda proper decay length (cm)"}; Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum Lambda cosPA"}; Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum Lambda DCA V0 daughters"}; Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum Lambda V0 radius"}; @@ -140,6 +145,22 @@ struct he3LambdaAnalysis { Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.0f, "Maximum pion TPC nSigma"}; } cfgLambda; + // K0s selection criteria + struct : ConfigurableGroup { + std::string prefix = "cfgK0s"; + Configurable ptMin{"ptMin", 0.5f, "Minimum K0s pT"}; + Configurable ptMax{"ptMax", 10.f, "Maximum K0s pT"}; + Configurable massWindow{"massWindow", 0.015f, "K0s mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable ctMax{"ctMax", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum K0s cosPA"}; + Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum K0s DCA V0 daughters"}; + Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum K0s V0 radius"}; + Configurable v0RadiusMax{"v0RadiusMax", 35.f, "Maximum K0s V0 radius"}; + Configurable tpcNClsMin{"tpcNClsMin", 70, "Minimum TPC clusters for K0s daughters"}; + Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.f, "Maximum pion TPC nSigma"}; + } cfgK0s; + // Pair selection criteria struct : ConfigurableGroup { std::string prefix = "cfgPair"; @@ -156,7 +177,7 @@ struct he3LambdaAnalysis { Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; } ccdbOptions; - std::array mBBparamsHe; + std::array mBBparamsHe = betheBlochDefault[0]; float mBz = 0.0f; // Magnetic field in T HistogramRegistry mRegistry{"He3LambdaAnalysis"}; int mRunNumber = 0; // Current run number @@ -186,7 +207,7 @@ struct he3LambdaAnalysis { zorroSummary.setObject(zorro.getZorroSummary()); mRegistry.add("hEventSelection", "Event Selection", {HistType::kTH1L, {{6, -.5, 5.5}}}); - std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and Lambda Candidates Found"}; + std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and V0 Candidates Found"}; for (size_t i = 1; i <= labels.size(); ++i) { mRegistry.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, labels[i - 1].c_str()); } @@ -203,6 +224,8 @@ struct he3LambdaAnalysis { hArmenterosPodolanskiAll = mRegistry.add("hArmenterosPodolanskiAll", "Armenteros-Podolanski All", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); hArmenterosPodolanskiSelected = mRegistry.add("hArmenterosPodolanskiSelected", "Armenteros-Podolanski Selected", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); + mRegistry.add("hMassK0s", "Selected K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2F, {{100, 0., 10.}, {100, MassK0Short - 0.025, MassK0Short + 0.025}}}); + constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; hInvariantMassUS = mRegistry.add("hInvariantMassUS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); hInvariantMassLS = mRegistry.add("hInvariantMassLS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); @@ -298,8 +321,9 @@ struct he3LambdaAnalysis { } mRegistry.get(HIST("hEventSelection"))->Fill(4); // He3 candidates found - // Process Lambda candidates + // Fit each V0 once and evaluate the K0s and Lambda hypotheses independently. std::vector lambdaCandidates; + std::vector k0sCandidates; for (auto const& v0 : v0s) { if (v0.v0Type() != 1) { continue; @@ -307,7 +331,9 @@ struct he3LambdaAnalysis { const auto posTrack = v0.posTrack_as(); const auto negTrack = v0.negTrack_as(); - if (posTrack.tpcNClsFound() < cfgLambda.tpcNClsMin || negTrack.tpcNClsFound() < cfgLambda.tpcNClsMin) { + const bool lambdaTrackQuality = posTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin && negTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin; + const bool k0sTrackQuality = cfgStoreK0s && posTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin && negTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin; + if (!lambdaTrackQuality && !k0sTrackQuality) { continue; // Skip V0s with insufficient TPC clusters } auto trackParPos = getTrackParCov(posTrack); @@ -332,6 +358,49 @@ struct he3LambdaAnalysis { float qt = qtAP(momV0, momPos); hArmenterosPodolanskiAll->Fill(alpha, qt); + const auto sv = fitter.getPCACandidate(0); + const float decayLength = std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()); + const float momentum = std::hypot(momV0[0], momV0[1], momV0[2]); + if (momentum <= 0.f || decayLength <= 0.f) { + continue; + } + const float cosPA = ((sv[0] - collVtx.x()) * momV0[0] + (sv[1] - collVtx.y()) * momV0[1] + (sv[2] - collVtx.z()) * momV0[2]) / (decayLength * momentum); + const float dcaDaughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + const float radius = std::hypot(sv[0], sv[1]); + std::array dcaInfoPos{}, dcaInfoNeg{}; + if (!o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPos) || + !o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoNeg)) { + continue; + } + + ROOT::Math::LorentzVector> posPionMom4D(momPos[0], momPos[1], momPos[2], MassPionCharged); + ROOT::Math::LorentzVector> negPionMom4D(momNeg[0], momNeg[1], momNeg[2], MassPionCharged); + const auto k0sMom4D = posPionMom4D + negPionMom4D; + const float ctK0s = decayLength * MassK0Short / momentum; + if (k0sTrackQuality && std::abs(posTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && std::abs(negTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && + std::abs(k0sMom4D.M() - MassK0Short) <= cfgK0s.massWindow && k0sMom4D.Pt() >= cfgK0s.ptMin && k0sMom4D.Pt() <= cfgK0s.ptMax && + ctK0s >= cfgK0s.ctMin && ctK0s <= cfgK0s.ctMax && cosPA >= cfgK0s.cosPAMin && dcaDaughters <= cfgK0s.dcaV0DaughtersMax && + radius >= cfgK0s.v0RadiusMin && radius <= cfgK0s.v0RadiusMax) { + k0sCandidate candidate; + candidate.momentum.SetCoordinates(k0sMom4D.Pt(), k0sMom4D.Eta(), k0sMom4D.Phi(), MassK0Short); + candidate.mass = k0sMom4D.M(); + candidate.ct = ctK0s; + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; + candidate.dcaPosToPV = std::hypot(dcaInfoPos[0], dcaInfoPos[1]); + candidate.dcaNegToPV = std::hypot(dcaInfoNeg[0], dcaInfoNeg[1]); + candidate.v0Radius = radius; + candidate.posPionNSigmaTPC = posTrack.tpcNSigmaPi(); + candidate.negPionNSigmaTPC = negTrack.tpcNSigmaPi(); + k0sCandidates.push_back(candidate); + mRegistry.fill(HIST("hMassK0s"), k0sMom4D.Pt(), candidate.mass); + } + + const float ctLambda = decayLength * MassLambda0 / momentum; + if (!lambdaTrackQuality || ctLambda < cfgLambda.ctMin || ctLambda > cfgLambda.ctMax || cosPA < cfgLambda.cosPAMin || + dcaDaughters > cfgLambda.dcaV0DaughtersMax || radius < cfgLambda.v0RadiusMin || radius > cfgLambda.v0RadiusMax) { + continue; + } bool matter = alpha > 0; const auto& protonTrack = matter ? posTrack : negTrack; const auto& pionTrack = matter ? negTrack : posTrack; @@ -347,37 +416,30 @@ struct he3LambdaAnalysis { auto lambdaMom4D = protonMom4D + pionMom4D; float massLambda = lambdaMom4D.M(); - if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow) { + if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow || lambdaMom4D.Pt() < cfgLambda.ptMin || lambdaMom4D.Pt() > cfgLambda.ptMax) { continue; // Skip V0s outside mass window } hArmenterosPodolanskiSelected->Fill(alpha, qt); - std::array dcaInfoProton, dcaInfoPion; - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParPos : trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoProton); - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParNeg : trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPion); - - const auto sv = fitter.getPCACandidate(0); - + const auto& dcaInfoProton = matter ? dcaInfoPos : dcaInfoNeg; + const auto& dcaInfoPion = matter ? dcaInfoNeg : dcaInfoPos; lambdaCandidate candidate; - candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), o2::constants::physics::MassLambda0); + candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), MassLambda0); candidate.mass = massLambda; - candidate.cosPA = (sv[0] - collVtx.x()) * lambdaMom4D.Px() + - (sv[1] - collVtx.y()) * lambdaMom4D.Py() + - (sv[2] - collVtx.z()) * lambdaMom4D.Pz(); - candidate.cosPA /= std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()) * lambdaMom4D.P(); - candidate.dcaV0Daughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; candidate.dcaProtonToPV = std::hypot(dcaInfoProton[0], dcaInfoProton[1]); candidate.dcaPionToPV = std::hypot(dcaInfoPion[0], dcaInfoPion[1]); - candidate.v0Radius = std::hypot(sv[0], sv[1]); + candidate.v0Radius = radius; candidate.protonNSigmaTPC = protonTrack.tpcNSigmaPr(); candidate.pionNSigmaTPC = pionTrack.tpcNSigmaPi(); candidate.sign = matter ? 1 : -1; // Positive sign for Lambda, negative for anti-Lambda lambdaCandidates.push_back(candidate); } - if (lambdaCandidates.empty()) { - return; // No valid Lambda candidates found + if (lambdaCandidates.empty() && (!cfgStoreK0s || k0sCandidates.empty())) { + return; // Require a Lambda, or a K0s when cfgStoreK0s is enabled } - mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and Lambda candidates found + mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and V0 candidates found mRegistry.get(HIST("hCentralitySelected"))->Fill(collision.centFT0C()); // Fill output tables @@ -386,6 +448,10 @@ struct he3LambdaAnalysis { lfHe3(lfHe3V0Collision.lastIndex(), he3.momentum.Pt(), he3.momentum.Eta(), he3.momentum.Phi(), he3.dcaXY, he3.dcaZ, he3.tpcNClsFound, he3.tpcNClsPID, he3.itsClusterSizes, he3.nSigmaTPC, he3.sign); } + for (const auto& k0s : k0sCandidates) { + lfK0s(lfHe3V0Collision.lastIndex(), k0s.momentum.Pt(), k0s.momentum.Eta(), k0s.momentum.Phi(), + k0s.mass, k0s.ct, k0s.cosPA, k0s.dcaV0Daughters, k0s.dcaPosToPV, k0s.dcaNegToPV, k0s.v0Radius, k0s.posPionNSigmaTPC, k0s.negPionNSigmaTPC); + } for (const auto& lambda : lambdaCandidates) { lfLambda(lfHe3V0Collision.lastIndex(), lambda.momentum.Pt(), lambda.momentum.Eta(), lambda.momentum.Phi(), lambda.mass, lambda.cosPA, lambda.dcaV0Daughters, lambda.dcaProtonToPV, lambda.dcaPionToPV, lambda.v0Radius, lambda.protonNSigmaTPC, lambda.pionNSigmaTPC, lambda.sign); diff --git a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx index afe3814afd9..52b0e7cc552 100644 --- a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,11 @@ std::shared_ptr hInvariantMassLambda[2]; std::shared_ptr hCosPALambda; std::shared_ptr hNsigmaHe3; std::shared_ptr hNsigmaProton; +std::shared_ptr hInvariantMassHe3K0s[2]; // 0: anti-He3, 1: He3 +std::shared_ptr hRotationInvariantMassHe3K0s[2]; +std::shared_ptr hInvariantMassK0s[2]; // Before and after selection +std::shared_ptr hCosPAK0s; +std::shared_ptr hCtK0s; }; // namespace using namespace o2; @@ -59,6 +65,15 @@ struct he3LambdaDerivedAnalysis { Configurable cfgMinLambdaPt{"cfgMinLambdaPt", 0.4, "Minimum pT for Lambda candidates"}; Configurable cfgMaxLambdaDeltaM{"cfgMaxLambdaDeltaM", 10.0e-3, "Maximum deltaM for Lambda candidates"}; + Configurable cfgMinK0sPt{"cfgMinK0sPt", 0.5f, "Minimum K0s pT"}; + Configurable cfgMaxK0sPt{"cfgMaxK0sPt", 10.f, "Maximum K0s pT"}; + Configurable cfgMinK0sCt{"cfgMinK0sCt", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable cfgMaxK0sCt{"cfgMaxK0sCt", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cfgMinK0sCosPA{"cfgMinK0sCosPA", 0.99f, "Minimum K0s cosPA"}; + Configurable cfgMaxK0sDeltaM{"cfgMaxK0sDeltaM", 10.e-3f, "Maximum K0s mass deviation (GeV/c^2)"}; + Configurable cfgMaxNSigmaTPCPionK0s{"cfgMaxNSigmaTPCPionK0s", 4.f, "Maximum daughter pion TPC nSigma for K0s"}; + ConfigurableAxis cfgHe3K0sMassAxis{"cfgHe3K0sMassAxis", {200, MassHelium3 + MassK0Short, MassHelium3 + MassK0Short + 1.}, "He3-K0s pair invariant mass (GeV/c^2)"}; + void init(InitContext const&) { constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; @@ -70,12 +85,26 @@ struct he3LambdaDerivedAnalysis { hInvariantMassLambda[i] = mRegistry.add(Form("hInvariantMassLambda%i", i), "Invariant Mass Lambda", {HistType::kTH2D, {{50, 0., 10.}, {30, o2::constants::physics::MassLambda0 - 0.015, o2::constants::physics::MassLambda0 + 0.015}}}); hRotationInvariantMassAntiLSeta[i] = mRegistry.add(Form("hRotationInvariantMassAntiLSeta%i", i), "Rotation Invariant Mass Anti-Lambda", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); } + if (doprocessSameEvent && doprocessSameEventLegacy) { + LOG(fatal) << "Enable only one Lambda table version: processSameEvent or processSameEventLegacy"; + } + if (doprocessK0s) { + const AxisSpec pairMassAxis{cfgHe3K0sMassAxis, "m(He3 K0s) (GeV/c^{2})"}; + for (int i = 0; i < 2; ++i) { + hInvariantMassHe3K0s[i] = mRegistry.add(Form("hInvariantMassHe3K0s%i", i), "Same-event He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hRotationInvariantMassHe3K0s[i] = mRegistry.add(Form("hRotationInvariantMassHe3K0s%i", i), "Rotated He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hInvariantMassK0s[i] = mRegistry.add(Form("hInvariantMassK0s%i", i), "K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2D, {{50, 0., 10.}, {100, MassK0Short - 0.015, MassK0Short + 0.015}}}); + } + hCosPAK0s = mRegistry.add("hCosPAK0s", "K0s;pT (GeV/c);cosPA", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); + hCtK0s = mRegistry.add("hCtK0s", "K0s;pT (GeV/c);ct (cm)", {HistType::kTH2D, {{50, 0., 10.}, {100, 0., 30.}}}); + } hCosPALambda = mRegistry.add("hCosPALambda", "Cosine of Pointing Angle for Lambda", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); hNsigmaHe3 = mRegistry.add("hNsigmaHe3", "nSigma TPC for He3", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); hNsigmaProton = mRegistry.add("hNsigmaProton", "nSigma TPC for Proton", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); } - void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + template + void analyseLambda(o2::aod::LFEvents::iterator const& collision, He3Table const& he3s, LambdaTable const& lambdas) { std::vector he3Candidates; he3Candidates.reserve(he3s.size()); @@ -111,6 +140,7 @@ struct he3LambdaDerivedAnalysis { candidate.mass = lambda.mass(); candidate.cosPA = lambda.cosPA(); candidate.dcaV0Daughters = lambda.dcaDaughters(); + candidate.sign = lambda.sign(); hCosPALambda->Fill(lambda.pt(), candidate.cosPA); // hNsigmaProton->Fill(lambda.pt() * lambda.sign(), lambda.protonNSigmaTPC()); hInvariantMassLambda[0]->Fill(lambda.pt(), lambda.mass()); @@ -128,7 +158,7 @@ struct he3LambdaDerivedAnalysis { (he3.sign * lambda.sign > 0 ? hInvariantMassLS : hInvariantMassUS)[he3.sign > 0]->Fill(pairMomentum.Pt(), pairMomentum.M()); } for (int iEta{0}; iEta <= cfgMirrorEta; ++iEta) { - for (int iR{0}; iR <= cfgNrotations; ++iR) { + for (int iR{0}; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { auto he3Momentum = ROOT::Math::LorentzVector>(he3.momentum.Pt(), (1. - iEta * 2.) * he3.momentum.Eta(), he3.momentum.Phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), he3.momentum.M()); for (const auto& lambda : lambdaCandidates) { auto pairMomentum = lambda.momentum + he3Momentum; // Calculate invariant mass @@ -141,7 +171,63 @@ struct he3LambdaDerivedAnalysis { } } } - PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same event", true); + void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_001 const& he3s, o2::aod::LFLambda_001 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same-event He3-Lambda pairs (version 001)", true); + + void processSameEventLegacy(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEventLegacy, "Process same-event He3-Lambda pairs (version 000)", false); + + void processK0s(o2::aod::LFEvents::iterator const&, o2::aod::LFHe3_001 const& he3s, o2::aod::LFK0s const& k0ss) + { + std::vector k0sCandidates; + k0sCandidates.reserve(k0ss.size()); + for (const auto& k0s : k0ss) { + hInvariantMassK0s[0]->Fill(k0s.pt(), k0s.mass()); + hCosPAK0s->Fill(k0s.pt(), k0s.cosPA()); + hCtK0s->Fill(k0s.pt(), k0s.ct()); + if (k0s.pt() < cfgMinK0sPt || k0s.pt() > cfgMaxK0sPt || k0s.ct() < cfgMinK0sCt || k0s.ct() > cfgMaxK0sCt || + k0s.cosPA() < cfgMinK0sCosPA || std::abs(k0s.mass() - MassK0Short) > cfgMaxK0sDeltaM || + std::abs(k0s.nSigmaTPCPosPion()) > cfgMaxNSigmaTPCPionK0s || std::abs(k0s.nSigmaTPCNegPion()) > cfgMaxNSigmaTPCPionK0s) { + continue; + } + k0sCandidate candidate; + // Use the nominal K0s mass for pair kinematics, as for Lambda. + candidate.momentum.SetCoordinates(k0s.pt(), k0s.eta(), k0s.phi(), MassK0Short); + k0sCandidates.push_back(candidate); + hInvariantMassK0s[1]->Fill(k0s.pt(), k0s.mass()); + } + for (const auto& he3 : he3s) { + if (!doprocessSameEvent && !doprocessSameEventLegacy) { + hNsigmaHe3->Fill(he3.pt() * he3.sign(), he3.nSigmaTPC()); + } + if (std::abs(he3.nSigmaTPC()) > cfgMaxNSigmaTPCHe3) { + continue; + } + const ROOT::Math::LorentzVector> he3Momentum(he3.pt(), he3.eta(), he3.phi(), MassHelium3); + const int signIndex = he3.sign() > 0; + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = he3Momentum + k0s.momentum; + hInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + // Match the Lambda background's rotation angles and optional eta mirroring. + for (int iEta = 0; iEta <= cfgMirrorEta; ++iEta) { + for (int iR = 0; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { + const ROOT::Math::LorentzVector> rotatedHe3(he3.pt(), (1. - iEta * 2.) * he3.eta(), he3.phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), MassHelium3); + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = rotatedHe3 + k0s.momentum; + hRotationInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + } + } + } + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processK0s, "Process same-event He3-K0s pairs (version 001)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 2e196f929650bb22c012b8dafe9e5c20b1cf3bbb Mon Sep 17 00:00:00 2001 From: nepeivodaRS <94179174+nepeivodaRS@users.noreply.github.com> Date: Tue, 6 Oct 2026 23:05:09 +0200 Subject: [PATCH 200/648] [PWGLF] Fix derived UPC analysis and add Lambda feeddown histograms (#18181) Co-authored-by: ALICE Action Bot --- .../Tasks/Strangeness/cascpostprocessing.cxx | 26 +- .../Tasks/Strangeness/derivedupcanalysis.cxx | 515 +++++++++++------- 2 files changed, 329 insertions(+), 212 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx index 2b6c5d094c3..00bb3a69e10 100644 --- a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx +++ b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx @@ -271,11 +271,17 @@ struct LfCascpostprocessing { registry.add("hTPCNSigmaBachPi", "hTPCNSigmaBachPi", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTPCNSigmaBachKa", "hTPCNSigmaBachKa", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPi", "hTOFNSigmaPosPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPiVsPt", "hTOFNSigmaPosPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPi", "hTOFNSigmaNegPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPiVsPt", "hTOFNSigmaNegPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPr", "hTOFNSigmaPosPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPrVsPt", "hTOFNSigmaPosPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPr", "hTOFNSigmaNegPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPrVsPt", "hTOFNSigmaNegPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachPi", "hTOFNSigmaBachPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachPiVsPt", "hTOFNSigmaBachPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachKa", "hTOFNSigmaBachKa", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachKaVsPt", "hTOFNSigmaBachKaVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hCascMinusEtaPos", "hCascMinusEtaPos", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaNeg", "hCascMinusEtaNeg", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaBach", "hCascMinusEtaBach", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); @@ -550,7 +556,7 @@ struct LfCascpostprocessing { if (TMath::Abs(candidate.massxi() - o2::constants::physics::MassXiMinus) < masswintpc) { isCandidate = 1; } - } else if (!isXi) { + } else { if (isMC) { isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == PDG_t::kOmegaMinus) && (candidate.isPrimary() == 1)) ? 1 : 0; } @@ -563,7 +569,13 @@ struct LfCascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPr"), candidate.pt(), candidate.ntpcsigmapospr()); registry.fill(HIST("hTPCNSigmaNegPi"), candidate.pt(), candidate.ntpcsigmanegpi()); registry.fill(HIST("hTOFNSigmaPosPr"), candidate.ntofsigmapospr()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPrVsPt"), candidate.pt(), candidate.ntofsigmapospr()); + } registry.fill(HIST("hTOFNSigmaNegPi"), candidate.ntofsigmanegpi()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPiVsPt"), candidate.pt(), candidate.ntofsigmanegpi()); + } registry.fill(HIST("hCascMinusEtaPos"), candidate.poseta()); registry.fill(HIST("hCascMinusEtaNeg"), candidate.negeta()); registry.fill(HIST("hCascMinusEtaBach"), candidate.bacheta()); @@ -571,14 +583,26 @@ struct LfCascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPi"), candidate.pt(), candidate.ntpcsigmapospi()); registry.fill(HIST("hTPCNSigmaNegPr"), candidate.pt(), candidate.ntpcsigmanegpr()); registry.fill(HIST("hTOFNSigmaPosPi"), candidate.ntofsigmapospi()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPiVsPt"), candidate.pt(), candidate.ntofsigmapospi()); + } registry.fill(HIST("hTOFNSigmaNegPr"), candidate.ntofsigmanegpr()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPrVsPt"), candidate.pt(), candidate.ntofsigmanegpr()); + } } if (isXi) { registry.fill(HIST("hTPCNSigmaBachPi"), candidate.pt(), candidate.ntpcsigmabachpi()); registry.fill(HIST("hTOFNSigmaBachPi"), candidate.ntofsigmabachpi()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachPiVsPt"), candidate.pt(), candidate.ntofsigmabachpi()); + } } else { registry.fill(HIST("hTPCNSigmaBachKa"), candidate.pt(), candidate.ntpcsigmabachka()); registry.fill(HIST("hTOFNSigmaBachKa"), candidate.ntofsigmabachka()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachKaVsPt"), candidate.pt(), candidate.ntofsigmabachka()); + } } } // registry.fill(HIST("hPosITSHits"), candidate.positshits()); diff --git a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx index 1741b68afa0..5470d63e14a 100644 --- a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -93,7 +95,7 @@ struct Derivedupcanalysis { Configurable analyseOmega{"analyseOmega", true, "process Omega-like candidates"}; Configurable analyseAntiOmega{"analyseAntiOmega", true, "process AntiOmega-like candidates"}; - Configurable> generatorIds{"generatorIds", std::vector{-1}, "MC generatorIds to process"}; + Configurable> generatorIds{"generatorIds", std::vector{-1}, "generator IDs for candidate truth origins and generated particles; event QA uses vertex labels"}; // Event selections struct : ConfigurableGroup { @@ -177,6 +179,7 @@ struct Derivedupcanalysis { static constexpr int kOmegaPartID = 5; static constexpr int kAntiOmegaPartID = 6; static constexpr float kNoPidNsigmaCut = 1e5f; + static constexpr float kNoTofDeltaTimeCut = 1e9f; static constexpr float kDefaultInvalidRapidity = 1e6f; static constexpr float kDefaultInvalidCtau = 1e6f; static constexpr float kDisabledArmenterosCut = 1e-4f; @@ -258,7 +261,7 @@ struct Derivedupcanalysis { } axisDetectors; // for MC - Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + Configurable doMCAssociation{"doMCAssociation", true, "identify candidate species and generator from MC truth; if disabled, select events by vertex generator"}; Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; ConfigurableAxis axisGeneratorIds{"axisGeneratorIds", {256, -0.5f, 255.5f}, "axis for generatorIds"}; @@ -348,144 +351,154 @@ struct Derivedupcanalysis { } } + void setTOFSelection(std::bitset& mask, float nsigmaCut, float deltaTimeCut, int nsigmaSelection, int deltaTimeSelection) + { + if (nsigmaCut < kNoPidNsigmaCut) + mask.set(nsigmaSelection); + if (deltaTimeCut < kNoTofDeltaTimeCut) + mask.set(deltaTimeSelection); + } + template - void addTopoHistograms(HistogramRegistry& histos) + void addTopoHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - if (isCascade) { - histos.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); - histos.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); - histos.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); - histos.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); - histos.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + // Template instantiations intentionally make this particle-type comparison constant. + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); + registry.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); + registry.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); + registry.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); + registry.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); if (doBachelorBaryonCut) { - histos.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); - histos.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); + registry.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); + registry.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); } } else { - histos.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); - histos.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); - histos.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); - histos.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + registry.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); + registry.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); + registry.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); + registry.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); } } template - void addTPCQAHistograms(HistogramRegistry& histos) + void addTPCQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - if (isCascade) { - histos.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); } } template - void addTOFQAHistograms(HistogramRegistry& histos) + void addTOFQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - if (isCascade) { - histos.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); } } template - void addKinematicQAHistograms(HistogramRegistry& histos) + void addKinematicQAHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; - histos.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); - if (isCascade) { - histos.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); } } template - void addDetectorPropHistograms(HistogramRegistry& histos) + void addDetectorPropHistograms(HistogramRegistry& registry) { - const bool isCascade = partID >= kFirstCascadePartID; if (doDetectPropQA == kDetectPropQABase) { - if (isCascade) { - histos.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } else { - histos.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + registry.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } - histos.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); } if (doDetectPropQA == kDetectPropQAWithMass) { - if (isCascade) { - histos.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } else { - histos.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } - histos.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); } } template - void addHistograms(HistogramRegistry& histos) + void addHistograms(HistogramRegistry& registry) { - histos.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); - histos.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); + registry.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); + registry.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); if (doPlainTopoQA) { - addTopoHistograms(histos); + addTopoHistograms(registry); } if (PIDConfigurations.doTPCQA) { - addTPCQAHistograms(histos); + addTPCQAHistograms(registry); } if (PIDConfigurations.doTOFQA) { - addTOFQAHistograms(histos); + addTOFQAHistograms(registry); } if (doKienmaticQA) { - addKinematicQAHistograms(histos); + addKinematicQAHistograms(registry); } - addDetectorPropHistograms(histos); + addDetectorPropHistograms(registry); } template @@ -521,7 +534,8 @@ struct Derivedupcanalysis { int negITSclusMap = computeITSclusBitmap(negTrackExtra.itsClusterMap(), negIsFromAfterburner); if (partID == kK0ShortPartID) { - histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); + if (doPlainTopoQA) + histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); invMass = cand.mK0Short(); rapidity = cand.yK0Short(); ctau = cand.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassK0Short; @@ -596,8 +610,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), negTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosTPCsignalVsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcSignal()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), tpcNsigmaPos); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPtot"), ft0ampl, cand.positivept() * std::cosh(cand.positiveeta()), tpcNsigmaPos); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPC"), ft0ampl, pT, tpcNsigmaPos); @@ -657,7 +671,7 @@ struct Derivedupcanalysis { if (partID == kXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / (totalMom * ctauxiPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -672,7 +686,7 @@ struct Derivedupcanalysis { } } else if (partID == kAntiXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / (totalMom * ctauxiPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -688,7 +702,7 @@ struct Derivedupcanalysis { } else if (partID == kOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / (totalMom * ctauomegaPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -697,14 +711,14 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPi(); - tofDeltaTNeg = cand.posTOFDeltaTOmPr(); + tofDeltaTPos = cand.posTOFDeltaTOmPr(); + tofDeltaTNeg = cand.negTOFDeltaTOmPi(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } } else if (partID == kAntiOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / (totalMom * ctauomegaPDG) : kDefaultInvalidCtau; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -713,8 +727,8 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPr(); - tofDeltaTNeg = cand.posTOFDeltaTOmPi(); + tofDeltaTPos = cand.posTOFDeltaTOmPi(); + tofDeltaTNeg = cand.negTOFDeltaTOmPr(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } } @@ -730,8 +744,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascCosPA"), pT, cand.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hDCACascDaughters"), pT, cand.dcacascdaughters()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascRadius"), pT, cand.cascradius()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.dcanegtopv()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.dcapostopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.sign() > 0 ? cand.dcapostopv() : cand.dcanegtopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.sign() > 0 ? cand.dcanegtopv() : cand.dcapostopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hBachDCAToPV"), pT, cand.dcabachtopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0CosPA"), pT, cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0Radius"), pT, cand.v0radius()); @@ -787,7 +801,7 @@ struct Derivedupcanalysis { void init(InitContext const&) { - if (doprocessV0s && doprocessCascades) { + if ((doprocessV0s && doprocessCascades) || (doprocessV0sMC && doprocessCascadesMC)) { LOG(fatal) << "Unable to analyze both v0s and cascades simultaneously. Please enable only one process at a time"; } @@ -842,20 +856,14 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDPositiveProton); maskAntiOmegaSpecific.set(selTPCPIDPositivePion); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda}); - setBits(maskXiSpecific, {selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega); } // negative track if (TrackConfigurations.requireNegITSonly) { @@ -873,20 +881,14 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDNegativePion); maskAntiOmegaSpecific.set(selTPCPIDNegativeProton); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda}); - setBits(maskXiSpecific, {selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega); } // bachelor track maskTrackPropertiesCasc = maskTrackPropertiesV0; @@ -901,15 +903,11 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDBachKaon); maskAntiOmegaSpecific.set(selTPCPIDBachKaon); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutXiPi < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - setBits(maskAntiXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - } - if (PIDConfigurations.tofPidNsigmaCutOmegaKaon < kNoPidNsigmaCut) { // safeguard for no cut - setBits(maskOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); } if (TrackConfigurations.skipTPConly) { @@ -1007,6 +1005,14 @@ struct Derivedupcanalysis { histos.add("eventQA/mc/hSelGeneratorsId", "hSelGeneratorsId", kTH1D, {axisGeneratorIds}); } + if (doprocessV0sMC || doprocessCascadesMC || doprocessGenerated) { + histos.add("generalQA/mc/hInvalidMCReferences", "Out-of-range MC references; Reference type; Entries", kTH1D, {{7, -0.5, 6.5}}); + const std::array referenceLabels{"Vertex collision", "V0 core", "V0 collision", "Cascade core", "Cascade collision", "Generated V0 collision", "Generated cascade collision"}; + for (std::size_t i = 0; i < referenceLabels.size(); ++i) { + histos.get(HIST("generalQA/mc/hInvalidMCReferences"))->GetXaxis()->SetBinLabel(i + 1, referenceLabels[i]); + } + } + if (doprocessV0sMC || doprocessCascadesMC) { // Event QA histos.add("eventQA/mc/hFakeEvents", "hFakeEvents", {kTH1D, {{1, -0.5f, 0.5f}}}); @@ -1018,16 +1024,29 @@ struct Derivedupcanalysis { } if (doprocessV0sMC) { - if (analyseLambda && calculateFeeddownMatrix) + histos.add("generalQA/mc/h3dV0CollisionAssociation", "V0 truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); + if (analyseLambda && calculateFeeddownMatrix) { histos.add(Form("%s/h3dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h3dLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); - if (analyseAntiLambda && calculateFeeddownMatrix) + histos.add(Form("%s/h5dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h5dLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kLambdaPartID)}); + } + if (analyseAntiLambda && calculateFeeddownMatrix) { histos.add(Form("%s/h3dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h3dAntiLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); + histos.add(Form("%s/h5dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h5dAntiLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kAntiLambdaPartID)}); + } + } + + if (doprocessCascadesMC) { + histos.add("generalQA/mc/h3dCascadeCollisionAssociation", "Cascade truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); } if (doprocessGenerated) { for (int partID = kK0ShortPartID; partID <= kAntiOmegaPartID; partID++) { histos.add(Form("%s/mc/h7dGen", kParticlenames[partID].data()), "h7dGen", kTHnSparseF, {axisDetectors.axisFT0ampl, axisNchInvMass, axisNchInvMass, axisPt, axisSelGap, axisRap, axisGeneratorIds}); } + if (analyseLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenXiFeeddown", kParticlenames[kLambdaPartID].data()), "h4dGenXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); + if (analyseAntiLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenAntiXiFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h4dGenAntiXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); } if (doprocessV0s || doprocessV0sMC) { @@ -1063,6 +1082,9 @@ struct Derivedupcanalysis { } if (doprocessCascades || doprocessCascadesMC) { + histos.add("eventQA/hCascadeChargeSelection", "Selected Xi charge; Charge; Entries", kTH1D, {{2, -0.5f, 1.5f}}); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(1, "Xi-"); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(2, "AntiXi+"); // For all candidates if (doPlainTopoQA) { histos.add("generalQA/hPt", "hPt", kTH1F, {axisPtCoarse}); @@ -1220,8 +1242,7 @@ struct Derivedupcanalysis { template int getGapSideWithoutRecoZDC(TCollision const& collision) { - int selGapSide = o2::aod::sgselector::NoGap; - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, std::numeric_limits::infinity()); + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, std::numeric_limits::infinity()); return applyFITTiming(selGapSide, collision); } @@ -1282,8 +1303,7 @@ struct Derivedupcanalysis { template int getGapSide(TCollision const& collision) { - int selGapSide = o2::aod::sgselector::NoGap; - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); selGapSide = applyZDCTiming(selGapSide, collision); return applyFITTiming(selGapSide, collision); } @@ -1422,10 +1442,11 @@ struct Derivedupcanalysis { } // Additional check for UPC collision flag - if (evSels.useUPCflag && collision.flags() < 1) { + const bool hasUPCFlag = (collision.flags() & o2::dataformats::Vertex>::Flags::UPCMode) != 0; + if (evSels.useUPCflag && !hasUPCFlag) { return false; } - if (collision.flags() >= 1 && fillQA) { + if (hasUPCFlag && fillQA) { histos.fill(HIST("eventQA/hEventSelection"), 16.0); // UPC event } @@ -1577,9 +1598,9 @@ struct Derivedupcanalysis { if (std::fabs(rapidityOmega) < rapidityCut) bitMap.set(selOmegaRapidity); if (std::fabs(poseta) < daughterEtaCut) - bitMap.set(selNegEta); - if (std::fabs(negeta) < daughterEtaCut) bitMap.set(selPosEta); + if (std::fabs(negeta) < daughterEtaCut) + bitMap.set(selNegEta); if (std::fabs(bacheta) < daughterEtaCut) bitMap.set(selBachEta); @@ -1673,8 +1694,8 @@ struct Derivedupcanalysis { bitMap.set(selTOFNSigmaPositiveProtonLambdaXi); } if (std::fabs(casc.tofNSigmaOmLaPr()) < PIDConfigurations.tofPidNsigmaCutLaPr) { - bitMap.set(selTOFNSigmaNegativePionLambdaOmega); - bitMap.set(selTOFNSigmaPositivePionLambdaOmega); + bitMap.set(selTOFNSigmaNegativeProtonLambdaOmega); + bitMap.set(selTOFNSigmaPositiveProtonLambdaOmega); } // bachelor track if (std::fabs(casc.tofNSigmaXiPi()) < PIDConfigurations.tofPidNsigmaCutXiPi) { @@ -1853,8 +1874,8 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hCascCosPA"), casc.pt(), casc.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hDCACascDaughters"), casc.pt(), casc.dcacascdaughters()); histos.fill(HIST("generalQA/hCascRadius"), casc.pt(), casc.cascradius()); - histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.dcanegtopv()); - histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.dcapostopv()); + histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcapostopv() : casc.dcanegtopv()); + histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcanegtopv() : casc.dcapostopv()); histos.fill(HIST("generalQA/hBachDCAToPV"), casc.pt(), casc.dcabachtopv()); histos.fill(HIST("generalQA/hV0CosPA"), casc.pt(), casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hV0Radius"), casc.pt(), casc.v0radius()); @@ -1867,22 +1888,24 @@ struct Derivedupcanalysis { } // Xi - if (verifyMask(selMap, maskSelectionXi) && analyseXi) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionXi) && analyseXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 0.); fillHistogramsCasc<3>(casc, coll, gap); } // Anti-Xi - if (verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 1.); fillHistogramsCasc<4>(casc, coll, gap); } // Omega - if (verifyMask(selMap, maskSelectionOmega) && analyseOmega) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionOmega) && analyseOmega) { fillHistogramsCasc<5>(casc, coll, gap); } // Anti-Omega - if (verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { fillHistogramsCasc<6>(casc, coll, gap); } } @@ -1936,13 +1959,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); @@ -1996,10 +2019,9 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hV0Radius"), v0.v0radius()); histos.fill(HIST("generalQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); histos.fill(HIST("generalQA/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); } - histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - // K0s if (verifyMask(selMap, maskSelectionK0Short) && analyseK0Short) { fillHistogramsV0<0>(v0, coll, gap); @@ -2026,6 +2048,9 @@ struct Derivedupcanalysis { std::vector listBestCollisionIds(mcCollisions.size(), -1); for (auto const& mcCollision : mcCollisions) { + if (std::abs(mcCollision.posZ()) > maxZVtxPosition) + continue; + if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { continue; } @@ -2033,6 +2058,9 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + // Find the collision with the biggest nbr of PV contributors // Follows what was done here: https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/mcCollsExtra.cxx#L93 int biggestNContribs = -1; @@ -2070,6 +2098,10 @@ struct Derivedupcanalysis { continue; } + auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + histos.fill(HIST("eventQA/mc/hSelGeneratorsId"), mcCollision.generatorsID()); histos.fill(HIST("eventQA/mc/hEventSelectionMC"), 0.0, mcCollision.multMCNParticlesEta08(), mcCollision.generatorsID()); @@ -2081,7 +2113,6 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); - auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); bool atLeastOne = false; float centrality = -1.f; @@ -2133,7 +2164,7 @@ struct Derivedupcanalysis { } template - void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap) + void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap, int gap) { if (!v0.has_motherMCPart()) { return; @@ -2156,11 +2187,13 @@ struct Derivedupcanalysis { if (analyseLambda && verifyMask(selMap, secondaryMaskSelectionLambda) && (v0mother.pdgCode() == PDG_t::kXiMinus) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[1]) + HIST("/h3dLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[1]) + HIST("/h5dLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mLambda()); } if (analyseAntiLambda && verifyMask(selMap, secondaryMaskSelectionAntiLambda) && (v0mother.pdgCode() == PDG_t::kXiPlusBar) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[2]) + HIST("/h3dAntiLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[2]) + HIST("/h5dAntiLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mAntiLambda()); } } @@ -2205,12 +2238,25 @@ struct Derivedupcanalysis { } } + bool hasValidMCReference(int64_t index, int64_t tableSize, int referenceType) + { + if (index < 0) { + return false; + } + // has_*() only checks the sign; older derived files can contain stale positive indices. + if (index >= tableSize) { + histos.fill(HIST("generalQA/mc/hInvalidMCReferences"), referenceType); + return false; + } + return true; + } + void processV0sMC(StraCollisonsFullMC const& collisions, V0CandidatesMC const& fullV0s, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - V0MCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + V0MCCoresFull const& v0MCCores, NeutronsMC const& neutrons) { v0sGrouped.clear(); @@ -2220,40 +2266,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; + + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) + continue; + + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); if (evSels.studyUPConly && (selGapSide < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); - - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nV0sThisColl = v0sGrouped[collision.globalIndex()].size(); @@ -2265,11 +2322,17 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapV0(v0, collision); if (doMCAssociation) { - if (v0.has_v0MCCore()) { + if (hasValidMCReference(v0.v0MCCoreId(), v0MCCores.size(), 1)) { const auto& v0MC = v0.v0MCCore_as(); - computeV0MCAssociation(v0MC, selMap); - if (calculateFeeddownMatrix) { - fillFeeddownMatrix(collision, v0, selMap); + if (hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 2) && v0MC.pdgCode() != -1) { + const auto& candidateCollision = v0MC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dV0CollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), v0MC.straMCCollisionId() == mcCollision.globalIndex()); + computeV0MCAssociation(v0MC, selMap); + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(collision, v0, selMap, selGapSide); + } + } } } } else { @@ -2327,8 +2390,8 @@ struct Derivedupcanalysis { CascadeCandidatesMC const& fullCascades, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - CascMCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + CascMCCoresFull const& cascMCCores, NeutronsMC const& neutrons) { cascadesGrouped.clear(); @@ -2338,40 +2401,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; + + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) + continue; + + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); if (evSels.studyUPConly && (selGapSide < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); - - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nCascadesThisColl = cascadesGrouped[collision.globalIndex()].size(); @@ -2380,9 +2454,15 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapCascade(casc, collision); if (doMCAssociation) { - if (casc.has_cascMCCore()) { + if (hasValidMCReference(casc.cascMCCoreId(), cascMCCores.size(), 3)) { const auto& cascMC = casc.cascMCCore_as(); - computeCascadeMCAssociation(cascMC, selMap); + if (hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 4) && cascMC.pdgCode() != -1) { + const auto& candidateCollision = cascMC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dCascadeCollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), cascMC.straMCCollisionId() == mcCollision.globalIndex()); + computeCascadeMCAssociation(cascMC, selMap); + } + } } } else { // the candidate may belong to any particle species @@ -2406,7 +2486,7 @@ struct Derivedupcanalysis { // V0 start for (auto const& v0MC : V0MCCores) { // Consider only primaries - if (!v0MC.has_straMCCollision() || !v0MC.isPhysicalPrimary()) + if (!v0MC.isPhysicalPrimary() || !hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 5)) continue; // Kinematics (|y| < rapidityCut) @@ -2473,7 +2553,7 @@ struct Derivedupcanalysis { // Cascade start for (auto const& cascMC : CascMCCores) { // Consider only primaries - if (!cascMC.has_straMCCollision() || !cascMC.isPhysicalPrimary()) + if (!cascMC.isPhysicalPrimary() || !hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 6)) continue; // Kinematics (|y| < rapidityCut) float pTmc = cascMC.ptMC(); @@ -2483,8 +2563,6 @@ struct Derivedupcanalysis { } else if ((cascMC.pdgCode() == PDG_t::kOmegaMinus) || (cascMC.pdgCode() == PDG_t::kOmegaPlusBar)) { ymc = cascMC.rapidityMC(2); } - if (std::abs(ymc) > rapidityCut) - continue; const auto& mcCollision = cascMC.straMCCollision_as(); // take gen. collision if (std::abs(mcCollision.posZ()) > maxZVtxPosition) @@ -2511,8 +2589,23 @@ struct Derivedupcanalysis { } else if (static_cast(upcCuts.genGapSide) == 1) { ft0ampl = collision.totalFT0AmplitudeA(); } + nTracksGlobal = collision.multNTracksGlobal(); + } + + // All generated Xi parents, before daughter topology cuts. Requires asymmetric + // CascMCCores populated with generated Xi of both charges in this rapidity range. + if (calculateFeeddownMatrix && nTracksGlobal >= 0 && std::abs(ymc) <= kMotherRapidityMax) { + if (analyseLambda && cascMC.pdgCode() == PDG_t::kXiMinus) { + histos.fill(HIST(kParticlenames[kLambdaPartID]) + HIST("/mc/h4dGenXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } + if (analyseAntiLambda && cascMC.pdgCode() == PDG_t::kXiPlusBar) { + histos.fill(HIST(kParticlenames[kAntiLambdaPartID]) + HIST("/mc/h4dGenAntiXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } } + if (std::abs(ymc) > rapidityCut) + continue; + const int pdgPos = cascMC.pdgCodePositive(); const int pdgNeg = cascMC.pdgCodeNegative(); const int pdgBach = cascMC.pdgCodeBachelor(); @@ -2520,13 +2613,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); From 7685b453074f9b1b0b5b92c176556376bf5a7f15 Mon Sep 17 00:00:00 2001 From: Chiara De Martin <39315597+ChiaraDeMartin95@users.noreply.github.com> Date: Wed, 7 Oct 2026 07:32:14 +0200 Subject: [PATCH 201/648] [PWGLF] Add k0s mass and T0M centrality to lambda tree, add histo for secondary lambda efficiency (#18193) Co-authored-by: Chiara De Martin Co-authored-by: ALICE Action Bot --- PWGLF/DataModel/cascqaanalysis.h | 3 +- .../TableProducer/Strangeness/cascadeflow.cxx | 103 ++++++++++++------ 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/PWGLF/DataModel/cascqaanalysis.h b/PWGLF/DataModel/cascqaanalysis.h index eca673c683d..61783278069 100644 --- a/PWGLF/DataModel/cascqaanalysis.h +++ b/PWGLF/DataModel/cascqaanalysis.h @@ -118,6 +118,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(MassLambda, masslambda, float); +DECLARE_SOA_COLUMN(MassK0Short, massk0short, float); DECLARE_SOA_COLUMN(CtauLambda, ctaulambda, float); DECLARE_SOA_COLUMN(MassXi, massxi, float); DECLARE_SOA_COLUMN(MassOmega, massomega, float); @@ -183,7 +184,7 @@ DECLARE_SOA_TABLE(CascAnalysisFull, "AOD", "CascAnalysisFull", o2::soa::Index<>, cascadesflow::CosThetaStarLambdaFromOmega, cascadesflow::CosThetaStarLambdaFromXi, cascadesflow::CosThetaStarProton, mycascades::McPdgCode); DECLARE_SOA_TABLE(LambdaAnalysis, "AOD", "LambdaAnalysis", o2::soa::Index<>, - cascadesflow::CentFT0C, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::CtauLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); + cascadesflow::CentFT0C, cascadesflow::CentFT0M, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::MassK0Short, cascadesflow::CtauLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); namespace myMCcascades { diff --git a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx index 6d825f80728..92472e75394 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx @@ -308,6 +308,8 @@ struct cascadeFlow { Configurable isStoreTrueCascOnly{"isStoreTrueCascOnly", 1, ""}; Configurable etaCascMCGen{"etaCascMCGen", 0.8, "etaCascMCGen"}; Configurable yCascMCGen{"yCascMCGen", 0.5, "yCascMCGen"}; + Configurable etaLambdaFromXiMCGen{"etaLambdaFromXiMCGen", 0.8, "etaLambdaFromXiMCGen"}; + Configurable yXiToLambdaMCGen{"yXiToLambdaMCGen", 1.0, "yXiToLambdaMCGen"}; struct : ConfigurableGroup { Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -588,6 +590,9 @@ struct cascadeFlow { template double ComputeEPResolutionwShifts(const TCollision& coll, double psiT0C, double psiV0A, double psiT0A, double psiTPCA, double psiTPCC, TProfile3D* shiftprofileA, TProfile3D* shiftprofileB, TProfile3D* shiftprofileC, TProfile3D* shiftprofileD, TProfile3D* shiftprofileE) { + float collcentrality = coll.centFT0C(); + if (isCollisionCentrality == 1) + collcentrality = coll.centFT0M(); int nmode = 2; auto deltapsiFT0C = 0.0; auto deltapsiFV0A = 0.0; @@ -595,36 +600,36 @@ struct cascadeFlow { auto deltapsiTPCA = 0.0; auto deltapsiTPCC = 0.0; for (int ishift = 1; ishift <= 10; ishift++) { - auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); + auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 1.5, ishift - 0.5)); deltapsiFT0C += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0C * std::cos(ishift * static_cast(nmode) * psiT0C) + coeffshiftyFT0C * TMath::Sin(ishift * static_cast(nmode) * psiT0C))); deltapsiFV0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFV0A * std::cos(ishift * static_cast(nmode) * psiV0A) + coeffshiftyFV0A * TMath::Sin(ishift * static_cast(nmode) * psiV0A))); deltapsiFT0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0A * std::cos(ishift * static_cast(nmode) * psiT0A) + coeffshiftyFT0A * TMath::Sin(ishift * static_cast(nmode) * psiT0A))); deltapsiTPCA += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCA * std::cos(ishift * static_cast(nmode) * psiTPCA) + coeffshiftyTPCA * TMath::Sin(ishift * static_cast(nmode) * psiTPCA))); deltapsiTPCC += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCC * std::cos(ishift * static_cast(nmode) * psiTPCC) + coeffshiftyTPCC * TMath::Sin(ishift * static_cast(nmode) * psiTPCC))); } - histos.fill(HIST("Psi_EP_FT0C_shifted"), coll.centFT0C(), psiT0C + deltapsiFT0C); - histos.fill(HIST("Psi_EP_FV0A_shifted"), coll.centFT0C(), psiV0A + deltapsiFV0A); - histos.fill(HIST("Psi_EP_FT0A_shifted"), coll.centFT0C(), psiT0A + deltapsiFT0A); - histos.fill(HIST("Psi_EP_TPCA_shifted"), coll.centFT0C(), psiTPCA + deltapsiTPCA); - histos.fill(HIST("Psi_EP_TPCC_shifted"), coll.centFT0C(), psiTPCC + deltapsiTPCC); - resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), coll.centFT0C()); + histos.fill(HIST("Psi_EP_FT0C_shifted"), collcentrality, psiT0C + deltapsiFT0C); + histos.fill(HIST("Psi_EP_FV0A_shifted"), collcentrality, psiV0A + deltapsiFV0A); + histos.fill(HIST("Psi_EP_FT0A_shifted"), collcentrality, psiT0A + deltapsiFT0A); + histos.fill(HIST("Psi_EP_TPCA_shifted"), collcentrality, psiTPCA + deltapsiTPCA); + histos.fill(HIST("Psi_EP_TPCC_shifted"), collcentrality, psiTPCC + deltapsiTPCC); + resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), collcentrality); + resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), collcentrality); return true; } @@ -802,6 +807,7 @@ struct cascadeFlow { invMassLambda = v0.mLambda(); double ctauLambda = v0.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassLambda0; analysisLambdaSample(coll.centFT0C(), + coll.centFT0M(), hasEventPlane, hasSpectatorPlane, chargeIndex, @@ -809,6 +815,7 @@ struct cascadeFlow { v0.phi(), v0.eta(), invMassLambda, + v0.mK0Short(), ctauLambda, v0.v0radius(), v0.dcapostopv(), @@ -1129,6 +1136,8 @@ struct cascadeFlow { histosMCGen.add("h2DGenLambdaY05", "h2DGenLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaEta08", "h2DGenAntiLambdaEta08", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaY05", "h2DGenAntiLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenXiVsPtLambda", "h2DGenXiVsPtLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenLambdaFromXiPtMatrix", "h2DGenLambdaFromXiPtMatrix", HistType::kTH2F, {{200, 0, 20}, {200, 0, 20}}); histosMCGen.add("hGenXiY", "hGenXiY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenOmegaY", "hGenOmegaY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenLambdaY", "hGenLambdaY", HistType::kTH1F, {{100, -2, 2}}); @@ -1149,6 +1158,10 @@ struct cascadeFlow { histosMCReco.add("h2DRecoTrueLambda", "h2DRecoTrueLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCReco.add("h2DRecoTrueAntiLambda", "h2DRecoTrueAntiLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaSec", "h2DRecoTrueLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaSec", "h2DRecoTrueAntiLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaFromXi", "h2DRecoTrueLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaFromXi", "h2DRecoTrueAntiLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); for (int iS{0}; iS < nParticles; ++iS) { cascadev2::hMassBeforeSelVsPt[iS] = histos.add(Form("hMassBeforeSelVsPt%s", cascadev2::speciesNames[iS].data()), "hMassBeforeSelVsPt", HistType::kTH2F, {massCascAxis[iS], ptAxisCasc}); @@ -2092,15 +2105,15 @@ struct cascadeFlow { resolution.fill(HIST("QVectorsT0ATPCC"), eventplaneVecT0A.Dot(eventplaneVecTPCC), collisionCentrality); resolution.fill(HIST("QVectorsT0ATPCA"), eventplaneVecT0A.Dot(eventplaneVecTPCA), collisionCentrality); - resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), coll.centFT0C()); + resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCA"), eventplaneVecT0C.Dot(eventplaneVecTPCA) / (coll.qTPCR() * coll.sumAmplFT0C()), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCC"), eventplaneVecT0C.Dot(eventplaneVecTPCC) / (coll.qTPCL() * coll.sumAmplFT0C()), collisionCentrality); @@ -2687,6 +2700,7 @@ struct cascadeFlow { float ptmc = RecoDecay::sqrtSumOfSquares(v0mc.pxMC(), v0mc.pyMC()); float lambdaMCeta = RecoDecay::eta(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}); float lambdaMCy = 0; + if (std::abs(v0mc.pdgCode()) == PDG_t::kLambda0) { lambdaMCy = RecoDecay::y(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}, constants::physics::MassLambda); if (std::abs(lambdaMCeta) < etaCascMCGen) { @@ -2740,6 +2754,19 @@ struct cascadeFlow { theta1 = o2::constants::math::PI + theta; // pi/2 < theta1 < pi --> pi/4 < theta1/2 < pi/2 --> 1 < tan (theta1/2) --> negative eta float cascMCeta = -std::log(std::tan(theta1 / 2)); + + float pxLambda = cascmc.pxPosMC() + cascmc.pxNegMC(); + float pyLambda = cascmc.pyPosMC() + cascmc.pyNegMC(); + float pzLambda = cascmc.pzPosMC() + cascmc.pzNegMC(); + float ptmcLambda = RecoDecay::sqrtSumOfSquares(pxLambda, pyLambda); + float thetaLambda = std::atan(ptmcLambda / pzLambda); + float theta1Lambda = 0; + if (thetaLambda > 0) + theta1Lambda = thetaLambda; + else + theta1Lambda = o2::constants::math::PI + thetaLambda; + float lambdaMCeta = -std::log(std::tan(theta1Lambda / 2)); + float cascMCy = 0; if (std::abs(cascmc.pdgCode()) == PDG_t::kXiMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassXiMinus); @@ -2750,6 +2777,10 @@ struct cascadeFlow { if (std::abs(cascMCy) < yCascMCGen) histosMCGen.fill(HIST("h2DGenXiY05"), centrality, ptmc); histosMCGen.fill(HIST("hGenXiY"), cascMCy); + if (std::abs(cascMCy) < yXiToLambdaMCGen && std::abs(lambdaMCeta) < etaLambdaFromXiMCGen) { + histosMCGen.fill(HIST("h2DGenXiVsPtLambda"), centrality, ptmcLambda); // to compute secondary lambda efficiency + histosMCGen.fill(HIST("h2DGenLambdaFromXiPtMatrix"), ptmc, ptmcLambda); + } } else if (std::abs(cascmc.pdgCode()) == PDG_t::kOmegaMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassOmegaMinus); if (std::abs(cascMCeta) < etaCascMCGen) { @@ -2871,6 +2902,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 1); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 1); + histosMCReco.fill(HIST("h2DRecoTrueLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueLambdaFromXi"), collisionCentrality, ptmc); } } else if (isTrueALambda) { if (isPrimary) { @@ -2880,6 +2914,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 3); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 3); + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == -PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaFromXi"), collisionCentrality, ptmc); } } } From f03b8bdf88b1429fae2461363058c9b977b224a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 7 Oct 2026 08:26:51 +0200 Subject: [PATCH 202/648] [PWGCF] Fix cpplint errors (#18097) --- PWGCF/Core/PWGCFCoreLinkDef.h | 5 +- .../Tasks/NetProtonCumulants.cxx | 3 +- .../Core/FemtoWorldTrackSelection.h | 2 +- PWGCF/FemtoWorld/Core/FemtoWorldV0Selection.h | 6 +- .../femtoWorldProducerReducedTask.cxx | 2 + .../femtoWorldProducerTaskV0Only.cxx | 5 +- .../Tasks/femtoWorldPairTaskTrackTrack.cxx | 25 ++++--- .../Core/GenericFrameworkLinkDef.h | 5 +- PWGCF/JCorran/Core/JCORRANLinkDef.h | 5 +- .../Core/EventSelectionFilterAndAnalysis.cxx | 2 - .../Core/EventSelectionFilterAndAnalysis.h | 4 +- .../Core/PIDSelectionFilterAndAnalysis.h | 6 +- .../Core/SkimmingConfigurableCuts.h | 74 +++++++++---------- .../Core/TwoPartCorrLinkDef.h | 6 +- 14 files changed, 74 insertions(+), 76 deletions(-) diff --git a/PWGCF/Core/PWGCFCoreLinkDef.h b/PWGCF/Core/PWGCFCoreLinkDef.h index fcc73500be2..235b4a7d2b7 100644 --- a/PWGCF/Core/PWGCFCoreLinkDef.h +++ b/PWGCF/Core/PWGCFCoreLinkDef.h @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_CORE_PWGCFCORELINKDEF_H_ -#define PWGCF_CORE_PWGCFCORELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -23,4 +23,3 @@ #pragma link C++ class o2::analysis::TrackSelectionPIDCfg + ; #pragma link C++ class o2::analysis::SimpleInclusiveCut + ; #pragma link C++ class CorrelationContainer + ; -#endif // PWGCF_CORE_PWGCFCORELINKDEF_H_ diff --git a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx index 68f4b4bc7a8..4f7c7de7d4c 100644 --- a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,7 @@ struct NetProtonCumulants_Table_QA { // Filter command*********** Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); + Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); // Connect to ccdb Service ccdb; diff --git a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h index 360587d82ef..69e16841ce0 100644 --- a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h +++ b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h @@ -96,7 +96,7 @@ class FemtoWorldTrackSelection : public FemtoWorldObjectSelection void init(o2::framework::HistogramRegistry* registry); @@ -472,9 +472,7 @@ std::array FemtoWorldV0Selection::getCutContainer(C const& sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax && diffAntiLambda < diffLambda) { sign = 1.; - } - // if it happens that none of these are true, ignore the invariant mass - else { + } else { // if it happens that none of these are true, ignore the invariant mass if (abs(nSigmaPrNeg) < nSigmaPIDMax && abs(nSigmaPiPos) < nSigmaPIDMax) { sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax) { diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx index 28ef5d0ea9e..b477e97f4e5 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx @@ -37,6 +37,8 @@ #include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx index 091afad61ba..c4d9491f507 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx @@ -35,9 +35,12 @@ #include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx index f5f84aff409..b8e514a06fe 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx @@ -214,32 +214,39 @@ struct femtoWorldPairTaskTrackTrack { if (mom > 0.5) { // if (TMath::Hypot( nsigmaTOFP, nsigmaTPCP )/TMath::Sqrt(2) < 3.0) if (mom < 2.0) { - if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) + if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) { return true; - } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) + } + } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) { return true; + } } else { - if (TMath::Abs(nsigmaTPCK) < fNsigma) + if (TMath::Abs(nsigmaTPCK) < fNsigma) { return true; + } } } else { - if (mom < 0.4) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (mom >= 0.4 && mom <= 0.6) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (nsigmaTOFK < -999.) { return false; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } return false; } diff --git a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h index 3fea9523064..e3907fec165 100644 --- a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h +++ b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ -#define PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -28,5 +27,3 @@ #pragma link C++ class o2::analysis::genericframework::GFWBinningCuts + ; #pragma link C++ class o2::analysis::genericframework::GFWRegions + ; #pragma link C++ class o2::analysis::genericframework::GFWCorrConfigs + ; - -#endif // PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ diff --git a/PWGCF/JCorran/Core/JCORRANLinkDef.h b/PWGCF/JCorran/Core/JCORRANLinkDef.h index d0e9bef308d..e48231cbdf1 100644 --- a/PWGCF/JCorran/Core/JCORRANLinkDef.h +++ b/PWGCF/JCorran/Core/JCORRANLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ -#define PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -22,5 +21,3 @@ #pragma link C++ class FlowJSPCAnalysis + ; #pragma link C++ class FlowJSPCObservables + ; #pragma link C++ class JEPFlowAnalysis + ; - -#endif // PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx index 3d8090a56c1..3181b07d8b5 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx @@ -474,8 +474,6 @@ void EventSelectionFilterAndAnalysis::StoreArmedMask() } mOptArmedMask.push_back(optMask); optMask = 0UL; - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { armedBrick(mZVertex); } diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index 0a0a6aa3e41..f05bfb365eb 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -111,7 +111,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis template void StoreMultiplicities(CollisionToFilter const&, AssociatedTracks const&); int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + void StoreArmedMask() override; std::vector*> mBFieldSelection; //! the magnetic field selection cuts MultiplicityBrick* mMultiplicityClasses; //! the multiplicity classes cuts @@ -231,8 +231,6 @@ inline uint64_t EventSelectionFilterAndAnalysis::Filter(CollisionToFilter const& bool acc = mMultiplicityClasses->ComplexBrickHelper::Filter(selectedMask, bit); acceptcollision = acceptcollision && acc; } - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { bool acc = filterBrickValue(selectedMask, bit, mZVertex, col.posZ()); acceptcollision = acceptcollision && acc; diff --git a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h index 8d24e4348c8..7cf25ff5d6a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h @@ -98,7 +98,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis PIDSelectionFilterAndAnalysis(); PIDSelectionFilterAndAnalysis(const TString&, selmodes); PIDSelectionFilterAndAnalysis(const PIDSelectionConfigurable& pidsel, selmodes mode); - virtual ~PIDSelectionFilterAndAnalysis() override; + ~PIDSelectionFilterAndAnalysis() override; void SetPTOF(float ptof) { mPTOF = ptof; } void SetRequireTOF(bool requiretof = false) { mRequireTOF = requiretof; } @@ -133,8 +133,8 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: void ConstructCutFromString(const TString&); - virtual int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + int CalculateMaskLength() override; + void StoreArmedMask() override; float mPTOF = 0.8f; ///< the p threshold for cheking TOF information bool mRequireTOF = false; ///< is TOF required diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 885b646e906..2e39ee2b1c7 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -46,7 +46,7 @@ class CutBrick : public TNamed CutBrick(); CutBrick(const char*, const char*); CutBrick(const CutBrick&) = delete; - virtual ~CutBrick() override = default; + ~CutBrick() override = default; CutBrick& operator=(const CutBrick&) = delete; public: @@ -111,13 +111,13 @@ class CutBrickLimit : public CutBrick CutBrickLimit(); CutBrickLimit(const char*, const TValueToFilter&); CutBrickLimit(const TString&); - virtual ~CutBrickLimit() override = default; + ~CutBrickLimit() override = default; CutBrickLimit(const CutBrickLimit&) = delete; CutBrickLimit& operator=(const CutBrickLimit&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -155,12 +155,12 @@ class CutBrickFnLimit : public CutBrickLimit CutBrickFnLimit(); CutBrickFnLimit(const char*, const TF1&); CutBrickFnLimit(const TString&); - virtual ~CutBrickFnLimit() override = default; + ~CutBrickFnLimit() override = default; CutBrickFnLimit(const CutBrickFnLimit&) = delete; CutBrickFnLimit& operator=(const CutBrickFnLimit&) = delete; /// sets the value of the limit according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLimit = TValueToFilter(mFunction.Eval(x)); } @@ -182,13 +182,13 @@ class CutBrickThreshold : public CutBrick CutBrickThreshold(); CutBrickThreshold(const char*, const TValueToFilter&); CutBrickThreshold(const TString&); - virtual ~CutBrickThreshold() override = default; + ~CutBrickThreshold() override = default; CutBrickThreshold(const CutBrickThreshold&) = delete; CutBrickThreshold& operator=(const CutBrickThreshold&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -226,12 +226,12 @@ class CutBrickFnThreshold : public CutBrickThreshold CutBrickFnThreshold(); CutBrickFnThreshold(const char*, const TF1&); CutBrickFnThreshold(const TString&); - virtual ~CutBrickFnThreshold() override = default; + ~CutBrickFnThreshold() override = default; CutBrickFnThreshold(const CutBrickFnThreshold&) = delete; CutBrickFnThreshold& operator=(const CutBrickFnThreshold&) = delete; /// sets the value of the threshold according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mThreshold = TValueToFilter(mFunction.Eval(x)); } @@ -253,13 +253,13 @@ class CutBrickRange : public CutBrick CutBrickRange(); CutBrickRange(const char*, const TValueToFilter&, const TValueToFilter&); CutBrickRange(const TString&); - virtual ~CutBrickRange() override = default; + ~CutBrickRange() override = default; CutBrickRange(const CutBrickRange&) = delete; CutBrickRange& operator=(const CutBrickRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -298,12 +298,12 @@ class CutBrickFnRange : public CutBrickRange CutBrickFnRange(); CutBrickFnRange(const char*, const TF1&, const TF1&); CutBrickFnRange(const TString&); - virtual ~CutBrickFnRange() override = default; + ~CutBrickFnRange() override = default; CutBrickFnRange(const CutBrickFnRange&) = delete; CutBrickFnRange& operator=(const CutBrickFnRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -327,13 +327,13 @@ class CutBrickExtToRange : public CutBrick CutBrickExtToRange(); CutBrickExtToRange(const char*, const TValueToFilter&, const TValueToFilter&); CutBrickExtToRange(const TString&); - virtual ~CutBrickExtToRange() override = default; + ~CutBrickExtToRange() override = default; CutBrickExtToRange(const CutBrickExtToRange&) = delete; CutBrickExtToRange& operator=(const CutBrickExtToRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -372,12 +372,12 @@ class CutBrickFnExtToRange : public CutBrickExtToRange CutBrickFnExtToRange(); CutBrickFnExtToRange(const char*, const TF1&, const TF1&); CutBrickFnExtToRange(const TString&); - virtual ~CutBrickFnExtToRange() override = default; + ~CutBrickFnExtToRange() override = default; CutBrickFnExtToRange(const CutBrickFnExtToRange&) = delete; CutBrickFnExtToRange& operator=(const CutBrickFnExtToRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -403,16 +403,16 @@ class CutBrickSelectorMultipleRanges : public CutBrick CutBrickSelectorMultipleRanges(); CutBrickSelectorMultipleRanges(const char*, const std::vector&); CutBrickSelectorMultipleRanges(const TString&); - virtual ~CutBrickSelectorMultipleRanges() override = default; + ~CutBrickSelectorMultipleRanges() override = default; CutBrickSelectorMultipleRanges(const CutBrickSelectorMultipleRanges&) = delete; CutBrickSelectorMultipleRanges& operator=(const CutBrickSelectorMultipleRanges&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; /// Return the length needed to code the brick status /// The length is in brick units. The actual length is implementation dependent /// \returns Brick length in units of bricks - virtual int Length() override { return mActive.size(); } + int Length() override { return mActive.size(); } private: void ConstructCutFromString(const TString&); @@ -433,7 +433,7 @@ class CutWithVariations : public CutBrick CutWithVariations(); CutWithVariations(const char*, const char*, bool); CutWithVariations(const TString&); - virtual ~CutWithVariations() override = default; + ~CutWithVariations() override = default; CutWithVariations(const CutWithVariations&) = delete; CutWithVariations& operator=(const CutWithVariations&) = delete; @@ -441,10 +441,10 @@ class CutWithVariations : public CutBrick bool AddVariationBrick(CutBrick* brick); TList& getDefaultBricks() { return mDefaultBricks; } TList& getVariantBricks() { return mVariationBricks; } - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override; - virtual int getArmedIndex() override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override; + int getArmedIndex() override; private: void ConstructCutFromString(const TString&); @@ -467,7 +467,7 @@ class SpecialCutBrick : public TNamed SpecialCutBrick(); SpecialCutBrick(const char*, const char*); SpecialCutBrick(const SpecialCutBrick&) = delete; - virtual ~SpecialCutBrick() override = default; + ~SpecialCutBrick() override = default; SpecialCutBrick& operator=(const SpecialCutBrick&) = delete; public: @@ -511,7 +511,7 @@ class TrackSelectionBrick : public SpecialCutBrick public: TrackSelectionBrick() = default; TrackSelectionBrick(const TString&); - virtual ~TrackSelectionBrick() override = default; + ~TrackSelectionBrick() override = default; enum class TrackCuts : int { kTrackType = 0, @@ -532,7 +532,7 @@ class TrackSelectionBrick : public SpecialCutBrick static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; - virtual std::vector IsArmed() override; + std::vector IsArmed() override; template bool Filter(TrackToFilter const& track) { diff --git a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h index 3bb61740aaa..3b3bddb5118 100644 --- a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h +++ b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ -#define PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -51,5 +51,3 @@ #pragma link C++ class o2::analysis::PWGCF::EventSelectionFilterAndAnalysis + ; #pragma link C++ class o2::analysis::PWGCF::FilterAndAnalysisFramework + ; #pragma link C++ class DihadronContainer + ; - -#endif // PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ From b8c8278e2c6bcef283e62908dc5c3c525e78f6ea Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Wed, 7 Oct 2026 09:58:07 +0300 Subject: [PATCH 203/648] [PWGCF] Add TRI multiplicity histograms for alternative selections (#18188) Co-authored-by: Iulian --- .../Tasks/FoxWolframCorrelation.cxx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index 217bbcf6872..6634b187869 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -410,6 +410,10 @@ struct FoxWolframCorrelation { for (int i = 1; i <= noOfMultipBins; i++) { histos.add(fmt::format("Multip/GlobalTrk_BIN{}", i).c_str(), fmt::format("GlobalTrk_BIN{}", i).c_str(), kTH1D, {axisMultip}); histos.add(fmt::format("Multip/GlobalTrkTri_BIN{}", i).c_str(), fmt::format("GlobalTrkTri_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/GlobalTrkTri1_BIN{}", i).c_str(), fmt::format("GlobalTrkTri1_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/GlobalTrkTri2_BIN{}", i).c_str(), fmt::format("GlobalTrkTri2_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/GlobalTrkTri3_BIN{}", i).c_str(), fmt::format("GlobalTrkTri3_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/GlobalTrkTriAux_BIN{}", i).c_str(), fmt::format("GlobalTrkTriAux_BIN{}", i).c_str(), kTH1D, {axisMultip}); histos.add(fmt::format("pTSpectra/pT_BIN{}", i).c_str(), fmt::format("pT_BIN{}", i).c_str(), kTH1D, {axispT}); histos.add(fmt::format("pTSpectra/pTTri_BIN{}", i).c_str(), fmt::format("pTTri_BIN{}", i).c_str(), kTH1D, {axispT}); @@ -585,6 +589,7 @@ struct FoxWolframCorrelation { histos.fill(HIST("Events_Details/eventsNoTRI"), binIndex); } + // The remaining filters if (isTRI1) { histos.fill(HIST("Events_Details/eventsNoTRI1"), binIndex); } @@ -628,8 +633,25 @@ struct FoxWolframCorrelation { histos.fill(HIST("Spectra/ptphiTri_") + HIST(binNames[idx]), track.phi(), track.pt()); } } + + // TRI Ev. Multip (the remaining filters) + if (isTRI1) { + histos.fill(HIST("Multip/GlobalTrkTri1_") + HIST(binNames[idx]), tracks.size()); + } + + if (isTRI2) { + histos.fill(HIST("Multip/GlobalTrkTri2_") + HIST(binNames[idx]), tracks.size()); + } + + if (isTRI3) { + histos.fill(HIST("Multip/GlobalTrkTri3_") + HIST(binNames[idx]), tracks.size()); + } + + if (isTRIAux) { + histos.fill(HIST("Multip/GlobalTrkTriAux_") + HIST(binNames[idx]), tracks.size()); + } } - }); + }); // per Multip Bin } // Fill same-event pair distributions for one multiplicity interval. From ba4ccc3e57b1c94ae9d76da862ba5c0ed9bc9bc1 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Wed, 7 Oct 2026 12:59:37 +0530 Subject: [PATCH 204/648] [PWGLF] Added QA histograms for phi meson (#18180) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 89bda99b335..1967b6090c8 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -173,6 +173,7 @@ struct Kstarqa { HistogramRegistry hInvMass{"hInvMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hPID{"hPID", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hOthers{"hOthers", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hShift{"hShift", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Confugrable for QA histograms Configurable calcLikeSign{"calcLikeSign", true, "Calculate Like Sign"}; @@ -359,6 +360,21 @@ struct Kstarqa { if (doprocessRecPhi) { hInvMass.add("PhiMCChecks", "PhiMCChecks", kTHnSparseF, {ptAxis, etaAxis, phiAxis, subAxis, subAxis, subAxis}); // pT, eta, phi, deltaEta, deltaPhi, deltaR + + hShift.add("h2D_dpx_vs_px_KaonFromPhi", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_KaonFromPhi", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_KaonFromPhi", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_KaonFromPhi", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_AllKaons", "Kaon from Phi: deta vs eta", kTH2F, {{subAxis}, {etaAxis}}); + hShift.add("h2D_dpy_vs_py_AllKaons", "Kaon from Phi: dphi vs phi", kTH2F, {{subAxis}, {phiAxis}}); + hShift.add("h2D_dpz_vs_pz_AllKaons", "Kaon from Phi: dR vs eta", kTH2F, {{subAxis}, {etaAxis}}); + hShift.add("h2D_dpt_vs_pt_AllKaons", "Kaon from Phi: dR vs phi", kTH2F, {{subAxis}, {phiAxis}}); + + hShift.add("h2D_dpx_vs_px_Phi", "Pion from Phi: deta vs eta", kTH2F, {{subAxis}, {etaAxis}}); + hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dphi vs phi", kTH2F, {{subAxis}, {phiAxis}}); + hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dR vs eta", kTH2F, {{subAxis}, {etaAxis}}); + hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dR vs phi", kTH2F, {{subAxis}, {phiAxis}}); } // Signal Loss & Event Loss in Light Ion Collisions @@ -3055,6 +3071,20 @@ struct Kstarqa { continue; } + // All Kaons (Filling single-track kaon resolution histograms) + const auto mctrack1_all = track1.mcParticle(); + if (std::abs(mctrack1_all.pdgCode()) == PDG_t::kKPlus) { + double dpx_all = mctrack1_all.px() - track1.px(); + double dpy_all = mctrack1_all.py() - track1.py(); + double dpz_all = mctrack1_all.pz() - track1.pz(); + double dpt_all = mctrack1_all.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_AllKaons"), dpx_all, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_AllKaons"), dpy_all, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_AllKaons"), dpz_all, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_AllKaons"), dpt_all, track1.pt()); + } + auto track1ID = track1.index(); for (const auto& track2 : tracks) { rEventSelection.fill(HIST("recMCparticles"), 0.5); @@ -3169,6 +3199,38 @@ struct Kstarqa { auto genPhiPhi = mothertrack1.phi(); hInvMass.fill(HIST("PhiMCChecks"), mother.pt(), mother.eta(), mother.phi(), genpTPhi - mother.pt(), genEtaPhi - mother.eta(), genPhiPhi - mother.phi()); + + // Kaons from Phi daughter (Filling for daughter track1 & daughter track2) + double dpx_k1 = mctrack1.px() - track1.px(); + double dpy_k1 = mctrack1.py() - track1.py(); + double dpz_k1 = mctrack1.pz() - track1.pz(); + double dpt_k1 = mctrack1.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k1, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k1, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k1, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k1, track1.pt()); + + double dpx_k2 = mctrack2.px() - track2.px(); + double dpy_k2 = mctrack2.py() - track2.py(); + double dpz_k2 = mctrack2.pz() - track2.pz(); + double dpt_k2 = mctrack2.pt() - track2.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k2, track2.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k2, track2.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k2, track2.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k2, track2.pt()); + + // Phi Meson + double dpx_phi = mothertrack1.px() - mother.Px(); + double dpy_phi = mothertrack1.py() - mother.Py(); + double dpz_phi = mothertrack1.pz() - mother.Pz(); + double dpt_phi = mothertrack1.pt() - mother.Pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_Phi"), dpx_phi, mother.Px()); + hShift.fill(HIST("h2D_dpy_vs_py_Phi"), dpy_phi, mother.Py()); + hShift.fill(HIST("h2D_dpz_vs_pz_Phi"), dpz_phi, mother.Pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_Phi"), dpt_phi, mother.Pt()); } } } From 4aa762ba232355229f9c44237e7514cd8e4e636a Mon Sep 17 00:00:00 2001 From: hfribert Date: Wed, 7 Oct 2026 09:42:37 +0200 Subject: [PATCH 205/648] [PWGCF] Fixed pT handling and minor fixes for Sigma-/+ in Femto framework (#18197) Co-authored-by: Henrik Fribert Co-authored-by: Henrik Fribert --- PWGCF/Femto/Core/femtoUtils.h | 12 +-- PWGCF/Femto/Core/kinkBuilder.h | 96 ++++++++++++------- PWGCF/Femto/Core/kinkHistManager.h | 16 ++-- .../femtoProducerKinkPtConverter.cxx | 5 +- 4 files changed, 80 insertions(+), 49 deletions(-) diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index 643ddffe654..d7a4a888e74 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -167,14 +167,14 @@ concept HasEventShapeRow = requires(T row) { template concept HasEventShape = HasEventShapeRow> || (requires { typename std::decay_t::iterator; } && HasEventShapeRow::iterator>); -/// Recalculate pT for Kinks (Sigmas) using kinematic constraints -inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter) +/// Recalculate the pT of a kink mother from its direction and the charged daughter momentum +inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter, + float massMother, float massChargedDaughter, float massNeutralDaughter) { float almost0 = 1e-6f; - // Particle masses in GeV/c^2 - auto massPion = o2::constants::physics::MassPionCharged; - auto massNeutron = o2::constants::physics::MassNeutron; - auto massSigmaMinus = o2::constants::physics::MassSigmaMinus; + const float massPion = massChargedDaughter; + const float massNeutron = massNeutralDaughter; + const float massSigmaMinus = massMother; // Calculate mother momentum and direction versor float pMother = std::sqrt(pxMother * pxMother + pyMother * pyMother + pzMother * pzMother); diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 4fd8670accb..d95cd34c6cd 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -27,6 +27,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -60,24 +61,26 @@ struct ConfKinkFilters : o2::framework::ConfigurableGroup { // selections bits for all kinks // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define KINK_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all Kinks are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {20.f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {1.0f}, "Maximum absolute pseudorapidity for daughter track"}; \ - o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.1f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ - o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ - o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; +#define KINK_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Kinks are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {19.6f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {1.0f}, "Maximum absolute pseudorapidity for daughter track"}; \ + o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.1f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ + o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> ptOriginalMin{"ptOriginalMin", {1.2f}, "Minimum original (not recalculated) pT of the mother (GeV/c)"}; \ + o2::framework::Configurable> ptOriginalMax{"ptOriginalMax", {10.f}, "Maximum original (not recalculated) pT of the mother (GeV/c)"}; // derived selection bits for sigma struct ConfSigmaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; o2::framework::Configurable> chaDauTpcPion{"chaDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for charged daughter tracks"}; }; @@ -85,9 +88,12 @@ struct ConfSigmaBits : o2::framework::ConfigurableGroup { struct ConfSigmaPlusBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaPlusBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {0.0f}, "Minimum Alpha_AP for SigmaPlus candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {1.0f}, "Maximum Alpha_AP for SigmaPlus candidates"}; o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; - o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {5.f}, "Maximum combined |nsigma_Proton| (TPC+TOF) for charged daughter tracks"}; - o2::framework::Configurable pidThres{"pidThres", 0.75f, "Momentum threshold for using TOF/combined pid for daughter tracks (GeV/c)"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; }; #undef KINK_DEFAULT_BITS @@ -153,6 +159,9 @@ enum KinkSeles { kQtAPMax, kCosPointingAngleMin, + kPtOriginalMin, + kPtOriginalMax, + kKinkSelsMax }; @@ -173,7 +182,9 @@ const std::unordered_map kinkSelectionNames = { {kAlphaAPMax, "alphaAPMax"}, {kQtAPMin, "qtAPMin"}, {kQtAPMax, "qtAPMax"}, - {kCosPointingAngleMin, "cosPointingAngleMin"}}; + {kCosPointingAngleMin, "cosPointingAngleMin"}, + {kPtOriginalMin, "ptOriginalMin"}, + {kPtOriginalMax, "ptOriginalMax"}}; /// enum for all kink pre-filters (evaluated in checkFilters, before the selection bitmask) enum KinkFilters { @@ -231,9 +242,9 @@ class KinkSelection : public baseselection::BaseSelectionaddSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, false, false, true); - this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kUpperLimit, false, false, true); + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); } this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false); @@ -247,6 +258,8 @@ class KinkSelection : public baseselection::BaseSelectionaddSelection(kQtAPMin, kinkSelectionNames.at(kQtAPMin), config.qtAPMin.value, limits::kLowerLimit, true, true, false); this->addSelection(kQtAPMax, kinkSelectionNames.at(kQtAPMax), config.qtAPMax.value, limits::kUpperLimit, true, true, false); this->addSelection(kCosPointingAngleMin, kinkSelectionNames.at(kCosPointingAngleMin), config.cosPointingAngleMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPtOriginalMin, kinkSelectionNames.at(kPtOriginalMin), config.ptOriginalMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPtOriginalMax, kinkSelectionNames.at(kPtOriginalMax), config.ptOriginalMax.value, limits::kUpperLimit, true, true, false); this->setupSelectionHistogram(registry); @@ -281,7 +294,7 @@ class KinkSelection : public baseselection::BaseSelection - void computeKinkKinematics(T1 const& kinkCand, T2 const& /*tracks*/, T3 const& col) + void computeKinkKinematics(T1 const& kinkCand, T2 const& /*tracks*/, T3 const& /*col*/) { std::array momMother = {kinkCand.pxMoth(), kinkCand.pyMoth(), kinkCand.pzMoth()}; float kinkMomP = RecoDecay::p(momMother); @@ -300,12 +313,11 @@ class KinkSelection : public baseselection::BaseSelection 0.f) ? std::sqrt(std::max(0.f, p2A - dp * dp / p2V0)) : 0.f; - std::array vMother = {kinkCand.xDecVtx() - col.posX(), kinkCand.yDecVtx() - col.posY(), kinkCand.zDecVtx() - col.posZ()}; + std::array vMother = {kinkCand.xDecVtx(), kinkCand.yDecVtx(), kinkCand.zDecVtx()}; float vMotherNorm = std::sqrt(std::inner_product(vMother.begin(), vMother.end(), vMother.begin(), 0.f)); mCosPointingAngle = (vMotherNorm > 0.f && kinkMomP > 0.f) ? (std::inner_product(momMother.begin(), momMother.end(), vMother.begin(), 0.f)) / (kinkMomP * vMotherNorm) : 0.f; mTransRadius = std::hypot(kinkCand.xDecVtx(), kinkCand.yDecVtx()); - mKinkDauP = kinkDauP; mKinkDauEta = RecoDecay::eta(momDaughter); mKinkAngle = 0.f; @@ -327,6 +339,9 @@ class KinkSelection : public baseselection::BaseSelectionevaluateObservable(kQtAPMin, mQtAp); this->evaluateObservable(kQtAPMax, mQtAp); this->evaluateObservable(kCosPointingAngleMin, mCosPointingAngle); + // the stored pT is the recalculated one, the original pT is only available as selection bits + this->evaluateObservable(kPtOriginalMin, mKinkMotherPtOriginal); + this->evaluateObservable(kPtOriginalMax, mKinkMotherPtOriginal); this->evaluateObservable(kKinkTopoDcaMax, kinkCand.dcaKinkTopo()); // Compute transRadius @@ -345,14 +360,11 @@ class KinkSelection : public baseselection::BaseSelectionevaluateObservable(kChaDaughTpcPion, chaDaughter.tpcNSigmaPi()); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - if (mKinkDauP < mPidThreshold) { - this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); } else { - if (chaDaughter.hasTOF()) { - this->evaluateObservable(kChaDaughTofProton, std::abs(chaDaughter.tofNSigmaPr())); - } else { - this->evaluateObservable(kChaDaughTofProton, 999.f); - } + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); } } @@ -368,9 +380,20 @@ class KinkSelection : public baseselection::BaseSelection 0.f) ? ptRecalc : std::hypot(momMother[0], momMother[1]); + // Recalculate pT using kinematic constraints of the decay channel + float ptRecalc = -999.f; + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { + // Sigma- -> pi- n + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); + } + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { + // Sigma+ -> p pi0 + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaPlus, o2::constants::physics::MassProton, o2::constants::physics::MassPionNeutral); + } + mKinkMotherPtOriginal = std::hypot(momMother[0], momMother[1]); + mKinkMotherPt = (ptRecalc > 0.f) ? ptRecalc : mKinkMotherPtOriginal; } template @@ -443,7 +466,7 @@ class KinkSelection : public baseselection::BaseSelection> const& ChaDauSpecs) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } @@ -332,26 +334,28 @@ class KinkHistManager T3 const& ConfChaDauBinningQa) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); this->enableOptionalHistograms(ConfKinkBinningQa); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } diff --git a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx index 8f61ef26d16..b97853e6829 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" +#include #include #include #include @@ -73,7 +74,9 @@ struct FemtoProducerKinkPtConverter { float pyMoth = sigma.pt() * std::sin(sigma.phi()); float pzMoth = sigma.pt() * std::sinh(sigma.eta()); - float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug); + // Sigma- -> pi- n + float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug, + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); ROOT::Math::PtEtaPhiMVector recalcVec(ptRecalc, sigma.eta(), sigma.phi(), sigma.mass()); float ptFrom4Vec = recalcVec.Pt(); From c05dd435c2c664f435ef629b992a9b81b77a37f5 Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Wed, 7 Oct 2026 09:43:54 +0200 Subject: [PATCH 206/648] [PWGCF] Add configurable pT-dependent material budget efficiency systematic for Radial Decorrelation Analysis (#18198) --- .../Tasks/radialFlowDecorr.cxx | 96 ++++++++++++------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 66dd110b560..2b87e7d5ad6 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -187,6 +187,8 @@ struct RadialFlowDecorr { Configurable cfgFlat{"cfgFlat", false, "Whether to use flattening weights"}; Configurable cfgEff{"cfgEff", false, "Whether to use Efficiency weights"}; + Configurable cfgEffVarPercent{"cfgEffVarPercent", 0.f, "Signed relative efficiency variation (%): +10 or -10"}; + Configurable cfgEffVarPtDep{"cfgEffVarPtDep", false, "false: constant variation; true: linear decrease with pT"}; Configurable cfgZDC{"cfgZDC", false, "Whether to use ZDC for pileup histograms"}; Configurable cfgCCDBurl{"cfgCCDBurl", "https://alice-ccdb.cern.ch", "ccdb url"}; @@ -213,13 +215,8 @@ struct RadialFlowDecorr { // binning and are rebuilt in init(). Placeholders here. AxisSpec etaAxis{9, -0.9, 0.9, "#eta"}; AxisSpec etaBinAxis{10, -0.5, 9.5, "#eta bin Number"}; - - AxisSpec gapAxis{{-1.5, -1.3, -1.1, -0.9, -0.7, -0.5, -0.3, -0.1, - 0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5}, - "Gap"}; - AxisSpec sumAxis{{-1.5, -1.3, -1.1, -0.9, -0.7, -0.5, -0.3, -0.1, - 0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5}, - "Sum"}; + AxisSpec gapAxis{15, -1.5, 1.5, "#Delta#eta"}; + AxisSpec sumAxis{15, -1.5, 1.5, "#Sigma#eta"}; // --- process switches -------------------------------------------------------- Configurable cfgRunGetEff{"cfgRunGetEff", false, "Run MC pass to build efficiency/fake maps"}; @@ -491,15 +488,36 @@ struct RadialFlowDecorr { return static_cast(val); } - // Reject missing or invalid efficiency/fake corrections consistently in all passes. + float efficiencyVariationFactor(float pt) const + { + if (cfgSystType.value != kSystEff) { + return 1.f; + } + // Empirical shape: full variation at cfgPtMin, zero at cfgPtMax. + const float shape = cfgEffVarPtDep.value ? (cfgPtMax.value - pt) / (cfgPtMax.value - cfgPtMin.value) : 1.f; + return 1.f + 0.01f * cfgEffVarPercent.value * shape; + } + bool getValidEffFake(float mult, float pt, float eta, bool useEff, float& eff, float& fake) const { eff = getEfficiency(mult, pt, eta, 0, useEff); fake = getEfficiency(mult, pt, eta, 1, useEff); - return std::isfinite(eff) && std::isfinite(fake) && - eff > KFloatEpsilon && eff <= 1.f && - fake >= 0.f && fake < 1.f; + + // Validate the nominal corrections before applying any variation. + if (!std::isfinite(eff) || !std::isfinite(fake) || + eff <= KFloatEpsilon || eff > 1.f || + fake < 0.f || fake >= 1.f) { + return false; + } + + // Apply the variation once, in DATA mean/fluctuation jobs. + if (useEff && (cfgRunDataMean.value || cfgRunDataFluc.value)) { + eff *= efficiencyVariationFactor(pt); + } + + // The varied efficiency is an effective correction denominator. + return std::isfinite(eff) && eff > KFloatEpsilon; } float getFlatteningWeight(float vz, float chg, float pt, float eta, float phi, bool useFlat) const @@ -1073,8 +1091,12 @@ struct RadialFlowDecorr { } etaAxis = AxisSpec{obsEdges, "#eta"}; etaBinAxis = AxisSpec{nEta + 1, -0.5, static_cast(nEta) + 0.5, "#eta bin Number"}; - LOGF(info, "Observable eta binning (%s): %d bins of width %.2f over |eta|<%.2f (+ reference), nEta=%d", - isMcRun ? "MC pinned" : "DATA", nbins, width, halfEta, nEta); + // (Delta eta, Sigma eta): fills are integer multiples of w, so 2N-1 bins + // of width w centred on them, out to (N-1/2) w. + const int nGS = 2 * nbins - 1; + const double gsMax = (nbins - 0.5) * width; + gapAxis = AxisSpec{nGS, -gsMax, gsMax, "#Delta#eta"}; + sumAxis = AxisSpec{nGS, -gsMax, gsMax, "#Sigma#eta"}; } // bootstrap active only for the base data fluctuation pass @@ -1487,7 +1509,7 @@ struct RadialFlowDecorr { histos.fill(HIST("MCGen/Prof_Cent_Nchrec"), cent, sumWiTruth[0]); histos.fill(HIST("MCGen/Prof_Mult_Nchrec"), multPV, sumWiTruth[0]); - if (sumWiTruth[0] > 1.0f) { + if (sumWiTruth[0] > KFloatEpsilon) { histos.fill(HIST("MCGen/Prof_Cent_MeanpT"), cent, sumWiptiTruth[0] / sumWiTruth[0]); histos.fill(HIST("MCGen/Prof_Mult_MeanpT"), multPV, sumWiptiTruth[0] / sumWiTruth[0]); } @@ -1554,26 +1576,26 @@ struct RadialFlowDecorr { float nRecoAB = sumWiReco[ietaA] + sumWiReco[ietaC]; float nCorrAB = sumWiRecoEffCorr[ietaA] + sumWiRecoEffCorr[ietaC]; - if (nTruAB > 0) { + if (sumWiTruth[ietaA] > KFloatEpsilon && sumWiTruth[ietaC] > KFloatEpsilon) { histos.fill(HIST("Prof2D_MeanpTSub_Tru"), cent, ietaA, ietaC, (sumWiptiTruth[ietaA] + sumWiptiTruth[ietaC]) / nTruAB); } - if (nRecoAB > 0) { + if (sumWiReco[ietaA] > KFloatEpsilon && sumWiReco[ietaC] > KFloatEpsilon) { histos.fill(HIST("Prof2D_MeanpTSub_Reco"), cent, ietaA, ietaC, (sumWiptiReco[ietaA] + sumWiptiReco[ietaC]) / nRecoAB); } - if (nCorrAB > 0) { + if (sumWiRecoEffCorr[ietaA] > KFloatEpsilon && sumWiRecoEffCorr[ietaC] > KFloatEpsilon) { histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr"), cent, ietaA, ietaC, (sumWiptiRecoEffCorr[ietaA] + sumWiptiRecoEffCorr[ietaC]) / nCorrAB); } } - if (sumWiTruth[ietaA] > 0) { + if (sumWiTruth[ietaA] > KFloatEpsilon) { histos.fill(HIST("pmeanTru_nch_etabin"), multPV, ietaA, sumWiptiTruth[ietaA] / sumWiTruth[ietaA]); histos.fill(HIST("pmeanMultTru_nch_etabin"), multPV, ietaA, sumWiTruth[ietaA]); } - if (sumWiReco[ietaA] > 0) { + if (sumWiReco[ietaA] > KFloatEpsilon) { histos.fill(HIST("pmeanReco_nch_etabin"), multPV, ietaA, sumWiptiReco[ietaA] / sumWiReco[ietaA]); histos.fill(HIST("pmeanMultReco_nch_etabin"), multPV, ietaA, sumWiReco[ietaA]); } - if (sumWiRecoEffCorr[ietaA] > 0) { + if (sumWiRecoEffCorr[ietaA] > KFloatEpsilon) { histos.fill(HIST("pmeanRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiptiRecoEffCorr[ietaA] / sumWiRecoEffCorr[ietaA]); histos.fill(HIST("pmeanMultRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiRecoEffCorr[ietaA]); } @@ -1706,7 +1728,7 @@ struct RadialFlowDecorr { float mmptRecoEffCor = state.pmeanRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); // truth - meanTru[ieta] = (sumWkTru[ieta][1] >= 1.0) ? (sumPmwkTru[ieta][1][1] / sumWkTru[ieta][1]) : std::numeric_limits::quiet_NaN(); + meanTru[ieta] = (sumWkTru[ieta][1] > KFloatEpsilon) ? (sumPmwkTru[ieta][1][1] / sumWkTru[ieta][1]) : std::numeric_limits::quiet_NaN(); c2Tru[ieta] = std::numeric_limits::quiet_NaN(); c3Tru[ieta] = std::numeric_limits::quiet_NaN(); p1kBarTru[ieta] = std::numeric_limits::quiet_NaN(); @@ -1719,7 +1741,7 @@ struct RadialFlowDecorr { } } // reco - meanReco[ieta] = (sumWkReco[ieta][1] >= 1.0) ? (sumPmwkReco[ieta][1][1] / sumWkReco[ieta][1]) : std::numeric_limits::quiet_NaN(); + meanReco[ieta] = (sumWkReco[ieta][1] > KFloatEpsilon) ? (sumPmwkReco[ieta][1][1] / sumWkReco[ieta][1]) : std::numeric_limits::quiet_NaN(); c2Reco[ieta] = std::numeric_limits::quiet_NaN(); c3Reco[ieta] = std::numeric_limits::quiet_NaN(); p1kBarReco[ieta] = std::numeric_limits::quiet_NaN(); @@ -1732,7 +1754,7 @@ struct RadialFlowDecorr { } } // reco, efficiency-corrected - meanRecoEffCor[ieta] = (sumWkRecoEffCor[ieta][1] >= 1.0) ? (sumPmwkRecoEffCor[ieta][1][1] / sumWkRecoEffCor[ieta][1]) : std::numeric_limits::quiet_NaN(); + meanRecoEffCor[ieta] = (sumWkRecoEffCor[ieta][1] > KFloatEpsilon) ? (sumPmwkRecoEffCor[ieta][1][1] / sumWkRecoEffCor[ieta][1]) : std::numeric_limits::quiet_NaN(); c2RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); c3RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); p1kBarRecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); @@ -1755,15 +1777,15 @@ struct RadialFlowDecorr { histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkRecoEffCor[ieta][1]); histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkRecoEffCor[ieta][1]); - if (sumWkTru[ieta][1] > 1.0f) { + if (sumWkTru[ieta][1] > KFloatEpsilon) { histos.fill(HIST("MCGen/Prof_Cent_NEta_MeanpT"), cent, ieta, meanTru[ieta]); histos.fill(HIST("MCGen/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanTru[ieta]); } - if (sumWkReco[ieta][1] > 1.0f) { + if (sumWkReco[ieta][1] > KFloatEpsilon) { histos.fill(HIST("MCReco/Prof_Cent_NEta_MeanpT"), cent, ieta, meanReco[ieta]); histos.fill(HIST("MCReco/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanReco[ieta]); } - if (sumWkRecoEffCor[ieta][1] > 1.0f) { + if (sumWkRecoEffCor[ieta][1] > KFloatEpsilon) { histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_MeanpT"), cent, ieta, meanRecoEffCor[ieta]); histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanRecoEffCor[ieta]); } @@ -1867,8 +1889,9 @@ struct RadialFlowDecorr { for (int ietaC = 1; ietaC < nEta; ++ietaC) { float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); + const float w = etaUp[ietaA] - etaLw[ietaA]; + const float gap = (ietaA - ietaC) * w; + const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * w; // = cA + cC on the lattice float c2SubTru = (ietaA == ietaC) ? static_cast(c2Tru[ietaA]) : p1kBarTru[ietaA] * p1kBarTru[ietaC]; float c2SubReco = (ietaA == ietaC) ? static_cast(c2Reco[ietaA]) : p1kBarReco[ietaA] * p1kBarReco[ietaC]; @@ -2059,7 +2082,7 @@ struct RadialFlowDecorr { } } - if (sumWi[0] >= 1.0f) { + if (sumWi[0] > KFloatEpsilon) { histos.fill(HIST("Prof_Cent_Nchrec"), cent, sumWi[0]); histos.fill(HIST("Prof_Mult_Nchrec"), coll.multNTracksPV(), sumWi[0]); histos.fill(HIST("Prof_Cent_MeanpT"), cent, sumWipti[0] / sumWi[0]); @@ -2068,7 +2091,7 @@ struct RadialFlowDecorr { for (int ietaA = 0; ietaA < nEta; ++ietaA) { for (int ietaC = 0; ietaC < nEta; ++ietaC) { - if ((sumWi[ietaA] < 1.0f) || (sumWi[ietaC] < 1.0f)) { + if ((sumWi[ietaA] < KFloatEpsilon) || (sumWi[ietaC] < KFloatEpsilon)) { continue; } double wCorrAB = sumWi[ietaA] + sumWi[ietaC]; @@ -2077,7 +2100,7 @@ struct RadialFlowDecorr { histos.fill(HIST("Prof2D_MeanpTSub"), cent, ietaA, ietaC, mptsub); } } - if (sumWi[ietaA] >= 1.0f) { + if (sumWi[ietaA] > KFloatEpsilon) { double mpt = sumWipti[ietaA] / sumWi[ietaA]; if (std::isfinite(mpt)) { histos.fill(HIST("pmean_nch_etabin"), coll.multNTracksPV(), ietaA, mpt); @@ -2177,7 +2200,9 @@ struct RadialFlowDecorr { if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { continue; } + float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); + float w = flatWeight * (1.0f - fake) / eff; if (!std::isfinite(w) || w <= 0.f) { continue; @@ -2203,9 +2228,7 @@ struct RadialFlowDecorr { float mmpt = state.pmeanNchEtabinStep2->GetBinContent(ibx, iby); meanMult[ieta] = sumwk[ieta][1]; - - // mean pT of the bin: valid only with >=1 (weighted) track - mean[ieta] = (sumwk[ieta][1] >= 1.0) ? (sumpmwk[ieta][1][1] / sumwk[ieta][1]) : std::numeric_limits::quiet_NaN(); + mean[ieta] = (sumwk[ieta][1] > KFloatEpsilon) ? (sumpmwk[ieta][1][1] / sumwk[ieta][1]) : std::numeric_limits::quiet_NaN(); // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference c2[ieta] = std::numeric_limits::quiet_NaN(); @@ -2285,8 +2308,9 @@ struct RadialFlowDecorr { for (int ietaC = 1; ietaC < nEta; ++ietaC) { float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); + const float w = etaUp[ietaA] - etaLw[ietaA]; + const float gap = (ietaA - ietaC) * w; + const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * w; // = cA + cC on the lattice float c2Sub = (ietaA == ietaC) ? static_cast(c2[ietaA]) : p1kBar[ietaA] * p1kBar[ietaC]; // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, From b5c070c1803d4ad3464ecc06d7c55e8a9f6cc8ca Mon Sep 17 00:00:00 2001 From: Rahul Verma <110929992+rahulverma012@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:26:19 +0530 Subject: [PATCH 207/648] [PWGCF] Hadron Associated Particle Correlation Task added (#18190) --- .../Tasks/CMakeLists.txt | 5 + .../Tasks/hParticleCorrelation.cxx | 5252 +++++++++++++++++ 2 files changed, 5257 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index b6fd881bf0e..61531e9f56e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -142,3 +142,8 @@ o2physics_add_dpl_workflow(fox-wolfram-correlation SOURCES FoxWolframCorrelation.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hparticle-correlation + SOURCES hParticleCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx new file mode 100644 index 00000000000..9f23c51d17f --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -0,0 +1,5252 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file hParticleCorrelation.cxx +/// \brief Task for same- and mixed-event azimuthal angular correlations of trigger hadrons with charged hadrons, identified particles, and resonances +/// +/// \author Rahul Verma (rahul.verma@iitb.ac.in) :: Durgesh Bhatt (durgesh.bhatt@cern.ch) :: Sadhana Dash (sadhana@phy.iitb.ac.in) + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; // for constants + +namespace o2::aod +{ +namespace resonancecndt +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Candidate to collision +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "_Pos"); //! Positive track +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, Tracks, "_Neg"); //! Negative track + +// Candidate kinematics +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); + +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); + +// Invariant-mass hypotheses +DECLARE_SOA_COLUMN(MPhi1020, mPhi1020, float); +DECLARE_SOA_COLUMN(MKStar892, mKStar892, float); +DECLARE_SOA_COLUMN(MKStar892Bar, mKStar892Bar, float); +DECLARE_SOA_COLUMN(MLambda1520, mLambda1520, float); +DECLARE_SOA_COLUMN(MLambda1520Bar, mLambda1520Bar, float); + +// Mass-region tags +DECLARE_SOA_COLUMN(Phi1020Tag, phi1020Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892Tag, kStar892Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892BarTag, kStar892BarTag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520Tag, lambda1520Tag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); + +} // namespace resonancecndt + +DECLARE_SOA_TABLE(ResonanceCndts, "AOD", "RESONCNDTS", o2::soa::Index<>, + o2::aod::resonancecndt::CollisionId, + o2::aod::resonancecndt::PosTrackId, + o2::aod::resonancecndt::NegTrackId, + + o2::aod::resonancecndt::Pt, + o2::aod::resonancecndt::Eta, + o2::aod::resonancecndt::Phi, + + o2::aod::resonancecndt::Px, + o2::aod::resonancecndt::Py, + o2::aod::resonancecndt::Pz, + + o2::aod::resonancecndt::MPhi1020, + o2::aod::resonancecndt::MKStar892, + o2::aod::resonancecndt::MKStar892Bar, + o2::aod::resonancecndt::MLambda1520, + o2::aod::resonancecndt::MLambda1520Bar, + + o2::aod::resonancecndt::Phi1020Tag, + o2::aod::resonancecndt::KStar892Tag, + o2::aod::resonancecndt::KStar892BarTag, + o2::aod::resonancecndt::Lambda1520Tag, + o2::aod::resonancecndt::Lambda1520BarTag); + +//______________________________________________________________________________ +// Derived correlation tables + +namespace hpcorrcollision +{ +DECLARE_SOA_COLUMN(GlobalCollisionId, globalCollisionId, int64_t); +DECLARE_SOA_COLUMN(OriginalCollisionId, originalCollisionId, int64_t); +DECLARE_SOA_COLUMN(MixingEstimator, mixingEstimator, uint8_t); +DECLARE_SOA_COLUMN(MixingBin, mixingBin, int32_t); +DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); +} // namespace hpcorrcollision + +DECLARE_SOA_TABLE(HPCorrCollisions, "AOD", "HPCORRCOLLS", o2::soa::Index<>, + hpcorrcollision::GlobalCollisionId, + hpcorrcollision::OriginalCollisionId, + o2::aod::origins::DataframeID, + o2::aod::collision::PosZ, + o2::aod::cent::CentFT0C, + o2::aod::cent::CentFT0M, + o2::aod::cent::CentFT0A, + o2::aod::cent::CentFV0A, + hpcorrcollision::MixingEstimator, + hpcorrcollision::MixingBin, + hpcorrcollision::CorrMask); +//______________________________________________________________________________ + +namespace hpcorrtrack +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrCollision, hpCorrCollision); // o2-linter: disable=name/o2-column (Keep HPCorrCollision naming consistent with the existing derived collision table) + +DECLARE_SOA_COLUMN(GlobalColRefId, globalColRefId, int64_t); +DECLARE_SOA_COLUMN(OriginalColRefId, originalColRefId, int64_t); + +DECLARE_SOA_COLUMN(GlobalTrackId, globalTrackId, int64_t); +DECLARE_SOA_COLUMN(OriginalTrackId, originalTrackId, int64_t); + +// Kinematics +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, float); +DECLARE_SOA_COLUMN(TofExpMom, tofExpMom, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); + +// Track quantities +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, float); + +// TOF information +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); +DECLARE_SOA_COLUMN(Beta, beta, float); + +// TPC PID +DECLARE_SOA_COLUMN(TpcNSigmaEl, tpcNSigmaEl, float); +DECLARE_SOA_COLUMN(TpcNSigmaMu, tpcNSigmaMu, float); +DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); +DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); +DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, float); +DECLARE_SOA_COLUMN(TpcNSigmaDe, tpcNSigmaDe, float); + +// TOF PID +DECLARE_SOA_COLUMN(TofNSigmaEl, tofNSigmaEl, float); +DECLARE_SOA_COLUMN(TofNSigmaMu, tofNSigmaMu, float); +DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); +DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); +DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); +DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); + +} // namespace hpcorrtrack + +DECLARE_SOA_TABLE(HPCorrTracks, "AOD", "HPCORRTRKS", o2::soa::Index<>, + hpcorrtrack::HPCorrCollisionId, + + hpcorrtrack::GlobalColRefId, + hpcorrtrack::OriginalColRefId, + + hpcorrtrack::GlobalTrackId, + hpcorrtrack::OriginalTrackId, + + o2::aod::origins::DataframeID, + + hpcorrtrack::Pt, + hpcorrtrack::P, + hpcorrtrack::TpcInnerParam, + hpcorrtrack::TofExpMom, + hpcorrtrack::Eta, + hpcorrtrack::Phi, + hpcorrtrack::Sign, + + hpcorrtrack::DcaXY, + hpcorrtrack::DcaZ, + hpcorrtrack::TpcSignal, + + hpcorrtrack::HasTOF, + hpcorrtrack::Beta, + + hpcorrtrack::TpcNSigmaEl, + hpcorrtrack::TpcNSigmaMu, + hpcorrtrack::TpcNSigmaPi, + hpcorrtrack::TpcNSigmaKa, + hpcorrtrack::TpcNSigmaPr, + hpcorrtrack::TpcNSigmaDe, + + hpcorrtrack::TofNSigmaEl, + hpcorrtrack::TofNSigmaMu, + hpcorrtrack::TofNSigmaPi, + hpcorrtrack::TofNSigmaKa, + hpcorrtrack::TofNSigmaPr, + hpcorrtrack::TofNSigmaDe); + +//______________________________________________________________________________ +namespace hpcorrresonance +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrCollision, hpCorrCollision); // o2-linter: disable=name/o2-column (Keep HPCorrCollision naming consistent with the existing derived collision table) +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, HPCorrTracks, "_Pos"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, HPCorrTracks, "_Neg"); + +// Collision references +DECLARE_SOA_COLUMN(GlobalColRefId, globalColRefId, int64_t); +DECLARE_SOA_COLUMN(OriginalColRefId, originalColRefId, int64_t); + +// Positive-daughter track references +DECLARE_SOA_COLUMN(GlobalPosTrackRefId, globalPosTrackRefId, int64_t); +DECLARE_SOA_COLUMN(OriginalPosTrackRefId, originalPosTrackRefId, int64_t); + +// Negative-daughter track references +DECLARE_SOA_COLUMN(GlobalNegTrackRefId, globalNegTrackRefId, int64_t); +DECLARE_SOA_COLUMN(OriginalNegTrackRefId, originalNegTrackRefId, int64_t); + +// Resonance references +DECLARE_SOA_COLUMN(GlobalResonanceId, globalResonanceId, int64_t); +DECLARE_SOA_COLUMN(OriginalResonanceId, originalResonanceId, int64_t); + +// Resonance kinematics +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); + +// Invariant masses +DECLARE_SOA_COLUMN(MPhi1020, mPhi1020, float); +DECLARE_SOA_COLUMN(MKStar892, mKStar892, float); +DECLARE_SOA_COLUMN(MKStar892Bar, mKStar892Bar, float); +DECLARE_SOA_COLUMN(MLambda1520, mLambda1520, float); +DECLARE_SOA_COLUMN(MLambda1520Bar, mLambda1520Bar, float); + +// Mass-region / resonance tags +DECLARE_SOA_COLUMN(Phi1020Tag, phi1020Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892Tag, kStar892Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892BarTag, kStar892BarTag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520Tag, lambda1520Tag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); + +} // namespace hpcorrresonance + +DECLARE_SOA_TABLE(HPCorrResonances, "AOD", "HPCORRRESOS", o2::soa::Index<>, + + // Derived-table relations + hpcorrresonance::HPCorrCollisionId, + hpcorrresonance::PosTrackId, + hpcorrresonance::NegTrackId, + + // Collision bookkeeping + hpcorrresonance::GlobalColRefId, + hpcorrresonance::OriginalColRefId, + + // Positive-track bookkeeping + hpcorrresonance::GlobalPosTrackRefId, + hpcorrresonance::OriginalPosTrackRefId, + + // Negative-track bookkeeping + hpcorrresonance::GlobalNegTrackRefId, + hpcorrresonance::OriginalNegTrackRefId, + + // Resonance bookkeeping + hpcorrresonance::GlobalResonanceId, + hpcorrresonance::OriginalResonanceId, + + // Source dataframe + o2::aod::origins::DataframeID, + + // Resonance kinematics + hpcorrresonance::Pt, + hpcorrresonance::Eta, + hpcorrresonance::Phi, + + // Invariant masses + hpcorrresonance::MPhi1020, + hpcorrresonance::MKStar892, + hpcorrresonance::MKStar892Bar, + hpcorrresonance::MLambda1520, + hpcorrresonance::MLambda1520Bar, + + // Resonance tags + hpcorrresonance::Phi1020Tag, + hpcorrresonance::KStar892Tag, + hpcorrresonance::KStar892BarTag, + hpcorrresonance::Lambda1520Tag, + hpcorrresonance::Lambda1520BarTag); +} // namespace o2::aod + +// Delta phi in [-pi/2, 3pi/2) +float computeDeltaPhi(float particlePhi, float leadPhi) +{ + return RecoDecay::constrainAngle(particlePhi - leadPhi, -o2::constants::math::PIHalf); +} + +template +float computeRapidity(const T& track, const float mass) +{ + const float energy = RecoDecay::e(track.px(), track.py(), track.pz(), mass); + return 0.5f * std::log((energy + track.pz()) / (energy - track.pz())); +} + +template +inline T getCfg(const auto& cfgLabelledArray, int32_t row, int32_t col) +{ + double val = cfgLabelledArray->get(row, col); // Always stored as double + + if constexpr (std::is_same_v) { + return val != 0.0; // will return anything nonzero as true + } else if constexpr (std::is_same_v) { + // cppcheck-suppress suspiciousFloatingPointCast + return static_cast(val); + } else if constexpr (std::is_same_v) { + return val; + } else if constexpr (std::is_same_v) { + return static_cast(val); + } else { + static_assert( + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, + "getCfg() only supports T = bool, float, double or int"); + } +} + +int binarySearchVector(const int64_t Key, const std::vector& List, int low, int high) // v2 : list by reference +{ + while (low <= high) { + int mid = low + (high - low) / 2; + if (Key == List[mid]) { + return mid; + } + + if (Key > List[mid]) { + low = mid + 1; // If Key is greater, ignore left half, update the low + } else { + high = mid - 1; + } // If Key is smaller, ignore right half, update the high + } + return -1; // Element is not present +} + +// do a fastest search in an sorted array. ==> Binary Search in an array. +template +bool checkTrackInList(const T& track, const std::vector& ParticleList) // v2 : list by reference +{ + return static_cast(binarySearchVector(track.globalIndex(), ParticleList, 0, ParticleList.size() - 1) != -1); +} + +enum CollisionRejectionTag { + kCollAccepted = 0, + + // Basic event selection + kCollRejectSel8, + kCollRejectVertexZ, + kCollRejectTriggerTVX, + + // TF / ROF borders + kCollRejectITSROFrameBorder, + kCollRejectTimeFrameBorder, + + // Vertex quality + kCollRejectVertexITSTPC, + kCollRejectGoodZvtxFT0vsPV, + kCollRejectVertexTOFmatched, + kCollRejectVertexTRDmatched, + kCollRejectGoodITSLayersAll, + + // Pileup / neighbouring collisions + kCollRejectSameBunchPileup, + kCollRejectCollInTimeRangeStandard, + kCollRejectCollInTimeRangeStrict, + kCollRejectCollInTimeRangeNarrow, + kCollRejectCollInRofStandard, + kCollRejectCollInRofStrict, + kCollRejectHighMultCollInPrevRof, + + // INEL classes + kCollRejectINELgt0, + kCollRejectINELgt1, + + // Multiplicity + kCollRejectMultiplicityLow, + kCollRejectMultiplicityHigh, + + // Centrality + kCollRejectCentralityLow, + kCollRejectCentralityHigh, + + // Occupancy + kCollRejectOccupancyLow, + kCollRejectOccupancyHigh, + + // Interaction rate + kCollRejectInteractionRateLow, + kCollRejectInteractionRateHigh, + + // RCT + kCollRejectRCT, + kCollRejectCorrelationRCT, + + kNCollisionRejectionTags +}; + +enum TrackRejectionTag { + kTrackAccepted = 0, + + // Kinematics + kTrackRejectPt, + kTrackRejectEta, + kTrackRejectMomentum, + kTrackRejectCharge, + + // Standard O2 track selections + kTrackRejectQualityTrack, + kTrackRejectQualityTrackITS, + kTrackRejectQualityTrackTPC, + kTrackRejectPrimaryTrack, + kTrackRejectInAcceptanceTrack, + kTrackRejectGlobalTrack, + kTrackRejectGlobalTrackWoDCA, + kTrackRejectGlobalTrackWoPtEta, + kTrackRejectGlobalTrackWoTPCCluster, + kTrackRejectGlobalTrackWoDCATPCCluster, + kTrackRejectPVContributor, + + // Detector presence + kTrackRejectITS, + kTrackRejectTPC, + kTrackRejectTOF, + kTrackRejectTRD, + + // TPC quality + kTrackRejectTPCNClsFound, + kTrackRejectTPCCrossedRows, + kTrackRejectTPCCrossedRowsOverFindable, + kTrackRejectTPCFoundOverFindable, + kTrackRejectTPCFractionShared, + kTrackRejectTPCChi2, + + // ITS quality + kTrackRejectITSNCls, + kTrackRejectITSNClsInnerBarrel, + kTrackRejectITSChi2, + + // DCA + kTrackRejectFixedDCAxy, + kTrackRejectFixedDCAz, + kTrackRejectPtDependentDCAxy, + kTrackRejectPtDependentDCAz, + + kNTrackRejectionTags +}; + +enum QAStageEnum { + kBeforeSelection = 0, + kAfterSelection, + kNQAStages +}; + +static constexpr std::array QAStageDire = { + "BeforeSelection/", + "AfterSelection/"}; + +enum EventQABin { + kEventAllFiltered = 0, + kEventPassedSelCollision, + kEventPassedMinFilteredTracks, + kEventPassedMinSelectedTracks, + kNEventQABins +}; + +enum TrackQABin { + kTrackAllFiltered = 0, + kTrackPassedSelectionTrack, + kNTrackQABins +}; + +enum DataFrameQABin { + kDFWithZeroFilteredColls = 0, + kNDataFrameQABins +}; + +enum EventTypeEnum { + kSameEvent = 0, + kMixedEvent, + kNEventTypes +}; + +static constexpr std::array EventTypeDire = { + "SE/", + "ME/"}; + +enum PairTypeEnum { + kHH = 0, + kHId, + kHPhi, + kHKStar, + kHKStarBar, + kHLambda, + kHLambdaBar, + kPhiPhi, + kNPairTypes +}; + +static constexpr std::array PairTypeDire = { + "hh/", + "hIdentified/", + "hPhi/", + "hKStar/", + "hKStarBar/", + "hLambda/", + "hLambdaBar/", + "PhiPhi/"}; + +enum AnalysisTypeEnum { + kQA = 0, + kCorr, + kNAnalysisTypes +}; + +static constexpr std::array AnalysisTypeDire = { + "QA/", + "Corr/"}; + +enum CorrRoleEnum { + kTrigger = 0, + kAssocLowPt, + kAssocHighPt, + kNCorrRoles +}; + +static constexpr std::array CorrRoleDire = { + "Trigger/", + "AssocLowPt/", + "AssocHighPt/"}; + +enum PrimVtxMassRegionTag : uint8_t { + kMassOutside = 0, + kMassPeak = 1, + kMassLSB = 2, + kMassRSB = 3, + kMassNone = 4, + kNMassRegions = 5 +}; + +static constexpr std::array MassRegionDire = { + "Outside/", + "Peak/", + "LSB/", + "RSB/", + ""}; + +enum DauTypeEnum { + kPosDau = 0, + kNegDau, + kNDauTypes +}; + +static constexpr std::array DauTypeDire = { + "PosDau/", + "NegDau/"}; + +// template +// uint8_t getMassRegionTag(T mass, T lsbLow, T lsbUp, T peakLow, T peakUp, T rsbLow, T rsbUp) +uint8_t getMassRegionTag(float mass, float lsbLow, float lsbUp, float peakLow, float peakUp, float rsbLow, float rsbUp) +{ + if (lsbLow < mass && mass < lsbUp) { + return kMassLSB; + } + if (peakLow < mass && mass < peakUp) { + return kMassPeak; + } + if (rsbLow < mass && mass < rsbUp) { + return kMassRSB; + } + return kMassOutside; +} + +enum CorrCountTypeEnum { + kCountHH = 0, + kCountHPi, + kCountHKa, + kCountHPr, + kCountHPhi, + kCountHKStar, + kCountHKStarBar, + kCountHLambda, + kCountHLambdaBar, + kNCorrCountTypes +}; + +enum CorrCountPtEnum { + kCountLowPt = 0, + kCountHighPt, + kNCorrCountPt +}; + +static constexpr int NCorrCountChannels = static_cast(kNCorrCountTypes) * static_cast(kNCorrCountPt); +static constexpr std::array CorrCountTypeName = { + "hh", + "hPi", + "hKa", + "hPr", + "hPhi", + "hKStar", + "hKStarBar", + "hLambda", + "hLambdaBar"}; + +using CorrCountArray = std::array, kNCorrCountTypes>; +enum ResoCorrCountTypeEnum { + kResoCountHPhi = 0, + kResoCountHKStar, + kResoCountHKStarBar, + kResoCountHLambda, + kResoCountHLambdaBar, + kNResoCorrCountTypes +}; + +using ResoRegionCountArray = std::array, kNCorrCountPt>, kNResoCorrCountTypes>; + +struct MixingBinStatus { + uint64_t nCollisions = 0; // nCollisions = total selected collisions entering this mixing bin in this dataframe + CorrCountArray nCorrCollisions{}; // nCorrCollisions[hPhi][LowPt] = how many COLLISIONS in this mixing bin had >=1 hPhi-low correlation +}; + +enum PidEnum { + kPi = 0, + kKa, + kPr, + kEl, + kMu, + kDe, + kNPid +}; + +static constexpr std::array PidTypeDire = { + "Pi/", + "Ka/", + "Pr/", + "El/", + "Mu/", + "De/"}; + +enum CutSettingEnum { + kThrPforTOF = 0, + kIdCutTypeLowP, + kNSigmaTPCLowP, + kNSigmaTOFLowP, + kNSigmaRadLowP, + kIdCutTypeHighP, + kNSigmaTPCHighP, + kNSigmaTOFHighP, + kNSigmaRadHighP, + kDoVetoOthers, + kDoRelativeTPCcheck, + kDoRelativeTOFcheck, + kDoRelativeTPCTOFcheck, + kNCutSettings +}; + +enum VetoSettingEnum { + kDoVetoTPC = 0, + kDoVetoTOF, + kVetoTPC, + kVetoTOF, + kNVetoSettings +}; + +enum IdentificationType { + kTPCidentified = 0, + kTPCTOFidentified, + kUnidentified, + kNIdentificationTypes +}; + +enum TpcTofCutType { + kCircularCut = 0, + kRectangularCut, + kEllipsoidalCut, + kNCutTypes +}; + +enum MixingEstimatorEnum { + kMixCentFT0C = 0, + kMixCentFT0M, + kMixCentFT0A, + kMixCentFV0A, + kNMixingEstimators +}; + +enum CorrPresenceMask : uint64_t { + kMaskHHLowPt = 1ULL << 0, + kMaskHHHighPt = 1ULL << 1, + kMaskHPiLowPt = 1ULL << 2, + kMaskHPiHighPt = 1ULL << 3, + kMaskHKaLowPt = 1ULL << 4, + kMaskHKaHighPt = 1ULL << 5, + kMaskHPrLowPt = 1ULL << 6, + kMaskHPrHighPt = 1ULL << 7, + + kMaskHPhiLowPt = 1ULL << 8, + kMaskHPhiHighPt = 1ULL << 9, + kMaskHKStarLowPt = 1ULL << 10, + kMaskHKStarHighPt = 1ULL << 11, + kMaskHKStarBarLowPt = 1ULL << 12, + kMaskHKStarBarHighPt = 1ULL << 13, + kMaskHLambdaLowPt = 1ULL << 14, + kMaskHLambdaHighPt = 1ULL << 15, + kMaskHLambdaBarLowPt = 1ULL << 16, + kMaskHLambdaBarHighPt = 1ULL << 17, + + kMaskHPhiLowPtPeak = 1ULL << 18, + kMaskHPhiLowPtLSB = 1ULL << 19, + kMaskHPhiLowPtRSB = 1ULL << 20, + kMaskHPhiHighPtPeak = 1ULL << 21, + kMaskHPhiHighPtLSB = 1ULL << 22, + kMaskHPhiHighPtRSB = 1ULL << 23, + + kMaskHKStarLowPtPeak = 1ULL << 24, + kMaskHKStarLowPtLSB = 1ULL << 25, + kMaskHKStarLowPtRSB = 1ULL << 26, + kMaskHKStarHighPtPeak = 1ULL << 27, + kMaskHKStarHighPtLSB = 1ULL << 28, + kMaskHKStarHighPtRSB = 1ULL << 29, + + kMaskHKStarBarLowPtPeak = 1ULL << 30, + kMaskHKStarBarLowPtLSB = 1ULL << 31, + kMaskHKStarBarLowPtRSB = 1ULL << 32, + kMaskHKStarBarHighPtPeak = 1ULL << 33, + kMaskHKStarBarHighPtLSB = 1ULL << 34, + kMaskHKStarBarHighPtRSB = 1ULL << 35, + + kMaskHLambdaLowPtPeak = 1ULL << 36, + kMaskHLambdaLowPtLSB = 1ULL << 37, + kMaskHLambdaLowPtRSB = 1ULL << 38, + kMaskHLambdaHighPtPeak = 1ULL << 39, + kMaskHLambdaHighPtLSB = 1ULL << 40, + kMaskHLambdaHighPtRSB = 1ULL << 41, + + kMaskHLambdaBarLowPtPeak = 1ULL << 42, + kMaskHLambdaBarLowPtLSB = 1ULL << 43, + kMaskHLambdaBarLowPtRSB = 1ULL << 44, + kMaskHLambdaBarHighPtPeak = 1ULL << 45, + kMaskHLambdaBarHighPtLSB = 1ULL << 46, + kMaskHLambdaBarHighPtRSB = 1ULL << 47, + + kMaskAll = (1ULL << 48) - 1ULL +}; + +constexpr uint64_t getCorrMaskBit(int corrType, int ptType) +{ + return 1ULL << (corrType * kNCorrCountPt + ptType); +} + +constexpr uint64_t getResoRegionMaskBit(int resoType, int ptType, int massRegion) +{ + if (massRegion < kMassPeak || massRegion > kMassRSB) { + return 0ULL; + } + return 1ULL << (18 + resoType * 6 + ptType * 3 + (massRegion - kMassPeak)); +} + +static constexpr std::array, kNPid> DefaultPIDcheckValues = {{ + {0.7, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pi + {0.5, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Ka + {0.8, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pr + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // El + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Mu + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1} // De +}}; + +static constexpr std::array, kNPid> DefaultPidVetoValues = {{ + {1, 1, 3.0, 3.0}, // Pi + {1, 1, 3.0, 3.0}, // Ka + {1, 1, 3.0, 3.0}, // Pr + {0, 0, 3.0, 3.0}, // El + {0, 0, 3.0, 3.0}, // Mu + {0, 0, 3.0, 3.0} // De +}}; + +//______________________________________________________________________________________________________________ +// Common functions independent of configurables used across the tasks + +template +bool relativeIdOthersTPC(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tpcNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tpcNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tpcNSigmaPr()); + } else { + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tpcNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tpcNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tpcNSigmaDe()); + // } + + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tpcNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tpcNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tpcNSigmaPr()) <= distSpecies) { + return false; + } + } + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tpcNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tpcNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tpcNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTOF(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tofNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tofNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tofNSigmaPr()); + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tofNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tofNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tofNSigmaDe()); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tofNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tofNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tofNSigmaPr()) <= distSpecies) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tofNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tofNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tofNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTPCTOF(const T& track) +{ + float distSpeciesSq = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpeciesSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + distSpeciesSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + distSpeciesSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpeciesSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // } else if constexpr (pidMode == kMu) { + // distSpeciesSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // } else if constexpr (pidMode == kDe) { + // distSpeciesSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + const float distSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kKa) { + const float distSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kPr) { + const float distSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + if (distSq <= distSpeciesSq) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) { + // const float distSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kMu) { + // const float distSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kDe) { + // const float distSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + return true; +} + +template +bool selIdRectangularCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC && std::fabs(track.tofNSigmaPi()) < nSigmaTOF; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC && std::fabs(track.tofNSigmaKa()) < nSigmaTOF; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC && std::fabs(track.tofNSigmaPr()) < nSigmaTOF; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC && std::fabs(track.tofNSigmaEl()) < nSigmaTOF; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC && std::fabs(track.tofNSigmaMu()) < nSigmaTOF; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC && std::fabs(track.tofNSigmaDe()) < nSigmaTOF; + } else { + return false; + } +} + +template +bool selIdEllipsoidalCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + float tpc = track.tpcNSigmaPi() / nSigmaTPC; + float tof = track.tofNSigmaPi() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kKa) { + float tpc = track.tpcNSigmaKa() / nSigmaTPC; + float tof = track.tofNSigmaKa() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kPr) { + float tpc = track.tpcNSigmaPr() / nSigmaTPC; + float tof = track.tofNSigmaPr() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kEl) { + float tpc = track.tpcNSigmaEl() / nSigmaTPC; + float tof = track.tofNSigmaEl() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kMu) { + float tpc = track.tpcNSigmaMu() / nSigmaTPC; + float tof = track.tofNSigmaMu() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kDe) { + float tpc = track.tpcNSigmaDe() / nSigmaTPC; + float tof = track.tofNSigmaDe() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else { + return false; + } +} + +// Circular cut : (tpcNSigma^2 + tofNSigma^2) < nSigmaSquaredRad ; for 3 sigma, nSigmaSquaredRad = 9.0 +template +constexpr bool selIdCircularCut(const T& track, const float& nSigmaSquaredRad) +{ + float tpc = 0.f; + float tof = 0.f; + + if constexpr (pidMode == kPi) { + tpc = track.tpcNSigmaPi(); + tof = track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + tpc = track.tpcNSigmaKa(); + tof = track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + tpc = track.tpcNSigmaPr(); + tof = track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + tpc = track.tpcNSigmaEl(); + tof = track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + tpc = track.tpcNSigmaMu(); + tof = track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + tpc = track.tpcNSigmaDe(); + tof = track.tofNSigmaDe(); + } else { + return false; // unknown pidMode + } + return (tpc * tpc + tof * tof) < nSigmaSquaredRad; +} + +template +bool applyVetoOthersTPC(const T& track, const std::array& doVetoTPC, const std::array& vetoTPC) +{ + if constexpr (pidMode != kPi) { + if (doVetoTPC[kPi] && std::fabs(track.tpcNSigmaPi()) < vetoTPC[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTPC[kKa] && std::fabs(track.tpcNSigmaKa()) < vetoTPC[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTPC[kPr] && std::fabs(track.tpcNSigmaPr()) < vetoTPC[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTPC[kEl] && std::fabs(track.tpcNSigmaEl()) < vetoTPC[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTPC[kMu] && std::fabs(track.tpcNSigmaMu()) < vetoTPC[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTPC[kDe] && std::fabs(track.tpcNSigmaDe()) < vetoTPC[kDe]) { + return false; + } + } + + return true; +} + +template +bool applyVetoOthersTOF(const T& track, const std::array& doVetoTOF, const std::array& vetoTOF) +{ + if constexpr (pidMode != kPi) { + if (doVetoTOF[kPi] && std::fabs(track.tofNSigmaPi()) < vetoTOF[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTOF[kKa] && std::fabs(track.tofNSigmaKa()) < vetoTOF[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTOF[kPr] && std::fabs(track.tofNSigmaPr()) < vetoTOF[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTOF[kEl] && std::fabs(track.tofNSigmaEl()) < vetoTOF[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTOF[kMu] && std::fabs(track.tofNSigmaMu()) < vetoTOF[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTOF[kDe] && std::fabs(track.tofNSigmaDe()) < vetoTOF[kDe]) { + return false; + } + } + + return true; +} + +// Common Helper functions are over. +//__________________________________________________________________________________________________________________________ + +//__________________________________________________________________________________________________________________________ +// Common Histograms booking functions - to be used inside +template +void addBasicTrackQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisPt, const AxisSpec& axisEta, const AxisSpec& axisPhi, const AxisSpec& axisDcaXY, const AxisSpec& axisDcaZ, const AxisSpec& axisSign) +{ + histReg.add((basePath + "P").c_str(), "p;p (GeV/c);Counts", kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "p_{T};p_{T} (GeV/c);Counts", kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "#eta;#eta;Counts", kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "#varphi;#varphi;Counts", kTH1F, {axisPhi}); + histReg.add((basePath + "DCAxy").c_str(), "DCA_{xy};DCA_{xy} (cm);Counts", kTH1F, {axisDcaXY}); + histReg.add((basePath + "DCAz").c_str(), "DCA_{z};DCA_{z} (cm);Counts", kTH1F, {axisDcaZ}); + histReg.add((basePath + "Sign").c_str(), "Sign;Sign;Counts", kTH1F, {axisSign}); + histReg.add((basePath + "DCAxyVsPt").c_str(), "DCA_{xy} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {axisPt, axisDcaXY}); + histReg.add((basePath + "DCAzVsPt").c_str(), "DCA_{z} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {axisPt, axisDcaZ}); +} + +template +void addIdentifiedQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisRapidity, const AxisSpec& axisTPCSignal, const AxisSpec& axisTOFBeta, const AxisSpec& axisTPCNSigma, const AxisSpec& axisTOFNSigma, const AxisSpec& axisIdMethod) +{ + histReg.add((basePath + "Rapidity").c_str(), "Rapidity;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", HistType::kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", HistType::kTH2F, {axisP, axisTOFBeta}); + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + histReg.add((basePath + "IdMethodVsP").c_str(), "Identification method vs p;p (GeV/c);ID method", HistType::kTH2F, {axisP, axisIdMethod}); +} + +// Common Histograms booking functions are over +//__________________________________________________________________________________________________________________________ + +struct HParticleCorrelationResonanceProducer { + Produces resonanceCndts; + HistogramRegistry resonanceQAPlots{"resonanceQAPlots", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + static constexpr float ResonancePidNSigmaMax = 3.5f; + + struct : ConfigurableGroup { + Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + Configurable requireSel8{"requireSel8", true, "Require sel8 event selection"}; + Configurable cutZvertex{"cutZvertex", 10.0f, "Maximum |z_{vtx}| (cm)"}; + } cfgEvent; + + struct : ConfigurableGroup { + Configurable ptMin{"ptMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable ptMax{"ptMax", 100.0f, "Maximum track pT (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta|"}; + + Configurable useFixedDCAxy{"useFixedDCAxy", false, "Apply fixed DCAxy cut"}; + Configurable dcaXYMax{"dcaXYMax", 0.1f, "Maximum |DCAxy| (cm)"}; + + Configurable useFixedDCAz{"useFixedDCAz", false, "Apply fixed DCAz cut"}; + Configurable dcaZMax{"dcaZMax", 0.2f, "Maximum |DCAz| (cm)"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable doPhi1020{"doPhi1020", true, "Build Phi(1020) candidates"}; + Configurable doKStar892{"doKStar892", true, "Build K*(892)0 candidates"}; + Configurable doKStar892Bar{"doKStar892Bar", true, "Build anti-K*(892)0 candidates"}; + Configurable doLambda1520{"doLambda1520", true, "Build Lambda(1520) candidates"}; + Configurable doLambda1520Bar{"doLambda1520Bar", true, "Build anti-Lambda(1520) candidates"}; + } cfgResonances; + + struct : ConfigurableGroup { + Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020Mass; + + struct : ConfigurableGroup { + Configurable kstar892PeakLow{"kstar892PeakLow", 0.846f, "K*(892)0 peak lower mass"}; + Configurable kstar892PeakUp{"kstar892PeakUp", 0.946f, "K*(892)0 peak upper mass"}; + Configurable kstar892LSBLow{"kstar892LSBLow", 0.700f, "K*(892)0 LSB lower mass"}; + Configurable kstar892LSBUp{"kstar892LSBUp", 0.780f, "K*(892)0 LSB upper mass"}; + Configurable kstar892RSBLow{"kstar892RSBLow", 1.010f, "K*(892)0 RSB lower mass"}; + Configurable kstar892RSBUp{"kstar892RSBUp", 1.090f, "K*(892)0 RSB upper mass"}; + } cfgKstar892Mass; + + struct : ConfigurableGroup { + Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) peak lower mass"}; + Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) peak upper mass"}; + Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) LSB lower mass"}; + Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) LSB upper mass"}; + Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) RSB lower mass"}; + Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) RSB upper mass"}; + } cfgLambda1520Mass; + + void init(InitContext const&) + { + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + + resonanceQAPlots.add("mPhi1020", "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisPhi1020Mass}); + resonanceQAPlots.add("mKStar892", "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mKStar892Bar", "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mLambda1520", "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + resonanceQAPlots.add("mLambda1520Bar", "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + } + + template + bool selPion(const T& track) + { + if (std::abs(track.tpcNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selKaon(const T& track) + { + if (std::abs(track.tpcNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selProton(const T& track) + { + if (std::abs(track.tpcNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + // Event Filter + Filter eventFilter = (!cfgEvent.requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.ptMin) && (o2::aod::track::pt < cfgTrackCuts.ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.etaMax); + Filter dcaFilter = ((!cfgTrackCuts.useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.dcaXYMax)) && + ((!cfgTrackCuts.useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + SliceCache cache; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + int dfNumber = 0; + // void processNothing(aod::HPCorrCollisions const&) + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelationResonanceProducer, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, o2::aod::Origins const& /*Origins*/) + { + dfNumber++; + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size(); + } + + float ePosPi = 0.0f; + float ePosKa = 0.0f; + float ePosPr = 0.0f; + + float eNegPi = 0.0f; + float eNegKa = 0.0f; + float eNegPr = 0.0f; + + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + float px = 0.0f; + float py = 0.0f; + float pz = 0.0f; + float p = 0.0f; + + float mPhi1020 = -1.0f; + float mKStar892 = -1.0f; + float mKStar892Bar = -1.0f; + float mLambda1520 = -1.0f; + float mLambda1520Bar = -1.0f; + + uint8_t phi1020Tag = kMassOutside; + uint8_t kStar892Tag = kMassOutside; + uint8_t kStar892BarTag = kMassOutside; + uint8_t lambda1520Tag = kMassOutside; + uint8_t lambda1520BarTag = kMassOutside; + + bool posIsPi = false, posIsKa = false, posIsPr = false, negIsPi = false, negIsKa = false, negIsPr = false; + bool fillTable = false; + + for (const auto& collision : collisions) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + for (const auto& posTrack : posTracksPerColl) { + posIsPi = selPion(posTrack); + posIsKa = selKaon(posTrack); + posIsPr = selProton(posTrack); + if (!(posIsPi || posIsKa || posIsPr)) { + continue; + } + // Precompute energies for fast speed + ePosPi = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassPiPlus); + ePosKa = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassKPlus); + ePosPr = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassProton); + for (const auto& negTrack : negTracksPerColl) { + negIsPi = selPion(negTrack); + negIsKa = selKaon(negTrack); + negIsPr = selProton(negTrack); + if (!(negIsPi || negIsKa || negIsPr)) { + continue; + } + + eNegPi = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassPiPlus); + eNegKa = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassKPlus); + eNegPr = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassProton); + + px = posTrack.px() + negTrack.px(); + py = posTrack.py() + negTrack.py(); + pz = posTrack.pz() + negTrack.pz(); + p = RecoDecay::p(px, py, pz); // definition = std::sqrt(px*px+py*py+pz*pz); + + fillTable = false; + + mPhi1020 = -1.0f; + mKStar892 = -1.0f; + mKStar892Bar = -1.0f; + mLambda1520 = -1.0f; + mLambda1520Bar = -1.0f; + + phi1020Tag = kMassOutside; + kStar892Tag = kMassOutside; + kStar892BarTag = kMassOutside; + lambda1520Tag = kMassOutside; + lambda1520BarTag = kMassOutside; + + // phi(1020) -> K+ + K- + if (cfgResonances.doPhi1020 && posIsKa && negIsKa) { + mPhi1020 = RecoDecay::m(p, ePosKa + eNegKa); + phi1020Tag = getMassRegionTag(mPhi1020, cfgPhi1020Mass.phi1020LSBLow, cfgPhi1020Mass.phi1020LSBUp, cfgPhi1020Mass.phi1020PeakLow, cfgPhi1020Mass.phi1020PeakUp, cfgPhi1020Mass.phi1020RSBLow, cfgPhi1020Mass.phi1020RSBUp); + if (phi1020Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mPhi1020"), mPhi1020); + } + + // K(892)* -> K+ + pi- + if (cfgResonances.doKStar892 && posIsKa && negIsPi) { + mKStar892 = RecoDecay::m(p, ePosKa + eNegPi); + kStar892Tag = getMassRegionTag(mKStar892, cfgKstar892Mass.kstar892LSBLow, cfgKstar892Mass.kstar892LSBUp, cfgKstar892Mass.kstar892PeakLow, cfgKstar892Mass.kstar892PeakUp, cfgKstar892Mass.kstar892RSBLow, cfgKstar892Mass.kstar892RSBUp); + if (kStar892Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892"), mKStar892); + } + + // K(892)*Bar -> K- + pi+ + if (cfgResonances.doKStar892Bar && posIsPi && negIsKa) { + mKStar892Bar = RecoDecay::m(p, ePosPi + eNegKa); + kStar892BarTag = getMassRegionTag(mKStar892Bar, cfgKstar892Mass.kstar892LSBLow, cfgKstar892Mass.kstar892LSBUp, cfgKstar892Mass.kstar892PeakLow, cfgKstar892Mass.kstar892PeakUp, cfgKstar892Mass.kstar892RSBLow, cfgKstar892Mass.kstar892RSBUp); + if (kStar892BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892Bar"), mKStar892Bar); + } + + // Λ(1520) -> P+ + Ka- + if (cfgResonances.doLambda1520 && posIsPr && negIsKa) { + mLambda1520 = RecoDecay::m(p, ePosPr + eNegKa); + lambda1520Tag = getMassRegionTag(mLambda1520, cfgLambda1520Mass.lambda1520LSBLow, cfgLambda1520Mass.lambda1520LSBUp, cfgLambda1520Mass.lambda1520PeakLow, cfgLambda1520Mass.lambda1520PeakUp, cfgLambda1520Mass.lambda1520RSBLow, cfgLambda1520Mass.lambda1520RSBUp); + if (lambda1520Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520"), mLambda1520); + } + + // Λ(1520)Bar -> PBar + Ka+ + if (cfgResonances.doLambda1520Bar && posIsKa && negIsPr) { + mLambda1520Bar = RecoDecay::m(p, ePosKa + eNegPr); + lambda1520BarTag = getMassRegionTag(mLambda1520Bar, cfgLambda1520Mass.lambda1520LSBLow, cfgLambda1520Mass.lambda1520LSBUp, cfgLambda1520Mass.lambda1520PeakLow, cfgLambda1520Mass.lambda1520PeakUp, cfgLambda1520Mass.lambda1520RSBLow, cfgLambda1520Mass.lambda1520RSBUp); + if (lambda1520BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520Bar"), mLambda1520Bar); + } + + if (!fillTable) { + continue; + } + pt = RecoDecay::pt(px, py); // definition = std::sqrt(px*px+py*py); + eta = RecoDecay::eta(std::array{px, py, pz}); // definition = 0.5 * std::log((p + pz) / (p - pz + 1e-12)); // 1e-12 protect divide-by-zero + phi = RecoDecay::phi(px, py); // definition = std::atan2(py, px); + + resonanceCndts(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), + pt, eta, phi, px, py, pz, + mPhi1020, mKStar892, mKStar892Bar, mLambda1520, mLambda1520Bar, + phi1020Tag, kStar892Tag, kStar892BarTag, lambda1520Tag, lambda1520BarTag); + } // Neg Tracks + } // Pos Tracks + } // collision Loop is over + } + PROCESS_SWITCH(HParticleCorrelationResonanceProducer, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelationSameEvent { + + Produces derivedCollisions; + Produces derivedTracks; + Produces derivedResonances; + + // Hisogram redistry: + HistogramRegistry seEventQA{"seEventQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry seTrackQA{"seTrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resonanceMassSpectra{"resonanceMassSpectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingQA{"mixingQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + struct : ConfigurableGroup { + Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + // Basic event selection + Configurable cutZvertex{"cutZvertex", 10.f, "Maximum |z_{vtx}| (cm)"}; // Imp : All + Configurable requireSel8{"requireSel8", true, "Require sel8 event selection"}; // Imp : All + Configurable requireTriggerTVX{"requireTriggerTVX", false, "Require kIsTriggerTVX"}; + + Configurable minNFilteredTracks{"minNFilteredTracks", 3, "Minimum number of filtered tracks required per collision"}; + Configurable minNSelectedTracks{"minNSelectedTracks", 3, "Minimum number of selected tracks required per collision"}; + + // Time-frame / ROF border selections + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; + + // Vertex quality + Configurable requireVertexITSTPC{"requireVertexITSTPC", true, "Require kIsVertexITSTPC"}; // Imp : Light Ions + pp + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; // Imp : Light Ions + Configurable requireVertexTOFmatched{"requireVertexTOFmatched", false, "Require kIsVertexTOFmatched"}; + Configurable requireVertexTRDmatched{"requireVertexTRDmatched", false, "Require kIsVertexTRDmatched"}; + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "Require kIsGoodITSLayersAll"}; // Imp : Light Ions + + // Pileup / neighbouring-collision rejection + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; // Imp : Light Ions + pp + Configurable requireNoCollInTimeRangeStandard{"requireNoCollInTimeRangeStandard", false, "Require kNoCollInTimeRangeStandard"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "Require kNoCollInTimeRangeStrict"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "Require kNoCollInTimeRangeNarrow"}; + Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", false, "Require kNoCollInRofStandard"}; + Configurable requireNoCollInRofStrict{"requireNoCollInRofStrict", false, "Require kNoCollInRofStrict"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "Require kNoHighMultCollInPrevRof"}; + + // INEL event classes + Configurable requireINELgt0{"requireINELgt0", false, "Require INEL > 0"}; + Configurable requireINELgt1{"requireINELgt1", false, "Require INEL > 1"}; + + // Multiplicity selection + Configurable useMultiplicitySelection{"useMultiplicitySelection", false, "Apply multiplicity selection"}; + Configurable multiplicityEstimator{"multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; + Configurable minMultiplicity{"minMultiplicity", 0.f, "Minimum multiplicity"}; + Configurable maxMultiplicity{"maxMultiplicity", 1.e9f, "Maximum multiplicity"}; + + // Centrality selection + Configurable useCentralitySelection{"useCentralitySelection", false, "Apply centrality selection"}; + Configurable centralityEstimator{"centralityEstimator", 0, "0: centFT0C, 1: centFT0M, 2: centFT0A, 3: centFV0A"}; + Configurable minCentrality{"minCentrality", 0.f, "Minimum centrality percentile"}; + Configurable maxCentrality{"maxCentrality", 100.f, "Maximum centrality percentile"}; + + // Occupancy selection + Configurable useOccupancySelection{"useOccupancySelection", false, "Apply event occupancy selection"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use FT0C occupancy instead of track occupancy"}; + Configurable minOccupancy{"minOccupancy", -1.f, "Minimum occupancy"}; + Configurable maxOccupancy{"maxOccupancy", 1.e9f, "Maximum occupancy"}; + + // Interaction-rate selection + Configurable useInteractionRateSelection{"useInteractionRateSelection", false, "Apply interaction-rate selection"}; + Configurable minInteractionRate{"minInteractionRate", -1.f, "Minimum interaction rate"}; + Configurable maxInteractionRate{"maxInteractionRate", 1.e9f, "Maximum interaction rate"}; + + // RCT / detector-quality selection + Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Apply Run Condition Table event-quality selection"}; + Configurable requireCorrelationAnalysisRCTFlagChecker{"requireCorrelationAnalysisRCTFlagChecker", false, "Apply correlation-analysis RCT selection"}; + Configurable rctFlagCheckerLabel{"rctFlagCheckerLabel", "CBT_muon_global", "RCT flag checker label"}; + Configurable rctCheckZDC{"rctCheckZDC", false, "Include ZDC in RCT detector-quality check"}; + Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "Treat limited detector acceptance as bad"}; + } cfgEvent; + + struct : ConfigurableGroup { + // Kinematics + Configurable ptMin{"ptMin", 0.2f, "Minimum track pT (GeV/c)"}; + Configurable ptMax{"ptMax", 1.e10f, "Maximum track pT (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta|"}; + + // Standard O2 track-selection flags + Configurable requireGlobalTrack{"requireGlobalTrack", false, "Require isGlobalTrack()"}; + Configurable requireGlobalTrackWoDCA{"requireGlobalTrackWoDCA", false, "Require isGlobalTrackWoDCA()"}; + Configurable requirePVContributor{"requirePVContributor", false, "Require track to be a PV contributor"}; + + // Detector matching / presence + Configurable requireITS{"requireITS", false, "Require ITS information"}; + Configurable requireTPC{"requireTPC", false, "Require TPC information"}; + Configurable requireTOF{"requireTOF", false, "Require TOF information"}; + Configurable requireTRD{"requireTRD", false, "Require TRD information"}; + + // TPC quality + Configurable tpcNClsFoundMin{"tpcNClsFoundMin", 0, "Minimum number of found TPC clusters"}; + Configurable tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 80, "Minimum number of TPC crossed rows"}; + Configurable tpcCrossedRowsOverFindableMin{"tpcCrossedRowsOverFindableMin", 0.f, "Minimum TPC crossed rows / findable clusters"}; + Configurable tpcFoundOverFindableMin{"tpcFoundOverFindableMin", 0.f, "Minimum TPC found / findable clusters"}; + Configurable tpcFractionSharedMax{"tpcFractionSharedMax", 1.f, "Maximum fraction of shared TPC clusters"}; + Configurable tpcChi2NClMin{"tpcChi2NClMin", 0.f, "Minimum TPC chi2 per cluster"}; + Configurable tpcChi2NClMax{"tpcChi2NClMax", 1.e10f, "Maximum TPC chi2 per cluster"}; + + // ITS quality + Configurable itsNClsMin{"itsNClsMin", 0, "Minimum number of ITS clusters"}; + Configurable itsNClsMax{"itsNClsMax", 7, "Maximum number of ITS clusters"}; + Configurable itsNClsInnerBarrelMin{"itsNClsInnerBarrelMin", 0, "Minimum number of ITS inner-barrel clusters"}; + Configurable itsChi2NClMin{"itsChi2NClMin", 0.f, "Minimum ITS chi2 per cluster"}; + Configurable itsChi2NClMax{"itsChi2NClMax", 1.e10f, "Maximum ITS chi2 per cluster"}; + + // Fixed DCA + Configurable useFixedDCAxy{"useFixedDCAxy", true, "Apply fixed |DCAxy| cut"}; + Configurable dcaXYMax{"dcaXYMax", 0.1f, "Maximum fixed |DCAxy| (cm)"}; + Configurable useFixedDCAz{"useFixedDCAz", true, "Apply fixed |DCAz| cut"}; + Configurable dcaZMax{"dcaZMax", 0.2f, "Maximum fixed |DCAz| (cm)"}; + + // pT-dependent DCAxy: + // |DCAxy| < A + B * pT^C + Configurable usePtDependentDCAxy{"usePtDependentDCAxy", false, "Apply pT-dependent DCAxy cut"}; + Configurable dcaXYPtA{"dcaXYPtA", 0.0105f, "A coefficient of pT-dependent DCAxy cut"}; + Configurable dcaXYPtB{"dcaXYPtB", 0.035f, "B coefficient of pT-dependent DCAxy cut"}; + Configurable dcaXYPtC{"dcaXYPtC", 1.1f, "Power C of pT-dependent DCAxy cut"}; + + // pT-dependent DCAz: + // |DCAz| < A + B * pT^C + Configurable usePtDependentDCAz{"usePtDependentDCAz", false, "Apply pT-dependent DCAz cut"}; + Configurable dcaZPtA{"dcaZPtA", 0.1f, "A coefficient of pT-dependent DCAz cut"}; + Configurable dcaZPtB{"dcaZPtB", 0.0f, "B coefficient of pT-dependent DCAz cut"}; + Configurable dcaZPtC{"dcaZPtC", 0.0f, "Power C of pT-dependent DCAz cut"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable triggerPtLow{"triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable triggerPtHigh{"triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable assocPtLowMin{"assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable assocPtLowMax{"assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable assocPtHighMin{"assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable assocPtHighMax{"assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + } cfgPartitions; + + struct : ConfigurableGroup { + Configurable resoRapidityMin{"resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable resoRapidityMax{"resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable phiPtLowMin{"phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable phiPtLowMax{"phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable phiPtHighMin{"phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable phiPtHighMax{"phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable kstarPtLowMin{"kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable kstarPtLowMax{"kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable kstarPtHighMin{"kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable kstarPtHighMax{"kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable kstarBarPtLowMin{"kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable kstarBarPtLowMax{"kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable kstarBarPtHighMin{"kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable kstarBarPtHighMax{"kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable lambda1520PtLowMin{"lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable lambda1520PtLowMax{"lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable lambda1520PtHighMin{"lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable lambda1520PtHighMax{"lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable lambda1520BarPtLowMin{"lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtLowMax{"lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtHighMin{"lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtHighMax{"lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + } cfgResPartitions; + + struct : ConfigurableGroup { + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + + ConfigurableAxis axisCorrSparseTriggerPt{"axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis axisCorrSparseAssocPt{"axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis axisCorrSparseDeltaPhi{"axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis axisCorrSparseDeltaEta{"axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + } cfgAxis; + + Configurable fillDauQAOnce{"fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; + Configurable rejectResoWithAnyTriggerDaughter{"rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; + Configurable requireSelectedTriggerForInvariantMass{"requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; + + struct : ConfigurableGroup { + Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020CorrMass; + + struct : ConfigurableGroup { + Configurable kStar892PeakLow{"kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable kStar892PeakUp{"kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable kStar892LSBLow{"kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable kStar892LSBUp{"kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable kStar892RSBLow{"kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable kStar892RSBUp{"kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + } cfgKStar892CorrMass; + + struct : ConfigurableGroup { + Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + } cfgLambda1520CorrMass; + + struct : ConfigurableGroup { + Configurable pairMask{"pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; + Configurable mixingBin{"mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; + Configurable requireAllPairBits{"requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; + Configurable resetGlobalCountersPerDF{"resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; + } cfgDerivedData; + + struct : ConfigurableGroup { + Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; + Configurable mixingEstimator{"mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis axisVtxMixing{"axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis axisCentMixing{"axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis axisMixingOccupancy{"axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; + } cfgMixing; + + using BinningTypeVtxZFT0C = ColumnBinningPolicy; + using BinningTypeVtxZFT0M = ColumnBinningPolicy; + using BinningTypeVtxZFT0A = ColumnBinningPolicy; + using BinningTypeVtxZFV0A = ColumnBinningPolicy; + + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + + int nMixBins = 0; + + template + int getMixingBin(const C& collision) + { + switch (cfgMixing.mixingEstimator) { + case kMixCentFT0C: + return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); + case kMixCentFT0M: + return colBinningFT0M.getBin({collision.posZ(), collision.centFT0M()}); + case kMixCentFT0A: + return colBinningFT0A.getBin({collision.posZ(), collision.centFT0A()}); + case kMixCentFV0A: + return colBinningFV0A.getBin({collision.posZ(), collision.centFV0A()}); + default: + return -1; + } + } + + template + float getMixingEstimatorValue(const C& collision) + { + switch (cfgMixing.mixingEstimator) { + case kMixCentFT0C: + return collision.centFT0C(); + case kMixCentFT0M: + return collision.centFT0M(); + case kMixCentFT0A: + return collision.centFT0A(); + case kMixCentFV0A: + return collision.centFV0A(); + default: + return -1.0f; + } + } + + struct : ConfigurableGroup { + Configurable> pidConfigSetting{"pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> pidVetoSetting{"pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable cfgId07CheckTofBeta{"cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + } cfgIdCut; + + void init(InitContext const&) + { + if (cfgDebug.printDebugMessages) { + LOGF(info, "Starting init"); + } + // Axes + AxisSpec axisVertexZ = {30, -15., 15., "vrtx_{Z} [cm]"}; + AxisSpec axisCentFT0C = {1200, -10.0, 110.0, "centFT0C(percentile)"}; + AxisSpec axisMult = {150, -1.0, 149.0}; + + const AxisSpec axisEventVertexZ = {60, -15.0f, 15.0f, "z_{vtx} (cm)"}; + const AxisSpec axisEventCentrality = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixingBinStatus = {2, -0.5f, 1.5f, "Mixing-bin status"}; + + const AxisSpec axisP = {200, 0.0f, 10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisTPCInnerParam = {200, 0.0f, 10.0f, "#it{p}_{tpcInnerParam} (GeV/#it{c})"}; + const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; + + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisRapidity = {200, -5.0f, 5.0f, "Rapidity (y)"}; + const AxisSpec axisDcaXY = {100, -5.0f, 5.0f, "dcaXY"}; + const AxisSpec axisDcaZ = {100, -5.0f, 5.0f, "dcaZ"}; + const AxisSpec axisDcaXYwide = {2000, -100.0f, 100.0f, "dcaXY"}; + const AxisSpec axisDcaZwide = {2000, -100.0f, 100.0f, "dcaZ"}; + const AxisSpec axisSign = {10, -5.0f, 5.0f, "track.sign"}; + + const AxisSpec axisTPCSignal = {100, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBeta = {40, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCSignalFine = {10010, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBetaFine = {400, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCNSigma = {200, -10.0f, 10.0f, "n#sigma_{TPC}"}; + const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; + const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.axisDeltaEta, "#Delta#eta"}; + + AxisSpec axisCorrSparseTriggerPt{cfgAxis.axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.axisCorrSparseDeltaEta, "#Delta#eta"}; + + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + const AxisSpec axisResoMassCent = {102, -1.0f, 101.0f, "Centrality (%)"}; + + AxisSpec axisVtxMixSpec{cfgMixing.axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixSpec{cfgMixing.axisCentMixing, "Centrality (%)"}; + AxisSpec axisPoolOccupancy{cfgMixing.axisMixingOccupancy, "N eligible collisions"}; + nMixBins = axisVtxMixSpec.getNbins() * axisCentMixSpec.getNbins(); + const AxisSpec axisMixBin{nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisCorrChannel{NCorrCountChannels, -0.5, static_cast(NCorrCountChannels) - 0.5, "Correlation channel"}; + const AxisSpec axisReadyBins{nMixBins + 1, -0.5, static_cast(nMixBins) + 0.5, "N ready mixing bins"}; + + const int nEventQABins = static_cast(kNEventQABins); + const int nTrackQABins = static_cast(kNTrackQABins); + const int nDataFrameQABins = static_cast(kNDataFrameQABins); + const int nCollisionRejectionBins = static_cast(kNCollisionRejectionTags); + const int nTrackRejectionBins = static_cast(kNTrackRejectionTags); + + auto addEventQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "VertexZ").c_str(), "Collision vertex;z_{vtx} (cm);Counts", HistType::kTH1F, {axisEventVertexZ}); + + histReg.add((basePath + "CentFT0C").c_str(), "FT0C centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0M").c_str(), "FT0M centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0A").c_str(), "FT0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFV0A").c_str(), "FV0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + + histReg.add((basePath + "VtxZVsCentFT0C").c_str(), "Vertex z vs FT0C centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0M").c_str(), "Vertex z vs FT0M centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0A").c_str(), "Vertex z vs FT0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFV0A").c_str(), "Vertex z vs FV0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFT0M").c_str(), "FT0C vs FT0M centrality;FT0C (%);FT0M (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0CVsCentFT0A").c_str(), "FT0C vs FT0A centrality;FT0C (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFT0A").c_str(), "FT0M vs FT0A centrality;FT0M (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFV0A").c_str(), "FT0C vs FV0A centrality;FT0C (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFV0A").c_str(), "FT0M vs FV0A centrality;FT0M (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0AVsCentFV0A").c_str(), "FT0A vs FV0A centrality;FT0A (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "MixingEstimator").c_str(), "Centrality estimator used for mixing;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "VtxZVsMixingEstimator").c_str(), "Physical mixing-bin map;z_{vtx} (cm);Mixing centrality (%)", HistType::kTH2F, {axisVtxMixSpec, axisCentMixSpec}); + histReg.add((basePath + "MixingBin").c_str(), "Mixing-bin index;Mixing bin;Counts", HistType::kTH1F, {axisMixBin}); + histReg.add((basePath + "MixingBinStatus").c_str(), "Mixing-bin validity;Status;Counts", HistType::kTH1F, {axisMixingBinStatus}); + }; + + addEventQAHistos(seEventQA, "SE/Events/BeforeSelection/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelection/"); + + auto hMixStatusBefore = seEventQA.get(HIST("SE/Events/BeforeSelection/MixingBinStatus")); + hMixStatusBefore->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusBefore->GetXaxis()->SetBinLabel(2, "Valid"); + + auto hMixStatusAfter = seEventQA.get(HIST("SE/Events/AfterSelection/MixingBinStatus")); + hMixStatusAfter->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusAfter->GetXaxis()->SetBinLabel(2, "Valid"); + + seEventQA.add("SE/Events/EventSelection", "Event selection QA", HistType::kTH1F, {{nEventQABins, -0.5, static_cast(nEventQABins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackSelection", "Track selection QA", HistType::kTH1F, {{nTrackQABins, -0.5, static_cast(nTrackQABins) - 0.5}}); + seEventQA.add("SE/Events/DataFrameQA", "Dataframe QA", HistType::kTH1F, {{nDataFrameQABins, -0.5, static_cast(nDataFrameQABins) - 0.5}}); + seEventQA.add("SE/Events/CollisionRejection", "Collision rejection reason", HistType::kTH1F, {{nCollisionRejectionBins, -0.5, static_cast(nCollisionRejectionBins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackRejection", "Track rejection reason", HistType::kTH1F, {{nTrackRejectionBins, -0.5, static_cast(nTrackRejectionBins) - 0.5}}); + + auto hEventQA = seEventQA.get(HIST("SE/Events/EventSelection")); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventAllFiltered) + 1, "Filtered collision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedSelCollision) + 1, "Passed selCollision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinFilteredTracks) + 1, "Passed min filtered tracks"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinSelectedTracks) + 1, "Passed min selected tracks"); + + auto hTrackQA = seTrackQA.get(HIST("SE/Tracks/TrackSelection")); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackAllFiltered) + 1, "Filtered track"); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackPassedSelectionTrack) + 1, "Passed selectionTrack"); + + auto hDataFrameQA = seEventQA.get(HIST("SE/Events/DataFrameQA")); + hDataFrameQA->GetXaxis()->SetBinLabel(static_cast(kDFWithZeroFilteredColls) + 1, "Zero filtered collisions"); + + auto hCollRej = seEventQA.get(HIST("SE/Events/CollisionRejection")); + hCollRej->GetXaxis()->SetBinLabel(kCollAccepted + 1, "Accepted"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSel8 + 1, "sel8"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexZ + 1, "Vertex Z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTriggerTVX + 1, "TVX"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectITSROFrameBorder + 1, "ITS ROF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTimeFrameBorder + 1, "TF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexITSTPC + 1, "ITS-TPC vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodZvtxFT0vsPV + 1, "FT0/PV z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTOFmatched + 1, "TOF vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTRDmatched + 1, "TRD vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodITSLayersAll + 1, "Good ITS layers"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSameBunchPileup + 1, "Same bunch pileup"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStandard + 1, "Time range std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStrict + 1, "Time range strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeNarrow + 1, "Time range narrow"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStandard + 1, "ROF std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStrict + 1, "ROF strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectHighMultCollInPrevRof + 1, "High mult prev ROF"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt0 + 1, "INEL > 0"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt1 + 1, "INEL > 1"); + + auto hTrackRej = seTrackQA.get(HIST("SE/Tracks/TrackRejection")); + + hTrackRej->GetXaxis()->SetBinLabel(kTrackAccepted + 1, "Accepted"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPt + 1, "pT"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectEta + 1, "Eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectMomentum + 1, "Momentum"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectCharge + 1, "Charge"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrack + 1, "Quality track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackITS + 1, "Quality ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackTPC + 1, "Quality TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPrimaryTrack + 1, "Primary track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectInAcceptanceTrack + 1, "Acceptance track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrack + 1, "Global track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCA + 1, "Global wo DCA"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoPtEta + 1, "Global wo pT eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoTPCCluster + 1, "Global wo TPC cluster"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCATPCCluster + 1, "Global wo DCA/TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPVContributor + 1, "PV contributor"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITS + 1, "No ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPC + 1, "No TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTOF + 1, "No TOF"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTRD + 1, "No TRD"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCNClsFound + 1, "TPC NCls found"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRows + 1, "TPC crossed rows"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRowsOverFindable + 1, "TPC crossed/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFoundOverFindable + 1, "TPC found/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFractionShared + 1, "TPC shared fraction"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCChi2 + 1, "TPC chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNCls + 1, "ITS NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNClsInnerBarrel + 1, "ITS IB NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSChi2 + 1, "ITS chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAxy + 1, "Fixed DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAz + 1, "Fixed DCAz"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAxy + 1, "pT-dep DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAz + 1, "pT-dep DCAz"); + + //______________________________________________________________________________ + // Correlation histogram booking helpers + auto addTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + histReg.add((basePath + "TPCSignalVsTPCInnerParam").c_str(), "TPC signal vs p_{TPC inner};p_{TPC inner} (GeV/c);TPC signal", kTH2F, {axisTPCInnerParam, axisTPCSignal}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTOFExpMom").c_str(), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", kTH2F, {axisTOFExpMom, axisTOFBeta}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); + }; + + auto addFullTrackPIDQAHistos = [axisP, axisTPCNSigma, axisTOFNSigma](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + }; + + auto addFullTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addTrackQAHistos(histReg, basePath); + + histReg.add((basePath + "TPCInnerParam").c_str(), "TPC inner momentum;p_{TPC inner} (GeV/c);Counts", HistType::kTH1F, {axisTPCInnerParam}); + histReg.add((basePath + "TOFExpMom").c_str(), "TOF expected momentum;p_{TOF} (GeV/c);Counts", HistType::kTH1F, {axisTOFExpMom}); + + histReg.add((basePath + "DCAxyVsP").c_str(), "DCA_{xy} vs p;p (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisP, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTPCInnerParam").c_str(), "DCA_{xy} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTOFExpMom").c_str(), "DCA_{xy} vs p_{TOF};p_{TOF} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaXY}); + + histReg.add((basePath + "DCAzVsP").c_str(), "DCA_{z} vs p;p (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisP, axisDcaZ}); + histReg.add((basePath + "DCAzVsTPCInnerParam").c_str(), "DCA_{z} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaZ}); + histReg.add((basePath + "DCAzVsTOFExpMom").c_str(), "DCA_{z} vs p_{TOF};p_{TOF} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaZ}); + + histReg.add((basePath + "PVsPt").c_str(), "p vs p_{T};p (GeV/c);p_{T} (GeV/c)", HistType::kTH2F, {axisP, axisPt}); + histReg.add((basePath + "PVsTPCInnerParam").c_str(), "p vs p_{TPC inner};p (GeV/c);p_{TPC inner} (GeV/c)", HistType::kTH2F, {axisP, axisTPCInnerParam}); + histReg.add((basePath + "PVsTOFExpMom").c_str(), "p vs p_{TOF};p (GeV/c);p_{TOF} (GeV/c)", HistType::kTH2F, {axisP, axisTOFExpMom}); + + histReg.add((basePath + "TPCSignalVsTOFExpMom").c_str(), "TPC signal vs p_{TOF};p_{TOF} (GeV/c);TPC signal", HistType::kTH2F, {axisTOFExpMom, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTPCInnerParam").c_str(), "TOF #beta vs p_{TPC inner};p_{TPC inner} (GeV/c);#beta", HistType::kTH2F, {axisTPCInnerParam, axisTOFBeta}); + + addFullTrackPIDQAHistos(histReg, basePath + "PID/Pi/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Ka/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Pr/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/El/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Mu/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/De/"); + }; + addFullTrackQAHistos(seTrackQA, "SE/Tracks/BeforeSelection/"); + seTrackQA.addClone("SE/Tracks/BeforeSelection/", "SE/Tracks/AfterSelection/"); + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta", HistType::kTH2F, {axisDeltaPhiSpec, axisDeltaEtaSpec}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta}); + }; + + auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { + histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi", HistType::kTH2F, {massAxis, axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta", HistType::kTH2F, {massAxis, axisDeltaEtaSpec}); + }; + + auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { + histReg.add((basePath + "P").c_str(), "Resonance;p (GeV/c);Counts", HistType::kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "Resonance;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Resonance;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Resonance;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Rapidity").c_str(), "Resonance;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "InvMass").c_str(), massHistTitle.c_str(), HistType::kTH1F, {massAxis}); + histReg.add((basePath + "InvMassVsPt").c_str(), "Invariant mass vs p_{T};p_{T} (GeV/c);Invariant mass (GeV/c^{2})", HistType::kTH2F, {axisPt, massAxis}); + }; + + auto addResoDauQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, basePath, axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + }; + + // h-h ______________________________________________________________________________ + addTrackQAHistos(hhCorrelation, "SE/hh/QA/Trigger/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocHighPt/"); + addCorrHistos(hhCorrelation, "SE/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/Corr/AssocLowPt/", "SE/hh/Corr/AssocHighPt/"); + + // h-Id ______________________________________________________________________________ + auto registerIdentifiedHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "SE/" + pairName + "/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Pr/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Pi/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Ka/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Pr/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerIdentifiedHistograms(hIdCorrelation, "hIdentified"); + + // h-Resonance ______________________________________________________________________________ + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName, const AxisSpec& massAxis, const std::string& massHistTitle) { + const std::string base = "SE/" + pairName + "/"; + const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); + addResoDauQAHistos(histReg, qaPeak + "PosDau/"); + histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + addResoMassCorrHistos(histReg, base + "Corr/AssocLowPt/MassDependent/", massAxis); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + + registerResonanceHistograms(hPhiCorrelation, "hPhi", axisPhi1020Mass, "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarCorrelation, "hKStar", axisKStar892Mass, "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar", axisKStar892Mass, "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda", axisLambda1520Mass, "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar", axisLambda1520Mass, "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts"); + + auto addInvariantMassSpectra = [&](const std::string& species, const AxisSpec& massAxis) { + const std::string seBase = "SE/InvariantMass/" + species + "/"; + const std::string meBase = "ME/InvariantMass/" + species + "/"; + + resonanceMassSpectra.add((seBase + "US").c_str(), "Unlike-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((seBase + "LS").c_str(), "Like-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((meBase + "US").c_str(), "Mixed-event;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + }; + + addInvariantMassSpectra("Phi", axisPhi1020Mass); + addInvariantMassSpectra("KStar", axisKStar892Mass); + addInvariantMassSpectra("KStarBar", axisKStar892Mass); + addInvariantMassSpectra("Lambda1520", axisLambda1520Mass); + addInvariantMassSpectra("Lambda1520Bar", axisLambda1520Mass); + + //______________________________________________________________________________ + // Event-mixing QA + mixingQA.add("Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingQA.add("Mixing/AllCollisionsPerBin", "Selected collisions per mixing bin;Mixing bin;Collisions", HistType::kTH1D, {axisMixBin}); + mixingQA.add("Mixing/AllCollisionsZCent", "Selected collisions in physical mixing pools;z_{vtx} (cm);Centrality (%)", HistType::kTH2D, {axisVtxMixSpec, axisCentMixSpec}); + mixingQA.add("Mixing/EligibleCollisionsPerBin", "Eligible collisions per mixing pool;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/EligibleCollisionsZCentChannel", "Eligible collisions in physical mixing pools;z_{vtx} (cm);Centrality (%);Correlation channel", HistType::kTH3D, {axisVtxMixSpec, axisCentMixSpec, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/AllPoolOccupancy", "Mixing-pool occupancy per dataframe;Mixing bin;N collisions", HistType::kTH2D, {axisMixBin, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/EligiblePoolOccupancy", "Eligible pool occupancy per dataframe;Mixing bin;Correlation channel;N eligible collisions", HistType::kTH3D, {axisMixBin, axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/PoolOccupancyDistribution", "Distribution of mixing-pool occupancies;Correlation channel;N eligible collisions", HistType::kTH2D, {axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/ReadyPoolMap", "Dataframes where pool was ready;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/NReadyMixBins", "Number of ready mixing bins per dataframe;Correlation channel;N ready mixing bins", HistType::kTH2D, {axisCorrChannel, axisReadyBins}); + + auto setCorrChannelLabels = [](TAxis* axis) { + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountLowPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_LowPt").c_str()); + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountHighPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_HighPt").c_str()); + } + }; + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsPerBin"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsZCentChannel"))->GetZaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/EligiblePoolOccupancy"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/PoolOccupancyDistribution"))->GetXaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/ReadyPoolMap"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/NReadyMixBins"))->GetXaxis()); + + if (cfgDebug.printDebugMessages) { + LOGF(info, "Finishing init"); + } + } // Init function is over. + + //_______________________________ Particle Identification _______________________________ + // p-dependent identification + // p < ThrPforTOF : TPC+TOF (circular cut) if TOF is reliable, otherwise TPC only + // p >= ThrPforTOF : TPC+TOF (circular cut), TOF is required + // Default cuts are 3 sigma (see DefaultPIDcheckValues). + + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + template + bool vetoIdOthersTPC(const T& track) + { + // Static is only run once, ever. + static const std::array doVetoTPC = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTPC)}; + + static const std::array vetoTPC = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTPC)}; + + return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); + } + + template + bool vetoIdOthersTOF(const T& track) + { + // Only computed once + static const std::array doVetoTOF = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTOF)}; + + static const std::array vetoTOF = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTOF)}; + + return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); + } + + template + bool vetoIdOthersTPCTOF(const T& track) + { + // If either veto fails, reject + return vetoIdOthersTPC(track) && vetoIdOthersTOF(track); + } + + // Check if TOF is reliable + template + inline bool checkReliableTOF(const T& track) + { + // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: + if (cfgIdCut.cfgId07CheckTofBeta) { + return (track.hasTOF() && track.beta() > 0.0f); + } + return track.hasTOF(); + } + + template + bool idTPC(const T& track, const float& nSigmaTPC, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + + static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPC(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCcheck); + if (doRelativeTPCcheck) { + if (!relativeIdOthersTPC(track)) { + // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPC = false; some particle has stronger nSigma compared to it + return false; + } + } + + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC; + } else { + return false; + } + } + + template + bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPCTOF(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTOFcheck); + if (doRelativeTOFcheck) { + if (!relativeIdOthersTOF(track)) { + // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + if (doRelativeTPCTOFcheck) { + if (!relativeIdOthersTPCTOF(track)) { + // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPCTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + switch (pidCutType) { + case kCircularCut: + return selIdCircularCut(track, nSigmaSquaredRad); + case kRectangularCut: + return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); + case kEllipsoidalCut: + return selIdEllipsoidalCut(track, nSigmaTPC, nSigmaTOF); + default: + return false; + } + } + + template + bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + // Static cache inside function - initialized once on first call + static const auto thrPforTOF = getCfg(cfgIdCut.pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadHighP); + + if (track.p() < thrPforTOF) { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeLowP, nSigmaTPCLowP, nSigmaTOFLowP, nSigmaRadLowP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } else { + if (idTPC(track, nSigmaTPCLowP, nSigmaIdDistSq)) { + IdMethod = kTPCidentified; + return true; + } + } + } else { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeHighP, nSigmaTPCHighP, nSigmaTOFHighP, nSigmaRadHighP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } + } + return false; + } + + //______________________________Identification Functions________________________________________________________________ + // Pion + template + bool selPion(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Kaon + template + bool selKaon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Proton + template + bool selProton(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Electron + template + bool selElectron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Muon + template + bool selMuon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Deuteron + template + bool selDeuteron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Two-argument versions + template + bool selPion(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selPion(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selKaon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selKaon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selProton(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selProton(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selElectron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selElectron(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selMuon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selMuon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selDeuteron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selDeuteron(track, IdMethod, nSigmaIdDistSq); + } + // + + template + CollisionRejectionTag selCollision(T const& collision) + { + // Basic event selection + if (cfgEvent.requireSel8 && !collision.sel8()) { + return kCollRejectSel8; + } + + if (std::abs(collision.posZ()) >= cfgEvent.cutZvertex) { + return kCollRejectVertexZ; + } + + if (cfgEvent.requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return kCollRejectTriggerTVX; + } + + // TF / ITS-ROF borders + if (cfgEvent.requireNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return kCollRejectITSROFrameBorder; + } + + if (cfgEvent.requireNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return kCollRejectTimeFrameBorder; + } + + // Vertex quality + if (cfgEvent.requireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return kCollRejectVertexITSTPC; + } + + if (cfgEvent.requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return kCollRejectGoodZvtxFT0vsPV; + } + + if (cfgEvent.requireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return kCollRejectVertexTOFmatched; + } + + if (cfgEvent.requireVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return kCollRejectVertexTRDmatched; + } + + if (cfgEvent.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return kCollRejectGoodITSLayersAll; + } + + // Pileup / neighbouring-collision rejection + if (cfgEvent.requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return kCollRejectSameBunchPileup; + } + + if (cfgEvent.requireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return kCollRejectCollInTimeRangeStandard; + } + + if (cfgEvent.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return kCollRejectCollInTimeRangeStrict; + } + + if (cfgEvent.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return kCollRejectCollInTimeRangeNarrow; + } + + if (cfgEvent.requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return kCollRejectCollInRofStandard; + } + + if (cfgEvent.requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return kCollRejectCollInRofStrict; + } + + if (cfgEvent.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return kCollRejectHighMultCollInPrevRof; + } + + // INEL classes + if (cfgEvent.requireINELgt0 && !collision.isInelGt0()) { + return kCollRejectINELgt0; + } + + if (cfgEvent.requireINELgt1 && !collision.isInelGt1()) { + return kCollRejectINELgt1; + } + + return kCollAccepted; + } // selCollision is Over. + + template + TrackRejectionTag selectionTrack(T const& track) + { + // Standard O2 flags + if (cfgTrackCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return kTrackRejectGlobalTrack; + } + + if (cfgTrackCuts.requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + return kTrackRejectGlobalTrackWoDCA; + } + + if (cfgTrackCuts.requirePVContributor && !track.isPVContributor()) { + return kTrackRejectPVContributor; + } + + // Detector presence + if (cfgTrackCuts.requireITS && !track.hasITS()) { + return kTrackRejectITS; + } + + if (cfgTrackCuts.requireTPC && !track.hasTPC()) { + return kTrackRejectTPC; + } + + if (cfgTrackCuts.requireTOF && !track.hasTOF()) { + return kTrackRejectTOF; + } + + if (cfgTrackCuts.requireTRD && !track.hasTRD()) { + return kTrackRejectTRD; + } + + // TPC quality + if (track.tpcNClsFound() < cfgTrackCuts.tpcNClsFoundMin) { + return kTrackRejectTPCNClsFound; + } + + if (track.tpcNClsCrossedRows() < cfgTrackCuts.tpcNClsCrossedRowsMin) { + return kTrackRejectTPCCrossedRows; + } + + if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCuts.tpcCrossedRowsOverFindableMin) { + return kTrackRejectTPCCrossedRowsOverFindable; + } + + if (track.tpcFoundOverFindableCls() < cfgTrackCuts.tpcFoundOverFindableMin) { + return kTrackRejectTPCFoundOverFindable; + } + + if (track.tpcFractionSharedCls() > cfgTrackCuts.tpcFractionSharedMax) { + return kTrackRejectTPCFractionShared; + } + + if (track.tpcChi2NCl() < cfgTrackCuts.tpcChi2NClMin || track.tpcChi2NCl() > cfgTrackCuts.tpcChi2NClMax) { + return kTrackRejectTPCChi2; + } + + // ITS quality + if (track.itsNCls() < cfgTrackCuts.itsNClsMin || track.itsNCls() > cfgTrackCuts.itsNClsMax) { + return kTrackRejectITSNCls; + } + + if (track.itsNClsInnerBarrel() < cfgTrackCuts.itsNClsInnerBarrelMin) { + return kTrackRejectITSNClsInnerBarrel; + } + + if (track.itsChi2NCl() < cfgTrackCuts.itsChi2NClMin || track.itsChi2NCl() > cfgTrackCuts.itsChi2NClMax) { + return kTrackRejectITSChi2; + } + + // pT-dependent DCA + if (cfgTrackCuts.usePtDependentDCAxy) { + const float dcaXYMax = cfgTrackCuts.dcaXYPtA + cfgTrackCuts.dcaXYPtB / std::pow(track.pt(), cfgTrackCuts.dcaXYPtC); + if (std::abs(track.dcaXY()) > dcaXYMax) { + return kTrackRejectPtDependentDCAxy; + } + } + + if (cfgTrackCuts.usePtDependentDCAz) { + const float dcaZMax = cfgTrackCuts.dcaZPtA; + if (std::abs(track.dcaZ()) > dcaZMax) { + return kTrackRejectPtDependentDCAz; + } + } + + return kTrackAccepted; + } + + template + void fillEventQA(const C& collision) + { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VertexZ"), collision.posZ()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0C"), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0M"), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0A"), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFV0A"), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0C"), collision.posZ(), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0M"), collision.posZ(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0A"), collision.posZ(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFV0A"), collision.posZ(), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0M"), collision.centFT0C(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0A"), collision.centFT0C(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFT0A"), collision.centFT0M(), collision.centFT0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFV0A"), collision.centFT0C(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFV0A"), collision.centFT0M(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0AVsCentFV0A"), collision.centFT0A(), collision.centFV0A()); + + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const int mixingBin = getMixingBin(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingEstimator"), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsMixingEstimator"), collision.posZ(), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBinStatus"), validMixingBin ? 1.0f : 0.0f); + + if (validMixingBin) { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBin"), mixingBin); + } + } + + template + void fillFullTrackPIDQA(const T& track) + { + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + + if constexpr (pidType == kPi) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + } else if constexpr (pidType == kKa) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + } else if constexpr (pidType == kPr) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + } else if constexpr (pidType == kEl) { + tpcNSigma = track.tpcNSigmaEl(); + tofNSigma = track.tofNSigmaEl(); + } else if constexpr (pidType == kMu) { + tpcNSigma = track.tpcNSigmaMu(); + tofNSigma = track.tofNSigmaMu(); + } else if constexpr (pidType == kDe) { + tpcNSigma = track.tpcNSigmaDe(); + tofNSigma = track.tofNSigmaDe(); + } + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + template + void fillFullTrackQA(const T& track) + { + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("P"), track.p()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Pt"), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCInnerParam"), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFExpMom"), track.tofExpMom()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Eta"), track.eta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Phi"), track.phi()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxy"), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAz"), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Sign"), track.sign()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsP"), track.p(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTPCInnerParam"), track.tpcInnerParam(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTOFExpMom"), track.tofExpMom(), track.dcaXY()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsP"), track.p(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTPCInnerParam"), track.tpcInnerParam(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTOFExpMom"), track.tofExpMom(), track.dcaZ()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsPt"), track.p(), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTPCInnerParam"), track.p(), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTOFExpMom"), track.p(), track.tofExpMom()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTOFExpMom"), track.tofExpMom(), track.tpcSignal()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTPCInnerParam"), track.tpcInnerParam(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + } + + template + void fillCorrParticleQA(H& histReg, const T& track) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + } + + template + void fillIdentifiedCorrelation(H& histReg, const T& trigger, const A& associate, const int idMethod) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("P"), associate.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassPiPlus)); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassKPlus)); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassProton)); + } + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxy"), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAz"), associate.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxyVsPt"), associate.pt(), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAzVsPt"), associate.pt(), associate.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCSignalVsP"), associate.p(), associate.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFBetaVsP"), associate.p(), associate.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("IdMethodVsP"), associate.p(), idMethod); + + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPi(), associate.tofNSigmaPi()); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaKa(), associate.tofNSigmaKa()); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPr(), associate.tofNSigmaPr()); + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + //______________________________________________________________________________ + // Resonance candidate QA + template + void fillResoAssocQA(H& histReg, const A& associate, const float invMass) + { + const float p = std::sqrt(associate.px() * associate.px() + associate.py() * associate.py() + associate.pz() * associate.pz()); + const float energy = std::sqrt(p * p + invMass * invMass); + const float rapidity = 0.5f * std::log((energy + associate.pz()) / (energy - associate.pz())); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("P"), p); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Rapidity"), rapidity); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMass"), invMass); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMassVsPt"), associate.pt(), invMass); + } + + //______________________________________________________________________________ + // Resonance daughter QA + template + void fillResoDauQA(H& histReg, const T& track, const int idMethod) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("IdMethodVsP"), track.p(), idMethod); + + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + float massHypothesis = 0.0f; + + if constexpr (pairType == kHPhi || (pairType == kHKStar && dauType == kPosDau) || (pairType == kHKStarBar && dauType == kNegDau) || + (pairType == kHLambda && dauType == kNegDau) || (pairType == kHLambdaBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + massHypothesis = MassKPlus; + } else if constexpr ((pairType == kHKStar && dauType == kNegDau) || (pairType == kHKStarBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + massHypothesis = MassPiPlus; + } else if constexpr ((pairType == kHLambda && dauType == kPosDau) || (pairType == kHLambdaBar && dauType == kNegDau)) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + massHypothesis = MassProton; + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Rapidity"), computeRapidity(track, massHypothesis)); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + //______________________________________________________________________________ + // Generic h-h correlation + template + void fillTrigAssociateCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + //______________________________________________________________________________ + // Resonance correlation + template + void fillResoCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + // Resonance correlation as a function of invariant mass + template + void fillResoMassCorrelation(H& histReg, const T& trigger, const A& associate, const float invMass) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta); + } + + template + void fillResoRegion(H& histReg, const T& trigger, const A& associate, const P& posDauTrack, const N& negDauTrack, const float invMass, const int posDauIdMethod, const int negDauIdMethod) + { + fillResoAssocQA(histReg, associate, invMass); + fillResoDauQA(histReg, posDauTrack, posDauIdMethod); + fillResoDauQA(histReg, negDauTrack, negDauIdMethod); + fillResoCorrelation(histReg, trigger, associate); + } + + template + bool selectResonanceDaughters(const P& posDauTrack, const N& negDauTrack, int& posDauIdMethod, int& negDauIdMethod) + { + // Phi -> K+ K- ----------------------- + // K(892)* -> K+ + pi- ----------------------- + // K(892)*Bar -> K- + pi+ ----------------------- + // Λ(1520) -> P+ + Ka- ----------------------- + // Λ(1520)Bar -> PBar + Ka+ ----------------------- + + posDauIdMethod = kUnidentified; + negDauIdMethod = kUnidentified; + if constexpr (pairType == kHPhi) { + return selKaon(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStar) { + return selKaon(posDauTrack, posDauIdMethod) && selPion(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStarBar) { + return selPion(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambda) { + return selProton(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambdaBar) { + return selKaon(posDauTrack, posDauIdMethod) && selProton(negDauTrack, negDauIdMethod); + } + return false; + } + + template + uint8_t getResonanceMassRegion(const A& associate, float& invMass) + { + if constexpr (pairType == kHPhi) { + invMass = associate.mPhi1020(); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.phi1020LSBLow, cfgPhi1020CorrMass.phi1020LSBUp, cfgPhi1020CorrMass.phi1020PeakLow, cfgPhi1020CorrMass.phi1020PeakUp, cfgPhi1020CorrMass.phi1020RSBLow, cfgPhi1020CorrMass.phi1020RSBUp); + } else if constexpr (pairType == kHKStar) { + invMass = associate.mKStar892(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + } else if constexpr (pairType == kHKStarBar) { + invMass = associate.mKStar892Bar(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + } else if constexpr (pairType == kHLambda) { + invMass = associate.mLambda1520(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + } else if constexpr (pairType == kHLambdaBar) { + invMass = associate.mLambda1520Bar(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + } + + invMass = -1.0f; + return kMassOutside; + } + + template + void incrementCorrCount(CorrCountArray& counts) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if constexpr (pairType == kHH) { + ++counts[kCountHH][PtType]; + } else if constexpr (pairType == kHId && pidType == kPi) { + ++counts[kCountHPi][PtType]; + } else if constexpr (pairType == kHId && pidType == kKa) { + ++counts[kCountHKa][PtType]; + } else if constexpr (pairType == kHId && pidType == kPr) { + ++counts[kCountHPr][PtType]; + } else if constexpr (pairType == kHPhi) { + ++counts[kCountHPhi][PtType]; + } else if constexpr (pairType == kHKStar) { + ++counts[kCountHKStar][PtType]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kCountHKStarBar][PtType]; + } else if constexpr (pairType == kHLambda) { + ++counts[kCountHLambda][PtType]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kCountHLambdaBar][PtType]; + } + } + + template + void incrementResoRegionCount(ResoRegionCountArray& counts, uint8_t massRegion) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if (massRegion != kMassPeak && massRegion != kMassLSB && massRegion != kMassRSB) { + return; + } + + if constexpr (pairType == kHPhi) { + ++counts[kResoCountHPhi][PtType][massRegion]; + } else if constexpr (pairType == kHKStar) { + ++counts[kResoCountHKStar][PtType][massRegion]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kResoCountHKStarBar][PtType][massRegion]; + } else if constexpr (pairType == kHLambda) { + ++counts[kResoCountHLambda][PtType][massRegion]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kResoCountHLambdaBar][PtType][massRegion]; + } + } + + template + float getInvariantMass(const T1& dau1, const T2& dau2) + { + const float p = RecoDecay::p(dau1.px() + dau2.px(), dau1.py() + dau2.py(), dau1.pz() + dau2.pz()); + + if constexpr (pairType == kHPhi) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHKStar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassPiPlus)); + } else if constexpr (pairType == kHKStarBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassPiPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambda) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassProton) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambdaBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassProton)); + } + + return -1.0f; + } + + template + bool fillInvariantMassPair(const C& collision, const T1& dau1, const T2& dau2) + { + int dau1IdMethod = kUnidentified; + int dau2IdMethod = kUnidentified; + + if (!selectResonanceDaughters(dau1, dau2, dau1IdMethod, dau2IdMethod)) { + return false; + } + + const float mass = getInvariantMass(dau1, dau2); + const float pt = RecoDecay::pt(dau1.px() + dau2.px(), dau1.py() + dau2.py()); + const float pz = dau1.pz() + dau2.pz(); + const float energy = RecoDecay::e(dau1.px() + dau2.px(), dau1.py() + dau2.py(), pz, mass); + const float rapidity = 0.5f * std::log((energy + pz) / (energy - pz)); + if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + return false; + } + + float centrality = -1.0f; + switch (cfgEvent.centralityEstimator) { + case 0: + centrality = collision.centFT0C(); + break; + case 1: + centrality = collision.centFT0M(); + break; + case 2: + centrality = collision.centFT0A(); + break; + case 3: + centrality = collision.centFV0A(); + break; + default: + return false; + } + + if constexpr (pairType == kHPhi && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/US"), mass, pt, centrality); + } else if constexpr (pairType == kHPhi && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/LS"), mass, pt, centrality); + } + + return true; + } + + template + void fillUnlikeSignInvariantMass(const C& collision, const P& posTracksPerColl, const N& negTracksPerColl) + { + for (const auto& posTrack : posTracksPerColl) { + if (selectionTrack(posTrack) != kTrackAccepted) { + continue; + } + + for (const auto& negTrack : negTracksPerColl) { + if (selectionTrack(negTrack) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + } + } + } + + template + void fillLikeSignInvariantMass(const C& collision, const T& tracksPerColl) + { + for (const auto& track1 : tracksPerColl) { + if (selectionTrack(track1) != kTrackAccepted) { + continue; + } + + for (const auto& track2 : tracksPerColl) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (selectionTrack(track2) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, track1, track2); + + if constexpr (positiveSign) { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } else { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } + } + } + } + + inline bool shouldStoreDerivedCollision(uint64_t corrMask, int mixingBin) + { + if (mixingBin < 0 || mixingBin >= nMixBins) { + return false; + } + + if (cfgDerivedData.mixingBin >= 0 && mixingBin != cfgDerivedData.mixingBin) { + return false; + } + + const uint64_t requestedMask = cfgDerivedData.pairMask; + + if (requestedMask == 0ULL) { + return corrMask != 0ULL; + } + + if (cfgDerivedData.requireAllPairBits) { + return (corrMask & requestedMask) == requestedMask; + } + + return (corrMask & requestedMask) != 0u; + } + + // 01-Start-obtaining trigger-LowPtAssociates correlation + template + void executeTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, const auto& triggerGIList, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + int associateSelTag = kTrackAccepted; + int assocPosDauSelTag = kTrackAccepted; + int assocNegDauSelTag = kTrackAccepted; + int posDauIdMethod = kUnidentified; + int negDauIdMethod = kUnidentified; + int pionIdMethod = kUnidentified; + int kaonIdMethod = kUnidentified; + int protonIdMethod = kUnidentified; + + for (const auto& associate : associates) { + //__________________________________________________________________________ + // Hadron-hadron and hadron-Identified correlation + if constexpr (pairType == kHH || pairType == kHId) { + associateSelTag = selectionTrack(associate); + if (associateSelTag != kTrackAccepted) { + continue; + } + + if constexpr (pairType == kHH) { + fillCorrParticleQA(histReg, associate); + fillTrigAssociateCorrelation(histReg, trigger, associate); + incrementCorrCount(corrCountPerColl); + } + + if constexpr (pairType == kHId) { + pionIdMethod = kUnidentified; + kaonIdMethod = kUnidentified; + protonIdMethod = kUnidentified; + + if (selPion(associate, pionIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); + incrementCorrCount(corrCountPerColl); + } + if (selKaon(associate, kaonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); + incrementCorrCount(corrCountPerColl); + } + if (selProton(associate, protonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); + incrementCorrCount(corrCountPerColl); + } + } + } + + //__________________________________________________________________________ + // Hadron-Resonance correlation + if constexpr (pairType == kHPhi || pairType == kHKStar || pairType == kHKStarBar || pairType == kHLambda || pairType == kHLambdaBar) { + const auto& posDauTrack = associate.template posTrack_as(); + const auto& negDauTrack = associate.template negTrack_as(); + + // Track Selection ------------------------ + assocPosDauSelTag = selectionTrack(posDauTrack); + if (assocPosDauSelTag != kTrackAccepted) { + continue; + } + + assocNegDauSelTag = selectionTrack(negDauTrack); + if (assocNegDauSelTag != kTrackAccepted) { + continue; + } + + if (!selectResonanceDaughters(posDauTrack, negDauTrack, posDauIdMethod, negDauIdMethod)) { + continue; + } + + float invMass = -1.0f; + const uint8_t massRegion = getResonanceMassRegion(associate, invMass); + const float rapidity = computeRapidity(associate, invMass); + if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + continue; + } + + // Trigger-resonance daughter overlap -----expensive check ---- + if (rejectResoWithAnyTriggerDaughter) { + // Strict mode: reject the whole resonance if either daughter belongs to the event trigger population. + if (checkTrackInList(posDauTrack, triggerGIList) || checkTrackInList(negDauTrack, triggerGIList)) { + continue; + } + } else { + // Standard pair-wise autocorrelation removal: reject only if the CURRENT trigger is a daughter. + if (trigger.globalIndex() == posDauTrack.globalIndex() || trigger.globalIndex() == negDauTrack.globalIndex()) { + continue; + } + } + + fillResoMassCorrelation(histReg, trigger, associate, invMass); + + if (massRegion == kMassOutside) { + continue; + } + + switch (massRegion) { + case kMassPeak: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassLSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassRSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + default: + continue; + } + + incrementCorrCount(corrCountPerColl); + incrementResoRegionCount(resoRegionCountsPerColl, massRegion); + } + } // associate particle loop is over + } // function definition is over + + template + void executeCorrelation(H& histReg, const T& triggers, const A& associatesLowPt, const B& associatesHighPt, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + // Trigger Selection + // int nTrigger = 0; + int triggerSelTag = kTrackAccepted; + + // Option 1 : Trigger tracks wont be accepted as daughter track of reconstructed resonance. + // Option 2 : + std::vector triggerGIList; + std::vector posDauGIListPeak; + std::vector negDauGIListPeak; + std::vector posDauGIListLSB; + std::vector negDauGIListLSB; + std::vector posDauGIListRSSB; + std::vector negDauGIListRSSB; + + if (rejectResoWithAnyTriggerDaughter) { + for (const auto& trigger : triggers) { + if (selectionTrack(trigger) != kTrackAccepted) { + continue; + } + triggerGIList.push_back(trigger.globalIndex()); + } + } + + for (const auto& trigger : triggers) { + triggerSelTag = selectionTrack(trigger); + if (triggerSelTag != kTrackAccepted) { + continue; + } + // fillTrigQA + fillCorrParticleQA(histReg, trigger); + // ++nTrigger; + + // triggerTrackIndexList.push_back(triggerTrack.globalIndex()); + executeTrigAssocCorrelation(histReg, trigger, associatesLowPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + executeTrigAssocCorrelation(histReg, trigger, associatesHighPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + } // trigger particle loop + } // execute Correlation Function is over + + // Event Filter + Filter eventFilter = (!cfgEvent.requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.ptMin) && (o2::aod::track::pt < cfgTrackCuts.ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.etaMax); + Filter dcaFilter = ((!cfgTrackCuts.useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.dcaXYMax)) && + ((!cfgTrackCuts.useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; // , + // using MyFilteredTracks = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + // For manual sliceBy + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + Preslice resoPerCollisionPreslice = aod::resonancecndt::collisionId; // registers RESONCNDTS/fIndexCollisions in the SliceCache + + // // definition of partitions + SliceCache cache; + Partition triggerTracks = cfgPartitions.triggerPtLow < aod::track::pt && aod::track::pt < cfgPartitions.triggerPtHigh; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + Partition assocTracksLowPt = cfgPartitions.assocPtLowMin < aod::track::pt && aod::track::pt < cfgPartitions.assocPtLowMax; + Partition assocTracksHighPt = cfgPartitions.assocPtHighMin < aod::track::pt && aod::track::pt < cfgPartitions.assocPtHighMax; + + Partition assocPhiLowPt = (cfgResPartitions.phiPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.phiPtLowMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + Partition assocPhiHighPt = (cfgResPartitions.phiPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.phiPtHighMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + + Partition assocKStarLowPt = (cfgResPartitions.kstarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarPtLowMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + Partition assocKStarHighPt = (cfgResPartitions.kstarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarPtHighMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + + Partition assocKStarBarLowPt = (cfgResPartitions.kstarBarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarBarPtLowMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + Partition assocKStarBarHighPt = (cfgResPartitions.kstarBarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarBarPtHighMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + + Partition assocLambda1520LowPt = (cfgResPartitions.lambda1520PtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520PtLowMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + Partition assocLambda1520HighPt = (cfgResPartitions.lambda1520PtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520PtHighMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + + Partition assocLambda1520BarLowPt = (cfgResPartitions.lambda1520BarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520BarPtLowMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + Partition assocLambda1520BarHighPt = (cfgResPartitions.lambda1520BarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520BarPtHighMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + + int dfNumber = 0; + int64_t iCollGlobalCount = -1; + int64_t iTrackGlobalCount = -1; + int64_t iResonanceGlobalCount = -1; + struct DerivedTrackRef { + int64_t globalTrackId; + }; + + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelationSameEvent, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, aod::ResonanceCndts const& resonanceCndts, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&) + { + dfNumber++; + mixingQA.fill(HIST("Mixing/DataFramesProcessed"), 0.0); + std::vector mixingBinStatusPerDF(nMixBins); + // LOG(info)<<"DEBUG :: Origins :: "<::max() - static_cast(dfNumber); + LOG(warn) << "Origins table is empty. Using synthetic dataframeID = " << dataframeID; + } else { + dataframeID = Origins.iteratorAt(0).dataframeID(); + } + + assocPhiLowPt.bindExternalIndices(&fullTracks); + assocPhiHighPt.bindExternalIndices(&fullTracks); + assocKStarLowPt.bindExternalIndices(&fullTracks); + assocKStarHighPt.bindExternalIndices(&fullTracks); + assocKStarBarLowPt.bindExternalIndices(&fullTracks); + assocKStarBarHighPt.bindExternalIndices(&fullTracks); + assocLambda1520LowPt.bindExternalIndices(&fullTracks); + assocLambda1520HighPt.bindExternalIndices(&fullTracks); + assocLambda1520BarLowPt.bindExternalIndices(&fullTracks); + assocLambda1520BarHighPt.bindExternalIndices(&fullTracks); + + if (collisions.size() == 0) { + seEventQA.fill(HIST("SE/Events/DataFrameQA"), kDFWithZeroFilteredColls); + + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = 0 :: No filtered collisions found in this dataframe"; + } + } else if (cfgDebug.printDebugMessages) { + auto bc = collisions.iteratorAt(0).bc_as(); + int currentRunNumber = bc.runNumber(); + + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: currentRunNumber = " << currentRunNumber << " :: " << collisions.iteratorAt(0).bc_as().runNumber(); + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size() << " :: resonanceCndts = " << resonanceCndts.size(); + } + + int nTrack = 0; + CollisionRejectionTag collisionTag = kCollAccepted; + TrackRejectionTag trackTag = kTrackAccepted; + std::vector dauGIListPeak; + std::vector dauGIListLSB; + std::vector dauGIListRSB; + + for (const auto& collision : collisions) { // CollisionLoop-Start + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventAllFiltered)); + fillEventQA(collision); + + collisionTag = selCollision(collision); + seEventQA.fill(HIST("SE/Events/CollisionRejection"), static_cast(collisionTag)); + if (collisionTag != kCollAccepted) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedSelCollision)); + + const auto tracksPerCollision = fullTracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); + if (tracksPerCollision.size() < cfgEvent.minNFilteredTracks) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinFilteredTracks)); + + nTrack = 0; + for (const auto& track : tracksPerCollision) { + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackAllFiltered)); + fillFullTrackQA(track); + + trackTag = selectionTrack(track); + seTrackQA.fill(HIST("SE/Tracks/TrackRejection"), static_cast(trackTag)); + + if (trackTag != kTrackAccepted) { + continue; + } + + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackPassedSelectionTrack)); + fillFullTrackQA(track); + nTrack++; + } // track loop is over. + + if (nTrack < cfgEvent.minNSelectedTracks) { + continue; + } + + dauGIListPeak.assign(tracksPerCollision.size(), 0); + dauGIListLSB.assign(tracksPerCollision.size(), 0); + dauGIListRSB.assign(tracksPerCollision.size(), 0); + fillEventQA(collision); + CorrCountArray corrCountsPerColl{}; + ResoRegionCountArray resoRegionCountsPerColl{}; + + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinSelectedTracks)); + + // Get the Partitions + auto triggerTracksPerColl = triggerTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocTrksPerCollLowPt = assocTracksLowPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto assocTrksPerCollHighPt = assocTracksHighPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocPhiPerCollLowPt = assocPhiLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocPhiPerCollHighPt = assocPhiHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarPerCollLowPt = assocKStarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarPerCollHighPt = assocKStarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarBarPerCollLowPt = assocKStarBarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarBarPerCollHighPt = assocKStarBarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaPerCollLowPt = assocLambda1520LowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaPerCollHighPt = assocLambda1520HighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaBarPerCollLowPt = assocLambda1520BarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaBarPerCollHighPt = assocLambda1520BarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + executeCorrelation(hhCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hIdCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hPhiCorrelation, triggerTracksPerColl, assocPhiPerCollLowPt, assocPhiPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarCorrelation, triggerTracksPerColl, assocKStarPerCollLowPt, assocKStarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarBarCorrelation, triggerTracksPerColl, assocKStarBarPerCollLowPt, assocKStarBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaCorrelation, triggerTracksPerColl, assocLambdaPerCollLowPt, assocLambdaPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaBarCorrelation, triggerTracksPerColl, assocLambdaBarPerCollLowPt, assocLambdaBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + + // Unlike Sign Signal + // LikeSign Signal + bool hasSelectedTrigger = false; + if (requireSelectedTriggerForInvariantMass) { + for (const auto& trigger : triggerTracksPerColl) { + if (selectionTrack(trigger) == kTrackAccepted) { + hasSelectedTrigger = true; + break; + } + } + } + + if (!requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillUnlikeSignInvariantMass(collision, posTracksPerColl, negTracksPerColl); + fillLikeSignInvariantMass(collision, posTracksPerColl); + fillLikeSignInvariantMass(collision, negTracksPerColl); + } + + //______________________________________________________________________________ + // Event-mixing information for this collision + uint64_t corrPresenceMask = 0ULL; + const int mixingBin = getMixingBin(collision); + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCollisions; + mixingQA.fill(HIST("Mixing/AllCollisionsPerBin"), mixingBin); + mixingQA.fill(HIST("Mixing/AllCollisionsZCent"), collision.posZ(), mixingEstimatorValue); + } + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + if (corrCountsPerColl[iCorr][iPt] == 0) { + continue; + } + + const int corrChannel = iCorr * kNCorrCountPt + iPt; + corrPresenceMask |= getCorrMaskBit(iCorr, iPt); + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCorrCollisions[iCorr][iPt]; + mixingQA.fill(HIST("Mixing/EligibleCollisionsPerBin"), mixingBin, corrChannel); + mixingQA.fill(HIST("Mixing/EligibleCollisionsZCentChannel"), collision.posZ(), mixingEstimatorValue, corrChannel); + } + } + } + + for (int iReso = 0; iReso < kNResoCorrCountTypes; ++iReso) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + for (int iMassRegion = kMassPeak; iMassRegion <= kMassRSB; ++iMassRegion) { + if (resoRegionCountsPerColl[iReso][iPt][iMassRegion] == 0) { + continue; + } + corrPresenceMask |= getResoRegionMaskBit(iReso, iPt, iMassRegion); + } + } + } + + //______________________________________________________________________________ + // Derived tables + if (shouldStoreDerivedCollision(corrPresenceMask, mixingBin)) { + iCollGlobalCount++; + derivedCollisions(iCollGlobalCount, + collision.globalIndex(), + dataframeID, + collision.posZ(), + collision.centFT0C(), + collision.centFT0M(), + collision.centFT0A(), + collision.centFV0A(), + static_cast(cfgMixing.mixingEstimator), + mixingBin, + corrPresenceMask); + + std::map derivedTrackIndex; + for (const auto& track : tracksPerCollision) { + if (selectionTrack(track) != kTrackAccepted) { + continue; // Store only selected tracks to reduce the size. + } + + iTrackGlobalCount++; + derivedTracks(iCollGlobalCount, // derivedCollisionId, individual will create issues in mereged dataframes + iCollGlobalCount, + collision.globalIndex(), + iTrackGlobalCount, + track.globalIndex(), + dataframeID, + + track.pt(), + track.p(), + track.tpcInnerParam(), + track.tofExpMom(), + track.eta(), + track.phi(), + static_cast(track.sign()), + + track.dcaXY(), + track.dcaZ(), + track.tpcSignal(), + + static_cast(track.hasTOF()), + track.beta(), + + track.tpcNSigmaEl(), + track.tpcNSigmaMu(), + track.tpcNSigmaPi(), + track.tpcNSigmaKa(), + track.tpcNSigmaPr(), + track.tpcNSigmaDe(), + + track.tofNSigmaEl(), + track.tofNSigmaMu(), + track.tofNSigmaPi(), + track.tofNSigmaKa(), + track.tofNSigmaPr(), + track.tofNSigmaDe()); + derivedTrackIndex.emplace(track.globalIndex(), DerivedTrackRef{iTrackGlobalCount}); + } + + const auto resonancesPerCollision = resonanceCndts.sliceBy(resoPerCollisionPreslice, collision.globalIndex()); + for (const auto& resonance : resonancesPerCollision) { + const auto originalPosTrackId = static_cast(resonance.posTrackId()); + const auto originalNegTrackId = static_cast(resonance.negTrackId()); + + // Positive daughter must have been stored in HPCorrTracks + const auto posIt = derivedTrackIndex.find(originalPosTrackId); + if (posIt == derivedTrackIndex.end()) { + continue; + } + + // Negative daughter must have been stored in HPCorrTracks + const auto negIt = derivedTrackIndex.find(originalNegTrackId); + if (negIt == derivedTrackIndex.end()) { + continue; + } + + iResonanceGlobalCount++; + derivedResonances( + // Relations to derived tables + iCollGlobalCount, // derivedCollisionId, + posIt->second.globalTrackId, + negIt->second.globalTrackId, + + // Collision references + iCollGlobalCount, + collision.globalIndex(), + + // Positive daughter references + posIt->second.globalTrackId, + originalPosTrackId, + + // Negative daughter references + negIt->second.globalTrackId, + originalNegTrackId, + + // Resonance references + iResonanceGlobalCount, + resonance.globalIndex(), + + // Source dataframe + dataframeID, + + // Resonance kinematics + resonance.pt(), + resonance.eta(), + resonance.phi(), + + // Invariant masses + resonance.mPhi1020(), + resonance.mKStar892(), + resonance.mKStar892Bar(), + resonance.mLambda1520(), + resonance.mLambda1520Bar(), + + // Resonance tags + resonance.phi1020Tag(), + resonance.kStar892Tag(), + resonance.kStar892BarTag(), + resonance.lambda1520Tag(), + resonance.lambda1520BarTag()); + } + } // Derived data creation block is over. + } // collision loop - Over. + + //______________________________________________________________________________ + // Dataframe-level mixing-pool QA + std::array nReadyMixBins{}; + + for (int iMixBin = 0; iMixBin < nMixBins; ++iMixBin) { + const auto& binStatus = mixingBinStatusPerDF[iMixBin]; + mixingQA.fill(HIST("Mixing/PerDF/AllPoolOccupancy"), iMixBin, binStatus.nCollisions); + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + const int corrChannel = iCorr * kNCorrCountPt + iPt; + const uint64_t nEligibleCollisions = binStatus.nCorrCollisions[iCorr][iPt]; + + mixingQA.fill(HIST("Mixing/PerDF/EligiblePoolOccupancy"), iMixBin, corrChannel, nEligibleCollisions); + mixingQA.fill(HIST("Mixing/PerDF/PoolOccupancyDistribution"), corrChannel, nEligibleCollisions); + + if (nEligibleCollisions >= static_cast(cfgMixing.nEvtMixing)) { + ++nReadyMixBins[corrChannel]; + mixingQA.fill(HIST("Mixing/PerDF/ReadyPoolMap"), iMixBin, corrChannel); + } + } + } + } + + for (int corrChannel = 0; corrChannel < NCorrCountChannels; ++corrChannel) { + mixingQA.fill(HIST("Mixing/PerDF/NReadyMixBins"), corrChannel, nReadyMixBins[corrChannel]); + } + } + PROCESS_SWITCH(HParticleCorrelationSameEvent, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelationMixedEvent { + + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingOperationQA{"mixingOperationQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + static constexpr int NMixOperationChannels = 38; + + static constexpr std::array MixOperationChannelName = { + "hh_LowPt", + "hh_HighPt", + "hPi_LowPt", + "hPi_HighPt", + "hKa_LowPt", + "hKa_HighPt", + "hPr_LowPt", + "hPr_HighPt", + + "hPhi_LowPt_Peak", + "hPhi_LowPt_LSB", + "hPhi_LowPt_RSB", + "hPhi_HighPt_Peak", + "hPhi_HighPt_LSB", + "hPhi_HighPt_RSB", + + "hKStar_LowPt_Peak", + "hKStar_LowPt_LSB", + "hKStar_LowPt_RSB", + "hKStar_HighPt_Peak", + "hKStar_HighPt_LSB", + "hKStar_HighPt_RSB", + + "hKStarBar_LowPt_Peak", + "hKStarBar_LowPt_LSB", + "hKStarBar_LowPt_RSB", + "hKStarBar_HighPt_Peak", + "hKStarBar_HighPt_LSB", + "hKStarBar_HighPt_RSB", + + "hLambda_LowPt_Peak", + "hLambda_LowPt_LSB", + "hLambda_LowPt_RSB", + "hLambda_HighPt_Peak", + "hLambda_HighPt_LSB", + "hLambda_HighPt_RSB", + + "hLambdaBar_LowPt_Peak", + "hLambdaBar_LowPt_LSB", + "hLambdaBar_LowPt_RSB", + "hLambdaBar_HighPt_Peak", + "hLambdaBar_HighPt_LSB", + "hLambdaBar_HighPt_RSB"}; + + static constexpr int getMixOperationChannel(const uint64_t mask) + { + constexpr int TrackMixMaskEndBit = 8; + constexpr int ResonanceMixMaskFirstBit = 18; + constexpr int ResonanceMixMaskEndBit = 48; + constexpr int ResonanceMixChannelOffset = ResonanceMixMaskFirstBit - TrackMixMaskEndBit; + + int bit = 0; + uint64_t value = mask; + + while (value > 1ULL) { + value >>= 1ULL; + ++bit; + } + + if (bit < TrackMixMaskEndBit) { + return bit; + } + + if (bit >= ResonanceMixMaskFirstBit && bit < ResonanceMixMaskEndBit) { + return bit - ResonanceMixChannelOffset; + } + + return -1; + } + + struct : ConfigurableGroup { + Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis axisCorrSparseTriggerPt{"axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis axisCorrSparseAssocPt{"axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis axisCorrSparseDeltaPhi{"axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis axisCorrSparseDeltaEta{"axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + } cfgAxis; + + struct : ConfigurableGroup { + Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; + Configurable mixingEstimator{"mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis axisVtxMixing{"axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis axisCentMixing{"axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + } cfgMixing; + + struct : ConfigurableGroup { + Configurable triggerPtLow{"triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable triggerPtHigh{"triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable assocPtLowMin{"assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable assocPtLowMax{"assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable assocPtHighMin{"assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable assocPtHighMax{"assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + } cfgPartitions; + + struct : ConfigurableGroup { + Configurable resoRapidityMin{"resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable resoRapidityMax{"resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable phiPtLowMin{"phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable phiPtLowMax{"phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable phiPtHighMin{"phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable phiPtHighMax{"phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable kstarPtLowMin{"kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable kstarPtLowMax{"kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable kstarPtHighMin{"kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable kstarPtHighMax{"kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable kstarBarPtLowMin{"kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable kstarBarPtLowMax{"kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable kstarBarPtHighMin{"kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable kstarBarPtHighMax{"kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable lambda1520PtLowMin{"lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable lambda1520PtLowMax{"lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable lambda1520PtHighMin{"lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable lambda1520PtHighMax{"lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable lambda1520BarPtLowMin{"lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtLowMax{"lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtHighMin{"lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable lambda1520BarPtHighMax{"lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + } cfgResPartitions; + + struct : ConfigurableGroup { + Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020CorrMass; + + struct : ConfigurableGroup { + Configurable kStar892PeakLow{"kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable kStar892PeakUp{"kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable kStar892LSBLow{"kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable kStar892LSBUp{"kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable kStar892RSBLow{"kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable kStar892RSBUp{"kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + } cfgKStar892CorrMass; + + struct : ConfigurableGroup { + Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + } cfgLambda1520CorrMass; + + struct : ConfigurableGroup { + Configurable> pidConfigSetting{"pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> pidVetoSetting{"pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable cfgId07CheckTofBeta{"cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + } cfgIdCut; + + void init(InitContext const&) + { + + if (cfgDebug.printDebugMessages) { + LOGF(info, "Starting init"); + } + + const AxisSpec axisP = {200, 0.0f, 10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisTPCInnerParam = {200, 0.0f, 10.0f, "#it{p}_{tpcInnerParam} (GeV/#it{c})"}; + const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; + + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisRapidity = {200, -5.0f, 5.0f, "Rapidity (y)"}; + const AxisSpec axisDcaXY = {100, -5.0f, 5.0f, "dcaXY"}; + const AxisSpec axisDcaZ = {100, -5.0f, 5.0f, "dcaZ"}; + const AxisSpec axisSign = {10, -5.0f, 5.0f, "track.sign"}; + + const AxisSpec axisTPCSignal = {100, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBeta = {40, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCNSigma = {200, -10.0f, 10.0f, "n#sigma_{TPC}"}; + const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; + const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.axisDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrSparseTriggerPt{cfgAxis.axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.axisCorrSparseDeltaEta, "#Delta#eta"}; + + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + + AxisSpec axisVtxMixOperation{cfgMixing.axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixOperation{cfgMixing.axisCentMixing, "Centrality (%)"}; + + const int nMixOperationBins = axisVtxMixOperation.getNbins() * axisCentMixOperation.getNbins(); + + const AxisSpec axisMixOperationBin = {nMixOperationBins, -0.5, static_cast(nMixOperationBins) - 0.5, "Mixing bin"}; + const AxisSpec axisMixOperationChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; + const AxisSpec axisMixPartners = {static_cast(cfgMixing.nEvtMixing) + 1, -0.5, static_cast(cfgMixing.nEvtMixing) + 0.5, "N mixed partner events"}; + + const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; + const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; + const AxisSpec axisMixCentralityQA = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixDeltaCentralityQA = {204, -102.0f, 102.0f, "#Delta Centrality (%)"}; + + auto addTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + histReg.add((basePath + "TPCSignalVsTPCInnerParam").c_str(), "TPC signal vs p_{TPC inner};p_{TPC inner} (GeV/c);TPC signal", kTH2F, {axisTPCInnerParam, axisTPCSignal}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTOFExpMom").c_str(), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", kTH2F, {axisTOFExpMom, axisTOFBeta}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); + }; + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta", HistType::kTH2F, {axisDeltaPhiSpec, axisDeltaEtaSpec}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta}); + }; + + auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { + histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi", HistType::kTH2F, {massAxis, axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta", HistType::kTH2F, {massAxis, axisDeltaEtaSpec}); + }; + + auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { + histReg.add((basePath + "P").c_str(), "Resonance;p (GeV/c);Counts", HistType::kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "Resonance;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Resonance;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Resonance;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Rapidity").c_str(), "Resonance;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "InvMass").c_str(), massHistTitle.c_str(), HistType::kTH1F, {massAxis}); + histReg.add((basePath + "InvMassVsPt").c_str(), "Invariant mass vs p_{T};p_{T} (GeV/c);Invariant mass (GeV/c^{2})", HistType::kTH2F, {axisPt, massAxis}); + }; + + auto addResoDauQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, basePath, axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + }; + + addTrackQAHistos(hhCorrelation, "ME/hh/QA/Trigger/"); + hhCorrelation.addClone("ME/hh/QA/Trigger/", "ME/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/QA/Trigger/", "ME/hh/QA/AssocHighPt/"); + addCorrHistos(hhCorrelation, "ME/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/Corr/AssocLowPt/", "ME/hh/Corr/AssocHighPt/"); + + auto registerIdentifiedHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "ME/" + pairName + "/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + + addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Pr/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Pi/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Ka/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Pr/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerIdentifiedHistograms(hIdCorrelation, "hIdentified"); + + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName, const AxisSpec& massAxis, const std::string& massHistTitle) { + const std::string base = "ME/" + pairName + "/"; + const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); + addResoDauQAHistos(histReg, qaPeak + "PosDau/"); + histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + addResoMassCorrHistos(histReg, base + "Corr/AssocLowPt/MassDependent/", massAxis); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + + registerResonanceHistograms(hPhiCorrelation, "hPhi", axisPhi1020Mass, "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarCorrelation, "hKStar", axisKStar892Mass, "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar", axisKStar892Mass, "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda", axisLambda1520Mass, "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar", axisLambda1520Mass, "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts"); + + mixingOperationQA.add("ME/Mixing/CollisionPairsPerChannel", "Actual mixed collision pairs;Mixing channel;Mixed collision pairs", HistType::kTH1D, {axisMixOperationChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerBinChannel", "Actual mixed collision pairs;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixOperationBin, axisMixOperationChannel}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEvent", "Actual mixed partners per trigger event;Mixing channel;N partner events", HistType::kTH2D, {axisMixOperationChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEventVsBin", "Actual mixed partners per trigger event;Mixing bin;Mixing channel;N partner events", HistType::kTH3D, {axisMixOperationBin, axisMixOperationChannel, axisMixPartners}); + + mixingOperationQA.add("ME/Mixing/VtxZ1VsVtxZ2", "Mixed-event vertex positions;z_{vtx}^{1} (cm);z_{vtx}^{2} (cm)", HistType::kTH2F, {axisMixVtxZQA, axisMixVtxZQA}); + mixingOperationQA.add("ME/Mixing/DeltaVtxZVsChannel", "Mixed-event #Delta z_{vtx};Mixing channel;z_{vtx}^{1}-z_{vtx}^{2} (cm)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaVtxZQA}); + + mixingOperationQA.add("ME/Mixing/CentFT0C1VsCentFT0C2", "FT0C centrality of mixed events;FT0C_{1} (%);FT0C_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/CentFT0M1VsCentFT0M2", "FT0M centrality of mixed events;FT0M_{1} (%);FT0M_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/CentFT0A1VsCentFT0A2", "FT0A centrality of mixed events;FT0A_{1} (%);FT0A_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/CentFV0A1VsCentFV0A2", "FV0A centrality of mixed events;FV0A_{1} (%);FV0A_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + + mixingOperationQA.add("ME/Mixing/DeltaCentFT0CVsChannel", "Mixed-event #Delta FT0C;Mixing channel;FT0C_{1}-FT0C_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentFT0MVsChannel", "Mixed-event #Delta FT0M;Mixing channel;FT0M_{1}-FT0M_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentFT0AVsChannel", "Mixed-event #Delta FT0A;Mixing channel;FT0A_{1}-FT0A_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentFV0AVsChannel", "Mixed-event #Delta FV0A;Mixing channel;FV0A_{1}-FV0A_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + + mixingOperationQA.add("ME/Mixing/MixingEstimator1Vs2", "Configured mixing estimator;Estimator_{1} (%);Estimator_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaMixingEstimatorVsChannel", "Configured mixing estimator difference;Mixing channel;Estimator_{1}-Estimator_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + + auto setMixOperationChannelLabels = [](TAxis* axis) { + for (int iChannel = 0; iChannel < NMixOperationChannels; ++iChannel) { + axis->SetBinLabel(iChannel + 1, MixOperationChannelName[iChannel].data()); + } + }; + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerBinChannel"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEvent"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaVtxZVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0CVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0MVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0AVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFV0AVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaMixingEstimatorVsChannel"))->GetXaxis()); + } + + using BinningTypeVtxZFT0C = ColumnBinningPolicy; + using BinningTypeVtxZFT0M = ColumnBinningPolicy; + using BinningTypeVtxZFT0A = ColumnBinningPolicy; + using BinningTypeVtxZFV0A = ColumnBinningPolicy; + + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + + template + int getDerivedMixingBin(const C& collision) + { + switch (cfgMixing.mixingEstimator) { + case kMixCentFT0C: + return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); + case kMixCentFT0M: + return colBinningFT0M.getBin({collision.posZ(), collision.centFT0M()}); + case kMixCentFT0A: + return colBinningFT0A.getBin({collision.posZ(), collision.centFT0A()}); + case kMixCentFV0A: + return colBinningFV0A.getBin({collision.posZ(), collision.centFV0A()}); + default: + return -1; + } + } + + template + float getDerivedMixingEstimatorValue(const C& collision) + { + switch (cfgMixing.mixingEstimator) { + case kMixCentFT0C: + return collision.centFT0C(); + case kMixCentFT0M: + return collision.centFT0M(); + case kMixCentFT0A: + return collision.centFT0A(); + case kMixCentFV0A: + return collision.centFV0A(); + default: + return -1.0f; + } + } + + //_______________________________ Particle Identification _______________________________ + // p-dependent identification + // p < ThrPforTOF : TPC+TOF (circular cut) if TOF is reliable, otherwise TPC only + // p >= ThrPforTOF : TPC+TOF (circular cut), TOF is required + // Default cuts are 3 sigma (see DefaultPIDcheckValues). + + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + template + bool vetoIdOthersTPC(const T& track) + { + // Static is only run once, ever. + static const std::array doVetoTPC = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTPC)}; + + static const std::array vetoTPC = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTPC)}; + + return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); + } + + template + bool vetoIdOthersTOF(const T& track) + { + // Only computed once + static const std::array doVetoTOF = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTOF)}; + + static const std::array vetoTOF = { + getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTOF)}; + + return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); + } + + template + bool vetoIdOthersTPCTOF(const T& track) + { + // If either veto fails, reject + return vetoIdOthersTPC(track) && vetoIdOthersTOF(track); + } + + // Check if TOF is reliable + template + inline bool checkReliableTOF(const T& track) + { + // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: + if (cfgIdCut.cfgId07CheckTofBeta) { + return (track.hasTOF() && track.beta() > 0.0f); + } + return track.hasTOF(); + } + + template + bool idTPC(const T& track, const float& nSigmaTPC, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + + static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPC(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCcheck); + if (doRelativeTPCcheck) { + if (!relativeIdOthersTPC(track)) { + // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPC = false; some particle has stronger nSigma compared to it + return false; + } + } + + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC; + } else { + return false; + } + } + + template + bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPCTOF(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTOFcheck); + if (doRelativeTOFcheck) { + if (!relativeIdOthersTOF(track)) { + // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + if (doRelativeTPCTOFcheck) { + if (!relativeIdOthersTPCTOF(track)) { + // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPCTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + switch (pidCutType) { + case kCircularCut: + return selIdCircularCut(track, nSigmaSquaredRad); + case kRectangularCut: + return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); + case kEllipsoidalCut: + return selIdEllipsoidalCut(track, nSigmaTPC, nSigmaTOF); + default: + return false; + } + } + + template + bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + // Static cache inside function - initialized once on first call + static const auto thrPforTOF = getCfg(cfgIdCut.pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadHighP); + + if (track.p() < thrPforTOF) { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeLowP, nSigmaTPCLowP, nSigmaTOFLowP, nSigmaRadLowP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } else { + if (idTPC(track, nSigmaTPCLowP, nSigmaIdDistSq)) { + IdMethod = kTPCidentified; + return true; + } + } + } else { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeHighP, nSigmaTPCHighP, nSigmaTOFHighP, nSigmaRadHighP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } + } + return false; + } + + //______________________________Identification Functions________________________________________________________________ + // Pion + template + bool selPion(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Kaon + template + bool selKaon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Proton + template + bool selProton(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Electron + template + bool selElectron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Muon + template + bool selMuon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Deuteron + template + bool selDeuteron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Two-argument versions + template + bool selPion(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selPion(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selKaon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selKaon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selProton(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selProton(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selElectron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selElectron(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selMuon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selMuon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selDeuteron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selDeuteron(track, IdMethod, nSigmaIdDistSq); + } + // + + template + float computeDerivedRapidity(const T& particle, const float mass) + { + const float pz = particle.pt() * std::sinh(particle.eta()); + const float p = particle.pt() * std::cosh(particle.eta()); + const float energy = std::sqrt(p * p + mass * mass); + return 0.5f * std::log((energy + pz) / (energy - pz)); + } + + template + void fillCorrParticleQA(H& histReg, const T& track) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + } + + template + void fillIdentifiedCorrelation(H& histReg, const T& trigger, const A& associate, const int idMethod) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("P"), associate.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassPiPlus)); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassKPlus)); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassProton)); + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxy"), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAz"), associate.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxyVsPt"), associate.pt(), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAzVsPt"), associate.pt(), associate.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCSignalVsP"), associate.p(), associate.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFBetaVsP"), associate.p(), associate.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("IdMethodVsP"), associate.p(), idMethod); + + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPi(), associate.tofNSigmaPi()); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaKa(), associate.tofNSigmaKa()); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPr(), associate.tofNSigmaPr()); + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + template + void fillResoAssocQA(H& histReg, const A& associate, const float invMass) + { + const float p = associate.pt() * std::cosh(associate.eta()); + const float rapidity = computeDerivedRapidity(associate, invMass); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("P"), p); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Rapidity"), rapidity); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMass"), invMass); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMassVsPt"), associate.pt(), invMass); + } + + template + void fillResoDauQA(H& histReg, const T& track, const int idMethod) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("IdMethodVsP"), track.p(), idMethod); + + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + float massHypothesis = 0.0f; + + if constexpr (pairType == kHPhi || (pairType == kHKStar && dauType == kPosDau) || (pairType == kHKStarBar && dauType == kNegDau) || + (pairType == kHLambda && dauType == kNegDau) || (pairType == kHLambdaBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + massHypothesis = MassKPlus; + } else if constexpr ((pairType == kHKStar && dauType == kNegDau) || (pairType == kHKStarBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + massHypothesis = MassPiPlus; + } else if constexpr ((pairType == kHLambda && dauType == kPosDau) || (pairType == kHLambdaBar && dauType == kNegDau)) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + massHypothesis = MassProton; + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Rapidity"), computeDerivedRapidity(track, massHypothesis)); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + template + void fillTrigAssociateCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + template + void fillResoCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + } + + template + void fillResoMassCorrelation(H& histReg, const T& trigger, const A& associate, const float invMass) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta); + } + + template + void fillResoRegion(H& histReg, const T& trigger, const A& associate, const P& posDauTrack, const N& negDauTrack, const float invMass, const int posDauIdMethod, const int negDauIdMethod) + { + fillResoAssocQA(histReg, associate, invMass); + fillResoDauQA(histReg, posDauTrack, posDauIdMethod); + fillResoDauQA(histReg, negDauTrack, negDauIdMethod); + fillResoCorrelation(histReg, trigger, associate); + fillResoMassCorrelation(histReg, trigger, associate, invMass); + } + + template + bool selectResonanceDaughters(const P& posDauTrack, const N& negDauTrack, int& posDauIdMethod, int& negDauIdMethod) + { + posDauIdMethod = kUnidentified; + negDauIdMethod = kUnidentified; + if constexpr (pairType == kHPhi) { + return selKaon(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStar) { + return selKaon(posDauTrack, posDauIdMethod) && selPion(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStarBar) { + return selPion(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambda) { + return selProton(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambdaBar) { + return selKaon(posDauTrack, posDauIdMethod) && selProton(negDauTrack, negDauIdMethod); + } + return false; + } + + template + uint8_t getResonanceMassRegion(const A& associate, float& invMass) + { + if constexpr (pairType == kHPhi) { + invMass = associate.mPhi1020(); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.phi1020LSBLow, cfgPhi1020CorrMass.phi1020LSBUp, cfgPhi1020CorrMass.phi1020PeakLow, cfgPhi1020CorrMass.phi1020PeakUp, cfgPhi1020CorrMass.phi1020RSBLow, cfgPhi1020CorrMass.phi1020RSBUp); + } else if constexpr (pairType == kHKStar) { + invMass = associate.mKStar892(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + } else if constexpr (pairType == kHKStarBar) { + invMass = associate.mKStar892Bar(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + } else if constexpr (pairType == kHLambda) { + invMass = associate.mLambda1520(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + } else if constexpr (pairType == kHLambdaBar) { + invMass = associate.mLambda1520Bar(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + } + + invMass = -1.0f; + return kMassOutside; + } + + template + constexpr uint64_t getDerivedResoRegionMask() + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if constexpr (pairType == kHPhi) { + return getResoRegionMaskBit(kResoCountHPhi, PtType, massRegion); + } else if constexpr (pairType == kHKStar) { + return getResoRegionMaskBit(kResoCountHKStar, PtType, massRegion); + } else if constexpr (pairType == kHKStarBar) { + return getResoRegionMaskBit(kResoCountHKStarBar, PtType, massRegion); + } else if constexpr (pairType == kHLambda) { + return getResoRegionMaskBit(kResoCountHLambda, PtType, massRegion); + } else if constexpr (pairType == kHLambdaBar) { + return getResoRegionMaskBit(kResoCountHLambdaBar, PtType, massRegion); + } + + return 0ULL; + } + + template + bool derivedChannelEnabled(uint64_t mask) + { + constexpr bool LowPt = roleType == kAssocLowPt; + if constexpr (pairType == kHH) { + return mask & (LowPt ? kMaskHHLowPt : kMaskHHHighPt); + } + if constexpr (pairType == kHId && pidType == kPi) { + return mask & (LowPt ? kMaskHPiLowPt : kMaskHPiHighPt); + } + if constexpr (pairType == kHId && pidType == kKa) { + return mask & (LowPt ? kMaskHKaLowPt : kMaskHKaHighPt); + } + if constexpr (pairType == kHId && pidType == kPr) { + return mask & (LowPt ? kMaskHPrLowPt : kMaskHPrHighPt); + } + if constexpr (pairType == kHPhi) { + return mask & (LowPt ? kMaskHPhiLowPt : kMaskHPhiHighPt); + } + if constexpr (pairType == kHKStar) { + return mask & (LowPt ? kMaskHKStarLowPt : kMaskHKStarHighPt); + } + if constexpr (pairType == kHKStarBar) { + return mask & (LowPt ? kMaskHKStarBarLowPt : kMaskHKStarBarHighPt); + } + if constexpr (pairType == kHLambda) { + return mask & (LowPt ? kMaskHLambdaLowPt : kMaskHLambdaHighPt); + } + if constexpr (pairType == kHLambdaBar) { + return mask & (LowPt ? kMaskHLambdaBarLowPt : kMaskHLambdaBarHighPt); + } + return false; + } + + template + bool derivedPairEnabled(uint64_t mask) + { + if constexpr (pairType == kHH) { + return mask & (kMaskHHLowPt | kMaskHHHighPt); + } + if constexpr (pairType == kHId) { + return mask & (kMaskHPiLowPt | kMaskHPiHighPt | kMaskHKaLowPt | kMaskHKaHighPt | kMaskHPrLowPt | kMaskHPrHighPt); + } + if constexpr (pairType == kHPhi) { + return mask & (kMaskHPhiLowPt | kMaskHPhiHighPt); + } + if constexpr (pairType == kHKStar) { + return mask & (kMaskHKStarLowPt | kMaskHKStarHighPt); + } + if constexpr (pairType == kHKStarBar) { + return mask & (kMaskHKStarBarLowPt | kMaskHKStarBarHighPt); + } + if constexpr (pairType == kHLambda) { + return mask & (kMaskHLambdaLowPt | kMaskHLambdaHighPt); + } + if constexpr (pairType == kHLambdaBar) { + return mask & (kMaskHLambdaBarLowPt | kMaskHLambdaBarHighPt); + } + return false; + } + + template + void executeDerivedTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, uint64_t commonCorrMask, uint8_t requiredMassRegion = kMassNone) + { + for (const auto& associate : associates) { + + if constexpr (pairType == kHH) { + if (!derivedChannelEnabled(commonCorrMask)) { + continue; + } + fillCorrParticleQA(histReg, associate); + fillTrigAssociateCorrelation(histReg, trigger, associate); + } + + if constexpr (pairType == kHId) { + int pionIdMethod = kUnidentified; + int kaonIdMethod = kUnidentified; + int protonIdMethod = kUnidentified; + if (derivedChannelEnabled(commonCorrMask) && selPion(associate, pionIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); + } + if (derivedChannelEnabled(commonCorrMask) && selKaon(associate, kaonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); + } + if (derivedChannelEnabled(commonCorrMask) && selProton(associate, protonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); + } + } + + if constexpr (pairType == kHPhi || pairType == kHKStar || pairType == kHKStarBar || pairType == kHLambda || pairType == kHLambdaBar) { + if (requiredMassRegion == kMassNone && !derivedChannelEnabled(commonCorrMask)) { + continue; + } + if (requiredMassRegion != kMassNone && commonCorrMask == 0ULL) { + continue; + } + + const auto& posDauTrack = associate.template posTrack_as(); + const auto& negDauTrack = associate.template negTrack_as(); + + int posDauIdMethod = kUnidentified; + int negDauIdMethod = kUnidentified; + if (!selectResonanceDaughters(posDauTrack, negDauTrack, posDauIdMethod, negDauIdMethod)) { + continue; + } + + float invMass = -1.0f; + const uint8_t massRegion = getResonanceMassRegion(associate, invMass); + if (massRegion == kMassOutside) { + continue; + } + const float rapidity = computeDerivedRapidity(associate, invMass); + if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + continue; + } + if (requiredMassRegion != kMassNone && massRegion != requiredMassRegion) { + continue; + } + + if (massRegion == kMassPeak) { + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + } else if (massRegion == kMassLSB) { + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + } else if (massRegion == kMassRSB) { + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + } + } + } + } + + template + void executeDerivedCorrelation(H& histReg, const T& triggers, const A& associatesLowPt, const B& associatesHighPt, uint64_t commonCorrMask) + { + if (!derivedPairEnabled(commonCorrMask)) { + return; + } + + for (const auto& trigger : triggers) { + fillCorrParticleQA(histReg, trigger); + executeDerivedTrigAssocCorrelation(histReg, trigger, associatesLowPt, commonCorrMask); + executeDerivedTrigAssocCorrelation(histReg, trigger, associatesHighPt, commonCorrMask); + } + } + + template + void executeDerivedCorrelationRole(H& histReg, const T& triggers, const A& associates, uint8_t requiredMassRegion = kMassNone) + { + for (const auto& trigger : triggers) { + fillCorrParticleQA(histReg, trigger); + executeDerivedTrigAssocCorrelation(histReg, trigger, associates, requiredMask, requiredMassRegion); + } + } + + Preslice drTracksPerCollisionPreslice = aod::hpcorrtrack::hpCorrCollisionId; + Preslice drResoPerCollisionPreslice = aod::hpcorrresonance::hpCorrCollisionId; + + // definition of partitions + SliceCache cache; + Partition drMixHHLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHLowPt)) != static_cast(0ULL); + Partition drMixHHHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHHighPt)) != static_cast(0ULL); + + Partition drMixHPiLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiLowPt)) != static_cast(0ULL); + Partition drMixHPiHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiHighPt)) != static_cast(0ULL); + Partition drMixHKaLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaLowPt)) != static_cast(0ULL); + Partition drMixHKaHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaHighPt)) != static_cast(0ULL); + Partition drMixHPrLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrLowPt)) != static_cast(0ULL); + Partition drMixHPrHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrHighPt)) != static_cast(0ULL); + + Partition drMixHPhiLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtPeak)) != static_cast(0ULL); + Partition drMixHPhiLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtLSB)) != static_cast(0ULL); + Partition drMixHPhiLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtRSB)) != static_cast(0ULL); + + Partition drMixHPhiHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtPeak)) != static_cast(0ULL); + Partition drMixHPhiHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtLSB)) != static_cast(0ULL); + Partition drMixHPhiHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtRSB)) != static_cast(0ULL); + + Partition drMixHKStarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtPeak)) != static_cast(0ULL); + Partition drMixHKStarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtLSB)) != static_cast(0ULL); + Partition drMixHKStarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtRSB)) != static_cast(0ULL); + + Partition drMixHKStarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtPeak)) != static_cast(0ULL); + Partition drMixHKStarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtLSB)) != static_cast(0ULL); + Partition drMixHKStarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtRSB)) != static_cast(0ULL); + + Partition drMixHKStarBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtPeak)) != static_cast(0ULL); + Partition drMixHKStarBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtLSB)) != static_cast(0ULL); + Partition drMixHKStarBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtRSB)) != static_cast(0ULL); + + Partition drMixHKStarBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtPeak)) != static_cast(0ULL); + Partition drMixHKStarBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtLSB)) != static_cast(0ULL); + Partition drMixHKStarBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtRSB)) != static_cast(0ULL); + + Partition drMixHLambdaLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtRSB)) != static_cast(0ULL); + + Partition drMixHLambdaHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtRSB)) != static_cast(0ULL); + + Partition drMixHLambdaBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtRSB)) != static_cast(0ULL); + + Partition drMixHLambdaBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtRSB)) != static_cast(0ULL); + + Partition drTriggerTracks = cfgPartitions.triggerPtLow < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.triggerPtHigh; + Partition drPosTracks = aod::hpcorrtrack::sign > static_cast(0); + Partition drNegTracks = aod::hpcorrtrack::sign < static_cast(0); + Partition drAssocTracksLowPt = cfgPartitions.assocPtLowMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.assocPtLowMax; + Partition drAssocTracksHighPt = cfgPartitions.assocPtHighMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.assocPtHighMax; + + Partition drAssocPhiLowPt = cfgResPartitions.phiPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.phiPtLowMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); + Partition drAssocPhiHighPt = cfgResPartitions.phiPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.phiPtHighMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); + Partition drAssocKStarLowPt = cfgResPartitions.kstarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarPtLowMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); + Partition drAssocKStarHighPt = cfgResPartitions.kstarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarPtHighMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); + Partition drAssocKStarBarLowPt = cfgResPartitions.kstarBarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarBarPtLowMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); + Partition drAssocKStarBarHighPt = cfgResPartitions.kstarBarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarBarPtHighMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); + Partition drAssocLambda1520LowPt = cfgResPartitions.lambda1520PtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520PtLowMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); + Partition drAssocLambda1520HighPt = cfgResPartitions.lambda1520PtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520PtHighMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); + Partition drAssocLambda1520BarLowPt = cfgResPartitions.lambda1520BarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520BarPtLowMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); + Partition drAssocLambda1520BarHighPt = cfgResPartitions.lambda1520BarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520BarPtHighMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); + + int dfNumber = 0; + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelationMixedEvent, processNothing, "Dummy process for analysis", true); + + int nColl = 0; + void processMixEventInDeriveData(aod::Origins const& origins, aod::HPCorrCollisions const& drCollisions, aod::HPCorrTracks const& drFullTracks, aod::HPCorrResonances const& drResonanceCndts /*, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&*/) + { + dfNumber++; + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: origins = " << origins.size() << " :: drCollisions = " << drCollisions.size() << " :: drFullTracks = " << drFullTracks.size() << " :: drResonanceCndts = " << drResonanceCndts.size(); + } + + int64_t nTrackChecks = 0; + int64_t nBadCollisionIndex = 0; + int64_t nBadTrackCollision = 0; + int64_t nResonanceChecks = 0; + int64_t nBadResonanceCollision = 0; + int64_t nBadResonanceDaughters = 0; + for (const auto& coll : drCollisions) { + auto drTracksPerColl = drFullTracks.sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drResonancesPerColl = drResonanceCndts.sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); + auto drTriggerTracksPerColl = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drPosTracksPerColl = drPosTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drNegTracksPerColl = drNegTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drAssocTracksLowPtPerColl = drAssocTracksLowPt->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drAssocTracksHighPtPerColl = drAssocTracksHighPt->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); + auto drAssocPhiLowPtPerColl = drAssocPhiLowPt->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); + auto drAssocPhiHighPtPerColl = drAssocPhiHighPt->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); + + if (coll.globalIndex() != coll.globalCollisionId()) { + nBadCollisionIndex++; + LOG(error) << "DEBUG :: nColl = " << nColl << " :: coll.globalIndex() != coll.globalCollisionId() i.e " << coll.globalIndex() << " != " << coll.globalCollisionId(); + } + + for (const auto& track : drTracksPerColl) { + nTrackChecks++; + if (track.hpCorrCollisionId() != coll.globalIndex()) { + nBadTrackCollision++; + LOG(error) << "TRACK CHECK :: hpCorrCollisionId mismatch :: " << track.hpCorrCollisionId() << " != " << coll.globalIndex(); + } + if (track.globalColRefId() != coll.globalCollisionId()) { + nBadTrackCollision++; + LOG(error) << "TRACK CHECK :: globalColRefId mismatch :: " << track.globalColRefId() << " != " << coll.globalCollisionId(); + } + if (track.originalColRefId() != coll.originalCollisionId()) { + nBadTrackCollision++; + LOG(error) << "TRACK CHECK :: originalColRefId mismatch :: " << track.originalColRefId() << " != " << coll.originalCollisionId(); + } + if (track.dataframeID() != coll.dataframeID()) { + nBadTrackCollision++; + LOG(error) << "TRACK CHECK :: dataframeID mismatch :: " << track.dataframeID() << " != " << coll.dataframeID(); + } + if (track.globalTrackId() != track.globalIndex()) { + nBadTrackCollision++; + LOG(error) << "TRACK CHECK :: globalTrackId mismatch :: " << track.globalTrackId() << " != " << track.globalIndex(); + } + } + + for (const auto& resonance : drResonancesPerColl) { + nResonanceChecks++; + + if (resonance.hpCorrCollisionId() != coll.globalIndex()) { + nBadResonanceCollision++; + LOG(error) << "RESONANCE CHECK :: hpCorrCollisionId mismatch :: " << resonance.hpCorrCollisionId() << " != " << coll.globalIndex(); + } + if (resonance.globalColRefId() != coll.globalCollisionId()) { + nBadResonanceCollision++; + LOG(error) << "RESONANCE CHECK :: globalColRefId mismatch :: " << resonance.globalColRefId() << " != " << coll.globalCollisionId(); + } + if (resonance.originalColRefId() != coll.originalCollisionId()) { + nBadResonanceCollision++; + LOG(error) << "RESONANCE CHECK :: originalColRefId mismatch :: " << resonance.originalColRefId() << " != " << coll.originalCollisionId(); + } + if (resonance.dataframeID() != coll.dataframeID()) { + nBadResonanceCollision++; + LOG(error) << "RESONANCE CHECK :: dataframeID mismatch :: " << resonance.dataframeID() << " != " << coll.dataframeID(); + } + if (resonance.globalResonanceId() != resonance.globalIndex()) { + nBadResonanceCollision++; + LOG(error) << "RESONANCE CHECK :: globalResonanceId mismatch :: " << resonance.globalResonanceId() << " != " << resonance.globalIndex(); + } + + const auto& posTrack = resonance.posTrack_as(); + const auto& negTrack = resonance.negTrack_as(); + + if (resonance.posTrackId() != posTrack.globalIndex()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: posTrackId mismatch :: " << resonance.posTrackId() << " != " << posTrack.globalIndex(); + } + if (resonance.negTrackId() != negTrack.globalIndex()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: negTrackId mismatch :: " << resonance.negTrackId() << " != " << negTrack.globalIndex(); + } + + if (resonance.globalPosTrackRefId() != posTrack.globalTrackId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: globalPosTrackRefId mismatch :: " << resonance.globalPosTrackRefId() << " != " << posTrack.globalTrackId(); + } + if (resonance.globalNegTrackRefId() != negTrack.globalTrackId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: globalNegTrackRefId mismatch :: " << resonance.globalNegTrackRefId() << " != " << negTrack.globalTrackId(); + } + + if (resonance.originalPosTrackRefId() != posTrack.originalTrackId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: originalPosTrackRefId mismatch :: " << resonance.originalPosTrackRefId() << " != " << posTrack.originalTrackId(); + } + if (resonance.originalNegTrackRefId() != negTrack.originalTrackId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: originalNegTrackRefId mismatch :: " << resonance.originalNegTrackRefId() << " != " << negTrack.originalTrackId(); + } + + if (resonance.dataframeID() != posTrack.dataframeID()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: resonance/posTrack dataframeID mismatch :: " << resonance.dataframeID() << " != " << posTrack.dataframeID(); + } + if (resonance.dataframeID() != negTrack.dataframeID()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: resonance/negTrack dataframeID mismatch :: " << resonance.dataframeID() << " != " << negTrack.dataframeID(); + } + + if (posTrack.hpCorrCollisionId() != coll.globalIndex()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: positive daughter belongs to wrong collision :: " << posTrack.hpCorrCollisionId() << " != " << coll.globalIndex(); + } + if (negTrack.hpCorrCollisionId() != coll.globalIndex()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: negative daughter belongs to wrong collision :: " << negTrack.hpCorrCollisionId() << " != " << coll.globalIndex(); + } + + if (posTrack.originalColRefId() != coll.originalCollisionId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: positive daughter original collision mismatch :: " << posTrack.originalColRefId() << " != " << coll.originalCollisionId(); + } + if (negTrack.originalColRefId() != coll.originalCollisionId()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: negative daughter original collision mismatch :: " << negTrack.originalColRefId() << " != " << coll.originalCollisionId(); + } + + if (posTrack.dataframeID() != coll.dataframeID()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: positive daughter dataframe mismatch :: " << posTrack.dataframeID() << " != " << coll.dataframeID(); + } + if (negTrack.dataframeID() != coll.dataframeID()) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: negative daughter dataframe mismatch :: " << negTrack.dataframeID() << " != " << coll.dataframeID(); + } + + if (posTrack.sign() <= 0) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: positive daughter has wrong sign :: " << static_cast(posTrack.sign()); + } + if (negTrack.sign() >= 0) { + nBadResonanceDaughters++; + LOG(error) << "DAUGHTER CHECK :: negative daughter has wrong sign :: " << static_cast(negTrack.sign()); + } + } + nColl++; + } // Collision loop is over + + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: SANITY CHECK SUMMARY :: bad collision indices = " << nBadCollisionIndex << " :: tracks checked = " << nTrackChecks << " :: bad track references = " << nBadTrackCollision << " :: resonances checked = " << nResonanceChecks << " :: bad resonance collision references = " << nBadResonanceCollision << " :: bad resonance daughter references = " << nBadResonanceDaughters; + } + if (nBadCollisionIndex == 0 && nBadTrackCollision == 0 && nBadResonanceCollision == 0 && nBadResonanceDaughters == 0) { + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: SANITY CHECK PASSED"; + } + } else { + LOG(fatal) << "DEBUG :: SANITY CHECK FAILED"; + } + + int64_t nMixedEventPairs = 0; + + auto runRestrictedMixing = [&](auto& colBinning, auto& collisionPool, const int mixChannel, auto&& processPair) { + collisionPool.bindTable(drCollisions); + std::vector partnerCountPerTriggerEvent(drCollisions.size(), -1); + for (const auto& collision : collisionPool) { + partnerCountPerTriggerEvent[collision.globalIndex()] = 0; + } + + for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.nEvtMixing, -1, collisionPool, collisionPool)) { + nMixedEventPairs++; + + if (collision1.dataframeID() == collision2.dataframeID() && collision1.originalCollisionId() == collision2.originalCollisionId()) { + LOG(fatal) << "EVENT MIXING ERROR :: same original collision mixed with itself"; + } + + const int mixingBin1 = getDerivedMixingBin(collision1); + const int mixingBin2 = getDerivedMixingBin(collision2); + + if (mixingBin1 < 0 || mixingBin2 < 0) { + LOG(fatal) << "EVENT MIXING ERROR :: invalid mixing bin :: " << mixingBin1 << " :: " << mixingBin2; + } + + if (mixingBin1 != mixingBin2) { + LOG(fatal) << "EVENT MIXING ERROR :: collisions from different mixing bins :: " << mixingBin1 << " :: " << mixingBin2; + } + + partnerCountPerTriggerEvent[collision1.globalIndex()]++; + + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerChannel"), mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerBinChannel"), mixingBin1, mixChannel); + + mixingOperationQA.fill(HIST("ME/Mixing/VtxZ1VsVtxZ2"), collision1.posZ(), collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaVtxZVsChannel"), mixChannel, collision1.posZ() - collision2.posZ()); + + mixingOperationQA.fill(HIST("ME/Mixing/CentFT0C1VsCentFT0C2"), collision1.centFT0C(), collision2.centFT0C()); + mixingOperationQA.fill(HIST("ME/Mixing/CentFT0M1VsCentFT0M2"), collision1.centFT0M(), collision2.centFT0M()); + mixingOperationQA.fill(HIST("ME/Mixing/CentFT0A1VsCentFT0A2"), collision1.centFT0A(), collision2.centFT0A()); + mixingOperationQA.fill(HIST("ME/Mixing/CentFV0A1VsCentFV0A2"), collision1.centFV0A(), collision2.centFV0A()); + + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0CVsChannel"), mixChannel, collision1.centFT0C() - collision2.centFT0C()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0MVsChannel"), mixChannel, collision1.centFT0M() - collision2.centFT0M()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0AVsChannel"), mixChannel, collision1.centFT0A() - collision2.centFT0A()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFV0AVsChannel"), mixChannel, collision1.centFV0A() - collision2.centFV0A()); + + const float mixingEstimator1 = getDerivedMixingEstimatorValue(collision1); + const float mixingEstimator2 = getDerivedMixingEstimatorValue(collision2); + + mixingOperationQA.fill(HIST("ME/Mixing/MixingEstimator1Vs2"), mixingEstimator1, mixingEstimator2); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaMixingEstimatorVsChannel"), mixChannel, mixingEstimator1 - mixingEstimator2); + + processPair(collision1, collision2); + } + + for (const auto& collision : collisionPool) { + const int nPartners = partnerCountPerTriggerEvent[collision.globalIndex()]; + const int mixingBin = getDerivedMixingBin(collision); + + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEvent"), mixChannel, nPartners); + + if (mixingBin >= 0) { + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"), mixingBin, mixChannel, nPartners); + } + } + }; + + auto runTrackChannelMixing = [&](auto& colBinning, auto& collisionPool, auto& assocPartition, auto& histReg) { + constexpr int MixChannel = getMixOperationChannel(requiredMask); + static_assert(MixChannel >= 0, "Invalid track mixing-operation channel"); + + runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { + auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision1.globalIndex(), cache); + auto associates2 = assocPartition->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision2.globalIndex(), cache); + + executeDerivedCorrelationRole(histReg, triggerTracks1, associates2); + }); + }; // NOLINT(readability/braces) + + auto runAllTrackMixing = [&](auto& colBinning) { + runTrackChannelMixing.template operator()(colBinning, drMixHHLowPt, drAssocTracksLowPt, hhCorrelation); + runTrackChannelMixing.template operator()(colBinning, drMixHHHighPt, drAssocTracksHighPt, hhCorrelation); + + runTrackChannelMixing.template operator()(colBinning, drMixHPiLowPt, drAssocTracksLowPt, hIdCorrelation); + runTrackChannelMixing.template operator()(colBinning, drMixHPiHighPt, drAssocTracksHighPt, hIdCorrelation); + + runTrackChannelMixing.template operator()(colBinning, drMixHKaLowPt, drAssocTracksLowPt, hIdCorrelation); + runTrackChannelMixing.template operator()(colBinning, drMixHKaHighPt, drAssocTracksHighPt, hIdCorrelation); + + runTrackChannelMixing.template operator()(colBinning, drMixHPrLowPt, drAssocTracksLowPt, hIdCorrelation); + runTrackChannelMixing.template operator()(colBinning, drMixHPrHighPt, drAssocTracksHighPt, hIdCorrelation); + }; + + auto runResoRegionMixing = [&](auto& colBinning, auto& collisionPool, auto& assocPartition, auto& histReg) { + constexpr int MixChannel = getMixOperationChannel(requiredMask); + static_assert(MixChannel >= 0, "Invalid resonance mixing-operation channel"); + + runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { + auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision1.globalIndex(), cache); + auto associates2 = assocPartition->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, collision2.globalIndex(), cache); + + associates2.bindExternalIndices(&drFullTracks); + + executeDerivedCorrelationRole(histReg, triggerTracks1, associates2, requiredMassRegion); + }); + }; // NOLINT(readability/braces) + + auto runAllResoMixing = [&](auto& colBinning) { + runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtPeak, drAssocPhiLowPt, hPhiCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtLSB, drAssocPhiLowPt, hPhiCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtRSB, drAssocPhiLowPt, hPhiCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtPeak, drAssocPhiHighPt, hPhiCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtLSB, drAssocPhiHighPt, hPhiCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtRSB, drAssocPhiHighPt, hPhiCorrelation); + + runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtPeak, drAssocKStarLowPt, hKStarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtLSB, drAssocKStarLowPt, hKStarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtRSB, drAssocKStarLowPt, hKStarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtPeak, drAssocKStarHighPt, hKStarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtLSB, drAssocKStarHighPt, hKStarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtRSB, drAssocKStarHighPt, hKStarCorrelation); + + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtPeak, drAssocKStarBarLowPt, hKStarBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtLSB, drAssocKStarBarLowPt, hKStarBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtRSB, drAssocKStarBarLowPt, hKStarBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtPeak, drAssocKStarBarHighPt, hKStarBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtLSB, drAssocKStarBarHighPt, hKStarBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtRSB, drAssocKStarBarHighPt, hKStarBarCorrelation); + + runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtPeak, drAssocLambda1520LowPt, hLambdaCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtLSB, drAssocLambda1520LowPt, hLambdaCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtRSB, drAssocLambda1520LowPt, hLambdaCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtPeak, drAssocLambda1520HighPt, hLambdaCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtLSB, drAssocLambda1520HighPt, hLambdaCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtRSB, drAssocLambda1520HighPt, hLambdaCorrelation); + + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtPeak, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtLSB, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtRSB, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtPeak, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtLSB, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); + runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtRSB, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); + }; + + switch (cfgMixing.mixingEstimator) { + case kMixCentFT0C: + runAllTrackMixing(colBinningFT0C); + runAllResoMixing(colBinningFT0C); + break; + + case kMixCentFT0M: + runAllTrackMixing(colBinningFT0M); + runAllResoMixing(colBinningFT0M); + break; + + case kMixCentFT0A: + runAllTrackMixing(colBinningFT0A); + runAllResoMixing(colBinningFT0A); + break; + + case kMixCentFV0A: + runAllTrackMixing(colBinningFV0A); + runAllResoMixing(colBinningFV0A); + break; + + default: + LOG(fatal) << "DEBUG :: Invalid mixingEstimator = " << static_cast(cfgMixing.mixingEstimator); + break; + } + if (cfgDebug.printDebugMessages) { + LOG(info) << "DEBUG :: EVENT MIXING SUMMARY :: mixed collision pairs = " << nMixedEventPairs; + } + } + PROCESS_SWITCH(HParticleCorrelationMixedEvent, processMixEventInDeriveData, "Process Mix event in derive data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context), + adaptAnalysisTask(context), + adaptAnalysisTask(context)}; +} From ad321d8244468599b49781a03d31f33db726f6cc Mon Sep 17 00:00:00 2001 From: Hamwooseok <168405791+Hamwooseok@users.noreply.github.com> Date: Wed, 7 Oct 2026 18:56:59 +0900 Subject: [PATCH 208/648] [PWGJE] add hasColl pass/fail diagnostics for event selections (#18199) Co-authored-by: Wooseok Ham --- PWGJE/Tasks/jetCrossSectionEfficiency.cxx | 58 +++++++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx index cfc984b2c50..9fce23a2353 100644 --- a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx +++ b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -151,6 +152,7 @@ struct JetCrossSectionEfficiency { static constexpr float ConfigSwitchHigh = 9998.0f; static constexpr float BrokenPtHardSentinel = 1.0f; static constexpr int MinITSClustersForOccupancy = 5; + static constexpr int NumberOfHasCollSelectionConditionDiagnostics = 5; static constexpr int HasCollQaMaxCount = 500; static constexpr int HasCollQaMaxDeltaGlobalBC = 100000; static constexpr int64_t NoOtherTruthCollision = -1; @@ -370,6 +372,8 @@ struct JetCrossSectionEfficiency { if (isSel8FullPbPb) { AxisSpec axisPbPbTruthSelectionOnly = {PbPbTruthSelectionOnlyNBins, 0.5, static_cast(PbPbTruthSelectionOnlyNBins) + 0.5, "event selection (PbPb truth-only)"}; + AxisSpec selectionConditionAxis = {NumberOfHasCollSelectionConditionDiagnostics, 0.5, static_cast(NumberOfHasCollSelectionConditionDiagnostics) + 0.5, "selection condition"}; + AxisSpec hasCollSelectionStateAxis = {4, 0.5, 4.5, "hasColl and selection-condition state"}; std::vector timeRangeOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInTimeRangeStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; std::vector rofOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInRofStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; @@ -390,6 +394,19 @@ struct JetCrossSectionEfficiency { {HistType::kTH1F, {axisPbPbTruthSelectionOnly}}); setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_rofOnly_truth"))->GetYaxis(), rofOnlyLabels); setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_bcBitsFirst_rofOnly_truth"))->GetXaxis(), rofOnlyLabels); + + registry.add("h2_mccollisions_selection_bit_vs_has_coll_state", + "weighted MC collisions after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + registry.add("h2_mcpjets_selection_bit_vs_has_coll_state", + "weighted selected MCP jets after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + std::vector selectionConditionLabels = {"kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup (truth proxy)", "kNoCollInTimeRangeStandard (truth proxy)", "kNoCollInRofStandard (truth proxy)"}; + std::vector hasCollSelectionStateLabels = {"noColl, fail", "noColl, pass", "hasColl, fail", "hasColl, pass"}; + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); } } @@ -1369,11 +1386,12 @@ struct JetCrossSectionEfficiency { bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); - bool truthNoSBP = true; - if (applySBP) { + bool truthNoSBPDiagnostic = true; + if (applySBP || isSel8FullPbPb) { auto sameBC = allMcCollisions.sliceBy(mcCollsPerBC, mccollision.bcId()); - truthNoSBP = (sameBC.size() == 1); + truthNoSBPDiagnostic = (sameBC.size() == 1); } + bool truthNoSBP = applySBP ? truthNoSBPDiagnostic : true; bool hasRecoColl = (collisions.size() >= 1); bool passesZvtxCutReco = false; @@ -1398,6 +1416,38 @@ struct JetCrossSectionEfficiency { } bool passesRct = applyRCT ? (truthBC.rct_raw() & rctMask) == 0 : true; + float weight = mccollision.weight(); + + if (isSel8FullPbPb && passesRct && passesTVXTruth && truthPbPbSelections.valid) { + // The five selection conditions are evaluated independently on the same RCT+TVX + // baseline with valid PbPb truth-proxy evaluation. + const std::array selectionConditionPasses = { + passesNoTFBTruth, + passesNoITSROFBTruth, + truthNoSBPDiagnostic, + truthPbPbSelections.noCollInTimeRangeStandard, + truthPbPbSelections.noCollInRofStandard}; + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); + } + + float pTHatDiagnostic = computePtHat(mccollision); + if (pTHatDiagnostic >= pTHatAbsoluteMin) { + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + jet.pt() < jetPtMin || jet.pt() > pTHatMaxMCP * pTHatDiagnostic || + !isAcceptedJet(jet)) { + continue; + } + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); + } + } + } + } + std::vector pass = {true, passesRct, passesTVXTruth, applyTFB ? passesNoTFBTruth : true, applyROFB ? passesNoITSROFBTruth : true}; @@ -1414,8 +1464,6 @@ struct JetCrossSectionEfficiency { pass.push_back(passesZvtxCutReco); pass.push_back(hasRecoColl && noSplitPass); - float weight = mccollision.weight(); - int sMaxTimeRangeOnly = 0; int sMaxRofOnly = 0; if (isSel8FullPbPb) { From e4f2aa67f11b9331c086c8515fa5782a1d73f85f Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:19:36 +0200 Subject: [PATCH 209/648] [PWGLF] Add EventRecoEfficiency Study and Ngen Study in hStrangeCorrelation.cxx (#18200) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 379 +++++++++++++----- 1 file changed, 288 insertions(+), 91 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 23286d1e870..a9224f9ad02 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -186,6 +187,8 @@ struct HStrangeCorrelation { ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.01f, 1.21f}, "Inv. Mass (GeV/c^{2})"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Multiplicity axis in model prediction process"}; ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; + ConfigurableAxis axisNchGen{"axisNchGen", {151, -0.5f, 150.5f}, "generated charged primaries in |eta| < 1"}; + ConfigurableAxis axisNRecoCollisions{"axisNRecoCollisions", {6, -0.5f, 5.5f}, "reconstructed collisions per MC collision"}; } axesConfigurations; @@ -324,9 +327,16 @@ struct HStrangeCorrelation { // processSameEventHV0s must be off when it is on. // doGenLevelStudy PairLossK0/GenStudy: generator-level only, see the // comment on runGenLevelStudy in processPairLossK0MC + // doEventRecoEfficiencyStudy + // PairLossK0/EventRecoEfficiency: MC-collision + // reconstruction efficiency and Nch^gen vs Nch^reco + // response, see runEventRecoEfficiencyStudy Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; + Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, and Nch^gen vs multNTracksPVeta1"}; + Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; + ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8 // by mCounter. Plain ConfigurableAxis: unlike the correlation axes it is NOT // trimmed by skipUnderOverflowInTHn, so what you configure is what you get. @@ -693,6 +703,9 @@ struct HStrangeCorrelation { int64_t bestCollisionId = -1; float vtxZ = 0.0f; float multiplicity = 0.0f; + // Nch^gen of the MC collision; >= 0 only with applyINELSelectionAtGenLevel, + // then the Rec branches fill only PairLossK0/Comparison/NchGen + int nChGen = -1; std::unordered_map trackToMc; std::unordered_map v0ToMc; std::unordered_map truthTriggers; @@ -713,6 +726,7 @@ struct HStrangeCorrelation { bestCollisionId = -1; vtxZ = 0.0f; multiplicity = 0.0f; + nChGen = -1; trackToMc.clear(); v0ToMc.clear(); truthTriggers.clear(); @@ -1261,7 +1275,8 @@ struct HStrangeCorrelation { hist->SetBinContent(binx, biny, newContent); hist->SetBinError(binx, biny, newUncert); } - void fillCorrelationHistogram(std::shared_ptr const& hist, std::array const& binFillThn, float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) + template + void fillCorrelationHistogram(std::shared_ptr const& hist, std::array const& binFillThn, float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) { float previousContent = 0.0f, previousError2 = 0.0f, currentContent = 0.0f, currentError2 = 0.0f; int bin = hist->GetBin(binFillThn.data()); @@ -1272,6 +1287,13 @@ struct HStrangeCorrelation { hist->SetBinContent(bin, currentContent); hist->SetBinError2(bin, currentError2); } + // Rec coordinates for the PairLossK0/Comparison/NchGen histograms: the vertex-z + // and multiplicity coordinates are replaced by the Nch^gen of the current + // PairLoss MC collision + static std::array toPairLossNchGenCoordinates(std::array const& binFillThn) + { + return {binFillThn[0], binFillThn[1], binFillThn[2], binFillThn[3], static_cast(pairLossComparison.nChGen)}; + } void fillCorrelationsV0(aod::TriggerTracks const& triggers, aod::AssocV0s const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; @@ -1330,12 +1352,16 @@ struct HStrangeCorrelation { if (!mixing) { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesV0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + if (pairLossComparison.active && pairLossComparison.nChGen >= 0) { + fillTriggerHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/TriggerRec")), trigg.pt(), pairLossComparison.nChGen, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } // Trigger-side control chain, filled from the exact Rec trigger loop so // that it is the same denominator the ordinary Rec correlation uses. // Each pair variant needs the matching trigger variant, otherwise only - // shapes and not per-trigger amplitudes can be compared. - if (pairLossComparison.active) { + // shapes and not per-trigger amplitudes can be compared. Not booked with + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0). + if (pairLossComparison.active && pairLossComparison.nChGen < 0) { const double triggerWeight = purityTrigg / efficiencyTrigg; auto fillTriggerVariant = [&](int variant, double fillPtTrigger, double fillVtxZ, double fillMult, double weight) { histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), variant, fillPtTrigger, fillVtxZ, fillMult, weight); @@ -1536,7 +1562,11 @@ struct HStrangeCorrelation { if (pairLossComparison.active && Index == IndexK0) { // Exact Rec left sideband, with the same reconstructed // selections, coordinates and weight as the ordinary Rec path. - fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecLeftBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecLeftBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecLeftBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -1564,8 +1594,15 @@ struct HStrangeCorrelation { // PairLoss Rec control study. Stage 0 is filled from this // exact Rec signal branch, so it is not a hand-written // approximation of the reconstructed pair selection. Each next - // stage adds exactly one cumulative condition. - if (pairLossComparison.active && Index == IndexK0) { + // stage adds exactly one cumulative condition. With + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0) + // only the Nch^gen-binned Rec is filled; fake K0 = Rec - RecPeakTrueK0. + if (pairLossComparison.active && Index == IndexK0 && pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/Rec")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (assocCandidate.mcTrue(IndexK0)) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecPeakTrueK0")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } else if (pairLossComparison.active && Index == IndexK0) { fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/Rec")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); // Rec is the complete peak-window sample. Split it into true // and fake K0 components without changing any reconstructed @@ -1725,7 +1762,11 @@ struct HStrangeCorrelation { if (pairLossComparison.active && Index == IndexK0) { // Exact Rec right sideband, with the same reconstructed // selections, coordinates and weight as the ordinary Rec path. - fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecRightBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecRightBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecRightBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2757,73 +2798,122 @@ struct HStrangeCorrelation { } } + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + if (!doprocessPairLossK0MC || !pairLossK0Configurations.doRecComparison) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel acts on the Rec/Gen/Truth/Final stages of part 2; enable processPairLossK0MC and pairLossK0Configurations.doRecComparison"); + } + if (!masterConfigurations.doPPAnalysis) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel is only defined for pp: the Pb-Pb event selection has no INEL cut"); + } + if (!masterConfigurations.applyNewMCSelection) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel requires masterConfigurations.applyNewMCSelection=true so that the Gen stage uses the same event selection function as the Rec stage"); + } + if (masterConfigurations.selectINELgtN) { + LOGF(warning, "pairLossK0Configurations.applyINELSelectionAtGenLevel: masterConfigurations.selectINELgtN is ignored in the PairLossK0/Comparison stages, which select Nch^gen >= 1 only"); + } + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doEventRecoEfficiencyStudy) { + // Per MC collision with |vz_gen| <= zVertexCut, each histogram a subset of + // the previous one: HasRecoColl/All is the MC-collision reconstruction + // efficiency vs Nch^gen. The event selection is the one of the Rec path + // (trigger presence + isCollisionSelected) evaluated on the best collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenAll", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl", "MC collisions with at least one reconstructed collision;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel", "best reconstructed collision passes the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSel", "best reconstructed collision passes the Rec event selection with reconstructed-level INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);reconstructed collisions", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNRecoCollisions}); + // Per reconstructed collision passing the Rec event selection without INEL + // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + } + if (doprocessPairLossK0MC && pairLossK0Configurations.doRecComparison) { if (doprocessSameEventHV0s) { LOGF(fatal, "pairLossK0Configurations.doRecComparison already runs the exact Rec path internally; set processSameEventHV0s=false to avoid double filling"); } - constexpr int PairLossComparisonNVariants = 16; - const AxisSpec axisPairLossComparisonVariant{PairLossComparisonNVariants, -0.5, PairLossComparisonNVariants - 0.5, "cumulative Rec control variant"}; - constexpr int PairLossComparisonNTriggerVariants = 10; - const AxisSpec axisPairLossComparisonTriggerVariant{PairLossComparisonNTriggerVariants, -0.5, PairLossComparisonNTriggerVariants - 0.5, "cumulative Rec trigger control variant"}; - histos.add("PairLossK0/Comparison/Rec", "exact Rec peak-window h-K0 pairs; all candidates", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecPeakTrueK0", "exact Rec peak-window pairs with a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecPeakFakeK0", "exact Rec peak-window pairs without a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecLeftBg", "exact Rec left invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecRightBg", "exact Rec right invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Truth", "PairLoss truth pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Gen", "Closure-test Gen pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/Final", "PairLoss final truth-matched pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecVariants", "cumulative controlled variants of the Rec pair definition", kTHnF, {axisPairLossComparisonVariant, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - // The two directional set differences between Rec variant 14 and the - // exact old Final definition. They make non-nested pair definitions - // explicit instead of hiding the mismatch in a single yield ratio. - histos.add("PairLossK0/Comparison/FinalNotInRec", "old-Final truth pairs absent from Rec variant 14", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - histos.add("PairLossK0/Comparison/RecNotInFinal", "Rec variant-14 truth pairs absent from old Final", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - // Trigger-side counterpart of the pair ladder. Pair variant i has to be - // divided by the trigger variant listed in the label to obtain a - // per-trigger yield that is narrowed by the same cumulative conditions. - histos.add("PairLossK0/Comparison/TriggerVariants", "cumulative controlled variants of the Rec trigger normalisation", kTHnF, {axisPairLossComparisonTriggerVariant, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - - const std::array variantLabels = { - "0 exact Rec", - "1 + truth-trigger match/acceptance", - "2 + true K0 (remove peak background)", - "3 + truth-K0 match/acceptance", - "4 + truth daughter exclusion", - "5 + truth-trigger primary", - "6 + truth-K0 primary", - "7 + old truth-leading choice", - "8 + best collision", - "9 + truth pT coordinates only", - "10 + truth event coordinates (vtxZ, mult)", - "11 + truth delta eta only", - "12 + truth delta phi and truth angular range", - "13 + unit pair weight", - "14 + one entry per truth pair", - "15 exact old Final reference"}; - auto recVariants = histos.get(HIST("PairLossK0/Comparison/RecVariants")); - for (size_t i = 0; i < variantLabels.size(); ++i) { - recVariants->GetAxis(0)->SetBinLabel(i + 1, variantLabels[i].data()); - } - - // Pair variant -> trigger variant pairing for per-trigger yields: - // pair 0-3 with trigger 0-1, pair 4-6 with trigger 2, pair 7 with - // trigger 3, pair 8-9 with trigger 4, pair 10-12 with trigger 6, - // pair 13-14 with trigger 7-8, pair 15 with trigger 9. - const std::array triggerVariantLabels = { - "0 exact Rec trigger", - "1 + truth-trigger match/acceptance", - "2 + truth-trigger primary", - "3 + old truth-leading choice", - "4 + best collision", - "5 + truth pT coordinate only", - "6 + truth event coordinates (vtxZ, mult)", - "7 + unit trigger weight", - "8 + one entry per truth trigger", - "9 exact old Final trigger reference"}; - auto triggerVariants = histos.get(HIST("PairLossK0/Comparison/TriggerVariants")); - for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { - triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + // Only Rec and Gen, with the Nch^gen of the MC collision in place of the + // vertex-z and multiplicity axes (memory), for a closure test per Nch^gen + // class; the rest of the Comparison ladder (Truth, Final, variants, set + // differences) is neither booked nor filled + const AxisSpec axisNchGenClass{pairLossK0Configurations.axisComparisonNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + histos.add("PairLossK0/Comparison/NchGen/Rec", "Comparison/Rec vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecPeakTrueK0", "Comparison/RecPeakTrueK0 vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecLeftBg", "Comparison/RecLeftBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecRightBg", "Comparison/RecRightBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/Gen", "Comparison/Gen vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + // Trigger counts for the per-trigger normalisation of the pairs above, with + // the same trigger and event selections: TriggerRec next to + // sameEvent/TriggerParticlesV0 with the same trigger weight, TriggerGen in + // the Gen trigger loop, including events without a K0 + histos.add("PairLossK0/Comparison/NchGen/TriggerRec", "Rec triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/TriggerGen", "Gen triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + } else { + constexpr int PairLossComparisonNVariants = 16; + const AxisSpec axisPairLossComparisonVariant{PairLossComparisonNVariants, -0.5, PairLossComparisonNVariants - 0.5, "cumulative Rec control variant"}; + constexpr int PairLossComparisonNTriggerVariants = 10; + const AxisSpec axisPairLossComparisonTriggerVariant{PairLossComparisonNTriggerVariants, -0.5, PairLossComparisonNTriggerVariants - 0.5, "cumulative Rec trigger control variant"}; + histos.add("PairLossK0/Comparison/Rec", "exact Rec peak-window h-K0 pairs; all candidates", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakTrueK0", "exact Rec peak-window pairs with a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakFakeK0", "exact Rec peak-window pairs without a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecLeftBg", "exact Rec left invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecRightBg", "exact Rec right invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Truth", "PairLoss truth pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Gen", "Closure-test Gen pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Final", "PairLoss final truth-matched pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecVariants", "cumulative controlled variants of the Rec pair definition", kTHnF, {axisPairLossComparisonVariant, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // The two directional set differences between Rec variant 14 and the + // exact old Final definition. They make non-nested pair definitions + // explicit instead of hiding the mismatch in a single yield ratio. + histos.add("PairLossK0/Comparison/FinalNotInRec", "old-Final truth pairs absent from Rec variant 14", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecNotInFinal", "Rec variant-14 truth pairs absent from old Final", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // Trigger-side counterpart of the pair ladder. Pair variant i has to be + // divided by the trigger variant listed in the label to obtain a + // per-trigger yield that is narrowed by the same cumulative conditions. + histos.add("PairLossK0/Comparison/TriggerVariants", "cumulative controlled variants of the Rec trigger normalisation", kTHnF, {axisPairLossComparisonTriggerVariant, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + + const std::array variantLabels = { + "0 exact Rec", + "1 + truth-trigger match/acceptance", + "2 + true K0 (remove peak background)", + "3 + truth-K0 match/acceptance", + "4 + truth daughter exclusion", + "5 + truth-trigger primary", + "6 + truth-K0 primary", + "7 + old truth-leading choice", + "8 + best collision", + "9 + truth pT coordinates only", + "10 + truth event coordinates (vtxZ, mult)", + "11 + truth delta eta only", + "12 + truth delta phi and truth angular range", + "13 + unit pair weight", + "14 + one entry per truth pair", + "15 exact old Final reference"}; + auto recVariants = histos.get(HIST("PairLossK0/Comparison/RecVariants")); + for (size_t i = 0; i < variantLabels.size(); ++i) { + recVariants->GetAxis(0)->SetBinLabel(i + 1, variantLabels[i].data()); + } + + // Pair variant -> trigger variant pairing for per-trigger yields: + // pair 0-3 with trigger 0-1, pair 4-6 with trigger 2, pair 7 with + // trigger 3, pair 8-9 with trigger 4, pair 10-12 with trigger 6, + // pair 13-14 with trigger 7-8, pair 15 with trigger 9. + const std::array triggerVariantLabels = { + "0 exact Rec trigger", + "1 + truth-trigger match/acceptance", + "2 + truth-trigger primary", + "3 + old truth-leading choice", + "4 + best collision", + "5 + truth pT coordinate only", + "6 + truth event coordinates (vtxZ, mult)", + "7 + unit trigger weight", + "8 + one entry per truth trigger", + "9 exact old Final trigger reference"}; + auto triggerVariants = histos.get(HIST("PairLossK0/Comparison/TriggerVariants")); + for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { + triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); + } } } @@ -3276,10 +3366,10 @@ struct HStrangeCorrelation { return collision.multNTracksPVeta1() > threshold; } - // INEL>N at generated level: more than N charged physical primaries in |eta| < 1, + // Number of charged physical primaries in |eta| < 1 at generated level, // counted the same way as multcenttable fills multMCNParticlesEta10 template - bool isInelGtNGen(TMcParticles const& mcParticles, int threshold) + int countNchGenEta1(TMcParticles const& mcParticles) { constexpr double ChargeTolerance = 1e-3; // |charge| below this counts as neutral int nChEta1 = 0; @@ -3295,12 +3385,40 @@ struct HStrangeCorrelation { ++nChEta1; } } - return nChEta1 > threshold; + return nChEta1; + } + + // INEL>N at generated level: more than N charged physical primaries in |eta| < 1 + template + bool isInelGtNGen(TMcParticles const& mcParticles, int threshold) + { + return countNchGenEta1(mcParticles) > threshold; + } + + // reconstructed INEL>0 / INEL>N cuts of the pp event selection + template + bool isCollisionInelSelected(TCollision const& collision) + { + if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { + return false; + } + if (!isInelGtNReco(collision, masterConfigurations.inelGtNThreshold) && masterConfigurations.selectINELgtN) { + return false; + } + return true; + } + + // generated-level INEL>0 of the PairLoss Comparison stages with + // applyINELSelectionAtGenLevel: Nch^gen (countNchGenEta1) >= 1 + static bool isGenInelGt0(int nChGen) + { + return nChGen > 0; } // this function allows for all event selections to be done in a modular way + // applyInelSelection = false skips only the reconstructed INEL>0 / INEL>N cuts template - bool isCollisionSelected(TCollision const& collision) + bool isCollisionSelected(TCollision const& collision, bool applyInelSelection = true) { // ________________________________________________ // Perform basic event selection @@ -3317,10 +3435,7 @@ struct HStrangeCorrelation { if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { return false; } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { - return false; - } - if (!isInelGtNReco(collision, masterConfigurations.inelGtNThreshold) && masterConfigurations.selectINELgtN) { + if (applyInelSelection && !isCollisionInelSelected(collision)) { return false; } if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { @@ -3490,8 +3605,12 @@ struct HStrangeCorrelation { for (auto const& collision : collisions) { // ________________________________________________ - // Perform basic event selection - if (!isCollisionSelected(collision)) { + // Perform basic event selection. The INEL cuts are left to the consumers of + // triggerPresenceMap, which apply their own event selection afterwards, so that + // a generated-level INEL selection (applyINELSelectionAtGenLevel) is not + // overridden. Only the generated-level closure paths with applyNewMCSelection + // and doGenEventSelection both false have no event selection of their own + if (!isCollisionSelected(collision, false)) { continue; } @@ -3756,19 +3875,25 @@ struct HStrangeCorrelation { } template - void runSameEventHV0s(TCollision const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks) + void runSameEventHV0s(TCollision const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks, + std::optional nChGenForInelSelection = std::nullopt) { const float cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // Keep the trigger-presence and reconstructed-event decisions identical - // for the ordinary Rec process and for its PairLoss replica. + // for the ordinary Rec process and for its PairLoss replica. When + // nChGenForInelSelection is given (PairLoss replica only), the reconstructed + // INEL cuts are replaced by Nch^gen >= 1 of that generated multiplicity if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } - if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision)) || + if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision, !nChGenForInelSelection.has_value())) || (!masterConfigurations.doPPAnalysis && !isCollisionSelectedPbPb(collision, true))) { return; } + if (nChGenForInelSelection.has_value() && !isGenInelGt0(*nChGenForInelSelection)) { + return; + } auto bc = collision.template bc_as(); const auto bField = getMagneticField(bc.timestamp()); @@ -4502,6 +4627,11 @@ struct HStrangeCorrelation { V0DatasWithoutTrackX const& v0Candidates, TracksComplete const& tracks) { + // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the + // event-reconstruction-efficiency study and the generated-level INEL selection + const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel; + const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel) ? countNchGenEta1(mcParticles) : -1; + // Part 3: a self-contained generator-level study. It deliberately touches no // reconstructed quantity in its event selection or in any of its axes: the // event is selected on generated INEL>0 (INEL>N if enabled) and the generated @@ -4717,8 +4847,11 @@ struct HStrangeCorrelation { bestCollisionVtxY = collision.posY(); bestCollisionVtxZ = collision.posZ(); bestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + // with applyGenInel (pp only) the reconstructed INEL cuts are replaced by + // Nch^gen >= 1, as in the Rec and Gen stages pairLossEventSelected = !pairLossK0Configurations.applyRecoEventSelection || - (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision) : isCollisionSelectedPbPb(collision, false)); + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false)); } if (bestCollisionId < 0) { return; @@ -4795,7 +4928,8 @@ struct HStrangeCorrelation { } // Truth: identical pair definition to the original PairLoss truth stage. - if (pairLossEventSelected) { + // Not booked with applyGenInel, like the rest of the ladder except Rec and Gen. + if (pairLossEventSelected && !applyGenInel) { for (auto const& truthTrigger : truthTriggers) { for (auto const& truthK0 : truthK0s) { if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { @@ -4833,12 +4967,13 @@ struct HStrangeCorrelation { } genLargestNContributors = recCollision.numContrib(); genBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); + // Both branches fill the Gen THn with this vertex position + genBestCollisionVtxZ = recCollision.posZ(); if (masterConfigurations.applyNewMCSelection) { - genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision)) || + genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision, !applyGenInel)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false)); } else { genBestCollisionSel8 = recCollision.sel8(); - genBestCollisionVtxZ = recCollision.posZ(); genBestCollisionINELgtZERO = recCollision.isInelGt0(); genBestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); genBestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); @@ -4851,7 +4986,7 @@ struct HStrangeCorrelation { } bool genEventSelected = triggerPresenceMap.size() == 0 || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); if (masterConfigurations.applyNewMCSelection) { - genEventSelected = genEventSelected && genCollisionSelected; + genEventSelected = genEventSelected && genCollisionSelected && (!applyGenInel || isGenInelGt0(nChGen)); } else if (masterConfigurations.doGenEventSelection) { genEventSelected = genEventSelected && genBestCollisionSel8 && std::abs(genBestCollisionVtxZ) <= masterConfigurations.zVertexCut && genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtN || genBestCollisionINELgtN) && @@ -4882,6 +5017,9 @@ struct HStrangeCorrelation { if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { continue; } + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/TriggerGen"), triggerParticle.pt(), nChGen); + } auto const& triggerMother = triggerParticle.mothers_first_as(); const auto triggerMotherIndex = triggerMother.globalIndex(); for (auto const& k0Index : genK0Indices) { @@ -4895,7 +5033,11 @@ struct HStrangeCorrelation { k0Particle.pt() < axisRanges[2][0] || k0Particle.pt() > axisRanges[2][1]) { continue; } - histos.fill(HIST("PairLossK0/Comparison/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), genBestCollisionVtxZ, genBestCollisionMultiplicity); + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), nChGen); + } else { + histos.fill(HIST("PairLossK0/Comparison/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), genBestCollisionVtxZ, genBestCollisionMultiplicity); + } } } } @@ -4990,7 +5132,9 @@ struct HStrangeCorrelation { }; std::vector finalPairRecords; - if (pairLossEventSelected) { + // Not filled with applyGenInel: finalPairRecords then stays empty, so + // FinalNotInRec below is not filled either. + if (pairLossEventSelected && !applyGenInel) { for (auto const& truthTrigger : truthTriggers) { if (oldFinalTriggers.find(truthTrigger.globalIndex) == oldFinalTriggers.end()) { continue; @@ -5093,12 +5237,14 @@ struct HStrangeCorrelation { pairLossComparison.bestCollisionId = bestCollisionId; pairLossComparison.vtxZ = bestCollisionVtxZ; pairLossComparison.multiplicity = bestCollisionMultiplicity; + pairLossComparison.nChGen = applyGenInel ? nChGen : -1; pairLossComparison.active = true; for (auto const& recCollision : recCollisions) { const auto recTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, recCollision.globalIndex()); const auto recV0Slice = associatedV0s.sliceBy(collisionSliceV0s, recCollision.globalIndex()); const auto recTrackSlice = tracks.sliceBy(pairLossTracksPerCollision, recCollision.globalIndex()); - runSameEventHV0s(recCollision, recV0Slice, recTriggerSlice, recTrackSlice); + runSameEventHV0s(recCollision, recV0Slice, recTriggerSlice, recTrackSlice, + applyGenInel ? std::optional{nChGen} : std::nullopt); } // Final-minus-Rec counterpart of RecNotInFinal. Both set differences use @@ -5113,6 +5259,57 @@ struct HStrangeCorrelation { pairLossComparison.clear(); }; + // MC-collision reconstruction efficiency vs Nch^gen and the Nch^gen vs + // multNTracksPVeta1 response. The event selection is the one runSameEventHV0s + // applies before its INEL cut: trigger presence + isCollisionSelected + auto runEventRecoEfficiencyStudy = [&]() { + auto passesRecEventSelectionNoInel = [&](auto const& collision) { + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return false; + } + return masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, false) : isCollisionSelectedPbPb(collision, false); + }; + + // Response: one entry per reconstructed collision, as in the Rec path + for (auto const& collision : recCollisions) { + if (passesRecEventSelectionNoInel(collision)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, collision.multNTracksPVeta1()); + } + } + + // Efficiency: one entry per MC collision, evaluated on the best collision + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenAll"), nChGen); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl"), nChGen, recCollisions.size()); + if (recCollisions.size() == 0) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl"), nChGen); + int largestNContributors = -1; + bool bestCollisionSelectedNoInel = false; + bool bestCollisionSelected = false; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionSelectedNoInel = passesRecEventSelectionNoInel(collision); + // reconstructed-level INEL cuts; the Pb-Pb event selection has none + bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); + } + if (bestCollisionSelectedNoInel) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel"), nChGen); + } + if (bestCollisionSelected) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSel"), nChGen); + } + }; + + if (pairLossK0Configurations.doEventRecoEfficiencyStudy) { + runEventRecoEfficiencyStudy(); + } if (pairLossK0Configurations.doGenLevelStudy) { runGenLevelStudy(); } From 858ef2281253fee1d5832663eac8b81b366c0683 Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Wed, 7 Oct 2026 15:40:14 +0200 Subject: [PATCH 210/648] [PWGCF] Femto 3d: make TPC rejection cut configurable (#18201) --- PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx index 22c2d5d4fe0..9cd26e1f250 100644 --- a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx +++ b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx @@ -85,6 +85,7 @@ struct singleTrackSelector { Configurable> keepWithinNsigmaTPC{"keepWithinNsigmaTPC", std::vector{-4.0f, 4.0f}, "TPC range for preselection of particles specified with PDG"}; Configurable> _particlesToReject{"particlesToRejectPDGs", std::vector{211, 321, 1000020030}, "PDG codes of particles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; Configurable> rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-5.0f, 5.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable> rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", std::vector{-5.0f, 5.0f}, "TPC Nsigma range required together with rejectWithinNsigmaTOF for particles specified with PDG to be rejected"}; Configurable _pRemoveTofOutOfRange{"pRemoveTofOutOfRange", 100.f, "momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; Configurable> _ptRemoveTofOutOfRange{"ptRemoveTofOutOfRange", {100.f, -10.f, 10.f}, "transverse momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; @@ -247,7 +248,7 @@ struct singleTrackSelector { for (auto i : particlesToReject) { // if satisfied, want to continue in the upper loop (over tracks) -- skip the current track // cannot use simple 'continue' since it will be applied to the current loop, so have to use a flag - if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF))) { + if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF), rejectWithinNsigmaTPC.value)) { skip_track = true; break; } From cc25f8a542244d77d45ce8418eb7b020a52820f2 Mon Sep 17 00:00:00 2001 From: Chiara Date: Wed, 7 Oct 2026 18:19:41 +0200 Subject: [PATCH 211/648] [Common] Updated chain to analyze ZDC in light ion systems (#18131) --- Common/DataModel/ZDCLightIons.h | 6 +- Common/TableProducer/zdcTaskLightIons.cxx | 67 ++- Common/Tasks/zdcTableReader.cxx | 607 ++++++++++++++++++---- 3 files changed, 588 insertions(+), 92 deletions(-) diff --git a/Common/DataModel/ZDCLightIons.h b/Common/DataModel/ZDCLightIons.h index b0e8e247622..a3d6db2b207 100644 --- a/Common/DataModel/ZDCLightIons.h +++ b/Common/DataModel/ZDCLightIons.h @@ -56,7 +56,8 @@ DECLARE_SOA_COLUMN(CentralityFt0c, centralityFt0c, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0a, centralityFt0a, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0m, centralityFt0m, float); //! Centrality DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp -DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection Flags +DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection flags x collisions +DECLARE_SOA_COLUMN(BcMask, bcMask, uint8_t); //! Selection flags x BC } // namespace zdclightions DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", @@ -92,7 +93,8 @@ DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", zdclightions::CentralityFt0a, zdclightions::CentralityFt0m, zdclightions::Timestamp, - zdclightions::SelectionBits); + zdclightions::SelectionBits, + zdclightions::BcMask); } // namespace o2::aod #endif // COMMON_DATAMODEL_ZDCLIGHTIONS_H_ diff --git a/Common/TableProducer/zdcTaskLightIons.cxx b/Common/TableProducer/zdcTaskLightIons.cxx index d778f5087a6..1cc5c7826d4 100644 --- a/Common/TableProducer/zdcTaskLightIons.cxx +++ b/Common/TableProducer/zdcTaskLightIons.cxx @@ -18,6 +18,9 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/ZDCLightIons.h" +#include +#include +#include #include #include #include @@ -31,7 +34,10 @@ #include #include +#include #include +#include +#include using namespace o2; using namespace o2::aod; @@ -45,6 +51,14 @@ using ColEvSels = soa::Join zdcTableLI; + Service ccdb; + static const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; + std::bitset beamPatternA; + std::bitset beamPatternC; + std::bitset bcPatternB; + std::bitset bcPatternA; + std::bitset bcPatternC; + std::bitset bcPatternE; // Configurable parameters Configurable nBinsTiming{"nBinsTiming", 200, "n bins for debunching histo"}; @@ -78,6 +92,14 @@ struct ZdcTaskLightIons { nEventSelections }; + enum bcMaskBits { + bcMask_beamB, + bcMask_beamA, + bcMask_beamC, + bcMask_beamE, + nBits + }; + void init(InitContext const&) { registry.add("zdcDebunchHist", "ZN sum vs. diff; ZNA-ZNC (ns); ZNA+ZNC (ns)", {HistType::kTH2D, {{nBinsTiming, -20., 20.}, {nBinsTiming, -20., 20.}}}); @@ -158,9 +180,46 @@ struct ZdcTaskLightIons { void processZDCBC(BCsRun3 const& bcs, aod::Zdcs const& /*zdcs*/) { + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t timeStamp = bcs.iteratorAt(0).timestamp(); + std::map metadata; + // use getSpecific to set metadata to avoid crashes related to specific run number + auto grplhcif = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + // auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", timeStamp); + if (grplhcif == nullptr) { + LOG(fatal) << "GRPLHCIFData not in database, timestamp:" << timeStamp; + } + + beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = grplhcif->getBunchFilling().getBCPattern(); + bcPatternA = beamPatternA & ~beamPatternC; + bcPatternC = ~beamPatternA & beamPatternC; + bcPatternE = ~beamPatternA & ~beamPatternC; + for (const auto& bc : bcs) { + if (bc.has_zdc()) { + // int64_t timestamp = bc.timestamp(); + auto timestampFromSOR = (bc.timestamp() - grplhcif->getFillNumberTime()) / 1e3; // Convert to seconds + + int bcInOrbit = bc.globalBC() % nBCsPerOrbit; + + uint8_t maskSel = 0; + if (bcPatternB[bcInOrbit]) { + maskSel |= (uint8_t)(0x1u << bcMask_beamB); + } + if (bcPatternA[bcInOrbit]) { + maskSel |= (uint8_t)(0x1u << bcMask_beamA); + } + if (bcPatternC[bcInOrbit]) { + maskSel |= (uint8_t)(0x1u << bcMask_beamC); + } + if (bcPatternE[bcInOrbit]) { + maskSel |= (uint8_t)(0x1u << bcMask_beamE); + } + auto tdcZNA = bc.zdc().timeZNA(); auto tdcZNC = bc.zdc().timeZNC(); auto tdcZPA = bc.zdc().timeZPA(); @@ -225,8 +284,9 @@ struct ZdcTaskLightIons { -1, -1, -1, -1., -1, -1, -1, - bc.timestamp(), - -1); + timestampFromSOR, + 0, + maskSel); } } } @@ -333,7 +393,8 @@ struct ZdcTaskLightIons { zv, centralityFT0C, centralityFT0A, centralityFT0M, foundBC.timestamp(), - evSelection); + evSelection, + 0); } } } diff --git a/Common/Tasks/zdcTableReader.cxx b/Common/Tasks/zdcTableReader.cxx index 07a581564cd..6efececddab 100644 --- a/Common/Tasks/zdcTableReader.cxx +++ b/Common/Tasks/zdcTableReader.cxx @@ -27,21 +27,35 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod; +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) + struct ZDCLIAnalysis { - // Configurable number of bins - Configurable useZvtx{"useZvtx", false, "If true uses Z_vertex"}; - Configurable zVval{"zVval", 10., "Z_vertex cut value"}; - Configurable tStampMin{"tStampMin", 100000., "minimum value for timestamp"}; - Configurable tStampMax{"tStampMax", 100000., ",maximum value for timestamp"}; + // Configurable + Configurable selectBC{"selectBC", 0, "Select BC events"}; + Configurable selectOnlyB{"selectOnlyB", 0, "Select BC with A && C"}; + Configurable selectColl{"selectColl", 0, "Select ALICE collision events"}; + // + Configurable tStampOffset{"tStampOffset", 0, "offset value for timestamp"}; + Configurable nBinstStamp{"nBinstStamp", 1000, "no. bins in histo vs. timestamp"}; + Configurable tStampMax{"tStampMax", 1000, ", maximum value for timestamp"}; + // + Configurable tdcCut{"tdcCut", false, "Flag for TDC cut"}; + Configurable tdcZNmincut{"tdcZNmincut", -1.5, "Min. ZN TDC cut value"}; + Configurable tdcZNmaxcut{"tdcZNmaxcut", 1.5, "Max. ZN TDC cut value"}; + Configurable tdcZPmincut{"tdcZPmincut", -1.5, "Min. ZP TDC cut value"}; + Configurable tdcZPmaxcut{"tdcZPmaxcut", 1.5, "Max. ZP TDC cut value"}; // Configurable nBinsADC{"nBinsADC", 1000, "n bins 4 ZDC ADCs"}; - Configurable nBinsAmp{"nBinsAmp", 1025, "n bins 4 ZDC amplitudes"}; + Configurable nBinsAmpZN{"nBinsAmpZN", 1025, "n bins 4 ZN amplitudes"}; + Configurable nBinsAmpZP{"nBinsAmpZP", 1025, "n bins 4 ZP amplitudes"}; Configurable nBinsTDC{"nBinsTDC", 480, "n bins 4 TDCs"}; Configurable nBinsFit{"nBinsFit", 1000, "n bins 4 FIT"}; Configurable MaxZN{"MaxZN", 4099.5, "Max 4 ZN histos"}; @@ -51,54 +65,110 @@ struct ZDCLIAnalysis { Configurable MaxMultFV0{"MaxMultFV0", 3000, "Max 4 FV0 histos"}; Configurable MaxMultFT0{"MaxMultFT0", 3000, "Max 4 FT0 histos"}; // + Configurable enCalibZNA{"enCalibZNA", 1.0, "Energy calibration ZNA"}; + Configurable enCalibZNC{"enCalibZNC", 1.0, "Energy calibration ZNC"}; + Configurable enCalibZPA{"enCalibZPA", 1.0, "Energy calibration ZPA"}; + Configurable enCalibZPC{"enCalibZPC", 1.0, "Energy calibration ZPC"}; + // + Configurable applyZDCcut{"applyZDCcut", false, "Apply ZDC cut for light ion analysis"}; + Configurable zdcaCutLow{"zdcaCutLow", 12., "ZDCA cut for light ion analysis"}; + Configurable zdccCutLow{"zdccCutLow", 12., "ZDCC cut for light ion analysis"}; + // + Configurable selectZvtx{"selectZvtx", true, "Activate Z vertex selection"}; + Configurable sel8{"sel8", true, "Activate sel8 selection"}; + Configurable triggerTVX{"triggerTVX", true, "Activate trigger TVX selection"}; + Configurable doOccupancySel{"doOccupancySel", false, "Activate occupancy selection"}; + Configurable noSameBunchPileupCut{"noSameBunchPileupCut", true, "Activate no same bunch pileup selection"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "Activate is good Z vertex FT0 vs PV selection"}; + Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", true, "Activate no collision in time range standard selection"}; + Configurable noTimeFrameBorder{"noTimeFrameBorder", true, "Activate no time frame border selection"}; + Configurable noITSROFFrameBorder{"noITSROFFrameBorder", true, "Activate no ITS ROF frame border selection"}; + Configurable isGoodITSLayersAll{"isGoodITSLayersAll", false, "Activate is good ITS layers all selection"}; + // HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) { - registry.add("hZNApmc", "ZNApmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPApmc", "ZPApmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZNCpmc", "ZNCpmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPCpmc", "ZPCpmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZEM}}}); - registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZEM}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hBCmask", "mask; BC; counts", {HistType::kTH1F, {{5, 0., 5.}}}); + registry.add("hcounts", "counts; selections; counts", {HistType::kTH1F, {{14, 0., 14.}}}); + registry.add("hzvertex", "z vertex; z_vertex (cm); Entries", {HistType::kTH1F, {{200, -20., 20.}}}); + // + registry.add("hZNApmc", "ZNA pmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPApmc", "ZPA pmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZNCpmc", "ZNC pmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPCpmc", "ZPC pmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZEM}}}); + registry.add("hZDCA", "ZNA+ZPA; ZNA+ZPA; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCC", "ZNC+ZPC; ZNC+ZPC; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZDCApmcwZDCCcut", "ZDCA w. ZDCC cut; ZDCA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCCpmcwZDCAcut", "ZDCC w. ZDCA cut; ZDCC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZNAtdc", "ZNA tdc; ZNA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPAtdc", "ZPA tdc; ZPA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZNCtdc", "ZNC tdc; ZNC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPCtdc", "ZPC tdc; ZPC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + // + registry.add("hZDCCvsA", "ZDC side C vs. side A; ZDCA; ZDCC", {HistType::kTH2F, {{{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}, {nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}}); + // + registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNCsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); - registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZPvsZEM", "ZP vs ZEM; ZEM; ZPA+ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, 2. * MaxZP}}}}); + registry.add("hZNAvsZEM", "ZNA vs ZEM; ZEM; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZEM", "ZNC vs ZEM; ZEM; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZEM", "ZPA vs ZEM; ZEM; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvsZEM", "ZPC vs ZEM; ZEM; ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCvstdc", "ZNC vs tdc; ZNC amplitude; ZNC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvstdc", "ZNA vs tdc; ZNA amplitude; ZNA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvstdc", "ZPC vs tdc; ZPC amplitude; ZPC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvstdc", "ZPA vs tdc; ZPA amplitude; ZPA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); // - registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNC sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAC ADC; ZNA sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); // - registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNCvstdc", "ZNC vs tdc; ZNC TDC (ns); ZNC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvstdc", "ZNA vs tdc; ZNA TDC (ns); ZNA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvstdc", "ZPC vs tdc; ZPC TDC (ns); ZPC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvstdc", "ZPA vs tdc; ZPA TDC (ns); ZPA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + // + registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + // + registry.add("hNnZNAvscentrFT0C", "N_{neutrons} in ZNA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0C", "N_{neutrons} in ZNC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0C", "N_{protons} in ZPA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0C", "N_{protons} in ZPC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNAvscentrFT0M", "N_{neutrons} in ZNA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0M", "N_{neutrons} in ZNC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0M", "N_{protons} in ZPA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0M", "N_{protons} in ZPC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + // + registry.add("hNpvsNnZNA", "N_{protons} vs N_{neutrons} in ZNA", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + registry.add("hNpvsNnZNC", "N_{protons} vs N_{neutrons} in ZNC", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + // + registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); } void process(aod::ZDCLightIons const& zdclightions) @@ -108,6 +178,8 @@ struct ZDCLIAnalysis { auto tdcznc = zdc.zncTdc(); auto tdczpa = zdc.zpaTdc(); auto tdczpc = zdc.zpcTdc(); + auto tdczem1 = zdc.zem1Tdc(); + auto tdczem2 = zdc.zem2Tdc(); auto zna = zdc.znaAmpl(); auto znaADC = zdc.znaPmc(); auto znapm1 = zdc.znaPm1(); @@ -134,55 +206,416 @@ struct ZDCLIAnalysis { auto centrFT0A = zdc.centralityFt0a(); auto centrFT0M = zdc.centralityFt0m(); auto timestamp = zdc.timestamp(); - // auto selectionBits = zdc.selectionBits(); - - if ((useZvtx && (zvtx < zVval)) || !useZvtx) { - registry.get(HIST("hZNApmc"))->Fill(zna); - registry.get(HIST("hZNCpmc"))->Fill(znc); - registry.get(HIST("hZPApmc"))->Fill(zpa); - registry.get(HIST("hZPCpmc"))->Fill(zpc); - registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + auto selectionBits = zdc.selectionBits(); + auto bcMask = zdc.bcMask(); + + bool isZNAtdc = false; + bool isZNCtdc = false; + bool isZPAtdc = false; + bool isZPCtdc = false; + bool isZEMtdc = false; + + if (tdczna > -99999.0) { + isZNAtdc = true; + } + if (tdcznc > -99999.0) { + isZNCtdc = true; + } + if (tdczpa > -99999.0) { + isZPAtdc = true; + } + if (tdczpc > -99999.0) { + isZPCtdc = true; + } + if (tdczem1 > -99999.0 || tdczem2 > -99999.0) { + isZEMtdc = true; + } + + if (tdcCut) { // TDC cuts applied + if ((tdczna < tdcZNmincut) || (tdczna > tdcZNmaxcut)) { + isZNAtdc = false; + } + if ((tdcznc < tdcZNmincut) || (tdcznc > tdcZNmaxcut)) { + isZNCtdc = false; + } + if ((tdczpa < tdcZPmincut) || (tdczpa > tdcZPmaxcut)) { + isZPAtdc = false; + } + if ((tdczpc < tdcZPmincut) || (tdczpc > tdcZPmaxcut)) { + isZPCtdc = false; + } + } + + bool eventSelected = false; + + // for BC events ------- + if (selectBC) { + // printf("BCmask: %x \n", bcMask); + + registry.get(HIST("hBCmask"))->Fill(0., 1.); + auto isB = CHECK_BIT(bcMask, 0); + if (isB) { + registry.get(HIST("hBCmask"))->Fill(1., 1.); + } + if (CHECK_BIT(bcMask, 1)) { + registry.get(HIST("hBCmask"))->Fill(2., 1.); + } + if (CHECK_BIT(bcMask, 2)) { + registry.get(HIST("hBCmask"))->Fill(3., 1.); + } + if (CHECK_BIT(bcMask, 3)) { + registry.get(HIST("hBCmask"))->Fill(4., 1.); + } + // + if (selectOnlyB && isB) { + eventSelected = true; + } else if (!selectOnlyB) { + eventSelected = true; + } + } + + // for collision events ------- + if (selectColl) { + bool zvtxSel = false; + if (selectZvtx && CHECK_BIT(selectionBits, 0)) { + zvtxSel = true; + } else if (!selectZvtx) { + zvtxSel = true; + } + // + bool ottoSel = false; + if (sel8 && CHECK_BIT(selectionBits, 1)) { + ottoSel = true; + } else if (!sel8) { + ottoSel = true; + } + // + bool isdoOccupancySel = false; + if (doOccupancySel && CHECK_BIT(selectionBits, 2)) + isdoOccupancySel = true; + else if (!doOccupancySel) + isdoOccupancySel = true; + // + bool isnoSameBunchPileupCut = false; + if (noSameBunchPileupCut && CHECK_BIT(selectionBits, 3)) + isnoSameBunchPileupCut = true; + else if (!noSameBunchPileupCut) + isnoSameBunchPileupCut = true; + // + bool isGoodZvtxFT0vsPVsel = false; + if (isGoodZvtxFT0vsPV && CHECK_BIT(selectionBits, 4)) + isGoodZvtxFT0vsPVsel = true; + else if (!isGoodZvtxFT0vsPV) + isGoodZvtxFT0vsPVsel = true; // - registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); - registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); - registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); - registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + bool isnoCollInTimeRangeStandard = false; + if (noCollInTimeRangeStandard && CHECK_BIT(selectionBits, 5)) + isnoCollInTimeRangeStandard = true; + else if (!noCollInTimeRangeStandard) + isnoCollInTimeRangeStandard = true; // - registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); - registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); - registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); - registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); - registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + bool isnoTimeFrameBorder = false; + if (noTimeFrameBorder && CHECK_BIT(selectionBits, 6)) + isnoTimeFrameBorder = true; + else if (!noTimeFrameBorder) + isnoTimeFrameBorder = true; // - registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); - registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); - registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); - registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + bool isnoITSROFFrameBorder = false; + if (noITSROFFrameBorder && CHECK_BIT(selectionBits, 7)) + isnoITSROFFrameBorder = true; + else if (!noITSROFFrameBorder) + isnoITSROFFrameBorder = true; // - registry.get(HIST("hZNAcvsZNAsum"))->Fill(znapm1 + znapm2 + znapm3 + znapm4, zna); - registry.get(HIST("hZNCcvsZNCsum"))->Fill(zncpm1 + zncpm2 + zncpm3 + zncpm4, znc); + bool isGoodITSLayersAllsel = false; + if (isGoodITSLayersAll && CHECK_BIT(selectionBits, 8)) + isGoodITSLayersAllsel = true; + else if (!isGoodITSLayersAll) + isGoodITSLayersAllsel = true; + // + bool istriggerTVX = false; + if (triggerTVX && CHECK_BIT(selectionBits, 9)) + istriggerTVX = true; + else if (!triggerTVX) + istriggerTVX = true; + + if (zvtxSel && ottoSel && istriggerTVX && isdoOccupancySel && isnoSameBunchPileupCut && isGoodZvtxFT0vsPVsel && isnoCollInTimeRangeStandard && isnoTimeFrameBorder && isnoITSROFFrameBorder && isGoodITSLayersAllsel) + eventSelected = true; + // if (zvtxSel && ottoSel && isnoSameBunchPileupCut) eventSelected = true; + + if (eventSelected) { + registry.get(HIST("hcounts"))->Fill(0., 1.); + if (isZNAtdc) + registry.get(HIST("hcounts"))->Fill(1., 1.); + if (isZPAtdc) + registry.get(HIST("hcounts"))->Fill(2., 1.); + if (isZNCtdc) + registry.get(HIST("hcounts"))->Fill(3., 1.); + if (isZPCtdc) + registry.get(HIST("hcounts"))->Fill(4., 1.); + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hcounts"))->Fill(5., 1.); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hcounts"))->Fill(6., 1.); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(7., 1.); + if (isZNAtdc || isZPAtdc || isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(8., 1.); + if (isZNAtdc && isZNCtdc) + registry.get(HIST("hcounts"))->Fill(9., 1.); + if (isZNAtdc && isZPAtdc) + registry.get(HIST("hcounts"))->Fill(10., 1.); + if (isZNCtdc && isZPCtdc) + registry.get(HIST("hcounts"))->Fill(11., 1.); + if (isZEMtdc) + registry.get(HIST("hcounts"))->Fill(12., 1.); + } + } + + if (eventSelected) { + + if (enCalibZNA > 0.) { + zna *= enCalibZNA; + znaADC *= enCalibZNA; + znapm1 *= enCalibZNA; + znapm2 *= enCalibZNA; + znapm3 *= enCalibZNA; + znapm4 *= enCalibZNA; + } + if (enCalibZNC > 0.) { + znc *= enCalibZNC; + zncADC *= enCalibZNC; + zncpm1 *= enCalibZNC; + zncpm2 *= enCalibZNC; + zncpm3 *= enCalibZNC; + zncpm4 *= enCalibZNC; + } + if (enCalibZPA > 0.) { + zpa *= enCalibZPA; + zpaADC *= enCalibZPA; + } + if (enCalibZPC > 0.) { + zpc *= enCalibZPC; + zpcADC *= enCalibZPC; + } + + registry.get(HIST("hzvertex"))->Fill(zvtx); + + if (!applyZDCcut) { + + if (isZNAtdc) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + // + if (isZNAtdc) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); + if (isZNAtdc) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } else { + bool isZNChigh = false; + if (znc >= zdccCutLow) + isZNChigh = true; + bool isZNAhigh = false; + if (zna >= zdcaCutLow) + isZNAhigh = true; + bool isZPAhigh = false; + if (zpa >= zdcaCutLow) + isZPAhigh = true; + bool isZPChigh = false; + if (zpc >= zdccCutLow) + isZPChigh = true; + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if ((isZNAtdc || isZPAtdc) && (isZNChigh && isZPChigh)) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if ((isZNCtdc || isZPCtdc) && (isZNAhigh && isZPAhigh)) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } + + // Timestamp + /*if (tStampOffset > timestamp) { + printf("\n\n ################# OFFSET timestamp too large!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp %lu \n\n", timestamp); + return; + }*/ + // float tsh = (timestamp / 1000.) - (tStampOffset / 1000.); // in hours + /*if (timestamp > tStampMax) { + printf("\n\n MAXIMUM timestamp too small!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp-offset %f \n\n", timestamp); + return; + }*/ + + if (!applyZDCcut) { + if (isZNAtdc) + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } else { + if (isZNAtdc && znc >= zdccCutLow) { + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + } + if (isZPAtdc && zpc >= zdccCutLow) { + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + } + if ((isZNAtdc || isZPAtdc) && (znc >= zdccCutLow && zpc >= zdccCutLow)) { + registry.get(HIST("hZDCApmcwZDCCcut"))->Fill(zna + zpa); + } + if (isZNCtdc && zna >= zdcaCutLow) { + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + } + if (isZPCtdc && zpa >= zdcaCutLow) { + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } + if ((isZNCtdc || isZPCtdc) && (zna >= zdcaCutLow && zpa >= zdcaCutLow)) { + registry.get(HIST("hZDCCpmcwZDCAcut"))->Fill(znc + zpc); + } + } + // TDCs + if (isZNAtdc) + registry.get(HIST("hZNAtdc"))->Fill(tdczna); + if (isZNCtdc) + registry.get(HIST("hZNCtdc"))->Fill(tdcznc); + if (isZPAtdc) + registry.get(HIST("hZPAtdc"))->Fill(tdczpa); + if (isZPCtdc) + registry.get(HIST("hZPCtdc"))->Fill(tdczpc); // - registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); - registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); - registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + if (isZEMtdc) { + registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + registry.get(HIST("hZNAvsZEM"))->Fill(zem1 + zem2, zna); + registry.get(HIST("hZNCvsZEM"))->Fill(zem1 + zem2, znc); + registry.get(HIST("hZPAvsZEM"))->Fill(zem1 + zem2, zpa); + registry.get(HIST("hZPCvsZEM"))->Fill(zem1 + zem2, zpc); + } + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPvsZEM"))->Fill(zem1 + zem2, zpa + zpc); // - registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); - registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); - registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); - registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); - registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); - registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); - registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); - registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); - registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); - registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); - registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); - registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + if (isZNAtdc || isZNCtdc || isZPAtdc || isZPCtdc) + registry.get(HIST("hZDCCvsA"))->Fill(zna + zpa, znc + zpc); // - registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); - registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); - registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); - registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + if (centrFT0C > -1. && centrFT0C < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); + registry.get(HIST("hNnZNAvscentrFT0C"))->Fill(centrFT0C, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0C"))->Fill(centrFT0C, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0C"))->Fill(centrFT0C, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0C"))->Fill(centrFT0C, zpc / 2.680); + } + if (centrFT0A > -1. && centrFT0A < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); + } + if (centrFT0M > -1. && centrFT0M < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + registry.get(HIST("hNnZNAvscentrFT0M"))->Fill(centrFT0M, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0M"))->Fill(centrFT0M, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0M"))->Fill(centrFT0M, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0M"))->Fill(centrFT0M, zpc / 2.680); + registry.get(HIST("hNpvsNnZNA"))->Fill(zna / 2.680, zpa / 2.680); + registry.get(HIST("hNpvsNnZNC"))->Fill(znc / 2.680, zpc / 2.680); + } } } } From aba4a6d0e4c9191f7295faedbb33ef05cbc76848 Mon Sep 17 00:00:00 2001 From: nzardosh Date: Wed, 7 Oct 2026 18:34:26 +0200 Subject: [PATCH 212/648] [Common,PWGJE] Adding embedding for jets (#18088) --- Common/DataModel/Multiplicity.h | 2 + PWGJE/Core/JetDerivedDataUtilities.h | 4 +- PWGJE/Core/JetFindingUtilities.h | 12 +- PWGJE/DataModel/Jet.h | 2 + PWGJE/JetFinders/jetFinder.h | 7 +- PWGJE/JetFinders/jetFinderHF.h | 5 +- PWGJE/JetFinders/jetFinderHFHFBar.h | 3 +- PWGJE/JetFinders/jetFinderV0.h | 3 +- PWGJE/TableProducer/CMakeLists.txt | 5 + PWGJE/TableProducer/Matching/CMakeLists.txt | 5 + .../Matching/jetMatchingMCDSubCharged.cxx | 45 + PWGJE/TableProducer/derivedDataProducer.cxx | 2 +- .../derivedDataProducerEmbedding.cxx | 1252 +++++++++++++++++ .../eventwiseConstituentSubtractor.cxx | 2 +- PWGJE/TableProducer/rhoEstimator.cxx | 18 +- PWGJE/Tasks/CMakeLists.txt | 4 + PWGJE/Tasks/embeddingQA.cxx | 186 +++ PWGJE/Tasks/jetSubstructure.cxx | 3 +- PWGJE/Tasks/jetSubstructureHF.h | 3 +- 19 files changed, 1535 insertions(+), 28 deletions(-) create mode 100644 PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx create mode 100644 PWGJE/TableProducer/derivedDataProducerEmbedding.cxx create mode 100644 PWGJE/Tasks/embeddingQA.cxx diff --git a/Common/DataModel/Multiplicity.h b/Common/DataModel/Multiplicity.h index d9b15e4f85e..df6dee0520e 100644 --- a/Common/DataModel/Multiplicity.h +++ b/Common/DataModel/Multiplicity.h @@ -217,6 +217,8 @@ DECLARE_SOA_TABLE_VERSIONED(MultMCExtras_001, "AOD", "MULTMCEXTRA", 1, //! Table using MultMCExtras = MultMCExtras_001; using MultMCExtra = MultMCExtras::iterator; using MultsExtraMC = MultMCExtras; // for backwards compatibility with previous naming scheme +template +using MultsExtraMCFrom = MultMCExtras_001From; // crosslinks namespace mult diff --git a/PWGJE/Core/JetDerivedDataUtilities.h b/PWGJE/Core/JetDerivedDataUtilities.h index 035293a9b7d..45f0a55d214 100644 --- a/PWGJE/Core/JetDerivedDataUtilities.h +++ b/PWGJE/Core/JetDerivedDataUtilities.h @@ -653,12 +653,12 @@ bool applyTrackKinematics(T const& track, float pTMin = 0.15, float pTMax = 100. } template -bool selectTrack(T const& track, int trackSelection, bool isEmbedded = false) +bool selectTrack(T const& track, int trackSelection, bool rejectNonEmbedded = false) { if (!(track.trackSel() & (1ULL << JTrackSel::notBadMcTrack))) { return false; } - if (isEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks + if (rejectNonEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks return false; } if (trackSelection == -1) { diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index 9d03d9e9054..bf5698e7d22 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -91,10 +91,10 @@ constexpr bool isEMCALClusterTable() */ template -bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nullptr) +bool isTrackSelected(T const& track, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { return false; } if (candidate != nullptr) { @@ -115,10 +115,10 @@ bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nu */ template -void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, const U* candidate = nullptr) +void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { for (auto& track : tracks) { - if (isTrackSelected(track, trackSelection, candidate)) { + if (isTrackSelected(track, trackSelection, rejectNonEmbedded, candidate)) { fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); } } @@ -134,11 +134,11 @@ void analyseTracks(std::vector& inputParticles, T const& tra */ template -void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, U const& candidates) +void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded, U const& candidates) { for (auto& track : tracks) { bool isSelected = true; - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { continue; } for (auto& candidate : candidates) { diff --git a/PWGJE/DataModel/Jet.h b/PWGJE/DataModel/Jet.h index f4afdc17d1f..18780a9c979 100644 --- a/PWGJE/DataModel/Jet.h +++ b/PWGJE/DataModel/Jet.h @@ -156,6 +156,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! absolute p DECLARE_JET_TABLES(JCollision, _jet_type_##EventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "EWSJET") \ DECLARE_JETMATCHING_TABLE(_jet_type_, _jet_type_##EventWiseSubtracted, _shortname_ "JET2EWS") \ DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_, _shortname_ "EWSJET2") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##EventWiseSubtracted, _shortname_ "DJET2EWS") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_##MCDetectorLevel, _shortname_ "EWSJET2D") \ DECLARE_JET_TABLES(JCollision, _jet_type_##MCDetectorLevelEventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "DEWSJET") \ DECLARE_MCEVENTWEIGHT_TABLE(_jet_type_##MCDetectorLevelEventWiseSubtracted, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJETEWSW") \ DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJET2DEWS") \ diff --git a/PWGJE/JetFinders/jetFinder.h b/PWGJE/JetFinders/jetFinder.h index 493251442b6..e23d910b9eb 100644 --- a/PWGJE/JetFinders/jetFinder.h +++ b/PWGJE/JetFinders/jetFinder.h @@ -64,6 +64,7 @@ struct JetFinderTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -207,7 +208,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -220,7 +221,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetEWSPtMin, jetEWSPtMax, jetRadius, jetAreaFractionMin, collision, jetsEvtWiseSubTable, constituentsEvtWiseSubTable, fillTHnSparse ? registry.get(HIST("hJetEWS")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -252,7 +253,7 @@ struct JetFinderTask { for (auto const& clusterDefinition : clusterDefinitionsVec) { for (auto const& hadronicCorrectionType : hadronicCorrectionTypesVec) { inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::analyseClusters(inputParticles, clusters, clusterDefinition, hadronicCorrectionType); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } diff --git a/PWGJE/JetFinders/jetFinderHF.h b/PWGJE/JetFinders/jetFinderHF.h index ebe5629f1b5..d09db8e1f47 100644 --- a/PWGJE/JetFinders/jetFinderHF.h +++ b/PWGJE/JetFinders/jetFinderHF.h @@ -65,6 +65,7 @@ struct JetFinderHFTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -239,9 +240,9 @@ struct JetFinderHFTask { } } if constexpr (isEvtWiseSub) { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding); } else { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); } jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderHFHFBar.h b/PWGJE/JetFinders/jetFinderHFHFBar.h index 8bc9506ec55..e2b90a0f540 100644 --- a/PWGJE/JetFinders/jetFinderHFHFBar.h +++ b/PWGJE/JetFinders/jetFinderHFHFBar.h @@ -65,6 +65,7 @@ struct JetFinderHFHFBarTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -244,7 +245,7 @@ struct JetFinderHFHFBarTask { return; } } - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderV0.h b/PWGJE/JetFinders/jetFinderV0.h index 8d3b9b7f660..6ed805f7b14 100644 --- a/PWGJE/JetFinders/jetFinderV0.h +++ b/PWGJE/JetFinders/jetFinderV0.h @@ -60,6 +60,7 @@ struct JetFinderV0Task { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -208,7 +209,7 @@ struct JetFinderV0Task { } } */ - jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, candidates); + jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, isEmbedding, candidates); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, saveJetsWithCandidatesOnly); } diff --git a/PWGJE/TableProducer/CMakeLists.txt b/PWGJE/TableProducer/CMakeLists.txt index 06c12902c91..dfb9537a478 100644 --- a/PWGJE/TableProducer/CMakeLists.txt +++ b/PWGJE/TableProducer/CMakeLists.txt @@ -18,6 +18,11 @@ o2physics_add_dpl_workflow(jet-deriveddata-producer PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(jet-deriveddata-embedding-producer + SOURCES derivedDataProducerEmbedding.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer SOURCES derivedDataTriggerProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore diff --git a/PWGJE/TableProducer/Matching/CMakeLists.txt b/PWGJE/TableProducer/Matching/CMakeLists.txt index a5450dfb05e..c5480ffbddc 100644 --- a/PWGJE/TableProducer/Matching/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/CMakeLists.txt @@ -179,4 +179,9 @@ o2physics_add_dpl_workflow(jet-matching-sub-dielectron-ch PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(jet-matching-mcd-sub-ch + SOURCES jetMatchingMCDSubCharged.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + endif() diff --git a/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx new file mode 100644 index 00000000000..3a38398b97d --- /dev/null +++ b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx @@ -0,0 +1,45 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet matching mcd subtracted charged task +// +/// \author Nima Zardoshti + +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" +#include "PWGJE/TableProducer/Matching/jetMatchingSub.h" + +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using ChargedJetMatchingSub = JetMatchingSub, + soa::Join, + aod::ChargedMCDetectorLevelJetsMatchedToChargedEventWiseSubtractedJets, + aod::ChargedEventWiseSubtractedJetsMatchedToChargedMCDetectorLevelJets, + aod::JTrackSubs, + aod::JDummys>; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + std::vector tasks; + tasks.emplace_back(adaptAnalysisTask(cfgc, SetDefaultProcesses{}, TaskName{"jet-matching-mcd-sub-ch"})); + return WorkflowSpec{tasks}; +} diff --git a/PWGJE/TableProducer/derivedDataProducer.cxx b/PWGJE/TableProducer/derivedDataProducer.cxx index 80bb8fecb6a..f4cac2e7ac4 100644 --- a/PWGJE/TableProducer/derivedDataProducer.cxx +++ b/PWGJE/TableProducer/derivedDataProducer.cxx @@ -298,7 +298,7 @@ struct JetDerivedDataProducerTask { } PROCESS_SWITCH(JetDerivedDataProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); - void processCollisions(soa::Join::iterator const& collision, soa::Join const&, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) { auto bc = collision.bc_as>(); if (config.includeHadronicRate) { diff --git a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx new file mode 100644 index 00000000000..a4f88e03809 --- /dev/null +++ b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx @@ -0,0 +1,1252 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// task to produce a self contained data format for jet analyses from the full AO2D +// +/// \author Nima Zardoshti + +#include "PWGJE/Core/JetDQUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetV0Utilities.h" +#include "PWGJE/DataModel/EMCALClusters.h" +#include "PWGJE/DataModel/EMCALMatchedCollisions.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetReducedDataDQ.h" +#include "PWGJE/DataModel/JetReducedDataHF.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +// +#include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "PWGHF/DataModel/DerivedTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGUD/Core/SGCutParHolder.h" +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" + +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +// O2ORIGIN("EMB"); +// template +// using McCollisionsFrom = McCollisions_001From; +// template +// using CollisionsFrom = Collisions_001From; +// template +// using TracksExtraFrom = TracksExtra_002From; +// template +// using McParticlesFrom = McParticles_001From; +// template +// using MultsExtraMCFrom = MultMCExtras_001From; +} // namespace o2::aod + +struct JetDerivedDataEmbeddingProducerTask { + struct : ProducesGroup { + Produces bcCountsTable; + Produces collisionCountsTable; + Produces jDummysTable; + Produces jBCsTable; + Produces jBCParentIndexTable; + Produces jCollisionsTable; + Produces jCollisionUPCsTable; + Produces jCollisionMcInfosTable; + Produces jCollisionsParentIndexTable; + Produces jCollisionsEMCalLabelTable; + Produces jMcCollisionsLabelTable; + Produces jMcCollisionsTable; + Produces jMcCollisionsParentIndexTable; + Produces jTracksTable; + Produces jTracksExtraTable; + Produces jTracksEMCalTable; + Produces jTracksParentIndexTable; + Produces jMcTracksLabelTable; + Produces jMcParticlesTable; + Produces jParticlesParentIndexTable; + Produces jClustersTable; + Produces jClustersParentIndexTable; + Produces jClustersMatchedTracksTable; + Produces jMcClustersLabelTable; + Produces jD0CollisionIdsTable; + Produces jD0McCollisionIdsTable; + Produces jD0IdsTable; + Produces jD0ParticleIdsTable; + Produces jDplusCollisionIdsTable; + Produces jDplusMcCollisionIdsTable; + Produces jDplusIdsTable; + Produces jDplusParticleIdsTable; + Produces jDsCollisionIdsTable; + Produces jDsMcCollisionIdsTable; + Produces jDsIdsTable; + Produces jDsParticleIdsTable; + Produces jDstarCollisionIdsTable; + Produces jDstarMcCollisionIdsTable; + Produces jDstarIdsTable; + Produces jDstarParticleIdsTable; + Produces jLcCollisionIdsTable; + Produces jLcMcCollisionIdsTable; + Produces jLcIdsTable; + Produces jLcParticleIdsTable; + Produces jB0CollisionIdsTable; + Produces jB0McCollisionIdsTable; + Produces jB0IdsTable; + Produces jB0ParticleIdsTable; + Produces jBplusCollisionIdsTable; + Produces jBplusMcCollisionIdsTable; + Produces jBplusIdsTable; + Produces jBplusParticleIdsTable; + Produces jXicToXiPiPiCollisionIdsTable; + Produces jXicToXiPiPiMcCollisionIdsTable; + Produces jXicToXiPiPiIdsTable; + Produces jXicToXiPiPiParticleIdsTable; + Produces jV0IdsTable; + Produces jV0McCollisionsTable; + Produces jV0McCollisionIdsTable; + Produces jV0McsTable; + Produces jV0McIdsTable; + Produces jDielectronCollisionIdsTable; + Produces jDielectronIdsTable; + Produces jDielectronMcCollisionsTable; + Produces jDielectronMcCollisionIdsTable; + Produces JDielectronMcRCollDummysTable; + Produces jDielectronMcsTable; + Produces jDielectronMcIdsTable; + + Produces>> jMcCollisionsLabelMCDTable; + Produces>> jCollisionMcInfosMCDTable; + } products; + + struct : ConfigurableGroup { + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + Configurable dcaZMax{"dcaZMax", 0.2, "maximum DCAZ selection for tracks - only applied for reassociation"}; + Configurable upcBCRangeTimeWindow{"upcBCRangeTimeWindow", 1000, "time range in ns for bunch crossing range checking in upc gap determination"}; + Configurable upcMinNBCs{"upcMinNBCs", 7, "minuimum number of bunch crossing to check in upc gap determination"}; + Configurable upcMinNTracks{"upcMinNTracks", 0, "minuimum number of tracks in upc event"}; + Configurable upcMaxNTracks{"upcMaxNTracks", 100, "maximum number of tracks in upc event"}; + Configurable upcMaxFITTime{"upcMaxFITTime", 4.0, "maximum fit time in upc event"}; + Configurable upcMaxFV0AAmplitude{"upcMaxFV0AAmplitude", 100.0, "maximum amplitude for FV0A in upc event"}; + Configurable upcMaxFT0AAmplitude{"upcMaxFT0AAmplitude", 100.0, "maximum amplitude for FT0A in upc event"}; + Configurable upcMaxFT0CAmplitude{"upcMaxFT0CAmplitude", 50.0, "maximum amplitude for FT0C in upc event"}; + Configurable upcMaxFDDAAmplitude{"upcMaxFDDAAmplitude", -1.0, "maximum amplitude for FDDA in upc event"}; + Configurable upcMaxFDDCAmplitude{"upcMaxFDDCAmplitude", -1.0, "maximum amplitude for FDDC in upc event"}; + + Configurable includeTriggers{"includeTriggers", false, "fill the collision information with software trigger decisions"}; + Configurable includeHadronicRate{"includeHadronicRate", true, "fill the collision information with the hadronic rate"}; + Configurable includeUpcs{"includeUpcs", true, "include option to identify UPC events"}; + Configurable v0ChargedDecaysOnly{"v0ChargedDecaysOnly", true, "store V0s (at particle-level) only if they decay to charged particles"}; + Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + + o2::framework::Configurable applyTrackingEfficiency{"applyTrackingEfficiency", {false}, "configurable to decide whether to apply artificial tracking efficiency (discarding tracks) in jet finding"}; + o2::framework::Configurable> trackingEfficiencyPtBinning{"trackingEfficiencyPtBinning", {0., 10, 999.}, "pt binning of tracking efficiency array if applyTrackingEfficiency is true"}; + o2::framework::Configurable> trackingEfficiency{"trackingEfficiency", {1.0, 1.0}, "tracking efficiency array applied to jet finding if applyTrackingEfficiency is true"}; + } config; + + struct : PresliceGroup { + Preslice perClusterCells = aod::emcalclustercell::emcalclusterId; + Preslice perClusterTracks = aod::emcalclustercell::emcalclusterId; + Preslice perCollisionTrackIndices = aod::track_association::collisionId; + Preslice perBCCollisions = aod::collision::bcId; + Preslice>> permcdBCCollisions = aod::collision::bcId; + Preslice> perCollisionTracks = aod::track::collisionId; + Preslice>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>>> perCollisionTracksMCD = aod::track::collisionId; + } preslices; + + std::map, int32_t> trackCollisionMapping; + std::map, int32_t> trackCollisionMCDMapping; + std::vector collisionMapping; + std::vector collisionTomcdCollisionMapping; + std::vector mcdcollisionToCollisionMapping; + Service ccdb; + o2::base::MatLayerCylSet* lut; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + Service pdgDatabase; + Zorro triggerDecider; + + SGCutParHolder upcCuts; + SGSelector upcSelector; + std::vector amplitudesFV0; + std::vector amplitudesFT0A; + std::vector amplitudesFT0C; + std::vector amplitudesFDDA; + std::vector amplitudesFDDC; + + std::vector trackMCSelection; + + std::vector bcSelMapping; + std::vector bcRctMapping; + + ctpRateFetcher rateFetcher; + int runNumber; + float hadronicRate; + bool withCollisionAssociator; + TRandom3 trackingEfficiencyRandomNumber; + + void init(InitContext const&) + { + hadronicRate = -1.0; + if (doprocessTracksWithCollisionAssociator || config.includeHadronicRate || config.includeTriggers) { + ccdb->setURL(config.ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + runNumber = 0; + if (doprocessTracksWithCollisionAssociator) { + withCollisionAssociator = true; + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(config.ccdbPathLut)); + } else { + withCollisionAssociator = false; + } + } + + upcCuts.SetNDtcoll(config.upcBCRangeTimeWindow); + upcCuts.SetMinNBCs(config.upcMinNBCs); + upcCuts.SetNTracks(config.upcMinNTracks, config.upcMaxNTracks); + upcCuts.SetMaxFITtime(config.upcMaxFITTime); + upcCuts.SetFITAmpLimits({config.upcMaxFV0AAmplitude, config.upcMaxFT0AAmplitude, config.upcMaxFT0CAmplitude, config.upcMaxFDDAAmplitude, config.upcMaxFDDCAmplitude}); + + if (config.applyTrackingEfficiency) { + trackingEfficiencyRandomNumber.SetSeed(0); + if (config.trackingEfficiencyPtBinning->size() < 2) { + LOGP(fatal, "jetFinder workflow: trackingEfficiencyPtBinning configurable should have at least two bin edges"); + } + if (config.trackingEfficiency->size() + 1 != config.trackingEfficiencyPtBinning->size()) { + LOGP(fatal, "jetFinder workflow: trackingEfficiency configurable should have exactly one less entry than the number of bin edges set in trackingEfficiencyPtBinning configurable"); + } + } + } + + void processClearMaps(aod::Collisions const& collisions, aod::CollisionsFrom> const& mcdCollisions, aod::BCs const& bcs, aod::BCsFrom> const& mcdBCs, aod::TracksFrom> const& mcTracks) + { + trackCollisionMapping.clear(); + trackCollisionMCDMapping.clear(); + trackMCSelection.clear(); + trackMCSelection.resize(mcTracks.size(), true); + + collisionTomcdCollisionMapping.clear(); + collisionTomcdCollisionMapping.resize(collisions.size(), -1); + for (const auto& collision : collisions) { + auto const& colisionBC = collision.bc_as(); + auto const& globalBC = colisionBC.globalBC(); + for (auto const& mcdBC : mcdBCs) { + if (mcdBC.globalBC() == globalBC) { + const auto bcCollisions = mcdCollisions.sliceBy(preslices.permcdBCCollisions, mcdBC.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + collisionTomcdCollisionMapping[collision.globalIndex()] = bcCollision.globalIndex(); // only takes the first collision. If multiple collisions are associated to the same BC then it skips the next ones + break; + } + break; + } + } + } + + mcdcollisionToCollisionMapping.clear(); + mcdcollisionToCollisionMapping.resize(mcdCollisions.size(), -1); + for (const auto& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + const auto bcCollisions = collisions.sliceBy(preslices.perBCCollisions, bc.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + mcdcollisionToCollisionMapping[mcdCollision.globalIndex()] = bcCollision.globalIndex(); + break; + } + break; + } + } + } + + if (config.applyTrackingEfficiency) { + std::vector trackingEfficiencyPtBinningVec = config.trackingEfficiencyPtBinning; + std::vector trackingEfficiencyVec = config.trackingEfficiency; + for (const auto& mcTrack : mcTracks) { + auto iter = std::upper_bound(trackingEfficiencyPtBinningVec.begin(), trackingEfficiencyPtBinningVec.end(), mcTrack.pt()); + if (iter != trackingEfficiencyPtBinningVec.begin() && iter != trackingEfficiencyPtBinningVec.end()) { + std::size_t index = std::distance(trackingEfficiencyPtBinningVec.begin(), iter) - 1; + if (trackingEfficiencyRandomNumber.Rndm() > trackingEfficiencyVec[index]) { + trackMCSelection[mcTrack.globalIndex()] = false; + } + } + } + } + if (!doprocessEMCalCollisionLabels) { + for (int i = 0; i < collisions.size(); i++) { // check if this is the correct collision loop + products.jCollisionsEMCalLabelTable(false, false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClearMaps, "clears all maps", true); + + void processBunchCrossings(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), bc.alias_raw(), bc.selection_raw(), bc.rct_raw()); + products.jBCParentIndexTable(bc.globalIndex()); + for (const auto& mcBC : mcBCs) { + if (mcBC.globalBC() == bc.globalBC()) { + bcSelMapping[mcBC.globalIndex()] = jetderiveddatautilities::setMCEventSelectionBit(bc); // note here we are setting the selection on the bc for the MC to the one in data. Is this correct? + bcRctMapping[mcBC.globalIndex()] = bc.rct_raw(); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossings, "produces derived bunch crossing table", true); + + void processBunchCrossingsWithoutSels(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), ~uint32_t{0}, ~uint64_t{0}, ~uint32_t{0}); + products.jBCParentIndexTable(bc.globalIndex()); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); + + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisions, "produces derived collision tables", true); + + void processCollisionsUPC(soa::Join::iterator const& collision, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + if (collision.has_foundBC()) { + auto const upcBC = collision.foundBC_as>(); + auto const upcBCRange = udhelpers::compatibleBCs(collision, upcCuts.NDtcoll(), bcs, upcCuts.minNBCs()); + auto const upcResult = upcSelector.IsSelected(upcCuts, collision, upcBCRange, upcBC, &litudesFV0, &litudesFT0A, &litudesFT0C, &litudesFDDA, &litudesFDDC); + upcGapResult = upcResult.value; + if (upcGapResult != o2::aod::sgselector::SingleGapA && upcGapResult != o2::aod::sgselector::SingleGapC && upcGapResult != o2::aod::sgselector::DoubleGap) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + } + } + float energyCommonZNA = -1.0; + float energyCommonZNC = -1.0; + float timeZNA = -1.0; + float timeZNC = -1.0; + if (bc.has_zdc()) { + auto const& zdc = bc.zdc_as(); + energyCommonZNA = zdc.energyCommonZNA(); + energyCommonZNC = zdc.energyCommonZNC(); + timeZNA = zdc.timeZNA(); + timeZNC = zdc.timeZNC(); + } + products.jCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC, energyCommonZNA, energyCommonZNC, timeZNA, timeZNC); + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsUPC, "produces derived collision tables with UPC information", false); + + void processCollisionsWithoutCentralityAndMultiplicity(soa::Join::iterator const& collision, soa::Join const&) + { + uint64_t triggerBit = 0; + if (config.includeTriggers) { + auto bc = collision.bc_as>(); + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), triggerBit); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsWithoutCentralityAndMultiplicity, "produces derived collision tables without centrality or multiplicity", false); + + void processCollisionsRun2(soa::Join::iterator const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, collision.centRun2V0A(), collision.centRun2V0M(), -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), 0); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsRun2, "produces derived collision tables for Run 2 data", false); + + void processCollisionsALICE3(aod::Collision const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, -1.0, 0, 0, 0); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsALICE3, "produces derived collision tables for ALICE 3 simulations", false); + + void processEMCalCollisionLabels(aod::EMCALMatchedCollision const& collision) + { + products.jCollisionsEMCalLabelTable(collision.ambiguous(), collision.isemcreadout()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processEMCalCollisionLabels, "produces derived collision labels for EMCal", false); + + void processMcCollisionLabels(soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::Collisions const& collisions, aod::McCollisionsFrom> const&) + { + for (auto const& mcdCollision : mcdCollisions) { + products.jMcCollisionsLabelMCDTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosMCDTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosMCDTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + + for (auto const& collision : collisions) { + int32_t mcdCollisionId = collisionTomcdCollisionMapping[collision.globalIndex()]; + if (mcdCollisionId >= 0) { + auto const& mcdCollision = mcdCollisions.iteratorAt(mcdCollisionId); // is this correct????!!!!! + products.jMcCollisionsLabelTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } else { + products.jMcCollisionsLabelTable(-1); + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionLabels, "produces derived MC collision labels table", false); + + void processMcCollisions(soa::Join>, aod::HepMCXSectionsFrom>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisions, "produces derived MC collision table", false); + + void processMcCollisionsWithoutCentralityAndMultiplicity(soa::Join>, aod::HepMCXSectionsFrom>>::iterator const& mcCollision) + { + + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicity, "produces derived MC collision table without centraility and multiplicity", false); + + void processMcCollisionsWithoutXsection(soa::Join>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutXsection, "produces derived MC collision table without cross section information", false); + + void processMcCollisionsWithoutCentralityAndMultiplicityAndXsection(aod::McCollisionsFrom>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicityAndXsection, "produces derived MC collision table without centrality, multiplicity and cross section information", false); + + void processTrackSelectionForWeightedMC(soa::Join>, aod::McTrackLabelsFrom>> const& tracks, soa::Join>, aod::McCollisionLabelsFrom>> const&, aod::McCollisionsFrom> const& mcCollisions, aod::McParticlesFrom> const&) + { + bool hasMBGap = false; + for (auto const& mcCollision : mcCollisions) { + if (mcCollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + hasMBGap = true; + break; + } + } + if (hasMBGap) { + for (auto const& track : tracks) { + if (track.has_collision()) { + auto const& trackCollision = track.collision_as>, aod::McCollisionLabelsFrom>>>(); + if (track.has_mcParticle() && trackCollision.has_mcCollision()) { + auto const& trackMcCollision = trackCollision.mcCollision_as>>(); + auto const& particleMcCollision = track.mcParticle().mcCollision_as>>(); + if (trackMcCollision.globalIndex() != particleMcCollision.globalIndex() && trackMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap && particleMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + trackMCSelection[track.globalIndex()] = false; + } + } + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTrackSelectionForWeightedMC, "select whether tracks should be dropped in weighted productions", false); + + void processTracks(aod::Collisions const& collisions, soa::Join const& tracks, soa::Join>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>> const& tracksMCD, aod::CollisionsFrom> const& collisionsMCD) // need to check where in the framework assumes that the JTracks are always indexed the same as tracks + { + + std::vector mcCollisionSelected(collisionsMCD.size(), false); + for (auto const& collision : collisions) { + auto collisionTrakcs = tracks.sliceBy(preslices.perCollisionTracks, collision.globalIndex()); + for (auto const& track : collisionTrakcs) { + + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + + if (collisionTomcdCollisionMapping[collision.globalIndex()] >= 0) { + + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, collisionTomcdCollisionMapping[collision.globalIndex()]); + mcCollisionSelected[collisionTomcdCollisionMapping[collision.globalIndex()]] = true; + for (auto const& track : collisionMCDTrakcs) { + + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as>>(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + /* + for (int i = 0; i < collisionsMCD.size(); i++) { + if (!mcCollisionSelected[i]) { + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, i); + for (auto const& track : collisionMCDTrakcs) { + products.jTracksTable(mcdcollisionToCollisionMapping[i], track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + + for (auto const& track : tracks) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + + for (auto const& track : tracksMCD) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + */ + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracks, "produces derived track table", true); + + void processTracksWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + runNumber = 0; + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + auto trackParCov = getTrackParCov(track); + if (track.collisionId() == collision.globalIndex()) { + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + } else { + auto bc = collision.bc_as>(); + initCCDB(bc, runNumber, ccdb, doprocessCollisionsRun2 ? config.ccdbPathGrp : config.ccdbPathGrpMag, lut, doprocessCollisionsRun2); + o2::dataformats::DCA dcaCovInfo; + dcaCovInfo.set(-999., -999., -999., -999., -999.); + o2::dataformats::VertexBase collisionInfo; + collisionInfo.setPos({collision.posX(), collision.posY(), collision.posZ()}); + collisionInfo.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(collisionInfo, trackParCov, 2.f, noMatCorr, &dcaCovInfo); + products.jTracksTable(collision.globalIndex(), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), jetderiveddatautilities::setTrackSelectionBit(track, dcaCovInfo.getZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); // only qualitytracksWDCA are a reliable selection + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float dcaXY = dcaCovInfo.getY(); + float dcaZ = dcaCovInfo.getZ(); + float dcaXYZ = std::sqrt(dcaXY * dcaXY + dcaZ * dcaZ); + float covYY = dcaCovInfo.getSigmaY2(); + float covZZ = dcaCovInfo.getSigmaZ2(); + float covYZ = dcaCovInfo.getSigmaYZ(); + float sigmaDCAXYZ; + if (dcaXYZ < o2::constants::math::Almost0) { + sigmaDCAXYZ = o2::constants::math::VeryBig; // Protection against division by zero + } else { + sigmaDCAXYZ = covYY * (2.f * dcaXY / dcaXYZ) * (2.f * dcaXY / dcaXYZ) + covZZ * (2.f * dcaZ / dcaXYZ) * (2.f * dcaZ / dcaXYZ) + 2.f * covYZ * (2.f * dcaXY / dcaXYZ) * (2.f * dcaZ / dcaXYZ); + } + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), dcaZ, dcaXY, dcaXYZ, std::sqrt(covZZ), std::sqrt(covYY), std::sqrt(sigmaDCAXYZ), std::sqrt(trackParCov.getSigma1Pt2())); + } + trackCollisionMapping[{track.globalIndex(), collision.globalIndex()}] = products.jTracksTable.lastIndex(); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksWithCollisionAssociator, "produces derived track table taking into account track-to-collision associations", false); + + void processTracksRun2(soa::Join::iterator const& track) + { + // TracksDCACov table is not yet available for Run 2 converted data. Remove this process function and use only processTracks when that becomes available. + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2 = 0.0; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(1.), std::sqrt(1.), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // dummy values - will be fixed when TracksDCACov table is available for Run 2 + products.jTracksParentIndexTable(track.globalIndex()); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksRun2, "produces derived track table for Run2 AO2Ds", false); + + void processMcTrackLabelsWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcTrackLabelsWithCollisionAssociator, "produces derived track labels table taking into account track-to-collision associations", false); + + void processParticles(aod::McParticlesFrom>::iterator const& particle) + { + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + products.jMcParticlesTable(particle.mcCollisionId(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), particle.pdgCode(), particle.statusCode(), particle.flags(), mothersId, daughtersId); + products.jParticlesParentIndexTable(particle.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processParticles, "produces derived parrticle table", false); + + void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) + { + + for (auto cluster : clusters) { + + auto const clusterCells = cells.sliceBy(preslices.perClusterCells, cluster.globalIndex()); + + float leadingCellEnergy = -1.0; + float subleadingCellEnergy = -1.0; + int leadingCellNumber = -1; + int subleadingCellNumber = -1; + for (auto const& clutserCell : clusterCells) { + float cellAmplitude = clutserCell.calo().amplitude(); + int cellNumber = clutserCell.calo().cellNumber(); + if (cellAmplitude > subleadingCellEnergy) { + subleadingCellEnergy = cellAmplitude; + subleadingCellNumber = cellNumber; + } + if (subleadingCellEnergy > leadingCellEnergy) { + std::swap(leadingCellEnergy, subleadingCellEnergy); + std::swap(leadingCellNumber, subleadingCellNumber); + } + } + + products.jClustersTable(cluster.collisionId(), cluster.id(), cluster.energy(), cluster.coreEnergy(), cluster.rawEnergy(), cluster.eta(), cluster.phi(), cluster.m02(), cluster.m20(), cluster.nCells(), cluster.time(), cluster.isExotic(), cluster.distanceToBadChannel(), cluster.nlm(), cluster.definition(), leadingCellEnergy, subleadingCellEnergy, leadingCellNumber, subleadingCellNumber); + products.jClustersParentIndexTable(cluster.globalIndex()); + + auto const clusterTracks = matchedTracks.sliceBy(preslices.perClusterTracks, cluster.globalIndex()); + std::vector clusterTrackIDs; + for (const auto& clusterTrack : clusterTracks) { + auto JClusterID = trackCollisionMapping.find({clusterTrack.trackId(), cluster.collisionId()}); // does EMCal use its own associator? + clusterTrackIDs.push_back(JClusterID->second); + auto emcTrack = clusterTrack.track_as>(); + products.jTracksEMCalTable(JClusterID->second, emcTrack.trackEtaEmcal(), emcTrack.trackPhiEmcal(), clusterTrack.deltaEta(), clusterTrack.deltaPhi()); + } + products.jClustersMatchedTracksTable(clusterTrackIDs); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClusters, "produces derived cluster tables", false); + + void processMcClusterLabels(aod::EMCALMCCluster const& cluster) + { + std::vector particleIds; + for (auto particleId : cluster.mcParticleIds()) { + particleIds.push_back(particleId); + } + std::vector amplitudeA; + auto amplitudeASpan = cluster.amplitudeA(); + std::copy(amplitudeASpan.begin(), amplitudeASpan.end(), std::back_inserter(amplitudeA)); + products.jMcClustersLabelTable(particleIds, amplitudeA); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcClusterLabels, "produces derived cluster particle label table", false); + /* + void processD0Collisions(aod::HfD0CollIds::iterator const& D0Collision) + { + products.jD0CollisionIdsTable(D0Collision.collisionId()); + } + + void processD0McCollisions(aod::HfD0McCollIdsFrom>::iterator const& D0McCollision) + { + products.jD0McCollisionIdsTable(D0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0McCollisions, "produces derived index for D0 MC collisions", false); + + void processD0(aod::HfD0Idss const& D0Candidates, aod::Tracks const&, aod::HfD0IdssFrom> const& D0CandidatesMCD, aod::TracksFrom> const&) + { + for (auto const& D0Candidate : D0Candidates) { + auto JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + // need to order them by collision though. Actually no this should be done in the HF + for (auto const& D0Candidate : D0CandidatesMCD) { + auto JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0, "produces derived index for D0 candidates", false); + + void processD0MC(aod::HfD0PIds::iterator const& D0Particle) + { + products.jD0ParticleIdsTable(D0Particle.mcCollisionId(), D0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0MC, "produces derived index for D0 particles", false); + + void processDplusCollisions(aod::HfDplusCollIds::iterator const& DplusCollision) + { + products.jDplusCollisionIdsTable(DplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusCollisions, "produces derived index for Dplus collisions", false); + + void processDplusMcCollisions(aod::HfDplusMcCollIds::iterator const& DplusMcCollision) + { + products.jDplusMcCollisionIdsTable(DplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMcCollisions, "produces derived index for Dplus MC collisions", false); + + void processDplus(aod::HfDplusIds::iterator const& DplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.collisionId()}); + } + products.jDplusIdsTable(DplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplus, "produces derived index for Dplus candidates", false); + + void processDplusMC(aod::HfDplusPIds::iterator const& DplusParticle) + { + products.jDplusParticleIdsTable(DplusParticle.mcCollisionId(), DplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMC, "produces derived index for Dplus particles", false); + + void processDsCollisions(aod::HfDsCollIds::iterator const& DsCollision) + { + products.jDsCollisionIdsTable(DsCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsCollisions, "produces derived index for Ds collisions", false); + + void processDsMcCollisions(aod::HfDsMcCollIds::iterator const& DsMcCollision) + { + products.jDsMcCollisionIdsTable(DsMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMcCollisions, "produces derived index for Ds MC collisions", false); + + void processDs(aod::HfDsIds::iterator const& DsCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.collisionId()}); + } + products.jDsIdsTable(DsCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDs, "produces derived index for Ds candidates", false); + + void processDsMC(aod::HfDsPIds::iterator const& DsParticle) + { + products.jDsParticleIdsTable(DsParticle.mcCollisionId(), DsParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMC, "produces derived index for Ds particles", false); + + void processDstarCollisions(aod::HfDstarCollIds::iterator const& DstarCollision) + { + products.jDstarCollisionIdsTable(DstarCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarCollisions, "produces derived index for Dstar collisions", false); + + void processDstarMcCollisions(aod::HfDstarMcCollIds::iterator const& DstarMcCollision) + { + products.jDstarMcCollisionIdsTable(DstarMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMcCollisions, "produces derived index for Dstar MC collisions", false); + + void processDstar(aod::HfDstarIds::iterator const& DstarCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.collisionId()}); + } + products.jDstarIdsTable(DstarCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstar, "produces derived index for Dstar candidates", false); + + void processDstarMC(aod::HfDstarPIds::iterator const& DstarParticle) + { + products.jDstarParticleIdsTable(DstarParticle.mcCollisionId(), DstarParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMC, "produces derived index for Dstar particles", false); + + void processLcCollisions(aod::HfLcCollIds::iterator const& LcCollision) + { + products.jLcCollisionIdsTable(LcCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcCollisions, "produces derived index for Lc collisions", false); + + void processLcMcCollisions(aod::HfLcMcCollIds::iterator const& LcMcCollision) + { + products.jLcMcCollisionIdsTable(LcMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMcCollisions, "produces derived index for Lc MC collisions", false); + + void processLc(aod::HfLcIds::iterator const& LcCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.collisionId()}); + } + products.jLcIdsTable(LcCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLc, "produces derived index for Lc candidates", false); + + void processLcMC(aod::HfLcPIds::iterator const& LcParticle) + { + products.jLcParticleIdsTable(LcParticle.mcCollisionId(), LcParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMC, "produces derived index for Lc particles", false); + + void processB0Collisions(aod::HfB0CollIds::iterator const& B0Collision) + { + products.jB0CollisionIdsTable(B0Collision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0Collisions, "produces derived index for B0 collisions", false); + + void processB0McCollisions(aod::HfB0McCollIds::iterator const& B0McCollision) + { + products.jB0McCollisionIdsTable(B0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0McCollisions, "produces derived index for B0 MC collisions", false); + + void processB0(aod::HfB0Ids::iterator const& B0Candidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.prong3_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.collisionId()}); + } + products.jB0IdsTable(B0Candidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0, "produces derived index for B0 candidates", false); + + void processB0MC(aod::HfB0PIds::iterator const& B0Particle) + { + products.jB0ParticleIdsTable(B0Particle.mcCollisionId(), B0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0MC, "produces derived index for B0 particles", false); + + void processBplusCollisions(aod::HfBplusCollIds::iterator const& BplusCollision) + { + products.jBplusCollisionIdsTable(BplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusCollisions, "produces derived index for Bplus collisions", false); + + void processBplusMcCollisions(aod::HfBplusMcCollIds::iterator const& BplusMcCollision) + { + products.jBplusMcCollisionIdsTable(BplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMcCollisions, "produces derived index for Bplus MC collisions", false); + + void processBplus(aod::HfBplusIds::iterator const& BplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.collisionId()}); + } + products.jBplusIdsTable(BplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplus, "produces derived index for Bplus candidates", false); + + void processBplusMC(aod::HfBplusPIds::iterator const& BplusParticle) + { + products.jBplusParticleIdsTable(BplusParticle.mcCollisionId(), BplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMC, "produces derived index for Bplus particles", false); + + void processXicToXiPiPiCollisions(aod::HfXicToXiPiPiCollIds::iterator const& XicToXiPiPiCollision) + { + products.jXicToXiPiPiCollisionIdsTable(XicToXiPiPiCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiCollisions, "produces derived index for XicToXiPiPi collisions", false); + + void processXicToXiPiPiMcCollisions(aod::HfXicToXiPiPiMcCollIds::iterator const& XicToXiPiPiMcCollision) + { + products.jXicToXiPiPiMcCollisionIdsTable(XicToXiPiPiMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMcCollisions, "produces derived index for XicToXiPiPi MC collisions", false); + + void processXicToXiPiPi(aod::HfXicToXiPiPiIds::iterator const& XicToXiPiPiCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.prong3_as().collisionId()}); + auto JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.prong4_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.collisionId()}); + JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.collisionId()}); + } + products.jXicToXiPiPiIdsTable(XicToXiPiPiCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second, JProng4ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPi, "produces derived index for XicToXiPiPi candidates", false); + + void processXicToXiPiPiMC(aod::HfXicToXiPiPiPIds::iterator const& XicToXiPiPiParticle) + { + products.jXicToXiPiPiParticleIdsTable(XicToXiPiPiParticle.mcCollisionId(), XicToXiPiPiParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMC, "produces derived index for XicToXiPiPi particles", false); + + void processV0(aod::V0Indices::iterator const& V0Candidate, aod::Tracks const&) + { + auto JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.posTrack_as().collisionId()}); + auto JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.negTrack_as().collisionId()}); + if (withCollisionAssociator) { + JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.collisionId()}); + JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.collisionId()}); + } + products.jV0IdsTable(V0Candidate.collisionId(), JPosTrackID->second, JNegTrackID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0, "produces derived index for V0 candidates", false); + + void processV0MC(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { // can loop over McV0Labels tables if we want to only store matched V0Particles + bool filledV0McCollisionTable = false; + for (auto const& particle : particles) { + if (jetv0utilities::isV0Particle(particles, particle, config.v0ChargedDecaysOnly)) { + if (!filledV0McCollisionTable) { + products.jV0McCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jV0McCollisionIdsTable(mcCollision.globalIndex()); + filledV0McCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jV0McsTable(products.jV0McCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetv0utilities::setV0ParticleDecayBit(particles, particle)); + products.jV0McIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0MC, "produces V0 particles", false); + + void processDielectronCollisions(aod::ReducedEventsInfo::iterator const& DielectronCollision) + { + products.jDielectronCollisionIdsTable(DielectronCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronCollisions, "produces derived index for Dielectron collisions", false); + + void processDielectron(aod::DielectronInfo const& DielectronCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.collisionId()}); + } + products.jDielectronIdsTable(DielectronCandidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectron, "produces derived index for Dielectron candidates", false); + + void processDielectronMc(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { + bool filledDielectronMcCollisionTable = false; + for (auto const& particle : particles) { + if (jetdqutilities::isDielectronParticle(particles, particle)) { + if (!filledDielectronMcCollisionTable) { + products.jDielectronMcCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jDielectronMcCollisionIdsTable(mcCollision.globalIndex()); + filledDielectronMcCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jDielectronMcsTable(products.jDielectronMcCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetdqutilities::setDielectronParticleDecayBit(particles, particle), RecoDecay::getCharmHadronOrigin(particles, particle, false)); // Todo: should the last thing be false? + products.jDielectronMcIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + products.JDielectronMcRCollDummysTable(false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronMc, "produces Dielectron mccollisions and particles", false); + */ +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"jet-deriveddata-embedding-producer"})}; +} diff --git a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx index d60275c6fac..ca50b715514 100644 --- a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx +++ b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx @@ -130,7 +130,7 @@ struct eventWiseConstituentSubtractorTask { } inputParticles.clear(); tracksSubtracted.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); tracksSubtracted = eventWiseConstituentSubtractor.JetBkgSubUtils::doEventConstSub(inputParticles, candidate.rho(), candidate.rhoM()); for (auto const& trackSubtracted : tracksSubtracted) { diff --git a/PWGJE/TableProducer/rhoEstimator.cxx b/PWGJE/TableProducer/rhoEstimator.cxx index c7f11e99b0c..1e340b41587 100644 --- a/PWGJE/TableProducer/rhoEstimator.cxx +++ b/PWGJE/TableProducer/rhoEstimator.cxx @@ -238,7 +238,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoD0Table(rho, rhoM); @@ -270,7 +270,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDplusTable(rho, rhoM); @@ -303,7 +303,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDsTable(rho, rhoM); @@ -335,7 +335,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDstarTable(rho, rhoM); @@ -367,7 +367,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoLcTable(rho, rhoM); @@ -399,7 +399,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoB0Table(rho, rhoM); @@ -431,7 +431,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoBplusTable(rho, rhoM); @@ -463,7 +463,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoXicToXiPiPiTable(rho, rhoM); @@ -495,7 +495,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDielectronTable(rho, rhoM); diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 9a2f85caa4e..c633110950f 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -515,4 +515,8 @@ if(FastJet_FOUND) SOURCES jetUpcQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(embedding-qa + SOURCES embeddingQA.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) endif() diff --git a/PWGJE/Tasks/embeddingQA.cxx b/PWGJE/Tasks/embeddingQA.cxx new file mode 100644 index 00000000000..65425fc82f0 --- /dev/null +++ b/PWGJE/Tasks/embeddingQA.cxx @@ -0,0 +1,186 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet tutorial task for hands on tutorial session (09/11/2023) +// +/// \author Nima Zardoshti +// + +#include "PWGJE/Core/FastJetUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#include + +struct EmbeddingQATask { + HistogramRegistry registry{"registry", + {{"h_collision_zdiff", "collision z diff;z_{Data}-z_{MC} (cm);entries", {HistType::kTH1F, {{200, -10., 10.}}}}, + {"h_tracks_all_pt", "all tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_embedded_pt", "embedded tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_sub_pt", "subtracted tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_particles_pt", "particles;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_pt", "jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_sub_pt", "subtracted jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcd_pt", "detector-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcp_pt", "particle-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_detector_response", "detector response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} detector (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_response", "response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} embedded subtracted (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_background_response", "background response matrix;#it{p}_{T,jet} detector (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_backgroundfluctuations_response", "background fluctuations response matrix;#it{p}_{T,jet} embedded subtracted (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_tracks_data_pt", "tracks derived data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_pt", "tracks derived detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_pt", "particles derived particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_data_original_pt", "tracks original data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_original_pt", "tracks original detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_original_pt", "particles original particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}}}; + + Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + + std::vector eventSelection; + int trackSelection = -1; + + void init(o2::framework::InitContext&) + { + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + } + + Preslice perBCCollisions = aod::collision::bcId; + + void processTracksOriginal(aod::Tracks const& tracks, aod::TracksFrom> const& tracksMCD, aod::McParticlesFrom> const& tracksMCP, aod::JetTracks const& tracksEmbedded, aod::JetParticles const& particles, aod::Collisions const& collisions, soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::McCollisionsFrom> const&, aod::BCs const& bcs, aod::BCsFrom> const&) + { + std::vector mcdCollisionGoodForEmbedding(mcdCollisions.size(), false); + for (auto const& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + auto const& collisionsPerBC = collisions.sliceBy(perBCCollisions, bc.globalIndex()); + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + if (collisionsPerBC.size() > 0) { + mcdCollisionGoodForEmbedding[mcdCollision.globalIndex()] = true; + registry.fill(HIST("h_collision_zdiff"), collisionsPerBC.iteratorAt(0).posZ() - mcCollision.posZ()); + break; + } + } + } + } + + for (auto const& track : tracks) { + if (track.collisionId() >= 0) { + registry.fill(HIST("h_tracks_data_original_pt"), track.pt()); + } + } + for (auto const& track : tracksMCD) { + if (track.collisionId() >= 0) { + if (mcdCollisionGoodForEmbedding[track.collisionId()]) { + registry.fill(HIST("h_tracks_mcd_original_pt"), track.pt()); + } + } + } + for (auto const& track : tracksMCP) { + registry.fill(HIST("h_tracks_mcp_original_pt"), track.pt()); + } + for (auto const& track : tracksEmbedded) { + if (!(track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_data_pt"), track.pt()); + } + } + for (auto const& track : tracksEmbedded) { + if ((track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_mcd_pt"), track.pt()); + } + } + for (auto const& particle : particles) { + registry.fill(HIST("h_tracks_mcp_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracksOriginal, "track QA", true); + + void processTracks(aod::JetCollision const&, aod::JetTracks const& tracks, aod::JetTracksSub const& subTracks) + { + for (auto const& track : tracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_all_pt"), track.pt()); + } + if (jetderiveddatautilities::selectTrack(track, trackSelection, true)) { + registry.fill(HIST("h_tracks_embedded_pt"), track.pt()); + } + } + for (auto const& track : subTracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_sub_pt"), track.pt()); + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracks, "track QA", true); + + void processParticles(aod::JetMcCollision const&, aod::JetParticles const& particles) + { + for (auto const& particle : particles) { + registry.fill(HIST("h_particles_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processParticles, "track QA", true); + + void processJets(aod::ChargedJets const& jets, aod::ChargedEventWiseSubtractedJets const& subJets, aod::ChargedMCDetectorLevelJets const& mcdJets, aod::ChargedMCParticleLevelJets const& mcpJets) + { + for (auto const& jet : jets) { + registry.fill(HIST("h_jets_pt"), jet.pt()); + } + for (auto const& jet : subJets) { + registry.fill(HIST("h_jets_sub_pt"), jet.pt()); + } + for (auto const& jet : mcdJets) { + registry.fill(HIST("h_jets_mcd_pt"), jet.pt()); + } + for (auto const& jet : mcpJets) { + registry.fill(HIST("h_jets_mcp_pt"), jet.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processJets, "jet QA", true); + + void processResponse(aod::ChargedJets const&, soa::Join const&, soa::Join const&, soa::Join const& mcpJets) + { + + for (auto const& mcpJet : mcpJets) { + for (auto const& mcdJet : mcpJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_detector_response"), mcpJet.pt(), mcdJet.pt()); + for (auto const& subJet : mcdJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_response"), mcpJet.pt(), subJet.pt()); + for (auto const& jet : subJet.matchedJetGeo_as()) { + registry.fill(HIST("h_jet_background_response"), mcdJet.pt(), jet.pt()); + registry.fill(HIST("h_jet_backgroundfluctuations_response"), subJet.pt(), jet.pt()); + } + } + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processResponse, "jet response", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"embedding-qa"})}; } diff --git a/PWGJE/Tasks/jetSubstructure.cxx b/PWGJE/Tasks/jetSubstructure.cxx index fe568b6c2a6..5552101896c 100644 --- a/PWGJE/Tasks/jetSubstructure.cxx +++ b/PWGJE/Tasks/jetSubstructure.cxx @@ -80,6 +80,7 @@ struct JetSubstructureTask { Configurable doPairBkg{"doPairBkg", true, "save bkg pairs"}; Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; Service pdg; std::vector jetConstituents; @@ -260,7 +261,7 @@ struct JetSubstructureTask { } if constexpr (!std::is_same_v, aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } diff --git a/PWGJE/Tasks/jetSubstructureHF.h b/PWGJE/Tasks/jetSubstructureHF.h index b0921af1f4e..4332f649f84 100644 --- a/PWGJE/Tasks/jetSubstructureHF.h +++ b/PWGJE/Tasks/jetSubstructureHF.h @@ -83,6 +83,7 @@ struct JetSubstructureHFTask { o2::framework::Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable recoilRegion{"recoilRegion", 0.6, "recoil acceptance in phi"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; o2::framework::Service pdg; float candMass; @@ -356,7 +357,7 @@ struct JetSubstructureHFTask { } if constexpr (!std::is_same_v, o2::aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } From 96d6e372feedfa99003c128c20e50a5921232c9c Mon Sep 17 00:00:00 2001 From: glromane <95305986+glromane@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:06:16 +0200 Subject: [PATCH 213/648] =?UTF-8?q?[PWGCF]=20Update=20the=20actual=20TOF?= =?UTF-8?q?=20(for=20binned)=20range=20for=20the=20"pRemoveTofOutOfRang?= =?UTF-8?q?=E2=80=A6=20(#18207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx index 9cd26e1f250..7f20deb9c56 100644 --- a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx +++ b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx @@ -260,7 +260,7 @@ struct singleTrackSelector { for (auto ii : particlesToKeep) if (o2::aod::singletrackselector::TPCselection(track, std::make_pair(ii, keepWithinNsigmaTPC))) { - if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-10.0, 10.0}), std::vector{-10.0, 10.0})) + if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-6.0, 6.0}), std::vector{-10.0, 10.0})) continue; if (track.pt() > _ptRemoveTofOutOfRange.value[0] && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{_ptRemoveTofOutOfRange.value[1], _ptRemoveTofOutOfRange.value[2]}), std::vector{-10.f, +10.f})) continue; From 0ebec5c4d4d95c59a8412c23e3bed19744eac29d Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Wed, 7 Oct 2026 22:39:34 +0200 Subject: [PATCH 214/648] [PWGLF,PWGMM] Update selections and histos in MC (#18204) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 590 ++++++++++++++++------------- 1 file changed, 330 insertions(+), 260 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index a985de4ba7d..a42e293165f 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -295,6 +295,7 @@ struct DndetaMFTPbPb { Configurable maxDCAxy{"maxDCAxy", 2.0f, "Cut on dca XY"}; Configurable useDCAzCut{"useDCAzCut", true, "use dca Z cut"}; Configurable maxDCAz{"maxDCAz", 2.0f, "Cut on dca Z"}; + Configurable removeFakeByMcMask{"removeFakeByMcMask", 1, "Remove fakes based on MC mask"}; Configurable selMcMask{"selMcMask", 0, "McMask for correct match"}; } trackCuts; @@ -305,7 +306,7 @@ struct DndetaMFTPbPb { Configurable minZvtx{"minZvtx", -10.0f, "minimum cut on z-vtx (cm)"}; Configurable useZDiffCut{"useZDiffCut", false, "use Zvtx reco-mc diff. cut"}; Configurable maxZvtxDiff{"maxZvtxDiff", 1.0f, "max allowed Z vtx difference for reconstruced collisions (cm)"}; - Configurable useZVtxCutMC{"useZVtxCutMC", false, "use Zvtx cut in MC"}; + Configurable useZVtxCutMC{"useZVtxCutMC", true, "use Zvtx cut in MC"}; Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; Configurable requireRejectSameBunchPileup{"requireRejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, " requireNoCollInTimeRangeStrict"}; @@ -322,6 +323,7 @@ struct DndetaMFTPbPb { Configurable maxIR{"maxIR", -1, "maximum IR (kHz) collisions"}; Configurable requireTVX{"requireTVX", false, "Use TVX trigger, i.e. FT0A and FT0C acceptance"}; Configurable useGenMult{"useGenMult", false, "use MultMC for centrality"}; + Configurable checkCollisionId{"checkCollisionId", false, "check collision ids"}; } eventCuts; Service pdg{}; @@ -569,13 +571,17 @@ struct DndetaMFTPbPb { } registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen_t", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {multAxis, zAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); } @@ -615,6 +621,8 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); @@ -630,9 +638,9 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); } registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genEvtTypeAxis, occupancyAxis}}}); registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); @@ -648,9 +656,10 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); } registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #varphi; #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); } @@ -679,8 +688,8 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEvtLossStatus::kMcEvtStatus); iBin++) { registryMC.get(HIST("Events/hMcEvtLossStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtLossStatus[iBin].data()); } - registryMC.add("Events/Centrality/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); - auto hstat = registryMC.get(HIST("Events/Centrality/EvtSigLossStatus")); + registryMC.add("Events/Centrality/hEvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); + auto hstat = registryMC.get(HIST("Events/Centrality/hEvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); @@ -762,6 +771,7 @@ struct DndetaMFTPbPb { using CollsCorr = soa::Join; using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; + /// Tracks using MftTracksLabeled = soa::Join; using MftBestTracksLabeled = soa::Join; @@ -1339,7 +1349,7 @@ struct DndetaMFTPbPb { } template - void fillHistMC(P const& particles, float zvtx, float c, float occ) + void fillHistMC(P const& particles, float zvtx, float c, float occ, bool const gtZeroColl) { for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { @@ -1349,11 +1359,20 @@ struct DndetaMFTPbPb { continue; } if constexpr (isCent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); - registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), particle.phi(), particle.eta(), c, occ); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), particle.phi(), particle.eta(), c, occ); } else { - registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); - registryMC.fill(HIST("Tracks/PhiEtaGen"), particle.phi(), particle.eta(), occ); + registryMC.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen_t"), particle.phi(), particle.eta(), occ); + } + if (gtZeroColl) { + if constexpr (isCent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), particle.phi(), particle.eta(), c, occ); + } else { + registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen"), particle.phi(), particle.eta(), occ); + } } } } @@ -1582,7 +1601,7 @@ struct DndetaMFTPbPb { if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { continue; } - if (gConf.useTrackSel && !isTrackSelected(itrack)) { + if (gConf.useTrackSel && !isTrackSelected(itrack)) { continue; } nBestTrks++; @@ -1615,7 +1634,7 @@ struct DndetaMFTPbPb { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { float o = getOccupancy(collision, eventCuts.occupancyEstimator); if (o > occGen) { occGen = o; @@ -1629,7 +1648,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { float c = getRecoCent(collision); if (c < crecMin) { crecMin = c; @@ -1641,6 +1660,8 @@ struct DndetaMFTPbPb { } } + bool gtZeroColl = false; + int gtOneColl = 0; for (auto const& collision : collisions) { float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); @@ -1649,73 +1670,74 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } - if (!isGoodEvent(collision)) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); - } - if (!collision.has_mcCollision()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); - } - if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); - } - - auto nTrkRec = 0; - auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); - for (auto const& track : perColSample) { - if (!isTrackSelected(track)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { + gtZeroColl = true; + ++gtOneColl; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); + } + if (!collision.has_mcCollision()) { continue; } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { - const auto& particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); + } + + auto nTrkRec = 0; + auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); + for (auto const& track : perColSample) { + if (!isTrackSelected(track)) { continue; } - if (gConf.useParticleSel && !isParticleSelected(particle)) { - continue; + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { + const auto& particle = track.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occRec); + registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + } + ++nTrkRec; } - if (collision.mcCollisionId() != particle.mcCollisionId()) { + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { continue; } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); - } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); - } - ++nTrkRec; } - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), occRec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); } } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); - } } if constexpr (has_reco_cent) { @@ -1724,6 +1746,21 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } + } auto nCharged = countPart(particles); if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); @@ -1737,7 +1774,7 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); } void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, @@ -1798,6 +1835,7 @@ struct DndetaMFTPbPb { buildLookupTable(collisions); + bool gtZeroColl = false; for (auto const& collision : collisions) { float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); @@ -1806,136 +1844,134 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } - if (!isGoodEvent(collision)) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); - } - if (!collision.has_mcCollision()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); - } - if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { - continue; - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); - } - - auto nATrk = 0; - auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); - for (auto const& atrack : perCollisionASample) { - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); - } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); - } - if (!isBestTrackSelected(atrack)) { - continue; - } + if (!gConf.useEvtSel || isGoodEvent(collision)) { + gtZeroColl = true; if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); } - const float bestDcaZ = getDCAz(atrack); - auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { + if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); } - if (!itrack.has_collision()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } - if (gConf.rmReassigned) { - if (itrack.collisionId() != atrack.bestCollisionId()) { - continue; + + auto nATrk = 0; + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); } - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); - } else { - registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); - } - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { - const auto& particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if (!isBestTrackSelected(atrack)) { continue; } - if (gConf.useParticleSel && !isParticleSelected(particle)) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { continue; } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { - continue; - } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); } - // if (collision.mcCollisionId() != particle.mcCollisionId()) { - // continue; - // } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + if (!itrack.has_collision()) { continue; } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { - continue; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + } + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); - registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + // if (collision.mcCollisionId() != particle.mcCollisionId()) { + // continue; + // } + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); + registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + } + ++nATrk; } - ++nATrk; } - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nATrk, collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nATrk, collision.posZ(), occRec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); } - } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nATrk, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nATrk, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); } } - if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto nCharged = countPart(particles); if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); @@ -1949,7 +1985,7 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); } void processMcBestInclusive(CollsMCExtraMult::iterator const& mccollision, @@ -1985,6 +2021,10 @@ struct DndetaMFTPbPb { auto cRec = CInvalid; auto occRec = CInvalid; bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -1996,11 +2036,13 @@ struct DndetaMFTPbPb { cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } - + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); } } else { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); @@ -2017,61 +2059,61 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); } } } } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision)); } - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision)); } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision)); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getRecoCent(collision)); } auto perColTrks = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); for (auto const& track : perColTrks) { if (!isTrackSelected(track)) { continue; } - if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); if (!isChrgParticle(particle.pdgCode())) { continue; @@ -2083,16 +2125,17 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.phi(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2133,6 +2176,10 @@ struct DndetaMFTPbPb { auto cRec = CInvalid; auto occRec = CInvalid; bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -2144,10 +2191,13 @@ struct DndetaMFTPbPb { cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); } } else { registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); @@ -2164,54 +2214,54 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); } } } } for (const auto& collision : collisions) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), occRec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getOccupancy(collision, eventCuts.occupancyEstimator)); } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), occRec); + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getOccupancy(collision, eventCuts.occupancyEstimator)); } auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); @@ -2231,7 +2281,7 @@ struct DndetaMFTPbPb { continue; } } - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); if (!isChrgParticle(particle.pdgCode())) { continue; @@ -2244,23 +2294,24 @@ struct DndetaMFTPbPb { continue; } } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { - continue; - } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { - continue; + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.phi(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } @@ -2301,6 +2352,10 @@ struct DndetaMFTPbPb { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); auto cRec = CInvalid; auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -2311,8 +2366,11 @@ struct DndetaMFTPbPb { cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { @@ -2323,6 +2381,9 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } @@ -2330,7 +2391,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { @@ -2346,10 +2407,10 @@ struct DndetaMFTPbPb { auto ncnt = 0; auto relatedTracks = particle.template mfttracks_as(); for (auto const& track : relatedTracks) { - if (!isTrackSelected(track)) { + if (!isTrackSelected(track)) { continue; } - if (track.mcMask() != trackCuts.selMcMask) { + if (trackCuts.removeFakeByMcMask && (track.mcMask() != trackCuts.selMcMask)) { continue; } ++ncnt; @@ -2357,15 +2418,15 @@ struct DndetaMFTPbPb { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; } } - registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); if (ncnt > 1) { // secondaries if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } else { @@ -2387,9 +2448,9 @@ struct DndetaMFTPbPb { } if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); for (auto const& track : relatedTracks) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); @@ -2403,7 +2464,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { @@ -2447,6 +2508,10 @@ struct DndetaMFTPbPb { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); auto cRec = CInvalid; auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; @@ -2457,11 +2522,14 @@ struct DndetaMFTPbPb { cRec = getRecoCent(collision); occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } buildLookupTable(collisions); for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { @@ -2472,6 +2540,9 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } @@ -2479,7 +2550,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { @@ -2495,31 +2566,34 @@ struct DndetaMFTPbPb { auto ncnt = 0; auto relatedTracks = particle.template mfttracks_as(); for (auto const& atrack : relatedTracks) { - if (!isBestTrackSelected(atrack)) { + if (!isBestTrackSelected(atrack)) { continue; } - const int bestRecColl = atrack.bestCollisionId(); - if (!mapMcCollIdPerRecColl.contains(bestRecColl)) { + if (trackCuts.removeFakeByMcMask && (atrack.mcMask() != trackCuts.selMcMask)) { continue; } - int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(bestRecColl)->second; - if (mcCollIdRec != particle.mcCollisionId()) { - continue; + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } } ++ncnt; if constexpr (has_reco_cent) { if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; } } - registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); if (ncnt > 1) { // secondaries if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } else { @@ -2541,9 +2615,9 @@ struct DndetaMFTPbPb { } if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); for (auto const& track : relatedTracks) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); @@ -2557,7 +2631,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { @@ -2593,7 +2667,7 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups> const& collisions, + soa::SmallGroups const& collisions, aod::McParticles const& particles) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); @@ -2618,28 +2692,24 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSel)); // Evt loss den bool gtZeroColl = false; - auto maxNcontributors = -1; float cRec = CInvalid; for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - if (std::abs(collision.posZ()) >= eventCuts.maxZvtx) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSplit)); registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), mcCollision.multMCFT0C()); - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - cRec = getRecoCent(collision); - } + cRec = getRecoCent(collision); gtZeroColl = true; } auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 1., cRec); // Evt split den + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 1., cRec); // Evt split den registryMC.fill(HIST("Events/hMultGenVsCent"), cRec, mcCollision.multMCFT0C()); registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); @@ -2649,13 +2719,13 @@ struct DndetaMFTPbPb { return; } registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenRecEvt)); // Evt loss num - registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 2., cRec); // Evt split num registryMC.fill(HIST("Events/hMultGenVsCentRec"), cRec, mcCollision.multMCFT0C()); registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); } if (collisions.size() == 0) { - registryMC.fill(HIST("Events/Centrality/EvtSigLossStatus"), 3., cRec); + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 3., cRec); } for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { @@ -2685,7 +2755,7 @@ struct DndetaMFTPbPb { for (const auto& collision : collisions) { auto crec = getRecoCent(collision); registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); - if (!isGoodEvent(collision)) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } if (!collision.has_mcCollision()) { @@ -2710,13 +2780,13 @@ struct DndetaMFTPbPb { auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { - if (!isBestTrackSelected(atrack)) { + if (!isBestTrackSelected(atrack)) { continue; } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { + if (!isTrackSelected(itrack)) { continue; } if (!itrack.has_collision()) { @@ -2729,7 +2799,7 @@ struct DndetaMFTPbPb { } registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRec"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), atrack.bestDCAZ(), crec, crec); - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { auto particle = itrack.template mcParticle_as(); if (!isChrgParticle(particle.pdgCode())) { continue; From b286a24ea1ca27b44803f672c24883b5482b0775 Mon Sep 17 00:00:00 2001 From: ariedel-cern <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:53:25 +0200 Subject: [PATCH 215/648] [PWGCF] Fix TOF selection logic of daughter tracks in femto framework (#18202) Co-authored-by: ALICE Action Bot --- PWGCF/Femto/Core/baseSelection.h | 5 +++ PWGCF/Femto/Core/cascadeBuilder.h | 58 ++++++++++++++++----------- PWGCF/Femto/Core/charmHadronBuilder.h | 3 ++ PWGCF/Femto/Core/collisionBuilder.h | 1 + PWGCF/Femto/Core/kinkBuilder.h | 1 + PWGCF/Femto/Core/photonBuilder.h | 10 ++++- PWGCF/Femto/Core/selectionContainer.h | 7 ++++ PWGCF/Femto/Core/trackBuilder.h | 42 ++++++++++++------- PWGCF/Femto/Core/v0Builder.h | 50 ++++++++++++++--------- 9 files changed, 119 insertions(+), 58 deletions(-) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 2cd6071f36a..947e766bb50 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -283,6 +283,11 @@ class BaseSelection /// \param comments Vector of comment strings, one per selection threshold. void addComments(int observableIndex, std::vector const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); } + /// \brief Attach the same comment to every selection threshold of a specific observable. + /// \param observableIndex Index of the observable. + /// \param comment Comment string applied to all thresholds. + void addComments(int observableIndex, std::string const& comment) { mSelectionContainers.at(observableIndex).addComments(comment); } + [[nodiscard]] bool isPassThrough() const { return mPassThrough; } /// \brief Check whether all required and optional cuts are passed. diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 84e3809d0f0..c0976be17d1 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -63,25 +64,25 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \ - o2::framework::Configurable> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \ - o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ - o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \ - o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ - o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection"}; \ - o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, candidates whose daughters have no TOF signal are kept"}; +#define CASCADE_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \ + o2::framework::Configurable> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \ + o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; struct ConfXiBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("XiBits"); @@ -248,6 +249,7 @@ class CascadeSelection : public baseselection::BaseSelection(mUseYCut)}, {charmHadronFilterNames.at(kYMin), mYMin}, {charmHadronFilterNames.at(kYMax), mYMax}, {charmHadronFilterNames.at(kMassMin), mMassMin}, diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 2ae41c34b78..7bd147009e5 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -262,6 +262,7 @@ class CollisionSelection : public baseselection::BaseSelectionaddSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value); + this->addComments(kRctFlags, "label = " + confRct.label.value + "; useZdc = " + std::to_string(confRct.useZdc.value) + "; treatLimitedAcceptanceAsBad = " + std::to_string(confRct.treatLimitedAcceptanceAsBad.value)); this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value); this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index d95cd34c6cd..e35e66ae0ff 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -245,6 +245,7 @@ class KinkSelection : public baseselection::BaseSelectionaddSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); } this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false); diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h index d64f86c56d6..384a6e7f3bc 100644 --- a/PWGCF/Femto/Core/photonBuilder.h +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -179,7 +180,9 @@ class PhotonSelection : public baseselection::BaseSelectionaddSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false); this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kPosDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kNegDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); this->setupSelectionHistogram(registry); this->template setupFilterHistogram( @@ -213,12 +216,17 @@ class PhotonSelection : public baseselection::BaseSelectionevaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end())); // TPC electron PID: evaluate only when a TPC signal is available, matching - // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC + // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC. + // If keepDaughtersWithoutTpc is false, feed a value that fails every limit instead of + // skipping evaluation entirely, so this (always mandatory) cut correctly rejects the + // candidate instead of silently letting it through auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) { if (hasTpc) { this->evaluateObservable(bit, tpcNSigmaEl); } else if (mKeepDaughtersWithoutTpc) { this->evaluateObservable(bit, 0.f); + } else { + this->evaluateObservable(bit, std::numeric_limits::max()); } }; evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl()); diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index cf3b7da0d11..4e60c0d8328 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -236,6 +236,13 @@ class SelectionContainer mComments = comments; } + /// \brief Attach the same comment to every threshold of this selection. + /// \param comment Comment string applied to all thresholds. + void addComments(std::string const& comment) + { + mComments = std::vector(getNSelections(), comment); + } + /// \brief Get comments attached to the selection thresholds. /// \return Vector of comment strings. [[nodiscard]] std::string getComment(int selectionIndex) const diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 51861002449..1a89eafeaa0 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -69,63 +69,63 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable minMomTofElectron{"minMomTofElectron", 0.3, "Minimum momentum to required TOF PID for Electron"}; o2::framework::Configurable> itsElectron{"itsElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Electron PID"}; o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Electron PID"}; - o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID"}; + o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum nsigma_TPCITS for Electron PID"}; - o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID"}; + o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; // Pion PID cuts o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make pion PID optional"}; o2::framework::Configurable minMomTofPion{"minMomTofPion", 0.5, "Minimum momentum to required TOF PID for Pion"}; o2::framework::Configurable> itsPion{"itsPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Pion PID"}; o2::framework::Configurable> tpcPion{"tpcPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Pion PID"}; - o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID"}; + o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum nsigma_TPCITS for Pion PID"}; - o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID"}; + o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; // Kaon PID cuts o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make kaon PID optional"}; o2::framework::Configurable minMomTofKaon{"minMomTofKaon", 0.4, "Minimum momentum to required TOF PID for Kaon"}; o2::framework::Configurable> itsKaon{"itsKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Kaon PID"}; o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Kaon PID"}; - o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID"}; + o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum nsigma_TPCITS for Kaon PID"}; - o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID"}; + o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; // Proton PID cuts o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make proton PID optional"}; o2::framework::Configurable minMomTofProton{"minMomTofProton", 0.75, "Minimum momentum to required TOF PID for Proton"}; o2::framework::Configurable> itsProton{"itsProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Proton PID"}; o2::framework::Configurable> tpcProton{"tpcProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Proton PID"}; - o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID"}; + o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; o2::framework::Configurable> tpcitsProton{"tpcitsProton", {}, "Maximum nsigma_TPCITS for Proton PID"}; - o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID"}; + o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; // Deuteron PID cuts o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make deuteron PID optional"}; o2::framework::Configurable minMomTofDeuteron{"minMomTofDeuteron", 1.2, "Minimum momentum to required TOF PID for Deuteron"}; o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Deuteron PID"}; o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Deuteron PID"}; - o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID"}; + o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum nsigma_TPCITS for Deuteron PID"}; - o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID"}; + o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; // Triton PID cuts o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make triton PID optional"}; o2::framework::Configurable minMomTofTriton{"minMomTofTriton", 1.4, "Minimum momentum to required TOF PID for Triton"}; o2::framework::Configurable> itsTriton{"itsTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Triton PID"}; o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Triton PID"}; - o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID"}; + o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum nsigma_TPCITS for Triton PID"}; - o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID"}; + o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; // Helium PID cuts o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make helium PID optional"}; o2::framework::Configurable minMomTofHelium{"minMomTofHelium", 1.6, "Minimum momentum to required TOF PID for Helium"}; o2::framework::Configurable> itsHelium{"itsHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Helium PID"}; o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Helium PID"}; - o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID"}; + o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum nsigma_TPCITS for Helium PID"}; - o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID"}; + o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; }; // define the template structure for TrackSelection @@ -358,56 +358,70 @@ class TrackSelection : public baseselection::BaseSelectionaddSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, false, false, config.requirePidElectron); this->addSelection(kTpcElectron, trackSelectionNames.at(kTpcElectron), config.tpcElectron.value, false, false, config.requirePidElectron); this->addSelection(kTofElectron, trackSelectionNames.at(kTofElectron), config.tofElectron.value, false, false, config.requirePidElectron); + this->addComments(kTofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); this->addSelection(kTpcitsElectron, trackSelectionNames.at(kTpcitsElectron), config.tpcitsElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); this->addSelection(kTpctofElectron, trackSelectionNames.at(kTpctofElectron), config.tpctofElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); + this->addComments(kTpctofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); mElectronTofThres = config.minMomTofElectron.value; // add selections for Pion pid this->addSelection(kItsPion, trackSelectionNames.at(kItsPion), config.itsPion.value, false, false, config.requirePidPion); this->addSelection(kTpcPion, trackSelectionNames.at(kTpcPion), config.tpcPion.value, false, false, config.requirePidPion); this->addSelection(kTofPion, trackSelectionNames.at(kTofPion), config.tofPion.value, false, false, config.requirePidPion); + this->addComments(kTofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); this->addSelection(kTpcitsPion, trackSelectionNames.at(kTpcitsPion), config.tpcitsPion.value, limits::kUpperLimit, false, false, config.requirePidPion); this->addSelection(kTpctofPion, trackSelectionNames.at(kTpctofPion), config.tpctofPion.value, limits::kUpperLimit, false, false, config.requirePidPion); + this->addComments(kTpctofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); mPionTofThres = config.minMomTofPion.value; // add selections for Kaon pid this->addSelection(kItsKaon, trackSelectionNames.at(kItsKaon), config.itsKaon.value, false, false, config.requirePidKaon); this->addSelection(kTpcKaon, trackSelectionNames.at(kTpcKaon), config.tpcKaon.value, false, false, config.requirePidKaon); this->addSelection(kTofKaon, trackSelectionNames.at(kTofKaon), config.tofKaon.value, false, false, config.requirePidKaon); + this->addComments(kTofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); this->addSelection(kTpcitsKaon, trackSelectionNames.at(kTpcitsKaon), config.tpcitsKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); this->addSelection(kTpctofKaon, trackSelectionNames.at(kTpctofKaon), config.tpctofKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); + this->addComments(kTpctofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); mKaonTofThres = config.minMomTofKaon.value; // add selections for Proton pid this->addSelection(kItsProton, trackSelectionNames.at(kItsProton), config.itsProton.value, false, false, config.requirePidProton); this->addSelection(kTpcProton, trackSelectionNames.at(kTpcProton), config.tpcProton.value, false, false, config.requirePidProton); this->addSelection(kTofProton, trackSelectionNames.at(kTofProton), config.tofProton.value, false, false, config.requirePidProton); + this->addComments(kTofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); this->addSelection(kTpcitsProton, trackSelectionNames.at(kTpcitsProton), config.tpcitsProton.value, limits::kUpperLimit, false, false, config.requirePidProton); this->addSelection(kTpctofProton, trackSelectionNames.at(kTpctofProton), config.tpctofProton.value, limits::kUpperLimit, false, false, config.requirePidProton); + this->addComments(kTpctofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); mProtonTofThres = config.minMomTofProton.value; // add selections for Deuteron pid this->addSelection(kItsDeuteron, trackSelectionNames.at(kItsDeuteron), config.itsDeuteron.value, false, false, config.requirePidDeuteron); this->addSelection(kTpcDeuteron, trackSelectionNames.at(kTpcDeuteron), config.tpcDeuteron.value, false, false, config.requirePidDeuteron); this->addSelection(kTofDeuteron, trackSelectionNames.at(kTofDeuteron), config.tofDeuteron.value, false, false, config.requirePidDeuteron); + this->addComments(kTofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); this->addSelection(kTpcitsDeuteron, trackSelectionNames.at(kTpcitsDeuteron), config.tpcitsDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); this->addSelection(kTpctofDeuteron, trackSelectionNames.at(kTpctofDeuteron), config.tpctofDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); + this->addComments(kTpctofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); mDeuteronTofThres = config.minMomTofDeuteron.value; // add selections for Triton pid this->addSelection(kItsTriton, trackSelectionNames.at(kItsTriton), config.itsTriton.value, false, false, config.requirePidTriton); this->addSelection(kTpcTriton, trackSelectionNames.at(kTpcTriton), config.tpcTriton.value, false, false, config.requirePidTriton); this->addSelection(kTofTriton, trackSelectionNames.at(kTofTriton), config.tofTriton.value, false, false, config.requirePidTriton); + this->addComments(kTofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); this->addSelection(kTpcitsTriton, trackSelectionNames.at(kTpcitsTriton), config.tpcitsTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); this->addSelection(kTpctofTriton, trackSelectionNames.at(kTpctofTriton), config.tpctofTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); + this->addComments(kTpctofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); mTritonTofThres = config.minMomTofTriton.value; // add selections for Helium pid this->addSelection(kItsHelium, trackSelectionNames.at(kItsHelium), config.itsHelium.value, false, false, config.requirePidHelium); this->addSelection(kTpcHelium, trackSelectionNames.at(kTpcHelium), config.tpcHelium.value, false, false, config.requirePidHelium); this->addSelection(kTofHelium, trackSelectionNames.at(kTofHelium), config.tofHelium.value, false, false, config.requirePidHelium); + this->addComments(kTofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); this->addSelection(kTpcitsHelium, trackSelectionNames.at(kTpcitsHelium), config.tpcitsHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); this->addSelection(kTpctofHelium, trackSelectionNames.at(kTpctofHelium), config.tpctofHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); + this->addComments(kTpctofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); mHeliumTofThres = config.minMomTofHelium.value; this->setupSelectionHistogram(registry); diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index f2b8f46440e..491a8dee403 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -62,18 +63,18 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { // selections bits for all v0s // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at V0 decay vertex (cm)"}; \ - o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ - o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection. If false, TOF PID is optional"}; \ - o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; +#define V0_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at V0 decay vertex (cm)"}; \ + o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; // derived selection bits for lambda struct ConfLambdaBits : o2::framework::ConfigurableGroup { @@ -256,6 +257,7 @@ class V0Selection : public baseselection::BaseSelectionadd((folderName + "/relPair3dEProtRmTMultPercentileQnPairphi").c_str(), "; DK_{x} (GeV/#it{c}); DK_{y} (GeV/#it{c}); DK_{z} (GeV/#it{c}); #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}", o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } else { + mHistogramRegistry->add((folderName + "/relPair3dRmTMultPercentileQnPairphi").c_str(), ("; " + femtoDKout + femtoDKside + femtoDKlong + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}").c_str(), o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } } template @@ -261,15 +267,23 @@ class FemtoDreamContainer framework::AxisSpec qnAxis = {std::move(qnBins), "qn"}; framework::AxisSpec pairPhiAxis = {std::move(pairPhiBins), "#varphi_{pair} - #Psi_{EP} (rad)"}; + // EP-rotated (B-frame) axis labels, same binning as out/side/long. + framework::AxisSpec DKxAxis = {DKoutBins, "DK_{x} (GeV/#it{c})"}; + framework::AxisSpec DKyAxis = {DKsideBins, "DK_{y} (GeV/#it{c})"}; + framework::AxisSpec DKzAxis = {DKlongBins, "DK_{z} (GeV/#it{c})"}; + framework::AxisSpec& mom1Axis = mStoreEProt ? DKxAxis : DKoutAxis; + framework::AxisSpec& mom2Axis = mStoreEProt ? DKyAxis : DKsideAxis; + framework::AxisSpec& mom3Axis = mStoreEProt ? DKzAxis : DKlongAxis; + std::string folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kRecon]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); if (isMC) { folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); init_3Dqn_MC(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, DKoutAxis, DKsideAxis, DKlongAxis, smearingByOrigin); } @@ -286,6 +300,9 @@ class FemtoDreamContainer mPDGTwo = pdg2; } + /// Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long. Call before init_3Dqn(). + void setStoreEProt(bool doStore) { mStoreEProt = doStore; } + /// Pass a pair to the container and compute all the relevant observables /// Called by setPair both in case of data/ and Monte Carlo reconstructed and for Monte Carlo truth /// \tparam T type of the femtodreamparticle @@ -508,6 +525,16 @@ class FemtoDreamContainer mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dRmTMultPercentileQnPairphi"), femtoDKout, femtoDKside, femtoDKlong, mT, multPercentile, myQnBin, pairPhiEP); } + /// EP-rotated (B-frame) version: rotate (out, side) by the signed φ_pair − Ψ_EP so DK_y is out-of-plane (||B); DK_z unchanged. + template + void setPair_3Dqn_base(const float femtoDKout, const float femtoDKside, const float femtoDKlong, const float mT, const float multPercentile, const float myQnBin, const float pairPhiEP, const float pairPhiEPforRot) + { + const float DKx = femtoDKout * std::cos(pairPhiEPforRot) - femtoDKside * std::sin(pairPhiEPforRot); + const float DKy = femtoDKout * std::sin(pairPhiEPforRot) + femtoDKside * std::cos(pairPhiEPforRot); + const float DKz = femtoDKlong; + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dEProtRmTMultPercentileQnPairphi"), DKx, DKy, DKz, mT, multPercentile, myQnBin, pairPhiEP); + } + /// Called by setPair_3Dqn only in case of Monte Carlo truth /// Fills resolution of DKout, DKside, DKlong void setPair_3Dqn_MC(std::vector k3dMC, std::vector k3d, const int originPart1, const int originPart2, bool smearingByOrigin) @@ -535,9 +562,14 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, eventPlane); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, eventPlane) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { @@ -549,9 +581,14 @@ class FemtoDreamContainer } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -580,9 +617,14 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, EP1, EP2); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, EP1, EP2) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { @@ -594,9 +636,14 @@ class FemtoDreamContainer } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -619,6 +666,7 @@ class FemtoDreamContainer int mPDGOne = 0; ///< PDG code of particle 1 int mPDGTwo = 0; ///< PDG code of particle 2 float mHighkstarCut = 6.; + bool mStoreEProt = false; ///< Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamMath.h b/PWGCF/FemtoDream/Core/femtoDreamMath.h index 2ddc7291818..90a83a9ed72 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamMath.h +++ b/PWGCF/FemtoDream/Core/femtoDreamMath.h @@ -373,6 +373,29 @@ class FemtoDreamMath phi_pair_onPsi = TMath::Abs(phi_pair_onPsi); return phi_pair_onPsi; } + + /// Signed φ_pair − Ψ_EP (same as getPairPhiEP without the final |·|); used as the B-frame rotation angle. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep); + } + + /// Signed counterpart of the plane-calibrated (two-event-plane) getPairPhiEP. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep1, const float Psi_ep2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const float psidiff = Psi_ep2 - Psi_ep1; + const float newPhi2 = TVector2::Phi_mpi_pi(vecpart2.Phi() - psidiff); + const ROOT::Math::PtEtaPhiMVector vecpart2_calibd(vecpart2.Pt(), vecpart2.Eta(), newPhi2, vecpart2.M()); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2_calibd; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep1); + } }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index d0afc20d35e..cb482f58715 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -115,6 +115,7 @@ struct femtoDreamPairTaskTrackTrack { ConfigurableAxis DKlong{"DKlong", {500, -2., 2.}, "binning DKlong for the 3-D femtoscopy plot: R_long(LCMS) vs mT vs multiplicity percentile vs qnBin vs pait phi wrt EP (set <> to true)"}; ConfigurableAxis qnBins{"qnBins", {10, 0, 10}, "binning of qn interval"}; ConfigurableAxis pairPhiBins{"pairPhiBins", {12, 0., TMath::Pi()}, "binning of pair phi"}; + Configurable storeEProt{"storeEProt", false, "Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long in the 3D qn histogram"}; } EPCal; using FilteredCollisions = soa::Filtered; @@ -333,6 +334,8 @@ struct femtoDreamPairTaskTrackTrack { } if (EPCal.do3DFemto) { + sameEventQnCont.setStoreEProt(EPCal.storeEProt); + mixedEventQnCont.setStoreEProt(EPCal.storeEProt); sameEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, Binning4D.mT, Binning4D.multPercentile, Option.IsMC, EPCal.qnBins, EPCal.pairPhiBins, Option.SmearingByOrigin); mixedEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, From 259515a42fdd76ffa85aa2b2691d2a8bed15725a Mon Sep 17 00:00:00 2001 From: Yiping Wang <142303052+ypwangg@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:47:51 +0800 Subject: [PATCH 217/648] [PWGDQ] update dqeff direct (#18191) --- PWGDQ/Core/CutsLibrary.cxx | 18 ++++++++++++++++ PWGDQ/Core/VarManager.h | 3 +++ PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 21 ++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 0974eab27a4..50fdb3b019b 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -3660,6 +3660,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (!nameStr.compare("pairX3872Minitree")) { + cut->AddCut(GetAnalysisCut("pairX3872_minitree")); + return cut; + } + if (nameStr == "DipionPairCut1") { cut->AddCut(GetAnalysisCut("DipionMassCut1")); return cut; @@ -7124,6 +7129,19 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + std::shared_ptr fDeltaR2High = std::make_shared("fDeltaR2High", "[0]+[1]*x", 0.0, 5.0); + fDeltaR2High->SetParameters(3.0, -0.4); + if (!nameStr.compare("pairX3872_minitree")) { + cut->AddCut(VarManager::kRap, -0.8, 0.8); + cut->AddCut(VarManager::kQuadDefaultDileptonMass, 3.0, 5.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, 1.0, false, VarManager::kPt, 5.0, 1000.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, fDeltaR2High, false, VarManager::kPt, 0.0, 5.0); + cut->AddCut(VarManager::kQuadPt, 0.0, 1000.0); + cut->AddCut(VarManager::kVertexingProcCode, 0.5, 2.5); + cut->AddCut(VarManager::kVertexingQuadProcCode, 0.5, 2.5); + return cut; + } + if (nameStr == "pairPtLow1") { cut->AddCut(VarManager::kPt, 2.0, 1000.0); return cut; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index c7eaf3b6edf..ed09b758655 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -1059,6 +1059,7 @@ class VarManager : public TObject kDeltaR1, kDeltaR2, kDeltaR, + kVertexingQuadProcCode, // DQ-HF correlation variables kMassCharmHadron, @@ -6782,6 +6783,8 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l // o2::track::TrackParCov parsDilepton = VarManager::fgFitterTwoProngBarrel.createParentTrackParCov(0); // procCodeDileptonTrackTrack = VarManager::fgFitterThreeProngBarrel.process(parsDilepton, pars3, pars4); procCodeDileptonTrackTrack = VarManager::fgFitterFourProngBarrel.process(pars1, pars2, pars3, pars4); + values[kVertexingProcCode] = procCodeDilepton; + values[kVertexingQuadProcCode] = procCodeDileptonTrackTrack; // fill values if (procCodeDilepton == 0 || procCodeDileptonTrackTrack == 0) { diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index 54e22436ad8..c35430d09dc 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -3321,6 +3321,17 @@ struct AnalysisDileptonTrackTrack { if (!cfgTrackSelection_TrackCuts.empty()) { cfgTrackSelection_objArrayTrackCuts = TString(cfgTrackSelection_TrackCuts).Tokenize(","); } + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); + if (!cfgTrackSelection_TrackCuts.empty()) { + if (cfgTrackSelection_objArrayTrackCuts == nullptr) { + cfgTrackSelection_objArrayTrackCuts = new TObjArray(); + } + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); + for (const auto& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); + cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); + } + } for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { TString cutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); fTrackCutNames.push_back(cutName); @@ -3721,7 +3732,7 @@ struct AnalysisDileptonTrackTrack { PROCESS_SWITCH(AnalysisDileptonTrackTrack, processX3872, "Run X(3872) -> J/psi + pi+ pi- pairing, using skimmed data", false); PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms with event selection", false); - PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) @@ -3824,6 +3835,10 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con // dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_track"); // } + if (classStr.Contains("MCTruthGenQuad")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_quad", histName); + } + if (classStr.Contains("DileptonsSelected")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); } @@ -3847,5 +3862,9 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con if (classStr.Contains("DileptonHadronCorrelation")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-correlation"); } + + if (classStr.Contains("Quadruplet")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-dihadron", histName); + } } // end loop over histogram classes } From 435a20e7536a7d2bc8a99a5e7793c13312aee6a0 Mon Sep 17 00:00:00 2001 From: m-rasa <99010072+m-rasa@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:37:20 +0200 Subject: [PATCH 218/648] [PWGUD] Add deuteron in UPC task (#18153) Co-authored-by: ALICE Action Bot --- PWGUD/Tasks/CMakeLists.txt | 5 + PWGUD/Tasks/sgDeuteronSpectra.cxx | 166 ++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 PWGUD/Tasks/sgDeuteronSpectra.cxx diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index bfe050bbe04..55f69f53d26 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -303,3 +303,8 @@ o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(sg-deuteron-spectra + SOURCES sgDeuteronSpectra.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGUD/Tasks/sgDeuteronSpectra.cxx b/PWGUD/Tasks/sgDeuteronSpectra.cxx new file mode 100644 index 00000000000..93c697908f8 --- /dev/null +++ b/PWGUD/Tasks/sgDeuteronSpectra.cxx @@ -0,0 +1,166 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file sgDeuteronSpectra.cxx +/// \brief Analysis for the (anti)deuteron production in UPC +/// \author Marika Rasa +/// \since September 2026 + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/SGTrackSelector.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct SgDeuteronSpectra { + // UPC cuts + SGSelector sgSelector; + Configurable fv0Cut{"fv0Cut", 50., "FV0A threshold"}; + Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; + Configurable ft0aCut{"ft0aCut", 150., "FT0A threshold"}; + Configurable ft0cCut{"ft0cCut", 50., "FT0C threshold"}; + Configurable fddaCut{"fddaCut", 10000., "FDDA threshold"}; + Configurable fddcCut{"fddcCut", 10000., "FDDC threshold"}; + + // Track cuts + Configurable pvCut{"pvCut", 1.0, "Use Only PV tracks"}; + Configurable dcaZCut{"dcaZCut", 2.0, "dcaZ cut"}; + Configurable dcaXYCut{"dcaXYCut", 0.0, "dcaXY cut (0 for Pt-function)"}; + Configurable tpcChi2Cut{"tpcChi2Cut", 4, "Max tpcChi2NCl"}; + Configurable tpcNClsFindableCut{"tpcNClsFindableCut", 70, "Min tpcNClsFindable"}; + Configurable itsChi2Cut{"itsChi2Cut", 36, "Max itsChi2NCl"}; + Configurable etaCut{"etaCut", 0.9, "Track Pseudorapidity"}; + Configurable ptCut{"ptCut", 0.1, "Track Pt"}; + Configurable nsigmatpcPreselCut{"nsigmatpcPreselCut", 3.0, "nsigma TPC preselection cut for TOF analysis"}; + + // Configurable axis for histograms + ConfigurableAxis ptAxis{"ptAxis", {200, 0.0, 10.0}, "p_{T}"}; + ConfigurableAxis nSigmaAxis{"nSigmaAxis", {800, -20.0, 20.0}, "nSigma axis for TPC and TOF"}; + + // Initialize histogram registry + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + const AxisSpec axispt{ptAxis, "p_{T}"}; + const AxisSpec axistpc{nSigmaAxis, "n#sigma_{TPC}"}; + const AxisSpec axistof{nSigmaAxis, "n#sigma_{TOF}"}; + + // Collision histograms + registry.add("collisions/GapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + registry.add("collisions/TrueGapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{4, -1.5, 2.5}}}); + registry.add("tracks/Deut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + } + + // Define data types + using UDCollisionsFull = soa::Join; // UDCollisions + using UDCollisionFull = UDCollisionsFull::iterator; + using UDTracksFull = soa::Join; + + void process(UDCollisionFull const& coll, UDTracksFull const& tracks) + { + registry.fill(HIST("collisions/GapSide"), coll.gapSide(), 1.); + std::vector fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + int truegapSide = sgSelector.trueGap(coll, fitCut[0], fitCut[1], fitCut[2], zdcCut); + registry.fill(HIST("collisions/TrueGapSide"), truegapSide, 1.); + + std::vector parameters = {pvCut, dcaZCut, dcaXYCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + for (const auto& t : tracks) { + if (trackselector(t, parameters) != 0) { + if (truegapSide == o2::aod::sgselector::SingleGapA) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::SingleGapC) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::DoubleGap) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} From 19b962fd68433640c962935b6b9ea402e186fe57 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Thu, 8 Oct 2026 13:29:30 +0530 Subject: [PATCH 219/648] [PWGCF] LambdaProtonBalanceFunction-Update 5 (#18206) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 458 ++++++------------ 1 file changed, 157 insertions(+), 301 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index f6b7b71026f..8fe2fd62647 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -76,12 +76,8 @@ float displayDeltaPhi(float dPhi) // Per-centrality-bin histogram set. Defined at file scope (NOT inside the task struct), // otherwise O2's struct reflection in adaptAnalysisTask fails to compile. struct CentRho2Set { - // eta space (only booked if cFillEtaSpace && cFillCentEtaSpace) - std::shared_ptr Lp, LAp, ALp, ALAp, pp, pAp, App, ApAp, LL, LAL, ALL, ALAL; // y space std::shared_ptr Lp_y, LAp_y, ALp_y, ALAp_y, pp_y, pAp_y, App_y, ApAp_y, LL_y, LAL_y, ALL_y, ALAL_y; - // rho1 eta - std::shared_ptr rho1_Proton, rho1_AntiProton, rho1_Lambda, rho1_AntiLambda; // rho1 y std::shared_ptr rho1_Proton_y, rho1_AntiProton_y, rho1_Lambda_y, rho1_AntiLambda_y; // event counter @@ -124,7 +120,7 @@ struct LambdaProtonBalanceFunction { // ConfigurableAxis, Filter, HistogramRegistry): those must remain top-level. // ════════════════════════════════════════════════════════════════════════ struct TaskState { - // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin rho2 / mass histograms) + // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin pT-spectra / mass histograms) std::vector ptEdges; // ════════════════════════════════════════════════════════════════════════ @@ -147,32 +143,8 @@ struct LambdaProtonBalanceFunction { std::vector>> hMassAntiLambdaExtended; // ════════════════════════════════════════════════════════════════════════ - // SECTION B — rho2 eta-space histograms - // Naming: hRho2_ = per-pT-bin vector - // hRho2__AllPt = merged (full pT range as - // set by the pT cuts: protons 0.5-3.6, Lambdas 0.6-3.6 GeV/c; - // "015" is a legacy name and does NOT reflect the actual lower cut) - // ════════════════════════════════════════════════════════════════════════ - // Lambda–proton family - std::shared_ptr hRho2_Lp_AllPt; - std::shared_ptr hRho2_LAp_AllPt; - std::shared_ptr hRho2_ALp_AllPt; - std::shared_ptr hRho2_ALAp_AllPt; - // Proton–proton family - std::shared_ptr hRho2_pp_AllPt; - std::shared_ptr hRho2_pAp_AllPt; - std::shared_ptr hRho2_App_AllPt; - std::shared_ptr hRho2_ApAp_AllPt; - // Lambda–Lambda family - std::shared_ptr hRho2_LL_AllPt; - std::shared_ptr hRho2_LAL_AllPt; - std::shared_ptr hRho2_ALL_AllPt; - std::shared_ptr hRho2_ALAL_AllPt; - - // ════════════════════════════════════════════════════════════════════════ - // SECTION C — rho2 rapidity-space histograms - // Same channels as Section B but filled with unrolledIndexY(y, phi) - // Naming: hRho2__y / hRho2__y_AllPt + // SECTION C — rho2 rapidity-space histograms, filled with unrolledIndexY(y, phi). + // Naming: hRho2__y_AllPt. // ════════════════════════════════════════════════════════════════════════ // Lambda–proton family (y) std::shared_ptr hRho2_Lp_y_AllPt; @@ -224,13 +196,9 @@ struct LambdaProtonBalanceFunction { // Runtime copies populated from Configurables in init(). // Not static, not constexpr — values are known only after O2 delivers the config. - int mRhoEtaBins = 0; int mRhoPhiBins = 0; int mRhoYBins = 0; - int mRhoUnrolledBins = 0; // mRhoEtaBins * mRhoPhiBins int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins - float mRhoEtaMin = 0.f; - float mRhoEtaMax = 0.f; float mRhoYMin = 0.f; float mRhoYMax = 0.f; }; @@ -242,7 +210,6 @@ struct LambdaProtonBalanceFunction { // ── Configurables ───────────────────────────────────────────────────── Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; - Configurable cFillEtaSpace{"cFillEtaSpace", false, "Fill eta-space rho1/rho2 histograms"}; Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; Configurable cQinvCutValue{"cQinvCutValue", 0.01f, "q_inv cut value (GeV/c)"}; Configurable cQinvQABins{"cQinvQABins", 6000, "Number of bins for QA q_inv histograms"}; @@ -320,12 +287,9 @@ struct LambdaProtonBalanceFunction { Configurable cV0DauMinTpcCrossedRows{"cV0DauMinTpcCrossedRows", 70, "Daughter TPC min crossed rows"}; // ── Rho1/Rho2 grid — configurable at runtime (Hyperloop-friendly) ──────── // These replace the former static constexpr k* constants. Default values - // reproduce the original hardcoded grid (eta: 40×72, y: 10×72). - Configurable cRhoEtaBins{"cRhoEtaBins", 40, "Number of eta bins for rho1/rho2"}; + // reproduce the original hardcoded grid (y: 10×72). Configurable cRhoPhiBins{"cRhoPhiBins", 72, "Number of phi bins for rho1/rho2"}; Configurable cRhoYBins{"cRhoYBins", 10, "Number of y bins for rho1/rho2"}; - Configurable cRhoEtaMin{"cRhoEtaMin", -0.5f, "Min eta for rho1/rho2"}; - Configurable cRhoEtaMax{"cRhoEtaMax", 0.5f, "Max eta for rho1/rho2"}; Configurable cRhoYMin{"cRhoYMin", -0.5f, "Min y for rho1/rho2"}; Configurable cRhoYMax{"cRhoYMax", 0.5f, "Max y for rho1/rho2"}; // ───────────────────────────────────────────────────────────────────────── @@ -341,15 +305,6 @@ struct LambdaProtonBalanceFunction { } } - int etaBinIndex(float eta) const - { - if (eta < state.mRhoEtaMin || eta >= state.mRhoEtaMax) { - return -1; - } - const float binWidth = (state.mRhoEtaMax - state.mRhoEtaMin) / state.mRhoEtaBins; - return static_cast((eta - state.mRhoEtaMin) / binWidth); - } - int phiBinIndex(float phi) const { constexpr float phiMin = 0.0f; @@ -361,16 +316,6 @@ struct LambdaProtonBalanceFunction { return static_cast((phi - phiMin) / binWidth); } - int unrolledIndex(float eta, float phi) const - { - const int iEta = etaBinIndex(eta); - const int iPhi = phiBinIndex(phi); - if (iEta < 0 || iPhi < 0) { - return -1; - } - return iEta * state.mRhoPhiBins + iPhi; - } - int yBinIndex(float y) const { if (y < state.mRhoYMin || y >= state.mRhoYMax) { @@ -444,6 +389,11 @@ struct LambdaProtonBalanceFunction { return cUseQinvCut.value && (qinv <= cQinvCutValue.value); } + // QA-only: true => pair would survive the q_inv cut, regardless of the cUseQinvCut master switch. + // Used ONLY for the "After_qinvCut" QA histograms so they always show the mathematical effect of the cut. + // Physics logic must keep using failsQinvCut(). + bool passesQinvQA(float qinv) const { return qinv > cQinvCutValue.value; } + // Returns the active centrality estimator's percentile for this collision, // selected at runtime via cUseFT0C (false = FT0M, true = FT0C). template @@ -491,6 +441,30 @@ struct LambdaProtonBalanceFunction { return std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value; } + // Bitmask of contaminant hypotheses that REJECT the track (bit0 = pi, bit1 = K, bit2 = e). + // A hypothesis rejects only if the track is inside its veto window AND closer to it than to the proton. + // Detector: TOF at/above the TPC/TOF switch momentum when TOF is available, TPC otherwise. + template + int contaminantVetoMask(TTrack const& trk, bool& usedTOF) const + { + usedTOF = cVetoUseTOF.value && trk.hasTOF() && trk.tpcInnerParam() >= pProtonTPCTOFSwitchP.value; + const float nsP = std::abs(usedTOF ? trk.tofNSigmaPr() : trk.tpcNSigmaPr()); + const float nsPi = std::abs(usedTOF ? trk.tofNSigmaPi() : trk.tpcNSigmaPi()); + const float nsK = std::abs(usedTOF ? trk.tofNSigmaKa() : trk.tpcNSigmaKa()); + const float nsE = std::abs(usedTOF ? trk.tofNSigmaEl() : trk.tpcNSigmaEl()); + int mask = 0; + if (nsPi < pProtonVetoPionNsigma.value && nsPi < nsP) { + mask |= 1; + } + if (nsK < pProtonVetoKaonNsigma.value && nsK < nsP) { + mask |= 2; + } + if (nsE < pProtonVetoElectronNsigma.value && nsE < nsP) { + mask |= 4; + } + return mask; + } + // true => track survives the pi/K/e contaminant veto template bool passesContaminantVeto(TTrack const& trk) const @@ -498,19 +472,27 @@ struct LambdaProtonBalanceFunction { if (!cUseContaminantVeto.value) { return true; } - if (std::abs(trk.tpcNSigmaPi()) < pProtonVetoPionNsigma.value || - std::abs(trk.tpcNSigmaKa()) < pProtonVetoKaonNsigma.value || - std::abs(trk.tpcNSigmaEl()) < pProtonVetoElectronNsigma.value) { - return false; - } - if (cVetoUseTOF.value && trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPi()) < pProtonVetoPionNsigma.value || - std::abs(trk.tofNSigmaKa()) < pProtonVetoKaonNsigma.value || - std::abs(trk.tofNSigmaEl()) < pProtonVetoElectronNsigma.value) { - return false; - } + bool usedTOF = false; + return contaminantVetoMask(trk, usedTOF) == 0; + } + + // 2D cutflow vs pT only (used directly for the veto-reason bins 16-21) + template + void fillCutFlow2D(float stage, TTrack const& trk) + { + if (trk.sign() > 0) { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton"), trk.pt(), stage); + } else { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton"), trk.pt(), stage); } - return true; + } + + // 1D hCutFlow + 2D cutflow (sequential stages 1-15) + template + void fillCutFlow(float stage, TTrack const& trk) + { + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), stage); + fillCutFlow2D(stage, trk); } // ───────────────────────────────────────────────────────────────────── @@ -777,6 +759,8 @@ struct LambdaProtonBalanceFunction { const float pt = trk.pt(); const float beta = trk.beta(); const float nsTOF = trk.tofNSigmaPr(); + const float nsTPC = trk.tpcNSigmaPr(); + const float radius = std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF); if (stage == ProtonQAStage::RawAfterTrackSel) { if constexpr (IsProton) { registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); @@ -787,6 +771,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTPC"), pTPC, beta); @@ -796,6 +782,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); } } else if (stage == ProtonQAStage::TpcPid) { if constexpr (IsProton) { @@ -807,6 +795,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTPC"), pTPC, beta); @@ -816,6 +806,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); } } else if (stage == ProtonQAStage::TpcTofPid) { if constexpr (IsProton) { @@ -827,6 +819,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTPC"), pTPC, beta); @@ -836,6 +830,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); } } else { // FinalSelected if constexpr (IsProton) { @@ -847,6 +843,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); } else { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_p"), p, beta); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTPC"), pTPC, beta); @@ -856,6 +854,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); } } } @@ -1200,8 +1200,7 @@ struct LambdaProtonBalanceFunction { struct PartInfo { int64_t gid = 0; float px = 0.f, py = 0.f, pz = 0.f, E = 0.f; - float eta = 0.f, phi = 0.f, y = 0.f, pt = 0.f; - int idxEta = -1; + float phi = 0.f, y = 0.f, pt = 0.f; int idxY = -1; }; @@ -1214,11 +1213,9 @@ struct LambdaProtonBalanceFunction { p.py = t.py(); p.pz = t.pz(); p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + kMassProton * kMassProton); - p.eta = t.eta(); p.phi = t.phi(); p.pt = t.pt(); p.y = protonRapidity(t); - p.idxEta = unrolledIndex(p.eta, p.phi); p.idxY = unrolledIndexY(p.y, p.phi); return p; } @@ -1226,14 +1223,10 @@ struct LambdaProtonBalanceFunction { void init(InitContext const&) { // ── Populate rho1/rho2 grid members from Configurables ────────────────── - state.mRhoEtaBins = cRhoEtaBins.value; state.mRhoPhiBins = cRhoPhiBins.value; state.mRhoYBins = cRhoYBins.value; - state.mRhoEtaMin = cRhoEtaMin.value; - state.mRhoEtaMax = cRhoEtaMax.value; state.mRhoYMin = cRhoYMin.value; state.mRhoYMax = cRhoYMax.value; - state.mRhoUnrolledBins = state.mRhoEtaBins * state.mRhoPhiBins; state.mRhoUnrolledBinsY = state.mRhoYBins * state.mRhoPhiBins; // ──────────────────────────────────────────────────────────────────────── @@ -1260,9 +1253,7 @@ struct LambdaProtonBalanceFunction { const std::string centLabel = cUseFT0C.value ? "FT0C" : "FT0M"; const std::string centAxisTitle = centLabel + " Percentile (%)"; AxisSpec multAxis = {100, 0.0f, 100.0f, centAxisTitle}; - AxisSpec etaAxis = {state.mRhoEtaBins, state.mRhoEtaMin, state.mRhoEtaMax, "#eta"}; AxisSpec phiAxis = {state.mRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; - AxisSpec unrolledAxis = {state.mRhoUnrolledBins, 0.0f, static_cast(state.mRhoUnrolledBins), "index(#eta,#varphi)"}; AxisSpec unrolledAxisY = {state.mRhoUnrolledBinsY, 0.0f, static_cast(state.mRhoUnrolledBinsY), "index(y,#varphi)"}; AxisSpec rapidityAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; AxisSpec yAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; @@ -1344,6 +1335,14 @@ struct LambdaProtonBalanceFunction { registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTPC", species, stage), "TOF n#sigma_{p} vs p_{TPC};p_{TPC} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTPC, nsigmaAxis}}); registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pt", species, stage), "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTOF", species, stage), "TOF n#sigma_{p} vs p_{TOF};p_{TOF} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTOF, nsigmaAxis}}); + + // --- 2D TPC vs TOF n-sigma and 1D circular radius (competitive-veto vs circle comparison) --- + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaTPC_vs_nsigmaTOF", species, stage), + "TPC vs TOF n#sigma_{p};n#sigma^{TPC}_{p};n#sigma^{TOF}_{p}", + {HistType::kTH2F, {nsigmaAxis, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h1f_circular_radius", species, stage), + "Circular PID Radius;R = #sqrt{(n#sigma^{TPC}_{p})^{2} + (n#sigma^{TOF}_{p})^{2}};Counts", + {HistType::kTH1F, {{100, 0.0f, 10.0f, "R"}}}); }; static constexpr std::array localProtonStages = { @@ -1465,6 +1464,23 @@ struct LambdaProtonBalanceFunction { hCF->GetXaxis()->SetBinLabel(15, "Selected"); } + // 2D cutflow vs pT, separate for protons and antiprotons. + // y bins 1-15 = sequential stages (same as hCutFlow); 16-21 = veto REASONS (not sequential, a track can fill several) + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton", "Proton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton", "AntiProton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + { + const char* cfLabels2D[21] = {"All tracks", "hasTPC", "TPC crossed rows", "TPC rows/findable", "TPC chi2", "ITS Ncls", "ITS chi2", + "TPC signal", "p_TPC & pT window", "|y| window", "DCA xy,z", "PID (TPC/TOF)", "pi/K/e veto", + "V0-daughter veto", "Selected", "veto: TPC pi", "veto: TPC K", "veto: TPC e", + "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; + auto hCFp = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton")); + auto hCFa = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton")); + for (int ib = 0; ib < 21; ++ib) { + hCFp->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + hCFa->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + } + } + // ══════════════════════════════════════════════════════════════════════════ // EXISTING QA_Detec REGISTRATION (Preserved for backward compatibility) // ══════════════════════════════════════════════════════════════════════════ @@ -1508,7 +1524,6 @@ struct LambdaProtonBalanceFunction { "Proton V0-daughter veto QA: fraction_{removed} = N_{removed}/N_{before} per event;fraction_{removed};Events", {HistType::kTH1F, {{110, 0.0f, 1.1f}}}); // ───────────────────────────────────────────────────────────────────── - // ───────────────────────────────────────────────────────────────────── // ── Item 5: Event-level pileup rejection QA ─────────────────────────── // hPileupFlags: each bin corresponds to a different event-selection bit. @@ -1554,11 +1569,6 @@ struct LambdaProtonBalanceFunction { // ── Rho histograms ─────────────────────────────────────────────────── registryRho.add("hEventCounter", "Events", {HistType::kTH1F, {{1, 0.0, 1.0}}}); - // eta-based rho1 - registryRho.add("h2_rho1_Proton", "#rho_{1}(p) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiProton", "#rho_{1}(#bar{p}) in (#eta,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_Lambda", "#rho_{1}(#Lambda) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); - registryRho.add("h2_rho1_AntiLambda", "#rho_{1}(#bar{#Lambda}) in (#eta,#varphi)", {HistType::kTH2F, {etaAxis, phiAxis}}); // rapidity-based rho1 registryRho.add("h2_rho1_Proton_y", "#rho_{1}(p) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); registryRho.add("h2_rho1_AntiProton_y", "#rho_{1}(#bar{p}) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); @@ -1862,58 +1872,8 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // ── Merged / AllPt histograms ─────────────────────────────────── - state.hRho2_Lp_AllPt = - registryRho.add("h2_rho2_Lp_AllPt", - "#rho_{2}(#Lambda, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_LAp_AllPt = - registryRho.add("h2_rho2_LAp_AllPt", - "#rho_{2}(#Lambda, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_ALp_AllPt = - registryRho.add("h2_rho2_ALp_AllPt", - "#rho_{2}(#bar{#Lambda}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_ALAp_AllPt = - registryRho.add("h2_rho2_ALAp_AllPt", - "#rho_{2}(#bar{#Lambda}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_pp_AllPt = - registryRho.add("h2_rho2_pp_AllPt", - "#rho_{2}(p, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_pAp_AllPt = - registryRho.add("h2_rho2_pAp_AllPt", - "#rho_{2}(p, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of p (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_App_AllPt = - registryRho.add("h2_rho2_App_AllPt", - "#rho_{2}(#bar{p}, p) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of p (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_ApAp_AllPt = - registryRho.add("h2_rho2_ApAp_AllPt", - "#rho_{2}(#bar{p}, #bar{p}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{p} (trigger);index(#eta,#varphi) of #bar{p} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_LL_AllPt = - registryRho.add("h2_rho2_LL_AllPt", - "#rho_{2}(#Lambda, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #Lambda (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_LAL_AllPt = - registryRho.add("h2_rho2_LAL_AllPt", - "#rho_{2}(#Lambda, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #Lambda (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_ALL_AllPt = - registryRho.add("h2_rho2_ALL_AllPt", - "#rho_{2}(#bar{#Lambda}, #Lambda) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #Lambda (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - state.hRho2_ALAL_AllPt = - registryRho.add("h2_rho2_ALAL_AllPt", - "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (#eta,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(#eta,#varphi) of #bar{#Lambda} (trigger);index(#eta,#varphi) of #bar{#Lambda} (associate)", - {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - // ── Rapidity merged rho2 histograms ───────────────────────────────────── // NOTE: these must use unrolledAxisY (mRhoYBins*mRhoPhiBins bins), - // NOT the eta-space unrolledAxis (mRhoEtaBins*mRhoPhiBins bins). // GetUnrolledIndexY() below produces indices in [0, mRhoUnrolledBinsY), // so the booked axis must match that range or downstream R2/BF code that // expects y-space-sized histograms will see a dimension mismatch. @@ -1970,7 +1930,7 @@ struct LambdaProtonBalanceFunction { {HistType::kTH1F, {lambdaMassAxis}}); // ───────────────────────────────────────────────────────────────────── - // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 6.0) ───────────────── + // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 3.6) ───────────────── state.hMassLambdaPtBins.clear(); state.hMassAntiLambdaPtBins.clear(); state.hPtPrimProton_Lambda.clear(); @@ -2125,30 +2085,6 @@ struct LambdaProtonBalanceFunction { addY(S.LAL_y, "h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); addY(S.ALL_y, "h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); addY(S.ALAL_y, "h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); - - // ── eta space (only if BOTH switches on: memory heavy) ── - if (cFillEtaSpace.value) { - S.rho1_Proton = registryCent.add((dir + "/h2_rho1_Proton").c_str(), ("#rho_{1}(p) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_AntiProton = registryCent.add((dir + "/h2_rho1_AntiProton").c_str(), ("#rho_{1}(#bar{p}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_Lambda = registryCent.add((dir + "/h2_rho1_Lambda").c_str(), ("#rho_{1}(#Lambda) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - S.rho1_AntiLambda = registryCent.add((dir + "/h2_rho1_AntiLambda").c_str(), ("#rho_{1}(#bar{#Lambda}) in (#eta,#varphi), " + ctag).c_str(), {HistType::kTH2F, {etaAxis, phiAxis}}); - - auto addEta = [&](std::shared_ptr& h, const char* name, const char* title) { - h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxis, unrolledAxis}}); - }; - addEta(S.Lp, "h2_rho2_Lp_AllPt", "#rho_{2}(#Lambda,p) (#eta,#varphi)"); - addEta(S.LAp, "h2_rho2_LAp_AllPt", "#rho_{2}(#Lambda,#bar{p}) (#eta,#varphi)"); - addEta(S.ALp, "h2_rho2_ALp_AllPt", "#rho_{2}(#bar{#Lambda},p) (#eta,#varphi)"); - addEta(S.ALAp, "h2_rho2_ALAp_AllPt", "#rho_{2}(#bar{#Lambda},#bar{p}) (#eta,#varphi)"); - addEta(S.pp, "h2_rho2_pp_AllPt", "#rho_{2}(p,p) (#eta,#varphi)"); - addEta(S.pAp, "h2_rho2_pAp_AllPt", "#rho_{2}(p,#bar{p}) (#eta,#varphi)"); - addEta(S.App, "h2_rho2_App_AllPt", "#rho_{2}(#bar{p},p) (#eta,#varphi)"); - addEta(S.ApAp, "h2_rho2_ApAp_AllPt", "#rho_{2}(#bar{p},#bar{p}) (#eta,#varphi)"); - addEta(S.LL, "h2_rho2_LL_AllPt", "#rho_{2}(#Lambda,#Lambda) (#eta,#varphi)"); - addEta(S.LAL, "h2_rho2_LAL_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (#eta,#varphi)"); - addEta(S.ALL, "h2_rho2_ALL_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (#eta,#varphi)"); - addEta(S.ALAL, "h2_rho2_ALAL_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (#eta,#varphi)"); - } } } } @@ -2191,9 +2127,7 @@ struct LambdaProtonBalanceFunction { // ALAp : trigger=AntiLambda,assoc=antiproton // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) // - // Each channel has both eta-space (unrolledIndex) and - // rapidity-space (unrolledIndexY) versions filled in the same loop; the - // eta-space fills are gated by cFillEtaSpace. + // Every channel fills y-space only (unrolledIndexY). // ───────────────────────────────────────────────────────────────────── // ── Event cutflow bin 1 — enters process (after sel8 + posZ filters) ── @@ -2218,7 +2152,6 @@ struct LambdaProtonBalanceFunction { for (auto const& trk : tracks) { if (std::abs(trk.eta()) < kMaxInelEta) { ++nTracksINEL; - continue; } } if (nTracksINEL < 1) { @@ -2570,15 +2503,9 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, v0PosDau); fillDaughterQA(DaughterQAStage::Final, v0NegDau); - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_Lambda"), v0.eta(), v0.phi()); - } registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); if (cs) { cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); - if (cs->rho1_Lambda) { - cs->rho1_Lambda->Fill(v0.eta(), v0.phi()); - } } registryOther.fill(HIST("hPtSelLambda"), pt); state.hPtSelLambda_AllPt->Fill(pt); @@ -2594,15 +2521,9 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, v0NegDau); fillDaughterQA(DaughterQAStage::Final, v0PosDau); - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_AntiLambda"), v0.eta(), v0.phi()); - } registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); if (cs) { cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); - if (cs->rho1_AntiLambda) { - cs->rho1_AntiLambda->Fill(v0.eta(), v0.phi()); - } } registryOther.fill(HIST("hPtSelAntiLambda"), pt); state.hPtSelAntiLambda_AllPt->Fill(pt); @@ -2639,35 +2560,35 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────── // ── Track quality cuts ──────────────────────────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 1.0f); + fillCutFlow(1.0f, trk); if (!trk.hasTPC()) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 2.0f); + fillCutFlow(2.0f, trk); if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 3.0f); + fillCutFlow(3.0f, trk); if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 4.0f); + fillCutFlow(4.0f, trk); if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 5.0f); + fillCutFlow(5.0f, trk); if (trk.itsNCls() < pProtonITSMinClusters.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 6.0f); + fillCutFlow(6.0f, trk); if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 7.0f); + fillCutFlow(7.0f, trk); if (trk.tpcSignal() < cPPMinTpcSignal.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 8.0f); + fillCutFlow(8.0f, trk); // ───────────────────────────────────────────────────────────────── // ── p_TPC and pT acceptance cuts ───────────────────────────────────────────── @@ -2678,7 +2599,7 @@ struct LambdaProtonBalanceFunction { if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 9.0f); + fillCutFlow(9.0f, trk); // ───────────────────────────────────────────────────────────────── // ── Proton kinematic acceptance ─────────────────────────────────── @@ -2688,7 +2609,7 @@ struct LambdaProtonBalanceFunction { if (std::abs(y) > pProtonMaxY.value) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 10.0f); + fillCutFlow(10.0f, trk); // ───────────────────────────────────────────────────────────────── // ── DCA cuts ───────────────────────────────────────────────────── const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && @@ -2696,7 +2617,7 @@ struct LambdaProtonBalanceFunction { if (!passDCA) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 11.0f); + fillCutFlow(11.0f, trk); // ───────────────────────────────────────────────────────────────── // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) @@ -2757,13 +2678,20 @@ struct LambdaProtonBalanceFunction { if (!passesPrimProtonPid(trk)) { continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 12.0f); + fillCutFlow(12.0f, trk); // pi/K/e contaminant veto if (!passesContaminantVeto(trk)) { + bool vetoUsedTOF = false; + const int vetoMask = contaminantVetoMask(trk, vetoUsedTOF); + for (int ib = 0; ib < 3; ++ib) { + if (vetoMask & (1 << ib)) { + fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); + } + } continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 13.0f); + fillCutFlow(13.0f, trk); // TOF Matching Fraction check on selected protons registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); @@ -2818,11 +2746,11 @@ struct LambdaProtonBalanceFunction { } continue; } - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 14.0f); + fillCutFlow(14.0f, trk); // ───────────────────────────────────────────────────────────────── // ── Classify into proton / antiproton lists ─────────────────────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), 15.0f); + fillCutFlow(15.0f, trk); // VS Code: commented out unused variables. // const float pz = trk.pt() * std::sinh(trk.eta()); // const float eta = trk.eta(); @@ -2891,31 +2819,19 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - // ── Single-particle density for protons (eta and y) ────────────────── + // ── Single-particle density for protons (y) ────────────────── for (auto const& primProton : selectedPrimProtons) { - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_Proton"), primProton.eta(), primProton.phi()); - } const float yp = protonRapidity(primProton); registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi()); if (cs) { cs->rho1_Proton_y->Fill(yp, primProton.phi()); - if (cs->rho1_Proton) { - cs->rho1_Proton->Fill(primProton.eta(), primProton.phi()); - } } } for (auto const& primAntiProton : selectedPrimAntiProtons) { - if (cFillEtaSpace.value) { - registryRho.fill(HIST("h2_rho1_AntiProton"), primAntiProton.eta(), primAntiProton.phi()); - } const float yap = protonRapidity(primAntiProton); registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi()); if (cs) { cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi()); - if (cs->rho1_AntiProton) { - cs->rho1_AntiProton->Fill(primAntiProton.eta(), primAntiProton.phi()); - } } } // ───────────────────────────────────────────────────────────────────── @@ -2935,7 +2851,7 @@ struct LambdaProtonBalanceFunction { std::back_inserter(antiProtonInfo), [this](auto const& t) { return makeProtonInfo(t); }); - // ── pp rho2 fills (eta and y) ─────────────────────────────────────────── + // ── pp rho2 fills (y) ─────────────────────────────────────────────────── for (auto const& p1 : selectedPrimProtons) { const float y1_pp = protonRapidity(p1); for (auto const& p2 : selectedPrimProtons) { @@ -2963,7 +2879,7 @@ struct LambdaProtonBalanceFunction { const float E2 = std::sqrt(p2mag * p2mag + kMassProton * kMassProton); const float qinvPP = computeQinv(p1.px(), p1.py(), p1.pz(), E1, p2.px(), p2.py(), p2.pz(), E2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut"), qinvPP); - if (!failsQinvCut(qinvPP)) { + if (passesQinvQA(qinvPP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut"), qinvPP); } } @@ -2995,20 +2911,14 @@ struct LambdaProtonBalanceFunction { } registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 4); } - const int idx1 = unrolledIndex(p1.eta(), p1.phi()); - const int idx2 = unrolledIndex(p2.eta(), p2.phi()); const float y2_pp = protonRapidity(p2); const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - state.hRho2_pp_AllPt->Fill(idx1, idx2); - } if (idxY1 >= 0 && idxY2 >= 0) { state.hRho2_pp_y_AllPt->Fill(idxY1, idxY2); } if (cs) { - fillIdx(cs->pp, idx1, idx2); fillIdx(cs->pp_y, idxY1, idxY2); } } @@ -3017,18 +2927,16 @@ struct LambdaProtonBalanceFunction { for (auto const& b : antiProtonInfo) { const float qinvPAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut"), qinvPAp); + if (passesQinvQA(qinvPAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); + } if (failsQinvCut(qinvPAp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - state.hRho2_pAp_AllPt->Fill(a.idxEta, b.idxEta); - } if (a.idxY >= 0 && b.idxY >= 0) { state.hRho2_pAp_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { - fillIdx(cs->pAp, a.idxEta, b.idxEta); fillIdx(cs->pAp_y, a.idxY, b.idxY); } } @@ -3037,18 +2945,16 @@ struct LambdaProtonBalanceFunction { for (auto const& b : protonInfo) { const float qinvApp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut"), qinvApp); + if (passesQinvQA(qinvApp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); + } if (failsQinvCut(qinvApp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - state.hRho2_App_AllPt->Fill(a.idxEta, b.idxEta); - } if (a.idxY >= 0 && b.idxY >= 0) { state.hRho2_App_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { - fillIdx(cs->App, a.idxEta, b.idxEta); fillIdx(cs->App_y, a.idxY, b.idxY); } } @@ -3060,18 +2966,16 @@ struct LambdaProtonBalanceFunction { } const float qinvApAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut"), qinvApAp); + if (passesQinvQA(qinvApAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); + } if (failsQinvCut(qinvApAp)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); - if (cFillEtaSpace.value && a.idxEta >= 0 && b.idxEta >= 0) { - state.hRho2_ApAp_AllPt->Fill(a.idxEta, b.idxEta); - } if (a.idxY >= 0 && b.idxY >= 0) { state.hRho2_ApAp_y_AllPt->Fill(a.idxY, b.idxY); } if (cs) { - fillIdx(cs->ApAp, a.idxEta, b.idxEta); fillIdx(cs->ApAp_y, a.idxY, b.idxY); } } @@ -3108,7 +3012,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvLP)) { + if (passesQinvQA(qinvLP)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLP); } // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primProton and Lambda dau proton-primProton @@ -3153,8 +3057,6 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primProton.tpcFractionSharedCls()); } } - const int idxLambda = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimP = unrolledIndex(primProton.eta(), primProton.phi()); const float yL = v0.rapidity(1); const float yP = protonRapidity(primProton); const int idxYL = unrolledIndexY(yL, v0.phi()); @@ -3179,19 +3081,13 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primProton.px(), primProton.py(), primProton.pz(), kMassProton)); - if (idxLambda >= 0 && idxPrimP >= 0) { - if (bin >= 0) { - state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); - } - if (cFillEtaSpace.value) { - state.hRho2_Lp_AllPt->Fill(idxLambda, idxPrimP); - } + if (idxYL >= 0 && idxYP >= 0 && bin >= 0) { + state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } if (idxYL >= 0 && idxYP >= 0) { state.hRho2_Lp_y_AllPt->Fill(idxYL, idxYP); } if (cs) { - fillIdx(cs->Lp, idxLambda, idxPrimP); fillIdx(cs->Lp_y, idxYL, idxYP); } ++nPairs_Lambda_PrimProton; @@ -3211,7 +3107,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvLAp)) { + if (passesQinvQA(qinvLAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLAp); } // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primAntiProton and Lambda dau proton-primAntiProton @@ -3249,8 +3145,6 @@ struct LambdaProtonBalanceFunction { v0.eta() - primAntiProton.eta(), RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf)); } - const int idxLambda = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimPbar = unrolledIndex(primAntiProton.eta(), primAntiProton.phi()); const float yL2 = v0.rapidity(1); const float yAp = protonRapidity(primAntiProton); const int idxYL2 = unrolledIndexY(yL2, v0.phi()); @@ -3265,19 +3159,13 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); - if (idxLambda >= 0 && idxPrimPbar >= 0) { - if (bin >= 0) { - state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); - } - if (cFillEtaSpace.value) { - state.hRho2_LAp_AllPt->Fill(idxLambda, idxPrimPbar); - } + if (idxYL2 >= 0 && idxYAp >= 0 && bin >= 0) { + state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } if (idxYL2 >= 0 && idxYAp >= 0) { state.hRho2_LAp_y_AllPt->Fill(idxYL2, idxYAp); } if (cs) { - fillIdx(cs->LAp, idxLambda, idxPrimPbar); fillIdx(cs->LAp_y, idxYL2, idxYAp); } ++nPairs_Lambda_PrimAntiProton; @@ -3369,7 +3257,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvALp)) { + if (passesQinvQA(qinvALp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALp); } // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primProton and AntiLambda dau antiproton-primProton @@ -3407,8 +3295,6 @@ struct LambdaProtonBalanceFunction { v0.eta() - primProton.eta(), RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf)); } - const int idxAL = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimP = unrolledIndex(primProton.eta(), primProton.phi()); const float yAL = v0.rapidity(2); const float yP2 = protonRapidity(primProton); const int idxYAL = unrolledIndexY(yAL, v0.phi()); @@ -3423,19 +3309,13 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primProton.px(), primProton.py(), primProton.pz(), kMassProton)); - if (idxAL >= 0 && idxPrimP >= 0) { - if (bin >= 0) { - state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); - } - if (cFillEtaSpace.value) { - state.hRho2_ALp_AllPt->Fill(idxAL, idxPrimP); - } + if (idxYAL >= 0 && idxYP2 >= 0 && bin >= 0) { + state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } if (idxYAL >= 0 && idxYP2 >= 0) { state.hRho2_ALp_y_AllPt->Fill(idxYAL, idxYP2); } if (cs) { - fillIdx(cs->ALp, idxAL, idxPrimP); fillIdx(cs->ALp_y, idxYAL, idxYP2); } ++nPairs_AntiLambda_PrimProton; @@ -3455,7 +3335,7 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: q_inv Before and After cut - if (!failsQinvCut(qinvALAp)) { + if (passesQinvQA(qinvALAp)) { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALAp); } // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primAntiProton and AntiLambda dau antiproton-primAntiProton @@ -3500,8 +3380,6 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primAntiProton.tpcFractionSharedCls()); } } - const int idxAL2 = unrolledIndex(v0.eta(), v0.phi()); - const int idxPrimPb = unrolledIndex(primAntiProton.eta(), primAntiProton.phi()); const float yAL2 = v0.rapidity(2); const float yAp2 = protonRapidity(primAntiProton); const int idxYAL2 = unrolledIndexY(yAL2, v0.phi()); @@ -3516,19 +3394,13 @@ struct LambdaProtonBalanceFunction { registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp"), computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); - if (idxAL2 >= 0 && idxPrimPb >= 0) { - if (bin >= 0) { - state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); - } - if (cFillEtaSpace.value) { - state.hRho2_ALAp_AllPt->Fill(idxAL2, idxPrimPb); - } + if (idxYAL2 >= 0 && idxYAp2 >= 0 && bin >= 0) { + state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } if (idxYAL2 >= 0 && idxYAp2 >= 0) { state.hRho2_ALAp_y_AllPt->Fill(idxYAL2, idxYAp2); } if (cs) { - fillIdx(cs->ALAp, idxAL2, idxPrimPb); fillIdx(cs->ALAp_y, idxYAL2, idxYAp2); } ++nPairs_AntiLambda_PrimAntiProton; @@ -3629,24 +3501,20 @@ struct LambdaProtonBalanceFunction { const float Ev2 = std::sqrt(v2pmag * v2pmag + kMassLambda * kMassLambda); const float qinvLL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1, v2.px(), v2.py(), v2.pz(), Ev2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLL); + if (passesQinvQA(qinvLL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); + } if (failsQinvCut(qinvLL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1L, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - state.hRho2_LL_AllPt->Fill(idx1, idx2); - } if (idxY1 >= 0 && idxY2 >= 0) { state.hRho2_LL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { - fillIdx(cs->LL, idx1, idx2); fillIdx(cs->LL_y, idxY1, idxY2); } } @@ -3683,24 +3551,20 @@ struct LambdaProtonBalanceFunction { const float Ev2L = std::sqrt(v2pmagL * v2pmagL + kMassLambda * kMassLambda); const float qinvLAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1L, v2.px(), v2.py(), v2.pz(), Ev2L); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLAL); + if (passesQinvQA(qinvLAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); + } if (failsQinvCut(qinvLAL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - state.hRho2_LAL_AllPt->Fill(idx1, idx2); - } if (idxY1 >= 0 && idxY2 >= 0) { state.hRho2_LAL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { - fillIdx(cs->LAL, idx1, idx2); fillIdx(cs->LAL_y, idxY1, idxY2); } } @@ -3737,24 +3601,20 @@ struct LambdaProtonBalanceFunction { const float Ev2AL = std::sqrt(v2pmagAL * v2pmagAL + kMassLambda * kMassLambda); const float qinvALL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL, v2.px(), v2.py(), v2.pz(), Ev2AL); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALL); + if (passesQinvQA(qinvALL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); + } if (failsQinvCut(qinvALL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - state.hRho2_ALL_AllPt->Fill(idx1, idx2); - } if (idxY1 >= 0 && idxY2 >= 0) { state.hRho2_ALL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { - fillIdx(cs->ALL, idx1, idx2); fillIdx(cs->ALL_y, idxY1, idxY2); } } @@ -3794,24 +3654,20 @@ struct LambdaProtonBalanceFunction { const float Ev2AL2 = std::sqrt(v2pmagAL2 * v2pmagAL2 + kMassLambda * kMassLambda); const float qinvALAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL2, v2.px(), v2.py(), v2.pz(), Ev2AL2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALAL); + if (passesQinvQA(qinvALAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); + } if (failsQinvCut(qinvALAL)) { continue; } - registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); } - const int idx1 = unrolledIndex(v1.eta(), v1.phi()); - const int idx2 = unrolledIndex(v2.eta(), v2.phi()); const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) - if (cFillEtaSpace.value && idx1 >= 0 && idx2 >= 0) { - state.hRho2_ALAL_AllPt->Fill(idx1, idx2); - } if (idxY1 >= 0 && idxY2 >= 0) { state.hRho2_ALAL_y_AllPt->Fill(idxY1, idxY2); } if (cs) { - fillIdx(cs->ALAL, idx1, idx2); fillIdx(cs->ALAL_y, idxY1, idxY2); } } From 8c73025cf7c813f2d460acb2eb2843d24205762b Mon Sep 17 00:00:00 2001 From: Preet-Bhanjan Date: Thu, 8 Oct 2026 10:01:09 +0200 Subject: [PATCH 220/648] [PWGCF] Addition of PID specific long-range correlations (#17940) Co-authored-by: Preet Pati Co-authored-by: Preet Pati --- .../Tasks/CMakeLists.txt | 5 + .../Tasks/pidLongRange.cxx | 1901 +++++++++++++++++ 2 files changed, 1906 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index 61531e9f56e..4261d7186a5 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -147,3 +147,8 @@ o2physics_add_dpl_workflow(hparticle-correlation SOURCES hParticleCorrelation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pid-long-range + SOURCES pidLongRange.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx new file mode 100644 index 00000000000..a8974041278 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -0,0 +1,1901 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidLongRange.cxx +/// \brief Identified particles long-range correlations using forward FIT detectors and TPC, with focus on particle +/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch), Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch) + +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; + +struct PidLongRange { + o2::aod::ITSResponse itsResponse; + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "DCAz range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"(0.0026+0.005/(x^1.01))", "(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") + } cfgFuncParas; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + } cfgPIDConfigs; + + SliceCache cache; + + // Axes for general QA + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + ConfigurableAxis axisDCAz{"axisDCAz", {200, -2, 2}, "DCA_{z} (cm)"}; + ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisDeltaEtaFt0aFt0c{"axisDeltaEtaFt0aFt0c", {32, -1.5, 3.0}, "delta eta axis"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; + ConfigurableAxis axisTpcNcls{"axisTpcNcls", {500, 0, 500}, "number of TPC clusters used for PID"}; + + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; + ConfigurableAxis cfgFITamp{"cfgFITamp", {1000, 0, 5000}, "FT0 amplitude"}; + + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; + + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); + + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + + // FT0 geometry + o2::ft0::Geometry ft0Det; + static constexpr uint64_t Ft0IndexA = 96; + std::vector* offsetFT0 = nullptr; + std::vector cstFT0RelGain; + + // Corrections + TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameFt0aFt0c{"sameEvent_FT0A_FT0C"}; + OutputObj mixedFt0aFt0c{"mixedEvent_FT0A_FT0C"}; + + HistogramRegistry histos{"histos"}; + + // For Labelled array value containers + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + + // define global variables + TRandom3 fRandom{0}; + + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + enum FITIndex { + IndexFT0A = 0, + IndexFT0C = 1 + }; + + enum PIDIndex { + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi + }; + enum PiKpArrayIndex { + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow + }; + enum DetectorType { + UseTPC = 0, + UseTof, + UseITS + }; + enum Stage { + Before = 0, + After + }; + + enum EventCutTypes { + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts + }; + + enum EventCutType { + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + + enum CentEstimators { + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + // Count the total number of enum + NumCentEstimators + }; + + RCTFlagsChecker rctChecker{"CBT"}; + + void init(InitContext&) + { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together and FT0A-FT0C correlations are done separately + // ---------------------------------------------------------------------------------------------------------------------- + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + } + + if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) && cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + } + + // Multiplicity correlation cuts + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + const AxisSpec axisT0C{70, 0, 70000, "N_{ch} (T0C)"}; + const AxisSpec axisT0A{200, 0, 200000, "N_{ch} (T0A)"}; + const AxisSpec axisChi2{100, 0., 10.}; + const AxisSpec axisChID = {220, 0, 220}; + + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (doprocessQA) { + histos.add("h_globalTracks_centT0C_before", "before cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_before", "before cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_before", "before cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_before", "before cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_before", "before cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_before", "before cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_before", "before cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + + histos.add("h_globalTracks_centT0C_after", "after cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_after", "after cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_after", "after cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_after", "after cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_after", "after cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_after", "after cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_after", "after cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + histos.add("h_centFT0M_centFT0C", "after cut;Centrality T0C;Centrality T0M", {HistType::kTH2D, {axisCent, axisCent}}); + histos.add("h_centFV0A_centFT0C", "after cut;Centrality T0C;Centrality V0A", {HistType::kTH2D, {axisCent, axisCent}}); + + histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); + + // Add track cuts table + histos.add("hDCAz_before", "DCAz before cuts; DCAz (cm); Pt", {HistType::kTH2D, {axisDCAz, axisPtFiner}}); + histos.add("hDCAxy_before", "DCAxy before cuts; DCAxy (cm); Pt", {HistType::kTH2D, {axisDCAxy, axisPtFiner}}); + histos.add("hTPCNclsPID_before", "hTPCNclsPID_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_before", "hTPCNclsFound_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_before", "hTPCCrossedRows_before", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hDCAz_after", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH3D, {axisDCAz, axisPtFiner, axisParticle}}); + histos.add("hDCAxy_after", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH3D, {axisDCAxy, axisPtFiner, axisParticle}}); + histos.add("hTPCNclsPID_after", "hTPCNclsPID_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_after", "hTPCNclsFound_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_after", "hTPCCrossedRows_after", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {axisChi2}}); + histos.add("hChi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {axisChi2}}); + histos.add("hITSNclsFound", "Number of found ITS clusters", {HistType::kTH1D, {axisTpcNcls}}); + } + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (doprocessSameTpcFt0 || doprocessQA) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + } + // PID nSigma histograms + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // TPC-TOF PID QA hists + if (cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // ITS-TOF PID QA hists + } // end of PID QA hists + + // PID dEdx histograms + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } + } + if (doprocessSameTpcFt0) { // QA plots are based on TPC tracks, so they are only included in TPC-FT0A process and not in TPC-FT0C process + // TPC-FT0A correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + // TPC-FT0C correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + if (doprocessSameFt0aFt0c) { + histos.add("Phi_FT0A_FT0C", "Phi_FT0A_FT0C", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta_FT0A_FT0C", "Eta_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected_FT0A_FT0C", "EtaCorrected_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner_FT0A_FT0C", "pTFiner_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected_FT0A_FT0C", "pTFinerCorrected_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); + histos.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass}}}); + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + if (!doprocessQA) { + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + } + + LOGF(info, "Initializing correlation container"); + + // Initialize Nch-related histograms and containers + std::vector corrAxisTpcFt0a = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}}; + + std::vector corrAxisTpcFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}}; + + std::vector corrAxisFt0aFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; + + if (doprocessSameTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); + } + } + + if (doprocessSameFt0aFt0c) { + sameFt0aFt0c.setObject(new DihadronContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + mixedFt0aFt0c.setObject(new DihadronContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + } + + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation + } + + LOGF(info, "End of init"); + } + + template + double getCentrality(TCollision const& collision) + { + double cent = 0.0; + switch (cfgTrkSel.cfgCentEstimator) { + case UseCentFT0C: + cent = collision.centFT0C(); + break; + case UseCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case UseCentFT0M: + cent = collision.centFT0M(); + break; + case UseCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool eventRct(TCollision const& collision, const bool fillCounter) + { + if (!rctChecker(collision)) { + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 0.5); + } + + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 1.5); + } + + return true; + } + + template + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) + { + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } + if (!collision.sel8()) { + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); + } + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); + } + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); + } + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); + } + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return false; + } + + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); + } + + auto multNTracksPV = collision.multNTracksPV(); + auto occupancy = collision.trackOccupancyInTimeRange(); + + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); + } + + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } + } + } + + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } + + // V0A T0A 5 sigma cut + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); + } + + return true; + } + + template + void eventSelectedIndividually(TCollision const& collision) + { + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); + + if (collision.sel8()) { + histos.fill(HIST("hPassedEventSelection"), AfterSel8); + } + + if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); + } + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow || occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); + } + } + + double getPhiFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); + } + + double getEtaFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + auto x = chPos.X() + (*offsetFT0)[i].getX(); + auto y = chPos.Y() + (*offsetFT0)[i].getY(); + auto z = chPos.Z() + (*offsetFT0)[i].getZ(); + if (chno >= Ft0IndexA) { + z = -z; + } + auto r = std::sqrt(x * x + y * y); + auto theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); + } + + void loadAlignParam(uint64_t timestamp) + { + offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); + if (offsetFT0 == nullptr) { + LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); + } + } + + template + bool trackSelected(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); + } + + void loadGain(aod::BCsWithTimestamps::iterator const& bc) + { + cstFT0RelGain.clear(); + std::string fullPath; + + auto timestamp = bc.timestamp(); + constexpr int ChannelsFT0 = 208; + if (cfgGeneral.cfgCorrLevel == 0) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + fullPath = cfgGeneral.cfgGainEqPath; + fullPath += "/FT0"; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + cstFT0RelGain = *(objft0Gain); + } + } + } + + template + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + { + if (fitType == IndexFT0C) { + id = ft0.channelC()[iCh]; + id = id + Ft0IndexA; + ampl = ft0.amplitudeC()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else if (fitType == IndexFT0A) { + id = ft0.channelA()[iCh]; + ampl = ft0.amplitudeA()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else { + LOGF(fatal, "Cor Index %d out of range", fitType); + } + } + + template + int getNsigmaPID(const TTrack& track) + { + // Computing Nsigma arrays for pion, kaon, and protons + std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; + int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + + std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTof] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTof]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTof] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTof]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTof] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTof]; + + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } + + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + return -1; // more than one particle satisfy the criteria + } + + if (isPion) { + pid = UsePions; + } else if (isKaon) { + pid = UseKaons; + } else if (isProton) { + pid = UseProtons; + } else { + return -1; // no particle satisfies the criteria + } + + return pid; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + + bool getEfficiencyCorrection(float& weight, float pt, float eta, float vertex) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency + eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); + } else { + eff = 1.0; + } + if (eff == 0) { + return false; + } + weight = 1. / eff; + return true; + } + + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + + template + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + { + double nTracksCorrected = 0; + double nTracksUncorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { + continue; + } + + nTracksUncorrected += 1.0; + nTracksCorrected += weightNch; + } + histos.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); + multiplicity = nTracksCorrected; + } + + template + void fillYieldFt0aFt0c(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + + float weff1 = 1.0; + float zvtx = collision.posZ(); + + for (auto const& track1 : tracks) { + + if (!trackSelected(track1)) { + continue; + } + + if (!getEfficiencyCorrection(weff1, track1.pt(), track1.eta(), zvtx)) { + continue; + } + + histos.fill(HIST("Phi_FT0A_FT0C"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta_FT0A_FT0C"), track1.eta()); + histos.fill(HIST("EtaCorrected_FT0A_FT0C"), track1.eta(), weff1); + histos.fill(HIST("pTFiner_FT0A_FT0C"), track1.pt()); + histos.fill(HIST("pTFinerCorrected_FT0A_FT0C"), track1.pt(), weff1); + } + } + + template + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection + { + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged) { + const bool useITS = cfgPIDConfigs.cfgUseItsPID; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } + + if (cfgPIDConfigs.cfgQAdEdx && !useITS) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle + } + + template + void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) { + continue; + } + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { + continue; + } + + fillTrackQA(track1, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track1, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseProtons, eventClass, Before, triggerWeight); + + int pidIndex = getNsigmaPID(track1); + + if (pidIndex > UseCharged) { + fillTrackQA(track1, pidIndex, eventClass, After, triggerWeight); + } + + if (system == SameEvent) { + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } + } + + std::size_t channelSize = 0; + if (corType == IndexFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == IndexFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType, system); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + } + } + } else if (system == MixedEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + } + } + } + } + } + } + + template + void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + std::size_t channelASize = ft0Col1.channelA().size(); + std::size_t channelCSize = ft0Col2.channelC().size(); + // loop over all tracks + for (std::size_t iChA = 0; iChA < channelASize; iChA++) { + + int chanelAid = 0; + float amplA = 0.; + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); + + if (system == SameEvent) { + histos.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, eventClass, eventWeight * amplA); + } + + for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { + int chanelCid = 0; + float amplC = 0.; + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); + float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); + float deltaEta = etaA - etaC; + + // fill the right sparse and histograms + if (system == SameEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + sameFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } else if (system == MixedEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + mixedFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + } + } + } + + bool isGoodRun(int runNumber) + { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { + return false; + } + } + + return true; + } + + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0C, weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); + + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity = static_cast(tracks1.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0 = collision2.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); + } + } + } + PROCESS_SWITCH(PidLongRange, processMixedTpcFt0, "Process mixed events for TPC-FT0A correlation", false); + + void processSameFt0aFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + fillYieldFt0aFt0c(collision, tracks); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0, ft0, collision.posZ(), SameEvent, eventClass, weightCent); + } + PROCESS_SWITCH(PidLongRange, processSameFt0aFt0c, "Process same event for FT0A-FT0C correlation", true); + + void processMixedFt0aFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + // should have the same event to TPC-FT0A/C correlations + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity = static_cast(tracks1.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0Col1 = collision1.foundFT0(); + const auto& ft0Col2 = collision2.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0Col1, ft0Col2, collision1.posZ(), MixedEvent, eventClass, eventWeight * weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", true); + + void processQA(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + eventSelectedIndividually(collision); + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + histos.fill(HIST("h_globalTracks_centT0C_before"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_before"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_before"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_before"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_before"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_before"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_before"), collision.centFT0C(), collision.multFT0C()); + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + histos.fill(HIST("h_globalTracks_centT0C_after"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_after"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_after"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_after"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_after"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_after"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_after"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("h_centFT0M_centFT0C"), collision.centFT0C(), collision.centFT0M()); + histos.fill(HIST("h_centFV0A_centFT0C"), collision.centFT0C(), collision.centFV0A()); + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + auto multiplicity = static_cast(tracks.size()); + + histos.fill(HIST("Nch"), multiplicity); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track : tracks) { + + histos.fill(HIST("hDCAz_before"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxy_before"), track.dcaXY(), track.pt()); + histos.fill(HIST("hTPCNclsPID_before"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_before"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_before"), track.tpcNClsCrossedRows()); + + if (!trackSelected(track)) { + continue; + } + + histos.fill(HIST("hTPCNclsPID_after"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_after"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_after"), track.tpcNClsCrossedRows()); + histos.fill(HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + histos.fill(HIST("hChi2prITScls"), track.itsChi2NCl()); + histos.fill(HIST("hITSNclsFound"), track.itsNCls()); + + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), UseCharged); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), UseCharged); + + fillTrackQA(track, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track, UseProtons, eventClass, Before, triggerWeight); + + int pidIndex = getNsigmaPID(track); + + if (pidIndex > UseCharged) { + fillTrackQA(track, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), pidIndex); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), pidIndex); + } + + } // end of track loop + } + PROCESS_SWITCH(PidLongRange, processQA, "Process for storing the QA for events and tracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} From 945193150e854d26b5916c15e6cc04ad4e9dcb3e Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Thu, 8 Oct 2026 15:12:53 +0700 Subject: [PATCH 221/648] [PWGLF] Omega(2012): shared selection core, Xi(1530)K mode and ML training-table exporter (#18208) Co-authored-by: Bong-Hwi Lim --- PWGLF/Core/Omega2012AnalysisCore.h | 1269 +++++++++++++++ PWGLF/Core/Omega2012MlFeatures.h | 1387 +++++++++++++++++ PWGLF/DataModel/LFOmega2012MlTables.h | 239 +++ PWGLF/Tasks/Resonances/CMakeLists.txt | 5 + PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 1241 +++++---------- .../Resonances/omega2012TrainingTable.cxx | 404 +++++ 6 files changed, 3670 insertions(+), 875 deletions(-) create mode 100644 PWGLF/Core/Omega2012AnalysisCore.h create mode 100644 PWGLF/Core/Omega2012MlFeatures.h create mode 100644 PWGLF/DataModel/LFOmega2012MlTables.h create mode 100644 PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx diff --git a/PWGLF/Core/Omega2012AnalysisCore.h b/PWGLF/Core/Omega2012AnalysisCore.h new file mode 100644 index 00000000000..d0d3600d7b4 --- /dev/null +++ b/PWGLF/Core/Omega2012AnalysisCore.h @@ -0,0 +1,1269 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012AnalysisCore.h +/// \brief Omega(2012) selection, candidate enumeration and truth classification, separately for the two decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (DecayMode::XiK0s): Omega(2012)- -> Xi- K0S. +/// Mode B (DecayMode::Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- (charged kaon track). +/// The two modes are never merged: each has its own enumeration, hooks, cut flow, pass bits and truth classification. +/// The Xi selection is common to both modes. The core owns the selection and its cut-flow instrumentation +/// (CutFlow/*, ML/*); the tasks own their output histograms and fill them from the hooks. + +#ifndef PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ +#define PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ + +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012 +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +enum class DecayMode : uint8_t { + XiK0s = 0, // Omega(2012)- -> Xi- K0S + Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- +}; + +inline constexpr int PdgXi1530Zero = 3324; // Xi(1530)0, not in o2::constants::physics::Pdg +inline constexpr float SmallNumber = 1e-10f; // avoids division by zero (value of the original task) +inline constexpr float MaxDCAV0ToPV = 1.0f; // maximum K0S DCA to PV (value of the original task) +inline constexpr float CascDCAzPtExponent = -1.1f; +inline constexpr const char* TrackCutsPrefix = "trk."; // JSON prefix of the mode-B track selection +inline constexpr int NXiK0sCandidateStages = 4; +inline constexpr int NXi1530KCandidateStages = 4; +inline constexpr int NGeneratedChannels = 3; + +// Last stage passed by a cascade (Xi selection, common to both modes). +enum XiStage : int { + kXiInput = 0, // failed |eta| or pT + kXiKinematics = 1, // passed |eta| and pT + kXiDCA = 2, // passed the cascade DCA to PV + kXiV0Topology = 3, // passed the Lambda topology and mass window + kXiCascTopology = 4, // passed the cascade topology + kXiMass = 5, // passed the Xi mass window + kXiSelected = kXiMass, + kXiNStages = 6 +}; + +// Last stage passed by a V0 (K0S selection of mode A). +enum K0sStage : int { + kK0sInput = 0, // failed |eta| or pT + kK0sKinematics = 1, // passed |eta| and pT + kK0sTopology = 2, // passed cosPA, daughter DCAs, radius and DCA to PV + kK0sLifetime = 3, // passed the proper lifetime and the minimum qT + kK0sMass = 4, // passed the K0S mass window and the (anti)Lambda rejection + kK0sDaughters = 5, // passed the daughter pion TPC nSigma and crossed rows + kK0sArmenteros = 6, // passed the Armenteros qT > coefficient * |alpha| cut + kK0sSelected = kK0sArmenteros, + kK0sNStages = 7 +}; + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative pass bits of mode A (see LFOmega2012MlTables.h) +enum XiK0sPassBit : uint16_t { + kXiK0sPassLoose = 1, + kXiK0sPassXi = 2, + kXiK0sPassK0s = 4, + kXiK0sPassKinematic = 8 +}; +inline constexpr uint16_t XiK0sPassBitsSelected = kXiK0sPassLoose | kXiK0sPassXi | kXiK0sPassK0s | kXiK0sPassKinematic; + +// Cumulative pass bits of mode B (see LFOmega2012MlTables.h) +enum Xi1530KPassBit : uint16_t { + kXi1530KPassLoose = 1, + kXi1530KPassXi = 2, + kXi1530KPassQuality = 4, + kXi1530KPassPID = 8, + kXi1530KPassWindow = 16 +}; +inline constexpr uint16_t Xi1530KPassBitsSelected = kXi1530KPassLoose | kXi1530KPassXi | kXi1530KPassQuality | kXi1530KPassPID | kXi1530KPassWindow; + +enum class XiK0sTruth : uint8_t { + None = 0, + Matched = 1 +}; + +enum class Xi1530KTruth : uint8_t { + None = 0, + Matched = 1 +}; + +// Immediate decay channel of a generated Omega(2012) +enum class GeneratedChannel : uint8_t { + Other = 0, + XiK0s = 1, + Xi1530K = 2 +}; + +// Configurable groups without prefix: the JSON keys are the plain configurable names of the original task. + +/// Xi (cascade) selection, common to both modes. cMinPtcut is also the K0S minimum pT (as in the original task). +struct XiCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; + o2::framework::Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; + o2::framework::Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; + o2::framework::Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; + o2::framework::Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; + o2::framework::Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; + // V0 topology inside cascade (Lambda) + o2::framework::Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; + o2::framework::Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; + o2::framework::Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; + o2::framework::Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; + o2::framework::Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; + o2::framework::Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; + o2::framework::Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; + o2::framework::Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; + o2::framework::Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; + // Cascade topology + o2::framework::Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; + o2::framework::Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; + o2::framework::Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; + o2::framework::Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; + o2::framework::Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; + o2::framework::Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; + o2::framework::Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG +}; + +/// K0S selection and Xi-K0S kinematic (opening-angle) cut of mode A +struct K0sCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; + o2::framework::Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; + o2::framework::Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; + o2::framework::Configurable> cKinUpperCutsAlpha{"cKinUpperCutsAlpha", {3.0, 2.0, 1.5, 1.4, 1.0, 0.8, 0.6, 0.5, 0.45, 0.35, 0.3, 0.25, 0.2}, "Upper cut on Xi-K0s opening angle"}; + o2::framework::Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; + o2::framework::Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; + o2::framework::Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; + o2::framework::Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; + o2::framework::Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; + o2::framework::Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; + o2::framework::Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha coefficient"}; + o2::framework::Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; + o2::framework::Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.05, "K0s negative daughter DCA to PV min"}; + o2::framework::Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; + o2::framework::Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; + o2::framework::Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; + o2::framework::Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; + o2::framework::Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; + o2::framework::Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; + o2::framework::Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; +}; + +/// Xi(1530)0 K- selection of mode B +struct Xi1530KCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; + o2::framework::Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; + o2::framework::Configurable cXi1530UseMassWindow{"cXi1530UseMassWindow", true, "Require m(Xi pi) inside the Xi(1530) mass window"}; + o2::framework::Configurable cXi1530KFillWrongSign{"cXi1530KFillWrongSign", true, "Fill the wrong-sign control histograms (xi1530K_wrongSign/)"}; + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection of the pion and the kaon"}; +}; + +/// Pion PID of mode B (keys of the original three-body pion selection) +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; + o2::framework::Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID of mode B (same shape as the pion keys) +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKaonTPCNSigmaMax{"cKaonTPCNSigmaMax", 3.0, "Maximum TPC NSigma for kaon"}; + o2::framework::Configurable cKaonTOFNSigmaMax{"cKaonTOFNSigmaMax", 3.0, "Maximum TOF NSigma for kaon"}; + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Common candidate selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; +}; + +/// Track selection of the mode-B pion and kaon: the common resonance TrackCuts with the JSON prefix "trk." +/// (the Xi selection owns cMinPtcut). The framework prefixes a group only through a `prefix` data member, which +/// TrackCuts does not have and a derived group cannot add (members must belong to one class for the structured +/// binding of the option registration); the names are therefore prefixed here in the same way ("trk.cMinPtcut"). +/// Defaults that differ from TrackCuts follow the original pion selection: |eta| < 0.8 (cPionEtaMax), +/// 70 found TPC clusters for ResoTracks (cPionTPCNClusMin) and DCAz < 0.15 cm (cPionDCAzMax was 0.2 cm, which is +/// beyond the 0.15 cm range of the micro001 DCA grid). +inline TrackCuts makeXi1530KTrackCuts() +{ + TrackCuts cuts; + cuts.cMaxEtacut.value = 0.8f; + cuts.cMaxDCAzToPVcut.value = 0.15; + cuts.cfgTPCcluster.value = 70; + o2::framework::homogeneous_apply_refs( + [](auto& option) { + if constexpr (requires { option.name; }) { + option.name.insert(0, TrackCutsPrefix); + } + return true; + }, + cuts); + return cuts; +} + +/// Process functions enabled in a task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool xiK0s = false; // any mode-A process function + bool xi1530K = false; // any mode-B process function + bool microTracks = false; // any mode-B process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC + bool mcGen = false; // generated Omega(2012) parents (ResoMCParents_001) + bool mixing = false; // event mixing +}; + +/// Loose-stage traversal for the export hook (as the K1 LooseStageOptions). +/// With the defaults and without an export hook, the enumeration applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML//looseCutflow and ML//looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Mode-A candidate handed to the candidate and export hooks. +/// alpha is computed when cKinCuts is on or the values were requested; passesKinCut is true when cKinCuts is off. +struct XiK0sCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + k0s + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector k0s; // (pt, eta, phi, MassK0Short) + float alpha = 0.f; // Xi-K0S opening angle + bool passesKinCut = true; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +/// Mode-B candidate handed to the candidate and export hooks. +/// massXiK, massPiK, openingAngleXi1530K and cosThetaStar are computed only when the values were requested. +struct Xi1530KCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + pion + kaon + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector pion; // (pt, eta, phi, MassPionCharged) + ROOT::Math::PxPyPzEVector kaon; // (pt, eta, phi, MassKaonCharged) + ROOT::Math::PxPyPzEVector xi1530; // xi + pion + float massXiPi = 0.f; + float massXiK = 0.f; + float massPiK = 0.f; + float openingAngleXi1530K = 0.f; + float cosThetaStar = 0.f; + uint8_t chargePattern = o2::analysis::omega2012ml::kSignalPattern; + bool inXi1530Window = false; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +// Truth classification, separately per mode + +/// Mode A: reconstructed Xi and K0S from the same Omega(2012) (criteria of the original task). +/// A K0S from an intermediate K0bar has motherPDG 311 and is not matched; the truth table keeps motherPDG for audit. +template +XiK0sTruth classifyXiK0sTruth(const Xi& xi, const V0& v0) +{ + if (std::abs(xi.pdgCode()) != kXiMinus || std::abs(v0.pdgCode()) != kK0Short) { + return XiK0sTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != v0.motherId()) { + return XiK0sTruth::None; + } + if (std::abs(xi.motherPDG()) != o2::constants::physics::Pdg::kOmega2012Minus || xi.motherPDG() != v0.motherPDG()) { + return XiK0sTruth::None; + } + return XiK0sTruth::Matched; +} + +template +bool hasSibling(const Track& track, int particleId) +{ + if (particleId < 0) { + return false; + } + const auto siblings = track.siblingIds(); + return siblings[0] == particleId || siblings[1] == particleId; +} + +/// Mode B: Xi and pion from the same Xi(1530)0, kaon from the Omega(2012) whose other daughter is that Xi(1530)0. +/// Cascades carry no sibling IDs, so the kaon carries the link (siblingIds contains the Xi(1530)0 ID). +template +Xi1530KTruth classifyXi1530KTruth(const Xi& xi, const Track& pion, const Track& kaon) +{ + if (std::abs(xi.pdgCode()) != kXiMinus) { + return Xi1530KTruth::None; + } + const int xiCharge = xi.pdgCode() > 0 ? -1 : 1; // Xi- has PDG code +3312 + if (pion.pdgCode() != -xiCharge * kPiPlus || kaon.pdgCode() != xiCharge * kKPlus) { + return Xi1530KTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != pion.motherId() || std::abs(xi.motherPDG()) != PdgXi1530Zero || pion.motherPDG() != xi.motherPDG()) { + return Xi1530KTruth::None; + } + // Omega(2012)- and Xi- both have positive PDG codes + if (kaon.motherId() < 0 || kaon.motherPDG() != -xiCharge * o2::constants::physics::Pdg::kOmega2012Minus) { + return Xi1530KTruth::None; + } + return hasSibling(kaon, xi.motherId()) ? Xi1530KTruth::Matched : Xi1530KTruth::None; +} + +/// Immediate channel of a generated Omega(2012) from the PDG codes of its two daughters (charge conjugates included). +inline GeneratedChannel classifyGeneratedOmega2012(int pdg, int daughter1, int daughter2) +{ + const int sign = pdg > 0 ? 1 : -1; + auto isPair = [&](int a, int b) { + if (a == sign * PdgXi1530Zero && b == -sign * kKPlus) { + return GeneratedChannel::Xi1530K; + } + if (a == sign * kXiMinus && (b == kK0Short || b == -sign * kK0)) { + return GeneratedChannel::XiK0s; + } + return GeneratedChannel::Other; + }; + const auto channel = isPair(daughter1, daughter2); + return channel != GeneratedChannel::Other ? channel : isPair(daughter2, daughter1); +} + +/// Omega(2012) selection and candidate enumeration of the Omega(2012) tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class Omega2012AnalysisCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, XiCuts const& xiCuts, K0sCuts const& k0sCuts, + Xi1530KCuts const& xi1530KCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + CandidateCuts const& candidateCuts, ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mXiCuts = xiCuts; + mK0sCuts = k0sCuts; + mXi1530KCuts = xi1530KCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cPionTPCNSigmaMax.value; + pion.maxTOFnSigma = pionPidCuts.cPionTOFNSigmaMax.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cPionTPCNSigmaMax"; + pion.maxTOFName = "cPionTOFNSigmaMax"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cKaonTPCNSigmaMax.value; + kaon.maxTOFnSigma = kaonPidCuts.cKaonTOFNSigmaMax.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cKaonTPCNSigmaMax"; + kaon.maxTOFName = "cKaonTOFNSigmaMax"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mXi1530KCuts.cByPassTOF.value, modes.microTracks); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + [[nodiscard]] bool kinCutsEnabled() const { return mK0sCuts.cKinCuts.value; } + [[nodiscard]] bool fillWrongSign() const { return mXi1530KCuts.cXi1530KFillWrongSign.value; } + + // Xi selection (common to both modes); equivalent to cascprimaryTrackCut && casctopCut of the original task. + template + [[nodiscard]] int xiSelectionStage(const CascadeType& c) const + { + const auto& cut = mXiCuts; + if (std::abs(c.eta()) > cut.cMaxEtaCut.value || std::abs(c.pt()) < cut.cMinPtcut.value) { + return kXiInput; + } + if (cut.cDCAxyToPVAsPtForCasc.value && std::abs(c.dcaXYCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * c.pt())) { + return kXiKinematics; + } + // The DCAz cut uses the DCAxy parameters, as in the original task + if (cut.cDCAzToPVAsPtForCasc.value && std::abs(c.dcaZCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * std::pow(c.pt(), CascDCAzPtExponent))) { + return kXiKinematics; + } + + // V0 (Lambda) topology inside the cascade + if (std::abs(c.daughDCA()) > cut.cDCALambdaDaugtherscut.value || std::abs(c.dcav0topv()) < cut.cDCALambdaToPVcut.value) { + return kXiDCA; + } + if (c.sign() < 0) { // Xi- + if (std::abs(c.dcanegtopv()) < cut.cDCAPionToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAProtonToPVcut.value) { + return kXiDCA; + } + } else { // Anti-Xi + if (std::abs(c.dcanegtopv()) < cut.cDCAProtonToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAPionToPVcut.value) { + return kXiDCA; + } + } + if (c.v0CosPA() < std::cos(cut.cV0CosPACutPtDepP0.value - cut.cV0CosPACutPtDepP1.value * c.pt())) { + return kXiDCA; + } + if (c.transRadius() > cut.cMaxV0radiuscut.value || c.transRadius() < cut.cMinV0radiuscut.value) { + return kXiDCA; + } + if (std::abs(c.mLambda() - o2::constants::physics::MassLambda) > cut.cMasswindowV0cut.value) { + return kXiDCA; + } + + // Cascade topology + if (std::abs(c.dcabachtopv()) < cut.cDCABachlorToPVcut.value) { + return kXiV0Topology; + } + if (c.pt() < cut.cDCAXiDaugthersCutPtRangeLower.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepLower.value) { + return kXiV0Topology; + } + } else if (c.pt() < cut.cDCAXiDaugthersCutPtRangeUpper.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepMiddle.value) { + return kXiV0Topology; + } + } else { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepUpper.value) { + return kXiV0Topology; + } + } + if (c.cascCosPA() < std::cos(cut.cCosPACascCutPtDepP0.value - cut.cCosPACascCutPtDepP1.value * c.pt())) { + return kXiV0Topology; + } + if (c.cascTransRadius() > cut.cMaxCascradiuscut.value || c.cascTransRadius() < cut.cMinCascradiuscut.value) { + return kXiV0Topology; + } + if (std::abs(c.mXi() - cut.cMassXiminus.value) > cut.cMasswindowCasccut.value) { + return kXiCascTopology; + } + return kXiMass; + } + + // K0S proper lifetime (cm/c), the expression of the original task + template + static double k0sProperLifetime(const CollisionType& collision, const V0Type& v0) + { + float dx = v0.decayVtxX() - collision.posX(); + float dy = v0.decayVtxY() - collision.posY(); + float dz = v0.decayVtxZ() - collision.posZ(); + float l = std::sqrt(dx * dx + dy * dy + dz * dz); + float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + return (l / (p + SmallNumber)) * o2::constants::physics::MassK0Short; + } + + // K0S selection of mode A; equivalent to v0CutEnhanced of the original task. + // The collision is the one of the V0 (its primary vertex defines the proper lifetime). + template + [[nodiscard]] int k0sSelectionStage(const CollisionType& collision, const V0Type& v0) const + { + const auto& cut = mK0sCuts; + if (std::abs(v0.eta()) > cut.cMaxV0Etacut.value || v0.pt() < mXiCuts.cMinPtcut.value) { + return kK0sInput; + } + if (v0.v0CosPA() < cut.cK0sMinCosPA.value || v0.daughDCA() > cut.cK0sMaxDaughDCA.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcapostopv()) < cut.cK0sDauPosDCAtoPVMin.value || std::abs(v0.dcanegtopv()) < cut.cK0sDauNegDCAtoPVMin.value) { + return kK0sKinematics; + } + auto radius = v0.transRadius(); + if (radius < cut.cK0sRadiusMin.value || radius > cut.cK0sRadiusMax.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcav0topv()) > MaxDCAV0ToPV) { + return kK0sKinematics; + } + if (k0sProperLifetime(collision, v0) > cut.cK0sProperLifetimeMax.value) { + return kK0sTopology; + } + if (v0.qtarm() < cut.cK0sArmenterosQtMin.value) { + return kK0sTopology; + } + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > cut.cK0sMassWindow.value) { + return kK0sLifetime; + } + if (cut.cK0sCrossMassRejection.value) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value || + std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value) { + return kK0sLifetime; + } + } + if (std::abs(v0.daughterTPCNSigmaPosPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value || + std::abs(v0.daughterTPCNSigmaNegPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value) { + return kK0sMass; + } + if (v0.nCrossedRowsPos() <= cut.cK0sPosDaughterMinCrossedRows.value || v0.nCrossedRowsNeg() <= cut.cK0sNegDaughterMinCrossedRows.value) { + return kK0sMass; + } + if (v0.qtarm() < cut.cK0sArmenterosAlphaCoeff.value * std::fabs(v0.alpha())) { + return kK0sDaughters; + } + return kK0sArmenteros; + } + + // Xi-K0S opening angle and the pT-dependent opening-angle window of the original task (kinCuts) + template + [[nodiscard]] bool passesKinematicCut(const FirstVecT& firstDaughter, const SecondVecT& secondDaughter, const MotherVecT& mother, float& alpha) const + { + auto firstP = std::sqrt(firstDaughter.Px() * firstDaughter.Px() + firstDaughter.Py() * firstDaughter.Py() + firstDaughter.Pz() * firstDaughter.Pz()); + auto secondP = std::sqrt(secondDaughter.Px() * secondDaughter.Px() + secondDaughter.Py() * secondDaughter.Py() + secondDaughter.Pz() * secondDaughter.Pz()); + if (firstP < SmallNumber || secondP < SmallNumber) { + alpha = 0.f; + return false; + } + + auto cosAlpha = (firstDaughter.Px() * secondDaughter.Px() + firstDaughter.Py() * secondDaughter.Py() + firstDaughter.Pz() * secondDaughter.Pz()) / (firstP * secondP); + if (cosAlpha > 1.) { + cosAlpha = 1.; + } else if (cosAlpha < -1.) { + cosAlpha = -1.; + } + alpha = std::acos(cosAlpha); + + const auto& kinCutsPt = mK0sCuts.cKinCutsPt.value; + const auto& kinLowerCutsAlpha = mK0sCuts.cKinLowerCutsAlpha.value; + const auto& kinUpperCutsAlpha = mK0sCuts.cKinUpperCutsAlpha.value; + + int kinCutsSize = static_cast(kinUpperCutsAlpha.size()); + if (kinCutsSize > static_cast(kinLowerCutsAlpha.size())) { + kinCutsSize = static_cast(kinLowerCutsAlpha.size()); + } + if (kinCutsSize > static_cast(kinCutsPt.size()) - 1) { + kinCutsSize = static_cast(kinCutsPt.size()) - 1; + } + + for (int i = 0; i < kinCutsSize; ++i) { + if ((mother.Pt() > kinCutsPt[i] && mother.Pt() <= kinCutsPt[i + 1]) && (alpha < kinLowerCutsAlpha[i] || alpha > kinUpperCutsAlpha[i])) { + return false; + } + } + return true; + } + + // Full selection stage of a mode-B track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + return speciesStage(track, mSelection.trackQualityStage(track)); + } + + template + int pionSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + template + int kaonSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + // Mode A: (Xi, K0S) enumeration of one collision, or of one mixed pair (Xi from collision, K0S from v0Collision). + // The order of the original task is kept: K0S selection, Xi selection, then Xi (outer) x K0S (inner). + // Hooks (nullptr to skip): + // - onK0s(v0, properLifetime, selected): every V0, in table order, + // - onXi(xi, selected): every cascade, in table order, + // - onCandidate(xi, v0, XiK0sCandidateValues): every pair of selected objects without shared daughters + // (the daughter-ID check is skipped in mixed events); kinematic and rapidity flags are in the values, + // - onExport(collision, xi, v0, values, passBits): same-event candidates at the loose or selected stage. + template + void forEachXiK0sCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const V0CollisionType& v0Collision, + const CascadesType& cascades, const V0sType& v0s, bool computeValues, + XiHook onXi = nullptr, K0sHook onK0s = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasK0sHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (!IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // K0S selection cache + using V0Row = std::decay_t; + std::vector> k0sList; + k0sList.reserve(v0s.size()); + for (const auto& v0 : v0s) { + const int stage = k0sSelectionStage(v0Collision, v0); + const bool selected = stage == kK0sSelected; + if constexpr (HasK0sHook) { + onK0s(v0, k0sProperLifetime(v0Collision, v0), selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/k0s"), i); + } + } + if (selected || visitLoose) { + const auto indices = v0.indices(); + k0sList.push_back({v0, {indices[0], indices[1]}, selected}); + } + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + const bool kinCutsOn = mK0sCuts.cKinCuts.value; + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + for (const auto& cachedK0s : k0sList) { + const auto& v0 = cachedK0s.row; + const bool objectsSelected = cachedXi.selected && cachedK0s.selected; + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + if (objectsSelected) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 0); + if (truthMatched) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 1); + } + } + } + }; + countCandidate(0); + if constexpr (!IsMix) { + if (sharesAnyDaughterId(cachedXi.daughterIds, cachedK0s.daughterIds)) { + continue; + } + } + countCandidate(1); + + // 4-vectors (construction of the original task) + XiK0sCandidateValues values; + values.xi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + values.k0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), o2::constants::physics::MassK0Short)); + values.omega = values.xi + values.k0s; + if (kinCutsOn || computeValues || checkValidity) { + float alpha = 0.f; + const bool kinCutFlag = passesKinematicCut(values.xi, values.k0s, values.omega, alpha); + values.alpha = alpha; + if (kinCutsOn) { + values.passesKinCut = kinCutFlag; + } + } + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (values.passesKinCut) { + countCandidate(2); + if (values.inRapidity) { + countCandidate(3); + } + } + + if constexpr (!IsMix) { + if (checkValidity) { + exportXiK0s(histos, collision, xi, v0, values, cachedXi.selected, cachedK0s.selected, truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (objectsSelected) { + onCandidate(xi, v0, values); + } + } + } + } + } + + // Mode B: (Xi, pion, kaon) enumeration of one collision, or of one mixed pair (Xi from collision, tracks from the other). + // trackIds: ResoTrackTracks for full ResoTracks (positional source track IDs), unused for ResoMicroTracks_001 (trackId()). + // Hooks (nullptr to skip): + // - onXi(xi, selected): every cascade, in table order, + // - onTrack(track, pionStage, kaonStage): every track, in table order (TrackStage values), + // - onCandidate(xi, pion, kaon, Xi1530KCandidateValues): every triplet of selected objects with five distinct + // source track IDs (no Xi-daughter check in mixed events) inside the Xi(1530) window; all charge patterns, + // - onExport(collision, xi, pion, kaon, values, passBits): same-event micro candidates at the loose or selected stage. + template + void forEachXi1530KCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, + const CascadesType& cascades, const TracksType& tracks, const TrackIdsType& trackIds, bool computeValues, + XiHook onXi = nullptr, TrackHook onTrack = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasTrackHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + // Track selection cache: every track is selected once per collision, for both species + const std::size_t nTracks = tracks.size(); + if (nTracks == 0) { + return; + } + const int64_t firstIndex = tracks.begin().index(); + std::vector trackCache(nTracks); + for (const auto& track : tracks) { + auto& entry = trackCache[getCacheIndex(track, firstIndex, nTracks)]; + const int qualityStage = mSelection.trackQualityStage(track); + const int pionStage = speciesStage(track, qualityStage); + const int kaonStage = speciesStage(track, qualityStage); + entry.sourceId = sourceTrackId(track, trackIds); + entry.quality = qualityStage == TrackStage::kTrkClusters; + entry.pionSelected = pionStage == TrackStage::kTrkPID; + entry.kaonSelected = kaonStage == TrackStage::kTrkPID; + if constexpr (HasTrackHook) { + onTrack(track, pionStage, kaonStage); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= pionStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Pion)); + } + for (int i = 0; i <= kaonStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Kaon)); + } + } + } + + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + const bool useWindow = mXi1530KCuts.cXi1530UseMassWindow.value; + const float windowCenter = mXi1530KCuts.cXi1530Mass.value; + const float windowWidth = mXi1530KCuts.cXi1530MassWindow.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + const int xiSign = xi.sign() < 0 ? -1 : 1; + const ROOT::Math::PxPyPzEVector pXi(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + for (const auto& pion : tracks) { + const auto& pionEntry = trackCache[getCacheIndex(pion, firstIndex, nTracks)]; + if (!pionEntry.pionSelected && !visitLoose) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, pionEntry.sourceId)) { + continue; + } + } + const bool pairSelected = cachedXi.selected && pionEntry.pionSelected; + const bool pionSignal = pion.sign() != xiSign; + const ROOT::Math::PxPyPzEVector pPion(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), o2::constants::physics::MassPionCharged)); + const ROOT::Math::PxPyPzEVector pXi1530 = pXi + pPion; + const float massXiPi = pXi1530.M(); + const bool inWindow = !useWindow || std::abs(massXiPi - windowCenter) < windowWidth; + if constexpr (FillCutFlow) { + if (pairSelected) { + countXi1530K(histos, 0, pionSignal, false); + if (inWindow) { + countXi1530K(histos, 1, pionSignal, false); + } + } + } + if (!(pairSelected && inWindow) && !visitLoose) { + continue; + } + for (const auto& kaon : tracks) { + if (kaon.index() == pion.index()) { + continue; + } + const auto& kaonEntry = trackCache[getCacheIndex(kaon, firstIndex, nTracks)]; + if (!kaonEntry.kaonSelected && !visitLoose) { + continue; + } + if (kaonEntry.sourceId == pionEntry.sourceId) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, kaonEntry.sourceId)) { + continue; + } + } + const bool tripletSelected = pairSelected && inWindow && kaonEntry.kaonSelected; + Xi1530KCandidateValues values; + values.xi = pXi; + values.pion = pPion; + values.kaon = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(kaon.pt(), kaon.eta(), kaon.phi(), o2::constants::physics::MassKaonCharged)); + values.xi1530 = pXi1530; + values.omega = pXi1530 + values.kaon; + values.massXiPi = massXiPi; + values.chargePattern = o2::analysis::omega2012ml::chargePattern(xiSign, pion.sign(), kaon.sign()); + values.inXi1530Window = inWindow; + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (computeValues || checkValidity) { + values.massXiK = (pXi + values.kaon).M(); + values.massPiK = (pPion + values.kaon).M(); + values.openingAngleXi1530K = ROOT::Math::VectorUtil::Angle(pXi1530, values.kaon); + values.cosThetaStar = static_cast(o2::analysis::omega2012ml::detail::cosThetaStar( + o2::analysis::omega2012ml::detail::LorentzVector(pXi1530), o2::analysis::omega2012ml::detail::LorentzVector(values.omega))); + } + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + } + if constexpr (FillCutFlow) { + if (tripletSelected) { + const bool signal = values.chargePattern == o2::analysis::omega2012ml::kSignalPattern; + countXi1530K(histos, 2, signal, truthMatched); + if (values.inRapidity) { + countXi1530K(histos, 3, signal, truthMatched); + } + } + } + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity) { + exportXi1530K(histos, collision, xi, pion, kaon, values, cachedXi.selected, + pionEntry.quality && kaonEntry.quality, pionEntry.pionSelected && kaonEntry.kaonSelected, + truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(xi, pion, kaon, values); + } + } + } + } + } + } + + // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The optional callback + // receives (parent, immediate channel) for the parents inside the rapidity window. Split reconstructed collisions + // repeat parent sets: this is not an unconditional generated denominator. + template + void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (!(std::abs(part.y()) < mCandidateCuts.cfgRapidityCut.value)) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + template + static bool sharesAnyDaughterId(const std::array& first, const std::array& second) + { + for (const auto& firstId : first) { + for (const auto& secondId : second) { + if (firstId == secondId) { + return true; + } + } + } + return false; + } + + template + static bool sharesDaughterId(const std::array& daughters, int64_t trackId) + { + for (const auto& daughterId : daughters) { + if (static_cast(daughterId) == trackId) { + return true; + } + } + return false; + } + + // Source track ID: trackId() of ResoMicroTracks_001, or the positional ResoTrackTracks row of a ResoTracks row. + template + static int64_t sourceTrackId(const TrackType& track, const TrackIdsType& trackIds) + { + if constexpr (requires { track.trackId(); }) { + return static_cast(track.trackId()); + } else { + const auto rowIndex = track.globalIndex(); + if (rowIndex >= 0 && rowIndex < static_cast(trackIds.size())) { + return static_cast(trackIds.rawIteratorAt(rowIndex).trackId()); + } + return static_cast(rowIndex); + } + } + + private: + template + struct CachedObject { + Row row; + std::array daughterIds{}; + bool selected = false; + }; + + struct TrackCacheEntry { + int64_t sourceId = -1; + bool quality = false; + bool pionSelected = false; + bool kaonSelected = false; + }; + + template + int speciesStage(const TrackType& track, int qualityStage) + { + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if (hasTOF) { + tofNSigma = (S == Species::Pion) ? track.tofNSigmaPi() : track.tofNSigmaKa(); + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + static void countXi1530K(o2::framework::HistogramRegistry& histos, int stage, bool signalPattern, bool truthMatched) + { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 0); + if (signalPattern) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 1); + } + if (truthMatched) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 2); + } + } + + // Loose / selected stage of a mode-A same-event candidate + template + void exportXiK0s(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, const V0Type& v0, + XiK0sCandidateValues const& values, bool xiSelected, bool k0sSelected, bool truthMatched, + bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXiK0sFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 0); + if (truthMatched) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 1); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && k0sSelected && values.passesKinCut; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXiK0sPassLoose; + if (xiSelected) { + passBits |= kXiK0sPassXi; + countMl(2); + if (k0sSelected) { + passBits |= kXiK0sPassK0s; + countMl(3); + if (values.passesKinCut) { + passBits |= kXiK0sPassKinematic; + countMl(4); + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, v0, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, v0, values, XiK0sPassBitsSelected); + } + } + } + + // Loose / selected stage of a mode-B same-event micro candidate + template + void exportXi1530K(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, + const TrackType& pion, const TrackType& kaon, Xi1530KCandidateValues const& values, + bool xiSelected, bool qualityBoth, bool pidBoth, bool truthMatched, bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXi1530KFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + const int category = values.chargePattern == ml::kSignalPattern ? 0 : 1; + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, category); + if (truthMatched) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, 2); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && qualityBoth && pidBoth && values.inXi1530Window; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit && category == 0) { + histos.fill(HIST("ML/xi1530K/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXi1530KPassLoose; + if (xiSelected) { + passBits |= kXi1530KPassXi; + countMl(2); + if (qualityBoth) { + passBits |= kXi1530KPassQuality; + countMl(3); + if (pidBoth) { + passBits |= kXi1530KPassPID; + countMl(4); + if (values.inXi1530Window) { + passBits |= kXi1530KPassWindow; + countMl(5); + } + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, pion, kaon, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, pion, kaon, values, Xi1530KPassBitsSelected); + } + } + } + + // Cut-flow instrumentation of the selection, per decay mode; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + const std::array xiLabels{"input", "|#eta|, p_{T}", "DCA to PV", "#Lambda topology", "cascade topology", "#Xi mass"}; + if (modes.xiK0s) { + auto xiFlow = histos.add("CutFlow/xiK0s/xi", "XiK0s mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + auto k0sFlow = histos.add("CutFlow/xiK0s/k0s", "XiK0s mode: V0s, once per selected collision;stage;V0s", HistType::kTH1D, {{kK0sNStages, -0.5, static_cast(kK0sNStages) - 0.5}}); + const std::array k0sLabels{"input", "|#eta|, p_{T}", "topology", "lifetime, min q_{T}", "mass, #Lambda rejection", "daughters", "Armenteros"}; + for (std::size_t i = 0; i < k0sLabels.size(); ++i) { + k0sFlow->GetXaxis()->SetBinLabel(i + 1, k0sLabels[i]); + } + auto candidateFlow = histos.add("CutFlow/xiK0s/candidates", "XiK0s mode: selected Xi x selected K0S, same event;stage;category", HistType::kTH2D, + {{NXiK0sCandidateStages, -0.5, NXiK0sCandidateStages - 0.5}, {2, -0.5, 1.5}}); + const std::array candidateLabels{"selected pairs", "distinct daughters", "kinematic cut", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xiK0s/looseCutflow", "XiK0s mode: valid canonical candidates;stage;category", HistType::kTH2D, {{5, -0.5, 4.5}, {2, -0.5, 1.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "K0S selection", "kinematic cut"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "all"); + flow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + histos.add("ML/xiK0s/looseMassPtActivity", "XiK0s mode loose candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.6, 2.8}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.xi1530K) { + auto xiFlow = histos.add("CutFlow/xi1530K/xi", "Xi1530K mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/xi1530K/tracks", "Xi1530K mode: tracks, once per selected collision;stage;species", HistType::kTH2D, + {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/xi1530K/candidates", "Xi1530K mode: same event;stage;category", HistType::kTH2D, + {{NXi1530KCandidateStages, -0.5, NXi1530KCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"selected Xi #pi pairs", "Xi(1530) window", "selected Xi #pi K triplets", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "signal charge pattern"); + candidateFlow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xi1530K/looseCutflow", "Xi1530K mode: valid canonical candidates;stage;category", HistType::kTH2D, {{6, -0.5, 5.5}, {3, -0.5, 2.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "track quality", "TOF + PID", "Xi(1530) window"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "signal charge pattern"); + flow->GetYaxis()->SetBinLabel(2, "wrong-sign control"); + flow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + histos.add("ML/xi1530K/looseMassPtActivity", "Xi1530K mode loose signal-pattern candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.8, 3.0}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "Generated Omega(2012) parent rows of selected reconstructed events;stage;immediate channel", HistType::kTH2D, + {{2, -0.5, 1.5}, {NGeneratedChannels, -0.5, NGeneratedChannels - 0.5}}); + generated->GetXaxis()->SetBinLabel(1, "all parent rows"); + generated->GetXaxis()->SetBinLabel(2, "rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "Xi K0S"); + generated->GetYaxis()->SetBinLabel(3, "Xi(1530) K"); + } + } + + ResoAnalysisSelectionCore mSelection; + XiCuts mXiCuts; + K0sCuts mK0sCuts; + Xi1530KCuts mXi1530KCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; +}; + +} // namespace o2::analysis::omega2012 + +#endif // PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ diff --git a/PWGLF/Core/Omega2012MlFeatures.h b/PWGLF/Core/Omega2012MlFeatures.h new file mode 100644 index 00000000000..345a6c70c38 --- /dev/null +++ b/PWGLF/Core/Omega2012MlFeatures.h @@ -0,0 +1,1387 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012MlFeatures.h +/// \brief Snapshots, canonical candidates and the two separate feature contracts of the Omega(2012) decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// The two modes have independent, separately versioned contracts (feature_contract_omega2012_v1.json); +/// no feature vector, status or projection is shared between them. + +#ifndef PWGLF_CORE_OMEGA2012MLFEATURES_H_ +#define PWGLF_CORE_OMEGA2012MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include + +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCascadeDaughters = 3; +inline constexpr std::size_t NV0Daughters = 2; +inline constexpr std::size_t NPidSpecies = 3; +inline constexpr std::size_t XiK0sNMasterFeatures = 63; +inline constexpr std::size_t Xi1530KNMasterFeatures = 126; +inline constexpr int8_t SaturatedLow = std::numeric_limits::lowest(); // x10 nSigma code of -inf / missing TOF +inline constexpr int8_t SaturatedHigh = std::numeric_limits::max(); // x10 nSigma code of NaN / +inf +inline constexpr float NSigmaScale = 10.f; +inline constexpr float MicroPidOverflow = 3.5f; + +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + StudyV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedDaughter, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +/// Charge pattern of a mode-B candidate relative to the Xi charge (metadata, never a feature). +enum ChargePattern : uint8_t { + kSignalPattern = 0, // pion opposite to the Xi charge, kaon equal to it (Xi- pi+ K- and conjugate) + kWrongSignPion = 1, // pion with the Xi charge + kWrongSignKaon = 2, // kaon opposite to the Xi charge + kWrongSignBoth = 3 // both wrong +}; + +/// All ResoCascades scalars of one cascade plus derived decay lengths. +/// Daughter arrays: index 0 positive, 1 negative, 2 bachelor; nSigma: [daughter * 3 + species], species pi, ka, pr. +struct CascadeSnapshot { + int64_t sourceRow = -1; // row of the cascade in the input ResoCascades table + int8_t sign = 0; + float px = 0.f, py = 0.f, pz = 0.f; + float mXi = 0.f, mLambda = 0.f; + float v0CosPA = 0.f, cascCosPA = 0.f, v0DaughDCA = 0.f, cascDaughDCA = 0.f; + float dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaBachToPV = 0.f, dcaV0ToPV = 0.f, dcaXYToPV = 0.f, dcaZToPV = 0.f; + float v0Radius = 0.f, cascRadius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassXiMinus / p +}; + +/// All ResoV0s scalars of one V0 plus derived decay lengths. Daughter arrays: index 0 positive, 1 negative. +struct V0Snapshot { + int64_t sourceRow = -1; // row of the V0 in the input ResoV0s table + float px = 0.f, py = 0.f, pz = 0.f; + float mK0Short = 0.f, mLambda = 0.f, mAntiLambda = 0.f; + float cosPA = 0.f, daughDCA = 0.f, dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaV0ToPV = 0.f, radius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + float alpha = 0.f, qtarm = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassK0Short / p (proper lifetime c*tau) +}; + +/// ResoMicroTracks_001 row: raw packed bytes and the decoded public accessors. +/// The encoding rules are those of the K1 micro001 contract; the type is separate so the K1 contract stays immutable. +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct XiK0sCandidateSnapshot { + CascadeSnapshot xi{}; + V0Snapshot k0s{}; +}; + +struct Xi1530KCandidateSnapshot { + CascadeSnapshot xi{}; + TrackSnapshot pion{}; + TrackSnapshot kaon{}; + uint8_t chargePattern = kSignalPattern; // metadata +}; + +template +struct CanonicalizationResult { + Snapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +/// Candidate kinematics from the stored 3-momenta and PDG masses (audit only, never features) +struct KinematicAudit { + float mass = 0.f; + float pt = 0.f; + float eta = 0.f; + float phi = 0.f; + float scalarSumPt = 0.f; +}; + +template +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; +using XiK0sFeaturePack = FeaturePack; +using Xi1530KFeaturePack = FeaturePack; + +namespace detail +{ +template +float decayLength(Collision const& collision, Row const& row) +{ + const float dx = row.decayVtxX() - collision.posX(); + const float dy = row.decayVtxY() - collision.posY(); + const float dz = row.decayVtxZ() - collision.posZ(); + return std::sqrt(dx * dx + dy * dy + dz * dz); +} + +inline float properLength(float length, float px, float py, float pz, double mass) +{ + const float p = std::sqrt(px * px + py * py + pz * pz); + if (!(p > 0.f)) { + return std::numeric_limits::quiet_NaN(); + } + return static_cast(length * mass / p); +} +} // namespace detail + +template +CascadeSnapshot makeCascadeSnapshot(Collision const& collision, Cascade const& row) +{ + CascadeSnapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.sign = static_cast(row.sign()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mXi = row.mXi(); + out.mLambda = row.mLambda(); + out.v0CosPA = row.v0CosPA(); + out.cascCosPA = row.cascCosPA(); + out.v0DaughDCA = row.daughDCA(); + out.cascDaughDCA = row.cascDaughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaBachToPV = row.dcabachtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.dcaXYToPV = row.dcaXYCascToPV(); + out.dcaZToPV = row.dcaZCascToPV(); + out.v0Radius = row.transRadius(); + out.cascRadius = row.cascTransRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10(), + row.daughterTPCNSigmaBachPi10(), row.daughterTPCNSigmaBachKa10(), row.daughterTPCNSigmaBachPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10(), + row.daughterTOFNSigmaBachPi10(), row.daughterTOFNSigmaBachKa10(), row.daughterTOFNSigmaBachPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg(), row.nCrossedRowsBach()}; + const auto indices = row.cascadeIndices(); + out.daughterIds = {indices[0], indices[1], indices[2]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassXiMinus); + return out; +} + +template +V0Snapshot makeV0Snapshot(Collision const& collision, V0 const& row) +{ + V0Snapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mK0Short = row.mK0Short(); + out.mLambda = row.mLambda(); + out.mAntiLambda = row.mAntiLambda(); + out.cosPA = row.v0CosPA(); + out.daughDCA = row.daughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.radius = row.transRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.alpha = row.alpha(); + out.qtarm = row.qtarm(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg()}; + const auto indices = row.indices(); + out.daughterIds = {indices[0], indices[1]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassK0Short); + return out; +} + +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(float px, float py, float pz) +{ + return std::isfinite(px) && std::isfinite(py) && std::isfinite(pz) && std::hypot(px, py) > 0.f; +} + +inline bool sharesDaughter(CascadeSnapshot const& xi, int64_t trackId) +{ + return std::any_of(xi.daughterIds.begin(), xi.daughterIds.end(), [trackId](int id) { return static_cast(id) == trackId; }); +} + +/// Mode A canonical candidate [Xi, K0s]; the roles are fixed by the object types. +inline CanonicalizationResult canonicalizeXiK0s(CascadeSnapshot const& xi, V0Snapshot const& k0s) +{ + CanonicalizationResult result; + for (const auto& id : k0s.daughterIds) { + if (sharesDaughter(xi, id)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + } + if (xi.sign != 1 && xi.sign != -1) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.k0s = k0s; + result.status = BuildStatus::Ok; + return result; +} + +/// Charge pattern of (Xi, pion, kaon); 0 is the Omega(2012) signal pattern. +inline uint8_t chargePattern(int xiSign, int pionSign, int kaonSign) +{ + uint8_t pattern = kSignalPattern; + if (pionSign == xiSign) { + pattern |= kWrongSignPion; + } + if (kaonSign != xiSign) { + pattern |= kWrongSignKaon; + } + return pattern; +} + +/// Mode B canonical candidate [Xi, pion, kaon]; the roles are fixed by the selected species, the charge pattern is metadata. +inline CanonicalizationResult canonicalizeXi1530K(CascadeSnapshot const& xi, TrackSnapshot const& pion, TrackSnapshot const& kaon) +{ + CanonicalizationResult result; + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + if ((xi.sign != 1 && xi.sign != -1) || (pion.charge != 1 && pion.charge != -1) || (kaon.charge != 1 && kaon.charge != -1)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.pion = pion; + result.candidate.kaon = kaon; + result.candidate.chargePattern = chargePattern(xi.sign, pion.charge, kaon.charge); + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +// int8 x10 nSigma of ResoCascades/ResoV0s: saturated codes carry no usable value +inline EncodedValue encodeNSigma10(int8_t code) +{ + if (code == SaturatedLow || code == SaturatedHigh) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + return {.value = static_cast(code) / NSigmaScale, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 nSigma, as the K1 contract +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -MicroPidOverflow : MicroPidOverflow, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 DCA, as the K1 contract +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +template +class Writer +{ + public: + explicit Writer(std::array& output) : out(output) {} + std::size_t index = 0; + // A count beyond N is detected by finishPack(); nothing is written past the end. + void add(float value) + { + if (index < N) { + out[index] = value; + } + ++index; + } + void add(double value) { add(static_cast(value)); } + void add(bool value) { add(value ? 1.f : 0.f); } + void addValid(EncodedValue value) + { + add(value.value); + add(value.valid); + } + void addFull(EncodedValue value) + { + add(value.value); + add(value.valid); + add(value.overflow); + } + + private: + std::array& out; +}; + +using LorentzVector = ROOT::Math::PxPyPzMVector; + +inline LorentzVector fourMomentum(float px, float py, float pz, double mass) +{ + return {px, py, pz, mass}; +} + +template +void appendCascade(Writer& w, CascadeSnapshot const& xi) +{ + w.add(xi.v0CosPA); + w.add(xi.cascCosPA); + w.add(xi.v0DaughDCA); + w.add(xi.cascDaughDCA); + w.add(std::abs(xi.dcaPosToPV)); + w.add(std::abs(xi.dcaNegToPV)); + w.add(std::abs(xi.dcaBachToPV)); + w.add(std::abs(xi.dcaV0ToPV)); + w.add(std::abs(xi.dcaXYToPV)); + w.add(std::abs(xi.dcaZToPV)); + w.add(xi.v0Radius); + w.add(xi.cascRadius); + w.add(static_cast(xi.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(xi.mXi) - o2::constants::physics::MassXiMinus); + w.add(xi.decayLength); + w.add(xi.properLength); + // Roles: the (anti)proton is the positive daughter of a Xi-, the negative daughter of a Xi+ + const bool isXiMinus = xi.sign < 0; + constexpr std::size_t Pi = 0, Pr = 2, Pos = 0, Neg = 1, Bach = 2; + const std::size_t baryon = isXiMinus ? Pos : Neg; + const std::size_t meson = isXiMinus ? Neg : Pos; + w.addValid(encodeNSigma10(xi.tpcNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[Bach * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tofNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[Bach * NPidSpecies + Pi])); + w.add(static_cast(xi.crossedRows[baryon])); + w.add(static_cast(xi.crossedRows[meson])); + w.add(static_cast(xi.crossedRows[Bach])); +} + +template +void appendV0(Writer& w, V0Snapshot const& k0s) +{ + w.add(k0s.cosPA); + w.add(k0s.daughDCA); + w.add(std::abs(k0s.dcaPosToPV)); + w.add(std::abs(k0s.dcaNegToPV)); + w.add(std::abs(k0s.dcaV0ToPV)); + w.add(k0s.radius); + w.add(static_cast(k0s.mK0Short) - o2::constants::physics::MassK0Short); + w.add(static_cast(k0s.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(k0s.mAntiLambda) - o2::constants::physics::MassLambda0); + w.add(k0s.alpha); + w.add(k0s.qtarm); + w.add(k0s.decayLength); + w.add(k0s.properLength); + constexpr std::size_t Pi = 0, Pos = 0, Neg = 1; + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Neg * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Neg * NPidSpecies + Pi])); + w.add(static_cast(k0s.crossedRows[Pos])); + w.add(static_cast(k0s.crossedRows[Neg])); +} + +template +void appendTrack(Writer& w, TrackSnapshot const& track) +{ + for (const float& decoded : track.tpcNSigma) { + w.addFull(encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + w.addFull(track.hasTOF ? encodePID(decoded) : EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + w.addFull(encodeDCA(track.dcaXY)); + w.addFull(encodeDCA(track.dcaZ)); + w.add(track.passedPtDependentDCAxy); + w.add(track.passedPtDependentDCAz); + w.add(track.hasTOF); + w.add(static_cast(track.tpcNClsCrossedRows)); + for (const bool& hit : track.itsHit) { + w.add(hit); + } + w.add(track.isPVContributor); +} + +// delta eta, sin/cos delta phi, delta R, z_pT of two objects +template +bool appendPair(Writer& w, LorentzVector const& a, LorentzVector const& b) +{ + const double deta = a.Eta() - b.Eta(); + const double dphi = std::remainder(a.Phi() - b.Phi(), o2::constants::math::TwoPI); + const double sumPt = a.Pt() + b.Pt(); + if (!std::isfinite(deta) || !std::isfinite(dphi) || !(sumPt > 0.)) { + return false; + } + w.add(deta); + w.add(std::sin(dphi)); + w.add(std::cos(dphi)); + w.add(std::hypot(deta, dphi)); + w.add(std::min(a.Pt(), b.Pt()) / sumPt); + return true; +} + +// Cosine of the daughter direction in the mother rest frame w.r.t. the mother direction +inline double cosThetaStar(LorentzVector const& daughter, LorentzVector const& mother) +{ + const double p = mother.P(); + if (!(p > 0.) || !(mother.E() > p)) { + return std::numeric_limits::quiet_NaN(); + } + const ROOT::Math::Boost boost{mother.BoostToCM()}; + const auto inRest = boost(daughter); + const double pStar = inRest.P(); + if (!(pStar > 0.)) { + return std::numeric_limits::quiet_NaN(); + } + return inRest.Vect().Dot(mother.Vect()) / (pStar * p); +} + +template +bool finishPack(FeaturePack& pack, std::size_t index, LorentzVector const& mother, double scalarSumPt) +{ + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != N || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return false; + } + pack.kinematics.mass = static_cast(mother.M()); + pack.kinematics.pt = static_cast(mother.Pt()); + pack.kinematics.eta = static_cast(mother.Eta()); + pack.kinematics.phi = static_cast(mother.Phi()); + pack.kinematics.scalarSumPt = static_cast(scalarSumPt); + pack.status = BuildStatus::Ok; + return true; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names, projections and SHA-256 are generated from feature_contract_omega2012_v1.json (one section per mode). +inline constexpr std::array XiK0sMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "k0s.cos_pa", + "k0s.daughter_dca", + "k0s.abs_dca_pos_to_pv", + "k0s.abs_dca_neg_to_pv", + "k0s.abs_dca_v0_to_pv", + "k0s.radius", + "k0s.delta_mass_k0s", + "k0s.delta_mass_lambda", + "k0s.delta_mass_antilambda", + "k0s.arm_alpha", + "k0s.arm_qt", + "k0s.decay_length", + "k0s.proper_length", + "k0s.pos_tpc_nsigma_pi", + "k0s.pos_tpc_nsigma_pi_valid", + "k0s.neg_tpc_nsigma_pi", + "k0s.neg_tpc_nsigma_pi_valid", + "k0s.pos_tof_nsigma_pi", + "k0s.pos_tof_nsigma_pi_valid", + "k0s.neg_tof_nsigma_pi", + "k0s.neg_tof_nsigma_pi_valid", + "k0s.pos_crossed_rows", + "k0s.neg_crossed_rows", + "xi.pt_fraction", + "k0s.pt_fraction", + "xi__k0s.delta_eta", + "xi__k0s.sin_delta_phi", + "xi__k0s.cos_delta_phi", + "xi__k0s.delta_r", + "xi__k0s.z_pt", + "xi__k0s.opening_angle", + "xi__k0s.cos_theta_star"}; +inline constexpr std::array XiK0sDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53}; +inline constexpr std::array XiK0sRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62}; +inline constexpr std::string_view XiK0sFeatureContractSha256 = "b767a5643da563246295f543ec7296c9e21b00fd921fef88acc7b19f3b24942c"; + +inline constexpr std::array Xi1530KMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "pion.tpc_nsigma_pi", + "pion.tpc_nsigma_pi_valid", + "pion.tpc_nsigma_pi_overflow", + "pion.tpc_nsigma_ka", + "pion.tpc_nsigma_ka_valid", + "pion.tpc_nsigma_ka_overflow", + "pion.tpc_nsigma_pr", + "pion.tpc_nsigma_pr_valid", + "pion.tpc_nsigma_pr_overflow", + "pion.tof_nsigma_pi", + "pion.tof_nsigma_pi_valid", + "pion.tof_nsigma_pi_overflow", + "pion.tof_nsigma_ka", + "pion.tof_nsigma_ka_valid", + "pion.tof_nsigma_ka_overflow", + "pion.tof_nsigma_pr", + "pion.tof_nsigma_pr_valid", + "pion.tof_nsigma_pr_overflow", + "pion.abs_dca_xy", + "pion.abs_dca_xy_valid", + "pion.abs_dca_xy_overflow", + "pion.abs_dca_z", + "pion.abs_dca_z_valid", + "pion.abs_dca_z_overflow", + "pion.passed_ptdep_dca_xy", + "pion.passed_ptdep_dca_z", + "pion.has_tof", + "pion.tpc_crossed_rows", + "pion.its_hit_l0", + "pion.its_hit_l1", + "pion.its_hit_l2", + "pion.its_hit_l3", + "pion.its_hit_l4", + "pion.its_hit_l5", + "pion.its_hit_l6", + "pion.is_pv_contributor", + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "xi.pt_fraction", + "pion.pt_fraction", + "kaon.pt_fraction", + "xi__pion.delta_eta", + "xi__pion.sin_delta_phi", + "xi__pion.cos_delta_phi", + "xi__pion.delta_r", + "xi__pion.z_pt", + "xi__kaon.delta_eta", + "xi__kaon.sin_delta_phi", + "xi__kaon.cos_delta_phi", + "xi__kaon.delta_r", + "xi__kaon.z_pt", + "pion__kaon.delta_eta", + "pion__kaon.sin_delta_phi", + "pion__kaon.cos_delta_phi", + "pion__kaon.delta_r", + "pion__kaon.z_pt", + "xi1530__kaon.opening_angle", + "xi1530__kaon.cos_theta_star", + "mass_xi_pi", + "mass_xi_k", + "mass_pi_k"}; +inline constexpr std::array Xi1530KDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102}; +inline constexpr std::array Xi1530KRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123}; +inline constexpr std::array Xi1530KStudyV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125}; +inline constexpr std::string_view Xi1530KFeatureContractSha256 = "ca244fe167a599f9bc60d3909630edca5c797bb7b84d75d80944e6ad1a44aec5"; +static_assert(XiK0sMasterFeatureNames.size() == XiK0sNMasterFeatures, "XiK0s feature name count must match XiK0sNMasterFeatures"); +static_assert(Xi1530KMasterFeatureNames.size() == Xi1530KNMasterFeatures, "Xi1530K feature name count must match Xi1530KNMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sDetectorV1Projection, XiK0sNMasterFeatures), "XiK0s DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sRelationalV1Projection, XiK0sNMasterFeatures), "XiK0s RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KDetectorV1Projection, Xi1530KNMasterFeatures), "Xi1530K DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KRelationalV1Projection, Xi1530KNMasterFeatures), "Xi1530K RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KStudyV1Projection, Xi1530KNMasterFeatures), "Xi1530K StudyV1 projection must be strictly increasing"); + +/// Mode A master features (XiK0s contract) +inline XiK0sFeaturePack buildXiK0sFeatures(XiK0sCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassK0Short; + using o2::constants::physics::MassXiMinus; + XiK0sFeaturePack pack; + const auto& xi = candidate.xi; + const auto& k0s = candidate.k0s; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pK0s = detail::fourMomentum(k0s.px, k0s.py, k0s.pz, MassK0Short); + const auto mother = pXi + pK0s; + const double sumPt = pXi.Pt() + pK0s.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendV0(w, k0s); + w.add(pXi.Pt() / sumPt); + w.add(pK0s.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pK0s)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(pXi, pK0s)); + w.add(detail::cosThetaStar(pXi, mother)); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +/// Mode B master features (Xi1530K contract) +inline Xi1530KFeaturePack buildXi1530KFeatures(Xi1530KCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassKaonCharged; + using o2::constants::physics::MassPionCharged; + using o2::constants::physics::MassXiMinus; + Xi1530KFeaturePack pack; + const auto& xi = candidate.xi; + const auto& pion = candidate.pion; + const auto& kaon = candidate.kaon; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + pack.status = BuildStatus::ReusedDaughter; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pPion = detail::fourMomentum(pion.px, pion.py, pion.pz, MassPionCharged); + const auto pKaon = detail::fourMomentum(kaon.px, kaon.py, kaon.pz, MassKaonCharged); + const auto xi1530 = pXi + pPion; + const auto mother = xi1530 + pKaon; + const double sumPt = pXi.Pt() + pPion.Pt() + pKaon.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendTrack(w, pion); + detail::appendTrack(w, kaon); + w.add(pXi.Pt() / sumPt); + w.add(pPion.Pt() / sumPt); + w.add(pKaon.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pPion) || !detail::appendPair(w, pXi, pKaon) || !detail::appendPair(w, pPion, pKaon)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(xi1530, pKaon)); + w.add(detail::cosThetaStar(xi1530, mother)); + w.add(xi1530.M()); + w.add((pXi + pKaon).M()); + w.add((pPion + pKaon).M()); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +template +std::vector project(FeaturePack const& pack, std::array const& indices) +{ + std::vector projected; + projected.reserve(M); + for (const auto& i : indices) { + projected.push_back(pack.master[i]); + } + return projected; +} + +inline std::vector projectFeatures(XiK0sFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, XiK0sDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, XiK0sRelationalV1Projection); + default: + return {}; // mode A has no study-only features + } +} + +inline std::vector projectFeatures(Xi1530KFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, Xi1530KDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, Xi1530KRelationalV1Projection); + case Profile::StudyV1: + return project(pack, Xi1530KStudyV1Projection); + default: + return {}; + } +} +} // namespace o2::analysis::omega2012ml + +#endif // PWGLF_CORE_OMEGA2012MLFEATURES_H_ diff --git a/PWGLF/DataModel/LFOmega2012MlTables.h b/PWGLF/DataModel/LFOmega2012MlTables.h new file mode 100644 index 00000000000..5cca99cbd46 --- /dev/null +++ b/PWGLF/DataModel/LFOmega2012MlTables.h @@ -0,0 +1,239 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFOmega2012MlTables.h +/// \brief Derived Omega(2012) training and audit tables, separate for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// Only the input-object tables (events, cascades, V0s, tracks) are common; every event row belongs to one +/// decay mode (column omDecayMode), so the object rows of the two modes never mix either. +/// +/// Cumulative pass bits (each bit requires all lower bits): +/// mode A: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 K0s selection, +/// 8 Xi-K0s kinematic (opening-angle) cut; selected = 15. +/// mode B: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 track quality of the +/// pion and the kaon, 8 TOF requirement and PID of the pion and the kaon, 16 Xi(1530) mass window; +/// selected = 31. +#ifndef PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ +#define PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace omega2012ml +{ +// Persisted relations target only these derived tables; source AO2D row numbers are scalar audit values. +// Events +DECLARE_SOA_COLUMN(OmDecayMode, omDecayMode, uint8_t); //! 0 XiK0s, 1 Xi1530K: the mode whose candidates reference this event row +DECLARE_SOA_COLUMN(OmRecoCollisionId, omRecoCollisionId, int64_t); //! row of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(OmPosZ, omPosZ, float); +DECLARE_SOA_COLUMN(OmBField, omBField, float); +DECLARE_SOA_COLUMN(OmCentrality, omCentrality, float); +DECLARE_SOA_COLUMN(OmMultiplicity, omMultiplicity, float); +DECLARE_SOA_COLUMN(OmRecINELgt0, omRecINELgt0, bool); +// Common object columns +DECLARE_SOA_COLUMN(OmSourceRow, omSourceRow, int64_t); //! row of the object in the input ResoCascades / ResoV0s table +DECLARE_SOA_COLUMN(OmPx, omPx, float); +DECLARE_SOA_COLUMN(OmPy, omPy, float); +DECLARE_SOA_COLUMN(OmPz, omPz, float); +DECLARE_SOA_COLUMN(OmV0CosPA, omV0CosPA, float); +DECLARE_SOA_COLUMN(OmV0DaughDCA, omV0DaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaPosToPV, omDcaPosToPV, float); +DECLARE_SOA_COLUMN(OmDcaNegToPV, omDcaNegToPV, float); +DECLARE_SOA_COLUMN(OmDcaV0ToPV, omDcaV0ToPV, float); +DECLARE_SOA_COLUMN(OmV0Radius, omV0Radius, float); +DECLARE_SOA_COLUMN(OmMassLambda, omMassLambda, float); +DECLARE_SOA_COLUMN(OmDecayVtxX, omDecayVtxX, float); +DECLARE_SOA_COLUMN(OmDecayVtxY, omDecayVtxY, float); +DECLARE_SOA_COLUMN(OmDecayVtxZ, omDecayVtxZ, float); +DECLARE_SOA_COLUMN(OmTpcPosPi10, omTpcPosPi10, int8_t); //! TPC nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTpcPosKa10, omTpcPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcPosPr10, omTpcPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPi10, omTpcNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegKa10, omTpcNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPr10, omTpcNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPi10, omTofPosPi10, int8_t); //! TOF nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTofPosKa10, omTofPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPr10, omTofPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPi10, omTofNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegKa10, omTofNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPr10, omTofNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsPos, omCrossedRowsPos, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsNeg, omCrossedRowsNeg, uint8_t); +// Cascade-only columns +DECLARE_SOA_COLUMN(OmSign, omSign, int8_t); +DECLARE_SOA_COLUMN(OmMassXi, omMassXi, float); +DECLARE_SOA_COLUMN(OmCascCosPA, omCascCosPA, float); +DECLARE_SOA_COLUMN(OmCascDaughDCA, omCascDaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaBachToPV, omDcaBachToPV, float); +DECLARE_SOA_COLUMN(OmDcaXYCascToPV, omDcaXYCascToPV, float); +DECLARE_SOA_COLUMN(OmDcaZCascToPV, omDcaZCascToPV, float); +DECLARE_SOA_COLUMN(OmCascRadius, omCascRadius, float); +DECLARE_SOA_COLUMN(OmTpcBachPi10, omTpcBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachKa10, omTpcBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachPr10, omTpcBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPi10, omTofBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachKa10, omTofBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPr10, omTofBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsBach, omCrossedRowsBach, uint8_t); +// The daughter-ID arrays mirror the persistent ResoCascades / ResoV0s columns. +DECLARE_SOA_COLUMN(OmCascadeIndices, omCascadeIndices, int[3]); //! source track IDs of the cascade daughters (positive, negative, bachelor) +// V0-only columns +DECLARE_SOA_COLUMN(OmMassK0Short, omMassK0Short, float); +DECLARE_SOA_COLUMN(OmMassAntiLambda, omMassAntiLambda, float); +DECLARE_SOA_COLUMN(OmArmAlpha, omArmAlpha, float); +DECLARE_SOA_COLUMN(OmArmQt, omArmQt, float); +DECLARE_SOA_COLUMN(OmV0Indices, omV0Indices, int[2]); //! source track IDs of the V0 daughters (positive, negative) +// Track columns +DECLARE_SOA_COLUMN(OmSourceTrackId, omSourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(OmPidPi, omPidPi, uint8_t); +DECLARE_SOA_COLUMN(OmPidKa, omPidKa, uint8_t); +DECLARE_SOA_COLUMN(OmPidPr, omPidPr, uint8_t); +DECLARE_SOA_COLUMN(OmSelectionFlags, omSelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(OmTrackFlags, omTrackFlags, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRows, omCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(OmItsClusterMap, omItsClusterMap, uint8_t); +// Candidate columns +DECLARE_SOA_COLUMN(OmMass, omMass, float); +DECLARE_SOA_COLUMN(OmPt, omPt, float); +DECLARE_SOA_COLUMN(OmY, omY, float); +DECLARE_SOA_COLUMN(OmEta, omEta, float); +DECLARE_SOA_COLUMN(OmPhi, omPhi, float); +DECLARE_SOA_COLUMN(OmOpeningAngle, omOpeningAngle, float); //! Xi-K0s opening angle alpha_oa of the kinematic cut +DECLARE_SOA_COLUMN(OmCharge, omCharge, int8_t); //! sign of the Xi +DECLARE_SOA_COLUMN(OmMassXiPi, omMassXiPi, float); +DECLARE_SOA_COLUMN(OmMassXiK, omMassXiK, float); +DECLARE_SOA_COLUMN(OmMassPiK, omMassPiK, float); +DECLARE_SOA_COLUMN(OmChargePattern, omChargePattern, uint8_t); //! 0 signal (pion opposite to the Xi, kaon equal), 1 wrong-sign pion, 2 wrong-sign kaon, 3 both +DECLARE_SOA_COLUMN(OmPassBits, omPassBits, uint16_t); //! cumulative pass bits of the mode, see the file header +DECLARE_SOA_COLUMN(OmXiK0sFeatures, omXiK0sFeatures, float[63]); //! master features of the XiK0s contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmXi1530KFeatures, omXi1530KFeatures, float[126]); //! master features of the Xi1530K contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmFeatureStatus, omFeatureStatus, uint8_t); //! o2::analysis::omega2012ml::BuildStatus; only Ok (0) rows are written +// Truth and generated audit +DECLARE_SOA_COLUMN(OmTruthStatus, omTruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(OmMotherPdg, omMotherPdg, int32_t); +DECLARE_SOA_COLUMN(OmMotherId, omMotherId, int64_t); +DECLARE_SOA_COLUMN(OmXiMotherPdg, omXiMotherPdg, int32_t); //! immediate-mother PDG of the Xi (any candidate) +DECLARE_SOA_COLUMN(OmV0MotherPdg, omV0MotherPdg, int32_t); //! immediate-mother PDG of the V0 (any candidate; 311 shows a K0bar intermediate) +DECLARE_SOA_COLUMN(OmXi1530Id, omXi1530Id, int64_t); //! immediate-mother ID of the Xi (the Xi(1530)0 candidate) +DECLARE_SOA_COLUMN(OmOriginalMcParticleId, omOriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(OmPdg, omPdg, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg1, omDaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg2, omDaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(OmGenChannel, omGenChannel, uint8_t); //! immediate channel of a generated Omega(2012): 0 other, 1 XiK0s, 2 Xi1530K +DECLARE_SOA_COLUMN(OmGenPt, omGenPt, float); +DECLARE_SOA_COLUMN(OmGenY, omGenY, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEvents, "AOD", "OMMLEVENT", + o2::soa::Index<>, omega2012ml::OmDecayMode, omega2012ml::OmRecoCollisionId, + omega2012ml::OmPosZ, omega2012ml::OmBField, omega2012ml::OmCentrality, + omega2012ml::OmMultiplicity, omega2012ml::OmRecINELgt0); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlEvent, omega2012MlEvent, int, Omega2012MlEvents, ""); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlCascades, "AOD", "OMMLCASCADE", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, omega2012ml::OmSign, + omega2012ml::OmMassXi, omega2012ml::OmMassLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmCascCosPA, omega2012ml::OmV0DaughDCA, omega2012ml::OmCascDaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaBachToPV, omega2012ml::OmDcaV0ToPV, + omega2012ml::OmDcaXYCascToPV, omega2012ml::OmDcaZCascToPV, + omega2012ml::OmV0Radius, omega2012ml::OmCascRadius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTpcBachPi10, omega2012ml::OmTpcBachKa10, omega2012ml::OmTpcBachPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmTofBachPi10, omega2012ml::OmTofBachKa10, omega2012ml::OmTofBachPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, omega2012ml::OmCrossedRowsBach, + omega2012ml::OmCascadeIndices); +DECLARE_SOA_TABLE(Omega2012MlV0s, "AOD", "OMMLV0", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmMassK0Short, omega2012ml::OmMassLambda, omega2012ml::OmMassAntiLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmV0DaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaV0ToPV, omega2012ml::OmV0Radius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmArmAlpha, omega2012ml::OmArmQt, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, + omega2012ml::OmV0Indices); +DECLARE_SOA_TABLE(Omega2012MlTracks, "AOD", "OMMLTRACK", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceTrackId, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmPidPi, omega2012ml::OmPidKa, omega2012ml::OmPidPr, + omega2012ml::OmSelectionFlags, omega2012ml::OmTrackFlags, + omega2012ml::OmCrossedRows, omega2012ml::OmItsClusterMap); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlCascade, omega2012MlCascade, int, Omega2012MlCascades, ""); +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlV0, omega2012MlV0, int, Omega2012MlV0s, ""); +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlPionTrack, omega2012MlPionTrack, int, Omega2012MlTracks, "_Pion"); +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlKaonTrack, omega2012MlKaonTrack, int, Omega2012MlTracks, "_Kaon"); +} // namespace omega2012ml + +// Mode A: Xi K0S +DECLARE_SOA_TABLE(Omega2012MlXiK0sCandidates, "AOD", "OMMLXIK0SCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlV0Id, + omega2012ml::OmMass, omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmOpeningAngle, omega2012ml::OmCharge, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlXiK0sCandidate, omega2012MlXiK0sCandidate, int, Omega2012MlXiK0sCandidates, ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXiK0sInputs, "AOD", "OMMLXIK0SINPUT", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmXiK0sFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXiK0sTruth, "AOD", "OMMLXIK0STRUTH", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmV0MotherPdg, omega2012ml::OmXiMotherPdg); + +// Mode B: Xi(1530)0 K- -> Xi- pi+ K- +DECLARE_SOA_TABLE(Omega2012MlXi1530KCandidates, "AOD", "OMMLXI1530KCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlPionTrackId, omega2012ml::Omega2012MlKaonTrackId, + omega2012ml::OmMass, omega2012ml::OmMassXiPi, omega2012ml::OmMassXiK, omega2012ml::OmMassPiK, + omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmChargePattern, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlXi1530KCandidate, omega2012MlXi1530KCandidate, int, Omega2012MlXi1530KCandidates, ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXi1530KInputs, "AOD", "OMMLXI1530KINP", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmXi1530KFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXi1530KTruth, "AOD", "OMMLXI1530KTRU", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmXi1530Id); + +// Generated Omega(2012) parents of selected reconstructed MC events (ResoMCParents_001) +DECLARE_SOA_TABLE(Omega2012MlGenAudit, "AOD", "OMMLGENAUDIT", + o2::soa::Index<>, omega2012ml::OmRecoCollisionId, omega2012ml::OmOriginalMcParticleId, + omega2012ml::OmPdg, omega2012ml::OmDaughterPdg1, omega2012ml::OmDaughterPdg2, + omega2012ml::OmGenChannel, omega2012ml::OmGenPt, omega2012ml::OmGenY); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index b1f09db8d9a..9c9fd4bf987 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -189,6 +189,11 @@ o2physics_add_dpl_workflow(omega2012analysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(omega2012-training-table + SOURCES omega2012TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(xi1820analysis SOURCES xi1820Analysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index cfbbab3d7d6..74146abee60 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -12,7 +12,27 @@ /// \file omega2012Analysis.cxx /// \brief Invariant Mass Reconstruction of Omega(2012) Resonance /// \author Bong-Hwi Lim - +/// +/// Two decay modes, analysed separately (never merged in any histogram or process function): +/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGenerated. +/// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged). +/// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track. +/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMixedMicro. +/// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls). +/// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h. +/// +/// Configuration keys: all mode-A keys are unchanged. The former three-body (Xi pi K0S) process functions +/// processThreeBodyWithTracks / processThreeBodyWithMicroTracks are removed (that final state cannot be an Omega(2012)-). +/// Mode-B pion/kaon track selection: the common resonance TrackCuts with the prefix "trk.": +/// cPionPtMin -> trk.cMinPtcut, cPionEtaMax -> trk.cMaxEtacut, cPionDCAxyMax -> trk.cMaxDCArToPVcut, +/// cPionDCAzMax -> trk.cMaxDCAzToPVcut (default 0.15 cm), cPionTPCNClusMin -> trk.cfgTPCcluster; +/// further trk.* keys as in PWGLF/Core/ResoAnalysisSelectionCore.h. +/// Pion PID keys cPion* are unchanged; the kaon PID keys cKaon* have the same shape. +/// New mode-B keys: cXi1530UseMassWindow, cXi1530KFillWrongSign, cByPassTOF. + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" #include @@ -25,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -33,28 +54,24 @@ #include #include -#include #include -#include -#include -#include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::constants::physics; +using namespace o2::analysis::omega2012; struct Omega2012Analysis { // Constants - static constexpr float kSmallNumber = 1e-10f; // Small number to avoid division by zero - static constexpr float kMaxDCAV0ToPV = 1.0f; // Maximum DCA of V0 to PV - static constexpr int kNumExpectedDaughters = 2; // Expected number of daughters for 2-body decay + static constexpr int NumExpectedDaughters = 2; // Expected number of daughters for 2-body decay SliceCache cache; Preslice perResoCollisionCasc = aod::resodaughter::resoCollisionId; Preslice perResoCollisionV0 = aod::resodaughter::resoCollisionId; Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; - Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Axes @@ -62,109 +79,39 @@ struct Omega2012Analysis { ConfigurableAxis binsPtQA{"binsPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pT (QA)"}; ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 1., 5., 10., 30., 50., 70., 100., 110.}, "Centrality"}; - // Invariant mass range for Omega(2012) → Xi + K0s + // Invariant mass range for Omega(2012) Configurable cInvMassStart{"cInvMassStart", 1.6, "Invariant mass start (GeV/c^2)"}; Configurable cInvMassEnd{"cInvMassEnd", 2.2, "Invariant mass end (GeV/c^2)"}; Configurable cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"}; - // Basic pre-selections (mirroring refs) - Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; - Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; - Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply Reco INEL>0 event selection"}; - Configurable cMCINELgt0{"cMCINELgt0", false, "Require generator INEL>0 in MC processes"}; - Configurable cMCVtxIn10{"cMCVtxIn10", false, "Require generator |vertex z| < 10 cm in MC processes"}; - Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; - Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; - Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; - Configurable> cKinUpperCutsAlpha{"cKinUpperCutsAlpha", {3.0, 2.0, 1.5, 1.4, 1.0, 0.8, 0.6, 0.5, 0.45, 0.35, 0.3, 0.25, 0.2}, "Upper cut on Xi-K0s opening angle"}; - // V0 selections (K0s) - Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; - Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; - Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; - Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; - - // Xi (cascade) selections from xi1530Analysisqa.cxx - Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; - Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; - Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; - - Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; - - // V0 topology inside cascade (Λ) - Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; - Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; - Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; - Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; - Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; - Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; - Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; - Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; - Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; - - // Cascade topology - Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; - Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; - Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; - Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; - Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; - Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; - Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; - Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; - Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; - Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG + // Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.") + o2::analysis::resonance::EventCuts eventCuts; + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Mixing bins - centrality"}; - // Enhanced K0s selections - Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; - Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; - Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha coefficient"}; - Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; - Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.05, "K0s negative daughter DCA to PV min"}; - Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; - Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; - Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; - Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; - Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; - Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; - Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; - - // Pion track selections for 3-body decay - Configurable cPionPtMin{"cPionPtMin", 0.15, "Minimum pion pT"}; - Configurable cPionEtaMax{"cPionEtaMax", 0.8, "Maximum pion |eta|"}; - Configurable cPionDCAxyMax{"cPionDCAxyMax", 0.1, "Maximum pion DCAxy to PV"}; - Configurable cPionDCAzMax{"cPionDCAzMax", 0.2, "Maximum pion DCAz to PV"}; - Configurable cPionTPCNClusMin{"cPionTPCNClusMin", 70, "Minimum TPC clusters for pion"}; - - // Pion PID selections - Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; - Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; - Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; - Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; - Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; - - // Xi1530 mass window cut - Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; - Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; - - // PDG masses - double massK0 = MassK0Short; - - // Module-initializer collision tables (matches the resonance-module-initializer producer) + // Module-initializer collision and daughter tables (matches the resonance-module-initializer producer) using ResoCollisions = aod::ResoCollisions_001; using ResoMCCollisions = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCMicroTracks = soa::Join; using BinningTypeVertexContributor = ColumnBinningPolicy; BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; + Omega2012AnalysisCore core; + void init(InitContext&) { AxisSpec centAxis = {binsCent, "V0M (%)"}; @@ -261,27 +208,6 @@ struct Omega2012Analysis { histos.add("QAbefore/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} before kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); histos.add("QAafter/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} after kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); - // 3-body decay: Xi + pi + K0s - histos.add("omega2012_3body/invmass", "Invariant mass of Omega(2012) → Xi + #pi + K^{0}_{S}", kTH1F, {invMassAxis}); - histos.add("omega2012_3body/massPtCent", "Omega(2012) 3-body mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - - // Pion QA histograms for 3-body - histos.add("QAbefore/pionPt", "Pion pT before cuts", kTH1F, {ptAxisQA}); - histos.add("QAbefore/pionEta", "Pion eta before cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAbefore/pionDCAxy", "Pion DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAbefore/pionDCAz", "Pion DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAbefore/pionTPCNcls", "Pion TPC clusters before cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - - histos.add("QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); - histos.add("QAafter/pionEta", "Pion eta after cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAafter/pionTPCNcls", "Pion TPC clusters after cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - // MC truth histograms AxisSpec etaAxis = {100, -2.0, 2.0, "#eta"}; AxisSpec rapidityAxis = {100, -2.0, 2.0, "y"}; @@ -301,590 +227,177 @@ struct Omega2012Analysis { histos.add("MC/hMCRecK0sPt", "MC Reconstructed K0s pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueXiPt", "MC True Xi pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueK0sPt", "MC True K0s pT", kTH1F, {ptAxis}); - } - - template - static std::array cascadeDaughterIds(const CascT& xi) - { - auto indices = xi.cascadeIndices(); - return {indices[0], indices[1], indices[2]}; - } - template - static std::array v0DaughterIds(const V0Type& v0) - { - auto indices = v0.indices(); - return {indices[0], indices[1]}; - } - - template - static bool sharesAnyDaughterId(const std::array& first, const std::array& second) - { - for (const auto& firstId : first) { - for (const auto& secondId : second) { - if (firstId == secondId) { - return true; - } + ProcessModes modes; + modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC; + modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.mcReco = doprocessMC || doprocessXi1530KMCMicro; + modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes); + + if (modes.xi1530K) { + AxisSpec xiPiMassAxis = {300, 1.4, 1.7, "M_{#Xi#pi} (GeV/#it{c}^{2})"}; + AxisSpec patternAxis = {3, 0.5, 3.5, "charge pattern (1: wrong-sign #pi, 2: wrong-sign K, 3: both)"}; + AxisSpec etaAxisQA = {100, -2.0, 2.0, "#eta"}; + + // Signal charge pattern: Xi- pi+ K- and Xi+ pi- K+ + histos.add("xi1530K/invmass", "Invariant mass of Omega(2012) → #Xi(1530)^{0} K → #Xi #pi K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent", "Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/invmass_Mix", "Mixed event invariant mass of Omega(2012) → #Xi(1530)^{0} K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent_Mix", "Mixed event Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/massXiPi", "#Xi #pi mass of selected candidates (before the rapidity cut)", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/massXiPiVsMass", "#Xi #pi mass vs #Xi #pi K mass", kTH2F, {invMassAxis, xiPiMassAxis}); + + // QA of the mode-B inputs (once per object and collision) + histos.add("xi1530K/QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAbefore/xiPt", "Xi pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/xiEta", "Xi eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/xiMass", "Xi mass after cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAafter/xiPt", "Xi pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/xiEta", "Xi eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackPt", "Track pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/trackEta", "Track eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackDCAxy", "Track DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAbefore/trackDCAz", "Track DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTPCNSigma", "Kaon TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTOFNSigma", "Kaon TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/pionEta", "Pion eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonPt", "Kaon pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/kaonEta", "Kaon eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/kaonDCAxy", "Kaon DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/kaonDCAz", "Kaon DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/kaonTPCNSigma", "Kaon TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonTOFNSigma", "Kaon TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + + // Charge-pattern controls (never part of the signal) + histos.add("xi1530K_wrongSign/invmassPattern", "Wrong-sign #Xi #pi K mass by charge pattern", kTH2F, {patternAxis, invMassAxis}); + histos.add("xi1530K_wrongSign/massPtPattern", "Wrong-sign #Xi #pi K mass vs pT by charge pattern", kTH3F, {invMassAxis, ptAxis, patternAxis}); + + if (doprocessXi1530KMCMicro) { + histos.add("xi1530K/MC/hMCRecOmega2012Pt", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecOmega2012PtEta", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT vs eta", kTH2F, {ptAxis, etaAxis}); + histos.add("xi1530K/MC/hMCRecMass", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K mass", kTH1F, {invMassAxis}); + histos.add("xi1530K/MC/hMCRecMassXiPi", "MC reconstructed #Xi #pi mass", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis}); } } - return false; - } - template - static bool sharesDaughterId(const std::array& daughters, int trackId) - { - for (const auto& daughterId : daughters) { - if (daughterId == trackId) { - return true; - } - } - return false; + LOG(info) << "Size of the histograms in Omega(2012) analysis task"; + histos.print(); } - template - static int trackSourceId(const TrackT& track, const TrackIdsT& trackIds) - { - auto rowIndex = track.globalIndex(); - if (rowIndex >= 0 && rowIndex < trackIds.size()) { - return trackIds.rawIteratorAt(rowIndex).trackId(); - } - return rowIndex; - } - - template - bool kinCuts(const FirstVecT& firstDaughter, const SecondVecT& secondDaughter, const MotherVecT& mother, float& alpha) - { - auto firstP = std::sqrt(firstDaughter.Px() * firstDaughter.Px() + firstDaughter.Py() * firstDaughter.Py() + firstDaughter.Pz() * firstDaughter.Pz()); - auto secondP = std::sqrt(secondDaughter.Px() * secondDaughter.Px() + secondDaughter.Py() * secondDaughter.Py() + secondDaughter.Pz() * secondDaughter.Pz()); - if (firstP < kSmallNumber || secondP < kSmallNumber) { - alpha = 0.f; - return false; - } - - auto cosAlpha = (firstDaughter.Px() * secondDaughter.Px() + firstDaughter.Py() * secondDaughter.Py() + firstDaughter.Pz() * secondDaughter.Pz()) / (firstP * secondP); - if (cosAlpha > 1.) { - cosAlpha = 1.; - } else if (cosAlpha < -1.) { - cosAlpha = -1.; - } - alpha = std::acos(cosAlpha); - - std::vector kinCutsPt = static_cast>(cKinCutsPt); - std::vector kinLowerCutsAlpha = static_cast>(cKinLowerCutsAlpha); - std::vector kinUpperCutsAlpha = static_cast>(cKinUpperCutsAlpha); - - int kinCutsSize = static_cast(kinUpperCutsAlpha.size()); - if (kinCutsSize > static_cast(kinLowerCutsAlpha.size())) { - kinCutsSize = static_cast(kinLowerCutsAlpha.size()); - } - if (kinCutsSize > static_cast(kinCutsPt.size()) - 1) { - kinCutsSize = static_cast(kinCutsPt.size()) - 1; - } - - for (int i = 0; i < kinCutsSize; ++i) { - if ((mother.Pt() > kinCutsPt[i] && mother.Pt() <= kinCutsPt[i + 1]) && (alpha < kinLowerCutsAlpha[i] || alpha > kinUpperCutsAlpha[i])) { - return false; - } - } - - return true; - } - - // Enhanced V0 selection (K0s) with detailed criteria - template - bool v0CutEnhanced(const CollisionType& collision, const V0Type& v0) - { - // Basic kinematic cuts - if (std::abs(v0.eta()) > cMaxV0Etacut) - return false; - if (v0.pt() < cMinPtcut) - return false; - - // Topological cuts - if (v0.v0CosPA() < cK0sMinCosPA) - return false; - if (v0.daughDCA() > cK0sMaxDaughDCA) - return false; - - // Enhanced selections from chk892Flow - // Daughter DCA to PV cuts - if (std::abs(v0.dcapostopv()) < cK0sDauPosDCAtoPVMin) - return false; - if (std::abs(v0.dcanegtopv()) < cK0sDauNegDCAtoPVMin) - return false; - - // Radius cuts - use transRadius instead of v0radius - auto radius = v0.transRadius(); - if (radius < cK0sRadiusMin || radius > cK0sRadiusMax) - return false; - - // DCA to PV - if (std::abs(v0.dcav0topv()) > kMaxDCAV0ToPV) - return false; // max DCA to PV - - // Proper lifetime cut - calculate manually - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallNumber)) * MassK0Short; - if (properLifetime > cK0sProperLifetimeMax) - return false; - - if (v0.qtarm() < cK0sArmenterosQtMin) - return false; - - // Mass window - if (std::abs(v0.mK0Short() - MassK0Short) > cK0sMassWindow) - return false; - - // Competing V0 rejection: remove (Anti)Λ - if (cK0sCrossMassRejection) { - if (std::abs(v0.mLambda() - MassLambda) < cK0sCrossMassRejectionWindow) - return false; - if (std::abs(v0.mAntiLambda() - MassLambda) < cK0sCrossMassRejectionWindow) - return false; - } - - if (std::abs(v0.daughterTPCNSigmaPosPi()) >= cK0sDaughterPiTPCNSigmaMax) - return false; - if (std::abs(v0.daughterTPCNSigmaNegPi()) >= cK0sDaughterPiTPCNSigmaMax) - return false; - - if (v0.nCrossedRowsPos() <= cK0sPosDaughterMinCrossedRows) - return false; - if (v0.nCrossedRowsNeg() <= cK0sNegDaughterMinCrossedRows) - return false; - - if (v0.qtarm() < cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) - return false; - - return true; - } - - // Helper function to find pT bin index - int getPtBinIndex(float pt) - { - auto ptBins = static_cast>(cPionPIDPtBins); - for (size_t i = 0; i < ptBins.size() - 1; i++) { - if (pt >= ptBins[i] && pt < ptBins[i + 1]) { - return i; - } - } - return -1; - } - - // Pion PID selection - template - bool pionPidCut(const TrackType& track) - { - float pt = track.pt(); - - if constexpr (IsResoMicrotrack) { - // For ResoMicroTracks - decode PID from flags - float tpcNSigma = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(track.pidNSigmaPiFlag()); - float tofNSigma = track.hasTOF() ? o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(track.pidNSigmaPiFlag()) : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; - } - } else { - // For ResoTracks - direct access - float tpcNSigma = track.tpcNSigmaPi(); - float tofNSigma = track.hasTOF() ? track.tofNSigmaPi() : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; - } - } - } - - // Pion track selection (for both ResoTracks and ResoMicroTracks) - template - bool pionCut(const TrackType& track) - { - // Basic kinematic cuts - if (track.pt() < cPionPtMin) - return false; - if (std::abs(track.eta()) > cPionEtaMax) - return false; - - // DCA cuts - different access for ResoMicroTracks - if constexpr (IsResoMicrotrack) { - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cPionDCAxyMax) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cPionDCAzMax) - return false; - } else { - if (std::abs(track.dcaXY()) > cPionDCAxyMax) - return false; - if (std::abs(track.dcaZ()) > cPionDCAzMax) - return false; - } - - // Track quality cuts - only for ResoTracks - if constexpr (!IsResoMicrotrack) { - if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cPionTPCNClusMin) - return false; - } - } - - // PID selection - if (!pionPidCut(track)) - return false; - - return true; - } - - // Xi1530 mass window cut - template - bool xi1530MassCut(const XiType& xi, const PionType& pion) - { - // Calculate Xi + pion invariant mass - ROOT::Math::PxPyPzEVector pXi, pPion, pXi1530; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); - pXi1530 = pXi + pPion; - - // Check if mass is within Xi(1530) window - float massDiff = std::abs(pXi1530.M() - cXi1530Mass); - return massDiff < cXi1530MassWindow; - } - - // Primary-level cascade kinematics - template - bool cascprimaryTrackCut(const CascT& c) - { - if (std::abs(c.eta()) > cMaxEtaCut) - return false; - if (std::abs(c.pt()) < cMinPtcut) - return false; - if (cDCAxyToPVAsPtForCasc) { - if (std::abs(c.dcaXYCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * c.pt())) - return false; - } - if (cDCAzToPVAsPtForCasc) { - if (std::abs(c.dcaZCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * std::pow(c.pt(), -1.1f))) - return false; - } - return true; - } - - // Cascade topological selections adapted from xi1530Analysisqa - template - bool casctopCut(const CascT& c) - { - // V0 (Λ) topology inside cascade - if (std::abs(c.daughDCA()) > cDCALambdaDaugtherscut) - return false; - if (std::abs(c.dcav0topv()) < cDCALambdaToPVcut) - return false; - - if (c.sign() < 0) { // Xi- - if (std::abs(c.dcanegtopv()) < cDCAPionToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAProtonToPVcut) - return false; - } else { // Anti-Xi - if (std::abs(c.dcanegtopv()) < cDCAProtonToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAPionToPVcut) - return false; - } - - if (c.v0CosPA() < std::cos(cV0CosPACutPtDepP0 - cV0CosPACutPtDepP1 * c.pt())) - return false; - if (c.transRadius() > cMaxV0radiuscut || c.transRadius() < cMinV0radiuscut) - return false; - if (std::abs(c.mLambda() - MassLambda) > cMasswindowV0cut) - return false; - - // Cascade-level topology - if (std::abs(c.dcabachtopv()) < cDCABachlorToPVcut) - return false; - - if (c.pt() < cDCAXiDaugthersCutPtRangeLower) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepLower) - return false; - } else if (c.pt() < cDCAXiDaugthersCutPtRangeUpper) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepMiddle) - return false; - } else { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepUpper) - return false; - } - - if (c.cascCosPA() < std::cos(cCosPACascCutPtDepP0 - cCosPACascCutPtDepP1 * c.pt())) - return false; - if (c.cascTransRadius() > cMaxCascradiuscut || c.cascTransRadius() < cMinCascradiuscut) - return false; - if (std::abs(c.mXi() - cMassXiminus) > cMasswindowCasccut) - return false; - - return true; - } - - template - struct SelectedXiCandidate { - CandidateT candidate; - std::array daughterIds{}; - }; - - template - struct SelectedK0sCandidate { - CandidateT candidate; - std::array daughterIds{}; - }; - - template - auto selectK0sCandidates(const CollisionT& collision, const V0sT& v0s, int& nV0sAfterCuts) - { - using V0Candidate = std::decay_t; - std::vector> selectedK0s; - selectedK0s.reserve(v0s.size()); - - for (const auto& v0 : v0s) { - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallNumber)) * MassK0Short; - - if constexpr (FillQA) { - histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); - histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); - histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAbefore/k0sArmenteros"), v0.alpha(), v0.qtarm()); - histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); - histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); - histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); - histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); - } - - if (!v0CutEnhanced(collision, v0)) - continue; - - if constexpr (FillQA) { - nV0sAfterCuts++; - histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAafter/k0sPt"), v0.pt()); - histos.fill(HIST("QAafter/k0sEta"), v0.eta()); - histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAafter/k0sArmenteros"), v0.alpha(), v0.qtarm()); - histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); - histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); - histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); - histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); - } - - selectedK0s.push_back({v0, v0DaughterIds(v0)}); - } - - return selectedK0s; - } - - template - auto selectXiCandidates(const CascadesT& cascades, int& nCascAfterCuts) - { - using XiCandidate = std::decay_t; - std::vector> selectedXis; - selectedXis.reserve(cascades.size()); - - for (const auto& xi : cascades) { - if constexpr (FillQA) { - histos.fill(HIST("QAbefore/xiMass"), xi.mXi()); - histos.fill(HIST("QAbefore/xiPt"), xi.pt()); - histos.fill(HIST("QAbefore/xiEta"), xi.eta()); - histos.fill(HIST("QAbefore/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); - histos.fill(HIST("QAbefore/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); - histos.fill(HIST("QAbefore/xiV0CosPA"), xi.pt(), xi.v0CosPA()); - histos.fill(HIST("QAbefore/xiCascCosPA"), xi.pt(), xi.cascCosPA()); - histos.fill(HIST("QAbefore/xiV0Radius"), xi.pt(), xi.transRadius()); - histos.fill(HIST("QAbefore/xiCascRadius"), xi.pt(), xi.cascTransRadius()); - histos.fill(HIST("QAbefore/xiV0DauDCA"), xi.pt(), xi.daughDCA()); - histos.fill(HIST("QAbefore/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); - } - - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - if constexpr (FillQA) { - nCascAfterCuts++; - histos.fill(HIST("QAafter/xiMass"), xi.mXi()); - histos.fill(HIST("QAafter/xiPt"), xi.pt()); - histos.fill(HIST("QAafter/xiEta"), xi.eta()); - histos.fill(HIST("QAafter/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); - histos.fill(HIST("QAafter/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); - histos.fill(HIST("QAafter/xiV0CosPA"), xi.pt(), xi.v0CosPA()); - histos.fill(HIST("QAafter/xiCascCosPA"), xi.pt(), xi.cascCosPA()); - histos.fill(HIST("QAafter/xiV0Radius"), xi.pt(), xi.transRadius()); - histos.fill(HIST("QAafter/xiCascRadius"), xi.pt(), xi.cascTransRadius()); - histos.fill(HIST("QAafter/xiV0DauDCA"), xi.pt(), xi.daughDCA()); - histos.fill(HIST("QAafter/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); - } - - selectedXis.push_back({xi, cascadeDaughterIds(xi)}); - } - - return selectedXis; - } - - template - void fill(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) + // Mode A, same event (data): event QA, Xi and K0S QA, kinematic-cut QA and the Xi K0S mass + template + void fillXiK0s(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) { auto cent = collision.cent(); - // Fill event QA histograms (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/posZ"), collision.posZ()); - histos.fill(HIST("Event/centrality"), cent); - histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); - histos.fill(HIST("Event/nCascades"), cascades.size()); - histos.fill(HIST("Event/nV0s"), v0s.size()); - } + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centrality"), cent); + histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); + histos.fill(HIST("Event/nCascades"), cascades.size()); + histos.fill(HIST("Event/nV0s"), v0s.size()); // Count candidates after cuts int nCascAfterCuts = 0; int nV0sAfterCuts = 0; - auto selectedK0s = selectK0sCandidates(collision, v0s, nV0sAfterCuts); - auto selectedXis = selectXiCandidates(cascades, nCascAfterCuts); - - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - // Build Xi + K0s - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - - if constexpr (!IsMix) { - if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) { - continue; - } - } - - // 4-vectors - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - float alpha = 0.f; - bool kinCutFlag = true; - if (cKinCuts) { - kinCutFlag = kinCuts(pXi, pK0s, pRes, alpha); - if constexpr (!IsMix) { - histos.fill(HIST("QAbefore/omegaAlphaVsPt"), pRes.Pt(), alpha); - } - } - - if (cKinCuts && !kinCutFlag) - continue; - - if constexpr (!IsMix) { - if (cKinCuts) { - histos.fill(HIST("QAafter/omegaAlphaVsPt"), pRes.Pt(), alpha); - } - } - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - if constexpr (!IsMix) { - histos.fill(HIST("omega2012/invmass"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent"), pRes.M(), pRes.Pt(), cent); - } else { - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + auto onK0s = [&](auto const& v0, double properLifetime, bool selected) { + histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); + histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); + histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAbefore/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + if (!selected) { + return; + } + nV0sAfterCuts++; + histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAafter/k0sPt"), v0.pt()); + histos.fill(HIST("QAafter/k0sEta"), v0.eta()); + histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAafter/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + }; + + auto onXi = [&](auto const& xi, bool selected) { + histos.fill(HIST("QAbefore/xiMass"), xi.mXi()); + histos.fill(HIST("QAbefore/xiPt"), xi.pt()); + histos.fill(HIST("QAbefore/xiEta"), xi.eta()); + histos.fill(HIST("QAbefore/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); + histos.fill(HIST("QAbefore/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); + histos.fill(HIST("QAbefore/xiV0CosPA"), xi.pt(), xi.v0CosPA()); + histos.fill(HIST("QAbefore/xiCascCosPA"), xi.pt(), xi.cascCosPA()); + histos.fill(HIST("QAbefore/xiV0Radius"), xi.pt(), xi.transRadius()); + histos.fill(HIST("QAbefore/xiCascRadius"), xi.pt(), xi.cascTransRadius()); + histos.fill(HIST("QAbefore/xiV0DauDCA"), xi.pt(), xi.daughDCA()); + histos.fill(HIST("QAbefore/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + if (!selected) { + return; + } + nCascAfterCuts++; + histos.fill(HIST("QAafter/xiMass"), xi.mXi()); + histos.fill(HIST("QAafter/xiPt"), xi.pt()); + histos.fill(HIST("QAafter/xiEta"), xi.eta()); + histos.fill(HIST("QAafter/xiDCAxy"), xi.pt(), xi.dcaXYCascToPV()); + histos.fill(HIST("QAafter/xiDCAz"), xi.pt(), xi.dcaZCascToPV()); + histos.fill(HIST("QAafter/xiV0CosPA"), xi.pt(), xi.v0CosPA()); + histos.fill(HIST("QAafter/xiCascCosPA"), xi.pt(), xi.cascCosPA()); + histos.fill(HIST("QAafter/xiV0Radius"), xi.pt(), xi.transRadius()); + histos.fill(HIST("QAafter/xiCascRadius"), xi.pt(), xi.cascTransRadius()); + histos.fill(HIST("QAafter/xiV0DauDCA"), xi.pt(), xi.daughDCA()); + histos.fill(HIST("QAafter/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); + }; + + const bool kinCutsOn = core.kinCutsEnabled(); + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn) { + histos.fill(HIST("QAbefore/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); + if (!c.passesKinCut) { + return; } + histos.fill(HIST("QAafter/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + }; - // Fill event QA for after-cuts counters (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); - histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); - } + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, false, onXi, onK0s, onCandidate); + + histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); + histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); } void processDummy(aod::ResoCollision const& /*collision*/) @@ -897,10 +410,10 @@ struct Omega2012Analysis { aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision)) { return; - - fill(collision, resocasc, resov0s); + } + fillXiK0s(collision, resocasc, resov0s); } PROCESS_SWITCH(Omega2012Analysis, processData, "Process Event for data", false); @@ -908,98 +421,53 @@ struct Omega2012Analysis { aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - auto cascV0sTuple = std::make_tuple(resocasc, resov0s); Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; + const bool kinCutsOn = core.kinCutsEnabled(); for (const auto& [collision1, casc1, collision2, v0s2] : pairs) { - if (cRecoINELgt0 && (!collision1.isRecINELgt0() || !collision2.isRecINELgt0())) + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { continue; - + } auto cent = collision1.cent(); - int unusedXiCount = 0; - int unusedK0sCount = 0; - auto selectedXis = selectXiCandidates(casc1, unusedXiCount); - auto selectedK0s = selectK0sCandidates(collision2, v0s2, unusedK0sCount); - - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - float alpha = 0.f; - bool kinCutFlag = true; - if (cKinCuts) { - kinCutFlag = kinCuts(pXi, pK0s, pRes, alpha); - } - - if (cKinCuts && !kinCutFlag) - continue; - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + // Xi from collision 1, K0s from collision 2 (selected with the vertex of collision 2) + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn && !c.passesKinCut) { + return; } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass_Mix"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); + }; + core.forEachXiK0sCandidate(histos, collision1, collision2, casc1, v0s2, false, nullptr, nullptr, onCandidate); } } PROCESS_SWITCH(Omega2012Analysis, processMixedEvent, "Process Mixed Event", false); // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother void processMC(ResoMCCollisions::iterator const& collision, - soa::Join const& resocasc, - soa::Join const& resov0s) + ResoMCCascades const& resocasc, + ResoMCV0s const& resov0s) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; - if (cMCINELgt0 && !collision.isINELgt0()) - return; - if (cMCVtxIn10 && !collision.isVtxIn10()) - return; - - int nCascAfterCuts = 0; - int nV0sAfterCuts = 0; - auto selectedXis = selectXiCandidates(resocasc, nCascAfterCuts); - auto selectedK0s = selectK0sCandidates(collision, resov0s, nV0sAfterCuts); - - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - if (std::abs(xi.pdgCode()) != kXiMinus) - continue; - - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - - if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) - continue; - if (std::abs(v0.pdgCode()) != kK0Short) - continue; - if (xi.motherId() < 0 || xi.motherId() != v0.motherId()) - continue; - if (std::abs(xi.motherPDG()) != kOmega2012Minus || xi.motherPDG() != v0.motherPDG()) - continue; - - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - histos.fill(HIST("MC/hMCRecOmega2012Pt"), pRes.Pt()); - histos.fill(HIST("MC/hMCRecOmega2012PtEta"), pRes.Pt(), pRes.Eta()); - histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); - histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); - } } + // No kinematic cut in the truth-matched spectra (as before the refactoring) + auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) { + if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) { + return; + } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + }; + core.forEachXiK0sCandidate(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate); } PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); @@ -1012,8 +480,9 @@ struct Omega2012Analysis { // Look for Omega(2012) int pdg = mcParticle.pdgCode(); - if (std::abs(pdg) != kOmega2012Minus) + if (std::abs(pdg) != kOmega2012Minus) { continue; + } // Fill generated level histograms auto pt = mcParticle.pt(); @@ -1026,8 +495,9 @@ struct Omega2012Analysis { // Get daughters auto daughters = mcParticle.daughters_as(); - if (daughters.size() != kNumExpectedDaughters) + if (daughters.size() != NumExpectedDaughters) { continue; + } int daughter1PDG = 0, daughter2PDG = 0; ROOT::Math::PxPyPzEVector p1, p2, pMother; @@ -1066,134 +536,155 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles", false); - // Fill function for 3-body decay analysis - template - void fillThreeBody(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s, const TracksT& tracks, const TrackIdsT& trackIds) + // Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions + template + void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds) { - auto cent = collision.cent(); - int unusedXiCount = 0; - int unusedK0sCount = 0; - auto selectedXis = selectXiCandidates(cascades, unusedXiCount); - auto selectedK0s = selectK0sCandidates(collision, v0s, unusedK0sCount); - - // First loop: xi + pion to check xi1530 mass window - for (const auto& selectedXi : selectedXis) { - const auto& xi = selectedXi.candidate; - - for (const auto& pion : tracks) { - auto pionTrackId = trackSourceId(pion, trackIds); - if (sharesDaughterId(selectedXi.daughterIds, pionTrackId)) { - continue; + // Input QA: same event only (each object once per collision) + auto onXi = [&](auto const& xi, bool selected) { + if constexpr (IsMix) { + return; + } + histos.fill(HIST("xi1530K/QAbefore/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAbefore/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAbefore/xiEta"), xi.eta()); + if (!selected) { + return; + } + histos.fill(HIST("xi1530K/QAafter/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAafter/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAafter/xiEta"), xi.eta()); + }; + + auto onTrack = [&](auto const& track, int pionStage, int kaonStage) { + if constexpr (IsMix) { + return; + } + const bool hasTOF = track.hasTOF(); + histos.fill(HIST("xi1530K/QAbefore/trackPt"), track.pt()); + histos.fill(HIST("xi1530K/QAbefore/trackEta"), track.eta()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAbefore/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAbefore/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + if (pionStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/pionPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/pionEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/pionDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/pionDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); } - - // Pion QA before cuts - histos.fill(HIST("QAbefore/pionPt"), pion.pt()); - histos.fill(HIST("QAbefore/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAbefore/pionTPCNcls"), pion.tpcNClsFound()); - } + } + if (kaonStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/kaonPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/kaonEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); } + } + }; - if (!pionCut(pion)) - continue; - - // Pion QA after cuts - histos.fill(HIST("QAafter/pionPt"), pion.pt()); - histos.fill(HIST("QAafter/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAafter/pionTPCNcls"), pion.tpcNClsFound()); + const bool fillWrongSign = core.fillWrongSign(); + auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) { + if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) { + // Charge-pattern controls: same event only, never in the signal histograms + if constexpr (!IsMix) { + if (fillWrongSign && c.inRapidity) { + histos.fill(HIST("xi1530K_wrongSign/invmassPattern"), c.chargePattern, c.omega.M()); + histos.fill(HIST("xi1530K_wrongSign/massPtPattern"), c.omega.M(), c.omega.Pt(), c.chargePattern); } } - - // Check xi1530 mass window cut - if (!xi1530MassCut(xi, pion)) - continue; - - // Second loop: v0 for the selected xi-pion pair - for (const auto& selectedK0 : selectedK0s) { - const auto& v0 = selectedK0.candidate; - if (sharesAnyDaughterId(selectedXi.daughterIds, selectedK0.daughterIds)) { - continue; - } - if (sharesDaughterId(selectedK0.daughterIds, pionTrackId)) { - continue; - } - - // 4-vectors for 3-body decay: Xi + K0s + pion - ROOT::Math::PxPyPzEVector pXi, pK0s, pPion, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); - - pRes = pXi + pK0s + pPion; - - if (std::abs(pRes.Rapidity()) >= cfgRapidityCut) - continue; - - histos.fill(HIST("omega2012_3body/invmass"), pRes.M()); - histos.fill(HIST("omega2012_3body/massPtCent"), pRes.M(), pRes.Pt(), cent); + return; + } + if constexpr (IsMix) { + if (c.inRapidity) { + histos.fill(HIST("xi1530K/invmass_Mix"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); } + return; } - } + histos.fill(HIST("xi1530K/massXiPi"), c.massXiPi); + if (!c.inRapidity) { + return; + } + histos.fill(HIST("xi1530K/invmass"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + histos.fill(HIST("xi1530K/massXiPiVsMass"), c.omega.M(), c.massXiPi); + if constexpr (IsMC) { + if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) { + return; + } + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M()); + histos.fill(HIST("xi1530K/MC/hMCRecMassXiPi"), c.massXiPi); + histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt()); + } + }; + + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, trackIds, false, onXi, onTrack, onCandidate); } - // 3-body decay analysis: Xi + pi + K0s with ResoTracks - void processThreeBodyWithTracks(ResoCollisions::iterator const& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoTracks const& resotracks, - aod::ResoTrackTracks const& resotrackids) + void processXi1530KMicro(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision)) { return; + } + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMicro, "Process Xi(1530)0 K mode with ResoMicroTracks_001", false); - fillThreeBody(collision, resocasc, resov0s, resotracks, resotrackids); + void processXi1530KTracks(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + aod::ResoTracks const& resotracks, + aod::ResoTrackTracks const& resotrackids) + { + if (!core.passesEventCuts(collision)) { + return; + } + fillXi1530K(collision, collision.cent(), resocasc, resotracks, resotrackids); } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithTracks, "Process 3-body decay with ResoTracks", false); - - // 3-body decay analysis: Xi + pi + K0s with ResoMicroTracks - void processThreeBodyWithMicroTracks(ResoCollisions::iterator const& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoMicroTracks const& resomicrotracks, - aod::ResoMicroTrackTracks const& resomicrotrackids) + PROCESS_SWITCH(Omega2012Analysis, processXi1530KTracks, "Process Xi(1530)0 K mode with ResoTracks", false); + + void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision, + ResoMCCascades const& resocasc, + ResoMCMicroTracks const& resomicrotracks) { - if (cRecoINELgt0 && !collision.isRecINELgt0()) + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; + } + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false); - fillThreeBody(collision, resocasc, resov0s, resomicrotracks, resomicrotrackids); + // Mixed events: Xi from collision 1, pion and kaon from collision 2 + void processXi1530KMixedMicro(ResoCollisions const& collisions, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) + { + auto cascTracksTuple = std::make_tuple(resocasc, resomicrotracks); + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascTracksTuple, &cache}; + for (const auto& [collision1, casc1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } + fillXi1530K(collision1, collision1.cent(), casc1, tracks2, nullptr); + } } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithMicroTracks, "Process 3-body decay with ResoMicroTracks", false); + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMixedMicro, "Process mixed events of the Xi(1530)0 K mode (ResoMicroTracks_001)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx new file mode 100644 index 00000000000..18502ccedb1 --- /dev/null +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -0,0 +1,404 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file omega2012TrainingTable.cxx +/// \brief Derived Omega(2012) training tables, separately for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): processXiK0s, processXiK0sMC. Mode B (Xi1530K, Xi- pi+ K- with micro tracks): +/// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. +/// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); +/// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFOmega2012MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis::omega2012; + +namespace ml = o2::analysis::omega2012ml; + +static_assert(std::extent_v == ml::XiK0sNMasterFeatures, + "OmXiK0sFeatures column size must match the XiK0s feature contract"); +static_assert(std::extent_v == ml::Xi1530KNMasterFeatures, + "OmXi1530KFeatures column size must match the Xi1530K feature contract"); + +struct Omega2012TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + static constexpr int NBuildStatus = 6; + + Produces mlEvents; + Produces mlCascades; + Produces mlV0s; + Produces mlTracks; + Produces mlXiK0sCandidates; + Produces mlXiK0sInputs; + Produces mlXiK0sTruth; + Produces mlXi1530KCandidates; + Produces mlXi1530KInputs; + Produces mlXi1530KTruth; + Produces mlGenAudit; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the Omega(2012) histogram task (same JSON keys) + o2::analysis::resonance::EventCuts eventCuts; + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; + + Configurable omega2012MlExportStage{"omega2012MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (all pass bits of the mode)"}; + Configurable omega2012MlLooseAudit{"omega2012MlLooseAudit", true, "Record the loose cut flow and mass/pT/activity spectrum per mode"}; + Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; + Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + + Omega2012AnalysisCore core; + int64_t mlEventRow = -1; + std::unordered_map mlCascadeRows; + std::unordered_map mlV0Rows; + std::unordered_map mlTrackRows; + int parityLoggedXiK0s = 0; + int parityLoggedXi1530K = 0; + + void init(InitContext&) + { + const bool xiK0s = doprocessXiK0s || doprocessXiK0sMC; + const bool xi1530K = doprocessXi1530KMicro || doprocessXi1530KMCMicro; + if ((doprocessXiK0s && doprocessXiK0sMC) || (doprocessXi1530KMicro && doprocessXi1530KMCMicro)) { + LOG(fatal) << "Enable at most one of processXiK0s/processXiK0sMC and one of processXi1530KMicro/processXi1530KMCMicro"; + } + if (!xiK0s && !xi1530K) { + LOG(fatal) << "Enable at least one candidate process function"; + } + if (doprocessMCTrue && !doprocessXiK0sMC && !doprocessXi1530KMCMicro) { + LOG(fatal) << "processMCTrue requires processXiK0sMC or processXi1530KMCMicro"; + } + if (omega2012MlParityRows < 0) { + LOG(fatal) << "omega2012MlParityRows must not be negative"; + } + if (omega2012MlExportStage.value != "loose" && omega2012MlExportStage.value != "selected") { + LOG(fatal) << "omega2012MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.xiK0s = xiK0s; + modes.xi1530K = xi1530K; + modes.microTracks = xi1530K; + modes.mcReco = doprocessXiK0sMC || doprocessXi1530KMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = omega2012MlLooseAudit; + looseOptions.exportSelected = omega2012MlExportStage.value == "selected"; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes, looseOptions); + + // Candidates that violate the canonical or feature contract are skipped, never written. + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedDaughter", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + if (xiK0s) { + auto skipped = histos.add("ML/xiK0s/exportSkipped", "XiK0s mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + } + if (xi1530K) { + auto skipped = histos.add("ML/xi1530K/exportSkipped", "Xi1530K mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + histos.add("ML/xi1530K/chargePattern", "Xi1530K mode candidates handed to the export;charge pattern (0 signal);candidates", HistType::kTH1D, {{4, -0.5, 3.5}}); + } + + LOG(info) << "Size of the histograms in Omega(2012) training table task"; + histos.print(); + } + + template + static uint64_t featureHash(std::array const& features) + { + uint64_t hash = FnvOffsetBasis; + for (const auto& value : features) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + return hash; + } + + template + void writeEvent(Collision const& collision, DecayMode mode) + { + mlCascadeRows.clear(); + mlV0Rows.clear(); + mlTrackRows.clear(); + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + mlEvents(static_cast(mode), static_cast(collision.globalIndex()), + collision.posZ(), collision.bMagField(), collision.cent(), collision.multiplicity(), collision.isRecINELgt0()); + mlEventRow = mlEvents.lastIndex(); + } + + template + int64_t writeCascade(Cascade const& xi) + { + const auto id = static_cast(xi.globalIndex()); + if (auto it = mlCascadeRows.find(id); it != mlCascadeRows.end()) { + return it->second; + } + const auto indices = xi.cascadeIndices(); + const std::array daughterIds{indices[0], indices[1], indices[2]}; + mlCascades(mlEventRow, id, xi.px(), xi.py(), xi.pz(), static_cast(xi.sign()), xi.mXi(), xi.mLambda(), + xi.v0CosPA(), xi.cascCosPA(), xi.daughDCA(), xi.cascDaughDCA(), + xi.dcapostopv(), xi.dcanegtopv(), xi.dcabachtopv(), xi.dcav0topv(), xi.dcaXYCascToPV(), xi.dcaZCascToPV(), + xi.transRadius(), xi.cascTransRadius(), xi.decayVtxX(), xi.decayVtxY(), xi.decayVtxZ(), + xi.daughterTPCNSigmaPosPi10(), xi.daughterTPCNSigmaPosKa10(), xi.daughterTPCNSigmaPosPr10(), + xi.daughterTPCNSigmaNegPi10(), xi.daughterTPCNSigmaNegKa10(), xi.daughterTPCNSigmaNegPr10(), + xi.daughterTPCNSigmaBachPi10(), xi.daughterTPCNSigmaBachKa10(), xi.daughterTPCNSigmaBachPr10(), + xi.daughterTOFNSigmaPosPi10(), xi.daughterTOFNSigmaPosKa10(), xi.daughterTOFNSigmaPosPr10(), + xi.daughterTOFNSigmaNegPi10(), xi.daughterTOFNSigmaNegKa10(), xi.daughterTOFNSigmaNegPr10(), + xi.daughterTOFNSigmaBachPi10(), xi.daughterTOFNSigmaBachKa10(), xi.daughterTOFNSigmaBachPr10(), + xi.nCrossedRowsPos(), xi.nCrossedRowsNeg(), xi.nCrossedRowsBach(), daughterIds.data()); + const auto row = mlCascades.lastIndex(); + mlCascadeRows.emplace(id, row); + return row; + } + + template + int64_t writeV0(V0 const& v0) + { + const auto id = static_cast(v0.globalIndex()); + if (auto it = mlV0Rows.find(id); it != mlV0Rows.end()) { + return it->second; + } + const auto indices = v0.indices(); + const std::array daughterIds{indices[0], indices[1]}; + mlV0s(mlEventRow, id, v0.px(), v0.py(), v0.pz(), v0.mK0Short(), v0.mLambda(), v0.mAntiLambda(), + v0.v0CosPA(), v0.daughDCA(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), v0.transRadius(), + v0.decayVtxX(), v0.decayVtxY(), v0.decayVtxZ(), v0.alpha(), v0.qtarm(), + v0.daughterTPCNSigmaPosPi10(), v0.daughterTPCNSigmaPosKa10(), v0.daughterTPCNSigmaPosPr10(), + v0.daughterTPCNSigmaNegPi10(), v0.daughterTPCNSigmaNegKa10(), v0.daughterTPCNSigmaNegPr10(), + v0.daughterTOFNSigmaPosPi10(), v0.daughterTOFNSigmaPosKa10(), v0.daughterTOFNSigmaPosPr10(), + v0.daughterTOFNSigmaNegPi10(), v0.daughterTOFNSigmaNegKa10(), v0.daughterTOFNSigmaNegPr10(), + v0.nCrossedRowsPos(), v0.nCrossedRowsNeg(), daughterIds.data()); + const auto row = mlV0s.lastIndex(); + mlV0Rows.emplace(id, row); + return row; + } + + template + int64_t writeTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = mlTrackRows.find(id); it != mlTrackRows.end()) { + return it->second; + } + mlTracks(mlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const auto row = mlTracks.lastIndex(); + mlTrackRows.emplace(id, row); + return row; + } + + template + void writeXiK0sCandidate(Collision const& collision, Cascade const& xi, V0 const& v0, XiK0sCandidateValues const& values, uint16_t passBits) + { + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXiK0sFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXiK0s < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=XiK0s collision={} cascade={} v0={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), v0.globalIndex(), passBits, featureHash(pack.master)); + ++parityLoggedXiK0s; + } + const auto cascadeRow = writeCascade(xi); + const auto v0Row = writeV0(v0); + mlXiK0sCandidates(mlEventRow, cascadeRow, v0Row, + static_cast(values.omega.M()), static_cast(values.omega.Pt()), + static_cast(values.omega.Rapidity()), static_cast(values.omega.Eta()), + static_cast(values.omega.Phi()), values.alpha, static_cast(xi.sign()), passBits); + const auto row = mlXiK0sCandidates.lastIndex(); + mlXiK0sInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + mlXiK0sTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? xi.motherPDG() : 0, + matched ? static_cast(xi.motherId()) : int64_t{-1}, v0.motherPDG(), xi.motherPDG()); + } else { + mlXiK0sTruth(row, uint8_t{0}, 0, int64_t{-1}, 0, 0); + } + } + + template + void writeXi1530KCandidate(Collision const& collision, Cascade const& xi, Track const& pion, Track const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) + { + histos.fill(HIST("ML/xi1530K/chargePattern"), values.chargePattern); + if (values.chargePattern != ml::kSignalPattern && !omega2012MlExportWrongSign) { + return; // charge-pattern controls are exported only on request + } + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXi1530KFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXi1530K < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=Xi1530K collision={} cascade={} tracks={},{} chargePattern={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), canonical.candidate.pion.sourceTrackId, canonical.candidate.kaon.sourceTrackId, + values.chargePattern, passBits, featureHash(pack.master)); + ++parityLoggedXi1530K; + } + const auto cascadeRow = writeCascade(xi); + const auto pionRow = writeTrack(pion); + const auto kaonRow = writeTrack(kaon); + mlXi1530KCandidates(mlEventRow, cascadeRow, pionRow, kaonRow, + static_cast(values.omega.M()), values.massXiPi, values.massXiK, values.massPiK, + static_cast(values.omega.Pt()), static_cast(values.omega.Rapidity()), + static_cast(values.omega.Eta()), static_cast(values.omega.Phi()), + values.chargePattern, passBits); + const auto row = mlXi1530KCandidates.lastIndex(); + mlXi1530KInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + mlXi1530KTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? kaon.motherPDG() : 0, + matched ? static_cast(kaon.motherId()) : int64_t{-1}, static_cast(xi.motherId())); + } else { + mlXi1530KTruth(row, uint8_t{0}, 0, int64_t{-1}, int64_t{-1}); + } + } + + template + void exportXiK0s(Collision const& collision, Cascades const& cascades, V0s const& v0s) + { + writeEvent(collision, DecayMode::XiK0s); + // Selection only: the analysis histograms belong to the Omega(2012) analysis task + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& v0, XiK0sCandidateValues const& values, uint16_t passBits) { + writeXiK0sCandidate(coll, xi, v0, values, passBits); + }); + } + + template + void exportXi1530K(Collision const& collision, Cascades const& cascades, Tracks const& tracks) + { + writeEvent(collision, DecayMode::Xi1530K); + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, nullptr, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& pion, auto const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) { + writeXi1530KCandidate(coll, xi, pion, kaon, values, passBits); + }); + } + + void processXiK0s(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, aod::ResoV0s const& v0s) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXiK0s(collision, cascades, v0s); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0s, "Write XiK0s-mode candidates from data", true); + + void processXiK0sMC(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCV0s const& v0s) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + exportXiK0s(collision, cascades, v0s); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0sMC, "Write XiK0s-mode candidates with truth from reconstructed MC", false); + + void processXi1530KMicro(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXi1530K(collision, cascades, tracks); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMicro, "Write Xi1530K-mode candidates from data micro v001 tables", false); + + void processXi1530KMCMicro(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + exportXi1530K(collision, cascades, tracks); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMCMicro, "Write Xi1530K-mode candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedOmega2012(histos, resoParents, [&](auto const& part, GeneratedChannel channel) { + mlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), part.pt(), part.y()); + }); + } + PROCESS_SWITCH(Omega2012TrainingTable, processMCTrue, "Write generated Omega(2012) parents of selected reconstructed MC events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 2c002b9622c938df362b723906f5e39613558e31 Mon Sep 17 00:00:00 2001 From: Yongxi Du <1260971129@qq.com> Date: Thu, 8 Oct 2026 17:26:21 +0800 Subject: [PATCH 222/648] [PWGUD] fix filter (#18210) --- PWGUD/Tasks/flowCorrelationsUpc.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/PWGUD/Tasks/flowCorrelationsUpc.cxx b/PWGUD/Tasks/flowCorrelationsUpc.cxx index 623265a92d4..b4d5e1ee93b 100644 --- a/PWGUD/Tasks/flowCorrelationsUpc.cxx +++ b/PWGUD/Tasks/flowCorrelationsUpc.cxx @@ -77,7 +77,7 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgDcaz, bool, false, "choose dcaz") O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 2.0, "dcaz cut") O2_DEFINE_CONFIGURABLE(cfgMaxTPCChi2NCl, int, 4, "tpcchi2") - O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 1, "choose one side 0:A; 1:C") + O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 0, "choose one side 0:A; 1:C") O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") @@ -90,7 +90,7 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgRctFlagEnabled, bool, false, "use run condition table flag") O2_DEFINE_CONFIGURABLE(cfgRctFlagIndex, int, 1, "1: isCBTOk; 2:isCBTZdcOk; 3: isCBTHadronOk; 4:isCBTHadronZdcOk ") O2_DEFINE_CONFIGURABLE(cfgIRMaxCut, double, 50, "maximum interaction rate for UPC events") - O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, false, "choose zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, true, "choose zdc time cut") O2_DEFINE_CONFIGURABLE(cfgZdcTimeCut, float, 2.0, "zdc time cut") O2_DEFINE_CONFIGURABLE(cfgSbp, bool, true, "choose sbp") O2_DEFINE_CONFIGURABLE(cfgvtxITSTPC, bool, true, "choose vtxITSTPC") @@ -121,7 +121,12 @@ struct FlowCorrelationsUpc { // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); - Filter collisionFilter = ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && (aod::upcservice::truegapside == cfgGapSide)); + Filter collisionFilter = ifnode( + cfgGapSide.node() == 0, + ((aod::udcollision::gapSide == static_cast(0)) && + (aod::upcservice::truegapside == 0)), + ((aod::udcollision::gapSide == static_cast(1)) && + (aod::upcservice::truegapside == 1))); // Connect to ccdb Service ccdb{}; From e0d72b9a983c6662fb4ca92326323d5a9eb073ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 8 Oct 2026 13:42:42 +0200 Subject: [PATCH 223/648] [ALICE3] Refine pdgCodes and update histogram logic (#18205) --- ALICE3/Tasks/alice3TrackingPerformance.cxx | 48 ++++++++++++++-------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/ALICE3/Tasks/alice3TrackingPerformance.cxx b/ALICE3/Tasks/alice3TrackingPerformance.cxx index 77ebd8a1e3d..f0af7813136 100644 --- a/ALICE3/Tasks/alice3TrackingPerformance.cxx +++ b/ALICE3/Tasks/alice3TrackingPerformance.cxx @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -41,36 +42,46 @@ using namespace o2; using namespace o2::framework; -std::shared_ptr particlePdgCodes; -std::map> particlePtDistribution; -std::map> particleEtaDistribution; -std::map> ptDistribution; -std::map> ptResolutionVsPt; -std::map> ptResolutionVsEta; -std::map> invPtResolutionVsPt; -std::map> invPtResolutionVsEta; -std::map> dcaXyResolutionVsPt; -std::map> dcaZResolutionVsPt; -std::map> covariancePtPtVsPt; -std::map> covarianceDcaXyDcaXyVsPt; -std::map> covarianceDcaZDcaZVsPt; struct Alice3TrackingPerformance { - Configurable> pdgCodes{"pdgCodes", {0, 211}, "List of PDG codes to consider for efficiency calculation. (0 means all)"}; + Configurable> pdgCodes{"pdgCodes", {211}, "List of signed PDG codes to consider for performance evaluation"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + // Histograms + std::shared_ptr particlePdgCodes; + std::map> particlePtDistribution; + std::map> particleEtaDistribution; + std::map> ptDistribution; + std::map> ptResolutionVsPt; + std::map> ptResolutionVsEta; + std::map> invPtResolutionVsPt; + std::map> invPtResolutionVsEta; + std::map> dcaXyResolutionVsPt; + std::map> dcaZResolutionVsPt; + std::map> covariancePtPtVsPt; + std::map> covarianceDcaXyDcaXyVsPt; + std::map> covarianceDcaZDcaZVsPt; + void init(o2::framework::InitContext&) { + // Check if pdgCodes has duplicates and remove them + std::sort(pdgCodes.value.begin(), pdgCodes.value.end()); + pdgCodes.value.erase(std::unique(pdgCodes.value.begin(), pdgCodes.value.end()), pdgCodes.value.end()); + // Add 0 to the list of pdgCodes if it is not already present + if (std::find(pdgCodes.value.begin(), pdgCodes.value.end(), 0) == pdgCodes.value.end()) { + pdgCodes.value.push_back(0); + } const AxisSpec axisPtDelta{100, -1, 1, "(#it{p}_{T}^{reco} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; const AxisSpec axisDcaXy{100, -1, 1, "DCA_{xy} (cm)"}; const AxisSpec axisDcaZ{100, -1, 1, "DCA_{z} (cm)"}; const AxisSpec axisCovariancePtPt{100, 0, 10, "cov(#it{p}_{T}, #it{p}_{T}) ((GeV/#it{c})^{2})"}; const AxisSpec axisCovarianceDcaXyDcaXy{100, 0, 1, "cov(DCA_{xy}, DCA_{xy}) (cm^{2})"}; const AxisSpec axisCovarianceDcaZDcaZ{100, 0, 1, "cov(DCA_{z}, DCA_{z}) (cm^{2})"}; - particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{100, -0.5, 99.5, "PDG Code"}}); + const int nBinsPdgCodes = static_cast(pdgCodes.value.size()); + particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{nBinsPdgCodes, -0.5, -0.5 + nBinsPdgCodes, "PDG Code"}}); for (const int& pdg : pdgCodes.value) { std::string prefix = Form("%i", pdg); if (pdg < 0) { @@ -80,6 +91,7 @@ struct Alice3TrackingPerformance { prefix += "/"; auto histoName = [&](const std::string& name) { return Form("%s%s%s", prefix.c_str(), name.c_str(), tag.c_str()); }; particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {ptAxis}); + LOG(info) << "Added histogram: " << histoName("particlePtDistribution"); particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {etaAxis}); ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {ptAxis}); @@ -125,7 +137,7 @@ struct Alice3TrackingPerformance { } const auto& mcParticle = track.mcParticle(); const float ptResolution = (track.pt() - mcParticle.pt()) / mcParticle.pt(); - const float invptResolution = 1.f / track.pt() - 1.f / mcParticle.pt(); + const float invptResolution = (track.pt() > 0.f ? 1.f / track.pt() : 0.f) - (mcParticle.pt() > 0.f ? 1.f / mcParticle.pt() : 0.f); const float covariancePtPt = track.sigma1Pt() * track.sigma1Pt() * track.pt() * track.pt() * track.pt() * track.pt(); auto fillResolutionHistograms = [&](const int p) { @@ -137,8 +149,8 @@ struct Alice3TrackingPerformance { dcaXyResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaXY()); dcaZResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaZ()); covariancePtPtVsPt[p]->Fill(mcParticle.pt(), covariancePtPt); - covarianceDcaXyDcaXyVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaXY2()); - covarianceDcaZDcaZVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaZ2()); + // covarianceDcaXyDcaXyVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaXY2()); + // covarianceDcaZDcaZVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaZ2()); }; fillResolutionHistograms(0); From 6d049449316fe605cc8cbf14beca11f1cc0f78ff Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:48:08 +0200 Subject: [PATCH 224/648] [PWGCF] add additional MC closure process in twoParticleCorrelationsMpi.cxx (#18215) --- .../Tasks/twoParticleCorrelationsMpi.cxx | 135 +++++++++++++----- 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index 777754814aa..1aa5b424941 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -116,13 +116,6 @@ struct TwoParticleCorrelationsMpi { Configurable cfgDropStepRECO{"cfgDropStepRECO", false, "choice to drop step RECO if efficiency correction is used"}; Configurable cfgCentBinsForMC{"cfgCentBinsForMC", 0, "0 = generated multiplicity; 1 = reconstructed multiplicity and all associated collisions"}; } cfgGeneral; - struct : ConfigurableGroup { - Configurable dcaxymax{"dcaxymax", 999.f, "maximum dcaxy of tracks"}; - Configurable dcazmax{"dcazmax", 999.f, "maximum dcaz of tracks"}; - Configurable enablePtDepDCAxy{"enablePtDepDCAxy", false, "Enable pT-dependent DCAxy cut: |DCAxy| < a + b/pT"}; - Configurable dcaXyConst{"dcaXyConst", 0.004f, "Constant term 'a' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm)"}; - Configurable dcaXySlope{"dcaXySlope", 0.013f, "Slope term 'b' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm x GeV/c)"}; - } cfgDCA; Configurable cfgTrackBitMask{"cfgTrackBitMask", 0, "BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task"}; Configurable cfgMultCorrelationsMask{"cfgMultCorrelationsMask", 0, "Selection bitmask for the multiplicity correlations. This should match the filter selection cfgEstimatorBitMask."}; Configurable cfgMultCutFormula{"cfgMultCutFormula", "", "Multiplicity correlations cut formula. A result greater than zero results in accepted event. Parameters: [cFT0C] FT0C centrality, [mFV0A] V0A multiplicity, [mGlob] global track multiplicity, [mPV] PV track multiplicity, [cFT0M] FT0M centrality"}; @@ -192,11 +185,7 @@ struct TwoParticleCorrelationsMpi { Filter collisionVertexTypeFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); // Track filters - Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && - (nabs(aod::track::dcaZ) < cfgDCA.dcazmax) && - ifnode(cfgDCA.enablePtDepDCAxy.node() == true, - nabs(aod::track::dcaXY) < (cfgDCA.dcaXyConst + cfgDCA.dcaXySlope / aod::track::pt), - nabs(aod::track::dcaXY) < cfgDCA.dcaxymax); + Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); Filter cfTrackFilter = (nabs(aod::cftrack::eta) < cfgGeneral.cfgCutEta) && (aod::cftrack::pt > cfgGeneral.cfgCutPt) && ncheckbit(aod::track::trackType, as(cfgTrackBitMask)); // MC filters @@ -345,6 +334,7 @@ struct TwoParticleCorrelationsMpi { using DerivedCollisions = soa::Filtered; using DerivedCollisionsCorrected = soa::Filtered; + using DerivedMCClosureCollisions = soa::Join; using DerivedCollisionsMultSet = soa::Filtered>; using DerivedCollisionsMultSetCorrected = soa::Filtered>; using DerivedTracks = soa::Filtered; @@ -365,11 +355,23 @@ struct TwoParticleCorrelationsMpi { if (enabledDerivedMixedProcesses > 1) { LOGF(fatal, "Only one reconstructed derived mixed-event process can be enabled"); } - if (doprocessMCSameDerived && enabledDerivedSameProcesses > 0) { - LOGF(fatal, "processMCSameDerived is mutually exclusive with the reconstructed derived same-event processes because it also fills those outputs"); + if ((doprocessMCSameDerived || doprocessMCDetectorClosure) && enabledDerivedSameProcesses > 0) { + LOGF(fatal, "The MC generated/reconstructed same-event processes are mutually exclusive with the reconstructed derived same-event processes because they also fill those outputs"); + } + if (doprocessMCSameDerived && doprocessMCDetectorClosure) { + LOGF(fatal, "processMCSameDerived and processMCDetectorClosure are mutually exclusive because both fill generated and reconstructed same-event outputs"); + } + if (doprocessSameGenMC && (doprocessMCSameDerived || doprocessMCDetectorClosure)) { + LOGF(fatal, "processSameGenMC is mutually exclusive with processMCSameDerived and processMCDetectorClosure because they fill the same generated outputs"); + } + if (doprocessMCDetectorClosure && cfgGeneral.cfgCentBinsForMC != 0) { + LOGF(fatal, "processMCDetectorClosure requires cfgCentBinsForMC=0 so generated and reconstructed outputs are both filled versus true charged multiplicity"); } - if (doprocessSameGenMC && doprocessMCSameDerived) { - LOGF(fatal, "processSameGenMC and processMCSameDerived are mutually exclusive because both fill the generated same-event outputs"); + if (doprocessMCDetectorClosure && cfgUserAxis == NMPIAxis) { + LOGF(fatal, "processMCDetectorClosure does not support cfgUserAxis=3 (true N MPI); use the true charged-multiplicity axis for detector closure"); + } + if (doprocessMCDetectorClosure && cfgCorrections.cfgFillAcceptanceWeights) { + LOGF(fatal, "processMCDetectorClosure cannot be used to produce acceptance weights"); } if (cfgUserAxis == NMPIAxis && (!(doprocessSameGenMC || doprocessMCSameDerived) || @@ -460,6 +462,18 @@ struct TwoParticleCorrelationsMpi { registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); } registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{100, 0, 100, "/multiplicity/centrality"}}}); + if (doprocessMCDetectorClosure) { + registry.add("detectorClosure/rawMultiplicityVsTrue", "raw reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{raw,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/correctedMultiplicityVsTrue", "corrected reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{corr,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/profileCorrectedMultiplicityVsTrue", "mean corrected reconstructed multiplicity versus true multiplicity;N_{ch}^{true};#LT N_{ch}^{corr,reco} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/profileTrueMultiplicityVsCorrected", "mean true multiplicity versus corrected reconstructed multiplicity;N_{ch}^{corr,reco};#LT N_{ch}^{true} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/recoCollisionsPerMCCollision", "accepted reconstructed collisions per MC collision;N_{coll}^{reco};events", {HistType::kTH1F, {{11, -0.5, 10.5}}}); + registry.add("detectorClosure/status", "detector-closure event status;status;events", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + auto* detectorClosureStatus = registry.get(HIST("detectorClosure/status")).get(); + detectorClosureStatus->GetXaxis()->SetBinLabel(1, "no reconstructed collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(2, "one best collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(3, "invalid best-collision count"); + } if (eventSeedEstimatorEnabled) { registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{100, 0, 100, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); registry.add("eventSeedPairProbabilities", "summed pair probabilities", {HistType::kTH3F, {{200, 0, 200, "#Sigma P_{baseline}"}, {200, 0, 200, "#Sigma P_{near}"}, {200, 0, 200, "#Sigma P_{away}"}}}); @@ -669,7 +683,7 @@ struct TwoParticleCorrelationsMpi { ++nPtPtSubevents; } - if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { auto recoProfiles = std::make_unique(); addConfigObjectsToObjArray(recoProfiles.get(), mCorrConfigs, cfgUserAxis == EventSeedAxis && eventClassifierPercentileAxisEnabled); fFC.setObject(new FlowContainer("FlowContainer")); @@ -683,7 +697,7 @@ struct TwoParticleCorrelationsMpi { fFCpt->initialise(axisMultiplicity, MaxPtCorrelationOrder, cfgCorrConfig.value, FlowNBootstrap); fFCpt->initialiseSubevent(axisMultiplicity, MaxPtCorrelationOrder, nPtPtSubevents, FlowNBootstrap); } - if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessSameGenMC)) { + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameGenMC)) { auto generatedProfiles = std::make_unique(); addConfigObjectsToObjArray(generatedProfiles.get(), mCorrConfigs, cfgUserAxis == NMPIAxis && eventClassifierPercentileAxisEnabled); fFCGen.setObject(new FlowContainer("FlowContainer_gen")); @@ -1759,11 +1773,12 @@ struct TwoParticleCorrelationsMpi { } template - void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true) + void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true, float efficiencyMultiplicity = -1.f) { if (multiplicity < 0.f) { return; } + const float efficiencyLookupMultiplicity = efficiencyMultiplicity >= 0.f ? efficiencyMultiplicity : multiplicity; // Cache efficiency for particles (too many FindBin lookups) if constexpr (step == CorrelationContainer::kCFStepCorrected) { @@ -1771,7 +1786,7 @@ struct TwoParticleCorrelationsMpi { efficiencyAssociatedCache.clear(); efficiencyAssociatedCache.reserve(tracks2.size()); for (const auto& track : tracks2) { - efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ)); + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), efficiencyLookupMultiplicity, posZ)); } } } @@ -1833,7 +1848,7 @@ struct TwoParticleCorrelationsMpi { float triggerWeight = eventWeight; if constexpr (step == CorrelationContainer::kCFStepCorrected) { if (cfg.mEfficiencyTrigger) { - triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ); + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), efficiencyLookupMultiplicity, posZ); } } @@ -2171,14 +2186,16 @@ struct TwoParticleCorrelationsMpi { PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameGenMC, "Process generated MC events from derived data and validate against the stored HepMC N MPI", false); template - void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr) + void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr, const float* outputMultiplicityOverride = nullptr, const float* efficiencyMultiplicityOverride = nullptr) { auto getMultiplicity = [](const auto& col) { return getAnalysisMultiplicity(col); }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; - const auto multiplicity = getMultiplicity(collision); + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + const float collisionMultiplicity = getMultiplicity(collision); + const float multiplicity = outputMultiplicityOverride ? *outputMultiplicityOverride : collisionMultiplicity; + const float efficiencyMultiplicity = efficiencyMultiplicityOverride ? *efficiencyMultiplicityOverride : collisionMultiplicity; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), multiplicity); } @@ -2200,8 +2217,8 @@ struct TwoParticleCorrelationsMpi { fGFW->Clear(); fFCpt->clearVector(); for (const auto& track : tracks2) { - fillPtSums(track, multiplicity, collision.posZ()); - fillGFW(track, multiplicity, collision.posZ()); + fillPtSums(track, efficiencyMultiplicity, collision.posZ()); + fillGFW(track, efficiencyMultiplicity, collision.posZ()); } int bin = configurableBinningDerived.getBin(std::tuple(collision.posZ(), multiplicity)); @@ -2230,14 +2247,14 @@ struct TwoParticleCorrelationsMpi { flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } else if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills, -1.0, true, true, efficiencyMultiplicity); finalizeEventSeedEstimate(seedEstimate); flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } if (fillReco && hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds())); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds()), true, true, efficiencyMultiplicity); } const auto seedClass = getEventSeedClass(multiplicity, seedEstimate.nuncSeeds()); fillOutputContainers(multiplicity, fRndm->Rndm(), seedClass); @@ -2254,7 +2271,7 @@ struct TwoParticleCorrelationsMpi { } if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate, nullptr, nullptr, -1.0, true, true, efficiencyMultiplicity); } finalizeEventSeedEstimate(seedEstimate); fillOutputContainers(multiplicity, fRndm->Rndm()); @@ -2444,8 +2461,8 @@ struct TwoParticleCorrelationsMpi { } PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerivedMultSetCorrected, "Process mixed events on derived data with corrected multiplicity and multiplicity sets", false); - template - void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, soa::SmallGroups const& collisions) + template + void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, Collisions const& collisions) { if (cfgCorrections.cfgFillAcceptanceWeights) { return; @@ -2470,7 +2487,7 @@ struct TwoParticleCorrelationsMpi { ? getTrueNMPIUserAxisValue(multiplicity, mcCollision.nMPI()) : -1.0; - if (!(doprocessMCSameDerived || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if (!(doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { if constexpr (std::experimental::is_detected::value) { fillQA(mcCollision, multiplicity, mcCollision.posZ(), mcParticles1, mcParticles2); } else { @@ -2540,6 +2557,60 @@ struct TwoParticleCorrelationsMpi { } PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCSameDerived, "Process generated and reconstructed MC same events on derived data and validate against HepMC N MPI", false); + void processMCDetectorClosure(soa::Filtered::iterator const& mcCollision, + soa::Filtered const& mcParticles, + soa::SmallGroups const& collisions, + soa::Filtered const& tracks) + { + registry.fill(HIST("detectorClosure/recoCollisionsPerMCCollision"), static_cast(collisions.size())); + + int bestRecoCollisionCount = 0; + for (const auto& collision : collisions) { + if (collision.bestRecoCollision()) { + ++bestRecoCollisionCount; + } + } + + if (collisions.size() == 0) { + registry.fill(HIST("detectorClosure/status"), 0.0); + } else if (bestRecoCollisionCount == 1) { + registry.fill(HIST("detectorClosure/status"), 1.0); + } else { + registry.fill(HIST("detectorClosure/status"), 2.0); + LOGF(fatal, "processMCDetectorClosure expected exactly one best reconstructed collision for MC collision %d, found %d among %d reconstructed collisions", + mcCollision.globalIndex(), bestRecoCollisionCount, collisions.size()); + } + + // Fill generated steps once. kCFStepVertex is filled only when at least one + // selected reconstructed collision is associated with this MC collision. + processMCSameDerivedT(mcCollision, mcParticles, mcParticles, collisions); + + if (bestRecoCollisionCount == 0) { + return; + } + + const float trueMultiplicity = mcCollision.multiplicity(); + const int trueNMPI = mcCollision.nMPI(); + for (const auto& collision : collisions) { + if (!collision.bestRecoCollision()) { + continue; + } + + const float rawMultiplicity = collision.multiplicity(); + const float correctedMultiplicity = collision.multiplicityCorrected(); + registry.fill(HIST("detectorClosure/rawMultiplicityVsTrue"), trueMultiplicity, rawMultiplicity); + registry.fill(HIST("detectorClosure/correctedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileCorrectedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileTrueMultiplicityVsCorrected"), correctedMultiplicity, trueMultiplicity); + + auto collisionTracks = tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex()); + // Physics histograms use true multiplicity, while efficiency weights use + // the reconstructed multiplicity coordinate for which the map was made. + processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI, &trueMultiplicity, &rawMultiplicity); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCDetectorClosure, "Fill generated, raw reconstructed, and efficiency-corrected MC correlations versus true charged multiplicity", false); + PresliceUnsorted collisionPerMCCollision = aod::cfcollision::cfMcCollisionId; template void processMCMixedDerivedT(soa::Filtered const& mcCollisions, soa::Filtered const& collisions, ParticleTypes&&... particles) From cb91f76390067edaf119a0d79e52c19430dee653 Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Thu, 8 Oct 2026 15:42:00 +0200 Subject: [PATCH 225/648] [PWGLF] Nuclei proton correlation: fix proton rejection (#18220) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 9f906a15541..97484eaf831 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -95,7 +95,6 @@ struct HadronNucleiCorrelation { Configurable isPrim{"isPrim", true, "is isPrim"}; Configurable doCorrection{"doCorrection", false, "do efficiency correction"}; Configurable doQuadraticPID{"doQuadraticPID", false, "do PID with sum in quadrature of TOF and TPC"}; - Configurable doPIDRej{"doPIDRej", false, "do PID rejection of competing species"}; struct : ConfigurableGroup { std::string prefix = "Coalescence"; // JSON group name @@ -116,7 +115,7 @@ struct HadronNucleiCorrelation { Configurable removeSameBunchPileup{"removeSameBunchPileup", false, "remove Same Bunch Pileup"}; // Track selection - Configurable doClosePairRejection{"doClosePairRejection", false, "doClosePairRejection"}; + Configurable doClosePairRejection{"doClosePairRejection", false, "doClosePairRejection for non identical particles"}; Configurable dcaPar0{"dcaPar0", 0.004, "par 0"}; Configurable dcaPar1{"dcaPar1", 0.013, "par 1"}; Configurable doDCAZ{"doDCAZ", true, "do DCA z cut"}; @@ -131,8 +130,8 @@ struct HadronNucleiCorrelation { Configurable nsigmaElPr{"nsigmaElPr", 1.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaElDe{"nsigmaElDe", 3.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaTOF{"nsigmaTOF", 3.5f, "cut nsigma TOF"}; - Configurable nsigmaTOFPrRej{"nsigmaTOFPrRej", 3.f, "cut nsigma TOF for proton rejection"}; - Configurable doPrRej{"doPrRej", true, "do TOF proton rejection"}; + Configurable nsigmaTPCPrRej{"nsigmaTPCPrRej", 3.f, "cut nsigma TPC for proton rejection"}; + Configurable doPrRej{"doPrRej", true, "do TPC proton rejection"}; Configurable nsigmaQuadratic{"nsigmaQuadratic", 3.0f, "cut on sqrt(nsigmaTPC^2 + nsigmaTOF^2), used above the TOF pT threshold when doQuadraticPID is on"}; Configurable nsigmaITSPr{"nsigmaITSPr", -2.0f, "cut nsigma ITS Pr"}; Configurable nsigmaITSDe{"nsigmaITSDe", -2.0f, "cut nsigma ITS De"}; @@ -622,7 +621,7 @@ struct HadronNucleiCorrelation { const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; - const bool isNotPr = std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value || !doPrRej.value; + const bool isNotPr = std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value || !doPrRej.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; @@ -1031,7 +1030,7 @@ struct HadronNucleiCorrelation { const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; - const bool isNotPr = std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value || !doPrRej.value; + const bool isNotPr = std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value || !doPrRej.value; if (track.sign() > 0) { From 6a6cfe3f68ce4d79faa62a24b31c926f36d3e4f4 Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Thu, 8 Oct 2026 16:55:50 +0200 Subject: [PATCH 226/648] [PWGEM] Revert using ccdb column to get magnetic field (#18059) Co-authored-by: jokonig --- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 15 +++++++++++---- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h | 15 ++++++++++----- PWGEM/PhotonMeson/Tasks/pcmQC.cxx | 15 +++++++++++---- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 9623f795ea6..9b35b491e03 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -475,10 +475,17 @@ struct Pi0EtaToGammaGamma { return; } - o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); + // d_bz = collision.grpMagField().getNominalL3Field(); + + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + } + d_bz = grpmag->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + fV0PhotonCut.SetD_Bz(d_bz); } @@ -1165,7 +1172,7 @@ struct Pi0EtaToGammaGamma { o2::framework::expressions::Filter prefilter_primaryelectron = ifnode(dileptoncuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::emprimaryelectron::pfbderived == static_cast(0), true); int ndf = 0; - void processAnalysis(o2::soa::Filtered> const& collisions, Types const&... args) + void processAnalysis(o2::soa::Filtered> const& collisions, Types const&... args) { // LOGF(info, "ndf = %d", ndf); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { @@ -1202,7 +1209,7 @@ struct Pi0EtaToGammaGamma { PROCESS_SWITCH(Pi0EtaToGammaGamma, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered> const& collisions, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, Types const&... args) { // LOGF(info, "ndf = %d", ndf); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index 79c7b4308ae..dc20e1b3883 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -326,9 +326,14 @@ struct Pi0EtaToGammaGammaMC { return; } - o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); - // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // // Fetch magnetic field from ccdb for current collision + // d_bz = collision.grpMagField().getNominalL3Field(); + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + } + d_bz = grpmag->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); } @@ -1005,7 +1010,7 @@ struct Pi0EtaToGammaGammaMC { o2::framework::expressions::Filter prefilter_pcm = ifnode(pcmcuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::v0photonkf::pfbderived == static_cast(0), true); o2::framework::expressions::Filter prefilter_primaryelectron = ifnode(dileptoncuts.cfg_apply_cuts_from_prefilter_derived.node(), o2::aod::emprimaryelectron::pfbderived == static_cast(0), true); - void processAnalysis(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) + void processAnalysis(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { auto&& [v0photons, v0legs] = std::forward_as_tuple(args...); @@ -1041,7 +1046,7 @@ struct Pi0EtaToGammaGammaMC { PROCESS_SWITCH(Pi0EtaToGammaGammaMC, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { auto&& [v0photons, v0legs] = std::forward_as_tuple(args...); diff --git a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx index 496cf7a8067..112a686abc9 100644 --- a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx @@ -85,7 +85,7 @@ using namespace o2::aod::pwgem::photon; using namespace o2::aod::pwgem::photonmeson::utils::mcutil; using namespace o2::aod::pwgem::dilepton::utils::mcutil; -using MyCollisions = soa::Join; +using MyCollisions = soa::Join; using MyCollision = MyCollisions::iterator; using MyV0Photons = soa::Join; @@ -95,7 +95,7 @@ using MyV0PhotonsML = soa::Join; using MyV0PhotonML = MyV0PhotonsML::iterator; // MC Joins -using MyCollisionsMC = soa::Join; +using MyCollisionsMC = soa::Join; using MyCollisionMC = MyCollisionsMC::iterator; using MyMCCollisions = soa::Join; @@ -276,8 +276,15 @@ struct PCMQC { } // Fetch magnetic field from ccdb for current collision - d_bz = collision.grpMagField().getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + // d_bz = collision.grpMagField().getNominalL3Field(); + // LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + return; + } + d_bz = grpmag->getNominalL3Field(); + LOG(info) << "Retrieved GRP for run " << collision.runNumber() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); } From a56ac6550b319180d26cc0cd53327a5b47f576f9 Mon Sep 17 00:00:00 2001 From: nzardosh Date: Thu, 8 Oct 2026 16:56:24 +0200 Subject: [PATCH 227/648] [PWGJE] fixing code cheker errors in embedding (#18211) --- .../derivedDataProducerEmbedding.cxx | 15 ++++++++------- PWGJE/Tasks/embeddingQA.cxx | 14 +++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx index a4f88e03809..1a87e66b360 100644 --- a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx +++ b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx @@ -13,9 +13,9 @@ // /// \author Nima Zardoshti -#include "PWGJE/Core/JetDQUtilities.h" +// #include "PWGJE/Core/JetDQUtilities.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" -#include "PWGJE/Core/JetV0Utilities.h" +// #include "PWGJE/Core/JetV0Utilities.h" #include "PWGJE/DataModel/EMCALClusters.h" #include "PWGJE/DataModel/EMCALMatchedCollisions.h" #include "PWGJE/DataModel/JetReducedData.h" @@ -23,17 +23,17 @@ #include "PWGJE/DataModel/JetReducedDataHF.h" #include "PWGJE/DataModel/JetReducedDataV0.h" // -#include "PWGDQ/DataModel/ReducedInfoTables.h" -#include "PWGHF/DataModel/DerivedTables.h" +// #include "PWGDQ/DataModel/ReducedInfoTables.h" +// #include "PWGHF/DataModel/DerivedTables.h" #include "PWGHF/Utils/utilsBfieldCCDB.h" -#include "PWGLF/DataModel/LFStrangenessTables.h" +// #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" #include "PWGUD/Core/SGCutParHolder.h" #include "PWGUD/Core/SGSelector.h" #include "PWGUD/Core/UDHelpers.h" #include "Common/CCDB/ctpRateFetcher.h" -#include "Common/Core/RecoDecay.h" +// #include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -819,7 +820,7 @@ struct JetDerivedDataEmbeddingProducerTask { void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) { - for (auto cluster : clusters) { + for (auto const& cluster : clusters) { auto const clusterCells = cells.sliceBy(preslices.perClusterCells, cluster.globalIndex()); diff --git a/PWGJE/Tasks/embeddingQA.cxx b/PWGJE/Tasks/embeddingQA.cxx index 65425fc82f0..f8736d460bb 100644 --- a/PWGJE/Tasks/embeddingQA.cxx +++ b/PWGJE/Tasks/embeddingQA.cxx @@ -14,20 +14,20 @@ /// \author Nima Zardoshti // -#include "PWGJE/Core/FastJetUtilities.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/DataModel/Jet.h" -#include "Common/Core/RecoDecay.h" -#include "Common/Core/TrackSelection.h" -#include "Common/Core/TrackSelectionDefaults.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/TrackSelectionTables.h" - #include #include #include +#include #include +#include +#include +#include + +#include +#include using namespace o2; using namespace o2::framework; From 11b3b493e137de5f5ffde3cabf2a5c62bb2f4834 Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:08:36 +0200 Subject: [PATCH 228/648] [PWGHF] Minor fix in D+ selected collision for D+-h correlator (#18219) --- PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index 8c3ec22ea1f..ee3f8571f6f 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -99,13 +99,12 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusData const& candidates) { bool isSelColl = true; - bool isDplusFound = true; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin) { - isDplusFound = false; continue; } isDplusFound = true; @@ -127,7 +126,7 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusMcRec const& candidates) { bool isSelColl = true; - bool isDplusFound = false; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { @@ -806,7 +805,7 @@ struct HfCorrelatorDplusHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -831,11 +830,11 @@ struct HfCorrelatorDplusHadrons { } } auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairMcRec{corrBinningMcRec, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcRec) { - int poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); - int const poolBinDplus = corrBinning.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); + int poolBin = corrBinningMcRec.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); + int const poolBinDplus = corrBinningMcRec.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); registry.fill(HIST("hMultFT0M"), c1.multFT0M()); registry.fill(HIST("hZVtx"), c1.posZ()); registry.fill(HIST("hTracksPoolBin"), poolBin); // note that the selections here are not yet applied From 4d25047b06c188f3f2605c4cb2b8272f080ab52d Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:58:47 +0200 Subject: [PATCH 229/648] =?UTF-8?q?[PWGEM,PWGEM-36]=20Pi0Flow:=20Add=20Del?= =?UTF-8?q?taEtaDeltaPhiMinvPtCent=20histograms=20f=E2=80=A6=20(#18212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index ecb1fe06214..e824436c401 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -220,7 +220,6 @@ struct TaskPi0FlowEMC { Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; Configurable enableTanThetadPhi{"enableTanThetadPhi", false, "flag to turn cut opening angle in delta theta delta phi on/off"}; Configurable minTanThetadPhi{"minTanThetadPhi", 4., "apply min opening angle in delta theta delta phi to cut on late conversion"}; - Configurable maxEnergyAsymmetry{"maxEnergyAsymmetry", 1., "apply max energy asymmetry for meson candidate"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; ConfigurableAxis thConfigAxisTanThetaPhi{"thConfigAxisTanThetaPhi", {180, -90.f, 90.f}, ""}; } mesonConfig; @@ -420,6 +419,12 @@ struct TaskPi0FlowEMC { const AxisSpec thnAxisMixingEP{mixingConfig.cfgEPBins, Form("cos(%d#varphi)", harmonic.value)}; const AxisSpec thAxisEtaPhiAngle{thConfigAxisEtaPhiAngle, "atan2(#Delta#eta,#Delta#varphi) (rad)"}; + const AxisSpec thnQaAxisMinv{16, 0, 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; // bin witdh 0.05 GeV/c^2 + const AxisSpec thnQaAxisPt{10, 0, 20., "#it{p}_{T} (GeV/#it{c})"}; // bin width 2 GeV/c + const AxisSpec thnQaAxisDeltaEta{20, -1.6, 1.6, "|#Delta#eta|"}; // bin witdh 0.16 + const AxisSpec thnQaAxisDeltaPhi{24, 0, o2::constants::math::PI, "#Delta#varphi"}; // bin width 7.5 degrees in rad + const AxisSpec thnQaAxisCent{10, 0, 100, "Centrality (%)"}; // bin width 10% + if (!doprocessM02) { registry.add("hSparsePi0Flow", " vs m_{inv} vs p_T vs cent for same event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); registry.add("hSparsePi0", "m_{inv} vs p_T vs cent for same event", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); @@ -474,6 +479,8 @@ struct TaskPi0FlowEMC { registry.add("mesonQA/hAlphaPtMixed", "Histo of meson asymmetry vs pT for mixed event", HistType::kTH2D, {thAxisAlpha, thnAxisPt}); registry.add("mesonQA/hEtaPhiAngleMassCent", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, same event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); registry.add("mesonQA/hEtaPhiAngleMassCentMixed", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, mixed event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for same event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for mixed event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); } if (correctionConfig.doEMCalCalib.value) { @@ -1094,6 +1101,7 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); @@ -1254,6 +1262,7 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); registry.fill(HIST("mesonQA/hEtaPhiAngleMassCentMixed"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); @@ -1326,6 +1335,7 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dEta = v1.Eta() - v2.Eta(); float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); if (openingAngle <= mesonConfig.minOpenAngle) { @@ -1347,6 +1357,8 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); @@ -1422,6 +1434,7 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dEta = v1.Eta() - v2.Eta(); float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1441,6 +1454,8 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hEtaPhiAngleMassCentMixed"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); @@ -1540,6 +1555,8 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhi = v1.Phi() - v2.Phi(); + float dEta = v1.Eta() - v2.Eta(); float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1558,6 +1575,8 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); } registry.fill(HIST("hMesonCuts"), 6); runFlowAnalysis<0>(collision, vMeson); @@ -1611,6 +1630,8 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhi = v1.Phi() - v2.Phi(); + float dEta = v1.Eta() - v2.Eta(); float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1629,6 +1650,8 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); } registry.fill(HIST("hMesonCutsMixed"), 6); runFlowAnalysis<2>(c1, vMeson); From dc2a2dfd07fffdfc7fe62eb5061fd199b0ccee85 Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:58:35 -0500 Subject: [PATCH 230/648] [PWGHF] First draft of Omegac0 to OmegaPi QA workflow (#18126) --- PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h | 234 +++++ PWGHF/D2H/Tasks/CMakeLists.txt | 5 + PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx | 557 +++++++++++ PWGHF/TableProducer/CMakeLists.txt | 10 + .../candidateSelectorOmegac0ToOmegaPiQa.cxx | 887 ++++++++++++++++++ .../treeCreatorOmegac0ToOmegaPiQa.cxx | 659 +++++++++++++ 6 files changed, 2352 insertions(+) create mode 100644 PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h create mode 100644 PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx create mode 100644 PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx create mode 100644 PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx diff --git a/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h new file mode 100644 index 00000000000..74633679551 --- /dev/null +++ b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h @@ -0,0 +1,234 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponseOmegacToOmegaPiQa.h +/// \brief Class to compute the ML response for Ωc0 → Ω- π+ (and charge-conjugate) analysis selections +/// \author Yunfan Liu , China University of Geosciences +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#ifndef PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ +#define PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ + +#include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_OMEGAC0(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesOmegacToOmegaPi::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER from OBJECT +#define CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::FEATURE): { \ + inputFeatures.emplace_back(OBJECT.GETTER()); \ + break; \ + } + +// Specific case of CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) +// where OBJECT is named candidate and FEATURE = GETTER +#define CHECK_AND_FILL_VEC_OMEGAC0(GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::GETTER): { \ + inputFeatures.emplace_back(candidate.GETTER()); \ + break; \ + } + +namespace o2::analysis +{ +enum class InputFeaturesOmegacToOmegaPi : uint8_t { + + // Common variables + dcaCharmBaryonDau = 0, + dcaCascDau, + nSigmaTPCPiFromV0, + nSigmaTPCPiFromOmegac, + nSigmaTPCKaFromCasc, + + // DCAFitter variables + impactParBachFromCharmBaryonXY, + impactParBachFromCharmBaryonZ, + impactParCascXY, + impactParCascZ, + cosPACharmBaryon, + cosPACasc, + + // KFParticle variables + cosPaOmegacToPv, + kfDcaXYPiFromOmegac, + chi2TopoPiFromOmegacToPv, + invMassCascade, + massCascChi2OverNdf, + kfDcaXYCascToPv, + cosPaCascToPv, + cosThetaStarPiFromOmegac, + chi2NdfTopoOmegacToPv, + ldlCasc, + cosPaCascToOmegac, + decayLenXYCasc, + decayLenXYOmegac, + ldlOmegac, + chi2NdfTopoCascToOmegac, + chi2NdfTopoCascToPv, + chi2GeoOmegac, + chi2GeoCasc, +}; + +template +class HfMlResponseOmegacToOmegaPi : public HfMlResponse +{ + public: + /// Default constructor + HfMlResponseOmegacToOmegaPi() = default; + /// Default destructor + virtual ~HfMlResponseOmegacToOmegaPi() = default; + + /// Method to get the input features vector needed for ML inference + /// \param candidate is the OMEGAC0 candidate + /// \param lamProngPi is the candidate's lamProngPi + /// \param cascProng is the bachelor track from the Omega decay + /// \param charmBaryonProng is the bachelor pion from the OmegaC decay + /// \return inputFeatures vector + template + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProng, T3 const& charmBaryonProng) + { + std::vector inputFeatures; + + for (const auto& idx : MlResponse::mCachedIndices) { + + // Variables common to DCAFitter and KFParticle + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_OMEGAC0(dcaCascDau); + // TPC PID variables + CHECK_AND_FILL_VEC_OMEGAC0_FULL(lamProngPi, nSigmaTPCPiFromV0, tpcNSigmaPi); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(cascProng, nSigmaTPCKaFromCasc, tpcNSigmaKa); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(charmBaryonProng, nSigmaTPCPiFromOmegac, tpcNSigmaPi); + } + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonZ); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascZ); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACasc); + } + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaOmegacToPv, cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2TopoPiFromOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0(invMassCascade); + CHECK_AND_FILL_VEC_OMEGAC0(massCascChi2OverNdf); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaCascToPv, cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(cosThetaStarPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoOmegacToPv, chi2TopoOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlCasc, cascldl); + CHECK_AND_FILL_VEC_OMEGAC0(cosPaCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYCasc); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlOmegac, omegacldl); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToOmegac, chi2TopoCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToPv, chi2TopoCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoCasc); + } + } + } + + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + // Common variables + MlResponse::mAvailableInputFeatures = { + + FILL_MAP_OMEGAC0(dcaCharmBaryonDau), + FILL_MAP_OMEGAC0(dcaCascDau), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromV0), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromOmegac), + FILL_MAP_OMEGAC0(nSigmaTPCKaFromCasc)}; + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + std::map mapDcaFeatures{ + + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonXY), + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonZ), + FILL_MAP_OMEGAC0(impactParCascXY), + FILL_MAP_OMEGAC0(impactParCascZ), + FILL_MAP_OMEGAC0(cosPACharmBaryon), + FILL_MAP_OMEGAC0(cosPACasc)}; + + MlResponse::mAvailableInputFeatures.insert(mapDcaFeatures.begin(), mapDcaFeatures.end()); + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + std::map mapKfFeatures{ + + FILL_MAP_OMEGAC0(cosPaOmegacToPv), + FILL_MAP_OMEGAC0(kfDcaXYPiFromOmegac), + FILL_MAP_OMEGAC0(chi2TopoPiFromOmegacToPv), + FILL_MAP_OMEGAC0(invMassCascade), + FILL_MAP_OMEGAC0(massCascChi2OverNdf), + FILL_MAP_OMEGAC0(kfDcaXYCascToPv), + FILL_MAP_OMEGAC0(cosPaCascToPv), + FILL_MAP_OMEGAC0(cosThetaStarPiFromOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoOmegacToPv), + FILL_MAP_OMEGAC0(ldlCasc), + FILL_MAP_OMEGAC0(cosPaCascToOmegac), + FILL_MAP_OMEGAC0(decayLenXYCasc), + FILL_MAP_OMEGAC0(decayLenXYOmegac), + FILL_MAP_OMEGAC0(ldlOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToPv), + FILL_MAP_OMEGAC0(chi2GeoOmegac), + FILL_MAP_OMEGAC0(chi2GeoCasc)}; + + MlResponse::mAvailableInputFeatures.insert(mapKfFeatures.begin(), mapKfFeatures.end()); + } + } +}; + +} // namespace o2::analysis + +#undef FILL_MAP_OMEGAC0 +#undef CHECK_AND_FILL_VEC_OMEGAC0_FULL +#undef CHECK_AND_FILL_VEC_OMEGAC0 +#endif // PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 4d6a60998fc..8ce14577196 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -144,6 +144,11 @@ o2physics_add_dpl_workflow(task-omegac0-to-omega-pi PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-omegac0-to-omega-pi-qa + SOURCES taskOmegac0ToOmegaPiQa.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-pt-fluc-charm-hadrons SOURCES taskPtFlucCharmHadrons.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils diff --git a/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..90ebbb0d312 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,557 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskOmegac0ToOmegaPiQa.cxx +/// \brief OmegaC0 analysis task +/// \author Yunfan Liu , China University of Geosciences +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannelsLegacy.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Omegac0 analysis task +struct HfTaskOmegac0ToOmegaPiQa { + + Configurable selectionFlagOmegac0{"selectionFlagOmegac0", true, "Select Omegac0 candidates"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "Max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "Max. cand. rapidity"}; + + SliceCache cache; + + using Omegac0Cands = soa::Filtered>; + using Omegac0CandsKF = soa::Filtered>; + using Omegac0CandsMc = soa::Filtered>; + using Omegac0CandsMcKF = soa::Filtered>; + using Omegac0CandsMl = soa::Filtered>; + using Omegac0CandsMlKF = soa::Filtered>; + using Omegac0CandsMlMc = soa::Filtered>; + using Omegac0CandsMlMcKF = soa::Filtered>; + + using Omegac0Gen = soa::Filtered>; + + using Collisions = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; + using CollisionsWithMcLabels = soa::Join; + + using McCollisionsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterOmegaCMatchedRec = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + Filter filterOmegaCMatchedGen = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + + Preslice candOmegacPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {700, 2.4, 3.1}, "Cand. inv. mass"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {500, 0, 50}, "Cand. pT"}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {500, 0, 50}, "Cand. beauty mother pT"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1, 1}, "Cand. rapidity"}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, "Cand. origin"}; + ConfigurableAxis thnConfigAxisMatchFlag{"thnConfigAxisMatchFlag", {15, -7.5, 7.5}, "Cand. MC match flag"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Coll. num. PV contributors"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {100, 0, 100}, "Coll. centrality precentile"}; + ConfigurableAxis thnConfigAxisPromptScore{"thnConfigAxisPromptScore", {100, 0, 1}, "Prompt score"}; + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + std::array doprocess{doprocessDataDCAFitter, doprocessDataDCAFitterMl, doprocessDataDCAFitterFT0C, doprocessDataDCAFitterMlFT0C, + doprocessDataDCAFitterFT0M, doprocessDataDCAFitterMlFT0M, + doprocessDataKFParticle, doprocessDataKFParticleMl, doprocessDataKFParticleFT0C, doprocessDataKFParticleMlFT0C, + doprocessDataKFParticleFT0M, doprocessDataKFParticleMlFT0M, + doprocessMcDCAFitter, doprocessMcDCAFitterMl, + doprocessMcDCAFitterFT0M, doprocessMcDCAFitterMlFT0M, + doprocessMcKFParticle, doprocessMcKFParticleMl, + doprocessMcKFParticleFT0M, doprocessMcKFParticleMlFT0M}; + if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { + LOGP(fatal, "One and only one process function should be enabled at a time."); + } + + const AxisSpec thnAxisMass{thnConfigAxisMass, "Inv. mass (#Omega#pi) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisY{thnConfigAxisY, "y"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "Origin"}; + const AxisSpec thnAxisMatchFlag{thnConfigAxisMatchFlag, "MC match flag"}; + const AxisSpec thnAxisNumPvContr{thnConfigAxisNumPvContr, "Number of primary vtx. contributors"}; + const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality percentile"}; + const AxisSpec thnAxisCentMc{thnConfigAxisCent, "Centrality percentile (from gen. MC info)"}; + const AxisSpec thnAxisPromptScore{thnConfigAxisPromptScore, "BDT score prompt"}; + + std::vector axes = {thnAxisMass, thnAxisPt, thnAxisY}; + std::vector axesMcGen = {thnAxisPt, thnAxisPtB, thnAxisY, thnAxisOrigin}; + + if (doprocessDataDCAFitterFT0C || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterFT0M || doprocessDataDCAFitterMlFT0M || doprocessDataKFParticleFT0C || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleFT0M || doprocessDataKFParticleMlFT0M) { + axes.push_back(thnAxisCent); + axes.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + axes.push_back(thnAxisCentMc); + axes.emplace_back(thnConfigAxisNumPvContr); + axesMcGen.push_back(thnAxisCentMc); + axesMcGen.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitter || doprocessMcDCAFitterMl || doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticle || doprocessMcKFParticleMl || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGen", "Gen. #Omega_{c}^{0} from charm and beauty", HistType::kTHnSparseD, axesMcGen); + registry.get(HIST("hMcGen"))->Sumw2(); + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGenWithRecoColl", "Gen. #Omega_{c}^{0} from charm and beauty (associated to a reco collision)", HistType::kTHnSparseD, axesMcGen); + registry.add("hNumRecoCollPerMcColl", "Number of reco collisions associated to a mc collision;Num. reco. coll. per Mc coll.;", {HistType::kTH1D, {{10, -0.5, 9.5}}}); + registry.get(HIST("hMcGenWithRecoColl"))->Sumw2(); + } + + axes.push_back(thnAxisPtB); + axes.push_back(thnAxisOrigin); + axes.push_back(thnAxisMatchFlag); + } + + if (doprocessDataKFParticleMl || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleMlFT0M || doprocessDataDCAFitterMl || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterMlFT0M || doprocessMcKFParticleMl || doprocessMcKFParticleMlFT0M || doprocessMcDCAFitterMl || doprocessMcDCAFitterMlFT0M) { + axes.push_back(thnAxisPromptScore); + } + + registry.add("hReco", "Reco. #Omega_{c}^{0} candidates", HistType::kTHnSparseD, axes); + registry.get(HIST("hReco"))->Sumw2(); + } + + template + void processData(const CandType& candidates) + { + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processDataCent(const CandType& candidate, CollType const& collision) + { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + return; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + return; + } + + auto numPvContributors = collision.numContrib(); + if constexpr (UseCentrality) { + float const cent = o2::hf_centrality::getCentralityColl(collision); + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors); + } + } else { + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processMc(const CandType& candidates, Omegac0Gen const& mcParticles) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt); + } + } + } + + template + void processMcCent(const CandType& candidates, Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, McCollisionWithCents const&) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + auto collision = candidate.template collision_as(); + uint16_t const numPvContributors = collision.numContrib(); + float const mcCent = o2::hf_centrality::getCentralityColl(collision.template mcCollision_as()); + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + auto mcCollision = particle.template mcCollision_as(); + + int maxNumContrib = 0; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcCollision.globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; + } + + float const mcCent = o2::hf_centrality::getCentralityColl(mcCollision); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + + registry.fill(HIST("hNumRecoCollPerMcColl"), recoCollsPerMcColl.size()); + + // fill sparse only for gen particles associated to a reconstructed collision + if (recoCollsPerMcColl.size() >= 1) { + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + } + } + } + + /////////////////////////////////// + /// Data with DCAFitter // + /////////////////////////////////// + + void processDataDCAFitter(Omegac0Cands const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitter, "process data with DCAFitter", false); + + void processDataDCAFitterMl(Omegac0CandsMl const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMl, "process data with DCAFitter, ML selections", false); + + void processDataDCAFitterFT0C(Omegac0Cands const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0C, "process data with DCAFitter, FT0C centrality", false); + + void processDataDCAFitterMlFT0C(Omegac0CandsMl const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0C, "process data with DCAFitter, ML selections, FT0C centrality", false); + + void processDataDCAFitterFT0M(Omegac0Cands const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0M, "process data with DCAFitter, FT0M centrality", false); + + void processDataDCAFitterMlFT0M(Omegac0CandsMl const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0M, "process data with DCAFitter, ML selections, FT0M centrality", false); + + //////////////////////////////////// + /// Data with KFParticle // + //////////////////////////////////// + + void processDataKFParticle(Omegac0CandsKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticle, "process data with KFParticle", false); + + void processDataKFParticleMl(Omegac0CandsMlKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMl, "process data with KFParticle, ML selections", false); + + void processDataKFParticleFT0C(Omegac0CandsKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0C, "process data with KFParticle, FT0C centrality", false); + + void processDataKFParticleMlFT0C(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0C, "process data with KFParticle, ML selections, FT0C centrality", false); + + void processDataKFParticleFT0M(Omegac0CandsKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0M, "process data with KFParticle, FT0M centrality", false); + + void processDataKFParticleMlFT0M(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0M, "process data with KFParticle, ML selections, FT0M centrality", false); + + /////////////////////////////////// + /// MC with DCAFitter // + /////////////////////////////////// + + void processMcDCAFitter(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitter, "Process MC with DCAFitter", false); + + void processMcDCAFitterMl(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMl, "Process MC with DCAFitter, ML selections", false); + + void processMcDCAFitterFT0M(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterFT0M, "Process MC with DCAFitter, FT0M centrality (from MC)", false); + + void processMcDCAFitterMlFT0M(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMlFT0M, "Process MC with DCAFitter, ML selections, FT0M centrality (from MC)", false); + + //////////////////////////////////// + /// MC with KFParticle // + //////////////////////////////////// + + void processMcKFParticle(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticle, "Process MC with KFParticle", false); + + void processMcKFParticleMl(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMl, "Process MC with KFParticle, ML selections", false); + + void processMcKFParticleFT0M(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleFT0M, "Process MC with KFParticle, FT0M centrality (from MC)", false); + + void processMcKFParticleMlFT0M(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMlFT0M, "Process MC with KFParticle, ML selections, FT0M centrality (from MC)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/CMakeLists.txt b/PWGHF/TableProducer/CMakeLists.txt index 174cbfa9621..657dd42e485 100644 --- a/PWGHF/TableProducer/CMakeLists.txt +++ b/PWGHF/TableProducer/CMakeLists.txt @@ -172,6 +172,11 @@ o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi-qa + SOURCES candidateSelectorOmegac0ToOmegaPiQa.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(candidate-selector-omegac0-xic0-to-omega-ka SOURCES candidateSelectorOmegac0Xic0ToOmegaKa.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore @@ -279,6 +284,11 @@ o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi-qa + SOURCES treeCreatorOmegac0ToOmegaPiQa.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(tree-creator-to-xi-pi SOURCES treeCreatorToXiPi.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..2f3ff74b2fe --- /dev/null +++ b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,887 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file candidateSelectorOmegac0ToOmegaPiQa.cxx +/// \brief Omegac0 → Omega Pi selection task + +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelectorPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::analysis; + +enum PidInfoStored { + PiFromLam = 0, + PrFromLam, + KaFromCasc, + PiFromCharm +}; + +enum { + doDcaFitter = 0, + doKfParticle +}; + +/// Struct for applying Omegac0 -> Omega pi selection cuts +struct HfCandidateSelectorToOmegaPiQa { + // DCAFitter and KFParticle + Produces hfSelToOmegaPi; + // ML selection - filled for both DCAFitter and KFParticle + Produces hfMlSelToOmegaPi; + + // cuts from SelectorCuts.h - pT dependent cuts + Configurable> binsPt{"binsPt", std::vector{hf_cuts_omegac_to_omega_pi::vecBinsPt}, "pT bin limits"}; + Configurable> cuts{"cuts", {hf_cuts_omegac_to_omega_pi::Cuts[0], hf_cuts_omegac_to_omega_pi::NBinsPt, hf_cuts_omegac_to_omega_pi::NCutVars, hf_cuts_omegac_to_omega_pi::labelsPt, hf_cuts_omegac_to_omega_pi::labelsCutVar}, "OmegaC0 candidate selection per pT bin"}; + + // ML inference + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTOmegac0"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_Omegac0ToOmegaPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + + // LF analysis selections + Configurable radiusCascMin{"radiusCascMin", 0.5, "Min cascade radius"}; + Configurable radiusV0Min{"radiusV0Min", 1.1, "Min V0 radius"}; + Configurable cosPAV0Min{"cosPAV0Min", 0.97, "Min value CosPA V0"}; + Configurable cosPACascMin{"cosPACascMin", 0.97, "Min value CosPA cascade"}; + Configurable dcaV0DauMax{"dcaV0DauMax", 1.0, "Max DCA V0 daughters"}; + Configurable dcaCascDauMax{"dcaCascDauMax", 1.0, "Max DCA cascade daughters"}; + Configurable dcaNegToPvMin{"dcaNegToPvMin", 0.06, "DCA Neg To PV"}; + Configurable dcaPosToPvMin{"dcaPosToPvMin", 0.06, "DCA Pos To PV"}; + Configurable dcaBachToPvMin{"dcaBachToPvMin", 0.04, "DCA Bach To PV"}; + Configurable applyTrkSelLf{"applyTrkSelLf", true, "Apply track selection for LF daughters"}; + + // Mass window + Configurable v0MassWindow{"v0MassWindow", 0.01, "V0 mass window"}; + Configurable cascadeMassWindow{"cascadeMassWindow", 0.01, "Cascade mass window"}; + Configurable invMassCharmBaryonMin{"invMassCharmBaryonMin", 2.3, "Lower limit invariant mass spectrum charm baryon"}; + Configurable invMassCharmBaryonMax{"invMassCharmBaryonMax", 3.1, "Upper limit invariant mass spectrum charm baryon"}; + + // kinematic selections + Configurable etaTrackCharmBachMax{"etaTrackCharmBachMax", 0.8, "Max absolute value of eta for charm baryon bachelor"}; + Configurable etaTrackLFDauMax{"etaTrackLFDauMax", 1.0, "Max absolute value of eta for V0 and cascade daughters"}; + Configurable ptKaFromCascMin{"ptKaFromCascMin", 0.15, "Min pT kaon <- casc"}; + + Configurable impactParameterXYPiFromCharmBaryonMin{"impactParameterXYPiFromCharmBaryonMin", 0., "Min dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYPiFromCharmBaryonMax{"impactParameterXYPiFromCharmBaryonMax", 10., "Max dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYCascMin{"impactParameterXYCascMin", 0., "Min dcaxy cascade track to PV"}; + Configurable impactParameterXYCascMax{"impactParameterXYCascMax", 10., "Max dcaxy cascade track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMin{"impactParameterZPiFromCharmBaryonMin", 0., "Min dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMax{"impactParameterZPiFromCharmBaryonMax", 10., "Max dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZCascMin{"impactParameterZCascMin", 0., "Min dcaz cascade track to PV"}; + Configurable impactParameterZCascMax{"impactParameterZCascMax", 10., "Max dcaz cascade track to PV"}; + + Configurable ptCandMin{"ptCandMin", 0., "Lower bound of candidate pT"}; + Configurable ptCandMax{"ptCandMax", 50., "Upper bound of candidate pT"}; + + Configurable dcaCharmBaryonDauMax{"dcaCharmBaryonDauMax", 2.0, "Max DCA charm baryon daughters"}; + + // PID options + Configurable usePidTpcOnly{"usePidTpcOnly", true, "Perform PID using only TPC"}; + Configurable usePidTpcTofCombined{"usePidTpcTofCombined", false, "Perform PID using TPC or TOF"}; + + // PID - TPC selections + Configurable ptPiPidTpcMin{"ptPiPidTpcMin", -1, "Lower bound of track pT for TPC PID for pion selection"}; + Configurable ptPiPidTpcMax{"ptPiPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for pion selection"}; + Configurable nSigmaTpcPiMax{"nSigmaTpcPiMax", 3., "Nsigma cut on TPC only for pion selection"}; + Configurable nSigmaTpcCombinedPiMax{"nSigmaTpcCombinedPiMax", 0., "Nsigma cut on TPC combined with TOF for pion selection"}; + + Configurable ptPrPidTpcMin{"ptPrPidTpcMin", -1, "Lower bound of track pT for TPC PID for proton selection"}; + Configurable ptPrPidTpcMax{"ptPrPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for proton selection"}; + Configurable nSigmaTpcPrMax{"nSigmaTpcPrMax", 3., "Nsigma cut on TPC only for proton selection"}; + Configurable nSigmaTpcCombinedPrMax{"nSigmaTpcCombinedPrMax", 0., "Nsigma cut on TPC combined with TOF for proton selection"}; + + Configurable ptKaPidTpcMin{"ptKaPidTpcMin", -1, "Lower bound of track pT for TPC PID for kaon selection"}; + Configurable ptKaPidTpcMax{"ptKaPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for kaon selection"}; + Configurable nSigmaTpcKaMax{"nSigmaTpcKaMax", 3., "Nsigma cut on TPC only for kaon selection"}; + Configurable nSigmaTpcCombinedKaMax{"nSigmaTpcCombinedKaMax", 0., "Nsigma cut on TPC combined with TOF for kaon selection"}; + + // PID - TOF selections + Configurable ptPiPidTofMin{"ptPiPidTofMin", -1, "Lower bound of track pT for TOF PID for pion selection"}; + Configurable ptPiPidTofMax{"ptPiPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for pion selection"}; + Configurable nSigmaTofPiMax{"nSigmaTofPiMax", 3., "Nsigma cut on TOF only for pion selection"}; + Configurable nSigmaTofCombinedPiMax{"nSigmaTofCombinedPiMax", 0., "Nsigma cut on TOF combined with TPC for pion selection"}; + + Configurable ptPrPidTofMin{"ptPrPidTofMin", -1, "Lower bound of track pT for TOF PID for proton selection"}; + Configurable ptPrPidTofMax{"ptPrPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for proton selection"}; + Configurable nSigmaTofPrMax{"nSigmaTofPrMax", 3., "Nsigma cut on TOF only for proton selection"}; + Configurable nSigmaTofCombinedPrMax{"nSigmaTofCombinedPrMax", 0., "Nsigma cut on TOF combined with TPC for proton selection"}; + + Configurable ptKaPidTofMin{"ptKaPidTofMin", -1, "Lower bound of track pT for TOF PID for kaon selection"}; + Configurable ptKaPidTofMax{"ptKaPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for kaon selection"}; + Configurable nSigmaTofKaMax{"nSigmaTofKaMax", 3., "Nsigma cut on TOF only for kaon selection"}; + Configurable nSigmaTofCombinedKaMax{"nSigmaTofCombinedKaMax", 0., "Nsigma cut on TOF combined with TPC for kaon selection"}; + + // detector clusters selections + Configurable nClustersTpcMin{"nClustersTpcMin", 70, "Minimum number of TPC clusters requirement"}; + Configurable nTpcCrossedRowsMin{"nTpcCrossedRowsMin", 70, "Minimum number of TPC crossed rows requirement"}; + Configurable tpcCrossedRowsOverFindableClustersRatioMin{"tpcCrossedRowsOverFindableClustersRatioMin", 0.8, "Minimum ratio TPC crossed rows over findable clusters requirement"}; + Configurable tpcChi2PerClusterMax{"tpcChi2PerClusterMax", 4, "Maximum value of chi2 fit over TPC clusters"}; + Configurable nClustersItsMin{"nClustersItsMin", 3, "Minimum number of ITS clusters requirement for pi <- charm baryon"}; + Configurable nClustersItsInnBarrMin{"nClustersItsInnBarrMin", 1, "Minimum number of ITS clusters in inner barrel requirement for pi <- charm baryon"}; + Configurable itsChi2PerClusterMax{"itsChi2PerClusterMax", 36, "Maximum value of chi2 fit over ITS clusters for pi <- charm baryon"}; + + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseDca; + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseKf; + std::vector outputMlOmegac; + o2::ccdb::CcdbApi ccdbApi; + + TrackSelectorPi selectorPion; + TrackSelectorPr selectorProton; + TrackSelectorKa selectorKaon; + + using TracksSel = soa::Join; + using TracksSelLf = soa::Join; + + HistogramRegistry registry{"registry"}; // for QA of selections + + struct : ConfigurableGroup { + //// KF selection + std::string prefix = "kfSel"; + Configurable applyKFpreselections{"applyKFpreselections", false, "Apply KFParticle related rejection"}; + Configurable applyCompetingCascRejection{"applyCompetingCascRejection", false, "Apply competing Xi(for Omegac0) rejection"}; + Configurable cascadeRejMassWindow{"cascadeRejMassWindow", 0.01, "competing Xi(for Omegac0) rejection mass window"}; + Configurable v0LdlMin{"v0LdlMin", 3., "Minimum value of l/dl of V0"}; // l/dl and Chi2 are to be determined + Configurable cascLdlMin{"cascLdlMin", 1., "Minimum value of l/dl of casc"}; + Configurable omegacLdlMax{"omegacLdlMax", 5., "Maximum value of l/dl of Omegac"}; + Configurable cTauOmegacMax{"cTauOmegacMax", 0.4, "lifetime τ of Omegac"}; + Configurable v0Chi2OverNdfMax{"v0Chi2OverNdfMax", 100., "Maximum chi2Geo/NDF of V0"}; + Configurable cascChi2OverNdfMax{"cascChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of casc"}; + Configurable omegacChi2OverNdfMax{"omegacChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of Omegac"}; + Configurable chi2TopoV0ToCascMax{"chi2TopoV0ToCascMax", 100., "Maximum chi2Topo/NDF of V0ToCas"}; + Configurable chi2TopoOmegacToPvMax{"chi2TopoOmegacToPvMax", 100., "Maximum chi2Topo/NDF of OmegacToPv"}; + Configurable chi2TopoCascToOmegacMax{"chi2TopoCascToOmegacMax", 100., "Maximum chi2Topo/NDF of CascToOmegac"}; + Configurable chi2TopoCascToPvMax{"chi2TopoCascToPvMax", 100., "Maximum chi2Topo/NDF of CascToPv"}; + Configurable decayLenXYOmegacMax{"decayLenXYOmegacMax", 1.5, "Maximum decay lengthXY of Omegac"}; + Configurable decayLenXYCascMin{"decayLenXYCascMin", 1., "Minimum decay lengthXY of Cascade"}; + Configurable decayLenXYLambdaMin{"decayLenXYLambdaMin", 0., "Minimum decay lengthXY of V0"}; + Configurable cosPaCascToOmegacMin{"cosPaCascToOmegacMin", 0.995, "Minimum cosPA of cascade<-Omegac"}; + Configurable cosPaV0ToCascMin{"cosPaV0ToCascMin", 0.99, "Minimum cosPA of V0<-cascade"}; + } kfConfigurableGroup; + + void init(InitContext const&) + { + std::array processesSelector = {doprocessOmegac0SelectorWithDCAFitter, doprocessOmegac0SelectorWithKFParticle}; + const int nProcessesSelector = std::accumulate(processesSelector.begin(), processesSelector.end(), 0); + if (nProcessesSelector != 1) { + LOGP(fatal, "Exactly one process function for selector can be enabled at a time."); + } + + selectorPion.setRangePtTpc(ptPiPidTpcMin, ptPiPidTpcMax); + selectorPion.setRangeNSigmaTpc(-nSigmaTpcPiMax, nSigmaTpcPiMax); + selectorPion.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPiMax, nSigmaTpcCombinedPiMax); + selectorPion.setRangePtTof(ptPiPidTofMin, ptPiPidTofMax); + selectorPion.setRangeNSigmaTof(-nSigmaTofPiMax, nSigmaTofPiMax); + selectorPion.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPiMax, nSigmaTofCombinedPiMax); + + selectorProton.setRangePtTpc(ptPrPidTpcMin, ptPrPidTpcMax); + selectorProton.setRangeNSigmaTpc(-nSigmaTpcPrMax, nSigmaTpcPrMax); + selectorProton.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPrMax, nSigmaTpcCombinedPrMax); + selectorProton.setRangePtTof(ptPrPidTofMin, ptPrPidTofMax); + selectorProton.setRangeNSigmaTof(-nSigmaTofPrMax, nSigmaTofPrMax); + selectorProton.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPrMax, nSigmaTofCombinedPrMax); + + selectorKaon.setRangePtTpc(ptKaPidTpcMin, ptKaPidTpcMax); + selectorKaon.setRangeNSigmaTpc(-nSigmaTpcKaMax, nSigmaTpcKaMax); + selectorKaon.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedKaMax, nSigmaTpcCombinedKaMax); + selectorKaon.setRangePtTof(ptKaPidTofMin, ptKaPidTofMax); + selectorKaon.setRangeNSigmaTof(-nSigmaTofKaMax, nSigmaTofKaMax); + selectorKaon.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedKaMax, nSigmaTofCombinedKaMax); + + const AxisSpec axisSel{2, -0.5, 1.5, "status"}; + const AxisSpec axisSelOnLfDca{16, -0.5, 15.5, "status"}; + const AxisSpec axisSelOnLfKf{23, -0.5, 22.5, "status"}; + const AxisSpec axisSelOnHfDca{6, -0.5, 5.5, "status"}; + const AxisSpec axisSelOnHfKf{12, -0.5, 11.5, "status"}; + + // for QA of the selections (bin 0 -> candidates that did not pass the selection, bin 1 -> candidates that passed the selection) + registry.add("hSelSignDec", "hSelSignDec;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hSelStatusCluster", "hSelStatusCluster:# of events Passed;;", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(2, "TpcCluster PiFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(3, "TpcCluster PrFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(4, "TpcCluster KaFromCasc"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(5, "TpcCluster PiFromCharm"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(6, "ItsCluster PiFromCharm"); + + registry.add("hSelStatusPID", "hSelStatusPID;# of events Passed;;", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(2, "Lambda"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(3, "Cascade"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(4, "CharmBaryon"); + + // For QA of LF & HF selection + if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfDca}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "impactParCascXY"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "impactParCascZ"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfDca}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "impactParBachFromCharmXY"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "impactParBachFromCharmZ"); + } + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfKf}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "cosPaV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "v0Chi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(17, "cascChi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(18, "chi2TopoCascToPv"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(19, "chi2TopoV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(20, "v0ldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(21, "cascldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(22, "decayLenXYLambda"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(23, "decayLenXYCasc"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfKf}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "kfptOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "cosPaCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(7, "omegacChi2OverNdf"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(8, "chi2TopoOmegacToPv"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(9, "chi2TopoCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(10, "decayLenXYOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(11, "cTauOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(12, "omegacldl"); + } + + registry.add("hInvMassCharmBaryon", "Charm baryon invariant mass; inv. mass; entries", {HistType::kTH1F, {{500, 2.3, 3.1}}}); + registry.add("hPtCharmBaryon", "Charm baryon transverse momentum; p_{T} (GeV/#it{c}); entries", {HistType::kTH1F, {{8000, 0., 80.}}}); + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelCompetingCasc", "hSelCompetingCasc;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hInvMassXiMinus_rej_cut", "hInvMassXiMinus_rej_cut;m_{#Lambda#pi} under Xi hypothesis (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{1000, 1.25f, 1.65f}}}); + } + + // HfMlResponse initialization + if (applyMl) { + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hBDTScoreKF", "hBDTScoreKF", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseKf.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseKf.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseKf.setModelPathsLocal(onnxFileNames); + } + hfMlResponseKf.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseKf.init(); + } else if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hBDTScoreDCA", "hBDTScoreDCA", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseDca.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseDca.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseDca.setModelPathsLocal(onnxFileNames); + } + hfMlResponseDca.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseDca.init(); + } + } + } + + // LF cuts - Cuts on LF tracks reco + // Selection on LF related informations + // returns true if all cuts are passed + template + bool selectOnLf(const T& candidate) + { + + registry.fill(HIST("hSelStatusLf"), 0.0); + + // Eta selection of V0, Cascade daughters + double etaV0PosDau = candidate.etaV0PosDau(); + double etaV0NegDau = candidate.etaV0NegDau(); + double etaKaFromCasc = candidate.etaBachFromCasc(); + + if (std::abs(etaV0PosDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 1.0); + + if (std::abs(etaV0NegDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 2.0); + + if (std::abs(etaKaFromCasc) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 3.0); + + // Minimum radius cut + double radiusV0 = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxV0(), candidate.yDecayVtxV0()); + double radiusCasc = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxCascade(), candidate.yDecayVtxCascade()); + + if (radiusV0 < radiusV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 4.0); + if (radiusCasc < radiusCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 5.0); + + // Cosine of pointing angle + if (candidate.cosPAV0() < cosPAV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 6.0); + if (candidate.cosPACasc() < cosPACascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 7.0); + + // Distance of Closest Approach(DCA) + if (candidate.dcaV0Dau() > dcaV0DauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 8.0); + + if (candidate.dcaCascDau() > dcaCascDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 9.0); + + if (std::abs(candidate.dcaXYToPvV0Dau0()) < dcaPosToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 10.0); + + if (std::abs(candidate.dcaXYToPvV0Dau1()) < dcaNegToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 11.0); + + if (std::abs(candidate.dcaXYToPvCascDau()) < dcaBachToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 12.0); + + // pT: Bachelor + double ptKaFromCasc = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCasc(), candidate.pyBachFromCasc()); + if (std::abs(ptKaFromCasc) < ptKaFromCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 13.0); + + // Extra cuts for KFParticle + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Cosine of Pointing angle + if (candidate.cosPaV0ToCasc() < kfConfigurableGroup.cosPaV0ToCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + + // Chi2 + if (candidate.v0Chi2OverNdf() < 0 || candidate.v0Chi2OverNdf() > kfConfigurableGroup.v0Chi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + if (candidate.cascChi2OverNdf() < 0 || candidate.cascChi2OverNdf() > kfConfigurableGroup.cascChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 16.0); + if (candidate.chi2TopoCascToPv() < 0 || candidate.chi2TopoCascToPv() > kfConfigurableGroup.chi2TopoCascToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 17.0); + if (candidate.chi2TopoV0ToCasc() < 0 || candidate.chi2TopoV0ToCasc() > kfConfigurableGroup.chi2TopoV0ToCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 18.0); + + // ldl + if (candidate.v0ldl() < kfConfigurableGroup.v0LdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 19.0); + if (candidate.cascldl() < kfConfigurableGroup.cascLdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 20.0); + + // Decay length + if (std::abs(candidate.decayLenXYLambda()) < kfConfigurableGroup.decayLenXYLambdaMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 21.0); + if (std::abs(candidate.decayLenXYCasc()) < kfConfigurableGroup.decayLenXYCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 22.0); + + // Competing Xi rejection + if (kfConfigurableGroup.applyCompetingCascRejection) { + const auto invMassXiHypothesis = candidate.cascRejectInvmass(); + + if (std::abs(invMassXiHypothesis - o2::constants::physics::MassXiMinus) < kfConfigurableGroup.cascadeRejMassWindow) { + registry.fill(HIST("hSelCompetingCasc"), 0.0); + return false; + } + + registry.fill(HIST("hSelCompetingCasc"), 1.0); + registry.fill(HIST("hInvMassXiMinus_rej_cut"), invMassXiHypothesis); + } + } + } else { + // Impact parameter + if (std::abs(candidate.impactParCascXY()) < impactParameterXYCascMin || std::abs(candidate.impactParCascXY()) > impactParameterXYCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + if (std::abs(candidate.impactParCascZ()) < impactParameterZCascMin || std::abs(candidate.impactParCascZ()) > impactParameterZCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + } + + // If passes all cuts, return true + return true; + } + + // HF cuts - Cuts on Charm baryon reco + // Apply cuts with charm baryon & charm bachelor related informations + // returns true if all cuts are passed + template + bool selectOnHf(const T& candidate, const int& inputPtBin) + { + registry.fill(HIST("hSelStatusHf"), 0.0); + + // eta selection on charm bayron bachelor + if (std::abs(candidate.etaBachFromCharmBaryon()) > etaTrackCharmBachMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 1.0); + // Distance of Closest Approach(DCA) + if (candidate.dcaCharmBaryonDau() > dcaCharmBaryonDauMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 2.0); + + // pT: Charm Bachelor + double ptPiFromCharmBaryon = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()); + if (inputPtBin < 0 || ptPiFromCharmBaryon < cuts->get(inputPtBin, "pT pi from Omegac")) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 3.0); + + // specific selections with KFParticle output + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Omegac Pt selection + if (std::abs(candidate.kfptOmegac()) <= ptCandMin || std::abs(candidate.kfptOmegac()) >= ptCandMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + + // Cosine of pointing angle + if (candidate.cosPaCascToOmegac() < kfConfigurableGroup.cosPaCascToOmegacMin) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + + // Chi2 + if (candidate.omegacChi2OverNdf() < 0 || candidate.omegacChi2OverNdf() > kfConfigurableGroup.omegacChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 6.0); + if (candidate.chi2TopoOmegacToPv() < 0 || candidate.chi2TopoOmegacToPv() > kfConfigurableGroup.chi2TopoOmegacToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 7.0); + if (candidate.chi2TopoCascToOmegac() < 0 || candidate.chi2TopoCascToOmegac() > kfConfigurableGroup.chi2TopoCascToOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 8.0); + + // Decay Length + if (std::abs(candidate.decayLenXYOmegac()) > kfConfigurableGroup.decayLenXYOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 9.0); + + // ctau + if (std::abs(candidate.cTauOmegac()) > kfConfigurableGroup.cTauOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 10.0); + + // Omegac l/dl + if (candidate.omegacldl() > kfConfigurableGroup.omegacLdlMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 11.0); + } + } else { + // Impact parameter + if ((std::abs(candidate.impactParBachFromCharmBaryonXY()) < impactParameterXYPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonXY()) > impactParameterXYPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + if ((std::abs(candidate.impactParBachFromCharmBaryonZ()) < impactParameterZPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonZ()) > impactParameterZPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + } + + // If passes all cuts, return true + return true; + } + + template + void runOmegac0Selector(TCandTable const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + // looping over charm baryon candidates + for (const auto& candidate : candidates) { + + bool resultSelections = true; // True if the candidate passes all the selections, False otherwise + outputMlOmegac.clear(); + + auto trackV0PosDau = lfTracks.rawIteratorAt(candidate.posTrackId()); + auto trackV0NegDau = lfTracks.rawIteratorAt(candidate.negTrackId()); + auto trackKaFromCasc = lfTracks.rawIteratorAt(candidate.bachelorId()); + auto trackPiFromCharm = tracks.rawIteratorAt(candidate.bachelorFromCharmBaryonId()); + + auto trackPiFromLam = trackV0NegDau; + auto trackPrFromLam = trackV0PosDau; + + int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + + if (signDecay > 0) { + trackPiFromLam = trackV0PosDau; + trackPrFromLam = trackV0NegDau; + registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay + } else { + registry.fill(HIST("hSelSignDec"), 0); // particle decay + } + + // pT selection + auto ptCandOmegac = RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + + if (ptCandOmegac < ptCandMin || ptCandOmegac > ptCandMax) { + resultSelections = false; + } + + int pTBin = findBin(binsPt, ptCandOmegac); + if (pTBin == -1) { + resultSelections = false; + } + + // Topological selection + const bool selectionResOnLF = selectOnLf(candidate); + const bool selectionResOnHF = selectOnHf(candidate, pTBin); + if (!selectionResOnLF || !selectionResOnHF) { + resultSelections = false; + } + + // TPC clusters selections + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 0.0); + } + if (applyTrkSelLf) { + if (!isSelectedTrackTpcQuality(trackPiFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 1.0); + } + } + + if (!isSelectedTrackTpcQuality(trackPrFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 2.0); + } + } + + if (!isSelectedTrackTpcQuality(trackKaFromCasc, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 3.0); + } + } + } + + if (!isSelectedTrackTpcQuality(trackPiFromCharm, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 4.0); + } + } + + // ITS clusters selection + if (!isSelectedTrackItsQuality(trackPiFromCharm, nClustersItsMin, itsChi2PerClusterMax) || trackPiFromCharm.itsNClsInnerBarrel() < nClustersItsInnBarrMin) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 5.0); + } + } + + // Track level PID selection + if (resultSelections) { + registry.fill(HIST("hSelStatusPID"), 0.0); + } + int statusPidPrFromLam = -999; + int statusPidPiFromLam = -999; + int statusPidKaFromCasc = -999; + int statusPidPiFromCharmBaryon = -999; + + int infoTpcStored = 0; + int infoTofStored = 0; + + if (usePidTpcOnly == usePidTpcTofCombined) { + LOGF(fatal, "Check the PID configurables, usePidTpcOnly and usePidTpcTofCombined can't have the same value"); + } + + if (trackPiFromLam.hasTPC()) { + SETBIT(infoTpcStored, PiFromLam); + } + if (trackPrFromLam.hasTPC()) { + SETBIT(infoTpcStored, PrFromLam); + } + if (trackKaFromCasc.hasTPC()) { + SETBIT(infoTpcStored, KaFromCasc); + } + if (trackPiFromCharm.hasTPC()) { + SETBIT(infoTpcStored, PiFromCharm); + } + if (trackPiFromLam.hasTOF()) { + SETBIT(infoTofStored, PiFromLam); + } + if (trackPrFromLam.hasTOF()) { + SETBIT(infoTofStored, PrFromLam); + } + if (trackKaFromCasc.hasTOF()) { + SETBIT(infoTofStored, KaFromCasc); + } + if (trackPiFromCharm.hasTOF()) { + SETBIT(infoTofStored, PiFromCharm); + } + + if (usePidTpcOnly) { + statusPidPrFromLam = selectorProton.statusTpc(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpc(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpc(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpc(trackPiFromCharm); + } else if (usePidTpcTofCombined) { + statusPidPrFromLam = selectorProton.statusTpcOrTof(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpcOrTof(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpcOrTof(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpcOrTof(trackPiFromCharm); + } + + bool statusPidLambda = (statusPidPrFromLam == TrackSelectorPID::Accepted) && (statusPidPiFromLam == TrackSelectorPID::Accepted); + if (statusPidLambda && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 1.0); + } + bool statusPidCascade = (statusPidLambda && statusPidKaFromCasc == TrackSelectorPID::Accepted); + if (statusPidCascade && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 2.0); + } + bool statusPidCharmBaryon = (statusPidCascade && statusPidPiFromCharmBaryon == TrackSelectorPID::Accepted); + if (statusPidCharmBaryon && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 3.0); + } + + // invariant mass cuts + bool statusInvMassLambda = false; + bool statusInvMassCascade = false; + bool statusInvMassCharmBaryon = false; + + double const invMassLambda = candidate.invMassLambda(); + double const invMassCascade = candidate.invMassCascade(); + double const invMassCharmBaryon = candidate.invMassCharmBaryon(); + + if (std::abs(invMassLambda - o2::constants::physics::MassLambda0) < v0MassWindow) { + statusInvMassLambda = true; + } + if (std::abs(invMassCascade - o2::constants::physics::MassOmegaMinus) < cascadeMassWindow) { + statusInvMassCascade = true; + } + if ((invMassCharmBaryon >= invMassCharmBaryonMin) && (invMassCharmBaryon <= invMassCharmBaryonMax)) { + statusInvMassCharmBaryon = true; + } + + // Fill in selection result + if (!statusPidLambda || !statusPidCascade || !statusPidCharmBaryon || + !statusInvMassLambda || !statusInvMassCascade || !statusInvMassCharmBaryon) { + resultSelections = false; + } + + // Check candidate pT range for ML inference + if (applyMl && findBin(binsPtMl, ptCandOmegac) == -1) { + resultSelections = false; + } + + // ML BDT selection + if (applyMl && resultSelections) { + bool isSelectedMlOmegac = false; + std::vector inputFeaturesOmegaC = {}; + + if constexpr (svReco == doKfParticle) { + inputFeaturesOmegaC = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseKf.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreKF"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } else if constexpr (svReco == doDcaFitter) { + inputFeaturesOmegaC = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseDca.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreDCA"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } + } + + if (applyMl) { + hfMlSelToOmegaPi(outputMlOmegac); + } + + hfSelToOmegaPi(statusPidLambda, statusPidCascade, statusPidCharmBaryon, statusInvMassLambda, statusInvMassCascade, statusInvMassCharmBaryon, resultSelections, infoTpcStored, infoTofStored, + trackPiFromCharm.tpcNSigmaPi(), trackKaFromCasc.tpcNSigmaKa(), trackPiFromLam.tpcNSigmaPi(), trackPrFromLam.tpcNSigmaPr(), + trackPiFromCharm.tofNSigmaPi(), trackKaFromCasc.tofNSigmaKa(), trackPiFromLam.tofNSigmaPi(), trackPrFromLam.tofNSigmaPr()); + + // Fill in invariant mass histogram + if (statusPidLambda && statusPidCascade && statusPidCharmBaryon && statusInvMassLambda && statusInvMassCascade && statusInvMassCharmBaryon && resultSelections) { + registry.fill(HIST("hInvMassCharmBaryon"), invMassCharmBaryon); + if constexpr (svReco == doKfParticle) { + registry.fill(HIST("hPtCharmBaryon"), candidate.kfptOmegac()); + } else { + registry.fill(HIST("hPtCharmBaryon"), ptCandOmegac); + } + } + + } // end of candidate loop + } // end run function + + /////////////////////////////////// + /// Process with DCAFitter // + /////////////////////////////////// + void processOmegac0SelectorWithDCAFitter(aod::HfCandToOmegaPi const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithDCAFitter, "Omegac0 candidate selection with DCAFitter output", true); + + //////////////////////////////////// + /// Process with KFParticle // + //////////////////////////////////// + void processOmegac0SelectorWithKFParticle(soa::Join const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithKFParticle, "Omegac0 candidate selection with KFParticle output", false); + +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..6d4855fa863 --- /dev/null +++ b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,659 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorOmegac0ToOmegaPiQa.cxx +/// \brief Writer of the omegac0 to Omega Pi candidates in the form of flat tables to be stored in TTrees. +/// In this file are defined and filled the output tables +/// +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// SV Reco method +enum { + DCAFITTER = 0, + KFPARTICLE +}; + +namespace o2::aod +{ +namespace full +{ +// collision info +DECLARE_SOA_COLUMN(IsEventSel8, isEventSel8, bool); +DECLARE_SOA_COLUMN(IsEventSelZ, isEventSelZ, bool); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +// from creator +DECLARE_SOA_COLUMN(XPv, xPv, float); +DECLARE_SOA_COLUMN(YPv, yPv, float); +DECLARE_SOA_COLUMN(ZPv, zPv, float); +DECLARE_SOA_COLUMN(XDecayVtxCharmBaryon, xDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(YDecayVtxCharmBaryon, yDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(ZDecayVtxCharmBaryon, zDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(XDecayVtxCascade, xDecayVtxCascade, float); +DECLARE_SOA_COLUMN(YDecayVtxCascade, yDecayVtxCascade, float); +DECLARE_SOA_COLUMN(ZDecayVtxCascade, zDecayVtxCascade, float); +DECLARE_SOA_COLUMN(XDecayVtxV0, xDecayVtxV0, float); +DECLARE_SOA_COLUMN(YDecayVtxV0, yDecayVtxV0, float); +DECLARE_SOA_COLUMN(ZDecayVtxV0, zDecayVtxV0, float); +DECLARE_SOA_COLUMN(SignDecay, signDecay, int8_t); // sign of ka <- omega +DECLARE_SOA_COLUMN(PxCharmBaryon, pxCharmBaryon, float); +DECLARE_SOA_COLUMN(PyCharmBaryon, pyCharmBaryon, float); +DECLARE_SOA_COLUMN(PzCharmBaryon, pzCharmBaryon, float); +DECLARE_SOA_COLUMN(PxCasc, pxCasc, float); +DECLARE_SOA_COLUMN(PyCasc, pyCasc, float); +DECLARE_SOA_COLUMN(PzCasc, pzCasc, float); +DECLARE_SOA_COLUMN(PxPiFromCharmBaryon, pxPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PyPiFromCharmBaryon, pyPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PzPiFromCharmBaryon, pzPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxLambda, pxLambda, float); +DECLARE_SOA_COLUMN(PyLambda, pyLambda, float); +DECLARE_SOA_COLUMN(PzLambda, pzLambda, float); +DECLARE_SOA_COLUMN(PtCharmBaryon, ptCharmBaryon, float); +DECLARE_SOA_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxKaFromCasc, pxKaFromCasc, float); +DECLARE_SOA_COLUMN(PyKaFromCasc, pyKaFromCasc, float); +DECLARE_SOA_COLUMN(PzKaFromCasc, pzKaFromCasc, float); +DECLARE_SOA_COLUMN(PxPosV0Dau, pxPosV0Dau, float); +DECLARE_SOA_COLUMN(PyPosV0Dau, pyPosV0Dau, float); +DECLARE_SOA_COLUMN(PzPosV0Dau, pzPosV0Dau, float); +DECLARE_SOA_COLUMN(PxNegV0Dau, pxNegV0Dau, float); +DECLARE_SOA_COLUMN(PyNegV0Dau, pyNegV0Dau, float); +DECLARE_SOA_COLUMN(PzNegV0Dau, pzNegV0Dau, float); +DECLARE_SOA_COLUMN(ImpactParCascXY, impactParCascXY, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonXY, impactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(ImpactParCascZ, impactParCascZ, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonZ, impactParPiFromCharmBaryonZ, float); +DECLARE_SOA_COLUMN(ErrImpactParCascXY, errImpactParCascXY, float); +DECLARE_SOA_COLUMN(ErrImpactParPiFromCharmBaryonXY, errImpactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(InvMassLambda, invMassLambda, float); +DECLARE_SOA_COLUMN(InvMassCascade, invMassCascade, float); +DECLARE_SOA_COLUMN(InvMassCharmBaryon, invMassCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAV0, cosPAV0, float); +DECLARE_SOA_COLUMN(CosPACharmBaryon, cosPACharmBaryon, float); +DECLARE_SOA_COLUMN(CosPACasc, cosPACasc, float); +DECLARE_SOA_COLUMN(CosPAXYV0, cosPAXYV0, float); +DECLARE_SOA_COLUMN(CosPAXYCharmBaryon, cosPAXYCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAXYCasc, cosPAXYCasc, float); +DECLARE_SOA_COLUMN(CTauCascade, cTauCascade, float); +DECLARE_SOA_COLUMN(CTauV0, cTauV0, float); +DECLARE_SOA_COLUMN(EtaV0PosDau, etaV0PosDau, float); +DECLARE_SOA_COLUMN(EtaV0NegDau, etaV0NegDau, float); +DECLARE_SOA_COLUMN(EtaKaFromCasc, etaKaFromCasc, float); +DECLARE_SOA_COLUMN(EtaPiFromCharmBaryon, etaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCharmBaryon, etaCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCascade, etaCascade, float); +DECLARE_SOA_COLUMN(EtaV0, etaV0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau0, dcaXYToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau1, dcaXYToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaXYToPvCascDau, dcaXYToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau0, dcaZToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau1, dcaZToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaZToPvCascDau, dcaZToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaCascDau, dcaCascDau, float); +DECLARE_SOA_COLUMN(DcaV0Dau, dcaV0Dau, float); +DECLARE_SOA_COLUMN(DcaCharmBaryonDau, dcaCharmBaryonDau, float); +DECLARE_SOA_COLUMN(DecLenCharmBaryon, decLenCharmBaryon, float); +DECLARE_SOA_COLUMN(DecLenCascade, decLenCascade, float); +DECLARE_SOA_COLUMN(DecLenV0, decLenV0, float); +DECLARE_SOA_COLUMN(ErrorDecayLengthCharmBaryon, errorDecayLengthCharmBaryon, float); +DECLARE_SOA_COLUMN(NormImpParCascade, normImpParCascade, double); +DECLARE_SOA_COLUMN(NormImpParPiFromCharmBar, normImpParPiFromCharmBar, double); +DECLARE_SOA_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, double); +DECLARE_SOA_COLUMN(IsPionGlbTrkWoDca, isPionGlbTrkWoDca, bool); +DECLARE_SOA_COLUMN(PionItsNCls, pionItsNCls, uint8_t); +DECLARE_SOA_COLUMN(NTpcRowsPion, nTpcRowsPion, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsKaFromCasc, nTpcRowsKaFromCasc, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsPosV0Dau, nTpcRowsPosV0Dau, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsNegV0Dau, nTpcRowsNegV0Dau, int16_t); +// from creator KF +DECLARE_SOA_COLUMN(KfDcaXYPiFromOmegac, kfDcaXYPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfDcaXYCascToPv, kfDcaXYCascToPv, float); +DECLARE_SOA_COLUMN(Chi2GeoV0, chi2GeoV0, float); +DECLARE_SOA_COLUMN(Chi2GeoCasc, chi2GeoCasc, float); +DECLARE_SOA_COLUMN(Chi2GeoOmegac, chi2GeoOmegac, float); +DECLARE_SOA_COLUMN(Chi2MassV0, chi2MassV0, float); +DECLARE_SOA_COLUMN(Chi2MassCasc, chi2MassCasc, float); +DECLARE_SOA_COLUMN(V0ldl, v0ldl, float); +DECLARE_SOA_COLUMN(Cascldl, cascldl, float); +DECLARE_SOA_COLUMN(Omegacldl, omegacldl, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToPv, chi2TopoV0ToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToPv, chi2TopoCascToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoPiFromOmegacToPv, chi2TopoPiFromOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoOmegacToPv, chi2TopoOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToCasc, chi2TopoV0ToCasc, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToOmegac, chi2TopoCascToOmegac, float); +DECLARE_SOA_COLUMN(DecayLenXYLambda, decayLenXYLambda, float); +DECLARE_SOA_COLUMN(DecayLenXYCasc, decayLenXYCasc, float); +DECLARE_SOA_COLUMN(DecayLenXYOmegac, decayLenXYOmegac, float); +DECLARE_SOA_COLUMN(CosPaV0ToCasc, cosPaV0ToCasc, float); +DECLARE_SOA_COLUMN(CosPaV0ToPv, cosPaV0ToPv, float); +DECLARE_SOA_COLUMN(CosPaCascToOmegac, cosPaCascToOmegac, float); +DECLARE_SOA_COLUMN(CosPaCascToPv, cosPaCascToPv, float); +DECLARE_SOA_COLUMN(CosPaOmegacToPv, cosPaOmegacToPv, float); +DECLARE_SOA_COLUMN(KfRapOmegac, kfRapOmegac, float); +DECLARE_SOA_COLUMN(KfptPiFromOmegac, kfptPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfptOmegac, kfptOmegac, float); +DECLARE_SOA_COLUMN(CosThetaStarPiFromOmegac, cosThetaStarPiFromOmegac, float); +DECLARE_SOA_COLUMN(CtOmegac, ctOmegac, float); +DECLARE_SOA_COLUMN(EtaOmegac, etaOmegac, float); +DECLARE_SOA_COLUMN(V0Ndf, v0Ndf, float); +DECLARE_SOA_COLUMN(CascNdf, cascNdf, float); +DECLARE_SOA_COLUMN(OmegacNdf, omegacNdf, float); +DECLARE_SOA_COLUMN(MassV0Ndf, massV0Ndf, float); +DECLARE_SOA_COLUMN(MassCascNdf, massCascNdf, float); +DECLARE_SOA_COLUMN(V0Chi2OverNdf, v0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(CascChi2OverNdf, cascChi2OverNdf, float); +DECLARE_SOA_COLUMN(OmegacChi2OverNdf, omegacChi2OverNdf, float); +DECLARE_SOA_COLUMN(MassV0Chi2OverNdf, massV0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(MassCascChi2OverNdf, massCascChi2OverNdf, float); +DECLARE_SOA_COLUMN(CascRejectInvmass, cascRejectInvmass, float); +// from creator - MC +DECLARE_SOA_COLUMN(FlagMcMatchRec, flagMcMatchRec, int8_t); // reconstruction level +DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); +DECLARE_SOA_COLUMN(CollisionMatched, collisionMatched, bool); +// from selector +DECLARE_SOA_COLUMN(StatusPidLambda, statusPidLambda, bool); +DECLARE_SOA_COLUMN(StatusPidCascade, statusPidCascade, bool); +DECLARE_SOA_COLUMN(StatusPidCharmBaryon, statusPidCharmBaryon, bool); +DECLARE_SOA_COLUMN(StatusInvMassLambda, statusInvMassLambda, bool); +DECLARE_SOA_COLUMN(StatusInvMassCascade, statusInvMassCascade, bool); +DECLARE_SOA_COLUMN(StatusInvMassCharmBaryon, statusInvMassCharmBaryon, bool); +DECLARE_SOA_COLUMN(ResultSelections, resultSelections, bool); +DECLARE_SOA_COLUMN(PidTpcInfoStored, pidTpcInfoStored, int); +DECLARE_SOA_COLUMN(PidTofInfoStored, pidTofInfoStored, int); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromCharmBaryon, tpcNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TpcNSigmaKaFromCasc, tpcNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromLambda, tpcNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TpcNSigmaPrFromLambda, tpcNSigmaPrFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromCharmBaryon, tofNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TofNSigmaKaFromCasc, tofNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromLambda, tofNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPrFromLambda, tofNSigmaPrFromLambda, float); +} // namespace full + +DECLARE_SOA_TABLE(HfToOmegaPiEvs, "AOD", "HFTOOMEPIEV", + full::IsEventSel8, full::IsEventSelZ); + +DECLARE_SOA_TABLE(HfOmegac0ToOmegaPi, "AOD", "HFTOOMEGAPI", + full::XPv, full::YPv, full::ZPv, full::Centrality, collision::NumContrib, collision::Chi2, + full::XDecayVtxCharmBaryon, full::YDecayVtxCharmBaryon, full::ZDecayVtxCharmBaryon, + full::XDecayVtxCascade, full::YDecayVtxCascade, full::ZDecayVtxCascade, + full::XDecayVtxV0, full::YDecayVtxV0, full::ZDecayVtxV0, + full::SignDecay, + full::PtCharmBaryon, full::PtPiFromCharmBaryon, + full::PxCharmBaryon, full::PyCharmBaryon, full::PzCharmBaryon, + full::PxPiFromCharmBaryon, full::PyPiFromCharmBaryon, full::PzPiFromCharmBaryon, + full::PxKaFromCasc, full::PyKaFromCasc, full::PzKaFromCasc, + full::PxPosV0Dau, full::PyPosV0Dau, full::PzPosV0Dau, + full::PxNegV0Dau, full::PyNegV0Dau, full::PzNegV0Dau, + full::ImpactParCascXY, full::ImpactParPiFromCharmBaryonXY, + full::ErrImpactParCascXY, full::ErrImpactParPiFromCharmBaryonXY, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::CosPAV0, full::CosPACharmBaryon, full::CosPACasc, + full::EtaV0PosDau, full::EtaV0NegDau, full::EtaKaFromCasc, full::EtaPiFromCharmBaryon, full::EtaCharmBaryon, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, + full::ErrorDecayLengthCharmBaryon, full::NormImpParCascade, full::NormImpParPiFromCharmBar, + full::IsPionGlbTrkWoDca, full::PionItsNCls, + full::NTpcRowsPion, full::NTpcRowsKaFromCasc, full::NTpcRowsPosV0Dau, full::NTpcRowsNegV0Dau, + full::StatusPidLambda, full::StatusPidCascade, full::StatusPidCharmBaryon, + full::StatusInvMassLambda, full::StatusInvMassCascade, full::StatusInvMassCharmBaryon, full::ResultSelections, + full::PidTpcInfoStored, full::PidTofInfoStored, + full::TpcNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TpcNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, + full::TofNSigmaPiFromCharmBaryon, full::TofNSigmaKaFromCasc, full::TofNSigmaPiFromLambda, full::TofNSigmaPrFromLambda, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); + +DECLARE_SOA_TABLE(HfKfOmegac, "AOD", "HFKFOMEGAC", + full::Centrality, + collision::NumContrib, + full::TpcNSigmaPiFromCharmBaryon, full::TofNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TofNSigmaKaFromCasc, + full::TpcNSigmaPiFromLambda, full::TofNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, full::TofNSigmaPrFromLambda, + full::KfDcaXYPiFromOmegac, full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, full::KfDcaXYCascToPv, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::Chi2GeoV0, full::Chi2GeoCasc, full::Chi2GeoOmegac, + full::Chi2MassV0, full::Chi2MassCasc, + full::V0ldl, full::Cascldl, full::Omegacldl, + full::Chi2TopoV0ToPv, full::Chi2TopoCascToPv, full::Chi2TopoPiFromOmegacToPv, full::Chi2TopoOmegacToPv, + full::Chi2TopoV0ToCasc, full::Chi2TopoCascToOmegac, + full::DecayLenXYLambda, full::DecayLenXYCasc, full::DecayLenXYOmegac, + full::CosPaV0ToCasc, full::CosPaV0ToPv, full::CosPaCascToOmegac, full::CosPaCascToPv, full::CosPaOmegacToPv, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::KfRapOmegac, full::KfptPiFromOmegac, full::KfptOmegac, + full::CosThetaStarPiFromOmegac, full::CtOmegac, full::EtaOmegac, + full::V0Ndf, full::CascNdf, full::OmegacNdf, + full::MassV0Ndf, full::MassCascNdf, + full::V0Chi2OverNdf, full::CascChi2OverNdf, full::OmegacChi2OverNdf, + full::MassV0Chi2OverNdf, full::MassCascChi2OverNdf, full::CascRejectInvmass, + full::ResultSelections, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); +} // namespace o2::aod + +/// Writes the full information in an output TTree +struct HfTreeCreatorToOmegaPiQa { + + Produces rowCandidate; + Produces rowKfCandidate; + Produces rowEv; + + Configurable zPvCut{"zPvCut", 10., "Cut on absolute value of primary vertex z coordinate"}; + Configurable keepOnlyMcSignal{"keepOnlyMcSignal", true, "Fill MC tree only with signal candidates"}; + + using MyTrackTable = soa::Join; + + using CandSel = soa::Filtered>; + using CandKfSel = soa::Filtered>; + using CandMcSel = soa::Filtered>; + using CandKfMcSel = soa::Filtered>; + + using CollsSel = soa::Join; + using CollsSelWithFT0M = soa::Join; + using CollsWithMcLabels = soa::Join; + using McCollsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterSelectCandidates = aod::hf_sel_toomegapi::resultSelections == true; + + void init(InitContext const&) + { + } + + ////////////////////////////////////////////////////// + // // + // Fill functions to fill in the tables // + // // + ////////////////////////////////////////////////////// + + template + void fillEvent(const T& collision) + { + rowEv(collision.sel8(), std::abs(collision.posZ()) < zPvCut); + } + + template + void fillCandidate(const T& candidate, int8_t flagMc, int8_t originMc, bool collisionMatched, float centrality = -999.f) + { + + // save all candidate information + if constexpr (useCentrality) { + if (centrality < -998.f) { + auto const& collision = candidate.template collision_as(); + centrality = o2::hf_centrality::getCentralityColl(collision); + } + } + + if constexpr (svReco == DCAFITTER) { + rowCandidate(candidate.xPv(), + candidate.yPv(), + candidate.zPv(), + centrality, + candidate.template collision_as().numContrib(), + candidate.template collision_as().chi2(), + candidate.xDecayVtxCharmBaryon(), + candidate.yDecayVtxCharmBaryon(), + candidate.zDecayVtxCharmBaryon(), + candidate.xDecayVtxCascade(), + candidate.yDecayVtxCascade(), + candidate.zDecayVtxCascade(), + candidate.xDecayVtxV0(), + candidate.yDecayVtxV0(), + candidate.zDecayVtxV0(), + candidate.signDecay(), + RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()), + RecoDecay::pt(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()), + candidate.pxCharmBaryon(), + candidate.pyCharmBaryon(), + candidate.pzCharmBaryon(), + candidate.pxBachFromCharmBaryon(), + candidate.pyBachFromCharmBaryon(), + candidate.pzBachFromCharmBaryon(), + candidate.pxBachFromCasc(), + candidate.pyBachFromCasc(), + candidate.pzBachFromCasc(), + candidate.pxPosV0Dau(), + candidate.pyPosV0Dau(), + candidate.pzPosV0Dau(), + candidate.pxNegV0Dau(), + candidate.pyNegV0Dau(), + candidate.pzNegV0Dau(), + candidate.impactParCascXY(), + candidate.impactParBachFromCharmBaryonXY(), + candidate.errImpactParCascXY(), + candidate.errImpactParBachFromCharmBaryonXY(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.cosPAV0(), + candidate.cosPACharmBaryon(), + candidate.cosPACasc(), + candidate.etaV0PosDau(), + candidate.etaV0NegDau(), + candidate.etaBachFromCasc(), + candidate.etaBachFromCharmBaryon(), + candidate.etaCharmBaryon(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.errorDecayLengthCharmBaryon(), + candidate.impactParCascXY() / candidate.errImpactParCascXY(), + candidate.impactParBachFromCharmBaryonXY() / candidate.errImpactParBachFromCharmBaryonXY(), + candidate.template bachelorFromCharmBaryon_as().isGlobalTrackWoDCA(), + candidate.template bachelorFromCharmBaryon_as().itsNCls(), + candidate.template bachelorFromCharmBaryon_as().tpcNClsCrossedRows(), + candidate.template bachelor_as().tpcNClsCrossedRows(), + candidate.template posTrack_as().tpcNClsCrossedRows(), + candidate.template negTrack_as().tpcNClsCrossedRows(), + candidate.statusPidLambda(), + candidate.statusPidCascade(), + candidate.statusPidCharmBaryon(), + candidate.statusInvMassLambda(), + candidate.statusInvMassCascade(), + candidate.statusInvMassCharmBaryon(), + candidate.resultSelections(), + candidate.pidTpcInfoStored(), + candidate.pidTofInfoStored(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaKaFromCasc(), + candidate.tofNSigmaPiFromLambda(), + candidate.tofNSigmaPrFromLambda(), + flagMc, + originMc, + collisionMatched); + } else { + rowKfCandidate(centrality, + candidate.template collision_as().numContrib(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tofNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tofNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPrFromLambda(), + candidate.kfDcaXYPiFromOmegac(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.kfDcaXYCascToPv(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.chi2GeoV0(), + candidate.chi2GeoCasc(), + candidate.chi2GeoOmegac(), + candidate.chi2MassV0(), + candidate.chi2MassCasc(), + candidate.v0ldl(), + candidate.cascldl(), + candidate.omegacldl(), + candidate.chi2TopoV0ToPv(), + candidate.chi2TopoCascToPv(), + candidate.chi2TopoPiFromOmegacToPv(), + candidate.chi2TopoOmegacToPv(), + candidate.chi2TopoV0ToCasc(), + candidate.chi2TopoCascToOmegac(), + candidate.decayLenXYLambda(), + candidate.decayLenXYCasc(), + candidate.decayLenXYOmegac(), + candidate.cosPaV0ToCasc(), + candidate.cosPAV0(), + candidate.cosPaCascToOmegac(), + candidate.cosPACasc(), + candidate.cosPACharmBaryon(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.kfRapOmegac(), + candidate.kfptPiFromOmegac(), + candidate.kfptOmegac(), + candidate.cosThetaStarPiFromOmegac(), + candidate.cTauOmegac(), + candidate.etaCharmBaryon(), + candidate.v0Ndf(), + candidate.cascNdf(), + candidate.omegacNdf(), + candidate.massV0Ndf(), + candidate.massCascNdf(), + candidate.v0Chi2OverNdf(), + candidate.cascChi2OverNdf(), + candidate.omegacChi2OverNdf(), + candidate.massV0Chi2OverNdf(), + candidate.massCascChi2OverNdf(), + candidate.cascRejectInvmass(), + candidate.resultSelections(), + flagMc, + originMc, + collisionMatched); + } + } + + //////////////////////////////////// + // // + // Process functions // + // // + //////////////////////////////////// + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processData(CollsSel const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processDataCent(CollsSelWithFT0M const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processData, "Process DCAFitter data, no cent", true); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processDataCent, "Process DCAFitter data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfData(CollsSel const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processKfDataCent(CollsSelWithFT0M const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfData, "Process KF data, no cent", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfDataCent, "Process KF data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processMc(CollsSel const& collisions, + MyTrackTable const&, + CandMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processMcCent(CollsWithMcLabels const& collisions, + MyTrackTable const&, + CandMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMc, "Process DCAFitter MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMcCent, "Process DCAFitter MC for Omegac0 with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfMc(CollsSel const& collisions, + CandKfMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processKfMcCent(CollsWithMcLabels const& collisions, + CandKfMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMc, "Process KF MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMcCent, "Process KF MC for Omegac0 with FT0M", false); +}; // end of struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 7512c031ef0792fe0df5f39e83115d1fd96a2d9f Mon Sep 17 00:00:00 2001 From: Rahul Verma <110929992+rahulverma012@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:45:40 +0530 Subject: [PATCH 231/648] [PWGCF] Changes the names of configurables and table descirption (#18218) --- .../Tasks/hParticleCorrelation.cxx | 1134 ++++++++--------- 1 file changed, 567 insertions(+), 567 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx index 9f23c51d17f..1325cf71f8a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -128,7 +128,7 @@ DECLARE_SOA_COLUMN(MixingBin, mixingBin, int32_t); DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); } // namespace hpcorrcollision -DECLARE_SOA_TABLE(HPCorrCollisions, "AOD", "HPCORRCOLLS", o2::soa::Index<>, +DECLARE_SOA_TABLE(HPCorrColls, "AOD", "HPCORRCOLL", o2::soa::Index<>, hpcorrcollision::GlobalCollisionId, hpcorrcollision::OriginalCollisionId, o2::aod::origins::DataframeID, @@ -144,7 +144,7 @@ DECLARE_SOA_TABLE(HPCorrCollisions, "AOD", "HPCORRCOLLS", o2::soa::Index<>, namespace hpcorrtrack { -DECLARE_SOA_INDEX_COLUMN(HPCorrCollision, hpCorrCollision); // o2-linter: disable=name/o2-column (Keep HPCorrCollision naming consistent with the existing derived collision table) +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep HPCorrColl naming consistent with the existing derived collision table) DECLARE_SOA_COLUMN(GlobalColRefId, globalColRefId, int64_t); DECLARE_SOA_COLUMN(OriginalColRefId, originalColRefId, int64_t); @@ -188,8 +188,8 @@ DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); } // namespace hpcorrtrack -DECLARE_SOA_TABLE(HPCorrTracks, "AOD", "HPCORRTRKS", o2::soa::Index<>, - hpcorrtrack::HPCorrCollisionId, +DECLARE_SOA_TABLE(HPCorrTracks, "AOD", "HPCORRTRACK", o2::soa::Index<>, + hpcorrtrack::HPCorrCollId, hpcorrtrack::GlobalColRefId, hpcorrtrack::OriginalColRefId, @@ -231,7 +231,7 @@ DECLARE_SOA_TABLE(HPCorrTracks, "AOD", "HPCORRTRKS", o2::soa::Index<>, //______________________________________________________________________________ namespace hpcorrresonance { -DECLARE_SOA_INDEX_COLUMN(HPCorrCollision, hpCorrCollision); // o2-linter: disable=name/o2-column (Keep HPCorrCollision naming consistent with the existing derived collision table) +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep HPCorrColl naming consistent with the existing derived collision table) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, HPCorrTracks, "_Pos"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, HPCorrTracks, "_Neg"); @@ -272,10 +272,10 @@ DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); } // namespace hpcorrresonance -DECLARE_SOA_TABLE(HPCorrResonances, "AOD", "HPCORRRESOS", o2::soa::Index<>, +DECLARE_SOA_TABLE(HPCorrResonances, "AOD", "HPCORRRESO", o2::soa::Index<>, // Derived-table relations - hpcorrresonance::HPCorrCollisionId, + hpcorrresonance::HPCorrCollId, hpcorrresonance::PosTrackId, hpcorrresonance::NegTrackId, @@ -1191,66 +1191,66 @@ void addIdentifiedQAHistos(H& histReg, const std::string& basePath, const AxisSp // Common Histograms booking functions are over //__________________________________________________________________________________________________________________________ -struct HParticleCorrelationResonanceProducer { +struct HParticleCorrelation1ResonanceProducer { Produces resonanceCndts; HistogramRegistry resonanceQAPlots{"resonanceQAPlots", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; static constexpr float ResonancePidNSigmaMax = 3.5f; struct : ConfigurableGroup { - Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + Configurable c0001printDebugMessages{"c0001printDebugMessages", false, "Print debug messages"}; } cfgDebug; struct : ConfigurableGroup { - Configurable requireSel8{"requireSel8", true, "Require sel8 event selection"}; - Configurable cutZvertex{"cutZvertex", 10.0f, "Maximum |z_{vtx}| (cm)"}; + Configurable c0002requireSel8{"c0002requireSel8", true, "Require sel8 event selection"}; + Configurable c0003cutZvertex{"c0003cutZvertex", 10.0f, "Maximum |z_{vtx}| (cm)"}; } cfgEvent; struct : ConfigurableGroup { - Configurable ptMin{"ptMin", 0.15f, "Minimum track pT (GeV/c)"}; - Configurable ptMax{"ptMax", 100.0f, "Maximum track pT (GeV/c)"}; - Configurable etaMax{"etaMax", 0.8f, "Maximum |eta|"}; + Configurable c0004ptMin{"c0004ptMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable c0005ptMax{"c0005ptMax", 100.0f, "Maximum track pT (GeV/c)"}; + Configurable c0006etaMax{"c0006etaMax", 0.8f, "Maximum |eta|"}; - Configurable useFixedDCAxy{"useFixedDCAxy", false, "Apply fixed DCAxy cut"}; - Configurable dcaXYMax{"dcaXYMax", 0.1f, "Maximum |DCAxy| (cm)"}; + Configurable c0007useFixedDCAxy{"c0007useFixedDCAxy", false, "Apply fixed DCAxy cut"}; + Configurable c0008dcaXYMax{"c0008dcaXYMax", 0.1f, "Maximum |DCAxy| (cm)"}; - Configurable useFixedDCAz{"useFixedDCAz", false, "Apply fixed DCAz cut"}; - Configurable dcaZMax{"dcaZMax", 0.2f, "Maximum |DCAz| (cm)"}; + Configurable c0009useFixedDCAz{"c0009useFixedDCAz", false, "Apply fixed DCAz cut"}; + Configurable c0010dcaZMax{"c0010dcaZMax", 0.2f, "Maximum |DCAz| (cm)"}; } cfgTrackCuts; struct : ConfigurableGroup { - Configurable doPhi1020{"doPhi1020", true, "Build Phi(1020) candidates"}; - Configurable doKStar892{"doKStar892", true, "Build K*(892)0 candidates"}; - Configurable doKStar892Bar{"doKStar892Bar", true, "Build anti-K*(892)0 candidates"}; - Configurable doLambda1520{"doLambda1520", true, "Build Lambda(1520) candidates"}; - Configurable doLambda1520Bar{"doLambda1520Bar", true, "Build anti-Lambda(1520) candidates"}; + Configurable c0011doPhi1020{"c0011doPhi1020", true, "Build Phi(1020) candidates"}; + Configurable c0012doKStar892{"c0012doKStar892", true, "Build K*(892)0 candidates"}; + Configurable c0013doKStar892Bar{"c0013doKStar892Bar", true, "Build anti-K*(892)0 candidates"}; + Configurable c0014doLambda1520{"c0014doLambda1520", true, "Build Lambda(1520) candidates"}; + Configurable c0015doLambda1520Bar{"c0015doLambda1520Bar", true, "Build anti-Lambda(1520) candidates"}; } cfgResonances; struct : ConfigurableGroup { - Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; - Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; - Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; - Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; - Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; - Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + Configurable c0016phi1020PeakLow{"c0016phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c0017phi1020PeakUp{"c0017phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c0018phi1020LSBLow{"c0018phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c0019phi1020LSBUp{"c0019phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c0020phi1020RSBLow{"c0020phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c0021phi1020RSBUp{"c0021phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; } cfgPhi1020Mass; struct : ConfigurableGroup { - Configurable kstar892PeakLow{"kstar892PeakLow", 0.846f, "K*(892)0 peak lower mass"}; - Configurable kstar892PeakUp{"kstar892PeakUp", 0.946f, "K*(892)0 peak upper mass"}; - Configurable kstar892LSBLow{"kstar892LSBLow", 0.700f, "K*(892)0 LSB lower mass"}; - Configurable kstar892LSBUp{"kstar892LSBUp", 0.780f, "K*(892)0 LSB upper mass"}; - Configurable kstar892RSBLow{"kstar892RSBLow", 1.010f, "K*(892)0 RSB lower mass"}; - Configurable kstar892RSBUp{"kstar892RSBUp", 1.090f, "K*(892)0 RSB upper mass"}; + Configurable c0022kstar892PeakLow{"c0022kstar892PeakLow", 0.846f, "K*(892)0 peak lower mass"}; + Configurable c0023kstar892PeakUp{"c0023kstar892PeakUp", 0.946f, "K*(892)0 peak upper mass"}; + Configurable c0024kstar892LSBLow{"c0024kstar892LSBLow", 0.700f, "K*(892)0 LSB lower mass"}; + Configurable c0025kstar892LSBUp{"c0025kstar892LSBUp", 0.780f, "K*(892)0 LSB upper mass"}; + Configurable c0026kstar892RSBLow{"c0026kstar892RSBLow", 1.010f, "K*(892)0 RSB lower mass"}; + Configurable c0027kstar892RSBUp{"c0027kstar892RSBUp", 1.090f, "K*(892)0 RSB upper mass"}; } cfgKstar892Mass; struct : ConfigurableGroup { - Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) peak lower mass"}; - Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) peak upper mass"}; - Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) LSB lower mass"}; - Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) LSB upper mass"}; - Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) RSB lower mass"}; - Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) RSB upper mass"}; + Configurable c0028lambda1520PeakLow{"c0028lambda1520PeakLow", 1.500f, "Lambda(1520) peak lower mass"}; + Configurable c0029lambda1520PeakUp{"c0029lambda1520PeakUp", 1.540f, "Lambda(1520) peak upper mass"}; + Configurable c0030lambda1520LSBLow{"c0030lambda1520LSBLow", 1.440f, "Lambda(1520) LSB lower mass"}; + Configurable c0031lambda1520LSBUp{"c0031lambda1520LSBUp", 1.480f, "Lambda(1520) LSB upper mass"}; + Configurable c0032lambda1520RSBLow{"c0032lambda1520RSBLow", 1.560f, "Lambda(1520) RSB lower mass"}; + Configurable c0033lambda1520RSBUp{"c0033lambda1520RSBUp", 1.600f, "Lambda(1520) RSB upper mass"}; } cfgLambda1520Mass; void init(InitContext const&) @@ -1309,14 +1309,14 @@ struct HParticleCorrelationResonanceProducer { } // Event Filter - Filter eventFilter = (!cfgEvent.requireSel8) || (o2::aod::evsel::sel8 == true); - Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.cutZvertex); + Filter eventFilter = (!cfgEvent.c0002requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c0003cutZvertex); // Track Filter - Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.ptMin) && (o2::aod::track::pt < cfgTrackCuts.ptMax); - Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.etaMax); - Filter dcaFilter = ((!cfgTrackCuts.useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.dcaXYMax)) && - ((!cfgTrackCuts.useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.dcaZMax)); + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c0004ptMin) && (o2::aod::track::pt < cfgTrackCuts.c0005ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c0006etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c0007useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c0008dcaXYMax)) && + ((!cfgTrackCuts.c0009useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c0010dcaZMax)); using MyFilteredCollisions = soa::Filtered>; using MyFilteredTracks = soa::Filtered>; @@ -1326,21 +1326,21 @@ struct HParticleCorrelationResonanceProducer { Partition negTracks = aod::track::signed1Pt < 0.0f; int dfNumber = 0; - // void processNothing(aod::HPCorrCollisions const&) + // void processNothing(aod::HPCorrColls const&) void processNothing(aod::Origins const& origins) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c0001printDebugMessages) { LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); } // Intentionally empty. // Keeps the task alive when running purely on derived data. } - PROCESS_SWITCH(HParticleCorrelationResonanceProducer, processNothing, "Dummy process for derived-data analysis", true); + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processNothing, "Dummy process for derived-data analysis", true); void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, o2::aod::Origins const& /*Origins*/) { dfNumber++; - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c0001printDebugMessages) { LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size(); } @@ -1422,9 +1422,9 @@ struct HParticleCorrelationResonanceProducer { lambda1520BarTag = kMassOutside; // phi(1020) -> K+ + K- - if (cfgResonances.doPhi1020 && posIsKa && negIsKa) { + if (cfgResonances.c0011doPhi1020 && posIsKa && negIsKa) { mPhi1020 = RecoDecay::m(p, ePosKa + eNegKa); - phi1020Tag = getMassRegionTag(mPhi1020, cfgPhi1020Mass.phi1020LSBLow, cfgPhi1020Mass.phi1020LSBUp, cfgPhi1020Mass.phi1020PeakLow, cfgPhi1020Mass.phi1020PeakUp, cfgPhi1020Mass.phi1020RSBLow, cfgPhi1020Mass.phi1020RSBUp); + phi1020Tag = getMassRegionTag(mPhi1020, cfgPhi1020Mass.c0018phi1020LSBLow, cfgPhi1020Mass.c0019phi1020LSBUp, cfgPhi1020Mass.c0016phi1020PeakLow, cfgPhi1020Mass.c0017phi1020PeakUp, cfgPhi1020Mass.c0020phi1020RSBLow, cfgPhi1020Mass.c0021phi1020RSBUp); if (phi1020Tag != kMassOutside) { fillTable = true; } @@ -1432,9 +1432,9 @@ struct HParticleCorrelationResonanceProducer { } // K(892)* -> K+ + pi- - if (cfgResonances.doKStar892 && posIsKa && negIsPi) { + if (cfgResonances.c0012doKStar892 && posIsKa && negIsPi) { mKStar892 = RecoDecay::m(p, ePosKa + eNegPi); - kStar892Tag = getMassRegionTag(mKStar892, cfgKstar892Mass.kstar892LSBLow, cfgKstar892Mass.kstar892LSBUp, cfgKstar892Mass.kstar892PeakLow, cfgKstar892Mass.kstar892PeakUp, cfgKstar892Mass.kstar892RSBLow, cfgKstar892Mass.kstar892RSBUp); + kStar892Tag = getMassRegionTag(mKStar892, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); if (kStar892Tag != kMassOutside) { fillTable = true; } @@ -1442,9 +1442,9 @@ struct HParticleCorrelationResonanceProducer { } // K(892)*Bar -> K- + pi+ - if (cfgResonances.doKStar892Bar && posIsPi && negIsKa) { + if (cfgResonances.c0013doKStar892Bar && posIsPi && negIsKa) { mKStar892Bar = RecoDecay::m(p, ePosPi + eNegKa); - kStar892BarTag = getMassRegionTag(mKStar892Bar, cfgKstar892Mass.kstar892LSBLow, cfgKstar892Mass.kstar892LSBUp, cfgKstar892Mass.kstar892PeakLow, cfgKstar892Mass.kstar892PeakUp, cfgKstar892Mass.kstar892RSBLow, cfgKstar892Mass.kstar892RSBUp); + kStar892BarTag = getMassRegionTag(mKStar892Bar, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); if (kStar892BarTag != kMassOutside) { fillTable = true; } @@ -1452,9 +1452,9 @@ struct HParticleCorrelationResonanceProducer { } // Λ(1520) -> P+ + Ka- - if (cfgResonances.doLambda1520 && posIsPr && negIsKa) { + if (cfgResonances.c0014doLambda1520 && posIsPr && negIsKa) { mLambda1520 = RecoDecay::m(p, ePosPr + eNegKa); - lambda1520Tag = getMassRegionTag(mLambda1520, cfgLambda1520Mass.lambda1520LSBLow, cfgLambda1520Mass.lambda1520LSBUp, cfgLambda1520Mass.lambda1520PeakLow, cfgLambda1520Mass.lambda1520PeakUp, cfgLambda1520Mass.lambda1520RSBLow, cfgLambda1520Mass.lambda1520RSBUp); + lambda1520Tag = getMassRegionTag(mLambda1520, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); if (lambda1520Tag != kMassOutside) { fillTable = true; } @@ -1462,9 +1462,9 @@ struct HParticleCorrelationResonanceProducer { } // Λ(1520)Bar -> PBar + Ka+ - if (cfgResonances.doLambda1520Bar && posIsKa && negIsPr) { + if (cfgResonances.c0015doLambda1520Bar && posIsKa && negIsPr) { mLambda1520Bar = RecoDecay::m(p, ePosKa + eNegPr); - lambda1520BarTag = getMassRegionTag(mLambda1520Bar, cfgLambda1520Mass.lambda1520LSBLow, cfgLambda1520Mass.lambda1520LSBUp, cfgLambda1520Mass.lambda1520PeakLow, cfgLambda1520Mass.lambda1520PeakUp, cfgLambda1520Mass.lambda1520RSBLow, cfgLambda1520Mass.lambda1520RSBUp); + lambda1520BarTag = getMassRegionTag(mLambda1520Bar, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); if (lambda1520BarTag != kMassOutside) { fillTable = true; } @@ -1486,12 +1486,12 @@ struct HParticleCorrelationResonanceProducer { } // Pos Tracks } // collision Loop is over } - PROCESS_SWITCH(HParticleCorrelationResonanceProducer, processSameEvent, "Process Same event", true); + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processSameEvent, "Process Same event", true); }; -struct HParticleCorrelationSameEvent { +struct HParticleCorrelation2SameEvent { - Produces derivedCollisions; + Produces derivedCollisions; Produces derivedTracks; Produces derivedResonances; @@ -1509,220 +1509,220 @@ struct HParticleCorrelationSameEvent { HistogramRegistry mixingQA{"mixingQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { - Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + Configurable c1001printDebugMessages{"c1001printDebugMessages", false, "Print debug messages"}; } cfgDebug; struct : ConfigurableGroup { // Basic event selection - Configurable cutZvertex{"cutZvertex", 10.f, "Maximum |z_{vtx}| (cm)"}; // Imp : All - Configurable requireSel8{"requireSel8", true, "Require sel8 event selection"}; // Imp : All - Configurable requireTriggerTVX{"requireTriggerTVX", false, "Require kIsTriggerTVX"}; + Configurable c1002cutZvertex{"c1002cutZvertex", 10.f, "Maximum |z_{vtx}| (cm)"}; // Imp : All + Configurable c1003requireSel8{"c1003requireSel8", true, "Require sel8 event selection"}; // Imp : All + Configurable c1004requireTriggerTVX{"c1004requireTriggerTVX", false, "Require kIsTriggerTVX"}; - Configurable minNFilteredTracks{"minNFilteredTracks", 3, "Minimum number of filtered tracks required per collision"}; - Configurable minNSelectedTracks{"minNSelectedTracks", 3, "Minimum number of selected tracks required per collision"}; + Configurable c1005minNFilteredTracks{"c1005minNFilteredTracks", 3, "Minimum number of filtered tracks required per collision"}; + Configurable c1006minNSelectedTracks{"c1006minNSelectedTracks", 3, "Minimum number of selected tracks required per collision"}; // Time-frame / ROF border selections - Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; - Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; + Configurable c1007requireNoITSROFrameBorder{"c1007requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; + Configurable c1008requireNoTimeFrameBorder{"c1008requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; // Vertex quality - Configurable requireVertexITSTPC{"requireVertexITSTPC", true, "Require kIsVertexITSTPC"}; // Imp : Light Ions + pp - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; // Imp : Light Ions - Configurable requireVertexTOFmatched{"requireVertexTOFmatched", false, "Require kIsVertexTOFmatched"}; - Configurable requireVertexTRDmatched{"requireVertexTRDmatched", false, "Require kIsVertexTRDmatched"}; - Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "Require kIsGoodITSLayersAll"}; // Imp : Light Ions + Configurable c1009requireVertexITSTPC{"c1009requireVertexITSTPC", true, "Require kIsVertexITSTPC"}; // Imp : Light Ions + pp + Configurable c1010requireGoodZvtxFT0vsPV{"c1010requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; // Imp : Light Ions + Configurable c1011requireVertexTOFmatched{"c1011requireVertexTOFmatched", false, "Require kIsVertexTOFmatched"}; + Configurable c1012requireVertexTRDmatched{"c1012requireVertexTRDmatched", false, "Require kIsVertexTRDmatched"}; + Configurable c1013requireGoodITSLayersAll{"c1013requireGoodITSLayersAll", false, "Require kIsGoodITSLayersAll"}; // Imp : Light Ions // Pileup / neighbouring-collision rejection - Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; // Imp : Light Ions + pp - Configurable requireNoCollInTimeRangeStandard{"requireNoCollInTimeRangeStandard", false, "Require kNoCollInTimeRangeStandard"}; - Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "Require kNoCollInTimeRangeStrict"}; - Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "Require kNoCollInTimeRangeNarrow"}; - Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", false, "Require kNoCollInRofStandard"}; - Configurable requireNoCollInRofStrict{"requireNoCollInRofStrict", false, "Require kNoCollInRofStrict"}; - Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "Require kNoHighMultCollInPrevRof"}; + Configurable c1014requireNoSameBunchPileup{"c1014requireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; // Imp : Light Ions + pp + Configurable c1015requireNoCollInTimeRangeStandard{"c1015requireNoCollInTimeRangeStandard", false, "Require kNoCollInTimeRangeStandard"}; + Configurable c1016requireNoCollInTimeRangeStrict{"c1016requireNoCollInTimeRangeStrict", false, "Require kNoCollInTimeRangeStrict"}; + Configurable c1017requireNoCollInTimeRangeNarrow{"c1017requireNoCollInTimeRangeNarrow", false, "Require kNoCollInTimeRangeNarrow"}; + Configurable c1018requireNoCollInRofStandard{"c1018requireNoCollInRofStandard", false, "Require kNoCollInRofStandard"}; + Configurable c1019requireNoCollInRofStrict{"c1019requireNoCollInRofStrict", false, "Require kNoCollInRofStrict"}; + Configurable c1020requireNoHighMultCollInPrevRof{"c1020requireNoHighMultCollInPrevRof", false, "Require kNoHighMultCollInPrevRof"}; // INEL event classes - Configurable requireINELgt0{"requireINELgt0", false, "Require INEL > 0"}; - Configurable requireINELgt1{"requireINELgt1", false, "Require INEL > 1"}; + Configurable c1021requireINELgt0{"c1021requireINELgt0", false, "Require INEL > 0"}; + Configurable c1022requireINELgt1{"c1022requireINELgt1", false, "Require INEL > 1"}; // Multiplicity selection - Configurable useMultiplicitySelection{"useMultiplicitySelection", false, "Apply multiplicity selection"}; - Configurable multiplicityEstimator{"multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; - Configurable minMultiplicity{"minMultiplicity", 0.f, "Minimum multiplicity"}; - Configurable maxMultiplicity{"maxMultiplicity", 1.e9f, "Maximum multiplicity"}; + Configurable c1023useMultiplicitySelection{"c1023useMultiplicitySelection", false, "Apply multiplicity selection"}; + Configurable c1024multiplicityEstimator{"c1024multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; + Configurable c1025minMultiplicity{"c1025minMultiplicity", 0.f, "Minimum multiplicity"}; + Configurable c1026maxMultiplicity{"c1026maxMultiplicity", 1.e9f, "Maximum multiplicity"}; // Centrality selection - Configurable useCentralitySelection{"useCentralitySelection", false, "Apply centrality selection"}; - Configurable centralityEstimator{"centralityEstimator", 0, "0: centFT0C, 1: centFT0M, 2: centFT0A, 3: centFV0A"}; - Configurable minCentrality{"minCentrality", 0.f, "Minimum centrality percentile"}; - Configurable maxCentrality{"maxCentrality", 100.f, "Maximum centrality percentile"}; + Configurable c1027useCentralitySelection{"c1027useCentralitySelection", false, "Apply centrality selection"}; + Configurable c1028centralityEstimator{"c1028centralityEstimator", 0, "0: centFT0C, 1: centFT0M, 2: centFT0A, 3: centFV0A"}; + Configurable c1029minCentrality{"c1029minCentrality", 0.f, "Minimum centrality percentile"}; + Configurable c1030maxCentrality{"c1030maxCentrality", 100.f, "Maximum centrality percentile"}; // Occupancy selection - Configurable useOccupancySelection{"useOccupancySelection", false, "Apply event occupancy selection"}; - Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use FT0C occupancy instead of track occupancy"}; - Configurable minOccupancy{"minOccupancy", -1.f, "Minimum occupancy"}; - Configurable maxOccupancy{"maxOccupancy", 1.e9f, "Maximum occupancy"}; + Configurable c1031useOccupancySelection{"c1031useOccupancySelection", false, "Apply event occupancy selection"}; + Configurable c1032useFT0CbasedOccupancy{"c1032useFT0CbasedOccupancy", false, "Use FT0C occupancy instead of track occupancy"}; + Configurable c1033minOccupancy{"c1033minOccupancy", -1.f, "Minimum occupancy"}; + Configurable c1034maxOccupancy{"c1034maxOccupancy", 1.e9f, "Maximum occupancy"}; // Interaction-rate selection - Configurable useInteractionRateSelection{"useInteractionRateSelection", false, "Apply interaction-rate selection"}; - Configurable minInteractionRate{"minInteractionRate", -1.f, "Minimum interaction rate"}; - Configurable maxInteractionRate{"maxInteractionRate", 1.e9f, "Maximum interaction rate"}; + Configurable c1035useInteractionRateSelection{"c1035useInteractionRateSelection", false, "Apply interaction-rate selection"}; + Configurable c1036minInteractionRate{"c1036minInteractionRate", -1.f, "Minimum interaction rate"}; + Configurable c1037maxInteractionRate{"c1037maxInteractionRate", 1.e9f, "Maximum interaction rate"}; // RCT / detector-quality selection - Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Apply Run Condition Table event-quality selection"}; - Configurable requireCorrelationAnalysisRCTFlagChecker{"requireCorrelationAnalysisRCTFlagChecker", false, "Apply correlation-analysis RCT selection"}; - Configurable rctFlagCheckerLabel{"rctFlagCheckerLabel", "CBT_muon_global", "RCT flag checker label"}; - Configurable rctCheckZDC{"rctCheckZDC", false, "Include ZDC in RCT detector-quality check"}; - Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "Treat limited detector acceptance as bad"}; + Configurable c1038requireRCTFlagChecker{"c1038requireRCTFlagChecker", false, "Apply Run Condition Table event-quality selection"}; + Configurable c1039requireCorrelationAnalysisRCTFlagChecker{"c1039requireCorrelationAnalysisRCTFlagChecker", false, "Apply correlation-analysis RCT selection"}; + Configurable c1040rctFlagCheckerLabel{"c1040rctFlagCheckerLabel", "CBT_muon_global", "RCT flag checker label"}; + Configurable c1041rctCheckZDC{"c1041rctCheckZDC", false, "Include ZDC in RCT detector-quality check"}; + Configurable c1042rctTreatLimitedAcceptanceAsBad{"c1042rctTreatLimitedAcceptanceAsBad", false, "Treat limited detector acceptance as bad"}; } cfgEvent; struct : ConfigurableGroup { // Kinematics - Configurable ptMin{"ptMin", 0.2f, "Minimum track pT (GeV/c)"}; - Configurable ptMax{"ptMax", 1.e10f, "Maximum track pT (GeV/c)"}; - Configurable etaMax{"etaMax", 0.8f, "Maximum |eta|"}; + Configurable c1043ptMin{"c1043ptMin", 0.2f, "Minimum track pT (GeV/c)"}; + Configurable c1044ptMax{"c1044ptMax", 1.e10f, "Maximum track pT (GeV/c)"}; + Configurable c1045etaMax{"c1045etaMax", 0.8f, "Maximum |eta|"}; // Standard O2 track-selection flags - Configurable requireGlobalTrack{"requireGlobalTrack", false, "Require isGlobalTrack()"}; - Configurable requireGlobalTrackWoDCA{"requireGlobalTrackWoDCA", false, "Require isGlobalTrackWoDCA()"}; - Configurable requirePVContributor{"requirePVContributor", false, "Require track to be a PV contributor"}; + Configurable c1046requireGlobalTrack{"c1046requireGlobalTrack", false, "Require isGlobalTrack()"}; + Configurable c1047requireGlobalTrackWoDCA{"c1047requireGlobalTrackWoDCA", false, "Require isGlobalTrackWoDCA()"}; + Configurable c1048requirePVContributor{"c1048requirePVContributor", false, "Require track to be a PV contributor"}; // Detector matching / presence - Configurable requireITS{"requireITS", false, "Require ITS information"}; - Configurable requireTPC{"requireTPC", false, "Require TPC information"}; - Configurable requireTOF{"requireTOF", false, "Require TOF information"}; - Configurable requireTRD{"requireTRD", false, "Require TRD information"}; + Configurable c1049requireITS{"c1049requireITS", false, "Require ITS information"}; + Configurable c1050requireTPC{"c1050requireTPC", false, "Require TPC information"}; + Configurable c1051requireTOF{"c1051requireTOF", false, "Require TOF information"}; + Configurable c1052requireTRD{"c1052requireTRD", false, "Require TRD information"}; // TPC quality - Configurable tpcNClsFoundMin{"tpcNClsFoundMin", 0, "Minimum number of found TPC clusters"}; - Configurable tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 80, "Minimum number of TPC crossed rows"}; - Configurable tpcCrossedRowsOverFindableMin{"tpcCrossedRowsOverFindableMin", 0.f, "Minimum TPC crossed rows / findable clusters"}; - Configurable tpcFoundOverFindableMin{"tpcFoundOverFindableMin", 0.f, "Minimum TPC found / findable clusters"}; - Configurable tpcFractionSharedMax{"tpcFractionSharedMax", 1.f, "Maximum fraction of shared TPC clusters"}; - Configurable tpcChi2NClMin{"tpcChi2NClMin", 0.f, "Minimum TPC chi2 per cluster"}; - Configurable tpcChi2NClMax{"tpcChi2NClMax", 1.e10f, "Maximum TPC chi2 per cluster"}; + Configurable c1053tpcNClsFoundMin{"c1053tpcNClsFoundMin", 0, "Minimum number of found TPC clusters"}; + Configurable c1054tpcNClsCrossedRowsMin{"c1054tpcNClsCrossedRowsMin", 80, "Minimum number of TPC crossed rows"}; + Configurable c1055tpcCrossedRowsOverFindableMin{"c1055tpcCrossedRowsOverFindableMin", 0.f, "Minimum TPC crossed rows / findable clusters"}; + Configurable c1056tpcFoundOverFindableMin{"c1056tpcFoundOverFindableMin", 0.f, "Minimum TPC found / findable clusters"}; + Configurable c1057tpcFractionSharedMax{"c1057tpcFractionSharedMax", 1.f, "Maximum fraction of shared TPC clusters"}; + Configurable c1058tpcChi2NClMin{"c1058tpcChi2NClMin", 0.f, "Minimum TPC chi2 per cluster"}; + Configurable c1059tpcChi2NClMax{"c1059tpcChi2NClMax", 1.e10f, "Maximum TPC chi2 per cluster"}; // ITS quality - Configurable itsNClsMin{"itsNClsMin", 0, "Minimum number of ITS clusters"}; - Configurable itsNClsMax{"itsNClsMax", 7, "Maximum number of ITS clusters"}; - Configurable itsNClsInnerBarrelMin{"itsNClsInnerBarrelMin", 0, "Minimum number of ITS inner-barrel clusters"}; - Configurable itsChi2NClMin{"itsChi2NClMin", 0.f, "Minimum ITS chi2 per cluster"}; - Configurable itsChi2NClMax{"itsChi2NClMax", 1.e10f, "Maximum ITS chi2 per cluster"}; + Configurable c1060itsNClsMin{"c1060itsNClsMin", 0, "Minimum number of ITS clusters"}; + Configurable c1061itsNClsMax{"c1061itsNClsMax", 7, "Maximum number of ITS clusters"}; + Configurable c1062itsNClsInnerBarrelMin{"c1062itsNClsInnerBarrelMin", 0, "Minimum number of ITS inner-barrel clusters"}; + Configurable c1063itsChi2NClMin{"c1063itsChi2NClMin", 0.f, "Minimum ITS chi2 per cluster"}; + Configurable c1064itsChi2NClMax{"c1064itsChi2NClMax", 1.e10f, "Maximum ITS chi2 per cluster"}; // Fixed DCA - Configurable useFixedDCAxy{"useFixedDCAxy", true, "Apply fixed |DCAxy| cut"}; - Configurable dcaXYMax{"dcaXYMax", 0.1f, "Maximum fixed |DCAxy| (cm)"}; - Configurable useFixedDCAz{"useFixedDCAz", true, "Apply fixed |DCAz| cut"}; - Configurable dcaZMax{"dcaZMax", 0.2f, "Maximum fixed |DCAz| (cm)"}; + Configurable c1065useFixedDCAxy{"c1065useFixedDCAxy", true, "Apply fixed |DCAxy| cut"}; + Configurable c1066dcaXYMax{"c1066dcaXYMax", 0.1f, "Maximum fixed |DCAxy| (cm)"}; + Configurable c1067useFixedDCAz{"c1067useFixedDCAz", true, "Apply fixed |DCAz| cut"}; + Configurable c1068dcaZMax{"c1068dcaZMax", 0.2f, "Maximum fixed |DCAz| (cm)"}; // pT-dependent DCAxy: // |DCAxy| < A + B * pT^C - Configurable usePtDependentDCAxy{"usePtDependentDCAxy", false, "Apply pT-dependent DCAxy cut"}; - Configurable dcaXYPtA{"dcaXYPtA", 0.0105f, "A coefficient of pT-dependent DCAxy cut"}; - Configurable dcaXYPtB{"dcaXYPtB", 0.035f, "B coefficient of pT-dependent DCAxy cut"}; - Configurable dcaXYPtC{"dcaXYPtC", 1.1f, "Power C of pT-dependent DCAxy cut"}; + Configurable c1069usePtDependentDCAxy{"c1069usePtDependentDCAxy", false, "Apply pT-dependent DCAxy cut"}; + Configurable c1070dcaXYPtA{"c1070dcaXYPtA", 0.0105f, "A coefficient of pT-dependent DCAxy cut"}; + Configurable c1071dcaXYPtB{"c1071dcaXYPtB", 0.035f, "B coefficient of pT-dependent DCAxy cut"}; + Configurable c1072dcaXYPtC{"c1072dcaXYPtC", 1.1f, "Power C of pT-dependent DCAxy cut"}; // pT-dependent DCAz: // |DCAz| < A + B * pT^C - Configurable usePtDependentDCAz{"usePtDependentDCAz", false, "Apply pT-dependent DCAz cut"}; - Configurable dcaZPtA{"dcaZPtA", 0.1f, "A coefficient of pT-dependent DCAz cut"}; - Configurable dcaZPtB{"dcaZPtB", 0.0f, "B coefficient of pT-dependent DCAz cut"}; - Configurable dcaZPtC{"dcaZPtC", 0.0f, "Power C of pT-dependent DCAz cut"}; + Configurable c1073usePtDependentDCAz{"c1073usePtDependentDCAz", false, "Apply pT-dependent DCAz cut"}; + Configurable c1074dcaZPtA{"c1074dcaZPtA", 0.1f, "A coefficient of pT-dependent DCAz cut"}; + Configurable c1075dcaZPtB{"c1075dcaZPtB", 0.0f, "B coefficient of pT-dependent DCAz cut"}; + Configurable c1076dcaZPtC{"c1076dcaZPtC", 0.0f, "Power C of pT-dependent DCAz cut"}; } cfgTrackCuts; struct : ConfigurableGroup { - Configurable triggerPtLow{"triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; - Configurable triggerPtHigh{"triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; - Configurable assocPtLowMin{"assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; - Configurable assocPtLowMax{"assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; - Configurable assocPtHighMin{"assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; - Configurable assocPtHighMax{"assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + Configurable c1077triggerPtLow{"c1077triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable c1078triggerPtHigh{"c1078triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable c1079assocPtLowMin{"c1079assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable c1080assocPtLowMax{"c1080assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable c1081assocPtHighMin{"c1081assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable c1082assocPtHighMax{"c1082assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; } cfgPartitions; struct : ConfigurableGroup { - Configurable resoRapidityMin{"resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; - Configurable resoRapidityMax{"resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; - - Configurable phiPtLowMin{"phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; - Configurable phiPtLowMax{"phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; - Configurable phiPtHighMin{"phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; - Configurable phiPtHighMax{"phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; - - Configurable kstarPtLowMin{"kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; - Configurable kstarPtLowMax{"kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; - Configurable kstarPtHighMin{"kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; - Configurable kstarPtHighMax{"kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; - - Configurable kstarBarPtLowMin{"kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; - Configurable kstarBarPtLowMax{"kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; - Configurable kstarBarPtHighMin{"kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; - Configurable kstarBarPtHighMax{"kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; - - Configurable lambda1520PtLowMin{"lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; - Configurable lambda1520PtLowMax{"lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; - Configurable lambda1520PtHighMin{"lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; - Configurable lambda1520PtHighMax{"lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; - - Configurable lambda1520BarPtLowMin{"lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtLowMax{"lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtHighMin{"lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtHighMax{"lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + Configurable c1083resoRapidityMin{"c1083resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable c1084resoRapidityMax{"c1084resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable c1085phiPtLowMin{"c1085phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable c1086phiPtLowMax{"c1086phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable c1087phiPtHighMin{"c1087phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable c1088phiPtHighMax{"c1088phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable c1089kstarPtLowMin{"c1089kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable c1090kstarPtLowMax{"c1090kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable c1091kstarPtHighMin{"c1091kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable c1092kstarPtHighMax{"c1092kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable c1093kstarBarPtLowMin{"c1093kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable c1094kstarBarPtLowMax{"c1094kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable c1095kstarBarPtHighMin{"c1095kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable c1096kstarBarPtHighMax{"c1096kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable c1097lambda1520PtLowMin{"c1097lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable c1098lambda1520PtLowMax{"c1098lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable c1099lambda1520PtHighMin{"c1099lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable c1100lambda1520PtHighMax{"c1100lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable c1101lambda1520BarPtLowMin{"c1101lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable c1102lambda1520BarPtLowMax{"c1102lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable c1103lambda1520BarPtHighMin{"c1103lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable c1104lambda1520BarPtHighMax{"c1104lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; } cfgResPartitions; struct : ConfigurableGroup { - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; - ConfigurableAxis axisDeltaEta{"axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c1105axisDeltaPhi{"c1105axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1106axisDeltaEta{"c1106axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; - ConfigurableAxis axisCorrSparseTriggerPt{"axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; - ConfigurableAxis axisCorrSparseAssocPt{"axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; - ConfigurableAxis axisCorrSparseDeltaPhi{"axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; - ConfigurableAxis axisCorrSparseDeltaEta{"axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c1107axisCorrSparseTriggerPt{"c1107axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c1108axisCorrSparseAssocPt{"c1108axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c1109axisCorrSparseDeltaPhi{"c1109axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1110axisCorrSparseDeltaEta{"c1110axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; } cfgAxis; - Configurable fillDauQAOnce{"fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; - Configurable rejectResoWithAnyTriggerDaughter{"rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; - Configurable requireSelectedTriggerForInvariantMass{"requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; + Configurable c1111fillDauQAOnce{"c1111fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; + Configurable c1112rejectResoWithAnyTriggerDaughter{"c1112rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; + Configurable c1113requireSelectedTriggerForInvariantMass{"c1113requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; struct : ConfigurableGroup { - Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; - Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; - Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; - Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; - Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; - Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + Configurable c1114phi1020PeakLow{"c1114phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c1115phi1020PeakUp{"c1115phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c1116phi1020LSBLow{"c1116phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c1117phi1020LSBUp{"c1117phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c1118phi1020RSBLow{"c1118phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c1119phi1020RSBUp{"c1119phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; } cfgPhi1020CorrMass; struct : ConfigurableGroup { - Configurable kStar892PeakLow{"kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; - Configurable kStar892PeakUp{"kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; - Configurable kStar892LSBLow{"kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; - Configurable kStar892LSBUp{"kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; - Configurable kStar892RSBLow{"kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; - Configurable kStar892RSBUp{"kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + Configurable c1120kStar892PeakLow{"c1120kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable c1121kStar892PeakUp{"c1121kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable c1122kStar892LSBLow{"c1122kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable c1123kStar892LSBUp{"c1123kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable c1124kStar892RSBLow{"c1124kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable c1125kStar892RSBUp{"c1125kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; } cfgKStar892CorrMass; struct : ConfigurableGroup { - Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; - Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; - Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; - Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; - Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; - Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + Configurable c1126lambda1520PeakLow{"c1126lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable c1127lambda1520PeakUp{"c1127lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable c1128lambda1520LSBLow{"c1128lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable c1129lambda1520LSBUp{"c1129lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable c1130lambda1520RSBLow{"c1130lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable c1131lambda1520RSBUp{"c1131lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; } cfgLambda1520CorrMass; struct : ConfigurableGroup { - Configurable pairMask{"pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; - Configurable mixingBin{"mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; - Configurable requireAllPairBits{"requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; - Configurable resetGlobalCountersPerDF{"resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; + Configurable c1132pairMask{"c1132pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; + Configurable c1133mixingBin{"c1133mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; + Configurable c1134requireAllPairBits{"c1134requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; + Configurable c1135resetGlobalCountersPerDF{"c1135resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; } cfgDerivedData; struct : ConfigurableGroup { - Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; - Configurable mixingEstimator{"mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; - ConfigurableAxis axisVtxMixing{"axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; - ConfigurableAxis axisCentMixing{"axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; - ConfigurableAxis axisMixingOccupancy{"axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; + Configurable c1136nEvtMixing{"c1136nEvtMixing", 5, "Number of events to mix"}; + Configurable c1137mixingEstimator{"c1137mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis c1138axisVtxMixing{"c1138axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c1139axisCentMixing{"c1139axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis c1140axisMixingOccupancy{"c1140axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; } cfgMixing; using BinningTypeVtxZFT0C = ColumnBinningPolicy; @@ -1730,17 +1730,17 @@ struct HParticleCorrelationSameEvent { using BinningTypeVtxZFT0A = ColumnBinningPolicy; using BinningTypeVtxZFV0A = ColumnBinningPolicy; - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; int nMixBins = 0; template int getMixingBin(const C& collision) { - switch (cfgMixing.mixingEstimator) { + switch (cfgMixing.c1137mixingEstimator) { case kMixCentFT0C: return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); case kMixCentFT0M: @@ -1757,7 +1757,7 @@ struct HParticleCorrelationSameEvent { template float getMixingEstimatorValue(const C& collision) { - switch (cfgMixing.mixingEstimator) { + switch (cfgMixing.c1137mixingEstimator) { case kMixCentFT0C: return collision.centFT0C(); case kMixCentFT0M: @@ -1772,14 +1772,14 @@ struct HParticleCorrelationSameEvent { } struct : ConfigurableGroup { - Configurable> pidConfigSetting{"pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; - Configurable> pidVetoSetting{"pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; - Configurable cfgId07CheckTofBeta{"cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + Configurable> c1141pidConfigSetting{"c1141pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> c1142pidVetoSetting{"c1142pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable c1143cfgId07CheckTofBeta{"c1143cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; } cfgIdCut; void init(InitContext const&) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c1001printDebugMessages) { LOGF(info, "Starting init"); } // Axes @@ -1815,22 +1815,22 @@ struct HParticleCorrelationSameEvent { const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; - AxisSpec axisDeltaPhiSpec{cfgAxis.axisDeltaPhi, "#Delta#varphi"}; - AxisSpec axisDeltaEtaSpec{cfgAxis.axisDeltaEta, "#Delta#eta"}; + AxisSpec axisDeltaPhiSpec{cfgAxis.c1105axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c1106axisDeltaEta, "#Delta#eta"}; - AxisSpec axisCorrSparseTriggerPt{cfgAxis.axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; - AxisSpec axisCorrSparseAssocPt{cfgAxis.axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; - AxisSpec axisCorrSparseDeltaPhi{cfgAxis.axisCorrSparseDeltaPhi, "#Delta#varphi"}; - AxisSpec axisCorrSparseDeltaEta{cfgAxis.axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c1107axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c1108axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c1109axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c1110axisCorrSparseDeltaEta, "#Delta#eta"}; const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; const AxisSpec axisResoMassCent = {102, -1.0f, 101.0f, "Centrality (%)"}; - AxisSpec axisVtxMixSpec{cfgMixing.axisVtxMixing, "z_{vtx} (cm)"}; - AxisSpec axisCentMixSpec{cfgMixing.axisCentMixing, "Centrality (%)"}; - AxisSpec axisPoolOccupancy{cfgMixing.axisMixingOccupancy, "N eligible collisions"}; + AxisSpec axisVtxMixSpec{cfgMixing.c1138axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixSpec{cfgMixing.c1139axisCentMixing, "Centrality (%)"}; + AxisSpec axisPoolOccupancy{cfgMixing.c1140axisMixingOccupancy, "N eligible collisions"}; nMixBins = axisVtxMixSpec.getNbins() * axisCentMixSpec.getNbins(); const AxisSpec axisMixBin{nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; const AxisSpec axisCorrChannel{NCorrCountChannels, -0.5, static_cast(NCorrCountChannels) - 0.5, "Correlation channel"}; @@ -2123,7 +2123,7 @@ struct HParticleCorrelationSameEvent { setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/ReadyPoolMap"))->GetYaxis()); setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/NReadyMixBins"))->GetXaxis()); - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c1001printDebugMessages) { LOGF(info, "Finishing init"); } } // Init function is over. @@ -2141,20 +2141,20 @@ struct HParticleCorrelationSameEvent { { // Static is only run once, ever. static const std::array doVetoTPC = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTPC)}; + getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kDoVetoTPC)}; static const std::array vetoTPC = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTPC)}; + getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kVetoTPC)}; return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); } @@ -2164,20 +2164,20 @@ struct HParticleCorrelationSameEvent { { // Only computed once static const std::array doVetoTOF = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTOF)}; + getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kDoVetoTOF)}; static const std::array vetoTOF = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTOF)}; + getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kVetoTOF)}; return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); } @@ -2194,7 +2194,7 @@ struct HParticleCorrelationSameEvent { inline bool checkReliableTOF(const T& track) { // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: - if (cfgIdCut.cfgId07CheckTofBeta) { + if (cfgIdCut.c1143cfgId07CheckTofBeta) { return (track.hasTOF() && track.beta() > 0.0f); } return track.hasTOF(); @@ -2219,7 +2219,7 @@ struct HParticleCorrelationSameEvent { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPC(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -2228,7 +2228,7 @@ struct HParticleCorrelationSameEvent { } } - static const bool doRelativeTPCcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCcheck); + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTPCcheck); if (doRelativeTPCcheck) { if (!relativeIdOthersTPC(track)) { // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others @@ -2272,7 +2272,7 @@ struct HParticleCorrelationSameEvent { } else { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPCTOF(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -2281,7 +2281,7 @@ struct HParticleCorrelationSameEvent { } } - static const bool doRelativeTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTOFcheck); + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTOFcheck); if (doRelativeTOFcheck) { if (!relativeIdOthersTOF(track)) { // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others @@ -2290,7 +2290,7 @@ struct HParticleCorrelationSameEvent { } } - static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); if (doRelativeTPCTOFcheck) { if (!relativeIdOthersTPCTOF(track)) { // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others @@ -2315,15 +2315,15 @@ struct HParticleCorrelationSameEvent { bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) { // Static cache inside function - initialized once on first call - static const auto thrPforTOF = getCfg(cfgIdCut.pidConfigSetting, pidMode, kThrPforTOF); - static const auto idCutTypeLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeLowP); - static const auto nSigmaTPCLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCLowP); - static const auto nSigmaTOFLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFLowP); - static const auto nSigmaRadLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadLowP); - static const auto idCutTypeHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeHighP); - static const auto nSigmaTPCHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCHighP); - static const auto nSigmaTOFHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFHighP); - static const auto nSigmaRadHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadHighP); + static const auto thrPforTOF = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaRadHighP); if (track.p() < thrPforTOF) { if (checkReliableTOF(track)) { @@ -2439,83 +2439,83 @@ struct HParticleCorrelationSameEvent { CollisionRejectionTag selCollision(T const& collision) { // Basic event selection - if (cfgEvent.requireSel8 && !collision.sel8()) { + if (cfgEvent.c1003requireSel8 && !collision.sel8()) { return kCollRejectSel8; } - if (std::abs(collision.posZ()) >= cfgEvent.cutZvertex) { + if (std::abs(collision.posZ()) >= cfgEvent.c1002cutZvertex) { return kCollRejectVertexZ; } - if (cfgEvent.requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + if (cfgEvent.c1004requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { return kCollRejectTriggerTVX; } // TF / ITS-ROF borders - if (cfgEvent.requireNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + if (cfgEvent.c1007requireNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return kCollRejectITSROFrameBorder; } - if (cfgEvent.requireNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + if (cfgEvent.c1008requireNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return kCollRejectTimeFrameBorder; } // Vertex quality - if (cfgEvent.requireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + if (cfgEvent.c1009requireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return kCollRejectVertexITSTPC; } - if (cfgEvent.requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (cfgEvent.c1010requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return kCollRejectGoodZvtxFT0vsPV; } - if (cfgEvent.requireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + if (cfgEvent.c1011requireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { return kCollRejectVertexTOFmatched; } - if (cfgEvent.requireVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + if (cfgEvent.c1012requireVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { return kCollRejectVertexTRDmatched; } - if (cfgEvent.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (cfgEvent.c1013requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return kCollRejectGoodITSLayersAll; } // Pileup / neighbouring-collision rejection - if (cfgEvent.requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (cfgEvent.c1014requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return kCollRejectSameBunchPileup; } - if (cfgEvent.requireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (cfgEvent.c1015requireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return kCollRejectCollInTimeRangeStandard; } - if (cfgEvent.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + if (cfgEvent.c1016requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return kCollRejectCollInTimeRangeStrict; } - if (cfgEvent.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + if (cfgEvent.c1017requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { return kCollRejectCollInTimeRangeNarrow; } - if (cfgEvent.requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (cfgEvent.c1018requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return kCollRejectCollInRofStandard; } - if (cfgEvent.requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + if (cfgEvent.c1019requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return kCollRejectCollInRofStrict; } - if (cfgEvent.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (cfgEvent.c1020requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return kCollRejectHighMultCollInPrevRof; } // INEL classes - if (cfgEvent.requireINELgt0 && !collision.isInelGt0()) { + if (cfgEvent.c1021requireINELgt0 && !collision.isInelGt0()) { return kCollRejectINELgt0; } - if (cfgEvent.requireINELgt1 && !collision.isInelGt1()) { + if (cfgEvent.c1022requireINELgt1 && !collision.isInelGt1()) { return kCollRejectINELgt1; } @@ -2526,83 +2526,83 @@ struct HParticleCorrelationSameEvent { TrackRejectionTag selectionTrack(T const& track) { // Standard O2 flags - if (cfgTrackCuts.requireGlobalTrack && !track.isGlobalTrack()) { + if (cfgTrackCuts.c1046requireGlobalTrack && !track.isGlobalTrack()) { return kTrackRejectGlobalTrack; } - if (cfgTrackCuts.requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + if (cfgTrackCuts.c1047requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { return kTrackRejectGlobalTrackWoDCA; } - if (cfgTrackCuts.requirePVContributor && !track.isPVContributor()) { + if (cfgTrackCuts.c1048requirePVContributor && !track.isPVContributor()) { return kTrackRejectPVContributor; } // Detector presence - if (cfgTrackCuts.requireITS && !track.hasITS()) { + if (cfgTrackCuts.c1049requireITS && !track.hasITS()) { return kTrackRejectITS; } - if (cfgTrackCuts.requireTPC && !track.hasTPC()) { + if (cfgTrackCuts.c1050requireTPC && !track.hasTPC()) { return kTrackRejectTPC; } - if (cfgTrackCuts.requireTOF && !track.hasTOF()) { + if (cfgTrackCuts.c1051requireTOF && !track.hasTOF()) { return kTrackRejectTOF; } - if (cfgTrackCuts.requireTRD && !track.hasTRD()) { + if (cfgTrackCuts.c1052requireTRD && !track.hasTRD()) { return kTrackRejectTRD; } // TPC quality - if (track.tpcNClsFound() < cfgTrackCuts.tpcNClsFoundMin) { + if (track.tpcNClsFound() < cfgTrackCuts.c1053tpcNClsFoundMin) { return kTrackRejectTPCNClsFound; } - if (track.tpcNClsCrossedRows() < cfgTrackCuts.tpcNClsCrossedRowsMin) { + if (track.tpcNClsCrossedRows() < cfgTrackCuts.c1054tpcNClsCrossedRowsMin) { return kTrackRejectTPCCrossedRows; } - if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCuts.tpcCrossedRowsOverFindableMin) { + if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCuts.c1055tpcCrossedRowsOverFindableMin) { return kTrackRejectTPCCrossedRowsOverFindable; } - if (track.tpcFoundOverFindableCls() < cfgTrackCuts.tpcFoundOverFindableMin) { + if (track.tpcFoundOverFindableCls() < cfgTrackCuts.c1056tpcFoundOverFindableMin) { return kTrackRejectTPCFoundOverFindable; } - if (track.tpcFractionSharedCls() > cfgTrackCuts.tpcFractionSharedMax) { + if (track.tpcFractionSharedCls() > cfgTrackCuts.c1057tpcFractionSharedMax) { return kTrackRejectTPCFractionShared; } - if (track.tpcChi2NCl() < cfgTrackCuts.tpcChi2NClMin || track.tpcChi2NCl() > cfgTrackCuts.tpcChi2NClMax) { + if (track.tpcChi2NCl() < cfgTrackCuts.c1058tpcChi2NClMin || track.tpcChi2NCl() > cfgTrackCuts.c1059tpcChi2NClMax) { return kTrackRejectTPCChi2; } // ITS quality - if (track.itsNCls() < cfgTrackCuts.itsNClsMin || track.itsNCls() > cfgTrackCuts.itsNClsMax) { + if (track.itsNCls() < cfgTrackCuts.c1060itsNClsMin || track.itsNCls() > cfgTrackCuts.c1061itsNClsMax) { return kTrackRejectITSNCls; } - if (track.itsNClsInnerBarrel() < cfgTrackCuts.itsNClsInnerBarrelMin) { + if (track.itsNClsInnerBarrel() < cfgTrackCuts.c1062itsNClsInnerBarrelMin) { return kTrackRejectITSNClsInnerBarrel; } - if (track.itsChi2NCl() < cfgTrackCuts.itsChi2NClMin || track.itsChi2NCl() > cfgTrackCuts.itsChi2NClMax) { + if (track.itsChi2NCl() < cfgTrackCuts.c1063itsChi2NClMin || track.itsChi2NCl() > cfgTrackCuts.c1064itsChi2NClMax) { return kTrackRejectITSChi2; } // pT-dependent DCA - if (cfgTrackCuts.usePtDependentDCAxy) { - const float dcaXYMax = cfgTrackCuts.dcaXYPtA + cfgTrackCuts.dcaXYPtB / std::pow(track.pt(), cfgTrackCuts.dcaXYPtC); + if (cfgTrackCuts.c1069usePtDependentDCAxy) { + const float dcaXYMax = cfgTrackCuts.c1070dcaXYPtA + cfgTrackCuts.c1071dcaXYPtB / std::pow(track.pt(), cfgTrackCuts.c1072dcaXYPtC); if (std::abs(track.dcaXY()) > dcaXYMax) { return kTrackRejectPtDependentDCAxy; } } - if (cfgTrackCuts.usePtDependentDCAz) { - const float dcaZMax = cfgTrackCuts.dcaZPtA; + if (cfgTrackCuts.c1073usePtDependentDCAz) { + const float dcaZMax = cfgTrackCuts.c1074dcaZPtA; if (std::abs(track.dcaZ()) > dcaZMax) { return kTrackRejectPtDependentDCAz; } @@ -2931,19 +2931,19 @@ struct HParticleCorrelationSameEvent { { if constexpr (pairType == kHPhi) { invMass = associate.mPhi1020(); - return getMassRegionTag(invMass, cfgPhi1020CorrMass.phi1020LSBLow, cfgPhi1020CorrMass.phi1020LSBUp, cfgPhi1020CorrMass.phi1020PeakLow, cfgPhi1020CorrMass.phi1020PeakUp, cfgPhi1020CorrMass.phi1020RSBLow, cfgPhi1020CorrMass.phi1020RSBUp); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.c1116phi1020LSBLow, cfgPhi1020CorrMass.c1117phi1020LSBUp, cfgPhi1020CorrMass.c1114phi1020PeakLow, cfgPhi1020CorrMass.c1115phi1020PeakUp, cfgPhi1020CorrMass.c1118phi1020RSBLow, cfgPhi1020CorrMass.c1119phi1020RSBUp); } else if constexpr (pairType == kHKStar) { invMass = associate.mKStar892(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1122kStar892LSBLow, cfgKStar892CorrMass.c1123kStar892LSBUp, cfgKStar892CorrMass.c1120kStar892PeakLow, cfgKStar892CorrMass.c1121kStar892PeakUp, cfgKStar892CorrMass.c1124kStar892RSBLow, cfgKStar892CorrMass.c1125kStar892RSBUp); } else if constexpr (pairType == kHKStarBar) { invMass = associate.mKStar892Bar(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1122kStar892LSBLow, cfgKStar892CorrMass.c1123kStar892LSBUp, cfgKStar892CorrMass.c1120kStar892PeakLow, cfgKStar892CorrMass.c1121kStar892PeakUp, cfgKStar892CorrMass.c1124kStar892RSBLow, cfgKStar892CorrMass.c1125kStar892RSBUp); } else if constexpr (pairType == kHLambda) { invMass = associate.mLambda1520(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1128lambda1520LSBLow, cfgLambda1520CorrMass.c1129lambda1520LSBUp, cfgLambda1520CorrMass.c1126lambda1520PeakLow, cfgLambda1520CorrMass.c1127lambda1520PeakUp, cfgLambda1520CorrMass.c1130lambda1520RSBLow, cfgLambda1520CorrMass.c1131lambda1520RSBUp); } else if constexpr (pairType == kHLambdaBar) { invMass = associate.mLambda1520Bar(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1128lambda1520LSBLow, cfgLambda1520CorrMass.c1129lambda1520LSBUp, cfgLambda1520CorrMass.c1126lambda1520PeakLow, cfgLambda1520CorrMass.c1127lambda1520PeakUp, cfgLambda1520CorrMass.c1130lambda1520RSBLow, cfgLambda1520CorrMass.c1131lambda1520RSBUp); } invMass = -1.0f; @@ -3033,12 +3033,12 @@ struct HParticleCorrelationSameEvent { const float pz = dau1.pz() + dau2.pz(); const float energy = RecoDecay::e(dau1.px() + dau2.px(), dau1.py() + dau2.py(), pz, mass); const float rapidity = 0.5f * std::log((energy + pz) / (energy - pz)); - if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { return false; } float centrality = -1.0f; - switch (cfgEvent.centralityEstimator) { + switch (cfgEvent.c1028centralityEstimator) { case 0: centrality = collision.centFT0C(); break; @@ -3145,17 +3145,17 @@ struct HParticleCorrelationSameEvent { return false; } - if (cfgDerivedData.mixingBin >= 0 && mixingBin != cfgDerivedData.mixingBin) { + if (cfgDerivedData.c1133mixingBin >= 0 && mixingBin != cfgDerivedData.c1133mixingBin) { return false; } - const uint64_t requestedMask = cfgDerivedData.pairMask; + const uint64_t requestedMask = cfgDerivedData.c1132pairMask; if (requestedMask == 0ULL) { return corrMask != 0ULL; } - if (cfgDerivedData.requireAllPairBits) { + if (cfgDerivedData.c1134requireAllPairBits) { return (corrMask & requestedMask) == requestedMask; } @@ -3234,12 +3234,12 @@ struct HParticleCorrelationSameEvent { float invMass = -1.0f; const uint8_t massRegion = getResonanceMassRegion(associate, invMass); const float rapidity = computeRapidity(associate, invMass); - if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { continue; } // Trigger-resonance daughter overlap -----expensive check ---- - if (rejectResoWithAnyTriggerDaughter) { + if (c1112rejectResoWithAnyTriggerDaughter) { // Strict mode: reject the whole resonance if either daughter belongs to the event trigger population. if (checkTrackInList(posDauTrack, triggerGIList) || checkTrackInList(negDauTrack, triggerGIList)) { continue; @@ -3294,7 +3294,7 @@ struct HParticleCorrelationSameEvent { std::vector posDauGIListRSSB; std::vector negDauGIListRSSB; - if (rejectResoWithAnyTriggerDaughter) { + if (c1112rejectResoWithAnyTriggerDaughter) { for (const auto& trigger : triggers) { if (selectionTrack(trigger) != kTrackAccepted) { continue; @@ -3319,14 +3319,14 @@ struct HParticleCorrelationSameEvent { } // execute Correlation Function is over // Event Filter - Filter eventFilter = (!cfgEvent.requireSel8) || (o2::aod::evsel::sel8 == true); - Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.cutZvertex); + Filter eventFilter = (!cfgEvent.c1003requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c1002cutZvertex); // Track Filter - Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.ptMin) && (o2::aod::track::pt < cfgTrackCuts.ptMax); - Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.etaMax); - Filter dcaFilter = ((!cfgTrackCuts.useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.dcaXYMax)) && - ((!cfgTrackCuts.useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.dcaZMax)); + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c1043ptMin) && (o2::aod::track::pt < cfgTrackCuts.c1044ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c1045etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c1065useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c1066dcaXYMax)) && + ((!cfgTrackCuts.c1067useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c1068dcaZMax)); using MyFilteredCollisions = soa::Filtered>; // , // using MyFilteredTracks = soa::Filtered>; using MyFilteredTracks = soa::Filtered>; @@ -3336,27 +3336,27 @@ struct HParticleCorrelationSameEvent { // // definition of partitions SliceCache cache; - Partition triggerTracks = cfgPartitions.triggerPtLow < aod::track::pt && aod::track::pt < cfgPartitions.triggerPtHigh; + Partition triggerTracks = cfgPartitions.c1077triggerPtLow < aod::track::pt && aod::track::pt < cfgPartitions.c1078triggerPtHigh; Partition posTracks = aod::track::signed1Pt > 0.0f; Partition negTracks = aod::track::signed1Pt < 0.0f; - Partition assocTracksLowPt = cfgPartitions.assocPtLowMin < aod::track::pt && aod::track::pt < cfgPartitions.assocPtLowMax; - Partition assocTracksHighPt = cfgPartitions.assocPtHighMin < aod::track::pt && aod::track::pt < cfgPartitions.assocPtHighMax; + Partition assocTracksLowPt = cfgPartitions.c1079assocPtLowMin < aod::track::pt && aod::track::pt < cfgPartitions.c1080assocPtLowMax; + Partition assocTracksHighPt = cfgPartitions.c1081assocPtHighMin < aod::track::pt && aod::track::pt < cfgPartitions.c1082assocPtHighMax; - Partition assocPhiLowPt = (cfgResPartitions.phiPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.phiPtLowMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); - Partition assocPhiHighPt = (cfgResPartitions.phiPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.phiPtHighMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + Partition assocPhiLowPt = (cfgResPartitions.c1085phiPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1086phiPtLowMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + Partition assocPhiHighPt = (cfgResPartitions.c1087phiPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1088phiPtHighMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); - Partition assocKStarLowPt = (cfgResPartitions.kstarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarPtLowMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); - Partition assocKStarHighPt = (cfgResPartitions.kstarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarPtHighMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + Partition assocKStarLowPt = (cfgResPartitions.c1089kstarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1090kstarPtLowMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + Partition assocKStarHighPt = (cfgResPartitions.c1091kstarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1092kstarPtHighMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); - Partition assocKStarBarLowPt = (cfgResPartitions.kstarBarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarBarPtLowMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); - Partition assocKStarBarHighPt = (cfgResPartitions.kstarBarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.kstarBarPtHighMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + Partition assocKStarBarLowPt = (cfgResPartitions.c1093kstarBarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1094kstarBarPtLowMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + Partition assocKStarBarHighPt = (cfgResPartitions.c1095kstarBarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1096kstarBarPtHighMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); - Partition assocLambda1520LowPt = (cfgResPartitions.lambda1520PtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520PtLowMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); - Partition assocLambda1520HighPt = (cfgResPartitions.lambda1520PtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520PtHighMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + Partition assocLambda1520LowPt = (cfgResPartitions.c1097lambda1520PtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1098lambda1520PtLowMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + Partition assocLambda1520HighPt = (cfgResPartitions.c1099lambda1520PtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1100lambda1520PtHighMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); - Partition assocLambda1520BarLowPt = (cfgResPartitions.lambda1520BarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520BarPtLowMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); - Partition assocLambda1520BarHighPt = (cfgResPartitions.lambda1520BarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.lambda1520BarPtHighMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + Partition assocLambda1520BarLowPt = (cfgResPartitions.c1101lambda1520BarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1102lambda1520BarPtLowMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + Partition assocLambda1520BarHighPt = (cfgResPartitions.c1103lambda1520BarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1104lambda1520BarPtHighMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); int dfNumber = 0; int64_t iCollGlobalCount = -1; @@ -3368,13 +3368,13 @@ struct HParticleCorrelationSameEvent { void processNothing(aod::Origins const& origins) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c1001printDebugMessages) { LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); } // Intentionally empty. // Keeps the task alive when running purely on derived data. } - PROCESS_SWITCH(HParticleCorrelationSameEvent, processNothing, "Dummy process for derived-data analysis", true); + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processNothing, "Dummy process for derived-data analysis", true); void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, aod::ResonanceCndts const& resonanceCndts, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&) { @@ -3384,7 +3384,7 @@ struct HParticleCorrelationSameEvent { // LOG(info)<<"DEBUG :: Origins :: "<(); int currentRunNumber = bc.runNumber(); @@ -3441,7 +3441,7 @@ struct HParticleCorrelationSameEvent { seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedSelCollision)); const auto tracksPerCollision = fullTracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); - if (tracksPerCollision.size() < cfgEvent.minNFilteredTracks) { + if (tracksPerCollision.size() < cfgEvent.c1005minNFilteredTracks) { continue; } seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinFilteredTracks)); @@ -3463,7 +3463,7 @@ struct HParticleCorrelationSameEvent { nTrack++; } // track loop is over. - if (nTrack < cfgEvent.minNSelectedTracks) { + if (nTrack < cfgEvent.c1006minNSelectedTracks) { continue; } @@ -3508,7 +3508,7 @@ struct HParticleCorrelationSameEvent { // Unlike Sign Signal // LikeSign Signal bool hasSelectedTrigger = false; - if (requireSelectedTriggerForInvariantMass) { + if (c1113requireSelectedTriggerForInvariantMass) { for (const auto& trigger : triggerTracksPerColl) { if (selectionTrack(trigger) == kTrackAccepted) { hasSelectedTrigger = true; @@ -3517,7 +3517,7 @@ struct HParticleCorrelationSameEvent { } } - if (!requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { + if (!c1113requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); @@ -3579,7 +3579,7 @@ struct HParticleCorrelationSameEvent { collision.centFT0M(), collision.centFT0A(), collision.centFV0A(), - static_cast(cfgMixing.mixingEstimator), + static_cast(cfgMixing.c1137mixingEstimator), mixingBin, corrPresenceMask); @@ -3709,7 +3709,7 @@ struct HParticleCorrelationSameEvent { mixingQA.fill(HIST("Mixing/PerDF/EligiblePoolOccupancy"), iMixBin, corrChannel, nEligibleCollisions); mixingQA.fill(HIST("Mixing/PerDF/PoolOccupancyDistribution"), corrChannel, nEligibleCollisions); - if (nEligibleCollisions >= static_cast(cfgMixing.nEvtMixing)) { + if (nEligibleCollisions >= static_cast(cfgMixing.c1136nEvtMixing)) { ++nReadyMixBins[corrChannel]; mixingQA.fill(HIST("Mixing/PerDF/ReadyPoolMap"), iMixBin, corrChannel); } @@ -3721,10 +3721,10 @@ struct HParticleCorrelationSameEvent { mixingQA.fill(HIST("Mixing/PerDF/NReadyMixBins"), corrChannel, nReadyMixBins[corrChannel]); } } - PROCESS_SWITCH(HParticleCorrelationSameEvent, processSameEvent, "Process Same event", true); + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processSameEvent, "Process Same event", true); }; -struct HParticleCorrelationMixedEvent { +struct HParticleCorrelation3MixedEvent { HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -3809,101 +3809,101 @@ struct HParticleCorrelationMixedEvent { } struct : ConfigurableGroup { - Configurable printDebugMessages{"printDebugMessages", false, "Print debug messages"}; + Configurable c2001printDebugMessages{"c2001printDebugMessages", false, "Print debug messages"}; } cfgDebug; struct : ConfigurableGroup { - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; - ConfigurableAxis axisDeltaEta{"axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; - ConfigurableAxis axisCorrSparseTriggerPt{"axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; - ConfigurableAxis axisCorrSparseAssocPt{"axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; - ConfigurableAxis axisCorrSparseDeltaPhi{"axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; - ConfigurableAxis axisCorrSparseDeltaEta{"axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2002axisDeltaPhi{"c2002axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2003axisDeltaEta{"c2003axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2004axisCorrSparseTriggerPt{"c2004axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c2005axisCorrSparseAssocPt{"c2005axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c2006axisCorrSparseDeltaPhi{"c2006axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2007axisCorrSparseDeltaEta{"c2007axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; } cfgAxis; struct : ConfigurableGroup { - Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; - Configurable mixingEstimator{"mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; - ConfigurableAxis axisVtxMixing{"axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; - ConfigurableAxis axisCentMixing{"axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + Configurable c2008nEvtMixing{"c2008nEvtMixing", 5, "Number of events to mix"}; + Configurable c2009mixingEstimator{"c2009mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis c2010axisVtxMixing{"c2010axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; } cfgMixing; struct : ConfigurableGroup { - Configurable triggerPtLow{"triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; - Configurable triggerPtHigh{"triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; - Configurable assocPtLowMin{"assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; - Configurable assocPtLowMax{"assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; - Configurable assocPtHighMin{"assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; - Configurable assocPtHighMax{"assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + Configurable c2012triggerPtLow{"c2012triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable c2013triggerPtHigh{"c2013triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable c2014assocPtLowMin{"c2014assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable c2015assocPtLowMax{"c2015assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable c2016assocPtHighMin{"c2016assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable c2017assocPtHighMax{"c2017assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; } cfgPartitions; struct : ConfigurableGroup { - Configurable resoRapidityMin{"resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; - Configurable resoRapidityMax{"resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; - - Configurable phiPtLowMin{"phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; - Configurable phiPtLowMax{"phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; - Configurable phiPtHighMin{"phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; - Configurable phiPtHighMax{"phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; - - Configurable kstarPtLowMin{"kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; - Configurable kstarPtLowMax{"kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; - Configurable kstarPtHighMin{"kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; - Configurable kstarPtHighMax{"kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; - - Configurable kstarBarPtLowMin{"kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; - Configurable kstarBarPtLowMax{"kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; - Configurable kstarBarPtHighMin{"kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; - Configurable kstarBarPtHighMax{"kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; - - Configurable lambda1520PtLowMin{"lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; - Configurable lambda1520PtLowMax{"lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; - Configurable lambda1520PtHighMin{"lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; - Configurable lambda1520PtHighMax{"lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; - - Configurable lambda1520BarPtLowMin{"lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtLowMax{"lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtHighMin{"lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; - Configurable lambda1520BarPtHighMax{"lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + Configurable c2018resoRapidityMin{"c2018resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable c2019resoRapidityMax{"c2019resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable c2020phiPtLowMin{"c2020phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable c2021phiPtLowMax{"c2021phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable c2022phiPtHighMin{"c2022phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable c2023phiPtHighMax{"c2023phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable c2024kstarPtLowMin{"c2024kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable c2025kstarPtLowMax{"c2025kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable c2026kstarPtHighMin{"c2026kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable c2027kstarPtHighMax{"c2027kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable c2028kstarBarPtLowMin{"c2028kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable c2029kstarBarPtLowMax{"c2029kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable c2030kstarBarPtHighMin{"c2030kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable c2031kstarBarPtHighMax{"c2031kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable c2032lambda1520PtLowMin{"c2032lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable c2033lambda1520PtLowMax{"c2033lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable c2034lambda1520PtHighMin{"c2034lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable c2035lambda1520PtHighMax{"c2035lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable c2036lambda1520BarPtLowMin{"c2036lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable c2037lambda1520BarPtLowMax{"c2037lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable c2038lambda1520BarPtHighMin{"c2038lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable c2039lambda1520BarPtHighMax{"c2039lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; } cfgResPartitions; struct : ConfigurableGroup { - Configurable phi1020PeakLow{"phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; - Configurable phi1020PeakUp{"phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; - Configurable phi1020LSBLow{"phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; - Configurable phi1020LSBUp{"phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; - Configurable phi1020RSBLow{"phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; - Configurable phi1020RSBUp{"phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + Configurable c2040phi1020PeakLow{"c2040phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c2041phi1020PeakUp{"c2041phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c2042phi1020LSBLow{"c2042phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c2043phi1020LSBUp{"c2043phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c2044phi1020RSBLow{"c2044phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c2045phi1020RSBUp{"c2045phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; } cfgPhi1020CorrMass; struct : ConfigurableGroup { - Configurable kStar892PeakLow{"kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; - Configurable kStar892PeakUp{"kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; - Configurable kStar892LSBLow{"kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; - Configurable kStar892LSBUp{"kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; - Configurable kStar892RSBLow{"kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; - Configurable kStar892RSBUp{"kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + Configurable c2046kStar892PeakLow{"c2046kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable c2047kStar892PeakUp{"c2047kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable c2048kStar892LSBLow{"c2048kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable c2049kStar892LSBUp{"c2049kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable c2050kStar892RSBLow{"c2050kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable c2051kStar892RSBUp{"c2051kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; } cfgKStar892CorrMass; struct : ConfigurableGroup { - Configurable lambda1520PeakLow{"lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; - Configurable lambda1520PeakUp{"lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; - Configurable lambda1520LSBLow{"lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; - Configurable lambda1520LSBUp{"lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; - Configurable lambda1520RSBLow{"lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; - Configurable lambda1520RSBUp{"lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + Configurable c2052lambda1520PeakLow{"c2052lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable c2053lambda1520PeakUp{"c2053lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable c2054lambda1520LSBLow{"c2054lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable c2055lambda1520LSBUp{"c2055lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable c2056lambda1520RSBLow{"c2056lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable c2057lambda1520RSBUp{"c2057lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; } cfgLambda1520CorrMass; struct : ConfigurableGroup { - Configurable> pidConfigSetting{"pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; - Configurable> pidVetoSetting{"pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; - Configurable cfgId07CheckTofBeta{"cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + Configurable> c2058pidConfigSetting{"c2058pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> c2059pidVetoSetting{"c2059pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable c2060cfgId07CheckTofBeta{"c2060cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; } cfgIdCut; void init(InitContext const&) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOGF(info, "Starting init"); } @@ -3926,25 +3926,25 @@ struct HParticleCorrelationMixedEvent { const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; - AxisSpec axisDeltaPhiSpec{cfgAxis.axisDeltaPhi, "#Delta#varphi"}; - AxisSpec axisDeltaEtaSpec{cfgAxis.axisDeltaEta, "#Delta#eta"}; - AxisSpec axisCorrSparseTriggerPt{cfgAxis.axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; - AxisSpec axisCorrSparseAssocPt{cfgAxis.axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; - AxisSpec axisCorrSparseDeltaPhi{cfgAxis.axisCorrSparseDeltaPhi, "#Delta#varphi"}; - AxisSpec axisCorrSparseDeltaEta{cfgAxis.axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisDeltaPhiSpec{cfgAxis.c2002axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c2003axisDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c2004axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c2005axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c2006axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c2007axisCorrSparseDeltaEta, "#Delta#eta"}; const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; - AxisSpec axisVtxMixOperation{cfgMixing.axisVtxMixing, "z_{vtx} (cm)"}; - AxisSpec axisCentMixOperation{cfgMixing.axisCentMixing, "Centrality (%)"}; + AxisSpec axisVtxMixOperation{cfgMixing.c2010axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixOperation{cfgMixing.c2011axisCentMixing, "Centrality (%)"}; const int nMixOperationBins = axisVtxMixOperation.getNbins() * axisCentMixOperation.getNbins(); const AxisSpec axisMixOperationBin = {nMixOperationBins, -0.5, static_cast(nMixOperationBins) - 0.5, "Mixing bin"}; const AxisSpec axisMixOperationChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; - const AxisSpec axisMixPartners = {static_cast(cfgMixing.nEvtMixing) + 1, -0.5, static_cast(cfgMixing.nEvtMixing) + 0.5, "N mixed partner events"}; + const AxisSpec axisMixPartners = {static_cast(cfgMixing.c2008nEvtMixing) + 1, -0.5, static_cast(cfgMixing.c2008nEvtMixing) + 0.5, "N mixed partner events"}; const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; @@ -4076,15 +4076,15 @@ struct HParticleCorrelationMixedEvent { using BinningTypeVtxZFT0A = ColumnBinningPolicy; using BinningTypeVtxZFV0A = ColumnBinningPolicy; - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.axisVtxMixing, cfgMixing.axisCentMixing}, true}; + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; template int getDerivedMixingBin(const C& collision) { - switch (cfgMixing.mixingEstimator) { + switch (cfgMixing.c2009mixingEstimator) { case kMixCentFT0C: return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); case kMixCentFT0M: @@ -4101,7 +4101,7 @@ struct HParticleCorrelationMixedEvent { template float getDerivedMixingEstimatorValue(const C& collision) { - switch (cfgMixing.mixingEstimator) { + switch (cfgMixing.c2009mixingEstimator) { case kMixCentFT0C: return collision.centFT0C(); case kMixCentFT0M: @@ -4128,20 +4128,20 @@ struct HParticleCorrelationMixedEvent { { // Static is only run once, ever. static const std::array doVetoTPC = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTPC)}; + getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kDoVetoTPC)}; static const std::array vetoTPC = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTPC), - getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTPC)}; + getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kVetoTPC)}; return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); } @@ -4151,20 +4151,20 @@ struct HParticleCorrelationMixedEvent { { // Only computed once static const std::array doVetoTOF = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kKa, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kPr, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kEl, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kMu, kDoVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kDe, kDoVetoTOF)}; + getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kDoVetoTOF)}; static const std::array vetoTOF = { - getCfg(cfgIdCut.pidVetoSetting, kPi, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kKa, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kPr, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kEl, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kMu, kVetoTOF), - getCfg(cfgIdCut.pidVetoSetting, kDe, kVetoTOF)}; + getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kVetoTOF)}; return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); } @@ -4181,7 +4181,7 @@ struct HParticleCorrelationMixedEvent { inline bool checkReliableTOF(const T& track) { // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: - if (cfgIdCut.cfgId07CheckTofBeta) { + if (cfgIdCut.c2060cfgId07CheckTofBeta) { return (track.hasTOF() && track.beta() > 0.0f); } return track.hasTOF(); @@ -4206,7 +4206,7 @@ struct HParticleCorrelationMixedEvent { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPC(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -4215,7 +4215,7 @@ struct HParticleCorrelationMixedEvent { } } - static const bool doRelativeTPCcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCcheck); + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTPCcheck); if (doRelativeTPCcheck) { if (!relativeIdOthersTPC(track)) { // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others @@ -4259,7 +4259,7 @@ struct HParticleCorrelationMixedEvent { } else { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPCTOF(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -4268,7 +4268,7 @@ struct HParticleCorrelationMixedEvent { } } - static const bool doRelativeTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTOFcheck); + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTOFcheck); if (doRelativeTOFcheck) { if (!relativeIdOthersTOF(track)) { // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others @@ -4277,7 +4277,7 @@ struct HParticleCorrelationMixedEvent { } } - static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); if (doRelativeTPCTOFcheck) { if (!relativeIdOthersTPCTOF(track)) { // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others @@ -4302,15 +4302,15 @@ struct HParticleCorrelationMixedEvent { bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) { // Static cache inside function - initialized once on first call - static const auto thrPforTOF = getCfg(cfgIdCut.pidConfigSetting, pidMode, kThrPforTOF); - static const auto idCutTypeLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeLowP); - static const auto nSigmaTPCLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCLowP); - static const auto nSigmaTOFLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFLowP); - static const auto nSigmaRadLowP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadLowP); - static const auto idCutTypeHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kIdCutTypeHighP); - static const auto nSigmaTPCHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTPCHighP); - static const auto nSigmaTOFHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaTOFHighP); - static const auto nSigmaRadHighP = getCfg(cfgIdCut.pidConfigSetting, pidMode, kNSigmaRadHighP); + static const auto thrPforTOF = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaRadHighP); if (track.p() < thrPforTOF) { if (checkReliableTOF(track)) { @@ -4626,19 +4626,19 @@ struct HParticleCorrelationMixedEvent { { if constexpr (pairType == kHPhi) { invMass = associate.mPhi1020(); - return getMassRegionTag(invMass, cfgPhi1020CorrMass.phi1020LSBLow, cfgPhi1020CorrMass.phi1020LSBUp, cfgPhi1020CorrMass.phi1020PeakLow, cfgPhi1020CorrMass.phi1020PeakUp, cfgPhi1020CorrMass.phi1020RSBLow, cfgPhi1020CorrMass.phi1020RSBUp); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.c2042phi1020LSBLow, cfgPhi1020CorrMass.c2043phi1020LSBUp, cfgPhi1020CorrMass.c2040phi1020PeakLow, cfgPhi1020CorrMass.c2041phi1020PeakUp, cfgPhi1020CorrMass.c2044phi1020RSBLow, cfgPhi1020CorrMass.c2045phi1020RSBUp); } else if constexpr (pairType == kHKStar) { invMass = associate.mKStar892(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c2048kStar892LSBLow, cfgKStar892CorrMass.c2049kStar892LSBUp, cfgKStar892CorrMass.c2046kStar892PeakLow, cfgKStar892CorrMass.c2047kStar892PeakUp, cfgKStar892CorrMass.c2050kStar892RSBLow, cfgKStar892CorrMass.c2051kStar892RSBUp); } else if constexpr (pairType == kHKStarBar) { invMass = associate.mKStar892Bar(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.kStar892LSBLow, cfgKStar892CorrMass.kStar892LSBUp, cfgKStar892CorrMass.kStar892PeakLow, cfgKStar892CorrMass.kStar892PeakUp, cfgKStar892CorrMass.kStar892RSBLow, cfgKStar892CorrMass.kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c2048kStar892LSBLow, cfgKStar892CorrMass.c2049kStar892LSBUp, cfgKStar892CorrMass.c2046kStar892PeakLow, cfgKStar892CorrMass.c2047kStar892PeakUp, cfgKStar892CorrMass.c2050kStar892RSBLow, cfgKStar892CorrMass.c2051kStar892RSBUp); } else if constexpr (pairType == kHLambda) { invMass = associate.mLambda1520(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c2054lambda1520LSBLow, cfgLambda1520CorrMass.c2055lambda1520LSBUp, cfgLambda1520CorrMass.c2052lambda1520PeakLow, cfgLambda1520CorrMass.c2053lambda1520PeakUp, cfgLambda1520CorrMass.c2056lambda1520RSBLow, cfgLambda1520CorrMass.c2057lambda1520RSBUp); } else if constexpr (pairType == kHLambdaBar) { invMass = associate.mLambda1520Bar(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.lambda1520LSBLow, cfgLambda1520CorrMass.lambda1520LSBUp, cfgLambda1520CorrMass.lambda1520PeakLow, cfgLambda1520CorrMass.lambda1520PeakUp, cfgLambda1520CorrMass.lambda1520RSBLow, cfgLambda1520CorrMass.lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c2054lambda1520LSBLow, cfgLambda1520CorrMass.c2055lambda1520LSBUp, cfgLambda1520CorrMass.c2052lambda1520PeakLow, cfgLambda1520CorrMass.c2053lambda1520PeakUp, cfgLambda1520CorrMass.c2056lambda1520RSBLow, cfgLambda1520CorrMass.c2057lambda1520RSBUp); } invMass = -1.0f; @@ -4777,7 +4777,7 @@ struct HParticleCorrelationMixedEvent { continue; } const float rapidity = computeDerivedRapidity(associate, invMass); - if (rapidity < cfgResPartitions.resoRapidityMin || rapidity > cfgResPartitions.resoRapidityMax) { + if (rapidity < cfgResPartitions.c2018resoRapidityMin || rapidity > cfgResPartitions.c2019resoRapidityMax) { continue; } if (requiredMassRegion != kMassNone && massRegion != requiredMassRegion) { @@ -4818,94 +4818,94 @@ struct HParticleCorrelationMixedEvent { } } - Preslice drTracksPerCollisionPreslice = aod::hpcorrtrack::hpCorrCollisionId; - Preslice drResoPerCollisionPreslice = aod::hpcorrresonance::hpCorrCollisionId; + Preslice drTracksPerCollisionPreslice = aod::hpcorrtrack::hpCorrCollId; + Preslice drResoPerCollisionPreslice = aod::hpcorrresonance::hpCorrCollId; // definition of partitions SliceCache cache; - Partition drMixHHLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHLowPt)) != static_cast(0ULL); - Partition drMixHHHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHHighPt)) != static_cast(0ULL); + Partition drMixHHLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHLowPt)) != static_cast(0ULL); + Partition drMixHHHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHHighPt)) != static_cast(0ULL); - Partition drMixHPiLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiLowPt)) != static_cast(0ULL); - Partition drMixHPiHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiHighPt)) != static_cast(0ULL); - Partition drMixHKaLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaLowPt)) != static_cast(0ULL); - Partition drMixHKaHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaHighPt)) != static_cast(0ULL); - Partition drMixHPrLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrLowPt)) != static_cast(0ULL); - Partition drMixHPrHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrHighPt)) != static_cast(0ULL); + Partition drMixHPiLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiLowPt)) != static_cast(0ULL); + Partition drMixHPiHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiHighPt)) != static_cast(0ULL); + Partition drMixHKaLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaLowPt)) != static_cast(0ULL); + Partition drMixHKaHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaHighPt)) != static_cast(0ULL); + Partition drMixHPrLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrLowPt)) != static_cast(0ULL); + Partition drMixHPrHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrHighPt)) != static_cast(0ULL); - Partition drMixHPhiLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtPeak)) != static_cast(0ULL); - Partition drMixHPhiLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtLSB)) != static_cast(0ULL); - Partition drMixHPhiLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtRSB)) != static_cast(0ULL); + Partition drMixHPhiLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtPeak)) != static_cast(0ULL); + Partition drMixHPhiLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtLSB)) != static_cast(0ULL); + Partition drMixHPhiLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtRSB)) != static_cast(0ULL); - Partition drMixHPhiHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtPeak)) != static_cast(0ULL); - Partition drMixHPhiHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtLSB)) != static_cast(0ULL); - Partition drMixHPhiHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtRSB)) != static_cast(0ULL); + Partition drMixHPhiHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtPeak)) != static_cast(0ULL); + Partition drMixHPhiHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtLSB)) != static_cast(0ULL); + Partition drMixHPhiHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtRSB)) != static_cast(0ULL); - Partition drMixHKStarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtPeak)) != static_cast(0ULL); - Partition drMixHKStarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtLSB)) != static_cast(0ULL); - Partition drMixHKStarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtRSB)) != static_cast(0ULL); + Partition drMixHKStarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtPeak)) != static_cast(0ULL); + Partition drMixHKStarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtLSB)) != static_cast(0ULL); + Partition drMixHKStarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtRSB)) != static_cast(0ULL); - Partition drMixHKStarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtPeak)) != static_cast(0ULL); - Partition drMixHKStarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtLSB)) != static_cast(0ULL); - Partition drMixHKStarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtRSB)) != static_cast(0ULL); + Partition drMixHKStarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtPeak)) != static_cast(0ULL); + Partition drMixHKStarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtLSB)) != static_cast(0ULL); + Partition drMixHKStarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtRSB)) != static_cast(0ULL); - Partition drMixHKStarBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtPeak)) != static_cast(0ULL); - Partition drMixHKStarBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtLSB)) != static_cast(0ULL); - Partition drMixHKStarBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtRSB)) != static_cast(0ULL); + Partition drMixHKStarBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtPeak)) != static_cast(0ULL); + Partition drMixHKStarBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtLSB)) != static_cast(0ULL); + Partition drMixHKStarBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtRSB)) != static_cast(0ULL); - Partition drMixHKStarBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtPeak)) != static_cast(0ULL); - Partition drMixHKStarBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtLSB)) != static_cast(0ULL); - Partition drMixHKStarBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtRSB)) != static_cast(0ULL); + Partition drMixHKStarBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtPeak)) != static_cast(0ULL); + Partition drMixHKStarBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtLSB)) != static_cast(0ULL); + Partition drMixHKStarBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtRSB)) != static_cast(0ULL); - Partition drMixHLambdaLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtRSB)) != static_cast(0ULL); + Partition drMixHLambdaLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtRSB)) != static_cast(0ULL); - Partition drMixHLambdaHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtRSB)) != static_cast(0ULL); + Partition drMixHLambdaHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtRSB)) != static_cast(0ULL); - Partition drMixHLambdaBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtRSB)) != static_cast(0ULL); + Partition drMixHLambdaBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtRSB)) != static_cast(0ULL); - Partition drMixHLambdaBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtRSB)) != static_cast(0ULL); + Partition drMixHLambdaBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtPeak)) != static_cast(0ULL); + Partition drMixHLambdaBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtLSB)) != static_cast(0ULL); + Partition drMixHLambdaBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtRSB)) != static_cast(0ULL); - Partition drTriggerTracks = cfgPartitions.triggerPtLow < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.triggerPtHigh; + Partition drTriggerTracks = cfgPartitions.c2012triggerPtLow < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2013triggerPtHigh; Partition drPosTracks = aod::hpcorrtrack::sign > static_cast(0); Partition drNegTracks = aod::hpcorrtrack::sign < static_cast(0); - Partition drAssocTracksLowPt = cfgPartitions.assocPtLowMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.assocPtLowMax; - Partition drAssocTracksHighPt = cfgPartitions.assocPtHighMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.assocPtHighMax; - - Partition drAssocPhiLowPt = cfgResPartitions.phiPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.phiPtLowMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); - Partition drAssocPhiHighPt = cfgResPartitions.phiPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.phiPtHighMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); - Partition drAssocKStarLowPt = cfgResPartitions.kstarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarPtLowMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); - Partition drAssocKStarHighPt = cfgResPartitions.kstarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarPtHighMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); - Partition drAssocKStarBarLowPt = cfgResPartitions.kstarBarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarBarPtLowMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); - Partition drAssocKStarBarHighPt = cfgResPartitions.kstarBarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.kstarBarPtHighMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); - Partition drAssocLambda1520LowPt = cfgResPartitions.lambda1520PtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520PtLowMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); - Partition drAssocLambda1520HighPt = cfgResPartitions.lambda1520PtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520PtHighMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); - Partition drAssocLambda1520BarLowPt = cfgResPartitions.lambda1520BarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520BarPtLowMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); - Partition drAssocLambda1520BarHighPt = cfgResPartitions.lambda1520BarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.lambda1520BarPtHighMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); + Partition drAssocTracksLowPt = cfgPartitions.c2014assocPtLowMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2015assocPtLowMax; + Partition drAssocTracksHighPt = cfgPartitions.c2016assocPtHighMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2017assocPtHighMax; + + Partition drAssocPhiLowPt = cfgResPartitions.c2020phiPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2021phiPtLowMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); + Partition drAssocPhiHighPt = cfgResPartitions.c2022phiPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2023phiPtHighMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); + Partition drAssocKStarLowPt = cfgResPartitions.c2024kstarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2025kstarPtLowMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); + Partition drAssocKStarHighPt = cfgResPartitions.c2026kstarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2027kstarPtHighMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); + Partition drAssocKStarBarLowPt = cfgResPartitions.c2028kstarBarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2029kstarBarPtLowMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); + Partition drAssocKStarBarHighPt = cfgResPartitions.c2030kstarBarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2031kstarBarPtHighMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); + Partition drAssocLambda1520LowPt = cfgResPartitions.c2032lambda1520PtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2033lambda1520PtLowMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); + Partition drAssocLambda1520HighPt = cfgResPartitions.c2034lambda1520PtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2035lambda1520PtHighMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); + Partition drAssocLambda1520BarLowPt = cfgResPartitions.c2036lambda1520BarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2037lambda1520BarPtLowMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); + Partition drAssocLambda1520BarHighPt = cfgResPartitions.c2038lambda1520BarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2039lambda1520BarPtHighMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); int dfNumber = 0; void processNothing(aod::Origins const& origins) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); } // Intentionally empty. // Keeps the task alive when running purely on derived data. } - PROCESS_SWITCH(HParticleCorrelationMixedEvent, processNothing, "Dummy process for analysis", true); + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processNothing, "Dummy process for analysis", true); int nColl = 0; - void processMixEventInDeriveData(aod::Origins const& origins, aod::HPCorrCollisions const& drCollisions, aod::HPCorrTracks const& drFullTracks, aod::HPCorrResonances const& drResonanceCndts /*, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&*/) + void processMixEventInDeriveData(aod::Origins const& origins, aod::HPCorrColls const& drCollisions, aod::HPCorrTracks const& drFullTracks, aod::HPCorrResonances const& drResonanceCndts /*, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&*/) { dfNumber++; - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: df_" << dfNumber << " :: origins = " << origins.size() << " :: drCollisions = " << drCollisions.size() << " :: drFullTracks = " << drFullTracks.size() << " :: drResonanceCndts = " << drResonanceCndts.size(); } @@ -4916,15 +4916,15 @@ struct HParticleCorrelationMixedEvent { int64_t nBadResonanceCollision = 0; int64_t nBadResonanceDaughters = 0; for (const auto& coll : drCollisions) { - auto drTracksPerColl = drFullTracks.sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drResonancesPerColl = drResonanceCndts.sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); - auto drTriggerTracksPerColl = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drPosTracksPerColl = drPosTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drNegTracksPerColl = drNegTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drAssocTracksLowPtPerColl = drAssocTracksLowPt->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drAssocTracksHighPtPerColl = drAssocTracksHighPt->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, coll.globalIndex(), cache); - auto drAssocPhiLowPtPerColl = drAssocPhiLowPt->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); - auto drAssocPhiHighPtPerColl = drAssocPhiHighPt->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, coll.globalIndex(), cache); + auto drTracksPerColl = drFullTracks.sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drResonancesPerColl = drResonanceCndts.sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); + auto drTriggerTracksPerColl = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drPosTracksPerColl = drPosTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drNegTracksPerColl = drNegTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drAssocTracksLowPtPerColl = drAssocTracksLowPt->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drAssocTracksHighPtPerColl = drAssocTracksHighPt->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); + auto drAssocPhiLowPtPerColl = drAssocPhiLowPt->sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); + auto drAssocPhiHighPtPerColl = drAssocPhiHighPt->sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); if (coll.globalIndex() != coll.globalCollisionId()) { nBadCollisionIndex++; @@ -4933,9 +4933,9 @@ struct HParticleCorrelationMixedEvent { for (const auto& track : drTracksPerColl) { nTrackChecks++; - if (track.hpCorrCollisionId() != coll.globalIndex()) { + if (track.hpCorrCollId() != coll.globalIndex()) { nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: hpCorrCollisionId mismatch :: " << track.hpCorrCollisionId() << " != " << coll.globalIndex(); + LOG(error) << "TRACK CHECK :: hpCorrCollId mismatch :: " << track.hpCorrCollId() << " != " << coll.globalIndex(); } if (track.globalColRefId() != coll.globalCollisionId()) { nBadTrackCollision++; @@ -4958,9 +4958,9 @@ struct HParticleCorrelationMixedEvent { for (const auto& resonance : drResonancesPerColl) { nResonanceChecks++; - if (resonance.hpCorrCollisionId() != coll.globalIndex()) { + if (resonance.hpCorrCollId() != coll.globalIndex()) { nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: hpCorrCollisionId mismatch :: " << resonance.hpCorrCollisionId() << " != " << coll.globalIndex(); + LOG(error) << "RESONANCE CHECK :: hpCorrCollId mismatch :: " << resonance.hpCorrCollId() << " != " << coll.globalIndex(); } if (resonance.globalColRefId() != coll.globalCollisionId()) { nBadResonanceCollision++; @@ -5018,13 +5018,13 @@ struct HParticleCorrelationMixedEvent { LOG(error) << "DAUGHTER CHECK :: resonance/negTrack dataframeID mismatch :: " << resonance.dataframeID() << " != " << negTrack.dataframeID(); } - if (posTrack.hpCorrCollisionId() != coll.globalIndex()) { + if (posTrack.hpCorrCollId() != coll.globalIndex()) { nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: positive daughter belongs to wrong collision :: " << posTrack.hpCorrCollisionId() << " != " << coll.globalIndex(); + LOG(error) << "DAUGHTER CHECK :: positive daughter belongs to wrong collision :: " << posTrack.hpCorrCollId() << " != " << coll.globalIndex(); } - if (negTrack.hpCorrCollisionId() != coll.globalIndex()) { + if (negTrack.hpCorrCollId() != coll.globalIndex()) { nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negative daughter belongs to wrong collision :: " << negTrack.hpCorrCollisionId() << " != " << coll.globalIndex(); + LOG(error) << "DAUGHTER CHECK :: negative daughter belongs to wrong collision :: " << negTrack.hpCorrCollId() << " != " << coll.globalIndex(); } if (posTrack.originalColRefId() != coll.originalCollisionId()) { @@ -5057,11 +5057,11 @@ struct HParticleCorrelationMixedEvent { nColl++; } // Collision loop is over - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: SANITY CHECK SUMMARY :: bad collision indices = " << nBadCollisionIndex << " :: tracks checked = " << nTrackChecks << " :: bad track references = " << nBadTrackCollision << " :: resonances checked = " << nResonanceChecks << " :: bad resonance collision references = " << nBadResonanceCollision << " :: bad resonance daughter references = " << nBadResonanceDaughters; } if (nBadCollisionIndex == 0 && nBadTrackCollision == 0 && nBadResonanceCollision == 0 && nBadResonanceDaughters == 0) { - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: SANITY CHECK PASSED"; } } else { @@ -5077,7 +5077,7 @@ struct HParticleCorrelationMixedEvent { partnerCountPerTriggerEvent[collision.globalIndex()] = 0; } - for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.nEvtMixing, -1, collisionPool, collisionPool)) { + for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.c2008nEvtMixing, -1, collisionPool, collisionPool)) { nMixedEventPairs++; if (collision1.dataframeID() == collision2.dataframeID() && collision1.originalCollisionId() == collision2.originalCollisionId()) { @@ -5139,8 +5139,8 @@ struct HParticleCorrelationMixedEvent { static_assert(MixChannel >= 0, "Invalid track mixing-operation channel"); runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { - auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision1.globalIndex(), cache); - auto associates2 = assocPartition->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision2.globalIndex(), cache); + auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision1.globalIndex(), cache); + auto associates2 = assocPartition->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision2.globalIndex(), cache); executeDerivedCorrelationRole(histReg, triggerTracks1, associates2); }); @@ -5165,8 +5165,8 @@ struct HParticleCorrelationMixedEvent { static_assert(MixChannel >= 0, "Invalid resonance mixing-operation channel"); runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { - auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollisionId, collision1.globalIndex(), cache); - auto associates2 = assocPartition->sliceByCached(aod::hpcorrresonance::hpCorrCollisionId, collision2.globalIndex(), cache); + auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision1.globalIndex(), cache); + auto associates2 = assocPartition->sliceByCached(aod::hpcorrresonance::hpCorrCollId, collision2.globalIndex(), cache); associates2.bindExternalIndices(&drFullTracks); @@ -5211,7 +5211,7 @@ struct HParticleCorrelationMixedEvent { runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtRSB, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); }; - switch (cfgMixing.mixingEstimator) { + switch (cfgMixing.c2009mixingEstimator) { case kMixCentFT0C: runAllTrackMixing(colBinningFT0C); runAllResoMixing(colBinningFT0C); @@ -5233,20 +5233,20 @@ struct HParticleCorrelationMixedEvent { break; default: - LOG(fatal) << "DEBUG :: Invalid mixingEstimator = " << static_cast(cfgMixing.mixingEstimator); + LOG(fatal) << "DEBUG :: Invalid mixingEstimator = " << static_cast(cfgMixing.c2009mixingEstimator); break; } - if (cfgDebug.printDebugMessages) { + if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: EVENT MIXING SUMMARY :: mixed collision pairs = " << nMixedEventPairs; } } - PROCESS_SWITCH(HParticleCorrelationMixedEvent, processMixEventInDeriveData, "Process Mix event in derive data", true); + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Process Mix event in derive data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(context), - adaptAnalysisTask(context), - adaptAnalysisTask(context)}; + adaptAnalysisTask(context), + adaptAnalysisTask(context), + adaptAnalysisTask(context)}; } From 8bb3f2f43651c25222fe0d5bae690b5018c56bc5 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:48:52 +0200 Subject: [PATCH 232/648] [PWGCF] Fix memory leaks and other issues in GFWCore (#18216) --- PWGCF/GenericFramework/Core/FlowContainer.cxx | 43 +-- PWGCF/GenericFramework/Core/GFW.cxx | 110 ++++--- PWGCF/GenericFramework/Core/GFW.h | 20 +- PWGCF/GenericFramework/Core/GFWPowerArray.cxx | 113 +++---- PWGCF/GenericFramework/Core/GFWPowerArray.h | 18 +- PWGCF/GenericFramework/Core/GFWWeights.cxx | 277 ++++++++++++++---- PWGCF/GenericFramework/Core/GFWWeights.h | 22 +- 7 files changed, 397 insertions(+), 206 deletions(-) diff --git a/PWGCF/GenericFramework/Core/FlowContainer.cxx b/PWGCF/GenericFramework/Core/FlowContainer.cxx index c51fe3ece8a..4b94468bde6 100644 --- a/PWGCF/GenericFramework/Core/FlowContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowContainer.cxx @@ -415,6 +415,10 @@ TProfile* FlowContainer::GetCorrXXVsMulti(const char* order, int l_pti) } delete rethist; } + if (!retSubset) { + LOGF(error, "Correlation profile is null"); + return nullptr; + } if (fMultiRebin > 0) { TString temp_name(retSubset->GetName()); TProfile* tempprof = dynamic_cast(retSubset->Clone("tempProfile")); @@ -525,9 +529,9 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) corrN2 = GetHistCorrXXVsMulti(Form("%i2", n), static_cast(arg1)); corrN2->SetName(Form("Corr_%s", corrN2->GetName())); TH1D* rethist = GetCN2(corrN2); - TString* nam = new TString(corrN2->GetName()); + const TString nam(corrN2->GetName()); delete corrN2; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -543,10 +547,10 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) { TH1D* corrh = GetCN2VsX(n, onPt, arg1, arg2); - TString* nam = new TString(corrh->GetName()); + const TString nam(corrh->GetName()); TH1D* rethist = GetVN2(corrh); delete corrh; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -556,7 +560,6 @@ TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) } else { rethist->SetTitle(Form(";#it{N}_{tr};v_{%i}{2}", n)); } - delete nam; return rethist; } @@ -706,10 +709,10 @@ TH1D* FlowContainer::GetCN4VsX(int n, bool onPt, double arg1, double arg2) corrN4->SetName(Form("Corr_%s", corrN4->GetName())); } TH1D* rethist = GetCN4(corrN4, corrN2); - TString* nam = new TString(corrN4->GetName()); + const TString nam(corrN4->GetName()); delete corrN2; delete corrN4; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -780,9 +783,9 @@ TH1D* FlowContainer::GetVN4VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN4VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN4(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -812,9 +815,9 @@ TH1D* FlowContainer::GetCN6VsX(int n, bool onPt, double arg1, double arg2) TH1D* rethist = GetCN6(corrN6, corrN4, corrN2); delete corrN2; delete corrN4; - TString* nam = new TString(corrN6->GetName()); + const TString nam(corrN6->GetName()); delete corrN6; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -830,9 +833,9 @@ TH1D* FlowContainer::GetVN6VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN6VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN6(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -866,9 +869,9 @@ TH1D* FlowContainer::GetCN8VsX(int n, bool onPt, double arg1, double arg2) delete corrN2; delete corrN4; delete corrN6; - TString* nam = new TString(corrN8->GetName()); + const TString nam(corrN8->GetName()); delete corrN8; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -884,9 +887,9 @@ TH1D* FlowContainer::GetVN8VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN8VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN8(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -928,7 +931,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double if (nStopBin < nStartBin) nStopBin = fProf->GetXaxis()->GetNbins(); int nBins = nStopBin - nStartBin + 1; - double* l_bins = new double[nBins + 1]; + std::vector l_bins(nBins + 1); for (int i = 0; i <= nBins; i++) l_bins[i] = i; TProfile* retpf = 0; @@ -939,7 +942,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double while (fIDName.Tokenize(l_name, l_pos)) { l_name.Append(order); int ybin = fProf->GetYaxis()->FindBin(l_name.Data()); - TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins); + TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins.data()); if (!retpf) retpf = dynamic_cast(tempprof->Clone("RefFlowProf")); else @@ -1237,5 +1240,5 @@ double* FlowContainer::GetMultiRebin(int& nbins) double* retBins = new double[fMultiRebin + 1]; for (int i = 0; i <= nbins; i++) retBins[i] = fMultiRebinEdges[i]; - return fMultiRebinEdges; + return retBins; } diff --git a/PWGCF/GenericFramework/Core/GFW.cxx b/PWGCF/GenericFramework/Core/GFW.cxx index 3faf5142531..9a4918af00c 100644 --- a/PWGCF/GenericFramework/Core/GFW.cxx +++ b/PWGCF/GenericFramework/Core/GFW.cxx @@ -9,12 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFW.cxx +/// \brief Initialize regions and calculate n-particle correlations +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFW.h" #include "PWGCF/GenericFramework/Core/GFWPowerArray.h" +#include + #include -#include +#include #include #include #include @@ -22,10 +28,20 @@ using std::complex; using std::pair; using std::string; -using std::vector; + +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr std::size_t ThreeParticleOrder = 3; +} // namespace GFW::GFW() : fInitialized(false) {} +void GFW::Region::PrintStructure() +{ + LOGF(info, "%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); +} + GFW::~GFW() { for (auto pItr = fCumulants.begin(); pItr != fCumulants.end(); ++pItr) @@ -34,15 +50,15 @@ GFW::~GFW() void GFW::AddRegion(const string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { if (lNpT < 1) { - printf("Number of pT bins cannot be less than 1! Not adding anything.\n"); + LOGF(warning, "Number of pT bins cannot be less than 1! Not adding anything."); return; } if (lEtaMin >= lEtaMax) { - printf("Eta min. cannot be more than eta max! Not adding...\n"); + LOGF(warning, "Eta min. cannot be more than eta max! Not adding..."); return; } if (refName == "") { - printf("Region must have a name!\n"); + LOGF(warning, "Region must have a name!"); return; } Region lOneRegion; @@ -56,26 +72,33 @@ void GFW::AddRegion(const string& refName, double lEtaMin, double lEtaMax, int l lOneRegion.BitMask = BitMask; // Bit mask AddRegion(lOneRegion); }; -void GFW::AddRegion(const string& refName, const vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - AddRegion(std::move(refName), lEtaMin, lEtaMax, lNpT, BitMask); + const auto oldSize = fRegions.size(); + AddRegion(refName, lEtaMin, lEtaMax, lNpT, BitMask); + if (fRegions.size() == oldSize) + return; (fRegions.end() - 1)->Nhar = static_cast(lNparVec.size()); (fRegions.end() - 1)->NparVec = lNparVec; (fRegions.end() - 1)->powsDefined = true; }; -void GFW::AddRegion(string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNpar); - AddRegion(std::move(refName), tVec, lEtaMin, lEtaMax, lNpT, BitMask); + AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); }; -void GFW::AddRegion(string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNparVec[i]); - AddRegion(std::move(refName), tVec, lEtaMin, lEtaMax, lNpT, BitMask); + AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); }; int GFW::CreateRegions() { @@ -84,14 +107,13 @@ int GFW::CreateRegions() fCumulants.clear(); InitializePowerArrays(); if (fRegions.size() < 1) { - printf("No regions set. Skipping...\n"); + LOGF(warning, "No regions set. Skipping..."); return 0; } int nRegions = 0; for (auto pItr = fRegions.begin(); pItr != fRegions.end(); pItr++) { - GFWCumulant* lCumulant = new GFWCumulant(); - lCumulant->CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); - fCumulants.push_back(*lCumulant); + fCumulants.emplace_back(); + fCumulants.back().CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); ++nRegions; } if (nRegions) @@ -114,22 +136,25 @@ complex GFW::TwoRec(int n1, int n2, int p1, int p2, int ptbin, GFWCumula complex formula = part1 * part2 - part3; return formula; }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars) { - vector pows; + std::vector pows; + pows.reserve(hars.size()); for (int i = 0; i < static_cast(hars.size()); i++) pows.push_back(1); return RecursiveCorr(qpoi, qref, qol, ptbin, hars, pows); }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars, vector& pows) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars, std::vector& pows) { + if (hars.empty() || pows.empty()) + return complex(0, 0); if ((pows.at(0) != 1) && qol) qpoi = qol; // if the power of POI is not unity, then always use overlap (if defined). // Only valid for 1 particle of interest though! - if (hars.size() < 2) + if (hars.size() < TwoParticleOrder) return qpoi->Vec(hars.at(0), pows.at(0), ptbin); - if (hars.size() < 3) + if (hars.size() < ThreeParticleOrder) return TwoRec(hars.at(0), hars.at(1), pows.at(0), pows.at(1), ptbin, qpoi, qref, qol); int harlast = hars.at(hars.size() - 1); int powlast = pows.at(pows.size() - 1); @@ -141,7 +166,7 @@ complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumu for (int i = harSize - 1; i >= 0; i--) { // checking if current configuration is a permutation of the next one. // Need to have more than 2 harmonics though, otherwise it doesn't make sense. - if (i > 2) { // only makes sense when we have more than two harmonics remaining + if (i > static_cast(TwoParticleOrder)) { // only makes sense when we have more than two harmonics remaining if (hars.at(i) == hars.at(i - 1) && pows.at(i) == pows.at(i - 1)) { // if it is a permutation, then increase degeneracy and continue; lDegeneracy++; continue; @@ -181,30 +206,28 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) // Then make sure we don't have any double-spaces: while (s_index(config, " ") > -1) s_replace_all(config, " ", " "); - vector regs; - vector hars; int sz1 = 0; int szend = 0; string ts, ts2; CorrConfig ReturnConfig; // Fetch region descriptor if (!s_tokenize(config, ts, szend, "{")) { - printf("Could not find any harmonics!\n"); + LOGF(warning, "Could not find any harmonics!"); return ReturnConfig; } szend = 0; int counter = 0; while (s_tokenize(config, ts, szend, "{")) { counter++; - ReturnConfig.Regs.push_back(vector{}); - ReturnConfig.Hars.push_back(vector{}); + ReturnConfig.Regs.push_back(std::vector{}); + ReturnConfig.Hars.push_back(std::vector{}); ReturnConfig.Overlap.push_back(-1); // initially, assume no overlap // Check if there's a particular pT bin I should be using here. If so, store it (otherwise, it's bin 0) int ptbin = -1; int sz2 = 0; if (s_contains(ts, "(")) { if (!s_contains(ts, ")")) { - printf("Missing \")\" in the configurator. Returning...\n"); + LOGF(warning, "Missing \")\" in the configurator. Returning..."); return ReturnConfig; } sz2 = s_index(ts, "("); @@ -231,7 +254,7 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) if (ts2 == " " || ts2 == "") continue; if (ind < 0) { - printf("Could not find region named %s!\n", ts2.c_str()); + LOGF(warning, "Could not find region named %s!", ts2.c_str()); break; } if (!isOverlap) @@ -253,7 +276,7 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) return ReturnConfig; }; -complex GFW::Calculate(int poi, int ref, vector hars, int ptbin) +complex GFW::Calculate(int poi, int ref, std::vector hars, int ptbin) { GFWCumulant* qref = &fCumulants.at(ref); GFWCumulant* qpoi = &fCumulants.at(poi); @@ -305,13 +328,13 @@ complex GFW::Calculate(CorrConfig corconf, int ptbin, bool SetHarmsToZer } return retval; }; -vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) +std::vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) { - vector>> retPair; + std::vector>> retPair; for (int iR = 0; iR < static_cast(incfg.Regs.size()); iR++) { if (static_cast(incfg.Regs[iR].size()) > 1) { - retPair.push_back(make_pair(incfg.Regs[iR][0], vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic - retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics + retPair.push_back(make_pair(incfg.Regs[iR][0], std::vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic + retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics } else { retPair.push_back(make_pair(incfg.Regs[iR][0], incfg.Hars[iR])); // Otherwise, it's only ref with all harmonics } @@ -322,7 +345,7 @@ vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& i }; void GFW::InitializePowerArrays() { - vector>> harSets(static_cast(fRegions.size())); + std::vector>> harSets(static_cast(fRegions.size())); for (const CorrConfig& lConf : fListOfCFGs) { auto HarPerReg = GetHarmonicsSingleConfig(lConf); for (const auto& oneHar : HarPerReg) @@ -332,13 +355,13 @@ void GFW::InitializePowerArrays() for (int i = 0; i < static_cast(harSets.size()); i++) { if (fRegions[i].powsDefined) continue; // Only do if powers have not been externally defined - vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); + std::vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); fRegions[i].Nhar = static_cast(powerArray.size()); fRegions[i].NparVec = powerArray; fRegions[i].powsDefined = true; } }; -complex GFW::Calculate(int poi, vector hars) +complex GFW::Calculate(int poi, std::vector hars) { GFWCumulant* qpoi = &fCumulants.at(poi); return RecursiveCorr(qpoi, qpoi, qpoi, 0, hars); @@ -351,13 +374,14 @@ int GFW::FindRegionByName(const string& refName) return -1; }; // String processing: -int GFW::s_index(string& instr, const string& pattern, const int& spos) +int GFW::s_index(const string& instr, const string& pattern, const int& spos) { - return instr.find(pattern, spos); + const auto position = instr.find(pattern, spos); + return position == string::npos ? -1 : static_cast(position); }; -bool GFW::s_contains(string& instr, const string& pattern) +bool GFW::s_contains(const string& instr, const string& pattern) { - return (s_index(instr, pattern) > -1); + return instr.find(pattern) != string::npos; }; void GFW::s_replace(string& instr, const string& pattern1, const string& pattern2, const int& spos) { @@ -374,7 +398,7 @@ void GFW::s_replace_all(string& instr, const string& pattern1, const string& pat lpos = s_index(instr, pattern1, lpos); } }; -bool GFW::s_tokenize(string& instr, string& subs, int& spos, const string& delim) +bool GFW::s_tokenize(const string& instr, string& subs, int& spos, const string& delim) { if (spos < 0 || spos >= static_cast(instr.size())) { spos = -1; diff --git a/PWGCF/GenericFramework/Core/GFW.h b/PWGCF/GenericFramework/Core/GFW.h index 481f5ce6bff..288ea0ed634 100644 --- a/PWGCF/GenericFramework/Core/GFW.h +++ b/PWGCF/GenericFramework/Core/GFW.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFW.h/.cxx +/// \file GFW.h /// \brief Class steers the initialization and calculation of n-particle correlations. Uses recursive function, all terms are calculated only once. /// \author Emil Gorm Nielsen (ack. V. Vislavicius), NBI, emil.gorm.nielsen@cern.ch @@ -19,7 +19,6 @@ #include "GFWCumulant.h" #include -#include #include #include #include @@ -28,7 +27,8 @@ class GFW { public: struct Region { - int Nhar, NpT; + int Nhar = 0; + int NpT = 0; std::vector NparVec{}; double EtaMin = -999; double EtaMax = -999; @@ -39,7 +39,7 @@ class GFW { return EtaMin < a.EtaMin; }; - void PrintStructure() { printf("%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); } + void PrintStructure(); }; struct CorrConfig { std::vector> Regs{}; @@ -50,13 +50,15 @@ class GFW std::string Head = ""; }; GFW(); + GFW(const GFW&) = delete; + GFW& operator=(const GFW&) = delete; ~GFW(); std::vector fRegions; std::vector fCumulants; void AddRegion(const std::string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask); void AddRegion(const std::string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy - void AddRegion(std::string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same - void AddRegion(std::string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector + void AddRegion(const std::string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same + void AddRegion(const std::string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector int CreateRegions(); void Fill(double eta, int ptin, double phi, double weight, int mask, double secondWeight = -1); void Clear(); @@ -79,10 +81,10 @@ class GFW std::complex Calculate(int poi, int ref, std::vector hars, int ptbin = 0); // For differential, need POI and reference std::complex Calculate(int poi, std::vector hars); // For integrated case // Operations on strings. Equivalent to TString operations, but one to rid of root dependence - int s_index(std::string& instr, const std::string& pattern, const int& spos = 0); - bool s_contains(std::string& instr, const std::string& pattern); + int s_index(const std::string& instr, const std::string& pattern, const int& spos = 0); + bool s_contains(const std::string& instr, const std::string& pattern); void s_replace(std::string& instr, const std::string& pattern1, const std::string& pattern2, const int& spos = 0); void s_replace_all(std::string& instr, const std::string& pattern1, const std::string& pattern2); - bool s_tokenize(std::string& instr, std::string& substr, int& spos, const std::string& delim); + bool s_tokenize(const std::string& instr, std::string& substr, int& spos, const std::string& delim); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFW_H_ diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx index d8c988b111e..5ebd708f38f 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx @@ -9,108 +9,119 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWPowerArray.cxx +/// \brief Compute required powers of Q-vectors for harmonic sets +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWPowerArray.h" +#include + +#include #include -#include +#include #include +#include +#include #include -#include #include -using std::string; -using std::vector; - int GFWPowerArray::getHighestHarmonic(const HarSet& inhar) { // Highest possible harmonic: sum of same-sign harmonics - int maxPos = 0, maxNeg = 0; - for (int val : inhar) + std::int64_t maxPos = 0, maxNeg = 0; + for (const int& val : inhar) { if (val > 0) maxPos += val; else - maxNeg += abs(val); - return maxPos > maxNeg ? maxPos : maxNeg; + maxNeg += std::abs(static_cast(val)); + if (maxPos >= std::numeric_limits::max() || maxNeg >= std::numeric_limits::max()) + throw std::overflow_error("Harmonic sum exceeds the supported range"); + } + return static_cast(std::max(maxPos, maxNeg)); }; -HarSet GFWPowerArray::TrimVec(HarSet hars, int ind) +HarSet GFWPowerArray::trimVec(const HarSet& hars, int ind) { - HarSet retVec = std::move(hars); + HarSet retVec = hars; retVec.erase(retVec.begin() + ind); return retVec; }; -HarSet GFWPowerArray::AddConstant(HarSet hars, int offset) +HarSet GFWPowerArray::addConstant(const HarSet& hars, int offset) { - HarSet retVec = std::move(hars); - for (int& val : retVec) + HarSet retVec = hars; + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (updates each harmonic) val += offset; return retVec; }; -void GFWPowerArray::FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower) +void GFWPowerArray::flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower) { - int nPartLoc = MaxPower - comVec.size() + 1; - for (auto& val : comVec) { - int absVal = abs(val); + int nPartLoc = maxPower - static_cast(comVec.size()) + 1; + for (const int& val : comVec) { + int absVal = std::abs(val); if (masterVector.at(absVal) < nPartLoc) { masterVector.at(absVal) = nPartLoc; } } }; -void GFWPowerArray::RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower) +void GFWPowerArray::recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex) { - HarSet compVec = AddConstant(hars, offset); - FlushVectorToMaster(masterVector, compVec, MaxPower); - for (size_t i = 0; i < hars.size(); i++) - RecursiveFunction(masterVector, TrimVec(hars, i), offset + hars.at(i), MaxPower); - ; + HarSet compVec = addConstant(hars, offset); + flushVectorToMaster(masterVector, compVec, maxPower); + for (std::size_t i = startIndex; i < hars.size(); i++) + recursiveFunction(masterVector, trimVec(hars, static_cast(i)), offset + hars.at(i), maxPower, i); }; -void GFWPowerArray::PrintVector(const HarSet& singleSet) +void GFWPowerArray::printVector(const HarSet& singleSet) { - int vcSize = static_cast(singleSet.size()); - if (!vcSize) - printf("Vector is empty!\n"); - printf("{%i", singleSet[0]); - for (int i = 1; i < vcSize; i++) - printf(", %i", singleSet[i]); - printf("}\n"); + if (singleSet.empty()) { + LOGF(info, "Vector is empty!"); + return; + } + std::string output = "{" + std::to_string(singleSet[0]); + for (std::size_t i = 1; i < singleSet.size(); i++) + output += ", " + std::to_string(singleSet[i]); + output += "}"; + LOGF(info, "%s", output.c_str()); } -HarSet GFWPowerArray::GetPowerArray(const vector& inHarmonics) +HarSet GFWPowerArray::GetPowerArray(const std::vector& inHarmonics) // o2-linter: disable=name/function-variable (preserve existing public API) { // First, find maximum number of particle correlations ( = max power) and maximum (sum of) harmonics - int MaxHar = 0; - int nMaxPart = 0; + int maxHar = 0; + int maxPart = 0; for (const HarSet& singleSet : inHarmonics) { int harSum = getHighestHarmonic(singleSet); - MaxHar = harSum > MaxHar ? harSum : MaxHar; + maxHar = harSum > maxHar ? harSum : maxHar; } - // Make a vector with MaxHar+1 entries (entry 0 for sum=0) - HarSet retVec = HarSet(MaxHar + 1); + // Make a vector with maxHar+1 entries (entry 0 for sum=0) + HarSet retVec = HarSet(maxHar + 1); // Then loop over all combinations and calculate max powers for (const HarSet& singleSet : inHarmonics) { + if (singleSet.size() >= static_cast(std::numeric_limits::max())) + throw std::overflow_error("Harmonic set exceeds the supported particle count"); int lNPart = static_cast(singleSet.size()); // Total number of particles correlated - RecursiveFunction(retVec, singleSet, 0, lNPart); + recursiveFunction(retVec, singleSet, 0, lNPart, 0); // Harmonic sum = 0 is a special case. In principle all 0 cases with non-zero harmonics are captured by the function above, but to calculate normalization, we set all harmonics to 0. This means that sum=0 power is the max number of harmonics/particles being correlated - nMaxPart = (lNPart > nMaxPart) ? lNPart : nMaxPart; + maxPart = (lNPart > maxPart) ? lNPart : maxPart; } // Override the sum=0 power with the number of correlated particles - if (retVec[0] < nMaxPart) - retVec[0] = nMaxPart; + if (retVec[0] < maxPart) + retVec[0] = maxPart; // Need an extra power ( = 0) for all non-zero powers - for (int& val : retVec) + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (increments the required powers) if (val != 0) val++; return retVec; }; -void GFWPowerArray::PowerArrayTest() +void GFWPowerArray::PowerArrayTest() // o2-linter: disable=name/function-variable (preserve existing public API) { - vector AllHars = { + std::vector allHars = { HarSet{2}, HarSet{3}, HarSet{2, 2}, HarSet{3, 3}}; - printf("Input harmonics are:\n"); - for (const HarSet& inSet : AllHars) - PrintVector(inSet); - printf("The configuration of powers must then be:\n"); - auto vc = GetPowerArray(AllHars); - PrintVector(vc); + LOGF(info, "Input harmonics are:"); + for (const HarSet& inSet : allHars) + printVector(inSet); + LOGF(info, "The configuration of powers must then be:"); + auto vc = GetPowerArray(allHars); + printVector(vc); }; diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.h b/PWGCF/GenericFramework/Core/GFWPowerArray.h index 7f83a72a3b7..cce3945d3f5 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.h +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.h @@ -9,29 +9,29 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFWPowerArray.h/.cxx +/// \file GFWPowerArray.h /// \brief Class to compute necessary powers of Q-vectors based on input correlations /// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch #ifndef PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ #define PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ -#include +#include #include typedef std::vector HarSet; class GFWPowerArray { public: - static HarSet GetPowerArray(const std::vector& inHarmonics); - static void PowerArrayTest(); + static HarSet GetPowerArray(const std::vector& inHarmonics); // o2-linter: disable=name/function-variable (preserve existing public API) + static void PowerArrayTest(); // o2-linter: disable=name/function-variable (preserve existing public API) private: static int getHighestHarmonic(const HarSet& inhar); - static HarSet TrimVec(HarSet hars, int ind); - static HarSet AddConstant(HarSet hars, int offset); - static void FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower); - static void RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower); - static void PrintVector(const HarSet& singleSet); + static HarSet trimVec(const HarSet& hars, int ind); + static HarSet addConstant(const HarSet& hars, int offset); + static void flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower); + static void recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex); + static void printVector(const HarSet& singleSet); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ diff --git a/PWGCF/GenericFramework/Core/GFWWeights.cxx b/PWGCF/GenericFramework/Core/GFWWeights.cxx index 7373995c3e2..b707e252764 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.cxx +++ b/PWGCF/GenericFramework/Core/GFWWeights.cxx @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWWeights.cxx +/// \brief Implementation of corrections for the Generic Framework +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWWeights.h" #include @@ -25,7 +29,49 @@ #include +#include #include +#include + +namespace +{ +constexpr int MCGenType = 2; + +TObjArray* cloneArray(TObjArray* source) +{ + if (!source) + return 0; + TObjArray* result = new TObjArray(); + result->SetName(source->GetName()); + result->SetOwner(kTRUE); + for (int i = 0; i < source->GetEntries(); i++) { + if (!source->At(i)) + continue; + TH3D* hist = reinterpret_cast(source->At(i)->Clone()); + hist->SetDirectory(0); + result->AddAt(hist, i); + } + return result; +} + +TH3D* cloneTH3D(TH3D* source) +{ + if (!source) + return 0; + TH3D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} + +TH1D* cloneTH1D(TH1D* source) +{ + if (!source) + return 0; + TH1D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} +} // namespace GFWWeights::GFWWeights() : TNamed("", ""), fDataFilled(kFALSE), @@ -49,6 +95,42 @@ GFWWeights::GFWWeights(const char* name) : TNamed(name, name), fAccInt(0), fNbinsPt(0), fbinsPt(0) {} +GFWWeights::GFWWeights(const GFWWeights& other) : TNamed(other), + fDataFilled(other.fDataFilled), + fMCFilled(other.fMCFilled), + fW_data(cloneArray(other.fW_data)), + fW_mcrec(cloneArray(other.fW_mcrec)), + fW_mcgen(cloneArray(other.fW_mcgen)), + fEffInt(cloneTH3D(other.fEffInt)), + fIntEff(cloneTH1D(other.fIntEff)), + fAccInt(cloneTH3D(other.fAccInt)), + fNbinsPt(other.fNbinsPt), + fbinsPt(0) +{ + if (other.fbinsPt) { + fbinsPt = new double[fNbinsPt + 1]; + for (int i = 0; i <= fNbinsPt; i++) + fbinsPt[i] = other.fbinsPt[i]; + } +} +GFWWeights& GFWWeights::operator=(const GFWWeights& other) +{ + if (this == &other) + return *this; + GFWWeights copy(other); + TNamed::operator=(other); + std::swap(fDataFilled, copy.fDataFilled); + std::swap(fMCFilled, copy.fMCFilled); + std::swap(fW_data, copy.fW_data); + std::swap(fW_mcrec, copy.fW_mcrec); + std::swap(fW_mcgen, copy.fW_mcgen); + std::swap(fEffInt, copy.fEffInt); + std::swap(fIntEff, copy.fIntEff); + std::swap(fAccInt, copy.fAccInt); + std::swap(fNbinsPt, copy.fNbinsPt); + std::swap(fbinsPt, copy.fbinsPt); + return *this; +} GFWWeights::~GFWWeights() { delete fW_data; @@ -78,14 +160,25 @@ void GFWWeights::init(bool AddData, bool AddMC) fbinsPt[1] = 1e6; } if (AddData) { + delete fAccInt; + fAccInt = 0; + delete fW_data; fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); const char* tnd = getBinName(0, 0, Form("data_%s", this->GetName())); - fW_data->Add(new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); + TH3D* hist = new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + fW_data->Add(hist); fDataFilled = kTRUE; } if (AddMC) { + delete fEffInt; + delete fIntEff; + fEffInt = 0; + fIntEff = 0; + delete fW_mcrec; + delete fW_mcgen; fW_mcrec = new TObjArray(); fW_mcrec->SetName("GFWWeights_MCRec"); fW_mcgen = new TObjArray(); @@ -94,8 +187,12 @@ void GFWWeights::init(bool AddData, bool AddMC) fW_mcgen->SetOwner(kTRUE); const char* tnr = getBinName(0, 0, "mcrec"); // all integrated over cent. anyway const char* tng = getBinName(0, 0, "mcgen"); // all integrated over cent. anyway - fW_mcrec->Add(new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); - fW_mcgen->Add(new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); + TH3D* rec = new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + TH3D* gen = new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + rec->SetDirectory(0); + gen->SetDirectory(0); + fW_mcrec->Add(rec); + fW_mcgen->Add(gen); reinterpret_cast(fW_mcrec->At(fW_mcrec->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); reinterpret_cast(fW_mcgen->At(fW_mcgen->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); fMCFilled = kTRUE; @@ -114,7 +211,7 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -122,8 +219,11 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen return; TH3D* th3 = reinterpret_cast(tar->FindObject(getBinName(0, 0, pf))); // pT bin 0, V0M bin 0, since all integrated if (!th3) { - if (!htype) - tar->Add(new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); // 0,0 since all integrated + if (!htype) { + TH3D* hist = new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + tar->Add(hist); + } th3 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); } th3->Fill(htype ? pt : phi, eta, vz, weight); @@ -134,13 +234,13 @@ double GFWWeights::getWeight(double phi, double eta, double vz, double pt, doubl const char* pf = ""; if (htype == 0) { tar = fW_data; - pf = "data"; + pf = Form("data_%s", this->GetName()); } if (htype == 1) { tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -161,6 +261,8 @@ double GFWWeights::getNUA(double phi, double eta, double vz) { if (!fAccInt) createNUA(); + if (!fAccInt) + return 1; int xind = fAccInt->GetXaxis()->FindBin(phi); int etaind = fAccInt->GetYaxis()->FindBin(eta); int vzind = fAccInt->GetZaxis()->FindBin(vz); @@ -173,6 +275,8 @@ double GFWWeights::getNUE(double pt, double eta, double vz) { if (!fEffInt) createNUE(); + if (!fEffInt) + return 1; int xind = fEffInt->GetXaxis()->FindBin(pt); int etaind = fEffInt->GetYaxis()->FindBin(eta); int vzind = fEffInt->GetZaxis()->FindBin(vz); @@ -212,7 +316,7 @@ void GFWWeights::mcToEfficiency() }; void GFWWeights::rebinNUA(int nX, int nY, int nZ) { - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; for (int i = 0; i < fW_data->GetEntries(); i++) { reinterpret_cast(fW_data->At(i))->RebinX(nX); @@ -227,44 +331,47 @@ void GFWWeights::createNUA(bool IntegrateOverCentAndPt) return; } TH1D* h1; - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; - if (IntegrateOverCentAndPt) { - if (fAccInt) - delete fAccInt; - fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); - fAccInt->Sumw2(); - for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { - fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt) + delete fAccInt; + fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); + fAccInt->SetDirectory(0); + fAccInt->Sumw2(); + for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { + fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt->Integral() < 1) + continue; + for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { + fAccInt->GetZaxis()->SetRange(vzi, vzi); if (fAccInt->Integral() < 1) continue; - for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { - fAccInt->GetZaxis()->SetRange(vzi, vzi); - if (fAccInt->Integral() < 1) - continue; - h1 = reinterpret_cast(fAccInt->Project3D("x")); - double maxv = h1->GetMaximum(); - for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { - fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); - fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); - } - delete h1; + h1 = reinterpret_cast(fAccInt->Project3D("x")); + double maxv = h1->GetMaximum(); + for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { + fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); + fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); } - fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); + delete h1; } - fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); - return; + fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); } + fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); }; TH1D* GFWWeights::getdNdPhi() { + if (!fW_data || !fW_data->GetEntries()) + return 0; TH3D* temph = reinterpret_cast(fW_data->At(0)->Clone("tempH3")); TH1D* reth = reinterpret_cast(temph->Project3D("x")); reth->SetName("RetHist"); + reth->SetDirectory(0); delete temph; double max = reth->GetMaximum(); - if (max == 0) + if (max == 0) { + delete reth; return 0; + } for (int phi = 1; phi <= reth->GetNbinsX(); phi++) { if (reth->GetBinContent(phi) == 0) continue; @@ -281,32 +388,40 @@ void GFWWeights::createNUE(bool IntegrateOverCentrality) } TH3D* num = 0; TH3D* den = 0; - if (fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) + if (!fW_mcrec || !fW_mcgen || fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) return; - if (IntegrateOverCentrality) { - num = reinterpret_cast(fW_mcrec->At(0)); - den = reinterpret_cast(fW_mcgen->At(0)); - num->Sumw2(); - den->Sumw2(); - num->RebinY(2); - den->RebinY(2); - num->RebinZ(5); - den->RebinZ(5); - fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); - fEffInt->Divide(den); - return; - } + num = reinterpret_cast(fW_mcrec->At(0)); + den = reinterpret_cast(fW_mcgen->At(0)); + num->Sumw2(); + den->Sumw2(); + num->RebinY(2); + den->RebinY(2); + num->RebinZ(5); + den->RebinZ(5); + delete fEffInt; + fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); + fEffInt->SetDirectory(0); + fEffInt->Divide(den); }; void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, bool addData, bool addRec, bool addGen) { FILE* flist = fopen(filelinks.Data(), "r"); + if (!flist) { + LOGF(warning, "Could not open file list %s!\n", filelinks.Data()); + return; + } char str[150]; int nFiles = 0; - while (fscanf(flist, "%s\n", str) == 1) + while (fscanf(flist, "%149s", str) == 1) nFiles++; - rewind(flist); + if (fseek(flist, 0, SEEK_SET) != 0) { + LOGF(warning, "Could not rewind file list %s!\n", filelinks.Data()); + fclose(flist); + return; + } if (nFiles == 0) { LOGF(info, "No files to read!\n"); + fclose(flist); return; } if (!fW_data && addData) { @@ -326,19 +441,31 @@ void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, } TFile* tf = 0; for (int i = 0; i < nFiles; i++) { - auto retVal = fscanf(flist, "%s\n", str); - (void)retVal; + if (fscanf(flist, "%149s", str) != 1) + break; tf = new TFile(str, "READ"); if (tf->IsZombie()) { LOGF(warning, "Could not open file %s!\n", str); tf->Close(); + delete tf; + continue; + } + TObject* obj = tf->Get(listName.Data()); + TList* tl = dynamic_cast(obj); + if (!tl) { + LOGF(warning, "Could not fetch list %s from %s\n", listName.Data(), str); + delete obj; + tf->Close(); + delete tf; continue; } - TList* tl = reinterpret_cast(tf->Get(listName.Data())); - GFWWeights* tw = reinterpret_cast(tl->FindObject(this->GetName())); + GFWWeights* tw = dynamic_cast(tl->FindObject(this->GetName())); if (!tw) { LOGF(warning, "Could not fetch weights object from %s\n", str); + tl->Delete(); + delete tl; tf->Close(); + delete tf; continue; } if (addData) @@ -347,9 +474,14 @@ void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, addArray(fW_mcrec, tw->getRecArray()); if (addGen) addArray(fW_mcgen, tw->getGenArray()); - tf->Close(); + tl->Remove(tw); delete tw; + tl->Delete(); + delete tl; + tf->Close(); + delete tf; } + fclose(flist); }; void GFWWeights::addArray(TObjArray* targ, TObjArray* sour) { @@ -373,15 +505,20 @@ void GFWWeights::overwriteNUA() { if (!fAccInt) createNUA(); + if (!fAccInt || !fW_data || !fW_data->GetEntries()) + return; TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(fAccInt->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(fAccInt->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); delete fAccInt; + fAccInt = 0; } -Long64_t GFWWeights::Merge(TCollection* collist) +Long64_t GFWWeights::Merge(TCollection* collist) // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) { - Long64_t nmerged = 0; + Long64_t nmerged = 0; // o2-linter: disable=root/entity (matches ROOT Merge return type) if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("Weights_Data"); @@ -397,12 +534,12 @@ Long64_t GFWWeights::Merge(TCollection* collist) fW_mcgen->SetName("Weights_MCGen"); fW_mcgen->SetOwner(kTRUE); } - GFWWeights* l_w = 0; - TIter all_w(collist); - while ((l_w = (reinterpret_cast(all_w())))) { - addArray(fW_data, l_w->getDataArray()); - addArray(fW_mcrec, l_w->getRecArray()); - addArray(fW_mcgen, l_w->getGenArray()); + GFWWeights* weight = 0; + TIter allWeights(collist); + while ((weight = (reinterpret_cast(allWeights())))) { + addArray(fW_data, weight->getDataArray()); + addArray(fW_mcrec, weight->getRecArray()); + addArray(fW_mcgen, weight->getGenArray()); nmerged++; } return nmerged; @@ -433,8 +570,10 @@ TH1D* GFWWeights::getIntegratedEfficiencyHist() den->Add(reinterpret_cast(fW_mcgen->At(i))); TH1D* num1d = reinterpret_cast(num->Project3D("x")); num1d->SetName("retHist"); + num1d->SetDirectory(0); num1d->Sumw2(); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + den1d->SetDirectory(0); den1d->Sumw2(); num1d->Divide(den1d); delete num; @@ -462,6 +601,8 @@ double GFWWeights::getIntegratedEfficiency(double pt) } TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, double vzmax) { + if (!fW_mcrec || !fW_mcgen || !fW_mcrec->GetEntries() || !fW_mcgen->GetEntries()) + return 0; TH3D* num = reinterpret_cast(fW_mcrec->At(0)->Clone("Numerator")); for (int i = 1; i < fW_mcrec->GetEntries(); i++) num->Add(reinterpret_cast(fW_mcrec->At(i))); @@ -478,6 +619,8 @@ TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, doub den->GetZaxis()->SetRange(vz1, vz2); TH1D* num1d = reinterpret_cast(num->Project3D("x")); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + num1d->SetDirectory(0); + den1d->SetDirectory(0); delete num; delete den; num1d->Sumw2(); @@ -498,15 +641,21 @@ void GFWWeights::mergeWeights(GFWWeights* other) } void GFWWeights::setTH3D(TH3D* th3d) { + if (!th3d) + return; if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); - fW_data->Add(th3d); + TH3D* hist = reinterpret_cast(th3d->Clone()); + hist->SetDirectory(0); + fW_data->Add(hist); return; } TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(th3d->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(th3d->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); } diff --git a/PWGCF/GenericFramework/Core/GFWWeights.h b/PWGCF/GenericFramework/Core/GFWWeights.h index bcec7463647..bf255a731e5 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.h +++ b/PWGCF/GenericFramework/Core/GFWWeights.h @@ -31,6 +31,8 @@ class GFWWeights : public TNamed public: GFWWeights(); explicit GFWWeights(const char* name); + GFWWeights(const GFWWeights& other); + GFWWeights& operator=(const GFWWeights& other); ~GFWWeights(); void init(bool AddData = kTRUE, bool AddM = kTRUE); void fill(double phi, double eta, double vz, double pt, double cent, int htype, double weight = 1); // htype: 0 for data, 1 for mc rec, 2 for mc gen @@ -53,25 +55,25 @@ class GFWWeights : public TNamed void setMCFilled(bool newval) { fMCFilled = newval; } void readAndMerge(const TString& filelinks, const TString& listName = "OutputList", bool addData = kTRUE, bool addRec = kTRUE, bool addGen = kTRUE); void setPtBins(int Nbins, double* bins); - Long64_t Merge(TCollection* collist); + Long64_t Merge(TCollection* collist); // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) void rebinNUA(int nX = 1, int nY = 2, int nZ = 5); void overwriteNUA(); TH1D* getdNdPhi(); TH1D* getEfficiency(double etamin, double etamax, double vzmin, double vzmax); void mergeWeights(GFWWeights* other); - void setTH3D(TH3D* th3d); + void setTH3D(TH3D* th3d); // Stores a clone of the input histogram private: bool fDataFilled; bool fMCFilled; - TObjArray* fW_data; - TObjArray* fW_mcrec; - TObjArray* fW_mcgen; - TH3D* fEffInt; //! - TH1D* fIntEff; //! - TH3D* fAccInt; //! - int fNbinsPt; //! do not store - double* fbinsPt; //! do not store + TObjArray* fW_data; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcrec; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcgen; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TH3D* fEffInt; //! + TH1D* fIntEff; //! + TH3D* fAccInt; //! + int fNbinsPt; //! do not store + double* fbinsPt; //! do not store void addArray(TObjArray* targ, TObjArray* sour); const char* getBinName(double /*ptv*/, double /*v0mv*/, const char* pf = "") { From 923b0fc7a0387de6df543da7b3186e6bbf32deb5 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:33:46 +0200 Subject: [PATCH 233/648] [PWGLF] Improve ZDC calibration for spectator plane (#18227) --- .../Common/zdc2stagecalibration.cxx | 150 +++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 95bf7d01141..97bbf49fa4b 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -113,6 +113,18 @@ struct zdc2stagecalibration { // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N. static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents; + // Structured regression diagnostic basis. The existing 35 cubic features are + // kept unchanged and augmented with truncated-cubic 1D basis functions for + // time, Vx, Vy and Vz. The additional basis is intentionally overcomplete so + // that the final subset and regularisation can be chosen offline without + // another derivation pass. + static constexpr int kQRecenteringStructuredNKnots = 9; + static constexpr int kQRecenteringStructuredNFeatures = kQRecenteringMultidimNFeatures + 4 * kQRecenteringStructuredNKnots; + static constexpr int kQRecenteringStructuredNMatrixMoments = kQRecenteringStructuredNFeatures * (kQRecenteringStructuredNFeatures + 1) / 2; + static constexpr int kQRecenteringStructuredNMoments = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + kQRecenteringNComponents; + // Per conditioning bin store sum(features), sum(Q) and sum(Q^2). + static constexpr int kQRecenteringStructuredFlatnessNMoments = kQRecenteringStructuredNFeatures + 2 * kQRecenteringNComponents; + void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -133,9 +145,11 @@ struct zdc2stagecalibration { AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments, 0.0, static_cast(kQRecenteringMultidimNMoments), "multidimensional regression moment"}; AxisSpec qRecenteringFlatnessMomentAxis = {kQRecenteringFlatnessNMoments, 0.0, static_cast(kQRecenteringFlatnessNMoments), "flatness sufficient-statistic index"}; + AxisSpec qRecenteringStructuredMomentAxis = {kQRecenteringStructuredNMoments, 0.0, static_cast(kQRecenteringStructuredNMoments), "structured regression moment"}; + AxisSpec qRecenteringStructuredFlatnessMomentAxis = {kQRecenteringStructuredFlatnessNMoments, 0.0, static_cast(kQRecenteringStructuredFlatnessNMoments), "structured flatness sufficient-statistic index"}; AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; AxisSpec resolutionCentralityAxis = {50, 0.0, 50.0, "centrality (%)"}; - AxisSpec vertexXYAxis = {100, -0.5, 0.5, "vertex x/y (cm)"}; + AxisSpec vertexXYAxis = {100, -0.1, 0.1, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; AxisSpec qCorrelationComponentAxis = {4, 0.0, 4.0, "A-C correlation component"}; @@ -213,6 +227,19 @@ struct zdc2stagecalibration { histos.add("QRecenteringCalibration/hFlatnessMomentsVx", "Flatness sufficient statistics vs v_{x};centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); histos.add("QRecenteringCalibration/hFlatnessMomentsVy", "Flatness sufficient statistics vs v_{y};centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); histos.add("QRecenteringCalibration/hFlatnessMomentsVz", "Flatness sufficient statistics vs v_{z};centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringFlatnessMomentAxis}); + + // Additional train/validation sufficient statistics for the structured + // regression test. These do not modify the current 35-feature correction. + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredTrain", "Structured Q recentering moments (train);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredValidation", "Structured Q recentering moments (validation);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain", "Structured flatness statistics vs time (train);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain", "Structured flatness statistics vs v_{x} (train);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain", "Structured flatness statistics vs v_{y} (train);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain", "Structured flatness statistics vs v_{z} (train);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation", "Structured flatness statistics vs time (validation);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation", "Structured flatness statistics vs v_{x} (validation);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation", "Structured flatness statistics vs v_{y} (validation);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation", "Structured flatness statistics vs v_{z} (validation);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); } histos.add("QRecenteringQA/pQBeforeVsCentrality", "Q before recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); @@ -374,6 +401,42 @@ struct zdc2stagecalibration { t * x * y, t * x * z, t * y * z, x * y * z}; } + std::array makeQRecenteringStructuredFeatures(float timeFromSOR, float vx, float vy, float vz) const + { + const auto baseFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + std::array features{}; + + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + features[i] = baseFeatures[i]; + } + + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + + // Internal knots in the normalized [-1,1] coordinate. Together with the + // existing 1,u,u^2,u^3 terms this is a cubic regression-spline basis. + constexpr std::array knots = { + -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; + + int index = kQRecenteringMultidimNFeatures; + auto appendSplineTerms = [&](double value) { + for (int i = 0; i < kQRecenteringStructuredNKnots; ++i) { + const double delta = value - knots[i]; + features[index++] = delta > 0.0 ? delta * delta * delta : 0.0; + } + }; + + appendSplineTerms(t); + appendSplineTerms(x); + appendSplineTerms(y); + appendSplineTerms(z); + + return features; + } + using MyCollisions = o2::soa::Join; void process(MyCollisions::iterator const& collision, o2::aod::FT0s const& /*ft0s*/, o2::aod::FV0As const& /*fv0s*/, BCsRun3 const& /*bcs*/, o2::aod::Zdcs const& /*zdcs*/) @@ -769,6 +832,91 @@ struct zdc2stagecalibration { histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, index + 0.5, value); histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, index + 0.5, value); } + + // Structured regression moments. Events are deterministically split + // 50/50 using a mixed global-BC key, avoiding any dependence on the + // regression variables themselves. + const auto structuredFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + uint64_t splitKey = globalBC + 0x9e3779b97f4a7c15ULL; + splitKey = (splitKey ^ (splitKey >> 30)) * 0xbf58476d1ce4e5b9ULL; + splitKey = (splitKey ^ (splitKey >> 27)) * 0x94d049bb133111ebULL; + splitKey = splitKey ^ (splitKey >> 31); + const bool isValidation = (splitKey & 1ULL) != 0ULL; + + int structuredMoment = 0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + for (int j = i; j < kQRecenteringStructuredNFeatures; ++j) { + const double value = structuredFeatures[i] * structuredFeatures[j]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, structuredMoment + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, structuredMoment + 0.5, value); + } + ++structuredMoment; + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const int index = kQRecenteringStructuredNMatrixMoments + component * kQRecenteringStructuredNFeatures + i; + const double value = structuredFeatures[i] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, index + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, index + 0.5, value); + } + } + + const int q2Index = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + component; + const double q2Value = qValues[component] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, q2Index + 0.5, q2Value); + } + } + + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double value = structuredFeatures[i]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, i + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, i + 0.5, value); + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int qIndex = kQRecenteringStructuredNFeatures + component; + const int q2Index = kQRecenteringStructuredNFeatures + kQRecenteringNComponents + component; + const double qValue = qValues[component]; + const double q2Value = qValue * qValue; + + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, q2Index + 0.5, q2Value); + } + } } } From bfc99d6172b0f0e32ba26cdd321bb80b7626aaa7 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Fri, 9 Oct 2026 02:35:54 +0530 Subject: [PATCH 234/648] [PWGLF] Updated histogram labels and axis for Kaon and Phi (#18217) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 1967b6090c8..31ac540c95a 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -366,15 +366,15 @@ struct Kstarqa { hShift.add("h2D_dpz_vs_pz_KaonFromPhi", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); hShift.add("h2D_dpt_vs_pt_KaonFromPhi", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); - hShift.add("h2D_dpx_vs_px_AllKaons", "Kaon from Phi: deta vs eta", kTH2F, {{subAxis}, {etaAxis}}); - hShift.add("h2D_dpy_vs_py_AllKaons", "Kaon from Phi: dphi vs phi", kTH2F, {{subAxis}, {phiAxis}}); - hShift.add("h2D_dpz_vs_pz_AllKaons", "Kaon from Phi: dR vs eta", kTH2F, {{subAxis}, {etaAxis}}); - hShift.add("h2D_dpt_vs_pt_AllKaons", "Kaon from Phi: dR vs phi", kTH2F, {{subAxis}, {phiAxis}}); - - hShift.add("h2D_dpx_vs_px_Phi", "Pion from Phi: deta vs eta", kTH2F, {{subAxis}, {etaAxis}}); - hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dphi vs phi", kTH2F, {{subAxis}, {phiAxis}}); - hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dR vs eta", kTH2F, {{subAxis}, {etaAxis}}); - hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dR vs phi", kTH2F, {{subAxis}, {phiAxis}}); + hShift.add("h2D_dpx_vs_px_AllKaons", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_AllKaons", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_AllKaons", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_AllKaons", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_Phi", "Pion from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); } // Signal Loss & Event Loss in Light Ion Collisions From 327a45830514c32939720eda1eff05f0298ca45c Mon Sep 17 00:00:00 2001 From: Subhadeep Roy Date: Fri, 9 Oct 2026 03:57:05 +0530 Subject: [PATCH 235/648] [PWGCF] Updated the lambdaSpinPolarization.cxx task with new configurations related to the Mixed events and derived-tables (#18228) --- .../Tasks/lambdaSpinPolarization.cxx | 432 +++++++++++++----- 1 file changed, 315 insertions(+), 117 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index 67a58ab98ec..a0d3ddcd6fd 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -64,6 +64,7 @@ #include #include #include +#include #include using namespace o2; @@ -132,15 +133,10 @@ using LambdaTrack = LambdaTracks::iterator; namespace lambdatrackext { DECLARE_SOA_COLUMN(LambdaSharingDaughter, lambdaSharingDaughter, bool); -DECLARE_SOA_COLUMN(LambdaSharingDauIds, lambdaSharingDauIds, - std::vector); -DECLARE_SOA_COLUMN(TrueLambdaFlag, trueLambdaFlag, bool); } // namespace lambdatrackext DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", - lambdatrackext::LambdaSharingDaughter, - lambdatrackext::LambdaSharingDauIds, - lambdatrackext::TrueLambdaFlag); + lambdatrackext::LambdaSharingDaughter); using LambdaTrackExt = LambdaTracksExt::iterator; namespace lambdamcgentrack @@ -183,6 +179,8 @@ namespace lambdamixeventtracks DECLARE_SOA_COLUMN(LambdaMixEventCollisionIdx, lambdaMixEventCollisionIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTrackIdx, lambdaMixEventTrackIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTimeStamp, lambdaMixEventTimeStamp, uint64_t); +DECLARE_SOA_COLUMN(LambdaMixEventPosTrackIdx, lambdaMixEventPosTrackIdx, int); +DECLARE_SOA_COLUMN(LambdaMixEventNegTrackIdx, lambdaMixEventNegTrackIdx, int); } // namespace lambdamixeventtracks DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", @@ -192,7 +190,9 @@ DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", lambdatrack::Py, lambdatrack::Pz, lambdatrack::Mass, lambdatrack::PrPx, lambdatrack::PrPy, lambdatrack::PrPz, lambdatrack::V0Type, - lambdamixeventtracks::LambdaMixEventTimeStamp); + lambdamixeventtracks::LambdaMixEventTimeStamp, + lambdamixeventtracks::LambdaMixEventPosTrackIdx, + lambdamixeventtracks::LambdaMixEventNegTrackIdx); using LambdaMixEventTrack = LambdaMixEventTracks::iterator; namespace lambdamixeventmcgentracks @@ -276,7 +276,13 @@ enum PairTier { kSameEvent = 0, kMixedEvent, kMcGenSameEvent, kMcGenSameEventInMixing, - kMcGenMixedEvent }; + kMcGenMixedEvent, + kSameEventSharedDau, + kSameEventInMixingSharedDau }; + +enum SharedDauRule { kSharedDauMaxSet = 0, + kSharedDauPairVeto, + kSharedDauDropAll }; enum PairEventCount { kPeAll = 1, kPeOneCand, @@ -1240,14 +1246,6 @@ struct LambdaTracksExtProducer { Produces lambdaTrackExtTable; - Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all lambda (ignore sharing)"}; - Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject lambda sharing daughters"}; - Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, "Select lambda closest to PDG mass"}; - Configurable cSelLambdaTScore{"cSelLambdaTScore", false, "Select lambda by t-score"}; - Configurable cA{"cA", 0.6, "t-score weight: |mass - PDGmass|"}; - Configurable cB{"cB", 0.6, "t-score weight: DCA daughters"}; - Configurable cC{"cC", 0.6, "t-score weight: |cosPA - 1|"}; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) @@ -1261,8 +1259,6 @@ struct LambdaTracksExtProducer { histos.add("h1i_totlambda_mult", "Multiplicity", kTH1I, {axisMult}); histos.add("h1i_totantilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_lambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_antilambda_mult", "Multiplicity", kTH1I, {axisMult}); histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", kTH2D, {axisDEta, axisDPhi}); histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", kTH2D, {axisDEta, axisDPhi}); @@ -1294,13 +1290,10 @@ struct LambdaTracksExtProducer { void process(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) { - int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; + int nTotLambda = 0, nTotAntiLambda = 0; for (auto const& lambda : tracks) { - bool lambdaMinDeltaMassFlag = true, lambdaMinTScoreFlag = true; - bool lambdaSharingDauFlag = false, trueLambdaFlag = false; - std::vector vSharedDauLambdaIndex; - float tLambda = 0., tTrack = 0.; + bool lambdaSharingDauFlag = false; if (lambda.v0Type() == kLambda) { ++nTotLambda; @@ -1308,15 +1301,12 @@ struct LambdaTracksExtProducer { ++nTotAntiLambda; } - tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); - for (auto const& track : tracks) { if (lambda.index() == track.index()) { continue; } if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { - vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; if (lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) { @@ -1328,15 +1318,6 @@ struct LambdaTracksExtProducer { } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } - - if (std::abs(lambda.mass() - MassLambda0) > std::abs(track.mass() - MassLambda0)) { - lambdaMinDeltaMassFlag = false; - } - - tTrack = (cA * std::abs(track.mass() - MassLambda0)) + (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); - if (tLambda > tTrack) { - lambdaMinTScoreFlag = false; - } } } @@ -1346,19 +1327,7 @@ struct LambdaTracksExtProducer { fillHistos(lambda); } - if (cAcceptAllLambda || (cRejAllLambdaShaDau && !lambdaSharingDauFlag) || (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) || (cSelLambdaTScore && lambdaMinTScoreFlag)) { - trueLambdaFlag = true; - } - - if (trueLambdaFlag) { - if (lambda.v0Type() == kLambda) { - ++nSelLambda; - } else if (lambda.v0Type() == kAntiLambda) { - ++nSelAntiLambda; - } - } - - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag); + lambdaTrackExtTable(lambdaSharingDauFlag); } if (nTotLambda != 0) { @@ -1367,12 +1336,6 @@ struct LambdaTracksExtProducer { if (nTotAntiLambda != 0) { histos.fill(HIST("h1i_totantilambda_mult"), nTotAntiLambda); } - if (nSelLambda != 0) { - histos.fill(HIST("h1i_lambda_mult"), nSelLambda); - } - if (nSelAntiLambda != 0) { - histos.fill(HIST("h1i_antilambda_mult"), nSelAntiLambda); - } } }; @@ -1387,9 +1350,10 @@ struct LambdaSpinPolarization { Configurable cMassHistMin{"cMassHistMin", 1.0806f, "Mass axes min (GeV/c2), below cMassAccMin"}; Configurable cMassHistMax{"cMassHistMax", 1.1806f, "Mass axes max (GeV/c2), above cMassAccMax"}; Configurable cNMassBins{"cNMassBins", 200, "Mass axes N bins"}; - ConfigurableAxis axisMEReplacedMass{"axisMEReplacedMass", {VARIABLE_WIDTH, 1.0956, 1.1031, 1.1081, 1.1231, 1.1281, 1.1356}, "ME: mass of the replaced leg, binned by the analysis windows LSB | gap | Sig | gap | RSB (add edges, e.g. of the 3-sigma windows, to keep more window sets)"}; ConfigurableAxis axisDeltaR{"axisDeltaR", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1, 3.5}, "DeltaR bins (last bin: outside the analysis windows)"}; Configurable cNBinsCosTS{"cNBinsCosTS", 10, "N costheta* bins"}; + Configurable cSharedDauRule{"cSharedDauRule", 0, "Candidates sharing a daughter track: 0 = keep the largest set without shared tracks per cluster (ties: closest to the PDG mass), 1 = veto only the pairs sharing a track, 2 = drop every candidate sharing a track"}; + Configurable cMinCandPerEvent{"cMinCandPerEvent", 2, "Collisions with fewer reconstructed Lambda + AntiLambda candidates are skipped by the pair, mixing and derived-table processes (the event QA counts all collisions)"}; Configurable cMEPoolDepth{"cMEPoolDepth", 100000, "Rolling ME pool: depth in donor events per (centrality, Vz) bin"}; Configurable cMEPoolMinEvents{"cMEPoolMinEvents", 20, "Rolling ME pool: events a pool must hold before its bin is mixed (warm-up)"}; @@ -1418,6 +1382,7 @@ struct LambdaSpinPolarization { Configurable cFillClosePairQA{"cFillClosePairQA", true, "Close-pair QA of the same-event pairs: SE (processDataReco) and SEproc (processDataRecoMixed)"}; Configurable cClosePairMassWindow{"cClosePairMassWindow", 0.004f, "Close-pair QA: both legs within |m - m_Lambda| < this (GeV/c2); <= 0: all pairs"}; ConfigurableAxis axisClosePair{"axisClosePair", {100, -0.15, 0.15}, "Close-pair QA: bins of the daughter Delta eta, Delta y and Delta phi"}; + Configurable cFillSharedDauQA{"cFillSharedDauQA", true, "Shared-daughter QA: SE and SEproc pairs with a candidate sharing a daughter track with another candidate, and the cluster sizes"}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Service ccdb{}; @@ -1425,9 +1390,20 @@ struct LambdaSpinPolarization { uint64_t pairOrderSeed = 0; std::array, 4> kinWeightMaps{}; std::vector legAxesRef{}; - TAxis replacedMassAxis{}; bool kinWeightsLoaded = false; + struct SharedDauCand { + int64_t globalIdx; + int64_t posId; + int64_t negId; + float dMass; + }; + static constexpr std::size_t MaxExactCluster = 16; + static constexpr int NoSharedTrackIdx = -1; + std::vector sdCands; + std::vector sdConflicted; + std::vector sdDropped; + struct PoolTrack { float mPx, mPy, mPz, mPt, mRap, mPhi, mMass; float mPrPx, mPrPy, mPrPz; @@ -1595,9 +1571,7 @@ struct LambdaSpinPolarization { const AxisSpec axisM2(cNMassBins, cMassHistMin, cMassHistMax, "M_{2} (GeV/#it{c}^{2})"); const AxisSpec axisDR(axisDeltaR, "#DeltaR"); const AxisSpec axisCosTS(cNBinsCosTS, -1, 1, "cos(#theta*)"); - const AxisSpec axisReplacedMass(axisMEReplacedMass, "M_{replaced leg} (GeV/#it{c}^{2})"); const std::vector pairAxes = {axisM1, axisM2, axisDR, axisCosTS}; - const std::vector meAxes = {axisM1, axisM2, axisDR, axisCosTS, axisReplacedMass}; auto nBins = [](int configured, float range, float window) { if (configured > 0) { @@ -1608,25 +1582,27 @@ struct LambdaSpinPolarization { const AxisSpec axisLegPt(nBins(cNKinWPtBins, cMaxPt - cMinPt, cMaxDeltaPt), cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); const AxisSpec axisLegRap(nBins(cNKinWRapBins, cMaxRap - cMinRap, cMaxDeltaRap), cMinRap, cMaxRap, "y"); const AxisSpec axisLegPhi(nBins(cNKinWPhiBins, TwoPI, cMaxDeltaPhi), 0., TwoPI, "#varphi (rad)"); - const std::vector legAxes = {axisLegPt, axisLegRap, axisLegPhi, axisReplacedMass}; + const std::vector legAxes = {axisLegPt, axisLegRap, axisLegPhi}; legAxesRef.clear(); for (auto const& spec : legAxes) { const std::vector edges = axisEdges(spec); legAxesRef.emplace_back(static_cast(edges.size()) - 1, edges.data()); } - replacedMassAxis = legAxesRef.back(); if (!(cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value)) { LOGF(fatal, "The mass axes [%.4f, %.4f] must be wider than the accepted mass window [%.4f, %.4f]", cMassHistMin.value, cMassHistMax.value, cMassAccMin.value, cMassAccMax.value); } - std::vector massEdges = axisEdges(axisReplacedMass); - massEdges.push_back(cMassAccMin.value); - massEdges.push_back(cMassAccMax.value); - for (auto const& edge : massEdges) { + for (auto const& edge : {cMassAccMin.value, cMassAccMax.value}) { if (!isOnMassBinEdge(edge)) { - LOGF(warning, "Mass %.5f (accepted window or axisMEReplacedMass) is not a bin edge of the mass axes [%.4f, %.4f] / %d", edge, cMassHistMin.value, cMassHistMax.value, cNMassBins.value); + LOGF(warning, "Accepted mass %.5f is not a bin edge of the mass axes [%.4f, %.4f] / %d", edge, cMassHistMin.value, cMassHistMax.value, cNMassBins.value); } } + if (cSharedDauRule.value < kSharedDauMaxSet || cSharedDauRule.value > kSharedDauDropAll) { + LOGF(fatal, "cSharedDauRule must be %d (largest set without shared tracks), %d (veto pairs sharing a track) or %d (drop every candidate sharing a track)", static_cast(kSharedDauMaxSet), static_cast(kSharedDauPairVeto), static_cast(kSharedDauDropAll)); + } + if (cMinCandPerEvent.value < 1) { + LOGF(fatal, "cMinCandPerEvent must be at least 1, it is %d", cMinCandPerEvent.value); + } if (doprocessDataRecoMixed || doprocessMcGenMixed) { const std::vector centEdges = axisEdges(AxisSpec(axisCentME)); @@ -1665,7 +1641,7 @@ struct LambdaSpinPolarization { hStatus->GetXaxis()->SetBinLabel(kMEEventDonated, "donated to the pool"); histos.add((dir + "hPoolEventsAtMixing").c_str(), "pool size when a collision is mixed;events in the pool;collisions", kTH1F, {axisPoolSize}); histos.add((dir + "hMatchesLeg2").c_str(), "matches per replaced leg 2;N matches;pair type", kTH2F, {{100, -0.5, 99.5}, {4, -0.5, 3.5}}); - histos.add((dir + "hDeltaRShift").c_str(), "ME entries are filed at the #DeltaR of their seed;#DeltaR_{seed};#DeltaR_{mixed pair} - #DeltaR_{seed}", kTH2F, {{35, 0., 3.5}, {100, -0.2, 0.2}}); + histos.add((dir + "hDeltaRShift").c_str(), "ME pairs are filed at their own #DeltaR;#DeltaR_{seed};#DeltaR_{mixed pair} - #DeltaR_{seed}", kTH2F, {{35, 0., 3.5}, {100, -0.2, 0.2}}); histos.add((dir + "hLambdaMultVsCent").c_str(), "ME #Lambda mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); histos.add((dir + "hAntiLambdaMultVsCent").c_str(), "ME #bar{#Lambda} mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); } @@ -1725,7 +1701,7 @@ struct LambdaSpinPolarization { histos.add(("SE/hPair_" + pair).c_str(), ("SE " + pair).c_str(), kTHnSparseF, pairAxes); } if (doprocessDataRecoMixed) { - histos.add(("ME/hPair_" + pair).c_str(), ("ME " + pair).c_str(), kTHnSparseD, meAxes, true); + histos.add(("ME/hPair_" + pair).c_str(), ("ME " + pair).c_str(), kTHnSparseD, pairAxes, true); } if (doprocessDataRecoMixed && cFillSEInMixing) { histos.add(("SEproc/hPair_" + pair).c_str(), ("SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); @@ -1734,16 +1710,25 @@ struct LambdaSpinPolarization { histos.add(("KinW/SEproc/hLeg2_" + pair).c_str(), ("leg 2 of SEproc " + pair).c_str(), kTHnSparseD, legAxes); histos.add(("KinW/ME/hLeg2_" + pair).c_str(), ("candidates replacing leg 2 " + pair).c_str(), kTHnSparseD, legAxes, true); } + if (cFillSharedDauQA && doprocessDataReco) { + histos.add(("QA/SharedDau/SE/hPair_" + pair).c_str(), ("SE pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (cFillSharedDauQA && doprocessDataRecoMixed) { + histos.add(("QA/SharedDau/SEproc/hPair_" + pair).c_str(), ("SEproc pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } if (doprocessMcGen) { histos.add(("McGen/SE/hPair_" + pair).c_str(), ("MC generated SE " + pair).c_str(), kTHnSparseF, pairAxes); } if (doprocessMcGenMixed) { - histos.add(("McGen/ME/hPair_" + pair).c_str(), ("MC generated ME " + pair).c_str(), kTHnSparseD, meAxes, true); + histos.add(("McGen/ME/hPair_" + pair).c_str(), ("MC generated ME " + pair).c_str(), kTHnSparseD, pairAxes, true); } if (doprocessMcGenMixed && cFillSEInMixing) { histos.add(("McGen/SEproc/hPair_" + pair).c_str(), ("MC generated SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); } } + if (cFillSharedDauQA && (doprocessDataReco || doprocessDataRecoMixed)) { + histos.add("QA/SharedDau/hClusterSize", "clusters of candidates sharing daughter tracks;candidates in the cluster;clusters", kTH1F, {{20, 0.5, 20.5}}); + } if (cFillClosePairQA && (doprocessDataReco || doprocessDataRecoMixed)) { const AxisSpec axisCPdEta(axisClosePair, "#Delta#eta"); @@ -1863,10 +1848,11 @@ struct LambdaSpinPolarization { } template - void fillPair(PoolTrack const& p1, PoolTrack const& p2, float dR, float w, float mReplaced = 0.f) + void fillPair(PoolTrack const& p1, PoolTrack const& p2, float dR, float w) { - static constexpr std::array TierDir = {"SE/hPair_", "SEproc/hPair_", "ME/hPair_", - "McGen/SE/hPair_", "McGen/SEproc/hPair_", "McGen/ME/hPair_"}; + static constexpr std::array TierDir = {"SE/hPair_", "SEproc/hPair_", "ME/hPair_", + "McGen/SE/hPair_", "McGen/SEproc/hPair_", "McGen/ME/hPair_", + "QA/SharedDau/SE/hPair_", "QA/SharedDau/SEproc/hPair_"}; static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; std::array l1 = {p1.px(), p1.py(), p1.pz(), p1.mass()}; @@ -1875,25 +1861,15 @@ struct LambdaSpinPolarization { std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; const float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); - if constexpr (tier == kMixedEvent || tier == kMcGenMixedEvent) { - histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, mReplaced, w); - } else { - histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, w); - } + histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, w); } template - void fillLeg2Spectrum(PoolTrack const& leg, float mReplaced, float w) + void fillLeg2Spectrum(PoolTrack const& leg, float w) { static constexpr std::array Dir = {"KinW/SEproc/hLeg2_", "KinW/ME/hLeg2_"}; static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - histos.fill(HIST(Dir[IsME]) + HIST(PairTag[part_pair]), leg.pt(), leg.rap(), leg.phi(), mReplaced, w); - } - - [[nodiscard]] bool isSameWindow(float m1, float m2) const - { - const int b = replacedMassAxis.FindFixBin(m1); - return b >= 1 && b <= replacedMassAxis.GetNbins() && b == replacedMassAxis.FindFixBin(m2); + histos.fill(HIST(Dir[IsME]) + HIST(PairTag[part_pair]), leg.pt(), leg.rap(), leg.phi(), w); } template @@ -1936,15 +1912,15 @@ struct LambdaSpinPolarization { std::abs(RecoDecay::constrainAngle(cand.phi() - leg.phi(), -PI)) < cMaxDeltaPhi.value; } - static constexpr std::size_t NLegAxes = 4; + static constexpr std::size_t NLegAxes = 3; template - float kinWeight(PoolTrack const& cand, float mReplaced) const + float kinWeight(PoolTrack const& cand) const { THnBase const* map = kinWeightMaps[part_pair].get(); if (!map) { return 1.f; } - const std::array x = {cand.pt(), cand.rap(), cand.phi(), mReplaced}; + const std::array x = {cand.pt(), cand.rap(), cand.phi()}; std::array idx{}; for (std::size_t i = 0; i < NLegAxes; ++i) { TAxis const* axis = map->GetAxis(i); @@ -2000,7 +1976,7 @@ struct LambdaSpinPolarization { const std::string name = "hKinW_" + std::string(PairTag[i]); auto* map = dynamic_cast(list->FindObject(name.c_str())); if (!map || !hasLegAxes(*map)) { - LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi, window of the replaced leg) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); + LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); return; } kinWeightMaps[i].reset(static_cast(map->Clone())); @@ -2039,26 +2015,211 @@ struct LambdaSpinPolarization { return firstIsLo != swapped; } + template + static bool sharesDaughter(T1 const& a, T2 const& b) + { + return a.posTrackId() == b.posTrackId() || a.negTrackId() == b.negTrackId(); + } + + [[nodiscard]] bool sharesDaughterCand(std::size_t a, std::size_t b) const + { + return a != b && (sdCands[a].posId == sdCands[b].posId || sdCands[a].negId == sdCands[b].negId); + } + + [[nodiscard]] bool isDropped(int64_t globalIdx) const + { + return std::binary_search(sdDropped.begin(), sdDropped.end(), globalIdx); + } + + [[nodiscard]] bool isConflicted(int64_t globalIdx) const + { + return std::binary_search(sdConflicted.begin(), sdConflicted.end(), globalIdx); + } + + template + [[nodiscard]] bool isKept(T const& trk) const + { + return !Reco || !isDropped(trk.globalIndex()); + } + + template + [[nodiscard]] bool isUsable(T const& trk) const + { + return isAcceptedMass(trk.mass()) && isKept(trk); + } + + template + [[nodiscard]] bool hasMinCandidates(T const& lTrks, T const& alTrks) const + { + return std::cmp_greater_equal(lTrks.size() + alTrks.size(), cMinCandPerEvent.value); + } + + void selectLargestFreeSet(std::vector const& members, std::vector& keep) const + { + const std::size_t s = members.size(); + keep.assign(s, 0); + if (s <= MaxExactCluster) { + std::array conf{}; + for (std::size_t a = 0; a < s; ++a) { + for (std::size_t b = 0; b < s; ++b) { + if (sharesDaughterCand(members[a], members[b])) { + conf[a] |= static_cast(1) << b; + } + } + } + uint32_t best = 0; + int bestN = -1; + float bestScore = 0.f; + const uint32_t nMasks = static_cast(1) << s; + for (uint32_t mask = 1; mask < nMasks; ++mask) { + bool isFree = true; + int nIn = 0; + float score = 0.f; + for (std::size_t a = 0; a < s && isFree; ++a) { + if (((mask >> a) & 1U) == 0U) { + continue; + } + if ((conf[a] & mask) != 0U) { + isFree = false; + continue; + } + ++nIn; + score += sdCands[members[a]].dMass; + } + if (isFree && (nIn > bestN || (nIn == bestN && score < bestScore))) { + best = mask; + bestN = nIn; + bestScore = score; + } + } + for (std::size_t a = 0; a < s; ++a) { + keep[a] = static_cast((best >> a) & 1U); + } + return; + } + std::vector order(s); + for (std::size_t a = 0; a < s; ++a) { + order[a] = a; + } + std::sort(order.begin(), order.end(), [this, &members](std::size_t x, std::size_t y) { + SharedDauCand const& cx = sdCands[members[x]]; + SharedDauCand const& cy = sdCands[members[y]]; + return cx.dMass < cy.dMass || (cx.dMass == cy.dMass && cx.globalIdx < cy.globalIdx); + }); + for (auto const& a : order) { + bool isFree = true; + for (std::size_t b = 0; b < s && isFree; ++b) { + if (keep[b] != 0 && sharesDaughterCand(members[a], members[b])) { + isFree = false; + } + } + if (isFree) { + keep[a] = 1; + } + } + } + + template + void resolveSharedDaughters(T const& lTrks, T const& alTrks, bool fillQA) + { + sdCands.clear(); + sdConflicted.clear(); + sdDropped.clear(); + auto collect = [this](auto const& trks) { + for (auto const& trk : trks) { + if (trk.lambdaSharingDaughter()) { + sdCands.push_back(SharedDauCand{trk.globalIndex(), trk.posTrackId(), trk.negTrackId(), static_cast(std::abs(trk.mass() - MassLambda0))}); + } + } + }; + collect(lTrks); + collect(alTrks); + const std::size_t n = sdCands.size(); + if (n == 0) { + return; + } + std::sort(sdCands.begin(), sdCands.end(), [](SharedDauCand const& a, SharedDauCand const& b) { return a.globalIdx < b.globalIdx; }); + std::vector cluster(n, -1); + std::vector members; + std::vector stack; + std::vector keep; + int nClusters = 0; + for (std::size_t seed = 0; seed < n; ++seed) { + if (cluster[seed] >= 0) { + continue; + } + members.clear(); + stack.assign(1, seed); + cluster[seed] = nClusters; + while (!stack.empty()) { + const std::size_t a = stack.back(); + stack.pop_back(); + members.push_back(a); + for (std::size_t b = 0; b < n; ++b) { + if (cluster[b] < 0 && sharesDaughterCand(a, b)) { + cluster[b] = nClusters; + stack.push_back(b); + } + } + } + ++nClusters; + std::sort(members.begin(), members.end()); + if (fillQA && cFillSharedDauQA) { + histos.fill(HIST("QA/SharedDau/hClusterSize"), members.size()); + } + if (members.size() < NCandidatesForPair) { + continue; + } + for (auto const& a : members) { + sdConflicted.push_back(sdCands[a].globalIdx); + } + if (cSharedDauRule.value == kSharedDauPairVeto) { + continue; + } + if (cSharedDauRule.value == kSharedDauDropAll) { + keep.assign(members.size(), 0); + } else { + selectLargestFreeSet(members, keep); + } + for (std::size_t a = 0; a < members.size(); ++a) { + if (keep[a] == 0) { + sdDropped.push_back(sdCands[members[a]].globalIdx); + } + } + } + std::sort(sdConflicted.begin(), sdConflicted.end()); + std::sort(sdDropped.begin(), sdDropped.end()); + } + template void analyzePairsSE(T const& trks_1, T const& trks_2) { + constexpr bool Reco = tier == kSameEvent; for (auto const& trk_1 : trks_1) { - if (!isAcceptedMass(trk_1.mass())) { + if (!isUsable(trk_1)) { continue; } const PoolTrack p1 = toPoolTrack(trk_1); for (auto const& trk_2 : trks_2) { - if (!isCountedOrder(trk_1, trk_2) || !isAcceptedMass(trk_2.mass())) { + if (!isCountedOrder(trk_1, trk_2) || !isUsable(trk_2)) { continue; } + if constexpr (Reco) { + if (sharesDaughter(trk_1, trk_2)) { + continue; + } + } const PoolTrack p2 = toPoolTrack(trk_2); const float dR = deltaR(p1, p2); fillPair(p1, p2, dR, 1.f); - if constexpr (tier == kSameEvent) { + if constexpr (Reco) { if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { fillClosePairQA(p1, p2, dR); } + if (cFillSharedDauQA && (isConflicted(trk_1.globalIndex()) || isConflicted(trk_2.globalIndex()))) { + fillPair(p1, p2, dR, 1.f); + } } } } @@ -2070,12 +2231,12 @@ struct LambdaSpinPolarization { static constexpr std::array EvDir = {"Events/", "McGen/Events/"}; std::size_t nLambda = 0, nAntiLambda = 0; for (auto const& trk : lTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { ++nLambda; } } for (auto const& trk : alTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { ++nAntiLambda; } } @@ -2099,12 +2260,12 @@ struct LambdaSpinPolarization { histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAntiLambdaAntiLambda); } for (auto const& trk : lTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { fillPairCandQA(trk); } } for (auto const& trk : alTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { fillPairCandQA(trk); } } @@ -2137,15 +2298,20 @@ struct LambdaSpinPolarization { constexpr PairTier MeTier = Gen ? kMcGenMixedEvent : kMixedEvent; std::vector matches; for (auto const& trk1 : se_trks_1) { - if (!isAcceptedMass(trk1.mass())) { + if (!isUsable(trk1)) { continue; } const PoolTrack p1 = toPoolTrack(trk1); for (auto const& trk2 : se_trks_2) { - if (!isCountedOrder(trk1, trk2) || !isAcceptedMass(trk2.mass())) { + if (!isCountedOrder(trk1, trk2) || !isUsable(trk2)) { continue; } + if constexpr (!Gen) { + if (sharesDaughter(trk1, trk2)) { + continue; + } + } const PoolTrack p2 = toPoolTrack(trk2); const float dRSeed = deltaR(p1, p2); @@ -2156,8 +2322,11 @@ struct LambdaSpinPolarization { if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { fillClosePairQA(p1, p2, dRSeed); } + if (cFillSharedDauQA && (isConflicted(trk1.globalIndex()) || isConflicted(trk2.globalIndex()))) { + fillPair(p1, p2, dRSeed, 1.f); + } if (cFillKinWSpectra) { - fillLeg2Spectrum(p2, p2.mass(), 1.f); + fillLeg2Spectrum(p2, 1.f); } } @@ -2166,14 +2335,15 @@ struct LambdaSpinPolarization { for (auto const& meP2 : matches) { float kinW = 1.f; if constexpr (!Gen) { - kinW = kinWeight(*meP2, p2.mass()); + kinW = kinWeight(*meP2); } const float w = kinW / static_cast(matches.size()); - fillPair(p1, *meP2, dRSeed, w, p2.mass()); - histos.fill(HIST(MixQaDir[Gen]) + HIST("hDeltaRShift"), dRSeed, deltaR(p1, *meP2) - dRSeed, w); + const float dRMixed = deltaR(p1, *meP2); + fillPair(p1, *meP2, dRMixed, w); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hDeltaRShift"), dRSeed, dRMixed - dRSeed, w); if constexpr (!Gen) { - if (cFillKinWSpectra && isSameWindow(meP2->mass(), p2.mass())) { - fillLeg2Spectrum(*meP2, p2.mass(), w); + if (cFillKinWSpectra) { + fillLeg2Spectrum(*meP2, w); } } } @@ -2191,17 +2361,28 @@ struct LambdaSpinPolarization { histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventOutsidePools); return; } - histos.fill(HIST(MixQaDir[Gen]) + HIST("hLambdaMultVsCent"), col.cent(), lTrks.size()); - histos.fill(HIST(MixQaDir[Gen]) + HIST("hAntiLambdaMultVsCent"), col.cent(), alTrks.size()); + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isKept(trk)) { + ++nLambda; + } + } + for (auto const& trk : alTrks) { + if (isKept(trk)) { + ++nAntiLambda; + } + } + histos.fill(HIST(MixQaDir[Gen]) + HIST("hLambdaMultVsCent"), col.cent(), nLambda); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hAntiLambdaMultVsCent"), col.cent(), nAntiLambda); eventCands.clear(); for (auto const& trk : lTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { eventCands.push_back(toPoolTrack(trk)); } } for (auto const& trk : alTrks) { - if (isAcceptedMass(trk.mass())) { + if (isUsable(trk)) { eventCands.push_back(toPoolTrack(trk)); } } @@ -2239,9 +2420,9 @@ struct LambdaSpinPolarization { Preslice perCollisionLambda = aod::lambdatrack::lambdaCollisionId; SliceCache cache; - Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); + Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); SliceCache cachemc; @@ -2257,9 +2438,13 @@ struct LambdaSpinPolarization { setPairOrderSeed(collision); auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, !doprocessDataRecoMixed); if (!doprocessDataRecoMixed) { fillEventQA(collision.cent(), lTrks, alTrks); } + if (!hasMinCandidates(lTrks, alTrks)) { + return; + } analyzePairsSE(lTrks, alTrks); analyzePairsSE(alTrks, lTrks); @@ -2274,7 +2459,11 @@ struct LambdaSpinPolarization { for (auto const& col : cols) { auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, true); fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { + continue; + } mixCollision(col, lTrks, alTrks, mePools, eventCands); } } @@ -2288,6 +2477,9 @@ struct LambdaSpinPolarization { if (!doprocessMcGenMixed) { fillEventQA(collision.cent(), lTrks, alTrks); } + if (!hasMinCandidates(lTrks, alTrks)) { + return; + } analyzePairsSE(lTrks, alTrks); analyzePairsSE(alTrks, lTrks); @@ -2303,6 +2495,9 @@ struct LambdaSpinPolarization { auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { + continue; + } mixCollision(col, lTrks, alTrks, mePoolsGen, eventCands); } } @@ -2313,23 +2508,26 @@ struct LambdaSpinPolarization { auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - if (lTrks.size() == 0 && alTrks.size() == 0) { + if (!hasMinCandidates(lTrks, alTrks)) { return; } lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), collision.timeStamp()); - for (auto const& track : lTrks) { + auto fillMixEventTrack = [&](auto const& track) { + const bool shared = track.lambdaSharingDaughter(); lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), track.py(), track.pz(), track.mass(), track.prPx(), track.prPy(), track.prPz(), track.v0Type(), - collision.timeStamp()); + collision.timeStamp(), + shared ? static_cast(track.posTrackId()) : NoSharedTrackIdx, + shared ? static_cast(track.negTrackId()) : NoSharedTrackIdx); + }; + for (auto const& track : lTrks) { + fillMixEventTrack(track); } for (auto const& track : alTrks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), - track.py(), track.pz(), track.mass(), track.prPx(), - track.prPy(), track.prPz(), track.v0Type(), - collision.timeStamp()); + fillMixEventTrack(track); } } PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, "Mix-event table filling", false); @@ -2339,7 +2537,7 @@ struct LambdaSpinPolarization { auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); - if (lTrks.size() == 0 && alTrks.size() == 0) { + if (!hasMinCandidates(lTrks, alTrks)) { return; } From d4cc190f81dd0be966ed23a8981220ed3e34a203 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Fri, 9 Oct 2026 08:46:19 +0800 Subject: [PATCH 236/648] [PWGCF] Add sel8 and NContrib cuts (#18226) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 179 ++++++++---------- 1 file changed, 82 insertions(+), 97 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 64137643dc9..2b702910b33 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -187,12 +187,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to { // *) Base TList to hold all output objects: TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function - OutputObj fBaseList{sBaseListName.Data(), - OutputObjHandlingPolicy::AnalysisObject, - OutputObjSourceType::OutputObjSource}; + OutputObj fBaseList{sBaseListName.Data(), OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType::OutputObjSource}; // *) CCDB: - Service ccdb{}; // support for offline callibration data base, not needed for the time being... + Service ccdb{}; // support for offline callibration data base Service pdg{}; // *) Define configurables: @@ -207,9 +205,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Binnings Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; - Configurable> cfMultBins{"cfMultBins", {400, 0., 40000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; - Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 40000.}, "Reference mult bins: nMultBins, multMin, multMax"}; - Configurable> cfContribBins{"cfContribBins", {400, 0., 6500.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; + Configurable> cfMultBins{"cfMultBins", {400, 0., 30000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; + Configurable> cfContribBins{"cfContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; @@ -228,6 +226,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply event level cut"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}[cm], with convention: min <= cent <= max"}; + Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; // particle level cuts Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "switch to apply pt cut"}; @@ -244,36 +243,38 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { - std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. - bool fDryRun = false; // book all histos and run without filling and calculating anything + double fSigmaInel = 7.71; + + std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; + + std::vector fVertexZCutRange = {-10., 10.}; + std::vector fCentCutRange = {0., 80.}; + std::vector fPtCutRange = {0.2, 5.}; + std::vector fEtaCutRange = {-0.8, 0.8}; + + std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; + int fCentralityEstimator = 0; int fMultiplicityTables = 0; + int fNContribCutInf = 2; + bool fDryRun = false; // book all histos and run without filling and calculating anything bool fExternalFileSwitch = false; - std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; bool fALICECentBinSwitch = true; bool fMasterCutSwitch = true; - std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; - bool fEventCutSwitch = true; - std::vector fVertexZCutRange = {-10., 10.}; - std::vector fCentCutRange = {0., 80.}; - bool fPtCutSwitch = true; - std::vector fPtCutRange = {0.2, 5.}; bool fEtaCutSwitch = true; - std::vector fEtaCutRange = {-0.8, 0.8}; - bool fChargeCutSwitch = true; - - double fSigmaInel = 7.71; bool fQualityAssuranceSwitch = false; bool fRunMessageSwitch = false; - } tc; // you have to prepend "tc." for all objects name in this group later in the code + + std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + } tc; struct ParticleHistograms { - TList* fParticleHistList = nullptr; //!, 2>, eParticleHistograms_N> fParticleHist{}; } pc; @@ -289,7 +290,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; - std::array fWeights{}; //! [type] + std::array fWeights{}; } ex; struct Observables { @@ -298,7 +299,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } obs; struct QualityAssurance { - TList* fQualityAssuranceList = nullptr; //! fHistCentralityRecSim{}; std::array fHistMultNContrib{}; } qa; @@ -627,7 +628,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof] == "1NoHighMultCollInPrevRof" && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } - return true; } @@ -640,28 +640,32 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fEventCutSwitch) // event level cuts for Rec - { + if (tc.fEventCutSwitch) { // event level cuts for Rec if constexpr (rm == eReal) { - if (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]) { + if (!collision.sel8()) { // sel8 cut + return false; + } + if (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut return false; - } // vertex z cut + } auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - if (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]) { + if (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]) { // centrality cut + return false; + } + if (collision.numContrib() < tc.fNContribCutInf) { // number of contribution cuts return false; - } // centrality cut + } } - if constexpr (rs == eRecAndSim && rm == eMC) // event level cuts for Sim - { + if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; - if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { + if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut return false; - } // vertex z cut - if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { + } + if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut return false; - } // centrality cut + } } } } @@ -735,45 +739,39 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fPtCutSwitch) // pt cuts for Rec - { + if (tc.fPtCutSwitch) { // pt cuts for Rec if constexpr (rm == eReal) { if (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]) { return false; } } - if constexpr (rs == eRecAndSim && rm == eMC) // pt cuts for Sim - { + if constexpr (rs == eRecAndSim && rm == eMC) { // pt cuts for Sim auto thisMCParticle = track.mcParticle(); if (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]) { return false; } } } - if (tc.fEtaCutSwitch) // eta cuts for Rec - { - if constexpr (rm == eReal) { + if (tc.fEtaCutSwitch) { + if constexpr (rm == eReal) { // eta cuts for Rec if (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]) { return false; } } - if constexpr (rs == eRecAndSim && rm == eMC) // eta cuts for Sim - { + if constexpr (rs == eRecAndSim && rm == eMC) { // eta cuts for Sim auto thisMCParticle = track.mcParticle(); if (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]) { return false; } } } - if (tc.fChargeCutSwitch) // charge cuts for Rec - { - if constexpr (rm == eReal) { + if (tc.fChargeCutSwitch) { + if constexpr (rm == eReal) { // charge cuts for Rec if (track.sign() != 1 && track.sign() != -1) { return false; } } - if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim - { + if constexpr (rs == eRecAndSim && rm == eMC) { // charge cuts for Sim auto thisMCParticle = track.mcParticle(); TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); if (particlePDG) { @@ -879,7 +877,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash int collisionCounter = 0; int thisRunNumber = 0; - const int runMessagePeriod = 100; + static constexpr int RunMessagePeriod = 100; // *) Define all member functions to be called in the main process* functions: template @@ -900,7 +898,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (tc.fRunMessageSwitch) { - if (collisionCounter % runMessagePeriod == 0) { + if (collisionCounter % RunMessagePeriod == 0) { LOGF(info, "Successfully running, run number is %d", thisRunNumber); collisionCounter = 0; } @@ -918,20 +916,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to eventHistFill(collision, tracks); bool passEventCutsReal = eventCuts(collision); - bool passEventCutsMC = eventCuts(collision); + // bool passEventCutsMC = eventCuts(collision); // this is currently useless bool passTechnicalCut = technicalCuts(collision); // Fill event hist and qa hist after cuts if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut) { eventHistFill(collision, tracks); - if (tc.fQualityAssuranceSwitch) { - qaFill(collision); - } - } - if (passEventCutsMC) { eventHistFill(collision, tracks); if (tc.fQualityAssuranceSwitch) { + qaFill(collision); qaFill(collision); } } @@ -977,7 +971,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to particleHistFill(track); updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeights[ePhiRec] * thisWeights[ePt]); } - if (passEventCutsMC && particleCuts(track)) { + if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { particleHistFill(track); if (hasMCP) { updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeights[ePhiSim] * thisWeights[ePt]); @@ -996,14 +990,14 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if constexpr (rs == eRecAndSim) { - if (passEventCutsMC) { + if (passEventCutsReal && passTechnicalCut) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; resultTwo = two(qVectorsTableMC, n2); if (noneZeroDenom(resultTwo)) { obs.fProfTwo[eSim]->Fill(thisCollCentMC, (resultTwo[0] / resultTwo[1].Re()).Re()); } - } // end of if (passEventCutsMC) { + } // end of if (passEventCutsReal && passTechnicalCut) { } // end of if constexpr (rs == eRecAndSim) { } // end of if (tc.fMasterCutSwitch) { @@ -1038,6 +1032,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fEventCutSwitch = cfEventCutSwitch; tc.fVertexZCutRange = cfVertexZCutRange.value; tc.fCentCutRange = cfCentCutRange.value; + tc.fNContribCutInf = cfNContribCutInf; tc.fPtCutSwitch = cfPtCutSwitch; tc.fPtCutRange = cfPtCutRange.value; @@ -1094,9 +1089,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const float minEta = lEtaBins[1]; const float maxEta = lEtaBins[2]; - const int nBinsCharge = 5; - const float minCharge = -2.5; - const float maxCharge = 2.5; + constexpr int NBinsCharge = 5; + constexpr float MinCharge = -2.5; + constexpr float MaxCharge = 2.5; if (doprocessRec || doprocessRecSim) { pc.fParticleHist[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); @@ -1108,7 +1103,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); pc.fParticleHist[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHist[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", NBinsCharge, MinCharge, MaxCharge); pc.fParticleHist[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { @@ -1126,7 +1121,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); pc.fParticleHist[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHist[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", NBinsCharge, MinCharge, MaxCharge); pc.fParticleHist[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { @@ -1146,7 +1141,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); pc.fParticleHist[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHist[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", NBinsCharge, MinCharge, MaxCharge); pc.fParticleHist[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { @@ -1164,7 +1159,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); pc.fParticleHist[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); - pc.fParticleHist[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", nBinsCharge, minCharge, maxCharge); + pc.fParticleHist[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", NBinsCharge, MinCharge, MaxCharge); pc.fParticleHist[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); for (int i = 0; i < eParticleHistograms_N; ++i) { @@ -1180,8 +1175,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHistList->SetOwner(true); fBaseList->Add(ec.fEventHistList); - float defaultCentBoundaries[] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; - const int nDefaultCentBins = sizeof(defaultCentBoundaries) / sizeof(defaultCentBoundaries[0]) - 1; + constexpr std::array DefaultCentBoundaries = {0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f}; + constexpr int NDefaultCentBins = DefaultCentBoundaries.size() - 1; std::vector lCent = cfCentBins.value; const int nBinsCent = static_cast(lCent[0]); @@ -1218,11 +1213,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const float minIp = lIp[1]; const float maxIp = lIp[2]; - // eEventHistograms_N - if (doprocessRec || doprocessRecSim) { if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nDefaultCentBins, defaultCentBoundaries); + ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", NDefaultCentBins, DefaultCentBoundaries.data()); } else { ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); } @@ -1252,7 +1245,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fMasterCutSwitch) { if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nDefaultCentBins, defaultCentBoundaries); + ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); } else { ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); } @@ -1284,7 +1277,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessSim || doprocessRecSim) { if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nDefaultCentBins, defaultCentBoundaries); + ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", NDefaultCentBins, DefaultCentBoundaries.data()); } else { ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); } @@ -1314,7 +1307,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fMasterCutSwitch) { if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nDefaultCentBins, defaultCentBoundaries); + ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); } else { ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); } @@ -1351,21 +1344,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(obs.fObservablesList); if (doprocessRec || doprocessRecSim) { - if (tc.fALICECentBinSwitch) { - obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nDefaultCentBins, defaultCentBoundaries); - } else { - obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", nBinsCent, minCent, maxCent); - } + obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); obs.fProfTwo[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwo[eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eRec]); if (doprocessRecSim) { - if (tc.fALICECentBinSwitch) { - obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nDefaultCentBins, defaultCentBoundaries); - } else { - obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", nBinsCent, minCent, maxCent); - } + obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwo[eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eSim]); @@ -1390,17 +1375,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); } - } - if (doprocessRecSim) { - for (int i = 0; i < eCuts_N; ++i) { - qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); - qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); - qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); - qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); - } - } - } + if (doprocessRecSim) { + for (int i = 0; i < eCuts_N; ++i) { + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); + qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); + qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); + qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); + } + } // end of if (doprocessRecSim) { + } // end of if (doprocessRec || doprocessRecSim) { + } // end of if (tc.fQualityAssuranceSwitch) { } // end of void init(InitContext&) { // A) Process only reconstructed data: From 12237292359324fb4667236a5155e6f01cb07e14 Mon Sep 17 00:00:00 2001 From: Subhadeep Roy Date: Fri, 9 Oct 2026 10:03:05 +0530 Subject: [PATCH 237/648] [PWGCF] lambdaSpinPolarization.cxx: fix clang-tidy code-check errors from the previous version (#18229) --- .../Tasks/lambdaSpinPolarization.cxx | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index a0d3ddcd6fd..8906ab8532b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -30,11 +30,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -45,7 +43,6 @@ #include #include -#include #include #include #include @@ -1389,7 +1386,7 @@ struct LambdaSpinPolarization { uint64_t pairOrderSeed = 0; std::array, 4> kinWeightMaps{}; - std::vector legAxesRef{}; + std::vector legAxesRef; bool kinWeightsLoaded = false; struct SharedDauCand { @@ -1450,8 +1447,8 @@ struct LambdaSpinPolarization { { for (auto const& cand : event) { const int64_t seq = seqBase + static_cast(slots.size()); - slots.push_back(Slot{cand, NoSeq}); - auto [it, isNew] = cells[cand.species()].try_emplace(cellKey(cand), Cell{seq, seq}); + slots.push_back(Slot{.cand = cand, .next = NoSeq}); + auto [it, isNew] = cells[cand.species()].try_emplace(cellKey(cand), Cell{.head = seq, .tail = seq}); if (!isNew) { slot(it->second.tail).next = seq; it->second.tail = seq; @@ -1554,16 +1551,16 @@ struct LambdaSpinPolarization { Grid grid{}; int maxEvents = 0; - std::deque slots{}; - std::deque nPerEvent{}; + std::deque slots; + std::deque nPerEvent; int64_t seqBase = 0; std::array, 2> cells{}; }; - std::vector mePools{}; - std::vector mePoolsGen{}; - TAxis mePoolAxisCent{}; - TAxis mePoolAxisVz{}; + std::vector mePools; + std::vector mePoolsGen; + TAxis mePoolAxisCent; + TAxis mePoolAxisVz; void init(InitContext const&) { @@ -1589,7 +1586,8 @@ struct LambdaSpinPolarization { legAxesRef.emplace_back(static_cast(edges.size()) - 1, edges.data()); } - if (!(cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value)) { + const bool massAxesContainWindow = cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value; + if (!massAxesContainWindow) { LOGF(fatal, "The mass axes [%.4f, %.4f] must be wider than the accepted mass window [%.4f, %.4f]", cMassHistMin.value, cMassHistMax.value, cMassAccMin.value, cMassAccMax.value); } for (auto const& edge : {cMassAccMin.value, cMassAccMax.value}) { @@ -1610,18 +1608,18 @@ struct LambdaSpinPolarization { mePoolAxisCent.Set(static_cast(centEdges.size()) - 1, centEdges.data()); mePoolAxisVz.Set(static_cast(vzEdges.size()) - 1, vzEdges.data()); - static constexpr std::array PoolSizeSteps = {1., 2., 5.}; - static constexpr double PoolSizeDecade = 10.; - const double depth = std::max(1, cMEPoolDepth.value); + static constexpr std::array PoolSizeSteps = {1, 2, 5}; + static constexpr int64_t PoolSizeDecade = 10; + const int64_t depth = std::max(1, cMEPoolDepth.value); std::vector poolSizeEdges = {0.}; - for (double decade = 1.; decade < depth; decade *= PoolSizeDecade) { + for (int64_t decade = 1; decade < depth; decade *= PoolSizeDecade) { for (auto const& step : PoolSizeSteps) { if (step * decade < depth) { - poolSizeEdges.push_back(step * decade); + poolSizeEdges.push_back(static_cast(step * decade)); } } } - poolSizeEdges.push_back(depth); + poolSizeEdges.push_back(static_cast(depth)); const AxisSpec axisPoolSize(poolSizeEdges, "events in the pool"); std::vector mixQaDirs; @@ -1658,7 +1656,7 @@ struct LambdaSpinPolarization { grid.nRap = std::max(1, static_cast(std::ceil((cMaxRap.value - cMinRap.value) / grid.cellRap))); grid.nPhi = std::max(1, static_cast(std::floor(TwoPI / cMaxDeltaPhi.value))); grid.cellPhi = TwoPI / static_cast(grid.nPhi); - const auto nPools = static_cast(mePoolAxisCent.GetNbins() * mePoolAxisVz.GetNbins()); + const auto nPools = static_cast(mePoolAxisCent.GetNbins()) * static_cast(mePoolAxisVz.GetNbins()); if (doprocessDataRecoMixed) { mePools.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); } @@ -1968,7 +1966,7 @@ struct LambdaSpinPolarization { return; } auto const* guard = dynamic_cast(list->FindObject("kinWeightGuardStatus")); - if (guard && !TString(guard->GetTitle()).BeginsWith("OK")) { + if (guard != nullptr && !TString(guard->GetTitle()).BeginsWith("OK")) { LOGF(fatal, "Kinematic weights at %s are flagged by their builder: %s", cKinWeightCcdbPath.value.c_str(), guard->GetTitle()); return; } @@ -1979,7 +1977,7 @@ struct LambdaSpinPolarization { LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); return; } - kinWeightMaps[i].reset(static_cast(map->Clone())); + kinWeightMaps[i].reset(dynamic_cast(map->Clone())); } auto const* form = dynamic_cast(list->FindObject("kinWeightForm")); kinWeightsLoaded = true; From eab29c9f631965cfc92b84ce3f01bec941e82d39 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Fri, 9 Oct 2026 08:56:48 +0200 Subject: [PATCH 238/648] [Common] Generalise navigation of mother indices for MC truth matching (#18222) Co-authored-by: ALICE Action Bot --- Common/Core/RecoDecay.h | 88 +++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/Common/Core/RecoDecay.h b/Common/Core/RecoDecay.h index 075a304c2bf..b8e8bedefdd 100644 --- a/Common/Core/RecoDecay.h +++ b/Common/Core/RecoDecay.h @@ -27,6 +27,7 @@ #include // std::abs, std::sqrt #include // std::size_t #include // intX_t +#include // std::iota #include // std::apply #include // std::decay_t #include // std::move @@ -49,6 +50,10 @@ struct RecoDecay { }; static constexpr int8_t StatusCodeAfterFlavourOscillation{92}; // decay products after B0(s) flavour oscillation + static constexpr int8_t StatusCodePrimaryMin{81}; // primary particles (after hadronization) have status code >= 81 + static constexpr int8_t StatusCodePrimaryMax{89}; // primary particles (after hadronization) have status code <= 89 + static constexpr int8_t StatusCodeRhadronMin{101}; // R-hadrons (after hadronization) have status code >= 101 + static constexpr int8_t StatusCodeRhadronMax{109}; // R-hadrons (after hadronization) have status code <= 109 static constexpr int PdgQuarkMax{8}; // largest quark PDG code; o2-linter: disable=pdg/explicit-code (t' does not have a named constant.) static constexpr int PdgBosonMin{PDG_t::kGluon}; // smallest boson (gauge or H) PDG code static constexpr int PdgBosonMax{37}; // largest boson (gauge or H) PDG code; o2-linter: disable=pdg/explicit-code (H+ does not have a named constant.) @@ -561,6 +566,46 @@ struct RecoDecay { return maxNormDeltaIP; } + template + static void getOrderedMotherIds(const PPart& particle, + std::vector& motherIdsOrdered, + const bool searchUpToQuark = false) + { + if (!particle.has_mothers()) { + return; + } + + if (particle.mothersIds().front() == particle.globalIndex()) { + return; + } + + if (!searchUpToQuark) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else { + if (particle.mothersIds().size() == 2) { + if (particle.mothersIds().front() == particle.mothersIds().back()) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else if (particle.mothersIds().front() < particle.mothersIds().back()) { + auto absStatusCode = std::abs(particle.getGenStatusCode()); + if ((absStatusCode >= StatusCodePrimaryMin && absStatusCode <= StatusCodePrimaryMax) || (absStatusCode >= StatusCodeRhadronMin && absStatusCode <= StatusCodeRhadronMax)) { + motherIdsOrdered.resize(particle.mothersIds().back() - particle.mothersIds().front() + 1); + std::iota(motherIdsOrdered.begin(), motherIdsOrdered.end(), particle.mothersIds().front()); + } else { + motherIdsOrdered.push_back(particle.mothersIds().front()); + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + } else if (particle.mothersIds().front() > particle.mothersIds().back()) { + if (particle.mothersIds().back() != 0) { + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } else if (particle.mothersIds().size() == 1) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } + } + /// Finds the mother of an MC particle by looking for the expected PDG code in the mother chain. /// \tparam acceptFlavourOscillation switch to accept decays where the mother oscillated (e.g. B0 -> B0bar) /// \param particlesMC table with MC particles @@ -599,10 +644,12 @@ struct RecoDecay { for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - // If searchUpToQuark is false we only take the first mother (since decay products only have one mother) - auto lastMotherIdxToCheck = searchUpToQuark ? particleMother.mothersIds().back() : particleMother.mothersIds().front(); - for (auto iMother = particleMother.mothersIds().front(); iMother <= lastMotherIdxToCheck; ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); @@ -1059,6 +1106,7 @@ struct RecoDecay { while (arrayIds[-stage].size() > 0) { // vector of mother indices for the current stage std::vector arrayIdsStage{}; + std::vector motherIdsOrdered{}; for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { @@ -1072,8 +1120,9 @@ struct RecoDecay { } } - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); @@ -1161,30 +1210,11 @@ struct RecoDecay { auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - // we break immediately if searchUpToQuark is false and the first mother is a quark or a boson (a hadron should never be the mother of a parton) - if (!searchUpToQuark) { - auto mother = particlesMC.rawIteratorAt(particleMother.mothersIds().front() - particlesMC.offset()); - auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother - if (pdgParticleIMother <= PdgQuarkMax || (pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax)) { - // auto PDGPaticle = std::abs(particleMother.pdgCode()); - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kBottom || // b mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kBottom) // b baryons - ) { - return OriginType::NonPrompt; // beauty - } - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kCharm || // c mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kCharm) // c baryons - ) { - return OriginType::Prompt; // charm - } - break; - } - } + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); From 28ce25d149822a24e3bb3baa390c9ee7d89437f9 Mon Sep 17 00:00:00 2001 From: nzardosh Date: Fri, 9 Oct 2026 10:03:17 +0200 Subject: [PATCH 239/648] [PWGJE] changing defaults for embedding (#18224) --- PWGJE/TableProducer/derivedDataProducerEmbedding.cxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx index 1a87e66b360..e093d61b686 100644 --- a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx +++ b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx @@ -183,7 +183,6 @@ struct JetDerivedDataEmbeddingProducerTask { Configurable includeTriggers{"includeTriggers", false, "fill the collision information with software trigger decisions"}; Configurable includeHadronicRate{"includeHadronicRate", true, "fill the collision information with the hadronic rate"}; - Configurable includeUpcs{"includeUpcs", true, "include option to identify UPC events"}; Configurable v0ChargedDecaysOnly{"v0ChargedDecaysOnly", true, "store V0s (at particle-level) only if they decay to charged particles"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; @@ -497,7 +496,7 @@ struct JetDerivedDataEmbeddingProducerTask { } } } - PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionLabels, "produces derived MC collision labels table", false); + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionLabels, "produces derived MC collision labels table", true); void processMcCollisions(soa::Join>, aod::HepMCXSectionsFrom>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) { @@ -515,7 +514,7 @@ struct JetDerivedDataEmbeddingProducerTask { products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); } - PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisions, "produces derived MC collision table", false); + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisions, "produces derived MC collision table", true); void processMcCollisionsWithoutCentralityAndMultiplicity(soa::Join>, aod::HepMCXSectionsFrom>>::iterator const& mcCollision) { @@ -815,7 +814,7 @@ struct JetDerivedDataEmbeddingProducerTask { products.jMcParticlesTable(particle.mcCollisionId(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), particle.pdgCode(), particle.statusCode(), particle.flags(), mothersId, daughtersId); products.jParticlesParentIndexTable(particle.globalIndex()); } - PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processParticles, "produces derived parrticle table", false); + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processParticles, "produces derived parrticle table", true); void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) { From d3c92c597bc71edf2d69b53530b8b098fb5c60b9 Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Fri, 9 Oct 2026 15:14:56 +0200 Subject: [PATCH 240/648] [PWGEM] Fix code check errors (#18223) Co-authored-by: jokonig --- PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h | 2 ++ .../TableProducer/skimmerPrimaryElectronFromDalitzEE.h | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index 95f181e49fd..483f38098b7 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -82,6 +82,7 @@ #include #include +// NOLINTBEGIN(google-global-names-in-headers) using o2::framework::AxisSpec; using o2::framework::Configurable; using o2::framework::ConfigurableGroup; @@ -92,6 +93,7 @@ using o2::framework::kTH2F; using o2::framework::kTHnSparseF; using o2::framework::Preslice; using o2::framework::Produces; +// NOLINTEND(google-global-names-in-headers) using MyCollisions = o2::soa::Join; // using MyCollisionsWithSWT = soa::Join; diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h index d89f3b002a1..559da2e0d8d 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -60,12 +60,7 @@ #include #include -// using namespace o2; -// using namespace o2::soa; -// using namespace o2::framework; -// using namespace o2::framework::expressions; -// using namespace o2::constants::physics; - +// NOLINTBEGIN(google-global-names-in-headers) using o2::framework::AxisSpec; using o2::framework::Configurable; using o2::framework::ConfigurableGroup; @@ -76,6 +71,7 @@ using o2::framework::kTH2F; using o2::framework::kTHnSparseF; using o2::framework::Preslice; using o2::framework::Produces; +// NOLINTEND(google-global-names-in-headers) using MyCollisions = o2::soa::Join; using MyCollisionsWithSWT = o2::soa::Join; From 48828338010ccd299d7b9de9a4ca8d39a15145b8 Mon Sep 17 00:00:00 2001 From: Cristian Andrei Date: Fri, 9 Oct 2026 17:21:31 +0300 Subject: [PATCH 241/648] [PWGLF] eventShapeCoex: switch to histogram output of sub-event moment sums (#18230) --- .../GlobalEventProperties/eventShapeCoex.cxx | 500 +++++++++++++----- 1 file changed, 377 insertions(+), 123 deletions(-) diff --git a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx index 1dfd790903e..4f91fdac417 100644 --- a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx +++ b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx @@ -10,31 +10,40 @@ // or submit itself to any jurisdiction. /// \file eventShapeCoex.cxx -/// \brief Per-collision forward/backward sub-event quantities of the mid-rapidity tracks. +/// \brief Moment sums of the forward/backward sub-event mean pT of mid-rapidity tracks /// \author Cristian Andrei /// /// First O2Physics task of the AliPhysics PWGLF/SPECTRA/MultEvShape analyses. -#include "PWGLF/DataModel/LFEventTopologyTables.h" - #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include + +#include #include #include #include +#include +#include using namespace o2; using namespace o2::framework; @@ -42,164 +51,431 @@ using namespace o2::framework::expressions; namespace { -/// Sums over one sub-event, shared by the reconstructed and generated levels. -struct SubEventSums { - uint32_t n = 0; +constexpr float PtMin = 0.15f; // GeV/c +constexpr float EtaMax = 0.8f; +constexpr double MeanPtShift = 0.6; // GeV/c + +// Sub-events are unions of |eta| x pT rings; the last pT ring is open-ended +constexpr int NEtaRings = 5; +constexpr std::array EtaRingEdges = {0.f, 0.2f, 0.3f, 0.4f, 0.5f, EtaMax}; +constexpr int NPtRings = 6; +constexpr std::array PtRingLowEdges = {PtMin, 1.f, 1.5f, 2.f, 3.f, 5.f}; +constexpr int EtaFirstNominal = 1; // |eta| > 0.2 +constexpr int PtEndNominal = 3; // pT < 2 GeV/c + +enum Side { kF = 0, + kB, + NSides }; + +// Monomial exponents of (xF, xB, vF, vB); the order is part of the output format +constexpr int NMono1 = 16; +constexpr int NMono = 81; +constexpr std::array, NMono> MonoExp = {{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {2, 0, 0, 0}, {0, 2, 0, 0}, {1, 1, 0, 0}, {2, 1, 0, 0}, {1, 2, 0, 0}, {2, 2, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 1, 1, 0}, {1, 0, 0, 1}, {0, 2, 1, 0}, {2, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 0, 2}, {0, 0, 1, 2}, {0, 0, 2, 0}, {0, 0, 2, 1}, {0, 0, 2, 2}, {0, 1, 0, 1}, {0, 1, 1, 1}, {0, 1, 2, 0}, {0, 1, 2, 1}, {0, 2, 0, 1}, {0, 2, 1, 1}, {0, 2, 2, 0}, {0, 2, 2, 1}, {0, 3, 0, 0}, {0, 3, 1, 0}, {0, 3, 2, 0}, {0, 4, 0, 0}, {0, 4, 1, 0}, {0, 4, 2, 0}, {1, 0, 0, 2}, {1, 0, 1, 0}, {1, 0, 1, 1}, {1, 0, 1, 2}, {1, 1, 0, 1}, {1, 1, 1, 0}, {1, 1, 1, 1}, {1, 2, 0, 1}, {1, 2, 1, 0}, {1, 2, 1, 1}, {1, 3, 0, 0}, {1, 3, 1, 0}, {1, 4, 0, 0}, {1, 4, 1, 0}, {2, 0, 0, 2}, {2, 0, 1, 0}, {2, 0, 1, 1}, {2, 0, 1, 2}, {2, 1, 0, 1}, {2, 1, 1, 0}, {2, 1, 1, 1}, {2, 2, 0, 1}, {2, 2, 1, 0}, {2, 2, 1, 1}, {2, 3, 0, 0}, {2, 3, 1, 0}, {2, 4, 0, 0}, {2, 4, 1, 0}, {3, 0, 0, 0}, {3, 0, 0, 1}, {3, 0, 0, 2}, {3, 1, 0, 0}, {3, 1, 0, 1}, {3, 2, 0, 0}, {3, 2, 0, 1}, {3, 3, 0, 0}, {3, 4, 0, 0}, {4, 0, 0, 0}, {4, 0, 0, 1}, {4, 0, 0, 2}, {4, 1, 0, 0}, {4, 1, 0, 1}, {4, 2, 0, 0}, {4, 2, 0, 1}, {4, 3, 0, 0}, {4, 4, 0, 0}}}; + +// Event-selection bits and the pile-up selection variants built from them +constexpr int NQaBits = 12; +constexpr std::array QaBitOrder = { + o2::aod::evsel::kNoSameBunchPileup, o2::aod::evsel::kIsGoodZvtxFT0vsPV, + o2::aod::evsel::kIsVertexITSTPC, o2::aod::evsel::kIsVertexTOFmatched, + o2::aod::evsel::kNoCollInTimeRangeNarrow, o2::aod::evsel::kNoCollInTimeRangeStrict, + o2::aod::evsel::kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInRofStrict, + o2::aod::evsel::kNoCollInRofStandard, o2::aod::evsel::kNoHighMultCollInPrevRof, + o2::aod::evsel::kNoITSROFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}; +enum EvVariant { kV1 = 0, + kV0, + kV3, + kV6, + NEvVariants }; +constexpr std::array EvVariantMask = { + (1u << 0) | (1u << 1), + 0u, + (1u << 0) | (1u << 1) | (1u << 6), + (1u << 0) | (1u << 1) | (1u << 2) | (1u << 6) | (1u << 8) | (1u << 9)}; +constexpr uint16_t AllQaBits = 0xFFFF; + +// Track count defining the stratum +enum Conditioning { kCondOwn = 0, // own sub-events + kCondAll, // all tracks, gap included + kCondUnwindowed }; // nominal gap, no upper pT cut + +struct Config { + const char* name; + int etaFirst; // |eta| rings [etaFirst, NEtaRings) + int ptEnd; // pT rings [0, ptEnd) + Conditioning cond; // stratum count + int validEtaFirst; // sub-events that must hold MinTracksValid tracks + int validPtEnd; + EvVariant evVariant; // pile-up selection +}; +constexpr int NConfigs = 13; +constexpr int INominal = 0; +constexpr std::array Configs = {{{.name = "NOM", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP02", .etaFirst = 1, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP03", .etaFirst = 2, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP04", .etaFirst = 3, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP05", .etaFirst = 4, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "PT10", .etaFirst = 1, .ptEnd = 1, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT15", .etaFirst = 1, .ptEnd = 2, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT20", .etaFirst = 1, .ptEnd = 3, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT30", .etaFirst = 1, .ptEnd = 4, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT50", .etaFirst = 1, .ptEnd = 5, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "EVV0", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV0}, + {.name = "EVV3", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV3}, + {.name = "EVV6", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV6}}}; + +constexpr int NCountBins = 64; // larger counts in the overflow bin +constexpr int NSubCountBins = 32; // per sub-event +constexpr int MinTracksValid = 2; // per sub-event + +// Data halves and subsample groups, defined on blocks of bunch crossings +constexpr int NHalves = 2; +constexpr std::array HalfName = {"A", "B_SEALED"}; +constexpr int HalfBlockShift = 26; // 2^26 BC = 1.7 s +constexpr int GroupBlockShift = 20; // 2^20 BC = 26 ms +constexpr int NGroups = 20; + +/// splitmix64 finaliser +constexpr uint64_t mix64(uint64_t z) +{ + z += 0x9e3779b97f4a7c15ULL; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + return z ^ (z >> 31); +} + +/// Hash of the (run, block of 2^shift bunch crossings) of an event +uint64_t blockHash(int run, uint64_t globalBC, int shift) +{ + return mix64(mix64(static_cast(run)) ^ (globalBC >> shift)); +} + +struct SubEvent { + int n = 0; double sumPt = 0.; double sumPt2 = 0.; - double qx2 = 0., qy2 = 0.; - double qx4 = 0., qy4 = 0.; +}; + +/// Track sums of one event per (side, |eta| ring, pT ring) +struct Rings { + std::array ring{}; + + void add(float eta, double pt) + { + const float absEta = std::abs(eta); + int iEta = 0; + while (iEta < NEtaRings - 1 && absEta > EtaRingEdges[iEta + 1]) { + ++iEta; + } + int iPt = 0; + while (iPt < NPtRings - 1 && pt >= PtRingLowEdges[iPt + 1]) { + ++iPt; + } + auto& r = ring[((eta > 0.f ? kF : kB) * NEtaRings + iEta) * NPtRings + iPt]; + ++r.n; + r.sumPt += pt; + r.sumPt2 += pt * pt; + } - void add(double pt, float phi) + /// Sub-event of one side: |eta| rings [etaFirst, NEtaRings), pT rings [0, ptEnd) + [[nodiscard]] SubEvent sum(int side, int etaFirst, int ptEnd) const { - // phi stays float, as stored in the AOD - const double c = std::cos(phi); - const double s = std::sin(phi); - const double cos2 = c * c - s * s; - const double sin2 = 2. * s * c; - ++n; - sumPt += pt; - sumPt2 += pt * pt; - qx2 += cos2; - qy2 += sin2; - qx4 += cos2 * cos2 - sin2 * sin2; - qy4 += 2. * sin2 * cos2; + SubEvent s; + for (int iEta = etaFirst; iEta < NEtaRings; ++iEta) { + for (int iPt = 0; iPt < ptEnd; ++iPt) { + const auto& r = ring[(side * NEtaRings + iEta) * NPtRings + iPt]; + s.n += r.n; + s.sumPt += r.sumPt; + s.sumPt2 += r.sumPt2; + } + } + return s; + } + + [[nodiscard]] int count(int etaFirst, int ptEnd) const { return sum(kF, etaFirst, ptEnd).n + sum(kB, etaFirst, ptEnd).n; } +}; + +/// x = - MeanPtShift, v = pT variance / n (0 for n < 2) +void observe(const SubEvent& s, double& x, double& v) +{ + x = s.sumPt / s.n - MeanPtShift; + v = 0.; + if (s.n > 1) { + const double var = (s.sumPt2 - s.sumPt * s.sumPt / s.n) / (s.n - 1); + v = std::max(var, 0.) / s.n; + } +} + +/// The first nMono monomials of (xF, xB, vF, vB) +void monomials(double xF, double xB, double vF, double vB, int nMono, std::array& m) +{ + const std::array pxF = {1., xF, xF * xF, xF * xF * xF, xF * xF * xF * xF}; + const std::array pxB = {1., xB, xB * xB, xB * xB * xB, xB * xB * xB * xB}; + const std::array pvF = {1., vF, vF * vF}; + const std::array pvB = {1., vB, vB * vB}; + for (int k = 0; k < nMono; ++k) { + const auto& e = MonoExp[k]; + m[k] = pxF[e[0]] * pxB[e[1]] * pvF[e[2]] * pvB[e[3]]; } +} + +/// Moment sums of one reconstructed half or of the generated events +struct Sums { + std::array, NConfigs> config{}; // (N_fwd, stratum count, monomial) + std::shared_ptr strata3; // (N_fwd, n_F, n_B, first-order monomial) + std::shared_ptr occupancy; // (configuration, N_fwd, stratum count) +}; - [[nodiscard]] float meanPt() const { return (n > 0) ? static_cast(sumPt / n) : std::nanf(""); } +/// Nominal-configuration observables of an event +struct Nominal { + bool valid = false; + int nMid = 0; + double xF = 0., xB = 0., vF = 0., vB = 0.; }; + +enum EventFlowBin { kFlowAll = 0, + kFlowSel8, + kFlowVtxZ, + kFlowPileupV1, + kFlowNominalValid, + kFlowHalfA, + kFlowHalfB, + NEventFlowBins }; +constexpr std::array EventFlowLabels = {"all", "sel8", "|z|", "pile-up V1", "nominal valid", "half A", "half B"}; } // namespace struct EventShapeCoex { - Produces coex; - Produces coexGen; - Produces coexMcLabels; - + HistogramRegistry registry{"registry"}; Service pdg{}; Configurable vtxZCut{"vtxZCut", 10.0f, "Max |PV z| (cm)"}; - Configurable ptMin{"ptMin", 0.15f, "Minimum track pT (GeV/c)"}; - Configurable ptMax{"ptMax", 2.0f, "Maximum track pT (GeV/c)"}; - Configurable etaMax{"etaMax", 0.8f, "Max |eta| (mid-rapidity measurement region)"}; - Configurable gapHalf{"gapHalf", 0.2f, "Central-gap half-width: |eta| <= gapHalf excluded from F/B"}; Configurable etaFwdAMin{"etaFwdAMin", 3.5f, "FT0-A proxy: lower eta edge (generator level)"}; Configurable etaFwdAMax{"etaFwdAMax", 4.9f, "FT0-A proxy: upper eta edge (generator level)"}; Configurable etaFwdCMin{"etaFwdCMin", -3.3f, "FT0-C proxy: lower eta edge (generator level)"}; Configurable etaFwdCMax{"etaFwdCMax", -2.1f, "FT0-C proxy: upper eta edge (generator level)"}; + ConfigurableAxis axisNFwd{"axisNFwd", {50, 1., 30000.}, "N_fwd = FT0A + FT0C amplitude (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisNFwdGen{"axisNFwdGen", {50, 1., 1000.}, "generator-level N_fwd = charged primaries in the FT0 acceptance (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisRun{"axisRun", {60000, 519999.5, 579999.5}, "run number (QA)"}; - Filter trackFilter = (aod::track::pt > ptMin) && (aod::track::pt < ptMax) && (nabs(aod::track::eta) < etaMax) && requireGlobalTrackInFilter(); + Filter trackFilter = (aod::track::pt > PtMin) && (nabs(aod::track::eta) < EtaMax) && requireGlobalTrackInFilter(); using MyCollisions = soa::Join; using MyCollisionsMc = soa::Join; using MyTracks = soa::Filtered>; + std::array sumsReco{}; + std::array, NHalves> designEffect{}; // (N_fwd, group, first-order monomial) + Sums sumsGen{}; + + /// Books the moment sums of one level under dir// + void bookSums(Sums& sums, const std::string& dir, const std::string& half, const AxisSpec& nFwdAxis, int nConfigs) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec subCountAxis{NSubCountBins, -0.5, NSubCountBins - 0.5, "sub-event count"}; + const AxisSpec monoAxis{NMono, -0.5, NMono - 0.5, "monomial"}; + const AxisSpec mono1Axis{NMono1, -0.5, NMono1 - 0.5, "monomial"}; + const AxisSpec configAxis{NConfigs, -0.5, NConfigs - 0.5, "configuration"}; + std::string label = "[half "; + label += half; + label += "] moment sums "; + for (int c = 0; c < nConfigs; ++c) { + std::string name = dir; + name += "/"; + name += Configs[c].name; + name += "/"; + name += half; + std::string title = label; + title += Configs[c].name; + sums.config[c] = registry.add(name, title.c_str(), HistType::kTH3D, {nFwdAxis, countAxis, monoAxis}); + sums.config[c]->SetBit(TH1::kIsNotW); // no Sumw2 + } + sums.strata3 = registry.add(dir + "/NOM3/" + half, (label + "NOM in (n_F, n_B) strata").c_str(), HistType::kTHnD, {nFwdAxis, subCountAxis, subCountAxis, mono1Axis}); + sums.occupancy = registry.add(dir + "/occupancy/" + half, "event counts per configuration", HistType::kTH3D, {configAxis, nFwdAxis, countAxis}); + } + void init(InitContext const&) { if (doprocessReco && doprocessRecoMC) { - LOGF(fatal, "processReco and processRecoMC both fill EvShapeCoex; enable only one of them."); + LOGF(fatal, "processReco and processRecoMC both fill the reconstructed-level sums; enable only one of them."); } - if (doprocessRecoMC && !doprocessMC) { - LOGF(fatal, "processRecoMC links to EvShapeCoexGen rows and requires processMC in the same job."); + + if (doprocessReco || doprocessRecoMC) { + AxisSpec nFwdAxis{axisNFwd, "#it{N}_{fwd} (FT0A+FT0C amplitude)"}; + if (nFwdAxis.nBins) { + nFwdAxis.makeLogarithmic(); + } + const AxisSpec groupAxis{NGroups, -0.5, NGroups - 0.5, "group"}; + const AxisSpec mono1Axis{NMono1, -0.5, NMono1 - 0.5, "monomial"}; + for (int h = 0; h < NHalves; ++h) { + bookSums(sumsReco[h], "accum", HalfName[h], nFwdAxis, NConfigs); + designEffect[h] = registry.add(std::string("designEffect/") + HalfName[h], "nominal first-order sums per block group", HistType::kTH3D, {nFwdAxis, groupAxis, mono1Axis}); + designEffect[h]->SetBit(TH1::kIsNotW); + } + + // QA + constexpr int NFlowBins = NEventFlowBins; + auto hFlow = registry.add("qa/hEventFlow", "events", HistType::kTH1D, {{NFlowBins, -0.5, NFlowBins - 0.5}}); + for (int b = 0; b < NFlowBins; ++b) { + hFlow->GetXaxis()->SetBinLabel(b + 1, EventFlowLabels[b]); + } + registry.add("qa/hQaBits", "events with qaBits bit i set (order: QaBitOrder)", HistType::kTH1D, {{NQaBits, -0.5, NQaBits - 0.5}}); + registry.add("qa/hNFwd", "selected events", HistType::kTH1D, {nFwdAxis}); + AxisSpec ptAxis{100, PtMin, 20., "#it{p}_{T} (GeV/#it{c})"}; + ptAxis.makeLogarithmic(); + registry.add("qa/hTrackPt", "selected tracks", HistType::kTH1D, {ptAxis}); + registry.add("qa/hTrackEta", "selected tracks", HistType::kTH1D, {{160, -EtaMax, EtaMax, "#eta"}}); + registry.add("qa/hTrackPhi", "selected tracks", HistType::kTH1D, {{72, 0., o2::constants::math::TwoPI, "#varphi"}}); + // quantities: count, a_F, a_B, v_F, v_B + const AxisSpec quantityAxis{5, -0.5, 4.5, "quantity"}; + registry.add("qa/hMarginalSums", "nominal single-side sums", HistType::kTH3D, {nFwdAxis, {NCountBins, -0.5, NCountBins - 0.5, "n_{mid}"}, quantityAxis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRunSums", "nominal single-side sums per run", HistType::kTH2D, {{axisRun, "run"}, quantityAxis})->SetBit(TH1::kIsNotW); } - } - /// Fills one EvShapeCoex row; returns false if the collision is rejected. - template - bool fillReco(TCollision const& coll, MyTracks const& tracks) - { - if (!coll.sel8() || std::abs(coll.posZ()) > vtxZCut) { - return false; + if (doprocessMC) { + AxisSpec nFwdGenAxis{axisNFwdGen, "#it{N}_{fwd} (charged primaries in FT0A+FT0C)"}; + if (nFwdGenAxis.nBins) { + nFwdGenAxis.makeLogarithmic(); + } + bookSums(sumsGen, "gen", "all", nFwdGenAxis, 1); // nominal configuration only + registry.add("gen/hEvents", "generated events", HistType::kTH1D, {{1, -0.5, 0.5}}); } + } - const float g = gapHalf; - SubEventSums fwd, bwd; - uint32_t nGap = 0, nPlus = 0, nMinus = 0, nPVC = 0; - for (const auto& track : tracks) { - if (track.isPVContributor()) { - ++nPVC; - } - const float eta = track.eta(); - if (eta > g) { - fwd.add(track.pt(), track.phi()); - } else if (eta < -g) { - bwd.add(track.pt(), track.phi()); - } else { - ++nGap; + /// Fills the sums of every valid configuration; returns the nominal observables + Nominal fillSums(Sums& sums, const Rings& rings, double nFwd, uint16_t qaBits, int nConfigs) + { + Nominal nominal; + std::array m{}; + double xF = 0., xB = 0., vF = 0., vB = 0.; + for (int c = 0; c < nConfigs; ++c) { + const auto& cfg = Configs[c]; + const uint16_t mask = EvVariantMask[cfg.evVariant]; + if ((qaBits & mask) != mask) { continue; } - if (track.sign() > 0) { - ++nPlus; - } else if (track.sign() < 0) { - ++nMinus; + if (rings.sum(kF, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid || rings.sum(kB, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid) { + continue; + } + const SubEvent fwd = rings.sum(kF, cfg.etaFirst, cfg.ptEnd); + const SubEvent bwd = rings.sum(kB, cfg.etaFirst, cfg.ptEnd); + int nCond = fwd.n + bwd.n; + if (cfg.cond == kCondAll) { + nCond = rings.count(0, PtEndNominal); + } else if (cfg.cond == kCondUnwindowed) { + nCond = rings.count(EtaFirstNominal, NPtRings); + } + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + sums.config[c]->Fill(nFwd, nCond, k, m[k]); + } + sums.occupancy->Fill(c, nFwd, nCond); + if (c == INominal) { + nominal = {.valid = true, .nMid = nCond, .xF = xF, .xB = xB, .vF = vF, .vB = vB}; } } - // bit i of qaBits = QaBitOrder[i] - static constexpr int NQaBits = 12; - static constexpr std::array QaBitOrder = { - o2::aod::evsel::kNoSameBunchPileup, o2::aod::evsel::kIsGoodZvtxFT0vsPV, - o2::aod::evsel::kIsVertexITSTPC, o2::aod::evsel::kIsVertexTOFmatched, - o2::aod::evsel::kNoCollInTimeRangeNarrow, o2::aod::evsel::kNoCollInTimeRangeStrict, - o2::aod::evsel::kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInRofStrict, - o2::aod::evsel::kNoCollInRofStandard, o2::aod::evsel::kNoHighMultCollInPrevRof, - o2::aod::evsel::kNoITSROFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}; + // (n_F, n_B) strata, n_F, n_B >= 1 + const uint16_t maskNominal = EvVariantMask[Configs[INominal].evVariant]; + const SubEvent fwd = rings.sum(kF, EtaFirstNominal, PtEndNominal); + const SubEvent bwd = rings.sum(kB, EtaFirstNominal, PtEndNominal); + if ((qaBits & maskNominal) == maskNominal && fwd.n > 0 && bwd.n > 0) { + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono1, m); + std::array coord = {nFwd, static_cast(fwd.n), static_cast(bwd.n), 0.}; + for (int k = 0; k < NMono1; ++k) { + coord[3] = k; + sums.strata3->Fill(coord.data(), m[k]); + } + } + return nominal; + } + + template + void fillReco(TCollision const& coll, MyTracks const& tracks) + { + registry.fill(HIST("qa/hEventFlow"), kFlowAll); + if (!coll.sel8()) { + return; + } + registry.fill(HIST("qa/hEventFlow"), kFlowSel8); + if (std::abs(coll.posZ()) > vtxZCut) { + return; + } + registry.fill(HIST("qa/hEventFlow"), kFlowVtxZ); + uint16_t qaBits = 0; for (int i = 0; i < NQaBits; ++i) { if (coll.selection_bit(QaBitOrder[i])) { qaBits |= static_cast(1u << i); + registry.fill(HIST("qa/hQaBits"), i); } } + if ((qaBits & EvVariantMask[kV1]) == EvVariantMask[kV1]) { + registry.fill(HIST("qa/hEventFlow"), kFlowPileupV1); + } + + Rings rings; + for (const auto& track : tracks) { + rings.add(track.eta(), track.pt()); + registry.fill(HIST("qa/hTrackPt"), track.pt()); + registry.fill(HIST("qa/hTrackEta"), track.eta()); + registry.fill(HIST("qa/hTrackPhi"), track.phi()); + } const auto& bc = coll.template bc_as(); + const double nFwd = coll.multFT0A() + coll.multFT0C(); + registry.fill(HIST("qa/hNFwd"), nFwd); + const int half = static_cast(blockHash(bc.runNumber(), bc.globalBC(), HalfBlockShift) & 1u); + const Nominal nominal = fillSums(sumsReco[half], rings, nFwd, qaBits, NConfigs); + if (!nominal.valid) { + return; + } + registry.fill(HIST("qa/hEventFlow"), kFlowNominalValid); + registry.fill(HIST("qa/hEventFlow"), kFlowHalfA + half); - coex(static_cast(coll.globalIndex()), bc.runNumber(), bc.globalBC(), coll.posZ(), - coll.multFT0A(), coll.multFT0C(), - static_cast(fwd.n), static_cast(bwd.n), static_cast(nGap), - fwd.meanPt(), bwd.meanPt(), static_cast(fwd.sumPt2), static_cast(bwd.sumPt2), - static_cast(fwd.qx2), static_cast(fwd.qy2), - static_cast(bwd.qx2), static_cast(bwd.qy2), - static_cast(fwd.qx4), static_cast(fwd.qy4), - static_cast(bwd.qx4), static_cast(bwd.qy4), - static_cast(nPlus), static_cast(nMinus), - qaBits, coll.trackOccupancyInTimeRange(), coll.ft0cOccupancyInTimeRange(), - coll.numContrib(), coll.collisionTimeRes(), static_cast(nPVC)); - return true; + // subsample groups + const int group = static_cast(blockHash(bc.runNumber(), bc.globalBC(), GroupBlockShift) % NGroups); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono1, m); + for (int k = 0; k < NMono1; ++k) { + designEffect[half]->Fill(nFwd, group, k, m[k]); + } + + const std::array quantities = {1., nominal.xF + MeanPtShift, nominal.xB + MeanPtShift, nominal.vF, nominal.vB}; + for (int q = 0; q < static_cast(quantities.size()); ++q) { + registry.fill(HIST("qa/hMarginalSums"), nFwd, nominal.nMid, q, quantities[q]); + registry.fill(HIST("qa/hRunSums"), bc.runNumber(), q, quantities[q]); + } } void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCs const&) { fillReco(coll, tracks); } - PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level reduction (EvShapeCoex)", true); + PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level moment sums", true); void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::BCs const&) { - if (fillReco(coll, tracks)) { - // EvShapeCoexGen has one row per McCollision, in order: the McCollision index is its row index - coexMcLabels(coll.mcCollisionId()); - } + fillReco(coll, tracks); } - PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level reduction with MC labels (EvShapeCoex + EvShapeCoexMcLabels)", false); + PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level moment sums on MC (MC-labelled collisions)", false); - void processMC(aod::McCollision const& mcCollision, aod::McParticles const& particles) + void processMC(aod::McCollision const&, aod::McParticles const& particles) { static constexpr double ChargeTolerance = 1.e-3; - const float g = gapHalf; - const float em = etaMax; - SubEventSums fwd, bwd; - uint32_t nFwdA = 0, nFwdC = 0, nGap = 0, nPlus = 0, nMinus = 0; + registry.fill(HIST("gen/hEvents"), 0); + uint32_t nFwdA = 0, nFwdC = 0; + Rings rings; for (const auto& particle : particles) { if (!particle.isPhysicalPrimary()) { continue; } const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle == nullptr) { - continue; // unknown PDG code, charge undetermined - } - const double charge = pdgParticle->Charge(); - if (std::abs(charge) < ChargeTolerance) { - continue; + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; // unknown PDG code or neutral } const float eta = particle.eta(); // forward counts: no pT window @@ -209,36 +485,14 @@ struct EventShapeCoex { if (eta > etaFwdCMin && eta < etaFwdCMax) { ++nFwdC; } - const float pt = particle.pt(); - if (pt <= ptMin || pt >= ptMax || std::abs(eta) >= em) { - continue; - } - if (eta > g) { - fwd.add(pt, particle.phi()); - } else if (eta < -g) { - bwd.add(pt, particle.phi()); - } else { - ++nGap; + if (particle.pt() <= PtMin || std::abs(eta) >= EtaMax) { continue; } - if (charge > 0.) { - ++nPlus; - } else { - ++nMinus; - } + rings.add(eta, particle.pt()); } - - coexGen(static_cast(mcCollision.globalIndex()), mcCollision.posZ(), - static_cast(nFwdA), static_cast(nFwdC), - static_cast(fwd.n), static_cast(bwd.n), static_cast(nGap), - fwd.meanPt(), bwd.meanPt(), static_cast(fwd.sumPt2), static_cast(bwd.sumPt2), - static_cast(fwd.qx2), static_cast(fwd.qy2), - static_cast(bwd.qx2), static_cast(bwd.qy2), - static_cast(fwd.qx4), static_cast(fwd.qy4), - static_cast(bwd.qx4), static_cast(bwd.qy4), - static_cast(nPlus), static_cast(nMinus)); + fillSums(sumsGen, rings, nFwdA + nFwdC, AllQaBits, 1); } - PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level reduction (EvShapeCoexGen)", false); + PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level moment sums (nominal configuration)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 86f47ce7c182e5f9941dde87cd7c6f2d8933a8cc Mon Sep 17 00:00:00 2001 From: Andrea Giovanni Riffero Date: Fri, 9 Oct 2026 23:38:44 +0200 Subject: [PATCH 242/648] [PWGUD] Update ptSpectraInclusiveUpc.cxx (#18231) Co-authored-by: ALICE Action Bot --- PWGUD/Tasks/ptSpectraInclusiveUpc.cxx | 527 +++++++++++++++----------- 1 file changed, 305 insertions(+), 222 deletions(-) diff --git a/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx index ce4feb743db..0ffc5ee5f6d 100644 --- a/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx +++ b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx @@ -11,8 +11,10 @@ /// /// \file ptSpectraInclusiveUpc.cxx /// \executable o2-analysis-ud-pt-spectra-inclusive-upc -/// \brief Task for the of pT spectra of pions, kaons and protons in inclusive UPC events. -/// Used to obtain the templates for the DCA_xy fits for the primary fractions +/// \brief Task for pT spectra of pions, kaons and protons in inclusive UPC events. +/// Used to obtain the templates for the DCA_xy fits for the primary fractions. +/// Fractions are obtained separately for TPC and TOF spectra: a particle passing both PID +/// selections will contribute to both histograms. /// /// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch @@ -50,10 +52,24 @@ struct PtSpectraInclusiveUpc { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histos"}; - Configurable nBinsDCAxy{"nBinsDCAxy", 100, "N bins in DCA_{xy} histos"}; + ConfigurableAxis ptBinning{"ptBinning", + {VARIABLE_WIDTH, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, + 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}, + "#it{p}_{T} (GeV/#it{c})"}; + + ConfigurableAxis dcaXYaxis{"dcaXYaxis", {1000, -0.6, 0.6}, "DCA_{xy} (cm) binning"}; Configurable applyKineCutsInGen{"applyKineCutsInGen", false, "Apply kinematic cuts in the generated level"}; + Configurable etaMax{"etaMax", 0.9, "Maximum track pseudorapidity"}; + Configurable yMax{"yMax", 0.9, "Maximum particle rapidity"}; + Configurable ptMin{"ptMin", 0.1, "Minimum track transverse momentum (GeV/c)"}; + Configurable nFindableMin{"nFindableMin", 70, "Minimum number of findable TPC clusters"}; + Configurable sigmaMax{"sigmaMax", 3., "Maximum absolute PID n-sigma"}; + Configurable dcaZlimit{"dcaZlimit", 2., "Maximum absolute DCA in z (cm)"}; + Configurable maxChi2TPC{"maxChi2TPC", 4., "Maximum TPC chi2 per cluster"}; + Configurable maxChi2ITS{"maxChi2ITS", 36., "Maximum ITS chi2 per cluster"}; + // define abbreviations using CCs = soa::Join; using CC = CCs::iterator; @@ -62,53 +78,172 @@ struct PtSpectraInclusiveUpc { using TCs = soa::Join; using TCMCs = soa::Join; - const double etaMax = 0.9; - const double yMax = 0.9; - const double ptMin = 0.1; - const int nFindableMin = 70; - const double sigmaMax = 3.; - const double dcaZlimit = 2.; - void init(InitContext const&) { // axes - const AxisSpec axisPt{nBinsPt, 0, 5, "#it{p}_{T} GeV/#it{c}"}; - const AxisSpec axisEventCounter{2, 0.5, 2.5, "Event type"}; - const AxisSpec axisDCAxy{nBinsDCAxy, -0.6, 0.6, "DCA_{xy} cm"}; + const AxisSpec axisPt{ + ptBinning, + "#it{p}_{T} (GeV/#it{c})", + "axisPt"}; + + const AxisSpec axisEventCounter{ + 2, + 0.5, + 2.5, + "Event type"}; + + const AxisSpec axisDCAxy{ + dcaXYaxis, + "DCA_{xy} (cm)"}; // histograms - histos.add("ptGeneratedPion", "ptGeneratedPion", kTH1F, {axisPt}); - histos.add("ptGeneratedKaon", "ptGeneratedKaon", kTH1F, {axisPt}); - histos.add("ptGeneratedProton", "ptGeneratedProton", kTH1F, {axisPt}); - - histos.add("ptReconstructedTPCPion", "ptReconstructedTPCPion", kTH1F, {axisPt}); - histos.add("ptReconstructedTPCKaon", "ptReconstructedTPCKaon", kTH1F, {axisPt}); - histos.add("ptReconstructedTPCProton", "ptReconstructedTPCProton", kTH1F, {axisPt}); - - histos.add("ptReconstructedTOFPion", "ptReconstructedTOFPion", kTH1F, {axisPt}); - histos.add("ptReconstructedTOFKaon", "ptReconstructedTOFKaon", kTH1F, {axisPt}); - histos.add("ptReconstructedTOFProton", "ptReconstructedTOFProton", kTH1F, {axisPt}); - - histos.add("ptDataTPCPion", "ptDataTPCPion", kTH1F, {axisPt}); - histos.add("ptDataTPCKaon", "ptDataTPCKaon", kTH1F, {axisPt}); - histos.add("ptDataTPCProton", "ptDataTPCProton", kTH1F, {axisPt}); - - histos.add("ptDataTOFPion", "ptDataTOFPion", kTH1F, {axisPt}); - histos.add("ptDataTOFKaon", "ptDataTOFKaon", kTH1F, {axisPt}); - histos.add("ptDataTOFProton", "ptDataTOFProton", kTH1F, {axisPt}); - - histos.add("myEventCounter", "myEventCounter", kTH1F, {axisEventCounter}); - histos.add("DCAxy_primary_pions", "DCAxy_primary_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_pions", "DCAxy_secondary_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_primary_kaons", "DCAxy_primary_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_kaons", "DCAxy_secondary_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_primary_protons", "DCAxy_primary_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_secondary_protons", "DCAxy_secondary_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_material_protons", "DCAxy_material_protons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_pions", "DCAxy_data_pions", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_kaons", "DCAxy_data_kaons", kTH1F, {axisDCAxy}); - histos.add("DCAxy_data_protons", "DCAxy_data_protons", kTH1F, {axisDCAxy}); + // generated pT spectra + histos.add("ptGeneratedPion", "Generated pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedKaon", "Generated kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedProton", "Generated protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TPC PID + histos.add("ptReconstructedTPCPion", "Reconstructed pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCKaon", "Reconstructed kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCProton", "Reconstructed protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TOF PID + histos.add("ptReconstructedTOFPion", "Reconstructed TOF pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFKaon", "Reconstructed TOF kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFProton", "Reconstructed TOF protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TPC PID + histos.add("ptDataTPCPion", "Data pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCKaon", "Data kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCProton", "Data protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TOF PID + histos.add("ptDataTOFPion", "Data pions (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFKaon", "Data kaons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFProton", "Data protons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + histos.add("myEventCounter", "Event counter;Event type;Counts", kTH1F, {axisEventCounter}); + + histos.add( + "DCAxy_TPC_primary_pions", + "Primary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_pions", + "Secondary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_kaons", + "Primary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_kaons", + "Secondary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_protons", + "Primary protons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_protons", + "Secondary protons from decays (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_material_protons", + "Secondary protons from material (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_pions", + "Data pion candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_kaons", + "Data kaon candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_protons", + "Data proton candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_pions", + "Primary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_pions", + "Secondary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_kaons", + "Primary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_kaons", + "Secondary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_protons", + "Primary protons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_protons", + "Secondary protons from decays (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_material_protons", + "Secondary protons from material (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_pions", + "Data pion candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_kaons", + "Data kaon candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_protons", + "Data proton candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); } void processSim(aod::UDMcCollision const&, aod::UDMcParticles const& mcParticles) @@ -151,10 +286,12 @@ struct PtSpectraInclusiveUpc { } } - void processReco(CCMC const&, TCMCs const& tracks, aod::UDMcParticles const&) + template + void selectDataAndFillHistos(TCollision const& /*collision*/, TTracks const& tracks) { double dcaXyLimit = 0; + bool passDCAxyCut = true; auto nSigmaPi = -999.; auto nSigmaKa = -999.; @@ -163,11 +300,23 @@ struct PtSpectraInclusiveUpc { std::array trackMomentum; for (const auto& track : tracks) { + passDCAxyCut = true; + if (!track.isPVContributor()) { continue; } - if (track.tpcNClsFindable() < nFindableMin) { + // here TPC selection on findable number of clusters + if (track.hasTPC()) { + if (track.tpcNClsFindable() < nFindableMin) { + continue; + } + if (track.tpcChi2NCl() > maxChi2TPC) { + continue; + } + } + + if (track.itsChi2NCl() > maxChi2ITS) { continue; } @@ -180,121 +329,150 @@ struct PtSpectraInclusiveUpc { } dcaXyLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (!(std::abs(track.dcaXY()) < dcaXyLimit)) { - continue; + if ((std::abs(track.dcaXY()) > dcaXyLimit)) { + passDCAxyCut = false; + } + + bool isPrimary = false; + bool isDecay = false; + if constexpr (isMc) { + if (!track.has_udMcParticle()) { + continue; + } + const auto mcParticle = track.udMcParticle(); + isPrimary = mcParticle.isPhysicalPrimary(); + isDecay = mcParticle.getProcess() == kPDecay; } trackMomentum[0] = track.px(); trackMomentum[1] = track.py(); trackMomentum[2] = track.pz(); - if (!track.has_udMcParticle()) { - continue; - } - auto mcParticle = track.udMcParticle(); - - bool hasTpc = false; + // compute rapidity for each ma // TPC tracks if (track.hasTPC()) { - hasTpc = true; nSigmaPi = track.tpcNSigmaPi(); nSigmaKa = track.tpcNSigmaKa(); nSigmaPr = track.tpcNSigmaPr(); if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCPion"), track.pt()); - histos.fill(HIST("DCAxy_primary_pions"), track.dcaXY()); - } else { - histos.fill(HIST("DCAxy_secondary_pions"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_pions"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCKaon"), track.pt()); - histos.fill(HIST("DCAxy_primary_kaons"), track.dcaXY()); - } else { - histos.fill(HIST("DCAxy_secondary_kaons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_kaons"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTPCProton"), track.pt()); - histos.fill(HIST("DCAxy_primary_protons"), track.dcaXY()); - } else { - if (mcParticle.getProcess() == kPDecay) { - histos.fill(HIST("DCAxy_secondary_protons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_protons"), track.pt(), track.dcaXY()); + } else { + if (isDecay) { + histos.fill(HIST("DCAxy_TPC_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_material_protons"), track.pt(), track.dcaXY()); + } + } } else { - histos.fill(HIST("DCAxy_material_protons"), track.dcaXY()); + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_protons"), track.pt(), track.dcaXY()); } } } } - // TPC tracks + // TOF tracks if (track.hasTOF()) { nSigmaPi = track.tofNSigmaPi(); nSigmaKa = track.tofNSigmaKa(); nSigmaPr = track.tofNSigmaPr(); if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFPion"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_pions"), track.dcaXY()); - } else { - if (!hasTpc) - histos.fill(HIST("DCAxy_secondary_pions"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_pions"), track.pt(), track.dcaXY()); + } } } - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFKaon"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_kaons"), track.dcaXY()); - } else { - if (!hasTpc) - histos.fill(HIST("DCAxy_secondary_kaons"), track.dcaXY()); + if (std::abs(nSigmaKa) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_kaons"), track.pt(), track.dcaXY()); + } } } if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("ptReconstructedTOFProton"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_primary_protons"), track.dcaXY()); - } else { - if (!hasTpc) { - if (mcParticle.getProcess() == kPDecay) { - histos.fill(HIST("DCAxy_secondary_protons"), track.dcaXY()); + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_protons"), track.pt(), track.dcaXY()); } else { - histos.fill(HIST("DCAxy_material_protons"), track.dcaXY()); + if (isDecay) { + histos.fill(HIST("DCAxy_TOF_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_material_protons"), track.pt(), track.dcaXY()); + } } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_protons"), track.pt(), track.dcaXY()); } } } @@ -302,109 +480,14 @@ struct PtSpectraInclusiveUpc { } } - void processData(CC const&, TCs const& tracks) + void processReco(CCMC const& collision, TCMCs const& tracks, aod::UDMcParticles const&) { + selectDataAndFillHistos(collision, tracks); + } - double dcaXyLimit = 0; - - auto nSigmaPi = -999.; - auto nSigmaKa = -999.; - auto nSigmaPr = -999.; - - std::array trackMomentum; - - for (const auto& track : tracks) { - if (!track.isPVContributor()) { - continue; - } - - if (track.tpcNClsFindable() < nFindableMin) { - continue; - } - - if (track.pt() < ptMin) { - continue; - } - - if (!(std::abs(track.dcaZ()) < dcaZlimit)) { - continue; - } - - dcaXyLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (!(std::abs(track.dcaXY()) < dcaXyLimit)) { - continue; - } - - trackMomentum[0] = track.px(); - trackMomentum[1] = track.py(); - trackMomentum[2] = track.pz(); - - bool hasTpc = false; - // TPC tracks - if (track.hasTPC()) { - hasTpc = true; - nSigmaPi = track.tpcNSigmaPi(); - nSigmaKa = track.tpcNSigmaKa(); - nSigmaPr = track.tpcNSigmaPr(); - - if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCPion"), track.pt()); - histos.fill(HIST("DCAxy_data_pions"), track.dcaXY()); - } - - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCKaon"), track.pt()); - histos.fill(HIST("DCAxy_data_kaons"), track.dcaXY()); - } - - if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTPCProton"), track.pt()); - histos.fill(HIST("DCAxy_data_protons"), track.dcaXY()); - } - } - - // TPC tracks - if (track.hasTOF()) { - nSigmaPi = track.tofNSigmaPi(); - nSigmaKa = track.tofNSigmaKa(); - nSigmaPr = track.tofNSigmaPr(); - - if (std::abs(nSigmaPi) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFPion"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_pions"), track.dcaXY()); - } - if (std::abs(nSigmaKa) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFKaon"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_kaons"), track.dcaXY()); - } - - if (std::abs(nSigmaPr) < sigmaMax) { - if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) > yMax) { - continue; - } - histos.fill(HIST("ptDataTOFProton"), track.pt()); - if (!hasTpc) - histos.fill(HIST("DCAxy_data_protons"), track.dcaXY()); - } - } - } + void processData(CC const& collision, TCs const& tracks) + { + selectDataAndFillHistos(collision, tracks); } PROCESS_SWITCH(PtSpectraInclusiveUpc, processSim, "processSim", false); From bf21b8fd84d840d709f64609a4d5a469ef509cc7 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Fri, 9 Oct 2026 23:44:03 +0200 Subject: [PATCH 243/648] [PWGLF,PWGMM] Fix histo dimension (#18237) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index a42e293165f..00ff92651f5 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -580,8 +580,8 @@ struct DndetaMFTPbPb { registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); - registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); } From e996e7b66dbc9c44c0dcc7ae57ed60265d9d741b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Mart=C3=ADnez=20Garc=C3=ADa?= Date: Fri, 9 Oct 2026 16:13:41 -0600 Subject: [PATCH 244/648] [PWGUD] Resolving PID issues (#18209) Co-authored-by: rolavick --- PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx index 16a473cc0b0..6503f39c085 100644 --- a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx +++ b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx @@ -395,7 +395,7 @@ struct UpcPhotonuclearAnalysisJMG { } std::vector vtxBinsEdges{VARIABLE_WIDTH, -10.0f, -7.0f, -5.0f, -2.5f, 0.0f, 2.5f, 5.0f, 7.0f, 10.0f}; - std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; + // std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; enum EventType { SameEvent = 1, @@ -706,7 +706,7 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); - float pTotal = momentum(track.px(), track.py(), track.pz()) * track.sign(); + float pTotal = momentum(track.px(), track.py(), track.pz()); float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideA/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideA/hTrackPhi"), phiVal); @@ -755,7 +755,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideA/hTrackPV"), nchPVGapSideA, nchGapSideA); nTracksChargedSideA = nTracksCharged; multiplicitySideA = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; case 1: // gap for side C if (isCollisionCutSG(reconstructedCollision, 1) == false) { @@ -782,7 +781,7 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); - float pTotal = momentum(track.px(), track.py(), track.pz()) * track.sign(); + float pTotal = momentum(track.px(), track.py(), track.pz()); float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideC/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideC/hTrackPhi"), phiVal); @@ -831,7 +830,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideC/hTrackPV"), nchPVGapSideC, nchGapSideC); nTracksChargedSideC = nTracksCharged; multiplicitySideC = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; default: return; @@ -871,16 +869,16 @@ struct UpcPhotonuclearAnalysisJMG { vTrackChargeSideC, vTrackEtaSideC, vTrackPhiSideC, - vTrackTPCSignalSideA, - vTrackTOFSignalSideA, - vTrackTPCNSigmaPiSideA, - vTrackTOFNSigmaPiSideA, - vTrackTPCNSigmaKaSideA, - vTrackTOFNSigmaKaSideA, - vTrackTPCNSigmaProSideA, - vTrackTOFNSigmaProSideA, - vTrackTOFBetaSideA, - vTrackTOFBetaErrorSideA, + vTrackTPCSignalSideC, + vTrackTOFSignalSideC, + vTrackTPCNSigmaPiSideC, + vTrackTOFNSigmaPiSideC, + vTrackTPCNSigmaKaSideC, + vTrackTOFNSigmaKaSideC, + vTrackTPCNSigmaProSideC, + vTrackTOFNSigmaProSideC, + vTrackTOFBetaSideC, + vTrackTOFBetaErrorSideC, nTracksChargedSideA, multiplicitySideA, nTracksChargedSideC, @@ -924,9 +922,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = tracks1.size(); - if (fillCollisionUD(mixed, multiplicity) == false) { - return; - } + // if (fillCollisionUD(mixed, multiplicity) == false) { + // return; + // } + fillCollisionUD(mixed, multiplicity); histos.fill(HIST("Events/hCountCollisionsMixed"), 2); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ()})); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ(), collision1.gapSide()})); @@ -1122,9 +1121,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = reconstructedTracks.size(); - if (fillCollisionUD(same, multiplicity) == false) { - return; - } + // if (fillCollisionUD(same, multiplicity) == false) { + // return; + // } + fillCollisionUD(same, multiplicity); // LOGF(debug, "Filling same events"); histos.fill(HIST("eventcount"), -2); if (minMultiplicity <= multiplicity && multiplicity <= range1Max) { From d56fd1ad8abcb2fa79db46c0b8eacac4485a1e9f Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sat, 10 Oct 2026 07:26:18 +0800 Subject: [PATCH 245/648] [PWGLF] Add an additional systematic uncertainty table, and also consider another method for removing shared tracks. (#18238) --- PWGLF/DataModel/LFSpincorrelationTables.h | 16 +- .../Strangeness/lambdaspincorrelation.cxx | 22 +- .../Strangeness/lambdaspincorrderived.cxx | 821 +++++++++++++++++- 3 files changed, 855 insertions(+), 4 deletions(-) diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index d4fdffd2442..fa7e2061f3e 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -53,6 +53,12 @@ DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Ph DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex +DECLARE_SOA_COLUMN(TpcPosNClsCrossedRows, tpcPosNClsCrossedRows, float); +DECLARE_SOA_COLUMN(TpcNegNClsCrossedRows, tpcNegNClsCrossedRows, float); +DECLARE_SOA_COLUMN(Ctau, ctau, float); +DECLARE_SOA_COLUMN(TpcNsigmaPos, tpcNsigmaPos, float); +DECLARE_SOA_COLUMN(TpcNsigmaNeg, tpcNsigmaNeg, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", @@ -74,7 +80,13 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonPhi, lambdapair::ProtonIndex, lambdapair::PionIndex, - lambdapair::DcaV0ToPV); + lambdapair::DcaV0ToPV, + lambdapair::TpcPosNClsCrossedRows, + lambdapair::TpcNegNClsCrossedRows, + lambdapair::Ctau, + lambdapair::TpcNsigmaPos, + lambdapair::TpcNsigmaNeg, + lambdapair::MK0Short); using LambdaPair = LambdaPairs::iterator; @@ -109,6 +121,8 @@ DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proto DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex + +//! TPC n sigma } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index fcb3bf21aff..615c4be269d 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -299,6 +299,13 @@ struct lambdaspincorrelation { std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; std::vector dcaV0ToPV = {}; + std::vector tpcPosNClsCrossedRows = {}; + std::vector tpcNegNClsCrossedRows = {}; + std::vector ctau = {}; + std::vector tpcNsigmaPos = {}; + std::vector tpcNsigmaNeg = {}; + std::vector mK0Short = {}; + int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -350,6 +357,9 @@ struct lambdaspincorrelation { if (aLambdaTag) { histos.fill(HIST("hV0Info"), 3.5); } + + float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassLambda); + // LOGF(info, "v0 index2: (%d)", v0.index()); auto postrack1 = v0.template posTrack_as(); auto negtrack1 = v0.template negTrack_as(); @@ -357,6 +367,16 @@ struct lambdaspincorrelation { negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); + + tpcPosNClsCrossedRows.push_back(postrack1.tpcNClsCrossedRows()); + tpcNegNClsCrossedRows.push_back(negtrack1.tpcNClsCrossedRows()); + ctau.push_back(ctauLambda); + + tpcNsigmaPos.push_back(std::abs(postrack1.tpcNSigmaPr())); + tpcNsigmaNeg.push_back(std::abs(negtrack1.tpcNSigmaPi())); + + mK0Short.push_back(v0.mK0Short()); + v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -393,7 +413,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5), tpcPosNClsCrossedRows.at(i5), tpcNegNClsCrossedRows.at(i5), ctau.at(i5), tpcNsigmaPos.at(i5), tpcNsigmaNeg.at(i5), mK0Short.at(i5)); } } } diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 04e4687225c..ede587a496c 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -59,6 +59,42 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; +template +static std::vector getCleanCandidateIndices( + const Container& candidates, + SelectionFunc&& passSelection) +{ + std::vector selected; + selected.reserve(candidates.size()); + + for (auto const& c : candidates) { + if (passSelection(c)) { + selected.push_back(c.globalIndex()); + } + } + + std::vector dirty(selected.size(), false); + for (size_t i = 0; i < selected.size(); ++i) { + for (size_t j = i + 1; j < selected.size(); ++j) { + auto a = candidates.iteratorAt(selected[i]); + auto b = candidates.iteratorAt(selected[j]); + if (hasSharedDaughters(a, b)) { + dirty[i] = true; + dirty[j] = true; + } + } + } + + std::vector clean; + clean.reserve(selected.size()); + for (size_t i = 0; i < selected.size(); ++i) { + if (!dirty[i]) { + clean.push_back(selected[i]); + } + } + return clean; +} + static inline double getKStar( const ROOT::Math::PtEtaPhiMVector& p1, const ROOT::Math::PtEtaPhiMVector& p2) @@ -350,7 +386,18 @@ struct lambdaspincorrderived { Configurable dcaPion{"dcaPion", 0.2, "DCA Pion"}; Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; - Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; + Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.5, "Eta cut on mix event buffer"}; + + // TPC crossed rows + Configurable minTpcNClsCrossedRows{"minTpcNClsCrossedRows", 70.f, "Minimum TPC crossed rows for daughter"}; + + // ctau (lifetime) + Configurable maxCtau{"maxCtau", 30.f, "Maximum ctau of Lambda candidate (cm)"}; + + // TPC nsigma + Configurable maxTpcNsigma{"maxTpcNsigma", 4.f, "Maximum |TPC nsigma| for Lambda daughters"}; + + Configurable confK0sMassWindow{"confK0sMassWindow", 0.01, "K0s competing mass rejection window (GeV/c2)"}; } v0Configurations; @@ -543,6 +590,8 @@ struct lambdaspincorrderived { histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + histos.add("hNMEperSE_leg1", "ME replacements per SE pair, leg1", HistType::kTH1D, {{12, -0.5, 11.5}}, true); + histos.add("hNMEperSE_leg2", "ME replacements per SE pair, leg2", HistType::kTH1D, {{12, -0.5, 11.5}}, true); } if (fillHistConfig.fillWeightQAHistos) { @@ -858,6 +907,34 @@ struct lambdaspincorrderived { if (candidate.lambdaPt() > ptMax) { return false; } + + // additional cuts + if (candidate.v0Status() == 0 && (std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma)) { + return false; + } + if (candidate.v0Status() == 1 && (std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma)) { + return false; + } + + // ---- K0s competing mass rejection ---- + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { + return false; + } + + /* + if (candidate.tpcPosNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows ) { + return false; + } + + if (candidate.tpcNegNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows ) { + return false; + } + + if (candidate.ctau() > v0Configurations.maxCtau ) { + return false; + } + */ + return true; } @@ -1749,6 +1826,744 @@ struct lambdaspincorrderived { } PROCESS_SWITCH(lambdaspincorrderived, processData, "Process data", true); + void processDataShareDaughter(EventCandidates::iterator const& collision, AllTrackCandidates const& V0s) + { + auto centrality = collision.cent(); + + // ------------------------------------------------------------ + // Candidate-level shared-daughter rejection + // + // First collect all candidates in this event that pass selectionV0, + // then flag every candidate that shares a daughter track with any + // other selected candidate. Only candidates that are not flagged + // are kept for the subsequent SE pairing. + // + // This corresponds to the lambdaSharingDauFlag + trueLambdaFlag + // approach used in Subhadeep Roy's analysis: a candidate that + // shares a daughter track is removed entirely, rather than only + // vetoing the specific pair that shares the track. + // + // Implementation note: we store index() (not globalIndex()) because + // V0s.iteratorAt() expects the row index within the current table. + // ------------------------------------------------------------ + std::vector selected; + selected.reserve(V0s.size()); + + for (auto const& v0 : V0s) { + if (selectionV0(v0)) { + selected.push_back(v0.index()); + } + } + + std::vector isDirty(selected.size(), false); + for (size_t i = 0; i < selected.size(); ++i) { + for (size_t j = i + 1; j < selected.size(); ++j) { + auto a = V0s.iteratorAt(static_cast(selected[i])); + auto b = V0s.iteratorAt(static_cast(selected[j])); + if (hasSharedDaughters(a, b)) { + isDirty[i] = true; + isDirty[j] = true; + } + } + } + + std::vector clean; + clean.reserve(selected.size()); + for (size_t i = 0; i < selected.size(); ++i) { + if (!isDirty[i]) { + clean.push_back(selected[i]); + } + } + + // ------------------------------------------------------------ + // SE pairing on clean candidates only + // ------------------------------------------------------------ + for (size_t i = 0; i < clean.size(); ++i) { + const auto& v0 = V0s.iteratorAt(static_cast(clean[i])); + + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); + } + + proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), + o2::constants::physics::MassProton); + lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), + v0.lambdaMass()); + + const double phi = RecoDecay::constrainAngle(v0.lambdaPhi(), 0.0F, harmonic); + const double eta = v0.lambdaEta(); + + if (v0.v0Status() == 0) { + histos.fill(HIST("hEtaPhiLambdaRaw"), phi, eta, + getNUAWeight(0, v0.lambdaPhi(), v0.lambdaEta())); + } else { + histos.fill(HIST("hEtaPhiAntiLambdaRaw"), phi, eta, + getNUAWeight(1, v0.lambdaPhi(), v0.lambdaEta())); + } + + for (size_t j = i + 1; j < clean.size(); ++j) { + const auto& v02 = V0s.iteratorAt(static_cast(clean[j])); + + // Note: hasSharedDaughters(v0, v02) is no longer needed here. + // All clean candidates are already guaranteed not to share a + // daughter track with any other selected candidate. + + proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), + o2::constants::physics::MassProton); + lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), + v02.lambdaMass()); + + if ((v0.v0Status() == 0 && v02.v0Status() == 1) || + (v0.v0Status() == 1 && v02.v0Status() == 0)) { + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiSame"), + RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), + -TMath::Pi(), harmonicDphi)); + } + } + + // const int ptype = pairTypeCode(v0.v0Status(), v02.v0Status()); + if (v0.v0Status() == 0 && v02.v0Status() == 0) { + fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0); + } + if (v0.v0Status() == 0 && v02.v0Status() == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 0, 1.0); + } + if (v0.v0Status() == 1 && v02.v0Status() == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 0, 1.0); + } + if (v0.v0Status() == 1 && v02.v0Status() == 1) { + fillHistograms(1, 1, lambda, lambda2, proton, proton2, 0, 1.0); + } + } + } + } + PROCESS_SWITCH(lambdaspincorrderived, processDataShareDaughter, "Process data share daughter", true); + + void processMEV6ShareDaughter(EventCandidates const& collisions, AllTrackCandidates const& V0s) + { + MixBinnerR mb{ + ptMin.value, + ptMax.value, + ptMix.value, + v0Configurations.v0etaMixBuffer.value, + etaMix.value, + phiMix.value, + MassMin.value, + MassMax.value, + cfgV5MassBins.value, + cfgMixRadiusParam.cfgMixRadiusBins.value}; + + const int nCol = colBinning.getAllBinsCount(); + const int nStat = N_STATUS; + const int nPt = mb.nPt(); + const int nEta = mb.nEta(); + const int nPhi = mb.nPhi(); + const int nM = mb.nM(); + const int nR = mb.nR(); + + const size_t nKeys = static_cast(nCol) * nStat * nPt * nEta * nPhi * nM * nR; + std::vector> buffer(nKeys); + + // -------- PASS 1: fill buffer -------- + for (auto const& col : collisions) { + const int colBin = colBinning.getBin(std::make_tuple(col.posz(), col.cent())); + if (colBin < 0) { + continue; + } + + auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); + + // ------------------------------------------------------------ + // Candidate-level shared-daughter rejection (PASS 1) + // + // Only clean candidates are inserted into the mixing buffer. + // A candidate is dirty if it shares a daughter track with any + // other selected candidate in the same event. + // + // Implementation note: we store index() (not globalIndex()) because + // slice.iteratorAt() expects the row index within the current table. + // ------------------------------------------------------------ + std::vector selected; + selected.reserve(slice.size()); + for (auto const& t : slice) { + if (selectionV0(t)) { + selected.push_back(t.index()); + } + } + + std::vector isDirty(selected.size(), false); + for (size_t i = 0; i < selected.size(); ++i) { + for (size_t j = i + 1; j < selected.size(); ++j) { + auto a = slice.iteratorAt(static_cast(selected[i])); + auto b = slice.iteratorAt(static_cast(selected[j])); + if (hasSharedDaughters(a, b)) { + isDirty[i] = true; + isDirty[j] = true; + } + } + } + + for (size_t i = 0; i < selected.size(); ++i) { + if (isDirty[i]) { + continue; + } + const auto& t = slice.iteratorAt(static_cast(selected[i])); + + const int status = static_cast(t.v0Status()); + if (status < 0 || status >= nStat) { + continue; + } + + const int ptB = mb.ptBin(t.lambdaPt()); + + int etaB = mb.etaBin(t.lambdaEta()); + if (userapidity) { + const auto lv = ROOT::Math::PtEtaPhiMVector(t.lambdaPt(), t.lambdaEta(), t.lambdaPhi(), t.lambdaMass()); + etaB = mb.etaBin(lv.Rapidity()); + } + + const int phiB = mb.phiBin(RecoDecay::constrainAngle(t.lambdaPhi(), 0.0, harmonic)); + const int mB = getMassMixClassFromEdges(t.lambdaMass(), massMixEdges.value); + const int rB = mb.radiusBin(t.v0Radius()); + + if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { + continue; + } + + const size_t key = linearKeyR(colBin, status, ptB, etaB, phiB, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR); + + buffer[key].push_back(BufferCandR{ + .collisionIdx = static_cast(col.index()), + .rowIndex = static_cast(t.globalIndex()), + .v0Status = static_cast(status), + .ptBin = static_cast(ptB), + .etaBin = static_cast(etaB), + .phiBin = static_cast(phiB), + .mBin = static_cast(mB), + .rBin = static_cast(rB)}); + } + } + + const int nN_pt = std::max(0, cfgV5NeighborPt.value); + const int nN_eta = std::max(0, cfgV5NeighborEta.value); + const int nN_phi = std::max(0, cfgV5NeighborPhi.value); + + std::vector ptBins, etaBins, phiBins; + std::vector matches1, matches2; + matches1.reserve(256); + matches2.reserve(256); + + auto collectMatchesForReplacedLeg = [&](auto const& tRep, auto const& tKeep, int colBin, int64_t curColIdx, std::vector& matches) { + matches.clear(); + + const int status = static_cast(tRep.v0Status()); + if (status < 0 || status >= nStat) { + return; + } + + const int ptB = mb.ptBin(tRep.lambdaPt()); + + int etaB = mb.etaBin(tRep.lambdaEta()); + if (userapidity) { + const auto lv = ROOT::Math::PtEtaPhiMVector(tRep.lambdaPt(), tRep.lambdaEta(), tRep.lambdaPhi(), tRep.lambdaMass()); + etaB = mb.etaBin(lv.Rapidity()); + } + + const int phiB = mb.phiBin(RecoDecay::constrainAngle(tRep.lambdaPhi(), 0.0, harmonic)); + const int mB = getMassMixClassFromEdges(tRep.lambdaMass(), massMixEdges.value); + const int rB = mb.radiusBin(tRep.v0Radius()); + + if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { + return; + } + auto collectFromBins = [&](const std::vector& ptUseBins, + const std::vector& etaUseBins, + const std::vector& phiUseBins) { + for (const auto& ptUse : ptUseBins) { + for (const auto& etaUse : etaUseBins) { + for (const auto& phiUse : phiUseBins) { + const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR)]; + + for (auto const& bc : vec) { + if (bc.collisionIdx == curColIdx) + continue; + + auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); + + if (!selectionV0(tX)) + continue; + if (!checkKinematics(tRep, tX)) + continue; + + if (tX.globalIndex() == tRep.globalIndex()) + continue; + if (tX.globalIndex() == tKeep.globalIndex()) + continue; + + if (hasSharedDaughters(tX, tKeep)) + continue; + if (hasSharedDaughters(tX, tRep)) + continue; + + matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); + } + } + } + } + }; + + matches.clear(); + + // 1) exact bin first + ptBins.clear(); + etaBins.clear(); + phiBins.clear(); + + ptBins.push_back(ptB); + etaBins.push_back(etaB); + phiBins.push_back(phiB); + + collectFromBins(ptBins, etaBins, phiBins); + + // 2) if exact bin gives fewer than required matches, also search neighbors + const int targetMatches = (cfgV5MaxMatches.value > 0) ? cfgV5MaxMatches.value : 1; + + if ((int)matches.size() < targetMatches) { + std::vector ptBinsN, etaBinsN, phiBinsN; + collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBinsN); + collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBinsN); + collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBinsN); + + for (const auto& ptUse : ptBinsN) { + for (const auto& etaUse : etaBinsN) { + for (const auto& phiUse : phiBinsN) { + if (ptUse == ptB && etaUse == etaB && phiUse == phiB) + continue; + + const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR)]; + + for (auto const& bc : vec) { + if (bc.collisionIdx == curColIdx) + continue; + + auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); + + if (!selectionV0(tX)) + continue; + if (!checkKinematics(tRep, tX)) + continue; + + if (tX.globalIndex() == tRep.globalIndex()) + continue; + if (tX.globalIndex() == tKeep.globalIndex()) + continue; + + if (hasSharedDaughters(tX, tKeep)) + continue; + if (hasSharedDaughters(tX, tRep)) + continue; + + matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); + } + } + } + } + } + + std::sort(matches.begin(), matches.end(), + [](auto const& a, auto const& b) { + return std::tie(a.collisionIdx, a.rowIndex) < std::tie(b.collisionIdx, b.rowIndex); + }); + matches.erase(std::unique(matches.begin(), matches.end(), + [](auto const& a, auto const& b) { + return a.collisionIdx == b.collisionIdx && a.rowIndex == b.rowIndex; + }), + matches.end()); + }; + + auto downsampleMatches = [&](std::vector& matches, uint64_t seedBase) { + if (cfgV5MaxMatches.value > 0 && (int)matches.size() > cfgV5MaxMatches.value) { + uint64_t seed = cfgMixSeed.value ^ splitmix64(seedBase); + const int K = cfgV5MaxMatches.value; + for (int i = 0; i < K; ++i) { + seed = splitmix64(seed); + const int j = i + (int)(seed % (uint64_t)(matches.size() - i)); + std::swap(matches[i], matches[j]); + } + matches.resize(K); + } + }; + + const size_t pendingAtStart = v6Pending.data.size(); + size_t pendingMatched = 0; + size_t pendingExpired = 0; + size_t pendingAdded = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.data.clear(); + } else { + for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpired; + it = v6Pending.data.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatched; + it = v6Pending.data.erase(it); + } + } + + // -------- PASS 2: configurable one-leg / two-leg mixing -------- + for (auto const& col1 : collisions) { + const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); + if (colBin < 0) { + continue; + } + + const int64_t curColIdx = static_cast(col1.index()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); + + // ------------------------------------------------------------ + // Candidate-level shared-daughter rejection (PASS 2) + // + // Build the clean set for this event so that SE pairing only + // uses clean candidates. The mixing buffer already contains + // only clean candidates, so matched replacements are clean. + // + // Implementation note: we store index() (not globalIndex()) because + // poolA.iteratorAt() expects the row index within the current table. + // ------------------------------------------------------------ + std::vector selectedA; + selectedA.reserve(poolA.size()); + for (auto const& t : poolA) { + if (selectionV0(t)) { + selectedA.push_back(t.index()); + } + } + + std::vector dirtyA(selectedA.size(), false); + for (size_t i = 0; i < selectedA.size(); ++i) { + for (size_t j = i + 1; j < selectedA.size(); ++j) { + auto a = poolA.iteratorAt(static_cast(selectedA[i])); + auto b = poolA.iteratorAt(static_cast(selectedA[j])); + if (hasSharedDaughters(a, b)) { + dirtyA[i] = true; + dirtyA[j] = true; + } + } + } + + // Build a set of clean row indices for fast lookup in the pairing loop. + // We use index() here, matching what soa::combinations yields via t.index(). + std::unordered_set cleanIdxSet; + cleanIdxSet.reserve(selectedA.size()); + for (size_t i = 0; i < selectedA.size(); ++i) { + if (!dirtyA[i]) { + cleanIdxSet.insert(selectedA[i]); + } + } + + for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { + if (!selectionV0(t1) || !selectionV0(t2)) { + continue; + } + if (!cleanIdxSet.count(t1.index()) || !cleanIdxSet.count(t2.index())) { + continue; + } + if (t2.index() <= t1.index()) { + continue; + } + + // Note: hasSharedDaughters(t1, t2) is no longer needed here. + // Both t1 and t2 are guaranteed to be clean candidates. + + const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); + const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + + // Fill TGT maps before searching for replacements. This makes TGT the + // true same-event target phase space for the selected pair, matching the + // hPtYSame definition. REP below is filled only for accepted replacements. + if (doMixLeg1) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + } + + if (doMixLeg1) { + collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); + limitMatchesToNEvents(matches1, nEvtMixing.value); + downsampleMatches(matches1, (uint64_t)t1.globalIndex() ^ (splitmix64((uint64_t)t2.globalIndex()) + 0x111ULL) ^ splitmix64((uint64_t)curColIdx)); + } else { + matches1.clear(); + } + + if (doMixLeg2) { + collectMatchesForReplacedLeg(t2, t1, colBin, curColIdx, matches2); + limitMatchesToNEvents(matches2, nEvtMixing.value); + downsampleMatches(matches2, (uint64_t)t2.globalIndex() ^ (splitmix64((uint64_t)t1.globalIndex()) + 0x222ULL) ^ splitmix64((uint64_t)curColIdx)); + } else { + matches2.clear(); + } + + // Do not fill TGT here. TGT has already been filled above as the + // selected same-event target phase space, independent of replacement success. + int nFill1 = 0; + int nFill2 = 0; + // count actual accepted fills for leg-1 replacement + if (doMixLeg1) { + for (auto const& m : matches1) { + auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tX)) + continue; + if (tX.v0Status() != t1.v0Status()) + continue; + if (!checkKinematics(t1, tX)) + continue; + if (tX.globalIndex() == t1.globalIndex()) + continue; + if (tX.globalIndex() == t2.globalIndex()) + continue; + if (hasSharedDaughters(tX, t2)) + continue; + if (hasSharedDaughters(tX, t1)) + continue; + ++nFill1; + } + } + + // count actual accepted fills for leg-2 replacement + if (doMixLeg2) { + for (auto const& m : matches2) { + auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tY)) + continue; + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) + continue; + if (tY.globalIndex() == t2.globalIndex()) + continue; + if (tY.globalIndex() == t1.globalIndex()) + continue; + if (hasSharedDaughters(tY, t2)) + continue; + if (hasSharedDaughters(tY, t1)) + continue; + ++nFill2; + } + } + + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + } + histos.fill(HIST("hNMEperSE_leg2"), nFill2); + histos.fill(HIST("hNMEperSE_leg1"), nFill1); + + if (nFill1 <= 0 && nFill2 <= 0) { + continue; + } + // Residual-weight QA needs a leg-specific normalization: + // TGT_leg is filled once per same-event target candidate with weight 1. + // REP_leg is the average replacement distribution for that same target. + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; + + // Do not fill target maps here: TGT has already been filled above, + // before searching for replacements, so that TGT represents all selected + // same-event targets rather than only targets with successful replacements. + + if (doMixLeg1 && nFill1 > 0) { + for (auto const& m : matches1) { + auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tX)) { + continue; + } + if (tX.v0Status() != t1.v0Status()) + continue; + if (!checkKinematics(t1, tX)) + continue; + if (tX.globalIndex() == t1.globalIndex()) + continue; + if (tX.globalIndex() == t2.globalIndex()) + continue; + if (hasSharedDaughters(tX, t1)) + continue; + if (hasSharedDaughters(tX, t2)) + continue; + fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), + wSELeg1); + // Fixed leg for leg1 replacement is original t2. + // Fill only for successful replacement. + fillFixedLegControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + wSELeg1); + auto proton = ROOT::Math::PtEtaPhiMVector(tX.protonPt(), tX.protonEta(), tX.protonPhi(), o2::constants::physics::MassProton); + auto lambda = ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()); + auto proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton); + auto lambda2 = ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()); + + // const int ptype = pairTypeCode(tX.v0Status(), t2.v0Status()); + + const float meWeight = finalMixWeightLeg1; + const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); + if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = tX.v0Status(); + const int s2 = t2.v0Status(); + + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 2, 1); + } else { + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); + } + } + } + + if (doMixLeg2 && nFill2 > 0) { + for (auto const& m : matches2) { + auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tY)) { + continue; + } + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) + continue; + if (tY.globalIndex() == t2.globalIndex()) + continue; + if (tY.globalIndex() == t1.globalIndex()) + continue; + if (hasSharedDaughters(tY, t1)) + continue; + if (hasSharedDaughters(tY, t2)) + continue; + fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), + wSELeg2); + // Fixed leg for leg2 replacement is original t1. + // Fill only for successful replacement. + fillFixedLegControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + wSELeg2); + auto proton = ROOT::Math::PtEtaPhiMVector(t1.protonPt(), t1.protonEta(), t1.protonPhi(), o2::constants::physics::MassProton); + auto lambda = ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()); + auto proton2 = ROOT::Math::PtEtaPhiMVector(tY.protonPt(), tY.protonEta(), tY.protonPhi(), o2::constants::physics::MassProton); + auto lambda2 = ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()); + + // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); + const float meWeight = finalMixWeightLeg2; + const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = t1.v0Status(); + const int s2 = tY.v0Status(); + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 1, 2); + } else { + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); + } + } + } + } + } + if (cfgV6LogPending) { + LOGF(info, "MEV6 data pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStart, pendingMatched, pendingExpired, pendingAdded, v6Pending.data.size()); + } + } + PROCESS_SWITCH(lambdaspincorrderived, processMEV6ShareDaughter, "Process data ME v6 with radius buffer share daughter", true); + void processDataSys(EventCandidates::iterator const&, AllTrackCandidates const& V0s) { for (const auto& v0 : V0s) { @@ -1951,7 +2766,7 @@ struct lambdaspincorrderived { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; Preslice tracksPerCollisionV0 = aod::lambdapair::lambdaeventId; void processMEV3(EventCandidates const& collisions, AllTrackCandidates const& V0s) @@ -3190,6 +4005,8 @@ struct lambdaspincorrderived { ++pendingAdded; } } + histos.fill(HIST("hNMEperSE_leg2"), nFill2); + histos.fill(HIST("hNMEperSE_leg1"), nFill1); if (nFill1 <= 0 && nFill2 <= 0) { continue; From b43613443a5f4679fe8ae5e3f342ce772bd41e08 Mon Sep 17 00:00:00 2001 From: spucillo <93769017+spucillo@users.noreply.github.com> Date: Sat, 10 Oct 2026 01:27:48 +0200 Subject: [PATCH 246/648] [PWGLF] Update MC centrality (#18233) --- .../cascadeAnalysisLightIonsDerivedData.cxx | 193 ++++++++++++++---- 1 file changed, 150 insertions(+), 43 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx index 33d3c9c51b0..bde34b181eb 100644 --- a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx +++ b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx @@ -17,6 +17,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" @@ -56,7 +57,7 @@ using SimCollisions = soa::Join; using CascadeMCCandidates = soa::Join; using DaughterTracks = soa::Join; -using CollisionMCTrueTable = soa::Join; +using CollisionMCTrueTable = soa::Join; using CascadeMCCores = soa::Join; struct CascadeAnalysisLightIonsDerivedData { @@ -151,6 +152,7 @@ struct CascadeAnalysisLightIonsDerivedData { // Centrality estimator Configurable centralityEstimator{"centralityEstimator", 0, "0 = FT0C, 1 = FTOM, 2 = FV0A, 3 = NGlobal"}; + Configurable useMcCentrality{"useMcCentrality", false, "Use MC centrality?"}; // List of estimators enum Option { kFT0C, @@ -299,6 +301,8 @@ struct CascadeAnalysisLightIonsDerivedData { // Multiplicity Histograms registryMC.add("hCentEstimator_truerec", "hCentEstimator_truerec", HistType::kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNch_truerec", "hCentralityVsNch_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); + if (useMcCentrality) + registryMC.add("hEventVsGenCentrality_truerec", "hEventVsGenCentrality_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, {101, 0.0f, 101.0f}}); if (centralityEstimator == Option::kNGlobal) registryMC.add("hCentralityVsMultiplicity_truerec", "hCentralityVsMultiplicity_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); else @@ -322,27 +326,46 @@ struct CascadeAnalysisLightIonsDerivedData { // QC Histograms registryQC.add("hVertexZGen", "hVertexZGen", HistType::kTH1D, {{vertexZAxis}}); // Histograms for xi (mc) - registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenXiMinus", "h2dGenXiMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenXiPlus", "h2dGenXiPlus", HistType::kTH2D, {centAxis, ptAxis}); // Histograms for omega (mc) - registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenOmegaMinus", "h2dGenOmegaMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenOmegaPlus", "h2dGenOmegaPlus", HistType::kTH2D, {centAxis, ptAxis}); - // Histograms for event loss/splitting - registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {{axisNch}, {4, -0.5f, +3.5f}}); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + if (useMcCentrality) { + registryMC.add("h2dGenXiMinusVsMcCentrality_RecoedEvt", "h2dGenXiMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality_RecoedEvt", "h2dGenXiPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiMinusVsMcCentrality", "h2dGenXiMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality", "h2dGenXiPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMcCentrality_RecoedEvt", "h2dGenOmegaMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality_RecoedEvt", "h2dGenOmegaPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMcCentrality", "h2dGenOmegaMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality", "h2dGenOmegaPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("hGenEventsVsMcCentrality", "hGenEventsVsMcCentrality", HistType::kTH2D, {axisCentEstimator, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } else { + registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {axisNch, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } registryMC.add("hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", HistType::kTH2D, {centAxis, {50, -0.5f, 49.5f}}); @@ -684,12 +707,29 @@ struct CascadeAnalysisLightIonsDerivedData { if (applyZVtxSelOnMCPV && std::fabs(mcCollision.posZ()) > zVtx) continue; - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + float centralityMC = -1.f; + if (centralityEstimator == Option::kFT0C) + centralityMC = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = mcCollision.centNGlobal(); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 0 /* all gen. events*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); if (requireInel0OnMC && mcCollision.multMCNParticlesEta10() < 1) { continue; } - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 1 /* all gen. events in INEL > 0*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); // Check if there is at least one of the reconstructed collisions associated to this MC collision @@ -740,14 +780,18 @@ struct CascadeAnalysisLightIonsDerivedData { if (biggestNContribs < collision.multPVTotalContributors()) { biggestNContribs = collision.multPVTotalContributors(); - if (centralityEstimator == Option::kFT0C) - centralitydata = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralitydata = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralitydata = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralitydata = collision.centNGlobal(); + if (useMcCentrality) { + centralitydata = centralityMC; + } else { + if (centralityEstimator == Option::kFT0C) + centralitydata = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralitydata = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralitydata = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralitydata = collision.centNGlobal(); + } } nCollisions++; @@ -761,8 +805,13 @@ struct CascadeAnalysisLightIonsDerivedData { registryQC.fill(HIST("hVertexZGen"), mcCollision.posZ()); if (atLeastOne) { - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + if (useMcCentrality) { + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 3 /* at least 1 rec. event in INEL > 0*/); + } else { + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + } registryMC.fill(HIST("hGenEventCentrality"), centralitydata); } } @@ -955,6 +1004,25 @@ struct CascadeAnalysisLightIonsDerivedData { multiplicityMcRec = RecCol.multNTracksGlobal(); } + if (useMcCentrality) { + const float recoCentrality = centralityMcRec; + centralityMcRec = -1.f; + + if (RecCol.has_straMCCollision()) { + auto mcCollision = RecCol.template straMCCollision_as(); + if (centralityEstimator == Option::kFT0C) + centralityMcRec = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMcRec = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMcRec = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMcRec = mcCollision.centNGlobal(); + } + + registryMC.fill(HIST("hEventVsGenCentrality_truerec"), recoCentrality, centralityMcRec); + } + registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 0, centralityMcRec); // event selections @@ -1148,47 +1216,86 @@ struct CascadeAnalysisLightIonsDerivedData { } float centralityMC = 100.5f; + float centralityMCtrue = -1.f; + + if (centralityEstimator == Option::kFT0C) + centralityMCtrue = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMCtrue = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMCtrue = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMCtrue = mcCollision.centNGlobal(); if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { auto collision = RecCols.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); - if (centralityEstimator == Option::kFT0C) - centralityMC = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralityMC = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralityMC = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralityMC = collision.centNGlobal(); + + if (useMcCentrality) { + centralityMC = centralityMCtrue; + } else { + if (centralityEstimator == Option::kFT0C) + centralityMC = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = collision.centNGlobal(); + } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } } // cascMC loop } From 3599af1070301a55131ad0103a648f539f9b3ea6 Mon Sep 17 00:00:00 2001 From: Giorgio Alberto Lucia <87222843+GiorgioAlbertoLucia@users.noreply.github.com> Date: Sat, 10 Oct 2026 01:28:59 +0200 Subject: [PATCH 247/648] [PWGLF] store signed pt of the candidate, swap hardcoded pdg codes for PDG_t (#18235) --- PWGLF/DataModel/LFDoubleOmegaTables.h | 4 +-- .../Strangeness/doubleOmegaTreeCreator.cxx | 29 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/PWGLF/DataModel/LFDoubleOmegaTables.h b/PWGLF/DataModel/LFDoubleOmegaTables.h index 493d0b6d262..16c03e444f7 100644 --- a/PWGLF/DataModel/LFDoubleOmegaTables.h +++ b/PWGLF/DataModel/LFDoubleOmegaTables.h @@ -21,7 +21,7 @@ namespace o2::aod namespace DoubleOmegaTables { -DECLARE_SOA_COLUMN(PtDoubleOmega, ptDoubleOmega, float); +DECLARE_SOA_COLUMN(PtDoubleOmega, ptDoubleOmega, float); // Signed transverse momentum of the double-Omega candidate, signed by the charge of the direct kaon. DECLARE_SOA_COLUMN(EtaDoubleOmega, etaDoubleOmega, float); DECLARE_SOA_COLUMN(PhiDoubleOmega, phiDoubleOmega, float); DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); @@ -55,7 +55,7 @@ DECLARE_SOA_COLUMN(MassDoubleOmega, massDoubleOmega, float); DECLARE_SOA_COLUMN(MassOmega, massOmega, float); DECLARE_SOA_COLUMN(MassXi, massXi, float); -DECLARE_SOA_COLUMN(GenPt, genPt, float); +DECLARE_SOA_COLUMN(GenPt, genPt, float); // Signed transverse momentum of the generated double-Omega candidate, signed by the charge of the direct kaon. DECLARE_SOA_COLUMN(GenEta, genEta, float); DECLARE_SOA_COLUMN(GenPhi, genPhi, float); DECLARE_SOA_COLUMN(GenDecayLength, genDecayLength, float); diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx index 8c081656ba7..6ce665dee2d 100644 --- a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -96,6 +96,7 @@ struct DoubleOmegaCandidate { float mass = -999.f; float massOmega = -999.f; float massXi = -999.f; + int8_t sign = 0; }; struct DoubleOmegaMCInfo { @@ -110,6 +111,7 @@ struct DoubleOmegaMCInfo { std::array genProductionVertex{-999.f, -999.f, -999.f}; std::array genDecayVertex{-999.f, -999.f, -999.f}; std::array genOmegaDecayVertex{-999.f, -999.f, -999.f}; + int8_t sign = 0; }; struct LambdaCandidate { @@ -669,6 +671,7 @@ struct doubleOmegaTreeCreator { } cand.massOmega = omega.massOmega; cand.massXi = omega.massXi; + cand.sign = omega.sign; return true; } @@ -681,9 +684,9 @@ struct doubleOmegaTreeCreator { auto mcPosTrack = posTrack.template mcParticle_as(); auto mcNegTrack = negTrack.template mcParticle_as(); - const int expectedPosPdg = sign < 0 ? 2212 : 211; - const int expectedNegPdg = sign < 0 ? -211 : -2212; - const int expectedLambdaPdg = sign < 0 ? 3122 : -3122; + const int expectedPosPdg = sign < 0 ? PDG_t::kProton : PDG_t::kPiPlus; + const int expectedNegPdg = sign < 0 ? PDG_t::kPiMinus : PDG_t::kProtonBar; + const int expectedLambdaPdg = sign < 0 ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; if (mcPosTrack.pdgCode() != expectedPosPdg || mcNegTrack.pdgCode() != expectedNegPdg) { return -1; } @@ -712,8 +715,8 @@ struct doubleOmegaTreeCreator { auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); auto mcBachelor = bachelorTrack.template mcParticle_as(); - const int expectedBachelorPdg = sign < 0 ? -321 : 321; - const int expectedOmegaPdg = sign < 0 ? 3334 : -3334; + const int expectedBachelorPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; + const int expectedOmegaPdg = sign < 0 ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; if (mcBachelor.pdgCode() != expectedBachelorPdg) { return -1; } @@ -746,7 +749,7 @@ struct doubleOmegaTreeCreator { auto mcOmega = mcParticles.rawIteratorAt(omegaLabel); auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); auto mcKaon = directKaonTrack.template mcParticle_as(); - const int expectedKaonPdg = sign < 0 ? -321 : 321; + const int expectedKaonPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; const int expectedDoubleOmegaPdg = sign < 0 ? kDoubleOmegaPdg : -kDoubleOmegaPdg; if (mcKaon.pdgCode() != expectedKaonPdg) { return false; @@ -770,6 +773,7 @@ struct doubleOmegaTreeCreator { mcOmega.vy() - omegaMother.vy(), mcOmega.vz() - omegaMother.vz()); mcInfo.pdgCode = omegaMother.pdgCode(); + mcInfo.sign = sign; return true; } } @@ -786,9 +790,9 @@ struct doubleOmegaTreeCreator { } const bool isMatter = particle.pdgCode() > 0; - const int expectedOmegaPdg = isMatter ? 3334 : -3334; - const int expectedLambdaPdg = isMatter ? 3122 : -3122; - const int expectedKaonPdg = isMatter ? -321 : 321; + const int expectedOmegaPdg = isMatter ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; + const int expectedLambdaPdg = isMatter ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; + const int expectedKaonPdg = isMatter ? PDG_t::kKMinus : PDG_t::kKPlus; bool foundOmega = false; bool foundLambda = false; bool foundKaon = false; @@ -825,6 +829,7 @@ struct doubleOmegaTreeCreator { info.genProductionVertex = {particle.vx(), particle.vy(), particle.vz()}; info.genDecayVertex = decayVertex; info.genOmegaDecayVertex = omegaDecayVertex; + info.sign = isMatter ? -1 : 1; return true; } @@ -847,7 +852,7 @@ struct doubleOmegaTreeCreator { void writeDataCandidate(DoubleOmegaCandidate const& cand) { - doubleOmegaTable(cand.pt, + doubleOmegaTable(cand.pt * cand.sign, cand.eta, cand.phi, cand.x, @@ -879,7 +884,7 @@ struct doubleOmegaTreeCreator { void writeMCCandidate(DoubleOmegaCandidate const& cand, DoubleOmegaMCInfo const& mcInfo, bool isReco) { - doubleOmegaTableMC(cand.pt, + doubleOmegaTableMC(cand.pt * cand.sign, cand.eta, cand.phi, cand.x, @@ -907,7 +912,7 @@ struct doubleOmegaTreeCreator { cand.mass, cand.massOmega, cand.massXi, - mcInfo.pt, + mcInfo.pt * mcInfo.sign, mcInfo.eta, mcInfo.phi, mcInfo.decayLength, From 095a27c8ed5d48dff376dbf610df5ec1841db1a1 Mon Sep 17 00:00:00 2001 From: Daiki Sekihata Date: Sat, 10 Oct 2026 11:36:38 +0200 Subject: [PATCH 248/648] [PWGEM] reduce memory usage in EMH (#18242) --- PWGEM/Dilepton/Core/Dilepton.h | 21 ++++++---- PWGEM/Dilepton/Core/DileptonHadronMPC.h | 22 +++++------ PWGEM/Dilepton/Core/DileptonSV.h | 10 ++--- PWGEM/Dilepton/Utils/EventMixingHandler.h | 44 ++++++++++++++++----- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 14 +++---- PWGEM/PhotonMeson/Core/TaggingPi0.h | 10 ++--- PWGEM/PhotonMeson/Tasks/photonhbt.cxx | 12 +++--- 7 files changed, 82 insertions(+), 51 deletions(-) diff --git a/PWGEM/Dilepton/Core/Dilepton.h b/PWGEM/Dilepton/Core/Dilepton.h index a63c927c422..738634eeb3f 100644 --- a/PWGEM/Dilepton/Core/Dilepton.h +++ b/PWGEM/Dilepton/Core/Dilepton.h @@ -1534,11 +1534,11 @@ struct Dilepton { std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. // make a vector of selected photons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { @@ -1555,8 +1555,8 @@ struct Dilepton { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1586,8 +1586,15 @@ struct Dilepton { if (nuls > 0 || nlspp > 0 || nlsmm > 0) { map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); - emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); - emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + } // end of if pair exist } // end of collision loop diff --git a/PWGEM/Dilepton/Core/DileptonHadronMPC.h b/PWGEM/Dilepton/Core/DileptonHadronMPC.h index 38b45983fb5..0825c1d2f40 100644 --- a/PWGEM/Dilepton/Core/DileptonHadronMPC.h +++ b/PWGEM/Dilepton/Core/DileptonHadronMPC.h @@ -1178,10 +1178,10 @@ struct DileptonHadronMPC { std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); // make a vector of selected electrons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "selected_posTracks_in_this_event.size() = %d, selected_negTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool.size() = %d", selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), collisionIds_in_mixing_pool.size()); @@ -1201,8 +1201,8 @@ struct DileptonHadronMPC { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "posTracks_from_event_pool.size() = %d, negTracks_from_event_pool.size() = %d", posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1233,8 +1233,8 @@ struct DileptonHadronMPC { } // end of loop over mixed event pool for lepton-lepton if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonHadronAnalysisType::kAzimuthalCorrelation)) { - auto selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); + const auto& selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); + const auto& collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); // for ULS and hadron mix for (const auto& pos : selected_posTracks_in_this_event) { @@ -1253,7 +1253,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos, neg, cut, ref); } @@ -1283,7 +1283,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos1, pos2, cut, ref); } @@ -1311,7 +1311,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(neg1, neg2, cut, ref); } @@ -1335,7 +1335,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "selected_refTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool_hadron.size() = %d, refTracks_from_event_pool.size() = %d", selected_refTracks_in_this_event.size(), collisionIds_in_mixing_pool_hadron.size(), refTracks_from_event_pool.size()); for (const auto& ref1 : selected_refTracks_in_this_event) { // ref-ref mix for (const auto& ref2 : refTracks_from_event_pool) { diff --git a/PWGEM/Dilepton/Core/DileptonSV.h b/PWGEM/Dilepton/Core/DileptonSV.h index d59725ef917..fb8232e344f 100644 --- a/PWGEM/Dilepton/Core/DileptonSV.h +++ b/PWGEM/Dilepton/Core/DileptonSV.h @@ -1695,11 +1695,11 @@ struct DileptonSV { std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. // make a vector of selected photons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { @@ -1717,8 +1717,8 @@ struct DileptonSV { } fRegistry.fill(HIST("Event/hsMixCounter"), collision.posZ(), centrality, ep2, occupancy); - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix diff --git a/PWGEM/Dilepton/Utils/EventMixingHandler.h b/PWGEM/Dilepton/Utils/EventMixingHandler.h index 4698654be49..1c69210490f 100644 --- a/PWGEM/Dilepton/Utils/EventMixingHandler.h +++ b/PWGEM/Dilepton/Utils/EventMixingHandler.h @@ -16,6 +16,8 @@ #define PWGEM_DILEPTON_UTILS_EVENTMIXINGHANDLER_H_ #include +#include +#include #include namespace o2::aod::pwgem::dilepton::utils @@ -36,6 +38,9 @@ class EventMixingHandler fNdepth = ndepth; fMapMixBins.clear(); fMap_Tracks_per_collision.clear(); + if (fNdepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } } ~EventMixingHandler() @@ -44,7 +49,13 @@ class EventMixingHandler fMap_Tracks_per_collision.clear(); } - void SetNdepth(int ndepth) { fNdepth = ndepth; } + void SetNdepth(int ndepth) + { + if (ndepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } + fNdepth = ndepth; + } void ReserveNTracksPerCollision(U key_df_collision, int ntrack) { @@ -56,20 +67,33 @@ class EventMixingHandler fMap_Tracks_per_collision[key_df_collision].emplace_back(obj); } - std::vector GetCollisionIdsFromEventPool(T key_bin) { return fMapMixBins[key_bin]; } - std::vector GetTracksPerCollision(T key_bin, int index) { return fMap_Tracks_per_collision[fMapMixBins[key_bin][index]]; } - std::vector GetTracksPerCollision(U key_df_collision) { return fMap_Tracks_per_collision[key_df_collision]; } + const std::vector& GetCollisionIdsFromEventPool(const T& key_bin) const + { + const auto it = fMapMixBins.find(key_bin); + static const std::vector empty; + return it == fMapMixBins.end() ? empty : it->second; + } + + const std::vector& GetTracksPerCollision(const U& key) const + { + const auto it = fMap_Tracks_per_collision.find(key); + static const std::vector empty; + return it == fMap_Tracks_per_collision.end() ? empty : it->second; + } // call this function at the end of collision loop - void AddCollisionIdAtLast(T key_bin, U key_df_collision) + std::optional AddCollisionIdAtLast(T key_bin, U key_df_collision) { // LOGF(info, "fMapMixBins[key_bin].size() = %d", fMapMixBins[key_bin].size()); - if (static_cast(fMapMixBins[key_bin].size()) >= fNdepth) { - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].clear(); - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].shrink_to_fit(); - fMapMixBins[key_bin].erase(fMapMixBins[key_bin].begin()); + auto& ids = fMapMixBins[key_bin]; + std::optional evicted; + if (static_cast(ids.size()) >= fNdepth) { + evicted = ids.front(); + fMap_Tracks_per_collision.erase(*evicted); + ids.erase(ids.begin()); } - fMapMixBins[key_bin].emplace_back(key_df_collision); + ids.emplace_back(key_df_collision); + return evicted; } private: diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 9b35b491e03..91818c3f3d2 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -1007,11 +1007,11 @@ struct Pi0EtaToGammaGamma { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing for (const auto& mix_dfId_collisionId : collisionIds1_in_mixing_pool) { @@ -1029,7 +1029,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { @@ -1093,7 +1093,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { @@ -1130,7 +1130,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), nll = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons2_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons2_in_this_event) { diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0.h b/PWGEM/PhotonMeson/Core/TaggingPi0.h index e81d5e3ba9c..64080cd2a2d 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0.h @@ -599,11 +599,11 @@ struct TaggingPi0 { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - // auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + // const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - // auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + // const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); for (const auto& mix_dfId_collisionId : collisionIds2_in_mixing_pool) { int mix_dfId = mix_dfId_collisionId.first; @@ -620,7 +620,7 @@ struct TaggingPi0 { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { // [photon from event1, dilepton from event2] diff --git a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx index 1bbaab29506..23cb1db4a70 100644 --- a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx @@ -2782,8 +2782,8 @@ struct Photonhbt { if (!mixing.cfgDoMix || ndiphoton == 0) { continue; } - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; @@ -2794,7 +2794,7 @@ struct Photonhbt { if (diffBC < mixing.ndiffBCMix) { continue; } - auto poolPhotons = emh1->GetTracksPerCollision(mixID); + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { if (!passAsymmetryCut(g1.pt(), g2.pt())) { @@ -3102,8 +3102,8 @@ struct Photonhbt { if (!mixing.cfgDoMix || ndiphoton == 0) { continue; } - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; @@ -3114,7 +3114,7 @@ struct Photonhbt { if (diffBC < mixing.ndiffBCMix) { continue; } - auto poolPhotons = emh1->GetTracksPerCollision(mixID); + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { if (!passAsymmetryCut(g1.pt(), g2.pt())) { From 40f656f8f60b158bacc09aae1a4f52f8180db6df Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:35:41 +0800 Subject: [PATCH 249/648] [PWGLF] Add additional systematic uncertainty configuration about k0s competing mass (#18245) --- PWGLF/DataModel/LFSpincorrelationTables.h | 16 +---- .../Strangeness/lambdaspincorrelation.cxx | 43 ++++-------- .../Strangeness/lambdaspincorrderived.cxx | 68 +++++++++---------- 3 files changed, 48 insertions(+), 79 deletions(-) diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index fa7e2061f3e..d4fdffd2442 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -53,12 +53,6 @@ DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Ph DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex -DECLARE_SOA_COLUMN(TpcPosNClsCrossedRows, tpcPosNClsCrossedRows, float); -DECLARE_SOA_COLUMN(TpcNegNClsCrossedRows, tpcNegNClsCrossedRows, float); -DECLARE_SOA_COLUMN(Ctau, ctau, float); -DECLARE_SOA_COLUMN(TpcNsigmaPos, tpcNsigmaPos, float); -DECLARE_SOA_COLUMN(TpcNsigmaNeg, tpcNsigmaNeg, float); -DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", @@ -80,13 +74,7 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonPhi, lambdapair::ProtonIndex, lambdapair::PionIndex, - lambdapair::DcaV0ToPV, - lambdapair::TpcPosNClsCrossedRows, - lambdapair::TpcNegNClsCrossedRows, - lambdapair::Ctau, - lambdapair::TpcNsigmaPos, - lambdapair::TpcNsigmaNeg, - lambdapair::MK0Short); + lambdapair::DcaV0ToPV); using LambdaPair = LambdaPairs::iterator; @@ -121,8 +109,6 @@ DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proto DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex - -//! TPC n sigma } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index 615c4be269d..d9a3d8cca27 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -95,14 +95,21 @@ struct lambdaspincorrelation { Configurable cMaxV0DCA{"cMaxV0DCA", 1.2, "Maximum V0 DCA to PV"}; Configurable cMinV0DCAPr{"cMinV0DCAPr", 0.05, "Minimum V0 daughters DCA to PV for Pr"}; Configurable cMinV0DCAPi{"cMinV0DCAPi", 0.05, "Minimum V0 daughters DCA to PV for Pi"}; - Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 50, "Maximum V0 life time"}; // config for V0 daughters Configurable confDaughEta{"confDaughEta", 0.8f, "V0 Daugh sel: max eta"}; Configurable cfgDaughPrPt{"cfgDaughPrPt", 0.2, "minimum daughter proton pt"}; Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2, "minimum daughter pion pt"}; Configurable confDaughTPCnclsMin{"confDaughTPCnclsMin", 50.f, "V0 Daugh sel: Min. nCls TPC"}; - Configurable confDaughPIDCuts{"confDaughPIDCuts", 3, "PID selections for Lambda daughters"}; + + struct : ConfigurableGroup { + std::string prefix = "v0Configuration"; + Configurable cfgDaughTPCCrossedRows{"cfgDaughTPCCrossedRows", 70, "Min. TPC crossed rows for V0 daughters"}; + + Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 30, "Maximum V0 life time"}; + Configurable confDaughPIDCuts{"confDaughPIDCuts", 4, "PID selections for Lambda daughters"}; + + } v0Configurations; Configurable iMNbins{"iMNbins", 50, "Number of bins in invariant mass"}; Configurable lbinIM{"lbinIM", 1.09, "lower bin value in IM histograms"}; @@ -149,9 +156,10 @@ struct lambdaspincorrelation { if (tranRad > confV0TranRadV0Max) { return false; } - if (std::abs(ctauLambda) > cMaxV0LifeTime) { + if (std::abs(ctauLambda) > v0Configurations.cMaxV0LifeTime) { return false; } + // if (std::abs(candidate.yLambda()) > confV0Rap) { // return false; // } @@ -162,10 +170,9 @@ struct lambdaspincorrelation { bool isSelectedV0Daughter(V0 const& candidate, T const& track, int pid) { const auto tpcNClsF = track.tpcNClsFound(); - const auto ncr = 70; const auto ncrfc = 0.8; - if (track.tpcNClsCrossedRows() < ncr) { + if (track.tpcNClsCrossedRows() < v0Configurations.cfgDaughTPCCrossedRows) { return false; } if (tpcNClsF < confDaughTPCnclsMin) { @@ -175,10 +182,10 @@ struct lambdaspincorrelation { return false; } - if (pid == 0 && std::abs(track.tpcNSigmaPr()) > confDaughPIDCuts) { + if (pid == 0 && std::abs(track.tpcNSigmaPr()) > v0Configurations.confDaughPIDCuts) { return false; } - if (pid == 1 && std::abs(track.tpcNSigmaPi()) > confDaughPIDCuts) { + if (pid == 1 && std::abs(track.tpcNSigmaPi()) > v0Configurations.confDaughPIDCuts) { return false; } if (pid == 0 && (candidate.positivept() < cfgDaughPrPt || candidate.negativept() < cfgDaughPiPt)) { @@ -299,13 +306,6 @@ struct lambdaspincorrelation { std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; std::vector dcaV0ToPV = {}; - std::vector tpcPosNClsCrossedRows = {}; - std::vector tpcNegNClsCrossedRows = {}; - std::vector ctau = {}; - std::vector tpcNsigmaPos = {}; - std::vector tpcNsigmaNeg = {}; - std::vector mK0Short = {}; - int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -357,9 +357,6 @@ struct lambdaspincorrelation { if (aLambdaTag) { histos.fill(HIST("hV0Info"), 3.5); } - - float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassLambda); - // LOGF(info, "v0 index2: (%d)", v0.index()); auto postrack1 = v0.template posTrack_as(); auto negtrack1 = v0.template negTrack_as(); @@ -367,16 +364,6 @@ struct lambdaspincorrelation { negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); - - tpcPosNClsCrossedRows.push_back(postrack1.tpcNClsCrossedRows()); - tpcNegNClsCrossedRows.push_back(negtrack1.tpcNClsCrossedRows()); - ctau.push_back(ctauLambda); - - tpcNsigmaPos.push_back(std::abs(postrack1.tpcNSigmaPr())); - tpcNsigmaNeg.push_back(std::abs(negtrack1.tpcNSigmaPi())); - - mK0Short.push_back(v0.mK0Short()); - v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -413,7 +400,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5), tpcPosNClsCrossedRows.at(i5), tpcNegNClsCrossedRows.at(i5), ctau.at(i5), tpcNsigmaPos.at(i5), tpcNsigmaNeg.at(i5), mK0Short.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); } } } diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index ede587a496c..6224d7509ab 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -59,6 +59,31 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; +// Compute the competing mass under the K0s (pi+, pi-) hypothesis. +template +static double getK0sCompetingMass(T const& v0) +{ + const ROOT::Math::PtEtaPhiMVector lambda( + v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass()); + + const ROOT::Math::PtEtaPhiMVector posDaugh( + v0.protonPt(), v0.protonEta(), v0.protonPhi(), + o2::constants::physics::MassProton); + + const auto negDaugh = lambda - posDaugh; // 反推 neg + + // + const ROOT::Math::PtEtaPhiMVector pi1( + posDaugh.Pt(), posDaugh.Eta(), posDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + const ROOT::Math::PtEtaPhiMVector pi2( + negDaugh.Pt(), negDaugh.Eta(), negDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + return (pi1 + pi2).M(); +} + template static std::vector getCleanCandidateIndices( const Container& candidates, @@ -388,16 +413,8 @@ struct lambdaspincorrderived { Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.5, "Eta cut on mix event buffer"}; - // TPC crossed rows - Configurable minTpcNClsCrossedRows{"minTpcNClsCrossedRows", 70.f, "Minimum TPC crossed rows for daughter"}; - - // ctau (lifetime) - Configurable maxCtau{"maxCtau", 30.f, "Maximum ctau of Lambda candidate (cm)"}; - - // TPC nsigma - Configurable maxTpcNsigma{"maxTpcNsigma", 4.f, "Maximum |TPC nsigma| for Lambda daughters"}; - - Configurable confK0sMassWindow{"confK0sMassWindow", 0.01, "K0s competing mass rejection window (GeV/c2)"}; + Configurable useK0sRejection{"useK0sRejection", true, "Apply K0s competing mass rejection"}; + Configurable confK0sMassWindow{"confK0sMassWindow", 0.01f, "K0s competing mass rejection window (GeV/c^2)"}; } v0Configurations; @@ -907,33 +924,12 @@ struct lambdaspincorrderived { if (candidate.lambdaPt() > ptMax) { return false; } - - // additional cuts - if (candidate.v0Status() == 0 && (std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma)) { - return false; - } - if (candidate.v0Status() == 1 && (std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma)) { - return false; - } - - // ---- K0s competing mass rejection ---- - if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { - return false; - } - - /* - if (candidate.tpcPosNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows ) { - return false; - } - - if (candidate.tpcNegNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows ) { - return false; - } - - if (candidate.ctau() > v0Configurations.maxCtau ) { - return false; + if (v0Configurations.useK0sRejection) { + const double mK0s = getK0sCompetingMass(candidate); + if (std::abs(mK0s - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { + return false; + } } - */ return true; } From fc257b81a50bd6d23c179bb5fdd91734b97bb779 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sat, 10 Oct 2026 21:25:05 +0800 Subject: [PATCH 250/648] [PWGCF] Add separated event-level cuts and improve getHistogramWithWeights (#18246) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 141 +++++++----------- 1 file changed, 55 insertions(+), 86 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 2b702910b33..ded6b3c22a2 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -203,9 +203,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; // *) Binnings - Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", true, "switch on or off to use ALICE default binning"}; + Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", false, "switch on or off to use ALICE default binning for centrality hist"}; Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; - Configurable> cfMultBins{"cfMultBins", {400, 0., 30000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfMultBins{"cfMultBins", {400, 0., 25000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; Configurable> cfContribBins{"cfContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; @@ -223,9 +223,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable> cfTechnicalCutSwitch{"cfTechnicalCutSwitch", {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}, "technical cuts switch, on and off by the first number before name"}; // event level cuts - Configurable cfEventCutSwitch{"cfEventCutSwitch", true, "switch to apply event level cut"}; + Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "switch to apply Sel8 cut"}; + Configurable cfVertexZCutSwitch{"cfVertexZCutSwitch", true, "switch to apply VertexZ cut"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; + Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "switch to apply centrality cut"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}[cm], with convention: min <= cent <= max"}; + Configurable cfNContribCutSwitch{"cfNContribCutSwitch", true, "switch to apply NContrib cut"}; Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; // particle level cuts @@ -263,7 +266,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool fALICECentBinSwitch = true; bool fMasterCutSwitch = true; - bool fEventCutSwitch = true; + bool fSel8CutSwitch = true; + bool fVertexZCutSwitch = true; + bool fCentCutSwitch = true; + bool fNContribCutSwitch = true; bool fPtCutSwitch = true; bool fEtaCutSwitch = true; bool fChargeCutSwitch = true; @@ -455,7 +461,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool bFileIsInCCDB = false; std::string path(filePath); - if (path.starts_with("/alice/cern.ch/")) { bFileIsInAliEn = true; } else if (path.starts_with("/alice-ccdb.cern.ch/")) { @@ -476,41 +481,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!baseList) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - - // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, for that there is a helper utility function getObjectFromList(...) , see its implementation further below - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return; - } - } - - // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) } else if (bFileIsInCCDB) { - // File you want to access is in your home dir in CCDB: - // Remember that here I do not access the file; instead, I directly access the object in that file. + // File you want to access is in your home dir in CCDB: Remember that here I do not access the file; instead, I directly access the object in that file. ccdb->setURL("https://alice-ccdb.cern.ch"); baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); if (!baseList) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - return; - } - } - - // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) } else { // this is the local case: // Check if the external ROOT file exists at the specified path: @@ -518,7 +495,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - TFile* weightsFile = TFile::Open(filePath, "READ"); if (!weightsFile) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m can't open file", __FUNCTION__, __LINE__); @@ -527,23 +503,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!baseList) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); - if (!listWithRuns) { - TString runNumberWithLeadingZeroes = "000"; - runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); - if (!listWithRuns) { - LOGF(error, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); - return; - } - } } // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { - LOGF(fatal, "\033[1;31m%s: listWithRuns is null for run %s\033[0m", __FUNCTION__, runNumber); - return; + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + return; + } } for (int i = 0; i < eWeightsHistograms_N; ++i) { @@ -563,7 +534,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } delete baseList; - } // end of TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) + baseList = nullptr; + } // end of void getHistogramWithWeights(const char* filePath, const char* runNumber) // templates template @@ -640,32 +612,30 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fEventCutSwitch) { // event level cuts for Rec - if constexpr (rm == eReal) { - if (!collision.sel8()) { // sel8 cut - return false; - } - if (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut - return false; - } - auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - if (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]) { // centrality cut - return false; - } - if (collision.numContrib() < tc.fNContribCutInf) { // number of contribution cuts - return false; - } + if (tc.fSel8CutSwitch && !collision.sel8()) { // sel8 cut + return false; + } + if constexpr (rm == eReal) { + if (tc.fVertexZCutSwitch && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut + return false; } - if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim - auto thisMCCollision = collision.mcCollision(); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; - if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut - return false; - } - if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut - return false; - } + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + if (tc.fCentCutSwitch && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut + return false; + } + if (tc.fNContribCutSwitch && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts + return false; + } + } + if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim + auto thisMCCollision = collision.mcCollision(); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut + return false; + } + if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut + return false; } } } @@ -717,7 +687,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); } - if constexpr (cuts == eAfter) { ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); @@ -726,7 +695,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); } - } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } } @@ -801,7 +770,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); } - if constexpr (cuts == eAfter) { pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); @@ -830,7 +798,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); } } - if constexpr (cuts == eAfter) { pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); @@ -839,9 +806,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); } } - } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } - } + } // end of void particleHistFill(T1 const& track) template void qaFill(T1 const& collision) @@ -987,18 +954,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (noneZeroDenom(resultTwo)) { obs.fProfTwo[eRec]->Fill(thisCollCentReal, (resultTwo[0] / resultTwo[1].Re()).Re()); } - } - if constexpr (rs == eRecAndSim) { - if (passEventCutsReal && passTechnicalCut) { + if constexpr (rs == eRecAndSim) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; resultTwo = two(qVectorsTableMC, n2); if (noneZeroDenom(resultTwo)) { obs.fProfTwo[eSim]->Fill(thisCollCentMC, (resultTwo[0] / resultTwo[1].Re()).Re()); } - } // end of if (passEventCutsReal && passTechnicalCut) { - } // end of if constexpr (rs == eRecAndSim) { + } // end of if constexpr (rs == eRecAndSim) { + } } // end of if (tc.fMasterCutSwitch) { isFirstCollision = false; // Now the first collision ends @@ -1029,7 +994,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fMasterCutSwitch = cfMasterCutSwitch; tc.fTechnicalCutSwitch = cfTechnicalCutSwitch.value; - tc.fEventCutSwitch = cfEventCutSwitch; + + tc.fSel8CutSwitch = cfSel8CutSwitch; + tc.fVertexZCutSwitch = cfVertexZCutSwitch; + tc.fCentCutSwitch = cfCentCutSwitch; + tc.fNContribCutSwitch = cfNContribCutSwitch; tc.fVertexZCutRange = cfVertexZCutRange.value; tc.fCentCutRange = cfCentCutRange.value; tc.fNContribCutInf = cfNContribCutInf; From b8307bb33aef06d7a6c8bdd5c579a32a9f2e48bc Mon Sep 17 00:00:00 2001 From: AlexianL <123153896+AlexianL@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:22:19 +0200 Subject: [PATCH 251/648] [PWGHF] taskFlow.cxx : fix centrality, fix mcGen, add outer and inner FT0 selections and other small changes (#18203) --- PWGHF/HFC/Tasks/taskFlow.cxx | 1347 ++++++++++++++++++---------------- 1 file changed, 697 insertions(+), 650 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 77aa48b5244..11251c85f98 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -63,6 +63,7 @@ #include #include +#include #include #include #include @@ -82,7 +83,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; enum CorrelationCase { - TpcTpc, + TpcTpc = 0, TpcMft, TpcFv0a, MftFv0a, @@ -103,6 +104,24 @@ enum DataType { Mc }; +enum FT0DetectorChannels { + FT0AInnerRingMin = 0, + FT0AInnerRingMax = 31, + FT0AOuterRingMin = 32, + FT0AOuterRingMax = 95, + FT0CInnerRingMin = 96, + FT0CInnerRingMax = 143, + FT0COuterRingMin = 144, + FT0COuterRingMax = 207 +}; + +enum FT0InnerOrOuter { + FT0Error, + FT0InnerRing, + FT0OuterRing, + NFT0InnerOrOuterSteps +}; + enum ReassociationMftTracks { NotReassociatedMftTracks = 0, ReassociatedMftTracks, @@ -168,6 +187,7 @@ enum SpecificEventSelectionStep { IsNoCollInTimeRangeStrict, IsNoHighMultCollInPrevRof, IsRctFlagChecked, + IsFoundFT0, NSpecificEventSelectionSteps }; @@ -176,12 +196,11 @@ enum TrackSelection { TrackSelectionGlobalTrack }; -// static constexpr std::string_view whatEventType[] = {"SameEvent/", "MixedEvent/"}; -static constexpr std::string_view WhatDataType[] = {"Data/", "MC/"}; -static constexpr std::string_view WhatCorrelationCase[] = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; -static constexpr std::string_view WhatParticles[] = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; -static constexpr std::string_view WhatCentralityEstimator[] = {"centFT0C", "CentFT0CVariant1", "centFT0M"}; -static constexpr std::string_view WhatMultiplicityEstimator[] = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; +static constexpr std::array WhatDataType = {"Data/", "MC/"}; +static constexpr std::array WhatCorrelationCase = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; +static constexpr std::array WhatParticles = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; +static constexpr std::array WhatCentralityEstimator = {"centFT0C", "CentFT0CVariant1", "centFT0M"}; +static constexpr std::array WhatMultiplicityEstimator = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; auto static constexpr MinFt0cCell = 96; static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = @@ -196,7 +215,7 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = TrackSelectionFlags::kDCAxy; -static constexpr float PairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; +static constexpr std::array, 1> PairCutDefaults{{{{-1, -1, -1, -1, -1}}}}; struct HfTaskFlow { @@ -234,6 +253,8 @@ struct HfTaskFlow { Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per event"}; Configurable nSamples{"nSamples", 10, "number of different samples for correlations"}; Configurable nameCorrelationContainer{"nameCorrelationContainer", "", "Add the possibility to the rename the correlation container as configurable"}; + Configurable removeQAForSystematics{"removeQAForSystematics", false, "Remove QA plots for systematics, deactivate all plots except trigger hist"}; + Configurable useCutNeutralParticles{"useCutNeutralParticles", false, "Choose to use or not use neutral particles in the correlation analysis"}; Configurable useEfficiencyCorrection{"useEfficiencyCorrection", false, "Choose to use or not use efficiency correction, if not used, weight is set to 1"}; Configurable useOnlyPrimaryInMc{"useOnlyPrimaryInMc", false, "Choose to use or not use only primaries for McGen"}; } configTask; @@ -258,6 +279,7 @@ struct HfTaskFlow { Configurable useCentrality{"useCentrality", false, "use centrality instead of multiplicity"}; Configurable useMultiplicityFromTracks{"useMultiplicityFromTracks", false, "Use multiplicity from counting tracks"}; Configurable useMultiplicityFromTracksCorrected{"useMultiplicityFromTracksCorrected", false, "Use multiplicity from counting tracks, corrected but takes a lot of computation time"}; + Configurable useNoFoundFT0Cut{"useNoFoundFT0Cut", false, "Use cut on no FT0 signal"}; Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Check event quality in run condition table"}; Configurable requireCorrelationAnalysisRCTFlagChecker{"requireCorrelationAnalysisRCTFlagChecker", false, "Check event quality in run condition table for correlation analysis"}; Configurable setRCTFlagCheckerLabel{"setRCTFlagCheckerLabel", "CBT_muon_global", "Evt sel: RCT flag checker label"}; @@ -286,7 +308,7 @@ struct HfTaskFlow { Configurable minMergingRadius{"minMergingRadius", 0.8, "max radius for merging cut"}; Configurable minTpcClusters{"minTpcClusters", 50.0f, "cut for minimum TPC clusters"}; Configurable minTpcCrossedRows{"minTpcCrossedRows", 70.0f, "cut for minimum TOC crossed rows"}; - Configurable> pairCut{"pairCut", {PairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + Configurable> pairCut{"pairCut", {PairCutDefaults[0].data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; Configurable ptCentralTrackMin{"ptCentralTrackMin", 0.2f, "min. pT of central tracks"}; Configurable ptCentralTrackMax{"ptCentralTrackMax", 10.0f, "max. pT of central tracks"}; Configurable trackSelectionType{"trackSelectionType", 1, "Track selection: 0 -> kGlobalTrack or isGlobalTrackSDD , 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> No globalTrack selection"}; @@ -334,6 +356,7 @@ struct HfTaskFlow { std::string prefix = "ConfigFit_group"; Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; Configurable cfgCorrLevel{"cfgCorrLevel", 1, "calibration step: 0 = no corr, 1 = gain corr"}; + Configurable discriminateInnerOrOuter{"discriminateInnerOrOuter", false, "if true, fill correlationContainer with either Inner ou Outer for FIT, if false, fill with real eta information"}; } configFit; SliceCache cache; @@ -344,7 +367,7 @@ struct HfTaskFlow { o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; // o2::fv0::Geometry* fv0Det{}; - std::vector cstFT0RelGain{}; + std::vector cstFT0RelGain; RCTFlagsChecker rctChecker; RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}; @@ -374,9 +397,11 @@ struct HfTaskFlow { // ========================= using SmallGroupMcCollisions = soa::SmallGroups>; - using FilteredMcCollisionsWMult = soa::Filtered>; - using FilteredMcCollisionsWMultWCollsExtra = soa::Filtered>; - using FilteredMcParticles = soa::Filtered; + // using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMult = soa::Join; + using FilteredMcCollisionsWMultWCollsExtra = soa::Join; + // using FilteredMcParticles = soa::Filtered; + using FilteredMcParticles = aod::McParticles; // ========================= // Filters & partitions : DATA @@ -419,10 +444,11 @@ struct HfTaskFlow { // Filters & partitions : MC // ========================= - Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt > configTask.ptMcParticlesTriggerMin) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); + // // Filter for McParticles + // Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); - // Filter for MCcollisions - Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; + // // Filter for MCcollisions + // Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; // ========================= // Preslice : DATA @@ -454,6 +480,7 @@ struct HfTaskFlow { ConfigurableAxis binsMixingMultiplicity{"binsMixingMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity bins for event mixing"}; ConfigurableAxis binsMixingVertex{"binsMixingVertex", {20, -10, 10}, "vertex bins for event mixing"}; ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisChi2Ndf{"axisChi2Ndf", {100, 0, 100}, "chi2/ndf axis for MFT histograms"}; ConfigurableAxis axisNClusters{"axisNClusters", {9, 1, 10}, "axis for number of clusters of MFT tracks"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisEtaAssociated{"axisEtaAssociated", {48, -4, -2}, "eta axis for MFT histograms"}; @@ -507,7 +534,7 @@ struct HfTaskFlow { void addMftHistograms() { registry.add("Data/Mft/hAmbiguityOfMftTracks", "hAmbiguityOfMftTracks", {HistType::kTH1D, {{MftTrackAmbiguityStep::NMftAmbiguitySteps, -0.5, +MftTrackAmbiguityStep::NMftAmbiguitySteps - 0.5}}}); - std::string labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NMftAmbiguitySteps]; + std::array labelsAmbiguityOfMftTracks{}; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AllMftTracks] = "all MFT tracks"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AfterTrackSelection] = "MFT tracks after selection"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NumberOfAmbiguousTracks] = "how much tracks are ambigous"; @@ -519,7 +546,7 @@ struct HfTaskFlow { } registry.add("Data/Mft/hMftTracksSelection", "hMftTracksSelection", {HistType::kTH1D, {{MftTrackSelectionStep::NMftTrackSelectionSteps, -0.5, +MftTrackSelectionStep::NMftTrackSelectionSteps - 0.5}}}); - std::string labelsMftTracksSelection[MftTrackSelectionStep::NMftTrackSelectionSteps]; + std::array labelsMftTracksSelection{}; labelsMftTracksSelection[MftTrackSelectionStep::NoSelection] = "all MFT tracks"; labelsMftTracksSelection[MftTrackSelectionStep::Eta] = "MFT tracks after eta selection"; labelsMftTracksSelection[MftTrackSelectionStep::Cluster] = "MFT tracks after clusters selection"; @@ -537,7 +564,7 @@ struct HfTaskFlow { } registry.add("Data/Mft/hReassociationMftTracks", "hReassociationMftTracks", {HistType::kTH1D, {{ReassociationMftTracks::NReassociationMftTracksSteps, -0.5, +ReassociationMftTracks::NReassociationMftTracksSteps - 0.5}}}); - std::string labelsReassociationMftTracks[ReassociationMftTracks::NReassociationMftTracksSteps]; + std::array labelsReassociationMftTracks{}; labelsReassociationMftTracks[ReassociationMftTracks::NotReassociatedMftTracks] = "Ambiguous MFT tracks after track selection"; labelsReassociationMftTracks[ReassociationMftTracks::ReassociatedMftTracks] = "Reassociated MFT tracks by DCAxy method"; registry.get(HIST("Data/Mft/hReassociationMftTracks"))->SetMinimum(0); @@ -546,6 +573,7 @@ struct HfTaskFlow { registry.get(HIST("Data/Mft/hReassociationMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociationMftTracks[iBin].data()); } + registry.add("Data/Mft/hChi2OverNdfMft", "", {HistType::kTH1D, {configAxis.axisChi2Ndf}}); registry.add("Data/Mft/hPtMft", "", {HistType::kTH1D, {configAxis.axisPt}}); registry.add("Data/Mft/hPtVsClustersLTF", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); registry.add("Data/Mft/hPtVsClustersCA", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); @@ -555,6 +583,22 @@ struct HfTaskFlow { registry.add("Data/Mft/hNBestCollisionFwd", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); } + void addFt0Histograms() + { + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + registry.add("Data/FT0IsInnerOrOuter", "FT0IsInnerOrOuter", {HistType::kTH1D, {{FT0InnerOrOuter::NFT0InnerOrOuterSteps, -0.5, +FT0InnerOrOuter::NFT0InnerOrOuterSteps - 0.5}}}); + std::array labels{}; + labels[FT0InnerOrOuter::FT0InnerRing] = "FT0InnerRing"; + labels[FT0InnerOrOuter::FT0OuterRing] = "FT0OuterRing"; + registry.get(HIST("Data/FT0IsInnerOrOuter"))->SetMinimum(0); + + for (int iBin = 0; iBin < FT0InnerOrOuter::NFT0InnerOrOuterSteps; iBin++) { + registry.get(HIST("Data/FT0IsInnerOrOuter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } + } + // ========================= // init() // ========================= @@ -581,47 +625,54 @@ struct HfTaskFlow { rctChecker.init(configCollision.setRCTFlagCheckerLabel, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad, true); correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}); - registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); - registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); - registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); - registry.add(Form("Data/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); - - registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); - std::string labels[EventSelectionStep::NEventSelectionSteps]; - labels[EventSelectionStep::AllEvents] = "all"; - labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; - registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); - - for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); - } - - registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); - std::string labelsPreciseEventSelection[SpecificEventSelectionStep::NSpecificEventSelectionSteps]; - labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; - registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); - - for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); - } - - mPairCuts.SetHistogramRegistry(®istry); - if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { - mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); - mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); - mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); - mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); - mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + if (!configTask.removeQAForSystematics) { + registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); + registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityWeighted", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + + registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); + std::array labels{}; + labels[EventSelectionStep::AllEvents] = "all"; + labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; + registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } + + registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); + std::array labelsPreciseEventSelection{}; + labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsFoundFT0] = "IsFoundFT0"; + registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); + } + + mPairCuts.SetHistogramRegistry(®istry); + if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { + mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); + mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); + mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); + mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); + mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + } } // ========================= @@ -659,7 +710,9 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcTpcChCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } if (!configTask.doEtaDependentFlow) { sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); @@ -671,14 +724,18 @@ struct HfTaskFlow { } if (doprocessSameTpcTpcD0Ch) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcTpcLcCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -689,12 +746,12 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); - registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); - registry.add("Data/hMultiplicity_uncorrected_vs_corrected", "", {HistType::kTH2D, {{configAxis.axisMultiplicity}, {configAxis.axisMultiplicity}}}); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + registry.add("Data/hMultiplicity_uncorrected_vs_corrected", "", {HistType::kTH2D, {{configAxis.axisMultiplicity}, {configAxis.axisMultiplicity}}}); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_MFT", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -712,16 +769,21 @@ struct HfTaskFlow { } if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { - addHistograms(); - addMftHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { - addHistograms(); - addMftHistograms(); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -780,9 +842,10 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcFt0aChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -800,18 +863,24 @@ struct HfTaskFlow { } if (doprocessSameTpcFt0aD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0aLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -822,10 +891,12 @@ struct HfTaskFlow { // ========================= if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_MFT_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -847,9 +918,11 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -867,18 +940,24 @@ struct HfTaskFlow { } if (doprocessSameTpcFt0cD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0cLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -889,9 +968,13 @@ struct HfTaskFlow { // ========================= if (doprocessSameFt0aFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); @@ -914,25 +997,25 @@ struct HfTaskFlow { if (doprocessSameMcGen) { - registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); - - if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + + if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { + addHistograms(); + } } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -951,12 +1034,15 @@ struct HfTaskFlow { } if (doprocessTrackEfficiencies) { - registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); - registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + if (!configTask.removeQAForSystematics) { + registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + } } + registry.print(); } // End of init() function // ========================= @@ -1004,22 +1090,22 @@ struct HfTaskFlow { { switch (configCollision.multiplicityEstimator) { case MultiplicityEstimators::MultNTracksPV: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNTracksPV"), collision.multNTracksPV()); } return collision.multNTracksPV(); case MultiplicityEstimators::MultNumContrib: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNumContrib"), collision.numContrib()); } return collision.numContrib(); case MultiplicityEstimators::MultFT0C: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0C"), collision.multFT0C()); } return collision.multFT0C(); case MultiplicityEstimators::MultFT0M: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0M"), collision.multFT0M()); } return collision.multFT0M(); @@ -1033,17 +1119,17 @@ struct HfTaskFlow { { switch (configCollision.centralityEstimator) { case CentralityEstimators::CentFT0C: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hCentrality_centFT0C"), collision.centFT0C()); } return collision.centFT0C(); case CentralityEstimators::CentFT0CVariant1: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); } return collision.centFT0CVariant1(); case CentralityEstimators::CentFT0M: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hCentrality_centFT0M"), collision.centFT0M()); } return collision.centFT0M(); @@ -1184,15 +1270,19 @@ struct HfTaskFlow { } else { LOGF(fatal, "Cor Index %d out of range", fitType); } - registry.fill(HIST("Data/FT0Amp"), rID, amplitude); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0Amp"), rID, amplitude); + } amplitude = amplitude / cstFT0RelGain[id]; - registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + } } void loadGain(aod::BCsWithTimestamps::iterator const& bc) { cstFT0RelGain.clear(); - cstFT0RelGain = {}; + // cstFT0RelGain = {}; std::string fullPath; auto timestamp = bc.timestamp(); @@ -1215,33 +1305,71 @@ struct HfTaskFlow { } } + int isInnerOrOuter(int channelId, int fitType) + { + if (fitType == isFT0C) { + if (channelId >= FT0DetectorChannels::FT0CInnerRingMin && channelId <= FT0DetectorChannels::FT0CInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0COuterRingMin && channelId <= FT0DetectorChannels::FT0COuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } else { + LOGF(fatal, "Channel ID %d out of range for FT0C", channelId); + return FT0InnerOrOuter::FT0Error; + } + } else if (fitType == isFT0A) { + if (channelId >= FT0DetectorChannels::FT0AInnerRingMin && channelId <= FT0DetectorChannels::FT0AInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0AOuterRingMin && channelId <= FT0DetectorChannels::FT0AOuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } else { + LOGF(fatal, "Channel ID %d out of range for FT0A", channelId); + return FT0InnerOrOuter::FT0Error; + } + } else { + LOGF(fatal, "Fit type %d is not valid for FT0", fitType); + return FT0InnerOrOuter::FT0Error; + } + } + void loadEfficiencyCorrection(uint64_t timestamp) { if (areCorrectionsLoaded) { return; } - if (configTask.loadEfficienciesForTpc.value.empty() == false) { + if (!configTask.loadEfficienciesForTpc.value.empty()) { mEfficiencyTpc = ccdb->getForTimeStamp(configTask.loadEfficienciesForTpc, timestamp); if (mEfficiencyTpc == nullptr) { LOGF(fatal, "Could not load efficiency histogram for TPC tracks from %s", configTask.loadEfficienciesForTpc.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForTpc.value.c_str(), static_cast(mEfficiencyTpc)); } - if (configTask.loadEfficienciesForMft.value.empty() == false) { + if (!configTask.loadEfficienciesForMft.value.empty()) { mEfficiencyMft = ccdb->getForTimeStamp(configTask.loadEfficienciesForMft, timestamp); if (mEfficiencyMft == nullptr) { LOGF(fatal, "Could not load efficiency histogram for MFT tracks from %s", configTask.loadEfficienciesForMft.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForMft.value.c_str(), static_cast(mEfficiencyMft)); } - if (configTask.loadEfficienciesForNch.value.empty() == false) { + if (!configTask.loadEfficienciesForNch.value.empty()) { mEfficiencyNch = ccdb->getForTimeStamp(configTask.loadEfficienciesForNch, timestamp); if (!mEfficiencyNch) { LOGF(fatal, "Could not load efficiency histogram for Nch estimator from %s", configTask.loadEfficienciesForNch.value.c_str()); } LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForNch.value.c_str(), static_cast(mEfficiencyNch)); } - if (configTask.loadCentralityWeight.value.empty() == false) { + if (!configTask.loadCentralityWeight.value.empty()) { mCentralityWeight = ccdb->getForTimeStamp(configTask.loadCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { LOGF(fatal, "Could not load centrality weight correction from %s", configTask.loadCentralityWeight.value.c_str()); @@ -1292,14 +1420,14 @@ struct HfTaskFlow { } template - double getCorrectedMultiplicity(const TTracks& tracks) // function to count the number of tracks in the event and fill the histogram + float getCorrectedMultiplicity(const TTracks& tracks) // function to count the number of tracks in the event and fill the histogram { - auto trackCounter = 0; + float trackCounter = 0.f; auto weightMultiplicity = 1.0f; for (auto const& track : tracks) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - trackCounter += 1; + if (std::abs(track.eta()) > configCentral.etaCentralTrackMax || track.pt() <= configCentral.ptCentralTrackMin || track.pt() >= configCentral.ptCentralTrackMax) { + continue; } auto efficiencyNch = 1.0f; @@ -1311,12 +1439,13 @@ struct HfTaskFlow { } if (efficiencyNch == 0) { - return false; + // return false; + efficiencyNch = 1.0f; } weightMultiplicity = 1. / efficiencyNch; } else { - weightMultiplicity = 0; + weightMultiplicity = 1.0f; } trackCounter += weightMultiplicity; @@ -1335,6 +1464,11 @@ struct HfTaskFlow { if (weight == 0) { return false; } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityWeighted"), centrality, weight); + } + weightCent = weight; return true; } @@ -1343,11 +1477,44 @@ struct HfTaskFlow { // Cuts with functions // ========================= + template + bool isAcceptedEventActivity(TCollision const& collision, TTracks const& tracks, auto& multiplicity, float& centrality, float& centralityWeight) + { + if (configCollision.useMultiplicityFromTracks) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityTracksSize"), tracks.size()); + } + multiplicity = tracks.size(); + } else { + multiplicity = getMultiplicityEstimator(collision, true); + } + + centrality = getCentralityEstimator(collision, true); + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, centrality); + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityUsed"), centrality); + } + } else { + if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityUsed"), multiplicity); + } + } + + return true; + } + // FIXME: Some collisions are rejected here, what causes (part of) differences with the D0 task template bool isAcceptedCollision(TCollision const& collision, bool fillHistograms = false) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AllEvents); registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); } @@ -1355,55 +1522,55 @@ struct HfTaskFlow { if (!collision.sel8()) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsSel8); } if (configCollision.isApplySameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoSameBunchPileup); } if (configCollision.isApplyGoodItsLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodItsLayersAll); } if (configCollision.isApplyGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodZvtxFT0vsPV); } if (configCollision.isApplyNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStandard); } if (configCollision.isApplyNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStrict); } if (configCollision.isApplyNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStandard); } if (configCollision.isApplyNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStrict); } if (configCollision.isApplyNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoHighMultCollInPrevRof); } if (configCollision.requireRCTFlagChecker && !rctChecker(collision)) { @@ -1412,12 +1579,20 @@ struct HfTaskFlow { if (configCollision.requireCorrelationAnalysisRCTFlagChecker && !correlationAnalysisRctChecker(collision)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsRctFlagChecked); - registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AfterEventSelection); + } + if (configCollision.useNoFoundFT0Cut && !collision.has_foundFT0()) { + return false; + } + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsFoundFT0); } - registry.fill(HIST("Data/hVtxZ"), collision.posZ()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AfterEventSelection); + registry.fill(HIST("Data/hVtxZ"), collision.posZ()); + } return true; } @@ -1479,7 +1654,7 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Eta); } @@ -1488,7 +1663,7 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Cluster); } @@ -1497,7 +1672,8 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hPtMft"), mftTrack.pt()); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Pt); } @@ -1505,14 +1681,14 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAxy); } if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hDcaXYMft"), dcaXY); registry.fill(HIST("Data/Mft/hDcaZMft"), dcaZ); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAz); @@ -1525,7 +1701,8 @@ struct HfTaskFlow { return false; } } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hChi2OverNdfMft"), mftTrack.chi2() / std::max(2.0f * mftTrack.nClusters() - 5.0f, 1.0f)); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Chi2OverNdf); } @@ -1537,13 +1714,13 @@ struct HfTaskFlow { return false; } } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Phi); } // cut on the track algorithm of MFT tracks if (mftTrack.isCA()) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsCA); registry.fill(HIST("Data/Mft/hPtVsClustersCA"), mftTrack.pt(), mftTrack.nClusters()); } @@ -1553,7 +1730,7 @@ struct HfTaskFlow { } } else { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsLTF); registry.fill(HIST("Data/Mft/hPtVsClustersLTF"), mftTrack.pt(), mftTrack.nClusters()); } @@ -1571,13 +1748,13 @@ struct HfTaskFlow { bool isAmbiguousMftTrack(TTrack const& mftTrack, bool fillHistograms) { if (mftTrack.ambDegree() > 1) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); } return true; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); } return false; @@ -1601,7 +1778,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (const auto& track1 : tracksTrigger) { @@ -1655,7 +1832,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillTriggerQa(multiplicity, eta1, phi1, pt1); @@ -1688,7 +1865,9 @@ struct HfTaskFlow { if constexpr (std::is_same_v) { if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(track2, 0.f, 0.f, true)) { continue; @@ -1731,19 +1910,19 @@ struct HfTaskFlow { const double kLimit = 3.0 * configCentral.mergingCut; - bool bIsBelow = false; + [[maybe_unused]] bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(configCentral.minMergingRadius); rad < configCentral.maxMergingRadius; rad += 0.01) { // FIXME: Variable 'rad' with floating point type 'double' should not be used as a loop counter. + constexpr double StepRad = 0.01; + const int nSteps = static_cast((configCentral.maxMergingRadius - configCentral.minMergingRadius) / StepRad); + for (int iStep = 1; iStep < nSteps; ++iStep) { + double rad = configCentral.minMergingRadius + iStep * StepRad; double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) { - continue; - } } } } @@ -1803,7 +1982,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillAssociatedQa(multiplicity, eta2, phi2); @@ -1815,13 +1994,10 @@ struct HfTaskFlow { } else { // IF TPC-MFT case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-MFT D0-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { // IF LC CASE -> TPC-MFT Lc-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { // IF NEITHER D0 NOR LC -> TPC-MFT h-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } // end of if condition for TPC-TPC or TPC-MFT case } } @@ -1838,7 +2014,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (const auto& track1 : tracksTrigger) { @@ -1888,14 +2064,13 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else { fillTriggerQa(multiplicity, eta1, phi1, pt1); - registry.fill(HIST("Data/hEfficiencyTrigger"), pt1, eta1, posZ); } } @@ -1903,7 +2078,7 @@ struct HfTaskFlow { for (const auto& track2 : tracksAssoc) { // Fill QA plot for all MFT tracks () (only if cutAmbiguousTracks is false to avoid double counting) - if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); } @@ -1920,7 +2095,10 @@ struct HfTaskFlow { } if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { continue; @@ -1932,7 +2110,7 @@ struct HfTaskFlow { } // Fill QA plot for MFT tracks after physical selection (eta + clusters) - if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); } @@ -1940,7 +2118,7 @@ struct HfTaskFlow { // Fill plots only if cutAmbiguousTracks is false (to avoid double counting) if (isAmbiguousMftTrack(track2, (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)))) { // If the MFT track is ambiguous we may cut or not on the ambiguous track - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::NotReassociatedMftTracks); } if (cutAmbiguousTracks) { @@ -1949,7 +2127,7 @@ struct HfTaskFlow { } if (reassociatedMftTrack.collisionId() != track2.bestCollisionId()) { - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::ReassociatedMftTracks); } } @@ -2012,17 +2190,13 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); - registry.fill(HIST("Data/hEfficiencyAssociated"), pt2, eta2, posZ); } } // end of fill QA } // end of loop over tracks2 @@ -2037,7 +2211,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (auto const& track1 : tracksTrigger) { @@ -2049,7 +2223,10 @@ struct HfTaskFlow { } } else if constexpr (std::is_same_v) { if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(track1, 0.f, 0.f, true)) { continue; @@ -2130,7 +2307,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-FV0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if constexpr (std::is_same_v) { // IF NEITHER D0 NOR LC -> @@ -2243,8 +2420,13 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, amplitude * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, - amplitude * centralityWeight * triggerWeight * associatedWeight); + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, amplitude * centralityWeight * triggerWeight * associatedWeight); @@ -2255,7 +2437,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-Ft0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if (fitType == isFT0A) { @@ -2298,7 +2480,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE for (auto const& track1 : tracksTrigger) { @@ -2308,16 +2490,18 @@ struct HfTaskFlow { auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; - if constexpr (std::is_same_v, TTracksAssoc>) { + if constexpr (std::is_same_v, TTracksTrig>) { reassociatedMftTrackDcaXY = track1.bestDCAXY(); } - if constexpr (std::is_same_v, TTracksAssoc>) { + if constexpr (std::is_same_v, TTracksTrig>) { reassociatedMftTrackDcaXY = track1.bestDCAXY(); reassociatedMftTrackDcaZ = track1.bestDCAZ(); } if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { continue; @@ -2353,7 +2537,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for trigger particle - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { @@ -2416,15 +2600,20 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, amplitude * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, - amplitude * centralityWeight * triggerWeight * associatedWeight); + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, amplitude * centralityWeight * triggerWeight * associatedWeight); } // FILL QA PLOTS for associated particle - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if (fitType == isFT0A) { fillAssociatedQa(multiplicity, eta2, phi2); } @@ -2442,7 +2631,7 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE FROM FT0A for (std::size_t indexChannelA = 0; indexChannelA < ft0as.channelA().size(); indexChannelA++) { @@ -2462,7 +2651,7 @@ struct HfTaskFlow { registry.fill(HIST("Trig_hist_FT0A_FT0C"), sampleIndex, posZ, 0.5, amplitudeA * centralityWeight * triggerWeight); } - if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { fillTriggerQa(multiplicity, etaA, phiA, 0.5); } // end of fill trigger QA @@ -2480,14 +2669,22 @@ struct HfTaskFlow { target->getPairHist()->Fill(step, etaA - etaC, 0.5, 0.5, multiplicity, deltaPhi, posZ, amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); } else if (configTask.doEtaDependentFlow) { - target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, - amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + // target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + // amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelIdA, isFT0A), isInnerOrOuter(channelIdC, isFT0C), deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, sampleIndex, posZ, 0.5, multiplicity, deltaPhi, etaA - etaC, amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); } - if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { fillAssociatedQa(multiplicity, etaC, phiC); } // end of fill associated QA } // end of associated loop @@ -2502,21 +2699,16 @@ struct HfTaskFlow { auto triggerWeight = 1.0f; auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - int sampleIndex = gRandom->Uniform(0, configTask.nSamples); - bool fillQaPlots = false; - - if (configTask.fillOnlyStepAll) { - if (step == CorrelationContainer::kCFStepAll) { - fillQaPlots = true; - } - } else { - if (step == CorrelationContainer::kCFStepTrackedOnlyPrim) { - fillQaPlots = true; - } - } + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); for (auto const& track1 : tracksTrigger) { - loopCounter++; + + if (configTask.useCutNeutralParticles) { + auto pdgTriggerParticle = pdg->GetParticle(track1.pdgCode()); + if (!pdgTriggerParticle || std::abs(pdgTriggerParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } if (track1.eta() < configTask.etaMcParticlesTriggerMin || track1.eta() > configTask.etaMcParticlesTriggerMax) { continue; @@ -2538,8 +2730,7 @@ struct HfTaskFlow { } // FILL QA FOR TRIGGER PARTICLE - if (isSameEvent && fillQaPlots) { - registry.fill(HIST("MC/hEfficiencyTrigger"), track1.pt(), track1.eta(), posZ); + if (isSameEvent && !configTask.removeQAForSystematics) { if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { @@ -2559,8 +2750,17 @@ struct HfTaskFlow { } } + loopCounter++; + for (auto const& track2 : tracksAssoc) { + if (configTask.useCutNeutralParticles) { + auto pdgAssociatedParticle = pdg->GetParticle(track2.pdgCode()); + if (!pdgAssociatedParticle || std::abs(pdgAssociatedParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } + if (track1.globalIndex() == track2.globalIndex()) { continue; } @@ -2574,8 +2774,8 @@ struct HfTaskFlow { continue; } - float deltaPhi = RecoDecay::constrainAngle(track2.phi() - track1.phi(), -PIHalf); - float deltaEta = track2.eta() - track1.eta(); + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, @@ -2589,8 +2789,7 @@ struct HfTaskFlow { } // FILL QA PLOTS for associated particle - if (isSameEvent && fillQaPlots && (loopCounter == 1)) { - registry.fill(HIST("MC/hEfficiencyAssociated"), track2.pt(), track2.eta(), posZ); + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { @@ -2655,7 +2854,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracks1.size(); } else { @@ -2666,14 +2865,14 @@ struct HfTaskFlow { if (configCollision.useCentrality) { getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelations(corrContainer, step, tracks1, tracks2, multiplicity, collision1.posZ(), false, getMagneticField(bc.timestamp()), centralityWeight); } @@ -2716,7 +2915,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksTrigger.size(); } else { @@ -2727,14 +2926,14 @@ struct HfTaskFlow { if (configCollision.useCentrality) { getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); @@ -2778,7 +2977,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2789,14 +2988,14 @@ struct HfTaskFlow { if (configCollision.useCentrality) { getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + // if TPC-MFT cases if constexpr (std::is_same_v) { auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); @@ -2824,8 +3023,8 @@ struct HfTaskFlow { // return multiplicity; // }; - auto getMultiplicity = [&tracksTrigger, this](FilteredCollisionsWSelMult::iterator const& collision) { - auto associatedTracks = tracksTrigger.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto getMultiplicity = [&tracksTpc, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; @@ -2857,7 +3056,7 @@ struct HfTaskFlow { // auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); // const auto& fv0 = collision2.foundFV0(); - // auto multiplicity = 0; + // float multiplicity = 0.f; // if (configCollision.useMultiplicityFromTracks) { // multiplicity = tracksForMultiplicity.size(); // } else { @@ -2865,7 +3064,7 @@ struct HfTaskFlow { // } // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - // return; + // continue; // } // corrContainer->fillEvent(multiplicity, step); @@ -2880,7 +3079,7 @@ struct HfTaskFlow { auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); const auto& ft0 = collision2.foundFT0(); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2889,16 +3088,16 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, fitType, centralityWeight); } @@ -2947,7 +3146,7 @@ struct HfTaskFlow { auto bc = collision1.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { multiplicity = tracksForMultiplicity.size(); } else { @@ -2958,14 +3157,14 @@ struct HfTaskFlow { if (configCollision.useCentrality) { getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; + continue; } } + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + corrContainer->fillEvent(multiplicity, step); fillCorrelationsFt0aFt0c(corrContainer, step, ft0as, ft0cs, multiplicity, collision1.posZ(), true, centralityWeight); } @@ -2994,26 +3193,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3041,26 +3232,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3088,26 +3271,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3128,26 +3303,18 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3170,28 +3337,20 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3214,34 +3373,30 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } auto uncorrectedMultiplicity = multiplicity; - registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); + if (configCollision.useMultiplicityFromTracksCorrected) { multiplicity = getCorrectedMultiplicity(tracks); } - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3264,28 +3419,20 @@ struct HfTaskFlow { return; // when process function has iterator } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3321,23 +3468,12 @@ struct HfTaskFlow { auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3358,23 +3494,12 @@ struct HfTaskFlow { auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3405,23 +3530,12 @@ struct HfTaskFlow { auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3440,25 +3554,14 @@ struct HfTaskFlow { return; // when process function has iterator } - auto multiplicity = 0; auto bc = collision.template bc_as(); loadEfficiencyCorrection(bc.timestamp()); - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3662,24 +3765,15 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3713,23 +3807,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3758,23 +3841,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -3803,23 +3875,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3848,25 +3909,15 @@ struct HfTaskFlow { loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { + loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3895,25 +3946,15 @@ struct HfTaskFlow { loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { + loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3944,23 +3985,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -3993,24 +4023,15 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4039,23 +4060,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4084,23 +4094,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4128,23 +4127,12 @@ struct HfTaskFlow { if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - auto multiplicity = 0; - if (configCollision.useMultiplicityFromTracks) { - multiplicity = tracks.size(); - } else { - multiplicity = getMultiplicityEstimator(collision, true); - } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - float centralityWeight = 1.0f; - if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision, true)); - if (getCentralityEstimator(collision, false) < configCollision.minCentrality || getCentralityEstimator(collision, false) >= configCollision.maxCentrality) { - return; - } - } else { - if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { - return; - } + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); @@ -4157,11 +4145,20 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== - void processSameMcGen(FilteredMcCollisionsWMult::iterator const& mcCollision, + void processSameMcGen(FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { - auto multiplicity = 0; + + if (mcParticles.size() == 0) { // guard against empty filtered batch + return; + } + + if (collisions.size() == 0) { + return; + } + + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { for (const auto& track : mcParticles) { if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { @@ -4172,12 +4169,27 @@ struct HfTaskFlow { multiplicity = mcCollision.multMCPVz(); } - if ((multiplicity < configCollision.minMultiplicity) || (multiplicity >= configCollision.maxMultiplicity)) { - return; + float centrality = -1.f; + for (const auto& collision : collisions) { + // if (!collision.sel8()) { + // continue; + // } + // if (collision.globalIndex() != mcCollision.bestCollisionIndex()) { + // continue; + // } + centrality = getCentralityEstimator(collision, false); + // break; } - if (collisions.size() == 0) { - return; + if (configCollision.useCentrality) { + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return; + } + } + if (!configCollision.useCentrality) { + if ((multiplicity < configCollision.minMultiplicity) || (multiplicity >= configCollision.maxMultiplicity)) { + return; + } } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); @@ -4546,14 +4558,14 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== - void processMixedMcGen(FilteredMcCollisionsWMult const& mcCollisions, + void processMixedMcGen(FilteredMcCollisionsWMultWCollsExtra const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { - auto getTracksSize = [&mcParticles, this](soa::Join::iterator const& mcCollision) { + auto getTracksSize = [&mcParticles, this](soa::Join::iterator const& mcCollision) { auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); - auto multiplicity = 0; + float multiplicity = 0.f; if (configCollision.useMultiplicityFromTracks) { for (const auto& track : associatedTracks) { if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { @@ -4571,13 +4583,26 @@ struct HfTaskFlow { auto tracksTuple = std::make_tuple(mcParticles, mcParticles); - Pair pairs{binningOnVtxAndMult, configTask.nMixedEvents, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, configTask.nMixedEvents, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - auto multiplicityCollision1 = 0; - auto multiplicityCollision2 = 0; + if (tracks1.size() == 0 || tracks2.size() == 0) { // guard against empty filtered batch + continue; + } + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); + if (groupedCollisions1.size() == 0) { + continue; + } + if (groupedCollisions2.size() == 0) { + continue; + } + + float multiplicityCollision1 = 0.f; + float multiplicityCollision2 = 0.f; if (configCollision.useMultiplicityFromTracks) { for (const auto& track : tracks1) { @@ -4595,20 +4620,42 @@ struct HfTaskFlow { multiplicityCollision2 = collision2.multMCPVz(); } - if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { - continue; + float centralityCollision1 = 999.f; + float centralityCollision2 = 999.f; + for (const auto& reconstructedCollision1 : groupedCollisions1) { + // if (!collision1.sel8()) { + // continue; + // } + // if (reconstructedCollision1.globalIndex() != collision1.bestCollisionIndex()) { + // continue; + // } + centralityCollision1 = getCentralityEstimator(reconstructedCollision1, false); + // break; } - if ((multiplicityCollision2 < configCollision.minMultiplicity || multiplicityCollision2 >= configCollision.maxMultiplicity)) { - continue; + for (const auto& reconstructedCollision2 : groupedCollisions2) { + // if (reconstructedCollision2.globalIndex() != collision2.bestCollisionIndex()) { + // continue; + // } + centralityCollision2 = getCentralityEstimator(reconstructedCollision2, false); + // break; } - auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); - auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); - if (groupedCollisions1.size() == 0) { - return; + if (configCollision.useCentrality) { + if (centralityCollision1 < configCollision.minCentrality || centralityCollision1 >= configCollision.maxCentrality) { + continue; + } + if (centralityCollision2 < configCollision.minCentrality || centralityCollision2 >= configCollision.maxCentrality) { + continue; + } } - if (groupedCollisions2.size() == 0) { - return; + + if (!configCollision.useCentrality) { + if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { + continue; + } + if ((multiplicityCollision2 < configCollision.minMultiplicity || multiplicityCollision2 >= configCollision.maxMultiplicity)) { + continue; + } } mixedEvent->fillEvent(multiplicityCollision1, CorrelationContainer::kCFStepAll); @@ -4706,7 +4753,7 @@ struct HfTaskFlow { } auto groupedreassociated3dMftTracks = reassociated3dMftTracks.sliceBy(perColReassociated3dTracks, reconstructedCollision.globalIndex()); - for (const auto& reassociated3dMftTrack : reassociated3dMftTracks) { + for (const auto& reassociated3dMftTrack : groupedreassociated3dMftTracks) { if (!reassociated3dMftTrack.has_mcParticle()) { continue; } From 6178aee8258a89b4e716c040f41bfb315cebb77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:14:16 +0200 Subject: [PATCH 252/648] [Infrastructure,Scripts] Add more pre-commit hooks (#18232) --- .pre-commit-config.yaml | 40 ++++++++++++++++-- Scripts/format_includes.awk | 2 +- Scripts/format_includes.py | 84 +++++++++++++++++++++++++++++++++++++ Scripts/o2_linter.py | 0 4 files changed, 121 insertions(+), 5 deletions(-) mode change 100644 => 100755 Scripts/format_includes.awk create mode 100755 Scripts/format_includes.py mode change 100644 => 100755 Scripts/o2_linter.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f837e1ad5a0..cf0e0a1b341 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,15 +2,47 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: a6564f5d57444e123433a5c9af733e3975c77229 # frozen: v0.11.0.1-1 + hooks: + - id: shellcheck + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10 + hooks: + - id: ruff-check # linter + args: ["--fix"] + - id: ruff-format # formatter + - repo: local + hooks: + - id: format-includes + name: format includes + language: python + entry: Scripts/format_includes.py + types: [c++] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.3 # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + rev: 7ec07eeb4135dc6ecf410d24ac4ead986a5d413d # frozen: v20.1.3 hooks: - id: clang-format - repo: https://github.com/cpplint/cpplint - rev: 2.0.2 + rev: 350702a4d39395571bd118c9a3cff0a573488d61 # frozen: 2.0.2 hooks: - id: cpplint + - repo: local + hooks: + - id: O2 linter + name: O2 linter + language: python + entry: Scripts/o2_linter.py + types_or: [c++, cmake, python] diff --git a/Scripts/format_includes.awk b/Scripts/format_includes.awk old mode 100644 new mode 100755 index 42484d62830..b0224f1f747 --- a/Scripts/format_includes.awk +++ b/Scripts/format_includes.awk @@ -15,7 +15,7 @@ else if ( h ~ /^([[:alnum:]_]+\/)+[[:alnum:]_]+\.h/ ) { $2 = "<"h">" } # other third-party else if ( $2 ~ /^".*\./ ) { } # other local-looking file else if ( h ~ /^[[:lower:]_]+\.h/ ) { $2 = "<"h">" } # C system - else if ( h ~ /^[[:lower:]_\/]+/ ) { $2 = "<"h">" } # C++ system + else if ( h ~ /^[[:lower:]_\/]+$/ ) { $2 = "<"h">" } # C++ system } print } diff --git a/Scripts/format_includes.py b/Scripts/format_includes.py new file mode 100755 index 00000000000..c545441d9af --- /dev/null +++ b/Scripts/format_includes.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +"""! +@brief Fix include style +@author Vít Kučera , Inha University +@date 2026-10-08 + +NB: Run before sorting. + +Usage: format_includes.py FILE [FILE ...] +""" + +import re +import sys + +LOCAL = ('"', '"') +EXTERNAL = ("<", ">") + +INCLUDE = re.compile(r"(\s*#include\s+)(\S+)") + + +def fix_line(line: str) -> str: + m = INCLUDE.match(line) + if not m: + return line + pre, tok = m.groups() + rest = line[m.end() :] + h = tok[1:-1] + + if re.match(r"(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)/.*\.h", h): + d = LOCAL # O2Physics + elif re.match( + r"(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*/.*\.h", + h, + ): + d = EXTERNAL # O2 + elif re.match(r"(T[A-Z]|Math/|Roo[A-Z])[A-Za-z0-9/]+\.h", h): + d = EXTERNAL # ROOT + elif re.match(r"KF[A-Z][A-Za-z0-9]+\.h", h): + d = EXTERNAL # KFParticle + elif re.match(r"(fastjet/|onnxruntime)", h): + d = EXTERNAL # FastJet, ONNX + elif re.match(r".*DataModel/", h): + d = LOCAL # incomplete path to DataModel + elif re.match(r"([A-Za-z0-9_]+/)+[A-Za-z0-9_]+\.h", h): + d = EXTERNAL # other third-party + elif re.match(r'".*\.', tok): + return line # other local-looking file + elif re.match(r"[a-z_]+\.h", h): + d = EXTERNAL # C system + elif re.match(r"[a-z_/]+$", h): + d = EXTERNAL # C++ system (whole string) + else: + return line + + return pre + d[0] + h + d[1] + rest + + +def process(path: str): + # newline='' keeps the original line endings untouched. + with open(path, newline="") as f: + lines = f.readlines() + new_lines = [fix_line(line) for line in lines] + if new_lines != lines: + with open(path, "w", newline="") as f: + f.writelines(new_lines) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit(__doc__) + for p in sys.argv[1:]: + process(p) diff --git a/Scripts/o2_linter.py b/Scripts/o2_linter.py old mode 100644 new mode 100755 From 8cbf54ac60a418125b377560cfe084dc295d7605 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:57:27 +0200 Subject: [PATCH 253/648] [PWGCF,PWGLF] Drop the redundant ignore-overflows argument from binning policies (#18247) --- .../Tasks/hParticleCorrelation.cxx | 16 ++++++++-------- .../Tasks/pidLongRange.cxx | 4 ++-- .../Tasks/twoParticleCorrelationsMpi.cxx | 2 +- .../Tasks/Strangeness/lambdaspincorrderived.cxx | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx index 1325cf71f8a..fe1e9b042fa 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -1730,10 +1730,10 @@ struct HParticleCorrelation2SameEvent { using BinningTypeVtxZFT0A = ColumnBinningPolicy; using BinningTypeVtxZFV0A = ColumnBinningPolicy; - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}, true}; + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; int nMixBins = 0; @@ -4076,10 +4076,10 @@ struct HParticleCorrelation3MixedEvent { using BinningTypeVtxZFT0A = ColumnBinningPolicy; using BinningTypeVtxZFV0A = ColumnBinningPolicy; - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; template int getDerivedMixingBin(const C& collision) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx index a8974041278..8e7f07b69ab 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -1546,7 +1546,7 @@ struct PidLongRange { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1708,7 +1708,7 @@ struct PidLongRange { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index 1aa5b424941..85f2005609b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -2192,7 +2192,7 @@ struct TwoParticleCorrelationsMpi { return getAnalysisMultiplicity(col); }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // underflows and overflows get bin -1 const float collisionMultiplicity = getMultiplicity(collision); const float multiplicity = outputMultiplicityOverride ? *outputMultiplicityOverride : collisionMultiplicity; const float efficiencyMultiplicity = efficiencyMultiplicityOverride ? *efficiencyMultiplicityOverride : collisionMultiplicity; diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 6224d7509ab..98167f6fe0d 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -2762,7 +2762,7 @@ struct lambdaspincorrderived { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}}; Preslice tracksPerCollisionV0 = aod::lambdapair::lambdaeventId; void processMEV3(EventCandidates const& collisions, AllTrackCandidates const& V0s) From a4613d448bacf5b56514b7a4eecc09fae6343aee Mon Sep 17 00:00:00 2001 From: Subhadeep Mandal <147193283+5ub-Man@users.noreply.github.com> Date: Mon, 12 Oct 2026 04:02:05 +0530 Subject: [PATCH 254/648] [PWGUD] Added histogram to store run by run interaction rate (#18243) --- PWGUD/Tasks/sginclusivePhiKstarSD.cxx | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx index e6059892c94..41713b8ca40 100644 --- a/PWGUD/Tasks/sginclusivePhiKstarSD.cxx +++ b/PWGUD/Tasks/sginclusivePhiKstarSD.cxx @@ -46,9 +46,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -155,6 +157,27 @@ struct SginclusivePhiKstarSD { ConfigurableAxis axispt{"axispt", {200, 0.0, 20.0}, ""}; ConfigurableAxis axisrapdity{"axisrapdity", {40, -2.0, 2.0}, ""}; + std::vector runNumbers = { + 544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, + 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, + 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, + 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, + 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, + 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, + 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, + 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, + 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, + 545296, 545312}; + + int getRunBin(int runNumber) + { + auto it = std::lower_bound(runNumbers.begin(), runNumbers.end(), runNumber); + if (it == runNumbers.end() || *it != runNumber) { + return -1; + } + return static_cast(std::distance(runNumbers.begin(), it)) + 1; + } + int numTwoTracks = 2; int numFourTracks = 4; @@ -307,6 +330,13 @@ struct SginclusivePhiKstarSD { rQA.add("V0A_2", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_0", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_1", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); + + rQA.add("hIntRateVsRun", "Hadronic interaction rate; Run number; Hadronic interaction rate (kHz)", kTH2F, {{92, 0.5, 92.5}, {1000, 0., 50.}}); + auto hIntRateVsRun = rQA.get(HIST("hIntRateVsRun")); + for (size_t i = 0; i < runNumbers.size(); ++i) { + hIntRateVsRun->GetXaxis()->SetBinLabel(i + 1, Form("%d", runNumbers[i])); + } + hIntRateVsRun->GetXaxis()->LabelsOption("v"); } registry.add("gap_mult0", "Mult 0", kTH1F, {{100, 0.0, 100.0}}); registry.add("gap_mult1", "Mult 1", kTH1F, {{100, 0.0, 100.0}}); @@ -785,6 +815,11 @@ struct SginclusivePhiKstarSD { { if (qa) { rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + + int runBin = getRunBin(collision.runNumber()); + if (runBin > 0 && collision.hadronicRate() > 0.) { + rQA.fill(HIST("hIntRateVsRun"), runBin, collision.hadronicRate()); + } } ROOT::Math::PxPyPzMVector v0; From d965f075af7a1b1764efffaf7e125ad2df963531 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Mon, 12 Oct 2026 06:35:40 +0530 Subject: [PATCH 255/648] [PWGCF] Lambda-Proton BF - Update 6: Enhance few diagnostics and cutflows (#18249) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 237 +++++++++++++++--- 1 file changed, 199 insertions(+), 38 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 8fe2fd62647..83aae01ed99 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -53,7 +53,9 @@ #include #include #include +#include #include +#include #include using namespace o2; @@ -417,7 +419,7 @@ struct LambdaProtonBalanceFunction { static void fillIdx(std::shared_ptr const& h, int a, int b) { if (h && a >= 0 && b >= 0) { - h->Fill(a, b); + h->Fill(a + 0.5f, b + 0.5f); } } // ───────────────────────────────────────────────────────────────────── @@ -1288,6 +1290,27 @@ struct LambdaProtonBalanceFunction { registryOther.add("hNPairs_AntiLambda_PrimProton", "#bar{#Lambda}-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); registryOther.add("hNPairs_AntiLambda_PrimAntiProton", "#bar{#Lambda}-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + // ── Shared-daughter (union) cleaning diagnostics for Lambda / AntiLambda ── + registryOther.add("h1f_nLambda_beforeClean", "N_{#Lambda} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_rejectedClean", "N_{#Lambda} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_afterClean", "N_{#Lambda} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_beforeClean", "N_{#bar{#Lambda}} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_rejectedClean", "N_{#bar{#Lambda}} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_afterClean", "N_{#bar{#Lambda}} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("hSharedDauCleaningSums", "Shared-daughter cleaning: sums over events;Quantity;Sum", {HistType::kTH1D, {{9, 0.5f, 9.5f}}}); + { + auto hSD = registryOther.get(HIST("hSharedDauCleaningSums")); + hSD->GetXaxis()->SetBinLabel(1, "Sum N_L raw"); + hSD->GetXaxis()->SetBinLabel(2, "Sum N_L rej"); + hSD->GetXaxis()->SetBinLabel(3, "Sum N_L clean"); + hSD->GetXaxis()->SetBinLabel(4, "Sum N_AL raw"); + hSD->GetXaxis()->SetBinLabel(5, "Sum N_AL rej"); + hSD->GetXaxis()->SetBinLabel(6, "Sum N_AL clean"); + hSD->GetXaxis()->SetBinLabel(7, "Sum N_L*(N_L-1)"); + hSD->GetXaxis()->SetBinLabel(8, "Sum N_AL*(N_AL-1)"); + hSD->GetXaxis()->SetBinLabel(9, "Sum N_L*N_AL"); + } + // ══════════════════════════════════════════════════════════════════════════ // NEW STAGED QA_Detec REGISTRATION // ══════════════════════════════════════════════════════════════════════════ @@ -1464,6 +1487,23 @@ struct LambdaProtonBalanceFunction { hCF->GetXaxis()->SetBinLabel(15, "Selected"); } + // ── V0 (Lambda / AntiLambda) cutflow histograms ───────────────────────── + // Bins 1-3 are filled for every V0 under BOTH hypotheses (PID hypothesis not yet known). + // From bin 4 on, each V0 is counted only in the cutflow of the hypothesis chosen by TPC PID. + // Bin 10 = survivors of shared-daughter cleaning (the sample entering rho1 / rho2). Bin 11 is spare. + registryQaDetector.add("Lambda/hCutFlow", "#Lambda candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + registryQaDetector.add("AntiLambda/hCutFlow", "#bar{#Lambda} candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + { + const char* v0CfLabels[10] = {"All V0s", "V0 Type", "Daughter Kinematics", "Unambiguous TPC PID", "Rapidity", + "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; + auto hCFL = registryQaDetector.get(HIST("Lambda/hCutFlow")); + auto hCFAL = registryQaDetector.get(HIST("AntiLambda/hCutFlow")); + for (int ib = 0; ib < 10; ++ib) { + hCFL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + hCFAL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + } + } + // 2D cutflow vs pT, separate for protons and antiprotons. // y bins 1-15 = sequential stages (same as hCutFlow); 16-21 = veto REASONS (not sequential, a track can fill several) registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton", "Proton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); @@ -2235,6 +2275,10 @@ struct LambdaProtonBalanceFunction { const auto& v0PosDauCached = v0.posTrack_as(); const auto& v0NegDauCached = v0.negTrack_as(); + // V0 cutflow bin 1: all V0s (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 1.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 1.0f); + // ── TRULY RAW QA Fills — before any V0 selection cut ───────────── // Placed here at the very top of the loop: before v0Type, daughter // kinematics, TPC PID, rapidity, pT, topology, DCA, CTau, K0S, or @@ -2268,6 +2312,9 @@ struct LambdaProtonBalanceFunction { if (v0.v0Type() != cV0TypeSelection.value) { continue; } + // V0 cutflow bin 2: V0 type (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 2.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 2.0f); // ───────────────────────────────────────────────────────────────── const auto& v0PosDau = v0PosDauCached; @@ -2280,6 +2327,9 @@ struct LambdaProtonBalanceFunction { v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { continue; } + // V0 cutflow bin 3: daughter kinematics (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 3.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 3.0f); // ───────────────────────────────────────────────────────────────── // ── PID hypothesis ──────────────────────────────────────────────── @@ -2294,6 +2344,12 @@ struct LambdaProtonBalanceFunction { if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { continue; } + // V0 cutflow bin 4: unambiguous TPC PID — from here on, fill only the chosen hypothesis + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 4.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 4.0f); + } // ───────────────────────────────────────────────────────────────── // ── TPC shared clusters cut on daughter tracks ──────────────────── @@ -2314,6 +2370,12 @@ struct LambdaProtonBalanceFunction { if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { continue; } + // V0 cutflow bin 5: rapidity + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 5.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 5.0f); + } // ───────────────────────────────────────────────────────────────── // ── Eta cuts ────────────────────────────────────────────────────── @@ -2328,6 +2390,12 @@ struct LambdaProtonBalanceFunction { (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value)) { continue; } + // V0 cutflow bin 6: pT acceptance + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 6.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 6.0f); + } // ───────────────────────────────────────────────────────────────── // ── DCA of daughters to PV ──────────────────────────────────────── @@ -2336,10 +2404,8 @@ struct LambdaProtonBalanceFunction { const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); - // ── Lifetime (needed for Final QA fills and CTau cut below) ────────── + // ── Lifetime (needed for the CTau cut below; Final QA recomputes ctau/bgctau on the cleaned sample) ── const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; - // FIX-3: compute lab decay length (gamma*c*tau) = ctau * |p| / m - const float bgctau = ctau * v0.p() / mLambdaPDG; // ── (Raw QA fills moved to top of V0 loop — see TRULY RAW block above) ─ @@ -2354,6 +2420,12 @@ struct LambdaProtonBalanceFunction { dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { continue; } + // V0 cutflow bin 7: topology (passTopoCuts) and daughter DCA-to-PV + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 7.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 7.0f); + } // ───────────────────────────────────────────────────────────────── if (ctau > lambdaV0MaxCTau.value) { @@ -2363,6 +2435,12 @@ struct LambdaProtonBalanceFunction { if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { continue; } + // V0 cutflow bin 8: cTau and K0s rejection + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 8.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 8.0f); + } // ───────────────────────────────────────────────────────────────── // ── Mass window selection: |m - mPDG| < lambdaV0MassWindow ───────── @@ -2494,44 +2572,127 @@ struct LambdaProtonBalanceFunction { // ── Step 3: Selected Lambda — populate veto set + rho1 + pT histos ─ if (selLambda) { - ++nSelLambda; + // V0 cutflow bin 9: mass window (pre-cleaning) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 9.0f); selectedLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); - fillLambdaQA(LambdaQAStage::Final, v0, ctau, bgctau); - fillDaughterQA(DaughterQAStage::Final, v0PosDau); - fillDaughterQA(DaughterQAStage::Final, v0NegDau); - - registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); - if (cs) { - cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); - } - registryOther.fill(HIST("hPtSelLambda"), pt); - state.hPtSelLambda_AllPt->Fill(pt); + // Final QA, rho1 / pT spectra and nSelLambda are filled after shared-daughter cleaning (below). } if (selAntiLambda) { - ++nSelAntiLambda; + // V0 cutflow bin 9: mass window (pre-cleaning) + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 9.0f); selectedAntiLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); - fillLambdaQA(LambdaQAStage::Final, v0, ctau, bgctau); - fillDaughterQA(DaughterQAStage::Final, v0NegDau); - fillDaughterQA(DaughterQAStage::Final, v0PosDau); - - registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); - if (cs) { - cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); - } - registryOther.fill(HIST("hPtSelAntiLambda"), pt); - state.hPtSelAntiLambda_AllPt->Fill(pt); + // Final QA, rho1 / pT spectra and nSelAntiLambda are filled after shared-daughter cleaning (below). } // ───────────────────────────────────────────────────────────────── } // ====== End of Step 2+3 V0 validation loop ============================ + // ====== Shared-daughter cleaning (symmetric, over the Lambda ∪ AntiLambda union) ====== + // Two candidates sharing a positive OR a negative daughter track are BOTH rejected. + // v0DaughterTrackIds is intentionally left untouched (conservative primary veto). + const int nRawLambda = static_cast(selectedLambdas.size()); + const int nRawAntiLambda = static_cast(selectedAntiLambdas.size()); + + std::unordered_map posIdCount; + std::unordered_map negIdCount; + for (const auto& v : selectedLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + for (const auto& v : selectedAntiLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + + std::vector cleanLambdas; + std::vector cleanAntiLambdas; + for (const auto& v : selectedLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanLambdas.push_back(v); + } + } + for (const auto& v : selectedAntiLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanAntiLambdas.push_back(v); + } + } + selectedLambdas = std::move(cleanLambdas); + selectedAntiLambdas = std::move(cleanAntiLambdas); + + // V0 cutflow bin 10: survivors of shared-daughter cleaning + for ([[maybe_unused]] const auto& v : selectedLambdas) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 10.0f); + } + for ([[maybe_unused]] const auto& v : selectedAntiLambdas) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 10.0f); + } + + nSelLambda = static_cast(selectedLambdas.size()); + nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + + // Cleaning diagnostics + registryOther.fill(HIST("h1f_nLambda_beforeClean"), static_cast(nRawLambda)); + registryOther.fill(HIST("h1f_nLambda_rejectedClean"), static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("h1f_nLambda_afterClean"), static_cast(nSelLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_beforeClean"), static_cast(nRawAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_rejectedClean"), static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_afterClean"), static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 1.0f, static_cast(nRawLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 2.0f, static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 3.0f, static_cast(nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 4.0f, static_cast(nRawAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 5.0f, static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 6.0f, static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 7.0f, static_cast(nSelLambda) * static_cast(nSelLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 8.0f, static_cast(nSelAntiLambda) * static_cast(nSelAntiLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 9.0f, static_cast(nSelLambda) * static_cast(nSelAntiLambda)); + + // rho1 and pT spectra from the cleaned sample (same sample as rho2) + for (const auto& v0 : selectedLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauPos); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + } + registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); + if (cs) { + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); + } + registryOther.fill(HIST("hPtSelLambda"), v0.pt()); + state.hPtSelLambda_AllPt->Fill(v0.pt()); + } + for (const auto& v0 : selectedAntiLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + fillDaughterQA(DaughterQAStage::Final, dauPos); + } + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); + if (cs) { + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); + } + registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); + state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); + } + // ====== End of shared-daughter cleaning =============================== + // ── Veto QA event counters (filled once per event) ─────────────────── for (auto const& trk : tracks) { @@ -2916,7 +3077,7 @@ struct LambdaProtonBalanceFunction { const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_pp_y_AllPt->Fill(idxY1, idxY2); + state.hRho2_pp_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); } if (cs) { fillIdx(cs->pp_y, idxY1, idxY2); @@ -2934,7 +3095,7 @@ struct LambdaProtonBalanceFunction { continue; } if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_pAp_y_AllPt->Fill(a.idxY, b.idxY); + state.hRho2_pAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); } if (cs) { fillIdx(cs->pAp_y, a.idxY, b.idxY); @@ -2952,7 +3113,7 @@ struct LambdaProtonBalanceFunction { continue; } if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_App_y_AllPt->Fill(a.idxY, b.idxY); + state.hRho2_App_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); } if (cs) { fillIdx(cs->App_y, a.idxY, b.idxY); @@ -2973,7 +3134,7 @@ struct LambdaProtonBalanceFunction { continue; } if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_ApAp_y_AllPt->Fill(a.idxY, b.idxY); + state.hRho2_ApAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); } if (cs) { fillIdx(cs->ApAp_y, a.idxY, b.idxY); @@ -3085,7 +3246,7 @@ struct LambdaProtonBalanceFunction { state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } if (idxYL >= 0 && idxYP >= 0) { - state.hRho2_Lp_y_AllPt->Fill(idxYL, idxYP); + state.hRho2_Lp_y_AllPt->Fill(idxYL + 0.5f, idxYP + 0.5f); } if (cs) { fillIdx(cs->Lp_y, idxYL, idxYP); @@ -3163,7 +3324,7 @@ struct LambdaProtonBalanceFunction { state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } if (idxYL2 >= 0 && idxYAp >= 0) { - state.hRho2_LAp_y_AllPt->Fill(idxYL2, idxYAp); + state.hRho2_LAp_y_AllPt->Fill(idxYL2 + 0.5f, idxYAp + 0.5f); } if (cs) { fillIdx(cs->LAp_y, idxYL2, idxYAp); @@ -3313,7 +3474,7 @@ struct LambdaProtonBalanceFunction { state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } if (idxYAL >= 0 && idxYP2 >= 0) { - state.hRho2_ALp_y_AllPt->Fill(idxYAL, idxYP2); + state.hRho2_ALp_y_AllPt->Fill(idxYAL + 0.5f, idxYP2 + 0.5f); } if (cs) { fillIdx(cs->ALp_y, idxYAL, idxYP2); @@ -3398,7 +3559,7 @@ struct LambdaProtonBalanceFunction { state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } if (idxYAL2 >= 0 && idxYAp2 >= 0) { - state.hRho2_ALAp_y_AllPt->Fill(idxYAL2, idxYAp2); + state.hRho2_ALAp_y_AllPt->Fill(idxYAL2 + 0.5f, idxYAp2 + 0.5f); } if (cs) { fillIdx(cs->ALAp_y, idxYAL2, idxYAp2); @@ -3512,7 +3673,7 @@ struct LambdaProtonBalanceFunction { const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_LL_y_AllPt->Fill(idxY1, idxY2); + state.hRho2_LL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); } if (cs) { fillIdx(cs->LL_y, idxY1, idxY2); @@ -3562,7 +3723,7 @@ struct LambdaProtonBalanceFunction { const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_LAL_y_AllPt->Fill(idxY1, idxY2); + state.hRho2_LAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); } if (cs) { fillIdx(cs->LAL_y, idxY1, idxY2); @@ -3612,7 +3773,7 @@ struct LambdaProtonBalanceFunction { const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_ALL_y_AllPt->Fill(idxY1, idxY2); + state.hRho2_ALL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); } if (cs) { fillIdx(cs->ALL_y, idxY1, idxY2); @@ -3665,7 +3826,7 @@ struct LambdaProtonBalanceFunction { const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_ALAL_y_AllPt->Fill(idxY1, idxY2); + state.hRho2_ALAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); } if (cs) { fillIdx(cs->ALAL_y, idxY1, idxY2); From ff6a324733be79d9a14e9c5b0ee6330c8946e0a0 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 12 Oct 2026 03:22:33 +0200 Subject: [PATCH 256/648] [PWGLF] Adapt matching study to the Qvector tables (#18250) --- PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx index 67837173150..17dcdd62ec5 100644 --- a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx +++ b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx @@ -230,6 +230,8 @@ std::shared_ptr hFlowHists[2][5]; std::shared_ptr hDCAHists[2][5]; std::shared_ptr hMatchingStudy[2]; std::shared_ptr hMatchingStudyHadrons[2]; +std::shared_ptr hMatchingCountsHadrons; +std::shared_ptr hMatchingCountsHe3; o2::base::MatLayerCylSet* lut = nullptr; std::vector candidates; @@ -366,6 +368,7 @@ struct nucleiSpectra { ConfigurableAxis cfgNITSClusBins{"cfgNITSClusBins", {3, 4.5, 7.5}, "N ITS clusters binning"}; ConfigurableAxis cfgNTPCClusBins{"cfgNTPCClusBins", {3, 89.5, 159.5}, "N TPC clusters binning"}; + Configurable cfgMatchingCounts{"cfgMatchingCounts", false, "Enable event-level ITS-TPC matching counts for binomiality studies"}; Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; Configurable cfgTriggerList{"cfgTriggerList", "fHe", "Trigger List"}; Configurable cfgSelectTrgEv{"cfgSelectTrgEv", false, "If true, select events with active trigger list"}; @@ -601,6 +604,10 @@ struct nucleiSpectra { if (doprocessMatching) { std::vector occBins{-0.5, 499.5, 999.5, 1999.5, 2999.5, 3999.5, 4999.5, 10000., 50000.}; AxisSpec occAxis{occBins, "Occupancy"}; + if (cfgMatchingCounts) { + nuclei::hMatchingCountsHadrons = spectra.add("hMatchingCountsHadrons", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + nuclei::hMatchingCountsHe3 = spectra.add("hMatchingCountsHe3", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + } for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { nuclei::hMatchingStudy[iC] = spectra.add(fmt::format("hMatchingStudy{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;n#sigma_{ITS};n#sigma{TPC};n#sigma_{TOF};Centrality", HistType::kTHnSparseF, {{20, 1., 9.}, {10, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {50, -5., 5.}, {50, -5., 5.}, {50, 0., 1.}, {8, 0., 80.}}); nuclei::hMatchingStudyHadrons[iC] = spectra.add(fmt::format("hMatchingStudyHadrons{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;Centrality;Track type; Occupancy", HistType::kTHnF, {{23, 0.4, 5.}, {20, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {8, 0., 80.}, {2, -0.5, 1.5}, occAxis}); @@ -1156,12 +1163,17 @@ struct nucleiSpectra { } PROCESS_SWITCH(nucleiSpectra, processMC, "MC analysis", false); - void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) + void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { - if (!eventSelection(collision) || !collision.triggereventep()) { + if (!eventSelection(collision) || !collision.isCalibrated()) { return; } const float centrality = collision.centFT0C(); + const float psi2FT0C = std::atan2(collision.qvecFT0CIm(), collision.qvecFT0CRe()) / 2.f; + int nITSHadrons[2]{}; + int nITSTPCHadrons[2]{}; + int nITSHe3[2]{}; + int nITSTPCHe3[2]{}; o2::aod::ITSResponse itsResponse; for (const auto& track : tracks) { if (std::abs(track.eta()) > cfgTrackCut.EtaMax || @@ -1173,13 +1185,28 @@ struct nucleiSpectra { double expSigma{expBethe * cfgBetheBlochParams->get(4, 5u)}; double nSigmaTPC{(track.tpcSignal() - expBethe) / expSigma}; int iC = track.signed1Pt() > 0; + if (cfgMatchingCounts) { + ++nITSHadrons[iC]; + nITSTPCHadrons[iC] += track.hasTPC(); + } const float pt = track.pt(); - const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - collision.psiFT0C(), 0.f, 2); + const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - psi2FT0C, 0.f, 2); nuclei::hMatchingStudyHadrons[iC]->Fill(pt, phi, track.eta(), centrality, track.hasTPC(), collision.trackOccupancyInTimeRange()); - if (itsResponse.nSigmaITS(track) > -1.) { + if (itsResponse.nSigmaITS(track) > -2.) { + if (cfgMatchingCounts) { + ++nITSHe3[iC]; + nITSTPCHe3[iC] += track.hasTPC(); + } nuclei::hMatchingStudy[iC]->Fill(pt * 2, phi, track.eta(), itsResponse.nSigmaITS(track), nSigmaTPC, o2::pid::tof::Beta::GetBeta(track), centrality); } } + if (cfgMatchingCounts) { + // One entry per collision and charge sign, including zero-track events. + for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { + nuclei::hMatchingCountsHadrons->Fill(nITSHadrons[iC], nITSTPCHadrons[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + nuclei::hMatchingCountsHe3->Fill(nITSHe3[iC], nITSTPCHe3[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + } + } } PROCESS_SWITCH(nucleiSpectra, processMatching, "Matching analysis", false); From cde9f2a3856bfda717d5e752df18ef06142171b0 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:26:19 +0530 Subject: [PATCH 257/648] [PWGLF] Added QA plots for phi (#18248) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 31ac540c95a..c2e2a545c8e 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -375,6 +375,10 @@ struct Kstarqa { hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h1D_MassPhiGen", "Invariant mass of generated Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h1D_MassPhiRec", "Invariant mass of reconstructed Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h2D_pt_massShiftPhi", "pT vs mass shift of Phi meson", kTH2F, {{ptAxis}, {300, -0.1, 0.1}}); } // Signal Loss & Event Loss in Light Ion Collisions @@ -3192,7 +3196,19 @@ struct Kstarqa { if (track1.sign() * track2.sign() < 0) { daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); + + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); + mother = daughter1 + daughter2; // Phi meson + genMother = genDaughter1 + genDaughter2; // Phi meson + + recMass = mother.M(); + genMass = genMother.M(); + + hShift.fill(HIST("h1D_MassPhiGen"), genMass); + hShift.fill(HIST("h1D_MassPhiRec"), recMass); + hShift.fill(HIST("h2D_pt_massShiftPhi"), mother.pt(), genMass - recMass); auto genpTPhi = mothertrack1.pt(); auto genEtaPhi = mothertrack1.eta(); From 5fe973c0b43c57d6fe68e2aad0d7859773a3ca57 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Mon, 12 Oct 2026 09:14:32 +0200 Subject: [PATCH 258/648] [DPG] treeCreatorPidTpcDiagnostics: reduce output tree size (#18241) --- .../TPC/treeCreatorPidTpcDiagnostics.cxx | 98 ++++++++++++------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx index 9f30e558d2c..66b1d831d3c 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx @@ -41,8 +41,12 @@ #include #include +#include #include +#include +#include + #include #include #include @@ -53,16 +57,16 @@ using namespace o2::framework; using namespace o2::track; using namespace o2::dpg_tpcskimstablecreator; -#define DO_FOR_ALL_PARTICLES(MACRO) \ - MACRO(El, Electron) \ - MACRO(Mu, Muon) \ - MACRO(Pi, Pion) \ - MACRO(Ka, Kaon) \ - MACRO(Pr, Proton) \ - MACRO(De, Deuteron) \ - MACRO(Tr, Triton) \ - MACRO(He, Helium3) \ - MACRO(Al, Alpha) +#define DO_FOR_ALL_PARTICLES(MACRO, ARG) \ + MACRO(El, Electron, ARG) \ + MACRO(Mu, Muon, ARG) \ + MACRO(Pi, Pion, ARG) \ + MACRO(Ka, Kaon, ARG) \ + MACRO(Pr, Proton, ARG) \ + MACRO(De, Deuteron, ARG) \ + MACRO(Tr, Triton, ARG) \ + MACRO(He, Helium3, ARG) \ + MACRO(Al, Alpha, ARG) struct TreeCreatorPidTpcDiagnostics { Produces rowPidTpcDiagnostics; @@ -86,28 +90,38 @@ struct TreeCreatorPidTpcDiagnostics { Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; -#define DECLARE_PARTICLE_WISE_CONFIGURABLES(ParticleNameShort, ParticleNameLong) \ - Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ - Configurable cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ - Configurable cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro) + struct ParticleWiseCfgs : ConfigurableGroup { + +#define DECLARE_PARTICLE_WISE_CONFIGURABLES(Unused1, ParticleNameLong, Unused2) \ + Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutExpSigmaMax##ParticleNameLong{"cutExpSigmaMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected sigma for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMin##ParticleNameLong{"cutDeDxExpectedMin" #ParticleNameLong, -1e9f, "Lower-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMax##ParticleNameLong{"cutDeDxExpectedMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMin##ParticleNameLong{"cutDeDxDiffMin" #ParticleNameLong, -1e9f, "Lower-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMax##ParticleNameLong{"cutDeDxDiffMax" #ParticleNameLong, 1e9f, "Upper-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable downsampleFactor##ParticleNameLong{"downsampleFactor" #ParticleNameLong, 1.f, "Downsampling factor for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro) - DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES) + DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES, _) #undef DECLARE_PARTICLE_WISE_CONFIGURABLES + } particleWiseCfgs; -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMin##ParticleNameLong, - std::array*, PID::Alpha + 1> cutTpcInnerParameterMin{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; -#undef PACK_CONFIGURABLES_TO_ARRAY +#define PARTICLE_WISE_CFG_NAMES \ + (cutTpcInnerParameterMin)(cutTpcInnerParameterMax)(cutExpSigmaMax)(cutDeDxExpectedMin)(cutDeDxExpectedMax)(cutDeDxDiffMin)(cutDeDxDiffMax)(cutNSigmaTpcAbs)(downsampleFactor) -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMax##ParticleNameLong, - std::array*, PID::Alpha + 1> cutTpcInnerParameterMax{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; -#undef PACK_CONFIGURABLES_TO_ARRAY +#define MAKE_PARTICLE_CUT_POINTER(Unused, ParticleNameLong, CfgName) \ + &particleWiseCfgs.BOOST_PP_CAT(CfgName, ParticleNameLong), + +#define PACK_CONFIGURABLES_TO_ARRAY(Unused1, Unused2, CfgName) \ + std::array*, PID::Alpha + 1> CfgName{ \ + DO_FOR_ALL_PARTICLES(MAKE_PARTICLE_CUT_POINTER, CfgName)}; + + BOOST_PP_SEQ_FOR_EACH(PACK_CONFIGURABLES_TO_ARRAY, _, PARTICLE_WISE_CFG_NAMES) -#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutNSigmaTpcAbs##ParticleNameLong, - std::array*, PID::Alpha + 1> cutNSigmaTpcAbs{ - DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)}; #undef PACK_CONFIGURABLES_TO_ARRAY +#undef MAKE_PARTICLE_CUT_POINTER +#undef PARTICLE_WISE_CFG_NAMES HistogramRegistry registry{"registry", {}}; @@ -117,6 +131,8 @@ struct TreeCreatorPidTpcDiagnostics { o2::aod::rctsel::RCTFlagsChecker rctChecker; + TRandom3 mRnd; + using CollisionsExtra = soa::Join; using TrackCandidates = soa::Join; @@ -132,7 +148,7 @@ struct TreeCreatorPidTpcDiagnostics { int enabledProcesses{0}; switch (ParticleId) { -#define INIT_PARTICLE(ParticleNameShort, ParticleNameLong) \ +#define INIT_PARTICLE(Unused1, ParticleNameLong, Unused2) \ case PID::ParticleNameLong: \ if (!doprocess##ParticleNameLong && !doprocessFull##ParticleNameLong && !doprocessFullWithTOF##ParticleNameLong) { \ return false; \ @@ -149,7 +165,7 @@ struct TreeCreatorPidTpcDiagnostics { LOG(info) << "Enabled TPC QA for " << #ParticleNameLong; \ break; - DO_FOR_ALL_PARTICLES(INIT_PARTICLE) + DO_FOR_ALL_PARTICLES(INIT_PARTICLE, _) #undef INIT_PARTICLE } if (enabledProcesses != 1) { @@ -212,6 +228,10 @@ struct TreeCreatorPidTpcDiagnostics { const auto tracksFromCollision = tracks.sliceBy(perCollisionTracks, static_cast(collision.globalIndex())); for (const auto& track : tracksFromCollision) { + if (*downsampleFactor.at(ParticleId) < 1.f && mRnd.Uniform(1) > *downsampleFactor.at(ParticleId)) { + continue; + } + bool isGoodTrack = isTrackSelected(track, trackSelection); isGoodTrack &= (!requireGlobalTrack || track.isGlobalTrack()); isGoodTrack &= (!requireIts || track.hasITS()); @@ -233,10 +253,6 @@ struct TreeCreatorPidTpcDiagnostics { isGoodTrack &= (tpcInnerParam <= *cutTpcInnerParameterMax.at(ParticleId)); isGoodTrack &= (std::fabs(nSigmaTpc) <= *cutNSigmaTpcAbs.at(ParticleId)); - if (!isGoodTrack) { - continue; - } - float dedxDiff{UndefValueFloat}; float dedxExpected{UndefValueFloat}; float expSigma{UndefValueFloat}; @@ -245,6 +261,16 @@ struct TreeCreatorPidTpcDiagnostics { dedxDiff = o2::aod::pidutils::tpcExpSignalDiff(track); dedxExpected = track.tpcSignal() - dedxDiff; expSigma = o2::aod::pidutils::tpcExpSigma(track); + + isGoodTrack &= (expSigma <= *cutExpSigmaMax.at(ParticleId)); + isGoodTrack &= (dedxExpected >= *cutDeDxExpectedMin.at(ParticleId)); + isGoodTrack &= (dedxExpected <= *cutDeDxExpectedMax.at(ParticleId)); + isGoodTrack &= (dedxDiff >= *cutDeDxDiffMin.at(ParticleId)); + isGoodTrack &= (dedxDiff <= *cutDeDxDiffMax.at(ParticleId)); + } + + if (!isGoodTrack) { + continue; } float nSigmaTof{UndefValueFloat}; @@ -265,7 +291,7 @@ struct TreeCreatorPidTpcDiagnostics { } } -#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong) \ +#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong, Unused) \ void process##ParticleNameLong(CollisionsExtra const& collisions, \ soa::Join const& tracks, \ aod::BCsWithTimestamps const&) \ @@ -290,12 +316,12 @@ struct TreeCreatorPidTpcDiagnostics { } \ PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFullWithTOF##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA with the TOF info added", #ParticleNameLong), false); - DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS) + DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS, _) #undef MAKE_PROCESS_FUNCTIONS }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } #undef DO_FOR_ALL_PARTICLES From fff9af329d5c4d7fabd6354143cc4370f3ee8db7 Mon Sep 17 00:00:00 2001 From: Preet-Bhanjan Date: Mon, 12 Oct 2026 11:25:38 +0200 Subject: [PATCH 259/648] [PWGCF] Fixed the issue of histograms not being found (#18240) Co-authored-by: Preet Pati --- .../Tasks/pidLongRange.cxx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx index 8e7f07b69ab..90d7de1838e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -488,8 +488,6 @@ struct PidLongRange { histos.add("h_centFT0M_centFT0C", "after cut;Centrality T0C;Centrality T0M", {HistType::kTH2D, {axisCent, axisCent}}); histos.add("h_centFV0A_centFT0C", "after cut;Centrality T0C;Centrality V0A", {HistType::kTH2D, {axisCent, axisCent}}); - histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); - // Add track cuts table histos.add("hDCAz_before", "DCAz before cuts; DCAz (cm); Pt", {HistType::kTH2D, {axisDCAz, axisPtFiner}}); histos.add("hDCAxy_before", "DCAxy before cuts; DCAxy (cm); Pt", {HistType::kTH2D, {axisDCAxy, axisPtFiner}}); @@ -514,6 +512,7 @@ struct PidLongRange { histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); } if (doprocessSameTpcFt0 || doprocessQA) { @@ -567,6 +566,7 @@ struct PidLongRange { histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event } if (doprocessSameFt0aFt0c) { @@ -581,9 +581,6 @@ struct PidLongRange { histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); - } - - if (!doprocessQA) { histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event } @@ -1159,7 +1156,7 @@ struct PidLongRange { } template - void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + void nchCounter(const TTracks& tracks, double& multiplicity, bool fillHistogram) // function to count the number of tracks in the event and fill the histogram { double nTracksCorrected = 0; double nTracksUncorrected = 0; @@ -1176,7 +1173,9 @@ struct PidLongRange { nTracksUncorrected += 1.0; nTracksCorrected += weightNch; } - histos.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); + if (fillHistogram) { + histos.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); + } multiplicity = nTracksCorrected; } @@ -1509,7 +1508,7 @@ struct PidLongRange { } if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks, multiplicity); + nchCounter(tracks, multiplicity, false); } if (cfgGeneral.cfgQABasic) { @@ -1591,7 +1590,7 @@ struct PidLongRange { auto multiplicity = static_cast(tracks1.size()); if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks1, multiplicity); + nchCounter(tracks1, multiplicity, false); } if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { @@ -1675,7 +1674,7 @@ struct PidLongRange { } if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks, multiplicity); + nchCounter(tracks, multiplicity, false); } if (cfgGeneral.cfgQABasic) { @@ -1753,7 +1752,7 @@ struct PidLongRange { auto multiplicity = static_cast(tracks1.size()); if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks1, multiplicity); + nchCounter(tracks1, multiplicity, false); } if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { @@ -1844,7 +1843,7 @@ struct PidLongRange { histos.fill(HIST("Nch"), multiplicity); if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks, multiplicity); + nchCounter(tracks, multiplicity, true); } histos.fill(HIST("Nch_corrected"), multiplicity); From 042aac103e3fa28f2927ab8db2d1ed9018d09883 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:43:44 +0800 Subject: [PATCH 260/648] [PWGLF] Add an additional systematic uncertainty table (#18253) --- PWGLF/DataModel/LFSpincorrelationTables.h | 16 +- .../Strangeness/lambdaspincorrelation.cxx | 43 +- .../Strangeness/lambdaspincorrderived.cxx | 774 +----------------- 3 files changed, 76 insertions(+), 757 deletions(-) diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index d4fdffd2442..fa7e2061f3e 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -53,6 +53,12 @@ DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Ph DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex +DECLARE_SOA_COLUMN(TpcPosNClsCrossedRows, tpcPosNClsCrossedRows, float); +DECLARE_SOA_COLUMN(TpcNegNClsCrossedRows, tpcNegNClsCrossedRows, float); +DECLARE_SOA_COLUMN(Ctau, ctau, float); +DECLARE_SOA_COLUMN(TpcNsigmaPos, tpcNsigmaPos, float); +DECLARE_SOA_COLUMN(TpcNsigmaNeg, tpcNsigmaNeg, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", @@ -74,7 +80,13 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonPhi, lambdapair::ProtonIndex, lambdapair::PionIndex, - lambdapair::DcaV0ToPV); + lambdapair::DcaV0ToPV, + lambdapair::TpcPosNClsCrossedRows, + lambdapair::TpcNegNClsCrossedRows, + lambdapair::Ctau, + lambdapair::TpcNsigmaPos, + lambdapair::TpcNsigmaNeg, + lambdapair::MK0Short); using LambdaPair = LambdaPairs::iterator; @@ -109,6 +121,8 @@ DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proto DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex + +//! TPC n sigma } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index d9a3d8cca27..615c4be269d 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -95,21 +95,14 @@ struct lambdaspincorrelation { Configurable cMaxV0DCA{"cMaxV0DCA", 1.2, "Maximum V0 DCA to PV"}; Configurable cMinV0DCAPr{"cMinV0DCAPr", 0.05, "Minimum V0 daughters DCA to PV for Pr"}; Configurable cMinV0DCAPi{"cMinV0DCAPi", 0.05, "Minimum V0 daughters DCA to PV for Pi"}; + Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 50, "Maximum V0 life time"}; // config for V0 daughters Configurable confDaughEta{"confDaughEta", 0.8f, "V0 Daugh sel: max eta"}; Configurable cfgDaughPrPt{"cfgDaughPrPt", 0.2, "minimum daughter proton pt"}; Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2, "minimum daughter pion pt"}; Configurable confDaughTPCnclsMin{"confDaughTPCnclsMin", 50.f, "V0 Daugh sel: Min. nCls TPC"}; - - struct : ConfigurableGroup { - std::string prefix = "v0Configuration"; - Configurable cfgDaughTPCCrossedRows{"cfgDaughTPCCrossedRows", 70, "Min. TPC crossed rows for V0 daughters"}; - - Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 30, "Maximum V0 life time"}; - Configurable confDaughPIDCuts{"confDaughPIDCuts", 4, "PID selections for Lambda daughters"}; - - } v0Configurations; + Configurable confDaughPIDCuts{"confDaughPIDCuts", 3, "PID selections for Lambda daughters"}; Configurable iMNbins{"iMNbins", 50, "Number of bins in invariant mass"}; Configurable lbinIM{"lbinIM", 1.09, "lower bin value in IM histograms"}; @@ -156,10 +149,9 @@ struct lambdaspincorrelation { if (tranRad > confV0TranRadV0Max) { return false; } - if (std::abs(ctauLambda) > v0Configurations.cMaxV0LifeTime) { + if (std::abs(ctauLambda) > cMaxV0LifeTime) { return false; } - // if (std::abs(candidate.yLambda()) > confV0Rap) { // return false; // } @@ -170,9 +162,10 @@ struct lambdaspincorrelation { bool isSelectedV0Daughter(V0 const& candidate, T const& track, int pid) { const auto tpcNClsF = track.tpcNClsFound(); + const auto ncr = 70; const auto ncrfc = 0.8; - if (track.tpcNClsCrossedRows() < v0Configurations.cfgDaughTPCCrossedRows) { + if (track.tpcNClsCrossedRows() < ncr) { return false; } if (tpcNClsF < confDaughTPCnclsMin) { @@ -182,10 +175,10 @@ struct lambdaspincorrelation { return false; } - if (pid == 0 && std::abs(track.tpcNSigmaPr()) > v0Configurations.confDaughPIDCuts) { + if (pid == 0 && std::abs(track.tpcNSigmaPr()) > confDaughPIDCuts) { return false; } - if (pid == 1 && std::abs(track.tpcNSigmaPi()) > v0Configurations.confDaughPIDCuts) { + if (pid == 1 && std::abs(track.tpcNSigmaPi()) > confDaughPIDCuts) { return false; } if (pid == 0 && (candidate.positivept() < cfgDaughPrPt || candidate.negativept() < cfgDaughPiPt)) { @@ -306,6 +299,13 @@ struct lambdaspincorrelation { std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; std::vector dcaV0ToPV = {}; + std::vector tpcPosNClsCrossedRows = {}; + std::vector tpcNegNClsCrossedRows = {}; + std::vector ctau = {}; + std::vector tpcNsigmaPos = {}; + std::vector tpcNsigmaNeg = {}; + std::vector mK0Short = {}; + int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -357,6 +357,9 @@ struct lambdaspincorrelation { if (aLambdaTag) { histos.fill(HIST("hV0Info"), 3.5); } + + float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassLambda); + // LOGF(info, "v0 index2: (%d)", v0.index()); auto postrack1 = v0.template posTrack_as(); auto negtrack1 = v0.template negTrack_as(); @@ -364,6 +367,16 @@ struct lambdaspincorrelation { negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); + + tpcPosNClsCrossedRows.push_back(postrack1.tpcNClsCrossedRows()); + tpcNegNClsCrossedRows.push_back(negtrack1.tpcNClsCrossedRows()); + ctau.push_back(ctauLambda); + + tpcNsigmaPos.push_back(std::abs(postrack1.tpcNSigmaPr())); + tpcNsigmaNeg.push_back(std::abs(negtrack1.tpcNSigmaPi())); + + mK0Short.push_back(v0.mK0Short()); + v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -400,7 +413,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5), tpcPosNClsCrossedRows.at(i5), tpcNegNClsCrossedRows.at(i5), ctau.at(i5), tpcNsigmaPos.at(i5), tpcNsigmaNeg.at(i5), mK0Short.at(i5)); } } } diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 98167f6fe0d..e150c3644cc 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -70,9 +70,8 @@ static double getK0sCompetingMass(T const& v0) v0.protonPt(), v0.protonEta(), v0.protonPhi(), o2::constants::physics::MassProton); - const auto negDaugh = lambda - posDaugh; // 反推 neg + const auto negDaugh = lambda - posDaugh; - // const ROOT::Math::PtEtaPhiMVector pi1( posDaugh.Pt(), posDaugh.Eta(), posDaugh.Phi(), o2::constants::physics::MassPionCharged); @@ -416,6 +415,15 @@ struct lambdaspincorrderived { Configurable useK0sRejection{"useK0sRejection", true, "Apply K0s competing mass rejection"}; Configurable confK0sMassWindow{"confK0sMassWindow", 0.01f, "K0s competing mass rejection window (GeV/c^2)"}; + Configurable useAdditionalCut{"useAdditionalCut", false, "Apply additional cut"}; + + // tpcnclustercorssedrows () + Configurable minTpcNClsCrossedRows{"minTpcNClsCrossedRows", 70.f, "Minimum TPC crossed rows for daughter"}; + // ctau (lifetime) + Configurable maxCtau{"maxCtau", 30.f, "Maximum ctau of Lambda candidate (cm)"}; + // TPC nsigma + Configurable maxTpcNsigma{"maxTpcNsigma", 4.f, "Maximum |TPC nsigma| for Lambda daughters"}; + } v0Configurations; // Event Mixing @@ -931,6 +939,28 @@ struct lambdaspincorrderived { } } + if (v0Configurations.useAdditionalCut) { + // additional cuts + if (candidate.v0Status() == 0 && (std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma)) { + return false; + } + if (candidate.v0Status() == 1 && (std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma)) { + return false; + } + + if (candidate.tpcPosNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.tpcNegNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.ctau() > v0Configurations.maxCtau) { + return false; + } + } + return true; } @@ -1822,744 +1852,6 @@ struct lambdaspincorrderived { } PROCESS_SWITCH(lambdaspincorrderived, processData, "Process data", true); - void processDataShareDaughter(EventCandidates::iterator const& collision, AllTrackCandidates const& V0s) - { - auto centrality = collision.cent(); - - // ------------------------------------------------------------ - // Candidate-level shared-daughter rejection - // - // First collect all candidates in this event that pass selectionV0, - // then flag every candidate that shares a daughter track with any - // other selected candidate. Only candidates that are not flagged - // are kept for the subsequent SE pairing. - // - // This corresponds to the lambdaSharingDauFlag + trueLambdaFlag - // approach used in Subhadeep Roy's analysis: a candidate that - // shares a daughter track is removed entirely, rather than only - // vetoing the specific pair that shares the track. - // - // Implementation note: we store index() (not globalIndex()) because - // V0s.iteratorAt() expects the row index within the current table. - // ------------------------------------------------------------ - std::vector selected; - selected.reserve(V0s.size()); - - for (auto const& v0 : V0s) { - if (selectionV0(v0)) { - selected.push_back(v0.index()); - } - } - - std::vector isDirty(selected.size(), false); - for (size_t i = 0; i < selected.size(); ++i) { - for (size_t j = i + 1; j < selected.size(); ++j) { - auto a = V0s.iteratorAt(static_cast(selected[i])); - auto b = V0s.iteratorAt(static_cast(selected[j])); - if (hasSharedDaughters(a, b)) { - isDirty[i] = true; - isDirty[j] = true; - } - } - } - - std::vector clean; - clean.reserve(selected.size()); - for (size_t i = 0; i < selected.size(); ++i) { - if (!isDirty[i]) { - clean.push_back(selected[i]); - } - } - - // ------------------------------------------------------------ - // SE pairing on clean candidates only - // ------------------------------------------------------------ - for (size_t i = 0; i < clean.size(); ++i) { - const auto& v0 = V0s.iteratorAt(static_cast(clean[i])); - - if (fillHistConfig.fillBasicQAHistos) { - histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); - } - if (fillHistConfig.fillBasicQAHistos) { - histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); - } - if (fillHistConfig.fillBasicQAHistos) { - histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); - } - - proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), - o2::constants::physics::MassProton); - lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), - v0.lambdaMass()); - - const double phi = RecoDecay::constrainAngle(v0.lambdaPhi(), 0.0F, harmonic); - const double eta = v0.lambdaEta(); - - if (v0.v0Status() == 0) { - histos.fill(HIST("hEtaPhiLambdaRaw"), phi, eta, - getNUAWeight(0, v0.lambdaPhi(), v0.lambdaEta())); - } else { - histos.fill(HIST("hEtaPhiAntiLambdaRaw"), phi, eta, - getNUAWeight(1, v0.lambdaPhi(), v0.lambdaEta())); - } - - for (size_t j = i + 1; j < clean.size(); ++j) { - const auto& v02 = V0s.iteratorAt(static_cast(clean[j])); - - // Note: hasSharedDaughters(v0, v02) is no longer needed here. - // All clean candidates are already guaranteed not to share a - // daughter track with any other selected candidate. - - proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), - o2::constants::physics::MassProton); - lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), - v02.lambdaMass()); - - if ((v0.v0Status() == 0 && v02.v0Status() == 1) || - (v0.v0Status() == 1 && v02.v0Status() == 0)) { - if (fillHistConfig.fillBasicQAHistos) { - histos.fill(HIST("deltaPhiSame"), - RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), - -TMath::Pi(), harmonicDphi)); - } - } - - // const int ptype = pairTypeCode(v0.v0Status(), v02.v0Status()); - if (v0.v0Status() == 0 && v02.v0Status() == 0) { - fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0); - } - if (v0.v0Status() == 0 && v02.v0Status() == 1) { - fillHistograms(0, 1, lambda, lambda2, proton, proton2, 0, 1.0); - } - if (v0.v0Status() == 1 && v02.v0Status() == 0) { - fillHistograms(0, 1, lambda2, lambda, proton2, proton, 0, 1.0); - } - if (v0.v0Status() == 1 && v02.v0Status() == 1) { - fillHistograms(1, 1, lambda, lambda2, proton, proton2, 0, 1.0); - } - } - } - } - PROCESS_SWITCH(lambdaspincorrderived, processDataShareDaughter, "Process data share daughter", true); - - void processMEV6ShareDaughter(EventCandidates const& collisions, AllTrackCandidates const& V0s) - { - MixBinnerR mb{ - ptMin.value, - ptMax.value, - ptMix.value, - v0Configurations.v0etaMixBuffer.value, - etaMix.value, - phiMix.value, - MassMin.value, - MassMax.value, - cfgV5MassBins.value, - cfgMixRadiusParam.cfgMixRadiusBins.value}; - - const int nCol = colBinning.getAllBinsCount(); - const int nStat = N_STATUS; - const int nPt = mb.nPt(); - const int nEta = mb.nEta(); - const int nPhi = mb.nPhi(); - const int nM = mb.nM(); - const int nR = mb.nR(); - - const size_t nKeys = static_cast(nCol) * nStat * nPt * nEta * nPhi * nM * nR; - std::vector> buffer(nKeys); - - // -------- PASS 1: fill buffer -------- - for (auto const& col : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col.posz(), col.cent())); - if (colBin < 0) { - continue; - } - - auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); - - // ------------------------------------------------------------ - // Candidate-level shared-daughter rejection (PASS 1) - // - // Only clean candidates are inserted into the mixing buffer. - // A candidate is dirty if it shares a daughter track with any - // other selected candidate in the same event. - // - // Implementation note: we store index() (not globalIndex()) because - // slice.iteratorAt() expects the row index within the current table. - // ------------------------------------------------------------ - std::vector selected; - selected.reserve(slice.size()); - for (auto const& t : slice) { - if (selectionV0(t)) { - selected.push_back(t.index()); - } - } - - std::vector isDirty(selected.size(), false); - for (size_t i = 0; i < selected.size(); ++i) { - for (size_t j = i + 1; j < selected.size(); ++j) { - auto a = slice.iteratorAt(static_cast(selected[i])); - auto b = slice.iteratorAt(static_cast(selected[j])); - if (hasSharedDaughters(a, b)) { - isDirty[i] = true; - isDirty[j] = true; - } - } - } - - for (size_t i = 0; i < selected.size(); ++i) { - if (isDirty[i]) { - continue; - } - const auto& t = slice.iteratorAt(static_cast(selected[i])); - - const int status = static_cast(t.v0Status()); - if (status < 0 || status >= nStat) { - continue; - } - - const int ptB = mb.ptBin(t.lambdaPt()); - - int etaB = mb.etaBin(t.lambdaEta()); - if (userapidity) { - const auto lv = ROOT::Math::PtEtaPhiMVector(t.lambdaPt(), t.lambdaEta(), t.lambdaPhi(), t.lambdaMass()); - etaB = mb.etaBin(lv.Rapidity()); - } - - const int phiB = mb.phiBin(RecoDecay::constrainAngle(t.lambdaPhi(), 0.0, harmonic)); - const int mB = getMassMixClassFromEdges(t.lambdaMass(), massMixEdges.value); - const int rB = mb.radiusBin(t.v0Radius()); - - if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { - continue; - } - - const size_t key = linearKeyR(colBin, status, ptB, etaB, phiB, mB, rB, - nStat, nPt, nEta, nPhi, nM, nR); - - buffer[key].push_back(BufferCandR{ - .collisionIdx = static_cast(col.index()), - .rowIndex = static_cast(t.globalIndex()), - .v0Status = static_cast(status), - .ptBin = static_cast(ptB), - .etaBin = static_cast(etaB), - .phiBin = static_cast(phiB), - .mBin = static_cast(mB), - .rBin = static_cast(rB)}); - } - } - - const int nN_pt = std::max(0, cfgV5NeighborPt.value); - const int nN_eta = std::max(0, cfgV5NeighborEta.value); - const int nN_phi = std::max(0, cfgV5NeighborPhi.value); - - std::vector ptBins, etaBins, phiBins; - std::vector matches1, matches2; - matches1.reserve(256); - matches2.reserve(256); - - auto collectMatchesForReplacedLeg = [&](auto const& tRep, auto const& tKeep, int colBin, int64_t curColIdx, std::vector& matches) { - matches.clear(); - - const int status = static_cast(tRep.v0Status()); - if (status < 0 || status >= nStat) { - return; - } - - const int ptB = mb.ptBin(tRep.lambdaPt()); - - int etaB = mb.etaBin(tRep.lambdaEta()); - if (userapidity) { - const auto lv = ROOT::Math::PtEtaPhiMVector(tRep.lambdaPt(), tRep.lambdaEta(), tRep.lambdaPhi(), tRep.lambdaMass()); - etaB = mb.etaBin(lv.Rapidity()); - } - - const int phiB = mb.phiBin(RecoDecay::constrainAngle(tRep.lambdaPhi(), 0.0, harmonic)); - const int mB = getMassMixClassFromEdges(tRep.lambdaMass(), massMixEdges.value); - const int rB = mb.radiusBin(tRep.v0Radius()); - - if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { - return; - } - auto collectFromBins = [&](const std::vector& ptUseBins, - const std::vector& etaUseBins, - const std::vector& phiUseBins) { - for (const auto& ptUse : ptUseBins) { - for (const auto& etaUse : etaUseBins) { - for (const auto& phiUse : phiUseBins) { - const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, - nStat, nPt, nEta, nPhi, nM, nR)]; - - for (auto const& bc : vec) { - if (bc.collisionIdx == curColIdx) - continue; - - auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); - - if (!selectionV0(tX)) - continue; - if (!checkKinematics(tRep, tX)) - continue; - - if (tX.globalIndex() == tRep.globalIndex()) - continue; - if (tX.globalIndex() == tKeep.globalIndex()) - continue; - - if (hasSharedDaughters(tX, tKeep)) - continue; - if (hasSharedDaughters(tX, tRep)) - continue; - - matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); - } - } - } - } - }; - - matches.clear(); - - // 1) exact bin first - ptBins.clear(); - etaBins.clear(); - phiBins.clear(); - - ptBins.push_back(ptB); - etaBins.push_back(etaB); - phiBins.push_back(phiB); - - collectFromBins(ptBins, etaBins, phiBins); - - // 2) if exact bin gives fewer than required matches, also search neighbors - const int targetMatches = (cfgV5MaxMatches.value > 0) ? cfgV5MaxMatches.value : 1; - - if ((int)matches.size() < targetMatches) { - std::vector ptBinsN, etaBinsN, phiBinsN; - collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBinsN); - collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBinsN); - collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBinsN); - - for (const auto& ptUse : ptBinsN) { - for (const auto& etaUse : etaBinsN) { - for (const auto& phiUse : phiBinsN) { - if (ptUse == ptB && etaUse == etaB && phiUse == phiB) - continue; - - const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, - nStat, nPt, nEta, nPhi, nM, nR)]; - - for (auto const& bc : vec) { - if (bc.collisionIdx == curColIdx) - continue; - - auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); - - if (!selectionV0(tX)) - continue; - if (!checkKinematics(tRep, tX)) - continue; - - if (tX.globalIndex() == tRep.globalIndex()) - continue; - if (tX.globalIndex() == tKeep.globalIndex()) - continue; - - if (hasSharedDaughters(tX, tKeep)) - continue; - if (hasSharedDaughters(tX, tRep)) - continue; - - matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); - } - } - } - } - } - - std::sort(matches.begin(), matches.end(), - [](auto const& a, auto const& b) { - return std::tie(a.collisionIdx, a.rowIndex) < std::tie(b.collisionIdx, b.rowIndex); - }); - matches.erase(std::unique(matches.begin(), matches.end(), - [](auto const& a, auto const& b) { - return a.collisionIdx == b.collisionIdx && a.rowIndex == b.rowIndex; - }), - matches.end()); - }; - - auto downsampleMatches = [&](std::vector& matches, uint64_t seedBase) { - if (cfgV5MaxMatches.value > 0 && (int)matches.size() > cfgV5MaxMatches.value) { - uint64_t seed = cfgMixSeed.value ^ splitmix64(seedBase); - const int K = cfgV5MaxMatches.value; - for (int i = 0; i < K; ++i) { - seed = splitmix64(seed); - const int j = i + (int)(seed % (uint64_t)(matches.size() - i)); - std::swap(matches[i], matches[j]); - } - matches.resize(K); - } - }; - - const size_t pendingAtStart = v6Pending.data.size(); - size_t pendingMatched = 0; - size_t pendingExpired = 0; - size_t pendingAdded = 0; - if (!cfgV6CarryUnmatched) { - v6Pending.data.clear(); - } else { - for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { - auto& pending = *it; - ++pending.age; - if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { - ++pendingExpired; - it = v6Pending.data.erase(it); - continue; - } - - auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; - collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); - limitMatchesToNEvents(matches, nEvtMixing.value); - downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); - - int nAccepted = 0; - for (auto const& m : matches) { - auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { - continue; - } - if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { - continue; - } - if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { - continue; - } - ++nAccepted; - } - - if (nAccepted == 0) { - ++it; - continue; - } - - const float controlWeight = 1.0f / static_cast(nAccepted); - const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; - const float mixWeight = branchNorm * controlWeight; - for (auto const& m : matches) { - auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { - continue; - } - if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { - continue; - } - if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { - continue; - } - fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); - } - ++pendingMatched; - it = v6Pending.data.erase(it); - } - } - - // -------- PASS 2: configurable one-leg / two-leg mixing -------- - for (auto const& col1 : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); - if (colBin < 0) { - continue; - } - - const int64_t curColIdx = static_cast(col1.index()); - auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); - - // ------------------------------------------------------------ - // Candidate-level shared-daughter rejection (PASS 2) - // - // Build the clean set for this event so that SE pairing only - // uses clean candidates. The mixing buffer already contains - // only clean candidates, so matched replacements are clean. - // - // Implementation note: we store index() (not globalIndex()) because - // poolA.iteratorAt() expects the row index within the current table. - // ------------------------------------------------------------ - std::vector selectedA; - selectedA.reserve(poolA.size()); - for (auto const& t : poolA) { - if (selectionV0(t)) { - selectedA.push_back(t.index()); - } - } - - std::vector dirtyA(selectedA.size(), false); - for (size_t i = 0; i < selectedA.size(); ++i) { - for (size_t j = i + 1; j < selectedA.size(); ++j) { - auto a = poolA.iteratorAt(static_cast(selectedA[i])); - auto b = poolA.iteratorAt(static_cast(selectedA[j])); - if (hasSharedDaughters(a, b)) { - dirtyA[i] = true; - dirtyA[j] = true; - } - } - } - - // Build a set of clean row indices for fast lookup in the pairing loop. - // We use index() here, matching what soa::combinations yields via t.index(). - std::unordered_set cleanIdxSet; - cleanIdxSet.reserve(selectedA.size()); - for (size_t i = 0; i < selectedA.size(); ++i) { - if (!dirtyA[i]) { - cleanIdxSet.insert(selectedA[i]); - } - } - - for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { - if (!selectionV0(t1) || !selectionV0(t2)) { - continue; - } - if (!cleanIdxSet.count(t1.index()) || !cleanIdxSet.count(t2.index())) { - continue; - } - if (t2.index() <= t1.index()) { - continue; - } - - // Note: hasSharedDaughters(t1, t2) is no longer needed here. - // Both t1 and t2 are guaranteed to be clean candidates. - - const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); - const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); - - // Fill TGT maps before searching for replacements. This makes TGT the - // true same-event target phase space for the selected pair, matching the - // hPtYSame definition. REP below is filled only for accepted replacements. - if (doMixLeg1) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - } - if (doMixLeg2) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - } - - if (doMixLeg1) { - collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); - limitMatchesToNEvents(matches1, nEvtMixing.value); - downsampleMatches(matches1, (uint64_t)t1.globalIndex() ^ (splitmix64((uint64_t)t2.globalIndex()) + 0x111ULL) ^ splitmix64((uint64_t)curColIdx)); - } else { - matches1.clear(); - } - - if (doMixLeg2) { - collectMatchesForReplacedLeg(t2, t1, colBin, curColIdx, matches2); - limitMatchesToNEvents(matches2, nEvtMixing.value); - downsampleMatches(matches2, (uint64_t)t2.globalIndex() ^ (splitmix64((uint64_t)t1.globalIndex()) + 0x222ULL) ^ splitmix64((uint64_t)curColIdx)); - } else { - matches2.clear(); - } - - // Do not fill TGT here. TGT has already been filled above as the - // selected same-event target phase space, independent of replacement success. - int nFill1 = 0; - int nFill2 = 0; - // count actual accepted fills for leg-1 replacement - if (doMixLeg1) { - for (auto const& m : matches1) { - auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tX)) - continue; - if (tX.v0Status() != t1.v0Status()) - continue; - if (!checkKinematics(t1, tX)) - continue; - if (tX.globalIndex() == t1.globalIndex()) - continue; - if (tX.globalIndex() == t2.globalIndex()) - continue; - if (hasSharedDaughters(tX, t2)) - continue; - if (hasSharedDaughters(tX, t1)) - continue; - ++nFill1; - } - } - - // count actual accepted fills for leg-2 replacement - if (doMixLeg2) { - for (auto const& m : matches2) { - auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tY)) - continue; - if (tY.v0Status() != t2.v0Status()) - continue; - if (!checkKinematics(t2, tY)) - continue; - if (tY.globalIndex() == t2.globalIndex()) - continue; - if (tY.globalIndex() == t1.globalIndex()) - continue; - if (hasSharedDaughters(tY, t2)) - continue; - if (hasSharedDaughters(tY, t1)) - continue; - ++nFill2; - } - } - - if (cfgV6CarryUnmatched) { - const auto hasPendingSpace = [&]() { - return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; - }; - if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { - v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, - static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); - ++pendingAdded; - } - if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { - v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, - static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); - ++pendingAdded; - } - } - histos.fill(HIST("hNMEperSE_leg2"), nFill2); - histos.fill(HIST("hNMEperSE_leg1"), nFill1); - - if (nFill1 <= 0 && nFill2 <= 0) { - continue; - } - // Residual-weight QA needs a leg-specific normalization: - // TGT_leg is filled once per same-event target candidate with weight 1. - // REP_leg is the average replacement distribution for that same target. - const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; - const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; - - const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + - ((doMixLeg2 && nFill2 > 0) ? 1 : 0); - float branchNorm = 1.0f; - if (cfgMixLegMode.value == 2) { - branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); - } - const float finalMixWeightLeg1 = branchNorm * wSELeg1; - const float finalMixWeightLeg2 = branchNorm * wSELeg2; - - // Do not fill target maps here: TGT has already been filled above, - // before searching for replacements, so that TGT represents all selected - // same-event targets rather than only targets with successful replacements. - - if (doMixLeg1 && nFill1 > 0) { - for (auto const& m : matches1) { - auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tX)) { - continue; - } - if (tX.v0Status() != t1.v0Status()) - continue; - if (!checkKinematics(t1, tX)) - continue; - if (tX.globalIndex() == t1.globalIndex()) - continue; - if (tX.globalIndex() == t2.globalIndex()) - continue; - if (hasSharedDaughters(tX, t1)) - continue; - if (hasSharedDaughters(tX, t2)) - continue; - fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, - ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), - wSELeg1); - // Fixed leg for leg1 replacement is original t2. - // Fill only for successful replacement. - fillFixedLegControlMap(tX.v0Status(), t2.v0Status(), 1, false, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - wSELeg1); - auto proton = ROOT::Math::PtEtaPhiMVector(tX.protonPt(), tX.protonEta(), tX.protonPhi(), o2::constants::physics::MassProton); - auto lambda = ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()); - auto proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton); - auto lambda2 = ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()); - - // const int ptype = pairTypeCode(tX.v0Status(), t2.v0Status()); - - const float meWeight = finalMixWeightLeg1; - const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) - if (fillHistConfig.fillBasicQAHistos) - histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); - const int s1 = tX.v0Status(); - const int s2 = t2.v0Status(); - - if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); - } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 2, 1); - } else { - fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); - } - } - } - - if (doMixLeg2 && nFill2 > 0) { - for (auto const& m : matches2) { - auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tY)) { - continue; - } - if (tY.v0Status() != t2.v0Status()) - continue; - if (!checkKinematics(t2, tY)) - continue; - if (tY.globalIndex() == t2.globalIndex()) - continue; - if (tY.globalIndex() == t1.globalIndex()) - continue; - if (hasSharedDaughters(tY, t1)) - continue; - if (hasSharedDaughters(tY, t2)) - continue; - fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, - ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), - wSELeg2); - // Fixed leg for leg2 replacement is original t1. - // Fill only for successful replacement. - fillFixedLegControlMap(t1.v0Status(), tY.v0Status(), 2, false, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - wSELeg2); - auto proton = ROOT::Math::PtEtaPhiMVector(t1.protonPt(), t1.protonEta(), t1.protonPhi(), o2::constants::physics::MassProton); - auto lambda = ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()); - auto proton2 = ROOT::Math::PtEtaPhiMVector(tY.protonPt(), tY.protonEta(), tY.protonPhi(), o2::constants::physics::MassProton); - auto lambda2 = ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()); - - // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); - const float meWeight = finalMixWeightLeg2; - const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - if (fillHistConfig.fillBasicQAHistos) - histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); - const int s1 = t1.v0Status(); - const int s2 = tY.v0Status(); - if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); - } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 1, 2); - } else { - fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); - } - } - } - } - } - if (cfgV6LogPending) { - LOGF(info, "MEV6 data pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", - pendingAtStart, pendingMatched, pendingExpired, pendingAdded, v6Pending.data.size()); - } - } - PROCESS_SWITCH(lambdaspincorrderived, processMEV6ShareDaughter, "Process data ME v6 with radius buffer share daughter", true); - void processDataSys(EventCandidates::iterator const&, AllTrackCandidates const& V0s) { for (const auto& v0 : V0s) { @@ -2762,7 +2054,7 @@ struct lambdaspincorrderived { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; Preslice tracksPerCollisionV0 = aod::lambdapair::lambdaeventId; void processMEV3(EventCandidates const& collisions, AllTrackCandidates const& V0s) From 07582ee0246e49e2351cf08ec884f5db30d04988 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Mon, 12 Oct 2026 13:57:46 +0300 Subject: [PATCH 261/648] [PWGCF] TwoParticleCorrelationsMultSpher (#18137) Co-authored-by: Claude Sonnet 5 --- .../Tasks/CMakeLists.txt | 5 + .../twoParticleCorrelationsMultSpher.cxx | 1262 +++++++++++++++++ 2 files changed, 1267 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index 4261d7186a5..b6fc8b9eaa5 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -143,6 +143,11 @@ o2physics_add_dpl_workflow(fox-wolfram-correlation PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(two-particle-correlations-mult-spher + SOURCES twoParticleCorrelationsMultSpher.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(hparticle-correlation SOURCES hParticleCorrelation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx new file mode 100644 index 00000000000..8284aa29488 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -0,0 +1,1262 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoParticleCorrelationsMultSpher.cxx +/// \brief Two-particle angular correlations in pp collisions at 13 TeV. +/// Structured in parallel with the AliRoot AliMESppColTask analysis. +/// MC truth level: McCollision + McParticles. +/// Includes: multiplicity classes, transverse sphericity, PID (MC: PDG code), +/// event mixing with 4 separate pools per sphericity class, +/// THnSparse for multi-dimensional analysis + TH2D SE/ME for +/// direct normalisation (both per-trigger and per-pair). +/// +/// Pool structure (separate for MC generated and Reco reconstructed data): +/// eventPoolsMC[sphClass][zvtxBin][multBin] +/// eventPoolsReco[sphClass][zvtxBin][multBin] +/// sphClass 0 : S_T in (0.0, 0.3] — jetty +/// sphClass 1 : S_T in (0.3, 0.6] — intermediate +/// sphClass 2 : S_T in (0.6, 1.0] — isotropic +/// sphClass 3 : S_T > 0.0 — all events (no sphericity cut) +/// +/// \author Madalina Tarzila + +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// ============================================================ +// PID species +// ============================================================ +enum PidSpecies { PidUnidentified = 0, + PidPion, + PidKaon, + PidProton, + NPidSpecies }; +static constexpr std::array PidNames{"unid", "pion", "kaon", "proton"}; + +// ============================================================ +// Sphericity classes +// ============================================================ +static constexpr int NSphClasses = 4; +static constexpr std::array SphMax{0.3, 0.6, 1.0, 1.0}; +static constexpr std::array SphLabels{"jetty", "intermediate", "isotropic", "all"}; + +// Half a bin: puts integer-valued axes (indices, cut-flow steps) at bin centres. +static constexpr double HalfBin = 0.5; + +// ============================================================ +// Cut-flow steps (bin numbers of the evSel_* and track cut-flow histograms) +// ============================================================ +enum EventCut { EventCutRead = 1, + EventCutZvtx, + EventCutMultBin, + EventCutZvtxBin, + EventCutLeadPt, + EventCutSphericity, + EventCutSphClass, + EventCutSeFilled, + NEventCuts = EventCutSeFilled }; +enum TrackCut { TrackCutSeen = 1, + TrackCutGlobal, + TrackCutDcaXY, + TrackCutDcaZ, + TrackCutEta, + TrackCutPt, + NTrackCuts = TrackCutPt }; + +// ============================================================ +struct TwoParticleCorrelationsMultSpher { + + // ---------------------------------------------------------------- + // Configurables + // ---------------------------------------------------------------- + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; + Configurable nBinsPt2{"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; + Configurable mixPoolSize{"mixPoolSize", 200, "Max events per mixing pool cell"}; + Configurable minAssocPt{"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; + Configurable minLeadPt{"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; + Configurable maxLeadPt{"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; + Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; + Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; + Configurable maxEta{"maxEta", 0.8, "Max |eta| for track selection"}; + Configurable maxZvtx{"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; + // |y| cut applied ONLY to PID-identified tracks (pion/kaon/proton) -- + // independent of maxEta, which applies to all-species tracks (hSE/hME). + // Rapidity is well-defined only for massive/identified particles; at + // pT=0.5 GeV/c, proton y_max~0.48, so |y|<0.5 keeps full acceptance for + // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- + // "tracks reconstructed within |eta|<0.8, with the additional rapidity + // cut of |y|<0.5". + Configurable maxRapPID{"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; + + // Reco-specific configurables + Configurable maxDcaXY{"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; + Configurable maxDcaZ{"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; + // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two + // different thresholds -- selection at 2 sigma, ambiguity rejection + // at 3 sigma. + Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> PidUnidentified)"}; + + // Thresholds of the legacy PID gating (independent 3-sigma cuts on TPC + // and TOF, no combined quadrature, no pT-dependent TOF gating), kept so + // that it can be switched on with useLegacyNSigmaGating without removing + // the ALICE 2511.10399 criterion. + Configurable maxNSigmaTPC{"maxNSigmaTPC", 3.0, "[legacy] Max |N_sigma| TPC for PID, used only when useLegacyNSigmaGating=true"}; + Configurable maxNSigmaTOF{"maxNSigmaTOF", 3.0, "[legacy] Max |N_sigma| TOF for PID, used only when useLegacyNSigmaGating=true"}; + + // Three independently toggleable flags to compare the legacy PID + // methodology (independent 3-sigma TPC/TOF cuts, minimum-chi2 species + // choice, no rapidity cut) with the ALICE 2511.10399-based one. Each + // isolates one component, so any combination of the 8 can be run to + // see which change drives a statistics difference. The gating and + // selection defaults reproduce the 2511.10399 behaviour; setting all + // three to the legacy side reproduces the legacy pidFromNSigma. + // + // applyRapidityCutPID defaults to false: the |y|<0.5 cut only discarded + // already-correctly-identified tracks between |y|=0.5 and the |eta|<0.8 + // acceptance, so dropping it raises both yield and purity at the same + // time. Gating and selection stay at the 2511.10399 values, because + // relaxing the gating trades purity for statistics. + Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (legacy)"}; + Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (legacy)"}; + Service pdg{}; + + Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| ZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; + static constexpr std::array MultEdges{1, 10, 15, 20, 30, 46, 81}; + + static std::string multLabel(int im) + { + return std::to_string(MultEdges[im]) + "-" + std::to_string(MultEdges[im + 1] - 1); + } + + // ---------------------------------------------------------------- + // Mixing pools + // [sphClass][zvtxBin][multBin] — "all" (existing) + // [pidIdx][sphClass][zvtxBin][multBin] — per PID species + // pidIdx: 0=pion, 1=kaon, 2=proton + // ---------------------------------------------------------------- + static constexpr int NPidCorr = 3; // pion, kaon, proton + static constexpr std::array PidCorrNames{"pion", "kaon", "proton"}; + // PDG codes corresponding to pidIdx + static int pidCorrPDG(int idx) + { + if (idx == 0) { + return PDG_t::kPiPlus; + } + if (idx == 1) { + return PDG_t::kKPlus; + } + return PDG_t::kProton; + } + /// Index of a pion/kaon/proton in the NPidCorr-sized arrays, or -1 for any other species. + static int pidSpeciesToIdx(PidSpecies pid) + { + if (pid < PidPion || pid > PidProton) { + return -1; + } + return pid - PidPion; + } + + using EventPool = std::deque>; + // Pool "all" — separate per MC (generated) and Reco (reconstructed) + std::array, NZvtxBins>, NSphClasses> eventPoolsMC; + std::array, NZvtxBins>, NSphClasses> eventPoolsReco; + // Pool per PID species (like-sign) — separate for MC and Reco + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidMC; + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidReco; + + // ---------------------------------------------------------------- + // Histogram registry + handles + // ---------------------------------------------------------------- + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // SE/ME TH2D "all": [sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses> hSeMC{}; + std::array, NMultBins>, NSphClasses> hMeMC{}; + std::array, NMultBins>, NSphClasses> hSeReco{}; + std::array, NMultBins>, NSphClasses> hMeReco{}; + + // SE/ME TH2D like-sign per species: [pidIdx][sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidReco{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidReco{}; + + // Trigger counter: axes = mult bin (x) x sphClass (y) — MC and Reco separate + std::shared_ptr hNtrigMC; + std::shared_ptr hNtrigReco; + std::array, NPidCorr> hNtrigPidMC{}; + std::array, NPidCorr> hNtrigPidReco{}; + + // Per-mult sphericity — MC and Reco separate + std::array, NMultBins> hSphMultMC{}; + std::array, NMultBins> hSphMultReco{}; + + // ---------------------------------------------------------------- + // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. + // Bins: 1=tracks seen, 2=passed isGlobalTrackWoDCA, 3=passed DCAxy, + // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). + // Remove once the empty-Reco-histograms issue is understood/fixed. + // ---------------------------------------------------------------- + std::shared_ptr hTrackCutDebugReco; + + // ---------------------------------------------------------------- + // PID confusion matrix (true species from PDG vs. tagged species from + // pidFromNSigma applied to the SAME reconstructed track, per pT bin). + // Quick contamination estimate, reported as a systematic; it does NOT + // correct anything. Filled only by processMCConfusion (needs both MC + // truth and reconstructed nSigma together, unlike + // processMC/processReco which only ever see one or the other). + // ---------------------------------------------------------------- + std::shared_ptr hPidConfusionMC; + + // ---------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------- + + static PidSpecies pidFromPDG(int pdg) + { + switch (std::abs(pdg)) { + case PDG_t::kPiPlus: + return PidPion; + case PDG_t::kKPlus: + return PidKaon; + case PDG_t::kProton: + return PidProton; + default: + return PidUnidentified; + } + } + + /// PID from TPC(+TOF) nSigma for reconstructed tracks. Two independently + /// toggleable behaviors (see the useLegacyNSigmaGating and + /// useLegacyPIDSelection Configurables), so that the legacy and the + /// ALICE 2511.10399-based methodologies can be compared: + /// + /// legacyGating (gate = "does this species hypothesis pass at all"): + /// false (default): pT>0.5 && TOF available -> combined + /// N_sigma,PID = sqrt(nsTPC^2+nsTOF^2) < maxSel (2 sigma, per ALICE + /// 2511.10399 Sec. 4); else TPC-only. + /// true: legacy style -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, + /// independently (no quadrature sum for the gate itself), TOF + /// required whenever available regardless of pT. + /// + /// legacySelection (what to do with hypotheses that pass the gate): + /// false (default): reject the track outright (PidUnidentified) if MORE + /// THAN ONE hypothesis also falls below the looser ambiguity + /// threshold maxAmbig (3 sigma) -- this comparison always uses the + /// combined-quadrature N_sigma,PID, regardless of legacyGating, so + /// the two flags stay independently meaningful. + /// true: legacy style -- among hypotheses passing the gate, + /// pick the one with minimum chi2 = nsTPC^2+nsTOF^2 (or nsTPC^2 + /// alone if no TOF); no rejection for multiple passing hypotheses. + /// + /// The two flags combine independently (e.g. legacy gating + new + /// selection is a valid, meaningful hybrid) -- this is deliberate, so + /// the statistics impact of each component can be isolated rather than + /// only comparing "old vs new" as one indivisible block. + template + PidSpecies pidFromNSigma(const T& track, double maxSel, double maxAmbig, + double maxTPC, double maxTOF, + bool legacyGating, bool legacySelection) const + { + struct Hypo { + PidSpecies pid = PidUnidentified; + double nsTPC = 0.; + double nsTOF = 0.; + }; + const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; + + // TOF unavailable -> nSigma returns -999; check with any species + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < MaxValidNSigma); + bool useCombined = (track.pt() > MinPtCombinedPid) && hasTOF; // only feeds the new-style gate + + auto passesGate = [&](const Hypo& h) { + if (legacyGating) { + return (std::fabs(h.nsTPC) < maxTPC) && (!hasTOF || std::fabs(h.nsTOF) < maxTOF); + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + return nsPID < maxSel; + }; + + if (legacySelection) { + // Best-fit (min chi2) among hypotheses passing the gate -- no + // ambiguity rejection (legacy style). + PidSpecies best = PidUnidentified; + double bestChi2 = std::numeric_limits::max(); + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); + if (chi2 < bestChi2) { + bestChi2 = chi2; + best = h.pid; + } + } + return best; + } + + // Ambiguity-rejection selection (current default style). + int nBelowAmbig = 0; + PidSpecies selected = PidUnidentified; + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + if (nsPID < maxAmbig) { + ++nBelowAmbig; + } + selected = h.pid; + } + if (nBelowAmbig > 1) { + return PidUnidentified; // ambiguous, reject + } + return selected; + } + + /// Returns PDG mass for identified species, 0 for unidentified. + static double pidMass(PidSpecies pid) + { + switch (pid) { + case PidPion: + return o2::constants::physics::MassPiPlus; + case PidKaon: + return o2::constants::physics::MassKPlus; + case PidProton: + return o2::constants::physics::MassProton; + default: + return 0.; + } + } + + // Returns specific sph class (0,1,2) or -1 if unphysical + // Note: class 3 ("all") is handled separately — it is always filled + // when sph > 0, without needing a specific bin lookup. + static int sphClass(double sph) + { + if (sph <= 0.) { + return -1; + } + for (int ic = 0; ic < NSphClasses - 1; ++ic) { + if (sph <= SphMax[ic]) { + return ic; + } + } + return -1; + } + + static double computeSphericity(const std::vector& tracks) + { + if (tracks.size() < MinTracksSphericity) { + return -1.0; + } + double sxx = 0., sxy = 0., syy = 0.; + for (const auto& t : tracks) { + double px = t.pt * std::cos(t.phi); + double py = t.pt * std::sin(t.phi); + sxx += px * px; + sxy += px * py; + syy += py * py; + } + double tr = sxx + syy; + double disc = tr * tr - 4.0 * (sxx * syy - sxy * sxy); + if (disc < 0.) { + disc = 0.; + } + double lambdaMin = (tr - std::sqrt(disc)) / 2.0; + if (tr < Epsilon) { + return -1.0; + } + return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); + } + + /// Compute rapidity from pT, eta and particle mass. + /// y = 0.5 * ln((energy + pz) / (energy - pz)) + /// For unidentified particles (mass <= 0), returns eta. + static double computeRapidity(double pt, double eta, double mass) + { + if (mass <= 0.) { + return eta; // massless / unidentified -> y = eta + } + double pz = pt * std::sinh(eta); + double energy = std::sqrt(mass * mass + pt * pt + pz * pz); + if (energy <= std::fabs(pz) + Epsilon) { + return eta; // safety + } + return 0.5 * std::log((energy + pz) / (energy - pz)); + } + + // ---------------------------------------------------------------- + // init + // ---------------------------------------------------------------- + void init(InitContext const&) + { + const AxisSpec axisZvtx{100, -15., 15., "z_{vtx} (cm)"}; + const AxisSpec axisMult{100, 0., 180., "N_{ch}"}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{60, 0., o2::constants::math::TwoPI, "#phi"}; + const AxisSpec axisDphi{30, -o2::constants::math::PIHalf, o2::constants::math::PI + o2::constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; + // Reserved for a possible rapidity-differentiated analysis -- unused + // while the PID correlations run pseudorapidity-only. + [[maybe_unused]] const AxisSpec axisDy{32, -1.6, 1.6, "#Delta y"}; + const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec axisSph{100, 0., 1., "S_{T}"}; + const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; + + // ---------------------------------------------------------------- + // Common lambda: books a complete set of histograms for one + // "channel" (MC or Reco), with all names suffixed accordingly. + // Takes references to all channel-specific members. + // ---------------------------------------------------------------- + auto bookChannel = [&](const char* suf, + std::array, NMultBins>, NSphClasses>& hSE, + std::array, NMultBins>, NSphClasses>& hME, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hSEpid, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hMEpid, + std::shared_ptr& hNtrig, + std::array, NPidCorr>& hNtrigPID, + std::array, NMultBins>& hSphMult) { + // QA + auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{NEventCuts, HalfBin, static_cast(NEventCuts) + HalfBin}}); + hQA->GetXaxis()->SetBinLabel(EventCutRead, "Events read"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtx, "|z_{vtx}|<10"); + hQA->GetXaxis()->SetBinLabel(EventCutMultBin, "multBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtxBin, "zvtxBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutLeadPt, "leadPt #in [min,max]"); + hQA->GetXaxis()->SetBinLabel(EventCutSphericity, "S_{T} valid"); + hQA->GetXaxis()->SetBinLabel(EventCutSphClass, "sphClass OK"); + hQA->GetXaxis()->SetBinLabel(EventCutSeFilled, "SE filled"); + + histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); + histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); + histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); + histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); + histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); + histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); + histos.add(Form("ptAssocHistogram_%s", suf), Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); + histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("sphericity_beforeCuts_%s", suf), Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); + + // Per-mult sphericity + for (int im = 0; im < NMultBins; ++im) { + hSphMult[im] = histos.add( + Form("sphericity_mult%d_%s", im, suf), + Form("S_T [%s] (%s);S_{T};events", multLabel(im).c_str(), suf), + HistType::kTH1D, {axisSph}); + } + + // Trigger counter TH2: x = multBin, y = sphClass + hNtrig = histos.add( + Form("hNtrig%s", suf), + Form("N_{trig} (%s);mult bin;sph class", suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrig->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrig->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + + // SE/ME TH2D: [sphClass][multBin] + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSE[ic][im] = histos.add( + Form("hSE%s_sph%d_mult%d", suf, ic, im), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hME[ic][im] = histos.add( + Form("hME%s_sph%d_mult%d", suf, ic, im), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + + // ---- SE/ME like-sign per PID species: [pidIdx][sphClass][multBin] --- + // PID correlations use Delta-eta, same as the inclusive hSE/hME + // histograms, NOT Delta-y. The rapidity infrastructure + // (computeRapidity(), the per-track y field, axisDy) is kept for a + // possible rapidity-differentiated analysis and is deliberately + // unused for now. + for (int ip = 0; ip < NPidCorr; ++ip) { + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSEpid[ip][ic][im] = histos.add( + Form("hSE%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hMEpid[ip][ic][im] = histos.add( + Form("hME%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + // Trigger counter per PID species + hNtrigPID[ip] = histos.add( + Form("hNtrig%s_pid%d", suf, ip), + Form("N_{trig} %s (%s);mult bin;sph class", PidCorrNames[ip], suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrigPID[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrigPID[ip]->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + } + }; + + // Book histograms for MC (generated) channel + bookChannel("MC", + hSeMC, hMeMC, hSePidMC, hMePidMC, + hNtrigMC, hNtrigPidMC, hSphMultMC); + + // Book histograms for Reco (reconstructed) channel + bookChannel("Reco", + hSeReco, hMeReco, hSePidReco, hMePidReco, + hNtrigReco, hNtrigPidReco, hSphMultReco); + + // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco + hTrackCutDebugReco = histos.add( + "hTrackCutDebug_Reco", + "Track cut flow (Reco, DEBUG);cut;tracks", + HistType::kTH1D, {{NTrackCuts, HalfBin, static_cast(NTrackCuts) + HalfBin}}); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutSeen, "seen"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutGlobal, "passed isGlobalTrackWoDCA"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaXY, "passed DCAxy"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaZ, "passed DCAz"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutEta, "passed eta"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutPt, "passed pt (selected)"); + + // PID confusion matrix (contamination estimate) -- + // axes: pT, true species (PDG), tagged species (pidFromNSigma). + // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. + const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -HalfBin, static_cast(NPidSpecies) - HalfBin, "species"}; + hPidConfusionMC = histos.add( + "hPidConfusion_MC", + "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", + HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); + for (int ip = 0; ip < static_cast(NPidSpecies); ++ip) { + hPidConfusionMC->GetYaxis()->SetBinLabel(ip + 1, PidNames[ip]); + hPidConfusionMC->GetZaxis()->SetBinLabel(ip + 1, PidNames[ip]); + } + } + + // ---------------------------------------------------------------- + // processMC + // ---------------------------------------------------------------- + void processMC(aod::McCollision const& mcCollision, + aod::McParticles const& mcParticles) + { + histos.fill(HIST("evSel_MC"), EventCutRead); + + const double zvtx = mcCollision.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtx); + histos.fill(HIST("Zvertex_MC"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& p : mcParticles) { + if (!p.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(p.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + if (std::fabs(p.eta()) > maxEta) { + continue; + } + if (p.pt() < minAssocPt) { + continue; + } + + double mass = pdgPtr->Mass(); + double rap = computeRapidity(p.pt(), p.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species + // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the + // track regardless, gated only by |eta| < maxEta above. Clean way to + // do this: keep pdg for all-species bookkeeping, but zero it (-> + // PidUnidentified via pidFromPDG) when the rapidity cut fails, so the + // track contributes to hSE/hME but not hSEpid/hMEpid. + int pdgForPID = p.pdgCode(); + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != PidUnidentified && std::fabs(rap) > maxRapPID) { + pdgForPID = 0; + } + + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), pdgForPID}); + + histos.fill(HIST("etaHistogram_MC"), p.eta()); + histos.fill(HIST("phiHistogram_MC"), p.phi()); + histos.fill(HIST("ptHistogram_MC"), p.pt()); + + if (p.pt() > pTlead) { + pTlead = p.pt(); + phiLead = p.phi(); + etaLead = p.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_MC"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_MC"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + // Fill sphericity_beforeCuts here — no additional cuts applied yet + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_MC"), sph); + } + if (sph < 0.) { + return; // fewer than 3 tracks + } + histos.fill(HIST("evSel_MC"), EventCutSphericity); + histos.fill(HIST("sphericity_MC"), sph); + hSphMultMC[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); // 0,1,2 or -1 + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigMC->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigMC->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); // "all" + + // ================================================================ + // SAME-EVENT correlations + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; // associated pT < leading pT + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // TH2D SE: specific sph class (uses Delta-eta) + hSeMC[sphCls][mBin]->Fill(dphi, deta); + // TH2D SE: "all" class (uses Delta-eta) + hSeMC[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // SE like-sign: fill only if trigger and associated are the same species + // Uses Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidMC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidMC[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_MC"), EventCutSeFilled); + } + + // ---- hNtrigMC per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE + // ================================================================ + // Pool "all" — specific sph + { + auto& pool = eventPoolsMC[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // Pool "all" — all sph + { + auto& poolAll = eventPoolsMC[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + // Pool per PID species — contains only the tracks of that species + for (int ip = 0; ip < NPidCorr; ++ip) { + // Build a vector with only the tracks of species ip + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + // Specific sph pool + { + auto& pool = eventPoolsPidMC[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // "all" sph pool + { + auto& pool = eventPoolsPidMC[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsMC[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeMC[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT like-sign per PID species + // Trigger = leading track of species pidLeadIdx + // Associated = tracks of the same species from the PID pool + // Uses Delta-eta (pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidMC[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidMC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMC, "Process MC truth", true); + + // ================================================================ + // processReco — reconstructed data (Collision + Tracks) + // ================================================================ + using ColReco = soa::Join; + using TrackReco = soa::Join; + + void processReco(ColReco::iterator const& col, + TrackReco const& tracks) + { + histos.fill(HIST("evSel_Reco"), EventCutRead); + + // ---- event selection ---- + if (!col.sel8()) { + return; + } + const double zvtx = col.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtx); + histos.fill(HIST("Zvertex_Reco"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& track : tracks) { + hTrackCutDebugReco->Fill(TrackCutSeen); // seen + + // Quality selection: global track without DCA (apply DCA separately) + if (!track.isGlobalTrackWoDCA()) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutGlobal); // passed isGlobalTrackWoDCA + + // DCA cuts + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaXY); // passed DCAxy + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaZ); // passed DCAz + + // Kinematic cuts (same as MC) + if (std::fabs(track.eta()) > maxEta) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutEta); // passed eta + if (track.pt() < minAssocPt) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutPt); // passed pt (selected) + + // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the + // legacy flags below switch to legacy gating/selection) + PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + double mass = pidMass(pid); + double rap = computeRapidity(track.pt(), track.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species -- see + // matching comment in processMC. Track stays in selTracks either way + // (all-species channel unaffected); only the PID assignment is reset. + if (applyRapidityCutPID && pid != PidUnidentified && std::fabs(rap) > maxRapPID) { + pid = PidUnidentified; + } + + // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), 0}); + // Store PID info in pdg field as signed pidIdx for later use: + // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) + const int pidIdx = pidSpeciesToIdx(pid); + const int pdgLike = pidIdx >= 0 ? pidCorrPDG(pidIdx) : 0; + selTracks.back().pdg = track.sign() * pdgLike; + + histos.fill(HIST("etaHistogram_Reco"), track.eta()); + histos.fill(HIST("phiHistogram_Reco"), track.phi()); + histos.fill(HIST("ptHistogram_Reco"), track.pt()); + + if (track.pt() > pTlead) { + pTlead = track.pt(); + phiLead = track.phi(); + etaLead = track.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_Reco"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); + } + if (sph < 0.) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphericity); + histos.fill(HIST("sphericity_Reco"), sph); + hSphMultReco[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigReco->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigReco->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + + // ================================================================ + // SAME-EVENT correlations (identical logic to processMC) + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // All-species SE (Delta-eta) + hSeReco[sphCls][mBin]->Fill(dphi, deta); + hSeReco[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // PID SE — same species, Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidReco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidReco[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_Reco"), EventCutSeFilled); + } + + // ---- hNtrigReco per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE (identical to processMC) + // ================================================================ + { + auto& pool = eventPoolsReco[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& poolAll = eventPoolsReco[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + for (int ip = 0; ip < NPidCorr; ++ip) { + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + { + auto& pool = eventPoolsPidReco[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& pool = eventPoolsPidReco[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" (Delta-eta) + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsReco[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeReco[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidReco[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidReco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processReco, "Process reconstructed data", false); + + // ---------------------------------------------------------------- + // processMCConfusion -- PID contamination estimate. Fills hPidConfusionMC (pT, true species, tagged + // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) + // AND the reconstructed track's nSigma values on the SAME track, + // which neither processMC (truth only) nor processReco (no truth + // link) has access to, hence a dedicated process function. Reports + // contamination as a systematic; does NOT correct anything (no + // unfolding is implemented). Off by default, like the other MC-only diagnostics. + // ---------------------------------------------------------------- + using TrackRecoMC = soa::Join; + + void processMCConfusion(ColReco::iterator const& col, + TrackRecoMC const& tracks, + aod::McParticles const&) + { + if (!col.sel8()) { + return; + } + if (std::fabs(col.posZ()) > maxZvtx) { + return; + } + + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + auto mcPart = track.mcParticle(); + if (!mcPart.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(mcPart.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + + // Same track-level cuts as processReco, so the matrix reflects the + // actual analyzed track population, not an unfiltered sample. + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + if (std::fabs(track.eta()) > maxEta) { + continue; + } + if (track.pt() < minAssocPt) { + continue; + } + + PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); + PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + + // Same rapidity cut as processReco applies to its "tagged" PID -- + // without this, applyRapidityCutPID=false would silently have zero + // effect on this matrix even though it changes the processReco yields. + if (applyRapidityCutPID && taggedPid != PidUnidentified) { + double tagMass = pidMass(taggedPid); + double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); + if (std::fabs(tagRap) > maxRapPID) { + taggedPid = PidUnidentified; + } + } + + histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); + +}; // end struct + +// ---------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 887dfeb674632316c104ea84eaa2c3e83e5b66a1 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Mon, 12 Oct 2026 20:12:23 +0800 Subject: [PATCH 262/648] [PWGCF] Add TProfiles to record on and off for cut switches (#18255) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 188 ++++++++++-------- 1 file changed, 110 insertions(+), 78 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index ded6b3c22a2..4b32e6d4c98 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -70,6 +70,25 @@ using TracksSim = aod::McParticles; using TrackSim = aod::McParticles::iterator; // *) Define enums: +enum ERecSim { + eRec = 0, + eSim, + eRecAndSim, + eRecSim_N +}; + +enum EProcess { + eProcessRec = 0, + eProcessRecSim, + eProcessSim, + eProcess_N +}; + +enum ERealMC { + eReal = 0, + eMC +}; + enum ECentralityEstimator { eFT0C = 0, eFT0M, @@ -87,13 +106,6 @@ enum EMultiplicityTables { eMultiplicityTables_N }; -enum ERecSim { - eRec = 0, - eSim, - eRecAndSim, - eRecSim_N -}; - enum ETechnicalCuts { eNoCollInTimeRangeStandard = 0, eNoCollInRofStandard, @@ -105,22 +117,25 @@ enum ETechnicalCuts { ETechnicalCuts_N }; -enum ECuts { - eBefore = 0, - eAfter, - eCuts_N +enum EEventCuts { + eSel8Cut = 0, + eCentCut, + eVertexZCut, + eNContribCut, + EEventCuts_N }; -enum ERealMC { - eReal = 0, - eMC +enum EParticleCuts { + ePtCut = 0, + eEtaCut, + eChargeCut, + EParticleCuts_N }; -enum EProcess { - eProcessRec = 0, - eProcessRecSim, - eProcessSim, - eProcess_N +enum ECuts { + eBefore = 0, + eAfter, + eCuts_N }; enum EParticleHistograms { @@ -142,11 +157,6 @@ enum EEventHistograms { eEventHistograms_N }; -enum EMiscHistograms { - eRunNumber = 0, - eMiscHistograms_N -}; - enum EWeightsHistograms { ePhiRec = 0, ePhiSim, @@ -154,33 +164,30 @@ enum EWeightsHistograms { eWeightsHistograms_N }; +enum EMiscHistograms { + eRunNumber = 0, + eMiscHistograms_N +}; + enum EObservables { eProfTwo = 0, eObservablesHist_N }; // *) Lists of names: -static constexpr std::array CentralityEstimatorNames = { - "FT0C", - "FT0M", - "FV0A", - "NTPV"}; - -static constexpr std::array MultiplicityTablesNames = { - "multTPC", - "multFV0M", - "multFT0C", - "multFT0M", - "multNTracksPV"}; - -static constexpr std::array WeightsNames = { - "ePhiRec", - "ePhiSim", - "ePt"}; - -static constexpr std::array CutsNames = { - "eBefore", - "eAfter"}; +static constexpr std::array CentralityEstimatorNames = {"FT0C", "FT0M", "FV0A", "NTPV"}; + +static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; + +static constexpr std::array WeightsNames = {"ePhiRec", "ePhiSim", "ePt"}; + +static constexpr std::array CutsNames = {"eBefore", "eAfter"}; + +static constexpr std::array TechnicalCutsNames = {"NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof"}; + +static constexpr std::array EventCutsNames = {"Sel8Cut", "CentCut", "VertexZCut", "NContribCut"}; + +static constexpr std::array ParticleCutsNames = {"PtCut", "EtaCut", "ChargeCut"}; // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root @@ -223,17 +230,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable> cfTechnicalCutSwitch{"cfTechnicalCutSwitch", {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}, "technical cuts switch, on and off by the first number before name"}; // event level cuts - Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "switch to apply Sel8 cut"}; + Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "switch to apply Sel8 (event selection decision based on TVX) cut"}; Configurable cfVertexZCutSwitch{"cfVertexZCutSwitch", true, "switch to apply VertexZ cut"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "switch to apply centrality cut"}; - Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}[cm], with convention: min <= cent <= max"}; + Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; Configurable cfNContribCutSwitch{"cfNContribCutSwitch", true, "switch to apply NContrib cut"}; Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; // particle level cuts Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "switch to apply pt cut"}; - Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}, with convention: min <= pt <= max"}; + Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}[GeV/c], with convention: min <= pt <= max"}; Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "switch to apply pt cut"}; Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; @@ -249,9 +256,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to double fSigmaInel = 7.71; std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; - + std::vector fEventCutSwitch = std::vector(EEventCuts_N, true); std::vector fVertexZCutRange = {-10., 10.}; std::vector fCentCutRange = {0., 80.}; + std::vector fParticleCutSwitch = std::vector(EParticleCuts_N, true); std::vector fPtCutRange = {0.2, 5.}; std::vector fEtaCutRange = {-0.8, 0.8}; @@ -264,15 +272,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool fDryRun = false; // book all histos and run without filling and calculating anything bool fExternalFileSwitch = false; bool fALICECentBinSwitch = true; - bool fMasterCutSwitch = true; - bool fSel8CutSwitch = true; - bool fVertexZCutSwitch = true; - bool fCentCutSwitch = true; - bool fNContribCutSwitch = true; - bool fPtCutSwitch = true; - bool fEtaCutSwitch = true; - bool fChargeCutSwitch = true; bool fQualityAssuranceSwitch = false; bool fRunMessageSwitch = false; @@ -289,11 +289,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] } ec; - struct MiscHistograms { - TList* fMiscHistList = nullptr; - TH1D* fMiscHistRunNumber = nullptr; - } misc; - struct ExternalHistograms { TList* fExternalHistogramsList = nullptr; std::array fWeights{}; @@ -310,6 +305,14 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::array fHistMultNContrib{}; } qa; + struct MiscHistograms { + TList* fMiscHistList = nullptr; + TProfile* fMiscHistRunNumber = nullptr; + TProfile* fMiscEventCuts = nullptr; + TProfile* fMiscParticleCuts = nullptr; + TProfile* fMiscTechnicalCuts = nullptr; + } misc; + // *) functions and templates std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { @@ -612,18 +615,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fSel8CutSwitch && !collision.sel8()) { // sel8 cut + if (tc.fEventCutSwitch[eSel8Cut] && !collision.sel8()) { // sel8 cut return false; } if constexpr (rm == eReal) { - if (tc.fVertexZCutSwitch && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut + if (tc.fEventCutSwitch[eVertexZCut] && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut return false; } auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - if (tc.fCentCutSwitch && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut + if (tc.fEventCutSwitch[eCentCut] && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut return false; } - if (tc.fNContribCutSwitch && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts + if (tc.fEventCutSwitch[eNContribCut] && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts return false; } } @@ -708,7 +711,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fPtCutSwitch) { // pt cuts for Rec + if (tc.fParticleCutSwitch[ePtCut]) { // pt cuts for Rec if constexpr (rm == eReal) { if (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]) { return false; @@ -721,7 +724,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } } - if (tc.fEtaCutSwitch) { + if (tc.fParticleCutSwitch[eEtaCut]) { if constexpr (rm == eReal) { // eta cuts for Rec if (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]) { return false; @@ -734,7 +737,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } } - if (tc.fChargeCutSwitch) { + if (tc.fParticleCutSwitch[eChargeCut]) { if constexpr (rm == eReal) { // charge cuts for Rec if (track.sign() != 1 && track.sign() != -1) { return false; @@ -753,7 +756,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return false; } } // end of if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim - } // end of if (tc.fChargeCutSwitch) // charge cuts for Rec + } // end of if (tc.fParticleCutSwitch[eChargeCut]) // charge cuts for Rec } // end of if constexpr (rs == eRec || rs == eRecAndSim) { return true; @@ -859,6 +862,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (isFirstCollision) { thisRunNumber = collision.bc().runNumber(); misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); // Get run number + misc.fMiscHistRunNumber->Fill(0.5, thisRunNumber); if (tc.fExternalFileSwitch) { getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", thisRunNumber)); } @@ -995,19 +999,19 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fMasterCutSwitch = cfMasterCutSwitch; tc.fTechnicalCutSwitch = cfTechnicalCutSwitch.value; - tc.fSel8CutSwitch = cfSel8CutSwitch; - tc.fVertexZCutSwitch = cfVertexZCutSwitch; - tc.fCentCutSwitch = cfCentCutSwitch; - tc.fNContribCutSwitch = cfNContribCutSwitch; + tc.fEventCutSwitch[eSel8Cut] = cfSel8CutSwitch; + tc.fEventCutSwitch[eVertexZCut] = cfVertexZCutSwitch; tc.fVertexZCutRange = cfVertexZCutRange.value; + tc.fEventCutSwitch[eCentCut] = cfCentCutSwitch; tc.fCentCutRange = cfCentCutRange.value; + tc.fEventCutSwitch[eNContribCut] = cfNContribCutSwitch; tc.fNContribCutInf = cfNContribCutInf; - tc.fPtCutSwitch = cfPtCutSwitch; + tc.fParticleCutSwitch[ePtCut] = cfPtCutSwitch; tc.fPtCutRange = cfPtCutRange.value; - tc.fEtaCutSwitch = cfEtaCutSwitch; + tc.fParticleCutSwitch[eEtaCut] = cfEtaCutSwitch; tc.fEtaCutRange = cfEtaCutRange.value; - tc.fChargeCutSwitch = cfChargeCutSwitch; + tc.fParticleCutSwitch[eChargeCut] = cfChargeCutSwitch; tc.fSigmaInel = cfSigmaInel; tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; @@ -1033,9 +1037,37 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to misc.fMiscHistList->SetOwner(true); fBaseList->Add(misc.fMiscHistList); - misc.fMiscHistRunNumber = new TH1D("eRunNumber", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber = new TProfile("[eRunNumber]", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); misc.fMiscHistList->Add(misc.fMiscHistRunNumber); + misc.fMiscTechnicalCuts = new TProfile("[eTechnicalCuts]", "Technical Cuts", ETechnicalCuts_N, 0, ETechnicalCuts_N); + for (int i = 1; i <= ETechnicalCuts_N; ++i) { + misc.fMiscTechnicalCuts->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); + if (tc.fTechnicalCutSwitch[i - 1].starts_with("1")) { + misc.fMiscTechnicalCuts->Fill(i - 0.5, 1); + } + } + misc.fMiscHistList->Add(misc.fMiscTechnicalCuts); + + misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts", EEventCuts_N, 0, EEventCuts_N); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); + if (tc.fEventCutSwitch[i - 1]) { + misc.fMiscEventCuts->Fill(i - 0.5, 1); + } + } + misc.fMiscHistList->Add(misc.fMiscEventCuts); + + misc.fMiscParticleCuts = new TProfile("[eParticleCuts]", "Particle Level Cuts", EParticleCuts_N, 0, EParticleCuts_N); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCuts->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + if (tc.fParticleCutSwitch[i - 1]) { + misc.fMiscParticleCuts->Fill(i - 0.5, 1); + } + } + misc.fMiscHistList->Add(misc.fMiscParticleCuts); + // *) Book particle TLists: pc.fParticleHistList = new TList(); pc.fParticleHistList->SetName("ParticleHistograms"); From 8bd279c51b038d128846f436126fa87cf9b0fa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 12 Oct 2026 15:34:44 +0200 Subject: [PATCH 263/648] [Scripts] Remove deprecated formatting script (#18239) --- Scripts/format_includes.awk | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100755 Scripts/format_includes.awk diff --git a/Scripts/format_includes.awk b/Scripts/format_includes.awk deleted file mode 100755 index b0224f1f747..00000000000 --- a/Scripts/format_includes.awk +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/awk -f - -# Fix include style. -# NB: Run before sorting. - -{ - if ($1 ~ /^#include/) { - h = substr($2, 2, length($2) - 2) - if ( h ~ /^(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)\/.*\.h/ ) { $2 = "\""h"\"" } # O2Physics - else if ( h ~ /^(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*\/.*\.h/ ) { $2 = "<"h">" } # O2 - else if ( h ~ /^(T[A-Z]|Math\/|Roo[A-Z])[[:alnum:]\/]+\.h/ ) { $2 = "<"h">" } # ROOT - else if ( h ~ /^KF[A-Z][[:alnum:]]+\.h/ ) { $2 = "<"h">" } # KFParticle - else if ( h ~ /^(fastjet\/|onnxruntime)/ ) { $2 = "<"h">" } # FastJet, ONNX - else if ( h ~ /^.*DataModel\// ) { $2 = "\""h"\"" } # incomplete path to DataModel - else if ( h ~ /^([[:alnum:]_]+\/)+[[:alnum:]_]+\.h/ ) { $2 = "<"h">" } # other third-party - else if ( $2 ~ /^".*\./ ) { } # other local-looking file - else if ( h ~ /^[[:lower:]_]+\.h/ ) { $2 = "<"h">" } # C system - else if ( h ~ /^[[:lower:]_\/]+$/ ) { $2 = "<"h">" } # C++ system - } - print -} From 1a0eaf70ab5df44f8a8b71e1280a342e30dfd9fa Mon Sep 17 00:00:00 2001 From: Rahul Verma <110929992+rahulverma012@users.noreply.github.com> Date: Mon, 12 Oct 2026 22:18:05 +0530 Subject: [PATCH 264/648] [PWGCF] Changed the derived table structure to reduce storage output (#18251) --- .../Tasks/hParticleCorrelation.cxx | 2470 +++++------------ 1 file changed, 658 insertions(+), 1812 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx index fe1e9b042fa..032a5e988f9 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -118,204 +117,54 @@ DECLARE_SOA_TABLE(ResonanceCndts, "AOD", "RESONCNDTS", o2::soa::Index<>, //______________________________________________________________________________ // Derived correlation tables +//______________________________________________________________________________ namespace hpcorrcollision { -DECLARE_SOA_COLUMN(GlobalCollisionId, globalCollisionId, int64_t); -DECLARE_SOA_COLUMN(OriginalCollisionId, originalCollisionId, int64_t); -DECLARE_SOA_COLUMN(MixingEstimator, mixingEstimator, uint8_t); -DECLARE_SOA_COLUMN(MixingBin, mixingBin, int32_t); -DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Value of the mixing estimator chosen in the same-event task +DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); //! Mixing pools this collision belongs to (CorrPresenceMask bits) } // namespace hpcorrcollision DECLARE_SOA_TABLE(HPCorrColls, "AOD", "HPCORRCOLL", o2::soa::Index<>, - hpcorrcollision::GlobalCollisionId, - hpcorrcollision::OriginalCollisionId, - o2::aod::origins::DataframeID, o2::aod::collision::PosZ, - o2::aod::cent::CentFT0C, - o2::aod::cent::CentFT0M, - o2::aod::cent::CentFT0A, - o2::aod::cent::CentFV0A, - hpcorrcollision::MixingEstimator, - hpcorrcollision::MixingBin, + hpcorrcollision::Centrality, hpcorrcollision::CorrMask); -//______________________________________________________________________________ -namespace hpcorrtrack +//______________________________________________________________________________ +// Trigger tracks +namespace hpcorrtrigger { -DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep HPCorrColl naming consistent with the existing derived collision table) - -DECLARE_SOA_COLUMN(GlobalColRefId, globalColRefId, int64_t); -DECLARE_SOA_COLUMN(OriginalColRefId, originalColRefId, int64_t); - -DECLARE_SOA_COLUMN(GlobalTrackId, globalTrackId, int64_t); -DECLARE_SOA_COLUMN(OriginalTrackId, originalTrackId, int64_t); - -// Kinematics +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(P, p, float); -DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, float); -DECLARE_SOA_COLUMN(TofExpMom, tofExpMom, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); -DECLARE_SOA_COLUMN(Sign, sign, int8_t); - -// Track quantities -DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); -DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); -DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, float); - -// TOF information -DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); -DECLARE_SOA_COLUMN(Beta, beta, float); - -// TPC PID -DECLARE_SOA_COLUMN(TpcNSigmaEl, tpcNSigmaEl, float); -DECLARE_SOA_COLUMN(TpcNSigmaMu, tpcNSigmaMu, float); -DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); -DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); -DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, float); -DECLARE_SOA_COLUMN(TpcNSigmaDe, tpcNSigmaDe, float); - -// TOF PID -DECLARE_SOA_COLUMN(TofNSigmaEl, tofNSigmaEl, float); -DECLARE_SOA_COLUMN(TofNSigmaMu, tofNSigmaMu, float); -DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); -DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); -DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); -DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); - -} // namespace hpcorrtrack - -DECLARE_SOA_TABLE(HPCorrTracks, "AOD", "HPCORRTRACK", o2::soa::Index<>, - hpcorrtrack::HPCorrCollId, - - hpcorrtrack::GlobalColRefId, - hpcorrtrack::OriginalColRefId, - - hpcorrtrack::GlobalTrackId, - hpcorrtrack::OriginalTrackId, - - o2::aod::origins::DataframeID, - - hpcorrtrack::Pt, - hpcorrtrack::P, - hpcorrtrack::TpcInnerParam, - hpcorrtrack::TofExpMom, - hpcorrtrack::Eta, - hpcorrtrack::Phi, - hpcorrtrack::Sign, - - hpcorrtrack::DcaXY, - hpcorrtrack::DcaZ, - hpcorrtrack::TpcSignal, - - hpcorrtrack::HasTOF, - hpcorrtrack::Beta, - - hpcorrtrack::TpcNSigmaEl, - hpcorrtrack::TpcNSigmaMu, - hpcorrtrack::TpcNSigmaPi, - hpcorrtrack::TpcNSigmaKa, - hpcorrtrack::TpcNSigmaPr, - hpcorrtrack::TpcNSigmaDe, - - hpcorrtrack::TofNSigmaEl, - hpcorrtrack::TofNSigmaMu, - hpcorrtrack::TofNSigmaPi, - hpcorrtrack::TofNSigmaKa, - hpcorrtrack::TofNSigmaPr, - hpcorrtrack::TofNSigmaDe); +} // namespace hpcorrtrigger + +DECLARE_SOA_TABLE(HPCorrTriggers, "AOD", "HPCORRTRIG", o2::soa::Index<>, + hpcorrtrigger::HPCorrCollId, + hpcorrtrigger::Pt, + hpcorrtrigger::Eta, + hpcorrtrigger::Phi); //______________________________________________________________________________ -namespace hpcorrresonance +// Associated particles: tracks, identified hadrons and resonance candidates +namespace hpcorrassoc { -DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep HPCorrColl naming consistent with the existing derived collision table) -DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, HPCorrTracks, "_Pos"); -DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, HPCorrTracks, "_Neg"); - -// Collision references -DECLARE_SOA_COLUMN(GlobalColRefId, globalColRefId, int64_t); -DECLARE_SOA_COLUMN(OriginalColRefId, originalColRefId, int64_t); - -// Positive-daughter track references -DECLARE_SOA_COLUMN(GlobalPosTrackRefId, globalPosTrackRefId, int64_t); -DECLARE_SOA_COLUMN(OriginalPosTrackRefId, originalPosTrackRefId, int64_t); - -// Negative-daughter track references -DECLARE_SOA_COLUMN(GlobalNegTrackRefId, globalNegTrackRefId, int64_t); -DECLARE_SOA_COLUMN(OriginalNegTrackRefId, originalNegTrackRefId, int64_t); - -// Resonance references -DECLARE_SOA_COLUMN(GlobalResonanceId, globalResonanceId, int64_t); -DECLARE_SOA_COLUMN(OriginalResonanceId, originalResonanceId, int64_t); - -// Resonance kinematics +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); - -// Invariant masses -DECLARE_SOA_COLUMN(MPhi1020, mPhi1020, float); -DECLARE_SOA_COLUMN(MKStar892, mKStar892, float); -DECLARE_SOA_COLUMN(MKStar892Bar, mKStar892Bar, float); -DECLARE_SOA_COLUMN(MLambda1520, mLambda1520, float); -DECLARE_SOA_COLUMN(MLambda1520Bar, mLambda1520Bar, float); - -// Mass-region / resonance tags -DECLARE_SOA_COLUMN(Phi1020Tag, phi1020Tag, uint8_t); -DECLARE_SOA_COLUMN(KStar892Tag, kStar892Tag, uint8_t); -DECLARE_SOA_COLUMN(KStar892BarTag, kStar892BarTag, uint8_t); -DECLARE_SOA_COLUMN(Lambda1520Tag, lambda1520Tag, uint8_t); -DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); - -} // namespace hpcorrresonance - -DECLARE_SOA_TABLE(HPCorrResonances, "AOD", "HPCORRRESO", o2::soa::Index<>, - - // Derived-table relations - hpcorrresonance::HPCorrCollId, - hpcorrresonance::PosTrackId, - hpcorrresonance::NegTrackId, - - // Collision bookkeeping - hpcorrresonance::GlobalColRefId, - hpcorrresonance::OriginalColRefId, - - // Positive-track bookkeeping - hpcorrresonance::GlobalPosTrackRefId, - hpcorrresonance::OriginalPosTrackRefId, - - // Negative-track bookkeeping - hpcorrresonance::GlobalNegTrackRefId, - hpcorrresonance::OriginalNegTrackRefId, - - // Resonance bookkeeping - hpcorrresonance::GlobalResonanceId, - hpcorrresonance::OriginalResonanceId, - - // Source dataframe - o2::aod::origins::DataframeID, - - // Resonance kinematics - hpcorrresonance::Pt, - hpcorrresonance::Eta, - hpcorrresonance::Phi, - - // Invariant masses - hpcorrresonance::MPhi1020, - hpcorrresonance::MKStar892, - hpcorrresonance::MKStar892Bar, - hpcorrresonance::MLambda1520, - hpcorrresonance::MLambda1520Bar, - - // Resonance tags - hpcorrresonance::Phi1020Tag, - hpcorrresonance::KStar892Tag, - hpcorrresonance::KStar892BarTag, - hpcorrresonance::Lambda1520Tag, - hpcorrresonance::Lambda1520BarTag); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Tracks: +1 (h+, pi+, K+, p) / -1 (h-, pi-, K-, pbar). Resonances: 0 (species, incl. anti-K*0 / anti-Lambda(1520), is in UsedMask) +DECLARE_SOA_COLUMN(UsedMask, usedMask, uint64_t); //! CorrPresenceMask bits of every correlation this particle was used in: 0-7 tracks (h, pi, K, p x low/high pT), 18-47 resonances (species x pT x mass window) +} // namespace hpcorrassoc + +DECLARE_SOA_TABLE(HPCorrAssocs, "AOD", "HPCORRASSOC", o2::soa::Index<>, + hpcorrassoc::HPCorrCollId, + hpcorrassoc::Pt, + hpcorrassoc::Eta, + hpcorrassoc::Phi, + hpcorrassoc::Sign, + hpcorrassoc::UsedMask); } // namespace o2::aod // Delta phi in [-pi/2, 3pi/2) @@ -483,12 +332,14 @@ enum TrackRejectionTag { enum QAStageEnum { kBeforeSelection = 0, + kAfterSelFunction, kAfterSelection, kNQAStages }; static constexpr std::array QAStageDire = { "BeforeSelection/", + "AfterSelFunction/", "AfterSelection/"}; enum EventQABin { @@ -791,6 +642,9 @@ constexpr uint64_t getResoRegionMaskBit(int resoType, int ptType, int massRegion return 1ULL << (18 + resoType * 6 + ptType * 3 + (massRegion - kMassPeak)); } +// Bits 18-47 (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB): resonance (species x pT x mass-window) pools +static constexpr uint64_t MaskResoRegions = static_cast(kMaskAll) & ~((1ULL << 18) - 1ULL); + static constexpr std::array, kNPid> DefaultPIDcheckValues = {{ {0.7, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pi {0.5, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Ka @@ -1492,8 +1346,8 @@ struct HParticleCorrelation1ResonanceProducer { struct HParticleCorrelation2SameEvent { Produces derivedCollisions; - Produces derivedTracks; - Produces derivedResonances; + Produces derivedTriggers; + Produces derivedAssocs; // Hisogram redistry: HistogramRegistry seEventQA{"seEventQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -1677,52 +1531,53 @@ struct HParticleCorrelation2SameEvent { ConfigurableAxis c1108axisCorrSparseAssocPt{"c1108axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; ConfigurableAxis c1109axisCorrSparseDeltaPhi{"c1109axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; ConfigurableAxis c1110axisCorrSparseDeltaEta{"c1110axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c1111axisCorrCentrality{"c1111axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator); keep equal to c2008"}; } cfgAxis; - Configurable c1111fillDauQAOnce{"c1111fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; - Configurable c1112rejectResoWithAnyTriggerDaughter{"c1112rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; - Configurable c1113requireSelectedTriggerForInvariantMass{"c1113requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; + Configurable c1112fillDauQAOnce{"c1112fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; + Configurable c1113rejectResoWithAnyTriggerDaughter{"c1113rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; + Configurable c1114requireSelectedTriggerForInvariantMass{"c1114requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; struct : ConfigurableGroup { - Configurable c1114phi1020PeakLow{"c1114phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; - Configurable c1115phi1020PeakUp{"c1115phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; - Configurable c1116phi1020LSBLow{"c1116phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; - Configurable c1117phi1020LSBUp{"c1117phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; - Configurable c1118phi1020RSBLow{"c1118phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; - Configurable c1119phi1020RSBUp{"c1119phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + Configurable c1115phi1020PeakLow{"c1115phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c1116phi1020PeakUp{"c1116phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c1117phi1020LSBLow{"c1117phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c1118phi1020LSBUp{"c1118phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c1119phi1020RSBLow{"c1119phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c1120phi1020RSBUp{"c1120phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; } cfgPhi1020CorrMass; struct : ConfigurableGroup { - Configurable c1120kStar892PeakLow{"c1120kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; - Configurable c1121kStar892PeakUp{"c1121kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; - Configurable c1122kStar892LSBLow{"c1122kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; - Configurable c1123kStar892LSBUp{"c1123kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; - Configurable c1124kStar892RSBLow{"c1124kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; - Configurable c1125kStar892RSBUp{"c1125kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + Configurable c1121kStar892PeakLow{"c1121kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable c1122kStar892PeakUp{"c1122kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable c1123kStar892LSBLow{"c1123kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable c1124kStar892LSBUp{"c1124kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable c1125kStar892RSBLow{"c1125kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable c1126kStar892RSBUp{"c1126kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; } cfgKStar892CorrMass; struct : ConfigurableGroup { - Configurable c1126lambda1520PeakLow{"c1126lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; - Configurable c1127lambda1520PeakUp{"c1127lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; - Configurable c1128lambda1520LSBLow{"c1128lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; - Configurable c1129lambda1520LSBUp{"c1129lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; - Configurable c1130lambda1520RSBLow{"c1130lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; - Configurable c1131lambda1520RSBUp{"c1131lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + Configurable c1127lambda1520PeakLow{"c1127lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable c1128lambda1520PeakUp{"c1128lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable c1129lambda1520LSBLow{"c1129lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable c1130lambda1520LSBUp{"c1130lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable c1131lambda1520RSBLow{"c1131lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable c1132lambda1520RSBUp{"c1132lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; } cfgLambda1520CorrMass; struct : ConfigurableGroup { - Configurable c1132pairMask{"c1132pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; - Configurable c1133mixingBin{"c1133mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; - Configurable c1134requireAllPairBits{"c1134requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; - Configurable c1135resetGlobalCountersPerDF{"c1135resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; + Configurable c1133pairMask{"c1133pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; + Configurable c1134mixingBin{"c1134mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; + Configurable c1135requireAllPairBits{"c1135requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; + Configurable c1136resetGlobalCountersPerDF{"c1136resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; } cfgDerivedData; struct : ConfigurableGroup { - Configurable c1136nEvtMixing{"c1136nEvtMixing", 5, "Number of events to mix"}; - Configurable c1137mixingEstimator{"c1137mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; - ConfigurableAxis c1138axisVtxMixing{"c1138axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; - ConfigurableAxis c1139axisCentMixing{"c1139axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; - ConfigurableAxis c1140axisMixingOccupancy{"c1140axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; + Configurable c1137nEvtMixing{"c1137nEvtMixing", 5, "Number of events to mix"}; + Configurable c1138mixingEstimator{"c1138mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis c1139axisVtxMixing{"c1139axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c1140axisCentMixing{"c1140axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis c1141axisMixingOccupancy{"c1141axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; } cfgMixing; using BinningTypeVtxZFT0C = ColumnBinningPolicy; @@ -1730,17 +1585,17 @@ struct HParticleCorrelation2SameEvent { using BinningTypeVtxZFT0A = ColumnBinningPolicy; using BinningTypeVtxZFV0A = ColumnBinningPolicy; - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1138axisVtxMixing, cfgMixing.c1139axisCentMixing}}; + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; int nMixBins = 0; template int getMixingBin(const C& collision) { - switch (cfgMixing.c1137mixingEstimator) { + switch (cfgMixing.c1138mixingEstimator) { case kMixCentFT0C: return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); case kMixCentFT0M: @@ -1757,7 +1612,7 @@ struct HParticleCorrelation2SameEvent { template float getMixingEstimatorValue(const C& collision) { - switch (cfgMixing.c1137mixingEstimator) { + switch (cfgMixing.c1138mixingEstimator) { case kMixCentFT0C: return collision.centFT0C(); case kMixCentFT0M: @@ -1772,9 +1627,9 @@ struct HParticleCorrelation2SameEvent { } struct : ConfigurableGroup { - Configurable> c1141pidConfigSetting{"c1141pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; - Configurable> c1142pidVetoSetting{"c1142pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; - Configurable c1143cfgId07CheckTofBeta{"c1143cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + Configurable> c1142pidConfigSetting{"c1142pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> c1143pidVetoSetting{"c1143pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable c1144cfgId07CheckTofBeta{"c1144cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; } cfgIdCut; void init(InitContext const&) @@ -1782,6 +1637,10 @@ struct HParticleCorrelation2SameEvent { if (cfgDebug.c1001printDebugMessages) { LOGF(info, "Starting init"); } + + if ((static_cast(cfgDerivedData.c1133pairMask) & ~(0xFFULL | MaskResoRegions)) != 0ULL) { + LOG(fatal) << "c1133pairMask = " << static_cast(cfgDerivedData.c1133pairMask) << " contains bits that are not mixing pools; allowed are bits 0-7 (h-h, h-pi, h-K, h-p) and 18-47 (resonance Peak/LSB/RSB)"; + } // Axes AxisSpec axisVertexZ = {30, -15., 15., "vrtx_{Z} [cm]"}; AxisSpec axisCentFT0C = {1200, -10.0, 110.0, "centFT0C(percentile)"}; @@ -1822,16 +1681,24 @@ struct HParticleCorrelation2SameEvent { AxisSpec axisCorrSparseAssocPt{cfgAxis.c1108axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c1109axisCorrSparseDeltaPhi, "#Delta#varphi"}; AxisSpec axisCorrSparseDeltaEta{cfgAxis.c1110axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c1111axisCorrCentrality, "Centrality (%)"}; const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; const AxisSpec axisResoMassCent = {102, -1.0f, 101.0f, "Centrality (%)"}; - AxisSpec axisVtxMixSpec{cfgMixing.c1138axisVtxMixing, "z_{vtx} (cm)"}; - AxisSpec axisCentMixSpec{cfgMixing.c1139axisCentMixing, "Centrality (%)"}; - AxisSpec axisPoolOccupancy{cfgMixing.c1140axisMixingOccupancy, "N eligible collisions"}; + AxisSpec axisVtxMixSpec{cfgMixing.c1139axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixSpec{cfgMixing.c1140axisCentMixing, "Centrality (%)"}; + AxisSpec axisPoolOccupancy{cfgMixing.c1141axisMixingOccupancy, "N eligible collisions"}; nMixBins = axisVtxMixSpec.getNbins() * axisCentMixSpec.getNbins(); + + // Re-build the binnings now that the configurables are set (as data members they hold the default axes) + colBinningFT0C = BinningTypeVtxZFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0M = BinningTypeVtxZFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0A = BinningTypeVtxZFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFV0A = BinningTypeVtxZFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + const AxisSpec axisMixBin{nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; const AxisSpec axisCorrChannel{NCorrCountChannels, -0.5, static_cast(NCorrCountChannels) - 0.5, "Correlation channel"}; const AxisSpec axisReadyBins{nMixBins + 1, -0.5, static_cast(nMixBins) + 0.5, "N ready mixing bins"}; @@ -1870,6 +1737,7 @@ struct HParticleCorrelation2SameEvent { }; addEventQAHistos(seEventQA, "SE/Events/BeforeSelection/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelFunction/"); seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelection/"); auto hMixStatusBefore = seEventQA.get(HIST("SE/Events/BeforeSelection/MixingBinStatus")); @@ -2007,13 +1875,13 @@ struct HParticleCorrelation2SameEvent { auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); - histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta", HistType::kTH2F, {axisDeltaPhiSpec, axisDeltaEtaSpec}); - histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); }; auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { - histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi", HistType::kTH2F, {massAxis, axisDeltaPhiSpec}); - histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta", HistType::kTH2F, {massAxis, axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaPhiSpec, axisCorrCentrality}); + histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaEtaSpec, axisCorrCentrality}); }; auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { @@ -2123,6 +1991,12 @@ struct HParticleCorrelation2SameEvent { setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/ReadyPoolMap"))->GetYaxis()); setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/NReadyMixBins"))->GetXaxis()); + mixingQA.add("DerivedData/RowsWritten", "Rows written to the derived tables;Table;Rows", HistType::kTH1D, {{4, -0.5, 3.5}}); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(1, "Collisions"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(2, "Triggers"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(3, "TrackAssocs"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(4, "ResoAssocs"); + if (cfgDebug.c1001printDebugMessages) { LOGF(info, "Finishing init"); } @@ -2141,20 +2015,20 @@ struct HParticleCorrelation2SameEvent { { // Static is only run once, ever. static const std::array doVetoTPC = { - getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kDoVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kDoVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kDoVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kDoVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kDoVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kDoVetoTPC)}; + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTPC)}; static const std::array vetoTPC = { - getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kVetoTPC), - getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kVetoTPC)}; + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTPC)}; return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); } @@ -2164,20 +2038,20 @@ struct HParticleCorrelation2SameEvent { { // Only computed once static const std::array doVetoTOF = { - getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kDoVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kDoVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kDoVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kDoVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kDoVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kDoVetoTOF)}; + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTOF)}; static const std::array vetoTOF = { - getCfg(cfgIdCut.c1142pidVetoSetting, kPi, kVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kKa, kVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kPr, kVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kEl, kVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kMu, kVetoTOF), - getCfg(cfgIdCut.c1142pidVetoSetting, kDe, kVetoTOF)}; + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTOF)}; return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); } @@ -2194,7 +2068,7 @@ struct HParticleCorrelation2SameEvent { inline bool checkReliableTOF(const T& track) { // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: - if (cfgIdCut.c1143cfgId07CheckTofBeta) { + if (cfgIdCut.c1144cfgId07CheckTofBeta) { return (track.hasTOF() && track.beta() > 0.0f); } return track.hasTOF(); @@ -2219,7 +2093,7 @@ struct HParticleCorrelation2SameEvent { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPC(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -2228,7 +2102,7 @@ struct HParticleCorrelation2SameEvent { } } - static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTPCcheck); + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCcheck); if (doRelativeTPCcheck) { if (!relativeIdOthersTPC(track)) { // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others @@ -2272,7 +2146,7 @@ struct HParticleCorrelation2SameEvent { } else { nSigmaIdDistSq = 1000000; } - static const bool doVetoOthers = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoVetoOthers); + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); if (doVetoOthers) { if (!vetoIdOthersTPCTOF(track)) { // If vetoIdOthers = true; it passed all veto checks @@ -2281,7 +2155,7 @@ struct HParticleCorrelation2SameEvent { } } - static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTOFcheck); + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTOFcheck); if (doRelativeTOFcheck) { if (!relativeIdOthersTOF(track)) { // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others @@ -2290,7 +2164,7 @@ struct HParticleCorrelation2SameEvent { } } - static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); if (doRelativeTPCTOFcheck) { if (!relativeIdOthersTPCTOF(track)) { // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others @@ -2315,15 +2189,15 @@ struct HParticleCorrelation2SameEvent { bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) { // Static cache inside function - initialized once on first call - static const auto thrPforTOF = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kThrPforTOF); - static const auto idCutTypeLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kIdCutTypeLowP); - static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTPCLowP); - static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTOFLowP); - static const auto nSigmaRadLowP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaRadLowP); - static const auto idCutTypeHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kIdCutTypeHighP); - static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTPCHighP); - static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaTOFHighP); - static const auto nSigmaRadHighP = getCfg(cfgIdCut.c1141pidConfigSetting, pidMode, kNSigmaRadHighP); + static const auto thrPforTOF = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadHighP); if (track.p() < thrPforTOF) { if (checkReliableTOF(track)) { @@ -2525,6 +2399,23 @@ struct HParticleCorrelation2SameEvent { template TrackRejectionTag selectionTrack(T const& track) { + + // Filter cuts again: resonance daughters are reached through an index into the full track table, + // which does not re-apply the Filter (the resonance producer uses looser cuts). + if (track.pt() <= cfgTrackCuts.c1043ptMin || track.pt() >= cfgTrackCuts.c1044ptMax) { + return kTrackRejectPt; + } + if (std::abs(track.eta()) >= cfgTrackCuts.c1045etaMax) { + return kTrackRejectEta; + } + if (cfgTrackCuts.c1065useFixedDCAxy && std::abs(track.dcaXY()) >= cfgTrackCuts.c1066dcaXYMax) { + return kTrackRejectFixedDCAxy; + } + if (cfgTrackCuts.c1067useFixedDCAz && std::abs(track.dcaZ()) >= cfgTrackCuts.c1068dcaZMax) { + return kTrackRejectFixedDCAz; + } + // Filter cuts are over + // Standard O2 flags if (cfgTrackCuts.c1046requireGlobalTrack && !track.isGlobalTrack()) { return kTrackRejectGlobalTrack; @@ -2786,8 +2677,8 @@ struct HParticleCorrelation2SameEvent { histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); } //______________________________________________________________________________ @@ -2863,8 +2754,8 @@ struct HParticleCorrelation2SameEvent { histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); } //______________________________________________________________________________ @@ -2877,8 +2768,8 @@ struct HParticleCorrelation2SameEvent { histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); } // Resonance correlation as a function of invariant mass @@ -2888,8 +2779,8 @@ struct HParticleCorrelation2SameEvent { const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); const float dEta = associate.eta() - trigger.eta(); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta, currentCentrality); } template @@ -2931,19 +2822,19 @@ struct HParticleCorrelation2SameEvent { { if constexpr (pairType == kHPhi) { invMass = associate.mPhi1020(); - return getMassRegionTag(invMass, cfgPhi1020CorrMass.c1116phi1020LSBLow, cfgPhi1020CorrMass.c1117phi1020LSBUp, cfgPhi1020CorrMass.c1114phi1020PeakLow, cfgPhi1020CorrMass.c1115phi1020PeakUp, cfgPhi1020CorrMass.c1118phi1020RSBLow, cfgPhi1020CorrMass.c1119phi1020RSBUp); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.c1117phi1020LSBLow, cfgPhi1020CorrMass.c1118phi1020LSBUp, cfgPhi1020CorrMass.c1115phi1020PeakLow, cfgPhi1020CorrMass.c1116phi1020PeakUp, cfgPhi1020CorrMass.c1119phi1020RSBLow, cfgPhi1020CorrMass.c1120phi1020RSBUp); } else if constexpr (pairType == kHKStar) { invMass = associate.mKStar892(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.c1122kStar892LSBLow, cfgKStar892CorrMass.c1123kStar892LSBUp, cfgKStar892CorrMass.c1120kStar892PeakLow, cfgKStar892CorrMass.c1121kStar892PeakUp, cfgKStar892CorrMass.c1124kStar892RSBLow, cfgKStar892CorrMass.c1125kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); } else if constexpr (pairType == kHKStarBar) { invMass = associate.mKStar892Bar(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.c1122kStar892LSBLow, cfgKStar892CorrMass.c1123kStar892LSBUp, cfgKStar892CorrMass.c1120kStar892PeakLow, cfgKStar892CorrMass.c1121kStar892PeakUp, cfgKStar892CorrMass.c1124kStar892RSBLow, cfgKStar892CorrMass.c1125kStar892RSBUp); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); } else if constexpr (pairType == kHLambda) { invMass = associate.mLambda1520(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1128lambda1520LSBLow, cfgLambda1520CorrMass.c1129lambda1520LSBUp, cfgLambda1520CorrMass.c1126lambda1520PeakLow, cfgLambda1520CorrMass.c1127lambda1520PeakUp, cfgLambda1520CorrMass.c1130lambda1520RSBLow, cfgLambda1520CorrMass.c1131lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); } else if constexpr (pairType == kHLambdaBar) { invMass = associate.mLambda1520Bar(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1128lambda1520LSBLow, cfgLambda1520CorrMass.c1129lambda1520LSBUp, cfgLambda1520CorrMass.c1126lambda1520PeakLow, cfgLambda1520CorrMass.c1127lambda1520PeakUp, cfgLambda1520CorrMass.c1130lambda1520RSBLow, cfgLambda1520CorrMass.c1131lambda1520RSBUp); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); } invMass = -1.0f; @@ -3139,33 +3030,78 @@ struct HParticleCorrelation2SameEvent { } } - inline bool shouldStoreDerivedCollision(uint64_t corrMask, int mixingBin) + uint64_t getStoredCorrMask(uint64_t corrMask, int mixingBin) { if (mixingBin < 0 || mixingBin >= nMixBins) { - return false; + return 0ULL; } - if (cfgDerivedData.c1133mixingBin >= 0 && mixingBin != cfgDerivedData.c1133mixingBin) { - return false; + if (cfgDerivedData.c1134mixingBin >= 0 && mixingBin != cfgDerivedData.c1134mixingBin) { + return 0ULL; } - const uint64_t requestedMask = cfgDerivedData.c1132pairMask; - + const uint64_t requestedMask = cfgDerivedData.c1133pairMask; if (requestedMask == 0ULL) { - return corrMask != 0ULL; + return corrMask; + } + + if (cfgDerivedData.c1135requireAllPairBits && (corrMask & requestedMask) != requestedMask) { + return 0ULL; + } + + // Keeping only the requested pools, so tracks / resonances of other channels are not written. + // Resonance pools are the Peak/LSB/RSB bits (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB). + return corrMask & requestedMask; + } + + //______________________________________________________________________________ + // Tracks : bit 0-7 = pools h, pi, K, p x low/high pT (same bit numbers as CorrPresenceMask), + // bit 15 = used as trigger + // Resonances : bit r = CorrPresenceMask pool bit (18 + r), r = species * 6 + pT class * 3 + (mass window - 1) + // Setting a bit again (e.g. for another trigger) changes nothing, so there are no duplicates, + // and no sorting or searching is needed. Re-sized per collision; the vectors keep their capacity. + static constexpr uint16_t TriggerUsedFlag = 1 << 15; + std::vector trackUsedFlags; + std::vector resoUsedFlags; + int64_t firstTrackGI = 0; + int64_t firstResoGI = 0; + float currentCentrality = -1.0f; // mixing-estimator value of the current collision, used as centrality axis of the correlation histograms + + template + void markTrackUsed(const T& track, const uint16_t flag) + { + const int64_t i = track.globalIndex() - firstTrackGI; + if (i >= 0 && i < static_cast(trackUsedFlags.size())) { + trackUsedFlags[i] |= flag; } + } - if (cfgDerivedData.c1134requireAllPairBits) { - return (corrMask & requestedMask) == requestedMask; + template + void markResoUsed(const R& resonance, const uint32_t flag) + { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i >= 0 && i < static_cast(resoUsedFlags.size())) { + resoUsedFlags[i] |= flag; } + } - return (corrMask & requestedMask) != 0u; + // Particle (+1) / antiparticle (-1) / self-conjugate (0) flag from the associate type + static int8_t getResoSign(const int assocType) + { + if (assocType == kCountHKStar || assocType == kCountHLambda) { + return 1; + } + if (assocType == kCountHKStarBar || assocType == kCountHLambdaBar) { + return -1; + } + return 0; // phi(1020) } // 01-Start-obtaining trigger-LowPtAssociates correlation template void executeTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, const auto& triggerGIList, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; int associateSelTag = kTrackAccepted; int assocPosDauSelTag = kTrackAccepted; int assocNegDauSelTag = kTrackAccepted; @@ -3188,6 +3124,7 @@ struct HParticleCorrelation2SameEvent { fillCorrParticleQA(histReg, associate); fillTrigAssociateCorrelation(histReg, trigger, associate); incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHH, PtType))); } if constexpr (pairType == kHId) { @@ -3198,14 +3135,17 @@ struct HParticleCorrelation2SameEvent { if (selPion(associate, pionIdMethod)) { fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPi, PtType))); } if (selKaon(associate, kaonIdMethod)) { fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHKa, PtType))); } if (selProton(associate, protonIdMethod)) { fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPr, PtType))); } } } @@ -3239,7 +3179,7 @@ struct HParticleCorrelation2SameEvent { } // Trigger-resonance daughter overlap -----expensive check ---- - if (c1112rejectResoWithAnyTriggerDaughter) { + if (c1113rejectResoWithAnyTriggerDaughter) { // Strict mode: reject the whole resonance if either daughter belongs to the event trigger population. if (checkTrackInList(posDauTrack, triggerGIList) || checkTrackInList(negDauTrack, triggerGIList)) { continue; @@ -3273,6 +3213,7 @@ struct HParticleCorrelation2SameEvent { incrementCorrCount(corrCountPerColl); incrementResoRegionCount(resoRegionCountsPerColl, massRegion); + markResoUsed(associate, static_cast(getResoRegionMaskBit(pairType - kHPhi, PtType, massRegion) >> 18)); } } // associate particle loop is over } // function definition is over @@ -3294,7 +3235,7 @@ struct HParticleCorrelation2SameEvent { std::vector posDauGIListRSSB; std::vector negDauGIListRSSB; - if (c1112rejectResoWithAnyTriggerDaughter) { + if (c1113rejectResoWithAnyTriggerDaughter) { for (const auto& trigger : triggers) { if (selectionTrack(trigger) != kTrackAccepted) { continue; @@ -3310,6 +3251,7 @@ struct HParticleCorrelation2SameEvent { } // fillTrigQA fillCorrParticleQA(histReg, trigger); + markTrackUsed(trigger, TriggerUsedFlag); // ++nTrigger; // triggerTrackIndexList.push_back(triggerTrack.globalIndex()); @@ -3360,11 +3302,6 @@ struct HParticleCorrelation2SameEvent { int dfNumber = 0; int64_t iCollGlobalCount = -1; - int64_t iTrackGlobalCount = -1; - int64_t iResonanceGlobalCount = -1; - struct DerivedTrackRef { - int64_t globalTrackId; - }; void processNothing(aod::Origins const& origins) { @@ -3381,13 +3318,10 @@ struct HParticleCorrelation2SameEvent { dfNumber++; mixingQA.fill(HIST("Mixing/DataFramesProcessed"), 0.0); std::vector mixingBinStatusPerDF(nMixBins); - // LOG(info)<<"DEBUG :: Origins :: "<(kEventAllFiltered)); @@ -3439,7 +3374,7 @@ struct HParticleCorrelation2SameEvent { continue; } seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedSelCollision)); - + fillEventQA(collision); const auto tracksPerCollision = fullTracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); if (tracksPerCollision.size() < cfgEvent.c1005minNFilteredTracks) { continue; @@ -3467,9 +3402,18 @@ struct HParticleCorrelation2SameEvent { continue; } - dauGIListPeak.assign(tracksPerCollision.size(), 0); - dauGIListLSB.assign(tracksPerCollision.size(), 0); - dauGIListRSB.assign(tracksPerCollision.size(), 0); + const auto resonancesPerCollision = resonanceCndts.sliceBy(resoPerCollisionPreslice, collision.globalIndex()); + trackUsedFlags.clear(); + resoUsedFlags.clear(); + if (tracksPerCollision.size() > 0) { + firstTrackGI = tracksPerCollision.begin().globalIndex(); + trackUsedFlags.assign(tracksPerCollision.iteratorAt(tracksPerCollision.size() - 1).globalIndex() - firstTrackGI + 1, 0); + } + if (resonancesPerCollision.size() > 0) { + firstResoGI = resonancesPerCollision.begin().globalIndex(); + resoUsedFlags.assign(resonancesPerCollision.iteratorAt(resonancesPerCollision.size() - 1).globalIndex() - firstResoGI + 1, 0); + } + fillEventQA(collision); CorrCountArray corrCountsPerColl{}; ResoRegionCountArray resoRegionCountsPerColl{}; @@ -3497,6 +3441,7 @@ struct HParticleCorrelation2SameEvent { auto assocLambdaBarPerCollLowPt = assocLambda1520BarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); auto assocLambdaBarPerCollHighPt = assocLambda1520BarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + currentCentrality = getMixingEstimatorValue(collision); executeCorrelation(hhCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); executeCorrelation(hIdCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); executeCorrelation(hPhiCorrelation, triggerTracksPerColl, assocPhiPerCollLowPt, assocPhiPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); @@ -3508,7 +3453,7 @@ struct HParticleCorrelation2SameEvent { // Unlike Sign Signal // LikeSign Signal bool hasSelectedTrigger = false; - if (c1113requireSelectedTriggerForInvariantMass) { + if (c1114requireSelectedTriggerForInvariantMass) { for (const auto& trigger : triggerTracksPerColl) { if (selectionTrack(trigger) == kTrackAccepted) { hasSelectedTrigger = true; @@ -3517,7 +3462,7 @@ struct HParticleCorrelation2SameEvent { } } - if (!c1113requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { + if (!c1114requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); @@ -3567,129 +3512,52 @@ struct HParticleCorrelation2SameEvent { } } + int nTriggersWritten = 0; + int nTrackAssocsWritten = 0; + int nResoAssocsWritten = 0; //______________________________________________________________________________ - // Derived tables - if (shouldStoreDerivedCollision(corrPresenceMask, mixingBin)) { + // Derived tables: only the triggers and associates marked as used, and only for the stored pools. + // Every particle is written once; its UsedMask holds all correlations it was used in. + const uint64_t storedMask = getStoredCorrMask(corrPresenceMask, mixingBin); + if (storedMask != 0ULL) { + derivedCollisions(collision.posZ(), mixingEstimatorValue, storedMask); iCollGlobalCount++; - derivedCollisions(iCollGlobalCount, - collision.globalIndex(), - dataframeID, - collision.posZ(), - collision.centFT0C(), - collision.centFT0M(), - collision.centFT0A(), - collision.centFV0A(), - static_cast(cfgMixing.c1137mixingEstimator), - mixingBin, - corrPresenceMask); - - std::map derivedTrackIndex; + + // Tracks: triggers, and h / pi / K / p associates (track pools = CorrPresenceMask bits 0-7) for (const auto& track : tracksPerCollision) { - if (selectionTrack(track) != kTrackAccepted) { - continue; // Store only selected tracks to reduce the size. + const int64_t i = track.globalIndex() - firstTrackGI; + if (i < 0 || i >= static_cast(trackUsedFlags.size()) || trackUsedFlags[i] == 0) { + continue; + } + const uint16_t flags = trackUsedFlags[i]; + if ((flags & TriggerUsedFlag) != 0u) { + nTriggersWritten++; + derivedTriggers(iCollGlobalCount, track.pt(), track.eta(), track.phi()); + } + const uint64_t usedMask = static_cast(flags & 0xFFu) & storedMask; + if (usedMask != 0ULL) { + nTrackAssocsWritten++; + derivedAssocs(iCollGlobalCount, track.pt(), track.eta(), track.phi(), static_cast(track.sign() > 0 ? 1 : -1), usedMask); } - - iTrackGlobalCount++; - derivedTracks(iCollGlobalCount, // derivedCollisionId, individual will create issues in mereged dataframes - iCollGlobalCount, - collision.globalIndex(), - iTrackGlobalCount, - track.globalIndex(), - dataframeID, - - track.pt(), - track.p(), - track.tpcInnerParam(), - track.tofExpMom(), - track.eta(), - track.phi(), - static_cast(track.sign()), - - track.dcaXY(), - track.dcaZ(), - track.tpcSignal(), - - static_cast(track.hasTOF()), - track.beta(), - - track.tpcNSigmaEl(), - track.tpcNSigmaMu(), - track.tpcNSigmaPi(), - track.tpcNSigmaKa(), - track.tpcNSigmaPr(), - track.tpcNSigmaDe(), - - track.tofNSigmaEl(), - track.tofNSigmaMu(), - track.tofNSigmaPi(), - track.tofNSigmaKa(), - track.tofNSigmaPr(), - track.tofNSigmaDe()); - derivedTrackIndex.emplace(track.globalIndex(), DerivedTrackRef{iTrackGlobalCount}); } - const auto resonancesPerCollision = resonanceCndts.sliceBy(resoPerCollisionPreslice, collision.globalIndex()); + // Resonance candidates (resonance pools = CorrPresenceMask bits 18-47; flag bit r = pool bit 18 + r) for (const auto& resonance : resonancesPerCollision) { - const auto originalPosTrackId = static_cast(resonance.posTrackId()); - const auto originalNegTrackId = static_cast(resonance.negTrackId()); - - // Positive daughter must have been stored in HPCorrTracks - const auto posIt = derivedTrackIndex.find(originalPosTrackId); - if (posIt == derivedTrackIndex.end()) { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i < 0 || i >= static_cast(resoUsedFlags.size()) || resoUsedFlags[i] == 0) { continue; } - - // Negative daughter must have been stored in HPCorrTracks - const auto negIt = derivedTrackIndex.find(originalNegTrackId); - if (negIt == derivedTrackIndex.end()) { - continue; + const uint64_t usedMask = (static_cast(resoUsedFlags[i]) << 18) & storedMask; + if (usedMask != 0ULL) { + nResoAssocsWritten++; + derivedAssocs(iCollGlobalCount, resonance.pt(), resonance.eta(), resonance.phi(), static_cast(0), usedMask); } - - iResonanceGlobalCount++; - derivedResonances( - // Relations to derived tables - iCollGlobalCount, // derivedCollisionId, - posIt->second.globalTrackId, - negIt->second.globalTrackId, - - // Collision references - iCollGlobalCount, - collision.globalIndex(), - - // Positive daughter references - posIt->second.globalTrackId, - originalPosTrackId, - - // Negative daughter references - negIt->second.globalTrackId, - originalNegTrackId, - - // Resonance references - iResonanceGlobalCount, - resonance.globalIndex(), - - // Source dataframe - dataframeID, - - // Resonance kinematics - resonance.pt(), - resonance.eta(), - resonance.phi(), - - // Invariant masses - resonance.mPhi1020(), - resonance.mKStar892(), - resonance.mKStar892Bar(), - resonance.mLambda1520(), - resonance.mLambda1520Bar(), - - // Resonance tags - resonance.phi1020Tag(), - resonance.kStar892Tag(), - resonance.kStar892BarTag(), - resonance.lambda1520Tag(), - resonance.lambda1520BarTag()); } + + mixingQA.fill(HIST("DerivedData/RowsWritten"), 0.0); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 1.0, nTriggersWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 2.0, nTrackAssocsWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 3.0, nResoAssocsWritten); } // Derived data creation block is over. } // collision loop - Over. @@ -3709,7 +3577,7 @@ struct HParticleCorrelation2SameEvent { mixingQA.fill(HIST("Mixing/PerDF/EligiblePoolOccupancy"), iMixBin, corrChannel, nEligibleCollisions); mixingQA.fill(HIST("Mixing/PerDF/PoolOccupancyDistribution"), corrChannel, nEligibleCollisions); - if (nEligibleCollisions >= static_cast(cfgMixing.c1136nEvtMixing)) { + if (nEligibleCollisions >= static_cast(cfgMixing.c1137nEvtMixing)) { ++nReadyMixBins[corrChannel]; mixingQA.fill(HIST("Mixing/PerDF/ReadyPoolMap"), iMixBin, corrChannel); } @@ -3735,78 +3603,19 @@ struct HParticleCorrelation3MixedEvent { HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry mixingOperationQA{"mixingOperationQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - static constexpr int NMixOperationChannels = 38; + // Mixing channels: 8 track channels (h-h, h-pi, h-K, h-p x low/high pT) followed by + // 30 resonance channels (5 species x low/high pT x Peak/LSB/RSB) + static constexpr int NMassWindows = 3; + static constexpr int NTrackMixChannels = static_cast(kCountHPr + 1) * static_cast(kNCorrCountPt); + static constexpr int NMixOperationChannels = NTrackMixChannels + static_cast(kNResoCorrCountTypes) * static_cast(kNCorrCountPt) * NMassWindows; static constexpr std::array MixOperationChannelName = { - "hh_LowPt", - "hh_HighPt", - "hPi_LowPt", - "hPi_HighPt", - "hKa_LowPt", - "hKa_HighPt", - "hPr_LowPt", - "hPr_HighPt", - - "hPhi_LowPt_Peak", - "hPhi_LowPt_LSB", - "hPhi_LowPt_RSB", - "hPhi_HighPt_Peak", - "hPhi_HighPt_LSB", - "hPhi_HighPt_RSB", - - "hKStar_LowPt_Peak", - "hKStar_LowPt_LSB", - "hKStar_LowPt_RSB", - "hKStar_HighPt_Peak", - "hKStar_HighPt_LSB", - "hKStar_HighPt_RSB", - - "hKStarBar_LowPt_Peak", - "hKStarBar_LowPt_LSB", - "hKStarBar_LowPt_RSB", - "hKStarBar_HighPt_Peak", - "hKStarBar_HighPt_LSB", - "hKStarBar_HighPt_RSB", - - "hLambda_LowPt_Peak", - "hLambda_LowPt_LSB", - "hLambda_LowPt_RSB", - "hLambda_HighPt_Peak", - "hLambda_HighPt_LSB", - "hLambda_HighPt_RSB", - - "hLambdaBar_LowPt_Peak", - "hLambdaBar_LowPt_LSB", - "hLambdaBar_LowPt_RSB", - "hLambdaBar_HighPt_Peak", - "hLambdaBar_HighPt_LSB", - "hLambdaBar_HighPt_RSB"}; - - static constexpr int getMixOperationChannel(const uint64_t mask) - { - constexpr int TrackMixMaskEndBit = 8; - constexpr int ResonanceMixMaskFirstBit = 18; - constexpr int ResonanceMixMaskEndBit = 48; - constexpr int ResonanceMixChannelOffset = ResonanceMixMaskFirstBit - TrackMixMaskEndBit; - - int bit = 0; - uint64_t value = mask; - - while (value > 1ULL) { - value >>= 1ULL; - ++bit; - } - - if (bit < TrackMixMaskEndBit) { - return bit; - } - - if (bit >= ResonanceMixMaskFirstBit && bit < ResonanceMixMaskEndBit) { - return bit - ResonanceMixChannelOffset; - } - - return -1; - } + "hh_LowPt", "hh_HighPt", "hPi_LowPt", "hPi_HighPt", "hKa_LowPt", "hKa_HighPt", "hPr_LowPt", "hPr_HighPt", + "hPhi_LowPt_Peak", "hPhi_LowPt_LSB", "hPhi_LowPt_RSB", "hPhi_HighPt_Peak", "hPhi_HighPt_LSB", "hPhi_HighPt_RSB", + "hKStar_LowPt_Peak", "hKStar_LowPt_LSB", "hKStar_LowPt_RSB", "hKStar_HighPt_Peak", "hKStar_HighPt_LSB", "hKStar_HighPt_RSB", + "hKStarBar_LowPt_Peak", "hKStarBar_LowPt_LSB", "hKStarBar_LowPt_RSB", "hKStarBar_HighPt_Peak", "hKStarBar_HighPt_LSB", "hKStarBar_HighPt_RSB", + "hLambda_LowPt_Peak", "hLambda_LowPt_LSB", "hLambda_LowPt_RSB", "hLambda_HighPt_Peak", "hLambda_HighPt_LSB", "hLambda_HighPt_RSB", + "hLambdaBar_LowPt_Peak", "hLambdaBar_LowPt_LSB", "hLambdaBar_LowPt_RSB", "hLambdaBar_HighPt_Peak", "hLambdaBar_HighPt_LSB", "hLambdaBar_HighPt_RSB"}; struct : ConfigurableGroup { Configurable c2001printDebugMessages{"c2001printDebugMessages", false, "Print debug messages"}; @@ -3819,112 +3628,32 @@ struct HParticleCorrelation3MixedEvent { ConfigurableAxis c2005axisCorrSparseAssocPt{"c2005axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; ConfigurableAxis c2006axisCorrSparseDeltaPhi{"c2006axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; ConfigurableAxis c2007axisCorrSparseDeltaEta{"c2007axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2008axisCorrCentrality{"c2008axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator)"}; } cfgAxis; + // Selections (track cuts, PID, pT classes, mass windows, rapidity, estimator) are applied in the same-event + // task and stored as tags. Only the mixing itself is configured here; keep these axes equal to c1139/c1140. struct : ConfigurableGroup { - Configurable c2008nEvtMixing{"c2008nEvtMixing", 5, "Number of events to mix"}; - Configurable c2009mixingEstimator{"c2009mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + Configurable c2009nEvtMixing{"c2009nEvtMixing", 5, "Number of partner events mixed with each trigger event"}; ConfigurableAxis c2010axisVtxMixing{"c2010axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; - ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality (estimator chosen by c1138mixingEstimator in the same-event task)"}; } cfgMixing; - struct : ConfigurableGroup { - Configurable c2012triggerPtLow{"c2012triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; - Configurable c2013triggerPtHigh{"c2013triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; - Configurable c2014assocPtLowMin{"c2014assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; - Configurable c2015assocPtLowMax{"c2015assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; - Configurable c2016assocPtHighMin{"c2016assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; - Configurable c2017assocPtHighMax{"c2017assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; - } cfgPartitions; - - struct : ConfigurableGroup { - Configurable c2018resoRapidityMin{"c2018resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; - Configurable c2019resoRapidityMax{"c2019resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; - - Configurable c2020phiPtLowMin{"c2020phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; - Configurable c2021phiPtLowMax{"c2021phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; - Configurable c2022phiPtHighMin{"c2022phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; - Configurable c2023phiPtHighMax{"c2023phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; - - Configurable c2024kstarPtLowMin{"c2024kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; - Configurable c2025kstarPtLowMax{"c2025kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; - Configurable c2026kstarPtHighMin{"c2026kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; - Configurable c2027kstarPtHighMax{"c2027kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; - - Configurable c2028kstarBarPtLowMin{"c2028kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; - Configurable c2029kstarBarPtLowMax{"c2029kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; - Configurable c2030kstarBarPtHighMin{"c2030kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; - Configurable c2031kstarBarPtHighMax{"c2031kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; - - Configurable c2032lambda1520PtLowMin{"c2032lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; - Configurable c2033lambda1520PtLowMax{"c2033lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; - Configurable c2034lambda1520PtHighMin{"c2034lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; - Configurable c2035lambda1520PtHighMax{"c2035lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; - - Configurable c2036lambda1520BarPtLowMin{"c2036lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; - Configurable c2037lambda1520BarPtLowMax{"c2037lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; - Configurable c2038lambda1520BarPtHighMin{"c2038lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; - Configurable c2039lambda1520BarPtHighMax{"c2039lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; - } cfgResPartitions; - - struct : ConfigurableGroup { - Configurable c2040phi1020PeakLow{"c2040phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; - Configurable c2041phi1020PeakUp{"c2041phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; - Configurable c2042phi1020LSBLow{"c2042phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; - Configurable c2043phi1020LSBUp{"c2043phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; - Configurable c2044phi1020RSBLow{"c2044phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; - Configurable c2045phi1020RSBUp{"c2045phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; - } cfgPhi1020CorrMass; - - struct : ConfigurableGroup { - Configurable c2046kStar892PeakLow{"c2046kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; - Configurable c2047kStar892PeakUp{"c2047kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; - Configurable c2048kStar892LSBLow{"c2048kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; - Configurable c2049kStar892LSBUp{"c2049kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; - Configurable c2050kStar892RSBLow{"c2050kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; - Configurable c2051kStar892RSBUp{"c2051kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; - } cfgKStar892CorrMass; - - struct : ConfigurableGroup { - Configurable c2052lambda1520PeakLow{"c2052lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; - Configurable c2053lambda1520PeakUp{"c2053lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; - Configurable c2054lambda1520LSBLow{"c2054lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; - Configurable c2055lambda1520LSBUp{"c2055lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; - Configurable c2056lambda1520RSBLow{"c2056lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; - Configurable c2057lambda1520RSBUp{"c2057lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; - } cfgLambda1520CorrMass; - - struct : ConfigurableGroup { - Configurable> c2058pidConfigSetting{"c2058pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; - Configurable> c2059pidVetoSetting{"c2059pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; - Configurable c2060cfgId07CheckTofBeta{"c2060cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; - } cfgIdCut; + // Re-built in init() with the configured axes (as a data member it holds the default axes) + using BinningTypeMixing = ColumnBinningPolicy; + BinningTypeMixing colBinning{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + int nMixBins = 0; void init(InitContext const&) { - if (cfgDebug.c2001printDebugMessages) { LOGF(info, "Starting init"); } - const AxisSpec axisP = {200, 0.0f, 10.0f, "#it{p} (GeV/#it{c})"}; const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisTPCInnerParam = {200, 0.0f, 10.0f, "#it{p}_{tpcInnerParam} (GeV/#it{c})"}; - const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; - const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; - const AxisSpec axisRapidity = {200, -5.0f, 5.0f, "Rapidity (y)"}; - const AxisSpec axisDcaXY = {100, -5.0f, 5.0f, "dcaXY"}; - const AxisSpec axisDcaZ = {100, -5.0f, 5.0f, "dcaZ"}; - const AxisSpec axisSign = {10, -5.0f, 5.0f, "track.sign"}; - - const AxisSpec axisTPCSignal = {100, -1.0f, 1000.0f, "tpcSignal"}; - const AxisSpec axisTOFBeta = {40, -2.0f, 2.0f, "tofBeta"}; - - const AxisSpec axisTPCNSigma = {200, -10.0f, 10.0f, "n#sigma_{TPC}"}; - const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; - const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; + const AxisSpec axisSign = {3, -1.5f, 1.5f, "Sign (tracks: charge, resonances: 0)"}; AxisSpec axisDeltaPhiSpec{cfgAxis.c2002axisDeltaPhi, "#Delta#varphi"}; AxisSpec axisDeltaEtaSpec{cfgAxis.c2003axisDeltaEta, "#Delta#eta"}; @@ -3932,1315 +3661,432 @@ struct HParticleCorrelation3MixedEvent { AxisSpec axisCorrSparseAssocPt{cfgAxis.c2005axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c2006axisCorrSparseDeltaPhi, "#Delta#varphi"}; AxisSpec axisCorrSparseDeltaEta{cfgAxis.c2007axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c2008axisCorrCentrality, "Centrality (%)"}; - const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; - const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; - const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; - - AxisSpec axisVtxMixOperation{cfgMixing.c2010axisVtxMixing, "z_{vtx} (cm)"}; - AxisSpec axisCentMixOperation{cfgMixing.c2011axisCentMixing, "Centrality (%)"}; - - const int nMixOperationBins = axisVtxMixOperation.getNbins() * axisCentMixOperation.getNbins(); - - const AxisSpec axisMixOperationBin = {nMixOperationBins, -0.5, static_cast(nMixOperationBins) - 0.5, "Mixing bin"}; - const AxisSpec axisMixOperationChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; - const AxisSpec axisMixPartners = {static_cast(cfgMixing.c2008nEvtMixing) + 1, -0.5, static_cast(cfgMixing.c2008nEvtMixing) + 0.5, "N mixed partner events"}; - - const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; - const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; - const AxisSpec axisMixCentralityQA = {102, -1.0f, 101.0f, "Centrality (%)"}; - const AxisSpec axisMixDeltaCentralityQA = {204, -102.0f, 102.0f, "#Delta Centrality (%)"}; - - auto addTrackQAHistos = [&](auto& histReg, const std::string& basePath) { - addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); - histReg.add((basePath + "TPCSignalVsTPCInnerParam").c_str(), "TPC signal vs p_{TPC inner};p_{TPC inner} (GeV/c);TPC signal", kTH2F, {axisTPCInnerParam, axisTPCSignal}); - histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", kTH2F, {axisP, axisTPCSignal}); - histReg.add((basePath + "TOFBetaVsTOFExpMom").c_str(), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", kTH2F, {axisTOFExpMom, axisTOFBeta}); - histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); + auto addAssocQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "Pt").c_str(), "Associate;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Associate;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Associate;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Sign").c_str(), "Associate;Sign;Counts", HistType::kTH1F, {axisSign}); }; auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); - histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta", HistType::kTH2F, {axisDeltaPhiSpec, axisDeltaEtaSpec}); - histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta}); - }; - - auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { - histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi", HistType::kTH2F, {massAxis, axisDeltaPhiSpec}); - histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta", HistType::kTH2F, {massAxis, axisDeltaEtaSpec}); - }; - - auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { - histReg.add((basePath + "P").c_str(), "Resonance;p (GeV/c);Counts", HistType::kTH1F, {axisP}); - histReg.add((basePath + "Pt").c_str(), "Resonance;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); - histReg.add((basePath + "Eta").c_str(), "Resonance;#eta;Counts", HistType::kTH1F, {axisEta}); - histReg.add((basePath + "Phi").c_str(), "Resonance;#varphi;Counts", HistType::kTH1F, {axisPhi}); - histReg.add((basePath + "Rapidity").c_str(), "Resonance;y;Counts", HistType::kTH1F, {axisRapidity}); - histReg.add((basePath + "InvMass").c_str(), massHistTitle.c_str(), HistType::kTH1F, {massAxis}); - histReg.add((basePath + "InvMassVsPt").c_str(), "Invariant mass vs p_{T};p_{T} (GeV/c);Invariant mass (GeV/c^{2})", HistType::kTH2F, {axisPt, massAxis}); - }; - - auto addResoDauQAHistos = [&](auto& histReg, const std::string& basePath) { - addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); - addIdentifiedQAHistos(histReg, basePath, axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); }; - addTrackQAHistos(hhCorrelation, "ME/hh/QA/Trigger/"); - hhCorrelation.addClone("ME/hh/QA/Trigger/", "ME/hh/QA/AssocLowPt/"); - hhCorrelation.addClone("ME/hh/QA/Trigger/", "ME/hh/QA/AssocHighPt/"); + // Histogram paths mirror the same-event task (SE/... -> ME/...) + // h-h + addAssocQAHistos(hhCorrelation, "ME/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/QA/AssocLowPt/", "ME/hh/QA/AssocHighPt/"); addCorrHistos(hhCorrelation, "ME/hh/Corr/AssocLowPt/"); hhCorrelation.addClone("ME/hh/Corr/AssocLowPt/", "ME/hh/Corr/AssocHighPt/"); - auto registerIdentifiedHistograms = [&](auto& histReg, const std::string& pairName) { - const std::string base = "ME/" + pairName + "/"; - - addTrackQAHistos(histReg, base + "QA/Trigger/"); - - addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); - addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); - histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); - histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Pr/"); - histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); - - addCorrHistos(histReg, base + "Corr/AssocLowPt/Pi/"); - histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Ka/"); - histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Pr/"); - histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); - }; - registerIdentifiedHistograms(hIdCorrelation, "hIdentified"); - - auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName, const AxisSpec& massAxis, const std::string& massHistTitle) { + // h-identified + addAssocQAHistos(hIdCorrelation, "ME/hIdentified/QA/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/", "ME/hIdentified/QA/AssocHighPt/"); + addCorrHistos(hIdCorrelation, "ME/hIdentified/Corr/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/", "ME/hIdentified/Corr/AssocHighPt/"); + + // h-resonance + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName) { const std::string base = "ME/" + pairName + "/"; - const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; - addTrackQAHistos(histReg, base + "QA/Trigger/"); - addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); - addResoDauQAHistos(histReg, qaPeak + "PosDau/"); - histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); + addAssocQAHistos(histReg, base + "QA/AssocLowPt/Peak/Reso/"); histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); - addResoMassCorrHistos(histReg, base + "Corr/AssocLowPt/MassDependent/", massAxis); histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); }; + registerResonanceHistograms(hPhiCorrelation, "hPhi"); + registerResonanceHistograms(hKStarCorrelation, "hKStar"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar"); - registerResonanceHistograms(hPhiCorrelation, "hPhi", axisPhi1020Mass, "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts"); - registerResonanceHistograms(hKStarCorrelation, "hKStar", axisKStar892Mass, "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts"); - registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar", axisKStar892Mass, "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts"); - registerResonanceHistograms(hLambdaCorrelation, "hLambda", axisLambda1520Mass, "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts"); - registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar", axisLambda1520Mass, "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts"); - - mixingOperationQA.add("ME/Mixing/CollisionPairsPerChannel", "Actual mixed collision pairs;Mixing channel;Mixed collision pairs", HistType::kTH1D, {axisMixOperationChannel}); - mixingOperationQA.add("ME/Mixing/CollisionPairsPerBinChannel", "Actual mixed collision pairs;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixOperationBin, axisMixOperationChannel}); - mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEvent", "Actual mixed partners per trigger event;Mixing channel;N partner events", HistType::kTH2D, {axisMixOperationChannel, axisMixPartners}); - mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEventVsBin", "Actual mixed partners per trigger event;Mixing bin;Mixing channel;N partner events", HistType::kTH3D, {axisMixOperationBin, axisMixOperationChannel, axisMixPartners}); - - mixingOperationQA.add("ME/Mixing/VtxZ1VsVtxZ2", "Mixed-event vertex positions;z_{vtx}^{1} (cm);z_{vtx}^{2} (cm)", HistType::kTH2F, {axisMixVtxZQA, axisMixVtxZQA}); - mixingOperationQA.add("ME/Mixing/DeltaVtxZVsChannel", "Mixed-event #Delta z_{vtx};Mixing channel;z_{vtx}^{1}-z_{vtx}^{2} (cm)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaVtxZQA}); - - mixingOperationQA.add("ME/Mixing/CentFT0C1VsCentFT0C2", "FT0C centrality of mixed events;FT0C_{1} (%);FT0C_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); - mixingOperationQA.add("ME/Mixing/CentFT0M1VsCentFT0M2", "FT0M centrality of mixed events;FT0M_{1} (%);FT0M_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); - mixingOperationQA.add("ME/Mixing/CentFT0A1VsCentFT0A2", "FT0A centrality of mixed events;FT0A_{1} (%);FT0A_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); - mixingOperationQA.add("ME/Mixing/CentFV0A1VsCentFV0A2", "FV0A centrality of mixed events;FV0A_{1} (%);FV0A_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); - - mixingOperationQA.add("ME/Mixing/DeltaCentFT0CVsChannel", "Mixed-event #Delta FT0C;Mixing channel;FT0C_{1}-FT0C_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); - mixingOperationQA.add("ME/Mixing/DeltaCentFT0MVsChannel", "Mixed-event #Delta FT0M;Mixing channel;FT0M_{1}-FT0M_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); - mixingOperationQA.add("ME/Mixing/DeltaCentFT0AVsChannel", "Mixed-event #Delta FT0A;Mixing channel;FT0A_{1}-FT0A_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); - mixingOperationQA.add("ME/Mixing/DeltaCentFV0AVsChannel", "Mixed-event #Delta FV0A;Mixing channel;FV0A_{1}-FV0A_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + //______________________________________________________________________________ + // Mixing binning (re-built here with the configured axes) and mixing QA + AxisSpec axisVtxMix{cfgMixing.c2010axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMix{cfgMixing.c2011axisCentMixing, "Centrality (%)"}; + nMixBins = axisVtxMix.getNbins() * axisCentMix.getNbins(); + colBinning = BinningTypeMixing{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + + const AxisSpec axisMixBin = {nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisMixChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; + const AxisSpec axisMixPartners = {static_cast(cfgMixing.c2009nEvtMixing) + 1, -0.5, static_cast(cfgMixing.c2009nEvtMixing) + 0.5, "N mixed partner events"}; + const AxisSpec axisCollPerDF = {200, 0.0, 20000.0, "Derived collisions per dataframe"}; + const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; + const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; + const AxisSpec axisMixCentralityQA = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixDeltaCentralityQA = {204, -102.0f, 102.0f, "#Delta Centrality (%)"}; - mixingOperationQA.add("ME/Mixing/MixingEstimator1Vs2", "Configured mixing estimator;Estimator_{1} (%);Estimator_{2} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); - mixingOperationQA.add("ME/Mixing/DeltaMixingEstimatorVsChannel", "Configured mixing estimator difference;Mixing channel;Estimator_{1}-Estimator_{2} (%)", HistType::kTH2F, {axisMixOperationChannel, axisMixDeltaCentralityQA}); + mixingOperationQA.add("ME/Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingOperationQA.add("ME/Mixing/CollisionsPerDF", "Derived collisions per (merged) dataframe;N collisions;Dataframes", HistType::kTH1D, {axisCollPerDF}); + mixingOperationQA.add("ME/Mixing/CollisionsPerBinChannel", "Collisions in mixing pools;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerChannel", "Mixed collision pairs;Mixing channel;Mixed collision pairs", HistType::kTH1D, {axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerBinChannel", "Mixed collision pairs;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEvent", "Mixed partners per trigger event;Mixing channel;N partner events", HistType::kTH2D, {axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEventVsBin", "Mixed partners per trigger event;Mixing bin;Mixing channel;N partner events", HistType::kTH3D, {axisMixBin, axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/VtxZ1VsVtxZ2", "Mixed-event vertex positions;z_{vtx}^{trig} (cm);z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixVtxZQA, axisMixVtxZQA}); + mixingOperationQA.add("ME/Mixing/DeltaVtxZVsChannel", "Mixed-event #Delta z_{vtx};Mixing channel;z_{vtx}^{trig}-z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixChannel, axisMixDeltaVtxZQA}); + mixingOperationQA.add("ME/Mixing/Centrality1VsCentrality2", "Mixing estimator of mixed events;Estimator^{trig} (%);Estimator^{assoc} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentralityVsChannel", "Mixing-estimator difference;Mixing channel;Estimator^{trig}-Estimator^{assoc} (%)", HistType::kTH2F, {axisMixChannel, axisMixDeltaCentralityQA}); auto setMixOperationChannelLabels = [](TAxis* axis) { for (int iChannel = 0; iChannel < NMixOperationChannels; ++iChannel) { axis->SetBinLabel(iChannel + 1, MixOperationChannelName[iChannel].data()); } }; + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionsPerBinChannel"))->GetYaxis()); setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerChannel"))->GetXaxis()); setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerBinChannel"))->GetYaxis()); setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEvent"))->GetXaxis()); setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"))->GetYaxis()); setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaVtxZVsChannel"))->GetXaxis()); - setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0CVsChannel"))->GetXaxis()); - setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0MVsChannel"))->GetXaxis()); - setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFT0AVsChannel"))->GetXaxis()); - setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentFV0AVsChannel"))->GetXaxis()); - setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaMixingEstimatorVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentralityVsChannel"))->GetXaxis()); + + if (cfgDebug.c2001printDebugMessages) { + LOGF(info, "Finishing init"); + } } - using BinningTypeVtxZFT0C = ColumnBinningPolicy; - using BinningTypeVtxZFT0M = ColumnBinningPolicy; - using BinningTypeVtxZFT0A = ColumnBinningPolicy; - using BinningTypeVtxZFV0A = ColumnBinningPolicy; + SliceCache cache; + Preslice triggersPerCollPreslice = aod::hpcorrtrigger::hpCorrCollId; + Preslice assocsPerCollPreslice = aod::hpcorrassoc::hpCorrCollId; // registers HPCORRASSOC/fIndexHPCorrColls in the SliceCache - BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; - BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; - BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; - BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}}; + //______________________________________________________________________________ + // Associate partitions: one per mixing channel. Every stored particle carries UsedMask = all correlations + // h-h, h-pi, h-K, h-p + Partition assocHHLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHLowPt)); + Partition assocHHHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHHighPt)); + Partition assocHPiLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiLowPt)); + Partition assocHPiHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiHighPt)); + Partition assocHKaLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaLowPt)); + Partition assocHKaHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaHighPt)); + Partition assocHPrLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrLowPt)); + Partition assocHPrHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition assocHPhiLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtPeak)); + Partition assocHPhiLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtLSB)); + Partition assocHPhiLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtRSB)); + Partition assocHPhiHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtPeak)); + Partition assocHPhiHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtLSB)); + Partition assocHPhiHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition assocHKStarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtPeak)); + Partition assocHKStarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtLSB)); + Partition assocHKStarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtRSB)); + Partition assocHKStarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtPeak)); + Partition assocHKStarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtLSB)); + Partition assocHKStarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition assocHKStarBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition assocHKStarBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition assocHKStarBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition assocHKStarBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition assocHKStarBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition assocHKStarBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition assocHLambdaLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition assocHLambdaLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition assocHLambdaLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition assocHLambdaHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition assocHLambdaHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition assocHLambdaHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition assocHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition assocHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition assocHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition assocHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition assocHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition assocHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtRSB)); - template - int getDerivedMixingBin(const C& collision) + //______________________________________________________________________________ + // Collision pools: one per mixing channel. A collision is in the pool of a channel if its CorrMask has the + // channel bit, i.e. it had at least one trigger-associate pair of this channel in the same event. + // h-h, h-pi, h-K, h-p + Partition collHHLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHLowPt)); + Partition collHHHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHHighPt)); + Partition collHPiLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiLowPt)); + Partition collHPiHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiHighPt)); + Partition collHKaLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaLowPt)); + Partition collHKaHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaHighPt)); + Partition collHPrLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrLowPt)); + Partition collHPrHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition collHPhiLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtPeak)); + Partition collHPhiLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtLSB)); + Partition collHPhiLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtRSB)); + Partition collHPhiHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtPeak)); + Partition collHPhiHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtLSB)); + Partition collHPhiHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition collHKStarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtPeak)); + Partition collHKStarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtLSB)); + Partition collHKStarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtRSB)); + Partition collHKStarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtPeak)); + Partition collHKStarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtLSB)); + Partition collHKStarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition collHKStarBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition collHKStarBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition collHKStarBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition collHKStarBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition collHKStarBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition collHKStarBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition collHLambdaLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition collHLambdaLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition collHLambdaLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition collHLambdaHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition collHLambdaHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition collHLambdaHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition collHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition collHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition collHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition collHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition collHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition collHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtRSB)); + + // Number of mixed partners of every collision as trigger event (QA only, re-used by all channels) + std::vector nPartnersPerColl; + + //______________________________________________________________________________ + // Channel bookkeeping: PairTypeEnum (+ PidEnum for identified hadrons) -> CorrCountTypeEnum, + // which gives the CorrPresenceMask bit and the mixing-QA channel number + template + static constexpr int getCorrType() { - switch (cfgMixing.c2009mixingEstimator) { - case kMixCentFT0C: - return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); - case kMixCentFT0M: - return colBinningFT0M.getBin({collision.posZ(), collision.centFT0M()}); - case kMixCentFT0A: - return colBinningFT0A.getBin({collision.posZ(), collision.centFT0A()}); - case kMixCentFV0A: - return colBinningFV0A.getBin({collision.posZ(), collision.centFV0A()}); - default: - return -1; + if constexpr (pairType == kHH) { + return kCountHH; + } else if constexpr (pairType == kHId) { + return pidType == kPi ? kCountHPi : (pidType == kKa ? kCountHKa : kCountHPr); + } else if constexpr (pairType == kHPhi) { + return kCountHPhi; + } else if constexpr (pairType == kHKStar) { + return kCountHKStar; + } else if constexpr (pairType == kHKStarBar) { + return kCountHKStarBar; + } else if constexpr (pairType == kHLambda) { + return kCountHLambda; + } else { + return kCountHLambdaBar; } } - template - float getDerivedMixingEstimatorValue(const C& collision) + //______________________________________________________________________________ + // Histogram filling for one mixed trigger-associate pair + template + void fillMixedPair(H& histReg, const T& trigger, const A& associate, const float centrality) { - switch (cfgMixing.c2009mixingEstimator) { - case kMixCentFT0C: - return collision.centFT0C(); - case kMixCentFT0M: - return collision.centFT0M(); - case kMixCentFT0A: - return collision.centFT0A(); - case kMixCentFV0A: - return collision.centFV0A(); - default: - return -1.0f; + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + if constexpr (pairType == kHH) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else if constexpr (pairType == kHId) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); } } - //_______________________________ Particle Identification _______________________________ - // p-dependent identification - // p < ThrPforTOF : TPC+TOF (circular cut) if TOF is reliable, otherwise TPC only - // p >= ThrPforTOF : TPC+TOF (circular cut), TOF is required - // Default cuts are 3 sigma (see DefaultPIDcheckValues). - - // If vetoIdOthers = true; it passed all veto checks - // If vetoIdOthers = false; it failed veto check with some other particle - template - bool vetoIdOthersTPC(const T& track) - { - // Static is only run once, ever. - static const std::array doVetoTPC = { - getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kDoVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kDoVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kDoVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kDoVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kDoVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kDoVetoTPC)}; - - static const std::array vetoTPC = { - getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kVetoTPC), - getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kVetoTPC)}; - - return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); - } - - template - bool vetoIdOthersTOF(const T& track) - { - // Only computed once - static const std::array doVetoTOF = { - getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kDoVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kDoVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kDoVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kDoVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kDoVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kDoVetoTOF)}; - - static const std::array vetoTOF = { - getCfg(cfgIdCut.c2059pidVetoSetting, kPi, kVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kKa, kVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kPr, kVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kEl, kVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kMu, kVetoTOF), - getCfg(cfgIdCut.c2059pidVetoSetting, kDe, kVetoTOF)}; - - return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); - } - - template - bool vetoIdOthersTPCTOF(const T& track) - { - // If either veto fails, reject - return vetoIdOthersTPC(track) && vetoIdOthersTOF(track); - } - - // Check if TOF is reliable - template - inline bool checkReliableTOF(const T& track) - { - // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: - if (cfgIdCut.c2060cfgId07CheckTofBeta) { - return (track.hasTOF() && track.beta() > 0.0f); - } - return track.hasTOF(); - } - - template - bool idTPC(const T& track, const float& nSigmaTPC, float& nSigmaIdDistSq) - { - if constexpr (pidMode == kPi) { - nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi(); - } else if constexpr (pidMode == kKa) { - nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa(); - } else if constexpr (pidMode == kPr) { - nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr(); - } else if constexpr (pidMode == kEl) { - nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl(); - } else if constexpr (pidMode == kMu) { - nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu(); - } else if constexpr (pidMode == kDe) { - nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe(); - } else { - nSigmaIdDistSq = 1000000; - } - - static const bool doVetoOthers = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoVetoOthers); - if (doVetoOthers) { - if (!vetoIdOthersTPC(track)) { - // If vetoIdOthers = true; it passed all veto checks - // If vetoIdOthers = false; it failed veto check with some other particle - return false; - } - } - - static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTPCcheck); - if (doRelativeTPCcheck) { - if (!relativeIdOthersTPC(track)) { - // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others - // If relativeIdOthersTPC = false; some particle has stronger nSigma compared to it - return false; - } - } - - if constexpr (pidMode == kPi) { - return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC; - } else if constexpr (pidMode == kKa) { - return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC; - } else if constexpr (pidMode == kPr) { - return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC; - } else if constexpr (pidMode == kEl) { - return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC; - } else if constexpr (pidMode == kMu) { - return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC; - } else if constexpr (pidMode == kDe) { - return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC; - } else { - return false; - } - } - - template - bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad, float& nSigmaIdDistSq) - { - if constexpr (pidMode == kPi) { - nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); - } else if constexpr (pidMode == kKa) { - nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); - } else if constexpr (pidMode == kPr) { - nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); - } else if constexpr (pidMode == kEl) { - nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); - } else if constexpr (pidMode == kMu) { - nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); - } else if constexpr (pidMode == kDe) { - nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); - } else { - nSigmaIdDistSq = 1000000; - } - static const bool doVetoOthers = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoVetoOthers); - if (doVetoOthers) { - if (!vetoIdOthersTPCTOF(track)) { - // If vetoIdOthers = true; it passed all veto checks - // If vetoIdOthers = false; it failed veto check with some other particle - return false; - } - } - - static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTOFcheck); - if (doRelativeTOFcheck) { - if (!relativeIdOthersTOF(track)) { - // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others - // If relativeIdOthersTOF = false; some particle has stronger nSigma compared to it - return false; - } - } - - static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); - if (doRelativeTPCTOFcheck) { - if (!relativeIdOthersTPCTOF(track)) { - // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others - // If relativeIdOthersTPCTOF = false; some particle has stronger nSigma compared to it - return false; - } - } - - switch (pidCutType) { - case kCircularCut: - return selIdCircularCut(track, nSigmaSquaredRad); - case kRectangularCut: - return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); - case kEllipsoidalCut: - return selIdEllipsoidalCut(track, nSigmaTPC, nSigmaTOF); - default: - return false; - } - } - - template - bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - // Static cache inside function - initialized once on first call - static const auto thrPforTOF = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kThrPforTOF); - static const auto idCutTypeLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kIdCutTypeLowP); - static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTPCLowP); - static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTOFLowP); - static const auto nSigmaRadLowP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaRadLowP); - static const auto idCutTypeHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kIdCutTypeHighP); - static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTPCHighP); - static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaTOFHighP); - static const auto nSigmaRadHighP = getCfg(cfgIdCut.c2058pidConfigSetting, pidMode, kNSigmaRadHighP); - - if (track.p() < thrPforTOF) { - if (checkReliableTOF(track)) { - if (idTPCTOF(track, idCutTypeLowP, nSigmaTPCLowP, nSigmaTOFLowP, nSigmaRadLowP, nSigmaIdDistSq)) { - IdMethod = kTPCTOFidentified; - return true; - } - } else { - if (idTPC(track, nSigmaTPCLowP, nSigmaIdDistSq)) { - IdMethod = kTPCidentified; - return true; - } - } - } else { - if (checkReliableTOF(track)) { - if (idTPCTOF(track, idCutTypeHighP, nSigmaTPCHighP, nSigmaTOFHighP, nSigmaRadHighP, nSigmaIdDistSq)) { - IdMethod = kTPCTOFidentified; - return true; - } - } - } - return false; - } - - //______________________________Identification Functions________________________________________________________________ - // Pion - template - bool selPion(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Kaon - template - bool selKaon(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Proton - template - bool selProton(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Electron - template - bool selElectron(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Muon - template - bool selMuon(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Deuteron - template - bool selDeuteron(const T& track, int& IdMethod, float& nSigmaIdDistSq) - { - return selPdependent(track, IdMethod, nSigmaIdDistSq); - } - - // Two-argument versions - template - bool selPion(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selPion(track, IdMethod, nSigmaIdDistSq); - } - - template - bool selKaon(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selKaon(track, IdMethod, nSigmaIdDistSq); - } - - template - bool selProton(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selProton(track, IdMethod, nSigmaIdDistSq); - } - - template - bool selElectron(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selElectron(track, IdMethod, nSigmaIdDistSq); - } - - template - bool selMuon(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selMuon(track, IdMethod, nSigmaIdDistSq); - } - - template - bool selDeuteron(const T& track, int& IdMethod) - { - float nSigmaIdDistSq = -1.0f; - return selDeuteron(track, IdMethod, nSigmaIdDistSq); - } - // - - template - float computeDerivedRapidity(const T& particle, const float mass) - { - const float pz = particle.pt() * std::sinh(particle.eta()); - const float p = particle.pt() * std::cosh(particle.eta()); - const float energy = std::sqrt(p * p + mass * mass); - return 0.5f * std::log((energy + pz) / (energy - pz)); - } - - template - void fillCorrParticleQA(H& histReg, const T& track) - { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("P"), track.p()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), track.pt()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), track.eta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), track.phi()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxy"), track.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAz"), track.dcaZ()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), track.sign()); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); - } - - template - void fillIdentifiedCorrelation(H& histReg, const T& trigger, const A& associate, const int idMethod) - { - const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); - const float dEta = associate.eta() - trigger.eta(); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("P"), associate.p()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); - - if constexpr (pidType == kPi) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassPiPlus)); - } else if constexpr (pidType == kKa) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassKPlus)); - } else if constexpr (pidType == kPr) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeDerivedRapidity(associate, MassProton)); - } - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxy"), associate.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAz"), associate.dcaZ()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxyVsPt"), associate.pt(), associate.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAzVsPt"), associate.pt(), associate.dcaZ()); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCSignalVsP"), associate.p(), associate.tpcSignal()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFBetaVsP"), associate.p(), associate.beta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("IdMethodVsP"), associate.p(), idMethod); - - if constexpr (pidType == kPi) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPi()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPi()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPi(), associate.tofNSigmaPi()); - } else if constexpr (pidType == kKa) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaKa()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaKa()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaKa(), associate.tofNSigmaKa()); - } else if constexpr (pidType == kPr) { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPr()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPr()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPr(), associate.tofNSigmaPr()); - } - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); - } - - template - void fillResoAssocQA(H& histReg, const A& associate, const float invMass) - { - const float p = associate.pt() * std::cosh(associate.eta()); - const float rapidity = computeDerivedRapidity(associate, invMass); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("P"), p); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Pt"), associate.pt()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Eta"), associate.eta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Phi"), associate.phi()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Rapidity"), rapidity); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMass"), invMass); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMassVsPt"), associate.pt(), invMass); - } - - template - void fillResoDauQA(H& histReg, const T& track, const int idMethod) - { - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("P"), track.p()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Pt"), track.pt()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Eta"), track.eta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Phi"), track.phi()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxy"), track.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAz"), track.dcaZ()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Sign"), track.sign()); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("IdMethodVsP"), track.p(), idMethod); - - float tpcNSigma = 0.0f; - float tofNSigma = 0.0f; - float massHypothesis = 0.0f; - - if constexpr (pairType == kHPhi || (pairType == kHKStar && dauType == kPosDau) || (pairType == kHKStarBar && dauType == kNegDau) || - (pairType == kHLambda && dauType == kNegDau) || (pairType == kHLambdaBar && dauType == kPosDau)) { - tpcNSigma = track.tpcNSigmaKa(); - tofNSigma = track.tofNSigmaKa(); - massHypothesis = MassKPlus; - } else if constexpr ((pairType == kHKStar && dauType == kNegDau) || (pairType == kHKStarBar && dauType == kPosDau)) { - tpcNSigma = track.tpcNSigmaPi(); - tofNSigma = track.tofNSigmaPi(); - massHypothesis = MassPiPlus; - } else if constexpr ((pairType == kHLambda && dauType == kPosDau) || (pairType == kHLambdaBar && dauType == kNegDau)) { - tpcNSigma = track.tpcNSigmaPr(); - tofNSigma = track.tofNSigmaPr(); - massHypothesis = MassProton; - } - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Rapidity"), computeDerivedRapidity(track, massHypothesis)); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); - } - - template - void fillTrigAssociateCorrelation(H& histReg, const T& trigger, const A& associate) - { - const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); - const float dEta = associate.eta() - trigger.eta(); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); - } - - template - void fillResoCorrelation(H& histReg, const T& trigger, const A& associate) - { - const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); - const float dEta = associate.eta() - trigger.eta(); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta); - } - - template - void fillResoMassCorrelation(H& histReg, const T& trigger, const A& associate, const float invMass) - { - const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); - const float dEta = associate.eta() - trigger.eta(); - - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi); - histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta); - } - - template - void fillResoRegion(H& histReg, const T& trigger, const A& associate, const P& posDauTrack, const N& negDauTrack, const float invMass, const int posDauIdMethod, const int negDauIdMethod) - { - fillResoAssocQA(histReg, associate, invMass); - fillResoDauQA(histReg, posDauTrack, posDauIdMethod); - fillResoDauQA(histReg, negDauTrack, negDauIdMethod); - fillResoCorrelation(histReg, trigger, associate); - fillResoMassCorrelation(histReg, trigger, associate, invMass); - } - - template - bool selectResonanceDaughters(const P& posDauTrack, const N& negDauTrack, int& posDauIdMethod, int& negDauIdMethod) - { - posDauIdMethod = kUnidentified; - negDauIdMethod = kUnidentified; - if constexpr (pairType == kHPhi) { - return selKaon(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); - } else if constexpr (pairType == kHKStar) { - return selKaon(posDauTrack, posDauIdMethod) && selPion(negDauTrack, negDauIdMethod); - } else if constexpr (pairType == kHKStarBar) { - return selPion(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); - } else if constexpr (pairType == kHLambda) { - return selProton(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); - } else if constexpr (pairType == kHLambdaBar) { - return selKaon(posDauTrack, posDauIdMethod) && selProton(negDauTrack, negDauIdMethod); - } - return false; - } - - template - uint8_t getResonanceMassRegion(const A& associate, float& invMass) + template + void fillMixingPairQA(const int mixChannel, const int mixBin, const C& collision1, const C& collision2) { - if constexpr (pairType == kHPhi) { - invMass = associate.mPhi1020(); - return getMassRegionTag(invMass, cfgPhi1020CorrMass.c2042phi1020LSBLow, cfgPhi1020CorrMass.c2043phi1020LSBUp, cfgPhi1020CorrMass.c2040phi1020PeakLow, cfgPhi1020CorrMass.c2041phi1020PeakUp, cfgPhi1020CorrMass.c2044phi1020RSBLow, cfgPhi1020CorrMass.c2045phi1020RSBUp); - } else if constexpr (pairType == kHKStar) { - invMass = associate.mKStar892(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.c2048kStar892LSBLow, cfgKStar892CorrMass.c2049kStar892LSBUp, cfgKStar892CorrMass.c2046kStar892PeakLow, cfgKStar892CorrMass.c2047kStar892PeakUp, cfgKStar892CorrMass.c2050kStar892RSBLow, cfgKStar892CorrMass.c2051kStar892RSBUp); - } else if constexpr (pairType == kHKStarBar) { - invMass = associate.mKStar892Bar(); - return getMassRegionTag(invMass, cfgKStar892CorrMass.c2048kStar892LSBLow, cfgKStar892CorrMass.c2049kStar892LSBUp, cfgKStar892CorrMass.c2046kStar892PeakLow, cfgKStar892CorrMass.c2047kStar892PeakUp, cfgKStar892CorrMass.c2050kStar892RSBLow, cfgKStar892CorrMass.c2051kStar892RSBUp); - } else if constexpr (pairType == kHLambda) { - invMass = associate.mLambda1520(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.c2054lambda1520LSBLow, cfgLambda1520CorrMass.c2055lambda1520LSBUp, cfgLambda1520CorrMass.c2052lambda1520PeakLow, cfgLambda1520CorrMass.c2053lambda1520PeakUp, cfgLambda1520CorrMass.c2056lambda1520RSBLow, cfgLambda1520CorrMass.c2057lambda1520RSBUp); - } else if constexpr (pairType == kHLambdaBar) { - invMass = associate.mLambda1520Bar(); - return getMassRegionTag(invMass, cfgLambda1520CorrMass.c2054lambda1520LSBLow, cfgLambda1520CorrMass.c2055lambda1520LSBUp, cfgLambda1520CorrMass.c2052lambda1520PeakLow, cfgLambda1520CorrMass.c2053lambda1520PeakUp, cfgLambda1520CorrMass.c2056lambda1520RSBLow, cfgLambda1520CorrMass.c2057lambda1520RSBUp); - } - - invMass = -1.0f; - return kMassOutside; + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerChannel"), mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerBinChannel"), mixBin, mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/VtxZ1VsVtxZ2"), collision1.posZ(), collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaVtxZVsChannel"), mixChannel, collision1.posZ() - collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/Centrality1VsCentrality2"), collision1.centrality(), collision2.centrality()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentralityVsChannel"), mixChannel, collision1.centrality() - collision2.centrality()); } - template - constexpr uint64_t getDerivedResoRegionMask() + //______________________________________________________________________________ + // Mix one channel with selfCombinations. + // collisionPool : collisions whose CorrMask has the channel bit (partition on HPCorrColls) + // assocPartition : associates whose UsedMask has the channel bit (partition on HPCorrAssocs) + // selfCombinations groups the pool by mixing bin (z-vertex x centrality) and pairs every collision with the + // next c2009nEvtMixing collisions of the same bin. Each pair comes once, collision1 before collision2: + // the triggers are taken from collision1 and the associates from collision2. + template + void mixChannel(H& histReg, P& collisionPool, A& assocPartition, aod::HPCorrTriggers const& triggers, const int64_t nCollisions) { constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; - - if constexpr (pairType == kHPhi) { - return getResoRegionMaskBit(kResoCountHPhi, PtType, massRegion); - } else if constexpr (pairType == kHKStar) { - return getResoRegionMaskBit(kResoCountHKStar, PtType, massRegion); - } else if constexpr (pairType == kHKStarBar) { - return getResoRegionMaskBit(kResoCountHKStarBar, PtType, massRegion); - } else if constexpr (pairType == kHLambda) { - return getResoRegionMaskBit(kResoCountHLambda, PtType, massRegion); - } else if constexpr (pairType == kHLambdaBar) { - return getResoRegionMaskBit(kResoCountHLambdaBar, PtType, massRegion); - } - - return 0ULL; - } - - template - bool derivedChannelEnabled(uint64_t mask) - { - constexpr bool LowPt = roleType == kAssocLowPt; - if constexpr (pairType == kHH) { - return mask & (LowPt ? kMaskHHLowPt : kMaskHHHighPt); - } - if constexpr (pairType == kHId && pidType == kPi) { - return mask & (LowPt ? kMaskHPiLowPt : kMaskHPiHighPt); - } - if constexpr (pairType == kHId && pidType == kKa) { - return mask & (LowPt ? kMaskHKaLowPt : kMaskHKaHighPt); - } - if constexpr (pairType == kHId && pidType == kPr) { - return mask & (LowPt ? kMaskHPrLowPt : kMaskHPrHighPt); - } - if constexpr (pairType == kHPhi) { - return mask & (LowPt ? kMaskHPhiLowPt : kMaskHPhiHighPt); - } - if constexpr (pairType == kHKStar) { - return mask & (LowPt ? kMaskHKStarLowPt : kMaskHKStarHighPt); - } - if constexpr (pairType == kHKStarBar) { - return mask & (LowPt ? kMaskHKStarBarLowPt : kMaskHKStarBarHighPt); - } - if constexpr (pairType == kHLambda) { - return mask & (LowPt ? kMaskHLambdaLowPt : kMaskHLambdaHighPt); - } - if constexpr (pairType == kHLambdaBar) { - return mask & (LowPt ? kMaskHLambdaBarLowPt : kMaskHLambdaBarHighPt); - } - return false; - } - - template - bool derivedPairEnabled(uint64_t mask) - { - if constexpr (pairType == kHH) { - return mask & (kMaskHHLowPt | kMaskHHHighPt); - } - if constexpr (pairType == kHId) { - return mask & (kMaskHPiLowPt | kMaskHPiHighPt | kMaskHKaLowPt | kMaskHKaHighPt | kMaskHPrLowPt | kMaskHPrHighPt); - } - if constexpr (pairType == kHPhi) { - return mask & (kMaskHPhiLowPt | kMaskHPhiHighPt); - } - if constexpr (pairType == kHKStar) { - return mask & (kMaskHKStarLowPt | kMaskHKStarHighPt); - } - if constexpr (pairType == kHKStarBar) { - return mask & (kMaskHKStarBarLowPt | kMaskHKStarBarHighPt); - } - if constexpr (pairType == kHLambda) { - return mask & (kMaskHLambdaLowPt | kMaskHLambdaHighPt); - } - if constexpr (pairType == kHLambdaBar) { - return mask & (kMaskHLambdaBarLowPt | kMaskHLambdaBarHighPt); - } - return false; - } - - template - void executeDerivedTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, uint64_t commonCorrMask, uint8_t requiredMassRegion = kMassNone) - { - for (const auto& associate : associates) { - - if constexpr (pairType == kHH) { - if (!derivedChannelEnabled(commonCorrMask)) { - continue; - } - fillCorrParticleQA(histReg, associate); - fillTrigAssociateCorrelation(histReg, trigger, associate); - } - - if constexpr (pairType == kHId) { - int pionIdMethod = kUnidentified; - int kaonIdMethod = kUnidentified; - int protonIdMethod = kUnidentified; - if (derivedChannelEnabled(commonCorrMask) && selPion(associate, pionIdMethod)) { - fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); - } - if (derivedChannelEnabled(commonCorrMask) && selKaon(associate, kaonIdMethod)) { - fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); - } - if (derivedChannelEnabled(commonCorrMask) && selProton(associate, protonIdMethod)) { - fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); - } + constexpr int CorrType = getCorrType(); + constexpr bool IsReso = CorrType >= kCountHPhi; + constexpr int MixChannel = IsReso ? NTrackMixChannels + (CorrType - kCountHPhi) * kNCorrCountPt * NMassWindows + PtType * NMassWindows + (massRegion - kMassPeak) + : CorrType * kNCorrCountPt + PtType; + static_assert(MixChannel >= 0 && MixChannel < NMixOperationChannels, "Invalid mixing channel"); + + nPartnersPerColl.assign(nCollisions, 0); + + for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.c2009nEvtMixing, -1, collisionPool, collisionPool)) { + const int mixBin1 = colBinning.getBin({collision1.posZ(), collision1.centrality()}); + const int mixBin2 = colBinning.getBin({collision2.posZ(), collision2.centrality()}); + if (mixBin1 != mixBin2) { + LOG(fatal) << "EVENT MIXING ERROR :: collisions from different mixing bins :: " << mixBin1 << " :: " << mixBin2; } - if constexpr (pairType == kHPhi || pairType == kHKStar || pairType == kHKStarBar || pairType == kHLambda || pairType == kHLambdaBar) { - if (requiredMassRegion == kMassNone && !derivedChannelEnabled(commonCorrMask)) { - continue; - } - if (requiredMassRegion != kMassNone && commonCorrMask == 0ULL) { - continue; - } - - const auto& posDauTrack = associate.template posTrack_as(); - const auto& negDauTrack = associate.template negTrack_as(); - - int posDauIdMethod = kUnidentified; - int negDauIdMethod = kUnidentified; - if (!selectResonanceDaughters(posDauTrack, negDauTrack, posDauIdMethod, negDauIdMethod)) { - continue; - } + nPartnersPerColl[collision1.globalIndex()]++; + fillMixingPairQA(MixChannel, mixBin1, collision1, collision2); - float invMass = -1.0f; - const uint8_t massRegion = getResonanceMassRegion(associate, invMass); - if (massRegion == kMassOutside) { - continue; - } - const float rapidity = computeDerivedRapidity(associate, invMass); - if (rapidity < cfgResPartitions.c2018resoRapidityMin || rapidity > cfgResPartitions.c2019resoRapidityMax) { - continue; - } - if (requiredMassRegion != kMassNone && massRegion != requiredMassRegion) { - continue; - } + const auto triggers1 = triggers.sliceBy(triggersPerCollPreslice, collision1.globalIndex()); + const auto associates2 = assocPartition->sliceByCached(aod::hpcorrassoc::hpCorrCollId, collision2.globalIndex(), cache); - if (massRegion == kMassPeak) { - fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); - } else if (massRegion == kMassLSB) { - fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); - } else if (massRegion == kMassRSB) { - fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + for (const auto& trigger : triggers1) { + for (const auto& associate : associates2) { + fillMixedPair(histReg, trigger, associate, collision1.centrality()); } } } - } - - template - void executeDerivedCorrelation(H& histReg, const T& triggers, const A& associatesLowPt, const B& associatesHighPt, uint64_t commonCorrMask) - { - if (!derivedPairEnabled(commonCorrMask)) { - return; - } - - for (const auto& trigger : triggers) { - fillCorrParticleQA(histReg, trigger); - executeDerivedTrigAssocCorrelation(histReg, trigger, associatesLowPt, commonCorrMask); - executeDerivedTrigAssocCorrelation(histReg, trigger, associatesHighPt, commonCorrMask); - } - } - template - void executeDerivedCorrelationRole(H& histReg, const T& triggers, const A& associates, uint8_t requiredMassRegion = kMassNone) - { - for (const auto& trigger : triggers) { - fillCorrParticleQA(histReg, trigger); - executeDerivedTrigAssocCorrelation(histReg, trigger, associates, requiredMask, requiredMassRegion); + // Pool QA: every collision of the pool and the number of partners it was mixed with as trigger event + for (const auto& collision : collisionPool) { + const int mixBin = colBinning.getBin({collision.posZ(), collision.centrality()}); + if (mixBin < 0) { + continue; + } + const int nPartners = nPartnersPerColl[collision.globalIndex()]; + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerBinChannel"), mixBin, MixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEvent"), MixChannel, nPartners); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"), mixBin, MixChannel, nPartners); } } - Preslice drTracksPerCollisionPreslice = aod::hpcorrtrack::hpCorrCollId; - Preslice drResoPerCollisionPreslice = aod::hpcorrresonance::hpCorrCollId; - - // definition of partitions - SliceCache cache; - Partition drMixHHLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHLowPt)) != static_cast(0ULL); - Partition drMixHHHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHHHighPt)) != static_cast(0ULL); - - Partition drMixHPiLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiLowPt)) != static_cast(0ULL); - Partition drMixHPiHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPiHighPt)) != static_cast(0ULL); - Partition drMixHKaLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaLowPt)) != static_cast(0ULL); - Partition drMixHKaHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKaHighPt)) != static_cast(0ULL); - Partition drMixHPrLowPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrLowPt)) != static_cast(0ULL); - Partition drMixHPrHighPt = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPrHighPt)) != static_cast(0ULL); - - Partition drMixHPhiLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtPeak)) != static_cast(0ULL); - Partition drMixHPhiLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtLSB)) != static_cast(0ULL); - Partition drMixHPhiLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiLowPtRSB)) != static_cast(0ULL); - - Partition drMixHPhiHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtPeak)) != static_cast(0ULL); - Partition drMixHPhiHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtLSB)) != static_cast(0ULL); - Partition drMixHPhiHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHPhiHighPtRSB)) != static_cast(0ULL); - - Partition drMixHKStarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtPeak)) != static_cast(0ULL); - Partition drMixHKStarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtLSB)) != static_cast(0ULL); - Partition drMixHKStarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarLowPtRSB)) != static_cast(0ULL); - - Partition drMixHKStarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtPeak)) != static_cast(0ULL); - Partition drMixHKStarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtLSB)) != static_cast(0ULL); - Partition drMixHKStarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarHighPtRSB)) != static_cast(0ULL); - - Partition drMixHKStarBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtPeak)) != static_cast(0ULL); - Partition drMixHKStarBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtLSB)) != static_cast(0ULL); - Partition drMixHKStarBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarLowPtRSB)) != static_cast(0ULL); - - Partition drMixHKStarBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtPeak)) != static_cast(0ULL); - Partition drMixHKStarBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtLSB)) != static_cast(0ULL); - Partition drMixHKStarBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHKStarBarHighPtRSB)) != static_cast(0ULL); - - Partition drMixHLambdaLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaLowPtRSB)) != static_cast(0ULL); - - Partition drMixHLambdaHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaHighPtRSB)) != static_cast(0ULL); - - Partition drMixHLambdaBarLowPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaBarLowPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaBarLowPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarLowPtRSB)) != static_cast(0ULL); - - Partition drMixHLambdaBarHighPtPeak = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtPeak)) != static_cast(0ULL); - Partition drMixHLambdaBarHighPtLSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtLSB)) != static_cast(0ULL); - Partition drMixHLambdaBarHighPtRSB = (aod::hpcorrcollision::corrMask & static_cast(kMaskHLambdaBarHighPtRSB)) != static_cast(0ULL); - - Partition drTriggerTracks = cfgPartitions.c2012triggerPtLow < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2013triggerPtHigh; - Partition drPosTracks = aod::hpcorrtrack::sign > static_cast(0); - Partition drNegTracks = aod::hpcorrtrack::sign < static_cast(0); - Partition drAssocTracksLowPt = cfgPartitions.c2014assocPtLowMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2015assocPtLowMax; - Partition drAssocTracksHighPt = cfgPartitions.c2016assocPtHighMin < aod::hpcorrtrack::pt && aod::hpcorrtrack::pt < cfgPartitions.c2017assocPtHighMax; - - Partition drAssocPhiLowPt = cfgResPartitions.c2020phiPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2021phiPtLowMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); - Partition drAssocPhiHighPt = cfgResPartitions.c2022phiPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2023phiPtHighMax && aod::hpcorrresonance::phi1020Tag != static_cast(kMassOutside); - Partition drAssocKStarLowPt = cfgResPartitions.c2024kstarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2025kstarPtLowMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); - Partition drAssocKStarHighPt = cfgResPartitions.c2026kstarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2027kstarPtHighMax && aod::hpcorrresonance::kStar892Tag != static_cast(kMassOutside); - Partition drAssocKStarBarLowPt = cfgResPartitions.c2028kstarBarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2029kstarBarPtLowMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); - Partition drAssocKStarBarHighPt = cfgResPartitions.c2030kstarBarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2031kstarBarPtHighMax && aod::hpcorrresonance::kStar892BarTag != static_cast(kMassOutside); - Partition drAssocLambda1520LowPt = cfgResPartitions.c2032lambda1520PtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2033lambda1520PtLowMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); - Partition drAssocLambda1520HighPt = cfgResPartitions.c2034lambda1520PtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2035lambda1520PtHighMax && aod::hpcorrresonance::lambda1520Tag != static_cast(kMassOutside); - Partition drAssocLambda1520BarLowPt = cfgResPartitions.c2036lambda1520BarPtLowMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2037lambda1520BarPtLowMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); - Partition drAssocLambda1520BarHighPt = cfgResPartitions.c2038lambda1520BarPtHighMin < aod::hpcorrresonance::pt && aod::hpcorrresonance::pt < cfgResPartitions.c2039lambda1520BarPtHighMax && aod::hpcorrresonance::lambda1520BarTag != static_cast(kMassOutside); - int dfNumber = 0; void processNothing(aod::Origins const& origins) { if (cfgDebug.c2001printDebugMessages) { LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); } - // Intentionally empty. - // Keeps the task alive when running purely on derived data. + // Intentionally empty. Keeps the task alive when the mixing is not run. } PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processNothing, "Dummy process for analysis", true); - int nColl = 0; - void processMixEventInDeriveData(aod::Origins const& origins, aod::HPCorrColls const& drCollisions, aod::HPCorrTracks const& drFullTracks, aod::HPCorrResonances const& drResonanceCndts /*, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&*/) + void processMixEventInDeriveData(aod::HPCorrColls const& collisions, aod::HPCorrTriggers const& triggers, aod::HPCorrAssocs const& assocs) { dfNumber++; - if (cfgDebug.c2001printDebugMessages) { - LOG(info) << "DEBUG :: df_" << dfNumber << " :: origins = " << origins.size() << " :: drCollisions = " << drCollisions.size() << " :: drFullTracks = " << drFullTracks.size() << " :: drResonanceCndts = " << drResonanceCndts.size(); - } - - int64_t nTrackChecks = 0; - int64_t nBadCollisionIndex = 0; - int64_t nBadTrackCollision = 0; - int64_t nResonanceChecks = 0; - int64_t nBadResonanceCollision = 0; - int64_t nBadResonanceDaughters = 0; - for (const auto& coll : drCollisions) { - auto drTracksPerColl = drFullTracks.sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drResonancesPerColl = drResonanceCndts.sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); - auto drTriggerTracksPerColl = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drPosTracksPerColl = drPosTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drNegTracksPerColl = drNegTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drAssocTracksLowPtPerColl = drAssocTracksLowPt->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drAssocTracksHighPtPerColl = drAssocTracksHighPt->sliceByCached(aod::hpcorrtrack::hpCorrCollId, coll.globalIndex(), cache); - auto drAssocPhiLowPtPerColl = drAssocPhiLowPt->sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); - auto drAssocPhiHighPtPerColl = drAssocPhiHighPt->sliceByCached(aod::hpcorrresonance::hpCorrCollId, coll.globalIndex(), cache); - - if (coll.globalIndex() != coll.globalCollisionId()) { - nBadCollisionIndex++; - LOG(error) << "DEBUG :: nColl = " << nColl << " :: coll.globalIndex() != coll.globalCollisionId() i.e " << coll.globalIndex() << " != " << coll.globalCollisionId(); - } - - for (const auto& track : drTracksPerColl) { - nTrackChecks++; - if (track.hpCorrCollId() != coll.globalIndex()) { - nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: hpCorrCollId mismatch :: " << track.hpCorrCollId() << " != " << coll.globalIndex(); - } - if (track.globalColRefId() != coll.globalCollisionId()) { - nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: globalColRefId mismatch :: " << track.globalColRefId() << " != " << coll.globalCollisionId(); - } - if (track.originalColRefId() != coll.originalCollisionId()) { - nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: originalColRefId mismatch :: " << track.originalColRefId() << " != " << coll.originalCollisionId(); - } - if (track.dataframeID() != coll.dataframeID()) { - nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: dataframeID mismatch :: " << track.dataframeID() << " != " << coll.dataframeID(); - } - if (track.globalTrackId() != track.globalIndex()) { - nBadTrackCollision++; - LOG(error) << "TRACK CHECK :: globalTrackId mismatch :: " << track.globalTrackId() << " != " << track.globalIndex(); - } - } - - for (const auto& resonance : drResonancesPerColl) { - nResonanceChecks++; - - if (resonance.hpCorrCollId() != coll.globalIndex()) { - nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: hpCorrCollId mismatch :: " << resonance.hpCorrCollId() << " != " << coll.globalIndex(); - } - if (resonance.globalColRefId() != coll.globalCollisionId()) { - nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: globalColRefId mismatch :: " << resonance.globalColRefId() << " != " << coll.globalCollisionId(); - } - if (resonance.originalColRefId() != coll.originalCollisionId()) { - nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: originalColRefId mismatch :: " << resonance.originalColRefId() << " != " << coll.originalCollisionId(); - } - if (resonance.dataframeID() != coll.dataframeID()) { - nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: dataframeID mismatch :: " << resonance.dataframeID() << " != " << coll.dataframeID(); - } - if (resonance.globalResonanceId() != resonance.globalIndex()) { - nBadResonanceCollision++; - LOG(error) << "RESONANCE CHECK :: globalResonanceId mismatch :: " << resonance.globalResonanceId() << " != " << resonance.globalIndex(); - } - - const auto& posTrack = resonance.posTrack_as(); - const auto& negTrack = resonance.negTrack_as(); - - if (resonance.posTrackId() != posTrack.globalIndex()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: posTrackId mismatch :: " << resonance.posTrackId() << " != " << posTrack.globalIndex(); - } - if (resonance.negTrackId() != negTrack.globalIndex()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negTrackId mismatch :: " << resonance.negTrackId() << " != " << negTrack.globalIndex(); - } - - if (resonance.globalPosTrackRefId() != posTrack.globalTrackId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: globalPosTrackRefId mismatch :: " << resonance.globalPosTrackRefId() << " != " << posTrack.globalTrackId(); - } - if (resonance.globalNegTrackRefId() != negTrack.globalTrackId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: globalNegTrackRefId mismatch :: " << resonance.globalNegTrackRefId() << " != " << negTrack.globalTrackId(); - } - - if (resonance.originalPosTrackRefId() != posTrack.originalTrackId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: originalPosTrackRefId mismatch :: " << resonance.originalPosTrackRefId() << " != " << posTrack.originalTrackId(); - } - if (resonance.originalNegTrackRefId() != negTrack.originalTrackId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: originalNegTrackRefId mismatch :: " << resonance.originalNegTrackRefId() << " != " << negTrack.originalTrackId(); - } - - if (resonance.dataframeID() != posTrack.dataframeID()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: resonance/posTrack dataframeID mismatch :: " << resonance.dataframeID() << " != " << posTrack.dataframeID(); - } - if (resonance.dataframeID() != negTrack.dataframeID()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: resonance/negTrack dataframeID mismatch :: " << resonance.dataframeID() << " != " << negTrack.dataframeID(); - } - - if (posTrack.hpCorrCollId() != coll.globalIndex()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: positive daughter belongs to wrong collision :: " << posTrack.hpCorrCollId() << " != " << coll.globalIndex(); - } - if (negTrack.hpCorrCollId() != coll.globalIndex()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negative daughter belongs to wrong collision :: " << negTrack.hpCorrCollId() << " != " << coll.globalIndex(); - } - - if (posTrack.originalColRefId() != coll.originalCollisionId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: positive daughter original collision mismatch :: " << posTrack.originalColRefId() << " != " << coll.originalCollisionId(); - } - if (negTrack.originalColRefId() != coll.originalCollisionId()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negative daughter original collision mismatch :: " << negTrack.originalColRefId() << " != " << coll.originalCollisionId(); - } - - if (posTrack.dataframeID() != coll.dataframeID()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: positive daughter dataframe mismatch :: " << posTrack.dataframeID() << " != " << coll.dataframeID(); - } - if (negTrack.dataframeID() != coll.dataframeID()) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negative daughter dataframe mismatch :: " << negTrack.dataframeID() << " != " << coll.dataframeID(); - } - - if (posTrack.sign() <= 0) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: positive daughter has wrong sign :: " << static_cast(posTrack.sign()); - } - if (negTrack.sign() >= 0) { - nBadResonanceDaughters++; - LOG(error) << "DAUGHTER CHECK :: negative daughter has wrong sign :: " << static_cast(negTrack.sign()); - } - } - nColl++; - } // Collision loop is over + const int64_t nCollisions = collisions.size(); + mixingOperationQA.fill(HIST("ME/Mixing/DataFramesProcessed"), 0.0); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerDF"), nCollisions); if (cfgDebug.c2001printDebugMessages) { - LOG(info) << "DEBUG :: SANITY CHECK SUMMARY :: bad collision indices = " << nBadCollisionIndex << " :: tracks checked = " << nTrackChecks << " :: bad track references = " << nBadTrackCollision << " :: resonances checked = " << nResonanceChecks << " :: bad resonance collision references = " << nBadResonanceCollision << " :: bad resonance daughter references = " << nBadResonanceDaughters; - } - if (nBadCollisionIndex == 0 && nBadTrackCollision == 0 && nBadResonanceCollision == 0 && nBadResonanceDaughters == 0) { - if (cfgDebug.c2001printDebugMessages) { - LOG(info) << "DEBUG :: SANITY CHECK PASSED"; - } - } else { - LOG(fatal) << "DEBUG :: SANITY CHECK FAILED"; + LOG(info) << "DEBUG :: df_" << dfNumber << " :: collisions = " << nCollisions << " :: triggers = " << triggers.size() << " :: assocs = " << assocs.size(); } - int64_t nMixedEventPairs = 0; - - auto runRestrictedMixing = [&](auto& colBinning, auto& collisionPool, const int mixChannel, auto&& processPair) { - collisionPool.bindTable(drCollisions); - std::vector partnerCountPerTriggerEvent(drCollisions.size(), -1); - for (const auto& collision : collisionPool) { - partnerCountPerTriggerEvent[collision.globalIndex()] = 0; - } - - for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.c2008nEvtMixing, -1, collisionPool, collisionPool)) { - nMixedEventPairs++; - - if (collision1.dataframeID() == collision2.dataframeID() && collision1.originalCollisionId() == collision2.originalCollisionId()) { - LOG(fatal) << "EVENT MIXING ERROR :: same original collision mixed with itself"; - } - - const int mixingBin1 = getDerivedMixingBin(collision1); - const int mixingBin2 = getDerivedMixingBin(collision2); - - if (mixingBin1 < 0 || mixingBin2 < 0) { - LOG(fatal) << "EVENT MIXING ERROR :: invalid mixing bin :: " << mixingBin1 << " :: " << mixingBin2; - } - - if (mixingBin1 != mixingBin2) { - LOG(fatal) << "EVENT MIXING ERROR :: collisions from different mixing bins :: " << mixingBin1 << " :: " << mixingBin2; - } - - partnerCountPerTriggerEvent[collision1.globalIndex()]++; - - mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerChannel"), mixChannel); - mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerBinChannel"), mixingBin1, mixChannel); - - mixingOperationQA.fill(HIST("ME/Mixing/VtxZ1VsVtxZ2"), collision1.posZ(), collision2.posZ()); - mixingOperationQA.fill(HIST("ME/Mixing/DeltaVtxZVsChannel"), mixChannel, collision1.posZ() - collision2.posZ()); - - mixingOperationQA.fill(HIST("ME/Mixing/CentFT0C1VsCentFT0C2"), collision1.centFT0C(), collision2.centFT0C()); - mixingOperationQA.fill(HIST("ME/Mixing/CentFT0M1VsCentFT0M2"), collision1.centFT0M(), collision2.centFT0M()); - mixingOperationQA.fill(HIST("ME/Mixing/CentFT0A1VsCentFT0A2"), collision1.centFT0A(), collision2.centFT0A()); - mixingOperationQA.fill(HIST("ME/Mixing/CentFV0A1VsCentFV0A2"), collision1.centFV0A(), collision2.centFV0A()); - - mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0CVsChannel"), mixChannel, collision1.centFT0C() - collision2.centFT0C()); - mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0MVsChannel"), mixChannel, collision1.centFT0M() - collision2.centFT0M()); - mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFT0AVsChannel"), mixChannel, collision1.centFT0A() - collision2.centFT0A()); - mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentFV0AVsChannel"), mixChannel, collision1.centFV0A() - collision2.centFV0A()); - - const float mixingEstimator1 = getDerivedMixingEstimatorValue(collision1); - const float mixingEstimator2 = getDerivedMixingEstimatorValue(collision2); - - mixingOperationQA.fill(HIST("ME/Mixing/MixingEstimator1Vs2"), mixingEstimator1, mixingEstimator2); - mixingOperationQA.fill(HIST("ME/Mixing/DeltaMixingEstimatorVsChannel"), mixChannel, mixingEstimator1 - mixingEstimator2); - - processPair(collision1, collision2); - } - - for (const auto& collision : collisionPool) { - const int nPartners = partnerCountPerTriggerEvent[collision.globalIndex()]; - const int mixingBin = getDerivedMixingBin(collision); - - mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEvent"), mixChannel, nPartners); - - if (mixingBin >= 0) { - mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"), mixingBin, mixChannel, nPartners); - } + // Index sanity check: every trigger / associate must point to a collision of this (merged) dataframe + int64_t nBadIndex = 0; + for (const auto& trigger : triggers) { + if (trigger.hpCorrCollId() < 0 || trigger.hpCorrCollId() >= nCollisions) { + ++nBadIndex; } - }; - - auto runTrackChannelMixing = [&](auto& colBinning, auto& collisionPool, auto& assocPartition, auto& histReg) { - constexpr int MixChannel = getMixOperationChannel(requiredMask); - static_assert(MixChannel >= 0, "Invalid track mixing-operation channel"); - - runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { - auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision1.globalIndex(), cache); - auto associates2 = assocPartition->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision2.globalIndex(), cache); - - executeDerivedCorrelationRole(histReg, triggerTracks1, associates2); - }); - }; // NOLINT(readability/braces) - - auto runAllTrackMixing = [&](auto& colBinning) { - runTrackChannelMixing.template operator()(colBinning, drMixHHLowPt, drAssocTracksLowPt, hhCorrelation); - runTrackChannelMixing.template operator()(colBinning, drMixHHHighPt, drAssocTracksHighPt, hhCorrelation); - - runTrackChannelMixing.template operator()(colBinning, drMixHPiLowPt, drAssocTracksLowPt, hIdCorrelation); - runTrackChannelMixing.template operator()(colBinning, drMixHPiHighPt, drAssocTracksHighPt, hIdCorrelation); - - runTrackChannelMixing.template operator()(colBinning, drMixHKaLowPt, drAssocTracksLowPt, hIdCorrelation); - runTrackChannelMixing.template operator()(colBinning, drMixHKaHighPt, drAssocTracksHighPt, hIdCorrelation); - - runTrackChannelMixing.template operator()(colBinning, drMixHPrLowPt, drAssocTracksLowPt, hIdCorrelation); - runTrackChannelMixing.template operator()(colBinning, drMixHPrHighPt, drAssocTracksHighPt, hIdCorrelation); - }; - - auto runResoRegionMixing = [&](auto& colBinning, auto& collisionPool, auto& assocPartition, auto& histReg) { - constexpr int MixChannel = getMixOperationChannel(requiredMask); - static_assert(MixChannel >= 0, "Invalid resonance mixing-operation channel"); - - runRestrictedMixing(colBinning, collisionPool, MixChannel, [&](const auto& collision1, const auto& collision2) { - auto triggerTracks1 = drTriggerTracks->sliceByCached(aod::hpcorrtrack::hpCorrCollId, collision1.globalIndex(), cache); - auto associates2 = assocPartition->sliceByCached(aod::hpcorrresonance::hpCorrCollId, collision2.globalIndex(), cache); - - associates2.bindExternalIndices(&drFullTracks); - - executeDerivedCorrelationRole(histReg, triggerTracks1, associates2, requiredMassRegion); - }); - }; // NOLINT(readability/braces) - - auto runAllResoMixing = [&](auto& colBinning) { - runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtPeak, drAssocPhiLowPt, hPhiCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtLSB, drAssocPhiLowPt, hPhiCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHPhiLowPtRSB, drAssocPhiLowPt, hPhiCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtPeak, drAssocPhiHighPt, hPhiCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtLSB, drAssocPhiHighPt, hPhiCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHPhiHighPtRSB, drAssocPhiHighPt, hPhiCorrelation); - - runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtPeak, drAssocKStarLowPt, hKStarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtLSB, drAssocKStarLowPt, hKStarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarLowPtRSB, drAssocKStarLowPt, hKStarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtPeak, drAssocKStarHighPt, hKStarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtLSB, drAssocKStarHighPt, hKStarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarHighPtRSB, drAssocKStarHighPt, hKStarCorrelation); - - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtPeak, drAssocKStarBarLowPt, hKStarBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtLSB, drAssocKStarBarLowPt, hKStarBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarLowPtRSB, drAssocKStarBarLowPt, hKStarBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtPeak, drAssocKStarBarHighPt, hKStarBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtLSB, drAssocKStarBarHighPt, hKStarBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHKStarBarHighPtRSB, drAssocKStarBarHighPt, hKStarBarCorrelation); - - runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtPeak, drAssocLambda1520LowPt, hLambdaCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtLSB, drAssocLambda1520LowPt, hLambdaCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaLowPtRSB, drAssocLambda1520LowPt, hLambdaCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtPeak, drAssocLambda1520HighPt, hLambdaCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtLSB, drAssocLambda1520HighPt, hLambdaCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaHighPtRSB, drAssocLambda1520HighPt, hLambdaCorrelation); - - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtPeak, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtLSB, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarLowPtRSB, drAssocLambda1520BarLowPt, hLambdaBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtPeak, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtLSB, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); - runResoRegionMixing.template operator()(colBinning, drMixHLambdaBarHighPtRSB, drAssocLambda1520BarHighPt, hLambdaBarCorrelation); - }; - - switch (cfgMixing.c2009mixingEstimator) { - case kMixCentFT0C: - runAllTrackMixing(colBinningFT0C); - runAllResoMixing(colBinningFT0C); - break; - - case kMixCentFT0M: - runAllTrackMixing(colBinningFT0M); - runAllResoMixing(colBinningFT0M); - break; - - case kMixCentFT0A: - runAllTrackMixing(colBinningFT0A); - runAllResoMixing(colBinningFT0A); - break; - - case kMixCentFV0A: - runAllTrackMixing(colBinningFV0A); - runAllResoMixing(colBinningFV0A); - break; - - default: - LOG(fatal) << "DEBUG :: Invalid mixingEstimator = " << static_cast(cfgMixing.c2009mixingEstimator); - break; } - if (cfgDebug.c2001printDebugMessages) { - LOG(info) << "DEBUG :: EVENT MIXING SUMMARY :: mixed collision pairs = " << nMixedEventPairs; + for (const auto& assoc : assocs) { + if (assoc.hpCorrCollId() < 0 || assoc.hpCorrCollId() >= nCollisions) { + ++nBadIndex; + } } + if (nBadIndex > 0) { + LOG(fatal) << "DERIVED DATA ERROR :: df_" << dfNumber << " :: " << nBadIndex << " trigger/associate rows point outside the collision table"; + } + + // h-h + mixChannel(hhCorrelation, collHHLowPt, assocHHLowPt, triggers, nCollisions); + mixChannel(hhCorrelation, collHHHighPt, assocHHHighPt, triggers, nCollisions); + + // h-pi, h-K, h-p + mixChannel(hIdCorrelation, collHPiLowPt, assocHPiLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPiHighPt, assocHPiHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaLowPt, assocHKaLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaHighPt, assocHKaHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrLowPt, assocHPrLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrHighPt, assocHPrHighPt, triggers, nCollisions); + + // h-Phi + mixChannel(hPhiCorrelation, collHPhiLowPtPeak, assocHPhiLowPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtLSB, assocHPhiLowPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtRSB, assocHPhiLowPtRSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtPeak, assocHPhiHighPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtLSB, assocHPhiHighPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtRSB, assocHPhiHighPtRSB, triggers, nCollisions); + + // h-KStar + mixChannel(hKStarCorrelation, collHKStarLowPtPeak, assocHKStarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtLSB, assocHKStarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtRSB, assocHKStarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtPeak, assocHKStarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtLSB, assocHKStarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtRSB, assocHKStarHighPtRSB, triggers, nCollisions); + + // h-KStarBar + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtPeak, assocHKStarBarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtLSB, assocHKStarBarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtRSB, assocHKStarBarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtPeak, assocHKStarBarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtLSB, assocHKStarBarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtRSB, assocHKStarBarHighPtRSB, triggers, nCollisions); + + // h-Lambda + mixChannel(hLambdaCorrelation, collHLambdaLowPtPeak, assocHLambdaLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtLSB, assocHLambdaLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtRSB, assocHLambdaLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtPeak, assocHLambdaHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtLSB, assocHLambdaHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtRSB, assocHLambdaHighPtRSB, triggers, nCollisions); + + // h-LambdaBar + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtPeak, assocHLambdaBarLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtLSB, assocHLambdaBarLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtRSB, assocHLambdaBarLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtPeak, assocHLambdaBarHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtLSB, assocHLambdaBarHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtRSB, assocHLambdaBarHighPtRSB, triggers, nCollisions); } - PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Process Mix event in derive data", true); + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Mixed-event correlations from the minimal derived data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) From f41f9211158f51836249169b13302fca98a240c8 Mon Sep 17 00:00:00 2001 From: sandor-lokos <69418976+sandor-lokos@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:51:07 +0200 Subject: [PATCH 265/648] [PWGUD] Add collision-associated FIT bit mapping (#18221) --- PWGUD/Core/UDHelpers.h | 76 +++--- PWGUD/DataModel/UDTables.h | 47 ++-- PWGUD/TableProducer/Converters/CMakeLists.txt | 4 + .../UDCollisionFITBitsConverter.cxx | 61 +++++ PWGUD/TableProducer/SGCandProducer.cxx | 3 +- PWGUD/Tasks/sgFITAnalyzer.cxx | 18 +- PWGUD/Tasks/upcTestFitBitMapping.cxx | 223 +++++++++++++----- 7 files changed, 316 insertions(+), 116 deletions(-) create mode 100644 PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index d014047bcc9..ed94841fda5 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -425,14 +425,14 @@ bool cleanFDD(T& bc, float maxFITtime, float limitA, float limitC) // lims[4]: FDDC template -bool cleanFIT(T& bc, float maxFITtime, std::vector lims) +bool cleanFIT(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0(bc, maxFITtime, lims[1], lims[2]) && cleanFDD(bc, maxFITtime, lims[3], lims[4]); } template -bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) +bool cleanFITCollision(T& col, float maxFITtime, std::vector const& lims) { bool isCleanFV0 = true; if (col.has_foundFV0()) { @@ -453,7 +453,7 @@ bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITA(T& bc, float maxFITtime, std::vector lims) +bool cleanFITA(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0A(bc, maxFITtime, lims[1]) && @@ -462,7 +462,7 @@ bool cleanFITA(T& bc, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITC(T& bc, float maxFITtime, std::vector lims) +bool cleanFITC(T& bc, float maxFITtime, std::vector const& lims) { return cleanFT0C(bc, maxFITtime, lims[2]) && cleanFDDC(bc, maxFITtime, lims[4]); @@ -506,7 +506,7 @@ bool TCE(T& bc) // ----------------------------------------------------------------------------- template -bool TOR(T& bc, float maxFITtime, std::vector lims) +bool TOR(T& bc, float maxFITtime, std::vector const& lims) { auto torA = !cleanFT0A(bc, maxFITtime, lims[1]); auto torC = !cleanFT0C(bc, maxFITtime, lims[2]); @@ -565,27 +565,48 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, constexpr int kFV0Offset = 208; auto ampsA = ft0.amplitudeA(); - const int nA = std::min(ampsA.size(), 96); + auto chanA = ft0.channelA(); + const int nA = std::min(ampsA.size(), chanA.size()); for (int i = 0; i < nA; ++i) { const auto a = ampsA[i]; - setBit(thr1, kFT0AOffset + i, a >= thr1_FT0A); - setBit(thr2, kFT0AOffset + i, a >= thr2_FT0A); + const int c = chanA[i]; + + if (c < 0 || c >= 96) { + continue; + } + + setBit(thr1, kFT0AOffset + c, a >= thr1_FT0A); + setBit(thr2, kFT0AOffset + c, a >= thr2_FT0A); } auto ampsC = ft0.amplitudeC(); - const int nC = std::min(ampsC.size(), 112); + auto chanC = ft0.channelC(); + const int nC = std::min(ampsC.size(), chanC.size()); for (int i = 0; i < nC; ++i) { const auto a = ampsC[i]; - setBit(thr1, kFT0COffset + i, a >= thr1_FT0C); - setBit(thr2, kFT0COffset + i, a >= thr2_FT0C); + const int c = chanC[i]; + + if (c < 0 || c >= 112) { + continue; + } + + setBit(thr1, kFT0COffset + c, a >= thr1_FT0C); + setBit(thr2, kFT0COffset + c, a >= thr2_FT0C); } auto ampsV = fv0a.amplitude(); - const int nV = std::min(ampsV.size(), 48); + auto chanV = fv0a.channel(); + const int nV = std::min(ampsV.size(), chanV.size()); for (int i = 0; i < nV; ++i) { const auto a = ampsV[i]; - setBit(thr1, kFV0Offset + i, a >= thr1_FV0A); - setBit(thr2, kFV0Offset + i, a >= thr2_FV0A); + const int c = chanV[i]; + + if (c < 0 || c >= 48) { + continue; + } + + setBit(thr1, kFV0Offset + c, a >= thr1_FV0A); + setBit(thr2, kFV0Offset + c, a >= thr2_FV0A); } } @@ -777,24 +798,15 @@ template bool goodCollision(T const& coll, DGCutparHolder const& diffCuts) // Return true if collision is accepted according to user-chosen rules from event selection task { - bool accepted = true; - std::vector sels = diffCuts.collisionSel(); - if (sels[0]) - accepted = accepted && cutNoTimeFrameBorder(coll); - if (sels[1]) - accepted = accepted && cutNoSameBunchPileup(coll); - if (sels[2]) - accepted = accepted && cutNoITSROFrameBorder(coll); - if (sels[3]) - accepted = accepted && cutIsGoodZvtxFT0vsPV(coll); - if (sels[4]) - accepted = accepted && cutIsVertexITSTPC(coll); - if (sels[5]) - accepted = accepted && cutIsVertexTRDmatched(coll); - if (sels[6]) - accepted = accepted && cutIsVertexTOFmatched(coll); - - return accepted; + auto const sels = diffCuts.collisionSel(); + + return (!sels[0] || cutNoTimeFrameBorder(coll)) && + (!sels[1] || cutNoSameBunchPileup(coll)) && + (!sels[2] || cutNoITSROFrameBorder(coll)) && + (!sels[3] || cutIsGoodZvtxFT0vsPV(coll)) && + (!sels[4] || cutIsVertexITSTPC(coll)) && + (!sels[5] || cutIsVertexTRDmatched(coll)) && + (!sels[6] || cutIsVertexTOFmatched(coll)); } // ----------------------------------------------------------------------------- diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index afb7728a5ab..c47f5befaa1 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,28 +410,43 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { +DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table + DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 DECLARE_SOA_COLUMN(Thr2W0, thr2W0, uint64_t); /// 2 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 } // namespace udcollfitbits -DECLARE_SOA_TABLE(UDCollisionFITBits, "AOD", "UDCOLLFITBITS", +DECLARE_SOA_TABLE(UDCollisionFITBits_000, "AOD", "UDCOLLFITBITS", o2::soa::Index<>, - udcollfitbits::Thr1W0, /// 1 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr1W1, /// 1 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr1W2, /// 1 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr1W3, /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 - udcollfitbits::Thr2W0, /// 2 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr2W1, /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr2W2, /// 2 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 -); + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, + o2::soa::Index<>, + udcollfitbits::UDCollisionId, + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +using UDCollisionFITBits = UDCollisionFITBits_001; using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 7d5a6297ba1..9fd7c5b02e9 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -19,6 +19,10 @@ o2physics_add_dpl_workflow(collisions-converter SOURCES UDCollisionsConverter.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(collision-fit-bits-converter + SOURCES UDCollisionFITBitsConverter.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter SOURCES UDCollisionSelExtrasConverter.cxx diff --git a/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx new file mode 100644 index 00000000000..82be0196959 --- /dev/null +++ b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file UDCollisionFITBitsConverter.cxx +/// \brief Converts UDCollisionFITBits from version 000 to 001 +/// +/// The legacy table was written with exactly one FIT-bit row for every +/// UDCollision, in identical row order. Therefore row i of +/// UDCollisionFITBits_000 belongs to row i of UDCollisions. Because the +/// legacy table does not store this association explicitly, the converter +/// materialises it by writing the FIT-bit row's global index as +/// UDCollisionId. The eight packed FIT-bit words are copied unchanged. +/// +/// Executable: o2-analysis-ud-collision-fit-bits-converter +/// +/// \author Sandor Lokos, sandor.lokos@cern.ch + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct UDCollisionFITBitsConverter { + Produces outputFITBits; + + void process(aod::UDCollisionFITBits_000 const& legacyFITBits) + { + outputFITBits.reserve(legacyFITBits.size()); + + for (const auto& bits : legacyFITBits) { + outputFITBits(bits.globalIndex(), + bits.thr1W0(), + bits.thr1W1(), + bits.thr1W2(), + bits.thr1W3(), + bits.thr2W0(), + bits.thr2W1(), + bits.thr2W2(), + bits.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index 8bc472c455f..6574d613986 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -351,7 +351,8 @@ struct SGCandProducer { fitCuts.thr2_FV0A()); } - outputFITBits(w1[0], w1[1], w1[2], w1[3], + outputFITBits(outputCollisions.lastIndex(), + w1[0], w1[1], w1[2], w1[3], w2[0], w2[1], w2[2], w2[3]); if (newbc.has_zdc()) { diff --git a/PWGUD/Tasks/sgFITAnalyzer.cxx b/PWGUD/Tasks/sgFITAnalyzer.cxx index 39aa9c2486a..cff17b961f6 100644 --- a/PWGUD/Tasks/sgFITAnalyzer.cxx +++ b/PWGUD/Tasks/sgFITAnalyzer.cxx @@ -778,36 +778,36 @@ struct SGFITAnalyzer { avPtPVa += track.pt(); vecEtaPVa.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaApv"))->Fill(vtrk.Eta(), 1.); - if (!an) + if (!an) { registry.get(HIST("tracks/eta2Apv"))->Fill(vtrk.Eta(), 1.); - if (!an) z0pva++; - else if (an == 4) + } else if (an == 4) { z1pva++; + } } if (truegapSide == 1) { pvc++; avPtPVc += track.pt(); vecEtaPVc.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaCpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) + if (!cn) { registry.get(HIST("tracks/eta2Cpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) z0pvc++; - else if (cn == 4) + } else if (cn == 4) { z1pvc++; + } } if (truegapSide == 2) { pvac++; avPtPVac += track.pt(); vecEtaPVac.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) + if (!an && !cn) { registry.get(HIST("tracks/eta2ACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) z0pvac++; - else if (an >= 3 && cn >= 3) + } else if (an >= 3 && cn >= 3) { z1pvac++; + } } registry.get(HIST("tracks/TPCSignalvspPVC"))->Fill(vtrk.Mag(), signalTPC, 1.); registry.get(HIST("tracks/TOFSignalvspPVC"))->Fill(vtrk.Mag(), signalTOF, 1.); diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx index bcee34c60fc..829e1df5f52 100644 --- a/PWGUD/Tasks/upcTestFitBitMapping.cxx +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -11,111 +11,218 @@ // /// \file upcTestFitBitMapping.cxx -/// \brief FIT bits to phi, eta mapping +/// \brief Tutorial for accessing and mapping the FIT-bit table /// \author Sandor Lokos, sandor.lokos@cern.ch -/// \since March 2026 +/// \since October 2026 -#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit -#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" -#include // o2::constants::math::TwoPI -#include // o2::ft0::Geometry +#include +#include #include #include #include #include #include +#include #include #include -#include using namespace o2; using namespace o2::framework; struct UpcTestFitBitMapping { - static constexpr int Thr2Selector = 2; // value of whichThr that selects the Thr2 bit set + static constexpr int kThr1Selector = 1; + static constexpr int kThr2Selector = 2; - Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; - Configurable maxEvents{"maxEvents", -1, "Process at most this many rows (-1 = all)"}; + Configurable whichThr{"whichThr", 1, "FIT amplitude threshold to use: 1 for Thr1 or 2 for Thr2"}; - // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ + // Minimal zero-offset container required by getPhiEtaFromFitBit(). + // Detector-alignment corrections are not applied in this tutorial. struct OffsetXYZ { - double x{0}, y{0}, z{0}; + double x{0.}; + double y{0.}; + double z{0.}; + double getX() const { return x; } double getY() const { return y; } double getZ() const { return z; } }; - std::array offsetFT0{}; // iRunOffset = 0 for now - int iRunOffset = 0; + std::array offsetFT0{}; + static constexpr int kRunOffsetIndex = 0; - o2::ft0::Geometry ft0Det{}; + o2::ft0::Geometry ft0Geometry{}; HistogramRegistry registry{ "registry", { - {"hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{8, 3.5, 5.0}}}}, - {"hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, - - {"hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, o2::constants::math::TwoPI}}}}, - {"hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{8, -3.5, -2.0}}}}, - {"hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0.0, o2::constants::math::TwoPI}}}}, + {"association/hRowsPerCollision", + "FIT-bit rows associated with each collision;rows;collisions", + {HistType::kTH1F, {{4, -0.5, 3.5}}}}, + + {"multiplicity/hFT0A", + "FT0A fired-channel multiplicity;N_{fired}^{FT0A};collisions", + {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"multiplicity/hFT0C", + "FT0C fired-channel multiplicity;N_{fired}^{FT0C};collisions", + {HistType::kTH1F, {{113, -0.5, 112.5}}}}, + {"multiplicity/hFV0A", + "FV0A fired-channel multiplicity;N_{fired}^{FV0A};collisions", + {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + {"multiplicity/hFT0AVsFT0C", + "FT0A versus FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", + {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"occupancy/hPackedBit", + "Selected FIT-bit occupancy;packed bit;counts", + {HistType::kTH1F, {{256, -0.5, 255.5}}}}, + {"occupancy/hFT0AChannel", + "FT0A channel occupancy;FT0A channel;counts", + {HistType::kTH1F, {{96, -0.5, 95.5}}}}, + {"occupancy/hFT0CChannel", + "FT0C channel occupancy;FT0C channel;counts", + {HistType::kTH1F, {{112, -0.5, 111.5}}}}, + {"occupancy/hFV0AChannel", + "FV0A channel occupancy;FV0A channel;counts", + {HistType::kTH1F, {{48, -0.5, 47.5}}}}, + + {"mapping/hPhiA", + "FT0A #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaA", + "FT0A #eta;#eta;counts", + {HistType::kTH1F, {{8, 3.5, 5.0}}}}, + {"mapping/hEtaPhiA", + "FT0A #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYA", + "FT0A channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, + + {"mapping/hPhiC", + "FT0C #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaC", + "FT0C #eta;#eta;counts", + {HistType::kTH1F, {{8, -3.5, -2.0}}}}, + {"mapping/hEtaPhiC", + "FT0C #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYC", + "FT0C channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, }}; void init(InitContext&) { - // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. - ft0Det.calculateChannelCenter(); + if (whichThr.value != kThr1Selector && whichThr.value != kThr2Selector) { + LOGF(fatal, + "Invalid whichThr=%d: allowed values are 1 and 2", + whichThr.value); + } + + ft0Geometry.calculateChannelCenter(); + } + + template + udhelpers::Bits256 getSelectedBits(T const& row) const + { + if (whichThr.value == kThr2Selector) { + return udhelpers::makeBits256( + row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + } + + return udhelpers::makeBits256( + row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); } - void process(aod::UDCollisionFITBits const& bitsTable) + void process(aod::UDCollisions::iterator const&, + aod::UDCollisionFITBits const& fitBits) { - int64_t nProcessed = 0; + // Because UDCollisionFITBits contains UDCollisionId, the framework + // supplies only the FIT-bit rows associated with the current collision. + registry.fill(HIST("association/hRowsPerCollision"), fitBits.size()); + + // The producer is expected to write exactly one FIT-bit row per collision. + if (fitBits.size() != 1) { + return; + } + + const auto row = fitBits.begin(); + const auto bits = getSelectedBits(row); - for (auto const& row : bitsTable) { - if (maxEvents >= 0 && nProcessed >= maxEvents) { - break; + int nFT0A = 0; + int nFT0C = 0; + int nFV0A = 0; + + // Packed layout: + // 0--95: FT0A + // 96--207: FT0C + // 208--255: FV0A + for (int bit = 0; bit < udhelpers::kTotalBits; ++bit) { + if (!udhelpers::testBit(bits, bit)) { + continue; + } + + registry.fill(HIST("occupancy/hPackedBit"), bit); + + const auto decoded = udhelpers::decodeFitBit(bit); + + if (decoded.det == udhelpers::FitBitRef::Det::FV0) { + ++nFV0A; + registry.fill(HIST("occupancy/hFV0AChannel"), decoded.ch); + continue; + } + + if (decoded.det != udhelpers::FitBitRef::Det::FT0) { + continue; } - ++nProcessed; - // Use udhelpers' canonical packed type + builder - udhelpers::Bits256 w{}; - if (whichThr == Thr2Selector) { - w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + if (decoded.isC) { + ++nFT0C; + registry.fill( + HIST("occupancy/hFT0CChannel"), + decoded.ch - udhelpers::kFT0AChannels); } else { - w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + ++nFT0A; + registry.fill(HIST("occupancy/hFT0AChannel"), decoded.ch); + } + + double phi = 0.; + double eta = 0.; + if (!udhelpers::getPhiEtaFromFitBit( + ft0Geometry, bit, offsetFT0, kRunOffsetIndex, phi, eta)) { + continue; } - // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here. - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - if (bit < udhelpers::kFT0AChannels) { - registry.fill(HIST("hPhiA"), phi); - registry.fill(HIST("hEtaA"), eta); - registry.fill(HIST("hEtaPhiA"), eta, phi); - } else { - registry.fill(HIST("hPhiC"), phi); - registry.fill(HIST("hEtaC"), eta); - registry.fill(HIST("hEtaPhiC"), eta, phi); - } + const auto position = ft0Geometry.getChannelCenter(decoded.ch); + const double x = position.X() + offsetFT0[kRunOffsetIndex].getX(); + const double y = position.Y() + offsetFT0[kRunOffsetIndex].getY(); + + if (decoded.isC) { + registry.fill(HIST("mapping/hPhiC"), phi); + registry.fill(HIST("mapping/hEtaC"), eta); + registry.fill(HIST("mapping/hEtaPhiC"), eta, phi); + registry.fill(HIST("mapping/hXYC"), x, y); + } else { + registry.fill(HIST("mapping/hPhiA"), phi); + registry.fill(HIST("mapping/hEtaA"), eta); + registry.fill(HIST("mapping/hEtaPhiA"), eta, phi); + registry.fill(HIST("mapping/hXYA"), x, y); } } + + registry.fill(HIST("multiplicity/hFT0A"), nFT0A); + registry.fill(HIST("multiplicity/hFT0C"), nFT0C); + registry.fill(HIST("multiplicity/hFV0A"), nFV0A); + registry.fill(HIST("multiplicity/hFT0AVsFT0C"), nFT0A, nFT0C); } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From e5a777c346b5f061e385d446d892a7ed2a039127 Mon Sep 17 00:00:00 2001 From: lucamicheletti93 <38209984+lucamicheletti93@users.noreply.github.com> Date: Tue, 13 Oct 2026 03:38:55 +0200 Subject: [PATCH 266/648] [PWGDQ] Adding part to process jpsi-Dplus (#18213) Co-authored-by: Lucamicheletti92 --- PWGDQ/Tasks/taskJpsiHf.cxx | 96 ++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/PWGDQ/Tasks/taskJpsiHf.cxx b/PWGDQ/Tasks/taskJpsiHf.cxx index b41950b4056..2c5135e93ba 100644 --- a/PWGDQ/Tasks/taskJpsiHf.cxx +++ b/PWGDQ/Tasks/taskJpsiHf.cxx @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -55,10 +56,16 @@ static const std::vector labelsPt{}; static const std::vector labelsCutsBdt = {"BDT background", "BDT prompt", "BDT nonprompt"}; } // namespace bdtcuts +enum class DmesonType { + D0, + Dplus +}; + // Declarations of various short names using MyRedEvents = aod::RedJpDmColls; using MyRedPairCandidatesSelected = aod::RedJpDmDileptons; using MyRedD0CandidatesSelected = soa::Join; +using MyRedDplusCandidatesSelected = soa::Join; struct taskJPsiHf { // @@ -80,7 +87,8 @@ struct taskJPsiHf { // Preslices for unsorted indexes PresliceUnsorted perCollisionDilepton = aod::jpsidmescorr::redJpDmCollId; - PresliceUnsorted perCollisionDmeson = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionD0 = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionDplus = aod::jpsidmescorr::redJpDmCollId; // histogram for normalisation std::shared_ptr hCollisions; @@ -95,7 +103,7 @@ struct taskJPsiHf { // Template function to run pair - hadron combinations // TODO: generalise to all charm-hadron species - template + template void runDileptonDmeson(TEvent const& /*event*/, TDqTrack const& dileptons, THfTrack const& dmesons) { float ptDilepton = -999; @@ -123,29 +131,55 @@ struct taskJPsiHf { continue; } - auto minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); - auto minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); - auto minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); - auto minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); - - if (dmeson.massD0() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo0(); - auto bdtPrompt = dmeson.bdtPromptMassHypo0(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + int minItsClsDmesDau = 0; + int minTpcCrossRowsDmesDau = 0; + float minPtDmesDau = 0.f; + float minAbsEtaDmesDau = 0.f; + if constexpr (TDmeson == DmesonType::D0) { + minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); + minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); + minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); + minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); + } + if constexpr (TDmeson == DmesonType::Dplus) { + minItsClsDmesDau = std::min({dmeson.numItsClsDmesProng0(), dmeson.numItsClsDmesProng1(), dmeson.numItsClsDmesProng2()}); + minTpcCrossRowsDmesDau = std::min({dmeson.numTpcCrossedRowsDmesProng0(), dmeson.numTpcCrossedRowsDmesProng1(), dmeson.numTpcCrossedRowsDmesProng2()}); + minPtDmesDau = std::min({dmeson.ptDmesProng0(), dmeson.ptDmesProng1(), dmeson.ptDmesProng2()}); + minAbsEtaDmesDau = std::min({std::abs(dmeson.etaDmesProng0()), std::abs(dmeson.etaDmesProng1()), std::abs(dmeson.etaDmesProng2())}); + } + + if constexpr (TDmeson == DmesonType::D0) { + if (dmeson.massD0() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } + } + if (dmeson.massD0bar() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo1(); + auto bdtPrompt = dmeson.bdtPromptMassHypo1(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } - if (dmeson.massD0bar() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo1(); - auto bdtPrompt = dmeson.bdtPromptMassHypo1(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + if constexpr (TDmeson == DmesonType::Dplus) { + if (dmeson.massDmes() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassDPlus); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massDmes() > massHfCandMin && dmeson.massDmes() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massDmes(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } } @@ -158,8 +192,19 @@ struct taskJPsiHf { for (auto& event : events) { hCollisions->Fill(1.f); auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); - auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDmeson, event.index()); - runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionD0, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + } + } + + void processRedJspiDplus(MyRedEvents const& events, MyRedPairCandidatesSelected const& dileptons, MyRedDplusCandidatesSelected const& dmesons) + { + // Fill the column of collisions with pairs + for (auto& event : events) { + hCollisions->Fill(1.f); + auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDplus, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); } } @@ -172,6 +217,7 @@ struct taskJPsiHf { } PROCESS_SWITCH(taskJPsiHf, processRedJspiD0, "Process J/psi - D0", true); + PROCESS_SWITCH(taskJPsiHf, processRedJspiDplus, "Process J/psi - Dplus", true); PROCESS_SWITCH(taskJPsiHf, processNormCounter, "Process normalization counter", true); }; From 733e81bc8e8f8b879ad4cbebf033bfbd3c918587 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Tue, 13 Oct 2026 03:40:04 +0200 Subject: [PATCH 267/648] [PWGJE] Added process function to check phi alignment in data in different pTbin (#18257) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 57b89a93a34..6810033f1ab 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -615,6 +615,12 @@ struct JetSpectraEseTask { registry.add("hSTrackPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}, {occAxis}}}); registry.add("hSJetPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {phiAxis}, {etaAxis}, {occAxis}}}); } + if (doprocessJetAlignment) { + LOGF(info, "JetSpectraEseTask::init() - Jet Alignment Process"); + registry.add("hCentq2", ";cent;#it{q}_{2};occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}}}); + registry.add("hTrackPtPhiEta", ";cent;p_{T,track};#phi;#eta", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}}}); + registry.add("hJetPtPhiEta", ";cent;p_{T,jet};jet #phi;jet #eta", {HistType::kTHnSparseF, {{centAxis}, {250, -50, 200}, {phiAxis}, {etaAxis}}}); + } if (doprocessMCGenTrack) { LOGF(info, "JetSpectraEseTask::init() - MCGen track"); registry.add("mcgen/h3TrackPtGen", ";#it{p}_{T,track}; vtxZ; #eta", {HistType::kTH3F, {{assocTrackPt}, {vertexZAxis}, {etaAxis}}}); @@ -1084,6 +1090,57 @@ struct JetSpectraEseTask { } PROCESS_SWITCH(JetSpectraEseTask, processESEOccupancy, "process occupancy", false); + void processJetAlignment(soa::Join::iterator const& collision, + soa::Join const& tracks, soa::Filtered> const& jets) + { + const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + const auto qPerc{collision.qPERCFT0C()}; + if (qPerc[0] < 0) { + return; + } + + std::unique_ptr rhoFit{nullptr}; + if (cfgrhoPhi) { + rhoFit = fitRho(collision, psi, tracks, jets); + if (!rhoFit) { + return; + } + } + + if (!isVertexSelected(collision)) { + return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return; + } + + registry.fill(HIST("hCentq2"), getCentrality(collision), qPerc[0]); + auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; + + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { + continue; + } + if (!isAcceptedJet>(jet)) { + continue; + } + auto vCorrL = cfgrhoPhi ? corrL(jet) : corr(collision, jet); + registry.fill(HIST("hJetPtPhiEta"), getCentrality(collision), vCorrL, jet.phi(), jet.eta()); + } + + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + registry.fill(HIST("hTrackPtPhiEta"), getCentrality(collision), track.pt(), track.phi(), track.eta()); + } + } + PROCESS_SWITCH(JetSpectraEseTask, processJetAlignment, "process jet+track spectra for QA checking alignment issues", false); + void processMCParticleLevel(soa::Filtered>::iterator const& mcCollision, soa::SmallGroups const& collisions, soa::Filtered> const& jets, From 06bd775908e10f822b408315ee049ed90fafdd06 Mon Sep 17 00:00:00 2001 From: HANSEO PARK <53218370+hanseopark@users.noreply.github.com> Date: Tue, 13 Oct 2026 10:42:09 +0900 Subject: [PATCH 268/648] [PWGJE] Add baryon pair correlations in charged jets (#18256) --- PWGJE/Tasks/CMakeLists.txt | 4 + PWGJE/Tasks/baryonPairInJets.cxx | 1554 ++++++++++++++++++++++++++++++ 2 files changed, 1558 insertions(+) create mode 100644 PWGJE/Tasks/baryonPairInJets.cxx diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index c633110950f..e010858ccbe 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -519,4 +519,8 @@ if(FastJet_FOUND) SOURCES embeddingQA.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(baryon-pair-in-jets + SOURCES baryonPairInJets.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) endif() diff --git a/PWGJE/Tasks/baryonPairInJets.cxx b/PWGJE/Tasks/baryonPairInJets.cxx new file mode 100644 index 00000000000..80539c2bbfb --- /dev/null +++ b/PWGJE/Tasks/baryonPairInJets.cxx @@ -0,0 +1,1554 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file baryonPairInJets.cxx +/// \brief Identified proton-pair correlations in charged jets +/// \author Hanseo Park + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BaryonTracks = soa::Join; +using BaryonTracksMC = soa::Join; +using JetTracksWithOriginal = soa::Join; +using JetTracksMCDWithOriginal = soa::Join; +using ChargedJetsWithConstituents = soa::Join; +using ChargedMCDJetsWithConstituents = soa::Join; +using ChargedMCPJetsWithConstituents = soa::Join; +using MatchedChargedMCDJets = soa::Join; +using MatchedChargedMCPJets = soa::Join; + +struct BaryonPairInJets { + enum class PairType : uint8_t { + SameSign = 0, + OppositeSign = 1 + }; + + enum class ChargePairType : uint8_t { + PositivePositive = 0, + NegativeNegative = 1, + OppositeSign = 2 + }; + + enum class MixedReference : uint8_t { + Nominal = 0, + Loose = 1, + Random = 2 + }; + + enum class ProtonPidMethod : int { + TpcOnly = 0, + TpcTofCircular = 1, + TpcTofRectangular = 2 + }; + + enum class ControlSpecies : uint8_t { + Pion = 0, + Kaon = 1 + }; + + // Species-independent selected-particle representation. Keep the full + // kinematics needed by pair and future jet-fragmentation observables. + struct SelectedParticle { + float pt; + float eta; + float phi; + int8_t sign; + }; + + struct SelectedParticles { + std::vector baryons; + std::vector pions; + std::vector kaons; + }; + + struct : ConfigurableGroup { + Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex (cm)"}; + Configurable eventSelections{"eventSelections", "sel8+NoITSROFrameBorder+NoTimeFrameBorder+NoSameBunchPileup+IsGoodZvtxFT0vsPV", "Event selections"}; + Configurable applyRctSelection{"applyRctSelection", true, "Apply the PWGJE hadron-PID RCT selection"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection label"}; + Configurable occupancyMin{"occupancyMin", -999999, "Minimum track occupancy in time range"}; + Configurable occupancyMax{"occupancyMax", 999999, "Maximum track occupancy in time range"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable jetRadius{"jetRadius", 0.4f, "Charged-jet resolution parameter"}; + Configurable jetPtMin{"jetPtMin", 5.f, "Minimum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetPtMax{"jetPtMax", 140.f, "Maximum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetEtaMax{"jetEtaMax", 0.5f, "Maximum absolute charged-jet pseudorapidity"}; + Configurable areaFractionMin{"areaFractionMin", -1.f, "Minimum jet area divided by pi R squared; negative disables the cut"}; + } jetCuts; + + struct : ConfigurableGroup { + Configurable protonPtMin{"protonPtMin", 0.5f, "Minimum proton transverse momentum (GeV/c)"}; + Configurable protonPtMax{"protonPtMax", 2.5f, "Maximum proton transverse momentum (GeV/c)"}; + Configurable protonEtaMax{"protonEtaMax", 0.8f, "Maximum absolute proton pseudorapidity"}; + Configurable requirePvContributor{"requirePvContributor", false, "Require the track to be a PV contributor"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 70, "Minimum number of crossed TPC rows"}; + Configurable tpcChi2Max{"tpcChi2Max", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable itsChi2Max{"itsChi2Max", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable dcaXYMax{"dcaXYMax", 0.2f, "Maximum absolute transverse DCA (cm)"}; + Configurable dcaZMax{"dcaZMax", 0.1f, "Maximum absolute longitudinal DCA (cm)"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable method{"method", static_cast(ProtonPidMethod::TpcTofCircular), "PID method: 0 TPC only, 1 circular TPC-TOF, 2 rectangular TPC-TOF"}; + Configurable tofPtThreshold{"tofPtThreshold", 0.75f, "Require TOF above this transverse momentum (GeV/c)"}; + Configurable tpcNSigmaMax{"tpcNSigmaMax", 3.f, "Maximum absolute proton TPC n-sigma"}; + Configurable tofNSigmaMax{"tofNSigmaMax", 3.f, "Maximum absolute proton TOF n-sigma"}; + Configurable combinedNSigmaMax{"combinedNSigmaMax", 3.f, "Maximum circular TPC-TOF proton n-sigma"}; + Configurable rejectPionKaon{"rejectPionKaon", false, "Reject candidates compatible with the pion or kaon hypothesis"}; + Configurable rejectionNSigma{"rejectionNSigma", 3.f, "Pion and kaon rejection threshold"}; + Configurable pionTpcNSigmaMax{"pionTpcNSigmaMax", 3.f, "Maximum absolute pion TPC n-sigma"}; + Configurable pionTofNSigmaMax{"pionTofNSigmaMax", 3.f, "Maximum absolute pion TOF n-sigma"}; + Configurable pionCombinedNSigmaMax{"pionCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF pion n-sigma"}; + Configurable kaonTpcNSigmaMax{"kaonTpcNSigmaMax", 3.f, "Maximum absolute kaon TPC n-sigma"}; + Configurable kaonTofNSigmaMax{"kaonTofNSigmaMax", 3.f, "Maximum absolute kaon TOF n-sigma"}; + Configurable kaonCombinedNSigmaMax{"kaonCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF kaon n-sigma"}; + } pidCuts; + + struct : ConfigurableGroup { + Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed-event neighbours"}; + ConfigurableAxis vertexZBins{"vertexZBins", {VARIABLE_WIDTH, -10.f, -5.f, -2.f, 0.f, 2.f, 5.f, 10.f}, "Mixing bins in collision z vertex"}; + ConfigurableAxis multiplicityBins{"multiplicityBins", {VARIABLE_WIDTH, 0.f, 10.f, 20.f, 40.f, 80.f, 150.f, 300.f, 600.f, 2000.f}, "Mixing bins in FT0C multiplicity"}; + Configurable nominalJetPtDifferenceMax{"nominalJetPtDifferenceMax", 2.5f, "Maximum jet-pT difference for nominal mixing (GeV/c)"}; + Configurable nominalJetNchDifferenceMax{"nominalJetNchDifferenceMax", 3, "Maximum jet-constituent multiplicity difference for nominal mixing"}; + Configurable nominalJetEtaDifferenceMax{"nominalJetEtaDifferenceMax", 0.1f, "Maximum jet-eta difference for nominal mixing"}; + Configurable looseJetPtDifferenceMax{"looseJetPtDifferenceMax", 7.5f, "Maximum jet-pT difference for loose mixing (GeV/c)"}; + Configurable looseJetNchDifferenceMax{"looseJetNchDifferenceMax", 8, "Maximum jet-constituent multiplicity difference for loose mixing"}; + Configurable looseJetEtaDifferenceMax{"looseJetEtaDifferenceMax", 0.3f, "Maximum jet-eta difference for loose mixing"}; + } mixing; + + struct : ConfigurableGroup { + Configurable fillQa{"fillQa", true, "Fill event, jet, track, and PID QA histograms"}; + Configurable fillReferenceVariations{"fillReferenceVariations", true, "Fill loose and unmatched mixed-event references"}; + Configurable fillChargeSeparated{"fillChargeSeparated", true, "Fill charge-resolved pair histograms"}; + Configurable fillPionControl{"fillPionControl", false, "Fill same-species pion control correlations"}; + Configurable fillKaonControl{"fillKaonControl", false, "Fill same-species kaon control correlations"}; + } output; + + struct : ConfigurableGroup { + Configurable nearSideDeltaPhiMax{"nearSideDeltaPhiMax", 0.2f, "Maximum absolute delta phi for near-side pair yields"}; + Configurable nearSideDeltaEtaMax{"nearSideDeltaEtaMax", 0.6f, "Maximum absolute delta eta for near-side pair yields"}; + ConfigurableAxis jetPt{"jetPt", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 15.f, 20.f, 25.f, 30.f, 40.f, 50.f, 60.f, 80.f, 100.f, 120.f, 140.f}, "#it{p}_{T, jet} (GeV/#it{c})"}; + ConfigurableAxis jetEta{"jetEta", {40, -1.f, 1.f}, "#eta_{jet}"}; + ConfigurableAxis jetPhi{"jetPhi", {72, 0.f, o2::constants::math::TwoPI}, "#varphi_{jet}"}; + ConfigurableAxis jetNch{"jetNch", {61, -0.5f, 60.5f}, "#it{N}_{ch}^{jet}"}; + ConfigurableAxis baryonPt{"baryonPt", {50, 0.f, 5.f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis baryonEta{"baryonEta", {48, -1.2f, 1.2f}, "#eta"}; + ConfigurableAxis baryonRapidity{"baryonRapidity", {48, -1.2f, 1.2f}, "#it{y}"}; + ConfigurableAxis nSigma{"nSigma", {100, -5.f, 5.f}, "n#sigma"}; + ConfigurableAxis dca{"dca", {100, -0.5f, 0.5f}, "DCA (cm)"}; + ConfigurableAxis deltaEta{"deltaEta", {80, -0.8f, 0.8f}, "#Delta#eta"}; + ConfigurableAxis deltaPhi{"deltaPhi", {80, -0.8f, 0.8f}, "#Delta#varphi"}; + ConfigurableAxis deltaR{"deltaR", {40, 0.f, 0.8f}, "#Delta#it{R}"}; + ConfigurableAxis inclusiveDeltaEta{"inclusiveDeltaEta", {64, -1.6f, 1.6f}, "#Delta#eta"}; + ConfigurableAxis inclusiveDeltaPhi{"inclusiveDeltaPhi", {72, -o2::constants::math::PIHalf, 3.f * o2::constants::math::PIHalf}, "#Delta#varphi"}; + } axes; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + std::vector eventSelectionBits; + o2::aod::rctsel::RCTFlagsChecker inclusiveRctChecker; + SliceCache mixingCache; + SliceCache mcdMixingCache; + SliceCache mcpMixingCache; + SliceCache inclusiveMixingCache; + using MixingBinning = ColumnBinningPolicy; + using MCPMixingBinning = ColumnBinningPolicy; + using InclusiveCollisions = soa::Join; + using InclusiveMixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventCuts.eventSelections)); + inclusiveRctChecker.init(static_cast(eventCuts.rctLabel)); + + AxisSpec axisPairType{2, -0.5, 1.5, "pair type"}; + AxisSpec axisChargePairType{3, -0.5, 2.5, "charge pair type"}; + AxisSpec axisCounter{8, 0.5, 8.5, "counter"}; + AxisSpec axisMultiplicity{11, -0.5, 10.5, "multiplicity per jet"}; + AxisSpec axisJetPt{axes.jetPt}; + AxisSpec axisJetEta{axes.jetEta}; + AxisSpec axisJetPhi{axes.jetPhi}; + AxisSpec axisJetNch{axes.jetNch}; + AxisSpec axisBaryonPt{axes.baryonPt}; + AxisSpec axisBaryonEta{axes.baryonEta}; + AxisSpec axisBaryonRapidity{axes.baryonRapidity}; + AxisSpec axisNSigma{axes.nSigma}; + AxisSpec axisDca{axes.dca}; + AxisSpec axisDeltaEta{axes.deltaEta}; + AxisSpec axisDeltaPhi{axes.deltaPhi}; + AxisSpec axisDeltaR{axes.deltaR}; + AxisSpec axisInclusiveDeltaEta{axes.inclusiveDeltaEta}; + AxisSpec axisInclusiveDeltaPhi{axes.inclusiveDeltaPhi}; + AxisSpec axisOrigin{3, 0.5, 3.5, "origin"}; + + registry.add("event/cutFlow", "Event selection;selection step;entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + registry.add("event/zVertex", "Selected collision z vertex;#it{z}_{vtx} (cm);entries", HistType::kTH1F, {{80, -20.f, 20.f}}); + registry.add("event/multiplicityFT0C", "Selected collision FT0C multiplicity;FT0C multiplicity;entries", HistType::kTH1F, {{200, 0.f, 2000.f}}); + + registry.add("jet/ptBeforePtSelection", "Charged jets before the jet-pT selection;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/pt", "Selected charged jets;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/eta", "Selected charged jets;#eta_{jet};entries", HistType::kTH1F, {axisJetEta}); + registry.add("jet/phi", "Selected charged jets;#varphi_{jet};entries", HistType::kTH1F, {axisJetPhi}); + registry.add("jet/nConstituents", "Charged-jet constituent multiplicity;#it{N}_{ch}^{jet};entries", HistType::kTH1F, {axisJetNch}); + registry.add("jet/nSelectedBaryons", "Selected baryon multiplicity per jet;#it{N}_{p+#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtons", "Selected proton multiplicity per jet;#it{N}_{p};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nAntiprotons", "Selected antiproton multiplicity per jet;#it{N}_{#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtonsVsAntiprotons", "Proton and antiproton multiplicities per jet;#it{N}_{p};#it{N}_{#bar{p}}", HistType::kTH2F, {axisMultiplicity, axisMultiplicity}); + registry.add("jet/nConstituentsVsPt", "Constituent multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{ch}^{jet}", HistType::kTH2F, {axisJetPt, axisJetNch}); + registry.add("jet/nBaryonsVsPt", "Selected baryon multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{p+#bar{p}}", HistType::kTH2F, {axisJetPt, axisMultiplicity}); + registry.add("jet/sameSignPairsVsPt", "Same-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + registry.add("jet/oppositeSignPairsVsPt", "Opposite-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + + registry.add("proton/pt", "Selected proton and antiproton candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisBaryonPt}); + registry.add("proton/eta", "Selected proton and antiproton candidates;#eta;entries", HistType::kTH1F, {axisBaryonEta}); + registry.add("proton/rapidity", "Selected proton and antiproton candidates;#it{y};entries", HistType::kTH1F, {axisBaryonRapidity}); + registry.add("proton/tpcNSigmaVsPt", "Proton TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/tofNSigmaVsPt", "Proton TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/dcaXYVsPt", "Selected proton transverse DCA;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("proton/dcaZVsPt", "Selected proton longitudinal DCA;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("pid/before/tpcSignalVsSignedP", "TPC response before proton PID;charge #times #it{p} (GeV/#it{c});TPC signal", HistType::kTH2F, {{120, -6.f, 6.f}, {200, 0.f, 200.f}}); + registry.add("pid/before/tofBetaVsSignedP", "TOF response before proton PID;charge #times #it{p} (GeV/#it{c});#beta", HistType::kTH2F, {{120, -6.f, 6.f}, {120, 0.f, 1.2f}}); + registry.add("pid/before/tpcNSigmaPiVsPt", "Pion TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaKaVsPt", "Kaon TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaPrVsPt", "Proton TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPiVsPt", "Pion TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaKaVsPt", "Kaon TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPrVsPt", "Proton TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/tofMatchDenominator", "Tracks entering PID QA;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/tofMatchNumerator", "Tracks with TOF;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/selectedPtByCharge", "Selected proton candidates;charge;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{2, -1.5f, 1.5f}, axisBaryonPt}); + } + + addPairHistograms("sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/nominal", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/loose", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/random", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("sameEvent/chargePairSparse", "Charge-separated baryon pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + registry.add("mixedEvent/nominal/chargePairSparse", "Charge-separated nominal mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + registry.add("mixedEvent/loose/chargePairSparse", "Charge-separated loose mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + registry.add("mixedEvent/random/chargePairSparse", "Charge-separated random mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + + addInclusivePairHistograms("inclusive/sameEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + addInclusivePairHistograms("inclusive/mixedEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + registry.add("inclusive/event/multiplicity", "Inclusive selected-event multiplicity;FT0C multiplicity;events", HistType::kTH1F, {{200, 0.f, 2000.f}}); + registry.add("inclusive/normalization/sameEvent", "Inclusive same-event normalization;counter;entries", HistType::kTH1F, {axisCounter}); + registry.add("inclusive/normalization/mixedEvent", "Inclusive mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + registry.add("normalization/sameEvent", "Same-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventNominal", "Nominal mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventLoose", "Loose mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventRandom", "Random mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mixedEvent/jetPtDifference", "Mixed-jet matching QA;|#Delta#it{p}_{T,jet}| (GeV/#it{c});jet pairs", HistType::kTH1F, {{80, 0.f, 40.f}}); + registry.add("mixedEvent/jetEtaDifference", "Mixed-jet matching QA;|#Delta#eta_{jet}|;jet pairs", HistType::kTH1F, {{40, 0.f, 1.f}}); + registry.add("mixedEvent/jetNchDifference", "Mixed-jet matching QA;|#Delta#it{N}_{ch}^{jet}|;jet pairs", HistType::kTH1F, {{61, -0.5f, 60.5f}}); + + registry.add("mc/jet/allDetectorPt", "Accepted detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/allParticlePt", "Accepted particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedDetectorPt", "Matched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedParticlePt", "Matched particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/fakeDetectorPt", "Unmatched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/responsePt", "Charged-jet response;#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c})", HistType::kTH2F, {axisJetPt, axisJetPt}); + registry.add("mc/jet/relativePtResolution", "Charged-jet momentum resolution;#it{p}_{T,jet}^{part} (GeV/#it{c});(#it{p}_{T}^{det}-#it{p}_{T}^{part})/#it{p}_{T}^{part}", HistType::kTH2F, {axisJetPt, {120, -1.5f, 1.5f}}); + registry.add("mc/jet/matchingDeltaR", "Detector-particle jet matching distance;#Delta#it{R}_{match};matched jets", HistType::kTH1F, {{100, 0.f, 1.f}}); + registry.add("mc/pid/generatedPrimaryProtonPt", "Generated primary protons in accepted particle-level jets;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/reconstructedTrueProtonPt", "Reconstructed primary true protons before PID;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedTrueProtonPt", "PID-selected primary true protons;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedCandidateOriginVsPt", "Selected proton-candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisBaryonPt, axisOrigin}); + registry.add("mc/pid/selectedCandidateTruePdgVsPt", "Selected proton-candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisBaryonPt, {801, -4005.f, 4005.f}}); + registry.add("mc/pid/ptResponsePrimaryProton", "Primary-proton momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisBaryonPt, axisBaryonPt}); + addPairHistograms("mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("mc/particle/normalizationSameEvent", "Particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mc/particle/normalizationMixedEvent", "Particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + if (output.fillPionControl.value) { + addControlSpeciesHistograms("control/pion", "Pion", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + if (output.fillKaonControl.value) { + addControlSpeciesHistograms("control/kaon", "Kaon", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + + setHistogramLabels(); + } + + void addControlSpeciesHistograms(std::string const& directory, + std::string const& speciesName, + AxisSpec const& axisPairType, + AxisSpec const& axisCounter, + AxisSpec const& axisJetPt, + AxisSpec const& axisParticlePt, + AxisSpec const& axisNSigma, + AxisSpec const& axisOrigin, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + auto addHistogram = [&](std::string const& suffix, std::string const& title, HistType type, std::vector const& histogramAxes) { + registry.add(directory + suffix, title.c_str(), type, histogramAxes); + }; + AxisSpec const axisPdg{801, -4005.f, 4005.f, "true PDG code"}; + addPairHistograms(directory + "/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/normalizationSameEvent", speciesName + " same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/normalizationMixedEvent", speciesName + " mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + addHistogram("/qa/pt", "Selected " + speciesName + " candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisParticlePt}); + addHistogram("/qa/tpcNSigmaVsPt", speciesName + " TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + addHistogram("/qa/tofNSigmaVsPt", speciesName + " TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + } + addHistogram("/mc/generatedPrimaryPt", "Generated primary " + speciesName + " candidates;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/reconstructedTruePt", "Reconstructed true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedTruePt", "PID-selected true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedCandidateOriginVsPt", "Selected " + speciesName + " candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisParticlePt, axisOrigin}); + addHistogram("/mc/selectedCandidateTruePdgVsPt", "Selected " + speciesName + " candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisParticlePt, axisPdg}); + addHistogram("/mc/ptResponsePrimary", "Primary " + speciesName + " momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisParticlePt, axisParticlePt}); + addPairHistograms(directory + "/mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/mc/particle/normalizationSameEvent", speciesName + " particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/mc/particle/normalizationMixedEvent", speciesName + " particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + } + + void addPairHistograms(std::string const& directory, + AxisSpec const& axisPairType, + AxisSpec const& axisJetPt, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/sameSignDeltaR", "Same-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/oppositeSignDeltaR", "Opposite-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/pairSparse", "Baryon-pair distribution;pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + } + + void addInclusivePairHistograms(std::string const& directory, + AxisSpec const& axisChargePairType, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi) + { + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Inclusive same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Inclusive opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/chargePairSparse", "Inclusive charge-separated baryon pairs;charge pair type;#Delta#eta;#Delta#varphi", HistType::kTHnSparseF, {axisChargePairType, axisDeltaEta, axisDeltaPhi}); + } + + void setHistogramLabels() + { + auto eventCutFlow = registry.get(HIST("event/cutFlow")); + eventCutFlow->GetXaxis()->SetBinLabel(1, "all"); + eventCutFlow->GetXaxis()->SetBinLabel(2, "z vertex"); + eventCutFlow->GetXaxis()->SetBinLabel(3, "occupancy"); + eventCutFlow->GetXaxis()->SetBinLabel(4, "event selection"); + + auto setNormalizationLabels = [](std::shared_ptr const& histogram) { + histogram->GetXaxis()->SetBinLabel(1, "events or event pairs"); + histogram->GetXaxis()->SetBinLabel(2, "selected jets or jet pairs"); + histogram->GetXaxis()->SetBinLabel(3, "same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(4, "opposite-sign pairs"); + histogram->GetXaxis()->SetBinLabel(5, "near-side same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(6, "near-side opposite-sign pairs"); + }; + setNormalizationLabels(registry.get(HIST("normalization/sameEvent"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventNominal"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventLoose"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventRandom"))); + if (output.fillPionControl.value) { + setNormalizationLabels(registry.get(HIST("control/pion/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationMixedEvent"))); + } + if (output.fillKaonControl.value) { + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationMixedEvent"))); + } + } + + template + bool isSelectedCollision(CollisionType const& collision, bool fillCutFlow) + { + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 1.f); + } + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 2.f); + } + if (collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 3.f); + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, eventCuts.applyRctSelection.value, static_cast(eventCuts.rctLabel))) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 4.f); + } + return true; + } + + template + bool isSelectedJetGeometry(JetType const& jet) const + { + if (jet.r() != static_cast(std::lround(jetCuts.jetRadius.value * 100.f)) || std::abs(jet.eta()) > jetCuts.jetEtaMax.value) { + return false; + } + if (jetCuts.areaFractionMin.value >= 0.f) { + float const fullArea = o2::constants::math::PI * jetCuts.jetRadius.value * jetCuts.jetRadius.value; + if (jet.area() / fullArea < jetCuts.areaFractionMin.value) { + return false; + } + } + return true; + } + + template + bool isSelectedJet(JetType const& jet) const + { + return isSelectedJetGeometry(jet) && jet.pt() >= jetCuts.jetPtMin.value && jet.pt() < jetCuts.jetPtMax.value; + } + + template + bool isSelectedTrack(TrackType const& track) const + { + if (trackCuts.requirePvContributor.value && !track.isPVContributor()) { + return false; + } + if (!track.hasITS() || !track.hasTPC() || !track.isGlobalTrack()) { + return false; + } + if (track.tpcNClsCrossedRows() < trackCuts.tpcCrossedRowsMin.value || track.tpcChi2NCl() > trackCuts.tpcChi2Max.value || track.itsChi2NCl() > trackCuts.itsChi2Max.value) { + return false; + } + if (std::abs(track.eta()) > trackCuts.protonEtaMax.value || track.pt() < trackCuts.protonPtMin.value || track.pt() >= trackCuts.protonPtMax.value) { + return false; + } + if (std::abs(track.dcaXY()) > trackCuts.dcaXYMax.value || std::abs(track.dcaZ()) > trackCuts.dcaZMax.value) { + return false; + } + return true; + } + + template + bool isSelectedProton(TrackType const& track) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + float const tpcNSigma = std::abs(track.tpcNSigmaPr()); + bool isProton = false; + + if (pidMethod == ProtonPidMethod::TpcOnly || track.pt() < pidCuts.tofPtThreshold.value) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value; + } else if (track.hasTOF()) { + float const tofNSigma = std::abs(track.tofNSigmaPr()); + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + isProton = std::hypot(tpcNSigma, tofNSigma) < pidCuts.combinedNSigmaMax.value; + } else if (pidMethod == ProtonPidMethod::TpcTofRectangular) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value && tofNSigma < pidCuts.tofNSigmaMax.value; + } + } + + if (!isProton || !pidCuts.rejectPionKaon.value) { + return isProton; + } + + bool const isPionCompatible = std::abs(track.tpcNSigmaPi()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaPi()) < pidCuts.rejectionNSigma.value)); + bool const isKaonCompatible = std::abs(track.tpcNSigmaKa()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaKa()) < pidCuts.rejectionNSigma.value)); + return !isPionCompatible && !isKaonCompatible; + } + + bool passesControlPid(float pt, bool hasTof, float tpcNSigma, float tofNSigma, float tpcMaximum, float tofMaximum, float combinedMaximum) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + tpcNSigma = std::abs(tpcNSigma); + tofNSigma = std::abs(tofNSigma); + if (pidMethod == ProtonPidMethod::TpcOnly || pt < pidCuts.tofPtThreshold.value) { + return tpcNSigma < tpcMaximum; + } + if (!hasTof) { + return false; + } + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + return std::hypot(tpcNSigma, tofNSigma) < combinedMaximum; + } + return tpcNSigma < tpcMaximum && tofNSigma < tofMaximum; + } + + template + bool isSelectedPion(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaPi(), track.tofNSigmaPi(), pidCuts.pionTpcNSigmaMax.value, pidCuts.pionTofNSigmaMax.value, pidCuts.pionCombinedNSigmaMax.value); + } + + template + bool isSelectedKaon(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaKa(), track.tofNSigmaKa(), pidCuts.kaonTpcNSigmaMax.value, pidCuts.kaonTofNSigmaMax.value, pidCuts.kaonCombinedNSigmaMax.value); + } + + template + SelectedParticles collectSelectedParticles(JetType const& jet, bool fillQa, float weight = 1.f, bool collectControls = true) + { + SelectedParticles selected; + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + + if (fillQa) { + float const signedMomentum = static_cast(track.sign()) * track.p(); + registry.fill(HIST("pid/before/tpcSignalVsSignedP"), signedMomentum, track.tpcSignal(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPiVsPt"), track.pt(), track.tpcNSigmaPi(), weight); + registry.fill(HIST("pid/before/tpcNSigmaKaVsPt"), track.pt(), track.tpcNSigmaKa(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPrVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + registry.fill(HIST("pid/tofMatchDenominator"), track.pt(), weight); + if (track.hasTOF()) { + registry.fill(HIST("pid/tofMatchNumerator"), track.pt(), weight); + registry.fill(HIST("pid/before/tofBetaVsSignedP"), signedMomentum, track.beta(), weight); + registry.fill(HIST("pid/before/tofNSigmaPiVsPt"), track.pt(), track.tofNSigmaPi(), weight); + registry.fill(HIST("pid/before/tofNSigmaKaVsPt"), track.pt(), track.tofNSigmaKa(), weight); + registry.fill(HIST("pid/before/tofNSigmaPrVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + } + + SelectedParticle const particle{track.pt(), track.eta(), track.phi(), static_cast(track.sign())}; + if (isSelectedProton(track)) { + selected.baryons.push_back(particle); + if (fillQa) { + float const rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, o2::constants::physics::MassProton); + registry.fill(HIST("proton/pt"), particle.pt, weight); + registry.fill(HIST("proton/eta"), particle.eta, weight); + registry.fill(HIST("proton/rapidity"), rapidity, weight); + registry.fill(HIST("proton/tpcNSigmaVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + if (track.hasTOF()) { + registry.fill(HIST("proton/tofNSigmaVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + registry.fill(HIST("proton/dcaXYVsPt"), track.pt(), track.dcaXY(), weight); + registry.fill(HIST("proton/dcaZVsPt"), track.pt(), track.dcaZ(), weight); + registry.fill(HIST("pid/selectedPtByCharge"), static_cast(particle.sign), particle.pt, weight); + } + } + if (collectControls && output.fillPionControl.value && isSelectedPion(track)) { + selected.pions.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/pion/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/pion/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaPi(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/pion/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaPi(), weight); + } + } + } + if (collectControls && output.fillKaonControl.value && isSelectedKaon(track)) { + selected.kaons.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/kaon/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/kaon/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaKa(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/kaon/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaKa(), weight); + } + } + } + } + return selected; + } + + static PairType getPairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + return baryon1.sign == baryon2.sign ? PairType::SameSign : PairType::OppositeSign; + } + + static ChargePairType getChargePairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + if (baryon1.sign > 0 && baryon2.sign > 0) { + return ChargePairType::PositivePositive; + } + if (baryon1.sign < 0 && baryon2.sign < 0) { + return ChargePairType::NegativeNegative; + } + return ChargePairType::OppositeSign; + } + + template + void fillControlNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlParticleNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/mc/particle/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/mc/particle/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlPair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + { + auto fillPairHistograms = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& deltaRSameSignName, auto const& deltaROppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaRSameSignName, deltaR, weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaROppositeSignName, deltaR, weight); + } + registry.fill(sparseName, static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(sparseName, static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + }; + + if constexpr (Species == ControlSpecies::Pion && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/sameSignDeltaR"), HIST("control/pion/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/sameSignDeltaR"), HIST("control/pion/sameEvent/oppositeSignDeltaR"), HIST("control/pion/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Kaon && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/sameSignDeltaR"), HIST("control/kaon/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/pairSparse")); + } + } else { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/pairSparse")); + } + } + } + + template + void fillControlSameJetPairs(std::vector const& particles, float jetPt, float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, 2.f, weight); + } else { + fillControlNormalization(false, 2.f, weight); + } + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + PairType const pairType = getPairType(particles[first], particles[second]); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + fillControlPair(false, pairType, jetPt, deltaEta, deltaPhi, deltaR, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + void fillControlMixedJetPairs(std::vector const& particles1, + std::vector const& particles2, + JetType1 const& jet1, + JetType2 const& jet2, + float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, 2.f, weight); + } else { + fillControlNormalization(true, 2.f, weight); + } + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + for (auto const& particle1 : particles1) { + float const localEta1 = particle1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(particle1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& particle2 : particles2) { + float const localEta2 = particle2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(particle2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(particle1, particle2); + fillControlPair(true, pairType, mixedJetPt, deltaEta, deltaPhi, deltaR, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + std::vector collectInclusiveBaryons(TracksType const& tracks) + { + std::vector baryons; + for (auto const& track : tracks) { + if (!isSelectedTrack(track)) { + continue; + } + if (!isSelectedProton(track)) { + continue; + } + baryons.push_back({track.pt(), track.eta(), track.phi(), static_cast(track.sign())}); + } + return baryons; + } + + template + bool isSelectedInclusiveCollision(CollisionType const& collision) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value || + collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || + collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + + uint16_t const collisionSelection = jetderiveddatautilities::setEventSelectionBit(collision); + bool isOrCondition = false; + bool passedOrCondition = false; + for (auto const& selectionBit : eventSelectionBits) { + if (selectionBit == -1) { + isOrCondition = true; + continue; + } + bool const passed = collisionSelection & (1U << selectionBit); + if (!isOrCondition && !passed) { + return false; + } + if (isOrCondition && passed) { + passedOrCondition = true; + } + } + if (isOrCondition && !passedOrCondition) { + return false; + } + return !eventCuts.applyRctSelection.value || inclusiveRctChecker.checkTable(collision); + } + + void fillInclusivePair(bool isMixed, + SelectedParticle const& baryon1, + SelectedParticle const& baryon2, + float weight) + { + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + float const deltaEta = baryon1.eta - baryon2.eta; + float const deltaPhi = RecoDecay::constrainAngle(baryon1.phi - baryon2.phi, -o2::constants::math::PIHalf); + float const reflectedDeltaPhi = RecoDecay::constrainAngle(-deltaPhi, -o2::constants::math::PIHalf); + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + } + } + + template + bool isMatchedJetPair(float jetPtDifference, int jetNchDifference, float jetEtaDifference) const + { + if constexpr (Reference == MixedReference::Random) { + return true; + } else if constexpr (Reference == MixedReference::Nominal) { + return jetPtDifference <= mixing.nominalJetPtDifferenceMax.value && + jetNchDifference <= mixing.nominalJetNchDifferenceMax.value && + jetEtaDifference <= mixing.nominalJetEtaDifferenceMax.value; + } else { + return jetPtDifference <= mixing.looseJetPtDifferenceMax.value && + jetNchDifference <= mixing.looseJetNchDifferenceMax.value && + jetEtaDifference <= mixing.looseJetEtaDifferenceMax.value; + } + } + + template + void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + { + float const pairTypeValue = static_cast(pairType); + auto fillSymmetrized = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } + registry.fill(sparseName, pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(sparseName, pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + }; + + if constexpr (Reference == MixedReference::Nominal) { + fillSymmetrized(HIST("mixedEvent/nominal/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/nominal/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/nominal/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } + } else if constexpr (Reference == MixedReference::Loose) { + fillSymmetrized(HIST("mixedEvent/loose/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/loose/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/loose/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } + } else { + fillSymmetrized(HIST("mixedEvent/random/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/random/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/random/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } + } + } + + template + void fillMixedNormalization(float counter, float weight) + { + if constexpr (Reference == MixedReference::Nominal) { + registry.fill(HIST("normalization/mixedEventNominal"), counter, weight); + } else if constexpr (Reference == MixedReference::Loose) { + registry.fill(HIST("normalization/mixedEventLoose"), counter, weight); + } else { + registry.fill(HIST("normalization/mixedEventRandom"), counter, weight); + } + } + + void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + { + float const pairTypeValue = static_cast(pairType); + if (pairType == PairType::SameSign) { + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + if (output.fillChargeSeparated.value) { + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } + } + + template + SelectedParticles collectParticleCandidates(JetType const& jet, bool fillQa, float weight) + { + SelectedParticles selected; + for (auto const& particle : jet.template tracks_as()) { + if (!particle.isPhysicalPrimary()) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || + std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{particle.pt(), particle.eta(), particle.phi(), sign}; + int const absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kProton) { + selected.baryons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("mc/pid/generatedPrimaryProtonPt"), selectedParticle.pt, weight); + } + } else if (output.fillPionControl.value && absolutePdg == PDG_t::kPiPlus) { + selected.pions.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/pion/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } else if (output.fillKaonControl.value && absolutePdg == PDG_t::kKPlus) { + selected.kaons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/kaon/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } + } + return selected; + } + + void fillParticlePair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + { + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + } + } + + template + void fillControlMCPidQa(TrackType const& track, ParticleType const& particle, float weight) + { + int const expectedPdg = Species == ControlSpecies::Pion ? PDG_t::kPiPlus : PDG_t::kKPlus; + bool const isTruePrimary = std::abs(particle.pdgCode()) == expectedPdg && particle.isPhysicalPrimary(); + bool const isSelected = Species == ControlSpecies::Pion ? isSelectedPion(track) : isSelectedKaon(track); + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + + if constexpr (Species == ControlSpecies::Pion) { + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/pion/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/pion/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/pion/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } else { + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/kaon/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/kaon/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/kaon/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillMCPidQa(JetType const& jet, float weight) + { + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track) || !track.has_mcParticle()) { + continue; + } + auto particle = track.template mcParticle_as(); + if (output.fillPionControl.value) { + fillControlMCPidQa(track, particle, weight); + } + if (output.fillKaonControl.value) { + fillControlMCPidQa(track, particle, weight); + } + bool const isTruePrimaryProton = std::abs(particle.pdgCode()) == PDG_t::kProton && particle.isPhysicalPrimary(); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/reconstructedTrueProtonPt"), track.pt(), weight); + } + if (!isSelectedProton(track)) { + continue; + } + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + registry.fill(HIST("mc/pid/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("mc/pid/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/selectedTrueProtonPt"), track.pt(), weight); + registry.fill(HIST("mc/pid/ptResponsePrimaryProton"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillReconstructedSameEvent(CollisionType const& collision, JetsType const& jets, float weight = 1.f) + { + if (!isSelectedCollision(collision, true)) { + return; + } + + if (output.fillQa.value) { + registry.fill(HIST("event/zVertex"), collision.posZ(), weight); + registry.fill(HIST("event/multiplicityFT0C"), collision.multFT0C(), weight); + } + registry.fill(HIST("normalization/sameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(false, 1.f, weight); + } + + for (auto const& jet : jets) { + if (output.fillQa.value && isSelectedJetGeometry(jet)) { + registry.fill(HIST("jet/ptBeforePtSelection"), jet.pt(), weight); + } + if (!isSelectedJet(jet)) { + continue; + } + + registry.fill(HIST("normalization/sameEvent"), 2.f, weight); + if (output.fillQa.value) { + registry.fill(HIST("jet/pt"), jet.pt(), weight); + registry.fill(HIST("jet/eta"), jet.eta(), weight); + registry.fill(HIST("jet/phi"), jet.phi(), weight); + registry.fill(HIST("jet/nConstituents"), static_cast(jet.tracksIds().size()), weight); + registry.fill(HIST("jet/nConstituentsVsPt"), jet.pt(), static_cast(jet.tracksIds().size()), weight); + } + + auto selectedParticles = collectSelectedParticles(jet, output.fillQa.value, weight); + auto const& baryons = selectedParticles.baryons; + if (output.fillQa.value) { + int numberOfProtons = 0; + int numberOfAntiprotons = 0; + for (auto const& baryon : baryons) { + baryon.sign > 0 ? ++numberOfProtons : ++numberOfAntiprotons; + } + registry.fill(HIST("jet/nSelectedBaryons"), static_cast(baryons.size()), weight); + registry.fill(HIST("jet/nProtons"), numberOfProtons, weight); + registry.fill(HIST("jet/nAntiprotons"), numberOfAntiprotons, weight); + registry.fill(HIST("jet/nProtonsVsAntiprotons"), numberOfProtons, numberOfAntiprotons, weight); + registry.fill(HIST("jet/nBaryonsVsPt"), jet.pt(), static_cast(baryons.size()), weight); + } + + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + ChargePairType const chargePairType = getChargePairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + fillSameEventPair(pairType, chargePairType, jet.pt(), deltaEta, deltaPhi, deltaR, weight); + if (output.fillQa.value) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("jet/sameSignPairsVsPt"), jet.pt(), weight); + } else { + registry.fill(HIST("jet/oppositeSignPairsVsPt"), jet.pt(), weight); + } + } + + float const counterBin = pairType == PairType::SameSign ? 3.f : 4.f; + registry.fill(HIST("normalization/sameEvent"), counterBin, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + registry.fill(HIST("normalization/sameEvent"), pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + + void processSameEvent(aod::JetCollision const& collision, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + fillReconstructedSameEvent(collision, jets); + } + PROCESS_SWITCH(BaryonPairInJets, processSameEvent, "Same-event identified baryon pairs in charged jets", true); + + template + void fillMixedJetPair(JetType1 const& jet1, JetType2 const& jet2, float weight) + { + float const jetPtDifference = std::abs(jet1.pt() - jet2.pt()); + int const jetNchDifference = std::abs(static_cast(jet1.tracksIds().size()) - static_cast(jet2.tracksIds().size())); + float const jetEtaDifference = std::abs(jet1.eta() - jet2.eta()); + if (!isMatchedJetPair(jetPtDifference, jetNchDifference, jetEtaDifference)) { + return; + } + + if constexpr (Reference == MixedReference::Random) { + registry.fill(HIST("mixedEvent/jetPtDifference"), jetPtDifference, weight); + registry.fill(HIST("mixedEvent/jetEtaDifference"), jetEtaDifference, weight); + registry.fill(HIST("mixedEvent/jetNchDifference"), jetNchDifference, weight); + } + + constexpr bool CollectControls = Reference == MixedReference::Nominal; + auto selectedParticles1 = collectSelectedParticles(jet1, false, 1.f, CollectControls); + auto selectedParticles2 = collectSelectedParticles(jet2, false, 1.f, CollectControls); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + fillMixedNormalization(2.f, weight); + + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + fillPair(pairType, chargePairType, mixedJetPt, deltaEta, deltaPhi, deltaR, weight); + fillMixedNormalization(pairType == PairType::SameSign ? 3.f : 4.f, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + fillMixedNormalization(pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if constexpr (Reference == MixedReference::Nominal) { + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + + void processMixedEvent(aod::JetCollisions const& collisions, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEvent, "Mixed-event identified baryon pairs in charged jets", true); + + void processMCD(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksMCDWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + float const weight = collision.mcCollision().weight(); + fillReconstructedSameEvent(collision, jets, weight); + if (!isSelectedCollision(collision, false)) { + return; + } + for (auto const& jet : jets) { + if (isSelectedJet(jet)) { + fillMCPidQa(jet, weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCD, "Detector-level MC reconstructed baryon pairs and PID truth QA", false); + + void processMixedEventMCD(aod::JetCollisionsMCD const& collisions, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksMCDWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcdMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + float const weight = collision1.mcCollision().weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCD, "Detector-level MC mixed-event baryon pairs", false); + + void processMCP(aod::JetMcCollision const& collision, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return; + } + float const weight = collision.weight(); + registry.fill(HIST("mc/particle/normalizationSameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + for (auto const& jet : jets) { + if (!isSelectedJet(jet)) { + continue; + } + registry.fill(HIST("mc/jet/allParticlePt"), jet.pt(), weight); + registry.fill(HIST("mc/particle/normalizationSameEvent"), 2.f, weight); + auto selectedParticles = collectParticleCandidates(jet, true, weight); + auto const& baryons = selectedParticles.baryons; + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + fillParticlePair(false, pairType, jet.pt(), deltaEta, deltaPhi, deltaR, weight); + registry.fill(HIST("mc/particle/normalizationSameEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCP, "Particle-level MC baryon pairs", false); + + void processMixedEventMCP(aod::JetMcCollisions const& collisions, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + MCPMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcpMixingCache}; + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (std::abs(collision1.posZ()) > eventCuts.vertexZMax.value || std::abs(collision2.posZ()) > eventCuts.vertexZMax.value) { + continue; + } + float const weight = collision1.weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + auto selectedParticles1 = collectParticleCandidates(jet1, false, weight); + auto selectedParticles2 = collectParticleCandidates(jet2, false, weight); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 2.f, weight); + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + fillParticlePair(true, pairType, 0.5f * (jet1.pt() + jet2.pt()), deltaEta, deltaPhi, deltaR, weight); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCP, "Particle-level MC mixed-event baryon pairs", false); + + void processMatched(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + MatchedChargedMCDJets const& detectorJets, + MatchedChargedMCPJets const&, + JetTracksMCDWithOriginal const&, + aod::JetParticles const&) + { + if (!isSelectedCollision(collision, false)) { + return; + } + float const weight = collision.mcCollision().weight(); + for (auto const& detectorJet : detectorJets) { + if (!isSelectedJet(detectorJet)) { + continue; + } + registry.fill(HIST("mc/jet/allDetectorPt"), detectorJet.pt(), weight); + bool hasAcceptedMatch = false; + for (auto const& particleJet : detectorJet.template matchedJetGeo_as()) { + if (!isSelectedJetGeometry(particleJet)) { + continue; + } + hasAcceptedMatch = true; + registry.fill(HIST("mc/jet/matchedDetectorPt"), detectorJet.pt(), weight); + registry.fill(HIST("mc/jet/matchedParticlePt"), particleJet.pt(), weight); + registry.fill(HIST("mc/jet/responsePt"), particleJet.pt(), detectorJet.pt(), weight); + if (particleJet.pt() > 0.f) { + registry.fill(HIST("mc/jet/relativePtResolution"), particleJet.pt(), (detectorJet.pt() - particleJet.pt()) / particleJet.pt(), weight); + } + float const deltaEta = detectorJet.eta() - particleJet.eta(); + float const deltaPhi = RecoDecay::constrainAngle(detectorJet.phi() - particleJet.phi(), -o2::constants::math::PI); + registry.fill(HIST("mc/jet/matchingDeltaR"), std::hypot(deltaEta, deltaPhi), weight); + break; + } + if (!hasAcceptedMatch) { + registry.fill(HIST("mc/jet/fakeDetectorPt"), detectorJet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMatched, "Matched detector-particle charged-jet response", false); + + void processInclusiveSameEvent(InclusiveCollisions::iterator const& collision, BaryonTracks const& tracks) + { + if (!isSelectedInclusiveCollision(collision)) { + return; + } + registry.fill(HIST("inclusive/event/multiplicity"), collision.multFT0C()); + registry.fill(HIST("inclusive/normalization/sameEvent"), 1.f); + auto baryons = collectInclusiveBaryons(tracks); + registry.fill(HIST("inclusive/normalization/sameEvent"), 2.f, static_cast(baryons.size())); + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + fillInclusivePair(false, baryons[first], baryons[second], 1.f); + registry.fill(HIST("inclusive/normalization/sameEvent"), getPairType(baryons[first], baryons[second]) == PairType::SameSign ? 3.f : 4.f); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveSameEvent, "Inclusive same-event identified baryon pairs", true); + + void processInclusiveMixedEvent(InclusiveCollisions const& collisions, BaryonTracks const& tracks) + { + InclusiveMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto tracksTuple = std::make_tuple(tracks); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, tracksTuple, &inclusiveMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, tracks1, collision2, tracks2] = *iterator; + if (!isSelectedInclusiveCollision(collision1) || !isSelectedInclusiveCollision(collision2)) { + continue; + } + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 1.f, weight); + auto baryons1 = collectInclusiveBaryons(tracks1); + auto baryons2 = collectInclusiveBaryons(tracks2); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 2.f, weight * static_cast(baryons1.size() + baryons2.size())); + for (auto const& baryon1 : baryons1) { + for (auto const& baryon2 : baryons2) { + fillInclusivePair(true, baryon1, baryon2, weight); + registry.fill(HIST("inclusive/normalization/mixedEvent"), getPairType(baryon1, baryon2) == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveMixedEvent, "Inclusive mixed-event identified baryon pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& configContext) +{ + return WorkflowSpec{adaptAnalysisTask(configContext)}; +} From 65550b9d696913ad22e8cfdbc65e34b45b3d4ab5 Mon Sep 17 00:00:00 2001 From: CesarOmarRA <35703156+CesarOmarRA@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:47:38 -0600 Subject: [PATCH 269/648] =?UTF-8?q?[PWGUD]=20upcVmRof:=20kIsVertexITSTPC?= =?UTF-8?q?=20and=20kNoSameBunchPileup=20cuts=20configu=E2=80=A6=20(#18261?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGUD/Tasks/upcVmRof.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PWGUD/Tasks/upcVmRof.cxx b/PWGUD/Tasks/upcVmRof.cxx index 00e3dc6ac7c..985a9b1ab1d 100644 --- a/PWGUD/Tasks/upcVmRof.cxx +++ b/PWGUD/Tasks/upcVmRof.cxx @@ -311,6 +311,8 @@ struct UpcVmRof { Configurable maxTrkDcaZ{"maxTrkDcaZ", 2.0, "max DCA in z of track to vtx (cm)"}; Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin 1e4 means some 28 s"}; Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + Configurable requireVtxItsTpc{"requireVtxItsTpc", false, "require kIsVertexITSTPC"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "require kNoSameBunchPileup"}; //-------------------------------------------------------------------------------- // get ITS ROF info @@ -492,11 +494,11 @@ struct UpcVmRof { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(3); - if (!col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + if (requireVtxItsTpc && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(4); - if (!col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (requireNoSameBunchPileup && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(5); From 7fb1af4e03114393457c4b9a0ed1cc58a9fff26f Mon Sep 17 00:00:00 2001 From: Subhadeep Roy Date: Tue, 13 Oct 2026 12:40:52 +0530 Subject: [PATCH 270/648] [PWGCF] lambdaSpinPolarization: loose-cut derived data for the systematic variations (#18262) --- .../DataModel/LambdaSpinPolarizationDerived.h | 110 ++++ .../TableProducer/CMakeLists.txt | 5 + .../lambdaSpinPolarizationLooseProducer.cxx | 482 ++++++++++++++++++ .../Tasks/lambdaSpinPolarization.cxx | 107 ++++ 4 files changed, 704 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h create mode 100644 PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h new file mode 100644 index 00000000000..6bb0dc95acc --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LambdaSpinPolarizationDerived.h +/// +/// \brief Derived tables of Lambda candidates with loose selections, input of lambdaSpinPolarization +/// \author Subhadeep Roy + +#ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ +#define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace lambdaloosecollision +{ +DECLARE_SOA_COLUMN(Cent, cent, float); +DECLARE_SOA_COLUMN(Mult, mult, float); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); +} // namespace lambdaloosecollision + +DECLARE_SOA_TABLE(LambdaLooseCollisions, "AOD", "LAMBDALOOSECOLS", + o2::soa::Index<>, + lambdaloosecollision::Cent, + lambdaloosecollision::Mult, + lambdaloosecollision::PosX, + lambdaloosecollision::PosY, + lambdaloosecollision::PosZ, + lambdaloosecollision::TimeStamp); +using LambdaLooseCollision = LambdaLooseCollisions::iterator; + +namespace lambdaloosetrack +{ +DECLARE_SOA_INDEX_COLUMN(LambdaLooseCollision, lambdaLooseCollision); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(MassOther, massOther, float); +DECLARE_SOA_COLUMN(PrPx, prPx, float); +DECLARE_SOA_COLUMN(PrPy, prPy, float); +DECLARE_SOA_COLUMN(PrPz, prPz, float); +DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int32_t); +DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int32_t); +DECLARE_SOA_COLUMN(V0Type, v0Type, int8_t); +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(CTau, cTau, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); +DECLARE_SOA_COLUMN(PosNSigmaPr, posNSigmaPr, float); +DECLARE_SOA_COLUMN(PosNSigmaPi, posNSigmaPi, float); +DECLARE_SOA_COLUMN(NegNSigmaPr, negNSigmaPr, float); +DECLARE_SOA_COLUMN(NegNSigmaPi, negNSigmaPi, float); +DECLARE_SOA_COLUMN(PosTpcCrossedRows, posTpcCrossedRows, int16_t); +DECLARE_SOA_COLUMN(NegTpcCrossedRows, negTpcCrossedRows, int16_t); +} // namespace lambdaloosetrack + +DECLARE_SOA_TABLE(LambdaLooseTracks, "AOD", "LAMBDALOOSETRKS", + o2::soa::Index<>, + lambdaloosetrack::LambdaLooseCollisionId, + lambdaloosetrack::Px, + lambdaloosetrack::Py, + lambdaloosetrack::Pz, + lambdaloosetrack::Pt, + lambdaloosetrack::Eta, + lambdaloosetrack::Phi, + lambdaloosetrack::Rap, + lambdaloosetrack::Mass, + lambdaloosetrack::MassOther, + lambdaloosetrack::PrPx, + lambdaloosetrack::PrPy, + lambdaloosetrack::PrPz, + lambdaloosetrack::PosTrackId, + lambdaloosetrack::NegTrackId, + lambdaloosetrack::V0Type, + lambdaloosetrack::CosPA, + lambdaloosetrack::DcaDau, + lambdaloosetrack::DcaV0ToPV, + lambdaloosetrack::CTau, + lambdaloosetrack::MK0Short, + lambdaloosetrack::PosNSigmaPr, + lambdaloosetrack::PosNSigmaPi, + lambdaloosetrack::NegNSigmaPr, + lambdaloosetrack::NegNSigmaPi, + lambdaloosetrack::PosTpcCrossedRows, + lambdaloosetrack::NegTpcCrossedRows); +using LambdaLooseTrack = LambdaLooseTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt index 0669a351d1e..919a0c205e9 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt @@ -33,3 +33,8 @@ o2physics_add_dpl_workflow(longrange-maker SOURCES longrangeMaker.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::TwoPartCorrCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda-spin-polarization-loose-producer + SOURCES lambdaSpinPolarizationLooseProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx new file mode 100644 index 00000000000..7c2b10e88b5 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx @@ -0,0 +1,482 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdaSpinPolarizationLooseProducer.cxx +/// \brief Lambda candidates with loose selections, written as derived data for lambdaSpinPolarization +/// \author Subhadeep Roy + +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +enum LooseEventCutFlow { kLeAll = 1, + kLeTrigger, + kLeTvx, + kLeTFBorder, + kLeItsRofBorder, + kLeItsTpcVtx, + kLeNoSameBunchPileup, + kLeGoodZvtxFT0vsPV, + kLeGoodItsLayers, + kLeCentrality, + kLeVz, + kLeOneV0, + kLeTwoV0, + kLeWritten, + kNLooseEventCutFlow }; + +enum LooseV0Count { kLvAll = 1, + kLvV0Type, + kLvAmbiguousTracks, + kLvKShortRejected, + kLvKinematics, + kLvTopology, + kLvNoHypothesis, + kLvLambdaRow, + kLvAntiLambdaRow, + kLvBothRows, + kNLooseV0Count }; + +enum LooseHypothesis { kLooseLambda = 0, + kLooseAntiLambda, + kNLooseHypotheses }; + +static constexpr std::size_t NV0ForPair = 2; + +enum LooseCentEstimator { kLooseCentFT0M = 0, + kLooseCentFT0C }; + +struct LambdaSpinPolarizationLooseProducer { + + Produces lambdaLooseCollisionTable; + Produces lambdaLooseTrackTable; + + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; + Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; + Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; + Configurable cMaxMult{"cMaxMult", 100.0, "Max centrality percentile"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; + Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; + Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; + Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; + Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; + + Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; + Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; + Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 50, "TPC Min Crossed Rows (loosest of the variations)"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut (loosest of the variations)"}; + Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; + Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.2, "Max DCA V0 to PV (loosest of the variations)"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; + Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; + Configurable cMaxV0CTau{"cMaxV0CTau", 36.0, "Max cTau (cm) (loosest of the variations)"}; + Configurable cMinV0CosPA{"cMinV0CosPA", 0.98, "Min V0 cos(PA) (loosest of the variations)"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window (narrowest of the variations)"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; + Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; + Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + + Configurable cMinV0PerEvent{"cMinV0PerEvent", 2, "Collisions with fewer V0s passing the loose selection are not written"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + struct LooseCandidate { + float px, py, pz, pt, eta, phi, rap, mass, massOther, prPx, prPy, prPz; + int32_t posTrackId, negTrackId; + int8_t v0Type; + float cosPA, dcaDau, dcaV0ToPV, cTau, mK0Short; + float posNSigmaPr, posNSigmaPi, negNSigmaPr, negNSigmaPi; + int16_t posTpcCrossedRows, negTpcCrossedRows; + }; + std::vector candidates; + + float cent = 0., mult = 0.; + + void init(InitContext const&) + { + if (cMinV0PerEvent.value < 1) { + LOGF(fatal, "cMinV0PerEvent must be at least 1, it is %d", cMinV0PerEvent.value); + } + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); + const AxisSpec axisNV0(20, -0.5, 19.5, "V0s written per collision"); + + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNLooseEventCutFlow - 1, 0.5, static_cast(kNLooseEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kLeAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kLeNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kLeCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeOneV0, "#geq 1 V0 with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTwoV0, "#geq 2 V0s with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeWritten, "written"); + + auto hV0 = histos.add("Tracks/hV0Count", "V0s;;V0s", kTH1D, {{kNLooseV0Count - 1, 0.5, static_cast(kNLooseV0Count) - 0.5}}); + hV0->GetXaxis()->SetBinLabel(kLvAll, "all"); + hV0->GetXaxis()->SetBinLabel(kLvV0Type, "rejected: V0 type"); + hV0->GetXaxis()->SetBinLabel(kLvAmbiguousTracks, "rejected: ambiguous tracks"); + hV0->GetXaxis()->SetBinLabel(kLvKShortRejected, "rejected: K^{0}_{S} mass"); + hV0->GetXaxis()->SetBinLabel(kLvKinematics, "rejected: p_{T} / y"); + hV0->GetXaxis()->SetBinLabel(kLvTopology, "rejected: topology"); + hV0->GetXaxis()->SetBinLabel(kLvNoHypothesis, "rejected: no hypothesis"); + hV0->GetXaxis()->SetBinLabel(kLvLambdaRow, "#Lambda candidate"); + hV0->GetXaxis()->SetBinLabel(kLvAntiLambdaRow, "#bar{#Lambda} candidate"); + hV0->GetXaxis()->SetBinLabel(kLvBothRows, "both candidates"); + + histos.add("Tracks/hLambdaMassVsPt", "#Lambda candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Tracks/hAntiLambdaMassVsPt", "#bar{#Lambda} candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Events/hNV0Written", "written collisions", kTH1F, {axisNV0}); + } + + template + bool selCollision(C const& col) + { + histos.fill(HIST("Events/hEventCutFlow"), kLeAll); + if (cCentEstimator == kLooseCentFT0M) { + cent = col.centFT0M(); + } else if (cCentEstimator == kLooseCentFT0C) { + cent = col.centFT0C(); + } + if (cSel8Trig && !col.sel8()) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTrigger); + + if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTvx); + if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTFBorder); + if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsRofBorder); + if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsTpcVtx); + if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeNoSameBunchPileup); + if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodZvtxFT0vsPV); + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodItsLayers); + + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeVz); + + mult = col.multNTracksPV(); + return true; + } + + bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) + { + return pt > ptMin && pt < ptMax && rap < rapMax; + } + + template + bool selTrack(T const& track) + { + if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { + return false; + } + if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + if (track.tpcCrossedRowsOverFindableCls() < cMinTpcCROverCls) { + return false; + } + if (track.tpcNClsShared() > cMaxTpcSharedClusters) { + return false; + } + if (track.tpcChi2NCl() > cMaxChi2Tpc) { + return false; + } + return true; + } + + template + bool selDaughterTracks(V const& v0, T const&, LooseHypothesis const& v0Type) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + if (!selTrack(posTrack) || !selTrack(negTrack)) { + return false; + } + + float dcaProton = 0., dcaPion = 0.; + if (v0Type == kLooseLambda) { + dcaProton = std::abs(v0.dcapostopv()); + dcaPion = std::abs(v0.dcanegtopv()); + } else if (v0Type == kLooseAntiLambda) { + dcaPion = std::abs(v0.dcapostopv()); + dcaProton = std::abs(v0.dcanegtopv()); + } + if (dcaProton < cMinDcaProtonToPV || dcaPion < cMinDcaPionToPV) { + return false; + } + return true; + } + + template + bool topoCutSelection(C const& col, V const& v0, T const&) + { + if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + return false; + } + if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { + return false; + } + if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { + return false; + } + + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { + return false; + } + if (v0.v0cosPA() <= cMinV0CosPA) { + return false; + } + return true; + } + + template + bool selLambdaDauWithTpcPid(T const& postrack, T const& negtrack) + { + float tpcNSigmaPr = 0., tpcNSigmaPi = 0.; + if constexpr (part == kLooseLambda) { + tpcNSigmaPr = postrack.tpcNSigmaPr(); + tpcNSigmaPi = negtrack.tpcNSigmaPi(); + } else { + tpcNSigmaPr = negtrack.tpcNSigmaPr(); + tpcNSigmaPi = postrack.tpcNSigmaPi(); + } + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + } + + template + bool hasAmbiguousDaughters(V const& v0, T const&) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + auto posCC = posTrack.compatibleCollIds(); + auto negCC = negTrack.compatibleCollIds(); + if (posCC.size() > 1 || negCC.size() > 1) { + return true; + } + if ((posCC.size() != 0 && posCC[0] != posTrack.collisionId()) || + (negCC.size() != 0 && negCC[0] != negTrack.collisionId())) { + return true; + } + return false; + } + + using CollisionsRun3 = soa::Join; + using Tracks = soa::Join; + + void processData(CollisionsRun3::iterator const& collision, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, Tracks const& tracks) + { + if (!selCollision(collision)) { + return; + } + + candidates.clear(); + std::size_t nV0 = 0; + for (auto const& v0 : V0s) { + histos.fill(HIST("Tracks/hV0Count"), kLvAll); + if (cV0TypeSelFlag && v0.v0Type() != cV0TypeSelection) { + histos.fill(HIST("Tracks/hV0Count"), kLvV0Type); + continue; + } + if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvAmbiguousTracks); + continue; + } + if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKShortRejected); + continue; + } + const float rap = cDoEtaAnalysis ? std::abs(v0.eta()) : std::abs(v0.yLambda()); + if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKinematics); + continue; + } + if (!topoCutSelection(collision, v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvTopology); + continue; + } + + auto postrack = v0.posTrack_as(); + auto negtrack = v0.negTrack_as(); + const std::array masses = {v0.mLambda(), v0.mAntiLambda()}; + const std::array pid = {selLambdaDauWithTpcPid(postrack, negtrack), selLambdaDauWithTpcPid(postrack, negtrack)}; + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassLambda0; + + std::size_t nRows = 0; + for (auto const& hyp : {kLooseLambda, kLooseAntiLambda}) { + const bool inMassWindow = masses[hyp] > cMinV0Mass && masses[hyp] < cMaxV0Mass; + if (!inMassWindow || !pid[hyp]) { + continue; + } + if (!selDaughterTracks(v0, tracks, hyp)) { + continue; + } + const bool isLambda = hyp == kLooseLambda; + candidates.push_back(LooseCandidate{.px = v0.px(), + .py = v0.py(), + .pz = v0.pz(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .rap = v0.yLambda(), + .mass = masses[hyp], + .massOther = masses[isLambda ? kLooseAntiLambda : kLooseLambda], + .prPx = isLambda ? v0.pxpos() : v0.pxneg(), + .prPy = isLambda ? v0.pypos() : v0.pyneg(), + .prPz = isLambda ? v0.pzpos() : v0.pzneg(), + .posTrackId = static_cast(postrack.index()), + .negTrackId = static_cast(negtrack.index()), + .v0Type = static_cast(hyp), + .cosPA = v0.v0cosPA(), + .dcaDau = v0.dcaV0daughters(), + .dcaV0ToPV = v0.dcav0topv(), + .cTau = ctau, + .mK0Short = v0.mK0Short(), + .posNSigmaPr = postrack.tpcNSigmaPr(), + .posNSigmaPi = postrack.tpcNSigmaPi(), + .negNSigmaPr = negtrack.tpcNSigmaPr(), + .negNSigmaPi = negtrack.tpcNSigmaPi(), + .posTpcCrossedRows = postrack.tpcNClsCrossedRows(), + .negTpcCrossedRows = negtrack.tpcNClsCrossedRows()}); + if (isLambda) { + histos.fill(HIST("Tracks/hV0Count"), kLvLambdaRow); + histos.fill(HIST("Tracks/hLambdaMassVsPt"), masses[hyp], v0.pt()); + } else { + histos.fill(HIST("Tracks/hV0Count"), kLvAntiLambdaRow); + histos.fill(HIST("Tracks/hAntiLambdaMassVsPt"), masses[hyp], v0.pt()); + } + ++nRows; + } + if (nRows == 0) { + histos.fill(HIST("Tracks/hV0Count"), kLvNoHypothesis); + continue; + } + if (nRows == kNLooseHypotheses) { + histos.fill(HIST("Tracks/hV0Count"), kLvBothRows); + } + ++nV0; + } + + if (nV0 >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kLeOneV0); + } + if (nV0 >= NV0ForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kLeTwoV0); + } + if (std::cmp_less(nV0, cMinV0PerEvent.value)) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeWritten); + histos.fill(HIST("Events/hNV0Written"), nV0); + + auto bc = collision.bc_as(); + lambdaLooseCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); + for (auto const& c : candidates) { + lambdaLooseTrackTable(lambdaLooseCollisionTable.lastIndex(), c.px, c.py, c.pz, c.pt, c.eta, c.phi, c.rap, c.mass, c.massOther, c.prPx, c.prPy, c.prPz, + c.posTrackId, c.negTrackId, c.v0Type, c.cosPA, c.dcaDau, c.dcaV0ToPV, c.cTau, c.mK0Short, + c.posNSigmaPr, c.posNSigmaPi, c.negNSigmaPr, c.negNSigmaPi, c.posTpcCrossedRows, c.negTpcCrossedRows); + } + } + PROCESS_SWITCH(LambdaSpinPolarizationLooseProducer, processData, "Run 3 data (or MC reconstructed as data)", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index 8906ab8532b..44daf7bc633 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -14,6 +14,7 @@ ///\author Subhadeep Roy /// \author Yash Patley , +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -1237,6 +1238,112 @@ struct LambdaTableProducer { analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, mcParticles); } PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); + + template + bool selLooseCandidate(T const& cand) + { + if (cKshortRejFlag && (std::abs(cand.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); + return false; + } + + auto passMassAndPid = [&](float mass, float tpcNSigmaPr, float tpcNSigmaPi) { + return (mass > cMinV0Mass && mass < cMaxV0Mass) && (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + }; + const bool isLambda = cand.v0Type() == kLambda; + const bool ownFlag = isLambda ? passMassAndPid(cand.mass(), cand.posNSigmaPr(), cand.negNSigmaPi()) : passMassAndPid(cand.mass(), cand.negNSigmaPr(), cand.posNSigmaPi()); + const bool otherFlag = isLambda ? passMassAndPid(cand.massOther(), cand.negNSigmaPr(), cand.posNSigmaPi()) : passMassAndPid(cand.massOther(), cand.posNSigmaPr(), cand.negNSigmaPi()); + if (!ownFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); + return false; + } + if (otherFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsLambdaOrAntiLambda); + + if (cand.posTpcCrossedRows() <= cMinTpcCrossedRows || cand.negTpcCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); + + float rap = cDoEtaAnalysis ? std::abs(cand.eta()) : std::abs(cand.rap()); + if (!kinCutSelection(cand.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0KinCuts); + + if (cand.dcaDau() <= cMinV0DcaDaughters || cand.dcaDau() >= cMaxV0DcaDaughters) { + return false; + } + if (cand.dcaV0ToPV() <= cMinDcaV0ToPV || cand.dcaV0ToPV() >= cMaxDcaV0ToPV) { + return false; + } + if (cand.cTau() <= cMinV0CTau || cand.cTau() >= cMaxV0CTau) { + return false; + } + if (cand.cosPA() <= cMinV0CosPA) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); + return true; + } + + void processDerivedData(aod::LambdaLooseCollisions::iterator const& collision, aod::LambdaLooseTracks const& looseTracks) + { + histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); + cent = collision.cent(); + mult = collision.mult(); + if (cent <= cMinMult || cent >= cMaxMult) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (collision.posZ() <= cMinZVtx || collision.posZ() >= cMaxZVtx) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); + + histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); + histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); + + lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), collision.timeStamp()); + + std::size_t nWritten = 0; + for (auto const& cand : looseTracks) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllV0Tracks); + if (!selLooseCandidate(cand)) { + continue; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllSelPassed); + + if (cand.v0Type() == kLambda) { + histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } else { + histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } + + lambdaTrackTable(lambdaCollisionTable.lastIndex(), cand.px(), cand.py(), + cand.pz(), cand.pt(), cand.eta(), cand.phi(), cand.rap(), cand.mass(), + cand.prPx(), cand.prPy(), cand.prPz(), + static_cast(cand.posTrackId()), + static_cast(cand.negTrackId()), + cand.cosPA(), cand.dcaDau(), cand.v0Type(), + static_cast(kPrimary), 1.f); + ++nWritten; + } + + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); + } + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); + } + } + PROCESS_SWITCH(LambdaTableProducer, processDerivedData, "Process the derived data of lambda-spin-polarization-loose-producer", false); }; struct LambdaTracksExtProducer { From faf54fe15d7169674ba75da002e18d9205388956 Mon Sep 17 00:00:00 2001 From: Daiki Sekihata Date: Tue, 13 Oct 2026 10:58:02 +0200 Subject: [PATCH 271/648] [PWGEM/Dilepton] remove unnecessary tasks (#18265) --- PWGEM/Dilepton/Core/DileptonSV.h | 2068 ---------------- PWGEM/Dilepton/Core/DileptonSVMC.h | 2941 ----------------------- PWGEM/Dilepton/Tasks/CMakeLists.txt | 20 - PWGEM/Dilepton/Tasks/dielectronSV.cxx | 29 - PWGEM/Dilepton/Tasks/dielectronSVMC.cxx | 29 - PWGEM/Dilepton/Tasks/dimuonSV.cxx | 29 - PWGEM/Dilepton/Tasks/dimuonSVMC.cxx | 29 - 7 files changed, 5145 deletions(-) delete mode 100644 PWGEM/Dilepton/Core/DileptonSV.h delete mode 100644 PWGEM/Dilepton/Core/DileptonSVMC.h delete mode 100644 PWGEM/Dilepton/Tasks/dielectronSV.cxx delete mode 100644 PWGEM/Dilepton/Tasks/dielectronSVMC.cxx delete mode 100644 PWGEM/Dilepton/Tasks/dimuonSV.cxx delete mode 100644 PWGEM/Dilepton/Tasks/dimuonSVMC.cxx diff --git a/PWGEM/Dilepton/Core/DileptonSV.h b/PWGEM/Dilepton/Core/DileptonSV.h deleted file mode 100644 index fb8232e344f..00000000000 --- a/PWGEM/Dilepton/Core/DileptonSV.h +++ /dev/null @@ -1,2068 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over leptons. -// Please write to: daiki.sekihata@cern.ch - -#ifndef PWGEM_DILEPTON_CORE_DILEPTONSV_H_ -#define PWGEM_DILEPTON_CORE_DILEPTONSV_H_ - -#include "PWGEM/Dilepton/Core/DielectronCut.h" -#include "PWGEM/Dilepton/Core/DimuonCut.h" -#include "PWGEM/Dilepton/Core/EMEventCut.h" -#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/Dilepton/Utils/EMFwdTrack.h" -#include "PWGEM/Dilepton/Utils/EMTrack.h" -#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" -#include "PWGEM/Dilepton/Utils/EventHistograms.h" -#include "PWGEM/Dilepton/Utils/EventMixingHandler.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/RecoDecay.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/fwdtrackUtilities.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using MyCollisions = o2::soa::Join; -using MyCollision = MyCollisions::iterator; - -using MyCollisionsSWT = o2::soa::Join; -using MyCollisionSWT = MyCollisionsSWT::iterator; - -using MyElectrons = o2::soa::Join; -using MyElectron = MyElectrons::iterator; -using FilteredMyElectrons = o2::soa::Filtered; -using FilteredMyElectron = FilteredMyElectrons::iterator; - -using MyElectronsSCT = o2::soa::Join; -using MyElectronSCT = MyElectronsSCT::iterator; -using FilteredMyElectronsSCT = o2::soa::Filtered; -using FilteredMyElectronSCT = FilteredMyElectronsSCT::iterator; - -using MyMuons = o2::soa::Join; -using MyMuon = MyMuons::iterator; -using FilteredMyMuons = o2::soa::Filtered; -using FilteredMyMuon = FilteredMyMuons::iterator; - -using MyEMH_electron = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMTrack>; -using MyEMH_muon = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMFwdTrack>; - -template -struct DileptonSV { - // Configurables - o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - o2::framework::Configurable matCorrType{"matCorrType", 1, "0: none, 1: TGeo, 2: LUT, else: none"}; - - o2::framework::Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "flag to apply resolution correction for flow analysis"}; - o2::framework::Configurable spresoPath{"spresoPath", "Users/d/dsekihat/PWGEM/dilepton/Qvector/resolution/LHC23zzh/pass3/test", "Path to SP resolution file"}; - o2::framework::Configurable spresoHistName{"spresoHistName", "h1_R2_FT0M_BPos_BNeg", "histogram name of SP resolution file"}; - - o2::framework::Configurable cfgAnalysisType{"cfgAnalysisType", static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC), "kQC:0, kUPC:1, kFlowV2SP:2, kFlowV3SP:3, kPolarization:4, kHFll:5, kBootstrapv2:6, kFlowV2EP:7"}; - o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 2, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; - o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - - o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; - o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; - - o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity - o2::framework::ConfigurableAxis ConfLog10Chi2PCABins{"ConfLog10Chi2PCABins", {1, -12.f, 0.f}, "log10 of chi2PCA bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDLBins{"ConfDLBins", {1, 0.f, 10.f}, "decay length bins for output histograms"}; - o2::framework::ConfigurableAxis ConfCPABins{"ConfCPABins", {o2::framework::VARIABLE_WIDTH, -1, -0.95, -0.9, -0.85, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1}, "cpa bins for output histograms"}; - - o2::framework::ConfigurableAxis ConfSPBins{"ConfSPBins", {200, -5, 5}, "SP bins for flow analysis"}; - o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {20, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; - o2::framework::ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; - o2::framework::ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {15, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> - - o2::framework::Configurable cfgNumBootstrapSamples{"cfgNumBootstrapSamples", 1, "Number of Bootstrap Samples"}; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "mixingGroup"; - o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; - o2::framework::Configurable cfgEP2Estimator{"cfgEP2Estimator", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; - o2::framework::Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; - o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; - o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; - o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {20, -10.0f, 10.f}, "Mixing bins - z-vertex"}; - o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 110.f}, "Mixing bins - centrality"}; - o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {1, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; - o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -2, 1e+10}, "Mixing bins - occupancy"}; - } mixingGroup; - - EMEventCut fEMEventCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "eventcut_group"; - o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; - o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; - o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; - o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; - o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. - o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. - o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; - o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; - o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; - o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; - // for RCT - o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; - o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; - o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; - o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; - - o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; - o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; - o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; - } eventcuts; - - DielectronCut fDielectronCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dielectroncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut"}; - o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; - o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; - o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; - o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.2, "min dphi between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; - o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; - // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; - - o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; - o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; - o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; - o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; - o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; - o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; - o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; - o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; - o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; - o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; - o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; - o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; - o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; - o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; - // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; - // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; - o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. - o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; - o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; - o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; - - o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; - o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; - // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; - // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; - o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - - // configuration for PID ML - // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; - // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - - o2::framework::Configurable> binsMLSCT{"binsMLSCT", std::vector{0.1, 0.4, 0.6, 0.8, 1, 2, 4, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLSCTeT_prompt_hc{"cutsMLSCTeT_prompt_hc", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for prompt hc"}; - o2::framework::Configurable> cutsMLSCTeT_nonprompt_hc{"cutsMLSCTeT_nonprompt_hc", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for nonprompt hc"}; - o2::framework::Configurable> cutsMLSCTeT_hb{"cutsMLSCTeT_hb", std::vector{0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8}, "ML cuts per bin for hb"}; - } dielectroncuts; - - DimuonCut fDimuonCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dimuoncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; - o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; - o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; - o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; - // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; - o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; - o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; - o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; - } dimuoncuts; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "zorroGroup"; - o2::framework::Configurable cfg_swt_name{"cfg_swt_name", "fLMeeIMR", "desired software trigger name. 1 trigger per 1 task."}; // 1 trigger per 1 task - o2::framework::Configurable ccdbPathSoftwareTrigger{"ccdbPathSoftwareTrigger", "EventFiltering/Zorro/", "ccdb path for ZORRO objects"}; - o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; - } zorroGroup; - - Zorro zorro; - int mToIidx = 0; - int mTOICounter = 0; - int mATCounter = 0; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDZIni{"maxDZIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } dfGroup; // for DCAFitterN - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "fdfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDXIni{"maxDXIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } fdfGroup; // for FwdDCAFitterN - - o2::vertexing::DCAFitterN<2> mDCAFitter; - o2::vertexing::FwdDCAFitterN<2> mFwdDCAFitter; - - o2::aod::rctsel::RCTFlagsChecker rctChecker; - o2::framework::Service ccdb; - int mRunNumber{0}; - float d_bz{0}; - - o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - // static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; - static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; - - std::mt19937 engine; - std::vector cent_bin_edges; - std::vector zvtx_bin_edges; - std::vector ep_bin_edges; - std::vector occ_bin_edges; - - int nmod = -1; // this is for flow analysis - float leptonM1 = 0.f; - float leptonM2 = 0.f; - - float beamM1 = o2::constants::physics::MassProton; // mass of beam - float beamM2 = o2::constants::physics::MassProton; // mass of beam - float beamE1 = 0.f; // beam energy - float beamE2 = 0.f; // beam energy - float beamP1 = 0.f; // beam momentum - float beamP2 = 0.f; // beam momentum - TH2D* h2sp_resolution = nullptr; - - void init(o2::framework::InitContext& /*context*/) - { - mRunNumber = 0; - d_bz = 0; - mToIidx = 0; - mTOICounter = 0; - mATCounter = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); - - if (mixingGroup.ConfVtxBins.value[0] == o2::framework::VARIABLE_WIDTH) { - zvtx_bin_edges = std::vector(mixingGroup.ConfVtxBins.value.begin(), mixingGroup.ConfVtxBins.value.end()); - zvtx_bin_edges.erase(zvtx_bin_edges.begin()); - // for (const auto& edge : zvtx_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); - // } - } else { - int nbins = static_cast(mixingGroup.ConfVtxBins.value[0]); - float xmin = static_cast(mixingGroup.ConfVtxBins.value[1]); - float xmax = static_cast(mixingGroup.ConfVtxBins.value[2]); - zvtx_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - zvtx_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); - } - } - - if (mixingGroup.ConfCentBins.value[0] == o2::framework::VARIABLE_WIDTH) { - cent_bin_edges = std::vector(mixingGroup.ConfCentBins.value.begin(), mixingGroup.ConfCentBins.value.end()); - cent_bin_edges.erase(cent_bin_edges.begin()); - // for (const auto& edge : cent_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: cent_bin_edges = %f", edge); - // } - } else { - int nbins = static_cast(mixingGroup.ConfCentBins.value[0]); - float xmin = static_cast(mixingGroup.ConfCentBins.value[1]); - float xmax = static_cast(mixingGroup.ConfCentBins.value[2]); - cent_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - cent_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); - } - } - - if (mixingGroup.ConfEPBins.value[0] == o2::framework::VARIABLE_WIDTH) { - ep_bin_edges = std::vector(mixingGroup.ConfEPBins.value.begin(), mixingGroup.ConfEPBins.value.end()); - ep_bin_edges.erase(ep_bin_edges.begin()); - // for (const auto& edge : ep_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: ep_bin_edges = %f", edge); - // } - } else { - int nbins = static_cast(mixingGroup.ConfEPBins.value[0]); - float xmin = static_cast(mixingGroup.ConfEPBins.value[1]); - float xmax = static_cast(mixingGroup.ConfEPBins.value[2]); - ep_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - ep_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); - } - } - - // LOGF(info, "mixingGroup.cfgOccupancyEstimator = %d", mixingGroup.cfgOccupancyEstimator.value); - if (mixingGroup.ConfOccupancyBins.value[0] == o2::framework::VARIABLE_WIDTH) { - occ_bin_edges = std::vector(mixingGroup.ConfOccupancyBins.value.begin(), mixingGroup.ConfOccupancyBins.value.end()); - occ_bin_edges.erase(occ_bin_edges.begin()); - // for (const auto& edge : occ_bin_edges) { - // LOGF(info, "o2::framework::VARIABLE_WIDTH: occ_bin_edges = %f", edge); - // } - } else { - int nbins = static_cast(mixingGroup.ConfOccupancyBins.value[0]); - float xmin = static_cast(mixingGroup.ConfOccupancyBins.value[1]); - float xmax = static_cast(mixingGroup.ConfOccupancyBins.value[2]); - occ_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - occ_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - // LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); - } - } - - emh_pos = new TEMH(mixingGroup.ndepth); - emh_neg = new TEMH(mixingGroup.ndepth); - - DefineEMEventCut(); - addhistograms(); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - DefineDielectronCut(); - leptonM1 = o2::constants::physics::MassElectron; - leptonM2 = o2::constants::physics::MassElectron; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - DefineDimuonCut(); - leptonM1 = o2::constants::physics::MassMuon; - leptonM2 = o2::constants::physics::MassMuon; - } - - fRegistry.add("Event/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", o2::framework::HistType::kTH1D, {{10001, -0.5, 10000.5}}, false); - const o2::framework::AxisSpec axis_mix_zvtx{mixingGroup.ConfVtxBins, "Z_{vtx} (cm)"}; - const o2::framework::AxisSpec axis_mix_cent{mixingGroup.ConfCentBins, "centrality (%)"}; - const o2::framework::AxisSpec axis_mix_ep2{mixingGroup.ConfEPBins, "event plane of 2nd harmonics (rad.)"}; - const o2::framework::AxisSpec axis_mix_occ{mixingGroup.ConfOccupancyBins, "occupancy"}; - fRegistry.add("Event/hsMixCounter", "mixed event counter", o2::framework::HistType::kTHnSparseD, {axis_mix_zvtx, axis_mix_cent, axis_mix_ep2, axis_mix_occ}, false); - - if (doprocessTriggerAnalysis) { - LOGF(info, "Trigger analysis is enabled. Desired trigger name = %s", zorroGroup.cfg_swt_name.value.data()); - fRegistry.add("Event/trigger/hInspectedTVX", "inspected TVX;run number;N_{TVX}", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 - fRegistry.add("Event/trigger/hScaler", "trigger counter before DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 - fRegistry.add("Event/trigger/hSelection", "trigger counter after DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 - fRegistry.add("Event/trigger/hAnalysedTrigger", Form("analysed trigger %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 - fRegistry.add("Event/trigger/hAnalysedToI", Form("analysed ToI %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 - } - - if (doprocessNorm) { - fRegistry.addClone("Event/before/hCollisionCounter", "Event/norm/hCollisionCounter"); - fRegistry.add("Event/norm/hZvtx", "hZvtx;Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{100, -50, +50}}, false); - } - if (doprocessBC) { - auto hTVXCounter = fRegistry.add("BC/hTVXCounter", "TVX counter", o2::framework::HistType::kTH1D, {{6, -0.5f, 5.5f}}); - hTVXCounter->GetXaxis()->SetBinLabel(1, "TVX"); - hTVXCounter->GetXaxis()->SetBinLabel(2, "TVX && NoTFB"); - hTVXCounter->GetXaxis()->SetBinLabel(3, "TVX && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(4, "TVX && GoodRCT"); - hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); - } - - mDCAFitter.setPropagateToPCA(dfGroup.propagateToPCA); - mDCAFitter.setMaxR(dfGroup.maxR); - mDCAFitter.setMaxDZIni(dfGroup.maxDZIni); - mDCAFitter.setMinParamChange(dfGroup.minParamChange); - mDCAFitter.setMinRelChi2Change(dfGroup.minRelChi2Change); - mDCAFitter.setUseAbsDCA(dfGroup.useAbsDCA); - mDCAFitter.setWeightedFinalPCA(dfGroup.useWeightedFinalPCA); - mDCAFitter.setMaxChi2(std::pow(10, dfGroup.maxLog10Chi2PCA)); - - mFwdDCAFitter.setPropagateToPCA(fdfGroup.propagateToPCA); - mFwdDCAFitter.setMaxR(fdfGroup.maxR); - mFwdDCAFitter.setMaxDXIni(fdfGroup.maxDXIni); - mFwdDCAFitter.setMinParamChange(fdfGroup.minParamChange); - mFwdDCAFitter.setMinRelChi2Change(fdfGroup.minRelChi2Change); - mFwdDCAFitter.setUseAbsDCA(fdfGroup.useAbsDCA); - mFwdDCAFitter.setMaxChi2(std::pow(10, fdfGroup.maxLog10Chi2PCA)); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - switch (matCorrType.value) { - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kNone): - mFwdDCAFitter.setTGeoMat(false); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kTGeo): - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - mFwdDCAFitter.setTGeoMat(true); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kLUT): { - LOGF(info, "LUT correction requested, loading LUT"); - o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - mFwdDCAFitter.setMatLUT(lut); - break; - } - default: - LOGF(info, "no correction requested, loading LUT by default!"); - // lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - break; - } - } - } - - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } - - mDCAFitter.setBz(d_bz); - mFwdDCAFitter.setBz(d_bz); - - // zorro - if constexpr (isTriggerAnalysis) { - zorro.setCCDBpath(zorroGroup.ccdbPathSoftwareTrigger); - zorro.setBCtolerance(zorroGroup.bcMarginForSoftwareTrigger); // this does nothing. - mToIidx = zorro.initCCDB(ccdb.service, collision.runNumber(), collision.timestamp(), zorroGroup.cfg_swt_name.value)[0]; - zorro.populateHistRegistry(fRegistry, collision.runNumber()); - - uint64_t nInspectedTVX = zorro.getInspectedTVX()->GetBinContent(1); - uint64_t nScalers = zorro.getScalers()->GetBinContent(zorro.getScalers()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); - uint64_t nSelections = zorro.getSelections()->GetBinContent(zorro.getSelections()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); - LOGF(info, "run number %d: total inspected TVX events = %llu, scalers = %llu, selections = %llu", collision.runNumber(), nInspectedTVX, nScalers, nSelections); - - fRegistry.fill(HIST("Event/trigger/hInspectedTVX"), collision.runNumber(), nInspectedTVX); - fRegistry.fill(HIST("Event/trigger/hScaler"), collision.runNumber(), nScalers); - fRegistry.fill(HIST("Event/trigger/hSelection"), collision.runNumber(), nSelections); - } - - mRunNumber = collision.runNumber(); - fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); - - auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); - int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); - int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); - int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); - int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); - beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); - beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); - beamM1 = o2::constants::physics::MassProton * beamA1; - beamM2 = o2::constants::physics::MassProton * beamA2; - beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); - beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); - LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); - - if (cfgApplySPresolution) { - auto list = ccdb->getForTimeStamp(spresoPath, collision.timestamp()); - h2sp_resolution = reinterpret_cast(list->FindObject(spresoHistName.value.data())); - LOGF(info, "h2sp_resolution.GetBinContent(40, 1) = %f", h2sp_resolution->GetBinContent(40, 1)); - } - } - - ~DileptonSV() - { - delete emh_pos; - emh_pos = 0x0; - delete emh_neg; - emh_neg = 0x0; - - used_trackIds_per_col.clear(); - used_trackIds_per_col.shrink_to_fit(); - map_mixed_eventId_to_globalBC.clear(); - - delete h2sp_resolution; - } - - void addhistograms() - { - const std::string qvec_det_names[7] = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg", "FV0A"}; - - std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; - std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; - std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; - std::string pair_y_axis_title = "y_{ll}"; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - mass_axis_title = "m_{ee} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,ee} (GeV/c)"; - pair_y_axis_title = "y_{ee}"; - pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; - if (dielectroncuts.cfgDCAType == 1) { - pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; - pair_dca_axis_title = "DCA_{#mu#mu}^{XY} (#sigma)"; - pair_y_axis_title = "y_{#mu#mu}"; - } - - // pair info - const o2::framework::AxisSpec axis_mass{ConfMllBins, mass_axis_title}; - const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; - const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; - const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; - const o2::framework::AxisSpec axis_chi2PCA{ConfLog10Chi2PCABins, "log_{10}(#chi^{2}_{PCA})"}; - const o2::framework::AxisSpec axis_dl{ConfDLBins, "decay length (#sigma)"}; - const o2::framework::AxisSpec axis_cpa{ConfCPABins, "cos(#theta_{p})"}; - - if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.add("Pair/same/uls/hDeltaEtaDeltaPhi", "#Delta#eta-#Delta#varphi between 2 tracks;#Delta#varphi (rad.);#Delta#eta;", o2::framework::HistType::kTH2D, {{360, -M_PI, M_PI}, {400, -2, +2}}, true); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.add("Pair/same/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); // phiv is only for dielectron - } - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { - const o2::framework::AxisSpec axis_aco{10, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; - const o2::framework::AxisSpec axis_asym_pt{10, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; - const o2::framework::AxisSpec axis_dphi_l_ll{18, 0, M_PI, "#Delta#varphi = #varphi_{l} - #varphi_{ll} (rad.)"}; - - std::string frameName = "CS"; - if (cfgPolarizationFrame == 0) { - frameName = "CS"; - } else if (cfgPolarizationFrame == 1) { - frameName = "HX"; - } else { - LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; - } - const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_aco, axis_asym_pt, axis_dphi_l_ll, axis_cos_theta}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { - if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { - nmod = 2; - } - - const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; - - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); - - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { - std::string frameName = "CS"; - if (cfgPolarizationFrame == 0) { - frameName = "CS"; - } else if (cfgPolarizationFrame == 1) { - frameName = "HX"; - } else { - LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; - } - - const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; - const o2::framework::AxisSpec axis_phi{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; - const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, true); - - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { - const o2::framework::AxisSpec axis_dphi_ll{36, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll - const o2::framework::AxisSpec axis_deta_ll{40, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_dphi_ll, axis_deta_ll}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { - nmod = 2; - const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; - const o2::framework::AxisSpec axis_bootstrap{cfgNumBootstrapSamples, 0.5, static_cast(cfgNumBootstrapSamples) + 0.5, "sample"}; // for bootstrap samples - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp, axis_bootstrap}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); - o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistogramsBootstrap(&fRegistry, cfgNumBootstrapSamples); - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { - if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { - nmod = 2; - } - - // similar to Eq.1 in PRL 126, 162001 (2021) - const o2::framework::AxisSpec axis_ep{4, 0, 2 * M_PI / nmod, Form("#varphi_{ll} #minus #Psi_{%d}^{%s}", nmod, qvec_det_names[cfgQvecEstimator].data())}; - - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_ep}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - - fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); - fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); - } else { // same as kQC to avoid seg. fault - fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y}, true); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - } - - // event info - if (nmod == 2) { - o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<2>(&fRegistry); - } else { - o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<-1>(&fRegistry); - } - fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[mixingGroup.cfgEP2Estimator].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); - fRegistry.add("Event/after/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[mixingGroup.cfgEP2Estimator].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); - } - - void DefineEMEventCut() - { - fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); - fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); - fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); - fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); - fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); - fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); - fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); - fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); - fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); - fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); - fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); - fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); - fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); - fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); - fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); - fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); - fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); - fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); - } - - void DefineDielectronCut() - { - fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); - - // for pair - fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); - fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); - fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); - fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma - fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); - fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); - fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); - fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); - // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); - - // for track - fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); - fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, dielectroncuts.cfg_max_eta_track); - fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); - fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); - fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); - fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); - fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); - fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); - fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); - fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); - fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); - fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); - fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); - fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); - fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); - fDielectronCut.SetChi2TOF(0, dielectroncuts.cfg_max_chi2tof); - // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); - fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); - - // for eID - fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); - fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); - // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); - fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); - fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); - fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); - fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); - fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); - - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut - std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); - } - std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); - } - fDielectronCut.SetMLThresholds(binsML, thresholdsML); - } // end of PID ML - } - - void DefineDimuonCut() - { - fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); - - // for pair - fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); - fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); - fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); - fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); - fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); - - // for track - fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); - fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); - fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); - fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); - fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); - fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); - fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); - fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); - // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); - fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); - fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); - fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); - fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); - fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); - fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); - fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons - fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); - fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); - } - - template - bool foundHFSV(TTrack const& track) - { - int ptbin = lower_bound(dielectroncuts.binsMLSCT.value.begin(), dielectroncuts.binsMLSCT.value.end(), track.pt()) - dielectroncuts.binsMLSCT.value.begin() - 1; - if (ptbin < 0) { - ptbin = 0; - } else if (static_cast(dielectroncuts.binsMLSCT.value.size()) - 2 < ptbin) { - ptbin = static_cast(dielectroncuts.binsMLSCT.value.size()) - 2; - } - - for (int i = 0; i < static_cast(track.nSV()); i++) { - auto probaSCT = track.probaSCT(i); - // LOGF(info, "track.globalIndex() = %d, pt = %f, i = %d, probaSCT[0] = %f, probaSCT[1] = %f, probaSCT[2] = %f, probaSCT[3] = %f", track.globalIndex(), track.pt(), i, probaSCT[0], probaSCT[1], probaSCT[2], probaSCT[3]); - if (probaSCT[1] > dielectroncuts.cutsMLSCTeT_prompt_hc.value[ptbin] || probaSCT[2] > dielectroncuts.cutsMLSCTeT_nonprompt_hc.value[ptbin] || probaSCT[3] > dielectroncuts.cutsMLSCTeT_hb.value[ptbin]) { - return true; - } - } - return false; - } - - template - bool isGoodQvector(TQvectors const& qvectors) - { - bool is_good = true; - for (const auto& qn : qvectors[nmod]) { - if (std::fabs(qn[0]) > 20.f || std::fabs(qn[1]) > 20.f) { - is_good = false; - break; - } - } - return is_good; - } - - float getSPresolution(const float centrality, const int occupancy) - { - if (h2sp_resolution == nullptr) { - return 1.f; - } else { - int binId_cen = h2sp_resolution->GetXaxis()->FindBin(centrality); - int binId_occ = h2sp_resolution->GetYaxis()->FindBin(occupancy); - - if (centrality < h2sp_resolution->GetXaxis()->GetXmin()) { - binId_cen = 1; - } - if (h2sp_resolution->GetXaxis()->GetXmax() < centrality) { - binId_cen = h2sp_resolution->GetXaxis()->GetNbins(); - } - - if (occupancy < h2sp_resolution->GetYaxis()->GetXmin()) { - binId_occ = 1; - } - if (h2sp_resolution->GetYaxis()->GetXmax() < occupancy) { - binId_occ = h2sp_resolution->GetYaxis()->GetNbins(); - } - return h2sp_resolution->GetBinContent(binId_cen, binId_occ); - } - } - - struct dileptonSV { - float pt1{999}; - float eta1{999}; - float phi1{999}; - - float pt2{999}; - float eta2{999}; - float phi2{999}; - - // float mass{999}; - // float pt{999}; - // float rapidity{999}; - - float chi2PCA{999}; - float cpa{999}; - float lxy{999}; - float lz{999}; - float lxyz{999}; - float lxyErr{999}; - float lzErr{999}; - float lxyzErr{999}; - }; - - template - bool findSV(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; - return false; - } - candidate.chi2PCA = mDCAFitter.getChi2AtPCACandidate(); - if (dfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - - mDCAFitter.propagateTracksToVertex(); - const auto& secondaryVertex = mDCAFitter.getPCACandidate(); - auto trackParCov0 = mDCAFitter.getTrack(0); - auto trackParCov1 = mDCAFitter.getTrack(1); - - // get track momenta - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - std::array pvec0{}; - std::array pvec1{}; - trackParCov0.getPxPyPzGlo(pvec0); - trackParCov1.getPxPyPzGlo(pvec1); - std::array momDilepton = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - bool findSVFwd(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mFwdDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". FwdDCAFitterN cannot work, skipping the candidate."; - return false; - } - mFwdDCAFitter.FwdpropagateTracksToVertex(); - - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - const auto& secondaryVertex = mFwdDCAFitter.getPCACandidate(); - candidate.chi2PCA = mFwdDCAFitter.getChi2AtPCACandidate(); - if (fdfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - auto trackParCov0 = mFwdDCAFitter.getTrack(0); - auto trackParCov1 = mFwdDCAFitter.getTrack(1); - std::array momDilepton = {static_cast(trackParCov0.getPx() + trackParCov1.getPx()), static_cast(trackParCov0.getPy() + trackParCov1.getPy()), static_cast(trackParCov0.getPz() + trackParCov1.getPz())}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mFwdDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector& weightvector) - { - dileptonSV candidate; - if constexpr (ev_id == 0) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - auto trackParCov1 = getTrackParCov(t1); - auto trackParCov2 = getTrackParCov(t2); - if (!findSV(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - if constexpr (withSCT) { - if (foundHFSV(t1) || foundHFSV(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - auto trackParCov1 = o2::aod::fwdtrackutils::getTrackParCovFwd(t1, t1); - auto trackParCov2 = o2::aod::fwdtrackutils::getTrackParCovFwd(t2, t2); - if (!findSVFwd(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - } - } else if constexpr (ev_id == 1) { // for mixed event - candidate.pt1 = t1.pt(); - candidate.eta1 = t1.eta(); - candidate.phi1 = t1.phi(); - candidate.pt2 = t2.pt(); - candidate.eta2 = t2.eta(); - candidate.phi2 = t2.phi(); - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - - float weight = 1.f; - if constexpr (ev_id == 0) { - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - // LOGF(info, "ev_id = %d, t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", ev_id, t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); - } - - // ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v1(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float pair_dca = 999.f; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); - if (dielectroncuts.cfgDCAType == 1) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); - } - - if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { - float deta = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); - float dphi = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi - - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDeltaEtaDeltaPhi"), dphi, deta, weight); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsPhiV"), phiv, v12.M(), weight); - } - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDeltaEtaDeltaPhi"), dphi, deta, weight); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsPhiV"), phiv, v12.M(), weight); - } - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDeltaEtaDeltaPhi"), dphi, deta, weight); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsPhiV"), phiv, v12.M(), weight); - } - } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); - float aco = 1.f - std::fabs(dphi) / M_PI; - float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); - float dphi_l_ll = v1.Phi() - v12.Phi(); - o2::math_utils::bringToPMPi(dphi_l_ll); - - float cos_thetaPol = 999, phiPol = 999.f; - auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; - auto arrD = t1.sign() * t1.pt() > t2.sign() * t2.pt() ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}; - - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } - - o2::math_utils::bringToPMPi(phiPol); - - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); - } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { - std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; - std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; - std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; - std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; - std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; - std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; - std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; - - std::vector>> qvectors = { - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics - {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics - }; - - if constexpr (ev_id == 0) { - float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); - } - } else if constexpr (ev_id == 1) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } - } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { - float cos_thetaPol = 999, phiPol = 999.f; - auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; - auto random_sign = std::pow(-1, engine() % 2); // -1^0 = +1 or -1^1 = -1; - auto arrD = t1.sign() * t2.sign() < 0 ? (t1.sign() > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}) : (random_sign > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}); - - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } - - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { - float dphi = v1.Phi() - v2.Phi(); - dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf); - float deta = v1.Eta() - v2.Eta(); - - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); - } - - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { - std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; - std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; - std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; - std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; - std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; - std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; - std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; - - std::vector>> qvectors = { - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics - {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics - }; - - if constexpr (ev_id == 0) { - // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); - - float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); - for (int i = 0; i < cfgNumBootstrapSamples; i++) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); - } - } - } else if constexpr (ev_id == 1) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } - } - } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { - if constexpr (ev_id == 0) { - std::vector> eventplanes = { - {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 0th harmonics - {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 1st harmonics - {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}, // 2nd harmonics - }; - - float ep = RecoDecay::constrainAngle(eventplanes[nmod][cfgQvecEstimator], -o2::constants::math::PI / nmod, static_cast(nmod)); - float phi_ll = RecoDecay::constrainAngle(v12.Phi(), -o2::constants::math::PI / nmod, 1U); - float dphi_ll_ep = std::fabs(RecoDecay::constrainAngle(phi_ll - ep, 0.f, static_cast(nmod))); - - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); - } - } else if constexpr (ev_id == 1) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } - } - } else { // same as kQC to avoid seg. fault - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); - } - } - - // store tracks for event mixing without double counting - if constexpr (ev_id == 0) { - std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { - used_trackIds_per_col.emplace_back(t1.globalIndex()); - if (mixingGroup.cfgDoMix) { - if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ(), - // t1.x(), t1.y(), t1.z(), t1.alpha(), t1.snp(), t1.tgl(), - // t1.cSnpY(), t1.cSnpZ(), t1.cSnpSnp(), - // t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), - // t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2())); - } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ(), - // t1.x(), t1.y(), t1.z(), t1.alpha(), t1.snp(), t1.tgl(), - // t1.cSnpY(), t1.cSnpZ(), t1.cSnpSnp(), - // t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), - // t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2())); - } - } - } - if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { - used_trackIds_per_col.emplace_back(t2.globalIndex()); - if (mixingGroup.cfgDoMix) { - if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ(), - // t2.x(), t2.y(), t2.z(), t2.alpha(), t2.snp(), t2.tgl(), - // t2.cSnpY(), t2.cSnpZ(), t2.cSnpSnp(), - // t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), - // t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2())); - } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ(), - // t2.x(), t2.y(), t2.z(), t2.alpha(), t2.snp(), t2.tgl(), - // t2.cSnpY(), t2.cSnpZ(), t2.cSnpSnp(), - // t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), - // t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2())); - } - } - } - } else if (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { - used_trackIds_per_col.emplace_back(t1.globalIndex()); - if (mixingGroup.cfgDoMix) { - if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA(), - // t1.x(), t1.y(), t1.z(), t1.tgl(), - // t1.cPhiX(), t1.cPhiY(), t1.cPhiPhi(), - // t1.cTglX(), t1.cTglY(), t1.cTglPhi(), t1.cTglTgl(), - // t1.c1PtX(), t1.c1PtY(), t1.c1PtPhi(), t1.c1PtTgl(), t1.c1Pt21Pt2(), t1.chi2())); - - } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA(), - // t1.x(), t1.y(), t1.z(), t1.tgl(), - // t1.cPhiX(), t1.cPhiY(), t1.cPhiPhi(), - // t1.cTglX(), t1.cTglY(), t1.cTglPhi(), t1.cTglTgl(), - // t1.c1PtX(), t1.c1PtY(), t1.c1PtPhi(), t1.c1PtTgl(), t1.c1Pt21Pt2(), t1.chi2())); - } - } - } - if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { - used_trackIds_per_col.emplace_back(t2.globalIndex()); - if (mixingGroup.cfgDoMix) { - if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); - // emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA(), - // t2.x(), t2.y(), t2.z(), t2.tgl(), - // t2.cPhiX(), t2.cPhiY(), t2.cPhiPhi(), - // t2.cTglX(), t2.cTglY(), t2.cTglPhi(), t2.cTglTgl(), - // t2.c1PtX(), t2.c1PtY(), t2.c1PtPhi(), t2.c1PtTgl(), t2.c1Pt21Pt2(), t2.chi2())); - } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); - // emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrackWithCov(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA(), - // t2.x(), t2.y(), t2.z(), t2.tgl(), - // t2.cPhiX(), t2.cPhiY(), t2.cPhiPhi(), - // t2.cTglX(), t2.cTglY(), t2.cTglPhi(), t2.cTglTgl(), - // t2.c1PtX(), t2.c1PtY(), t2.c1PtPhi(), t2.c1PtTgl(), t2.c1Pt21Pt2(), t2.chi2())); - } - } - } - } - } - return true; - } - - o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); - o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; - o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - using FilteredMyCollisions = o2::soa::Filtered; - using FilteredMyCollisionsSWT = o2::soa::Filtered; - - o2::framework::SliceCache cache; - o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; - o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; - o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; - o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); - - o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfb >= static_cast(0)); - - o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); - o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); - - o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; - o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; - o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - - o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); - o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); - - TEMH* emh_pos = nullptr; - TEMH* emh_neg = nullptr; - - std::map, uint64_t> map_mixed_eventId_to_globalBC; - std::unordered_map map_best_match_globalmuon; - - std::vector used_trackIds_per_col; - int ndf = 0; - - template - void runPairing(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) - { - for (const auto& collision : collisions) { - initCCDB(collision); - - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - float centrality = centralities[cfgCentEstimator]; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; - std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; - std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; - std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; - std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; - std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; - std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; - const float eventplanes_2_for_mix[7] = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}; - float ep2 = eventplanes_2_for_mix[mixingGroup.cfgEP2Estimator]; - - std::vector>> qvectors = { - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics - {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics - }; - - if (nmod == 2) { - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 2>(&fRegistry, collision); - } else { - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); - } - if (nmod < 0 || isGoodQvector(qvectors)) { - fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated - } - fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - if (nmod > 0 && !isGoodQvector(qvectors)) { - continue; - } - - if constexpr (isTriggerAnalysis) { - int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); - if (!collision.triggerMask_bit(emswtId)) { - continue; - } - - // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event - // continue; - // } - - // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected - // auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected - // auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected - - // if (swt_bitset.test(mToIidx)) { - // while (ATcounter > mATCounter) { - // mATCounter++; - // fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); - // } - - // while (TOIcounter > mTOICounter) { - // fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); - // mTOICounter++; // always incremented by 1 in zorro!! - // } - // // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); - // } - } - - std::vector bootstrapweights = {}; - if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { // bootstrapping for accepted events - int randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - TRandom3 randomNumber(randomSeed); - for (int i = 0; i < cfgNumBootstrapSamples; i++) { - float poissonweight = 0.; - poissonweight = static_cast(randomNumber.PoissonD(1.0)); - bootstrapweights.push_back(poissonweight); - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfoBootstrap(&fRegistry, collision, i, poissonweight); - } - } else { - bootstrapweights.push_back(1.0); // to pass as non-empyt dummy to use - } - - if (nmod == 2) { - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 2>(&fRegistry, collision); - } else { - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); - } - fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated - - fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - // LOGF(info, "collision.globalIndex() = %d , collision.posZ() = %f , collision.numContrib() = %d, centrality = %f , posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", collision.globalIndex(), collision.posZ(), collision.numContrib(), centralities[cfgCentEstimator], posTracks_per_coll.size(), negTracks_per_coll.size()); - - used_trackIds_per_col.reserve(posTracks_per_coll.size() + negTracks_per_coll.size()); - int nuls = 0, nlspp = 0, nlsmm = 0; - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - bool is_pair_ok = fillPairInfo<0>(collision, pos, neg, cut, tracks, bootstrapweights); - if (is_pair_ok) { - nuls++; - } - } - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - bool is_pair_ok = fillPairInfo<0>(collision, pos1, pos2, cut, tracks, bootstrapweights); - if (is_pair_ok) { - nlspp++; - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - bool is_pair_ok = fillPairInfo<0>(collision, neg1, neg2, cut, tracks, bootstrapweights); - if (is_pair_ok) { - nlsmm++; - } - } - used_trackIds_per_col.clear(); - used_trackIds_per_col.shrink_to_fit(); - - if (!mixingGroup.cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { - continue; - } - - // event mixing - int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; - if (zbin < 0) { - zbin = 0; - } else if (static_cast(zvtx_bin_edges.size()) - 2 < zbin) { - zbin = static_cast(zvtx_bin_edges.size()) - 2; - } - - int centbin = lower_bound(cent_bin_edges.begin(), cent_bin_edges.end(), centrality) - cent_bin_edges.begin() - 1; - if (centbin < 0) { - centbin = 0; - } else if (static_cast(cent_bin_edges.size()) - 2 < centbin) { - centbin = static_cast(cent_bin_edges.size()) - 2; - } - - int epbin = lower_bound(ep_bin_edges.begin(), ep_bin_edges.end(), ep2) - ep_bin_edges.begin() - 1; - if (epbin < 0) { - epbin = 0; - } else if (static_cast(ep_bin_edges.size()) - 2 < epbin) { - epbin = static_cast(ep_bin_edges.size()) - 2; - } - - float occupancy = std::array{collision.ft0cOccupancyInTimeRange(), static_cast(collision.trackOccupancyInTimeRange())}[mixingGroup.cfgOccupancyEstimator]; - int occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), occupancy) - occ_bin_edges.begin() - 1; - if (occbin < 0) { - occbin = 0; - } else if (static_cast(occ_bin_edges.size()) - 2 < occbin) { - occbin = static_cast(occ_bin_edges.size()) - 2; - } - - // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); - - std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); - std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. - - // make a vector of selected photons in this collision. - const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); - // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - - const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. - // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); - - for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { - int mix_dfId = mix_dfId_collisionId.first; - int mix_collisionId = mix_dfId_collisionId.second; - if (collision.globalIndex() == mix_collisionId && ndf == mix_dfId) { // this never happens. only protection. - continue; - } - - auto globalBC_mix = map_mixed_eventId_to_globalBC[mix_dfId_collisionId]; - uint64_t diffBC = std::max(collision.globalBC(), globalBC_mix) - std::min(collision.globalBC(), globalBC_mix); - fRegistry.fill(HIST("Event/hDiffBC"), diffBC); - if (diffBC < mixingGroup.ndiff_bc_mix) { - continue; - } - fRegistry.fill(HIST("Event/hsMixCounter"), collision.posZ(), centrality, ep2, occupancy); - - const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); - // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); - - for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix - for (const auto& neg : negTracks_from_event_pool) { - fillPairInfo<1>(collision, pos, neg, cut, nullptr, bootstrapweights); - } - } - - for (const auto& neg : selected_negTracks_in_this_event) { // ULS mix - for (const auto& pos : posTracks_from_event_pool) { - fillPairInfo<1>(collision, neg, pos, cut, nullptr, bootstrapweights); - } - } - - for (const auto& pos1 : selected_posTracks_in_this_event) { // LS++ mix - for (const auto& pos2 : posTracks_from_event_pool) { - fillPairInfo<1>(collision, pos1, pos2, cut, nullptr, bootstrapweights); - } - } - - for (const auto& neg1 : selected_negTracks_in_this_event) { // LS-- mix - for (const auto& neg2 : negTracks_from_event_pool) { - fillPairInfo<1>(collision, neg1, neg2, cut, nullptr, bootstrapweights); - } - } - } // end of loop over mixed event pool - - if (nuls > 0 || nlspp > 0 || nlsmm > 0) { - map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); - emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); - emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); - } // end of if pair exist - - } // end of collision loop - - } // end of DF - - template - bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - - if constexpr (withSCT) { - if (foundHFSV(t1) || foundHFSV(t2)) { - return false; - } - } - - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - return true; - } - - std::map, float> map_weight; // -> float - template - void fillPairWeightMap(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) - { - std::vector> passed_pairIds; - passed_pairIds.reserve(posTracks.size() * negTracks.size()); - - for (const auto& collision : collisions) { - initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - // if constexpr (isTriggerAnalysis) { // don't call this here. this causes double counting. - // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event - // continue; - // } - // } - - std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; - std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; - std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; - std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; - std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; - std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; - std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; - - std::vector>> qvectors = { - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics - {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics - {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics - }; - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - if (nmod > 0 && !isGoodQvector(qvectors)) { - continue; - } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - if (isPairOK(pos, neg, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); - } - } - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - if (isPairOK(pos1, pos2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - if (isPairOK(neg1, neg2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); - } - } - } // end of collision loop - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousElectronsIds()) { - for (const auto& ambId2 : t2.ambiguousElectronsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousMuonsIds()) { - for (const auto& ambId2 : t2.ambiguousMuonsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } - passed_pairIds.clear(); - passed_pairIds.shrink_to_fit(); - } - - void processAnalysis(FilteredMyCollisions const& collisions, Types const&... args) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - auto electrons = std::get<0>(std::tie(args...)); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } - runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - auto muons = std::get<0>(std::tie(args...)); - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - ndf++; - } - PROCESS_SWITCH(DileptonSV, processAnalysis, "run dilepton analysis", true); - - void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - auto electrons = std::get<0>(std::tie(args...)); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } - runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - auto muons = std::get<0>(std::tie(args...)); - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - ndf++; - - // for nomalization - int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); - for (const auto& counter : countersAT) { - if (counter.isAT_bit(emswtId)) { - fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), mRunNumber); - } - } - for (const auto& counter : countersATOI) { - if (counter.isAToI_bit(emswtId)) { - fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), mRunNumber); - } - } - } - PROCESS_SWITCH(DileptonSV, processTriggerAnalysis, "run dilepton analysis on triggered data", false); - - void processNorm(o2::aod::EMEventNormInfos const& collisions) - { - for (const auto& collision : collisions) { - if (collision.centFT0C() < eventcuts.cfgCentMin || eventcuts.cfgCentMax < collision.centFT0C()) { - continue; - } - fRegistry.fill(HIST("Event/norm/hZvtx"), collision.posZ()); - - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 1.0); - if (collision.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 2.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 3.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 4.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoSameBunchPileup)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 5.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodZvtxFT0vsPV)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 6.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexITSTPC)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 7.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTRDmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 8.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTOFmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 9.0); - } - if (collision.sel8()) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 10.0); - } - if (std::fabs(collision.posZ()) < 10.0) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 11.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 12.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 13.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 14.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 15.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoHighMultCollInPrevRof)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 16.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer3)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 17.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer0123)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 18.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayersAll)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 19.0); - } - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - } // end of collision loop - } - PROCESS_SWITCH(DileptonSV, processNorm, "process normalization info", false); - - void processBC(o2::aod::EMBCs const& bcs) - { - for (const auto& bc : bcs) { - if (bc.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 0.f); - - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 1.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 2.f); - } - if (rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 3.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 4.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder) && rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 5.f); - } - } - } - } - PROCESS_SWITCH(DileptonSV, processBC, "process BC counter", false); - - void processDummy(MyCollisions const&) {} - PROCESS_SWITCH(DileptonSV, processDummy, "Dummy function", false); -}; - -#endif // PWGEM_DILEPTON_CORE_DILEPTONSV_H_ diff --git a/PWGEM/Dilepton/Core/DileptonSVMC.h b/PWGEM/Dilepton/Core/DileptonSVMC.h deleted file mode 100644 index 37c58fe8d53..00000000000 --- a/PWGEM/Dilepton/Core/DileptonSVMC.h +++ /dev/null @@ -1,2941 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over leptons in MC -// Please write to: daiki.sekihata@cern.ch - -#ifndef PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ -#define PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ - -#include "PWGEM/Dilepton/Core/DielectronCut.h" -#include "PWGEM/Dilepton/Core/DimuonCut.h" -#include "PWGEM/Dilepton/Core/EMEventCut.h" -#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" -#include "PWGEM/Dilepton/Utils/EventHistograms.h" -#include "PWGEM/Dilepton/Utils/MCUtilities.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/RecoDecay.h" -#include "Common/Core/fwdtrackUtilities.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using MyCollisions = o2::soa::Join; -using MyCollision = MyCollisions::iterator; - -using MyMCCollisions = o2::soa::Join; -using MyMCCollision = MyMCCollisions::iterator; - -using MyMCElectrons = o2::soa::Join; -using MyMCElectron = MyMCElectrons::iterator; -using FilteredMyMCElectrons = o2::soa::Filtered; -using FilteredMyMCElectron = FilteredMyMCElectrons::iterator; - -using MyMCMuons = o2::soa::Join; -using MyMCMuon = MyMCMuons::iterator; -using FilteredMyMCMuons = o2::soa::Filtered; -using FilteredMyMCMuon = FilteredMyMCMuons::iterator; - -using MySmearedElectrons = o2::soa::Join; -using MySmearedElectron = MySmearedElectrons::iterator; - -using MySmearedMuons = o2::soa::Join; -using MySmearedMuon = MySmearedMuons::iterator; - -// template -template -struct DileptonSVMC { - - // Configurables - o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - o2::framework::Configurable matCorrType{"matCorrType", 1, "0: none, 1: TGeo, 2: LUT, else: none"}; - - o2::framework::Configurable cfgEventGeneratorType{"cfgEventGeneratorType", -1, "if positive, select event generator type. i.e. gap or signal"}; - o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; - o2::framework::Configurable cfgFillUnfolding{"cfgFillUnfolding", false, "flag to fill histograms for unfolding"}; - o2::framework::Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; - o2::framework::Configurable cfgFillSeparateCharmHadronPairs{"cfgFillSeparateCharmHadronPairs", false, "flag to fill different ccbar pairs separately"}; - - o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; - - o2::framework::ConfigurableAxis ConfDPtBins{"ConfDPtBins", {220, -1.0, +10.0}, "dpt bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDCAllNarrowBins{"ConfDCAllNarrowBins", {200, 0.0, 10.0}, "narrow DCAll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfTrackDCA{"ConfTrackDCA", {o2::framework::VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "DCA binning for single tacks"}; - - o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, +1.f}, "yll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfLog10Chi2PCABins{"ConfLog10Chi2PCABins", {1, -12.f, 0.f}, "log10 of chi2PCA bins for output histograms"}; - o2::framework::ConfigurableAxis ConfDLBins{"ConfDLBins", {1, 0.f, 10.f}, "decay length bins for output histograms"}; - o2::framework::ConfigurableAxis ConfCPABins{"ConfCPABins", {o2::framework::VARIABLE_WIDTH, -1, -0.95, -0.9, -0.85, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1}, "cpa bins for output histograms"}; - - // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. - - o2::framework::Configurable cfg_nbin_dphi_ee{"cfg_nbin_dphi_ee", 1, "number of bins for dphi_ee"}; // 36 - o2::framework::Configurable cfg_nbin_deta_ee{"cfg_nbin_deta_ee", 1, "number of bins for deta_ee"}; // 40 - // o2::framework::Configurable cfg_nbin_cos_theta_cs{"cfg_nbin_cos_theta_cs", 1, "number of bins for cos theta cs"}; // 10 - // o2::framework::Configurable cfg_nbin_phi_cs{"cfg_nbin_phi_cs", 1, "number of bins for phi cs"}; // 10 - o2::framework::Configurable cfg_nbin_aco{"cfg_nbin_aco", 1, "number of bins for acoplanarity"}; // 10 - o2::framework::Configurable cfg_nbin_asym_pt{"cfg_nbin_asym_pt", 1, "number of bins for pt asymmetry"}; // 10 - o2::framework::Configurable cfg_nbin_dphi_e_ee{"cfg_nbin_dphi_e_ee", 1, "number of bins for dphi_ee_e"}; // 18 - o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {1, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; - o2::framework::ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; - o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; - o2::framework::ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {1, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> - - EMEventCut fEMEventCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "eventcut_group"; - o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; - o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; - o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; - o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; - o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. - o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. - o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; - o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; - o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; - o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; - - // for RCT - o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; - o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; - o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; - o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; - - o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; - o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; - o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; - } eventcuts; - - DielectronCut fDielectronCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dielectroncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut"}; - o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; - o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; - o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; - o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.04, "min deta between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.12, "min dphi between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; - o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; - // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; - - o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; - o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply phiv cut inherited from prefilter"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; - o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; - o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; - o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; - o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; - o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; - o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; - o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; - o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; - o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; - o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; - o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; - o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; - // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; - // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; - o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. - o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; - o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; - o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; - - o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; - o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; - // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; - // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; - o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - - // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - } dielectroncuts; - - DimuonCut fDimuonCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dimuoncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "max phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; - o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; - o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; - o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; - // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; - o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; - o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; - o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; - o2::framework::Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - o2::framework::Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - o2::framework::Configurable acceptOnlyXORMatching{"acceptOnlyXORMatching", false, "flag to accept only correct-wrong XOR pairs between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - } dimuoncuts; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDZIni{"maxDZIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } dfGroup; // for DCAFitterN - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "fdfGroup"; - o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; - o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; - o2::framework::Configurable maxR{"maxR", 20.f, "reject PCA's above this radius"}; - o2::framework::Configurable maxDXIni{"maxDXIni", 4.f, "reject (if > 0) PCA candidate if tracks DZ exceeds threshold"}; - o2::framework::Configurable minParamChange{"minParamChange", 1e-3, "stop iterations if largest change of any X is smaller than this"}; - o2::framework::Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; - o2::framework::Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; - o2::framework::Configurable maxLog10Chi2PCA{"maxLog10Chi2PCA", 10, "max. log10(chi2PCA) for dilepton pair"}; - } fdfGroup; // for FwdDCAFitterN - - o2::vertexing::DCAFitterN<2> mDCAFitter; - o2::vertexing::FwdDCAFitterN<2> mFwdDCAFitter; - - o2::aod::rctsel::RCTFlagsChecker rctChecker; - o2::framework::Service ccdb; - // o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - int mRunNumber{0}; - float d_bz{0}; - - struct : o2::framework::ConfigurableGroup { - std::string prefix = "mctrackcut_group"; - o2::framework::Configurable min_mcPt{"min_mcPt", 0.1, "min. MC pT for generated single lepton"}; - o2::framework::Configurable max_mcPt{"max_mcPt", 1e+10, "max. MC pT for generated single lepton"}; - o2::framework::Configurable min_mcEta{"min_mcEta", -0.8, "max. MC eta for generated single lepton"}; - o2::framework::Configurable max_mcEta{"max_mcEta", +0.8, "max. MC eta for generated single lepton"}; - } mctrackcuts; - - o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; - static constexpr std::string_view pair_sign_types[3] = {"uls/", "lspp/", "lsmm/"}; - static constexpr std::string_view dilepton_source_types[20] = { - "sm/Photon/", // 0 - "sm/PromptPi0/", // 1 - "sm/NonPromptPi0/", // 2 - "sm/Eta/", // 3 - "sm/EtaPrime/", // 4 - "sm/Rho/", // 5 - "sm/Omega/", // 6 - "sm/Phi/", // 7 - "sm/PromptJPsi/", // 8 - "sm/NonPromptJPsi/", // 9 - "sm/PromptPsi2S/", // 10 - "sm/NonPromptPsi2S/", // 11 - "sm/Upsilon1S/", // 12 - "sm/Upsilon2S/", // 13 - "sm/Upsilon3S/", // 14 - "ccbar/c2l_c2l/", // 15 - "bbbar/b2l_b2l/", // 16 - "bbbar/b2c2l_b2c2l/", // 17 - "bbbar/b2c2l_b2l_sameb/", // 18 - "bbbar/b2c2l_b2l_diffb/" // 19 - }; // unordered_map is better, but cannot be constexpr. - static constexpr std::string_view unfolding_dilepton_source_types[3] = {"sm/", "ccbar/", "bbbar/"}; - - ~DileptonSVMC() {} - - void addhistograms() - { - // event info - o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms(&fRegistry); - fRegistry.add("MCEvent/before/hZvtx", "mc vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1F, {{100, -50, +50}}, false); - fRegistry.add("MCEvent/before/hZvtx_rec", "rec. mc vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1F, {{100, -50, +50}}, false); - fRegistry.addClone("MCEvent/before/", "MCEvent/after/"); - - std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; - std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; - std::string pair_y_axis_title = "y_{ll}"; - std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - mass_axis_title = "m_{ee} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,ee} (GeV/c)"; - pair_y_axis_title = "y_{ee}"; - pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; - if (dielectroncuts.cfgDCAType == 1) { - pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; - pair_y_axis_title = "y_{#mu#mu}"; - pair_dca_axis_title = "DCA_{#mu#mu}^{XY} (#sigma)"; - } - - // pair info - const o2::framework::AxisSpec axis_mass{ConfMllBins, mass_axis_title}; - const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; - const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; - const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; - const o2::framework::AxisSpec axis_pt_meson{ConfPtllBins, "p_{T}^{VM} (GeV/c)"}; // for omega, phi meson pT spectra - const o2::framework::AxisSpec axis_y_meson{ConfYllBins, "y^{VM}"}; // for omega, phi meson pT spectra - const o2::framework::AxisSpec axis_chi2PCA{ConfLog10Chi2PCABins, "log_{10}(#chi^{2}_{PCA})"}; - const o2::framework::AxisSpec axis_dl{ConfDLBins, "decay length (#sigma)"}; - const o2::framework::AxisSpec axis_cpa{ConfCPABins, "cos(#theta_{p})"}; - - const o2::framework::AxisSpec axis_dca_narrow{ConfDCAllNarrowBins, pair_dca_axis_title}; - const o2::framework::AxisSpec axis_dpt{ConfDPtBins, "#Delta p_{T,1}^{gen-rec} + #Delta p_{T,2}^{gen-rec} (GeV/c)"}; - const o2::framework::AxisSpec axis_dca_track1{ConfTrackDCA, "DCA_{e,1}^{Z} (#sigma)"}; - const o2::framework::AxisSpec axis_dca_track2{ConfTrackDCA, "DCA_{e,2}^{Z} (#sigma)"}; - - std::string frameName = "CS"; - if (cfgPolarizationFrame == 0) { - frameName = "CS"; - } else if (cfgPolarizationFrame == 1) { - frameName = "HX"; - } else { - LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; - } - - const o2::framework::AxisSpec axis_dphi_ee{cfg_nbin_dphi_ee, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll - const o2::framework::AxisSpec axis_deta_ee{cfg_nbin_deta_ee, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; // for kHFll - const o2::framework::AxisSpec axis_cos_theta_pol{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; // for kPolarization, kUPC - const o2::framework::AxisSpec axis_phi_pol{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; // for kPolarization - const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; // for kPolarization - const o2::framework::AxisSpec axis_aco{cfg_nbin_aco, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; // for kUPC - const o2::framework::AxisSpec axis_asym_pt{cfg_nbin_asym_pt, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; // for kUPC - const o2::framework::AxisSpec axis_dphi_e_ee{cfg_nbin_dphi_e_ee, 0, M_PI, "#Delta#varphi = #varphi_{l} - #varphi_{ll} (rad.)"}; // for kUPC - - // generated info - fRegistry.add("Generated/sm/PromptPi0/uls/hs", "gen. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee}, true); - fRegistry.addClone("Generated/sm/PromptPi0/uls/", "Generated/sm/PromptPi0/lspp/"); - fRegistry.addClone("Generated/sm/PromptPi0/uls/", "Generated/sm/PromptPi0/lsmm/"); - - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptPi0/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Eta/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/EtaPrime/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Rho/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Omega/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Omega2ll/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Phi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Phi2ll/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/PromptJPsi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptJPsi/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/PromptPsi2S/"); - fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptPsi2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon1S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon3S/"); - - fRegistry.add("Generated/ccbar/c2l_c2l/uls/hs", "generated dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee}, true); - fRegistry.addClone("Generated/ccbar/c2l_c2l/uls/", "Generated/ccbar/c2l_c2l/lspp/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/uls/", "Generated/ccbar/c2l_c2l/lsmm/"); - - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2l_b2l/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2l_diffb/"); // LS - - // for charmed hadrons // create 28 combinations - static constexpr std::string_view charmed_mesons[] = {"Dplus", "D0", "Dsplus"}; // 411, 421, 431 - static constexpr std::string_view anti_charmed_mesons[] = {"Dminus", "D0bar", "Dsminus"}; - const int nm = sizeof(charmed_mesons) / sizeof(charmed_mesons[0]); - static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xicplus", "Xic0", "Omegac0"}; // 4122, 4232, 4132, 4332 - static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xicminus", "Xic0bar", "Omegac0bar"}; - const int nb = sizeof(charmed_baryons) / sizeof(charmed_baryons[0]); - static constexpr std::string_view sum_charmed_mesons[] = {"Dpm", "D0", "Dspm"}; - static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xicpm", "Xic0", "Omegac0"}; - - if (cfgFillSeparateCharmHadronPairs) { - for (int im = 0; im < nm; im++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", charmed_mesons[im].data(), anti_charmed_mesons[im].data())); - } - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", charmed_baryons[ib].data(), anti_charmed_baryons[ib].data())); - } - for (int im1 = 0; im1 < nm - 1; im1++) { - for (int im2 = im1 + 1; im2 < nm; im2++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_mesons[im1].data(), sum_charmed_mesons[im2].data())); - } - } - for (int ib1 = 0; ib1 < nb - 1; ib1++) { - for (int ib2 = ib1 + 1; ib2 < nb; ib2++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_baryons[ib1].data(), sum_charmed_baryons[ib2].data())); - } - } - for (int im = 0; im < nm; im++) { - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Generated/ccbar/c2l_c2l/", Form("Generated/ccbar/%s_%s/", sum_charmed_mesons[im].data(), sum_charmed_baryons[ib].data())); - } - } - } - - // evaluate acceptance for polarization - fRegistry.add("Generated/VM/All/Phi/hs", "gen. VM #rightarrow ll", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_cos_theta_pol, axis_phi_pol, axis_quadmom}, true); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/Rho/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/Omega/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/PromptJPsi/"); - fRegistry.addClone("Generated/VM/All/Phi/", "Generated/VM/All/NonPromptJPsi/"); - fRegistry.addClone("Generated/VM/All/", "Generated/VM/Acc/"); - - // reconstructed pair info - fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - - fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lspp/"); - fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lsmm/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptPi0/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptPi0/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Eta/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/EtaPrime/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Rho/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Omega/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Omega2ll/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Phi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Phi2ll/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptJPsi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptJPsi/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/PromptPsi2S/"); - fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/NonPromptPsi2S/"); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.add("Pair/sm/Photon/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - fRegistry.add("Pair/sm/Photon/uls/hMvsRxy", "m_{ee} vs. r_{xy};r_{xy}^{true} (cm);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{100, 0, 100}, {100, 0.0f, 1.0f}}, true); - for (const auto& strSign : pair_sign_types) { - fRegistry.add(std::format("Pair/sm/PromptPi0/{0}hMvsPhiV", strSign), "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - fRegistry.add(std::format("Pair/sm/NonPromptPi0/{0}hMvsPhiV", strSign), "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, true); - } - } - - fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lspp/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lsmm/"); - - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS - - if (cfgFillSeparateCharmHadronPairs) { - for (int im = 0; im < nm; im++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", charmed_mesons[im].data(), anti_charmed_mesons[im].data())); - } - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", charmed_baryons[ib].data(), anti_charmed_baryons[ib].data())); - } - for (int im1 = 0; im1 < nm - 1; im1++) { - for (int im2 = im1 + 1; im2 < nm; im2++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_mesons[im1].data(), sum_charmed_mesons[im2].data())); - } - } - for (int ib1 = 0; ib1 < nb - 1; ib1++) { - for (int ib2 = ib1 + 1; ib2 < nb; ib2++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_baryons[ib1].data(), sum_charmed_baryons[ib2].data())); - } - } - for (int im = 0; im < nm; im++) { - for (int ib = 0; ib < nb; ib++) { - fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/ccbar/%s_%s/", sum_charmed_mesons[im].data(), sum_charmed_baryons[ib].data())); - } - } - } - - // for correlated bkg due to mis-identified hadrons, and true combinatorial bkg - fRegistry.add("Pair/corr_bkg_lh/uls/hs", "rec. bkg", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee, axis_dca, axis_chi2PCA, axis_dl, axis_cpa}, true); - fRegistry.addClone("Pair/corr_bkg_lh/uls/", "Pair/corr_bkg_lh/lspp/"); - fRegistry.addClone("Pair/corr_bkg_lh/uls/", "Pair/corr_bkg_lh/lsmm/"); - fRegistry.addClone("Pair/corr_bkg_lh/", "Pair/corr_bkg_hh/"); - fRegistry.addClone("Pair/corr_bkg_lh/", "Pair/comb_bkg/"); - - if (doprocessGen_VM) { - fRegistry.add("Generated/VM/Omega/hPtY", "pT vs. y of #omega", o2::framework::HistType::kTH2D, {axis_y_meson, axis_pt_meson}, true); // for pT spectrum - fRegistry.add("Generated/VM/Phi/hPtY", "pT vs. y of #phi", o2::framework::HistType::kTH2D, {axis_y_meson, axis_pt_meson}, true); // for pT spectrum - } - - if (cfgFillUnfolding) { - // for 2D unfolding - const o2::framework::AxisSpec axis_mass_gen{ConfMllBins, "m_{ll}^{gen} (GeV/c^{2})"}; - const o2::framework::AxisSpec axis_pt_gen{ConfPtllBins, "p_{T,ll}^{gen} (GeV/c)"}; - const o2::framework::AxisSpec axis_mass_rec{ConfMllBins, "m_{ll}^{rec} (GeV/c^{2})"}; - const o2::framework::AxisSpec axis_pt_rec{ConfPtllBins, "p_{T,ll}^{rec} (GeV/c)"}; - fRegistry.add("Unfold/sm/uls/hsRM", "response matrix", o2::framework::HistType::kTHnSparseD, {axis_mass_gen, axis_pt_gen, axis_mass_rec, axis_pt_rec}, true); - fRegistry.add("Unfold/sm/uls/hMiss", "missing dilepton", o2::framework::HistType::kTH2D, {axis_mass_gen, axis_pt_gen}, true); // e.g. true eta is in acceptance, but reconstructed eta is out of acceptance. - fRegistry.add("Unfold/sm/uls/hFake", "fake dilepton", o2::framework::HistType::kTH2D, {axis_mass_rec, axis_pt_rec}, true); // e.g. true eta is out of acceptance, but reconstructed eta is in acceptance. - fRegistry.addClone("Unfold/sm/uls/", "Unfold/sm/lspp/"); - fRegistry.addClone("Unfold/sm/uls/", "Unfold/sm/lsmm/"); - fRegistry.addClone("Unfold/sm/", "Unfold/ccbar/"); - fRegistry.addClone("Unfold/sm/", "Unfold/bbbar/"); - } - } - - float beamM1 = o2::constants::physics::MassProton; // mass of beam - float beamM2 = o2::constants::physics::MassProton; // mass of beam - float beamE1 = 0.f; // beam energy - float beamE2 = 0.f; // beam energy - float beamP1 = 0.f; // beam momentum - float beamP2 = 0.f; // beam momentum - - float leptonM1 = 0.f; - float leptonM2 = 0.f; - int pdg_lepton = 0; - void init(o2::framework::InitContext&) - { - if (doprocessAnalysis && doprocessAnalysis_Smeared) { - LOGF(fatal, "Cannot enable doprocessAnalysis and doprocessAnalysis_Smeared at the same time. Please choose one."); - } - - mRunNumber = 0; - d_bz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); - - DefineEMEventCut(); - addhistograms(); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - DefineDielectronCut(); - leptonM1 = o2::constants::physics::MassElectron; - leptonM2 = o2::constants::physics::MassElectron; - pdg_lepton = 11; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - DefineDimuonCut(); - leptonM1 = o2::constants::physics::MassMuon; - leptonM2 = o2::constants::physics::MassMuon; - pdg_lepton = 13; - } - if (doprocessNorm) { - fRegistry.addClone("Event/before/hCollisionCounter", "Event/norm/hCollisionCounter"); - } - if (doprocessBC) { - auto hTVXCounter = fRegistry.add("BC/hTVXCounter", "TVX counter", o2::framework::HistType::kTH1D, {{6, -0.5f, 5.5f}}); - hTVXCounter->GetXaxis()->SetBinLabel(1, "TVX"); - hTVXCounter->GetXaxis()->SetBinLabel(2, "TVX && NoTFB"); - hTVXCounter->GetXaxis()->SetBinLabel(3, "TVX && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(4, "TVX && GoodRCT"); - hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); - hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); - } - - mDCAFitter.setPropagateToPCA(dfGroup.propagateToPCA); - mDCAFitter.setMaxR(dfGroup.maxR); - mDCAFitter.setMaxDZIni(dfGroup.maxDZIni); - mDCAFitter.setMinParamChange(dfGroup.minParamChange); - mDCAFitter.setMinRelChi2Change(dfGroup.minRelChi2Change); - mDCAFitter.setUseAbsDCA(dfGroup.useAbsDCA); - mDCAFitter.setWeightedFinalPCA(dfGroup.useWeightedFinalPCA); - mDCAFitter.setMaxChi2(std::pow(10, dfGroup.maxLog10Chi2PCA)); - - mFwdDCAFitter.setPropagateToPCA(fdfGroup.propagateToPCA); - mFwdDCAFitter.setMaxR(fdfGroup.maxR); - mFwdDCAFitter.setMaxDXIni(fdfGroup.maxDXIni); - mFwdDCAFitter.setMinParamChange(fdfGroup.minParamChange); - mFwdDCAFitter.setMinRelChi2Change(fdfGroup.minRelChi2Change); - mFwdDCAFitter.setUseAbsDCA(fdfGroup.useAbsDCA); - mFwdDCAFitter.setMaxChi2(std::pow(10, fdfGroup.maxLog10Chi2PCA)); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - switch (matCorrType.value) { - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kNone): - mFwdDCAFitter.setTGeoMat(false); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kTGeo): - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - mFwdDCAFitter.setTGeoMat(true); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::pairutil::MatCorrType::kLUT): { - LOGF(info, "LUT correction requested, loading LUT"); - o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - mFwdDCAFitter.setMatLUT(lut); - break; - } - default: - LOGF(info, "no correction requested, loading LUT by default!"); - // lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - // matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - mFwdDCAFitter.setTGeoMat(false); - break; - } - } - } - - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } - mRunNumber = collision.runNumber(); - fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); - - mDCAFitter.setBz(d_bz); - mFwdDCAFitter.setBz(d_bz); - - auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); - int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); - int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); - int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); - int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); - beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); - beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); - beamM1 = o2::constants::physics::MassProton * beamA1; - beamM2 = o2::constants::physics::MassProton * beamA2; - beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); - beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); - LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); - } - - void DefineEMEventCut() - { - fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); - fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); - fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); - fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); - fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); - fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); - fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); - fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); - fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); - fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); - fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); - fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); - fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); - fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); - fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); - fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); - fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); - fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); - } - - void DefineDielectronCut() - { - fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); - - // for pair - fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); - fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); - fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); - fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma - fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); - fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); - fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); - fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); - // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); - - // for track - fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); - fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, +dielectroncuts.cfg_max_eta_track); - fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); - fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); - fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); - fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); - fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); - fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); - fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); - fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); - fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); - fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); - fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); - fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); - fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); - fDielectronCut.SetChi2TOF(0.0, dielectroncuts.cfg_max_chi2tof); - // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); - fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); - - // for eID - fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); - fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); - // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); - fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); - fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); - fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); - fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); - fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); - - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut - std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); - } - std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); - } - fDielectronCut.SetMLThresholds(binsML, thresholdsML); - } // end of PID ML - } - - void DefineDimuonCut() - { - fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); - - // for pair - fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); - fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); - fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); - fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); // DCAxy in cm - fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); - - // for track - fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); - fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); - fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); - fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); - fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); - fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); - fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); - fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); - // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); - fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); - fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); - fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); - fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); - fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); - fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); - fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons - fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); - fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); - } - - template - int FindSMULS(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 22, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 333, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 443, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 100443, mcparticles) - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 553, mcparticles), - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 100553, mcparticles), - // o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, pdg_lepton, 200553, mcparticles) - }; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - int FindSMLSPP(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, -pdg_lepton, -pdg_lepton, 333, mcparticles)}; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - int FindSMLSMM(TTrack const& t1mc, TTrack const& t2mc, TMCParticles const& mcparticles) - { - int arr[] = { - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 111, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 221, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 331, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 113, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 223, mcparticles), - o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(t1mc, t2mc, pdg_lepton, pdg_lepton, 333, mcparticles)}; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; - } - - template - bool isInAcceptance(T const& lepton) - { - float pt = 0.f, eta = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt = lepton.ptSmeared(); - eta = lepton.etaSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt = lepton.ptSmeared_sa_muon(); - eta = lepton.etaSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt = lepton.ptSmeared_gl_muon(); - eta = lepton.etaSmeared_gl_muon(); - } else { - pt = lepton.pt(); - eta = lepton.eta(); - } - } - } else { - pt = lepton.pt(); - eta = lepton.eta(); - } - - return isInAcceptance(pt, eta); - - // if ((mctrackcuts.min_mcPt < pt && pt < mctrackcuts.max_mcPt) && (mctrackcuts.min_mcEta < eta && eta < mctrackcuts.max_mcEta)) { - // return true; - // } else { - // return false; - // } - } - - bool isInAcceptance(const float pt, const float eta) - { - if ((mctrackcuts.min_mcPt < pt && pt < mctrackcuts.max_mcPt) && (mctrackcuts.min_mcEta < eta && eta < mctrackcuts.max_mcEta)) { - return true; - } else { - return false; - } - } - - struct dileptonSV { - float pt1{999}; - float eta1{999}; - float phi1{999}; - - float pt2{999}; - float eta2{999}; - float phi2{999}; - - // float mass{999}; - // float pt{999}; - // float rapidity{999}; - - float chi2PCA{999}; - float cpa{999}; - float lxy{999}; - float lz{999}; - float lxyz{999}; - float lxyErr{999}; - float lzErr{999}; - float lxyzErr{999}; - }; - - template - bool findSV(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; - return false; - } - candidate.chi2PCA = mDCAFitter.getChi2AtPCACandidate(); - if (dfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - - mDCAFitter.propagateTracksToVertex(); - const auto& secondaryVertex = mDCAFitter.getPCACandidate(); - auto trackParCov0 = mDCAFitter.getTrack(0); - auto trackParCov1 = mDCAFitter.getTrack(1); - - // get track momenta - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - std::array pvec0{}; - std::array pvec1{}; - trackParCov0.getPxPyPzGlo(pvec0); - trackParCov1.getPxPyPzGlo(pvec1); - std::array momDilepton = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - bool findSVFwd(TCollision const& collision, TTrackParCov const& t1, TTrackParCov const& t2, dileptonSV& candidate) - { - try { - if (mFwdDCAFitter.process(t1, t2) == 0) { - return false; - } - } catch (const std::runtime_error& error) { - LOG(info) << "Run time error found: " << error.what() << ". FwdDCAFitterN cannot work, skipping the candidate."; - return false; - } - mFwdDCAFitter.FwdpropagateTracksToVertex(); - - std::array pvertex = {collision.posX(), collision.posY(), collision.posZ()}; - const auto& secondaryVertex = mFwdDCAFitter.getPCACandidate(); - candidate.chi2PCA = mFwdDCAFitter.getChi2AtPCACandidate(); - if (fdfGroup.maxLog10Chi2PCA < std::log10(candidate.chi2PCA)) { - return false; - } - auto trackParCov0 = mFwdDCAFitter.getTrack(0); - auto trackParCov1 = mFwdDCAFitter.getTrack(1); - std::array momDilepton = {static_cast(trackParCov0.getPx() + trackParCov1.getPx()), static_cast(trackParCov0.getPy() + trackParCov1.getPy()), static_cast(trackParCov0.getPz() + trackParCov1.getPz())}; - candidate.cpa = std::clamp(RecoDecay::cpa(pvertex, secondaryVertex, momDilepton), -1.0, std::nextafter(1.0, 0.0)); - - candidate.lxy = std::sqrt(std::pow(secondaryVertex[0] - collision.posX(), 2) + std::pow(secondaryVertex[1] - collision.posY(), 2)); - candidate.lz = secondaryVertex[2] - collision.posZ(); - candidate.lxyz = std::sqrt(std::pow(candidate.lxy, 2) + std::pow(candidate.lz, 2)); - - auto primaryVertex = getPrimaryVertex(collision); - std::array covVtx = mFwdDCAFitter.calcPCACovMatrixFlat(); - double phi{0}, theta{0}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); - candidate.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); - candidate.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); - candidate.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); - - candidate.pt1 = trackParCov0.getPt(); - candidate.eta1 = trackParCov0.getEta(); - candidate.phi1 = RecoDecay::constrainAngle(trackParCov0.getPhi(), 0, 1U); // 0-2pi - - candidate.pt2 = trackParCov1.getPt(); - candidate.eta2 = trackParCov1.getEta(); - candidate.phi2 = RecoDecay::constrainAngle(trackParCov1.getPhi(), 0, 1U); // 0-2pi - - return true; - } - - template - void fillGenHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float rapidity, const float dphi, const float deta, const float cos_thetaPol, const float phiPol, const float quadmom, const float aco, const float asym, const float dphi_e_ee, const float weight) - { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - - if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { - if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_D0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_D0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, weight); - } - } - } - } - - template - void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float rapidity, const float dphi, const float deta, const float cos_thetaPol, const float phiPol, const float quadmom, const float aco, const float asym, const float dphi_e_ee, const float pair_dca, const float log10Chi2PCA, const float decayLength, const float cpa, const float weight) - { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - - if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { - if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { - if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, rapidity, dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, dphi_e_ee, pair_dca, log10Chi2PCA, decayLength, cpa, weight); - } - } - } - } - - template - bool fillTruePairInfo(TCollision const& collision, TMCCollisions const&, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - bool is_pair_from_same_mcevent = (t1mc.emmceventId() == t2mc.emmceventId()); - - auto mccollision1 = t1mc.template emmcevent_as(); - auto mccollision2 = t2mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision1.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - if (cfgEventGeneratorType >= 0 && mccollision2.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - if (!isInAcceptance(t1mc) || !isInAcceptance(t2mc)) { - return false; - } - - dileptonSV candidate; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - - auto trackParCov1 = getTrackParCov(t1); - auto trackParCov2 = getTrackParCov(t2); - if (!findSV(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - - bool isCorrectMatch1 = t1.emmcparticleId() == t1.emmftmcparticleId(); - bool isCorrectMatch2 = t2.emmcparticleId() == t2.emmftmcparticleId(); - - if (dimuoncuts.acceptOnlyCorrectMatch) { - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch1) { - return false; - } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch2) { - return false; - } - } - if (dimuoncuts.acceptOnlyWrongMatch) { - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch1) { - return false; - } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch2) { - return false; - } - } - if (dimuoncuts.acceptOnlyXORMatching) { // this is dummy comment. - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - if (!(isCorrectMatch1 ^ isCorrectMatch2)) { - return false; - } - } - } - - auto trackParCov1 = o2::aod::fwdtrackutils::getTrackParCovFwd(t1, t1); - auto trackParCov2 = o2::aod::fwdtrackutils::getTrackParCovFwd(t2, t2); - if (!findSVFwd(collision, trackParCov1, trackParCov2, candidate)) { - return false; - } - - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - - float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, pt2 = 0.f, eta2 = 0.f, phi2 = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt1 = t1mc.ptSmeared(); - eta1 = t1mc.etaSmeared(); - phi1 = t1mc.phiSmeared(); - pt2 = t2mc.ptSmeared(); - eta2 = t2mc.etaSmeared(); - phi2 = t2mc.phiSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt1 = t1mc.ptSmeared_sa_muon(); - eta1 = t1mc.etaSmeared_sa_muon(); - phi1 = t1mc.phiSmeared_sa_muon(); - pt2 = t2mc.ptSmeared_sa_muon(); - eta2 = t2mc.etaSmeared_sa_muon(); - phi2 = t2mc.phiSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt1 = t1mc.ptSmeared_gl_muon(); - eta1 = t1mc.etaSmeared_gl_muon(); - phi1 = t1mc.phiSmeared_gl_muon(); - pt2 = t2mc.ptSmeared_gl_muon(); - eta2 = t2mc.etaSmeared_gl_muon(); - phi2 = t2mc.phiSmeared_gl_muon(); - } else { - pt1 = t1mc.pt(); - eta1 = t1mc.eta(); - phi1 = t1mc.phi(); - pt2 = t2mc.pt(); - eta2 = t2mc.eta(); - phi2 = t2mc.phi(); - } - } - } else { - pt1 = t1mc.pt(); - eta1 = t1mc.eta(); - phi1 = t1mc.phi(); - pt2 = t2mc.pt(); - eta2 = t2mc.eta(); - phi2 = t2mc.phi(); - } - - ROOT::Math::PtEtaPhiMVector v1mc(pt1, eta1, phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2mc(pt2, eta2, phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (v12mc.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12mc.Rapidity()) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (v12mc.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12mc.Rapidity()) { - return false; - } - } - - float weight = 1.f; - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - // LOGF(info, "t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); - - // ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v1(candidate.pt1, candidate.eta1, candidate.phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(candidate.pt2, candidate.eta2, candidate.phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float pair_dca = 999.f; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); - if (dielectroncuts.cfgDCAType == 1) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); - } else if (dielectroncuts.cfgDCAType == 2) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); - } - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - - float deta = v1.Eta() - v2.Eta(); - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); - - float aco = 1.f - std::fabs(dphi) / M_PI; - float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); - float dphi_e_ee = v1.Phi() - v12.Phi(); - o2::math_utils::bringToPMPi(dphi_e_ee); - dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf, 1); // shift dphi in [-pi/2, +3pi/2] rad. - - std::array arrP1 = {t1.px(), t1.py(), t1.pz(), leptonM1}; - std::array arrP2 = {t2.px(), t2.py(), t2.pz(), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, t1.sign(), cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, t1.sign(), cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if ((o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2ProngsWithoutPDG(t1mc, t2mc) > 0 || o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles) > 0) && is_pair_from_same_mcevent) { // for bkg study - if (std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) { // hh or lh correlated bkg - if (std::abs(t1mc.pdgCode()) != pdg_lepton && std::abs(t2mc.pdgCode()) != pdg_lepton) { // hh correlated bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/corr_bkg_hh/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/corr_bkg_hh/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/corr_bkg_hh/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } else { // lh correlated bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/corr_bkg_lh/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/corr_bkg_lh/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/corr_bkg_lh/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } - } - } else { // true combinatorial bkg - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/comb_bkg/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/comb_bkg/lspp/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/comb_bkg/lsmm/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); - } - } - - if (std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) { - return false; - } - - if (!is_pair_from_same_mcevent) { - return false; - } - if (cfgRequireTrueAssociation && (t1mc.emmceventId() != collision.emmceventId() || t2mc.emmceventId() != collision.emmceventId())) { - return false; - } - int mother_id = std::max({FindSMULS(t1mc, t2mc, mcparticles), FindSMULS(t2mc, t1mc, mcparticles), FindSMLSPP(t1mc, t2mc, mcparticles), FindSMLSMM(t1mc, t2mc, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - if ((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - switch (std::abs(mcmother.pdgCode())) { - case 111: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromCharm(mcmother, mcparticles) < 0 && o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt pi0 - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/PromptPi0/uls/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/sm/PromptPi0/lspp/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/sm/PromptPi0/lsmm/hMvsPhiV"), phiv, v12.M()); - } - } - } else { // non-prompt pi0 - fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt pi0 - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/uls/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/lspp/hMvsPhiV"), phiv, v12.M()); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Pair/sm/NonPromptPi0/lsmm/hMvsPhiV"), phiv, v12.M()); - } - } - } - break; - case 221: - fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // eta - break; - case 331: - fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // eta' - break; - case 113: - fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // rho - break; - case 223: - fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // omega - if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // omeag->ee - } - } - break; - case 333: - fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // phi - if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // phi->ee - } - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt J/psi - } else { - fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt J/psi - } - break; - case 100443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // non-prompt psi2S - } else { - fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // prompt psi2S - } - break; - default: - break; - } - } else if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && !(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - switch (std::abs(mcmother.pdgCode())) { - case 22: - fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // photon conversion - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsPhiV"), phiv, v12.M()); - float rxy_gen = std::sqrt(std::pow(t1mc.vx(), 2) + std::pow(t1mc.vy(), 2)); - fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsRxy"), rxy_gen, v12.M()); - } - break; - default: - break; - } - } // end of primary/secondary selection - } // end of primary selection for same mother - } else if (hfee_type > -1) { - if ((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - auto mp1 = mcparticles.iteratorAt(t1mc.mothersIds()[0]); - auto mp2 = mcparticles.iteratorAt(t2mc.mothersIds()[0]); - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // c2l_c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillRecHistograms<16>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2l_b2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillRecHistograms<17>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillRecHistograms<18>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2l_sameb - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillRecHistograms<19>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), pair_dca, std::log10(candidate.chi2PCA), candidate.lxyz / candidate.lxyzErr, candidate.cpa, weight); // b2c2l_b2l_diffb - break; - default: - break; - } - } - } // end of HF evaluation - return true; - } - - template - bool fillGenPairInfo(TMCParticle const& t1, TMCParticle const& t2, TMCParticles const& mcparticles) - { - if (!t1.isPhysicalPrimary() && !t1.producedByGenerator()) { - return false; - } - if (!t2.isPhysicalPrimary() && !t2.producedByGenerator()) { - return false; - } - - int mother_id = std::max({FindSMULS(t1, t2, mcparticles), FindSMULS(t2, t1, mcparticles), FindSMLSPP(t1, t2, mcparticles), FindSMLSMM(t1, t2, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1, t2, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, pt2 = 0.f, eta2 = 0.f, phi2 = 0.f; - if constexpr (isSmeared) { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - pt1 = t1.ptSmeared(); - eta1 = t1.etaSmeared(); - phi1 = t1.phiSmeared(); - pt2 = t2.ptSmeared(); - eta2 = t2.etaSmeared(); - phi2 = t2.phiSmeared(); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - pt1 = t1.ptSmeared_sa_muon(); - eta1 = t1.etaSmeared_sa_muon(); - phi1 = t1.phiSmeared_sa_muon(); - pt2 = t2.ptSmeared_sa_muon(); - eta2 = t2.etaSmeared_sa_muon(); - phi2 = t2.phiSmeared_sa_muon(); - } else if (dimuoncuts.cfg_track_type == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - pt1 = t1.ptSmeared_gl_muon(); - eta1 = t1.etaSmeared_gl_muon(); - phi1 = t1.phiSmeared_gl_muon(); - pt2 = t2.ptSmeared_gl_muon(); - eta2 = t2.etaSmeared_gl_muon(); - phi2 = t2.phiSmeared_gl_muon(); - } else { - pt1 = t1.pt(); - eta1 = t1.eta(); - phi1 = t1.phi(); - pt2 = t2.pt(); - eta2 = t2.eta(); - phi2 = t2.phi(); - } - } - } else { - pt1 = t1.pt(); - eta1 = t1.eta(); - phi1 = t1.phi(); - pt2 = t2.pt(); - eta2 = t2.eta(); - phi2 = t2.phi(); - } - - ROOT::Math::PtEtaPhiMVector v1(pt1, eta1, phi1, leptonM1); - ROOT::Math::PtEtaPhiMVector v2(pt2, eta2, phi2, leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float deta = v1.Eta() - v2.Eta(); - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (v12.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - // if (dielectroncuts.cfg_apply_detadphi && std::pow(deta / dielectroncuts.cfg_min_deta, 2) + std::pow(dphi / dielectroncuts.cfg_min_dphi, 2) < 1.f) { - // continue; - // } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (v12.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - // if (dimuoncuts.cfg_apply_detadphi && std::pow(deta / dimuoncuts.cfg_min_deta, 2) + std::pow(dphi / dimuoncuts.cfg_min_dphi, 2) < 1.f) { - // continue; - // } - } - - int sign1 = -t1.pdgCode() / pdg_lepton; - int sign2 = -t2.pdgCode() / pdg_lepton; - - float aco = 1.f - std::fabs(dphi) / M_PI; - float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); - float dphi_e_ee = v1.Phi() - v12.Phi(); - o2::math_utils::bringToPMPi(dphi_e_ee); - dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf, 1); // shift dphi in [-pi/2, +3pi/2] rad. after deta-dphi cut. - - std::array arrP1 = {static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1}; - std::array arrP2 = {static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, -t1.pdgCode() / pdg_lepton, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, -t1.pdgCode() / pdg_lepton, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if (!isInAcceptance(t1) || !isInAcceptance(t2)) { - return false; - } - - float weight = 1.f; - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - switch (std::abs(mcmother.pdgCode())) { - case 111: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromCharm(mcmother, mcparticles) < 0 && o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillGenHistograms<1>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt pi0 - } else { // non-prompt pi0 - fillGenHistograms<2>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt pi0 - } - break; - case 221: - fillGenHistograms<3>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // eta - break; - case 331: - fillGenHistograms<4>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // eta' - break; - case 113: - fillGenHistograms<5>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // rho - break; - case 223: - fillGenHistograms<6>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // omega - if (mcmother.daughtersIds().size() == 2) { - fRegistry.fill(HIST("Generated/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // omega->ee - } - break; - case 333: - fillGenHistograms<7>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // phi - if (mcmother.daughtersIds().size() == 2) { - fRegistry.fill(HIST("Generated/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee)); // phi->ee - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillGenHistograms<9>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt J/psi - } else { - fillGenHistograms<8>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt J/psi - } - break; - case 100443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcmother, mcparticles) > 0) { - fillGenHistograms<11>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // non-prompt psi2S - } else { - fillGenHistograms<10>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // prompt psi2S - } - break; - default: - break; - } - } - } else if (hfee_type > -1) { - auto mp1 = mcparticles.iteratorAt(t1.mothersIds()[0]); - auto mp2 = mcparticles.iteratorAt(t2.mothersIds()[0]); - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillGenHistograms<15>(sign1, sign2, mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // c2l_c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillGenHistograms<16>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2l_b2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillGenHistograms<17>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2c2l - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillGenHistograms<18>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2l_sameb - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillGenHistograms<19>(sign1, sign2, 0, 0, v12.M(), v12.Pt(), v12.Rapidity(), dphi, deta, cos_thetaPol, phiPol, quadmom, aco, asym, std::fabs(dphi_e_ee), weight); // b2c2l_b2l_diffb - break; - default: - break; - } - } // end of HF evaluation - return false; - } - - template - bool fillGenParticleAcc(TMCParticle const& mcParticle, TMCParticles const& mcParticles) - { - if (!mcParticle.isPhysicalPrimary() && !mcParticle.producedByGenerator()) { - return false; - } - if (mcParticle.daughtersIds().size() < 2) { - return false; - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (mcParticle.y() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < mcParticle.y()) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (mcParticle.y() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < mcParticle.y()) { - return false; - } - } - - int pdg = mcParticle.pdgCode(); - if (std::abs(pdg) == 113 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - if (std::abs(pdg) == 223 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - if (std::abs(pdg) == 333 && mcParticle.daughtersIds().size() != 2) { // reject dalitz decay - return false; - } - // accept radiative decay of charmonia (ee + multiple gamma). - - // float pt1 = 0.f, eta1 = 0.f, phi1 = 0.f, sign1 = 0.f; - // float pt2 = 0.f, eta2 = 0.f, phi2 = 0.f, sign2 = 0.f; - std::vector> vDau; - vDau.reserve(mcParticle.daughtersIds().size()); - for (const auto& daughterId : mcParticle.daughtersIds()) { - auto dau = mcParticles.rawIteratorAt(daughterId); - if (std::abs(dau.pdgCode()) == pdg_lepton) { - vDau.emplace_back(std::array{dau.pt(), dau.eta(), dau.phi(), dau.pdgCode() > 0 ? -1.f : +1.f}); - } - } - if (vDau.size() != 2 || vDau[0][3] * vDau[1][3] > 0.f) { // decay objects must be ULS 2 leptons. - vDau.clear(); - vDau.shrink_to_fit(); - return false; - } - - // LOGF(info, "mcParticle.globalIndex() = %d, mcParticle.pdgCode() = %d, mcParticle.daughtersIds().size() = %d", mcParticle.globalIndex(), mcParticle.pdgCode(), mcParticle.daughtersIds().size()); - - ROOT::Math::PtEtaPhiMVector v1(vDau[0][0], vDau[0][1], vDau[0][2], leptonM1); - ROOT::Math::PtEtaPhiMVector v2(vDau[1][0], vDau[1][1], vDau[1][2], leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - std::array arrP1 = {static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1}; - std::array arrP2 = {static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}; - float cos_thetaPol = 999, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, vDau[0][3] > 0 ? 1 : -1, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrP1, arrP2, beamE1, beamE2, beamP1, beamP2, vDau[0][3] > 0 ? 1 : -1, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - float weight = 1.f; - switch (std::abs(mcParticle.pdgCode())) { - case 113: - fRegistry.fill(HIST("Generated/VM/All/Rho/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Rho/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 223: - fRegistry.fill(HIST("Generated/VM/All/Omega/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Omega/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 333: - fRegistry.fill(HIST("Generated/VM/All/Phi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/Phi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - break; - case 443: - if (o2::aod::pwgem::dilepton::utils::mcutil::IsFromBeauty(mcParticle, mcParticles) > 0) { - fRegistry.fill(HIST("Generated/VM/All/NonPromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/NonPromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - } else { - fRegistry.fill(HIST("Generated/VM/All/PromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - if (isInAcceptance(v1.Pt(), v1.Eta()) && isInAcceptance(v2.Pt(), v2.Eta())) { - fRegistry.fill(HIST("Generated/VM/Acc/PromptJPsi/hs"), v12.M(), mcParticle.pt(), mcParticle.y(), cos_thetaPol, phiPol, quadmom, weight); - } - } - break; - default: - break; - } - - vDau.clear(); - vDau.shrink_to_fit(); - return false; - } - - template - void runTruePairing(TCollisions const& collisions, TMCLeptons const& posTracks, TMCLeptons const& negTracks, TPreslice const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const& mccollisions, TMCParticles const& mcparticles) - { - for (const auto& collision : collisions) { - initCCDB(collision); - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - // LOGF(info, "centrality = %f , posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", centralities[cfgCentEstimator], posTracks_per_coll.size(), negTracks_per_coll.size()); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillTruePairInfo(collision, mccollisions, pos, neg, cut, tracks, mcparticles); - } // end of ULS pair loop - - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillTruePairInfo(collision, mccollisions, pos1, pos2, cut, tracks, mcparticles); - } // end of LS++ pair loop - - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillTruePairInfo(collision, mccollisions, neg1, neg2, cut, tracks, mcparticles); - } // end of LS-- pair loop - - } // end of collision loop - } - - template - void runGenInfo(TCollisions const& collisions, TMCCollisions const& mccollisions, TMCLeptons const& posTracksMC, TMCLeptons const& negTracksMC, TMCParticles const& mcparticles) - { - for (const auto& mccollision : mccollisions) { - if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - fRegistry.fill(HIST("MCEvent/before/hZvtx"), mccollision.posZ()); - if (mccollision.mpemeventId() < 0) { - continue; - } - auto collision = collisions.rawIteratorAt(mccollision.mpemeventId()); - - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - fRegistry.fill(HIST("MCEvent/before/hZvtx_rec"), mccollision.posZ()); - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - if (!(eventcuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgTrackOccupancyMax)) { - continue; - } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - continue; - } - fRegistry.fill(HIST("MCEvent/after/hZvtx"), mccollision.posZ()); - - auto posTracks_per_coll = posTracksMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); - auto negTracks_per_coll = negTracksMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillGenPairInfo(t1, t2, mcparticles); - } // end of true ULS pair loop - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillGenPairInfo(t1, t2, mcparticles); - } // end of true LS++ pair loop - - for (const auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillGenPairInfo(t1, t2, mcparticles); - } // end of true LS-- pair loop - - // acceptance for polarization of vector mesons - auto mcParticles_per_coll = mcparticles.sliceBy(perMcCollision, mccollision.globalIndex()); - for (const auto& mcParticle : mcParticles_per_coll) { - if (!mcParticle.isPhysicalPrimary() && !mcParticle.producedByGenerator()) { - continue; - } - int pdg = std::abs(mcParticle.pdgCode()); - if (pdg == 113 || pdg == 223 || pdg == 333 || pdg == 443) { // select only VMs - fillGenParticleAcc(mcParticle, mcparticles); // VMs that decay into dilepton are stored in derived data. This is sufficient for polarization. - } - } // end of mc particle loop - } // end of collision loop - } - - template - bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.template IsSelectedPair(t1, t2)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedPair(t1, t2)) { - return false; - } - } - return true; - } - - std::map, float> map_weight; // -> float - template - void fillPairWeightMap(TCollisions const& collisions, TTracks1 const& posTracks, TTracks2 const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const&, TMCParticles const& mcparticles) - { - std::vector> passed_pairIds; - passed_pairIds.reserve(posTracks.size() * negTracks.size()); - - for (const auto& collision : collisions) { - initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - // auto mccollision = collision.template emmcevent_as(); - // if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - // continue; - // } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - auto mcpos = mcparticles.iteratorAt(pos.emmcparticleId()); - auto mccollision_from_pos = mcpos.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcneg = mcparticles.iteratorAt(neg.emmcparticleId()); - auto mccollision_from_neg = mcneg.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcpos.emmceventId() != collision.emmceventId() || mcneg.emmceventId() != collision.emmceventId())) { - continue; - } - - if (isPairOK(pos, neg, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); - } - } - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - auto mcpos1 = mcparticles.iteratorAt(pos1.emmcparticleId()); - auto mccollision_from_pos1 = mcpos1.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos1.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcpos2 = mcparticles.iteratorAt(pos2.emmcparticleId()); - auto mccollision_from_pos2 = mcpos2.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_pos2.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcpos1.emmceventId() != collision.emmceventId() || mcpos2.emmceventId() != collision.emmceventId())) { - continue; - } - - if (isPairOK(pos1, pos2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - auto mcneg1 = mcparticles.iteratorAt(neg1.emmcparticleId()); - auto mccollision_from_neg1 = mcneg1.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg1.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mcneg2 = mcparticles.iteratorAt(neg2.emmcparticleId()); - auto mccollision_from_neg2 = mcneg2.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_neg2.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (cfgRequireTrueAssociation && (mcneg1.emmceventId() != collision.emmceventId() || mcneg2.emmceventId() != collision.emmceventId())) { - continue; - } - if (isPairOK(neg1, neg2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); - } - } - } // end of collision loop - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - // LOGF(info, "std::get<0>(pairId) = %d, std::get<1>(pairId) = %d, t1.globalIndex() = %d, t2.globalIndex() = %d", std::get<0>(pairId), std::get<1>(pairId), t1.globalIndex(), t2.globalIndex()); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousElectronsIds()) { - for (const auto& ambId2 : t2.ambiguousElectronsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousMuonsIds()) { - for (const auto& ambId2 : t2.ambiguousMuonsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } - passed_pairIds.clear(); - passed_pairIds.shrink_to_fit(); - } - - template - bool isPairInAcc(TTrack const& t1, TTrack const& t2) - { - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if ((t1.pt() < dielectroncuts.cfg_min_pt_track || dielectroncuts.cfg_max_pt_track < t1.pt()) || (t2.pt() < dielectroncuts.cfg_min_pt_track || dielectroncuts.cfg_max_pt_track < t2.pt())) { - return false; - } - if ((t1.eta() < dielectroncuts.cfg_min_eta_track || dielectroncuts.cfg_max_eta_track < t1.eta()) || (t2.eta() < dielectroncuts.cfg_min_eta_track || dielectroncuts.cfg_max_eta_track < t2.eta())) { - return false; - } - if (v12.Rapidity() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - return true; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if ((t1.pt() < dimuoncuts.cfg_min_pt_track || dimuoncuts.cfg_max_pt_track < t1.pt()) || (t2.pt() < dimuoncuts.cfg_min_pt_track || dimuoncuts.cfg_max_pt_track < t2.pt())) { - return false; - } - if ((t1.eta() < dimuoncuts.cfg_min_eta_track || dimuoncuts.cfg_max_eta_track < t1.eta()) || (t2.eta() < dimuoncuts.cfg_min_eta_track || dimuoncuts.cfg_max_eta_track < t2.eta())) { - return false; - } - if (v12.Rapidity() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - return true; - } else { - return false; - } - return true; - } - - template - void fillHistogramsUnfolding(TTrack const& t1, TTrack const& t2, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - - ROOT::Math::PtEtaPhiMVector v1rec(t1.pt(), t1.eta(), t1.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2rec(t2.pt(), t2.eta(), t2.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12rec = v1rec + v2rec; - - ROOT::Math::PtEtaPhiMVector v1mc(t1mc.pt(), t1mc.eta(), t1mc.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2mc(t2mc.pt(), t2mc.eta(), t2mc.phi(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - float weight = 1.f; - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - - if (isPairInAcc(t1, t2) && isPairInAcc(t1mc, t2mc)) { // both rec and mc info are in acceptance. - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hsRM"), v12mc.M(), v12mc.Pt(), v12rec.M(), v12rec.Pt(), weight); - } - } else if (!isPairInAcc(t1, t2) && isPairInAcc(t1mc, t2mc)) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hMiss"), v12mc.M(), v12mc.Pt(), weight); - } - } else if (isPairInAcc(t1, t2) && !isPairInAcc(t1mc, t2mc)) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("uls/hFake"), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lspp/hFake"), v12rec.M(), v12rec.Pt(), weight); - } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- - fRegistry.fill(HIST("Unfold/") + HIST(unfolding_dilepton_source_types[sourceId]) + HIST("lsmm/hFake"), v12rec.M(), v12rec.Pt(), weight); - } - } - } - - template - bool fillPairUnfolding(TTrack const& t1, TTrack const& t2, TTracks const& tracks, TCut const& cut, TMCCollisions const&, TMCParticles const& mcparticles) - { - auto t1mc = mcparticles.iteratorAt(t1.emmcparticleId()); - auto mccollision_from_t1 = t1mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_t1.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - - auto t2mc = mcparticles.iteratorAt(t2.emmcparticleId()); - auto mccollision_from_t2 = t2mc.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision_from_t2.getSubGeneratorId() != cfgEventGeneratorType) { - return false; - } - - if ((std::abs(t1mc.pdgCode()) != pdg_lepton || std::abs(t2mc.pdgCode()) != pdg_lepton) || (t1mc.emmceventId() != t2mc.emmceventId())) { - return false; - } - if (t1mc.pdgCode() * t2mc.pdgCode() > 0) { // ULS - return false; - } - if (!((t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && (t2mc.isPhysicalPrimary() || t2mc.producedByGenerator()))) { - return false; - } - int mother_id = std::max({FindSMULS(t1mc, t2mc, mcparticles), FindSMULS(t2mc, t1mc, mcparticles), FindSMLSPP(t1mc, t2mc, mcparticles), FindSMLSMM(t1mc, t2mc, mcparticles)}); - int hfee_type = o2::aod::pwgem::dilepton::utils::mcutil::IsHF(t1mc, t2mc, mcparticles); - if (mother_id < 0 && hfee_type < 0) { - return false; - } - - if (!isPairOK(t1, t2, cut, tracks)) { // without acceptance - return false; - } - - // ROOT::Math::PtEtaPhiMVector v1rec(t1.pt(), t1.eta(), t1.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2rec(t2.pt(), t2.eta(), t2.phi(), leptonM2); - // ROOT::Math::PtEtaPhiMVector v12rec = v1rec + v2rec; - - // ROOT::Math::PtEtaPhiMVector v1mc(t1mc.pt(), t1mc.eta(), t1mc.phi(), leptonM1); - // ROOT::Math::PtEtaPhiMVector v2mc(t2mc.pt(), t2mc.eta(), t2mc.phi(), leptonM2); - // ROOT::Math::PtEtaPhiMVector v12mc = v1mc + v2mc; - - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (!(mcmother.isPhysicalPrimary() || mcmother.producedByGenerator())) { - return false; - } - switch (std::abs(mcmother.pdgCode())) { - case 111: - case 221: - case 331: - case 113: - case 223: - case 333: - case 443: - case 100443: - fillHistogramsUnfolding<0>(t1, t2, mcparticles); - break; - default: - break; - } - } else if (hfee_type > -1) { - switch (hfee_type) { - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kCe_Ce): - fillHistogramsUnfolding<1>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBe_Be): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_BCe): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_SameB): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - case static_cast(o2::aod::pwgem::dilepton::utils::mcutil::EM_HFeeType::kBCe_Be_DiffB): - fillHistogramsUnfolding<2>(t1, t2, mcparticles); - break; - default: - break; - } - } - return true; - } - - template - void fillUnfolding(TCollisions const& collisions, TTracks1 const& posTracks, TTracks2 const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks, TMCCollisions const& mcCollisions, TMCParticles const& mcparticles) - { - for (const auto& collision : collisions) { - initCCDB(collision); - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); // reconstructed pos tracks - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); // reconstructed neg tracks - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - fillPairUnfolding(pos, neg, tracks, cut, mcCollisions, mcparticles); - } // end of ULS pairing - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - fillPairUnfolding(pos1, pos2, tracks, cut, mcCollisions, mcparticles); - } // end of LS++ pairing - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - fillPairUnfolding(neg1, neg2, tracks, cut, mcCollisions, mcparticles); - } // end of LS-- pairing - } // end of collision loop - } - - std::unordered_map map_best_match_globalmuon; - - o2::framework::SliceCache cache; - o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; - o2::framework::expressions::Filter trackFilter_electron = nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; - o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; - o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); - - o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfb >= static_cast(0)); - - o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; - o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type; - o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - - o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); - o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; - o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - using FilteredMyCollisions = o2::soa::Filtered; - - o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); // reconstructed tracks - o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); // reconstructed tracks - o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); // reconstructed tracks - o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); // reconstructed tracks - - o2::framework::Partition positive_electronsMC = o2::aod::mcparticle::pdgCode == -11; // e+ - o2::framework::Partition negative_electronsMC = o2::aod::mcparticle::pdgCode == 11; // e- - o2::framework::Partition positive_muonsMC = o2::aod::mcparticle::pdgCode == -13; // mu+ - o2::framework::Partition negative_muonsMC = o2::aod::mcparticle::pdgCode == 13; // mu- - o2::framework::PresliceUnsorted perMcCollision = o2::aod::emmcparticle::emmceventId; - o2::framework::PresliceUnsorted perMcCollision_vm = o2::aod::emmcgenvectormeson::emmceventId; - // o2::framework::PresliceUnsorted recColperMcCollision = o2::aod::emmceventlabel::emmceventId; - - void processAnalysis(FilteredMyCollisions const& collisions, MyMCCollisions const& mccollisions, o2::aod::EMMCParticles const& mcparticles, TLeptons const& leptons) - { - // LOGF(info, "collisions.size() = %d, mccollisions.size() = %d, mcparticles.size() = %d", collisions.size(), mccollisions.size(), mcparticles.size()); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - } - runTruePairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - runGenInfo(collisions, mccollisions, positive_electronsMC, negative_electronsMC, mcparticles); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(leptons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - } - runTruePairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - runGenInfo(collisions, mccollisions, positive_muonsMC, negative_muonsMC, mcparticles); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles); - } - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - } - PROCESS_SWITCH(DileptonSVMC, processAnalysis, "run dilepton mc analysis", true); - - o2::framework::Partition positive_electronsMC_smeared = o2::aod::mcparticle::pdgCode == -11; // e+ - o2::framework::Partition negative_electronsMC_smeared = o2::aod::mcparticle::pdgCode == 11; // e- - o2::framework::Partition positive_muonsMC_smeared = o2::aod::mcparticle::pdgCode == -13; // mu+ - o2::framework::Partition negative_muonsMC_smeared = o2::aod::mcparticle::pdgCode == 13; // mu- - - void processAnalysis_Smeared(FilteredMyCollisions const& collisions, MyMCCollisions const& mccollisions, TLeptons const& leptons, TSmeardMCParitlces const& mcparticles_smeared) - { - // LOGF(info, "collisions.size() = %d, mccollisions.size() = %d, mcparticles.size() = %d", collisions.size(), mccollisions.size(), mcparticles.size()); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - } - runTruePairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - runGenInfo(collisions, mccollisions, positive_electronsMC_smeared, negative_electronsMC_smeared, mcparticles_smeared); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, leptons, mccollisions, mcparticles_smeared); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(leptons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - } - runTruePairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - runGenInfo(collisions, mccollisions, positive_muonsMC_smeared, negative_muonsMC_smeared, mcparticles_smeared); - if (cfgFillUnfolding) { - fillUnfolding(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, leptons, mccollisions, mcparticles_smeared); - } - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - } - PROCESS_SWITCH(DileptonSVMC, processAnalysis_Smeared, "run dilepton mc analysis with smearing", false); - - void processGen_VM(FilteredMyCollisions const& collisions, MyMCCollisions const&, o2::aod::EMMCGenVectorMesons const& mcparticles) - { - // for oemga, phi efficiency - for (const auto& collision : collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - auto mccollision = collision.template emmcevent_as(); - if (cfgEventGeneratorType >= 0 && mccollision.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - auto mctracks_per_coll = mcparticles.sliceBy(perMcCollision_vm, mccollision.globalIndex()); - - for (const auto& mctrack : mctracks_per_coll) { - if (!(mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { - continue; - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (mctrack.y() < dielectroncuts.cfg_min_pair_y || dielectroncuts.cfg_max_pair_y < mctrack.y()) { - continue; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (mctrack.y() < dimuoncuts.cfg_min_pair_y || dimuoncuts.cfg_max_pair_y < mctrack.y()) { - continue; - } - } - - switch (std::abs(mctrack.pdgCode())) { - case 223: - fRegistry.fill(HIST("Generated/VM/Omega/hPtY"), mctrack.y(), mctrack.pt(), 1.f / mctrack.dsf()); - break; - case 333: - fRegistry.fill(HIST("Generated/VM/Phi/hPtY"), mctrack.y(), mctrack.pt(), 1.f / mctrack.dsf()); - break; - default: - break; - } - - } // end of mctracks per mccollision - } // end of collision loop - } - PROCESS_SWITCH(DileptonSVMC, processGen_VM, "process generated info for vector mesons", false); - - void processNorm(o2::aod::EMEventNormInfos const& collisions) - { - for (const auto& collision : collisions) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 1.0); - if (collision.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 2.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 3.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 4.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoSameBunchPileup)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 5.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodZvtxFT0vsPV)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 6.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexITSTPC)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 7.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTRDmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 8.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsVertexTOFmatched)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 9.0); - } - if (collision.sel8()) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 10.0); - } - if (std::fabs(collision.posZ()) < 10.0) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 11.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 12.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 13.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStandard)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 14.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStrict)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 15.0); - } - if (collision.selection_bit(o2::aod::emevsel::kNoHighMultCollInPrevRof)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 16.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer3)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 17.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer0123)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 18.0); - } - if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayersAll)) { - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 19.0); - } - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted - } // end of collision loop - } - PROCESS_SWITCH(DileptonSVMC, processNorm, "process normalization info", false); - - void processBC(o2::aod::EMBCs const& bcs) - { - for (const auto& bc : bcs) { - if (bc.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 0.f); - - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 1.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 2.f); - } - if (rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 3.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 4.f); - } - if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder) && rctChecker(bc)) { - fRegistry.fill(HIST("BC/hTVXCounter"), 5.f); - } - } - } - } - PROCESS_SWITCH(DileptonSVMC, processBC, "process BC counter", false); - - void processDummy(FilteredMyCollisions const&) {} - PROCESS_SWITCH(DileptonSVMC, processDummy, "Dummy function", false); -}; - -#endif // PWGEM_DILEPTON_CORE_DILEPTONSVMC_H_ diff --git a/PWGEM/Dilepton/Tasks/CMakeLists.txt b/PWGEM/Dilepton/Tasks/CMakeLists.txt index 0bcd1085217..425ea54f510 100644 --- a/PWGEM/Dilepton/Tasks/CMakeLists.txt +++ b/PWGEM/Dilepton/Tasks/CMakeLists.txt @@ -95,16 +95,6 @@ o2physics_add_dpl_workflow(dielectron-mc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dielectron-sv - SOURCES dielectronSV.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(dielectron-sv-mc - SOURCES dielectronSVMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(dimuon SOURCES dimuon.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils @@ -115,16 +105,6 @@ o2physics_add_dpl_workflow(dimuon-mc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dimuon-sv - SOURCES dimuonSV.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(dimuon-sv-mc - SOURCES dimuonSVMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(dimuon-v1 SOURCES dimuonV1.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore diff --git a/PWGEM/Dilepton/Tasks/dielectronSV.cxx b/PWGEM/Dilepton/Tasks/dielectronSV.cxx deleted file mode 100644 index d61e9beea71..00000000000 --- a/PWGEM/Dilepton/Tasks/dielectronSV.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code is for dielectron analyses. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSV.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dielectron-sv"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx b/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx deleted file mode 100644 index 7bcd0ab497d..00000000000 --- a/PWGEM/Dilepton/Tasks/dielectronSVMC.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over electron table and make pairs. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSVMC.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dielectron-sv-mc"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dimuonSV.cxx b/PWGEM/Dilepton/Tasks/dimuonSV.cxx deleted file mode 100644 index ec829a60065..00000000000 --- a/PWGEM/Dilepton/Tasks/dimuonSV.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code is for dimuon analyses. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSV.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dimuon-sv"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx b/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx deleted file mode 100644 index 2643f9a2344..00000000000 --- a/PWGEM/Dilepton/Tasks/dimuonSVMC.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over dalitz ee table for dalitz QC. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/Core/DileptonSVMC.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include -#include - -using namespace o2::framework; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"dimuon-sv-mc"})}; -} From 3c91d2c77bfb6e9e32d2358b62b3bdc47eaafc63 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Tue, 13 Oct 2026 13:01:35 +0200 Subject: [PATCH 272/648] [ALICE3] Add inner- and outermost hit information to alice 3 tracks (#18260) --- ALICE3/Core/DetLayer.h | 16 +++++------ ALICE3/Core/FastTracker.cxx | 16 +++++++++-- ALICE3/Core/FastTracker.h | 6 ++++- ALICE3/Core/GeometryContainer.cxx | 9 +++---- ALICE3/Core/GeometryContainer.h | 6 ++--- ALICE3/DataModel/tracksAlice3.h | 6 ++++- ALICE3/Macros/Configuration/a3geometry_v3.ini | 27 ++++++++++++------- ALICE3/TableProducer/OTF/onTheFlyTracker.cxx | 18 ++++++++++++- .../alice3TrackingTranslator.cxx | 10 ++++--- 9 files changed, 80 insertions(+), 34 deletions(-) diff --git a/ALICE3/Core/DetLayer.h b/ALICE3/Core/DetLayer.h index b3ed3ed6b34..eb5b1cf0550 100644 --- a/ALICE3/Core/DetLayer.h +++ b/ALICE3/Core/DetLayer.h @@ -108,25 +108,25 @@ struct DetLayer { TString mName; // position variables - float mR; // radius in centimeters - float mZ; // mZ dimension in centimeters + float mR{}; // radius in centimeters + float mZ{}; // mZ dimension in centimeters // material variables - float mX0; // radiation length - float mXrho; // density + float mX0{}; // radiation length + float mXrho{}; // density // resolution variables for active layers - float mResRPhi; // RPhi resolution in centimeters - float mResZ; // Z resolution in centimeters + float mResRPhi{}; // RPhi resolution in centimeters + float mResZ{}; // Z resolution in centimeters // efficiency - float mEff; // detection efficiency + float mEff{1.f}; // detection efficiency // dead regions in phi (in radians) TGraph* mDeadPhiRegions = nullptr; // layer type - int mType; // 0: undefined/inert, 1: silicon, 2: gas/tpc + int mType{0}; // 0: undefined/inert, 1: silicon, 2: gas/tpc }; } // namespace o2::fastsim diff --git a/ALICE3/Core/FastTracker.cxx b/ALICE3/Core/FastTracker.cxx index 20eb7087f16..5bf0bcd87e3 100644 --- a/ALICE3/Core/FastTracker.cxx +++ b/ALICE3/Core/FastTracker.cxx @@ -312,6 +312,8 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa nIntercepts = 0; nSiliconPoints = 0; nGasPoints = 0; + trackIU = -1.f; + trackOU = -1.f; std::array posIni{}; // provision for != PV inputTrack.getXYZGlo(posIni); const float initialRadius = std::hypot(posIni[0], posIni[1]); @@ -416,6 +418,9 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa firstLayerReached = il; } lastLayerReached = il; + if (layers[il].isTOF()) { + continue; // TOF layers are not used for tracking, but are kept in range for the innermost/outermost update + } nIntercepts++; } @@ -475,7 +480,7 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa continue; } - if (!layers[il].isInert()) { // only update covm for tracker hits + if (!layers[il].isInert() && !layers[il].isTOF()) { // only update covm for tracker hits const o2::track::TrackParametrization::dim2_t hitpoint = {xyz1[1], xyz1[2]}; const o2::track::TrackParametrization::dim3_t hitpointcov = {layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi(), 0.f, layers[il].getResolutionZ() * layers[il].getResolutionZ()}; @@ -510,7 +515,14 @@ int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } hits.push_back(thisHit); - if (!layers[il].isInert()) { // good hit probability calculation + if (!layers[il].isInert()) { + trackIU = std::hypot(spacePoint[0], spacePoint[1]); + if (trackOU < 0.f) { + trackOU = trackIU; + } + } + + if (!layers[il].isInert() && !layers[il].isTOF()) { // good hit probability calculation float sigYCmb = o2::math_utils::sqrt(inwardTrack.getSigmaY2() + layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi()); float sigZCmb = o2::math_utils::sqrt(inwardTrack.getSigmaZ2() + layers[il].getResolutionZ() * layers[il].getResolutionZ()); goodHitProbability[il] = probGoodChiSqHit(layers[il].getRadius() * 100, sigYCmb * 100, sigZCmb * 100); diff --git a/ALICE3/Core/FastTracker.h b/ALICE3/Core/FastTracker.h index d3d9a11d4ed..1d272595bbb 100644 --- a/ALICE3/Core/FastTracker.h +++ b/ALICE3/Core/FastTracker.h @@ -58,7 +58,7 @@ class FastTracker /// \param phiEnd End angle of the dead region (in radians) void addDeadPhiRegionInLayer(const std::string& layerName, float phiStart, float phiEnd); [[nodiscard]] DetLayer getLayer(const int layer) const { return layers[layer]; } - [[nodiscard]] std::vector getLayers() const { return layers; } + [[nodiscard]] const std::vector& getLayers() const { return layers; } [[nodiscard]] int getLayerIndex(const std::string& name) const; [[nodiscard]] size_t getNLayers() const { return layers.size(); } [[nodiscard]] bool isLayerInert(const int layer) const { return layers[layer].isInert(); } @@ -127,6 +127,8 @@ class FastTracker [[nodiscard]] int getNIntercepts() const { return nIntercepts; } [[nodiscard]] int getNSiliconPoints() const { return nSiliconPoints; } [[nodiscard]] int getNGasPoints() const { return nGasPoints; } + [[nodiscard]] float getTrackIU() const { return trackIU; } + [[nodiscard]] float getTrackOU() const { return trackOU; } [[nodiscard]] float getGoodHitProb(int layer) const { return (layer >= 0 && static_cast(layer) < goodHitProbability.size()) ? goodHitProbability[layer] : 0.0f; @@ -172,6 +174,8 @@ class FastTracker int nIntercepts = 0; /// found in first outward propagation int nSiliconPoints = 0; /// silicon-based space points added to track int nGasPoints = 0; /// tpc-based space points added to track + float trackIU = -1.f; /// innermost update: radius of the innermost active-layer hit added to track + float trackOU = -1.f; /// outermost update: radius of the outermost active-layer hit added to track std::vector goodHitProbability; ClassDef(FastTracker, 1); diff --git a/ALICE3/Core/GeometryContainer.cxx b/ALICE3/Core/GeometryContainer.cxx index 7936e66b629..130c3f9e1e9 100644 --- a/ALICE3/Core/GeometryContainer.cxx +++ b/ALICE3/Core/GeometryContainer.cxx @@ -19,7 +19,6 @@ #include "Common/Core/TableHelper.h" -#include #include #include @@ -41,15 +40,15 @@ void GeometryContainer::init(o2::framework::InitContext& initContext) } LOG(info) << "Size of detector configuration: " << detectorConfiguration.size(); - bool cleanLutWhenLoaded; - const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLutWhenLoaded, false); + bool cleanLut; + const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLut, false); if (!foundCleanLutWhenLoaded) { LOG(fatal) << "Could not retrieve foundCleanLutWhenLoaded option from OnTheFlyDetectorGeometryProvider task."; return; } - setLutCleanupSetting(cleanLutWhenLoaded); + setLutCleanupSetting(cleanLut); - for (std::string& configFile : detectorConfiguration) { + for (const std::string& configFile : detectorConfiguration) { LOG(info) << "Detector geometry configuration file used: " << configFile; addEntry(configFile); } diff --git a/ALICE3/Core/GeometryContainer.h b/ALICE3/Core/GeometryContainer.h index c343ecedbb0..deb69663e3b 100644 --- a/ALICE3/Core/GeometryContainer.h +++ b/ALICE3/Core/GeometryContainer.h @@ -36,8 +36,8 @@ struct GeometryEntry { // Default constructor GeometryEntry() = default; explicit GeometryEntry(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + : mFileName(ConfigurationParser::accessFile(filename, "./.ALICE3/Configuration/", ccdb)) { - mFileName = ConfigurationParser::accessFile(filename, "./.ALICE3/Configuration/", ccdb); mConfigurations = ConfigurationParser::parseTEnvConfiguration(mFileName, mLayerNames); LOG(info) << "Loaded geometry configuration from file: " << mFileName << " with " << mLayerNames.size() << " layers."; if (mLayerNames.empty()) { @@ -45,9 +45,9 @@ struct GeometryEntry { } } - std::map> getConfigurations() const { return mConfigurations; } + const std::map>& getConfigurations() const { return mConfigurations; } std::map getConfiguration(const std::string& layerName) const; - std::vector getLayerNames() const { return mLayerNames; } + const std::vector& getLayerNames() const { return mLayerNames; } bool hasValue(const std::string& layerName, const std::string& key) const; std::string getValue(const std::string& layerName, const std::string& key, bool require = true) const; void setValue(const std::string& layerName, const std::string& key, const std::string& value) { mConfigurations[layerName][key] = value; } diff --git a/ALICE3/DataModel/tracksAlice3.h b/ALICE3/DataModel/tracksAlice3.h index 8e19feb0d0e..327d0587b64 100644 --- a/ALICE3/DataModel/tracksAlice3.h +++ b/ALICE3/DataModel/tracksAlice3.h @@ -32,9 +32,13 @@ DECLARE_SOA_COLUMN(NTPCHits, nTPCHits, int); //! number of tpc hi DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); //! PDG code of the linked truth MC particle DECLARE_SOA_COLUMN(TrackType, trackType, int); //! Type of the track DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! Has track contributed to the PV fit? +DECLARE_SOA_COLUMN(TrackIU, trackIU, float); //! Innermost update: radius (cm) of the innermost hit assigned to the track +DECLARE_SOA_COLUMN(TrackOU, trackOU, float); //! Outermost update: radius (cm) of the outermost hit assigned to the track } // namespace track_alice3 DECLARE_SOA_TABLE(TracksAlice3, "AOD", "TRACKSALICE3", - track_alice3::IsReconstructed); + track_alice3::IsReconstructed, + track_alice3::TrackIU, + track_alice3::TrackOU); using TrackAlice3 = TracksAlice3::iterator; DECLARE_SOA_TABLE(TracksAlice3Pdg, "AOD", "TRACKSALICE3PDG", diff --git a/ALICE3/Macros/Configuration/a3geometry_v3.ini b/ALICE3/Macros/Configuration/a3geometry_v3.ini index e4eb415c133..3381df08209 100644 --- a/ALICE3/Macros/Configuration/a3geometry_v3.ini +++ b/ALICE3/Macros/Configuration/a3geometry_v3.ini @@ -92,15 +92,6 @@ B05.resZ: 0.001 B05.eff: 1.00 B05.type: 1 -iTOF.r: 19 -iTOF.z: 250 -iTOF.x0: 0.01*3. -iTOF.xrho: 0.03 -iTOF.resRPhi: 0.001 -iTOF.resZ: 0.001 -iTOF.eff: 1.00 -iTOF.type: 0 - B06.r: 20. B06.z: 250 B06.x0: 0.01 @@ -110,6 +101,15 @@ B06.resZ: 0.001 B06.eff: 1.00 B06.type: 1 +iTOF.r: 21 +iTOF.z: 250 +iTOF.x0: 0.01*3. +iTOF.xrho: 0.03 +iTOF.resRPhi: 0.001 +iTOF.resZ: 0.001 +iTOF.eff: 1.00 +iTOF.type: 3 + B07.r: 30. B07.z: 250 B07.x0: 0.01 @@ -146,6 +146,15 @@ B10.resZ: 0.001 B10.eff: 1.00 B10.type: 1 +oTOF.r: 92 +oTOF.z: 250 +oTOF.x0: 0.01*3. +oTOF.xrho: 0.03 +oTOF.resRPhi: 0.001 +oTOF.resZ: 0.001 +oTOF.eff: 1.00 +oTOF.type: 3 + # Lookup tables global.lutEl: ccdb:/Users/j/jekarlss/LUTs/v3/b/el global.lutMu: ccdb:/Users/j/jekarlss/LUTs/v3/b/mu diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index ca0bc59230d..7e84ec9c223 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -299,6 +299,8 @@ struct OnTheFlyTracker { int nSiliconHits = 0; ///< number of silicon hits int nTPCHits = 0; ///< number of TPC hits TrackType trackType = TrackType::kNone; + float trackIU = -1.f; ///< radius of the innermost hit (-1 if not available) + float trackOU = -1.f; ///< radius of the outermost hit (-1 if not available) }; // Helper struct to pass cascade information @@ -1828,7 +1830,7 @@ struct OnTheFlyTracker { tableTrackSelection(static_cast(0), false, false, false, false, false, false); tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); } - tableTracksAlice3(true); + tableTracksAlice3(true, trackParCov.trackIU, trackParCov.trackOU); } } @@ -1949,12 +1951,14 @@ struct OnTheFlyTracker { bool reconstructed = true; int nTrkHits = 0; + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} if (enablePrimarySmearing) { if (fastPrimaryTrackerSettings.fastTrackPrimaries && longLivedToBeHandled) { o2::track::TrackParCov perfectTrackParCov = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDB); perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } @@ -1965,6 +1969,7 @@ struct OnTheFlyTracker { const std::array decayVtx = decayer.generateDecayVertex(mcParticle, pdgDB); const float decayRadius2D = std::hypot(decayVtx[0], decayVtx[1]); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, decayRadius2D); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } @@ -2033,8 +2038,12 @@ struct OnTheFlyTracker { bool isPVContributor = trackType == TrackType::kRecoPrimary; if (reconstructed) { recoPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + recoPrimaries.back().trackIU = hitRadii[0]; + recoPrimaries.back().trackOU = hitRadii[1]; } else { ghostPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + ghostPrimaries.back().trackIU = hitRadii[0]; + ghostPrimaries.back().trackOU = hitRadii[1]; } } @@ -2176,6 +2185,7 @@ struct OnTheFlyTracker { bool reconstructed = false; int nTrkHits = 0; + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} const bool isSecondary = !otfParticle.isPrimary() && otfParticle.checkBit(o2::upgrade::DecayerBits::ProducedByDecayer) && otfParticle.isAlive(); if (enablePrimarySmearing && longLivedToBeHandled && otfParticle.isPrimary()) { o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); @@ -2186,6 +2196,7 @@ struct OnTheFlyTracker { perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, mcParticle.decayRadius()); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; getHist(histPath + "h2dGenShortLivedParticleRadius")->Fill(mcParticle.decayRadius(), perfectTrackParCov.getPt()); getHist(histPath + "h2dGenRadiusIniVsDecay")->Fill(std::hypot(perfectTrackParCov.getX(), perfectTrackParCov.getY()), mcParticle.decayRadius()); if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { @@ -2203,6 +2214,7 @@ struct OnTheFlyTracker { computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; reconstructed = nTrkHits >= fastTrackerSettings.minSiliconHits; } @@ -2260,9 +2272,13 @@ struct OnTheFlyTracker { if (reconstructed) { tracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, true, false, 0, nTrkHits, kRecoPrimary}); + tracksAlice3.back().trackIU = hitRadii[0]; + tracksAlice3.back().trackOU = hitRadii[1]; getHist(histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); } else { ghostTracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, false, false, false, 0, nTrkHits, kGhostPrimary}); + ghostTracksAlice3.back().trackIU = hitRadii[0]; + ghostTracksAlice3.back().trackOU = hitRadii[1]; } } diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index 983092eb228..26f53cf9f5f 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -419,7 +419,7 @@ struct Alice3TrackingTranslator { // Convert tracks from ACTS to ALICE format const size_t nParticlesSim = fileParticlesSim.m_vx->size(); const size_t nTracks = fileTracksummary.m_ePX_fit->size(); - std::vector idMCparticles; + // std::vector idMCparticles; // fixme: unused // local index k within this event -> global AO2D index int firstIdxThisEvent = tableStoredMcParticles.lastIndex() + 1; @@ -438,7 +438,7 @@ struct Alice3TrackingTranslator { fileParticlesSim.m_particle->at(iPart), fileParticlesSim.m_generation->at(iPart), fileParticlesSim.m_sub_particle->at(iPart)); - barcodeToGlobalIdx[key] = firstIdxThisEvent + (int)iPart; + barcodeToGlobalIdx[key] = firstIdxThisEvent + static_cast(iPart); if (iPart == 0) { tableMcCollisions(0, // mccollision::BCId, @@ -486,7 +486,7 @@ struct Alice3TrackingTranslator { } } for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { - int globalIdx = firstIdxThisEvent + (int)iPart; + int globalIdx = firstIdxThisEvent + static_cast(iPart); int motherIdx = -1; if (GlobalIdxToMotherIdx.find(globalIdx) != GlobalIdxToMotherIdx.end()) { motherIdx = GlobalIdxToMotherIdx[globalIdx]; @@ -666,7 +666,9 @@ struct Alice3TrackingTranslator { tableTracksDCACov(0.0f, // sigmaDcaXY2 0.0f); // sigmaDcaZ2 // Fill ALICE3 specific tables - tableTracksAlice3(true); // isReconstructed + tableTracksAlice3(true, // isReconstructed + -1.f, // todo: track innermost update + -1.f); // todo: track outermost update if (mcParticleIdx > 0) tableTracksAlice3Pdg(GlobalIdxToPDGCode[mcParticleIdx]); // PdgCode to the linked MC truth particle else From 0eddf045bc35d1394a0f68afa5bbc89cd43be87b Mon Sep 17 00:00:00 2001 From: YubiaoWang Date: Tue, 13 Oct 2026 13:24:34 +0200 Subject: [PATCH 273/648] [PWGJE] jetChargedV2: fix the memory consumption issue problem (#18267) --- PWGJE/Tasks/jetChargedV2.cxx | 315 ++++++++++++++++++----------------- 1 file changed, 159 insertions(+), 156 deletions(-) diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 51e31090ffc..124d8bb6f10 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -91,31 +91,34 @@ struct JetChargedV2 { Configurable nBinsEta{"nBinsEta", 200, "number of bins for eta axes"}; Configurable randomConeLeadJetDeltaR{"randomConeLeadJetDeltaR", -99.0, "min distance between leading jet axis and random cone (RC) axis; if negative, min distance is set to automatic value of R_leadJet+R_RC "}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; - // Fluctuation histogram axes, kept identical to jetBackgroundAnalysis (h2_centrality_rhorandomcone*) - // so that the in-plane / out-of-plane fluctuations can be combined into the response matrix - // by the local unfolding tools (AnalysisToolsO2-evtpln). Configurable nBinsFluct{"nBinsFluct", 1000, "number of bins for fluctuations axes"}; Configurable deltaPtMinMaxFluct{"deltaPtMinMaxFluct", 200, "maximum and minimum delta pt value for fluctuation axes"}; + Configurable nBinsFluctCentrality{"nBinsFluctCentrality", 1100, "number of centrality bins for the fluctuation axes; reduce it (e.g. 110) to cut the memory of the fluctuation histograms"}; + Configurable nBinsDeltaPhi{"nBinsDeltaPhi", 100, "number of Delta phi bins for the fluctuation axes; 96 keeps the pi/4 in-/out-of-plane edges on bin boundaries"}; + Configurable fillFluctH3{"fillFluctH3", false, "fill the 3D centrality-vs-deltaPt-vs-DeltaPhi fluctuation histograms (memory heavy); the direct 2D in-/out-of-plane histograms are always filled"}; + Configurable fillQvecQA{"fillQvecQA", true, "fill the Q-vector QA histograms (histQvec*V#, 3D Qx-vs-Qy-vs-centrality, memory heavy); the event-plane QA histograms are always filled"}; //=====================< tracking efficiency >=====================// Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; //=====================< evt pln >=====================// - Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; - Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; - Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; - Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; - Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; - Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; - Configurable detCheck{"detCheck", 4, "total qvector number"}; + struct EpQvecSettings : ConfigurableGroup { + Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; + Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; + Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; + Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; + Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; + Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; + Configurable detCheck{"detCheck", 4, "total qvector number"}; + ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; + ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; + ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; + } epQvec; Configurable rhoFitRangeMargin{"rhoFitRangeMargin", 0.5, "extension of the rho modulation fit function range"}; Configurable checkAbsDeltaPhi{"checkAbsDeltaPhi", false, "use 0-pi event-plane region for in/out-plane"}; - ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; - ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; ConfigurableAxis cfgAxisVnCent{"cfgAxisVnCent", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 50, 70, 100}, " % "}; - ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; EventPlaneHelper helperEP; int detId = 0; int refAId = 0; @@ -190,10 +193,10 @@ struct JetChargedV2 { void init(o2::framework::InitContext&) { doSumw2 = skipMBGapEvents; - detId = getDetId(cfgDetName); - detIdCheck = getDetId(cfgDetCheckName); - refAId = getDetId(cfgRefAName); - refBId = getDetId(cfgRefBName); + detId = getDetId(epQvec.cfgDetName); + detIdCheck = getDetId(epQvec.cfgDetCheckName); + refAId = getDetId(epQvec.cfgRefAName); + refBId = getDetId(epQvec.cfgRefBName); if (detId == refAId || detId == refBId || refAId == refBId || detId == detIdCheck) { detId = 0; detIdCheck = 0; @@ -236,8 +239,9 @@ struct JetChargedV2 { AxisSpec jetPtAxisRhoAreaSub = {400, -200., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetEtaAxis = {nBinsEta, -1.0, 1.0, "#eta"}; // fluctuation axes, same as jetBackgroundAnalysis's h2_centrality_rhorandomcone* - AxisSpec fluctCentralityAxis = {1100, 0.0, 110.0, "centrality (%)"}; + AxisSpec fluctCentralityAxis = {nBinsFluctCentrality, 0.0, 110.0, "centrality (%)"}; AxisSpec fluctDeltaPtAxis = {nBinsFluct, -deltaPtMinMaxFluct, deltaPtMinMaxFluct, "#delta #it{p}_{T} (GeV/#it{c})"}; + AxisSpec fluctDeltaPhiAxis = {nBinsDeltaPhi, 0., o2::constants::math::TwoPI, "#Delta#varphi_{jet}"}; AxisSpec axisPt = {40, 0.0, 4.0}; AxisSpec axisEta = {32, -0.8, 0.8}; @@ -305,27 +309,29 @@ struct JetChargedV2 { registry.add("h_mcd_pt_before_matching_mcdprocess", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); //=====================< evt pln plot >=====================// - AxisSpec axisCent{cfgAxisCent, "centrality"}; - AxisSpec axisQvec{cfgAxisQvec, "Q"}; - AxisSpec axisQvecF{cfgAxisQvecF, "Q"}; + AxisSpec axisCent{epQvec.cfgAxisCent, "centrality"}; + AxisSpec axisQvec{epQvec.cfgAxisQvec, "Q"}; + AxisSpec axisQvecF{epQvec.cfgAxisQvecF, "Q"}; AxisSpec axisEvtPl{360, -constants::math::PI, constants::math::PI}; - for (uint i = 0; i < cfgnMods->size(); i++) { - histosQA.add(Form("histQvecUncorV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecRectrV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecTwistV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecFinalV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + if (fillQvecQA) { // 3D Qx-vs-Qy-vs-centrality, memory heavy + histosQA.add(Form("histQvecUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + } - histosQA.add(Form("histEvtPlUncorV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRectrV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlTwistV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlFinalV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("h_ep2_FT0CV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("h_ep2_FT0MV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0CV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0MV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefAV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_RefARefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_SigRefAV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_SigRefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_RefARefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); } histosQA.add("histCent", "Centrality TrkProcess", HistType::kTH1F, {axisCent}); @@ -382,13 +388,19 @@ struct JetChargedV2 { registry.add("leadJetEta", "leadJet constituent #eta ", {HistType::kTH1F, {{100, -1.0, 1.0}}}); //< RC test plots >// - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, {100, 0., o2::constants::math::TwoPI}}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); if (checkAbsDeltaPhi) { @@ -581,8 +593,6 @@ struct JetChargedV2 { Filter mcEventCuts = (nabs(aod::jmccollision::posZ) < vertexZCut); Preslice tracksPerJCollision = o2::aod::jtrack::collisionId; - Preslice mcdjetsPerJCollision = o2::aod::jet::collisionId; - PresliceUnsorted> collisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; template bool isAcceptedJet(TJets const& jet, bool mcLevelIsParticleLevel = false) @@ -936,29 +946,27 @@ struct JetChargedV2 { registry.fill(HIST("leadJetEtaMCP"), leadingJetEta); } - // Integrate the rho modulation fit over [phi - radius, phi + radius]. The integration - // limits are kept inside [0, 2pi) by splitting the window into two pieces whenever it + // Integrate the rho modulation fit over [phi - radius, phi + radius]. Both limits are + // wrapped into [0, 2pi) with RecoDecay::constrainAngle; when the wrapped interval + // crosses zero it is split into two pieces (a plain integral between the two wrapped + // limits would be negative there). double getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin) { if (!fitFunc) { return 0.0; } if (radius > fitRangeMargin) { - static bool warnOnce = true; // called once per jet / random cone, so warn only once + static bool warnOnce = true; if (warnOnce) { LOGF(warning, "Rho fit range margin (%.3f) is smaller than integration radius (%.3f)", fitRangeMargin, radius); warnOnce = false; } } - phi = RecoDecay::constrainAngle(phi, 0.); const double twoPi = o2::constants::math::TwoPI; - const double low = phi - radius; - const double high = phi + radius; - if (low < 0.) { // window crosses 0: split into two pieces - return fitFunc->Integral(low + twoPi, twoPi) + fitFunc->Integral(0., high); - } - if (high > twoPi) { // window crosses 2*pi: split into two pieces - return fitFunc->Integral(low, twoPi) + fitFunc->Integral(0., high - twoPi); + const double low = RecoDecay::constrainAngle(phi - radius, 0.); + const double high = RecoDecay::constrainAngle(phi + radius, 0.); + if (high < low) { // wrapped interval crosses zero + return fitFunc->Integral(low, twoPi) + fitFunc->Integral(0., high); } return fitFunc->Integral(low, high); } @@ -992,9 +1000,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -1028,7 +1036,6 @@ struct JetChargedV2 { hPtsumSumptFitMCPFill->Fit(fFitModulationV2v3P, "Q", "ep", 0, o2::constants::math::TwoPI); - // double temppara[5]; std::array temppara{}; temppara[0] = fFitModulationV2v3P->GetParameter(0); temppara[1] = fFitModulationV2v3P->GetParameter(1); @@ -1043,9 +1050,9 @@ struct JetChargedV2 { registry.fill(HIST("h_mcp_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_mcp_evtnum_centrlity"), evtnum, centrality); - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1380,19 +1387,21 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int detIndFT0M = (detIdCheck + 2) * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = (detIdCheck + 2) * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1408,10 +1417,12 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1446,18 +1457,20 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1469,10 +1482,12 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1547,9 +1562,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { @@ -1599,54 +1614,41 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) { - return; - } + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { - continue; + continue; // empty bins do not enter the chi2 and must not count in the dof } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - - } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); - } - if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int detIndFT0M = 4 * detCheck + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = 4 * epQvec.detCheck + epQvec.cfgnTotalSystem * 4 * (nmode - 2); int bumpLow = 75; int bumpUp = 125; @@ -1805,7 +1807,7 @@ struct JetChargedV2 { } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; @@ -1814,7 +1816,7 @@ struct JetChargedV2 { integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; float absRcPhiPsi2 = 0.0; @@ -1831,7 +1833,9 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); @@ -1877,8 +1881,10 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); @@ -1990,9 +1996,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -2044,47 +2050,40 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) { - return; - } + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { - continue; + continue; // empty bins do not enter the chi2 and must not count in the dof } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -2140,14 +2139,14 @@ struct JetChargedV2 { } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; double integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); double rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; // rcPhiPsi2 = randomConePhi - ep2; @@ -2162,7 +2161,9 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet @@ -2200,8 +2201,10 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } } else if (nmode == cfgNmodB) { continue; } @@ -2388,9 +2391,9 @@ struct JetChargedV2 { int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); From 428eb63f852dda3536d9cb427bbf55eb382d67ed Mon Sep 17 00:00:00 2001 From: Jaehyeok Ryu <57662730+JaehyeokRyu@users.noreply.github.com> Date: Tue, 13 Oct 2026 20:24:37 +0900 Subject: [PATCH 274/648] [PWGJE] Align UPC QA track selection with derived data (#18264) Co-authored-by: jryu --- PWGJE/Tasks/jetUpcQa.cxx | 103 +++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/PWGJE/Tasks/jetUpcQa.cxx b/PWGJE/Tasks/jetUpcQa.cxx index fa1da836679..ed86c080f57 100644 --- a/PWGJE/Tasks/jetUpcQa.cxx +++ b/PWGJE/Tasks/jetUpcQa.cxx @@ -66,6 +66,7 @@ DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); } // namespace upcjetevent DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", @@ -78,7 +79,8 @@ DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", upcjetevent::TimeZNC, upcjetevent::NeutronClass, upcjetevent::IsGapTagged, - upcjetevent::IsZDCTagged); + upcjetevent::IsZDCTagged, + upcjetevent::CollisionIndex); using UpcJetEvent = UpcJetEvents::iterator; namespace upcjet @@ -106,13 +108,23 @@ DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, " DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); } // namespace upctrack DECLARE_SOA_TABLE(UpcTracks, "AOD", "UPCTRACK", upctrack::UpcJetEventId, upctrack::Pt, upctrack::Eta, - upctrack::Phi); + upctrack::Phi, + upctrack::TrackCutFlag, + upctrack::TrackCutFlagFb5, + upctrack::DcaXY, + upctrack::DcaZ, + upctrack::PassesConfiguredSelection); using UpcTrack = UpcTracks::iterator; namespace upcjettrack @@ -147,6 +159,7 @@ DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); } // namespace upcjeteventmcd DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", @@ -159,7 +172,8 @@ DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", upcjeteventmcd::TimeZNC, upcjeteventmcd::NeutronClass, upcjeteventmcd::IsGapTagged, - upcjeteventmcd::IsZDCTagged); + upcjeteventmcd::IsZDCTagged, + upcjeteventmcd::CollisionIndex); using UpcJetEventMCD = UpcJetEventsMCD::iterator; namespace upctrackmcd @@ -168,13 +182,23 @@ DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEve DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); } // namespace upctrackmcd DECLARE_SOA_TABLE(UpcTracksMCD, "AOD", "UPCTRACKMCD", upctrackmcd::UpcJetEventMCDId, upctrackmcd::Pt, upctrackmcd::Eta, - upctrackmcd::Phi); + upctrackmcd::Phi, + upctrackmcd::TrackCutFlag, + upctrackmcd::TrackCutFlagFb5, + upctrackmcd::DcaXY, + upctrackmcd::DcaZ, + upctrackmcd::PassesConfiguredSelection); using UpcTrackMCD = UpcTracksMCD::iterator; namespace upcjetmcd @@ -332,8 +356,11 @@ struct JetUpcQa { Configurable cfgMaxNTracks{"cfgMaxNTracks", 100, "Max tracks"}; Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.15f, "Min track pT (GeV/c)"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 100.f, "Max track pT (GeV/c), matching derived-data kinematics"}; Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.9f, "Max track |eta|"}; - Configurable cfgTrackSelection{"cfgTrackSelection", "kinematicsOnly", "Reconstructed-track selection: kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgTrackSelection{"cfgTrackSelection", "derivedDataSelection", "Reconstructed-track selection: derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgDerivedTrackDCAZMax{"cfgDerivedTrackDCAZMax", 0.2f, "Maximum |DCAz| for the derived-data QualityTracksWDCA selection (cm)"}; + Configurable cfgVertexZCut{"cfgVertexZCut", 10.f, "Maximum absolute collision vertex z (cm)"}; Configurable cfgTrackDCAZMax{"cfgTrackDCAZMax", 2.f, "Maximum |DCAz| for udDefault track selection (cm)"}; Configurable cfgTrackDCAXYMax{"cfgTrackDCAXYMax", 0.f, "Maximum |DCAxy| for udDefault (cm); 0 uses the pT-dependent UD cut"}; Configurable cfgTrackTPCChi2NClMax{"cfgTrackTPCChi2NClMax", 4.f, "Maximum TPC chi2 per cluster for udDefault"}; @@ -367,23 +394,29 @@ struct JetUpcQa { HistogramRegistry registry{"registry"}; enum class TrackSelectionMode { + DerivedDataSelection, + DerivedQualityTracksWDCA, KinematicsOnly, GlobalTracks, UDDefault }; - TrackSelectionMode trackSelectionMode = TrackSelectionMode::KinematicsOnly; + TrackSelectionMode trackSelectionMode = TrackSelectionMode::DerivedDataSelection; // Init void init(InitContext&) { - if (cfgTrackSelection.value == "kinematicsOnly") { + if (cfgTrackSelection.value == "derivedDataSelection") { + trackSelectionMode = TrackSelectionMode::DerivedDataSelection; + } else if (cfgTrackSelection.value == "derivedQualityTracksWDCA") { + trackSelectionMode = TrackSelectionMode::DerivedQualityTracksWDCA; + } else if (cfgTrackSelection.value == "kinematicsOnly") { trackSelectionMode = TrackSelectionMode::KinematicsOnly; } else if (cfgTrackSelection.value == "globalTracks") { trackSelectionMode = TrackSelectionMode::GlobalTracks; } else if (cfgTrackSelection.value == "udDefault") { trackSelectionMode = TrackSelectionMode::UDDefault; } else { - LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); + LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); } sgCuts.SetNDtcoll(cfgNDtcoll); @@ -420,12 +453,29 @@ struct JetUpcQa { registry.add("hSelectionBitsMCD", "Selection bits (MCD);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); } + template + bool passesTrackKinematics(TTrack const& track) const + { + return std::abs(track.eta()) <= cfgTrackEtaMax.value && + track.pt() >= cfgTrackPtMin.value && + track.pt() <= cfgTrackPtMax.value; + } + template bool isSelectedTrack(TTrack const& track) const { - if (std::abs(track.eta()) > cfgTrackEtaMax.value || track.pt() < cfgTrackPtMin.value) { + if (!passesTrackKinematics(track)) { return false; } + if (trackSelectionMode == TrackSelectionMode::DerivedQualityTracksWDCA) { + // Mirror JetDerivedDataUtilities::setTrackSelectionBit(...): QualityTracksWDCA. + return track.isQualityTrack() && std::abs(track.dcaZ()) < cfgDerivedTrackDCAZMax.value; + } + if (trackSelectionMode == TrackSelectionMode::DerivedDataSelection) { + // Match the derived-data writer: retain tracks passing any stored selection bit + // (global, quality, or hybrid). QualityTracksWDCA is a subset of QualityTracks. + return track.isGlobalTrackWoPtEta() || track.isQualityTrack() || track.trackCutFlagFb5(); + } if (trackSelectionMode == TrackSelectionMode::KinematicsOnly) { return true; } @@ -505,9 +555,10 @@ struct JetUpcQa { return zdc.neutronClass > 0; } - bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged) const + bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged, float posZ) const { - return (!cfgUseGapTagging.value || isGapTagged) && + return std::abs(posZ) <= cfgVertexZCut.value && + (!cfgUseGapTagging.value || isGapTagged) && (!cfgUseZDCTagging.value || isZDCTagged); } @@ -748,7 +799,7 @@ struct JetUpcQa { int gapSide = gapInfo.gapSide; bool isGapTagged = isSelectedGap(gapSide); bool isZDCTagged = isSelectedZDC(gapInfo.zdc); - bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); registry.fill(HIST("hGapSide"), gapSide); registry.fill(HIST("hZNAEnergy"), gapInfo.zdc.energyCommonZNA); registry.fill(HIST("hZNCEnergy"), gapInfo.zdc.energyCommonZNC); @@ -773,7 +824,9 @@ struct JetUpcQa { gapInfo.zdc.timeZNC, gapInfo.zdc.neutronClass, isGapTagged, - isZDCTagged); + isZDCTagged, + collision.globalIndex()); + // This is the row index in our output event table, not the input collision index. int evtIdx = upcJetEventTable.lastIndex(); std::vector fjInput; @@ -783,12 +836,17 @@ struct JetUpcQa { if (t.collisionId() != collision.globalIndex()) { continue; } - if (!isSelectedTrack(t)) { + if (!passesTrackKinematics(t)) { continue; } + bool passesConfiguredSelection = isSelectedTrack(t); - // event-level track table - upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi()); + // Store all kinematically accepted candidates and the current decision so + // alternate track selections can be reproduced from the output table. + upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } // jet finding input fastjet::PseudoJet p; @@ -843,7 +901,7 @@ struct JetUpcQa { int gapSide = gapInfo.gapSide; bool isGapTagged = isSelectedGap(gapSide); bool isZDCTagged = isSelectedZDC(gapInfo.zdc); - bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); registry.fill(HIST("hGapSideMCD"), gapSide); registry.fill(HIST("hZNAEnergyMCD"), gapInfo.zdc.energyCommonZNA); registry.fill(HIST("hZNCEnergyMCD"), gapInfo.zdc.energyCommonZNC); @@ -868,7 +926,8 @@ struct JetUpcQa { gapInfo.zdc.timeZNC, gapInfo.zdc.neutronClass, isGapTagged, - isZDCTagged); + isZDCTagged, + collision.globalIndex()); int evtIdx = upcJetEventTableMCD.lastIndex(); std::vector fjInput; @@ -878,11 +937,15 @@ struct JetUpcQa { if (t.collisionId() != collision.globalIndex()) { continue; } - if (!isSelectedTrack(t)) { + if (!passesTrackKinematics(t)) { continue; } + bool passesConfiguredSelection = isSelectedTrack(t); - upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi()); + upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } fastjet::PseudoJet p; p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); From 5122ef2e83adc37e9a04566a779bed3840e5bfe0 Mon Sep 17 00:00:00 2001 From: MRazza <118839113+MRazza879@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:26:30 +0200 Subject: [PATCH 275/648] [PWGHF] Add efficiency calculations and update selections in deuteron-from-Lb task (#18266) Co-authored-by: Marta Razza --- PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx | 413 +++++++++++++++++-------- 1 file changed, 282 insertions(+), 131 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index d50ee5c5c1b..496ee9c4176 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -59,7 +59,6 @@ struct HfTaskDeuteronFromLb { Configurable applySkimming{"applySkimming", false, "Skimmed dataset processing"}; Configurable cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"}; Configurable sel8{"sel8", true, "sel8 event selection"}; - Configurable separateAntideuterons{"separateAntideuterons", true, "Fill FromLb histos for primary deuterons whose mother is Lb, Primary histos for all others"}; Configurable cfgEta{"cfgEta", 0.8f, "Track eta selection"}; Configurable cfgTPCNclsFound{"cfgTPCNclsFound", 100, "Minimum TPC clusters found"}; Configurable cfgTPCChi2Ncl{"cfgTPCChi2Ncl", 4.0f, "Maximum TPC chi2 per N clusters"}; @@ -73,29 +72,31 @@ struct HfTaskDeuteronFromLb { Configurable cfgDCAmin{"cfgDCAmin", 0.05f, "Minimum DCA for deuteron PID"}; Configurable cfgDCAmax{"cfgDCAmax", 1000.0f, "Maximum DCA for deuteron PID"}; Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; - Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, above this value TPC+TOF PID is used, below only TPC PID is used"}; + Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, above this value TPC+TOF PID is used, below TPC+ITS PID is used"}; // PDG codes Configurable pdgCodeBeautyMeson{"pdgCodeBeautyMeson", -521, "PDG code of the beauty meson mother particle (default: B-)"}; Configurable pdgCodeBeautyBaryon{"pdgCodeBeautyBaryon", -5122, "PDG code of the beauty baryon mother particle (default: anti-Lambda_b)"}; Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; + Configurable pdgCodeHypertriton{"pdgCodeHypertriton", -1010010030, "PDG code of the hypertriton mother particle (default: anti-hypertriton)"}; int mRunNumber = 0; - float dBz = 0.f; int mCurrentRun = -1; framework::Service ccdb{}; using CollisionCandidates = o2::soa::Join; - using MCTrackCandidates = o2::soa::Join; + using TrackCandidates = o2::soa::Join; + using MCTrackCandidates = o2::soa::Join; using MCCollisionCandidates = o2::soa::Join; - using TrackCandidates = o2::soa::Join; Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + Preslice mcParticlesPerCollision = o2::aod::mcparticle::mcCollisionId; ConfigurableAxis ptAxis{"ptAxis", {100, 0.f, 10.f}, "p_{T} GeV/c"}; ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"}; - ConfigurableAxis dcaXyAxis{"dcaXyAxis", {1000, -0.2f, 0.2f}, "DCA xy (cm)"}; - ConfigurableAxis dcaZAxis{"dcaZAxis", {1000, -0.2f, 0.2f}, "DCA z (cm)"}; + ConfigurableAxis dcaXyAxis{"dcaXyAxis", {4000, -2.0f, 2.0f}, "DCA xy (cm)"}; + ConfigurableAxis dcaZAxis{"dcaZAxis", {4000, -2.0f, 2.0f}, "DCA z (cm)"}; + ConfigurableAxis ctauHypertritonAxis{"ctauHypertritonAxis", {100, 0.f, 30.f}, "c#tau (cm)"}; HistogramRegistry qaHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -103,12 +104,12 @@ struct HfTaskDeuteronFromLb { OutputObj zorroSummary{"zorroSummary"}; OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; - bool isSelectedBeautyHadron(int pdgCode) + bool isSelectedMother(int pdgCode) { - return pdgCode == pdgCodeBeautyMeson || pdgCode == pdgCodeBeautyBaryon; + return pdgCode == pdgCodeBeautyMeson || pdgCode == pdgCodeBeautyBaryon || pdgCode == pdgCodeHypertriton; } - double getBeautyHadronMass(int pdgCode) + double getMotherMass(int pdgCode) { if (pdgCode == pdgCodeBeautyMeson) { return o2::constants::physics::MassBPlus; @@ -117,6 +118,9 @@ struct HfTaskDeuteronFromLb { if (pdgCode == pdgCodeBeautyBaryon) { return o2::constants::physics::MassLambdaB0; } + if (pdgCode == pdgCodeHypertriton) { + return o2::constants::physics::MassHyperTriton; + } return -1.; } @@ -139,54 +143,76 @@ struct HfTaskDeuteronFromLb { qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); qaHistos.add("Data/hnSigmaTOFVsPtPurity", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVetoTPCTOFClean", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC and TOF veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); // MC generated-level histograms qaHistos.add("MCGen/ptGeneratedBminus", "p_{T} generated B^{-};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptGeneratedAntiLambdaB", "p_{T} generated #bar{#Lambda}_{b};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/ptAntiDeuteronFromBeautyHadron", "p_{T} #bar{d} from beauty gen hadrons;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCGen/hMotherPdgCode", "PDG code of mother, gen level;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); qaHistos.add("MCGen/ctauBminus", "ctau of B^{-}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCGen/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); - - // MC reco/MC-anchored histograms - qaHistos.add("MCReco/ptAntiDeuteronFromBminus", - "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - - qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", - "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - qaHistos.add( - "MCReco/ptAntiDeuteronPrimary", - "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", - HistType::kTH1F, - {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ptAntiDeuteronAll", "p_{T} #bar{d} all gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hDCAxy-Primary", "DCAxy primary reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); - qaHistos.add("MCReco/hDCAxy-FromBeautyHadron", "DCAxy from beauty reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {{400, -0.2f, 0.2f, "DCA xy (cm)"}}}); + qaHistos.add("MCGen/hEvents", "Events MC gen;;Counts", HistType::kTH1F, {{2, 0., 2.}}); + qaHistos.add("MCGen/ptGeneratedAntiHypertriton", "p_{T} generated ^{3}_{#bar{#Lambda}}#bar{H};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, gen level;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); + qaHistos.add("MCGen/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + // MC reco/MC-anchored histograms + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminus", "DCAxy #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaB", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimary", "DCAxy #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminus", "DCAz #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaB", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimary", "DCAz #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/hEvents", "Events MC reco;;Counts", HistType::kTH1F, {{3, 0., 3.}}); + qaHistos.add("MCReco/hDCAxy-Primary", "DCAxy primary reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxy-FromBMeson", "DCAxy from B^{-} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxy-FromAntiLambdaB", "DCAxy from #bar{#Lambda}_{b} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); qaHistos.add("MCReco/hMotherPdgCode", "PDG code of mother, reco/MC anchored;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); qaHistos.add("MCReco/ctauBminus", "ctau of B^{-}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCReco/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelected", "p_{T} #bar{d} reco/MC anchored PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("MCReco/hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC only PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC only PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromBminus", "p_{T} #bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertriton", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertriton", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/hDCAxy-FromAntiHypertriton", "DCAxy from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); + qaHistos.add("MCReco/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, reco/MC anchored;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenPrimary", "p_{T} #bar{d} primary gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex"); + auto hEvents = qaHistos.get(HIST("MCReco/hEvents")); + hEvents->GetXaxis()->SetBinLabel(1, "All"); + hEvents->GetXaxis()->SetBinLabel(2, "sel8"); + hEvents->GetXaxis()->SetBinLabel(3, "z-vertex"); + auto hEventsGen = qaHistos.get(HIST("MCGen/hEvents")); + hEventsGen->GetXaxis()->SetBinLabel(1, "All"); + hEventsGen->GetXaxis()->SetBinLabel(2, "z-vertex"); + + qaHistos.add("MCReco/hMotherCategory", "Mother of #bar{d};;Counts", HistType::kTH1F, {{5, 0., 5.}}); + auto hMotherCategory = qaHistos.get(HIST("MCReco/hMotherCategory")); + hMotherCategory->GetXaxis()->SetBinLabel(1, "B^{-}"); + hMotherCategory->GetXaxis()->SetBinLabel(2, "#bar{#Lambda}_{b}"); + hMotherCategory->GetXaxis()->SetBinLabel(3, "^{3}_{#bar{#Lambda}}#bar{H}"); + hMotherCategory->GetXaxis()->SetBinLabel(4, "primary"); + hMotherCategory->GetXaxis()->SetBinLabel(5, "other"); } void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) @@ -194,12 +220,12 @@ struct HfTaskDeuteronFromLb { if (mRunNumber == bc.runNumber()) { return; } - dBz = 0.f; if (applySkimming) { zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgSkimming.value); zorro.populateHistRegistry(registry, bc.runNumber()); } + mRunNumber = bc.runNumber(); } template @@ -208,6 +234,9 @@ struct HfTaskDeuteronFromLb { if (std::abs(track.eta()) > cfgEta) { return false; } + if (std::abs(track.rapidity(o2::constants::physics::MassDeuteron)) > rapidityCut) { + return false; + } if (!track.hasITS()) { return false; } @@ -244,6 +273,7 @@ struct HfTaskDeuteronFromLb { TrackCandidates const& tracks, o2::aod::BCsWithTimestamps const&) { + auto tracksWithItsPid = o2::soa::Attach(tracks); for (const auto& collision : collisions) { if (mCurrentRun != collision.bc_as().runNumber()) { auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", collision.bc_as().timestamp()); @@ -271,7 +301,6 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hVtxZ"), collision.posZ()); const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - auto tracksWithItsPid = o2::soa::Attach(tracks); for (const auto& trackId : trackIdsThisCollision) { const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); @@ -296,10 +325,18 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); - const bool isTPCDeClean = isTPCDe && - std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma; + // TPC veto against pi/K/p/e + const bool passTPCVeto = std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaEl()) > cfgTPCNsigma; + // TOF veto against pi/K/p/e + const bool passTOFVeto = std::abs(track.tofNSigmaPi()) > cfgTofNsigma && + std::abs(track.tofNSigmaKa()) > cfgTofNsigma && + std::abs(track.tofNSigmaPr()) > cfgTofNsigma && + std::abs(track.tofNSigmaEl()) > cfgTofNsigma; + + const bool isTPCDeClean = isTPCDe && passTPCVeto; if (track.pt() < ptThresholdforPID) { if (isITSDe) { @@ -312,17 +349,15 @@ struct HfTaskDeuteronFromLb { if (isTPCDeClean && track.hasTOF()) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); } + if (isTPCDeClean && passTOFVeto && track.hasTOF()) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVetoTPCTOFClean"), track.pt(), track.tofNSigmaDe()); + } } - if (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) { - qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); - qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); - qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); - qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); - qaHistos.fill(HIST("Data/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("Data/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); - } - if (track.pt() >= ptThresholdforPID && isTPCDe && isTOFDe && track.hasTOF()) { + const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || + (track.pt() >= ptThresholdforPID && isTPCDeClean && passTOFVeto && isTOFDe && track.hasTOF()); + + if (passFinalSel) { qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); @@ -335,95 +370,200 @@ struct HfTaskDeuteronFromLb { } PROCESS_SWITCH(HfTaskDeuteronFromLb, processData, "processData", true); - void processMC(MCCollisionCandidates::iterator const& collision, MCTrackCandidates const& tracks, o2::aod::McParticles const&) + void processMC(MCCollisionCandidates const& collisions, + o2::aod::TrackAssoc const& trackIndices, + MCTrackCandidates const& tracks, + o2::aod::McParticles const& mcParticles, + o2::aod::BCsWithTimestamps const&) { - for (const auto& track : tracks) { - if (std::abs(track.dcaXY()) < cfgDCAmin || std::abs(track.dcaXY()) > cfgDCAmax) { - continue; + auto tracksWithItsPid = o2::soa::Attach(tracks); + tracksWithItsPid.bindExternalIndices(&mcParticles); + + for (const auto& collision : collisions) { + const auto& bc = collision.bc_as(); + if (mCurrentRun != bc.runNumber()) { + auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + o2::base::Propagator::initFieldFromGRP(grpo); + mCurrentRun = bc.runNumber(); } - if (!passedSingleTrackSelection(track)) { + + qaHistos.fill(HIST("MCReco/hEvents"), 0.5); + if (sel8 && !collision.sel8()) { continue; } - - if (!track.has_mcParticle()) { + qaHistos.fill(HIST("MCReco/hEvents"), 1.5); + if (std::abs(collision.posZ()) > cutzvertex) { continue; } + qaHistos.fill(HIST("MCReco/hEvents"), 2.5); - auto mcParticle = track.mcParticle(); - const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); - const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; - const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; - const bool selTpcOrTpcTof = isTPCDe && (!track.hasTOF() || isTOFDe); + if (collision.has_mcCollision()) { + const auto mcParticlesThisEvent = mcParticles.sliceBy(mcParticlesPerCollision, collision.mcCollisionId()); - if (isTPCDe) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue"), track.pt()); + for (const auto& mcParticle : mcParticlesThisEvent) { + + if (mcParticle.pdgCode() != pdgCodeDaughter) { + continue; + } + if (std::abs(mcParticle.eta()) > cfgEta || std::abs(mcParticle.y()) > rapidityCut) { + continue; + } + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { + continue; + } + bool fromMother = false; + for (const auto& mom : mcParticle.mothers_as()) { + if (mom.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromBminus"), mcParticle.pt()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt()); + fromMother = true; + break; + } + } + if (!fromMother && mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenPrimary"), mcParticle.pt()); + } } } - if (track.hasTOF()) { - qaHistos.fill(HIST("MCReco/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("MCReco/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); - } + const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - if (selTpcOrTpcTof) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue"), track.pt()); + for (const auto& trackId : trackIdsThisCollision) { + const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); + std::array dca{track.dcaXY(), track.dcaZ()}; + + if (track.collisionId() != collision.globalIndex()) { + auto trackPar = getTrackParCov(track); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca); } - } - if (isTPCDe && isTOFDe && track.hasTOF()) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelected"), track.pt()); - if (isTrueDeuteron) { - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelectedTrue"), track.pt()); + if (std::abs(dca[0]) < cfgDCAmin || std::abs(dca[0]) > cfgDCAmax) { + continue; + } + if (!passedSingleTrackSelection(track)) { + continue; } - } else { - continue; - } - if (isTrueDeuteron) { - if (std::abs(mcParticle.y()) > rapidityCut) { + if (!track.has_mcParticle()) { continue; } - if (mcParticle.isPhysicalPrimary()) { + + auto mcParticle = track.mcParticle(); + const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); + const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; + const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; + const bool isITSDe = track.itsNSigmaDe() > cfgITSNsigma; + + const bool selTpcOrTpcTof = isTPCDe && (!track.hasTOF() || isTOFDe); + + if (isTPCDe) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue"), track.pt()); + } + } + + if (selTpcOrTpcTof) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue"), track.pt()); + } + } + // Purity study: TPC && TOF + if (isTPCDe && isTOFDe && track.hasTOF()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelectedTrue"), track.pt()); + } + } + + // TPC veto against pi/K/p/e + const bool passTPCVeto = std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma && + std::abs(track.tpcNSigmaEl()) > cfgTPCNsigma; + // TOF veto against pi/K/p/e + const bool passTOFVeto = std::abs(track.tofNSigmaPi()) > cfgTofNsigma && + std::abs(track.tofNSigmaKa()) > cfgTofNsigma && + std::abs(track.tofNSigmaPr()) > cfgTofNsigma && + std::abs(track.tofNSigmaEl()) > cfgTofNsigma; + + const bool isTPCDeClean = isTPCDe && passTPCVeto; + + if (isTrueDeuteron) { int motherPdg = 0; - if (separateAntideuterons) { - for (const auto& mom : mcParticle.mothers_as()) { - qaHistos.fill(HIST("MCReco/hMotherPdgCode"), mom.pdgCode()); - if (mom.pdgCode() == pdgCodeBeautyMeson || mom.pdgCode() == pdgCodeBeautyBaryon) { - motherPdg = mom.pdgCode(); - double dx = mcParticle.vx() - collision.posX(); - double dy = mcParticle.vy() - collision.posY(); - double dz = mcParticle.vz() - collision.posZ(); - - double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); - double massBeauty = getBeautyHadronMass(mom.pdgCode()); - - if (massBeauty > 0.) { - double ctauBeauty = flightDistance / mom.p() * massBeauty; - ctauBeauty *= 1.e4; - - if (motherPdg == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCReco/ctauBminus"), ctauBeauty); - } else if (motherPdg == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCReco/ctauAntiLambdaB"), ctauBeauty); - } + for (const auto& mom : mcParticle.mothers_as()) { + qaHistos.fill(HIST("MCReco/hMotherPdgCode"), mom.pdgCode()); + if (mom.pdgCode() == pdgCodeBeautyMeson || mom.pdgCode() == pdgCodeBeautyBaryon || mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + double dx = mcParticle.vx() - mom.vx(); + double dy = mcParticle.vy() - mom.vy(); + double dz = mcParticle.vz() - mom.vz(); + + double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); + double massBeauty = getMotherMass(mom.pdgCode()); + + if (massBeauty > 0.) { + double ctauBeauty = flightDistance / mom.p() * massBeauty; + + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ctauBminus"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ctauAntiHypertriton"), ctauBeauty); // cm } - break; } + break; } } + if (motherPdg == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCReco/hDCAxy-FromBeautyHadron"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 0.5); } else if (motherPdg == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCReco/hDCAxy-FromBeautyHadron"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 1.5); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 2.5); + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 3.5); } else { - qaHistos.fill(HIST("MCReco/hDCAxy-Primary"), track.dcaXY()); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + qaHistos.fill(HIST("MCReco/hMotherCategory"), 4.5); + } + + // same final selection as in data + const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || + (track.pt() >= ptThresholdforPID && isTPCDeClean && passTOFVeto && isTOFDe && track.hasTOF()); + if (passFinalSel) { + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/hDCAxy-FromBMeson"), dca[0]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaB"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiLambdaB"), dca[0]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertriton"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiHypertriton"), dca[0]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimary"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimary"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/hDCAxy-Primary"), dca[0]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + } } } } @@ -433,38 +573,39 @@ struct HfTaskDeuteronFromLb { void processGen(o2::aod::McCollision const& collision, o2::aod::McParticles const& mcParticles) { - hProcessedEvents->Fill(0.5); + qaHistos.fill(HIST("MCGen/hEvents"), 0.5); + if (std::abs(collision.posZ()) > cutzvertex) { + return; + } + qaHistos.fill(HIST("MCGen/hEvents"), 1.5); for (const auto& mcParticle : mcParticles) { - - // Beauty hadron mother - if (isSelectedBeautyHadron(mcParticle.pdgCode())) { - if (std::abs(mcParticle.y()) <= rapidityCut) { - if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCGen/ptGeneratedBminus"), mcParticle.pt()); - } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCGen/ptGeneratedAntiLambdaB"), mcParticle.pt()); - } + if (isSelectedMother(mcParticle.pdgCode())) { + if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCGen/ptGeneratedBminus"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiLambdaB"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiHypertriton"), mcParticle.pt()); } - if (mcParticle.has_daughters()) { for (const auto& daughter : mcParticle.daughters_as()) { - double dx = daughter.vx() - collision.posX(); - double dy = daughter.vy() - collision.posY(); - double dz = daughter.vz() - collision.posZ(); - + double dx = daughter.vx() - mcParticle.vx(); + double dy = daughter.vy() - mcParticle.vy(); + double dz = daughter.vz() - mcParticle.vz(); double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); - double massBeauty = getBeautyHadronMass(mcParticle.pdgCode()); + double massBeauty = getMotherMass(mcParticle.pdgCode()); if (massBeauty > 0.) { double ctauBeauty = flightDistance / mcParticle.p() * massBeauty; - ctauBeauty *= 1.e4; // cm -> micrometers if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { - qaHistos.fill(HIST("MCGen/ctauBminus"), ctauBeauty); + qaHistos.fill(HIST("MCGen/ctauBminus"), ctauBeauty * 1.e4); } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { - qaHistos.fill(HIST("MCGen/ctauAntiLambdaB"), ctauBeauty); + qaHistos.fill(HIST("MCGen/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ctauAntiHypertriton"), ctauBeauty); // cm } } break; @@ -473,14 +614,17 @@ struct HfTaskDeuteronFromLb { } if (mcParticle.pdgCode() == pdgCodeDaughter) { - if (std::abs(mcParticle.eta()) < cfgEta) { - qaHistos.fill(HIST("MCGen/ptAntiDeuteronAll"), mcParticle.pt()); + + if (std::abs(mcParticle.eta()) > cfgEta) { + continue; } if (std::abs(mcParticle.y()) > rapidityCut) { continue; } + qaHistos.fill(HIST("MCGen/ptAntiDeuteronAll"), mcParticle.pt()); + int motherPdg = 0; if (mcParticle.has_mothers()) { for (const auto& mom : mcParticle.mothers_as()) { @@ -496,6 +640,11 @@ struct HfTaskDeuteronFromLb { motherPdg = mom.pdgCode(); break; } + + if (mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + break; + } } } @@ -503,6 +652,8 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromBminus"), mcParticle.pt()); } else if (motherPdg == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiLambdaB"), mcParticle.pt()); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiHypertriton"), mcParticle.pt()); } else if (mcParticle.isPhysicalPrimary()) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronPrimary"), mcParticle.pt()); } From 0786066f74b9bceb958443b7c8f56e994de5a3d4 Mon Sep 17 00:00:00 2001 From: spucillo <93769017+spucillo@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:02:10 +0200 Subject: [PATCH 276/648] [PWGLF] Update strangenessInJets: Add generated cascade histograms relative to reconstructed jet and UE axes (#18268) --- PWGLF/Tasks/Strangeness/strangenessInJets.cxx | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx index 245ce30e294..54b8fc923cf 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx @@ -444,6 +444,15 @@ struct StrangenessInJets { registryMC.add("OmegaPos_reconstructed_ue", "OmegaPos_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_jet", "OmegaNeg_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_ue", "OmegaNeg_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + // Generated cascades associated to reconstructed jet / UE axes. + registryMC.add("XiPos_generated_recojet_jet", "XiPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_generated_recojet_ue", "XiPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_jet", "XiNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_ue", "XiNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_jet", "OmegaPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_ue", "OmegaPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_jet", "OmegaNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_ue", "OmegaNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); // Histograms to calculate probability of hyperons to be found within jets registryMC.add("XiPos_reconstructed_fullevent", "XiPos_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_reconstructed_fullevent", "XiNeg_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); @@ -1891,7 +1900,6 @@ struct StrangenessInJets { continue; if (!motherPos.isPhysicalPrimary()) continue; - double maxPseudorap = 0.8; if (std::abs(motherPos.eta()) > maxPseudorap) continue; @@ -1911,7 +1919,6 @@ struct StrangenessInJets { } for (auto const& particle : mcParticlesPerColl) { - double maxPseudorap = 0.8; if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= maxPseudorap) { switch (particle.pdgCode()) { case kK0Short: @@ -1974,6 +1981,50 @@ struct StrangenessInJets { } } break; + + // Cascades: generated particles classified with respect to the + // reconstructed jet and perpendicular-cone axes, exactly as for V0s. + case kXiPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kXiMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + default: break; } From 67cd2f8d18d69c78007065c345a9072b3167d970 Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Tue, 13 Oct 2026 19:47:07 +0200 Subject: [PATCH 277/648] [PWGLF] Add 2 More Response Matrix in hStrangeCorrelation.cxx (#18272) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 216 ++++++++++-------- 1 file changed, 127 insertions(+), 89 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index a9224f9ad02..cedfad14ba0 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -98,9 +98,9 @@ using V0DatasWithoutTrackXWithMCCores = soa::Join ccdb; + Service ccdb{}; - Service pdgDB; + Service pdgDB{}; o2::pwglf::ParticleCounter mCounter; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -148,6 +148,7 @@ struct HStrangeCorrelation { Configurable doCorrelationsHadronV0daughter{"doCorrelationsHadronV0daughter", false, "if true, do correlations of hadrons with V0 daughters"}; Configurable doLocalDensityStudy{"doLocalDensityStudy", false, "if true, create and fill the pt vs eta vs local density spectra of triggers and V0s"}; Configurable localDensityConeRadius{"localDensityConeRadius", 0.4, "radius of the cone in which the local density is counted"}; + Configurable doMultEstimatorResponse{"doMultEstimatorResponse", false, "processSameEventHV0s: fill MultEstimatorResponse, multNTracksPVeta1 vs FT0M percentile and amplitude, with the event selection of PairLossK0/EventRecoEfficiency (no INEL cuts)"}; } masterConfigurations; // master analysis switches @@ -189,6 +190,9 @@ struct HStrangeCorrelation { ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; ConfigurableAxis axisNchGen{"axisNchGen", {151, -0.5f, 150.5f}, "generated charged primaries in |eta| < 1"}; ConfigurableAxis axisNRecoCollisions{"axisNRecoCollisions", {6, -0.5f, 5.5f}, "reconstructed collisions per MC collision"}; + // finer than axisMult but containing all its default edges, so the analysis classes can be recovered by merging bins + ConfigurableAxis axisCentFT0M{"axisCentFT0M", {VARIABLE_WIDTH, 0.0f, 0.01f, 0.1f, 0.5f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 45.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "FT0M percentile of the multNTracksPVeta1 vs FT0M response"}; + ConfigurableAxis axisMultFT0M{"axisMultFT0M", {1000, 0.0f, 20000.0f}, "FT0M amplitude of the multNTracksPVeta1 vs FT0M response"}; } axesConfigurations; @@ -329,12 +333,13 @@ struct HStrangeCorrelation { // comment on runGenLevelStudy in processPairLossK0MC // doEventRecoEfficiencyStudy // PairLossK0/EventRecoEfficiency: MC-collision - // reconstruction efficiency and Nch^gen vs Nch^reco - // response, see runEventRecoEfficiencyStudy + // reconstruction efficiency, Nch^gen vs Nch^reco and + // Nch^reco vs FT0M (percentile and amplitude) responses, + // see runEventRecoEfficiencyStudy Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; - Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, and Nch^gen vs multNTracksPVeta1"}; + Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude"}; Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8 @@ -881,7 +886,11 @@ struct HStrangeCorrelation { const double maxRadius = pairLossK0Configurations.maxRadius; const double radiusStep = pairLossK0Configurations.radiusStep; - for (double radius = startRadius; radius <= maxRadius + 0.5 * radiusStep; radius += radiusStep) { + // radii startRadius + i * radiusStep up to maxRadius, with half a step of tolerance; + // nRadii <= 0 (no radius) if startRadius is beyond that + const int nRadii = static_cast(std::floor((maxRadius - startRadius) / radiusStep + 0.5)) + 1; + for (int iRadius = 0; iRadius < nRadii; ++iRadius) { + const double radius = startRadius + iRadius * radiusStep; const double daughterArgument = daughterPhase * radius; const double triggerArgument = triggerPhase * radius; if (std::abs(daughterArgument) > 1.0 || std::abs(triggerArgument) > 1.0) { @@ -1257,9 +1266,13 @@ struct HStrangeCorrelation { double phaseProton = (-0.3 * B * assoc[2]) / (2 * assoc[1]); double phaseTrack = (-0.3 * B * trigg[2]) / (2 * trigg[1]); - for (double r = MinRadiusTPC; r <= MaxRadiusTPC; r += 0.05) { + // radii MinRadiusTPC + i * RadiusStep below MaxRadiusTPC: 34 radii, 0.80 to 2.45 m + constexpr double RadiusStep = 0.05; + constexpr int NRadii = static_cast((MaxRadiusTPC - MinRadiusTPC) / RadiusStep + 0.5); + for (int iRadius = 0; iRadius < NRadii; ++iRadius) { + const double r = MinRadiusTPC + iRadius * RadiusStep; dPhiStar = dPhi + std::asin(phaseProton * r) - std::asin(phaseTrack * r); - dPhiStarMean += (dPhiStar / 34); + dPhiStarMean += (dPhiStar / NRadii); } return dPhiStarMean; @@ -1710,7 +1723,7 @@ struct HStrangeCorrelation { // weight. Reconstructed matches remain separate. histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 13, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); - const PairLossPairKey truthPairKey{truthTrigger.globalIndex, truthK0.globalIndex}; + const PairLossPairKey truthPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}; if (pairLossComparison.countedPairs.insert(truthPairKey).second) { // 14 additionally keeps one reconstructed // match per truth pair. Stage 15 and Final @@ -2826,6 +2839,16 @@ struct HStrangeCorrelation { // Per reconstructed collision passing the Rec event selection without INEL // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + } + + if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse) { + // Same histograms and event selection as the multNTracksPVeta1 vs FT0M responses of + // PairLossK0/EventRecoEfficiency, filled per reconstructed collision in data + // (or in MC reco, which then reproduces the PairLossK0 ones) + histos.add("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); } if (doprocessPairLossK0MC && pairLossK0Configurations.doRecComparison) { @@ -3450,7 +3473,7 @@ struct HStrangeCorrelation { // rejects collisions which are associated with the same "found-by-T0" bunch crossing return false; } - if (zorroMask.value != "") { + if (!zorroMask.value.empty()) { auto bc = collision.template bc_as(); initZorro(bc); bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting @@ -3559,6 +3582,17 @@ struct HStrangeCorrelation { return true; } + // event selection of runSameEventHV0s before its INEL cut: trigger presence + + // isCollisionSelected (pp) or isCollisionSelectedPbPb, without filling hEventSelection + template + bool isCollisionSelectedNoInel(TCollision const& collision) + { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return false; + } + return masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, false) : isCollisionSelectedPbPb(collision, false); + } + double getMagneticField(uint64_t timestamp) { static parameters::GRPMagField* grpo = nullptr; @@ -3639,7 +3673,7 @@ struct HStrangeCorrelation { // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -3884,7 +3918,7 @@ struct HStrangeCorrelation { // for the ordinary Rec process and for its PairLoss replica. When // nChGenForInelSelection is given (PairLoss replica only), the reconstructed // INEL cuts are replaced by Nch^gen >= 1 of that generated multiplicity - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision, !nChGenForInelSelection.has_value())) || @@ -3904,10 +3938,15 @@ struct HStrangeCorrelation { collision.posX(), collision.posY(), collision.posZ(), cent, collision.multNTracksPVeta1(), bField); } - void processSameEventHV0s(soa::Join::iterator const& collision, + void processSameEventHV0s(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, V0DatasWithoutTrackX const&, TracksComplete const& tracks, aod::BCsWithTimestamps const&) { + // filled here and not in runSameEventHV0s, which the PairLoss Rec replica also runs + if (masterConfigurations.doMultEstimatorResponse && isCollisionSelectedNoInel(collision)) { + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); + } runSameEventHV0s(collision, associatedV0s, triggerTracks, tracks); } @@ -3924,7 +3963,7 @@ struct HStrangeCorrelation { double cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -4079,7 +4118,7 @@ struct HStrangeCorrelation { BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -4164,7 +4203,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { return; } @@ -4221,7 +4260,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4280,7 +4319,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4330,7 +4369,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -4443,7 +4482,7 @@ struct HStrangeCorrelation { bestCollisionGoodTriggerTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { bestCollisionTriggerPresenceMap = triggerPresenceMap[collision.globalIndex()]; } } @@ -4473,7 +4512,7 @@ struct HStrangeCorrelation { // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } if (masterConfigurations.applyNewMCSelection) { @@ -4617,7 +4656,7 @@ struct HStrangeCorrelation { } } void processPairLossK0MC(aod::McCollision const& mcCollision, - soa::SmallGroups> const& recCollisions, + soa::SmallGroups> const& recCollisions, aod::McParticles const& mcParticles, aod::McTrackLabels const& trackLabels, aod::V0MCCores const& v0MCCores, @@ -4736,9 +4775,9 @@ struct HStrangeCorrelation { auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { histos.fill(HIST("PairLossK0/GenStudy/Gen/hTrigger"), genPt, genEta, genPhi, generatedNch); - if (reconstructedTrackMcIds.count(mcParticle.globalIndex()) > 0) { + if (reconstructedTrackMcIds.contains(mcParticle.globalIndex())) { histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); } else { histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); @@ -4747,9 +4786,9 @@ struct HStrangeCorrelation { .pt = genPt, .eta = genEta, .phi = genPhi, - .globalIndex = static_cast(mcParticle.globalIndex()), - .motherIndex = mcParticle.has_mothers() ? static_cast(mcParticle.mothers_first_as().globalIndex()) : -1, - .reconstructed = reconstructedTrackMcIds.count(mcParticle.globalIndex()) > 0}); + .globalIndex = mcParticle.globalIndex(), + .motherIndex = mcParticle.has_mothers() ? mcParticle.mothers_first_as().globalIndex() : -1, + .reconstructed = reconstructedTrackMcIds.contains(mcParticle.globalIndex())}); } } @@ -4782,7 +4821,7 @@ struct HStrangeCorrelation { const float k0Findable = (hasPositiveDaughter && hasNegativeDaughter && daughtersInAcceptance) ? 1.0f : 0.0f; histos.fill(HIST("PairLossK0/GenStudy/Gen/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); - if (reconstructedV0McIds.count(mcParticle.globalIndex()) > 0) { + if (reconstructedV0McIds.contains(mcParticle.globalIndex())) { histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); } else { histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); @@ -4791,9 +4830,9 @@ struct HStrangeCorrelation { .pt = genPt, .eta = genEta, .phi = genPhi, - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .motherIndex = -1, - .reconstructed = reconstructedV0McIds.count(mcParticle.globalIndex()) > 0}); + .reconstructed = reconstructedV0McIds.contains(mcParticle.globalIndex())}); } } @@ -4869,9 +4908,9 @@ struct HStrangeCorrelation { auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -4885,7 +4924,7 @@ struct HStrangeCorrelation { continue; } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -4896,7 +4935,7 @@ struct HStrangeCorrelation { .negativeDaughter = {}}; for (auto const& daughter : mcParticle.daughters_as()) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -4980,11 +5019,11 @@ struct HStrangeCorrelation { genBestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); genBestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { genBestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; } } - bool genEventSelected = triggerPresenceMap.size() == 0 || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); + bool genEventSelected = triggerPresenceMap.empty() || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); if (masterConfigurations.applyNewMCSelection) { genEventSelected = genEventSelected && genCollisionSelected && (!applyGenInel || isGenInelGt0(nChGen)); } else if (masterConfigurations.doGenEventSelection) { @@ -5109,9 +5148,9 @@ struct HStrangeCorrelation { continue; } oldFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -5136,7 +5175,7 @@ struct HStrangeCorrelation { // FinalNotInRec below is not filled either. if (pairLossEventSelected && !applyGenInel) { for (auto const& truthTrigger : truthTriggers) { - if (oldFinalTriggers.find(truthTrigger.globalIndex) == oldFinalTriggers.end()) { + if (!oldFinalTriggers.contains(truthTrigger.globalIndex)) { continue; } histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), 9, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); @@ -5190,8 +5229,11 @@ struct HStrangeCorrelation { histos.fill(HIST("PairLossK0/Comparison/Final"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 15, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); finalPairRecords.push_back(PairLossFinalRecord{ - PairLossPairKey{truthTrigger.globalIndex, truthK0.globalIndex}, - truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt}); + .key = PairLossPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}, + .deltaPhi = truthDeltaPhi, + .deltaEta = truthDeltaEta, + .k0Pt = truthK0.pt, + .triggerPt = truthTrigger.pt}); } } } @@ -5260,20 +5302,16 @@ struct HStrangeCorrelation { }; // MC-collision reconstruction efficiency vs Nch^gen and the Nch^gen vs - // multNTracksPVeta1 response. The event selection is the one runSameEventHV0s - // applies before its INEL cut: trigger presence + isCollisionSelected + // multNTracksPVeta1 and multNTracksPVeta1 vs FT0M (percentile and amplitude) + // responses. The event selection is the one runSameEventHV0s applies before + // its INEL cut: trigger presence + isCollisionSelected (isCollisionSelectedNoInel) auto runEventRecoEfficiencyStudy = [&]() { - auto passesRecEventSelectionNoInel = [&](auto const& collision) { - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { - return false; - } - return masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, false) : isCollisionSelectedPbPb(collision, false); - }; - - // Response: one entry per reconstructed collision, as in the Rec path + // Responses: one entry per reconstructed collision, as in the Rec path for (auto const& collision : recCollisions) { - if (passesRecEventSelectionNoInel(collision)) { + if (isCollisionSelectedNoInel(collision)) { histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, collision.multNTracksPVeta1()); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); } } @@ -5295,7 +5333,7 @@ struct HStrangeCorrelation { continue; } largestNContributors = collision.numContrib(); - bestCollisionSelectedNoInel = passesRecEventSelectionNoInel(collision); + bestCollisionSelectedNoInel = isCollisionSelectedNoInel(collision); // reconstructed-level INEL cuts; the Pb-Pb event selection has none bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); } @@ -5359,7 +5397,7 @@ struct HStrangeCorrelation { continue; } v0sAnyCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), + .globalIndex = v0.globalIndex(), .positiveTrackId = static_cast(v0.posTrackId()), .negativeTrackId = static_cast(v0.negTrackId()), .pt = v0.pt(), @@ -5373,7 +5411,7 @@ struct HStrangeCorrelation { } for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != bestCollisionId) { + if (collision.globalIndex() != bestCollisionId) { continue; } @@ -5407,7 +5445,7 @@ struct HStrangeCorrelation { continue; } v0sBestCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), + .globalIndex = v0.globalIndex(), .positiveTrackId = static_cast(v0.posTrackId()), .negativeTrackId = static_cast(v0.negTrackId()), .pt = v0.pt(), @@ -5424,7 +5462,7 @@ struct HStrangeCorrelation { PairLossV0Map v0sInTable; PairLossV0Map v0sFinal; for (auto const& finalCollision : recCollisions) { - const int64_t finalCollisionId = static_cast(finalCollision.globalIndex()); + const int64_t finalCollisionId = finalCollision.globalIndex(); const bool isBestCollision = finalCollisionId == bestCollisionId; if (!isBestCollision) { if (pairLossK0Configurations.finalPairUseBestCollisionOnly) { @@ -5471,9 +5509,9 @@ struct HStrangeCorrelation { auto positiveTrack = v0.posTrack_as(); auto negativeTrack = v0.negTrack_as(); const PairLossV0Info v0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -5531,7 +5569,7 @@ struct HStrangeCorrelation { continue; } truthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -5544,7 +5582,7 @@ struct HStrangeCorrelation { continue; } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -5555,7 +5593,7 @@ struct HStrangeCorrelation { const auto daughters = mcParticle.daughters_as(); for (auto const& daughter : daughters) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -5635,7 +5673,7 @@ struct HStrangeCorrelation { true, // V0 in AssocV0s: K0 side only true, // V0 final selection: K0 side only contains(triggersFinal, truthTrigger.globalIndex)}; // Final reconstructed pair - std::array triggerStageFills; + std::array triggerStageFills{}; triggerStageFills.fill(1); if (!pairLossK0Configurations.fillFinalPairOnce) { triggerStageFills[PairLossTriggerPureReco] = static_cast(numberOfMatches(tracksBestCollision, truthTrigger.globalIndex)); @@ -5991,7 +6029,7 @@ struct HStrangeCorrelation { PairLossTrackMap pairLossFinalTriggers; PairLossV0Map pairLossFinalV0s; for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != pairLossBestCollisionId) { + if (collision.globalIndex() != pairLossBestCollisionId) { continue; } @@ -6055,9 +6093,9 @@ struct HStrangeCorrelation { continue; } pairLossFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ - .globalIndex = static_cast(v0.globalIndex()), - .positiveTrackId = static_cast(positiveTrack.globalIndex()), - .negativeTrackId = static_cast(negativeTrack.globalIndex()), + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), .pt = v0.pt(), .eta = v0.eta(), .phi = v0.phi(), @@ -6074,10 +6112,10 @@ struct HStrangeCorrelation { // requires the reconstructed pair itself to fall in the reconstructed angular // range and to survive the reconstructed autocorrelation rejection. auto pairLossHasFinalTrigger = [&](int64_t mcId) { - return pairLossFinalTriggers.find(mcId) != pairLossFinalTriggers.end(); + return pairLossFinalTriggers.contains(mcId); }; auto pairLossHasFinalK0 = [&](int64_t mcId) { - return pairLossFinalV0s.find(mcId) != pairLossFinalV0s.end(); + return pairLossFinalV0s.contains(mcId); }; auto pairLossHasFinalPair = [&](int64_t triggerMcId, int64_t k0McId) { const auto triggerMatches = pairLossFinalTriggers.find(triggerMcId); @@ -6109,7 +6147,7 @@ struct HStrangeCorrelation { }; for (auto const& collision : recCollisions) { - if (static_cast(collision.globalIndex()) != pairLossBestCollisionId) { + if (collision.globalIndex() != pairLossBestCollisionId) { continue; } @@ -6158,7 +6196,7 @@ struct HStrangeCorrelation { double sumPt = 0., q2x = 0., q2y = 0.; for (auto const& particle : mcParticles) { currentMcIds.insert(particle.globalIndex()); - nAnyMatched += pairLossAnyTrackMcParticleIds.count(particle.globalIndex()) != 0; + nAnyMatched += pairLossAnyTrackMcParticleIds.contains(particle.globalIndex()); if (!particle.isPhysicalPrimary() || !std::isfinite(particle.eta()) || std::abs(particle.eta()) >= GenEtaMax) { continue; } @@ -6180,7 +6218,7 @@ struct HStrangeCorrelation { std::unordered_set bestMatchedMcIds; const auto bestTracks = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); for (auto const& track : bestTracks) { - const bool sameMc = track.has_mcParticle() && currentMcIds.count(track.mcParticleId()) != 0; + const bool sameMc = track.has_mcParticle() && currentMcIds.contains(track.mcParticleId()); if (sameMc) { bestMatchedMcIds.insert(track.mcParticleId()); } @@ -6241,9 +6279,9 @@ struct HStrangeCorrelation { auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if (!((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0)) { + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { pairLossTruthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -6258,7 +6296,7 @@ struct HStrangeCorrelation { } PairLossTruthK0Info truthK0{ - .globalIndex = static_cast(mcParticle.globalIndex()), + .globalIndex = mcParticle.globalIndex(), .pt = mcParticle.pt(), .eta = mcParticle.eta(), .phi = mcParticle.phi(), @@ -6268,7 +6306,7 @@ struct HStrangeCorrelation { .negativeDaughter = {}}; for (auto const& daughter : mcParticle.daughters_as()) { PairLossTruthTrackInfo daughterInfo{ - .globalIndex = static_cast(daughter.globalIndex()), + .globalIndex = daughter.globalIndex(), .pt = daughter.pt(), .eta = daughter.eta(), .phi = daughter.phi(), @@ -6296,11 +6334,11 @@ struct HStrangeCorrelation { // AnyTrackBoth/, at fully-selected level in Final/. for (auto const& truthTrigger : pairLossTruthTriggers) { if (pairLossK0Configurations.doEventQualityStudy) { - fillEventQuality(1, countDeltaPhi, countDeltaEta, axisRanges[2][0], truthTrigger.pt, pairLossAnyTrackMcParticleIds.count(truthTrigger.globalIndex) != 0, pairLossHasFinalTrigger(truthTrigger.globalIndex)); + fillEventQuality(1, countDeltaPhi, countDeltaEta, axisRanges[2][0], truthTrigger.pt, pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex), pairLossHasFinalTrigger(truthTrigger.globalIndex)); } histos.fill(HIST("ClosureTest/PairLossK0/Truth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); - if (pairLossAnyTrackMcParticleIds.find(truthTrigger.globalIndex) != pairLossAnyTrackMcParticleIds.end()) { + if (pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex)) { histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); } @@ -6311,7 +6349,7 @@ struct HStrangeCorrelation { for (auto const& truthK0 : pairLossTruthK0s) { histos.fill(HIST("ClosureTest/PairLossK0/Truth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); - if (pairLossAnyV0McParticleIds.find(truthK0.globalIndex) != pairLossAnyV0McParticleIds.end()) { + if (pairLossAnyV0McParticleIds.contains(truthK0.globalIndex)) { histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); } @@ -6321,7 +6359,7 @@ struct HStrangeCorrelation { } for (auto const& truthTrigger : pairLossTruthTriggers) { - const bool triggerHasAnyTrack = pairLossAnyTrackMcParticleIds.find(truthTrigger.globalIndex) != pairLossAnyTrackMcParticleIds.end(); + const bool triggerHasAnyTrack = pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex); for (auto const& truthK0 : pairLossTruthK0s) { if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { continue; @@ -6336,7 +6374,7 @@ struct HStrangeCorrelation { continue; } - const bool k0HasAnyV0 = pairLossAnyV0McParticleIds.find(truthK0.globalIndex) != pairLossAnyV0McParticleIds.end(); + const bool k0HasAnyV0 = pairLossAnyV0McParticleIds.contains(truthK0.globalIndex); histos.fill(HIST("ClosureTest/PairLossK0/Truth/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); if (triggerHasAnyTrack) { @@ -6446,14 +6484,14 @@ struct HStrangeCorrelation { bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) { + if (!triggerPresenceMap.empty()) { bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; } } } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } @@ -6524,7 +6562,7 @@ struct HStrangeCorrelation { if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { triggerIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile); - if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.find(mcParticle.globalIndex()) != mcParticleIdsWithRecoTrackMatch.end()) { + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); } } @@ -6590,7 +6628,7 @@ struct HStrangeCorrelation { double getatrigger = triggerParticle.eta(); double gphitrigger = triggerParticle.phi(); double pttrigger = triggerParticle.pt(); - const bool triggerHasRecoTrackMatch = masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.find(triggerParticle.globalIndex()) != mcParticleIdsWithRecoTrackMatch.end(); + const bool triggerHasRecoTrackMatch = masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(triggerParticle.globalIndex()); auto const* triggerPdg = pdgDB->GetParticle(triggerParticle.pdgCode()); const double triggerCharge = triggerPdg ? triggerPdg->Charge() : 0.; auto const& mother = triggerParticle.mothers_first_as(); @@ -6748,7 +6786,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -6778,7 +6816,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } // Perform basic event selection on both collisions From 3e98361ffe3f677b73a7ec5249f6ae783bda9d70 Mon Sep 17 00:00:00 2001 From: Andrea Giovanni Riffero Date: Tue, 13 Oct 2026 20:14:35 +0200 Subject: [PATCH 278/648] [PWGUD] Add task upcTrackVertexingQA.cxx (#18278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andrea Tavira García <118979672+atavirag@users.noreply.github.com> Co-authored-by: ALICE Action Bot --- PWGUD/Tasks/CMakeLists.txt | 5 + PWGUD/Tasks/upcTrackVertexingQA.cxx | 533 ++++++++++++++++++++++++++++ 2 files changed, 538 insertions(+) create mode 100644 PWGUD/Tasks/upcTrackVertexingQA.cxx diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 55f69f53d26..2d238dc6ed9 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -14,6 +14,11 @@ o2physics_add_dpl_workflow(upc-polarisation-jpsi-incoh PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(upc-track-vertexing-qa + SOURCES upcTrackVertexingQA.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(upc-analysis SOURCES upcAnalysis.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase diff --git a/PWGUD/Tasks/upcTrackVertexingQA.cxx b/PWGUD/Tasks/upcTrackVertexingQA.cxx new file mode 100644 index 00000000000..7dc4be7bbc6 --- /dev/null +++ b/PWGUD/Tasks/upcTrackVertexingQA.cxx @@ -0,0 +1,533 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcTrackVertexingQA.cxx +/// \brief task to study the performance of vertexing for low-multiplicity UPC collisions +/// \author Andrea Tavira Garcia a.tavira@cern.ch +/// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +enum class CandSpecies { + kRho = 0, + kJpsi = 1 +}; + +struct UpcTrackVertexingQA { + // Configurables + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable ptCandMin{"ptCandMin", 0., "min. cand. pT (GeV/c)"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track pT (GeV/c)"}; + Configurable etaTrackMax{"etaTrackMax", 0.9, "max. |eta| of tracks"}; + Configurable nMinTpcClusters{"nMinTpcClusters", 60, "min. number of TPC clusters"}; + + // Axes (the mass axis matches the default rho window; override it for the J/psi) + ConfigurableAxis axisMass{"axisMass", {200, 0.5, 1.3}, "m_{#pi#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisY{"axisY", {100, -1., 1.}, "Rapidity"}; + ConfigurableAxis axisPtTrk{"axisPtTrk", {80, 0.0, 0.8}, "#it{p}_{T} (GeV/#it{c})"}; + + HistogramConfigSpec hVtxZ{HistType::kTH1F, {{80, -20., 20.}}}; + HistogramConfigSpec hVtxX{HistType::kTH1F, {{50, -0.04, -0.015}}}; + HistogramConfigSpec hVtxY{HistType::kTH1F, {{60, -0.015, 0.015}}}; + HistogramConfigSpec hEtaTrk{HistType::kTH1F, {{100, -1.0, 1.0}}}; + + HistogramRegistry registry{"registry"}; + + // services + Service ccdb; // access to database + + // variables to store run info + int runNumberBc = 0; // run number used to process BCs + int runNumberCol = 0; // run number used to process collisions + int64_t sor = 0; // best known timestamp for the start of run + int64_t orbitsPerTF = 0; // number of orbits per TF + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t currentTF = -1; // current time frame being looked at + int64_t nTF = 0; // number of time frames in run + + // constants + const unsigned int twoTracks = 2; + const int unassignedDCA = 999; + + static constexpr float MassPion = o2::constants::physics::MassPionCharged; + static constexpr float MassMuon = o2::constants::physics::MassMuon; + + // folders that contains plots for all tracks and associated tracks + static constexpr std::array TrackFolders = {"Trk/", "TrkColl/", "TrkAmb/"}; + + // Name shortenings + // passed* columns are in TrackSelectionExtension; isGlobalTrack* and trackCutFlag in TrackSelection + using TracksExtraSels = soa::Join; + using TracksExtraWPidPi = soa::Join; + using TracksExtraWPidMu = soa::Join; + using TracksExtraWPidPiMc = soa::Join; + using TracksExtraWPidMuMc = soa::Join; + using CollisionsWithMc = soa::Join; + using TracksExtraIU = soa::Join; + + void init(InitContext&) + { + // collision level + registry.add("Coll/hNContrib", ";N_{PV contributors};entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + registry.add("Coll/hBCid", ";BCid;entries", {HistType::kTH1F, {{1000, 0., 100000.}}}); + registry.add("Coll/hVtxZ", ";#it{z}_{vtx} (cm);entries", hVtxZ); + registry.add("Coll/hnTF", ";TF;entries", {HistType::kTH1F, {{2000, 0., 2000}}}); + registry.add("Coll/hVtxX", ";#it{x}_{vtx} (cm);entries", hVtxX); + registry.add("Coll/hVtxY", ";#it{y}_{vtx} (cm);entries", hVtxY); + registry.add("Coll/hVtxChi2", ";#chi^{2} vtx;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + + // track level (all good tracks, before grouping by collision) + registry.add("Trk/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Trk/hEta", ";#eta;entries", hEtaTrk); + registry.add("Trk/hChi2NCl", ";#chi^{2}/N_{cls} TPC;entries", {HistType::kTH1F, {{50, 0., 5.}}}); + registry.add("Trk/hTpcSignalVsP", ";#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x}", {HistType::kTH2F, {{200, 0., 2.}, {300, 0., 300.}}}); + registry.add("Trk/hNSigmaVsP", ";#it{p} (GeV/#it{c});n#sigma^{TPC}", {HistType::kTH2F, {{200, 0., 2.}, {100, -10., 10.}}}); + registry.add("Trk/hHasIts", ";has ITS;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hIsPvContrib", ";is PV contributor;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hTpcNClsFound", ";N_{cls} TPC;entries", {HistType::kTH1F, {{90, 60, 150.}}}); + registry.add("Trk/hItsChi2NCl", ";#chi^{2}/N_{cls} ITS;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + registry.add("Trk/hItsNCls", ";N_{cls} ITS;entries", {HistType::kTH1F, {{8, -0.5, 7.5}}}); + registry.add("Trk/hItsNClsInnerBarrel", ";N_{cls} ITS Inner Barrel;entries", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("Trk/hDcaXY", ";DCA_{xy} (cm);entries", {HistType::kTH1F, {{140, -0.035, 0.035}}}); + registry.add("Trk/hDcaZ", ";DCA_{z} (cm);entries", {HistType::kTH1F, {{200, -0.05, 0.05}}}); + + // track level (prongs of the candidate, after collision matching) + registry.addClone("Trk/", "TrkColl/"); + registry.add("TrkColl/hTrackSizePerEvent", ";#it{N}_{tracks} per event;entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + + // track level plots for ambiguous tracks + registry.addClone("Trk/", "TrkAmb/"); + + // collision Id counter + registry.add("Coll/hCollIdCounter", ";collision ID;entries", {HistType::kTH1F, {{3, -1.5, 1.5}}}); + + // candidate level plots + registry.add("Cand/hMass", ";m_{#pi#pi} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMass}}); + registry.add("Cand/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("Cand/hRapidity", ";#it{y};entries", {HistType::kTH1F, {axisY}}); + registry.add("Cand/hPtVsRapidity", ";#it{p}_{T} (GeV/#it{c});#it{y}", {HistType::kTH2F, {axisPt, axisY}}); + + // MC histograms are only booked if at least one MC process function is enabled + const bool needMc = doprocessRhoTracksBeforeGroupingMcInfo || doprocessJpsiTracksBeforeGroupingMcInfo || + doprocessRhoCandMcInfo || doprocessJpsiCandMcInfo || doprocessMcGenExclusive || doprocessMcGenEmbedded; + if (!needMc) { + return; + } + + // MC reco matched plots + registry.addClone("Cand/", "Matched/Cand/"); + + registry.addClone("Coll/hNContrib", "Matched/Coll/hNContrib"); + registry.addClone("Coll/hVtxZ", "Matched/Coll/hVtxZ"); + registry.addClone("Coll/hVtxX", "Matched/Coll/hVtxX"); + registry.addClone("Coll/hVtxY", "Matched/Coll/hVtxY"); + + registry.add("Matched/Trk/hPt", ";#it{p}_{T}^{MC matched} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Matched/Trk/hEta", ";#eta^{MC};entries", hEtaTrk); + registry.add("Matched/Trk/hMcIsPhysicalPrimary", ";is physical primary;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Matched/Trk/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + + registry.addClone("Matched/Trk/", "Matched/TrkColl/"); + + // MC generated plots + registry.addClone("Matched/Coll/", "McGen/Coll/"); + + registry.addClone("Matched/Trk/", "McGen/Part/"); + + registry.add("McGen/MotherPart/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + registry.add("McGen/MotherPart/hPt", ";#it{p}_{T}^{MC} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("McGen/MotherPart/hRapidity", ";#it{y}^{MC};entries", {HistType::kTH1F, {axisY}}); + registry.add("McGen/MotherPart/hPtVsRapidity", ";#it{p}_{T}^{MC} (GeV/#it{c});#it{y}^{MC}", {HistType::kTH2F, {axisPt, axisY}}); + + registry.addClone("TrkColl/hTrackSizePerEvent", "McGen/Part/hTrackSizePerEvent"); + + } // end init() + + // FUNCTIONS: all functions defined here + + //-------------------------------------------------------------------------------- + // get info on the run, given the run number. Fills info needed to use getTimeFrame() + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + sor = runInfo.sor; // in ms + auto orbitSOR = runInfo.orbitSOR; + auto orbitEOR = runInfo.orbitEOR; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = orbitSOR * o2::constants::lhc::LHCMaxBunches; // first bc of the first orbit + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; // duration of TF in bcs + nTF = std::ceil((orbitEOR - orbitSOR) / orbitsPerTF); + } + + //-------------------------------------------------------------------------------- + // compute and return the TF for the given BC + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // Basic single-track selection + template + bool isGoodTrack(TTrack const& track) + { + if (!track.hasTPC()) { + return false; + } + if (track.tpcNClsFound() < nMinTpcClusters) { + return false; + } + if (track.pt() < ptTrackMin || std::abs(track.eta()) > etaTrackMax) { + return false; + } + if (std::abs(track.dcaZ()) >= unassignedDCA || std::abs(track.dcaXY()) >= unassignedDCA) { + return false; + } + return true; + } + + //-------------------------------------------------------------------------------- + // Select the PID column at compile time + template + float getNSigma(TTrack const& track) + { + if constexpr (species == CandSpecies::kRho) { + return track.tpcNSigmaPi(); + } else { + return track.tpcNSigmaMu(); + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on track properties + // works for both all and associated tracks, and if MC is available or not + template + void fillTrackHistograms(TTrack const& track) + { + // fill the histos only for good tracks + if (!isGoodTrack(track)) { + return; + } + + registry.fill(HIST(TrackFolders[folder]) + HIST("hPt"), track.pt()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hEta"), track.eta()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hChi2NCl"), track.tpcChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcSignalVsP"), track.p(), track.tpcSignal()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hNSigmaVsP"), track.p(), getNSigma(track)); + registry.fill(HIST(TrackFolders[folder]) + HIST("hHasIts"), static_cast(track.hasITS())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hIsPvContrib"), static_cast(track.isPVContributor())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcNClsFound"), track.tpcNClsFound()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNCls"), track.itsNCls()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNClsInnerBarrel"), track.itsNClsInnerBarrel()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaXY"), track.dcaXY()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaZ"), track.dcaZ()); + + // if the MC info are available, fill the corresponding histograms + if constexpr (isMc) { + if (!track.has_mcParticle()) { + return; + } + auto mcPart = track.template mcParticle_as(); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hPt"), mcPart.pt()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hEta"), mcPart.eta()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMcIsPhysicalPrimary"), static_cast(mcPart.isPhysicalPrimary())); + if (mcPart.has_mothers()) { + for (auto const& mcMother : mcPart.template mothers_as()) { + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMotherPdg"), std::abs(mcMother.pdgCode())); + } + } + } + } + + //-------------------------------------------------------------------------------- + // loop over tracks and fill the histograms with info on track properties + template + void loopOverTracksBeforeGrouping(TTrack const& tracks) + { + for (auto const& track : tracks) { + int32_t collId = track.collisionId(); + fillTrackHistograms(track); + if (collId < 0) { + fillTrackHistograms(track); // isMc always false here since it fills Matched/.. histos + } + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on candidate properties + template + void fillCandidateHistograms(TCollision const& collision, TTrack const& tracks, float candMass) + { + registry.fill(HIST("Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Coll/hBCid"), collision.bcId()); + registry.fill(HIST("Coll/hVtxZ"), collision.posZ()); + registry.fill(HIST("Coll/hVtxX"), collision.posX()); + registry.fill(HIST("Coll/hVtxY"), collision.posY()); + registry.fill(HIST("Coll/hVtxChi2"), collision.chi2()); + if constexpr (isMc) { + if (collision.has_mcCollision()) { + auto mcColl = collision.template mcCollision_as(); + registry.fill(HIST("Matched/Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Matched/Coll/hVtxX"), mcColl.posX()); + registry.fill(HIST("Matched/Coll/hVtxY"), mcColl.posY()); + registry.fill(HIST("Matched/Coll/hVtxZ"), mcColl.posZ()); + } + } + + // Select tracks for the candidate + std::vector goodTracks; + for (auto const& track : tracks) { + if (isGoodTrack(track)) { + goodTracks.push_back(track); + // use TrkColl directory + fillTrackHistograms(track); + } + } + + registry.fill(HIST("TrkColl/hTrackSizePerEvent"), goodTracks.size()); + + // build UPC candidates using events with exactly two opposite sign good tracks + if (goodTracks.size() != twoTracks) { + return; + } + auto const& track0 = goodTracks[0]; + auto const& track1 = goodTracks[1]; + if (track0.sign() * track1.sign() >= 0) { + return; + } + + ROOT::Math::PxPyPzMVector p0(track0.px(), track0.py(), track0.pz(), candMass); + ROOT::Math::PxPyPzMVector p1(track1.px(), track1.py(), track1.pz(), candMass); + auto candidate = p0 + p1; + + registry.fill(HIST("Cand/hMass"), candidate.M()); + registry.fill(HIST("Cand/hPt"), candidate.Pt()); + registry.fill(HIST("Cand/hRapidity"), candidate.Rapidity()); + registry.fill(HIST("Cand/hPtVsRapidity"), candidate.Pt(), candidate.Rapidity()); + + if constexpr (isMc) { + if (track0.has_mcParticle() && track1.has_mcParticle()) { + auto mcPart0 = track0.template mcParticle_as(); + auto mcPart1 = track1.template mcParticle_as(); + ROOT::Math::PxPyPzMVector mcP0(mcPart0.px(), mcPart0.py(), mcPart0.pz(), candMass); + ROOT::Math::PxPyPzMVector mcP1(mcPart1.px(), mcPart1.py(), mcPart1.pz(), candMass); + auto mcCandidate = mcP0 + mcP1; + registry.fill(HIST("Matched/Cand/hMass"), mcCandidate.M()); + registry.fill(HIST("Matched/Cand/hPt"), mcCandidate.Pt()); + registry.fill(HIST("Matched/Cand/hRapidity"), mcCandidate.Rapidity()); + registry.fill(HIST("Matched/Cand/hPtVsRapidity"), mcCandidate.Pt(), mcCandidate.Rapidity()); + } + } + } + + //-------------------------------------------------------------------------------- + // generated (MC truth) level: daughters and their mothers + template + void checkMcGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + + registry.fill(HIST("McGen/Coll/hVtxX"), mcCollision.posX()); + registry.fill(HIST("McGen/Coll/hVtxY"), mcCollision.posY()); + registry.fill(HIST("McGen/Coll/hVtxZ"), mcCollision.posZ()); + + std::vector goodMcGenParticles; + for (auto const& mcPart : mcParticles) { + + if (!mcPart.isPhysicalPrimary() || mcPart.pt() < ptTrackMin || std::abs(mcPart.eta()) > etaTrackMax) { + continue; + } + registry.fill(HIST("McGen/Part/hPt"), mcPart.pt()); + registry.fill(HIST("McGen/Part/hEta"), mcPart.eta()); + + goodMcGenParticles.push_back(mcPart); + } + registry.fill(HIST("McGen/Part/hTrackSizePerEvent"), goodMcGenParticles.size()); + + // consider only j/psi->mumu and rho->pipi events + auto genId = mcCollision.getGeneratorId(); + LOGF(info, "Gen Id: {%d}", genId); + auto sourceId = mcCollision.getSourceId(); + LOGF(info, "Source Id: {%d}", sourceId); + + if constexpr (isEmbedded) { + if (!sourceId) { + return; + } + } else { + if (sourceId) { + LOGF(fatal, "Source Id: {%d} is not zero for non-embedded MC", sourceId); + return; + } + } + + if (goodMcGenParticles.size() != twoTracks) { + return; + } + + // we have 2 good tracks, with the same mother, taking it from the first one + auto mothers = goodMcGenParticles[0].template mothers_as(); + + if (mothers.empty()) { + LOGF(fatal, "Selected particle has no mother: pdgCode=%d", goodMcGenParticles[0].pdgCode()); + return; + } + auto motherPart = mothers[0]; + + registry.fill(HIST("McGen/MotherPart/hMotherPdg"), std::abs(motherPart.pdgCode())); + registry.fill(HIST("McGen/MotherPart/hPt"), motherPart.pt()); + registry.fill(HIST("McGen/MotherPart/hRapidity"), motherPart.y()); + registry.fill(HIST("McGen/MotherPart/hPtVsRapidity"), motherPart.pt(), motherPart.y()); + } + + //-------------------------------------------------------------------------------- + // PROCESS FUNCTIONS + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // check collisionId and TF + void processTracksIU(TracksExtraIU const& tracks, aod::Collisions const&, aod::BCs const&) + { + // track loop + for (const auto& track : tracks) { + + if (!isGoodTrack(track)) { + continue; + } + + int32_t collId = track.collisionId(); + + if (collId < 0) { + registry.fill(HIST("Coll/hCollIdCounter"), -1); + } else if (collId > 0) { + registry.fill(HIST("Coll/hCollIdCounter"), 1); + } else { + registry.fill(HIST("Coll/hCollIdCounter"), 0); + } + + if (collId < 0) { + continue; + } + + auto collision = track.collision(); + auto bc = collision.bc(); + + getRunInfo(bc.runNumber()); + + registry.fill(HIST("Coll/hnTF"), getTimeFrame(bc.globalBC())); + } + } + + // look at all tracks + void processRhoTracksBeforeGrouping(TracksExtraWPidPi const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGrouping, "Process rho tracks before asking for collisions", true); + + void processJpsiTracksBeforeGrouping(TracksExtraWPidMu const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGrouping, "Process J/Psi tracks before asking for collisions", false); + + void processRhoTracksBeforeGroupingMcInfo(TracksExtraWPidPiMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGroupingMcInfo, "Process rho tracks before asking for collisions", false); + + void processJpsiTracksBeforeGroupingMcInfo(TracksExtraWPidMuMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGroupingMcInfo, "Process J/Psi tracks before asking for collisions", false); + + // look at matched tracks + void processRhoCand(aod::Collision const& collision, TracksExtraWPidPi const& tracks) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCand, "Rho -> pi pi candidates and track QA", true); + + void processJpsiCand(aod::Collision const& collision, TracksExtraWPidMu const& tracks) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCand, "J/Psi -> mu mu candidates and track QA", false); + + void processRhoCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidPiMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCandMcInfo, "Rho -> pi pi candidates and track QA", false); + + void processJpsiCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidMuMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCandMcInfo, "J/Psi -> mu mu candidates and track QA", false); + + // look at gen MC + void processMcGenEmbedded(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenEmbedded, "Generated embedded events", false); + + void processMcGenExclusive(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenExclusive, "Generated exclusive events", false); + + PROCESS_SWITCH(UpcTrackVertexingQA, processTracksIU, "Track QA for IU tracks", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 8018361ff628234b5f6d812eeae2703b04c3db7c Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Tue, 13 Oct 2026 20:54:45 +0200 Subject: [PATCH 279/648] [PWGLF] Nuclei proton correlation: add TOF proton rejection (#18280) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 97484eaf831..96615e4414e 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -131,6 +131,7 @@ struct HadronNucleiCorrelation { Configurable nsigmaElDe{"nsigmaElDe", 3.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaTOF{"nsigmaTOF", 3.5f, "cut nsigma TOF"}; Configurable nsigmaTPCPrRej{"nsigmaTPCPrRej", 3.f, "cut nsigma TPC for proton rejection"}; + Configurable nsigmaTOFPrRej{"nsigmaTOFPrRej", 3.f, "cut nsigma TOF for proton rejection"}; Configurable doPrRej{"doPrRej", true, "do TPC proton rejection"}; Configurable nsigmaQuadratic{"nsigmaQuadratic", 3.0f, "cut on sqrt(nsigmaTPC^2 + nsigmaTOF^2), used above the TOF pT threshold when doQuadraticPID is on"}; Configurable nsigmaITSPr{"nsigmaITSPr", -2.0f, "cut nsigma ITS Pr"}; @@ -621,7 +622,7 @@ struct HadronNucleiCorrelation { const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; - const bool isNotPr = std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value || !doPrRej.value; + const bool isNotPr = (std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value) || !doPrRej.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; @@ -1030,7 +1031,7 @@ struct HadronNucleiCorrelation { const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; - const bool isNotPr = std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value || !doPrRej.value; + const bool isNotPr = (std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value) || !doPrRej.value; if (track.sign() > 0) { From 45efcbf5842a32fd50a0078551b4928ecd837626 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:08:40 +0800 Subject: [PATCH 280/648] [PWGLF] Add additional systematic uncertainty configurations (#18276) --- PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index e150c3644cc..1a3b71e02f8 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -932,12 +932,6 @@ struct lambdaspincorrderived { if (candidate.lambdaPt() > ptMax) { return false; } - if (v0Configurations.useK0sRejection) { - const double mK0s = getK0sCompetingMass(candidate); - if (std::abs(mK0s - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { - return false; - } - } if (v0Configurations.useAdditionalCut) { // additional cuts @@ -959,6 +953,12 @@ struct lambdaspincorrderived { if (candidate.ctau() > v0Configurations.maxCtau) { return false; } + + if (v0Configurations.useK0sRejection) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { + return false; + } + } } return true; From 975569beb223ce632c08bf37007064a0978d072c Mon Sep 17 00:00:00 2001 From: Xufei Xue <150438610+dongfangqingxin@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:33:03 +0800 Subject: [PATCH 281/648] [PWGHF] Add isSelLcToPiKP handling in derived-data creator correlations (#18277) --- .../derivedDataCreatorCorrelationsReduced.cxx | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 55bc064a960..a8fe13040bf 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -65,6 +65,7 @@ enum CandidateType { D0ToPiK, D0ToKPi, LcToPKPi, + LcToPiKP, Hadron }; @@ -121,7 +122,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectTrkData = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); Preslice candsDsPerColl = aod::hf_cand::collisionId; @@ -135,6 +136,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; Partition selectedLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Partition selectedLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, ""}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; @@ -222,6 +224,9 @@ struct HfDerivedDataCreatorCorrelationsReduced { if constexpr (CandType == CandidateType::LcToPKPi) { return HfHelper::invMassLcToPKPi(candidate); } + if constexpr (CandType == CandidateType::LcToPiKP) { + return HfHelper::invMassLcToPiKP(candidate); + } return -1.; } @@ -261,6 +266,11 @@ struct HfDerivedDataCreatorCorrelationsReduced { outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; } } + if constexpr (CandType == CandidateType::LcToPiKP) { + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl->at(iclass)]; + } + } return outputMl; } @@ -276,7 +286,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { y = HfHelper::yDplus(candidate); } else if constexpr (CandType == CandidateType::D0ToPiK || CandType == CandidateType::D0ToKPi) { y = HfHelper::yD0(candidate); - } else if constexpr (CandType == CandidateType::LcToPKPi) { + } else if constexpr (CandType == CandidateType::LcToPKPi || CandType == CandidateType::LcToPiKP) { y = HfHelper::yLc(candidate); } else { return true; @@ -582,7 +592,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { CandLcData const&) { auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } float cent{-1.}, mult{-1.}; @@ -591,6 +602,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillSameEvent(candsLcToPKPi, tracks, cent); + fillSameEvent(candsLcToPiKP, tracks, cent); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcSameEvent, "Process Same Event for Lc candidates", false); @@ -600,7 +612,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { CandLcData const&) { auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } float cent{-1.}, mult{-1.}; @@ -609,6 +622,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsLcToPKPi); + fillCharmMixedEvent(candsLcToPiKP); fillTrkMixedEvent(tracks, cent); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcMixedEvent, "Process Mixed Event for Lc candidates", false); From 91215a41ffbf974339f01971ad7eb9ae8f5520d4 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Wed, 14 Oct 2026 08:53:53 +0200 Subject: [PATCH 282/648] [PWGLF] Add structured71 Q-vector recentering to zdc2stagecalibration (#18282) --- .../Common/zdc2stagecalibration.cxx | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 97bbf49fa4b..bdc3b44c824 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -78,9 +78,10 @@ struct zdc2stagecalibration { Configurable deriveSpatialCalib{"deriveSpatialCalib", false, "store spatial regression moments after gain correction"}; Configurable deriveQRecentering{"deriveQRecentering", false, "store Q-vector recentering regression moments after gain and spatial correction"}; Configurable useQRecentering{"useQRecentering", false, "apply Q-vector recentering calibration"}; - Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression"}; + Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression, 4=structured71 regression"}; Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; + Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, @@ -128,8 +129,8 @@ struct zdc2stagecalibration { void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - if (qRecenteringMethod.value < 0 || qRecenteringMethod.value > 1) { - LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression or 1 for the multidimensional cubic regression.", qRecenteringMethod.value); + if (qRecenteringMethod.value != 0 && qRecenteringMethod.value != 1 && qRecenteringMethod.value != 4) { + LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression, 1 for the multidimensional cubic regression or 4 for the structured71 regression.", qRecenteringMethod.value); } const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); @@ -324,12 +325,19 @@ struct zdc2stagecalibration { } } if (calibrationStage.value == 2 && useQRecentering.value) { - const std::string qRecenteringPath = qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value; + const std::string qRecenteringPath = qRecenteringMethod.value == 4 ? confQRecenteringStructured71Path.value : (qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value); if (!qRecenteringPath.empty()) { qRecenteringProfile = ccdb->getForTimeStamp(qRecenteringPath, timestamp); } if (!qRecenteringProfile) { LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + } else if (qRecenteringMethod.value == 4) { + const bool structuredMapOK = qRecenteringProfile->GetYaxis()->GetNbins() == kQRecenteringStructuredNFeatures && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringMultidimNFeatures + 1)) == "(T--0.8)_+^3" && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringStructuredNFeatures)) == "(Vz-+0.8)_+^3"; + if (!structuredMapOK) { + LOGF(fatal, "CCDB object at %s is not a valid structured71 map for run %d", qRecenteringPath.c_str(), runNumber); + } } } } @@ -942,6 +950,24 @@ struct zdc2stagecalibration { } qValues[component] -= predictedBias; } + } else if (qRecenteringMethod.value == 4) { + const auto qFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } } else { const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); for (int component = 0; component < kQRecenteringNComponents; ++component) { From c5a388ffb08914d6ca24cd89c7c10530e39a6612 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:51:06 +0200 Subject: [PATCH 283/648] [ALICE3] Correctly handle response for short lived particles that do not reach tof layers in otf tof (#18270) --- ALICE3/DataModel/OTFTOF.h | 7 ++++ ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx | 44 ++++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/ALICE3/DataModel/OTFTOF.h b/ALICE3/DataModel/OTFTOF.h index 8643abc959c..621e878a1e7 100644 --- a/ALICE3/DataModel/OTFTOF.h +++ b/ALICE3/DataModel/OTFTOF.h @@ -79,6 +79,8 @@ DECLARE_SOA_COLUMN(OuterTOFExpectedTimeDe, outerTOFExpectedTimeDe, float); //! DECLARE_SOA_COLUMN(OuterTOFExpectedTimeTr, outerTOFExpectedTimeTr, float); //! Reconstructed expected time at the OuterTOF for the Triton mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeHe3, outerTOFExpectedTimeHe3, float); //! Reconstructed expected time at the OuterTOF for the Helium3 mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeAl, outerTOFExpectedTimeAl, float); //! Reconstructed expected time at the OuterTOF for the Alpha mass hypotheses +DECLARE_SOA_COLUMN(HasInnerTOF, hasInnerTOF, bool); //! InnerTOF short-lived values are filled (false if dummy) +DECLARE_SOA_COLUMN(HasOuterTOF, hasOuterTOF, bool); //! OuterTOF short-lived values are filled (false if dummy) DECLARE_SOA_DYNAMIC_COLUMN(NSigmaInnerTOF, nSigmaInnerTOF, //! General function to get the nSigma for the InnerTOF [](const float el, const float mu, @@ -260,10 +262,15 @@ DECLARE_SOA_TABLE(UpgradeTofShortLiveds, "AOD", "UPGRTOFSHRTLVD", upgrade_tof::OuterTOFExpectedTimeXi, upgrade_tof::OuterTOFExpectedTimeOm); +DECLARE_SOA_TABLE(UpgradeTofShortLivedSignals, "AOD", "UPGRTOFSLSIGNAL", + upgrade_tof::HasInnerTOF, + upgrade_tof::HasOuterTOF); + using UpgradeTofMC = UpgradeTofMCs::iterator; using UpgradeTof = UpgradeTofs::iterator; using UpgradeTofExpectedTime = UpgradeTofExpectedTimes::iterator; using UpgradeTofShortLived = UpgradeTofShortLiveds::iterator; +using UpgradeTofShortLivedSignal = UpgradeTofShortLivedSignals::iterator; } // namespace o2::aod diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index 9bfc03a70a2..f1ba29ab1d4 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -30,6 +30,7 @@ #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/tracksAlice3.h" #include "Common/Core/trackUtilities.h" #include @@ -77,6 +78,7 @@ struct OnTheFlyTofPid { Produces upgradeTof; Produces upgradeTofExpectedTime; Produces upgradeTofShortLived; + Produces upgradeTofShortLivedSignal; // necessary for particle charges Service pdgDatabase{}; @@ -103,6 +105,7 @@ struct OnTheFlyTofPid { Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; Configurable flagTOFLoadDelphesLUTs{"flagTOFLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + Configurable shortLivedRadiusTolerance{"shortLivedRadiusTolerance", 0.1f, "tolerance (cm) when checking if a TOF layer is within the innermost and outermost track update of short-lived particles"}; } simConfig; struct : ConfigurableGroup { @@ -211,6 +214,39 @@ struct OnTheFlyTofPid { particleHe, particleAl}; + template + void fillShortLivedTables(const TTrack& track, + std::array nSigmaInnerTOF, + std::array nSigmaOuterTOF, + std::array expectedTimeInnerTOF, + std::array expectedTimeOuterTOF, + const std::pair& trackLengthInnerTOF, + const std::pair& trackLengthOuterTOF) + { + const auto isWithinTrackUpdates = [&](const float tofRadius) { + return (track.trackIU() - simConfig.shortLivedRadiusTolerance <= tofRadius) && (tofRadius <= track.trackOU() + simConfig.shortLivedRadiusTolerance); + }; + + const bool hasInnerTOF = isWithinTrackUpdates(simConfig.innerTOFRadius) && trackLengthInnerTOF.first > 0 && trackLengthInnerTOF.second > 0; + const bool hasOuterTOF = isWithinTrackUpdates(simConfig.outerTOFRadius) && trackLengthOuterTOF.first > 0 && trackLengthOuterTOF.second > 0; + static constexpr float DummyValue = -999.f; + for (const int ii : {Sp, Sm, Xi, Om}) { + if (!hasInnerTOF) { + nSigmaInnerTOF[ii] = DummyValue; + expectedTimeInnerTOF[ii] = DummyValue; + } + if (!hasOuterTOF) { + nSigmaOuterTOF[ii] = DummyValue; + expectedTimeOuterTOF[ii] = DummyValue; + } + } + upgradeTofShortLived(nSigmaInnerTOF[Sp], nSigmaInnerTOF[Sm], nSigmaInnerTOF[Xi], nSigmaInnerTOF[Om], + nSigmaOuterTOF[Sp], nSigmaOuterTOF[Sm], nSigmaOuterTOF[Xi], nSigmaOuterTOF[Om], + expectedTimeInnerTOF[Sp], expectedTimeInnerTOF[Sm], expectedTimeInnerTOF[Xi], expectedTimeInnerTOF[Om], + expectedTimeOuterTOF[Sp], expectedTimeOuterTOF[Sm], expectedTimeOuterTOF[Xi], expectedTimeOuterTOF[Om]); + upgradeTofShortLivedSignal(hasInnerTOF, hasOuterTOF); + } + bool doQaForParticle(const int pdgCode) { return std::find(plotsConfig.particlesForQa.value.begin(), plotsConfig.particlesForQa.value.end(), std::abs(pdgCode)) != plotsConfig.particlesForQa.value.end(); @@ -652,7 +688,7 @@ struct OnTheFlyTofPid { } void process(soa::Join::iterator const& collision, - soa::Join const& tracks, + soa::Join const& tracks, aod::McParticles const&, aod::McCollisions const&) { @@ -971,10 +1007,8 @@ struct OnTheFlyTofPid { measuredTimeOuterTOF, trackLengthRecoOuterTOF); upgradeTofExpectedTime(expectedTimeInnerTOF[El], expectedTimeInnerTOF[Mu], expectedTimeInnerTOF[Pi], expectedTimeInnerTOF[Ka], expectedTimeInnerTOF[Pr], expectedTimeInnerTOF[De], expectedTimeInnerTOF[Tr], expectedTimeInnerTOF[He], expectedTimeInnerTOF[Al], expectedTimeOuterTOF[El], expectedTimeOuterTOF[Mu], expectedTimeOuterTOF[Pi], expectedTimeOuterTOF[Ka], expectedTimeOuterTOF[Pr], expectedTimeOuterTOF[De], expectedTimeOuterTOF[Tr], expectedTimeOuterTOF[He], expectedTimeOuterTOF[Al]); - upgradeTofShortLived(nSigmaInnerTOF[Sp], nSigmaInnerTOF[Sm], nSigmaInnerTOF[Xi], nSigmaInnerTOF[Om], - nSigmaOuterTOF[Sp], nSigmaOuterTOF[Sm], nSigmaOuterTOF[Xi], nSigmaOuterTOF[Om], - expectedTimeInnerTOF[Sp], expectedTimeInnerTOF[Sm], expectedTimeInnerTOF[Xi], expectedTimeInnerTOF[Om], - expectedTimeOuterTOF[Sp], expectedTimeOuterTOF[Sm], expectedTimeOuterTOF[Xi], expectedTimeOuterTOF[Om]); + fillShortLivedTables(track, nSigmaInnerTOF, nSigmaOuterTOF, expectedTimeInnerTOF, expectedTimeOuterTOF, + trkWithTime.mTrackLengthInnerTOF, trkWithTime.mTrackLengthOuterTOF); } if (trackWithTimeIndex != tracks.size()) { From fee6ed0812aa76302085eb2ca8f7d097f9a6f9d7 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:53:13 +0200 Subject: [PATCH 284/648] [ALICE3] Use track updates instead of mc info for causality check in a3 strangeness (#18269) --- .../TableProducer/alice3strangenessFinder.cxx | 56 ++----------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/ALICE3/TableProducer/alice3strangenessFinder.cxx b/ALICE3/TableProducer/alice3strangenessFinder.cxx index 9725d9506cf..8d8216c4c5d 100644 --- a/ALICE3/TableProducer/alice3strangenessFinder.cxx +++ b/ALICE3/TableProducer/alice3strangenessFinder.cxx @@ -21,7 +21,6 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "ALICE3/Core/GeometryContainer.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" @@ -32,7 +31,6 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -74,9 +72,6 @@ struct Alice3strangenessFinder { SliceCache cache; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb{}; - o2::fastsim::GeometryContainer geoContainer; - std::vector> trackingLayers; Produces v0CandidateIndices; // contains V0 candidate indices Produces v0CandidateCores; // contains V0 candidate core information @@ -151,7 +146,6 @@ struct Alice3strangenessFinder { o2::vertexing::DCAFitterN<2> fitter; Service pdgDB{}; static constexpr float ToMicrons = 1e+4; - static constexpr int LutConfig = 0; // todo // partitions for v0/casc dau tracks Partition positiveSecondaryTracksACTS = @@ -199,7 +193,7 @@ struct Alice3strangenessFinder { float dcaToPV{}; }; - void init(InitContext& initContext) + void init(InitContext&) { // Initialization code here fitter.setBz(magneticField); @@ -280,30 +274,6 @@ struct Alice3strangenessFinder { histos.add("Generated/hGeneratedAntiOmega", "hGeneratedAntiOmega", kTH2D, {{axisPt}, {axisEta}}); } - if (doprocessFindV0CandidateOTF) { - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setTimestamp(-1); - geoContainer.setCcdbManager(ccdb.operator->()); - geoContainer.init(initContext); - const int nGeometries = geoContainer.getNumberOfConfigurations(); - trackingLayers.resize(nGeometries); - - for (int icfg = 0; icfg < nGeometries; ++icfg) { - auto globalConfiguration = geoContainer.getConfigurations(icfg); - - for (const auto& [outerKey, innerMap] : globalConfiguration) { - if (outerKey.empty() || outerKey[0] != 'B') { - continue; - } - - auto it = innerMap.find("r"); - if (it != innerMap.end()) { - trackingLayers[icfg].push_back(std::stof(it->second)); - } - } - } - } - if (buildCascade) { histos.add("CascadeBuilding/hDcaBetweenDaus", "hDcaBetweenDaus", kTH1D, {{axisDCA}}); histos.add("CascadeBuilding/hXiMass", "", kTH1D, {axisXiMass}); @@ -374,19 +344,6 @@ struct Alice3strangenessFinder { return returnValue; } - template - [[nodiscard]] float getFirstLayerHitRadius(const TTrackType& track, const std::vector& layers) - { - const float trueRadius = std::hypot(track.x(), track.y()); - for (const float layerRadius : layers) { - if (layerRadius >= trueRadius) { - return layerRadius; - } - } - static constexpr float OutsideALICE3 = 100.f; - return OutsideALICE3; - } - template bool buildDecayCandidateTwoBody(TTrackType const& t0, TTrackType const& t1, std::array vtx, Candidate& thisCandidate) { @@ -672,15 +629,12 @@ struct Alice3strangenessFinder { const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); const float radiusCasc = std::hypot(cascCand.posSV[0], cascCand.posSV[1]); - const float posCausalityRadius = getFirstLayerHitRadius(posTrack, trackingLayers[LutConfig]); - const float negCausalityRadius = getFirstLayerHitRadius(negTrack, trackingLayers[LutConfig]); - const float bachCausalityRadius = getFirstLayerHitRadius(bachTrack, trackingLayers[LutConfig]); - - if (posCausalityRadius < radiusV0) { + // tracks without update information (IU < 0) are not checked + if (posTrack.trackIU() > 0.f && posTrack.trackIU() < radiusV0) { continue; // positive track hit a layer before v0 decayed } - if (negCausalityRadius < radiusV0) { + if (negTrack.trackIU() > 0.f && negTrack.trackIU() < radiusV0) { continue; // negative track hit a layer before v0 decayed } @@ -688,7 +642,7 @@ struct Alice3strangenessFinder { continue; // v0 decayed before cascade } - if (bachCausalityRadius < radiusCasc) { + if (bachTrack.trackIU() > 0.f && bachTrack.trackIU() < radiusCasc) { continue; // bachelor track hit a layer before v0 decayed } From 4444acc969f79f3bc91198b825ff8879c6b9ed58 Mon Sep 17 00:00:00 2001 From: Rocco Liotino Date: Thu, 15 Oct 2026 14:26:23 +0200 Subject: [PATCH 285/648] [DPG] Update systematics for deuteron analysis (#18289) --- .../AOTTrack/PID/HMPID/hmpidDeuteron.cxx | 251 +++++++++++++++--- 1 file changed, 217 insertions(+), 34 deletions(-) diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx index e41e69d842c..af8f96dac9a 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx @@ -44,11 +44,16 @@ struct HmpidDeuteron { Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; - Configurable cutMinMomGlobalTrack{"cutMinMomGlobalTrack", 1.5f, "minimum momentum of global track"}; + Configurable qMipThreshold{"qMipThreshold", 120.0f, "minimum MIP charge required for a real signal"}; + Configurable cutDistanceMipTrack{"cutDistanceMipTrack", 6.0f, "cut distance between MIP and track"}; Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; + Configurable collisionsPbPb{"collisionsPbPb", false, "Require PbPb collisions"}; + + Configurable centralityCut{"centralityCut", 20.0f, "centrality cut for PbPb collisions"}; + // Configurables for plotting // Momentum Configurable momNBins{"momNBins", 490, "Momentum bins"}; @@ -56,17 +61,17 @@ struct HmpidDeuteron { Configurable momMax{"momMax", 5.0f, "Momentum max"}; // DeltaR - Configurable deltaRNBins{"deltaRNBins", 500, "DeltaR bins"}; + Configurable deltaRNBins{"deltaRNBins", 50, "DeltaR bins"}; Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; // nSigma TPC - Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 20, "nSigmaTPC bins"}; + Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 100, "nSigmaTPC bins"}; Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; // nSigma TOF - Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 20, "nSigmaTOF bins"}; + Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 100, "nSigmaTOF bins"}; Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; @@ -161,6 +166,17 @@ struct HmpidDeuteron { registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DeNeg", "nSigmaTPC_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTPC}); registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DeNeg", "nSigmaTOF_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("pidTPC_TOF_p_ch_DePos", "pidTPC_TOF_p_ch_DePos", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + registryDA.add("pidTPC_TOF_p_ch_DeNeg", "pidTPC_TOF_p_ch_DeNeg", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + // general plots registryDA.add("hEta", "hEta", kTH1F, {axisEta}); registryDA.add("hPhi", "hPhi", kTH1F, {axisPhi}); @@ -175,6 +191,14 @@ struct HmpidDeuteron { registryDA.add(Form("De_Neg_deltaR_%d", iCh), Form("De_Neg_deltaR_%d", iCh), HistType::kTH1F, {axisDeltaR}); registryDA.add(Form("hEta_%d", iCh), Form("hEta_%d", iCh), HistType::kTH1F, {axisEta}); registryDA.add(Form("hPhi_%d", iCh), Form("hPhi_%d", iCh), HistType::kTH1F, {axisPhi}); + + // tracking efficiency hmpid + registryDA.add(Form("hAllTracks_%d", iCh), Form("hAllTracks_%d", iCh), HistType::kTH1F, {axisMom}); + registryDA.add(Form("hCutQandD_%d", iCh), Form("hCutQandD_%d", iCh), HistType::kTH1F, {axisMom}); + registryDA.add(Form("hAllTracks_Pos_%d", iCh), Form("hAllTracks_Pos_%d", iCh), HistType::kTH1F, {axisMom}); + registryDA.add(Form("hAllTracks_Neg_%d", iCh), Form("hAllTracks_Neg_%d", iCh), HistType::kTH1F, {axisMom}); + registryDA.add(Form("hCutQandD_Pos_%d", iCh), Form("hCutQandD_Pos_%d", iCh), HistType::kTH1F, {axisMom}); + registryDA.add(Form("hCutQandD_Neg_%d", iCh), Form("hCutQandD_Neg_%d", iCh), HistType::kTH1F, {axisMom}); } } @@ -216,6 +240,7 @@ struct HmpidDeuteron { const float dx = hmpid.xMip() - hmpid.xTrack(); const float dy = hmpid.yMip() - hmpid.yTrack(); const float dr = std::hypot(dx - offsetX, dy - offsetY); + const float dMipTrack = std::hypot(dx, dy); const float mass = std::pow(Nmean * momAbs * std::cos(hmpid.chAngle()), 2) - @@ -225,6 +250,8 @@ struct HmpidDeuteron { const bool isPos = momHmpid > 0; const bool isNeg = momHmpid < 0; + const bool passQandD = hmpid.chargeMip() > qMipThreshold && dMipTrack < cutDistanceMipTrack; + // ------------------------- // global distributions // ------------------------- @@ -238,35 +265,171 @@ struct HmpidDeuteron { } // ------------------------- - // RICH chamber maps (0–6) + // Fill plots per chamber // ------------------------- - if (chamber == Rich0) { - registryDA.fill(HIST("hEta_0"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_0"), hmpid.phiTrack()); - } - if (chamber == Rich1) { - registryDA.fill(HIST("hEta_1"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_1"), hmpid.phiTrack()); - } - if (chamber == Rich2) { - registryDA.fill(HIST("hEta_2"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_2"), hmpid.phiTrack()); - } - if (chamber == Rich3) { - registryDA.fill(HIST("hEta_3"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_3"), hmpid.phiTrack()); - } - if (chamber == Rich4) { - registryDA.fill(HIST("hEta_4"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_4"), hmpid.phiTrack()); + + switch (chamber) { + + case Rich0: + registryDA.fill(HIST("hEta_0"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_0"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_0"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_0"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_0"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_0"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_0"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_0"), momAbs); + } + } + break; + + case Rich1: + registryDA.fill(HIST("hEta_1"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_1"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_1"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_1"), momAbs); + } + + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_1"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_1"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_1"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_1"), momAbs); + } + } + break; + + case Rich2: + registryDA.fill(HIST("hEta_2"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_2"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_2"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_2"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_2"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_2"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_2"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_2"), momAbs); + } + } + break; + + case Rich3: + registryDA.fill(HIST("hEta_3"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_3"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_3"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_3"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_3"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_3"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_3"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_3"), momAbs); + } + } + break; + + case Rich4: + registryDA.fill(HIST("hEta_4"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_4"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_4"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_4"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_4"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_4"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_4"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_4"), momAbs); + } + } + break; + + case Rich5: + registryDA.fill(HIST("hEta_5"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_5"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_5"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_5"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_5"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_5"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_5"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_5"), momAbs); + } + } + break; + + case Rich6: + registryDA.fill(HIST("hEta_6"), hmpid.etaTrack()); + registryDA.fill(HIST("hPhi_6"), hmpid.phiTrack()); + registryDA.fill(HIST("hAllTracks_6"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_6"), momAbs); + } + if (isPos) { + registryDA.fill(HIST("hAllTracks_Pos_6"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Pos_6"), momAbs); + } + } + if (isNeg) { + registryDA.fill(HIST("hAllTracks_Neg_6"), momAbs); + if (passQandD) { + registryDA.fill(HIST("hCutQandD_Neg_6"), momAbs); + } + } + break; + + default: + break; } - if (chamber == Rich5) { - registryDA.fill(HIST("hEta_5"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_5"), hmpid.phiTrack()); + + // ------------------------- + // TPC + TOF PID distributions + // ------------------------- + if (isPos) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_DePos"), momAbs, hmpid.tpcNSigmaDe(), hmpid.tofNSigmaDe(), chamber); } - if (chamber == Rich6) { - registryDA.fill(HIST("hEta_6"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_6"), hmpid.phiTrack()); + if (isNeg) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_DeNeg"), momAbs, hmpid.tpcNSigmaDe(), hmpid.tofNSigmaDe(), chamber); } // ------------------------- @@ -281,12 +444,25 @@ struct HmpidDeuteron { // post TPC cut // ------------------------- if (isPos) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DePos"), - momAbs, hmpid.tpcNSigmaDe()); + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DePos"), momAbs, hmpid.tpcNSigmaDe()); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos"), momAbs, hmpid.tofNSigmaDe()); } if (isNeg) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DeNeg"), - momAbs, hmpid.tpcNSigmaDe()); + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DeNeg"), momAbs, hmpid.tpcNSigmaDe()); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg"), momAbs, hmpid.tofNSigmaDe()); + } + + if (isPos && chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm"), momAbs, hmpid.tofNSigmaDe()); + } + if (isPos && chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm"), momAbs, hmpid.tofNSigmaDe()); + } + if (isNeg && chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm"), momAbs, hmpid.tofNSigmaDe()); + } + if (isNeg && chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm"), momAbs, hmpid.tofNSigmaDe()); } registryDA.fill(HIST("hMass_postDeuteron"), mass); @@ -316,6 +492,13 @@ struct HmpidDeuteron { // DEUTERON CANDIDATES AFTER ALL CUTS // ------------------------- + // Pb-Pb collisions centrality requirement + if (collisionsPbPb) { + if (hmpid.centrality() < centralityCut) { + continue; + } + } + // ------------------------- // deltaR per chamber (Pos/Neg) // ------------------------- From 70d381f393fe510c47e71b5498ed5de35efda811 Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:30:43 +0200 Subject: [PATCH 286/648] [PWGEM,PWGEM-36] PhotonMeson: two-photon MC truth task, pair background QA and PCM-EMC MC (#18275) --- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 4 +- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h | 46 +- PWGEM/PhotonMeson/Tasks/CMakeLists.txt | 9 + .../Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx | 39 + PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx | 107 +- PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx | 1278 +++++++++++++++++ PWGEM/PhotonMeson/Utils/MCUtilities.h | 64 + PWGEM/PhotonMeson/Utils/NMHistograms.h | 17 +- 8 files changed, 1522 insertions(+), 42 deletions(-) create mode 100644 PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx create mode 100644 PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 91818c3f3d2..85c74cc14bb 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -428,9 +428,9 @@ struct Pi0EtaToGammaGamma { std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs); } DefineEMEventCut(); DefinePCMCut(); diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index dc20e1b3883..351bb86db35 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -637,7 +637,7 @@ struct Pi0EtaToGammaGammaMC { fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted int photonid1 = -1, photonid2 = -1, pi0id = -1, etaid = -1; - if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing + if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { // same kinds pairing and PCM-EMC auto photons1_per_collision = photons1.sliceByCached(TDetectorTag1::perCollision(), collision.globalIndex(), cache); auto photons2_per_collision = photons2.sliceByCached(TDetectorTag2::perCollision(), collision.globalIndex(), cache); @@ -652,7 +652,7 @@ struct Pi0EtaToGammaGammaMC { continue; } } else { - if (!TDetectorTag1::applyCut(*this, g1) || !TDetectorTag2::applyCut(*this, g2)) { + if (!TDetectorTag1::applyCut(*this, g1)) { // g2 is checked below with its own tag (and matched tracks for EMC) continue; } } @@ -696,6 +696,20 @@ struct Pi0EtaToGammaGammaMC { photonid1 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster1mcparticle, mcparticles, std::vector{111, 221}); photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + // PCM: true conversion as for PCM-PCM + auto pos1 = g1.template posTrack_as(); + auto ele1 = g1.template negTrack_as(); + auto pos1mc = pos1.template emmcparticle_as(); + auto ele1mc = ele1.template emmcparticle_as(); + photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, -11, 11, 22, mcparticles); + + // EMC: photon below the pi0/eta of the leading contributor as for EMC-EMC + if (g2.emmcparticleIds().empty()) { + continue; // noise cluster without MC label + } + auto cluster2mcparticle = mcparticles.iteratorAt(g2.emmcparticleIds()[0]); + photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); } else { photonid1 = -1; photonid2 = -1; @@ -711,6 +725,10 @@ struct Pi0EtaToGammaGammaMC { if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles) || !o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g2mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { continue; } + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { + continue; + } } pi0id = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(g1mc, g2mc, 22, 22, 111, mcparticles); @@ -1024,19 +1042,17 @@ struct Pi0EtaToGammaGammaMC { auto&& [emcclusters, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); @@ -1070,19 +1086,17 @@ struct Pi0EtaToGammaGammaMC { // runTruePairing(collisions, emcclusters, emcclusters, nullptr, nullptr, perCollision_emc, perCollision_emc, fEMCCut, fEMCCut, mccollisions, mcparticles); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index 729346bab9e..56c32ab6a3b 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -116,6 +116,11 @@ o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-emcemc PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-pcmemc + SOURCES Pi0EtaToGammaGammaMCPCMEMC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore O2Physics::MLCore @@ -205,3 +210,7 @@ o2physics_add_dpl_workflow(pair-qc-task SOURCES pairQcTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(two-photon-mc-task + SOURCES twoPhotonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx new file mode 100644 index 00000000000..6bd33481ce2 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx @@ -0,0 +1,39 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file Pi0EtaToGammaGammaMCPCMEMC.cxx +/// \brief This code loops over photons and makes pairs for neutral mesons analyses in MC for PCM-EMC. +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::aod::pwgem::photonmeson::photonpair; + +using MyV0Photons = o2::soa::Filtered>; +using MyMCV0Legs = soa::Join; +using MyEMCClusters = soa::Join; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmemc"}), + }; +} diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index e824436c401..230ad975aec 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -133,7 +133,6 @@ struct TaskPi0FlowEMC { ConfigurableAxis thnConfigAxisCosDeltaPhi{"thnConfigAxisCosDeltaPhi", {8, -1., 1.}, "cos(delta phi) axis for the current event"}; ConfigurableAxis thnConfigAxisM02{"thnConfigAxisM02", {200, 0., 5.}, "M02 axis for the EMCal cluster"}; ConfigurableAxis thnConfigAxisEnergyCalib{"thnConfigAxisEnergyCalib", {200, 0., 20.}, "energy axis for the emcal clusters for the calibration process"}; - ConfigurableAxis thConfigAxisEtaPhiAngle{"thConfigAxisEtaPhiAngle", {180, -o2::constants::math::PI, o2::constants::math::PI}, "atan2(#Delta#eta,#Delta#varphi) axis (deg)"}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -222,6 +221,11 @@ struct TaskPi0FlowEMC { Configurable minTanThetadPhi{"minTanThetadPhi", 4., "apply min opening angle in delta theta delta phi to cut on late conversion"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; ConfigurableAxis thConfigAxisTanThetaPhi{"thConfigAxisTanThetaPhi", {180, -90.f, 90.f}, ""}; + Configurable cfgPairBkgMeson{"cfgPairBkgMeson", 0, "meson for the mass binning of the pair background QA THn (0: pi0, 1: eta)"}; + ConfigurableAxis thnConfigAxisPairBkgMassPi0{"thnConfigAxisPairBkgMassPi0", {VARIABLE_WIDTH, 0.0f, 0.07f, 0.10f, 0.17f, 0.30f, 0.50f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the pi0"}; + ConfigurableAxis thnConfigAxisPairBkgMassEta{"thnConfigAxisPairBkgMassEta", {VARIABLE_WIDTH, 0.30f, 0.45f, 0.50f, 0.60f, 0.70f, 0.80f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the eta"}; + ConfigurableAxis thnConfigAxisPairBkgPt{"thnConfigAxisPairBkgPt", {VARIABLE_WIDTH, 0.f, 1.f, 2.f, 3.f, 5.f, 8.f, 20.f}, "pT axis of the pair background QA THn"}; + ConfigurableAxis thnConfigAxisPairBkgCent{"thnConfigAxisPairBkgCent", {VARIABLE_WIDTH, 0.f, 10.f, 30.f, 50.f, 90.f}, "centrality axis of the pair background QA THn"}; } mesonConfig; struct : ConfigurableGroup { @@ -417,7 +421,6 @@ struct TaskPi0FlowEMC { const AxisSpec thnAxisMixingVtx{mixingConfig.cfgVtxBins, "#it{z} (cm)"}; const AxisSpec thnAxisMixingCent{mixingConfig.cfgCentBins, "Centrality (%)"}; const AxisSpec thnAxisMixingEP{mixingConfig.cfgEPBins, Form("cos(%d#varphi)", harmonic.value)}; - const AxisSpec thAxisEtaPhiAngle{thConfigAxisEtaPhiAngle, "atan2(#Delta#eta,#Delta#varphi) (rad)"}; const AxisSpec thnQaAxisMinv{16, 0, 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; // bin witdh 0.05 GeV/c^2 const AxisSpec thnQaAxisPt{10, 0, 20., "#it{p}_{T} (GeV/#it{c})"}; // bin width 2 GeV/c @@ -477,8 +480,14 @@ struct TaskPi0FlowEMC { registry.add("mesonQA/hInvMassPtMixed", "Histo for inv pair mass vs pt for mixed event", HistType::kTH2D, {thnAxisInvMass, thnAxisPt}); registry.add("mesonQA/hTanThetaPhiMixed", "Histo for identification of conversion cluster for mixed event", HistType::kTH2D, {thnAxisInvMass, thAxisTanThetaPhi}); registry.add("mesonQA/hAlphaPtMixed", "Histo of meson asymmetry vs pT for mixed event", HistType::kTH2D, {thAxisAlpha, thnAxisPt}); - registry.add("mesonQA/hEtaPhiAngleMassCent", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, same event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); - registry.add("mesonQA/hEtaPhiAngleMassCentMixed", "atan2(#Delta#eta,#Delta#varphi) vs m_{inv} vs cent, mixed event", HistType::kTH3D, {thAxisEtaPhiAngle, thnAxisInvMass, thnAxisCent}); + const bool isPCMEMC = doprocessEMCalPCMC.value || doprocessEMCalPCMMixed.value; + const AxisSpec thnAxisPairBkgMass{mesonConfig.cfgPairBkgMeson.value == 0 ? mesonConfig.thnConfigAxisPairBkgMassPi0 : mesonConfig.thnConfigAxisPairBkgMassEta, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPairBkgPt{mesonConfig.thnConfigAxisPairBkgPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPairBkgCent{mesonConfig.thnConfigAxisPairBkgCent, "Centrality (%)"}; + const AxisSpec thnAxisPairBkgAlpha = isPCMEMC ? AxisSpec{40, -1.f, 1.f, "#alpha = (#it{E}_{PCM} - #it{E}_{EMC}) / (#it{E}_{PCM} + #it{E}_{EMC})"} : AxisSpec{20, 0.f, 1.f, "#alpha = |#it{E}_{1} - #it{E}_{2}| / (#it{E}_{1} + #it{E}_{2})"}; + const AxisSpec thnAxisPairBkgDirection{36, -o2::constants::math::PIHalf, o2::constants::math::PIHalf, "atan2(#Delta#eta, #Delta#varphi) (rad)"}; + registry.add("mesonQA/hPairBkgSE", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for same event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); + registry.add("mesonQA/hPairBkgME", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for mixed event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for same event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for mixed event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); } @@ -541,6 +550,38 @@ struct TaskPi0FlowEMC { registry.fill(HIST(HistTypes[histType]), mass, pt, cent); } + /// \brief Fill the pair background QA THn (m_inv, pT, cent, alpha, direction) + /// The direction is folded such that dPhi is in [0, pi] and dEta keeps its sign, identical for same and mixed event. + /// \tparam isMixed fill the mixed instead of the same event histogram + /// \tparam isSignedAlpha use the signed asymmetry (E1 - E2) / (E1 + E2), for PCM-EMC photon1 has to be the PCM photon + /// \param photon1 first photon + /// \param photon2 second photon + /// \param meson pair of photon1 and photon2 + /// \param cent centrality of the (first) collision + template + void fillPairBkg(ROOT::Math::PtEtaPhiMVector const& photon1, ROOT::Math::PtEtaPhiMVector const& photon2, ROOT::Math::PtEtaPhiMVector const& meson, const float cent) + { + if (!mesonConfig.cfgEnableQA.value) { + return; + } + float alpha = (photon1.E() - photon2.E()) / (photon1.E() + photon2.E()); + if constexpr (!isSignedAlpha) { + alpha = std::fabs(alpha); + } + float dPhi = RecoDecay::constrainAngle(photon1.Phi() - photon2.Phi(), -o2::constants::math::PI); // wrap to [-pi, pi) + float dEta = photon1.Eta() - photon2.Eta(); + if (dPhi < 0.f) { // swap the labels: flip BOTH signs + dPhi = -dPhi; + dEta = -dEta; + } + const float direction = std::atan2(dEta, dPhi); // in [-pi/2, pi/2] since dPhi >= 0 + if constexpr (isMixed) { + registry.fill(HIST("mesonQA/hPairBkgME"), meson.M(), meson.Pt(), cent, alpha, direction); + } else { + registry.fill(HIST("mesonQA/hPairBkgSE"), meson.M(), meson.Pt(), cent, alpha, direction); + } + } + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, /// assuming a nominal cylindrical EMCal surface at transverse radius emcalR (cm). /// phi is untouched: transverse vertex spread is negligible next to emcalR. @@ -1077,7 +1118,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1100,14 +1146,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } @@ -1241,7 +1287,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); - float dEta = v1.Eta() - eta2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1261,14 +1312,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCentMixed"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } @@ -1335,7 +1386,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); if (openingAngle <= mesonConfig.minOpenAngle) { @@ -1357,13 +1413,13 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } + fillPairBkg(v2, v1, vMeson, getCentrality(collision)); // v2 is the PCM photon runFlowAnalysis<0>(collision, vMeson); } } @@ -1434,7 +1490,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1454,14 +1515,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCentMixed"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v2, v1, vMeson, getCentrality(c1)); // v2 is the PCM photon runFlowAnalysis<2>(c1, vMeson); } } @@ -1555,8 +1616,12 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; - float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1575,10 +1640,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(collision)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(collision)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } // end of loop over collisions @@ -1630,8 +1695,12 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; - float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1650,10 +1719,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); - registry.fill(HIST("mesonQA/hEtaPhiAngleMassCent"), RecoDecay::constrainAngle(std::atan2(dEta, dPhi), -o2::constants::math::PI), vMeson.M(), getCentrality(c1)); - registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, std::fabs(RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI)), getCentrality(c1)); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx new file mode 100644 index 00000000000..6b2e5fd2aa7 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -0,0 +1,1278 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonMcTask.cxx +/// \brief Analysis task to obtain the invariant mass distributions for different MC truth classes +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + +#include "Common/DataModel/Centrality.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; +using namespace o2::aod::pwgem::photonmeson::utils::mcutil; + +static constexpr int kMaxChainLength = 16; +using Chain = std::array; // filled with -1 after the last ancestor + +/// \brief MC truth of a single photon candidate, ordered from best to worst such that std::max gives the worse flag of a pair +enum class PhotonTruth : uint8_t { + kTruePhoton = 0, // PCM: both legs from the same photon, EMC: leading contributor is a photon (or e+-, converted in front of the EMCal) + kNonPhoton, // PCM: both legs have the same direct mother which is not a photon (K0S, Lambda, pi0 Dalitz), EMC: leading contributor is not a photon + kFake, // PCM: leg without MC label or legs without common direct mother, EMC: cluster without MC label + kNTruth +}; + +/// \brief Class of the closest common ancestor of the two photon candidates of a pair +enum class PairBucket : uint8_t { + kNone = 0, // no common ancestor + kSameParticle, // one particle seen twice: the common ancestor is one of the two candidates themselves (EMC conversion split, Dalitz), + // or a photon/electron whose electromagnetic shower made both candidates (e.g. bremsstrahlung of both conversion legs) + kSignalPi0, // pi0 is the direct mother of both + kSignalEta, // eta is the direct mother of both + kCrossPi0, // pi0 further up in the chain: the pi0 has no hadronic decays, so these are pi0 decays with an EM interaction in between (conversion, bremsstrahlung, shower in front of the EMCal) + kCrossEta, // eta further up in the chain: hadronic decays (eta -> 3 pi0, photons of different pi0) and eta decays with an EM interaction in between + kCrossOmega, // omega + kCrossK0S, // K0S + kCrossEtaPrime, // eta' + kCrossOtherHadron, // any other hadron (including the rare Sigma0) + kPartonString, // quark, gluon, diquark or string/cluster: jet-like + kNBuckets +}; + +/// \brief What the leading contributor of the EMC cluster is in a PCM-EMC same particle pair +enum class SameParticleOrigin : uint8_t { + kLegElectron = 0, // the e+- that was reconstructed as one of the V0 legs, should have been removed by track matching + kBremsFromLeg, // a photon radiated by one of the V0 leg e+-, cannot be removed by track matching + kOtherElectron, // any other e+- + kOther, // anything else + kNOrigins +}; + +/// \brief Why the V0 leg did not veto the EMC cluster, from the tracks matched to the cluster +enum class LegMatchStatus : uint8_t { + kNoMatchStored = 0, // no matched track stored for this cluster at all + kNoCompatibleMatch, // matched tracks stored, but none with the momentum of the leg + kOutsideWindow, // leg found, but outside the track matching window of the analysis + kInsideWindowEoverP, // leg found inside the window, but E/p above the cut (primaries only) + kInsideWindow, // leg found inside the window: the cluster should have been vetoed + kNStatus +}; + +/// \brief Species of the closest common ancestor of an "other hadron" pair +enum class OtherHadronSpecies : uint8_t { + kChargedPion = 0, + kChargedKaon, + kK0L, + kRho, + kKstar, + kPhi, + kA1, // a1(1260) + kExcitedK, // K1(1270), K1(1400), K0*(1430), K2*(1430) + kOpenCharm, // D, D*, Ds, ... (one c quark) + kCharmonium, // J/psi, psi(2S), chi_c, ... + kOpenBeauty, // B, B*, Bs, Bc, ... (one b quark) + kBottomonium, // Upsilon, chi_b, ... + kOtherMeson, // incl. f0, f2, b, h resonances + kProton, + kNeutron, + kDelta, + kLambda, + kSigma, + kXi, + kOtherBaryon, + kOther, + kNSpecies +}; + +/// \brief How the particles directly below the common ancestor were produced, i.e. how the ancestor split into the two branches. +/// Ordered such that std::max of the two branches gives the material interaction if one branch comes from it. +enum class SplitProduction : uint8_t { + kGeneratorDecay = 0, // produced by the event generator: decay in the generator (resonances, Sigma0, ...) + kTransportDecay, // produced by the transport code in a decay (weak decays of K+-, K0L, Lambda, Xi, ...) + kHadronicInteraction, // produced by the transport code in a hadronic interaction in the detector material + kOtherProcess, // produced by the transport code in any other process + kNProduction +}; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +struct TwoPhotonMcTask { + // configurable axis + ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {400, 0.0, 0.8}, "invariant mass axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {100, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisERelative{"thnConfigAxisERelative", {600, -1., 5.}, "(E rec - E true) / E true axis"}; + ConfigurableAxis thnConfigAxisPRelative{"thnConfigAxisPRelative", {300, -1., 2.}, "(P rec - P true) / P true axis"}; + ConfigurableAxis thnConfigAxisEtaRelative{"thnConfigAxisEtaRelative", {300, -1., 2.}, "(eta rec - eta true) / eta true axis"}; + ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {10, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "mesonConfig"; + Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } mesonConfig; + + // same mixing as taskPi0FlowEMC, but without event plane bins: the derived MC has no Q vectors + struct : ConfigurableGroup { + std::string prefix = "mixingConfig"; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgCentBins{"cfgCentBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Mixing bins - centrality"}; + Configurable cfgMixingDepth{"cfgMixingDepth", 2, "Mixing depth"}; + Configurable cfgCorrectMixedVtxEta{"cfgCorrectMixedVtxEta", true, "EMC-EMC: re-project the eta of cluster 2 onto the vertex of collision 1 in the mixed event"}; + Configurable cfgEmcalEffRadius{"cfgEmcalEffRadius", 430.f, "effective EMCal radius (cm) used for the mixed event vertex correction"}; + } mixingConfig; + + using BinningType = ColumnBinningPolicy; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + using PcmPhotons = soa::Join; + + using PcmMcLegs = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + int mRunNumber{-1}; + float dBz{0.f}; + + static constexpr int PhotonPCM = 0; + static constexpr int PhotonEMC = 1; + static constexpr int PairPCMPCM = 0; + static constexpr int PairEMCEMC = 1; + static constexpr int PairPCMEMC = 2; + + // ancestor chains and truth flags, filled once per DF, index = globalIndex of the V0 photon / cluster + std::vector mChainsPCM; + std::vector mChainsEMC; + std::vector mTruthPCM; + std::vector mTruthEMC; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + + const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPGen{thnConfigAxisPt, "#it{p}_{Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPRec{thnConfigAxisPt, "#it{p}_{Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPRelative{thnConfigAxisPRelative, "#it{p}_{Rec} - #it{p}_{Gen} / #it{p}_{Gen}"}; + const AxisSpec thnAxisEGen{thnConfigAxisPt, "#it{E}_{Gen} (GeV)"}; + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisERelative{thnConfigAxisERelative, "#it{E}_{Rec} - #it{E}_{Gen} / #it{E}_{Gen}"}; + const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + + const AxisSpec thnAxisEtaRelative{thnConfigAxisEtaRelative, "#it{#eta}_{Rec} - #it{#eta}_{Gen} / #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiRelative{thnConfigAxisPhiRelative, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen} / #it{#varphi}_{Gen}"}; + + const AxisSpec thnAxisEtaGen{280, -0.7, 0.7, "#it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiGen{360, 0., o2::constants::math::TwoPI, "#it{#varphi}_{Gen} (rad)"}; + + const AxisSpec thnAxisPairTruth{static_cast(PhotonTruth::kNTruth), -0.5, static_cast(PhotonTruth::kNTruth) - 0.5, "pair truth (worse photon)"}; + const AxisSpec thnAxisPairBucket{static_cast(PairBucket::kNBuckets), -0.5, static_cast(PairBucket::kNBuckets) - 0.5, "closest common ancestor"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + auto hPairAncestor = registry.add("PCM/hPairAncestor", "minv vs pT vs cent vs pair truth vs closest common ancestor", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth, thnAxisPairBucket}); + static constexpr std::array(PhotonTruth::kNTruth)> TruthLabels = {"true #gamma", "non #gamma", "fake"}; + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hPairAncestor->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + static constexpr std::array(PairBucket::kNBuckets)> BucketLabels = {"none", "same particle", "#pi^{0} direct", "#eta direct", "#pi^{0} cross", "#eta cross", "#omega", "K^{0}_{S}", "#eta'", "other hadron", "parton/string"}; + for (std::size_t i = 0; i < BucketLabels.size(); ++i) { + hPairAncestor->GetAxis(4)->SetBinLabel(static_cast(i) + 1, BucketLabels[i].data()); + } + // "other hadron" pairs: which hadron is the common ancestor, and did it split in a decay or a material interaction + const AxisSpec axisOHMass{160, 0., 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisOHPt{20, 0., 20., "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisOHSpecies{static_cast(OtherHadronSpecies::kNSpecies), -0.5, static_cast(OtherHadronSpecies::kNSpecies) - 0.5, "common ancestor"}; + const AxisSpec axisOHProduction{static_cast(SplitProduction::kNProduction), -0.5, static_cast(SplitProduction::kNProduction) - 0.5, "split of the common ancestor"}; + auto hOtherHadron = registry.add("PCM/OtherHadron/hAncestor", "other hadron pairs: minv vs pT vs ancestor species vs split production vs pair truth", HistType::kTHnF, {axisOHMass, axisOHPt, axisOHSpecies, axisOHProduction, thnAxisPairTruth}); + static constexpr std::array(OtherHadronSpecies::kNSpecies)> SpeciesLabels = {"#pi^{#pm}", "K^{#pm}", "K^{0}_{L}", "#rho", "K^{*}", "#phi", "a_{1}", "K_{1}, K^{*}_{0,2}", "open charm", "c#bar{c}", "open beauty", "b#bar{b}", "other meson", "p", "n", "#Delta", "#Lambda", "#Sigma", "#Xi", "other baryon", "other"}; + for (std::size_t i = 0; i < SpeciesLabels.size(); ++i) { + hOtherHadron->GetAxis(2)->SetBinLabel(static_cast(i) + 1, SpeciesLabels[i].data()); + } + static constexpr std::array(SplitProduction::kNProduction)> ProductionLabels = {"generator decay", "transport decay", "hadronic interaction", "other process"}; + for (std::size_t i = 0; i < ProductionLabels.size(); ++i) { + hOtherHadron->GetAxis(3)->SetBinLabel(static_cast(i) + 1, ProductionLabels[i].data()); + } + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hOtherHadron->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.add("PCM/OtherHadron/hAncestorPdg", "other hadron pairs: |PDG| of the common ancestor;|PDG| (last bin: #geq 9999);pairs", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + + // mixed event, same axes as hPairAncestor without the bucket (pairs from different events share no ancestor) + auto hMixed = registry.add("PCM/hMixed", "minv vs pT vs cent vs pair truth, mixed event", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth}); + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hMixed->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.addClone("PCM/", "EMC/"); + registry.addClone("PCM/", "PCMEMC/"); + + // ancestor chain QA + registry.add("QA/PCM/hChainLength", "length of the ancestor chain;chain length;counts", HistType::kTH1D, {{kMaxChainLength, 0.5, kMaxChainLength + 0.5}}); + auto hChainEnd = registry.add("QA/PCM/hChainEnd", "reason the ancestor chain ended;;counts", HistType::kTH1D, {{4, -0.5, 3.5}}); + hChainEnd->GetXaxis()->SetBinLabel(1, "parton-like"); + hChainEnd->GetXaxis()->SetBinLabel(2, "beam nucleus"); + hChainEnd->GetXaxis()->SetBinLabel(3, "no mothers"); + hChainEnd->GetXaxis()->SetBinLabel(4, "truncated"); + auto hChainEndKind = registry.add("QA/PCM/hChainEndKind", "kind of the last chain element;;counts", HistType::kTH1D, {{static_cast(AncestorKind::NClasses), -0.5, static_cast(AncestorKind::NClasses) - 0.5}}); + static constexpr std::array(AncestorKind::NClasses)> KindLabels = {"none", "string/parton", "beam/nucleus", "#pi^{0}", "#eta", "other meson", "baryon", "#gamma", "e^{#pm}", "other"}; + for (std::size_t i = 0; i < KindLabels.size(); ++i) { + hChainEndKind->GetXaxis()->SetBinLabel(static_cast(i) + 1, KindLabels[i].data()); + } + registry.add("QA/PCM/hChainEndPdg", "|PDG| of the last chain element;|PDG| (last bin: #geq 9999, incl. nuclei);counts", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + registry.addClone("QA/PCM/", "QA/EMC/"); + + // origin of PCM-EMC same particle pairs and why track matching did not remove them + if (doprocessPcmEmcal.value) { + const AxisSpec axisOrigin{static_cast(SameParticleOrigin::kNOrigins), -0.5, static_cast(SameParticleOrigin::kNOrigins) - 0.5, "leading contributor of the cluster"}; + const AxisSpec axisStatus{static_cast(LegMatchStatus::kNStatus), -0.5, static_cast(LegMatchStatus::kNStatus) - 0.5, "V0 leg among the matched tracks"}; + const AxisSpec axisPtLeg{40, 0., 20., "#it{p}_{T,leg} (GeV/#it{c})"}; + const AxisSpec axisDeltaEta{100, -0.1, 0.1, "#Delta#eta (cluster - track)"}; + const AxisSpec axisDeltaPhi{100, -0.1, 0.1, "#Delta#varphi (cluster - track)"}; + const AxisSpec axisDp{200, -1., 1., "(#it{p}_{match} - #it{p}_{leg}) / #it{p}_{leg}"}; + static constexpr std::array(SameParticleOrigin::kNOrigins)> OriginLabels = {"leg e^{#pm}", "brems #gamma of leg", "other e^{#pm}", "other"}; + static constexpr std::array(LegMatchStatus::kNStatus)> StatusLabels = {"no match stored", "no match with leg #it{p}", "outside window", "in window, #it{E}/#it{p} #geq cut", "in window"}; + + auto hOrigin = registry.add("PCMEMC/SameParticle/hOrigin", "same particle pairs: leading contributor of the cluster", HistType::kTH3F, {thnAxisInvMass, thnAxisPtRec, axisOrigin}); + for (std::size_t i = 0; i < OriginLabels.size(); ++i) { + hOrigin->GetZaxis()->SetBinLabel(static_cast(i) + 1, OriginLabels[i].data()); + } + registry.add("PCMEMC/SameParticle/hLegEoverP", "cluster from a V0 leg: #it{E}_{cls}/#it{p}_{leg}", HistType::kTH2F, {{200, 0., 4., "#it{E}_{cls}/#it{p}_{leg}"}, axisPtLeg}); + for (const auto& [type, title] : {std::pair{"Prim", "primary"}, {"Sec", "secondary"}}) { + auto hStatus = registry.add(std::format("PCMEMC/SameParticle/hLeg{}Status", type.data()).data(), std::format("cluster from a V0 leg: leg among the matched {} tracks", title.data()).data(), HistType::kTH2F, {axisStatus, axisPtLeg}); + for (std::size_t i = 0; i < StatusLabels.size(); ++i) { + hStatus->GetXaxis()->SetBinLabel(static_cast(i) + 1, StatusLabels[i].data()); + } + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Dp", type.data()).data(), std::format("cluster from a V0 leg: closest {} match in #it{{p}}", title.data()).data(), HistType::kTH1F, {axisDp}); + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Residual", type.data()).data(), std::format("cluster from a V0 leg: residual of the {} match", title.data()).data(), HistType::kTH3F, {axisDeltaEta, axisDeltaPhi, axisPtLeg}); + } + } + + auto hMesonCuts = registry.add("hMesonCuts", "hMesonCuts;;Counts", kTH1D, {{6, 0.5, 6.5}}, false); + hMesonCuts->GetXaxis()->SetBinLabel(1, "in"); + hMesonCuts->GetXaxis()->SetBinLabel(2, "opening angle"); + hMesonCuts->GetXaxis()->SetBinLabel(3, "#it{M}_{#gamma#gamma}"); + hMesonCuts->GetXaxis()->SetBinLabel(4, "#it{p}_{T}"); + hMesonCuts->GetXaxis()->SetBinLabel(5, "conversion cut"); + hMesonCuts->GetXaxis()->SetBinLabel(6, "out"); + if (mesonConfig.cfgEnableQA.value) { + registry.add("mesonQA/hInvMassPt", "Histo for inv pair mass vs pt", HistType::kTH2D, {thnAxisInvMass, thnAxisPtRec}); + } + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; + fV0PhotonCut.SetD_Bz(dBz); + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + // occupancy selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); + registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted + registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + } + return true; + } + + /// \brief Reset the per DF cache of one photon type + /// \param chains ancestor chains, set to -1 (not {}, which gives 0, a valid index) + /// \param truth truth flags, set to kFake + /// \param size size of the full photon table + static void resetCache(std::vector& chains, std::vector& truth, const std::size_t size) + { + Chain empty; + empty.fill(-1); + chains.assign(size, empty); + truth.assign(size, PhotonTruth::kFake); + } + + /// \brief Fill the ancestor chain QA + /// \param mcIter MC particle iterator, has to sit where getAncestorChain left it (last chain element or the nucleus that ended the chain) + /// \param length length of the chain as returned by getAncestorChain + template + void fillChainQA(TMCIter const& mcIter, const int length) + { + static constexpr std::array HistLength = {"QA/PCM/hChainLength", "QA/EMC/hChainLength"}; + static constexpr std::array HistEnd = {"QA/PCM/hChainEnd", "QA/EMC/hChainEnd"}; + static constexpr std::array HistEndKind = {"QA/PCM/hChainEndKind", "QA/EMC/hChainEndKind"}; + static constexpr std::array HistEndPdg = {"QA/PCM/hChainEndPdg", "QA/EMC/hChainEndPdg"}; + if (length <= 0) { + return; + } + static constexpr int MaxPdgBin = 9999; // nuclei and other large codes go to the last bin + const int lastPdg = mcIter.pdgCode(); + int endReason = 3; // chain is full without reaching a parton, a nucleus or a particle without mothers + if (isPartonLike(lastPdg)) { + endReason = 0; + } else if (isNucleus(lastPdg)) { + endReason = 1; + } else if (!mcIter.has_mothers()) { + endReason = 2; + } + registry.fill(HIST(HistLength[photonType]), length); + registry.fill(HIST(HistEnd[photonType]), endReason); + registry.fill(HIST(HistEndKind[photonType]), static_cast(ancestorKindOfPdg(lastPdg))); + registry.fill(HIST(HistEndPdg[photonType]), std::min(std::abs(lastPdg), MaxPdgBin)); + } + + /// \brief Fill ancestor chains and truth flags of all EMC clusters of the DF + /// \param clusters full (unfiltered) cluster table + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillEmcCache(TClusters const& clusters, TMCIter& mcIter, const bool doChainQA = true) + { + for (const auto& cluster : clusters) { + const auto mcIds = cluster.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + continue; // noise cluster without MC label: kFake, chain stays -1 + } + auto& chain = mChainsEMC[cluster.globalIndex()]; + const int length = getAncestorChain(mcIter, mcIds[0], chain); // leading contributor + if (doChainQA) { + fillChainQA(mcIter, length); + } + if (length <= 0) { + continue; // leading contributor is a nucleus: kFake, chain stays -1 + } + + // e+- whose mother is a photon (conversion in front of the EMCal): start the chain at the photon, + // otherwise the direct mother check of the pair classification fails for converted photons + mcIter.setCursor(chain[0]); + if (length > 1 && std::abs(mcIter.pdgCode()) == PDG_t::kElectron) { + mcIter.setCursor(chain[1]); + if (mcIter.pdgCode() == PDG_t::kGamma) { + std::shift_left(chain.begin(), chain.end(), 1); + chain.back() = -1; + } + } + mcIter.setCursor(chain[0]); + mTruthEMC[cluster.globalIndex()] = mcIter.pdgCode() == PDG_t::kGamma ? PhotonTruth::kTruePhoton : PhotonTruth::kNonPhoton; + } + } + + /// \brief Fill ancestor chains and truth flags of all V0 photons of the DF + /// \param photons full (unfiltered) V0 photon table + /// \param legs full V0 leg table with MC labels + /// \param mcParticles full MC particle table the leg labels point into + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillPcmCache(TPhotons const& photons, TLegs const& legs, TMCParticles const& mcParticles, const bool doChainQA = true) + { + auto mcIter = mcParticles.begin(); + auto posLeg = legs.begin(); + auto negLeg = legs.begin(); + auto posMc = mcParticles.begin(); + auto negMc = mcParticles.begin(); + + Chain chainPos{}; + Chain chainNeg{}; + + for (const auto& photon : photons) { + posLeg.setCursor(photon.posTrackId()); + negLeg.setCursor(photon.negTrackId()); + if (posLeg.emmcparticleId() < 0 || negLeg.emmcparticleId() < 0) { + continue; // leg without MC label: kFake, chain stays -1 + } + posMc.setCursor(posLeg.emmcparticleId()); + negMc.setCursor(negLeg.emmcparticleId()); + + auto& chain = mChainsPCM[photon.globalIndex()]; + auto& truth = mTruthPCM[photon.globalIndex()]; + const int photonId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posMc, negMc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + if (photonId >= 0) { + truth = PhotonTruth::kTruePhoton; + const int length = getAncestorChain(mcIter, photonId, chain); + if (doChainQA) { + fillChainQA(mcIter, length); + } + continue; + } + + // not a true conversion: K0S/Lambda V0, Dalitz pair or combinatorial fake + const int lengthPos = getAncestorChain(mcIter, posLeg.emmcparticleId(), chainPos); + if (doChainQA) { + fillChainQA(mcIter, lengthPos); // the kept chain is a tail of this one, so it ends the same way + } + getAncestorChain(mcIter, negLeg.emmcparticleId(), chainNeg); + const int ancestor = getFirstCommonAncestor(chainPos, chainNeg); + if (ancestor < 0) { + continue; // no common ancestor: kFake, chain stays -1 + } + const bool isDirectMother = chainPos[1] == ancestor && chainNeg[1] == ancestor; + truth = isDirectMother ? PhotonTruth::kNonPhoton : PhotonTruth::kFake; + const auto start = std::ranges::find(chainPos, ancestor); + std::ranges::fill(std::copy(start, chainPos.end(), chain.begin()), chain.end(), -1); + } + } + + /// \brief Classify a pair by the closest common ancestor of the two candidates + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + PairBucket classifyPair(Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + const int ancestor = getFirstCommonAncestor(chain1, chain2); + if (ancestor < 0) { + return PairBucket::kNone; + } + if (ancestor == chain1[0] || ancestor == chain2[0]) { + return PairBucket::kSameParticle; + } + mcIter.setCursor(ancestor); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + return PairBucket::kSameParticle; // both candidates come from the electromagnetic shower of one photon or electron + } + const bool isDirectMother = chain1[1] == ancestor && chain2[1] == ancestor; + if (isDirectMother && absPdg == PDG_t::kPi0) { + return PairBucket::kSignalPi0; + } + if (isDirectMother && absPdg == o2::constants::physics::Pdg::kEta) { + return PairBucket::kSignalEta; + } + switch (absPdg) { + case PDG_t::kPi0: + return PairBucket::kCrossPi0; + case o2::constants::physics::Pdg::kEta: + return PairBucket::kCrossEta; + case o2::constants::physics::Pdg::kOmega: + return PairBucket::kCrossOmega; + case PDG_t::kK0Short: + return PairBucket::kCrossK0S; + case o2::constants::physics::Pdg::kEtaPrime: + return PairBucket::kCrossEtaPrime; + default: + break; + } + if (isPartonLike(absPdg)) { + return PairBucket::kPartonString; + } + return PairBucket::kCrossOtherHadron; + } + + /// \brief Look for the V0 leg among the tracks matched to an EMC cluster and classify why it did not veto the cluster. + /// The matched track tables store no track index, so the leg is identified by its momentum. + /// \tparam isSecondary matches are secondaries (V0 legs, no E/p condition) instead of primaries + /// \param matches tracks matched to this cluster + /// \param eCluster cluster energy + /// \param pLeg momentum of the V0 leg + /// \param ptLeg transverse momentum of the V0 leg + template + void fillLegMatch(TMatches const& matches, const float eCluster, const float pLeg, const float ptLeg) + { + static constexpr std::array HistStatus = {"PCMEMC/SameParticle/hLegPrimStatus", "PCMEMC/SameParticle/hLegSecStatus"}; + static constexpr std::array HistDp = {"PCMEMC/SameParticle/hLegPrimDp", "PCMEMC/SameParticle/hLegSecDp"}; + static constexpr std::array HistResidual = {"PCMEMC/SameParticle/hLegPrimResidual", "PCMEMC/SameParticle/hLegSecResidual"}; + static constexpr float MaxRelDp = 0.1f; // max |p_match - p_leg| / p_leg to identify the match as the leg + + LegMatchStatus status = LegMatchStatus::kNoMatchStored; + if (matches.size() > 0) { + float bestDp = 1e9f; + float dEta = 0.f, dPhi = 0.f, trackPt = 0.f, trackP = 0.f; + for (const auto& match : matches) { + const float dp = (match.trackP() - pLeg) / pLeg; + if (std::fabs(dp) < std::fabs(bestDp)) { + bestDp = dp; + dEta = match.deltaEta(); + dPhi = match.deltaPhi(); + trackPt = match.trackPt(); + trackP = match.trackP(); + } + } + registry.fill(HIST(HistDp[isSecondary]), bestDp); + if (std::fabs(bestDp) > MaxRelDp) { + status = LegMatchStatus::kNoCompatibleMatch; + } else { + registry.fill(HIST(HistResidual[isSecondary]), dEta, dPhi, ptLeg); + // same windows as EMCPhotonCut::checkTrackMatching + bool isInWindow = false; + if constexpr (isSecondary) { + isInWindow = std::fabs(dEta) <= fEMCCut.GetSecTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetSecTrackMatchingPhi(trackPt); + } else { + isInWindow = std::fabs(dEta) <= fEMCCut.GetTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetTrackMatchingPhi(trackPt); + } + if (!isInWindow) { + status = LegMatchStatus::kOutsideWindow; + } else if (!isSecondary && eCluster / trackP >= emccuts.cfgEMCEoverp) { + status = LegMatchStatus::kInsideWindowEoverP; + } else { + status = LegMatchStatus::kInsideWindow; + } + } + } + registry.fill(HIST(HistStatus[isSecondary]), static_cast(status), ptLeg); + } + + /// \brief Find out where a PCM-EMC same particle pair comes from: what made the EMC cluster and why track matching did not remove it + /// \param pcm V0 photon + /// \param emc EMC cluster + /// \param legs full V0 leg table with MC labels + /// \param matchedPrims full table of primary tracks matched to clusters + /// \param matchedSeconds full table of secondary tracks (V0 legs) matched to clusters + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseSameParticle(TPCM const& pcm, TEMC const& emc, TLegs const& legs, TMatchedPrims const& matchedPrims, TMatchedSecs const& matchedSeconds, TMCIter& mcIter) + { + const auto mcIds = emc.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + return; + } + const int lead = mcIds[0]; // leading contributor, before the conversion shift of the chain + const auto posLeg = legs.iteratorAt(pcm.posTrackId()); + const auto negLeg = legs.iteratorAt(pcm.negTrackId()); + const std::array legMcIds = {posLeg.emmcparticleId(), negLeg.emmcparticleId()}; + + SameParticleOrigin origin = SameParticleOrigin::kOther; + int legIndex = -1; + if (lead == legMcIds[0] || lead == legMcIds[1]) { + origin = SameParticleOrigin::kLegElectron; + legIndex = lead == legMcIds[0] ? 0 : 1; + } else { + mcIter.setCursor(lead); + const int absPdg = std::abs(mcIter.pdgCode()); + const int motherId = mcIter.has_mothers() ? mcIter.mothersIds()[0] : -1; + if (absPdg == PDG_t::kGamma && motherId >= 0 && (motherId == legMcIds[0] || motherId == legMcIds[1])) { + origin = SameParticleOrigin::kBremsFromLeg; + } else if (absPdg == PDG_t::kElectron) { + origin = SameParticleOrigin::kOtherElectron; + } + } + + ROOT::Math::PtEtaPhiMVector v1(pcm.pt(), pcm.eta(), pcm.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(emc.pt(), emc.eta(), emc.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + registry.fill(HIST("PCMEMC/SameParticle/hOrigin"), vMeson.M(), vMeson.Pt(), static_cast(origin)); + if (origin != SameParticleOrigin::kLegElectron) { + return; + } + + const auto& leg = legIndex == 0 ? posLeg : negLeg; + const float pLeg = std::hypot(leg.px(), leg.py(), leg.pz()); + const float ptLeg = std::hypot(leg.px(), leg.py()); + if (pLeg <= 0.f) { + return; + } + registry.fill(HIST("PCMEMC/SameParticle/hLegEoverP"), emc.e() / pLeg, ptLeg); + fillLegMatch(matchedPrims.sliceBy(perEMCClusterMT, emc.globalIndex()), emc.e(), pLeg, ptLeg); + fillLegMatch(matchedSeconds.sliceBy(perEMCClusterMS, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } + + /// \brief Species class of the common ancestor of an "other hadron" pair + /// \param pdg PDG code of the ancestor + static OtherHadronSpecies otherHadronSpecies(const int pdg) + { + const int absPdg = std::abs(pdg); + switch (absPdg) { + case PDG_t::kPiPlus: + return OtherHadronSpecies::kChargedPion; + case PDG_t::kKPlus: + return OtherHadronSpecies::kChargedKaon; + case PDG_t::kK0Long: + return OtherHadronSpecies::kK0L; + case PDG_t::kRho770_0: + case PDG_t::kRho770Plus: + return OtherHadronSpecies::kRho; + case o2::constants::physics::Pdg::kK0Star892: + case o2::constants::physics::Pdg::kKPlusStar892: + return OtherHadronSpecies::kKstar; + case o2::constants::physics::Pdg::kPhi: + return OtherHadronSpecies::kPhi; + case PDG_t::kProton: + return OtherHadronSpecies::kProton; + case PDG_t::kNeutron: + return OtherHadronSpecies::kNeutron; + case 1114: // o2-linter: disable=pdg/explicit-code (Delta-, no named constant) + case 2114: // o2-linter: disable=pdg/explicit-code (Delta0, no named constant) + case 2214: // o2-linter: disable=pdg/explicit-code (Delta+, no named constant) + case 2224: // o2-linter: disable=pdg/explicit-code (Delta++, no named constant) + return OtherHadronSpecies::kDelta; + case PDG_t::kLambda0: + return OtherHadronSpecies::kLambda; + case PDG_t::kSigmaMinus: + case PDG_t::kSigma0: + case PDG_t::kSigmaPlus: + return OtherHadronSpecies::kSigma; + case PDG_t::kXiMinus: + case 3322: // o2-linter: disable=pdg/explicit-code (Xi0, no named constant) + return OtherHadronSpecies::kXi; + default: + break; + } + constexpr int MinHadronPdg = 100; + constexpr int MaxHadronPdg = 10000000; + if (absPdg < MinHadronPdg || absPdg >= MaxHadronPdg) { + return OtherHadronSpecies::kOther; + } + // PDG numbering: mesons have no third quark, i.e. the thousands digit is 0 (also for 9000000 type resonances) + if ((absPdg / 1000) % 10 != 0) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kOtherBaryon; + } + // mesons: hundreds digit = heavier quark, tens digit = lighter quark, last digit = 2J+1 + // (also covers excited states with a radial/orbital prefix, e.g. 10441, 20443, 100553) + const int heavyQuark = (absPdg / 100) % 10; // o2-linter: disable=magic-number (PDG digit) + const int lightQuark = (absPdg / 10) % 10; // o2-linter: disable=magic-number (PDG digit) + constexpr int Strange = 3, Charm = 4, Beauty = 5; + constexpr int ExcitedPrefix = 10000; + constexpr int SpinTwo = 5; // 2J+1 for J = 2 + if (heavyQuark == Charm) { + return lightQuark == Charm ? OtherHadronSpecies::kCharmonium : OtherHadronSpecies::kOpenCharm; + } + if (heavyQuark == Beauty) { + return lightQuark == Beauty ? OtherHadronSpecies::kBottomonium : OtherHadronSpecies::kOpenBeauty; + } + if (absPdg == 20213 || absPdg == 20113) { // o2-linter: disable=pdg/explicit-code (a1(1260), no named constant) + return OtherHadronSpecies::kA1; + } + if (heavyQuark == Strange && lightQuark < Strange && (absPdg >= ExcitedPrefix || absPdg % 10 == SpinTwo)) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kExcitedK; + } + return OtherHadronSpecies::kOtherMeson; + } + + /// \brief How an MC particle was produced + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param id index of the particle + template + static SplitProduction productionOf(TMCIter& mcIter, const int id) + { + mcIter.setCursor(id); + if (mcIter.producedByGenerator()) { + return SplitProduction::kGeneratorDecay; + } + switch (mcIter.getProcess()) { + case TMCProcess::kPDecay: + return SplitProduction::kTransportDecay; + case TMCProcess::kPHadronic: + case TMCProcess::kPHInhelastic: + case TMCProcess::kPNuclearAbsorption: + case TMCProcess::kPNCapture: + case TMCProcess::kPPbarAnnihilation: + case TMCProcess::kPNbarAnnihilation: + case TMCProcess::kPEvaporation: + case TMCProcess::kPNuclearFission: + case TMCProcess::kPPhotoNuclear: + case TMCProcess::kPPhotonInhelastic: + return SplitProduction::kHadronicInteraction; + default: + return SplitProduction::kOtherProcess; + } + } + + /// \brief Fill which hadron is the common ancestor of an "other hadron" pair and how it split into the two branches + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param pairTruth truth flag of the pair + /// \param vMeson pair four vector + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseOtherHadron(Chain const& chain1, Chain const& chain2, const PhotonTruth pairTruth, ROOT::Math::PtEtaPhiMVector const& vMeson, TMCIter& mcIter) + { + static constexpr std::array HistAncestor = {"PCM/OtherHadron/hAncestor", "EMC/OtherHadron/hAncestor", "PCMEMC/OtherHadron/hAncestor"}; + static constexpr std::array HistAncestorPdg = {"PCM/OtherHadron/hAncestorPdg", "EMC/OtherHadron/hAncestorPdg", "PCMEMC/OtherHadron/hAncestorPdg"}; + static constexpr int MaxPdgBin = 9999; + + const int ancestor = getFirstCommonAncestor(chain1, chain2); + const auto pos1 = std::distance(chain1.begin(), std::ranges::find(chain1, ancestor)); + const auto pos2 = std::distance(chain2.begin(), std::ranges::find(chain2, ancestor)); + if (ancestor < 0 || pos1 < 1 || pos2 < 1 || pos1 >= std::ssize(chain1) || pos2 >= std::ssize(chain2)) { + return; // not possible for "other hadron" (the ancestor is neither of the candidates), protection only + } + // the particle directly below the ancestor in each branch tells how the ancestor split + const SplitProduction production = std::max(productionOf(mcIter, chain1[pos1 - 1]), productionOf(mcIter, chain2[pos2 - 1])); + mcIter.setCursor(ancestor); + const int pdg = mcIter.pdgCode(); + registry.fill(HIST(HistAncestor[pairType]), vMeson.M(), vMeson.Pt(), static_cast(otherHadronSpecies(pdg)), static_cast(production), static_cast(pairTruth)); + registry.fill(HIST(HistAncestorPdg[pairType]), std::min(std::abs(pdg), MaxPdgBin)); + } + + /// \brief Apply the pair cuts and fill the pair ancestor histogram + /// \return bucket of the pair, std::nullopt if the pair was rejected + template + std::optional fillPair(TPhoton1 const& g1, TPhoton2 const& g2, Chain const& chain1, Chain const& chain2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult, TMCIter& mcIter) + { + static constexpr std::array HistPairAncestor = {"PCM/hPairAncestor", "EMC/hPairAncestor", "PCMEMC/hPairAncestor"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + + registry.fill(HIST("hMesonCuts"), 1); + if (openingAngle <= mesonConfig.minOpenAngle) { + registry.fill(HIST("hMesonCuts"), 2); + return std::nullopt; + } + registry.fill(HIST("hMesonCuts"), 6); + + const PhotonTruth pairTruth = std::max(truth1, truth2); + const PairBucket bucket = classifyPair(chain1, chain2, mcIter); + registry.fill(HIST(HistPairAncestor[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(pairTruth), static_cast(bucket)); + if (bucket == PairBucket::kCrossOtherHadron) { + diagnoseOtherHadron(chain1, chain2, pairTruth, vMeson, mcIter); + } + if (mesonConfig.cfgEnableQA.value) { + registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); + } + return bucket; + } + + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, same as in taskPi0FlowEMC + /// \param etaOld old eta value + /// \param vzOld old primary vertex z position + /// \param vzNew new primary vertex z position + /// \param emcalR radius of the EMCal + static float correctEtaForVertexShift(float etaOld, float vzOld, float vzNew, float emcalR) + { + return std::asinh(std::sinh(etaOld) + (vzOld - vzNew) / emcalR); + } + + /// \brief Apply the pair cuts to a mixed event pair and fill the mixed event histogram + /// \param eta2 eta of the second photon, possibly re-projected onto the vertex of the first collision + template + void fillMixedPair(TPhoton1 const& g1, TPhoton2 const& g2, const float eta2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult) + { + static constexpr std::array HistMixed = {"PCM/hMixed", "EMC/hMixed", "PCMEMC/hMixed"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), eta2, g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return; + } + registry.fill(HIST(HistMixed[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(std::max(truth1, truth2))); + } + + // PCM-PCM same event + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + auto mcIter = mcParticles.begin(); + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsPCM[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult, mcIter); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcm, "Process for pcm photons", false); + + // EMCal-EMCal same event + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + fillPair(g1, g2, mChainsEMC[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, mcIter); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcal, "Process for emcal photons", false); + + // PCM-EMCal same event + void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles); + fillEmcCache(clusters, mcIter); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + // PCM - EMC loop, two different tables: all combinations + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCMPerCollision, photonsEMCPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + const auto bucket = fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, mcIter); + if (bucket == PairBucket::kSameParticle) { + diagnoseSameParticle(g1, g2, legs, matchedPrims, matchedSeconds, mcIter); + } + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcal, "Process for pcm and emcal photons", true); + + // PCM-PCM mixed event + void processPcmMixed(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles, false); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto photonTuple = std::make_tuple(photons); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, photonTuple, &cache}; + + for (const auto& [c1, photons1, c2, photons2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photons1, photons2))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmMixed, "Process for pcm photons, mixed event", false); + + // EMCal-EMCal mixed event + void processEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto clusterTuple = std::make_tuple(clusters); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clusterTuple, &cache}; + + for (const auto& [c1, clusters1, c2, clusters2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(clusters1, clusters2))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + // cluster 2 seen from the vertex of collision 1, as in taskPi0FlowEMC + const float eta2 = mixingConfig.cfgCorrectMixedVtxEta.value ? correctEtaForVertexShift(g2.eta(), c2.posZ(), c1.posZ(), mixingConfig.cfgEmcalEffRadius.value) : g2.eta(); + fillMixedPair(g1, g2, eta2, mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalMixed, "Process for emcal photons, mixed event", false); + + // PCM-EMCal mixed event + void processPcmEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles, false); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto associatedTables = std::make_tuple(photons, clusters); + Pair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, associatedTables, &cache}; + + for (const auto& [c1, photonsPCM, c2, photonsEMC] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCM, photonsEMC))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcalMixed, "Process for pcm and emcal photons, mixed event", false); + +}; // End struct TwoPhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Utils/MCUtilities.h b/PWGEM/PhotonMeson/Utils/MCUtilities.h index 270d6d2ead9..1326f513aef 100644 --- a/PWGEM/PhotonMeson/Utils/MCUtilities.h +++ b/PWGEM/PhotonMeson/Utils/MCUtilities.h @@ -32,12 +32,16 @@ #include #include #include +#include #include //_______________________________________________________________________ namespace o2::aod::pwgem::photonmeson::utils::mcutil { +template +concept is_unfiltered_iterator = o2::soa::is_iterator && !o2::soa::has_filtered_policy; + constexpr float kVertexEps = 1e-4f; // cm template @@ -1012,6 +1016,66 @@ float trueOpeningAngle(TMCParticles const& mcParticles, const int id1, const int return std::acos(std::clamp(c, -1.f, 1.f)); } +//_______________________________________________________________________ +/// \brief Whether the PDG code is a quark, gluon, diquark or string/cluster, i.e. where the decay chain ends +[[nodiscard]] inline bool isPartonLike(int pdg) +{ + const int absPdg = std::abs(pdg); + const bool isDiquark = absPdg > 1000 && absPdg < 6000 && (absPdg / 10) % 10 == 0; // o2-linter: disable=magic-number + return (absPdg >= PDG_t::kDown && absPdg <= PDG_t::kTop) || absPdg == PDG_t::kGluon || absPdg == 91 || absPdg == 92 || isDiquark; // o2-linter: disable=magic-number +} + +//_______________________________________________________________________ +/// \brief Whether the PDG code is a nucleus (10LZZZAAAI), e.g. one of the colliding beam nuclei +[[nodiscard]] inline bool isNucleus(int pdg) +{ + constexpr int NucleusOffset = 1000000000; + return std::abs(pdg) >= NucleusOffset; +} + +//_______________________________________________________________________ +/// \brief Fill the ancestor chain of a particle, starting with the particle itself. +/// Ends with the first parton-like ancestor (included), a particle without mothers, or when chain is full. +/// A nucleus (the colliding beam nuclei) is never part of the chain, otherwise all particles of an event would share it. +/// Unused entries of chain are set to -1. +/// \param mcParticle iterator of the full (unsliced) MC particle table. !WILL BE CHANGED! +/// Afterwards it points to the last chain element, or to the nucleus that ended the chain. +/// \param startId global index of the particle of interest +/// \param chain output, global indices, closest first +/// \return number of filled entries +template +int getAncestorChain(TMCParticle& mcParticle, int startId, std::span chain) +{ + int length = 0; + int id = startId; + while (id >= 0 && length < std::ssize(chain)) { + mcParticle.setCursor(id); + if (isNucleus(mcParticle.pdgCode())) { + break; // colliding nuclei are not an ancestor + } + chain[length++] = id; + if (isPartonLike(mcParticle.pdgCode()) || !mcParticle.has_mothers()) { + break; + } + id = mcParticle.mothersIds()[0]; + } + std::ranges::fill(chain.subspan(length), -1); + return length; +} + +//_______________________________________________________________________ +/// \brief Find the closest common ancestor of two ancestor chains +/// \param chain1 ancestor chain of first particle (particle itself first, may be padded with -1) +/// \param chain2 ancestor chain of second particle (particle itself first, may be padded with -1) +/// \return global index of the closest common ancestor, -1 if none found +[[nodiscard]] inline int getFirstCommonAncestor(std::span chain1, std::span chain2) +{ + chain1 = chain1.first(std::ranges::distance(chain1.begin(), std::ranges::find(chain1, -1))); + chain2 = chain2.first(std::ranges::distance(chain2.begin(), std::ranges::find(chain2, -1))); + const auto it = std::ranges::find_first_of(chain1, chain2); + return it != chain1.end() ? *it : -1; +} + } // namespace o2::aod::pwgem::photonmeson::utils::mcutil #endif // PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/NMHistograms.h b/PWGEM/PhotonMeson/Utils/NMHistograms.h index ab69f458636..448e94c3007 100644 --- a/PWGEM/PhotonMeson/Utils/NMHistograms.h +++ b/PWGEM/PhotonMeson/Utils/NMHistograms.h @@ -33,12 +33,19 @@ namespace o2::aod::pwgem::photonmeson::utils::nmhistogram { +/// \brief Add the pair histograms +/// \param isMC book the MC truth histograms (only mass and pT axes) instead of the same/mixed event histograms +/// \param pairname name of the pair used in the axis titles +/// \param sparseFullAxes data only: add EP, occupancy and z-vertex axes to the same/mixed event histograms +/// \param occBins data only: occupancy bin edges +/// \param epBins data only: event plane bin edges +/// \param vtxBins data only: z-vertex bin edges inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, + const char* pairname = "#gamma#gamma", bool sparseFullAxes = false, std::vector const& occBins = {0, 500, 1000, 2000, 3000, 5000, 10000}, std::vector const& epBins = {-o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf}, - std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, - const char* pairname = "#gamma#gamma") + std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}) { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at skimming.!! std::vector ptbins; @@ -57,9 +64,6 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is } const o2::framework::AxisSpec axis_pt{ptbins, Form("p_{T,%s} (GeV/c)", pairname)}; const o2::framework::AxisSpec axis_mass{400, 0, 0.8, Form("m_{%s} (GeV/c^{2})", pairname)}; - const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; - const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; - const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; if (isMC) { fRegistry->add("Pair/Pi0/hs_Primary", "rec. true pi0", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); @@ -84,6 +88,9 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is fRegistry->get(HIST("Generated/Eta/hPtY"))->SetYTitle("rapidity |y|"); } else { if (sparseFullAxes) { + const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; + const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; + const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; fRegistry->add("Pair/same/hs", "diphoton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_ep, axis_occ, axis_vtx}, true); From 56d4040e850347d521290b3067d2846fe09b44b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:49:49 +0200 Subject: [PATCH 287/648] [Common et all] Fix cpplint errors (#18106) --- ALICE3/Tasks/alice3DqEfficiency.cxx | 2 +- Common/CCDB/TriggerAliases.cxx | 3 +- Common/CCDB/TriggerAliases.h | 3 +- .../macros/upload_event_selection_params.C | 15 +++--- Common/Core/PID/TPCPIDResponse.h | 13 ++--- Common/Core/TrackSelection.cxx | 5 +- Common/Core/TrackSelection.h | 3 +- Common/Tasks/flowTest.cxx | 4 +- Common/Tasks/multiplicityQa.cxx | 4 +- Common/Tools/Multiplicity/MultModule.h | 3 +- Common/Tools/Multiplicity/multCalibrator.cxx | 7 +-- DPG/Tasks/AOTTrack/qaEventTrackLite.h | 2 +- DPG/Tasks/TOF/tofSkimsTableCreator.h | 2 +- PWGCF/Core/CorrelationContainer.cxx | 3 +- .../femtoWorldProducerReducedTask.cxx | 2 +- .../femtoWorldProducerTaskV0Only.cxx | 2 +- .../Tasks/multiparticle-correlations-ar.cxx | 52 +++++++++---------- .../Core/EventSelectionFilterAndAnalysis.h | 9 ++-- .../Core/SkimmingConfigurableCuts.cxx | 26 +++++----- .../Core/SkimmingConfigurableCuts.h | 25 ++++----- PWGDQ/Core/AnalysisCompositeCut.h | 2 +- PWGDQ/Tasks/dqEfficiency.cxx | 2 +- PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 8 +-- PWGDQ/Tasks/tableReader.cxx | 2 +- PWGDQ/Tasks/tableReader_withAssoc.h | 2 +- PWGDQ/Tasks/tableReader_withAssoc_direct.cxx | 4 +- PWGEM/Dilepton/DataModel/dileptonTables.h | 6 +-- PWGEM/Dilepton/DataModel/lmeeMLTables.h | 2 +- PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx | 13 ++--- .../TableProducer/mlBasedTrackSelector.cxx | 6 +-- PWGHF/HFC/Macros/FitCorrel.C | 30 +++++------ PWGHF/TableProducer/trackIndexSkimCreator.cxx | 6 +-- PWGJE/Tasks/triggerCorrelations.cxx | 7 +-- PWGLF/TableProducer/Common/lfTPCPID.cxx | 45 ++++++++-------- .../Common/zdc2stagecalibration.cxx | 8 +-- PWGLF/TableProducer/Common/zdcvector.cxx | 2 +- .../Resonances/doublephitable.cxx | 2 +- .../Resonances/xi1530kaonreducedtable.cxx | 3 +- .../Strangeness/strangenessbuilder.cxx | 2 +- PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx | 5 +- PWGLF/Tasks/QC/straRecoStudy.cxx | 3 +- .../Strangeness/strangenessInJetsIons.cxx | 15 +++--- PWGLF/Utils/mcCentralityModule.h | 3 +- Tutorials/OpenData/flowAnalysis.cxx | 4 +- Tutorials/src/ccdbaccess.cxx | 3 +- Tutorials/src/configurableObjects.cxx | 13 +++-- Tutorials/src/efficiencyGlobal.cxx | 3 +- Tutorials/src/efficiencyPerRun.cxx | 3 +- 48 files changed, 198 insertions(+), 191 deletions(-) diff --git a/ALICE3/Tasks/alice3DqEfficiency.cxx b/ALICE3/Tasks/alice3DqEfficiency.cxx index 59757563955..29361efe44e 100644 --- a/ALICE3/Tasks/alice3DqEfficiency.cxx +++ b/ALICE3/Tasks/alice3DqEfficiency.cxx @@ -775,7 +775,7 @@ struct Alice3DqEfficiencyAnalysisSameEventPairing { std::vector fGenMCSignals; std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; bool fUseMCGenAccCut = false; uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream diff --git a/Common/CCDB/TriggerAliases.cxx b/Common/CCDB/TriggerAliases.cxx index c3d28889bca..5ad08da8798 100644 --- a/Common/CCDB/TriggerAliases.cxx +++ b/Common/CCDB/TriggerAliases.cxx @@ -13,10 +13,11 @@ #include +#include #include #include -std::string aliasLabels[kNaliases] = { +const std::array aliasLabels{ "kINT7", "kEMC7", "kINT7inMUON", diff --git a/Common/CCDB/TriggerAliases.h b/Common/CCDB/TriggerAliases.h index 60dbd965741..ffe00462a5b 100644 --- a/Common/CCDB/TriggerAliases.h +++ b/Common/CCDB/TriggerAliases.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ enum triggerAliases { kNaliases }; -extern std::string aliasLabels[kNaliases]; +extern const std::array aliasLabels; class TriggerAliases { diff --git a/Common/CCDB/macros/upload_event_selection_params.C b/Common/CCDB/macros/upload_event_selection_params.C index c6385c4d09d..f5995d730c4 100644 --- a/Common/CCDB/macros/upload_event_selection_params.C +++ b/Common/CCDB/macros/upload_event_selection_params.C @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -33,12 +34,12 @@ void upload_event_selection_params() // ccdb.truncate("EventSelection/EventSelectionParams"); ccdb.init("https://alice-ccdb.cern.ch"); - const int nPeriodsMax = 100; - EventSelectionParams* par[nPeriodsMax]; - string period[nPeriodsMax]; - int runFirst[nPeriodsMax]; - int runLast[nPeriodsMax]; - bool isNew[nPeriodsMax] = {0}; + constexpr int NPeriodsMax{100}; + std::array par{}; + std::array period{}; + std::array runFirst{}; + std::array runLast{}; + std::array isNew{}; int n = 0; period[n] = "pp2010"; @@ -256,7 +257,7 @@ void upload_event_selection_params() auto sor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runFirst[i]).first; auto eor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runLast[i]).second; - printf("sor=%llu eor=%llu\n", sor, eor); + printf("sor=%ld eor=%ld\n", sor, eor); metadata["period"] = period[i]; metadata["run_first"] = Form("%d", runFirst[i]); metadata["run_last"] = Form("%d", runLast[i]); diff --git a/Common/Core/PID/TPCPIDResponse.h b/Common/Core/PID/TPCPIDResponse.h index 5120950fa5f..a8b082cad54 100644 --- a/Common/Core/PID/TPCPIDResponse.h +++ b/Common/Core/PID/TPCPIDResponse.h @@ -25,6 +25,7 @@ #include #include +#include #include namespace o2::pid::tpc @@ -77,7 +78,7 @@ class Response float GetExpectedSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the expected resolution of the track with multTPC explicitly provided template - float GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id) const; + float GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the number of sigmas with respect the expected value template float GetNumberOfSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; @@ -86,7 +87,7 @@ class Response float GetNumberOfSigmaMCTuned(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; // Number of sigmas with respect to expected for MC, defining a tune-on-data signal value, explicit multTPC template - float GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; + float GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; /// Gets the deviation to the expected signal template float GetSignalDelta(const TrackType& trk, const o2::track::PID::ID id) const; @@ -99,7 +100,7 @@ class Response private: /// Compute expected sigma given a pre-computed expected signal, avoiding a redundant Bethe-Bloch call. template - float sigmaFromSignal(float expectedSignal, const long multTPC, const TrackType& track, const o2::track::PID::ID id) const; + float sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const; std::array mBetheBlochParams = {0.03209809958934784, 19.9768009185791, 2.5266601063857674e-16, 2.7212300300598145, 6.080920219421387}; std::array mResolutionParamsDefault = {0.07, 0.0}; @@ -135,7 +136,7 @@ inline float Response::GetExpectedSigma(const CollisionType& collision, const Tr /// Gets the expected resolution of the measurement template -inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& track, const o2::track::PID::ID id) const +inline float Response::GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const { if (!track.hasTPC()) { return -999.f; @@ -144,7 +145,7 @@ inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const } template -inline float Response::sigmaFromSignal(float expectedSignal, const long multTPC, const TrackType& track, const o2::track::PID::ID id) const +inline float Response::sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const { float resolution = 0.f; if (mUseDefaultResolutionParam) { @@ -188,7 +189,7 @@ inline float Response::GetNumberOfSigmaMCTuned(const CollisionType& collision, c } template -inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const +inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const { const float signal = GetExpectedSignal(trk, id); if (signal < 0.f) { diff --git a/Common/Core/TrackSelection.cxx b/Common/Core/TrackSelection.cxx index 97de07b2bd0..5ee1ce57b55 100644 --- a/Common/Core/TrackSelection.cxx +++ b/Common/Core/TrackSelection.cxx @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ bool TrackSelection::FulfillsITSHitRequirements(uint8_t itsClusterMap) const return true; } -const std::string TrackSelection::mCutNames[static_cast(TrackSelection::TrackCuts::kNCuts)] = {"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; +const std::array(TrackSelection::TrackCuts::kNCuts)> TrackSelection::mCutNames{"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; void TrackSelection::SetTrackType(o2::aod::track::TrackTypeEnum trackType) { @@ -149,7 +150,7 @@ void TrackSelection::print() const for (int i = 0; i < static_cast(TrackCuts::kNCuts); i++) { switch (static_cast(i)) { case TrackCuts::kTrackType: - LOG(info) << mCutNames[i].data() << " == " << static_cast(mTrackType); + LOG(info) << mCutNames[i] << " == " << static_cast(mTrackType); break; case TrackCuts::kPtRange: LOG(info) << mCutNames[i] << " in [" << mMinPt << ", " << mMaxPt << "]"; diff --git a/Common/Core/TrackSelection.h b/Common/Core/TrackSelection.h index 80e676c625c..ff0c92b9baf 100644 --- a/Common/Core/TrackSelection.h +++ b/Common/Core/TrackSelection.h @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -68,7 +69,7 @@ class TrackSelection ppPass3 }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackCuts::kNCuts)> mCutNames; // True for Run 2 track types. Hoisted out of the per-cut IsSelected() so that // the trackType() column is dereferenced once per track instead of once per cut. diff --git a/Common/Tasks/flowTest.cxx b/Common/Tasks/flowTest.cxx index bfb42acff04..0e3334e91f1 100644 --- a/Common/Tasks/flowTest.cxx +++ b/Common/Tasks/flowTest.cxx @@ -30,6 +30,8 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -100,7 +102,7 @@ struct flowTest { if (evPhi < 0) evPhi += 2. * TMath::Pi(); - long nCh = 0; + int64_t nCh = 0; if (imp > minB && imp < maxB) { // event within range diff --git a/Common/Tasks/multiplicityQa.cxx b/Common/Tasks/multiplicityQa.cxx index 2178164854c..3c8c1100af8 100644 --- a/Common/Tasks/multiplicityQa.cxx +++ b/Common/Tasks/multiplicityQa.cxx @@ -290,8 +290,8 @@ struct MultiplicityQa { return; } - long NcontribsTOF = 0; - long NcontribsTRD = 0; + int64_t NcontribsTOF = 0; + int64_t NcontribsTRD = 0; for (auto& track : tracks) { if (track.isPVContributor()) { if (track.hasTRD()) diff --git a/Common/Tools/Multiplicity/MultModule.h b/Common/Tools/Multiplicity/MultModule.h index 3c9151ca78c..168a5b4488a 100644 --- a/Common/Tools/Multiplicity/MultModule.h +++ b/Common/Tools/Multiplicity/MultModule.h @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -480,7 +481,7 @@ class MultModule LOGF(info, "Metadata information: isMC? %i", metadataInfo.isMC()); const auto& workflows = context.services().template get(); - TString listOfRequestors[nTablesConst]; + std::array listOfRequestors{}; for (int i = 0; i < nTablesConst; i++) { int f = internalOpts.enabledTables->get(tableNames[i].c_str(), "enable"); if (f == 1) { diff --git a/Common/Tools/Multiplicity/multCalibrator.cxx b/Common/Tools/Multiplicity/multCalibrator.cxx index d65f615d584..964bb468b72 100644 --- a/Common/Tools/Multiplicity/multCalibrator.cxx +++ b/Common/Tools/Multiplicity/multCalibrator.cxx @@ -30,6 +30,7 @@ #include #include // FIXME +#include using namespace std; @@ -353,8 +354,8 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, const TString& lHis // place squarely at the middle to ensure it's all fine lMiddleOfBins[lB - 1] = 0.5 * (lDesiredBoundaries[lB] + lDesiredBoundaries[lB - 1]); } - Double_t lBounds[lNDesiredBoundaries + 1]; - Double_t lPrecision[lNDesiredBoundaries + 1]; + std::vector lBounds(lNDesiredBoundaries + 1); + std::vector lPrecision(lNDesiredBoundaries + 1); if (fAnchorPointValue > 0) { lBounds[0] = 0; @@ -382,7 +383,7 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, const TString& lHis cout << histoRaw->GetName() << " boundaries, percentile: " << lDesiredBoundaries[ii] << "%\t Signal value = " << lBounds[lDisplacedii] << "\tprecision = " << lPrecision[ii] << "% " << lPrecisionString.Data() << endl; } } - TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds); + TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds.data()); hCalib->SetDirectory(0); hCalib->SetBinContent(0, 100.5); hCalib->SetBinContent(1, 100.5); diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLite.h b/DPG/Tasks/AOTTrack/qaEventTrackLite.h index 3af51e15d65..9ea778698b7 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLite.h +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.h @@ -106,7 +106,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); //! Eta DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi DECLARE_SOA_COLUMN(PtReso, ptReso, float); //! Pt resolution -DECLARE_SOA_COLUMN(Sign, sign, short); //! Sign +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! Sign DECLARE_SOA_COLUMN(HasITS, hasITS, bool); //! Track has the ITS DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); //! Track has the TPC DECLARE_SOA_COLUMN(HasTRD, hasTRD, bool); //! Track has the TRD diff --git a/DPG/Tasks/TOF/tofSkimsTableCreator.h b/DPG/Tasks/TOF/tofSkimsTableCreator.h index f3a6f63bb9b..80db220c030 100644 --- a/DPG/Tasks/TOF/tofSkimsTableCreator.h +++ b/DPG/Tasks/TOF/tofSkimsTableCreator.h @@ -46,7 +46,7 @@ DECLARE_SOA_COLUMN(DeltaTPi, deltaTPi, float); //! Difference betw DECLARE_SOA_COLUMN(DeltaTKa, deltaTKa, float); //! Difference between the tofSignal and the kaon expected signal DECLARE_SOA_COLUMN(DeltaTPr, deltaTPr, float); //! Difference between the tofSignal and the proton expected signal DECLARE_SOA_COLUMN(DoubleDelta, doubleDelta, float); //! Double difference between DeltaT -DECLARE_SOA_COLUMN(RefSign, refSign, short); //! Sign of the reference track +DECLARE_SOA_COLUMN(RefSign, refSign, int16_t); //! Sign of the reference track DECLARE_SOA_COLUMN(PIDForTracking, pidForTracking, uint8_t); //! Index for mass hypothesis used in tracking see PID.h for definition DECLARE_SOA_COLUMN(EvTimeT0AC, evTimeT0AC, float); //! Event time of the track computed with the T0AC DECLARE_SOA_COLUMN(EvTimeT0ACErr, evTimeT0ACErr, float); //! Resolution of the event time of the track computed with the T0AC diff --git a/PWGCF/Core/CorrelationContainer.cxx b/PWGCF/Core/CorrelationContainer.cxx index 8cbd834ec8f..6c4a2e56d0d 100644 --- a/PWGCF/Core/CorrelationContainer.cxx +++ b/PWGCF/Core/CorrelationContainer.cxx @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -112,7 +113,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl std::vector pairAxis(correlationAxis); pairAxis.insert(pairAxis.end(), userAxis.begin(), userAxis.end()); - long bins = 1; + int64_t bins = 1; for (const auto& axis : pairAxis) { bins *= axis.getNbins(); } diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx index b477e97f4e5..0a0ba927a22 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx @@ -131,7 +131,7 @@ struct femtoWorldProducerReducedTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx index c4d9491f507..72e78653f44 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx @@ -183,7 +183,7 @@ struct femtoWorldProducerTaskV0Only { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx index 2814c562225..411c1ec8426 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx @@ -86,7 +86,7 @@ enum CorEventDep { kCENDEP, kLAST_CorEventDep }; -const std::string CorEventDepNames[kLAST_CorEventDep] = { +const std::array CorEventDepNames{ "[kINTEGRATED]", "[kMULDEP]", "[kCENDEP]"}; @@ -101,7 +101,7 @@ enum CorTrackDep { kETADEP, kLAST_CorTrackDep }; -const std::string CorTrackDepNames[kLAST_CorTrackDep] = { +const std::array CorTrackDepNames{ "[kPTDEP]", "[kETADEP]"}; std::vector> CorTrackDepDefaultBins = { @@ -121,7 +121,7 @@ enum EventVariable { kMULTPC, kLAST_EventVariable }; -static constexpr std::string_view EventVariableNames[kLAST_EventVariable] = { +static constexpr std::array EventVariableNames{ "EventVariable_VertexX", "EventVariable_VertexY", "EventVariable_VertexZ", @@ -201,10 +201,10 @@ std::vector> TrackVariableDefaultCuts = { {0., 6., 1.}}; // kITSCLUSTERS // common info string for all configurables -const std::string BinningSuffix = std::string("-Binning"); -const std::string CutSuffix = std::string("-Cut"); -const std::string CorrelatorHistPrefix = std::string("CorDepBinning_"); -const std::vector CutInfo = {std::string("Low"), std::string("High"), std::string("Option")}; +static constexpr std::string BinningSuffix{"-Binning"}; +static constexpr std::string CutSuffix{"-Cut"}; +static constexpr std::string CorrelatorHistPrefix{"CorDepBinning_"}; +static const std::vector cutInfo{"Low", "High", "Option"}; const int MaxHarmonic = 10; const int MaxPower = 10; @@ -259,7 +259,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVX{ std::string(AR::EventVariableNames[AR::kVX]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::BinningSuffix, @@ -267,7 +267,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::BinningSuffix, @@ -275,7 +275,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::BinningSuffix, @@ -283,7 +283,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::BinningSuffix, @@ -291,7 +291,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::BinningSuffix, @@ -299,7 +299,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::BinningSuffix, @@ -307,7 +307,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::BinningSuffix, @@ -315,7 +315,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::BinningSuffix, @@ -323,7 +323,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::cutInfo}, ""}; std::vector cfgEventBinning = { cfgEventAxisVX, @@ -343,7 +343,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPT{ std::string(AR::TrackVariableNames[AR::kPT]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::BinningSuffix, @@ -351,7 +351,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::BinningSuffix, @@ -359,7 +359,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::BinningSuffix, @@ -367,7 +367,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::BinningSuffix, @@ -375,7 +375,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::BinningSuffix, @@ -383,7 +383,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::BinningSuffix, @@ -391,7 +391,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::BinningSuffix, @@ -399,7 +399,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::BinningSuffix, @@ -407,7 +407,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::BinningSuffix, @@ -415,7 +415,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::cutInfo}, ""}; std::vector cfgTrackBinning = { cfgTrackAxisPT, diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index f05bfb365eb..a746cb96248 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -84,6 +84,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: struct ComplexBrickHelper { + virtual ~ComplexBrickHelper() = default; CutBrick* mBrick = nullptr; int mDefaultEstimatorIndex = -1; std::vector mAlternateEstimatorIndex = std::vector{}; @@ -95,15 +96,15 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis void armedBrick(uint64_t&, uint64_t&, uint64_t&, int&); }; struct MultiplicityBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector GetMultiplicities(); int GetArmedIndex() { return mBrick->getArmedIndex(); } - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; struct PileUpRejBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector mIndepVar = std::vector{}; - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; static bool filterBrickValue(uint64_t& mask, int& bit, CutBrick* brick, float value); diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx index ffd29c96571..56563c682ef 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -1188,19 +1189,18 @@ bool TrackSelectionBrick::FulfillsITSHitRequirements(uint8_t itsClusterMap) return true; } -const std::string TrackSelectionBrick::mCutNames[static_cast(TrackSelectionBrick::TrackCuts::kNCuts)] = - { - "TrackType", - "TPCNCls", - "TPCCrossedRowsOverNCls", - "TPCRefit", - "ITSNCls", - "ITSChi2NDF", - "ITSRefit", - "ITSHits", - "GoldenChi2", - "DCAxy", - "DCAz"}; +const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> TrackSelectionBrick::mCutNames{ + "TrackType", + "TPCNCls", + "TPCCrossedRowsOverNCls", + "TPCRefit", + "ITSNCls", + "ITSChi2NDF", + "ITSRefit", + "ITSHits", + "GoldenChi2", + "DCAxy", + "DCAz"}; /// \brief Returns wether the cut brick is incorporated in the selection chain /// \return true if the cut brick is incorporated diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 2e39ee2b1c7..05a4c32be31 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -110,7 +111,7 @@ class CutBrickLimit : public CutBrick public: CutBrickLimit(); CutBrickLimit(const char*, const TValueToFilter&); - CutBrickLimit(const TString&); + explicit CutBrickLimit(const TString&); ~CutBrickLimit() override = default; CutBrickLimit(const CutBrickLimit&) = delete; CutBrickLimit& operator=(const CutBrickLimit&) = delete; @@ -154,7 +155,7 @@ class CutBrickFnLimit : public CutBrickLimit public: CutBrickFnLimit(); CutBrickFnLimit(const char*, const TF1&); - CutBrickFnLimit(const TString&); + explicit CutBrickFnLimit(const TString&); ~CutBrickFnLimit() override = default; CutBrickFnLimit(const CutBrickFnLimit&) = delete; CutBrickFnLimit& operator=(const CutBrickFnLimit&) = delete; @@ -181,7 +182,7 @@ class CutBrickThreshold : public CutBrick public: CutBrickThreshold(); CutBrickThreshold(const char*, const TValueToFilter&); - CutBrickThreshold(const TString&); + explicit CutBrickThreshold(const TString&); ~CutBrickThreshold() override = default; CutBrickThreshold(const CutBrickThreshold&) = delete; CutBrickThreshold& operator=(const CutBrickThreshold&) = delete; @@ -225,7 +226,7 @@ class CutBrickFnThreshold : public CutBrickThreshold public: CutBrickFnThreshold(); CutBrickFnThreshold(const char*, const TF1&); - CutBrickFnThreshold(const TString&); + explicit CutBrickFnThreshold(const TString&); ~CutBrickFnThreshold() override = default; CutBrickFnThreshold(const CutBrickFnThreshold&) = delete; CutBrickFnThreshold& operator=(const CutBrickFnThreshold&) = delete; @@ -252,7 +253,7 @@ class CutBrickRange : public CutBrick public: CutBrickRange(); CutBrickRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickRange(const TString&); + explicit CutBrickRange(const TString&); ~CutBrickRange() override = default; CutBrickRange(const CutBrickRange&) = delete; CutBrickRange& operator=(const CutBrickRange&) = delete; @@ -297,7 +298,7 @@ class CutBrickFnRange : public CutBrickRange public: CutBrickFnRange(); CutBrickFnRange(const char*, const TF1&, const TF1&); - CutBrickFnRange(const TString&); + explicit CutBrickFnRange(const TString&); ~CutBrickFnRange() override = default; CutBrickFnRange(const CutBrickFnRange&) = delete; CutBrickFnRange& operator=(const CutBrickFnRange&) = delete; @@ -326,7 +327,7 @@ class CutBrickExtToRange : public CutBrick public: CutBrickExtToRange(); CutBrickExtToRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickExtToRange(const TString&); + explicit CutBrickExtToRange(const TString&); ~CutBrickExtToRange() override = default; CutBrickExtToRange(const CutBrickExtToRange&) = delete; CutBrickExtToRange& operator=(const CutBrickExtToRange&) = delete; @@ -371,7 +372,7 @@ class CutBrickFnExtToRange : public CutBrickExtToRange public: CutBrickFnExtToRange(); CutBrickFnExtToRange(const char*, const TF1&, const TF1&); - CutBrickFnExtToRange(const TString&); + explicit CutBrickFnExtToRange(const TString&); ~CutBrickFnExtToRange() override = default; CutBrickFnExtToRange(const CutBrickFnExtToRange&) = delete; CutBrickFnExtToRange& operator=(const CutBrickFnExtToRange&) = delete; @@ -402,7 +403,7 @@ class CutBrickSelectorMultipleRanges : public CutBrick public: CutBrickSelectorMultipleRanges(); CutBrickSelectorMultipleRanges(const char*, const std::vector&); - CutBrickSelectorMultipleRanges(const TString&); + explicit CutBrickSelectorMultipleRanges(const TString&); ~CutBrickSelectorMultipleRanges() override = default; CutBrickSelectorMultipleRanges(const CutBrickSelectorMultipleRanges&) = delete; CutBrickSelectorMultipleRanges& operator=(const CutBrickSelectorMultipleRanges&) = delete; @@ -432,7 +433,7 @@ class CutWithVariations : public CutBrick /// Default constructor CutWithVariations(); CutWithVariations(const char*, const char*, bool); - CutWithVariations(const TString&); + explicit CutWithVariations(const TString&); ~CutWithVariations() override = default; CutWithVariations(const CutWithVariations&) = delete; CutWithVariations& operator=(const CutWithVariations&) = delete; @@ -510,7 +511,7 @@ class TrackSelectionBrick : public SpecialCutBrick { public: TrackSelectionBrick() = default; - TrackSelectionBrick(const TString&); + explicit TrackSelectionBrick(const TString&); ~TrackSelectionBrick() override = default; enum class TrackCuts : int { @@ -530,7 +531,7 @@ class TrackSelectionBrick : public SpecialCutBrick kNCuts }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> mCutNames; std::vector IsArmed() override; template diff --git a/PWGDQ/Core/AnalysisCompositeCut.h b/PWGDQ/Core/AnalysisCompositeCut.h index 7e78faebfd6..a9482c3cb91 100644 --- a/PWGDQ/Core/AnalysisCompositeCut.h +++ b/PWGDQ/Core/AnalysisCompositeCut.h @@ -27,7 +27,7 @@ class AnalysisCompositeCut : public AnalysisCut { public: - AnalysisCompositeCut(bool useAND = kTRUE); + explicit AnalysisCompositeCut(bool useAND = kTRUE); AnalysisCompositeCut(const char* name, const char* title, bool useAND = kTRUE); AnalysisCompositeCut(const AnalysisCompositeCut& c); AnalysisCompositeCut& operator=(const AnalysisCompositeCut& c); diff --git a/PWGDQ/Tasks/dqEfficiency.cxx b/PWGDQ/Tasks/dqEfficiency.cxx index afcb0388c08..dca19a23c13 100644 --- a/PWGDQ/Tasks/dqEfficiency.cxx +++ b/PWGDQ/Tasks/dqEfficiency.cxx @@ -1369,7 +1369,7 @@ struct AnalysisDileptonTrackTrack { float* fValuesQuadruplet = nullptr; std::vector fQuadrupletCutNames; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCuts; TString fTrackCutName1; TString fTrackCutName2; diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index c35430d09dc..9e1364dc5c5 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -776,7 +776,7 @@ struct AnalysisTrackSelection { // compute quantities which depend on the associated collision, such as DCA if (track.collisionId() != event.globalIndex()) { VarManager::FillTrackCollision(track, event); - // cout << "Filled track observables for association" << endl; + // cout << "Filled track observables for association" << endl; } bool isCorrectAssoc = false; @@ -1976,9 +1976,9 @@ struct AnalysisSameEventPairing { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs + if (sign1 * sign2 < 0) { // +- pairs fHistMan->FillHistClass(histNames[icut][0].Data(), static_cast(VarManager::fgValues)); // reconstructed, unmatched - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals + for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], @@ -3233,7 +3233,7 @@ struct AnalysisDileptonTrackTrack { uint32_t fTrackCutBitMap1; uint32_t fTrackCutBitMap2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; diff --git a/PWGDQ/Tasks/tableReader.cxx b/PWGDQ/Tasks/tableReader.cxx index e9deca68d91..4d385593a01 100644 --- a/PWGDQ/Tasks/tableReader.cxx +++ b/PWGDQ/Tasks/tableReader.cxx @@ -2117,7 +2117,7 @@ struct AnalysisDileptonTrackTrack { TString fTrackCutName1; TString fTrackCutName2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index e7952a1fdad..88bdbc0fd67 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -4751,7 +4751,7 @@ struct AnalysisDileptonTrackTrack { TString fTrackCutName1; TString fTrackCutName2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index 7bbff3763c9..78fd2b46359 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -1362,7 +1362,7 @@ struct AnalysisSameEventPairing { std::map> fTrackMuonHistNames; // for electron-muon pairs: key = iTrack * fNCutsMuon + iMuon std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; // bool fUseMCGenAccCut = false; uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream @@ -1861,7 +1861,7 @@ struct AnalysisSameEventPairing { for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); if (!cut.IsSelected(static_cast(VarManager::fgValues))) { - continue; // apply pair cuts + continue; // apply pair cuts } if (sign1 * sign2 < 0) { // opposite sign pairs fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), static_cast(VarManager::fgValues)); diff --git a/PWGEM/Dilepton/DataModel/dileptonTables.h b/PWGEM/Dilepton/DataModel/dileptonTables.h index 4178d0c7b89..991818d04e1 100644 --- a/PWGEM/Dilepton/DataModel/dileptonTables.h +++ b/PWGEM/Dilepton/DataModel/dileptonTables.h @@ -1234,7 +1234,7 @@ DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! (sign of charge)/Pt in c/ DECLARE_SOA_COLUMN(Eta, eta, float); //! DECLARE_SOA_COLUMN(Phi, phi, float); //! DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float signed1Pt) -> float { return 1.f / std::fabs(signed1Pt); }); -DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> short { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 +DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> int16_t { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 } // namespace emprimarytrack DECLARE_SOA_TABLE_VERSIONED(EMPrimaryTracks_000, "AOD", "EMPRIMARYTRACK", 0, //! primary charged track table for 2PC @@ -1281,13 +1281,13 @@ DECLARE_SOA_INDEX_COLUMN(EMThinEvent, emthinevent); //! DECLARE_SOA_COLUMN(Pt1, pt1, float); //! DECLARE_SOA_COLUMN(Eta1, eta1, float); //! DECLARE_SOA_COLUMN(Phi1, phi1, float); //! -DECLARE_SOA_COLUMN(Sign1, sign1, short); //! +DECLARE_SOA_COLUMN(Sign1, sign1, int16_t); //! DECLARE_SOA_COLUMN(DCA1, dca1, float); //! DCA in sigma. Users should decide 3D or XY or Z DECLARE_SOA_COLUMN(Pt2, pt2, float); //! DECLARE_SOA_COLUMN(Eta2, eta2, float); //! DECLARE_SOA_COLUMN(Phi2, phi2, float); //! DECLARE_SOA_COLUMN(DCA2, dca2, float); //! DCA in sigma. Users should decide 3D or XY or Z -DECLARE_SOA_COLUMN(Sign2, sign2, short); //! +DECLARE_SOA_COLUMN(Sign2, sign2, int16_t); //! DECLARE_SOA_COLUMN(Weight, weight, float); //! possible pair weight } // namespace emdilepton diff --git a/PWGEM/Dilepton/DataModel/lmeeMLTables.h b/PWGEM/Dilepton/DataModel/lmeeMLTables.h index 3fc0d0aa90b..f7965db9a57 100644 --- a/PWGEM/Dilepton/DataModel/lmeeMLTables.h +++ b/PWGEM/Dilepton/DataModel/lmeeMLTables.h @@ -62,7 +62,7 @@ DECLARE_SOA_COLUMN(TPCNClsFound, tpcNClsFound, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsPID, tpcNClsPID, uint8_t); //! DECLARE_SOA_COLUMN(IsForValidation, isForValidation, bool); //! -DECLARE_SOA_COLUMN(Sign, sign, short); //! +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! DECLARE_SOA_COLUMN(P, p, float); //! DECLARE_SOA_COLUMN(PtGen, ptGen, float); //! DECLARE_SOA_COLUMN(EtaGen, etaGen, float); //! diff --git a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx index 9cf9385ac71..a739ff9ca50 100644 --- a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx +++ b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx @@ -1043,8 +1043,7 @@ struct AnalysisTrackSelection { template void runRecTrackMore(TEvents const& events, TEventsMC const& /*eventsMC*/, TTracks const& groupedTracks, TTracksMC const& tracksMC, TAmbigTracks const& ambiTracksMid) { - - std::map fRecTrackLabels[fTrackCuts.size() + 1]; + std::vector> fRecTrackLabels(fTrackCuts.size() + 1); uint32_t filterMap = 0; trackSel.reserve(groupedTracks.size()); @@ -1052,10 +1051,7 @@ struct AnalysisTrackSelection { for (auto& track : groupedTracks) { // How many time the associated MC track was seen for this cut - Int_t fRecCounters[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - fRecCounters[k] = 0; - } + std::vector fRecCounters(fTrackCuts.size() + 1, 0); filterMap = 0; Int_t ambiguousinfo = 0; @@ -1157,10 +1153,7 @@ struct AnalysisTrackSelection { uint32_t mcDecision = 0; int isig = 0; Int_t mctrackindex = -999; - Int_t doublereconstructedtrack[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - doublereconstructedtrack[k] = 0; - } + std::vector doublereconstructedtrack(fTrackCuts.size() + 1, 0); for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { if constexpr ((TTrackFillMap & VarManager::ObjTypes::Track) > 0) { if (track.has_mcParticle()) { diff --git a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx index 32a7ba5ad8c..5961630d3d2 100644 --- a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx +++ b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx @@ -190,9 +190,9 @@ struct HfTrackSelectorTagSelCollisions { } // set numerical value of the Run 2 trigger class - auto* const triggerAlias = std::find(aliasLabels, aliasLabels + kNaliases, triggerClassName.value.data()); - if (triggerAlias != aliasLabels + kNaliases) { - hfEvSel.triggerClass.value = std::distance(aliasLabels, triggerAlias); + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); } hfEvSel.init(registry, &zorroSummary); // collision monitoring diff --git a/PWGHF/HFC/Macros/FitCorrel.C b/PWGHF/HFC/Macros/FitCorrel.C index a90d502e58f..5d29d451eaf 100644 --- a/PWGHF/HFC/Macros/FitCorrel.C +++ b/PWGHF/HFC/Macros/FitCorrel.C @@ -110,10 +110,7 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") const int nBinsPtCand = binsPtCandIntervalsVec.size() - 1; const int nBinsPtHad = binsPtHadIntervals.size() - 1; - double binsPtCandIntervals[nBinsPtCand + 1]; - for (int i = 0; i < nBinsPtCand + 1; i++) { - binsPtCandIntervals[i] = binsPtCandIntervalsVec[i]; - } + std::vector binsPtCandIntervals{binsPtCandIntervalsVec}; const Value& fitFuncValue = config["FitFunction"]; readArray(fitFuncValue, fitFunc); @@ -122,17 +119,14 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") int const fixMean = config["FixMean"].GetInt(); int const nBaselinePoints = config["nBaselinePoints"].GetInt(); - vector pointsForBaselineVec; + std::vector pointsForBaselineVec; const Value& pointsForBaselineValue = config["binsForBaseline"]; readArray(pointsForBaselineValue, pointsForBaselineVec); if (pointsForBaselineVec.size() != nBaselinePoints) { cout << "ERROR: size of the vector pointsForBaseline is different from the number of nBaselinePoints" << endl; return; } - int pointsForBaseline[nBaselinePoints]; - for (int i = 0; i < nBaselinePoints; i++) { - pointsForBaseline[i] = pointsForBaselineVec[i]; - } + std::vector pointsForBaseline{pointsForBaselineVec}; std::cout << "=========================== " << std::endl; std::cout << "Input variables from config" << std::endl; @@ -202,7 +196,7 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") corrFitter[iBinPtHad][iBinPtCand]->setHistoIsReflected(refl); corrFitter[iBinPtHad][iBinPtCand]->setFixBaseline(fixBase); corrFitter[iBinPtHad][iBinPtCand]->setBaselineUpOrDown(shiftBaseUp, shiftBaseDown); - corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline); + corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline.data()); corrFitter[iBinPtHad][iBinPtCand]->setv2(v2AssocPart[iBinPtCand], v2Dmeson[iBinPtCand]); corrFitter[iBinPtHad][iBinPtCand]->setReflectedCorrHisto(isReflected); @@ -222,14 +216,14 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") canvasCorrPhi[iBinPtHad]->Divide(3, 2); } // histograms with fir parameters - hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); + hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { setTH1HistoStyle(hCorrPhi[iBinPtCand][iBinPtHad], "", "#Delta#phi [rad]", "#frac{1}{N_{D_{s}}}#frac{dN^{assoc}}{d#Delta#phi} [rad^{-1}]", kFullCircle, kRed + 1, 1.4, kRed + 1, 3); diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index c8456183f31..c4fa6635115 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -174,9 +174,9 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { } // set numerical value of the Run 2 trigger class - auto* const triggerAlias = std::find(aliasLabels, aliasLabels + kNaliases, triggerClassName.value.data()); - if (triggerAlias != aliasLabels + kNaliases) { - hfEvSel.triggerClass.value = std::distance(aliasLabels, triggerAlias); + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); } hfEvSel.init(registry, &zorroSummary); // collision monitoring diff --git a/PWGJE/Tasks/triggerCorrelations.cxx b/PWGJE/Tasks/triggerCorrelations.cxx index 2a32add9252..ba66fb5abac 100644 --- a/PWGJE/Tasks/triggerCorrelations.cxx +++ b/PWGJE/Tasks/triggerCorrelations.cxx @@ -26,6 +26,7 @@ #include +#include #include #include @@ -38,9 +39,9 @@ struct TriggerCorrelationsTask { HistogramRegistry registry; std::vector triggerMaskBits; - long unsigned int nChargedTriggers = 4; - long unsigned int nChargedHFTriggers = 4; - long unsigned int nFullTriggers = 13; + uint64_t nChargedTriggers = 4; + uint64_t nChargedHFTriggers = 4; + uint64_t nFullTriggers = 13; void init(o2::framework::InitContext&) { triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(jetderiveddatautilities::JTriggerMasks); diff --git a/PWGLF/TableProducer/Common/lfTPCPID.cxx b/PWGLF/TableProducer/Common/lfTPCPID.cxx index 90c124bbbe8..d84c977c101 100644 --- a/PWGLF/TableProducer/Common/lfTPCPID.cxx +++ b/PWGLF/TableProducer/Common/lfTPCPID.cxx @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -62,34 +63,34 @@ static const std::vector parameterNames{"Use default tiny", "Set parameters", "bb1", "bb2", "bb3", "bb4", "bb5", "MIP value", "Charge exponent", "Resolution"}; -static constexpr float defaultParameters[nSpecies][nParameters]{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}; +static constexpr std::array, nSpecies> defaultParameters{{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}}; static constexpr int nOptions = 4; static const std::vector optionNames{"Bethe Bloch path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Post calibration path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Simple Bethe Bloch", // (true/false) "RequirePostCalib"}; // (true/false) -const std::string defaultOptions[nSpecies][nOptions]{{"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}}; +const std::array, nSpecies> defaultOptions{{{"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}}}; // Structure to hold the parameters struct bbParams { const std::string name; - bbParams(const std::string& n) : name(n) {} + explicit bbParams(const std::string& n) : name(n) {} // Parameters for the Bethe-Bloch parametrization float bb1 = 0.03209809958934784f; // Aleph Bethe Bloch parameter 1 float bb2 = 19.9768009185791f; // Aleph Bethe Bloch parameter 2 @@ -472,15 +473,15 @@ struct lfTpcPid { // Parameters setting from json Configurable> bbParameters{"bbParameters", - {defaultParameters[0], nSpecies, nParameters, particleNames, parameterNames}, + {defaultParameters.front().data(), nSpecies, nParameters, particleNames, parameterNames}, "Bethe Bloch parameters"}; // Parameter setting from input file (including the ccdb) Configurable> fileParamBbPositive{"fileParamBbPositive", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for positive particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable> fileParamBbNegative{"fileParamBbNegative", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for negative particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index bdc3b44c824..c5581b3e3be 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -307,21 +307,21 @@ struct zdc2stagecalibration { auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), runNumber); sorTimestamp = runInfo.sor; eorTimestamp = runInfo.eor; - bcSOR = static_cast(runInfo.orbitSOR) * static_cast(o2::constants::lhc::LHCMaxBunches); + bcSOR = runInfo.orbitSOR * static_cast(o2::constants::lhc::LHCMaxBunches); gainprofile = nullptr; spatialprofile = nullptr; qRecenteringProfile = nullptr; if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); if (!gainprofile) { - LOGF(warn, "No ZDC stage-1 gain map found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC stage-1 gain map found for run %d at timestamp %llu", runNumber, timestamp); } } if (calibrationStage.value == 2 && useSpatialCalib.value && !confSpatialPath.value.empty()) { spatialprofile = ccdb->getForTimeStamp(confSpatialPath.value, timestamp); if (!spatialprofile) { - LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, timestamp); } } if (calibrationStage.value == 2 && useQRecentering.value) { @@ -330,7 +330,7 @@ struct zdc2stagecalibration { qRecenteringProfile = ccdb->getForTimeStamp(qRecenteringPath, timestamp); } if (!qRecenteringProfile) { - LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, static_cast(timestamp)); + LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, timestamp); } else if (qRecenteringMethod.value == 4) { const bool structuredMapOK = qRecenteringProfile->GetYaxis()->GetNbins() == kQRecenteringStructuredNFeatures && std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringMultidimNFeatures + 1)) == "(T--0.8)_+^3" && diff --git a/PWGLF/TableProducer/Common/zdcvector.cxx b/PWGLF/TableProducer/Common/zdcvector.cxx index 852834dcb8b..ff67bf9f081 100644 --- a/PWGLF/TableProducer/Common/zdcvector.cxx +++ b/PWGLF/TableProducer/Common/zdcvector.cxx @@ -239,7 +239,7 @@ struct zdcvector { LOGF(info, "Run %d: SOR timestamp = %llu ms", currentRunNumber, - static_cast(sorTimestamp)); + sorTimestamp); } float timeInMinutes = 0.f; diff --git a/PWGLF/TableProducer/Resonances/doublephitable.cxx b/PWGLF/TableProducer/Resonances/doublephitable.cxx index 7845dbdf6b3..1cd5309b47e 100644 --- a/PWGLF/TableProducer/Resonances/doublephitable.cxx +++ b/PWGLF/TableProducer/Resonances/doublephitable.cxx @@ -459,7 +459,7 @@ struct doublephitable { auto* grpmag = ccdb->getForTimeStamp(ccdbPathGrpMag.value, bc.timestamp()); if (grpmag == nullptr) { - LOGF(fatal, "GRPMagField unavailable for run %d, timestamp %llu", bc.runNumber(), static_cast(bc.timestamp())); + LOGF(fatal, "GRPMagField unavailable for run %d, timestamp %llu", bc.runNumber(), bc.timestamp()); } o2::base::Propagator::initFieldFromGRP(grpmag); diff --git a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx index 9565711b486..a13f5a57ae2 100644 --- a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx +++ b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx @@ -512,8 +512,7 @@ struct xi1530kaonreducedtable { { auto* grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); if (grpo == nullptr) { - LOGF(fatal, "GRPMagField object not found in CCDB for timestamp %llu", - static_cast(timestamp)); + LOGF(fatal, "GRPMagField object not found in CCDB for timestamp %llu", timestamp); return 0.f; } return 0.1f * grpo->getNominalL3Field(); // kG -> T diff --git a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx index 0d034c5b1a8..493244cb856 100644 --- a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx @@ -687,7 +687,7 @@ struct StrangenessBuilder { LOGF(info, "Configuring tables to generate"); auto& workflows = context.services().get(); - TString listOfRequestors[nTables]; + std::array listOfRequestors{}; for (int i = 0; i < nTables; i++) { // adjust bookkeeping histogram h->GetXaxis()->SetBinLabel(i + 1, tableNames[i].c_str()); diff --git a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx index 6ed90f63f57..bbb966da90c 100644 --- a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -192,8 +193,8 @@ struct hypertritonAnalysis { kEvSelAllSteps }; // Helper to do bookkeeping and late filling of QA histos - std::array stats; - std::array evselstats; + std::array stats; + std::array evselstats; void resetHistos() { diff --git a/PWGLF/Tasks/QC/straRecoStudy.cxx b/PWGLF/Tasks/QC/straRecoStudy.cxx index b191f182677..4eddea21f14 100644 --- a/PWGLF/Tasks/QC/straRecoStudy.cxx +++ b/PWGLF/Tasks/QC/straRecoStudy.cxx @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -328,7 +329,7 @@ struct straRecoStudy { kEvSelVtxZ, kEvSelAllSteps }; - std::array evselstats; + std::array evselstats; void resetCounters() { diff --git a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx index 47769cced1e..974350c21dd 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -2049,7 +2050,7 @@ struct StrangenessInJetsIons { bool isV0 = isK0S || isLambda || isAntiLambda; if (!isV0) continue; - for (long unsigned int i = 0; i < fjTracks.size(); ++i) { + for (uint64_t i = 0; i < fjTracks.size(); ++i) { if (isV0DaughterTrack(fjTracks[i], v0)) { // LOG(info) << "[AddV0sForJetReconstructionData] V0 daughter track found in fjTracks."; isTrackReplaced[i] = true; @@ -2059,7 +2060,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2147,7 +2148,7 @@ struct StrangenessInJetsIons { bool isV0 = isK0S || isLambda || isAntiLambda; if (!isV0) continue; - for (long unsigned int i = 0; i < fjTracks.size(); ++i) { + for (uint64_t i = 0; i < fjTracks.size(); ++i) { if (isV0DaughterTrack(fjTracks[i], v0)) { // LOG(info) << "[AddV0sForJetReconstructionMCD] V0 daughter track found in fjTracks."; isTrackReplaced[i] = true; @@ -2157,7 +2158,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2205,7 +2206,7 @@ struct StrangenessInJetsIons { } // Remove V0 daughter particles if already in the input list for the jet finder - for (long unsigned int i = 0; i < fjParticleObj.size(); ++i) { + for (uint64_t i = 0; i < fjParticleObj.size(); ++i) { const auto& mcPart = fjParticleObj[i]; if (!mcPart.has_mothers()) continue; @@ -2220,7 +2221,7 @@ struct StrangenessInJetsIons { std::vector cleanFjInput; cleanFjInput.reserve(fjInput.size()); - for (long unsigned int i = 0; i < fjInput.size(); ++i) { + for (uint64_t i = 0; i < fjInput.size(); ++i) { if (!isTrackReplaced[i]) cleanFjInput.push_back(fjInput[i]); } @@ -2341,7 +2342,7 @@ struct StrangenessInJetsIons { double minDeltaR = maxDeltaR; // Search closest jet RECO in (eta,phi) space - for (long unsigned int iReco = 0; iReco < jetsReco.size(); ++iReco) { + for (uint64_t iReco = 0; iReco < jetsReco.size(); ++iReco) { if (isRecoJetMatched[iReco]) continue; diff --git a/PWGLF/Utils/mcCentralityModule.h b/PWGLF/Utils/mcCentralityModule.h index 0a7871609aa..dd53bbb2ac8 100644 --- a/PWGLF/Utils/mcCentralityModule.h +++ b/PWGLF/Utils/mcCentralityModule.h @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -257,7 +258,7 @@ struct BuilderModule { nEnabledTables = 0; - TString listOfRequestors[nTablesConst]; + std::array listOfRequestors{}; for (int i = 0; i < nTablesConst; i++) { int f = baseOpts.enabledTables->get(tableNames[i].c_str(), "enable"); if (f == 1) { diff --git a/Tutorials/OpenData/flowAnalysis.cxx b/Tutorials/OpenData/flowAnalysis.cxx index 6ab74ebfced..35bc2c2021c 100644 --- a/Tutorials/OpenData/flowAnalysis.cxx +++ b/Tutorials/OpenData/flowAnalysis.cxx @@ -88,7 +88,7 @@ struct flow_base { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Filter collisionFilter = (aod::collision::flags & (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks) == (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks; - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); void fillAPt(double trackpt, double cent, double vn, double sinHarm, double cosHarm) { @@ -286,7 +286,7 @@ struct flow_base { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } diff --git a/Tutorials/src/ccdbaccess.cxx b/Tutorials/src/ccdbaccess.cxx index e1ea568f46f..0a987fae790 100644 --- a/Tutorials/src/ccdbaccess.cxx +++ b/Tutorials/src/ccdbaccess.cxx @@ -29,6 +29,7 @@ #include #include +#include #include using namespace o2::framework; @@ -39,7 +40,7 @@ struct TimestampUserTask { Service ccdb; Configurable path{"ccdb-path", "qc/TOF/TOFTaskCompressed/hDiagnostic", "path to the ccdb object"}; Configurable url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; void init(o2::framework::InitContext&) { diff --git a/Tutorials/src/configurableObjects.cxx b/Tutorials/src/configurableObjects.cxx index e1a758b1851..a0941834e8d 100644 --- a/Tutorials/src/configurableObjects.cxx +++ b/Tutorials/src/configurableObjects.cxx @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -63,10 +64,8 @@ auto printMatrix(Array2D const& m) return ss.str(); } -static constexpr float defaultm[3][4] = {{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}; -static LabeledArray la{&defaultm[0][0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}; -const std::string defaultmS[3][4] = {{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}; -static LabeledArray laS{&defaultmS[0][0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}; +static constexpr std::array, 3> defaultm{{{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}}; +static constexpr std::array, 3> defaultmS{{{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}}; struct ConfigurableObjectDemo { // Simple type configurables @@ -82,9 +81,9 @@ struct ConfigurableObjectDemo { Configurable> array{"array", {0, 0, 0, 0, 0, 0, 0}, "generic int array"}; Configurable> farray{"farray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic float array"}; Configurable> darray{"darray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic double array"}; - Configurable> vmatrix{"matrix", {&defaultm[0][0], 3, 4}, "generic matrix"}; - Configurable> vla{"vla", {defaultm[0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; - Configurable> vlaS{"vlaS", {defaultmS[0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; + Configurable> vmatrix{"matrix", {defaultm.front().data(), 3, 4}, "generic matrix"}; + Configurable> vla{"vla", {defaultm.front().data(), 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; + Configurable> vlaS{"vlaS", {defaultmS.front().data(), 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; // Configurables can be grouped into `ConfigurableGroup`s. // Their names must be unique. diff --git a/Tutorials/src/efficiencyGlobal.cxx b/Tutorials/src/efficiencyGlobal.cxx index 13e1fdb9df3..a697162622b 100644 --- a/Tutorials/src/efficiencyGlobal.cxx +++ b/Tutorials/src/efficiencyGlobal.cxx @@ -28,6 +28,7 @@ #include #include +#include #include using namespace o2::framework; @@ -37,7 +38,7 @@ struct EfficiencyGlobal { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; diff --git a/Tutorials/src/efficiencyPerRun.cxx b/Tutorials/src/efficiencyPerRun.cxx index 7cbebd815ab..944b7fbb105 100644 --- a/Tutorials/src/efficiencyPerRun.cxx +++ b/Tutorials/src/efficiencyPerRun.cxx @@ -31,6 +31,7 @@ #include #include +#include #include using namespace o2::framework; @@ -40,7 +41,7 @@ struct EfficiencyPerRun { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; From 11c9761fb2c7da38a1ba3d2642efa5187637973d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 17:18:02 +0200 Subject: [PATCH 288/648] [PWGJE] Add PCH for PWGJE (#18309) --- PWGJE/Core/CMakeLists.txt | 18 ++++++++ PWGJE/Core/jetDataModelPCH.cxx | 13 ++++++ PWGJE/JetFinders/CMakeLists.txt | 45 +++++++++++++++++++ PWGJE/JetFinders/Duplicates/CMakeLists.txt | 3 ++ PWGJE/TableProducer/CMakeLists.txt | 15 +++++++ PWGJE/TableProducer/Matching/CMakeLists.txt | 34 ++++++++++++++ .../Matching/Duplicates/CMakeLists.txt | 3 ++ .../Matching/Substructure/CMakeLists.txt | 20 +++++++++ PWGJE/Tasks/CMakeLists.txt | 9 ++++ 9 files changed, 160 insertions(+) create mode 100644 PWGJE/Core/jetDataModelPCH.cxx diff --git a/PWGJE/Core/CMakeLists.txt b/PWGJE/Core/CMakeLists.txt index a7713c9900d..a05330e18f6 100644 --- a/PWGJE/Core/CMakeLists.txt +++ b/PWGJE/Core/CMakeLists.txt @@ -27,3 +27,21 @@ o2physics_target_root_dictionary(PWGJECore utilsTrackMatchingEMC.h LINKDEF PWGJECoreLinkDef.h) endif() + +# Precompiled header shared across PWGJE. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# Jet.h reaches 90% of the group; the existing substructure carriers already +# precompile it, so this one serves everything they do not cover. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(JetDataModelPCH OBJECT jetDataModelPCH.cxx) +target_link_libraries(JetDataModelPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(JetDataModelPCH PRIVATE + [["PWGJE/DataModel/Jet.h"]] # 90% + ) +endif() diff --git a/PWGJE/Core/jetDataModelPCH.cxx b/PWGJE/Core/jetDataModelPCH.cxx new file mode 100644 index 00000000000..ed5b36ec798 --- /dev/null +++ b/PWGJE/Core/jetDataModelPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the JetDataModelPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGJE/JetFinders/CMakeLists.txt b/PWGJE/JetFinders/CMakeLists.txt index 4426a43c4a4..218d03cad31 100644 --- a/PWGJE/JetFinders/CMakeLists.txt +++ b/PWGJE/JetFinders/CMakeLists.txt @@ -15,227 +15,272 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged SOURCES jetFinderDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged SOURCES jetFinderMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged SOURCES jetFinderMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-full SOURCES jetFinderDataFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-full SOURCES jetFinderMCDFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-full SOURCES jetFinderMCPFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-neutral SOURCES jetFinderDataNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-neutral SOURCES jetFinderMCDNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-neutral SOURCES jetFinderMCPNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-data-charged SOURCES jetFinderD0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcd-charged SOURCES jetFinderD0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcp-charged SOURCES jetFinderD0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-data-charged SOURCES jetFinderDplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcd-charged SOURCES jetFinderDplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcp-charged SOURCES jetFinderDplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-data-charged SOURCES jetFinderDsDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcd-charged SOURCES jetFinderDsMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcp-charged SOURCES jetFinderDsMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-data-charged SOURCES jetFinderDstarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcd-charged SOURCES jetFinderDstarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcp-charged SOURCES jetFinderDstarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-data-charged SOURCES jetFinderLcDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcd-charged SOURCES jetFinderLcMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcp-charged SOURCES jetFinderLcMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-data-charged SOURCES jetFinderB0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcd-charged SOURCES jetFinderB0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcp-charged SOURCES jetFinderB0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-data-charged SOURCES jetFinderBplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcd-charged SOURCES jetFinderBplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcp-charged SOURCES jetFinderBplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-data-charged SOURCES jetFinderXicToXiPiPiDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcd-charged SOURCES jetFinderXicToXiPiPiMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcp-charged SOURCES jetFinderXicToXiPiPiMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-data-charged SOURCES jetFinderV0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcd-charged SOURCES jetFinderV0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcp-charged SOURCES jetFinderV0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-data-charged SOURCES jetFinderDielectronDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcd-charged SOURCES jetFinderDielectronMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcp-charged SOURCES jetFinderDielectronMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-data-charged SOURCES jetFinderD0D0BarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcd-charged SOURCES jetFinderD0D0BarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcp-charged SOURCES jetFinderD0D0BarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-data-charged SOURCES jetFinderDplusDminusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcd-charged SOURCES jetFinderDplusDminusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcp-charged SOURCES jetFinderDplusDminusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/JetFinders/Duplicates/CMakeLists.txt b/PWGJE/JetFinders/Duplicates/CMakeLists.txt index 4270de9f221..2e0217b488d 100644 --- a/PWGJE/JetFinders/Duplicates/CMakeLists.txt +++ b/PWGJE/JetFinders/Duplicates/CMakeLists.txt @@ -14,16 +14,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged-1 SOURCES jetFinderDataCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged-1 SOURCES jetFinderMCDCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged-1 SOURCES jetFinderMCPCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/CMakeLists.txt b/PWGJE/TableProducer/CMakeLists.txt index dfb9537a478..c789aff9d49 100644 --- a/PWGJE/TableProducer/CMakeLists.txt +++ b/PWGJE/TableProducer/CMakeLists.txt @@ -30,16 +30,19 @@ o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer o2physics_add_dpl_workflow(jet-deriveddata-selector SOURCES derivedDataSelector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-deriveddata-writer SOURCES derivedDataWriter.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-luminosity-producer SOURCES luminosityProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -50,16 +53,19 @@ o2physics_add_dpl_workflow(jet-luminosity-calculator o2physics_add_dpl_workflow(jet-eventweight-mcd SOURCES jetEventWeightMCD.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-eventweight-mcp SOURCES jetEventWeightMCP.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mc-outlier-rejector SOURCES mcOutlierRejector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -70,31 +76,37 @@ o2physics_add_dpl_workflow(jet-track-derived o2physics_add_dpl_workflow(jet-hf-definition SOURCES heavyFlavourDefinition.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-taggerhf SOURCES jetTaggerHF.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(estimator-rho SOURCES rhoEstimator.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(subtractor-eventwiseconstituent SOURCES eventwiseConstituentSubtractor.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-sv-reconstruction SOURCES secondaryVertexReconstruction.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(quark-gluon-jets-producer SOURCES quarkGluonJetsProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -113,16 +125,19 @@ o2physics_add_dpl_workflow(emcal-matchedtracks-writer o2physics_add_dpl_workflow(emcal-cluster-hadronic-correction-task SOURCES emcalClusterHadronicCorrectionTask.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2::EMCALBase O2::EMCALReconstruction COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(slim-tables-producer SOURCES slimTablesProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(berkeley-tree-producer SOURCES berkeleyTreeProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/CMakeLists.txt b/PWGJE/TableProducer/Matching/CMakeLists.txt index c5480ffbddc..3ea0bfb3d33 100644 --- a/PWGJE/TableProducer/Matching/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/CMakeLists.txt @@ -16,171 +16,205 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-mc-ch SOURCES jetMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-full SOURCES jetMatchingMCFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-neutral SOURCES jetMatchingMCNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-d0-ch SOURCES jetMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dplus-ch SOURCES jetMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-ds-ch SOURCES jetMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dstar-ch SOURCES jetMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-lc-ch SOURCES jetMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-b0-ch SOURCES jetMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-bplus-ch SOURCES jetMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-xictoxipipi-ch SOURCES jetMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dielectron-ch SOURCES jetMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-v0-ch SOURCES jetMatchingMCV0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ch SOURCES jetMatchingMCSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-d0-ch SOURCES jetMatchingMCSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dplus-ch SOURCES jetMatchingMCSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ds-ch SOURCES jetMatchingMCSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dstar-ch SOURCES jetMatchingMCSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-lc-ch SOURCES jetMatchingMCSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-b0-ch SOURCES jetMatchingMCSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-bplus-ch SOURCES jetMatchingMCSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-xictoxipipi-ch SOURCES jetMatchingMCSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dielectron-ch SOURCES jetMatchingMCSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ch SOURCES jetMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-d0-ch SOURCES jetMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dplus-ch SOURCES jetMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ds-ch SOURCES jetMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dstar-ch SOURCES jetMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-lc-ch SOURCES jetMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-bplus-ch SOURCES jetMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-b0-ch SOURCES jetMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-xictoxipipi-ch SOURCES jetMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dielectron-ch SOURCES jetMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcd-sub-ch SOURCES jetMatchingMCDSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt index 52a7a0377c9..1b754ba9fb5 100644 --- a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt @@ -13,16 +13,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-data-ch-1 SOURCES jetMatchingDuplicatesChargedData1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcd-ch-1 SOURCES jetMatchingDuplicatesChargedMCD1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcp-ch-1 SOURCES jetMatchingDuplicatesChargedMCP1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt index 238e84ddc91..8d15ef727c8 100644 --- a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt @@ -14,101 +14,121 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ch SOURCES jetSubstructureMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-d0-ch SOURCES jetSubstructureMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dplus-ch SOURCES jetSubstructureMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ds-ch SOURCES jetSubstructureMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dstar-ch SOURCES jetSubstructureMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-lc-ch SOURCES jetSubstructureMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-b0-ch SOURCES jetSubstructureMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-bplus-ch SOURCES jetSubstructureMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-xictoxipipi-ch SOURCES jetSubstructureMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dielectron-ch SOURCES jetSubstructureMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ch SOURCES jetSubstructureMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-d0-ch SOURCES jetSubstructureMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dplus-ch SOURCES jetSubstructureMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ds-ch SOURCES jetSubstructureMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dstar-ch SOURCES jetSubstructureMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-lc-ch SOURCES jetSubstructureMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-b0-ch SOURCES jetSubstructureMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-bplus-ch SOURCES jetSubstructureMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-xictoxipipi-ch SOURCES jetSubstructureMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dielectron-ch SOURCES jetSubstructureMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index e010858ccbe..1cab772205b 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -86,6 +86,7 @@ o2physics_add_dpl_workflow(photon-isolation-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-correlation SOURCES photonChargedTriggerCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-emc-extensive-mc-qa @@ -94,10 +95,12 @@ o2physics_add_dpl_workflow(task-emc-extensive-mc-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-producer SOURCES photonChargedTriggerProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hadron-photon-correlation SOURCES hadronPhotonCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -299,6 +302,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-spectra-charged-gen SOURCES jetSpectraChargedGen.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(charged-jet-hadron @@ -471,6 +475,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-correlation-dmeson SOURCES jetCorrelationDmeson.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hf-debug @@ -505,10 +510,12 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hadrons-pid SOURCES jetHadronsPid.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hf-ang-substructure SOURCES jetHFAngularity.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-upc-qa @@ -517,10 +524,12 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(embedding-qa SOURCES embeddingQA.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(baryon-pair-in-jets SOURCES baryonPairInJets.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) endif() From 68526b1c00195f52104e28db44a3a43694e4a2af Mon Sep 17 00:00:00 2001 From: akyadav1963 <84583181+akyadav1963@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:07:40 +0530 Subject: [PATCH 289/648] [PWGLF] ptmultCorr: configurable pT-dependent DCAxy cut,fine DCAxy vs pT for sigma extraction (#18283) --- .../GlobalEventProperties/ptmultCorr.cxx | 194 ++++++++++++++---- 1 file changed, 159 insertions(+), 35 deletions(-) diff --git a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx index a819b5c808c..6de0a7a250f 100644 --- a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx @@ -120,12 +120,12 @@ const AxisSpec axisPhi{{0, o2::constants::math::PIQuarter, o2::constants::math:: const AxisSpec axisPhi2{629, 0, o2::constants::math::TwoPI, "#phi"}; const AxisSpec axisCent{100, 0, 100, "#Cent"}; const AxisSpec axisDeltaPt{50, -1.0, +1.0, "#Delta(pT)"}; -const AxisSpec axisDCAxy{600, -3.0, 3.0, "DCA_{xy} (cm)", "DCAxyAxis"}; // range ±3 cm for TPC-only case const AxisSpec axisGenPtVary = {kGenpTend - 1, +kGenpTbegin + 0.5, +kGenpTend - 0.5, "", "GenpTVaryAxis"}; const AxisSpec axisGenTrkType = {kGenTrkTypeend - 1, +kGenTrkTypebegin + 0.5, +kGenTrkTypeend - 0.5, "", "GenTrackTypeAxis"}; const AxisSpec axisRecTrkType = {kRecTrkTypeend - 1, +kRecTrkTypebegin + 0.5, +kRecTrkTypeend - 0.5, "", "RecTrackTypeAxis"}; const AxisSpec axisTrackType = {kTrackTypeend - 1, +kTrackTypebegin + 0.5, +kTrackTypeend - 0.5, "", "TrackTypeAxis"}; auto static constexpr KminCharge = 3.f; +static constexpr float kZeroFloat{0.f}; struct PtmultCorr { @@ -150,6 +150,69 @@ struct PtmultCorr { Configurable cfgMaxChi2ClsITS{"cfgMaxChi2ClsITS", 36.0f, "Maximum ITS chi2/cls"}; Configurable cfgDCAz{"cfgDCAz", 0.04, "DCAz cut in cm"}; + // ── pT-dependent DCAxy cut ────────────────────────────────────────────────── + // The THnSparses keep the FULL DCAxy distribution (needed for the template fit). + // When the flag is on, additional *DcaCut histograms (no DCAxy axis) are + // filled only with tracks passing |DCAxy| < N * (A + B/pT^C), using reco pT. + Configurable cfgApplyPtDepDcaXY{"cfgApplyPtDepDcaXY", false, "fill *DcaCut spectra with |DCAxy| < N*(A + B/pT^C)"}; + Configurable cfgDcaXYSigmaA{"cfgDcaXYSigmaA", 0.0015f, "sigma_DCAxy(pT) = A + B/pT^C : A (cm)"}; + Configurable cfgDcaXYSigmaB{"cfgDcaXYSigmaB", 0.0050f, "sigma_DCAxy(pT) = A + B/pT^C : B (cm (GeV/c)^C)"}; + Configurable cfgDcaXYSigmaC{"cfgDcaXYSigmaC", 1.1f, "sigma_DCAxy(pT) = A + B/pT^C : C"}; + Configurable cfgDcaXYNSigma{"cfgDcaXYNSigma", 7.f, "number of sigmas for the DCAxy cut"}; + + // DCAxy binning of the THnSparses, by regions in |DCAxy| from 0 outwards: + // region i spans (edge[i-1], edge[i]] with bin width width[i]. + // Default = previous uniform binning (20 um over +-5 cm). + // Finer core, e.g.: edges {0.05, 0.5, 5.0}, widths {0.0005, 0.005, 0.1} + Configurable> cfgDcaXYRegionEdges{"cfgDcaXYRegionEdges", {5.0}, "DCAxy binning: outer |DCAxy| edge of each region (cm), increasing"}; + Configurable> cfgDcaXYRegionWidths{"cfgDcaXYRegionWidths", {0.002}, "DCAxy binning: bin width in each region (cm)"}; + + // Fine DCAxy vs reco pT of primaries (sigma(pT) extraction, MC only) + ConfigurableAxis binsDcaXYFine{"binsDcaXYFine", {4000, -0.4, 0.4}, "fine DCAxy binning for sigma(pT) (cm)"}; + ConfigurableAxis binsPtDcaFine{"binsPtDcaFine", {VARIABLE_WIDTH, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.6, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 20.0, 30.0, 50.0}, "reco pT binning of the fine DCAxy histograms"}; + + bool passDcaXY(float pt, float dcaXY) const + { + if (pt <= kZeroFloat) { + return false; + } + const float sigma = cfgDcaXYSigmaA.value + cfgDcaXYSigmaB.value / std::pow(pt, cfgDcaXYSigmaC.value); + return std::fabs(dcaXY) < cfgDcaXYNSigma.value * sigma; + } + + static std::vector makeDcaXYEdges(const std::vector& regionEdges, const std::vector& regionWidths) + { + if (regionEdges.empty() || regionEdges.size() != regionWidths.size()) { + LOGF(fatal, "DCAxy binning: %zu region edges vs %zu widths", regionEdges.size(), regionWidths.size()); + } + std::vector pos{0.}; + double x0 = 0.; + for (size_t i = 0; i < regionEdges.size(); ++i) { + const double xMax = regionEdges[i]; + const double w = regionWidths[i]; + if (xMax <= x0 || w <= kZeroFloat) { + LOGF(fatal, "DCAxy binning: region %zu invalid (edge %g after %g, width %g)", i, xMax, x0, w); + } + const int n = std::max(1, static_cast(std::lround((xMax - x0) / w))); + for (int j = 1; j <= n; ++j) { + pos.push_back(x0 + j * (xMax - x0) / n); // integer steps: region edges are exact + } + if (std::fabs((xMax - x0) / n - w) > kWidthRelTol * w) { + LOGF(warning, "DCAxy binning: region %zu width adjusted from %g to %g cm to fit the range", i, w, (xMax - x0) / n); + } + x0 = xMax; + } + std::vector edges; + for (auto it = pos.rbegin(); it != pos.rend(); ++it) { + if (*it > kZeroFloat) { + edges.push_back(-*it); + } + } + edges.insert(edges.end(), pos.begin(), pos.end()); + LOGF(info, "DCAxy axis: %zu bins in [%g, %g] cm", edges.size() - 1, edges.front(), edges.back()); + return edges; + } + ConfigurableAxis ptHistBin{"ptHistBin", {200, 0., 20.}, ""}; ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, ""}; ConfigurableAxis binsImpactPar{"binsImpactPar", {VARIABLE_WIDTH, 0.0, 3.00065, 4.28798, 6.14552, 7.6196, 8.90942, 10.0897, 11.2002, 12.2709, 13.3167, 14.4173, 23.2518}, "Binning of the impact parameter axis"}; @@ -183,6 +246,7 @@ struct PtmultCorr { static constexpr int kZeroInt{0}; static constexpr float kOne{1.0f}; + static constexpr double kWidthRelTol{1e-3}; void init(InitContext const&) { @@ -192,6 +256,9 @@ struct PtmultCorr { AxisSpec centAxis = {centralityBinning, "Centrality", "CentralityAxis"}; AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; AxisSpec impactParAxis = {binsImpactPar, "Impact Parameter"}; + const AxisSpec axisDCAxy{makeDcaXYEdges(cfgDcaXYRegionEdges.value, cfgDcaXYRegionWidths.value), "DCA_{xy} (cm)", "DCAxyAxis"}; + const AxisSpec axisDcaXYFine{binsDcaXYFine, "DCA_{xy} (cm)", "DCAxyFineAxis"}; + const AxisSpec axisPtDcaFine{binsPtDcaFine, "#it{p}_{T}^{rec} (GeV/#it{c})", "pTDcaFineAxis"}; histos.add("EventHist", "EventHist", kTH1D, {axisEvent}, false); histos.add("VtxZHist", "VtxZHist", kTH1D, {axisVtxZ}, false); @@ -202,10 +269,16 @@ struct PtmultCorr { if (doprocessDataPbPb) { histos.add("hdatazvtxcent", "hdatazvtxcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hdatahistPbPb", "hdatahistPbPb", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistPbPbDcaCut", "hdatahistPbPbDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessDatapp) { histos.add("hdatahistpp", "hdatahistpp", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistppDcaCut", "hdatahistppDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessMCeffPbPb) { @@ -221,7 +294,14 @@ struct PtmultCorr { histos.add("hPbPbRecMCvtxzcent", "hPbPbRecMCvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hPbPbRecMCcent", "hPbPbRecMCcent", kTH1D, {axisCent}, false); histos.add("hPbPbRecMCdndpt", "hPbPbRecMCdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hPbPbRecMCdndptDcaCut", "hPbPbRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hPbPbEtaReso", "hPbPbEtaReso", kTH2D, {axisPt, axisDeltaPt}); + // DCAxy of reco primaries (global tracks, same MC collision) vs RECO pT and centrality + histos.add("hPbPbDcaXYPrimFine", "hPbPbDcaXYPrimFine", kTH3F, {axisPtDcaFine, axisDcaXYFine, centAxis}, false); + // primaries whose MC particle belongs to another MC collision (mis-association) + histos.add("hPbPbDcaXYPrimWrongColl", "hPbPbDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessMCeffpp) { @@ -233,7 +313,12 @@ struct PtmultCorr { histos.add("hppRecMCvtxz", "hppRecMCvtxz", kTH1D, {axisVtxZ}, false); histos.add("hppRecMCdndpt", "hppRecMCdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hppRecMCdndptDcaCut", "hppRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hppEtaReso", "hppEtaReso", kTH2D, {axisPt, axisDeltaPt}); + histos.add("hppDcaXYPrimFine", "hppDcaXYPrimFine", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); + histos.add("hppDcaXYPrimWrongColl", "hppDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessEvtLossSigLossMCpp || doprocessEvtLossSigLossMCPbPb) { @@ -389,6 +474,9 @@ struct PtmultCorr { return true; } + // All track cuts EXCEPT the pT-dependent DCAxy cut (the flat DCAz cut is included). + // The DCAxy cut is applied at the fill points via passDcaXY(), so that the + // THnSparses keep the full DCAxy distribution for the template fit. template bool isTrackSelected(CheckTrack const& track) { @@ -512,11 +600,14 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalonly, track.dcaXY()); - } else { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kITSonly, track.dcaXY()); + histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType); } } } @@ -539,11 +630,14 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalonly, track.dcaXY()); - } else { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kITSonly, track.dcaXY()); + histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), trkType); } } } @@ -651,23 +745,41 @@ struct PtmultCorr { auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + // fills the full-DCAxy THnSparse (templates) and, for tracks passing + // the DCAxy cut, the *DcaCut copy (spectrum / efficiency numerator) + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType); + } + }; - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hPbPbEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), trkType, Rectrack.dcaXY()); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hPbPbDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY(), RecCol.centFT0C()); + } else { + histos.fill(HIST("hPbPbDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -690,8 +802,7 @@ struct PtmultCorr { pid = kRecoMaterial; } // also fill the union bin as a cross-check (weak + material) - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } // duplicate-track (split) relabel — overrides origin, as before @@ -700,11 +811,9 @@ struct PtmultCorr { } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(pid), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(pid), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } } } // collision loop @@ -797,21 +906,39 @@ struct PtmultCorr { auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll), trkType, Rectrack.dcaXY()); + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), trkType); + } + }; + + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); + if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hppEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary), trkType, Rectrack.dcaXY()); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hppDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY()); + } else { + histos.fill(HIST("hppDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -834,19 +961,16 @@ struct PtmultCorr { pid = kRecoMaterial; } // union cross-check bin (weak + material) - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoSecondary), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } // duplicate-track (split) relabel — overrides origin, as before if (find(mclabels.begin(), mclabels.end(), Rectrack.mcParticleId()) != mclabels.end()) { pid = kRecoFake; } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(pid), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(pid), trkType, Rectrack.dcaXY()); + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), kGlobalplusITS, Rectrack.dcaXY()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg), trkType, Rectrack.dcaXY()); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } } // track (mcrec) loop } // collision loop From f03effc73c2bbc7a025e839bce8ef0df1f83f04f Mon Sep 17 00:00:00 2001 From: "Q.Y. Xia" <91366503+huinaibing@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:10:29 +0800 Subject: [PATCH 290/648] [PWGCF] Add pid-ch v2^2 correlation (#18312) --- PWGCF/Flow/Tasks/pidFlowPtCorr.cxx | 97 ++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index c7d9c1a80dd..60744d63d4a 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -85,7 +85,8 @@ enum : std::size_t { kC24 = 1, kC22Full = 2, kC32Gap = 3, - kC34Gap = 4 + kC34Gap = 4, + kC24Gap = 52 }; } // namespace charged @@ -103,7 +104,8 @@ enum : std::size_t { kC22Pure = 29, kC32Pure = 32, kC22FullA = 35, - kC22FullB = 36 + kC22FullB = 36, + kC24PIDChargedGap = 48 }; } // namespace pion @@ -121,7 +123,8 @@ enum : std::size_t { kC22Pure = 30, kC32Pure = 33, kC22FullA = 37, - kC22FullB = 38 + kC22FullB = 38, + kC24PIDChargedGap = 49 }; } // namespace kaon @@ -139,7 +142,8 @@ enum : std::size_t { kC22Pure = 31, kC32Pure = 34, kC22FullA = 39, - kC22FullB = 40 + kC22FullB = 40, + kC24PIDChargedGap = 50 }; } // namespace proton @@ -154,12 +158,13 @@ enum : std::size_t { kC24B = 44, kC22Pure = 45, kC22FullA = 46, - kC22FullB = 47 + kC22FullB = 47, + kC24PIDChargedGap = 51 }; } // namespace unidentified enum : std::size_t { - kCount = 48 + kCount = 53 }; } // namespace corrConfigIndex @@ -369,6 +374,7 @@ struct PidFlowPtCorr { ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.20, 2.40, 2.60, 2.80, 3.00, 3.50, 4.00, 4.50, 5.00, 5.50, 6.00, 10.0}, "pt (GeV)"}; ConfigurableAxis cfgaxisTPCDedx{"cfgaxisTPCDedx", {500, 0.0f, 1000.0f}, "d#it{E}/d#it{x} axis for detector-PID QA"}; ConfigurableAxis cfgaxisTOFDelta{"cfgaxisTOFDelta", {200, -1000.0f, 1000.0f}, "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTOFTime{"cfgaxisTOFTime", {10000, 0.0f, 2.0e6f}, "TOF #it{t} - #it{t}_{ev} (ps) axis for detector-PID QA"}; ConfigurableAxis cfgaxisMeanPt{"cfgaxisMeanPt", {300, 0, 3}, "pt (GeV)"}; ConfigurableAxis cfgaxisNch{"cfgaxisNch", {3000, 0.5, 3000.5}, "Nch"}; ConfigurableAxis cfgaxisLocalDensity{"cfgaxisLocalDensity", {200, 0, 600}, "local density"}; @@ -398,7 +404,7 @@ struct PidFlowPtCorr { using TracksPIDForDetectorPidQA = soa::Join; // data tracks filter using AodTracks = soa::Filtered>; - using AodTracksForDetectorPidQA = soa::Filtered>; + using AodTracksForDetectorPidQA = soa::Filtered>; // data collisions filter using AodCollisions = soa::Filtered>; @@ -618,10 +624,12 @@ struct PidFlowPtCorr { registry.add("DetectorPidQA/TPC/dEdxPi", "TPC d#it{E}/d#it{x} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxKa", "TPC d#it{E}/d#it{x} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxPr", "TPC d#it{E}/d#it{x} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + registry.add("DetectorPidQA/TPC/dEdxAll", "TPC d#it{E}/d#it{x} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TOF/deltaPi", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/deltaKa", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/deltaPr", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); + registry.add("DetectorPidQA/TOF/tofTimeAll", "TOF #it{t} - #it{t}_{ev} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFTime}}); // run by run QA hists /** @@ -868,6 +876,10 @@ struct PidFlowPtCorr { oba4Ch->Add(new TNamed("hMeanPtWeightFull", "hMeanPtWeightFull")); // end fill TObjArray for charged + // Clone the shared profiles before appending the charged-only SC baseline. + auto* oba4PID = dynamic_cast(oba4Ch->Clone()); + oba4Ch->Add(new TNamed("c24Gap", "c24Gap")); + // init fFCCh if (switchsOpts.cfgOutputCharged.value) { fFCCh->SetName("FlowContainerCharged"); @@ -877,13 +889,14 @@ struct PidFlowPtCorr { // init fFCPID // note that need to add c22pure and c32pure - auto* oba4PID = dynamic_cast(oba4Ch->Clone()); oba4PID->Add(new TNamed("c22pure", "c22pure")); oba4PID->Add(new TNamed("c32pure", "c32pure")); oba4PID->Add(new TNamed("covV2PtPID", "covV2PtPID")); oba4PID->Add(new TNamed("c22TrackWeightPID", "c22TrackWeightPID")); oba4PID->Add(new TNamed("hMeanPtWeightCharged", "hMeanPtWeightCharged")); + // Raw mixed <4> for SC/NSC; reuse PID c22pure and charged c22 as <2>. + oba4PID->Add(new TNamed("c24PIDChargedGap", "c24PIDChargedGap")); if (switchsOpts.cfgOutputPion.value) { fFCPi->SetName("FlowContainerPi"); @@ -1006,6 +1019,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olPiN", -etaMax, 0, 1, gfwBitMask::pion::kOverlap); fGFW->AddRegion("olPiP", 0, etaMax, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiN08", -etaMax, -etaGap, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiP08", etaGap, etaMax, 1, gfwBitMask::pion::kOverlap); // Kaon POI and overlap regions. fGFW->AddRegion("poiKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kPOI); @@ -1015,6 +1030,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olKaN", -etaMax, 0, 1, gfwBitMask::kaon::kOverlap); fGFW->AddRegion("olKaP", 0, etaMax, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaP08", etaGap, etaMax, 1, gfwBitMask::kaon::kOverlap); // Proton POI and overlap regions. fGFW->AddRegion("poiPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kPOI); @@ -1024,6 +1041,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("olPrN", -etaMax, 0, 1, gfwBitMask::proton::kOverlap); fGFW->AddRegion("olPrP", 0, etaMax, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrP08", etaGap, etaMax, 1, gfwBitMask::proton::kOverlap); // Tracks for which the PID selection returns -1. They use the charged // pT range and corrections, but have independent POI and overlap masks. @@ -1033,6 +1052,8 @@ struct PidFlowPtCorr { fGFW->AddRegion("poiUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kPOI); fGFW->AddRegion("olUnidentifiedN", -etaMax, 0, 1, gfwBitMask::unidentified::kOverlap); fGFW->AddRegion("olUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedN08", -etaMax, -etaGap, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedP08", etaGap, etaMax, 1, gfwBitMask::unidentified::kOverlap); // end data region add // pushback @@ -1090,6 +1111,16 @@ struct PidFlowPtCorr { corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN reffull | olUnidentifiedN {2 -2}", "UnidentifiedFull22a", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedP reffull | olUnidentifiedP {2 -2}", "UnidentifiedFull22b", false)); // 47 + // One POI and one reference on each eta side, with distinct tracks. + // Each overlap region has the same eta range as its POI/reference pair. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 refN08 | olPiN08 {2 2} poiPiP08 refP08 | olPiP08 {-2 -2}", "PionPIDChargedGap24", false)); // 48 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 refN08 | olKaN08 {2 2} poiKaP08 refP08 | olKaP08 {-2 -2}", "KaonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 refN08 | olPrN08 {2 2} poiPrP08 refP08 | olPrP08 {-2 -2}", "ProtonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN08 refN08 | olUnidentifiedN08 {2 2} poiUnidentifiedP08 refP08 | olUnidentifiedP08 {-2 -2}", "UnidentifiedPIDChargedGap24", false)); // 51 + + // Charged-charged SC baseline: two distinct reference tracks per eta side. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("refN08 {2 2} refP08 {-2 -2}", "Ref08Gap24", false)); // 52 + if (corrconfigs.size() != corrConfigIndex::kCount) { LOGF(fatal, "Correlation index table expects %zu configurations, but %zu were created", static_cast(corrConfigIndex::kCount), corrconfigs.size()); } @@ -2767,7 +2798,10 @@ struct PidFlowPtCorr { // Fill GFW and counters using the same species-specific POI pT range. if (pid == MyParticleType::kPion) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kOverlap, wacc * weff); + // Only tracks in both pT selections contribute to POI-ref overlap. + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiPi"), track.phi()); registry.fill(HIST("hPhicorrPi"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEPi"), track.phi(), waccPid * weffPid); @@ -2776,7 +2810,9 @@ struct PidFlowPtCorr { numOfPi++; } else if (pid == MyParticleType::kKaon) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kOverlap, wacc * weff); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiKa"), track.phi()); registry.fill(HIST("hPhicorrKa"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEKa"), track.phi(), waccPid * weffPid); @@ -2785,7 +2821,9 @@ struct PidFlowPtCorr { numOfKa++; } else if (pid == MyParticleType::kProton) { fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kPOI); - fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kOverlap, wacc * weff); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kOverlap, wacc * weff); + } registry.fill(HIST("hPhiPr"), track.phi()); registry.fill(HIST("hPhicorrPr"), track.phi(), waccPid); registry.fill(HIST("hPhiCorrNUANUEPr"), track.phi(), waccPid * weffPid); @@ -2813,6 +2851,8 @@ struct PidFlowPtCorr { if (nch > 0) { fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + // Raw gap <4> for SC(ch,ch); reuse c22 for both two-particle factors. + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); @@ -2854,6 +2894,14 @@ struct PidFlowPtCorr { bool filledPr = fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, "c22pure"); bool filledUnidentified = fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, "c22pure"); + // SC/NSC components only: the mixed raw <4> uses its GFW tuple weight + // and the same bootstrap draw as the existing c22pure and charged c22. + // Existing <2> profiles retain their own valid-event samples/weights. + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, "c32pure"); fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, "c32pure"); fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, "c32pure"); @@ -3987,32 +4035,43 @@ struct PidFlowPtCorr { // start filling graphs for (const auto& track : tracks) { - // Use the same track-quality, PID and species pT selection as processData. + // Use the same track-quality selection as processData, but do not + // require a PID hypothesis for the all-particle QA histograms. if (!trackSelectedForFlow(track)) { continue; } + const float pt = track.pt(); + if (std::abs(track.sign()) == 1) { + registry.fill(HIST("DetectorPidQA/TPC/dEdxAll"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/tofTimeAll"), pt, track.tofSignal() - track.tofEvTime()); + } + } + + // The species-dependent histograms additionally require an identified + // PID and the corresponding POI pT range. const int pid = getPidConfigurable(track); - if (!isWithinPOIPtRange(pid, track.pt())) { + if (!isWithinPOIPtRange(pid, pt)) { continue; } switch (pid) { case MyParticleType::kPion: - registry.fill(HIST("DetectorPidQA/TPC/dEdxPi"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxPi"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaPi"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaPi"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; case MyParticleType::kKaon: - registry.fill(HIST("DetectorPidQA/TPC/dEdxKa"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxKa"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaKa"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaKa"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; case MyParticleType::kProton: - registry.fill(HIST("DetectorPidQA/TPC/dEdxPr"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("DetectorPidQA/TPC/dEdxPr"), pt, track.tpcSignal()); if (track.hasTOF()) { - registry.fill(HIST("DetectorPidQA/TOF/deltaPr"), track.p(), o2::aod::pidutils::tofExpSignalDiff(track)); + registry.fill(HIST("DetectorPidQA/TOF/deltaPr"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); } break; default: From 1d6e6e7171e9e6f3b73a4590d21a808e265cbfe7 Mon Sep 17 00:00:00 2001 From: AlexianL <123153896+AlexianL@users.noreply.github.com> Date: Thu, 15 Oct 2026 19:55:46 +0200 Subject: [PATCH 291/648] [PWGLF,PWGMM] mftReassociationValidation.cxx : add plots for DCA template fit study (#18293) --- .../Mult/Tasks/mftReassociationValidation.cxx | 138 ++++++++++++++++-- 1 file changed, 124 insertions(+), 14 deletions(-) diff --git a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx index 59b9a20e76b..65cb143aa0a 100644 --- a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx +++ b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx @@ -272,6 +272,7 @@ struct MftReassociationValidation { Configurable fillGeneralHistograms{"fillGeneralHistograms", true, "Fill general reassociation efficiencyhistograms"}; Configurable fillSpecificHistograms{"fillSpecificHistograms", true, "Fill specific histograms (DCA, DeltaPos)"}; Configurable fillParametersHistograms{"fillParametersHistograms", true, "Fill parameters histograms"}; + Configurable fillDcaTemplateFit{"fillDcaTemplateFit", true, "Fill DCA template histograms for primary/secondary fraction fit"}; Configurable keepOnlyPhysicalPrimary{"keepOnlyPhysicalPrimary", true, "Keep only physical primary particles"}; Configurable keepOnlySecondaries{"keepOnlySecondaries", false, "Keep only secondary particles"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; @@ -309,8 +310,6 @@ struct MftReassociationValidation { Configurable cutOrphanTracksExplicitly{"cutOrphanTracksExplicitly", false, "if true, cut orphan tracks explicitly"}; Configurable cutOnDcaXY{"cutOnDcaXY", false, "if true, cut on DCA XY"}; Configurable cutOnDcaZ{"cutOnDcaZ", false, "if true, cut on DCA Z"}; - Configurable dcaXYMax{"dcaXYMax", 2.0f, "Maximum value for DCA XY"}; - Configurable dcaZMax{"dcaZMax", 10.0f, "Maximum value for DCA Z"}; Configurable etaMftTrackMax{"etaMftTrackMax", -2.4f, "Maximum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMin{"etaMftTrackMin", -3.36f, "Minimum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMaxFilter{"etaMftTrackMaxFilter", -2.0f, "Maximum value for the eta of MFT tracks when used in filter"}; @@ -406,6 +405,8 @@ struct MftReassociationValidation { ConfigurableAxis axisDcaX{"axisDcaX", {500, -0.1, 2.1}, "DCAx binning (cm)"}; ConfigurableAxis axisDcaY{"axisDcaY", {500, -0.1, 2.1}, "DCAy binning (cm)"}; ConfigurableAxis axisDcaZ{"axisDcaZ", {500, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis axisDcaXYfit{"axisDcaXYfit", {500, 0., 2.}, "DCAxy binning (cm) for the template fit"}; + ConfigurableAxis axisDcaZfit{"axisDcaZfit", {500, -1., 1.}, "DCAz binning (cm) for the template fit"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for histograms"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "phi axis for histograms"}; ConfigurableAxis axisPt{"axisPt", {72, 0, 36}, "pt axis for histograms"}; @@ -578,6 +579,11 @@ struct MftReassociationValidation { registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); } } + + if (configTask.fillDcaTemplateFit) { + registry.add("MC/2D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/2D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + } } void addMftMonteCarloHistograms3D() @@ -689,6 +695,20 @@ struct MftReassociationValidation { registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); } } + + if (configTask.fillDcaTemplateFit) { + // 3D reassociated + registry.add("MC/3D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_prim", "Primary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_sec", "Secondary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + + // Non-ambiguous + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim", "NonAmb Primary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec", "NonAmb Secondary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim", "NonAmb Primary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec", "NonAmb Secondary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + } } template @@ -833,6 +853,10 @@ struct MftReassociationValidation { if (doprocessData) { addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/2D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + } } if (doprocessMcReassociated2d) { @@ -857,6 +881,18 @@ struct MftReassociationValidation { addMftMonteCarloHistograms3D(); } + if (doprocessData3d) { + addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/3D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/hPtVsDcaZ", "Data DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaXY", "NonAmb Data DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaZ", "NonAmb Data DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + } + } + } // End of init() function // ========================= @@ -1108,11 +1144,11 @@ struct MftReassociationValidation { } } - if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.dcaXYMax) { + if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.mftMaxDCAxy) { return false; } registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaXYCut); - if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.dcaZMax) { + if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { return false; } registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaZCut); @@ -1199,31 +1235,77 @@ struct MftReassociationValidation { for (const auto& reassociated2dMftTrack : reassociated2dMftTracks) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); auto templatedMftTrack = reassociated2dMftTrack.template mfttrack_as(); o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); std::array dcaInfOrig; trackPar.propagateToDCAhelix(bZ, {collision.posX(), collision.posY(), collision.posZ()}, dcaInfOrig); - auto dcaXYoriginal = 999.f; - dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); + // auto dcaXYoriginal = 999.f; + // dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); - if (!isAcceptedMftTrack(templatedMftTrack, false, true, dcaXYoriginal, dcaInfOrig[2])) { + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated2dMftTrack.bestDCAXY(), dcaInfOrig[2])) { continue; } - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); - - if (isAmbiguousMftTrack(reassociated2dMftTrack, true)) { - registry.fill(HIST("Data/hReassociation2dMftTracks"), Reassociation2dMftTracks::NotReassociated2dMftTracks); + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/2D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated2dMftTrack.bestDCAXY()); } } // end of loop over reassociated MFT tracks - - // TO-DO the same for reassociated3d (change the histograms) } PROCESS_SWITCH(MftReassociationValidation, processData, "Process MFT reassociation validation for DATA", false); + void processData3d(FilteredCollisionsWSelMult::iterator const& collision, + FilteredMftTracks const& /*mftTracks*/, + soa::SmallGroups const& reassociated3dMftTracks, + aod::BCsWithTimestamps const&) + { + auto bc = collision.template bc_as(); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } + + if (!isAcceptedCollision(collision, false)) { + return; + } + + for (const auto& reassociated3dMftTrack : reassociated3dMftTracks) { + auto templatedMftTrack = reassociated3dMftTrack.template mfttrack_as(); + + // // Compute DCA at original collision for isAcceptedMftTrack + // o2::track::TrackParCovFwd trackPar = + // o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); + // std::array dcaInfOrig; + // trackPar.propagateToDCAhelix( + // bZ, + // {collision.posX(), collision.posY(), collision.posZ()}, + // dcaInfOrig); + // const float dcaXYoriginal = + // std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + + // dcaInfOrig[1] * dcaInfOrig[1]); + + // Use existing track selection function (unchanged) + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ())) { + continue; + } + + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + + // The same for nonambi only + if (reassociated3dMftTrack.ambDegree() == 1) { + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + } + PROCESS_SWITCH(MftReassociationValidation, processData3d, "Process DCA fit histograms for DATA 3D reassociation", false); + void processCreateLookupTable(FilteredCollisionsWSelMultMcLabels const& collisions, soa::Join const& mcCollisions) { recoVtxX.clear(); @@ -1347,6 +1429,15 @@ struct MftReassociationValidation { registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } else { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; @@ -1734,6 +1825,25 @@ struct MftReassociationValidation { registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } else { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; From 97d0c4504643d94b4b5c3b02a0038aef26240961 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Thu, 15 Oct 2026 19:59:49 +0200 Subject: [PATCH 292/648] [ALICE3] Add hist to qa singleparticle needed for signal injection in DittoMC (#18291) --- ALICE3/Tasks/alice3-qa-singleparticle.cxx | 716 ++++++++-------------- 1 file changed, 241 insertions(+), 475 deletions(-) diff --git a/ALICE3/Tasks/alice3-qa-singleparticle.cxx b/ALICE3/Tasks/alice3-qa-singleparticle.cxx index 25d21fcc7cd..851f67adbe7 100644 --- a/ALICE3/Tasks/alice3-qa-singleparticle.cxx +++ b/ALICE3/Tasks/alice3-qa-singleparticle.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// -/// \file qa-singleparticle.cxx +/// \file alice3-qa-singleparticle.cxx /// \author Nicolò Jacazio nicolo.jacazio@cern.ch /// \brief Task to monitor the single particle QA, at the particle and track level, showing the tracked and the origin of particles /// @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -38,7 +37,9 @@ #include #include +#include #include +#include #include using namespace o2; @@ -46,26 +47,50 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct Alice3SingleParticle { - Service pdg; - Configurable PDG{"PDG", 2212, "PDG code of the particle of interest"}; - Configurable IsStable{"IsStable", 0, "Flag to check stable particles"}; + Service pdg{}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable ptBins{"pt-bins", 500, "Number of pT bins"}; - Configurable ptMin{"pt-min", 0.f, "Lower limit in pT"}; - Configurable ptMax{"pt-max", 10.f, "Upper limit in pT"}; - Configurable etaBins{"eta-bins", 500, "Number of eta bins"}; - Configurable etaMin{"eta-min", -3.f, "Lower limit in eta"}; - Configurable etaMax{"eta-max", 3.f, "Upper limit in eta"}; - Configurable yMin{"y-min", -3.f, "Lower limit in y"}; - Configurable yMax{"y-max", 3.f, "Upper limit in y"}; - Configurable prodBins{"prod-bins", 100, "Number of production vertex bins"}; - Configurable prodMin{"prod-min", -1.f, "Lower limit in production vertex"}; - Configurable prodMax{"prod-max", 1.f, "Upper limit in production vertex"}; - Configurable prodBinsZ{"prod-bins-z", 100, "Number of production vertex bins"}; - Configurable prodMinZ{"prod-min-z", -10.f, "Lower limit in production vertex along Z"}; - Configurable prodMaxZ{"prod-max-z", 10.f, "Upper limit in production vertex along Z"}; - Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; - Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + Configurable pdgCode{"pdgCode", 2212, "PDG code of the particle of interest"}; + + struct : ConfigurableGroup { + std::string prefix = "settings"; + Configurable isStable{"isStable", 0, "Flag to check stable particles"}; + Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; + Configurable nchEtaMax{"nchEtaMax", 0.5f, "|eta| acceptance of the Nch estimator (use the Ditto tune mActivityEtaMax)"}; + Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + } settings; + + // The pT, eta, y and production-vertex-z axes also define the selection of the particles of interest + struct : ConfigurableGroup { + std::string prefix = "axes"; + ConfigurableAxis axisMomentum{"axisMomentum", {500, 0.f, 10.f}, "Binning for p and pT"}; + ConfigurableAxis axisMomentumComponent{"axisMomentumComponent", {500, -10.f, 10.f}, "Binning for px, py and pz"}; + ConfigurableAxis axisEnergy{"axisEnergy", {500, 0.f, 1000.f}, "Binning for the energy"}; + ConfigurableAxis axisEta{"axisEta", {500, -3.f, 3.f}, "Binning for eta"}; + ConfigurableAxis axisY{"axisY", {200, -3.f, 3.f}, "Binning for rapidity"}; + ConfigurableAxis axisVtxXY{"axisVtxXY", {100, -1.f, 1.f}, "Binning for the event vertex in X and Y"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {100, -20.f, 20.f}, "Binning for the event vertex in Z"}; + ConfigurableAxis axisProdXY{"axisProdXY", {100, -1.f, 1.f}, "Binning for the production vertex in X and Y"}; + ConfigurableAxis axisProdZ{"axisProdZ", {100, -10.f, 10.f}, "Binning for the production vertex in Z"}; + ConfigurableAxis axisProdRadius{"axisProdRadius", {100, 0.f, 2.f}, "Binning for the production vertex radius"}; + ConfigurableAxis axisNch{"axisNch", {150, 0.f, 150.f}, "Charged-particle multiplicity"}; + ConfigurableAxis axisNSignal{"axisNSignal", {20, -0.5f, 19.5f}, "Number of selected particles of interest per collision"}; + } axes; + + std::vector particlesOfInterest; // global indices of the MC particles selected in genericProcessParticles, per DF + + // Selection range [min, max) of the particles of interest, taken from an axis so that every selected particle is inside its histograms + struct Range { + double min = 0.; + double max = 0.; + bool contains(double value) const { return value >= min && value < max; } + }; + Range ptRange, etaRange, yRange, prodZRange; + + static Range getRange(const ConfigurableAxis& axis) + { + const AxisSpec spec{axis}; + return {spec.binEdges.front(), spec.binEdges.back()}; + } void init(InitContext&) { @@ -95,123 +120,123 @@ struct Alice3SingleParticle { pdg->AddParticle("helium4", "helium4", o2::constants::physics::MassAlpha, kTRUE, 0.0, 6, "Nucleus", 1000020040); pdg->AddAntiParticle("anti-helium4", -1000020040); - const TString tit = Form("%i", PDG.value); - const AxisSpec axisVx{100, -1, 1, "Vtx_{x}"}; - const AxisSpec axisVy{100, -1, 1, "Vtx_{y}"}; - const AxisSpec axisVz{100, -20, 20, "Vtx_{z}"}; + ptRange = getRange(axes.axisMomentum); + etaRange = getRange(axes.axisEta); + yRange = getRange(axes.axisY); + prodZRange = getRange(axes.axisProdZ); + LOG(info) << "Selecting particles of interest in pT [" << ptRange.min << ", " << ptRange.max << "), eta [" << etaRange.min << ", " << etaRange.max + << "), y [" << yRange.min << ", " << yRange.max << "), production z [" << prodZRange.min << ", " << prodZRange.max << ")"; + + const TString titlePdgCode = Form("%i", pdgCode.value); const AxisSpec axisPDGs{100, 0.f, 100.f, "PDG code"}; const AxisSpec axisCharge{21, -10.5f, 10.5f, "Charge (e/3)"}; - const AxisSpec axisP{ptBins, ptMax, ptMax, "#it{p} (GeV/#it{c})"}; - const AxisSpec axisPt{ptBins, ptMin, ptMax, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisPtMC{ptBins, ptMin, ptMax, "#it{p}_{T}^{MC} (GeV/#it{c})"}; - const AxisSpec axisPx{ptBins, -ptMax, ptMax, "#it{p}_{x} (GeV/#it{c})"}; - const AxisSpec axisPy{ptBins, -ptMax, ptMax, "#it{p}_{y} (GeV/#it{c})"}; - const AxisSpec axisPz{ptBins, -ptMax, ptMax, "#it{p}_{z} (GeV/#it{c})"}; - const AxisSpec axisEta{etaBins, etaMin, etaMax, "#it{#eta}"}; - const AxisSpec axisY{etaBins, yMin, yMax, "#it{y}"}; - const AxisSpec axisE{etaBins, 0, 1000, "E"}; - const AxisSpec axisProdx{prodBins, prodMin, prodMax, "Prod. Vertex X (cm)"}; - const AxisSpec axisPrody{prodBins, prodMin, prodMax, "Prod. Vertex Y (cm)"}; - const AxisSpec axisProdz{prodBinsZ, prodMinZ, prodMaxZ, "Prod. Vertex Z (cm)"}; - const AxisSpec axisProdRadius{prodBins, 0., 2. * prodMax, "Prod. Vertex Radius (cm)"}; - - histos.add("event/VtxX", "Vertex X", kTH1D, {axisVx}); - histos.add("event/VtxY", "Vertex Y", kTH1D, {axisVy}); - histos.add("event/VtxZ", "Vertex Z", kTH1D, {axisVz}); + + histos.add("AnalysedParticlePdgCode", "Analysed particle PDG code", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticlePdgCode"))->SetBinContent(1, pdgCode); + + const auto* particle = pdg->GetParticle(pdgCode); + histos.add("AnalysedParticleMass", "Analysed particle mass", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticleMass"))->SetBinContent(1, particle ? particle->Mass() : -1.); + + histos.add("event/VtxX", "Vertex X", kTH1D, {axes.axisVtxXY}); + histos.add("event/VtxY", "Vertex Y", kTH1D, {axes.axisVtxXY}); + histos.add("event/VtxZ", "Vertex Z", kTH1D, {axes.axisVtxZ}); + histos.add("event/Nch", "Events;N_{ch};Events", kTH1D, {axes.axisNch}); + histos.add("particle/NSignalVsNch", "Particles of interest per event " + titlePdgCode + ";N_{ch};N_{signal}", kTH2D, {axes.axisNch, axes.axisNSignal}); histos.add("particle/PDGs", "Particle PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsPrimaries", "Particle PDGs of Primaries", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsSecondaries", "Particle PDGs of Secondaries", kTH2D, {axisPDGs, axisCharge}); - auto h = histos.add("particle/producedProcess", "Particle process of production " + tit, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); + auto h = histos.add("particle/producedProcess", "Particle process of production " + titlePdgCode, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); for (int i = 0; i < kMaxMCProcess; i++) { h->GetXaxis()->SetBinLabel(i + 1, TMCProcessName[i]); } - histos.add("particle/Pt", "Particle Pt " + tit, kTH1D, {axisPt}); - histos.add("particle/P", "Particle P " + tit, kTH1D, {axisP}); - histos.add("particle/primariesPt", "Particle Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("particle/primariesP", "Particle P (primary) " + tit, kTH1D, {axisP}); - histos.add("particle/secondariesPt", "Particle Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("particle/secondariesP", "Particle P (secondary) " + tit, kTH1D, {axisP}); - histos.add("particle/prodVx", "Particle Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/prodVy", "Particle Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/prodVz", "Particle Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/prodRadius", "Particle Prod. Vertex Radius " + tit, kTH1D, {axisProdRadius}); - histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + tit, kTH2D, {axisPt, axisProdx}); - histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + tit, kTH2D, {axisPt, axisPrody}); - histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + tit, kTH2D, {axisPt, axisProdz}); - histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisEta, axisProdRadius}); - histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/Eta", "Particle Eta " + tit, kTH1D, {axisEta}); - histos.add("particle/primariesEta", "Particle Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("particle/secondariesEta", "Particle Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("particle/Y", "Particle Y " + tit, kTH1D, {axisY}); - histos.add("particle/primariesY", "Particle Y (primary)" + tit, kTH1D, {axisY}); - histos.add("particle/secondariesY", "Particle Y (secondary)" + tit, kTH1D, {axisY}); - histos.add("particle/EvsPz", "Particle E vs Pz " + tit, kTH2D, {axisE, axisPz}); - histos.add("particle/YvzPz", "Particle Y vs Pz " + tit, kTH2D, {axisY, axisPz}); - histos.add("particle/EtavzPz", "Particle Eta vs Pz " + tit, kTH2D, {axisEta, axisPz}); - histos.add("particle/PtvzPz", "Particle Pt vs Pz " + tit, kTH2D, {axisPt, axisPz}); - histos.add("particle/PvzPz", "Particle P vs Pz " + tit, kTH2D, {axisP, axisPz}); - histos.add("particle/Px", "Particle Px " + tit, kTH1D, {axisPx}); - histos.add("particle/Py", "Particle Py " + tit, kTH1D, {axisPy}); - histos.add("particle/Pz", "Particle Pz " + tit, kTH1D, {axisPz}); - histos.add("particle/decayDistanceX", "Particle decay distance " + tit, kTH1D, {axisProdx}); - histos.add("particle/decayDistanceY", "Particle decay distance " + tit, kTH1D, {axisPrody}); - histos.add("particle/decayDistanceZ", "Particle decay distance " + tit, kTH1D, {axisProdz}); - histos.add("particle/decayDistanceVsPt", "Particle decay distance " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/decayCtau", "Particle decay distance " + tit, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); - - histos.add("particle/daughters/Number", "Number of Daughters " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/daughters/PDGs", "Daughters PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - - histos.add("particle/mothers/Number", "Number of Mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/PDGs", "Mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Pt", "Particle Pt " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/P", "Particle P " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/primariesPt", "Particle Pt (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/primariesP", "Particle P (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/secondariesPt", "Particle Pt (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/secondariesP", "Particle P (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("particle/prodVx", "Particle Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/prodVy", "Particle Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/prodVz", "Particle Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); + histos.add("particle/prodRadius", "Particle Prod. Vertex Radius " + titlePdgCode, kTH1D, {axes.axisProdRadius}); + histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); + histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); + histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); + histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); + histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdXY}); + histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdXY}); + histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdZ}); + histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisEta, axes.axisProdRadius}); + histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/Eta", "Particle Eta " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("particle/primariesEta", "Particle Eta (primary) " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("particle/secondariesEta", "Particle Eta (secondary) " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("particle/Y", "Particle Y " + titlePdgCode, kTH1D, {axes.axisY}); + histos.add("particle/primariesY", "Particle Y (primary)" + titlePdgCode, kTH1D, {axes.axisY}); + histos.add("particle/secondariesY", "Particle Y (secondary)" + titlePdgCode, kTH1D, {axes.axisY}); + histos.add("particle/EvsPz", "Particle E vs Pz " + titlePdgCode, kTH2D, {axes.axisEnergy, axes.axisMomentumComponent}); + histos.add("particle/YvzPz", "Particle Y vs Pz " + titlePdgCode, kTH2D, {axes.axisY, axes.axisMomentumComponent}); + histos.add("particle/EtavzPz", "Particle Eta vs Pz " + titlePdgCode, kTH2D, {axes.axisEta, axes.axisMomentumComponent}); + histos.add("particle/PtvzPz", "Particle Pt vs Pz " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentumComponent}); + histos.add("particle/PvzPz", "Particle P vs Pz " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentumComponent}); + histos.add("particle/Px", "Particle Px " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); + histos.add("particle/Py", "Particle Py " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); + histos.add("particle/Pz", "Particle Pz " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); + histos.add("particle/decayDistanceX", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/decayDistanceY", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/decayDistanceZ", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdZ}); + histos.add("particle/decayDistanceVsPt", "Particle decay distance " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/decayCtau", "Particle decay distance " + titlePdgCode, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); + + histos.add("particle/daughters/Number", "Number of Daughters " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/daughters/PDGs", "Daughters PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); + histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + + histos.add("particle/mothers/Number", "Number of Mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/PDGs", "Mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); + histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); // Go up one generation - histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); + histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); + histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); if (doprocessParticleOnly) { return; } histos.add("track/PDGs", "Track PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axisPt}); + histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axes.axisMomentum}); histos.add("track/tofPDGs", "Track wTOF PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/Pt", "Track Pt " + tit, kTH1D, {axisPt}); - histos.add("track/primariesPt", "Track Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("track/secondariesPt", "Track Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + tit, kTH2D, {axisPt, axisPtMC}); - histos.add("track/Eta", "Track Eta " + tit, kTH1D, {axisEta}); - histos.add("track/primariesEta", "Track Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("track/secondariesEta", "Track Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("track/primaries", "Source for primaries " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("track/secondaries", "Source for secondaries " + tit, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/Pt", "Track Pt " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("track/primariesPt", "Track Pt (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("track/secondariesPt", "Track Pt (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); + histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentum}); + histos.add("track/Eta", "Track Eta " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("track/primariesEta", "Track Eta (primary) " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("track/secondariesEta", "Track Eta (secondary) " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("track/primaries", "Source for primaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/secondaries", "Source for secondaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); } template @@ -230,236 +255,102 @@ struct Alice3SingleParticle { return Form("%i", particle.pdgCode()); } - void processStandard(const o2::aod::McCollisions& colls, - const soa::Join& tracks, - const aod::McParticles& mcParticles) + // Nch estimator: charged physical primaries in |eta| < settings.nchEtaMax + template + bool isNchParticle(ParticleType const& particle) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= settings.nchEtaMax) { + return false; } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { - continue; - } - histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - const auto& daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - bool filledDistance = false; - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - if (filledDistance) { - continue; - } - filledDistance = true; - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy() + daughter.vz() * daughter.vz())); - - const float dx = daughter.vx() - mcParticle.vx(); - const float dy = daughter.vy() - mcParticle.vy(); - const float dz = daughter.vz() - mcParticle.vz(); - histos.fill(HIST("particle/decayDistanceX"), dx); - histos.fill(HIST("particle/decayDistanceY"), dy); - histos.fill(HIST("particle/decayDistanceZ"), dz); - histos.fill(HIST("particle/decayDistanceVsPt"), mcParticle.pt(), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz))); - if (pdg->GetParticle(mcParticle.pdgCode())) { - const float mass = pdg->GetParticle(mcParticle.pdgCode())->Mass(); - histos.fill(HIST("particle/decayCtau"), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz)) / mcParticle.pt() * mass * 10 * 1000); - } - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - const auto& mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - if (mother.has_mothers()) { - const auto& mothers2 = mother.mothers_as(); - for (const auto& mother2 : mothers2) { - const auto& pdgStringMot2 = getPdgCodeString(mother2); - const auto& pdgChargeMot2 = getCharge(mother2); - - histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } else { - histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } - - histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); - histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); - histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); - histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); - histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); - } - } - } - } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0; + } - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); + void fillSignalMultiplicity(const std::vector& nchPerCollision, const std::vector& nSignalPerCollision) + { + for (std::size_t iCollision = 0; iCollision < nchPerCollision.size(); ++iCollision) { + histos.fill(HIST("event/Nch"), nchPerCollision[iCollision]); + histos.fill(HIST("particle/NSignalVsNch"), nchPerCollision[iCollision], nSignalPerCollision[iCollision]); } + } + // Track histograms. Must be called after genericProcessParticles, which fills particlesOfInterest. + template + void genericProcessTracks(const TTracks& tracks) + { for (const auto& track : tracks) { if (!track.has_mcParticle()) { histos.fill(HIST("track/withoutParticle"), track.pt()); continue; } - const auto mcParticle = track.mcParticle_as(); + const auto mcParticle = track.template mcParticle_as(); histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); if (track.hasTOF()) { histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); } - if (IsStable.value == 0) { - LOG(info) << mcParticle.pdgCode() << " asked for " << PDG.value; + if (settings.isStable.value == 0) { + if (settings.doPrint) { + LOG(info) << mcParticle.pdgCode() << " asked for " << pdgCode.value; + } if (!mcParticle.has_mothers()) { continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - const auto ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { + auto mothers = mcParticle.template mothers_as(); + const auto particleIsInteresting = std::find(particlesOfInterest.begin(), particlesOfInterest.end(), mothers[0].globalIndex()) != particlesOfInterest.end(); + if (!particleIsInteresting) { continue; } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); } } else { - if (PDG != 0 && mcParticle.pdgCode() != PDG) { + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); + histos.fill(HIST("track/Pt"), track.pt() * settings.charge); + histos.fill(HIST("track/PtvsMCPt"), track.pt() * settings.charge, mcParticle.pt()); histos.fill(HIST("track/Eta"), track.eta()); if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); + histos.fill(HIST("track/primariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/primariesEta"), track.eta()); } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); + histos.fill(HIST("track/secondariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/secondariesEta"), track.eta()); } if (!mcParticle.has_mothers()) { - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); } continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); + auto mothers = mcParticle.template mothers_as(); if (mcParticle.isPhysicalPrimary()) { histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } else { histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; } } } } - PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", true); - void processParticleOnly(const o2::aod::McCollisions& colls, - const aod::McParticles& mcParticles) + // Event and MC particle histograms. Stores the selected MC particles in particlesOfInterest. + void genericProcessParticles(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) { for (const auto& col : colls) { histos.fill(HIST("event/VtxX"), col.posX()); histos.fill(HIST("event/VtxY"), col.posY()); histos.fill(HIST("event/VtxZ"), col.posZ()); } + std::vector nchPerCollision(colls.size(), 0); + std::vector nSignalPerCollision(colls.size(), 0); + particlesOfInterest.clear(); for (const auto& mcParticle : mcParticles) { const auto& pdgString = getPdgCodeString(mcParticle); const auto& pdgCharge = getCharge(mcParticle); @@ -469,18 +360,25 @@ struct Alice3SingleParticle { } else { histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { + if (isNchParticle(mcParticle)) { + ++nchPerCollision.at(mcParticle.mcCollisionId()); + } + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { + if (!yRange.contains(mcParticle.y())) { continue; } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { + if (!etaRange.contains(mcParticle.eta())) { continue; } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { + if (!ptRange.contains(mcParticle.pt())) { continue; } + if (!prodZRange.contains(mcParticle.vz())) { + continue; + } + ++nSignalPerCollision.at(mcParticle.mcCollisionId()); histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); histos.fill(HIST("particle/Pt"), mcParticle.pt()); histos.fill(HIST("particle/P"), mcParticle.p()); @@ -549,7 +447,7 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/daughters/Number"), 0.f); } if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); + const auto& mothers = mcParticle.mothers_as(); for (const auto& mother : mothers) { const auto& pdgStringMot = getPdgCodeString(mother); const auto& pdgChargeMot = getCharge(mother); @@ -566,13 +464,33 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); + if (mother.has_mothers()) { + const auto& mothers2 = mother.mothers_as(); + for (const auto& mother2 : mothers2) { + const auto& pdgStringMot2 = getPdgCodeString(mother2); + const auto& pdgChargeMot2 = getCharge(mother2); + + histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + if (mcParticle.isPhysicalPrimary()) { + histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } else { + histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } + + histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); + histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); + histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); + histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); + histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); + } + } } } histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { + if (mcParticle.getProcess() == TMCProcess::kPDecay) { histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); } else { histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); @@ -586,187 +504,35 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); + particlesOfInterest.push_back(mcParticle.globalIndex()); } + fillSignalMultiplicity(nchPerCollision, nSignalPerCollision); + } + + void processStandard(const o2::aod::McCollisions& colls, + const soa::Join& tracks, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); + } + + void processParticleOnly(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); } - PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", false); void processNonIU(const o2::aod::McCollisions& colls, const soa::Join& tracks, const aod::McParticles& mcParticles) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); - } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - auto daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - } - } - - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); - } - - for (const auto& track : tracks) { - if (!track.has_mcParticle()) { - histos.fill(HIST("track/withoutParticle"), track.pt()); - continue; - } - const auto mcParticle = track.mcParticle_as(); - histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - if (track.hasTOF()) { - histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - } - if (IsStable.value == 0) { - if (!mcParticle.has_mothers()) { - continue; - } - const auto& mothers = mcParticle.mothers_as(); - const auto& ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { - continue; - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); - } - } else { - if (PDG != 0 && mcParticle.pdgCode() != PDG) { - continue; - } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); - histos.fill(HIST("track/Eta"), track.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); - histos.fill(HIST("track/primariesEta"), track.eta()); - } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); - histos.fill(HIST("track/secondariesEta"), track.eta()); - } - - if (!mcParticle.has_mothers()) { - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); - } - continue; - } - // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } else { - histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; - } - } - } + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); } + + PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", false); + PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", true); PROCESS_SWITCH(Alice3SingleParticle, processNonIU, "Process non IU tracks", false); }; From c202da529b6a52f811339226ba67827ab10168f0 Mon Sep 17 00:00:00 2001 From: Subhadeep Roy Date: Thu, 15 Oct 2026 23:31:15 +0530 Subject: [PATCH 293/648] [PWGCF] lambdaSpinPolarization: rename the loose derived collision table for AO2D merging (#18290) --- .../DataModel/LambdaSpinPolarizationDerived.h | 8 ++++---- .../TableProducer/lambdaSpinPolarizationLooseProducer.cxx | 2 +- .../Tasks/lambdaSpinPolarization.cxx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h index 6bb0dc95acc..8941e813244 100644 --- a/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h +++ b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h @@ -34,7 +34,7 @@ DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); } // namespace lambdaloosecollision -DECLARE_SOA_TABLE(LambdaLooseCollisions, "AOD", "LAMBDALOOSECOLS", +DECLARE_SOA_TABLE(LambdaLooseCols, "AOD", "LAMBDALOOSECOL", o2::soa::Index<>, lambdaloosecollision::Cent, lambdaloosecollision::Mult, @@ -42,11 +42,11 @@ DECLARE_SOA_TABLE(LambdaLooseCollisions, "AOD", "LAMBDALOOSECOLS", lambdaloosecollision::PosY, lambdaloosecollision::PosZ, lambdaloosecollision::TimeStamp); -using LambdaLooseCollision = LambdaLooseCollisions::iterator; +using LambdaLooseCol = LambdaLooseCols::iterator; namespace lambdaloosetrack { -DECLARE_SOA_INDEX_COLUMN(LambdaLooseCollision, lambdaLooseCollision); +DECLARE_SOA_INDEX_COLUMN(LambdaLooseCol, lambdaLooseCol); DECLARE_SOA_COLUMN(Px, px, float); DECLARE_SOA_COLUMN(Py, py, float); DECLARE_SOA_COLUMN(Pz, pz, float); @@ -77,7 +77,7 @@ DECLARE_SOA_COLUMN(NegTpcCrossedRows, negTpcCrossedRows, int16_t); DECLARE_SOA_TABLE(LambdaLooseTracks, "AOD", "LAMBDALOOSETRKS", o2::soa::Index<>, - lambdaloosetrack::LambdaLooseCollisionId, + lambdaloosetrack::LambdaLooseColId, lambdaloosetrack::Px, lambdaloosetrack::Py, lambdaloosetrack::Pz, diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx index 7c2b10e88b5..dd5ed9595b4 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx @@ -88,7 +88,7 @@ enum LooseCentEstimator { kLooseCentFT0M = 0, struct LambdaSpinPolarizationLooseProducer { - Produces lambdaLooseCollisionTable; + Produces lambdaLooseCollisionTable; Produces lambdaLooseTrackTable; Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index 44daf7bc633..0d0f413c951 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -1290,7 +1290,7 @@ struct LambdaTableProducer { return true; } - void processDerivedData(aod::LambdaLooseCollisions::iterator const& collision, aod::LambdaLooseTracks const& looseTracks) + void processDerivedData(aod::LambdaLooseCols::iterator const& collision, aod::LambdaLooseTracks const& looseTracks) { histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); cent = collision.cent(); From 6a6b264c452f714a3e672ac7a62ac3184ddaa85b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:23:45 +0200 Subject: [PATCH 294/648] [PWGDQ] Add PCH for PWGDQ (#18307) --- PWGDQ/Core/CMakeLists.txt | 18 ++++++++++++++++++ PWGDQ/Core/dqAnalysisPCH.cxx | 13 +++++++++++++ PWGDQ/TableProducer/CMakeLists.txt | 7 +++++++ PWGDQ/Tasks/CMakeLists.txt | 27 +++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 PWGDQ/Core/dqAnalysisPCH.cxx diff --git a/PWGDQ/Core/CMakeLists.txt b/PWGDQ/Core/CMakeLists.txt index 835c14c17ae..b8b50c71f30 100644 --- a/PWGDQ/Core/CMakeLists.txt +++ b/PWGDQ/Core/CMakeLists.txt @@ -22,3 +22,21 @@ o2physics_add_library(PWGDQCore MCProng.cxx MCSignal.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2::GlobalTracking O2Physics::AnalysisCore KFParticle::KFParticle O2Physics::MLCore) + +# Precompiled header shared across PWGDQ. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# VarManager.h alone is ~8000 lines and is parsed by two thirds of the group. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(DQAnalysisPCH OBJECT dqAnalysisPCH.cxx) +target_link_libraries(DQAnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(DQAnalysisPCH PRIVATE + [["PWGDQ/DataModel/ReducedInfoTables.h"]] # 65% + [["PWGDQ/Core/VarManager.h"]] # 65%, ~8000 lines + ) +endif() diff --git a/PWGDQ/Core/dqAnalysisPCH.cxx b/PWGDQ/Core/dqAnalysisPCH.cxx new file mode 100644 index 00000000000..aab5454efc3 --- /dev/null +++ b/PWGDQ/Core/dqAnalysisPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the DQAnalysisPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGDQ/TableProducer/CMakeLists.txt b/PWGDQ/TableProducer/CMakeLists.txt index b8fd20d356d..5ba06a37cb5 100644 --- a/PWGDQ/TableProducer/CMakeLists.txt +++ b/PWGDQ/TableProducer/CMakeLists.txt @@ -11,35 +11,42 @@ o2physics_add_dpl_workflow(table-maker SOURCES tableMaker.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-with-assoc SOURCES tableMaker_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc SOURCES tableMakerMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc-with-assoc SOURCES tableMakerMC_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-jpsi-hf SOURCES tableMakerJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-muon-mch-trk-eff SOURCES tableMakerMuonMchTrkEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(generated-quarkonia-mc SOURCES generatedQuarkoniaMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase O2Physics::PWGDQCore COMPONENT_NAME Analysis) diff --git a/PWGDQ/Tasks/CMakeLists.txt b/PWGDQ/Tasks/CMakeLists.txt index 6e5ce44fb90..b8a0bfa35bd 100644 --- a/PWGDQ/Tasks/CMakeLists.txt +++ b/PWGDQ/Tasks/CMakeLists.txt @@ -26,6 +26,7 @@ endif() o2physics_add_dpl_workflow(table-reader SOURCES tableReader.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::MLCore COMPONENT_NAME Analysis) @@ -73,56 +74,67 @@ o2physics_add_dpl_workflow(table-reader-with-assoc-dilepton-track-track-pairing o2physics_add_dpl_workflow(table-reader-with-assoc-direct SOURCES tableReader_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency SOURCES dqEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc SOURCES dqEfficiency_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc-direct SOURCES dqEfficiency_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(energy-correlator-direct SOURCES dqEnergyCorrelator_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp SOURCES filterPP.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp-with-association SOURCES filterPPwithAssociation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pb-pb SOURCES filterPbPb.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0-selector SOURCES v0selector.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(dalitz-selection SOURCES DalitzSelection.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow SOURCES dqFlow.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) @@ -133,21 +145,25 @@ o2physics_add_dpl_workflow(task-muon-mch-trk-eff o2physics_add_dpl_workflow(task-j-psi-hf SOURCES taskJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-muon-dca SOURCES muonDCA.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlation SOURCES dqCorrelation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mch-align-record SOURCES mchAlignRecord.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -168,11 +184,13 @@ o2physics_add_dpl_workflow(task-mch-align-record o2physics_add_dpl_workflow(task-muon-mid-eff SOURCES MIDefficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::MIDBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-fwd-track-pid SOURCES taskFwdTrackPid.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) @@ -183,45 +201,54 @@ o2physics_add_dpl_workflow(quarkonia-to-hyperons o2physics_add_dpl_workflow(model-converter-mult-pv SOURCES ModelConverterMultPv.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-event-extended SOURCES ModelConverterEventExtended.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-mc-reduced-event SOURCES ModelConverterReducedMCEvents.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(qa-matching SOURCES qaMatching.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mft-mch-matcher SOURCES mftMchMatcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(muon-global-alignment SOURCES muonGlobalAlignment.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(j-psi-muon-correlations SOURCES jPsiMuonCorrelations.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(global-muon-matcher SOURCES global-muon-matcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) From dcafedcab369d34543f807204ffd7d8bb6fb9b08 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:56:39 +0200 Subject: [PATCH 295/648] [Common] Improve Common PCH (#18314) --- Common/Core/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Common/Core/CMakeLists.txt b/Common/Core/CMakeLists.txt index deaacfbd972..2522d1b1cce 100644 --- a/Common/Core/CMakeLists.txt +++ b/Common/Core/CMakeLists.txt @@ -38,6 +38,13 @@ o2physics_add_library(AnalysisCore # is linked here. add_library(AnalysisPCH OBJECT analysisPCH.cxx) target_link_libraries(AnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not +# put it in the 'analysis' job pool the way o2physics_add_dpl_workflow() does +# for the workflows. Left out, it runs unthrottled next to the pooled +# compilations and blows past the per-job memory budget the pool exists to +# enforce. +set_property(TARGET AnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(AnalysisPCH PRIVATE From 52922c55d0e3ce9d3224435e59971d11e9aeb422 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:58:41 +0200 Subject: [PATCH 296/648] [PWGUD] Add PCH for PWGUD (#18310) --- PWGUD/Core/CMakeLists.txt | 38 ++++++++++++++- PWGUD/Core/udTablesPCH.cxx | 13 +++++ PWGUD/TableProducer/CMakeLists.txt | 10 ++++ PWGUD/TableProducer/Converters/CMakeLists.txt | 6 +++ PWGUD/Tasks/CMakeLists.txt | 48 +++++++++++++++++++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 PWGUD/Core/udTablesPCH.cxx diff --git a/PWGUD/Core/CMakeLists.txt b/PWGUD/Core/CMakeLists.txt index 3a7fe0950cf..243935064d4 100644 --- a/PWGUD/Core/CMakeLists.txt +++ b/PWGUD/Core/CMakeLists.txt @@ -71,4 +71,40 @@ o2physics_add_library(FITCutParHolder o2physics_target_root_dictionary(FITCutParHolder HEADERS FITCutParHolder.h - LINKDEF FITCutParHolderLinkDef.h) \ No newline at end of file + LINKDEF FITCutParHolderLinkDef.h) +# Precompiled header shared across PWGUD. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# UDTables.h is the group's single shared data model. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(UDTablesPCH OBJECT udTablesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET UDTablesPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(UDTablesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(UDTablesPCH PRIVATE + # Framework baseline — same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows were + # precompiling LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGUD/DataModel/UDTables.h"]] # 79%, ~650 lines + ) +endif() diff --git a/PWGUD/Core/udTablesPCH.cxx b/PWGUD/Core/udTablesPCH.cxx new file mode 100644 index 00000000000..8faa701be69 --- /dev/null +++ b/PWGUD/Core/udTablesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the UDTablesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index 41ee7f57d8d..a26219c2e6f 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(dg-cand-producer SOURCES dgCandProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -33,21 +34,25 @@ o2physics_add_dpl_workflow(upccand-producer # o2-linter: disable=name/o2-workflo o2physics_add_dpl_workflow(tau-event-table-producer SOURCES tauEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(two-tracks-event-table-producer SOURCES twoTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(two-fwd-tracks-event-table-producer SOURCES twoFwdTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-track-propagation SOURCES fwdTrackPropagation.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking COMPONENT_NAME Analysis) @@ -63,26 +68,31 @@ o2physics_add_dpl_workflow(udmccollisions-to-udcollisions # o2-linter: disable=n o2physics_add_dpl_workflow(tau-three-prong-event-table-producer SOURCES tauThreeProngEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-muon SOURCES upcCandProducerMuon.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(true-gap-producer SOURCES trueGapProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-global-muon SOURCES upcCandProducerGlobalMuon.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-semi-fwd SOURCES upcCandProducerSemiFwd.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 9fd7c5b02e9..8197eb878be 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -11,30 +11,36 @@ o2physics_add_dpl_workflow(fwd-tracks-extra-converter SOURCES UDFwdTracksExtraConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisions-converter SOURCES UDCollisionsConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collision-fit-bits-converter SOURCES UDCollisionFITBitsConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter SOURCES UDCollisionSelExtrasConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v002 SOURCES UDCollisionSelExtrasV002Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v003 SOURCES UDCollisionSelExtrasV003Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 2d238dc6ed9..9e24487513f 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(upc-polarisation-jpsi-incoh SOURCES upcPolarisationJpsiIncoh.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -26,11 +27,13 @@ o2physics_add_dpl_workflow(upc-analysis o2physics_add_dpl_workflow(sg-spectra SOURCES sgSpectraAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-pid-spectra-table SOURCES sgPIDSpectraTable.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -41,46 +44,55 @@ o2physics_add_dpl_workflow(sg-pid-analyzer o2physics_add_dpl_workflow(sg-pid-spectra SOURCES sgPIDSpectra.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-incl-jpsi SOURCES sgInclJpsi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-omega SOURCES sgExclOmega.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-universe SOURCES sgExcUniverse.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fourpi SOURCES sgFourPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-sixpi SOURCES sgSixPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-twopi SOURCES sgTwoPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-d0 SOURCES sgD0Analyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fit SOURCES sgFITAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -106,11 +118,13 @@ o2physics_add_dpl_workflow(diff-qa o2physics_add_dpl_workflow(dg-cand-analyzer SOURCES dgCandAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::UDGoodRunSelector O2Physics::DGPIDSelector O2Physics::UDFSParser COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-analyzer SOURCES upcCandidateAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -131,121 +145,145 @@ o2physics_add_dpl_workflow(upc-veto-analysis o2physics_add_dpl_workflow(tautau13topo SOURCES upcTauTau13topo.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-tau-rl SOURCES upcTauRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(polarisation-rho SOURCES polarisationRho.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-jpsi-corr SOURCES upcJpsiCorr.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi SOURCES exclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-photonuclear-jmg SOURCES upcPhotonuclearAnalysisJMG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons SOURCES exclusiveTwoProtons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons-sg SOURCES exclusiveTwoProtonsSG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons SOURCES exclusivePhiLeptons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons-trees SOURCES exclusivePhiLeptonsTrees.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-pentaquark SOURCES exclusivePentaquark.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(inclusive-phikstar-sd SOURCES sginclusivePhiKstarSD.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi SOURCES sgExclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi-its SOURCES sgExclusivePhiITSselections.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-pion-analysis SOURCES upcPionAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-muons-upc SOURCES fwdMuonsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-fwd-jpsi-rl SOURCES upcFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-semi-fwd-jpsi-rl SOURCES upcSemiFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-event-its-rof-counter SOURCES upcEventItsRofCounter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-analysis SOURCES upcRhoAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-prime-analysis SOURCES upcRhoPrimeAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(event-by-event SOURCES eventByevent.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-rho-to-four-pi SOURCES exclusiveRhoTo4Pi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-quarkonia-central-barrel SOURCES upcQuarkoniaCentralBarrel.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -256,16 +294,19 @@ o2physics_add_dpl_workflow(upc-test-mc-std-tabs-rl o2physics_add_dpl_workflow(decaytree-analyzer SOURCES decayTreeAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UDGoodRunSelector O2Physics::decayTree COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-cumulants-upc SOURCES flowCumulantsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-correlations-upc SOURCES flowCorrelationsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore COMPONENT_NAME Analysis) @@ -276,21 +317,25 @@ o2physics_add_dpl_workflow(flow-mc-upc o2physics_add_dpl_workflow(flow-long-range-corr-upc SOURCES flowLongRangeCorrUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGCFCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(analysis-mc-dpm-jet-sg-v3 SOURCES analysisMCDPMJetSGv3.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-test-rct-tables SOURCES upcTestRctTables.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-jpsi-midrapidity SOURCES sgExclusiveJpsiMidrapidity.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -301,15 +346,18 @@ o2physics_add_dpl_workflow(upc-vm-rof o2physics_add_dpl_workflow(upc-test-fit-bit-mapping SOURCES upcTestFitBitMapping.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(pt-spectra-inclusive-upc SOURCES ptSpectraInclusiveUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-deuteron-spectra SOURCES sgDeuteronSpectra.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) From ff8956e13ecb9d311694b0706b24c77c035f77f1 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Fri, 16 Oct 2026 03:09:03 +0800 Subject: [PATCH 297/648] [PWGCF] Simplify QVector table calculation and fix fetching data from CCDB related bugs (#18298) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 148 ++++++++---------- 1 file changed, 66 insertions(+), 82 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 4b32e6d4c98..870f5b8f331 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -132,10 +132,10 @@ enum EParticleCuts { EParticleCuts_N }; -enum ECuts { +enum EBeforeAfterCuts { eBefore = 0, eAfter, - eCuts_N + eBeforeAfterCuts_N }; enum EParticleHistograms { @@ -181,7 +181,7 @@ static constexpr std::array MultiplicityTabl static constexpr std::array WeightsNames = {"ePhiRec", "ePhiSim", "ePt"}; -static constexpr std::array CutsNames = {"eBefore", "eAfter"}; +static constexpr std::array CutsNames = {"eBefore", "eAfter"}; static constexpr std::array TechnicalCutsNames = {"NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof"}; @@ -210,7 +210,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; // *) Binnings - Configurable cfALICECentBinSwitch{"cfALICECentBinSwitch", false, "switch on or off to use ALICE default binning for centrality hist"}; Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; Configurable> cfMultBins{"cfMultBins", {400, 0., 25000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; @@ -271,7 +270,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool fDryRun = false; // book all histos and run without filling and calculating anything bool fExternalFileSwitch = false; - bool fALICECentBinSwitch = true; bool fMasterCutSwitch = true; bool fQualityAssuranceSwitch = false; bool fRunMessageSwitch = false; @@ -281,12 +279,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct ParticleHistograms { TList* fParticleHistList = nullptr; - std::array, 2>, eParticleHistograms_N> fParticleHist{}; + std::array, 2>, eParticleHistograms_N> fParticleHist{}; } pc; struct EventHistograms { TList* fEventHistList = nullptr; - std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] + std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] } ec; struct ExternalHistograms { @@ -301,8 +299,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct QualityAssurance { TList* fQualityAssuranceList = nullptr; - std::array fHistCentralityRecSim{}; - std::array fHistMultNContrib{}; + std::array fHistCentralityRecSim{}; + std::array fHistMultNContrib{}; } qa; struct MiscHistograms { @@ -314,7 +312,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } misc; // *) functions and templates - std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) + std::vector> calculateQvectors(int maxCorrelator, const std::vector& harmonic, const std::vector& angles, const std::vector& weights) { int sum = 0; for (const int& x : harmonic) { @@ -323,20 +321,21 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const int maxHarmonic = sum + 1; // rows const int maxPower = maxCorrelator + 1; // cols - return std::vector>(maxHarmonic, std::vector(maxPower, TComplex(0., 0.))); - } - - template - void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 weight = T1(1)) - { - const int maxHarmonic = QVectorsTable.size(); - const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); - for (int h = 0; h < maxHarmonic; ++h) { - for (int p = 0; p < maxPower; ++p) { - const auto wp = std::pow(weight, p); - QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); + // a) Make sure all Q-vectors are initially zero: + std::vector> qVector(maxHarmonic, std::vector(maxPower, TComplex(0., 0.))); + + // b) Calculate Q-vectors for available angles and weights: + const int nParticles = angles.size(); + for (int i = 0; i < nParticles; ++i) { // loop over particles + const float phi = static_cast(angles[i]); + for (int h = 0; h < maxHarmonic; ++h) { + for (int p = 0; p < maxPower; ++p) { + const float wp = std::pow(static_cast(weights[i]), p); + qVector[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); + } } } + return qVector; } std::vector two(const std::vector>& QVectorsTable, const std::vector& harmonic) @@ -356,22 +355,22 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return {n, d}; } - std::vector recursion(int m, const std::vector>& Qvector, std::vector& harmonic, int mult = 1, int skip = 0) + std::vector recursion(int m, const std::vector>& qVector, std::vector& harmonic, int mult = 1, int skip = 0) { // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by // Kristjan Gulbrandsen (gulbrand@nbi.dk). auto q = [&](int n, int p) -> TComplex { if (n >= 0) { - return Qvector[n][p]; + return qVector[n][p]; } - return TComplex::Conjugate(Qvector[-n][p]); + return TComplex::Conjugate(qVector[-n][p]); }; std::vector c = {q(harmonic[m - 1], mult), q(0, mult)}; if ((m - 1) == 0) { return c; } - std::vector temp = recursion(m - 1, Qvector, harmonic); + std::vector temp = recursion(m - 1, qVector, harmonic); c[0] *= temp[0]; c[1] *= temp[1]; if ((m - 1) == skip) { @@ -381,7 +380,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to int hhold = harmonic[counter1]; harmonic[counter1] = harmonic[m - 2]; harmonic[m - 2] = hhold + harmonic[m - 1]; - std::vector c2 = recursion(m - 1, Qvector, harmonic, mult + 1, m - 2); + std::vector c2 = recursion(m - 1, qVector, harmonic, mult + 1, m - 2); int counter2 = m - 3; while (counter2 >= skip) { harmonic[m - 2] = harmonic[counter1]; @@ -390,7 +389,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to hhold = harmonic[counter1]; harmonic[counter1] = harmonic[m - 2]; harmonic[m - 2] = hhold + harmonic[m - 1]; - temp = recursion(m - 1, Qvector, harmonic, mult + 1, counter2); + temp = recursion(m - 1, qVector, harmonic, mult + 1, counter2); c2[0] += temp[0]; c2[1] += temp[1]; --counter2; @@ -533,11 +532,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to continue; } ex.fWeights[i]->SetTitle(tc.fFileWithWeights.c_str()); + ex.fWeights[i]->SetDirectory(nullptr); ex.fExternalHistogramsList->Add(ex.fWeights[i]); } - - delete baseList; - baseList = nullptr; } // end of void getHistogramWithWeights(const char* filePath, const char* runNumber) // templates @@ -582,25 +579,25 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool technicalCuts(T1 const& collision) { - if (tc.fTechnicalCutSwitch[eNoCollInTimeRangeStandard] == "1NoCollInTimeRangeStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (tc.fTechnicalCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (tc.fTechnicalCutSwitch[eNoCollInRofStandard] == "1NoCollInRofStandard" && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (tc.fTechnicalCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (tc.fTechnicalCutSwitch[eNoSameBunchPileUp] == "1NoSameBunchPileUp" && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (tc.fTechnicalCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (tc.fTechnicalCutSwitch[eIsVertexITSTPC] == "1IsVertexITSTPC" && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + if (tc.fTechnicalCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - if (tc.fTechnicalCutSwitch[eIsGoodITSLayersAll] == "1IsGoodITSLayersAll" && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (tc.fTechnicalCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (tc.fTechnicalCutSwitch[eIsGoodZvtxFT0vsPV] == "1IsGoodZvtxFT0vsPV" && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (tc.fTechnicalCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof] == "1NoHighMultCollInPrevRof" && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } return true; @@ -646,7 +643,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return true; } - template + template void eventHistFill(T1 const& collision, T2 const& tracks) { if constexpr (rs == eRec || rs == eRecAndSim) { @@ -762,7 +759,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return true; } - template + template void particleHistFill(T1 const& track) { if constexpr (rs == eRec || rs == eRecAndSim) { @@ -813,7 +810,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } // end of void particleHistFill(T1 const& track) - template + template void qaFill(T1 const& collision) { auto thisCent = chooseCent(collision, tc.fCentralityEstimator); @@ -902,15 +899,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - std::vector n2 = {-2, 2}; - auto qVectorsTableMC = initQVectorsTable(2, n2); - auto qVectorsTableReal = initQVectorsTable(2, n2); + std::vector vecPhiReal{}; + std::vector vecPhiMC{}; + std::vector vecWeightsReal{}; + std::vector vecWeightsMC{}; // Main loop over particles: - auto track = tracks.iteratorAt(0); // set the type and scope from one instance - for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { - track = tracks.iteratorAt(nTrack); - + for (auto const& track : tracks) { particleHistFill(track); particleHistFill(track); @@ -940,31 +935,37 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { particleHistFill(track); - updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeights[ePhiRec] * thisWeights[ePt]); + vecPhiReal.push_back(track.phi()); + vecWeightsReal.push_back(thisWeights[ePhiRec] * thisWeights[ePt]); } if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { particleHistFill(track); if (hasMCP) { - updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeights[ePhiSim] * thisWeights[ePt]); + vecPhiMC.push_back(thisPhiSim); + vecWeightsMC.push_back(thisWeights[ePhiSim] * thisWeights[ePt]); } } } } // end of for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { - std::vector resultTwo(2, TComplex(0., 0.)); + std::vector n22 = {-2, 2}; + auto qVectorsTableReal = calculateQvectors(2, n22, vecPhiReal, vecWeightsReal); + auto qVectorsTableMC = calculateQvectors(2, n22, vecPhiMC, vecWeightsMC); + + std::vector result22(2, TComplex(0., 0.)); if (tc.fMasterCutSwitch) { if (passEventCutsReal && passTechnicalCut) { - resultTwo = two(qVectorsTableReal, n2); - if (noneZeroDenom(resultTwo)) { - obs.fProfTwo[eRec]->Fill(thisCollCentReal, (resultTwo[0] / resultTwo[1].Re()).Re()); + result22 = two(qVectorsTableReal, n22); + if (noneZeroDenom(result22)) { + obs.fProfTwo[eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); } if constexpr (rs == eRecAndSim) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; - resultTwo = two(qVectorsTableMC, n2); - if (noneZeroDenom(resultTwo)) { - obs.fProfTwo[eSim]->Fill(thisCollCentMC, (resultTwo[0] / resultTwo[1].Re()).Re()); + result22 = two(qVectorsTableMC, n22); + if (noneZeroDenom(result22)) { + obs.fProfTwo[eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); } } // end of if constexpr (rs == eRecAndSim) { } @@ -994,7 +995,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fExternalFileSwitch = cfExternalFileSwitch; tc.fFileWithWeights = cfFileWithWeights; - tc.fALICECentBinSwitch = cfALICECentBinSwitch; tc.fMasterCutSwitch = cfMasterCutSwitch; tc.fTechnicalCutSwitch = cfTechnicalCutSwitch.value; @@ -1215,11 +1215,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const float maxIp = lIp[2]; if (doprocessRec || doprocessRecSim) { - if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - } else { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); - } + ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); @@ -1245,11 +1241,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (tc.fMasterCutSwitch) { - if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - } else { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); - } + ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); @@ -1272,16 +1264,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); ec.fEventHistList->Add(ec.fEventHist[i][eRec][eAfter]); } - } - } + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + } // end of if (tc.fMasterCutSwitch) { } if (doprocessSim || doprocessRecSim) { - if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - } else { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); - } + ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); @@ -1307,11 +1295,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (tc.fMasterCutSwitch) { - if (tc.fALICECentBinSwitch) { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - } else { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); - } + ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); @@ -1370,7 +1354,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(qa.fQualityAssuranceList); if (doprocessRec || doprocessRecSim) { - for (int i = 0; i < eCuts_N; ++i) { + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), nBinsMultRef, minMultRef, maxMultRef, nBinsContrib, minContrib, maxContrib); qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); @@ -1378,7 +1362,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (doprocessRecSim) { - for (int i = 0; i < eCuts_N; ++i) { + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); From a1cb915458263a0e9b85acb8f50875e02c3be789 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Thu, 15 Oct 2026 21:18:38 +0200 Subject: [PATCH 298/648] [PWGHF] Add setter for DCAFitter maxChi2 in code for B hadrons (#18292) --- .../candidateCreatorB0Reduced.cxx | 37 +++++++++++-------- .../candidateCreatorBplusReduced.cxx | 2 + .../candidateCreatorBsReduced.cxx | 2 + .../candidateCreatorLbReduced.cxx | 2 + .../dataCreatorCharmHadPiReduced.cxx | 5 ++- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx index df07d60011b..81a8e3d79b0 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx @@ -65,6 +65,7 @@ struct HfCandidateCreatorB0Reduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DPi pair preselections (GeV/c2)"}; @@ -94,21 +95,27 @@ struct HfCandidateCreatorB0Reduced { } // Initialize fitter - df2.setPropagateToPCA(propagateToPCA); - df2.setMaxR(maxR); - df2.setMaxDZIni(maxDZIni); - df2.setMinParamChange(minParamChange); - df2.setMinRelChi2Change(minRelChi2Change); - df2.setUseAbsDCA(useAbsDCA); - df2.setWeightedFinalPCA(useWeightedFinalPCA); - - df3.setPropagateToPCA(propagateToPCA); - df3.setMaxR(maxR); - df3.setMaxDZIni(maxDZIni); - df3.setMinParamChange(minParamChange); - df3.setMinRelChi2Change(minRelChi2Change); - df3.setUseAbsDCA(useAbsDCA); - df3.setWeightedFinalPCA(useWeightedFinalPCA); + if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { + df2.setPropagateToPCA(propagateToPCA); + df2.setMaxR(maxR); + df2.setMaxDZIni(maxDZIni); + df2.setMinParamChange(minParamChange); + df2.setMinRelChi2Change(minRelChi2Change); + df2.setUseAbsDCA(useAbsDCA); + df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); + } + + if (doprocessDataDstarPi || doprocessDataDstarPiWithDmesMl) { + df3.setPropagateToPCA(propagateToPCA); + df3.setMaxR(maxR); + df3.setMaxDZIni(maxDZIni); + df3.setMinParamChange(minParamChange); + df3.setMinRelChi2Change(minRelChi2Change); + df3.setUseAbsDCA(useAbsDCA); + df3.setWeightedFinalPCA(useWeightedFinalPCA); + df3.setMaxChi2(maxChi2); + } // histograms if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx index 2ef89ce34c5..b58fcc51d7e 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBplusReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B+ is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowD0PiTolerance{"invMassWindowD0PiTolerance", 0.01, "invariant-mass window tolerance for D0Pi pair preselections (GeV/c2)"}; @@ -101,6 +102,7 @@ struct HfCandidateCreatorBplusReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBplusToD0Pi", "2-prong candidates;inv. mass (B^{+} #rightarrow #overline{D^{0}}#pi^{#plus} #rightarrow #pi^{#minus}K^{#plus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx index 8bed227e12d..a46d274b8d5 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBsReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DsPi pair preselections (GeV/c2)"}; @@ -97,6 +98,7 @@ struct HfCandidateCreatorBsReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBsToDsPi", "2-prong candidates;inv. mass (B^{0}_{s} #rightarrow D_{s}^{#minus}#pi^{#plus} #rightarrow #K^{#minus}K^{#plus}#pi^{#minus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx index 1b43d988285..64692dc72eb 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx @@ -64,6 +64,7 @@ struct HfCandidateCreatorLbReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any Lb is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowLcPiTolerance{"invMassWindowLcPiTolerance", 0.01, "invariant-mass window tolerance for LcPi pair preselections (GeV/c2)"}; @@ -96,6 +97,7 @@ struct HfCandidateCreatorLbReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassLambdaB0ToLcPi", "2-prong candidates;inv. mass (#Lambda_{b}^{0} #rightarrow #Lambda_{c}^{#plus}#pi^{#minus} #rightarrow pK^{#minus}#pi^{#plus}#pi^{#minus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 6d09d96b998..49076dc9217 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -187,7 +187,8 @@ struct HfDataCreatorCharmHadPiReduced { Configurable maxR{"maxR", 200., "reject PCA's above this radius"}; Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; - Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old < this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; } vertexConfigurations; // selection struct : o2::framework::ConfigurableGroup { @@ -325,6 +326,7 @@ struct HfDataCreatorCharmHadPiReduced { df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df2.setMaxChi2(vertexConfigurations.maxChi2); df2.setMatCorrType(noMatCorr); if (doprocessDplusPiData || doprocessDplusPiDataWithMl || doprocessDplusPiDataWithQvec || doprocessDplusPiDataWithMlAndQvec || doprocessDplusPiMc || doprocessDplusPiMcWithMl || doprocessDsPiData || doprocessDsPiDataWithMl || doprocessDsPiDataWithQvec || doprocessDsPiDataWithMlAndQvec || doprocessDsPiMc || doprocessDsPiMcWithMl || @@ -336,6 +338,7 @@ struct HfDataCreatorCharmHadPiReduced { df3.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); df3.setUseAbsDCA(vertexConfigurations.useAbsDCA); df3.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df3.setMaxChi2(vertexConfigurations.maxChi2); df3.setMatCorrType(noMatCorr); } From df0ebbefe4f7e6881bfbea2b63de0f4c20074e91 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 22:44:32 +0200 Subject: [PATCH 299/648] [PWGHF] Add PCH for PWGHF (#18308) --- PWGHF/ALICE3/TableProducer/CMakeLists.txt | 15 ++++++ PWGHF/ALICE3/Tasks/CMakeLists.txt | 9 ++++ PWGHF/Core/CMakeLists.txt | 19 +++++++ PWGHF/Core/hfCandidatesPCH.cxx | 13 +++++ PWGHF/D2H/Core/d2hReducedPCH.cxx | 14 +++++ PWGHF/D2H/TableProducer/CMakeLists.txt | 1 + PWGHF/D2H/Tasks/CMakeLists.txt | 25 +++++++++ PWGHF/HFC/TableProducer/CMakeLists.txt | 16 ++++++ PWGHF/HFC/Tasks/CMakeLists.txt | 5 ++ PWGHF/TableProducer/CMakeLists.txt | 65 +++++++++++++++++++++++ PWGHF/Tasks/CMakeLists.txt | 4 ++ 11 files changed, 186 insertions(+) create mode 100644 PWGHF/Core/hfCandidatesPCH.cxx create mode 100644 PWGHF/D2H/Core/d2hReducedPCH.cxx diff --git a/PWGHF/ALICE3/TableProducer/CMakeLists.txt b/PWGHF/ALICE3/TableProducer/CMakeLists.txt index 5bbc40353ee..4aba14d7e30 100644 --- a/PWGHF/ALICE3/TableProducer/CMakeLists.txt +++ b/PWGHF/ALICE3/TableProducer/CMakeLists.txt @@ -13,16 +13,19 @@ o2physics_add_dpl_workflow(candidate-creator-chic SOURCES candidateCreatorChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-x SOURCES candidateCreatorX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xicc SOURCES candidateCreatorXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) @@ -30,46 +33,55 @@ o2physics_add_dpl_workflow(candidate-creator-xicc o2physics_add_dpl_workflow(candidate-selector-chic-to-jpsi-gamma SOURCES candidateSelectorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-barrel SOURCES candidateSelectorD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-forward SOURCES candidateSelectorD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-parametrized-pid SOURCES candidateSelectorD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-jpsi SOURCES candidateSelectorJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-alice3 SOURCES candidateSelectorLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-parametrized-pid SOURCES candidateSelectorLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-x-to-jpsi-pi-pi SOURCES candidateSelectorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi SOURCES candidateSelectorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -77,15 +89,18 @@ o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi o2physics_add_dpl_workflow(tree-creator-chic-to-jpsi-gamma SOURCES treeCreatorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-x-to-jpsi-pi-pi SOURCES treeCreatorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xicc-to-p-k-pi-pi SOURCES treeCreatorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/ALICE3/Tasks/CMakeLists.txt b/PWGHF/ALICE3/Tasks/CMakeLists.txt index c94c68a9587..3394d304c58 100644 --- a/PWGHF/ALICE3/Tasks/CMakeLists.txt +++ b/PWGHF/ALICE3/Tasks/CMakeLists.txt @@ -11,36 +11,43 @@ o2physics_add_dpl_workflow(task-chic SOURCES taskChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-barrel SOURCES taskD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-forward SOURCES taskD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-parametrized-pid SOURCES taskD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-jpsi SOURCES taskJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-alice3 SOURCES taskLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-parametrized-pid SOURCES taskLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -51,10 +58,12 @@ o2physics_add_dpl_workflow(task-qa-pid-rejection o2physics_add_dpl_workflow(task-x SOURCES taskX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xicc SOURCES taskXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/Core/CMakeLists.txt b/PWGHF/Core/CMakeLists.txt index cb84e8bb00c..61beb363212 100644 --- a/PWGHF/Core/CMakeLists.txt +++ b/PWGHF/Core/CMakeLists.txt @@ -12,3 +12,22 @@ o2physics_add_library(HfMlCore SOURCES HfMlResponse.cxx PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across PWGHF. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# D2H already has its own carrier (D2HReducedPCH); those targets keep it. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(HfCandidatesPCH OBJECT hfCandidatesPCH.cxx) +target_link_libraries(HfCandidatesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(HfCandidatesPCH PRIVATE + [["PWGHF/DataModel/CandidateReconstructionTables.h"]] # 78%, 2479 lines + [["PWGHF/DataModel/CandidateSelectionTables.h"]] # 72% + [["Common/Core/RecoDecay.h"]] # 65%, 1523 lines + ) +endif() diff --git a/PWGHF/Core/hfCandidatesPCH.cxx b/PWGHF/Core/hfCandidatesPCH.cxx new file mode 100644 index 00000000000..95f76a2131e --- /dev/null +++ b/PWGHF/Core/hfCandidatesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the HfCandidatesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGHF/D2H/Core/d2hReducedPCH.cxx b/PWGHF/D2H/Core/d2hReducedPCH.cxx new file mode 100644 index 00000000000..9ee76e63009 --- /dev/null +++ b/PWGHF/D2H/Core/d2hReducedPCH.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the precompiled header shared by the D2H reduced-data-model +// workflows; see D2HReducedPCH in this directory's CMakeLists.txt. It exists +// only to own the PCH, so it has no code of its own. diff --git a/PWGHF/D2H/TableProducer/CMakeLists.txt b/PWGHF/D2H/TableProducer/CMakeLists.txt index 3492a98acff..ebc99c83782 100644 --- a/PWGHF/D2H/TableProducer/CMakeLists.txt +++ b/PWGHF/D2H/TableProducer/CMakeLists.txt @@ -118,5 +118,6 @@ o2physics_add_dpl_workflow(converter-reduced-3-prongs-ml o2physics_add_dpl_workflow(tree-creator-dstar-spin-align-mixing SOURCES treeCreatorDstarSpinAlignMixing.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 8ce14577196..a3726135da8 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-b0 SOURCES taskB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -26,6 +27,7 @@ o2physics_add_dpl_workflow(task-b0-to-jpsi-k0-star-reduced o2physics_add_dpl_workflow(task-bplus SOURCES taskBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -51,16 +53,19 @@ o2physics_add_dpl_workflow(task-bs-to-jpsi-phi-reduced o2physics_add_dpl_workflow(task-bs SOURCES taskBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-cd SOURCES taskCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-charm-polarisation SOURCES taskCharmPolarisation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -76,41 +81,49 @@ o2physics_add_dpl_workflow(task-charm-reso-to-d-trk-reduced o2physics_add_dpl_workflow(task-d0 SOURCES taskD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-directed-flow-charm-hadrons SOURCES taskDirectedFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dplus SOURCES taskDplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-ds SOURCES taskDs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dstar-to-d0-pi SOURCES taskDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flattenicity-d0-lc SOURCES taskFlattenicityD0Lc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flow-charm-hadrons SOURCES taskFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lb SOURCES taskLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -121,61 +134,73 @@ o2physics_add_dpl_workflow(task-lb-reduced o2physics_add_dpl_workflow(task-lc SOURCES taskLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-upc-lc SOURCES taskUpcLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-to-k0s-p SOURCES taskLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-net-charm-fluctuations SOURCES taskNetCharmFluctuations.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-omegac0-to-omega-pi SOURCES taskOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-omegac0-to-omega-pi-qa SOURCES taskOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-pt-fluc-charm-hadrons SOURCES taskPtFlucCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac SOURCES taskSigmac.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac-to-cascade SOURCES taskSigmacToCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic SOURCES taskXic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic-to-xi-pi-pi SOURCES taskXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic0-to-xi-pi SOURCES taskXic0ToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index 299981d8c1f..8da21e3474a 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -11,46 +11,55 @@ o2physics_add_dpl_workflow(correlator-d0-d0bar SOURCES correlatorD0D0bar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-d0bar-barrel-full-pid SOURCES correlatorD0D0barBarrelFullPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-hadrons SOURCES correlatorD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d-meson-pairs SOURCES correlatorDMesonPairs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dminus SOURCES correlatorDplusDminus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dplus-reduced SOURCES correlatorDplusDplusReduced.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-hadrons SOURCES correlatorDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-ds-hadrons SOURCES correlatorDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-correlations-reduced SOURCES derivedDataCreatorCorrelationsReduced.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -66,6 +75,7 @@ o2physics_add_dpl_workflow(correlator-ds-hadrons-reduced o2physics_add_dpl_workflow(correlator-dstar-hadrons SOURCES correlatorDstarHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -76,30 +86,36 @@ o2physics_add_dpl_workflow(correlator-hfe-hadrons o2physics_add_dpl_workflow(correlator-lc-hadrons SOURCES correlatorLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-lc-sc-hadrons SOURCES correlatorLcScHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-xic-hadrons SOURCES correlatorXicHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-track-femto-dream SOURCES producerCharmHadronsTrackFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-v0-femto-dream SOURCES producerCharmHadronsV0FemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-charm-femto-dream SOURCES producerCharmHadronsCharmFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 69e279424c9..4551ccd2b0f 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -21,6 +21,7 @@ o2physics_add_dpl_workflow(task-charm-hadrons-v0-femto-dream o2physics_add_dpl_workflow(task-correlation-d0-hadrons SOURCES taskCorrelationD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +42,13 @@ o2physics_add_dpl_workflow(task-correlation-dplus-dplus-reduced o2physics_add_dpl_workflow(task-correlation-dplus-hadrons SOURCES taskCorrelationDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-correlation-ds-hadrons SOURCES taskCorrelationDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -61,11 +64,13 @@ o2physics_add_dpl_workflow(task-correlation-hfe-hadrons o2physics_add_dpl_workflow(task-correlation-lc-hadrons SOURCES taskCorrelationLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flow SOURCES taskFlow.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) diff --git a/PWGHF/TableProducer/CMakeLists.txt b/PWGHF/TableProducer/CMakeLists.txt index 657dd42e485..0029240333b 100644 --- a/PWGHF/TableProducer/CMakeLists.txt +++ b/PWGHF/TableProducer/CMakeLists.txt @@ -25,6 +25,7 @@ o2physics_add_dpl_workflow(refit-pv-dummy o2physics_add_dpl_workflow(pid-creator SOURCES pidCreator.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -37,71 +38,85 @@ o2physics_add_dpl_workflow(mc-pid-tof o2physics_add_dpl_workflow(candidate-creator-2prong SOURCES candidateCreator2Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-3prong SOURCES candidateCreator3Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-b0 SOURCES candidateCreatorB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bplus SOURCES candidateCreatorBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bs SOURCES candidateCreatorBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-cascade SOURCES candidateCreatorCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-dstar SOURCES candidateCreatorDstar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-lb SOURCES candidateCreatorLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus SOURCES candidateCreatorSigmac0plusplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus-cascade SOURCES candidateCreatorSigmac0plusplusCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0 SOURCES candidateCreatorXic0Omegac0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0-qa SOURCES candidateCreatorXic0Omegac0Qa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic-to-xi-pi-pi SOURCES candidateCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-mc-gen SOURCES candidateCreatorMcGen.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -109,106 +124,127 @@ o2physics_add_dpl_workflow(candidate-creator-mc-gen o2physics_add_dpl_workflow(candidate-selector-b0-to-d-pi SOURCES candidateSelectorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bplus-to-d0-pi SOURCES candidateSelectorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bs-to-ds-pi SOURCES candidateSelectorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0 SOURCES candidateSelectorD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dplus-to-pi-k-pi SOURCES candidateSelectorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-ds-to-k-k-pi SOURCES candidateSelectorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dstar-to-d0-pi SOURCES candidateSelectorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lb-to-lc-pi SOURCES candidateSelectorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc SOURCES candidateSelectorLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-pid-ml SOURCES candidateSelectorLcPidMl.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-to-k0s-p SOURCES candidateSelectorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-ka SOURCES candidateSelectorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi SOURCES candidateSelectorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi-qa SOURCES candidateSelectorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-xic0-to-omega-ka SOURCES candidateSelectorOmegac0Xic0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic0-to-xi-pi-kf SOURCES candidateSelectorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi SOURCES candidateSelectorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi-qa SOURCES candidateSelectorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-p-k-pi SOURCES candidateSelectorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-xi-pi-pi SOURCES candidateSelectorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-cd SOURCES candidateSelectorCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -216,56 +252,67 @@ o2physics_add_dpl_workflow(candidate-selector-cd o2physics_add_dpl_workflow(tree-creator-b0-to-d-pi SOURCES treeCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bplus-to-d0-pi SOURCES treeCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bs-to-ds-pi SOURCES treeCreatorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-d0-to-k-pi SOURCES treeCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dplus-to-pi-k-pi SOURCES treeCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-ds-to-k-k-pi SOURCES treeCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dstar-to-d0-pi SOURCES treeCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lb-to-lc-pi SOURCES treeCreatorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-k0s-p SOURCES treeCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-p-k-pi SOURCES treeCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-sigmac-corr-bkg SOURCES treeCreatorSigmacCorrBkg.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -276,92 +323,110 @@ o2physics_add_dpl_workflow(tree-creator-omegac-st o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-ka SOURCES treeCreatorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi SOURCES treeCreatorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi-qa SOURCES treeCreatorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi SOURCES treeCreatorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi-qa SOURCES treeCreatorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic0-to-xi-pi-kf SOURCES treeCreatorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-p-k-pi SOURCES treeCreatorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-xi-pi-pi SOURCES treeCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-tcc-to-d0-d0-pi SOURCES treeCreatorTccToD0D0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) # Derived-data creators o2physics_add_dpl_workflow(derived-data-creator-b0-to-d-pi SOURCES derivedDataCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-bplus-to-d0-pi SOURCES derivedDataCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-d0-to-k-pi SOURCES derivedDataCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dplus-to-pi-k-pi SOURCES derivedDataCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-ds-to-k-k-pi SOURCES derivedDataCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dstar-to-d0-pi SOURCES derivedDataCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-p-k-pi SOURCES derivedDataCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-k0s-p SOURCES derivedDataCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-xic-to-xi-pi-pi SOURCES derivedDataCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/Tasks/CMakeLists.txt b/PWGHF/Tasks/CMakeLists.txt index d75bbfee3c8..6c4a4a71b71 100644 --- a/PWGHF/Tasks/CMakeLists.txt +++ b/PWGHF/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par SOURCES taskCharmHadImpactPar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,11 +22,13 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par o2physics_add_dpl_workflow(task-mc-efficiency SOURCES taskMcEfficiency.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mc-efficiency-to-xi-pi SOURCES taskMcEfficiencyToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -36,6 +39,7 @@ o2physics_add_dpl_workflow(task-mc-gen-pt-rap-shapes o2physics_add_dpl_workflow(task-mc-validation SOURCES taskMcValidation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) From e1fe8379f422ad6370b2f79a4b58d3b814d9a931 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 15 Oct 2026 22:49:39 +0200 Subject: [PATCH 300/648] [PWGDQ,PWGHF,PWGJE] More clean-ups for PCH (#18318) --- PWGDQ/Core/CMakeLists.txt | 20 ++++++++++++++++++++ PWGDQ/Tasks/CMakeLists.txt | 6 ++++++ PWGHF/Core/CMakeLists.txt | 22 +++++++++++++++++++++- PWGHF/D2H/Core/d2hReducedPCH.cxx | 14 -------------- PWGJE/Core/CMakeLists.txt | 20 ++++++++++++++++++++ PWGJE/Tasks/CMakeLists.txt | 12 ++++++++++++ 6 files changed, 79 insertions(+), 15 deletions(-) delete mode 100644 PWGHF/D2H/Core/d2hReducedPCH.cxx diff --git a/PWGDQ/Core/CMakeLists.txt b/PWGDQ/Core/CMakeLists.txt index b8b50c71f30..265d1c76be2 100644 --- a/PWGDQ/Core/CMakeLists.txt +++ b/PWGDQ/Core/CMakeLists.txt @@ -33,9 +33,29 @@ o2physics_add_library(PWGDQCore # # Skipped under recc, which caches compilations remotely instead. add_library(DQAnalysisPCH OBJECT dqAnalysisPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET DQAnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(DQAnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(DQAnalysisPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + [["PWGDQ/DataModel/ReducedInfoTables.h"]] # 65% [["PWGDQ/Core/VarManager.h"]] # 65%, ~8000 lines ) diff --git a/PWGDQ/Tasks/CMakeLists.txt b/PWGDQ/Tasks/CMakeLists.txt index b8a0bfa35bd..6305b7c0f9a 100644 --- a/PWGDQ/Tasks/CMakeLists.txt +++ b/PWGDQ/Tasks/CMakeLists.txt @@ -18,6 +18,12 @@ # # Skipped under recc, which caches compilations remotely instead. add_library(TableReaderWithAssocPCH OBJECT tableReaderWithAssocPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET TableReaderWithAssocPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(TableReaderWithAssocPCH PUBLIC O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(TableReaderWithAssocPCH PRIVATE diff --git a/PWGHF/Core/CMakeLists.txt b/PWGHF/Core/CMakeLists.txt index 61beb363212..bd5c35a3f9b 100644 --- a/PWGHF/Core/CMakeLists.txt +++ b/PWGHF/Core/CMakeLists.txt @@ -15,7 +15,7 @@ o2physics_add_library(HfMlCore # Precompiled header shared across PWGHF. Percentages are the share of the # group's workflow translation units that include the header, measured over the tree. -# D2H already has its own carrier (D2HReducedPCH); those targets keep it. +# The D2H reduced-data-model workflows share this carrier too. # # runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's # customize() to be declared before it is included, so precompiling it silently @@ -23,9 +23,29 @@ o2physics_add_library(HfMlCore # # Skipped under recc, which caches compilations remotely instead. add_library(HfCandidatesPCH OBJECT hfCandidatesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET HfCandidatesPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(HfCandidatesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(HfCandidatesPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + [["PWGHF/DataModel/CandidateReconstructionTables.h"]] # 78%, 2479 lines [["PWGHF/DataModel/CandidateSelectionTables.h"]] # 72% [["Common/Core/RecoDecay.h"]] # 65%, 1523 lines diff --git a/PWGHF/D2H/Core/d2hReducedPCH.cxx b/PWGHF/D2H/Core/d2hReducedPCH.cxx deleted file mode 100644 index 9ee76e63009..00000000000 --- a/PWGHF/D2H/Core/d2hReducedPCH.cxx +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// Carrier for the precompiled header shared by the D2H reduced-data-model -// workflows; see D2HReducedPCH in this directory's CMakeLists.txt. It exists -// only to own the PCH, so it has no code of its own. diff --git a/PWGJE/Core/CMakeLists.txt b/PWGJE/Core/CMakeLists.txt index a05330e18f6..f1adfc59b7a 100644 --- a/PWGJE/Core/CMakeLists.txt +++ b/PWGJE/Core/CMakeLists.txt @@ -39,9 +39,29 @@ endif() # # Skipped under recc, which caches compilations remotely instead. add_library(JetDataModelPCH OBJECT jetDataModelPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetDataModelPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetDataModelPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore) if(NOT DEFINED ENV{USE_RECC}) target_precompile_headers(JetDataModelPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + [["PWGJE/DataModel/Jet.h"]] # 90% ) endif() diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 1cab772205b..ea882ad0e41 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -9,6 +9,12 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. add_library(JetSubstructureHFPCH OBJECT jetSubstructureHFPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetSubstructureHFPCH PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) @@ -29,6 +35,12 @@ target_precompile_headers(JetSubstructureHFPCH PRIVATE endif() add_library(JetSubstructureHFOutputPCH OBJECT jetSubstructureHFOutputPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFOutputPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetSubstructureHFOutputPCH PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) From 62bcd8be119641ffcede38785431da5f3e81f88c Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Fri, 16 Oct 2026 01:40:14 +0200 Subject: [PATCH 301/648] [PWGCF] Add extendable mult-axis for control hists for HM pp in twoParticleCorrelationsMpi.cxx (#18287) --- .../Tasks/twoParticleCorrelationsMpi.cxx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index 85f2005609b..e7f5d4e2f9f 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -159,6 +159,7 @@ struct TwoParticleCorrelationsMpi { ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pt associated axis for histograms"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + ConfigurableAxis axisMultiplicityQA{"axisMultiplicityQA", {250, 0, 250}, "fine multiplicity / centrality axis for control histograms"}; ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; @@ -437,8 +438,8 @@ struct TwoParticleCorrelationsMpi { eventSeedEstimatorEnabled ? "enabled" : "disabled", cfgNuncSeedsTemplateFile.value.c_str(), cfgNuncSeedsTemplate.value.c_str()); - 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"}}}); - 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"}}}); + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultiplicityQA, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultiplicityQA, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); if (doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected) { if (cfgMultCorrelationsMask == 0) { LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when MultSet process functions are in use."); @@ -461,7 +462,7 @@ struct TwoParticleCorrelationsMpi { } registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); } - registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{100, 0, 100, "/multiplicity/centrality"}}}); + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultiplicityQA, "/multiplicity/centrality"}}}); if (doprocessMCDetectorClosure) { registry.add("detectorClosure/rawMultiplicityVsTrue", "raw reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{raw,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); registry.add("detectorClosure/correctedMultiplicityVsTrue", "corrected reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{corr,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); @@ -475,14 +476,14 @@ struct TwoParticleCorrelationsMpi { detectorClosureStatus->GetXaxis()->SetBinLabel(3, "invalid best-collision count"); } if (eventSeedEstimatorEnabled) { - registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{100, 0, 100, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{axisMultiplicityQA, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); registry.add("eventSeedPairProbabilities", "summed pair probabilities", {HistType::kTH3F, {{200, 0, 200, "#Sigma P_{baseline}"}, {200, 0, 200, "#Sigma P_{near}"}, {200, 0, 200, "#Sigma P_{away}"}}}); registry.add("eventSeedEstimatorStatus", "event-estimator status", {HistType::kTH1F, {{5, -0.5, 4.5, "status"}}}); - registry.add("eventSeedTemplateCoverage", "template coverage vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {102, -0.01, 1.01, "matched/candidate pairs"}}}); - registry.add("eventSeedEstimateVsMultiplicity", "event-level seed estimate vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedTemplateCoverage", "template coverage vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {102, -0.01, 1.01, "matched/candidate pairs"}}}); + registry.add("eventSeedEstimateVsMultiplicity", "event-level seed estimate vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); registry.add("eventSeedFixedProbabilityVsMultiplicity", "fixed-probability event Seed versus multiplicity;multiplicity;N_{seed}^{probability sum}", {HistType::kTH2F, {axisMultiplicity, {1000, 0, 100}}}); - registry.add("eventNearYieldVsMultiplicity", "event-level near yield vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 20, "Y_{near}"}}}); - registry.add("eventAwayYieldVsMultiplicity", "event-level away yield vs multiplicity", {HistType::kTH2F, {{100, 0, 100, "multiplicity"}, {200, 0, 20, "Y_{away}"}}}); + registry.add("eventNearYieldVsMultiplicity", "event-level near yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{near}"}}}); + registry.add("eventAwayYieldVsMultiplicity", "event-level away yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{away}"}}}); registry.add("profileEventNTriggers", "mean event trigger count", {HistType::kTProfile, {axisMultiplicity}}); registry.add("profileEventNearPairs", "mean summed near-pair probability", {HistType::kTProfile, {axisMultiplicity}}); registry.add("profileEventAwayPairs", "mean summed away-pair probability", {HistType::kTProfile, {axisMultiplicity}}); From 9ee21d91cf9ba294fc090ef148978b76aa579df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:40:45 +0200 Subject: [PATCH 302/648] [PWGDQ] MIDefficiency.cxx: Fix compilation (#18320) --- PWGDQ/Tasks/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGDQ/Tasks/CMakeLists.txt b/PWGDQ/Tasks/CMakeLists.txt index 6305b7c0f9a..59594f03b54 100644 --- a/PWGDQ/Tasks/CMakeLists.txt +++ b/PWGDQ/Tasks/CMakeLists.txt @@ -191,7 +191,7 @@ o2physics_add_dpl_workflow(task-mch-align-record o2physics_add_dpl_workflow(task-muon-mid-eff SOURCES MIDefficiency.cxx REUSE_FROM DQAnalysisPCH - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::MIDBase + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MIDBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-fwd-track-pid From 8540f7c0f05c4827e4aa6ea4406f5ca30a77d836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrea=20Tavira=20Garc=C3=ADa?= <118979672+atavirag@users.noreply.github.com> Date: Fri, 16 Oct 2026 07:57:05 +0200 Subject: [PATCH 303/648] [PWGUD] Fix code check and remove logf in upcTrackVertexingQA (#18286) --- PWGUD/Tasks/upcTrackVertexingQA.cxx | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/PWGUD/Tasks/upcTrackVertexingQA.cxx b/PWGUD/Tasks/upcTrackVertexingQA.cxx index 7dc4be7bbc6..70900886cee 100644 --- a/PWGUD/Tasks/upcTrackVertexingQA.cxx +++ b/PWGUD/Tasks/upcTrackVertexingQA.cxx @@ -14,14 +14,15 @@ /// \author Andrea Tavira Garcia a.tavira@cern.ch /// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch -#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include +#include #include #include #include @@ -30,10 +31,9 @@ #include #include -#include -#include -#include -#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include @@ -72,7 +72,7 @@ struct UpcTrackVertexingQA { HistogramRegistry registry{"registry"}; // services - Service ccdb; // access to database + Service ccdb{}; // access to database // variables to store run info int runNumberBc = 0; // run number used to process BCs @@ -85,8 +85,8 @@ struct UpcTrackVertexingQA { int64_t nTF = 0; // number of time frames in run // constants - const unsigned int twoTracks = 2; - const int unassignedDCA = 999; + static constexpr unsigned int TwoTracks = 2; + static constexpr int UnassignedDCA = 999; static constexpr float MassPion = o2::constants::physics::MassPionCharged; static constexpr float MassMuon = o2::constants::physics::MassMuon; @@ -221,7 +221,7 @@ struct UpcTrackVertexingQA { if (track.pt() < ptTrackMin || std::abs(track.eta()) > etaTrackMax) { return false; } - if (std::abs(track.dcaZ()) >= unassignedDCA || std::abs(track.dcaXY()) >= unassignedDCA) { + if (std::abs(track.dcaZ()) >= UnassignedDCA || std::abs(track.dcaXY()) >= UnassignedDCA) { return false; } return true; @@ -329,7 +329,7 @@ struct UpcTrackVertexingQA { registry.fill(HIST("TrkColl/hTrackSizePerEvent"), goodTracks.size()); // build UPC candidates using events with exactly two opposite sign good tracks - if (goodTracks.size() != twoTracks) { + if (goodTracks.size() != TwoTracks) { return; } auto const& track0 = goodTracks[0]; @@ -386,10 +386,7 @@ struct UpcTrackVertexingQA { registry.fill(HIST("McGen/Part/hTrackSizePerEvent"), goodMcGenParticles.size()); // consider only j/psi->mumu and rho->pipi events - auto genId = mcCollision.getGeneratorId(); - LOGF(info, "Gen Id: {%d}", genId); auto sourceId = mcCollision.getSourceId(); - LOGF(info, "Source Id: {%d}", sourceId); if constexpr (isEmbedded) { if (!sourceId) { @@ -402,7 +399,7 @@ struct UpcTrackVertexingQA { } } - if (goodMcGenParticles.size() != twoTracks) { + if (goodMcGenParticles.size() != TwoTracks) { return; } @@ -413,7 +410,7 @@ struct UpcTrackVertexingQA { LOGF(fatal, "Selected particle has no mother: pdgCode=%d", goodMcGenParticles[0].pdgCode()); return; } - auto motherPart = mothers[0]; + auto const& motherPart = mothers[0]; registry.fill(HIST("McGen/MotherPart/hMotherPdg"), std::abs(motherPart.pdgCode())); registry.fill(HIST("McGen/MotherPart/hPt"), motherPart.pt()); From 07682c5c8614d0867fecf2b96d50c0fab053fa49 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Fri, 16 Oct 2026 12:22:39 +0530 Subject: [PATCH 304/648] [PWGCF] Lambda-Proton Balance function - Update 7 (#18305) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 171 ++++-------------- 1 file changed, 37 insertions(+), 134 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 83aae01ed99..51b3a29335b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -88,7 +88,6 @@ struct CentRho2Set { struct LambdaProtonBalanceFunction { HistogramRegistry registryLambda{"Lambda_invMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryLambdaExtended{"Lambda_invMassExtended", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryRho{"rho1ANDrho2_LP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryCent{"rho1ANDrho2_LP_CentralityBased", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryOther{"Other_Hists", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -101,19 +100,6 @@ struct LambdaProtonBalanceFunction { // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // ════════════════════════════════════════════════════════════════════════ - // Static compile-time constants (not non-static data members: do not count - // toward the O2 StructToTuple arity). - // ════════════════════════════════════════════════════════════════════════ - static constexpr int kExtNyBins = 2; - static constexpr int kExtNptBins = 25; - static constexpr float kExtYMin = -0.8f; - static constexpr float kExtYMax = -0.6f; - static constexpr float kExtYStep = 0.1f; - static constexpr float kExtPtMin = 0.0f; - static constexpr float kExtPtMax = 2.5f; - static constexpr float kExtPtStep = 0.1f; - // ════════════════════════════════════════════════════════════════════════ // TaskState — ordinary C++ runtime/implementation state. // Grouped into ONE nested object so that the number of direct data members of @@ -133,17 +119,6 @@ struct LambdaProtonBalanceFunction { std::shared_ptr hMassLambdaMerged; std::shared_ptr hMassAntiLambdaMerged; - // ════════════════════════════════════════════════════════════════════════ - // SECTION A2— Extended invariant-mass histograms (y × pT grid) - // hMassLambdaExtended[iY][iPt] → Lambda_invMassExtended/Lambda/ - // hMassAntiLambdaExtended[iY][iPt] → Lambda_invMassExtended/AntiLambda/ - // Rapidity bins (kExtNyBins = 2): [-0.8,-0.7), [-0.7,-0.6] - // pT bins (kExtNptBins = 25): [0.0,0.1), [0.1,0.2), ..., [2.4,2.5] - // ════════════════════════════════════════════════════════════════════════ - - std::vector>> hMassLambdaExtended; - std::vector>> hMassAntiLambdaExtended; - // ════════════════════════════════════════════════════════════════════════ // SECTION C — rho2 rapidity-space histograms, filled with unrolledIndexY(y, phi). // Naming: hRho2__y_AllPt. @@ -392,7 +367,7 @@ struct LambdaProtonBalanceFunction { } // QA-only: true => pair would survive the q_inv cut, regardless of the cUseQinvCut master switch. - // Used ONLY for the "After_qinvCut" QA histograms so they always show the mathematical effect of the cut. + // Used for ALL "After_qinvCut" QA histograms (PP, pAp, App, ApAp, LP-family, LL-family) so they always show the mathematical effect of the cut. // Physics logic must keep using failsQinvCut(). bool passesQinvQA(float qinv) const { return qinv > cQinvCutValue.value; } @@ -2016,53 +1991,6 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - // ── Extended y × pT invariant-mass histograms ───────────────────────── - // Rapidity bins: 2 bins from -0.8 to -0.6 in steps of 0.1 - // pT bins: 25 bins from 0.0 to 2.5 in steps of 0.1 - // Folder structure: - // Lambda_invMassExtended/Lambda/hMassLambda_yBinXX_ptBinYY - // Lambda_invMassExtended/AntiLambda/hMassAntiLambda_yBinXX_ptBinYY - state.hMassLambdaExtended.clear(); - state.hMassAntiLambdaExtended.clear(); - state.hMassLambdaExtended.resize(kExtNyBins); - state.hMassAntiLambdaExtended.resize(kExtNyBins); - - for (int iY = 0; iY < kExtNyBins; ++iY) { - const float yLow = kExtYMin + iY * kExtYStep; - const float yHigh = yLow + kExtYStep; - // Last rapidity bin [-0.7,-0.6] has inclusive upper edge in filling logic - const bool lastYBin = (iY == kExtNyBins - 1); - const char* yUpBracket = lastYBin ? "<=" : "<"; - - state.hMassLambdaExtended[iY].resize(kExtNptBins); - state.hMassAntiLambdaExtended[iY].resize(kExtNptBins); - - for (int iPt = 0; iPt < kExtNptBins; ++iPt) { - const float ptLow = kExtPtMin + iPt * kExtPtStep; - const float ptHigh = ptLow + kExtPtStep; - // Last pT bin [2.4,2.5] has inclusive upper edge in filling logic - const bool lastPtBin = (iPt == kExtNptBins - 1); - const char* ptUpBracket = lastPtBin ? "<=" : "<"; - - // Lambda - state.hMassLambdaExtended[iY][iPt] = - registryLambdaExtended.add( - Form("Lambda/hMassLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("#Lambda mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", - yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), - {HistType::kTH1F, {lambdaMassAxis}}); - - // AntiLambda - state.hMassAntiLambdaExtended[iY][iPt] = - registryLambdaExtended.add( - Form("AntiLambda/hMassAntiLambda_yBin%02d_ptBin%02d", iY, iPt), - Form("#bar{#Lambda} mass (no mass-window cut) | %.1f #leq y %s %.1f | %.1f #leq #it{p}_{T} %s %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", - yLow, yUpBracket, yHigh, ptLow, ptUpBracket, ptHigh), - {HistType::kTH1F, {lambdaMassAxis}}); - } - } - // ───────────────────────────────────────────────────────────────────── - // ══════════════════════════════════════════════════════════════════════ // Centrality-differential histograms → top-level folder "rho1ANDrho2_LP_CentralityBased" // ══════════════════════════════════════════════════════════════════════ @@ -2461,7 +2389,6 @@ struct LambdaProtonBalanceFunction { // const float eta = v0.eta(); // VS Code: commented out unused variable. // const float pz = pt * std::sinh(eta); - const float y = v0DauPID_AsLambda ? v0.rapidity(1) : v0.rapidity(2); int bin = -1; for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { @@ -2487,27 +2414,6 @@ struct LambdaProtonBalanceFunction { if (selLambda) { registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); } - - // ── Extended y × pT Lambda invariant-mass fill ──────────────────── - // Use the same rapidity variable `y` computed above - // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. - if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { - // Determine rapidity bin index - int iYext = static_cast((y - kExtYMin) / kExtYStep); - if (iYext >= kExtNyBins) { - iYext = kExtNyBins - 1; - } // clamp upper edge of last bin - // Determine pT bin index - int iPtext = static_cast((pt - kExtPtMin) / kExtPtStep); - if (iPtext >= kExtNptBins) { - iPtext = kExtNptBins - 1; - } // clamp upper edge of last bin - if (iYext >= 0 && iPtext >= 0) { - state.hMassLambdaExtended[iYext][iPtext]->Fill(v0.mLambda()); - } - } - // ───────────────────────────────────────────────────────────────── } if (v0DauPID_AsAntiLambda) { if (bin >= 0) { @@ -2522,25 +2428,6 @@ struct LambdaProtonBalanceFunction { if (selAntiLambda) { registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); } - - // ── Extended y × pT AntiLambda invariant-mass fill ──────────────── - // Use the same rapidity variable `y` computed above - // and the same pT variable `pt` = v0.pt() already in scope. - // Accept only -0.8 <= y <= -0.6 and 0.0 <= pT <= 2.5 GeV/c. - if (y >= kExtYMin && y <= kExtYMax && pt >= kExtPtMin && pt <= kExtPtMax) { - int iYext = static_cast((y - kExtYMin) / kExtYStep); - if (iYext >= kExtNyBins) { - iYext = kExtNyBins - 1; - } // clamp upper edge of last bin - int iPtext = static_cast((pt - kExtPtMin) / kExtPtStep); - if (iPtext >= kExtNptBins) { - iPtext = kExtNptBins - 1; - } // clamp upper edge of last bin - if (iYext >= 0 && iPtext >= 0) { - state.hMassAntiLambdaExtended[iYext][iPtext]->Fill(v0.mAntiLambda()); - } - } - // ───────────────────────────────────────────────────────────────── } // ───────────────────────────────────────────────────────────────── @@ -3165,11 +3052,11 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted Lambda-proton pair float qinvLP = 0.0f; { - const float dauPmag = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float pprmag = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); - const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Eppr = std::sqrt(pprmag * pprmag + kMassProton * kMassProton); - qinvLP = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau, primProton.px(), primProton.py(), primProton.pz(), Eppr); + qinvLP = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); } // QA3/SplitTrackQA: q_inv Before and After cut @@ -3191,6 +3078,10 @@ struct LambdaProtonBalanceFunction { const float pprmag_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); const float Eppr_3d = std::sqrt(pprmag_3d * pprmag_3d + kMassProton * kMassProton); const float qinvLP_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3d = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3d = std::sqrt(dauPmag_3d * dauPmag_3d + kMassProton * kMassProton); + const float qinvLP_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); // Angular variables w.r.t. Lambda daughter proton const float dPhi_dau_p = RecoDecay::constrainAngle((v0PosDau.phi() - primProton.phi()), -PIHalf3D); const float dEta_dau_p = v0PosDau.eta() - primProton.eta(); @@ -3198,10 +3089,10 @@ struct LambdaProtonBalanceFunction { const float dY_dau_p = yDauP - yPrimP; // eta space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp"), qinvLP_V0, dPhi_V0_p, dEta_V0_p); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau"), qinvLP, dPhi_dau_p, dEta_dau_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau"), qinvLP_dau, dPhi_dau_p, dEta_dau_p); // y space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp"), qinvLP_V0, dPhi_V0_p, dY_V0_p); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau"), qinvLP, dPhi_dau_p, dY_dau_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau"), qinvLP_dau, dPhi_dau_p, dY_dau_p); } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { @@ -3260,11 +3151,11 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted Lambda-antiproton pair float qinvLAp = 0.0f; { - const float dauPmag = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float paprmag = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Epapr = std::sqrt(paprmag * paprmag + kMassProton * kMassProton); - qinvLAp = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); + qinvLAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); } // QA3/SplitTrackQA: q_inv Before and After cut @@ -3286,6 +3177,10 @@ struct LambdaProtonBalanceFunction { const float paprmag_3dL = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); const float Epapr_3dL = std::sqrt(paprmag_3dL * paprmag_3dL + kMassProton * kMassProton); const float qinvLAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3dL = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3dL = std::sqrt(dauPmag_3dL * dauPmag_3dL + kMassProton * kMassProton); + const float qinvLAp_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); // Angular variables w.r.t. Lambda daughter proton const float dPhi_dau_ap = RecoDecay::constrainAngle((v0PosDau.phi() - primAntiProton.phi()), -PIHalf3D); const float dEta_dau_ap = v0PosDau.eta() - primAntiProton.eta(); @@ -3293,10 +3188,10 @@ struct LambdaProtonBalanceFunction { const float dY_dau_ap = yDauPL - yPrimAp; // eta space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp"), qinvLAp_V0, dPhi_V0_ap, dEta_V0_ap); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau"), qinvLAp, dPhi_dau_ap, dEta_dau_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dEta_dau_ap); // y space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp"), qinvLAp_V0, dPhi_V0_ap, dY_V0_ap); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau"), qinvLAp, dPhi_dau_ap, dY_dau_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dY_dau_ap); } // QA3/RawPairDensity: Lambda-antiproton → same LP histogram { @@ -3410,11 +3305,11 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-proton pair float qinvALp = 0.0f; { - const float dauPmag = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float pprmag3 = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); - const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Eppr3 = std::sqrt(pprmag3 * pprmag3 + kMassProton * kMassProton); - qinvALp = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), Edau, primProton.px(), primProton.py(), primProton.pz(), Eppr3); + qinvALp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr3); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); } // QA3/SplitTrackQA: q_inv Before and After cut @@ -3436,6 +3331,10 @@ struct LambdaProtonBalanceFunction { const float pprmag3_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); const float Eppr3_3d = std::sqrt(pprmag3_3d * pprmag3_3d + kMassProton * kMassProton); const float qinvALp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL_3d = std::sqrt(dauPmagAL_3d * dauPmagAL_3d + kMassProton * kMassProton); + const float qinvALp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); // Angular variables w.r.t. AntiLambda daughter antiproton const float dPhi_dauAL_p = RecoDecay::constrainAngle((v0NegDau.phi() - primProton.phi()), -PIHalf3D); const float dEta_dauAL_p = v0NegDau.eta() - primProton.eta(); @@ -3443,10 +3342,10 @@ struct LambdaProtonBalanceFunction { const float dY_dauAL_p = yDauAL - yPrimP3; // eta space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp"), qinvALp_V0, dPhi_AL_p, dEta_AL_p); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau"), qinvALp, dPhi_dauAL_p, dEta_dauAL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dEta_dauAL_p); // y space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp"), qinvALp_V0, dPhi_AL_p, dY_AL_p); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau"), qinvALp, dPhi_dauAL_p, dY_dauAL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dY_dauAL_p); } // QA3/RawPairDensity: AntiLambda-proton → same LP histogram { @@ -3488,11 +3387,11 @@ struct LambdaProtonBalanceFunction { // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-antiproton pair float qinvALAp = 0.0f; { - const float dauPmag = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); const float paprmag2 = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); - const float Edau = std::sqrt(dauPmag * dauPmag + kMassProton * kMassProton); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); const float Epapr2 = std::sqrt(paprmag2 * paprmag2 + kMassProton * kMassProton); - qinvALAp = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), Edau, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); + qinvALAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); } // QA3/SplitTrackQA: q_inv Before and After cut @@ -3514,6 +3413,10 @@ struct LambdaProtonBalanceFunction { const float paprmag2_3d = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); const float Epapr2_3d = std::sqrt(paprmag2_3d * paprmag2_3d + kMassProton * kMassProton); const float qinvALAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL2_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL2_3d = std::sqrt(dauPmagAL2_3d * dauPmagAL2_3d + kMassProton * kMassProton); + const float qinvALAp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); // Angular variables w.r.t. AntiLambda daughter antiproton const float dPhi_dauAL_ap = RecoDecay::constrainAngle((v0NegDau.phi() - primAntiProton.phi()), -PIHalf3D); const float dEta_dauAL_ap = v0NegDau.eta() - primAntiProton.eta(); @@ -3521,10 +3424,10 @@ struct LambdaProtonBalanceFunction { const float dY_dauAL_ap = yDauAL2 - yPrimAp2; // eta space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp"), qinvALAp_V0, dPhi_AL_ap, dEta_AL_ap); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau"), qinvALAp, dPhi_dauAL_ap, dEta_dauAL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dEta_dauAL_ap); // y space registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp"), qinvALAp_V0, dPhi_AL_ap, dY_AL_ap); - registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau"), qinvALAp, dPhi_dauAL_ap, dY_dauAL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dY_dauAL_ap); } // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks { From f899d8b72870d3bd604fc61ef8a801ede3ce9e55 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:04:05 +0200 Subject: [PATCH 305/648] [PWGHF] Cache D* soft pions propagation (#18214) Co-authored-by: Marcello Di Costanzo Co-authored-by: ALICE Action Bot --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 136 ++++++++++++------ 1 file changed, 93 insertions(+), 43 deletions(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index c4fa6635115..6f00d719a0b 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1506,6 +1506,8 @@ struct HfTrackIndexSkimCreator { std::array, kN2ProngDecays> binsPt2Prong{}; std::array, kN3ProngDecays> cut3Prong{}; std::array, kN3ProngDecays> binsPt3Prong{}; + LabeledArray cutsDstar{}; + std::vector binsPtDstar{}; // ML response o2::analysis::MlResponse hfMlResponse2Prongs; // only D0 @@ -1584,6 +1586,9 @@ struct HfTrackIndexSkimCreator { // cuts for 3-prong decays retrieved by json. the order must be then one in hf_cand_3prong::DecayType cut3Prong = {config.cutsDplusToPiKPi, config.cutsLcToPKPi, config.cutsDsToKKPi, config.cutsXicToPKPi, config.cutsCdToDeKPi, config.cutsCtToTrKPi, config.cutsChToHeKPi, config.cutsCaToAlKPi}; binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtLcToPKPi, config.binsPtDsToKKPi, config.binsPtXicToPKPi, config.binsPtCdToDeKPi, config.binsPtCtToTrKPi, config.binsPtChToHeKPi, config.binsPtCaToAlKPi}; + // D* cuts retrieved by json + cutsDstar = config.cutsDstarToD0Pi; + binsPtDstar = config.binsPtDstarToD0Pi; df2.setPropagateToPCA(config.propagateToPCA); df2.setMaxR(config.maxR); @@ -1724,6 +1729,15 @@ struct HfTrackIndexSkimCreator { } } + /// One track of the collision under study, propagated to that collision's primary vertex. + /// \tparam TTrackIndex is the iterator type of the track-collision associations + template + struct HfPropagatedSoftPion { + TTrackIndex trackIndex; ///< track-collision association, carries isSelProng and isIdentifiedPid + int64_t trackGlobalIndex{-1}; ///< track global index + std::array pVec{}; ///< momentum at the PCA to the primary vertex + }; + /// One track of the collision under study, propagated to that collision's primary vertex. /// \tparam TTrackIndex is the iterator type of the track-collision associations /// \tparam TTrack is the iterator type of the track table @@ -1741,21 +1755,44 @@ struct HfTrackIndexSkimCreator { /// \param collision is the collision under study /// \param trackIndices are the track associations of this collision /// \param prongs is the cache to be filled, in the order of trackIndices - template + template void fillPropagatedProngCache(TCollision const& collision, TTrackIndices const& trackIndices, std::vector& prongs) { prongs.clear(); const auto thisCollId = collision.globalIndex(); for (auto trackIndex = trackIndices.begin(); trackIndex != trackIndices.end(); ++trackIndex) { const auto track = trackIndex.template track_as(); - auto& prong = prongs.emplace_back(TProng{trackIndex, track, getTrackParCov(track), track.pVector(), {track.dcaXY(), track.dcaZ()}}); - if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.trackParVar, 2.f, noMatCorr, &prong.dcaInfo); - getPxPyPz(prong.trackParVar, prong.pVec); + if constexpr (IsSoftPion) { + std::array pVec = track.pVector(); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + auto trackParCov = getTrackParCov(track); + std::array dcaInfo{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCov, 2.f, noMatCorr, &dcaInfo); + getPxPyPz(trackParCov, pVec); + } + prongs.push_back(TProng{trackIndex, track.globalIndex(), pVec}); + } else { + auto& prong = prongs.emplace_back(TProng{trackIndex, track, getTrackParCov(track), track.pVector(), {track.dcaXY(), track.dcaZ()}}); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.trackParVar, 2.f, noMatCorr, &prong.dcaInfo); + getPxPyPz(prong.trackParVar, prong.pVec); + } } } } + /// Overloaded function of fillPropagatedProngCache to handle optional track indices. + /// \param collision is the collision under study + /// \param trackIndices are the track associations of this collision + /// \param prongs is the cache to be filled, in the order of trackIndices + template + void fillPropagatedProngCache(TCollision const& collision, std::optional const& trackIndices, std::vector& prongs) + { + if (trackIndices.has_value()) { + fillPropagatedProngCache(collision, *trackIndices, prongs); + } + } + /// Method to perform selections for 2-prong candidates before vertex reconstruction /// \param pVecTrack0 is the momentum array of the first daughter track /// \param pVecTrack1 is the momentum array of the second daughter track @@ -1827,10 +1864,10 @@ struct HfTrackIndexSkimCreator { // additional check for D0 to be used in D* finding if (iDecay2P == hf_cand_2prong::DecayType::D0ToPiK && config.doDstar && TESTBIT(isSelected, iDecay2P)) { - const auto binPtDstar = findBin(config.binsPtDstarToD0Pi, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe + const auto binPtDstar = findBin(&binsPtDstar, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe if (binPtDstar >= 0) { whichHypo[kN2ProngDecays] = whichHypo[hf_cand_2prong::DecayType::D0ToPiK]; - const double deltaMass = config.cutsDstarToD0Pi->get(binPtDstar, 1u); + const double deltaMass = cutsDstar.get(binPtDstar, 1u); if (TESTBIT(whichHypo[iDecay2P], 0) && (massHypos[0] > (MassD0 + deltaMass) * (MassD0 + deltaMass) || massHypos[0] < (MassD0 - deltaMass) * (MassD0 - deltaMass))) { CLRBIT(whichHypo[kN2ProngDecays], 0); @@ -2156,18 +2193,18 @@ struct HfTrackIndexSkimCreator { /// \param pVecTrack1 is the momentum array of the second daughter track (opposite charge) /// \param pVecTrack2 is the momentum array of the third daughter track (same charge) /// \param cutStatus is the cut status (filled only in case of debug) + /// \param invMassD0 is the invariant mass of the D0 candidate /// \param deltaMass is the M(Kpipi) - M(Kpi) value to be filled in the control histogram /// \return a bitmap with selection outcome template - uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2& deltaMass) + uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2 invMassD0, T2& deltaMass) { uint8_t isSelected{1}; const std::array arrMom{pVecTrack0, pVecTrack1, pVecTrack2}; - const std::array arrMomD0{pVecTrack0, pVecTrack1}; const auto pt = RecoDecay::pt(pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance; // add tolerance because of no reco decay vertex // pT - const auto binPt = findBin(config.binsPtDstarToD0Pi, pt); + const auto binPt = findBin(&binsPtDstar, pt); // return immediately if it is outside the defined pT bins if (binPt == -1) { isSelected = 0; @@ -2178,8 +2215,7 @@ struct HfTrackIndexSkimCreator { } // D0 mass - const double deltaMassD0 = config.cutsDstarToD0Pi->get(binPt, 1u); // 1u == deltaMassD0Index - const double invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + const double deltaMassD0 = cutsDstar.get(binPt, 1u); // 1u == deltaMassD0Index if (std::abs(invMassD0 - MassD0) > deltaMassD0) { isSelected = 0; if (config.debug) { @@ -2189,7 +2225,7 @@ struct HfTrackIndexSkimCreator { } // D*+ mass - const double maxDeltaMass = config.cutsDstarToD0Pi->get(binPt, 0u); // 0u == deltaMassIndex + const double maxDeltaMass = cutsDstar.get(binPt, 0u); // 0u == deltaMassIndex const double invMassDstar = RecoDecay::m(arrMom, std::array{MassPiPlus, MassKPlus, MassPiPlus}); deltaMass = invMassDstar - invMassD0; if (deltaMass > maxDeltaMass) { @@ -2356,9 +2392,17 @@ struct HfTrackIndexSkimCreator { using PropagatedProng = HfPropagatedProng; std::vector prongsPos{}; std::vector prongsNeg{}; + using SoftPionIterator = decltype(positiveSoftPions->sliceByCached(aod::track::collisionId, 0, cache).begin()); + using PropagatedSoftPi = HfPropagatedSoftPion; + std::vector softPionsPos{}; + std::vector softPionsNeg{}; + bool softPiPosCacheFilled{false}, softPiNegCacheFilled{false}; for (const auto& collision : collisions) { + softPiPosCacheFilled = false; + softPiNegCacheFilled = false; + /// retrieve PV contributors for the current collision std::vector vecPvContributorGlobId{}; std::vector vecPvContributorTrackParCov{}; @@ -2438,8 +2482,8 @@ struct HfTrackIndexSkimCreator { int lastFilledD0 = -1; // index to be filled in table for D* mesons // propagate each track to this collision's PV once, instead of once per pair or triplet - fillPropagatedProngCache(collision, groupedTrackIndicesPos1, prongsPos); - fillPropagatedProngCache(collision, groupedTrackIndicesNeg1, prongsNeg); + fillPropagatedProngCache(collision, groupedTrackIndicesPos1, prongsPos); + fillPropagatedProngCache(collision, groupedTrackIndicesNeg1, prongsNeg); for (std::size_t iPos1 = 0; iPos1 < prongsPos.size(); ++iPos1) { const auto& trackIndexPos1 = prongsPos[iPos1].trackIndex; @@ -3216,32 +3260,35 @@ struct HfTrackIndexSkimCreator { } } - if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > config.binsPtDstarToD0Pi->at(0) && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) - // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) + if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > binsPtDstar[0] && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) + // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) // second loop over positive tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 0) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexNeg1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0 candidates; moreover if kaon PID enabled, apply to the negative track - if (!groupedTrackIndicesSoftPionsPos) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackPos1, pVecTrackNeg1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiPosCacheFilled) { groupedTrackIndicesSoftPionsPos.emplace(positiveSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsPos, softPionsPos); + softPiPosCacheFilled = true; } - for (auto trackIndexPos2 = groupedTrackIndicesSoftPionsPos->begin(); trackIndexPos2 != groupedTrackIndicesSoftPionsPos->end(); ++trackIndexPos2) { - if (trackIndexPos2 == trackIndexPos1) { + + for (std::size_t iPos2 = 0; iPos2 < softPionsPos.size(); ++iPos2) { + if (softPionsPos[iPos2].trackIndex == trackIndexPos1) { continue; } - auto trackPos2 = trackIndexPos2.template track_as(); - std::array pVecTrackPos2{trackPos2.pVector()}; - if (thisCollId != trackPos2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarPos2 = getTrackParCov(trackPos2); - std::array dcaInfoPos2{trackPos2.dcaXY(), trackPos2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarPos2, 2.f, noMatCorr, &dcaInfoPos2); - getPxPyPz(trackParVarPos2, pVecTrackPos2); - } + + const auto& trackGlobalIndexPos2 = softPionsPos[iPos2].trackGlobalIndex; + const auto& pVecTrackPos2 = softPionsPos[iPos2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackPos2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexPos2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } @@ -3259,28 +3306,31 @@ struct HfTrackIndexSkimCreator { // second loop over negative tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 1) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexPos1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0bar candidates; moreover if kaon PID enabled, apply to the positive track - if (!groupedTrackIndicesSoftPionsNeg) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackNeg1, pVecTrackPos1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiNegCacheFilled) { groupedTrackIndicesSoftPionsNeg.emplace(negativeSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsNeg, softPionsNeg); + softPiNegCacheFilled = true; } - for (auto trackIndexNeg2 = groupedTrackIndicesSoftPionsNeg->begin(); trackIndexNeg2 != groupedTrackIndicesSoftPionsNeg->end(); ++trackIndexNeg2) { - if (trackIndexNeg1 == trackIndexNeg2) { + + for (std::size_t iNeg2 = 0; iNeg2 < softPionsNeg.size(); ++iNeg2) { + if (trackIndexNeg1 == softPionsNeg[iNeg2].trackIndex) { continue; } - auto trackNeg2 = trackIndexNeg2.template track_as(); - std::array pVecTrackNeg2{trackNeg2.pVector()}; - if (thisCollId != trackNeg2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarNeg2 = getTrackParCov(trackNeg2); - std::array dcaInfoNeg2{trackNeg2.dcaXY(), trackNeg2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarNeg2, 2.f, noMatCorr, &dcaInfoNeg2); - getPxPyPz(trackParVarNeg2, pVecTrackNeg2); - } + + const auto& trackGlobalIndexNeg2 = softPionsNeg[iNeg2].trackGlobalIndex; + const auto& pVecTrackNeg2 = softPionsNeg[iNeg2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackNeg2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexNeg2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } From b2a325ab94c48038aaf698ee412fde98de4e575e Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Fri, 16 Oct 2026 11:13:07 +0100 Subject: [PATCH 306/648] [PWGLF] cascade dca (#18326) --- PWGLF/DataModel/LFNonPromptCascadeTables.h | 11 +++++ PWGLF/Tasks/Strangeness/nonPromptCascade.cxx | 43 ++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/PWGLF/DataModel/LFNonPromptCascadeTables.h b/PWGLF/DataModel/LFNonPromptCascadeTables.h index 25449bc2f81..f8a532448f4 100644 --- a/PWGLF/DataModel/LFNonPromptCascadeTables.h +++ b/PWGLF/DataModel/LFNonPromptCascadeTables.h @@ -63,6 +63,9 @@ DECLARE_SOA_COLUMN(PionDCAz, pionDCAz, float); DECLARE_SOA_COLUMN(BachDCAxy, bachDCAxy, float); DECLARE_SOA_COLUMN(BachDCAz, bachDCAz, float); +DECLARE_SOA_COLUMN(CascDCADaughters, cascDCADaughters, float); // DCA between V0 and bachelor +DECLARE_SOA_COLUMN(V0DCADaughters, v0DCADaughters, float); // DCA between proton and pion + DECLARE_SOA_COLUMN(CascCosPA, casccosPA, float); DECLARE_SOA_COLUMN(V0CosPA, v0cosPA, float); @@ -167,6 +170,8 @@ DECLARE_SOA_TABLE(NPCascTable, "AOD", "NPCASCTABLE", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -235,6 +240,8 @@ DECLARE_SOA_TABLE(NPCascTableNT, "AOD", "NPCASCTABLENT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -309,6 +316,8 @@ DECLARE_SOA_TABLE(NPCascTableMC, "AOD", "NPCASCTABLEMC", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -396,6 +405,8 @@ DECLARE_SOA_TABLE(NPCascTableMCNT, "AOD", "NPCASCTABLEMCNT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, diff --git a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx index 5ae932534dd..3e0a451e21b 100644 --- a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx +++ b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,8 @@ struct NPCascCandidate { float pionDCAz; float bachDCAxy; float bachDCAz; + float cascDCADaughters; + float v0DCADaughters; float casccosPA; float v0cosPA; double massXi; @@ -233,6 +236,7 @@ struct NonPromptCascadeTask { Configurable cfgMaxMult{"cfgMaxMult", 8000.f, "Upper range of multiplicty histo"}; Configurable cfgMaxMultFV0{"cfgMaxMultFV0", 10000.f, "Upper range of multiplicty FV0 histo"}; Configurable cfgPtEdgesdNdeta{"ptEdges", "0,0.2,0.4,0.6,0.8,1,1.2,1.6,2.0,2.4,2.8,3.2,3.6,4,4.5,5,5.5,6,7,8,10", "Pt bin edges (comma-separated)"}; + Configurable cfgDownscaleCascTF{"cfgDownscaleCascTF", 1, "Keep one in every N input timeframes for cascade reco and generator output (1 keeps all)"}; Configurable cfgDownscaleMB{"cfgDownscaleMB", 1, "Downscaling for pile up study sample"}; Configurable cfgEtaCutdNdeta{"cfgEtaCutdNdeta", 0.8, "Eta cut for charged tracks"}; @@ -241,6 +245,8 @@ struct NonPromptCascadeTask { SliceCache cache; Service mCCDB; + uint64_t mTimeframeCount = 0; + bool mKeepTimeframe = true; int mRunNumber = 0; float mBz = 0.f; o2::vertexing::DCAFitterN<2> mDCAFitter; @@ -272,8 +278,21 @@ struct NonPromptCascadeTask { } } + // The framework calls run once before all enabled process functions for a timeframe. + void run(ProcessingContext&) + { + mKeepTimeframe = (mTimeframeCount++ % static_cast(cfgDownscaleCascTF.value)) == 0; + mRegistry.fill(HIST("hTimeframeSelection"), mKeepTimeframe ? 1.f : 0.f); + } + void init(InitContext& context) { + if (cfgDownscaleCascTF.value < 1) { + LOG(fatal) << "cfgDownscaleCascTF must be at least 1"; + } + auto timeframeSelection = mRegistry.add("hTimeframeSelection", ";Timeframe selection;Timeframes", HistType::kTH1D, {{2, -0.5, 1.5}}); + timeframeSelection->GetXaxis()->SetBinLabel(1, "Skipped"); + timeframeSelection->GetXaxis()->SetBinLabel(2, "Kept"); mZorroSummary.setObject(mZorro.getZorroSummary()); mCCDB->setURL(ccdbUrl); mCCDB->setFatalWhenNull(true); @@ -529,11 +548,14 @@ struct NonPromptCascadeTask { o2::math_utils::SVector cascadePos, v0Pos; float cascCpa = -1, v0Cpa = -1; + float cascDCADaughters = -1.f, v0DCADaughters = -1.f; o2::track::TrackParCov ntCascadeTrack; if (mDCAFitter.process(pionTrkParCov, protonTrkParCov)) { + v0DCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); auto trackParCovV0 = mDCAFitter.createParentTrackParCov(0); // V0 track retrieved from p and pi daughters v0Pos = mDCAFitter.getPCACandidate(); if (mDCAFitter.process(trackParCovV0, bachTrkParCov)) { + cascDCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); mDCAFitter.getTrackParamAtPCA(0).getPxPyPzGlo(momenta[0]); mDCAFitter.getTrackParamAtPCA(1).getPxPyPzGlo(momenta[1]); ntCascadeTrack = mDCAFitter.createParentTrackParCov(); @@ -655,7 +677,7 @@ struct NonPromptCascadeTask { cascadeLvector.pt(), cascadeLvector.eta(), cascadeLvector.phi(), protonTrack.pt(), protonTrack.eta(), pionTrack.pt(), pionTrack.eta(), bachelor.pt(), bachelor.eta(), motherDCA.getY(), motherDCA.getZ(), protonDCA.getY(), protonDCA.getZ(), pionDCA.getY(), pionDCA.getZ(), bachDCA.getY(), bachDCA.getZ(), - cascCpa, v0Cpa, massXi, massOmega, v0mass, + cascDCADaughters, v0DCADaughters, cascCpa, v0Cpa, massXi, massOmega, v0mass, static_cast(std::hypot(cascadePos[0], cascadePos[1])), static_cast(std::hypot(v0Pos[0], v0Pos[1])), static_cast(std::hypot(cascadePos[0], cascadePos[1], cascadePos[2])), static_cast(std::hypot(v0Pos[0], v0Pos[1], v0Pos[2])), cascITSclusters, protonTrack.itsNCls(), pionTrack.itsNCls(), bachelor.itsNCls(), protonTrack.tpcNClsFound(), pionTrack.tpcNClsFound(), bachelor.tpcNClsFound(), protonTrack.tpcNSigmaPr(), pionTrack.tpcNSigmaPi(), bachelor.tpcNSigmaKa(), bachelor.tpcNSigmaPi(), @@ -673,7 +695,7 @@ struct NonPromptCascadeTask { c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, @@ -712,7 +734,7 @@ struct NonPromptCascadeTask { c.pvContributors, c.cascPVContribs, c.pvTimeResolution, c.pvX, c.pvY, c.pvZ, c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, c.protonTPCNSigma, c.pionTPCNSigma, c.bachKaonTPCNSigma, c.bachPionTPCNSigma, c.protonHasTOF, c.pionHasTOF, c.bachHasTOF, c.protonTOFNSigma, c.pionTOFNSigma, c.bachKaonTOFNSigma, c.bachPionTOFNSigma, @@ -748,6 +770,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); fillMCtable(mcParticles, collisions, gCandidates); } @@ -757,6 +782,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); fillMCtable(mcParticles, collisions, gCandidatesNT); // fillMultHistos(collisions); @@ -765,6 +793,9 @@ struct NonPromptCascadeTask { void processGenParticles(aod::McParticles const& mcParticles, aod::McCollisions const&) { + if (!mKeepTimeframe) { + return; + } for (const auto& p : mcParticles) { auto absCode = std::abs(p.pdgCode()); if (absCode != 3312 && absCode != 3334) { @@ -795,6 +826,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[0]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); @@ -806,6 +840,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[1]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); From ae5843ce7b0d05984b4ce6c4756b2938fa1362f8 Mon Sep 17 00:00:00 2001 From: Antonio Ortiz <42810873+aortizve@users.noreply.github.com> Date: Fri, 16 Oct 2026 04:32:27 -0600 Subject: [PATCH 307/648] [ALICE3] Fast simulations for the ALICE 3 MID (#18325) Co-authored-by: ALICE Action Bot --- ALICE3/DataModel/OTFMID.h | 68 ++ ALICE3/DataModel/ReducedTablesAlice3.h | 9 + ALICE3/TableProducer/OTF/CMakeLists.txt | 5 + ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx | 1123 +++++++++++++++++++ ALICE3/Tasks/CMakeLists.txt | 5 + ALICE3/Tasks/alice3-prefilterdimuon.cxx | 418 +++++++ 6 files changed, 1628 insertions(+) create mode 100644 ALICE3/DataModel/OTFMID.h create mode 100644 ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx create mode 100644 ALICE3/Tasks/alice3-prefilterdimuon.cxx diff --git a/ALICE3/DataModel/OTFMID.h b/ALICE3/DataModel/OTFMID.h new file mode 100644 index 00000000000..e188f75fd14 --- /dev/null +++ b/ALICE3/DataModel/OTFMID.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file OTFMID.h +/// \author David Dobrigkeit Chinellato +/// \author Antonio Ortiz, UNAM +/// \since 11/05/2023 +/// \brief Set of tables for the ALICE3 OTFMID information +/// TODO: remove the columns that are not usefulf for me + +#ifndef ALICE3_DATAMODEL_OTFMID_H_ +#define ALICE3_DATAMODEL_OTFMID_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace upgrade_mid +{ +DECLARE_SOA_COLUMN(MIDTrackTime, midTrackTime, float); //! Track time generated at the MID +DECLARE_SOA_COLUMN(MIDTrackLength, midTrackLength, float); //! track length for calculation of MID (generated) + +DECLARE_SOA_COLUMN(MIDEventTime, midEventTime, float); //! Event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDEventTimeErr, midEventTimeErr, float); //! Uncertainty on the event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDTrackPosReco, midTrackPosReco, std::vector); //! Measured position in MID +DECLARE_SOA_COLUMN(MIDTrackPos, midTrackPos, std::vector); //! Expected position in MID +DECLARE_SOA_COLUMN(MIDCandidate, midCandidate, bool); //! Muon candidate +DECLARE_SOA_COLUMN(MIDTrackTimeReco, midTrackTimeReco, float); //! Track time measured at MID +DECLARE_SOA_COLUMN(MIDTrackLengthReco, midTrackLengthReco, float); //! track length for calculation of MID (reconstructed) + +} // namespace upgrade_mid + +DECLARE_SOA_TABLE(UpgradeMidMCs, "AOD", "UPGRADEMIDMC", + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +DECLARE_SOA_TABLE(UpgradeMids, "AOD", "UPGRADEMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + +DECLARE_SOA_TABLE(UpgradeMidExpecteds, "AOD", "UPGRADEMIDEXPT", + upgrade_mid::MIDTrackPos, + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +using UpgradeMidMC = UpgradeMidMCs::iterator; +using UpgradeMid = UpgradeMids::iterator; +using UpgradeMidExpected = UpgradeMidExpecteds::iterator; + +} // namespace o2::aod + +#endif // ALICE3_DATAMODEL_OTFMID_H_ diff --git a/ALICE3/DataModel/ReducedTablesAlice3.h b/ALICE3/DataModel/ReducedTablesAlice3.h index 363ba50842f..7ff870f9308 100644 --- a/ALICE3/DataModel/ReducedTablesAlice3.h +++ b/ALICE3/DataModel/ReducedTablesAlice3.h @@ -20,6 +20,7 @@ #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "ALICE3/DataModel/OTFMID.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFTOF.h" @@ -275,6 +276,14 @@ DECLARE_SOA_TABLE(ReducedA3PIDTOF, "AOD", "REA3PIDTOF", upgrade_tof::NSigmaHelium3OuterTOF, upgrade_tof::NSigmaAlphaOuterTOF>); +DECLARE_SOA_TABLE(ReducedA3PIDMID, "AOD", "REA3PIDMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + DECLARE_SOA_TABLE(ReducedA3PIDRich, "AOD", "REA3PIDRICH", upgrade_rich::NSigmaElectronRich, upgrade_rich::NSigmaMuonRich, diff --git a/ALICE3/TableProducer/OTF/CMakeLists.txt b/ALICE3/TableProducer/OTF/CMakeLists.txt index 6fe3f41a02f..d92809cae76 100644 --- a/ALICE3/TableProducer/OTF/CMakeLists.txt +++ b/ALICE3/TableProducer/OTF/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(onthefly-tofpid PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(onthefly-midpid + SOURCES onTheFlyMidPid.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(onthefly-richpid SOURCES onTheFlyRichPid.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker diff --git a/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx new file mode 100644 index 00000000000..6b69d082961 --- /dev/null +++ b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx @@ -0,0 +1,1123 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file onTheFlyMidPid.cxx +/// +/// \brief This task goes straight from a combination of track table and mcParticles +/// and a custom MID configuration to a table of muon id for the particles +/// being analysed. +/// +/// \author David Dobrigkeit Chinellato, UNICAMP +/// \author Antonio Ortiz, UNAM +/// \since May 22, 2024 +/// + +#include "GeometryContainer.h" + +#include "ALICE3/Core/FlatTrackSmearer.h" +#include "ALICE3/Core/TrackUtilities.h" +#include "ALICE3/DataModel/OTFCollision.h" +#include "ALICE3/DataModel/OTFMID.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct OnTheFlyMidPid { + Produces upgradeMidMC; + Produces upgradeMid; + Produces upgradeMidExpected; + + // necessary for particle charges + Service pdgDatabase; + // Necessary for LUTs + Service ccdb; + + // these are the settings governing the MID layer to be used + // in the evolution of this effort + struct : ConfigurableGroup { + Configurable considerEventTime{"considerEventTime", true, "flag to consider event time"}; + // Parametrization of muon/pion absortion in iron + Configurable magRadiusC{"magRadiusC", 160., "Magnet coil radius (cm)"}; + Configurable absRadius1{"absRadius1", 220., "Absorber inner radius (cm)"}; + Configurable absRadius2{"absRadius2", 290., "Absorber outer radius (cm)"}; + Configurable absDensity{"absDensity", 7.874, "iron density (g/cm3)"}; + Configurable absLambdaPi{"absLambdaPi", 20.41, "illustrative effective interaction length (cm)"}; + Configurable absDeDx{"absDeDxMIP", 1.7, "dE/(rho dx) MIP MeV/g/cm2"}; + Configurable absFakeAt3{"absFakeAt3", 0.032, "fake-muon efficiency at 3 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absFakeAt10{"absFakeAt10", 0.08, "fake-muon efficiency at 10 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absSigmaEta{"absSigmaEta", 10.65, "user-configurable phenomenological eta width"}; + Configurable absPionFractionAt3{"absPionFractionAt3", 0.5, "parameter from GEANT4+BDT"}; // assumed, NOT measured + Configurable absEtaMax{"absEtaMax", 1.24, "max eta covered by detector"}; + Configurable midRadius{"midRadius", 302.5, "MID radius (cm)"}; + Configurable midLength{"midLength", 1000.0, "MID length (cm)"}; + Configurable> midCellDimension{"midCellDimension", {5.1, 5.1}, "MID cell dimension in Z and RPhi (cm)"}; + Configurable midFractionOfInactiveArea{"midFractionOfInactiveArea", (0.1 / 5.1), "MID fraction of inactive area"}; + Configurable midTimeReso{"midTimeReso", 1500, "MID time error (ps)"}; + Configurable nStepsLIntegrator{"nStepsLIntegrator", 200, "number of steps in length integrator"}; + Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; + Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; + Configurable flagMIDLoadDelphesLUTs{"flagMIDLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + } simConfig; + + struct : ConfigurableGroup { + Configurable doQAplots{"doQAplots", true, "do basic velocity plot qa"}; + Configurable doSeparationVsPt{"doSeparationVsPt", true, "Produce plots vs pt or p"}; + Configurable nBinsBeta{"nBinsBeta", 2200, "number of bins in beta"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsTrackLength{"nBinsTrackLength", 500, "number of bins in track length"}; + Configurable nBinsNsigmaWrongSpecies{"nBinsNsigmaWrongSpecies", 200, "number of bins in Nsigma plot (wrong species)"}; + Configurable minNsigmaRange{"minNsigmaRange", -10, "lower limit for the nsigma axis"}; + Configurable maxNsigmaRange{"maxNsigmaRange", +10, "upper limit for the nsigma axis"}; + Configurable nBinsDeltaCorrectSpecies{"nBinsDeltaCorrectSpecies", 200, "number of bins in Delta plot (correct speies)"}; + Configurable nBinsDeltaWrongSpecies{"nBinsDeltaWrongSpecies", 200, "number of bins in Delta plot (wrong species)"}; + Configurable nBinsTrackDeltaLength{"nBinsTrackDeltaLength", 100, "number of bins in delta track length"}; + Configurable nBinsNsigmaCorrectSpecies{"nBinsNsigmaCorrectSpecies", 200, "number of bins in Nsigma plot (correct speies)"}; + Configurable minDeltaRange{"minDeltaRange", -100, "lower limit for the nsigma axis"}; + Configurable maxDeltaRange{"maxDeltaRange", +100, "upper limit for the nsigma axis"}; + Configurable nBinsTimeRes{"nBinsTimeRes", 400, "number of bins plots time resolution"}; + Configurable nBinsRelativeEtaPt{"nBinsRelativeEtaPt", 400, "number of bins plots pt and eta relative errors"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + Configurable nBinsMult{"nBinsMult", 200, "number of bins in multiplicity"}; + Configurable maxMultRange{"maxMultRange", 1000.f, "upper limit in multiplicity plots"}; + } plotsConfig; + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + + // Track smearer array, one per geometry + // std::vector> mSmearer; + std::vector> mSmearer; + // needed: random number generator for smearing + TRandom3 pRandomNumberGenerator; + + // for handling basic QA histograms if requested + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + OutputObj listEfficiency{"efficiency"}; + + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + void init(o2::framework::InitContext& initContext) + { + mGeoContainer.setCcdbManager(ccdb.operator->()); + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + + pRandomNumberGenerator.SetSeed(0); // fully randomize + if (simConfig.flagMIDLoadDelphesLUTs) { + for (int icfg = 0; icfg < nGeometries; ++icfg) { + mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; + } + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; + } + + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; + } + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } + } + } + } + + if (plotsConfig.doQAplots) { + const AxisSpec axisdNdeta{plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange, Form("dN/d#eta in |#eta| < %f", simConfig.multiplicityEtaRange.value)}; + + histos.add("h1dNdeta", "h2dNdeta", kTH1F, {axisdNdeta}); + histos.add("h2dEventTime", "h2dEventTime", kTH2F, {{2000, -10000, 10000, "computed"}, {2000, -10000, 10000, "generated"}}); + histos.add("h1dEventTimegen", "h1dEventTimegen", kTH1F, {{200, -1000, 1000, "generated"}}); + histos.add("h1dEventTimerec", "h1dEventTimerec", kTH1F, {{2000, -10000, 10000, "computed"}}); + histos.add("h1dEventTimedelta", "h1dEventTimedelta", kTH1F, {{200, -1000, 1000, "generated - computed"}}); + histos.add("h2dEventTimeres", "h2dEventTimeres", kTH2F, {axisdNdeta, {300, 0, 300, "resolution"}}); + listEfficiency.setObject(new THashList); + listEfficiency->Add(new TEfficiency("effEventTime", "effEventTime", plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange)); + + const AxisSpec axisMomentum{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisRigidity{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} / |#it{z}| (GeV/#it{c})"}; + const AxisSpec axisVelocity{static_cast(plotsConfig.nBinsBeta), 0.0f, +1.1f, "Measured #beta"}; + const AxisSpec axisMomentumSmall{static_cast(plotsConfig.nBinsP), 0.0f, +1.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisTrackLength{static_cast(plotsConfig.nBinsTrackLength), 0.0f, 1000.0f, "Track length (cm)"}; + const AxisSpec axisTrackDeltaLength{static_cast(plotsConfig.nBinsTrackDeltaLength), 0.0f, 30.0f, "Delta Track length (cm)"}; + + histos.add("h2dVelocityVsMomentumInner", "h2dVelocityVsMomentumInner", kTH2F, {axisMomentum, axisVelocity}); + histos.add("h2dVelocityVsRigidityInner", "h2dVelocityVsRigidityInner", kTH2F, {axisRigidity, axisVelocity}); + histos.add("h2dTrackLengthInnerVsPt", "h2dTrackLengthInnerVsPt", kTH2F, {axisMomentum, axisTrackLength}); + histos.add("h2dTrackLengthInnerRecoVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisTrackLength}); + + histos.add("h2dTrackLengthB2vsB0", "h2dDeltaTrackLengthInnerVsPt; for B=0; for B=2 T", kTH2F, {axisTrackLength, axisTrackLength}); + histos.add("h2HitMap", "h2HitMap", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitMapRec", "h2HitMapRec", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitDeltaXY", "h2HitDeltaXY", kTH2F, {{200, -100, 100, "#Delta#it{x} (cm)"}, {200, -100, 100, "#Delta#it{y} (cm)"}}); + + const AxisSpec axisPt{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtRes{1000, -1.0f, +1.0f, "(#it{p}_{T}^{gen}-#it{p}_{T}^{rec})/#it{p}_{T}^{gen}"}; + const AxisSpec axisEta{static_cast(plotsConfig.nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisRelativePt{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{p_{T}}} / #it{p_{T}} (%)"}; + const AxisSpec axisRelativeEta{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{#eta}} / #it{#eta} (%)"}; + histos.add("h2dEtaVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisEta}); + histos.add("h2dEtaVsP", "h2dTrackLengthInnerRecoVsP", kTH2F, {axisMomentum, axisEta}); + + const AxisSpec axisDelta{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta#{t} (ps)"}; + const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, "N#sigma - True "}; + const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta - True "}; + const AxisSpec axisX{plotsConfig.doSeparationVsPt.value ? axisPt : axisMomentum}; + // const AxisSpec axisNSigma{axisNsigmaCorrect}; + + const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Track time resolution (ps)"}; + const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Total time resolution (ps)"}; + + histos.add("h1PtRes", "", kTH2F, {axisMomentum, axisPtRes}); + histos.add("h2EkinVsEloss", "", kTH2F, {{200, 0.0f, 2000.f, "#it{E}_{loss} (MeV)"}, {200, 0.0f, 20000.f, "KE (MeV)"}}); + } + } + + struct MIDEfficiency { + private: + const float layerRadius; + const float layerLength; + const float cellDimensionZ; + const float cellDimensionRPhi; + const float fractionInactive; + const float magField; + + TAxis* axisZ = nullptr; + TAxis* axisRPhi = nullptr; + TAxis* axisInCellZ = nullptr; + TAxis* axisInCellRPhi = nullptr; + + TH2F* hHitMapInCell = nullptr; + TH2F* hHitMapInCellBefore = nullptr; + TH2F* hHitMap = nullptr; + + public: + ~MIDEfficiency() + { + if (0) { + hHitMap->SaveAs(Form("/tmp/%s.png", hHitMap->GetName())); + hHitMapInCell->SaveAs(Form("/tmp/%s.png", hHitMapInCell->GetName())); + hHitMapInCellBefore->SaveAs(Form("/tmp/%s.png", hHitMapInCellBefore->GetName())); + } + + delete axisZ; + delete axisRPhi; + delete axisInCellZ; + delete axisInCellRPhi; + delete hHitMap; + delete hHitMapInCell; + delete hHitMapInCellBefore; + } + + MIDEfficiency(const MIDEfficiency&) = delete; + MIDEfficiency& operator=(const MIDEfficiency&) = delete; + + MIDEfficiency(float r, float l, std::array pDimensions, float fIA, float m) + : layerRadius(r), layerLength(l), cellDimensionZ(pDimensions[0]), cellDimensionRPhi(pDimensions[1]), fractionInactive(fIA), magField(m) + { + // Assuming square cells for simplicity + const float circumference = 2.0f * M_PI * layerRadius; + axisZ = new TAxis(static_cast(layerLength / cellDimensionZ), -layerLength / 2, layerLength); + axisRPhi = new TAxis(static_cast(circumference / cellDimensionRPhi), 0.f, circumference); + + const float inactiveBorderRPhi = cellDimensionRPhi * std::sqrt(fractionInactive) / 2; + const float inactiveBorderZ = cellDimensionZ * std::sqrt(fractionInactive) / 2; + const double arrayRPhi[4] = {-cellDimensionRPhi / 2, -cellDimensionRPhi / 2 + inactiveBorderRPhi, cellDimensionRPhi / 2 - inactiveBorderRPhi, cellDimensionRPhi / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayRPhi[" << i << "] = " << arrayRPhi[i]; + } + axisInCellRPhi = new TAxis(3, arrayRPhi); + const double arrayZ[4] = {-cellDimensionZ / 2, -cellDimensionZ / 2 + inactiveBorderZ, cellDimensionZ / 2 - inactiveBorderZ, cellDimensionZ / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayZ[" << i << "] = " << arrayZ[i]; + } + axisInCellZ = new TAxis(3, arrayZ); + + hHitMap = new TH2F(Form("hHitMap_R%.0f", layerRadius), "HitMap;z (cm); r#phi (cm)", 1000, -1000, 1000, 1000, -1000, 1000); + hHitMapInCell = new TH2F(Form("hHitMapInCell_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + hHitMapInCellBefore = new TH2F(Form("hHitMapInCellBefore_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + } + + bool isInMIDActiveArea(std::array hitPosition) + { + if (fractionInactive <= 0.0f) { + return true; + } + if (fractionInactive >= 1.0f) { + return false; + } + + // Convert 3D position to cylindrical coordinates for area calculation + const float phi = std::atan2(hitPosition[1], hitPosition[0]); + const float rphi = phi * layerRadius; + const float z = hitPosition[2]; + const float r = std::sqrt(hitPosition[0] * hitPosition[0] + hitPosition[1] * hitPosition[1]); + + // Check if hit is within layer geometric acceptance + if (std::abs(layerRadius - r) > 10.f) { + LOG(debug) << "Hit out of MID layer acceptance: r=" << r << " cm with respect to the layer radius " << layerRadius; + return false; + } + if (std::abs(z) > layerLength / 2.0f) { + LOG(debug) << "Hit out of MID layer acceptance: z=" << z << " cm with respect to the layer length " << layerLength; + return false; + } + + const int cellIndexPhi = axisRPhi->FindBin(rphi); + const int cellIndexZ = axisZ->FindBin(z); + + if (cellIndexPhi <= 0 || cellIndexPhi > axisRPhi->GetNbins() || cellIndexZ <= 0 || cellIndexZ > axisZ->GetNbins()) { + return false; + } + // Calculate local position within the pixel + const float localRPhi = (rphi - axisRPhi->GetBinCenter(cellIndexPhi)); + const float localZ = (z - axisZ->GetBinCenter(cellIndexZ)); + + // The difference between the hit and the pixel position cannot be greater than the size of the pixel + if (std::abs(localRPhi - axisRPhi->GetBinCenter(cellIndexPhi)) > axisRPhi->GetBinWidth(cellIndexPhi)) { + // LOG(warning) << "Local hit difference in phi is bigger than the pixel size"; + } + if (std::abs(localZ - axisZ->GetBinCenter(cellIndexZ)) > axisZ->GetBinWidth(cellIndexZ)) { + // LOG(warning) << "Local hit difference in z is bigger than the pixel size"; + } + hHitMapInCellBefore->Fill(localZ, localRPhi); + switch (axisInCellRPhi->FindBin(localRPhi)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + switch (axisInCellZ->FindBin(localZ)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + hHitMapInCell->Fill(localZ, localRPhi); + hHitMap->Fill(z, rphi); + return true; + } + + /// Check if a track hits the active area (convenience function) + /// \param track the track parameters for automatic hit position calculation + /// \return true if the hit is in the active area + bool isTrackInActiveArea(const o2::track::TrackParCov& track) + { + if (fractionInactive <= 0.f) { + return true; + } + float x, y, z; + if (!track.getXatLabR(layerRadius, x, magField)) { + LOG(debug) << "Could not propagate track to TOF layer at radius " << layerRadius << " cm"; + return false; + } + bool b; + ROOT::Math::PositionVector3D hit = track.getXYZGloAt(x, magField, b); + if (!b) { + LOG(debug) << "Could not get hit position at radius " << layerRadius << " cm"; + return false; + } + hit.GetCoordinates(x, y, z); + return isInMIDActiveArea(std::array{x, y, z}); + } + }; + + bool isInMIDActiveArea(const o2::track::TrackParCov& track) + { + static MIDEfficiency midLayer(simConfig.midRadius, simConfig.midLength, simConfig.midCellDimension, simConfig.midFractionOfInactiveArea, mMagneticField); + return midLayer.isTrackInActiveArea(track); + } + + struct TracksWithTime { + TracksWithTime(int pdgCode, + std::pair midTime, + std::pair trackLengthMID, + std::pair trackLengthInIron, + std::pair, std::array> posInMid, + std::pair momentum, + std::pair pseudorapidity, + float noSmearingPt) + : mPdgCode(pdgCode), + mMIDTime(midTime), + mTrackLengthMID(trackLengthMID), + mTrackLengthInIron(trackLengthInIron), + mPosInMid(posInMid), + mMomentum(momentum), + mPseudorapidity{pseudorapidity}, + mNoSmearingPt{noSmearingPt} {} + + int mPdgCode; + std::pair mMIDTime; // Measured time and expected resolution at the MID [ps] + std::pair mTrackLengthMID; // Measured and generated length at the MID [cm] + std::pair mTrackLengthInIron; // Measured and generated length within absorber [cm] + std::pair, std::array> mPosInMid; + std::pair mMomentum; // Measured momentum and uncertainty on the momentum [GeV/c] + std::pair mPseudorapidity; // Measured pseudorapidity and uncertainty on the pseudorapidity + float mNoSmearingPt; // No smearing pt + }; + + std::vector tracksWithTime; + bool eventTime(const std::vector& tracks, + std::array& tzero) + { + + float sum = 0.; + float sumw = 0.; + + // Todo: check the different mass hypothesis iteratively + for (const auto& track : tracks) { + auto pdgInfo = pdgDatabase->GetParticle(track.mPdgCode); + if (pdgInfo == nullptr) { + continue; + } + const float mass = pdgInfo->Mass(); + const float mass2 = mass * mass; + const float tof = track.mMIDTime.first; // [ps] + if (tof <= 0) { + continue; + } + const float etof = track.mMIDTime.second; // [ps] + const float length = track.mTrackLengthMID.first; // [cm] + float p = track.mMomentum.first; // [GeV/c] + p *= std::abs(pdgInfo->Charge()) / 3.; // Total momentum + const float ep = track.mMomentum.second; // [GeV/c] + const float p2 = p * p; + const float lengthOverC = length * o2::constants::physics::invLightSpeedCm2PS; + const float texp = lengthOverC / p * std::sqrt(mass2 + p2); + // LOG(info) << "TOF: " << tof << " " << etof << " vs " << texp; + const float etexp = lengthOverC * mass2 / p2 / std::sqrt(mass2 + p2) * ep; + const float sigma = std::sqrt(etexp * etexp + etof * etof); + const float deltat = tof - texp; + + const float w = 1. / (sigma * sigma); + + sum += w * deltat; + sumw += w; + } + + static constexpr float kMaxEventTimeResolution = 200.f; + if (sumw <= 0. || tracks.size() <= 1 || std::sqrt(1. / sumw) > kMaxEventTimeResolution) { + tzero[0] = 0.; // [ps] + tzero[1] = kMaxEventTimeResolution; // [ps] + return false; + } + + tzero[0] = sum / sumw; + tzero[1] = std::sqrt(1. / sumw); + return true; + } + + /// returns track time resolution + /// \param pt the transverse momentum of the tarck + /// \param eta the pseudorapidity of the tarck + /// \param trackPtResolution the absolute resolution on pt + /// \param trackEtaResolution the absolute resolution on eta + /// \param mass the mass of the particle + /// \param detRadius the radius of the cylindrical layer + /// \param magneticField the magnetic field (along Z) + double calculateTrackTimeResolutionAdvanced(const float pt, + const float eta, + const float trackPtResolution, + const float trackEtaResolution, + const float mass, + const float detRadius, + const float magneticField) + { + // Compute tracking contribution to timing using the error propagation formula + // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) + float a0 = mass * mass; + float a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); + float a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; + float dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); + return trackTimeResolution; + } + + /* + // ============================================================ + // Track length in a B = 0 region + // + // Calculates the distance from the CURRENT track position + // to the cylinder of radius "radius". + // ============================================================ + */ + float computeTrackLengthB0(const o2::track::TrackParCov& track, + const float radius) + { + // Current global position + std::array pos{}; + track.getXYZGlo(pos); + + // Current global momentum + std::array mom{}; + if (!track.getPxPyPzGlo(mom)) { + return -100.f; + } + + const float px = mom[0]; + const float py = mom[1]; + // const float pz = mom[2]; + + const float pt = std::hypot(px, py); + + if (pt <= 0.f) { + return -100.f; + } + + // Unit vector in transverse plane + const float ux = px / pt; + const float uy = py / pt; + + const float b = pos[0] * ux + pos[1] * uy; + const float c = pos[0] * pos[0] + + pos[1] * pos[1] - + radius * radius; + + const float discriminant = b * b - c; + + if (discriminant < 0.f) { + return -100.f; + } + + const float sqrtDisc = std::sqrt(discriminant); + + const float t1 = -b + sqrtDisc; + const float t2 = -b - sqrtDisc; + + // We want the outward/forward intersection + float t = -1.f; + + if (t1 > 0.f && t2 > 0.f) { + t = std::min(t1, t2); + } else if (t1 > 0.f) { + t = t1; + } else if (t2 > 0.f) { + t = t2; + } else { + return -100.f; + } + + return t * std::sqrt(1.f + track.getTgl() * track.getTgl()); + } + + /* + // ============================================================ + // MID piecewise track length + // + // B = +20.0 kG R < R0 + // B = 0.0 kG R0 < R < R1 + // B = -14.3 kG R1 < R < R2 + // + // Returns total track length from current track position to R3. + // ============================================================ + */ + float computeTrackLengthMID(const o2::track::TrackParCov& inputTrack, + float R0, + float R1, + float R2, + float R3, + std::array& posMID, + std::array& deltaMID, + float& deltaposInIron) + { + float B0_m = +20.0f; // kG + float B1_m = 0.0f; // kG + float B2_m = 0.0f; + B2_m = -B0_m * pow(R0, 2) / (pow(R2, 2) - pow(R1, 2)); // kG + float B3_m = 0.0f; // kG + + // Work with a copy + auto track = inputTrack; + + float totalLength = 0.f; + float xAtR = 0.f; + + // ========================================================== + // REGION 1 + // current position -> R0 + // B = +20 kG + // ========================================================== + + const float L0 = + o2::upgrade::computeTrackLength(track, R0, B0_m); + + if (L0 < 0.f) { + return -100.f; + } + + totalLength += L0; + + // Find local x corresponding to global radius R0 + if (!track.getXatLabR(R0, + xAtR, + B0_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R0 + if (!track.propagateTo(xAtR, B0_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R0 -> R1 + // B = 0 + // ========================================================== + + const float L01 = computeTrackLengthB0(track, R1); + + if (L01 < 0.f) { + return -100.f; + } + + totalLength += L01; + + // Find local x at R1 assuming B = 0 + if (!track.getXatLabR(R1, + xAtR, + B1_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Straight-line propagation to R1 + if (!track.propagateTo(xAtR, B1_m)) { + return -100.f; + } + + // ========================================================== + // REGION 3 (absorber) + // R1 -> R2 + // B = -14.3 kG + // ========================================================== + + std::array pos1{}; + track.getXYZGlo(pos1); + + float L12 = + o2::upgrade::computeTrackLength(track, R2, B2_m); + + if (L12 < 0.f) { + return -100.f; + } + totalLength += L12; + deltaposInIron = L12; + // Find local x corresponding to global radius R2 + if (!track.getXatLabR(R2, + xAtR, + B2_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R2 + if (!track.propagateTo(xAtR, B2_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R2 -> R3 + // B = 0 + // ========================================================== + + const float L23 = computeTrackLengthB0(track, R3); + + if (L23 < 0.f) { + return -100.f; + } + + totalLength += L23; + + // Find local x corresponding to global radius R3 + if (!track.getXatLabR(R3, + xAtR, + B3_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R3 + if (!track.propagateTo(xAtR, B3_m)) { + return -100.f; + } + + // ========================================================== + // GLOBAL POSITION AT R3 + // ========================================================== + + std::array pos3{}; + track.getXYZGlo(pos3); + + posMID[0] = pos3[0]; + posMID[1] = pos3[1]; + posMID[2] = pos3[2]; + + // Displacement projections + deltaMID[0] = posMID[0] - pos1[0]; + deltaMID[1] = posMID[1] - pos1[1]; + deltaMID[2] = posMID[2] - pos1[2]; + + return totalLength; + } + // Multiple Coulomb scattering angle in iron + // + // momentum : particle momentum [GeV/c] + // thickness : iron thickness [cm] + // + // returns theta0 [rad] + + float moliereThetaIron(float momentum, float mass, float thickness) + { + float X0 = 1.757f; // radiation length of iron [cm] + // constexpr float mMuon = 0.105658f; // muon mass [GeV/c^2] + + if (momentum <= 0.f || thickness <= 0.f) { + return 0.f; + } + + // beta = p/E + const float energy = std::sqrt(momentum * momentum + + mass * mass); + + const float beta = momentum / energy; + + // Thickness in radiation lengths + const float xOverX0 = thickness / X0; + + // Highland approximation + const float theta0 = (0.0136f / (beta * momentum)) * sqrt(xOverX0) * (1.f + 0.038f * log(xOverX0)); + + return theta0; + } + float fPi(double p) + { + return p > 1. ? 1. - std::exp(-(p - 1.) / 1.5) : 0.; + } + float fMu(double p) + { + return p > 0.7 ? (1. - std::exp(-(p - 0.7) / 0.8)) * std::exp(-p / 3.) : 0.; + } + float refPi(double p) + { + if (p <= 1.) + return 0.; + float f3 = fPi(3.); + float mu10 = simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(10.) / fMu(3.); + float slope = ((simConfig.absFakeAt10 - mu10) / (simConfig.absFakeAt3 * simConfig.absPionFractionAt3) / (fPi(10.) / f3) - 1.) / 7.; + return std::max(0., simConfig.absFakeAt3 * simConfig.absPionFractionAt3) * fPi(p) / f3 * (1. + slope * (p - 3.)); + } + float refMu(double p) + { + return simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(p) / fMu(3.); + } + float decayAir(double p, double airCm) + { + if (p <= 0.) + return 0.; + return -std::expm1(-airCm * 0.13957039 / (p * 780.45)); // assume pion mass, ctau=780.45 cm + } + float survivalAir(double p, double airCm) + { + return 1. - decayAir(p, airCm); + } + // Range proxy: kinetic energy minus mean ionization loss in iron. + // Smoothed only to represent missing fluctuations; NOT a Geant4 range calculation. + float rangeTurnOn(double p, double ironCm, double mass) + { + if (p <= 0.) + return 0.; + float kinetic = std::sqrt(p * p + mass * mass) - mass; + float deficit = kinetic - (simConfig.absDensity * simConfig.absDeDx / 1000.) * ironCm; + float width = 0.10; // GeV, phenomenological + return 1. / (1. + std::exp(-std::max(-60., std::min(60., deficit / width)))); + } + float etaShape(double eta) + { + return std::exp(-0.5 * eta * eta / (simConfig.absSigmaEta * simConfig.absSigmaEta)); + } + float pion(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float survivalRatio = survivalAir(p, airCm * c) / survivalAir(p, simConfig.absRadius1 * c); + const float hadronRatio = std::exp(-(ironCm - (simConfig.absRadius2 - simConfig.absRadius1)) * c / simConfig.absLambdaPi); + // Ratio to reference range at the SAME eta; reference shape is phenomenological. + const float rangeRatio = rangeTurnOn(p, ironCm * c, 0.13957039) / rangeTurnOn(p, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.13957039); // assume pion mass + return std::max(0., refPi(p) * etaShape(eta) * survivalRatio * hadronRatio * rangeRatio); + } + float muon(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float d0 = decayAir(p, simConfig.absRadius1 * c); + const float decayRatio = d0 > 0. ? decayAir(p, airCm * c) / d0 : 0.; + // Effective secondary-muon momentum proxy: 0.8 * pion momentum. + // Secondary muons born in iron are NOT modeled; this is a phenomenological air-decay term. + const float pMu = 0.8 * p; + const float rangeRatio = rangeTurnOn(pMu, ironCm * c, 0.1056583755) / + rangeTurnOn(pMu, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.1056583755); + return std::max(0., refMu(p) * etaShape(eta) * decayRatio * rangeRatio); + } + float total(float p, float eta, float airCm, float ironCm) + { + return std::min(1., pion(p, eta, airCm, ironCm) + muon(p, eta, airCm, ironCm)); + } + float meanEta(float p, float airCm, float ironCm) + { + const int n = 1000; + float s = 0.; + for (int i = 0; i < n; ++i) { + float eta = -simConfig.absEtaMax + (i + .5) * 2. * simConfig.absEtaMax / n; + s += total(p, eta, airCm, ironCm); + } + return s / n; + } + + void process(soa::Join::iterator const& collision, + soa::Join const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + o2::dataformats::VertexBase pvVtx({collision.posX(), collision.posY(), collision.posZ()}, + {collision.covXX(), collision.covXY(), collision.covYY(), + collision.covXZ(), collision.covYZ(), collision.covZZ()}); + + std::array mcPvCov = {0.}; + o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, mcPvCov); + const float eventCollisionTimePS = (simConfig.considerEventTime.value ? collision.collisionTime() * 1e3 : 0.f); // convert ns to ps + if (collision.has_mcCollision()) { + auto mcCollision = collision.mcCollision(); + mcPvVtx.setX(mcCollision.posX()); + mcPvVtx.setY(mcCollision.posY()); + mcPvVtx.setZ(mcCollision.posZ()); + } // else remains untreated for now + + // First we compute the number of charged particles in the event if LUTs are loaded + float dNdEta = 0.f; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) + continue; + auto mcParticle = track.mcParticle(); + if (std::abs(mcParticle.eta()) > simConfig.multiplicityEtaRange) { + continue; + } + if (mcParticle.has_daughters()) { + continue; + } + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo) { + // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + continue; + } + if (pdgInfo->Charge() == 0) { + continue; + } + dNdEta += 1.f; + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h1dNdeta"), dNdEta); + } + + tracksWithTime.clear(); // clear the vector of tracks with time to prepare the cache for the next event + tracksWithTime.reserve(tracks.size()); + // First loop to generate the arrival time of the particles to the MID layers + for (const auto& track : tracks) { + // first step: find precise arrival time (if any) + // --- convert track into perfect track + if (!track.has_mcParticle()) { // should always be OK but check please + upgradeMidMC(-999.f, -999.f); + continue; + } else { + LOG(debug) << "Track without mcParticle found!"; + } + const auto& mcParticle = track.mcParticle(); + float true_pt = mcParticle.pt(); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); + // get mass to calculate velocity + auto pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (pdgInfo == nullptr) { + LOG(error) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + upgradeMidMC(-999.f, -999.f); + continue; + } + + float xPv = -100.f; + static constexpr float kTrkXThreshold = -99.f; // Threshold to consider a good propagation of the track + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { + xPv = o2track.getX(); + } + float trackLengthMID = -1; + float trackLengthMIDB2 = -1; + std::array posMid{-999., -999., -999.}; + std::array delaPosMid{-999., -999., -999.}; + float trackLengthInIron = -999.; + if (xPv > kTrkXThreshold) { + trackLengthMIDB2 = o2::upgrade::computeTrackLength(o2track, simConfig.magRadiusC, mMagneticField); + trackLengthMID = computeTrackLengthMID(o2track, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posMid, delaPosMid, trackLengthInIron); + // thetamolInIron = moliereThetaIron(o2track.getP(), pdgInfo->Mass(), trackLengthInIron); + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dTrackLengthB2vsB0"), trackLengthMIDB2, trackLengthMID); + } + + // Check if the track hit a sensitive area of the MID + const bool activeMID = isInMIDActiveArea(o2track); + if (!activeMID) { + trackLengthMID = -999.f; + } else { + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posMid[1], posMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMap"), posMid[2], simConfig.midRadius * phi_tmp); + } + } + + const float v = o2::upgrade::computeParticleVelocity(o2track.getP(), pdgInfo->Mass()); + const float expectedTimeMID = trackLengthMID > 0 ? trackLengthMID / v + eventCollisionTimePS : -999.f; // arrival time to the MID in ps + + upgradeMidMC(expectedTimeMID, trackLengthMID); + + // Smear with expected resolutions + const float measuredTimeMID = pRandomNumberGenerator.Gaus(expectedTimeMID, simConfig.midTimeReso); + + // Now we calculate the expected arrival time following certain mass hypotheses + // and the (imperfect!) reconstructed track parametrizations + float trackLengthRecoMID = -1; + auto recoTrack = getTrackParCov(track); + xPv = -100.f; + std::array posRecMid{-999., -999., -999.}; + std::array deltaPosRecMid{-999., -999., -999.}; + float trackLengthRecoInIron = -1.; + trackLengthRecoInIron = -1.; + trackLengthRecoMID = -1.; + if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { + xPv = recoTrack.getX(); + } + if (xPv > kTrkXThreshold) { + trackLengthRecoMID = computeTrackLengthMID(recoTrack, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posRecMid, deltaPosRecMid, trackLengthRecoInIron); + // mimic scattering in iron + std::array mom{-999., -999., -999.}; + recoTrack.getPxPyPzGlo(mom); + for (int jj = 0; jj < 3; ++jj) { + float posRectmp = 0.0f; + float molieretmp = 0.0f; + molieretmp = moliereThetaIron(mom[jj], pdgInfo->Mass(), deltaPosRecMid[jj]); + posRectmp = pRandomNumberGenerator.Gaus(posRecMid[jj], molieretmp); + posRecMid[jj] = posRectmp; + } + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posRecMid[1], posRecMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMapRec"), posRecMid[2], simConfig.midRadius * phi_tmp); + histos.fill(HIST("h2HitDeltaXY"), posRecMid[0] - posMid[0], posRecMid[1] - posMid[1]); + histos.fill(HIST("h1PtRes"), true_pt, (true_pt - recoTrack.getPt()) / true_pt); + } + } + // cache the track info needed for the event time calculation + // Reuse or emplace a new object in the vector + tracksWithTime.emplace_back(TracksWithTime{mcParticle.pdgCode(), + {measuredTimeMID, expectedTimeMID}, + {trackLengthRecoMID, trackLengthMID}, + {trackLengthRecoInIron, trackLengthInIron}, + {posRecMid, posMid}, + {recoTrack.getP(), recoTrack.getSigma1Pt2()}, + {recoTrack.getEta(), recoTrack.getSigmaTgl2()}, + o2track.getPt()}); + } + // Now we compute the event time for the tracks + + std::array tzero = {0.f, 0.f}; + bool etStatus = false; + if (simConfig.considerEventTime.value) { + etStatus = eventTime(tracksWithTime, tzero); + if (!etStatus) { + LOG(debug) << "Event time calculation failed with " << tracksWithTime.size() << " tracks with time and " << dNdEta << " charged particles"; + } + } + + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dEventTime"), tzero[0], eventCollisionTimePS); + histos.fill(HIST("h1dEventTimegen"), eventCollisionTimePS); + histos.fill(HIST("h1dEventTimerec"), tzero[0]); + histos.fill(HIST("h2dEventTimeres"), dNdEta, tzero[1]); + if (etStatus) { + histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); + } + static_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + } + + // Then we do a second loop to compute the measured quantities with the measured event time + int trackWithTimeIndex = 0; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { // should always be OK but check please + continue; + } + const auto& mcParticle = track.mcParticle(); + + const auto& trkWithTime = tracksWithTime[trackWithTimeIndex++]; + const float trackLengthRecoMID = trkWithTime.mTrackLengthMID.first; + const float trackLengthMID = trkWithTime.mTrackLengthMID.second; + const float measuredTimeMID = trkWithTime.mMIDTime.first; + const float expecTimeMID = trkWithTime.mMIDTime.second; + const float momentum = trkWithTime.mMomentum.first; + const float pseudorapidity = trkWithTime.mPseudorapidity.first; + const float noSmearingPt = trkWithTime.mNoSmearingPt; + std::array expectedPosMid = trkWithTime.mPosInMid.second; + std::array measuredPosMid = trkWithTime.mPosInMid.first; + float trackLengthInIronRec = trkWithTime.mTrackLengthInIron.first; + bool isMuCandidate = false; + if (abs(pseudorapidity) < simConfig.absEtaMax) { + if (trackLengthInIronRec > 0) { + float kineticEnergy = std::sqrt(pow(momentum, 2) + pow(0.1057, 2)) - 0.1057; + float energyloss = trackLengthInIronRec * simConfig.absDeDx * simConfig.absDensity / 1000.; + if (abs(trkWithTime.mPdgCode) == 13) { + if ((kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } else if (abs(trkWithTime.mPdgCode) == 11) { + isMuCandidate = false; + } else { // mostly hadrons, parametrization from simulations using pions, GEANT4+BDT + float eff_rndm = pRandomNumberGenerator.Uniform(0., 1.); + float eff_had = total(momentum, pseudorapidity, trackLengthRecoMID - trackLengthInIronRec, trackLengthInIronRec); + if (eff_rndm < eff_had && (kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } + } + } + auto truePdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + float rigidity = momentum; // fallback to momentum if charge unknown + + // Use MC truth charge for rigidity calculation + if (truePdgInfo) { + const float trueCharge = std::abs(truePdgInfo->Charge()) / 3.0f; + if (trueCharge > 0) { + rigidity = momentum / trueCharge; + } + } + + if (plotsConfig.doQAplots) { + // unit conversion: length in cm, time in ps + const float midBeta = (trackLengthRecoMID / measuredTimeMID) / o2::constants::physics::LightSpeedCm2PS; + if (trackLengthRecoMID > 0) { + histos.fill(HIST("h2dVelocityVsMomentumInner"), momentum, midBeta); + histos.fill(HIST("h2dVelocityVsRigidityInner"), rigidity, midBeta); + histos.fill(HIST("h2dTrackLengthInnerVsPt"), noSmearingPt, trackLengthMID); + histos.fill(HIST("h2dTrackLengthInnerRecoVsPt"), noSmearingPt, trackLengthRecoMID); + histos.fill(HIST("h2dEtaVsPt"), noSmearingPt, pseudorapidity); + histos.fill(HIST("h2dEtaVsP"), momentum, pseudorapidity); + } + } + std::vector measuredPosMidVec(measuredPosMid.begin(), measuredPosMid.end()); + upgradeMid(tzero[0], tzero[1], measuredPosMidVec, measuredTimeMID, trackLengthRecoMID, isMuCandidate); + + std::vector expectedPosMidVec(expectedPosMid.begin(), expectedPosMid.end()); + upgradeMidExpected(expectedPosMidVec, expecTimeMID, trackLengthMID); + } + + if (trackWithTimeIndex != tracks.size()) { + LOG(fatal) << "Track with time index " << trackWithTimeIndex << " does not match the number of tracks " << tracks.size(); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index 6e6fbc37ea0..e86859ec374 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -44,6 +44,11 @@ o2physics_add_dpl_workflow(alice3-prefilterdilepton PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(alice3-prefilterdimuon + SOURCES alice3-prefilterdimuon.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(alice3-taskcorrelationddbar SOURCES alice3-taskcorrelationDDbar.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/ALICE3/Tasks/alice3-prefilterdimuon.cxx b/ALICE3/Tasks/alice3-prefilterdimuon.cxx new file mode 100644 index 00000000000..c797296b4a6 --- /dev/null +++ b/ALICE3/Tasks/alice3-prefilterdimuon.cxx @@ -0,0 +1,418 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file alice3-dimuon.cxx +/// \author s.scheid@cern.ch, daiki.sekihata@cern.ch +/// + +#include "ALICE3/DataModel/OTFMID.h" +#include "ALICE3/DataModel/prefilterDilepton.h" +#include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct Alice3DimuonEventCentSelection { + + Service inspdg; + + Produces eventCentSel; + Configurable minFITPart{"minFITPart", 0.f, "Minimum number of charged particles in the FIT acceptance"}; + Configurable maxFITPart{"maxFITPart", 0.f, "Maximum number of charged particles in the FIT acceptance"}; + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + registry.add("Generated/Before/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/After/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/Before/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + registry.add("Generated/After/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + } + + using MyEvents = soa::Join; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + void processGen(MyEvents::iterator const& event, o2::aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (!event.has_mcCollision()) { + eventCentSel(0); + } else { + auto mccollision = event.mcCollision(); + auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); + int nParticlesFIT = 0; + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/Before/ParticlesEta"), mcParticle.eta()); + if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { + nParticlesFIT++; + } + } + } + } + } // end of mc particle loop + registry.fill(HIST("Generated/Before/ParticlesFit"), nParticlesFIT); + if (nParticlesFIT > minFITPart && nParticlesFIT < maxFITPart) { + registry.fill(HIST("Generated/After/ParticlesFit"), nParticlesFIT); + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/After/ParticlesEta"), mcParticle.eta()); + } + } + } + } // end of mc particle loop + eventCentSel(1); + } else { + eventCentSel(0); + } + } // if mc collision loop + } // end of process + + void processDummy(MyEvents::iterator const&) + { + eventCentSel(1); + } + + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processGen, "Select centrality", false); + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processDummy, "Dummy", true); +}; + +struct Alice3DimuonPrefilter { + + Produces pfb_derived; + + SliceCache cache_mc; + SliceCache cache_rec; + + Service inspdg; + + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable maxMass{"maxMass", 5.f, "Upper limit in mass"}; + Configurable maxOp{"maxOp", 5.f, "Upper limit in opening angle"}; + Configurable isMuMID{"isMuMID", true, "Muon candidate reaching MID"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + std::unordered_map map_pfb; // map track.globalIndex -> prefilter bit + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + using MyTracksWithSmearings = soa::Join; + using MyTracksWithSmearing = MyTracksWithSmearings::iterator; + using MyTracksMCs = soa::Join; + using MyTracksMC = MyTracksMCs::iterator; + using DileptonCollisions = soa::Join; + using DileptonCollision = DileptonCollisions::iterator; + + Preslice perCollision = aod::track::collisionId; + Partition posTracks = o2::aod::track::signed1Pt > 0.f; + Partition negTracks = o2::aod::track::signed1Pt < 0.f; + Partition posTracksWithSmearing = o2::aod::track::signed1Pt > 0.f; + Partition negTracksWithSmearing = o2::aod::track::signed1Pt < 0.f; + + void init(InitContext&) + { + const AxisSpec axisM{500, 0, 5, "#it{m}_{ll} (GeV/#it{c}^{2})"}; + // const AxisSpec axisPt{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPt{nBinsP, 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + // const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; + const AxisSpec axisTrackLengthMID{310, 0., 310., "Track length (cm)"}; + // const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; + const AxisSpec axisEta{static_cast(nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; + const AxisSpec axisProdx{2000, -100, 100, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{2000, -100, 100, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{2000, -100, 100, "Prod. Vertex Z (cm)"}; + + registry.add("Reconstructed/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("ReconstructedFiltered/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta_P", "Track Eta vs Track P", kTH2F, {axisPt, axisEta}); + registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + + if (doprocessPreFilter) { + // registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/midTrackLength", "Track length MID", kTH1F, {axisTrackLengthMID}); + } + } + + template + void PreFilter(TTracks const& posTracks_coll, TTracks const& negTracks_coll) + { + for (const auto& pos : posTracks_coll) { + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), pos.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.momentumSmeared(),pos.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.ptSmeared(), pos.etaSmeared()); + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (pos.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), pos.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), pos.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.p(), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.pt(), pos.eta()); + } + } + } + for (const auto& ele : negTracks_coll) { + if constexpr (isWithSmearing) { + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), ele.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.pSmeared(),ele.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.ptSmeared(), ele.etaSmeared()); + } else { + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + if (ele.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), ele.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), ele.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.p(), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.pt(), ele.eta()); + } + } + } + + for (const auto& [pos, ele] : combinations(CombinationsFullIndexPolicy(posTracks_coll, negTracks_coll))) { // ULS + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + } + if constexpr (isWithSmearing) { + + ROOT::Math::PtEtaPhiMVector v1(pos.ptSmeared(), pos.etaSmeared(), pos.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.ptSmeared(), ele.etaSmeared(), ele.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + + } else { + + if (pos.midCandidate()) { + if (ele.midCandidate()) { + + ROOT::Math::PtEtaPhiMVector v1(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.pt(), ele.eta(), ele.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + } + } + } + } // combination + } + + void processPreFilter(DileptonCollisions const& collisions, MyTracksMCs const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processPreFilterWithSmearing(DileptonCollisions const& collisions, MyTracksWithSmearings const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processDummy(MyTracksMCs const& tracks) + { + for (int i = 0; i < tracks.size(); i++) { + pfb_derived(0); + } + } + + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilter, "Run prefilter", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilterWithSmearing, "Run prefilter with smearing", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processDummy, "Dummy", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} From 26e73dd3311ae75d26c9bdf759f3211ed2cecb79 Mon Sep 17 00:00:00 2001 From: Preet-Bhanjan Date: Fri, 16 Oct 2026 13:49:31 +0200 Subject: [PATCH 308/648] =?UTF-8?q?[PWGCF]=20Addition=20of=20DihadronConta?= =?UTF-8?q?iner=20to=20CorrReso.=20Minor=20code=20structuring=20in=20?= =?UTF-8?q?=E2=80=A6=20(#18259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Preet Pati Co-authored-by: Preet Pati --- .../Tasks/CMakeLists.txt | 4 +- .../Tasks/corrReso.cxx | 1596 +++++++------ .../Tasks/pidDiHadron.cxx | 2050 ++++++++++------- .../Tasks/pidLongRange.cxx | 127 +- 4 files changed, 2149 insertions(+), 1628 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index b6fc8b9eaa5..8c283cecc40 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -45,7 +45,7 @@ o2physics_add_dpl_workflow(dpt-dpt-per-run-extra-qc o2physics_add_dpl_workflow(corr-reso SOURCES corrReso.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(neutron-proton-corr-zdc @@ -60,7 +60,7 @@ o2physics_add_dpl_workflow(di-hadron-cor o2physics_add_dpl_workflow(pid-di-hadron SOURCES pidDiHadron.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(longrange-correlation diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx index 1f75e51f647..f3185668771 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx @@ -13,7 +13,7 @@ /// \brief Ultra long range correlation using forward FIT detectors and TPC, with focus on resonances /// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch) -#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -87,121 +88,131 @@ struct CorrReso { o2::aod::ITSResponse itsResponse; Service ccdb{}; - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, true, "Enable QA histograms for event selection") - O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") - O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") - Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; - O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z")} cfgTrackCuts; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy")} cfgEventSelection; + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object")} cfgEffWeights; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; - O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; std::vector multT0CCutPars; std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") - O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> cfgResoCuts{"cfgResoCuts", {LongArrayFloat.front().data(), 13, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMinVal", "DCABetDaug"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> cfgResoSwitches{"cfgResoSwitches", {LongArrayInt.front().data(), 6, 3, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; + O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "4 = kshort, 5 = lambda, 0 for no PID") + Configurable> cfgResoCuts{"cfgResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgResoSwitches{"cfgResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; } cfgPIDConfigs; - Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; - Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; - Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; - Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; - SliceCache cache; + // Axes for general QA ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; - ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; - - ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; - ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for efficiency histograms"}; - ConfigurableAxis axisAmplitudeFt0a{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; - Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; - Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; - ConfigurableAxis cfgaxisFITamp{"cfgaxisFITamp", {1000, 0, 5000}, ""}; - AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; - AxisSpec axisChID = {220, 0, 220}; - - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgZVtxCut); - Filter trackFilter = (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaCut) && (cfgTrackCuts.cfgPtCutMin < aod::track::pt) && (cfgTrackCuts.cfgPtCutMax > aod::track::pt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; @@ -220,21 +231,22 @@ struct CorrReso { bool correctionsLoaded = false; // Define the outputs - OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; - OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; - OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; - OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; - HistogramRegistry registry{"registry"}; + HistogramRegistry histos{"histos"}; // For Labelled array value containers - std::array, 13> resoCutVals{}; - std::array, 6> resoSwitchVals{}; + std::array, 10> trackCuts{}; std::array, 14> eventCuts{}; std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum EventType { SameEvent = 1, @@ -242,101 +254,142 @@ struct CorrReso { }; enum FITIndex { - kFT0A = 0, - kFT0C = 1 + IndexFT0A = 0, + IndexFT0C = 1 }; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow - }; - enum ResoArrayIndex { - iK0 = 0, - iLambda = 1, - iPhi = 2, - NResoParticles = 3 - }; - enum ResoParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity, - kArmPodMinVal, - kDCABetDaug, - kNParticleCuts - }; - enum ResoParticleSwitches { - kUseCosPA = 0, - kMassBins, - kUseDCABetDaug, - kUseProperLifetime, - kUseV0Radius, - kUseArmPodCut, - kNParticleSwitches + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTOF, + UseITS }; enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseGoodITSLayer0123, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + enum ResoArrayIndex { + IndexK0 = 0, + IndexLambda = 1, + IndexPhi = 2, + NumResoParticles = 3 + }; + enum ResoParticleCuts { + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts + }; + enum ResoParticleSwitches { + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches + }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, // Count the total number of enum - kCount_CentEstimators + NumCentEstimators }; RCTFlagsChecker rctChecker{"CBT"}; void init(InitContext&) { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together + // ---------------------------------------------------------------------------------------------------------------------- + ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -344,249 +397,286 @@ struct CorrReso { LOGF(info, "Starting init"); - if (cfgPIDConfigs.cfgPIDParticle < kK0) { + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgResoSwitches->getData(), resoSwitchVals); + + if (cfgPIDConfigs.cfgPIDParticle < UseK0) { LOGF(fatal, "The input number does not correspond to any particle"); return; } - readMatrix(cfgPIDConfigs.cfgResoCuts->getData(), resoCutVals); - readMatrix(cfgPIDConfigs.cfgResoSwitches->getData(), resoSwitchVals); - readMatrix(cfgUseEventCuts->getData(), eventCuts); - readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); - // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi AxisSpec axisInvMass = {10, 0, 1, "mass"}; - if (cfgPIDConfigs.cfgPIDParticle == kK0) { - const auto bins = resoSwitchVals[kMassBins][iK0]; - const auto min = resoCutVals[kMassMin][iK0]; - const auto max = resoCutVals[kMassMax][iK0]; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { - const auto bins = resoSwitchVals[kMassBins][iLambda]; - const auto min = resoCutVals[kMassMin][iLambda]; - const auto max = resoCutVals[kMassMax][iLambda]; + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; } - if (cfgPIDConfigs.cfgPIDParticle == kPhi) { - const auto bins = resoSwitchVals[kMassBins][iPhi]; - const auto min = resoCutVals[kMassMin][iPhi]; - const auto max = resoCutVals[kMassMax][iPhi]; - - axisInvMass = {bins, min, max, "M_{K^{+}K^{-}} (GeV/c^{2})"}; - } - - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - if (cfgPIDConfigs.cfgPIDParticle == kK0) { // For K0 - registry.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); - registry.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); - - registry.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{11, 0, 11}}}); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(1, "K0 candidates"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(7, "V0radius"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(8, "CosPA"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(10, "ArmenterosPod"); - registry.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(11, "Daughter track selection"); - } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { // For Lambda - registry.add("PrPlusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiMinusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PrPlusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiMinusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - - registry.add("PrMinusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PiPlusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); - registry.add("PrMinusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - registry.add("PiPlusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); - - registry.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); - registry.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); - - registry.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0, 10}}}); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(1, "Lambda candidates"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(7, "V0radius"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(8, "CosPA"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - registry.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(10, "Daughter track selection"); - } - } - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - registry.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); - registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); - registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); - registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{14, -0.5, 13.5}}}); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); - } - - if ((doprocessSameTpcFt0a || doprocessSameTpcFt0c) && cfgQaCheck) { - registry.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hK0Rap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("PrMinusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrMinusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiPlusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hLambdaRap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + + if (cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); } // Multiplicity correlation cuts - if (eventCuts[kUseMultCorrCut][kEvCut1] != 0) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); } - if (eventCuts[kUseT0AV0ACut][kEvCut1] != 0) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c) { - registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - registry.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); - registry.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); - registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); - registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - - registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); - registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisFit}}); - } // end of single particle distribution histograms + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } - if (cfgQaCheck) { - registry.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + if (doprocessSameTpcFt0) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); - if (doprocessSameTpcFt0a) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); - registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisInvMass}}}); + if (cfgGeneral.cfgStrictTrackCounter) { + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + } + if (!cfgEvSel.cfgSelCollByNch) { + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); + } + + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } } - if (doprocessSameTpcFt0c) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); - registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisInvMass}}}); + + if (doprocessMixedTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + } } - registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event LOGF(info, "Initializing correlation container"); - // Initialize Nch-related histograms and containers - - std::vector corrAxisTpcFt0a = {{axisSample, "Sample"}, + // Initialize containers + std::vector corrAxisTpcFt0a = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; - std::vector corrAxisTpcFt0c = {{axisSample, "Sample"}, + std::vector corrAxisTpcFt0c = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - std::vector userAxis; - - if (doprocessSameTpcFt0a) { - sameTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); - mixedTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); } - if (doprocessSameTpcFt0c) { - sameTpcFt0c.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - mixedTpcFt0c.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); } - if (!cfgEvSelRCTflags.value.empty()) { - rctChecker.init(cfgEvSelRCTflags.value); // override initialzation + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation } LOGF(info, "End of init"); } template - float getCentrality(TCollision const& collision) + double getCentrality(TCollision const& collision) { - float cent = 0.0; - switch (cfgCentEstimator) { - case kCentFT0C: + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -598,195 +688,217 @@ struct CorrReso { template bool eventRct(TCollision const& collision, const bool fillCounter) { + if (cfgEvSel.cfgEvSelRCTflags.value.empty()) { + return true; + } if (!rctChecker(collision)) { if (fillCounter) { - registry.fill(HIST("hEventCountRct"), 0.5); + histos.fill(HIST("hEventCountRct"), 0.5); } - return 0; + return false; } if (fillCounter) { - registry.fill(HIST("hEventCountRct"), 1.5); + histos.fill(HIST("hEventCountRct"), 1.5); } - return 1; + return true; } template - bool eventSelected(TCollision const& collision, const int mult, const bool fillCounter) + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { - if (eventCuts[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); } - if (fillCounter && eventCuts[kUseNoTimeFrameBorder][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); + if (!collision.sel8()) { + return false; } - if (eventCuts[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && eventCuts[kUseNoITSROFrameBorder][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (eventCuts[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoSameBunchPileup][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoSameBunchPileup); + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; } - if (fillCounter && eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); } - if (eventCuts[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; } - if (fillCounter && eventCuts[kUseGoodITSLayersAll][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodITSLayersAll); + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); } - if (eventCuts[kUseGoodITSLayer0123][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - return 0; + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; } - if (fillCounter && eventCuts[kUseGoodITSLayer0123][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseGoodITSLayer0123); + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (eventCuts[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInRofStandard][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoCollInRofStandard); + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); } - if (eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } + auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; } - if (fillCounter && eventCuts[kUseOccupancy][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseOccupancy); + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); } - if (eventCuts[kUseMultCorrCut][kEvCut1]) { - float cent = getCentrality(collision); + + if (eventCuts[UseMultCorrCut][EvCut1]) { if (cfgFuncParas.cfgMultPVT0CCutEnabled) { if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { - return 0; + return false; } if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultT0CCutEnabled) { if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { - return 0; + return false; } if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { - return 0; + return false; } if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { - return 0; + return false; } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { - return 0; + return false; } if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { - return 0; + return false; } } } - if (fillCounter && eventCuts[kUseMultCorrCut][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); } // V0A T0A 5 sigma cut - if (eventCuts[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { - return 0; + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; } - if (fillCounter && eventCuts[kUseT0AV0ACut][kEvCut1]) { - registry.fill(HIST("hEventCount"), kUseT0AV0ACut); + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); } - return 1; + return true; } template void eventSelectedIndividually(TCollision const& collision) { - registry.fill(HIST("hPassedEventSelection"), kFilteredEvents); + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); if (collision.sel8()) { - registry.fill(HIST("hPassedEventSelection"), kAfterSel8); + histos.fill(HIST("hPassedEventSelection"), AfterSel8); } if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoTimeFrameBorder); + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoITSROFrameBorder); + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoSameBunchPileup); + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); } if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodZvtxFT0vsPV); + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); } if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInTimeRangeStandard); + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayersAll); + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayer0123); + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); } if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInRofStandard); + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); } if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoHighMultCollInPrevRof); + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); } auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy > cfgEventSelection.cfgCutOccupancyLow && occupancy < cfgEventSelection.cfgCutOccupancyHigh)) { - registry.fill(HIST("hPassedEventSelection"), kUseOccupancy); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); } } @@ -832,7 +944,39 @@ struct CorrReso { template bool trackSelected(TTrack const& track) { - return ((track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgTrackCuts.cfgCutITSclu) && (track.tpcChi2NCl() < cfgTrackCuts.cfgCutChi2prTPCcls) && (std::abs(track.dcaZ()) < cfgTrackCuts.cfgCutDCAz)); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } void loadGain(aod::BCsWithTimestamps::iterator const& bc) @@ -842,12 +986,12 @@ struct CorrReso { auto timestamp = bc.timestamp(); constexpr int ChannelsFT0 = 208; - if (cfgCorrLevel == 0) { + if (cfgGeneral.cfgCorrLevel == 0) { for (auto i{0u}; i < ChannelsFT0; i++) { cstFT0RelGain.push_back(1.); } } else { - fullPath = cfgGainEqPath; + fullPath = cfgGeneral.cfgGainEqPath; fullPath += "/FT0"; const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); if (!objft0Gain) { @@ -861,29 +1005,17 @@ struct CorrReso { } template - void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType) { - if (fitType == kFT0C) { + if (fitType == IndexFT0C) { id = ft0.channelC()[iCh]; id = id + Ft0IndexA; ampl = ft0.amplitudeC()[iCh]; - if (system == SameEvent) { - registry.fill(HIST("FT0Amp"), id, ampl); - } ampl = ampl / cstFT0RelGain[id]; - if (system == SameEvent) { - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } - } else if (fitType == kFT0A) { + } else if (fitType == IndexFT0A) { id = ft0.channelA()[iCh]; ampl = ft0.amplitudeA()[iCh]; - if (system == SameEvent) { - registry.fill(HIST("FT0Amp"), id, ampl); - } ampl = ampl / cstFT0RelGain[id]; - if (system == SameEvent) { - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } } else { LOGF(fatal, "Cor Index %d out of range", fitType); } @@ -899,18 +1031,26 @@ struct CorrReso { int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - int kIndexDetector = cfgPIDConfigs.cfgUseItsPID ? kITS : kTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS bool isPion = false; bool isKaon = false; bool isProton = false; - bool isDetectedPion = nSigmaToUse[iPionUp] < nSigmaVals[iPionUp][kIndexDetector] && nSigmaToUse[iPionUp] > nSigmaVals[iPionLow][kIndexDetector]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < nSigmaVals[iKaonUp][kIndexDetector] && nSigmaToUse[iKaonUp] > nSigmaVals[iKaonLow][kIndexDetector]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < nSigmaVals[iProtonUp][kIndexDetector] && nSigmaToUse[iProtonUp] > nSigmaVals[iProtonLow][kIndexDetector]; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; - bool isTofPion = nSigmaTOF[iPionUp] < nSigmaVals[iPionUp][kTOF] && nSigmaTOF[iPionUp] > nSigmaVals[iPionLow][kTOF]; - bool isTofKaon = nSigmaTOF[iKaonUp] < nSigmaVals[iKaonUp][kTOF] && nSigmaTOF[iKaonUp] > nSigmaVals[iKaonLow][kTOF]; - bool isTofProton = nSigmaTOF[iProtonUp] < nSigmaVals[iProtonUp][kTOF] && nSigmaTOF[iProtonUp] > nSigmaVals[iProtonLow][kTOF]; + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { return -1; @@ -930,11 +1070,11 @@ struct CorrReso { } if (isPion) { - pid = kPions; + pid = UsePions; } else if (isKaon) { - pid = kKaons; + pid = UseKaons; } else if (isProton) { - pid = kProtons; + pid = UseProtons; } else { return -1; // no particle satisfies the criteria } @@ -945,30 +1085,27 @@ struct CorrReso { template bool selectionV0Daughter(TTrack const& track, int pid) { - if (!(track.itsNCls() >= cfgTrackCuts.cfgCutITSclu)) { + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { return false; } if (!track.hasTPC()) { return false; } - if (!(track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu)) { - return false; - } - if (!(track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows)) { + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { return false; } - if (!(std::abs(track.dcaZ()) < cfgTrackCuts.cfgCutDCAz)) { + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { return false; } if (cfgPIDConfigs.cfgUseOnlyTPC) { - if (pid == kPions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgTpcCut) { return false; } - if (pid == kKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgTpcCut) { return false; } - if (pid == kProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgTpcCut) { + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgTpcCut) { return false; } } else { @@ -987,38 +1124,40 @@ struct CorrReso { if (correctionsLoaded) { return; } - if (!cfgEfficiency.value.empty()) { - if (cfgLocalEfficiency) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } - if (!cfgEfficiencyNch.value.empty()) { - if (cfgLocalEfficiencyNch) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyNch.value.c_str(), "READ"); + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - mEfficiencyNch = ccdb->getForTimeStamp(cfgEfficiencyNch, timestamp); + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); } if (!mEfficiencyNch) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyNch.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); } - if (!cfgCentralityWeight.value.empty()) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } @@ -1027,10 +1166,8 @@ struct CorrReso { { float effNch = 1.; if (mEfficiencyNch) { - int ptBin = mEfficiencyNch->FindBin(pt); effNch = mEfficiencyNch->GetBinContent(ptBin); - } else { effNch = 1.0; } @@ -1045,12 +1182,10 @@ struct CorrReso { { float eff = 1.; if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency int ptBin = mEfficiency->GetYaxis()->FindBin(pt); int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); - } else { eff = 1.0; } @@ -1061,17 +1196,34 @@ struct CorrReso { return true; } + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + template - void trackCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram { double nTracksCorrected = 0; float weightNch = 1.0f; for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { continue; } - nTracksCorrected += weightNch; } multiplicity = nTracksCorrected; @@ -1094,16 +1246,16 @@ struct CorrReso { continue; } - registry.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - registry.fill(HIST("Eta"), track1.eta()); - registry.fill(HIST("EtaCorrected"), track1.eta(), weff1); - registry.fill(HIST("pTFiner"), track1.pt()); - registry.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta"), track1.eta()); + histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); + histos.fill(HIST("pTFiner"), track1.pt()); + histos.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); } } template - bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) { double mk0 = candidate.mK0Short(); @@ -1111,68 +1263,103 @@ struct CorrReso { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - registry.fill(HIST("hK0Count"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iK0] || negtrack.pt() < resoCutVals[kNegTrackPt][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 1.5); - if (mk0 < resoCutVals[kMassMin][iK0] || mk0 > resoCutVals[kMassMax][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 2.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillMassCut); + } // Rapidity correction - if (std::abs(candidate.yK0Short()) > resoCutVals[kRapidity][iK0]) { + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 3.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } // DCA cuts for K0short - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iK0]) { + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 4.5); - if (resoSwitchVals[kUseDCABetDaug][iK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[kDCABetDaug][iK0]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 5.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iK0] && (candidate.v0radius() < resoCutVals[kRadiusMin][iK0] || candidate.v0radius() > resoCutVals[kRadiusMax][iK0])) { + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { return false; } - registry.fill(HIST("hK0Count"), 6.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } // cosine pointing angle cuts - if (resoSwitchVals[kUseCosPA][iK0] && candidate.v0cosPA() < resoCutVals[kCosPA][iK0]) { + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 7.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCosPA); + } // Proper lifetime float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; - if (resoSwitchVals[kUseProperLifetime][iK0] && cTauK0 > resoCutVals[kLifeTime][iK0]) { + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 8.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } // ArmenterosPodolanskiCut - if (resoSwitchVals[kUseArmPodCut][iK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[kArmPodMinVal][iK0]) { + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { return false; } - registry.fill(HIST("hK0Count"), 9.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + // Selection on V0 daughters - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kPions)) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { return false; } - registry.fill(HIST("hK0Count"), 10.5); - - registry.fill(HIST("hK0Phi"), candidate.phi()); - registry.fill(HIST("hK0Eta"), candidate.eta()); - registry.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); - registry.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); - registry.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); - registry.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } return true; } template - bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) { bool isL = false; // Is lambda candidate bool isAL = false; // Is anti-lambda candidate @@ -1184,98 +1371,135 @@ struct CorrReso { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - registry.fill(HIST("hLambdaCount"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iLambda] || negtrack.pt() < resoCutVals[kNegTrackPt][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 1.5); - if (mlambda > resoCutVals[kMassMin][iLambda] && mlambda < resoCutVals[kMassMax][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { isL = true; } - if (mantilambda > resoCutVals[kMassMin][iLambda] && mantilambda < resoCutVals[kMassMax][iLambda]) { + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { isAL = true; } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 2.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } // Rapidity correction - if (std::abs(candidate.yLambda()) > resoCutVals[kRapidity][iLambda]) { + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 3.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } // DCA cuts for lambda and antilambda - if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iLambda])) { + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { isL = false; } - if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[kDCANegToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCAPosToPVMin][iLambda])) { + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { isAL = false; } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 4.5); - if (resoSwitchVals[kUseDCABetDaug][iLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[kDCABetDaug][iLambda]) { + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 5.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iLambda] && (candidate.v0radius() < resoCutVals[kRadiusMin][iLambda] || candidate.v0radius() > resoCutVals[kRadiusMax][iLambda])) { + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { return false; } - registry.fill(HIST("hLambdaCount"), 6.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (resoSwitchVals[kUseCosPA][iLambda] && candidate.v0cosPA() < resoCutVals[kCosPA][iLambda]) { + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 7.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } // Proper lifetime float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; - if (resoSwitchVals[kUseProperLifetime][iLambda] && cTauLambda > resoCutVals[kLifeTime][iLambda]) { + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { return false; } - registry.fill(HIST("hLambdaCount"), 8.5); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); + } + + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } if (isL) { - if (!selectionV0Daughter(postrack, kProtons) || !selectionV0Daughter(negtrack, kPions)) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { isL = false; } } if (isAL) { - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kProtons)) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { isAL = false; } } if (!isL && !isAL) { return false; } - registry.fill(HIST("hLambdaCount"), 9.5); - - registry.fill(HIST("hLambdaPhi"), candidate.phi()); - registry.fill(HIST("hLambdaEta"), candidate.eta()); - if (isL) { - registry.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); - registry.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); - registry.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); - registry.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); } - if (isAL) { - registry.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); - registry.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); - registry.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); - registry.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + if (isL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); } return true; } - template - void fillCorrelationsTPCFT0(const TV0Tracks& tracks1, TFT0s const& ft0, float posZ, float posY, float posX, int system, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelationsTPCFT0(const TV0Tracks& tracks1, TFT0s const& ft0, float posZ, float posY, float posX, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; // loop over all tracks @@ -1283,16 +1507,16 @@ struct CorrReso { double resoMass = -1; // 4 = kshort, 5 = lambda, 6 = phi - if (cfgPIDConfigs.cfgPIDParticle == kK0) { - if (!isSelectedK0(track1, posZ, posY, posX)) { + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(track1, posZ, posY, posX, system, corType)) { continue; // Reject if called for K0 but V0 is not K0 } resoMass = track1.mK0Short(); } - if (cfgPIDConfigs.cfgPIDParticle == kLambda) { - if (!isSelectedLambda(track1, posZ, posY, posX)) { + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(track1, posZ, posY, posX, system, corType)) { continue; // Reject if called for Lambda but V0 is not lambda } @@ -1300,17 +1524,17 @@ struct CorrReso { } if (system == SameEvent) { - if (corType == kFT0C) { - registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); - } else if (corType == kFT0A) { - registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); } } std::size_t channelSize = 0; - if (corType == kFT0C) { + if (corType == IndexFT0C) { channelSize = ft0.channelC().size(); - } else if (corType == kFT0A) { + } else if (corType == IndexFT0A) { channelSize = ft0.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", corType); @@ -1318,7 +1542,7 @@ struct CorrReso { for (std::size_t iCh = 0; iCh < channelSize; iCh++) { int chanelid = 0; float ampl = 0.; - getChannel(ft0, iCh, chanelid, ampl, corType, system); + getChannel(ft0, iCh, chanelid, ampl, corType); auto phi = getPhiFT0(chanelid, corType); auto eta = getEtaFT0(chanelid, corType); @@ -1327,32 +1551,24 @@ struct CorrReso { float deltaEta = track1.eta() - eta; // fill the right sparse and histograms if (system == SameEvent) { - if (corType == kFT0A) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (system == MixedEvent) { - if (corType == kFT0A) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { - if (cfgQaCheck) { - registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } - mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } } @@ -1361,8 +1577,8 @@ struct CorrReso { bool isGoodRun(int runNumber) { - for (const auto& ExcludedRun : cfgRunRemoveList.value) { - if (runNumber == ExcludedRun) { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { return false; } } @@ -1370,24 +1586,15 @@ struct CorrReso { return true; } - double massKaPlus = o2::constants::physics::MassKPlus; // same as MassKMinus double massLambda = o2::constants::physics::MassLambda; // same as MassLambda0 and MassLambda0Bar double massK0Short = o2::constants::physics::MassK0Short; // same as o2::constants::physics::MassK0 and o2::constants::physics::MassK0Bar - void processSameTpcFt0a(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) { - registry.fill(HIST("hEventCount"), kFilteredEvents); - - if (cfgQaCheck) { + if (cfgGeneral.cfgQABasic) { eventSelectedIndividually(collision); } - if (!collision.sel8()) { - return; - } - - registry.fill(HIST("hEventCount"), kAfterSel8); - if (!eventRct(collision, true)) { return; } @@ -1395,12 +1602,14 @@ struct CorrReso { auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { return; } @@ -1408,41 +1617,56 @@ struct CorrReso { return; } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; - - registry.fill(HIST("zVtx"), collision.posZ()); + float weightCent = 1.0f; - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - fillYield(collision, tracks); + histos.fill(HIST("Centrality"), cent); + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + } - double multiplicity = static_cast(tracks.size()); + histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgQaCheck) { - registry.fill(HIST("Nch"), multiplicity); + if (cfgGeneral.cfgQABasic) { + fillYield(collision, tracks); } - if (cfgStrictTrackCounter) { - trackCounter(tracks, multiplicity); - } + auto multiplicity = static_cast(tracks.size()); + histos.fill(HIST("Nch"), multiplicity); - if (cfgQaCheck) { - registry.fill(HIST("Nch_corrected"), multiplicity); + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + histos.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { return; } + histos.fill(HIST("eventcount"), SameEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + const auto& ft0 = collision.foundFT0(); - fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, kFT0A, eventWeight); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0C, weightCent); + } } - PROCESS_SWITCH(CorrReso, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", false); + PROCESS_SWITCH(CorrReso, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); - void processMixedTpcFt0a(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) { auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); @@ -1452,14 +1676,14 @@ struct CorrReso { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) { + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) continue; } @@ -1469,10 +1693,13 @@ struct CorrReso { auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { continue; } - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { continue; } @@ -1480,11 +1707,9 @@ struct CorrReso { continue; } - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; } @@ -1492,154 +1717,55 @@ struct CorrReso { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; - double multiplicity = static_cast(tracks1.size()); + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (cfgStrictTrackCounter) { - trackCounter(tracks1, multiplicity); + if (cfgEvSel.cfgSelCollByNch && cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } - - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, kFT0A, eventWeight); - } - } - PROCESS_SWITCH(CorrReso, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", false); - - void processSameTpcFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) - { - registry.fill(HIST("hEventCount"), kFilteredEvents); - - if (cfgQaCheck) { - eventSelectedIndividually(collision); - } - - if (!collision.sel8()) { - return; - } - - registry.fill(HIST("hEventCount"), kAfterSel8); - - if (!eventRct(collision, true)) { - return; - } - - auto bc = collision.bc_as(); - int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number - return; - } - } - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { - return; - } - if (!collision.has_foundFT0()) { - return; - } - loadAlignParam(bc.timestamp()); - loadGain(bc); - loadCorrection(bc.timestamp()); - - registry.fill(HIST("zVtx"), collision.posZ()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - const auto& ft0 = collision.foundFT0(); - - double multiplicity = static_cast(tracks.size()); - - if (cfgQaCheck) { - registry.fill(HIST("Nch"), multiplicity); - } - - if (cfgStrictTrackCounter) { - trackCounter(tracks, multiplicity); - } - - if (cfgQaCheck) { - registry.fill(HIST("Nch_corrected"), multiplicity); - } - - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { - return; - } - - fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, kFT0C, 1.0f); - } - PROCESS_SWITCH(CorrReso, processSameTpcFt0c, "Process same event for TPC-FT0C correlation", false); - - void processMixedTpcFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) - { - - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; - - auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto it = pairs.begin(); it != pairs.end(); it++) { - auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) { + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } - - if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - - auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { - continue; - } + histos.fill(HIST("eventcount"), MixedEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { - continue; - } + float eventWeight = 1.0f; - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - int currentRunNumber = bc.runNumber(); - if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number - continue; + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent1)) { + continue; // or continue in a loop } } - loadAlignParam(bc.timestamp()); - loadCorrection(bc.timestamp()); - float eventWeight = 1.0f; const auto& ft0 = collision2.foundFT0(); - double multiplicity = static_cast(tracks1.size()); - - if (cfgStrictTrackCounter) { - trackCounter(tracks1, multiplicity); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); } - - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { - continue; + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); } - - fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, kFT0C, eventWeight); } } - PROCESS_SWITCH(CorrReso, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); + PROCESS_SWITCH(CorrReso, processMixedTpcFt0, "Process mixed events for TPC-FT0 correlations", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx index af48e2069a7..5c4102c666d 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx @@ -14,11 +14,10 @@ /// \author Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch), Zhiyong Lu (zhiyong.lu@cern.ch) /// \since July/29/2025 -#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -51,14 +50,12 @@ #include #include #include -#include - -#include #include #include #include #include +#include #include #include @@ -68,13 +65,13 @@ using namespace o2::framework::expressions; using namespace constants::math; // define the filtered collisions and tracks -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) template -auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int colStart, int colEnd) +auto readMatrix(Array2D const& mat, P& array) { - for (auto i = rowStart; i < rowEnd; ++i) { - for (auto j = colStart; j < colEnd; ++j) { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { array[i][j] = mat(i, j); } } @@ -82,57 +79,79 @@ auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int c return; } -static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; -static constexpr int LongArrayInt[3][20] = {{1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {3, 3, 3, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}}; +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; struct PidDiHadron { o2::aod::ITSResponse itsResponse; - Service ccdb; - - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 3.0f, "Maximal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCluster, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCrossRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") - O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgITScluster, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Minimum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Maximum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgFakeKaonCut, float, 0.1f, "Maximum difference in measured momentum and TPC inner ring momentum of particle") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") - O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") - O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") - O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgVerbosity, bool, false, "Verbose output") - O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") - O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgUseAntiLambda, bool, true, "Use AntiLambda candidates for analysis") - O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMin, float, 0.2f, "Minimal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMax, float, 3.0f, "Maximal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgUseCutMerging, bool, false, "use two track cut"); + O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + O2_DEFINE_CONFIGURABLE(cfgUseTrigWeight, bool, false, "enable trigger efficiency correction") + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Maximum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Minimum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") + O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgPIDUseIts, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + O2_DEFINE_CONFIGURABLE(cfgPIDOnlyTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseOnlyTpc, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 0 for no PID") + O2_DEFINE_CONFIGURABLE(cfgPIDUseReso, bool, false, "Switch to turn on resonance analysis") + Configurable> cfgPIDnSigmas{"cfgPIDnSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> cfgPIDResoCuts{"cfgPIDResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgPIDResoSwitches{"cfgPIDResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgPIDConfigs; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -149,208 +168,224 @@ struct PidDiHadron { O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); - O2_DEFINE_CONFIGURABLE(cfgDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"); std::vector multT0CCutPars; std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; - TF1* fPtDepDCAxy = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; SliceCache cache; + // Axes for general QA ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity axis for histograms"}; - ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {100, 0, 1000}, "multiplicity axis for histograms"}; + ConfigurableAxis axisCentrality{"axisCentrality", {100, 0, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisPt{"axisPt", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -2.4, 2.4}, "delta eta axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + + // Axes for PID QA ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt[0], 15, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut", "kIsVertexITSTPC", "kTVXinTRD"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; - Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> resonanceCuts{"resonanceCuts", {LongArrayFloat[0], 12, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMinVal"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> resonanceSwitches{"resonanceSwitches", {LongArrayInt[0], 6, 3, {"UseCosPA", "NMassBins", "DCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {1, 0, 1}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, 0, 1}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0, 1}, "pt axis for efficiency histograms"}; - // make the filters and cuts. - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; using FilteredMcTracks = soa::Filtered>; using V0TrackCandidate = aod::V0Datas; + Partition asscTracks = (aod::track::pt > cfgGeneral.cfgAsscPtMin) && (aod::track::pt < cfgGeneral.cfgAsscPtMax); + Preslice perCollision = aod::track::collisionId; Preslice perCollisionV0 = aod::v0::collisionId; // Corrections TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; TH1D* mCentralityWeight = nullptr; bool correctionsLoaded = false; // Define the outputs - OutputObj same{"sameEvent"}; - OutputObj mixed{"mixedEvent"}; - OutputObj sameReso{"sameEventReso"}; - OutputObj mixedReso{"mixedEventReso"}; + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + OutputObj sameReso{"sameEventReso"}; + OutputObj mixedReso{"mixedEventReso"}; HistogramRegistry histos{"histos"}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum ResoArrayIndex { - iK0 = 0, - iLambda = 1, - iPhi = 2, - NResoParticles = 3 + IndexK0 = 0, + IndexLambda, + NumResoParticles }; enum ResoParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity, - kArmPodMinVal, - kNParticleCuts + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts }; enum ResoParticleSwitches { - kUseCosPA = 0, - kMassBins, - kDCABetDaug, - kUseProperLifetime, - kUseV0Radius, - kUseArmPodCut, - kNParticleSwitches + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, + }; + enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kUseVertexITSTPC, - kUseTVXinTRD, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + NumEventCuts + }; + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, - // Count the total number of enum - kCount_CentEstimators + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + NumCentEstimators }; enum EventType { SameEvent = 1, MixedEvent = 3 }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTOF, + UseITS }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + enum Stage { + Before = 0, + After }; - std::array, 15> eventCuts; - std::array, 12> resoCutVals; - std::array, 7> resoSwitchVals; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; - + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; // persistent caches std::vector efficiencyAssociatedCache; + std::vector useAssociatedCache; void init(InitContext&) { - readMatrix(resonanceCuts->getData(), resoCutVals, kCosPA, kNParticleCuts, iK0, NResoParticles); - readMatrix(resonanceSwitches->getData(), resoSwitchVals, kUseCosPA, kNParticleSwitches, iK0, NResoParticles); - readMatrix(cfgUseEventCuts->getData(), eventCuts, kFilteredEvents, kNEventCuts, kEvCut1, kNEvCutTypes); - - tpcNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTPC]; - tpcNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTPC]; - tpcNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTPC]; - tpcNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTPC]; - tpcNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTPC]; - tpcNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTPC]; - - tofNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTOF]; - tofNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTOF]; - tofNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTOF]; - tofNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTOF]; - tofNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTOF]; - tofNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTOF]; - - itsNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kITS]; - itsNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kITS]; - itsNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kITS]; - itsNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kITS]; - itsNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kITS]; - itsNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kITS]; - - AxisSpec axisK0Mass = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0]}; - AxisSpec axisLambdaMass = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda]}; - AxisSpec axisPhiMass = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi]}; - - if (cfgCentTableUnavailable && !cfgSelCollByNch) { + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.cfgPIDnSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgPIDResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgPIDResoSwitches->getData(), resoSwitchVals); + + if (cfgEvSel.cfgCentTableUnavailable && !cfgEvSel.cfgSelCollByNch) { LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); } const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; @@ -363,238 +398,263 @@ struct PidDiHadron { LOGF(info, "Starting init"); - // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi - AxisSpec massAxisReso = {10, 0, 1, "mass"}; - if (cfgPIDParticle == kK0) - massAxisReso = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0], "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kLambda) - massAxisReso = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda], "M_{p#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kPhi) - massAxisReso = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi], "M_{K^{+}K^{-}} (GeV/c^{2})"}; + // Creating mass axis depending on particle - 4 = kshort, 5 = lambda + AxisSpec axisInvMass = {10, 0, 1, "mass"}; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; + + axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; + } + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; + + axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; + } + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } // Event Counter - if ((doprocessSame || doprocessSameReso || doprocessMC) && cfgUseAdditionalEventCut) { + if ((doprocessSame || doprocessSameReso || doprocessMC) && cfgEvSel.cfgUseAdditionalEventCut) { histos.add("hEventCount", "Number of Events;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered event"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseVertexITSTPC + 1, "kIsVertexITSTPC"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseTVXinTRD + 1, "kTVXinTRD"); - } - - if (cfgPIDParticle == kK0) { // For K0 - histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + } + + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{11, 0, 11}}}); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(1, "K0 candidates"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(10, "ArmenterosPod"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(11, "Daughter track selection"); - } - if (cfgPIDParticle == kLambda) { // For Lambda - histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0, 10}}}); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(1, "Lambda candidates"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(10, "Daughter track selection"); - } - if (cfgPIDParticle == kPhi) { // For Phi - histos.add("KaPlusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaMinusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaPlusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("KaMinusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("hPhiPhi", "", {HistType::kTH1D, {axisPhi}}); - histos.add("hPhiEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hPhiMass_sparse", "", {HistType::kTHnSparseD, {{axisPhiMass, axisPt, axisMultiplicity}}}); - - histos.add("hPhiCount", "Number of Phi;; Count", {HistType::kTH1D, {{5, 0, 5}}}); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(1, "Phi candidates"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(2, "Daughter track selection"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(3, "Fake Kaon"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(4, "CosPA"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(5, "Rapidity cut"); + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); } // Multiplicity correlation cuts - if (eventCuts[kUseMultCorrCut][kEvCut1]) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutLow->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutHigh->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutLow->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutHigh->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutLow->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutLow->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutHigh->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - } - if (eventCuts[kUseT0AV0ACut][kEvCut1]) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str()), 0.001, 100); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgFuncParas.cfgDCAxyNSigma); - LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str())); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgEvSel.cfgCentEstimator); - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); // Make histograms to check the distributions after cuts if (doprocessSame || doprocessSameReso) { - histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); - histos.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + histos.add("Nch_corrected", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); histos.add("CentralityWeighted", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); - histos.add("Centrality_used", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); // histogram to see how many events are in the same and mixed event histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - histos.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); - - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - histos.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - - if (cfgGetNsigmaQA) { - if (!cfgUseItsPID) { - histos.add("TofTpcNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - histos.add("TofTpcNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - - if (cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - - histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - } - } // TPC-TOF PID QA hists - if (cfgUseItsPID) { - histos.add("TofItsNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - histos.add("TofItsNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - } // ITS-TOF PID QA hists - } // end of PID QA hists + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + } + + if (doprocessSame) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, massAxisReso}}}); + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgPIDUseIts) { // TPC-TOF PID QA hists + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + if (cfgPIDConfigs.cfgPIDUseIts) { // ITS-TOF PID QA hists + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + } // end of PID nsigma QA hists + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } // end of PID dEdx QA hists + + // Trigger histogram + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + if (doprocessSameReso) { + if (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda) { + histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); } } + if (doprocessMixed || doprocessMixedReso) { histos.add("deltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); } if (doprocessMC) { - histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are pions and selected as pions - histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are kaons and selected as kaons - histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are protons and selected as protons + histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are pions and selected as pions + histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are kaons and selected as kaons + histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are protons and selected as protons - histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPt}}); + histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPt}}); // All true pions from MC - histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPt}}); // All true kaons from MC - histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPt}}); // All true protons from MC + histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true pions from MC + histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true kaons from MC + histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true protons from MC - histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); } histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event LOGF(info, "Initializing correlation container"); - std::vector corrAxis = {{axisSample, "Sample"}, + std::vector corrAxis = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector corrAxisReso = {{axisSample, "Sample"}, + std::vector corrAxisReso = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {massAxisReso}, + {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - std::vector userAxis; - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + if (!cfgPIDConfigs.cfgPIDUseReso) { + same.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxis)); + mixed.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxis)); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - sameReso.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisReso, effAxis, userAxis)); - mixedReso.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisReso, effAxis, userAxis)); + if (cfgPIDConfigs.cfgPIDUseReso && (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda)) { + sameReso.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxisReso)); + mixedReso.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxisReso)); } LOGF(info, "End of init"); } @@ -617,18 +677,18 @@ struct PidDiHadron { template float getCentrality(TCollision const& collision) { - float cent; - switch (cfgCentEstimator) { - case kCentFT0C: + float cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -638,37 +698,75 @@ struct PidDiHadron { } template - bool trackSelected(const TTrack& track) + bool trackSelected(TTrack const& track) { - if (cfgFuncParas.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { return false; - return ((track.tpcNClsFound() >= cfgTpcCluster) && (track.tpcNClsCrossedRows() >= cfgTpcCrossRows) && (track.itsNCls() >= cfgITScluster)); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } template bool selectionV0Daughter(TTrack const& track, int pid) { - if (!(track.itsNCls() > cfgITScluster)) - return 0; - if (!track.hasTPC()) + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { return false; - if (track.tpcNClsFound() < cfgTpcCluster) + } + if (!track.hasTPC()) { + return false; + } + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { return false; - if (!(track.tpcNClsCrossedRows() > cfgTpcCrossRows)) - return 0; + } + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { + return false; + } - if (cfgUseOnlyTPC) { - if (pid == kPions && std::abs(track.tpcNSigmaPi()) > cfgTpcCut) + if (cfgPIDConfigs.cfgPIDUseOnlyTpc) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kKaons && std::abs(track.tpcNSigmaKa()) > cfgTpcCut) + } + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kProtons && std::abs(track.tpcNSigmaPr()) > cfgTpcCut) + } + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; + } } else { int partIndex = getNsigmaPID(track); int pidIndex = partIndex; // 1 = pion, 2 = kaon, 3 = proton - if (pidIndex != pid) + if (pidIndex != pid) { return false; + } } return true; @@ -683,21 +781,32 @@ struct PidDiHadron { std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - - bool isPion, isKaon, isProton; - bool isDetectedPion = nSigmaToUse[iPionUp] < detectorNsigmaCut[iPionUp] && nSigmaToUse[iPionUp] > detectorNsigmaCut[iPionLow]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < detectorNsigmaCut[iKaonUp] && nSigmaToUse[iKaonUp] > detectorNsigmaCut[iKaonLow]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < detectorNsigmaCut[iProtonUp] && nSigmaToUse[iProtonUp] > detectorNsigmaCut[iProtonLow]; - - bool isTofPion = nSigmaTOF[iPionUp] < tofNsigmaCut[iPionUp] && nSigmaTOF[iPionUp] > tofNsigmaCut[iPionLow]; - bool isTofKaon = nSigmaTOF[iKaonUp] < tofNsigmaCut[iKaonUp] && nSigmaTOF[iKaonUp] > tofNsigmaCut[iKaonLow]; - bool isTofProton = nSigmaTOF[iProtonUp] < tofNsigmaCut[iProtonUp] && nSigmaTOF[iProtonUp] > tofNsigmaCut[iProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + std::array nSigmaToUse = cfgPIDConfigs.cfgPIDUseIts ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgPIDUseIts ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && !track.hasTOF()) { return -1; - } else if (track.pt() > cfgTofPtCut && track.hasTOF()) { + } + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -707,16 +816,16 @@ struct PidDiHadron { isProton = isDetectedProton; } - if (cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { return -1; // more than one particle satisfy the criteria } if (isPion) { - pid = kPions; + pid = UsePions; } else if (isKaon) { - pid = kKaons; + pid = UseKaons; } else if (isProton) { - pid = kProtons; + pid = UseProtons; } else { return -1; // no particle satisfies the criteria } @@ -729,29 +838,60 @@ struct PidDiHadron { if (correctionsLoaded) { return; } - if (cfgEfficiency.value.empty() == false) { - if (cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); - mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); - if (cfgCentralityWeight.value.empty() == false) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) { float eff = 1.; @@ -763,93 +903,135 @@ struct PidDiHadron { } else { eff = 1.0; } - if (eff == 0) + if (eff == 0) { return false; + } weight_nue = 1. / eff; return true; } - bool getCentralityWeight(float& weightCent, const float centrality) + bool getCentralityWeight(float& weightCent, const double centrality) { float weight = 1.; - if (mCentralityWeight) + if (mCentralityWeight) { weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); - else + } else { weight = 1.0; - if (weight == 0) + } + if (weight == 0) { return false; + } weightCent = weight; return true; } - // fill multiple histograms - template - void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + template + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram { - float weff1 = 1; - float vtxz = collision.posZ(); - for (auto const& track1 : tracks) { - if (!trackSelected(track1)) + double nTracksCorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { continue; - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID - if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), vtxz)) + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { continue; - histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - histos.fill(HIST("Eta"), track1.eta()); - histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); - histos.fill(HIST("pT"), track1.pt()); - histos.fill(HIST("pTCorrected"), track1.pt(), weff1); + } + nTracksCorrected += weightNch; } + multiplicity = nTracksCorrected; } template - void fillNsigmaAfterCut(const TTrack& track1, Int_t pid) // function to fill the QA after Nsigma selection + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection { - switch (pid) { - case 1: // For Pions - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged && (cfgPIDConfigs.cfgQANsigma || cfgPIDConfigs.cfgQAdEdx)) { + const bool useITS = cfgPIDConfigs.cfgPIDUseIts; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - break; - case 2: // For Kaons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - break; - case 3: // For Protons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (cfgPIDConfigs.cfgQANsigma) { + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); - break; - } // end of switch + } + + if (cfgPIDConfigs.cfgQAdEdx) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle } float getDPhiStar(float charge1, float charge2, float phi1, float phi2, float pt1, float pt2, float radius, int magField) @@ -858,246 +1040,252 @@ struct PidDiHadron { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) + } + if (dPhiStar < -constants::math::PI) { dPhiStar = -constants::math::TwoPI - dPhiStar; + } return dPhiStar; } - template - void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelations(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } - - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks1.size()); - } + } // end of efficieny requirement - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { - if (!trackSelected(track1)) + if (!trackSelected(trig)) { continue; - - // Fill Nsigma QA - if (cfgGetNsigmaQA && !cfgUseItsPID) { - if (cfgPIDParticle == kPions) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); - } - } - if (cfgPIDParticle == kKaons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); - } - } - if (cfgPIDParticle == kProtons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); - } - } } - if (cfgGetNsigmaQA && cfgUseItsPID) { - if (cfgPIDParticle == kPions) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - if (cfgPIDParticle == kKaons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - if (cfgPIDParticle == kProtons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + + if (cfgEffWeights.cfgUseTrigWeight && !getEfficiencyCorrection(triggerWeight, trig.eta(), trig.pt(), posZ)) { + continue; } - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID + if (system == SameEvent) { + fillTrackQA(trig, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(trig, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseProtons, eventClass, Before, triggerWeight); + } - if (cfgGetNsigmaQA) - fillNsigmaAfterCut(track1, cfgPIDParticle); + int pidIndex = getNsigmaPID(trig); - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; if (system == SameEvent) { - histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + fillTrackQA(trig, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) + if (useAssociatedCache[assc.filteredIndex()] == 0) { // rejecting associated tracks continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; } - if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { // loading associated efficiency + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { continue; // For pt-differential correlations, skip if the trigger and associate are the same track - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } - } + } // End of merging cut condition // fill the right sparse and histograms if (system == SameEvent) { - - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } } } - template - void fillCorrelationsReso(const TV0Tracks& tracks1, const TTracksAssoc& tracks2, float posZ, float posY, float posX, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelationsReso(const TV0Tracks& triggers, const TTracksAssoc& assocs, float posZ, float posY, float posX, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } - - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks2.size()); // Taking Nch from tracks2 since tracks1 are V0s - } + } // end of efficieny requirement - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { double resoMass = -1; - // 4 = kshort, 5 = lambda, 6 = phi - if (cfgPIDParticle == kK0) { - if (!isSelectedK0(track1, posZ, posY, posX)) + // 4 = kshort, 5 = lambda + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(trig, posZ, posY, posX, system)) { continue; // Reject if called for K0 but V0 is not K0 + } - resoMass = track1.mK0Short(); + resoMass = trig.mK0Short(); } - if (cfgPIDParticle == kLambda) { - if (!isSelectedLambda(track1, posZ, posY, posX)) + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(trig, posZ, posY, posX, system)) { continue; // Reject if called for Lambda but V0 is not lambda - - resoMass = track1.mLambda(); + } + resoMass = trig.mLambda(); } if (system == SameEvent) { - histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); + histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, eventClass, trig.pt(), resoMass, eventWeight * triggerWeight); } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) - continue; - if (track2.pt() < cfgCutPtMin || track2.pt() > cfgCutPtMax) // Select associated particles in the pt range 0.2 - 3.0 GeV/c - continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + if (useAssociatedCache[assc.filteredIndex()] == 0) { + continue; // rejecting associated tracks } - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; // loading associated efficiency + } + + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } // fill the right sparse and histograms if (system == SameEvent) { - sameReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + sameReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - mixedReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixedReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } @@ -1105,144 +1293,165 @@ struct PidDiHadron { } template - bool selectionEvent(const TCollision& collision, const int mult, const float cent, const bool fillCounter) + bool selectionEvent(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { - if (fillCounter) - histos.fill(HIST("hEventCount"), kFilteredEvents); + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } if (!collision.sel8()) { - return 0; + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); } - if (fillCounter) - histos.fill(HIST("hEventCount"), kAfterSel8); - if (eventCuts[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && eventCuts[kUseNoTimeFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); - if (eventCuts[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (fillCounter && eventCuts[kUseNoITSROFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); - if (eventCuts[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (fillCounter && eventCuts[kUseNoSameBunchPileup][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoSameBunchPileup); - if (eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (fillCounter && eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); - if (eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); } - if (fillCounter && eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); - if (eventCuts[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; } - if (fillCounter && eventCuts[kUseGoodITSLayersAll][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodITSLayersAll); - if (eventCuts[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInRofStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInRofStandard); - if (eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } - if (fillCounter && eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy < cfgCutOccupancyMin || occupancy > cfgCutOccupancyMax)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyMin || occupancy > cfgEvSel.cfgCutOccupancyMax)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); } - if (fillCounter && eventCuts[kUseOccupancy][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseOccupancy); - if (eventCuts[kUseMultCorrCut][kEvCut1]) { - if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) - return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) - return 0; + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } } - if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgCentTableUnavailable) { - if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) - return 0; - if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) - return 0; + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) - return 0; + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } } } - if (fillCounter && eventCuts[kUseMultCorrCut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } // V0A T0A 5 sigma cut - if (eventCuts[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && eventCuts[kUseT0AV0ACut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseT0AV0ACut); - - if (eventCuts[kUseVertexITSTPC][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return 0; - if (fillCounter && eventCuts[kUseVertexITSTPC][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseVertexITSTPC); - - if (eventCuts[kUseTVXinTRD][kEvCut1] && collision.alias_bit(kTVXinTRD)) { - return 0; + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); } - if (fillCounter && eventCuts[kUseTVXinTRD][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseTVXinTRD); - return 1; + return true; } void processSame(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1250,24 +1459,32 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - fillYield(collision, tracks); - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelations(tracks, assocs, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSame, "Process same event", true); // the process for filling the mixed events void processMixed(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); @@ -1279,53 +1496,71 @@ struct PidDiHadron { MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { continue; - - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } - fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); + fillCorrelations(tracks1, assocs, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixed, "Process mixed events", true); template - bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system) { double mk0 = candidate.mK0Short(); @@ -1333,58 +1568,103 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hK0Count"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iK0] || negtrack.pt() < resoCutVals[kNegTrackPt][iK0]) + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 1.5); - if (mk0 < resoCutVals[kMassMin][iK0] && mk0 > resoCutVals[kMassMax][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 2.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillMassCut); + } // Rapidity correction - if (candidate.yK0Short() > resoCutVals[kRapidity][iK0]) + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } // DCA cuts for K0short - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iK0]) + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iK0] && (candidate.v0radius() < resoCutVals[kRadiusMin][iK0] || candidate.v0radius() > resoCutVals[kRadiusMax][iK0])) + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { return false; - histos.fill(HIST("hK0Count"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iK0]) + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCosPA); + } // Proper lifetime float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; - if (resoSwitchVals[kUseProperLifetime][iK0] && std::abs(cTauK0) > resoCutVals[kLifeTime][iK0]) + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 8.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } // ArmenterosPodolanskiCut - if (resoSwitchVals[kUseArmPodCut][iK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[kArmPodMinVal][iK0]) + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 9.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + // Selection on V0 daughters - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kPions)) + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { return false; - histos.fill(HIST("hK0Count"), 10.5); - - histos.fill(HIST("hK0Phi"), candidate.phi()); - histos.fill(HIST("hK0Eta"), candidate.eta()); - histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); - histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); - histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } return true; } template - bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system) { bool isL = false; // Is lambda candidate bool isAL = false; // Is anti-lambda candidate @@ -1396,71 +1676,127 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hLambdaCount"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iLambda] || negtrack.pt() < resoCutVals[kNegTrackPt][iLambda]) + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { return false; - - histos.fill(HIST("hLambdaCount"), 1.5); - if (mlambda > resoCutVals[kMassMin][iLambda] && mlambda < resoCutVals[kMassMax][iLambda]) + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { isL = true; - if (mantilambda > resoCutVals[kMassMin][iLambda] && mantilambda < resoCutVals[kMassMax][iLambda]) + } + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { isAL = true; + } if (!isL && !isAL) { return false; } - histos.fill(HIST("hLambdaCount"), 2.5); + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } // Rapidity correction - if (candidate.yLambda() > resoCutVals[kRapidity][iLambda]) + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } + // DCA cuts for lambda and antilambda - if (isL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iLambda]) - return false; + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { + isL = false; } - if (isAL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCANegToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCAPosToPVMin][iLambda]) - return false; + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { + isAL = false; + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); } - histos.fill(HIST("hLambdaCount"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iLambda]) + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iLambda] && (candidate.v0radius() < resoCutVals[kRadiusMin][iLambda] || candidate.v0radius() > resoCutVals[kRadiusMax][iLambda])) + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { return false; - histos.fill(HIST("hLambdaCount"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iLambda]) + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } // Proper lifetime float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; - if (resoSwitchVals[kUseProperLifetime][iLambda] && cTauLambda > resoCutVals[kLifeTime][iLambda]) + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 8.5); - if (isL) { - if (!selectionV0Daughter(postrack, kProtons) || !selectionV0Daughter(negtrack, kPions)) - return false; } - if (isAL) { - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kProtons)) - return false; + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); } - histos.fill(HIST("hLambdaCount"), 9.5); - if (!cfgUseAntiLambda && isAL) { // Reject the track if it is antilambda + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { return false; } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } - histos.fill(HIST("hLambdaPhi"), candidate.phi()); - histos.fill(HIST("hLambdaEta"), candidate.eta()); - histos.fill(HIST("PrPlusTPC_L"), postrack.pt(), postrack.tpcNSigmaPr()); - histos.fill(HIST("PrPlusTOF_L"), postrack.pt(), postrack.tofNSigmaPr()); - histos.fill(HIST("PiMinusTPC_L"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_L"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } + + if (isL) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { + isL = false; + } + } + if (isAL) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { + isAL = false; + } + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); + } + if (isL && system == SameEvent) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + } return true; } @@ -1472,15 +1808,15 @@ struct PidDiHadron { void processSameReso(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1488,17 +1824,28 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - sameReso->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReso(V0s, tracks, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelationsReso(V0s, assocs, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSameReso, "Process same event for resonances", true); @@ -1515,48 +1862,69 @@ struct PidDiHadron { MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) - continue; + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { + continue; } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - fillCorrelationsReso(v0s1, tracks2, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + fillCorrelationsReso(v0s1, assocs, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixedReso, "Process mixed events", true); @@ -1564,54 +1932,64 @@ struct PidDiHadron { void processMC(FilteredCollisions::iterator const& collision, FilteredMcTracks const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) { float cent = -1.; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) { return; + } - if (cfgSelCollByNch && (tracksmc.size() < cfgCutMultMin || tracksmc.size() >= cfgCutMultMax)) { + if (cfgEvSel.cfgSelCollByNch && (tracksmc.size() < cfgEvSel.cfgCutMultMin || tracksmc.size() >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } // loop over all tracks for (auto const& track : tracksmc) { - if (!trackSelected(track)) + if (!trackSelected(track)) { continue; + } int pidIndex = getNsigmaPID(track); // Fill Counts for selection through Nsigma cuts - if (pidIndex == kPions) + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionSelected"), track.pt()); - if (pidIndex == kKaons) + } + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonSelected"), track.pt()); - if (pidIndex == kProtons) + } + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonSelected"), track.pt()); + } if (track.mcParticle().isPhysicalPrimary()) { - if (cfgPIDParticle == kPions) + if (cfgPIDConfigs.cfgPIDParticle == UsePions) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (cfgPIDParticle == kKaons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseKaons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (cfgPIDParticle == kProtons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseProtons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } } // Check the PDG code for the particles (MC truth) and match with analysed Nsigma PID if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kPiPlus) { histos.fill(HIST("hNsigmaPionTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pi"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pi_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + } double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); histos.fill(HIST("ExpTpcdEdx_ptwise_pi"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - if (pidIndex == kPions) { + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionTruePositives"), track.pt()); } } // Pion condition @@ -1619,13 +1997,14 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kKPlus) { histos.fill(HIST("hNsigmaKaonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_ka"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_ka_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + } double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); histos.fill(HIST("ExpTpcdEdx_ptwise_ka"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - if (pidIndex == kKaons) { + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonTruePositives"), track.pt()); } } // Kaon condition @@ -1633,13 +2012,14 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kProton) { histos.fill(HIST("hNsigmaProtonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pr"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pr_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); histos.fill(HIST("ExpTpcdEdx_ptwise_pr"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - if (pidIndex == kProtons) { + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonTruePositives"), track.pt()); } } // Proton condition diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx index 90d7de1838e..555d4f8de81 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -111,10 +111,9 @@ struct PidLongRange { O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "DCAz range for tracks") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") - Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"(0.0026+0.005/(x^1.01))", "(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; } cfgTrkSel; @@ -127,8 +126,10 @@ struct PidLongRange { O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; } cfgEvSel; @@ -204,7 +205,7 @@ struct PidLongRange { ConfigurableAxis axisTpcNcls{"axisTpcNcls", {500, 0, 500}, "number of TPC clusters used for PID"}; // Axes for long-range correlations QA - ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; ConfigurableAxis cfgFITamp{"cfgFITamp", {1000, 0, 5000}, "FT0 amplitude"}; @@ -281,7 +282,7 @@ struct PidLongRange { }; enum DetectorType { UseTPC = 0, - UseTof, + UseTOF, UseITS }; enum Stage { @@ -512,7 +513,6 @@ struct PidLongRange { histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); - histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); } if (doprocessSameTpcFt0 || doprocessQA) { @@ -566,7 +566,6 @@ struct PidLongRange { histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); - histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event } if (doprocessSameFt0aFt0c) { @@ -581,27 +580,29 @@ struct PidLongRange { histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); - histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event } + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); + LOGF(info, "Initializing correlation container"); - // Initialize Nch-related histograms and containers + // Initialize containers std::vector corrAxisTpcFt0a = {{axisSample}, {axisVertex, "z-vtx (cm)"}, {axisEventClass}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcFt0a, "#Delta#eta"}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}}; + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; std::vector corrAxisTpcFt0c = {{axisSample}, {axisVertex, "z-vtx (cm)"}, {axisEventClass}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcFt0c, "#Delta#eta"}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}}; + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; std::vector corrAxisFt0aFt0c = {{axisSample}, {axisVertex, "z-vtx (cm)"}, @@ -636,7 +637,7 @@ struct PidLongRange { double getCentrality(TCollision const& collision) { double cent = 0.0; - switch (cfgTrkSel.cfgCentEstimator) { + switch (cfgEvSel.cfgCentEstimator) { case UseCentFT0C: cent = collision.centFT0C(); break; @@ -866,7 +867,7 @@ struct PidLongRange { } auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow || occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { histos.fill(HIST("hPassedEventSelection"), UseOccupancy); } } @@ -1029,9 +1030,9 @@ struct PidLongRange { isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; - isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTof] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTof]; - isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTof] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTof]; - isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTof] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTof]; + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { return -1; @@ -1305,22 +1306,23 @@ struct PidLongRange { if (!trackSelected(track1)) { continue; } - if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { // Keep efficiency string empty to disable, efficiency is not used for tpc-ft0 correlations continue; } - fillTrackQA(track1, UseCharged, eventClass, Before, triggerWeight); - fillTrackQA(track1, UsePions, eventClass, Before, triggerWeight); - fillTrackQA(track1, UseKaons, eventClass, Before, triggerWeight); - fillTrackQA(track1, UseProtons, eventClass, Before, triggerWeight); + if (system == SameEvent) { + fillTrackQA(track1, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track1, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseProtons, eventClass, Before, triggerWeight); + } int pidIndex = getNsigmaPID(track1); - if (pidIndex > UseCharged) { - fillTrackQA(track1, pidIndex, eventClass, After, triggerWeight); - } - if (system == SameEvent) { + if (pidIndex > UseCharged) { + fillTrackQA(track1, pidIndex, eventClass, After, triggerWeight); + } if (corType == IndexFT0C) { histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); if (pidIndex > UseCharged) { @@ -1359,18 +1361,18 @@ struct PidLongRange { histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } - sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); if (pidIndex > UseCharged) { - sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (corType == IndexFT0C) { if (cfgGeneral.cfgQABasic) { histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } - sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); if (pidIndex > UseCharged) { - sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } } else if (system == MixedEvent) { @@ -1379,18 +1381,18 @@ struct PidLongRange { histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } - mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); if (pidIndex > UseCharged) { - mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (corType == IndexFT0C) { if (cfgGeneral.cfgQABasic) { histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } - mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), UseCharged, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); if (pidIndex > UseCharged) { - mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, track1.pt(), pidIndex, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } } @@ -1499,8 +1501,6 @@ struct PidLongRange { histos.fill(HIST("zVtx"), collision.posZ()); - histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - auto multiplicity = static_cast(tracks.size()); if (cfgGeneral.cfgQABasic) { @@ -1522,6 +1522,8 @@ struct PidLongRange { return; } + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; const auto& ft0 = collision.foundFT0(); @@ -1536,7 +1538,6 @@ struct PidLongRange { void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) { - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); @@ -1574,8 +1575,6 @@ struct PidLongRange { continue; } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgGeneral.cfgRunRemoveList.value.empty()) { @@ -1587,23 +1586,31 @@ struct PidLongRange { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); - auto multiplicity = static_cast(tracks1.size()); + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks1, multiplicity, false); + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } } - if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - if (!cfgEvSel.cfgSelCollByNch && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent1; + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float eventWeight = 1.0f; @@ -1663,7 +1670,6 @@ struct PidLongRange { histos.fill(HIST("CentralityWeighted"), cent, weightCent); histos.fill(HIST("zVtx"), collision.posZ()); - histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin fillYieldFt0aFt0c(collision, tracks); @@ -1688,6 +1694,8 @@ struct PidLongRange { return; } + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; const auto& ft0 = collision.foundFT0(); @@ -1737,8 +1745,6 @@ struct PidLongRange { continue; } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgGeneral.cfgRunRemoveList.value.empty()) { @@ -1749,23 +1755,32 @@ struct PidLongRange { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); - auto multiplicity = static_cast(tracks1.size()); + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); if (cfgGeneral.cfgStrictTrackCounter) { - nchCounter(tracks1, multiplicity, false); + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } } - if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { continue; } if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - if (!cfgEvSel.cfgSelCollByNch && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { continue; } - auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent1; + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float eventWeight = 1.0f; From d2eec1a09a778c2b2ed379b9c5050102c273b81b Mon Sep 17 00:00:00 2001 From: HANSEO PARK <53218370+hanseopark@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:20:37 +0900 Subject: [PATCH 309/648] [PWGJE] Add matched proton and pair efficiencies (#18294) --- PWGJE/Tasks/baryonPairInJets.cxx | 167 +++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 30 deletions(-) diff --git a/PWGJE/Tasks/baryonPairInJets.cxx b/PWGJE/Tasks/baryonPairInJets.cxx index 80539c2bbfb..ca47fe594f6 100644 --- a/PWGJE/Tasks/baryonPairInJets.cxx +++ b/PWGJE/Tasks/baryonPairInJets.cxx @@ -28,14 +28,15 @@ #include #include #include -#include #include #include +#include #include #include #include #include #include +#include #include #include @@ -44,10 +45,12 @@ #include #include +#include #include #include #include #include +#include #include using namespace o2; @@ -66,7 +69,7 @@ using BaryonTracks = soa::Join; using BaryonTracksMC = soa::Join; using JetTracksWithOriginal = soa::Join; -using JetTracksMCDWithOriginal = soa::Join; +using JetTracksWithOriginalAndMC = soa::Join; using ChargedJetsWithConstituents = soa::Join; using ChargedMCDJetsWithConstituents = soa::Join; using ChargedMCPJetsWithConstituents = soa::Join; @@ -102,6 +105,12 @@ struct BaryonPairInJets { Kaon = 1 }; + enum class EfficiencyStage : uint8_t { + Generated = 0, + TrackSelected = 1, + PidSelected = 2 + }; + // Species-independent selected-particle representation. Keep the full // kinematics needed by pair and future jet-fragmentation observables. struct SelectedParticle { @@ -197,6 +206,7 @@ struct BaryonPairInJets { ConfigurableAxis deltaEta{"deltaEta", {80, -0.8f, 0.8f}, "#Delta#eta"}; ConfigurableAxis deltaPhi{"deltaPhi", {80, -0.8f, 0.8f}, "#Delta#varphi"}; ConfigurableAxis deltaR{"deltaR", {40, 0.f, 0.8f}, "#Delta#it{R}"}; + ConfigurableAxis particleJetDistance{"particleJetDistance", {40, 0.f, 0.4f}, "#Delta#it{R}(p, particle-level jet)"}; ConfigurableAxis inclusiveDeltaEta{"inclusiveDeltaEta", {64, -1.6f, 1.6f}, "#Delta#eta"}; ConfigurableAxis inclusiveDeltaPhi{"inclusiveDeltaPhi", {72, -o2::constants::math::PIHalf, 3.f * o2::constants::math::PIHalf}, "#Delta#varphi"}; } axes; @@ -211,7 +221,8 @@ struct BaryonPairInJets { SliceCache inclusiveMixingCache; using MixingBinning = ColumnBinningPolicy; using MCPMixingBinning = ColumnBinningPolicy; - using InclusiveCollisions = soa::Join; + using InclusiveSameEventCollisions = soa::Join; + using InclusiveMixedEventCollisions = soa::Join; using InclusiveMixingBinning = ColumnBinningPolicy; void init(InitContext const&) @@ -235,6 +246,7 @@ struct BaryonPairInJets { AxisSpec axisDeltaEta{axes.deltaEta}; AxisSpec axisDeltaPhi{axes.deltaPhi}; AxisSpec axisDeltaR{axes.deltaR}; + AxisSpec axisParticleJetDistance{axes.particleJetDistance}; AxisSpec axisInclusiveDeltaEta{axes.inclusiveDeltaEta}; AxisSpec axisInclusiveDeltaPhi{axes.inclusiveDeltaPhi}; AxisSpec axisOrigin{3, 0.5, 3.5, "origin"}; @@ -315,6 +327,14 @@ struct BaryonPairInJets { registry.add("mc/pid/selectedCandidateOriginVsPt", "Selected proton-candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisBaryonPt, axisOrigin}); registry.add("mc/pid/selectedCandidateTruePdgVsPt", "Selected proton-candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisBaryonPt, {801, -4005.f, 4005.f}}); registry.add("mc/pid/ptResponsePrimaryProton", "Primary-proton momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisBaryonPt, axisBaryonPt}); + AxisSpec const axisCharge{2, -1.5f, 1.5f, "charge"}; + registry.add("mc/efficiency/generatedPrimaryProton", "Generated primary protons in matched particle-level jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/trackSelectedPrimaryProton", "Track-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/tofMatchedPrimaryProton", "TOF-matched primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/pidSelectedPrimaryProton", "PID-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/generatedPair", "Generated primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); + registry.add("mc/efficiency/trackSelectedPair", "Track-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); + registry.add("mc/efficiency/pidSelectedPair", "PID-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); addPairHistograms("mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); addPairHistograms("mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); registry.add("mc/particle/normalizationSameEvent", "Particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); @@ -552,12 +572,12 @@ struct BaryonPairInJets { return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaKa(), track.tofNSigmaKa(), pidCuts.kaonTpcNSigmaMax.value, pidCuts.kaonTofNSigmaMax.value, pidCuts.kaonCombinedNSigmaMax.value); } - template + template SelectedParticles collectSelectedParticles(JetType const& jet, bool fillQa, float weight = 1.f, bool collectControls = true) { SelectedParticles selected; for (auto const& jetTrack : jet.template tracks_as()) { - auto track = jetTrack.template track_as(); + auto track = jetTrack.template track_as(); if (!isSelectedTrack(track)) { continue; } @@ -875,7 +895,7 @@ struct BaryonPairInJets { template void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) { - float const pairTypeValue = static_cast(pairType); + auto const pairTypeValue = static_cast(pairType); auto fillSymmetrized = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& sparseName) { if (pairType == PairType::SameSign) { registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); @@ -938,7 +958,7 @@ struct BaryonPairInJets { void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) { - float const pairTypeValue = static_cast(pairType); + auto const pairTypeValue = static_cast(pairType); if (pairType == PairType::SameSign) { registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); @@ -1020,6 +1040,29 @@ struct BaryonPairInJets { } } + template + void fillEfficiencyPairs(std::vector const& particles, float detectorJetPt, float weight) + { + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + auto const pairType = static_cast(getPairType(particles[first], particles[second])); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + if constexpr (Stage == EfficiencyStage::Generated) { + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + } else if constexpr (Stage == EfficiencyStage::TrackSelected) { + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + } else { + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + } + } + } + } + template void fillControlMCPidQa(TrackType const& track, ParticleType const& particle, float weight) { @@ -1099,7 +1142,7 @@ struct BaryonPairInJets { } } - template + template void fillReconstructedSameEvent(CollisionType const& collision, JetsType const& jets, float weight = 1.f) { if (!isSelectedCollision(collision, true)) { @@ -1135,7 +1178,7 @@ struct BaryonPairInJets { registry.fill(HIST("jet/nConstituentsVsPt"), jet.pt(), static_cast(jet.tracksIds().size()), weight); } - auto selectedParticles = collectSelectedParticles(jet, output.fillQa.value, weight); + auto selectedParticles = collectSelectedParticles(jet, output.fillQa.value, weight); auto const& baryons = selectedParticles.baryons; if (output.fillQa.value) { int numberOfProtons = 0; @@ -1187,11 +1230,11 @@ struct BaryonPairInJets { JetTracksWithOriginal const&, BaryonTracks const&) { - fillReconstructedSameEvent(collision, jets); + fillReconstructedSameEvent(collision, jets); } PROCESS_SWITCH(BaryonPairInJets, processSameEvent, "Same-event identified baryon pairs in charged jets", true); - template + template void fillMixedJetPair(JetType1 const& jet1, JetType2 const& jet2, float weight) { float const jetPtDifference = std::abs(jet1.pt() - jet2.pt()); @@ -1208,8 +1251,8 @@ struct BaryonPairInJets { } constexpr bool CollectControls = Reference == MixedReference::Nominal; - auto selectedParticles1 = collectSelectedParticles(jet1, false, 1.f, CollectControls); - auto selectedParticles2 = collectSelectedParticles(jet2, false, 1.f, CollectControls); + auto selectedParticles1 = collectSelectedParticles(jet1, false, 1.f, CollectControls); + auto selectedParticles2 = collectSelectedParticles(jet2, false, 1.f, CollectControls); auto const& baryons1 = selectedParticles1.baryons; auto const& baryons2 = selectedParticles2.baryons; float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); @@ -1280,10 +1323,10 @@ struct BaryonPairInJets { if (!isSelectedJet(jet2)) { continue; } - fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); if (output.fillReferenceVariations.value) { - fillMixedJetPair(jet1, jet2, weight); - fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); } } } @@ -1294,12 +1337,12 @@ struct BaryonPairInJets { void processMCD(aod::JetCollisionsMCD::iterator const& collision, aod::JetMcCollisions const&, ChargedMCDJetsWithConstituents const& jets, - JetTracksMCDWithOriginal const&, + JetTracksWithOriginal const&, BaryonTracksMC const&, aod::McParticles const&) { float const weight = collision.mcCollision().weight(); - fillReconstructedSameEvent(collision, jets, weight); + fillReconstructedSameEvent(collision, jets, weight); if (!isSelectedCollision(collision, false)) { return; } @@ -1314,7 +1357,7 @@ struct BaryonPairInJets { void processMixedEventMCD(aod::JetCollisionsMCD const& collisions, aod::JetMcCollisions const&, ChargedMCDJetsWithConstituents const& jets, - JetTracksMCDWithOriginal const&, + JetTracksWithOriginal const&, BaryonTracksMC const&, aod::McParticles const&) { @@ -1348,10 +1391,10 @@ struct BaryonPairInJets { if (!isSelectedJet(jet2)) { continue; } - fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); if (output.fillReferenceVariations.value) { - fillMixedJetPair(jet1, jet2, weight); - fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); } } } @@ -1466,10 +1509,11 @@ struct BaryonPairInJets { aod::JetMcCollisions const&, MatchedChargedMCDJets const& detectorJets, MatchedChargedMCPJets const&, - JetTracksMCDWithOriginal const&, + JetTracksWithOriginalAndMC const&, + BaryonTracks const&, aod::JetParticles const&) { - if (!isSelectedCollision(collision, false)) { + if (!isSelectedCollision(collision, false) || !collision.has_mcCollision()) { return; } float const weight = collision.mcCollision().weight(); @@ -1480,7 +1524,7 @@ struct BaryonPairInJets { registry.fill(HIST("mc/jet/allDetectorPt"), detectorJet.pt(), weight); bool hasAcceptedMatch = false; for (auto const& particleJet : detectorJet.template matchedJetGeo_as()) { - if (!isSelectedJetGeometry(particleJet)) { + if (!isSelectedJetGeometry(particleJet) || particleJet.mcCollisionId() != collision.mcCollisionId()) { continue; } hasAcceptedMatch = true; @@ -1493,6 +1537,65 @@ struct BaryonPairInJets { float const deltaEta = detectorJet.eta() - particleJet.eta(); float const deltaPhi = RecoDecay::constrainAngle(detectorJet.phi() - particleJet.phi(), -o2::constants::math::PI); registry.fill(HIST("mc/jet/matchingDeltaR"), std::hypot(deltaEta, deltaPhi), weight); + + std::unordered_set generatedParticleIds; + std::vector generatedProtons; + for (auto const& particle : particleJet.template tracks_as()) { + if (!particle.isPhysicalPrimary() || std::abs(particle.pdgCode()) != PDG_t::kProton) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + generatedParticleIds.insert(particle.globalIndex()); + generatedProtons.push_back({particle.pt(), particle.eta(), particle.phi(), sign}); + registry.fill(HIST("mc/efficiency/generatedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + fillEfficiencyPairs(generatedProtons, detectorJet.pt(), weight); + + std::unordered_set trackSelectedParticleIds; + std::unordered_set tofMatchedParticleIds; + std::unordered_set pidSelectedParticleIds; + std::vector trackSelectedProtons; + std::vector pidSelectedProtons; + for (auto const& jetTrack : detectorJet.template tracks_as()) { + if (!jetTrack.has_mcParticle()) { + continue; + } + auto particle = jetTrack.template mcParticle_as(); + auto const particleId = particle.globalIndex(); + if (!generatedParticleIds.contains(particleId)) { + continue; + } + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{ + .pt = particle.pt(), + .eta = particle.eta(), + .phi = particle.phi(), + .sign = sign}; + if (trackSelectedParticleIds.insert(particleId).second) { + trackSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/trackSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (track.hasTOF() && tofMatchedParticleIds.insert(particleId).second) { + registry.fill(HIST("mc/efficiency/tofMatchedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (isSelectedProton(track) && pidSelectedParticleIds.insert(particleId).second) { + pidSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/pidSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + } + fillEfficiencyPairs(trackSelectedProtons, detectorJet.pt(), weight); + fillEfficiencyPairs(pidSelectedProtons, detectorJet.pt(), weight); break; } if (!hasAcceptedMatch) { @@ -1500,14 +1603,13 @@ struct BaryonPairInJets { } } } - PROCESS_SWITCH(BaryonPairInJets, processMatched, "Matched detector-particle charged-jet response", false); + PROCESS_SWITCH(BaryonPairInJets, processMatched, "Matched charged-jet response and proton efficiency", false); - void processInclusiveSameEvent(InclusiveCollisions::iterator const& collision, BaryonTracks const& tracks) + void processInclusiveSameEvent(InclusiveSameEventCollisions::iterator const& collision, BaryonTracks const& tracks) { if (!isSelectedInclusiveCollision(collision)) { return; } - registry.fill(HIST("inclusive/event/multiplicity"), collision.multFT0C()); registry.fill(HIST("inclusive/normalization/sameEvent"), 1.f); auto baryons = collectInclusiveBaryons(tracks); registry.fill(HIST("inclusive/normalization/sameEvent"), 2.f, static_cast(baryons.size())); @@ -1520,11 +1622,16 @@ struct BaryonPairInJets { } PROCESS_SWITCH(BaryonPairInJets, processInclusiveSameEvent, "Inclusive same-event identified baryon pairs", true); - void processInclusiveMixedEvent(InclusiveCollisions const& collisions, BaryonTracks const& tracks) + void processInclusiveMixedEvent(InclusiveMixedEventCollisions const& collisions, BaryonTracks const& tracks) { + for (auto const& collision : collisions) { + if (isSelectedInclusiveCollision(collision)) { + registry.fill(HIST("inclusive/event/multiplicity"), collision.multFT0C()); + } + } InclusiveMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; auto tracksTuple = std::make_tuple(tracks); - SameKindPair mixedCollisionPairs{ + SameKindPair mixedCollisionPairs{ mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, tracksTuple, &inclusiveMixingCache}; for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { From df5c671a61352075f8d32562ed438226dfa4a381 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:11:51 +0200 Subject: [PATCH 310/648] [Common] Improve Common PCH (#18306) --- Common/Core/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Common/Core/CMakeLists.txt b/Common/Core/CMakeLists.txt index 2522d1b1cce..8e9afaf06c9 100644 --- a/Common/Core/CMakeLists.txt +++ b/Common/Core/CMakeLists.txt @@ -51,6 +51,15 @@ if(NOT DEFINED ENV{USE_RECC}) + # Measured over all 1463 workflow translation units in the tree: these are + # the framework headers a large majority include and that were not + # precompiled here. Percentages are of those 1463. + # 78% + # 78% + # 76% + # 70% + # 69% + # 67% # NOT runDataProcessing.h. It states the contract itself: "we need to # declare the customize method before include this file". Precompiling it # makes it the first include in every workflow, so a source's customize() is @@ -58,6 +67,7 @@ if(NOT DEFINED ENV{USE_RECC}) # --dump-workflow with "missing option: ...". The link succeeds, so this # surfaces as a runtime failure with no hint that a PCH caused it. + # 51% From ea33048881fe8f71cab01b1491728117a35b08e5 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:13:08 +0200 Subject: [PATCH 311/648] [Common] Add fv0 and fdd channel info to centralityQa (#18273) --- Common/Tasks/centralityQa.cxx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Common/Tasks/centralityQa.cxx b/Common/Tasks/centralityQa.cxx index 72ad8e21925..2bf1fd28e77 100644 --- a/Common/Tasks/centralityQa.cxx +++ b/Common/Tasks/centralityQa.cxx @@ -332,6 +332,9 @@ struct CentralityQa { getHist("hBCSelection")->GetXaxis()->SetBinLabel(8, "isFlangeEvent"); insertHist("hAmpVsChFT0A", "hAmpVsChFT0A;Channel; Amplitude", kTH2D, {{96, -0.5, 95.5}, axisChannelAmplitude}); insertHist("hAmpVsChFT0C", "hAmpVsChFT0C;Channel; Amplitude", kTH2D, {{112, -0.5, 111.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFV0A", "hAmpVsChFV0A;Channel; Amplitude", kTH2D, {{48, -0.5, 47.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDA", "hAmpVsChFDDA;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDC", "hAmpVsChFDDC;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); } histos.print(); @@ -883,7 +886,7 @@ struct CentralityQa { } using BCsWithRun3Matchings = soa::Join; - void processBunchCrossings(soa::Join::iterator const& bc, aod::FT0s const&, aod::FV0As const&) + void processBunchCrossings(soa::Join::iterator const& bc, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) { if (!isBunchCrossingAccepted(bc, true)) { return; @@ -899,6 +902,24 @@ struct CentralityQa { histos.fill(HIST("hAmpVsChFT0C"), ft0.channelC()[ii], ft0.amplitudeC()[ii]); } } + + if (bc.has_fv0a()) { + const auto& fv0a = bc.fv0a(); + for (size_t ii{0}; ii < fv0a.channel().size(); ++ii) { + histos.fill(HIST("hAmpVsChFV0A"), fv0a.channel()[ii], fv0a.amplitude()[ii]); + } + } + + if (bc.has_fdd()) { + const auto& fdd = bc.fdd(); + for (size_t ii{0}; ii < std::size(fdd.chargeA()); ++ii) { + histos.fill(HIST("hAmpVsChFDDA"), ii, fdd.chargeA()[ii]); + } + + for (size_t ii{0}; ii < std::size(fdd.chargeC()); ++ii) { + histos.fill(HIST("hAmpVsChFDDC"), ii, fdd.chargeC()[ii]); + } + } } PROCESS_SWITCH(CentralityQa, processRun2, "Process with Run 2 V0A", false); From b0d17e0cececcd2967d8b0e85351586fa79caf92 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:13:43 +0200 Subject: [PATCH 312/648] [Common] Add mult per ancestor histogram to nbd fitter (#18258) --- .../Multiplicity/multGlauberNBDFitter.cxx | 18 +++++++++++++++++- .../Tools/Multiplicity/multGlauberNBDFitter.h | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx index e3c4635533d..c5d067c7e22 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx @@ -443,7 +443,20 @@ double multGlauberNBDFitter::ContinuousNBD(const double n, const double mu, cons return F; } -void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, double lLoRange, double lHiRange, TH3D* lNpNcEcc, TH2F* lEcc2DPlot, TH3D* lNpNcB, TH2F* lB2DPlot, TH2F* lNancestor2DPlot, double fProbabilityCutoff) +void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange, + double lHiRange, + TH3D* lNpNcEcc, + TH2F* lEcc2DPlot, + TH3D* lNpNcB, + TH2F* lB2DPlot, + TH2F* lNancestor2DPlot, + TH2F* lMult2DPlot, + double fProbabilityCutoff) { LOG(info) << "Calculating , in centrality bins..."; LOG(info) << "Range to calculate: " << lLoRange << " to " << lHiRange; @@ -557,6 +570,9 @@ void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCol // fill cross-check histogram with lNancestorCount at lNancestors value lNancestor2DPlot->Fill(lMultValueToFill, lNancestors, lProbability); } + if (lMult2DPlot) { + lMult2DPlot->Fill(lMultValueToFill, lMultValue, lProbability); + } if (lNPart2DPlot) { lNPart2DPlot->Fill(lMultValueToFill, fNpart[ibin], lProbability); } diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.h b/Common/Tools/Multiplicity/multGlauberNBDFitter.h index 7bf8c5a0346..3c5f6370722 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.h +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.h @@ -131,7 +131,20 @@ class multGlauberNBDFitter : public TNamed // For estimating Npart, Ncoll in multiplicity bins // also viable: eccentricity, impact parameter, ancestor cross-check plot - void CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, double lLoRange = -1, double lHiRange = -1, TH3D* lNpNcEcc = nullptr, TH2F* lEcc2DPlot = nullptr, TH3D* lNpNcB = nullptr, TH2F* lB2DPlot = nullptr, TH2F* lNancestor2DPlot = nullptr, double fProbabilityCutoff = -1); + void CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange = -1, + double lHiRange = -1, + TH3D* lNpNcEcc = nullptr, + TH2F* lEcc2DPlot = nullptr, + TH3D* lNpNcB = nullptr, + TH2F* lB2DPlot = nullptr, + TH2F* lNancestor2DPlot = nullptr, + TH2F* lMult2DPlot = nullptr, + double fProbabilityCutoff = -1); private: // This function serves as the (analytical) NBD From 02b7f9f4388fa7f45deac2c6424279d3d292f609 Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:17:43 +0200 Subject: [PATCH 313/648] [PWGCF] radialFlowDecorr: add pT-dependent efficiency systematic check. (#18319) --- .../Tasks/radialFlowDecorr.cxx | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 2b87e7d5ad6..e917af7fb09 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -112,23 +112,26 @@ struct RadialFlowDecorr { // Systematic-variation selector for DATA. Base is the main measurement; each // variation reads its own DataMean object (systSuffix) for the correlation step. - // kSystEtaBinning additionally changes the eta bin width via cfgEtaBinWidth. + // kSystEff and kSystEffPtDep scale the efficiency by cfgEffVarPercent; kSystEffPtDep + // always uses the pT-dependent shape. kSystCent is run with a different + // cfgCentralityChoice than the base (base FT0C, variation FT0M). enum ESystType { kSystBase = 0, + kSystCent, kSystDCA, kSystEff, + kSystEffPtDep, kSystFlat, kSystNITS, kSystNTPC, kSystPileup, kSystVz, - kSystEtaBinning, kNSystType }; // Suffix appended to the DataMean CCDB path per systematic (Base -> no suffix). inline static const std::vector systSuffix = { - "", "_systDCA", "_systEff", "_systFlat", - "_systNITS", "_systNTPC", "_systPileup", "_systVz", "_systEtaBinning"}; + "", "_systCent", "_systDCA", "_systEff", "_systEffpTDep", "_systFlat", + "_systNITS", "_systNTPC", "_systPileup", "_systVz"}; static constexpr float KinvalidCentrality = -1.0f; @@ -181,8 +184,8 @@ struct RadialFlowDecorr { Configurable cfgNchOMax{"cfgNchOMax", 800, "Max Nch range for OO collisions"}; Configurable cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; - Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systDCA,2=systEff,3=systFlat,4=systNITS,5=systNTPC,6=systPileup,7=systVz,8=systEtaBinning"}; - Configurable cfgNBootstrap{"cfgNBootstrap", 16, "Number of Poisson bootstrap samples (base data run only)"}; + Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systCent,2=systDCA,3=systEff,4=systEffpTDep,5=systFlat,6=systNITS,7=systNTPC,8=systPileup,9=systVz"}; + Configurable cfgNBootstrap{"cfgNBootstrap", 20, "Number of Poisson bootstrap samples (base data run only)"}; Configurable cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"}; Configurable cfgFlat{"cfgFlat", false, "Whether to use flattening weights"}; @@ -490,11 +493,12 @@ struct RadialFlowDecorr { float efficiencyVariationFactor(float pt) const { - if (cfgSystType.value != kSystEff) { + if (cfgSystType.value != kSystEff && cfgSystType.value != kSystEffPtDep) { return 1.f; } // Empirical shape: full variation at cfgPtMin, zero at cfgPtMax. - const float shape = cfgEffVarPtDep.value ? (cfgPtMax.value - pt) / (cfgPtMax.value - cfgPtMin.value) : 1.f; + const bool ptDep = cfgEffVarPtDep.value || cfgSystType.value == kSystEffPtDep; + const float shape = ptDep ? (cfgPtMax.value - pt) / (cfgPtMax.value - cfgPtMin.value) : 1.f; return 1.f + 0.01f * cfgEffVarPercent.value * shape; } @@ -1029,6 +1033,9 @@ struct RadialFlowDecorr { void init(InitContext&) { TH1::SetDefaultSumw2(true); + if (cfgSystType == kSystCent && cfgCentralityChoice == kCentFT0C) { + LOGF(warning, "cfgSystType=systCent but cfgCentralityChoice is still FT0C (the base estimator)."); + } // Nch axes by system if (cfgSys == kPbPb) { nChAxis = {cfgNchPbMax / 2, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; From e453690ea3d0ff15c48aa90405f3c881c1d8763d Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:02:38 +0200 Subject: [PATCH 314/648] [PWGHF] D+-h corr: add pTB to MC process functions (#18279) --- PWGHF/HFC/DataModel/CorrelationTables.h | 15 +++++ .../DataModel/DerivedDataCorrelationTables.h | 24 ++++---- .../TableProducer/correlatorDplusHadrons.cxx | 23 ++++++- .../HFC/TableProducer/correlatorDsHadrons.cxx | 6 +- .../HFC/Tasks/taskCorrelationDplusHadrons.cxx | 58 ++++++++++++++++-- PWGHF/HFC/Utils/utilsCorrelations.h | 61 +++++++++++++++++++ 6 files changed, 165 insertions(+), 22 deletions(-) diff --git a/PWGHF/HFC/DataModel/CorrelationTables.h b/PWGHF/HFC/DataModel/CorrelationTables.h index de52b75420f..fcea57be77c 100644 --- a/PWGHF/HFC/DataModel/CorrelationTables.h +++ b/PWGHF/HFC/DataModel/CorrelationTables.h @@ -497,6 +497,21 @@ DECLARE_SOA_TABLE(TrkRecInfoDplus, "AOD", "TRKRECINFODPLUS", //! Tracks Reconstr aod::hf_correlation_dplus_hadron::TrackDcaZ, aod::hf_correlation_dplus_hadron::TrackTPCNClsCrossedRows); +// definition of columns and tables for the beauty-mother information of the charm-hadron trigger (MC only), usable by any D-hadron correlation analysis +namespace hf_correlation_info_b_mother +{ +DECLARE_SOA_COLUMN(PtB, ptB, float); //! Generated pT of the beauty mother of the charm hadron (-1 if not from beauty) +DECLARE_SOA_COLUMN(DeltaPhiBtoD, deltaPhiBtoD, float); //! phi(charm hadron) - phi(B mother, generated) in [-pihalf, 3.*pihalf) (-999 if not from beauty) +} // namespace hf_correlation_info_b_mother + +DECLARE_SOA_TABLE(BMotherPairInfo, "AOD", "BMOTHERPAIRINFO", //! Charm-hadron-hadron pairs beauty-mother information, joinable with the pair table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + +DECLARE_SOA_TABLE(BMotherCandInfo, "AOD", "BMOTHERCANDINFO", //! Charm-hadron candidates beauty-mother information, joinable with the candidate table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + // definition of columns and tables for Dstar-Hadron correlation pair namespace hf_correlation_dstar_hadron { diff --git a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h index e7c69695f32..6a807a0db0c 100644 --- a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h +++ b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h @@ -47,16 +47,17 @@ using HfcRedCollision = HfcRedCollisions::iterator; namespace hf_candidate_reduced { -DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index -DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index -DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index -DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index -DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate -DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate -DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate -DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Invariant mass of charm candidate -DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis -DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis +DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index +DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index +DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index +DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index +DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate +DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate +DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate +DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Invariant mass of charm candidate +DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreNonPrompt, bdtScoreNonPrompt, float); //! BDT output score for non-prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis } // namespace hf_candidate_reduced DECLARE_SOA_TABLE(HcCandReduceds, "AOD", "HCCANDREDUCED", //! Table with charm hadron candidate info soa::Index<>, @@ -72,8 +73,9 @@ DECLARE_SOA_TABLE(HcCandReduceds, "AOD", "HCCANDREDUCED", //! Table with charm h DECLARE_SOA_TABLE(HcCandSelInfos, "AOD", "HCCANDSELINFO", //! Table with charm hadron candidate selection info soa::Index<>, aod::hf_candidate_reduced::HfcRedCollisionId, + aod::hf_candidate_reduced::BdtScoreBkg, aod::hf_candidate_reduced::BdtScorePrompt, - aod::hf_candidate_reduced::BdtScoreBkg); + aod::hf_candidate_reduced::BdtScoreNonPrompt); namespace hf_assoc_track_reduced { diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index ee3f8571f6f..a4d52996823 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -22,6 +22,7 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsCorrelations.h" #include "PWGHF/Utils/utilsAnalysis.h" #include "Common/CCDB/EventSelectionParams.h" @@ -186,9 +187,12 @@ struct HfCorrelatorDplusHadrons { Produces candSelInfo; Produces assocTrackReduced; Produces assocTrackSelInfo; + Produces entryDplusHadronBMotherInfo; + Produces entryDplusCandBMotherInfo; static constexpr std::size_t NDaughters{3u}; - static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr int8_t DepthMaxDplusDaughters{2}; // max. depth of the D+ daughters in the decay chain (resonant channels) Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for Dplus"}; // 7 corresponds to topo+PID cuts Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; @@ -401,6 +405,9 @@ struct HfCorrelatorDplusHadrons { // Count triggers registry.fill(HIST("hcountDplustriggersMCGen"), 0, particle.pt()); + // beauty-mother information + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(particle, particlesToLoop); + for (const auto& particleAssoc : groupedMcParticles) { if (std::abs(particleAssoc.eta()) > etaTrackMax || @@ -447,6 +454,7 @@ struct HfCorrelatorDplusHadrons { entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } @@ -552,7 +560,7 @@ struct HfCorrelatorDplusHadrons { int indexHfcReducedCollision = collReduced.lastIndex() + 1; - // Ds fill histograms and Dplus candidates information stored + // Dplus candidates information stored for (const auto& candidate : candsDplusThisColl) { std::vector outputMl = {-1., -1., -1.}; // candidate selected @@ -561,7 +569,8 @@ struct HfCorrelatorDplusHadrons { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt(), HfHelper::invMassDplusToPiKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + // Check compatibility with the candidate selector options: BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[1], outputMl[2]); } } @@ -631,6 +640,8 @@ struct HfCorrelatorDplusHadrons { bool isDplusNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; std::vector outputMl = {-1., -1., -1.}; + // beauty-mother information (generated B, reconstructed D+) + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); // fill invariant mass plots from Dplus signal and background candidates registry.fill(HIST("hMassDplusMcRec"), HfHelper::invMassDplusToPiKPi(candidate), efficiencyWeightD); @@ -663,6 +674,7 @@ struct HfCorrelatorDplusHadrons { registry.fill(HIST("hMassDplusMcRecSig"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), efficiencyWeightD); entryDplusCandRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2]); entryDplusCandGenInfo(isDplusPrompt); + entryDplusCandBMotherInfo(ptB, deltaPhiBtoD); } else { registry.fill(HIST("hPtCandMcRecBkg"), candidate.pt()); registry.fill(HIST("hEtaMcRecBkg"), candidate.eta()); @@ -691,6 +703,7 @@ struct HfCorrelatorDplusHadrons { track.pt(), poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronMlInfo(outputMl[0], outputMl[1], outputMl[2]); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); if (track.has_mcParticle()) { auto mcParticle = track.template mcParticle_as(); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); @@ -866,6 +879,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronGenInfo(isDplusPrompt, isPhysicalPrimary, trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } @@ -910,6 +925,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(MassDPlus, true); entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(candidate, mcParticles); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx index f0d4194148b..17add0a004d 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx @@ -673,7 +673,6 @@ struct HfCorrelatorDsHadrons { entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.tpcNClsCrossedRows()); } else if (isCorrectInvMassHypo && candidate.isSelDsToPiKK() >= selectionFlagDs) { // if (track.has_mcParticle()) { - auto mcParticle = track.template mcParticle_as(); entryDsHadronPair(getDeltaPhi(track.phi(), candidate.phi()), track.eta() - candidate.eta(), candidate.pt() * chargeDs, @@ -859,13 +858,14 @@ struct HfCorrelatorDsHadrons { outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToKKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + // Check compatibility with the candidate selector options; candSelInfo considers BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } else if (candidate.isSelDsToPiKK() >= selectionFlagDs) { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToPiKK(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } } diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index dc49dd86dfe..5ac86bf95f9 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -141,6 +141,8 @@ struct HfTaskCorrelationDplusHadrons { Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; Configurable storeMassAxis{"storeMassAxis", false, "Store Mass axis in THnSparse"}; + Configurable storeBMotherInfo{"storeBMotherInfo", false, "Store generated pT(B mother) and phi(D+) - phi(B mother) of non-prompt D+ in MC Rec histograms"}; + Configurable> binsPtBMother{"binsPtBMother", std::vector{0., 2., 4., 6., 8., 12., 16., 24., 36., 50., 99.}, "pT bin limits for the B mother of non-prompt D+"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable associatedEffCcdbPath{"associatedEffCcdbPath", "", "CCDB path for associated efficiency"}; @@ -164,8 +166,9 @@ struct HfTaskCorrelationDplusHadrons { kCandidateStepMcRecoInAcceptance, kCandidateNSteps }; - using DplusHadronPair = soa::Join; + using DplusHadronPairWithBMother = soa::Join; using DplusHadronPairFullWithMl = soa::Join; + using DplusHadronPairFullWithMlAndBMother = soa::Join; using CandDplusMcReco = soa::Filtered>; using CandDplusMcGen = soa::Join; using TracksWithMc = soa::Filtered>; // trackFilter applied @@ -179,6 +182,7 @@ struct HfTaskCorrelationDplusHadrons { ConfigurableAxis binsPhi{"binsPhi", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 8000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; + ConfigurableAxis binsDeltaPhiBMother{"binsDeltaPhiBMother", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}^{D}-#it{#varphi}^{B}"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -195,6 +199,8 @@ struct HfTaskCorrelationDplusHadrons { AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisDplusPrompt = {2, -0.5, 1.5, "Prompt D+"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; + AxisSpec axisPtBMother = {(std::vector)binsPtBMother, "#it{p}_{T}^{B} (GeV/#it{c})"}; + AxisSpec axisDeltaPhiBMother = {binsDeltaPhiBMother, "#it{#varphi}^{D}-#it{#varphi}^{B} (rad)"}; // Histograms for data analysis registry.add("hBdtScorePrompt", "D+ BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); @@ -259,6 +265,17 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtSidebandRightMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"))->Sumw2(); + + // beauty-mother information of non-prompt D+ (generated B, reconstructed D+) + if (storeBMotherInfo) { + registry.add("hMassNonPromptDplusVsPtVsPtBMother", "D+ non prompt candidates mass Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisPtBMother}}}); + registry.add("hDeltaPhiBMotherVsPtVsPtBMother", "D+ non prompt candidates #varphi^{D}-#varphi^{B} Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisDeltaPhiBMother}, {axisPtD}, {axisPtBMother}}}); + registry.add("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother", stringDHadron + "non-prompt D (only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother", stringDHadron + "signal region NonPromptD - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"))->Sumw2(); + } } // Histograms for MC Gen analysis if (fillHistoMcGen) { @@ -274,6 +291,15 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtMcGen"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenPrompt"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenNonPrompt"))->Sumw2(); + + // beauty-mother information of non-prompt D+, same axes as the MC Rec counterparts to allow Rec/Gen ratios + if (storeBMotherInfo) { + registry.add("hCorrel2DVsPtMcGenNonPromptBMother", stringDHadron + " NonPrompt MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother", stringDHadron + " non prompt D non prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"))->Sumw2(); + } } // Histograms for efficiencies registry.add("Efficiency/hPtCandMcRecPrompt", stringMCRecoDPrompt + stringPtD, {HistType::kTH1F, {axisPtD}}); @@ -312,7 +338,7 @@ struct HfTaskCorrelationDplusHadrons { } LOGF(info, "Loaded associated efficiency histogram from %s", associatedEffCcdbPath.value.c_str()); } - auto effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; + effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; idxBdtScore = isPromptAnalysis ? 1 : 2; if (activateQA) { @@ -439,8 +465,8 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processData, "Process data", false); /// D-Hadron correlation pair filling task, from pair tables - for MC reco-level analysis (candidates matched to true signal only, but also bkg sources are studied) - void processMcRec(DplusHadronPairFullWithMl const& pairEntries, - soa::Join const& candidates) + void processMcRec(DplusHadronPairFullWithMlAndBMother const& pairEntries, + soa::Join const& candidates) { for (const auto& candidate : candidates) { float const massD = candidate.mD(); @@ -482,6 +508,10 @@ struct HfTaskCorrelationDplusHadrons { registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); + if (storeBMotherInfo) { + registry.fill(HIST("hMassNonPromptDplusVsPtVsPtBMother"), massD, ptD, candidate.ptB(), efficiencyWeightD); + registry.fill(HIST("hDeltaPhiBMotherVsPtVsPtBMother"), candidate.deltaPhiBtoD(), ptD, candidate.ptB(), efficiencyWeightD); + } } } } @@ -556,6 +586,15 @@ struct HfTaskCorrelationDplusHadrons { } else if (!isDplusPrompt && originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } + // beauty-mother information, only for true non-prompt D+, to study the structures in the MC Closure test + if (storeBMotherInfo && pairEntry.signalStatus() && !isDplusPrompt) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + } + } } } // in sideband left region @@ -583,7 +622,7 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processMcRec, "Process MC Reco mode", true); /// D-Hadron correlation pair filling task, from pair tables - for MC gen-level analysis (no filter/selection, only true signal) - void processMcGen(DplusHadronPair const& pairEntries) + void processMcGen(DplusHadronPairWithBMother const& pairEntries) { for (const auto& pairEntry : pairEntries) { // define variables for widely used quantities @@ -608,6 +647,15 @@ struct HfTaskCorrelationDplusHadrons { if (originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadron"), deltaPhi, deltaEta, ptD, ptHadron, poolBin); } + // beauty-mother information + if (storeBMotherInfo) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + } + } } } } diff --git a/PWGHF/HFC/Utils/utilsCorrelations.h b/PWGHF/HFC/Utils/utilsCorrelations.h index 0f5fea00a9c..884be2d1704 100644 --- a/PWGHF/HFC/Utils/utilsCorrelations.h +++ b/PWGHF/HFC/Utils/utilsCorrelations.h @@ -18,6 +18,7 @@ #include "PWGHF/Core/DecayChannels.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -29,6 +30,9 @@ #include #include +#include +#include +#include namespace o2::analysis::hf_correlations { @@ -227,5 +231,62 @@ int findLeadingParticleMcGen(TMcParticles const& mcParticles, T1 const etaTrackM } return leadingParticle.globalIndex(); } + +// ======= Beauty-mother information of non-prompt charm hadrons ============ +constexpr float PtBMotherDefault{-1.f}; // pT(B mother) returned when the charm hadron is not from beauty +constexpr float DeltaPhiBMotherDefault{-999.f}; // phi(charm hadron) - phi(B mother) returned when the charm hadron is not from beauty + +/// Gets the generated beauty mother of a reconstructed charm-hadron candidate matched to a non-prompt charm hadron +/// \tparam TTracks is the track table with MC labels, used to access the candidate prongs +/// \param candidate is the reconstructed candidate (with originMcRec column) +/// \param mcParticles is the table of MC particles +/// \param pdgCharmHadron is the PDG code of the charm hadron (e.g. Pdg::kDPlus), antiparticles are accepted +/// \param depthMaxDaughters is the max. depth of the candidate prong in the charm-hadron decay chain (> 1 for resonant channels) +/// \return pair (pT of the B mother, phi(candidate, reco) - phi(B mother, gen) in [-pi, pi)), default values if the candidate is not from beauty +template +std::pair getBMotherInfoMcRec(TCandidate const& candidate, TMcParticles const& mcParticles, int pdgCharmHadron, int8_t depthMaxDaughters = 2) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (candidate.originMcRec() != RecoDecay::OriginType::NonPrompt) { + return bMotherInfo; + } + // retrieve the generated charm hadron from the MC particle of one of its daughters + auto prong0 = candidate.template prong0_as(); + if (!prong0.has_mcParticle()) { + return bMotherInfo; + } + auto prong0McPart = prong0.template mcParticle_as(); + int const indexCharmHadronGen = RecoDecay::getMother(mcParticles, prong0McPart, pdgCharmHadron, true, nullptr, depthMaxDaughters); + if (indexCharmHadronGen < 0) { + return bMotherInfo; + } + // same procedure used in the candidate creators to fill PtBhadMotherPart + auto charmHadronGen = mcParticles.rawIteratorAt(indexCharmHadronGen - mcParticles.offset()); + std::vector idxBhadMothers{}; + if (RecoDecay::getCharmHadronOrigin(mcParticles, charmHadronGen, false, &idxBhadMothers) != RecoDecay::OriginType::NonPrompt || idxBhadMothers.empty()) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0] - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(candidate.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} + +/// Gets the beauty mother of a generated charm hadron +/// \param particle is the generated charm hadron (with originMcGen and idxBhadMotherPart columns) +/// \param mcParticles is the table of MC particles containing the B mother +/// \return pair (pT of the B mother, phi(charm hadron) - phi(B mother) in [-pi, pi)), default values if the charm hadron is not from beauty +template +std::pair getBMotherInfoMcGen(TParticle const& particle, TMcParticles const& mcParticles) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (particle.originMcGen() != RecoDecay::OriginType::NonPrompt || particle.idxBhadMotherPart() < 0) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(particle.idxBhadMotherPart() - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(particle.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} } // namespace o2::analysis::hf_correlations #endif // PWGHF_HFC_UTILS_UTILSCORRELATIONS_H_ From 50a4050e0670a50053e2c56bc72af9d0c8238200 Mon Sep 17 00:00:00 2001 From: omvazque Date: Fri, 16 Oct 2026 11:03:52 -0600 Subject: [PATCH 315/648] [PWGLF] Removed the unsued processSim() function (#18321) --- PWGLF/Tasks/Nuspex/piKpRAA.cxx | 1299 +++++--------------------------- 1 file changed, 185 insertions(+), 1114 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index 03f52ce8c5d..7a4fb0200c8 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -128,10 +128,6 @@ struct PiKpRAA { static constexpr float KmaxdEdxMIP{60.0f}; static constexpr float KmindEdxMIPPlateau{70.0f}; static constexpr float KmaxdEdxMIPPlateau{90.0f}; - static constexpr float KminFT0A{3.5f}; - static constexpr float KmaxFT0A{4.9f}; - static constexpr float KminFT0C{-3.3f}; - static constexpr float KmaxFT0C{-2.1f}; static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; Configurable> lifetimecut{"lifetimecut", {DefaultLifetimeCuts[0], 2, {"lifetimecutLambda", "lifetimecutK0S"}}, "lifetimecut"}; @@ -499,88 +495,9 @@ struct PiKpRAA { } } - if (doprocessSim) { - registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{5, 0, 5}}); - registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); - registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); - registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Pion", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Kaon", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Proton", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Electron", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("EtaMCParAllMCColl_Rest", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); - registry.add("dcaVsPtPiDec", "Secondary pions from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPrDec", "Secondary protons from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPiMat", "Secondary pions from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("dcaVsPtPrMat", "Secondary protons from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - registry.add("DCAxyVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); - registry.add("DCAzVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); - - registry.add("CentralityVsBCVsFT0VsTVXVsEvSel", "All=1 | BC=2 | FT0=3 | TVX=4 | EvSel=5;;Status;", kTH2F, {{axisCent}, {5, 0.5, 5.5}}); - registry.add("NumOfRecColl", "Num. of times a MC evt. is reconstructed;N;Entries", kTH1F, {{5, -0.5, 4.5}}); - registry.add("NumOfRecCollVsNContri", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{5, -0.5, 4.5}, axisNch}); - // registry.add("NumOfRecCollVsNContriWithEvtSel", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{5, -0.5, 4.5}, axisNch}); - - // Pt resolution - registry.add("PtResolution", "p_{T} resolution;;(pt_{rec} - pt_{gen})/pt_{gen};", kTH2F, {axisPt, {100, -1.0, 1.0}}); - - // Needed to calculate the numerator of the Acceptance X Efficiency - registry.add("PtPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - - registry.add("PtGenPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtGenKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtGenPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - - // Needed to calculate the denominator of the Acceptance X Efficiency - registry.add("PtPiVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtKaVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - registry.add("PtPrVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - - // Needed for the Gen. Nch to Centrality conversion - registry.add("NchMCVsCent", "Generated Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); - - // Needed to measure Event Loss - registry.add("NchMC_WithRecoEvt", "MC Nch from the event associated with the Rec. Coll. (with largest Num. of PV contributors) + Evt. Sel.;Gen Nch MC;Entries", kTH1F, {axisNch}); - registry.add("NchMC_AllGen", "MC Nch of ALL events;Gen. Nch;Entries", kTH1F, {axisNch}); - registry.add("NchMC_WithRecColl", "MC Nch from the event associated with at least one Rec. Coll.;Gen. Nch;Entries", kTH1F, {axisNch}); - registry.add("NchMC_BestCollIdx", "MC Nch from the event associated with ONLY ONE Rec. Coll. with largest Num. of PV contributors;Gen. Nch;Entries", kTH1F, {axisNch}); - - // Needed to measure Event Splitting - // registry.add("Centrality_WRecoEvt", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_WRecoEvtWSelCri", "From the association between the MC event & the Rec. Coll with largest number of PV contributors + Evt. Sel.;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); - - // Needed to calculate the numerator of the Signal Loss correction - registry.add("PtPiVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // Needed to calculate the denominator of the Signal Loss correction - registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - registry.add("MCclosure_PtMCPiVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtMCKaVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtMCPrVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - registry.add("MCclosure_PtPiVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtKaVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - registry.add("MCclosure_PtPrVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - - auto hMcCouter = registry.get(HIST("EventCounterMC")); - auto* xMcCounter = hMcCouter->GetXaxis(); - xMcCounter->SetBinLabel(1, "All MC Coll"); - xMcCounter->SetBinLabel(2, "Rec Coll w/ nContributors > 0"); - xMcCounter->SetBinLabel(3, "Rec Coll W/ nContributors > 0 + HasFT0"); - xMcCounter->SetBinLabel(4, "Rec Coll W/ nContributors > 0 + HasFT0 + HasTVX"); - } - if (doprocessSignalLoss) { registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{5, 0, 5}}); - registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evy. Sel.;;Entries;", kTH1F, {axisZpos}); + registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); registry.add("EtaMCParAllMCColl_Pion", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); @@ -618,18 +535,20 @@ struct PiKpRAA { registry.add("PtPrVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); // FOR MAPPING BETWEN MC NCH AND CENTRALITY. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("NchMCVsCent", "Gen Nch vs Cent (Rec. Coll. W/Largest Contributors + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + registry.add("NchMCVsCent_WithRecoEvt_BestCollIdx", "Gen Nch vs Cent (Evt. Sel. + W/Largest N PV Contributors);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); // FOR MAPPING BETWEN MC NCH AND CENTRALITY. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("NchMCVsCent_AllRecoEvt", "Gen Nch vs Cent (All Rec. Coll. + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + registry.add("NchMCVsCent_WithRecoEvt", "Gen Nch vs Cent (All Rec. Coll. + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); // DENOMINATOR OF THE EVENT LOSS. ALL GEN EVENTS registry.add("NchMC_AllGen", "MC Nch of ALL events;Gen. Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("NchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;Gen Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;Gen Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("NchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch;Entries", kTH1F, {axisNch}); - // MC NCH DISTRIBUTION. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV & NO EVT SEL. (QA PURPOSES) - registry.add("NchMC_BestCollIdx", "MC Nch from the event associated with ONLY ONE Rec. Coll. with largest Num. of PV contributors;Gen. Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch;Entries", kTH1F, {axisNch}); + // DENOMINATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES WITH FT0 INFO + TVX-TRIGGERED + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); // MC NCH DISTRIBUTION ASSOCIATED TO ALL THE RECONSTRUCTED COLLISIONS & NO EVT. SEL. (QA PURPOSES) registry.add("NchMC_WithRecColl", "MC Nch from the event associated with at least one Rec. Coll.;Gen. Nch;Entries", kTH1F, {axisNch}); @@ -639,30 +558,20 @@ struct PiKpRAA { registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV - registry.add("PtPiVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_WithRecoEvt", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPiVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES - registry.add("PtPiVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_AllRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); // DENOMINATOR OF THE SIGNAL LOSS registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // DENOMINATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) - registry.add("PtPiVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_HasFT0", "Gen Evts W/at leas One Rec. Coll + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // DENOMINATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV (A LA PI0) - registry.add("PtPiVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtKaVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - registry.add("PtPrVsNchMC_HasFT0_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) registry.add("PtPiVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtKaVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); @@ -673,14 +582,6 @@ struct PiKpRAA { registry.add("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCPiVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCKaVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtMCPrVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch;", kTH2F, {axisPt, axisNch}); - - // registry.add("MCclosure_PtPiVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtKaVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - // registry.add("MCclosure_PtPrVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {axisPt, axisNch}); - auto hMcCouter = registry.get(HIST("EventCounterMC")); auto* xMcCounter = hMcCouter->GetXaxis(); xMcCounter->SetBinLabel(1, "All MC Coll"); @@ -1351,7 +1252,7 @@ struct PiKpRAA { Preslice perCollision = aod::track::collisionId; Service pdg; - void processSim(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) + void processSignalLoss(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) { const auto& kLowEta = (std::vector)vecLowEta; @@ -1359,13 +1260,10 @@ struct PiKpRAA { registry.fill(HIST("EventCounterMC"), 0.5); //--------------------------- - // Nch counters in different pseudorapidity intervals + // Nch COUNTER IN THE TPC ACCEPTANCE + // ONLY ETA SELECTION IS IMPOSED //--------------------------- - int nChMC{0}; int nChMCTPCAcc{0}; - int nChFT0A{0}; - int nChFT0C{0}; - for (const auto& particle : mcParticles) { auto charge{0.}; @@ -1418,68 +1316,26 @@ struct PiKpRAA { registry.fill(HIST("EtaMCParAllMCColl_Rest"), eta); } - // TVX requirement - if (eta > KminFT0A && eta < KmaxFT0A) { - nChFT0A++; - } - - if (eta > KminFT0C && eta < KmaxFT0C) { - nChFT0C++; - } - if (std::abs(eta) < tpcNchAcceptance) { nChMCTPCAcc++; } - - // INEL > 0 - if (std::abs(eta) > Kone) { - continue; - } - - nChMC++; - } - - //--------------------------- - // Selects MC events with at least one charged particle in the FT0A & FT0C - //--------------------------- - if (selTVXMC) { - if (!(nChFT0A > KzeroInt && nChFT0C > KzeroInt)) { - return; - } } //--------------------------- - // Selects MC events with |Zvtx| < zVtx_Cut + // SELECTS MC EVENTS WITH |ZVTX| < ZVTX_CUT //--------------------------- if (isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > posZcut)) { return; } //--------------------------- - // Selects INEL > 0 MC evets - //--------------------------- - if (selINELgt0) { - if (!(nChMC > KzeroInt)) { - return; - } - } - - //--------------------------- - // All Generated events irrespective of whether there is an associated reconstructed collision - // Consequently, the centrality being a reconstructed quantity, is not always be available - // Therefore it is expressed as a function of the generated pT and the generated Nch in ∣eta∣ < 0.8 - // This is used for the denominator of the SIGNAL LOSS correction - // Also for MC closure: True Pt vs Generated Nch + // DENOMINATOR OF THE SIGNAL LOSS: ALL MC EVENTS //--------------------------- for (const auto& particle : mcParticles) { if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { continue; } - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) { - continue; - } - auto charge{0.}; // Get the MC particle auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); @@ -1516,127 +1372,202 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCPiVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCKaVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); - registry.fill(HIST("MCclosure_PtMCPrVsNchMC"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS } else { continue; } } // Loop over Generated Particles //--------------------------- - // This is used for the denominator of the EVENT LOSS correction - // Only charge particles within tpcNchAcceptance and without pT selection + // nChMCTPCAc = CHARGEd PARTICLES WITHIN THE TPC ACCEPTANCE AND WITHOUT PT SELECTION //--------------------------- + + // DENOMINATOR OF THE EVENT LOSS: ALL THE GENERATED EVENTS registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); registry.fill(HIST("zPosMCAll"), mccollision.posZ()); //--------------------------- - // How many times was the MC event reconstrued? + // HOW MANY TIMES WAS THE MC EVENT RECONSTRUED? //--------------------------- const auto& nRecColls{collisions.size()}; registry.fill(HIST("NumOfRecColl"), nRecColls); //--------------------------- - // Only Generated evets with at least one reconstrued collision + // MC EVENTS WITH AT LEAST ONE ASSOCIATED RECONSTRUCTED COLLISION //--------------------------- if (nRecColls > KzeroInt) { //--------------------------- - // Looks for the collision with the largest number of tracks contributing to the PV reconstruction - // The selected collision is identified by its global index (bestCollisionIndex) + // THIS COLLISIONS-LOOP GETS THE INDEX (bestCollisionIndex) OF THE RECONSTRUCTED COLLISION WITH THE LARGEST NUMBER OF PV CONTRIBUTORS + // NO EVENT SELECTIONS APPLIED //--------------------------- int biggestNContribs{-1}; int bestCollisionIndex{-1}; - for (const auto& collision : collisions) { + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; + } registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); + // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED + registry.fill(HIST("NumOfRecCollVsCentrality"), nRecColls, centrality); //--------------------------- - // Gets the index of the collision with the largest number of contributors + // GETS THE BESTCOLLISION INDEX //--------------------------- if (biggestNContribs < collision.numContrib()) { biggestNContribs = collision.numContrib(); bestCollisionIndex = collision.globalIndex(); } + const bool hasft0{collision.has_foundFT0()}; + const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; + + //--------------------------- + // ALL COLLISIONS WITH N CONTRIBUTORS > 0 + //--------------------------- + registry.fill(HIST("EventCounterMC"), 1.5); + + // NCH IN |ETA| < TPC_ACCEPTANCE + // BELONGING TO A MC EVENT, WHICH POSSIBLY WAS RECONSTRUCTED MULTIPLE TIMES + registry.fill(HIST("NchMC_WithRecColl"), nChMCTPCAcc); + + //--------------------------- + // COLLISIONS WITH FT0 INFO + //--------------------------- + if (hasft0) { + registry.fill(HIST("EventCounterMC"), 2.5); + } + + //--------------------------- + // TVX-triggered collision + //--------------------------- + if (hasft0 && hastvx) { + registry.fill(HIST("EventCounterMC"), 3.5); + } + } + + //--------------------------- + // EVENT SELECTIONS APPLIED: ONLY FT0-INFO AND TVX-TRIGGERED REQUIRED + //--------------------------- + for (const auto& collision : collisions) { + + bool isBestCollIndex{false}; + if (bestCollisionIndex == collision.globalIndex()) { + isBestCollIndex = true; + } + + bool hasbc{false}; + bool hasft0{false}; + bool hastvx{false}; + bool hasBcFT0TVX{false}; + + hasbc = collision.has_foundBC(); + hasft0 = collision.has_foundFT0(); + hastvx = collision.selection_bit(o2::aod::evsel::kIsTriggerTVX); + hasBcFT0TVX = hasbc && hasft0 && hastvx; + + if (hasBcFT0TVX) { + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX"), nChMCTPCAcc); + if (hasBcFT0TVX && bestCollisionIndex) { + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCTPCAcc); + } + } + + // TVX-triggered + bestCollisionIndex + if (hasBcFT0TVX && isBestCollIndex) { + registry.fill(HIST("EventCounterMC"), 4.5); + } + //--------------------------- - // This is only for QA + // LOOPING OVER THE MC EVENT //--------------------------- - int mcNchInTPCAcc{0}; for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; + } auto charge{0.}; - // Get the MC particle - const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + // GET THE MC PARTICLE + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); if (pdgParticle != nullptr) { charge = pdgParticle->Charge(); } else { continue; } - // Is it a charged particle? + // IS IT A CHARGED PARTICLE? if (std::abs(charge) < KminCharge) { continue; } - // Is it a primary particle? + // SELECTS PARTICLE BASED ON ITS CHARGE + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // SELECTS ONLY PRIMARY PARTICLES if (!particle.isPhysicalPrimary()) { continue; } - // Selects particles based on their vertex creation - // This is added to reject loopers + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION + // THIS IS ADDED TO REJECT LOOPERS const float vX{particle.vx()}; const float vY{particle.vy()}; if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { continue; } - const float eta{particle.eta()}; - if (std::abs(eta) < tpcNchAcceptance) { - mcNchInTPCAcc++; + // TO COMPUTE SIGNAL LOSS RELATIVE TO THE MC SIGNAL OF EVENTS WITH FT0 INFO + TVX-TRIGGERED + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + if (hasBcFT0TVX && isBestCollIndex) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + } + } + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + if (hasBcFT0TVX && isBestCollIndex) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + } + } + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + if (hasBcFT0TVX && isBestCollIndex) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + } + } + } else { + continue; } - } - - //--------------------------- - // All collisions with N contributors > 0 - //--------------------------- - if (collision.numContrib() > KzeroInt) { - registry.fill(HIST("EventCounterMC"), 1.5); - // To calculate the amount of charged-primary MC particles (|eta|<0.5) - // that belongs to a MC event that was reconstructed at least once - registry.fill(HIST("NchMC_WithRecColl"), mcNchInTPCAcc); - } - - //--------------------------- - // Records only one collision with the largest number of contributors - //--------------------------- - if (bestCollisionIndex == collision.globalIndex()) { - registry.fill(HIST("EventCounterMC"), 2.5); - // To calculate the amount of charged-primary MC particles (|eta|<0.5) - // that belongs to a MC event that was reconstructed - // and only from the with the largest number of contributors - registry.fill(HIST("NchMC_BestCollIdx"), mcNchInTPCAcc); - } - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("EventCounterMC"), 3.5); - } + } // LOOP OVER TRUE MC EVENTS } //--------------------------- - // Needed to calculate denominator of the EVENT SPLITTING correction - // MC Collisions that are reconstructed more than one will be stored here + // MC EVENTS RECONSTRUCTED MULTIPLE TIMES + // EVENT SELECTION APPLIED MANUALLY TO AVOID DOUBLE FILLING IN THE EVENT COUNTER + // *) DENOMINATOR OF THE EVENT SPLITTING + // *) NUMERATOR OF THE EVENT LOSS + // *) NUMERATOR OF SIGNAL LOSS //--------------------------- for (const auto& collision : collisions) { @@ -1651,33 +1582,28 @@ struct PiKpRAA { centrality = -999.0; } - //--------------------------- - // Event Selection - // Applied manually to avoid double filling the EventCounter - //--------------------------- - - // Has BC? + // HAS BC? if (selHasBC) { if (!collision.has_foundBC()) { continue; } } - // Has FT0 information? + // HAS FT0 INFORMATION? if (selHasFT0) { if (!collision.has_foundFT0()) { continue; } } - // Use sel8? + // USE SEL8? if (useSel8) { if (!collision.sel8()) { continue; } } - // kIsTriggerTVX + // KISTRIGGERTVX if (selTriggerTVX) { if (!collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { continue; @@ -1730,881 +1656,27 @@ struct PiKpRAA { } } - //--------------------------- - // Needed to calculate denominator of the EVENT SPLITTING correction - // MC Collisions that are reconstructed more than one will be stored here - //--------------------------- - registry.fill(HIST("Centrality_AllRecoEvt"), centrality); - // registry.fill(HIST("NumOfRecCollVsNContriWithEvtSel"), nRecColls, collision.numContrib()); - } - - //--------------------------- - // Loop over the reconstructed collisions - // Only that one with the largest number of contributors is considered - //--------------------------- - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - //--------------------------- - // Pick the collisions with the largest number of contributors - //--------------------------- - if (bestCollisionIndex != collision.globalIndex()) { - continue; - } + registry.fill(HIST("NchMCVsCent_WithRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION - // Needed to load the Phi selection from the CCDB - const auto& foundBC = collision.foundBC_as(); - uint64_t timeStamp{foundBC.timestamp()}; - // const int magField{getMagneticField(timeStamp)}; + for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; + } - if (trackSelections.applyPhiCut) { - const int nextRunNumber{foundBC.runNumber()}; - if (currentRunNumberPhiSel != nextRunNumber) { - loadPhiCutSelections(timeStamp); - currentRunNumberPhiSel = nextRunNumber; - LOG(info) << "\tcurrentRunNumberPhiSel= " << currentRunNumberPhiSel << " timeStamp = " << timeStamp; + auto charge{0.}; + // Get the MC particle + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle != nullptr) { + charge = pdgParticle->Charge(); + } else { + continue; } - // return if phi cut objects are nullptr - if (!(phiCut.hPhiCutHigh && phiCut.hPhiCutLow)) { - return; - } - } - - // registry.fill(HIST("Centrality_WRecoEvt"), centrality); - registry.fill(HIST("zPosMC"), mccollision.posZ()); - - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 1.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 1.0); - - if (collision.has_foundBC()) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 2.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 2.0); - } - - if (collision.has_foundBC() && collision.has_foundFT0()) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 3.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 3.0); - } - - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 4.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 4.0); - } - - //--------------------------- - // RCT Selection - //--------------------------- - if (requireGoodRct) { - // Checks if collisions passes RCT selection - const bool isFT0Bad{requireBCRct ? foundBC.rct_bit(kFT0Bad) : collision.rct_bit(kFT0Bad)}; - const bool isITSBad{requireBCRct ? foundBC.rct_bit(kITSBad) : collision.rct_bit(kITSBad)}; - const bool isITSLimAcc{requireBCRct ? foundBC.rct_bit(kITSLimAccMCRepr) : collision.rct_bit(kITSLimAccMCRepr)}; - const bool isTOFBad{requireBCRct ? foundBC.rct_bit(kTOFBad) : collision.rct_bit(kTOFBad)}; - const bool isTOFLimAcc{requireBCRct ? foundBC.rct_bit(kTOFLimAccMCRepr) : collision.rct_bit(kTOFLimAccMCRepr)}; - const bool isTPCTrackingBad{requireBCRct ? foundBC.rct_bit(kTPCBadTracking) : collision.rct_bit(kTPCBadTracking)}; - const bool isTPCPIDBad{requireBCRct ? foundBC.rct_bit(kTPCBadPID) : collision.rct_bit(kTPCBadPID)}; - const bool isTPCLimAcc{requireBCRct ? foundBC.rct_bit(kTPCLimAccMCRepr) : collision.rct_bit(kTPCLimAccMCRepr)}; - - registry.fill(HIST("CentralityVsRCTSel"), centrality, 1.0); - if (!isFT0Bad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 2.0); - } - if (!isITSBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 3.0); - } - if (!isITSLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 4.0); - } - if (!isTOFBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 5.0); - } - if (!isTOFLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 6.0); - } - if (!isTPCTrackingBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 7.0); - } - if (!isTPCPIDBad) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 8.0); - } - if (!isTPCLimAcc) { - registry.fill(HIST("CentralityVsRCTSel"), centrality, 9.0); - } - - registry.fill(HIST("RCTSel"), 1.0); - if (!rctChecker(collision)) { - return; - } - - registry.fill(HIST("RCTSel"), 2.0); - } - - //--------------------------- - // Event Selection - //--------------------------- - if (!isEventSelected(collision)) { - return; - } - - //--------------------------- - // Needed to construct the correlation between MC Nch v.s. centrality - //--------------------------- - registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // To calculate numerator of EVENT SPLITTING - registry.fill(HIST("NchMCVsCent"), centrality, nChMCTPCAcc); // Needed for the mapping betwee MC Nch and centrality - registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // Numerator of EVENT LOSS correction - registry.fill(HIST("zPos"), collision.posZ()); - registry.fill(HIST("Centrality"), centrality); - - //--------------------------- - // has_foundFT0() ? - //--------------------------- - - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("CentralityVsBCVsFT0VsTVXVsEvSel"), centrality, 5.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 5.0); - } - - if (collision.has_foundFT0()) { - registry.fill(HIST("CentralityVsFoundFT0"), centrality, 1.5); - } else { - registry.fill(HIST("CentralityVsFoundFT0"), centrality, 0.5); - } - - //--------------------------- - // All Generated events with at least one associated reconstructed collision - // The Generated events are not subjected to any selection criteria - // However, the associated reconstructed collisions pass the selection criteria - // This histograms are used for the denominator of the tracking efficiency - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) { - continue; - } - - auto charge{0.}; - // Get the MC particle - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // Denominator of tracking efficiency - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // Numerator of signal loss - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); - } else { - continue; - } - } // Loop over generated particles per generated collision - - const auto& groupedTracks{tracksMC.sliceBy(perCollision, collision.globalIndex())}; - - // ================ - // Track selection WITHOUT DCAxy and DCAz selections - // Needed for the Secondary Particle Correction - // ================ - for (const auto& track : groupedTracks) { - - const bool applyDca{false}; - if (!selectPrimary(track, applyDca)) { - continue; - } - - if (!track.has_mcParticle()) { - continue; - } - - // Get the MC particle - const auto& particle{track.mcParticle()}; - auto charge{0.}; - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - registry.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - registry.fill(HIST("DCAzVsPt"), track.pt(), track.dcaZ()); - - // float phiPrime{track.phi()}; - // phiPrimeFunc(phiPrime, magField, charge); - - bool isPrimary{false}; - bool isDecay{false}; - bool isMaterial{false}; - if (particle.isPhysicalPrimary()) { - isPrimary = true; - } else if (particle.getProcess() == TMCProcess::kPDecay) { - isDecay = true; - } else { - isMaterial = true; - } - - bool isPi{false}; - bool isPr{false}; - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - isPi = true; - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - isPr = true; - } else { - continue; - } - - if (isPrimary && !isDecay && !isMaterial) { - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPi"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPr"), track.pt(), track.dcaXY(), centrality); - } - } - - if (isDecay && !isPrimary && !isMaterial) { - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPiDec"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPrDec"), track.pt(), track.dcaXY(), centrality); - } - } - - if (isMaterial && !isPrimary && !isDecay) { - if (isPi && !isPr) { - registry.fill(HIST("dcaVsPtPiMat"), track.pt(), track.dcaXY(), centrality); - } - if (isPr && !isPi) { - registry.fill(HIST("dcaVsPtPrMat"), track.pt(), track.dcaXY(), centrality); - } - } - } - - // ================ - // Track selection WITH DCAxy and DCAz selections - // Needed for the numerator in the tracking efficiency - // ================ - int nCh{0}; - for (const auto& track : groupedTracks) { - - const bool applyDca{true}; - if (!selectPrimary(track, applyDca)) { - continue; - } - - // Has MC particle? - if (!track.has_mcParticle()) { - continue; - } - - // Get the MC particle - const auto& particle{track.mcParticle()}; - auto charge{0.}; - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // float phiPrime{track.phi()}; - // phiPrimeFunc(phiPrime, magField, charge); - - int indexEta{-999}; - const float eta{track.eta()}; - for (int i = 0; i < KnEtaHists; ++i) { - if (eta >= kLowEta[i] && eta < kHighEta[i]) { - indexEta = i; - break; - } - } - - if (indexEta < KzeroInt || indexEta > KsevenInt) { - continue; - } - - nCh++; - - registry.fill(HIST("MomentumTPCVsP"), track.p(), track.tpcInnerParam()); - registry.fill(HIST("DCAxyVsPtWithSelection"), track.pt(), track.dcaXY()); - registry.fill(HIST("DCAzVsPtWithSelection"), track.pt(), track.dcaZ()); - registry.fill(HIST("EtaVsPhi"), track.eta(), track.phi()); - registry.fill(HIST("NclVsPhiHist"), track.phi(), track.tpcNClsFound()); - registry.fill(HIST("NclVsPhiProfile"), track.phi(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEta"), track.eta(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEtap"), track.eta(), track.tpcNClsFound()); - registry.fill(HIST("NclVsEtaPID"), track.eta(), track.tpcNClsPID()); - registry.fill(HIST("NclVsEtaPIDp"), track.eta(), track.tpcNClsPID()); - - // Rejects non-physical primary particle - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - bool isPi{false}; - bool isKa{false}; - bool isPr{false}; - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - isPi = true; - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - isKa = true; - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - isPr = true; - } else { - continue; - } - - if (isPi && !isKa && !isPr) { - registry.fill(HIST("PtPiVsCent_WithRecoEvt"), track.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("PtGenPiVsCent_WithRecoEvt"), particle.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("MCclosure_PtPiVsNchMC"), track.pt(), nChMCTPCAcc); - } - if (isKa && !isPi && !isPr) { - registry.fill(HIST("PtKaVsCent_WithRecoEvt"), track.pt(), centrality); - registry.fill(HIST("PtGenKaVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtKaVsNchMC"), track.pt(), nChMCTPCAcc); - } - if (isPr && !isPi && !isKa) { - registry.fill(HIST("PtPrVsCent_WithRecoEvt"), track.pt(), centrality); - registry.fill(HIST("PtGenPrVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtPrVsNchMC"), track.pt(), nChMCTPCAcc); - } - registry.fill(HIST("PtResolution"), particle.pt(), (track.pt() - particle.pt()) / particle.pt()); - } // Loop over reconstructed tracks - registry.fill(HIST("NchVsCent"), centrality, nCh); - } // Loop over Reco. Collisions: Only the collisions with the largest number of contributors - } // If condition: Only simulated evets with at least one reconstrued collision - } - PROCESS_SWITCH(PiKpRAA, processSim, "Process Sim", false); - - void processSignalLoss(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) - { - - const auto& kLowEta = (std::vector)vecLowEta; - const auto& kHighEta = (std::vector)vecUpEta; - registry.fill(HIST("EventCounterMC"), 0.5); - - //--------------------------- - // Nch COUNTER IN THE TPC ACCEPTANCE - // ONLY ETA SELECTION IS IMPOSED - //--------------------------- - int nChMCTPCAcc{0}; - for (const auto& particle : mcParticles) { - - auto charge{0.}; - // Get the MC particle - const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Select particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - const float eta{particle.eta()}; - registry.fill(HIST("EtaMCParAllMCColl"), eta); - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("EtaMCParAllMCColl_Pion"), eta); - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("EtaMCParAllMCColl_Kaon"), eta); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("EtaMCParAllMCColl_Proton"), eta); - } else if (particle.pdgCode() == PDG_t::kElectron || particle.pdgCode() == PDG_t::kPositron) { - registry.fill(HIST("EtaMCParAllMCColl_Electron"), eta); - } else { - registry.fill(HIST("EtaMCParAllMCColl_Rest"), eta); - } - - if (std::abs(eta) < tpcNchAcceptance) { - nChMCTPCAcc++; - } - } - - //--------------------------- - // SELECTS MC EVENTS WITH |ZVTX| < ZVTX_CUT - //--------------------------- - if (isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > posZcut)) { - return; - } - - //--------------------------- - // *) DENOMINATOR OF THE SIGNAL LOSS - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - auto charge{0.}; - // Get the MC particle - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; - } - - // Selects particle based on its charge - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // Selects only primary particles - if (!particle.isPhysicalPrimary()) { - continue; - } - - // Selects particles based on their vertex creation - // This is added to reject loopers - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS - } else { - continue; - } - } // Loop over Generated Particles - - //--------------------------- - // nChMCTPCAc = CHARGEd PARTICLES WITHIN THE TPC ACCEPTANCE AND WITHOUT PT SELECTION - //--------------------------- - registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); // DENOMINATOR OF THE EVENT LOSS - registry.fill(HIST("zPosMCAll"), mccollision.posZ()); - - //--------------------------- - // HOW MANY TIMES WAS THE MC EVENT RECONSTRUED? - //--------------------------- - const auto& nRecColls{collisions.size()}; - registry.fill(HIST("NumOfRecColl"), nRecColls); - - //--------------------------- - // MC EVENTS WITH AT LEAST ONE ASSOCIATED RECONSTRUCTED COLLISION - //--------------------------- - if (nRecColls > KzeroInt) { - - //--------------------------- - // THIS COLLISIONS LOOP ONLY FINDS THE ONE WITH THE LARGEST NUMBER OF CONTRIBUTORS - // IT IS IDENTIFIED BY ITS GLOBAL INDEX (bestCollisionIndex) - // NO EVENT SELECTIONS APPLIED - //--------------------------- - int biggestNContribs{-1}; - int bestCollisionIndex{-1}; - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); - registry.fill(HIST("NumOfRecCollVsCentrality"), nRecColls, centrality); // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED - - //--------------------------- - // GETS THE BESTCOLLISIONINDEX - //--------------------------- - if (biggestNContribs < collision.numContrib()) { - biggestNContribs = collision.numContrib(); - bestCollisionIndex = collision.globalIndex(); - } - - const bool hasft0{collision.has_foundFT0()}; - const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; - - //--------------------------- - // ALL COLLISIONS WITH N CONTRIBUTORS > 0 - //--------------------------- - registry.fill(HIST("EventCounterMC"), 1.5); - - // NCH IN |ETA| < TPC_ACCEPTANCE - // BELONGING TO A MC EVENT, WHICH POSSIBLY WAS RECONSTRUCTED MULTIPLE TIMES - registry.fill(HIST("NchMC_WithRecColl"), nChMCTPCAcc); - - //--------------------------- - // COLLISIONS WITH FT0 INFO - //--------------------------- - if (hasft0) { - registry.fill(HIST("EventCounterMC"), 2.5); - } - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (hasft0 && hastvx) { - registry.fill(HIST("EventCounterMC"), 3.5); - } - } - - //--------------------------- - // NO EVENT SELECTIONS APPLIED: ONLY FT0-INFO AND TVX-TRIGGERED REQUIRED - //--------------------------- - for (const auto& collision : collisions) { - - bool isBestCollIndex{false}; - if (bestCollisionIndex == collision.globalIndex()) { - isBestCollIndex = true; - } - - const bool hasft0{collision.has_foundFT0()}; - const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; - - //--------------------------- - // TVX-triggered collision - //--------------------------- - if (hasft0 && hastvx && isBestCollIndex) { - registry.fill(HIST("EventCounterMC"), 4.5); - } - - // TO CALCULATE NCH IN |ETA| < TPC_ACCEPTANCE - // BELONGING TO A MC EVENT ASSOCIATED WITH - // THE ONLY COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS - if (isBestCollIndex) { - registry.fill(HIST("NchMC_BestCollIdx"), nChMCTPCAcc); - } - - //--------------------------- - // LOOPING OVER THE MC TRUTH EVENT - //--------------------------- - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - auto charge{0.}; - // GET THE MC PARTICLE - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // IS IT A CHARGED PARTICLE? - if (std::abs(charge) < KminCharge) { - continue; - } - - // SELECTS PARTICLE BASED ON ITS CHARGE - if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { - continue; - } - if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { - continue; - } - - // SELECTS ONLY PRIMARY PARTICLES - if (!particle.isPhysicalPrimary()) { - continue; - } - - // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION - // THIS IS ADDED TO REJECT LOOPERS - const float vX{particle.vx()}; - const float vY{particle.vy()}; - if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { - continue; - } - - // TO COMPUTE SIGNAL LOSS AS THE RATIO BETWEEN MC EVENTS WITH FT0 INFO - // AND THOSE WITH FT0 INFO AND TVX-TRIGGERED - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - if (hasft0) { - registry.fill(HIST("PtPiVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPiVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - if (hasft0) { - registry.fill(HIST("PtKaVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtKaVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - if (hasft0) { - registry.fill(HIST("PtPrVsNchMC_HasFT0"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPrVsNchMC_HasFT0_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - if (hasft0 && hastvx) { - registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); - if (isBestCollIndex) { - registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); - } - } - } else { - continue; - } - } // LOOP OVER TRUE MC EVENTS - } - - //--------------------------- - // MC EVENTS RECONSTRUCTED MULTIPLE TIMES ARE STORED HERE - // EVENT SELECTION APPLIED MANUALLY TO AVOID DOUBLE FILLING IN THE EVENTCOUNTER - // *) DENOMINATOR OF THE EVENT SPLITTING - // *) NUMERATOR OF THE EVENT LOSS - // *) NUMERATOR OF SIGNAL LOSS - //--------------------------- - for (const auto& collision : collisions) { - - float centrality{-999.0}; - if (centralitySelector.value == "FT0C") { - centrality = collision.centFT0C(); - } else if (centralitySelector.value == "FT0M") { - centrality = collision.centFT0M(); - } else if (centralitySelector.value == "FV0A") { - centrality = collision.centFV0A(); - } else { - centrality = -999.0; - } - - // HAS BC? - if (selHasBC) { - if (!collision.has_foundBC()) { - continue; - } - } - - // HAS FT0 INFORMATION? - if (selHasFT0) { - if (!collision.has_foundFT0()) { - continue; - } - } - - // USE SEL8? - if (useSel8) { - if (!collision.sel8()) { - continue; - } - } - - // KISTRIGGERTVX - if (selTriggerTVX) { - if (!collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - continue; - } - } - - // kNoITSROFrameBorder - if (selNoITSROFrameBorder) { - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - continue; - } - } - - // kNoTimeFrameBorder - if (selNoTimeFrameBorder) { - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - continue; - } - } - - // Zvtx - if (isZvtxPosSel) { - if (std::fabs(collision.posZ()) > posZcut) { - continue; - } - } - - // Short distance between FT0 & PV vertices - if (selIsGoodZvtxFT0vsPV) { - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - continue; - } - } - - if (selNoSameBunchPileup) { - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - continue; - } - } - - if (isCentSel) { - if (centrality < minCentCut || centrality > maxCentCut) { - continue; - } - } - - if (selINELgt0) { - if (!collision.isInelGt0()) { - continue; - } - } - - registry.fill(HIST("NchMCVsCent_AllRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY - registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING - registry.fill(HIST("NchMC_AllRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION - - for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { - continue; - } - - auto charge{0.}; - // Get the MC particle - auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } - - // Is it a charged particle? - if (std::abs(charge) < KminCharge) { - continue; + // Is it a charged particle? + if (std::abs(charge) < KminCharge) { + continue; } // Select particle based on its charge @@ -2629,11 +1701,11 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS } else { continue; } @@ -2670,7 +1742,6 @@ struct PiKpRAA { } const auto& foundBC = collision.foundBC_as(); - registry.fill(HIST("zPosMC"), mccollision.posZ()); //--------------------------- @@ -2731,11 +1802,11 @@ struct PiKpRAA { //--------------------------- // NEEDED TO CONSTRUCT THE CORRELATION BETWEEN MC NCH V.S. CENTRALITY //--------------------------- - registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // NUMERATOR OF EVENT SPLITTING - registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION - registry.fill(HIST("NchMCVsCent"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY - registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION - registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // NUMERATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt_BestCollIdx"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("NchMCVsCent_WithRecoEvt_BestCollIdx"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION //--------------------------- // THE GENERATED EVENTS ARE NOT SUBJECTED TO ANY SELECTION CRITERIA @@ -2783,14 +1854,14 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); } else { continue; } From bc5d5274931de31c306ff29b446fdfea0cd759f3 Mon Sep 17 00:00:00 2001 From: Jiabin Geng <997406694@qq.com> Date: Sat, 17 Oct 2026 01:08:48 +0800 Subject: [PATCH 316/648] [PWGLF] Add an additional axis for hTrigger to the ClosureTest in hStrangeCorrelation.cxx (#18330) --- PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index cedfad14ba0..8a1390ca80b 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -3300,7 +3300,7 @@ struct HStrangeCorrelation { histos.add(fmt::format("ClosureTest/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); } } - histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult, axesConfigurations.axisPhi}); if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { histos.add("ClosureTest/TriggerWithRecoTrackMatch/hTrigger", "Truth triggers with at least one reconstructed track with a matching MC label;#it{p}_{T}^{truth} (GeV/c);#eta^{truth};centrality (%)", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); } @@ -6561,7 +6561,7 @@ struct HStrangeCorrelation { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { triggerIndices.emplace_back(iteratorNum); - histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile); + histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile, gphi); if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); } From 9eae0c656f565b98beb2b209df0b74600f50e458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrea=20Tavira=20Garc=C3=ADa?= <118979672+atavirag@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:11:31 +0200 Subject: [PATCH 317/648] [PWGHF] Cleanup of the correlatorDMesonPairs (#18313) Co-authored-by: ALICE Action Bot --- PWGHF/HFC/DataModel/DMesonPairsTables.h | 17 - .../TableProducer/correlatorDMesonPairs.cxx | 956 +++++++----------- 2 files changed, 347 insertions(+), 626 deletions(-) diff --git a/PWGHF/HFC/DataModel/DMesonPairsTables.h b/PWGHF/HFC/DataModel/DMesonPairsTables.h index 65f76dc586e..a5dccbaf0aa 100644 --- a/PWGHF/HFC/DataModel/DMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/DMesonPairsTables.h @@ -113,15 +113,6 @@ DECLARE_SOA_COLUMN(PoolBinD, poolBinD, int); //! Pool bin of the D meson DECLARE_SOA_COLUMN(GIndexColD, gIndexColD, int); //! G-index column of the D meson DECLARE_SOA_COLUMN(TimestampD, timestampD, int64_t); //! Timestamp of the D meson -// Associated hadron -DECLARE_SOA_COLUMN(PtHad, ptHad, float); //! Transverse momentum of the associated hadron -DECLARE_SOA_COLUMN(YHad, yHad, float); //! Rapidity of the associated hadron -DECLARE_SOA_COLUMN(EtaHad, etaHad, float); //! Pseudorapidity of the associated hadron -DECLARE_SOA_COLUMN(PhiHad, phiHad, float); //! Azimuthal angle of the associated hadron -DECLARE_SOA_COLUMN(PoolBinHad, poolBinHad, int); //! Pool bin of the associated hadron -DECLARE_SOA_COLUMN(GIndexColHad, gIndexColHad, int); //! G-index column of the associated hadron -DECLARE_SOA_COLUMN(TimestampHad, timestampHad, int64_t); //! Timestamp of the associated hadron - } // namespace hf_correlation_d_meson_had // Definition of the D meson table for D-had correlations. Contains the info needed at Data level. @@ -139,14 +130,6 @@ DECLARE_SOA_COLUMN(TimestampHad, timestampHad, int64_t); //! Timestamp of the as DECLARE_DMESON_ME_TABLE(DMesonInfoCand1, 1, "DMESCAND1"); //! D0 cand 1 info for event mixing DECLARE_DMESON_ME_TABLE(DMesonInfoCand2, 2, "DMESCAND2"); //! D0 cand 2 info for event mixing -DECLARE_SOA_TABLE(AssocHadInfo, "AOD", "ASSOCHADINFO", - hf_correlation_d_meson_had::PtHad, - hf_correlation_d_meson_had::EtaHad, - hf_correlation_d_meson_had::PhiHad, - hf_correlation_d_meson_had::PoolBinHad, - hf_correlation_d_meson_had::GIndexColHad, - hf_correlation_d_meson_had::TimestampHad); - } // namespace o2::aod #endif // PWGHF_HFC_DATAMODEL_DMESONPAIRSTABLES_H_ diff --git a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx index 76c9767de64..b6d40c9e764 100644 --- a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx @@ -21,7 +21,6 @@ #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/DataModel/DMesonPairsTables.h" @@ -41,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include using namespace o2; @@ -66,6 +68,8 @@ using namespace o2::framework::expressions; namespace { +using DecayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain; + enum CandidateType { SelectedD = 0, // This particle is selected as a D SelectedDbar, // This particle is selected as a Dbar @@ -102,25 +106,21 @@ struct HfCorrelatorD0HadronsSelection { using CandidatesD0Data = soa::Join; Partition selectedD0Candidates = - aod::hf_sel_candidate_d0::isSelD0 >= 1 || - aod::hf_sel_candidate_d0::isSelD0bar >= 1; + aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; // Process function to select collisions with at least one D0 candidate passing the selection criteria void processD0SelectionData(SelCollisions::iterator const& collision, CandidatesD0Data const&) { - bool isSel8 = !useSel8 || collision.sel8(); - bool isNoSameBunchPileUp = !selNoSameBunchPileUpColl || - collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bool const isSel8 = !useSel8 || collision.sel8(); + bool const isNoSameBunchPileUp = !selNoSameBunchPileUpColl || + collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); bool isD0Found = !doSelD0Collision; if (doSelD0Collision) { auto grouped = selectedD0Candidates.sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); for (const auto& candidate : grouped) { - - if (candidate.isSelD0() < selectionFlagD0 && candidate.isSelD0bar() < selectionFlagD0bar) { - continue; - } if (std::abs(HfHelper::yD0(candidate)) > yCandMax || candidate.pt() < ptCandMin) { continue; } @@ -145,11 +145,9 @@ struct HfCorrelatorDMesonPairs { // Tables for event mixing Produces entryDMesonInfoCand1; Produces entryDMesonInfoCand2; - Produces entryAssocHad; Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; - Configurable selectionFlagHf{"selectionFlagHf", 1, "Selection Flag for HF flagged candidates"}; Configurable yCandMax{"yCandMax", 0.8, "maximum |y| of D0 candidates"}; Configurable ptCandMin{"ptCandMin", -1., "minimum pT of D0 candidates"}; Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots"}; @@ -157,18 +155,7 @@ struct HfCorrelatorDMesonPairs { Configurable massCut{"massCut", 0.05, "Maximum deviation from PDG peak allowed for signal region"}; Configurable removeAmbiguous{"removeAmbiguous", false, "Flag to remove ambiguous candidates"}; Configurable ptMaxRemoveAmbiguous{"ptMaxRemoveAmbiguous", 5.0, "Max. pT to remove the ambiguous candidates"}; - - // Event mixing configurables Configurable applyMixedEvent{"applyMixedEvent", false, "Flag to make the event mixing"}; - Configurable daughterTracksCutFlag{"daughterTracksCutFlag", false, "Flag to add cut on daughter tracks"}; - Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. eta of tracks"}; - Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. DCA_xy of tracks"}; - Configurable dcaZTrackMax{"dcaZTrackMax", 1., "max. DCA_z of tracks"}; - Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; - Configurable ptTrackMax{"ptTrackMax", 100., "max. track pT"}; - Configurable multMin{"multMin", 0., "minimum multiplicity accepted"}; - Configurable multMax{"multMax", 10000., "maximum multiplicity accepted"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; @@ -187,19 +174,16 @@ struct HfCorrelatorDMesonPairs { SliceCache cache; + // Table aliases. The partitions below must have exactly the same type as the + // candidate table taken by the process functions, otherwise they are never bound. using SelCollisionsWithD0 = soa::Filtered>; - using CandidatesD0Data = soa::Join; - - using FilteredD0Candidates = soa::Filtered; - using TracksData = soa::Filtered>; // trackFilter applied + using CandidatesD0Data = soa::Join; + using CandidatesD0McRec = soa::Join; Filter collisionFilter = aod::hf_selection_dmeson_collision::dmesonSel == true; - Filter d0Filter = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_2prong::DecayType::D0ToPiK))) != static_cast(0); - Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (nabs(aod::track::pt) > ptTrackMin) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); - Preslice perCol2Prong = aod::hf_cand::collisionId; - Preslice candsD0PerCollision = aod::hf_cand::collisionId; - Preslice trackIndicesPerCollision = aod::track::collisionId; + Partition selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; o2::analysis::HfMlResponseD0ToKPi hfMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -210,11 +194,6 @@ struct HfCorrelatorDMesonPairs { std::vector outputMlD0Cand2; std::vector outputMlD0barCand2; - // using TracksWPid = soa::Join; - - Partition> selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; - Partition> selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isRecoHfFlag >= selectionFlagHf; - // ThnSparse for ML outputScores and Vars ConfigurableAxis thnConfigAxisBkgScore{"thnConfigAxisBkgScore", {100, 0, 1}, "Bkg score bins"}; ConfigurableAxis thnConfigAxisSignalScore{"thnConfigAxisSignalScore", {100, 0, 1}, "Signal score bins"}; @@ -235,8 +214,6 @@ struct HfCorrelatorDMesonPairs { ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; - ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; - ConfigurableAxis binsDcaXY{"binsDcaXY", {128, -0.2, 0.2}, "DCA xy"}; std::unique_ptr corrBinning; HistogramRegistry registry{ @@ -256,7 +233,7 @@ struct HfCorrelatorDMesonPairs { {"hPhiMcGen", "D meson candidates MC Gen;candidate #it{#varphi};entries", hTH1Phi}, {"hPtVsYVsNContribMcGen", "D meson candidates MC Gen;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hPtVsYVsNContribMcGenPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, - {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, + {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Non-prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hNContribMcGen", "D meson candidates MC Gen;Number of PV contributors", hTH1NContrib}, // MC Rec plots {"hPtVsYVsNContribMcRec", "D meson candidates MC Rec;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, @@ -273,7 +250,6 @@ struct HfCorrelatorDMesonPairs { void init(InitContext&) { - if (applyMl) { hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); if (loadModelsFromCCDB) { @@ -382,6 +358,7 @@ struct HfCorrelatorDMesonPairs { registry.add("hInputCheckD0OrD0barMcGen", "Check on input D0 | D0bar meson candidates/event MC Gen", {HistType::kTH1F, {axisInputD0}}); registry.add("hMass", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMassSig", "MC-matched D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecNonPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecReflections", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -409,26 +386,93 @@ struct HfCorrelatorDMesonPairs { } // Event mixing - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; - AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; - AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; - AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; - AxisSpec const axisDcaXY = {binsDcaXY, "DCA xy"}; corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}}); if (applyMixedEvent) { - registry.add("hMultiplicityPreSelection", "multiplicity prior to selection;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); - registry.add("hMultiplicity", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); registry.add("hMultFT0M", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); registry.add("hZvtx", "z vertex;z vertex;entries", {HistType::kTH1F, {{200, -20., 20.}}}); registry.add("hD0Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); registry.add("hD0Cand2Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); - registry.add("hTracksBin", "Tracks selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); - registry.add("hDcaXYVsPt", "DCA xy vs pt", {HistType::kTH2F, {{axisDcaXY}, {axisPtHadron}}}); } } + /// ------- FUNCTION DEFINITIONS ------- + + /// Kinematic, signal-region and ambiguity selections of a candidate + /// \return true if the candidate passes the selections + template + bool passKinematicSelections(const T& candidate, const bool isDCand, const bool isDbarCand) + { + if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { + return false; + } + if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + return false; + } + bool const isSignalD0Cand = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; + bool const isSignalD0barCand = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; + if (selectSignalRegionOnly && !(isSignalD0Cand || isSignalD0barCand)) { + return false; + } + // Remove ambiguous D0 candidates if flag is true + if (removeAmbiguous && (isDCand && isDbarCand) && candidate.pt() < ptMaxRemoveAmbiguous) { + return false; + } + return true; + } + + /// ML selections of a candidate + /// \return true if the candidate is selected by the ML model (as D0 and/or D0bar) + template + bool passMlSelections(const T& candidate, const bool isDCand, const bool isDbarCand, std::vector& outputMlD0Cand, std::vector& outputMlD0barCand) + { + bool isSelectedMlD0Cand = false; + bool isSelectedMlD0barCand = false; + + if (isDCand) { + std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0); + isSelectedMlD0Cand = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate.pt(), outputMlD0Cand); + } + if (isDbarCand) { + std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0Bar); + isSelectedMlD0barCand = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate.pt(), outputMlD0barCand); + } + + return isSelectedMlD0Cand || isSelectedMlD0barCand; + } + + /// Rapidity and pT selections of gen-level particles + template + bool passGenSelections(const T& particle) + { + return std::abs(particle.y()) <= yCandMax && (ptCandMin < 0. || particle.pt() >= ptCandMin); + } + + /// Sets bits to select pair types. + /// \param candidate is candidate + /// \return bitmap with type of pair (DD, DbarDbar, DDbar, DbarD) + uint8_t setPairType(const uint8_t candidateType1, const uint8_t candidateType2) + { + uint8_t pairType(0); + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + + if (isDCand1 && isDCand2) { + SETBIT(pairType, DD); + } + if (isDbarCand1 && isDbarCand2) { + SETBIT(pairType, DbarDbar); + } + if (isDCand1 && isDbarCand2) { + SETBIT(pairType, DDbar); + } + if (isDbarCand1 && isDCand2) { + SETBIT(pairType, DbarD); + } + return pairType; + } + /// Sets bits to select candidate type for D0 /// SelectedD and SelectedDbar bits look at whether the candidate passed the selection flags. /// \param candidate is candidate @@ -444,10 +488,10 @@ struct HfCorrelatorDMesonPairs { SETBIT(candidateType, SelectedDbar); } if constexpr (IsMcRec) { - if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0 + if (candidate.flagMcMatchRec() == DecayChannel::D0ToPiK) { // matched as D0 SETBIT(candidateType, TrueD); } - if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0bar + if (candidate.flagMcMatchRec() == -DecayChannel::D0ToPiK) { // matched as D0bar SETBIT(candidateType, TrueDbar); } } @@ -471,37 +515,40 @@ struct HfCorrelatorDMesonPairs { } /// Check if the candidate is a D - /// \param candidate is candidate - /// \return true if is a D bool isD(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedD)); } /// Check if the candidate is a Dbar - /// \param candidate is candidate - /// \return true if is a Dbar bool isDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedDbar)); } /// Check if the candidate is a true D - /// \param candidate is candidate - /// \return true if is a true D bool isTrueD(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueD)); } /// Check if the candidate is a true Dbar - /// \param candidate is candidate - /// \return true if is a true Dbar bool isTrueDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueDbar)); } + /// Canonical pair ordering: D0 before D0bar; for equal flavours, higher pT first. + /// \return true if candidate 1 and candidate 2 should be exchanged + template + static bool shouldSwapOrder(const bool isD1, const bool isDbar1, const bool isD2, const bool isDbar2, const T& candidate1, const T& candidate2) + { + if (isD1 != isD2 || isDbar1 != isDbar2) { // different flavour content + return !isD1 && isD2; // Dbar(-only) first, D second -> swap + } + return candidate1.pt() < candidate2.pt(); + } + /// Fill PID related plots for D0 and D0bar /// \param candidate is candidate template @@ -526,51 +573,51 @@ struct HfCorrelatorDMesonPairs { } /// Fill counters for D0 and D0bar - /// \param d0Candidates contains all D0 candidates - template + /// \param d0Candidates contains the D0 candidates of the collision + template void getCountersPerEvent(const T& d0Candidates) { int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; for (const auto& candidate : d0Candidates) { // Get counters per event - bool const isSignalD0 = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; - bool const isSignalD0bar = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0 || isSignalD0bar)) { - continue; - } - auto candidateType1 = assignCandidateTypeD0(candidate); // Candidate type attribution - registry.fill(HIST("hPtCand"), candidate.pt()); - if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + auto const candidateType = assignCandidateTypeD0(candidate); // Candidate type attribution + bool const isDCand = isD(candidateType); + bool const isDbarCand = isDbar(candidateType); + bool const isTrueDCand = isTrueD(candidateType); + bool const isTrueDbarCand = isTrueDbar(candidateType); + + if (!passKinematicSelections(candidate, isDCand, isDbarCand)) { continue; } - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - if (isDCand1) { + if (isDCand) { nDevent++; } - if (isDbarCand1) { + if (isDbarCand) { nDbarevent++; } - if (isDCand1 && isDbarCand1) { + if (isDCand && isDbarCand) { nDDbarevent++; } - if (isDCand1 || isDbarCand1) { + if (isDCand || isDbarCand) { nDorDbarevent++; } // Fill selection status single particle registry.fill(HIST("hStatusSinglePart"), 1); - if (isDCand1 && !isDbarCand1) { + if (isDCand && !isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 2); - } else if (isDbarCand1 && !isDCand1) { + } else if (isDbarCand && !isDCand) { registry.fill(HIST("hStatusSinglePart"), 3); - } else if (isDCand1 && isDbarCand1) { + } else if (isDCand && isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 4); } + // fill true info (available only in MC) + if (isTrueDCand) { + registry.fill(HIST("hStatusSinglePart"), 5); + } else if (isTrueDbarCand) { + registry.fill(HIST("hStatusSinglePart"), 6); + } } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0"), nDevent); @@ -586,11 +633,12 @@ struct HfCorrelatorDMesonPairs { } } - /// Fill selection status histogram - void fillEntry(const bool& isDCand1, const bool& isDbarCand1, const bool& isDCand2, const bool& isDbarCand2, - const uint8_t candidateType1, const uint8_t candidateType2, float yCand1, float yCand2, float etaCand1, float etaCand2, float phiCand1, float phiCand2, - double ptCand1, double ptCand2, float massDCand1, float massDbarCand1, float massDCand2, float massDbarCand2) + void fillSelectionStatusHisto(const uint8_t candidateType1, const uint8_t candidateType2) { + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + bool const isTrueDCand1 = isTrueD(candidateType1), isTrueDCand2 = isTrueD(candidateType2); + bool const isTrueDbarCand1 = isTrueDbar(candidateType1), isTrueDbarCand2 = isTrueDbar(candidateType2); /// Fill information on the D candidates registry.fill(HIST("hSelectionStatus"), 2); @@ -612,168 +660,119 @@ struct HfCorrelatorDMesonPairs { } /// Collect information on the D pairs - uint8_t pairType(0); registry.fill(HIST("hSelectionStatus"), 1); if (isDCand1 && isDCand2) { - SETBIT(pairType, DD); registry.fill(HIST("hSelectionStatus"), 14); if ((!isDbarCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 18); } } if (isDbarCand1 && isDbarCand2) { - SETBIT(pairType, DbarDbar); registry.fill(HIST("hSelectionStatus"), 15); if ((!isDCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 19); } } if (isDCand1 && isDbarCand2) { - SETBIT(pairType, DDbar); registry.fill(HIST("hSelectionStatus"), 16); if ((!isDbarCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 20); } } if (isDbarCand1 && isDCand2) { - SETBIT(pairType, DbarD); registry.fill(HIST("hSelectionStatus"), 17); if ((!isDCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 21); } } - - entryD0Pair(ptCand1, ptCand2, yCand1, yCand2, etaCand1, etaCand2, phiCand1, phiCand2, massDCand1, massDbarCand1, massDCand2, massDbarCand2, pairType, candidateType1, candidateType2); - } - - void fillMcHistos(int8_t matchedRec1, int8_t matchedRec2, int8_t isTrueDCand1, int8_t isTrueDbarCand1, int8_t isTrueDCand2, int8_t isTrueDbarCand2) - { - // Fill hMatchingMcRec - Cand 1 - registry.fill(HIST("hMatchingMcRec"), 1); - if (matchedRec1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 2); - } else if (matchedRec1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 3); - } else if (matchedRec1 == 0) { - registry.fill(HIST("hMatchingMcRec"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcRec"), 5); - if (matchedRec2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 6); - } else if (matchedRec2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 7); - } else if (matchedRec2 == 0) { - registry.fill(HIST("hMatchingMcRec"), 8); - } // Fill True info - if (isTrueDCand1 != 0) { + if (isTrueDCand1) { registry.fill(HIST("hSelectionStatus"), 6); - } else if (isTrueDbarCand1 != 0) { + } else if (isTrueDbarCand1) { registry.fill(HIST("hSelectionStatus"), 7); } - if (isTrueDCand2 != 0) { + if (isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 12); - } else if (isTrueDbarCand2 != 0) { + } else if (isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 13); } - if ((isTrueDCand1 != 0) && (isTrueDCand2 != 0)) { + if (isTrueDCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 22); - } else if ((isTrueDbarCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 23); - } else if ((isTrueDCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 24); - } else if ((isTrueDbarCand1 != 0) && (isTrueDCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 25); } } - /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) - void processData(SelCollisionsWithD0::iterator const& collision, - CandidatesD0Data const& candidates, TracksData const& tracks, aod::BCsWithTimestamps const&) + template + void fillMatchingHisto(const int8_t matched1, const int8_t matched2) { - - auto bc = collision.bc_as(); - int gCollisionId = collision.globalIndex(); - int64_t timeStamp = bc.timestamp(); - - int poolBin = corrBinning->getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); - - int nTracks = 0; - if (collision.numContrib() > 1) { - for (const auto& track : tracks) { - if (std::abs(track.eta()) >= etaTrackMax || std::abs(track.dcaXY()) >= dcaXYTrackMax || std::abs(track.dcaZ()) >= dcaZTrackMax) { - continue; - } - nTracks++; - } - } - if (applyMixedEvent) { - registry.fill(HIST("hMultiplicityPreSelection"), nTracks); + auto hist = IsGen ? registry.get(HIST("hMatchingMcGen")) : registry.get(HIST("hMatchingMcRec")); + hist->Fill(1); + if (matched1 == DecayChannel::D0ToPiK) { + hist->Fill(2); + } else if (matched1 == -DecayChannel::D0ToPiK) { + hist->Fill(3); + } else if (matched1 == 0) { + hist->Fill(4); } - if (nTracks < multMin || nTracks > multMax) { - return; - } - if (applyMixedEvent) { - registry.fill(HIST("hMultiplicity"), nTracks); + hist->Fill(5); + if (matched2 == DecayChannel::D0ToPiK) { + hist->Fill(6); + } else if (matched2 == -DecayChannel::D0ToPiK) { + hist->Fill(7); + } else if (matched2 == 0) { + hist->Fill(8); } + } - int cntD0 = 0; + /// D0(bar)-D0(bar) correlation pair builder, shared by the data and MC-reco modes + /// \param collision is the collision + /// \param selectedD0CandidatesGrouped is the per-collision slice of the selected D0 candidates + /// \param tracks are the (already filtered) tracks of the collision + /// \param timeStamp is the BC timestamp of the collision + template + void processImpl(TCollision const& collision, + TCandidates const& selectedD0CandidatesGrouped, + const int64_t timeStamp) + { + int const gCollisionId = collision.globalIndex(); + int const poolBin = corrBinning->getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); + + getCountersPerEvent(selectedD0CandidatesGrouped); - for (const auto& candidate : candidates) { - analysePid(candidate); - } - auto selectedD0CandidatesGrouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, gCollisionId, cache); - getCountersPerEvent(selectedD0CandidatesGrouped); - // protection against empty tables to be sliced - if (selectedD0Candidates.size() <= 1) { - return; - } for (const auto& candidate1 : selectedD0CandidatesGrouped) { + analysePid(candidate1); outputMlD0Cand1.clear(); outputMlD0barCand1.clear(); - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; - } - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); + auto const prong0Cand1 = candidate1.prong0Id(); + auto const prong1Cand1 = candidate1.prong1Id(); - bool const isSignalD0Cand1 = std::abs(HfHelper::invMassD0ToPiK(candidate1) - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(HfHelper::invMassD0barToKPi(candidate1) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; + [[maybe_unused]] int8_t matchedRec1 = 0; + [[maybe_unused]] int8_t originRec1 = 0; + if constexpr (IsMcRec) { + matchedRec1 = candidate1.flagMcMatchRec(); + originRec1 = candidate1.originMcRec(); } - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution + auto const candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution bool const isDCand1 = isD(candidateType1); bool const isDbarCand1 = isDbar(candidateType1); + bool const isTrueDCand1 = isTrueD(candidateType1); + bool const isTrueDbarCand1 = isTrueDbar(candidateType1); - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; + registry.fill(HIST("hPtCand"), candidate1.pt()); - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } + if (!passKinematicSelections(candidate1, isDCand1, isDbarCand1)) { + continue; } - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { + if (applyMl && !passMlSelections(candidate1, isDCand1, isDbarCand1, outputMlD0Cand1, outputMlD0barCand1)) { continue; } @@ -790,240 +789,15 @@ struct HfCorrelatorDMesonPairs { if (isDCand1) { registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - // Fill D0 table for offline event mixing - if (applyMixedEvent) { - entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); - } - } - if (isDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - // Fill D0 table for offline event mixing - if (applyMixedEvent) { - entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); - } - } - - if (applyMixedEvent) { - // Loop on the associated tracks for offline event mixing - for (const auto& track : tracks) { - // apply track selection - if (track.collisionId() != gCollisionId) { - continue; - } - - // Manual trackFilter check - if (std::abs(track.eta()) >= etaTrackMax) { - continue; - } - if (track.pt() <= ptTrackMin) { - continue; - } - if (std::abs(track.dcaXY()) >= dcaXYTrackMax) { - continue; - } - if (std::abs(track.dcaZ()) >= dcaZTrackMax) { - continue; - } - // Removing D0 daughters by checking track indices - if (daughterTracksCutFlag) { - if ((candidate1.prong0Id() == track.globalIndex()) || (candidate1.prong1Id() == track.globalIndex())) { - continue; - } - } - if (cntD0 == 0) { - entryAssocHad(track.pt(), track.eta(), track.phi(), poolBin, gCollisionId, timeStamp); - registry.fill(HIST("hTracksBin"), poolBin); - registry.fill(HIST("hDcaXYVsPt"), track.dcaXY(), track.pt()); - } - } // Hadron Tracks loop - cntD0++; - } - - // Loop on the second D0 candidate - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { - - outputMlD0Cand2.clear(); - outputMlD0barCand2.clear(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - if ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2)) { - continue; - } - - bool const isSignalD0Cand2 = std::abs(HfHelper::invMassD0ToPiK(candidate2) - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(HfHelper::invMassD0barToKPi(candidate2) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution - - bool const isDCand2 = isD(candidateType2); - bool const isDbarCand2 = isDbar(candidateType2); - - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; - - if (applyMl) { - if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); - } - if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - if (applyMixedEvent) { - if (isDCand2) { - entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); - } - if (isDbarCand2) { - entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); - } - registry.fill(HIST("hD0Cand2Bin"), poolBin); - } - - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), - candidate1.eta(), candidate2.eta(), candidate1.phi(), candidate2.phi(), - candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - } else { - // Fill entries - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), - candidate1.eta(), candidate2.eta(), candidate1.phi(), candidate2.phi(), - candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - } - } // end inner loop (Cand2) - } // end outer loop (Cand1) - - if (applyMixedEvent) { - registry.fill(HIST("hZvtx"), collision.posZ()); - registry.fill(HIST("hMultFT0M"), collision.multFT0M()); - } - } - - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); - - void processMcRec(aod::Collision const& collision, soa::Join const& candidates, aod::Tracks const&) - { - for (const auto& candidate : candidates) { - analysePid(candidate); - } - auto selectedD0CandidatesGroupedMc = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - getCountersPerEvent(selectedD0CandidatesGroupedMc); - for (const auto& candidate1 : selectedD0CandidatesGroupedMc) { - - outputMlD0Cand1.clear(); - outputMlD0barCand1.clear(); - - auto ptCandidate1 = candidate1.pt(); - auto yCandidate1 = HfHelper::yD0(candidate1); - auto phiCandidate1 = candidate1.phi(); - auto etaCandidate1 = candidate1.eta(); - float const massD0Cand1 = HfHelper::invMassD0ToPiK(candidate1); - float const massD0barCand1 = HfHelper::invMassD0barToKPi(candidate1); - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); - - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; - } - bool const isSignalD0Cand1 = std::abs(massD0Cand1 - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(massD0barCand1 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; - } - if (candidate1.isSelD0() < selectionFlagD0 && candidate1.isSelD0bar() < selectionFlagD0bar) { - continue; - } - - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution - - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - bool const isTrueDCand1 = isTrueD(candidateType1); - bool const isTrueDbarCand1 = isTrueDbar(candidateType1); - - int8_t matchedRec1 = candidate1.flagMcMatchRec(); - int8_t originRec1 = candidate1.originMcRec(); - - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; - - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - if (isTrueDCand1) { - registry.fill(HIST("hStatusSinglePart"), 5); - } else if (isTrueDbarCand1) { - registry.fill(HIST("hStatusSinglePart"), 6); - } - - registry.fill(HIST("hPtProng0"), candidate1.ptProng0()); - registry.fill(HIST("hPtProng1"), candidate1.ptProng1()); - registry.fill(HIST("hEta"), candidate1.eta()); - registry.fill(HIST("hPhi"), candidate1.phi()); - registry.fill(HIST("hY"), candidate1.y(MassD0)); - registry.fill(HIST("hPtCandAfterCut"), candidate1.pt()); - - if (isDCand1) { + // Fill THnSparse for D0 candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0 candidates if (isTrueDCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { @@ -1036,149 +810,190 @@ struct HfCorrelatorDMesonPairs { } else if (isTrueDbarCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); } + // Fill D0 table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } if (isDbarCand1) { + registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + // Fill THnSparse for D0bar candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0bar candidates if (isTrueDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { registry.fill(HIST("hMassMcRecPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } else if (originRec1 == RecoDecay::NonPrompt) { registry.fill(HIST("hMassMcRecNonPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecNonPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } } else if (isTrueDCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); } + // Fill D0bar table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0barToKPi(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGroupedMc.end(); ++candidate2) { + // Loop on the second D0 candidate + for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { outputMlD0Cand2.clear(); outputMlD0barCand2.clear(); - auto ptCandidate2 = candidate2.pt(); - auto yCandidate2 = HfHelper::yD0(candidate2); - auto phiCandidate2 = candidate2.phi(); - auto etaCandidate2 = candidate2.eta(); - float const massD0Cand2 = HfHelper::invMassD0ToPiK(candidate2); - float const massD0barCand2 = HfHelper::invMassD0barToKPi(candidate2); - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - bool const isSignalD0Cand2 = std::abs(massD0Cand2 - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(massD0barCand2 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - if (candidate2.isSelD0() < selectionFlagD0 && candidate2.isSelD0bar() < selectionFlagD0bar) { - continue; - } + auto const prong0Cand2 = candidate2.prong0Id(); + auto const prong1Cand2 = candidate2.prong1Id(); if ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2)) { continue; } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution + auto const candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution bool const isDCand2 = isD(candidateType2); bool const isDbarCand2 = isDbar(candidateType2); - bool const isTrueDCand2 = isTrueD(candidateType2); - bool const isTrueDbarCand2 = isTrueDbar(candidateType2); - int8_t matchedRec2 = candidate2.flagMcMatchRec(); - int8_t originRec2 = candidate2.originMcRec(); + int8_t matchedRec2 = 0; + int8_t originRec2 = 0; + if constexpr (IsMcRec) { + matchedRec2 = candidate2.flagMcMatchRec(); + originRec2 = candidate2.originMcRec(); + } - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; + if (!passKinematicSelections(candidate2, isDCand2, isDbarCand2)) { + continue; + } - if (applyMl) { + if (applyMl && !passMlSelections(candidate2, isDCand2, isDbarCand2, outputMlD0Cand2, outputMlD0barCand2)) { + continue; + } + + if (applyMixedEvent) { if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0ToPiK(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } + registry.fill(HIST("hD0Cand2Bin"), poolBin); + } - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, etaCandidate1, etaCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - - } else { - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, etaCandidate1, etaCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); + // Canonical ordering of the pair: D0 first (higher pT first for equal flavours) + bool const swapOrder = shouldSwapOrder(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidate1, candidate2); + uint8_t const type1 = swapOrder ? candidateType2 : candidateType1; + uint8_t const type2 = swapOrder ? candidateType1 : candidateType2; + auto const& first = swapOrder ? candidate2 : candidate1; + auto const& second = swapOrder ? candidate1 : candidate2; + uint8_t const pairType = setPairType(type1, type2); + + // Fill entries for D0-D0(bar) pairs and plot histos + fillSelectionStatusHisto(type1, type2); + entryD0Pair(first.pt(), second.pt(), HfHelper::yD0(first), HfHelper::yD0(second), first.eta(), second.eta(), + first.phi(), second.phi(), HfHelper::invMassD0ToPiK(first), HfHelper::invMassD0barToKPi(first), + HfHelper::invMassD0ToPiK(second), HfHelper::invMassD0barToKPi(second), pairType, type1, type2); + if (applyMl) { + entryD0PairMl(swapOrder ? outputMlD0Cand2 : outputMlD0Cand1, swapOrder ? outputMlD0barCand2 : outputMlD0barCand1, + swapOrder ? outputMlD0Cand1 : outputMlD0Cand2, swapOrder ? outputMlD0barCand1 : outputMlD0barCand2); + } + if constexpr (IsMcRec) { + fillMatchingHisto(swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); + entryD0PairMcInfo(swapOrder ? originRec2 : originRec1, swapOrder ? originRec1 : originRec2, + swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); } } // end inner loop (Cand2) } // end outer loop (Cand1) + + if (applyMixedEvent) { + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + } } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process Mc reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) + void processData(SelCollisionsWithD0::iterator const& collision, + CandidatesD0Data const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); + } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); + + /// D0(bar)-D0(bar) correlation pair builder - MC reco level (candidates matched to MC truth) + void processMcRec(SelCollisionsWithD0::iterator const& collision, + CandidatesD0McRec const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); + } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process MC reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - MC generator level (particles flagged as D0 -> pi K) void processMcGen(aod::McCollision const&, soa::SmallGroups> const& collisions, McParticlesPlus2Prong const& mcParticles) { + // Take the associated reconstructed collision with the highest number of PV contributors int numPvContributorsGen{0}; - for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision + for (const auto& collision : collisions) { int const numPvContributors = collision.numContrib(); - - if (numPvContributors > numPvContributorsGen) { // we take the associated reconstructed collision with higher number of PV contributors + if (numPvContributors > numPvContributorsGen) { numPvContributorsGen = numPvContributors; } } - // Get counters per event - int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + + // Select the D0(bar) -> K pi particles + std::vector selectedParticles; for (const auto& particle : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle.pdgCode()) != Pdg::kD0) { + if (std::abs(particle.flagMcMatchGen()) != DecayChannel::D0ToPiK) { continue; } - if (std::abs(particle.y()) > yCandMax) { + registry.fill(HIST("hPtCandMcGen"), particle.pt()); + if (!passGenSelections(particle)) { continue; } - if (ptCandMin >= 0. && particle.pt() < ptCandMin) { - continue; - } - auto particleType = assignParticleTypeD0Gen(particle); // Candidate type attribution + selectedParticles.push_back(particle); + } + + // Single-particle histograms and per-event counters + int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + for (const auto& particle : selectedParticles) { + auto const particleType = assignParticleTypeD0Gen(particle); bool const isDParticle = isTrueD(particleType); bool const isDbarParticle = isTrueDbar(particleType); - if (isDParticle) { - nDevent++; - } - if (isDbarParticle) { - nDbarevent++; - } - if (isDParticle && isDbarParticle) { - nDDbarevent++; + nDevent += isDParticle; + nDbarevent += isDbarParticle; + nDDbarevent += (isDParticle && isDbarParticle); + nDorDbarevent += (isDParticle || isDbarParticle); + + registry.fill(HIST("hEtaMcGen"), particle.eta()); + registry.fill(HIST("hPhiMcGen"), particle.phi()); + registry.fill(HIST("hPtCandAfterCutMcGen"), particle.pt()); + + registry.fill(HIST("hStatusSinglePartMcGen"), 1); + if (isDParticle && !isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 2); + registry.fill(HIST("hStatusSinglePartMcGen"), 5); + } else if (isDbarParticle && !isDParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 3); + registry.fill(HIST("hStatusSinglePartMcGen"), 6); + } else if (isDParticle && isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 4); } - if (isDParticle || isDbarParticle) { - nDorDbarevent++; + + registry.fill(HIST("hPtVsYVsNContribMcGen"), particle.pt(), particle.y(), numPvContributorsGen); + if (particle.originMcGen() == RecoDecay::Prompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle.pt(), particle.y(), numPvContributorsGen); + } else if (particle.originMcGen() == RecoDecay::NonPrompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle.pt(), particle.y(), numPvContributorsGen); } + registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0McGen"), nDevent); @@ -1193,138 +1008,61 @@ struct HfCorrelatorDMesonPairs { registry.fill(HIST("hInputCheckD0OrD0barMcGen"), nDorDbarevent); } - for (const auto& particle1 : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle1.pdgCode()) != Pdg::kD0) { - continue; - } - registry.fill(HIST("hPtCandMcGen"), particle1.pt()); + // Pairs of selected particles + for (size_t i = 0; i < selectedParticles.size(); ++i) { + for (size_t j = i + 1; j < selectedParticles.size(); ++j) { + auto const& particle1 = selectedParticles[i]; + auto const& particle2 = selectedParticles[j]; - if (std::abs(particle1.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle1.pt() < ptCandMin) { - continue; - } - registry.fill(HIST("hEtaMcGen"), particle1.eta()); - registry.fill(HIST("hPhiMcGen"), particle1.phi()); - registry.fill(HIST("hPtCandAfterCutMcGen"), particle1.pt()); - - auto particleType1 = assignParticleTypeD0Gen(particle1); // Candidate sign attribution - bool const isDParticle1 = isTrueD(particleType1); - bool const isDbarParticle1 = isTrueDbar(particleType1); - - // check if it's MC matched - int8_t matchedGen1 = particle1.flagMcMatchGen(); - // check origin - int8_t originGen1 = particle1.originMcGen(); - - // Fill selection status single particle - registry.fill(HIST("hStatusSinglePartMcGen"), 1); - if (isDParticle1 && !isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 2); - registry.fill(HIST("hStatusSinglePartMcGen"), 5); - } else if (isDbarParticle1 && !isDParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 3); - registry.fill(HIST("hStatusSinglePartMcGen"), 6); - } else if (isDParticle1 && isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 4); - } + auto const type1 = assignParticleTypeD0Gen(particle1); + auto const type2 = assignParticleTypeD0Gen(particle2); - registry.fill(HIST("hPtVsYVsNContribMcGen"), particle1.pt(), particle1.y(), numPvContributorsGen); - if (originGen1 == RecoDecay::Prompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - if (originGen1 == RecoDecay::NonPrompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); + // Canonical ordering (same as reco): D0 first, higher pT first for equal flavours + bool const swapOrder = shouldSwapOrder(isTrueD(type1), isTrueDbar(type1), isTrueD(type2), isTrueDbar(type2), particle1, particle2); + auto const& first = swapOrder ? particle2 : particle1; + auto const& second = swapOrder ? particle1 : particle2; + auto const typeFirst = swapOrder ? type2 : type1; + auto const typeSecond = swapOrder ? type1 : type2; + bool const isDFirst = isTrueD(typeFirst), isDbarFirst = isTrueDbar(typeFirst); + bool const isDSecond = isTrueD(typeSecond), isDbarSecond = isTrueDbar(typeSecond); - for (auto particle2 = particle1 + 1; particle2 != mcParticles.end(); ++particle2) { - // check if the particle is D0 or D0bar - if (std::abs(particle2.pdgCode()) != Pdg::kD0) { - continue; - } - if (std::abs(particle2.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle2.pt() < ptCandMin) { - continue; - } - // Candidate sign attribution. - auto particleType2 = assignParticleTypeD0Gen(particle2); - bool const isDParticle2 = isTrueD(particleType2); - bool const isDbarParticle2 = isTrueDbar(particleType2); - - // check if it's MC matched - int8_t matchedGen2 = particle2.flagMcMatchGen(); - // check origin - int8_t originGen2 = particle2.originMcGen(); - - // Fill hMatchingMcGen - Cand 1 - registry.fill(HIST("hMatchingMcGen"), 1); - if (matchedGen1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 2); - } else if (matchedGen1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 3); - } else if (matchedGen1 == 0) { - registry.fill(HIST("hMatchingMcGen"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcGen"), 5); - if (matchedGen2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 6); - } else if (matchedGen2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 7); - } else if (matchedGen2 == 0) { - registry.fill(HIST("hMatchingMcGen"), 8); - } + fillMatchingHisto(first.flagMcMatchGen(), second.flagMcMatchGen()); - // Fill particle1's Selection Status + // Selection status registry.fill(HIST("hSelectionStatusMcGen"), 2); - if (isDParticle1 && !isDbarParticle1) { + if (isDFirst && !isDbarFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 6); - } else if (isDbarParticle1 && !isDParticle1) { + } else if (isDbarFirst && !isDFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 7); } - - // Fill particle2's Selection Status registry.fill(HIST("hSelectionStatusMcGen"), 8); - if (isDParticle2 && !isDbarParticle2) { + if (isDSecond && !isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 12); - } else if (isDbarParticle2 && !isDParticle2) { + } else if (isDbarSecond && !isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 13); } - // Fill pair Selection Status - uint8_t pairType(0); registry.fill(HIST("hSelectionStatusMcGen"), 1); - - if (isDParticle1 && isDParticle2) { - SETBIT(pairType, DD); + if (isDFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 22); } - if (isDbarParticle1 && isDbarParticle2) { - SETBIT(pairType, DbarDbar); + if (isDbarFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 23); } - if (isDParticle1 && isDbarParticle2) { - SETBIT(pairType, DDbar); + if (isDFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 24); } - if (isDbarParticle1 && isDParticle2) { - SETBIT(pairType, DbarD); + if (isDbarFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 25); } - // Fill pair Selection Status - entryD0PairMcGen(particle1.pt(), particle2.pt(), particle1.y(), particle2.y(), particle1.eta(), particle2.eta(), particle1.phi(), particle2.phi(), MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, particleType1, particleType2); - entryD0PairMcGenInfo(originGen1, originGen2, matchedGen1, matchedGen2); - - } // end inner loop - } // end outer loop + uint8_t const pairType = setPairType(typeFirst, typeSecond); + entryD0PairMcGen(first.pt(), second.pt(), first.y(), second.y(), first.eta(), second.eta(), first.phi(), second.phi(), + MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, typeFirst, typeSecond); + entryD0PairMcGenInfo(first.originMcGen(), second.originMcGen(), first.flagMcMatchGen(), second.flagMcMatchGen()); + } + } } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcGen, "Process D0 Mc Gen mode", false); }; From dff5492009debe0a950e5053a1ea28cec816c12f Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:19:07 +0200 Subject: [PATCH 318/648] [PWGHF] Update of B+ routines (#18263) --- PWGHF/Core/HfMlResponseBplusToD0Pi.h | 39 +- PWGHF/Core/HfMlResponseBplusToD0PiReduced.h | 39 +- .../candidateCreatorBplusReduced.cxx | 3 +- .../candidateSelectorBplusToD0PiReduced.cxx | 36 +- PWGHF/D2H/Tasks/taskBplusReduced.cxx | 529 ++++++++++-------- .../DataModel/CandidateReconstructionTables.h | 29 + PWGHF/TableProducer/candidateCreatorBplus.cxx | 3 +- 7 files changed, 431 insertions(+), 247 deletions(-) diff --git a/PWGHF/Core/HfMlResponseBplusToD0Pi.h b/PWGHF/Core/HfMlResponseBplusToD0Pi.h index 9593101719e..3ca6e41e53e 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0Pi.h +++ b/PWGHF/Core/HfMlResponseBplusToD0Pi.h @@ -29,10 +29,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -89,6 +88,14 @@ enum class InputFeaturesBplusToD0Pi : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlProbBkg, prong0MlProbPrompt, @@ -134,6 +141,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbBkg, mlProbD0, mlProbD0bar, 0); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbPrompt, mlProbD0, mlProbD0bar, 1); @@ -159,6 +174,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -190,6 +213,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlProbBkg), FILL_MAP_BPLUS(prong0MlProbPrompt), diff --git a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h index 93e2ea67eb4..43052a86ccf 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h +++ b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h @@ -27,10 +27,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -76,6 +75,14 @@ enum class InputFeaturesBplusToD0PiReduced : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlScoreBkg, prong0MlScorePrompt, @@ -119,6 +126,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_BPLUS(prong0MlScoreBkg); CHECK_AND_FILL_VEC_BPLUS(prong0MlScorePrompt); @@ -144,6 +159,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -175,6 +198,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlScoreBkg), FILL_MAP_BPLUS(prong0MlScorePrompt), diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx index b58fcc51d7e..0278f36fd50 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx @@ -200,7 +200,8 @@ struct HfCandidateCreatorBplusReduced { pVecD0[0], pVecD0[1], pVecD0[2], pVecPion[0], pVecPion[1], pVecPion[2], dcaD0.getY(), dcaPion.getY(), - std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2())); + std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx index e9088c68e93..a72783a8e12 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx @@ -19,11 +19,13 @@ #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" #include "PWGHF/Utils/utilsPid.h" #include "Common/Core/TrackSelectorPID.h" #include +#include #include #include #include @@ -91,6 +93,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_BPLUSToD0Pi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable useD0TriggerMassCut{"useD0TriggerMassCut", false, "Flag to enable parametrize pT differential mass cut for triggered data"}; // variable that will store the value of selectionFlagD (defined in dataCreatorD0PiReduced.cxx) int mySelectionFlagD0 = -1; int mySelectionFlagD0bar = -1; @@ -101,6 +104,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; + HfTrigger2ProngCuts hfTriggerCuts; using TracksPion = soa::Join; @@ -154,6 +158,19 @@ struct HfCandidateSelectorBplusToD0PiReduced { } } + /// Utility function to apply the pT-differential mass cut for triggered data + /// on the D0 daughter of the B+ candidate + /// \param candidate is the B+ candidate + /// \param trackPi is the bachelor pion of the B+ candidate + /// \return true if the D0 daughter passes the mass selection + template + bool isDmesInMassRange(const T1& candidate, TracksPion::iterator const& trackPi) + { + const auto candD0 = candidate.template prong0_as(); + const auto invMassD0 = (trackPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + return isCandidateInMassRange(invMassD0, o2::constants::physics::MassD0, candidate.ptProng0(), hfTriggerCuts); + } + /// Main function to perform B+ candidate creation /// \param withDmesMl is the flag to use the table with ML scores for the D- daughter (only possible if present in the derived data) /// \param hfCandsBp B+ candidates @@ -189,6 +206,18 @@ struct HfCandidateSelectorBplusToD0PiReduced { continue; } + auto trackPi = hfCandBp.template prong1_as(); + + // pT-differential mass cut on the D0 daughter for triggered data + if (useD0TriggerMassCut && !isDmesInMassRange(hfCandBp, trackPi)) { + hfSelBplusToD0PiCandidate(statusBplus); + if (applyBplusMl) { + hfMlBplusToD0PiCandidate(outputMlNotPreselected); + } + // LOGF(info, "B+ candidate selection failed at D0-meson mass selection"); + continue; + } + if constexpr (WithDmesMl) { // we include it in the topological selections if (!HfHelper::selectionDmesMlScoresForBReduced(hfCandBp, cutsDmesMl, binsPtDmesMl)) { hfSelBplusToD0PiCandidate(statusBplus); @@ -206,7 +235,6 @@ struct HfCandidateSelectorBplusToD0PiReduced { } // track-level PID selection - auto trackPi = hfCandBp.template prong1_as(); if (pionPidMethod == PidMethod::TpcOrTof || pionPidMethod == PidMethod::TpcAndTof) { int pidTrackPi{TrackSelectorPID::Status::NotApplicable}; if (pionPidMethod == PidMethod::TpcOrTof) { @@ -250,7 +278,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelection(HfRedCandBplus const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelection @@ -259,7 +288,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelectionWithDmesMl(soa::Join const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelectionWithDmesMl diff --git a/PWGHF/D2H/Tasks/taskBplusReduced.cxx b/PWGHF/D2H/Tasks/taskBplusReduced.cxx index 5913760dcea..190918846e4 100644 --- a/PWGHF/D2H/Tasks/taskBplusReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusReduced.cxx @@ -15,8 +15,8 @@ /// \author Antonio Palasciano , Università degli Studi di Bari & INFN, Sezione di Bari #include "PWGHF/Core/HfHelper.h" -#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" +#include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -38,15 +38,16 @@ #include #include +#include #include #include -#include using namespace o2; using namespace o2::aod; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::analysis::hf_flow_utils; namespace o2::aod { @@ -87,19 +88,26 @@ DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate DECLARE_SOA_COLUMN(DecayLengthD, decayLengthD, float); //! Decay length of D-meson daughter candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXYD, decayLengthXYD, float); //! Transverse decay length of D-meson daughter candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthDToB, decayLengthDToB, float); //! Decay length of D-meson daughter candidate from B decay vertex (cm) +DECLARE_SOA_COLUMN(DecayLengthXYDToB, decayLengthXYDToB, float); //! Transverse decay length of D-meson daughter candidate from B decay vertex (cm) DECLARE_SOA_COLUMN(ImpactParameterD, impactParameterD, float); //! Impact parameter product of D-meson daughter candidate DECLARE_SOA_COLUMN(ImpactParameterBach, impactParameterBach, float); //! Impact parameter product of bachelor pion DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of daughters DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine pointing angle of candidate DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine pointing angle of candidate in transverse plane -DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D-meson daughter candidate -DECLARE_SOA_COLUMN(CpaXYD, cpaXYD, float); //! Cosine pointing angle in transverse plane of D-meson daughter candidate +DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D candidate to PV +DECLARE_SOA_COLUMN(CpaDXY, cpaDXY, float); //! Cosine pointing angle of D candidate to PV in transverse plane +DECLARE_SOA_COLUMN(CpaDToB, cpaDToB, float); //! Cosine pointing angle of D candidate to B decay vertex +DECLARE_SOA_COLUMN(CpaDToBXY, cpaDToBXY, float); //! Cosine pointing angle of D candidate to B decay vertex in transverse plane +DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product of B candidate +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of the collision DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreSig, mlScoreSig, float); //! ML score for signal class DECLARE_SOA_COLUMN(FlagWrongCollision, flagWrongCollision, int8_t); //! Flag for association with wrong collision } // namespace hf_cand_bplus_lite DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with some B+ properties + // B meson features hf_cand_bplus_lite::M, hf_cand_bplus_lite::Pt, hf_cand_bplus_lite::Eta, @@ -116,14 +124,20 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplus_lite::MaxNormalisedDeltaIP, hf_cand_bplus_lite::MlScoreSig, hf_sel_candidate_bplus::IsSelBplusToD0Pi, + hf_cand_bplus_lite::ScalarProd, + hf_cand_bplus_lite::Centrality, // D meson features hf_cand_bplus_lite::MD, hf_cand_bplus_lite::PtD, hf_cand_bplus_lite::DecayLengthD, hf_cand_bplus_lite::DecayLengthXYD, - hf_cand_bplus_lite::ImpactParameterD, + hf_cand_bplus_lite::DecayLengthDToB, + hf_cand_bplus_lite::DecayLengthXYDToB, hf_cand_bplus_lite::CpaD, - hf_cand_bplus_lite::CpaXYD, + hf_cand_bplus_lite::CpaDXY, + hf_cand_bplus_lite::CpaDToB, + hf_cand_bplus_lite::CpaDToBXY, + hf_cand_bplus_lite::ImpactParameterD, hf_cand_bplus_lite::PtDmesProngMin, hf_cand_bplus_lite::AbsEtaDmesProngMin, hf_cand_bplus_lite::ItsNClsDmesProngMin, @@ -170,13 +184,14 @@ DECLARE_SOA_TABLE(HfRedBpMcCheck, "AOD", "HFREDBPMCCHECK", //! Table with MC dec } // namespace o2::aod // string definitions, used for histogram axis labels -const TString stringPt = "#it{p}_{T} (GeV/#it{c})"; -const TString stringPtD = "#it{p}_{T}(D0) (GeV/#it{c});"; -const TString bPlusCandTitle = "B+ candidates;"; +const TString stringPt = "#it{p}_{T}(B^{+}) (GeV/#it{c});"; +const TString stringPtGen = "#it{p}_{T}^{gen}(B^{+}) (GeV/#it{c});"; +const TString stringPtD = "#it{p}_{T}(D^{0}) (GeV/#it{c});"; +const TString bPlusCandTitle = "B^{+} candidates;"; const TString entries = "entries"; -const TString bPlusCandMatch = "B+ candidates (matched);"; -const TString bPlusCandUnmatch = "B+ candidates (unmatched);"; -const TString mcParticleMatched = "MC particles (matched);"; +const TString bPlusCandMatch = "B^{+} candidates (matched);"; +const TString bPlusCandUnmatch = "B^{+} candidates (unmatched);"; +const TString bPlusPartGen = "B^{+} particles (generated);"; /// B+ analysis task struct HfTaskBplusReduced { @@ -184,18 +199,22 @@ struct HfTaskBplusReduced { Produces hfRedBpMcCheck; Configurable selectionFlagBplus{"selectionFlagBplus", 1, "Selection Flag for Bplus"}; - Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; - Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity"}; - Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity for acceptance calculation"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum for acceptance calculation"}; Configurable fillHistograms{"fillHistograms", true, "Flag to enable histogram filling"}; Configurable fillSparses{"fillSparses", false, "Flag to enable sparse filling"}; Configurable fillTree{"fillTree", false, "Flag to enable tree filling"}; Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable> binsPt{"binsPt", std::vector{hf_cuts_bplus_to_d0_pi::vecBinsPt}, "pT bin limits"}; + Configurable centralityEstimator{"centralityEstimator", 1, "Centrality estimator (FT0M: 0, FT0C: 1)"}; + Configurable harmonic{"harmonic", 2, "harmonic number"}; + Configurable qVecDetector{"qVecDetector", 3, "Detector for Q vector estimation (FT0M: 1, FT0A: 2, FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; + using CollisionsWithCents = soa::Join; + using CollisionsWithCentsQvecs = soa::Join; using TracksPion = soa::Join; using CandsD0 = soa::Join; @@ -205,7 +224,7 @@ struct HfTaskBplusReduced { void init(InitContext&) { - std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithBplusMl}; + std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithDmesMlScalarProd, doprocessDataWithBplusMl, doprocessDataWithBplusMlScalarProd}; if ((std::accumulate(processFuncData.begin(), processFuncData.end(), 0)) > 1) { LOGP(fatal, "Only one process function for data can be enabled at a time."); } @@ -215,131 +234,125 @@ struct HfTaskBplusReduced { } const AxisSpec axisMlScore{100, 0.f, 1.f}; - const AxisSpec axisMassBplus{150, 4.5, 6.0}; + const AxisSpec axisMassBplus{300, 4.5f, 6.0f}; const AxisSpec axisMassD0{300, 1.75f, 2.05f}; - const AxisSpec axisCpa{120, -1.1, 1.1}; - const AxisSpec axisCpaD{101, 0.9, 1.01}; - const AxisSpec axisPtProng{100, 0., 10.}; - const AxisSpec axisD0Prong{200, -0.05, 0.05}; - const AxisSpec axisImpParProd{200, -0.001, 0.001}; - const AxisSpec axisDecLength{100, 0., 0.5}; - const AxisSpec axisNormDecLength{40, 0., 20}; - const AxisSpec axisEta{100, -2., 2.}; - const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtD0{100, 0., 50.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisDecayLength{200, 0.f, 0.4f}; + const AxisSpec axisNormDecayLength{100, 0.f, 50.f}; + const AxisSpec axisDca{100, -0.05f, 0.05f}; + const AxisSpec axisCosp{110, 0.f, 1.1f}; + const AxisSpec axisEta{30, -1.5f, 1.5f}; + const AxisSpec axisImpParProd{100, -1.e-3, 1.e-3}; + const AxisSpec axisPtBplus{100, 0.f, 50.f}; + const AxisSpec axisPtD0{100, 0.f, 50.f}; const AxisSpec axisPtPi{100, 0.f, 10.f}; - if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithBplusMl) { + if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd || doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { if (fillHistograms) { - registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hPtCand", bPlusCandTitle + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{1000, 0., 50.}}}); - registry.add("hPtProng0", bPlusCandTitle + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1", bPlusCandTitle + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXy", bPlusCandTitle + "decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXy", bPlusCandTitle + "Norm. decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0Prong0", bPlusCandTitle + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1", bPlusCandTitle + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hCpa", bPlusCandTitle + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXy", bPlusCandTitle + "candidate cosine of pointing angle xy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEta", bPlusCandTitle + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidity", bPlusCandTitle + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hd0d0", bPlusCandTitle + "candidate product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hInvMassD0", bPlusCandTitle + "prong0, D0 inv. mass (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassD0, axisPtD0}}); - registry.add("hDecLengthD0", bPlusCandTitle + "D^{0} candidate decay length (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hDecLengthXyD0", bPlusCandTitle + "decay length XY (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hCpaD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); - registry.add("hCpaXyD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P}^{XY});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); + registry.add("hMass", bPlusCandTitle + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLength", bPlusCandTitle + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXy", bPlusCandTitle + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXy", bPlusCandTitle + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0", bPlusCandTitle + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1", bPlusCandTitle + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCosp", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXy", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEta", bPlusCandTitle + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidity", bPlusCandTitle + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProd", bPlusCandTitle + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0", bPlusCandTitle + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1", bPlusCandTitle + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassD", bPlusCandTitle + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthD", bPlusCandTitle + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyD", bPlusCandTitle + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); // ML scores of D0 daughter - if (doprocessDataWithDmesMl) { - registry.add("hMlScoreBkgD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + if (doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd) { + registry.add("hMlScoreBkgD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate - if (doprocessDataWithBplusMl) { - registry.add("hMlScoreSigBplus", bPlusCandTitle + "#it{p}_{T} (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + if (doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { + registry.add("hMlScoreSigBplus", bPlusCandTitle + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { - if (!(doprocessDataWithDmesMl || doprocessDataWithBplusMl)) { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd)) { + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } else { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } - // histograms processMC if (doprocessMc || doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { if (fillHistograms) { - // Gen Level - registry.add("hEtaGen", mcParticleMatched + "candidate #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hYGen", mcParticleMatched + "candidate #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0Gen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1Gen", mcParticleMatched + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYProng0Gen", mcParticleMatched + "prong 0 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hYProng1Gen", mcParticleMatched + "prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hEtaProng0Gen", mcParticleMatched + "prong 0 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hPtProngsVsPtBGen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hYProngsVsBplusGen", mcParticleMatched + "prong 0 #it{y}^{gen};prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH3F, {axisRapidity, axisRapidity, axisPtB}}); - registry.add("hEtaProngsVsBplusGen", mcParticleMatched + "prong 0 #it{#eta}^{gen};prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH3F, {axisEta, axisEta, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{+} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - // Reco Level - Signal - registry.add("hPtRecSig", bPlusCandMatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecSig", bPlusCandMatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecSig", bPlusCandMatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecSig", bPlusCandMatch + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0RecSig", bPlusCandMatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecSig", bPlusCandMatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecSig", bPlusCandMatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecSig", bPlusCandMatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecSig", bPlusCandMatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecSig", bPlusCandMatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecSig", bPlusCandMatch + "candidate decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecSig", bPlusCandMatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + // gen histos + registry.add("hEtaGen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);" + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hPtProng0Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(D^{0}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hYProng0Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYProng1Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng0Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng1Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + + // reco histos + // signal + registry.add("hMassRecSig", bPlusCandMatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecSig", bPlusCandMatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecSig", bPlusCandMatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecSig", bPlusCandMatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecSig", bPlusCandMatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecSig", bPlusCandMatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecSig", bPlusCandMatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecSig", bPlusCandMatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecSig", bPlusCandMatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecSig", bPlusCandMatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + // background if (fillBackground) { - registry.add("hPtRecBg", bPlusCandUnmatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecBg", bPlusCandUnmatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecBg", bPlusCandUnmatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecBg", bPlusCandUnmatch + "candidate #it{#y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProngsVsBplusRecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hPtProng0RecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecBg", bPlusCandUnmatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecBg", bPlusCandUnmatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecBg", bPlusCandUnmatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecBg", bPlusCandUnmatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecBg", bPlusCandUnmatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + "candidate decay length xy(cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecBg", bPlusCandUnmatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + registry.add("hMassRecBg", bPlusCandUnmatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecBg", bPlusCandUnmatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecBg", bPlusCandUnmatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecBg", bPlusCandUnmatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecBg", bPlusCandUnmatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecBg", bPlusCandUnmatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecBg", bPlusCandUnmatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); } // MC checks if (doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMlAndDecayTypeCheck) { constexpr uint8_t kNBinsDecayTypeMc = hf_cand_bplus::DecayTypeMc::NDecayTypeMc; TString labels[kNBinsDecayTypeMc]; labels[hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #pi^{#plus}"; - labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #K^{#plus}"; + labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) K^{#plus}"; labels[hf_cand_bplus::DecayTypeMc::PartlyRecoDecay] = "Partly reconstructed decay channel"; labels[hf_cand_bplus::DecayTypeMc::OtherDecay] = "Other decays"; static const AxisSpec axisDecayType = {kNBinsDecayTypeMc, 0.5, kNBinsDecayTypeMc + 0.5, ""}; - registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtB}}); + registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtBplus}}); for (uint8_t iBin = 0; iBin < kNBinsDecayTypeMc; ++iBin) { registry.get(HIST("hDecayTypeMc"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin]); } @@ -347,36 +360,37 @@ struct HfTaskBplusReduced { // ML scores of D0 daughter if (doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); // background - registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate if (doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); // background - registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { // gen sparses - registry.add("hPtYGenSig", "B^{+} particles (generated);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); - registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); + registry.add("hPtYGenSig", bPlusPartGen + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);" + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + // reco sparses - if (!doprocessDataWithDmesMl) { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck)) { + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } } else { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } @@ -398,19 +412,21 @@ struct HfTaskBplusReduced { /// \param withDecayTypeCheck is the flag to enable MC with decay type check /// \param withDmesMl is the flag to enable the filling with ML scores for the D0 daughter /// \param withBplusMl is the flag to enable the filling with ML scores for the B+ candidate + /// \param withScalarProd is the flag to enable the filling with scalar product + /// \param collision is the collision /// \param candidate is the B+ candidate /// \param candidatesD is the table with D0 candidates - template - void fillCand(Cand const& candidate, - CandsDmes const& /*candidatesD*/, - TracksPion const&) + template + void fillCand(Collision const& collision, + Cand const& candidate, + CandsDmes const&) { auto ptCandBplus = candidate.pt(); auto invMassBplus = HfHelper::invMassBplusToD0Pi(candidate); auto candD0 = candidate.template prong0_as(); - auto candPi = candidate.template prong1_as(); + auto prong1 = candidate.template prong1_as(); auto ptD0 = candidate.ptProng0(); - auto invMassD0 = (candPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + auto invMassD0 = (prong1.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); std::array const posPv{candidate.posX(), candidate.posY(), candidate.posZ()}; std::array const posSvD{candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()}; std::array const momD{candD0.pVector()}; @@ -431,26 +447,24 @@ struct HfTaskBplusReduced { if (fillHistograms) { if constexpr (DoMc) { if (isSignal) { - registry.fill(HIST("hMassRecSig"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecSig"), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecSig"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecSig"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecSig"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecSig"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecSig"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecSig"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecSig"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecSig"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecSig"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecSig"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecSig"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hDecLengthXyD0RecSig"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0RecSig"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0RecSig"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecSig"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecSig"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecSig"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecSig"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecSig"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecSig"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecSig"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecSig"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecSig"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecSig"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecSig"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecSig"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecSig"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecSig"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecSig"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecSig"), ptD0, cpaXyD0); if constexpr (WithDecayTypeCheck) { registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi, invMassBplus, ptCandBplus); } @@ -463,26 +477,24 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecSig"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if (fillBackground) { - registry.fill(HIST("hMassRecBg"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecBg"), ptCandBplus); - registry.fill(HIST("hPtProng0RecBg"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecBg"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecBg"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecBg"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecBg"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecBg"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecBg"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecBg"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecBg"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecBg"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecBg"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecBg"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecBg"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hInvMassDRecBg"), invMassD0, ptD0); - registry.fill(HIST("hDecLengthDRecBg"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyDRecBg"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaDRecBg"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyDRecBg"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecBg"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecBg"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecBg"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecBg"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecBg"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecBg"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecBg"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecBg"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecBg"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecBg"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecBg"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecBg"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecBg"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecBg"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecBg"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecBg"), ptD0, cpaXyD0); if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgDRecBg"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptDRecBg"), ptD0, candidate.prong0MlScorePrompt()); @@ -492,7 +504,7 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecBg"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if constexpr (WithDecayTypeCheck) { - if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // Partly reconstructed decay channel + if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // B+ → D0bar K+ → (K+ π-) K+ registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK, invMassBplus, ptCandBplus); } else if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::PartlyRecoDecay)) { // Partly reconstructed decay channel registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::PartlyRecoDecay, invMassBplus, ptCandBplus); @@ -501,25 +513,25 @@ struct HfTaskBplusReduced { } } } else { - registry.fill(HIST("hMass"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtCand"), ptCandBplus); - registry.fill(HIST("hPtProng0"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hd0d0"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLength"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXy"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXy"), candidate.decayLengthXY() / candidate.errorDecayLengthXY(), ptCandBplus); - registry.fill(HIST("hd0Prong0"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hCpa"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXy"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEta"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidity"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hInvMassD0"), invMassD0, ptCandBplus); - registry.fill(HIST("hDecLengthD0"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyD0"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0"), cpaXyD0, ptD0); + registry.fill(HIST("hMass"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProd"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLength"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXy"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXy"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCosp"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXy"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEta"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidity"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassD"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthD"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyD"), ptD0, decLenXyD0); + registry.fill(HIST("hCospD"), ptD0, cpaD0); + registry.fill(HIST("hCospXyD"), ptD0, cpaXyD0); + if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgD"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptD"), ptD0, candidate.prong0MlScorePrompt()); @@ -555,11 +567,12 @@ struct HfTaskBplusReduced { } if (fillTree) { float const pseudoRndm = ptD0 * 1000. - static_cast(ptD0 * 1000); - if (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor) { + if (flagMcMatchRec != 0 || (((DoMc && fillBackground) || !DoMc) && (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor))) { float prong0MlScoreBkg = -1.; float prong0MlScorePrompt = -1.; float prong0MlScoreNonprompt = -1.; float candidateMlScoreSig = -1; + float scalarProd = -1; if constexpr (WithDmesMl) { prong0MlScoreBkg = candidate.prong0MlScoreBkg(); prong0MlScorePrompt = candidate.prong0MlScorePrompt(); @@ -568,7 +581,12 @@ struct HfTaskBplusReduced { if constexpr (WithBplusMl) { candidateMlScoreSig = candidate.mlProbBplusToD0Pi(); } - auto prong1 = candidate.template prong1_as(); + if constexpr (WithScalarProd) { + auto qVecs = getQvec(collision, qVecDetector.value); + float xQVec = qVecs[0]; + float yQVec = qVecs[1]; + scalarProd = std::cos(harmonic * candidate.phi()) * xQVec + std::sin(harmonic * candidate.phi()) * yQVec; + } float tpcNSigmaPi, tofNSigmaPi, tpcTofNSigmaPi, tpcNSigmaKa, tofNSigmaKa, tpcTofNSigmaKa; if (prong1.signed1Pt() < 0) { tpcNSigmaPi = candD0.tpcNSigmaPiProng1(); @@ -592,7 +610,7 @@ struct HfTaskBplusReduced { } hfRedCandBpLite( - // B+ - meson features + // B-meson features invMassBplus, ptCandBplus, candidate.eta(), @@ -609,14 +627,20 @@ struct HfTaskBplusReduced { candidate.maxNormalisedDeltaIP(), candidateMlScoreSig, candidate.isSelBplusToD0Pi(), + scalarProd, + centralityEstimator == 0 ? collision.centFT0M() : collision.centFT0C(), // D-meson features invMassD0, ptD0, - decLenD0, - decLenXyD0, + candidate.decayLengthDToPv(), + candidate.decayLengthDToPvXY(), + candidate.decayLengthDToB(), + candidate.decayLengthDToBXY(), + candidate.cpaDToPv(), + candidate.cpaDToPvXY(), + candidate.cpaDToB(), + candidate.cpaDToBXY(), candidate.impactParameter0(), - cpaD0, - cpaXyD0, candD0.ptProngMin(), candD0.absEtaProngMin(), candD0.itsNClsProngMin(), @@ -705,56 +729,88 @@ struct HfTaskBplusReduced { } // Process functions - void processData(soa::Filtered> const& candidates, + void processData(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processData - PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for D0 daughter", true); + PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for B+ and D0 daughter", true); - void processDataWithDmesMl(soa::Filtered> const& candidates, + void processDataWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithDmesMl - PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with ML scores for D0 daughter", false); + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with(out) ML scores for D0 daughter (B+)", false); - void processDataWithBplusMl(soa::Filtered> const& candidates, + void processDataWithDmesMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithDmesMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMlScalarProd, "Process data with(out) ML scores for D0 daughter (B+) and scalar products", false); + + void processDataWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMl, "Process data with(out) ML scores for B+ (D0 daughter)", false); - void processMc(soa::Filtered> const& candidates, + void processDataWithBplusMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithBplusMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMlScalarProd, "Process data with(out) ML scores for B+ (D0 daughter) and scalar products", false); + + void processMc(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -764,37 +820,39 @@ struct HfTaskBplusReduced { } // processMc PROCESS_SWITCH(HfTaskBplusReduced, processMc, "Process MC without ML scores for B+ and D0 daughter", false); - void processMcWithDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc - PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D daughter", false); + } // processMcWithDecayTypeCheck + PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D0 daughter", false); - void processMcWithDmesMl(soa::Filtered> const& candidates, + void processMcWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -804,37 +862,39 @@ struct HfTaskBplusReduced { } // processMcWithDmesMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMl, "Process MC with(out) ML scores for D0 daughter (B+)", false); - void processMcWithDmesMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDmesMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithDmesMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMl(soa::Filtered> const& candidates, + void processMcWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -844,24 +904,25 @@ struct HfTaskBplusReduced { } // processMcWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMl, "Process MC with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithBplusMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithBplusMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); }; // struct diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index c5c3eea19f1..4f75bb6ddfa 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -584,6 +584,9 @@ DECLARE_SOA_TABLE(HfCandCascadeMcGen, "AOD", "HFCANDCASCMCGEN", //! // specific BPlus candidate properties namespace hf_cand_bplus { +DECLARE_SOA_COLUMN(XSecondaryVertexD, xSecondaryVertexD, float); +DECLARE_SOA_COLUMN(YSecondaryVertexD, ySecondaryVertexD, float); +DECLARE_SOA_COLUMN(ZSecondaryVertexD, zSecondaryVertexD, float); DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // D0 index DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, // Impact parameter product for B+ -> J/Psi K [](float px0, float py0, float pz0, float px1, float py1, float pz1, float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float impParK) -> float { return impParK * RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{px0, py0, pz0}, std::array{px1, py1, pz1})); }); @@ -593,6 +596,22 @@ DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPv, cpaDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPvXY, cpaDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToB, cpaDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToBXY, cpaDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPv, decayLengthDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPvXY, decayLengthDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToB, decayLengthDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToBXY, decayLengthDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}); }); enum DecayTypeMc : uint8_t { BplusToD0PiToKPiPi = 0, BplusToD0KToKPiK, @@ -616,7 +635,17 @@ DECLARE_SOA_TABLE(HfCandBplusBase, "AOD", "HFCANDBPLUSBASE", hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + // D meson decay vertex position + hf_cand_bplus::XSecondaryVertexD, hf_cand_bplus::YSecondaryVertexD, hf_cand_bplus::ZSecondaryVertexD, /* dynamic columns */ + hf_cand_bplus::CpaDToPv, + hf_cand_bplus::CpaDToPvXY, + hf_cand_bplus::CpaDToB, + hf_cand_bplus::CpaDToBXY, + hf_cand_bplus::DecayLengthDToPv, + hf_cand_bplus::DecayLengthDToPvXY, + hf_cand_bplus::DecayLengthDToB, + hf_cand_bplus::DecayLengthDToBXY, hf_cand_2prong::M, hf_cand_2prong::M2, hf_cand_2prong::ImpactParameterProduct, diff --git a/PWGHF/TableProducer/candidateCreatorBplus.cxx b/PWGHF/TableProducer/candidateCreatorBplus.cxx index 22ded3ecc7b..8894c7ec60f 100644 --- a/PWGHF/TableProducer/candidateCreatorBplus.cxx +++ b/PWGHF/TableProducer/candidateCreatorBplus.cxx @@ -366,7 +366,8 @@ struct HfCandidateCreatorBplus { pVecD0[0], pVecD0[1], pVecD0[2], pVecBach[0], pVecBach[1], pVecBach[2], impactParameter0.getY(), impactParameter1.getY(), - std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2())); + std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); // index D0 and bachelor } // track loop From 3264d4a62f5f7cc006de5ee7b695d992bea5eb5f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:05:31 +0200 Subject: [PATCH 319/648] [Infrastructure] Properly adapt flags in case of ENABLE_ASSERT (#18329) --- dependencies/O2PhysicsCompileFlags.cmake | 34 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/dependencies/O2PhysicsCompileFlags.cmake b/dependencies/O2PhysicsCompileFlags.cmake index 3aff4b73547..111ddb1bacd 100644 --- a/dependencies/O2PhysicsCompileFlags.cmake +++ b/dependencies/O2PhysicsCompileFlags.cmake @@ -80,12 +80,38 @@ IF (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RELWITHDEBINFO) ENDIF (NOT CMAKE_BUILD_TYPE) +# Adjust the per-config flags rather than replacing them. These used to be plain +# set() calls, which create normal variables that shadow the cache for the rest of +# the configure -- so whatever the build recipe passed as +# -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=... was silently discarded. alidist asks for +# "-O2 -g1 -DNDEBUG" (overridable via O2PHYSICS_DEBUG_FLAGS) and got -g anyway; +# full debug info costs ~18% of total compile time across the tree, because the +# register allocator's rewrite pass scales with the number of debug records. +# Here we only enforce what this project actually cares about: -pipe, and whether +# NDEBUG is defined. The optimisation and debug-info levels stay as given. +function(o2physics_adjust_config_flags varname want_ndebug) + set(flags "${${varname}}") + if(want_ndebug) + if(NOT flags MATCHES "(^| )-DNDEBUG($| )") + string(APPEND flags " -DNDEBUG") + endif() + else() + # ENABLE_CASSERT: the CI wants assertions live. + string(REGEX REPLACE "(^| )-DNDEBUG($| )" " " flags "${flags}") + endif() + if(NOT flags MATCHES "(^| )-pipe($| )") + string(APPEND flags " -pipe") + endif() + string(STRIP "${flags}" flags) + set(${varname} "${flags}" PARENT_SCOPE) +endfunction() + IF(ENABLE_CASSERT) #For the CI, we want to have assertions enabled - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE FALSE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO FALSE) ELSE() - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE TRUE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO TRUE) if (CMAKE_BUILD_TYPE STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") set(FAIR_MIN_SEVERITY "info") endif() From 425c812984d0bc99f46adc7c77332d5035798a70 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Fri, 16 Oct 2026 22:26:39 +0200 Subject: [PATCH 320/648] [PWGLF,PWGMM] Update phi selection for data and mc (#18296) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 79 ++++++++++++++++++------------ 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 00ff92651f5..ab57a5da14e 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -108,6 +109,7 @@ enum class TrkSel { trkSelNCls, trkSelChi2Ncl, trkSelEta, + trkSelPhi, trkSelPhiCut, trkSelPt, trkSelCA, @@ -449,6 +451,7 @@ struct DndetaMFTPbPb { "Ncls", "Chi2", "Eta", + "Phi", "Phi cut", "Pt", "CA"}; @@ -771,7 +774,6 @@ struct DndetaMFTPbPb { using CollsCorr = soa::Join; using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; - /// Tracks using MftTracksLabeled = soa::Join; using MftBestTracksLabeled = soa::Join; @@ -977,8 +979,15 @@ struct DndetaMFTPbPb { if constexpr (fillHis) { registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } + if constexpr (fillHis) { + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhi)); + } if (trackCuts.usephiCut) { - float phi = track.phi(); if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -1023,14 +1032,16 @@ struct DndetaMFTPbPb { continue; } if (fillHis) { + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); registryData.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); registryData.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); } else { registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); registryData.fill(HIST("Tracks/TanLambda"), track.tgl(), occ); registryData.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), occ); } @@ -1062,16 +1073,18 @@ struct DndetaMFTPbPb { } ++nATrk; if (fillHis) { + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); registryData.fill(HIST("Tracks/Centrality/TanLambda"), itrack.tgl(), c, occ); registryData.fill(HIST("Tracks/Centrality/InvQPt"), itrack.signed1Pt(), c, occ); registryData.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); registryData.fill(HIST("Tracks/TanLambda"), itrack.tgl(), occ); registryData.fill(HIST("Tracks/InvQPt"), itrack.signed1Pt(), occ); registryData.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); @@ -1080,13 +1093,15 @@ struct DndetaMFTPbPb { } if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { if (fillHis) { + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { registryData.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); - registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), itrack.phi(), itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), phi, itrack.eta(), c, occ); } else { registryData.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); - registryData.fill(HIST("Tracks/ReTracksPhiEta"), itrack.phi(), itrack.eta(), occ); + registryData.fill(HIST("Tracks/ReTracksPhiEta"), phi, itrack.eta(), occ); } } } @@ -1175,8 +1190,12 @@ struct DndetaMFTPbPb { if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { return false; } + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } if (trackCuts.usephiCut) { - float phi = particle.phi(); if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || @@ -1188,16 +1207,6 @@ struct DndetaMFTPbPb { return true; } - template - bool cutInPhi(P const& particle) - { - if (!trackCuts.usephiCut) { - return true; - } - auto phi = particle.phi(); - return (phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut)); - } - template float getOccupancy(C const& collision, uint occEstimator) { @@ -1358,20 +1367,22 @@ struct DndetaMFTPbPb { if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (isCent) { registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), zvtx, c, occ); - registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), particle.phi(), particle.eta(), c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), phi, particle.eta(), c, occ); } else { registryMC.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx, occ); - registryMC.fill(HIST("Tracks/PhiEtaGen_t"), particle.phi(), particle.eta(), occ); + registryMC.fill(HIST("Tracks/PhiEtaGen_t"), phi, particle.eta(), occ); } if (gtZeroColl) { if constexpr (isCent) { registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); - registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), particle.phi(), particle.eta(), c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), phi, particle.eta(), c, occ); } else { registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); - registryMC.fill(HIST("Tracks/PhiEtaGen"), particle.phi(), particle.eta(), occ); + registryMC.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); } } } @@ -1702,6 +1713,8 @@ struct DndetaMFTPbPb { if (!isTrackSelected(track)) { continue; } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); if (!isChrgParticle(particle.pdgCode())) { @@ -1715,11 +1728,11 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), track.phi(), track.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); } else { registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), track.phi(), track.eta(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occRec); registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); } ++nTrkRec; @@ -1890,6 +1903,8 @@ struct DndetaMFTPbPb { if (!isTrackSelected(itrack)) { continue; } + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); } else { @@ -1939,13 +1954,13 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/PhiEta"), itrack.phi(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); } else { registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); - registryMC.fill(HIST("Tracks/PhiEta"), itrack.phi(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occRec); registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); @@ -2313,7 +2328,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.phi(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2389,13 +2404,13 @@ struct DndetaMFTPbPb { } // MC gen if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } @@ -2548,13 +2563,13 @@ struct DndetaMFTPbPb { } // MC gen if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta && cutInPhi(particle)) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } From 1fed18aeef8449e0dc3960954a7630424710a6c6 Mon Sep 17 00:00:00 2001 From: Fabio Catalano Date: Fri, 16 Oct 2026 23:05:31 +0200 Subject: [PATCH 321/648] [PWGHF] Fix centrality estimator in charm-baryon Qa creator (#18331) --- PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx index 4ee67bd2ec1..b52a48005c2 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx @@ -1651,7 +1651,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); @@ -1667,7 +1667,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); From 23b1cd0d18b2552936cfe10875394bfb38afcb8a Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:12:13 +0200 Subject: [PATCH 322/648] [PWGHF] Adding d0xd0 for beauty in derived data creator to match triggers (#18337) --- .../TableProducer/dataCreatorCharmHadPiReduced.cxx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 49076dc9217..0080ceb0715 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -202,6 +202,7 @@ struct HfDataCreatorCharmHadPiReduced { struct : o2::framework::ConfigurableGroup { Configurable cpaMin{"cpaMin", 0., "Minimum cosine of pointing angle for B candidates"}; Configurable decLenMin{"decLenMin", 0., "Minimum decay length for B candidates"}; + Configurable impParProductMax{"impParProductMax", 10., "Maximum product of the impact parameters of the B daughters (cm^2)"}; } bhadronConfigurations; // HF flags struct : o2::framework::ConfigurableGroup { @@ -1034,8 +1035,9 @@ struct HfDataCreatorCharmHadPiReduced { /// \param momentum is the B meson momentum /// \param secondaryVertex is the reconstructed secondary vertex /// \param collision is the reconstructed collision + /// \param impParProduct is the product of the impact parameters of the B daughters template - bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision) + bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision, const float impParProduct) { // B candidate CPA if (RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, momentum) < bhadronConfigurations.cpaMin) { @@ -1047,6 +1049,11 @@ struct HfDataCreatorCharmHadPiReduced { return false; } + // B candidate product of daughter impact parameters + if (impParProduct > bhadronConfigurations.impParProductMax) { + return false; + } + return true; } @@ -1364,7 +1371,10 @@ struct HfDataCreatorCharmHadPiReduced { df2.getTrack(0).getPxPyPzGlo(pVecDHad); df2.getTrack(1).getPxPyPzGlo(pVecDau); pVecBHad = RecoDecay::pVec(pVecDHad, pVecDau); - if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision)) { + o2::track::TrackParCov trackParCovCharmHadAtPv = trackParCovCharmHad; + std::array dcaCharmHad{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCovCharmHadAtPv, 2.f, noMatCorr, &dcaCharmHad); + if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision, dcaCharmHad[0] * dcaPion[0])) { continue; } // fill Pion tracks table From 0bcb21b422bab78f106ed4f2ebe6b081cad2591a Mon Sep 17 00:00:00 2001 From: Mattia Faggin Date: Fri, 16 Oct 2026 23:41:39 +0200 Subject: [PATCH 323/648] [PWGHF] bug fix in taskD0 - swap D0 prompt and D0 non-prompt ML scores. (#18335) Co-authored-by: Mattia Faggin --- PWGHF/D2H/Tasks/taskD0.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskD0.cxx b/PWGHF/D2H/Tasks/taskD0.cxx index 66d9c90d620..3b0f37b233d 100644 --- a/PWGHF/D2H/Tasks/taskD0.cxx +++ b/PWGHF/D2H/Tasks/taskD0.cxx @@ -389,8 +389,8 @@ struct HfTaskD0 { axes.insert(axes.begin() + 2, thnAxisPromptScore); axes.insert(axes.begin() + 2, thnAxisBkgScore); } else { - axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisNonPromptScore); + axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisBkgScore); } } From fd3668f18c4f509d9221dbf9090e2d04c66ffc55 Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Sat, 17 Oct 2026 00:39:28 +0200 Subject: [PATCH 324/648] [PWGLF] Nuspex: hadron nuclei correlation: fix proton rejection (#18333) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 96615e4414e..90717213795 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -622,13 +622,13 @@ struct HadronNucleiCorrelation { const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; - const bool isNotPr = (std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value) || !doPrRej.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; // Check if the sign of the track matches the expected sign for deuterons or antideuterons const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); - if (!isNotPr) { + if (isPr) { return false; } if (!doQuadraticPID.value) { @@ -1031,7 +1031,7 @@ struct HadronNucleiCorrelation { const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; - const bool isNotPr = (std::abs(track.tpcNSigmaPr()) >= nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) >= nsigmaTOFPrRej.value) || !doPrRej.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; if (track.sign() > 0) { @@ -1039,7 +1039,7 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { - if (isNotPr) { + if (!isPr) { registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); if (isTPCElRejection || isTOFPID) { registryQa.fill(HIST("QA/h3dTPCTOF_De_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); @@ -1052,7 +1052,7 @@ struct HadronNucleiCorrelation { registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { - if (isNotPr) { + if (!isPr) { registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); if (isTPCElRejection || isTOFPID) { registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); From 83f14127eda245cd467c4dac852af8eb130f4574 Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:44:57 -0500 Subject: [PATCH 325/648] [PWGHF] Fix signDecay assignment in OmegaC QA KFParticle candidate creator (#18336) --- PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx index b52a48005c2..67f4bee267d 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx @@ -1290,7 +1290,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { 0.f, 0.f, 0.f, // -> vertexCharmBaryonFromFitter. For KF, this is 0 vertexCasc[0], vertexCasc[1], vertexCasc[2], vertexV0[0], vertexV0[1], vertexV0[2], - trackCharmBachelor.sign(), + bachTrack.sign(), covVtxCharmBaryon[0], covVtxCharmBaryon[1], covVtxCharmBaryon[2], covVtxCharmBaryon[3], covVtxCharmBaryon[4], covVtxCharmBaryon[5], kfCharmBaryon.GetPx(), kfCharmBaryon.GetPy(), kfCharmBaryon.GetPz(), kfCascToCharmBaryon.GetPx(), kfCascToCharmBaryon.GetPy(), kfCascToCharmBaryon.GetPz(), From 4534b7d18c23cd837dd3c5742746a91f1f4c682d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:11:31 +0200 Subject: [PATCH 326/648] [PWGLF] Precompiled headers specific for strangeness (#18328) --- PWGLF/CMakeLists.txt | 3 +- .../TableProducer/Strangeness/CMakeLists.txt | 31 +++++++++++++++ .../Strangeness/Converters/CMakeLists.txt | 32 ++++++++++++++++ PWGLF/Tasks/QC/CMakeLists.txt | 16 ++++++++ PWGLF/Tasks/Strangeness/CMakeLists.txt | 31 +++++++++++++++ PWGLF/Utils/CMakeLists.txt | 38 +++++++++++++++++++ PWGLF/Utils/strangenessPCH.cxx | 13 +++++++ 7 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 PWGLF/Utils/strangenessPCH.cxx diff --git a/PWGLF/CMakeLists.txt b/PWGLF/CMakeLists.txt index 797ac01500f..2aa66ef9fb4 100644 --- a/PWGLF/CMakeLists.txt +++ b/PWGLF/CMakeLists.txt @@ -11,6 +11,7 @@ # add_subdirectory(Core) # add_subdirectory(DataModel) +# Utils first: it owns the LfStrangenessPCH carrier the workflows reuse. +add_subdirectory(Utils) add_subdirectory(Tasks) add_subdirectory(TableProducer) -add_subdirectory(Utils) diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index a61a01e6a56..6ba1048506f 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -13,26 +13,31 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(cascadefinder SOURCES cascadefinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeflow SOURCES cascadeflow.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcbuilder SOURCES cascademcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcfinder SOURCES cascademcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenesstofpid SOURCES strangenesstofpid.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -43,36 +48,43 @@ o2physics_add_dpl_workflow(cascadespawner o2physics_add_dpl_workflow(cascqaanalysis SOURCES cascqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascderivedqaanalysis SOURCES cascderivedqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hstrangecorrelationfilter # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES hStrangeCorrelationFilter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(double-omega-tree-creator SOURCES doubleOmegaTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzerofinder SOURCES lambdakzerofinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcbuilder SOURCES lambdakzeromcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcfinder SOURCES lambdakzeromcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -88,6 +100,7 @@ o2physics_add_dpl_workflow(sigmaminus-task # o2-linter: disable=name/o2-workflow o2physics_add_dpl_workflow(sigmaplusbuilder SOURCES sigmaplusbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -98,77 +111,92 @@ o2physics_add_dpl_workflow(strange-tree-creator o2physics_add_dpl_workflow(st-coll-ids SOURCES stCollIds.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangederivedbuilder SOURCES strangederivedbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenessbuilder SOURCES strangenessbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0-selector # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES v0selector.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0qaanalysis SOURCES v0qaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdalambdatable # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES LambdaLambdatable.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing COMPONENT_NAME Analysis) # ML selection o2physics_add_dpl_workflow(lambdakzeromlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdakzeroMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES cascadeMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromlselection SOURCES lambdakzeromlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademlselection SOURCES cascademlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigma0builder SOURCES sigma0builder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationbuilder # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetpolarizationbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationions # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetPolarizationIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracents SOURCES stracents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdaspincorrelation SOURCES lambdaspincorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -180,16 +208,19 @@ o2physics_add_dpl_workflow(sigmahadcorr # o2-linter: disable=name/o2-workflow (K if(FastJet_FOUND) o2physics_add_dpl_workflow(lfinjets SOURCES lfinjets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) endif() o2physics_add_dpl_workflow(phi-strange-correlator SOURCES phiStrangeCorrelator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangeness-mc-centrality SOURCES strangenessMcCentrality.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt index e1d42ea534a..64bcc3a249d 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(stradautracksconverter SOURCES stradautracksconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,155 +22,186 @@ o2physics_add_dpl_workflow(stradautrackstpcpidconverter o2physics_add_dpl_workflow(stradautrackstofpidconverter SOURCES stradautrackstofpidconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter2 SOURCES stradautrackstofpidconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter3 SOURCES stradautrackstofpidconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter4 SOURCES stradautrackstofpidconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter SOURCES stradautracksextraconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter2 SOURCES stradautracksextraconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter3 SOURCES stradautracksextraconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter SOURCES strarawcentsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter2v4 SOURCES strarawcentsconverter2v4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter SOURCES straevselsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2 SOURCES straevselsconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter3 SOURCES straevselsconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter4 SOURCES straevselsconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter5 SOURCES straevselsconverter5.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter6 SOURCES straevselsconverter6.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselextrasconverter SOURCES straevselextrasconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselextrasconverter2 SOURCES straevselextrasconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents SOURCES straevselsconverter2rawcents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents2 SOURCES straevselsconverter2rawcents2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents3 SOURCES straevselsconverter2rawcents3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter SOURCES v0coresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter2 SOURCES v0coresconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0mlscoresconverter SOURCES v0mlscoresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollisionconverter SOURCES stramccollisionconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollisionconverter2 SOURCES stramccollisionconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strastampsconverter SOURCES strastampsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter SOURCES stracentconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter2 SOURCES stracentconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollmultconverter SOURCES stramccollmultconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollmultconverter2 SOURCES stramccollmultconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(zdcneutronsconverter SOURCES zdcneutronsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/QC/CMakeLists.txt b/PWGLF/Tasks/QC/CMakeLists.txt index e1618a43d06..8185c1c91e4 100644 --- a/PWGLF/Tasks/QC/CMakeLists.txt +++ b/PWGLF/Tasks/QC/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(v0cascades-qa SOURCES v0cascadesqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) @@ -31,11 +32,13 @@ o2physics_add_dpl_workflow(pid-qa o2physics_add_dpl_workflow(strarecostudy SOURCES straRecoStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfperformancestudy SOURCES kfPerformanceStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -46,6 +49,7 @@ o2physics_add_dpl_workflow(its-tpc-matching-qa o2physics_add_dpl_workflow(strangeness-tracking-qc SOURCES strangenessTrackingQC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -62,11 +66,13 @@ o2physics_add_dpl_workflow(lfpropstudy o2physics_add_dpl_workflow(tpc-dedx-postcalibration SOURCES tpc_dEdx_postcalibration.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tpc-dedx-qa SOURCES tpc_dedx_qa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -87,21 +93,25 @@ o2physics_add_dpl_workflow(strangenessqcpp o2physics_add_dpl_workflow(strangepidqa SOURCES strangepidqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stqa SOURCES stqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfstrangenessstudy SOURCES kfStrangenessStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(findable-study SOURCES findableStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore O2Physics::v0SelectionGroup COMPONENT_NAME Analysis) @@ -112,6 +122,7 @@ o2physics_add_dpl_workflow(mcinelgt0 o2physics_add_dpl_workflow(tracked-cascade-properties SOURCES trackedCascadeProperties.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -122,26 +133,31 @@ o2physics_add_dpl_workflow(mc-particle-predictions o2physics_add_dpl_workflow(strange-derived-qa SOURCES strangederivedqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(str-derived-genqa SOURCES strderivedGenQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(its-tpc-matching-vzeros SOURCES lfITSTPCMatchingSecondaryTracksQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0assoqa SOURCES v0assoqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(systematics-mapping SOURCES systematicsMapping.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 5d3711f586a..5ddab116838 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -11,26 +11,31 @@ o2physics_add_dpl_workflow(hyperon-reco-test SOURCES hyperon-reco-test.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedlambdakzeroanalysis SOURCES derivedlambdakzeroanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeroanalysis-mc SOURCES lambdakzeroanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysis SOURCES cascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysismc SOURCES cascadeanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +46,13 @@ o2physics_add_dpl_workflow(v0postprocessing o2physics_add_dpl_workflow(cascadecorrelations SOURCES cascadecorrelations.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(non-prompt-cascade SOURCES nonPromptCascade.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -56,21 +63,25 @@ o2physics_add_dpl_workflow(k0mixedevents o2physics_add_dpl_workflow(xi-lambda-corr-task SOURCES xiLambdaCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(vzero-cascade-absorption SOURCES vzero_cascade_absorption.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedcascadeanalysis SOURCES derivedcascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedxi0analysis SOURCES derivedXi0analysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) @@ -81,16 +92,19 @@ o2physics_add_dpl_workflow(cascpostprocessing o2physics_add_dpl_workflow(hstrangecorrelation SOURCES hStrangeCorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigmaanalysis SOURCES sigmaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phik0shortanalysis SOURCES phik0shortanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -101,37 +115,44 @@ o2physics_add_dpl_workflow(phi-strange-correlation o2physics_add_dpl_workflow(lambdapolarization SOURCES lambdapolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdapolsp SOURCES lambdapolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0seff SOURCES lambdak0seff.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lambda-spin-corr SOURCES taskLambdaSpinCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascpolsp SOURCES cascpolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) if(FastJet_FOUND) o2physics_add_dpl_workflow(strangeness-in-jets SOURCES strangenessInJets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangeness-in-jets-ions SOURCES strangenessInJetsIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -143,31 +164,37 @@ endif() o2physics_add_dpl_workflow(v0topologicalcuts SOURCES v0topologicalcuts.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0ptinvmassplots SOURCES v0ptinvmassplots.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedupcanalysis SOURCES derivedupcanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0sflattenicity SOURCES lambdak0sflattenicity.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdalambda SOURCES lambdalambda.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarization SOURCES lambdaJetpolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -183,21 +210,25 @@ o2physics_add_dpl_workflow(lambdaspincorrderived o2physics_add_dpl_workflow(strangenessderivedbinnedinfo SOURCES strangenessderivedbinnedinfo.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambda-two-part-polarization SOURCES lambdaTwoPartPolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysislightions SOURCES cascadeAnalysisLightIonsDerivedData.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strange-casc-track SOURCES strangeCascTrack.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGLF/Utils/CMakeLists.txt b/PWGLF/Utils/CMakeLists.txt index b81a6eb7198..afa87d8a86c 100644 --- a/PWGLF/Utils/CMakeLists.txt +++ b/PWGLF/Utils/CMakeLists.txt @@ -40,3 +40,41 @@ o2physics_add_library(LfStrangenessBuilderModule o2physics_add_library(LfCascadeMlResponse SOURCES cascadeMlResponse.cxx PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across the PWGLF strangeness workflows. Percentages +# are the share of the group's workflow translation units that include the header, +# measured over the tree. LFStrangenessPIDTables.h is deliberately left out: it +# reaches only 26% of them, and it includes LFStrangenessTables.h anyway. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(LfStrangenessPCH OBJECT strangenessPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET LfStrangenessPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(LfStrangenessPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(LfStrangenessPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGLF/DataModel/LFStrangenessTables.h"]] # 100%, 2045 lines + ) +endif() diff --git a/PWGLF/Utils/strangenessPCH.cxx b/PWGLF/Utils/strangenessPCH.cxx new file mode 100644 index 00000000000..e9bcd249723 --- /dev/null +++ b/PWGLF/Utils/strangenessPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the LfStrangenessPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. From e9eff5782b5c6c48ecae1aaf8d38e997e7a58d3b Mon Sep 17 00:00:00 2001 From: VDiBella <162152869+VDiBella@users.noreply.github.com> Date: Sat, 17 Oct 2026 10:53:09 +0200 Subject: [PATCH 327/648] [PWGHF] Adding : ML and skimming in double Dplus correlator + other minor change (#18125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Valerio Di Bella Co-authored-by: Vít Kučera <26327373+vkucera@users.noreply.github.com> --- .../HFC/DataModel/ReducedDMesonPairsTables.h | 48 +++---- PWGHF/HFC/TableProducer/CMakeLists.txt | 2 +- .../correlatorDplusDplusReduced.cxx | 130 ++++++++++++++++-- .../taskCorrelationDplusDplusReduced.cxx | 129 ++++++++++++++--- 4 files changed, 257 insertions(+), 52 deletions(-) diff --git a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h index 9c4484de852..42724605adf 100644 --- a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h @@ -38,6 +38,8 @@ DECLARE_SOA_TABLE(HfCandDpMcEvs, "AOD", "HFCANDDPMCEV", namespace full { +DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); +DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); DECLARE_SOA_COLUMN(RSecondaryVertex, rSecondaryVertex, float); //! Radius of secondary vertex (cm) DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); //! Transverse momentum of prong0 (GeV/c) DECLARE_SOA_COLUMN(PProng0, pProng0, float); //! Momentum of prong0 (GeV/c) @@ -57,8 +59,6 @@ DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuth angle of candidate DECLARE_SOA_COLUMN(E, e, float); //! Energy of candidate (GeV) DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Collision centrality -DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); //! The Mc collision index this MC particles belongs to -DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); //! The collision index this candidate belongs to DECLARE_SOA_COLUMN(NSigTpcPi0, nSigTpcPi0, float); //! TPC Nsigma separation for prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKa0, nSigTpcKa0, float); //! TPC Nsigma separation for prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi0, nSigTofPi0, float); //! TOF Nsigma separation for prong0 with pion mass hypothesis @@ -71,12 +71,12 @@ DECLARE_SOA_COLUMN(NSigTpcPi2, nSigTpcPi2, float); DECLARE_SOA_COLUMN(NSigTpcKa2, nSigTpcKa2, float); //! TPC Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi2, nSigTofPi2, float); //! TOF Nsigma separation for prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofKa2, nSigTofKa2, float); //! TOF Nsigma separation for prong2 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi0, nSigTpcTofPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa0, nSigTpcTofKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi1, nSigTpcTofPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa1, nSigTpcTofKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi2, nSigTpcTofPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa2, nSigTpcTofKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi0, tpcTofNSigmaPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa0, tpcTofNSigmaKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi1, tpcTofNSigmaPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa1, tpcTofNSigmaKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi2, tpcTofNSigmaPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa2, tpcTofNSigmaKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -96,7 +96,7 @@ DECLARE_SOA_TABLE(HfCandDpMls, "AOD", "HFCANDDPML", full::MlScore0, full::MlScore1) -DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", +DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", o2::soa::Index<>, full::CandidateSelFlag, full::M, full::Pt, @@ -107,7 +107,7 @@ DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", +DECLARE_SOA_TABLE(HfCandDpSimples, "AOD", "HFCANDDPSIMPLE", o2::soa::Index<>, hf_cand::Chi2PCA, full::DecayLength, full::DecayLengthXY, @@ -126,20 +126,20 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -155,7 +155,7 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", +DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", o2::soa::Index<>, hf_cand::XSecondaryVertex, hf_cand::YSecondaryVertex, hf_cand::ZSecondaryVertex, @@ -201,20 +201,20 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -233,7 +233,7 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec); -DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", +DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", o2::soa::Index<>, full::Pt, full::Eta, full::Phi, diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index 8da21e3474a..87d3e12ea40 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -42,7 +42,7 @@ o2physics_add_dpl_workflow(correlator-dplus-dminus o2physics_add_dpl_workflow(correlator-dplus-dplus-reduced SOURCES correlatorDplusDplusReduced.cxx REUSE_FROM HfCandidatesPCH - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-hadrons diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx index d58da7fccd6..096e5420005 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx @@ -20,6 +20,8 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/HfMlResponseDplusToPiKPi.h" +#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h" @@ -30,10 +32,12 @@ #include "Common/DataModel/Centrality.h" #include +#include #include #include #include #include +#include #include #include #include @@ -41,9 +45,12 @@ #include #include +#include +#include #include using namespace o2; +using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::hf_centrality; @@ -51,7 +58,7 @@ using namespace o2::hf_centrality; /// Writes the full information in an output TTree struct HfCorrelatorDplusDplusReduced { Produces rowCandidateFull; - Produces rowCandidateLite; + Produces rowCandidateLite; Produces rowCandidateTiny; Produces rowCandidateFullEvents; Produces rowCandidateMl; @@ -69,6 +76,34 @@ struct HfCorrelatorDplusDplusReduced { Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", true, "Enables processing of skimmed datasets"}; Configurable skipSingleD{"skipSingleD", true, "Skip collisions with one or less D candidates"}; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTDPlus"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_DPlusToKPiPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + Configurable> cutPtSkimming{"cutPtSkimming", {1, 5, 1000}, "pT bin limits for Skimming application"}; + Configurable> massMin{"massMin", {1.7, 1.7}, "Mass minimal for the cut for each pt bin"}; + Configurable> massMax{"massMax", {2.0, 2.1}, "Mass maximal for the cut for each pt bin"}; + Configurable> cosThetaMin{"cosThetaMin", {0.96, 0.98}, "CosTheta minimal for the cut for each pt bin"}; + Configurable> decayLengthMin{"decayLengthMin", {0.02, 0.03}, "DecayLength minimal for the cut for each pt bin"}; + Configurable> nSigmaTpcMax{"nSigmaTpcMax", {3, 3}, "NsigmaTPC maximal for the cut for each pt bin"}; + Configurable> nSigmaTofMax{"nSigmaTofMax", {3, 3}, "NsigmaTOF maximal for the cut for each pt bin"}; + + Configurable> binsPtSkimming{"binsPtSkimming", {0}, "pT bin limits for Skimming application"}; + + HfMlResponseDplusToPiKPi hfMlResponse; + + std::vector outputMl; + o2::ccdb::CcdbApi ccdbApi; + HfHelper hfHelper; Service ccdb; @@ -102,6 +137,39 @@ struct HfCorrelatorDplusDplusReduced { if (cfgSkimmedProcessing) { zorroSummary.setObject(zorro.getZorroSummary()); } + + if (applyMl) { + hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponse.setModelPathsLocal(onnxFileNames); + } + hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponse.init(); + } + } + + bool skimming(auto const& candidate) + { + if (candidate.pt() < cutPtSkimming.value.at(0) || candidate.pt() > cutPtSkimming.value.at(cutPtSkimming.value.size() - 1)) { + return false; + } + for (size_t i = 0; i < cutPtSkimming.value.size() - 1; i++) { + if (candidate.pt() <= cutPtSkimming.value.at(i + 1)) { + if (hfHelper.invMassDplusToPiKPi(candidate) < massMin.value.at(i) || + hfHelper.invMassDplusToPiKPi(candidate) > massMax.value.at(i) || + candidate.cpa() < cosThetaMin.value.at(i) || + candidate.decayLength() < decayLengthMin.value.at(i) || + std::abs(candidate.nSigTofKa1()) > nSigmaTofMax.value.at(i) || + std::abs(candidate.nSigTpcKa1()) > nSigmaTpcMax.value.at(i)) { + return false; + } + return true; + } + } + return false; } template @@ -127,14 +195,14 @@ struct HfCorrelatorDplusDplusReduced { channelMc = candidate.flagMcDecayChanRec(); } - std::vector outputMl = {-999., -999.}; + std::vector mlProb = {-999., -999.}; if constexpr (DoMl) { for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + mlProb[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; } rowCandidateMl( - outputMl[0], - outputMl[1]); + mlProb[0], + mlProb[1]); } float cent{-1.}; @@ -291,7 +359,9 @@ struct HfCorrelatorDplusDplusReduced { static int lastRunNumber = -1; // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -313,13 +383,31 @@ struct HfCorrelatorDplusDplusReduced { const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -332,7 +420,9 @@ struct HfCorrelatorDplusDplusReduced { { // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -341,13 +431,30 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& collision : collisions) { // No skimming for MC data. No Zorro ! const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -364,9 +471,12 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& mccollision : mccollisions) { // No skimming for MC data. No Zorro ! const auto colId = mccollision.globalIndex(); const auto particlesInThisCollision = mcparticles.sliceBy(mcParticlesPerMcCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (particlesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } rowCandidateMcCollisions( mccollision.posX(), mccollision.posY(), diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx index c37c1f73feb..a1746b6663a 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx @@ -30,6 +30,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -37,8 +38,13 @@ using namespace o2::framework::expressions; struct HfTaskCorrelationDplusDplusReduced { Configurable selectionFlagDplus{"selectionFlagDplus", 1, "Selection Flag for Dplus"}; + Configurable pseudorapidityMax{"pseudorapidityMax", 1, "Selection the cut of rapidity"}; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; - using SelectedCandidates = soa::Filtered; + using SelectedCandidatesTiny = soa::Filtered; + using SelectedCandidatesLite = soa::Filtered; + using SelectedCandidatesFull = soa::Filtered; using SelectedMcParticles = o2::aod::HfCandDpMcPs; Filter filterSelectCandidates = aod::full::candidateSelFlag >= selectionFlagDplus; @@ -54,24 +60,28 @@ struct HfTaskCorrelationDplusDplusReduced { void init(InitContext const&) { - registry.add("hMassDplus", "D+ candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminus", "D- candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusMatched", "D+ matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminusMatched", "D- matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusminusPair", "D plus-minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDplusPair", "D plus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDminusPair", "D minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplus", "D^{+} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminus", "D^{-} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusMatched", "D^{+} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminusMatched", "D^{-} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPairsMatched", "D^{+} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPairsMatched", "D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPairsMatched", "D^{+} and D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPair", "D^{+-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPair", "D^{+} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPair", "D^{-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); registry.add("hDltPhiMcGen", "Azimuthal correlation for D mesons; #Delta#phi", {HistType::kTH1F, {{100, -3.141593, 3.141593}}}); } - void processLocalData(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processData(const T& localCandidates) { registry.fill(HIST("hNCand"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); @@ -85,6 +95,12 @@ struct HfTaskCorrelationDplusDplusReduced { if (cand2.pt() < 0) { sign2 = -1; } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } if (sign1 == sign2) { if (sign1 == 1) { registry.fill(HIST("hMassDplusPair"), mass2, mass1); @@ -97,27 +113,104 @@ struct HfTaskCorrelationDplusDplusReduced { } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalData, "Process local data", true); - void processLocalDataMcRec(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processLocalDataMcRec(const T& localCandidates) { registry.fill(HIST("hNMcRec"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); + auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { + sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDminusMatched"), mass1); + } } else { registry.fill(HIST("hMassDplus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDplusMatched"), mass1); + } + } + + for (auto cand2 = cand1 + 1; cand2 != localCandidates.end(); ++cand2) { + auto mass2 = cand2.m(); + auto sign2 = 1; + if (cand2.pt() < 0) { + sign2 = -1; + } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } + if (sign1 == sign2) { + if (sign1 == 1) { + registry.fill(HIST("hMassDplusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusPairsMatched"), mass1, mass2); + } + } else { + registry.fill(HIST("hMassDminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDminusPairsMatched"), mass1, mass2); + } + } + } else { + registry.fill(HIST("hMassDplusminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusminusPairsMatched"), mass1, mass2); + } + } } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRec, "Process local MC data", false); + + void processLocalDataTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataTiny, "Process local data for Tiny", false); + + void processLocalDataLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataLite, "Process local data for Lite", false); + + void processLocalDataFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataFull, "Process local data for Full", true); + + void processLocalDataMcRecTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecTiny, "Process local MC data for Tiny", false); + + void processLocalDataMcRecLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecLite, "Process local MC data for Lite", false); + + void processLocalDataMcRecFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecFull, "Process local MC data for Full", false); void processLocalDataMcGen(o2::aod::HfCandDpMcEvs::iterator const&, SelectedMcParticles const& localMcParticles) @@ -126,7 +219,9 @@ struct HfTaskCorrelationDplusDplusReduced { for (const auto& part1 : localMcParticles) { for (auto part2 = part1 + 1; part2 != localMcParticles.end(); ++part2) { - registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + if (std::abs(part1.eta()) < pseudorapidityMax && std::abs(part2.eta()) < pseudorapidityMax) { + registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + } } } } From c4f3a1e538afd7cebaf80b945a6b5ab3a8b9fbda Mon Sep 17 00:00:00 2001 From: omvazque Date: Sat, 17 Oct 2026 09:27:08 -0600 Subject: [PATCH 328/648] [PWGLF] Hist to get Event/Signal loss based on Fwd Nch (#18339) --- PWGLF/Tasks/Nuspex/piKpRAA.cxx | 117 ++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 17 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index 7a4fb0200c8..16edb8c98be 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -128,6 +128,12 @@ struct PiKpRAA { static constexpr float KmaxdEdxMIP{60.0f}; static constexpr float KmindEdxMIPPlateau{70.0f}; static constexpr float KmaxdEdxMIPPlateau{90.0f}; + static constexpr float KminFV0A{2.2f}; + static constexpr float KmaxFV0A{5.1f}; + static constexpr float KminFT0A{3.5f}; + static constexpr float KmaxFT0A{4.9f}; + static constexpr float KminFT0C{-3.3f}; + static constexpr float KmaxFT0C{-2.1f}; static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; Configurable> lifetimecut{"lifetimecut", {DefaultLifetimeCuts[0], 2, {"lifetimecutLambda", "lifetimecutK0S"}}, "lifetimecut"}; @@ -253,6 +259,7 @@ struct PiKpRAA { ConfigurableAxis axisPtFineFixedWidth{"axisPtFineFixedWidth", {250, 0.1f, 20.1f}, "DCAxy axis"}; ConfigurableAxis axisNPV{"axisNPV", {600, 0.0f, 600.0f}, "Number of Primary Vertex Contributors"}; ConfigurableAxis axisNch{"axisNch", {400, 0.0f, 400.0f}, "N_{ch} Multiplicity"}; + ConfigurableAxis axisFineCent{"axisFineCent", {100, 0.0, 100.0}, "Fine centrality bins"}; Configurable> vecLowEta{"vecLowEta", {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6}, "lower eta cuts"}; Configurable> vecUpEta{"vecUpEta", {-0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}, "upper eta cuts"}; @@ -496,7 +503,7 @@ struct PiKpRAA { } if (doprocessSignalLoss) { - registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{5, 0, 5}}); + registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{7, 0, 7}}); registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); @@ -514,7 +521,7 @@ struct PiKpRAA { registry.add("NumOfRecColl", "Num. of times a MC evt. is reconstructed;N;Entries", kTH1F, {{5, -0.5, 4.5}}); registry.add("NumOfRecCollVsNContri", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{8, -0.5, 7.5}, axisNch}); - registry.add("NumOfRecCollVsCentrality", "Num. of times a MC evt. is reconstructed VS Centrality WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{8, -0.5, 7.5}, axisCent}); + registry.add("NumOfRecCollVsCentrality", "Num. of times a MC evt. is reconstructed VS Centrality WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {axisCent, {8, -0.5, 7.5}}); // PT RESOLUTION registry.add("PtResolution", "p_{T} resolution;;(pt_{rec} - pt_{gen})/pt_{gen};", kTH2F, {axisPt, {100, -1.0, 1.0}}); @@ -549,14 +556,48 @@ struct PiKpRAA { registry.add("NchMC_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV registry.add("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); - // MC NCH DISTRIBUTION ASSOCIATED TO ALL THE RECONSTRUCTED COLLISIONS & NO EVT. SEL. (QA PURPOSES) - registry.add("NchMC_WithRecColl", "MC Nch from the event associated with at least one Rec. Coll.;Gen. Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE EVENT SPLITTING. registry.add("Centrality_WRecoEvtWSelCri", "From the association between the MC event & the Rec. Coll with largest number of PV contributors + Evt. Sel.;;Entries", kTH1F, {axisCent}); // DENOMINATOR OF THE EVENT SPLITTING. registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); + // ======================================= + + // USED FOR CALCULATING EVENT LOSS AND SIGNAL LOSS AS IN THE DN/DETA ANALYSIS + registry.add("Centrality_FineBins_WithRecoEvt", "Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("Centrality_FineBins_WithRecoEvt_BestCollIdx", "Rec. Coll. W/best Coll Indx + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("NchMC_AllGen_Fwd", "MC Nch of ALL events;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt", "True Nch from MC events W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_BestCollIdx", "True Nch from MC events W/best Coll Idx + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMINATOR OF THE SIGNAL LOSS + registry.add("PtPiVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMITOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // ======================================= + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV registry.add("PtPiVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); registry.add("PtKaVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); @@ -589,6 +630,7 @@ struct PiKpRAA { xMcCounter->SetBinLabel(3, "nContributors > 0 + HasFT0"); xMcCounter->SetBinLabel(4, "nContributors > 0 + HasFT0 & HasTVX"); xMcCounter->SetBinLabel(5, "nContributors > 0 + HasFT0 & HasTVX & Only largest Contributors"); + xMcCounter->SetBinLabel(6, "W/Asso Rec. Coll + Evt. Sel + bestCollIdx"); } LOG(info) << "\trequireGoodRct=" << requireGoodRct.value; @@ -1264,6 +1306,10 @@ struct PiKpRAA { // ONLY ETA SELECTION IS IMPOSED //--------------------------- int nChMCTPCAcc{0}; + int nChMCFV0A{0}; + int nChMCFT0C{0}; + int nChMCFT0M{0}; + int nChMCForward{0}; for (const auto& particle : mcParticles) { auto charge{0.}; @@ -1319,6 +1365,26 @@ struct PiKpRAA { if (std::abs(eta) < tpcNchAcceptance) { nChMCTPCAcc++; } + if (eta > KminFV0A && eta < KmaxFV0A) { + nChMCFV0A++; + } + if (eta > KminFT0C && eta < KmaxFT0C) { + nChMCFT0C++; + } + if ((eta > KminFT0A && eta < KmaxFT0A) || (eta > KminFT0C && eta < KmaxFT0C)) { + nChMCFT0M++; + } + } + + // DEFINE THE MC NCH MULTIPLICITY BASED ON THE CENTRALITY ESTIMATOR + if (centralitySelector.value == "FT0C") { + nChMCForward = nChMCFT0C; + } else if (centralitySelector.value == "FT0M") { + nChMCForward = nChMCFT0M; + } else if (centralitySelector.value == "FV0A") { + nChMCForward = nChMCFV0A; + } else { + nChMCForward = -999; } //--------------------------- @@ -1372,11 +1438,14 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS } else { continue; } @@ -1387,6 +1456,7 @@ struct PiKpRAA { //--------------------------- // DENOMINATOR OF THE EVENT LOSS: ALL THE GENERATED EVENTS + registry.fill(HIST("NchMC_AllGen_Fwd"), nChMCForward); registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); registry.fill(HIST("zPosMCAll"), mccollision.posZ()); @@ -1421,7 +1491,7 @@ struct PiKpRAA { registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED - registry.fill(HIST("NumOfRecCollVsCentrality"), nRecColls, centrality); + registry.fill(HIST("NumOfRecCollVsCentrality"), centrality, nRecColls); //--------------------------- // GETS THE BESTCOLLISION INDEX @@ -1439,10 +1509,6 @@ struct PiKpRAA { //--------------------------- registry.fill(HIST("EventCounterMC"), 1.5); - // NCH IN |ETA| < TPC_ACCEPTANCE - // BELONGING TO A MC EVENT, WHICH POSSIBLY WAS RECONSTRUCTED MULTIPLE TIMES - registry.fill(HIST("NchMC_WithRecColl"), nChMCTPCAcc); - //--------------------------- // COLLISIONS WITH FT0 INFO //--------------------------- @@ -1538,22 +1604,28 @@ struct PiKpRAA { if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { if (hasBcFT0TVX) { registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); if (hasBcFT0TVX && isBestCollIndex) { registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } } } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { if (hasBcFT0TVX) { registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); if (hasBcFT0TVX && isBestCollIndex) { registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } } } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { if (hasBcFT0TVX) { registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); if (hasBcFT0TVX && isBestCollIndex) { registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } } } else { @@ -1659,6 +1731,8 @@ struct PiKpRAA { registry.fill(HIST("NchMCVsCent_WithRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt"), nChMCForward); // Nch MC Forward for (const auto& particle : mcParticles) { if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { @@ -1701,11 +1775,14 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else { continue; } @@ -1807,6 +1884,9 @@ struct PiKpRAA { registry.fill(HIST("NchMCVsCent_WithRecoEvt_BestCollIdx"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt_BestCollIdx"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("EventCounterMC"), 5.5); // COUNTER: ASSOC. REC. COLL + EVT SEL + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_BestCollIdx"), nChMCForward); // Nch MC Forward //--------------------------- // THE GENERATED EVENTS ARE NOT SUBJECTED TO ANY SELECTION CRITERIA @@ -1854,14 +1934,17 @@ struct PiKpRAA { } if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); registry.fill(HIST("PtKaVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); registry.fill(HIST("PtPrVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS } else { continue; } From edaf1f15563b09bd34a967ee28d566643b88c63d Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sun, 18 Oct 2026 04:38:20 +0800 Subject: [PATCH 329/648] [PWGCF] Add cuts counter and experimental eta gap (#18340) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 172 ++++++++++++------ 1 file changed, 117 insertions(+), 55 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 870f5b8f331..74654d087f8 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -48,7 +48,6 @@ #include #include -#include #include #include @@ -185,9 +184,9 @@ static constexpr std::array CutsNames = {"eBefo static constexpr std::array TechnicalCutsNames = {"NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof"}; -static constexpr std::array EventCutsNames = {"Sel8Cut", "CentCut", "VertexZCut", "NContribCut"}; +static constexpr std::array EventCutsNames = {"Sel8", "Cent", "VertexZ", "NContrib"}; -static constexpr std::array ParticleCutsNames = {"PtCut", "EtaCut", "ChargeCut"}; +static constexpr std::array ParticleCutsNames = {"Pt", "Eta", "Charge"}; // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root @@ -245,14 +244,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; // *) Misc - Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; + Configurable cfEtaGap{"cfEtaGap", 1.0, "eta gap for two particle correlation"}; + Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; Configurable cfQualityAssuranceSwitch{"cfQualityAssuranceSwitch", false, "quality assurance switch"}; Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { - double fSigmaInel = 7.71; + float fSigmaInel = 7.71; + float fEtaGap = 1.0; std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; std::vector fEventCutSwitch = std::vector(EEventCuts_N, true); @@ -295,6 +296,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct Observables { TList* fObservablesList = nullptr; std::array fProfTwo{}; //! [reco,sim] + std::array fProfTwoGap{}; } obs; struct QualityAssurance { @@ -308,7 +310,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TProfile* fMiscHistRunNumber = nullptr; TProfile* fMiscEventCuts = nullptr; TProfile* fMiscParticleCuts = nullptr; - TProfile* fMiscTechnicalCuts = nullptr; + TH1D* fMiscEventCutsCounter = nullptr; + TH1D* fMiscParticleCutsCounter = nullptr; } misc; // *) functions and templates @@ -327,7 +330,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // b) Calculate Q-vectors for available angles and weights: const int nParticles = angles.size(); for (int i = 0; i < nParticles; ++i) { // loop over particles - const float phi = static_cast(angles[i]); + const auto phi = static_cast(angles[i]); for (int h = 0; h < maxHarmonic; ++h) { for (int p = 0; p < maxPower; ++p) { const float wp = std::pow(static_cast(weights[i]), p); @@ -355,6 +358,22 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return {n, d}; } + float twoCorrWithGap(std::vector const& harmonic, std::vector const& vecPhi, std::vector const& vecWeights, std::vector const& vecEta) + { + const int nParticles = vecPhi.size(); + float n = 0.f; + float d = 0.f; + for (int a = 0; a <= nParticles; ++a) { + for (int b = 0; b < nParticles; ++b) { + if (a != b && std::fabs(vecEta[a] - vecEta[b]) > tc.fEtaGap) { + n += vecWeights[a] * vecWeights[b] * std::cos(harmonic[0] * vecPhi[a] + harmonic[1] * vecPhi[b]); + d += vecWeights[a] * vecWeights[b]; + } + } + } + return n / d; + } + std::vector recursion(int m, const std::vector>& qVector, std::vector& harmonic, int mult = 1, int skip = 0) { // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by @@ -576,67 +595,71 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - template - bool technicalCuts(T1 const& collision) + template + bool eventCuts(T1 const& collision) { if (tc.fTechnicalCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eNoCollInTimeRangeStandard) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eNoCollInRofStandard) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eNoSameBunchPileUp) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eIsVertexITSTPC) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eIsGoodITSLayersAll) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eIsGoodZvtxFT0vsPV) + 0.5, 1); return false; } if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + misc.fMiscEventCutsCounter->Fill(static_cast(eNoHighMultCollInPrevRof) + 0.5, 1); return false; } - return true; - } - template - bool eventCuts(T1 const& collision) - { if constexpr (rs == eRecAndSim && rm == eMC) { if (!collision.has_mcCollision()) { return false; } } - if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fEventCutSwitch[eSel8Cut] && !collision.sel8()) { // sel8 cut + if (tc.fEventCutSwitch[eSel8Cut] && !collision.sel8()) { // sel8 cut + misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eSel8Cut) + 0.5, 1); + return false; + } + if constexpr (rm == eReal) { + if (tc.fEventCutSwitch[eVertexZCut] && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut + misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eVertexZCut) + 0.5, 1); return false; } - if constexpr (rm == eReal) { - if (tc.fEventCutSwitch[eVertexZCut] && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut - return false; - } - auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - if (tc.fEventCutSwitch[eCentCut] && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut - return false; - } - if (tc.fEventCutSwitch[eNContribCut] && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts - return false; - } + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + if (tc.fEventCutSwitch[eCentCut] && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut + misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eCentCut) + 0.5, 1); + return false; } - if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim - auto thisMCCollision = collision.mcCollision(); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; - if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut - return false; - } - if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut - return false; - } + if (tc.fEventCutSwitch[eNContribCut] && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts + misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eNContribCut) + 0.5, 1); + return false; + } + } + if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim + auto thisMCCollision = collision.mcCollision(); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut + return false; + } + if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut + return false; } } @@ -711,6 +734,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fParticleCutSwitch[ePtCut]) { // pt cuts for Rec if constexpr (rm == eReal) { if (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]) { + misc.fMiscParticleCutsCounter->Fill(static_cast(ePtCut) + 0.5, 1); return false; } } @@ -724,6 +748,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fParticleCutSwitch[eEtaCut]) { if constexpr (rm == eReal) { // eta cuts for Rec if (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]) { + misc.fMiscParticleCutsCounter->Fill(static_cast(eEtaCut) + 0.5, 1); return false; } } @@ -737,6 +762,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (tc.fParticleCutSwitch[eChargeCut]) { if constexpr (rm == eReal) { // charge cuts for Rec if (track.sign() != 1 && track.sign() != -1) { + misc.fMiscParticleCutsCounter->Fill(static_cast(eChargeCut) + 0.5, 1); return false; } } @@ -885,11 +911,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool passEventCutsReal = eventCuts(collision); // bool passEventCutsMC = eventCuts(collision); // this is currently useless - bool passTechnicalCut = technicalCuts(collision); // Fill event hist and qa hist after cuts if (tc.fMasterCutSwitch) { - if (passEventCutsReal && passTechnicalCut) { + if (passEventCutsReal) { eventHistFill(collision, tracks); eventHistFill(collision, tracks); if (tc.fQualityAssuranceSwitch) { @@ -903,6 +928,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::vector vecPhiMC{}; std::vector vecWeightsReal{}; std::vector vecWeightsMC{}; + std::vector vecEtaReal{}; + std::vector vecEtaMC{}; // Main loop over particles: for (auto const& track : tracks) { @@ -912,12 +939,15 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to float thisPhiRec = track.phi(); float thisPhiSim = 0.f; float thisPt = track.pt(); + float thisEtaRec = track.eta(); + float thisEtaSim = 0.f; bool hasMCP = false; if constexpr (rs == eRecAndSim) { hasMCP = track.has_mcParticle(); if (hasMCP) { thisPhiSim = track.mcParticle().phi(); + thisEtaSim = track.mcParticle().eta(); } } @@ -933,16 +963,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (tc.fMasterCutSwitch) { - if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { + if (passEventCutsReal && particleCuts(track)) { particleHistFill(track); vecPhiReal.push_back(track.phi()); vecWeightsReal.push_back(thisWeights[ePhiRec] * thisWeights[ePt]); + vecEtaReal.push_back(thisEtaRec); } - if (passEventCutsReal && passTechnicalCut && particleCuts(track)) { + if (passEventCutsReal && particleCuts(track)) { particleHistFill(track); if (hasMCP) { vecPhiMC.push_back(thisPhiSim); vecWeightsMC.push_back(thisWeights[ePhiSim] * thisWeights[ePt]); + vecEtaMC.push_back(thisEtaSim); } } } @@ -954,7 +986,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::vector result22(2, TComplex(0., 0.)); if (tc.fMasterCutSwitch) { - if (passEventCutsReal && passTechnicalCut) { + if (passEventCutsReal) { + obs.fProfTwoGap[eRec]->Fill(thisCollCentReal, twoCorrWithGap(n22, vecPhiReal, vecWeightsReal, vecEtaReal)); + result22 = two(qVectorsTableReal, n22); if (noneZeroDenom(result22)) { obs.fProfTwo[eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); @@ -963,6 +997,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr (rs == eRecAndSim) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; + obs.fProfTwoGap[eSim]->Fill(thisCollCentMC, twoCorrWithGap(n22, vecPhiMC, vecWeightsMC, vecEtaMC)); + result22 = two(qVectorsTableMC, n22); if (noneZeroDenom(result22)) { obs.fProfTwo[eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); @@ -1013,6 +1049,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fEtaCutRange = cfEtaCutRange.value; tc.fParticleCutSwitch[eChargeCut] = cfChargeCutSwitch; + tc.fEtaGap = cfEtaGap; tc.fSigmaInel = cfSigmaInel; tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; tc.fRunMessageSwitch = cfRunMessageSwitch; @@ -1041,25 +1078,34 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); misc.fMiscHistList->Add(misc.fMiscHistRunNumber); - misc.fMiscTechnicalCuts = new TProfile("[eTechnicalCuts]", "Technical Cuts", ETechnicalCuts_N, 0, ETechnicalCuts_N); + const auto nTotalEventCuts = static_cast(ETechnicalCuts_N) + static_cast(EEventCuts_N); + misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts Switches", nTotalEventCuts, 0, nTotalEventCuts); for (int i = 1; i <= ETechnicalCuts_N; ++i) { - misc.fMiscTechnicalCuts->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); + misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); if (tc.fTechnicalCutSwitch[i - 1].starts_with("1")) { - misc.fMiscTechnicalCuts->Fill(i - 0.5, 1); + misc.fMiscEventCuts->Fill(i - 0.5, 1); } } - misc.fMiscHistList->Add(misc.fMiscTechnicalCuts); - - misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts", EEventCuts_N, 0, EEventCuts_N); for (int i = 1; i <= EEventCuts_N; ++i) { - misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); + const int thisIndex = i + static_cast(ETechnicalCuts_N); + misc.fMiscEventCuts->GetXaxis()->SetBinLabel(thisIndex, EventCutsNames[i - 1]); if (tc.fEventCutSwitch[i - 1]) { - misc.fMiscEventCuts->Fill(i - 0.5, 1); + misc.fMiscEventCuts->Fill(thisIndex - 0.5, 1); } } misc.fMiscHistList->Add(misc.fMiscEventCuts); - misc.fMiscParticleCuts = new TProfile("[eParticleCuts]", "Particle Level Cuts", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscEventCutsCounter = new TH1D("[eEventCutsCounter]", "Event Level Cuts Counter (Sequential)", nTotalEventCuts, 0, nTotalEventCuts); + for (int i = 1; i <= ETechnicalCuts_N; ++i) { + misc.fMiscEventCutsCounter->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); + } + for (int i = 1; i <= EEventCuts_N; ++i) { + const int thisIndex = i + static_cast(ETechnicalCuts_N); + misc.fMiscEventCutsCounter->GetXaxis()->SetBinLabel(thisIndex, EventCutsNames[i - 1]); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter); + + misc.fMiscParticleCuts = new TProfile("[eParticleCuts]", "Particle Level Cuts Switches", EParticleCuts_N, 0, EParticleCuts_N); for (int i = 1; i <= EParticleCuts_N; ++i) { misc.fMiscParticleCuts->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); if (tc.fParticleCutSwitch[i - 1]) { @@ -1068,6 +1114,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } misc.fMiscHistList->Add(misc.fMiscParticleCuts); + misc.fMiscParticleCutsCounter = new TH1D("[eParticleCutsCounter]", "Particle Level Cuts Counter (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter); + // *) Book particle TLists: pc.fParticleHistList = new TList(); pc.fParticleHistList->SetName("ParticleHistograms"); @@ -1219,7 +1271,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity (track.size())"); ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); @@ -1245,7 +1297,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity (track.size())"); ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); @@ -1273,7 +1325,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity (track.size())"); ec.fEventHist[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); ec.fEventHist[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); @@ -1299,7 +1351,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity"); + ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity (track.size())"); ec.fEventHist[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); ec.fEventHist[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); @@ -1334,11 +1386,21 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to obs.fProfTwo[eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eRec]); + obs.fProfTwoGap[eRec] = new TProfile("fProfTwoGap[eRec]", "Two particle correlation for reconstructed data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwoGap[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfTwoGap[eRec]->Sumw2(); + obs.fObservablesList->Add(obs.fProfTwoGap[eRec]); + if (doprocessRecSim) { obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwo[eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eSim]); + + obs.fProfTwoGap[eSim] = new TProfile("fProfTwoGap[eSim]", "Two particle correlation for simulated data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwoGap[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfTwoGap[eSim]->Sumw2(); + obs.fObservablesList->Add(obs.fProfTwoGap[eSim]); } } From 1f06de90f9ab6bf3a27ad66800ef130c233ee926 Mon Sep 17 00:00:00 2001 From: Akash-Raj-285 <157659199+Akash-Raj-285@users.noreply.github.com> Date: Sat, 17 Oct 2026 18:35:48 -0400 Subject: [PATCH 330/648] [PWGCF] Add Lambda jackknife sampling and daughter PID QA (#18338) --- .../lambdaOrAntiLambdaProducerWithSpin.cxx | 210 +++++++++++++++++- 1 file changed, 198 insertions(+), 12 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx index 13e8add711b..72a7a8cca45 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -123,6 +124,21 @@ DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int64_t); DECLARE_SOA_COLUMN(Alpha, alpha, float); DECLARE_SOA_COLUMN(QtArm, qtArm, float); +// Compatible with the K0Short mass window; not a truth label +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + +// Positive daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); + } // namespace lambdaorantilambda DECLARE_SOA_TABLE(LambdaOrAntiLambdas, @@ -176,7 +192,19 @@ DECLARE_SOA_TABLE(LambdaOrAntiLambdas, lambdaorantilambda::NegTrackId, lambdaorantilambda::Alpha, - lambdaorantilambda::QtArm); + lambdaorantilambda::QtArm, + + lambdaorantilambda::IsKShortCompatible, + + lambdaorantilambda::PosTPCMomentum, + lambdaorantilambda::PosTPCSignal, + lambdaorantilambda::PosTPCNSigmaPr, + lambdaorantilambda::PosTPCNSigmaPi, + + lambdaorantilambda::NegTPCMomentum, + lambdaorantilambda::NegTPCSignal, + lambdaorantilambda::NegTPCNSigmaPr, + lambdaorantilambda::NegTPCNSigmaPi); namespace lambdahyperon { @@ -210,6 +238,18 @@ DECLARE_SOA_COLUMN(PionDcaToPV, pionDcaToPV, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(Alpha, alpha, float); DECLARE_SOA_COLUMN(QtArm, qtArm, float); + +// Positive daughter +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); } // namespace lambdahyperon DECLARE_SOA_TABLE(Lambdas, "AOD", "LAMBDAS", o2::soa::Index<>, @@ -222,7 +262,11 @@ DECLARE_SOA_TABLE(Lambdas, "AOD", "LAMBDAS", o2::soa::Index<>, lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, - lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm); + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); DECLARE_SOA_TABLE(AntiLambdas, "AOD", "ANTILAMBDAS", o2::soa::Index<>, lambdahyperon::CollisionId, @@ -234,7 +278,11 @@ DECLARE_SOA_TABLE(AntiLambdas, "AOD", "ANTILAMBDAS", o2::soa::Index<>, lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, - lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm); + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); // ===================================================================== // MC-reconstructed Lambda/anti-Lambda candidates @@ -305,6 +353,9 @@ DECLARE_SOA_COLUMN(PieDcaToPV, pieDcaToPV, float); DECLARE_SOA_COLUMN(PieTPCCrossedRows, pieTPCCrossedRows, int); DECLARE_SOA_COLUMN(PieTPCNSigma, pieTPCNSigma, float); +// KShort Mass Campatability +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + } // namespace mcrecolambda DECLARE_SOA_TABLE(McRecoLambdaCandidates, @@ -365,7 +416,8 @@ DECLARE_SOA_TABLE(McRecoLambdaCandidates, mcrecolambda::PiePhi, mcrecolambda::PieDcaToPV, mcrecolambda::PieTPCCrossedRows, - mcrecolambda::PieTPCNSigma); + mcrecolambda::PieTPCNSigma, + mcrecolambda::IsKShortCompatible); // ===================================================================== // MC-generated Lambda/anti-Lambda candidates @@ -409,6 +461,7 @@ struct LambdaOrAntiLambdaProducerWithSpin { Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum number of TPC crossed rows"}; Configurable maxTPCNSigma{"maxTPCNSigma", 3.0f, "Maximum absolute TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; ConfigurableAxis axisLambdaAntiLambdaPt{"axisLambdaAntiLambdaPt", {200, 0.f, 10.f}, "LambdaAntiLambda transverse-momentum axis"}; @@ -466,6 +519,16 @@ struct LambdaOrAntiLambdaProducerWithSpin { rLambdaOrAntiLambda.add("Pion/hPhi", "Pion azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, o2::constants::math::TwoPI}}); rLambdaOrAntiLambda.add("Pion/hDcaToPV", "Pion DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + // Positive daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Positive/hTPCdEdxVsP", "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPrVsP", "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPiVsP", "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Negative/hTPCdEdxVsP", "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPrVsP", "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPiVsP", "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + // Armenteros-Podolanski rLambdaOrAntiLambda.add("Armenteros/hAlpha", "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); rLambdaOrAntiLambda.add("Armenteros/hQtArm", "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); @@ -494,6 +557,9 @@ struct LambdaOrAntiLambdaProducerWithSpin { continue; } + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + const bool isLambda = std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; @@ -575,7 +641,22 @@ struct LambdaOrAntiLambdaProducerWithSpin { // Armenteros-Podolanski v0.alpha(), - v0.qtarm()); + v0.qtarm(), + + // K0 Mass Comaptablity test + isKShortCompatible, + + // Positive daughter: measured signal and both PID hypotheses + posTrack.tpcInnerParam(), + posTrack.tpcSignal(), + posTrack.tpcNSigmaPr(), + posTrack.tpcNSigmaPi(), + + // Negative daughter: measured signal and both PID hypotheses + negTrack.tpcInnerParam(), + negTrack.tpcSignal(), + negTrack.tpcNSigmaPr(), + negTrack.tpcNSigmaPi()); rLambdaOrAntiLambda.fill(HIST("Event/hCentrality"), collision.centFT0M()); @@ -619,6 +700,16 @@ struct LambdaOrAntiLambdaProducerWithSpin { rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlpha"), v0.alpha()); rLambdaOrAntiLambda.fill(HIST("Armenteros/hQtArm"), v0.qtarm()); rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlphaVsQt"), v0.alpha(), v0.qtarm()); + + // Positive daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCdEdxVsP"), posTrack.tpcInnerParam(), posTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPrVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPiVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPi()); + + // Negative daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCdEdxVsP"), negTrack.tpcInnerParam(), negTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPrVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPiVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPi()); } } void processDummy(aod::Collisions const&) {} @@ -680,6 +771,16 @@ struct LambdaAntiLambdaSelector { rSelected.add((prefix + "/Armenteros/hAlpha").c_str(), "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); rSelected.add((prefix + "/Armenteros/hQtArm").c_str(), "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); rSelected.add((prefix + "/Armenteros/hAlphaVsQt").c_str(), "Armenteros-Podolanski;#alpha;q_{T} (GeV/c)", HistType::kTH2F, {{200, -1.f, 1.f}, {200, 0.f, 0.4f}}); + + // Positive daughter + rSelected.add((prefix + "/Positive/hTPCdEdxVsP").c_str(), "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPrVsP").c_str(), "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPiVsP").c_str(), "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter + rSelected.add((prefix + "/Negative/hTPCdEdxVsP").c_str(), "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPrVsP").c_str(), "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPiVsP").c_str(), "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); } void init(InitContext const&) @@ -720,6 +821,14 @@ struct LambdaAntiLambdaSelector { rSelected.fill(HIST("Lambda/Armenteros/hAlpha"), candidate.alpha()); rSelected.fill(HIST("Lambda/Armenteros/hQtArm"), candidate.qtArm()); rSelected.fill(HIST("Lambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("Lambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("Lambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); } else { rSelected.fill(HIST("AntiLambda/Event/hCentrality"), candidate.centrality()); rSelected.fill(HIST("AntiLambda/Event/hMultiplicity"), candidate.multiplicity()); @@ -748,6 +857,14 @@ struct LambdaAntiLambdaSelector { rSelected.fill(HIST("AntiLambda/Armenteros/hAlpha"), candidate.alpha()); rSelected.fill(HIST("AntiLambda/Armenteros/hQtArm"), candidate.qtArm()); rSelected.fill(HIST("AntiLambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("AntiLambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("AntiLambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); } } // ================================================================ @@ -845,7 +962,7 @@ struct LambdaAntiLambdaSelector { Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT (GeV/c)"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT (GeV/c)"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT (GeV/c)"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; @@ -860,6 +977,12 @@ struct LambdaAntiLambdaSelector { aod::lambdaorantilambda::pt < maxLambdaPt&& nabs(aod::lambdaorantilambda::rapidity) < maxLambdaRapidity; + // ===================================================================== + // Applying KShort Mass Compatablilty Filtere + // ===================================================================== + + Filter kShortRejectionFilter = + aod::lambdaorantilambda::isKShortCompatible == false; // All compatible filters above are combined with logical AND. using SelectedLambdaOrAntiLambdas = soa::Filtered; @@ -916,7 +1039,17 @@ struct LambdaAntiLambdaSelector { candidate.v0Radius(), candidate.alpha(), - candidate.qtArm()); + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); } // ============================================================ @@ -959,7 +1092,17 @@ struct LambdaAntiLambdaSelector { candidate.v0Radius(), candidate.alpha(), - candidate.qtArm()); + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); } } void processDummy(aod::Collisions const&) {} @@ -979,6 +1122,7 @@ struct LambdaAntiLambdaMcRecoTableProducer { // ================================================================ Configurable maxTPCNSigma{"maxTPCNSigma", 3.f, "Maximum TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; // ================================================================ // Lambda/anti-Lambda invariant-mass selection @@ -1009,6 +1153,9 @@ struct LambdaAntiLambdaMcRecoTableProducer { auto posTrack = v0.posTrack_as(); auto negTrack = v0.negTrack_as(); + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + const bool isLambda = std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; @@ -1140,7 +1287,8 @@ struct LambdaAntiLambdaMcRecoTableProducer { pionTrack.phi(), pionDcaToPV, static_cast(pionTrack.tpcNClsCrossedRows()), - pionTrack.tpcNSigmaPi()); + pionTrack.tpcNSigmaPi(), + isKShortCompatible); } } @@ -1228,7 +1376,7 @@ struct LambdaAntiLambdaEfficiencyPlots { // ================================================================ Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; Filter mcRecoKinematicFilter = aod::mcrecolambda::recoPt > minLambdaPt&& @@ -1247,6 +1395,13 @@ struct LambdaAntiLambdaEfficiencyPlots { nabs(aod::mcrecolambda::proEta) < maxDaughterEta&& nabs(aod::mcrecolambda::pieEta) < maxDaughterEta&& aod::mcrecolambda::proTPCCrossedRows >= minTPCCrossedRows&& aod::mcrecolambda::pieTPCCrossedRows >= minTPCCrossedRows; + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + + Filter mcRecoMassFilter = aod::mcrecolambda::massLambda >= minLambdaMass && aod::mcrecolambda::massLambda <= maxLambdaMass; + + Filter mcRecoKShortRejectionFilter = aod::mcrecolambda::isKShortCompatible == false; + void addEfficiencyPlotsHistograms(std::string const& particle) { const std::string recoDirectory = particle + "/Reco/"; @@ -1375,14 +1530,18 @@ struct LambdaAntiLambdaSelectionCutFlow { // Lambda kinematics Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; - Configurable maxLambdaPt{"maxLambdaPt", 4.5f, "Maximum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; + // Lambda Mass Cuts + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + void init(InitContext const&) { AxisSpec eventCutAxis{3, -0.5, 2.5, "Event-selection stage"}; - AxisSpec lambdaCutAxis{8, -0.5, 7.5, "Candidate-selection stage"}; + AxisSpec lambdaCutAxis{10, -0.5, 9.5, "Candidate-selection stage"}; cutFlowRegistry.add("Event/hCutFlow", "Event cut flow;Selection;Collisions", HistType::kTH1D, {eventCutAxis}); cutFlowRegistry.add("Lambda/hCutFlow", "Lambda cut flow;Selection;Candidates", HistType::kTH1D, {lambdaCutAxis}); @@ -1407,6 +1566,8 @@ struct LambdaAntiLambdaSelectionCutFlow { histogram->GetXaxis()->SetBinLabel(6, "TPC crossed rows"); histogram->GetXaxis()->SetBinLabel(7, "V0 topology"); histogram->GetXaxis()->SetBinLabel(8, "Lambda kinematics"); + histogram->GetXaxis()->SetBinLabel(9, "K0Short rejection"); + histogram->GetXaxis()->SetBinLabel(10, "Lambda mass window"); } } @@ -1487,6 +1648,17 @@ struct LambdaAntiLambdaSelectionCutFlow { continue; } fillStage(7.f); + + if (candidate.isKShortCompatible()) { + continue; + } + fillStage(8.f); + + if (candidate.massLambda() < minLambdaMass.value || + candidate.massLambda() > maxLambdaMass.value) { + continue; + } + fillStage(9.f); } } void processDummy(aod::Collisions const&) {} @@ -1526,6 +1698,11 @@ struct LambdaAntiLambdaPairAnalysis { Configurable sameEventShortRangePairMaxDeltaRapidity{"sameEventShortRangePairMaxDeltaRapidity", 0.5f, "ShortRangePai Maximum absolute rapidity difference for same-event pairs"}; Configurable sameEventShortRangePairMaxDeltaPhi{"sameEventShortRangePairMaxDeltaPhi", 1.047f, "ShortRangePai Maximum absolute wrapped phi difference for same-event pairs"}; // Pi/3 =1.047 + // For JackKnife Method of Error estemation + Configurable jackknife{"jackknife", false, "Enable leave-one-group-out jackknife"}; + Configurable jackknifeExcludedGroup{"jackknifeExcludedGroup", 0, "Group to exclude: 0 to 9"}; + static constexpr int NJackknifeGroups = 10; + void addHistograms(TString const& candidate1, TString const& candidate2) { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Same-Event~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1727,6 +1904,9 @@ struct LambdaAntiLambdaPairAnalysis { addHistograms("Lambda", "AntiLambda"); addHistograms("Lambda", "Lambda"); addHistograms("AntiLambda", "AntiLambda"); + if (jackknife.value && (jackknifeExcludedGroup.value < 0 || jackknifeExcludedGroup.value >= NJackknifeGroups)) { + LOGF(fatal, "jackknifeExcludedGroup must be between 0 and 9"); + } } ROOT::Math::PxPyPzMVector daughterInParentRestFrame(float parentLambdaPx, float parentLambdaPy, float parentLambdaPz, float parentLambdaMass, float daughterProtonPx, float daughterProtonPy, float daughterProtonPz, float daughterProtonMass) @@ -2264,6 +2444,9 @@ struct LambdaAntiLambdaPairAnalysis { static constexpr int MinimumSameSpeciesCandidates = 2; for (auto const& collision : collisions) { + if (jackknife.value && collision.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value) { + continue; + } const auto lambdasThisCollision = lambdas.sliceBy(lambdasPerCollision, collision.globalIndex()); const auto antiLambdasThisCollision = antiLambdas.sliceBy(antiLambdasPerCollision, collision.globalIndex()); @@ -2290,6 +2473,9 @@ struct LambdaAntiLambdaPairAnalysis { int mixedEventCounter = 0; for (auto const& [collision1, collision2] : combinations(CombinationsStrictlyUpperIndexPolicy(collisions, collisions))) { + if (jackknife.value && (collision1.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value || collision2.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value)) { + continue; + } const float deltaMultiplicity = std::abs(static_cast(collision1.multNTracksPV()) - static_cast(collision2.multNTracksPV())); From 143192ab91704504598720ba12d65373d2d1e1e7 Mon Sep 17 00:00:00 2001 From: tutripat <73981392+tutripat@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:42:11 +0200 Subject: [PATCH 331/648] [PWGLF] New histograms for systematics calculation (#18341) Co-authored-by: Tulika Tripathy Co-authored-by: ALICE Action Bot --- .../PseudorapidityDensityMFT.cxx | 256 ++++++++++++++++-- 1 file changed, 238 insertions(+), 18 deletions(-) diff --git a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx index 0fddd6d62d9..8390f501389 100644 --- a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -97,7 +98,7 @@ const AxisSpec centAxis = {{0, 10, 20, 30, 40, 50, 60, 70, 80, 100}}; const AxisSpec vxAxis = {200, -0.5, 0.5, "V_{x} (cm)"}; const AxisSpec vyAxis = {200, -0.5, 0.5, "V_{y} (cm)"}; // Status axis for reco/truth (1=reco, 2=true) -AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; +const AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; // Delta-vertex axes (reco - true) in cm const AxisSpec deltaVxAxis = {400, -0.5, 0.5, "#DeltaV_{x} = V_{x}^{rec}-V_{x}^{true} (cm)"}; @@ -395,6 +396,11 @@ struct PseudorapidityDensityMFT { Configurable useNoITSROFrameBorderCut{"useNoITSROFrameBorderCut", true, "Require kNoITSROFrameBorder in processGenReco"}; AxisSpec multAxisRecoMFT = {multBinning, "N_{ch}^{reco,MFT}"}; AxisSpec multAxisGenMFT = {multBinning, "N_{ch}^{gen,MFT}"}; + // Namespace plus invocation/reco IDs makes offline event keys independent of + // local collision-index reuse across dataframes and separate worker outputs. + TUUID associationKeyNamespace; + uint64_t associationInvocation{0}; + HistogramRegistry registry{ "registry", { @@ -498,6 +504,40 @@ struct PseudorapidityDensityMFT { {HistType::kTH1F, {dcaXyAxis}}}); if (doprocessGenReco3d || doprocessGenReco2d || doprocessGenRecoTimeCom) { + // Additional inputs for offline vertex/DCA studies. Existing histograms are unchanged. + if (doprocessGenReco3d) { + registry.add({"Association/RightTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/WrongTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/UnclassifiedTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + // Key components are exact 16-bit integers. Six namespace components + // retain 96 UUID bits; then two invocation and two chosen-reco-ID parts. + const AxisSpec key16{65536, -0.5, 65535.5}; + registry.add({"Association/EventTrackKeysBeforeDCA", + ";#eta;z_{chosen}^{reco};DCAxy;DCAz;track category;association (0:right,1:wrong,2:unclassified);namespace0;namespace1;namespace2;namespace3;namespace4;namespace5;callLow;callHigh;recoLow;recoHigh", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}, {3, -0.5, 2.5}, key16, key16, key16, key16, key16, key16, key16, key16, key16, key16}}}); + registry.add({"Association/hNumRecoEventsWithRightTracks", ";Selected reco collisions with right-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/hNumRecoEventsWithWrongTracks", ";Selected reco collisions with wrong-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/RecoEventVzWithRightTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"Association/RecoEventVzWithWrongTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"OfflineVariations/RecoTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"OfflineVariations/RecoStrangeSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + registry.add({"OfflineVariations/SelectedRecoEventVertexResponse", + ";z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm);selected reco collisions", + {HistType::kTHnSparseD, {zAxis, zAxis}}}); + registry.add({"OfflineVariations/GenPrimaryEtaTrueZRecoZ", + ";#eta_{gen};z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm)", + {HistType::kTHnSparseD, {etaBinning, zAxis, zAxis}}}); registry.add({"EventsRecoCuts_GenReco", ";cut;events", {HistType::kTH1F, {{16, 0.5, 16.5}}}}); @@ -1062,6 +1102,14 @@ struct PseudorapidityDensityMFT { {HistType::kTHnSparseF, {dcaXyAxis, dcaZAxis, dcaXAxis, dcaYAxis}}}); + // Category-specific selected reconstructed-collision counts (not generated events). + registry.add({"Purity/hNumCollisionsNonAmb_InelMFT", + ";Selected reco collisions with nonambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/hNumCollisionsAmb_InelMFT", + ";Selected reco collisions with ambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/RecoSparseAll_EventCount", ";events;counts", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); @@ -1167,6 +1215,16 @@ struct PseudorapidityDensityMFT { } if (doprocessMultReassoc || doprocessMultReassoc3d) { + // Offline DCA variations need tracks before either DCA rejection. + // Only the 3D process supplies a measured bestDCAZ; do not fill fake zeros + // for the 2D process. Category 0 = nonambiguous, 1 = ambiguous; no orphans. + if (doprocessMultReassoc3d) { + registry.add({"Tracks/2Danalysis/DataTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);track category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, + {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + // Purity denominator histograms (reco side after DCAxy selection) registry.add({"Tracks/Control/DeltaZ", @@ -1318,8 +1376,8 @@ struct PseudorapidityDensityMFT { // {HistType::kTH1F,{{100000, 0.5, 100000.0}}}}); // {HistType::kTH1F, {{100000, -50000.0, 50000.0}}}}); // registry.add({"NonambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsNonAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // + registry.add({"hNumCollisionsNonAmb_InelMFT", ";Selected collisions with nonambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"hNumCollisionsAmb_InelMFT", ";Selected collisions with ambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); registry.add({"hNumCollisions_InelMFT", " ; Number of selected events with Inel>0 and MFT>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"hNumCollisions_Inel", " ; Number of selected events with Inel>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"ambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // @@ -1567,8 +1625,6 @@ struct PseudorapidityDensityMFT { { std::unordered_set uniqueEvents; std::unordered_set uniqueEventsAmb; - std::unordered_set uniqueCollisions; - std::unordered_set uniqueCollisionsAmb; std::unordered_set eventsInelMFT; std::unordered_set eventsInel; @@ -1674,7 +1730,7 @@ struct PseudorapidityDensityMFT { o2::math_utils::bringTo02Pi(phi); const float dcaXyCut = retrack.bestDCAXY(); - bool failTrackCuts = + const bool failTrackQualityCuts = track.nClusters() < cfgnCluster || track.eta() <= cfgnEta1 || track.eta() >= cfgnEta2 || @@ -1686,8 +1742,23 @@ struct PseudorapidityDensityMFT { ((phi <= PhiVetoLow) || ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || (phi >= PhiVetoHigh))) || - (useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy) || (usePtCut && ptCut > cfgnPt); + (usePtCut && ptCut > cfgnPt); + + if (failTrackQualityCuts) { + continue; + } + // Selected INEL>0 events and quality-selected non-orphan tracks, before + // both DCA cuts. This keeps the existing post-DCA spectra unchanged. + if constexpr (std::is_same_v>) { + if (hasMidtracks) { + const int category = retrack.ambDegree() == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("Tracks/2Danalysis/DataTrackSparseBeforeDCA"), + track.eta(), z, dcaXyCut, retrack.bestDCAZ(), category); + } + } + + bool failTrackCuts = useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy; if constexpr (std::is_same_v>) { const float dcaZCut = retrack.bestDCAZ(); failTrackCuts = failTrackCuts || (useDCAzCut && std::abs(dcaZCut) > maxDCAz); @@ -1776,9 +1847,6 @@ struct PseudorapidityDensityMFT { } uniqueEventsAmb.insert(retrack.bestCollisionId()); } - if (midtracks.size() > 0 && retrack.ambDegree() > 1) { - uniqueCollisionsAmb.insert(collision.globalIndex()); - } registry.fill(HIST("Tracks/Control/TrackIsAmb"), isAmbiguous); if (retrack.ambDegree() == 1) { ++j; @@ -1795,9 +1863,6 @@ struct PseudorapidityDensityMFT { } uniqueEvents.insert(retrack.bestCollisionId()); } - if (midtracks.size() > 0 && retrack.ambDegree() == 1) { - uniqueCollisions.insert(collision.globalIndex()); - } registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksEtaZvtx"), track.eta(), z); registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksPhiEta"), phi, @@ -1810,11 +1875,20 @@ struct PseudorapidityDensityMFT { } registry.fill(HIST("ambEventCounts"), 1, uniqueEventsAmb.size()); registry.fill(HIST("NonambEventCounts"), 1, uniqueEvents.size()); - registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1, uniqueCollisions.size()); - registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1, uniqueCollisionsAmb.size()); registry.fill(HIST("hNumCollisions_InelMFT"), 1, eventsInelMFT.size()); } + // Count selected reconstructed collisions once, not once per track. + // Mixed events contribute to both categories; orphan tracks were rejected above. + if (midtracks.size() > 0) { + if (nMFTSelectedNonAmb > 0) { + registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1.); + } + if (nMFTSelectedAmb > 0) { + registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1.); + } + } + registry.fill(HIST("Tracks/Control/amb/nTrkAmb"), i); registry.fill(HIST("Tracks/Control/nonamb/nTrkNonAmb"), j); registry.fill(HIST("Tracks/Control/woOrp/nTrk"), k); @@ -2207,7 +2281,7 @@ struct PseudorapidityDensityMFT { const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { bool enabled{false}; - decltype(aod::evsel::kIsTriggerTVX) bit; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; @@ -2534,7 +2608,7 @@ struct PseudorapidityDensityMFT { const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { bool enabled{false}; - decltype(aod::evsel::kIsTriggerTVX) bit; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; @@ -2625,7 +2699,7 @@ struct PseudorapidityDensityMFT { // The reconstructed event has now passed the z window, reconstructed // INEL>0, reco-gen z matching and the complete event selection. if (!atLeastOneSel8VzGt0) { - selectedRecoNtrkForEventLoss = static_cast(perCollisionSample.size()); + selectedRecoNtrkForEventLoss = perCollisionSample.size(); selectedRecoZvtxForEventLoss = collision.posZ(); } atLeastOneSel8VzGt0 = true; @@ -2696,6 +2770,10 @@ struct PseudorapidityDensityMFT { recoVtxByRecoId[recoCol] = {collision.posX(), collision.posY(), collision.posZ()}; recoVtxByMcId[mcCol] = {mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()}; + // One entry per accepted reconstructed collision, including zero-MFT events. + registry.fill(HIST("OfflineVariations/SelectedRecoEventVertexResponse"), + mcCollision.posZ(), collision.posZ()); + registry.fill(HIST("Purity/xReco"), collision.posX()); registry.fill(HIST("Purity/xTrue"), mcCollision.posX()); registry.fill(HIST("Purity/yReco"), collision.posY()); @@ -2717,6 +2795,107 @@ struct PseudorapidityDensityMFT { int64_t nRecoMFTSelectedForPNch{0}; int64_t nGenPrimaryChargedMFT{0}; std::unordered_set uniqueBestRecoCols; + std::unordered_set recoColsWithNonAmbTracks; + std::unordered_set recoColsWithAmbTracks; + + std::unordered_set recoEventsWithRightTracks; + std::unordered_set recoEventsWithWrongTracks; + if (associationInvocation >= 0xffffffffULL) { + LOGF(fatal, "Association offline event-key invocation range exhausted"); + } + const auto associationCall = static_cast(associationInvocation++); + std::array associationUuid{}; + associationKeyNamespace.GetUUID(associationUuid.data()); + std::array associationNamespaceParts{}; + for (size_t part = 0; part < associationNamespaceParts.size(); ++part) { + associationNamespaceParts[part] = + (static_cast(associationUuid[2 * part]) << 8) | associationUuid[2 * part + 1]; + } + + // Separate pre-DCA pass: intentionally does not alter any existing track + // cuts, counters or fills. The 3D process supplies both measured DCA values. + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + for (const auto& offlineTrack : tracks) { + const auto original = offlineTrack.template mfttrack_as(); + float offlinePhi = original.phi(); + o2::math_utils::bringTo02Pi(offlinePhi); + const auto compatibleCount = offlineTrack.compatibleCollIds().size(); + if (compatibleCount == 0 || original.nClusters() < cfgnCluster || + original.eta() <= cfgnEta1 || original.eta() >= cfgnEta2 || + original.chi2() / getTrackNdf(original) >= cfgChi2NDFMax || + offlinePhi <= cfgPhiCut1 || offlinePhi >= cfgPhiCut2 || + (usePhiCut && (offlinePhi <= PhiVetoLow || + (offlinePhi >= PhiVetoPiMin && offlinePhi <= PhiVetoPiMax) || + offlinePhi >= PhiVetoHigh)) || + (usePtCut && original.pt() > cfgnPt) || !passGenRecoTrackMode(offlineTrack)) { + continue; + } + // Same original-collision association as the existing reco denominator. + const int offlineRecoId = original.collisionId(); + if (!acceptedRecoCols.contains(offlineRecoId)) { + continue; + } + auto offlineVz = recoVtxZ.find(offlineRecoId); + if (offlineVz == recoVtxZ.end()) { + continue; + } + const int category = compatibleCount == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("OfflineVariations/RecoTrackSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + const int chosenId = offlineTrack.bestCollisionId(); + auto chosenVz = recoVtxZ.find(chosenId); + auto chosenMc = recoToMc.find(chosenId); + // Only selected chosen reco collisions have a meaningful event denominator. + if (chosenId >= 0 && chosenVz != recoVtxZ.end() && chosenMc != recoToMc.end()) { + int association = 2; // Neither fake nor missing labels are forced into "wrong". + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + association = chosenMc->second == offlineTrack.mcParticle().mcCollisionId() ? 0 : 1; + } + if (association == 0) { + registry.fill(HIST("Association/RightTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else if (association == 1) { + registry.fill(HIST("Association/WrongTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else { + registry.fill(HIST("Association/UnclassifiedTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + const auto chosenKey = static_cast(chosenId); + registry.fill(HIST("Association/EventTrackKeysBeforeDCA"), + original.eta(), chosenVz->second, offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), + category, association, + associationNamespaceParts[0], associationNamespaceParts[1], associationNamespaceParts[2], + associationNamespaceParts[3], associationNamespaceParts[4], associationNamespaceParts[5], + associationCall & 0xffffU, associationCall >> 16, + chosenKey & 0xffffU, chosenKey >> 16); + const bool passesConfiguredDCA = + (!useDCAxyCut || std::abs(offlineTrack.bestDCAXY()) <= maxDCAxy) && + (!useDCAzCut || std::abs(offlineTrack.bestDCAZ()) <= maxDCAz); + if (passesConfiguredDCA) { + if (association == 0) { + recoEventsWithRightTracks.insert(chosenId); + } + if (association == 1) { + recoEventsWithWrongTracks.insert(chosenId); + } + } + } + + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + const auto particle = offlineTrack.mcParticle(); + if (!particle.isPhysicalPrimary() && particle.has_mothers()) { + const auto mother = particle.template mothers_as().front(); + if (mother.pdgCode() == PDG_t::kK0Short || std::abs(mother.pdgCode()) == PDG_t::kLambda0) { + registry.fill(HIST("OfflineVariations/RecoStrangeSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + } + } + } + } if (tracks.size() > 0) { for (const auto& track : tracks) { @@ -2804,6 +2983,16 @@ struct PseudorapidityDensityMFT { continue; } + // Match the inclusive reconstructed-MC denominator below: all accepted + // tracks, including fakes/no-label tracks, grouped by original recoCol. + // Event selection, track mode and both configured DCA cuts have passed. + const auto nCompatibleCollisions = track.compatibleCollIds().size(); + if (nCompatibleCollisions == SingleCompatibleCollision) { + recoColsWithNonAmbTracks.insert(recoCol); + } else if (nCompatibleCollisions > SingleCompatibleCollision) { + recoColsWithAmbTracks.insert(recoCol); + } + ++nRecoMFTSelectedForPNch; // Inclusive reconstructed-MC denominator: fill every selected track, @@ -3235,6 +3424,33 @@ struct PseudorapidityDensityMFT { } // hasmclable } // track loop } // track>mid + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + if (!recoEventsWithRightTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithRightTracks"), 1., + static_cast(recoEventsWithRightTracks.size())); + } + if (!recoEventsWithWrongTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithWrongTracks"), 1., + static_cast(recoEventsWithWrongTracks.size())); + } + for (const int id : recoEventsWithRightTracks) { + registry.fill(HIST("Association/RecoEventVzWithRightTracks"), recoVtxZ.at(id)); + } + for (const int id : recoEventsWithWrongTracks) { + registry.fill(HIST("Association/RecoEventVzWithWrongTracks"), recoVtxZ.at(id)); + } + } + + // Sets contain distinct reconstructed collisions, even when several reco + // collisions belong to this generated event. Mixed events enter both sets. + if (!recoColsWithNonAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsNonAmb_InelMFT"), 1., + static_cast(recoColsWithNonAmbTracks.size())); + } + if (!recoColsWithAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsAmb_InelMFT"), 1., + static_cast(recoColsWithAmbTracks.size())); + } registry.fill(HIST("Purity/HashTableRowCounts"), static_cast(HashTableRowCountsBin::UniqueBestRecoCols), uniqueBestRecoCols.size()); registry.fill(HIST("Purity/reco/woOrp/nTrk"), woOrpCount); @@ -3324,6 +3540,10 @@ struct PseudorapidityDensityMFT { registry.fill(HIST("Purity/mc/PrimaryAllEta"), particle.eta()); registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx"), particle.eta(), mcCollision.posZ()); registry.fill(HIST("Purity/mc/PrimaryTracksPhiEta"), particle.phi(), particle.eta()); + for (const auto& [offlineRecoId, offlineRecoZ] : recoVtxZ) { + registry.fill(HIST("OfflineVariations/GenPrimaryEtaTrueZRecoZ"), + particle.eta(), mcCollision.posZ(), offlineRecoZ); + } } if (atLeastOneSel8VzGt0) { From 2085f0ff946e4d163d2ce2acb6583fa825bde16b Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Sun, 18 Oct 2026 16:11:00 +0200 Subject: [PATCH 332/648] [PWGHF] Add missing or fix incorred PID information for B-hadron reduced tasks (#18332) --- PWGHF/D2H/Tasks/taskBsReduced.cxx | 20 +++++++++++++++++++- PWGHF/D2H/Tasks/taskLbReduced.cxx | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskBsReduced.cxx b/PWGHF/D2H/Tasks/taskBsReduced.cxx index 703a527ffac..d304fe782c4 100644 --- a/PWGHF/D2H/Tasks/taskBsReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsReduced.cxx @@ -79,12 +79,18 @@ DECLARE_SOA_COLUMN(TpcChi2NClDmesProngMax, tpcChi2NClDmesProngMax, float); DECLARE_SOA_COLUMN(NSigTpcPiDmesProng0, nSigTpcPiDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng0, nSigTofPiDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng0, nSigTpcTofPiDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng0, nSigTpcKaDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng0, nSigTofKaDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng0, nSigTpcTofKaDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKaDmesProng1, nSigTpcKaDmesProng1, float); //! TPC Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofKaDmesProng1, nSigTofKaDmesProng1, float); //! TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng1, nSigTpcTofKaDmesProng1, float); //! Combined TPC and TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPiDmesProng2, nSigTpcPiDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng2, nSigTofPiDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng2, nSigTpcKaDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng2, nSigTofKaDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng2, nSigTpcTofKaDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis // pion features DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); //! Transverse momentum of prong1 (GeV/c) DECLARE_SOA_COLUMN(AbsEtaProng1, absEtaProng1, float); //! Absolute pseudorapidity of Prong1 @@ -135,12 +141,18 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bs_lite::NSigTpcPiDmesProng0, hf_cand_bs_lite::NSigTofPiDmesProng0, hf_cand_bs_lite::NSigTpcTofPiDmesProng0, + hf_cand_bs_lite::NSigTpcKaDmesProng0, + hf_cand_bs_lite::NSigTofKaDmesProng0, + hf_cand_bs_lite::NSigTpcTofKaDmesProng0, hf_cand_bs_lite::NSigTpcKaDmesProng1, hf_cand_bs_lite::NSigTofKaDmesProng1, hf_cand_bs_lite::NSigTpcTofKaDmesProng1, hf_cand_bs_lite::NSigTpcPiDmesProng2, hf_cand_bs_lite::NSigTofPiDmesProng2, hf_cand_bs_lite::NSigTpcTofPiDmesProng2, + hf_cand_bs_lite::NSigTpcKaDmesProng2, + hf_cand_bs_lite::NSigTofKaDmesProng2, + hf_cand_bs_lite::NSigTpcTofKaDmesProng2, hf_cand_bs_reduced::Prong0MlScoreBkg, hf_cand_bs_reduced::Prong0MlScorePrompt, hf_cand_bs_reduced::Prong0MlScoreNonprompt, @@ -596,9 +608,15 @@ struct HfTaskBsReduced { candDs.tpcNSigmaPiProng0(), candDs.tofNSigmaPiProng0(), candDs.tpcTofNSigmaPiProng0(), + candDs.tpcNSigmaKaProng0(), + candDs.tofNSigmaKaProng0(), + candDs.tpcTofNSigmaKaProng0(), candDs.tpcNSigmaKaProng1(), candDs.tofNSigmaKaProng1(), candDs.tpcTofNSigmaKaProng1(), + candDs.tpcNSigmaPiProng2(), + candDs.tofNSigmaPiProng2(), + candDs.tpcTofNSigmaPiProng2(), candDs.tpcNSigmaKaProng2(), candDs.tofNSigmaKaProng2(), candDs.tpcTofNSigmaKaProng2(), diff --git a/PWGHF/D2H/Tasks/taskLbReduced.cxx b/PWGHF/D2H/Tasks/taskLbReduced.cxx index fe43e8d2c6e..da430368482 100644 --- a/PWGHF/D2H/Tasks/taskLbReduced.cxx +++ b/PWGHF/D2H/Tasks/taskLbReduced.cxx @@ -73,6 +73,9 @@ DECLARE_SOA_COLUMN(E, e, float); DECLARE_SOA_COLUMN(NSigTpcPiBachelor, nSigTpcPiBachelor, float); //! TPC Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiBachelor, nSigTofPiBachelor, float); //! TOF Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiBachelor, nSigTpcTofPiBachelor, float); //! Combined TPC and TOF Nsigma separation for bachelor with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPiLcProng0, nSigTpcPiLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPiLcProng0, nSigTofPiLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng0, nSigTpcTofPiLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPrLcProng0, nSigTpcPrLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTofPrLcProng0, nSigTofPrLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng0, nSigTpcTofPrLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis @@ -82,6 +85,9 @@ DECLARE_SOA_COLUMN(NSigTpcTofKaLcProng1, nSigTpcTofKaLcProng1, float); DECLARE_SOA_COLUMN(NSigTpcPiLcProng2, nSigTpcPiLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiLcProng2, nSigTofPiLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng2, nSigTpcTofPiLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPrLcProng2, nSigTpcPrLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPrLcProng2, nSigTofPrLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng2, nSigTpcTofPrLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -127,6 +133,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::ItsNClsLcProngMin, hf_cand_lb_lite::TpcNClsCrossedRowsLcProngMin, hf_cand_lb_lite::TpcChi2NClLcProngMax, + hf_cand_lb_lite::NSigTpcPiLcProng0, + hf_cand_lb_lite::NSigTofPiLcProng0, + hf_cand_lb_lite::NSigTpcTofPiLcProng0, hf_cand_lb_lite::NSigTpcPrLcProng0, hf_cand_lb_lite::NSigTofPrLcProng0, hf_cand_lb_lite::NSigTpcTofPrLcProng0, @@ -136,6 +145,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::NSigTpcPiLcProng2, hf_cand_lb_lite::NSigTofPiLcProng2, hf_cand_lb_lite::NSigTpcTofPiLcProng2, + hf_cand_lb_lite::NSigTpcPrLcProng2, + hf_cand_lb_lite::NSigTofPrLcProng2, + hf_cand_lb_lite::NSigTpcTofPrLcProng2, hf_cand_lb_reduced::Prong0MlScoreBkg, hf_cand_lb_reduced::Prong0MlScorePrompt, hf_cand_lb_reduced::Prong0MlScoreNonprompt, @@ -587,6 +599,9 @@ struct HfTaskLbReduced { candLc.itsNClsProngMin(), candLc.tpcNClsCrossedRowsProngMin(), candLc.tpcChi2NClProngMax(), + candLc.tpcNSigmaPiProng0(), + candLc.tofNSigmaPiProng0(), + candLc.tpcTofNSigmaPiProng0(), candLc.tpcNSigmaPrProng0(), candLc.tofNSigmaPrProng0(), candLc.tpcTofNSigmaPrProng0(), @@ -596,6 +611,9 @@ struct HfTaskLbReduced { candLc.tpcNSigmaPiProng2(), candLc.tofNSigmaPiProng2(), candLc.tpcTofNSigmaPiProng2(), + candLc.tpcNSigmaPrProng2(), + candLc.tofNSigmaPrProng2(), + candLc.tpcTofNSigmaPrProng2(), prong0MlScoreBkg, prong0MlScorePrompt, prong0MlScoreNonprompt, From 9404f3dc216ff106cf94c7616316b26993e5e0ad Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Sun, 18 Oct 2026 16:24:02 +0200 Subject: [PATCH 333/648] [PWGLF] Enabling MC and reducing memory consumption (#18343) Co-authored-by: Sushanta Tripathy Co-authored-by: ALICE Action Bot --- PWGLF/Tasks/Resonances/lambdastarproxy.cxx | 1437 ++++++++++++-------- 1 file changed, 883 insertions(+), 554 deletions(-) diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index 9ec02766b4c..a900e07e9c3 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -24,20 +24,27 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include +#include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -48,6 +55,8 @@ using namespace o2::framework::expressions; using namespace constants::math; struct Lambdastarproxy { + Service pdgDB{}; + // ---- Ion/nucleus PDG encoding helpers (10LZZZAAAI) ---- // Note: these are *format* constants (not particle PDG species codes) static constexpr int IonCodeThreshold = 1000000000; // 10^9 @@ -182,25 +191,44 @@ struct Lambdastarproxy { Configurable lstarEnableSparse{"lstarEnableSparse", 1, "Enable THnSparse invariant-mass histograms (#Lambda^{*} pK and proxy); 1 = ON, 0 = OFF"}; Configurable lstarLambdaAbsYMax{"lstarLambdaAbsYMax", 0.5f, "Max |y_{pK}| (or y_{proxy K}) for #Lambda^{*} candidates"}; + // Booking and filling use the same switches. Full mass maps grow with + // occupied mass x mass x pT x multiplicity cells and are diagnostic only. + Configurable lstarEnableFullPKMaps{"lstarEnableFullPKMaps", 0, "Book/fill 4D and 8D proxy/full-pK maps; enable only for diagnostic productions"}; + Configurable lstarEnableProtonLikeSparse{"lstarEnableProtonLikeSparse", 0, "Book/fill redundant proton-like proxy spectra (also obtainable from compact controls)"}; + Configurable lstarSparseMultBins{"lstarSparseMultBins", 100, "Multiplicity-axis bins in all invariant-mass sparse histograms; keep identical across merge inputs"}; + Configurable lstarMCPrimaryOnly{"lstarMCPrimaryOnly", 1, "Generated p/K/d fiducial spectra: require physical primaries (strong-decay daughters included)"}; + + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + float px = 0.f, py = 0.f, pz = 0.f; + bool primary = false; + std::vector lambdaMothers; + }; + struct KaonCand { - float px, py, pz; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; // PID flags describe detector compatibility, not truth-level purity. struct ProxyCand { - float px, py, pz; - float pxFull, pyFull, pzFull; - bool protonLike; - bool hasTOF; - bool protonTPCCompatible; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + float pxFull = 0.f, pyFull = 0.f, pzFull = 0.f; + bool protonLike = false; + bool hasTOF = false; + bool protonTPCCompatible = false; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; struct ProtonCand { - float px, py, pz; - int charge; - int tid; + float px = 0.f, py = 0.f, pz = 0.f; + int charge = 0; + int tid = -1; + TruthInfo truth{}; }; // Helpers for invariant-mass kinematics @@ -226,7 +254,6 @@ struct Lambdastarproxy { float zvtx = 0.f; std::vector kaons; std::vector protons; - std::vector proxies; }; // Keep last N events for event-mixing @@ -309,7 +336,7 @@ struct Lambdastarproxy { return fallback; } - static int chargeFromPdg(int pdg) + int chargeFromPdg(int pdg) const { const int apdg = std::abs(pdg); @@ -322,11 +349,15 @@ struct Lambdastarproxy { switch (apdg) { case PdgElectron: // e case PdgMuon: // mu - case PdgPion: // pi - case PdgKaon: // K - case PdgProton: // p + return (pdg >= 0) ? -1 : 1; + case PdgPion: // pi + case PdgKaon: // K + case PdgProton: // p return (pdg >= 0) ? 1 : -1; default: + if (auto* particle = pdgDB->GetParticle(pdg)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } return 0; } } @@ -460,6 +491,12 @@ struct Lambdastarproxy { void init(o2::framework::InitContext&) { + if (doprocess && doprocessMCReco) { + LOGF(fatal, "Enable either process (data) or processMCReco, never both"); + } + if (lstarNoMixedEvents.value < 0 || lstarSparseMultBins.value <= 0) { + LOGF(fatal, "lstarNoMixedEvents must be nonnegative; lstarSparseMultBins must be positive"); + } AxisSpec massAxis{200, 1.4, 1.9, "M_{pK} (GeV/c^{2})"}; AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; AxisSpec nsAxis{500, -50., 50., "n#sigma"}; @@ -467,7 +504,7 @@ struct Lambdastarproxy { AxisSpec pAxis{100, 0., 10., "p (GeV/c)"}; AxisSpec etaAxis{80, -2., 2., "#eta"}; AxisSpec phiAxis{64, 0., o2::constants::math::TwoPI, "#varphi"}; - AxisSpec centAxis{100, 0., 100., "multiplicity/centrality"}; + AxisSpec centAxis{lstarSparseMultBins.value, 0., 100., "multiplicity/centrality"}; AxisSpec pdgAxis{10001, -5000.5, 5000.5, "PDG code"}; @@ -511,61 +548,74 @@ struct Lambdastarproxy { mapAxes.emplace_back(1000, 1.4, 2.4, "M_{pK}^{full} (GeV/c^{2})"); mapAxes.emplace_back(2, -0.5, 1.5, "passes full-pK rapidity (0/1)"); for (auto const& pairClass : {"Unlike", "Like", "Mixed"}) { + if (std::strcmp(pairClass, "Mixed") == 0 && lstarNoMixedEvents.value == 0) { + continue; + } const std::string spectrumName = std::string("hLambdaStarProxyControl") + pairClass + "Sparse"; const std::string mapName = std::string("hLambdaStarProxyControlFullPK") + pairClass + "Sparse"; histos.add(spectrumName.c_str(), "Inclusive proxy acceptance; PID compatibility controls", HistType::kTHnSparseF, controlAxes, true); - histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + if (lstarEnableFullPKMaps.value != 0) { + histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + } } } - histos.add( - "hLambdaStarProxyVsFullPKMixedSparse", - "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.add( + "hLambdaStarProxyVsFullPKMixedSparse", + "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } + } - histos.add( - "hLambdaStarProxyProtonLikeUnlikeSparse", - "d-selected and proton-like OS proxy candidates;" - "M_{(d/2)K} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeUnlikeSparse", + "d-selected and proton-like OS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } - histos.add( - "hLambdaStarProxyProtonLikeLikeSparse", - "d-selected and proton-like LS proxy candidates;" - "M_{(d/2)K} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeLikeSparse", + "d-selected and proton-like LS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } histos.add("hLambdaStarPKUnlikeSparse", "#Lambda^{*}(1520) pK unlike-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", @@ -579,11 +629,13 @@ struct Lambdastarproxy { {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); - histos.add("hLambdaStarPKMixedSparse", - "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarPKMixedSparse", + "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + } // THnSparse for deuteron-proxy invariant-mass analysis (mass, pT, multiplicity/centrality) histos.add("hLambdaStarProxySparse", @@ -598,49 +650,55 @@ struct Lambdastarproxy { {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); - histos.add("hLambdaStarProxyMixedSparse", - "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarProxyMixedSparse", + "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + } - histos.add( - "hLambdaStarProxyVsFullPKUnlikeSparse", - "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKUnlikeSparse", + "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } - histos.add( - "hLambdaStarProxyVsFullPKLikeSparse", - "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" - "M_{(d/2)K} (GeV/c^{2});" - "M_{pK}^{full} (GeV/c^{2});" - "p_{T}^{(d/2)K} (GeV/c);" - "multiplicity/centrality", - HistType::kTHnSparseF, - {AxisSpec{400, 1.4, 1.8, - "M_{(d/2)K} (GeV/c^{2})"}, + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKLikeSparse", + "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, - AxisSpec{400, 1.4, 1.8, - "M_{pK}^{full} (GeV/c^{2})"}, + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., - "p_{T}^{(d/2)K} (GeV/c)"}, + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, - centAxis}); + centAxis}); + } } // Deuteron-proxy invariant mass (p_{proxy} from d/2 combined with K) @@ -648,248 +706,444 @@ struct Lambdastarproxy { "#Lambda^{*} proxy invariant mass from (d/2 + K);M_{p_{proxy}K} (GeV/c^{2});Counts", HistType::kTH1F, {massAxis}); - // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only - // TPC dE/dx vs total momentum - histos.add("hTPCdEdxVsP", - "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - - // TOF #beta vs total momentum - histos.add("hTOFBetaVsP", - "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - - histos.add("hHasTOFVsP", - "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", - HistType::kTH2F, {pAxis, tofMatchAxis}); - - histos.add("hHasTOFVsPt", - "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", - HistType::kTH2F, {ptAxis, tofMatchAxis}); - - // --- Per-species inclusive PID QA before final candidate PID cuts --- - // Species tagging here uses classifyPidSpecies() and is meant only for QA. - histos.add("hTPCdEdxVsP_Pi", - "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_K", - "TPC dE/dx vs p (tagged K);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_P", - "TPC dE/dx vs p (tagged p);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - histos.add("hTPCdEdxVsP_D", - "TPC dE/dx vs p (tagged d);p (GeV/c);dE/dx (arb. units);Counts", - HistType::kTH2F, {pAxis, dEdxAxis}); - - histos.add("hTOFBetaVsP_Pi", - "TOF #beta vs p (tagged #pi);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_K", - "TOF #beta vs p (tagged K);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_P", - "TOF #beta vs p (tagged p);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - histos.add("hTOFBetaVsP_D", - "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {pAxis, betaAxis}); - - // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. - // These are separate from the final-candidate PID QA histograms. - histos.add("hNsigmaTPCPionTaggedVsP", - "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFPionTaggedVsP", - "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCKaonTaggedVsP", - "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFKaonTaggedVsP", - "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCProtonTaggedVsP", - "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFProtonTaggedVsP", - "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - histos.add("hNsigmaTPCDeuteronTaggedVsP", - "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronTaggedVsP", - "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // --- MC QA (AO2D-MC mode) --- - histos.add("hMcPrimariesPtEta", - "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", - HistType::kTH2F, {ptAxis, etaAxis}); - - histos.add("hRecoMatchedPdg", - "Reco tracks matched to MC (PDG); PDG code; Counts", - HistType::kTH1F, {pdgAxis}); - - histos.add("hRecoFakePtEta", - "Reco tracks without MC label (fakes / unmatched); p_{T} (GeV/c); #eta; Counts", - HistType::kTH2F, {ptAxis, etaAxis}); - - // Deuteron-proxy kinematics and PID QA - histos.add("hDeuteronProxyPt", - "Deuteron proxy p_{T};p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - histos.add("hDeuteronProxyEta", - "Deuteron proxy #eta;#eta;Counts", - HistType::kTH1F, {etaAxis}); - histos.add("hDeuteronProxyPhi", - "Deuteron proxy #varphi;#varphi;Counts", - HistType::kTH1F, {phiAxis}); - - histos.add("hNsigmaTPCDeuteron", - "TPC n#sigma_{d};n#sigma^{TPC}_{d};Counts", - HistType::kTH1F, {nsAxis}); - histos.add("hNsigmaTOFDeuteron", - "TOF n#sigma_{d};n#sigma^{TOF}_{d};Counts", - HistType::kTH1F, {nsAxis}); - - histos.add("hNsigmaTPCDeuteronVsP", - "TPC n#sigma_{d} vs p; p (GeV/c); n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronVsP", - "TOF n#sigma_{d} vs p; p (GeV/c); n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // Kaon kinematics and PID QA - histos.add("hKaonPt", - "Kaon p_{T};p_{T} (GeV/c);Counts", - HistType::kTH1F, {ptAxis}); - histos.add("hKaonEta", - "Kaon #eta;#eta;Counts", - HistType::kTH1F, {etaAxis}); - histos.add("hKaonPhi", - "Kaon #varphi;#varphi;Counts", - HistType::kTH1F, {phiAxis}); - - histos.add("hNsigmaTPCKaon", - "TPC n#sigma_{K};n#sigma^{TPC}_{K};Counts", - HistType::kTH1F, {nsAxis}); - histos.add("hNsigmaTOFKaon", - "TOF n#sigma_{K};n#sigma^{TOF}_{K};Counts", - HistType::kTH1F, {nsAxis}); - - histos.add("hNsigmaTPCKaonVsP", - "TPC n#sigma_{K} vs p; p (GeV/c); n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFKaonVsP", - "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - - // --- Candidate PID QA after final selected K/p/d PID cuts --- - // These histograms are needed for PID studies in the same pT intervals used by the analysis. - histos.add("hTOFBetaVsPt_K", - "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - histos.add("hTOFBetaVsPt_P", - "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - histos.add("hTOFBetaVsPt_D", - "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", - HistType::kTH2F, {ptAxis, betaAxis}); - - histos.add("hNsigmaTPCKaonVsPt", - "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFKaonVsPt", - "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - - histos.add("hNsigmaTPCProtonVsP", - "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTOFProtonVsP", - "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {pAxis, nsAxis}); - histos.add("hNsigmaTPCProtonVsPt", - "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFProtonVsPt", - "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - - histos.add("hNsigmaTPCDeuteronVsPt", - "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add("hNsigmaTOFDeuteronVsPt", - "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {ptAxis, nsAxis}); - histos.add( - "hNsigmaTPCProtonForSelectedDeuteronVsPt", - "TPC n#sigma_{p} for selected d candidates vs p_{T};" - "p_{T} (GeV/c);" - "n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, - {ptAxis, nsAxis}); - - histos.add( - "hNsigmaTOFProtonForSelectedDeuteronVsPt", - "TOF n#sigma_{p} for selected d candidates vs p_{T};" - "p_{T} (GeV/c);" - "n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, - {ptAxis, nsAxis}); - - histos.add( - "hTPCNsigmaDeVsPrForSelectedDeuteron", - "TPC PID overlap for selected d candidates;" - "n#sigma^{TPC}_{d};" - "n#sigma^{TPC}_{p};Counts", - HistType::kTH2F, - {nsAxis, nsAxis}); - - histos.add( - "hTOFNsigmaDeVsPrForSelectedDeuteron", - "TOF PID overlap for selected d candidates;" - "n#sigma^{TOF}_{d};" - "n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, - {nsAxis, nsAxis}); - - histos.add("hTPCvsTOFNsigma_K", - "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - histos.add("hTPCvsTOFNsigma_P", - "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - histos.add("hTPCvsTOFNsigma_D", - "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", - HistType::kTH2F, {nsAxis, nsAxis}); - - // --- DCA QA for final selected K/p/d candidates --- - // Filled only when lstarEnablePidQA = 1. - histos.add("hDCAxyVsPt_K", - "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_K", - "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); - - histos.add("hDCAxyVsPt_P", - "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_P", - "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); - - histos.add("hDCAxyVsPt_D", - "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaXYAxis}); - histos.add("hDCAzVsPt_D", - "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", - HistType::kTH2F, {ptAxis, dcaZAxis}); + if (lstarEnablePidQA.value != 0) { + // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only + // TPC dE/dx vs total momentum + histos.add("hTPCdEdxVsP", + "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + // TOF #beta vs total momentum + histos.add("hTOFBetaVsP", + "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + histos.add("hHasTOFVsP", + "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", + HistType::kTH2F, {pAxis, tofMatchAxis}); + + histos.add("hHasTOFVsPt", + "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", + HistType::kTH2F, {ptAxis, tofMatchAxis}); + + // --- Per-species inclusive PID QA before final candidate PID cuts --- + // Species tagging here uses classifyPidSpecies() and is meant only for QA. + histos.add("hTPCdEdxVsP_Pi", + "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_K", + "TPC dE/dx vs p (tagged K);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_P", + "TPC dE/dx vs p (tagged p);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_D", + "TPC dE/dx vs p (tagged d);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + histos.add("hTOFBetaVsP_Pi", + "TOF #beta vs p (tagged #pi);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_K", + "TOF #beta vs p (tagged K);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_P", + "TOF #beta vs p (tagged p);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_D", + "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. + // These are separate from the final-candidate PID QA histograms. + histos.add("hNsigmaTPCPionTaggedVsP", + "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFPionTaggedVsP", + "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCKaonTaggedVsP", + "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonTaggedVsP", + "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonTaggedVsP", + "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonTaggedVsP", + "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronTaggedVsP", + "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronTaggedVsP", + "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + } + + if (doprocessMCQA && lstarFillMCTruth.value != 0) { + // --- MC QA (AO2D-MC mode) --- + histos.add("hMcPrimariesPtEta", + "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + + histos.add("hRecoMatchedPdg", + "Reco tracks matched to MC (PDG); PDG code; Counts", + HistType::kTH1F, {pdgAxis}); + + histos.add("hRecoFakePtEta", + "Reco tracks without MC label (fakes / unmatched); p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + } + + if (lstarEnablePidQA.value != 0) { + // Deuteron-proxy kinematics and PID QA + histos.add("hDeuteronProxyPt", + "Deuteron proxy p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hDeuteronProxyEta", + "Deuteron proxy #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hDeuteronProxyPhi", + "Deuteron proxy #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCDeuteron", + "TPC n#sigma_{d};n#sigma^{TPC}_{d};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFDeuteron", + "TOF n#sigma_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsP", + "TPC n#sigma_{d} vs p; p (GeV/c); n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsP", + "TOF n#sigma_{d} vs p; p (GeV/c); n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // Kaon kinematics and PID QA + histos.add("hKaonPt", + "Kaon p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hKaonEta", + "Kaon #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hKaonPhi", + "Kaon #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCKaon", + "TPC n#sigma_{K};n#sigma^{TPC}_{K};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFKaon", + "TOF n#sigma_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCKaonVsP", + "TPC n#sigma_{K} vs p; p (GeV/c); n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsP", + "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // --- Candidate PID QA after final selected K/p/d PID cuts --- + // These histograms are needed for PID studies in the same pT intervals used by the analysis. + histos.add("hTOFBetaVsPt_K", + "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_P", + "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_D", + "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + + histos.add("hNsigmaTPCKaonVsPt", + "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsPt", + "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonVsP", + "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsP", + "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTPCProtonVsPt", + "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsPt", + "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsPt", + "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsPt", + "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add( + "hNsigmaTPCProtonForSelectedDeuteronVsPt", + "TPC n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hNsigmaTOFProtonForSelectedDeuteronVsPt", + "TOF n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hTPCNsigmaDeVsPrForSelectedDeuteron", + "TPC PID overlap for selected d candidates;" + "n#sigma^{TPC}_{d};" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add( + "hTOFNsigmaDeVsPrForSelectedDeuteron", + "TOF PID overlap for selected d candidates;" + "n#sigma^{TOF}_{d};" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add("hTPCvsTOFNsigma_K", + "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_P", + "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_D", + "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + + // --- DCA QA for final selected K/p/d candidates --- + // Filled only when lstarEnablePidQA = 1. + histos.add("hDCAxyVsPt_K", + "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_K", + "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_P", + "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_P", + "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_D", + "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_D", + "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + } + bookMC(); + } + + static constexpr int PdgLambdaStar = static_cast(PDG_t::kLambda1520); + static constexpr int PdgDeuteron = static_cast(o2::constants::physics::Pdg::kDeuteron); + // MC outputs are 2D mass/pT histograms. They add no high-dimensional maps. + std::array, 5> mcGenSpectra{}; + std::array, 2>, 4> mcRecoProxy{}; + std::shared_ptr mcRecoPKTrue, mcPKResponse, mcProxyResponse; + + bool mixingEnabled() const + { + return lstarEnableSparse.value != 0 && lstarNoMixedEvents.value > 0; + } + + void bookMC() + { + const std::vector massPt{ + AxisSpec{400, 1.4, 1.8, "Invariant mass (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "Pair p_{T} (GeV/c)"}}; + const std::vector response{ + AxisSpec{100, 0., 10., "Generated pair p_{T} (GeV/c)"}, + AxisSpec{100, 0., 10., "Reconstructed pair p_{T} (GeV/c)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;Stage;Events", HistType::kTH1D, + {AxisSpec{3, 0.5, 3.5, "1=all, 2=vertex, 3=vertex+truth INEL>0"}}); + const std::array names{ + "hMCGenPKUnlike", "hMCGenPKLike", "hMCGenProxyUnlike", "hMCGenProxyLike", "hMCGenPKTrueLambdaStar"}; + for (size_t i = 0; i < names.size(); ++i) { + mcGenSpectra[i] = histos.add(names[i], "Generated fiducial pairs", HistType::kTH2D, massPt, true); + } + } + if (doprocessMCReco) { + const std::array categories{"TrueD", "ProtonMisID", "Other", "Unmatched"}; + for (size_t i = 0; i < categories.size(); ++i) { + for (size_t sign = 0; sign < mcRecoProxy[i].size(); ++sign) { + const std::string name = std::string("hMCRecoProxy") + categories[i] + (sign == 0 ? "Unlike" : "Like"); + mcRecoProxy[i][sign] = histos.add(name.c_str(), "Reconstructed proxy truth component", HistType::kTH2D, massPt, true); + } + } + mcRecoPKTrue = histos.add("hMCRecoPKTrueLambdaStar", "Selected true common-parent pK", HistType::kTH2D, massPt, true); + mcPKResponse = histos.add("hMCRecoPKPtResponse", "Selected true common-parent pK;Generated pair pT;Reconstructed pair pT", HistType::kTH2D, response, true); + mcProxyResponse = histos.add("hMCRecoProxyPtResponse", "Selected true primary dK pairs;Generated proxy pair pT;Reconstructed proxy pair pT", HistType::kTH2D, response, true); + } + } + + template + static TruthInfo particleTruth(TParticle const& particle) + { + TruthInfo info; + info.id = particle.globalIndex(); + info.collisionId = particle.mcCollisionId(); + info.pdg = particle.pdgCode(); + info.px = particle.px(); + info.py = particle.py(); + info.pz = particle.pz(); + info.primary = particle.isPhysicalPrimary(); + for (auto const& mother : particle.template mothers_as()) { + if (std::abs(mother.pdgCode()) == PdgLambdaStar && mother.mcCollisionId() == info.collisionId) { + info.lambdaMothers.push_back(mother.globalIndex()); + } + } + return info; + } + + template + static TruthInfo trackTruth(TTrack const& track, int collisionId) + { + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != collisionId) { + return {}; // pileup or wrong-collision labels do not enter truth components + } + return particleTruth(particle); + } + + static bool commonLambdaStar(TruthInfo const& proton, TruthInfo const& kaon) + { + if (std::abs(proton.pdg) != PdgProton || std::abs(kaon.pdg) != PdgKaon || + proton.pdg * kaon.pdg >= 0 || proton.id == kaon.id || + proton.collisionId != kaon.collisionId) { + return false; + } + for (auto const& id : proton.lambdaMothers) { + if (std::find(kaon.lambdaMothers.begin(), kaon.lambdaMothers.end(), id) != kaon.lambdaMothers.end()) { + return true; + } + } + return false; + } + + template + void fillRecoMC(TCandidate const& candidate, KaonCand const& kaon, double mass, float pt, bool proxy) + { + const auto& a = candidate.truth; + const auto& b = kaon.truth; + if (!proxy) { + if (commonLambdaStar(a, b) && chargeFromPdg(a.pdg) == candidate.charge && chargeFromPdg(b.pdg) == kaon.charge) { + mcRecoPKTrue->Fill(mass, pt); + mcPKResponse->Fill(ptFromPxPy(a.px + b.px, a.py + b.py), pt); + } + return; + } + size_t category = 2; // all other labelled species, secondaries included + if (a.id < 0 || b.id < 0) { + category = 3; + } else if (std::abs(b.pdg) == PdgKaon && chargeFromPdg(b.pdg) == kaon.charge && chargeFromPdg(a.pdg) == candidate.charge) { + if (std::abs(a.pdg) == PdgDeuteron) { + category = 0; + } else if (std::abs(a.pdg) == PdgProton) { + category = 1; + } + } + const size_t sign = candidate.charge * kaon.charge < 0 ? 0 : 1; + mcRecoProxy[category][sign]->Fill(mass, pt); + if (category == 0 && a.primary && b.primary && a.id != b.id) { + mcProxyResponse->Fill(ptFromPxPy(ProxyMomentumScale * a.px + b.px, ProxyMomentumScale * a.py + b.py), pt); + } + // TrueD means true d + true K, not a truth tag of resonance-fed coalescence. + } + + template + void fillGenerated(TCollision const& collision, TParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (!(std::abs(collision.posZ()) < lstarCutVertex.value)) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + // Truth INEL>0 uses charged physical primaries in |eta|<1, with no pT cut. + // It is independent of detector trigger/vertex reconstruction efficiency. + bool inel = false; + for (auto const& particle : particles) { + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (lstarRequireINELgt0.value != 0 && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + std::vector protons, kaons, deuterons; + for (auto const& particle : particles) { + // Use the DATA daughter kinematic acceptance for generated pair spectra. + // lstarCutMC* remains specific to the optional legacy track-QA process. + if (!(particle.pt() > lstarCutPtMin.value) || !(std::abs(particle.eta()) < lstarCutEtaMax.value) || + (lstarMCPrimaryOnly.value != 0 && !particle.isPhysicalPrimary())) { + continue; + } + switch (std::abs(particle.pdgCode())) { + case PdgProton: + protons.push_back(particleTruth(particle)); + break; + case PdgKaon: + kaons.push_back(particleTruth(particle)); + break; + case PdgDeuteron: + deuterons.push_back(particleTruth(particle)); + break; + default: + break; + } + } + auto fillPairs = [&](auto const& candidates, bool proxy) { + const float scale = proxy ? ProxyMomentumScale : 1.f; + for (auto const& a : candidates) { + for (auto const& b : kaons) { + if (a.id == b.id) { + continue; + } + const double mass = invariantMass(scale * a.px, scale * a.py, scale * a.pz, o2::constants::physics::MassProton, + b.px, b.py, b.pz, o2::constants::physics::MassKaonCharged); + const float px = scale * a.px + b.px, py = scale * a.py + b.py, pz = scale * a.pz + b.pz; + if (std::abs(rapidityFromMomentumAndMass(px, py, pz, mass)) > lstarLambdaAbsYMax.value) { + continue; + } + const float pt = ptFromPxPy(px, py); + const size_t sign = chargeFromPdg(a.pdg) * chargeFromPdg(b.pdg) < 0 ? 0 : 1; + mcGenSpectra[(proxy ? 2 : 0) + sign]->Fill(mass, pt); + if (!proxy && commonLambdaStar(a, b)) { + mcGenSpectra[4]->Fill(mass, pt); + } + } + } + }; + fillPairs(protons, false); + fillPairs(deuterons, true); } // AO2D-MC QA: truth primaries + reco-to-MC matching sanity plots @@ -1187,7 +1441,8 @@ struct Lambdastarproxy { std::array{m1, m2}); } - void process(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) + template + void processReco(TCollision const& collision, TTracks const& tracks) { histos.fill(HIST("hEventSelection"), 1.); // Use unfiltered collisions here so the first bin includes vertex rejects. @@ -1467,9 +1722,11 @@ struct Lambdastarproxy { static_cast( trkD.sign()), - .tid = - static_cast( - trkD.globalIndex())}); + .tid = static_cast(trkD.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + proxyCands.back().truth = trackTruth(trkD, collision.mcCollisionId()); + } } // Proton candidates (for genuine pK #Lambda^{*} reconstruction) @@ -1529,7 +1786,16 @@ struct Lambdastarproxy { const float pyP = ptP * std::sin(phiP); const float pzP = ptP * std::sinh(etaP); - protonCands.push_back(ProtonCand{.px = pxP, .py = pyP, .pz = pzP, .charge = static_cast(trkP.sign()), .tid = static_cast(trkP.globalIndex())}); + protonCands.push_back(ProtonCand{ + .px = pxP, + .py = pyP, + .pz = pzP, + .charge = static_cast(trkP.sign()), + .tid = static_cast(trkP.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + protonCands.back().truth = trackTruth(trkP, collision.mcCollisionId()); + } } // Kaon candidates @@ -1597,22 +1863,33 @@ struct Lambdastarproxy { const float pyK = ptK * std::sin(phiK); const float pzK = ptK * std::sinh(etaK); - kaonCands.push_back(KaonCand{.px = pxK, .py = pyK, .pz = pzK, .charge = static_cast(trkK.sign()), .tid = static_cast(trkK.globalIndex())}); + kaonCands.push_back(KaonCand{ + .px = pxK, + .py = pyK, + .pz = pzK, + .charge = static_cast(trkK.sign()), + .tid = static_cast(trkK.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + kaonCands.back().truth = trackTruth(trkK, collision.mcCollisionId()); + } } if (kaonCands.empty()) { // Still update mixing buffer so that later events can mix with this one. // The pK mixed-event background needs stored protons, while the proxy background // needs stored proxy candidates. Therefore, do not require proxy candidates here. - LStarMixEventEntry entry; - entry.mult = eventMult; - entry.zvtx = collision.posZ(); - entry.kaons = std::move(kaonCands); - entry.protons = std::move(protonCands); - entry.proxies = std::move(proxyCands); - mLStarMixEvents.push_front(std::move(entry)); - if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { - mLStarMixEvents.pop_back(); + if (mixingEnabled()) { + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); + + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } } return; } @@ -1641,6 +1918,9 @@ struct Lambdastarproxy { continue; } + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, false); + } const bool unlikeSignPK = (pr.charge * k.charge) < 0; if (unlikeSignPK) { histos.fill(HIST("hInvMassPKUnlike"), mass); @@ -1713,6 +1993,9 @@ struct Lambdastarproxy { continue; } + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, true); + } const bool unlikeSignProxy = (pr.charge * k.charge) < 0; @@ -1723,10 +2006,14 @@ struct Lambdastarproxy { const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; if (unlikeSignProxy) { histos.fill(HIST("hLambdaStarProxyControlUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } } else { histos.fill(HIST("hLambdaStarProxyControlLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } } } @@ -1753,42 +2040,50 @@ struct Lambdastarproxy { if (unlikeSignProxy) { - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKUnlikeSparse"), - mass, - massFullPK, - ptPair, - eventMult); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKUnlikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } } else { - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKLikeSparse"), - mass, - massFullPK, - ptPair, - eventMult); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKLikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } } } if (pr.protonLike) { if (unlikeSignProxy) { - histos.fill( - HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), - mass, - ptPair, - eventMult); + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), + mass, + ptPair, + eventMult); + } } else { - histos.fill( - HIST("hLambdaStarProxyProtonLikeLikeSparse"), - mass, - ptPair, - eventMult); + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeLikeSparse"), + mass, + ptPair, + eventMult); + } } } } @@ -1796,100 +2091,9 @@ struct Lambdastarproxy { } } - // --- MIXED-EVENT: current kaons + previous-event real protons --- - // This fills the standard pK mixed-event background. - for (auto const& prev : mLStarMixEvents) { - - if (std::abs(prev.zvtx - collision.posZ()) > - lstarMixZvtxMax.value) { - continue; - } - - if (std::abs(prev.mult - eventMult) > - lstarMixMultMax.value) { - continue; - } - - if (prev.protons.empty()) { - continue; - } - - for (auto const& pr : prev.protons) { - - for (auto const& k : kaonCands) { - - // Unlike-sign pK mixed-event background. - if ((pr.charge * k.charge) >= 0) { - continue; - } - - const double mass = - invariantMass( - pr.px, - pr.py, - pr.pz, - MassProton, - k.px, - k.py, - k.pz, - MassKaonCharged); - - const float pxTot = - pr.px + k.px; - - const float pyTot = - pr.py + k.py; - - const float pzTot = - pr.pz + k.pz; - - const float ptPair = - ptFromPxPy( - pxTot, - pyTot); - - const float yPair = - rapidityFromMomentumAndMass( - pxTot, - pyTot, - pzTot, - mass); - - if (std::abs(yPair) > - lstarLambdaAbsYMax.value) { - continue; - } - - if (lstarEnableSparse.value != 0) { - - histos.fill( - HIST( - "hLambdaStarPKMixedSparse"), - mass, - ptPair, - eventMult); - } - } - } - } - - // --- MIXED-EVENT: current proxies + previous-event kaons --- - // This fills the deuteron-proxy mixed-event background. - // - // For every d-selected proxy candidate we calculate: - // - // 1) M_{(d/2)K} - // using p_proxy = p_track / 2 - // - // 2) M_{pK}^{full} - // using the FULL measured momentum of the same d-selected track, - // but assigning the proton mass. - // - // The standard proxy mixed spectrum requires only the proxy rapidity - // acceptance. The 2D proxy-vs-full-pK map additionally requires the - // full-pK hypothesis to satisfy the same rapidity acceptance. - if (hasProxyCandidates) { - + if (mixingEnabled()) { + // --- MIXED-EVENT: current kaons + previous-event real protons --- + // This fills the standard pK mixed-event background. for (auto const& prev : mLStarMixEvents) { if (std::abs(prev.zvtx - collision.posZ()) > @@ -1902,23 +2106,19 @@ struct Lambdastarproxy { continue; } - if (prev.kaons.empty()) { + if (prev.protons.empty()) { continue; } - for (auto const& pr : proxyCands) { + for (auto const& pr : prev.protons) { - for (auto const& k : prev.kaons) { + for (auto const& k : kaonCands) { - // Unlike-sign proxy-K mixed-event background. + // Unlike-sign pK mixed-event background. if ((pr.charge * k.charge) >= 0) { continue; } - // ---------------------------------------------------------- - // Proxy hypothesis: p_proxy = p_d / 2 - // ---------------------------------------------------------- - const double mass = invariantMass( pr.px, @@ -1944,107 +2144,236 @@ struct Lambdastarproxy { pxTot, pyTot); - const float yProxy = + const float yPair = rapidityFromMomentumAndMass( pxTot, pyTot, pzTot, mass); - // Standard proxy rapidity acceptance. - if (std::abs(yProxy) > + if (std::abs(yPair) > lstarLambdaAbsYMax.value) { continue; } - // ---------------------------------------------------------- - // Full-momentum proton hypothesis for the SAME d-selected - // track. - // ---------------------------------------------------------- + if (lstarEnableSparse.value != 0) { - const double massFullPK = - invariantMass( - pr.pxFull, - pr.pyFull, - pr.pzFull, - MassProton, - k.px, - k.py, - k.pz, - MassKaonCharged); + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarPKMixedSparse"), + mass, + ptPair, + eventMult); + } + } + } + } + } - const float pxTotFullPK = - pr.pxFull + k.px; + // --- MIXED-EVENT: current proxies + previous-event kaons --- + // This fills the deuteron-proxy mixed-event background. + // + // For every d-selected proxy candidate we calculate: + // + // 1) M_{(d/2)K} + // using p_proxy = p_track / 2 + // + // 2) M_{pK}^{full} + // using the FULL measured momentum of the same d-selected track, + // but assigning the proton mass. + // + // The standard proxy mixed spectrum requires only the proxy rapidity + // acceptance. The 2D proxy-vs-full-pK map additionally requires the + // full-pK hypothesis to satisfy the same rapidity acceptance. + if (hasProxyCandidates) { + + for (auto const& prev : mLStarMixEvents) { + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { + continue; + } - const float pyTotFullPK = - pr.pyFull + k.py; + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { + continue; + } - const float pzTotFullPK = - pr.pzFull + k.pz; + if (prev.kaons.empty()) { + continue; + } - const float yFullPK = - rapidityFromMomentumAndMass( - pxTotFullPK, - pyTotFullPK, - pzTotFullPK, - massFullPK); + for (auto const& pr : proxyCands) { - if (lstarEnableSparse.value != 0) { + for (auto const& k : prev.kaons) { - if (lstarEnableProxyControls.value != 0) { - const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; - histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); - histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + // Unlike-sign proxy-K mixed-event background. + if ((pr.charge * k.charge) >= 0) { + continue; } - // Standard mixed-event proxy spectrum. - // - // This requires only the proxy hypothesis to satisfy - // |y| < lstarLambdaAbsYMax. - histos.fill( - HIST( - "hLambdaStarProxyMixedSparse"), - mass, - ptPair, - eventMult); - - // Mixed-event 2D map. - // - // Since this histogram explicitly represents both - // hypotheses, require the full-pK hypothesis to satisfy - // the same configurable rapidity acceptance as well. - if (std::abs(yFullPK) <= + // ---------------------------------------------------------- + // Proxy hypothesis: p_proxy = p_d / 2 + // ---------------------------------------------------------- + + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + // Standard proxy rapidity acceptance. + if (std::abs(yProxy) > lstarLambdaAbsYMax.value) { + continue; + } - histos.fill( - HIST( - "hLambdaStarProxyVsFullPKMixedSparse"), - mass, - massFullPK, - ptPair, - eventMult); + // ---------------------------------------------------------- + // Full-momentum proton hypothesis for the SAME d-selected + // track. + // ---------------------------------------------------------- + + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } + + // Standard mixed-event proxy spectrum. + // + // This requires only the proxy hypothesis to satisfy + // |y| < lstarLambdaAbsYMax. + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyMixedSparse"), + mass, + ptPair, + eventMult); + } + + // Mixed-event 2D map. + // + // Since this histogram explicitly represents both + // hypotheses, require the full-pK hypothesis to satisfy + // the same configurable rapidity acceptance as well. + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKMixedSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } + } } } } } } - } - // --- Update mixing buffer with current event --- - LStarMixEventEntry entry; - entry.mult = eventMult; - entry.zvtx = collision.posZ(); - entry.kaons = std::move(kaonCands); - entry.protons = std::move(protonCands); - entry.proxies = std::move(proxyCands); + // --- Update mixing buffer with current event --- + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); - mLStarMixEvents.push_front(std::move(entry)); - if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { - mLStarMixEvents.pop_back(); + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } } } - PROCESS_SWITCH(Lambdastarproxy, process, "Lambda* proxy via (d/2)+K", true); + void processData(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) + { + processReco(collision, tracks); + } + + // A literal process() is always invoked by O2 in addition to optional methods. + // Keep the established JSON key "process" while registering data only once. + PROCESS_SWITCH_FULL(Lambdastarproxy, processData, process, "Lambda* proxy via (d/2)+K", true); + + void processMCReco(CollisionsWithEvSelMC::iterator const& collision, + FilteredTracksMC const& tracks, aod::McParticles const&, + aod::McCollisions const&) + { + // Inclusive reconstructed spectra include unmatched tracks and collisions, + // just as in data. Truth components require a valid same-event MC label. + processReco(collision, tracks); + } + PROCESS_SWITCH(Lambdastarproxy, processMCReco, "MC: data-identical reconstruction and truth-component spectra", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + fillGenerated(collision, particles); + } + PROCESS_SWITCH(Lambdastarproxy, processMCGen, "MC: generated pK and (d/2)K spectra, without reconstructed-event requirements", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) From 7ff6f62633e12fc532ac88c3d2b632fadae077f1 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Sun, 18 Oct 2026 23:24:31 +0900 Subject: [PATCH 334/648] [PWGLF] Fix derived index table labels for AO2D merging (#18301) Co-authored-by: Bong-Hwi Lim --- PWGLF/DataModel/LFK1MlTables.h | 10 +++++----- PWGLF/DataModel/LFOmega2012MlTables.h | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h index 892d2adefe0..6add0d58252 100644 --- a/PWGLF/DataModel/LFK1MlTables.h +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -82,7 +82,7 @@ DECLARE_SOA_TABLE(K1MlEvents, "AOD", "K1MLEVENT", k1ml::K1Centrality, k1ml::K1Multiplicity, k1ml::K1RecINELgt0); namespace k1ml { -DECLARE_SOA_INDEX_COLUMN_FULL(K1MlEvent, k1MlEvent, int, K1MlEvents, ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlEvent, k1MlEvent, int, K1MlEvents, "K1mlEvents", ""); } // namespace k1ml DECLARE_SOA_TABLE(K1MlTracks, "AOD", "K1MLTRACK", o2::soa::Index<>, k1ml::K1MlEventId, k1ml::K1SourceTrackId, @@ -92,9 +92,9 @@ DECLARE_SOA_TABLE(K1MlTracks, "AOD", "K1MLTRACK", k1ml::K1CrossedRows, k1ml::K1ItsClusterMap); namespace k1ml { -DECLARE_SOA_INDEX_COLUMN_FULL(K1MlKaonTrack, k1MlKaonTrack, int, K1MlTracks, "_Kaon"); -DECLARE_SOA_INDEX_COLUMN_FULL(K1MlSamePionTrack, k1MlSamePionTrack, int, K1MlTracks, "_Same"); -DECLARE_SOA_INDEX_COLUMN_FULL(K1MlOppPionTrack, k1MlOppPionTrack, int, K1MlTracks, "_Opp"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlKaonTrack, k1MlKaonTrack, int, K1MlTracks, "K1mlTracks", "_Kaon"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlSamePionTrack, k1MlSamePionTrack, int, K1MlTracks, "K1mlTracks", "_Same"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlOppPionTrack, k1MlOppPionTrack, int, K1MlTracks, "K1mlTracks", "_Opp"); } // namespace k1ml DECLARE_SOA_TABLE(K1MlCandidates, "AOD", "K1MLCANDIDATE", o2::soa::Index<>, k1ml::K1MlEventId, @@ -105,7 +105,7 @@ DECLARE_SOA_TABLE(K1MlCandidates, "AOD", "K1MLCANDIDATE", k1ml::K1BaselinePassBits); namespace k1ml { -DECLARE_SOA_INDEX_COLUMN_FULL(K1MlCandidate, k1MlCandidate, int, K1MlCandidates, ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlCandidate, k1MlCandidate, int, K1MlCandidates, "K1mlCandidates", ""); } // namespace k1ml DECLARE_SOA_TABLE(K1MlInputs, "AOD", "K1MLINPUT", o2::soa::Index<>, k1ml::K1MlCandidateId, diff --git a/PWGLF/DataModel/LFOmega2012MlTables.h b/PWGLF/DataModel/LFOmega2012MlTables.h index 5cca99cbd46..dd81f009845 100644 --- a/PWGLF/DataModel/LFOmega2012MlTables.h +++ b/PWGLF/DataModel/LFOmega2012MlTables.h @@ -144,7 +144,7 @@ DECLARE_SOA_TABLE(Omega2012MlEvents, "AOD", "OMMLEVENT", omega2012ml::OmMultiplicity, omega2012ml::OmRecINELgt0); namespace omega2012ml { -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlEvent, omega2012MlEvent, int, Omega2012MlEvents, ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlEvent, omega2012MlEvent, int, Omega2012MlEvents, "OmmlEvents", ""); } // namespace omega2012ml DECLARE_SOA_TABLE(Omega2012MlCascades, "AOD", "OMMLCASCADE", @@ -186,10 +186,10 @@ DECLARE_SOA_TABLE(Omega2012MlTracks, "AOD", "OMMLTRACK", omega2012ml::OmCrossedRows, omega2012ml::OmItsClusterMap); namespace omega2012ml { -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlCascade, omega2012MlCascade, int, Omega2012MlCascades, ""); -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlV0, omega2012MlV0, int, Omega2012MlV0s, ""); -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlPionTrack, omega2012MlPionTrack, int, Omega2012MlTracks, "_Pion"); -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlKaonTrack, omega2012MlKaonTrack, int, Omega2012MlTracks, "_Kaon"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlCascade, omega2012MlCascade, int, Omega2012MlCascades, "OmmlCascades", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlV0, omega2012MlV0, int, Omega2012MlV0s, "OmmlV0s", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlPionTrack, omega2012MlPionTrack, int, Omega2012MlTracks, "OmmlTracks", "_Pion"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlKaonTrack, omega2012MlKaonTrack, int, Omega2012MlTracks, "OmmlTracks", "_Kaon"); } // namespace omega2012ml // Mode A: Xi K0S @@ -200,7 +200,7 @@ DECLARE_SOA_TABLE(Omega2012MlXiK0sCandidates, "AOD", "OMMLXIK0SCAND", omega2012ml::OmOpeningAngle, omega2012ml::OmCharge, omega2012ml::OmPassBits); namespace omega2012ml { -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlXiK0sCandidate, omega2012MlXiK0sCandidate, int, Omega2012MlXiK0sCandidates, ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXiK0sCandidate, omega2012MlXiK0sCandidate, int, Omega2012MlXiK0sCandidates, "OmmlXiK0sCands", ""); } // namespace omega2012ml DECLARE_SOA_TABLE(Omega2012MlXiK0sInputs, "AOD", "OMMLXIK0SINPUT", o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, @@ -219,7 +219,7 @@ DECLARE_SOA_TABLE(Omega2012MlXi1530KCandidates, "AOD", "OMMLXI1530KCAND", omega2012ml::OmChargePattern, omega2012ml::OmPassBits); namespace omega2012ml { -DECLARE_SOA_INDEX_COLUMN_FULL(Omega2012MlXi1530KCandidate, omega2012MlXi1530KCandidate, int, Omega2012MlXi1530KCandidates, ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXi1530KCandidate, omega2012MlXi1530KCandidate, int, Omega2012MlXi1530KCandidates, "OmmlXi1530KCands", ""); } // namespace omega2012ml DECLARE_SOA_TABLE(Omega2012MlXi1530KInputs, "AOD", "OMMLXI1530KINP", o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, From 698b1d68f7f563ed61902cc794b09745e3d98129 Mon Sep 17 00:00:00 2001 From: Cristian Andrei Date: Sun, 18 Oct 2026 17:27:41 +0300 Subject: [PATCH 335/648] [PWGLF] Pwglf eventshape coex split (#18303) --- PWGLF/DataModel/LFEventTopologyTables.h | 120 -------------- .../GlobalEventProperties/CMakeLists.txt | 2 +- .../GlobalEventProperties/eventShapeCoex.cxx | 153 +++++++++++++++--- 3 files changed, 136 insertions(+), 139 deletions(-) delete mode 100644 PWGLF/DataModel/LFEventTopologyTables.h diff --git a/PWGLF/DataModel/LFEventTopologyTables.h b/PWGLF/DataModel/LFEventTopologyTables.h deleted file mode 100644 index a42baa8c94d..00000000000 --- a/PWGLF/DataModel/LFEventTopologyTables.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file LFEventTopologyTables.h -/// \brief Per-collision forward/backward sub-event tables, reconstructed and generated level. -/// \author Cristian Andrei - -#ifndef PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ -#define PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ - -#include - -#include - -namespace o2::aod -{ - -namespace evshapecoex -{ -DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! source collision global index (DF-local; not an index column) -DECLARE_SOA_COLUMN(RunNumber, runNumber, int); //! run number -DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! global bunch crossing -DECLARE_SOA_COLUMN(PosZ, posZ, float); //! primary-vertex z (cm) -DECLARE_SOA_COLUMN(MultFT0A, multFT0A, float); //! FT0-A amplitude -DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); //! FT0-C amplitude -DECLARE_SOA_COLUMN(NF, nF, uint16_t); //! track count, forward sub-event -DECLARE_SOA_COLUMN(NB, nB, uint16_t); //! track count, backward sub-event -DECLARE_SOA_COLUMN(NGap, nGap, uint16_t); //! track count, central gap -DECLARE_SOA_COLUMN(AF, aF, float); //! mean pT, forward (GeV/c); NaN if empty -DECLARE_SOA_COLUMN(AB, aB, float); //! mean pT, backward (GeV/c); NaN if empty -DECLARE_SOA_COLUMN(SumPt2F, sumPt2F, float); //! sum of pT^2, forward (GeV^2/c^2) -DECLARE_SOA_COLUMN(SumPt2B, sumPt2B, float); //! sum of pT^2, backward (GeV^2/c^2) -DECLARE_SOA_COLUMN(QxF, qxF, float); //! sum of cos(2 phi), forward -DECLARE_SOA_COLUMN(QyF, qyF, float); //! sum of sin(2 phi), forward -DECLARE_SOA_COLUMN(QxB, qxB, float); //! sum of cos(2 phi), backward -DECLARE_SOA_COLUMN(QyB, qyB, float); //! sum of sin(2 phi), backward -DECLARE_SOA_COLUMN(Qx4F, qx4F, float); //! sum of cos(4 phi), forward -DECLARE_SOA_COLUMN(Qy4F, qy4F, float); //! sum of sin(4 phi), forward -DECLARE_SOA_COLUMN(Qx4B, qx4B, float); //! sum of cos(4 phi), backward -DECLARE_SOA_COLUMN(Qy4B, qy4B, float); //! sum of sin(4 phi), backward -DECLARE_SOA_COLUMN(NPlus, nPlus, uint16_t); //! positive tracks, forward + backward -DECLARE_SOA_COLUMN(NMinus, nMinus, uint16_t); //! negative tracks, forward + backward -DECLARE_SOA_COLUMN(QaBits, qaBits, uint16_t); //! event-selection bits, order as QaBitOrder in eventShapeCoex.cxx -DECLARE_SOA_COLUMN(OccTracks, occTracks, int); //! track occupancy in time range -DECLARE_SOA_COLUMN(OccFT0C, occFT0C, float); //! FT0C occupancy in time range -DECLARE_SOA_COLUMN(NumContrib, numContrib, uint16_t); //! number of PV contributors -DECLARE_SOA_COLUMN(CollTimeRes, collTimeRes, float); //! collision time resolution (ns) -DECLARE_SOA_COLUMN(NPVC, nPVC, uint16_t); //! PV contributors among the selected tracks -} // namespace evshapecoex - -DECLARE_SOA_TABLE(EvShapeCoex, "AOD", "EVSHAPECOEX", //! per-collision forward/backward sub-events, reconstructed level - o2::soa::Index<>, - evshapecoex::CollisionId, evshapecoex::RunNumber, evshapecoex::GlobalBC, evshapecoex::PosZ, - evshapecoex::MultFT0A, evshapecoex::MultFT0C, - evshapecoex::NF, evshapecoex::NB, evshapecoex::NGap, - evshapecoex::AF, evshapecoex::AB, evshapecoex::SumPt2F, evshapecoex::SumPt2B, - evshapecoex::QxF, evshapecoex::QyF, evshapecoex::QxB, evshapecoex::QyB, - evshapecoex::Qx4F, evshapecoex::Qy4F, evshapecoex::Qx4B, evshapecoex::Qy4B, - evshapecoex::NPlus, evshapecoex::NMinus, - evshapecoex::QaBits, evshapecoex::OccTracks, evshapecoex::OccFT0C, - evshapecoex::NumContrib, evshapecoex::CollTimeRes, evshapecoex::NPVC); -using EvShapeCoexRow = EvShapeCoex::iterator; - -// Generator level: charged physical primaries, one row per McCollision (no event selection). -namespace evshapecoexgen -{ -DECLARE_SOA_COLUMN(McCollisionId, mcCollisionId, int); //! source McCollision global index (DF-local; not an index column) -DECLARE_SOA_COLUMN(PosZ, posZ, float); //! generated primary-vertex z (cm) -DECLARE_SOA_COLUMN(NFwdA, nFwdA, uint16_t); //! charged primaries in the FT0-A acceptance -DECLARE_SOA_COLUMN(NFwdC, nFwdC, uint16_t); //! charged primaries in the FT0-C acceptance -DECLARE_SOA_COLUMN(NF, nF, uint16_t); //! particle count, forward sub-event -DECLARE_SOA_COLUMN(NB, nB, uint16_t); //! particle count, backward sub-event -DECLARE_SOA_COLUMN(NGap, nGap, uint16_t); //! particle count, central gap -DECLARE_SOA_COLUMN(AF, aF, float); //! mean pT, forward (GeV/c); NaN if empty -DECLARE_SOA_COLUMN(AB, aB, float); //! mean pT, backward (GeV/c); NaN if empty -DECLARE_SOA_COLUMN(SumPt2F, sumPt2F, float); //! sum of pT^2, forward (GeV^2/c^2) -DECLARE_SOA_COLUMN(SumPt2B, sumPt2B, float); //! sum of pT^2, backward (GeV^2/c^2) -DECLARE_SOA_COLUMN(QxF, qxF, float); //! sum of cos(2 phi), forward -DECLARE_SOA_COLUMN(QyF, qyF, float); //! sum of sin(2 phi), forward -DECLARE_SOA_COLUMN(QxB, qxB, float); //! sum of cos(2 phi), backward -DECLARE_SOA_COLUMN(QyB, qyB, float); //! sum of sin(2 phi), backward -DECLARE_SOA_COLUMN(Qx4F, qx4F, float); //! sum of cos(4 phi), forward -DECLARE_SOA_COLUMN(Qy4F, qy4F, float); //! sum of sin(4 phi), forward -DECLARE_SOA_COLUMN(Qx4B, qx4B, float); //! sum of cos(4 phi), backward -DECLARE_SOA_COLUMN(Qy4B, qy4B, float); //! sum of sin(4 phi), backward -DECLARE_SOA_COLUMN(NPlus, nPlus, uint16_t); //! positive particles, forward + backward -DECLARE_SOA_COLUMN(NMinus, nMinus, uint16_t); //! negative particles, forward + backward -} // namespace evshapecoexgen - -DECLARE_SOA_TABLE(EvShapeCoexGen, "AOD", "EVSHAPECOEXGEN", //! per-McCollision forward/backward sub-events, generator level - o2::soa::Index<>, - evshapecoexgen::McCollisionId, evshapecoexgen::PosZ, - evshapecoexgen::NFwdA, evshapecoexgen::NFwdC, - evshapecoexgen::NF, evshapecoexgen::NB, evshapecoexgen::NGap, - evshapecoexgen::AF, evshapecoexgen::AB, evshapecoexgen::SumPt2F, evshapecoexgen::SumPt2B, - evshapecoexgen::QxF, evshapecoexgen::QyF, evshapecoexgen::QxB, evshapecoexgen::QyB, - evshapecoexgen::Qx4F, evshapecoexgen::Qy4F, evshapecoexgen::Qx4B, evshapecoexgen::Qy4B, - evshapecoexgen::NPlus, evshapecoexgen::NMinus); -using EvShapeCoexGenRow = EvShapeCoexGen::iterator; - -namespace evshapecoexmclabel -{ -DECLARE_SOA_INDEX_COLUMN_FULL(EvShapeCoexGen, evShapeCoexGen, int, EvShapeCoexGen, ""); //! matching EvShapeCoexGen row; negative if unlabelled -} // namespace evshapecoexmclabel - -DECLARE_SOA_TABLE(EvShapeCoexMcLabels, "AOD", "EVSHAPECOEXLBL", //! MC label, joinable with EvShapeCoex - o2::soa::Index<>, evshapecoexmclabel::EvShapeCoexGenId); -using EvShapeCoexMcLabel = EvShapeCoexMcLabels::iterator; - -} // namespace o2::aod - -#endif // PWGLF_DATAMODEL_LFEVENTTOPOLOGYTABLES_H_ diff --git a/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt index 6161ead0a4b..a93df119cbc 100644 --- a/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt +++ b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt @@ -11,5 +11,5 @@ o2physics_add_dpl_workflow(event-shape-coex SOURCES eventShapeCoex.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx index 4f91fdac417..c8f66fce50f 100644 --- a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx +++ b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx @@ -16,10 +16,12 @@ /// First O2Physics task of the AliPhysics PWGLF/SPECTRA/MultEvShape analyses. #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include @@ -109,6 +111,8 @@ struct Config { }; constexpr int NConfigs = 13; constexpr int INominal = 0; +constexpr int IPairFirst = 1; // GAP02 +constexpr int IPairSecond = 4; // GAP05, same events and stratum count as GAP02 constexpr std::array Configs = {{{.name = "NOM", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV1}, {.name = "GAP02", .etaFirst = 1, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, {.name = "GAP03", .etaFirst = 2, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, @@ -126,6 +130,7 @@ constexpr std::array Configs = {{{.name = "NOM", .etaFirst = 1 constexpr int NCountBins = 64; // larger counts in the overflow bin constexpr int NSubCountBins = 32; // per sub-event constexpr int MinTracksValid = 2; // per sub-event +constexpr int NCovariateQuantities = 7; // Data halves and subsample groups, defined on blocks of bunch crossings constexpr int NHalves = 2; @@ -134,6 +139,11 @@ constexpr int HalfBlockShift = 26; // 2^26 BC = 1.7 s constexpr int GroupBlockShift = 20; // 2^20 BC = 26 ms constexpr int NGroups = 20; +// MC only: nominal sums split by two hashes of the MC collision (run, globalBC) +constexpr int NSplitBins = 8; +constexpr int NSubsamples = 16; +constexpr uint64_t SplitSalt = 0x6a09e667f3bcc909ULL; + /// splitmix64 finaliser constexpr uint64_t mix64(uint64_t z) { @@ -149,6 +159,12 @@ uint64_t blockHash(int run, uint64_t globalBC, int shift) return mix64(mix64(static_cast(run)) ^ (globalBC >> shift)); } +/// Per-event hash, independent of the block hashes +uint64_t eventHash(int run, uint64_t globalBC) +{ + return mix64(mix64(static_cast(run) ^ SplitSalt) ^ globalBC); +} + struct SubEvent { int n = 0; double sumPt = 0.; @@ -223,11 +239,13 @@ struct Sums { std::array, NConfigs> config{}; // (N_fwd, stratum count, monomial) std::shared_ptr strata3; // (N_fwd, n_F, n_B, first-order monomial) std::shared_ptr occupancy; // (configuration, N_fwd, stratum count) + std::shared_ptr paired; // (N_fwd, stratum count, GAP02 x GAP05 first-order monomials) }; /// Nominal-configuration observables of an event struct Nominal { bool valid = false; + double nFwd = 0.; int nMid = 0; double xF = 0., xB = 0., vF = 0., vB = 0.; }; @@ -239,13 +257,16 @@ enum EventFlowBin { kFlowAll = 0, kFlowNominalValid, kFlowHalfA, kFlowHalfB, + kFlowNoMcLabel, NEventFlowBins }; -constexpr std::array EventFlowLabels = {"all", "sel8", "|z|", "pile-up V1", "nominal valid", "half A", "half B"}; +constexpr std::array EventFlowLabels = {"all", "sel8", "|z|", "pile-up V1", "nominal valid", "half A", "half B", "nominal valid, no MC label"}; } // namespace struct EventShapeCoex { HistogramRegistry registry{"registry"}; Service pdg{}; + Service ccdb{}; + ctpRateFetcher rateFetcher; Configurable vtxZCut{"vtxZCut", 10.0f, "Max |PV z| (cm)"}; Configurable etaFwdAMin{"etaFwdAMin", 3.5f, "FT0-A proxy: lower eta edge (generator level)"}; @@ -255,6 +276,12 @@ struct EventShapeCoex { ConfigurableAxis axisNFwd{"axisNFwd", {50, 1., 30000.}, "N_fwd = FT0A + FT0C amplitude (a fixed-width setting is made logarithmic)"}; ConfigurableAxis axisNFwdGen{"axisNFwdGen", {50, 1., 1000.}, "generator-level N_fwd = charged primaries in the FT0 acceptance (a fixed-width setting is made logarithmic)"}; ConfigurableAxis axisRun{"axisRun", {60000, 519999.5, 579999.5}, "run number (QA)"}; + ConfigurableAxis axisOccupancy{"axisOccupancy", {50, 0., 1000.}, "track occupancy in time range (QA; unset in underflow)"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -10., 10.}, "PV z (cm, QA)"}; + ConfigurableAxis axisRate{"axisRate", {100, 0., 1000.}, "interaction rate (kHz, QA; unavailable in underflow)"}; + Configurable irSource{"irSource", "T0VTX", "interaction-rate estimator for QA; empty = not fetched"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable doSplitSums{"doSplitSums", true, "MC: nominal sums split by hashed event bins and subsamples"}; Filter trackFilter = (aod::track::pt > PtMin) && (nabs(aod::track::eta) < EtaMax) && requireGlobalTrackInFilter(); @@ -263,9 +290,40 @@ struct EventShapeCoex { using MyTracks = soa::Filtered>; std::array sumsReco{}; - std::array, NHalves> designEffect{}; // (N_fwd, group, first-order monomial) + std::array, NHalves> groupSums{}; // (N_fwd, stratum count, group x first-order monomial) Sums sumsGen{}; + /// Nominal sums of MC events split by hashed event bin (full) and by (subsample, event bin) (sub) + struct SplitSums { + std::shared_ptr full; // (N_fwd, stratum count, event bin x monomial) + std::shared_ptr sub; // (N_fwd, stratum count, (subsample x event bin) x first-order monomial) + }; + SplitSums splitReco{}, splitGen{}; + + void bookSplit(SplitSums& split, const std::string& dir, const AxisSpec& nFwdAxis) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + split.full = registry.add(dir + "/full", "NOM sums per event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSplitBins * NMono, -0.5, NSplitBins * NMono - 0.5, "event bin x monomial"}}); + split.sub = registry.add(dir + "/sub", "NOM first-order sums per subsample and event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSubsamples * NSplitBins * NMono1, -0.5, NSubsamples * NSplitBins * NMono1 - 0.5, "(subsample x event bin) x monomial"}}); + split.full->SetBit(TH1::kIsNotW); + split.sub->SetBit(TH1::kIsNotW); + } + + void fillSplit(SplitSums& split, const Nominal& nominal, int run, uint64_t globalBC) + { + const uint64_t h = eventHash(run, globalBC); + const int bin = static_cast(h % NSplitBins); + const int subsample = static_cast((h >> 32) % NSubsamples); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + split.full->Fill(nominal.nFwd, nominal.nMid, bin * NMono + k, m[k]); + } + for (int k = 0; k < NMono1; ++k) { + split.sub->Fill(nominal.nFwd, nominal.nMid, (subsample * NSplitBins + bin) * NMono1 + k, m[k]); + } + } + /// Books the moment sums of one level under dir// void bookSums(Sums& sums, const std::string& dir, const std::string& half, const AxisSpec& nFwdAxis, int nConfigs) { @@ -290,6 +348,17 @@ struct EventShapeCoex { } sums.strata3 = registry.add(dir + "/NOM3/" + half, (label + "NOM in (n_F, n_B) strata").c_str(), HistType::kTHnD, {nFwdAxis, subCountAxis, subCountAxis, mono1Axis}); sums.occupancy = registry.add(dir + "/occupancy/" + half, "event counts per configuration", HistType::kTH3D, {configAxis, nFwdAxis, countAxis}); + if (nConfigs > IPairSecond) { + std::string pairName = dir; + pairName += "/"; + pairName += Configs[IPairFirst].name; + pairName += "x"; + pairName += Configs[IPairSecond].name; + pairName += "/"; + pairName += half; + sums.paired = registry.add(pairName, (label + "cross products of two configurations").c_str(), HistType::kTH3D, {nFwdAxis, countAxis, {NMono1 * NMono1, -0.5, NMono1 * NMono1 - 0.5, "monomial pair"}}); + sums.paired->SetBit(TH1::kIsNotW); + } } void init(InitContext const&) @@ -303,12 +372,15 @@ struct EventShapeCoex { if (nFwdAxis.nBins) { nFwdAxis.makeLogarithmic(); } - const AxisSpec groupAxis{NGroups, -0.5, NGroups - 0.5, "group"}; - const AxisSpec mono1Axis{NMono1, -0.5, NMono1 - 0.5, "monomial"}; + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec groupMonoAxis{NGroups * NMono1, -0.5, NGroups * NMono1 - 0.5, "group x monomial"}; for (int h = 0; h < NHalves; ++h) { bookSums(sumsReco[h], "accum", HalfName[h], nFwdAxis, NConfigs); - designEffect[h] = registry.add(std::string("designEffect/") + HalfName[h], "nominal first-order sums per block group", HistType::kTH3D, {nFwdAxis, groupAxis, mono1Axis}); - designEffect[h]->SetBit(TH1::kIsNotW); + groupSums[h] = registry.add(std::string("groups/") + HalfName[h], "nominal first-order sums per block group", HistType::kTH3D, {nFwdAxis, countAxis, groupMonoAxis}); + groupSums[h]->SetBit(TH1::kIsNotW); + } + if (doprocessRecoMC && doSplitSums) { + bookSplit(splitReco, "split", nFwdAxis); } // QA @@ -328,6 +400,14 @@ struct EventShapeCoex { const AxisSpec quantityAxis{5, -0.5, 4.5, "quantity"}; registry.add("qa/hMarginalSums", "nominal single-side sums", HistType::kTH3D, {nFwdAxis, {NCountBins, -0.5, NCountBins - 0.5, "n_{mid}"}, quantityAxis})->SetBit(TH1::kIsNotW); registry.add("qa/hRunSums", "nominal single-side sums per run", HistType::kTH2D, {{axisRun, "run"}, quantityAxis})->SetBit(TH1::kIsNotW); + // quantities: count, a_F, a_B, a_F^2, a_B^2, v_F, v_B + const AxisSpec quantity2Axis{NCovariateQuantities, -0.5, NCovariateQuantities - 0.5, "quantity"}; + registry.add("qa/hOccupancySums", "nominal single-side sums vs occupancy", HistType::kTH2D, {{axisOccupancy, "track occupancy"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hVtxZSums", "nominal single-side sums vs PV z", HistType::kTH2D, {{axisVtxZ, "PV z (cm)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRateSums", "nominal single-side sums vs interaction rate", HistType::kTH2D, {{axisRate, "IR (kHz)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); } if (doprocessMC) { @@ -337,6 +417,9 @@ struct EventShapeCoex { } bookSums(sumsGen, "gen", "all", nFwdGenAxis, 1); // nominal configuration only registry.add("gen/hEvents", "generated events", HistType::kTH1D, {{1, -0.5, 0.5}}); + if (doSplitSums) { + bookSplit(splitGen, "gen/split", nFwdGenAxis); + } } } @@ -345,6 +428,8 @@ struct EventShapeCoex { { Nominal nominal; std::array m{}; + std::array mPairFirst{}; + bool pairFirstValid = false; double xF = 0., xB = 0., vF = 0., vB = 0.; for (int c = 0; c < nConfigs; ++c) { const auto& cfg = Configs[c]; @@ -370,8 +455,18 @@ struct EventShapeCoex { sums.config[c]->Fill(nFwd, nCond, k, m[k]); } sums.occupancy->Fill(c, nFwd, nCond); + if (c == IPairFirst) { + mPairFirst = m; + pairFirstValid = true; + } else if (c == IPairSecond && pairFirstValid) { + for (int k = 0; k < NMono1; ++k) { + for (int l = 0; l < NMono1; ++l) { + sums.paired->Fill(nFwd, nCond, k * NMono1 + l, mPairFirst[k] * m[l]); + } + } + } if (c == INominal) { - nominal = {.valid = true, .nMid = nCond, .xF = xF, .xB = xB, .vF = vF, .vB = vB}; + nominal = {.valid = true, .nFwd = nFwd, .nMid = nCond, .xF = xF, .xB = xB, .vF = vF, .vB = vB}; } } @@ -392,16 +487,17 @@ struct EventShapeCoex { return nominal; } + /// Fills the reconstructed-level sums; returns the nominal observables template - void fillReco(TCollision const& coll, MyTracks const& tracks) + Nominal fillReco(TCollision const& coll, MyTracks const& tracks) { registry.fill(HIST("qa/hEventFlow"), kFlowAll); if (!coll.sel8()) { - return; + return {}; } registry.fill(HIST("qa/hEventFlow"), kFlowSel8); if (std::abs(coll.posZ()) > vtxZCut) { - return; + return {}; } registry.fill(HIST("qa/hEventFlow"), kFlowVtxZ); @@ -424,13 +520,13 @@ struct EventShapeCoex { registry.fill(HIST("qa/hTrackPhi"), track.phi()); } - const auto& bc = coll.template bc_as(); + const auto& bc = coll.template bc_as(); const double nFwd = coll.multFT0A() + coll.multFT0C(); registry.fill(HIST("qa/hNFwd"), nFwd); const int half = static_cast(blockHash(bc.runNumber(), bc.globalBC(), HalfBlockShift) & 1u); const Nominal nominal = fillSums(sumsReco[half], rings, nFwd, qaBits, NConfigs); if (!nominal.valid) { - return; + return nominal; } registry.fill(HIST("qa/hEventFlow"), kFlowNominalValid); registry.fill(HIST("qa/hEventFlow"), kFlowHalfA + half); @@ -440,7 +536,7 @@ struct EventShapeCoex { std::array m{}; monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono1, m); for (int k = 0; k < NMono1; ++k) { - designEffect[half]->Fill(nFwd, group, k, m[k]); + groupSums[half]->Fill(nFwd, nominal.nMid, group * NMono1 + k, m[k]); } const std::array quantities = {1., nominal.xF + MeanPtShift, nominal.xB + MeanPtShift, nominal.vF, nominal.vB}; @@ -448,21 +544,38 @@ struct EventShapeCoex { registry.fill(HIST("qa/hMarginalSums"), nFwd, nominal.nMid, q, quantities[q]); registry.fill(HIST("qa/hRunSums"), bc.runNumber(), q, quantities[q]); } + const double rate = irSource.value.empty() ? -1. : rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource.value) * 1.e-3; + const std::array covariateQuantities = {1., quantities[1], quantities[2], quantities[1] * quantities[1], quantities[2] * quantities[2], nominal.vF, nominal.vB}; + for (int q = 0; q < NCovariateQuantities; ++q) { + registry.fill(HIST("qa/hOccupancySums"), coll.trackOccupancyInTimeRange(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hVtxZSums"), coll.posZ(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hRateSums"), rate, q, covariateQuantities[q]); + } + return nominal; } - void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCs const&) + void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCsWithTimestamps const&) { fillReco(coll, tracks); } PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level moment sums", true); - void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::BCs const&) + void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::McCollisions const&, aod::BCsWithTimestamps const&) { - fillReco(coll, tracks); + const Nominal nominal = fillReco(coll, tracks); + if (!nominal.valid || !doSplitSums) { + return; + } + if (!coll.has_mcCollision()) { + registry.fill(HIST("qa/hEventFlow"), kFlowNoMcLabel); + return; + } + const auto& bc = coll.mcCollision().bc_as(); + fillSplit(splitReco, nominal, bc.runNumber(), bc.globalBC()); } PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level moment sums on MC (MC-labelled collisions)", false); - void processMC(aod::McCollision const&, aod::McParticles const& particles) + void processMC(aod::McCollision const& mcCollision, aod::McParticles const& particles, aod::BCs const&) { static constexpr double ChargeTolerance = 1.e-3; @@ -490,7 +603,11 @@ struct EventShapeCoex { } rings.add(eta, particle.pt()); } - fillSums(sumsGen, rings, nFwdA + nFwdC, AllQaBits, 1); + const Nominal nominal = fillSums(sumsGen, rings, nFwdA + nFwdC, AllQaBits, 1); + if (nominal.valid && doSplitSums) { + const auto& bc = mcCollision.bc_as(); + fillSplit(splitGen, nominal, bc.runNumber(), bc.globalBC()); + } } PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level moment sums (nominal configuration)", false); }; From 4bf88857bd697228208e33994f85a54a26f3dcfd Mon Sep 17 00:00:00 2001 From: mj525 <99000728+mjkim525@users.noreply.github.com> Date: Sun, 18 Oct 2026 22:28:04 +0800 Subject: [PATCH 336/648] [PWGLF] Add additional ITS and TPC track quality cut and MC injection selection on `xi1530Analysisqa.cxx` (#18300) --- PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx | 63 ++++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx index 5d7f09bee53..ff27e613f62 100644 --- a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx +++ b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -49,6 +50,7 @@ #include #include #include +#include #include using namespace o2; @@ -81,6 +83,12 @@ struct Xi1530Analysisqa { using ResoMicroTracks = aod::ResoMicroTracks_001; using ResoMCCols = soa::Join; + // This native generator status is a production-specific injection tag. + static constexpr auto IsInjectedParent = [](auto const& mcparent) { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(mcparent.statusCode()) == InjectedGeneratorStatus; + }; + // Own just the event rows: zero-copy slices would retain entire DF buffers. struct MixingCollision { float x, y, z, centrality, multiplicity; @@ -142,8 +150,8 @@ struct Xi1530Analysisqa { struct : ConfigurableGroup { // Primary track selections - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - Configurable cMaxetacut{"cMaxetacut", 0.8, "Track maximum eta cut"}; + Configurable cMinPtcut{"cMinPtcut", 0.00, "Track minium pt cut"}; + Configurable cMaxetacut{"cMaxetacut", 999., "Track maximum eta cut"}; Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; Configurable cfgPVContributor{"cfgPVContributor", true, "PV contributor track selection"}; @@ -152,6 +160,11 @@ struct Xi1530Analysisqa { Configurable cfgTPCRows{"cfgTPCRows", 80, "Minimum Number of TPC Crossed Rows "}; + // Micro001 quality cuts use strict lower bounds; -1 preserves the previous selection. + // Full ResoTracks has no ITS cluster map and retains cfgTPCcluster/cfgTPCRows. + Configurable cfgITSNCls{"cfgITSNCls", -1, "Require ITS NCls > cut; -1 disables the cut"}; + Configurable cfgITSNClsInnerBarrel{"cfgITSNClsInnerBarrel", -1, "Require inner-barrel ITS NCls > cut; -1 disables the cut"}; + Configurable cfgRapidityMinCut{"cfgRapidityMinCut", -0.5, "Rapidity cut for tracks"}; Configurable cfgRapidityMaxCut{"cfgRapidityMaxCut", 0.5, "Rapidity cut for tracks"}; @@ -315,6 +328,8 @@ struct Xi1530Analysisqa { Configurable cfgMaxRot{"cfgMaxRot", 7.0 * constants::math::PI / 6.0, "Maximum of rotation"}; Configurable cfgRotPion{"cfgRotPion", true, "Rotate pion"}; Configurable cfgNrotBkg{"cfgNrotBkg", 4, "Number of rotated copies (background) per each original candidate"}; + + Configurable cMCInjection{"cMCInjection", true, "Select generated MC parents with native status -11 (production-specific injection tag)"}; } additionalConfig; //*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*// @@ -589,7 +604,13 @@ struct Xi1530Analysisqa { if (std::abs(track.dcaXY()) >= dcaXYCut || std::abs(track.dcaZ()) >= dcaZCut) { return false; } - if constexpr (!IsResoMicrotrack) { + if constexpr (IsResoMicrotrack) { + if (track.itsNCls() <= primarytrackConfig.cfgITSNCls || + track.itsNClsInnerBarrel() <= primarytrackConfig.cfgITSNClsInnerBarrel || + track.tpcNClsCrossedRows() <= primarytrackConfig.cfgTPCRows) { + return false; + } + } else { if (track.tpcNClsFound() <= primarytrackConfig.cfgTPCcluster) { return false; } @@ -808,9 +829,25 @@ struct Xi1530Analysisqa { return properLifetime; } + // Build once per selected collision, not once per reconstructed pair. + std::unordered_set getInjectedMotherIds(aod::ResoMCParents_001 const& parents) + { + std::unordered_set motherIds; + if (!additionalConfig.cMCInjection) { + return motherIds; + } + for (auto const& parent : parents) { + if (std::abs(parent.pdgCode()) == kXiStar && IsInjectedParent(parent)) { + motherIds.insert(parent.originalMcParticleId()); + } + } + return motherIds; + } + template void fillHistograms(const CollisionType& collision, const centType& inCent, const TracksType& dTracks1, const TracksTypeCasc& dTracks2, - const MixingCollision* cascadeCollision = nullptr) // Order: ResoColl, ResoTrack, ResoCascTrack + const MixingCollision* cascadeCollision = nullptr, + const std::unordered_set& injectedMotherIds = {}) // Order: ResoColl, ResoTrack, ResoCascTrack { auto cent = inCent; @@ -1194,6 +1231,9 @@ struct Xi1530Analysisqa { if (xiCand.motherId() < 0 || pionCand.motherId() != xiCand.motherId()) { continue; } + if (additionalConfig.cMCInjection && !injectedMotherIds.contains(xiCand.motherId())) { + continue; + } auto lResonancePtMC = xiCand.motherPt(); if (additionalConfig.cUseTruthRapidity) { const bool passesTruthRapidity = xiCand.motherRap() > primarytrackConfig.cfgRapidityMinCut && xiCand.motherRap() < primarytrackConfig.cfgRapidityMaxCut; @@ -1352,7 +1392,8 @@ struct Xi1530Analysisqa { // Calculate numerator for the Acceptance x Efficiency void processMC(ResoMCCols::iterator const& resoCollision, soa::Join const& cascTracks, - soa::Join const& resoTracks) + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& resoParents) { auto inCent = resoCollision.cent(); const auto multiplicity = resoCollision.multiplicity(); @@ -1365,12 +1406,14 @@ struct Xi1530Analysisqa { if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); } - fillHistograms(resoCollision, inCent, resoTracks, cascTracks); + const auto injectedMotherIds = getInjectedMotherIds(resoParents); + fillHistograms(resoCollision, inCent, resoTracks, cascTracks, nullptr, injectedMotherIds); } void processMCMicro(ResoMCCols::iterator const& resoCollision, soa::Join const& cascTracks, - soa::Join const& resoTracks) + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& reParent) { if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || @@ -1380,7 +1423,8 @@ struct Xi1530Analysisqa { if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), resoCollision.cent(), resoCollision.multiplicity()); } - fillHistograms(resoCollision, resoCollision.cent(), resoTracks, cascTracks); + const auto injectedMotherIds = getInjectedMotherIds(reParent); + fillHistograms(resoCollision, resoCollision.cent(), resoTracks, cascTracks, nullptr, injectedMotherIds); } // Calculate denominator for the Acceptance x Efficiency, actually it is not Trueth info... @@ -1402,6 +1446,9 @@ struct Xi1530Analysisqa { if (std::abs(part.pdgCode()) != kXiStar) { continue; } + if (additionalConfig.cMCInjection && !IsInjectedParent(part)) { + continue; + } if ((part.y() <= primarytrackConfig.cfgRapidityMinCut) || (part.y() >= primarytrackConfig.cfgRapidityMaxCut)) { continue; } From ecbed1820b4ef51e20ae10678928bc0ec79ae412 Mon Sep 17 00:00:00 2001 From: Giorgio Alberto Lucia <87222843+GiorgioAlbertoLucia@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:28:30 +0200 Subject: [PATCH 337/648] [PWGLF] Add same and mixed event version with derived data (#18295) --- PWGLF/DataModel/LFDoubleOmegaTables.h | 110 ++ .../TableProducer/Strangeness/CMakeLists.txt | 5 + .../Strangeness/doubleOmegaBuilder.cxx | 321 +++++ .../Strangeness/doubleOmegaTreeCreator.cxx | 1069 +++++++++-------- PWGLF/Utils/doubleOmegaBuilderHelper.h | 628 ++++++++++ 5 files changed, 1662 insertions(+), 471 deletions(-) create mode 100644 PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx create mode 100644 PWGLF/Utils/doubleOmegaBuilderHelper.h diff --git a/PWGLF/DataModel/LFDoubleOmegaTables.h b/PWGLF/DataModel/LFDoubleOmegaTables.h index 16c03e444f7..83cee30b84a 100644 --- a/PWGLF/DataModel/LFDoubleOmegaTables.h +++ b/PWGLF/DataModel/LFDoubleOmegaTables.h @@ -62,6 +62,8 @@ DECLARE_SOA_COLUMN(GenDecayLength, genDecayLength, float); DECLARE_SOA_COLUMN(PdgDoubleOmega, pdgDoubleOmega, int); DECLARE_SOA_COLUMN(GenMotherId, genMotherId, int64_t); // MC particle index within the input dataframe. DECLARE_SOA_COLUMN(IsReco, isReco, bool); + +DECLARE_SOA_COLUMN(MixingType, mixingType, uint8_t); // 1: Omega, 2: Lambda, 3: kaon from another collision; 4: Omega, Lambda and kaon from three different collisions. } // namespace DoubleOmegaTables #define DOUBLE_OMEGA_RECO_COLUMNS \ @@ -107,8 +109,116 @@ DECLARE_SOA_TABLE(DoubleOmegaTableMC, "AOD", "DBLOMEGAMCTABLE", DoubleOmegaTables::GenMotherId, DoubleOmegaTables::IsReco); +DECLARE_SOA_TABLE(DoubleOmegaMixedTable, "AOD", "DBLOMEGAMIXTABLE", + DOUBLE_OMEGA_RECO_COLUMNS, + DoubleOmegaTables::MixingType); + #undef DOUBLE_OMEGA_RECO_COLUMNS +// Derived data: Omega, Lambda and kaon candidates of the collisions with at least one Omega, one +// Lambda and one kaon candidate not sharing tracks with it. The tables only index each other, so +// they are self-contained and dataframes can be merged. +namespace domega +{ +DECLARE_SOA_COLUMN(MultFT0M, multFT0M, float); //! FT0M multiplicity of the collision +DECLARE_SOA_COLUMN(Bz, bz, float); //! Nominal magnetic field (kG) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaColls, "AOD", "DOMEGACOLL", //! Collisions with double-Omega building blocks + o2::soa::Index<>, + collision::PosX, collision::PosY, collision::PosZ, + domega::MultFT0M, + bc::RunNumber, + domega::Bz); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(DOmegaColl, dOmegaColl, int, DOmegaColls, ""); //! Collision index + +// Track parametrisation covariance, in the order expected by getTrackParCov. +DECLARE_SOA_COLUMN(CYY, cYY, float); +DECLARE_SOA_COLUMN(CZY, cZY, float); +DECLARE_SOA_COLUMN(CZZ, cZZ, float); +DECLARE_SOA_COLUMN(CSnpY, cSnpY, float); +DECLARE_SOA_COLUMN(CSnpZ, cSnpZ, float); +DECLARE_SOA_COLUMN(CSnpSnp, cSnpSnp, float); +DECLARE_SOA_COLUMN(CTglY, cTglY, float); +DECLARE_SOA_COLUMN(CTglZ, cTglZ, float); +DECLARE_SOA_COLUMN(CTglSnp, cTglSnp, float); +DECLARE_SOA_COLUMN(CTglTgl, cTglTgl, float); +DECLARE_SOA_COLUMN(C1PtY, c1PtY, float); +DECLARE_SOA_COLUMN(C1PtZ, c1PtZ, float); +DECLARE_SOA_COLUMN(C1PtSnp, c1PtSnp, float); +DECLARE_SOA_COLUMN(C1PtTgl, c1PtTgl, float); +DECLARE_SOA_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float); + +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, float); //! -999 when TPC is unavailable +DECLARE_SOA_COLUMN(TOFNSigmaKa, tofNSigmaKa, float); //! -999 when TOF is unavailable +DECLARE_SOA_COLUMN(DetectorMap, detectorMap, uint8_t); //! ITS, TPC, TOF bits +DECLARE_SOA_COLUMN(DcaXYToPV, dcaXYToPV, float); //! Transverse DCA to the primary vertex of its collision +DECLARE_SOA_COLUMN(IsKaon, isKaon, bool); //! Selected as a direct-kaon candidate +} // namespace domega + +#define DOMEGA_TRACK_PAR_COV_COLUMNS \ + track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, \ + domega::CYY, domega::CZY, domega::CZZ, domega::CSnpY, domega::CSnpZ, domega::CSnpSnp, \ + domega::CTglY, domega::CTglZ, domega::CTglSnp, domega::CTglTgl, \ + domega::C1PtY, domega::C1PtZ, domega::C1PtSnp, domega::C1PtTgl, domega::C1Pt21Pt2 + +DECLARE_SOA_TABLE(DOmegaTracks, "AOD", "DOMEGATRACK", //! Direct-kaon candidates and Omega/Lambda daughters, at the innermost update + o2::soa::Index<>, + domega::DOmegaCollId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::TPCNSigmaKa, + domega::TOFNSigmaKa, + domega::DetectorMap, + domega::DcaXYToPV, + domega::IsKaon, + track::Sign); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, DOmegaTracks, "_Pos"); //! Positive daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, DOmegaTracks, "_Neg"); //! Negative daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, DOmegaTracks, "_Bach"); //! Bachelor kaon of the Omega + +DECLARE_SOA_COLUMN(DecayX, decayX, float); +DECLARE_SOA_COLUMN(DecayY, decayY, float); +DECLARE_SOA_COLUMN(DecayZ, decayZ, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); +DECLARE_SOA_COLUMN(MassXi, massXi, float); +DECLARE_SOA_COLUMN(MassLambda, massLambda, float); +DECLARE_SOA_COLUMN(DcaDaughters, dcaDaughters, float); //! DCA between the daughters +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Charge of the Omega (Omega table) or of the Omega the Lambda can be paired with (Lambda table) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaOmegas, "AOD", "DOMEGAOMEGA", //! Omega candidates; track parameters at the Omega decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, domega::BachelorId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassOmega, domega::MassXi, + domega::DcaDaughters, + domega::Sign); + +DECLARE_SOA_TABLE(DOmegaLambdas, "AOD", "DOMEGALAMBDA", //! Lambda candidates; neutral track parameters at the Lambda decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassLambda, + domega::DcaDaughters, + domega::Sign); + +#undef DOMEGA_TRACK_PAR_COV_COLUMNS + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFDOUBLEOMEGATABLES_H_ diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 6ba1048506f..e892688ebb4 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -70,6 +70,11 @@ o2physics_add_dpl_workflow(double-omega-tree-creator PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(double-omega-builder + SOURCES doubleOmegaBuilder.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambdakzerofinder SOURCES lambdakzerofinder.cxx REUSE_FROM LfStrangenessPCH diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx new file mode 100644 index 00000000000..459997de976 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx @@ -0,0 +1,321 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilder.cxx +/// \brief Builds the Omega, Lambda and direct-kaon candidates of the double-Omega analysis and stores +/// them in derived tables, consumed on the fly (or from saved derived data) by doubleOmegaTreeCreator +/// for the same-event candidates and the event mixing. + +#include "PWGLF/DataModel/LFDoubleOmegaTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::pwglf::doubleomega; + +using CollisionsTable = soa::Join; +using TracksFull = soa::Join; + +struct doubleOmegaBuilder { + Produces dOmegaColls; + Produces dOmegaTracks; + Produces dOmegaOmegas; + Produces dOmegaLambdas; + Service ccdb{}; + + enum CollisionStep : uint8_t { + kAllCollisions, + kSelectedCollisions, + kWithOmega, + kStoredCollisions, + kNCollisionSteps + }; + + Preslice tracksPerCollision = aod::track::collisionId; + Preslice cascadesPerCollision = aod::cascade::collisionId; + Preslice v0sPerCollision = aod::v0::collisionId; + + DoubleOmegaBuilderHelper helper; + int mRunNumber = 0; + float mBz = 0.f; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable cfgApplyEventSelection{"cfgApplyEventSelection", true, "Apply the standard collision event selection"}; + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrLUT), "Material correction for the V0/cascade fits"}; + + ConfigurableAxis zVtxAxis{"zVtxBins", {100, -20.f, 20.f}, "Binning for the vertex z in cm"}; + ConfigurableAxis massOmegaAxis{"massOmegaAxis", {400, o2::constants::physics::MassOmegaMinus - 0.05f, o2::constants::physics::MassOmegaMinus + 0.05f}, "binning for the Omega invariant-mass"}; + ConfigurableAxis massXiAxis{"massXiAxis", {400, o2::constants::physics::MassXiMinus - 0.05f, o2::constants::physics::MassXiMinus + 0.05f}, "binning for the Xi invariant-mass"}; + ConfigurableAxis massLambdaAxis{"massLambdaAxis", {400, o2::constants::physics::MassLambda0 - 0.05f, o2::constants::physics::MassLambda0 + 0.05f}, "binning for the Lambda invariant-mass"}; + ConfigurableAxis momAxis{"momAxisFine", {5.e2, 0.f, 5.f}, "momentum axis binning"}; + + // Production selections: loose, they can be tightened when the derived tables are read. + Configurable zVtxMax{"zVtxMax", 10.0f, "maximum z position of the primary vertex"}; + Configurable etaMax{"etaMax", 0.9f, "maximum eta"}; + Configurable minNCrossedRows{"minNCrossedRows", 100, "Minimum number of crossed TPC rows"}; + Configurable minNITSClus{"minNITSClus", 0., "Minimum number of ITS clusters"}; + Configurable minNTPCClus{"minNTPCClus", 80, "Minimum number of TPC clusters"}; + Configurable maxNSharedTPCClus{"maxNSharedTPCClus", 5, "Maximum number of shared TPC clusters"}; + Configurable nSigmaTPCCut{"nSigmaTPCCut", 3.f, "Number of sigmas for the TPC PID"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.05f, "Minimum DCA of a cascade bachelor to the primary vertex"}; + Configurable dcaV0DauToPV{"dcaV0DauToPV", 0.05f, "Minimum DCA of Lambda daughters to the primary vertex"}; + Configurable dcaV0Bach{"dcaV0Bach", 1.f, "Maximum DCA between the V0 and cascade bachelor"}; + Configurable dcaLambdaDaughters{"dcaLambdaDaughters", 1.f, "Maximum DCA between Lambda daughters"}; + Configurable mOmegaWindow{"mOmegaWindow", 0.01f, "Omega mass window"}; + Configurable mLambdaWindow{"mLambdaWindow", 0.01f, "Lambda mass window"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + + zorroSummary.setObject(zorro.getZorroSummary()); + + histos.add("QA/zVtx", ";#it{z}_{vtx} (cm);Entries", HistType::kTH1F, {zVtxAxis}); + histos.add("QA/massXi", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); + histos.add("QA/massOmega", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + K) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); + histos.add("QA/massLambda", ";#it{p}_{T} (GeV/#it{c});#it{M}(p + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massLambdaAxis}); + histos.add("QA/collisions", ";;Collisions", HistType::kTH1F, {{kNCollisionSteps, -0.5f, static_cast(kNCollisionSteps) - 0.5f}}); + auto collisions = histos.get(HIST("QA/collisions")); + collisions->GetXaxis()->SetBinLabel(kAllCollisions + 1, "All"); + collisions->GetXaxis()->SetBinLabel(kSelectedCollisions + 1, "Event selection"); + collisions->GetXaxis()->SetBinLabel(kWithOmega + 1, "With #Omega"); + collisions->GetXaxis()->SetBinLabel(kStoredCollisions + 1, "With #Omega, #Lambda and K (stored)"); + } + + template + void initCCDB(Bc const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + auto* grpmag = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for timestamp " << bc.timestamp(); + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + + mRunNumber = bc.runNumber(); + if (cfgSkimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDoubleOmega,fOmegaXi"); + zorro.populateHistRegistry(histos, bc.runNumber()); + } + } + + template + bool acceptCollision(C const& collision) + { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if (cfgApplyEventSelection && + (!collision.sel8() || + std::abs(collision.posZ()) > zVtxMax || + !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || + !collision.selection_bit(aod::evsel::kNoTimeFrameBorder))) { + return false; + } + if (cfgSkimmedProcessing) { + zorro.isSelected(bc.globalBC()); + } + histos.fill(HIST("QA/zVtx"), collision.posZ()); + return true; + } + + void fillCandidateQA(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + } + } + } + + // Writes the candidates of a collision. Tracks are written once per collision: the direct-kaon + // candidates first, then the Omega and Lambda daughters not written yet. + template + void writeCollision(C const& collision, CollisionCandidates const& candidates, T const& tracks) + { + dOmegaColls(collision.posX(), collision.posY(), collision.posZ(), collision.multFT0M(), mRunNumber, mBz); + const auto collisionIndex = static_cast(dOmegaColls.lastIndex()); + + std::unordered_map trackRows; // Track index -> DOmegaTracks row. + const auto writeTrack = [&](int64_t trackId, bool isKaon) { + const auto [row, inserted] = trackRows.try_emplace(trackId, -1); + if (!inserted) { + return row->second; + } + const auto track = tracks.rawIteratorAt(trackId); + float dcaXY = -999.f; + helper.dcaXYToPV(collision, track, dcaXY); + const auto trackParCov = getTrackParCov(track); + const auto& cov = trackParCov.getCov(); + dOmegaTracks(collisionIndex, + trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + static_cast(DoubleOmegaBuilderHelper::detectorMap(track)), + dcaXY, + isKaon); + row->second = static_cast(dOmegaTracks.lastIndex()); + return row->second; + }; + + for (const auto& kaons : candidates.kaons) { + for (const auto& kaon : kaons) { + writeTrack(kaon.trackId, true); + } + } + for (const auto& omega : candidates.omegas) { + const auto posTrackRow = writeTrack(omega.posTrackId, false); + const auto negTrackRow = writeTrack(omega.negTrackId, false); + const auto bachelorRow = writeTrack(omega.bachelorId, false); + const auto& parent = omega.parentTrack; + const auto& cov = parent.getCov(); + dOmegaOmegas(collisionIndex, posTrackRow, negTrackRow, bachelorRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + omega.x, omega.y, omega.z, + omega.px, omega.py, omega.pz, + omega.massOmega, omega.massXi, + omega.dcaDaughters, + omega.sign); + } + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& lambda : candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(charge)]) { + const auto posTrackRow = writeTrack(lambda.posTrackId, false); + const auto negTrackRow = writeTrack(lambda.negTrackId, false); + const auto& parent = lambda.parentTrack; + const auto& cov = parent.getCov(); + dOmegaLambdas(collisionIndex, posTrackRow, negTrackRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + lambda.decayVertex[0], lambda.decayVertex[1], lambda.decayVertex[2], + lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz, + lambda.candidate.mass, + lambda.dcaDaughters, + charge); + } + } + } + + void processData(CollisionsTable const& collisions, + TracksFull const& tracks, + aod::V0s const& v0s, + aod::Cascades const& cascades, + aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + histos.fill(HIST("QA/collisions"), kAllCollisions); + if (!acceptCollision(collision)) { + continue; + } + histos.fill(HIST("QA/collisions"), kSelectedCollisions); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + auto cascadesThisCollision = cascades.sliceBy(cascadesPerCollision, collision.globalIndex()); + auto v0sThisCollision = v0s.sliceBy(v0sPerCollision, collision.globalIndex()); + v0sThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&v0s); + const auto candidates = helper.buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + fillCandidateQA(candidates); + if (candidates.omegas.empty()) { + continue; + } + histos.fill(HIST("QA/collisions"), kWithOmega); + if (!DoubleOmegaBuilderHelper::hasOmegaLambdaKaon(candidates)) { + continue; + } + histos.fill(HIST("QA/collisions"), kStoredCollisions); + writeCollision(collision, candidates, tracks); + } + } + PROCESS_SWITCH(doubleOmegaBuilder, processData, "Build the Omega, Lambda and kaon candidates and store them in derived tables", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx index 6ce665dee2d..8622f2e626a 100644 --- a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -11,6 +11,7 @@ #include "PWGLF/DataModel/LFDoubleOmegaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -23,14 +24,16 @@ #include "Common/DataModel/PIDResponseTPC.h" #include +#include #include -#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -43,20 +46,29 @@ #include #include +#include #include #include +#include #include #include #include +#include +#include #include +#include #include #include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::pwglf::doubleomega; using CollisionsTable = soa::Join; using Collisions = CollisionsTable::iterator; @@ -67,38 +79,6 @@ using FullCascades = aod::CascDataExt; using TracksFull = soa::Join; using TracksFullMC = soa::Join; -struct DoubleOmegaCandidate { - float pt = -999.f; - float eta = -999.f; - float phi = -999.f; - float x = -999.f; - float y = -999.f; - float z = -999.f; - float pvX = -999.f; - float pvY = -999.f; - float pvZ = -999.f; - float cosPAOmega = -999.f; - float cosPADirectLambda = -999.f; - float cosPADoubleOmega = -999.f; - float cosPAOmegaToPV = -999.f; - float cosPADirectLambdaToPV = -999.f; - float dcaXYOmegaToPV = -999.f; - float dcaXYDirectLambdaToPV = -999.f; - float dcaXYDirectKaonToPV = -999.f; - float dcaXYOmegaKaonToPV = -999.f; - float dcaXYOmegaProtonToPV = -999.f; - float dcaXYOmegaPionToPV = -999.f; - float dcaXYDirectProtonToPV = -999.f; - float dcaXYDirectPionToPV = -999.f; - float tpcNSigmaDirectKaon = -999.f; - float tofNSigmaDirectKaon = -999.f; - uint32_t daughterDetectorMap = 0; - float mass = -999.f; - float massOmega = -999.f; - float massXi = -999.f; - int8_t sign = 0; -}; - struct DoubleOmegaMCInfo { int64_t motherId = -1; float pt = -999.f; @@ -114,47 +94,22 @@ struct DoubleOmegaMCInfo { int8_t sign = 0; }; -struct LambdaCandidate { - float px = 0.f; - float py = 0.f; - float pz = 0.f; - float mass = -999.f; -}; - -struct BuiltLambdaCandidate { - LambdaCandidate candidate; - o2::track::TrackParCov parentTrack; - std::array decayVertex{}; - int64_t posTrackId = -1; - int64_t negTrackId = -1; - uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. - float dcaXYProtonToPV = -999.f; - float dcaXYPionToPV = -999.f; -}; - -struct BuiltOmegaCandidate { - float px = 0.f; - float py = 0.f; - float pz = 0.f; - float x = 0.f; - float y = 0.f; - float z = 0.f; - float massOmega = -999.f; - float massXi = -999.f; - int8_t sign = 0; - int64_t posTrackId = -1; - int64_t negTrackId = -1; - int64_t bachelorId = -1; - o2::track::TrackParCov parentTrack; - uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. - float dcaXYKaonToPV = -999.f; - float dcaXYProtonToPV = -999.f; - float dcaXYPionToPV = -999.f; +// Same-event double-Omega candidate used as a seed of the candidate-level event mixing. Seeds are +// kept in a pool across dataframes, so they store copies of their daughters, with the collision and +// track indices made unique across dataframes (see uniqueIndex). +struct MixingSeed { + int64_t collisionId = -1; + std::array primaryVertex{}; + float bz = 0.f; + BuiltOmegaCandidate omega; + BuiltLambdaCandidate lambda; + BuiltKaonCandidate kaon; }; struct doubleOmegaTreeCreator { Produces doubleOmegaTable; Produces doubleOmegaTableMC; + Produces doubleOmegaMixedTable; Service ccdb{}; static constexpr int kDoubleOmegaPdg = 1060020020; @@ -169,16 +124,26 @@ struct doubleOmegaTreeCreator { kFindableCascadeAndV0SelectedTracks, kNFindabilitySteps }; + enum MixingType : uint8_t { + kSameEvent = 0, + kMixedOmega, // Lambda and kaon from the same collision, Omega from another one + kMixedLambda, // Omega and kaon from the same collision, Lambda from another one + kMixedKaon, // Omega and Lambda from the same collision, kaon from another one + kMixedAll, // Omega, Lambda and kaon from three different collisions + kNMixingTypes + }; std::vector reconstructedDoubleOmegaIds; Preslice tracksPerCollision = aod::track::collisionId; Preslice rawCascadesPerCollision = aod::cascade::collisionId; Preslice rawV0sPerCollision = aod::v0::collisionId; + Preslice derivedTracksPerCollision = aod::domega::dOmegaCollId; + Preslice derivedOmegasPerCollision = aod::domega::dOmegaCollId; + Preslice derivedLambdasPerCollision = aod::domega::dOmegaCollId; int mRunNumber = 0; float mBz = 0.f; - o2::vertexing::DCAFitterN<2> fitter; - o2::vertexing::DCAFitterN<3> fitter3Body; + DoubleOmegaBuilderHelper helper; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; @@ -221,32 +186,25 @@ struct doubleOmegaTreeCreator { Configurable minCosPADoubleOmega{"minCosPADoubleOmega", -1.f, "Minimum double-Omega cosPA relative to the primary vertex"}; Configurable minDoubleOmegaDecayRadius{"minDoubleOmegaDecayRadius", 1.f, "Minimum double-Omega transverse decay radius in cm"}; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - template - static uint32_t detectorMap(Track const& track) - { - return static_cast(track.hasITS()) | - (static_cast(track.hasTPC()) << 1) | - (static_cast(track.hasTOF()) << 2); - } + struct : ConfigurableGroup { + std::string prefix = "mixingOpts"; + Configurable nMixingCollisions{"nMixingCollisions", 5, "Number of neighbouring collisions in the same bin each collision is mixed with"}; + Configurable mixingTypes{"mixingTypes", 0b1111, "Bitmask of the mixed backgrounds: 1 Omega, 2 Lambda, 4 kaon from another collision, 8 all from different collisions"}; + Configurable maxDeltaPVz{"maxDeltaPVz", 1.f, "Maximum difference of the primary-vertex z among the mixed collisions (cm)"}; + ConfigurableAxis binsVtxZ{"binsVtxZ", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - primary-vertex z (cm)"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.f, 20.f, 50.f, 100.f, 200.f, 500.f, 1.e5f}, "Mixing bins - FT0M multiplicity"}; + // Candidate-level mixing (processDerivedMixingCandidates) + Configurable nMixingCandidates{"nMixingCandidates", 10, "Number of same-event candidates from other collisions each candidate is mixed with, per (R, phi, charge) bin"}; + Configurable poolDepth{"poolDepth", 50, "Number of same-event candidates kept in the mixing pool per (R, phi, charge) bin, across dataframes"}; + Configurable monitoringInterval{"monitoringInterval", 50, "Log the mixing pool size, memory and timing every this many dataframes (0: never)"}; + ConfigurableAxis binsRadius{"binsRadius", {VARIABLE_WIDTH, 0.f, 0.25f, 0.5f, 1.f, 2.f, 4.f, 10.f, 40.f}, "Mixing bins - double-Omega decay radius (cm)"}; + ConfigurableAxis binsPhi{"binsPhi", {VARIABLE_WIDTH, -o2::constants::math::PI, -2.f * o2::constants::math::PI / 3.f, -o2::constants::math::PI / 3.f, 0.f, o2::constants::math::PI / 3.f, 2.f * o2::constants::math::PI / 3.f, o2::constants::math::PI}, "Mixing bins - double-Omega momentum azimuth (rad)"}; + } mixingOpts; + + using DerivedMixingBinning = ColumnBinningPolicy; + static constexpr float kBzTolerance = 1.e-3f; // kG - template - bool selectTrack(T const& track) - { - if (std::abs(track.eta()) > etaMax) { - return false; - } - if (track.itsNCls() < minNITSClus || - track.tpcNClsFound() < minNTPCClus || - track.tpcNClsCrossedRows() < minNCrossedRows || - track.tpcNClsCrossedRows() < 0.8f * track.tpcNClsFindable() || - track.tpcNClsShared() > maxNSharedTPCClus) { - LOG(debug) << "Track failed selection: itsNCls=" << static_cast(track.itsNCls()) << ", tpcNClsFound=" << track.tpcNClsFound() << ", tpcNClsCrossedRows=" << static_cast(track.tpcNClsCrossedRows()) << ", tpcNClsFindable=" << static_cast(track.tpcNClsFindable()) << ", tpcNClsShared=" << static_cast(track.tpcNClsShared()); - return false; - } - return true; - } + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; template void initCCDB(Bc const& bc) @@ -260,8 +218,7 @@ struct doubleOmegaTreeCreator { } o2::base::Propagator::initFieldFromGRP(grpmag); mBz = o2::base::Propagator::Instance()->getNominalBz(); - fitter.setBz(mBz); - fitter3Body.setBz(mBz); + helper.setBz(mBz); if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); @@ -270,8 +227,7 @@ struct doubleOmegaTreeCreator { } o2::base::Propagator::Instance()->setMatLUT(lut); } - fitter.setMatCorrType(static_cast(cfgMaterialCorrection.value)); - fitter3Body.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); mRunNumber = bc.runNumber(); if (cfgSkimmedProcessing) { @@ -332,10 +288,23 @@ struct doubleOmegaTreeCreator { printValue(minCosPADirectLambda); printValue(minCosPADoubleOmega); printValue(minDoubleOmegaDecayRadius); + printValue(mixingOpts.nMixingCollisions); + printValue(mixingOpts.mixingTypes); + printValue(mixingOpts.maxDeltaPVz); + printAxis(mixingOpts.binsVtxZ); + printAxis(mixingOpts.binsMultiplicity); + printValue(mixingOpts.nMixingCandidates); + printValue(mixingOpts.poolDepth); + printValue(mixingOpts.monitoringInterval); + printAxis(mixingOpts.binsRadius); + printAxis(mixingOpts.binsPhi); printValue(doprocessData); printValue(doprocessDataFromCascades); printValue(doprocessMC); printValue(doprocessFindableTracks); + printValue(doprocessDerivedData); + printValue(doprocessDerivedMixing); + printValue(doprocessDerivedMixingCandidates); } void init(InitContext const&) @@ -347,19 +316,29 @@ struct doubleOmegaTreeCreator { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - fitter.setPropagateToPCA(true); - fitter.setMaxR(200.); - fitter.setMaxDZIni(4.); - fitter.setMinParamChange(1.e-3); - fitter.setMinRelChi2Change(0.9); - fitter.setUseAbsDCA(true); - - fitter3Body.setPropagateToPCA(true); - fitter3Body.setMaxR(200.); - fitter3Body.setMaxDZIni(4.); - fitter3Body.setMinParamChange(1.e-3); - fitter3Body.setMinRelChi2Change(0.9); - fitter3Body.setUseAbsDCA(true); + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaKaonToPV = dcaKaonToPV; + selections.dcaOmegaToPV = dcaOmegaToPV; + selections.dcaDirectLambdaToPV = dcaDirectLambdaToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + selections.maxDoubleOmegaMass = maxDoubleOmegaMass; + selections.minCosPAOmega = minCosPAOmega; + selections.minCosPADirectLambda = minCosPADirectLambda; + selections.minCosPADoubleOmega = minCosPADoubleOmega; + selections.minDoubleOmegaDecayRadius = minDoubleOmegaDecayRadius; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); zorroSummary.setObject(zorro.getZorroSummary()); @@ -377,302 +356,29 @@ struct doubleOmegaTreeCreator { generatedAndFindable->GetXaxis()->SetBinLabel(kFindableSelectedTracks + 1, "Findable, selected tracks"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0 + 1, "Findable cascade + V0"); generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0SelectedTracks + 1, "Findable cascade + V0, selected tracks"); - } - - template - bool isSelectedKaon(Track const& track, int8_t charge) - { - return track.sign() == charge && - selectTrack(track) && - std::abs(track.tpcNSigmaKa()) <= nSigmaTPCCut; - } - - static float invariantMass2Body(std::array const& momentum1, float mass1, - std::array const& momentum2, float mass2) - { - const float momentum1Squared = momentum1[0] * momentum1[0] + momentum1[1] * momentum1[1] + momentum1[2] * momentum1[2]; - const float momentum2Squared = momentum2[0] * momentum2[0] + momentum2[1] * momentum2[1] + momentum2[2] * momentum2[2]; - const float energy = std::sqrt(momentum1Squared + mass1 * mass1) + std::sqrt(momentum2Squared + mass2 * mass2); - const std::array totalMomentum{ - momentum1[0] + momentum2[0], momentum1[1] + momentum2[1], momentum1[2] + momentum2[2]}; - const float massSquared = energy * energy - totalMomentum[0] * totalMomentum[0] - totalMomentum[1] * totalMomentum[1] - totalMomentum[2] * totalMomentum[2]; - return std::sqrt(std::max(0.f, massSquared)); - } - - // Transverse DCA of a track to the collision primary vertex, computed by propagating - // a copy of the track parameters. Returns false if the propagation fails. - template - bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const - { - auto trackParCov = getTrackParCov(track); - const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; - const auto matCorr = static_cast(cfgMaterialCorrection.value); - std::array dca{}; - if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { - return false; - } - dcaXY = dca[0]; - return true; - } - - template - bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) - { - if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || - !selectTrack(posTrack) || !selectTrack(negTrack)) { - return false; - } - - const bool isMatter = charge < 0; - const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); - const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); - if (std::abs(protonNSigma) > nSigmaTPCCut || std::abs(pionNSigma) > nSigmaTPCCut) { - return false; - } - float dcaXYPos = -999.f; - float dcaXYNeg = -999.f; - if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { - LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); - return false; - } - if (std::abs(dcaXYPos) < dcaV0DauToPV || std::abs(dcaXYNeg) < dcaV0DauToPV) { - LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; - return false; - } - - auto posTrackParCov = getTrackParCov(posTrack); - auto negTrackParCov = getTrackParCov(negTrack); - int nCandidates = 0; - try { - nCandidates = fitter.process(posTrackParCov, negTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting Lambda daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); - return false; - } - if (nCandidates == 0) { - return false; - } - - std::array posMomentum{}; - std::array negMomentum{}; - fitter.getTrack(0).getPxPyPzGlo(posMomentum); - fitter.getTrack(1).getPxPyPzGlo(negMomentum); - const std::array lambdaMomentum{ - posMomentum[0] + negMomentum[0], - posMomentum[1] + negMomentum[1], - posMomentum[2] + negMomentum[2]}; - const auto& fittedVertex = fitter.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const float mass = isMatter ? invariantMass2Body(posMomentum, o2::constants::physics::MassProton, - negMomentum, o2::constants::physics::MassPionCharged) - : invariantMass2Body(posMomentum, o2::constants::physics::MassPionCharged, - negMomentum, o2::constants::physics::MassProton); - const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); - const float eta = etaFromMomentum(lambdaMomentum[0], lambdaMomentum[1], lambdaMomentum[2]); - if (std::abs(eta) > etaMax || - dcaDaughters > dcaLambdaDaughters || - std::abs(mass - o2::constants::physics::MassLambda0) > mLambdaWindow) { - return false; - } - - builtLambda.candidate = { - .px = lambdaMomentum[0], - .py = lambdaMomentum[1], - .pz = lambdaMomentum[2], - .mass = mass}; - builtLambda.parentTrack = fitter.createParentTrackParCov(0); - builtLambda.decayVertex = decayVertex; - builtLambda.posTrackId = posTrack.globalIndex(); - builtLambda.negTrackId = negTrack.globalIndex(); - // Keep particle-role ordering for matter and antimatter alike. - builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) - : detectorMap(negTrack) | (detectorMap(posTrack) << 3); - builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; - builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; - return true; - } - - template - bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) - { - if (bachelor.sign() == 0 || - !selectTrack(bachelor) || - std::abs(bachelor.tpcNSigmaKa()) > nSigmaTPCCut) { - return false; - } - float dcaXYBachelor = -999.f; - if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < dcaBachToPV) { - return false; - } - - BuiltLambdaCandidate lambda; - if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { - return false; - } - auto v0TrackParCov = lambda.parentTrack; - auto bachelorTrackParCov = getTrackParCov(bachelor); - int nCandidates = 0; - try { - nCandidates = fitter.process(v0TrackParCov, bachelorTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting Omega daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex() << ", " << bachelor.globalIndex(); - return false; - } - if (nCandidates == 0) { - return false; + if (doprocessDerivedMixingCandidates) { + histos.add("Mixing/hSeedRadiusPhi", ";Double-#Omega decay radius (cm);Double-#Omega #varphi (rad);Same-event candidates", HistType::kTH2F, {mixingOpts.binsRadius, mixingOpts.binsPhi}); + // Resource monitoring of the candidate-level mixing, one entry per dataframe. + histos.add("Mixing/hProcessingTime", ";Processing time per dataframe (ms);Dataframes", HistType::kTH1F, {{500, 0.f, 5000.f}}); + histos.add("Mixing/hPoolSeeds", ";Seeds in the mixing pool;Dataframes", HistType::kTH1F, {{500, 0.f, 50000.f}}); + histos.add("Mixing/hPoolMemory", ";Estimated mixing-pool memory (MB);Dataframes", HistType::kTH1F, {{500, 0.f, 50.f}}); + histos.add("Mixing/hProcessingTimeVsPoolSeeds", ";Seeds in the mixing pool;Processing time per dataframe (ms)", HistType::kTH2F, {{100, 0.f, 50000.f}, {100, 0.f, 5000.f}}); } - - std::array lambdaMomentum{}; - std::array bachelorMomentum{}; - fitter.getTrack(0).getPxPyPzGlo(lambdaMomentum); - fitter.getTrack(1).getPxPyPzGlo(bachelorMomentum); - const std::array omegaMomentum{ - lambdaMomentum[0] + bachelorMomentum[0], - lambdaMomentum[1] + bachelorMomentum[1], - lambdaMomentum[2] + bachelorMomentum[2]}; - const auto& fittedVertex = fitter.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const float massOmega = invariantMass2Body(lambdaMomentum, o2::constants::physics::MassLambda0, - bachelorMomentum, o2::constants::physics::MassKaonCharged); - const float massXi = invariantMass2Body(lambdaMomentum, o2::constants::physics::MassLambda0, - bachelorMomentum, o2::constants::physics::MassPionCharged); - const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); - const float eta = etaFromMomentum(omegaMomentum[0], omegaMomentum[1], omegaMomentum[2]); - if (dcaDaughters > dcaV0Bach || - std::abs(eta) > etaMax || - std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > mOmegaWindow) { - return false; + if (doprocessDerivedMixing || doprocessDerivedMixingCandidates) { + histos.add("Mixing/hMixedCollisions", ";;Entries", HistType::kTH1F, {{4, -0.5f, 3.5f}}); + auto mixedCollisions = histos.get(HIST("Mixing/hMixedCollisions")); + mixedCollisions->GetXaxis()->SetBinLabel(1, "Pairs"); + mixedCollisions->GetXaxis()->SetBinLabel(2, "Pairs, #Delta#it{z}_{PV}, same #it{B}"); + mixedCollisions->GetXaxis()->SetBinLabel(3, "Triples"); + mixedCollisions->GetXaxis()->SetBinLabel(4, "Triples, #Delta#it{z}_{PV}, same #it{B}"); + histos.add("Mixing/hMixedCandidates", ";;Entries", HistType::kTH1F, {{kNMixingTypes - 1, 0.5f, static_cast(kNMixingTypes) - 0.5f}}); + auto mixedCandidates = histos.get(HIST("Mixing/hMixedCandidates")); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedOmega, "#Omega mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedLambda, "#Lambda mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedKaon, "K mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedAll, "All mixed"); } - - builtOmega = { - omegaMomentum[0], - omegaMomentum[1], - omegaMomentum[2], - decayVertex[0], - decayVertex[1], - decayVertex[2], - massOmega, - massXi, - static_cast(bachelor.sign()), - posTrack.globalIndex(), - negTrack.globalIndex(), - bachelor.globalIndex(), - fitter.createParentTrackParCov(), - detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), - dcaXYBachelor, - lambda.dcaXYProtonToPV, - lambda.dcaXYPionToPV}; - return true; - } - - static float etaFromMomentum(float px, float py, float pz) - { - const float pt = std::hypot(px, py); - return pt > 0.f ? std::asinh(pz / pt) : 0.f; - } - - template - bool buildDoubleOmega(C const& collision, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& directLambda, Kaon const& kaon, DoubleOmegaCandidate& cand) - { - auto omegaTrackParCov = omega.parentTrack; - auto lambdaTrackParCov = directLambda.parentTrack; - auto kaonTrackParCov = getTrackParCov(kaon); - int nCandidates = 0; - try { - nCandidates = fitter3Body.process(omegaTrackParCov, lambdaTrackParCov, kaonTrackParCov); - } catch (...) { - LOG(error) << "Exception while fitting the double-Omega candidate"; - return false; - } - if (nCandidates == 0) { - return false; - } - - auto omegaTrackAtVertex = fitter3Body.getTrack(0); - auto lambdaTrackAtVertex = fitter3Body.getTrack(1); - auto kaonTrackAtVertex = fitter3Body.getTrack(2); - std::array omegaMomentum{}; - std::array lambdaMomentum{}; - std::array kaonMomentum{}; - omegaTrackAtVertex.getPxPyPzGlo(omegaMomentum); - lambdaTrackAtVertex.getPxPyPzGlo(lambdaMomentum); - kaonTrackAtVertex.getPxPyPzGlo(kaonMomentum); - - const std::array totalMomentum{ - omegaMomentum[0] + lambdaMomentum[0] + kaonMomentum[0], - omegaMomentum[1] + lambdaMomentum[1] + kaonMomentum[1], - omegaMomentum[2] + lambdaMomentum[2] + kaonMomentum[2]}; - const auto& fittedVertex = fitter3Body.getPCACandidate(); - const std::array decayVertex{ - static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; - const std::array primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; - const o2::math_utils::Point3D primaryVertexPoint{collision.posX(), collision.posY(), collision.posZ()}; - const std::array omegaDecayVertex{omega.x, omega.y, omega.z}; - - std::array dcaOmega{}; - std::array dcaDirectLambda{}; - std::array dcaDirectKaon{}; - const auto matCorr = static_cast(cfgMaterialCorrection.value); - if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, omegaTrackAtVertex, 2.f, matCorr, &dcaOmega) || - !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, lambdaTrackAtVertex, 2.f, matCorr, &dcaDirectLambda) || - !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, kaonTrackAtVertex, 2.f, matCorr, &dcaDirectKaon)) { - return false; - } - - const auto momentumSquared = [](std::array const& momentum) { - return momentum[0] * momentum[0] + momentum[1] * momentum[1] + momentum[2] * momentum[2]; - }; - const float energy = std::sqrt(momentumSquared(omegaMomentum) + o2::constants::physics::MassOmegaMinus * o2::constants::physics::MassOmegaMinus) + - std::sqrt(momentumSquared(lambdaMomentum) + o2::constants::physics::MassLambda0 * o2::constants::physics::MassLambda0) + - std::sqrt(momentumSquared(kaonMomentum) + o2::constants::physics::MassKaonCharged * o2::constants::physics::MassKaonCharged); - const float massSquared = energy * energy - totalMomentum[0] * totalMomentum[0] - totalMomentum[1] * totalMomentum[1] - totalMomentum[2] * totalMomentum[2]; - - cand.pt = std::hypot(totalMomentum[0], totalMomentum[1]); - cand.eta = etaFromMomentum(totalMomentum[0], totalMomentum[1], totalMomentum[2]); - cand.phi = std::atan2(totalMomentum[1], totalMomentum[0]); - cand.x = decayVertex[0]; - cand.y = decayVertex[1]; - cand.z = decayVertex[2]; - cand.pvX = primaryVertex[0]; - cand.pvY = primaryVertex[1]; - cand.pvZ = primaryVertex[2]; - cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); - cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); - cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); - // PV pointing angles use the daughter momenta at their own decay vertices. - cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); - cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); - cand.dcaXYOmegaToPV = dcaOmega[0]; - cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; - cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; - cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; - cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; - cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; - cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; - cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; - cand.tpcNSigmaDirectKaon = kaon.hasTPC() ? kaon.tpcNSigmaKa() : -999.f; - cand.tofNSigmaDirectKaon = kaon.hasTOF() ? kaon.tofNSigmaKa() : -999.f; - cand.daughterDetectorMap = omega.daughterDetectorMap | - (directLambda.daughterDetectorMap << 9) | - (detectorMap(kaon) << 15); - cand.mass = std::sqrt(std::max(0.f, massSquared)); - if (cand.cosPAOmega < minCosPAOmega || - cand.cosPADirectLambda < minCosPADirectLambda || - cand.cosPADoubleOmega < minCosPADoubleOmega || - std::hypot(decayVertex[0], decayVertex[1]) < minDoubleOmegaDecayRadius || - std::abs(cand.dcaXYOmegaToPV) < dcaOmegaToPV || - std::abs(cand.dcaXYDirectLambdaToPV) < dcaDirectLambdaToPV || - std::abs(cand.dcaXYDirectKaonToPV) < dcaKaonToPV || - cand.mass > maxDoubleOmegaMass) { - return false; - } - cand.massOmega = omega.massOmega; - cand.massXi = omega.massXi; - cand.sign = omega.sign; - return true; } template @@ -882,6 +588,39 @@ struct doubleOmegaTreeCreator { cand.massXi); } + void writeMixedCandidate(DoubleOmegaCandidate const& cand, MixingType mixingType) + { + doubleOmegaMixedTable(cand.pt * cand.sign, + cand.eta, + cand.phi, + cand.x, + cand.y, + cand.z, + cand.pvX, + cand.pvY, + cand.pvZ, + cand.cosPAOmega, + cand.cosPADirectLambda, + cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, + cand.dcaXYOmegaToPV, + cand.dcaXYDirectLambdaToPV, + cand.dcaXYDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, + cand.tpcNSigmaDirectKaon, + cand.tofNSigmaDirectKaon, + cand.daughterDetectorMap, + cand.mass, + cand.massOmega, + cand.massXi, + mixingType); + } + void writeMCCandidate(DoubleOmegaCandidate const& cand, DoubleOmegaMCInfo const& mcInfo, bool isReco) { doubleOmegaTableMC(cand.pt * cand.sign, @@ -942,74 +681,34 @@ struct doubleOmegaTreeCreator { LOG(debug) << "collision " << collision.globalIndex() << " (direct kaon), numContrib = " << collision.numContrib(); } - template - void fillFromRawTables(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, - aod::McParticles const* mcParticles = nullptr) + void fillCandidateQA(CollisionCandidates const& candidates) { - for (const auto& rawCascade : cascades) { - const auto omegaV0 = rawCascade.template v0_as(); - const auto omegaPosTrack = omegaV0.template posTrack_as(); - const auto omegaNegTrack = omegaV0.template negTrack_as(); - const auto omegaBachelorTrack = rawCascade.template bachelor_as(); - BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, omegaBachelorTrack, omega)) { - continue; - } + for (const auto& omega : candidates.omegas) { const float omegaPt = std::hypot(omega.px, omega.py); histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); - const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; - - for (const auto& rawV0 : v0s) { - const auto lambdaPosTrack = rawV0.template posTrack_as(); - const auto lambdaNegTrack = rawV0.template negTrack_as(); - BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, omega.sign, lambda)) { - continue; - } - if (std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || - std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.negTrackId) != omegaTrackIds.end()) { - continue; - } + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); - - for (const auto& kaon : tracks) { - if (!isSelectedKaon(kaon, omega.sign) || - kaon.globalIndex() == lambda.posTrackId || - kaon.globalIndex() == lambda.negTrackId || - std::find(omegaTrackIds.begin(), omegaTrackIds.end(), kaon.globalIndex()) != omegaTrackIds.end()) { - continue; - } - DoubleOmegaCandidate cand; - if (!buildDoubleOmega(collision, omega, lambda, kaon, cand)) { - continue; - } - if constexpr (isMC) { - if (mcParticles == nullptr) { - continue; - } - DoubleOmegaMCInfo mcInfo; - if (getMCInfo(mcInfo, - omegaPosTrack, omegaNegTrack, omegaBachelorTrack, - lambdaPosTrack, lambdaNegTrack, kaon, - omega.sign, *mcParticles)) { - reconstructedDoubleOmegaIds.push_back(mcInfo.motherId); - writeMCCandidate(cand, mcInfo, true); - } - } else { - writeDataCandidate(cand); - } - } } } } + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + auto candidates = helper.buildCollisionCandidates(collision, tracks, cascades, v0s, requireOmega); + fillCandidateQA(candidates); + return candidates; + } + template void fillFromCascades(C const& collision, T const&, Cascades const& cascades) { for (const auto& omegaRow : cascades) { BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { + if (!helper.buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { continue; } const float omegaPt = std::hypot(omega.px, omega.py); @@ -1037,16 +736,16 @@ struct doubleOmegaTreeCreator { } auto kaon = lambdaKaonSource.template bachelor_as(); - if (!isSelectedKaon(kaon, omega.sign)) { + if (!helper.isSelectedKaon(kaon, omega.sign)) { continue; } BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { + if (!helper.buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { continue; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); DoubleOmegaCandidate cand; - if (buildDoubleOmega(collision, omega, lambda, kaon, cand)) { + if (helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { writeDataCandidate(cand); } } @@ -1078,7 +777,7 @@ struct doubleOmegaTreeCreator { const auto lambdaPosTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 3 : 4]); const auto lambdaNegTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 4 : 3]); const auto kaon = tracks.rawIteratorAt(daughterTrackIds[5]); - if (!isSelectedKaon(kaon, charge)) { + if (!helper.isSelectedKaon(kaon, charge)) { return false; } const int64_t collisionId = selectFindableCollision(kaon); @@ -1090,19 +789,19 @@ struct doubleOmegaTreeCreator { initCCDB(bc); BuiltOmegaCandidate omega; - if (!buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { + if (!helper.buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { return false; } const float omegaPt = std::hypot(omega.px, omega.py); histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); BuiltLambdaCandidate lambda; - if (!buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { + if (!helper.buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { return false; } histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); DoubleOmegaCandidate cand; - if (!buildDoubleOmega(collision, omega, lambda, kaon, cand)) { + if (!helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { return false; } logVertexComparison(collision, cand, omega, mcInfo); @@ -1130,15 +829,54 @@ struct doubleOmegaTreeCreator { return true; } + void mix(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, MixingType mixingType) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + helper.combine(reference, omegaSource, lambdaSource, kaonSource, [&](DoubleOmegaCandidate const& cand, auto const&...) { + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + }); + } + + // Two candidates from one collision and one from the other, in both directions. The collision + // providing two candidates is the reference one. + void mixPair(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1) + { + mix(candidates0, candidates1, candidates0, candidates0, kMixedOmega); + mix(candidates1, candidates0, candidates1, candidates1, kMixedOmega); + mix(candidates0, candidates0, candidates1, candidates0, kMixedLambda); + mix(candidates1, candidates1, candidates0, candidates1, kMixedLambda); + mix(candidates0, candidates0, candidates0, candidates1, kMixedKaon); + mix(candidates1, candidates1, candidates1, candidates0, kMixedKaon); + } + + // All the assignments of the Omega, Lambda and kaon to three different collisions. The collision + // providing the Omega is the reference one. + void mixTriple(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1, CollisionCandidates const& candidates2) + { + const std::array sources{&candidates0, &candidates1, &candidates2}; + std::array roles{0, 1, 2}; // Sources of the Omega, Lambda and kaon. + do { + const auto& omegaSource = *sources[roles[0]]; + mix(omegaSource, omegaSource, *sources[roles[1]], *sources[roles[2]], kMixedAll); + } while (std::next_permutation(roles.begin(), roles.end())); + } + void processData(Collisions const& collision, TracksFull const& tracks, aod::V0s const& v0s, aod::Cascades const& cascades, aod::BCsWithTimestamps const& bcs) { - if (acceptCollision(collision, bcs)) { - fillFromRawTables(collision, tracks, cascades, v0s); + if (!acceptCollision(collision, bcs)) { + return; } + const auto candidates = buildCollisionCandidates(collision, tracks, cascades, v0s, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); } PROCESS_SWITCH(doubleOmegaTreeCreator, processData, "Reconstruct Omega + Lambda + kaon from raw V0/cascade indices", true); @@ -1174,7 +912,18 @@ struct doubleOmegaTreeCreator { v0sThisCollision.bindExternalIndices(&tracks); cascadesThisCollision.bindExternalIndices(&tracks); cascadesThisCollision.bindExternalIndices(&v0s); - fillFromRawTables(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, &mcParticles); + const auto candidates = buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + DoubleOmegaMCInfo mcInfo; + if (getMCInfo(mcInfo, + tracks.rawIteratorAt(omega.posTrackId), tracks.rawIteratorAt(omega.negTrackId), tracks.rawIteratorAt(omega.bachelorId), + tracks.rawIteratorAt(lambda.posTrackId), tracks.rawIteratorAt(lambda.negTrackId), tracks.rawIteratorAt(kaon.trackId), + omega.sign, mcParticles)) { + reconstructedDoubleOmegaIds.push_back(mcInfo.motherId); + writeMCCandidate(cand, mcInfo, true); + } + }); } for (const auto& mcParticle : mcParticles) { DoubleOmegaMCInfo mcInfo; @@ -1278,7 +1027,7 @@ struct doubleOmegaTreeCreator { if (daughterTrackIds[daughterIndex] < 0) { daughterTrackIds[daughterIndex] = track.globalIndex(); } - if (selectTrack(track)) { + if (helper.selectTrack(track)) { selectedReconstructedDaughters[daughterIndex] = true; if (selectedTrackIds[daughterIndex] < 0) { selectedTrackIds[daughterIndex] = track.globalIndex(); @@ -1384,6 +1133,384 @@ struct doubleOmegaTreeCreator { } } PROCESS_SWITCH(doubleOmegaTreeCreator, processFindableTracks, "Count findable double-Omega decays and reconstruct selected daughter tracks", false); + + // Bin index of value on a ConfigurableAxis (variable or fixed width), -1 outside the axis. + static int findBin(std::vector const& axis, double value) + { + if (axis.size() < 3) { + return -1; + } + if (axis[0] == VARIABLE_WIDTH) { + const auto edge = std::upper_bound(axis.begin() + 1, axis.end(), value); + if (edge == axis.begin() + 1 || edge == axis.end()) { + return -1; + } + return static_cast(std::distance(axis.begin() + 2, edge)); + } + const int nBins = static_cast(axis[0]); + if (value < axis[1] || value >= axis[2]) { + return -1; + } + return static_cast((value - axis[1]) / (axis[2] - axis[1]) * nBins); + } + + static int nBins(std::vector const& axis) + { + return axis[0] == VARIABLE_WIDTH ? static_cast(axis.size()) - 2 : static_cast(axis[0]); + } + + // Daughter track indices identifying a combination. Seeds sharing a daughter would produce the same + // combination more than once: each combination is written only once. + using MixedCombination = std::array; + + // Mixing pool of same-event candidates per (radius, phi, charge) bin, kept across dataframes. + std::unordered_map> mSeedPool; + std::size_t mPoolSeeds = 0; + int64_t mDataframeCounter = 0; + double mProcessingTimeSum = 0.; + + // Indices are only unique within a dataframe: the dataframe counter goes in the upper bits. + int64_t uniqueIndex(int64_t index) const + { + return index < 0 ? index : (mDataframeCounter << 32) | index; + } + + // The track identifiers read from the derived tables (see trackFingerprint) already identify a track + // in any dataframe; the collision index is made unique across dataframes. + MixingSeed makeSeed(int64_t collisionId, std::array const& primaryVertex, BuiltOmegaCandidate const& omega, + BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) const + { + return {uniqueIndex(collisionId), primaryVertex, mBz, omega, lambda, kaon}; + } + + // Builds and writes one mixed candidate, with the primary vertex of the reference seed. + void mixSeedDaughters(MixingSeed const& reference, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, + BuiltKaonCandidate const& kaon, MixingType mixingType, std::set& written) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + const MixedCombination combination{omega.posTrackId, omega.negTrackId, omega.bachelorId, lambda.posTrackId, lambda.negTrackId, kaon.trackId}; + if (!written.insert(combination).second || DoubleOmegaBuilderHelper::sharesTracks(omega, lambda, kaon)) { + return; + } + DoubleOmegaCandidate cand; + if (!helper.buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + return; + } + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + } + + // One daughter of each seed replaced by the corresponding daughter of the other seed. The seed + // keeping two of its daughters provides the primary vertex. + void mixSeedPair(MixingSeed const& seed0, MixingSeed const& seed1, std::set& written) + { + mixSeedDaughters(seed0, seed1.omega, seed0.lambda, seed0.kaon, kMixedOmega, written); + mixSeedDaughters(seed1, seed0.omega, seed1.lambda, seed1.kaon, kMixedOmega, written); + mixSeedDaughters(seed0, seed0.omega, seed1.lambda, seed0.kaon, kMixedLambda, written); + mixSeedDaughters(seed1, seed1.omega, seed0.lambda, seed1.kaon, kMixedLambda, written); + mixSeedDaughters(seed0, seed0.omega, seed0.lambda, seed1.kaon, kMixedKaon, written); + mixSeedDaughters(seed1, seed1.omega, seed1.lambda, seed0.kaon, kMixedKaon, written); + } + + // Two daughters of a seed replaced by those of two other seeds: Omega, Lambda and kaon from three + // different collisions, in all the assignments. The seed providing the Omega is the reference. + void mixSeedTriple(MixingSeed const& seed0, MixingSeed const& seed1, MixingSeed const& seed2, std::set& written) + { + const std::array seeds{&seed0, &seed1, &seed2}; + std::array roles{0, 1, 2}; // Seeds providing the Omega, Lambda and kaon. + do { + const auto& omegaSeed = *seeds[roles[0]]; + mixSeedDaughters(omegaSeed, omegaSeed.omega, seeds[roles[1]]->lambda, seeds[roles[2]]->kaon, kMixedAll, written); + } while (std::next_permutation(roles.begin(), roles.end())); + } + + // Seeds can be mixed if they come from different collisions with close primary vertices, recorded + // with the same magnetic field. + bool canMix(MixingSeed const& seed0, MixingSeed const& seed1) const + { + return seed0.collisionId != seed1.collisionId && + std::abs(seed0.bz - seed1.bz) < 1.e-3f && + std::abs(seed0.primaryVertex[2] - seed1.primaryVertex[2]) <= mixingOpts.maxDeltaPVz.value; + } + + // Mixes a new seed with the most recent compatible seeds of its pool bin: pairs with each of them, + // triples with each pair of them coming from different collisions. + void mixWithPool(MixingSeed const& seed, std::deque const& pool, std::set& written) + { + const auto nPartners = static_cast(mixingOpts.nMixingCandidates.value); + std::vector partners; + for (auto partner = pool.rbegin(); partner != pool.rend() && partners.size() < nPartners; ++partner) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (!canMix(seed, *partner)) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + partners.push_back(&*partner); + mixSeedPair(seed, *partner, written); + } + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (std::size_t j = 0; j < partners.size(); ++j) { + for (std::size_t k = j + 1; k < partners.size(); ++k) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + if (!canMix(*partners[j], *partners[k])) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + mixSeedTriple(seed, *partners[j], *partners[k], written); + } + } + } + + // Same-event candidates of a collision become seeds, binned in decay radius, momentum azimuth and + // charge (mixingOpts.binsRadius, mixingOpts.binsPhi). The seeds are mixed with the pool first and + // added to it afterwards, so seeds of the same collision are never mixed with each other. + void mixCollisionSeeds(int64_t collisionId, CollisionCandidates const& candidates, std::set& written) + { + const int nPhiBins = nBins(mixingOpts.binsPhi.value); + const auto poolDepth = static_cast(mixingOpts.poolDepth.value); + std::vector> newSeeds; + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + const float radius = std::hypot(cand.x, cand.y); + const int radiusBin = findBin(mixingOpts.binsRadius.value, radius); + const int phiBin = findBin(mixingOpts.binsPhi.value, cand.phi); + if (radiusBin < 0 || phiBin < 0) { + return; + } + histos.fill(HIST("Mixing/hSeedRadiusPhi"), radius, cand.phi); + const int key = (radiusBin * nPhiBins + phiBin) * 2 + DoubleOmegaBuilderHelper::chargeIndex(cand.sign); + newSeeds.emplace_back(key, makeSeed(collisionId, candidates.primaryVertex, omega, lambda, kaon)); + }); + for (const auto& [key, seed] : newSeeds) { + mixWithPool(seed, mSeedPool[key], written); + } + for (auto& [key, seed] : newSeeds) { + auto& pool = mSeedPool[key]; + pool.push_back(std::move(seed)); + ++mPoolSeeds; + if (pool.size() > poolDepth) { + pool.pop_front(); + --mPoolSeeds; + } + } + } + + void monitorMixing(std::chrono::steady_clock::time_point const& start) + { + const double processingTime = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + mProcessingTimeSum += processingTime; + // Lower bound: deque and hash-map bookkeeping are not included. + const double poolMemory = static_cast(mPoolSeeds * sizeof(MixingSeed)) / (1024. * 1024.); + histos.fill(HIST("Mixing/hProcessingTime"), processingTime); + histos.fill(HIST("Mixing/hPoolSeeds"), mPoolSeeds); + histos.fill(HIST("Mixing/hPoolMemory"), poolMemory); + histos.fill(HIST("Mixing/hProcessingTimeVsPoolSeeds"), mPoolSeeds, processingTime); + if (mixingOpts.monitoringInterval > 0 && mDataframeCounter % mixingOpts.monitoringInterval == 0) { + LOGP(info, "Candidate mixing, dataframe {}: {} seeds in {} pool bins ({:.2f} MB), {:.1f} ms in this dataframe, {:.1f} ms on average", + mDataframeCounter, mPoolSeeds, mSeedPool.size(), poolMemory, processingTime, mProcessingTimeSum / mDataframeCounter); + } + } + + // --------------------------------------------------------------------------------------------- + // Derived data produced by doubleOmegaBuilder, read on the fly or from saved (merged) derived data. + + // The derived tables store a track once per collision, so the same track can appear in the rows of + // different collisions (V0s and cascades can use tracks associated to another collision). Tracks are + // identified across collisions by their parameters, which are bitwise identical for the same track. + template + static int64_t trackFingerprint(Row const& row) + { + std::size_t hash = 0; + for (const float value : {row.x(), row.alpha(), row.y(), row.z(), row.snp(), row.tgl(), row.signed1Pt()}) { + hash ^= std::hash{}(value) + 0x9e3779b97f4a7c15ULL + (hash << 6) + (hash >> 2); + } + return static_cast(hash); + } + + // Derived collisions store the run number and the magnetic field: the propagator, the material LUT + // and the fitters are configured again only when the field changes. + void initCCDBForDerived(int runNumber, float bz) + { + if (mRunNumber != 0 && std::abs(bz - mBz) < kBzTolerance) { + return; + } + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", runNumber); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for run " << runNumber; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForRun("GLO/Param/MatLUT", runNumber)); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for run " << runNumber; + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + mRunNumber = runNumber; + } + + // Reads the candidates of a derived collision back, applying again the selections that can be + // tightened with respect to the production ones. + template + CollisionCandidates readCollisionCandidates(C const& collision, aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, aod::DOmegaLambdas const& lambdas) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + + for (const auto& row : omegas.sliceBy(derivedOmegasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const auto bachelor = tracks.rawIteratorAt(row.bachelorId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + const uint32_t lambdaDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + const BuiltOmegaCandidate omega{row.px(), row.py(), row.pz(), + row.decayX(), row.decayY(), row.decayZ(), + row.massOmega(), row.massXi(), + row.sign(), + trackFingerprint(posTrack), trackFingerprint(negTrack), trackFingerprint(bachelor), + getTrackParCov(row), + static_cast(bachelor.detectorMap()) | (lambdaDetectorMap << 3), + bachelor.dcaXYToPV(), proton.dcaXYToPV(), pion.dcaXYToPV(), + row.dcaDaughters()}; + if (helper.isSelectedOmega(omega)) { + candidates.omegas.push_back(omega); + } + } + + for (const auto& row : lambdas.sliceBy(derivedLambdasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + BuiltLambdaCandidate lambda; + lambda.candidate = {.px = row.px(), .py = row.py(), .pz = row.pz(), .mass = row.massLambda()}; + lambda.parentTrack = getTrackParCov(row); + lambda.parentTrack.setAbsCharge(0); // Neutral, as created by the V0 fit. + lambda.decayVertex = {row.decayX(), row.decayY(), row.decayZ()}; + lambda.posTrackId = trackFingerprint(posTrack); + lambda.negTrackId = trackFingerprint(negTrack); + lambda.daughterDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + lambda.dcaXYProtonToPV = proton.dcaXYToPV(); + lambda.dcaXYPionToPV = pion.dcaXYToPV(); + lambda.dcaDaughters = row.dcaDaughters(); + if (helper.isSelectedLambda(lambda)) { + candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(row.sign())].push_back(lambda); + } + } + + for (const auto& row : tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex())) { + if (!row.isKaon()) { + continue; + } + const BuiltKaonCandidate kaon{getTrackParCov(row), + trackFingerprint(row), + static_cast(row.sign()), + row.tpcNSigmaKa(), + row.tofNSigmaKa(), + row.detectorMap()}; + if (helper.isSelectedKaon(kaon)) { + candidates.kaons[DoubleOmegaBuilderHelper::chargeIndex(kaon.sign)].push_back(kaon); + } + } + fillCandidateQA(candidates); + return candidates; + } + + void processDerivedData(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + for (const auto& collision : collisions) { + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedData, "Same-event Omega + Lambda + kaon from the derived tables of doubleOmegaBuilder", false); + + // Mixed-event background from the derived tables. Saved derived data can be merged into large + // dataframes, which provides many more mixing partners than the input AO2D dataframes. Only + // collisions recorded with the same magnetic field are mixed. + void processDerivedMixing(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + std::unordered_map candidatesPerCollision; + for (const auto& collision : collisions) { + candidatesPerCollision.emplace(collision.globalIndex(), readCollisionCandidates(collision, tracks, omegas, lambdas)); + } + + DerivedMixingBinning binning{{mixingOpts.binsVtxZ, mixingOpts.binsMultiplicity}, true}; + for (const auto& [collision0, collision1] : selfPairCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (std::abs(collision0.posZ() - collision1.posZ()) > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixPair(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex())); + } + + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (const auto& [collision0, collision1, collision2] : selfTripleCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + const auto [minPVz, maxPVz] = std::minmax({collision0.posZ(), collision1.posZ(), collision2.posZ()}); + if (maxPVz - minPVz > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance || + std::abs(collision0.bz() - collision2.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixTriple(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex()), + candidatesPerCollision.at(collision2.globalIndex())); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixing, "Mixed-event Omega + Lambda + kaon background from the derived tables of doubleOmegaBuilder", false); + + // Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, as in + // decay3bodybuilder. The same-event candidates (seeds) are binned in decay radius, momentum azimuth + // and charge, so seeds in the same bin have their decay vertex in the same region of the detector and + // point in the same direction. Mixed candidates are built by swapping one (or two) daughters between + // seeds of different collisions, which keeps the vertex topology without moving any track. Seeds are + // pooled across dataframes. + void processDerivedMixingCandidates(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + const auto start = std::chrono::steady_clock::now(); + ++mDataframeCounter; + std::set written; // Every combination has a daughter from this dataframe. + + for (const auto& collision : collisions) { + // Seeds are mixed only with seeds recorded with the same field (canMix), so the field of the + // current collision is the right one for all its mixed candidates. + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + mixCollisionSeeds(collision.globalIndex(), candidates, written); + } + monitorMixing(start); + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixingCandidates, "Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, seeds binned in decay radius and azimuth", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Utils/doubleOmegaBuilderHelper.h b/PWGLF/Utils/doubleOmegaBuilderHelper.h new file mode 100644 index 00000000000..ac071658516 --- /dev/null +++ b/PWGLF/Utils/doubleOmegaBuilderHelper.h @@ -0,0 +1,628 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilderHelper.h +/// \brief Building blocks of the double-Omega (Omega + Lambda + kaon) reconstruction, shared by the +/// candidate builder (derived data producer) and the tree creator. + +#ifndef PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ +#define PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwglf::doubleomega +{ + +struct DoubleOmegaCandidate { + float pt = -999.f; + float eta = -999.f; + float phi = -999.f; + float x = -999.f; + float y = -999.f; + float z = -999.f; + float pvX = -999.f; + float pvY = -999.f; + float pvZ = -999.f; + float cosPAOmega = -999.f; + float cosPADirectLambda = -999.f; + float cosPADoubleOmega = -999.f; + float cosPAOmegaToPV = -999.f; + float cosPADirectLambdaToPV = -999.f; + float dcaXYOmegaToPV = -999.f; + float dcaXYDirectLambdaToPV = -999.f; + float dcaXYDirectKaonToPV = -999.f; + float dcaXYOmegaKaonToPV = -999.f; + float dcaXYOmegaProtonToPV = -999.f; + float dcaXYOmegaPionToPV = -999.f; + float dcaXYDirectProtonToPV = -999.f; + float dcaXYDirectPionToPV = -999.f; + float tpcNSigmaDirectKaon = -999.f; + float tofNSigmaDirectKaon = -999.f; + uint32_t daughterDetectorMap = 0; + float mass = -999.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; +}; + +struct LambdaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float mass = -999.f; +}; + +struct BuiltLambdaCandidate { + LambdaCandidate candidate; + o2::track::TrackParCov parentTrack; + std::array decayVertex{}; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltOmegaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float x = 0.f; + float y = 0.f; + float z = 0.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + int64_t bachelorId = -1; + o2::track::TrackParCov parentTrack; + uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. + float dcaXYKaonToPV = -999.f; + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltKaonCandidate { + o2::track::TrackParCov track; + int64_t trackId = -1; + int8_t sign = 0; + float tpcNSigma = -999.f; // -999 when TPC is unavailable. + float tofNSigma = -999.f; // -999 when TOF is unavailable. + uint32_t detectorMap = 0; +}; + +// Omega, Lambda and kaon candidates of one collision, built once and paired afterwards. +// Lambdas and kaons are stored per charge of the Omega they can be paired with (see chargeIndex). +struct CollisionCandidates { + std::array primaryVertex{}; + std::vector omegas; + std::array, 2> lambdas; + std::array, 2> kaons; +}; + +struct DoubleOmegaSelections { + float etaMax = 0.9f; + float minNITSClus = 0.f; + float minNTPCClus = 80.f; + float minNCrossedRows = 100.f; + float maxNSharedTPCClus = 5.f; + float nSigmaTPCCut = 3.f; + float dcaBachToPV = 0.05f; + float dcaKaonToPV = 0.05f; + float dcaOmegaToPV = 0.f; + float dcaDirectLambdaToPV = 0.f; + float dcaV0DauToPV = 0.05f; + float dcaV0Bach = 1.f; + float dcaLambdaDaughters = 1.f; + float mOmegaWindow = 0.01f; + float mLambdaWindow = 0.01f; + float maxDoubleOmegaMass = 3.6f; + float minCosPAOmega = -1.f; + float minCosPADirectLambda = -1.f; + float minCosPADoubleOmega = -1.f; + float minDoubleOmegaDecayRadius = 1.f; +}; + +class DoubleOmegaBuilderHelper +{ + public: + DoubleOmegaSelections selections; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::vertexing::DCAFitterN<2> fitter; + o2::vertexing::DCAFitterN<3> fitter3Body; + + void initFitters() + { + configureFitter(fitter); + configureFitter(fitter3Body); + } + + void setBz(float bz) + { + fitter.setBz(bz); + fitter3Body.setBz(bz); + } + + void setMatCorrType(o2::base::Propagator::MatCorrType matCorrType) + { + matCorr = matCorrType; + fitter.setMatCorrType(matCorrType); + fitter3Body.setMatCorrType(matCorrType); + } + + static int chargeIndex(int8_t charge) + { + return charge < 0 ? 0 : 1; + } + + template + static uint32_t detectorMap(Track const& track) + { + return static_cast(track.hasITS()) | + (static_cast(track.hasTPC()) << 1) | + (static_cast(track.hasTOF()) << 2); + } + + template + static BuiltKaonCandidate buildKaon(Track const& track) + { + return {getTrackParCov(track), + track.globalIndex(), + static_cast(track.sign()), + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + detectorMap(track)}; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda) + { + const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; + return std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || + std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.negTrackId) != omegaTrackIds.end(); + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltKaonCandidate const& kaon) + { + return kaon.trackId == omega.posTrackId || kaon.trackId == omega.negTrackId || kaon.trackId == omega.bachelorId; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) + { + return sharesTracks(omega, lambda) || sharesTracks(omega, kaon) || + kaon.trackId == lambda.posTrackId || kaon.trackId == lambda.negTrackId; + } + + template + bool selectTrack(T const& track) const + { + if (std::abs(track.eta()) > selections.etaMax) { + return false; + } + if (track.itsNCls() < selections.minNITSClus || + track.tpcNClsFound() < selections.minNTPCClus || + track.tpcNClsCrossedRows() < selections.minNCrossedRows || + track.tpcNClsCrossedRows() < 0.8f * track.tpcNClsFindable() || + track.tpcNClsShared() > selections.maxNSharedTPCClus) { + LOG(debug) << "Track failed selection: itsNCls=" << static_cast(track.itsNCls()) << ", tpcNClsFound=" << track.tpcNClsFound() << ", tpcNClsCrossedRows=" << static_cast(track.tpcNClsCrossedRows()) << ", tpcNClsFindable=" << static_cast(track.tpcNClsFindable()) << ", tpcNClsShared=" << static_cast(track.tpcNClsShared()); + return false; + } + return true; + } + + template + bool isSelectedKaon(Track const& track, int8_t charge) const + { + return track.sign() == charge && isSelectedKaon(track); + } + + template + bool isSelectedKaon(Track const& track) const + { + return selectTrack(track) && + std::abs(track.tpcNSigmaKa()) <= selections.nSigmaTPCCut; + } + + // Transverse DCA of a track to the collision primary vertex, computed by propagating + // a copy of the track parameters. Returns false if the propagation fails. + template + bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const + { + auto trackParCov = getTrackParCov(track); + const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { + return false; + } + dcaXY = dca[0]; + return true; + } + + template + bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) + { + if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || + !selectTrack(posTrack) || !selectTrack(negTrack)) { + return false; + } + + const bool isMatter = charge < 0; + const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); + const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); + if (std::abs(protonNSigma) > selections.nSigmaTPCCut || std::abs(pionNSigma) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYPos = -999.f; + float dcaXYNeg = -999.f; + if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { + LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (std::abs(dcaXYPos) < selections.dcaV0DauToPV || std::abs(dcaXYNeg) < selections.dcaV0DauToPV) { + LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; + return false; + } + + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + int nCandidates = 0; + try { + nCandidates = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Lambda daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array posMomentum{}; + std::array negMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(posMomentum); + fitter.getTrack(1).getPxPyPzGlo(negMomentum); + const std::array lambdaMomentum{ + posMomentum[0] + negMomentum[0], + posMomentum[1] + negMomentum[1], + posMomentum[2] + negMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float mass = isMatter ? RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPionCharged}) + : RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(lambdaMomentum); + if (std::abs(eta) > selections.etaMax || + dcaDaughters > selections.dcaLambdaDaughters || + std::abs(mass - o2::constants::physics::MassLambda0) > selections.mLambdaWindow) { + return false; + } + + builtLambda.candidate = { + .px = lambdaMomentum[0], + .py = lambdaMomentum[1], + .pz = lambdaMomentum[2], + .mass = mass}; + builtLambda.parentTrack = fitter.createParentTrackParCov(0); + builtLambda.decayVertex = decayVertex; + builtLambda.posTrackId = posTrack.globalIndex(); + builtLambda.negTrackId = negTrack.globalIndex(); + // Keep particle-role ordering for matter and antimatter alike. + builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) + : detectorMap(negTrack) | (detectorMap(posTrack) << 3); + builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; + builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; + builtLambda.dcaDaughters = dcaDaughters; + return true; + } + + template + bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) + { + if (bachelor.sign() == 0 || + !selectTrack(bachelor) || + std::abs(bachelor.tpcNSigmaKa()) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYBachelor = -999.f; + if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < selections.dcaBachToPV) { + return false; + } + + BuiltLambdaCandidate lambda; + if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { + return false; + } + + auto v0TrackParCov = lambda.parentTrack; + auto bachelorTrackParCov = getTrackParCov(bachelor); + int nCandidates = 0; + try { + nCandidates = fitter.process(v0TrackParCov, bachelorTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Omega daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex() << ", " << bachelor.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array lambdaMomentum{}; + std::array bachelorMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(lambdaMomentum); + fitter.getTrack(1).getPxPyPzGlo(bachelorMomentum); + const std::array omegaMomentum{ + lambdaMomentum[0] + bachelorMomentum[0], + lambdaMomentum[1] + bachelorMomentum[1], + lambdaMomentum[2] + bachelorMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float massOmega = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + const float massXi = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassPionCharged}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(omegaMomentum); + if (dcaDaughters > selections.dcaV0Bach || + std::abs(eta) > selections.etaMax || + std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > selections.mOmegaWindow) { + return false; + } + + builtOmega = { + omegaMomentum[0], + omegaMomentum[1], + omegaMomentum[2], + decayVertex[0], + decayVertex[1], + decayVertex[2], + massOmega, + massXi, + static_cast(bachelor.sign()), + posTrack.globalIndex(), + negTrack.globalIndex(), + bachelor.globalIndex(), + fitter.createParentTrackParCov(), + detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), + dcaXYBachelor, + lambda.dcaXYProtonToPV, + lambda.dcaXYPionToPV, + dcaDaughters}; + return true; + } + + // Selections of the Omega, Lambda and kaon candidates that can be applied again on their stored + // properties (e.g. when reading them back from derived data), to tighten the production selections. + bool isSelectedOmega(BuiltOmegaCandidate const& omega) const + { + return std::abs(RecoDecay::eta(std::array{omega.px, omega.py, omega.pz})) <= selections.etaMax && + std::abs(omega.massOmega - o2::constants::physics::MassOmegaMinus) <= selections.mOmegaWindow && + omega.dcaDaughters <= selections.dcaV0Bach && + std::abs(omega.dcaXYKaonToPV) >= selections.dcaBachToPV && + std::abs(omega.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(omega.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedLambda(BuiltLambdaCandidate const& lambda) const + { + return std::abs(RecoDecay::eta(std::array{lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz})) <= selections.etaMax && + std::abs(lambda.candidate.mass - o2::constants::physics::MassLambda0) <= selections.mLambdaWindow && + lambda.dcaDaughters <= selections.dcaLambdaDaughters && + std::abs(lambda.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(lambda.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedKaon(BuiltKaonCandidate const& kaon) const + { + return kaon.tpcNSigma > -999.f && std::abs(kaon.tpcNSigma) <= selections.nSigmaTPCCut; + } + + bool buildDoubleOmega(std::array const& primaryVertex, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& directLambda, BuiltKaonCandidate const& kaon, DoubleOmegaCandidate& cand) + { + auto omegaTrackParCov = omega.parentTrack; + auto lambdaTrackParCov = directLambda.parentTrack; + auto kaonTrackParCov = kaon.track; + int nCandidates = 0; + try { + nCandidates = fitter3Body.process(omegaTrackParCov, lambdaTrackParCov, kaonTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting the double-Omega candidate"; + return false; + } + if (nCandidates == 0) { + return false; + } + + auto omegaTrackAtVertex = fitter3Body.getTrack(0); + auto lambdaTrackAtVertex = fitter3Body.getTrack(1); + auto kaonTrackAtVertex = fitter3Body.getTrack(2); + std::array omegaMomentum{}; + std::array lambdaMomentum{}; + std::array kaonMomentum{}; + omegaTrackAtVertex.getPxPyPzGlo(omegaMomentum); + lambdaTrackAtVertex.getPxPyPzGlo(lambdaMomentum); + kaonTrackAtVertex.getPxPyPzGlo(kaonMomentum); + + const std::array totalMomentum{ + omegaMomentum[0] + lambdaMomentum[0] + kaonMomentum[0], + omegaMomentum[1] + lambdaMomentum[1] + kaonMomentum[1], + omegaMomentum[2] + lambdaMomentum[2] + kaonMomentum[2]}; + const auto& fittedVertex = fitter3Body.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const o2::math_utils::Point3D primaryVertexPoint{primaryVertex[0], primaryVertex[1], primaryVertex[2]}; + const std::array omegaDecayVertex{omega.x, omega.y, omega.z}; + + std::array dcaOmega{}; + std::array dcaDirectLambda{}; + std::array dcaDirectKaon{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, omegaTrackAtVertex, 2.f, matCorr, &dcaOmega) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, lambdaTrackAtVertex, 2.f, matCorr, &dcaDirectLambda) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, kaonTrackAtVertex, 2.f, matCorr, &dcaDirectKaon)) { + return false; + } + + const float massSquared = RecoDecay::m2(std::array{omegaMomentum, lambdaMomentum, kaonMomentum}, + std::array{o2::constants::physics::MassOmegaMinus, o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + + cand.pt = std::hypot(totalMomentum[0], totalMomentum[1]); + cand.eta = RecoDecay::eta(totalMomentum); + cand.phi = std::atan2(totalMomentum[1], totalMomentum[0]); + cand.x = decayVertex[0]; + cand.y = decayVertex[1]; + cand.z = decayVertex[2]; + cand.pvX = primaryVertex[0]; + cand.pvY = primaryVertex[1]; + cand.pvZ = primaryVertex[2]; + cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); + cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); + cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); + // PV pointing angles use the daughter momenta at their own decay vertices. + cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); + cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); + cand.dcaXYOmegaToPV = dcaOmega[0]; + cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; + cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; + cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; + cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; + cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; + cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; + cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; + cand.tpcNSigmaDirectKaon = kaon.tpcNSigma; + cand.tofNSigmaDirectKaon = kaon.tofNSigma; + cand.daughterDetectorMap = omega.daughterDetectorMap | + (directLambda.daughterDetectorMap << 9) | + (kaon.detectorMap << 15); + cand.mass = std::sqrt(std::max(0.f, massSquared)); + if (cand.cosPAOmega < selections.minCosPAOmega || + cand.cosPADirectLambda < selections.minCosPADirectLambda || + cand.cosPADoubleOmega < selections.minCosPADoubleOmega || + std::hypot(decayVertex[0], decayVertex[1]) < selections.minDoubleOmegaDecayRadius || + std::abs(cand.dcaXYOmegaToPV) < selections.dcaOmegaToPV || + std::abs(cand.dcaXYDirectLambdaToPV) < selections.dcaDirectLambdaToPV || + std::abs(cand.dcaXYDirectKaonToPV) < selections.dcaKaonToPV || + cand.mass > selections.maxDoubleOmegaMass) { + return false; + } + cand.massOmega = omega.massOmega; + cand.massXi = omega.massXi; + cand.sign = omega.sign; + return true; + } + + // Builds the Omega, Lambda and kaon candidates of a collision. If requireOmega is set, Lambdas and + // kaons are built only if the collision has at least one Omega. + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + for (const auto& rawCascade : cascades) { + const auto omegaV0 = rawCascade.template v0_as(); + BuiltOmegaCandidate omega; + if (buildOmega(collision, omegaV0.template posTrack_as(), omegaV0.template negTrack_as(), rawCascade.template bachelor_as(), omega)) { + candidates.omegas.push_back(omega); + } + } + if (requireOmega && candidates.omegas.empty()) { + return candidates; + } + + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& rawV0 : v0s) { + BuiltLambdaCandidate lambda; + if (buildLambda(collision, rawV0.template posTrack_as(), rawV0.template negTrack_as(), charge, lambda)) { + candidates.lambdas[chargeIndex(charge)].push_back(lambda); + } + } + for (const auto& track : tracks) { + if (isSelectedKaon(track, charge)) { + candidates.kaons[chargeIndex(charge)].push_back(buildKaon(track)); + } + } + } + return candidates; + } + + // Pairs the Omegas, Lambdas and kaons of (possibly different) collisions. The double-Omega candidate + // is built with respect to the primary vertex of the reference collision. + template + void combine(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, F&& onCandidate) + { + for (const auto& omega : omegaSource.omegas) { + for (const auto& lambda : lambdaSource.lambdas[chargeIndex(omega.sign)]) { + for (const auto& kaon : kaonSource.kaons[chargeIndex(omega.sign)]) { + // Tracks can also be shared by V0s and cascades associated to different collisions. + if (sharesTracks(omega, lambda, kaon)) { + continue; + } + DoubleOmegaCandidate cand; + if (!buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + continue; + } + onCandidate(cand, omega, lambda, kaon); + } + } + } + } + + // True if at least one Omega has a Lambda and a kaon of compatible charge that do not share its tracks. + static bool hasOmegaLambdaKaon(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const auto& lambdas = candidates.lambdas[chargeIndex(omega.sign)]; + const auto& kaons = candidates.kaons[chargeIndex(omega.sign)]; + const bool hasLambda = std::any_of(lambdas.begin(), lambdas.end(), [&](BuiltLambdaCandidate const& lambda) { return !sharesTracks(omega, lambda); }); + const bool hasKaon = std::any_of(kaons.begin(), kaons.end(), [&](BuiltKaonCandidate const& kaon) { return !sharesTracks(omega, kaon); }); + if (hasLambda && hasKaon) { + return true; + } + } + return false; + } + + private: + template + static void configureFitter(Fitter& dcaFitter) + { + dcaFitter.setPropagateToPCA(true); + dcaFitter.setMaxR(200.); + dcaFitter.setMaxDZIni(4.); + dcaFitter.setMinParamChange(1.e-3); + dcaFitter.setMinRelChi2Change(0.9); + dcaFitter.setUseAbsDCA(true); + } +}; + +} // namespace o2::pwglf::doubleomega + +#endif // PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ From 09cc87fb9c019e1dd1f060b65dcff8f744ca66d9 Mon Sep 17 00:00:00 2001 From: alcaliva <32872606+alcaliva@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:29:11 +0200 Subject: [PATCH 338/648] [PWGLF] Add jet pt histograms (#18284) --- PWGLF/Tasks/Nuspex/antinucleiInJets.cxx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx index 624b5bebb22..e636fba327a 100644 --- a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx +++ b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx @@ -282,7 +282,7 @@ struct AntinucleiInJets { getReweightingHistograms(ccdb, TString(pathToFile)); } - // Binning + // pt binning double min = 0.0; double max = 6.0; int nbins = 120; @@ -326,6 +326,9 @@ struct AntinucleiInJets { registryData.add("jetEffectiveAreaOverPiR2", "jet effective area / piR^2", HistType::kTH1F, {{2000, 0, 2, "Area/#piR^{2}"}}); registryData.add("jetArea", "jetArea", HistType::kTH1F, {{5000, 0, 5, "area"}}); + // Jet pt distribution in data + registryData.add("jetPtData", "jet pt distribution in data", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // angle between track and jet axis registryData.add("theta_track_jet", "theta_track_jet", HistType::kTH2F, {{100, 0, 100, "#it{p}^{jet}_{T} (GeV/#it{c})"}, {400, 0, 20.0, "#theta_{track-jet} (deg)"}}); @@ -379,6 +382,9 @@ struct AntinucleiInJets { registryMC.add("genEvents", "number of generated events in mc", HistType::kTH1F, {{10, 0, 10, "counter"}}); registryMC.add("genJets", "number of generated jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in gen MC + registryMC.add("jetPtMCgenerated", "jet pt distribution in gen MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Size of particle array registryMC.add("sizeParticleArray", "number of particles", HistType::kTH1F, {{1000, 0, 1000, "#it{N}_{part}"}}); @@ -415,6 +421,9 @@ struct AntinucleiInJets { registryMC.add("recEvents", "number of reconstructed events in mc", HistType::kTH1F, {{20, 0, 20, "counter"}}); registryMC.add("recJets", "number of reconstructed jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in rec MC + registryMC.add("jetPtMCreconstructed", "jet pt distribution in rec MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Configuration registryMC.add("settingMC", "settingMC", HistType::kTH2F, {{100, 0.0, 50.0, "min #it{p}^{jet}_{T} (GeV/#it{c})"}, {20, 0.0, 1.0, "#it{R}_{jet}"}}); @@ -1303,6 +1312,10 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Fill jet pt distribution + registryData.fill(HIST("jetPtData"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) @@ -2141,6 +2154,10 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCgenerated"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) @@ -2440,6 +2457,10 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCreconstructed"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) From a4ecfca20aba08aabb73430140adaf4913ffe0f2 Mon Sep 17 00:00:00 2001 From: bhattdurgesh921-sys Date: Sun, 18 Oct 2026 20:31:53 +0530 Subject: [PATCH 339/648] =?UTF-8?q?[PWGLF]=20increase=20the=20upper=20limi?= =?UTF-8?q?t=20of=20the=20transverse=20momentum=20axis=20and=20momentum?= =?UTF-8?q?=E2=80=A6=20(#18344)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGLF/Tasks/Resonances/deltaAnalysis.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx index edcaa3b35ff..2e86337b6bb 100644 --- a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -246,15 +246,15 @@ struct DeltaAnalysis { mPionDCAPtEdges = dcaCuts.pionDCAPtBinEdges.value; mPionMaxDCAxy = dcaCuts.pionMaxDCAxyPerPtBin.value; - const AxisSpec ptAxis{200, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec ptAxis{2000, 0., 20., "p_{T} (GeV/c)"}; const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 8.0, "M_{inv} (GeV/#it{c}^{2})"}; const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; const AxisSpec nSigmaTPCaxis{100, -10., 10., "n#sigma^{TPC}"}; const AxisSpec nSigmaTOFaxis{100, -10., 10., "n#sigma^{TOF}"}; - const AxisSpec momentumAxis{200, 0., 10., "p (GeV/#it{c})"}; - const AxisSpec ptForPIDAxis{200, 0., 10., "p_{T} (GeV/#it{c})"}; + const AxisSpec momentumAxis{2000, 0., 20., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{2000, 0., 20., "p_{T} (GeV/#it{c})"}; const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; From d0dc6896f124c53ded16a55d6725bd7841340fa1 Mon Sep 17 00:00:00 2001 From: Zhiyong Lu <71517277+Luzhiyongg@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:02:46 +0800 Subject: [PATCH 340/648] [PWGCF] add manual ck calculations (#18342) Co-authored-by: ALICE Action Bot --- PWGCF/Flow/Tasks/flowTask.cxx | 77 ++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index 447ceeaa5d6..ec583bdabd8 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -243,6 +243,7 @@ struct FlowTask { std::vector nPt; // fraction of particles in each pT bin in one event std::vector> listNptV2; std::vector> listPtX; + O2_DEFINE_CONFIGURABLE(cfgManualCk, bool, false, "Produce pt-pt variance") } cfgAdditionObs; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "pt axis for histograms"}; @@ -287,7 +288,7 @@ struct FlowTask { std::vector corrconfigs; GFWCorrConfigs gfwConfigs; std::vector corrconfigsPtVn; - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; TRandom3* fRndm = new TRandom3(0); std::vector> etagapsPtPt; std::vector>> bootstrapArray; @@ -324,15 +325,15 @@ struct FlowTask { double mMinSeconds{-1.}; std::unordered_map gHadronicRate; ctpRateFetcher mRateFetcher; - TH2* gCurrentHadronicRate; + TH2* gCurrentHadronicRate = nullptr; RCTFlagsChecker rctChecker{"CBT"}; // phi-EP correction std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; void init(InitContext const&) { @@ -439,6 +440,13 @@ struct FlowTask { registry.add("hPtA_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); registry.add("hPtB_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); } + if (cfgAdditionObs.cfgManualCk) { + registry.add("ptpt_1sub1_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + } if (doprocessMCGen) { registry.add("MCGen/MChPhi", "#phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("MCGen/MChEta", "#eta distribution", {HistType::kTH1D, {axisEta}}); @@ -910,30 +918,30 @@ struct FlowTask { if (cfgUserIO.cfgAcceptance.value.empty() == false) { mAcceptance = ccdb->getForTimeStamp(cfgUserIO.cfgAcceptance, timestamp); if (mAcceptance) - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); else - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); } if (cfgUserIO.cfgEfficiency.value.empty() == false) { mEfficiency = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiency, timestamp); if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } if (cfgUserIO.cfgEfficiencyForNch.value.empty() == false) { mEfficiencyForNch = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiencyForNch, timestamp); if (mEfficiencyForNch == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiencyForNch.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), (void*)mEfficiencyForNch); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), static_cast(mEfficiencyForNch)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.empty() == false) { cfgAdditionObs.hEvAvgMeanPt = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDishEvAvgMeanPt, timestamp); if (cfgAdditionObs.hEvAvgMeanPt == nullptr) { LOGF(fatal, "Could not load mean pT histogram from %s", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str()); } - LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), (void*)cfgAdditionObs.hEvAvgMeanPt); + LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), static_cast(cfgAdditionObs.hEvAvgMeanPt)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDisSelectionSwitch > kNoDptCut) { if (cfgAdditionObs.cfgDptDisCutLow.value.empty() == false) { @@ -941,14 +949,14 @@ struct FlowTask { if (cfgAdditionObs.fDptDisCutLow == nullptr) { LOGF(fatal, "Could not load dptDis low cut histogram from %s", cfgAdditionObs.cfgDptDisCutLow.value.c_str()); } - LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), (void*)cfgAdditionObs.fDptDisCutLow); + LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutLow)); } if (cfgAdditionObs.cfgDptDisCutHigh.value.empty() == false) { cfgAdditionObs.fDptDisCutHigh = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDisCutHigh, timestamp); if (cfgAdditionObs.fDptDisCutHigh == nullptr) { LOGF(fatal, "Could not load dptDis high cut histogram from %s", cfgAdditionObs.cfgDptDisCutHigh.value.c_str()); } - LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), (void*)cfgAdditionObs.fDptDisCutHigh); + LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutHigh)); } } correctionsLoaded = true; @@ -1289,6 +1297,10 @@ struct FlowTask { double weffEventWithinGap08 = 0., weffEventSquareWithinGap08 = 0.; double sumPtsquareWsquareWithinGap08 = 0., sumPtWsquareWithinGap08 = 0.; double nTracksCorrected = 0; + double mpt_sub1 = 0; + double mpt_sub1_weffEv = 0; + double mpt_sub3 = 0; + double mpt_sub3_weffEv = 0; int magnetfield = 0; float independent = cent; float nTracksUncorrected = 0; @@ -1405,6 +1417,16 @@ struct FlowTask { weffEventWithinGap08 += weff; weffEventSquareWithinGap08 += weff * weff; } + if (cfgAdditionObs.cfgManualCk) { + if (track.eta() >= etagapsPtPt[0].first && track.eta() <= etagapsPtPt[0].second) { + mpt_sub1 += weff * track.pt(); + mpt_sub1_weffEv += weff; + } + if (track.eta() >= etagapsPtPt[2].first && track.eta() <= etagapsPtPt[2].second) { + mpt_sub3 += weff * track.pt(); + mpt_sub3_weffEv += weff; + } + } } if (cfgUserIO.cfgConsistentEventFlag && consistentEventVector.size() == 6) { // o2-linter: disable=magic-number (size match) acceptedTracks.nFull += 1; @@ -1477,13 +1499,12 @@ struct FlowTask { if (weffEvent) { registry.fill(HIST("hMeanPt"), independent, ptSum / weffEvent, weffEvent); } - if (weffEventWithinGap08) + if (weffEventWithinGap08) { registry.fill(HIST("hMeanPtWithinGap08"), independent, ptSum_Gap08 / weffEventWithinGap08, cfgUserIO.cfgUserPtVnEvWeightEnabled ? weffEventWithinGap08 : 1.); - if (weffEventWithinGap08) bootstrapArray[sampleIndex][kMeanPtWithinGap08]->Fill(independent, ptSum_Gap08 / weffEventWithinGap08, 1.0); - // c22_gap8 * pt_withGap8 - if (weffEventWithinGap08) + // c22_gap8 * pt_withGap8 fillpTvnProfile(corrconfigs.at(7), ptSum_Gap08, weffEventWithinGap08, HIST("c22_gap08_Weff"), HIST("c22_gap08_trackMeanPt"), independent); + } // PtVariance if (weffEventDiffWithGap08) { registry.fill(HIST("PtVariance_partA_WithinGap08"), independent, @@ -1494,6 +1515,25 @@ struct FlowTask { weffEventDiffWithGap08); } + if (cfgAdditionObs.cfgManualCk) { + // 2pc event weight: + double ev2pc = mpt_sub1_weffEv * mpt_sub3_weffEv; + // manual pt-pt variance + if (mpt_sub1_weffEv) { + mpt_sub1 = mpt_sub1 / mpt_sub1_weffEv; + registry.fill(HIST("pt_1sub1_1pc"), independent, mpt_sub1, mpt_sub1_weffEv); + } + if (mpt_sub3_weffEv) { + mpt_sub3 = mpt_sub3 / mpt_sub3_weffEv; + registry.fill(HIST("pt_1sub3_1pc"), independent, mpt_sub3, mpt_sub3_weffEv); + } + if (mpt_sub1_weffEv && mpt_sub3_weffEv) { + registry.fill(HIST("ptpt_1sub1_1sub3_2pc"), independent, mpt_sub1 * mpt_sub3, ev2pc); + registry.fill(HIST("pt_1sub1_2pc"), independent, mpt_sub1, ev2pc); + registry.fill(HIST("pt_1sub3_2pc"), independent, mpt_sub3, ev2pc); + } + } + // v02 if (cfgAdditionObs.cfgV02Enabled && weffEventFrac > 0) { for (auto ipt = 0; ipt < fPtAxis->GetNbins(); ipt++) { @@ -1569,9 +1609,8 @@ struct FlowTask { ptSum_Gap08 += mcParticle.pt(); count_Gap08 += 1.; } - } - if (withinPtRef) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 1); + } if (withinPtPOI) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 2); if (withinPtPOI && withinPtRef) From 6b68792567a8dc36034f8edfe34782ebfc8c120f Mon Sep 17 00:00:00 2001 From: hfribert Date: Mon, 19 Oct 2026 09:20:52 +0200 Subject: [PATCH 341/648] [PWGCF] Femto: Add MC derived-to-derived processing for Sigmas (#18315) Co-authored-by: Henrik Fribert --- PWGCF/Femto/Core/kinkBuilder.h | 52 +++++++++++++++ PWGCF/Femto/Core/mcBuilder.h | 26 ++++++++ PWGCF/Femto/Core/trackBuilder.h | 7 +++ .../femtoProducerDerivedToDerived.cxx | 63 ++++++++++++++++++- 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index e35e66ae0ff..e747922663d 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -846,6 +846,58 @@ class KinkBuilderDerivedToDerived } } + template + void processSigmaWithMc(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigma == 0) { + return; + } + + auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigma : sigmaSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigma.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaTable.producedSigmas(newCollisionTable.producedCollision.lastIndex(), + sigma.signedPt(), + sigma.eta(), + sigma.phi(), + sigma.mass(), + chaDaughterIndex); + newSigmaTable.producedSigmaMasks(sigma.mask()); + mcBuilder.fillSigmaWithLabel(sigma, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + + template + void processSigmaPlusWithMc(T1 const& col, T2 const& /*sigmaPlusTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlus, T5& trackBuilder, T6& cache, T7& newSigmaPlusTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlus == 0) { + return; + } + + auto sigmaPlusSlice = partitionSigmaPlus->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlus : sigmaPlusSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlus.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaPlusTable.producedSigmaPluses(newCollisionTable.producedCollision.lastIndex(), + sigmaPlus.signedPt(), + sigmaPlus.eta(), + sigmaPlus.phi(), + sigmaPlus.mass(), + chaDaughterIndex); + newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + mcBuilder.fillSigmaPlusWithLabel(sigmaPlus, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + private: int mLimitSigma = 0; int mLimitSigmaPlus = 0; diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 9a76b65d0f9..6727bbb0f47 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -765,6 +765,8 @@ struct McBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedCollisionLabels; o2::framework::Produces producedTrackLabels; + o2::framework::Produces producedSigmaLabels; + o2::framework::Produces producedSigmaPlusLabels; }; struct ConfMcTablesDerivedToDerived : o2::framework::ConfigurableGroup { @@ -824,6 +826,30 @@ class McBuilderDerivedToDerived this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); } + template + void fillSigmaWithLabel(T1 const& sigma, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigma.has_fMcParticle()) { + mcProducts.producedSigmaLabels(-1); + return; + } + auto mcParticle = sigma.template fMcParticle_as(); + mcProducts.producedSigmaLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaPlusWithLabel(T1 const& sigmaPlus, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlus.has_fMcParticle()) { + mcProducts.producedSigmaPlusLabels(-1); + return; + } + auto mcParticle = sigmaPlus.template fMcParticle_as(); + mcProducts.producedSigmaPlusLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + template void reset(T1 const& mcCols, T2 const& mcParticles) { diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 1a89eafeaa0..6ea1d97ebe5 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -998,6 +998,13 @@ class TrackBuilderDerivedToDerived return this->fillTrack(daughter, trackProducts, collisionProducts); } + template + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts, + T4& mcBuilder, T5 const& mcCols, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& mcProducts) + { + return this->fillTrackWithMcLabel(daughter, trackProducts, collisionProducts, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + template void reset(T const& tracks) { diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 0168d308901..3ca85915f55 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -53,6 +53,8 @@ struct FemtoProducerDerivedToDerived { using FemtoSigmaPlus = o2::soa::Join; using FemtoMcTracks = o2::soa::Join; + using FemtoMcSigma = o2::soa::Join; + using FemtoMcSigmaPlus = o2::soa::Join; using FemtoMcParticles = o2::soa::Join; o2::framework::SliceCache cache; @@ -126,6 +128,12 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcSigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); + o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; + void init(o2::framework::InitContext& /*context*/) { const int activeProcesses = @@ -135,7 +143,8 @@ struct FemtoProducerDerivedToDerived { static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + static_cast(doprocessTracksSigmaPlus) + - static_cast(doprocessTracksMc); + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + + static_cast(doprocessTracksSigmaPlusMc); if (activeProcesses != 1) { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } @@ -293,6 +302,58 @@ struct FemtoProducerDerivedToDerived { } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksMc, "Process tracks with MC information", false); + + void processTracksSigmaMc(FilteredFemtoMcCollisions const& cols, + FemtoMcTracks const& tracks, + FemtoMcSigma const& sigma, + o2::aod::FMcCols const& mcCols, + FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + mcBuilder.reset(mcCols, mcParticles); + + for (auto const& col : cols) { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { + continue; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + kinkBuilder.processSigmaWithMc(col, sigma, tracks, mcSigmaPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaMc, "Process sigma and tracks with MC information", false); + + void processTracksSigmaPlusMc(FilteredFemtoMcCollisions const& cols, + FemtoMcTracks const& tracks, + FemtoMcSigmaPlus const& sigmaplus, + o2::aod::FMcCols const& mcCols, + FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + mcBuilder.reset(mcCols, mcParticles); + + for (auto const& col : cols) { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { + continue; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusMc, "Process sigmaPlus and tracks with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) From 9fc0adeea515e2999a3b626bae94aaf6009f2a18 Mon Sep 17 00:00:00 2001 From: MRazza <118839113+MRazza879@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:07:04 +0200 Subject: [PATCH 342/648] [PWGHF] Fix deuteronFromLb for particle VETOes (#18334) Co-authored-by: Marta Razza --- PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx | 66 ++++++++++++-------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index 496ee9c4176..9111456a5ff 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -137,14 +137,13 @@ struct HfTaskDeuteronFromLb { qaHistos.add("Data/hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("Data/hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaZAxis}}); - qaHistos.add("Data/hnSigmaTPCVsPt", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPt", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTPCVsPtFinalSel", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtFinalSel", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurity", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurityVetoTPCTOFClean", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC and TOF veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityNoVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC+TOF veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); // MC generated-level histograms @@ -325,18 +324,16 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); - // TPC veto against pi/K/p/e - const bool passTPCVeto = std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaEl()) > cfgTPCNsigma; - // TOF veto against pi/K/p/e - const bool passTOFVeto = std::abs(track.tofNSigmaPi()) > cfgTofNsigma && - std::abs(track.tofNSigmaKa()) > cfgTofNsigma && - std::abs(track.tofNSigmaPr()) > cfgTofNsigma && - std::abs(track.tofNSigmaEl()) > cfgTofNsigma; + const bool isElectron = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma && std::abs(track.tofNSigmaEl()) < cfgTofNsigma; + const bool isNotElectron = !isElectron; + const bool isPion = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma && std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isNotPion = !isPion; + const bool isKaon = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma && std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isNotKaon = !isKaon; + const bool isProton = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma && std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + const bool isNotProton = !isProton; - const bool isTPCDeClean = isTPCDe && passTPCVeto; + const bool isTPCTOFDeClean = isTPCDe && isNotElectron && isNotPion && isNotKaon && isNotProton; if (track.pt() < ptThresholdforPID) { if (isITSDe) { @@ -344,26 +341,25 @@ struct HfTaskDeuteronFromLb { } } else { if (isTPCDe && track.hasTOF()) { - qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurity"), track.pt(), track.tofNSigmaDe()); + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityNoVeto"), track.pt(), track.tofNSigmaDe()); } - if (isTPCDeClean && track.hasTOF()) { + if (isTPCTOFDeClean && track.hasTOF()) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); } - if (isTPCDeClean && passTOFVeto && track.hasTOF()) { - qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVetoTPCTOFClean"), track.pt(), track.tofNSigmaDe()); - } } const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || - (track.pt() >= ptThresholdforPID && isTPCDeClean && passTOFVeto && isTOFDe && track.hasTOF()); + (track.pt() >= ptThresholdforPID && isTPCTOFDeClean && isTOFDe && track.hasTOF()); if (passFinalSel) { qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); - qaHistos.fill(HIST("Data/hnSigmaTPCVsPt"), track.pt(), track.tpcNSigmaDe()); - qaHistos.fill(HIST("Data/hnSigmaTOFVsPt"), track.pt(), track.tofNSigmaDe()); + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtFinalSel"), track.pt(), track.tpcNSigmaDe()); + if (track.pt() >= ptThresholdforPID) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtFinalSel"), track.pt(), track.tofNSigmaDe()); + } } } } @@ -486,18 +482,16 @@ struct HfTaskDeuteronFromLb { } } - // TPC veto against pi/K/p/e - const bool passTPCVeto = std::abs(track.tpcNSigmaPi()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaKa()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaPr()) > cfgTPCNsigma && - std::abs(track.tpcNSigmaEl()) > cfgTPCNsigma; - // TOF veto against pi/K/p/e - const bool passTOFVeto = std::abs(track.tofNSigmaPi()) > cfgTofNsigma && - std::abs(track.tofNSigmaKa()) > cfgTofNsigma && - std::abs(track.tofNSigmaPr()) > cfgTofNsigma && - std::abs(track.tofNSigmaEl()) > cfgTofNsigma; + const bool isElectron = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma && std::abs(track.tofNSigmaEl()) < cfgTofNsigma; + const bool isNotElectron = !isElectron; + const bool isPion = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma && std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isNotPion = !isPion; + const bool isKaon = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma && std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isNotKaon = !isKaon; + const bool isProton = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma && std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + const bool isNotProton = !isProton; - const bool isTPCDeClean = isTPCDe && passTPCVeto; + const bool isTPCTOFDeClean = isTPCDe && isNotElectron && isNotPion && isNotKaon && isNotProton; if (isTrueDeuteron) { int motherPdg = 0; @@ -541,7 +535,7 @@ struct HfTaskDeuteronFromLb { // same final selection as in data const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || - (track.pt() >= ptThresholdforPID && isTPCDeClean && passTOFVeto && isTOFDe && track.hasTOF()); + (track.pt() >= ptThresholdforPID && isTPCTOFDeClean && isTOFDe && track.hasTOF()); if (passFinalSel) { if (motherPdg == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); From 36494ec0b3fe9489e67cdb7b324afc33b7ecd632 Mon Sep 17 00:00:00 2001 From: syano0822 <32352856+syano0822@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:53:37 +0900 Subject: [PATCH 343/648] [PWGLF] Add f0(980) scalar-product flow analysis (#18274) --- PWGLF/Tasks/Resonances/CMakeLists.txt | 5 + PWGLF/Tasks/Resonances/f0980FlowSP.cxx | 934 +++++++++++++++++++++++++ 2 files changed, 939 insertions(+) create mode 100644 PWGLF/Tasks/Resonances/f0980FlowSP.cxx diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 9c9fd4bf987..8978355db6f 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -99,6 +99,11 @@ o2physics_add_dpl_workflow(f0980pbpbanalysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(f0980-flow-sp + SOURCES f0980FlowSP.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambda1520spherocityanalysis SOURCES lambda1520SpherocityAnalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Resonances/f0980FlowSP.cxx b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx new file mode 100644 index 00000000000..b868aca99f1 --- /dev/null +++ b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx @@ -0,0 +1,934 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file f0980FlowSP.cxx +/// \brief pi+pi- invariant-mass analysis and f0(980) v2 with scalar-product and event-plane methods + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +namespace +{ +using P4 = ROOT::Math::PxPyPzMVector; +constexpr float InvalidQ = -900.f; +constexpr int ProtonMassNumber = 1; +constexpr int OxygenMassNumber = 16; +} // namespace + +struct F0980FlowSP { + SliceCache cache; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + + enum class CollisionSystem : int { + PP = 0, + OO = 1, + PO = 2, + Unsupported = 3 + }; + + enum class PionPidMode : int { + TPCWithTOFWhenAvailable = 0, + TPCOnly = 1, + TPCTOFRequired = 2, + MomentumDependentTOF = 4 + }; + + enum class EventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Centrality, + QVectorCalibrated, + QVectorValid, + Count + }; + + enum class PPEventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Inelastic, + QVectorValid, + Count + }; + + enum class QVectorStatus { + IndexUnavailable, + Uncalibrated, + Invalid, + Valid + }; + + struct QVectorData { + double qAx = 0.; + double qAy = 0.; + double qBx = 0.; + double qBy = 0.; + double qCx = 0.; + double qCy = 0.; + double qAMagnitude = 0.; + double qBMagnitude = 0.; + double qCMagnitude = 0.; + + bool hasValidEventPlanes() const + { + return qAMagnitude > 0. && qBMagnitude > 0. && qCMagnitude > 0.; + } + }; + + int cachedRunNumber = -1; + CollisionSystem cachedCollisionSystem = CollisionSystem::Unsupported; + + // Pair and daughter selection. Defaults follow rho770analysis.cxx. + Configurable cfgMinPt{"cfgMinPt", 0.15f, "Minimum daughter transverse momentum"}; + Configurable cfgMaxEta{"cfgMaxEta", 0.8f, "Maximum daughter pseudorapidity"}; + Configurable cfgMinRap{"cfgMinRap", -0.5f, "Minimum pair rapidity"}; + Configurable cfgMaxRap{"cfgMaxRap", 0.5f, "Maximum pair rapidity"}; + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Require primary track"}; + Configurable cfgGlobalWoDCA{"cfgGlobalWoDCA", true, "Require global track without DCA cuts"}; + Configurable cfgPVContributor{"cfgPVContributor", true, "Require PV contributor"}; + Configurable cfgMaxDCAxy{"cfgMaxDCAxy", 0.1f, "Maximum |DCAxy| (cm)"}; + Configurable cfgMaxDCAz{"cfgMaxDCAz", 0.1f, "Maximum |DCAz| (cm)"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable cfgMinTPCRows{"cfgMinTPCRows", 70, "Minimum TPC crossed rows"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgRequireNoTimeFrameBorder{"cfgRequireNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgRequireNoITSROFrameBorder{"cfgRequireNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRequireITSTPCVertex{"cfgRequireITSTPCVertex", true, "Require kIsVertexITSTPC"}; + Configurable cfgCollisionSystem{"cfgCollisionSystem", -1, "Collision system: -1 auto from GRPLHCIF, 0 pp, 1 OO, 2 pO"}; + + Configurable cfgPIDMode{"cfgPIDMode", 0, "Pion PID: 0 TPC plus TOF when available, 1 TPC only, 2 require both TPC and TOF, 4 TPC-only below momentum threshold and require TOF above it"}; + Configurable cfgTPCNSigmaPi{"cfgTPCNSigmaPi", 3.f, "TPC pion n-sigma with TOF"}; + Configurable cfgTPCNSigmaPiNoTOF{"cfgTPCNSigmaPiNoTOF", 3.f, "TPC pion n-sigma without TOF"}; + Configurable cfgTOFNSigmaPi{"cfgTOFNSigmaPi", 3.f, "TOF pion n-sigma"}; + Configurable cfgTOFRequiredAboveP{"cfgTOFRequiredAboveP", 0.6f, "For PID mode 4, require TOF at and above this total momentum (GeV/c)"}; + // The vector tables contain one entry per harmonic requested from qVectorsTable. + Configurable cfgHarmonicIndex{"cfgHarmonicIndex", 0, "Index of n=2 in Q-vector vector columns"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum FT0C centrality for OO"}; + Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum FT0C centrality for OO"}; + + ConfigurableAxis axisMass{"axisMass", {400, 0.2, 2.2}, "m_{pi pi} (GeV/c^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT (GeV/c)"}; + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 100.}, "FT0C centrality (%)"}; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0., 1., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., 105.}, "N_{PV contributors}"}; + ConfigurableAxis axisRun{"axisRun", {2001, 563999.5, 566000.5}, "run number"}; + + using PPCollisions = soa::Join; + using OOCollisions = soa::Join; + using POCollisions = OOCollisions; + using TracksTPC = soa::Join; + using TracksTPCTOF = soa::Join; + + template + CollisionSystem collisionSystem(Collision const& collision) + { + if (cfgCollisionSystem.value >= static_cast(CollisionSystem::PP) && cfgCollisionSystem.value <= static_cast(CollisionSystem::PO)) { + return static_cast(cfgCollisionSystem.value); + } + + const auto bc = collision.template bc_as(); + if (bc.runNumber() == cachedRunNumber) { + return cachedCollisionSystem; + } + + auto* grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", bc.timestamp()); + if (grplhcif == nullptr) { + LOGF(fatal, "Cannot load GLO/Config/GRPLHCIF for run %d and timestamp %" PRIu64, bc.runNumber(), bc.timestamp()); + return CollisionSystem::Unsupported; + } + + const int beamAA = grplhcif->getBeamA(o2::constants::lhc::BeamA); + const int beamCA = grplhcif->getBeamA(o2::constants::lhc::BeamC); + if (beamAA == ProtonMassNumber && beamCA == ProtonMassNumber) { + cachedCollisionSystem = CollisionSystem::PP; + } else if (beamAA == OxygenMassNumber && beamCA == OxygenMassNumber) { + cachedCollisionSystem = CollisionSystem::OO; + } else if ((beamAA == ProtonMassNumber && beamCA == OxygenMassNumber) || + (beamAA == OxygenMassNumber && beamCA == ProtonMassNumber)) { + cachedCollisionSystem = CollisionSystem::PO; + } else { + cachedCollisionSystem = CollisionSystem::Unsupported; + } + cachedRunNumber = bc.runNumber(); + LOGF(info, "Automatically identified collision system for run %d: beam A values %d and %d, mode %d", cachedRunNumber, beamAA, beamCA, static_cast(cachedCollisionSystem)); + return cachedCollisionSystem; + } + + template + QVectorStatus getQVectorData(Collision const& collision, QVectorData& qVector) const + { + const size_t harmonicIndex = cfgHarmonicIndex.value; + if (harmonicIndex >= collision.qvecFT0CReVec().size() || + harmonicIndex >= collision.qvecFT0CImVec().size() || + harmonicIndex >= collision.qvecFT0AReVec().size() || + harmonicIndex >= collision.qvecFT0AImVec().size() || + harmonicIndex >= collision.qvecTPCposReVec().size() || + harmonicIndex >= collision.qvecTPCposImVec().size()) { + return QVectorStatus::IndexUnavailable; + } + if (!collision.isCalibrated()) { + return QVectorStatus::Uncalibrated; + } + + qVector.qAx = collision.qvecFT0CReVec()[harmonicIndex]; + qVector.qAy = collision.qvecFT0CImVec()[harmonicIndex]; + qVector.qBx = collision.qvecFT0AReVec()[harmonicIndex]; + qVector.qBy = collision.qvecFT0AImVec()[harmonicIndex]; + qVector.qCx = collision.qvecTPCposReVec()[harmonicIndex]; + qVector.qCy = collision.qvecTPCposImVec()[harmonicIndex]; + if (qVector.qAx < InvalidQ || qVector.qAy < InvalidQ || + qVector.qBx < InvalidQ || qVector.qBy < InvalidQ || + qVector.qCx < InvalidQ || qVector.qCy < InvalidQ) { + return QVectorStatus::Invalid; + } + + qVector.qAMagnitude = std::hypot(qVector.qAx, qVector.qAy); + qVector.qBMagnitude = std::hypot(qVector.qBx, qVector.qBy); + qVector.qCMagnitude = std::hypot(qVector.qCx, qVector.qCy); + return QVectorStatus::Valid; + } + + void init(InitContext const&) + { + // Counts and weighted SP numerators have identical axes. Their ratio is + // in each (mass, pT, centrality) bin. + registry.add("PP/hMassUS", "pp unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSpp", "pp ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSmm", "pp --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/pQAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQABIm", "Im(qFT0C qFT0A*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQACIm", "Im(qFT0C qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBCIm", "Im(qFT0A qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Event/hSelection", "pp event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(PPEventSelectionStep::Count), -0.5, static_cast(PPEventSelectionStep::Count) - 0.5}}); + registry.add("PP/QA/Event/hMultiplicityBefore", "pp events before selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hMultiplicityAfter", "pp events after selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hVertexZBefore", "pp events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Event/hVertexZAfter", "pp events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});multiplicity;q_{A}#upointq_{B}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});multiplicity;q_{A}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});multiplicity;q_{B}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/pQxFT0CVsMult", "mean corrected q_{x,FT0C};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0CVsMult", "mean corrected q_{y,FT0C};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0AVsMult", "mean corrected q_{x,FT0A};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0AVsMult", "mean corrected q_{y,FT0A};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxTPCposVsMult", "mean corrected q_{x,TPCpos};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyTPCposVsMult", "mean corrected q_{y,TPCpos};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + registry.add("OO/hMassUS", "OO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSpp", "OO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSmm", "OO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + + registry.add("OO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("PO/hMassUS", "pO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSpp", "pO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSmm", "pO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("QA/Event/hSelection", "ion-collision event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(EventSelectionStep::Count), -0.5, static_cast(EventSelectionStep::Count) - 0.5}}); + registry.add("QA/Event/hCentrality", "selected ion collisions;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityBefore", "ion-collision events before selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityAfter", "ion-collision events after selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hVertexZBefore", "ion-collision events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("QA/Event/hVertexZAfter", "ion-collision events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + + registry.add("QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});FT0C centrality (%);q_{A}#upointq_{B}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});FT0C centrality (%);q_{A}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});FT0C centrality (%);q_{B}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/pQxFT0CVsCent", "mean corrected q_{x,FT0C};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0CVsCent", "mean corrected q_{y,FT0C};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0AVsCent", "mean corrected q_{x,FT0A};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0AVsCent", "mean corrected q_{y,FT0A};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxTPCposVsCent", "mean corrected q_{x,TPCpos};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyTPCposVsCent", "mean corrected q_{y,TPCpos};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + auto eventSelection = registry.get(HIST("QA/Event/hSelection")); + constexpr std::array eventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "centrality", "Q calibrated", "Q valid"}; + for (size_t i = 0; i < eventSelectionLabels.size(); ++i) { + eventSelection->GetXaxis()->SetBinLabel(i + 1, eventSelectionLabels[i]); + } + auto ppEventSelection = registry.get(HIST("PP/QA/Event/hSelection")); + constexpr std::array ppEventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "INEL > 0", "Q valid"}; + for (size_t i = 0; i < ppEventSelectionLabels.size(); ++i) { + ppEventSelection->GetXaxis()->SetBinLabel(i + 1, ppEventSelectionLabels[i]); + } + registry.add("QA/TrackSelected/hPt", "track-selected candidates;p_{T} (GeV/c);counts", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/TrackSelected/hEta", "track-selected candidates;#eta;counts", HistType::kTH1F, {{160, -0.8, 0.8}}); + registry.add("QA/TrackSelected/hDCAxy", "track-selected candidates;DCA_{xy} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hDCAz", "track-selected candidates;DCA_{z} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hTPCCrossedRows", "track-selected candidates;TPC crossed rows;counts", HistType::kTH1F, {{165, -0.5, 164.5}}); + registry.add("QA/TrackSelected/hTPCNSigmaPiVsP", "track-selected candidates before pion PID;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTPCSignalVsP", "track-selected candidates before pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/TrackSelected/hTOFNSigmaPiVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTOFBetaVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + registry.add("QA/SelectedPions/hTPCNSigmaPiVsP", "selected pion candidates;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTPCSignalVsP", "selected pion candidates after pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/SelectedPions/hTOFNSigmaPiVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTOFBetaVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + } + + template + bool selectTrack(T const& track) const + { + if (track.pt() < cfgMinPt.value || std::abs(track.eta()) > cfgMaxEta.value) { + return false; + } + if (std::abs(track.dcaXY()) >= cfgMaxDCAxy.value || std::abs(track.dcaZ()) >= cfgMaxDCAz.value) { + return false; + } + if (cfgPrimaryTrack.value && !track.isPrimaryTrack()) { + return false; + } + if (cfgGlobalWoDCA.value && !track.isGlobalTrackWoDCA()) { + return false; + } + if (cfgPVContributor.value && !track.isPVContributor()) { + return false; + } + return track.tpcChi2NCl() < cfgMaxTPCChi2NCl.value && + track.itsChi2NCl() < cfgMaxITSChi2NCl.value && + track.tpcNClsCrossedRows() > cfgMinTPCRows.value && + track.itsNCls() > cfgMinITSClusters.value; + } + + template + bool selectEvent(C const& collision) const + { + return (!cfgRequireSel8.value || collision.sel8()) && + (!cfgRequireNoSameBunchPileup.value || collision.selection_bit(aod::evsel::kNoSameBunchPileup)) && + (!cfgRequireNoTimeFrameBorder.value || collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) && + (!cfgRequireNoITSROFrameBorder.value || collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) && + (!cfgRequireITSTPCVertex.value || collision.selection_bit(aod::evsel::kIsVertexITSTPC)); + } + + template + bool selectOOEventWithQA(C const& collision, float centrality) + { + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::All)); + registry.fill(HIST("QA/Event/hCentralityBefore"), centrality); + registry.fill(HIST("QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::ITSTPCVertex)); + if (centrality < cfgCentMin.value || centrality >= cfgCentMax.value) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Centrality)); + registry.fill(HIST("QA/Event/hCentrality"), centrality); + registry.fill(HIST("QA/Event/hCentralityAfter"), centrality); + registry.fill(HIST("QA/Event/hVertexZAfter"), collision.posZ()); + return true; + } + + void fillQVectorQA(float centrality, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0C"), centrality, std::hypot(qAx, qAy)); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0A"), centrality, std::hypot(qBx, qBy)); + registry.fill(HIST("QA/Qvector/hMagnitudeTPCpos"), centrality, std::hypot(qCx, qCy)); + registry.fill(HIST("QA/Qvector/hPsi2FT0C"), centrality, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("QA/Qvector/hPsi2FT0A"), centrality, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("QA/Qvector/hPsi2TPCpos"), centrality, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("QA/Qvector/hDotAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("QA/Qvector/hDotAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("QA/Qvector/hDotBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsCent"), centrality, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsCent"), centrality, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsCent"), centrality, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsCent"), centrality, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsCent"), centrality, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsCent"), centrality, qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + void fillPPQVectorQA(float multiplicity, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("PP/QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("PP/QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("PP/QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0C"), multiplicity, std::hypot(qAx, qAy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0A"), multiplicity, std::hypot(qBx, qBy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeTPCpos"), multiplicity, std::hypot(qCx, qCy)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0C"), multiplicity, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0A"), multiplicity, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2TPCpos"), multiplicity, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("PP/QA/Qvector/hDotAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/QA/Qvector/hDotAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/QA/Qvector/hDotBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsMult"), multiplicity, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsMult"), multiplicity, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsMult"), multiplicity, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsMult"), multiplicity, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsMult"), multiplicity, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsMult"), multiplicity, qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + template + bool selectPion(T const& track) const + { + const float tpc = std::abs(track.tpcNSigmaPi()); + const float tof = std::abs(track.tofNSigmaPi()); + switch (static_cast(cfgPIDMode.value)) { + case PionPidMode::TPCWithTOFWhenAvailable: + if (track.hasTOF()) { + return tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + } + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCOnly: + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCTOFRequired: + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + case PionPidMode::MomentumDependentTOF: + if (track.p() < cfgTOFRequiredAboveP.value) { + return tpc < cfgTPCNSigmaPiNoTOF.value; + } + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + default: + return false; + } + } + + template + bool selectPionTPCOnly(T const& track) const + { + return std::abs(track.tpcNSigmaPi()) < cfgTPCNSigmaPiNoTOF.value; + } + + template + void loopPairsSelected(Tracks const& tracks, TrackSelect trackSelect, PionSelect pionSelect, FillPair fillPair) + { + struct SelectedPion { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + int8_t sign = 0; + }; + + // Apply daughter-quality and PID selections once per track before building + // pairs. In OO events this avoids constructing all unfiltered O(N^2) + // combinations and repeatedly evaluating the same track cuts. + std::vector pions; + pions.reserve(tracks.size()); + for (auto const& track : tracks) { + if (!trackSelect(track)) { + continue; + } + registry.fill(HIST("QA/TrackSelected/hPt"), track.pt()); + registry.fill(HIST("QA/TrackSelected/hEta"), track.eta()); + registry.fill(HIST("QA/TrackSelected/hDCAxy"), track.dcaXY()); + registry.fill(HIST("QA/TrackSelected/hDCAz"), track.dcaZ()); + registry.fill(HIST("QA/TrackSelected/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + registry.fill(HIST("QA/TrackSelected/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/TrackSelected/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTOFBetaVsP"), track.p(), track.beta()); + } + } + if (!pionSelect(track)) { + continue; + } + registry.fill(HIST("QA/SelectedPions/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/SelectedPions/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTOFBetaVsP"), track.p(), track.beta()); + } + } + pions.push_back({track.px(), track.py(), track.pz(), static_cast(track.sign())}); + } + + const double mpi = o2::constants::physics::MassPionCharged; + for (size_t i = 0; i < pions.size(); ++i) { + for (size_t j = i + 1; j < pions.size(); ++j) { + auto const& track1 = pions[i]; + auto const& track2 = pions[j]; + P4 pion1(track1.px, track1.py, track1.pz, mpi); + P4 pion2(track2.px, track2.py, track2.pz, mpi); + auto pair = pion1 + pion2; + if (pair.Rapidity() < cfgMinRap.value || pair.Rapidity() > cfgMaxRap.value) { + continue; + } + fillPair(track1.sign, track2.sign, pair); + } + } + } + + template + void loopPairs(Tracks const& tracks, FillPair&& fillPair) + { + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPion(track); }, std::forward(fillPair)); + } + + void processPP(PPCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PP) { + return; + } + const float multiplicity = collision.numContrib(); + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::All)); + registry.fill(HIST("PP/QA/Event/hMultiplicityBefore"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::ITSTPCVertex)); + if (collision.numContrib() < 1) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Inelastic)); + registry.fill(HIST("PP/QA/Event/hMultiplicityAfter"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZAfter"), collision.posZ()); + + QVectorData qVector; + if (getQVectorData(collision, qVector) != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillPPQVectorQA(multiplicity, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + + registry.fill(HIST("PP/pQAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/pQAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/pQBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/pQABIm"), multiplicity, qAy * qBx - qAx * qBy); + registry.fill(HIST("PP/pQACIm"), multiplicity, qAy * qCx - qAx * qCy); + registry.fill(HIST("PP/pQBCIm"), multiplicity, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PP/pCosPsiAB"), multiplicity, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PP/pCosPsiAC"), multiplicity, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PP/pCosPsiBC"), multiplicity, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PP/hMassUS"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumUS"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumUS"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PP/hMassLSpp"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSpp"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSpp"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PP/hMassLSmm"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSmm"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSmm"), pair.M(), pair.Pt(), multiplicity, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPP, "Process pp invariant-mass scalar-product and event-plane analyses", true); + + void processOO(OOCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processOO, "Process OO invariant-mass scalar-product and event-plane analyses", true); + + void processPO(POCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("PO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("PO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("PO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("PO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("PO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("PO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPO, "Process pO invariant-mass scalar-product and event-plane analyses", true); + + void processOOTPCOnly(OOCollisions::iterator const& collision, TracksTPC const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPionTPCOnly(track); }, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } }); + } + PROCESS_SWITCH(F0980FlowSP, processOOTPCOnly, "Process OO with TPC-only pion PID", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From d5b43761768fcf09b2386b5d8ddeec898abe1744 Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Mon, 19 Oct 2026 10:54:23 +0200 Subject: [PATCH 344/648] [PWGLF] Enabling Kstar proxy, MC and reducing memory consumption (#18346) Co-authored-by: Sushanta Tripathy --- PWGLF/Tasks/Resonances/f0phiproxy.cxx | 750 +++++++++++++++++++------- 1 file changed, 553 insertions(+), 197 deletions(-) diff --git a/PWGLF/Tasks/Resonances/f0phiproxy.cxx b/PWGLF/Tasks/Resonances/f0phiproxy.cxx index b4f97e7180f..b83cdee0f7c 100644 --- a/PWGLF/Tasks/Resonances/f0phiproxy.cxx +++ b/PWGLF/Tasks/Resonances/f0phiproxy.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file f0phiproxy.cxx -/// \brief task for dipion f0 candidates with a half-momentum kaon proxy and bachelor kaon. +/// \brief Dipion-based half-momentum kaon proxies with kaon/phi and pion/K*0 bachelors, in data and MC. /// \author Sushanta Tripathy #include "Common/DataModel/EventSelection.h" @@ -22,23 +22,34 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include #include +#include #include +#include #include #include @@ -47,288 +58,633 @@ using namespace o2::framework; struct F0phiproxy { using Collisions = soa::Join; - using Tracks = - soa::Join; + using Tracks = soa::Join; + using CollisionsMC = soa::Join; + using TracksMC = soa::Join; using FourVector = ROOT::Math::PxPyPzMVector; Preslice perCollision = aod::track::collisionId; - HistogramRegistry histos{ - "histos", - {}, - OutputObjHandlingPolicy::AnalysisObject}; - Configurable vertexMax{"vertexMax", 10.f, - "Maximum absolute vertex z (cm)"}; + Preslice perCollisionMC = aod::track::collisionId; + Service pdgDB{}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable vertexMax{"vertexMax", 10.f, "Maximum absolute vertex z (cm)"}; Configurable requireSel8{"requireSel8", true, "Require sel8"}; - Configurable requireINELgt0{"requireINELgt0", true, - "Require at least one PV track in |eta|<1"}; - Configurable trackPtMin{"trackPtMin", 0.15f, - "Minimum track pT (GeV/c)"}; + Configurable requireINELgt0{"requireINELgt0", true, "Require at least one PV track in |eta|<1; MC gen uses charged physical primaries"}; + Configurable trackPtMin{"trackPtMin", 0.15f, "Minimum track pT (GeV/c)"}; Configurable trackEtaMax{"trackEtaMax", 0.8f, "Maximum track |eta|"}; - Configurable requireGlobalTrack{"requireGlobalTrack", true, - "Require global track selection"}; - Configurable requirePVContributor{"requirePVContributor", true, - "Require PV contributor"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track selection"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require PV contributor"}; Configurable pionTPC{"pionTPC", 3.f, "Pion TPC nSigma cut"}; Configurable pionTOF{"pionTOF", 3.f, "Pion TOF veto when matched"}; Configurable kaonTPC{"kaonTPC", 3.f, "Kaon TPC nSigma cut"}; Configurable kaonTOF{"kaonTOF", 3.f, "Kaon TOF veto when matched"}; Configurable requireTOF{"requireTOF", false, "Require TOF for both species instead of veto-only"}; Configurable dipionYMax{"dipionYMax", 0.5f, "Maximum dipion |y|"}; - Configurable proxyYMax{"proxyYMax", 0.5f, - "Maximum proxy+bachelor |y|"}; - Configurable dipionMassMin{"dipionMassMin", 0.6f, - "Lower dipion mass for proxy construction"}; - Configurable dipionMassMax{"dipionMassMax", 1.4f, - "Upper dipion mass for proxy construction"}; - Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables mixing"}; + Configurable proxyYMax{"proxyYMax", 0.5f, "Maximum proxy+bachelor and standard-control |y|"}; + Configurable dipionMassMin{"dipionMassMin", 0.6f, "Lower dipion mass for proxy construction"}; + Configurable dipionMassMax{"dipionMassMax", 1.4f, "Upper dipion mass for proxy construction"}; + Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables ME booking and mixing"}; Configurable mixZWidth{"mixZWidth", 2.f, "Vertex bin width (cm)"}; - Configurable mixMultWidth{"mixMultWidth", 20.f, - "PV multiplicity bin width"}; + Configurable mixMultWidth{"mixMultWidth", 20.f, "PV multiplicity bin width"}; + + Configurable enablePhiProxy{"enablePhiProxy", true, "Dipion/2 as on-shell K plus bachelor K"}; + Configurable enableKstarProxy{"enableKstarProxy", false, "Dipion/2 as on-shell K plus bachelor pion; use the dedicated Kstar configuration"}; + Configurable enablePhiControls{"enablePhiControls", true, "Standard KK SE/ME OS/LS spectra"}; + Configurable enableKstarControls{"enableKstarControls", false, "Standard K pi SE/ME OS/LS spectra"}; + Configurable enableCompactProxy{"enableCompactProxy", true, "Proxy maps retaining mass, pT, dipion mass, grouped multiplicity, charges and TOF"}; + Configurable enableFullProxySparse{"enableFullProxySparse", false, "Diagnostic 7D maps including multiplicity, bachelor charge and TOF; expensive to merge"}; + Configurable enablePidQA{"enablePidQA", false, "Book/fill the four PID QA histograms"}; + Configurable multiplicityBins{"multiplicityBins", 100, "Multiplicity bins in dipion/control and full diagnostic maps; identical settings required for merging"}; + Configurable proxyDipionMassBins{"proxyDipionMassBins", 300, "Dipion mass bins in compact proxy maps; default preserves original 5 MeV resolution"}; + ConfigurableAxis proxyMultiplicityAxis{"proxyMultiplicityAxis", {0., 0., 20., 50., 200., 1000000.}, "Compact proxy NPV intervals; first zero selects variable-width bins"}; + ConfigurableAxis proxyPtAxis{"proxyPtAxis", {0., 0., 0.3, 1., 2., 3., 4., 6., 10.}, "Compact proxy pT intervals covering both the data and Kstar-study selections"}; + Configurable kstarMassBins{"kstarMassBins", 200, "K pi and Kstar-proxy mass bins over 0.6-1.4 GeV; 200 gives the study's 4 MeV bins"}; + Configurable mcPrimaryOnly{"mcPrimaryOnly", true, "Generated daughters must be physical primaries; strong-decay daughters are included"}; + Configurable mcF0PdgCode{"mcF0PdgCode", 0, "0 resolves f0_980 from the O2 PDG service; override only for a verified generator convention"}; + + static constexpr size_t Phi = 0; + static constexpr size_t Kstar = 1; + static constexpr size_t NChannels = 2; + static constexpr size_t NCharges = 3; + static constexpr size_t NMixClasses = 2; + static constexpr size_t NTruthClasses = 4; + static constexpr double ProxyMomentumScale = 0.5; + static constexpr int PdgPion = static_cast(PDG_t::kPiPlus); + static constexpr int PdgKaon = static_cast(PDG_t::kKPlus); + static constexpr int PdgPhi = static_cast(constants::physics::Pdg::kPhi); + static constexpr int PdgKstar = static_cast(constants::physics::Pdg::kK0Star892); + static constexpr size_t TwoBodyDaughters = 2; + static constexpr int IonCodeThreshold = 1000000000; + static constexpr int IonZDivisor = 10000; + static constexpr int IonZModulo = 1000; + int resolvedF0Code = 0; + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + double px = 0., py = 0., pz = 0.; + bool primary = false; + std::vector> parents{}; + }; struct TrackCandidate { - int64_t id; - int sign; - double px, py, pz; - bool hasTOF; - [[nodiscard]] FourVector vector(double mass) const - { - return {px, py, pz, mass}; - } + int64_t id = -1; + int sign = 0; + double px = 0., py = 0., pz = 0.; + bool hasTOF = false; + TruthInfo truth{}; + [[nodiscard]] FourVector vector(double mass) const { return {px, py, pz, mass}; } }; struct Event { - std::vector pions, kaons; + std::vector pions{}, kaons{}; }; + std::array, NMixClasses> dipionSpectra{}; + std::array, NMixClasses>, NChannels> compactProxy{}, fullProxy{}; + std::array, NCharges>, NMixClasses>, NChannels> controls{}; + std::array, NTruthClasses>, NChannels> recoComponents{}; + std::array, NChannels> recoTrueParent{}, recoPtResponse{}, proxyPtResponse{}; + std::shared_ptr recoTrueF0{}; + enum GenSpectrum : size_t { GenDipion, + GenF0, + GenPhi, + GenPhiTrue, + GenKstar, + GenKstarTrue, + GenProxyPhi, + GenProxyPhiF0, + GenProxyKstar, + GenProxyKstarF0, + NGenSpectra }; + std::array, NGenSpectra> generated{}; + + bool mixingEnabled() const { return mixDepth.value > 0; } + bool channelEnabled(size_t channel) const { return channel == Phi ? enablePhiProxy.value : enableKstarProxy.value; } + bool controlEnabled(size_t channel) const { return channel == Phi ? enablePhiControls.value : enableKstarControls.value; } + static double bachelorMass(size_t channel) { return channel == Phi ? constants::physics::MassKaonCharged : constants::physics::MassPionCharged; } + static int bachelorPdg(size_t channel) { return channel == Phi ? PdgKaon : PdgPion; } + void init(InitContext const&) { - if (mixDepth < 0 || mixZWidth <= 0.f || mixMultWidth <= 0.f || - vertexMax <= 0.f || pionTPC <= 0.f || pionTOF <= 0.f || - kaonTPC <= 0.f || kaonTOF <= 0.f || dipionMassMin >= dipionMassMax) { + if (doprocess && doprocessMCReco) { + throw std::runtime_error("Enable either process (data) or processMCReco, never both"); + } + if (mixDepth.value < 0 || mixZWidth.value <= 0.f || mixMultWidth.value <= 0.f || + vertexMax.value <= 0.f || trackPtMin.value < 0.f || trackEtaMax.value <= 0.f || + pionTPC.value <= 0.f || pionTOF.value <= 0.f || kaonTPC.value <= 0.f || kaonTOF.value <= 0.f || + dipionMassMin.value >= dipionMassMax.value || dipionYMax.value <= 0.f || proxyYMax.value <= 0.f || + multiplicityBins.value <= 0 || proxyDipionMassBins.value <= 0 || kstarMassBins.value <= 0 || mcF0PdgCode.value < 0) { throw std::runtime_error("Invalid F0PhiProxy configuration"); } - AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; - AxisSpec massProxy{600, 0.9, 1.5, "m_{K,proxy K} (GeV/c^{2})"}; - AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; - AxisSpec mult{100, 0., 200., "N_{PV}(|#eta|<1)"}; - AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; - AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; - AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; - histos.add("hEventSelection", "Received; selected", HistType::kTH1D, - {{2, 0.5, 2.5}}); - histos.add("hMixPartners", "Mixing partners;N", HistType::kTH1D, - {{101, -0.5, 100.5}}); - histos.add("hPionTPC", "Quality-selected tracks;pT;nSigmaTPC pion", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hPionTOF", "TOF-matched quality tracks;pT;nSigmaTOF pion", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTPC", "Quality-selected tracks;pT;nSigmaTPC kaon", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTOF", "TOF-matched quality tracks;pT;nSigmaTOF kaon", - HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hDipionSE", "Dipion SE", HistType::kTHnSparseF, - {massPi, pt, mult, charge}); - histos.add("hDipionME", "Dipion ME (raw)", HistType::kTHnSparseF, - {massPi, pt, mult, charge}); - // Keep measured dipion mass: no f0 identification by a mass window alone. - histos.add("hProxySE", "Dipion plus bachelor proxy SE", - HistType::kTHnSparseF, - {massProxy, pt, massPi, mult, charge, bachelorCharge, tof}); - histos.add("hProxyME", "Intact same-event dipion plus mixed bachelor (raw)", - HistType::kTHnSparseF, - {massProxy, pt, massPi, mult, charge, bachelorCharge, tof}); - AxisSpec massKK{600, 0.9, 1.5, "m_{KK} (GeV/c^{2})"}; - histos.add("hPhiKK", "Unlike-sign kaon control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKLikePP", "K+K+ control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKLikeMM", "K-K- control SE", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixed", "Unlike-sign kaon control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixedLikePP", "K+K+ control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); - histos.add("hPhiKKMixedLikeMM", "K-K- control ME (raw)", HistType::kTHnSparseF, - {massKK, pt, mult}); + if (doprocess || doprocessMCReco) { + if ((enablePhiProxy.value || enableKstarProxy.value) && !enableCompactProxy.value && !enableFullProxySparse.value) { + throw std::runtime_error("Enable compact or full proxy output when a proxy channel is enabled"); + } + bookReco(); + } + if (doprocessMCReco || doprocessMCGen) { + resolvedF0Code = mcF0PdgCode.value; + if (resolvedF0Code == 0) { + auto const* particle = pdgDB->GetParticle("f0_980"); + if (!particle) { + throw std::runtime_error("f0_980 not found in O2 PDG service; configure mcF0PdgCode explicitly"); + } + resolvedF0Code = particle->PdgCode(); + } + bookMC(); + } } - bool passPID(float tpc, float tof, bool matched, float tpcCut, - float tofCut) const + void bookReco() { - return std::isfinite(tpc) && std::abs(tpc) < tpcCut && - (!requireTOF.value || matched) && - (!matched || (std::isfinite(tof) && std::abs(tof) < tofCut)); + const AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec compactMassPi{proxyDipionMassBins.value, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec mult{multiplicityBins.value, 0., 200., "N_{PV}(|#eta|<1)"}; + const AxisSpec compactMult{proxyMultiplicityAxis, "N_{PV}(|#eta|<1), grouped"}; + const AxisSpec compactPt{proxyPtAxis, "proxy pair p_{T} (GeV/c), grouped"}; + const AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; + const AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; + const AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; + histos.add("hEventSelection", "Received; selected", HistType::kTH1D, {{2, 0.5, 2.5}}); + if (mixingEnabled()) { + histos.add("hMixPartners", "Mixing partners;N", HistType::kTH1D, {{101, -0.5, 100.5}}); + } + if (enablePidQA.value) { + histos.add("hPionTPC", "Quality-selected tracks;pT;nSigmaTPC pion", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hPionTOF", "TOF-matched quality tracks;pT;nSigmaTOF pion", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hKaonTPC", "Quality-selected tracks;pT;nSigmaTPC kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hKaonTOF", "TOF-matched quality tracks;pT;nSigmaTOF kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); + } + const std::array dipionNames{"hDipionSE", "hDipionME"}; + for (size_t mix = 0; mix < dipionNames.size(); ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + dipionSpectra[mix] = histos.add(dipionNames[mix], "Dipion SE or raw ME", HistType::kTHnSparseF, {massPi, pt, mult, charge}); + } + const std::array, NChannels> compactNames{{{"hProxyCompactSE", "hProxyCompactME"}, {"hKstarProxyCompactSE", "hKstarProxyCompactME"}}}; + const std::array, NChannels> fullNames{{{"hProxySE", "hProxyME"}, {"hKstarProxySE", "hKstarProxyME"}}}; + const std::array, NMixClasses>, NChannels> controlNames{{{{{"hPhiKK", "hPhiKKLikePP", "hPhiKKLikeMM"}, {"hPhiKKMixed", "hPhiKKMixedLikePP", "hPhiKKMixedLikeMM"}}}, + {{{"hKstarKPi", "hKstarKPiLikePP", "hKstarKPiLikeMM"}, {"hKstarKPiMixed", "hKstarKPiMixedLikePP", "hKstarKPiMixedLikeMM"}}}}}; + for (size_t channel = 0; channel < NChannels; ++channel) { + const AxisSpec mass = channel == Phi ? AxisSpec{600, 0.9, 1.5, "m_{K,proxy K} (GeV/c^{2})"} : AxisSpec{kstarMassBins.value, 0.6, 1.4, "m_{K,proxy #pi} (GeV/c^{2})"}; + for (size_t mix = 0; mix < NMixClasses; ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + if (channelEnabled(channel)) { + // The measured dipion mass is retained; a mass window is not an f0 tag. + if (enableCompactProxy.value) { + compactProxy[channel][mix] = histos.add(compactNames[channel][mix], "Grouped-pT/multiplicity proxy; no constituent charge is measured", HistType::kTHnSparseF, {mass, compactPt, compactMassPi, compactMult, charge, bachelorCharge, tof}); + } + if (enableFullProxySparse.value) { + fullProxy[channel][mix] = histos.add(fullNames[channel][mix], "Diagnostic full proxy", HistType::kTHnSparseF, {mass, pt, massPi, mult, charge, bachelorCharge, tof}); + } + } + if (controlEnabled(channel)) { + for (size_t sign = 0; sign < NCharges; ++sign) { + controls[channel][mix][sign] = histos.add(controlNames[channel][mix][sign], "Standard control SE or raw ME", HistType::kTHnSparseF, {mass, pt, mult}); + } + } + } + } + } + + void bookMC() + { + const AxisSpec pt{100, 0., 10., "pair pT (GeV/c)"}; + const AxisSpec pionMass{300, 0.2, 1.7, "dipion mass (GeV/c2)"}; + const std::array mass{AxisSpec{600, 0.9, 1.5, "KK mass (GeV/c2)"}, AxisSpec{kstarMassBins.value, 0.6, 1.4, "K pi mass (GeV/c2)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;1=all,2=vertex,3=truth INEL>0", HistType::kTH1D, {{3, 0.5, 3.5}}); + const std::array names{"hMCGenDipion", "hMCGenTrueF0", "hMCGenPhiKK", "hMCGenTruePhi", "hMCGenKstarKPi", "hMCGenTrueKstar", "hMCGenProxyPhi", "hMCGenProxyPhiTrueF0", "hMCGenProxyKstar", "hMCGenProxyKstarTrueF0"}; + for (size_t i = 0; i < names.size(); ++i) { + const AxisSpec axis = i <= GenF0 ? pionMass : (i == GenKstar || i == GenKstarTrue || i >= GenProxyKstar ? mass[Kstar] : mass[Phi]); + generated[i] = histos.add(names[i], "Generated unweighted fiducial spectrum", HistType::kTH2D, {axis, pt}, true); + } + } + if (doprocessMCReco) { + recoTrueF0 = histos.add("hMCRecoTrueF0", "Selected common-parent f0 -> pi pi", HistType::kTH2D, {pionMass, pt}, true); + const std::array channelNames{"Phi", "Kstar"}; + const std::array classNames{"TrueF0", "Combinatorial", "MisID", "Unmatched"}; + for (size_t channel = 0; channel < NChannels; ++channel) { + if (channelEnabled(channel)) { + for (size_t category = 0; category < classNames.size(); ++category) { + const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + classNames[category]; + recoComponents[channel][category] = histos.add(name.c_str(), "Exclusive proxy truth component; not a coalescence ancestry tag", HistType::kTH2D, {mass[channel], pt}, true); + } + const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + "PtResponse"; + proxyPtResponse[channel] = histos.add(name.c_str(), "Primary true f0 daughters and true bachelor;generated proxy pair pT;reconstructed proxy pair pT", HistType::kTH2D, {pt, pt}, true); + } + if (controlEnabled(channel)) { + const std::string truthName = std::string("hMCRecoTrue") + channelNames[channel]; + const std::string responseName = std::string("hMCReco") + channelNames[channel] + "PtResponse"; + recoTrueParent[channel] = histos.add(truthName.c_str(), "Selected exact common-parent standard control", HistType::kTH2D, {mass[channel], pt}, true); + recoPtResponse[channel] = histos.add(responseName.c_str(), "Selected standard control;generated pair pT;reconstructed pair pT", HistType::kTH2D, {pt, pt}, true); + } + } + } + } + + static void fillSparse(std::shared_ptr const& histogram, std::initializer_list coordinates) + { + if (histogram) { + histogram->Fill(coordinates.begin()); + } } - static int chargeCategory(int a, int b) + bool passPID(float tpc, float tof, bool matched, float tpcCut, float tofCut) const + { + return std::isfinite(tpc) && std::abs(tpc) < tpcCut && (!requireTOF.value || matched) && + (!matched || (std::isfinite(tof) && std::abs(tof) < tofCut)); + } + static int chargeCategory(int a, int b) { return a != b ? 0 : (a > 0 ? 1 : 2); } + static FourVector constituent(FourVector const& dipion) + { + return {ProxyMomentumScale * dipion.Px(), ProxyMomentumScale * dipion.Py(), ProxyMomentumScale * dipion.Pz(), constants::physics::MassKaonCharged}; + } + int chargeFromPdg(int code) const + { + const int absolute = std::abs(code); + if (absolute >= IonCodeThreshold) { + const int z = (absolute / IonZDivisor) % IonZModulo; + return code > 0 ? z : -z; + } + if (auto const* particle = pdgDB->GetParticle(code)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } + return 0; + } + template + TruthInfo particleTruth(TParticle const& particle) const + { + TruthInfo truth{.id = particle.globalIndex(), .collisionId = particle.mcCollisionId(), .pdg = particle.pdgCode(), .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .primary = particle.isPhysicalPrimary(), .parents = {}}; + for (auto const& mother : particle.template mothers_as()) { + const int code = std::abs(mother.pdgCode()); + if (mother.mcCollisionId() == truth.collisionId && (code == resolvedF0Code || code == PdgPhi || code == PdgKstar)) { + // The complementary-channel study uses exact two-body parent tags. + if (mother.template daughters_as().size() == TwoBodyDaughters) { + truth.parents.emplace_back(mother.globalIndex(), code); + } + } + } + return truth; + } + template + TruthInfo trackTruth(TTrack const& track, int collisionId) const { - return a != b ? 0 : (a > 0 ? 1 : 2); + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + return particle.mcCollisionId() == collisionId ? particleTruth(particle) : TruthInfo{}; + } + static bool commonParent(TruthInfo const& first, TruthInfo const& second, int parentCode) + { + if (first.id < 0 || second.id < 0 || first.id == second.id || first.collisionId != second.collisionId) { + return false; + } + return std::any_of(first.parents.begin(), first.parents.end(), [&second, parentCode](auto const& parent) { + return parent.second == parentCode && std::find(second.parents.begin(), second.parents.end(), parent) != second.parents.end(); + }); + } + static bool identified(TrackCandidate const& candidate, int positiveCode) + { + return candidate.truth.id >= 0 && candidate.truth.pdg == candidate.sign * positiveCode; + } + bool trueF0(TrackCandidate const& first, TrackCandidate const& second) const + { + return first.sign != second.sign && identified(first, PdgPion) && identified(second, PdgPion) && commonParent(first.truth, second.truth, resolvedF0Code); + } + static double truthPairPt(TruthInfo const& first, TruthInfo const& second) + { + return std::hypot(first.px + second.px, first.py + second.py); + } + size_t proxyTruthClass(TrackCandidate const& first, TrackCandidate const& second, TrackCandidate const& bachelor, size_t channel) const + { + if (first.truth.id < 0 || second.truth.id < 0 || bachelor.truth.id < 0 || + first.truth.collisionId != second.truth.collisionId || first.truth.collisionId != bachelor.truth.collisionId) { + return NTruthClasses - 1; + } + if (!identified(first, PdgPion) || !identified(second, PdgPion) || !identified(bachelor, bachelorPdg(channel))) { + return 2; + } + const bool distinctBachelor = bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id; + return distinctBachelor && trueF0(first, second) ? 0 : 1; } - void fillProxy(FourVector const& dipion, TrackCandidate const& first, - TrackCandidate const& second, - std::vector const& kaons, float mult, - bool mixed) + void fillProxy(FourVector const& dipion, TrackCandidate const& first, TrackCandidate const& second, + std::vector const& bachelors, float mult, bool mixed, size_t channel, bool mc = false) { - const FourVector constituent(dipion.Px() * 0.5, dipion.Py() * 0.5, - dipion.Pz() * 0.5, - constants::physics::MassKaonCharged); + if (!channelEnabled(channel)) { + return; + } + const FourVector proxy = constituent(dipion); const int category = chargeCategory(first.sign, second.sign); - for (auto const& kaon : kaons) { - if (!mixed && (kaon.id == first.id || kaon.id == second.id)) { + for (auto const& bachelor : bachelors) { + if (!mixed && (bachelor.id == first.id || bachelor.id == second.id)) { continue; } - const auto pair = - constituent + kaon.vector(constants::physics::MassKaonCharged); - if (std::abs(pair.Rapidity()) >= proxyYMax) { + const auto pair = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { continue; } - // The neutral dipion has no measured constituent-kaon charge: fill once - // per bachelor. - if (mixed) { - histos.fill(HIST("hProxyME"), pair.M(), pair.Pt(), dipion.M(), mult, - category, kaon.sign, kaon.hasTOF); - } else { - histos.fill(HIST("hProxySE"), pair.M(), pair.Pt(), dipion.M(), mult, - category, kaon.sign, kaon.hasTOF); + // Neutral dipion: one entry per bachelor. There is no measured proxy-kaon charge. + fillSparse(compactProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + fillSparse(fullProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + if (mc && !mixed) { + const size_t truthClass = proxyTruthClass(first, second, bachelor, channel); + recoComponents[channel][truthClass]->Fill(pair.M(), pair.Pt()); + if (truthClass == 0 && first.truth.primary && second.truth.primary && bachelor.truth.primary && + bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id) { + const double px = ProxyMomentumScale * (first.truth.px + second.truth.px) + bachelor.truth.px; + const double py = ProxyMomentumScale * (first.truth.py + second.truth.py) + bachelor.truth.py; + proxyPtResponse[channel]->Fill(std::hypot(px, py), pair.Pt()); + } } } } - void fillPhiPair(TrackCandidate const& first, TrackCandidate const& second, float mult, bool mixed) + void fillControlPair(TrackCandidate const& first, TrackCandidate const& second, float mult, bool mixed, size_t channel, bool mc = false) { - const auto pair = first.vector(constants::physics::MassKaonCharged) + - second.vector(constants::physics::MassKaonCharged); + if ((!mixed && first.id == second.id) || !controlEnabled(channel)) { + return; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); if (std::abs(pair.Rapidity()) >= proxyYMax.value) { return; } - if (mixed) { - if (first.sign != second.sign) { - histos.fill(HIST("hPhiKKMixed"), pair.M(), pair.Pt(), mult); - } else if (first.sign > 0) { - histos.fill(HIST("hPhiKKMixedLikePP"), pair.M(), pair.Pt(), mult); - } else { - histos.fill(HIST("hPhiKKMixedLikeMM"), pair.M(), pair.Pt(), mult); - } - } else { - if (first.sign != second.sign) { - histos.fill(HIST("hPhiKK"), pair.M(), pair.Pt(), mult); - } else if (first.sign > 0) { - histos.fill(HIST("hPhiKKLikePP"), pair.M(), pair.Pt(), mult); - } else { - histos.fill(HIST("hPhiKKLikeMM"), pair.M(), pair.Pt(), mult); - } + fillSparse(controls[channel][mixed][chargeCategory(first.sign, second.sign)], {pair.M(), pair.Pt(), mult}); + const int parent = channel == Phi ? PdgPhi : PdgKstar; + if (mc && !mixed && first.sign != second.sign && identified(first, PdgKaon) && identified(second, bachelorPdg(channel)) && commonParent(first.truth, second.truth, parent)) { + recoTrueParent[channel]->Fill(pair.M(), pair.Pt()); + recoPtResponse[channel]->Fill(truthPairPt(first.truth, second.truth), pair.Pt()); } } - - void fillPhiControls(Event const& event, std::deque const& pool, float mult) + void fillControls(Event const& event, std::deque const& pool, float mult, bool mc) { for (size_t i = 0; i < event.kaons.size(); ++i) { - // Each same-event unordered pair is counted once. - for (size_t j = i + 1; j < event.kaons.size(); ++j) { - fillPhiPair(event.kaons[i], event.kaons[j], mult, false); + auto const& kaon = event.kaons[i]; + if (enablePhiControls.value) { + for (size_t j = i + 1; j < event.kaons.size(); ++j) { + fillControlPair(kaon, event.kaons[j], mult, false, Phi, mc); + } + } + if (enableKstarControls.value) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, false, Kstar, mc); + } + } + for (auto const& previous : pool) { + if (enablePhiControls.value) { + for (auto const& other : previous.kaons) { + fillControlPair(kaon, other, mult, true, Phi); + } + } + if (enableKstarControls.value) { + for (auto const& pion : previous.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } + } } - // Includes K+current K-previous and K-current K+previous once each. - // Pool insertion occurs after this call; no event is mixed with itself. + } + if (enableKstarControls.value) { + // Both K(current)-pi(previous) and K(previous)-pi(current) are included. for (auto const& previous : pool) { - for (auto const& second : previous.kaons) { - fillPhiPair(event.kaons[i], second, mult, true); + for (auto const& kaon : previous.kaons) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } } } } } - void process(Collisions const& collisions, Tracks const& tracks, - aod::BCsWithTimestamps const&) + template + void processReco(TCollisions const& collisions, TTracks const& tracks, TSlice const& slice) { - // Local pools cannot retain track IDs across dataframes. Run is part of the - // key. + // Pools reset at each dataframe; the key also separates runs, z and multiplicity. std::map>, std::deque> pools; + const std::deque emptyPool; for (auto const& collision : collisions) { histos.fill(HIST("hEventSelection"), 1.); - if (std::abs(collision.posZ()) >= vertexMax || - (requireSel8 && !collision.sel8()) || - (requireINELgt0 && collision.multNTracksPVeta1() < 1)) { + if (std::abs(collision.posZ()) >= vertexMax.value || (requireSel8.value && !collision.sel8()) || + (requireINELgt0.value && collision.multNTracksPVeta1() < 1)) { continue; } histos.fill(HIST("hEventSelection"), 2.); const float mult = collision.multNTracksPVeta1(); - const int run = collision.bc_as().runNumber(); - auto& pool = - pools[{run, - {static_cast(std::floor(collision.posZ() / mixZWidth)), - static_cast(std::floor(mult / mixMultWidth))}}]; + std::deque* activePool = nullptr; + if (mixingEnabled()) { + const int run = collision.template bc_as().runNumber(); + activePool = &pools[{run, {static_cast(std::floor(collision.posZ() / mixZWidth.value)), static_cast(std::floor(mult / mixMultWidth.value))}}]; + histos.fill(HIST("hMixPartners"), activePool->size()); + } + auto const& pool = activePool ? *activePool : emptyPool; Event event; - auto selected = tracks.sliceBy(perCollision, collision.globalIndex()); + auto selected = tracks.sliceBy(slice, collision.globalIndex()); for (auto const& track : selected) { - if (track.sign() == 0 || track.pt() < trackPtMin || - std::abs(track.eta()) >= trackEtaMax || - (requireGlobalTrack && !track.isGlobalTrack()) || - (requirePVContributor && !track.isPVContributor())) { + if (track.sign() == 0 || track.pt() < trackPtMin.value || std::abs(track.eta()) >= trackEtaMax.value || + (requireGlobalTrack.value && !track.isGlobalTrack()) || (requirePVContributor.value && !track.isPVContributor())) { continue; } const bool tof = track.hasTOF(); - histos.fill(HIST("hPionTPC"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hKaonTPC"), track.pt(), track.tpcNSigmaKa()); - if (tof) { - histos.fill(HIST("hPionTOF"), track.pt(), track.tofNSigmaPi()); - histos.fill(HIST("hKaonTOF"), track.pt(), track.tofNSigmaKa()); + if (enablePidQA.value) { + histos.fill(HIST("hPionTPC"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("hKaonTPC"), track.pt(), track.tpcNSigmaKa()); + if (tof) { + histos.fill(HIST("hPionTOF"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("hKaonTOF"), track.pt(), track.tofNSigmaKa()); + } } - TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof}; - if (passPID(track.tpcNSigmaPi(), track.tofNSigmaPi(), tof, pionTPC, - pionTOF)) { + TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof, .truth = {}}; + if constexpr (IsMC) { + candidate.truth = trackTruth(track, collision.mcCollisionId()); + } + if (passPID(track.tpcNSigmaPi(), track.tofNSigmaPi(), tof, pionTPC.value, pionTOF.value)) { event.pions.push_back(candidate); } - if (passPID(track.tpcNSigmaKa(), track.tofNSigmaKa(), tof, kaonTPC, - kaonTOF)) { + if (passPID(track.tpcNSigmaKa(), track.tofNSigmaKa(), tof, kaonTPC.value, kaonTOF.value)) { event.kaons.push_back(candidate); } } - histos.fill(HIST("hMixPartners"), pool.size()); - for (size_t i = 0; i < event.pions.size(); ++i) { - auto const& first = event.pions[i]; - for (size_t j = i + 1; j < event.pions.size(); ++j) { - auto const& second = event.pions[j]; - const auto pair = first.vector(constants::physics::MassPionCharged) + - second.vector(constants::physics::MassPionCharged); - if (std::abs(pair.Rapidity()) >= dipionYMax) { - continue; - } - histos.fill(HIST("hDipionSE"), pair.M(), pair.Pt(), mult, - chargeCategory(first.sign, second.sign)); - if (pair.M() < dipionMassMin || pair.M() >= dipionMassMax) { - continue; - } - fillProxy(pair, first, second, event.kaons, mult, false); - for (auto const& previous : pool) { - fillProxy(pair, first, second, previous.kaons, mult, true); + fillDipions(event, pool, mult, IsMC); + fillControls(event, pool, mult, IsMC); + if (activePool) { + // Mixed events never use ancestry; release that storage before caching. + if constexpr (IsMC) { + for (auto const& collection : {&event.pions, &event.kaons}) { + for (size_t i = 0; i < collection->size(); ++i) { + (*collection)[i].truth = {}; + } } } + activePool->push_back(std::move(event)); + if (activePool->size() > static_cast(mixDepth.value)) { + activePool->pop_front(); + } + } + } + } + + void fillDipions(Event const& event, std::deque const& pool, float mult, bool mc) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + fillSparse(dipionSpectra[0], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + if (mc && trueF0(first, second)) { + recoTrueF0->Fill(pair.M(), pair.Pt()); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + fillProxy(pair, first, second, event.kaons, mult, false, Phi, mc); + fillProxy(pair, first, second, event.pions, mult, false, Kstar, mc); for (auto const& previous : pool) { - for (auto const& second : previous.pions) { - const auto pair = - first.vector(constants::physics::MassPionCharged) + - second.vector(constants::physics::MassPionCharged); - if (std::abs(pair.Rapidity()) < dipionYMax) { - histos.fill(HIST("hDipionME"), pair.M(), pair.Pt(), mult, - chargeCategory(first.sign, second.sign)); + fillProxy(pair, first, second, previous.kaons, mult, true, Phi); + fillProxy(pair, first, second, previous.pions, mult, true, Kstar); + } + } + for (auto const& previous : pool) { + for (auto const& second : previous.pions) { + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) < dipionYMax.value) { + fillSparse(dipionSpectra[1], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + } + } + } + } + } + + void fillGenerated(Event const& event) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + if (first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + const bool tagged = trueF0(first, second); + generated[GenDipion]->Fill(pair.M(), pair.Pt()); + if (tagged) { + generated[GenF0]->Fill(pair.M(), pair.Pt()); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + const FourVector proxy = constituent(pair); + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!channelEnabled(channel)) { + continue; + } + auto const& bachelors = channel == Phi ? event.kaons : event.pions; + for (auto const& bachelor : bachelors) { + if (bachelor.id == first.id || bachelor.id == second.id) { + continue; + } + const auto candidate = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(candidate.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenProxyPhi : GenProxyKstar]->Fill(candidate.M(), candidate.Pt()); + if (tagged) { + generated[channel == Phi ? GenProxyPhiF0 : GenProxyKstarF0]->Fill(candidate.M(), candidate.Pt()); } } } } - fillPhiControls(event, pool, mult); - if (mixDepth > 0) { - pool.push_back(std::move(event)); - if (pool.size() > static_cast(mixDepth.value)) { - pool.pop_front(); + } + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!controlEnabled(channel)) { + continue; + } + for (size_t i = 0; i < event.kaons.size(); ++i) { + auto const& first = event.kaons[i]; + auto const& others = channel == Phi ? event.kaons : event.pions; + const size_t start = channel == Phi ? i + 1 : 0; + for (size_t j = start; j < others.size(); ++j) { + auto const& second = others[j]; + if (first.id == second.id || first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenPhi : GenKstar]->Fill(pair.M(), pair.Pt()); + if (commonParent(first.truth, second.truth, channel == Phi ? PdgPhi : PdgKstar)) { + generated[channel == Phi ? GenPhiTrue : GenKstarTrue]->Fill(pair.M(), pair.Pt()); + } } } } } + + void processData(Collisions const& collisions, Tracks const& tracks, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollision); + } + // Keep the original JSON option "process"; no unconditional process() method. + PROCESS_SWITCH_FULL(F0phiproxy, processData, process, "Dipion phi/Kstar proxies in data", true); + + void processMCReco(CollisionsMC const& collisions, TracksMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollisionMC); + } + PROCESS_SWITCH(F0phiproxy, processMCReco, "Data-identical reconstruction with truth components and responses", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (std::abs(collision.posZ()) >= vertexMax.value) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + bool inel = false; + for (auto const& particle : particles) { + // O2 ASoA row iterators and their end sentinel are not STL input iterators. + // cppcheck-suppress useStlAlgorithm + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (requireINELgt0.value && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + Event event; + for (auto const& particle : particles) { + const int absolute = std::abs(particle.pdgCode()); + if ((absolute != PdgPion && absolute != PdgKaon) || particle.pt() < trackPtMin.value || + std::abs(particle.eta()) >= trackEtaMax.value || (mcPrimaryOnly.value && !particle.isPhysicalPrimary())) { + continue; + } + TrackCandidate candidate{.id = particle.globalIndex(), .sign = particle.pdgCode() > 0 ? 1 : -1, .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .hasTOF = false, .truth = particleTruth(particle)}; + (absolute == PdgPion ? event.pions : event.kaons).push_back(std::move(candidate)); + } + // Unweighted primary-daughter fiducial spectra, with no detector selection. + // Native f0 truth is not a resonance-fed coalescence ancestry tag. + fillGenerated(event); + } + PROCESS_SWITCH(F0phiproxy, processMCGen, "Independent generated fiducial spectra and exact common-parent tags", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{ - adaptAnalysisTask(context)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } From e60e90342a649fe341bbab0ca756e8cb54524e70 Mon Sep 17 00:00:00 2001 From: hfribert Date: Mon, 19 Oct 2026 14:10:37 +0200 Subject: [PATCH 345/648] [ALICE3] OTF: New tracker ToT LUT approach (#18297) Co-authored-by: Henrik Fribert Co-authored-by: Henrik Fribert --- .../TableProducer/OTF/onTheFlyTrackerPid.cxx | 919 ++++++------------ 1 file changed, 293 insertions(+), 626 deletions(-) diff --git a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx index bf7a1f1350e..51a8dcb95db 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx @@ -11,23 +11,20 @@ /// /// \file onTheFlyTrackerPid.cxx /// -/// \brief This task produces the PID information that can be obtained from the tracker layers (i.e. ToT and possibly cluster size). -/// So far only ToT implemented. It currently contemplates 5 (9) particle types: electrons, muons, pions, kaons and -/// protons (as well as deuterons, tritons, helium-3 and alphas/helium-4 if added via the event generator). +/// \brief This task simulates the PID signal that can be obtained from the tracker layers as ToT (Time-over-Threshold). +/// The dE/dx of every track is sampled from a per-track LUT made from the full simulation. +/// It contemplates 9 particle types: electrons, muons, pions, kaons, protons, deuterons, tritons, +/// helium-3 and alphas/helium-4. /// /// \author Henrik Fribert TUM /// \author Nicolò Jacazio Università del Piemonte Orientale /// \since May 22, 2025 /// -#include "GeometryContainer.h" - -#include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include #include -#include #include #include #include @@ -38,732 +35,402 @@ #include #include #include -#include -#include -#include -#include #include #include #include +#include +#include #include #include #include #include #include -#include #include #include +#include #include #include #include #include -#include #include using namespace o2; using namespace o2::framework; -static constexpr std::array kTrackerRadii = {0.5f, 1.2f, 2.5f, 3.75f, 7.0f, 12.0f, 20.0f, 30.0f, 45.0f, 60.0f, 80.0f}; - -// Constants for magic numbers -static constexpr int kMinEntriesForProjection = 10; -static constexpr double kMinMomentumForLogBins = 0.05; -static constexpr double kMaxMomentumForLogBins = 10.0; -static constexpr int kMinLayerForTruncation = 3; - -// Constants for validation conditions -static constexpr size_t kMinValidHits = 1; -static constexpr size_t kLowValidHits = 2; -static constexpr size_t kMidLowValidHits = 3; -static constexpr size_t kMidHighValidHits = 4; -static constexpr size_t kHighValidHits1 = 5; -static constexpr size_t kHighValidHits2 = 6; -static constexpr size_t kMaxValidHits = 7; - -// Constants for truncated mean calculation -static constexpr size_t kMaxValidHitsForTruncation7Plus = 4; -static constexpr size_t kMaxValidHitsForTruncation56 = 3; -static constexpr size_t kMaxValidHitsForTruncation34 = 2; -static constexpr size_t kMaxValidHitsForTruncation12 = 1; - -class ToTLUT +static constexpr double MinMomentumForLogBins = 0.05; +static constexpr double MaxMomentumForLogBins = 10.0; + +/// Local path of a LUT file; "ccdb:" is downloaded once to /tmp/ToTLUTs//snapshot.root +std::string getLocalLutFile(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager) { - public: - explicit ToTLUT(int maxLayers, - int etaBins, float etaMin, float etaMax, - int pBins, float pMin, float pMax, - float analysisEtaMinVal = 0.0f, float analysisEtaMaxVal = 1.0f, - float analysisPMinVal = 0.0f, float analysisPMaxVal = 10.0f) - : mMaxLayers(maxLayers), - mAnalysisEtaMin(analysisEtaMinVal), - mAnalysisEtaMax(analysisEtaMaxVal), - mAnalysisPMin(analysisPMinVal), - mAnalysisPMax(analysisPMaxVal), - mEtaBins(etaBins), - mEtaMin(etaMin), - mEtaMax(etaMax), - mPBins(pBins), - mPMin(pMin), - mPMax(pMax), - mEtaBinWidth((etaMax - etaMin) / etaBins), - mPBinWidth((pMax - pMin) / pBins) - { - mPdgToIndexMap.reserve(10); - mIndexToPdgMap.reserve(10); + if (!filename.starts_with("ccdb:")) { + return filename; } - ToTLUT() = delete; - - ~ToTLUT() - { - for (const auto& hist_ptr : mLUTHistogramFlat) { - - delete hist_ptr; + const std::string ccdbPath = filename.substr(5); + const std::string outPath = "/tmp/ToTLUTs/"; + const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; + if (!std::ifstream(localFilename).is_open()) { + if (!ccdbManager) { + LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; + } + std::map metadata; + LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; + ccdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); + if (!std::ifstream(localFilename).is_open()) { + LOG(fatal) << "Could not find downloaded CCDB file " << localFilename; } } + return localFilename; +} - void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } - - bool load(int pdg, const std::string& filename) +/// Per-track dE/dx LUT; for every species, |eta| bin and rigidity bin the +/// distribution of the track dE/dx in e-, as a histogram. The dE/dx of a track is sampled from it, +/// interpolated linearly in log(rigidity) between the rigidity bin centres with the same random number. +class TrackDedxLut +{ + public: + template + bool load(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager, const std::array& pdgCodes) { - if (filename.empty()) { - LOG(warning) << "Provided filename is empty for PDG " << pdg; + std::unique_ptr f(TFile::Open(getLocalLutFile(filename, ccdbManager).c_str())); + if (!f || f->IsZombie()) { + LOG(fatal) << "Failed to open dE/dx LUT file: " << filename; return false; } - if (filename.starts_with("ccdb:")) { // Check if filename starts with "ccdb:" - std::string ccdbPath = filename.substr(5); // remove "ccdb:" prefix - const std::string outPath = "/tmp/ToTLUTs/"; - const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; - std::ifstream checkFile(localFilename); - if (!checkFile.is_open()) { // File is not found, need to download it from CCDB - if (!mCcdbManager) { - LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; - } - std::map metadata; - LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; - mCcdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); - std::ifstream testFile(localFilename); - if (!testFile.is_open()) { - LOG(fatal) << "Could not find downloaded CCDB file for PDG " << pdg << " in file " << localFilename; - return false; - } - testFile.close(); - return load(pdg, localFilename); - } // File is found, proceed to load it - checkFile.close(); - return load(pdg, localFilename); - } - // In case the file is already available locally - TFile* f = TFile::Open(filename.c_str()); - if (!f || f->IsZombie()) { - LOG(fatal) << "Failed to open LUT file: " << filename; + const auto* etaBins = f->Get("etaBins"); + const auto* rigBins = f->Get("rigBins"); + const auto* dedxBins = f->Get("dedxBins"); + if (!etaBins || !rigBins || !dedxBins) { + LOG(fatal) << "No etaBins/rigBins/dedxBins in dE/dx LUT file: " << filename; return false; } - - int currentPdgIdx = 0; - auto it = mPdgToIndexMap.find(pdg); - if (it == mPdgToIndexMap.end()) { - currentPdgIdx = mIndexToPdgMap.size(); - mPdgToIndexMap[pdg] = currentPdgIdx; - mIndexToPdgMap.push_back(pdg); - - size_t totalSize = (currentPdgIdx + 1) * mMaxLayers * mEtaBins * mPBins; - if (mLUTHistogramFlat.size() < totalSize) { - mLUTHistogramFlat.resize(totalSize, nullptr); - } - } else { - currentPdgIdx = it->second; + mEtaEdges = binEdges(etaBins); + mRigEdges = binEdges(rigBins); + mDedxEdges = binEdges(dedxBins); + mLogRigCentres.clear(); + for (size_t i = 0; i + 1 < mRigEdges.size(); ++i) { + mLogRigCentres.push_back(0.5 * (std::log(mRigEdges[i]) + std::log(mRigEdges[i + 1]))); } - - bool success = true; - for (int layer = 0; layer < mMaxLayers; ++layer) { - for (int etaBin = 0; etaBin < mEtaBins; ++etaBin) { - float etaMinBin = mEtaMin + etaBin * mEtaBinWidth; - float etaMaxBin = etaMinBin + mEtaBinWidth; - - float etaCenter = (etaMinBin + etaMaxBin) / 2.0f; - if (std::abs(etaCenter) < mAnalysisEtaMin || std::abs(etaCenter) > mAnalysisEtaMax) { + const size_t nBins = (mEtaEdges.size() - 1) * (mRigEdges.size() - 1); + const size_t nDedx = mDedxEdges.size() - 1; + + bool allLoaded = true; + for (const auto& pdg : pdgCodes) { + std::unique_ptr> h(f->Get>(Form("h_%d", pdg))); + if (!h || h->size() != nBins * nDedx) { + LOG(warning) << "No dE/dx LUT for PDG " << pdg << " in " << filename; + allLoaded = false; + continue; + } + // cumulative distributions and raw moments, only for the filled bins + auto& offset = mOffset[pdg]; + auto& cdf = mCdf[pdg]; + auto& mean = mMean[pdg]; + auto& sigma = mSigma[pdg]; + offset.assign(nBins, -1); + mean.assign(nBins, -1.f); + sigma.assign(nBins, -1.f); + for (size_t b = 0; b < nBins; ++b) { + const float* hb = h->data() + b * nDedx; + double sum = 0., m1 = 0., m2 = 0.; + for (size_t k = 0; k < nDedx; ++k) { + const double x = 0.5 * (mDedxEdges[k] + mDedxEdges[k + 1]); + sum += hb[k]; + m1 += hb[k] * x; + m2 += hb[k] * x * x; + } + if (sum <= 0.) { continue; } - - for (int pBin = 0; pBin < mPBins; ++pBin) { - float pMinBin = mPMin + pBin * mPBinWidth; - float pMaxBin = pMinBin + mPBinWidth; - float pCenter = (pMinBin + pMaxBin) / 2.0f; - - if (pCenter < mAnalysisPMin || pCenter >= mAnalysisPMax) { - continue; - } - - TString histName = Form("tot_%d_barrel%d_eta%.2f-%.2f_p%.2f-%.2f", pdg, layer, etaMinBin, etaMaxBin, pMinBin, pMaxBin); - - TH1F* histFromFile = dynamic_cast(f->Get(histName)); - if (histFromFile) { - TH1F* clonedHist = dynamic_cast(histFromFile->Clone()); - clonedHist->SetDirectory(nullptr); - - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, pBin); - mLUTHistogramFlat[flatIdx] = clonedHist; - } else { - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, pBin); - mLUTHistogramFlat[flatIdx] = nullptr; - if (layer >= kMinLayerForTruncation) { - success = false; - } - } + offset[b] = cdf.size(); + double cum = 0.; + for (size_t k = 0; k < nDedx; ++k) { + cum += hb[k]; + cdf.push_back(cum / sum); } + const double mu = m1 / sum; + mean[b] = mu; + sigma[b] = std::sqrt(std::max(0., m2 / sum - mu * mu)); } } - - f->Close(); - delete f; - return success; + LOG(info) << "Loaded dE/dx LUT " << filename << " (" << mEtaEdges.size() - 1 << " |eta| x " << mRigEdges.size() - 1 + << " rigidity x " << mDedxEdges.size() - 1 << " dE/dx bins)"; + return allLoaded; } - TH1F* getHistogramForSampling(int pdgIdx, int layer, int etaBin, int pBin) const + /// Sampled dE/dx for uniform random number u, or -1 if there is no LUT entry + float sample(int pdg, float eta, float rigidity, double u) const { - if (pdgIdx < 0 || static_cast(pdgIdx) >= getNumPdgTypes() || - layer < 0 || layer >= mMaxLayers || - etaBin < 0 || etaBin >= mEtaBins || pBin < 0 || pBin >= mPBins) { - return nullptr; + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return -1.f; } - size_t flatIdx = getFlatIndex(pdgIdx, layer, etaBin, pBin); - return (flatIdx < mLUTHistogramFlat.size()) ? mLUTHistogramFlat[flatIdx] : nullptr; - } - - int getPdgIndex(int pdgCode) const - { - auto it = mPdgToIndexMap.find(pdgCode); - if (it != mPdgToIndexMap.end()) { - return it->second; + const auto& offset = mOffset.at(pdg); + const float* cdf = mCdf.at(pdg).data(); + float dedx = inverseCdf(cdf + offset[bin], u); + if (binNext >= 0) { + dedx += weight * (inverseCdf(cdf + offset[binNext], u) - dedx); } - return -1; - } - - inline int getEtaBin(float eta) const - { - const float clampedEta = std::max(mEtaMin, std::min(eta, mEtaMax - 1e-6f)); - return std::min(static_cast((clampedEta - mEtaMin) / mEtaBinWidth), mEtaBins - 1); - } - - inline int getPBin(float p) const - { - const float clampedP = std::max(mPMin, std::min(p, mPMax - 1e-6f)); - return std::min(static_cast((clampedP - mPMin) / mPBinWidth), mPBins - 1); + return dedx; } - inline size_t getFlatIndex(int pdgIdx, int layer, int etaBin, int pBin) const + bool getMeanSigma(int pdg, float eta, float rigidity, float& mean, float& sigma) const { - return ((pdgIdx * mMaxLayers + layer) * mEtaBins + etaBin) * mPBins + pBin; - } - - size_t getNumPdgTypes() const - { - return mIndexToPdgMap.size(); + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return false; + } + const auto& m = mMean.at(pdg); + const auto& s = mSigma.at(pdg); + mean = m[bin]; + sigma = s[bin]; + if (binNext >= 0) { + mean += weight * (m[binNext] - mean); + sigma += weight * (s[binNext] - sigma); + } + return sigma > 0.f; } - std::vector mLUTHistogramFlat; - - std::unordered_map mPdgToIndexMap; - std::vector mIndexToPdgMap; - - int mMaxLayers; - float mAnalysisEtaMin; - float mAnalysisEtaMax; - float mAnalysisPMin; - float mAnalysisPMax; - int mEtaBins; - float mEtaMin; - float mEtaMax; - int mPBins; - float mPMin; - float mPMax; - - float mEtaBinWidth; - float mPBinWidth; - private: - o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; -}; - -struct OnTheFlyTrackerPid { - static constexpr int kNumHypothesisParticles = 9; - std::array, kNumHypothesisParticles>, kNumHypothesisParticles> h2dBarrelNsigmaTrue; - std::array, kNumHypothesisParticles> h2dHitsPerTrackVsP; - std::array, kNumHypothesisParticles> h2dToTvsPperParticle; - std::array, kNumHypothesisParticles> h2dHitsPerTrackVsPLin; - std::array, kNumHypothesisParticles> h2dToTvsPperParticleLin; - - float calculateNsigma(float measuredToT, float expectedToT, float resolution) + /// LUT bin of the track and the neighbouring rigidity bin on its side (-1 if empty) + bool locate(int pdg, float eta, float rigidity, int& bin, int& binNext, float& weight) const { - if (resolution <= 0) { - return 999.f; + const auto it = mOffset.find(pdg); + const float absEta = std::abs(eta); + if (it == mOffset.end() || mEtaEdges.empty() || absEta < mEtaEdges.front() || absEta >= mEtaEdges.back() || + rigidity < mRigEdges.front() || rigidity >= mRigEdges.back()) { + return false; } - return (measuredToT - expectedToT) / resolution; - } - - float getToTMeanFromMomentumSlice(const std::shared_ptr& hist, float momentum) - { - if (!hist) { - return -1.f; + const int nRig = mRigEdges.size() - 1; + const int iEta = std::upper_bound(mEtaEdges.begin(), mEtaEdges.end(), absEta) - mEtaEdges.begin() - 1; + const int iRig = std::upper_bound(mRigEdges.begin(), mRigEdges.end(), rigidity) - mRigEdges.begin() - 1; + bin = iEta * nRig + iRig; + if (it->second[bin] < 0) { + return false; } - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; + binNext = -1; + weight = 0.f; + const double logRig = std::log(rigidity); + const int iNext = logRig < mLogRigCentres[iRig] ? iRig - 1 : iRig + 1; + if (iNext >= 0 && iNext < nRig && it->second[iEta * nRig + iNext] >= 0) { + binNext = iEta * nRig + iNext; + weight = (logRig - mLogRigCentres[iRig]) / (mLogRigCentres[iNext] - mLogRigCentres[iRig]); } - float mean = proj->GetMean(); - delete proj; - return mean; + return true; } - float getToTResolutionFromMomentumSlice(const std::shared_ptr& hist, float momentum) + /// dE/dx at which the cumulative distribution reaches u, uniform within the dE/dx bin + float inverseCdf(const float* cdf, double u) const { - if (!hist) { - return -1.f; - } - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; - } - float stddev = proj->GetStdDev(); - delete proj; - return stddev; + const int nDedx = mDedxEdges.size() - 1; + const int k = std::min(static_cast(std::upper_bound(cdf, cdf + nDedx, u) - cdf), nDedx - 1); + const double lo = k == 0 ? 0. : cdf[k - 1]; + const double frac = cdf[k] > lo ? (u - lo) / (cdf[k] - lo) : 0.5; + return mDedxEdges[k] + frac * (mDedxEdges[k + 1] - mDedxEdges[k]); } - float computeTrackLength(o2::track::TrackParCov track, float radius, float magneticField) + static std::vector binEdges(const TH1* h) { - o2::math_utils::CircleXYf_t trcCircle; - float sna = NAN, csa = NAN; - track.getCircleParams(magneticField, trcCircle, sna, csa); - - const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); - - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - float length = 0.0f; - const float ux = trcCircle.xC / centerDistance; - const float uy = trcCircle.yC / centerDistance; - const float vx = -uy; - const float vy = +ux; - const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + radius * radius) / (2.0f * centerDistance); - const float displace = (0.5f / centerDistance) * std::sqrt( - (-centerDistance + trcCircle.rC - radius) * - (-centerDistance - trcCircle.rC + radius) * - (-centerDistance + trcCircle.rC + radius) * - (centerDistance + trcCircle.rC + radius)); - - const std::array point1 = {radical * ux + displace * vx, radical * uy + displace * vy}; - const std::array point2 = {radical * ux - displace * vx, radical * uy - displace * vy}; - - std::array mom{}; - track.getPxPyPzGlo(mom); - const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; - const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; - - std::array startPoint{}; - track.getXYZGlo(startPoint); - - float cosAngle = -1000, modulus = -1000; - - if (scalarProduct1 > scalarProduct2) { - modulus = std::hypot(point1[0] - trcCircle.xC, point1[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point1[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point1[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } else { - modulus = std::hypot(point2[0] - trcCircle.xC, point2[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point2[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point2[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } - cosAngle /= modulus; - length = trcCircle.rC * std::acos(cosAngle); - length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); - return length; + std::vector edges; + for (int i = 1; i <= h->GetNbinsX() + 1; ++i) { + edges.push_back(h->GetXaxis()->GetBinLowEdge(i)); } - return -100.f; + return edges; } + std::vector mEtaEdges; + std::vector mRigEdges; + std::vector mLogRigCentres; + std::vector mDedxEdges; + std::unordered_map> mOffset; // |pdg| -> start of the cdf of bin etaBin * nRig + rigBin, -1 if empty + std::unordered_map> mCdf; // |pdg| -> cumulative dE/dx distributions of the filled bins + std::unordered_map> mMean; // |pdg| -> mean dE/dx per bin, -1 if empty + std::unordered_map> mSigma; +}; + +struct OnTheFlyTrackerPid { + static constexpr int NumSpecies = 9; + static constexpr std::array PdgCodes = {11, 13, 211, 321, 2212, 1000010020, 1000010030, 1000020030, 1000020040}; + static constexpr std::array SpeciesNames = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; + static constexpr std::array SpeciesLabels = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "#it{^{3}He}", "#it{^{4}He}"}; + static constexpr float NoNsigma = 999.f; + + std::array, NumSpecies> h2dToTvsPperParticle; + std::array, NumSpecies> h2dToTvsPperParticleLin; + std::array, NumSpecies> h3dToTvsPEtaPerParticle; + std::array, NumSpecies> h2dTracks; + std::array, NumSpecies> h2dTracksWithDedx; + Produces tableUpgradeTrkPidSignals; Produces tableUpgradeTrkPids; Service pdgDatabase{}; Service ccdb{}; - std::unique_ptr mToTLUT; + TrackDedxLut mTrackLut; + TRandom3 mRandom; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable lutTotEl{"lutTotEl", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_11", "ToT LUT for electrons"}; - Configurable lutTotMu{"lutTotMu", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_13", "ToT LUT for muons"}; - Configurable lutTotPi{"lutTotPi", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_211", "ToT LUT for pions"}; - Configurable lutTotKa{"lutTotKa", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_321", "ToT LUT for kaons"}; - Configurable lutTotPr{"lutTotPr", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_2212", "ToT LUT for protons"}; - Configurable lutTotDe{"lutTotDe", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000010020", "ToT LUT for deuteron"}; - Configurable lutTotTr{"lutTotTr", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000010030", "ToT LUT for triton"}; - Configurable lutTotHe{"lutTotHe", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000020030", "ToT LUT for helium-3"}; - Configurable lutTotAl{"lutTotAl", "ccdb:Users/h/hfribert/ToT_LUTs_ALMIRA/Scale1/PDG_1000020040", "ToT LUT for alphas"}; - - Configurable maxBarrelLayers{"maxBarrelLayers", 11, "Maximum number of barrel layers"}; - Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; - Configurable analysisEtaMin{"analysisEtaMin", 0.0f, "Minimum |eta| for LUT loading optimization"}; - Configurable analysisEtaMax{"analysisEtaMax", 1.0f, "Maximum |eta| for LUT loading optimization"}; - Configurable analysisPMin{"analysisPMin", 0.0f, "Minimum |p| (GeV/c) for LUT loading optimization"}; - Configurable analysisPMax{"analysisPMax", 10.0f, "Maximum |p| (GeV/c) for LUT loading optimization"}; - - // LUT binning (has to match LUTs) - Configurable lutEtaBins{"lutEtaBins", 50, "Number of eta bins in the LUT"}; - Configurable lutEtaMin{"lutEtaMin", -2.5f, "Minimum eta in the LUT"}; - Configurable lutEtaMax{"lutEtaMax", 2.5f, "Maximum eta in the LUT"}; - Configurable lutPBins{"lutPBins", 500, "Number of momentum bins in the LUT"}; - Configurable lutPMin{"lutPMin", 0.0f, "Minimum momentum in the LUT (GeV/c)"}; - Configurable lutPMax{"lutPMax", 10.0f, "Maximum momentum in the LUT (GeV/c)"}; - - // ToT histogram axis - Configurable totHistBins{"totHistBins", 600, "Number of bins on the ToT axis"}; - Configurable totHistMin{"totHistMin", 0.0f, "Minimum of the ToT axis (#mus/10#mum)"}; - Configurable totHistMax{"totHistMax", 10.0f, "Maximum of the ToT axis (#mus/10#mum)"}; + Configurable trackLut{"trackLut", "", "Per-track dE/dx LUT from MakeToTLUT.C (local file or ccdb:)"}; + Configurable seed{"seed", 0, "Seed for the dE/dx sampling (0: different in every job)"}; + Configurable primariesOnly{"primariesOnly", false, "Fill the histograms only with physical primaries (the tables are filled for all tracks)"}; + Configurable useTrueKinematics{"useTrueKinematics", false, "Fill the histograms vs the true instead of the reconstructed p/z and |eta| (the dE/dx is always sampled at the true ones)"}; + + // dE/dx axis, logarithmic + Configurable numDedxBins{"numDedxBins", 300, "Number of logarithmic dE/dx bins"}; + Configurable dedxMin{"dedxMin", 1e2f, "Minimum of the dE/dx axis in e-"}; + Configurable dedxMax{"dedxMax", 1e5f, "Maximum of the dE/dx axis in e-"}; + + ConfigurableAxis axisEta{"axisEta", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f}, "|#eta| binning of the eta-differential histograms"}; // Momentum axis binning choice - Configurable enableLogPBins{"enableLogPBins", true, "Enable logarithmic momentum binning for ToT vs p histograms"}; - Configurable enableLinearPBins{"enableLinearPBins", false, "Enable linear momentum binning for ToT vs p histograms"}; + Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; + Configurable enableLogPBins{"enableLogPBins", true, "Enable logarithmic momentum binning for dE/dx vs p histograms"}; + Configurable enableLinearPBins{"enableLinearPBins", false, "Enable linear momentum binning for dE/dx vs p histograms"}; Configurable numLinearBins{"numLinearBins", 500, "Number of linear momentum bins"}; Configurable linearPMin{"linearPMin", 0.0f, "Minimum momentum for linear-binned histograms (GeV/c)"}; Configurable linearPMax{"linearPMax", 10.0f, "Maximum momentum for linear-binned histograms (GeV/c)"}; - std::vector mLogBins; - - std::array mHypothesisPdgCodes = { - 11, // Electron - 13, // Muon - 211, // Pion - 321, // Kaon - 2212, // Proton - 1000010020, // Deuteron - 1000010030, // Triton - 1000020030, // Helium-3 - 1000020040 // Alpha - }; - - // Configuration defined at init time - o2::fastsim::GeometryContainer mGeoContainer; - float mMagneticField = 0.0f; - void init(o2::framework::InitContext& initContext) + void init(o2::framework::InitContext&) { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setTimestamp(-1); - mGeoContainer.setCcdbManager(ccdb.operator->()); - mGeoContainer.init(initContext); - mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); - - if (static_cast(maxBarrelLayers.value) > kTrackerRadii.size()) { - LOG(fatal) << "Configured maxBarrelLayers (" << maxBarrelLayers.value - << ") exceeds the size of kTrackerRadii (" << kTrackerRadii.size() - << "). Please adjust maxBarrelLayers."; - } - - mToTLUT = std::make_unique(maxBarrelLayers.value, - lutEtaBins.value, lutEtaMin.value, lutEtaMax.value, - lutPBins.value, lutPMin.value, lutPMax.value, - analysisEtaMin.value, analysisEtaMax.value, - analysisPMin.value, analysisPMax.value); - - mToTLUT->setCcdbManager(ccdb.operator->()); - - bool loaded = true; - loaded &= mToTLUT->load(11, lutTotEl.value); - loaded &= mToTLUT->load(13, lutTotMu.value); - loaded &= mToTLUT->load(211, lutTotPi.value); - loaded &= mToTLUT->load(321, lutTotKa.value); - loaded &= mToTLUT->load(2212, lutTotPr.value); - loaded &= mToTLUT->load(1000010020, lutTotDe.value); - loaded &= mToTLUT->load(1000010030, lutTotTr.value); - loaded &= mToTLUT->load(1000020030, lutTotHe.value); - loaded &= mToTLUT->load(1000020040, lutTotAl.value); - - if (!loaded) { - LOG(warning) << "Failed to load one or more ToT LUTs. PID results might be incomplete."; + mRandom.SetSeed(seed.value); + if (!mTrackLut.load(trackLut.value, ccdb.operator->(), PdgCodes)) { + LOG(warning) << "dE/dx LUT incomplete, species without LUT get no tracker PID."; } + bookHistograms(); + } - // Logarithmic momentum bins - mLogBins.clear(); - double pMin = kMinMomentumForLogBins; - double pMax = kMaxMomentumForLogBins; - double logMin = std::log10(pMin); - double logMax = std::log10(pMax); - double dLog = (logMax - logMin) / numLogBins.value; - for (int i = 0; i <= numLogBins.value; ++i) { - mLogBins.push_back(std::pow(10, logMin + i * dLog)); + static std::vector logBinning(int nBins, double min, double max) + { + std::vector edges; + const double logMin = std::log10(min); + const double dLog = (std::log10(max) - logMin) / nBins; + for (int i = 0; i <= nBins; ++i) { + edges.push_back(std::pow(10, logMin + i * dLog)); } + return edges; + } - const AxisSpec axisToT{totHistBins.value, static_cast(totHistMin.value), static_cast(totHistMax.value), "ToT (#mus/10#mum)"}; - const AxisSpec axisNsigma{200, -10., 10., "N#sigma"}; - const AxisSpec axisLayer{maxBarrelLayers.value, -0.5, static_cast(maxBarrelLayers.value) - 0.5, "Layer"}; - const AxisSpec axisHitsPerTrack{maxBarrelLayers.value + 1, -0.5, static_cast(maxBarrelLayers.value) + 0.5, "# Hits per track"}; - + void bookHistograms() + { if (!enableLogPBins.value && !enableLinearPBins.value) { LOG(fatal) << "At least one of enableLogPBins or enableLinearPBins must be true."; } - histos.add("hHitLayers", "Number of hits on each detector layer;Layer;Counts", kTH1F, {axisLayer}); - histos.add("hHitMultiplicity", "Hit multiplicity along the track; # Hits per track;Counts", kTH1F, {axisHitsPerTrack}); + const std::vector logBins = logBinning(numLogBins.value, MinMomentumForLogBins, MaxMomentumForLogBins); + const std::string signalTitle = "d#it{E}/d#it{x} (e^{-})"; + const AxisSpec axisDedx{logBinning(numDedxBins.value, dedxMin.value, dedxMax.value), signalTitle}; + const AxisSpec axisMomentumLog{logBins, "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisAbsEta{axisEta, "|#eta|"}; if (enableLogPBins.value) { - const AxisSpec axisMomentumLog{mLogBins, "#it{p/z} (GeV/#it{c})"}; - histos.add("hToTvsP", "ToT vs #it{p/z}; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLog, axisToT}); - histos.add("hToTvsMom", "ToT vs #it{p}; #it{p} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {mLogBins, axisToT}); + histos.add("hToTvsP", ("Signal vs #it{p/z}; #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLog, axisDedx}); + histos.add("hToTvsMom", ("Signal vs #it{p}; #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {logBins, axisDedx}); } - if (enableLinearPBins.value) { - const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; - histos.add("hToTvsPLin", "ToT vs #it{p/z} (linear); #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLin, axisToT}); - histos.add("hToTvsMomLin", "ToT vs #it{p} (linear); #it{p} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentumLin, axisToT}); + histos.add("hToTvsPLin", ("Signal vs #it{p/z} (linear); #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); + histos.add("hToTvsMomLin", ("Signal vs #it{p} (linear); #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); } - std::vector> particleInfo = { - {11, "Elec"}, {13, "Muon"}, {211, "Pion"}, {321, "Kaon"}, {2212, "Prot"}, {1000010020, "Deut"}, {1000010030, "Trit"}, {1000020030, "He3"}, {1000020040, "Al"}}; - - for (size_t iTrue = 0; iTrue < particleInfo.size(); ++iTrue) { - std::string trueName = particleInfo[iTrue].second; - std::string trueNamePretty = trueName; // Fallback - if (trueName == "Elec") { - trueNamePretty = "#it{e}"; - } else if (trueName == "Muon") { - trueNamePretty = "#it{#mu}"; - } else if (trueName == "Pion") { - trueNamePretty = "#it{#pi}"; - } else if (trueName == "Kaon") { - trueNamePretty = "#it{K}"; - } else if (trueName == "Prot") { - trueNamePretty = "#it{p}"; - } else if (trueName == "Deut") { - trueNamePretty = "#it{d}"; - } else if (trueName == "Trit") { - trueNamePretty = "#it{t}"; - } else if (trueName == "He3") { - trueNamePretty = "#it{^{3}He}"; - } else if (trueName == "Al") { - trueNamePretty = "#it{^{4}He}"; - } - + for (int i = 0; i < NumSpecies; ++i) { if (enableLogPBins.value) { - const AxisSpec axisMomentumLog{mLogBins, "#it{p/z} (GeV/#it{c})"}; - - std::string hitsVsPName = "HitsPerTrack/hHitsPerTrackVsP_" + trueName; - std::string hitsVsPTitle = "N_hits vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); N_hits"; - h2dHitsPerTrackVsP[iTrue] = histos.add(hitsVsPName.c_str(), hitsVsPTitle.c_str(), kTH2F, {axisMomentumLog, axisHitsPerTrack}); - - std::string totVsPName = "ToTvsP/hToTvsP_" + trueName; - std::string totVsPTitle = "ToT vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)"; - h2dToTvsPperParticle[iTrue] = histos.add(totVsPName.c_str(), totVsPTitle.c_str(), kTH2F, {axisMomentumLog, axisToT}); - - for (size_t iHyp = 0; iHyp < particleInfo.size(); ++iHyp) { - std::string hypName = particleInfo[iHyp].second; - std::string hypNamePretty = hypName; // Fallback - if (hypName == "Elec") { - hypNamePretty = "#it{e}"; - } else if (hypName == "Muon") { - hypNamePretty = "#it{#mu}"; - } else if (hypName == "Pion") { - hypNamePretty = "#it{#pi}"; - } else if (hypName == "Kaon") { - hypNamePretty = "#it{K}"; - } else if (hypName == "Prot") { - hypNamePretty = "#it{p}"; - } else if (hypName == "Deut") { - hypNamePretty = "#it{d}"; - } else if (hypName == "Trit") { - hypNamePretty = "#it{t}"; - } else if (hypName == "He3") { - hypNamePretty = "#it{^{3}He}"; - } else if (hypName == "Al") { - hypNamePretty = "#it{^{4}He}"; - } - - const std::string histName = std::format("NSigma/BarrelNsigmaTrue{}Vs{}Hypothesis", trueName, hypName); - const std::string histTitle = std::format("Nsigma (True {} vs Hyp {}); #it{{p/z}} (GeV/#it{{c}}); N#sigma", trueNamePretty, hypNamePretty); - h2dBarrelNsigmaTrue[iTrue][iHyp] = histos.add(histName.c_str(), histTitle.c_str(), kTH2F, {axisMomentumLog, axisNsigma}); - } + const std::string name = std::format("ToTvsP/hToTvsP_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {}; #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticle[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLog, axisDedx}); } - if (enableLinearPBins.value) { - const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; + const std::string name = std::format("ToTvsPLin/hToTvsPLin_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticleLin[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLin, axisDedx}); + } - const std::string hitsVsPLinName = std::format("HitsPerTrackLin/hHitsPerTrackVsPLin_{}", trueName); - const std::string hitsVsPLinTitle = std::format("N_hits vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); N_hits", trueNamePretty); - h2dHitsPerTrackVsPLin[iTrue] = histos.add(hitsVsPLinName.c_str(), hitsVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisHitsPerTrack}); + const std::string nameEta = std::format("ToTvsPEta/hToTvsPEta_{}", SpeciesNames[i]); + const std::string titleEta = std::format("Signal vs #it{{p/z}} and |#eta| for {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|; {}", SpeciesLabels[i], signalTitle); + h3dToTvsPEtaPerParticle[i] = histos.add(nameEta.c_str(), titleEta.c_str(), kTH3F, {axisMomentumLog, axisAbsEta, axisDedx}); - const std::string totVsPLinName = std::format("ToTvsPLin/hToTvsPLin_{}", trueName); - const std::string totVsPLinTitle = std::format("ToT vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); ToT (#mus/10#mum)", trueNamePretty); - h2dToTvsPperParticleLin[iTrue] = histos.add(totVsPLinName.c_str(), totVsPLinTitle.c_str(), kTH2F, {axisMomentumLin, axisToT}); - } + const std::string nameTracks = std::format("Bookkeeping/hTracks_{}", SpeciesNames[i]); + const std::string titleTracks = std::format("Reconstructed {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracks[i] = histos.add(nameTracks.c_str(), titleTracks.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); + + const std::string nameWithDedx = std::format("Bookkeeping/hTracksWithDedx_{}", SpeciesNames[i]); + const std::string titleWithDedx = std::format("Reconstructed {} with dE/dx; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracksWithDedx[i] = histos.add(nameWithDedx.c_str(), titleWithDedx.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); } } - void process(soa::Join::iterator const& collision, - soa::Join const& tracks, - aod::McParticles const& /*mcParticles*/, - aod::McCollisions const& /*mcCollisions*/) + /// dE/dx of the track (-1 if there is none) and its nsigma for every species + template + float processTrack(TTrack const& track, std::array& nSigma) { - o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, {0.}); - - if (collision.has_mcCollision()) { - const auto& mcCollisionObject = collision.mcCollision(); - mcPvVtx.setX(mcCollisionObject.posX()); - mcPvVtx.setY(mcCollisionObject.posY()); - mcPvVtx.setZ(mcCollisionObject.posZ()); + if (!track.has_mcParticle()) { + return -1.f; } - - for (const auto& track : tracks) { - float truncatedMeanToT = -1.0f; - std::array nSigmaValues{}; - nSigmaValues.fill(999.f); - - if (!track.has_mcParticle()) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const auto& mcParticle = track.mcParticle(); - - const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); - if (!pdgInfo) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const float p = mcParticle.p(); - const float eta = mcParticle.eta(); - const int truePdgCode = std::abs(mcParticle.pdgCode()); - - float rigidity = p; - if (pdgInfo) { - const float trueCharge = std::abs(pdgInfo->Charge()) / 3.0f; - if (trueCharge > 0) { - rigidity = p / trueCharge; - } - } - - int truePdgIdx = mToTLUT->getPdgIndex(truePdgCode); - if (truePdgIdx == -1) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const int binnedP = mToTLUT->getPBin(p); - const int binnedEta = mToTLUT->getEtaBin(std::abs(eta)); - - uint16_t hitMap = 0; - int nHitLayers = 0; - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); - - float xPv = -100.f; - static constexpr float kTrkXThreshold = -99.f; - if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { - xPv = o2track.getX(); - } - - if (xPv > kTrkXThreshold) { - for (int layer = 0; layer < maxBarrelLayers.value; ++layer) { - float layerRadius = kTrackerRadii[layer]; - float trackLength = computeTrackLength(o2track, layerRadius, mMagneticField); - - if (trackLength > 0) { - hitMap |= (1 << layer); - histos.fill(HIST("hHitLayers"), layer); - nHitLayers++; - } - } - } - - histos.fill(HIST("hHitMultiplicity"), nHitLayers); - if (enableLogPBins.value) { - h2dHitsPerTrackVsP[truePdgIdx]->Fill(rigidity, nHitLayers); - } - if (enableLinearPBins.value) { - h2dHitsPerTrackVsPLin[truePdgIdx]->Fill(rigidity, nHitLayers); - } - - std::vector validToTs; - - for (int layer = kMinLayerForTruncation; layer < maxBarrelLayers.value; ++layer) { - if (((hitMap >> layer) & 0x1) != 0) { - TH1F* totHist = mToTLUT->getHistogramForSampling(truePdgIdx, layer, binnedEta, binnedP); - - if (totHist && totHist->GetEntries() > 1) { - float sampledToT = totHist->GetRandom(); - validToTs.push_back(sampledToT); - } - } - } - - truncatedMeanToT = -1.0f; - const size_t nValid = validToTs.size(); - size_t nUse = 0; - - if (nValid == kMidLowValidHits || nValid == kMidHighValidHits) { - nUse = kMaxValidHitsForTruncation34; - } else if (nValid == kMinValidHits || nValid == kLowValidHits) { - nUse = kMaxValidHitsForTruncation12; - } else if (nValid == kHighValidHits1 || nValid == kHighValidHits2) { - nUse = kMaxValidHitsForTruncation56; - } else if (nValid >= kMaxValidHits) { - nUse = kMaxValidHitsForTruncation7Plus; - } - - if (nUse > 0 && nValid >= nUse) { - std::sort(validToTs.begin(), validToTs.end()); - float sum = 0.0f; - for (size_t i = 0; i < nUse; ++i) { - sum += validToTs[i]; - } - truncatedMeanToT = sum / static_cast(nUse); - - if (enableLogPBins.value) { - histos.fill(HIST("hToTvsMom"), p, truncatedMeanToT); - histos.fill(HIST("hToTvsP"), rigidity, truncatedMeanToT); - h2dToTvsPperParticle[truePdgIdx]->Fill(rigidity, truncatedMeanToT); - } - if (enableLinearPBins.value) { - histos.fill(HIST("hToTvsMomLin"), p, truncatedMeanToT); - histos.fill(HIST("hToTvsPLin"), rigidity, truncatedMeanToT); - h2dToTvsPperParticleLin[truePdgIdx]->Fill(rigidity, truncatedMeanToT); + const auto& mcParticle = track.mcParticle(); + const int pdg = std::abs(mcParticle.pdgCode()); + const auto itSpecies = std::find(PdgCodes.begin(), PdgCodes.end(), pdg); + if (itSpecies == PdgCodes.end()) { + return -1.f; + } + const auto* pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo || pdgInfo->Charge() == 0) { + return -1.f; + } + const int iSpecies = std::distance(PdgCodes.begin(), itSpecies); + const float absCharge = std::abs(pdgInfo->Charge()) / 3.f; + const float rigidityTrue = mcParticle.p() / absCharge; + const float dedx = mTrackLut.sample(pdg, mcParticle.eta(), rigidityTrue, mRandom.Rndm()); + + if (dedx > 0.f) { + for (int i = 0; i < NumSpecies; ++i) { + float mean = 0.f, sigma = 0.f; + if (mTrackLut.getMeanSigma(PdgCodes[i], track.eta(), track.p(), mean, sigma)) { + nSigma[i] = (dedx - mean) / sigma; } } + } - nSigmaValues.fill(999.f); - - if (enableLogPBins.value && truncatedMeanToT > 0) { - for (size_t iHyp = 0; iHyp < mHypothesisPdgCodes.size(); ++iHyp) { - int hypPdgCode = mHypothesisPdgCodes[iHyp]; - int hypPdgIdx = mToTLUT->getPdgIndex(hypPdgCode); - - if (hypPdgIdx == -1) { - nSigmaValues[iHyp] = 999.f; - continue; - } - - float expectedToT = getToTMeanFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - float resolution = getToTResolutionFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - - if (expectedToT > 0 && resolution > 0) { - nSigmaValues[iHyp] = calculateNsigma(truncatedMeanToT, expectedToT, resolution); - h2dBarrelNsigmaTrue[truePdgIdx][iHyp]->Fill(rigidity, nSigmaValues[iHyp]); - } else { - nSigmaValues[iHyp] = 999.f; - } - } - } + if (primariesOnly.value && !mcParticle.isPhysicalPrimary()) { + return dedx; + } + const float rigidity = useTrueKinematics.value ? rigidityTrue : track.p(); + const float absEta = std::abs(useTrueKinematics.value ? mcParticle.eta() : track.eta()); + const float p = rigidity * absCharge; + h2dTracks[iSpecies]->Fill(rigidity, absEta); + if (dedx < 0.f) { + return dedx; + } + h2dTracksWithDedx[iSpecies]->Fill(rigidity, absEta); + h3dToTvsPEtaPerParticle[iSpecies]->Fill(rigidity, absEta, dedx); + if (enableLogPBins.value) { + histos.fill(HIST("hToTvsMom"), p, dedx); + histos.fill(HIST("hToTvsP"), rigidity, dedx); + h2dToTvsPperParticle[iSpecies]->Fill(rigidity, dedx); + } + if (enableLinearPBins.value) { + histos.fill(HIST("hToTvsMomLin"), p, dedx); + histos.fill(HIST("hToTvsPLin"), rigidity, dedx); + h2dToTvsPperParticleLin[iSpecies]->Fill(rigidity, dedx); + } + return dedx; + } - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); + void process(soa::Join const& tracks, + aod::McParticles const&) + { + std::array nSigma{}; + for (const auto& track : tracks) { + nSigma.fill(NoNsigma); + const float dedx = processTrack(track, nSigma); + tableUpgradeTrkPidSignals(dedx); + tableUpgradeTrkPids(nSigma[0], nSigma[1], nSigma[2], nSigma[3], nSigma[4], nSigma[5], nSigma[6], nSigma[7], nSigma[8]); } } }; From fa6a42ed52d5f508336bba58ec6e5b4432dd45da Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:53:48 +0200 Subject: [PATCH 346/648] [PWGCF] Add Track-TwoPhotonResonance pair task to femto framework (#18353) --- PWGCF/Femto/Core/closePairRejection.h | 59 ++++- PWGCF/Femto/Core/femtoUtils.h | 6 + PWGCF/Femto/Core/modes.h | 1 + PWGCF/Femto/Core/pairBuilder.h | 124 +++++++++++ PWGCF/Femto/Core/pairCleaner.h | 2 + PWGCF/Femto/Core/pairHistManager.h | 5 + PWGCF/Femto/Core/photonBuilder.h | 8 +- PWGCF/Femto/Core/twoPhotonResonanceBuilder.h | 27 +-- PWGCF/Femto/Tasks/CMakeLists.txt | 5 + .../femtoPairTrackTwoPhotonResonance.cxx | 204 ++++++++++++++++++ 10 files changed, 416 insertions(+), 25 deletions(-) create mode 100644 PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index 8e763b7affc..67c1e92f532 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -95,6 +95,7 @@ constexpr const char PrefixCprTrackLcPion[] = "CprTrackLcPion"; constexpr const char PrefixCprD0DaughterD0DaughterPos[] = "CprD0DaughterD0DaughterPos"; constexpr const char PrefixCprD0DaughterD0DaughterNeg[] = "CprD0DaughterD0DaughterNeg"; constexpr const char PrefixCprTrackResonanceDaughter[] = "CprTrackResonanceDaughter"; +constexpr const char PrefixCprTrackTwoPhotonResonanceDaughter[] = "CprTrackTwoPhotonResonanceDaughter"; constexpr const char PrefixCprTrackKinkDaughter[] = "CprTrackKinkDaughter"; constexpr const char PrefixCprV0DaughterV0DaughterPos[] = "CprV0DaughterV0DaughterPos"; constexpr const char PrefixCprV0DaughterV0DaughterNeg[] = "CprV0DaughterV0DaughterNeg"; @@ -113,6 +114,8 @@ using ConfCprTrackLcPion = ConfCpr; using ConfCprD0DaugherD0DaughterPos = ConfCpr; using ConfCprD0DaugherD0DaughterNeg = ConfCpr; using ConfCprTrackResonanceDaughter = ConfCpr; +// no cut by default: CPR is a no-op for two-photon resonances (see ClosePairRejectionTrackTwoPhotonResonance) +using ConfCprTrackTwoPhotonResonanceDaughter = ConfCpr; using ConfCprTrackKinkDaughter = ConfCpr; using ConfCprV0DaugherV0DaughterPos = ConfCpr; using ConfCprV0DaugherV0DaughterNeg = ConfCpr; @@ -152,6 +155,8 @@ constexpr char PrefixV0TwoTrackResonancePosMe[] = "CPR_V0Resonance_PosDau/ME/"; constexpr char PrefixV0TwoTrackResonanceNegMe[] = "CPR_V0Resonance_NegDau/ME/"; constexpr char PrefixTrackTwoTrackResonanceSe[] = "CPR_TrackResonanceDau/SE/"; constexpr char PrefixTrackTwoTrackResonanceMe[] = "CPR_TrackResonanceDau/ME/"; +constexpr char PrefixTrackTwoPhotonResonanceSe[] = "CPR_TrackTwoPhotonResonanceDau/SE/"; +constexpr char PrefixTrackTwoPhotonResonanceMe[] = "CPR_TrackTwoPhotonResonanceDau/ME/"; constexpr char PrefixTrackCascadeBachelorSe[] = "CPR_TrackCascadeBachelor/SE/"; constexpr char PrefixTrackCascadeBachelorMe[] = "CPR_TrackCascadeBachelor/ME/"; constexpr char PrefixTrackKinkSe[] = "CPR_TrackKink/SE/"; @@ -320,14 +325,22 @@ class CloseTrackRejection swapTracks = (mSwapDist(mRng) == 1); } - auto t1 = swapTracks ? track2 : track1; - auto t2 = swapTracks ? track1 : track2; + // get kinematics of the particles + // if configured, randomly swap particle 1 and particle 2 + float eta1 = swapTracks ? track2.eta() : track1.eta(); + float eta2 = swapTracks ? track1.eta() : track2.eta(); + float phi1 = swapTracks ? track2.phi() : track1.phi(); + float phi2 = swapTracks ? track1.phi() : track2.phi(); + float signedPt1 = swapTracks ? track2.signedPt() : track1.signedPt(); + float signedPt2 = swapTracks ? track1.signedPt() : track2.signedPt(); + int chargeAbs1 = swapTracks ? mChargeAbsTrack2 : mChargeAbsTrack1; + int chargeAbs2 = swapTracks ? mChargeAbsTrack1 : mChargeAbsTrack2; - mDeta = t1.eta() - t2.eta(); + mDeta = eta1 - eta2; for (size_t i = 0; i < TpcRadii.size(); i++) { - auto phistar1 = phistar(mMagField, TpcRadii.at(i), mChargeAbsTrack1 * t1.signedPt(), t1.phi()); - auto phistar2 = phistar(mMagField, TpcRadii.at(i), mChargeAbsTrack2 * t2.signedPt(), t2.phi()); + auto phistar1 = phistar(mMagField, TpcRadii.at(i), chargeAbs1 * signedPt1, phi1); + auto phistar2 = phistar(mMagField, TpcRadii.at(i), chargeAbs2 * signedPt2, phi2); if (phistar1 && phistar2) { mDphistar.at(i) = RecoDecay::constrainAngle(phistar1.value() - phistar2.value(), -o2::constants::math::PI); // constrain angular difference between -pi and pi mDphistarMask.at(i) = true; @@ -338,14 +351,14 @@ class CloseTrackRejection if (count > 0) { mAverageDphistar = std::accumulate(mDphistar.begin(), mDphistar.end(), 0.f) / count; // only average values if phistar could be computed } else { - mAverageDphistar = 0.f; // if computation at all radii fail, set it 0 + mAverageDphistar = 0.f; // if computation at all radii failed, set it 0 } if (mPlotAngularCorrelation) { - mPhi1 = t1.phi(); - mPhi2 = t2.phi(); - mEta1 = t1.eta(); - mEta2 = t2.eta(); + mPhi1 = phi1; + mPhi2 = phi2; + mEta1 = eta1; + mEta2 = eta2; } } @@ -731,6 +744,32 @@ class ClosePairRejectionTrackKink CloseTrackRejection mCtr; }; +// PCM photon daughters are, for now, not indexed into the track table (see photonBuilder.h): +// they are TPC-only tracks, so they cannot be confused for a global track the way two V0 +// daughters or a track and a V0 daughter can -- there is no detector-overlap effect to correct +// for. This is a no-op placeholder so Track-TwoPhotonResonance pairs can use the same generic +// pair-building code path. +template +class ClosePairRejectionTrackTwoPhotonResonance +{ + public: + template + void init(o2::framework::HistogramRegistry* /*registry*/, + std::map> const& /*specs*/, + T const& /*confCpr*/, + int /*absChargeTrack*/) + { + } + + void setMagField(float /*magField*/) {} + + template + [[nodiscard]] bool isClosePair(T1 const& /*track*/, T2 const& /*resonance*/, T3 const& /*photonTable*/, T4 const& /*pairHistManager*/) + { + return false; + } +}; + template class ClosePairRejectionMcParticleMcParticle { diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index d7a4a888e74..07b6e47705a 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -141,6 +141,12 @@ inline double getPdgMass(int pdgCode) case kOmegaMinus: mass = o2::constants::physics::MassOmegaMinus; break; + case kPi0: + mass = o2::constants::physics::MassPi0; + break; + case o2::constants::physics::kEta: + mass = o2::constants::physics::MassEta; + break; default: LOG(warn) << "PDG code " << pdgCode << " is not suppored. Return 0... "; } diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index 609effb965b..e4306101980 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -113,6 +113,7 @@ enum class Particle : o2::analysis::femto::datatypes::ParticleType { kKink = 4, kCascade = 5, kCharmHadron = 6, + kTwoPhotonResonance = 7, }; enum class McOrigin : o2::analysis::femto::datatypes::McOriginType { diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 0b408b64487..9ad834301e7 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -29,6 +29,7 @@ #include "PWGCF/Femto/Core/pairProcessHelpers.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" #include "PWGCF/Femto/Core/twoTrackResonanceHistManager.h" #include "PWGCF/Femto/Core/v0HistManager.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -1384,6 +1385,129 @@ class PairTrackV0Builder int mMixingDepth = 5; }; +// Track-TwoPhotonResonance pair builder. +// Note: close pair rejection is a no-op (ClosePairRejectionTrackTwoPhotonResonance) and pair cleaning +// reuses the plain TrackTrackPairCleaner (globalIndex-only), since photon daughters are, for +// now, not indexed into FTracks: they are TPC-only tracks, so they cannot be confused for a +// global track the way two V0 daughters or a track and a V0 daughter can. +template +class PairTrackTwoPhotonResonanceBuilder +{ + public: + PairTrackTwoPhotonResonanceBuilder() = default; + ~PairTrackTwoPhotonResonanceBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confResonanceSelection, + T4 const& confCpr, + T5 const& confMixing, + T6 const& confPairBinning, + T7 const& confPairCuts, + T8 const& confDauQaBinning, + std::map> const& colHistSpec, + std::map> const& trackHistSpec, + std::map> const& resonanceHistSpec, + std::map> const& dauHistSpec, + std::map> const& pairHistSpec, + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManager.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto resonanceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || resonanceSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, resonanceSlice, photonTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManager; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManager; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprSe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprMe; + paircleaner::TrackTrackPairCleaner mPcSe; + paircleaner::TrackTrackPairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; +}; + template +#include #include #include #include @@ -31,6 +32,7 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include @@ -58,26 +60,27 @@ using ConfEtaFilters = ConfTwoPhotonResonanceFilters; // unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no momentum-threshold PID // switch: photon daughters are unordered and have no momentum-dependent PID regime. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax) \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ - o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ - o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ +#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCodeAbs) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCodeAbs), "Absolute PDG code of the resonance, used to set its mass for kstar/kt/mt (not the reconstructed two-photon mass)"}; \ + o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ o2::framework::Configurable dau2Mask{"dau2Mask", 0, "Bitmask required for second photon daughter"}; struct ConfPi0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Pi0Selection1"); - TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f) + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f, kPi0) }; struct ConfEtaSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("EtaSelection1"); - TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f) + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f, o2::constants::physics::kEta) }; #undef TWOPHOTONRESONANCE_DEFAULT_SELECTION diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index 6d4bb461d62..1c31864d7f7 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -79,6 +79,11 @@ o2physics_add_dpl_workflow(femto-pair-track-two-track-resonance PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-pair-track-two-photon-resonance + SOURCES femtoPairTrackTwoPhotonResonance.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-pair-v0-two-track-resonance SOURCES femtoPairV0TwoTrackResonance.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx new file mode 100644 index 00000000000..2ed5afba3c2 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -0,0 +1,204 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairTrackTwoPhotonResonance.cxx +/// \brief Task that computes correlation between tracks and resonances decaying into two photons (pi0, eta) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairTrackTwoPhotonResonance { + + // setup tables + using Collisions = o2::soa::Join; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks + trackbuilder::ConfTrackSelection1 trackSelection; + trackhistmanager::ConfTrackBinning1 confTrackBinning; + o2::framework::Partition trackPartition = MAKE_TRACK_PARTITION(trackSelection); + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + + // setup for photon daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + // setup pi0s + twophotonresonancebuilder::ConfPi0Selection pi0Selection; + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(pi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + // setup etas + twophotonresonancebuilder::ConfEtaSelection etaSelection; + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(etaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + // setup for track-pi0 pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1, + twophotonresonancehistmanager::PrefixPi0, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + pairhistmanager::PrefixTrackPi0Se, + pairhistmanager::PrefixTrackPi0Me, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kPi0> + pairTrackPi0Builder; + + // setup for track-eta pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1, + twophotonresonancehistmanager::PrefixEta, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + pairhistmanager::PrefixTrackEtaSe, + pairhistmanager::PrefixTrackEtaMe, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kEta> + pairTrackEtaBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoPhotonResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr (no-op for two-photon resonances: PCM photon daughters are not indexed into the track + // table, so there is no trajectory to compare against a track's -- see closePairRejection.h) + closepairrejection::ConfCprTrackTwoPhotonResonanceDaughter confCpr; + + void init(o2::framework::InitContext&) + { + + if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent) + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent) > 1) { + LOG(fatal) << "Can only process SE/ME for pi0-tracks or eta-tracks"; + } + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histograms + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + auto trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + auto dauHistSpec = photonhistmanager::makePhotonHistSpecMap(confPhotonDauBinning); + auto cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + // setup for pi0 + if (doprocessPi0SameEvent || doprocessPi0MixedEvent) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confPi0Binning); + auto pairTrackPi0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackPi0Builder.init(&hRegistry, confCollisionBinning, trackSelection, pi0Selection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, pi0HistSpec, dauHistSpec, pairTrackPi0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + + // setup for eta + if (doprocessEtaSameEvent || doprocessEtaMixedEvent) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confEtaBinning); + auto pairTrackEtaHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackEtaBuilder.init(&hRegistry, confCollisionBinning, trackSelection, etaSelection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, etaHistSpec, dauHistSpec, pairTrackEtaHistSpec, cprHistSpec, pairCleanerHistSpec); + } + + hRegistry.print(); + }; + + void processPi0SameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processSameEvent(col, trackPartition, pi0Partition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0SameEvent, "Enable same event processing for tracks and pi0s", true); + + void processPi0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processMixedEvent(cols, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEvent, "Enable mixed event processing for tracks and pi0s", true); + + void processEtaSameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processSameEvent(col, trackPartition, etaPartition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaSameEvent, "Enable same event processing for tracks and etas", false); + + void processEtaMixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processMixedEvent(cols, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEvent, "Enable mixed event processing for tracks and etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} From a1e75aa429b40fd305ef27a9fe8a6524236d9565 Mon Sep 17 00:00:00 2001 From: MRazza <118839113+MRazza879@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:03:12 +0200 Subject: [PATCH 347/648] [PWGHF] Add MC collision matching checks for tracks (#18348) Co-authored-by: Marta Razza --- PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index 9111456a5ff..93244015912 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -167,6 +167,10 @@ struct HfTaskDeuteronFromLb { qaHistos.add("MCReco/hDCAzVsPtDeFromBminus", "DCAz #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaB", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); qaHistos.add("MCReco/hDCAzVsPtDePrimary", "DCAz #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminusSameMC", "DCAxy from B^{-}, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC", "DCAxy from anti-Lambda_b, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC", "DCAxy from anti-hypertriton, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimarySameMC", "DCAxy primary, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("MCReco/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); @@ -454,6 +458,7 @@ struct HfTaskDeuteronFromLb { } auto mcParticle = track.mcParticle(); + const bool isSameMcCollision = collision.has_mcCollision() && (mcParticle.mcCollisionId() == collision.mcCollisionId()); const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; @@ -539,16 +544,25 @@ struct HfTaskDeuteronFromLb { if (passFinalSel) { if (motherPdg == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminusSameMC"), track.pt(), dca[0]); + } qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); qaHistos.fill(HIST("MCReco/hDCAxy-FromBMeson"), dca[0]); qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); } else if (motherPdg == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaB"), track.pt(), dca[0]); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[0]); + } qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiLambdaB"), dca[0]); qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); } else if (motherPdg == pdgCodeHypertriton) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertriton"), track.pt(), dca[0]); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[0]); + } qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiHypertriton"), dca[0]); qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); @@ -556,6 +570,9 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimary"), track.pt(), dca[0]); qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimary"), track.pt(), dca[1]); qaHistos.fill(HIST("MCReco/hDCAxy-Primary"), dca[0]); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimarySameMC"), track.pt(), dca[0]); + } qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); } } From ea53c75ac9e96f7a6ecd7b9b3fb7eebddff402d1 Mon Sep 17 00:00:00 2001 From: Nicolas Strangmann <77485327+nstrangm@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:28:47 +0200 Subject: [PATCH 348/648] [PWGEM/PhotonMeson] Enable cross section normalization in generator task (#18355) Co-authored-by: Nicolas Strangmann --- .../PhotonMeson/Tasks/mcGeneratorStudies.cxx | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx index 6aebaf77f63..6a0ca911f01 100644 --- a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx +++ b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx @@ -13,6 +13,8 @@ /// \brief Task that produces the generated pT spectrum of a given particle for MC studies based on on the fly MC simulations /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + #include #include #include @@ -22,6 +24,9 @@ #include #include +#include +#include + #include using namespace o2; @@ -31,23 +36,64 @@ struct MCGeneratorStudies { HistogramRegistry mHistManager{"MCGeneratorStudyHistograms"}; Configurable cfgSelectedParticleCode{"cfgSelectedParticleCode", 111, "PDG code of the particle to be investigated"}; Configurable cfgMaxZVertex{"cfgMaxZVertex", 10, "Maximum absolute z-vertex distance (cm)"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapditity of selected generated particles"}; - ConfigurableAxis cfgMultiplicityBinning{"cfgMultiplicityBinning", {1000, 0, 10000}, "Binning used for the binning of the number of particles in the event"}; + Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapidity of selected generated particles"}; expressions::Filter zVertexFilter = aod::mccollision::posZ < cfgMaxZVertex && aod::mccollision::posZ > -cfgMaxZVertex; void init(InitContext const&) { - mHistManager.add("Multiplicity", "Number of generated particles per MC collision;#bf{#it{N} (Multiplicity)};#bf{#it{N}_{MC collisions}}", HistType::kTH1F, {cfgMultiplicityBinning}); - mHistManager.add("YieldVsMultiplicity", "pT of selected particles in all MC collisions vs number of all generated particles in event;#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N} (Multiplicity)};#bf{#it{N}}", HistType::kTH2F, {{200, 0, 20}, cfgMultiplicityBinning}); + mHistManager.add("Yield", Form("Yield of primary selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("FullYield", Form("Yield of selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("MCGenXSec", "Generated cross section = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->GetXaxis()->SetBinLabel(1, "#sigma_{gen} (b)"); + hX->GetXaxis()->SetBinLabel(2, "nStreams"); + hX->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hX->SetBinError(2, 0); + mHistManager.add("RapidityCut", "#Delta y = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hY = mHistManager.get(HIST("RapidityCut")); + hY->GetXaxis()->SetBinLabel(1, "2#Delta y"); + hY->GetXaxis()->SetBinLabel(2, "nStreams"); + hY->SetBinContent(1, 2 * cfgRapidityCut); + hY->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hY->SetBinError(2, 0); + mHistManager.add("EventWeight", "MC collision weight;Event weight;#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{1000, 0, 10}}); + mHistManager.add("MCGenXSecDistribution", "Generated cross section distribution;#sigma_{gen} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("MCGenXSecErrDistribution", "Generated cross section error distribution;#sigma_{gen}^{err} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("EventWeightSum", "Sum of event weights", HistType::kTH1D, {{2, 0., 2.}}, true); + auto hE = mHistManager.get(HIST("EventWeightSum")); + hE->GetXaxis()->SetBinLabel(1, "#Sigma w"); + hE->GetXaxis()->SetBinLabel(2, "N_{MC Collisions}"); + mHistManager.add("zVertex", "zVertex position;#bf{z (cm)};#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{500, -25., 25.}}, true); } - void process(soa::Filtered::iterator const&, aod::McParticles const& mcParticles) + void process(soa::Filtered::iterator const& mcCollision, aod::McParticles const& mcParticles, aod::HepMCXSections const& crossSections) { - int nParticles = mcParticles.size(); - mHistManager.fill(HIST("Multiplicity"), nParticles); + const double w = mcCollision.weight(); + mHistManager.fill(HIST("EventWeight"), w); + mHistManager.fill(HIST("EventWeightSum"), 0.5, w); // Fill with event weight (used for normalization) + mHistManager.fill(HIST("EventWeightSum"), 1.5, 1); // Fill with number of MC collisions + mHistManager.fill(HIST("zVertex"), mcCollision.posZ(), w); + if (crossSections.size() == 1) { + auto const xs = crossSections.begin(); + const double sigmaGen = xs.xsectGen() * 1E-12; // From pb to b + const double sigmaGenErr = xs.xsectErr() * 1E-12; // From pb to b + mHistManager.fill(HIST("MCGenXSecDistribution"), sigmaGen); + mHistManager.fill(HIST("MCGenXSecErrDistribution"), sigmaGenErr); + + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->SetBinContent(1, sigmaGen); // Set bin content to latest generated cross section and uncertainty + hX->SetBinError(1, sigmaGenErr); + } else { + LOG(warn) << "Multiple cross sections found: " << crossSections.size(); + } for (auto& mcParticle : mcParticles) { if (mcParticle.pdgCode() == cfgSelectedParticleCode && std::abs(mcParticle.y()) < cfgRapidityCut) { - mHistManager.fill(HIST("YieldVsMultiplicity"), mcParticle.pt(), nParticles); + mHistManager.fill(HIST("FullYield"), mcParticle.pt(), w); + bool isPrimary = mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator(); + bool isFromWD = (aod::pwgem::photonmeson::utils::mcutil::IsFromWD(mcCollision, mcParticle, mcParticles)) > 0; + if (isPrimary && !isFromWD) { + mHistManager.fill(HIST("Yield"), mcParticle.pt(), w); + } } } } From 28a07f2a6339071bbeacfd9c89939ddcb1e35860 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Tue, 20 Oct 2026 00:39:37 +0900 Subject: [PATCH 349/648] [PWGLF] omega2012TrainingTable: write only extracted truth tables for MC (#18356) Co-authored-by: Bong-Hwi Lim --- PWGLF/DataModel/LFOmega2012MlTables.h | 34 +++ .../Resonances/omega2012TrainingTable.cxx | 201 +++++++++++++++++- 2 files changed, 226 insertions(+), 9 deletions(-) diff --git a/PWGLF/DataModel/LFOmega2012MlTables.h b/PWGLF/DataModel/LFOmega2012MlTables.h index dd81f009845..e17bde5d569 100644 --- a/PWGLF/DataModel/LFOmega2012MlTables.h +++ b/PWGLF/DataModel/LFOmega2012MlTables.h @@ -16,6 +16,8 @@ /// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. /// Only the input-object tables (events, cascades, V0s, tracks) are common; every event row belongs to one /// decay mode (column omDecayMode), so the object rows of the two modes never mix either. +/// Event rows are written only for collisions with at least one written candidate. In reconstructed MC the +/// object MC labels and the pruned McParticles rows replace the source MC tables, which are not written. /// /// Cumulative pass bits (each bit requires all lower bits): /// mode A: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 K0s selection, @@ -234,6 +236,38 @@ DECLARE_SOA_TABLE(Omega2012MlGenAudit, "AOD", "OMMLGENAUDIT", o2::soa::Index<>, omega2012ml::OmRecoCollisionId, omega2012ml::OmOriginalMcParticleId, omega2012ml::OmPdg, omega2012ml::OmDaughterPdg1, omega2012ml::OmDaughterPdg2, omega2012ml::OmGenChannel, omega2012ml::OmGenPt, omega2012ml::OmGenY); + +// MC truth of reconstructed MC: daughter labels of the written objects and the pruned source McParticles rows +// (Omega(2012) decay trees and ancestry of the daughter labels). Source rows are DF-local scalar values. +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlTrack, omega2012MlTrack, int, Omega2012MlTracks, "OmmlTracks", ""); +DECLARE_SOA_COLUMN(OmMcCollisionRow, omMcCollisionRow, int64_t); //! row of the MC collision in the input McCollisions table +DECLARE_SOA_COLUMN(OmCascadeMcLabels, omCascadeMcLabels, int[3]); //! McParticles rows of the cascade daughters, order of omCascadeIndices; -1 if absent +DECLARE_SOA_COLUMN(OmV0McLabels, omV0McLabels, int[2]); //! McParticles rows of the V0 daughters, order of omV0Indices; -1 if absent +DECLARE_SOA_COLUMN(OmMcLabel, omMcLabel, int); //! McParticles row of the track; -1 if absent +DECLARE_SOA_COLUMN(OmMcRow, omMcRow, int64_t); //! row of the particle in the input McParticles table +DECLARE_SOA_COLUMN(OmMcSelection, omMcSelection, uint8_t); //! 1 Omega(2012) decay tree, 2 ancestry of a written daughter label +DECLARE_SOA_COLUMN(OmNMothers, omNMothers, uint8_t); //! number of mothers in the input row +DECLARE_SOA_COLUMN(OmMotherRows, omMotherRows, int[2]); //! first two McParticles mother rows; -1 if absent +DECLARE_SOA_COLUMN(OmDaughterRows, omDaughterRows, int[2]); //! McParticles daughter slice (first, last) as stored in the input row +DECLARE_SOA_COLUMN(OmE, omE, float); +DECLARE_SOA_COLUMN(OmWeight, omWeight, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEventMcs, "AOD", "OMMLEVENTMC", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmMcCollisionRow); +DECLARE_SOA_TABLE(Omega2012MlCascadeMcs, "AOD", "OMMLCASCMC", + o2::soa::Index<>, omega2012ml::Omega2012MlCascadeId, omega2012ml::OmCascadeMcLabels); +DECLARE_SOA_TABLE(Omega2012MlV0Mcs, "AOD", "OMMLV0MC", + o2::soa::Index<>, omega2012ml::Omega2012MlV0Id, omega2012ml::OmV0McLabels); +DECLARE_SOA_TABLE(Omega2012MlTrackMcs, "AOD", "OMMLTRACKMC", + o2::soa::Index<>, omega2012ml::Omega2012MlTrackId, omega2012ml::OmMcLabel); +DECLARE_SOA_TABLE(Omega2012MlMcParticles, "AOD", "OMMLMCPART", + o2::soa::Index<>, omega2012ml::OmMcRow, omega2012ml::OmMcSelection, omega2012ml::OmPdg, + omega2012ml::OmMcCollisionRow, omega2012ml::OmNMothers, omega2012ml::OmMotherRows, + omega2012ml::OmDaughterRows, omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmE, omega2012ml::OmWeight); } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx index 18502ccedb1..3d2a397f6d6 100644 --- a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -14,9 +14,12 @@ /// \author Bong-Hwi Lim /// /// Mode A (XiK0s): processXiK0s, processXiK0sMC. Mode B (Xi1530K, Xi- pi+ K- with micro tracks): -/// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. +/// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. MC truth of reconstructed MC: processMcTruth. /// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); /// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). +/// Only collisions with written candidates get an event row. In reconstructed MC the daughter labels of the written +/// objects and the pruned McParticles rows (Omega(2012) decay trees, ancestry of the labels) carry the truth, so the +/// output needs no source table. #include "PWGLF/Core/Omega2012AnalysisCore.h" #include "PWGLF/Core/Omega2012MlFeatures.h" @@ -24,6 +27,7 @@ #include "PWGLF/DataModel/LFOmega2012MlTables.h" #include "PWGLF/DataModel/LFResonanceTables.h" +#include #include #include #include @@ -38,13 +42,19 @@ #include +#include #include #include #include #include +#include +#include #include #include #include +#include +#include +#include using namespace o2; using namespace o2::framework; @@ -59,7 +69,7 @@ static_assert(std::extent_v == ml::Xi struct Omega2012TrainingTable { using ResoCollisions = aod::ResoCollisions_001; - using ResoMCCols = soa::Join; + using ResoMCCols = soa::Join; using ResoMicroTracks = aod::ResoMicroTracks_001; using ResoMCMicroTracks = soa::Join; using ResoMCCascades = soa::Join; @@ -73,6 +83,12 @@ struct Omega2012TrainingTable { static constexpr unsigned int BitsPerFloat = 32; static constexpr uint32_t ByteMask = 0xffU; static constexpr int NBuildStatus = 6; + // Pruned McParticles: generations of Omega(2012) descendants and mother steps above a daughter label + static constexpr int McTreeDepth = 3; + static constexpr int MaxAncestorSteps = 8; + static constexpr uint8_t McSelectionTree = 1; + static constexpr uint8_t McSelectionAncestry = 2; + static constexpr std::size_t NStoredMothers = 2; Produces mlEvents; Produces mlCascades; @@ -85,6 +101,11 @@ struct Omega2012TrainingTable { Produces mlXi1530KInputs; Produces mlXi1530KTruth; Produces mlGenAudit; + Produces mlEventMcs; + Produces mlCascadeMcs; + Produces mlV0Mcs; + Produces mlTrackMcs; + Produces mlMcParticles; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -103,8 +124,26 @@ struct Omega2012TrainingTable { Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + // Event row written before the first candidate of the collision + struct PendingEvent { + DecayMode mode = DecayMode::XiK0s; + int64_t recoCollision = -1; + float posZ = 0.f; + float bField = 0.f; + float centrality = 0.f; + float multiplicity = 0.f; + bool recINELgt0 = false; + bool isMC = false; + int64_t mcCollision = -1; + }; + Omega2012AnalysisCore core; + PendingEvent pendingEvent; int64_t mlEventRow = -1; + // MC tables of the current DF, set only by the reconstructed-MC process functions + aod::McParticles const* mcParticlesDf = nullptr; + aod::McTrackLabels const* mcTrackLabelsDf = nullptr; + std::unordered_set mlAncestryRows; std::unordered_map mlCascadeRows; std::unordered_map mlV0Rows; std::unordered_map mlTrackRows; @@ -124,6 +163,9 @@ struct Omega2012TrainingTable { if (doprocessMCTrue && !doprocessXiK0sMC && !doprocessXi1530KMCMicro) { LOG(fatal) << "processMCTrue requires processXiK0sMC or processXi1530KMCMicro"; } + if (doprocessMcTruth != (doprocessXiK0sMC || doprocessXi1530KMCMicro)) { + LOG(fatal) << "processMcTruth must be enabled exactly when processXiK0sMC or processXi1530KMCMicro is enabled"; + } if (omega2012MlParityRows < 0) { LOG(fatal) << "omega2012MlParityRows must not be negative"; } @@ -176,15 +218,84 @@ struct Omega2012TrainingTable { } template - void writeEvent(Collision const& collision, DecayMode mode) + void beginEvent(Collision const& collision, DecayMode mode) { mlCascadeRows.clear(); mlV0Rows.clear(); mlTrackRows.clear(); + mlEventRow = -1; // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. - mlEvents(static_cast(mode), static_cast(collision.globalIndex()), - collision.posZ(), collision.bMagField(), collision.cent(), collision.multiplicity(), collision.isRecINELgt0()); + pendingEvent = PendingEvent{mode, static_cast(collision.globalIndex()), collision.posZ(), collision.bMagField(), + collision.cent(), collision.multiplicity(), collision.isRecINELgt0(), false, -1}; + if constexpr (requires { collision.originalMcCollisionId(); }) { + pendingEvent.isMC = true; + pendingEvent.mcCollision = static_cast(collision.originalMcCollisionId()); + } + } + + void ensureEventRow() + { + if (mlEventRow >= 0) { + return; + } + mlEvents(static_cast(pendingEvent.mode), pendingEvent.recoCollision, pendingEvent.posZ, pendingEvent.bField, + pendingEvent.centrality, pendingEvent.multiplicity, pendingEvent.recINELgt0); mlEventRow = mlEvents.lastIndex(); + if (pendingEvent.isMC) { + mlEventMcs(mlEventRow, pendingEvent.mcCollision); + } + } + + template + void writeMcParticle(Particle const& particle, uint8_t selection) + { + const auto mothers = particle.mothersIds(); + std::array motherRows{-1, -1}; + for (std::size_t i = 0; i < NStoredMothers && i < mothers.size(); ++i) { + motherRows[i] = mothers[i]; + } + const auto daughters = particle.daughtersIds(); + std::array daughterRows{daughters[0], daughters[1]}; + const auto nMothers = static_cast(std::min(mothers.size(), std::numeric_limits::max())); + mlMcParticles(static_cast(particle.globalIndex()), selection, particle.pdgCode(), static_cast(particle.mcCollisionId()), + nMothers, motherRows.data(), daughterRows.data(), particle.px(), particle.py(), particle.pz(), particle.e(), particle.weight()); + } + + // McParticles row of a source track, -1 without MC tables or label + int mcLabel(int64_t trackId) const + { + if (mcTrackLabelsDf == nullptr) { + return -1; + } + const auto row = trackId - static_cast(mcTrackLabelsDf->offset()); + if (row < 0 || row >= static_cast(mcTrackLabelsDf->size())) { + return -1; + } + return mcTrackLabelsDf->rawIteratorAt(row).mcParticleId(); + } + + // Writes the label particle and its first-mother chain, once per DF + void writeAncestry(int label) + { + if (mcParticlesDf == nullptr) { + return; + } + int64_t row = label; + for (int step = 0; step <= MaxAncestorSteps; ++step) { + const auto local = row - static_cast(mcParticlesDf->offset()); + if (row < 0 || local < 0 || local >= static_cast(mcParticlesDf->size())) { + return; + } + const auto particle = mcParticlesDf->rawIteratorAt(local); + if (mlAncestryRows.insert(row).second) { + writeMcParticle(particle, McSelectionAncestry); + } + const auto mothers = particle.mothersIds(); + if (mothers.empty()) { + return; + } + row = mothers[0]; + } } template @@ -209,6 +320,14 @@ struct Omega2012TrainingTable { xi.nCrossedRowsPos(), xi.nCrossedRowsNeg(), xi.nCrossedRowsBach(), daughterIds.data()); const auto row = mlCascades.lastIndex(); mlCascadeRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlCascadeMcs(row, labels.data()); + } return row; } @@ -231,6 +350,14 @@ struct Omega2012TrainingTable { v0.nCrossedRowsPos(), v0.nCrossedRowsNeg(), daughterIds.data()); const auto row = mlV0s.lastIndex(); mlV0Rows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlV0Mcs(row, labels.data()); + } return row; } @@ -246,6 +373,11 @@ struct Omega2012TrainingTable { track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); const auto row = mlTracks.lastIndex(); mlTrackRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + const auto label = mcLabel(static_cast(track.trackId())); + writeAncestry(label); + mlTrackMcs(row, label); + } return row; } @@ -267,6 +399,7 @@ struct Omega2012TrainingTable { collision.globalIndex(), xi.globalIndex(), v0.globalIndex(), passBits, featureHash(pack.master)); ++parityLoggedXiK0s; } + ensureEventRow(); const auto cascadeRow = writeCascade(xi); const auto v0Row = writeV0(v0); mlXiK0sCandidates(mlEventRow, cascadeRow, v0Row, @@ -308,6 +441,7 @@ struct Omega2012TrainingTable { values.chargePattern, passBits, featureHash(pack.master)); ++parityLoggedXi1530K; } + ensureEventRow(); const auto cascadeRow = writeCascade(xi); const auto pionRow = writeTrack(pion); const auto kaonRow = writeTrack(kaon); @@ -330,7 +464,7 @@ struct Omega2012TrainingTable { template void exportXiK0s(Collision const& collision, Cascades const& cascades, V0s const& v0s) { - writeEvent(collision, DecayMode::XiK0s); + beginEvent(collision, DecayMode::XiK0s); // Selection only: the analysis histograms belong to the Omega(2012) analysis task core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, true, nullptr, nullptr, nullptr, [&](auto const& coll, auto const& xi, auto const& v0, XiK0sCandidateValues const& values, uint16_t passBits) { @@ -341,7 +475,7 @@ struct Omega2012TrainingTable { template void exportXi1530K(Collision const& collision, Cascades const& cascades, Tracks const& tracks) { - writeEvent(collision, DecayMode::Xi1530K); + beginEvent(collision, DecayMode::Xi1530K); core.forEachXi1530KCandidate(histos, collision, cascades, tracks, nullptr, true, nullptr, nullptr, nullptr, [&](auto const& coll, auto const& xi, auto const& pion, auto const& kaon, Xi1530KCandidateValues const& values, uint16_t passBits) { @@ -358,12 +492,19 @@ struct Omega2012TrainingTable { } PROCESS_SWITCH(Omega2012TrainingTable, processXiK0s, "Write XiK0s-mode candidates from data", true); - void processXiK0sMC(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCV0s const& v0s) + void processXiK0sMC(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCV0s const& v0s, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; exportXiK0s(collision, cascades, v0s); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; } PROCESS_SWITCH(Omega2012TrainingTable, processXiK0sMC, "Write XiK0s-mode candidates with truth from reconstructed MC", false); @@ -376,12 +517,19 @@ struct Omega2012TrainingTable { } PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMicro, "Write Xi1530K-mode candidates from data micro v001 tables", false); - void processXi1530KMCMicro(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCMicroTracks const& tracks) + void processXi1530KMCMicro(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCMicroTracks const& tracks, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; exportXi1530K(collision, cascades, tracks); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; } PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMCMicro, "Write Xi1530K-mode candidates with truth from reconstructed MC micro v001 tables", false); @@ -396,6 +544,41 @@ struct Omega2012TrainingTable { }); } PROCESS_SWITCH(Omega2012TrainingTable, processMCTrue, "Write generated Omega(2012) parents of selected reconstructed MC events", false); + + // Once per DF: every Omega(2012) with McTreeDepth generations of descendants. The ancestry set is reset here, so + // the result does not depend on the order of this call relative to the per-collision process functions. + void processMcTruth(aod::McParticles const& mcParticles) + { + mlAncestryRows.clear(); + const auto offset = static_cast(mcParticles.offset()); + const auto nRows = static_cast(mcParticles.size()); + std::unordered_set treeRows; + std::vector> stack; + for (const auto& parent : mcParticles) { + if (std::abs(parent.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + stack.assign(1, {static_cast(parent.globalIndex()), 0}); + while (!stack.empty()) { + const auto [row, depth] = stack.back(); + stack.pop_back(); + const auto local = row - offset; + if (local < 0 || local >= nRows || !treeRows.insert(row).second) { + continue; + } + const auto particle = mcParticles.rawIteratorAt(local); + writeMcParticle(particle, McSelectionTree); + const auto daughters = particle.daughtersIds(); + if (depth >= McTreeDepth || daughters[0] < 0 || daughters[1] < daughters[0]) { + continue; + } + for (int64_t daughter = daughters[0]; daughter <= daughters[1]; ++daughter) { + stack.emplace_back(daughter, depth + 1); + } + } + } + } + PROCESS_SWITCH(Omega2012TrainingTable, processMcTruth, "Write the pruned McParticles rows of the Omega(2012) decay trees; required with the reconstructed-MC process functions", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 32f9890fc857dce77e63b51c47f775084f1e6df2 Mon Sep 17 00:00:00 2001 From: JimunLee Date: Tue, 20 Oct 2026 00:40:04 +0900 Subject: [PATCH 350/648] [PWGLF] Added unfolding process and modified MC inv mass process (#18288) Co-authored-by: jimun_lee --- PWGLF/Tasks/Resonances/CMakeLists.txt | 4 +- .../{kstarInOO.cxx => kstarInJets.cxx} | 682 ++++++++++-------- 2 files changed, 390 insertions(+), 296 deletions(-) rename PWGLF/Tasks/Resonances/{kstarInOO.cxx => kstarInJets.cxx} (82%) diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 8978355db6f..238fc095c38 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -269,8 +269,8 @@ o2physics_add_dpl_workflow(double-resonance-scan PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(kstarinoo - SOURCES kstarInOO.cxx +o2physics_add_dpl_workflow(kstarinjets + SOURCES kstarInJets.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/kstarInOO.cxx b/PWGLF/Tasks/Resonances/kstarInJets.cxx similarity index 82% rename from PWGLF/Tasks/Resonances/kstarInOO.cxx rename to PWGLF/Tasks/Resonances/kstarInJets.cxx index 7126483b17c..d62d47cd5dc 100644 --- a/PWGLF/Tasks/Resonances/kstarInOO.cxx +++ b/PWGLF/Tasks/Resonances/kstarInJets.cxx @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file kstarInOO.cxx -/// \brief the pT spectra of k*0(892) resonance analysis in OO collisions +/// \file kstarInJets.cxx +/// \brief the pT spectra of k*0(892) resonance analysis in Jets collisions /// \author Jimun Lee #include "PWGJE/Core/JetDerivedDataUtilities.h" @@ -28,11 +28,8 @@ #include #include #include -#include #include -#include #include -#include #include #include #include @@ -46,6 +43,7 @@ #include #include +#include #include #include #include @@ -59,8 +57,9 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace ROOT::Math; -struct kstarInOO { +struct kstarInJets { SliceCache cache; Preslice perCollision = aod::track::collisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -70,6 +69,8 @@ struct kstarInOO { //| Selection //| //================================== + Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; + // Event Selection Configurable cfgEventSelections{"cfgEventSelections", "sel8", "Set event selection"}; Configurable cfgEventVtxCut{"cfgEventVtxCut", 10.0, "V_z cut selection"}; @@ -86,11 +87,11 @@ struct kstarInOO { Configurable cfgTrackPrimaryTrack{"cfgTrackPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz Configurable cfgTrackConnectedToPV{"cfgTrackConnectedToPV", true, "PV contributor track selection"}; // PV Contriuibutor Configurable cfgTrackGlobalWoDCATrack{"cfgTrackGlobalWoDCATrack", true, "Global track selection without DCA"}; - // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNTCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) // TPC - Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 50, "nFindable TPC Clusters"}; + Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 0, "nFindable TPC Clusters"}; // origin = 50 Configurable cfgTrackTPCCrossedRows{"cfgTrackTPCCrossedRows", 70, "nCrossed TPC Rows"}; - Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 1.2, "nRowsOverFindable TPC CLusters"}; + Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 0, "nRowsOverFindable TPC CLusters"}; // origin = 1.2 Configurable cfgTrackTPCChi2{"cfgTrackTPCChi2", 4.0, "nTPC Chi2 per Cluster"}; // ITS @@ -103,24 +104,12 @@ struct kstarInOO { Configurable cfgTrackCirclePIDCut{"cfgTrackCirclePIDCut", true, "Enables PID cut shape circle switch"}; Configurable cfgTrackCircleValue{"cfgTrackCircleValue", 2, "Enables TOF TPC PID circle cut value"}; Configurable cfgTrackTOFHard{"cfgTrackTOFHard", false, "Enables TOF Hard"}; - Configurable isMIDCut{"isMIDCut", false, "Enables Mis-IDentified PID cut"}; - Configurable cfgTrackTPCPIDnSig{"cfgTrackTPCPIDnSig", 4.0, "nTPC PID sigma"}; Configurable cfgTrackTOFPIDnSig{"cfgTrackTOFPIDnSig", 4.0, "nTOF PID sigma"}; - Configurable cfgMIDnSig{"cfgMIDnSig", 1.5, "nTPC Mis-IDentification Particle sigma"}; - - Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; - - // Mixing - ConfigurableAxis cfgBinsMixMult{"cfgBinsCent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0}, "Binning of the centrality axis"}; - ConfigurableAxis cfgBinsMixVtx{"cfgBinsMixVtx", {VARIABLE_WIDTH, -10.0f, -5.f, 0.f, 5.f, 10.f}, "Mixing bins - z-vertex"}; - Configurable cfgMixNMixedEvents{"cfgMixNMixedEvents", 10, "Number of mixed events per event"}; - Configurable cfgVtxMixCut{"cfgVtxMixCut", 10, "Vertex Mix Cut"}; // MCGen Configurable cfgForceGenReco{"cfgForceGenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; Configurable cfgReqMcEffPID{"cfgReqMcEffPID", false, "Request McEfficiency PID"}; - Configurable cfgReqMcEffTrackQA{"cfgRecMcEffTrackQA", false, "Enable Jet Cut on Trig"}; // Pair Configurable cfgMinvNBins{"cfgMinvNBins", 300, "Number of bins for Minv axis"}; @@ -135,10 +124,11 @@ struct kstarInOO { Configurable cfgJetQAHistos{"cfgJetQAHistos", false, "Enable Jet QA Histos"}; Configurable cfgMCHistos{"cfgMCHistos", false, "Enable MC Histos"}; - Configurable cfgMixedHistos{"cfgMixedHistos", false, "Enable Mixed Histos"}; Configurable cfgJetHistos{"cfgJetHistos", false, "Enable Jet Histos"}; Configurable cfgJetMCHistos{"cfgJetMCHistos", false, "Enable Jet MC Histos"}; Configurable cfgJetdRHistos{"cfgJetdRHistos", false, "Enable Jet dR QA Histos"}; + Configurable cfgMatchedGenHistos{"cfgMatchedGenHistos", false, "Enable Matched Gen Histos"}; + Configurable cfgCutonTrig{"cfgCutonTrig", false, "Enable Jet Cut on Trig"}; //====================== @@ -168,9 +158,10 @@ struct kstarInOO { void init(o2::framework::InitContext&) { // HISTOGRAMS - const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; - const AxisSpec axisPhi{200, -1, +7, "#phi"}; - const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{200, -1, 7, "#phi"}; + const AxisSpec axisPt{100, 0, 100, "#it{p}_{T}"}; // Jet pT + const AxisSpec ptAxis = {100, 0, 100.0}; // Particle pT const AxisSpec pidAxis = {120, -6, 6}; const AxisSpec minvAxis = {cfgMinvNBins, cfgMinvMin, cfgMinvMax}; const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; @@ -219,9 +210,6 @@ struct kstarInOO { histos.add("QA_kaon_TPC_TOF_BC", "QA_kaon_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); histos.add("QA_track_pT_BC", "QA_track_pT_BC", kTH1F, {{13, 0.0, 13.0}}); - histos.add("Possible_MID_Pion_TPC", "Possible_MID_Pion_TPC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("Possible_MID_Kaon_TPC", "Possible_MID_Kaon_TPC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); @@ -240,6 +228,7 @@ struct kstarInOO { histos.add("QA_kaon_TPC_TOF_AC", "QA_kaon_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); histos.add("QA_track_pT_AC", "QA_track_pT_AC", kTH1F, {{13, 0.0, 13.0}}); } + if (cfgJetQAHistos) { histos.add("nTriggerQA", "nTriggerQA", kTH1F, {{8, 0.0, 8.0}}); @@ -258,8 +247,6 @@ struct kstarInOO { histos.add("mcpjet_eta", "MC particle-level Jet Eta", kTH1F, {{100, -1.0, 1.0}}); histos.add("mcpjet_phi", "MC particle-level Jet Phi", kTH1F, {{80, -1.0, 7.0}}); - histos.add("nJet_6_8", "MC particle-level nJet pT 6 < jet pT < 8 (GeV/c)", kTH1F, {{2000, 0., 100.}}); - histos.add("Gen_KstarPt_All_BR", "Generated K*0 All pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); histos.add("Gen_Kstar_BR", "Generated K*0 BR", kTH1F, {minvAxis}); histos.add("Gen_KstarPt_BR", "Generated K*0 pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); @@ -268,6 +255,9 @@ struct kstarInOO { histos.add("dR_taggedjet_pion", "dR between tagged jet and pion wo pT cut", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_taggedjet_all", "dR between tagged jet and kpi", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_taggedjet_all_6_8", "dR between tagged jet and kpi 6 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_all_8_12", "dR between tagged jet and kpi 8 < jet pT < 12", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + + histos.add("dR_taggedjet_0_8", "dR between tagged jet and kpi 0 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_kstar_pion", "dR between kstar and pion", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); histos.add("dR_kstar_kaon", "dR between kstar and kaon", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); @@ -275,6 +265,7 @@ struct kstarInOO { histos.add("Kstar_pT_INJet_6_8", "K*0 pT Inside 6-8 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("Kstar_pT_INJet_8_10", "K*0 pT Inside 8-10 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("Kstar_pT_INJet_10_12", "K*0 pT Inside 10-12 Jet", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_INJet_8_12", "K*0 pT Inside 8-12 Jet", kTH1F, {{1000, 0., 50.}}); histos.add("JetpT_8to", "Jet pT 8 to", kTH1F, {{2000, 0., 100.}}); histos.add("recoveredJetpT_6_8to8", "Recovered Jet pT 6-8 --> 8", kTH1F, {{2000, 0., 100.}}); @@ -292,17 +283,13 @@ struct kstarInOO { histos.add("JetMigration", "bin to bin migration", {HistType::kTH2F, {{150, 0.0, 15.0, "True jet pT (GeV/c)"}, {150, 0., 15., "Recovered jet pT (GeV/c)"}}}); } - //////////////////////////////////// + /////////////////////////////////// histos.add("nEvents", "nEvents", kTH1F, {{7, 0.0, 7.0}}); histos.add("hUSS_KPi", "hUSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hLSS_KPi", "hLSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hUSS_PiK", "hUSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); histos.add("hLSS_PiK", "hLSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - if (cfgMixedHistos) { - histos.add("hUSS_KPi_Mix", "hUSS_KPi_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK_Mix", "hUSS_PiK_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - } if (cfgMCHistos) { histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{7, 0.0, 7.0}}); histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); @@ -322,23 +309,26 @@ struct kstarInOO { histos.add("hGen_pT_Kstar", "Gen_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); histos.add("hRec_pT_Kstar", "Rec_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRec_pT", "EffRec_pT (GeV/c)", kTH1F, {{1600, 0., 80.}}); - histos.add("hEffRecTest0_pT", "EffRecTest0_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest1_pT", "EffRecTest1_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest2_pT", "EffRecTest2_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest3_pT", "EffRecTest3_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest4_pT", "EffRecTest4_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest5_pT", "EffRecTest5_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest6_pT", "EffRecTest6_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest7_pT", "EffRecTest7_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest8_pT", "EffRecTest8_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); + } + + if (cfgMatchedGenHistos) { + histos.add("nEvents_MCGen_MATCHED", "nEvents_MCGen_MATCHED", kTH1F, {{7, 0.0, 7.0}}); + histos.add("matchedJets_pT", "#it{p}_{T,jet part}; #it{p}_{T,jet det}", {HistType::kTH2F, {{100, 0., 20.}, {400, -20., 20.0}}}); + histos.add("matchedJets_eta", "#eta_{jet part}; #eta_{jet det}", {HistType::kTH2F, {{100, -1.0, 1.0}, {400, -20.0, 20.0}}}); + histos.add("matchedJets_phi", "#phi_{jet part}; #phi_{jet det}", {HistType::kTH2F, {{80, -1.0, 7.}, {400, -20.0, 20.}}}); + + histos.add("NoMatchedJets_pT", "Gen. No Matched detector-level Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_Matched_rand0", "Gen. Response Matrix Matched 0", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); + histos.add("RespGen_Matched_rand1", "Gen. Response Matrix Matched 1", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); - histos.add("hEffRecLowPtKstarDaughter", "EffRecLow Kstar daughter_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); + // ALL Jets + histos.add("nEvents_MCGen_MATCHED_allJet", "nEvents_MCGen_MATCHED_allJet", kTH1F, {{7, 0.0, 7.0}}); + histos.add("NoMatchedJets_pT_allJet", "Gen. No Matched detector-level ALL Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_MatchedJet_TEST_allJet", "Gen. Response Matrix Matched ALL Jet Test", kTHnSparseD, {axisPt, axisPt}); + histos.add("RespGen_Matrix_Matched_allJet", "Gen. Response Matrix Matched ALL Jet", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); - histos.add("hEffGen_pT", "EffGen_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hMotherPdg1", "hMotherPdg1", kTH1F, {{5000, 0., 5000.}}); - histos.add("hMotherPdg2", "hMotherPdg2", kTH1F, {{5000, 0., 5000.}}); + histos.add("RespGen_MatchedJet_TEST_rand0_allJet", "Gen. Response Matrix Matched ALL Jet TEST", kTHnSparseD, {axisPt, axisPt}); } // Jet @@ -368,14 +358,6 @@ struct kstarInOO { using JetTrackCandidatesMC = soa::Join; - // For Mixed Event - using BinningType = ColumnBinningPolicy; - - Partition kaon = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pion = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - Partition kaonMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pionMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - double massKa = o2::constants::physics::MassKPlus; double massPi = o2::constants::physics::MassPiMinus; @@ -459,10 +441,13 @@ struct kstarInOO { } // pion pid Selection } // fill QA + // enum class objectType { Inclusive, + // Jets, + // RecoParticle, + // JetsRecoParticle }; + enum class objectType { Inclusive, - Jets, - RecoParticle, - JetsRecoParticle }; + Jets }; template void fillMinv(objectType type, const TrackType& trk1, const TrackType& trk2, const ROOT::Math::PxPyPzMVector& lReso, double centrality, double jetpt, bool flip) { @@ -502,21 +487,6 @@ struct kstarInOO { } break; - case objectType::RecoParticle: - if (cfgMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec"), centrality, lReso.Pt(), lReso.M()); - } - } - break; - - case objectType::JetsRecoParticle: - if (cfgJetMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - } - } - break; } // switch } // fillMinv //====================================================================== @@ -658,24 +628,6 @@ struct kstarInOO { return true; }; - template - bool isMIDPion(const TrackPID& candidate, const bool QA) - { - if (cfgTrackCutQA && QA) { - histos.fill(HIST("Possible_MID_Pion_TPC"), candidate.tpcNSigmaKa(), candidate.tofNSigmaKa()); - } - return std::abs(candidate.tpcNSigmaKa()) < cfgMIDnSig && std::abs(candidate.tpcNSigmaPi()) < cfgMIDnSig; - } - - template - bool isMIDKaon(const TrackPID& candidate, const bool QA) - { - if (cfgTrackCutQA && QA) { - histos.fill(HIST("Possible_MID_Kaon_TPC"), candidate.tpcNSigmaPi(), candidate.tofNSigmaPi()); - } - return std::abs(candidate.tpcNSigmaPi()) < cfgMIDnSig && std::abs(candidate.tpcNSigmaKa()) < cfgMIDnSig; - } - template bool trackPIDKaon(const TrackPID& candidate, const bool QA) { @@ -727,8 +679,8 @@ struct kstarInOO { // TPC & TOF if (tpcPIDPassed && tofPIDPassed) { - // if (cfgTrackCutQA && QA) - // fillQA(true, candidate, 3); + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 3); return true; } return false; @@ -786,8 +738,8 @@ struct kstarInOO { // TPC & TOF if (tpcPIDPassed && tofPIDPassed) { - // if (cfgTrackCutQA && QA) - // fillQA(true, candidate, 4); + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 4); return true; } return false; @@ -806,18 +758,6 @@ struct kstarInOO { return {}; } - if (isMIDCut && isMIDPion(trk1, QA)) { - return {}; - } - if (isMIDCut && isMIDKaon(trk2, QA)) { - return {}; - } - - if (cfgTrackCutQA && QA) { - fillQA(true, trk1, 3); - fillQA(true, trk2, 4); - } - ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; if (!flip) { lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); @@ -867,10 +807,10 @@ struct kstarInOO { mothers2PDG.push_back(particle2_mom.pdgCode()); } - if (mothers1PDG[0] != Kstar0PDG) { + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { return {}; // mother not K*0 } - if (mothers2PDG[0] != Kstar0PDG) { + if (std::abs(mothers2PDG[0]) != Kstar0PDG) { return {}; // mothers not K*0 } if (mothers1[0] != mothers2[0]) { @@ -883,7 +823,8 @@ struct kstarInOO { return {}; } - double track1_mass, track2_mass; + double track1_mass = 0; + double track2_mass = 0; if (std::abs(particle1.pdgCode()) == PionPDG) { track1_mass = massPi; } else { @@ -916,13 +857,10 @@ struct kstarInOO { } // TrueReconstruction template - void TrackSlicing(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA) + void TrackSlicing(const CollisionType& collision, const TracksType& tracks, const bool QA) { - auto tracks1 = kaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pion->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { for (bool flip : {false, true}) { auto lReso = minvReconstruction(trk1, trk2, QA, flip); if (lReso.M() < 0) { @@ -935,13 +873,10 @@ struct kstarInOO { } // TrackSlicing template - void TrackSlicingMC(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA) + void TrackSlicingMC(const CollisionType& collision, const TracksType& tracks, const bool QA) { - auto tracks1 = kaonMC->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pionMC->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { continue; } @@ -958,12 +893,15 @@ struct kstarInOO { //| True Reconstruction //============================ auto lTrueReso = TrueReconstruction(trk1, trk2); - fillMinv(objectType::RecoParticle, trk1, trk2, lTrueReso, centrality, -1.0, false); + if (cfgMCHistos) { + histos.fill(HIST("hUSS_TrueRec"), centrality, lTrueReso.Pt(), lTrueReso.M()); + } + } // tracks } // TrackSlicingMC template - double DistinguishJets(const JetType& jets, const ROOT::Math::PxPyPzMVector& lResonance) + double DistinguishJets(const JetType& jets, ROOT::Math::PxPyPzMVector& lResonance) { if (cDebugLevel > 0) { LOG(info) << "Found multiple jets to the same phi."; @@ -975,10 +913,7 @@ struct kstarInOO { double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); double etadiff = jet.eta() - lResonance.Eta(); double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); - if (R < cfgJetR && bestR == 0) { - bestR = R; - bestJetpT = jet.pt(); - } else if (R < bestR) { + if (R < cfgJetR && (bestR == 0 || R < bestR)) { bestR = R; bestJetpT = jet.pt(); } @@ -986,6 +921,28 @@ struct kstarInOO { return bestJetpT; } + template + double DistinguishJetsMC(const Jet_pt& jet_pt, const Jet_phi& jet_phi, const Jet_eta& jet_eta, const Resonance& lResonance) + { + if (cDebugLevel > 0) { + LOG(info) << "Multiple jets fit to the same K*0. Time to find the best K*0-jet"; + } + + double best_R = 0; + double best_jetpt = 0; + for (std::vector::size_type i = 0; i < jet_pt.size(); i++) { + double phidiff = TVector2::Phi_mpi_pi(jet_phi[i] - lResonance.Phi()); + double etadiff = jet_eta[i] - lResonance.Eta(); + double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); + + if (R < cfgJetR && (best_R == 0 || R < best_R)) { + best_R = R; + best_jetpt = jet_pt[i]; + } + } // jet loop + return best_jetpt; + } + template void JetTrackSlicing(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& chargedjets, const bool QA) { @@ -1060,8 +1017,8 @@ struct kstarInOO { if (lResonance.M() < 0) { continue; } - fillMinv(objectType::Inclusive, trk1, trk2, lResonance, centrality, -1.0, flip); + //============================== //| Jets: Track Reconstruction //============================== @@ -1091,6 +1048,7 @@ struct kstarInOO { fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, flip); } // jetFlag } // filp + } // Tracks loop } // JetTrackSlicingMC @@ -1102,15 +1060,16 @@ struct kstarInOO { using JetTrackCandidates = soa::Join; using JetFilteredJets = soa::Filtered>; - int nJetEvents = 0; void processDataJets(o2::aod::JetCollision const& collision, JetFilteredJets const& chargedjets, JetTrackCandidates const& jetTracks, TrackCandidates const&) { + static int nJetEvents = 0; if (cDebugLevel > 0) { nJetEvents++; if ((nJetEvents + 1) % 10000 == 0) { LOG(info) << "Processed Jet Data Events: " << nJetEvents; } } + histos.fill(HIST("nEvents"), 0.5); // Raw event bool INELgt0 = false; @@ -1176,7 +1135,7 @@ struct kstarInOO { bool HasJets = false; int nJets = 0; - for (const auto& chargedjet : chargedjets) { + for (auto& chargedjet : chargedjets) { if (std::abs(chargedjet.eta()) > (cfgJetMaxEta - cfgJetdR)) { continue; } @@ -1220,16 +1179,16 @@ struct kstarInOO { JetTrackSlicing(collision, jetTracks, chargedjets, true); } // ProcessDataJets - PROCESS_SWITCH(kstarInOO, processDataJets, "process Data Jets", false); + PROCESS_SWITCH(kstarInJets, processDataJets, "process Data Jets", false); //======================================================= //| //| JET MC STUFF //| //======================================================= - int nJetMCEvents = 0; void processMCJets(o2::aod::JetCollision const& collision, JetTrackCandidatesMC const& jetTracks, soa::Filtered const& mcdjets, TrackCandidatesMC const&, aod::McParticles const&, aod::JetParticles const&) { + static int nJetMCEvents = 0; if (cDebugLevel > 0) { nJetMCEvents++; if ((nJetMCEvents + 1) % 10000 == 0) { @@ -1263,7 +1222,7 @@ struct kstarInOO { bool HasJets = false; int nJets = 0; - for (const auto& mcdjet : mcdjets) { + for (auto& mcdjet : mcdjets) { if (std::abs(mcdjet.eta()) > cfgJetMaxEta - cfgJetdR) { continue; } @@ -1291,62 +1250,37 @@ struct kstarInOO { } histos.fill(HIST("nEvents"), 3.5); // Jet event - // JetTrackSlicingMC(collision, jetTracks, mcdjets, true); + JetTrackSlicingMC(collision, jetTracks, mcdjets, true); // Instead of using "JetTrackSlicingMC", Once we have to test showing the distribution each condition + for (auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { - auto trk1 = track1.track_as(); - auto trk2 = track2.track_as(); + auto trk1 = track1.template track_as(); + auto trk2 = track2.template track_as(); // Each section, test pT. which section we lost the entries - ROOT::Math::PxPyPzMVector lDecayDaughterTest1, lDecayDaughterTest2, lResonanceTest1; - lDecayDaughterTest1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughterTest2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); - lResonanceTest1 = lDecayDaughterTest1 + lDecayDaughterTest2; - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest0_pT"), lResonanceTest1.Pt()); - } + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); + lResonance = lDecayDaughter1 + lDecayDaughter2; if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest1_pT"), lResonanceTest1.Pt()); - } - ////////////////////////////// - ////////////////////////////// - if (cfgReqMcEffTrackQA) { // false, true - if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) { - continue; - } - } - ////////////////////////////// - ////////////////////////////// - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest2_pT"), lResonanceTest1.Pt()); + if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) { + continue; } if (cfgReqMcEffPID) { // false, true if (!trackPIDKaon(trk1, true) || !trackPIDPion(trk2, true)) { continue; } } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest3_pT"), lResonanceTest1.Pt()); - } + auto particle1 = trk1.mcParticle(); auto particle2 = trk2.mcParticle(); - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// if (!particle1.has_mothers() || !particle2.has_mothers()) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest4_pT"), lResonanceTest1.Pt()); - } - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// std::vector mothers1{}; std::vector mothers1PDG{}; @@ -1355,69 +1289,46 @@ struct kstarInOO { mothers1PDG.push_back(particle1_mom.pdgCode()); } - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg1"), std::abs(mothers1PDG[0])); - } - std::vector mothers2{}; std::vector mothers2PDG{}; for (auto& particle2_mom : particle2.template mothers_as()) { mothers2.push_back(particle2_mom.globalIndex()); mothers2PDG.push_back(particle2_mom.pdgCode()); } - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg2"), std::abs(mothers2PDG[0])); - } + if (mothers1[0] != mothers2[0]) { continue; // Kaon and pion not from the same K*0 } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest5_pT"), lResonanceTest1.Pt()); - } - // kaon if (std::abs(particle1.pdgCode()) != KaonPDG) { continue; } - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest6_pT"), lResonanceTest1.Pt()); - } - // pion if (std::abs(particle2.pdgCode()) != PionPDG) { continue; } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest7_pT"), lResonanceTest1.Pt()); - } + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { continue; // mother not K*0 } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest8_pT"), lResonanceTest1.Pt()); - if (lResonanceTest1.Pt() < 0.4) { - histos.fill(HIST("hEffRecLowPtKstarDaughter"), lDecayDaughterTest1.Pt()); - histos.fill(HIST("hEffRecLowPtKstarDaughter"), lDecayDaughterTest2.Pt()); - } - } - if (std::abs(mothers2PDG[0]) != Kstar0PDG) { continue; // mothers not K*0 } if (cfgJetMCHistos) { - histos.fill(HIST("hEffRec_pT"), lResonanceTest1.Pt()); + histos.fill(HIST("hEffRec_pT"), lResonance.Pt()); } + } // track loop + } // process loop - PROCESS_SWITCH(kstarInOO, processMCJets, "process MC Jets", false); + PROCESS_SWITCH(kstarInJets, processMCJets, "process MC Jets", false); //======================================================= //| //| DATA STUFF (SE) //| //======================================================= - int nEvents = 0; void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { + static int nEvents = 0; if (cDebugLevel > 0) { nEvents++; if ((nEvents + 1) % 10000 == 0) { @@ -1451,63 +1362,18 @@ struct kstarInOO { } } - TrackSlicing(collision, tracks, collision, tracks, true); + TrackSlicing(collision, tracks, true); } // processSameEvents - PROCESS_SWITCH(kstarInOO, processDataSameEvent, "process Data Same Event", false); - - //======================================================= - //| - //| DATA STUFF (ME) - //| - //======================================================= - int nEventsMix = 0; - void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}}; - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMix++; - if ((nEventsMix + 1) % 10000 == 0) { - LOG(info) << "Processed DATA Mixed Events : " << nEventsMix; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - bool VtxMixFlag = false; - bool CentMixFlag = false; - // bool OccupanacyMixFlag = false; - if (std::abs(collision1.posZ() - collision2.posZ()) <= cfgVtxMixCut) { // set default to maybe 10 - VtxMixFlag = true; - } - if (std::abs(collision1.centFT0C() - collision2.centFT0C()) <= cfgVtxMixCut) { // set default to maybe 10 - CentMixFlag = true; - } - - if (!goodEv1 || !goodEv2) { - continue; - } - if (!CentMixFlag) { - continue; - } - if (!VtxMixFlag) { - continue; - } - - TrackSlicing(collision1, tracks1, collision2, tracks2, false); - } - } - PROCESS_SWITCH(kstarInOO, processDataMixedEvent, "process DATA Mixed Event", false); + PROCESS_SWITCH(kstarInJets, processDataSameEvent, "process Data Same Event", false); //======================================================= //| //| MC STUFF (SE) //| //========================================================= - int nEventsMC = 0; void processSameEventMC(EventCandidates::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) { + static int nEventsMC = 0; if (cDebugLevel > 0) { nEventsMC++; if ((nEventsMC + 1) % 10000 == 0) { @@ -1543,47 +1409,18 @@ struct kstarInOO { } } - TrackSlicingMC(collision, tracks, collision, tracks, true); + TrackSlicingMC(collision, tracks, true); } // processSameEvents_MC - PROCESS_SWITCH(kstarInOO, processSameEventMC, "process Same Event MC", false); - - //======================================================= - //| - //| MC STUFF (ME) - //| - //======================================================= - int nEventsMCMix = 0; - void processMixedEventMC(EventCandidates const& collisions, TrackCandidatesMC const& tracks, aod::McParticles const&) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}}; - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMCMix++; - if ((nEventsMCMix + 1) % 10000 == 0) { - LOG(info) << "Processed Mixed Events: " << nEventsMCMix; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) { - continue; - } - - TrackSlicingMC(collision1, tracks1, collision2, tracks2, false); - } // mixing - } // processMixedEvent_MC - PROCESS_SWITCH(kstarInOO, processMixedEventMC, "process Mixed Event MC", false); + PROCESS_SWITCH(kstarInJets, processSameEventMC, "process Same Event MC", false); //====================================== //| //| GENERATED STUFF //| //====================================== - int nEventsGen = 0; void processGen(EventCandidatesTrue::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& mcParticles) { + static int nEventsGen = 0; if (cDebugLevel > 0) { ++nEventsGen; if (nEventsGen % 10000 == 0) { @@ -1678,16 +1515,16 @@ struct kstarInOO { } } // loop over particles } // processGen - PROCESS_SWITCH(kstarInOO, processGen, "process Generated Particles", false); + PROCESS_SWITCH(kstarInJets, processGen, "process Generated Particles", false); //============================================== //| //| GENERATED STUFF (INCLUSIVE & JETS) //| //============================================== - int nprocessGenEvents = 0; void processGenJets(o2::aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, aod::JetParticles const& mcParticles) { + static int nprocessGenEvents = 0; if (cDebugLevel > 0) { ++nprocessGenEvents; if (nprocessGenEvents % 10000 == 0) { @@ -1796,11 +1633,11 @@ struct kstarInOO { } } // loop over particles } // end of process - PROCESS_SWITCH(kstarInOO, processGenJets, "Process Generated Particles Inclusive&Jets", false); + PROCESS_SWITCH(kstarInJets, processGenJets, "Process Generated Particles Inclusive&Jets", false); - int ndRtest = 0; void processJetQA(o2::aod::JetMcCollision const& collision, soa::Filtered const& mcpjets, aod::JetParticles const& mcParticles, aod::McParticles const&) { + static int ndRtest = 0; if (cDebugLevel > 0) { ++ndRtest; if (ndRtest % 10000 == 0) { @@ -1894,12 +1731,6 @@ struct kstarInOO { continue; } - if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { - if (cfgJetdRHistos) { - histos.fill(HIST("nJet_6_8"), bestJet.Pt()); - } - } - //================== // daughters double dR_kaon = 0; @@ -1960,6 +1791,11 @@ struct kstarInOO { histos.fill(HIST("dR_taggedjet_kaon"), dR_kaon, lResonance.Pt()); histos.fill(HIST("dR_taggedjet_pion"), dR_pion, lResonance.Pt()); + if (bestJet.Pt() < 8.0) { + histos.fill(HIST("dR_taggedjet_0_8"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_0_8"), dR_pion, lResonance.Pt()); + } + if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { histos.fill(HIST("dR_taggedjet_all_6_8"), dR_kaon, lResonance.Pt()); histos.fill(HIST("dR_taggedjet_all_6_8"), dR_pion, lResonance.Pt()); @@ -1974,6 +1810,11 @@ struct kstarInOO { if (bestJet.Pt() > 10.0 && bestJet.Pt() < 12.0) { histos.fill(HIST("Kstar_pT_INJet_10_12"), lResonance.Pt()); } + if (bestJet.Pt() > 8.0 && bestJet.Pt() < 12.0) { + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_pion, lResonance.Pt()); + histos.fill(HIST("Kstar_pT_INJet_8_12"), lResonance.Pt()); + } // Daughters out of jets if (!isKaonInJet || !isPionInJet) { @@ -2013,9 +1854,262 @@ struct kstarInOO { } // cfgJetdRHistos } // mcParticles }; - PROCESS_SWITCH(kstarInOO, processJetQA, "Process dR of K*0 Inclusive and Inside jet", false); + PROCESS_SWITCH(kstarInJets, processJetQA, "Process dR of K*0 Inclusive and Inside jet", false); + + //======================== + // + // Response matrix + // + //======================== + + using JetMCDTable = soa::Filtered>; + using JetMCPTable = soa::Filtered>; + + void processMatchedJet(aod::JetMcCollision const& collision, + soa::SmallGroups> const& recocolls, + JetMCPTable const& mcpjets, JetMCDTable const&, + JetTrackCandidatesMC const& tracks, TrackCandidatesMC const&, + aod::JetParticles const& mcParticles, aod::McParticles const&) + { + static int nMatched = 0; + if (cDebugLevel > 0) { + ++nMatched; + if (nMatched % 10000 == 0) { + LOG(info) << "Processed Matched test: " << nMatched; + } + } + + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 0.5); + } + if (std::fabs(collision.posZ()) > cfgEventVtxCut) { + return; + } + + if (recocolls.size() <= 0) { + return; + } + for (auto& recocoll : recocolls) { + if (!jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits)) { + return; + } + } + + bool INELgt0 = false; + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } // mcParticles + if (!INELgt0) { + return; + } + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 1.5); + } + + // 1. Gen jet --> Reco jet + + std::vector mcd_pt{}; + std::vector mcd_phi{}; + std::vector mcd_eta{}; + + std::vector mcp_pt{}; + std::vector mcp_phi{}; + std::vector mcp_eta{}; + + bool hasJets = false; + for (const auto& mcpjet : mcpjets) { + if (!mcpjet.has_matchedJetGeo()) { + + if (cfgMatchedGenHistos) { + histos.fill(HIST("NoMatchedJets_pT"), mcpjet.pt()); + } + continue; + } + + for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { + if (!mcdjet.has_matchedJetGeo()) { + continue; + } + hasJets = true; + + // QA + // double dPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - mcpjet.phi()); + // double dEta = mcdjet.eta() - mcpjet.eta(); + // double dR = std::sqrt((dPhi * dPhi) + (dEta * dEta)); + + if (cfgMatchedGenHistos) { + histos.fill(HIST("matchedJets_pT"), mcpjet.pt(), mcdjet.pt() - mcpjet.pt()); + histos.fill(HIST("matchedJets_phi"), mcpjet.phi(), mcdjet.phi() - mcpjet.phi()); + histos.fill(HIST("matchedJets_eta"), mcpjet.eta(), mcdjet.eta() - mcpjet.eta()); + } + + mcd_pt.push_back(mcdjet.pt()); + mcd_phi.push_back(mcdjet.phi()); + mcd_eta.push_back(mcdjet.eta()); + + mcp_pt.push_back(mcpjet.pt()); + mcp_phi.push_back(mcpjet.phi()); + mcp_eta.push_back(mcpjet.eta()); + + } // mcdjet + } // mcpjet + + if (hasJets) { + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 2.5); + } + } + + // 2. Find Gen K*0 + + // True K*0 + for (const auto& mcParticle : mcParticles) { + if (std::fabs(mcParticle.eta()) > cfgTrackMaxEta) { + continue; + } + if (std::fabs(mcParticle.pdgCode()) != Kstar0PDG) { + continue; + } + if (!mcParticle.has_daughters()) { + continue; + } + + // K*0 --> Kpi + int nKaon = 0; + int nPion = 0; + + int kaonIndex = -1; + int pionIndex = -1; + + double kaonPx = 0; + double kaonPy = 0; + double kaonPz = 0; + + double pionPx = 0; + double pionPy = 0; + double pionPz = 0; + + // Truth K*0 --> Keep the KPi Index + for (const auto& daughter : mcParticle.daughters_as()) { + + if (std::abs(daughter.pdgCode()) == KaonPDG) { + ++nKaon; + kaonIndex = daughter.globalIndex(); + } else if (std::abs(daughter.pdgCode()) == PionPDG) { + ++nPion; + pionIndex = daughter.globalIndex(); + } + } + if (nKaon != 1 || nPion != 1) { + continue; + } + + //======================= + // Track KPi --> Reco K*0 + + bool kaon_daughter = false; + bool pion_daughter = false; + + for (const auto& track : tracks) { + auto trk = track.track_as(); + + if (!trk.has_mcParticle()) { + continue; + } + if (!trackSelection(trk, true)) { + continue; + } + if (std::fabs(trk.eta()) > cfgTrackMaxEta) { + continue; + } + if (!trackPIDKaon(trk, false) || !trackPIDPion(trk, false)) { + continue; + } + + auto particle1 = trk.mcParticle(); + auto particle2 = trk.mcParticle(); + + if (particle1.globalIndex() == kaonIndex) { + kaonPx = trk.px(); + kaonPy = trk.py(); + kaonPz = trk.pz(); + kaon_daughter = true; + } + if (particle2.globalIndex() == pionIndex) { + pionPx = trk.px(); + pionPy = trk.py(); + pionPz = trk.pz(); + pion_daughter = true; + } + } // track + + if (!kaon_daughter || !pion_daughter) { + continue; + } + + // 3. Prepare Gen and Rec K*0 + + ROOT::Math::PxPyPzEVector lResonance = ROOT::Math::PxPyPzEVector(mcParticle.px(), mcParticle.py(), mcParticle.pz(), mcParticle.e()); + ROOT::Math::PxPyPzMVector lDecayKaon_Rec = ROOT::Math::PxPyPzMVector(kaonPx, kaonPy, kaonPz, massKa); + ROOT::Math::PxPyPzMVector lDecayPion_Rec = ROOT::Math::PxPyPzMVector(pionPx, pionPy, pionPz, massPi); + ROOT::Math::PxPyPzMVector lResonance_Rec = lDecayKaon_Rec + lDecayPion_Rec; + + // 4. Calculate dR between jet and K*0 + + int goodjets = 0; + bool jetFlag = false; + double jetpt_mcp = 0; + double jetpt_mcd = 0; + + for (std::vector::size_type i = 0; i < mcp_pt.size(); i++) { + double dphi = TVector2::Phi_mpi_pi(mcp_phi[i] - lResonance.Phi()); + double deta = mcp_eta[i] - lResonance.Eta(); + double dR = TMath::Sqrt((deta * deta) + (dphi * dphi)); + + if (dR < cfgJetdR) { + jetFlag = true; + jetpt_mcp = mcp_pt[i]; + jetpt_mcd = mcd_pt[i]; + + goodjets++; + } + } // mcp jet + + if (cfgSingleJet) { + if (goodjets > 1) { + jetpt_mcp = DistinguishJetsMC(mcp_pt, mcp_phi, mcp_eta, lResonance); + jetpt_mcd = DistinguishJetsMC(mcd_pt, mcd_phi, mcd_eta, lResonance_Rec); + } + } + + if (!jetFlag) { + continue; + } + + if (cfgMatchedGenHistos) { + unsigned int seed = static_cast(std::chrono::system_clock::now().time_since_epoch().count()); + int dice = rand_r(&seed) % 2; + + // for checking + if (dice > 0) { + // Train + histos.fill(HIST("RespGen_Matched_rand0"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } else { + // Test + histos.fill(HIST("RespGen_Matched_rand1"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } + + } // cfgMatchedGenHistos + + } // mcParticles + }; // processJetUnfolding + PROCESS_SWITCH(kstarInJets, processMatchedJet, "Process Unfolding of K*0 jets", false); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 03d2dbd3ff7b6030b0d1ebf77fbf04294d2c5509 Mon Sep 17 00:00:00 2001 From: hernasab Date: Mon, 19 Oct 2026 11:43:20 -0500 Subject: [PATCH 351/648] [PWGCF] remove duplicate histogram name (#18234) Co-authored-by: Sabrina Hernandez --- PWGCF/Flow/Tasks/flowZdcTask.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowZdcTask.cxx b/PWGCF/Flow/Tasks/flowZdcTask.cxx index 21cacbd92ea..0186d851235 100644 --- a/PWGCF/Flow/Tasks/flowZdcTask.cxx +++ b/PWGCF/Flow/Tasks/flowZdcTask.cxx @@ -220,8 +220,6 @@ struct FlowZdcTask { histos.add("ZNAVsFT0M", ";T0A+T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0C", ";T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0M", ";T0A+T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPAZNAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); - histos.add("ZPCZNCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); histos.add("ZPAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); From a0cefa437a830f407d13b4aff135dd9c5e9fbd17 Mon Sep 17 00:00:00 2001 From: vojmach <137265951+vojmach@users.noreply.github.com> Date: Mon, 19 Oct 2026 18:59:32 +0200 Subject: [PATCH 352/648] [PWGCF] Addition of new task for transmutation measurement using flow (#18352) Co-authored-by: Vojtech Machacek --- PWGCF/Flow/Tasks/CMakeLists.txt | 4 + PWGCF/Flow/Tasks/flowTransmutation.cxx | 624 +++++++++++++++++++++++++ 2 files changed, 628 insertions(+) create mode 100644 PWGCF/Flow/Tasks/flowTransmutation.cxx diff --git a/PWGCF/Flow/Tasks/CMakeLists.txt b/PWGCF/Flow/Tasks/CMakeLists.txt index 226449b78fe..ad5dbd56ba3 100644 --- a/PWGCF/Flow/Tasks/CMakeLists.txt +++ b/PWGCF/Flow/Tasks/CMakeLists.txt @@ -119,3 +119,7 @@ o2physics_add_dpl_workflow(flow-directed-flow-task PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(flow-transmutation + SOURCES flowTransmutation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Flow/Tasks/flowTransmutation.cxx b/PWGCF/Flow/Tasks/flowTransmutation.cxx new file mode 100644 index 00000000000..c7d00adb5b5 --- /dev/null +++ b/PWGCF/Flow/Tasks/flowTransmutation.cxx @@ -0,0 +1,624 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowTransmutation.cxx +/// \author Vojtech Machacek (vojtech.machacek@cern.ch) +/// \since Oct/05/2026 +/// \brief Task to measure flow observables with cumulants method in transmuted events. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) +static constexpr std::array, 7> LongArrayFloat = {{{{2.5, 2.5}}, {{50.0, 50.0}}, {{70.0, 70.0}}, {{5.0, 5.0}}, {{7.0, 7.0}}, {{7.0, 7.0}}, {{0., 0.}}}}; +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + return; +} +struct FlowTransmutation { + struct : ConfigurableGroup{ + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT") + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "CBT", "keep empty to disable, usage: 'CBT', 'CBT_hadronPID' ") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersFlag, int, 0, "0: Disabled; 1: kIsGoodITSLayersAll; 2: kIsGoodITSLayer0123; 3: kIsGoodITSLayer3") + O2_DEFINE_CONFIGURABLE(cfgEvSelUsage, bool, true, "Use event selection cuts: (0/1)") + O2_DEFINE_CONFIGURABLE(cfgEvSel8, bool, true, "rejects collisions which are not selected by sel8") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, true, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, true, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, true, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, true, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, true, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, true, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, true, "veto an event if FT0C amplitude in previous ITS ROF is above threshold")} cfgEventSelection; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentCutEnabled, bool, true, "Enable global multiplicity vs centrality cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality upper cut"); + Configurable> cfgGlobalMultCentCutPars{"cfgGlobalMultCentCutPars", std::vector{177.409, -5.06256, 0.0695392, -0.000539089, 0.00000177447, 27.3677, -0.427405, 0.00452115, -0.0000382143, 1.13518e-7}, "Global multiplicity vs centrality cut values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultCutEnabled, bool, true, "Enable global multiplicity vs PV multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation upper cut"); + Configurable> cfgGlobalMultPVMultCutPars{"cfgGlobalMultPVMultCutPars", std::vector{-0.0893236, 0.721511, 2.05725, 0.0244317}, "global multiplicity vs PV multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultCutEnabled, bool, true, "Enable global multiplicity vs V0A multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation upper cut"); + Configurable> cfgGlobalMultV0AMultCutPars{"cfgGlobalMultV0AMultCutPars", std::vector{485.385, 191.561, 0.296174, -0.00282594, 0.00000497112, 370.073, 102.096, -1.00597, 0.00405269, -0.00000520521}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgPVMultCentCutEnabled, bool, true, "Enable primary vertex multiplicity vs centrality cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality upper cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality lower cut"); + Configurable> cfgPVMultCentCutCutPars{"cfgPVMultCentCutPars", std::vector{245.882, -6.90255, 0.0930817, -0.000710842, 0.00000231095, 35.9647, -0.530632, 0.00513954, -0.0000411738, 1.10106e-7}, "Primary vertex multiplicity vs centrality cut parameter values"}; + } cfgMultiplicityCorrelationCut; + O2_DEFINE_CONFIGURABLE(cfgNUAWeightsRunByRun, bool, true, "Fill NUA weights run by run") + O2_DEFINE_CONFIGURABLE(cfgGetNumberOfCorr, bool, false, "Get histograms with number of correlations") + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "Maximal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 3.0f, "Maximal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAxy cut, keep empty for const. cut based on DCAxy (Nsigma)") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAz cut, keep empty for const. cut based on DCAz (Nsigma)") + Configurable> cfgRemovedRunList{"cfgRemovedRunList", std::vector{-1}, "excluded run numbers"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAxy (Nsigma)", "DCAz (Nsigma)", "DCAz (cm) override"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + ConfigurableAxis binningNch{"binningNch", {500, 0, 500}, "binning for N_{ch} histograms"}; + ConfigurableAxis binningPt{"binningPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "binning for pT histograms"}; + HistogramRegistry registryQA{"registryQA"}; + HistogramRegistry registryCorr{"registryCorr"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEventSelection.cfgCutVertex); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Service ccdb{}; + RCTFlagsChecker rctChecker{"CBT"}; + enum TrackSelectionGroup { + TrkSelNch = 0, + TrkSelObs, + TrkCounter + }; + enum TrackCut { + Chi2prTPCcls = 0, // max chi2 per TPC clusters + TPCclu, // minimum TPC found clusters + TPCCrossedRows, // minimum TPC crossed rows + ITSclu, // minimum ITS found clusters + DCAxyNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAzNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAz, // constant DCAz cut, set to 0 to override with pT dependent one + Count_TrackCut + }; + enum CentEstimators { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT, + // Count the total number of enum + Count_CentEstimators + }; + enum GoodITSLayersFlag { + Disabled = 0, + ITSLayersAll, + ITSLayer0123, + ITSLayer3, + Count_ITSLayersFlag + }; + enum EventSelectionCuts { + FilteredEvent = 1, + Sel8, + RCTflags, + IsGoodITSLayers, + NoSameBunchPileup, + NoITSROFrameBorder, + NoTimeFrameBorder, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + NoCollInRofStandard, + NoHighMultCollInPrevRof, + MultCorrelation, + BadRunsRemoved + }; + enum EtaGapsSettings { + NoGap = -1, + Gap00, + Gap02, + Gap04, + Gap06, + Gap08, + Gap10, + Gap12, + Gap14, + EtaGapCounter + }; + enum QAFillTime { + BeforeSel, + AfterSel + }; + struct EventCut { + bool enabled; + int histBin; + int flag; // just store the enum + }; + struct TrackRegion { + float forward; + float backward; + float gap; + }; + static constexpr std::array FillTimeName = {"beforeEventSelQA/", "afterEventSelQA/"}; + static constexpr std::array FillTrackName = {"cutNch/", "cutObs/"}; + std::array eventSelectionCuts{}; + std::array, 7> trackSel{}; + TF1* fGlobalMultPVMultLowCut = nullptr; + TF1* fGlobalMultPVMultHighCut = nullptr; + TF1* fGlobalMultV0AMultLowCut = nullptr; + TF1* fGlobalMultV0AMultHighCut = nullptr; + TF1* fGlobalMultCentLowCut = nullptr; + TF1* fGlobalMultCentHighCut = nullptr; + TF1* fPVMultCentLowCut = nullptr; + TF1* fPVMultCentHighCut = nullptr; + TH1D* hEff = nullptr; + std::array fDCAzCut{}; + std::array fDCAxyCut{}; + std::vector runNumbers; + std::map> th3sPerRun; + int lastRun = -1; + int getCorrectITSLayersFlag(int flag) + { + switch (flag) { + case ITSLayersAll: + return o2::aod::evsel::kIsGoodITSLayersAll; + case ITSLayer0123: + return o2::aod::evsel::kIsGoodITSLayer0123; + case ITSLayer3: + return o2::aod::evsel::kIsGoodITSLayer3; + default: + return 0; // Disabled + } + } + void init(InitContext const&) + { + AxisSpec axisVertex{40, -20, 20, "Vtxz (cm)"}; + AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; + AxisSpec axisEta{40, -1., 1., "#eta"}; + AxisSpec axisCent{100, 0., 100., "Centrality"}; + AxisSpec axisT0C = {1000, 0, 50000, "N_{ch} (T0C)"}; + AxisSpec axisT0A = {1800, 0, 180000, "N_{ch} (T0A)"}; + AxisSpec axisV0A = {1800, 0, 180000, "N_{ch} (V0A)"}; + AxisSpec axisNch = {binningNch, "N_{ch}"}; + AxisSpec axisPt = {binningPt, "p_{T} (GeV/c)"}; + registryQA.add("cutNch/hTrackPhi", "Track #varphi distribution;#varphi;Count", {HistType::kTH1D, {axisPhi}}); + registryQA.add("cutNch/hTrackEta", "Track #eta distribution;#eta;Count", {HistType::kTH1D, {axisEta}}); + registryQA.add("cutNch/hPt", "p_{T} distribution for reconstructed particles in a given pT range;p_{T};Counts", {HistType::kTH1D, {axisPt}}); + registryQA.add("cutNch/hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 5.}}}); + registryQA.add("cutNch/hChi2prITScls", "#chi^{2}/cluster for the ITS track;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 50.}}}); + registryQA.add("cutNch/hnTPCClu", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hnITSClu", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); + registryQA.add("cutNch/hnTPCCrossedRow", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hDCAz", "DCAz after cuts; DCAz (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hDCAxy", "DCAxy after cuts; DCAxy (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hPhiEtaVtxZ", ";#varphi;#eta;v_{z}", {HistType::kTH3D, {axisPhi, {32, -0.8, 0.8}, {40, -10, 10}}}); + registryQA.addClone("cutNch/", "cutObs/"); + registryQA.add("hVtxZ", "Vertex Z distribution;Vtxz (cm);Count", {HistType::kTH1D, {axisVertex}}); + registryQA.add("hCentrality", "Centrality distribution;Centrality;Count", {HistType::kTH1D, {axisCent}}); + registryQA.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{13, 0.5, 13.5}}}); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(Sel8, "after sel8"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(RCTflags, "RCTflags"); + std::array itsLayersFlag = {"ITSLayers-Disabled", "ITSLayersAll", "ITSLayers0123", "ITSLayer3"}; + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodITSLayers, Form("kIsGood%s", itsLayersFlag[cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag].c_str())); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoSameBunchPileup, "kNoSameBunchPileup"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "kNoITSROFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "kNoTimeFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "kIsGoodZvtxFT0vsPV"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "kNoCollInTimeRangeStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInRofStandard, "kNoCollInRofStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "kNoHighMultCollInPrevRof"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(MultCorrelation, "MultCorrelation"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(BadRunsRemoved, "BadRunsRemoved"); + registryQA.add("beforeEventSelQA/globalTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity global tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/PVTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_PVTracks", "before event selection;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisNch, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multT0A", "before event selection;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multV0A", "before event selection;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/multV0A_multT0A", "before event selection;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + registryQA.add("beforeEventSelQA/multT0C_centT0C", "before event selection;FT0C centrality (%);mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + if (cfgEventSelection.cfgEvSelUsage) { + registryQA.addClone("beforeEventSelQA/", "afterEventSelQA/"); + } + eventSelectionCuts = { + EventCut({.enabled = (cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag != Disabled), .histBin = IsGoodITSLayers, .flag = getCorrectITSLayersFlag(cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag)}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoSameBunchPileup, .histBin = NoSameBunchPileup, .flag = o2::aod::evsel::kNoSameBunchPileup}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoITSROFrameBorder, .histBin = NoITSROFrameBorder, .flag = o2::aod::evsel::kNoITSROFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoTimeFrameBorder, .histBin = NoTimeFrameBorder, .flag = o2::aod::evsel::kNoTimeFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkIsGoodZvtxFT0vsPV, .histBin = IsGoodZvtxFT0vsPV, .flag = o2::aod::evsel::kIsGoodZvtxFT0vsPV}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInTimeRangeStandard, .histBin = NoCollInTimeRangeStandard, .flag = o2::aod::evsel::kNoCollInTimeRangeStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInRofStandard, .histBin = NoCollInRofStandard, .flag = o2::aod::evsel::kNoCollInRofStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoHighMultCollInPrevRof, .histBin = NoHighMultCollInPrevRof, .flag = o2::aod::evsel::kNoHighMultCollInPrevRof})}; + if (cfgEventSelection.cfgEvSelUsage) { + LOGF(info, "Event selection cuts are enabled"); + LOGF(info, "Sel8 is %senabled", (cfgEventSelection.cfgEvSel8) ? "" : "not "); + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEventSelection.cfgEvSelRCTflags.value); + LOGF(info, "RCT is %senabled", (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) ? "" : "not "); + LOGF(info, "RCT selection flag: %s", cfgEventSelection.cfgEvSelRCTflags.value.c_str()); + } + for (const auto& cut : eventSelectionCuts) { + LOGF(info, "Flag %d is %senabled", cut.histBin, (cut.enabled) ? "" : "not "); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + LOGF(info, "Multiplicity correlation cuts are enabled"); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + LOGF(info, "Global multiplicity vs PV multiplicity cut is enabled: %f", axisNch.binEdges.back()); + fGlobalMultPVMultHighCut = new TF1("fGlobalMultPVMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + fGlobalMultPVMultLowCut = new TF1("fGlobalMultPVMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs PV multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultPVMultLowCut par %zu = %g", i, fGlobalMultPVMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultPVMultHighCut par %zu = %g", i, fGlobalMultPVMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + LOGF(info, Form("Global multiplicity vs V0A multiplicity cut is enabled: %f ", axisV0A.binEdges.back())); + + fGlobalMultV0AMultHighCut = new TF1("fGlobalMultV0AMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + fGlobalMultV0AMultLowCut = new TF1("fGlobalMultV0AMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs V0A multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultV0AMultLowCut par %zu = %g", i, fGlobalMultV0AMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultV0AMultHighCut par %zu = %g", i, fGlobalMultV0AMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + LOGF(info, "Global multiplicity vs centrality cut is enabled"); + fGlobalMultCentHighCut = new TF1("fGlobalMultCentHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str(), 0, 100); + fGlobalMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + fGlobalMultCentLowCut = new TF1("fGlobalMultCentLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str(), 0, 100); + fGlobalMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + LOGF(info, "Global multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultCentLowCut par %zu = %g", i, fGlobalMultCentLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultCentHighCut par %zu = %g", i, fGlobalMultCentHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + LOGF(info, "PV multiplicity vs centrality cut is enabled"); + fPVMultCentHighCut = new TF1("fPVMultCentHighCut", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str(), 0, 100); + fPVMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + fPVMultCentLowCut = new TF1("fPVMultCentLowCut", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str(), 0, 100); + fPVMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + LOGF(info, "PV multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.size(); ++i) { + LOGF(info, "fPVMultCentLowCut par %zu = %g", i, fPVMultCentLowCut->GetParameter(i)); + LOGF(info, "fPVMultCentHighCut par %zu = %g", i, fPVMultCentHighCut->GetParameter(i)); + } + } + } + } else { + LOGF(warning, "Event selection cuts are disabled"); + } + readMatrix(cfgTrackCuts->getData(), trackSel); + if (trackSel[DCAz][TrkSelNch] == 0) { + fDCAzCut[TrkSelNch] = new TF1("pT-DCAzNch", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelNch]->SetParameter(0, trackSel[DCAzNsigma][TrkSelNch]); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: [0]*%s", cfgCutDCAz->c_str())); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: %f*%s", fDCAzCut[TrkSelNch]->GetParameter(0), cfgCutDCAz->c_str())); + } + if (trackSel[DCAz][TrkSelObs] == 0) { + fDCAzCut[TrkSelObs] = new TF1("pT-DCAzObs", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelObs]->SetParameter(0, trackSel[DCAzNsigma][TrkSelObs]); + } + fDCAxyCut[TrkSelNch] = new TF1("pT-DCAxyNch", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelNch]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelNch]); + fDCAxyCut[TrkSelObs] = new TF1("pT-DCAxyObs", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelObs]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelObs]); + LOGF(info, Form("pT-dependent DCAxy cut for Nch : %f*%s", fDCAxyCut[TrkSelObs]->GetParameter(0), cfgCutDCAxy->c_str())); + std::array etaGapsFlag = {"2-sub |#Delta#eta|>0", "2-sub |#Delta#eta|>0.2", "2-sub |#Delta#eta|>0.4", "2-sub |#Delta#eta|>0.6", "2-sub |#Delta#eta|>0.8", "2-sub |#Delta#eta|>1.0", "2-sub |#Delta#eta|>1.2", "2-sub |#Delta#eta|>1.4"}; + if (cfgGetNumberOfCorr) { + registryCorr.add("hTwoParticleCorrelations", "2 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hFourParticleCorrelations", "4 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hSixParticleCorrelations", "6 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hEightParticleCorrelations", "8 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + for (uint iSet = 0; iSet < static_cast(EtaGapCounter); iSet++) { + registryCorr.get(HIST("hTwoParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hFourParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hSixParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hEightParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + } + } + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + template + float getCentrality(const TCollision& collision) + { + switch (cfgEventSelection.cfgCentEstimator) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + template + bool selectMultiplicityCorrelation(const TCollision& collision, const int multTrk, const float& centrality) + { + auto multNTracksPV = collision.multNTracksPV(); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + if (multTrk > fGlobalMultPVMultHighCut->Eval(multNTracksPV) || multTrk < fGlobalMultPVMultLowCut->Eval(multNTracksPV)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + if (collision.multFV0A() > fGlobalMultV0AMultHighCut->Eval(multTrk) || collision.multFV0A() < fGlobalMultV0AMultLowCut->Eval(multTrk)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + if (multTrk > fGlobalMultCentHighCut->Eval(centrality) || multTrk < fGlobalMultCentLowCut->Eval(centrality)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + if (multNTracksPV > fPVMultCentHighCut->Eval(centrality) || multNTracksPV < fPVMultCentLowCut->Eval(centrality)) { + return false; + } + } + return true; + } + template + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const int runNumber) + { + if (!cfgEventSelection.cfgEvSelUsage) { + return true; + } + if (cfgEventSelection.cfgEvSel8) { + if (!collision.sel8()) { + return false; + } + registryQA.fill(HIST("hEventCount"), Sel8); + } + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + if (!rctChecker(collision)) { + return false; + } + registryQA.fill(HIST("hEventCount"), RCTflags); + } + for (auto& cut : eventSelectionCuts) { + if (!cut.enabled) { + continue; + } + if (!collision.selection_bit(cut.flag)) { + return false; + } + registryQA.fill(HIST("hEventCount"), cut.histBin); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + if (!selectMultiplicityCorrelation(collision, multTrk, centrality)) { + return false; + } + registryQA.fill(HIST("hEventCount"), MultCorrelation); + } + if (std::find(cfgRemovedRunList.value.begin(), cfgRemovedRunList.value.end(), runNumber) != cfgRemovedRunList.value.end()) { + return false; + } + registryQA.fill(HIST("hEventCount"), BadRunsRemoved); + return true; + } + template + bool trackSelected(const TTracks& track) + { + if (trackSel[DCAz][sg] == 0 && std::fabs(track.dcaZ()) > fDCAzCut[sg]->Eval(track.pt())) { + return false; + } + if (trackSel[DCAz][sg] != 0 && std::fabs(track.dcaZ()) > trackSel[DCAz][sg]) { + return false; + } + return ((track.tpcChi2NCl() < trackSel[Chi2prTPCcls][sg]) && (track.tpcNClsFound() >= trackSel[TPCclu][sg]) && (track.itsNCls() >= trackSel[ITSclu][sg]) && (track.tpcNClsCrossedRows() >= trackSel[TPCCrossedRows][sg]) && (std::fabs(track.dcaXY()) < fDCAxyCut[sg]->Eval(track.pt()))); + } + template + inline void fillEventQA(const TCollision& collision, const TTracks& tracks) + { + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); + } + template + inline void fillTrackQA(const TTracks& track, const float& vtxz) + { + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackPhi"), track.phi()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackEta"), track.eta()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPhiEtaVtxZ"), track.phi(), track.eta(), vtxz); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPt"), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prITScls"), track.itsChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCClu"), track.tpcNClsFound()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnITSClu"), track.itsNCls()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCCrossedRow"), track.tpcNClsCrossedRows()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAz"), track.dcaZ(), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAxy"), track.dcaXY(), track.pt()); + } + template + inline void fillCorrProfile(const TTrack& track, std::array& trackRegion) + { + float eta = track.eta(); + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (std::fabs(eta) > trackRegion[iRegion].gap) { + if (eta > 0) { + trackRegion[iRegion].forward++; + } else { + trackRegion[iRegion].backward++; + } + } + } + } + double getTimeSinceStartOfFill(uint64_t timestamp, int firstRun) + { + auto runDuration = ccdb->getRunDuration(firstRun); + uint64_t tsSOF = runDuration.first; + uint64_t diff = timestamp - tsSOF; + return static_cast(diff) / 3600000.0; + } + using FilteredTracks = soa::Filtered>; + using FilteredCollisions = soa::Filtered>; + void process(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredTracks const& tracks) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + int nch = 0; + std::array trackCorr{TrackRegion{.forward = 0, .backward = 0, .gap = 0.}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.1}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.2}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.3}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.4}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.5}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.6}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.7}}; + registryQA.fill(HIST("hEventCount"), FilteredEvent); + if (tracks.size() < 1) { + return; + } + fillEventQA(collision, tracks); + const float cent = getCentrality(collision); + const float vtxz = collision.posZ(); + if (!eventSelected(collision, tracks.size(), cent, run)) { + return; + } + if (cfgEventSelection.cfgEvSelUsage) { + fillEventQA(collision, tracks); + } + registryQA.fill(HIST("hVtxZ"), vtxz); + registryQA.fill(HIST("hCentrality"), cent); + if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { + if (cfgNUAWeightsRunByRun) { + std::shared_ptr histNUA = registryQA.add(Form("NUA/hPhiEtaVtxZ_%i", run), "", {HistType::kTH3D, {{60, 0.0, constants::math::TwoPI}, {32, -0.8, 0.8}, {40, -10, 10}}}); + th3sPerRun.insert(std::make_pair(run, histNUA)); + } + runNumbers.push_back(run); + } + for (const auto& track : tracks) { + + if (trackSelected(track)) { + fillTrackQA(track, vtxz); + } + if (!trackSelected(track)) { + continue; + } + fillTrackQA(track, vtxz); + if (cfgGetNumberOfCorr) { + fillCorrProfile(track, trackCorr); + + nch++; + } + if (cfgNUAWeightsRunByRun) { + th3sPerRun[run]->Fill(track.phi(), track.eta(), vtxz); + } + } + float binContent = 0; + float nCorrelations = 0; + if (cfgGetNumberOfCorr) { + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (trackCorr[iRegion].forward > 0 && trackCorr[iRegion].backward > 0) { + binContent = registryCorr.get(HIST("hTwoParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * trackCorr[iRegion].backward; + registryCorr.get(HIST("hTwoParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 1 && trackCorr[iRegion].backward > 1) { + binContent = registryCorr.get(HIST("hFourParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) / 4.; + registryCorr.get(HIST("hFourParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 2 && trackCorr[iRegion].backward > 2) { + binContent = registryCorr.get(HIST("hSixParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) / 36.; + registryCorr.get(HIST("hSixParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 3 && trackCorr[iRegion].backward > 3) { + binContent = registryCorr.get(HIST("hEightParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * (trackCorr[iRegion].forward - 3) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) * (trackCorr[iRegion].backward - 3) / 576; + registryCorr.get(HIST("hEightParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + } + } + } +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From 4a90c8b6e1a22ad120d9c79263c7984b30937d8e Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Mon, 19 Oct 2026 23:52:37 +0200 Subject: [PATCH 353/648] [PWGLF,PWGMM] Set track QA configurable (#18358) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 179 +++++++++++------------------ 1 file changed, 67 insertions(+), 112 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index ab57a5da14e..9a1251a4d9d 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -174,7 +174,6 @@ enum class EvtLossType { enum class DataEvtStatus { kDataAll = 0, kDataSel, - kDataSelGt0, kDataRej, nDataEvtStatus }; @@ -244,6 +243,7 @@ struct DndetaMFTPbPb { Configurable useInelgt0{"useInelgt0", false, "Use INEL > 0 condition"}; Configurable useInelgt0wMFT{"useInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fillTrackQaHist{"fillTrackQaHist", false, "fill track QA histograms"}; } gConf; struct : ConfigurableGroup { @@ -480,60 +480,62 @@ struct DndetaMFTPbPb { } } if (doprocessDataInclusive || doprocessDatawBestTracksInclusive) { - registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); + if (gConf.useIRFromCCDB) { + registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); + } registryData.add("Events/DataEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, occupancyAxis}}); std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatus{ "Data all", "Data sel", - "Data sel gt0", "Data rejected"}; for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { registryData.get(HIST("Events/DataEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelDataEvtStatus[iBin].data()); } registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/EtaZvtxGt0", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/TanLambda", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - registryData.add({"Tracks/InvQPt", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); + registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTH2F, {nclsAxis, occupancyAxis}}}); + } if (doprocessDatawBestTracksInclusive) { - registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } } LOG(info) << "doprocessData[BestTrack]Inclusive -> Size of the Data histograms:"; registryData.print(); } if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { - registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); + if (gConf.useIRFromCCDB) { + registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); + } registryData.add("Events/Centrality/DataEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatusCent{ "Data all", "Data sel", - "Data sel gt0", "Data rejected"}; for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { registryData.get(HIST("Events/Centrality/DataEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelDataEvtStatusCent[iBin].data()); } + registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/EtaZvtxGt0", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; #eta; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, centralityAxis, occupancyAxis}}}); + } if (doprocessDatawBestTracksCentFT0C) { - registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } } LOG(info) << "doprocessData[BestTrack]CentFT0C -> Size of the Data histograms:"; registryData.print(); @@ -1014,44 +1016,38 @@ struct DndetaMFTPbPb { return true; } - template + template int countTracks(T const& tracks, float z, float c, float occ) { auto nTrk = 0; for (auto const& track : tracks) { - if (fillHis) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), track.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), track.eta(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); - } - } if (!isTrackSelected(track)) { continue; } - if (fillHis) { - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), c, occ); } else { - registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); - registryData.fill(HIST("Tracks/TanLambda"), track.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), occ); + registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), occ); } } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); + } ++nTrk; } return nTrk; } - template + template int countBestTracks(T const& /*tracks*/, B const& besttracks, float z, float c, float occ) { auto nATrk = 0; @@ -1061,40 +1057,36 @@ struct DndetaMFTPbPb { } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), itrack.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), itrack.eta(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occ); - } if (!isTrackSelected(itrack)) { continue; } - ++nATrk; - if (fillHis) { - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); + if (gConf.fillTrackQaHist) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), c, occ); + } else { + registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), occ); + } + } + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); + } + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); - registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); - registryData.fill(HIST("Tracks/Centrality/TanLambda"), itrack.tgl(), c, occ); - registryData.fill(HIST("Tracks/Centrality/InvQPt"), itrack.signed1Pt(), c, occ); registryData.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { - registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); - registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); - registryData.fill(HIST("Tracks/TanLambda"), itrack.tgl(), occ); - registryData.fill(HIST("Tracks/InvQPt"), itrack.signed1Pt(), occ); registryData.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); registryData.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); } - } - if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { - if (fillHis) { - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); + if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { registryData.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); @@ -1105,6 +1097,7 @@ struct DndetaMFTPbPb { } } } + ++nATrk; } return nATrk; } @@ -1457,23 +1450,11 @@ struct DndetaMFTPbPb { registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); } - auto nTrk = countTracks(tracks, z, c, occ); + auto nTrk = countTracks(tracks, z, c, occ); if constexpr (has_reco_cent) { - if (nTrk > 0) { - registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), c, occ); - for (auto const& track : tracks) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtxGt0"), track.eta(), z, c, occ); - } - } registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); } else { - if (nTrk > 0) { - registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), occ); - for (auto const& track : tracks) { - registryData.fill(HIST("Tracks/EtaZvtxGt0"), track.eta(), z, occ); - } - } registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); } } else { @@ -1524,37 +1505,11 @@ struct DndetaMFTPbPb { registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); } - auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); + auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); if constexpr (has_reco_cent) { - if (nBestTrks > 0) { - registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), c, occ); - for (auto const& atrack : besttracks) { - if (!isBestTrackSelected(atrack)) { - continue; - } - auto itrack = atrack.mfttrack_as(); - if (!isTrackSelected(itrack)) { - continue; - } - registryData.fill(HIST("Tracks/Centrality/EtaZvtxGt0"), itrack.eta(), z, c, occ); - } - } registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); } else { - if (nBestTrks > 0) { - registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSelGt0), occ); - for (auto const& atrack : besttracks) { - if (!isBestTrackSelected(atrack)) { - continue; - } - auto itrack = atrack.mfttrack_as(); - if (!isTrackSelected(itrack)) { - continue; - } - registryData.fill(HIST("Tracks/EtaZvtxGt0"), itrack.eta(), z, occ); - } - } registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); } } else { From b197d72c349b11b1dc5a94f0d1be0033fe77882d Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Tue, 20 Oct 2026 00:17:27 +0200 Subject: [PATCH 354/648] [PWGCF] Add Omega mesons to femto framework (#18357) --- PWGCF/Femto/Core/dataTypes.h | 3 + PWGCF/Femto/Core/modes.h | 6 + PWGCF/Femto/Core/partitions.h | 49 +++- .../trackTrackTwoPhotonResonanceBuilder.h | 236 ++++++++++++++++++ .../trackTrackTwoPhotonResonanceHistManager.h | 231 +++++++++++++++++ PWGCF/Femto/Core/twoPhotonResonanceBuilder.h | 16 +- PWGCF/Femto/DataModel/FemtoTables.h | 60 ++++- PWGCF/Femto/TableProducer/CMakeLists.txt | 5 + ...oProducerTrackTrackTwoPhotonResonances.cxx | 92 +++++++ PWGCF/Femto/Tasks/CMakeLists.txt | 5 + .../femtoTrackTrackTwoPhotonResonanceQa.cxx | 127 ++++++++++ 11 files changed, 802 insertions(+), 28 deletions(-) create mode 100644 PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h create mode 100644 PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h create mode 100644 PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx create mode 100644 PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index bbda8a76e93..d96d1cf69bf 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -50,6 +50,9 @@ using TwoTrackResonanceType = uint16_t; // datatype for resonances built from two photons (pi0, eta, ...) using TwoPhotonResonanceType = uint16_t; +// datatype for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +using TrackTrackTwoPhotonResonanceType = uint8_t; + // datatypes for cascades using CascadeMaskType = uint32_t; using CascadeMaskType001 = uint16_t; // old data type, was too narrow diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index e4306101980..e0a70e644ec 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -241,6 +241,12 @@ enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonan kEta }; +// resonances reconstructed from two tracks and a two-photon resonance, e.g. omega(782) -> pi+ pi- pi0. +// Named kOmegaMeson (not kOmega) to not collide with Cascade::kOmega, the Omega- baryon. +enum class TrackTrackTwoPhotonResonance : o2::analysis::femto::datatypes::TrackTrackTwoPhotonResonanceType { + kOmegaMeson +}; + enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType { kD0, kD0Bar, diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index ad82cde98ea..6900186a342 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -95,19 +95,42 @@ ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) // partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON -// there is no sign and no momentum-threshold PID switch, just a plain mask check per (unordered) daughter -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ - (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ - (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ - (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ - (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ - (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ - (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ - (o2::aod::femtobase::stored::mass > (selection).massMin) && \ - (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ncheckbit(o2::aod::femtotwophotonresonances::maskDau1, (selection).dau1Mask) && \ - ncheckbit(o2::aod::femtotwophotonresonances::maskDau2, (selection).dau2Mask) +// there is no sign and no momentum-threshold PID switch, and just a single mask check -- both +// daughters are plain photons with the same selection requirement, so the candidate only stores the +// AND of both legs' own masks (see femtotwophotonresonances::MaskDau) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau, (selection).dauMask) + +// partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> +// pi+ pi- pi0): same pattern as MAKE_RESONANCE_1_PARTITON (daughter masks copied onto the candidate +// at build time, systematics reselected here), just with 3 daughter masks instead of 2 -- the two +// charged daughters keep their momentum-threshold PID switch, the neutral daughter does not +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskBelowThres)) && \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask) // partition for lambdas // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..2e280ea1365 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h @@ -0,0 +1,236 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceBuilder.h +/// \brief builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::trackstwophotonresonancebuilder +{ +// filters applied in the producer task (loose pre-selection, applied before the mass window itself) +struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; +}; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window +// and daughter mask -- same pattern as the two-track resonances (TWOTRACKRESONANCE_DEFAULT_SELECTION), +// just with a 3rd mask for the neutral daughter (no momentum-threshold split there, same as +// femtotwophotonresonances' single dauMask) +template +struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kOmega, "Absolute PDG code of the resonance (pdg[omega(782)]=223)"}; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; + o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; + o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; + o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; + o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; + o2::framework::Configurable neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"}; +}; + +constexpr const char PrefixOmegaMesonSelection1[] = "OmegaMesonSelection1"; +using ConfOmegaMesonSelection1 = ConfOmegaMesonSelection; + +struct TrackTrackTwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedOmegaMesons; + o2::framework::Produces producedOmegaMesonMasks; +}; + +struct ConfTrackTrackTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackTrackTwoPhotonResonanceTables"); + o2::framework::Configurable produceOmegaMesons{"produceOmegaMesons", -1, "Produce OmegaMesons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaMesonMasks{"produceOmegaMesonMasks", -1, "Produce OmegaMesonMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0). +/// +/// posDau/negDau are drawn from already-selected FTracks partitions (sign and PID handled by the +/// normal track selection, same as any other track daughter use case), and neutralDau from an +/// already-built FPi0s/FEtas partition. This builder does the 3-body combinatorics, the +/// resonance's own mass window, and -- like TwoTrackResonanceBuilder -- copies the daughters' +/// bitmasks onto the candidate so systematic variations can be reselected downstream from the +/// candidate table alone, without rejoining the daughter tables. +template +class TrackTrackTwoPhotonResonanceBuilder +{ + public: + TrackTrackTwoPhotonResonanceBuilder() = default; + ~TrackTrackTwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confPosDauSelection, T3& confNegDauSelection, T4& confTable, T5& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + mPosDauThreshold = confPosDauSelection.pidThres.value; + mNegDauThreshold = confNegDauSelection.pidThres.value; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + LOG(info) << "Initialize femto OmegaMeson builder..."; + mPosDauMass = o2::constants::physics::MassPiPlus; + mNegDauMass = o2::constants::physics::MassPiMinus; + mProduceOmegaMesons = utils::enableTable("FOmegaMesons_001", confTable.produceOmegaMesons.value, initContext); + mProduceOmegaMesonMasks = utils::enableTable("FOmegaMesonMasks_001", confTable.produceOmegaMesonMasks.value, initContext); + } + + if (mProduceOmegaMesons || mProduceOmegaMesonMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& posDauPartition, T4& negDauPartition, T5& neutralDauPartition, T6& cache) + { + if (!mFillAnyTable) { + return; + } + auto posDauSlice = posDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto negDauSlice = negDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto neutralDauSlice = neutralDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [posDau, negDau, neutralDau] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDauSlice, negDauSlice, neutralDauSlice))) { + this->fillResonance(col, posDau, negDau, neutralDau, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& posDau, T2 const& negDau, T3 const& neutralDau) + { + ROOT::Math::PtEtaPhiMVector vecPosDau{posDau.pt(), posDau.eta(), posDau.phi(), mPosDauMass}; + ROOT::Math::PtEtaPhiMVector vecNegDau{negDau.pt(), negDau.eta(), negDau.phi(), mNegDauMass}; + ROOT::Math::PtEtaPhiMVector vecNeutralDau{neutralDau.pt(), neutralDau.eta(), neutralDau.phi(), neutralDau.mass()}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDau + vecNegDau + vecNeutralDau; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& posDau, T3 const& negDau, T4 const& neutralDau, T5& resonanceProducts) + { + reconstructResonance(posDau, negDau, neutralDau); + if (!checkFilters()) { + return; + } + + bool posDauHasHighMomentum = posDau.p() > mPosDauThreshold; + bool negDauHasHighMomentum = negDau.p() > mNegDauThreshold; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + if (mProduceOmegaMesons) { + resonanceProducts.producedOmegaMesons(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + posDau.globalIndex(), + negDau.globalIndex(), + neutralDau.globalIndex()); + } + if (mProduceOmegaMesonMasks) { + resonanceProducts.producedOmegaMesonMasks(posDau.mask(), + posDauHasHighMomentum, + negDau.mask(), + negDauHasHighMomentum, + neutralDau.maskDau()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + + float mMassMin = 0.f; + float mMassMax = 1.f; + float mPtMin = 0.f; + float mPtMax = 10.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + float mPosDauMass = 0.f; + float mNegDauMass = 0.f; + + float mPosDauThreshold = 0.f; + float mNegDauThreshold = 0.f; + + bool mFillAnyTable = false; + bool mProduceOmegaMesons = false; + bool mProduceOmegaMesonMasks = false; +}; + +} // namespace o2::analysis::femto::trackstwophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..936fd87dd36 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h @@ -0,0 +1,231 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::trackstwophotonresonancehistmanager +{ +enum TrackTrackTwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTrackTrackTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfOmegaMesonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonBinning"); + TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(0.6f, 0.95f) +}; +#undef TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTrackTrackTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, + +template +auto makeTrackTrackTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTrackTrackTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixOmegaMeson[] = "OmegaMeson/"; +constexpr char PrefixOmegaMesonNeutralDau[] = "OmegaMesonNeutralDau/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau1[] = "OmegaMesonNeutralDauPhotonDau1Qa/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau2[] = "OmegaMesonNeutralDauPhotonDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TrackTrackTwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two tracks and a two-photon resonance (e.g. +/// omega(782) -> pi+ pi- pi0). The charged daughters reuse the regular TrackHistManager; +/// the embedded two-photon-resonance daughter reuses TwoPhotonResonanceHistManager as-is +/// (same reco+QA histograms, including its own photon-leg QA, that the dedicated Pi0/Eta +/// QA task gets) by also threading the FPhotons table through to it. +template +class TrackTrackTwoPhotonResonanceHistManager +{ + public: + TrackTrackTwoPhotonResonanceHistManager() = default; + ~TrackTrackTwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& PosDauSpecs, + std::map> const& NegDauSpecs, + std::map> const& NeutralDauSpecs, + std::map> const& NeutralDauPhotonSpecs, + T1 const& ConfPosDauQaBinning, + T2 const& ConfNegDauQaBinning, + T3 const& ConfNeutralDauPhotonQaBinning) + { + mHistogramRegistry = registry; + + // omega(782) -> pi+ pi- pi0: both charged daughters are pions. Daughter managers are kept at + // kReco regardless of the outer mode: their fillQa() needs detector-QA columns (tpcNClsFound, + // itsNCls, ...) that plain FTracks doesn't carry (only the dedicated femtoTrackQa.cxx joins + // those in) -- detailed per-track detector QA isn't this manager's job. + mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); + mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); + + // the embedded pi0/eta candidate is a fully-fledged femto table with its own mask/extras + // columns, so unlike the plain-track daughters above, it can just follow the outer mode. + mNeutralDauManager.template init(registry, NeutralDauSpecs, NeutralDauPhotonSpecs, ConfNeutralDauPhotonQaBinning); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& tracks, T3 const& neutralDaus, T4 const& photons) + { + auto posDau = tracks.rawIteratorAt(resonance.posDauId() - tracks.offset()); + mPosDauManager.template fill(posDau, tracks); + auto negDau = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); + mNegDauManager.template fill(negDau, tracks); + auto neutralDau = neutralDaus.rawIteratorAt(resonance.neutralDauId() - neutralDaus.offset()); + mNeutralDauManager.template fill(neutralDau, photons); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mNeutralDauManager; +}; +} // namespace o2::analysis::femto::trackstwophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h index 4e4d9ed31b0..5883ca23411 100644 --- a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -56,9 +56,12 @@ constexpr const char PrefixEtaFilters[] = "EtaFilters1"; using ConfPi0Filters = ConfTwoPhotonResonanceFilters; using ConfEtaFilters = ConfTwoPhotonResonanceFilters; -// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + daughter mask. -// unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no momentum-threshold PID -// switch: photon daughters are unordered and have no momentum-dependent PID regime. +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + +// daughter mask. unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no +// momentum-threshold PID switch: photon daughters are unordered and have no momentum-dependent PID +// regime. there is also just a single dauMask (not one per leg): both legs are plain photons with +// the same selection requirement, so the candidate only ever stores/needs the AND of both legs' +// masks (see femtotwophotonresonances::MaskDau in FemtoTables.h) -- no per-leg distinction to select on. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCodeAbs) \ o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ @@ -70,8 +73,7 @@ using ConfEtaFilters = ConfTwoPhotonResonanceFilters; o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCodeAbs), "Absolute PDG code of the resonance, used to set its mass for kstar/kt/mt (not the reconstructed two-photon mass)"}; \ - o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ - o2::framework::Configurable dau2Mask{"dau2Mask", 0, "Bitmask required for second photon daughter"}; + o2::framework::Configurable dauMask{"dauMask", 0, "Bitmask required for both photon daughters"}; struct ConfPi0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Pi0Selection1"); @@ -199,7 +201,7 @@ class TwoPhotonResonanceBuilder dau2.globalIndex()); } if (mProducePi0Masks) { - resonanceProducts.producedPi0Masks(dau1.mask(), dau2.mask()); + resonanceProducts.producedPi0Masks(dau1.mask() & dau2.mask()); } } if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { @@ -213,7 +215,7 @@ class TwoPhotonResonanceBuilder dau2.globalIndex()); } if (mProduceEtaMasks) { - resonanceProducts.producedEtaMasks(dau1.mask(), dau2.mask()); + resonanceProducts.producedEtaMasks(dau1.mask() & dau2.mask()); } } } diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 89841e1b412..029e5d3632e 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -901,10 +901,12 @@ using StoredFPhotonExtras = StoredFPhotonExtras_001; namespace femtotwophotonresonances { -// columns for daughter (photon) bit masks -- no momentum-threshold bit: photon daughters have no -// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances -DECLARE_SOA_COLUMN(MaskDau1, maskDau1, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for first photon daughter -DECLARE_SOA_COLUMN(MaskDau2, maskDau2, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for second photon daughter +// column for the daughter (photon) bit mask -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances. Both +// legs are plain photons with the same selection requirement, so only the AND of both legs' own +// masks is stored (not one column per leg): requiring a set of bits in the AND is equivalent to +// requiring that same set of bits in both legs individually. +DECLARE_SOA_COLUMN(MaskDau, maskDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the photon daughters (AND of both legs' own masks) // id columns for the two photon daughters (unordered, no pos/neg distinction) DECLARE_SOA_INDEX_COLUMN_FULL(Dau1, dau1, int32_t, FPhotons, "_Dau1"); //! index column for first photon daughter @@ -928,8 +930,7 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s femtobase::dynamic::Theta); using FPi0s = FPi0s_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s - femtotwophotonresonances::MaskDau1, - femtotwophotonresonances::MaskDau2); + femtotwophotonresonances::MaskDau); using FPi0Masks = FPi0Masks_001; // table for etas (-> gamma gamma) @@ -949,10 +950,53 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas femtobase::dynamic::Theta); using FEtas = FEtas_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas - femtotwophotonresonances::MaskDau1, - femtotwophotonresonances::MaskDau2); + femtotwophotonresonances::MaskDau); using FEtaMasks = FEtaMasks_001; +namespace femtotrackstwophotonresonances +{ +// columns for daughter bit masks, copied onto the candidate at build time -- same pattern as +// femtotwotrackresonances: systematic reselection happens on the candidate itself, not by rejoining +// the daughter tables. The two charged daughters keep their own momentum-threshold PID switch; the +// neutral (pi0/eta) daughter has no such switch and no dedicated mask of its own -- it is a direct +// copy of its FPi0Masks/FEtaMasks maskDau (itself already the AND of its two photon legs). +DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for positive charged-track daughter +DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for negative charged-track daughter +DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNeutralDau, maskNeutralDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the two-photon-resonance daughter (copy of its own maskDau) + +// id columns for the two charged-track daughters and the two-photon-resonance daughter +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for the positive charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for the negative charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NeutralDau, neutralDau, int32_t, FPi0s, "_NeutralDau"); //! index column for the two-photon-resonance daughter +} // namespace femtotrackstwophotonresonances + +// table for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesons_001, "FOMEMES", 1, //! femto omega mesons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotrackstwophotonresonances::PosDauId, + femtotrackstwophotonresonances::NegDauId, + femtotrackstwophotonresonances::NeutralDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FOmegaMesons = FOmegaMesons_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesonMasks_001, "FOMEMESMASK", 1, //! mask for omega mesons + femtotrackstwophotonresonances::MaskPosDau, + femtotrackstwophotonresonances::PosDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNegDau, + femtotrackstwophotonresonances::NegDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNeutralDau); +using FOmegaMesonMasks = FOmegaMesonMasks_001; + namespace femtokinks { // columns for bit masks diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 91add294624..9658d1b354f 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -43,3 +43,8 @@ o2physics_add_dpl_workflow(femto-producer-two-photon-resonances SOURCES ./femtoProducerTwoPhotonResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-track-track-two-photon-resonances + SOURCES ./femtoProducerTrackTrackTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx new file mode 100644 index 00000000000..3aed1730218 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTrackTrackTwoPhotonResonances.cxx +/// \brief Task that produces femto tables for resonances built from two tracks and a two-photon +/// resonance (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +// reads the already-built FTracks (for the charged pion daughters) and FPi0s (for the neutral +// daughter) and combines them into omega(782) -> pi+ pi- pi0 candidates. A 3rd-stage producer, +// chained after femtoProducer.cxx (FTracks) and femtoProducerTwoPhotonResonances.cxx (FPi0s), same +// pattern as femtoProducerTwoPhotonResonances.cxx itself reading femtoProducer.cxx's FPhotons. +struct FemtoProducerTrackTrackTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for the two charged (pion) daughters -- plain track selections, same mechanism used for + // any other track daughter; sign is configured through these, not hardcoded in the builder + trackbuilder::ConfTrackSelection1 posDauSelection; + o2::framework::Partition posDauPartition = MAKE_TRACK_PARTITION(posDauSelection); + o2::framework::Preslice perColPosDau = o2::aod::femtobase::stored::fColId; + + trackbuilder::ConfTrackSelection2 negDauSelection; + o2::framework::Partition negDauPartition = MAKE_TRACK_PARTITION(negDauSelection); + o2::framework::Preslice perColNegDau = o2::aod::femtobase::stored::fColId; + + // setup for the neutral (pi0) daughter + twophotonresonancebuilder::ConfPi0Selection neutralDauSelection; + o2::framework::Partition neutralDauPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(neutralDauSelection); + o2::framework::Preslice perColNeutralDau = o2::aod::femtobase::stored::fColId; + + // resonance filter and builder + trackstwophotonresonancebuilder::ConfOmegaMesonFilters confOmegaMesonFilter; + trackstwophotonresonancebuilder::ConfTrackTrackTwoPhotonResonanceTables confOmegaMesonTables; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilderProducts omegaMesonBuilderProducts; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilder omegaMesonBuilder; + + void init(o2::framework::InitContext& context) + { + omegaMesonBuilder.init(confOmegaMesonFilter, posDauSelection, negDauSelection, confOmegaMesonTables, context); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/) + { + omegaMesonBuilder.fillResonances(col, omegaMesonBuilderProducts, posDauPartition, negDauPartition, neutralDauPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTrackTrackTwoPhotonResonances, processOmegaMesons, "Build OmegaMeson candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index 1c31864d7f7..1f9647bd7b2 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(femto-two-photon-resonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-track-track-two-photon-resonance-qa + SOURCES femtoTrackTrackTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-v0-qa SOURCES femtoV0Qa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..35a09042d94 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTrackTrackTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two tracks and a two-photon resonance (omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTrackTrackTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoOmegaMesons = o2::soa::Join; + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for omega mesons + trackstwophotonresonancebuilder::ConfOmegaMesonSelection1 confOmegaMesonSelection; + o2::framework::Partition omegaMesonPartition = MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(confOmegaMesonSelection); + o2::framework::Preslice perColOmegaMesons = o2::aod::femtobase::stored::fColId; + + trackstwophotonresonancehistmanager::ConfOmegaMesonBinning confOmegaMesonBinning; + + // setup for the two charged (pion) daughters + trackhistmanager::ConfResonancePosDauBinning confPosDauBinning; + trackhistmanager::ConfResonancePosDauQaBinning confPosDauQaBinning; + trackhistmanager::ConfResonanceNegDauBinning confNegDauBinning; + trackhistmanager::ConfResonanceNegDauQaBinning confNegDauQaBinning; + + // setup for the neutral (pi0) daughter -- reuses the regular Pi0 binning/QA config, since the + // embedded candidate gets the same TwoPhotonResonanceHistManager treatment (including its own + // photon-leg QA) as the dedicated Pi0/Eta QA task + twophotonresonancehistmanager::ConfPi0Binning confNeutralDauBinning; + photonhistmanager::ConfPhotonBinning confNeutralDauPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confNeutralDauPhotonQaBinning; + + trackstwophotonresonancehistmanager::TrackTrackTwoPhotonResonanceHistManager< + trackstwophotonresonancehistmanager::PrefixOmegaMeson, + trackhistmanager::PrefixResonancePosDaughter, + trackhistmanager::PrefixResonanceNegDaughter, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDau, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau1, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau2, + modes::TrackTrackTwoPhotonResonance::kOmegaMeson, + modes::TwoPhotonResonance::kPi0> + omegaMesonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto omegaMesonHistSpec = trackstwophotonresonancehistmanager::makeTrackTrackTwoPhotonResonanceQaHistSpecMap(confOmegaMesonBinning); + auto posDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDauBinning, confPosDauQaBinning); + auto negDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDauBinning, confNegDauQaBinning); + auto neutralDauSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confNeutralDauBinning); + auto neutralDauPhotonSpec = photonhistmanager::makePhotonQaHistSpecMap(confNeutralDauPhotonBinning, confNeutralDauPhotonQaBinning); + + omegaMesonHistManager.init(&hRegistry, omegaMesonHistSpec, posDauSpec, negDauSpec, neutralDauSpec, neutralDauPhotonSpec, confPosDauQaBinning, confNegDauQaBinning, confNeutralDauPhotonQaBinning); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoOmegaMesons const& /*omegaMesons*/, FemtoTracks const& tracks, FemtoPi0s const& pi0s, FemtoPhotons const& photons) + { + auto omegaMesonSlice = omegaMesonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (omegaMesonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& omegaMeson : omegaMesonSlice) { + omegaMesonHistManager.fill(omegaMeson, tracks, pi0s, photons); + } + } + PROCESS_SWITCH(FemtoTrackTrackTwoPhotonResonanceQa, processOmegaMesons, "Process OmegaMesons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} From 177407e84988baa0459ac92ecda8e2125d4a7e2d Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Tue, 20 Oct 2026 09:00:45 +0200 Subject: [PATCH 355/648] [PWGEM] PhotonMeson: Add configurables for number of bins and axis range for inv. mass - Deleted shadowed variable that was declared again inside an if-else statement Co-authored-by: jokonig --- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 6 ++++-- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h | 7 +++++-- PWGEM/PhotonMeson/Utils/NMHistograms.h | 16 ++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 85c74cc14bb..22ee95e1bc3 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -99,6 +99,8 @@ struct Pi0EtaToGammaGamma { o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {o2::framework::VARIABLE_WIDTH, -o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0.0f, +o2::constants::math::PIQuarter, +o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; o2::framework::Configurable cfgSparseFullAxes{"cfgSparseFullAxes", false, "add event plane, occupancy and z-vertex axes to Pair/same/hs and Pair/mix/hs"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; @@ -428,9 +430,9 @@ struct Pi0EtaToGammaGamma { std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } DefineEMEventCut(); DefinePCMCut(); diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index 351bb86db35..e8c2333ccd3 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -89,6 +89,9 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable fd_k0s_to_pi0{"fd_k0s_pi0", "1.0", "feed down correction to pi0"}; o2::framework::Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; + o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; o2::framework::Configurable cfgAlphaMesonA{"cfgAlphaMesonA", 0.65, "photon energy asymmetry distribution parameter A for pT dependent cut (A * tanh(B*pT))"}; @@ -276,9 +279,9 @@ struct Pi0EtaToGammaGammaMC { { o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } DefineEMEventCut(); DefinePCMCut(); diff --git a/PWGEM/PhotonMeson/Utils/NMHistograms.h b/PWGEM/PhotonMeson/Utils/NMHistograms.h index 448e94c3007..d7c87380417 100644 --- a/PWGEM/PhotonMeson/Utils/NMHistograms.h +++ b/PWGEM/PhotonMeson/Utils/NMHistograms.h @@ -29,6 +29,7 @@ #include #include +#include #include namespace o2::aod::pwgem::photonmeson::utils::nmhistogram @@ -40,12 +41,15 @@ namespace o2::aod::pwgem::photonmeson::utils::nmhistogram /// \param occBins data only: occupancy bin edges /// \param epBins data only: event plane bin edges /// \param vtxBins data only: z-vertex bin edges -inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, +inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, + bool isMC, const char* pairname = "#gamma#gamma", bool sparseFullAxes = false, std::vector const& occBins = {0, 500, 1000, 2000, 3000, 5000, 10000}, std::vector const& epBins = {-o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf}, - std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}) + std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, + const int nBinsMass = 400, + const std::array massBinRange = {0., 0.8}) { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at skimming.!! std::vector ptbins; @@ -63,7 +67,10 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is ptbins.emplace_back(1.0 * (i - 61) + 10.0); // from 10 to 20 GeV/c, every 1 GeV/c } const o2::framework::AxisSpec axis_pt{ptbins, Form("p_{T,%s} (GeV/c)", pairname)}; - const o2::framework::AxisSpec axis_mass{400, 0, 0.8, Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_mass{nBinsMass, massBinRange[0], massBinRange[1], Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; + const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; + const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; if (isMC) { fRegistry->add("Pair/Pi0/hs_Primary", "rec. true pi0", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); @@ -88,9 +95,6 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is fRegistry->get(HIST("Generated/Eta/hPtY"))->SetYTitle("rapidity |y|"); } else { if (sparseFullAxes) { - const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; - const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; - const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; fRegistry->add("Pair/same/hs", "diphoton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_ep, axis_occ, axis_vtx}, true); From 3c6f63183fb0ae1007873111f774006b7fda8118 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:04:13 +0200 Subject: [PATCH 356/648] [PWGCF] Simplify daughter selection of resonances in femto framework (#18361) --- PWGCF/Femto/Core/partitions.h | 93 +++++++++++-------- .../trackTrackTwoPhotonResonanceBuilder.h | 13 ++- PWGCF/Femto/Core/twoTrackResonanceBuilder.h | 49 +++++++--- 3 files changed, 92 insertions(+), 63 deletions(-) diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index 6900186a342..4f03bbe7c04 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -56,25 +56,29 @@ (o2::aod::femtobase::stored::mass > (selection).massMin) && \ (o2::aod::femtobase::stored::mass < (selection).massMax) -// partition for phis and rhos, i.e. resonance that are their own antiparticle +// partition for phis and rhos, i.e. resonance that are their own antiparticle -- both daughters are +// the same species, so the selection carries a single dauMask pair applied to both stored columns // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define MAKE_RESONANCE_0_PARTITON(selection) \ - (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ - (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ - (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ - (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ - (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ - (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ - (o2::aod::femtobase::stored::mass > (selection).massMin) && \ - (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) +#define MAKE_RESONANCE_0_PARTITON(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskBelowThres)) -// partition for kstars, they have distinct antiparticle +// partition for kstars, they have distinct antiparticle -- K*0 (K+ pi-) and K*0bar (K- pi+) swap +// which physical daughter (pos/neg) is the kaon vs the pion, so the selection carries masks named by +// species (kaon/pion) and this macro resolves pos/neg <-> kaon/pion from the candidate's own +// signedPt, the same way it's reconstructed (no need for the user to reason about this downstream) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_RESONANCE_1_PARTITON(selection) \ ifnode((selection).sign.node() != 0, \ @@ -87,12 +91,19 @@ (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ (o2::aod::femtobase::stored::mass > (selection).massMin) && \ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) + ifnode(o2::aod::femtobase::stored::signedPt > 0.f, \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskBelowThres)), \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskBelowThres))) // partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON // there is no sign and no momentum-threshold PID switch, and just a single mask check -- both @@ -111,25 +122,25 @@ ncheckbit(o2::aod::femtotwophotonresonances::maskDau, (selection).dauMask) // partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> -// pi+ pi- pi0): same pattern as MAKE_RESONANCE_1_PARTITON (daughter masks copied onto the candidate -// at build time, systematics reselected here), just with 3 daughter masks instead of 2 -- the two -// charged daughters keep their momentum-threshold PID switch, the neutral daughter does not +// pi+ pi- pi0): same pattern as MAKE_RESONANCE_0_PARTITON (both charged daughters are the same +// species -- pions, just opposite charge, no kstar-style species swap -- so a single dauMask pair +// covers both), plus a 3rd mask for the neutral daughter (no momentum-threshold split there) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \ - (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ - (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ - (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ - (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ - (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ - (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ - (o2::aod::femtobase::stored::mass > (selection).massMin) && \ - (o2::aod::femtobase::stored::mass < (selection).massMax) && \ - ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskBelowThres)) && \ +#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskBelowThres)) && \ ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask) // partition for lambdas diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h index 2e280ea1365..dacdde4f272 100644 --- a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h @@ -52,9 +52,10 @@ struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup { }; // selection used downstream (QA/pairing) to re-select already produced resonances by mass window -// and daughter mask -- same pattern as the two-track resonances (TWOTRACKRESONANCE_DEFAULT_SELECTION), -// just with a 3rd mask for the neutral daughter (no momentum-threshold split there, same as -// femtotwophotonresonances' single dauMask) +// and daughter mask. both charged daughters are pions (just opposite charge, no species swap the +// way kstar's kaon/pion does), so -- same as TWOTRACKRESONANCE_SAME_SPECIES_MASK for phi/rho0 -- +// a single pair of mask thresholds is applied to both. plus a 3rd mask for the neutral daughter (no +// momentum-threshold split there, same as femtotwophotonresonances' single dauMask) template struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; @@ -67,10 +68,8 @@ struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; - o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; - o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; - o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; - o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x10u, "Bitmask for both pion daughters below threshold"}; + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x8u, "Bitmask for both pion daughters above threshold"}; o2::framework::Configurable neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"}; }; diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index 379ea697b0a..fece3d0e06f 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -58,40 +58,60 @@ using ConfPhiFilters = ConfTwoTrackResonanceFilters; using ConfKstarFilters = ConfTwoTrackResonanceFilters; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Resonance"}; \ - o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Resonance"}; \ - o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; \ - o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; \ - o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; \ - o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; +#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Resonance"}; + +// phi -> K+ K-, rho0 -> pi+ pi-: both daughters are the same species, so a single pair of mask +// thresholds is enough, applied to both the positive and negative daughter alike +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_SAME_SPECIES_MASK \ + o2::framework::Configurable dauMaskBelowThres{"dauMaskBelowThres", 0x10u, "Bitmask for both daughters below threshold"}; \ + o2::framework::Configurable dauMaskAboveThres{"dauMaskAboveThres", 0x8u, "Bitmask for both daughters above threshold"}; struct ConfPhiSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("PhiSelection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(333, 0.95f, 1.05f) + TWOTRACKRESONANCE_SAME_SPECIES_MASK o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; struct ConfRho0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Rho0Selection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(113, 0.7f, 0.84f) + TWOTRACKRESONANCE_SAME_SPECIES_MASK o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; +#undef TWOTRACKRESONANCE_SAME_SPECIES_MASK + +// K*0 -> K+ pi-, K*0bar -> K- pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign, so the masks are named by species (kaon/pion) rather than by charge -- +// MAKE_RESONANCE_1_PARTITON resolves which stored column (maskPosDau/maskNegDau) each applies to +// from the candidate's own signedPt, so the user never has to reason about pos/neg <-> kaon/pion +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_KSTAR_MASK \ + o2::framework::Configurable kaonDauMaskBelowThres{"kaonDauMaskBelowThres", 0x10u, "Bitmask for the kaon daughter below threshold"}; \ + o2::framework::Configurable kaonDauMaskAboveThres{"kaonDauMaskAboveThres", 0x8u, "Bitmask for the kaon daughter above threshold"}; \ + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x2u, "Bitmask for the pion daughter below threshold"}; \ + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x1u, "Bitmask for the pion daughter above threshold"}; + struct ConfKstar0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Kstar0Selection"); o2::framework::Configurable sign{"sign", 1, "Sign (+1 for Kstar0 and -1 for Kstar0Bar) "}; TWOTRACKRESONANCE_DEFAULT_SELECTION(313, 0.8f, 1.0f) + TWOTRACKRESONANCE_KSTAR_MASK }; #undef TWOTRACKRESONANCE_DEFAULT_SELECTION +#undef TWOTRACKRESONANCE_KSTAR_MASK struct TwoTrackResonanceBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedPhis; @@ -182,7 +202,6 @@ class TwoTrackResonanceBuilder } auto posDaughterSlice = posDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto negDaughterSlice = negDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - // LOG(warn) << "Size: " << posDaughterSlice.size() << " " << negDaughterSlice.size(); for (auto const& [posDaughter, negDaughter] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDaughterSlice, negDaughterSlice))) { this->fillResonance(col, posDaughter, negDaughter, resonanceProducts); } From 19880781cb5232e34219bc85c50575888eda7e70 Mon Sep 17 00:00:00 2001 From: hzalewski Date: Tue, 20 Oct 2026 11:11:10 +0200 Subject: [PATCH 357/648] [PWGJE] jetHadronsPid.cxx task: Add TPC and ITS clusters selection, new PID method and pt-dependent cut on DCA (#18345) Co-authored-by: Leonard Co-authored-by: Leonard Lorenc <107263905+llorenc-11@users.noreply.github.com> --- PWGJE/Tasks/jetHadronsPid.cxx | 38 +++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/PWGJE/Tasks/jetHadronsPid.cxx b/PWGJE/Tasks/jetHadronsPid.cxx index 2bdce5fb820..f1e59447406 100644 --- a/PWGJE/Tasks/jetHadronsPid.cxx +++ b/PWGJE/Tasks/jetHadronsPid.cxx @@ -104,7 +104,10 @@ struct JetHadronsPid { Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; - Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 70, "minimum number of TPC crossed pad rows"}; + Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 100, "minimum number of TPC crossed pad rows"}; + Configurable minTpcNclusters{"minTpcNclusters", 110, "Minimum number of clusters in TPC"}; + Configurable minItsNclusters{"minItsNclusters", 7, "Minimum number of clusters in ITS"}; + Configurable minItsIbNclusters{"minItsIbNclusters", 3, "Minimum number of clusters in inner barrel (max 3) of ITS"}; Configurable minChiSquareTpc{"minChiSquareTpc", 0.0, "minimum TPC chi^2/Ncls"}; Configurable maxChiSquareTpc{"maxChiSquareTpc", 4.0, "maximum TPC chi^2/Ncls"}; Configurable maxChiSquareIts{"maxChiSquareIts", 36.0, "maximum ITS chi^2/Ncls"}; @@ -112,8 +115,10 @@ struct JetHadronsPid { Configurable maxPt{"maxPt", 4.0, "maximum pt of the tracks for PID analysis"}; Configurable minEta{"minEta", -0.8, "minimum eta"}; Configurable maxEta{"maxEta", +0.8, "maximum eta"}; - Configurable maxDcaxy{"maxDcaxy", 0.2, "Maximum DCAxy"}; - Configurable maxDcaz{"maxDcaz", 0.1, "Maximum DCAz"}; + Configurable maxDcaxy{"maxDcaxy", 0.004, "constant term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT, in cm"}; + Configurable maxDcaz{"maxDcaz", 0.004, "constant term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT, in cm"}; + Configurable dcaXyPtPart{"dcaXyPtPart", 0.013, "pt-dependent term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT"}; + Configurable dcaZPtPart{"dcaZPtPart", 0.013, "pt-dependent term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT"}; Configurable setMCDefaultItsParams{"setMCDefaultItsParams", true, "set MC default parameters"}; struct : ConfigurableGroup { @@ -1139,6 +1144,7 @@ struct JetHadronsPid { constexpr int ClosestMatch = 0; constexpr int ExclusiveMatch = 1; constexpr int RejectionBased = 2; + constexpr int NoRejection = 3; double const buffer = 999.0; double pt = track.pt(); @@ -1208,8 +1214,17 @@ struct JetHadronsPid { bool isPrMatch = (dForProtonsPr <= cfg.nSigmaCutPr); PidResult res{.isPion = false, .isKaon = false, .isProton = false}; - - if (cfg.pidMethod == ClosestMatch) { + if (cfg.pidMethod == NoRejection) { + if (isPiMatch) { + res.isPion = true; + } + if (isKaMatch) { + res.isKaon = true; + } + if (isPrMatch) { + res.isProton = true; + } + } else if (cfg.pidMethod == ClosestMatch) { if (isPiMatch && dForPionsPi < dForPionsKa && dForPionsPi < dForPionsPr) { res.isPion = true; } @@ -1269,6 +1284,15 @@ struct JetHadronsPid { if (track.tpcNClsCrossedRows() < minTpcNcrossedRows) { return false; } + if (track.tpcNClsFound() < minTpcNclusters) { + return false; + } + if (track.itsNCls() < minItsNclusters) { + return false; + } + if (track.itsNClsInnerBarrel() < minItsIbNclusters) { + return false; + } if (!track.passedTPCCrossedRowsOverNCls()) { return false; } @@ -1287,7 +1311,9 @@ struct JetHadronsPid { if (track.pt() < minPt || track.pt() > maxPt) { return false; } - if (std::abs(track.dcaXY()) > maxDcaxy || std::abs(track.dcaZ()) > maxDcaz) { + const double maxDcaxyCut = maxDcaxy + dcaXyPtPart / track.pt(); + const double maxDcazCut = maxDcaz + dcaZPtPart / track.pt(); + if (std::abs(track.dcaXY()) > maxDcaxyCut || std::abs(track.dcaZ()) > maxDcazCut) { return false; } return true; From 3144327b2b56344ecc9b9a3f2030c4d98af5df76 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:11:28 +0200 Subject: [PATCH 358/648] [PWGJE] Fix max pt lead bug for generated jets in jetSpectraESetask (#18317) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 6810033f1ab..a170ea714f3 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -88,7 +88,7 @@ struct JetSpectraEseTask { Configurable cfgUseMCEventWeights{"cfgUseMCEventWeights", false, "Flag for using MC event weights"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable checkLeadConstituentMinPtForMcpJets{"checkLeadConstituentMinPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; + Configurable checkLeadConstituentPtForMcpJets{"checkLeadConstituentPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; Configurable pTHatMaxMCD{"pTHatMaxMCD", 999.0, "maximum fraction of hard scattering for jet acceptance in detector MC"}; Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum fraction of hard scattering for jet acceptance in particle MC"}; Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; @@ -2077,6 +2077,9 @@ struct JetSpectraEseTask { if (!checkConstituentMinPt && !checkConstituentMaxPt) { checkConstituentPt = false; } + if (mcLevelIsParticleLevel && !checkLeadConstituentPtForMcpJets) { + checkConstituentPt = false; + } if (checkConstituentPt) { bool isMinLeadingConstituent = !checkConstituentMinPt; @@ -2085,7 +2088,7 @@ struct JetSpectraEseTask { for (const auto& constituent : jet.template tracks_as()) { double pt = constituent.pt(); - if ((!checkLeadConstituentMinPtForMcpJets && mcLevelIsParticleLevel) || (checkConstituentMinPt && pt >= leadingConstituentPtMin)) { // if the jet is mcp level and checkLeadConstituentMinPtForMcpJets is true, then the pt of the leading track of that jet does not need to be below the defined leadingConstituentPtMin cut + if ((checkConstituentMinPt && pt >= leadingConstituentPtMin)) { isMinLeadingConstituent = true; } if (checkConstituentMaxPt && pt > leadingConstituentPtMax) { From dabc1082fe40d065d08df73c94ef943eaf9e5db4 Mon Sep 17 00:00:00 2001 From: DongJo Kim Date: Tue, 20 Oct 2026 14:19:03 +0300 Subject: [PATCH 359/648] [PWGCF] Add a JCorran D-meson v02(pT) task. (#18362) --- PWGCF/JCorran/Tasks/CMakeLists.txt | 10 + PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx | 375 +++++++++++++++++++++ PWGCF/JCorran/Tasks/jV02DmesonToy.cxx | 153 +++++++++ 3 files changed, 538 insertions(+) create mode 100644 PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx create mode 100644 PWGCF/JCorran/Tasks/jV02DmesonToy.cxx diff --git a/PWGCF/JCorran/Tasks/CMakeLists.txt b/PWGCF/JCorran/Tasks/CMakeLists.txt index 465083509d0..4c9c34b46c5 100644 --- a/PWGCF/JCorran/Tasks/CMakeLists.txt +++ b/PWGCF/JCorran/Tasks/CMakeLists.txt @@ -39,6 +39,16 @@ o2physics_add_dpl_workflow(epdzeroflow-analysis PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(j-v02-dmeson-analysis + SOURCES jV02DmesonAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(j-v02-dmeson-toy + SOURCES jV02DmesonToy.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(j-fluc-efficiency-task SOURCES jFlucEfficiencyTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::JCorran diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx new file mode 100644 index 00000000000..06b6d01cf12 --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -0,0 +1,375 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonAnalysis.cxx +/// \brief D0 v02(pT) on the JCorran derived tables +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// v02(pT) = _c / (_c ). +/// X and Y are the corrected Q2 of two forward detectors (default FT0A x FT0C) +/// from the combined Qvectors table, with the same indexing as jEPDzeroFlowAnalysis. +/// Z_D(pT) = N_D(pT) / N_ch. N_D comes from CF2ProngTracks (D0 and D0bar). +/// N_ch comes from CFTracks. Both derived tables are indexed to CFCollisions, +/// so the original collision is matched through CFCollRefs (one row per kept +/// collision, written by filter-correlations with cfgTransientTables). +/// The D azimuth is not part of the Q-vector. +/// Do not divide by an extra resolution: it cancels in the ratio. +/// +/// Definition: arXiv:2506.18690, Appendix A. + +#include "PWGCF/DataModel/CorrelationsDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonAnalysis { + enum Detector { + kFT0C = 0, + kFT0A, + kFT0M, + kFV0A, + kTPCpos, + kTPCneg, + kTPCall + }; + static constexpr int NDetectors = 7; + static constexpr int NCorrections = 4; + static constexpr int FullyCorrected = 3; + static constexpr int StepAccepted = 5; + static constexpr float InvalidQvector = 900.f; + + using MyCollisions = soa::Join; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable cfgCentEst{"cfgCentEst", "FT0C", "Centrality estimator: FT0C or FT0M"}; + Configurable cfgRefAName{"cfgRefAName", "FT0A", "Reference detector A: FT0A, FT0C, FT0M, FV0A, TPCpos, TPCneg, TPCall"}; + Configurable cfgRefBName{"cfgRefBName", "FT0C", "Reference detector B"}; + Configurable cfgQvecMode{"cfgQvecMode", 0, "Harmonic slot in qvector-table cfgnMods. 0 is v2 when cfgnMods starts at 2"}; + Configurable cfgNSubsample{"cfgNSubsample", 10, "Number of bootstrap subsamples"}; + Configurable cfgSeed{"cfgSeed", 1, "Seed for the subsample index"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "Maximum track occupancy"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "Minimum track occupancy"}; + Configurable cfgVtxZ{"cfgVtxZ", 10.f, "Maximum |z_vtx| (cm)"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality"}; + Configurable cfgCentMax{"cfgCentMax", 80.f, "Maximum centrality"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of CF tracks used for N_ch"}; + Configurable cfgPtMinCh{"cfgPtMinCh", 0.2f, "Minimum charged-track pT (GeV/c)"}; + Configurable cfgPtMaxCh{"cfgPtMaxCh", 3.f, "Maximum charged-track pT (GeV/c)"}; + Configurable cfgPtMinD{"cfgPtMinD", 1.f, "Minimum D0 pT (GeV/c)"}; + Configurable cfgPtMaxD{"cfgPtMaxD", 24.f, "Maximum D0 pT (GeV/c)"}; + Configurable cfgMinNch{"cfgMinNch", 10.f, "Minimum N_ch"}; + Configurable cfgMinSumAmpl{"cfgMinSumAmpl", 1e-4f, "Minimum Q-vector amplitude"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgCutNoSameBunchPileup{"cfgCutNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgCutGoodZvtxFT0vsPV{"cfgCutGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV"}; + Configurable cfgCutNoTimeFrameBorder{"cfgCutNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgCutNoITSROFrameBorder{"cfgCutNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRejectDaughters{"cfgRejectDaughters", false, "Drop D0 daughter CF tracks from N_ch"}; + + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {8, 0., 80.}, "Centrality axis"}; + ConfigurableAxis cfgAxisPtD{"cfgAxisPtD", {VARIABLE_WIDTH, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 12.f, 16.f, 24.f}, "D0 pT axis"}; + ConfigurableAxis cfgAxisPtHad{"cfgAxisPtHad", {VARIABLE_WIDTH, 0.2f, 0.4f, 0.6f, 0.8f, 1.f, 1.2f, 1.5f, 2.f, 2.5f, 3.f}, "Charged-hadron pT axis"}; + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {70, 1.70, 2.05}, "D0 invariant-mass axis"}; + + int mRefA = kFT0A; + int mRefB = kFT0C; + int mNSubsample = 1; + Preslice mPerCFTrack = aod::cftrack::cfCollisionId; + Preslice mPerCFCand = aod::cftrack::cfCollisionId; + const TAxis* mHadPtAxis = nullptr; + std::vector mHadCounts; + std::mt19937 mRng{1}; + std::uniform_int_distribution mSampleDist; + + int detectorId(const std::string& name) const + { + if (name == "FT0C") { + return kFT0C; + } + if (name == "FT0A") { + return kFT0A; + } + if (name == "FT0M") { + return kFT0M; + } + if (name == "FV0A") { + return kFV0A; + } + if (name == "TPCpos") { + return kTPCpos; + } + if (name == "TPCneg") { + return kTPCneg; + } + if (name == "TPCall") { + return kTPCall; + } + return -1; + } + + void init(InitContext const&) + { + mRefA = detectorId(cfgRefAName.value); + mRefB = detectorId(cfgRefBName.value); + if (mRefA < 0 || mRefB < 0 || mRefA == mRefB) { + LOGF(fatal, "cfgRefAName and cfgRefBName must be two different detectors"); + } + mNSubsample = cfgNSubsample > 0 ? cfgNSubsample.value : 1; + mRng.seed(static_cast(cfgSeed)); + mSampleDist = std::uniform_int_distribution(0, mNSubsample - 1); + + const AxisSpec subsampleAxis{mNSubsample, 0., static_cast(mNSubsample), "subsample"}; + const AxisSpec qaAxis{6, 0.5, 6.5, "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted"}; + + if (doprocessReference) { + histos.add("hadron/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("hadron/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("hadron/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Z", "Z = n(p_{T})/N_{ch};centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ", "XY Z;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XY_sub", "Bulk #LTXY#GT per subsample;subsample;centrality (%);#LTXY#GT", {HistType::kTProfile2D, {subsampleAxis, cfgAxisCent}}); + histos.add("hadron/Prof_Z_sub", "Z per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ_sub", "XYZ per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + mHadPtAxis = histos.get(HIST("hadron/Prof_Z"))->GetYaxis(); + mHadCounts.assign(mHadPtAxis->GetNbins(), 0.f); + } + if (doprocessD0) { + histos.add("D0/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("D0/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("D0/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/hMassRaw", "Weight 1/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorr", "Weight XY/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassRawSub", "Weight 1/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorrSub", "Weight XY/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + } + } + + template + float centralityOf(const TCollision& collision) + { + if (cfgCentEst.value == "FT0M") { + return collision.centFT0M(); + } + return collision.centFT0C(); + } + + template + int passedEventStep(const TCollision& collision, float& centrality) + { + if (cfgRequireSel8 && !collision.sel8()) { + return 1; + } + if (std::abs(collision.posZ()) > cfgVtxZ) { + return 2; + } + const bool rejected = + (cfgCutNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) || + (cfgCutGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) || + (cfgCutNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) || + (cfgCutNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) || + collision.trackOccupancyInTimeRange() > cfgMaxOccupancy || + collision.trackOccupancyInTimeRange() < cfgMinOccupancy; + if (rejected) { + return 3; + } + centrality = centralityOf(collision); + if (centrality < cfgCentMin || centrality > cfgCentMax) { + return 4; + } + return 5; + } + + template + bool referenceProduct(const TCollision& collision, float& xy) + { + const auto& re = collision.qvecRe(); + const auto& im = collision.qvecIm(); + const auto& amp = collision.qvecAmp(); + const int idxA = cfgQvecMode * NDetectors * NCorrections + mRefA * NCorrections + FullyCorrected; + const int idxB = cfgQvecMode * NDetectors * NCorrections + mRefB * NCorrections + FullyCorrected; + const int ampA = cfgQvecMode * NDetectors + mRefA; + const int ampB = cfgQvecMode * NDetectors + mRefB; + if (idxA >= static_cast(re.size()) || idxB >= static_cast(im.size()) || ampA >= static_cast(amp.size()) || ampB >= static_cast(amp.size())) { + return false; + } + if (!(amp[ampA] > cfgMinSumAmpl) || !(amp[ampB] > cfgMinSumAmpl)) { + return false; + } + if (re[idxA] > InvalidQvector || re[idxB] > InvalidQvector) { + return false; + } + xy = re[idxA] * re[idxB] + im[idxA] * im[idxB]; + return std::isfinite(xy); + } + + template + float countCharged(const TTracks& tracks, const std::unordered_set& daughters, bool differential) + { + float nch = 0.f; + if (differential) { + std::fill(mHadCounts.begin(), mHadCounts.end(), 0.f); + } + for (const auto& track : tracks) { + const float pt = track.pt(); + if (track.sign() == 0 || pt < cfgPtMinCh || pt >= cfgPtMaxCh || std::abs(track.eta()) > cfgEtaMax) { + continue; + } + if (cfgRejectDaughters && daughters.contains(static_cast(track.globalIndex()))) { + continue; + } + nch += 1.f; + if (differential && mHadPtAxis != nullptr) { + const int bin = mHadPtAxis->FindBin(pt); + if (bin >= 1 && bin <= mHadPtAxis->GetNbins()) { + mHadCounts[bin - 1] += 1.f; + } + } + } + return nch; + } + + static bool isD0Decay(uint8_t decay) + { + return decay == aod::cf2prongtrack::D0ToPiK || decay == aod::cf2prongtrack::D0barToKPi || decay == aod::cf2prongtrack::D0barToKPiExclusive; + } + + template + static int cfCollisionIndex(const TRefs& refs) + { + if (refs.size() != 1) { + return -1; + } + return static_cast(refs.begin().globalIndex()); + } + + void processReference(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReference, "Charged-hadron v02(pT) closure", true); + + void processD0(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0, "D0 and D0bar v02(pT) from CF2ProngTracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx new file mode 100644 index 00000000000..46f72929cf6 --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -0,0 +1,153 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonToy.cxx +/// \brief One timeframe of synthetic collisions for JV02DmesonAnalysis. +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// 200 events at centrality 25. N_ch = 20. +/// Even events: XY = 0.08, 6 tracks in pT [1.0, 1.2), 4 D0 at mass 1.864 and pT 3.5. +/// Odd events: XY = 0.02, 2 tracks in pT [1.0, 1.2), 1 D0 at the same mass and pT. +/// The remaining charged tracks sit at pT 0.3. +/// Expected charged-hadron v02 in the 1.1 GeV/c bin is 0.3. +/// Expected D0 v02, with an empty sideband, is 0.36. + +#include "PWGCF/DataModel/CorrelationsDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonToy { + Produces bcs; + Produces collisions; + Produces evsels; + Produces centsC; + Produces centsM; + Produces qvectors; + Produces cfCollisions; + Produces cfRefs; + Produces cfTracks; + Produces cfCandidates; + + void fill() + { + constexpr int NEvents = 200; + constexpr int NDetectors = 7; + constexpr int NCorrections = 4; + constexpr int FullyCorrected = 3; + constexpr int Ft0C = 0; + constexpr int Ft0A = 1; + constexpr int EventPair = 2; + constexpr float Centrality = 25.f; + constexpr float Nch = 20.f; + const uint64_t selection = + (uint64_t{1} << aod::evsel::kNoSameBunchPileup) | + (uint64_t{1} << aod::evsel::kIsGoodZvtxFT0vsPV) | + (uint64_t{1} << aod::evsel::kNoTimeFrameBorder) | + (uint64_t{1} << aod::evsel::kNoITSROFrameBorder); + + std::vector re(NDetectors * NCorrections, 0.f); + std::vector im(NDetectors * NCorrections, 0.f); + std::vector amp(NDetectors, 0.f); + amp[Ft0A] = 1.f; + amp[Ft0C] = 1.f; + + for (int ievt = 0; ievt < NEvents; ++ievt) { + const bool high = ievt % EventPair == 0; + const float xy = high ? 0.08f : 0.02f; + const float q = std::sqrt(xy); + re[Ft0C * NCorrections + FullyCorrected] = q; + re[Ft0A * NCorrections + FullyCorrected] = q; + + bcs(544116, static_cast(ievt), uint64_t{0}, uint64_t{0}); + collisions(ievt, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, uint16_t{0}, 1.f, uint16_t{20}, 0.f, 0.f); + evsels(uint32_t{0}, selection, uint32_t{0}, false, true, ievt, -1, -1, -1, -1, 100, 0.f, 0.f); + centsC(Centrality); + centsM(Centrality); + qvectors(Centrality, true, re, im, amp); + cfCollisions(544116, 0.f, Nch, uint64_t{0}); + cfRefs(ievt); + + const int nSignalTracks = high ? 6 : 2; + for (int itrk = 0; itrk < static_cast(Nch); ++itrk) { + const float pt = itrk < nSignalTracks ? 1.1f : 0.3f; + const int8_t sign = itrk % EventPair == 0 ? int8_t{1} : int8_t{-1}; + cfTracks(ievt, pt, 0.f, 0.f, sign, uint8_t{1}); + } + const int nD0 = high ? 4 : 1; + for (int icand = 0; icand < nD0; ++icand) { + cfCandidates(ievt, 0, 1, 3.5f, 0.f, 0.f, static_cast(o2::constants::physics::MassD0), static_cast(aod::cf2prongtrack::D0ToPiK)); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + DataProcessorSpec{ + .name = "jv02-dmeson-toy", + .inputs = {}, + .outputs = { + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + }, + .algorithm = AlgorithmSpec{adaptStateless([](ProcessingContext& pc) { + JV02DmesonToy toy; + toy.bcs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.collisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.evsels.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsC.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsM.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.qvectors.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCollisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfRefs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfTracks.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCandidates.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.fill(); + pc.outputs().make(Output{"TFN", "TFNumber"}) = 1; + pc.outputs().make(Output{"TFF", "TFFilename"}) = std::string{"toy"}; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + })}, + }, + }; +} From b313b31e65313e162a2e48226440e82f2861dcf6 Mon Sep 17 00:00:00 2001 From: arvindkhuntia <31609955+arvindkhuntia@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:32:18 +0200 Subject: [PATCH 360/648] [PWGJE] Add configurable MC closure split and antiproton DCA templates (#18347) Co-authored-by: Arvind Khuntia --- PWGJE/Tasks/nucleiInJets.cxx | 189 ++++++++++++++++++++++++++++++++--- 1 file changed, 175 insertions(+), 14 deletions(-) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 156020d18b3..2abfb2f8fa3 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -48,7 +48,6 @@ #include #include #include -#include #include #include @@ -57,6 +56,7 @@ #include #include #include +#include #include #include @@ -177,6 +177,8 @@ struct nucleiInJets { Configurable useTOFVeto{"useTOFVeto", false, "true: use TOF veto, false: no TOF veto"}; Configurable isRequireHitsInITSLayers{"isRequireHitsInITSLayers", true, "true: at least one hit in the its inner layes"}; Configurable useMcC{"useMcC", true, "use mcC"}; + Configurable mcClosureTrainingFraction{"mcClosureTrainingFraction", 0.5f, "fraction of events assigned to MC correction training; the rest are data-like"}; + Configurable mcClosureSplitSeed{"mcClosureSplitSeed", 1234, "random seed for MC/data-like split (0: automatic seed)"}; Configurable usebkgSubractionMC{"usebkgSubractionMC", true, "use rho-area background subtraction for detector-level matched MC jets"}; Configurable cfgjetPtBkgSubMinMC{"cfgjetPtBkgSubMinMC", 10.0f, "minimum detector-level matched MC jet pT after optional background subtraction"}; Configurable useFullConeAcceptance{"useFullConeAcceptance", true, "require jet/perpendicular-cone axes to satisfy |eta_axis| < cfgtrkMaxEta - cfgjetR for jet and efficiency histograms"}; @@ -267,13 +269,33 @@ struct nucleiInJets { OutputObj zorroSummary{"zorroSummary"}; Service ccdb; Service pdgDB; - TRandom3 randUniform; + uint64_t mcClosureSplitSeedEffective = 1; + + bool useTrainingHistForMcCollision(int64_t mcCollisionId) + { + // Use the MC collision ID as part of the seed, so both process functions + // obtain the same decision without storing one entry per collision. + const auto collisionId = static_cast(mcCollisionId); + std::mt19937_64 collisionRandom(mcClosureSplitSeedEffective ^ collisionId); + std::uniform_real_distribution uniform(0.0, 1.0); + return uniform(collisionRandom) < mcClosureTrainingFraction; + } + void init(o2::framework::InitContext&) { if (doprocessJetTracksData && doprocessJetTracksDataLfPid) { LOGP(fatal, "only one process function should be enabled!!!"); } + if (!(mcClosureTrainingFraction >= 0.f && mcClosureTrainingFraction <= 1.f)) { + LOGP(fatal, "mcClosureTrainingFraction must be between 0 and 1, inclusive"); + } + if (mcClosureSplitSeed == 0) { + std::random_device randomDevice; + mcClosureSplitSeedEffective = (static_cast(randomDevice()) << 32) ^ randomDevice(); + } else { + mcClosureSplitSeedEffective = static_cast(mcClosureSplitSeed); + } jetHist.print(); // const AxisSpec PtAxis = {100, 0, 10.0}; const AxisSpec PtJetAxis = {100, 0, 100.0}; @@ -642,6 +664,16 @@ struct nucleiInJets { jetHist.add(("tracksInc/dcaxy/rec/" + particle + "/tpcPtVsDcaxy3DPIDTOF").c_str(), "pT vs Dcaxy vs Centrality (PID TOF)", HistType::kTH3F, {{PtAxis}, {100, 0, 100}, dcaxyAxis}); } + // DCAxy templates are needed by both the data and reconstructed-MC processes. + const AxisSpec antiProtonTemplateOriginAxis{4, 0.5, 4.5, "1 primary #bar{p}, 2 material #bar{p}, 3 weak-decay #bar{p}, 4 other/fake"}; + for (const auto& region : {"jetCone", "perpCone"}) { + for (const auto& pid : {"TPC", "TPCTOF", "TPCTOFVeto"}) { + const std::string base = std::string("feeddown/antiProton/template/") + region + "/" + pid + "/"; + jetHist.add(base + "data", "data #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}", HistType::kTH2D, {{PtAxis}, {dcaxyAxis}}); + jetHist.add(base + "mc", "MC #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}; truth origin", HistType::kTH3D, {{PtAxis}, {dcaxyAxis}, {antiProtonTemplateOriginAxis}}); + } + } + if (isMC) { // inc jetHist.add("recInc/eventStat", "Event statistics (inclusive)", HistType::kTH1F, {{6, 0.f, 6.f}}); @@ -818,6 +850,19 @@ struct nucleiInJets { jetHist.add("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType", "Pt (gen, mcC, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcClosure/hSelectedRecoEvents", "matched reconstructed events per closure sample; sample; events", HistType::kTH1D, {{2, 0., 2.}}); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(1, "training"); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(2, "data-like"); + for (const auto& sample : {"training", "dataLike"}) { + for (const auto& region : {"jetCone", "perpCone"}) { + const std::string base = std::string("eff/recmatched/mcClosure/") + sample + "/" + region + "/"; + jetHist.add(base + "hRecAll", "matched primary tracks; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOFVeto", "matched primary tracks with TPC PID and TOF veto; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOF", "matched primary tracks with TPC and TOF PID; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hGen", "generated primary particles; true #it{p}_{T}; particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + } + } + jetHist.add("jetSelCorr/genSel/hLeadingJetPt", "particle-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{gen} (GeV/#it{c}); Entries", HistType::kTH1F, {{100, 0., 100.}}); jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); jetHist.add("jetSelCorr/recoSel/hMatchedLeadingJetPt", "selected matched leading jet; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); #it{p}_{T,lead}^{gen} (GeV/#it{c})", HistType::kTH2F, {{120, -20., 100.}, {100, 0., 100.}}); @@ -953,6 +998,36 @@ struct nucleiInJets { template void fillTrackInfo(const TracksType& trk, const JetType& jets, std::vector& leadingJetPtEtaPhi, float backgroundRho = -1.0f) { + if constexpr (!IsMC) { + if (isWithLeadingJet && leadingJetPtEtaPhi.size() >= 3 && isConeAxisAccepted(leadingJetPtEtaPhi[1]) && + backgroundRho > 0.f && leadingJetPtEtaPhi[0] - backgroundRho * M_PI * cfgjetR * cfgjetR > cfgjetPtBkgSubMin && + trk.sign() < 0 && trk.pt() <= cMaxPt && isTrackSelectedWithoutDcaxy(trk) && + (!useRapidityCutForPID || isRapiditySelectedForPID(trk.rapidity(o2::constants::physics::MassProton)))) { + const bool tpcPassed = std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr; + if (tpcPassed) { + const bool tofPassed = trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = leadingJetPtEtaPhi[1] - trk.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(leadingJetPtEtaPhi[2] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + const auto perpPhi = getPerpendicuarPhi(leadingJetPtEtaPhi[2]); + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - trk.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + } + } + } if (!isTrackSelected(trk)) return; if (trk.pt() > cMaxPt) @@ -2483,8 +2558,8 @@ struct nucleiInJets { jetHist.fill(HIST("recmatched/vertexZ"), collision.posZ()); - // Event-wise random splitting for closure test: decide once per event - bool useDataLikeHist = (randUniform.Uniform(0, 1) < 0.5); + // One random choice is shared by the reconstructed and generated spectra of this event. + const bool useTrainingHist = useTrainingHistForMcCollision(collision.mcCollision().globalIndex()); const float backgroundRho = collision.rho(); if (usebkgSubractionMC && backgroundRho <= 0.f) { return; @@ -2580,13 +2655,16 @@ struct nucleiInJets { indexJet = std::distance(mcdJetIds.begin(), leadingMatch); } if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); else jetHist.fill(HIST("recmatched/mcC/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } else { jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } + if (useMcC) { + jetHist.fill(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"), useTrainingHist ? 0.5 : 1.5); + } if (useLeadingJetDetLevelValue) { leadingJetWithPtEtaPhi[0] = mcdJetPt.at(indexJet); leadingJetWithPtEtaPhi[1] = mcdJetEta.at(indexJet); @@ -2597,6 +2675,48 @@ struct nucleiInJets { leadingJetWithPtEtaPhi[2] = mcpJetPhi.at(indexJet); } + // Fill detector-level DCA templates before the DCAxy selection used by the spectra. + // mcdJetPt is already background-subtracted when usebkgSubractionMC is enabled. + const double templateJetPt = mcdJetPt.at(indexJet); + if (isWithLeadingJet && backgroundRho > 0.f && templateJetPt > cfgjetPtBkgSubMin) { + const double templateJetEta = mcdJetEta.at(indexJet); + const double templateJetPhi = mcdJetPhi.at(indexJet); + const auto perpPhi = getPerpendicuarPhi(templateJetPhi); + for (const auto& track : tracks) { + const auto completeTrack = track.track_as(); + if (completeTrack.sign() >= 0 || completeTrack.pt() > cMaxPt || !isTrackSelectedWithoutDcaxy(completeTrack) || + (useRapidityCutForPID && !isRapiditySelectedForPID(completeTrack.rapidity(o2::constants::physics::MassProton)))) + continue; + if (std::abs(completeTrack.tpcNSigmaPr()) >= cfgnTPCPIDPr) + continue; + int origin = 4; + if (track.has_mcParticle()) { + const auto mcTrack = track.mcParticle_as(); + if (mcTrack.pdgCode() == -2212) + origin = getParticleOriginType(mcTrack); + } + const bool tofPassed = completeTrack.hasTOF() && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = templateJetEta - completeTrack.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(templateJetPhi - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - completeTrack.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + } + } + for (const auto& track : tracks) { auto completeTrack = track.track_as(); if (std::fabs(completeTrack.eta()) > cfgtrkMaxEta) @@ -2606,7 +2726,7 @@ struct nucleiInJets { if (!track.has_mcParticle()) continue; auto mcTrack = track.mcParticle_as(); - if (!isRapiditySelectedForPID(mcTrack.y())) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcTrack.y())) continue; bool isTof(completeTrack.hasTOF()); @@ -2710,7 +2830,7 @@ struct nucleiInJets { const bool isTPCTOFVetoPassed = isTpcPassed && (!isTof || isTofPidPassed); jetHist.fill(HIST("eff/recmatched/pt/PtParticleType"), mcTrack.pt(), jetFlag, particleType); if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) jetHist.fill(HIST("eff/recmatched/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else jetHist.fill(HIST("eff/recmatched/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); @@ -2726,6 +2846,21 @@ struct nucleiInJets { if (jetFlag) { jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); + if (useMcC) { + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } if (isTpcPassed) jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPC"), mcTrack.pt(), mcTrack.eta(), particleType); if (isTof) @@ -2740,7 +2875,22 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleType"), mcTrack.pt(), particleType); jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); if (useMcC) { - if (useDataLikeHist) + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } + if (useMcC) { + if (useTrainingHist) jetHist.fill(HIST("eff/recmatched/perpCone/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else jetHist.fill(HIST("eff/recmatched/perpCone/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); @@ -2771,7 +2921,7 @@ struct nucleiInJets { continue; if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) continue; - if (!isRapiditySelectedForPID(mcParticle.y())) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcParticle.y())) continue; bool jetFlagMC = false; bool jetFlagPerpConeMC = false; @@ -2806,9 +2956,15 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); if (jetFlagMC) { jetHist.fill(HIST("eff/recmatched/gen/jetCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); + if (useMcC) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hGen"), mcParticle.pt(), particleType); + } } if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) { jetHist.fill(HIST("eff/recmatched/mcC/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); } else { jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); @@ -2818,7 +2974,13 @@ struct nucleiInJets { jetHist.fill(HIST("eff/recmatched/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); jetHist.fill(HIST("eff/recmatched/gen/perpCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hGen"), mcParticle.pt(), particleType); + } + if (useMcC) { + if (useTrainingHist) { jetHist.fill(HIST("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); } else { jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); @@ -2957,8 +3119,7 @@ struct nucleiInJets { return; jetHist.fill(HIST("genmatched/vertexZ"), collision.posZ()); - // Event-wise random splitting for closure test: decide once per event - bool useDataLikeHist = (randUniform.Uniform(0, 1) < 0.5); + const bool useTrainingHist = useTrainingHistForMcCollision(collision.globalIndex()); std::vector mcpJetPt{}; std::vector mcpJetPhi{}; @@ -3010,7 +3171,7 @@ struct nucleiInJets { jetHist.fill(HIST("genmatched/hRecMatchedJetEta"), leadingMCPJet.eta(), leadingMCPJet.eta() - mcdjet.eta()); if (useMcC) { - if (useDataLikeHist) { + if (useTrainingHist) { jetHist.fill(HIST("genmatched/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); } else { jetHist.fill(HIST("genmatched/mcC/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); From 73d3c4dcb5d80f6d7e04c6175bf1d111ac10547b Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Tue, 20 Oct 2026 14:50:11 +0200 Subject: [PATCH 361/648] [PWGEM] Add option to save Dalitz and two-gamma events (#18363) Co-authored-by: jokonig --- .../PhotonMeson/TableProducer/skimmerDalitzEvents.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx index e038b916848..4d5bcf13a00 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx @@ -59,6 +59,7 @@ struct skimmerDalitzEvents { Configurable minNElecCand{"minNElecCand", 2, "minimum number of electrons/positron candidates in one event"}; Configurable minNGammaCand{"minNGammaCand", 1, "minimum number of V0 photon candidates in one event"}; + Configurable accPCMGGAndDalitz{"accPCMGGAndDalitz", false, "set to true if you want to create DD for both PCM-PCM and PCM-Dalitz"}; // ---------- for data ---------- template @@ -71,7 +72,7 @@ struct skimmerDalitzEvents { auto tracks = emPrimaryElecTmp.sliceBy(perCollisionEl, collision.globalIndex()); // check that there are at least minNElecCand in the collision - if (tracks.size() < minNElecCand) { + if (!accPCMGGAndDalitz && tracks.size() < minNElecCand) { continue; } @@ -79,10 +80,16 @@ struct skimmerDalitzEvents { auto photonskf = v0photonskfTmp.sliceBy(perCol_pcm, collision.globalIndex()); // check that there are at least minNGammaCand in the collision - if (photonskf.size() < minNGammaCand) { + if (!accPCMGGAndDalitz && photonskf.size() < minNGammaCand) { continue; } + if (accPCMGGAndDalitz) { + if (!(photonskf.size() >= 2 || (tracks.size() >= 2 && photonskf.size() >= 1))) { // o2-linter: disable=magic-number (settings to obtain either two photons in the event or one photon with one dielectron) + continue; + } + } + // Write electron tracks for (const auto& track : tracks) { emprimaryelectrons(track.collisionId(), track.trackId(), track.sign(), From 57eabe2178964077c80a708fe39a4acc4653aaf0 Mon Sep 17 00:00:00 2001 From: Yash Patley <52608802+yashpatley@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:04:50 +0530 Subject: [PATCH 362/648] [PWGCF] Lambda R2 Correlation MC on the fly update (#18367) --- .../Tasks/lambdaR2Correlation.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index bcf8852121d..0b25d067843 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -363,7 +363,7 @@ struct LambdaTableProducer { histos.add("QA/Lambda/DCA/h3f_Dca_vs_pT_cent", "DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); // MC Generated Histograms - if (doprocessMCRecoGen || doprocessMCReco) { + if (doprocessMCRecoGen || doprocessMCReco || doprocessMCGen) { // DCA analysis histos.add("QA/Lambda/DCA/h3f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); histos.add("QA/Lambda/DCA/h3f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); @@ -923,6 +923,13 @@ struct LambdaTableProducer { } PROCESS_SWITCH(LambdaTableProducer, processMCRecoGen, "Process for MC RecoGen", false); + + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + fillLambdaMcGenTables(mcCollision, mcParticles); + } + + PROCESS_SWITCH(LambdaTableProducer, processMCGen, "Process for MC Gen", false); }; struct LambdaTracksExtProducer { From c49c25a3b8a3ca3054f6c3dd47896cbf79e76193 Mon Sep 17 00:00:00 2001 From: Neelkamal Mallick <104082831+nmallick19@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:40:56 +0300 Subject: [PATCH 363/648] [PWGCF] JCorran/Core: Bug Fix to C0-scaling term (#18366) --- PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx | 12 +++++++++--- PWGCF/JCorran/Core/FlowJSPCAnalysis.h | 7 ++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx index 2fc35810957..3533e4c4987 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx @@ -121,15 +121,21 @@ void FlowJSPCAnalysis::calculateCorrelators(const int fCentBin) fillHistograms(fCentBin, j, correlationNum, correlationDenom, weightCorrelationNum, weightCorrelationDenom); } - // N_m = Re(Q_{0,1}); weight M_m. 3SPC → fN3, 4SPC → fN4. + // Effective multiplicity N_m (arXiv:2606.10258 Eq. IV.2 / manuscript eq:Neff): + // 1/N_m^{m-1} = sum_i M_m(N_i) N_i^{-(m-1)} / sum_i M_m(N_i), + // with M_m = N(N-1)...(N-m+1) = fCorrelDenoms[m-1] and N = Re(Q_{0,1}). + // TProfile stores _comb; invert offline as N_m = content^{-1/(m-1)}. + // 3SPC → fN3 (m=3); 4SPC → fN4 (m=4). if (mHistRegistry && qvecs) { const double nSel = qvecs->QvectorQC[0][1].Re(); const float centX = static_cast(fCentBin) + 0.5f; if (nSel > 0.0 && std::isfinite(nSel)) { if (mWhichSPC == 0 && fCorrelDenoms[2] > 0.0) { - mHistRegistry->fill(HIST("fN3"), centX, nSel, fCorrelDenoms[2]); + const double invN3Pow = 1.0 / (nSel * nSel); // N^{-(3-1)} + mHistRegistry->fill(HIST("fN3"), centX, invN3Pow, fCorrelDenoms[2]); } else if (mWhichSPC == 1 && fCorrelDenoms[3] > 0.0) { - mHistRegistry->fill(HIST("fN4"), centX, nSel, fCorrelDenoms[3]); + const double invN4Pow = 1.0 / (nSel * nSel * nSel); // N^{-(4-1)} + mHistRegistry->fill(HIST("fN4"), centX, invN4Pow, fCorrelDenoms[3]); } } } diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h index 97ddedc1af2..425c78ee9d0 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h @@ -79,9 +79,10 @@ class FlowJSPCAnalysis return; } mHistRegistry->add("FullCentrality", "FullCentrality", o2::framework::HistType::kTH1D, {{100, 0., 100.}}, true); - // Effective N_m per centrality class, weighted by M_m (arXiv:2606.10258 c0). - mHistRegistry->add("fN3", "Effective N_{3};centrality class;N_{3}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); - mHistRegistry->add("fN4", "Effective N_{4};centrality class;N_{4}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); + // Stores _comb = 1/N_m^{m-1} (arXiv:2606.10258 Eq. IV.2). + // Invert offline: N_m = content^{-1/(m-1)}. Weight is M_m. + mHistRegistry->add("fN3", "1/N_{3}^{2};centrality class;1/N_{3}^{2}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); + mHistRegistry->add("fN4", "1/N_{4}^{3};centrality class;1/N_{4}^{3}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); mHistRegistry->add("Centrality_0/fResults", "Numerators and denominators", {o2::framework::HistType::kTProfile, {{24, 0., 24.}}}, true); mHistRegistry->add("Centrality_0/fCovResults", "Covariance N*D", {o2::framework::HistType::kTProfile, {{48, 0., 48.}}}, true); mHistRegistry->add("Centrality_0/phiBefore", "Phi before", {o2::framework::HistType::kTH1D, {{100, 0., o2::constants::math::TwoPI}}}, true); From c6f337da36daa5ca357a531502e442ff60ca4d86 Mon Sep 17 00:00:00 2001 From: Igor Ptak <62450343+IgorPtak@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:12:57 +0200 Subject: [PATCH 364/648] [PWGLF] Adapt cascadeMLSelectionTreeCreator to derived MC and fix MC signal selection (#18364) --- PWGLF/DataModel/LFStrangenessMLTables.h | 4 ++ .../cascadeMLSelectionTreeCreator.cxx | 66 ++++++++++++------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/PWGLF/DataModel/LFStrangenessMLTables.h b/PWGLF/DataModel/LFStrangenessMLTables.h index 7784a521dd7..983648f791d 100644 --- a/PWGLF/DataModel/LFStrangenessMLTables.h +++ b/PWGLF/DataModel/LFStrangenessMLTables.h @@ -180,6 +180,8 @@ DECLARE_SOA_COLUMN(DCACascDaughters, dcaCascDaughters, float); DECLARE_SOA_COLUMN(DCAv0topv, dcav0topv, float); DECLARE_SOA_COLUMN(V0PA, v0PA, float); DECLARE_SOA_COLUMN(CascPA, cascPA, float); +DECLARE_SOA_COLUMN(BachBaryonCosPA, bachBaryonCosPA, float); +DECLARE_SOA_COLUMN(BachBaryonDCAxyToPV, bachBaryonDCAxyToPV, float); DECLARE_SOA_COLUMN(IsXiMinus, isXiMinus, bool); DECLARE_SOA_COLUMN(IsXiPlus, isXiPlus, bool); @@ -231,6 +233,8 @@ DECLARE_SOA_TABLE(CascMLCandidates, "AOD", "CAMLCANDIDATES", cascmlcandidates::DCAv0topv, cascmlcandidates::V0PA, cascmlcandidates::CascPA, + cascmlcandidates::BachBaryonCosPA, + cascmlcandidates::BachBaryonDCAxyToPV, cascmlcandidates::IsXiMinus, cascmlcandidates::IsXiPlus, cascmlcandidates::IsOmegaMinus, diff --git a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx index a97f2dad659..7fe676a81e0 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx @@ -139,13 +139,15 @@ struct cascadeMLSelectionTreeCreator { float dcav0topv; float v0CosPA; float cascCosPA; + float bachBaryonCosPA; + float bachBaryonDCAxyToPV; // reserved for MC operation bool isXiMinus; bool isXiPlus; bool isOmegaMinus; bool isOmegaPlus; - } cascadeCandidate; + } cascadeCandidate{}; // Process candidate and store properties in object template @@ -161,10 +163,10 @@ struct cascadeMLSelectionTreeCreator { // Track quality cascadeCandidate.posITSCls = posTrackExtra.itsNCls(); cascadeCandidate.negITSCls = negTrackExtra.itsNCls(); - cascadeCandidate.bachITSCls = negTrackExtra.itsNCls(); + cascadeCandidate.bachITSCls = bachTrackExtra.itsNCls(); cascadeCandidate.posITSClsSizes = posTrackExtra.itsClusterSizes(); cascadeCandidate.negITSClsSizes = negTrackExtra.itsClusterSizes(); - cascadeCandidate.bachITSClsSizes = negTrackExtra.itsClusterSizes(); + cascadeCandidate.bachITSClsSizes = bachTrackExtra.itsClusterSizes(); cascadeCandidate.posTPCRows = posTrackExtra.tpcCrossedRows(); cascadeCandidate.negTPCRows = negTrackExtra.tpcCrossedRows(); cascadeCandidate.bachTPCRows = bachTrackExtra.tpcCrossedRows(); @@ -207,6 +209,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv = cand.dcav0topv(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.v0CosPA = cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.cascCosPA = cand.casccosPA(coll.posX(), coll.posY(), coll.posZ()); + cascadeCandidate.bachBaryonCosPA = cand.bachBaryonCosPA(); + cascadeCandidate.bachBaryonDCAxyToPV = cand.bachBaryonDCAxyToPV(); // MC flags cascadeCandidate.isXiMinus = false; @@ -214,36 +218,50 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaMinus = false; cascadeCandidate.isOmegaPlus = false; - if constexpr (requires { cand.pdgCode(); }) { - cascadeCandidate.isXiMinus = (cand.pdgCode() == 3312); - cascadeCandidate.isXiPlus = (cand.pdgCode() == -3312); - cascadeCandidate.isOmegaMinus = (cand.pdgCode() == 3334); - cascadeCandidate.isOmegaPlus = (cand.pdgCode() == -3334); + if constexpr (requires { cand.has_cascMCCore(); }) { + if (cand.has_cascMCCore()) { + auto cascMC = cand.template cascMCCore_as>(); + cascadeCandidate.isXiMinus = (cascMC.pdgCode() == 3312); + cascadeCandidate.isXiPlus = (cascMC.pdgCode() == -3312); + cascadeCandidate.isOmegaMinus = (cascMC.pdgCode() == 3334); + cascadeCandidate.isOmegaPlus = (cascMC.pdgCode() == -3334); + } } // base topological selections - if (cascadeCandidate.v0CosPA < v0setting_cospa) + if (cascadeCandidate.v0CosPA < v0setting_cospa) { return; - if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) + } + if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcapostopv) + } + if (std::abs(cascadeCandidate.dcapostopv) < v0setting_dcapostopv) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcanegtopv) + } + if (std::abs(cascadeCandidate.dcanegtopv) < v0setting_dcanegtopv) { return; - if (cascadeCandidate.dcabachtopv < cascadesetting_dcabachtopv) + } + if (std::abs(cascadeCandidate.dcabachtopv) < cascadesetting_dcabachtopv) { return; - if (cascadeCandidate.v0radius < v0setting_radius) + } + if (cascadeCandidate.v0radius < v0setting_radius) { return; - if (cascadeCandidate.cascCosPA < cascadesetting_cospa) + } + if (cascadeCandidate.cascCosPA < cascadesetting_cospa) { return; - if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) + } + if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) { return; - if (cascadeCandidate.cascradius < cascadesetting_cascradius) + } + if (cascadeCandidate.cascradius < cascadesetting_cascradius) { return; - if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) + } + if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) { return; - if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) + } + if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) { return; + } // Mass window selections cascadeCandidate.massWindows = 0; @@ -312,6 +330,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv, TMath::ACos(cascadeCandidate.v0CosPA), TMath::ACos(cascadeCandidate.cascCosPA), + cascadeCandidate.bachBaryonCosPA, + cascadeCandidate.bachBaryonDCAxyToPV, // MC identity cascadeCandidate.isXiMinus, @@ -320,17 +340,17 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaPlus); } - void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } - void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&, soa::Join const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } From bf10c6c3d53f5463e899dbc3a11b34c8051c870a Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Tue, 20 Oct 2026 19:26:38 +0200 Subject: [PATCH 365/648] [PWGLF] Nuclei proton correlation: extend ITS rejection for deuterons (#18368) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 90717213795..d66da98c8ae 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -637,7 +637,7 @@ struct HadronNucleiCorrelation { if (!doITSPID.value || isITSPID) { return signCheck; } - } else if (isTPCElRejection || isTOFPID) { + } else if (isTOFPID || (isTPCElRejection && (!doITSPID.value || isITSPID))) { return signCheck; } } @@ -1041,7 +1041,7 @@ struct HadronNucleiCorrelation { if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { if (!isPr) { registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); - if (isTPCElRejection || isTOFPID) { + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { registryQa.fill(HIST("QA/h3dTPCTOF_De_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); } } @@ -1054,7 +1054,7 @@ struct HadronNucleiCorrelation { if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { if (!isPr) { registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); - if (isTPCElRejection || isTOFPID) { + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); } } From f7539ce0e3a08e694a6276b730bc9fe27632c3f7 Mon Sep 17 00:00:00 2001 From: syano0822 <32352856+syano0822@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:28:34 +0900 Subject: [PATCH 366/648] [PWGLF] Add K0S-K0S flow analysis task (#18316) --- PWGLF/Tasks/Resonances/CMakeLists.txt | 5 + PWGLF/Tasks/Resonances/k0sK0sFlow.cxx | 926 ++++++++++++++++++++++++++ 2 files changed, 931 insertions(+) create mode 100644 PWGLF/Tasks/Resonances/k0sK0sFlow.cxx diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 238fc095c38..8c289ef8675 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -144,6 +144,11 @@ o2physics_add_dpl_workflow(kshortkshort PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k0s-k0s-flow + SOURCES k0sK0sFlow.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phipbpb SOURCES phipbpb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx new file mode 100644 index 00000000000..583ef56a6d4 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx @@ -0,0 +1,926 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file k0sK0sFlow.cxx +/// \brief K0S and K0S-K0S spectra, angular distributions, SP and EP observables +/// \author Satoshi Yano + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct K0sK0sFlow { + enum class PionPidMode : int { + TpcWithTofIfAvailable = 0, + TpcOnly = 1, + TpcAndTof = 2, + TofRequiredAboveMomentum = 4 + }; + + enum class EventSelectionStep : int { + All = 1, + Vertex, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoItsRofBorder, + Centrality, + NSteps + }; + + enum class TrackSelectionStep : int { + All = 1, + Kinematics, + TpcClusters, + TrackQuality, + TpcPid, + TofPid, + NSteps + }; + + enum class K0sSelectionStep : int { + All = 1, + Daughters, + Kinematics, + Topology, + DaughterDca, + DecayGeometry, + Armenteros, + LambdaRejection, + NSteps + }; + + enum class PairSelectionStep : int { + All = 1, + NoSharedDaughters, + SelectedK0s, + ClosePair, + Kinematics, + SignalMass, + NSteps + }; + + enum class LikeSignCategory : int { + PositivePositive, + NegativeNegative, + PositiveNegative + }; + + enum class SidebandRegion : int { + Lower = -1, + Outside = 0, + Upper = 1 + }; + + static constexpr double VectorTolerance = 1.e-12; + static constexpr int QVectorComponents = 4; + + template + static constexpr auto enumValue(E value) + { + return static_cast>(value); + } + + HistogramRegistry registry{"registry"}; + SliceCache cache; + o2::vertexing::DCAFitterN<2> lsFitter; + + struct : ConfigurableGroup { + Configurable maxAbsZvtx{"maxAbsZvtx", 10.f, "maximum |z vertex| (cm)"}; + Configurable requireSel8{"requireSel8", true, "require sel8"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "reject same-bunch pileup"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", true, "reject TF borders"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", true, "reject ITS ROF borders"}; + Configurable minCentrality{"minCentrality", 0.f, "minimum FT0C centrality"}; + Configurable maxCentrality{"maxCentrality", 100.f, "maximum FT0C centrality"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable daughterMinPt{"daughterMinPt", 0.1f, "daughter minimum pT"}; + Configurable maxAbsEta{"maxAbsEta", 0.8f, "daughter maximum |eta|"}; + Configurable minTPCRows{"minTPCRows", 70, "minimum TPC crossed rows"}; + Configurable minTPCRowsOverFindable{"minTPCRowsOverFindable", 0.8f, "minimum crossed/findable ratio"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 4.f, "maximum TPC chi2/cluster"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 36.f, "maximum ITS chi2/cluster"}; + Configurable minITSClusters{"minITSClusters", 0, "minimum ITS clusters; zero disables"}; + Configurable pidMode{"pidMode", static_cast(PionPidMode::TpcWithTofIfAvailable), "0 TPC and TOF if present; 1 TPC only; 2 require TOF; 4 require TOF above momentum threshold"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5.f, "maximum pion TPC |n sigma|"}; + Configurable maxTOFNSigmaPi{"maxTOFNSigmaPi", 3.f, "maximum pion TOF |n sigma|"}; + Configurable tofRequiredAboveP{"tofRequiredAboveP", 0.6f, "momentum threshold for PID mode 4"}; + } daughterCuts; + + struct : ConfigurableGroup { + Configurable k0sMinPt{"k0sMinPt", 0.f, "K0S minimum pT"}; + Configurable k0sMaxPt{"k0sMaxPt", 20.f, "K0S maximum pT"}; + Configurable k0sMaxAbsY{"k0sMaxAbsY", 0.5f, "K0S maximum |y|"}; + Configurable minCosPA{"minCosPA", 0.98f, "minimum cos(PA)"}; + Configurable maxDCADaughters{"maxDCADaughters", 0.3f, "maximum daughter DCA (cm)"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05f, "minimum positive daughter |DCA to PV| (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05f, "minimum negative daughter |DCA to PV| (cm)"}; + Configurable minRadius{"minRadius", 0.5f, "minimum decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.f, "maximum decay radius (cm)"}; + Configurable maxCtau{"maxCtau", 20.f, "maximum c tau (cm)"}; + Configurable useArmenterosCut{"useArmenterosCut", true, "apply K0S Armenteros cut"}; + Configurable armenterosSlope{"armenterosSlope", 0.2f, "require qT > slope |alpha|"}; + Configurable rejectLambda{"rejectLambda", true, "reject Lambda and anti-Lambda"}; + Configurable lambdaRejectionWindow{"lambdaRejectionWindow", 0.01f, "Lambda rejection half-width"}; + Configurable k0sMassMin{"k0sMassMin", 0.47f, "K0S pair-building mass minimum"}; + Configurable k0sMassMax{"k0sMassMax", 0.525f, "K0S pair-building mass maximum"}; + Configurable k0sLowerSidebandMin{"k0sLowerSidebandMin", 0.42f, "K0S lower sideband minimum"}; + Configurable k0sLowerSidebandMax{"k0sLowerSidebandMax", 0.46f, "K0S lower sideband maximum"}; + Configurable k0sUpperSidebandMin{"k0sUpperSidebandMin", 0.535f, "K0S upper sideband minimum"}; + Configurable k0sUpperSidebandMax{"k0sUpperSidebandMax", 0.575f, "K0S upper sideband maximum"}; + } k0Cuts; + + struct : ConfigurableGroup { + Configurable pairMinPt{"pairMinPt", 0.f, "pair minimum pT"}; + Configurable pairMaxPt{"pairMaxPt", 20.f, "pair maximum pT"}; + Configurable pairMaxAbsY{"pairMaxAbsY", 0.5f, "pair maximum |y|"}; + Configurable pairMassMin{"pairMassMin", 0.99f, "pair mass minimum"}; + Configurable pairMassMax{"pairMassMax", 3.f, "pair mass maximum"}; + Configurable enableClosePairRejection{"enableClosePairRejection", false, "enable minimum DeltaR cut"}; + Configurable minDeltaR{"minDeltaR", 0.0f, "minimum K0S-K0S DeltaR"}; + Configurable mixingDepth{"mixingDepth", 5, "number of mixed events"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable enableLikeSign{"enableLikeSign", true, "build like-sign V0 and V0-V0 background candidates"}; + Configurable likeSignBz{"likeSignBz", 5.f, "magnetic field Bz used by the like-sign DCA fitter (kG)"}; + Configurable likeSignFitterMaxR{"likeSignFitterMaxR", 200.f, "maximum radius used by the like-sign DCA fitter (cm)"}; + Configurable likeSignFitterMaxChi2{"likeSignFitterMaxChi2", 1.e9f, "maximum fitter chi2"}; + } likeSign; + + struct : ConfigurableGroup { + Configurable harmonic{"harmonic", 2, "flow harmonic"}; + Configurable correctionStep{"correctionStep", 3, "Q-vector correction step (0-3)"}; + Configurable numberOfDetectors{"numberOfDetectors", 7, "detectors stored in Qvectors"}; + Configurable ft0cId{"ft0cId", 0, "FT0C detector index"}; + Configurable ft0aId{"ft0aId", 1, "FT0A detector index"}; + Configurable tpcPosId{"tpcPosId", 4, "TPC positive-eta detector index"}; + Configurable minQAmplitude{"minQAmplitude", 1.e-4f, "minimum Q-vector amplitude"}; + } flow; + + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "centrality"}; + ConfigurableAxis axisK0Pt{"axisK0Pt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "K0S pT"}; + ConfigurableAxis axisPairPt{"axisPairPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT"}; + ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4, 0.6}, "pi-pi mass"}; + ConfigurableAxis axisPairMass{"axisPairMass", {402, 0.99, 3.0}, "K0S-K0S mass"}; + ConfigurableAxis axisAbsCosTheta{"axisAbsCosTheta", {10, 0., 1.}, "|cos(theta*)|"}; + ConfigurableAxis axisPhiStar{"axisPhiStar", {12, -3.141592653589793, 3.141592653589793}, "phi*"}; + ConfigurableAxis axisMixZvtx{"axisMixZvtx", {VARIABLE_WIDTH, -10., -5., 0., 5., 10.}, "event-mixing vertex-z bins"}; + ConfigurableAxis axisMixCent{"axisMixCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "event-mixing centrality bins"}; + + using Collisions = soa::Join; + using DaughterTracks = soa::Join; + Preslice perCollisionV0 = aod::v0data::collisionId; + Preslice perCollisionTrack = aod::track::collisionId; + using MixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + lsFitter.setPropagateToPCA(true); + lsFitter.setMaxR(likeSign.likeSignFitterMaxR); + lsFitter.setMinParamChange(1.e-3f); + lsFitter.setMinRelChi2Change(0.9f); + lsFitter.setMaxDZIni(1.e9f); + lsFitter.setMaxChi2(likeSign.likeSignFitterMaxChi2); + lsFitter.setUseAbsDCA(true); + lsFitter.setBz(likeSign.likeSignBz); + AxisSpec cent{axisCent, "FT0C centrality (%)"}, k0pt{axisK0Pt, "p_{T}(K^{0}_{S})"}, pairpt{axisPairPt, "p_{T}(K^{0}_{S}K^{0}_{S})"}; + AxisSpec k0mass{axisK0Mass, "m_{#pi#pi}"}, pairmass{axisPairMass, "m_{K^{0}_{S}K^{0}_{S}}"}; + AxisSpec absct{axisAbsCosTheta, "|cos#theta*|"}, phistar{axisPhiStar, "#phi*"}; + constexpr int NEventSelectionSteps = enumValue(EventSelectionStep::NSteps) - 1; + constexpr int NTrackSelectionSteps = enumValue(TrackSelectionStep::NSteps) - 1; + constexpr int NK0sSelectionSteps = enumValue(K0sSelectionStep::NSteps) - 1; + constexpr int NPairSelectionSteps = enumValue(PairSelectionStep::NSteps) - 1; + registry.add("Event/hEventCutFlow", "event cut flow", HistType::kTH1F, {{NEventSelectionSteps, 0.5, NEventSelectionSteps + 0.5}}); + registry.add("Event/hVertexZBefore", "vertex z before", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hVertexZAfter", "vertex z after", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hCentralityBefore", "centrality before", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("Event/hCentralityAfter", "centrality after", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Tracks/hCutFlow", "daughter cut flow", HistType::kTH1F, {{NTrackSelectionSteps, 0.5, NTrackSelectionSteps + 0.5}}); + registry.add("QA/Tracks/BeforeSelection/hPt", "daughter pT before", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/BeforeSelection/hEta", "daughter eta before", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAxyVsPt", "DCAxy vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAzVsPt", "DCAz vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT before", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCSignalVsP", "TPC signal before", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP", "TPC pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/BeforeSelection/hTOFBetaVsP", "TOF beta before", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP", "TOF pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hPt", "daughter pT", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/AfterSelection/hEta", "daughter eta", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/AfterSelection/hDCAxyVsPt", "DCAxy vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hDCAzVsPt", "DCAz vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/AfterSelection/hTPCSignalVsP", "TPC signal", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP", "TPC pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hTOFBetaVsP", "TOF beta", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP", "TOF pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/K0s/hCutFlow", "K0S cut flow", HistType::kTH1F, {{NK0sSelectionSteps, 0.5, NK0sSelectionSteps + 0.5}}); + registry.add("QA/K0s/BeforeSelection/hMassVsPt", "K0S before", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/BeforeSelection/hArmenteros", "Armenteros before", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/BeforeSelection/hCosPA", "cosPA before", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/BeforeSelection/hDCADaughters", "DCA daughters before", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/BeforeSelection/hCtau", "c tau before", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0s/AfterSelection/hMassVsPt", "K0S after (no mass window)", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/AfterSelection/hArmenteros", "Armenteros", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/AfterSelection/hCosPA", "cosPA", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/AfterSelection/hDCADaughters", "DCA daughters", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/AfterSelection/hCtau", "c tau", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0sK0s/hPairCutFlow", "pair cut flow", HistType::kTH1F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}}); + registry.add("QA/K0sK0s/hPairCutFlowVsCent", "pair cut flow vs centrality", HistType::kTH2F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}, cent}); + registry.add("QA/K0sK0s/SameEvent/hMass", "same-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/K0sK0s/SameEvent/hDeltaR", "same-event DeltaR", HistType::kTH1F, {{200, 0., 5.}}); + registry.add("QA/K0sK0s/SameEvent/hPt1VsPt2", "K0S pT correlation", HistType::kTH2F, {k0pt, k0pt}); + registry.add("QA/K0sK0s/SameEvent/hK0Mass1VsMass2", "K0S mass correlation", HistType::kTH2F, {k0mass, k0mass}); + registry.add("QA/K0sK0s/MixedEvent/hMass", "mixed-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/Flow/QVectors/hQxQyFT0C", "corrected FT0C q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyFT0A", "corrected FT0A q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyTPCpos", "corrected TPCpos q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/EventPlane/hPsi2VsCentFT0C", "FT0C event plane", HistType::kTH2F, {cent, {120, -1.5708, 1.5708}}); + registry.add("QA/Flow/Calibration/hCalibrationFlag", "Q-vector calibration flag", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("K0s/hCounts", "K0S counts", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/hCos2Weighted", "K0S EP numerator", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0C", "K0S SP numerator FT0C", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0A", "K0S SP numerator FT0A", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/LikeSign/PP/hCounts", "like-sign pi+pi+ candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/LikeSign/MM/hCounts", "like-sign pi-pi- candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + for (auto const& base : {std::string("K0sK0s/SameEvent/"), std::string("K0sK0s/MixedEvent/"), std::string("K0sK0s/Sideband/Combined/"), std::string("K0sK0s/Sideband/LowerLower/"), std::string("K0sK0s/Sideband/LowerUpper/"), std::string("K0sK0s/Sideband/UpperUpper/")}) { + registry.add((base + "hCounts").c_str(), "K0S-K0S counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "pair angular moment P2(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "pair angular moment P4(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("K0sK0s/SameEvent/hCos2Weighted", "pair EP numerator", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0C", "pair SP numerator FT0C", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0A", "pair SP numerator FT0A", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + for (auto const& base : {std::string("K0sK0s/LikeSign/PPPP/"), std::string("K0sK0s/LikeSign/MMMM/"), std::string("K0sK0s/LikeSign/PPMM/"), std::string("K0sK0s/LikeSign/Combined/")}) { + registry.add((base + "hCounts").c_str(), "like-sign K0S-K0S mimic counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "like-sign pair angular moment P2", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "like-sign pair angular moment P4", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("Resolution/SP/pFT0CFT0AVsCent", "FT0C-FT0A SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0CTPCposVsCent", "FT0C-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0ATPCposVsCent", "FT0A-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CFT0AVsCent", "FT0C-FT0A EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CTPCposVsCent", "FT0C-TPCpos EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0ATPCposVsCent", "FT0A-TPCpos EP correlation", HistType::kTProfile, {cent}); + + constexpr std::array EventSelectionLabels{"all", "vertex", "sel8", "no same-bunch pileup", "no TF border", "no ITS ROF border", "centrality"}; + constexpr std::array TrackSelectionLabels{"all", "kinematics", "TPC clusters", "track quality", "TPC PID", "TOF PID"}; + constexpr std::array K0sSelectionLabels{"all", "daughters", "kinematics", "topology", "daughter DCA", "decay geometry", "Armenteros", "Lambda rejection"}; + constexpr std::array PairSelectionLabels{"all", "no shared daughters", "selected K0S", "close pair", "kinematics", "signal mass"}; + auto eventCutFlow = registry.get(HIST("Event/hEventCutFlow")); + auto trackCutFlow = registry.get(HIST("QA/Tracks/hCutFlow")); + auto k0sCutFlow = registry.get(HIST("QA/K0s/hCutFlow")); + auto pairCutFlow = registry.get(HIST("QA/K0sK0s/hPairCutFlow")); + auto pairCutFlowVsCent = registry.get(HIST("QA/K0sK0s/hPairCutFlowVsCent")); + for (size_t index = 0; index < EventSelectionLabels.size(); ++index) { + eventCutFlow->GetXaxis()->SetBinLabel(index + 1, EventSelectionLabels[index]); + } + for (size_t index = 0; index < TrackSelectionLabels.size(); ++index) { + trackCutFlow->GetXaxis()->SetBinLabel(index + 1, TrackSelectionLabels[index]); + } + for (size_t index = 0; index < K0sSelectionLabels.size(); ++index) { + k0sCutFlow->GetXaxis()->SetBinLabel(index + 1, K0sSelectionLabels[index]); + } + for (size_t index = 0; index < PairSelectionLabels.size(); ++index) { + pairCutFlow->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + pairCutFlowVsCent->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + } + } + + SidebandRegion sidebandRegion(float mass) const + { + if (mass >= k0Cuts.k0sLowerSidebandMin.value && mass < k0Cuts.k0sLowerSidebandMax.value) { + return SidebandRegion::Lower; + } + if (mass >= k0Cuts.k0sUpperSidebandMin.value && mass < k0Cuts.k0sUpperSidebandMax.value) { + return SidebandRegion::Upper; + } + return SidebandRegion::Outside; + } + + void fillSidebandPair(ROOT::Math::PxPyPzMVector const& d1, ROOT::Math::PxPyPzMVector const& m, float cent, SidebandRegion side1, SidebandRegion side2) + { + auto ang = decayAngles(d1, m); + const double cosTheta2 = ang[0] * ang[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/Sideband/Combined/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (side1 == SidebandRegion::Lower && side2 == SidebandRegion::Lower) { + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else if (side1 == SidebandRegion::Upper && side2 == SidebandRegion::Upper) { + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } + } + + template + bool selectEvent(C const& c, bool fill) + { + float cent = c.centFT0C(); + if (fill) { + registry.fill(HIST("Event/hVertexZBefore"), c.posZ()); + registry.fill(HIST("Event/hCentralityBefore"), cent); + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::All)); + } + if (std::abs(c.posZ()) > eventCuts.maxAbsZvtx) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Vertex)); + } + if (eventCuts.requireSel8 && !c.sel8()) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Sel8)); + } + if (eventCuts.requireNoSameBunchPileup && !c.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoSameBunchPileup)); + } + if (eventCuts.requireNoTimeFrameBorder && !c.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoTimeFrameBorder)); + } + if (eventCuts.requireNoITSROFrameBorder && !c.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoItsRofBorder)); + } + if (cent < eventCuts.minCentrality || cent >= eventCuts.maxCentrality) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Centrality)); + registry.fill(HIST("Event/hVertexZAfter"), c.posZ()); + registry.fill(HIST("Event/hCentralityAfter"), cent); + } + return true; + } + + template + bool selectDaughter(T const& t, bool qa) + { + bool hasTOF = t.hasTOF(); + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::All)); + registry.fill(HIST("QA/Tracks/BeforeSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + if (t.pt() < daughterCuts.daughterMinPt || std::abs(t.eta()) > daughterCuts.maxAbsEta) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::Kinematics)); + } + if (t.tpcNClsCrossedRows() < daughterCuts.minTPCRows || t.tpcCrossedRowsOverFindableCls() < daughterCuts.minTPCRowsOverFindable) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcClusters)); + } + if (t.tpcChi2NCl() > daughterCuts.maxTPCChi2NCl || t.itsChi2NCl() > daughterCuts.maxITSChi2NCl) { + return false; + } + if (daughterCuts.minITSClusters > 0 && t.itsNCls() < daughterCuts.minITSClusters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TrackQuality)); + } + if (std::abs(t.tpcNSigmaPi()) > daughterCuts.maxTPCNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcPid)); + } + const auto pidMode = static_cast(daughterCuts.pidMode.value); + bool requireTOF = pidMode == PionPidMode::TpcAndTof || (pidMode == PionPidMode::TofRequiredAboveMomentum && t.p() >= daughterCuts.tofRequiredAboveP); + bool useTOF = requireTOF || (pidMode == PionPidMode::TpcWithTofIfAvailable && hasTOF); + if (requireTOF && !hasTOF) { + return false; + } + if (useTOF && std::abs(t.tofNSigmaPi()) > daughterCuts.maxTOFNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TofPid)); + registry.fill(HIST("QA/Tracks/AfterSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/AfterSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + return true; + } + + template + bool selectK0(C const& c, V const& v, DaughterTracks const&, bool qa) + { + float ctau = v.distovertotmom(c.posX(), c.posY(), c.posZ()) * o2::constants::physics::MassK0Short; + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::All)); + registry.fill(HIST("QA/K0s/BeforeSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/BeforeSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/BeforeSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCtau"), ctau); + } + auto pos = v.template posTrack_as(); + auto neg = v.template negTrack_as(); + if (!selectDaughter(pos, qa)) { + return false; + } + if (!selectDaughter(neg, qa)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Daughters)); + } + if (v.pt() < k0Cuts.k0sMinPt || v.pt() >= k0Cuts.k0sMaxPt || std::abs(v.rapidity(0)) >= k0Cuts.k0sMaxAbsY) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Kinematics)); + } + if (v.v0cosPA() < k0Cuts.minCosPA || v.dcaV0daughters() > k0Cuts.maxDCADaughters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Topology)); + } + if (std::abs(v.dcapostopv()) < k0Cuts.minDCAPosToPV || std::abs(v.dcanegtopv()) < k0Cuts.minDCANegToPV) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DaughterDca)); + } + if (v.v0radius() < k0Cuts.minRadius || v.v0radius() >= k0Cuts.maxRadius) { + return false; + } + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DecayGeometry)); + } + if (k0Cuts.useArmenterosCut && v.qtarm() <= k0Cuts.armenterosSlope * std::abs(v.alpha())) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Armenteros)); + } + if (k0Cuts.rejectLambda && (std::abs(v.mLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow || std::abs(v.mAntiLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::LambdaRejection)); + registry.fill(HIST("QA/K0s/AfterSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/AfterSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/AfterSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/AfterSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/AfterSelection/hCtau"), ctau); + } + return true; + } + + template + bool getQ(C const& c, int det, float& qx, float& qy) const + { + const int rawIndex = det * QVectorComponents + flow.correctionStep.value + (flow.harmonic.value - 2) * flow.numberOfDetectors.value * QVectorComponents; + if (rawIndex < 0 || det < 0) { + return false; + } + const auto index = static_cast(rawIndex); + const auto detectorIndex = static_cast(det); + if (index >= c.qvecRe().size() || index >= c.qvecIm().size() || detectorIndex >= c.qvecAmp().size() || c.qvecAmp()[detectorIndex] < flow.minQAmplitude.value) { + return false; + } + qx = c.qvecRe()[index]; + qy = c.qvecIm()[index]; + return std::isfinite(qx) && std::isfinite(qy); + } + + std::array decayAngles(ROOT::Math::PxPyPzMVector const& d, ROOT::Math::PxPyPzMVector const& mother) const + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto rest = boost(d); + ROOT::Math::XYZVector z(mother.Px(), mother.Py(), mother.Pz()); + if (z.R() == 0.) { + z.SetZ(1.); + } + z = z.Unit(); + ROOT::Math::XYZVector beam(0., 0., 1.); + ROOT::Math::XYZVector y = beam.Cross(z); + if (y.R() < VectorTolerance) { + y.SetY(1.); + } + y = y.Unit(); + ROOT::Math::XYZVector x = y.Cross(z).Unit(); + ROOT::Math::XYZVector k(rest.Px(), rest.Py(), rest.Pz()); + k = k.Unit(); + return {std::abs(k.Dot(z)), std::atan2(k.Dot(y), k.Dot(x))}; + } + + template + void fillResolution(C const& c, float cent, float cX, float cY, float aX, float aY, float tX, float tY) + { + auto dot = [](float x1, float y1, float x2, float y2) { return x1 * x2 + y1 * y2; }; + double psiC = std::atan2(cY, cX) / flow.harmonic, psiA = std::atan2(aY, aX) / flow.harmonic, psiT = std::atan2(tY, tX) / flow.harmonic; + registry.fill(HIST("Resolution/SP/pFT0CFT0AVsCent"), cent, dot(cX, cY, aX, aY)); + registry.fill(HIST("Resolution/SP/pFT0CTPCposVsCent"), cent, dot(cX, cY, tX, tY)); + registry.fill(HIST("Resolution/SP/pFT0ATPCposVsCent"), cent, dot(aX, aY, tX, tY)); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CFT0AVsCent"), cent, std::cos(flow.harmonic * (psiC - psiA))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CTPCposVsCent"), cent, std::cos(flow.harmonic * (psiC - psiT))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0ATPCposVsCent"), cent, std::cos(flow.harmonic * (psiA - psiT))); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0C"), cX, cY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0A"), aX, aY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyTPCpos"), tX, tY); + registry.fill(HIST("QA/Flow/EventPlane/hPsi2VsCentFT0C"), cent, psiC); + registry.fill(HIST("QA/Flow/Calibration/hCalibrationFlag"), c.isCalibrated()); + } + + struct LikeSignCandidate { + ROOT::Math::PxPyPzMVector p4; + uint64_t firstTrackId{}; + uint64_t secondTrackId{}; + }; + + template + bool buildLikeSignCandidate(C const& c, T const& t1, T const& t2, LikeSignCandidate& candidate) + { + auto track1 = getTrackParCov(t1); + auto track2 = getTrackParCov(t2); + int nCandidates = 0; + try { + nCandidates = lsFitter.process(track1, track2); + } catch (std::runtime_error const&) { + return false; + } + if (nCandidates == 0) { + return false; + } + auto const& vertex = lsFitter.getPCACandidate(); + std::array p1{}; + std::array p2{}; + lsFitter.getTrack(0).getPxPyPzGlo(p1); + lsFitter.getTrack(1).getPxPyPzGlo(p2); + ROOT::Math::PxPyPzMVector pion1(p1[0], p1[1], p1[2], o2::constants::physics::MassPionCharged); + ROOT::Math::PxPyPzMVector pion2(p2[0], p2[1], p2[2], o2::constants::physics::MassPionCharged); + auto pair = pion1 + pion2; + if (pair.Pt() < k0Cuts.k0sMinPt || pair.Pt() >= k0Cuts.k0sMaxPt || std::abs(pair.Rapidity()) >= k0Cuts.k0sMaxAbsY) { + return false; + } + const std::array pv{c.posX(), c.posY(), c.posZ()}; + const std::array sv{static_cast(vertex[0]), static_cast(vertex[1]), static_cast(vertex[2])}; + const std::array momentum{static_cast(pair.Px()), static_cast(pair.Py()), static_cast(pair.Pz())}; + const float cosPA = RecoDecay::cpa(pv, sv, momentum); + const float dcaDaughters = lsFitter.getChi2AtPCACandidate(); + if (cosPA < k0Cuts.minCosPA || dcaDaughters > k0Cuts.maxDCADaughters) { + return false; + } + if (std::abs(t1.dcaXY()) < k0Cuts.minDCAPosToPV || std::abs(t2.dcaXY()) < k0Cuts.minDCANegToPV) { + return false; + } + const float radius = std::hypot(vertex[0], vertex[1]); + if (radius < k0Cuts.minRadius || radius >= k0Cuts.maxRadius) { + return false; + } + const float decayLength = std::sqrt((vertex[0] - c.posX()) * (vertex[0] - c.posX()) + (vertex[1] - c.posY()) * (vertex[1] - c.posY()) + (vertex[2] - c.posZ()) * (vertex[2] - c.posZ())); + const float ctau = decayLength * o2::constants::physics::MassK0Short / pair.P(); + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + ROOT::Math::XYZVector total(pair.Px(), pair.Py(), pair.Pz()); + if (total.R() <= 0.) { + return false; + } + total = total.Unit(); + ROOT::Math::XYZVector daughter1(p1[0], p1[1], p1[2]); + ROOT::Math::XYZVector daughter2(p2[0], p2[1], p2[2]); + const double longitudinal1 = daughter1.Dot(total); + const double longitudinal2 = daughter2.Dot(total); + const double denominator = longitudinal1 + longitudinal2; + if (std::abs(denominator) < VectorTolerance) { + return false; + } + const double alpha = (longitudinal1 - longitudinal2) / denominator; + const double qt = (daughter1 - longitudinal1 * total).R(); + if (k0Cuts.useArmenterosCut && qt <= k0Cuts.armenterosSlope * std::abs(alpha)) { + return false; + } + candidate = {.p4 = pair, .firstTrackId = static_cast(t1.globalIndex()), .secondTrackId = static_cast(t2.globalIndex())}; + return true; + } + + void fillLikeSignPair(LikeSignCandidate const& first, LikeSignCandidate const& second, float cent, LikeSignCategory category) + { + if (first.firstTrackId == second.firstTrackId || first.firstTrackId == second.secondTrackId || first.secondTrackId == second.firstTrackId || first.secondTrackId == second.secondTrackId) { + return; + } + ROOT::Math::PxPyPzMVector d1(first.p4.Px(), first.p4.Py(), first.p4.Pz(), o2::constants::physics::MassK0Short); + ROOT::Math::PxPyPzMVector d2(second.p4.Px(), second.p4.Py(), second.p4.Pz(), o2::constants::physics::MassK0Short); + auto pair = d1 + d2; + if (std::abs(pair.Rapidity()) >= pairCuts.pairMaxAbsY || pair.Pt() < pairCuts.pairMinPt || pair.Pt() >= pairCuts.pairMaxPt || pair.M() < pairCuts.pairMassMin || pair.M() >= pairCuts.pairMassMax) { + return; + } + const auto angles = decayAngles(d1, pair); + const double cosTheta2 = angles[0] * angles[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + if (category == LikeSignCategory::PositivePositive) { + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else if (category == LikeSignCategory::NegativeNegative) { + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } + } + + template + void fillLikeSign(C const& c, DaughterTracks const& tracks) + { + auto tracksInCollision = tracks.sliceBy(perCollisionTrack, c.globalIndex()); + std::vector positiveTrackRows; + std::vector negativeTrackRows; + positiveTrackRows.reserve(tracksInCollision.size()); + negativeTrackRows.reserve(tracksInCollision.size()); + + // Apply all inexpensive daughter-quality and PID selections before forming + // pairs. Running the DCA fitter over every track pair scales as N^2 and + // dominated the processing time in high-multiplicity events. + for (auto const& track : tracksInCollision) { + if (!selectDaughter(track, false)) { + continue; + } + const auto localRow = static_cast(track.globalIndex()) - tracks.offset(); + (track.signed1Pt() > 0.f ? positiveTrackRows : negativeTrackRows).push_back(localRow); + } + + std::vector positiveCandidates; + std::vector negativeCandidates; + auto buildCandidates = [&](std::vector const& selectedRows, std::vector& candidates, bool positive) { + for (size_t i = 0; i < selectedRows.size(); ++i) { + auto track1 = tracks.iteratorAt(selectedRows[i]); + for (size_t j = i + 1; j < selectedRows.size(); ++j) { + auto track2 = tracks.iteratorAt(selectedRows[j]); + LikeSignCandidate candidate; + if (!buildLikeSignCandidate(c, track1, track2, candidate)) { + continue; + } + if (positive) { + registry.fill(HIST("K0s/LikeSign/PP/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } else { + registry.fill(HIST("K0s/LikeSign/MM/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } + if (candidate.p4.M() >= k0Cuts.k0sMassMin && candidate.p4.M() < k0Cuts.k0sMassMax) { + candidates.push_back(candidate); + } + } + } + }; + buildCandidates(positiveTrackRows, positiveCandidates, true); + buildCandidates(negativeTrackRows, negativeCandidates, false); + for (size_t i = 0; i < positiveCandidates.size(); ++i) { + for (size_t j = i + 1; j < positiveCandidates.size(); ++j) { + fillLikeSignPair(positiveCandidates[i], positiveCandidates[j], c.centFT0C(), LikeSignCategory::PositivePositive); + } + } + for (size_t i = 0; i < negativeCandidates.size(); ++i) { + for (size_t j = i + 1; j < negativeCandidates.size(); ++j) { + fillLikeSignPair(negativeCandidates[i], negativeCandidates[j], c.centFT0C(), LikeSignCategory::NegativeNegative); + } + } + for (auto const& positive : positiveCandidates) { + for (auto const& negative : negativeCandidates) { + fillLikeSignPair(positive, negative, c.centFT0C(), LikeSignCategory::PositiveNegative); + } + } + } + + template + void fillSame(C const& c, V const& v0s, DaughterTracks const& tracks) + { + float cent = c.centFT0C(), cX = 0.f, cY = 0.f, aX = 0.f, aY = 0.f, tX = 0.f, tY = 0.f; + bool qok = getQ(c, flow.ft0cId, cX, cY) && getQ(c, flow.ft0aId, aX, aY) && getQ(c, flow.tpcPosId, tX, tY); + if (qok) { + fillResolution(c, cent, cX, cY, aX, aY, tX, tY); + } + for (auto const& v : v0s) { + if (!selectK0(c, v, tracks, true)) { + continue; + } + double phi = v.phi(), psi = qok ? std::atan2(cY, cX) / flow.harmonic : 0.; + double ux = std::cos(flow.harmonic * phi), uy = std::sin(flow.harmonic * phi); + registry.fill(HIST("K0s/hCounts"), v.mK0Short(), v.pt(), cent); + if (qok) { + registry.fill(HIST("K0s/hCos2Weighted"), v.mK0Short(), v.pt(), cent, std::cos(flow.harmonic * (phi - psi))); + registry.fill(HIST("K0s/hSPWeightedFT0C"), v.mK0Short(), v.pt(), cent, ux * cX + uy * cY); + registry.fill(HIST("K0s/hSPWeightedFT0A"), v.mK0Short(), v.pt(), cent, ux * aX + uy * aY); + } + } + for (auto const& [v1, v2] : combinations(CombinationsStrictlyUpperIndexPolicy(v0s, v0s))) { + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::All)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::All), cent); + if (v1.posTrackId() == v2.posTrackId() || v1.posTrackId() == v2.negTrackId() || v1.negTrackId() == v2.posTrackId() || v1.negTrackId() == v2.negTrackId()) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::NoSharedDaughters)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::NoSharedDaughters), cent); + if (!selectK0(c, v1, tracks, false) || !selectK0(c, v2, tracks, false)) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SelectedK0s)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SelectedK0s), cent); + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + double dr = std::hypot(v1.eta() - v2.eta(), TVector2::Phi_mpi_pi(v1.phi() - v2.phi())); + if (pairCuts.enableClosePairRejection && dr < pairCuts.minDeltaR) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::ClosePair)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::ClosePair), cent); + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::Kinematics)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::Kinematics), cent); + const bool signalPair = v1.mK0Short() >= k0Cuts.k0sMassMin && v1.mK0Short() < k0Cuts.k0sMassMax && v2.mK0Short() >= k0Cuts.k0sMassMin && v2.mK0Short() < k0Cuts.k0sMassMax; + const auto side1 = sidebandRegion(v1.mK0Short()); + const auto side2 = sidebandRegion(v2.mK0Short()); + if (side1 != SidebandRegion::Outside && side2 != SidebandRegion::Outside) { + fillSidebandPair(d1, m, cent, side1, side2); + } + if (!signalPair) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SignalMass)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SignalMass), cent); + registry.fill(HIST("QA/K0sK0s/SameEvent/hMass"), m.M()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hDeltaR"), dr); + registry.fill(HIST("QA/K0sK0s/SameEvent/hPt1VsPt2"), v1.pt(), v2.pt()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hK0Mass1VsMass2"), v1.mK0Short(), v2.mK0Short()); + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/SameEvent/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/SameEvent/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/SameEvent/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (qok) { + double ux = std::cos(flow.harmonic * m.Phi()), uy = std::sin(flow.harmonic * m.Phi()), psi = std::atan2(cY, cX) / flow.harmonic; + registry.fill(HIST("K0sK0s/SameEvent/hCos2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], std::cos(flow.harmonic * (m.Phi() - psi))); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0C"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * cX + uy * cY); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0A"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * aX + uy * aY); + } + } + if (likeSign.enableLikeSign) { + fillLikeSign(c, tracks); + } + } + + void processSame(Collisions::iterator const& collision, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + if (!selectEvent(collision, true)) { + return; + } + fillSame(collision, v0s, tracks); + } + PROCESS_SWITCH(K0sK0sFlow, processSame, "same-event K0S-K0S and flow", true); + + void processMixed(Collisions const& collisions, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + MixingBinning binning{{axisMixZvtx, axisMixCent}, true}; + for (auto const& [c1, c2] : selfCombinations(binning, pairCuts.mixingDepth, -1, collisions, collisions)) { + if (c1.index() == c2.index()) { + continue; + } + if (!selectEvent(c1, false) || !selectEvent(c2, false)) { + continue; + } + auto s1 = v0s.sliceBy(perCollisionV0, c1.globalIndex()); + auto s2 = v0s.sliceBy(perCollisionV0, c2.globalIndex()); + if (s1.size() == 0 || s2.size() == 0) { + continue; + } + for (auto const& [v1, v2] : combinations(CombinationsFullIndexPolicy(s1, s2))) { + if (!selectK0(c1, v1, tracks, false) || !selectK0(c2, v2, tracks, false) || v1.mK0Short() < k0Cuts.k0sMassMin || v1.mK0Short() >= k0Cuts.k0sMassMax || v2.mK0Short() < k0Cuts.k0sMassMin || v2.mK0Short() >= k0Cuts.k0sMassMax) { + continue; + } + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/MixedEvent/hCounts"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/MixedEvent/hP2Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/MixedEvent/hP4Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p4Weight); + registry.fill(HIST("QA/K0sK0s/MixedEvent/hMass"), m.M()); + } + } + } + PROCESS_SWITCH(K0sK0sFlow, processMixed, "mixed-event K0S-K0S (optional cross-check)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From fd5c236edbd953ae4fbac4db627ad9cc64e331dc Mon Sep 17 00:00:00 2001 From: Paola Vargas Torres <88360333+PaolaVT@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:53:49 -0600 Subject: [PATCH 367/648] [PWGLF] Add histograms with both charges combined (#18359) Co-authored-by: PaolaVT --- PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx | 157 ++++++++++++++++++------- 1 file changed, 113 insertions(+), 44 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx index b4f80254804..b6a3e3afd0c 100644 --- a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx @@ -70,19 +70,25 @@ struct DedxPidAnalysis { // dE/dx for all charged particles HistogramRegistry registryDeDx{"registryDeDx", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryDeDxBothCharges{"registryDeDxBothCharges", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Constant values static constexpr int EtaIntervals = 8; static constexpr int ParticlesType = 4; static constexpr int CentralityClasses = 10; std::array, CentralityClasses> hDedxVsMomentumVsCentPos{}; std::array, CentralityClasses> hDedxVsMomentumVsCentNeg{}; + std::array, CentralityClasses> hDedxVsMomentumVsCent{}; std::array, ParticlesType> hDedxvsMomentumPos{}; std::array, ParticlesType> hDedxvsMomentumNeg{}; - std::array, CentralityClasses + 1> hDedxVspTMomentumVsCent{}; + std::array, ParticlesType> hDedxvsMomentum{}; + std::array, CentralityClasses + 1> hDedxVspTVsCent{}; + std::array, CentralityClasses + 1> hDedxVspTVsCentCh{}; std::array, CentralityClasses + 1> hMomentumVsEtaPos{}; std::array, CentralityClasses + 1> hMomentumVsEtaNeg{}; + std::array, CentralityClasses + 1> hMomentumVsEta{}; std::array, CentralityClasses + 1> hpTVsEtaPos{}; std::array, CentralityClasses + 1> hpTVsEtaNeg{}; + std::array, CentralityClasses + 1> hpTVsEta{}; std::array, EtaIntervals> hNclFoundTPCPosBefore; std::array, EtaIntervals> hNclFoundTPCNegBefore; std::array, EtaIntervals> hNclFoundTPCPosAfter; @@ -277,6 +283,7 @@ struct DedxPidAnalysis { static constexpr std::array CentClasses = {0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}; Configurable> calibrationFactorNeg{"calibrationFactorNeg", {50.4011, 50.4764, 50.186, 49.2955, 48.8222, 49.4273, 49.9292, 50.0556}, "negative calibration factors"}; Configurable> calibrationFactorPos{"calibrationFactorPos", {50.5157, 50.6359, 50.3198, 49.3345, 48.9197, 49.4931, 50.0188, 50.1406}, "positive calibration factors"}; + Configurable> calibrationFactor{"calibrationFactor", {50.5157, 50.6359, 50.3198, 49.3345, 48.9197, 49.4931, 50.0188, 50.1406}, "calibration factors for both charges"}; ConfigurableAxis binP{"binP", {VARIABLE_WIDTH, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0}, ""}; ConfigurableAxis centBins{"centBins", {100, 0, 100}, "Binning for centralidad plots"}; ConfigurableAxis dedxBins{"dedxBins", {100, 0, 100}, "Binning for dedx plots"}; @@ -489,6 +496,9 @@ struct DedxPidAnalysis { registryDeDx.add( "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, {{etaAxis}, {dedxAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); // MIP for electrons registryDeDx.add( "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, @@ -496,6 +506,9 @@ struct DedxPidAnalysis { registryDeDx.add( "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_vs_p_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); // Pions from TOF registryDeDx.add( "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, @@ -503,6 +516,9 @@ struct DedxPidAnalysis { registryDeDx.add( "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_vs_p_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); } else { // MIP for pions @@ -514,6 +530,10 @@ struct DedxPidAnalysis { "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, {{etaAxis}, {dedxAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + // MIP for electrons registryDeDx.add( "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, @@ -523,6 +543,10 @@ struct DedxPidAnalysis { "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_vs_p_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + // Pions from TOF registryDeDx.add( "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, @@ -532,6 +556,10 @@ struct DedxPidAnalysis { "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); @@ -540,6 +568,10 @@ struct DedxPidAnalysis { "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDxBothCharges.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + // pt vs p registryDeDx.add( "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, @@ -548,6 +580,10 @@ struct DedxPidAnalysis { "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, {{etaAxis}, {ptAxis}, {pAxisTrack}}); + registryDeDxBothCharges.add( + "heta_vs_pt_vs_p_all", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + // De/Dx for ch and v0 particles for (int i = 0; i < ParticlesType; ++i) { const auto& part = v0Names[i]; @@ -555,21 +591,27 @@ struct DedxPidAnalysis { "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxvsMomentum[i] = registryDeDxBothCharges.add(Form("dEdx_vs_Momentum_%s", part.c_str()), + "dE/dx vs Momentum", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); } for (int i = 0; i < CentralityClasses; ++i) { const auto& cent = centNames[i]; hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxVsMomentumVsCent[i] = registryDeDxBothCharges.add(Form("dEdx_vs_Momentum_%s", cent.c_str()), "dE/dx vs Momentum", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); } for (int i = 0; i < CentralityClasses + 1; ++i) { const auto& cent = centNames[i]; - hDedxVspTMomentumVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hDedxVspTVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hDedxVspTVsCentCh[i] = registryDeDxBothCharges.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hMomentumVsEta[i] = registryDeDxBothCharges.add(Form("p_vs_eta_%s", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + hpTVsEta[i] = registryDeDxBothCharges.add(Form("pT_vs_eta_%s", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); } // Invariant Mass @@ -773,6 +815,10 @@ struct DedxPidAnalysis { "hbeta_vs_p_Pos", "beta", HistType::kTH2F, {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + registryDeDxBothCharges.add( + "hbeta_vs_p", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + // Event Counter registryDeDx.add("histRecVtxZData", "collision z position", HistType::kTH1F, {{100, -20.0, +20.0, "z_{vtx} (cm)"}}); @@ -1234,8 +1280,6 @@ struct DedxPidAnalysis { return false; } - const float gammaMass = 2 * MassElectron; // GeV/c^2 - if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionGamma); @@ -1250,7 +1294,7 @@ struct DedxPidAnalysis { if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::V0RapidityGamma); - if (std::abs(v0.mGamma() - gammaMass) > invMassCutGamma) // Invarian mass + if (std::abs(v0.mGamma() - MassGamma) > invMassCutGamma) // Invarian mass return false; if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::MassCutGamma); @@ -1588,13 +1632,14 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hTPCClustersAfter"), trk.tpcNClsFound()); registryDeDx.fill(HIST("hTPCPIDAfter"), trk.tpcNClsPID()); - float signedP = trk.sign() * getMomentum(trk); - float signedpT = trk.sign() * trk.pt(); + float p = getMomentum(trk); + float pT = trk.pt(); // MIP calibration for pions if (getMomentum(trk) >= pionMin && getMomentum(trk) <= pionMax) { if (calibrationMode) { - if (signedP < 0) { + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_Pi"), trk.eta(), trk.tpcSignal()); + if (trk.sign() < 0) { registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); } else { registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); @@ -1603,7 +1648,8 @@ struct DedxPidAnalysis { } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i)); + if (trk.sign() < 0) { registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); } else { registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); @@ -1613,26 +1659,29 @@ struct DedxPidAnalysis { } } // Beta from TOF - if (signedP < 0) { - registryDeDx.fill(HIST("hbeta_vs_p_Neg"), std::abs(signedP), trk.beta()); + registryDeDxBothCharges.fill(HIST("hbeta_vs_p"), p, trk.beta()); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hbeta_vs_p_Neg"), p, trk.beta()); } else { - registryDeDx.fill(HIST("hbeta_vs_p_Pos"), signedP, trk.beta()); + registryDeDx.fill(HIST("hbeta_vs_p_Pos"), p, trk.beta()); } // Electrons from TOF if (std::abs(trk.beta() - 1) < elTofCut) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_El"), trk.eta(), trk.tpcSignal(), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), p); } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); } } } @@ -1641,18 +1690,20 @@ struct DedxPidAnalysis { // pions from TOF if (trk.beta() > pionTofCut && trk.beta() < pionTofCut + 0.05) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_TOF"), trk.eta(), trk.tpcSignal(), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), p); } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); } } } @@ -1666,10 +1717,11 @@ struct DedxPidAnalysis { if (std::abs(sigmaPi) < nsigmaMaxTOF) { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); } } } @@ -1692,26 +1744,35 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP > 0) { - hDedxvsMomentumPos[0]->Fill(signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); + registryDeDxBothCharges.fill(HIST("heta_vs_pt_vs_p_all"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); + hMomentumVsEta[centIndex]->Fill(trk.eta(), p); + hMomentumVsEta[10]->Fill(trk.eta(), p); + hpTVsEta[centIndex]->Fill(trk.eta(), pT); + hpTVsEta[10]->Fill(trk.eta(), pT); + if (trk.sign() > 0) { + hDedxvsMomentumPos[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentPos[centIndex]->Fill(signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTMomentumVsCent[centIndex]->Fill(signedpT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTMomentumVsCent[10]->Fill(signedpT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), signedP); - hMomentumVsEtaPos[10]->Fill(trk.eta(), signedP); - hpTVsEtaPos[centIndex]->Fill(trk.eta(), signedpT); - hpTVsEtaPos[10]->Fill(trk.eta(), signedpT); + hDedxVsMomentumVsCentPos[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaPos[10]->Fill(trk.eta(), p); + hpTVsEtaPos[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaPos[10]->Fill(trk.eta(), pT); } else { - hDedxvsMomentumNeg[0]->Fill(std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxvsMomentumNeg[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentNeg[centIndex]->Fill(std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTMomentumVsCent[centIndex]->Fill(std::abs(signedpT), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTMomentumVsCent[10]->Fill(std::abs(signedpT), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), std::abs(signedP)); - hMomentumVsEtaNeg[10]->Fill(trk.eta(), std::abs(signedP)); - hpTVsEtaNeg[centIndex]->Fill(trk.eta(), std::abs(signedpT)); - hpTVsEtaNeg[10]->Fill(trk.eta(), std::abs(signedpT)); + hDedxVsMomentumVsCentNeg[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaNeg[10]->Fill(trk.eta(), p); + hpTVsEtaNeg[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaNeg[10]->Fill(trk.eta(), pT); } } } @@ -1819,9 +1880,11 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); } } } @@ -1837,9 +1900,11 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + hDedxvsMomentum[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); } } } @@ -1855,9 +1920,11 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + hDedxvsMomentum[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); } } } @@ -1873,9 +1940,11 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + hDedxvsMomentum[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + hDedxvsMomentum[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); } } } From 62661e693e9156f894bec244db660f459d0ba12d Mon Sep 17 00:00:00 2001 From: baasingh <142407539+baasingh@users.noreply.github.com> Date: Wed, 21 Oct 2026 12:57:08 +0530 Subject: [PATCH 368/648] [PWGCF] FactorialMoments: replace subsampling error calculation with merge-safe slot pooling (#18327) --- .../Tasks/FactorialMomentsTask.cxx | 718 ++++++++---------- 1 file changed, 332 insertions(+), 386 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx index 26331d34127..e34f44ecf46 100644 --- a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// -/// \brief This task is for Normalized Factorial Moments Analysis: PRC 85, 044914 (2012), nucl-ex:1411.6083 +/// \file FactorialMomentsTask.cxx +/// \brief This task is for Normalized Factorial Moments Analysis: Hwa and Yang, Phys. Rev. C 85, 044914 (2012) /// \author Salman Malik /// \author Balwan Singh @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,94 +37,64 @@ #include #include #include -#include #include #include #include -#include #include #include -#include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; + struct FactorialMomentsTask { Configurable useITS{"useITS", false, "Select tracks with ITS"}; Configurable useTPC{"useTPC", false, "Select tracks with TPC"}; Configurable useGlobal{"useGlobal", true, "Select global tracks"}; Configurable applyCheckPtForRec{"applyCheckPtForRec", false, "Apply checkpT for reconstructed tracks"}; Configurable applyCheckPtForMC{"applyCheckPtForMC", true, "Apply checkpT for MC-generated tracks"}; - Configurable cfgEvSelkNoITSROFrameBorder{"cfgEvSelkNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; - Configurable cfgEvSelkNoTimeFrameBorder{"cfgEvSelkNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; + Configurable smearPhi{"smearPhi", false, "Randomise track phi with a Gaussian of sigma=2pi before filling the eta-phi lattices"}; + Configurable evSelNoITSROFrameBorder{"evSelNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; + Configurable evSelNoTimeFrameBorder{"evSelNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; Configurable centralEta{"centralEta", 0.9, "eta limit for tracks"}; Configurable numPt{"numPt", 5, "number of pT bins"}; Configurable ptMin{"ptMin", 0.2f, "lower pT cut"}; Configurable dcaXY{"dcaXY", 0.1f, "DCA xy cut"}; Configurable dcaZ{"dcaZ", 1.0f, "DCA z cut"}; - Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; - Configurable cfgCutItsChi2NCl{"cfgCutItsChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; - Configurable mintPCCls{"mintPCCls", 70.0f, "minimum number of TPC clusters"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; + Configurable minTPCNCls{"minTPCNCls", 70.0f, "minimum number of TPC clusters"}; Configurable> centLimits{"centLimits", {0, 5}, "centrality min and max"}; Configurable> vertexXYZ{"vertexXYZ", {0.3f, 0.4f, 10.0f}, "vertex cuts"}; Configurable> ptCuts{"ptCuts", {0.2f, 2.0f}, "pT cuts"}; - Configurable isApplySameBunchPileup{"isApplySameBunchPileup", true, "Enable SameBunchPileup cut"}; - Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; - Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time interval with dead ITS zone"}; - Configurable isApplyVertexITSTPC{"isApplyVertexITSTPC", true, "Enable VertexITSTPC cut"}; - Configurable isApplyVertexTOFmatched{"isApplyVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; - Configurable isApplyVertexTRDmatched{"isApplyVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; - Configurable isApplyExtraCorrCut{"isApplyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; - Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable evSelNoSameBunchPileup{"evSelNoSameBunchPileup", true, "Enable SameBunchPileup cut"}; + Configurable evSelGoodZvtxFT0vsPV{"evSelGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; + Configurable evSelGoodITSLayersAll{"evSelGoodITSLayersAll", true, "Remove time interval with dead ITS zone"}; + Configurable evSelVertexITSTPC{"evSelVertexITSTPC", true, "Enable VertexITSTPC cut"}; + Configurable evSelVertexTOFmatched{"evSelVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; + Configurable evSelVertexTRDmatched{"evSelVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; + Configurable applyExtraCorrCut{"applyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; + Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Enable extra phi cut"}; Configurable includeGlobalTracks{"includeGlobalTracks", false, "Enable Global Tracks"}; Configurable includeTPCTracks{"includeTPCTracks", false, "TPC Tracks"}; Configurable includeITSTracks{"includeITSTracks", false, "ITS Tracks"}; - Configurable samplesize{"samplesize", 100, "Sample size"}; + Configurable nSlots{"nSlots", 1250, "Number of hash slots for merge-safe subsampling (slot = splitmix64(globalBC) % nSlots); must be identical for every job"}; Configurable useMC{"useMC", false, "Use MC information"}; Configurable useGlobalTrack{"useGlobalTrack", true, "Require global track in filter"}; - Configurable cfgITScluster{"cfgITScluster", 6, "Minimum Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 80, "Minimum Number of TPC cluster"}; - Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; - Configurable cfgTPCnCrossedRowsOverFindableCls{"cfgTPCnCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; + Configurable minITSClusters{"minITSClusters", 6, "Minimum Number of ITS cluster"}; + Configurable minTPCClusters{"minTPCClusters", 80, "Minimum Number of TPC cluster"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + Configurable minTPCCrossedRowsOverFindableCls{"minTPCCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable reduceOutput{"reduceOutput", 0, "Suppress info level output (0 = all output, 1 = per collision, 2 = none)"}; Filter filterTracks = (nabs(aod::track::eta) < centralEta) && (aod::track::pt >= ptMin); Filter filterCollisions = (nabs(aod::collision::posZ) < vertexXYZ.value[2]) && (nabs(aod::collision::posX) < vertexXYZ.value[0]) && (nabs(aod::collision::posY) < vertexXYZ.value[1]); Service pdg{}; // Histograms - HistogramRegistry histos1{ - "histos1", - { - {"h3DVtxZetaPhi", "VtxZ vs #eta vs #phi;VtxZ;#eta;#phi", {HistType::kTH3F, {{20, -10, 10}, {16, -0.8, 0.8}, {100, 0., o2::constants::math::TwoPI}}}}, - {"hRecoPtBefore", "Reco pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtBefore", "Gen pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoPtAfter", "Reco pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtAfter", "Gen pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoEtaBefore", "Reco #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"mCentMCImpactFull", "Impact Parameter (MC All Events);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 50.0}}}}, - {"mCentMCImpact005", "Impact Parameter (MC 0–5%);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 20.0}}}}, - {"hGenEtaBefore", "Gen #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoEtaAfter", "Reco #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hGenEtaAfter", "Gen #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoPhiBefore", "Reco #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenPhiBefore", "Gen #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hRecoPhiAfter", "Reco #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenCharge", "Gen particle charge;Charge;Counts", {HistType::kTH1F, {{13, -6.5, 6.5}}}}, - {"hGenPhiAfter", "Gen #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"mChargeDist", "MC particle charge;Charge;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, - {"mDiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mPrimariesPerEvent", "Primary MC particles per event;N_{primaries};Counts", {HistType::kTH1I, {{20000, 0, 20000}}}}, - {"mDiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mDiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - }, - }; - HistogramRegistry histos{ "histos", { @@ -132,7 +103,7 @@ struct FactorialMomentsTask { {"mChargeBefore", "Charge before MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mChargeAfter", "Charge after MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mCollID", "collisionID", {HistType::kTH1I, {{1000, -10000, 10000}}}}, - {"mCentFT0M", "centFT0C_0A", {HistType::kTH1F, {{100, 0, 100}}}}, + {"mCentFT0M", "centFT0M / Run2 V0M", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFV0A", "centFV0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0A", "centFT0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0C", "centFT0C", {HistType::kTH1F, {{100, 0, 100}}}}, @@ -142,7 +113,6 @@ struct FactorialMomentsTask { {"mEta", "#eta", {HistType::kTH1F, {{1000, -2, 2}}}}, {"mPt", "#pt", {HistType::kTH1F, {{1000, -0.01, 50}}}}, {"mPhi", "#phi", {HistType::kTH1F, {{100, 0, o2::constants::math::TwoPI}}}}, - {"mEvents", "events", {HistType::kTH1D, {{5, -0.5, 4.5}}}}, {"mNFindableClsTPC", "findable TPC clusters;findable clusters", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsTPC", "number of clusters TPC; nClusters TPC", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsITS", "number of clusters ITS; nClusters ITS", {HistType::kTH1F, {{100, 0, 10}}}}, @@ -150,10 +120,8 @@ struct FactorialMomentsTask { {"mChi2ITS", "chi2 ITS", {HistType::kTH1F, {{100, 0, 10}}}}, {"mChi2TRD", "chi2 TRD", {HistType::kTH1F, {{100, 0, 100}}}}, {"mDCAxy", "DCA xy", {HistType::kTH1F, {{500, -0.8, 0.8}}}}, - {"mDCAx", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, + {"mDCAz", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, {"mDCAxyPt", "DCA xy vs #pt;#pt;DCAxy", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAxyPtbcut", "DCA xy vs #pt;#pt;DCAxycut", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAzPtbcut", "DCA z vs #pt;#pt;DCAzcut", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mDCAzPt", "DCA z vs #pt;#pt;DCAz", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mNSharedClsTPC", "shared clusters in TPC", {HistType::kTH1F, {{100, 0, 10}}}}, {"mCrossedRowsTPC", "crossedrows in TPC", {HistType::kTH1F, {{100, 0, 200}}}}, @@ -165,88 +133,110 @@ struct FactorialMomentsTask { OutputObjHandlingPolicy::AnalysisObject, true}; float collisionZ = 0.f; - double dcaxyMaxTrackPar0 = 0.0105; - double dcaxyMaxTrackPar1 = 0.035; - double dcaxyMaxTrackPar2 = 1.1; + static constexpr double kDcaXY0 = 0.0105; + static constexpr double kDcaXY1 = 0.035; + static constexpr double kDcaXY2 = 1.1; static const int nBins = 52; - double kMinCharge = 1e-6; + static constexpr double kMinCharge = 1e-6; static const int nfqOrder = 6; - int countSamples = 0; - int testc1 = 0, testc2 = 0, testc3 = 0; + static constexpr int kEvAll = 1; + static constexpr int kEvSel8 = 2; + static constexpr int kEvITSROFrame = 3; + static constexpr int kEvTimeFrame = 4; + static constexpr int kEvSameBunch = 5; + static constexpr int kEvGoodITS = 6; + static constexpr int kEvGoodZvtx = 7; + static constexpr int kEvVertexITSTPC = 8; + static constexpr int kEvCent = 9; + static constexpr int kEvAccepted = 10; + static constexpr int kNEventSteps = 10; std::array binningM{}; - std::array countTracks{0, 0, 0, 0, 0}; - std::array, 5>, 6> fqEvent{}; - std::array, 5>, 6> fqEventSampled{}; + std::array countTracks{}; + std::array, 5>, nfqOrder> fqEvent{}; std::array, 5> binConEvent{}; - std::array, 5>, 6> binConEventSampled{}; - std::array, 5>, nfqOrder> nSubsamples{}; - std::array, 5>, nfqOrder> tmpFqErr{}; std::vector> mHistArrReset; - std::vector> mHistArrQA; + std::vector> mHistEtaQA; + std::vector> mHistPtQA; + std::vector> mHistPhiQA; + std::vector> mHistMultQA; std::vector> mHistArrEff; std::vector> mFqBinFinal; std::vector> mBinConFinal; - std::vector> mFqBinFinalSampled; - std::vector> mBinConFinalSampled; - std::vector> mFqError; + // merge-safe slot output: additive per-slot sums, hadd-exact + std::shared_ptr mSlotNev; + std::vector> mSlotFq; + std::vector> mSlotOcc; + uint64_t mEventGlobalBC = 0; void init(o2::framework::InitContext&) { - // NOTE: check to make number of pt and the vector consistent - if (numPt != static_cast(ptCuts.value.size()) / 2) { - for (int i = numPt; i < static_cast(ptCuts.value.size() / 2); i++) { - ptCuts.value[2 * i] = 0; - ptCuts.value[2 * i + 1] = 0; + if (numPt.value < 1 || numPt.value > 5) { + LOG(fatal) << "numPt must be between 1 and 5, got " << numPt.value; + } + if (static_cast(ptCuts.value.size()) < 2 * numPt.value) { + LOG(fatal) << "ptCuts needs " << 2 * numPt.value << " values for numPt=" << numPt.value + << ", got " << ptCuts.value.size(); + } + if (nSlots.value < 1 || nSlots.value > 1000000) { + LOG(fatal) << "nSlots must be in [1, 1000000], got " << nSlots.value; + } + if (centLimits.value.size() != 2 || vertexXYZ.value.size() != 3) { + LOG(fatal) << "centLimits needs 2 and vertexXYZ needs 3 values"; + } + if (applyCheckPtForRec.value == applyCheckPtForMC.value) { + LOG(fatal) << "exactly one of applyCheckPtForRec / applyCheckPtForMC must be enabled"; + } + TString cfg = Form("FMtask centLimits=%d,%d numPt=%d centralEta=%g ptMin=%g dcaXY=%g dcaZ=%g useMC=%d nfqOrder=%d smearPhi=%d nSlots=%d", + centLimits.value[0], centLimits.value[1], numPt.value, centralEta.value, + ptMin.value, dcaXY.value, dcaZ.value, useMC.value ? 1 : 0, nfqOrder, smearPhi.value ? 1 : 0, nSlots.value); + cfg += Form(" vertexXYZ=%g,%g,%g", vertexXYZ.value[0], vertexXYZ.value[1], vertexXYZ.value[2]); + cfg += " ptCuts="; + for (int i = 0; i < static_cast(ptCuts.value.size()); i++) { + cfg += Form("%s%g", i > 0 ? "," : "", ptCuts.value[i]); + } + histos.add("metaConfig", cfg.Data(), HistType::kTH1D, {{1, 0.5, 1.5}}); + auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{kNEventSteps, 0.5, kNEventSteps + 0.5}})); + const std::array evLabels = {"all", "sel8", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "kNoSameBunchPileup", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "centrality", "accepted"}; + for (int iStep = 0; iStep < kNEventSteps; ++iStep) { + mEventSelected->GetXaxis()->SetBinLabel(iStep + 1, evLabels[iStep]); + } + // merge-safe slot output: additive per-slot sums, hadd-exact (see slotpool.C) + const AxisSpec slotAxis{nSlots.value, -0.5, nSlots.value - 0.5, ""}; + const AxisSpec slotMAxis{nBins, -0.5, nBins - 0.5, ""}; + mSlotNev = std::get>(histos.add("slots/nEv", "events per slot;slot;events", HistType::kTH1F, {slotAxis})); + // bin1 = merged jobs, bin2 = sum of their nSlots: mismatch witness (hadd keeps the first axis silently) + auto slotParams = std::get>(histos.add("slots/params", "slot campaign parameters;jobs;entries", HistType::kTH1D, {{2, 0.5, 2.5}})); + slotParams->SetBinContent(1, 1.0); + slotParams->SetBinContent(2, static_cast(nSlots.value)); + for (int iPt = 0; iPt < numPt; ++iPt) { + mSlotOcc.push_back(std::get>(histos.add(Form("slots/sumOcc_pt%d", iPt + 1), + Form("sum of occupancy per slot for bin %.2f-%.2f;slot;M", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); + for (int iq = 0; iq < nfqOrder; ++iq) { + mSlotFq.push_back(std::get>(histos.add(Form("slots/sumFq_pt%d_o%d", iPt + 1, iq + 2), + Form("sum of f_%d per slot for bin %.2f-%.2f;slot;M", iq + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); } } - std::array axisPt = {AxisSpec{100, -0.01, 3 * ptCuts.value[1], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[3], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[5], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[7], ""}, AxisSpec{100, -0.01, 3 * ptCuts.value[9], ""}}; // pT axis - auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{7, 0.5, 8.5}})); - mEventSelected->GetXaxis()->SetBinLabel(0, "all"); - mEventSelected->GetXaxis()->SetBinLabel(1, "sel8"); - mEventSelected->GetXaxis()->SetBinLabel(2, "kNoITSROFrameBorder"); - mEventSelected->GetXaxis()->SetBinLabel(3, "kNoTimeFrameBorder"); - mEventSelected->GetXaxis()->SetBinLabel(4, "sameBunchPileup"); - mEventSelected->GetXaxis()->SetBinLabel(5, "kIsGoodITSLayersAll"); - mEventSelected->GetXaxis()->SetBinLabel(6, "kIsGoodZvtxFT0vsPV"); - mEventSelected->GetXaxis()->SetBinLabel(7, "FTOC"); - auto mTrackSelected = std::get>(histos.add( - "mTrackSelected", "Track Selection Steps", HistType::kTH1D, {{5, 0.5, 5.5}})); - mTrackSelected->GetXaxis()->SetBinLabel(1, "all"); - mTrackSelected->GetXaxis()->SetBinLabel(2, "charge"); - mTrackSelected->GetXaxis()->SetBinLabel(3, "PIDs"); - mTrackSelected->GetXaxis()->SetBinLabel(4, "Pids exclude pions"); - mTrackSelected->GetXaxis()->SetBinLabel(5, "Pids exclude pions + kions"); - if (useMC) { - auto mMcTrackSelected = std::get>(histos.add("mMcTrackSelected", "mcTrackSelection", HistType::kTH1D, {{5, 0.5, 5.5}})); - } for (int iM = 0; iM < nBins; ++iM) { binningM[iM] = 2 * (iM + 2); } for (int iPt = 0; iPt < numPt; ++iPt) { + const AxisSpec ptAxis{100, -0.01, 3 * ptCuts.value[2 * iPt + 1], ""}; mHistArrEff.push_back(std::get>(histos.add(Form("bin%i/m3DVtxZetaPhi", iPt + 1), Form("#eta #phi #vtxz for bin %.2f-%.2f;vz;#eta;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH3F, {{20, -10, 10}, {16, -0.8, +0.8}, {100, 0., o2::constants::math::TwoPI}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {axisPt[iPt]}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); + mHistEtaQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); + mHistPtQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {ptAxis}))); + mHistPhiQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); + mHistMultQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); for (int iM = 0; iM < nBins; ++iM) { auto mHistsR = std::get>(histos.add(Form("bin%i/Reset/mEtaPhi%i", iPt + 1, iM), Form("#eta#phi_%i for bin %.2f-%.2f;#eta;#phi", iM, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH2F, {{binningM[iM], -0.8, 0.8}, {binningM[iM], 0, o2::constants::math::TwoPI}})); mHistArrReset.push_back(mHistsR); - for (int iq = 0; iq < nfqOrder; ++iq) { - tmpFqErr[iq][iPt][iM] = new TH1D(Form("tmpFqErr%i%i%i", iq, iPt, iM), Form("tmpFqErr%i%i%i", iq, iPt, iM), 100, 0, 10); - } } for (int i = 0; i < nfqOrder; ++i) { auto mHistFq = std::get>(histos.add(Form("mFinalFq%i_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mFqBinFinal.push_back(mHistFq); auto mHistAv = std::get>(histos.add(Form("mFinalAvBin%i_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mBinConFinal.push_back(mHistAv); - auto mHistFqSampled = std::get>(histos.add(Form("mFinalFq%iSampled_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqBinFinalSampled.push_back(mHistFqSampled); - auto mHistAvSampled = std::get>(histos.add(Form("mFinalAvBin%iSampled_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mBinConFinalSampled.push_back(mHistAvSampled); - - auto mHistError = std::get>(histos.add(Form("mFqError%i_bin%i", i + 2, iPt + 1), Form("Error for F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqError.push_back(mHistError); } } } @@ -256,12 +246,14 @@ struct FactorialMomentsTask { for (auto iPt = 0; iPt < numPt; ++iPt) { if (track.pt() > ptCuts.value[2 * iPt] && track.pt() < ptCuts.value[2 * iPt + 1]) { float iphi = track.phi(); - iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); - iphi = RecoDecay::constrainAngle(iphi, 0.); + if (smearPhi) { + iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); + iphi = RecoDecay::constrainAngle(iphi, 0.); + } mHistArrEff[iPt]->Fill(collisionZ, track.eta(), iphi); - mHistArrQA[iPt * 4]->Fill(track.eta()); - mHistArrQA[iPt * 4 + 1]->Fill(track.pt()); - mHistArrQA[iPt * 4 + 2]->Fill(iphi); + mHistEtaQA[iPt]->Fill(track.eta()); + mHistPtQA[iPt]->Fill(track.pt()); + mHistPhiQA[iPt]->Fill(iphi); countTracks[iPt]++; for (auto iM = 0; iM < nBins; ++iM) { mHistArrReset[iPt * nBins + iM]->Fill(track.eta(), iphi); @@ -269,106 +261,224 @@ struct FactorialMomentsTask { } } } - void calculateMoments(std::vector> hist) + // splitmix64: deterministic stateless hash of the global BC + static uint64_t slotOf(uint64_t x) + { + x += 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); + } + // additive fold of the current accepted event into its slot (never Fill with weights: no fSumw2, hadd-exact) + void accumulateSlot() + { + const int slot = static_cast(slotOf(mEventGlobalBC) % static_cast(nSlots.value)); + mSlotNev->SetBinContent(slot + 1, mSlotNev->GetBinContent(slot + 1) + 1.); + for (int iPt = 0; iPt < numPt; ++iPt) { + auto* occ = mSlotOcc[iPt].get(); + for (int iM = 0; iM < nBins; ++iM) { + occ->SetBinContent(slot + 1, iM + 1, occ->GetBinContent(slot + 1, iM + 1) + binConEvent[iPt][iM]); + for (int iq = 0; iq < nfqOrder; ++iq) { + auto* fq = mSlotFq[iPt * nfqOrder + iq].get(); + fq->SetBinContent(slot + 1, iM + 1, fq->GetBinContent(slot + 1, iM + 1) + fqEvent[iq][iPt][iM]); + } + } + } + } + void calculateMoments(const std::vector>& hist) { - double binContent = 0; - countSamples++; - bool compSample = kFALSE; - if (countSamples == samplesize) { - compSample = kTRUE; - countSamples = 0; - } - // Calculate the normalized factorial moments + // normalized factorial moments per accepted event, per pT bin and M for (int iPt = 0; iPt < numPt; ++iPt) { for (int iM = 0; iM < nBins; ++iM) { - binContent = 0; - std::array sumfqBin{0.}; + double binContent = 0; + std::array sumfqBin{0.}; + const double m2 = static_cast(binningM[iM]) * static_cast(binningM[iM]); + const auto& histM = hist[iPt * nBins + iM]; - for (int iEta = 1; iEta <= hist[iPt * nBins + iM]->GetNbinsX(); ++iEta) { - for (int iPhi = 1; iPhi <= hist[iPt * nBins + iM]->GetNbinsY(); ++iPhi) { - double binconVal = 0; - binconVal = hist[iPt * nBins + iM]->GetBinContent(iEta, iPhi); + for (int iEta = 1; iEta <= histM->GetNbinsX(); ++iEta) { + for (int iPhi = 1; iPhi <= histM->GetNbinsY(); ++iPhi) { + const double binconVal = histM->GetBinContent(iEta, iPhi); binContent += binconVal; for (int iq = 0; iq < nfqOrder; ++iq) { double fqBin = 0; if (binconVal >= iq + 2) { - fqBin = TMath::Factorial(static_cast(binconVal)) / (TMath::Factorial(static_cast(binconVal) - (iq + 2))); - } - if (std::isnan(fqBin)) { - break; + const int nOcc = static_cast(binconVal); + fqBin = 1.0; + for (int k = 0; k < iq + 2; ++k) { + fqBin *= (nOcc - k); + } } sumfqBin[iq] += fqBin; } } } - binConEvent[iPt][iM] = binContent / (std::pow(binningM[iM], 2)); + binConEvent[iPt][iM] = binContent / m2; for (int iq = 0; iq < nfqOrder; ++iq) { - // Always accumulate raw fq numerator and binCon unconditionally so - // both sums run over exactly the same set of events. - fqEvent[iq][iPt][iM] = sumfqBin[iq] / (std::pow(binningM[iM], 2)); - fqEventSampled[iq][iPt][iM] += fqEvent[iq][iPt][iM]; - binConEventSampled[iq][iPt][iM] += binConEvent[iPt][iM]; - mFqBinFinal[iPt * 6 + iq]->Fill(iM, fqEvent[iq][iPt][iM]); - mBinConFinal[iPt * 6 + iq]->Fill(iM, binConEvent[iPt][iM]); - if (compSample) { - const double avM = binConEventSampled[iq][iPt][iM] / samplesize; - mBinConFinalSampled[iPt * 6 + iq]->Fill(iM, avM); - const double den = std::pow(avM, iq + 2); - const double fqM = ((fqEventSampled[iq][iPt][iM]) / samplesize); + fqEvent[iq][iPt][iM] = sumfqBin[iq] / m2; + mFqBinFinal[iPt * nfqOrder + iq]->Fill(iM, fqEvent[iq][iPt][iM]); + mBinConFinal[iPt * nfqOrder + iq]->Fill(iM, binConEvent[iPt][iM]); + } + } + } + accumulateSlot(); + } - const double tmp = (den > 0.) ? fqM / den : 0.; - mFqBinFinalSampled[iPt * 6 + iq]->Fill(iM, tmp); + template + bool passDcaCut(const T& track) const + { + return std::fabs(track.dcaXY()) < (kDcaXY0 + kDcaXY1 / std::pow(track.pt(), kDcaXY2)); + } - tmpFqErr[iq][iPt][iM]->Fill(tmp); - ++nSubsamples[iq][iPt][iM]; - const double stdErr = (nSubsamples[iq][iPt][iM] > 1) - ? tmpFqErr[iq][iPt][iM]->GetStdDev() / std::sqrt(static_cast(nSubsamples[iq][iPt][iM])) - : 0.; - mFqError[iPt * 6 + iq]->SetBinContent(iM + 1, stdErr); - fqEventSampled[iq][iPt][iM] = 0; - binConEventSampled[iq][iPt][iM] = 0; - } - } // end of loop over order of F - } // end of loop over M bins - } // end of loop over pT bins + template + void fillTrackQA(const T& track) + { + histos.fill(HIST("mCollID"), track.collisionId()); + histos.fill(HIST("mEta"), track.eta()); + histos.fill(HIST("mPt"), track.pt()); + histos.fill(HIST("mPhi"), track.phi()); + histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); + histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); + histos.fill(HIST("mNClsITS"), track.itsNCls()); + histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); + histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); + histos.fill(HIST("mChi2TRD"), track.trdChi2()); + histos.fill(HIST("mDCAxy"), track.dcaXY()); + histos.fill(HIST("mDCAz"), track.dcaZ()); + histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); + histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); + histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); + histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); + histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); + histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); + histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); + histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); + const double crossedRows = track.tpcNClsCrossedRows(); + histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), crossedRows > 0. ? track.tpcFractionSharedCls() / crossedRows : 0.); } - // write a template that takes coll and tracks from processRun3 and processMCRec - using TracksFMs = soa::Filtered>; - Preslice perMcCollision = aod::mcparticle::mcCollisionId; - void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + template + void fillMcQA(const T& mcParts) + { + for (auto const& mc : mcParts) { + int pdgCode = mc.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + if (!pdgInfo) { + continue; + } + double charge = pdgInfo->Charge(); + double physCharge = charge / 3.0; + histos.fill(HIST("mChargeBefore"), physCharge); + if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { + histos.fill(HIST("mChargeAfter"), physCharge); + histos.fill(HIST("mEta"), mc.eta()); + histos.fill(HIST("mPt"), mc.pt()); + histos.fill(HIST("mPhi"), mc.phi()); + if (applyCheckPtForMC && !applyCheckPtForRec) { + checkpT(mc); + } + } + } + } + + template + bool selectEventRun3(const T& coll) { - // selection of events - histos.fill(HIST("mEventSelected"), 0); + histos.fill(HIST("mEventSelected"), kEvAll); if (!coll.sel8()) { - return; + return false; } - if (cfgEvSelkNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { - return; + histos.fill(HIST("mEventSelected"), kEvSel8); + if (evSelNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvITSROFrame); + if (evSelNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvTimeFrame); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvSameBunch); + if (evSelGoodITSLayersAll && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvGoodITS); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { + return false; } + collisionZ = coll.posZ(); + histos.fill(HIST("mEventSelected"), kEvCent); + histos.fill(HIST("mEventSelected"), kEvAccepted); + return true; + } - histos.fill(HIST("mEventSelected"), 2); - if (cfgEvSelkNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { - return; + template + bool selectEventMCRec(const T& coll) + { + histos.fill(HIST("mEventSelected"), kEvAll); + if (!coll.sel8()) { + return false; } - histos.fill(HIST("mEventSelected"), 3); - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return; + histos.fill(HIST("mEventSelected"), kEvSel8); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; } - histos.fill(HIST("mEventSelected"), 4); - if (cfgUseGoodITSLayerAllCut && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { - return; + histos.fill(HIST("mEventSelected"), kEvSameBunch); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; } - histos.fill(HIST("mEventSelected"), 5); - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return; + histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + if (evSelVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; } - histos.fill(HIST("mEventSelected"), 6); + histos.fill(HIST("mEventSelected"), kEvVertexITSTPC); if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { - return; + return false; } collisionZ = coll.posZ(); - histos.fill(HIST("mEventSelected"), 7); + histos.fill(HIST("mEventSelected"), kEvCent); + histos.fill(HIST("mEventSelected"), kEvAccepted); + return true; + } + + void beginEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] == 0) { + continue; + } + for (int iM = 0; iM < nBins; ++iM) { + mHistArrReset[iPt * nBins + iM]->Reset(); + } + } + countTracks.fill(0); + fqEvent = {}; + binConEvent = {}; + } + + void endEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] > 0) { + mHistMultQA[iPt]->Fill(countTracks[iPt]); + } + } + calculateMoments(mHistArrReset); + } + + using TracksFMs = soa::Filtered>; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) + { + mEventGlobalBC = coll.bc_as().globalBC(); + if (!selectEventRun3(coll)) { + return; + } histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); @@ -376,12 +486,7 @@ struct FactorialMomentsTask { histos.fill(HIST("mCentFV0A"), coll.centFV0A()); histos.fill(HIST("mCentFT0A"), coll.centFT0A()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { if (useITS && !track.hasITS()) { continue; @@ -392,79 +497,31 @@ struct FactorialMomentsTask { if (useGlobal && !track.isGlobalTrack()) { continue; } - - if (std::fabs(track.dcaXY()) < (dcaxyMaxTrackPar0 + dcaxyMaxTrackPar1 / std::pow(track.pt(), dcaxyMaxTrackPar2))) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - checkpT(track); - } - } - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); + if (!passDcaCut(track)) { + continue; } + fillTrackQA(track); + checkpT(track); } - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun3, "main process function", false); using CollisionCandidateMCRec = soa::Join; using TracksMc = soa::Filtered>; - void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&) + void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if (!coll.has_mcCollision()) { return; } - histos.fill(HIST("mEventSelected"), 0); - if (!coll.sel8()) { - return; - } - histos.fill(HIST("mEventSelected"), 1); - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return; - } - histos.fill(HIST("mEventSelected"), 2); - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (!selectEventMCRec(coll)) { return; } - histos.fill(HIST("mEventSelected"), 3); - if (isApplyVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - return; - } - histos.fill(HIST("mEventSelected"), 4); - if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { - return; - } - - histos.fill(HIST("mEventSelected"), 5); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : colltracks) { if (useITS && !track.hasITS()) { continue; @@ -475,60 +532,17 @@ struct FactorialMomentsTask { if (useGlobal && !track.isGlobalTrack()) { continue; } - if (std::fabs(track.dcaXY()) < (dcaxyMaxTrackPar0 + dcaxyMaxTrackPar1 / std::pow(track.pt(), dcaxyMaxTrackPar2))) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - if (applyCheckPtForRec && !applyCheckPtForMC) { - checkpT(track); - } - } - } - auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { + if (!passDcaCut(track)) { continue; } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } - } - } - for (auto iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); + fillTrackQA(track); + if (applyCheckPtForRec && !applyCheckPtForMC) { + checkpT(track); } } - histos.fill(HIST("mEventSelected"), 6); - calculateMoments(mHistArrReset); + auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); + fillMcQA(mcParts); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processMCRec, "main process function", false); using EventSelectionrun2 = soa::Join; @@ -543,132 +557,64 @@ struct FactorialMomentsTask { BCsWithRun2Info const&) { auto bc = coll.bc_as(); + mEventGlobalBC = bc.globalBC(); if (!(static_cast(bc.eventCuts() & BIT(aod::Run2EventCuts::kAliEventCutsAccepted)))) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { double recoCharge = (track.sign() != 0) ? track.sign() : 0.; if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= kMinCharge) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + fillTrackQA(track); if (applyCheckPtForRec && !applyCheckPtForMC) { checkpT(track); } } } auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { - continue; - } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } - } - } - - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - - calculateMoments(mHistArrReset); + fillMcQA(mcParts); + endEvent(); } - PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", true); - void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", false); + void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if ((!coll.alias_bit(kINT7)) || (!coll.sel7())) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { - if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < mintPCCls)) { + if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < minTPCNCls)) { continue; } - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + fillTrackQA(track); checkpT(track); } - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - // Calculate the normalized factorial moments - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun2, "for RUN2", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; } From 228c8e6f0ed5476cd6cbb32871f2d26bd9b65e96 Mon Sep 17 00:00:00 2001 From: lgansbartl Date: Wed, 21 Oct 2026 13:19:01 +0200 Subject: [PATCH 369/648] [PWGEM/PhotonMeson] Add Dalitz electron efficiency task (#18311) Co-authored-by: Laura Gansbartl --- PWGEM/PhotonMeson/Tasks/CMakeLists.txt | 7 + .../Tasks/dileptonEffiForDalitzSkimmer.cxx | 446 ++++++++++++++++++ 2 files changed, 453 insertions(+) create mode 100644 PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index 56c32ab6a3b..cdb29b26af8 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -210,7 +210,14 @@ o2physics_add_dpl_workflow(pair-qc-task SOURCES pairQcTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(two-photon-mc-task SOURCES twoPhotonMcTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dilepton-effi-for-dalitz-skimmer + SOURCES dileptonEffiForDalitzSkimmer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + diff --git a/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx new file mode 100644 index 00000000000..58a62d358f1 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx @@ -0,0 +1,446 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file dielptonEffi.cxx +/// \brief calculating dilepton efficiency for dalitz analyses +/// \author Laura Gans-Bartl, laura.gans-bartl@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::framework; + +using MyEMPrimaryElectrons = soa::Join; +using MyEMPrimaryElectron = MyEMPrimaryElectrons::iterator; + +using MyTracks = soa::Join; +using MyTrack = MyTracks::iterator; +using MyTracksMC = soa::Join; +using MyTrackMC = MyTracksMC::iterator; + +using MyMCV0Legs = soa::Join; +using MyMCV0Leg = MyMCV0Legs::iterator; + +using MyV0Photons = soa::Join; +using MyV0Photon = MyV0Photons::iterator; + +using MyCollisionsMC = soa::Join; +using MyCollisionMC = MyCollisionsMC::iterator; + +struct DileptonEffiForDalitzSkimmer { + SliceCache cache; + PresliceOptional perCollision = aod::emprimaryelectronda::pmeventId; + PresliceOptional perTracksCollision = aod::track::collisionId; + PresliceOptional perDileptonMcCollision = aod::mcparticle::mcCollisionId; + Preslice perCollisionV0 = aod::v0photonkf::pmeventId; + + HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + bool isAmbiguousTrack(const auto& track, const aod::AmbiguousTracks& ambiguousTracks) + { + for (const auto& ambiguousTrack : ambiguousTracks) { + if (ambiguousTrack.trackId() == track.globalIndex()) { + return true; + } + } + return false; + } + + void init(o2::framework::InitContext&) + { + fRegistry.add("hPtSglEpEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEpEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEpMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSgltpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtSglTracktpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtMcEtaGeneral", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtMcEtaDalitz", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtEPrimMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hEPrimNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtTrackMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hTrackNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtSglEpTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaRecoRecoPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmFromTrueEtaRecoGenPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaReco", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hMcPartsPerColl", " ;N_{tracks}/collision; N", kTH1F, {{400, -0.5, 400.5}}, false); + + fRegistry.add("hCheckAsso", "pT;p_{T} (GeV/c)", kTH1F, {{2, -0.5f, 1.5}}, false); + } + void processReco(aod::PMEvents const& collisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) // aod::EMEvents_005, aod::EMMCParticles_001 + { + LOG(info) << "started process"; + if (emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << "size emprimaryelectrons: " << emprimaryelectrons.size(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMC = emmcparticle.begin(); + std::unordered_map mapMultCount; + + //**Single electrons and positrons**/ + for (auto const& collision : collisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, collision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt())) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSgltpcNSigmaEl"), ePrimary.pt(), ePrimary.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSgltpcNSigmaPi"), ePrimary.pt(), ePrimary.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSgltofNSigmaEl"), ePrimary.pt(), ePrimary.tofNSigmaEl()); + + const auto mcId = ePrimary.emmcparticleId(); + + if (mcId < 0 || mcId >= emmcparticle.size()) { + continue; + } + iterMC.setCursor(mcId); + mapMultCount[static_cast(iterMC.globalIndex())]++; + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEpEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEmEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipEMPrimTrue"), iterMC.pt()); + } + if (iterMC.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimEMPrimTrue"), iterMC.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint32_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint32_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMC.setCursor(mcGlobalIndex); + // fRegistry.fill(HIST("hPtEPrimMulti"), iterMC.pt()); + // fRegistry.fill(HIST("hEPrimNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + // LOG(info) << "numOkMc: " << numOkMc; + // LOG(info) << "numNotOkMc: " << numNotOkMc; + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processReco, "process for reco table emprimary", false); + + void processTracks(aod::Collisions const& collisions, aod::McParticles const& mcParticles, MyTracksMC const& tracks) + { + if (tracks.size() <= 0 || mcParticles.size() <= 0) { + return; + } + LOG(info) << "size tracks: " << tracks.size(); + + ////**Analysing tracks **/ + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMCTrack = mcParticles.begin(); + + std::unordered_map mapMultCount; + ////******Tracks before process function******//// + for (auto const& collision : collisions) { + auto slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + for (const auto& track : slicedTracks) { + if (!track.has_mcParticle() || !std::isfinite(track.pt())) { //|| std::abs(track.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSglTracktpcNSigmaEl"), track.pt(), track.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSglTracktpcNSigmaPi"), track.pt(), track.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSglTracktofNSigmaEl"), track.pt(), track.tofNSigmaEl()); + + const auto mcId = track.mcParticleId(); + + if (mcId < 0 || mcId >= mcParticles.size()) { + continue; + } + + iterMCTrack.setCursor(mcId); + mapMultCount[static_cast(iterMCTrack.globalIndex())]++; + + if (!iterMCTrack.isPhysicalPrimary() || fabs(iterMCTrack.y()) > 0.8) { + continue; + } + + if (iterMCTrack.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpTrackEMPrimTrue"), iterMCTrack.pt()); + } + + if (iterMCTrack.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimTrackEMPrimTrue"), iterMCTrack.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint64_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint64_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMCTrack.setCursor(mcGlobalIndex); + // // LOG(info) << "Track | Global Index: " << iterMCTrack.globalIndex(); + // fRegistry.fill(HIST("hPtTrackMulti"), iterMCTrack.pt()); + // fRegistry.fill(HIST("hTrackNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processTracks, "process for reco MyTracks", true); + + using FilteredMyCollisionsMC = soa::Filtered; + void processGens(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, aod::PMEvents const& pmcollisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) + { + LOG(info) << "started process MC"; + if (mcParticles.size() <= 0 || emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << " size mcParticles: " << mcParticles.size(); + auto iterChild = mcParticles.begin(); + auto iterMom = mcParticles.begin(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgEta = 221; + const int pdgGamma = 22; + + ////******generated Dileptons******//// + std::vector vecEpMCParticles; + vecEpMCParticles.reserve(1e5); + std::vector vecEmMCParticles; + vecEpMCParticles.reserve(1e5); + + std::vector vecEmMCPartPt; + vecEmMCPartPt.reserve(1e5); + + //**Single electrons and positrons**// + for (auto const& mcCollision : mcCollisions) { + auto slicedMcParticles = mcParticles.sliceBy(perDileptonMcCollision, mcCollision.globalIndex()); + fRegistry.fill(HIST("hMcPartsPerColl"), slicedMcParticles.size()); + for (const auto& mcParticle : slicedMcParticles) { + if (!mcParticle.isPhysicalPrimary() || fabs(mcParticle.y()) > 0.8) { + continue; + } + vecEmMCPartPt.push_back(mcParticle.pt()); + + // const float dx = mcParticle.vx(); //- mcCollision.posX() + // const float dy = mcParticle.vy(); + // const float dz = mcParticle.vz(); + + // const float prodDist = sqrt(dx * dx + dy * dy); //+ dz * dz + + if (mcParticle.pdgCode() == pdgEp) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEpMCPart"), mcParticle.pt()); + vecEpMCParticles.push_back(mcParticle.globalIndex()); + } + if (mcParticle.pdgCode() == pdgEm) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEmMCPart"), mcParticle.pt()); + vecEmMCParticles.push_back(mcParticle.globalIndex()); + } + + ////******Investigating Eta Daltz Decay******//// + if (!mcParticle.has_mothers()) { + continue; + } + if (mcParticle.mothersIds()[0] < 0 || mcParticle.mothersIds()[0] >= mcParticles.size()) { + continue; + } + iterMom.setCursor(mcParticle.mothersIds()[0]); + if (iterMom.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtMcEtaGeneral"), iterMom.pt()); + + if (!iterMom.has_daughters()) { + continue; + } + bool childEp = false, childEm = false, childG = false; + double ptEmFromDalitzEta = -1; + double ptGammaFromDalitzEta = -1; + for (int childId = iterMom.daughtersIds()[0]; childId <= iterMom.daughtersIds()[1]; ++childId) { + iterChild.setCursor(childId); + if (iterChild.pdgCode() == pdgEp) { + childEp = true; + } + if (iterChild.pdgCode() == pdgEm) { + childEm = true; + ptEmFromDalitzEta = iterChild.pt(); + } + if (iterChild.pdgCode() == pdgGamma) { + childG = true; + ptGammaFromDalitzEta = iterChild.pt(); + } + } + if (childEp && childEm && childG) { + fRegistry.fill(HIST("hPtMcEtaDalitz"), iterMom.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaGen"), ptEmFromDalitzEta); + fRegistry.fill(HIST("hPtGammaFromTrueEtaGen"), ptGammaFromDalitzEta); + } + } + if (vecEpMCParticles.empty() || vecEmMCParticles.empty()) { + continue; + } + } + + // Reco loop Elektronen + auto iterMC = emmcparticle.begin(); + auto iterMomE = emmcparticle.begin(); + for (auto const& pmcollision : pmcollisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, pmcollision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt()) || fabs(iterMC.y()) > 0.8) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + const auto mcId = ePrimary.emmcparticleId(); + iterMC.setCursor(mcId); + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() != pdgEm) { + continue; + } + + if (!iterMC.has_mothers()) { + continue; + } + iterMomE.setCursor(iterMC.mothersIds()[0]); + if (iterMomE.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoRecoPt"), ePrimary.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoGenPt"), iterMC.pt()); // to do!!!!!!!!!!!!!!! + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processGens, "process for generated (MCParticles)", false); + + void processCheckAssociation(MyEMPrimaryElectrons const& emprimaryelectrons, MyTracksMC const& tracks) + { + auto iterTrack = tracks.begin(); + LOG(info) << "start process Association"; + for (const auto& ePrimary : emprimaryelectrons) { + int idEm = ePrimary.emmcparticleId(); + iterTrack.setCursor(ePrimary.trackId()); + int idTrack = iterTrack.mcParticleId(); + + if (idEm == idTrack) { + fRegistry.fill(HIST("hCheckAsso"), 1); + } else { + fRegistry.fill(HIST("hCheckAsso"), 0); + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processCheckAssociation, "checks if McParticle association is valid", false); +}; + +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"dilepton-effi-for-dalitz-skimmer"})}; +} From dc125267bcaa252e3024430cf474e10fc3a7a323 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Wed, 21 Oct 2026 13:58:13 +0200 Subject: [PATCH 370/648] [PWGCF] Add custom event selection to energyFlowGfw.cxx (#18371) --- PWGCF/GenericFramework/Tasks/CMakeLists.txt | 2 +- .../GenericFramework/Tasks/energyFlowGfw.cxx | 121 ++++++++++++------ 2 files changed, 81 insertions(+), 42 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/CMakeLists.txt b/PWGCF/GenericFramework/Tasks/CMakeLists.txt index bc5feb0779c..90b65bdba93 100644 --- a/PWGCF/GenericFramework/Tasks/CMakeLists.txt +++ b/PWGCF/GenericFramework/Tasks/CMakeLists.txt @@ -31,5 +31,5 @@ o2physics_add_dpl_workflow(flow-gfw-nonflow o2physics_add_dpl_workflow(energy-flow-gfw SOURCES energyFlowGfw.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCCDB O2Physics::AnalysisCore O2Physics::GFWCore COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx index e1d2ea659cc..db7d4c1cdb5 100644 --- a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -18,6 +18,7 @@ #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -39,7 +40,6 @@ #include #include -#include #include #include #include @@ -136,6 +136,7 @@ struct EnergyFlowGfw { Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"}; Configurable cfgFillEfficiency{"fillEfficiency", false, "Fill MC tracking efficiency; ignored by data processes"}; Configurable cfgFillAcceptance{"fillAcceptance", false, "Fill GFW acceptance weights instead of running the reconstructed analysis"}; + Configurable cfgUseRun2EventSelectionWithoutZdc{"cfgUseRun2EventSelectionWithoutZdc", false, "For Run 2 data, require kINT7 and every CCDB-enabled barrel sel7 criterion except the ZDC beam-beam criteria; retain the ZDC beam-gas vetoes"}; Configurable cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"}; Configurable cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"}; @@ -145,8 +146,10 @@ struct EnergyFlowGfw { Configurable cfgZdcResponseZPC{"cfgZdcResponseZPC", 1.f, "ZPC efficiency/acceptance response factor alpha; corrected energy is E_ZPC/alpha_ZPC"}; Configurable cfgZdcMaxAbsTimeForQa{"cfgZdcMaxAbsTimeForQa", 100.f, "Maximum absolute ZDC time considered available in QA; excludes missing-time sentinels"}; - Configurable efficiencyPath{"efficiencyPath", "", "CCDB path or local ROOT file containing a 4D efficiency-correction THn"}; - Configurable efficiencyFromLocalFile{"efficiencyFromLocalFile", false, "Load efficiencyPath from a local ROOT file"}; + struct : ConfigurableGroup { + Configurable cfgEfficiencyPath{"cfgEfficiencyPath", "", "CCDB path to efficiency object"}; + Configurable cfgUsePIDEfficiencies{"cfgUsePIDEfficiencies", false, "Use separate charged, pion, kaon, and proton efficiencies"}; + } cfgEfficiency; Configurable acceptancePath{"acceptancePath", "", "CCDB path to GFW acceptance weights"}; Configurable acceptanceRunByRun{"acceptanceRunByRun", false, "Load acceptance weights from the RunByRun subdirectory"}; @@ -197,8 +200,9 @@ struct EnergyFlowGfw { std::vector zeroHarmonicConfigs; int gfwMask = 0; - std::unique_ptr localEfficiency; - THn* efficiency = nullptr; + TH1* efficiency = nullptr; + std::vector pidEfficiencies; + bool efficiencyLoaded = false; GFWWeights* acceptance = nullptr; int acceptanceRunNumber = -1; @@ -601,39 +605,70 @@ struct EnergyFlowGfw { return true; } - void loadEfficiency(uint64_t timestamp) + static bool isZdcBeamBeamSelectionFlag(int flag) { - if (efficiencyPath.value.empty()) { - efficiency = nullptr; - return; + return flag == aod::evsel::kIsBBZNA || flag == aod::evsel::kIsBBZNC || flag == aod::evsel::kIsBBZAC; + } + + template + bool run2EventSelected(TCollision const& collision) + { + if (!collision.alias_bit(kINT7)) { + return false; } - if (efficiencyFromLocalFile) { - if (localEfficiency) { - efficiency = localEfficiency.get(); - return; - } - std::unique_ptr input(TFile::Open(efficiencyPath.value.c_str(), "READ")); - if (!input || input->IsZombie()) { - LOGF(fatal, "Could not open efficiency file %s", efficiencyPath.value.c_str()); - return; - } - auto* source = dynamic_cast(input->Get("ccdb_object")); - if (!source || source->GetNdimensions() != 4) { - LOGF(fatal, "Efficiency object must be a 4D THn with axes eta, pT, centrality, z-vtx"); + if (!cfgUseRun2EventSelectionWithoutZdc) { + return collision.sel7(); + } + if (!collision.has_foundBC()) { + return false; + } + + const auto& bc = collision.template foundBC_as(); + auto* selectionParameters = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", bc.timestamp()); + if (!selectionParameters) { + LOGF(fatal, "Could not load the Run 2 event-selection parameters"); + } + const bool* enabledSelections = selectionParameters->getSelection(0); + for (int flag = 0; flag < aod::evsel::kNsel; ++flag) { + if (isZdcBeamBeamSelectionFlag(flag)) { + continue; } - localEfficiency.reset(dynamic_cast(source->Clone("eecEfficiency"))); - if (!localEfficiency) { - LOGF(fatal, "Could not clone the efficiency object from %s", efficiencyPath.value.c_str()); + if (enabledSelections[flag] && !collision.selection_bit(flag)) { + return false; } - efficiency = localEfficiency.get(); + } + return true; + } + + void loadEfficiency(uint64_t timestamp) + { + if (efficiencyLoaded) { return; } - if (!efficiency || !ccdb->isCachedObjectValid(efficiencyPath.value, timestamp)) { - efficiency = ccdb->getForTimeStamp>(efficiencyPath.value, timestamp); - if (!efficiency || efficiency->GetNdimensions() != 4) { - LOGF(fatal, "Could not load a 4D efficiency correction from %s", efficiencyPath.value.c_str()); + if (cfgEfficiency.cfgEfficiencyPath.value.empty()) { + efficiencyLoaded = true; + return; + } + + if (!cfgEfficiency.cfgUsePIDEfficiencies) { + efficiency = ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath.value, timestamp); + if (!efficiency) { + LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } else { + static constexpr std::array EfficiencySpeciesNames{"ch", "pi", "ka", "pr"}; + pidEfficiencies.reserve(EfficiencySpeciesNames.size()); + for (const auto& speciesName : EfficiencySpeciesNames) { + const std::string path = cfgEfficiency.cfgEfficiencyPath.value + "/" + speciesName; + pidEfficiencies.push_back(ccdb->getForTimeStamp(path, timestamp)); + if (!pidEfficiencies.back()) { + LOGF(fatal, "Could not load PID efficiency histogram from %s", path.c_str()); + } + LOGF(info, "Loaded PID efficiency histogram from %s", path.c_str()); } } + efficiencyLoaded = true; } void loadAcceptance(uint64_t timestamp, int runNumber) @@ -659,17 +694,20 @@ struct EnergyFlowGfw { acceptanceRunNumber = runNumber; } - double efficiencyWeight(float eta, float pt, float centrality, float posZ) const + double getEfficiency(float pt, int pid) const { - if (!efficiency) { + TH1* efficiencyHistogram = efficiency; + if (cfgEfficiency.cfgUsePIDEfficiencies.value) { + if (pidEfficiencies.empty()) { + return 1.; + } + efficiencyHistogram = pidEfficiencies[pid]; + } else if (!efficiencyHistogram) { return 1.; } - std::array bins{ - efficiency->GetAxis(0)->FindBin(eta), - efficiency->GetAxis(1)->FindBin(pt), - efficiency->GetAxis(2)->FindBin(centrality), - efficiency->GetAxis(3)->FindBin(posZ)}; - return efficiency->GetBinContent(bins.data()); + + const double value = efficiencyHistogram->GetBinContent(efficiencyHistogram->FindBin(pt)); + return value == 0. ? -1. : 1. / value; } template @@ -816,9 +854,10 @@ struct EnergyFlowGfw { gfw->Clear(); for (const auto& track : tracks) { - const int pid = energy.weightMode.value == PtWeight ? Unidentified : particleId(track); + const bool needsParticleId = energy.weightMode.value != PtWeight || cfgEfficiency.cfgUsePIDEfficiencies; + const int pid = needsParticleId ? particleId(track) : Unidentified; const double et = transverseEnergy(track, pid); - const double weff = efficiencyWeight(track.eta(), track.pt(), centrality, collision.posZ()); + const double weff = getEfficiency(track.pt(), pid); const double wacc = acceptance ? acceptance->getNUA(track.phi(), track.eta(), collision.posZ()) : 1.; if (!std::isfinite(et) || !std::isfinite(weff) || !std::isfinite(wacc) || et <= 0. || weff <= 0. || wacc <= 0.) { continue; @@ -868,7 +907,7 @@ struct EnergyFlowGfw { void processRun2(CollisionsRun2::iterator const& collision, Tracks const& tracks, BCsRun2 const&, aod::Zdcs const&) { float spectators = -1.f; - if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { + if (!eventSelected(collision, run2EventSelected(collision), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { return; } if (cfgFillAcceptance) { From a11fc96a793e7e66173f1e5f1d0b07eab7e68fe9 Mon Sep 17 00:00:00 2001 From: Rrantu <156880782+Rrantu@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:28:27 +0200 Subject: [PATCH 371/648] [PWGHF] Add more input features for Xic0 KF selection (#18349) --- PWGHF/Core/HfMlResponseXic0ToXiPiKf.h | 135 ++++++++++++++---- .../candidateSelectorToXiPiQa.cxx | 15 +- .../candidateSelectorXic0ToXiPiKf.cxx | 29 ++-- 3 files changed, 128 insertions(+), 51 deletions(-) diff --git a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h index 3fbb6cf4052..0c4e7fc4d65 100644 --- a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h +++ b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h @@ -18,6 +18,7 @@ #include "PWGHF/Core/HfMlResponse.h" +#include "Common/Core/RecoDecay.h" #include "Tools/ML/MlResponse.h" #include @@ -26,10 +27,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XIC0TOXIPIKF(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE) \ - } +#define FILL_MAP_XIC0TOXIPIKF(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -37,7 +37,7 @@ // by calling the corresponding GETTER from OBJECT #define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(OBJECT, FEATURE, GETTER) \ case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ - inputFeatures.emplace_back(OBJECT.GETTER()); \ + inputFeatures.emplace_back((OBJECT).GETTER()); \ break; \ } @@ -48,10 +48,20 @@ break; \ } +#define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(FEATURE, VALUE) \ + case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ + inputFeatures.emplace_back(VALUE); \ + break; \ + } + +#define FILL_MAP_XIC0TOXIPIKF_AS(NAME, FEATURE) \ + {NAME, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE)} + namespace o2::analysis { enum class InputFeaturesXic0ToXiPiKf : uint8_t { + // Existing feature keys retained for backwards compatibility. tpcNSigmaPiFromLambda, tpcNSigmaPiFromCasc, tpcNSigmaPiFromCharmBaryon, @@ -65,7 +75,24 @@ enum class InputFeaturesXic0ToXiPiKf : uint8_t { chi2TopoCascToPv, chi2TopoCascToXic, cosPaCascToXic, - decayLenXYCasc + decayLenXYCasc, + // Remaining features used by the local PbPb Transformer. + decayLenXYXic, + chi2TopoXicToPv, + chi2TopoPiFromXicToPv, + kfptPiFromXic, + cosPaCascToPv, + dcaXYToPvCascDau, + decayLenXYLambda, + v0Chi2OverNdf, + chi2TopoV0ToPv, + chi2TopoV0ToCasc, + cosPaV0ToPv, + cosPaV0ToCasc, + dcaV0Dau, + dcaXYToPvV0Dau0, + dcaXYToPvV0Dau1, + tpcNSigmaPrFromLambda }; template @@ -81,34 +108,50 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse /// \param candidate is the Xic0 candidate /// \return inputFeatures vector template - // std::vector getInputFeatures(T1 const& candidate) - std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProngPi, T3 const& charmBaryonProngPi) + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& lamProngPr, + T2 const& cascProngPi, T3 const& charmBaryonProngPi) { std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); for (const auto& idx : MlResponse::mCachedIndices) { switch (idx) { - // PID variables - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); - // DCA - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + // Xic candidate topology. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoXicToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCharmBaryonDau); + // Bachelor pion from Xic. CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYPiFromXic); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); - // Chi2Geo + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoPiFromXicToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(kfptPiFromXic, RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon())); + // Cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascChi2OverNdf); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); - // ldl - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); - // Chi2Topo CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToXic); - // CosPa + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaCascToPv, cosPACasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaCascToXic); - // Decay length - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); + // Pion from cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvCascDau); + // Lambda and its charged daughters. The selector passes sign-corrected prongs. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYLambda); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(v0Chi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaV0ToPv, cosPAV0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaV0Dau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau1); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPr, tpcNSigmaPrFromLambda, tpcNSigmaPr); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); + // Legacy features that are not part of the local 28-feature model. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); } } @@ -120,20 +163,48 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse void setAvailableInputFeatures() { MlResponse::mAvailableInputFeatures = { - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromLambda), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCasc), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCharmBaryon), - FILL_MAP_XIC0TOXIPIKF(dcaCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYXic", decayLenXYXic), + FILL_MAP_XIC0TOXIPIKF(xicChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fXicChi2OverNdf", xicChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoXicToPv", chi2TopoXicToPv), FILL_MAP_XIC0TOXIPIKF(dcaCharmBaryonDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCharmBaryonDau", dcaCharmBaryonDau), FILL_MAP_XIC0TOXIPIKF(kfDcaXYPiFromXic), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYPiFromXic", kfDcaXYPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoPiFromXicToPv", chi2TopoPiFromXicToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fKfptPiFromXic", kfptPiFromXic), + FILL_MAP_XIC0TOXIPIKF(decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYCasc", decayLenXYCasc), FILL_MAP_XIC0TOXIPIKF(cascChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(xicChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(cascldl), + FILL_MAP_XIC0TOXIPIKF_AS("fCascChi2OverNdf", cascChi2OverNdf), FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToPv", chi2TopoCascToPv), FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToXic", chi2TopoCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToPv", cosPaCascToPv), FILL_MAP_XIC0TOXIPIKF(cosPaCascToXic), - FILL_MAP_XIC0TOXIPIKF(decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToXic", cosPaCascToXic), + FILL_MAP_XIC0TOXIPIKF(dcaCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCascDau", dcaCascDau), + FILL_MAP_XIC0TOXIPIKF(kfDcaXYCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYCascToPv", kfDcaXYCascToPv), + FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCasc", tpcNSigmaPiFromCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvCascDau", dcaXYToPvCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYLambda", decayLenXYLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fV0Chi2OverNdf", v0Chi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToPv", chi2TopoV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToCasc", chi2TopoV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToPv", cosPaV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToCasc", cosPaV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaV0Dau", dcaV0Dau), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau0", dcaXYToPvV0Dau0), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau1", dcaXYToPvV0Dau1), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPrFromLambda", tpcNSigmaPrFromLambda), + FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromLambda", tpcNSigmaPiFromLambda), + FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCharmBaryon), + FILL_MAP_XIC0TOXIPIKF(cascldl), }; } }; @@ -141,7 +212,9 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse } // namespace o2::analysis #undef FILL_MAP_XIC0TOXIPIKF +#undef FILL_MAP_XIC0TOXIPIKF_AS #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF +#undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE #endif // PWGHF_CORE_HFMLRESPONSEXIC0TOXIPIKF_H_ diff --git a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx index 8845d78ee70..2c8dfc1cbb7 100644 --- a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx +++ b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx @@ -78,12 +78,12 @@ struct HfCandidateSelectorToXiPiQa { // cuts from SelectorCuts.h - pT dependent cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; // CCDB configuration @@ -144,7 +144,7 @@ struct HfCandidateSelectorToXiPiQa { o2::analysis::HfMlResponseXic0ToXiPi hfMlResponseDca; o2::analysis::HfMlResponseXic0ToXiPiKf hfMlResponseKf; - std::vector outputMlXic0ToXiPi = {}; + std::vector outputMlXic0ToXiPi; o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; @@ -718,12 +718,11 @@ struct HfCandidateSelectorToXiPiQa { // ML BDT selection if required if (applyMl) { bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = {}; if constexpr (svReco == doDcaFitter) { - inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + auto inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); isSelectedMlXic0 = hfMlResponseDca.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); } else { - inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + auto inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); isSelectedMlXic0 = hfMlResponseKf.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); } @@ -767,8 +766,8 @@ struct HfCandidateSelectorToXiPiQa { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx index 69d2a73339b..5139c504e57 100644 --- a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx +++ b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx @@ -120,15 +120,23 @@ struct HfCandidateSelectorXic0ToXiPiKf { // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; // ML inference Configurable applyMl{"applyMl", true, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> namesInputFeatures{"namesInputFeatures", + std::vector{"fDecayLenXYXic", "fXicChi2OverNdf", "fChi2TopoXicToPv", "fDcaCharmBaryonDau", + "fKfDcaXYPiFromXic", "fChi2TopoPiFromXicToPv", "fKfptPiFromXic", "fDecayLenXYCasc", + "fCascChi2OverNdf", "fChi2TopoCascToPv", "fChi2TopoCascToXic", "fCosPaCascToPv", + "fCosPaCascToXic", "fDcaCascDau", "fKfDcaXYCascToPv", "fTpcNSigmaPiFromCasc", + "fDcaXYToPvCascDau", "fDecayLenXYLambda", "fV0Chi2OverNdf", "fChi2TopoV0ToPv", + "fChi2TopoV0ToCasc", "fCosPaV0ToPv", "fCosPaV0ToCasc", "fDcaV0Dau", + "fDcaXYToPvV0Dau0", "fDcaXYToPvV0Dau1", "fTpcNSigmaPrFromLambda", "fTpcNSigmaPiFromLambda"}, + "Names of ML model input features"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -224,14 +232,11 @@ struct HfCandidateSelectorXic0ToXiPiKf { auto trackPiFromCasc = lfTracks.rawIteratorAt(trackPiFromCascId); auto trackPiFromCharm = tracks.rawIteratorAt(trackPiFromCharmId); - auto trackPiFromLam = trackV0NegDau; - auto trackPrFromLam = trackV0PosDau; - int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + auto const trackPiFromLam = signDecay > 0 ? trackV0PosDau : trackV0NegDau; + auto const trackPrFromLam = signDecay > 0 ? trackV0NegDau : trackV0PosDau; if (signDecay > 0) { - trackPiFromLam = trackV0PosDau; - trackPrFromLam = trackV0NegDau; registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay } else if (signDecay < 0) { registry.fill(HIST("hSelSignDec"), 0); // particle decay @@ -418,7 +423,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { // ML selections if (applyMl) { bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); if (!resultSelections) { hfMlToXiPi(outputMlXic0ToXiPiKf); } else { @@ -526,7 +531,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { } // dcaXY v0 daughters to PV cut - if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau0")) { + if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau1")) { return false; } @@ -597,8 +602,8 @@ struct HfCandidateSelectorXic0ToXiPiKf { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } From d00ddd7b34b3d1124ab168666666958c87923e24 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:44:44 +0530 Subject: [PATCH 372/648] [PWGLF] Modified event loss for INEL (#18373) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 47 +++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index c2e2a545c8e..21e7db35a50 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -232,11 +232,11 @@ struct Kstarqa { std::vector eveCutLabels = { "All Events", - "|Vz| < 10", - std::string("Sel8") + check(configGp.isSel8.value), std::string("kIsTriggerTVX") + check(configGp.isTriggerTVX.value), + "|Vz| < 10", std::string("NoTimeFrameBorder") + check(configGp.isNoTimeFrameBorder.value), std::string("NoITSROFrameBorder") + check(configGp.isNoITSROFrameBorder.value), + std::string("Sel8") + check(configGp.isSel8.value), std::string("rctChecker") + check(rctCut.requireRCTFlagChecker.value), std::string("IsINELgt0") + check(configGp.isINELgt0.value), std::string("IsGoodITSLayersAll") + check(configGp.isGoodITSLayersAll.value), @@ -355,6 +355,7 @@ struct Kstarqa { hInvMass.add("MCcorrections/MultiplicityRec", "Multiplicity in generated MC with at least 1 reconstruction", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParameterGen", "Impact parameter in generated MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityGen", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); + hInvMass.add("MCcorrections/MultiplicityRecINEL", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParametervsMultiplicity", "Impact parameter vs multiplicity in reconstructed MC", kTH2F, {{impactParAxis}, {multiplicityAxis}}); } @@ -473,27 +474,27 @@ struct Kstarqa { if (fillHist) rEventSelection.fill(HIST("hEventCut"), 0); - if (std::abs(collision.posZ()) > configGp.cutzvertex) + if (configGp.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 1); - if (configGp.isSel8 && !collision.sel8()) + if (std::abs(collision.posZ()) > configGp.cutzvertex) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 2); - if (configGp.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) + if (configGp.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 3); - if (configGp.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (configGp.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 4); - if (configGp.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (configGp.isSel8 && !collision.sel8()) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 5); @@ -1911,6 +1912,7 @@ struct Kstarqa { { // auto multiplicityRec = -1; bool isSelectedEvent = false; + bool isSelectedEventINEL = false; auto multiplicity1 = -999.; if (configGp.isINELgt0Gen && !mcCollision.isInelGt0()) { @@ -1942,6 +1944,31 @@ struct Kstarqa { isSelectedEvent = true; } + for (const auto& RecCollision : recCollisions) { + if (!RecCollision.has_mcCollision()) + continue; + + if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + continue; + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + if (configGp.isSel8 && !RecCollision.sel8()) + continue; + + if (rctCut.requireRCTFlagChecker && !rctChecker(RecCollision)) + continue; + + isSelectedEventINEL = true; + } + bool isINELgt0true = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { @@ -1957,6 +1984,10 @@ struct Kstarqa { hInvMass.fill(HIST("MCcorrections/hImpactParameterGen"), impactPar); hInvMass.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGen); + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/MultiplicityRecINEL"), multiplicityGen); + } + // Event loss if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hImpactParameterRec"), impactPar); @@ -3200,7 +3231,7 @@ struct Kstarqa { genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); - mother = daughter1 + daughter2; // Phi meson + mother = daughter1 + daughter2; // Phi meson genMother = genDaughter1 + genDaughter2; // Phi meson recMass = mother.M(); From bb0c1e7529eb0c85b03da1d0ca71c8742797d966 Mon Sep 17 00:00:00 2001 From: Maxim Virta <84773378+MaximVirta@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:04:03 +0200 Subject: [PATCH 373/648] [PWGCF] Added v24 calculations and high purity selection for v02 analysis (#18374) Co-authored-by: Maxim Virta --- PWGCF/GenericFramework/Tasks/flowGfwV02.cxx | 151 ++++++++++++-------- 1 file changed, 91 insertions(+), 60 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx index db3767664e9..5768bb877f7 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx @@ -74,6 +74,8 @@ using namespace analysis::genericframework; #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +// Rows: UpCut_pi, UpCut_ka, UpCut_pr, LowCut_pi, LowCut_ka, LowCut_pr. Columns: TPC, TOF, ITS. +static constexpr std::array, 6> HighPurityNsigma = {{{{1.5f, 0.f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, -1.5f, 0.f}}, {{-1.5f, 0.f, 0.f}}, {{-1.5f, 0.f, 0.f}}}}; template auto projectMatrix(Array2D const& mat, std::array& array1, std::array& array2, std::array& array3) @@ -124,6 +126,8 @@ struct FlowGfwV02 { O2_DEFINE_CONFIGURABLE(cfgFixedMultMax, int, 3000, "Maximum for fixed nch range"); O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgUseHighPurityPID, bool, false, "Use high purity PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgHighPurityPtMin, float, 3.f, "Minimum pt (GeV/c) for high purity PID") O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFlowWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting"); @@ -197,8 +201,9 @@ struct FlowGfwV02 { Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 5.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull", "refMid", "piP", "kaP", "prP"}, {-0.8, 0.5, -0.8, -0.4, 0.5, 0.5, 0.5}, {-0.5, 0.8, 0.8, 0.4, 0.8, 0.8, 0.8}, {0, 0, 0, 0, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1}}, "Configurations for GFW regions"}; - Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {2 2} refN {-2 -2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "ChGap24", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> nSigmasHighPurity{"nSigmasHighPurity", {HighPurityNsigma.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "High purity n-sigma windows for TPC, TOF, ITS for pions, kaons, and protons"}; struct : ConfigurableGroup { Configurable cfgZvtxMax{"cfgZvtxMax", 10.0f, "Maximum primary vertex cut applied for the events."}; @@ -270,6 +275,9 @@ struct FlowGfwV02 { std::array tofNsigmaCut{}; std::array itsNsigmaCut{}; std::array tpcNsigmaCut{}; + std::array highPurityTofNsigmaCut{}; + std::array highPurityItsNsigmaCut{}; + std::array highPurityTpcNsigmaCut{}; std::array, 4> hPtMid{}; std::array, 4> hPtForward{}; std::array, 4> hPtBackward{}; @@ -307,6 +315,7 @@ struct FlowGfwV02 { { projectMatrix(nSigmas->getData(), pidStates.tpcNsigmaCut, pidStates.tofNsigmaCut, pidStates.itsNsigmaCut); + projectMatrix(nSigmasHighPurity->getData(), pidStates.highPurityTpcNsigmaCut, pidStates.highPurityTofNsigmaCut, pidStates.highPurityItsNsigmaCut); if (cfgGetNsigmaQA) { if (cfgUseItsPID) { @@ -439,12 +448,14 @@ struct FlowGfwV02 { registry.add("analysis/charged/ptAB", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptC", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/v22", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v24", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); if (cfgUseMultiplicityFracWeights) { registry.add("analysis/charged/ptA2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptB2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/ptC3pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); registry.add("analysis/charged/v223pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v224pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); } ccdb->setURL("http://alice-ccdb.cern.ch"); @@ -599,6 +610,21 @@ struct FlowGfwV02 { std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; std::array nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + if (cfgUseHighPurityPID && track.pt() > cfgHighPurityPtMin) { + bool isHighPurityPion = nSigmaTPC[IndPionUp] < pidStates.highPurityTpcNsigmaCut[IndPionUp] && nSigmaTPC[IndPionUp] > pidStates.highPurityTpcNsigmaCut[IndPionLow] && nSigmaTOF[IndPionUp] < pidStates.highPurityTofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.highPurityTofNsigmaCut[IndPionLow]; + bool isHighPurityKaon = nSigmaTPC[IndKaonUp] < pidStates.highPurityTpcNsigmaCut[IndKaonUp] && nSigmaTPC[IndKaonUp] > pidStates.highPurityTpcNsigmaCut[IndKaonLow] && nSigmaTOF[IndKaonUp] < pidStates.highPurityTofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.highPurityTofNsigmaCut[IndKaonLow]; + bool isHighPurityProton = nSigmaTPC[IndProtonUp] < pidStates.highPurityTpcNsigmaCut[IndProtonUp] && nSigmaTPC[IndProtonUp] > pidStates.highPurityTpcNsigmaCut[IndProtonLow] && nSigmaTOF[IndProtonUp] < pidStates.highPurityTofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.highPurityTofNsigmaCut[IndProtonLow]; + if (isHighPurityPion) { + pid = PidPions; + } else if (isHighPurityKaon) { + pid = PidKaons; + } else if (isHighPurityProton) { + pid = PidProtons; + } else { + return -1; + } + return pid; + } std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS std::array detectorNsigmaCut = cfgUseItsPID ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS @@ -869,8 +895,8 @@ struct FlowGfwV02 { template void fillOutputContainers(const float& centmult) { - constexpr double threshold = 1.01; - constexpr double minDnxAB = 1e-8; // skip events with vanishing V22 weight + constexpr double Threshold = 1.01; + constexpr double MinDnxAB = 1e-8; // skip events with vanishing V22 weight int bootstrap = fRndm->Integer(gfwMemberCache.nBootstrap); // Calculate V02 @@ -880,12 +906,15 @@ struct FlowGfwV02 { double ptFractionMid = 0.; double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB auto valAB = fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB; - if (std::abs(valAB) > threshold || std::isnan(valAB) || std::isinf(valAB) || dnxAB < minDnxAB) { + double dnxABAB = fGFW->Calculate(corrconfigs.at(1), 0, kTRUE).real(); // V22 weight for ABAB + auto valABAB = fGFW->Calculate(corrconfigs.at(1), 0, kFALSE).real() / dnxABAB; + if (std::abs(valAB) > Threshold || std::isnan(valAB) || std::isinf(valAB) || dnxAB < MinDnxAB || std::abs(valABAB) > Threshold || std::isnan(valABAB) || std::isinf(valABAB) || dnxABAB < MinDnxAB) { return; } double v22pt = valAB * ptMeanMid; - double WeightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; - double WeightC = 1.0; + double weightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; + double weightABAB = (cfgUseMultiplicityFlowWeights) ? dnxABAB : 1.0; + double weightC = 1.0; // Calculate V02 for each particle type for (int pid = 0; pid < PidTotal; pid++) { int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; @@ -893,7 +922,7 @@ struct FlowGfwV02 { continue; // Mid pT distribution is not defined } if (cfgUseMultiplicityFracWeights) { - WeightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight + weightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight } for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); @@ -901,31 +930,31 @@ struct FlowGfwV02 { switch (pid) { case PidCharged: - registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); + registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); + registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); } break; case PidPions: - registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); + registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); + registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); } break; case PidKaons: - registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); + registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); + registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); } break; case PidProtons: - registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, WeightC * WeightAB); - registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC); + registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, WeightC * WeightAB); + registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); } break; default: @@ -936,15 +965,17 @@ struct FlowGfwV02 { // Calculate NCv22pt if (cfgUseMultiplicityFracWeights) { - WeightC = pidStates.hPtMid[PidCharged]->Integral(); + weightC = pidStates.hPtMid[PidCharged]->Integral(); } - registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, WeightAB); - registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, WeightC); + registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, weightC * weightAB); + registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, weightAB); + registry.fill(HIST("analysis/charged/v24"), bootstrap, centmult, valABAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, weightC); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, WeightC * WeightAB); - registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, WeightC * WeightAB); + registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, weightC * weightAB); + registry.fill(HIST("analysis/charged/v224pc"), bootstrap, centmult, valAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, weightC * weightAB); } } // End of V02 @@ -956,16 +987,16 @@ struct FlowGfwV02 { double ptMeanBackward = pidStates.hPtBackward[PidCharged]->GetMean(); double ptFractionForward = 0.; double ptFractionBackward = 0.; - double WeightA = 1.0; - double WeightB = 1.0; + double weightA = 1.0; + double weightB = 1.0; for (int pid = 0; pid < PidTotal; pid++) { int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; if (!(pidStates.hPtForward[normIndex]->Integral() > 0) || !(pidStates.hPtBackward[normIndex]->Integral() > 0)) { continue; // Forward or backward pT distribution is not defined } if (cfgUseMultiplicityFracWeights) { - WeightA = pidStates.hPtForward[PidCharged]->Integral(); - WeightB = pidStates.hPtBackward[PidCharged]->Integral(); + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); } for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { ptFractionForward = pidStates.hPtForward[pid]->GetBinContent(i) / pidStates.hPtForward[normIndex]->Integral(); @@ -974,43 +1005,43 @@ struct FlowGfwV02 { v0corrBA = ptFractionBackward * ptMeanForward; switch (pid) { case PidCharged: - registry.fill(HIST("analysis/charged/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/charged/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/charged/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/charged/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/charged/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/charged/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/charged/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/charged/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/charged/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/charged/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidPions: - registry.fill(HIST("analysis/pion/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/pion/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/pion/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/pion/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/pion/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/pion/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/pion/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/pion/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/pion/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/pion/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/pion/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/pion/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidKaons: - registry.fill(HIST("analysis/kaon/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/kaon/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/kaon/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/kaon/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/kaon/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/kaon/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/kaon/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/kaon/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; case PidProtons: - registry.fill(HIST("analysis/proton/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, WeightA * WeightB); - registry.fill(HIST("analysis/proton/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, WeightA * WeightB); - registry.fill(HIST("analysis/proton/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA); - registry.fill(HIST("analysis/proton/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightB); + registry.fill(HIST("analysis/proton/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/proton/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/proton/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/proton/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/proton/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, WeightA * WeightB); - registry.fill(HIST("analysis/proton/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, WeightA * WeightB); + registry.fill(HIST("analysis/proton/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/proton/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); } break; default: @@ -1021,16 +1052,16 @@ struct FlowGfwV02 { // Calculate PID independent parts of V0 if (cfgUseMultiplicityFracWeights) { - WeightA = pidStates.hPtForward[PidCharged]->Integral(); - WeightB = pidStates.hPtBackward[PidCharged]->Integral(); + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); } - registry.fill(HIST("analysis/charged/ptA"), bootstrap, centmult, ptMeanForward, WeightA); - registry.fill(HIST("analysis/charged/ptB"), bootstrap, centmult, ptMeanBackward, WeightB); + registry.fill(HIST("analysis/charged/ptA"), bootstrap, centmult, ptMeanForward, weightA); + registry.fill(HIST("analysis/charged/ptB"), bootstrap, centmult, ptMeanBackward, weightB); if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/ptA2pc"), bootstrap, centmult, ptMeanForward, WeightA * WeightB); - registry.fill(HIST("analysis/charged/ptB2pc"), bootstrap, centmult, ptMeanBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/ptA2pc"), bootstrap, centmult, ptMeanForward, weightA * weightB); + registry.fill(HIST("analysis/charged/ptB2pc"), bootstrap, centmult, ptMeanBackward, weightA * weightB); } - registry.fill(HIST("analysis/charged/ptAB"), bootstrap, centmult, ptMeanForward * ptMeanBackward, WeightA * WeightB); + registry.fill(HIST("analysis/charged/ptAB"), bootstrap, centmult, ptMeanForward * ptMeanBackward, weightA * weightB); } // End of V0 } // End of Filling From 1b35063bb0d685db5f6aaa5e89385d9b09b27ac6 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:37:53 +0200 Subject: [PATCH 374/648] [PWGCF] Add new process function to femto producer (#18376) --- PWGCF/Femto/Core/cascadeHistManager.h | 3 --- PWGCF/Femto/Core/kinkHistManager.h | 3 --- PWGCF/Femto/Core/mcParticleHistManager.h | 10 ++++----- PWGCF/Femto/Core/v0HistManager.h | 3 --- PWGCF/Femto/TableProducer/femtoProducer.cxx | 23 +++++++++++++++++++++ 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index c9b6659678a..9270b5b0cdc 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -719,9 +719,6 @@ class CascadeHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index 6a439153f80..7bb73ebad68 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -599,9 +599,6 @@ class KinkHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; diff --git a/PWGCF/Femto/Core/mcParticleHistManager.h b/PWGCF/Femto/Core/mcParticleHistManager.h index 641558589f5..7b5ff24eee2 100644 --- a/PWGCF/Femto/Core/mcParticleHistManager.h +++ b/PWGCF/Femto/Core/mcParticleHistManager.h @@ -213,9 +213,10 @@ class McParticleHistManager if (mPlotOrigins) { // NOTE: kNoMcParticle and kMissidentified are kept here only for enum/histogram // symmetry with TrackHist — they describe reco-vs-truth mismatches that cannot - // occur for a pure MC-truth particle, so these bins are expected to stay empty - // unless the producer assigns those origin codes for some other reason - // (e.g. pileup-associated particles tagged as kFromWrongCollision). + // occur for a pure MC-truth particle, so these bins are expected to stay empty. + // kFromWrongCollision is not handled here at all: mcBuilder.h's getOrigin() never + // assigns it (only kPhysicalPrimary/kFromSecondaryDecay/kFromMaterial), so it would + // be dead code the same way it was in the per-candidate hist managers. mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kNoMcParticle: @@ -224,9 +225,6 @@ class McParticleHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), mcParticle.pt()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), mcParticle.pt()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), mcParticle.pt()); break; diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 6301c1e8880..d5e8848e434 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -716,9 +716,6 @@ class V0HistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index e10eb8a366c..460190c1e54 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -669,6 +669,29 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPbWithEventShape, "Provide tracks, v0s and cascades in PbPb collisions with event shape information", false); + void processTracksV0sCascadesPhotonsRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + processV0s(col, tracks, v0s); + processCascades(col, tracks, cascades); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesPhotonsRun3PbPbWithEventShape, "Provide tracks, v0s, cascades and PCM photons in PbPb collisions with event shape information", false); + void processTracksKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, From cfd760e31c0f0ed460dde3e8e160c16989d1cfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:39:43 +0200 Subject: [PATCH 375/648] [ALICE3] Fix casts (#18381) --- ALICE3/Core/FlatLutEntry.cxx | 16 ++++++++-------- ALICE3/Core/FlatLutWriter.cxx | 4 ++-- ALICE3/Core/FlatTrackSmearer.cxx | 2 +- ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx | 2 +- ALICE3/TableProducer/alice3-correlatorDDbar.cxx | 2 +- ALICE3/TableProducer/alice3DqTableMaker.cxx | 4 ++-- ALICE3/TableProducer/alice3HfSelector3Prong.cxx | 2 +- .../TableProducer/alice3TrackingTranslator.cxx | 2 +- ALICE3/Tasks/alice3-qa-multiplicity.cxx | 2 +- ALICE3/Tasks/alice3DqEfficiency.cxx | 2 +- ALICE3/Tasks/alice3HfTask3Prong.cxx | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ALICE3/Core/FlatLutEntry.cxx b/ALICE3/Core/FlatLutEntry.cxx index 984157f4520..66a6cedf860 100644 --- a/ALICE3/Core/FlatLutEntry.cxx +++ b/ALICE3/Core/FlatLutEntry.cxx @@ -140,23 +140,23 @@ size_t FlatLutData::getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt const lutEntry_t* FlatLutData::getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mDataRef.data() + offset); + return reinterpret_cast(mDataRef.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutEntry_t* FlatLutData::getEntry(int nch_bin, int rad_bin, int eta_bin, int pt_bin) { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mData.data() + offset); + return reinterpret_cast(mData.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } const lutHeader_t& FlatLutData::getHeaderRef() const { - return *static_cast(static_cast(mDataRef.data())); + return *reinterpret_cast(mDataRef.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutHeader_t& FlatLutData::getHeader() { - return *static_cast(static_cast(mData.data())); + return *reinterpret_cast(mData.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } void FlatLutData::updateRef() @@ -216,7 +216,7 @@ lutHeader_t FlatLutData::previewHeader(const uint8_t* buffer, size_t size) if (size < sizeof(lutHeader_t)) { throw framework::runtime_error_f("Buffer too small for LUT header: expected at least %zu, got %zu", sizeof(lutHeader_t), size); } - const auto* header = static_cast(static_cast(buffer)); + const auto* header = reinterpret_cast(buffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!header->checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, header->version); } @@ -246,7 +246,7 @@ FlatLutData FlatLutData::ViewFromBuffer(const uint8_t* buffer, size_t size) FlatLutData FlatLutData::ViewFromBuffer(std::span const& span) { - return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); + return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool FlatLutData::isLoaded() const @@ -257,7 +257,7 @@ bool FlatLutData::isLoaded() const lutHeader_t FlatLutData::previewHeader(std::ifstream& file, const char* filename) { lutHeader_t tempHeader; - file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); + file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(sizeof(lutHeader_t))) { throw framework::runtime_error_f("Failed to read LUT header from %s", filename); } @@ -282,7 +282,7 @@ FlatLutData FlatLutData::loadFromFile(std::ifstream& file, const char* filename) size_t numEntries = static_cast(data.mNchBins) * data.mRadBins * data.mEtaBins * data.mPtBins; size_t entriesSize = numEntries * sizeof(lutEntry_t); - file.read(reinterpret_cast(data.data() + headerSize), entriesSize); + file.read(reinterpret_cast(data.data() + headerSize), entriesSize); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(entriesSize)) { throw framework::runtime_error_f("Failed to read LUT entries from %s: expected %zu bytes, got %zu", filename, entriesSize, static_cast(file.gcount())); } diff --git a/ALICE3/Core/FlatLutWriter.cxx b/ALICE3/Core/FlatLutWriter.cxx index dfb874af223..a85c242a8b1 100644 --- a/ALICE3/Core/FlatLutWriter.cxx +++ b/ALICE3/Core/FlatLutWriter.cxx @@ -266,7 +266,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t setMap(lutHeader.ptmap, mPtBinning); // Write header to file - lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); + lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT header to %s", filename); lutFile.close(); @@ -361,7 +361,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t diagonalise(lutEntry); // Write entry to file - lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); + lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT entry at index [%d,%d,%d,%d]", inch, irad, ieta, ipt); lutFile.close(); diff --git a/ALICE3/Core/FlatTrackSmearer.cxx b/ALICE3/Core/FlatTrackSmearer.cxx index 1bd79f088a8..9617315d2e4 100644 --- a/ALICE3/Core/FlatTrackSmearer.cxx +++ b/ALICE3/Core/FlatTrackSmearer.cxx @@ -171,7 +171,7 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f bool TrackSmearer::viewTable(int pdg, std::span const& span, bool forceReload) { - return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); + return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool TrackSmearer::hasTable(int pdg) const diff --git a/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx index 6b69d082961..689da6f6e83 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx @@ -1036,7 +1036,7 @@ struct OnTheFlyMidPid { if (etStatus) { histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); } - static_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + dynamic_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); } // Then we do a second loop to compute the measured quantities with the measured event time diff --git a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx index 0e27bec4476..5603fb97348 100644 --- a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx +++ b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx @@ -115,7 +115,7 @@ struct alice3correlatorddbar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/ALICE3/TableProducer/alice3DqTableMaker.cxx b/ALICE3/TableProducer/alice3DqTableMaker.cxx index 041fc71ebfe..1cbbb4fa8a9 100644 --- a/ALICE3/TableProducer/alice3DqTableMaker.cxx +++ b/ALICE3/TableProducer/alice3DqTableMaker.cxx @@ -301,7 +301,7 @@ struct Alice3DqTableMaker { fStatsList.setObject(new TList()); fStatsList->SetOwner(true); std::vector eventLabels{"Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); int ibX = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ibX++) { histEvents->GetXaxis()->SetBinLabel(ibX, (*label).Data()); @@ -583,7 +583,7 @@ struct Alice3DqTableMaker { if (fDoDetailedQA) { j = 0; for (const auto& cut : fTrackCuts) { - if ((trackTempFilterMap & (uint8_t(1) << j)) != 0u) { + if ((trackTempFilterMap & (static_cast(1) << j)) != 0u) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqefficiency_helpers::varValues()); // fill the reconstructed truth } j++; diff --git a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx index 89c34ff1cf3..3669ffd834d 100644 --- a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx @@ -114,7 +114,7 @@ struct Alice3HfSelector3Prong { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index 26f53cf9f5f..e0e742fc9d0 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -360,7 +360,7 @@ struct Alice3TrackingTranslator { if (filesList) { TIter next(filesList); TSystemFile* file; - while ((file = static_cast(next()))) { + while ((file = dynamic_cast(next()))) { TString fname = file->GetName(); LOG(debug) << "Found file: " << fname.Data(); if (!file->IsDirectory() && fname.EndsWith(".root")) { diff --git a/ALICE3/Tasks/alice3-qa-multiplicity.cxx b/ALICE3/Tasks/alice3-qa-multiplicity.cxx index a5440ff8d53..5e81cd8fd5a 100644 --- a/ALICE3/Tasks/alice3-qa-multiplicity.cxx +++ b/ALICE3/Tasks/alice3-qa-multiplicity.cxx @@ -41,7 +41,7 @@ struct ALICE3MultTask { void init(InitContext&) { - const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : (int)MaxMult, 0, MaxMult, "Reconstructed tracks"}; + const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : static_cast(MaxMult), 0, MaxMult, "Reconstructed tracks"}; TString tit = Form("%.3f < #it{#eta} < %.3f", MinEta.value, MaxEta.value); histos.add("multiplicity/numberOfTracksNoCut", tit, kTH1D, {axisMult}); histos.add("multiplicity/numberOfTracks", tit, kTH1D, {axisMult}); diff --git a/ALICE3/Tasks/alice3DqEfficiency.cxx b/ALICE3/Tasks/alice3DqEfficiency.cxx index 29361efe44e..d90a6f0b320 100644 --- a/ALICE3/Tasks/alice3DqEfficiency.cxx +++ b/ALICE3/Tasks/alice3DqEfficiency.cxx @@ -983,7 +983,7 @@ struct Alice3DqEfficiencyAnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.syst, fConfigOptions.energy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.syst), fConfigOptions.energy); // set collision system and center of mass energy defineHistograms(fHistMan, histNames.Data(), cfgAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/ALICE3/Tasks/alice3HfTask3Prong.cxx b/ALICE3/Tasks/alice3HfTask3Prong.cxx index 4f21bada994..823d054395a 100644 --- a/ALICE3/Tasks/alice3HfTask3Prong.cxx +++ b/ALICE3/Tasks/alice3HfTask3Prong.cxx @@ -119,7 +119,7 @@ struct Alice3HfTask3Prong { registry.add(("MC/gen/nonprompt/" + histoName + "GenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); }; - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// Reconstructed Histograms addHistogramsRec("hMass", "inv. mass (p K #pi) (GeV/#it{c}^{2})", "", {HistType::kTH1F, {{600, 1.98, 2.58}}}); From d83fd36a29636005e1d3c50ac057eb2991639d5c Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 21 Oct 2026 19:44:32 +0200 Subject: [PATCH 376/648] [PWGCF] Simplify daughter selections in femto framework (#18395) --- PWGCF/Femto/Core/partitions.h | 18 +++++++ PWGCF/Femto/Core/trackBuilder.h | 48 ++++++++++++++----- .../TableProducer/femtoProducerResonances.cxx | 8 ++-- ...oProducerTrackTrackTwoPhotonResonances.cxx | 12 ++--- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index 4f03bbe7c04..cb3cfe24b53 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -56,6 +56,24 @@ (o2::aod::femtobase::stored::mass > (selection).massMin) && \ (o2::aod::femtobase::stored::mass < (selection).massMax) +// partition for resonance daughters (pion+/-, kaon+/- legs): unlike MAKE_TRACK_PARTITION, chargeSign +// and chargeAbs are compile-time constants on the selection struct (ConfResonanceDaughterSelection in +// trackBuilder.h), not runtime Configurables, so the sign comparison is just a comparison to a constant +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_DAUGHTER_PARTITION(selection) \ + ((selection).chargeSign > 0 ? o2::aod::femtobase::stored::signedPt > 0.f : o2::aod::femtobase::stored::signedPt < 0.f) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ifnode(TRACK_MOMENTUM((selection).chargeAbs, o2::aod::femtobase::stored::signedPt, o2::aod::femtobase::stored::eta) <= (selection).pidThres, \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskLowMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskLowMomentum) == static_cast(0), \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskHighMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskHighMomentum) == static_cast(0)) + // partition for phis and rhos, i.e. resonance that are their own antiparticle -- both daughters are // the same species, so the selection carries a single dauMask pair applied to both stored columns // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 6ea1d97ebe5..eaf95a5c033 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -160,12 +160,6 @@ constexpr const char PrefixTrackSelection1[] = "TrackSelection1"; constexpr const char PrefixTrackSelection2[] = "TrackSelection2"; constexpr const char PrefixTrackSelection3[] = "TrackSelection3"; -// for resonances builder -constexpr const char PrefixPionPlus[] = "PionPlusSelection"; -constexpr const char PrefixPionMinus[] = "PionMinusSelection"; -constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; -constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; - // for dN/deta: tracks with TPC (ITS + TPC quality bits) and tracks without TPC (ITS quality bits only) // set maskLowMomentum = maskHighMomentum (quality bits only), chargeSign = 0 and the eta/pT range of the measurement constexpr const char PrefixTrackSelectionDndetaGlobal[] = "TrackSelectionDndetaGlobal"; // Global = ITS + TPC @@ -176,14 +170,46 @@ using ConfTrackSelection1 = ConfTrackSelection; using ConfTrackSelection2 = ConfTrackSelection; using ConfTrackSelection3 = ConfTrackSelection; -using ConfPionPlusSelection = ConfTrackSelection; -using ConfPionMinusSelection = ConfTrackSelection; -using ConfKaonPlusSelection = ConfTrackSelection; -using ConfKaonMinusSelection = ConfTrackSelection; - using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; +// Track selection for resonance daughters (pion+/-, kaon+/-). Charge sign and abs charge are fixed +// by the species itself, so they are compile-time constants rather than configurables. No pdg code +// here at all: the daughter mass used to reconstruct the resonance is set directly in the resonance +// builder (e.g. MassPiPlus/MassKMinus), never read from this selection. Used exclusively by the +// resonance builders via MAKE_RESONANCE_DAUGHTER_PARTITION. +template +struct ConfResonanceDaughterSelection : public o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + static const int chargeAbs = 1; + static const int chargeSign = ChargeSign; + // filters for kinematics + o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // track selection masks + o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 1ul, "Bitmask for selections below momentum threshold"}; + o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 2ul, "Bitmask for selections above momentum threshold"}; + // track rejection masks + o2::framework::Configurable rejectionMaskLowMomentum{"rejectionMaskLowMomentum", 0ul, "Bitmask for rejections below momentum threshold"}; + o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; + // momentum threshold for PID usage + o2::framework::Configurable pidThres{"pidThres", PidThresDefault, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; +}; + +constexpr const char PrefixPionPlus[] = "PionPlusSelection"; +constexpr const char PrefixPionMinus[] = "PionMinusSelection"; +constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; +constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; + +using ConfPionPlusSelection = ConfResonanceDaughterSelection; +using ConfPionMinusSelection = ConfResonanceDaughterSelection; +using ConfKaonPlusSelection = ConfResonanceDaughterSelection; +using ConfKaonMinusSelection = ConfResonanceDaughterSelection; + /// enum for all track selections enum TrackSels { // track quality cuts diff --git a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx index 38b787c2c0e..179023fc657 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx @@ -47,16 +47,16 @@ struct FemtoProducerResonances { // setup for resonance daughter tracks trackbuilder::ConfPionPlusSelection confPionPlusSelection; - o2::framework::Partition pionPlusPartition = MAKE_TRACK_PARTITION(confPionPlusSelection); + o2::framework::Partition pionPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionPlusSelection); trackbuilder::ConfPionMinusSelection confPionMinusSelection; - o2::framework::Partition pionMinusPartition = MAKE_TRACK_PARTITION(confPionMinusSelection); + o2::framework::Partition pionMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionMinusSelection); trackbuilder::ConfKaonPlusSelection confKaonPlusSelection; - o2::framework::Partition kaonPlusPartition = MAKE_TRACK_PARTITION(confKaonPlusSelection); + o2::framework::Partition kaonPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonPlusSelection); trackbuilder::ConfKaonMinusSelection confKaonMinusSelection; - o2::framework::Partition kaonMinusPartition = MAKE_TRACK_PARTITION(confKaonMinusSelection); + o2::framework::Partition kaonMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonMinusSelection); o2::framework::Preslice perColtracks = o2::aod::femtobase::stored::fColId; diff --git a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx index 3aed1730218..20308a135a4 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx @@ -52,14 +52,14 @@ struct FemtoProducerTrackTrackTwoPhotonResonances { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; - // setup for the two charged (pion) daughters -- plain track selections, same mechanism used for - // any other track daughter; sign is configured through these, not hardcoded in the builder - trackbuilder::ConfTrackSelection1 posDauSelection; - o2::framework::Partition posDauPartition = MAKE_TRACK_PARTITION(posDauSelection); + // setup for the two charged (pion) daughters. Dedicated pion-species selections, same ones + // femtoProducerResonances.cxx uses for other two track resonances + trackbuilder::ConfPionPlusSelection posDauSelection; + o2::framework::Partition posDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(posDauSelection); o2::framework::Preslice perColPosDau = o2::aod::femtobase::stored::fColId; - trackbuilder::ConfTrackSelection2 negDauSelection; - o2::framework::Partition negDauPartition = MAKE_TRACK_PARTITION(negDauSelection); + trackbuilder::ConfPionMinusSelection negDauSelection; + o2::framework::Partition negDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(negDauSelection); o2::framework::Preslice perColNegDau = o2::aod::femtobase::stored::fColId; // setup for the neutral (pi0) daughter From ae803ecf858d3e35ba3b2e2557a4d181d52b95fa Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Wed, 21 Oct 2026 20:38:28 +0200 Subject: [PATCH 377/648] [PWGHF] Add information to get generated signals with daughters in acceptance (#18377) --- .../TableProducer/treeCreatorXicToXiPiPi.cxx | 140 +++++++++++++++++- 1 file changed, 134 insertions(+), 6 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index b407f3b51be..79eb3e81c13 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -70,13 +70,20 @@ DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! DECLARE_SOA_COLUMN(PtXi, ptXi, float); //! Transverse momentum of Xi (prong0) (GeV/c) DECLARE_SOA_COLUMN(ImpactParameterXi, impactParameterXi, float); //! Impact parameter of Xi (prong0) DECLARE_SOA_COLUMN(ImpactParameterNormalisedXi, impactParameterNormalisedXi, float); //! Normalised impact parameter of Xi (prong0) +DECLARE_SOA_COLUMN(EtaBachelorPi, etaBachelorPi, float); //! Pseudorapidity of bachelor Pi DECLARE_SOA_COLUMN(PPi0, pPi0, float); //! Momentum of Pi0 (prong1) (GeV/c) DECLARE_SOA_COLUMN(PtPi0, ptPi0, float); //! Transverse momentum of Pi0 (prong1) (GeV/c) +DECLARE_SOA_COLUMN(EtaPi0, etaPi0, float); //! Pseudorapidity of Pi0 (prong1 DECLARE_SOA_COLUMN(ImpactParameterPi0, impactParameterPi0, float); //! Impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi0, impactParameterNormalisedPi0, float); //! Normalised impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(PPi1, pPi1, float); //! Momentum of Pi1 (prong2) (GeV/c) DECLARE_SOA_COLUMN(PtPi1, ptPi1, float); //! Transverse momentum of Pi1 (prong2) (GeV/c) -DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Normalised impact parameter of Pi1 (prong2) +DECLARE_SOA_COLUMN(EtaPi1, etaPi1, float); //! Pseudorapidity of Pi1 (prong2) +DECLARE_SOA_COLUMN(PtPiFromLambda, ptPiFromLambda, float); //! Transverse momentum of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPiFromLambda, etaPiFromLambda, float); //! Pseudorapidity of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(PtPrFromLambda, ptPrFromLambda, float); //! Transverse momentum of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPrFromLambda, etaPrFromLambda, float); //! Pseudorapidity of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi1, impactParameterNormalisedPi1, float); //! Normalised impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreBkg, mlScoreBkg, float); //! ML score for background class @@ -367,7 +374,17 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullPs, "AOD", "HFXICXI2PIFULLP", full::Eta, full::Phi, full::Y, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + full::PtPi0, + full::PtPi1, + hf_cand_xic_to_xi_pi_pi::PtBachelorPi, + full::PtPiFromLambda, + full::PtPrFromLambda, + full::EtaPi0, + full::EtaPi1, + full::EtaBachelorPi, + full::EtaPiFromLambda, + full::EtaPrFromLambda); } // namespace o2::aod /// Writes the full information in an output TTree @@ -382,6 +399,7 @@ struct HfTreeCreatorXicToXiPiPi { Configurable selectionFlagXic{"selectionFlagXic", 1, "Selection Flag for Xic"}; Configurable fillCandidateLiteTable{"fillCandidateLiteTable", false, "Switch to fill lite table with candidate properties"}; Configurable fillGenParticleTable{"fillGenParticleTable", false, "Switch to fill table with MC truth for generated particles"}; + Configurable fillGenInfoForAcceptance{"fillGenInfoForAcceptance", true, "Enable filling of information for acceptance computation"}; // parameters for production of training samples Configurable fillOnlySignal{"fillOnlySignal", false, "Flag to fill derived tables with signal for ML trainings"}; Configurable fillOnlyBackground{"fillOnlyBackground", false, "Flag to fill derived tables with background for ML trainings"}; @@ -399,7 +417,7 @@ struct HfTreeCreatorXicToXiPiPi { using MatchedGenXicToXiPiPi = soa::Filtered>; Filter filterSelectCandidates = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= selectionFlagXic; - Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi))); + Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) || fillGenInfoForAcceptance == true); Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); @@ -715,6 +733,61 @@ struct HfTreeCreatorXicToXiPiPi { } } + template + void getMcDaughterInformation(Parts const& particles, + Parts::iterator const& particle, + float& ptPi0, + float& ptPi1, + float& ptPiBachelor, + float& ptPiFromLambda, + float& ptPrFromLambda, + float& etaPi0, + float& etaPi1, + float& etaPiBachelor, + float& etaPiFromLambda, + float& etaPrFromLambda) + { + std::vector arrDaughIndex = {}; + RecoDecay::getDaughters(particle, &arrDaughIndex, std::array{+kXiMinus, +kPiPlus, +kPiPlus}, 2); + bool foundFirstPi{false}; + for (const int iProng : arrDaughIndex) { + auto daugh = particles.rawIteratorAt(iProng); + if (std::abs(daugh.pdgCode()) == +kPiPlus) { + if (foundFirstPi) { + ptPi1 = daugh.pt(); + etaPi1 = daugh.eta(); + } else { + ptPi0 = daugh.pt(); + etaPi0 = daugh.eta(); + foundFirstPi = true; + } + } else if (std::abs(daugh.pdgCode()) == +kXiMinus) { + std::vector arrDaughIndexXi = {}; + RecoDecay::getDaughters(daugh, &arrDaughIndexXi, std::array{+kLambda0, -kPiPlus}, 1); + for (const int iProngXi : arrDaughIndexXi) { + auto daughXi = particles.rawIteratorAt(iProngXi); + if (std::abs(daughXi.pdgCode()) == +kPiPlus) { + ptPiBachelor = daughXi.pt(); + etaPiBachelor = daughXi.eta(); + } else { + std::vector arrDaughIndexLambda = {}; + RecoDecay::getDaughters(daughXi, &arrDaughIndexLambda, std::array{+kProton, -kPiPlus}, 1); + for (const int iProngLambda : arrDaughIndexLambda) { + auto daughLambda = particles.rawIteratorAt(iProngLambda); + if (std::abs(daughLambda.pdgCode()) == +kPiPlus) { + ptPiFromLambda = daughLambda.pt(); + etaPiFromLambda = daughLambda.eta(); + } else { + ptPrFromLambda = daughLambda.pt(); + etaPrFromLambda = daughLambda.eta(); + } + } + } + } + } + } + } + void processData(SelectedCandidates const& candidates) { // Filling candidate properties @@ -806,6 +879,14 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), @@ -814,7 +895,17 @@ struct HfTreeCreatorXicToXiPiPi { particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -860,6 +951,14 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), @@ -868,7 +967,17 @@ struct HfTreeCreatorXicToXiPiPi { particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -906,6 +1015,15 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } + rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), @@ -914,7 +1032,17 @@ struct HfTreeCreatorXicToXiPiPi { particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } From dc62da0f890476d0b36db2cdd36964e4f26a3b9a Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Wed, 21 Oct 2026 21:47:05 +0200 Subject: [PATCH 378/648] [PWGLF] Add `Best Collision` Cut in Response Hist in hStrangeCorrelation.cxx (#18378) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 136 +++++++++++++++--- 1 file changed, 114 insertions(+), 22 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 8a1390ca80b..3b29c9d3ef8 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -338,10 +338,23 @@ struct HStrangeCorrelation { // see runEventRecoEfficiencyStudy Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; + Configurable recComparisonBestCollisionOnly{"recComparisonBestCollisionOnly", false, "part 2: run the Rec path of PairLossK0/Comparison only in the best reconstructed collision (most PV contributors) of each MC collision, the one the Gen, Truth and Final stages are selected on. Set false to run it in every reconstructed collision, as the data path does"}; Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude"}; + Configurable nchGenResponseBestCollisionOnly{"nchGenResponseBestCollisionOnly", false, "fill PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1 with the best reconstructed collision (most PV contributors) of each MC collision only, instead of with every reconstructed collision; the multNTracksPVeta1 vs FT0M responses stay per reconstructed collision"}; Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; + // MC-only event quality cut on the Nch^gen vs multNTracksPVeta1 response + // (PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1): a reconstructed collision + // is kept only if |multNTracksPVeta1 - mu(Nch^gen)| <= nSigma * max(sigma(Nch^gen), sigmaMin), + // with mu a polynomial in Nch^gen and sigma a polynomial in sqrt(Nch^gen), e.g. fits of + // the mean and width of each Nch^gen column of that response. It uses the generated + // multiplicity, so it has no data counterpart + Configurable applyNchResponseBandCut{"applyNchResponseBandCut", false, "part 2 (MC only): keep only reconstructed collisions inside the mu +- nSigma*sigma band of the Nch^gen vs multNTracksPVeta1 response, in the Rec (every collision), Gen, Truth and Final (best collision) stages of PairLossK0/Comparison"}; + Configurable> nchResponseBandMeanPars{"nchResponseBandMeanPars", {0.0, 0.9}, "mean multNTracksPVeta1 = p0 + p1*x + p2*x^2 + ..., x = Nch^gen"}; + Configurable> nchResponseBandSigmaPars{"nchResponseBandSigmaPars", {0.0, 0.35}, "multNTracksPVeta1 width = p0 + p1*sqrt(x) + p2*x + p3*x^1.5 + ..., x = Nch^gen"}; + Configurable nchResponseBandNSigma{"nchResponseBandNSigma", 3.0f, "half-width of the band in units of sigma"}; + Configurable nchResponseBandSigmaMin{"nchResponseBandSigmaMin", 1.0f, "lower bound of sigma, which keeps the band open at low Nch^gen"}; // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8 // by mCounter. Plain ConfigurableAxis: unlike the correlation axes it is NOT // trimmed by skipUnderOverflowInTHn, so what you configure is what you get. @@ -2826,6 +2839,10 @@ struct HStrangeCorrelation { } } + if (pairLossK0Configurations.applyNchResponseBandCut && !doprocessPairLossK0MC) { + LOGF(fatal, "pairLossK0Configurations.applyNchResponseBandCut needs the generated multiplicity; enable processPairLossK0MC"); + } + if (doprocessPairLossK0MC && pairLossK0Configurations.doEventRecoEfficiencyStudy) { // Per MC collision with |vz_gen| <= zVertexCut, each histogram a subset of // the previous one: HasRecoColl/All is the MC-collision reconstruction @@ -2837,10 +2854,21 @@ struct HStrangeCorrelation { histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSel", "best reconstructed collision passes the Rec event selection with reconstructed-level INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);reconstructed collisions", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNRecoCollisions}); // Per reconstructed collision passing the Rec event selection without INEL - // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut - histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut. + // With nchGenResponseBestCollisionOnly the Nch^gen response takes only the best + // collision, which drops the split and duplicate vertices of the MC collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", + pairLossK0Configurations.nchGenResponseBestCollisionOnly ? "best reconstructed collision passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" + : "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", + kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + if (pairLossK0Configurations.applyNchResponseBandCut) { + // Checks of applyNchResponseBandCut: the part of the Nch^gen response it keeps, + // and hNchGenEvSelNoInelInBand / hNchGenEvSelNoInel, its efficiency vs Nch^gen + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand", "hNchGenVsNTracksPVeta1 inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand", "best reconstructed collision passes the Rec event selection without INEL cuts and lies inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + } } if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse) { @@ -3438,6 +3466,29 @@ struct HStrangeCorrelation { return nChGen > 0; } + // MC-only event quality cut of the PairLoss Comparison stages (applyNchResponseBandCut): + // multNTracksPVeta1 inside mu(Nch^gen) +- nSigma * max(sigma(Nch^gen), sigmaMin), with mu + // a polynomial in Nch^gen and sigma one in sqrt(Nch^gen). Evaluated here rather than + // through TF1 members: the task struct is at the StructToTuple member limit + bool isInNchResponseBand(int nChGen, int nTracksPVeta1) + { + const double sqrtNchGen = std::sqrt(static_cast(std::max(nChGen, 0))); + double mean = 0.0; + double power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandMeanPars.value) { + mean += par * power; + power *= nChGen; + } + double sigma = 0.0; + power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandSigmaPars.value) { + sigma += par * power; + power *= sqrtNchGen; + } + sigma = std::max(sigma, static_cast(pairLossK0Configurations.nchResponseBandSigmaMin)); + return std::abs(nTracksPVeta1 - mean) <= pairLossK0Configurations.nchResponseBandNSigma * sigma; + } + // this function allows for all event selections to be done in a modular way // applyInelSelection = false skips only the reconstructed INEL>0 / INEL>N cuts template @@ -4667,9 +4718,11 @@ struct HStrangeCorrelation { TracksComplete const& tracks) { // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the - // event-reconstruction-efficiency study and the generated-level INEL selection + // event-reconstruction-efficiency study, the generated-level INEL selection and + // the Nch^gen vs multNTracksPVeta1 band cut const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel; - const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel) ? countNchGenEta1(mcParticles) : -1; + const bool applyBandCut = pairLossK0Configurations.applyNchResponseBandCut; + const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel || applyBandCut) ? countNchGenEta1(mcParticles) : -1; // Part 3: a self-contained generator-level study. It deliberately touches no // reconstructed quantity in its event selection or in any of its axes: the @@ -4887,10 +4940,12 @@ struct HStrangeCorrelation { bestCollisionVtxZ = collision.posZ(); bestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // with applyGenInel (pp only) the reconstructed INEL cuts are replaced by - // Nch^gen >= 1, as in the Rec and Gen stages - pairLossEventSelected = !pairLossK0Configurations.applyRecoEventSelection || - (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) - : isCollisionSelectedPbPb(collision, false)); + // Nch^gen >= 1, as in the Rec and Gen stages; the band cut acts on top, + // independently of applyRecoEventSelection + pairLossEventSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); } if (bestCollisionId < 0) { return; @@ -4999,6 +5054,7 @@ struct HStrangeCorrelation { bool genBestCollisionGoodZvtxFT0vsPV = false; bool genCollisionSelected = false; int genLargestNContributors = -1; + int genBestCollisionNTracksPVeta1 = -1; uint32_t genBestCollisionTriggerPresenceMap = 0; for (auto const& recCollision : recCollisions) { if (genLargestNContributors >= recCollision.numContrib()) { @@ -5008,6 +5064,7 @@ struct HStrangeCorrelation { genBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); // Both branches fill the Gen THn with this vertex position genBestCollisionVtxZ = recCollision.posZ(); + genBestCollisionNTracksPVeta1 = recCollision.multNTracksPVeta1(); if (masterConfigurations.applyNewMCSelection) { genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision, !applyGenInel)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false)); @@ -5034,6 +5091,10 @@ struct HStrangeCorrelation { (!masterConfigurations.requireGoodZvtxFT0vsPV || genBestCollisionGoodZvtxFT0vsPV) && genBestCollisionMultiplicity >= axisRanges[5][0] && genBestCollisionMultiplicity <= axisRanges[5][1]; } + // the band cut acts on top of either Gen event selection, on the best collision + if (applyBandCut) { + genEventSelected = genEventSelected && isInNchResponseBand(nChGen, genBestCollisionNTracksPVeta1); + } if (genEventSelected && masterConfigurations.doCorrelationK0Short && TESTBIT(doCorrelation, IndexK0)) { std::vector genTriggerIndices; @@ -5282,6 +5343,16 @@ struct HStrangeCorrelation { pairLossComparison.nChGen = applyGenInel ? nChGen : -1; pairLossComparison.active = true; for (auto const& recCollision : recCollisions) { + // recComparisonBestCollisionOnly: one reconstructed collision per MC collision, + // the best one, as in the Gen stage + if (pairLossK0Configurations.recComparisonBestCollisionOnly && recCollision.globalIndex() != bestCollisionId) { + continue; + } + // The band cut tests every reconstructed collision against the Nch^gen of + // this MC collision, so it also rejects split and duplicate vertices + if (applyBandCut && !isInNchResponseBand(nChGen, recCollision.multNTracksPVeta1())) { + continue; + } const auto recTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, recCollision.globalIndex()); const auto recV0Slice = associatedV0s.sliceBy(collisionSliceV0s, recCollision.globalIndex()); const auto recTrackSlice = tracks.sliceBy(pairLossTracksPerCollision, recCollision.globalIndex()); @@ -5306,14 +5377,44 @@ struct HStrangeCorrelation { // responses. The event selection is the one runSameEventHV0s applies before // its INEL cut: trigger presence + isCollisionSelected (isCollisionSelectedNoInel) auto runEventRecoEfficiencyStudy = [&]() { - // Responses: one entry per reconstructed collision, as in the Rec path + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + bool bestCollisionSelectedNoInel = false; + bool bestCollisionSelected = false; + int bestCollisionNTracksPVeta1 = -1; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionSelectedNoInel = isCollisionSelectedNoInel(collision); + // reconstructed-level INEL cuts; the Pb-Pb event selection has none + bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + } + + // Nch^gen response, and with applyNchResponseBandCut the part of it inside the band + auto fillNchGenResponse = [&](int nTracksPVeta1) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, nTracksPVeta1); + if (applyBandCut && isInNchResponseBand(nChGen, nTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand"), nChGen, nTracksPVeta1); + } + }; + + // Responses: one entry per reconstructed collision, as in the Rec path, except + // the Nch^gen response with nchGenResponseBestCollisionOnly (best collision only) for (auto const& collision : recCollisions) { if (isCollisionSelectedNoInel(collision)) { - histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, collision.multNTracksPVeta1()); + if (!pairLossK0Configurations.nchGenResponseBestCollisionOnly) { + fillNchGenResponse(collision.multNTracksPVeta1()); + } histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); } } + if (pairLossK0Configurations.nchGenResponseBestCollisionOnly && bestCollisionSelectedNoInel) { + fillNchGenResponse(bestCollisionNTracksPVeta1); + } // Efficiency: one entry per MC collision, evaluated on the best collision if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { @@ -5325,20 +5426,11 @@ struct HStrangeCorrelation { return; } histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl"), nChGen); - int largestNContributors = -1; - bool bestCollisionSelectedNoInel = false; - bool bestCollisionSelected = false; - for (auto const& collision : recCollisions) { - if (collision.numContrib() <= largestNContributors) { - continue; - } - largestNContributors = collision.numContrib(); - bestCollisionSelectedNoInel = isCollisionSelectedNoInel(collision); - // reconstructed-level INEL cuts; the Pb-Pb event selection has none - bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); - } if (bestCollisionSelectedNoInel) { histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel"), nChGen); + if (applyBandCut && isInNchResponseBand(nChGen, bestCollisionNTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand"), nChGen); + } } if (bestCollisionSelected) { histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSel"), nChGen); From 4d3475512494be04e3c9ce145dd1ae2d30f4298f Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Thu, 22 Oct 2026 00:22:44 +0200 Subject: [PATCH 379/648] [PWGLF,PWGMM] Remove particle filtering for MC efficiencies (#18399) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 45 +++++++++++++----------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 9a1251a4d9d..6ad1b5e086b 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -81,7 +80,6 @@ using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; auto static constexpr CintZero = 0; auto static constexpr CintOne = 1; -auto static constexpr CfloatFive = 5.f; auto static constexpr CInvalid = -999.f; enum class EvtSel { @@ -257,7 +255,7 @@ struct DndetaMFTPbPb { ConfigurableAxis fv0aMultBins{"fv0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0aMultBins{"ft0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0cMultBins{"ft0cMultBins", {501, -0.5, 500.5}, ""}; - ConfigurableAxis ptBins{"ptBins", {101, -0.5, 10.5}, "pT binning (GeV/c)"}; + ConfigurableAxis ptBins{"ptBins", {11, -0.5, 10.5}, "pT binning (GeV/c)"}; ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; ConfigurableAxis zvtxBins{"zvtxBins", {60, -30., 30.}, "Z-vtx binning (cm)"}; ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; @@ -762,8 +760,6 @@ struct DndetaMFTPbPb { } } - /// Filters - particles - Filter primaries = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > CfloatFive, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)); /// Joined tables using FullBCs = soa::Join; using CollBCs = soa::Join; @@ -781,7 +777,6 @@ struct DndetaMFTPbPb { using MftBestTracksLabeled = soa::Join; /// Particles using ParticlesIdx = soa::Join; - using FiltParticlesIdx = soa::Filtered; template float getDCAz(const T& track) @@ -2310,13 +2305,11 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); - // Partition primariesIdx = ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > 5.f, false, ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)) && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); - /// @brief process template function to calculate tracking efficiency (indexed as particle-to-MFT-tracks) template void processEfficiencyIdx(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& /*tracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); @@ -2360,13 +2353,13 @@ struct DndetaMFTPbPb { // MC gen if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } } @@ -2387,7 +2380,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; @@ -2402,7 +2395,7 @@ struct DndetaMFTPbPb { } else { if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); } iscounted = true; @@ -2433,13 +2426,13 @@ struct DndetaMFTPbPb { // MC FAKES if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } @@ -2451,7 +2444,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); @@ -2461,7 +2454,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftTracksLabeled const& tracks) { processEfficiencyIdx(mccollision, collisions, particles, tracks); @@ -2472,7 +2465,7 @@ struct DndetaMFTPbPb { template void processEfficiencyIdxBest(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& /*atracks*/) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); @@ -2519,13 +2512,13 @@ struct DndetaMFTPbPb { // MC gen if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); } } @@ -2554,7 +2547,7 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } iscounted = true; @@ -2569,7 +2562,7 @@ struct DndetaMFTPbPb { } else { if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); } iscounted = true; @@ -2600,13 +2593,13 @@ struct DndetaMFTPbPb { // MC FAKES if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } @@ -2618,7 +2611,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); @@ -2628,7 +2621,7 @@ struct DndetaMFTPbPb { void processMcEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, - FiltParticlesIdx const& particles, + ParticlesIdx const& particles, MftBestTracksLabeled const& atracks) { processEfficiencyIdxBest(mccollision, collisions, particles, atracks); From b2fa2bb65c43e7b5c621aff05b4ddc12f58b2b84 Mon Sep 17 00:00:00 2001 From: omvazque Date: Wed, 21 Oct 2026 17:14:41 -0600 Subject: [PATCH 380/648] [PWGLF] Fixed bug:Using isBestCollIndex instead of bestCollisionIndex (#18398) --- PWGLF/Tasks/Nuspex/piKpRAA.cxx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index 16edb8c98be..c45074ecae0 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -1542,21 +1542,20 @@ struct PiKpRAA { hasbc = collision.has_foundBC(); hasft0 = collision.has_foundFT0(); hastvx = collision.selection_bit(o2::aod::evsel::kIsTriggerTVX); - hasBcFT0TVX = hasbc && hasft0 && hastvx; + hasBcFT0TVX = (hasbc && hasft0 && hastvx); if (hasBcFT0TVX) { // NUMERATOR/DENOMINATOR OF THE EVENT LOSS registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX"), nChMCTPCAcc); - if (hasBcFT0TVX && bestCollisionIndex) { + + // TVX-triggered + bestCollisionIndex + if (isBestCollIndex) { + registry.fill(HIST("EventCounterMC"), 4.5); + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCTPCAcc); } } - // TVX-triggered + bestCollisionIndex - if (hasBcFT0TVX && isBestCollIndex) { - registry.fill(HIST("EventCounterMC"), 4.5); - } - //--------------------------- // LOOPING OVER THE MC EVENT //--------------------------- @@ -1605,7 +1604,7 @@ struct PiKpRAA { if (hasBcFT0TVX) { registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); - if (hasBcFT0TVX && isBestCollIndex) { + if (isBestCollIndex) { registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } @@ -1614,7 +1613,7 @@ struct PiKpRAA { if (hasBcFT0TVX) { registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); - if (hasBcFT0TVX && isBestCollIndex) { + if (isBestCollIndex) { registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } @@ -1623,7 +1622,7 @@ struct PiKpRAA { if (hasBcFT0TVX) { registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); - if (hasBcFT0TVX && isBestCollIndex) { + if (isBestCollIndex) { registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); } From 4083b57029b3a1a44efb0fde92d3f9c18b4a8630 Mon Sep 17 00:00:00 2001 From: Maxime Guilbaud Date: Thu, 22 Oct 2026 01:25:01 +0200 Subject: [PATCH 381/648] [Infrastructure] updating code ownership for DQ (#18397) --- CODEOWNERS | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index b2560d073fd..7a7a8b840a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -49,11 +49,17 @@ /PWGCF/TwoParticleCorrelations @alibuild @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser /PWGCF/Tutorial @alibuild @ariedel-cern @victor-gonzalez @dsarkaralice @lauraser -/PWGDQ @alibuild @iarsene @mcoquet642 @XiaozhiBai @mguilbau +#PWG-DQ +/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya + +#PWG-EM /PWGEM @alibuild @feisenhu @dsekihat @jokonig @hscheid /PWGEM/Dilepton @alibuild @mikesas @rbailhac @dsekihat @feisenhu @hscheid @jokonig /PWGEM/PhotonMeson @alibuild @mhemmer-cern @dsekihat @jokonig @hscheid @amaringarcia @reygers + +#PWG-HF /PWGHF @alibuild @vkucera @fcolamar @fgrosa @fcatalan92 @mfaggin @mmazzilli @deepathoms @NicoleBastid @hahassan7 @jpxrk @apalasciano @zhangbiao-phy @gluparel @stefanopolitano @xinyepeng @singhra1994 + # PWG-LF /PWGLF @alibuild @omvazque @skundu692 @mpuccio /PWGLF/DataModel @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova @@ -80,7 +86,7 @@ /Tools/PIDML @alibuild @saganatt /Tools/ML @alibuild @fcatalan92 @fmazzasc @ChSonnabend /Tutorials/PWGCF @alibuild @jgrosseo @victor-gonzalez @zchochul -/Tutorials/PWGDQ @alibuild @iarsene @mcoquet642 @XiaozhiBai @mguilbau +/Tutorials/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya /Tutorials/PWGEM @alibuild @mikesas @rbailhac @dsekihat @ivorobye @feisenhu /Tutorials/PWGHF @alibuild @vkucera @fcolamar @fgrosa @gluparel @xinyepeng /Tutorials/PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman From e910b0cf9744dd17f9928eff633729228833491a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:58:04 +0200 Subject: [PATCH 382/648] [PWGHF] Fix BC association in charm-hadron flow task (#18400) --- PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx | 55 +++++++++++++++--------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx index 707fdf48373..4738a6cc74e 100644 --- a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx @@ -182,9 +182,9 @@ struct HfTaskFlowCharmHadrons { using CandXic0Data = soa::Filtered>; using CandXic0DataWMl = soa::Filtered>; using CandD0DataWMl = soa::Filtered>; - using CollsWithSPQvecs = soa::Join; - using CollsWithEsEQvecs = soa::Join; - using CollsWithSPEsEQvecs = soa::Join; + using CollsWithSPQvecs = soa::Join; + using CollsWithEsEQvecs = soa::Join; + using CollsWithSPEsEQvecs = soa::Join; using TracksWithExtra = soa::Join; Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; @@ -621,11 +621,13 @@ struct HfTaskFlowCharmHadrons { float const amplRedQVec = qVecRedComps[2]; if (useOnlineResoCorrection) { + // Access BC information through the collision's BC index. + const auto bc = collision.template bc_as(); // Per-run update - if (collision.runNumber() != lastRunNumber) { - LOG(info) << "Run number changed from " << lastRunNumber << " to " << collision.runNumber() << ". Updating resolution values."; - qVecResoHelper.update(ccdb, ccdbResoHistPath, resoTripletPath.value, collision.timestamp(), collision.runNumber()); - lastRunNumber = collision.runNumber(); + if (bc.runNumber() != lastRunNumber) { + LOG(info) << "Run number changed from " << lastRunNumber << " to " << bc.runNumber() << ". Updating resolution values."; + qVecResoHelper.update(ccdb, ccdbResoHistPath, resoTripletPath.value, bc.timestamp(), bc.runNumber()); + lastRunNumber = bc.runNumber(); } scalarProdReso = qVecResoHelper[static_cast(cent - 0.5f)]; // Cent is given in X.5f format } @@ -840,7 +842,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML void processDsMl(CollsWithSPQvecs::iterator const& collision, CandDsDataWMl const&, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -852,7 +855,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML EsE void processDsEsEMl(CollsWithEsEQvecs::iterator const& collision, CandDsDataWMl const& /*candidatesDs*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -864,7 +868,8 @@ struct HfTaskFlowCharmHadrons { // Ds with ML EsE and SP void processDsSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandDsDataWMl const& /*candidatesDs*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -876,7 +881,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML void processDplusMl(CollsWithSPQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -885,7 +891,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML EsE void processDplusEsEMl(CollsWithEsEQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -894,7 +901,8 @@ struct HfTaskFlowCharmHadrons { // Dplus with ML SP and EsE void processDplusSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDplus, tracks); } @@ -903,7 +911,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0Ml(CollsWithSPQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -915,7 +924,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0EsEMl(CollsWithEsEQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -927,7 +937,8 @@ struct HfTaskFlowCharmHadrons { // D0 with ML void processD0SPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -939,7 +950,8 @@ struct HfTaskFlowCharmHadrons { // Lc with ML void processLcMl(CollsWithSPQvecs::iterator const& collision, CandLcDataWMl const& /*candidatesLc*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsLcToPKPiWMl = selectedLcToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsLcToPiKPWMl = selectedLcToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -951,7 +963,8 @@ struct HfTaskFlowCharmHadrons { // Xic with ML void processXicMl(CollsWithSPQvecs::iterator const& collision, CandXicDataWMl const& /*candidatesXic*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXicToPKPiWMl = selectedXicToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsXicToPiKPWMl = selectedXicToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -963,7 +976,8 @@ struct HfTaskFlowCharmHadrons { // Xic0 with ML void processXic0Ml(CollsWithSPQvecs::iterator const& collision, CandXic0DataWMl const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0WMl = selectedXic0WMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); runFlowAnalysis(collision, candsXic0WMl, tracks); @@ -973,7 +987,8 @@ struct HfTaskFlowCharmHadrons { // Xic0 void processXic0(CollsWithSPQvecs::iterator const& collision, CandXic0Data const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0 = selectedXic0->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); runFlowAnalysis(collision, candsXic0, tracks); From 64c3f8fb23c112baeaad3988ee1b62582412c7a3 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 22 Oct 2026 09:33:02 +0200 Subject: [PATCH 383/648] [PWGCF] Add triton purity QA in HadNucleiFemto.cxx (#18375) Co-authored-by: ALICE Action Bot --- .../TableProducer/HadNucleiFemto.cxx | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 3af64a1e310..44a92a9ff16 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -11,7 +11,7 @@ // /// \file HadNucleiFemtoDcaPurity.cxx -/// \brief Nuclei-hadron femtoscopy task +/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs /// \author CMY /// \date 2025-04-10 @@ -428,6 +428,7 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"tritonPid"}; // Triton track-quality, purity and PID cuts + Configurable settingITSNClsMinTr{"settingITSNClsMinTr", 5, "Minimum number of ITS clusters for triton candidates"}; Configurable settingPIDMomentumSplitTr{"settingPIDMomentumSplitTr", 2.f, "Momentum separating low- and high-momentum triton TPC PID"}; Configurable settingCutTPCNsigmaLowPTr{"settingCutTPCNsigmaLowPTr", 3.f, "Maximum absolute TPC n-sigma for tritons below the momentum split"}; Configurable settingCutITSNsigmaLowPTr{"settingCutITSNsigmaLowPTr", 3.f, "Maximum absolute ITS n-sigma for tritons below the momentum split"}; @@ -827,6 +828,7 @@ struct HadNucleiFemto { {"h2NsigmaHadKaTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(K)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, // Purity + {"purity/h2MassTOFTr_preselection", "Triton TOF mass before mass cut; signed #it{p}_{T} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {280, 0.0f, 7.0f}}}}, {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -5.0f, 5.0f}}}}, @@ -1254,7 +1256,6 @@ struct HadNucleiFemto { constexpr int minTPCCrossedRows = 70; constexpr float maxTPCChi2NCl = 5.f; constexpr float maxTPCFractionSharedCls = 0.3f; - constexpr int minITSNCls = 5; constexpr float maxITSChi2NCl = 10.f; return !(std::abs(candidate.eta()) >= maxAbsEta || @@ -1263,7 +1264,7 @@ struct HadNucleiFemto { candidate.tpcNClsCrossedRows() < minTPCCrossedRows || candidate.tpcChi2NCl() >= maxTPCChi2NCl || candidate.tpcFractionSharedCls() >= maxTPCFractionSharedCls || - candidate.itsNCls() < minITSNCls || + candidate.itsNCls() < tritonPid.settingITSNClsMinTr || candidate.itsChi2NCl() >= maxITSChi2NCl); } @@ -3189,7 +3190,7 @@ struct HadNucleiFemto { std::unordered_map>& mixingPools, int& mixingRunNumber) { - const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}}; + const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; @@ -3344,8 +3345,8 @@ struct HadNucleiFemto { void processMixedEvent(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { LOG(debug) << "Processing mixed event"; - const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}}; - const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}}; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; for (const auto& collision : collisions) { mQaRegistry.fill(HIST("hMixedEventSelections"), 0); @@ -3478,8 +3479,8 @@ struct HadNucleiFemto { void processMixedEventDiagnostic(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { - const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}}; - const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}}; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; ++mDiagnosticCall; MEDiagnosticRow call; call.run = mDiagnosticRun; @@ -4079,10 +4080,33 @@ struct HadNucleiFemto { } } else if (passTrackNu && useTritonNucleus()) { const float tpcNSigmaTr = track.tpcNSigmaTr(); + const float absPTr = std::abs(track.p()); + const float signedPtTr = track.sign() * track.pt(); + const bool passTPCRejectTr = !(std::abs(track.tpcNSigmaDe()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPr()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPi()) < tritonPid.settingTPCRejectNsig); o2::aod::ITSResponse itsResponse; - const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), std::abs(track.p()), track.eta()); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaTr); - mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), track.sign() * track.pt(), itsNSigmaTr); + const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), absPTr, track.eta()); + if (passTPCRejectTr) { + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtTr, itsNSigmaTr); + } + const bool passITSTr = absPTr >= tritonPid.settingPIDMomentumSplitTr || + std::abs(itsNSigmaTr) < tritonPid.settingCutITSNsigmaLowPTr; + const bool passTPCTr = absPTr < tritonPid.settingPIDMomentumSplitTr + ? std::abs(tpcNSigmaTr) < tritonPid.settingCutTPCNsigmaLowPTr + : tpcNSigmaTr > tritonPid.settingCutTPCNsigmaHighPMinTr && + tpcNSigmaTr < tritonPid.settingCutTPCNsigmaHighPMaxTr; + // Keep triton TPC PID open, while matching the species-rejection and ITS cuts. + if (passTPCRejectTr && passITSTr) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtTr, tpcNSigmaTr); + // Apply the triton TPC PID cut but keep TOF mass uncut to retain its sidebands. + if (passTPCTr && absPTr > tritonPid.settingTOFMassMomentumMinTr && track.hasTOF()) { + const float massTOFTr = computeTOFMass(track); + if (std::isfinite(massTOFTr)) { + mQaRegistry.fill(HIST("purity/h2MassTOFTr_preselection"), signedPtTr, massTOFTr); + } + } + } } const bool isHadronSelected = passTrackHad && selectionPIDHadron(track); From e0b8ca9db8e9351cc9f751afa613aa80999bdd36 Mon Sep 17 00:00:00 2001 From: amalietbclausen Date: Thu, 22 Oct 2026 10:51:26 +0200 Subject: [PATCH 384/648] [PWGCF] Addition of mult-axis to trigger hist (#18285) Co-authored-by: AmalieTBC --- .../TwoParticleCorrelations/Tasks/corrFit.cxx | 944 +++++++++++------- 1 file changed, 576 insertions(+), 368 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx index 9376c2fe182..cc82c070425 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,19 @@ using namespace o2::aod::rctsel; using namespace constants::math; #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; @@ -73,34 +87,37 @@ struct CorrFit { o2::aod::ITSResponse itsResponse; Service ccdb{}; - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgUseTransverseMomentum, bool, false, "Use transverse momentum for correlation container") O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, true, "Enable QA histograms for event selection") O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") - O2_DEFINE_CONFIGURABLE(cfgRefpTt, bool, false, "Apply upper pT cut on reference tracks") - O2_DEFINE_CONFIGURABLE(cfgRefpTMax, float, 3.0f, "maximum pT for reference tracks if cfgRefpTt is true") - O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") - O2_DEFINE_CONFIGURABLE(cfgRefMultiplicity, bool, false, "Use multiplicity of reference tracks for multiplicity correlation cut instead of Nch") + O2_DEFINE_CONFIGURABLE(cfgUseRefpTCut, bool, false, "Apply upper pT cut on reference tracks") + O2_DEFINE_CONFIGURABLE(cfgRefpTMax, float, 3.0f, "maximum pT for reference tracks if cfgUseRefpTCut is true") + O2_DEFINE_CONFIGURABLE(cfgRefpTMin, float, 0.2f, "minimum pT for reference tracks if cfgUseRefpTCut is true") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z")} cfgTrackCuts; - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy")} cfgEventSelection; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "DCAz range for tracks") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"(0.0026+0.005/(x^1.01))", "(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") O2_DEFINE_CONFIGURABLE(cfgMergingCut, float, 0.02, "Merging cut on track merge") @@ -114,7 +131,6 @@ struct CorrFit { O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -135,16 +151,20 @@ struct CorrFit { std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; @@ -200,8 +220,8 @@ struct CorrFit { AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; AxisSpec axisChID = {220, 0, 220}; - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgZVtxCut); - Filter trackFilter = (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaCut) && (cfgTrackCuts.cfgPtCutMin < aod::track::pt) && (cfgTrackCuts.cfgPtCutMax > aod::track::pt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; @@ -229,9 +249,12 @@ struct CorrFit { OutputObj mixedTPC{"mixedEvent_TPC"}; HistogramRegistry registry{"registry"}; + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum EventType { SameEvent = 1, @@ -239,63 +262,87 @@ struct CorrFit { }; enum FITIndex { - kFT0A = 0, - kFT0C = 1 + IndexFT0A = 0, + IndexFT0C = 1 }; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTof, + UseITS + }; + enum Stage { + Before = 0, + After }; enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseGoodITSLayer0123, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, // Count the total number of enum - kCount_CentEstimators + NumCentEstimators }; RCTFlagsChecker rctChecker{"CBT"}; @@ -309,77 +356,102 @@ struct CorrFit { ccdb->setCreatedNotAfter(now); LOGF(info, "Starting init"); - + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); if (doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) { registry.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{14, -0.5, 13.5}}}); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); } if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) && cfgQaCheck) { registry.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered events"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); } // Multiplicity correlation cuts - if (cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1] != 0) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); } - if (cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1] != 0) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + if (doprocessSameTPC || doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c) { registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); @@ -426,24 +498,24 @@ struct CorrFit { registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameTpcFt0c) { registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameFt0aFt0c) { registry.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); - registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } if (doprocessSameTPC) { registry.add("deltaEta_deltaPhi_same_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); - registry.add("Trig_hist_TPC", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("Trig_hist_TPC", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); } registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event @@ -511,20 +583,20 @@ struct CorrFit { } template - float getCentrality(TCollision const& collision) + double getCentrality(TCollision const& collision) { - float cent = 0.0; - switch (cfgCentEstimator) { - case kCentFT0C: + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -537,183 +609,206 @@ struct CorrFit { bool eventRct(TCollision const& collision, const bool fillCounter) { if (!rctChecker(collision)) { - if (fillCounter) + if (fillCounter) { registry.fill(HIST("hEventCountRct"), 0.5); + } - return 0; + return false; } - if (fillCounter) + if (fillCounter) { registry.fill(HIST("hEventCountRct"), 1.5); + } - return 1; + return true; } template bool eventSelected(TCollision const& collision, const int mult, const bool fillCounter) { - if (cfgUseEventCuts->getData()[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoTimeFrameBorder][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); - if (cfgUseEventCuts->getData()[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoITSROFrameBorder][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); - if (cfgUseEventCuts->getData()[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoSameBunchPileup][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoSameBunchPileup); - if (cfgUseEventCuts->getData()[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodZvtxFT0vsPV][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); - if (cfgUseEventCuts->getData()[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoCollInTimeRangeStandard][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } - if (cfgUseEventCuts->getData()[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodITSLayersAll][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodITSLayersAll); + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } - if (cfgUseEventCuts->getData()[kUseGoodITSLayer0123][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - return 0; + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (fillCounter && cfgUseEventCuts->getData()[kUseGoodITSLayer0123][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseGoodITSLayer0123); - if (cfgUseEventCuts->getData()[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoCollInRofStandard][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoCollInRofStandard); + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } - if (cfgUseEventCuts->getData()[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } - if (fillCounter && cfgUseEventCuts->getData()[kUseNoHighMultCollInPrevRof][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + registry.fill(HIST("hEventCount"), UseOccupancy); } - if (fillCounter && cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseOccupancy); - if (cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1]) { + if (eventCuts[UseMultCorrCut][EvCut1]) { float cent = getCentrality(collision); if (cfgFuncParas.cfgMultPVT0CCutEnabled) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) - return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) - return 0; + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultT0CCutEnabled) { - if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) - return 0; - if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) - return 0; + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) - return 0; + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } } } - if (fillCounter && cfgUseEventCuts->getData()[kUseMultCorrCut][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseMultCorrCut); + } // V0A T0A 5 sigma cut - if (cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && cfgUseEventCuts->getData()[kUseT0AV0ACut][kEvCut1]) - registry.fill(HIST("hEventCount"), kUseT0AV0ACut); + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseT0AV0ACut); + } - return 1; + return true; } template void eventSelectedIndividually(TCollision const& collision) { - registry.fill(HIST("hPassedEventSelection"), kFilteredEvents); + registry.fill(HIST("hPassedEventSelection"), FilteredEvents); if (collision.sel8()) { - registry.fill(HIST("hPassedEventSelection"), kAfterSel8); + registry.fill(HIST("hPassedEventSelection"), AfterSel8); } if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoTimeFrameBorder); + registry.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoITSROFrameBorder); + registry.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); } if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoSameBunchPileup); + registry.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); } if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodZvtxFT0vsPV); + registry.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); } if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInTimeRangeStandard); + registry.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayersAll); + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); } if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - registry.fill(HIST("hPassedEventSelection"), kUseGoodITSLayer0123); + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); } if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoCollInRofStandard); + registry.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); } if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - registry.fill(HIST("hPassedEventSelection"), kUseNoHighMultCollInPrevRof); + registry.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); } auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgUseEventCuts->getData()[kUseOccupancy][kEvCut1] && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) { - registry.fill(HIST("hPassedEventSelection"), kUseOccupancy); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + registry.fill(HIST("hPassedEventSelection"), UseOccupancy); } } @@ -759,7 +854,39 @@ struct CorrFit { template bool trackSelected(TTrack const& track) { - return ((track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgTrackCuts.cfgCutITSclu) && (track.tpcChi2NCl() < cfgTrackCuts.cfgCutChi2prTPCcls) && (track.dcaZ() < cfgTrackCuts.cfgCutDCAz)); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } template @@ -778,10 +905,12 @@ struct CorrFit { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) + } + if (dPhiStar < -constants::math::PI) { dPhiStar = -constants::math::TwoPI - dPhiStar; + } return dPhiStar; } @@ -814,21 +943,23 @@ struct CorrFit { template void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) { - if (fitType == kFT0C) { + if (fitType == IndexFT0C) { id = ft0.channelC()[iCh]; id = id + Ft0IndexA; ampl = ft0.amplitudeC()[iCh]; - if (system == SameEvent) + if (system == SameEvent) { registry.fill(HIST("FT0Amp"), id, ampl); + } ampl = ampl / cstFT0RelGain[id]; if (system == SameEvent) { registry.fill(HIST("FT0AmpCorrect"), id, ampl); } - } else if (fitType == kFT0A) { + } else if (fitType == IndexFT0A) { id = ft0.channelA()[iCh]; ampl = ft0.amplitudeA()[iCh]; - if (system == SameEvent) + if (system == SameEvent) { registry.fill(HIST("FT0Amp"), id, ampl); + } ampl = ampl / cstFT0RelGain[id]; if (system == SameEvent) { registry.fill(HIST("FT0AmpCorrect"), id, ampl); @@ -848,18 +979,26 @@ struct CorrFit { int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - int kIndexDetector = cfgPIDConfigs.cfgUseItsPID ? kITS : kTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS bool isPion = false; bool isKaon = false; bool isProton = false; - bool isDetectedPion = nSigmaToUse[iPionUp] < cfgPIDConfigs.nSigmas->getData()[iPionUp][kIndexDetector] && nSigmaToUse[iPionUp] > cfgPIDConfigs.nSigmas->getData()[iPionLow][kIndexDetector]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < cfgPIDConfigs.nSigmas->getData()[iKaonUp][kIndexDetector] && nSigmaToUse[iKaonUp] > cfgPIDConfigs.nSigmas->getData()[iKaonLow][kIndexDetector]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < cfgPIDConfigs.nSigmas->getData()[iProtonUp][kIndexDetector] && nSigmaToUse[iProtonUp] > cfgPIDConfigs.nSigmas->getData()[iProtonLow][kIndexDetector]; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; - bool isTofPion = nSigmaTOF[iPionUp] < cfgPIDConfigs.nSigmas->getData()[iPionUp][kTOF] && nSigmaTOF[iPionUp] > cfgPIDConfigs.nSigmas->getData()[iPionLow][kTOF]; - bool isTofKaon = nSigmaTOF[iKaonUp] < cfgPIDConfigs.nSigmas->getData()[iKaonUp][kTOF] && nSigmaTOF[iKaonUp] > cfgPIDConfigs.nSigmas->getData()[iKaonLow][kTOF]; - bool isTofProton = nSigmaTOF[iProtonUp] < cfgPIDConfigs.nSigmas->getData()[iProtonUp][kTOF] && nSigmaTOF[iProtonUp] > cfgPIDConfigs.nSigmas->getData()[iProtonLow][kTOF]; + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTof] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTof]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTof] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTof]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTof] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTof]; if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { return -1; @@ -878,21 +1017,25 @@ struct CorrFit { return -1; // more than one particle satisfy the criteria } - if (fillYields) - registry.fill(HIST("hParticleCounts"), kCharged); + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseCharged); + } if (isPion) { - pid = kPions; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kPions); + pid = UsePions; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UsePions); + } } else if (isKaon) { - pid = kKaons; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kKaons); + pid = UseKaons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseKaons); + } } else if (isProton) { - pid = kProtons; - if (fillYields) - registry.fill(HIST("hParticleCounts"), kProtons); + pid = UseProtons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseProtons); + } } else { return -1; // no particle satisfies the criteria } @@ -945,15 +1088,14 @@ struct CorrFit { { float effNch = 1.; if (mEfficiencyNch) { - int ptBin = mEfficiencyNch->FindBin(pt); effNch = mEfficiencyNch->GetBinContent(ptBin); - } else { effNch = 1.0; } - if (effNch == 0) + if (effNch == 0) { return false; + } weightNch = 1. / effNch; return true; } @@ -962,17 +1104,16 @@ struct CorrFit { { float eff = 1.; if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency int ptBin = mEfficiency->GetYaxis()->FindBin(pt); int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); - } else { eff = 1.0; } - if (eff == 0) + if (eff == 0) { return false; + } weight = 1. / eff; return true; } @@ -998,10 +1139,8 @@ struct CorrFit { double nTracksCorrected = 0; float weightNch = 1.0f; for (auto const& track : tracks) { - - if (cfgRefMultiplicity) { - if (track.pt() > cfgRefpTMax) - continue; + if (!trackSelectedForNch(track)) { + continue; } if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { @@ -1025,8 +1164,9 @@ struct CorrFit { if (!trackSelected(track1)) { continue; } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, true) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, true) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), zvtx)) { continue; @@ -1045,10 +1185,11 @@ struct CorrFit { void fillNsigmaBeforeCut(const TTrack& track1, int pid) // function to fill the QA before Nsigma selection { switch (pid) { - case kPions: // For Pions + case UsePions: // For Pions if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); @@ -1057,13 +1198,15 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } break; - case kKaons: // For Kaons + case UseKaons: // For Kaons if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); @@ -1072,13 +1215,16 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } break; - case kProtons: // For Protons + + case UseProtons: // For Protons if (!cfgPIDConfigs.cfgUseItsPID) { - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); @@ -1087,8 +1233,9 @@ struct CorrFit { registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); } } - if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } break; default: LOGF(info, "PID %d not recognized, skipping nSigma QA filling", pid); @@ -1100,7 +1247,7 @@ struct CorrFit { void fillNsigmaAfterCut(const TTrack& track1, int pid) // function to fill the QA after Nsigma selection { switch (pid) { - case kPions: // For Pions + case UsePions: // For Pions if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); @@ -1109,13 +1256,15 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } break; - case kKaons: // For Kaons + case UseKaons: // For Kaons if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); @@ -1124,13 +1273,15 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID) + if (cfgPIDConfigs.cfgUseItsPID) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } break; - case kProtons: // For Protons + case UseProtons: // For Protons if (!cfgPIDConfigs.cfgUseItsPID) { if (cfgPIDConfigs.cfgGetdEdx) { double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); @@ -1139,14 +1290,16 @@ struct CorrFit { registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); } - if (cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } } - if (cfgPIDConfigs.cfgUseItsPID && cfgPIDConfigs.cfgGetNsigmaQA) + if (cfgPIDConfigs.cfgUseItsPID && cfgPIDConfigs.cfgGetNsigmaQA) { registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } break; default: - LOGF(info, "PID %d not recognized, skipping nSigma QA filling", pid); + LOGF(info, "PID %d not recognized, skipping nSigma QA filling, breaking", pid); break; } // end of switch } @@ -1154,40 +1307,43 @@ struct CorrFit { template void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double multiplicity, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; // loop over all tracks for (auto const& track1 : tracks1) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; - - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + } + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { continue; + } if (system == SameEvent) { - if (corType == kFT0C) { - registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); - } else if (corType == kFT0A) { - registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + if (corType == IndexFT0C) { + registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); } } std::size_t channelSize = 0; - if (corType == kFT0C) { + if (corType == IndexFT0C) { channelSize = ft0.channelC().size(); - } else if (corType == kFT0A) { + } else if (corType == IndexFT0A) { channelSize = ft0.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", corType); @@ -1204,13 +1360,13 @@ struct CorrFit { float deltaEta = track1.eta() - eta; // fill the right sparse and histograms if (system == SameEvent) { - if (corType == kFT0A) { + if (corType == IndexFT0A) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { + } else if (corType == IndexFT0C) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); @@ -1218,13 +1374,13 @@ struct CorrFit { sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } } else if (system == MixedEvent) { - if (corType == kFT0A) { + if (corType == IndexFT0A) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); } mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); - } else if (corType == kFT0C) { + } else if (corType == IndexFT0C) { if (cfgQaCheck) { registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); @@ -1239,7 +1395,7 @@ struct CorrFit { template void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double multiplicity, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; std::size_t channelASize = ft0Col1.channelA().size(); @@ -1249,20 +1405,20 @@ struct CorrFit { int chanelAid = 0; float amplA = 0.; - getChannel(ft0Col1, iChA, chanelAid, amplA, kFT0A, system); - auto phiA = getPhiFT0(chanelAid, kFT0A); - auto etaA = getEtaFT0(chanelAid, kFT0A); + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); if (system == SameEvent) { - registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, eventWeight * amplA); + registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, multiplicity, eventWeight * amplA); } for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { int chanelCid = 0; float amplC = 0.; - getChannel(ft0Col2, iChC, chanelCid, amplC, kFT0C, system); - auto phiC = getPhiFT0(chanelCid, kFT0C); - auto etaC = getEtaFT0(chanelCid, kFT0C); + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); float deltaEta = etaA - etaC; @@ -1283,10 +1439,10 @@ struct CorrFit { } template - void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, double multiplicity, int magneticField) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, double multiplicity, int magneticField, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = static_cast(gRandom->Uniform(0.0, cfgSampleSize)); + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); float triggerWeight = 1.0f; @@ -1295,41 +1451,49 @@ struct CorrFit { // loop over all tracks for (auto const& track1 : tracks1) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; + } // Fill Nsigma QA - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { continue; // if PID is selected, check if the track has the right PID + } - if (cfgPIDConfigs.cfgPIDParticle > kCharged) + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } - if (!getEfficiencyCorrectionNch(triggerWeight, track1.pt())) + if (!getEfficiencyCorrectionNch(triggerWeight, track1.pt())) { continue; + } if (system == SameEvent) { - registry.fill(HIST("Trig_hist_TPC"), fSampleIndex, posZ, track1.pt(), triggerWeight); + registry.fill(HIST("Trig_hist_TPC"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); } for (auto const& track2 : tracks2) { - if (!trackSelected(track2)) + if (!trackSelected(track2)) { continue; + } - if (!getEfficiencyCorrectionNch(associateWeight, track2.pt())) + if (!getEfficiencyCorrectionNch(associateWeight, track2.pt())) { continue; + } - if (cfgRefpTt) { - if (track2.pt() > cfgRefpTMax) { + if (cfgUseRefpTCut) { + if (track2.pt() > cfgRefpTMax || track2.pt() < cfgRefpTMin) { continue; } } - if (track1.pt() <= track2.pt()) + if (track1.pt() <= track2.pt()) { continue; // skip if the trigger pt is less than the associate pt + } float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); float deltaEta = track1.eta() - track2.eta(); @@ -1355,8 +1519,9 @@ struct CorrFit { break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } @@ -1381,8 +1546,8 @@ struct CorrFit { bool isGoodRun(int runNumber) { - for (const auto& ExcludedRun : cfgRunRemoveList.value) { - if (runNumber == ExcludedRun) { + for (const auto& excludedRun : cfgRunRemoveList.value) { + if (runNumber == excludedRun) { return false; } } @@ -1396,25 +1561,30 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } - if (!collision.has_foundFT0()) + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); @@ -1427,7 +1597,7 @@ struct CorrFit { fillYield(collision, tracks); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1441,12 +1611,12 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } const auto& ft0 = collision.foundFT0(); - fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, kFT0A, eventWeight); + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0A, eventWeight); } PROCESS_SWITCH(CorrFit, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", false); @@ -1468,45 +1638,51 @@ struct CorrFit { for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } const auto& ft0 = collision2.foundFT0(); - fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, kFT0A, eventWeight); + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0A, eventWeight); } } PROCESS_SWITCH(CorrFit, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", false); @@ -1518,23 +1694,28 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; - if (!collision.has_foundFT0()) + } + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); @@ -1545,7 +1726,7 @@ struct CorrFit { const auto& ft0 = collision.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1559,11 +1740,11 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } - fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, kFT0C, 1.0f); + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0C, 1.0f); } PROCESS_SWITCH(CorrFit, processSameTpcFt0c, "Process same event for TPC-FT0C correlation", false); @@ -1584,44 +1765,49 @@ struct CorrFit { Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; const auto& ft0 = collision2.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } - fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, kFT0C, eventWeight); + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0C, eventWeight); } } PROCESS_SWITCH(CorrFit, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); @@ -1633,24 +1819,29 @@ struct CorrFit { eventSelectedIndividually(collision); } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } - if (!collision.has_foundFT0()) + if (!collision.has_foundFT0()) { return; + } loadAlignParam(bc.timestamp()); loadGain(bc); @@ -1663,7 +1854,7 @@ struct CorrFit { registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1677,7 +1868,7 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } @@ -1704,25 +1895,31 @@ struct CorrFit { auto& [collision1, tracks1, collision2, tracks2] = *it; // should have the same event to TPC-FT0A/C correlations - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { continue; + } auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); @@ -1731,13 +1928,13 @@ struct CorrFit { const auto& ft0Col1 = collision1.foundFT0(); const auto& ft0Col2 = collision2.foundFT0(); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } @@ -1754,28 +1951,34 @@ struct CorrFit { if (cfgQaCheck) { eventSelectedIndividually(collision); } - if (!eventRct(collision, true)) + if (!eventRct(collision, true)) { return; + } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } auto bc = collision.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number return; + } } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { return; + } + + registry.fill(HIST("zVtx"), collision.posZ()); registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin loadCorrection(bc.timestamp()); fillYield(collision, tracks); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks.size()); if (cfgQaCheck) { registry.fill(HIST("Nch"), multiplicity); @@ -1789,11 +1992,11 @@ struct CorrFit { registry.fill(HIST("Nch_corrected"), multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { return; } - fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, multiplicity, getMagneticField(bc.timestamp())); + fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, multiplicity, getMagneticField(bc.timestamp()), 1.0f); } PROCESS_SWITCH(CorrFit, processSameTPC, "Process same event for TPC-TPC correlation", false); @@ -1815,39 +2018,44 @@ struct CorrFit { for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!eventRct(collision1, false) || !eventRct(collision2, false)) + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { continue; + } - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { continue; + } registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); int currentRunNumber = bc.runNumber(); if (!cfgRunRemoveList.value.empty()) { - if (!isGoodRun(currentRunNumber)) // Rejects runs if bad run number + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number continue; + } } loadCorrection(bc.timestamp()); - double multiplicity = static_cast(tracks.size()); + auto multiplicity = static_cast(tracks1.size()); if (cfgStrictTrackCounter) { trackCounter(tracks1, multiplicity); } - if (multiplicity > cfgMaxMultForCorrelations || multiplicity < cfgMinMultForCorrelations) { + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { continue; } - fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, multiplicity, getMagneticField(collision1.bc_as().timestamp())); + fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, multiplicity, getMagneticField(collision1.bc_as().timestamp()), 1.0f); } } PROCESS_SWITCH(CorrFit, processMixedTPC, "Process mixed events for TPC-TPC correlation", false); From 61525931fd010003281c459ab10215c040d37279 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:03:24 +0200 Subject: [PATCH 385/648] [PWGCF] Add additional MC response histograms in twoParticleCorrelationsMpi.cxx (#18396) --- .../Tasks/twoParticleCorrelationsMpi.cxx | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx index e7f5d4e2f9f..d0f62249d26 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -293,6 +293,12 @@ struct TwoParticleCorrelationsMpi { float weight; }; + struct DetectorClosureResponseCoordinates { + float trueMultiplicity; + float rawMultiplicity; + float correctedMultiplicity; + }; + std::vector yieldTemplates; std::vector> pairAcceptanceMaps; std::vector> pairAcceptanceEtaVertexMaps; @@ -466,6 +472,10 @@ struct TwoParticleCorrelationsMpi { if (doprocessMCDetectorClosure) { registry.add("detectorClosure/rawMultiplicityVsTrue", "raw reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{raw,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); registry.add("detectorClosure/correctedMultiplicityVsTrue", "corrected reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{corr,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/triggerResponseRaw", "efficiency-weighted trigger response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/triggerResponseCorrected", "efficiency-weighted trigger response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/pairResponseRaw", "efficiency-weighted pair response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); + registry.add("detectorClosure/pairResponseCorrected", "efficiency-weighted pair response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); registry.add("detectorClosure/profileCorrectedMultiplicityVsTrue", "mean corrected reconstructed multiplicity versus true multiplicity;N_{ch}^{true};#LT N_{ch}^{corr,reco} #GT", {HistType::kTProfile, {axisMultiplicity}}); registry.add("detectorClosure/profileTrueMultiplicityVsCorrected", "mean true multiplicity versus corrected reconstructed multiplicity;N_{ch}^{corr,reco};#LT N_{ch}^{true} #GT", {HistType::kTProfile, {axisMultiplicity}}); registry.add("detectorClosure/recoCollisionsPerMCCollision", "accepted reconstructed collisions per MC collision;N_{coll}^{reco};events", {HistType::kTH1F, {{11, -0.5, 10.5}}}); @@ -1774,7 +1784,7 @@ struct TwoParticleCorrelationsMpi { } template - void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true, float efficiencyMultiplicity = -1.f) + void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true, float efficiencyMultiplicity = -1.f, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) { if (multiplicity < 0.f) { return; @@ -1851,6 +1861,10 @@ struct TwoParticleCorrelationsMpi { if (cfg.mEfficiencyTrigger) { triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), efficiencyLookupMultiplicity, posZ); } + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/triggerResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, track1.pt(), triggerWeight); + registry.fill(HIST("detectorClosure/triggerResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, track1.pt(), triggerWeight); + } } if (cfgUserAxis == EventSeedAxis) { @@ -1975,6 +1989,13 @@ struct TwoParticleCorrelationsMpi { float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); const float deltaEta = track1.eta() - track2.eta(); + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/pairResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + registry.fill(HIST("detectorClosure/pairResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + } + } + if (triggerHasTemplate) { ++seedEstimate->nCandidatePairs; if (const auto* yieldTemplate = findYieldTemplate(multiplicity, track1.pt(), track2.pt())) { @@ -2187,7 +2208,7 @@ struct TwoParticleCorrelationsMpi { PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameGenMC, "Process generated MC events from derived data and validate against the stored HepMC N MPI", false); template - void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr, const float* outputMultiplicityOverride = nullptr, const float* efficiencyMultiplicityOverride = nullptr) + void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr, const float* outputMultiplicityOverride = nullptr, const float* efficiencyMultiplicityOverride = nullptr, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) { auto getMultiplicity = [](const auto& col) { return getAnalysisMultiplicity(col); @@ -2248,14 +2269,14 @@ struct TwoParticleCorrelationsMpi { flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } else if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills, -1.0, true, true, efficiencyMultiplicity); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); finalizeEventSeedEstimate(seedEstimate); flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); } if (fillReco && hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds()), true, true, efficiencyMultiplicity); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds()), true, true, efficiencyMultiplicity, detectorClosureResponse); } const auto seedClass = getEventSeedClass(multiplicity, seedEstimate.nuncSeeds()); fillOutputContainers(multiplicity, fRndm->Rndm(), seedClass); @@ -2272,7 +2293,7 @@ struct TwoParticleCorrelationsMpi { } if (hasEfficiency) { fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); - fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate, nullptr, nullptr, -1.0, true, true, efficiencyMultiplicity); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate, nullptr, nullptr, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); } finalizeEventSeedEstimate(seedEstimate); fillOutputContainers(multiplicity, fRndm->Rndm()); @@ -2605,9 +2626,10 @@ struct TwoParticleCorrelationsMpi { registry.fill(HIST("detectorClosure/profileTrueMultiplicityVsCorrected"), correctedMultiplicity, trueMultiplicity); auto collisionTracks = tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex()); + const DetectorClosureResponseCoordinates responseCoordinates{trueMultiplicity, rawMultiplicity, correctedMultiplicity}; // Physics histograms use true multiplicity, while efficiency weights use // the reconstructed multiplicity coordinate for which the map was made. - processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI, &trueMultiplicity, &rawMultiplicity); + processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI, &trueMultiplicity, &rawMultiplicity, &responseCoordinates); } } PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCDetectorClosure, "Fill generated, raw reconstructed, and efficiency-corrected MC correlations versus true charged multiplicity", false); From edd6d7505881d18f54077e6327817d46fdd2d6e4 Mon Sep 17 00:00:00 2001 From: vojmach <137265951+vojmach@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:15:53 +0200 Subject: [PATCH 386/648] [PWGCF] Time-dependent QA histogram added (#18380) Co-authored-by: Vojtech Machacek --- PWGCF/Flow/Tasks/flowTransmutation.cxx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PWGCF/Flow/Tasks/flowTransmutation.cxx b/PWGCF/Flow/Tasks/flowTransmutation.cxx index c7d00adb5b5..fa0fe8210f4 100644 --- a/PWGCF/Flow/Tasks/flowTransmutation.cxx +++ b/PWGCF/Flow/Tasks/flowTransmutation.cxx @@ -107,6 +107,7 @@ struct FlowTransmutation { } cfgMultiplicityCorrelationCut; O2_DEFINE_CONFIGURABLE(cfgNUAWeightsRunByRun, bool, true, "Fill NUA weights run by run") O2_DEFINE_CONFIGURABLE(cfgGetNumberOfCorr, bool, false, "Get histograms with number of correlations") + Configurable> cfgFirstFillRunsList{"cfgFirstFillRunsList", std::vector{}, "First run of the fill for time-dependent analysis, keep empty to disable"}; O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "Maximal pT for all tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for all tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 3.0f, "Maximal pT for poi tracks") @@ -117,6 +118,7 @@ struct FlowTransmutation { Configurable> cfgRemovedRunList{"cfgRemovedRunList", std::vector{-1}, "excluded run numbers"}; Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAxy (Nsigma)", "DCAz (Nsigma)", "DCAz (cm) override"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; ConfigurableAxis binningNch{"binningNch", {500, 0, 500}, "binning for N_{ch} histograms"}; + ConfigurableAxis binningTime{"binningTime", {12, 0, 12}, "binning for time-dependent histograms transmutation QA"}; ConfigurableAxis binningPt{"binningPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "binning for pT histograms"}; HistogramRegistry registryQA{"registryQA"}; HistogramRegistry registryCorr{"registryCorr"}; @@ -215,7 +217,7 @@ struct FlowTransmutation { std::array fDCAxyCut{}; std::vector runNumbers; std::map> th3sPerRun; - int lastRun = -1; + int firstRunOfFill = -1; int getCorrectITSLayersFlag(int flag) { switch (flag) { @@ -395,6 +397,10 @@ struct FlowTransmutation { ccdb->setCaching(true); auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); + if (!cfgFirstFillRunsList.value.empty()) { + registryQA.add("multT0A_multT0C", "FT0A-C multiplicity correlation;N_{ch}(FT0A);N_{ch}(FT0C);t[h]", {HistType::kTH3D, {axisT0A, axisT0C, binningTime}}); + firstRunOfFill = *std::min_element(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end()); + } } template float getCentrality(const TCollision& collision) @@ -562,6 +568,12 @@ struct FlowTransmutation { if (cfgEventSelection.cfgEvSelUsage) { fillEventQA(collision, tracks); } + if (!cfgFirstFillRunsList.value.empty()) { + if (std::find(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end(), run) != cfgFirstFillRunsList.value.end() && firstRunOfFill != run) { + firstRunOfFill = run; + } + registryQA.fill(HIST("multT0A_multT0C"), collision.multFT0A(), collision.multFT0C(), getTimeSinceStartOfFill(bc.timestamp(), firstRunOfFill)); + } registryQA.fill(HIST("hVtxZ"), vtxz); registryQA.fill(HIST("hCentrality"), cent); if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { From 2f3ffe9cfb8aa30df935eb1d81b7fde94e31621b Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Thu, 22 Oct 2026 18:08:32 +0800 Subject: [PATCH 387/648] [PWGCF] Fix eta gap calculation and improvement on cut counters (#18404) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 616 ++++++++---------- 1 file changed, 279 insertions(+), 337 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 74654d087f8..02329c116d6 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -105,19 +105,15 @@ enum EMultiplicityTables { eMultiplicityTables_N }; -enum ETechnicalCuts { - eNoCollInTimeRangeStandard = 0, +enum EEventCuts { + eSel8Cut = 0, + eNoCollInTimeRangeStandard, eNoCollInRofStandard, eNoSameBunchPileUp, eIsVertexITSTPC, eIsGoodITSLayersAll, eIsGoodZvtxFT0vsPV, eNoHighMultCollInPrevRof, - ETechnicalCuts_N -}; - -enum EEventCuts { - eSel8Cut = 0, eCentCut, eVertexZCut, eNContribCut, @@ -182,9 +178,7 @@ static constexpr std::array WeightsNames = {" static constexpr std::array CutsNames = {"eBefore", "eAfter"}; -static constexpr std::array TechnicalCutsNames = {"NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof"}; - -static constexpr std::array EventCutsNames = {"Sel8", "Cent", "VertexZ", "NContrib"}; +static constexpr std::array EventCutsNames = {"Sel8", "NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof", "Cent", "VertexZ", "NContrib"}; static constexpr std::array ParticleCutsNames = {"Pt", "Eta", "Charge"}; @@ -211,7 +205,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Binnings Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; Configurable> cfMultBins{"cfMultBins", {400, 0., 25000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; - Configurable> cfMultBinsRef{"cfMultBinsRef", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; + Configurable> cfRefMultBins{"cfRefMultBins", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; Configurable> cfContribBins{"cfContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; @@ -224,24 +218,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Cuts Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; - // technical cuts - Configurable> cfTechnicalCutSwitch{"cfTechnicalCutSwitch", {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}, "technical cuts switch, on and off by the first number before name"}; // event level cuts - Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "switch to apply Sel8 (event selection decision based on TVX) cut"}; - Configurable cfVertexZCutSwitch{"cfVertexZCutSwitch", true, "switch to apply VertexZ cut"}; + Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1Cent", "1VertexZ", "1NContrib"}, "event cuts switch, on and off by the first number before name"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; - Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "switch to apply centrality cut"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; - Configurable cfNContribCutSwitch{"cfNContribCutSwitch", true, "switch to apply NContrib cut"}; Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; // particle level cuts - Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "switch to apply pt cut"}; + Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge"}, "particle cuts switch, on and off by the first number before name"}; Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}[GeV/c], with convention: min <= pt <= max"}; - Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "switch to apply pt cut"}; Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; - Configurable cfChargeCutSwitch{"cfChargeCutSwitch", true, "switch to apply charge cut (cut neutral particle out)"}; // *) Misc Configurable cfEtaGap{"cfEtaGap", 1.0, "eta gap for two particle correlation"}; @@ -255,11 +242,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to float fSigmaInel = 7.71; float fEtaGap = 1.0; - std::vector fTechnicalCutSwitch = {"1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof"}; - std::vector fEventCutSwitch = std::vector(EEventCuts_N, true); + std::vector fEventCutSwitch = {"1Sel8", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1Cent", "1VertexZ", "1NContrib"}; std::vector fVertexZCutRange = {-10., 10.}; std::vector fCentCutRange = {0., 80.}; - std::vector fParticleCutSwitch = std::vector(EParticleCuts_N, true); + std::vector fParticleCutSwitch = {"1Pt", "1Eta", "1Charge"}; std::vector fPtCutRange = {0.2, 5.}; std::vector fEtaCutRange = {-0.8, 0.8}; @@ -310,12 +296,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TProfile* fMiscHistRunNumber = nullptr; TProfile* fMiscEventCuts = nullptr; TProfile* fMiscParticleCuts = nullptr; - TH1D* fMiscEventCutsCounter = nullptr; - TH1D* fMiscParticleCutsCounter = nullptr; + std::array fMiscEventCutsCounter{}; //! [reco,sim] + std::array fMiscParticleCutsCounter{}; //! [reco,sim] } misc; // *) functions and templates - std::vector> calculateQvectors(int maxCorrelator, const std::vector& harmonic, const std::vector& angles, const std::vector& weights) + std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { int sum = 0; for (const int& x : harmonic) { @@ -324,21 +310,33 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const int maxHarmonic = sum + 1; // rows const int maxPower = maxCorrelator + 1; // cols - // a) Make sure all Q-vectors are initially zero: - std::vector> qVector(maxHarmonic, std::vector(maxPower, TComplex(0., 0.))); - - // b) Calculate Q-vectors for available angles and weights: - const int nParticles = angles.size(); - for (int i = 0; i < nParticles; ++i) { // loop over particles - const auto phi = static_cast(angles[i]); - for (int h = 0; h < maxHarmonic; ++h) { - for (int p = 0; p < maxPower; ++p) { - const float wp = std::pow(static_cast(weights[i]), p); - qVector[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); - } + return std::vector>(maxHarmonic, std::vector(maxPower, TComplex(0., 0.))); + } + + template + void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 const& weight) + { + const int maxHarmonic = QVectorsTable.size(); + const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); + for (int h = 0; h < maxHarmonic; ++h) { + for (int p = 0; p < maxPower; ++p) { + const auto wp = std::pow(weight, p); + QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); } } - return qVector; + } + + template + void updateVecForGap(std::vector const& harmonic, std::vector& qVecAB, std::vector& weightAB, T1 const& phi, T1 const& eta, T1 const& weight) + { + if (eta < -tc.fEtaGap / 2.f) { + qVecAB[0] += TComplex(weight * std::cos(harmonic[0] * phi), weight * std::sin(harmonic[0] * phi)); + weightAB[0] += weight; + } + if (eta > tc.fEtaGap / 2.f) { + qVecAB[1] += TComplex(weight * std::cos(harmonic[1] * phi), weight * std::sin(harmonic[1] * phi)); + weightAB[1] += weight; + } } std::vector two(const std::vector>& QVectorsTable, const std::vector& harmonic) @@ -358,22 +356,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return {n, d}; } - float twoCorrWithGap(std::vector const& harmonic, std::vector const& vecPhi, std::vector const& vecWeights, std::vector const& vecEta) - { - const int nParticles = vecPhi.size(); - float n = 0.f; - float d = 0.f; - for (int a = 0; a <= nParticles; ++a) { - for (int b = 0; b < nParticles; ++b) { - if (a != b && std::fabs(vecEta[a] - vecEta[b]) > tc.fEtaGap) { - n += vecWeights[a] * vecWeights[b] * std::cos(harmonic[0] * vecPhi[a] + harmonic[1] * vecPhi[b]); - d += vecWeights[a] * vecWeights[b]; - } - } - } - return n / d; - } - std::vector recursion(int m, const std::vector>& qVector, std::vector& harmonic, int mult = 1, int skip = 0) { // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by @@ -428,8 +410,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TObject* getObjectFromList(TList* list, const char* objectName) { - // Get TObject pointer from TList, even if it's in some nested TList. Foreseen - // to be used to fetch histograms or profiles from files directly. + // Get TObject pointer from TList, even if it's in some nested TList. Foreseen to be used to fetch histograms or profiles from files directly. // Some ideas taken from TCollection::ls() // If you have added histograms directly to files (without TList's), then you can fetch them directly with // file->Get("hist-name"). @@ -469,6 +450,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return nullptr; } // TObject* getObjectFromList(TList *list, char *objectName) + template void getHistogramWithWeights(const char* filePath, const char* runNumber) { // *) Return value: @@ -499,16 +481,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } weightsFile->GetObject("ccdb_object", baseList); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } } else if (bFileIsInCCDB) { // File you want to access is in your home dir in CCDB: Remember that here I do not access the file; instead, I directly access the object in that file. ccdb->setURL("https://alice-ccdb.cern.ch"); baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } } else { // this is the local case: // Check if the external ROOT file exists at the specified path: @@ -521,12 +497,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(fatal, "\033[1;31m%s at line %d\033[0m can't open file", __FUNCTION__, __LINE__); } weightsFile->GetObject("ccdb_object", baseList); - if (!baseList) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); - } } // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d for case %s\033[0m", __FUNCTION__, __LINE__, filePath); + } listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; @@ -539,6 +515,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } for (int i = 0; i < eWeightsHistograms_N; ++i) { + if constexpr (rs == eRec) { + if (i == ePhiSim) { + continue; + } + } histW[i] = dynamic_cast(listWithRuns->FindObject(Form("[%s]", WeightsNames[i]))); if (!histW[i]) { LOGF(warning, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, WeightsNames[i], runNumber); @@ -550,7 +531,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(warning, "%s: failed to clone histogram '%s' with run number %s", __FUNCTION__, WeightsNames[i], runNumber); continue; } - ex.fWeights[i]->SetTitle(tc.fFileWithWeights.c_str()); + ex.fWeights[i]->SetTitle(filePath); ex.fWeights[i]->SetDirectory(nullptr); ex.fExternalHistogramsList->Add(ex.fWeights[i]); } @@ -598,67 +579,44 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool eventCuts(T1 const& collision) { - if (tc.fTechnicalCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eNoCollInTimeRangeStandard) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eNoCollInRofStandard) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eNoSameBunchPileUp) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eIsVertexITSTPC) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eIsGoodITSLayersAll) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eIsGoodZvtxFT0vsPV) + 0.5, 1); - return false; - } - if (tc.fTechnicalCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - misc.fMiscEventCutsCounter->Fill(static_cast(eNoHighMultCollInPrevRof) + 0.5, 1); - return false; + std::vector doNotPassCut(EEventCuts_N, false); + + doNotPassCut[eSel8Cut] = tc.fEventCutSwitch[eSel8Cut].starts_with("1") && !collision.sel8(); + doNotPassCut[eNoCollInTimeRangeStandard] = tc.fEventCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); + doNotPassCut[eNoCollInRofStandard] = tc.fEventCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard); + doNotPassCut[eNoSameBunchPileUp] = tc.fEventCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + doNotPassCut[eIsVertexITSTPC] = tc.fEventCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); + doNotPassCut[eIsGoodITSLayersAll] = tc.fEventCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + doNotPassCut[eIsGoodZvtxFT0vsPV] = tc.fEventCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + doNotPassCut[eNoHighMultCollInPrevRof] = tc.fEventCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); + doNotPassCut[eNContribCut] = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); + + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + doNotPassCut[eVertexZCut] = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + doNotPassCut[eCentCut] = tc.fEventCutSwitch[eCentCut].starts_with("1") && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]); } - if constexpr (rs == eRecAndSim && rm == eMC) { + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { if (!collision.has_mcCollision()) { return false; } - } - if (tc.fEventCutSwitch[eSel8Cut] && !collision.sel8()) { // sel8 cut - misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eSel8Cut) + 0.5, 1); - return false; - } - if constexpr (rm == eReal) { - if (tc.fEventCutSwitch[eVertexZCut] && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0])) { // vertex z cut - misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eVertexZCut) + 0.5, 1); - return false; - } - auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - if (tc.fEventCutSwitch[eCentCut] && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0])) { // centrality cut - misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eCentCut) + 0.5, 1); - return false; - } - if (tc.fEventCutSwitch[eNContribCut] && (collision.numContrib() < tc.fNContribCutInf)) { // number of contribution cuts - misc.fMiscEventCutsCounter->Fill(static_cast(ETechnicalCuts_N) + static_cast(eNContribCut) + 0.5, 1); - return false; - } - } - if constexpr (rs == eRecAndSim && rm == eMC) { // event level cuts for Sim auto thisMCCollision = collision.mcCollision(); auto impactParameter = thisMCCollision.impactParameter(); auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; - if (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]) { // vertex z cut - return false; - } - if (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]) { // centrality cut + + doNotPassCut[eVertexZCut] = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]); + doNotPassCut[eCentCut] = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); + } + + for (int i = 0; i < EEventCuts_N; ++i) { + if (doNotPassCut[i]) { + if constexpr (rm == eReal) { + misc.fMiscEventCutsCounter[eRec]->Fill(i + 0.5, 1); + } + if constexpr (rm == eMC) { + misc.fMiscEventCutsCounter[eSim]->Fill(i + 0.5, 1); + } return false; } } @@ -669,118 +627,99 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void eventHistFill(T1 const& collision, T2 const& tracks) { - if constexpr (rs == eRec || rs == eRecAndSim) { - // Fill reconstructed-level event histograms - if constexpr (rm == eReal) { - auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); - int multiplicityRec = static_cast(tracks.size()); - if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); - ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); - ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); - ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); - ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); - } - if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); - ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); - ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); - ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); - ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); - } + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { // Fill reconstructed-level event histograms + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + int multiplicityRec = static_cast(tracks.size()); + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); + } + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); + ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); } - - // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed - if constexpr (rs == eRecAndSim && rm == eMC) { - if (!collision.has_mcCollision()) { - return; - } - auto thisMCCollision = collision.mcCollision(); - int multiplicitySim = static_cast(tracks.size()); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter - if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); - } - if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); - ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); - ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); - ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); - ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); - ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); - } - } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } + + // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed + if constexpr ((rs == eRecAndSim || rs == eSim) && rm == eMC) { + if (!collision.has_mcCollision()) { + return; + } + auto thisMCCollision = collision.mcCollision(); + int multiplicitySim = static_cast(tracks.size()); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); + } + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } template bool particleCuts(T const& track) { - if constexpr (rs == eRecAndSim && rm == eMC) { + std::vector doNotPassCut(EParticleCuts_N, false); + + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + doNotPassCut[ePtCut] = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]); + doNotPassCut[eEtaCut] = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]); + doNotPassCut[eChargeCut] = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); + } // end of if constexpr (rm == eReal) { + + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { if (!track.has_mcParticle()) { return false; } - } - if constexpr (rs == eRec || rs == eRecAndSim) { - if (tc.fParticleCutSwitch[ePtCut]) { // pt cuts for Rec - if constexpr (rm == eReal) { - if (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]) { - misc.fMiscParticleCutsCounter->Fill(static_cast(ePtCut) + 0.5, 1); - return false; - } - } - if constexpr (rs == eRecAndSim && rm == eMC) { // pt cuts for Sim - auto thisMCParticle = track.mcParticle(); - if (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]) { - return false; + auto thisMCParticle = track.mcParticle(); + doNotPassCut[ePtCut] = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]); + doNotPassCut[eEtaCut] = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]); + if (tc.fParticleCutSwitch[eChargeCut].starts_with("1")) { + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + if (particlePDG) { + const int chargeMCUnit = 3; + auto chargeMC = particlePDG->Charge() / chargeMCUnit; + if (chargeMC != 1 && chargeMC != -1) { + doNotPassCut[eChargeCut] = true; } + } else { + doNotPassCut[eChargeCut] = true; } - } - if (tc.fParticleCutSwitch[eEtaCut]) { - if constexpr (rm == eReal) { // eta cuts for Rec - if (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]) { - misc.fMiscParticleCutsCounter->Fill(static_cast(eEtaCut) + 0.5, 1); - return false; - } + } // end of if (tc.fParticleCutSwitch[eChargeCut]) { + } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + + for (int i = 0; i < EParticleCuts_N; ++i) { + if (doNotPassCut[i]) { + if constexpr (rm == eReal) { + misc.fMiscParticleCutsCounter[eRec]->Fill(i + 0.5, 1); } - if constexpr (rs == eRecAndSim && rm == eMC) { // eta cuts for Sim - auto thisMCParticle = track.mcParticle(); - if (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]) { - return false; - } + if constexpr (rm == eMC) { + misc.fMiscParticleCutsCounter[eSim]->Fill(i + 0.5, 1); } + return false; } - if (tc.fParticleCutSwitch[eChargeCut]) { - if constexpr (rm == eReal) { // charge cuts for Rec - if (track.sign() != 1 && track.sign() != -1) { - misc.fMiscParticleCutsCounter->Fill(static_cast(eChargeCut) + 0.5, 1); - return false; - } - } - if constexpr (rs == eRecAndSim && rm == eMC) { // charge cuts for Sim - auto thisMCParticle = track.mcParticle(); - TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); - if (particlePDG) { - const int chargeMCUnit = 3; - auto chargeMC = particlePDG->Charge() / chargeMCUnit; - if (chargeMC != 1 && chargeMC != -1) { - return false; - } - } else { - return false; - } - } // end of if constexpr (rs == eRecAndSim && rm == eMC) // charge cuts for Sim - } // end of if (tc.fParticleCutSwitch[eChargeCut]) // charge cuts for Rec - } // end of if constexpr (rs == eRec || rs == eRecAndSim) { + } return true; } @@ -788,52 +727,50 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void particleHistFill(T1 const& track) { - if constexpr (rs == eRec || rs == eRecAndSim) { - if constexpr (rm == eReal) { - if constexpr (cuts == eBefore) { - pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); - pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); - pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); - pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); - } - if constexpr (cuts == eAfter) { - pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); - pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); - pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); - pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); - } + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); } + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); + } + } - if constexpr (rs == eRecAndSim && rm == eMC) { - if (!track.has_mcParticle()) { - return; - } - auto thisMCParticle = track.mcParticle(); - TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); - float chargeMC = 0.f; - const int chargeMCUnit = 3; + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return; + } + auto thisMCParticle = track.mcParticle(); + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + float chargeMC = 0.f; + const int chargeMCUnit = 3; + if (particlePDG) { + chargeMC = particlePDG->Charge(); + chargeMC /= chargeMCUnit; + } + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(thisMCParticle.eta()); if (particlePDG) { - chargeMC = particlePDG->Charge(); - chargeMC /= chargeMCUnit; + pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); } - if constexpr (cuts == eBefore) { - pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(thisMCParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(thisMCParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(thisMCParticle.eta()); - if (particlePDG) { - pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); - } - } - if constexpr (cuts == eAfter) { - pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); - pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); - pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(thisMCParticle.eta()); - if (particlePDG) { - pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); - } + } + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(thisMCParticle.eta()); + if (particlePDG) { + pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); } - } // end of if constexpr (rs == eRecAndSim && rm == eMC) { - } + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } // end of void particleHistFill(T1 const& track) template @@ -887,7 +824,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); // Get run number misc.fMiscHistRunNumber->Fill(0.5, thisRunNumber); if (tc.fExternalFileSwitch) { - getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", thisRunNumber)); + getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", thisRunNumber)); } } @@ -924,70 +861,70 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - std::vector vecPhiReal{}; - std::vector vecPhiMC{}; - std::vector vecWeightsReal{}; - std::vector vecWeightsMC{}; - std::vector vecEtaReal{}; - std::vector vecEtaMC{}; + std::vector n22 = {-2, 2}; + + auto qVectorsTableReal = initQVectorsTable(2, n22); + auto qVectorsTableMC = initQVectorsTable(2, n22); + + std::vector qVecABReal(2, TComplex(0., 0.)); + std::vector qVecABMC(2, TComplex(0., 0.)); + std::vector weightABReal(2, 0.f); + std::vector weightABMC(2, 0.f); // Main loop over particles: for (auto const& track : tracks) { - particleHistFill(track); - particleHistFill(track); - float thisPhiRec = track.phi(); - float thisPhiSim = 0.f; float thisPt = track.pt(); - float thisEtaRec = track.eta(); - float thisEtaSim = 0.f; - - bool hasMCP = false; - if constexpr (rs == eRecAndSim) { - hasMCP = track.has_mcParticle(); - if (hasMCP) { - thisPhiSim = track.mcParticle().phi(); - thisEtaSim = track.mcParticle().eta(); - } - } - std::array thisPhiAndPt = {thisPhiRec, thisPhiSim, thisPt}; + std::array thisPhiAndPt = {thisPhiRec, 0.f, thisPt}; // {thisPhiRec, thisPhiSim, thisPt} std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} - if (tc.fExternalFileSwitch) { - for (int k = 0; k < eWeightsHistograms_N; ++k) { - if (ex.fWeights[k]) { - thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { + particleHistFill(track); + + if (tc.fExternalFileSwitch) { + for (int k = 0; k < eWeightsHistograms_N; ++k) { + if (k != ePhiSim && ex.fWeights[k]) { + thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); + } } } + auto thisWeightReal = thisWeights[ePhiRec] * thisWeights[ePt]; + + updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeightReal); + auto thisEtaRec = track.eta(); + updateVecForGap(n22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); } - if (tc.fMasterCutSwitch) { - if (passEventCutsReal && particleCuts(track)) { - particleHistFill(track); - vecPhiReal.push_back(track.phi()); - vecWeightsReal.push_back(thisWeights[ePhiRec] * thisWeights[ePt]); - vecEtaReal.push_back(thisEtaRec); - } - if (passEventCutsReal && particleCuts(track)) { + if constexpr (rs == eRecAndSim) { + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { particleHistFill(track); - if (hasMCP) { - vecPhiMC.push_back(thisPhiSim); - vecWeightsMC.push_back(thisWeights[ePhiSim] * thisWeights[ePt]); - vecEtaMC.push_back(thisEtaSim); + + auto thisPhiSim = track.mcParticle().phi(); + auto thisEtaSim = track.mcParticle().eta(); + thisPhiAndPt[ePhiSim] = thisPhiSim; + if (tc.fExternalFileSwitch && ex.fWeights[ePhiSim]) { + thisWeights[ePhiSim] = ex.fWeights[ePhiSim]->GetBinContent(ex.fWeights[ePhiSim]->FindBin(thisPhiSim)); } + auto thisWeightSim = thisWeights[ePhiSim] * thisWeights[ePt]; + + updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeightSim); + updateVecForGap(n22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); } } - } // end of for (int64_t nTrack = 0; nTrack < tracks.size(); ++nTrack) { - - std::vector n22 = {-2, 2}; - auto qVectorsTableReal = calculateQvectors(2, n22, vecPhiReal, vecWeightsReal); - auto qVectorsTableMC = calculateQvectors(2, n22, vecPhiMC, vecWeightsMC); + } // end of for (auto const& track : tracks) { std::vector result22(2, TComplex(0., 0.)); + std::vector result22Gap(2, 0.f); if (tc.fMasterCutSwitch) { if (passEventCutsReal) { - obs.fProfTwoGap[eRec]->Fill(thisCollCentReal, twoCorrWithGap(n22, vecPhiReal, vecWeightsReal, vecEtaReal)); + result22Gap[0] = (qVecABReal[0] * qVecABReal[1]).Re(); + result22Gap[1] = weightABReal[0] * weightABReal[1]; + if (result22Gap[1] != 0) { + obs.fProfTwoGap[eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); + } result22 = two(qVectorsTableReal, n22); if (noneZeroDenom(result22)) { @@ -997,7 +934,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr (rs == eRecAndSim) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; - obs.fProfTwoGap[eSim]->Fill(thisCollCentMC, twoCorrWithGap(n22, vecPhiMC, vecWeightsMC, vecEtaMC)); + + result22Gap[0] = (qVecABMC[0] * qVecABMC[1]).Re(); + result22Gap[1] = weightABMC[0] * weightABMC[1]; + if (result22Gap[1] != 0) { + obs.fProfTwoGap[eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); + } result22 = two(qVectorsTableMC, n22); if (noneZeroDenom(result22)) { @@ -1033,21 +975,15 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fFileWithWeights = cfFileWithWeights; tc.fMasterCutSwitch = cfMasterCutSwitch; - tc.fTechnicalCutSwitch = cfTechnicalCutSwitch.value; - tc.fEventCutSwitch[eSel8Cut] = cfSel8CutSwitch; - tc.fEventCutSwitch[eVertexZCut] = cfVertexZCutSwitch; + tc.fEventCutSwitch = cfEventCutSwitch.value; tc.fVertexZCutRange = cfVertexZCutRange.value; - tc.fEventCutSwitch[eCentCut] = cfCentCutSwitch; tc.fCentCutRange = cfCentCutRange.value; - tc.fEventCutSwitch[eNContribCut] = cfNContribCutSwitch; tc.fNContribCutInf = cfNContribCutInf; - tc.fParticleCutSwitch[ePtCut] = cfPtCutSwitch; + tc.fParticleCutSwitch = cfParticleCutSwitch.value; tc.fPtCutRange = cfPtCutRange.value; - tc.fParticleCutSwitch[eEtaCut] = cfEtaCutSwitch; tc.fEtaCutRange = cfEtaCutRange.value; - tc.fParticleCutSwitch[eChargeCut] = cfChargeCutSwitch; tc.fEtaGap = cfEtaGap; tc.fSigmaInel = cfSigmaInel; @@ -1078,47 +1014,49 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); misc.fMiscHistList->Add(misc.fMiscHistRunNumber); - const auto nTotalEventCuts = static_cast(ETechnicalCuts_N) + static_cast(EEventCuts_N); - misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts Switches", nTotalEventCuts, 0, nTotalEventCuts); - for (int i = 1; i <= ETechnicalCuts_N; ++i) { - misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); - if (tc.fTechnicalCutSwitch[i - 1].starts_with("1")) { - misc.fMiscEventCuts->Fill(i - 0.5, 1); - } - } + misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts Switches", EEventCuts_N, 0, EEventCuts_N); for (int i = 1; i <= EEventCuts_N; ++i) { - const int thisIndex = i + static_cast(ETechnicalCuts_N); - misc.fMiscEventCuts->GetXaxis()->SetBinLabel(thisIndex, EventCutsNames[i - 1]); - if (tc.fEventCutSwitch[i - 1]) { - misc.fMiscEventCuts->Fill(thisIndex - 0.5, 1); + misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); + if (tc.fEventCutSwitch[i - 1].starts_with("1")) { + misc.fMiscEventCuts->Fill(i - 0.5, 1); } } misc.fMiscHistList->Add(misc.fMiscEventCuts); - misc.fMiscEventCutsCounter = new TH1D("[eEventCutsCounter]", "Event Level Cuts Counter (Sequential)", nTotalEventCuts, 0, nTotalEventCuts); - for (int i = 1; i <= ETechnicalCuts_N; ++i) { - misc.fMiscEventCutsCounter->GetXaxis()->SetBinLabel(i, TechnicalCutsNames[i - 1]); - } + misc.fMiscEventCutsCounter[eRec] = new TH1D("[eEventCutsCounter][eRec]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); for (int i = 1; i <= EEventCuts_N; ++i) { - const int thisIndex = i + static_cast(ETechnicalCuts_N); - misc.fMiscEventCutsCounter->GetXaxis()->SetBinLabel(thisIndex, EventCutsNames[i - 1]); + misc.fMiscEventCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); } - misc.fMiscHistList->Add(misc.fMiscEventCutsCounter); + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eRec]); misc.fMiscParticleCuts = new TProfile("[eParticleCuts]", "Particle Level Cuts Switches", EParticleCuts_N, 0, EParticleCuts_N); for (int i = 1; i <= EParticleCuts_N; ++i) { misc.fMiscParticleCuts->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); - if (tc.fParticleCutSwitch[i - 1]) { + if (tc.fParticleCutSwitch[i - 1].starts_with("1")) { misc.fMiscParticleCuts->Fill(i - 0.5, 1); } } misc.fMiscHistList->Add(misc.fMiscParticleCuts); - misc.fMiscParticleCutsCounter = new TH1D("[eParticleCutsCounter]", "Particle Level Cuts Counter (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eRec] = new TH1D("[eParticleCutsCounter][eRec]", "Particle Level Cuts Counter for Reconstructed Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); for (int i = 1; i <= EParticleCuts_N; ++i) { - misc.fMiscParticleCutsCounter->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + misc.fMiscParticleCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eRec]); + + if (doprocessSim || doprocessRecSim) { + misc.fMiscParticleCutsCounter[eSim] = new TH1D("[eParticleCutsCounter][eSim]", "Particle Level Cuts Counter for Simulated Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eSim]); + + misc.fMiscEventCutsCounter[eSim] = new TH1D("[eEventCutsCounter][eSim]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eSim]); } - misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter); // *) Book particle TLists: pc.fParticleHistList = new TList(); @@ -1185,7 +1123,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } if (doprocessSim || doprocessRecSim) { - pc.fParticleHist[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); + pc.fParticleHist[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); pc.fParticleHist[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); pc.fParticleHist[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); @@ -1241,7 +1179,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to const float minMult = lMult[1]; const float maxMult = lMult[2]; - std::vector lMultRef = cfMultBinsRef.value; + std::vector lMultRef = cfRefMultBins.value; const int nBinsMultRef = static_cast(lMultRef[0]); const float minMultRef = lMultRef[1]; const float maxMultRef = lMultRef[2]; @@ -1382,22 +1320,26 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessRec || doprocessRecSim) { obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwo[eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); obs.fProfTwo[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwo[eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eRec]); obs.fProfTwoGap[eRec] = new TProfile("fProfTwoGap[eRec]", "Two particle correlation for reconstructed data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwo[eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); obs.fProfTwoGap[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwoGap[eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwoGap[eRec]); if (doprocessRecSim) { obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwo[eSim]->GetXaxis()->SetTitle("Centrality"); obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwo[eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwo[eSim]); obs.fProfTwoGap[eSim] = new TProfile("fProfTwoGap[eSim]", "Two particle correlation for simulated data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); + obs.fProfTwoGap[eSim]->GetXaxis()->SetTitle("Centrality"); obs.fProfTwoGap[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); obs.fProfTwoGap[eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfTwoGap[eSim]); From a3f8cc7ee55928d8f14bb1555be430b058215db0 Mon Sep 17 00:00:00 2001 From: Yongxi Du <1260971129@qq.com> Date: Thu, 22 Oct 2026 18:29:10 +0800 Subject: [PATCH 388/648] [PWGUD] add Nchvsetagaps (#18402) --- PWGUD/Tasks/flowCumulantsUpc.cxx | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/PWGUD/Tasks/flowCumulantsUpc.cxx b/PWGUD/Tasks/flowCumulantsUpc.cxx index 19f29620a21..01bf6d76a7c 100644 --- a/PWGUD/Tasks/flowCumulantsUpc.cxx +++ b/PWGUD/Tasks/flowCumulantsUpc.cxx @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,7 @@ struct FlowCumulantsUpc { TRandom3* fRndmMc = new TRandom3(0); int lastRunNumber = -1; std::vector runNumbers; + std::map neventsIdBins; std::map> th3sPerRun; // map of TH3 histograms for all runs using UdTracks = soa::Join; @@ -320,6 +322,26 @@ struct FlowCumulantsUpc { } } } + + // One Y bin for every FlowContainer IDName already stored in oba. + const int nIdNames = oba->GetEntries(); + + const AxisSpec axisIdName{nIdNames, 0.5, static_cast(nIdNames) + 0.5, "ID name"}; + + registry.add("hNeventsVsNch", "Selected events;N_{ch};ID name", {HistType::kTH2D, {axisIndependent, axisIdName}}); + + auto hNeventsVsNch = registry.get(HIST("hNeventsVsNch")); + + for (int i = 0; i < nIdNames; ++i) { + auto* observable = dynamic_cast(oba->At(i)); + if (!observable) { + continue; + } + const std::string idName = observable->GetName(); + const int yBin = i + 1; + neventsIdBins[idName] = yBin; + hNeventsVsNch->GetYaxis()->SetBinLabel(yBin, idName.c_str()); + } fFC->SetName("FlowContainer"); fFC->SetXAxis(fPtAxis); fFC->Initialize(oba, axisIndependent, cfgNbootstrap); @@ -520,6 +542,16 @@ struct FlowCumulantsUpc { fGFW->CreateRegions(); } + void fillNeventsVsNch(const std::string& idName, double independent) + { + const auto it = neventsIdBins.find(idName); + if (it == neventsIdBins.end()) { + return; + } + + registry.fill(HIST("hNeventsVsNch"), independent, static_cast(it->second)); + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -538,6 +570,7 @@ struct FlowCumulantsUpc { val = fGFW->Calculate(corrconf, 0, kFALSE).real() / dnx; if (std::fabs(val) < 1) { fFC->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + fillNeventsVsNch(corrconf.Head, cent); } return; } From 6f47fff27639d1f2ff4e26739c43a75de06b654b Mon Sep 17 00:00:00 2001 From: MRazza <118839113+MRazza879@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:33:50 +0200 Subject: [PATCH 389/648] [PWGHF] Add configurable PID vetoes and kinematic QA to deuteronFromLb-task (#18403) Co-authored-by: Marta Razza Co-authored-by: Claude Opus 5.5 --- PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx | 252 ++++++++++++++++++------- 1 file changed, 189 insertions(+), 63 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx index 93244015912..28106a38b6e 100644 --- a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -72,12 +72,22 @@ struct HfTaskDeuteronFromLb { Configurable cfgDCAmin{"cfgDCAmin", 0.05f, "Minimum DCA for deuteron PID"}; Configurable cfgDCAmax{"cfgDCAmax", 1000.0f, "Maximum DCA for deuteron PID"}; Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; - Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, above this value TPC+TOF PID is used, below TPC+ITS PID is used"}; + Configurable applyRapidityCut{"applyRapidityCut", true, "Apply the rapidity cut |y| < rapidityCut (reco and gen)"}; + Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, below TPC+ITS PID is used, above TPC+TOF PID is used and VETO on TPC for e,pi,K,p is applied"}; + Configurable ptThresholdforPID2{"ptThresholdforPID2", 2.2f, "pT threshold above which TPC+TOF PID with TOF veto on pi,K,p is applied (TPC veto below)"}; // PDG codes Configurable pdgCodeBeautyMeson{"pdgCodeBeautyMeson", -521, "PDG code of the beauty meson mother particle (default: B-)"}; Configurable pdgCodeBeautyBaryon{"pdgCodeBeautyBaryon", -5122, "PDG code of the beauty baryon mother particle (default: anti-Lambda_b)"}; Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; Configurable pdgCodeHypertriton{"pdgCodeHypertriton", -1010010030, "PDG code of the hypertriton mother particle (default: anti-hypertriton)"}; + // Veto switches: if true, tracks compatible with that species are rejected + Configurable applyVetoElTPC{"applyVetoElTPC", true, "Apply TPC veto on electrons"}; + Configurable applyVetoPiTPC{"applyVetoPiTPC", true, "Apply TPC veto on pions"}; + Configurable applyVetoKaTPC{"applyVetoKaTPC", true, "Apply TPC veto on kaons"}; + Configurable applyVetoPrTPC{"applyVetoPrTPC", true, "Apply TPC veto on protons"}; + Configurable applyVetoPiTOF{"applyVetoPiTOF", true, "Apply TOF veto on pions"}; + Configurable applyVetoKaTOF{"applyVetoKaTOF", true, "Apply TOF veto on kaons"}; + Configurable applyVetoPrTOF{"applyVetoPrTOF", true, "Apply TOF veto on protons"}; int mRunNumber = 0; int mCurrentRun = -1; @@ -97,6 +107,8 @@ struct HfTaskDeuteronFromLb { ConfigurableAxis dcaXyAxis{"dcaXyAxis", {4000, -2.0f, 2.0f}, "DCA xy (cm)"}; ConfigurableAxis dcaZAxis{"dcaZAxis", {4000, -2.0f, 2.0f}, "DCA z (cm)"}; ConfigurableAxis ctauHypertritonAxis{"ctauHypertritonAxis", {100, 0.f, 30.f}, "c#tau (cm)"}; + ConfigurableAxis etaAxis{"etaAxis", {20, -1.f, 1.f}, "#eta"}; + ConfigurableAxis rapidityAxis{"rapidityAxis", {20, -1.f, 1.f}, "y"}; HistogramRegistry qaHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -141,9 +153,10 @@ struct HfTaskDeuteronFromLb { qaHistos.add("Data/hnSigmaTOFVsPtFinalSel", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); + qaHistos.add("Data/rapidityAntiDeuteron", "rapidityAntiDeuteron;y #bar{d};Counts", {HistType::kTH1F, {rapidityAxis}}); qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); qaHistos.add("Data/hnSigmaTOFVsPtPurityNoVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); - qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC+TOF veto for e,pi,K,p; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for e,pi,K,p below the threshold and TOF veto for pi,K,p above the threshold; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); // MC generated-level histograms @@ -160,27 +173,44 @@ struct HfTaskDeuteronFromLb { qaHistos.add("MCGen/ptGeneratedAntiHypertriton", "p_{T} generated ^{3}_{#bar{#Lambda}}#bar{H};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCGen/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, gen level;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); qaHistos.add("MCGen/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromBminus", "#bar{d} from B^{-} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronPrimary", "#bar{d} primary gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronPrimary", "y #bar{d} primary gen;y;Counts", HistType::kTH1F, {rapidityAxis}); // MC reco/MC-anchored histograms qaHistos.add("MCReco/hDCAxyVsPtDeFromBminus", "DCAxy #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaB", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertriton", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("MCReco/hDCAxyVsPtDePrimary", "DCAxy #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); qaHistos.add("MCReco/hDCAzVsPtDeFromBminus", "DCAz #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaB", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertriton", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); qaHistos.add("MCReco/hDCAzVsPtDePrimary", "DCAz #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); - qaHistos.add("MCReco/hDCAxyVsPtDeFromBminusSameMC", "DCAxy from B^{-}, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); - qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC", "DCAxy from anti-Lambda_b, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); - qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC", "DCAxy from anti-hypertriton, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); - qaHistos.add("MCReco/hDCAxyVsPtDePrimarySameMC", "DCAxy primary, same MC collision", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminusSameMC", "DCAxy #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimarySameMC", "DCAxy #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminusSameMC", "DCAz #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimarySameMC", "DCAz #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminusSameMC", "p_{T} #bar{d} from B^{-} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimarySameMC", "p_{T} #bar{d} primary reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/hEvents", "Events MC reco;;Counts", HistType::kTH1F, {{3, 0., 3.}}); - qaHistos.add("MCReco/hDCAxy-Primary", "DCAxy primary reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); - qaHistos.add("MCReco/hDCAxy-FromBMeson", "DCAxy from B^{-} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); - qaHistos.add("MCReco/hDCAxy-FromAntiLambdaB", "DCAxy from #bar{#Lambda}_{b} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); qaHistos.add("MCReco/hMotherPdgCode", "PDG code of mother, reco/MC anchored;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); qaHistos.add("MCReco/ctauBminus", "ctau of B^{-}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); qaHistos.add("MCReco/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCReco/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, reco/MC anchored;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelected", "p_{T} #bar{d} reco/MC anchored PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC only PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); @@ -189,13 +219,33 @@ struct HfTaskDeuteronFromLb { qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronGenFromBminus", "p_{T} #bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertriton", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); - qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertriton", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); - qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); - qaHistos.add("MCReco/hDCAxy-FromAntiHypertriton", "DCAxy from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;DCA xy (cm);Counts", {HistType::kTH1D, {dcaXyAxis}}); - qaHistos.add("MCReco/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, reco/MC anchored;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); qaHistos.add("MCReco/ptAntiDeuteronGenPrimary", "p_{T} #bar{d} primary gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + // MC reco: eta and rapidity of reconstructed tracks passing the final selection + qaHistos.add("MCReco/etaAntiDeuteronFromBminus", "#eta #bar{d} from B^{-} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaB", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertriton", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimary", "#eta #bar{d} primary reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimary", "y #bar{d} primary reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromBminusSameMC", "#eta #bar{d} from B^{-} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimarySameMC", "#eta #bar{d} primary reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminusSameMC", "y #bar{d} from B^{-} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimarySameMC", "y #bar{d} primary reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromBminus", "#bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenPrimary", "#bar{d} primary gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromBminus", "y #bar{d} from B^{-} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenPrimary", "y #bar{d} primary gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); @@ -237,7 +287,7 @@ struct HfTaskDeuteronFromLb { if (std::abs(track.eta()) > cfgEta) { return false; } - if (std::abs(track.rapidity(o2::constants::physics::MassDeuteron)) > rapidityCut) { + if (applyRapidityCut && std::abs(track.rapidity(o2::constants::physics::MassDeuteron)) > rapidityCut) { return false; } if (!track.hasITS()) { @@ -328,40 +378,58 @@ struct HfTaskDeuteronFromLb { qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); - const bool isElectron = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma && std::abs(track.tofNSigmaEl()) < cfgTofNsigma; - const bool isNotElectron = !isElectron; - const bool isPion = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma && std::abs(track.tofNSigmaPi()) < cfgTofNsigma; - const bool isNotPion = !isPion; - const bool isKaon = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma && std::abs(track.tofNSigmaKa()) < cfgTofNsigma; - const bool isNotKaon = !isKaon; - const bool isProton = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma && std::abs(track.tofNSigmaPr()) < cfgTofNsigma; - const bool isNotProton = !isProton; - - const bool isTPCTOFDeClean = isTPCDe && isNotElectron && isNotPion && isNotKaon && isNotProton; + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } - if (track.pt() < ptThresholdforPID) { - if (isITSDe) { - qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); - } - } else { - if (isTPCDe && track.hasTOF()) { - qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityNoVeto"), track.pt(), track.tofNSigmaDe()); - } - if (isTPCTOFDeClean && track.hasTOF()) { + // pT < 1.0 GeV/c: purity TPC using ITS + if (isLowPt && isITSDe) { + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); + } + // pT >= 1.0 GeV/c: purity TOF with and without vetoes + if (!isLowPt && isTPCDe && track.hasTOF()) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityNoVeto"), track.pt(), track.tofNSigmaDe()); + if (passVeto) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); } } - const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || - (track.pt() >= ptThresholdforPID && isTPCTOFDeClean && isTOFDe && track.hasTOF()); + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); if (passFinalSel) { qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); + qaHistos.fill(HIST("Data/rapidityAntiDeuteron"), track.rapidity(o2::constants::physics::MassDeuteron)); qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); qaHistos.fill(HIST("Data/hnSigmaTPCVsPtFinalSel"), track.pt(), track.tpcNSigmaDe()); - if (track.pt() >= ptThresholdforPID) { + if (!isLowPt) { qaHistos.fill(HIST("Data/hnSigmaTOFVsPtFinalSel"), track.pt(), track.tofNSigmaDe()); } } @@ -405,7 +473,7 @@ struct HfTaskDeuteronFromLb { if (mcParticle.pdgCode() != pdgCodeDaughter) { continue; } - if (std::abs(mcParticle.eta()) > cfgEta || std::abs(mcParticle.y()) > rapidityCut) { + if (std::abs(mcParticle.eta()) > cfgEta || (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut)) { continue; } if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { @@ -415,22 +483,30 @@ struct HfTaskDeuteronFromLb { for (const auto& mom : mcParticle.mothers_as()) { if (mom.pdgCode() == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromBminus"), mcParticle.y()); fromMother = true; break; } if (mom.pdgCode() == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB"), mcParticle.y()); fromMother = true; break; } if (mom.pdgCode() == pdgCodeHypertriton) { qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton"), mcParticle.y()); fromMother = true; break; } } if (!fromMother && mcParticle.isPhysicalPrimary()) { qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenPrimary"), mcParticle.y()); } } } @@ -487,16 +563,35 @@ struct HfTaskDeuteronFromLb { } } - const bool isElectron = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma && std::abs(track.tofNSigmaEl()) < cfgTofNsigma; - const bool isNotElectron = !isElectron; - const bool isPion = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma && std::abs(track.tofNSigmaPi()) < cfgTofNsigma; - const bool isNotPion = !isPion; - const bool isKaon = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma && std::abs(track.tofNSigmaKa()) < cfgTofNsigma; - const bool isNotKaon = !isKaon; - const bool isProton = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma && std::abs(track.tofNSigmaPr()) < cfgTofNsigma; - const bool isNotProton = !isProton; - - const bool isTPCTOFDeClean = isTPCDe && isNotElectron && isNotPion && isNotKaon && isNotProton; + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } if (isTrueDeuteron) { int motherPdg = 0; @@ -537,43 +632,62 @@ struct HfTaskDeuteronFromLb { } else { qaHistos.fill(HIST("MCReco/hMotherCategory"), 4.5); } - // same final selection as in data - const bool passFinalSel = (track.pt() < ptThresholdforPID && isTPCDe && isITSDe) || - (track.pt() >= ptThresholdforPID && isTPCTOFDeClean && isTOFDe && track.hasTOF()); + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); if (passFinalSel) { + const float rapidity = track.rapidity(o2::constants::physics::MassDeuteron); if (motherPdg == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminus"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminus"), rapidity); if (isSameMcCollision) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminusSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminusSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminusSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminusSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminusSameMC"), rapidity); } - qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); - qaHistos.fill(HIST("MCReco/hDCAxy-FromBMeson"), dca[0]); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); } else if (motherPdg == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaB"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaB"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaB"), rapidity); if (isSameMcCollision) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC"), rapidity); } - qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); - qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiLambdaB"), dca[0]); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); } else if (motherPdg == pdgCodeHypertriton) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertriton"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertriton"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertriton"), rapidity); if (isSameMcCollision) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC"), rapidity); } - qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); - qaHistos.fill(HIST("MCReco/hDCAxy-FromAntiHypertriton"), dca[0]); - qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); } else if (mcParticle.isPhysicalPrimary()) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimary"), track.pt(), dca[0]); qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimary"), track.pt(), dca[1]); - qaHistos.fill(HIST("MCReco/hDCAxy-Primary"), dca[0]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimary"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimary"), rapidity); if (isSameMcCollision) { qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimarySameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimarySameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimarySameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimarySameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimarySameMC"), rapidity); } - qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); } } } @@ -630,7 +744,11 @@ struct HfTaskDeuteronFromLb { continue; } - if (std::abs(mcParticle.y()) > rapidityCut) { + if (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut) { + continue; + } + + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { continue; } @@ -661,12 +779,20 @@ struct HfTaskDeuteronFromLb { if (motherPdg == pdgCodeBeautyMeson) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromBminus"), mcParticle.y()); } else if (motherPdg == pdgCodeBeautyBaryon) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiLambdaB"), mcParticle.y()); } else if (motherPdg == pdgCodeHypertriton) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiHypertriton"), mcParticle.y()); } else if (mcParticle.isPhysicalPrimary()) { qaHistos.fill(HIST("MCGen/ptAntiDeuteronPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronPrimary"), mcParticle.y()); } } } From c1c4b5d21ef59f08247c9f2cdd3c452481d0d2dd Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:37:56 +0200 Subject: [PATCH 390/648] [PWGHF] correlatorFlowCharmHadronsReduced: add multiplicity selections (#18225) Co-authored-by: ALICE Action Bot --- .../correlatorFlowCharmHadronsReduced.cxx | 80 ++++++++++++++----- .../derivedDataCreatorCorrelationsReduced.cxx | 40 ++++++---- 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index 911ae2a2b0d..777eff5c01b 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -96,8 +96,12 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable> bdtScore0PtMaxs{"bdtScore0PtMaxs", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential maximum score 0 for charm candidates"}; Configurable> bdtScore1PtMins{"bdtScore1PtMins", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential minimum score 1 for charm candidates"}; Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.3, 1., 2., 50.}, "pT bin limits for associated particles"}; - Configurable centralityMin{"centralityMin", 0, "min. centrality"}; - Configurable centralityMax{"centralityMax", 10., "max. centrality"}; + Configurable selCentrality{"selCentrality", false, "Use centrality to select events"}; + Configurable selMultiplicity{"selMultiplicity", false, "Use multiplicity to select events"}; + Configurable centMin{"centMin", 0, "min. centrality"}; + Configurable centMax{"centMax", 10., "max. centrality"}; + Configurable multMin{"multMin", 0, "min. multiplicity"}; + Configurable multMax{"multMax", 10., "max. multiplicity"}; Configurable deltaEtaAbsMin{"deltaEtaAbsMin", 0.5, "min. pair delta eta"}; Configurable deltaEtaAbsMax{"deltaEtaAbsMax", 2., "max. pair delta eta"}; Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. track DCA XY"}; @@ -106,7 +110,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable itsNClsMin{"itsNClsMin", 1, "min. ITS clusters"}; Configurable downSamplePairs{"downSamplePairs", 1., "Fraction of pairs to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable centMaxForDownSample{"centMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; + Configurable centMultMaxForDownSample{"centMultMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; SliceCache cache; @@ -131,10 +135,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; - ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; - ConfigurableAxis binsPhi{"binsPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Phi bins"}; ConfigurableAxis binsDeltaEta{"binsDeltaEta", {100, -2., 2.}, "Delta Eta bins"}; - ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -3., 3.}, "Delta Phi bins"}; + ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Delta Phi bins"}; ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, "ML score index 1 bins"}; ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, "ML score index 2 bins"}; @@ -142,6 +144,21 @@ struct HfCorrelatorFlowCharmHadronsReduced { void init(InitContext&) { + if (selCentrality && selMultiplicity) { + LOGP(fatal, "You cannot select events based on both centrality and multiplicity! Please check your configuration!"); + } + if (!selCentrality && !selMultiplicity) { + LOGP(fatal, "You must select events based on either centrality or multiplicity! Please check your configuration!"); + } + + // Fatal if selCentrality and process functions using multiplicity mixing, or selMultiplicity and process functions using centrality mixing + if (selCentrality && (doprocessSameEventCharmHadWMultMix || doprocessMixedEventCharmHadWMultMix || doprocessSameEventHadHadWMultMix || doprocessMixedEventHadHadWMultMix)) { + LOGP(fatal, "You cannot select events based on centrality and process functions using multiplicity mixing! Please check your configuration!"); + } + if (selMultiplicity && (doprocessSameEventCharmHadWCentMix || doprocessMixedEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix || doprocessMixedEventHadHadWCentMix)) { + LOGP(fatal, "You cannot select events based on multiplicity and process functions using centrality mixing! Please check your configuration!"); + } + if ((doprocessSameEventCharmHadWCentMix && doprocessMixedEventCharmHadWMultMix) || (doprocessSameEventCharmHadWMultMix && doprocessMixedEventCharmHadWCentMix) || (doprocessSameEventHadHadWCentMix && doprocessMixedEventHadHadWMultMix) || @@ -185,6 +202,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } registry.add("hZVtxPoolBinSE", "z vertex SE", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); registry.add("hZVtxPoolBinME", "z vertex ME", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); + registry.add("hCollisionsCentMultAll", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); + registry.add("hCollisionsCentMultSel", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); registry.add("hPoolBinTrigSE", "Trigger candidates pool bin SE", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinTrigME", "Trigger candidates pool bin ME", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocSE", "Associated particles pool bin SE", {HistType::kTH1F, {axisPoolBin}}); @@ -211,6 +230,18 @@ struct HfCorrelatorFlowCharmHadronsReduced { } } + template + bool isSelCollision(const TColl& collision) + { + bool isSel = false; + if (selCentrality) { + isSel = (collision.centrality() >= centMin && collision.centrality() <= centMax); + } else if (selMultiplicity) { + isSel = (collision.multiplicity() >= multMin && collision.multiplicity() <= multMax); + } + return isSel; + } + /// Get the binning pool associated to the collision /// \param collision is the collision /// \param binPolicy is the binning policy for the correlation @@ -265,9 +296,12 @@ struct HfCorrelatorFlowCharmHadronsReduced { const TBinningType& binPolicy) { auto collision = pair.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), collision.centrality(), collision.multiplicity()); + if (!isSelCollision(collision)) { return; } + registry.fill(HIST("hCollisionsCentMultSel"), collision.centrality(), collision.multiplicity()); + double const ptTrig = trigCand.ptTrig(); if constexpr (requires { trigCand.bdtScore0Trig(); }) { // ML selection on bkg score for Charm-Had case if (!isSelBdtScoreCut(trigCand, ptTrig)) { @@ -276,7 +310,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } if (downSamplePairs < 1.) { float const pseudoRndm = ptTrig * 1000. - static_cast(ptTrig * 1000); - if (ptTrig < ptMaxForDownSample && collision.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float collCentMult = selCentrality ? collision.centrality() : collision.multiplicity(); + if (ptTrig < ptMaxForDownSample && collCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { return; } } @@ -317,10 +352,14 @@ struct HfCorrelatorFlowCharmHadronsReduced { if (trigCands.size() == 0 || assocTracks.size() == 0) { continue; } - if (trigColl.centrality() < centralityMin || trigColl.centrality() > centralityMax || - assocColl.centrality() < centralityMin || assocColl.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultAll"), assocColl.centrality(), assocColl.multiplicity()); + if (!isSelCollision(trigColl) || !isSelCollision(assocColl)) { continue; } + registry.fill(HIST("hCollisionsCentMultSel"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultSel"), assocColl.centrality(), assocColl.multiplicity()); + int const poolBinTrig = getPoolBin(trigColl, binPolicy); int const poolBinAssoc = getPoolBin(assocColl, binPolicy); if (poolBinAssoc != poolBinTrig) { @@ -345,8 +384,10 @@ struct HfCorrelatorFlowCharmHadronsReduced { double const ptAssoc = getPt(assocTrack); if (downSamplePairs < 1.) { float const pseudoRndm = ptAssoc * 1000. - static_cast(ptAssoc * 1000); - if (ptTrig < ptMaxForDownSample && trigColl.centrality() < centMaxForDownSample && - assocColl.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float trigCollCentMult = selCentrality ? trigColl.centrality() : trigColl.multiplicity(); + float assocCollCentMult = selCentrality ? assocColl.centrality() : assocColl.multiplicity(); + if (ptTrig < ptMaxForDownSample && trigCollCentMult < centMultMaxForDownSample && + assocCollCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { continue; } } @@ -430,12 +471,15 @@ struct HfCorrelatorFlowCharmHadronsReduced { Pair pairData{binPolicy, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairData) { + if (!isSelCollision(c1) || !isSelCollision(c2)) { + continue; + } if (tracks1.size() == 0) { continue; } - int poolBin = binPolicy.getBin({c2.posZ(), c2.centrality()}); - int poolBinTrigCand = binPolicy.getBin({c1.posZ(), c1.centrality()}); + int poolBin = binPolicy.getBin({c2.posZ(), selCentrality ? c2.centrality() : c2.multiplicity()}); + int poolBinTrigCand = binPolicy.getBin({c1.posZ(), selCentrality ? c1.centrality() : c1.multiplicity()}); if (poolBin != poolBinTrigCand) { LOGF(info, "Error, poolBins are different"); @@ -446,7 +490,6 @@ struct HfCorrelatorFlowCharmHadronsReduced { if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { continue; } - LOGF(info, "Mixed event tracks pair: (%d, %d) from events (%d, %d), track event: (%d, %d)", trigCand.index(), assTrk.index(), c1.index(), c2.index(), trigCand.hfcRedCorrCollId(), assTrk.hfcRedCorrCollId()); double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); @@ -476,7 +519,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { fillSameEvent(pair, trigCand, binPolicyPosZMult); } } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", true); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", false); void processSameEventHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, aod::HfcRedTrigTracks const&, @@ -595,9 +638,10 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processCharmTriggers(aod::HfcRedTrigCharms const& trigCands, aod::HfcRedCorrColls const&) { + for (const auto& trigCand : trigCands) { auto collision = trigCand.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + if (!isSelCollision(collision)) { continue; } if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { @@ -615,7 +659,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; for (const auto& collision : collisions) { - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + if (!isSelCollision(collision)) { continue; } int poolBin = binPolicyPosZCent.getBin({collision.posZ(), collision.centrality()}); diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index a8fe13040bf..c6a79d8dfaa 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -139,7 +139,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Partition selectedLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, ""}; - ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; + ConfigurableAxis binsMult{"binsMult", {100, 0., 10000.}, "Multiplicity bins"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; @@ -174,7 +174,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { ccdb->setLocalObjectValidityChecking(); const AxisSpec axisCent = {binsCent, "Centrality"}; - const AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; + const AxisSpec axisMult = {binsMult, "Multiplicity"}; const AxisSpec axisPosZ = {binsPosZ, "PosZ"}; const AxisSpec axisEta = {binsEta, "#it{#eta}"}; const AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; @@ -190,8 +190,10 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.add("hEtaVsPtTrigAssoc", "Associated particles etaVsPt", {HistType::kTH3F, {{axisEta}, {axisPtTrig}, {axisPtAssoc}}}); registry.add("hPhiVsPtAssoc", "Associated particles phiVsPt", {HistType::kTH2F, {{axisPhi}, {axisPtAssoc}}}); registry.add("hEtaVsPtAssoc", "Associated particles etaVsPt", {HistType::kTH2F, {{axisEta}, {axisPtAssoc}}}); - registry.add("hDcaXYVsPtAssoc", "Associated particles DCAxyVsPt", {HistType::kTH2F, {{axisDcaXY}, {axisPtAssoc}}}); - registry.add("hDcaZVsPtAssoc", "Associated particles DCAzVsPt", {HistType::kTH2F, {{axisDcaZ}, {axisPtAssoc}}}); + registry.add("hDcaXYVsPtVsCentAssoc", "Associated particles DCAxyVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisCent}}}); + registry.add("hDcaZVsPtVsCentAssoc", "Associated particles DCAzVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisCent}}}); + registry.add("hDcaXYVsPtVsMultAssoc", "Associated particles DCAxyVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisMult}}}); + registry.add("hDcaZVsPtVsMultAssoc", "Associated particles DCAzVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisMult}}}); // Setup pt-dependent DCAxy cut function if (usePtDiffDcaXYCut) { @@ -359,11 +361,13 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill histograms and tables for same-event correlations /// \param trigCands are the trigger candidates /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity template void fillSameEvent(TTrigCands const& trigCands, TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { for (const auto& trigCand : trigCands) { if (!cutCandRapidity(trigCand)) { @@ -394,7 +398,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (downSampleTrksFactor < 1.) { float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } @@ -402,8 +406,10 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.fill(HIST("hEtaVsPtTrigAssoc"), assTrk.eta(), trigCandPt, assTrkPt); registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); double deltaPhi = RecoDecay::constrainAngle(assTrk.phi() - trigCand.phi(), -o2::constants::math::PIHalf); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); @@ -482,7 +488,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDplus, tracks, cent); + fillSameEvent(candsDplus, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusSameEvent, "Process Same Event for Dplus candidates", true); @@ -519,8 +525,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDsToPiKK, tracks, cent); - fillSameEvent(candsDsToKKPi, tracks, cent); + fillSameEvent(candsDsToPiKK, tracks, cent, mult); + fillSameEvent(candsDsToKKPi, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsSameEvent, "Process Same Event for Ds candidates", false); @@ -560,8 +566,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsD0ToPiK, tracks, cent); - fillSameEvent(candsD0ToKPi, tracks, cent); + fillSameEvent(candsD0ToPiK, tracks, cent, mult); + fillSameEvent(candsD0ToKPi, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0SameEvent, "Process Same Event for D0 candidates", false); @@ -601,8 +607,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsLcToPKPi, tracks, cent); - fillSameEvent(candsLcToPiKP, tracks, cent); + fillSameEvent(candsLcToPKPi, tracks, cent, mult); + fillSameEvent(candsLcToPiKP, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcSameEvent, "Process Same Event for Lc candidates", false); @@ -636,7 +642,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(tracks, tracks, cent); + fillSameEvent(tracks, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronSameEvent, "Process Same Event for hadron candidates", false); From 59933dcce42ee9e5b4296463c0702f8d67c2206f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 22 Oct 2026 13:43:20 +0200 Subject: [PATCH 391/648] [ALICE3] Add root writers for LUTs (#18406) --- ALICE3/Core/CMakeLists.txt | 6 +- ALICE3/Core/FlatLutRootWriter.cxx | 210 ++++++++++++++++++++++++++++++ ALICE3/Core/FlatLutRootWriter.h | 71 ++++++++++ 3 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 ALICE3/Core/FlatLutRootWriter.cxx create mode 100644 ALICE3/Core/FlatLutRootWriter.h diff --git a/ALICE3/Core/CMakeLists.txt b/ALICE3/Core/CMakeLists.txt index 4bfb95fba00..de89ec6f1a4 100644 --- a/ALICE3/Core/CMakeLists.txt +++ b/ALICE3/Core/CMakeLists.txt @@ -13,10 +13,14 @@ o2physics_add_library(ALICE3Core SOURCES TrackUtilities.cxx ConfigurationParser.cxx FlatLutEntry.cxx + FlatLutRootWriter.cxx FlatTrackSmearer.cxx GeometryContainer.cxx PUBLIC_LINK_LIBRARIES O2::Framework - O2Physics::AnalysisCore) + O2Physics::AnalysisCore + PRIVATE_LINK_LIBRARIES ROOT::Core + ROOT::RIO + ROOT::Tree) o2physics_target_root_dictionary(ALICE3Core HEADERS TrackUtilities.h diff --git a/ALICE3/Core/FlatLutRootWriter.cxx b/ALICE3/Core/FlatLutRootWriter.cxx new file mode 100644 index 00000000000..162084dcffe --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.cxx @@ -0,0 +1,210 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ALICE3/Core/FlatLutRootWriter.h" + +#include +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +FlatLutRootWriter::FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma) + : mHeader(header), + mCovarianceStoresSigma(covarianceStoresSigma) +{ +} + +void FlatLutRootWriter::reserve(std::size_t n) +{ + mRows.reserve(n); +} + +void FlatLutRootWriter::addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry) +{ + FlatLutRootRow row; + row.entryIndex = entryIndex; + row.nchBin = nchBin; + row.radBin = radBin; + row.etaBin = etaBin; + row.ptBin = ptBin; + + row.nch = entry.nch; + row.eta = entry.eta; + row.pt = entry.pt; + + row.valid = entry.valid; + row.eff = entry.eff; + row.eff2 = entry.eff2; + row.itof = entry.itof; + row.otof = entry.otof; + + std::copy(entry.covm.begin(), entry.covm.end(), row.covm.begin()); + std::copy(entry.eigval.begin(), entry.eigval.end(), row.eigval.begin()); + row.eigvec = entry.eigvec; + row.eiginv = entry.eiginv; + + mRows.push_back(std::move(row)); +} + +bool FlatLutRootWriter::writeToFile(const std::string& rootFileName) const +{ + TFile output(rootFileName.c_str(), "RECREATE"); + if (output.IsZombie()) { + return false; + } + + int version = mHeader.version; + int pdg = mHeader.pdg; + float mass = mHeader.mass; + float field = mHeader.field; + + int nch_nbins = mHeader.nchmap.nbins; + float nch_min = mHeader.nchmap.min; + float nch_max = mHeader.nchmap.max; + bool nch_log = mHeader.nchmap.log; + + int rad_nbins = mHeader.radmap.nbins; + float rad_min = mHeader.radmap.min; + float rad_max = mHeader.radmap.max; + bool rad_log = mHeader.radmap.log; + + int eta_nbins = mHeader.etamap.nbins; + float eta_min = mHeader.etamap.min; + float eta_max = mHeader.etamap.max; + bool eta_log = mHeader.etamap.log; + + int pt_nbins = mHeader.ptmap.nbins; + float pt_min = mHeader.ptmap.min; + float pt_max = mHeader.ptmap.max; + bool pt_log = mHeader.ptmap.log; + + bool covarianceStoresSigma = mCovarianceStoresSigma; + + TTree headerTree("lutHeader", "LUT header"); + headerTree.Branch("version", &version, "version/I"); + headerTree.Branch("pdg", &pdg, "pdg/I"); + headerTree.Branch("mass", &mass, "mass/F"); + headerTree.Branch("field", &field, "field/F"); + + headerTree.Branch("nch_nbins", &nch_nbins, "nch_nbins/I"); + headerTree.Branch("nch_min", &nch_min, "nch_min/F"); + headerTree.Branch("nch_max", &nch_max, "nch_max/F"); + headerTree.Branch("nch_log", &nch_log, "nch_log/O"); + + headerTree.Branch("rad_nbins", &rad_nbins, "rad_nbins/I"); + headerTree.Branch("rad_min", &rad_min, "rad_min/F"); + headerTree.Branch("rad_max", &rad_max, "rad_max/F"); + headerTree.Branch("rad_log", &rad_log, "rad_log/O"); + + headerTree.Branch("eta_nbins", &eta_nbins, "eta_nbins/I"); + headerTree.Branch("eta_min", &eta_min, "eta_min/F"); + headerTree.Branch("eta_max", &eta_max, "eta_max/F"); + headerTree.Branch("eta_log", &eta_log, "eta_log/O"); + + headerTree.Branch("pt_nbins", &pt_nbins, "pt_nbins/I"); + headerTree.Branch("pt_min", &pt_min, "pt_min/F"); + headerTree.Branch("pt_max", &pt_max, "pt_max/F"); + headerTree.Branch("pt_log", &pt_log, "pt_log/O"); + + headerTree.Branch("covarianceStoresSigma", &covarianceStoresSigma, "covarianceStoresSigma/O"); + headerTree.Fill(); + + TTree entryTree("lutEntries", "LUT entries"); + + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + float covm[kNumCovarianceTerms] = {0.f}; + float eigval[kNumEigenModes] = {0.f}; + float eigvec[kNumEigenModes * kNumEigenModes] = {0.f}; + float eiginv[kNumEigenModes * kNumEigenModes] = {0.f}; + + entryTree.Branch("entryIndex", &entryIndex, "entryIndex/I"); + entryTree.Branch("nchBin", &nchBin, "nchBin/I"); + entryTree.Branch("radBin", &radBin, "radBin/I"); + entryTree.Branch("etaBin", &etaBin, "etaBin/I"); + entryTree.Branch("ptBin", &ptBin, "ptBin/I"); + + entryTree.Branch("nch", &nch, "nch/F"); + entryTree.Branch("eta", &eta, "eta/F"); + entryTree.Branch("pt", &pt, "pt/F"); + + entryTree.Branch("valid", &valid, "valid/O"); + entryTree.Branch("eff", &eff, "eff/F"); + entryTree.Branch("eff2", &eff2, "eff2/F"); + entryTree.Branch("itof", &itof, "itof/F"); + entryTree.Branch("otof", &otof, "otof/F"); + + entryTree.Branch("covm", covm, "covm[15]/F"); + entryTree.Branch("eigval", eigval, "eigval[5]/F"); + entryTree.Branch("eigvec", eigvec, "eigvec[25]/F"); + entryTree.Branch("eiginv", eiginv, "eiginv[25]/F"); + + for (const auto& row : mRows) { + entryIndex = row.entryIndex; + nchBin = row.nchBin; + radBin = row.radBin; + etaBin = row.etaBin; + ptBin = row.ptBin; + + nch = row.nch; + eta = row.eta; + pt = row.pt; + + valid = row.valid; + eff = row.eff; + eff2 = row.eff2; + itof = row.itof; + otof = row.otof; + + std::copy(row.covm.begin(), row.covm.end(), covm); + std::copy(row.eigval.begin(), row.eigval.end(), eigval); + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { + eigvec[kNumEigenModes * i + j] = row.eigvec[i][j]; + eiginv[kNumEigenModes * i + j] = row.eiginv[i][j]; + } + } + + entryTree.Fill(); + } + + headerTree.Write(); + entryTree.Write(); + output.Close(); + + return true; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatLutRootWriter.h b/ALICE3/Core/FlatLutRootWriter.h new file mode 100644 index 00000000000..4360c7f72ae --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICE3_CORE_FLATLUTROOTWRITER_H_ +#define ALICE3_CORE_FLATLUTROOTWRITER_H_ + +#include "ALICE3/Core/FlatLutEntry.h" + +#include +#include +#include +#include + +namespace o2::fastsim +{ + +struct FlatLutRootRow { + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + std::array covm{}; + std::array eigval{}; + EigenMatrix eigvec{}; + EigenMatrix eiginv{}; +}; + +class FlatLutRootWriter +{ + public: + FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma); + + void reserve(std::size_t n); + void addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry); + + bool writeToFile(const std::string& rootFileName) const; + + private: + lutHeader_t mHeader; + bool mCovarianceStoresSigma = false; + std::vector mRows; +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_FLATLUTROOTWRITER_H_ From 7900bb8844c6102f9fbf5d62d361b6576b64fb25 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:48:54 +0200 Subject: [PATCH 392/648] [PWGCF] Fix mode of daughter hist managers in resonance hist manager (#18407) --- .../Core/trackTrackTwoPhotonResonanceHistManager.h | 13 +++++-------- .../Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h index 936fd87dd36..e50ade02d91 100644 --- a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h @@ -148,12 +148,9 @@ class TrackTrackTwoPhotonResonanceHistManager { mHistogramRegistry = registry; - // omega(782) -> pi+ pi- pi0: both charged daughters are pions. Daughter managers are kept at - // kReco regardless of the outer mode: their fillQa() needs detector-QA columns (tpcNClsFound, - // itsNCls, ...) that plain FTracks doesn't carry (only the dedicated femtoTrackQa.cxx joins - // those in) -- detailed per-track detector QA isn't this manager's job. - mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); - mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); + // omega(782) -> pi+ pi- pi0: both charged daughters are pions + mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); + mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); // the embedded pi0/eta candidate is a fully-fledged femto table with its own mask/extras // columns, so unlike the plain-track daughters above, it can just follow the outer mode. @@ -171,9 +168,9 @@ class TrackTrackTwoPhotonResonanceHistManager void fill(T1 const& resonance, T2 const& tracks, T3 const& neutralDaus, T4 const& photons) { auto posDau = tracks.rawIteratorAt(resonance.posDauId() - tracks.offset()); - mPosDauManager.template fill(posDau, tracks); + mPosDauManager.template fill(posDau, tracks); auto negDau = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); - mNegDauManager.template fill(negDau, tracks); + mNegDauManager.template fill(negDau, tracks); auto neutralDau = neutralDaus.rawIteratorAt(resonance.neutralDauId() - neutralDaus.offset()); mNeutralDauManager.template fill(neutralDau, photons); diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx index 35a09042d94..ff68ce1f256 100644 --- a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -44,7 +44,7 @@ struct FemtoTrackTrackTwoPhotonResonanceQa { using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; using FemtoOmegaMesons = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoPi0s = o2::soa::Join; using FemtoPhotons = o2::soa::Join; From 38d355e8aef0735d79b2c8a9a969a77f3484cac3 Mon Sep 17 00:00:00 2001 From: Jiabin Geng <997406694@qq.com> Date: Thu, 22 Oct 2026 20:59:22 +0800 Subject: [PATCH 393/648] [PWGLF] Perform P-Lambda correlation in hStrangeCorrelationFilter.cxx and hStrangeCorrelation.cxx (#18411) --- .../Strangeness/hStrangeCorrelationFilter.cxx | 15 +++++++++++++-- PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx | 7 +++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx index 927c13ae023..7643c9ab979 100644 --- a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx @@ -138,6 +138,10 @@ struct HStrangeCorrelationFilter { // formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + + // perform P-Lambda correlation analysis + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable triggerProtonNSigmaTPCFOF{"triggerProtonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Proton ID"}; } trackSelections; struct : ConfigurableGroup { // V0 selections @@ -213,8 +217,8 @@ struct HStrangeCorrelationFilter { // using V0LinkedTagged = soa::Join; // using CascadesLinkedTagged = soa::Join; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; using DauTracks = soa::Join; using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID @@ -459,6 +463,13 @@ struct HStrangeCorrelationFilter { if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } + bool isProton = std::sqrt(track.tofNSigmaPr() * track.tofNSigmaPr() + track.tpcNSigmaPr() * track.tpcNSigmaPr()) <= trackSelections.triggerProtonNSigmaTPCFOF; + if (trackSelections.performPLambdacorrelation <= 1 && !isProton) { + return false; + } + if (trackSelections.performPLambdacorrelation == 2 && isProton) { + return false; + } return true; } template diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 3b29c9d3ef8..68f5053ccaf 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -149,6 +149,7 @@ struct HStrangeCorrelation { Configurable doLocalDensityStudy{"doLocalDensityStudy", false, "if true, create and fill the pt vs eta vs local density spectra of triggers and V0s"}; Configurable localDensityConeRadius{"localDensityConeRadius", 0.4, "radius of the cone in which the local density is counted"}; Configurable doMultEstimatorResponse{"doMultEstimatorResponse", false, "processSameEventHV0s: fill MultEstimatorResponse, multNTracksPVeta1 vs FT0M percentile and amplitude, with the event selection of PairLossK0/EventRecoEfficiency (no INEL cuts)"}; + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; } masterConfigurations; // master analysis switches @@ -1538,6 +1539,12 @@ struct HStrangeCorrelation { float efficiency = 1.0f; float totalEffUncert = 0.0; float efficiencyError = 0.0f; + if (masterConfigurations.performPLambdacorrelation == 0 && ((Index == 1 && triggSign == 1) || (Index == 2 && triggSign == -1))) { + return; + } + if (masterConfigurations.performPLambdacorrelation == 1 && ((Index == 1 && triggSign == -1) || (Index == 2 && triggSign == 1))) { + return; + } if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { std::array bin = {ptassoc, assoc.eta(), assoc.phi(), mult}; From 81c8dc01e119ad36bf3fafcc36500f969e3d81fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 22 Oct 2026 15:54:26 +0200 Subject: [PATCH 394/648] [ALICE3] Refactor axes and histogram definitions in singleparticle (#18405) Co-authored-by: ALICE Action Bot --- ALICE3/Tasks/alice3-qa-singleparticle.cxx | 180 +++++++++++++--------- 1 file changed, 104 insertions(+), 76 deletions(-) diff --git a/ALICE3/Tasks/alice3-qa-singleparticle.cxx b/ALICE3/Tasks/alice3-qa-singleparticle.cxx index 851f67adbe7..4fe232bf07f 100644 --- a/ALICE3/Tasks/alice3-qa-singleparticle.cxx +++ b/ALICE3/Tasks/alice3-qa-singleparticle.cxx @@ -61,7 +61,7 @@ struct Alice3SingleParticle { // The pT, eta, y and production-vertex-z axes also define the selection of the particles of interest struct : ConfigurableGroup { - std::string prefix = "axes"; + std::string prefix = "settingsAxes"; ConfigurableAxis axisMomentum{"axisMomentum", {500, 0.f, 10.f}, "Binning for p and pT"}; ConfigurableAxis axisMomentumComponent{"axisMomentumComponent", {500, -10.f, 10.f}, "Binning for px, py and pz"}; ConfigurableAxis axisEnergy{"axisEnergy", {500, 0.f, 1000.f}, "Binning for the energy"}; @@ -74,22 +74,27 @@ struct Alice3SingleParticle { ConfigurableAxis axisProdRadius{"axisProdRadius", {100, 0.f, 2.f}, "Binning for the production vertex radius"}; ConfigurableAxis axisNch{"axisNch", {150, 0.f, 150.f}, "Charged-particle multiplicity"}; ConfigurableAxis axisNSignal{"axisNSignal", {20, -0.5f, 19.5f}, "Number of selected particles of interest per collision"}; - } axes; + ConfigurableAxis distanceAxis{"distanceAxis", {1000, 0, 1e-10}, "Distance (cm)"}; + } settingsAxes; std::vector particlesOfInterest; // global indices of the MC particles selected in genericProcessParticles, per DF // Selection range [min, max) of the particles of interest, taken from an axis so that every selected particle is inside its histograms struct Range { + Range() = default; + Range(double min_, double max_, int64_t bins_) : min(min_), max(max_), bins(bins_) {} double min = 0.; double max = 0.; - bool contains(double value) const { return value >= min && value < max; } + int64_t bins = 0; + int getBins() const { return static_cast(bins); } + [[nodiscard]] bool contains(double value) const { return value >= min && value < max; } }; - Range ptRange, etaRange, yRange, prodZRange; + Range ptRange, etaRange, yRange, prodXYrange, prodZRange; static Range getRange(const ConfigurableAxis& axis) { const AxisSpec spec{axis}; - return {spec.binEdges.front(), spec.binEdges.back()}; + return Range(spec.binEdges.front(), spec.binEdges.back(), spec.getNbins()); } void init(InitContext&) @@ -120,16 +125,37 @@ struct Alice3SingleParticle { pdg->AddParticle("helium4", "helium4", o2::constants::physics::MassAlpha, kTRUE, 0.0, 6, "Nucleus", 1000020040); pdg->AddAntiParticle("anti-helium4", -1000020040); - ptRange = getRange(axes.axisMomentum); - etaRange = getRange(axes.axisEta); - yRange = getRange(axes.axisY); - prodZRange = getRange(axes.axisProdZ); + ptRange = getRange(settingsAxes.axisMomentum); + etaRange = getRange(settingsAxes.axisEta); + yRange = getRange(settingsAxes.axisY); + prodXYrange = getRange(settingsAxes.axisProdXY); + prodZRange = getRange(settingsAxes.axisProdZ); LOG(info) << "Selecting particles of interest in pT [" << ptRange.min << ", " << ptRange.max << "), eta [" << etaRange.min << ", " << etaRange.max << "), y [" << yRange.min << ", " << yRange.max << "), production z [" << prodZRange.min << ", " << prodZRange.max << ")"; const TString titlePdgCode = Form("%i", pdgCode.value); const AxisSpec axisPDGs{100, 0.f, 100.f, "PDG code"}; const AxisSpec axisCharge{21, -10.5f, 10.5f, "Charge (e/3)"}; + const AxisSpec axisP{settingsAxes.axisMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt{settingsAxes.axisMomentum, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtMC{settingsAxes.axisMomentum, "#it{p}_{T}^{MC} (GeV/#it{c})"}; + const AxisSpec axisPx{settingsAxes.axisMomentumComponent, "#it{p}_{x} (GeV/#it{c})"}; + const AxisSpec axisPy{settingsAxes.axisMomentumComponent, "#it{p}_{y} (GeV/#it{c})"}; + const AxisSpec axisPz{settingsAxes.axisMomentumComponent, "#it{p}_{z} (GeV/#it{c})"}; + const AxisSpec axisEta{settingsAxes.axisEta, "#it{#eta}"}; + const AxisSpec axisY{settingsAxes.axisY, "#it{y}"}; + const AxisSpec axisE{etaRange.getBins(), 0, 1000, "E"}; + const AxisSpec axisProdx{settingsAxes.axisProdXY, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{settingsAxes.axisProdXY, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{settingsAxes.axisProdZ, "Prod. Vertex Z (cm)"}; + const AxisSpec axisProdRadius{prodXYrange.getBins(), 0., 2. * prodXYrange.max, "Prod. Vertex Radius (cm)"}; + const AxisSpec axisDistx{settingsAxes.distanceAxis, "Distance X (fm)"}; + const AxisSpec axisDisty{settingsAxes.distanceAxis, "Distance Y (fm)"}; + const AxisSpec axisDistz{settingsAxes.distanceAxis, "Distance Z (fm)"}; + const AxisSpec axisProdDistance{settingsAxes.distanceAxis, "Distance Radius (fm)"}; + const AxisSpec axisVx{settingsAxes.axisVtxXY, "Vertex X (cm)"}; + const AxisSpec axisVy{settingsAxes.axisVtxXY, "Vertex Y (cm)"}; + const AxisSpec axisVz{settingsAxes.axisVtxZ, "Vertex Z (cm)"}; histos.add("AnalysedParticlePdgCode", "Analysed particle PDG code", kTH1D, {{1, -0.5, 0.5}}); histos.get(HIST("AnalysedParticlePdgCode"))->SetBinContent(1, pdgCode); @@ -138,11 +164,11 @@ struct Alice3SingleParticle { histos.add("AnalysedParticleMass", "Analysed particle mass", kTH1D, {{1, -0.5, 0.5}}); histos.get(HIST("AnalysedParticleMass"))->SetBinContent(1, particle ? particle->Mass() : -1.); - histos.add("event/VtxX", "Vertex X", kTH1D, {axes.axisVtxXY}); - histos.add("event/VtxY", "Vertex Y", kTH1D, {axes.axisVtxXY}); - histos.add("event/VtxZ", "Vertex Z", kTH1D, {axes.axisVtxZ}); - histos.add("event/Nch", "Events;N_{ch};Events", kTH1D, {axes.axisNch}); - histos.add("particle/NSignalVsNch", "Particles of interest per event " + titlePdgCode + ";N_{ch};N_{signal}", kTH2D, {axes.axisNch, axes.axisNSignal}); + histos.add("event/VtxX", "Vertex X", kTH1D, {axisVx}); + histos.add("event/VtxY", "Vertex Y", kTH1D, {axisVy}); + histos.add("event/VtxZ", "Vertex Z", kTH1D, {axisVz}); + histos.add("event/Nch", "Events;N_{ch};Events", kTH1D, {settingsAxes.axisNch}); + histos.add("particle/NSignalVsNch", "Particles of interest per event " + titlePdgCode + ";N_{ch};N_{signal}", kTH2D, {settingsAxes.axisNch, settingsAxes.axisNSignal}); histos.add("particle/PDGs", "Particle PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsPrimaries", "Particle PDGs of Primaries", kTH2D, {axisPDGs, axisCharge}); @@ -151,90 +177,93 @@ struct Alice3SingleParticle { for (int i = 0; i < kMaxMCProcess; i++) { h->GetXaxis()->SetBinLabel(i + 1, TMCProcessName[i]); } - histos.add("particle/Pt", "Particle Pt " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/P", "Particle P " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/primariesPt", "Particle Pt (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/primariesP", "Particle P (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/secondariesPt", "Particle Pt (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/secondariesP", "Particle P (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("particle/prodVx", "Particle Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/prodVy", "Particle Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/prodVz", "Particle Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); - histos.add("particle/prodRadius", "Particle Prod. Vertex Radius " + titlePdgCode, kTH1D, {axes.axisProdRadius}); - histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); - histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); - histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); - histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + titlePdgCode, kTH2D, {axes.axisProdXY, axes.axisProdXY}); - histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdXY}); - histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdXY}); - histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdZ}); - histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); - histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisEta, axes.axisProdRadius}); - histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); - histos.add("particle/Eta", "Particle Eta " + titlePdgCode, kTH1D, {axes.axisEta}); - histos.add("particle/primariesEta", "Particle Eta (primary) " + titlePdgCode, kTH1D, {axes.axisEta}); - histos.add("particle/secondariesEta", "Particle Eta (secondary) " + titlePdgCode, kTH1D, {axes.axisEta}); - histos.add("particle/Y", "Particle Y " + titlePdgCode, kTH1D, {axes.axisY}); - histos.add("particle/primariesY", "Particle Y (primary)" + titlePdgCode, kTH1D, {axes.axisY}); - histos.add("particle/secondariesY", "Particle Y (secondary)" + titlePdgCode, kTH1D, {axes.axisY}); - histos.add("particle/EvsPz", "Particle E vs Pz " + titlePdgCode, kTH2D, {axes.axisEnergy, axes.axisMomentumComponent}); - histos.add("particle/YvzPz", "Particle Y vs Pz " + titlePdgCode, kTH2D, {axes.axisY, axes.axisMomentumComponent}); - histos.add("particle/EtavzPz", "Particle Eta vs Pz " + titlePdgCode, kTH2D, {axes.axisEta, axes.axisMomentumComponent}); - histos.add("particle/PtvzPz", "Particle Pt vs Pz " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentumComponent}); - histos.add("particle/PvzPz", "Particle P vs Pz " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentumComponent}); - histos.add("particle/Px", "Particle Px " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); - histos.add("particle/Py", "Particle Py " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); - histos.add("particle/Pz", "Particle Pz " + titlePdgCode, kTH1D, {axes.axisMomentumComponent}); - histos.add("particle/decayDistanceX", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/decayDistanceY", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/decayDistanceZ", "Particle decay distance " + titlePdgCode, kTH1D, {axes.axisProdZ}); - histos.add("particle/decayDistanceVsPt", "Particle decay distance " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/Pt", "Particle Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/P", "Particle P " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/primariesPt", "Particle Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/primariesP", "Particle P (primary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/secondariesPt", "Particle Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/secondariesP", "Particle P (secondary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/prodVx", "Particle Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/prodVy", "Particle Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/prodVz", "Particle Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/prodRadius", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodRadius3D", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodDistance", "Particle Prod. Vertex Distance XY " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodDistance3D", "Particle Prod. Vertex Distance XYZ " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + titlePdgCode, kTH2D, {axisPt, axisProdx}); + histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + titlePdgCode, kTH2D, {axisPt, axisPrody}); + histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + titlePdgCode, kTH2D, {axisPt, axisProdz}); + histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisEta, axisProdRadius}); + histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Eta", "Particle Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/primariesEta", "Particle Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/secondariesEta", "Particle Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/Y", "Particle Y " + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/primariesY", "Particle Y (primary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/secondariesY", "Particle Y (secondary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/EvsPz", "Particle E vs Pz " + titlePdgCode, kTH2D, {axisE, axisPz}); + histos.add("particle/YvzPz", "Particle Y vs Pz " + titlePdgCode, kTH2D, {axisY, axisPz}); + histos.add("particle/EtavzPz", "Particle Eta vs Pz " + titlePdgCode, kTH2D, {axisEta, axisPz}); + histos.add("particle/PtvzPz", "Particle Pt vs Pz " + titlePdgCode, kTH2D, {axisPt, axisPz}); + histos.add("particle/PvzPz", "Particle P vs Pz " + titlePdgCode, kTH2D, {axisP, axisPz}); + histos.add("particle/Px", "Particle Px " + titlePdgCode, kTH1D, {axisPx}); + histos.add("particle/Py", "Particle Py " + titlePdgCode, kTH1D, {axisPy}); + histos.add("particle/Pz", "Particle Pz " + titlePdgCode, kTH1D, {axisPz}); + histos.add("particle/decayDistanceX", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/decayDistanceY", "Particle decay distance " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/decayDistanceZ", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/decayDistanceVsPt", "Particle decay distance " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); histos.add("particle/decayCtau", "Particle decay distance " + titlePdgCode, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); histos.add("particle/daughters/Number", "Number of Daughters " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); histos.add("particle/daughters/PDGs", "Daughters PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); - histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); - histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); histos.add("particle/mothers/Number", "Number of Mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); histos.add("particle/mothers/PDGs", "Mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); - histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); - histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); // Go up one generation histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axes.axisProdXY}); - histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axes.axisProdZ}); - histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); - histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisProdRadius}); + histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); if (doprocessParticleOnly) { return; } histos.add("track/PDGs", "Track PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axes.axisMomentum}); + histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axisPt}); histos.add("track/tofPDGs", "Track wTOF PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/Pt", "Track Pt " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("track/primariesPt", "Track Pt (primary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("track/secondariesPt", "Track Pt (secondary) " + titlePdgCode, kTH1D, {axes.axisMomentum}); - histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + titlePdgCode, kTH2D, {axes.axisMomentum, axes.axisMomentum}); - histos.add("track/Eta", "Track Eta " + titlePdgCode, kTH1D, {axes.axisEta}); - histos.add("track/primariesEta", "Track Eta (primary) " + titlePdgCode, kTH1D, {axes.axisEta}); - histos.add("track/secondariesEta", "Track Eta (secondary) " + titlePdgCode, kTH1D, {axes.axisEta}); + histos.add("track/Pt", "Track Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/primariesPt", "Track Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/secondariesPt", "Track Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + titlePdgCode, kTH2D, {axisPt, axisPt}); + histos.add("track/Eta", "Track Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/primariesEta", "Track Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/secondariesEta", "Track Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); histos.add("track/primaries", "Source for primaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); histos.add("track/secondaries", "Source for secondaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); } @@ -273,7 +302,6 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/NSignalVsNch"), nchPerCollision[iCollision], nSignalPerCollision[iCollision]); } } - // Track histograms. Must be called after genericProcessParticles, which fills particlesOfInterest. template void genericProcessTracks(const TTracks& tracks) From b6e8f900304320e26e11c60e4f33e5ffd06a3ca9 Mon Sep 17 00:00:00 2001 From: Mattia Faggin Date: Thu, 22 Oct 2026 20:17:42 +0200 Subject: [PATCH 395/648] PWGHF: bug fix in treeCreatorD0: swap D0 prompt and D0 non-prompt ML scores. (#18413) Co-authored-by: Mattia Faggin --- PWGHF/TableProducer/treeCreatorD0ToKPi.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx index 4200d499db8..03f97287433 100644 --- a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx @@ -228,8 +228,8 @@ DECLARE_SOA_TABLE(HfCandD0FullPs, "AOD", "HFCANDD0FULLP", DECLARE_SOA_TABLE(HfCandD0Mls, "AOD", "HFCANDD0ML", ml::BdtOutputBkg, - ml::BdtOutputNonPrompt, - ml::BdtOutputPrompt); + ml::BdtOutputPrompt, + ml::BdtOutputNonPrompt); } // namespace o2::aod From b81a350ca9ba72d578b8b8ada99dfdfea7dff595 Mon Sep 17 00:00:00 2001 From: Yash Patley <52608802+yashpatley@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:04:44 +0530 Subject: [PATCH 396/648] [PWGCF] lambda R2 correlation update for on the fly MC (#18415) --- .../Tasks/lambdaR2Correlation.cxx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index 0b25d067843..1cddee698c9 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -926,7 +926,34 @@ struct LambdaTableProducer { void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { - fillLambdaMcGenTables(mcCollision, mcParticles); + // Fill McGen Collision Table + lambdaMCGenCollisionTable(cent, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + + // initialize track objects + ParticleType partType = kLambda; + + // Loop over MC particles + for (auto const& mcpart : mcParticles) { + // Check for Primary Lambda/Anti-Lambda + if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0) { + partType = kLambda; + } else if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0Bar) { + partType = kAntiLambda; + } else { + continue; + } + + // Kinematic selection + if (mcpart.pt() <= cLambdaMinPt || mcpart.pt() >= cLambdaMaxPt || std::abs(mcpart.y()) >= cLambdaRapCut) { + continue; + } + + histos.fill(HIST("Tracks/h1f_lambda_info"), kGenTotAccLambda); + + // Fill table + lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), + -1, -1, (int8_t)partType, 1.); + } } PROCESS_SWITCH(LambdaTableProducer, processMCGen, "Process for MC Gen", false); From 720874e7d236da494e20a6964cd018a35ef1df1c Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Thu, 22 Oct 2026 22:22:50 +0200 Subject: [PATCH 397/648] [PWGLF,PWGMM] Include strange decays in reconstructed MC (#18418) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 430 +++++++++++++++++++++++------ 1 file changed, 351 insertions(+), 79 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 6ad1b5e086b..fe38f76e522 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -135,7 +136,7 @@ enum class AmbTrkType { nAmbTrkType }; -enum class GenTrkType { +enum class McGenTrkType { kGenAll = 0, kGenRecEvt, nGenTrkType @@ -148,6 +149,11 @@ enum class GenIdxTrkType { nGenIdxTrkType }; +enum class GenIdxTrkLightType { + kGenIdxAll = 0, + nGenIdxTrkType +}; + enum class RecTrkType { kRecAll = 0, kRecFake, @@ -161,6 +167,12 @@ enum class RecIdxTrkType { nRecIdxTrkType }; +enum class RecIdxTrkLightType { + kRecIdxPrim = 0, + kRecIdxSec, + nRecIdxTrkType +}; + enum class EvtLossType { kGenAll = 0, kGenSel, @@ -208,6 +220,7 @@ enum class McTrackStatus { kTrkAsBestSel, kTrkHasColl, kTrkReassignedRemoved, + kTrkFake, nMcTrackStatus }; @@ -216,6 +229,15 @@ enum class OccupancyEst { Ft0C }; +enum class McRecTrkType { + kPrimary = 0, + kSecondary, + kStrangeDecay, + kTrkSel, + kTrkFake, + nMcRecTrkStatus +}; + struct DndetaMFTPbPb { SliceCache cache; @@ -366,10 +388,12 @@ struct DndetaMFTPbPb { const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; const AxisSpec invQPtAxis{binOpt.invQPtBins, "q/p_{T} (1/GeV)"}; - const AxisSpec genEvtTypeAxis = {static_cast(GenTrkType::nGenTrkType), -0.5, +static_cast(GenTrkType::nGenTrkType) - 0.5, "", "Gen-Trk Type axis"}; - const AxisSpec recEvtTypeAxis = {static_cast(RecTrkType::nRecTrkType), -0.5, +static_cast(RecTrkType::nRecTrkType) - 0.5, "", "Rec-Trk Type axis"}; + const AxisSpec recTrkTypeAxis = {static_cast(McRecTrkType::nMcRecTrkStatus), -0.5, +static_cast(McRecTrkType::nMcRecTrkStatus) - 0.5, "", "MC Rec-Trk Type axis"}; + const AxisSpec genTrkTypeAxis = {static_cast(McGenTrkType::nGenTrkType), -0.5, +static_cast(McGenTrkType::nGenTrkType) - 0.5, "", "MC Gen-Trk Type axis"}; const AxisSpec genEvtIdxTypeAxis = {static_cast(GenIdxTrkType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Type axis"}; const AxisSpec recEvtIdxTypeAxis = {static_cast(RecIdxTrkType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Type axis"}; + const AxisSpec genEvtIdxLightTypeAxis = {static_cast(GenIdxTrkLightType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkLightType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Light Type axis"}; + const AxisSpec recEvtIdxLightTypeAxis = {static_cast(RecIdxTrkLightType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkLightType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Light Type axis"}; const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType), -0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -401,9 +425,15 @@ struct DndetaMFTPbPb { if (static_cast(doprocessMcEfficiencyIdxInlusive) + static_cast(doprocessMcEfficiencyIdxBestInlusive) > 1) { LOGP(fatal, "Either doprocessMcEfficiencyIdxInlusive OR doprocessMcEfficiencyIdxBestInlusive should be enabled!"); } + if (static_cast(doprocessMcEfficiencyIdxBestInlusive) + static_cast(doprocessMcEfficiencyIdxBestLightInlusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestInlusive OR doprocessMcEfficiencyIdxBestLightInlusive should be enabled!"); + } if (static_cast(doprocessMcEfficiencyIdxCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestCentFT0C) > 1) { LOGP(fatal, "Either doprocessMcEfficiencyIdxCentFT0C OR doprocessMcEfficiencyIdxBestCentFT0C should be enabled!"); } + if (static_cast(doprocessMcEfficiencyIdxBestCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestLightCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestCentFT0C OR doprocessMcEfficiencyIdxBestLightCentFT0C should be enabled!"); + } registryQC.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); std::array(EvtSel::nEvtSel)> labelEvtSel{ @@ -442,6 +472,7 @@ struct DndetaMFTPbPb { doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C || doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxCentFT0C || + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); std::array(TrkSel::nTrkSel)> labelTrkSel{ @@ -463,6 +494,7 @@ struct DndetaMFTPbPb { doprocessMcBestInclusive || doprocessMcBestCentFT0C || doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyBestCentFT0C || doprocessMcEfficiencyIdxBestInlusive || doprocessMcEfficiencyIdxBestCentFT0C || + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || doprocessDataCorrelationwBestTracksInclusive) { registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ @@ -565,14 +597,17 @@ struct DndetaMFTPbPb { "Best sel", "Trk sel", "Has coll", - "Reas rm"}; + "Reas rm", + "Fake"}; for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { registryMC.get(HIST("Tracks/hMcTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcTrackStatus[iBin].data()); } - registryMC.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); + registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } } - registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); @@ -606,14 +641,17 @@ struct DndetaMFTPbPb { "Best sel", "Trk sel", "Has coll", - "Reas rm"}; + "Reas rm", + "Fake"}; for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCentBest[iBin].data()); } - registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } } - registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); @@ -640,10 +678,10 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); } - registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); @@ -658,28 +696,42 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); } - registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRecClosure", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recEvtTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); } if (doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxBestInlusive) { - registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {{10, 0.5, 10.5}, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiencyIdx[Best]Inlusive -> Size of the MC histograms:"; registryMC.print(); } if (doprocessMcEfficiencyIdxCentFT0C || doprocessMcEfficiencyIdxBestCentFT0C) { - registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTHnSparseF, {{10, 0.5, 10.5}, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiencyIdx[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); } + if (doprocessMcEfficiencyIdxBestLightInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightInlusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxBestLightCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightCentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } if (doprocessMcSgnEvtLossCentFT0C) { registryMC.add("Events/hMcEvtLossStatus", "Number of events; Cut", {HistType::kTH1F, {{static_cast(McEvtLossStatus::kMcEvtStatus), -0.5, +static_cast(McEvtLossStatus::kMcEvtStatus) - 0.5}}}); std::array(McEvtLossStatus::kMcEvtStatus)> labelMcEvtLossStatus{ @@ -1665,27 +1717,47 @@ struct DndetaMFTPbPb { } float phi = track.phi(); o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), occRec); + } if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - if (gConf.useParticleSel && !isParticleSelected(particle)) { + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { continue; } - if (collision.mcCollisionId() != particle.mcCollisionId()) { - continue; + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), occRec); registryMC.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occRec); registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); } ++nTrkRec; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } } } if (eventCuts.useZDiffCut) { @@ -1875,25 +1947,18 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); } else { registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), occRec); } if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.useParticleSel && !isParticleSelected(particle)) { - continue; - } if (eventCuts.useZDiffCut) { if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { continue; } } - // if (collision.mcCollisionId() != particle.mcCollisionId()) { - // continue; - // } if (eventCuts.checkCollisionId) { if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { continue; @@ -1902,20 +1967,47 @@ struct DndetaMFTPbPb { continue; } } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); + } } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(particleOrigin), occRec); registryMC.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occRec); registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); - registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); + registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); + } } ++nATrk; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } } } if (eventCuts.useZDiffCut) { @@ -2005,14 +2097,14 @@ struct DndetaMFTPbPb { cGen = cRec; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } else { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { @@ -2024,16 +2116,16 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } @@ -2078,29 +2170,43 @@ struct DndetaMFTPbPb { if (!isTrackSelected(track)) { continue; } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - if (gConf.useParticleSel && !isParticleSelected(particle)) { + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { continue; } - if (collision.mcCollisionId() != particle.mcCollisionId()) { - continue; + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); - registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2160,14 +2266,14 @@ struct DndetaMFTPbPb { cGen = cRec; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } else { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } for (const auto& particle : particles) { @@ -2179,16 +2285,16 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(GenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } @@ -2246,14 +2352,13 @@ struct DndetaMFTPbPb { continue; } } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.useParticleSel && !isParticleSelected(particle)) { - continue; - } if (eventCuts.useZDiffCut) { if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { continue; @@ -2267,18 +2372,33 @@ struct DndetaMFTPbPb { continue; } } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); - registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(RecTrkType::kRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(RecTrkType::kRecFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2629,6 +2749,158 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); + template + void processEfficiencyIdxBestLight(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& /*tracks*/, + MftBestTracksLabeled const& /*atracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + cRec = getRecoCent(collision); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } + + buildLookupTable(collisions); + + for (const auto& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + // MC gen + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll), cGen); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll)); + } + } + } + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& atrack : relatedTracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + if (!itrack.has_collision()) { + continue; + } + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + ++ncnt; + if constexpr (has_reco_cent) { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim), cRec); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec), cRec); + } + } + } else { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim)); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec)); + } + } + } + } + } + } + } + } + } + + void processMcEfficiencyIdxBestLightInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightInlusive, "Process tracking efficiency best light (inclusive, indexed)", false); + + void processMcEfficiencyIdxBestLightCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightCentFT0C, "Process tracking efficiency best light (in FT0C centrality bins, indexed)", false); + void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::McParticles const& particles) From 8c849405a4bef60a0d3fd66972d3f5ac6a50f641 Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Thu, 22 Oct 2026 23:08:43 +0200 Subject: [PATCH 398/648] [PWGEM] PhotonMeson: Add signal loss task for mesons (#18408) Co-authored-by: jokonig --- PWGEM/PhotonMeson/DataModel/EventTables.h | 18 + PWGEM/PhotonMeson/Tasks/CMakeLists.txt | 4 + .../Tasks/eventNormalizationTask.cxx | 23 +- PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx | 433 ++++++++++++++++++ 4 files changed, 459 insertions(+), 19 deletions(-) create mode 100644 PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx diff --git a/PWGEM/PhotonMeson/DataModel/EventTables.h b/PWGEM/PhotonMeson/DataModel/EventTables.h index 0d58611d8a5..dd24a33457b 100644 --- a/PWGEM/PhotonMeson/DataModel/EventTables.h +++ b/PWGEM/PhotonMeson/DataModel/EventTables.h @@ -29,6 +29,7 @@ #include // for BIT #include +#include #include namespace o2::aod @@ -61,6 +62,23 @@ enum EventAcceptanceBits { kSize }; +const std::vector vecEventAccBitNames = { + "all", + "has MC coll", + "good zVtx", + "is FT0AND", + "No TFB", + "ITS ROFB", + "Same bunch Pileup", + "ZVtxFT0PV", + "No coll in time range", + "good track occi", + "Good FT0 occupancy", + "kTVXinEMC", + "Good centrality", + "Good RCT", + "Good Sel8"}; + } // namespace pmevsel namespace pmevent diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index cdb29b26af8..cd9513dcbe3 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -221,3 +221,7 @@ o2physics_add_dpl_workflow(dilepton-effi-for-dalitz-skimmer PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(meson-signal-loss + SOURCES mesonSignalLoss.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx index 1c5b4bc8eaa..5ac8343abf8 100644 --- a/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx @@ -43,25 +43,10 @@ struct EventNormalizationTask { void init(InitContext& /*context*/) { // Names derived from EventAcceptance bits in EventTables.h - std::vector vecLabelNames = { - "all", - "has MC coll", - "good zVtx", - "is FT0AND", - "No TFB", - "ITS ROFB", - "Same bunch Pileup", - "ZVtxFT0PV", - "No coll in time range", - "good track occi", - "Good FT0 occupancy", - "kTVXinEMC", - "Good centrality", - "Good RCT", - "Good Sel8"}; - fRegistry.add("EventNormalization/hEventCounter", "Event Counter;category;#it{N}_{evt}", kTH1D, {{static_cast(vecLabelNames.size()), -0.5, -0.5 + static_cast(vecLabelNames.size())}}, false); - for (size_t i = 0; i < vecLabelNames.size(); ++i) { - fRegistry.get(HIST("EventNormalization/hEventCounter"))->GetXaxis()->SetBinLabel(i + 1, vecLabelNames[i].c_str()); + + fRegistry.add("EventNormalization/hEventCounter", "Event Counter;category;#it{N}_{evt}", kTH1D, {{static_cast(o2::aod::pmevsel::vecEventAccBitNames.size()), -0.5, -0.5 + static_cast(o2::aod::pmevsel::vecEventAccBitNames.size())}}, false); + for (size_t i = 0; i < o2::aod::pmevsel::vecEventAccBitNames.size(); ++i) { + fRegistry.get(HIST("EventNormalization/hEventCounter"))->GetXaxis()->SetBinLabel(i + 1, o2::aod::pmevsel::vecEventAccBitNames[i].c_str()); } } diff --git a/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx new file mode 100644 index 00000000000..3973884823d --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx @@ -0,0 +1,433 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// Calculate signal and event loss for neutral mesons and photons +// Joshua König, joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct MesonSignalLoss { + + using MyBCs = soa::Join; + + using RecoCollisions = soa::Join; + using EventAccBits = o2::aod::pmevsel::EventAcceptanceBits; + + enum MultiplicityEstimator { kCentralBarrel, + kFT0A, + kFT0C }; + + // stage kStageHasReco + k means "passed event cut k of EventAccBits" + enum Stage { kStageAllMC = 0, + kStageTVX, + kStageHasReco }; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + Preslice mcCollisionsPerBC = aod::mccollision::bcId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoCollisionsPerMCCollision = aod::mccollisionlabel::mcCollisionId; + + struct : ConfigurableGroup { + std::string prefix = "axisConfig"; + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {400, 0.f, 20.f}, "pT axis for generated particles"}; + ConfigurableAxis cfgMultiplicityAxis{"cfgMultiplicityAxis", {200, -0.5f, 199.5f}, "axis for true charged-particle multiplicity"}; + } axisConfig; + + struct : ConfigurableGroup { + std::string prefix = "particleConfig"; + Configurable cfgRapidity{"cfgRapidity", 0.8, "rapidity selection"}; + Configurable cfgSecondaryDecayLength{"cfgSecondaryDecayLength", 1.f, "Minimum ancestor decay length (cm) to classify a particle as secondary"}; + Configurable cfgEnableMultiplicityHistograms{"cfgEnableMultiplicityHistograms", false, "enable generated-particle and event-stage histograms versus true charged-particle multiplicity"}; + Configurable cfgMultiplicityEstimator{"cfgMultiplicityEstimator", "centralBarrel", "eta range for true multiplicity: centralBarrel, FT0A, or FT0C"}; + } particleConfig; + + struct : ConfigurableGroup { + std::string prefix = "rctConfig"; + Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfig; + + struct : ConfigurableGroup { + std::string prefix = "eventCuts"; + Configurable cfgZvtxMin{"cfgZvtxMin", -1e+10, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", 1e+10, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", false, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + Configurable cfgRequireTVXinEMC{"cfgRequireTVXinEMC", false, "require kTVXinEMC (only for EMC analyses)"}; + Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 condition"}; + } eventCuts; + + Service pdgDB{}; + o2::aod::rctsel::RCTFlagsChecker rctChecker; + MultiplicityEstimator multiplicityEstimator{kCentralBarrel}; + + /// \brief Creates the histograms used for signal-loss and event-selection analysis. + void Inithistograms() + { + registry.add("hBCCounter", "hBCCounter;;#it{N}_{BC}", kTH1F, {{3, -0.5, 3 - 0.5}}); + auto h = registry.get(HIST("hBCCounter")); + h->GetXaxis()->SetBinLabel(1, "All BCs"); + h->GetXaxis()->SetBinLabel(2, "kTVX fired"); + h->GetXaxis()->SetBinLabel(3, "kTVX fired && has MC collision"); + + registry.add("hMCCollStage", "hMCCollStage;;#it{N}_{MC coll}", kTH1F, {{20, -0.5, 20 - 0.5}}); + auto hStage = registry.get(HIST("hMCCollStage")); + hStage->GetXaxis()->SetBinLabel(kStageAllMC + 1, "all MC coll"); + hStage->GetXaxis()->SetBinLabel(kStageTVX + 1, "kTVX fired"); + hStage->GetXaxis()->SetBinLabel(kStageHasReco + 1, "has reco coll"); + for (int cut = 1; cut < EventAccBits::kSize; ++cut) { + hStage->GetXaxis()->SetBinLabel(kStageHasReco + cut + 1, o2::aod::pmevsel::vecEventAccBitNames[cut].c_str()); + } + + registry.add("QC/hNRecoPerMCColl", "reco collisions per MC collision;N_{reco};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNSelectedRecoPerMCColl", "selected reco collisions per MC collision;N_{sel};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNContribVsOrder", "N contributors;order in matched list;N_{contrib}", kTH2F, {{10, -0.5, 9.5}, {200, 0, 200}}); + + const AxisSpec ptAxis{axisConfig.cfgPtAxis, "p_{T} (GeV/c)"}; + const AxisSpec multAxis{axisConfig.cfgMultiplicityAxis, "N_{ch}^{true}"}; + registry.add("Generated/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.add("Generated/Multiplicity/hMCCollStage", "MC collision stages vs. true multiplicity;N_{ch}^{true};selection stage", kTH2F, {multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + } + } + + /// \brief Configures the multiplicity estimator and initializes histograms. + /// \param[in] context Workflow initialization context. + void init([[maybe_unused]] InitContext& context) + { + // Init multiplicity estimator (only relevant for multiplicity dependent analyses) + if (particleConfig.cfgMultiplicityEstimator.value == "centralBarrel") { + multiplicityEstimator = kCentralBarrel; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0A") { + multiplicityEstimator = kFT0A; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0C") { + multiplicityEstimator = kFT0C; + } else if (particleConfig.cfgEnableMultiplicityHistograms) { + LOGF(fatal, "Unknown cfgMultiplicityEstimator '%s'; use centralBarrel, FT0A, or FT0C", particleConfig.cfgMultiplicityEstimator.value.c_str()); + } + Inithistograms(); + } + + /// \brief Checks whether eta lies in the configured multiplicity-estimator range. + /// \param[in] eta Pseudorapidity to check. + /// \return True if eta is inside the configured range, otherwise false. + bool isInMultiplicityEtaRange(float eta) const + { + bool decision = false; + if (multiplicityEstimator == kCentralBarrel) { + decision = std::abs(eta) < 0.8f; // o2-linter: disable=magic-number (rapidity range of central barrel) + } else if (multiplicityEstimator == kFT0A) { + decision = 3.5f < eta && eta < 4.9f; // o2-linter: disable=magic-number (rapidity range of FT0) + } else if (multiplicityEstimator == kFT0C) { + decision = -3.3f < eta && eta < -2.1f; // o2-linter: disable=magic-number (rapidity range of FT0) + } + return decision; + } + + /// \brief Counts charged physical-primary particles in the configured eta range. + /// \param[in] particles Particles to include in the multiplicity count. + /// \return Number of charged physical-primary particles in range. + template + int countTrueMultiplicity(TParticles const& particles) const + { + int multiplicity = 0; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || !isInMultiplicityEtaRange(particle.eta())) { + continue; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + if (pdgParticle && std::abs(pdgParticle->Charge()) >= 1.) { // o2-linter: disable=magic-number (check that particle has electric charge) + ++multiplicity; + } + } + return multiplicity; + } + + /// \brief Determines whether a particle has a secondary-production origin. + /// \param[in] particle Particle whose origin is checked. + /// \param[in] mcParticles MC-particle table used to trace the particle ancestry. + /// \return True if the particle is classified as secondary, otherwise false. + bool isSecondary(aod::McParticle const& particle, aod::McParticles const& mcParticles) const + { + std::vector particlesToCheck{particle.globalIndex()}; + std::unordered_set checkedParticles; + auto currentParticle = mcParticles.begin(); + auto mother = mcParticles.begin(); + + while (!particlesToCheck.empty()) { + const auto particleIndex = particlesToCheck.back(); + particlesToCheck.pop_back(); + if (particleIndex < 0 || particleIndex >= mcParticles.size() || !checkedParticles.insert(particleIndex).second) { + continue; + } + + currentParticle.setCursor(particleIndex); + const auto process = currentParticle.getProcess(); + const bool fromDecay = currentParticle.producedByGenerator() || process == TMCProcess::kPDecay || process == TMCProcess::kPRadDecay; + if (!currentParticle.producedByGenerator() && !fromDecay) { + return true; + } + + for (const auto motherIndex : currentParticle.mothersIds()) { + if (motherIndex < 0 || motherIndex >= mcParticles.size()) { + continue; + } + mother.setCursor(motherIndex); + const float decayLength = std::sqrt(std::pow(currentParticle.vx() - mother.vx(), 2) + std::pow(currentParticle.vy() - mother.vy(), 2) + std::pow(currentParticle.vz() - mother.vz(), 2)); + if (fromDecay && decayLength > particleConfig.cfgSecondaryDecayLength.value) { + return true; + } + particlesToCheck.push_back(motherIndex); + } + } + return false; + } + + /// \brief Fills generated-particle histograms for the requested selection stages. + /// \param[in] particlesInMCCollision Particles associated with the MC collision. + /// \param[in] mcParticles MC-particle table used to inspect particle ancestry. + /// \param[in] selectionStage First selection stage to fill. + /// \param[in] selectionStageMax Last selection stage to fill. + /// \param[in] trueMultiplicity True charged-particle multiplicity of the collision. + template + void fillGeneratedParticles(TParticles const& particlesInMCCollision, aod::McParticles const& mcParticles, int selectionStage, int selectionStageMax, int trueMultiplicity) + { + if (selectionStageMax < selectionStage) { + selectionStageMax = selectionStage; + } + for (const auto& particle : particlesInMCCollision) { + if (std::abs(particle.y()) > particleConfig.cfgRapidity) { + continue; + } + if (isSecondary(particle, mcParticles)) { + continue; + } + for (int iSel = selectionStage; iSel <= selectionStageMax; ++iSel) { + switch (particle.pdgCode()) { + case PDG_t::kPi0: + registry.fill(HIST("Generated/Pi0/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Pi0/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case o2::constants::physics::Pdg::kEta: + registry.fill(HIST("Generated/Eta/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Eta/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case PDG_t::kGamma: + registry.fill(HIST("Generated/Gamma/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Gamma/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + default: + break; + } + } + } + } + + /// \brief Returns the event-acceptance bit for the first failed collision selection. + /// \param[in] collision Reconstructed collision to evaluate. + /// \return Event-acceptance bit identifying the first failed cut, or kSize if accepted. + template + int isSelectedEvent(TCollision const& collision) + { + if (!collision.has_mcCollision()) { + return EventAccBits::kHasMCColl; + } + + if (collision.posZ() < eventCuts.cfgZvtxMin || eventCuts.cfgZvtxMax < collision.posZ()) { + return EventAccBits::kGoodZVtx; + } + + if (eventCuts.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return EventAccBits::kIsFT0AND; + } + + if (eventCuts.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return EventAccBits::kNoTFB; + } + + if (eventCuts.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return EventAccBits::kITSROFB; + } + + if (eventCuts.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return EventAccBits::kNoSameBunchPileUp; + } + + if (eventCuts.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return EventAccBits::kGoodZVtxFTOPV; + } + + if (eventCuts.cfgRequireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return EventAccBits::kNoCollInTimeRange; + } + + if (!(eventCuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventCuts.cfgTrackOccupancyMax)) { + return EventAccBits::kGoodTrackOccupancy; + } + + if (!(eventCuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventCuts.cfgFT0COccupancyMax)) { + return EventAccBits::kGoodFT0Occupancy; + } + + if (eventCuts.cfgRequireTVXinEMC && !collision.alias_bit(triggerAliases::kTVXinEMC)) { + return EventAccBits::kTVXInEMC; + } + + // if constexpr (std::is_same_v, MyCollisions_Cent::iterator>) { + // std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + // if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + // return false; + // } + // } + + if (rctConfig.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + return EventAccBits::kGoodRCT; + } + + if (eventCuts.cfgRequireSel8 && !collision.sel8()) { + return EventAccBits::kGoodSel8; + } + + return EventAccBits::kSize; + } + + /// \brief Processes MC bunch crossings and fills generated signal-loss histograms. + /// \param[in] bcs Bunch crossings to process. + /// \param[in] collisions Reconstructed collisions and their MC labels. + /// \param[in] mcCollisions MC collisions to process. + /// \param[in] mcParticles MC particles used for multiplicity and ancestry checks. + void processMBMC(MyBCs const& bcs, RecoCollisions const& collisions, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + { + // loop over all bcs + for (const auto& bc : bcs) { + registry.fill(HIST("hBCCounter"), 0); + + // Check if TVX trigger fired in that bc + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + registry.fill(HIST("hBCCounter"), 1); + } + + auto mcCollisionsInBC = mcCollisions.sliceBy(mcCollisionsPerBC, bc.globalIndex()); + // check if has at least one mc collision + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX) && mcCollisionsInBC.size() > 0) { + registry.fill(HIST("hBCCounter"), 2); + } + + // loop over all mc collisions in the current bc (should be only 1 mc collision in most cases) + for (const auto& mcCollision : mcCollisionsInBC) { + int lastStage = kStageAllMC; + auto particlesInMCCollision = mcParticles.sliceBy(particlesPerMCCollision, mcCollision.globalIndex()); + const int trueMultiplicity = particleConfig.cfgEnableMultiplicityHistograms ? countTrueMultiplicity(particlesInMCCollision) : 0; + + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + lastStage = kStageTVX; + + auto matchedRecoCollisions = collisions.sliceBy(recoCollisionsPerMCCollision, mcCollision.globalIndex()); + registry.fill(HIST("QC/hNRecoPerMCColl"), matchedRecoCollisions.size()); + + // if at least one reco collision is found for the current mc collision, loop over all reco collisions and check if the collision is selected + if (matchedRecoCollisions.size() > 0) { + + int bestReject = EventAccBits::kHasMCColl; + int nSelected = 0; + int order = 0; + for (const auto& recColl : matchedRecoCollisions) { + // this function returns if the collision is accepted or not. If not, the reason is encoded in the return value + const int reject = isSelectedEvent(recColl); + // furthest-passing matched collision decides, so split vertices are not double counted + bestReject = std::max(bestReject, reject); + if (reject == EventAccBits::kSize) { + ++nSelected; + } + // Fill how many contributers to the primary vertex are in the current collision (QA info) + registry.fill(HIST("QC/hNContribVsOrder"), order++, recColl.numContrib()); + } + // Fill number of collisions that are assigned to the current mc collision + registry.fill(HIST("QC/hNSelectedRecoPerMCColl"), nSelected); + // this variable is used to keep track at which stage the collision was rejected or if it was accepted. It is used later for filling the generated meson distributions + lastStage = kStageHasReco + (bestReject - 1); + } + } + // Fill the particles of interested as a function of the reject reason (lastStage) + fillGeneratedParticles(particlesInMCCollision, mcParticles, kStageAllMC, lastStage, trueMultiplicity); + for (int stage = kStageAllMC; stage <= lastStage; ++stage) { + registry.fill(HIST("hMCCollStage"), stage); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/hMCCollStage"), trueMultiplicity, stage); + } + } + } + } + } + PROCESS_SWITCH(MesonSignalLoss, processMBMC, "Process MC bunch crossings for signal and event loss", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} From 2ced90d87eda78093870c0692d66092ccdfafd9d Mon Sep 17 00:00:00 2001 From: Stefanie Mrozinski <63045530+Steffimro@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:09:30 +0200 Subject: [PATCH 399/648] [PWGEM] Add Photon-Finder, change conversion point calculation and change deduplication logic (#18412) --- .../TableProducer/photonconversionbuilder.h | 1371 +++++++++++++---- .../Utils/ConversionPointProfile.h | 177 +++ PWGEM/PhotonMeson/Utils/PCMUtilities.h | 175 ++- PWGEM/PhotonMeson/Utils/V0DedupUtilities.h | 309 ++++ 4 files changed, 1706 insertions(+), 326 deletions(-) create mode 100644 PWGEM/PhotonMeson/Utils/ConversionPointProfile.h create mode 100644 PWGEM/PhotonMeson/Utils/V0DedupUtilities.h diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index 483f38098b7..70ed1809bb0 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -26,8 +26,10 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/ConversionPointProfile.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/TrackSelection.h" +#include "PWGEM/PhotonMeson/Utils/V0DedupUtilities.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TPCVDriftManager.h" @@ -39,9 +41,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -52,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +79,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -90,9 +97,20 @@ using o2::framework::HistType; using o2::framework::kTH1D; using o2::framework::kTH1F; using o2::framework::kTH2F; +using o2::framework::kTH3F; using o2::framework::kTHnSparseF; using o2::framework::Preslice; using o2::framework::Produces; +using o2::pwgem::photonmeson::cpRadiusProfile; +using o2::pwgem::photonmeson::cpScanLeg; +using o2::pwgem::photonmeson::FinderCollision; +using o2::pwgem::photonmeson::FinderCuts; +using o2::pwgem::photonmeson::FinderLeg; +using o2::pwgem::photonmeson::FinderStep; +using o2::pwgem::photonmeson::LegScan; +using o2::pwgem::photonmeson::qInv; +using o2::pwgem::photonmeson::RadiusProfile; +using o2::pwgem::photonmeson::solveConflictGroup; // NOLINTEND(google-global-names-in-headers) using MyCollisions = o2::soa::Join; @@ -135,6 +153,12 @@ struct V0CandidateHelper { float pxAtPV = 0.f; float pyAtPV = 0.f; float pzAtPV = 0.f; + float svX = 0.f; + float svY = 0.f; + float svZ = 0.f; + float purity = -999.f; + float cosPAxy = -2.f; // transverse pointing angle + float cosPA3D = -2.f; // 3D pointing angle V0CandidateHelper() = default; @@ -149,14 +173,42 @@ struct V0CandidateHelper { }; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -using CandKey = std::tuple; +struct CandKey { + int64_t v0Id = -1; // SVertexer V0 index, -1 for finder and resolver photons + int64_t collisionId = -1; + int64_t posId = -1; + int64_t eleId = -1; + auto operator<=>(const CandKey&) const = default; +}; + +// result of a probe fit (FitOptions::probe): leg pair passed the cuts, and its dedup score +struct PairFit { + bool ok = false; + float score = 999.f; +}; + +// options for fitting a leg pair that was not built by the S-Vertexer (finder, ambiguity resolver) +struct FitOptions { + bool nonSVertexer = false; // finder PCA cut for mixed legs, side-based TPC drift correction + bool relaxAP = false; // no Armenteros-Podolanski cut + bool purityGate = false; // finder: require purity score > finderMinScore + PairFit* probe = nullptr; // only return the dedup score, do not add a candidate +}; + +// one tested pair of stored photons in the ambiguity resolver (also read by the MC diagnostics) +struct SwapRecord { + CandKey keyI{}, keyJ{}; + float q = -1.f; + bool complete = false; + bool swapped = false; +}; -// Everything the truth diagnosis need, struct DedupDiag { - std::map> blockersByKey; // rejected candidate -> ALL candidates that took one of its legs - std::vector snapshot; // ALL candidates, before deduplication - std::vector stored; // the survivors + std::map> blockersByKey; // rejected candidate -> candidates that took one of its legs + std::vector snapshot; // all candidates, before deduplication + std::vector stored; // surviving candidates std::map> lossMargin; + std::vector swaps; }; template @@ -197,8 +249,36 @@ struct PhotonConversionBuilder { Configurable deduplicationScoreWeight{"deduplicationScoreWeight", 0.5f, "0.:only pca goes into the score, 1: only cosPA goes itno score, any number in between is a mix of pca and cosPA"}; Configurable cfgDedupTruthMaps{"cfgDedupTruthMaps", true, "fill the fine (dEta, dPhi, q) truth maps in MC"}; Configurable dedupMaxGroupSize{"dedupMaxGroupSize", 12, "group matching (mode 2): conflict groups up to this size are solved exactly, larger ones greedily (capped at 16)"}; + Configurable groupMatchingScoreThreshold{"groupMatchingScoreThreshold", -1.f, "dedup mode 2, choice of the best leg-disjoint subset of a conflict group. <= 0 (master): most photons first, then lowest summed score. > 0: largest sum of max(threshold - score, 1e-3) over the photons, so a photon with a score near or above the threshold adds almost nothing and two poor candidates cannot outweigh one good one (e.g. 0.4)"}; + Configurable gapWeight{"gapWeight", 0.f, "dedup score: weight of the helix-circle gap of the legs (0 = off)"}; + Configurable gapScale{"gapScale", 1.f, "dedup score: scale of the circle gap (cm)"}; + Configurable useCosPA3D{"useCosPA3D", false, "use the 3D cosPA instead of the xy cosPA in V0PhotonCandidate"}; + Configurable resolveSplitLegs{"resolveSplitLegs", false, "after the dedup, remove photons that share a split track with a better photon - avoids duplicates for photon candidates"}; + Configurable splitLegMaxDTgl{"splitLegMaxDTgl", 0.03f, "split legs: max |tgl1 - tgl2|"}; + Configurable splitLegMaxChi2{"splitLegMaxChi2", 15.f, "split legs: max chi2 of two track pieces"}; + Configurable cfgExtendedDiag{"cfgExtendedDiag", false, "MC: truth of the stored photons, pairs and swaps"}; } dedup; + // recovery finder for not thinned data (step 2) and ambiguity resolver settings (step 5) + struct : ConfigurableGroup { + std::string prefix = "recovery_group"; + Configurable runFinder{"runFinder", false, "photon finder for free legs without ITS, candidates enter the dedup"}; + Configurable finderMinScore{"finderMinScore", 0.f, "finder: min. purity score (log-likelihood ratio true / fake)"}; + Configurable finderMaxDeltaTgl{"finderMaxDeltaTgl", 0.15f, "finder: max |tgl+ - tgl-|"}; + Configurable finderMaxCircleGap{"finderMaxCircleGap", 5.f, "finder: max |circle gap| (cm)"}; + Configurable finderMinCosXY{"finderMinCosXY", 0.9f, "finder: min. xy pointing at the touching point"}; + Configurable finderMaxDeltaZ{"finderMaxDeltaZ", 3.f, "finder: max |z+ - z-| at the touching point (cm)"}; + Configurable finderMaxDz{"finderMaxDz", 4.f, "finder: max |z of the photon line at the beam axis - z_PV| (cm)"}; + Configurable finderRMargin{"finderRMargin", 5.f, "finder: margin below the min. conversion radius (cm)"}; + Configurable finderTimeMargin{"finderTimeMargin", 100.f, "finder: margin of the leg time windows (ns)"}; + Configurable finderTimeMarginFwd{"finderTimeMarginFwd", 3000.f, "finder: extra late margin for TPC-only legs (ns)"}; + Configurable finderMaxPCAMixed{"finderMaxPCAMixed", -1.f, "finder: max PCA for an ITS and a non-ITS leg (cm), < 0 builder cuts"}; + Configurable finderFreeCollinearLegs{"finderFreeCollinearLegs", 0.f, "finder: two candidates of one collision with q_inv below this (GeV/c) count as collinear; their legs stay available to the finder (leg pairs already built are not refitted). Recovers true photons next to a close partner photon. 0 = off"}; + Configurable runSwapResolver{"runSwapResolver", false, "ambiguity resolver: for close stored photon pairs, fit the crossed leg pairing (no AP cut) and keep the pairing with the lower dedup score"}; + Configurable swapMaxQ{"swapMaxQ", 0.1f, "swap resolver: max q_inv of the two photons (GeV/c)"}; + Configurable swapMaxConvDist{"swapMaxConvDist", 20.f, "swap resolver: max distance of the conversion points (cm)"}; + } recovery; + // event-level skim for derived data struct : ConfigurableGroup { std::string prefix = "skim_group"; @@ -238,6 +318,7 @@ struct PhotonConversionBuilder { Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; + Configurable conversionPointMethod{"conversionPointMethod", 0, "0: helix touching point of the legs (Run 2), 1: radius-profiled pair fit through the material, falls back to 0 without a valid minimum"}; Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; @@ -289,6 +370,8 @@ struct PhotonConversionBuilder { o2::base::MatLayerCylSet* lut = nullptr; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; o2::aod::common::TPCVDriftManager mVDriftMgr; + uint64_t mLastCCDBTimestamp{0}; + std::set mRelaxedKeys; o2::framework::HistogramRegistry registry{ "registry", @@ -448,12 +531,32 @@ struct PhotonConversionBuilder { LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; } + if (v0cuts.conversionPointMethod == 1) { + registry.add("V0/hConvPointProfile", "conversionPointMethod 1;0 fallback to the touching point, 1 profile minimum used;candidates", kTH1F, {{2, -0.5f, 1.5f}}); + registry.add("V0/hConvPointProfileShift", "conversionPointMethod 1;R_{profile} - R_{touching point} (cm);candidates", kTH1F, {{200, -10.f, 10.f}}); + } + if (recovery.runSwapResolver && dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { + LOG(fatal) << "runSwapResolver needs deduplicationMode 0, 1 or 2"; + } + if (dedup.resolveSplitLegs) { + registry.add("Dedup/hSplitLegRemoved", "split legs: photons removed per DF;photons;DFs", kTH1F, {{100, -0.5f, 99.5f}}); + } + if (recovery.runFinder) { + registry.add("Finder/hSteps", "finder;step passed;pairs", kTH1D, {{FinderStep::kNFindStep, -0.5f, static_cast(FinderStep::kNFindStep) - 0.5f}}); + registry.add("Finder/hScore", "finder;purity score;pairs", kTH1F, {{100, -20.f, 10.f}}); + registry.add("Finder/hNAdded", "finder;photons added per DF;DFs", kTH1F, {{200, 0.f, 2000.f}}); + } + if (recovery.runSwapResolver) { + registry.add("Swap/hTested", "swap resolver;0 tested, 1 swapped;pairs", kTH1D, {{2, -0.5f, 1.5f}}); + registry.add("Swap/hQ", "swap resolver;q_{inv} (GeV/c);0 tested, 1 swapped", kTH2F, {{100, 0.f, 0.1f}, {2, -0.5f, 1.5f}}); + } + if (doprocessMC) { const AxisSpec axQ{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; const AxisSpec axDEta{80, -1.6f, 1.6f, "#Delta#eta_{#gamma#gamma}^{true}"}; const AxisSpec axClass{3, -0.5f, 2.5f, "0 = true, 1 = cross-leg fake, 2 = other fake"}; // lostByPartnerFake is a subset of lostByCrossFake, and a pair can appear in several of them. - for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "oneLost", "bothLost", + for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "surviveAsCrossedPairing", "halfCrossedStored", "oneLost", "bothLost", "lostByCrossFake", "lostByPartnerFake", "lostByOtherFake", "lostByTrue"}) { const std::string dir = std::string("MCDedup/Pairs/") + nm + "/"; registry.add((dir + "hQ").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;q_{inv}^{true} (GeV/c);pairs", kTH1F, {axQ}, true); @@ -478,6 +581,17 @@ struct PhotonConversionBuilder { registry.add((std::string("MCDedup/Pairs/") + nm + "/hMap").c_str(), "truth photon pairs", kTHnSparseF, {axDEtaFine, axDPhi, axQ}, true); } } + if (dedup.cfgExtendedDiag) { + const AxisSpec axPt{50, 0.f, 5.f, "p_{T,#gamma} (GeV/c)"}; + const AxisSpec axQrec{50, 0.f, 0.1f, "q_{inv} (GeV/c)"}; + registry.add("MCDedup/Stored/hClassSource", "stored photons;class;0 SVertexer, 1 finder or swap;p_{T,#gamma} (GeV/c)", kTH3F, {axClass, {2, -0.5f, 1.5f}, axPt}); + registry.add("MCDedup/Stored/hPairClass", "stored photon pairs, same collision;0 true-true, 1 crossed doublet, 2 other;q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); + registry.add("MCDedup/Copies/hChoice", "true photons with a candidate in the dedup sample;collisions with a true candidate;0 = right coll., 1 = wrong (right available), 2 = wrong (no right copy), 3 = lost", kTH2F, {{5, 0.5f, 5.5f}, {4, -0.5f, 3.5f}}); + registry.add("MCDedup/Copies/hRule", "true photons, >= 2 copies, right copy available;0 = current dedup choice, 1 = smallest |#Deltaz| of the photon line, 2 = largest xy cos(PA) (master), 3 = largest 3D cos(PA);0 = wrong, 1 = right", kTH2F, {{4, -0.5f, 3.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/Copies/hPAxy", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;transverse pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Copies/hPA3D", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;3D pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Swap/hCategory", "swap resolver;3 #times true stored + true crossed;0 kept, 1 swapped;q_{inv} (GeV/c)", kTH3F, {{9, -0.5f, 8.5f}, {2, -0.5f, 1.5f}, axQrec}); + } } mVDriftMgr.setUseSideBasedCorrection(useSideBasedCorrection.value); @@ -523,6 +637,7 @@ struct PhotonConversionBuilder { auto timestamp = bc.timestamp(); mVDriftMgr.update(timestamp); + mLastCCDBTimestamp = timestamp; } std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. @@ -649,12 +764,40 @@ struct PhotonConversionBuilder { template void fillV0Table(TV0 const& v0, const bool filltable) { - using namespace o2::pwgem::photonmeson; - // Get tracks const auto& pos = v0.template posTrack_as(); const auto& ele = v0.template negTrack_as(); const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. - // LOGF(info, "v0.collisionId() = %d, pos.collisionId() = %d, ele.collisionId() = %d", v0.collisionId(), pos.collisionId(), ele.collisionId()); + fitV0Candidate(collision, pos, ele, v0.globalIndex(), filltable); + } + + // the purity score (finder LLR tables) is only needed for the finder gate and the split-leg order + bool usePurity() const { return recovery.runFinder.value || dedup.resolveSplitLegs.value; } + + template + static float purityScore(TTrack const& pos, TTrack const& ele, const float cosPA3D, const float pca, const float mee, const float gap, const float psipair) + { + return o2::pwgem::photonmeson::finderPurityScore({cosPA3D, pca, mee, gap, std::fabs(pos.tgl() - ele.tgl()), psipair, + std::max(std::fabs(pos.tpcNSigmaEl()), std::fabs(ele.tpcNSigmaEl())), + std::min(std::fabs(pos.tpcNSigmaPi()), std::fabs(ele.tpcNSigmaPi()))}); + } + + // deduplication score, lower = better: master score (cosPA, PCA) + optional circle-gap term + float dedupScore(const float cosPA, const float pca, const float gap) + { + float score = getScoreV0(cosPA, pca, dedup.deduplicationScoreWeight); + if (dedup.gapWeight > 0.f) { + score += dedup.gapWeight * std::min(std::fabs(gap) / dedup.gapScale.value, 3.f); // o2-linter: disable=magic-number (cap) + } + return score; + } + + // fit of one leg pair with the V0 cuts; filltable = false adds a dedup candidate, + // true writes the tables; opt.probe only returns the score + template + void fitV0Candidate(TCollision const& collision, TTrack const& pos, TTrack const& ele, const int64_t v0Id, const bool filltable, FitOptions const& opt = {}) + { + using namespace o2::pwgem::photonmeson; + mVDriftMgr.setUseSideBasedCorrection(opt.nonSVertexer || useSideBasedCorrection.value); if (pos.sign() * ele.sign() > 0) { // reject same sign pair return; @@ -692,7 +835,9 @@ struct PhotonConversionBuilder { auto pTrack = getTrackParCov(pos); if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { - LOGP(error, "failed correction for positive tpc track"); + if (opt.probe == nullptr) { + LOGP(error, "failed correction for positive tpc track"); + } return; } auto pTrackC = pTrack; @@ -718,7 +863,9 @@ struct PhotonConversionBuilder { auto nTrack = getTrackParCov(ele); if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { - LOGP(error, "failed correction for negative tpc track"); + if (opt.probe == nullptr) { + LOGP(error, "failed correction for negative tpc track"); + } return; } auto nTrackC = nTrack; @@ -744,6 +891,19 @@ struct PhotonConversionBuilder { std::array xyz = {0.f, 0.f, 0.f}; Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); + if (v0cuts.conversionPointMethod == 1) { + LegScan scanP, scanN; + cpScanLeg(pTrack, d_bz, matCorr, scanP); + cpScanLeg(nTrack, d_bz, matCorr, scanN); + RadiusProfile prof; + cpRadiusProfile(scanP, scanN, pos.hasITS() ? 0.f : 0.25f, ele.hasITS() ? 0.f : 0.25f, prof); // o2-linter: disable=magic-number (0.5 cm extra z for legs without ITS) + const bool useProfile = prof.iMin >= 0 && prof.status != 4; // o2-linter: disable=magic-number (profile class: failed) + if (useProfile) { + registry.fill(HIST("V0/hConvPointProfileShift"), prof.rMin - std::hypot(xyz[0], xyz[1])); + xyz = prof.xyz; + } + registry.fill(HIST("V0/hConvPointProfile"), useProfile ? 1.f : 0.f); + } float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { return; @@ -861,7 +1021,7 @@ struct PhotonConversionBuilder { return; } - if (!filltable) { + if (!filltable && opt.probe == nullptr) { if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { @@ -933,7 +1093,8 @@ struct PhotonConversionBuilder { float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); auto centType = static_cast(mlcuts.centTypePCMMl.value); - v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); + // cosPA slot of the candidate (dedup score, cosPA cut): xy as in master, or 3D with dedup_group.useCosPA3D + v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, dedup.useCosPA3D ? cospa_kf : cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { @@ -945,6 +1106,10 @@ struct PhotonConversionBuilder { return; } } + } else if (opt.nonSVertexer && recovery.finderMaxPCAMixed > 0.f && pos.hasITS() != ele.hasITS()) { + if (v0photoncandidate.getPCA() > recovery.finderMaxPCAMixed) { + return; + } } else { // V0s with ITS hits if (rxy < v0cuts.max_r_req_its) { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { @@ -973,22 +1138,43 @@ struct PhotonConversionBuilder { return; } - if (!checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + if (!opt.relaxAP && !checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions return; } - pca_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getPCA(); - cospa_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getCosPA(); ROOT::Math::PxPyPzMVector vposDedup(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); ROOT::Math::PxPyPzMVector veleDedup(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); const auto meeSV = static_cast((vposDedup + veleDedup).M()); - const auto score = getScoreV0(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), dedup.deduplicationScoreWeight); - V0CandidateHelper v0Helper(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), + // passed all cuts: score and dedup candidate + const float gap = circleGap(o2::track::TrackAuxPar(pTrack, d_bz), o2::track::TrackAuxPar(nTrack, d_bz)); + const float score = dedupScore(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), gap); + if (opt.probe != nullptr) { + *opt.probe = {true, score}; + return; + } + const float purity = usePurity() ? purityScore(pos, ele, cospa_kf, v0photoncandidate.getPCA(), meeSV, gap, v0photoncandidate.getPsiPair()) : -999.f; + if (opt.purityGate) { + registry.fill(HIST("Finder/hScore"), purity); + if (purity <= recovery.finderMinScore) { + return; + } + } + + pca_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getPCA(); + cospa_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getCosPA(); + + V0CandidateHelper v0Helper(v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), score, meeSV); v0Helper.pxAtPV = gammaKF_PV.GetPx(); v0Helper.pyAtPV = gammaKF_PV.GetPy(); v0Helper.pzAtPV = gammaKF_PV.GetPz(); + v0Helper.svX = gammaKF_DecayVtx.GetX(); + v0Helper.svY = gammaKF_DecayVtx.GetY(); + v0Helper.svZ = gammaKF_DecayVtx.GetZ(); + v0Helper.purity = purity; + v0Helper.cosPAxy = cospaXYKF; + v0Helper.cosPA3D = cospa_kf; vecV0Dedup.emplace_back(v0Helper); if (mlcuts.applyPCMMl) { @@ -1070,7 +1256,7 @@ struct PhotonConversionBuilder { ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); - v0photonskf(collision.globalIndex(), v0.globalIndex(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, + v0photonskf(collision.globalIndex(), v0Id, v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), @@ -1085,338 +1271,718 @@ struct PhotonConversionBuilder { } // end of fill table } - Preslice perCollision = o2::aod::v0::collisionId; - std::map, float> pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca - std::map, float> cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa - std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) - std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) - std::vector> stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) - std::unordered_map nv0_map; // map collisionId -> nv0 + // bookkeeping of candidates and stored photons + static CandKey keyOf(V0CandidateHelper const& c) + { + return CandKey{c.v0ID, c.colID, c.posID, c.eleID}; + } - template - void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + std::map candidateIndexByKey() const { - for (const auto& collision : collisions) { + std::map index; + for (int i = 0; i < static_cast(vecV0Dedup.size()); ++i) { + index[keyOf(vecV0Dedup[i])] = i; + } + return index; + } + + // replaces the stored photons after the dedup and keeps stored_v0Ids and nv0_map consistent + void setStored(std::vector keys) + { + std::stable_sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + stored_fullv0Ids.swap(keys); + stored_v0Ids.clear(); + for (auto& [col, n] : nv0_map) { // o2-linter: disable=const-ref-in-for-loop (counters are reset in place) + n = 0; + } + for (const auto& key : stored_fullv0Ids) { + stored_v0Ids.emplace_back(key.posId, key.eleId); + nv0_map[key.collisionId]++; + } + } + + // photon finder + // finder leg: helix circle and the time window in which the track can belong to a collision (TPC-only legs: from the drift-time asymmetry) + template + bool makeFinderLeg(TTrack const& track, TCollisions const& collisions, const int64_t bcRef, FinderLeg& leg) + { + using namespace o2::pwgem::photonmeson; + if (track.collisionId() < 0 || !track.hasTPC()) { + return false; + } + leg.id = track.globalIndex(); + leg.tgl = track.tgl(); + leg.noITS = !track.hasITS(); + leg.movable = isTPConlyTrack(track) && (track.flags() & o2::aod::track::TrackTimeAsym) != 0; + const auto bc = collisions.rawIteratorAt(track.collisionId()).template bc_as(); + leg.t = static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + track.trackTime(); + if (leg.movable) { + const int side = tpcDriftSide(track); + if (side == TPCSideUndetermined) { + return false; + } + leg.side = side; + leg.tMin = leg.t - track.tpcDeltaTBwd() - recovery.finderTimeMargin; + leg.tMax = leg.t + track.tpcDeltaTFwd() + recovery.finderTimeMarginFwd; + } else { + constexpr float NSigmaTime = 4.f; + const float sigma = std::max(track.trackTimeRes(), 1.f); + const float halfWidth = (track.flags() & o2::aod::track::TrackTimeResIsRange) != 0 ? sigma : NSigmaTime * sigma; + leg.side = 0; + leg.tMin = leg.t - halfWidth - recovery.finderTimeMargin; + leg.tMax = leg.t + halfWidth + recovery.finderTimeMargin; + } + leg.par = getTrackPar(track); + leg.circle = o2::track::TrackAuxPar(leg.par, d_bz); + return true; + } + + // pairs the free legs of the whole DF (not used by any candidate unless finderFreeCollinearLegs, at least one leg without ITS); a pair that passes the geometry + // (evalFinderPair) and matches a collision in z is fitted and becomes a dedup candidate with v0Id = -1 + template + void runPhotonFinder(TCollisions const& collisions, TTracks const& tracks) + { + std::set usedLegs; + std::set> builtPairs; + // legs of accepted candidates are not free; with finderFreeCollinearLegs, legs of collinear candidate pairs stay free + std::vector collinear(vecV0Dedup.size(), false); + if (recovery.finderFreeCollinearLegs > 0.f) { + std::map> candsOfColl; + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + candsOfColl[vecV0Dedup[i].colID].push_back(i); + } + for (const auto& [colId, members] : candsOfColl) { + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { + continue; + } + if (qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV) < recovery.finderFreeCollinearLegs) { + collinear[members[a]] = true; + collinear[members[b]] = true; + } + } + } + } + } + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + const auto& c = vecV0Dedup[i]; + builtPairs.emplace(c.posID, c.eleID); + if (!collinear[i]) { + usedLegs.insert(c.posID); + usedLegs.insert(c.eleID); + } + } + std::vector colls; + int64_t bcRef = -1; + for (const auto& col : collisions) { if constexpr (isMC) { - if (!collision.has_mcCollision()) { + if (!col.has_mcCollision()) { continue; } } - - if (!collision.isSelected()) { + if (!col.isSelected()) { continue; } + const auto bc = col.template bc_as(); + if (bcRef < 0) { + bcRef = static_cast(bc.globalBC()); + } + colls.push_back({static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + col.collisionTime(), col.globalIndex(), col.posX(), col.posY(), col.posZ()}); + } + if (colls.empty()) { + return; + } + std::sort(colls.begin(), colls.end(), [](const FinderCollision& a, const FinderCollision& b) { return a.t < b.t; }); + const auto* vd = ccdb->getForTimeStamp("TPC/Calib/VDriftTgl", mLastCCDBTimestamp); + if (vd == nullptr) { + LOG(warning) << "photon finder: no TPC drift velocity for timestamp " << mLastCCDBTimestamp; + return; + } + const double vDrift = vd->refVDrift * vd->corrFact * 1e-3; + float xB = 0.f, yB = 0.f; + for (const auto& c : colls) { + xB += c.x / static_cast(colls.size()); + yB += c.y / static_cast(colls.size()); + } - // if constexpr (isTriggerAnalysis) { - // if (collision.triggerMask_raw() == 0) { - // continue; - // } - // } - - nv0_map[collision.globalIndex()] = 0; - - const auto& bc = collision.template foundBC_as(); - initCCDB(bc); - registry.fill(HIST("hCollisionCounter"), 1); + std::vector posLegs, eleLegs; + for (const auto& track : tracks) { + if (usedLegs.contains(track.globalIndex()) || track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + FinderLeg leg; + if (makeFinderLeg(track, collisions, bcRef, leg)) { + (track.sign() > 0 ? posLegs : eleLegs).push_back(leg); + } + } + std::sort(eleLegs.begin(), eleLegs.end(), [](const FinderLeg& a, const FinderLeg& b) { return a.tgl < b.tgl; }); - updateCCDB(bc); // delay update until is needed + const FinderCuts cuts{recovery.finderMaxDeltaTgl, recovery.finderMaxCircleGap, v0cuts.min_r_tpconly, v0cuts.max_r_req_its, recovery.finderRMargin, + trackcuts.maxX + v0cuts.margin_r_tpc, recovery.finderMinCosXY, recovery.finderMaxDeltaZ, recovery.finderMaxDz, d_bz}; + const FitOptions finderFit{.nonSVertexer = true, .purityGate = true}; + const size_t nBefore = vecV0Dedup.size(); + for (const auto& p : posLegs) { + for (auto itE = std::lower_bound(eleLegs.begin(), eleLegs.end(), p.tgl - recovery.finderMaxDeltaTgl, [](const FinderLeg& c, float v) { return c.tgl < v; }); + itE != eleLegs.end() && itE->tgl <= p.tgl + recovery.finderMaxDeltaTgl; ++itE) { + if ((!p.noITS && !itE->noITS) || builtPairs.contains({p.id, itE->id})) { + continue; + } + int64_t colId = -1; + const int step = evalFinderPair(p, *itE, colls, cuts, xB, yB, vDrift, colId); + if (step >= 0) { + registry.fill(HIST("Finder/hSteps"), static_cast(step)); + } + if (step == FinderStep::kFindCollision) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(p.id), tracks.rawIteratorAt(itE->id), -1, false, finderFit); + } + } + } + registry.fill(HIST("Finder/hNAdded"), static_cast(vecV0Dedup.size() - nBefore)); + } - vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF - const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); - // LOGF(info, "n v0 = %d", v0s_per_coll.size()); - for (const auto& v0 : v0s_per_coll) { - // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); - fillV0Table(v0, false); - } // end of v0 loop - } // end of collision loop + // two stored photons that use two pieces of one track (same tgl, compatible parameters): keep the better one + template + void resolveSplitLegConflicts(TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::vector> order; + order.reserve(stored_fullv0Ids.size()); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + const auto it = indexOfKey.find(stored_fullv0Ids[k]); + order.emplace_back(it == indexOfKey.end() ? std::numeric_limits::max() : (usePurity() ? -vecV0Dedup[it->second].purity : vecV0Dedup[it->second].score), k); + } + std::stable_sort(order.begin(), order.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + auto samePiece = [&](int64_t idA, int64_t idB) { + if (idA == idB) { + return false; + } + const auto trA = tracks.rawIteratorAt(idA); + const auto trB = tracks.rawIteratorAt(idB); + if (std::fabs(trA.tgl() - trB.tgl()) > dedup.splitLegMaxDTgl) { + return false; + } + const float chi2 = splitPieceChi2(trA, trB, d_bz); + return chi2 >= 0.f && chi2 < dedup.splitLegMaxChi2; + }; + std::vector> keptPos, keptEle; + std::vector removed(stored_fullv0Ids.size(), 0); + int nRemoved = 0; + for (const auto& [rank, k] : order) { + const auto& key = stored_fullv0Ids[k]; + int64_t owner = -1; + for (size_t m = 0; m < keptPos.size() && owner < 0; ++m) { + if (samePiece(key.posId, keptPos[m].first)) { + owner = static_cast(keptPos[m].second); + } + } + for (size_t m = 0; m < keptEle.size() && owner < 0; ++m) { + if (samePiece(key.eleId, keptEle[m].first)) { + owner = static_cast(keptEle[m].second); + } + } + if (owner >= 0) { + removed[k] = 1; + ++nRemoved; + if (diag != nullptr) { + diag->blockersByKey[key].push_back(stored_fullv0Ids[owner]); + } + continue; + } + keptPos.emplace_back(key.posId, k); + keptEle.emplace_back(key.eleId, k); + } + if (nRemoved > 0) { + std::vector kept; + kept.reserve(stored_fullv0Ids.size() - nRemoved); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + if (removed[k] == 0) { + kept.push_back(stored_fullv0Ids[k]); + } + } + setStored(std::move(kept)); + } + registry.fill(HIST("Dedup/hSplitLegRemoved"), static_cast(nRemoved)); + } - stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF - stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + // ambiguity resolver + // two stored photons of one collision, close in q and in space: fit the crossed leg pairing; if both crossed photons pass and + // their summed score is lower, swap + template + void runSwapResolver(TCollisions const& collisions, TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::map> storedOfColl; + for (const auto& key : stored_fullv0Ids) { + if (const auto it = indexOfKey.find(key); it != indexOfKey.end()) { + storedOfColl[key.collisionId].push_back(it->second); + } + } + // the AP cut is the main reason the true pairing of a crossed group was not built + auto fitPair = [&](int64_t colId, int64_t posId, int64_t eleId, PairFit* probe) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(posId), tracks.rawIteratorAt(eleId), -1, false, + FitOptions{.nonSVertexer = true, .relaxAP = true, .probe = probe}); + }; + auto probeFit = [&](int64_t colId, int64_t posId, int64_t eleId) { + PairFit out; + fitPair(colId, posId, eleId, &out); + return out; + }; + auto crossedPrefilter = [&](int64_t posId, int64_t eleId) { + const auto pos = tracks.rawIteratorAt(posId); + const auto ele = tracks.rawIteratorAt(eleId); + if (std::fabs(pos.tgl() - ele.tgl()) > recovery.finderMaxDeltaTgl) { + return false; + } + const o2::track::TrackAuxPar cp(getTrackPar(pos), d_bz); + const o2::track::TrackAuxPar ce(getTrackPar(ele), d_bz); + return std::fabs(circleGap(cp, ce)) < recovery.finderMaxCircleGap; + }; + std::map storedFit; + auto storedFitOf = [&](int idx) -> const PairFit& { + auto it = storedFit.find(idx); + if (it == storedFit.end()) { + const auto& c = vecV0Dedup[idx]; + it = storedFit.emplace(idx, probeFit(c.colID, c.posID, c.eleID)).first; + } + return it->second; + }; - // find minimal pca - if (dedup.deduplicationMode == V0DeduplicationMode::Pairwise) { - for (const auto& [key, value] : pca_map) { - auto v0Id = std::get<0>(key); - auto collisionId = std::get<1>(key); - auto posId = std::get<2>(key); - auto eleId = std::get<3>(key); - float v0pca = value; - float cospa = cospa_map[key]; - bool is_closest_v0 = true; - bool is_most_aligned_v0 = true; - - for (const auto& [key_tmp, value_tmp] : pca_map) { - auto v0Id_tmp = std::get<0>(key_tmp); - auto collisionId_tmp = std::get<1>(key_tmp); - auto posId_tmp = std::get<2>(key_tmp); - auto eleId_tmp = std::get<3>(key_tmp); - float v0pca_tmp = value_tmp; - float cospa_tmp = cospa_map[key_tmp]; - - if (v0Id == v0Id_tmp) { // skip exactly the same v0 + // test all close pairs of stored photons + struct Test { + SwapRecord rec; + float gain = -999.f; + }; + std::vector swaps; + for (const auto& [colId, members] : storedOfColl) { + std::vector tests; + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { continue; } - - if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_most_aligned_v0 = false; - break; + const float q = qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV); + if (q > recovery.swapMaxQ || std::hypot(ci.svX - cj.svX, ci.svY - cj.svY, ci.svZ - cj.svZ) > recovery.swapMaxConvDist) { + continue; } - if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { - if (diag != nullptr) { - diag->blockersByKey[key].push_back(key_tmp); - } - is_closest_v0 = false; - break; + const PairFit storedI = storedFitOf(members[a]); + const PairFit storedJ = storedFitOf(members[b]); + const PairFit crossedI = crossedPrefilter(ci.posID, cj.eleID) ? probeFit(colId, ci.posID, cj.eleID) : PairFit{}; + const PairFit crossedJ = crossedPrefilter(cj.posID, ci.eleID) ? probeFit(colId, cj.posID, ci.eleID) : PairFit{}; + Test t; + t.rec = {keyOf(ci), keyOf(cj), q, storedI.ok && storedJ.ok && crossedI.ok && crossedJ.ok, false}; + if (t.rec.complete) { // gain > 0: the crossed pairing has the lower (better) summed dedup score + t.gain = (storedI.score + storedJ.score) - (crossedI.score + crossedJ.score); } - } // end of pca_map tmp loop - - bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); - if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { - // auto v0 = v0s.rawIteratorAt(v0Id); - // auto collision = collisions.rawIteratorAt(collisionId); - // auto pos = tracks.rawIteratorAt(posId); - // auto ele = tracks.rawIteratorAt(eleId); - // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); - - // fillV0Table(v0, true); - stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); - stored_fullv0Ids.emplace_back(std::make_tuple(v0Id, collisionId, posId, eleId)); - nv0_map[collisionId]++; + tests.push_back(t); } - } // end of pca_map loop - // LOGF(info, "pca_map.size() = %d", pca_map.size()); - - } else if (dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { - // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; - } - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); - } else if (dedup.deduplicationMode == V0DeduplicationMode::GroupMatching) { - const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) - const int nCand = static_cast(vecV0Dedup.size()); - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - int nGreedyFallback = 0; - - // ---- conflict groups: candidates connected through a shared leg ----- - std::vector parent(nCand); - for (int i = 0; i < nCand; ++i) { - parent[i] = i; - } - auto findRoot = [&parent](int x) { - while (parent[x] != x) { - parent[x] = parent[parent[x]]; // path halving - x = parent[x]; + } + std::vector order(tests.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), [&](int x, int y) { return tests[x].gain > tests[y].gain; }); + std::set used; + for (const int& k : order) { + auto& t = tests[k]; + if (t.rec.complete && t.gain > 0.f && !used.contains(t.rec.keyI) && !used.contains(t.rec.keyJ)) { + t.rec.swapped = true; + used.insert(t.rec.keyI); + used.insert(t.rec.keyJ); + swaps.push_back(t.rec); } - return x; - }; - std::unordered_map firstCandOfTrack; - for (int i = 0; i < nCand; ++i) { - for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { - auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); - if (!inserted) { - parent[findRoot(i)] = findRoot(it->second); - } + registry.fill(HIST("Swap/hTested"), 0.f); + registry.fill(HIST("Swap/hQ"), t.rec.q, t.rec.swapped ? 1.f : 0.f); + if (diag != nullptr) { + diag->swaps.push_back(t.rec); } } - std::map> groups; - for (int i = 0; i < nCand; ++i) { - groups[findRoot(i)].push_back(i); + } + if (swaps.empty()) { + return; + } + + // apply swaps: the crossed photons become candidates and replace the stored pair + const std::set storedBefore(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::set removedKeys; + std::vector addedKeys; + for (const auto& s : swaps) { + const int64_t colId = s.keyI.collisionId; + const int64_t posI = s.keyI.posId, eleI = s.keyI.eleId, posJ = s.keyJ.posId, eleJ = s.keyJ.eleId; + const CandKey a1 = CandKey{-1, colId, posI, eleJ}; + const CandKey a2 = CandKey{-1, colId, posJ, eleI}; + if (storedBefore.contains(a1) || storedBefore.contains(a2)) { + continue; + } + if (!pca_map.contains(a1)) { + fitPair(colId, posI, eleJ, nullptr); } + if (!pca_map.contains(a2)) { + fitPair(colId, posJ, eleI, nullptr); + } + if (!pca_map.contains(a1) || !pca_map.contains(a2)) { + continue; + } + removedKeys.insert(s.keyI); + removedKeys.insert(s.keyJ); + addedKeys.push_back(a1); + addedKeys.push_back(a2); + mRelaxedKeys.insert(a1); // refitted for the table without the AP cut, as in the resolver fit + mRelaxedKeys.insert(a2); + registry.fill(HIST("Swap/hTested"), 1.f); + if (diag != nullptr) { + diag->blockersByKey[s.keyI] = {a1, a2}; + diag->blockersByKey[s.keyJ] = {a1, a2}; + } + } + std::vector kept; + kept.reserve(stored_fullv0Ids.size()); + for (const auto& key : stored_fullv0Ids) { + if (!removedKeys.contains(key)) { + kept.push_back(key); + } + } + kept.insert(kept.end(), addedKeys.begin(), addedKeys.end()); + setStored(std::move(kept)); + } - // ---- decide each group on its own ---------------------------------- - for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) - const int nMembers = static_cast(members.size()); - std::vector selected(nMembers, 0); + // deduplication + Preslice perCollision = o2::aod::v0::collisionId; + std::map pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca + std::map cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa + std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) + std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) + std::vector stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) + std::unordered_map nv0_map; // map collisionId -> nv0 + + // mode 0: a candidate is kept if no other candidate sharing a leg has a smaller PCA and no copy in another collision has a larger cosPA + void selectPairwise(DedupDiag* diag) + { + for (const auto& [key, value] : pca_map) { + auto v0Id = key.v0Id; + auto collisionId = key.collisionId; + auto posId = key.posId; + auto eleId = key.eleId; + float v0pca = value; + float cospa = cospa_map[key]; + bool is_closest_v0 = true; + bool is_most_aligned_v0 = true; + + for (const auto& [key_tmp, value_tmp] : pca_map) { + auto collisionId_tmp = key_tmp.collisionId; + auto posId_tmp = key_tmp.posId; + auto eleId_tmp = key_tmp.eleId; + float v0pca_tmp = value_tmp; + float cospa_tmp = cospa_map[key_tmp]; + + if (key == key_tmp) { // skip exactly the same candidate + continue; + } - // Fix the order inside the group: best score first - std::sort(members.begin(), members.end(), [this](int a, int b) { - if (vecV0Dedup[a].score != vecV0Dedup[b].score) { - return vecV0Dedup[a].score < vecV0Dedup[b].score; + if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); } - return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; - }); - - if (nMembers == 1) { - selected[0] = 1; - } else if (nMembers <= maxEnum) { - - std::unordered_map legBit; - std::vector legMask(nMembers, 0); - for (int k = 0; k < nMembers; ++k) { - for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { - const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; - legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit - } + is_most_aligned_v0 = false; + break; + } + if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); } - uint32_t bestMask = 0; - int bestCount = 0; - float bestScore = 0.f; - - std::vector bestWithCount(nMembers, -1); - std::vector bestWithScore(nMembers, 0.f); - for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { - uint64_t usedLegsLocal = 0; - float scoreSum = 0.f; - int count = 0; - bool disjoint = true; - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if ((usedLegsLocal & legMask[k]) != 0) { - disjoint = false; - break; - } - usedLegsLocal |= legMask[k]; - scoreSum += vecV0Dedup[members[k]].score; - ++count; - } - if (!disjoint) { - continue; - } + is_closest_v0 = false; + break; + } + } // end of pca_map tmp loop + + bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); + if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { + // auto v0 = v0s.rawIteratorAt(v0Id); + // auto collision = collisions.rawIteratorAt(collisionId); + // auto pos = tracks.rawIteratorAt(posId); + // auto ele = tracks.rawIteratorAt(eleId); + // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); + + // fillV0Table(v0, true); + stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); + stored_fullv0Ids.push_back(CandKey{v0Id, collisionId, posId, eleId}); + nv0_map[collisionId]++; + } + } // end of pca_map loop + if (recovery.runFinder || recovery.runSwapResolver) { + std::stable_sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + } + // LOGF(info, "pca_map.size() = %d", pca_map.size()); + } - if (count > bestCount || (count == bestCount && scoreSum < bestScore)) { - bestCount = count; - bestScore = scoreSum; - bestMask = mask; - } - if (diag != nullptr) { - for (int k = 0; k < nMembers; ++k) { - if ((mask & (1u << k)) == 0) { - continue; - } - if (count > bestWithCount[k] || (count == bestWithCount[k] && scoreSum < bestWithScore[k])) { - bestWithCount[k] = count; - bestWithScore[k] = scoreSum; - } - } - } - } - for (int k = 0; k < nMembers; ++k) { - selected[k] = ((bestMask & (1u << k)) != 0) ? 1 : 0; - if (diag != nullptr && selected[k] == 0 && bestWithCount[k] >= 0) { - const auto& cand = vecV0Dedup[members[k]]; - diag->lossMargin[std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID))] = - std::make_pair(bestCount - bestWithCount[k], bestWithScore[k] - bestScore); - } + // mode 3: every candidate is kept, including all duplicates + void keepAll() + { + // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 2: candidates sharing a leg form a conflict group; per group the best leg-disjoint subset is kept + void selectByConflictGroup(DedupDiag* diag) + { + const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) + const int nCand = static_cast(vecV0Dedup.size()); + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + int nGreedyFallback = 0; + + // conflict groups: candidates connected through a shared leg + std::vector parent(nCand); + for (int i = 0; i < nCand; ++i) { + parent[i] = i; + } + auto findRoot = [&parent](int x) { + while (parent[x] != x) { + parent[x] = parent[parent[x]]; // path halving + x = parent[x]; + } + return x; + }; + std::unordered_map firstCandOfTrack; + for (int i = 0; i < nCand; ++i) { + for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { + auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); + if (!inserted) { + parent[findRoot(i)] = findRoot(it->second); + } + } + } + std::map> groups; + for (int i = 0; i < nCand; ++i) { + groups[findRoot(i)].push_back(i); + } + + // decide each group + for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) + const int nMembers = static_cast(members.size()); + std::vector selected(nMembers, 0); + + // Fix the order inside the group: best score first + std::sort(members.begin(), members.end(), [this](int a, int b) { + if (vecV0Dedup[a].score != vecV0Dedup[b].score) { + return vecV0Dedup[a].score < vecV0Dedup[b].score; + } + if (vecV0Dedup[a].v0ID != vecV0Dedup[b].v0ID) { + return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; + } + return std::tie(vecV0Dedup[a].posID, vecV0Dedup[a].eleID, vecV0Dedup[a].colID) < std::tie(vecV0Dedup[b].posID, vecV0Dedup[b].eleID, vecV0Dedup[b].colID); + }); + + if (nMembers == 1) { + selected[0] = 1; + } else if (nMembers <= maxEnum) { + + std::unordered_map legBit; + std::vector legMask(nMembers, 0); + for (int k = 0; k < nMembers; ++k) { + for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { + const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; + legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit } - } else { - // Too large to enumerate: greedy by score (members is already sorted that way) - ++nGreedyFallback; - std::vector usedTracks; - usedTracks.reserve(2 * nMembers); - for (int k = 0; k < nMembers; ++k) { + } + std::vector scores(nMembers); + for (int k = 0; k < nMembers; ++k) { + scores[k] = vecV0Dedup[members[k]].score; + } + const auto sol = solveConflictGroup(legMask, scores, dedup.groupMatchingScoreThreshold.value, diag != nullptr); + for (int k = 0; k < nMembers; ++k) { + selected[k] = ((sol.bestMask & (1u << k)) != 0) ? 1 : 0; + if (diag != nullptr && selected[k] == 0 && sol.bestWithCount[k] >= 0) { const auto& cand = vecV0Dedup[members[k]]; - if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || - std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { - continue; - } - usedTracks.push_back(cand.posID); - usedTracks.push_back(cand.eleID); - selected[k] = 1; + diag->lossMargin[keyOf(cand)] = + std::make_pair(sol.bestCount - sol.bestWithCount[k], sol.bestWithScore[k] - sol.bestScore); } } - - // ---- store the winners, book the blockers of the losers ---------- + } else { + // Too large to enumerate: greedy by score + ++nGreedyFallback; + std::vector usedTracks; + usedTracks.reserve(2 * nMembers); for (int k = 0; k < nMembers; ++k) { const auto& cand = vecV0Dedup[members[k]]; - if (selected[k] != 0) { - stored_v0Ids.emplace_back(cand.posID, cand.eleID); - stored_fullv0Ids.emplace_back(cand.v0ID, cand.colID, cand.posID, cand.eleID); - nv0_map[cand.colID]++; + if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || + std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { continue; } - if (diag == nullptr) { + usedTracks.push_back(cand.posID); + usedTracks.push_back(cand.eleID); + selected[k] = 1; + } + } + + // store the winners, book the blockers of the losers + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (selected[k] != 0) { + stored_v0Ids.emplace_back(cand.posID, cand.eleID); + stored_fullv0Ids.push_back(keyOf(cand)); + nv0_map[cand.colID]++; + continue; + } + if (diag == nullptr) { + continue; + } + // all winners that took one leg + const CandKey victimKey = keyOf(cand); + for (int j = 0; j < nMembers; ++j) { + if (selected[j] == 0) { continue; } - // all winners that took one leg - const CandKey victimKey = std::make_tuple(static_cast(cand.v0ID), static_cast(cand.colID), - static_cast(cand.posID), static_cast(cand.eleID)); - for (int j = 0; j < nMembers; ++j) { - if (selected[j] == 0) { + const auto& winner = vecV0Dedup[members[j]]; + if (winner.posID == cand.posID || winner.eleID == cand.eleID) { + diag->blockersByKey[victimKey].push_back(keyOf(winner)); + } + } + } + } + + if (nGreedyFallback > 0) { + LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); + } + + // the table expects candidates ordered by collision + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 1: candidates sorted by score (lower = better), a candidate is kept if both legs are still free + void selectGreedy(const size_t nTracks, DedupDiag* diag) + { + // Sort best candidates first, depending on score + std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); + // container to keep track of which electron and positron tracks have already been used + std::vector usedLegs(nTracks, false); + std::unordered_map ownerOfLeg; // diagnostics only + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + const CandKey candKey = keyOf(v0Cand); + if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { + if (diag != nullptr) { + // both legs, not just the first one found: a cross-leg fake is usually blocked by + // two different winners, and that is exactly the case being measured here + auto& blockers = diag->blockersByKey[candKey]; + for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { + const auto itOwner = ownerOfLeg.find(legId); + if (itOwner == ownerOfLeg.end()) { continue; } - const auto& winner = vecV0Dedup[members[j]]; - if (winner.posID == cand.posID || winner.eleID == cand.eleID) { - diag->blockersByKey[victimKey].emplace_back(static_cast(winner.v0ID), static_cast(winner.colID), - static_cast(winner.posID), static_cast(winner.eleID)); + if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { + blockers.push_back(itOwner->second); // the same winner may hold both legs } } } + continue; } - - if (nGreedyFallback > 0) { - LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); + usedLegs[v0Cand.posID] = true; + usedLegs[v0Cand.eleID] = true; + if (diag != nullptr) { + ownerOfLeg[v0Cand.posID] = candKey; + ownerOfLeg[v0Cand.eleID] = candKey; } + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } - // the table expects candidates ordered by collision - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); + // sort accepted candidates by collision ID to be compatible with table + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } - } else { - // Sort best candidates first, depending on score - std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); - // container to keep track of which electron and positron tracks have already been used - std::vector usedLegs(tracks.size(), false); - std::unordered_map ownerOfLeg; // diagnostics only - stored_v0Ids.clear(); - stored_fullv0Ids.clear(); - nv0_map.clear(); - for (const auto& v0Cand : vecV0Dedup) { - const CandKey candKey = std::make_tuple(static_cast(v0Cand.v0ID), static_cast(v0Cand.colID), - static_cast(v0Cand.posID), static_cast(v0Cand.eleID)); - if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { - if (diag != nullptr) { - // both legs, not just the first one found: a cross-leg fake is usually blocked by - // two different winners, and that is exactly the case being measured here - auto& blockers = diag->blockersByKey[candKey]; - for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { - const auto itOwner = ownerOfLeg.find(legId); - if (itOwner == ownerOfLeg.end()) { - continue; - } - if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { - blockers.push_back(itOwner->second); // the same winner may hold both legs - } - } - } + template + void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + { + mRelaxedKeys.clear(); + // step 1: SVertexer V0s -> dedup candidates + for (const auto& collision : collisions) { + if constexpr (isMC) { + if (!collision.has_mcCollision()) { continue; } - usedLegs[v0Cand.posID] = true; - usedLegs[v0Cand.eleID] = true; - if (diag != nullptr) { - ownerOfLeg[v0Cand.posID] = candKey; - ownerOfLeg[v0Cand.eleID] = candKey; - } - stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); - stored_fullv0Ids.emplace_back(v0Cand.v0ID, v0Cand.colID, v0Cand.posID, v0Cand.eleID); - nv0_map[v0Cand.colID]++; } - // sort accepted candidates by collision ID to be compatible with table - std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), - [](const auto& a, const auto& b) { - return std::get<1>(a) < std::get<1>(b); - }); + if (!collision.isSelected()) { + continue; + } - // End of deduplication + // if constexpr (isTriggerAnalysis) { + // if (collision.triggerMask_raw() == 0) { + // continue; + // } + // } + + nv0_map[collision.globalIndex()] = 0; + + const auto& bc = collision.template foundBC_as(); + initCCDB(bc); + registry.fill(HIST("hCollisionCounter"), 1); + + updateCCDB(bc); // delay update until is needed + + vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF + const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); + // LOGF(info, "n v0 = %d", v0s_per_coll.size()); + for (const auto& v0 : v0s_per_coll) { + // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); + fillV0Table(v0, false); + } // end of v0 loop + } // end of collision loop + + // photon finder + if (recovery.runFinder) { + runPhotonFinder(collisions, tracks); + } + + stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF + stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + + // step 3: deduplication + switch (dedup.deduplicationMode.value) { + case V0DeduplicationMode::Pairwise: + selectPairwise(diag); + break; + case V0DeduplicationMode::KeepAll: + keepAll(); + break; + case V0DeduplicationMode::GroupMatching: + selectByConflictGroup(diag); + break; + default: + selectGreedy(tracks.size(), diag); + break; + } + + // split legs, ambiguity resolver (on the stored photons only) + if (dedup.resolveSplitLegs) { + resolveSplitLegConflicts(tracks, diag); + } + if (recovery.runSwapResolver) { + runSwapResolver(collisions, tracks, diag); } if (diag != nullptr) { @@ -1429,8 +1995,7 @@ struct PhotonConversionBuilder { const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); std::unordered_map> candsPerColl; for (const auto& c : vecV0Dedup) { - if (storedSetSkim.contains(std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)))) { + if (storedSetSkim.contains(keyOf(c))) { candsPerColl[c.colID].push_back(&c); } } @@ -1458,26 +2023,38 @@ struct PhotonConversionBuilder { } } + // refit the stored photons and write the tables (SVertexer V0s through fillV0Table, the others directly) for (const auto& fullv0Id : stored_fullv0Ids) { - auto v0Id = std::get<0>(fullv0Id); - const auto collisionId = std::get<1>(fullv0Id); + auto v0Id = fullv0Id.v0Id; + const auto collisionId = fullv0Id.collisionId; if (skimRejected.contains(collisionId)) { continue; // collision failed the skim - do not store its photons/legs } - // auto posId = std::get<2>(fullv0Id); - // auto eleId = std::get<3>(fullv0Id); + // auto posId = fullv0Id.posId; + // auto eleId = fullv0Id.eleId; // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); - auto v0 = v0s.rawIteratorAt(v0Id); + if (v0Id >= 0) { + auto v0 = v0s.rawIteratorAt(v0Id); + if constexpr (enableFilter) { + auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. + if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); + } + + fillV0Table(v0, true); + continue; + } + const auto collision = collisions.rawIteratorAt(collisionId); if constexpr (enableFilter) { - auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. - if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + if (!(collision.neeuls() >= 1 || collision.neeuls() + nv0_map[collisionId] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) continue; } - // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); } - - fillV0Table(v0, true); + fitV0Candidate(collision, tracks.rawIteratorAt(fullv0Id.posId), tracks.rawIteratorAt(fullv0Id.eleId), v0Id, true, + FitOptions{.nonSVertexer = true, .relaxAP = mRelaxedKeys.contains(fullv0Id)}); } // end of fullv0Id loop // for (const auto& collision : collisions) { @@ -1576,8 +2153,71 @@ struct PhotonConversionBuilder { } } - template - void fillDedupTruthDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + struct PhotonFate { + bool alive = false; + int best = -1; // best-score candidate + std::vector cands; // all true candidates of this photon + std::set aliveColls; // collisions with a surviving candidate + }; + + // which collision the copies of a true photon end up in + template + void fillCopyDiagnostics(TCollisions const& collisions, TMCParticles const& mcparticles, DedupDiag const& diag, std::map const& fate) + { + auto isRightColl = [&](int64_t colId, int64_t mid) { + const auto col = collisions.rawIteratorAt(colId); + return col.has_mcCollision() && col.mcCollisionId() == mcparticles.iteratorAt(mid).mcCollisionId(); + }; + auto dzPointing = [&](V0CandidateHelper const& c) { + const auto col = collisions.rawIteratorAt(c.colID); + const float pt = std::hypot(c.pxAtPV, c.pyAtPV); + return pt > 0.f ? std::fabs(c.svZ - std::hypot(c.svX - col.posX(), c.svY - col.posY()) * c.pzAtPV / pt - col.posZ()) : 999.f; // o2-linter: disable=magic-number (no direction) + }; + for (const auto& [mid, f] : fate) { + std::set copyColls; + bool rightAvailable = false; + for (const int& ci : f.cands) { + copyColls.insert(diag.snapshot[ci].colID); + rightAvailable = rightAvailable || isRightColl(diag.snapshot[ci].colID, mid); + } + int choice = 3; // o2-linter: disable=magic-number (bin 3 = lost) + if (f.alive) { + const bool storedRight = std::any_of(f.aliveColls.begin(), f.aliveColls.end(), [&](int64_t c) { return isRightColl(c, mid); }); + choice = storedRight ? 0 : (rightAvailable ? 1 : 2); + } + const int nCopies = static_cast(copyColls.size()); + registry.fill(HIST("MCDedup/Copies/hChoice"), static_cast(std::min(nCopies, 5)), static_cast(choice)); // o2-linter: disable=magic-number (last bin) + constexpr int MinCopies = 2; + if (nCopies < MinCopies || !rightAvailable) { + continue; + } + int bestDz = -1, bestPA = -1, bestPA3D = -1; + for (const int& ci : f.cands) { + const auto& c = diag.snapshot[ci]; + const float right = isRightColl(c.colID, mid) ? 1.f : 0.f; + registry.fill(HIST("MCDedup/Copies/hPAxy"), right, std::acos(std::clamp(c.cosPAxy, -1.f, 1.f))); + registry.fill(HIST("MCDedup/Copies/hPA3D"), right, std::acos(std::clamp(c.cosPA3D, -1.f, 1.f))); + if (bestDz < 0 || dzPointing(c) < dzPointing(diag.snapshot[bestDz])) { + bestDz = ci; + } + if (bestPA < 0 || c.cosPAxy > diag.snapshot[bestPA].cosPAxy) { + bestPA = ci; + } + if (bestPA3D < 0 || c.cosPA3D > diag.snapshot[bestPA3D].cosPA3D) { + bestPA3D = ci; + } + } + if (f.alive) { // same photons for all rules + registry.fill(HIST("MCDedup/Copies/hRule"), 0.f, choice == 0 ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 1.f, isRightColl(diag.snapshot[bestDz].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 2.f, isRightColl(diag.snapshot[bestPA].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 3.f, isRightColl(diag.snapshot[bestPA3D].colID, mid) ? 1.f : 0.f); + } + } + } + + template + void fillDedupTruthDiagnostics(TCollisions const& collisions, TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) { const int nCand = static_cast(diag.snapshot.size()); if (nCand == 0) { @@ -1591,8 +2231,7 @@ struct PhotonConversionBuilder { std::map indexOfKey; for (int i = 0; i < nCand; ++i) { const auto& c = diag.snapshot[i]; - key[i] = std::make_tuple(static_cast(c.v0ID), static_cast(c.colID), - static_cast(c.posID), static_cast(c.eleID)); + key[i] = keyOf(c); indexOfKey[key[i]] = i; cls[i] = static_cast(classifyV0Truth(tracks.rawIteratorAt(c.posID), tracks.rawIteratorAt(c.eleID), mcparticles, motherPos[i], motherEle[i])); @@ -1602,12 +2241,6 @@ struct PhotonConversionBuilder { } } - struct PhotonFate { - bool alive = false; - int best = -1; // best-score candidate, only used for the margin plots - std::vector cands; // all true candidates of this photon - std::set aliveColls; // collisions with a surviving candidate - }; std::map fate; for (int i = 0; i < nCand; ++i) { if (cls[i] != kV0True) { @@ -1617,7 +2250,7 @@ struct PhotonConversionBuilder { f.cands.push_back(i); if (storedSet.contains(key[i]) > 0) { f.alive = true; - f.aliveColls.insert(std::get<1>(key[i])); + f.aliveColls.insert(key[i].collisionId); } if (f.best < 0 || diag.snapshot[i].score < diag.snapshot[f.best].score) { f.best = i; @@ -1683,7 +2316,19 @@ struct PhotonConversionBuilder { } } - // ---- truth pairs, grouped by MC collision ---------------------------- + if (dedup.cfgExtendedDiag) { + fillCopyDiagnostics(collisions, mcparticles, diag, fate); + } + + // stored cross-leg fakes by collisions + std::map, std::set> storedCrossColl; + for (int i = 0; i < nCand; ++i) { + if (cls[i] == kV0CrossLegFake && storedSet.contains(key[i])) { + storedCrossColl[{motherPos[i], motherEle[i]}].insert(key[i].collisionId); + } + } + + // truth pairs, grouped by MC collision std::map> mothersByMcColl; for (const auto& [mid, f] : fate) { mothersByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); @@ -1720,6 +2365,21 @@ struct PhotonConversionBuilder { } continue; } + { // the pair stored as the crossed pairing (A+B-, B+A-) in one collision or one crossed pair only + const auto itAB = storedCrossColl.find({mids[a], mids[b]}); + const auto itBA = storedCrossColl.find({mids[b], mids[a]}); + bool bothCrossed = false; + if (itAB != storedCrossColl.end() && itBA != storedCrossColl.end()) { + for (const auto& c : itAB->second) { + bothCrossed = bothCrossed || itBA->second.contains(c); + } + } + if (bothCrossed) { + registry.fill(HIST("MCDedup/Pairs/surviveAsCrossedPairing/hQ"), qTrue); + } else if (itAB != storedCrossColl.end() || itBA != storedCrossColl.end()) { + registry.fill(HIST("MCDedup/Pairs/halfCrossedStored/hQ"), qTrue); + } + } if (nLost == 1) { fillTruthPair(kOneLost, qTrue, dEta); } else { @@ -1771,6 +2431,64 @@ struct PhotonConversionBuilder { } } + // MC, cfgExtendedDiag: class of the stored photons by source, stored photon pairs at low q, outcome of the resolver tests + template + void fillExtendedDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + { + struct Stored { + CandKey key; + int cls = 0; + int64_t mPos = -1; + int64_t mEle = -1; + std::array p{}; + }; + std::map candOfKey; + for (const auto& c : diag.snapshot) { + candOfKey[keyOf(c)] = &c; + } + std::map> storedOfColl; + for (const auto& key : diag.stored) { + const auto it = candOfKey.find(key); + if (it == candOfKey.end()) { + continue; + } + Stored s{key, 0, -1, -1, {it->second->pxAtPV, it->second->pyAtPV, it->second->pzAtPV}}; + s.cls = classifyV0Truth(tracks.rawIteratorAt(key.posId), tracks.rawIteratorAt(key.eleId), mcparticles, s.mPos, s.mEle); + const float pt = std::hypot(s.p[0], s.p[1]); + registry.fill(HIST("MCDedup/Stored/hClassSource"), static_cast(s.cls), key.v0Id < 0 ? 1.f : 0.f, pt); + storedOfColl[key.collisionId].push_back(s); + } + constexpr float MaxQ = 0.1f; + for (const auto& [col, photons] : storedOfColl) { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + const float q = qInv(a.p[0], a.p[1], a.p[2], b.p[0], b.p[1], b.p[2]); + if (q > MaxQ) { + continue; + } + int cat = 2; + if (a.cls == kV0True && b.cls == kV0True && a.mPos != b.mPos) { + cat = 0; + } else if (a.cls == kV0CrossLegFake && b.cls == kV0CrossLegFake && a.mPos == b.mEle && a.mEle == b.mPos) { + cat = 1; + } + registry.fill(HIST("MCDedup/Stored/hPairClass"), static_cast(cat), q); + } + } + } + for (const auto& s : diag.swaps) { + const std::array, 4> legs = {{{s.keyI.posId, s.keyI.eleId}, {s.keyJ.posId, s.keyJ.eleId}, {s.keyI.posId, s.keyJ.eleId}, {s.keyJ.posId, s.keyI.eleId}}}; + std::array isTrue{}; + for (int k = 0; k < 4; ++k) { // o2-linter: disable=magic-number (four leg pairs) + int64_t mPos = -1, mEle = -1; + isTrue[k] = classifyV0Truth(tracks.rawIteratorAt(legs[k].first), tracks.rawIteratorAt(legs[k].second), mcparticles, mPos, mEle) == kV0True ? 1 : 0; + } + registry.fill(HIST("MCDedup/Swap/hCategory"), static_cast(3 * (isTrue[0] + isTrue[1]) + isTrue[2] + isTrue[3]), s.swapped ? 1.f : 0.f, s.q); + } + } + void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) { build(collisions, v0s, tracks, bcs); @@ -1788,7 +2506,10 @@ struct PhotonConversionBuilder { { DedupDiag diag; build(collisions, v0s, tracks, bcs, &diag); - fillDedupTruthDiagnostics(tracks, mcparticles, diag); + fillDedupTruthDiagnostics(collisions, tracks, mcparticles, diag); + if (dedup.cfgExtendedDiag) { + fillExtendedDiagnostics(tracks, mcparticles, diag); + } } PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); diff --git a/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h new file mode 100644 index 00000000000..2749319def2 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h @@ -0,0 +1,177 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ConversionPointProfile.h +/// \brief radius-profiled conversion point of a photon conversion (alternative to the helix touching point) +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ +#define PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +constexpr int NProfPar = 4; // (rphi, z, phi, tgl) +constexpr int NProfR = 75; +constexpr float ProfRMin = 10.f; // trial radii 10 .. 84 cm + +/// \brief chi2 of a leg pair vs the trial radius, its minimum and class +struct RadiusProfile { + std::array chi2{}; + std::array ok{}; + int status = 4; + int iMin = -1; + float rMin = -1.f; + float chi2Min = -1.f; + float halfWidth = -1.f; + std::array xyz{-999.f, -999.f, -999.f}; +}; + +/// \brief parameters and covariance of one leg at every trial radius +struct LegScan { + std::array ok{}; + std::array, NProfR> m{}; // phi of the point, z, phi of the direction, tgl + std::array, NProfR> c{}; // lower triangle of the (rphi, z, phi, tgl) covariance + std::array, NProfR> pos{}; // global point +}; + +//_______________________________________________________________________ +/// \brief propagate one leg inward from its IU to a radius and keep its parameters there +/// \param trackIU leg at its innermost update +/// \param bz magnetic field +/// \param matCorr material correction of the propagation +/// \param out output: parameters and covariance per radius, ok = false outside the reach of the leg +inline void cpScanLeg(o2::track::TrackParCov const& trackIU, const float bz, const o2::base::Propagator::MatCorrType matCorr, LegScan& out) +{ + auto trk = trackIU; + const auto iu = trk.getXYZGlo(); + const float rIU = std::hypot(iu.X(), iu.Y()); + for (int k = NProfR - 1; k >= 0; --k) { + const float r = ProfRMin + k; + out.ok[k] = false; + if (r >= rIU - 0.1f) { // o2-linter: disable=magic-number (margin) + continue; + } + float xl = 0.f; + if (!trk.getXatLabR(r, xl, bz, o2::track::DirInward)) { + break; + } + if (!o2::base::Propagator::Instance()->propagateToX(trk, xl, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorr)) { + break; + } + const auto g = trk.getXYZGlo(); + const float snp = trk.getSnp(); + const float f = 1.f / std::sqrt(std::max(1.f - snp * snp, 1e-4f)); // o2-linter: disable=magic-number (floor) + out.m[k] = {std::atan2(g.Y(), g.X()), trk.getZ(), trk.getAlpha() + std::asin(std::clamp(snp, -1.f, 1.f)), trk.getTgl()}; + out.c[k] = {trk.getSigmaY2(), trk.getSigmaZY(), trk.getSigmaZ2(), trk.getSigmaSnpY() * f, trk.getSigmaSnpZ() * f, trk.getSigmaSnp2() * f * f, trk.getSigmaTglY(), trk.getSigmaTglZ(), trk.getSigmaTglSnp() * f, trk.getSigmaTgl2()}; + out.pos[k] = {g.X(), g.Y(), g.Z()}; + out.ok[k] = true; + } +} + +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out); + +//_______________________________________________________________________ +/// \brief chi2 of a common point and a common direction of two legs at every trial radius, then minimum and class +/// \param p scan of the positive leg +/// \param n scan of the negative leg +/// \param zSig2Pos extra z variance of the positive leg (cm^2, legs without ITS) +/// \param zSig2Ele extra z variance of the negative leg (cm^2, legs without ITS) +/// \param out output: profile, minimum, half width and class +inline void cpRadiusProfile(LegScan const& p, LegScan const& n, const float zSig2Pos, const float zSig2Ele, RadiusProfile& out) +{ + out = RadiusProfile{}; + for (int k = 0; k < NProfR; ++k) { + out.ok[k] = p.ok[k] && n.ok[k]; + if (!out.ok[k]) { + continue; + } + const float r = ProfRMin + k; + ROOT::Math::SVector d; + d[0] = r * RecoDecay::constrainAngle(p.m[k][0] - n.m[k][0], -o2::constants::math::PI); + d[1] = p.m[k][1] - n.m[k][1]; + d[2] = RecoDecay::constrainAngle(p.m[k][2] - n.m[k][2], -o2::constants::math::PI); + d[3] = p.m[k][3] - n.m[k][3]; + ROOT::Math::SMatrix> cov; + int idx = 0; + for (int i = 0; i < NProfPar; ++i) { + for (int j = 0; j <= i; ++j) { + cov(i, j) = p.c[k][idx] + n.c[k][idx]; + ++idx; + } + } + cov(1, 1) += zSig2Pos + zSig2Ele; + if (!cov.Invert()) { + out.ok[k] = false; + continue; + } + out.chi2[k] = static_cast(ROOT::Math::Similarity(d, cov)); + if (out.iMin < 0 || out.chi2[k] < out.chi2Min) { + out.iMin = k; + out.chi2Min = out.chi2[k]; + } + } + cpProfileFinish(p, n, out); +} +//_______________________________________________________________________ +/// \brief minimum, half width of the dchi2 < 1 region, other minima and class of a filled profile +/// \param p scan of the positive leg (point at the minimum) +/// \param n scan of the negative leg (point at the minimum) +/// \param out profile, completed in place +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out) +{ + if (out.iMin < 0) { + return; + } + out.rMin = ProfRMin + out.iMin; + out.xyz = {0.5f * (p.pos[out.iMin][0] + n.pos[out.iMin][0]), 0.5f * (p.pos[out.iMin][1] + n.pos[out.iMin][1]), 0.5f * (p.pos[out.iMin][2] + n.pos[out.iMin][2])}; + // valid range, dchi2 < 1 region around the minimum, other minima + int kLo = out.iMin; + int kHi = kLo; + while (kLo - 1 >= 0 && out.ok[kLo - 1] && out.chi2[kLo - 1] - out.chi2Min < 1.f) { + --kLo; + } + while (kHi + 1 < NProfR && out.ok[kHi + 1] && out.chi2[kHi + 1] - out.chi2Min < 1.f) { + ++kHi; + } + out.halfWidth = 0.5f * (kHi - kLo) + 0.5f; + const bool edge = (out.iMin == 0 || !out.ok[out.iMin - 1]) || (out.iMin == NProfR - 1 || !out.ok[out.iMin + 1]); + bool other = false; + for (int k = 1; k < NProfR - 1 && !other; ++k) { + if (k >= kLo && k <= kHi) { + continue; + } + if (out.ok[k] && out.ok[k - 1] && out.ok[k + 1] && out.chi2[k] <= out.chi2[k - 1] && out.chi2[k] <= out.chi2[k + 1] && out.chi2[k] - out.chi2Min < 1.f) { + other = true; + } + } + out.status = edge ? 3 : (other ? 2 : (out.halfWidth > 5.f ? 1 : 0)); // o2-linter: disable=magic-number (broad: half width above 5 cm) +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ diff --git a/PWGEM/PhotonMeson/Utils/PCMUtilities.h b/PWGEM/PhotonMeson/Utils/PCMUtilities.h index e69703a3ec5..f8f6daed325 100644 --- a/PWGEM/PhotonMeson/Utils/PCMUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PCMUtilities.h @@ -21,12 +21,18 @@ #include #include +#include #include +#include #include +#include #include #include // IWYU pragma: keep (for rotate) -#include // IWYU pragma: keep (do not replace with Math/Vector2Dfwd.h) +#include +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector2Dfwd.h) #include #include @@ -37,12 +43,22 @@ #include //_______________________________________________________________________ +/// \brief Armenteros-Podolanski cut of photon conversions: (alpha, qT) inside the ellipse with half axes alpha_max and qt_max +/// \param alpha Armenteros alpha +/// \param qt Armenteros qT (GeV/c) +/// \param alpha_max half axis in alpha +/// \param qt_max half axis in qT (GeV/c) +/// \return true if inside the ellipse inline bool checkAP(const float alpha, const float qt, const float alpha_max = 0.95, const float qt_max = 0.05) { float ellipse = std::pow(alpha / alpha_max, 2) + std::pow(qt / qt_max, 2); return (ellipse < 1.0); } //_______________________________________________________________________ +/// \brief Armenteros alpha of a V0: (pL+ - pL-) / (pL+ + pL-), pL = momentum of a leg along the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return alpha inline float v0Alpha(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); @@ -51,6 +67,10 @@ inline float v0Alpha(float pxpos, float pypos, float pzpos, float pxneg, float p return (lQlPos - lQlNeg) / (lQlPos + lQlNeg); // longitudinal momentum asymmetry of v0 } //_______________________________________________________________________ +/// \brief Armenteros qT of a V0: momentum of the negative leg transverse to the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return qT (GeV/c) inline float v0Qt(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p2(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); @@ -58,6 +78,13 @@ inline float v0Qt(float pxpos, float pypos, float pzpos, float pxneg, float pyne return std::sqrt(RecoDecay::p2(pxneg, pyneg, pzneg) - dp * dp / momTot); // qt of v0 } //_______________________________________________________________________ +/// \brief conversion point of two legs (Run 2 recipe): xy = touching point of the two helix circles (centres weighted with the +/// radius of the other circle), z = radius-weighted mean of the leg z after propagating each leg to its point closest to it +/// \param prop propagator (also gives the nominal field) +/// \param trackPosInformation positive leg +/// \param trackNegInformation negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation template inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track::TrackParametrizationWithError& trackPosInformation, const o2::track::TrackParametrizationWithError& trackNegInformation, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { @@ -187,6 +214,12 @@ inline std::array getPropMomentumFromTrackHelix(const float s, const T } //_______________________________________________________________________ +/// \brief conversion point of two legs from their track rows (Vtx_recalculationParCov) +/// \param prop propagator (also gives the nominal field) +/// \param lTrackPos positive leg +/// \param lTrackNeg negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation template inline void Vtx_recalculation(o2::base::Propagator* prop, T1 lTrackPos, T2 lTrackNeg, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { @@ -262,6 +295,11 @@ inline V0TruthClass classifyV0Truth(TTrack const& pos, TTrack const& ele, TMCPar return kV0OtherFake; } +//_______________________________________________________________________ +/// \brief electron-PID compatibility of the two legs: mean |n sigma_e| + 2 * (n sigma_e+ + n sigma_e-) in the TPC +/// \param pos positive leg +/// \param ele negative leg +/// \return kappa template float getV0Kappa(TLeg const& pos, TLeg const& ele) { @@ -269,4 +307,139 @@ float getV0Kappa(TLeg const& pos, TLeg const& ele) return kappa; } +//_______________________________________________________________________ +/// \brief gap between the helix circles of two legs in xy +/// \param cPos circle of the positive leg +/// \param cEle circle of the negative leg +/// \return distance of the centres minus the sum of the radii (cm); < 0: the circles overlap +inline float circleGap(o2::track::TrackAuxPar const& cPos, o2::track::TrackAuxPar const& cEle) +{ + return std::hypot(cEle.xC - cPos.xC, cEle.yC - cPos.yC) - (cPos.rC + cEle.rC); +} + +/// \brief return value of tpcDriftSide if the side cannot be determined +constexpr int TPCSideUndetermined = 2; + +//_______________________________________________________________________ +/// \brief TPC side in which a track drifted, from the TPC side flags, else from z, tgl and the time asymmetry +/// \param track track row (flags, z, tgl, tpcDeltaTFwd, tpcDeltaTBwd) +/// \return 1 A side, -1 C side, 0 both sides (crosses the central electrode), TPCSideUndetermined +template +inline int tpcDriftSide(TTrack const& track) +{ + constexpr float MaxCECrossingDeltaTNS = 1000.f; + const auto sides = track.flags() & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC); + if (sides == o2::aod::track::TPCSideA) { + return 1; + } + if (sides == o2::aod::track::TPCSideC) { + return -1; + } + if (sides != 0) { + return 0; + } + const float dFwd = track.tpcDeltaTFwd(); + const float dBwd = track.tpcDeltaTBwd(); + if (dFwd == dBwd && dFwd < MaxCECrossingDeltaTNS) { + return 0; + } + const bool zPositive = track.z() > 0.f; + const bool tglPositive = track.tgl() > 0.f; + if (zPositive == tglPositive) { + return tglPositive ? 1 : -1; + } + if (dBwd == 0.f && dFwd > 0.f) { + return zPositive ? 1 : -1; + } + if (dBwd > 0.f) { + return tglPositive ? 1 : -1; + } + return TPCSideUndetermined; +} + +/// \brief z and momentum of a track helix at a point of its circle (helixPointAt) +struct HelixPoint { + bool ok = false; + bool inward = false; + float z = 0.f; + std::array p{}; +}; + +//_______________________________________________________________________ +/// \brief z and momentum of a track helix at an xy point of its circle +/// \param par track parameters +/// \param xP x of the point on the circle (cm) +/// \param yP y of the point on the circle (cm) +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param bz magnetic field (kG) +/// \return ok = false for a degenerate circle; inward = the track moves towards the beam at the point (more than 10 cm from +/// the beam), then the momentum is reversed; z and momentum at the point +inline HelixPoint helixPointAt(o2::track::TrackPar const& par, const float xP, const float yP, const float xB, const float yB, const float bz) +{ + HelixPoint h; + const o2::track::TrackAuxPar c(par, bz); + std::array r{}, p{}; + par.getXYZGlo(r); + par.getPxPyPzGlo(p); + const float pt = std::hypot(p[0], p[1]); + if (!(c.rC > 0.f) || !(pt > 0.f)) { + return h; + } + const float sense = (r[0] - c.xC) * p[1] - (r[1] - c.yC) * p[0] > 0.f ? 1.f : -1.f; + float back = RecoDecay::constrainAngle(sense * (std::atan2(r[1] - c.yC, r[0] - c.xC) - std::atan2(yP - c.yC, xP - c.xC)), 0.f); + const float tx = -sense * (yP - c.yC) / c.rC; + const float ty = sense * (xP - c.xC) / c.rC; + constexpr float MinRadiusOrientation = 10.f; + h.inward = std::hypot(xP - xB, yP - yB) > MinRadiusOrientation && tx * (xP - xB) + ty * (yP - yB) < 0.f; + if (!h.inward) { + if (back > o2::constants::math::PI && (r[0] - xB) * p[0] + (r[1] - yB) * p[1] > 0.f) { + back -= o2::constants::math::TwoPI; // o2-linter: disable=two-pi-add-subtract (signed arc length to a point behind the track, not an angle range) + } + h.z = r[2] - par.getTgl() * c.rC * back; + h.p = {pt * tx, pt * ty, pt * par.getTgl()}; + } else { + h.z = r[2] + par.getTgl() * c.rC * (back > 0.f ? o2::constants::math::TwoPI - back : 0.f); + h.p = {-pt * tx, -pt * ty, -pt * par.getTgl()}; + } + h.ok = true; + return h; +} + +//_______________________________________________________________________ +/// \brief compatibility of two track pieces with one track +/// \param trA first track +/// \param trB second track +/// \param bz magnetic field (kG) +/// \return chi2 of (y, snp, tgl, q/pt) at a common x with the summed covariances (4 dof); -1 if the propagation or the inversion fails +template +inline float splitPieceChi2(TTrack const& trA, TTrack const& trB, const float bz) +{ + auto a = getTrackParCov(trA); + auto b = getTrackParCov(trB); + if (!a.rotate(b.getAlpha()) || !a.propagateTo(b.getX(), bz)) { + a = getTrackParCov(trA); + b = getTrackParCov(trB); + if (!b.rotate(a.getAlpha()) || !b.propagateTo(a.getX(), bz)) { + return -1.f; + } + } + ROOT::Math::SMatrix> c; + c(0, 0) = a.getSigmaY2() + b.getSigmaY2(); + c(1, 0) = a.getSigmaSnpY() + b.getSigmaSnpY(); + c(1, 1) = a.getSigmaSnp2() + b.getSigmaSnp2(); + c(2, 0) = a.getSigmaTglY() + b.getSigmaTglY(); + c(2, 1) = a.getSigmaTglSnp() + b.getSigmaTglSnp(); + c(2, 2) = a.getSigmaTgl2() + b.getSigmaTgl2(); + c(3, 0) = a.getSigma1PtY() + b.getSigma1PtY(); + c(3, 1) = a.getSigma1PtSnp() + b.getSigma1PtSnp(); + c(3, 2) = a.getSigma1PtTgl() + b.getSigma1PtTgl(); + c(3, 3) = a.getSigma1Pt2() + b.getSigma1Pt2(); + if (!c.Invert()) { + return -1.f; + } + const ROOT::Math::SVector d(a.getY() - b.getY(), a.getSnp() - b.getSnp(), a.getTgl() - b.getTgl(), a.getQ2Pt() - b.getQ2Pt()); + return static_cast(ROOT::Math::Similarity(c, d)); +} + #endif // PWGEM_PHOTONMESON_UTILS_PCMUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h new file mode 100644 index 00000000000..757d86b5a86 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h @@ -0,0 +1,309 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file V0DedupUtilities.h +/// \brief helpers of the photon conversion builder: conflict-group solver of the deduplication and the geometry of the photon finder +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ +#define PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ + +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +//_______________________________________________________________________ +/// \brief q_inv of two massless particles (photons) +/// \param px1 px of the first photon (GeV/c), same for py1, pz1 +/// \param px2 px of the second photon (GeV/c), same for py2, pz2 +/// \return sqrt(2 (|p1| |p2| - p1 . p2)) (GeV/c) +inline float qInv(const float px1, const float py1, const float pz1, const float px2, const float py2, const float pz2) +{ + return std::sqrt(std::max(0.f, 2.f * (std::hypot(px1, py1, pz1) * std::hypot(px2, py2, pz2) - px1 * px2 - py1 * py2 - pz1 * pz2))); +} + +/// \brief result of solveConflictGroup +struct ConflictGroupSolution { + uint32_t bestMask = 0; + int bestCount = 0; + float bestScore = 0.f; + std::vector bestWithCount; // per candidate: size of the best subset that contains it + std::vector bestWithScore; // per candidate: score sum of that subset +}; + +//_______________________________________________________________________ +/// \brief best leg-disjoint subset of a conflict group of the deduplication (mode 2), exact enumeration of all subsets +/// \param legMask per candidate: bit mask of its legs (group-local bits); at most 16 candidates +/// \param score per candidate: dedup score, lower = better +/// \param s0 objective: <= 0 maximum number of candidates, then minimum score sum; > 0 maximum sum of max(s0 - score, 1e-3), +/// ties by the score sum +/// \param memberBest also find, per candidate, the best subset that contains it (MC diagnostics) +/// \return mask of the best subset, its size and score sum, and the per-candidate results +inline ConflictGroupSolution solveConflictGroup(std::vector const& legMask, std::vector const& score, const float s0, const bool memberBest) +{ + const int nMembers = static_cast(legMask.size()); + const bool weighted = s0 > 0.f; + constexpr float MinWeight = 1e-3f; + constexpr float WeightTolerance = 1e-6f; + ConflictGroupSolution sol; + sol.bestWithCount.assign(nMembers, -1); + sol.bestWithScore.assign(nMembers, 0.f); + float bestWeight = -1.f; + for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { + uint64_t legs = 0; + float scoreSum = 0.f; + float weightSum = 0.f; + int count = 0; + bool disjoint = true; + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if ((legs & legMask[k]) != 0) { + disjoint = false; + break; + } + legs |= legMask[k]; + scoreSum += score[k]; + weightSum += std::max(s0 - score[k], MinWeight); + ++count; + } + if (!disjoint) { + continue; + } + const bool better = weighted ? (weightSum > bestWeight + WeightTolerance || (std::fabs(weightSum - bestWeight) <= WeightTolerance && scoreSum < sol.bestScore)) + : (count > sol.bestCount || (count == sol.bestCount && scoreSum < sol.bestScore)); + if (better) { + sol.bestCount = count; + sol.bestScore = scoreSum; + bestWeight = weightSum; + sol.bestMask = mask; + } + if (memberBest) { + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if (count > sol.bestWithCount[k] || (count == sol.bestWithCount[k] && scoreSum < sol.bestWithScore[k])) { + sol.bestWithCount[k] = count; + sol.bestWithScore[k] = scoreSum; + } + } + } + } + return sol; +} + +/// \brief steps of the photon finder (pairing of free legs at the touching point of their helix circles); evalFinderPair returns the last step passed +enum FinderStep { + kFindTglWindow = 0, + kFindTime, + kFindGap, + kFindRTouch, + kFindPointing, + kFindAlongMotion, + kFindZMatch, + kFindCollision, + kNFindStep +}; + +/// \brief collision for the photon finder: time (ns, relative to the first BC of the DF), index, vertex (cm) +struct FinderCollision { + double t = 0.; + int64_t id = -1; + float x = 0.f, y = 0.f, z = 0.f; +}; + +/// \brief leg for the photon finder: track parameters, helix circle, TPC drift side and the time window (ns) of the track +struct FinderLeg { + int64_t id = -1; + float tgl = 0.f; + bool noITS = false; + bool movable = false; + int side = 0; + double t = 0., tMin = 0., tMax = 0.; + o2::track::TrackPar par; + o2::track::TrackAuxPar circle; +}; + +/// \brief cuts of the photon finder (cm) and the magnetic field (kG) +struct FinderCuts { + float maxDeltaTgl = 0.f; + float maxCircleGap = 0.f; + float minRTPConly = 0.f; + float maxRReqITS = 0.f; + float rMargin = 0.f; + float maxR = 0.f; + float minCosXY = 0.f; + float maxDeltaZ = 0.f; + float maxDz = 0.f; + float bz = 0.f; +}; + +//_______________________________________________________________________ +/// \brief geometric test of one leg pair of the photon finder: tgl, time windows, circle gap, touching radius, xy pointing to the +/// beam, direction of motion, z match at the touching point (incl. the drift of TPC-only legs), collision by z +/// \param p positive leg +/// \param e negative leg +/// \param colls collisions of the DF, sorted by time +/// \param cuts cuts of the finder +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param vDrift TPC drift velocity (cm/ns) +/// \param colId output: index of the matched collision, -1 if none +/// \return last FinderStep passed, -1 if the tgl window fails; kFindCollision if a collision matches +inline int evalFinderPair(FinderLeg const& p, FinderLeg const& e, std::vector const& colls, FinderCuts const& cuts, const float xB, const float yB, const double vDrift, int64_t& colId) +{ + colId = -1; + if (std::fabs(p.tgl - e.tgl) > cuts.maxDeltaTgl) { + return -1; + } + const double tLo = std::max(p.tMin, e.tMin); + const double tHi = std::min(p.tMax, e.tMax); + if (tLo > tHi) { + return kFindTglWindow; + } + const auto& cp = p.circle; + const auto& ce = e.circle; + const float dxC = ce.xC - cp.xC; + const float dyC = ce.yC - cp.yC; + const float dC = std::hypot(dxC, dyC); + if (std::fabs(circleGap(cp, ce)) > cuts.maxCircleGap) { + return kFindTime; + } + const float xT = (cp.xC * ce.rC + ce.xC * cp.rC) / (cp.rC + ce.rC); + const float yT = (cp.yC * ce.rC + ce.yC * cp.rC) / (cp.rC + ce.rC); + const float rTouch = std::hypot(xT, yT); + const float rMin = (p.noITS && e.noITS ? cuts.minRTPConly : cuts.maxRReqITS) - cuts.rMargin; + if (rTouch > cuts.maxR || rTouch < rMin) { + return kFindGap; + } + const float dB = std::hypot(xT - xB, yT - yB); + if (!(dC > 0.f) || !(dB > 0.f) || std::fabs((xT - xB) * (-dyC / dC) + (yT - yB) * (dxC / dC)) / dB < cuts.minCosXY) { + return kFindRTouch; + } + auto nearest = [&](o2::track::TrackAuxPar const& c, float& x, float& y) { + const float d = std::max(std::hypot(xT - c.xC, yT - c.yC), 1e-6f); + x = c.xC + c.rC * (xT - c.xC) / d; + y = c.yC + c.rC * (yT - c.yC) / d; + }; + float xP = 0.f, yP = 0.f, xE = 0.f, yE = 0.f; + nearest(cp, xP, yP); + nearest(ce, xE, yE); + const auto hp = helixPointAt(p.par, xP, yP, xB, yB, cuts.bz); + const auto he = helixPointAt(e.par, xE, yE, xB, yB, cuts.bz); + if (!hp.ok || !he.ok || hp.inward || he.inward) { + return kFindPointing; + } + const double b = vDrift * (p.side - e.side); + const double a = (hp.z - p.side * vDrift * p.t) - (he.z - e.side * vDrift * e.t); + const double tBest = b == 0. ? tLo : std::clamp(-a / b, tLo, tHi); + if (std::fabs(a + b * tBest) > cuts.maxDeltaZ) { + return kFindAlongMotion; + } + const float ptG = std::hypot(hp.p[0] + he.p[0], hp.p[1] + he.p[1]); + const float tglG = ptG > 0.f ? (hp.p[2] + he.p[2]) / ptG : 0.f; + float best = std::numeric_limits::max(); + for (auto it = std::lower_bound(colls.begin(), colls.end(), tLo, [](const FinderCollision& c, double t) { return c.t < t; }); it != colls.end() && it->t <= tHi; ++it) { + const auto zPos = static_cast(hp.z + p.side * vDrift * (it->t - p.t)); + const auto zEle = static_cast(he.z + e.side * vDrift * (it->t - e.t)); + const float deltaZ = zPos - zEle; + const float dz = (zPos * ce.rC + zEle * cp.rC) / (cp.rC + ce.rC) - std::hypot(xT - it->x, yT - it->y) * tglG - it->z; + const float dist = std::hypot(dz, deltaZ); + if (dist < best && std::fabs(dz) < cuts.maxDz && std::fabs(deltaZ) < cuts.maxDeltaZ) { + best = dist; + colId = it->id; + } + } + return colId >= 0 ? kFindCollision : kFindZMatch; +} + +/// \brief input features of the finder purity score (naive-Bayes log-likelihood ratio, finderScore_v1, PbPb MC LHC26a5a) +enum FinderScoreFeature { + kScoreCosPA = 0, // 3D cosine of the pointing angle + kScorePCA, // distance of the legs at the conversion point (cm) + kScoreMee, // e+e- mass at the conversion point (GeV/c^2) + kScoreGap, // helix-circle gap of the legs (cm) + kScoreDTgl, // |tgl+ - tgl-| + kScorePsiPair, + kScoreMaxNSigmaEl, // max(|n sigma_e|) of the legs in the TPC + kScoreMinNSigmaPi, // min(|n sigma_pi|) of the legs in the TPC + kNScoreFeatures +}; + +/// \brief log-likelihood-ratio table of one feature: nBins uniform bins in [lo, hi), llr[0] underflow, llr[nBins + 1] overflow +/// (same bin lookup as TH1::FindBin) +struct FinderScoreTable { + int nBins; + double lo; + double hi; + const double* llr; +}; + +/// \brief log(p_true / p_fake) per bin of each feature (finderScore_v1) +inline constexpr std::array FinderLlrCosPA = {-0.42326231102291223, -3.0746401250262112, -2.902679468526141, -2.7737498628462491, -2.8653323764415854, -2.9977154358984874, -2.879287982624958, -2.9449559801189884, -2.8305718792936898, -2.7610493575753212, -2.6743115383729115, -2.739759837578176, -2.7445126982363175, -2.6010956990444813, -2.5377329273302194, -2.4413963521466235, -2.5007549142569676, -2.5484647707016048, -2.3339023465737707, -2.3706555897531327, -2.3350661347670365, -2.3987685637953868, -2.2562477335729185, -2.2430310349096305, -2.1429211398936303, -2.1233771903398493, -2.0022839108013391, -1.9558000169723206, -1.8729479034124974, -1.9065302845458256, -1.8542708039089619, -1.7230031706190478, -1.6490950919627059, -1.633800343346663, -1.5180730364736743, -1.389137677593709, -1.3181114339137541, -1.2615432549457208, -1.1021895055813662, -0.97913284177589832, -0.97669632321874755, -0.81070582095170196, -0.70030094035730539, -0.55130110346066707, -0.34392181466201621, -0.16444806113591265, 0.025741391801742678, 0.22392951134446418, 0.48449501950084528, 0.74029958080748082, 1.3544181524605163, 2.1416870464386246}; +inline constexpr std::array FinderLlrPca = {-0.42328563731423596, 0.74173300442294698, 0.60064005460866021, 0.48774214670698213, 0.43172212684872524, 0.34949695425445604, 0.2876013953788461, 0.13114334603545452, -0.013681195276045386, -0.10436433242742214, -0.27099684361934573, -0.38200720615175005, -0.47403001970996045, -0.57728096929145511, -0.65985610396473016, -0.75403841222733481, -0.76619973024014831, -0.76182156236311549, -0.84274340404963699, -0.93501651159467125, -0.99602554213200045, -1.029448537116822, -1.1240438482723172, -1.124554873172243, -1.2439733445571921, -1.2077440689826793, -1.3081144381420802, -1.3626265850153199, -1.3892300573742717, -1.3978783977548588, -1.4463232066948644, -0.42328563731423596}; +inline constexpr std::array FinderLlrMee = {-0.42329146929478079, 0.82940375143002754, 0.6225570701387575, 0.30541176737301567, -0.042962035242895512, -0.43745174446394713, -0.78901712806679281, -1.1834130413160895, -1.5486037315484942, -1.8446313857300292, -2.2387380284795446, -2.1811493868471543, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079}; +inline constexpr std::array FinderLlrGap = {-0.42329730143842487, -3.0127420263468552, -2.6803080781356927, -2.2310834132674926, -1.852433867664681, -1.4426351719772665, -1.1194240909923954, -0.72769983474283484, -0.24592785900768224, 0.40475570320106047, 1.4082550958014817, 1.1535415604659081, 0.43945228763843225, 0.14326171246634631, -0.19523294768817565, -0.39258756076294099, -0.41539680606824775, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487}; +inline constexpr std::array FinderLlrDTgl = {-0.42330313374517559, 0.84513809550928687, 0.46735826574627454, 0.13082343003051436, -0.12182979016333238, -0.33846868362980387, -0.52696466906551476, -0.72450796222957359, -0.89155673129371438, -1.0310133083500654, -1.2327015991375558, -1.3726345840013925, -1.4519781151564646, -1.7004785855213946, -1.7559986132128569, -1.818203125662764, -0.42330313374517559}; +inline constexpr std::array FinderLlrPsiPair = {-0.42330196727077668, 1.5226081817845367, 0.42399589311642699, 1.6969615689293145, 1.6135799599902634, 0.44573587975283285, 0.50095693425255516, 0.43535965176674207, 0.0028547913604746104, -0.0059523726814127804, 0.61779960880440121, 0.82263503615419142, 0.81841116503800626, 0.99808371366038406, 0.57522686284035063, 0.80047346435133904, 2.521137011895664, 2.2392858597546761}; +inline constexpr std::array FinderLlrMaxNSigmaEl = {-0.42331129924865812, 0.58307082727859216, 0.52708190318359982, 0.41704446150850366, 0.16536503067868297, -0.38368187287820599, -1.4189814933894278, -3.0185512358113016, -5.190101150118414, -0.42331129924865812}; +inline constexpr std::array FinderLlrMinNSigmaPi = {-0.42330896621503916, -0.9609371208084968, -0.46431089172823747, -0.06050992822674324, 0.16077238768804142, 0.3920347340880036, 0.64255032840474002, 0.82818496003043784, 0.96447986209716363, 1.0700938177484094, 1.1082284119787249, 1.017209914244056}; + +/// \brief binning and LLR table of each feature, in the order of FinderScoreFeature +inline constexpr std::array FinderScoreTables = {{ + {50, 0.99000000953674316, 1, FinderLlrCosPA.data()}, + {30, 0, 3, FinderLlrPca.data()}, + {25, 0, 0.05000000074505806, FinderLlrMee.data()}, + {20, -5, 5, FinderLlrGap.data()}, + {15, 0, 0.15000000596046448, FinderLlrDTgl.data()}, + {16, -1.6000000238418579, 1.6000000238418579, FinderLlrPsiPair.data()}, + {8, 0, 4, FinderLlrMaxNSigmaEl.data()}, + {10, 0, 10, FinderLlrMinNSigmaPi.data()}, +}}; + +//_______________________________________________________________________ +/// \brief purity score of a finder candidate: log(p_true / p_fake) summed over the 8 features (naive Bayes) +/// \param x feature values in the order of FinderScoreFeature +/// \return score; > 0: more likely a true photon +inline float finderPurityScore(std::array const& x) +{ + float score = 0.f; + for (int i = 0; i < kNScoreFeatures; ++i) { + const auto& t = FinderScoreTables[i]; + const double v = x[i]; + int bin = 0; + if (v < t.lo) { + bin = 0; + } else if (!(v < t.hi)) { + bin = t.nBins + 1; + } else { + bin = 1 + static_cast(t.nBins * (v - t.lo) / (t.hi - t.lo)); + } + score += t.llr[bin]; + } + return score; +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ From 0d596a818516313ee5d4771e4fef6b36415dbeec Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:14:30 +0200 Subject: [PATCH 400/648] [PWGCF] Add exclusion ranges to femto framework (#18422) --- PWGCF/Femto/Core/baseSelection.h | 51 ++++++++- PWGCF/Femto/Core/cascadeBuilder.h | 121 +++++++------------- PWGCF/Femto/Core/selectionContainer.h | 155 +++++++++++++++++++++++--- PWGCF/Femto/Core/v0Builder.h | 114 ++++++++----------- 4 files changed, 274 insertions(+), 167 deletions(-) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 947e766bb50..5e5b0deab97 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -125,20 +125,24 @@ class BaseSelection addSelection(observableIndex); } - /// \brief Add a static-value based selection for a specific observable. + /// \brief Add a range-based selection (either inside or outside the configured ranges) for a + /// specific observable. /// \param observableIndex Index of the observable. /// \param selectionName Name of the selection. - /// \param selectionValues Vector of threshold values. - /// \param limitType Type of limit (from limits::LimitType). + /// \param selectionRanges Vector of "lower;upper" range strings. /// \param skipMostPermissiveBit Whether to skip the loosest threshold when assembling the bitmask. /// \param isMinimalCut Whether this cut is mandatory (must be passed for the candidate to be accepted). /// \param isOptionalCut Whether this cut is optional (candidate is accepted if any optional cut passes). + /// \param limitType Either limits::kInsideRange (value must fall inside) or limits::kOutsideRange + /// (value must fall outside, e.g. rejecting a competing mass hypothesis). A + /// fully-open range entry (";") is exempt for either type -- see SelectionContainer. void addSelection(int observableIndex, std::string const& selectionName, std::vector const& selectionRanges, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) { // check index if (static_cast(observableIndex) >= NumObservables) { @@ -149,7 +153,8 @@ class BaseSelection selectionRanges, mPassThrough ? false : skipMostPermissiveBit, mPassThrough ? false : isMinimalCut, - mPassThrough ? false : isOptionalCut); + mPassThrough ? false : isOptionalCut, + limitType); addSelection(observableIndex); } @@ -248,6 +253,42 @@ class BaseSelection } } + /// \brief Evaluate the same observable for two independently-computed values of the same + /// candidate and store only the logical AND of the two resulting per-threshold bitmasks + /// \param observableIndex Index of the observable. + /// \param value1 First value to evaluate. + /// \param value2 Second value to evaluate. + void evaluateObservable(int observableIndex, T value1, T value2) + { + // if there are no selections configured, bail out + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; + } + // if a previous observable already failed a minimal selection, + // there is no point in evaluating further observables + if (!mPassesMinimalSelections) { + return; + } + auto& container = mSelectionContainers.at(observableIndex); + container.evaluate(value1); + auto const bitmask1 = container.getRawBitmask(); + container.evaluate(value2); + auto const bitmask2 = container.getRawBitmask(); + container.setBitmask((bitmask1 & bitmask2).to_ullong()); + // if any minimal selection is not fulfilled, the candidate is rejected + if (mHasMinimalSelection) { + if (!container.passesAsMinimalCut()) { + mPassesMinimalSelections = false; + } + } + // if any optional selection is fulfilled, the candidate is accepted + if (mHasOptionalSelection) { + if (container.passesAsOptionalCut()) { + mPassesOptionalSelections = true; + } + } + } + /// \brief Evaluate a single observable against its configured selections. /// \param observableIndex Index of the observable. /// \param values Vector of values of the observable. diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index c0976be17d1..a8ab5f6620c 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -48,19 +48,11 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; - o2::framework::Configurable massLambdaMin{"massLambdaMin", 1.0f, "Minimum mass of Lambda daughters in cascade decay"}; - o2::framework::Configurable massLambdaMax{"massLambdaMax", 1.2f, "Maximum mass of Lambda daughters in cascade decay"}; o2::framework::Configurable massXiMin{"massXiMin", 1.2f, "Minimum Xi mass"}; o2::framework::Configurable massXiMax{"massXiMax", 1.4f, "Maximum Xi mass"}; - o2::framework::Configurable rejectHypothesisOmega{"rejectHypothesisOmega", false, "Rejection of Omega hypothesis for Xi candidates"}; - o2::framework::Configurable rejectMassOmegaMin{"rejectMassOmegaMin", 1.668f, "Reject minimum Omega mass for Xi hypothesis"}; - o2::framework::Configurable rejectMassOmegaMax{"rejectMassOmegaMax", 1.676f, "Reject maximum Omega mass for Xi hypothesis"}; o2::framework::Configurable massOmegaMin{"massOmegaMin", 1.5f, "Minimum Omega mass"}; o2::framework::Configurable massOmegaMax{"massOmegaMax", 1.9f, "Maximum Omega mass"}; - o2::framework::Configurable rejectHypothesisXi{"rejectHypothesisXi", false, "Rejection of Xi hypothesis for Omega candidates"}; - o2::framework::Configurable rejectMassXiMin{"rejectMassXiMin", 1.317f, "Reject Minimum Xi mass for Omega hypothesis"}; - o2::framework::Configurable rejectMassXiMax{"rejectMassXiMax", 1.325f, "Reject Maximum Xi mass for Omega hypothesis"}; }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -74,6 +66,7 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ + o2::framework::Configurable> lambdaMassRanges{"lambdaMassRanges", {"1.0;1.2"}, "Lambda daughter mass ranges (lower;upper) to accept -- bit is set if the cascade's mLambda falls inside the range"}; \ o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ @@ -89,6 +82,7 @@ struct ConfXiBits : o2::framework::ConfigurableGroup { CASCADE_DEFAULT_BITS o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for bachelor tracks"}; o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsigma_Pion| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectOmegaMassRanges{"rejectOmegaMassRanges", {"1.668;1.676"}, "Omega mass ranges (lower;upper) to reject -- bit is set if the candidate's mOmega falls outside the range"}; }; struct ConfOmegaBits : o2::framework::ConfigurableGroup { @@ -96,6 +90,7 @@ struct ConfOmegaBits : o2::framework::ConfigurableGroup { CASCADE_DEFAULT_BITS o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsigma_Kaon| TPC for bachelor tracks"}; o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsigma_Kaon| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectXiMassRanges{"rejectXiMassRanges", {"1.317;1.325"}, "Xi mass ranges (lower;upper) to reject -- bit is set if the candidate's mXi falls outside the range"}; }; #undef CASCADE_DEFAULT_BITS @@ -139,6 +134,11 @@ enum CascadeSels { kLambdaDcaDauMax, ///< Max. DCA between the lambda daughters at lambda decay vertex kLambdaTransRadMin, ///< Min. transverse radius of the lambda kLambdaDcaToPvMin, ///< Min. DCA of the lambda to the primary vertex + kLambdaMassRange, ///< Lambda daughter mass must fall inside the configured range(s) + + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectOmegaMass, ///< Xi: reject Omega mass hypothesis + kRejectXiMass, ///< Omega: reject Xi mass hypothesis // selection for bachelor/daughters kDauAbsEtaMax, ///< Max. |eta| of daughter tracks @@ -173,6 +173,10 @@ const std::unordered_map cascadeSelectionNames = { {kLambdaDcaDauMax, "Lambda DCA Daughter Max"}, {kLambdaTransRadMin, "Lambda Transverse Radius Min"}, {kLambdaDcaToPvMin, "Lambda DCA to PV Min"}, + {kLambdaMassRange, "Lambda Daughter Mass Range"}, + + {kRejectOmegaMass, "Reject Omega mass hypothesis"}, + {kRejectXiMass, "Reject Xi mass hypothesis"}, {kDauAbsEtaMax, "Daughter Abs Eta Max"}, {kDauTpcClsMin, "Daughter TPC Clusters Min"}, @@ -195,14 +199,8 @@ enum CascadeFilters { kEtaMax, kPhiMin, kPhiMax, - kLambdaMassMin, - kLambdaMassMax, - kXiMassMin, - kXiMassMax, - kRejectionOmegaMass, - kOmegaMassMin, - kOmegaMassMax, - kRejectionXiMass, + kMassMin, ///< signal mass window lower bound, for this cascadeType's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this cascadeType's own mass hypothesis kCascadeFiltersMax }; @@ -215,14 +213,8 @@ const std::unordered_map cascadeFilterNames = { {kEtaMax, "etaMax"}, {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, - {kLambdaMassMin, "lambdaMassMin"}, - {kLambdaMassMax, "lambdaMassMax"}, - {kXiMassMin, "xiMassMin"}, - {kXiMassMax, "xiMassMax"}, - {kRejectionOmegaMass, "rejectOmega"}, - {kOmegaMassMin, "omegaMassMin"}, - {kOmegaMassMax, "omegaMassMax"}, - {kRejectionXiMass, "rejectXi"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, }; /// \class FemtoDreamTrackCuts @@ -245,8 +237,6 @@ class CascadeSelection : public baseselection::BaseSelection const& rangeStrings, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) : mSelectionName(selectionName), - mLimitType(limits::kRange), + mLimitType(limitType), mSkipMostPermissiveBit(skipMostPermissiveBit), mIsMinimalCut(isMinimalCut), mIsOptionalCut(isOptionalCut) { + if (mLimitType != limits::kInsideRange && mLimitType != limits::kOutsideRange) { + LOG(fatal) << "Range-string selections only support kInsideRange or kOutsideRange"; + } if (rangeStrings.size() > sizeof(BitmaskType) * CHAR_BIT) { LOG(fatal) << "Too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } @@ -158,7 +170,9 @@ class SelectionContainer parseRangeString(rangeStr, lower, upper); mSelectionRanges.emplace_back(lower, upper); } - // ranges are sorted by their lenghts, i.e. from widest range to tightest range + // for kInsideRange, ranges are sorted from widest (most permissive) to tightest; for + // kOutsideRange it's the opposite -- the narrowest excluded region is the most permissive + // (fewer values get rejected) // in principle the ranges do not have to include each other, exepct this is configured as minimal cut, check is added at the end // to cover both cases, this also means we always check all ranges, when assembling the bit mask sortSelections(); @@ -167,15 +181,25 @@ class SelectionContainer mSelectionValues.push_back(mSelectionRanges[i].second - mSelectionRanges[i].first); } - // for minimal range cut, ranges must be strictly nested (each range contains all narrower ones) - // this is required for the early-exit logic in passesAsMinimalCut() to be correct + // for a minimal range cut, ranges must be strictly nested (each range contains all ranges that + // sort after it) -- this is required for the single-bit shortcut in passesAsMinimalCut() to be + // correct. For kInsideRange (sorted widest-first) that means range[i] must contain range[i+1]; + // for kOutsideRange (sorted narrowest-first) it's the reverse: range[i] must be contained in + // range[i+1]. A fully-open ("reject nothing") entry is the empty interval [max, -max], which is + // trivially contained in (or containing, for kInsideRange) anything, so this check already holds + // for it without any special-casing. if (isMinimalCut) { for (std::size_t i = 0; i + 1 < mSelectionRanges.size(); i++) { - if (mSelectionRanges[i].first > mSelectionRanges[i + 1].first || - mSelectionRanges[i].second < mSelectionRanges[i + 1].second) { + bool const nested = (mLimitType == limits::kOutsideRange) + ? (mSelectionRanges[i].first >= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second <= mSelectionRanges[i + 1].second) + : (mSelectionRanges[i].first <= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second >= mSelectionRanges[i + 1].second); + if (!nested) { LOG(fatal) << "Ranges for minimal cut " << selectionName << " are not nested. Range [" << mSelectionRanges[i].first << ";" << mSelectionRanges[i].second - << "] does not contain [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; + << "] is not " << (mLimitType == limits::kOutsideRange ? "contained in" : "containing") + << " [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; } } } @@ -318,7 +342,7 @@ class SelectionContainer } } break; - case (limits::kRange): + case (limits::kInsideRange): // ranges are sorted widest-first but a narrower range passing does not imply a wider one fails (no check on boundries), // so all ranges must be checked explicitly — no early exit for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { @@ -327,6 +351,18 @@ class SelectionContainer } } break; + case (limits::kOutsideRange): + // ranges are sorted narrowest-first (most permissive -- smallest excluded region); same + // no-early-exit reasoning as kInsideRange applies, just with the pass condition inverted. + // A fully-open range string (";") is parsed as the empty interval [max, -max] (see + // parseRangeString()), so this plain check already evaluates true for every value -- no + // special-casing needed here to express "reject nothing". + for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { + if (value < mSelectionRanges.at(i).first || value > mSelectionRanges.at(i).second) { + mBitmask.set(i); + } + } + break; case (limits::kEqual): // kEqual has no natural ordering, so all thresholds must be checked and we cannot bail early for (std::size_t i = 0; i < mSelectionValues.size(); i++) { @@ -385,6 +421,14 @@ class SelectionContainer mBitmask = std::bitset(bitmask); } + /// \brief Retrieve the raw (unshifted) internal bitmask, including the most-permissive-threshold + /// bit even if mSkipMostPermissiveBit is set. Used when two separate evaluate() calls for + /// the same observable need to be combined externally (e.g. logical AND) before the + /// result is written back via setBitmask() -- unlike getBitmask(), this never shifts, so + /// it stays the correct counterpart to setBitmask() (which also never shifts). + /// \return Bitset exactly as currently stored. + [[nodiscard]] std::bitset const& getRawBitmask() const { return mBitmask; } + /// \brief Reset the internal bitmask to zero. void reset() { mBitmask.reset(); } @@ -458,25 +502,65 @@ class SelectionContainer /// \brief Build a histogram bin label string encoding the full configuration of a single threshold. /// \param selectionIndex Index of the threshold within this container. /// \return Encoded label string. + /// \brief Describes an open (sentinel, i.e. omitted-by-the-user) side of a range, if any. + /// For kOutsideRange, a single omitted side flips which half of the "value < lower || + /// value > upper" OR-condition stays active, so the resulting satisfied/reject direction + /// is spelled out explicitly rather than named generically -- omitting the upper bound + /// there means the lower-bound inequality alone remains, i.e. "reject value < lower". + /// \param range Lower/upper bound pair for a kInsideRange/kOutsideRange threshold. + /// \return A human-readable description of the effective cut, or "" if both sides are bounded. + [[nodiscard]] std::string getOpenSideComment(std::pair const& range) const + { + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (!lowerOpen && !upperOpen) { + return ""; + } + if (lowerOpen && upperOpen) { + return "no cut"; + } + std::ostringstream oss; + if (mLimitType == limits::kInsideRange) { + oss << "accept value " << (upperOpen ? ">= " : "<= ") << (upperOpen ? range.first : range.second); + } else { + oss << "reject value " << (upperOpen ? "< " : "> ") << (upperOpen ? range.first : range.second); + } + return oss.str(); + } + [[nodiscard]] std::string getBinLabel(int selectionIndex) const { std::ostringstream oss; // Determine value string std::string valueStr; + std::string autoComment; - if (mLimitType == limits::kRange) { - // Print actual lower;upper interval + if (mLimitType == limits::kInsideRange || mLimitType == limits::kOutsideRange) { + // Print the actual lower;upper interval -- kept as real, parseable numbers (not a word like + // "no cut") since this Value field is read by cutculator.py; an open side (lower and/or + // upper bound left at the sentinel, i.e. omitted by the user) is instead flagged via the + // Comment field below. const auto& range = mSelectionRanges.at(selectionIndex); std::ostringstream rangeStream; rangeStream << range.first << RangeDelimiter << range.second; valueStr = rangeStream.str(); + autoComment = getOpenSideComment(range); } else if (mSelectionFunctions.empty()) { valueStr = std::to_string(mSelectionValues.at(selectionIndex)); } else { valueStr = mSelectionFunctions.at(selectionIndex).GetExpFormula().Data(); } + // combine rather than override -- e.g. TOF PID selections attach a "minMomTofX = ..." comment + // to every threshold via addComments(), independent of whether a given threshold's range + // also happens to be open-sided + std::string const userComment = mComments.empty() ? "" : mComments.at(selectionIndex); + std::string comment = userComment; + if (!autoComment.empty()) { + comment = userComment.empty() ? autoComment : autoComment + "; " + userComment; + } + oss << "SelectionName" << ValueDelimiter << mSelectionName << SectionDelimiter << "LimitType" << ValueDelimiter << getLimitTypeAsString() << SectionDelimiter << "MinimalCut" << ValueDelimiter << (mIsMinimalCut ? "1" : "0") << SectionDelimiter @@ -486,7 +570,7 @@ class SelectionContainer << "Offset" << ValueDelimiter << mOffset << SectionDelimiter << "Value" << ValueDelimiter << valueStr << SectionDelimiter << "BitPosition" << ValueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? NoValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << SectionDelimiter - << "Comment" << ValueDelimiter << (mComments.empty() ? NoValue : mComments.at(selectionIndex)); + << "Comment" << ValueDelimiter << (comment.empty() ? NoValue : comment); return oss.str(); } @@ -499,8 +583,16 @@ class SelectionContainer return std::string(mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()); } if (!mSelectionRanges.empty()) { + const auto& range = mSelectionRanges.at(selectionIndex); + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (lowerOpen && upperOpen) { + return std::string{"no cut"}; + } std::ostringstream oss; - oss << mSelectionRanges.at(selectionIndex).first << ";" << mSelectionRanges.at(selectionIndex).second; + oss << (lowerOpen ? "-inf" : std::to_string(range.first).c_str()) + << ";" + << (upperOpen ? "+inf" : std::to_string(range.second).c_str()); return oss.str(); } return std::to_string(mSelectionValues.at(selectionIndex)); @@ -559,13 +651,23 @@ class SelectionContainer case (limits::kAbsLowerLimit): std::sort(mSelectionValues.begin(), mSelectionValues.end(), [](T a, T b) { return a < b; }); break; - case (limits::kRange): + case (limits::kInsideRange): // sort by range width descending so the most permissive (widest) range comes first std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), [](std::pair const& a, std::pair const& b) { return (a.second - a.first) > (b.second - b.first); }); break; + case (limits::kOutsideRange): + // opposite of kInsideRange: the most permissive (most likely to be satisfied) exclusion is + // the narrowest one, since it rejects the fewest values. A fully-open ("reject nothing") + // range parses to the empty interval [max, -max] (see parseRangeString()), whose "width" is + // the most negative possible value, so it naturally sorts first without any special-casing. + std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), + [](std::pair const& a, std::pair const& b) { + return (a.second - a.first) < (b.second - b.first); + }); + break; default: break; } @@ -591,7 +693,12 @@ class SelectionContainer /// \brief Parse a range string of the form "lower;upper" into a lower and upper bound. /// Either bound may be omitted (e.g. ";1.0" or "0.5;"), in which case - /// -/+ numeric_limits::max() is used respectively. + /// -/+ numeric_limits::max() is used respectively. For kOutsideRange, a fully-open string + /// (";", both bounds omitted) is the one exception: it is parsed as the empty interval + /// [max, -max] instead of [-max, max] -- every value then trivially fails the normal + /// "value >= lower && value <= upper" check, i.e. every value counts as outside, which is + /// what "reject nothing" needs. [-max, max] would instead mean every value is inside, + /// i.e. nothing is ever outside, which would reject everything -- the opposite intent. /// \param rangeStr Input string to parse. /// \param lower Output lower bound. /// \param upper Output upper bound. @@ -604,6 +711,18 @@ class SelectionContainer std::string lowerStr = rangeStr.substr(0, pos); std::string upperStr = rangeStr.substr(pos + 1); + if (lowerStr.empty() && upperStr.empty() && mLimitType == limits::kOutsideRange) { + // kOutsideRange's "value < lower || value > upper" is the negation of kInsideRange's + // "value >= lower && value <= upper" -- substituting the same [-max, +max] that makes the + // latter trivially true for every value would make the former trivially FALSE for every + // value instead (nothing is < -max or > +max), i.e. "reject everything" rather than the + // intended "reject nothing". The empty interval [max, -max] is the encoding that keeps the + // disjunction trivially true for every value instead, giving the correct "exempt" meaning. + lower = std::numeric_limits::max(); + upper = -std::numeric_limits::max(); + return; + } + lower = lowerStr.empty() ? -std::numeric_limits::max() : static_cast(std::stod(lowerStr)); upper = upperStr.empty() ? std::numeric_limits::max() : static_cast(std::stod(upperStr)); @@ -615,7 +734,7 @@ class SelectionContainer std::string mSelectionName; std::vector mSelectionValues = {}; ///< Threshold values, sorted from most permissive to most restrictive std::vector mSelectionFunctions; ///< TF1 threshold functions (empty for static selections) - std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kRange selections, one pair per threshold + std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kInsideRange/kOutsideRange selections, one pair per threshold limits::LimitType mLimitType = limits::kLimitTypeLast; ///< Comparison type applied during evaluation bool mSkipMostPermissiveBit = false; ///< If true, the most permissive threshold does not occupy a bit in the global bitmask bool mIsMinimalCut = false; ///< If true, this selection is mandatory; failing it rejects the candidate diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 491a8dee403..c753fd1edc6 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -51,14 +51,8 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massMinLambda{"massMinLambda", 1.f, "Minimum mass for Lambda hypothesis"}; o2::framework::Configurable massMaxLambda{"massMaxLambda", 1.2f, "Maximum mass for Lambda hypothesis"}; - o2::framework::Configurable rejectHypothesisK0short{"rejectHypothesisK0short", true, "Rejection of K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.475f, "Minimum mass to reject K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.515f, "Maximum mass to reject K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable massMinK0short{"massMinK0short", 0.45f, "Minimum mass for K0Short hypothesis"}; - o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.53f, "Maximum mass for K0Short hypothesis"}; - o2::framework::Configurable rejectHypothesisLambda{"rejectHypothesisLambda", true, "Rejection of Lambda hypothesis for K0short candidates"}; - o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to reject Lambda hypothesis for K0short candidates"}; - o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to reject Lambda hypothesis for K0short candidates"}; + o2::framework::Configurable massMinK0short{"massMinK0short", 0.485f, "Minimum mass for K0Short hypothesis"}; + o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.51f, "Maximum mass for K0Short hypothesis"}; }; // selections bits for all v0s @@ -88,6 +82,7 @@ struct ConfLambdaBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> posDauTofProton{"posDauTofProton", {}, "Maximum |nsigma_Proton| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; o2::framework::Configurable> negDauTofProton{"negDauTofProton", {}, "Maximum |nsigma_Proton| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectK0shortMassRanges{"rejectK0shortMassRanges", {"0.475;0.515"}, "K0short mass ranges (lower;upper) to reject -- bit is set if the candidate's mK0Short falls outside the range"}; }; // derived selection bits for K0Short @@ -98,6 +93,7 @@ struct ConfK0shortBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectLambdaMassRanges{"rejectLambdaMassRanges", {"1.11;1.12"}, "Lambda/AntiLambda mass ranges (lower;upper) to reject -- bit is set if the candidate's mLambda (resp. mAntiLambda) falls outside the range; same range for both since they share the same PDG mass"}; }; #undef V0_DEFAULT_BITS @@ -128,7 +124,7 @@ struct ConfLambdaSelection : o2::framework::ConfigurableGroup { template struct ConfK0shortSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; - V0_DEFAULT_SELECTIONS(0.47, 0.51, 310) + V0_DEFAULT_SELECTIONS(0.485, 0.51, 310) o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatibility with Lambda selection"}; }; @@ -174,6 +170,10 @@ enum V0Sels { kNegDaughTofPion, ///< TOF Pion PID for negative daughter kNegDaughTofProton, ///< TOF Proton PID for negative daughter + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectK0shortMass, ///< Lambda/AntiLambda: reject K0short mass hypothesis + kRejectLambdaMass, ///< K0short: reject Lambda/AntiLambda mass hypothesis (single bit, see applySelections()) + kV0SelsMax }; @@ -199,7 +199,9 @@ const std::unordered_map v0SelectionNames = { {kPosDaughTofPion, "TOF Pion PID for positive daughter"}, {kPosDaughTofProton, "TOF Proton PID for positive daughter"}, {kNegDaughTofPion, "TOF Pion PID for negative daughter"}, - {kNegDaughTofProton, "TOF Proton PID for negative daughter"}}; + {kNegDaughTofProton, "TOF Proton PID for negative daughter"}, + {kRejectK0shortMass, "Reject K0short mass hypothesis"}, + {kRejectLambdaMass, "Reject Lambda/AntiLambda mass hypothesis"}}; // enum for all track filters (loose pre-selection, applied before quality/PID cuts) enum V0Filters { @@ -209,12 +211,8 @@ enum V0Filters { kEtaMax, kPhiMin, kPhiMax, - kRejectionK0shortMass, - kK0shortMassMin, - kK0shortMassMax, - kRejectionLambdaMass, - kLambdaMassMin, - kLambdaMassMax, + kMassMin, ///< signal mass window lower bound, for this v0Type's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this v0Type's own mass hypothesis kV0FiltersMax }; @@ -228,12 +226,8 @@ const std::unordered_map v0FilterNames = { {kEtaMax, "etaMax"}, {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, - {kRejectionK0shortMass, "rejectK0short"}, - {kK0shortMassMin, "k0shortMassMin"}, - {kK0shortMassMax, "k0shortMassMax"}, - {kRejectionLambdaMass, "rejectLambda"}, - {kLambdaMassMin, "lambdaMassMin"}, - {kLambdaMassMax, "lambdaMassMax"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, }; /// \brief Cut class to contain and execute all cuts applied to v0s @@ -262,9 +256,8 @@ class V0Selection : public baseselection::BaseSelectionaddSelection(kRejectK0shortMass, v0SelectionNames.at(kRejectK0shortMass), config.rejectK0shortMassRanges.value, true, true, false, limits::kOutsideRange); if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { this->addSelection(kPosDaughTpcProton, v0SelectionNames.at(kPosDaughTpcProton), config.posDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); @@ -287,9 +280,8 @@ class V0Selection : public baseselection::BaseSelectionaddSelection(kRejectLambdaMass, v0SelectionNames.at(kRejectLambdaMass), config.rejectLambdaMassRanges.value, true, true, false, limits::kOutsideRange); this->addSelection(kPosDaughTpcPion, v0SelectionNames.at(kPosDaughTpcPion), config.posDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); this->addSelection(kNegDaughTpcPion, v0SelectionNames.at(kNegDaughTpcPion), config.negDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); @@ -309,6 +301,10 @@ class V0Selection : public baseselection::BaseSelectionaddSelection(kDauTpcClsMin, v0SelectionNames.at(kDauTpcClsMin), config.dauTpcClustersMin.value, limits::kLowerLimit, true, true, false); this->setupSelectionHistogram(registry); + // signal mass window bins carry this v0Type's own mass hypothesis only -- otherwise e.g. the + // K0short filter histogram would also show Lambda's (irrelevant, default-valued) mass window + float const massLowerLimit = modes::isEqual(v0Type, modes::V0::kK0short) ? mMassK0shortLowerLimit : mMassLambdaLowerLimit; + float const massUpperLimit = modes::isEqual(v0Type, modes::V0::kK0short) ? mMassK0shortUpperLimit : mMassLambdaUpperLimit; this->template setupFilterHistogram( registry, { @@ -318,12 +314,8 @@ class V0Selection : public baseselection::BaseSelectionevaluateObservable(kRejectK0shortMass, v0candidate.mK0Short()); + } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { + // K0short's V0Data carries both mLambda() and mAntiLambda(), but we only want a single + // rejection bit -- evaluate both against the configured range(s) and keep only the AND + // (bit set if BOTH independently fall outside), without assuming anything about where + // the configured range(s) are centered. See the two-value evaluateObservable() overload + // in baseSelection.h. + this->evaluateObservable(kRejectLambdaMass, v0candidate.mLambda(), v0candidate.mAntiLambda()); + } + this->assembleBitmask(); } @@ -430,49 +436,27 @@ class V0Selection : public baseselection::BaseSelectiontemplate fillFilter(kPhiMax, p); pass &= p; - // mass hypothesis: signal window is a gating AND-cut (two bounds, both must pass); - // the competing hypothesis is a rejection OR-cut (pass if outside the window, or if - // rejection is disabled) — its own bin records whether the candidate cleared the - // rejection, while the min/max bins are diagnostic bounds on where the mass sits. + // signal mass window: gating AND-cut (both bounds must pass). The competing hypothesis is no + // longer rejected here -- it's a stored bit instead if constexpr (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { float const mass = modes::isEqual(v0Type, modes::V0::kLambda) ? v0.mLambda() : v0.mAntiLambda(); p = mass > mMassLambdaLowerLimit; - this->template fillFilter(kLambdaMassMin, p); + this->template fillFilter(kMassMin, p); pass &= p; p = mass < mMassLambdaUpperLimit; - this->template fillFilter(kLambdaMassMax, p); - pass &= p; - - bool const belowK0s = v0.mK0Short() < mMassK0shortLowerLimit; - this->template fillFilter(kK0shortMassMin, belowK0s); - bool const aboveK0s = v0.mK0Short() > mMassK0shortUpperLimit; - this->template fillFilter(kK0shortMassMax, aboveK0s); - - p = !mRejectK0shortHypothesis || belowK0s || aboveK0s; - this->template fillFilter(kRejectionK0shortMass, p); + this->template fillFilter(kMassMax, p); pass &= p; } if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { p = v0.mK0Short() > mMassK0shortLowerLimit; - this->template fillFilter(kK0shortMassMin, p); + this->template fillFilter(kMassMin, p); pass &= p; p = v0.mK0Short() < mMassK0shortUpperLimit; - this->template fillFilter(kK0shortMassMax, p); - pass &= p; - - bool const belowLambda = v0.mLambda() < mMassLambdaLowerLimit; - bool const aboveLambda = v0.mLambda() > mMassLambdaUpperLimit; - bool const belowAntiLambda = v0.mAntiLambda() < mMassLambdaLowerLimit; - bool const aboveAntiLambda = v0.mAntiLambda() > mMassLambdaUpperLimit; - this->template fillFilter(kLambdaMassMin, belowLambda && belowAntiLambda); - this->template fillFilter(kLambdaMassMax, aboveLambda && aboveAntiLambda); - - p = !mRejectLambdaHypothesis || ((belowLambda || aboveLambda) && (belowAntiLambda || aboveAntiLambda)); - this->template fillFilter(kRejectionLambdaMass, p); + this->template fillFilter(kMassMax, p); pass &= p; } @@ -482,13 +466,11 @@ class V0Selection : public baseselection::BaseSelection Date: Thu, 22 Oct 2026 23:52:59 +0200 Subject: [PATCH 401/648] [ALICE3] Enhance tracking performance configuration (#18421) --- ALICE3/Tasks/alice3TrackingPerformance.cxx | 66 +++++++++++++++------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/ALICE3/Tasks/alice3TrackingPerformance.cxx b/ALICE3/Tasks/alice3TrackingPerformance.cxx index f0af7813136..fa69139b33a 100644 --- a/ALICE3/Tasks/alice3TrackingPerformance.cxx +++ b/ALICE3/Tasks/alice3TrackingPerformance.cxx @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -46,9 +47,20 @@ using namespace o2::framework; struct Alice3TrackingPerformance { Configurable> pdgCodes{"pdgCodes", {211}, "List of signed PDG codes to consider for performance evaluation"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; - ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + struct : ConfigurableGroup { + std::string prefix = "Axes"; // JSON group name + ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; + ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + ConfigurableAxis dcaAxis{"dcaAxis", {1000, -0.1f, 0.1f}, "DCA (cm)"}; + } settingsAxes; + + struct : ConfigurableGroup { + std::string prefix = "Selections"; // JSON group name + Configurable vtxSelX{"vtxSelX", -1.f, "Vertex selection in X (cm), if negative no selection is applied"}; + Configurable vtxSelY{"vtxSelY", -1.f, "Vertex selection in Y (cm), if negative no selection is applied"}; + Configurable vtxSelZ{"vtxSelZ", -1.f, "Vertex selection in Z (cm), if negative no selection is applied"}; + } settingsSelections; // Histograms std::shared_ptr particlePdgCodes; @@ -75,8 +87,11 @@ struct Alice3TrackingPerformance { pdgCodes.value.push_back(0); } const AxisSpec axisPtDelta{100, -1, 1, "(#it{p}_{T}^{reco} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; - const AxisSpec axisDcaXy{100, -1, 1, "DCA_{xy} (cm)"}; - const AxisSpec axisDcaZ{100, -1, 1, "DCA_{z} (cm)"}; + const AxisSpec axisPt{settingsAxes.ptAxis, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisInvPt{settingsAxes.invPtDeltaAxis, "1/#it{p}_{T} (GeV/#it{c})^{-1}"}; + const AxisSpec axisEta{settingsAxes.etaAxis, "#eta"}; + const AxisSpec axisDcaXy{settingsAxes.dcaAxis, "DCA_{xy} (cm)"}; + const AxisSpec axisDcaZ{settingsAxes.dcaAxis, "DCA_{z} (cm)"}; const AxisSpec axisCovariancePtPt{100, 0, 10, "cov(#it{p}_{T}, #it{p}_{T}) ((GeV/#it{c})^{2})"}; const AxisSpec axisCovarianceDcaXyDcaXy{100, 0, 1, "cov(DCA_{xy}, DCA_{xy}) (cm^{2})"}; const AxisSpec axisCovarianceDcaZDcaZ{100, 0, 1, "cov(DCA_{z}, DCA_{z}) (cm^{2})"}; @@ -90,25 +105,26 @@ struct Alice3TrackingPerformance { const std::string tag = "_" + prefix; prefix += "/"; auto histoName = [&](const std::string& name) { return Form("%s%s%s", prefix.c_str(), name.c_str(), tag.c_str()); }; - particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {ptAxis}); + particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {axisPt}); LOG(info) << "Added histogram: " << histoName("particlePtDistribution"); - particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {etaAxis}); - - ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {ptAxis}); - ptResolutionVsPt[pdg] = histos.add(histoName("ptResolutionVsPt"), "", kTH2D, {ptAxis, axisPtDelta}); - ptResolutionVsEta[pdg] = histos.add(histoName("ptResolutionVsEta"), "", kTProfile2D, {ptAxis, etaAxis}); - invPtResolutionVsPt[pdg] = histos.add(histoName("invPtResolutionVsPt"), "", kTH2D, {ptAxis, invPtDeltaAxis}); - invPtResolutionVsEta[pdg] = histos.add(histoName("invPtResolutionVsEta"), "", kTProfile2D, {ptAxis, etaAxis}); - dcaXyResolutionVsPt[pdg] = histos.add(histoName("dcaXyResolutionVsPt"), "", kTH2D, {ptAxis, axisDcaXy}); - dcaZResolutionVsPt[pdg] = histos.add(histoName("dcaZResolutionVsPt"), "", kTH2D, {ptAxis, axisDcaZ}); - covariancePtPtVsPt[pdg] = histos.add(histoName("covariancePtPtVsPt"), "", kTH2D, {ptAxis, axisCovariancePtPt}); - covarianceDcaXyDcaXyVsPt[pdg] = histos.add(histoName("covarianceDcaXyDcaXyVsPt"), "", kTH2D, {ptAxis, axisCovarianceDcaXyDcaXy}); - covarianceDcaZDcaZVsPt[pdg] = histos.add(histoName("covarianceDcaZDcaZVsPt"), "", kTH2D, {ptAxis, axisCovarianceDcaZDcaZ}); + particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {axisEta}); + + ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {axisPt}); + ptResolutionVsPt[pdg] = histos.add(histoName("ptResolutionVsPt"), "", kTH2D, {axisPt, axisPtDelta}); + ptResolutionVsEta[pdg] = histos.add(histoName("ptResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + invPtResolutionVsPt[pdg] = histos.add(histoName("invPtResolutionVsPt"), "", kTH2D, {axisPt, axisInvPt}); + invPtResolutionVsEta[pdg] = histos.add(histoName("invPtResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + dcaXyResolutionVsPt[pdg] = histos.add(histoName("dcaXyResolutionVsPt"), "", kTH2D, {axisPt, axisDcaXy}); + dcaZResolutionVsPt[pdg] = histos.add(histoName("dcaZResolutionVsPt"), "", kTH2D, {axisPt, axisDcaZ}); + covariancePtPtVsPt[pdg] = histos.add(histoName("covariancePtPtVsPt"), "", kTH2D, {axisPt, axisCovariancePtPt}); + covarianceDcaXyDcaXyVsPt[pdg] = histos.add(histoName("covarianceDcaXyDcaXyVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaXyDcaXy}); + covarianceDcaZDcaZVsPt[pdg] = histos.add(histoName("covarianceDcaZDcaZVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaZDcaZ}); } } void process(soa::Join const& tracks, - aod::McParticles const& mcParticles) + aod::McParticles const& mcParticles, + aod::McCollisions const&) { auto isParticleSelected = [&](const o2::aod::McParticle& p) { if (!p.isPhysicalPrimary()) { @@ -119,6 +135,18 @@ struct Alice3TrackingPerformance { }; for (const auto& mcParticle : mcParticles) { + // Selecting collisions + const auto& collision = mcParticle.mcCollision(); + if (settingsSelections.vtxSelX >= 0.f && std::abs(collision.posX()) > settingsSelections.vtxSelX) { + continue; + } + if (settingsSelections.vtxSelY >= 0.f && std::abs(collision.posY()) > settingsSelections.vtxSelY) { + continue; + } + if (settingsSelections.vtxSelZ >= 0.f && std::abs(collision.posZ()) > settingsSelections.vtxSelZ) { + continue; + } + particlePdgCodes->Fill(Form("%d", mcParticle.pdgCode()), 1); if (!isParticleSelected(mcParticle)) { continue; From a2758f44dadd0fb395e9c4c5f4e74f3e7fb159f5 Mon Sep 17 00:00:00 2001 From: Paola Vargas Torres <88360333+PaolaVT@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:55:43 -0600 Subject: [PATCH 402/648] [PWGLF] Add flag to run positive and negative charges separately or combined (#18427) Co-authored-by: PaolaVT Co-authored-by: Pao Vargas --- PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx | 527 +++++++++++++++---------- 1 file changed, 320 insertions(+), 207 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx index b6a3e3afd0c..4eda03292a3 100644 --- a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx @@ -70,7 +70,6 @@ struct DedxPidAnalysis { // dE/dx for all charged particles HistogramRegistry registryDeDx{"registryDeDx", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryDeDxBothCharges{"registryDeDxBothCharges", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Constant values static constexpr int EtaIntervals = 8; static constexpr int ParticlesType = 4; @@ -260,6 +259,7 @@ struct DedxPidAnalysis { Configurable invMassCutLambda{"invMassCutLambda", 0.015f, "invariant Mass Cut for Lambda"}; Configurable invMassCutGamma{"invMassCutGamma", 0.015f, "invariant Mass Cut for Gamma"}; Configurable calibrationMode{"calibrationMode", false, "calibration mode"}; + Configurable separateCharges{"separateCharges", true, "true: fill Pos and Neg separately; false: fill both charges in one histogram"}; Configurable phiVarCut{"phiVarCut", true, "phi var cut"}; Configurable nClTPCFoundCut{"nClTPCFoundCut", false, "number of found clusters in TPC cut"}; Configurable nClTPCPIDCut{"nClTPCPIDCut", true, "number of PID clusters in TPC cut"}; @@ -283,7 +283,6 @@ struct DedxPidAnalysis { static constexpr std::array CentClasses = {0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}; Configurable> calibrationFactorNeg{"calibrationFactorNeg", {50.4011, 50.4764, 50.186, 49.2955, 48.8222, 49.4273, 49.9292, 50.0556}, "negative calibration factors"}; Configurable> calibrationFactorPos{"calibrationFactorPos", {50.5157, 50.6359, 50.3198, 49.3345, 48.9197, 49.4931, 50.0188, 50.1406}, "positive calibration factors"}; - Configurable> calibrationFactor{"calibrationFactor", {50.5157, 50.6359, 50.3198, 49.3345, 48.9197, 49.4931, 50.0188, 50.1406}, "calibration factors for both charges"}; ConfigurableAxis binP{"binP", {VARIABLE_WIDTH, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0}, ""}; ConfigurableAxis centBins{"centBins", {100, 0, 100}, "Binning for centralidad plots"}; ConfigurableAxis dedxBins{"dedxBins", {100, 0, 100}, "Binning for dedx plots"}; @@ -392,6 +391,12 @@ struct DedxPidAnalysis { LOGF(info, "Running data applying calibration values"); } + if (separateCharges) { + LOGF(info, "Charge mode: Pos and Neg histograms filled separately"); + } else { + LOGF(info, "Charge mode: Pos and Neg combined in a single histogram"); + } + if (phiVarCut) { LOGF(info, "Applying phi prime cut"); } else { @@ -489,131 +494,161 @@ struct DedxPidAnalysis { "all", "Pi_v0", "Pr_v0", "El_v0"}; if (calibrationMode) { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - registryDeDxBothCharges.add( - "hdEdx_vs_eta_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDxBothCharges.add( - "hdEdx_vs_eta_vs_p_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDxBothCharges.add( - "hdEdx_vs_eta_vs_p_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - } else { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - - registryDeDx.add( - "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - - registryDeDxBothCharges.add( - "hdEdx_vs_eta_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - registryDeDxBothCharges.add( - "hdEdx_vs_eta_vs_p_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + if (separateCharges) { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDxBothCharges.add( - "hdEdx_vs_eta_vs_p_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + if (separateCharges) { + + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + registryDeDx.add( + "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + registryDeDx.add( + "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentumPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", part.c_str()), + "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), + "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - registryDeDxBothCharges.add( - "hdEdx_vs_eta_vs_p_calibrated_TOF2", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } - // pt vs p - registryDeDx.add( - "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - registryDeDx.add( - "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - - registryDeDxBothCharges.add( - "heta_vs_pt_vs_p_all", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - - // De/Dx for ch and v0 particles - for (int i = 0; i < ParticlesType; ++i) { - const auto& part = v0Names[i]; - hDedxvsMomentumPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", part.c_str()), - "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), - "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxvsMomentum[i] = registryDeDxBothCharges.add(Form("dEdx_vs_Momentum_%s", part.c_str()), - "dE/dx vs Momentum", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - } + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + + registryDeDx.add( + "heta_vs_pt_vs_p_all", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentum[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", part.c_str()), + "dE/dx vs Momentum", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - for (int i = 0; i < CentralityClasses; ++i) { - const auto& cent = centNames[i]; - hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxVsMomentumVsCent[i] = registryDeDxBothCharges.add(Form("dEdx_vs_Momentum_%s", cent.c_str()), "dE/dx vs Momentum", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - } + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCent[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", cent.c_str()), "dE/dx vs Momentum", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - for (int i = 0; i < CentralityClasses + 1; ++i) { - const auto& cent = centNames[i]; - hDedxVspTVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); - hDedxVspTVsCentCh[i] = registryDeDxBothCharges.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); - hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); - hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); - hMomentumVsEta[i] = registryDeDxBothCharges.add(Form("p_vs_eta_%s", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); - hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); - hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); - hpTVsEta[i] = registryDeDxBothCharges.add(Form("pT_vs_eta_%s", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCentCh[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEta[i] = registryDeDx.add(Form("p_vs_eta_%s", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEta[i] = registryDeDx.add(Form("pT_vs_eta_%s", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } } - // Invariant Mass registryDeDx.add("hMassK0s", "Invariant mass K0s;m_{#pi#pi} (GeV/c^{2});Counts", HistType::kTH1F, {{200, 0.4, 0.6}}); @@ -807,17 +842,20 @@ struct DedxPidAnalysis { {{pAxisTrack}, {100, 0, 160, "N_{cl, PID}"}}); // beta plot - registryDeDx.add( - "hbeta_vs_p_Neg", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + if (separateCharges) { + registryDeDx.add( + "hbeta_vs_p_Neg", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); - registryDeDx.add( - "hbeta_vs_p_Pos", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + registryDeDx.add( + "hbeta_vs_p_Pos", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } else { - registryDeDxBothCharges.add( - "hbeta_vs_p", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + registryDeDx.add( + "hbeta_vs_p", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } // Event Counter registryDeDx.add("histRecVtxZData", "collision z position", HistType::kTH1F, {{100, -20.0, +20.0, "z_{vtx} (cm)"}}); @@ -1638,50 +1676,74 @@ struct DedxPidAnalysis { // MIP calibration for pions if (getMomentum(trk) >= pionMin && getMomentum(trk) <= pionMax) { if (calibrationMode) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_Pi"), trk.eta(), trk.tpcSignal()); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + registryDeDx.fill(HIST("hdEdx_vs_eta_Pi"), trk.eta(), trk.tpcSignal()); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i)); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } } } } } // Beta from TOF - registryDeDxBothCharges.fill(HIST("hbeta_vs_p"), p, trk.beta()); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hbeta_vs_p_Neg"), p, trk.beta()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hbeta_vs_p_Neg"), p, trk.beta()); + } else { + registryDeDx.fill(HIST("hbeta_vs_p_Pos"), p, trk.beta()); + } } else { - registryDeDx.fill(HIST("hbeta_vs_p_Pos"), p, trk.beta()); + registryDeDx.fill(HIST("hbeta_vs_p"), p, trk.beta()); } + // Electrons from TOF if (std::abs(trk.beta() - 1) < elTofCut) { // beta cut if (calibrationMode) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_El"), trk.eta(), trk.tpcSignal(), p); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), p); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), p); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_El"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1690,20 +1752,30 @@ struct DedxPidAnalysis { // pions from TOF if (trk.beta() > pionTofCut && trk.beta() < pionTofCut + 0.05) { // beta cut if (calibrationMode) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_TOF"), trk.eta(), trk.tpcSignal(), p); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), p); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), p); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_TOF"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1717,11 +1789,18 @@ struct DedxPidAnalysis { if (std::abs(sigmaPi) < nsigmaMaxTOF) { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - registryDeDxBothCharges.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactor->at(i), p); - if (trk.sign() < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1744,35 +1823,45 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); - registryDeDxBothCharges.fill(HIST("heta_vs_pt_vs_p_all"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); - hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); - hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactor->at(i), trk.eta()); - hMomentumVsEta[centIndex]->Fill(trk.eta(), p); - hMomentumVsEta[10]->Fill(trk.eta(), p); - hpTVsEta[centIndex]->Fill(trk.eta(), pT); - hpTVsEta[10]->Fill(trk.eta(), pT); - if (trk.sign() > 0) { - hDedxvsMomentumPos[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentPos[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), p); - hMomentumVsEtaPos[10]->Fill(trk.eta(), p); - hpTVsEtaPos[centIndex]->Fill(trk.eta(), pT); - hpTVsEtaPos[10]->Fill(trk.eta(), pT); + if (separateCharges) { + if (trk.sign() > 0) { + hDedxvsMomentumPos[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentPos[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaPos[10]->Fill(trk.eta(), p); + hpTVsEtaPos[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaPos[10]->Fill(trk.eta(), pT); + } else { + hDedxvsMomentumNeg[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentNeg[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaNeg[10]->Fill(trk.eta(), p); + hpTVsEtaNeg[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaNeg[10]->Fill(trk.eta(), pT); + } } else { - hDedxvsMomentumNeg[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentNeg[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), p); - hMomentumVsEtaNeg[10]->Fill(trk.eta(), p); - hpTVsEtaNeg[centIndex]->Fill(trk.eta(), pT); - hpTVsEtaNeg[10]->Fill(trk.eta(), pT); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all"), trk.eta(), trk.pt(), trk.p()); + hMomentumVsEta[centIndex]->Fill(trk.eta(), p); + hMomentumVsEta[10]->Fill(trk.eta(), p); + hpTVsEta[centIndex]->Fill(trk.eta(), pT); + hpTVsEta[10]->Fill(trk.eta(), pT); + if (trk.sign() > 0) { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + } else { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + } } } } @@ -1808,7 +1897,7 @@ struct DedxPidAnalysis { if (!passedPhiCutSecondaries(negTrack, magField)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::PhiVarCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::PhiVarCutSec); } fillHist = false; @@ -1848,7 +1937,7 @@ struct DedxPidAnalysis { if (!passedNClTPCFoundCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCFoundCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCFoundCutSec); } if (nClTPCPIDCut) { @@ -1858,7 +1947,7 @@ struct DedxPidAnalysis { if (!passedNClTPCPIDCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCPIDCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCPIDCutSec); } float signedPpos = posTrack.sign() * getMomentum(posTrack); @@ -1879,12 +1968,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosK0s"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); - hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); - hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1899,12 +1994,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); - hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); - hDedxvsMomentum[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1919,12 +2020,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosAntiLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); - hDedxvsMomentum[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); - hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1939,12 +2046,18 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hArmenterosGamma"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); - hDedxvsMomentum[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactor->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); - hDedxvsMomentum[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactor->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } From 42ab57642393eea79547b529b02b305cc18076cd Mon Sep 17 00:00:00 2001 From: Pei-Ying Kuan <128475937+pykuan@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:58:48 +0200 Subject: [PATCH 403/648] [PWGCF] add occupancy cut for MC data (#18428) Co-authored-by: Pei-Ying Kuan --- .../Tasks/multiparticleCumulants.cxx | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx index ff637d76aa7..4ea562dd5b0 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -230,6 +230,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "centrality cut switch"}; Configurable cfNumContribCutSwitch{"cfNumContribCutSwitch", true, "NContribution cut switch"}; Configurable cfNumTrackCutSwitch{"cfNumTrackCutSwitch", true, "NTrack cut switch"}; + Configurable cfOccupancyCutSwitch{"cfOccupancyCutSwitch", true, "occupancy cut switch"}; Configurable cfCentCorrCutSwitch{"cfCentCorrCutSwitch", true, "centrality correlation outlier cut switch"}; Configurable cfMultCorrCutSwitch{"cfMultCorrCutSwitch", true, "multiplicity correlation outlier cut switch"}; Configurable cfNumContribMultCorrCutSwitch{"cfNumContribMultCorrCutSwitch", true, "NContribution vs. multiplicity correlation outlier cut switch"}; @@ -248,6 +249,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfCentCut{"cfCentCut", {10., 20.}, "centrality range: {min, max}[%]"}; Configurable> cfNumContribCut{"cfNumContribCut", {0, 3000.}, "NContribution range: {min, max}"}; Configurable> cfNumTrackCut{"cfNumTrackCut", {0, 3000.}, "NTrack range: {min, max}"}; + Configurable> cfOccupancyCut{"cfOccupancyCut", {0, 6000.}, "Occupancy range: {min, max}"}; Configurable> cfCentABCorrCut{"cfCentABCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentACCorrCut{"cfCentACCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; Configurable> cfCentBCCorrCut{"cfCentBCCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; @@ -302,6 +304,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bool fCentCutSwitch = true; bool fNumContribCutSwitch = true; bool fNumTrackCutSwitch = true; + bool fOccupancyCutSwitch = true; bool fCentCorrCutSwitch = true; bool fMultCorrCutSwitch = true; bool fNumContribMultCorrCutSwitch = true; @@ -318,6 +321,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fCentCut = {10., 20.}; std::vector fNumContribCut = {0, 3000.}; std::vector fNumTrackCut = {0, 350.}; + std::vector fOccupancyCut = {0, 6000.}; std::vector>> fCentAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector>> fMultAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; std::vector fNumContribMultCorrCut = {0.024, -600., 0.035, 100.}; @@ -455,6 +459,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam bool bSel8Cut = true; bool bCentCut = true; bool bNumContribCut = true; + bool bNumOccupancyCut = true; bool bCentCorrCut = true; bool bMultCorrCut = true; bool bNumContribMultCorrCut = true; @@ -481,6 +486,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // *) NumContribCut bNumContribCut = ebye.fNumContrib < tc.fNumContribCut[1] && ebye.fNumContrib > tc.fNumContribCut[0]; + // *) NumOccupancyCut + bNumOccupancyCut = collision.trackOccupancyInTimeRange() < tc.fOccupancyCut[1] && + collision.trackOccupancyInTimeRange() > tc.fOccupancyCut[0]; // *) CentCorrCut float iCent = 0.; float jCent = 0.; @@ -538,6 +546,9 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam if (tc.fNumContribCutSwitch) { pass &= bNumContribCut; } + if (tc.fOccupancyCutSwitch) { + pass &= bNumOccupancyCut; + } if (tc.fCentCorrCutSwitch) { pass &= bCentCorrCut; } @@ -1405,11 +1416,13 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double diffPtMax = (tc.fPtBins[2] - tc.fPtBins[1]) / tc.fPtBins[0]; if (diffPt < diffPtMax) { wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); - } else { - if (tc.fPrintSwitch) { - LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); - } } + // else { + // if (tc.fPrintSwitch) { + // LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); + // } + // } + // Fill MC particle histograms after cut: pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); pc.fParticleHistograms[ePhi][eSim][eAfter]->Fill(mcparticle.phi()); @@ -1423,6 +1436,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam ev.fEventHistograms[eNumTrack][eRec][eBefore]->Fill(nTracksBefore); ev.fEventHistograms[eNumTrack][eRec][eAfter]->Fill(nTracksAfter); bool bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + // LOGF(info, "nTracksAfter = %d", nTracksAfter); // 2-p correlations: for (int i = 0; i < NumTwoPCorrBins; i++) { @@ -1560,7 +1574,6 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam double wFourRecursionAfter = mccRecursion(4, harmonicsFourDen, eAfter).Re(); ebye.fFourParticleCorrelationEbye[eAfter][i] = fourRecursionAfter.Re(); - // LOGF(info, "nTracksAfter = %d", nTracksAfter); if (nTracksAfter > k - 1 && wFourRecursionAfter > 0. && bNumTrackCut) { // Fill vn^2vm^2 profile and histogram: mc.fFourParticleCorrelationProfiles[eAfter]->Fill(mc.fFourParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); @@ -1971,6 +1984,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fCentCutSwitch = cfCentCutSwitch; tc.fNumContribCutSwitch = cfNumContribCutSwitch; tc.fNumTrackCutSwitch = cfNumTrackCutSwitch; + tc.fOccupancyCutSwitch = cfOccupancyCutSwitch; tc.fCentCorrCutSwitch = cfCentCorrCutSwitch; tc.fMultCorrCutSwitch = cfMultCorrCutSwitch; tc.fNumContribMultCorrCutSwitch = cfNumContribMultCorrCutSwitch; @@ -1987,6 +2001,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fCentCut = cfCentCut; tc.fNumContribCut = cfNumContribCut; tc.fNumTrackCut = cfNumTrackCut; + tc.fOccupancyCut = cfOccupancyCut; tc.fCentAllCorrCut[0][1] = cfCentABCorrCut; tc.fCentAllCorrCut[0][2] = cfCentACCorrCut; tc.fCentAllCorrCut[1][2] = cfCentBCCorrCut; From be2cc33bd23fcb9e951ad5e01d63045319a54392 Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Fri, 23 Oct 2026 08:12:46 +0200 Subject: [PATCH 404/648] [PWGCF] Add region-aware initialisation to FlowPtContainer, mimicking GFW flow initialisation (#18426) --- PWGCF/GenericFramework/Core/CMakeLists.txt | 0 .../GenericFramework/Core/FlowPtContainer.cxx | 969 +++++++++++++++++- PWGCF/GenericFramework/Core/FlowPtContainer.h | 60 +- .../Core/GenericFrameworkLinkDef.h | 4 + 4 files changed, 1008 insertions(+), 25 deletions(-) mode change 100755 => 100644 PWGCF/GenericFramework/Core/CMakeLists.txt diff --git a/PWGCF/GenericFramework/Core/CMakeLists.txt b/PWGCF/GenericFramework/Core/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx index d2d5a1740f6..d76b8a6bb39 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx @@ -16,6 +16,7 @@ #include "FlowPtContainer.h" #include "PWGCF/GenericFramework/Core/BootstrapProfile.h" +#include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include @@ -31,16 +32,154 @@ #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include using namespace o2::analysis::genericframework; using namespace o2::analysis::genericframework::eventweight; +using o2::analysis::genericframework::flowptcontainer::PtCorrConfig; +using o2::analysis::genericframework::flowptcontainer::PtMomentConfig; + +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr int ParityDivisor = 2; + +bool parseInteger(std::string_view token, int& value) +{ + if (!token.empty() && token.front() == '+') { + token.remove_prefix(1); + } + const auto result = std::from_chars(token.data(), token.data() + token.size(), value); + return !token.empty() && result.ec == std::errc{} && result.ptr == token.data() + token.size(); +} + +std::optional>> replacePtTerms(std::string& expression, const std::string& head) +{ + std::vector> isPt; + size_t cursor = 0; + while (true) { + auto openBrace = expression.find('{', cursor); + if (openBrace == std::string::npos) { + break; + } + if (openBrace > 0 && std::isspace(static_cast(expression[openBrace - 1])) == 0) { + expression.insert(openBrace++, 1, ' '); + } + const auto closeBrace = expression.find('}', openBrace + 1); + if (closeBrace == std::string::npos || expression.find('{', openBrace + 1) < closeBrace) { + LOGF(error, "Invalid pT correlation %s: unmatched brace", head.c_str()); + return std::nullopt; + } + + std::string terms = expression.substr(openBrace + 1, closeBrace - openBrace - 1); + std::replace(terms.begin(), terms.end(), ',', ' '); + std::replace(terms.begin(), terms.end(), ';', ' '); + std::istringstream stream(terms); + std::string replacement; + std::string token; + std::vector groupIsPt; + while (stream >> token) { + int harmonic = 0; + const bool tokenIsPt = token == "pt"; + if (!tokenIsPt && !parseInteger(token, harmonic)) { + LOGF(error, "Invalid pT correlation %s: unknown observable %s", head.c_str(), token.c_str()); + return std::nullopt; + } + if (!replacement.empty()) { + replacement += ' '; + } + replacement += tokenIsPt ? "0" : token; + groupIsPt.push_back(tokenIsPt); + } + if (groupIsPt.empty()) { + LOGF(error, "Invalid pT correlation %s: empty observable group", head.c_str()); + return std::nullopt; + } + + expression.replace(openBrace + 1, closeBrace - openBrace - 1, replacement); + cursor = openBrace + replacement.size() + 2; + isPt.push_back(std::move(groupIsPt)); + } + + if (isPt.empty()) { + LOGF(error, "Invalid pT correlation %s: no observable groups", head.c_str()); + return std::nullopt; + } + return isPt; +} + +std::string makeFullTopologyName(const std::string& head, std::uint64_t activeFactors, std::uint64_t allFactors, bool hasFlow, int ptOrder) +{ + if (activeFactors == allFactors) { + return head; + } + + std::string name = head + "_full"; + if (hasFlow && (activeFactors & std::uint64_t{1}) != 0) { + name += "_flow"; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int i = 0; i < ptOrder; ++i) { + if ((activeFactors & (std::uint64_t{1} << (ptOffset + i))) != 0) { + name += "_pt" + std::to_string(i + 1); + } + } + return name; +} + +double binomialCoefficient(int n, int k) +{ + k = std::min(k, n - k); + double result = 1.; + for (int i = 1; i <= k; ++i) { + result *= static_cast(n - k + i) / i; + } + return result; +} + +std::string makePostprocessName(std::string_view observable, const char* head, int ind) +{ + return std::string{head} + "_" + std::string{observable} + "_" + std::to_string(ind); +} + +bool sameMomentConfigs(const std::vector& first, const std::vector& second) +{ + if (first.size() != second.size()) { + return false; + } + for (size_t config = 0; config < first.size(); ++config) { + if (first[config].head != second[config].head || first[config].groups.size() != second[config].groups.size()) { + return false; + } + for (size_t group = 0; group < first[config].groups.size(); ++group) { + const auto& lhs = first[config].groups[group]; + const auto& rhs = second[config].groups[group]; + if (lhs.region != rhs.region || lhs.order != rhs.order || lhs.meanProfile != rhs.meanProfile) { + return false; + } + } + } + return true; +} +} // namespace FlowPtContainer::FlowPtContainer() : fCMTermList(nullptr), fCorrList(nullptr), @@ -54,7 +193,9 @@ FlowPtContainer::FlowPtContainer() : fCMTermList(nullptr), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) { } FlowPtContainer::~FlowPtContainer() @@ -66,6 +207,8 @@ FlowPtContainer::~FlowPtContainer() delete fSubCMList; delete fCumulantList; delete fCentralMomentList; + delete fPtCentralMomentList; + delete fPtCumulantList; }; FlowPtContainer::FlowPtContainer(const char* name) : TNamed(name, name), fCMTermList(nullptr), @@ -80,7 +223,9 @@ FlowPtContainer::FlowPtContainer(const char* name) : TNamed(name, name), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) { } FlowPtContainer::FlowPtContainer(const char* name, const char* title) : TNamed(name, title), @@ -96,8 +241,349 @@ FlowPtContainer::FlowPtContainer(const char* name, const char* title) : TNamed(n fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), - fUseGap(false) + fUseGap(false), + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) +{ +} +void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const GFWRegions& regions, const GFWCorrConfigs& configs, const int& nsub) { + const auto numberOfRegions = regions.GetNames().size(); + const auto numberOfConfigs = configs.GetCorrs().size(); + if (regions.GetEtaMin().size() != numberOfRegions || regions.GetEtaMax().size() != numberOfRegions || + regions.GetpTDifs().size() != numberOfRegions || regions.GetBitmasks().size() != numberOfRegions || + configs.GetHeads().size() != numberOfConfigs || configs.GetpTDifs().size() != numberOfConfigs) { + LOGF(error, "Cannot initialize %s: GFW configuration vectors have different sizes", GetName()); + return; + } + if (nsub < 0) { + LOGF(error, "Cannot initialize %s: number of subsamples cannot be negative", GetName()); + return; + } + + std::vector multiBins = axis.binEdges; + int numberOfBins = axis.nBins.value_or(0); + const bool isVariableBinning = numberOfBins <= 0; + if (isVariableBinning) { + numberOfBins = multiBins.size() - 1; + } + const size_t expectedEdges = isVariableBinning ? static_cast(numberOfBins + 1) : 2u; + if (numberOfBins <= 0 || multiBins.size() != expectedEdges) { + LOGF(error, "Cannot initialize %s: invalid multiplicity axis", GetName()); + return; + } + + GFW parser; + for (size_t i = 0; i < numberOfRegions; ++i) { + parser.AddRegion(regions.GetNames()[i], regions.GetEtaMin()[i], regions.GetEtaMax()[i], regions.GetpTDifs()[i] != 0 ? 2 : 1, regions.GetBitmasks()[i]); + } + if (parser.fRegions.size() != numberOfRegions) { + LOGF(error, "Cannot initialize %s: invalid GFW region", GetName()); + return; + } + + struct ParsedPtRequest { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::vector> ptTerms; + int ptOrder{}; + bool hasFlow{}; + std::uint64_t allFactors{}; + }; + + std::vector requests; + requests.reserve(numberOfConfigs); + std::unordered_set correlationHeads; + std::vector regionMaxOrders(numberOfRegions, 0); + int maxPtOrder = 0; + for (size_t i = 0; i < numberOfConfigs; ++i) { + if (configs.GetpTDifs()[i] != 0) { + LOGF(error, "Invalid pT correlation %s: differential configurations are not supported", configs.GetHeads()[i].c_str()); + return; + } + std::string expression = configs.GetCorrs()[i]; + auto isPt = replacePtTerms(expression, configs.GetHeads()[i]); + if (!isPt) { + return; + } + auto config = parser.GetCorrelatorConfig(expression, configs.GetHeads()[i], configs.GetpTDifs()[i] != 0); + if (config.Regs.size() != isPt->size() || config.Hars.size() != isPt->size() || config.Overlap.size() != isPt->size() || config.ptInd.size() != isPt->size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + if (std::any_of(config.ptInd.begin(), config.ptInd.end(), [](int ptIndex) { return ptIndex >= 0; })) { + LOGF(error, "Invalid pT correlation %s: explicit pT-bin indices are not supported", config.Head.c_str()); + return; + } + int ptOrder = 0; + bool hasFlow = false; + std::vector> ptTerms; + std::vector> factors(isPt->size()); + for (size_t group = 0; group < isPt->size(); ++group) { + if (config.Regs[group].empty() || config.Hars[group].size() != isPt->at(group).size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + const bool groupHasPt = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return value; }); + const bool groupHasFlow = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return !value; }); + if (groupHasPt && groupHasFlow) { + LOGF(error, "Invalid pT correlation %s: mixed flow and pt terms in one group are not supported", config.Head.c_str()); + return; + } + if (groupHasPt && (config.Regs[group].size() != 1 || config.Overlap[group] >= 0)) { + LOGF(error, "Invalid pT correlation %s: pt groups must contain exactly one region without an explicit overlap", config.Head.c_str()); + return; + } + factors[group].resize(isPt->at(group).size()); + for (size_t term = 0; term < isPt->at(group).size(); ++term) { + if (isPt->at(group)[term]) { + ptTerms.emplace_back(group, term); + ++ptOrder; + } else { + hasFlow = true; + } + } + } + if (ptOrder == 0) { + LOGF(error, "Invalid pT correlation %s: configuration does not contain pt", config.Head.c_str()); + return; + } + if (config.Head.empty() || !correlationHeads.insert(config.Head).second) { + LOGF(error, "Cannot initialize %s: correlation heads must be non-empty and unique", GetName()); + return; + } + + const int factorCount = ptOrder + (hasFlow ? 1 : 0); + if (factorCount >= std::numeric_limits::digits) { + LOGF(error, "Invalid pT correlation %s: too many observable factors", config.Head.c_str()); + return; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int ptIndex = 0; ptIndex < ptOrder; ++ptIndex) { + const auto [group, term] = ptTerms[ptIndex]; + factors[group][term] = ptOffset + ptIndex; + } + if (hasFlow) { + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + std::fill(factors[group].begin(), factors[group].end(), 0); + } + } + } + const std::uint64_t allFactors = (std::uint64_t{1} << factorCount) - 1; + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + continue; + } + const int order = isPt->at(group).size(); + for (const int& region : config.Regs[group]) { + regionMaxOrders[region] = std::max(regionMaxOrders[region], order); + } + if (config.Overlap[group] >= 0) { + regionMaxOrders[config.Overlap[group]] = std::max(regionMaxOrders[config.Overlap[group]], order); + } + } + + maxPtOrder = std::max(maxPtOrder, ptOrder); + requests.push_back({.config = std::move(config), + .isPt = std::move(*isPt), + .factors = std::move(factors), + .ptTerms = std::move(ptTerms), + .ptOrder = ptOrder, + .hasFlow = hasFlow, + .allFactors = allFactors}); + } + + struct NaturalMean { + int region{}; + int ptIndex{}; + bool pTDif{}; + std::string profile; + }; + std::vector naturalMeans; + auto getPtRegion = [](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + return request.config.Regs[group].front(); + }; + auto findNaturalMean = [&](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + const int region = getPtRegion(request, ptIndex); + return std::find_if(naturalMeans.begin(), naturalMeans.end(), [&](const auto& mean) { + return mean.region == region && mean.ptIndex == request.config.ptInd[group] && mean.pTDif == request.config.pTDif; + }); + }; + + // Prefer explicitly requested one-particle observables as the shared native means, + // irrespective of their order in the configuration list. + for (const auto& request : requests) { + if (request.hasFlow || request.ptOrder != 1 || findNaturalMean(request, 0) != naturalMeans.end()) { + continue; + } + const auto group = request.ptTerms.front().first; + naturalMeans.push_back({.region = getPtRegion(request, 0), + .ptIndex = request.config.ptInd[group], + .pTDif = request.config.pTDif, + .profile = request.config.Head}); + } + + std::vector parsedConfigs; + parsedConfigs.reserve(requests.size()); + std::vector momentConfigs; + momentConfigs.reserve(requests.size()); + for (const auto& request : requests) { + const int ptOffset = request.hasFlow ? 1 : 0; + PtCorrConfig parsed{ + .head = request.config.Head, + .allFactors = request.allFactors, + .hasFlow = request.hasFlow, + .profiles = {}, + .meanProfiles = std::vector(request.ptOrder)}; + + // Factors in one single-region pT group are exchangeable. Their full-topology + // terms therefore depend only on how many factors are active. + std::vector> factorClasses; + if (request.hasFlow) { + factorClasses.push_back({0}); + } + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + continue; + } + factorClasses.push_back(request.factors[group]); + } + + std::vector activeMasks{0}; + for (const auto& factors : factorClasses) { + const size_t previousSize = activeMasks.size(); + for (size_t i = 0; i < previousSize; ++i) { + std::uint64_t addedFactors = 0; + for (const int& factor : factors) { + addedFactors |= std::uint64_t{1} << factor; + activeMasks.push_back(activeMasks[i] | addedFactors); + } + } + } + std::sort(activeMasks.begin(), activeMasks.end()); + parsed.profiles.reserve(activeMasks.size() + request.ptOrder); + + // Inactive factors stay in the tuple as harmonic-zero multiplicity insertions, + // so every subset retains the event weight of the full correlation. + auto addFullTopologyProfile = [&](std::uint64_t activeFactors) { + auto profileConfig = request.config; + if (request.hasFlow && (activeFactors & std::uint64_t{1}) == 0) { + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + std::fill(profileConfig.Hars[group].begin(), profileConfig.Hars[group].end(), 0); + } + } + } + profileConfig.Head = makeFullTopologyName(request.config.Head, activeFactors, request.allFactors, request.hasFlow, request.ptOrder); + parsed.profiles.push_back({.config = std::move(profileConfig), + .isPt = request.isPt, + .factors = request.factors, + .activeFactors = activeFactors, + .usesFullTopology = true}); + }; + + addFullTopologyProfile(request.allFactors); + for (const auto& activeFactors : activeMasks) { + if (activeFactors != 0 && activeFactors != request.allFactors) { + addFullTopologyProfile(activeFactors); + } + } + + // Each distinct pT source has one naturally weighted mean. Explicit one-particle + // requests provide that mean directly; otherwise the first consumer books it. + for (int ptIndex = 0; ptIndex < request.ptOrder; ++ptIndex) { + const std::uint64_t factor = std::uint64_t{1} << (ptOffset + ptIndex); + if (factor == request.allFactors) { + parsed.meanProfiles[ptIndex] = request.config.Head; + continue; + } + auto existingMean = findNaturalMean(request, ptIndex); + if (existingMean != naturalMeans.end()) { + parsed.meanProfiles[ptIndex] = existingMean->profile; + continue; + } + + const auto group = request.ptTerms[ptIndex].first; + GFW::CorrConfig meanConfig; + meanConfig.Regs = {{getPtRegion(request, ptIndex)}}; + meanConfig.Hars = {{0}}; + meanConfig.Overlap = {-1}; + meanConfig.ptInd = {request.config.ptInd[group]}; + meanConfig.pTDif = request.config.pTDif; + meanConfig.Head = request.config.Head + "_mean_pt" + std::to_string(ptIndex + 1); + parsed.meanProfiles[ptIndex] = meanConfig.Head; + naturalMeans.push_back({.region = meanConfig.Regs.front().front(), + .ptIndex = meanConfig.ptInd.front(), + .pTDif = meanConfig.pTDif, + .profile = meanConfig.Head}); + parsed.profiles.push_back({.config = std::move(meanConfig), + .isPt = {{true}}, + .factors = {{ptOffset + ptIndex}}, + .activeFactors = factor, + .usesFullTopology = false}); + } + + if (!request.hasFlow) { + PtMomentConfig momentConfig{ + .head = request.config.Head, + .groups = {}}; + momentConfig.groups.reserve(request.isPt.size()); + for (size_t group = 0; group < request.isPt.size(); ++group) { + const int firstFactor = request.factors[group].front(); + const int region = request.config.Regs[group].front(); + momentConfig.groups.push_back({.region = regions.GetNames()[region], + .order = static_cast(request.factors[group].size()), + .meanProfile = parsed.meanProfiles[firstFactor]}); + } + momentConfigs.push_back(std::move(momentConfig)); + } + + parsedConfigs.push_back(std::move(parsed)); + } + + auto* profiles = new TList(); + profiles->SetOwner(kTRUE); + std::unordered_set profileNames; + for (const auto& parsed : parsedConfigs) { + for (const auto& profile : parsed.profiles) { + const auto& head = profile.config.Head; + if (!profileNames.insert(head).second) { + LOGF(error, "Cannot initialize %s: generated profile name %s is not unique", GetName(), head.c_str()); + delete profiles; + return; + } + if (isVariableBinning) { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.data())); + } else { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.front(), multiBins.back())); + } + } + } + if (nsub > 0) { + for (int i = 0; i < profiles->GetEntries(); ++i) { + dynamic_cast(profiles->At(i))->InitializeSubsamples(nsub); + } + } + + delete fCorrList; + fCorrList = profiles; + fPtRegions = regions; + fPtCorrConfigs = std::move(parsedConfigs); + fPtMomentConfigs = std::move(momentConfigs); + clearPtPostprocessLists(); + fPtRegionMoments.clear(); + fPtRegionMoments.reserve(regionMaxOrders.size()); + for (const int& order : regionMaxOrders) { + fPtRegionMoments.push_back({.maxOrder = order, + .values = std::vector((order + 1) * (order + 1), 0.)}); + } + mpar = maxPtOrder; + nSubevents = static_cast(numberOfRegions); + LOGF(info, "Container %s initialized with %zu pT correlations, %i profiles and %i subsamples", GetName(), numberOfConfigs, profiles->GetEntries(), nsub); } void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const GFWCorrConfigs& configs, const int& nsub) { @@ -618,6 +1104,94 @@ void FlowPtContainer::fill(const double& w, const double& pt) sumP[i] += std::pow(w, i % (mpar + 1)) * std::pow(pt, i / (mpar + 1)); } } +void FlowPtContainer::fill(const double& eta, const double& w, const double& pt, int mask) +{ + int maxOrder = 0; + for (const auto& moments : fPtRegionMoments) { + maxOrder = std::max(maxOrder, moments.maxOrder); + } + if (maxOrder == 0) { + return; + } + + std::vector weightPowers(maxOrder + 1, 1.); + std::vector ptPowers(maxOrder + 1, 1.); + for (int power = 1; power <= maxOrder; ++power) { + weightPowers[power] = weightPowers[power - 1] * w; + ptPowers[power] = ptPowers[power - 1] * pt; + } + + for (size_t region = 0; region < fPtRegionMoments.size(); ++region) { + auto& moments = fPtRegionMoments[region]; + const bool inEtaRange = fPtRegions.GetEtaMin()[region] < eta && eta < fPtRegions.GetEtaMax()[region]; + const bool maskMatches = (fPtRegions.GetBitmasks()[region] & mask) != 0; + if (moments.maxOrder == 0 || !inEtaRange || !maskMatches) { + continue; + } + for (int weightPower = 1; weightPower <= moments.maxOrder; ++weightPower) { + for (int ptPower = 0; ptPower <= weightPower; ++ptPower) { + moments.values[weightPower * (moments.maxOrder + 1) + ptPower] += weightPowers[weightPower] * ptPowers[ptPower]; + } + } + } +} +void FlowPtContainer::clearPtRegionMoments() +{ + for (auto& moments : fPtRegionMoments) { // o2-linter: disable=const-ref-in-for-loop (contents are reset) + std::fill(moments.values.begin(), moments.values.end(), 0.); + } +} +void FlowPtContainer::clearPtPostprocessLists() +{ + delete fPtCentralMomentList; + fPtCentralMomentList = nullptr; + delete fPtCumulantList; + fPtCumulantList = nullptr; +} +double FlowPtContainer::getPtMoment(int region, int weightPower, int ptPower) const +{ + if (region < 0 || region >= static_cast(fPtRegionMoments.size())) { + return 0.; + } + const auto& moments = fPtRegionMoments[region]; + if (weightPower < 1 || weightPower > moments.maxOrder || ptPower < 0 || ptPower > weightPower) { + return 0.; + } + return moments.values[weightPower * (moments.maxOrder + 1) + ptPower]; +} +double FlowPtContainer::recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const +{ + if (weightPowers.empty() || weightPowers.size() != ptPowers.size()) { + return 0.; + } + if (weightPowers.front() != 1 && overlap >= 0) { + poi = overlap; + } + if (weightPowers.size() == 1) { + return getPtMoment(poi, weightPowers.front(), ptPowers.front()); + } + if (weightPowers.size() == TwoParticleOrder) { + const double product = getPtMoment(poi, weightPowers[0], ptPowers[0]) * getPtMoment(ref, weightPowers[1], ptPowers[1]); + const double selfCorrelation = overlap >= 0 ? getPtMoment(overlap, weightPowers[0] + weightPowers[1], ptPowers[0] + ptPowers[1]) : 0.; + return product - selfCorrelation; + } + + const int lastWeightPower = weightPowers.back(); + const int lastPtPower = ptPowers.back(); + weightPowers.pop_back(); + ptPowers.pop_back(); + double result = recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers) * getPtMoment(ref, lastWeightPower, lastPtPower); + for (int term = static_cast(weightPowers.size()) - 1; term >= 0; --term) { + weightPowers[term] += lastWeightPower; + ptPowers[term] += lastPtPower; + result -= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + weightPowers[term] -= lastWeightPower; + ptPowers[term] -= lastPtPower; + } + weightPowers.push_back(lastWeightPower); + ptPowers.push_back(lastPtPower); + return result; +} void FlowPtContainer::fillSub(const double& w, const double& pt, int subIndex) { for (size_t i = 0; i < insub[subIndex].size(); ++i) { @@ -689,6 +1263,57 @@ void FlowPtContainer::fillPtProfiles(const double& centmult, const double& rn) } } } +void FlowPtContainer::fillPtProfiles(GFW& gfw, const double& centmult, const double& rn) +{ + if (!fCorrList) { + return; + } + clearPtPostprocessLists(); + for (const auto& correlation : fPtCorrConfigs) { + for (const auto& profileConfig : correlation.profiles) { + std::complex numerator{1., 0.}; + double denominator = 1.; + for (size_t group = 0; group < profileConfig.config.Regs.size(); ++group) { + if (profileConfig.isPt[group].front()) { + const int poi = profileConfig.config.Regs[group].front(); + const int ref = profileConfig.config.Regs[group].size() > 1 ? profileConfig.config.Regs[group][1] : poi; + int overlap = profileConfig.config.Overlap[group]; + if (overlap < 0 && poi == ref) { + overlap = ref; + } + std::vector weightPowers(profileConfig.isPt[group].size(), 1); + std::vector ptPowers; + ptPowers.reserve(profileConfig.isPt[group].size()); + for (const int& factor : profileConfig.factors[group]) { + ptPowers.push_back((profileConfig.activeFactors & (std::uint64_t{1} << factor)) != 0 ? 1 : 0); + } + auto denominatorPtPowers = std::vector(ptPowers.size(), 0); + denominator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, denominatorPtPowers); + numerator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + } else { + GFW::CorrConfig groupConfig; + groupConfig.Regs = {profileConfig.config.Regs[group]}; + groupConfig.Hars = {profileConfig.config.Hars[group]}; + groupConfig.Overlap = {profileConfig.config.Overlap[group]}; + groupConfig.ptInd = {profileConfig.config.ptInd[group]}; + groupConfig.pTDif = profileConfig.config.pTDif; + numerator *= gfw.Calculate(groupConfig, 0, false); + denominator *= gfw.Calculate(groupConfig, 0, true).real(); + } + } + if (!std::isfinite(numerator.real()) || !std::isfinite(numerator.imag()) || !std::isfinite(denominator) || denominator <= 0.) { + continue; + } + auto* profile = dynamic_cast(fCorrList->FindObject(profileConfig.config.Head.c_str())); + if (!profile) { + LOGF(error, "pT profile %s has not been booked", profileConfig.config.Head.c_str()); + continue; + } + const double eventWeight = fEventWeight == EventWeight::UnityWeight ? 1. : denominator; + profile->FillProfile(centmult, numerator.real() / denominator, eventWeight, rn); + } + } +} bool FlowPtContainer::addPtProfile(const char* name, int observableOrder) { if (!fCorrList || !name || (name[0] == 0) || observableOrder < 1 || observableOrder > mpar) { @@ -1748,6 +2373,7 @@ double FlowPtContainer::orderedAddition(std::vector vec) } void FlowPtContainer::rebinMulti(int nbins) { + clearPtPostprocessLists(); if (fCMTermList) { for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins); @@ -1776,6 +2402,7 @@ void FlowPtContainer::rebinMulti(int nbins) } void FlowPtContainer::rebinMulti(int nbins, double* binedges) { + clearPtPostprocessLists(); if (fCMTermList) { for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins, binedges); @@ -1806,6 +2433,295 @@ TH1* FlowPtContainer::getCorrHist(int ind, int m) { return dynamic_cast(fCorrList->FindObject(Form("mpt%i", m)))->getHist(ind); } +TH1* FlowPtContainer::getPtCentralMomentHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcentral", head, ind); + if (fPtCentralMomentList) { + if (auto* result = dynamic_cast(fPtCentralMomentList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT central moment %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + int order = 0; + for (const auto& group : config->groups) { + order += group.order; + } + if (order < 1 || order >= std::numeric_limits::digits) { + LOGF(error, "Cannot calculate pT central moment %s: invalid order %i", head, order); + return nullptr; + } + + auto getProfileHist = [&](const std::string& name) -> std::unique_ptr { + auto* profile = dynamic_cast(fCorrList->FindObject(name.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT central moment %s: profile %s is missing", head, name.c_str()); + return nullptr; + } + std::unique_ptr histogram{profile->getHist(ind)}; + if (histogram) { + histogram->SetDirectory(nullptr); + } + return histogram; + }; + + auto shape = getProfileHist(config->head); + if (!shape) { + return nullptr; + } + auto* result = dynamic_cast(shape->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + const std::uint64_t allFactors = (std::uint64_t{1} << order) - 1; + std::vector activeCounts(config->groups.size(), 0); + bool finished = false; + while (!finished) { + int activeOrder = 0; + int factorOffset = 0; + double coefficient = 1.; + std::uint64_t activeFactors = 0; + std::map meanPowers; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + const auto& group = config->groups[groupIndex]; + const int active = activeCounts[groupIndex]; + activeOrder += active; + coefficient *= binomialCoefficient(group.order, active); + for (int factor = 0; factor < active; ++factor) { + activeFactors |= std::uint64_t{1} << (factorOffset + factor); + } + if (active < group.order) { + meanPowers[group.meanProfile] += group.order - active; + } + factorOffset += group.order; + } + + std::unique_ptr term; + if (activeOrder == 0) { + term.reset(dynamic_cast(shape->Clone())); + term->SetDirectory(nullptr); + term->Reset(); + for (int bin = 1; bin <= term->GetNbinsX(); ++bin) { + if (shape->GetBinContent(bin) != 0. || shape->GetBinError(bin) != 0.) { + term->SetBinContent(bin, 1.); + } + } + } else { + term = getProfileHist(makeFullTopologyName(config->head, activeFactors, allFactors, false, order)); + if (!term) { + delete result; + return nullptr; + } + term->SetDirectory(nullptr); + } + + for (const auto& [meanName, power] : meanPowers) { + auto mean = getProfileHist(meanName); + if (!mean) { + delete result; + return nullptr; + } + mean->SetDirectory(nullptr); + if (power > 1) { + for (int bin = 0; bin <= mean->GetNbinsX() + 1; ++bin) { + const double value = mean->GetBinContent(bin); + const double error = mean->GetBinError(bin); + mean->SetBinContent(bin, std::pow(value, power)); + mean->SetBinError(bin, std::abs(power * std::pow(value, power - 1) * error)); + } + } + term->Multiply(mean.get()); + } + + if ((order - activeOrder) % ParityDivisor != 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + + size_t groupIndex = 0; + for (; groupIndex < activeCounts.size(); ++groupIndex) { + if (++activeCounts[groupIndex] <= config->groups[groupIndex].order) { + break; + } + activeCounts[groupIndex] = 0; + } + finished = groupIndex == activeCounts.size(); + } + + if (!fPtCentralMomentList) { + fPtCentralMomentList = new TList(); + fPtCentralMomentList->SetOwner(kTRUE); + } + fPtCentralMomentList->Add(result); + return result; +} +TH1* FlowPtContainer::getPtCumulantHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcumulant", head, ind); + if (fPtCumulantList) { + if (auto* result = dynamic_cast(fPtCumulantList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + std::vector factorGroups; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + factorGroups.insert(factorGroups.end(), config->groups[groupIndex].order, groupIndex); + } + const int order = factorGroups.size(); + if (order < 1) { + return nullptr; + } + + if (order == 1) { + auto* profile = dynamic_cast(fCorrList->FindObject(config->groups.front().meanProfile.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT cumulant %s: profile %s is missing", head, config->groups.front().meanProfile.c_str()); + return nullptr; + } + auto* result = profile->getHist(ind); + if (!result) { + return nullptr; + } + result->SetName(resultName.c_str()); + result->SetDirectory(nullptr); + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; + } + + auto* centralMoment = getPtCentralMomentHist(head, ind); + if (!centralMoment) { + return nullptr; + } + auto* result = dynamic_cast(centralMoment->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + using Topology = std::vector>; + auto topology = [](const PtMomentConfig& candidate, const std::vector* counts = nullptr) { + Topology value; + for (size_t groupIndex = 0; groupIndex < candidate.groups.size(); ++groupIndex) { + const int count = counts ? counts->at(groupIndex) : candidate.groups[groupIndex].order; + if (count > 0) { + value.emplace_back(candidate.groups[groupIndex].region, count); + } + } + std::sort(value.begin(), value.end()); + return value; + }; + auto findMomentConfig = [&](const std::vector& counts) { + const auto required = topology(*config, &counts); + return std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return topology(candidate) == required; }); + }; + auto describeTopology = [&](const std::vector& counts) { + std::string description; + for (const auto& [region, count] : topology(*config, &counts)) { + if (!description.empty()) { + description += " "; + } + description += region + "{" + std::to_string(count) + "pt}"; + } + return description; + }; + + bool failed = false; + std::vector blockForFactor(order, 0); + std::function addPartitions = [&](int factor, int highestBlock) { + if (failed) { + return; + } + if (factor < order) { + for (int block = 0; block <= highestBlock + 1; ++block) { + blockForFactor[factor] = block; + addPartitions(factor + 1, std::max(highestBlock, block)); + } + return; + } + + const int numberOfBlocks = highestBlock + 1; + std::vector> blockCounts(numberOfBlocks, std::vector(config->groups.size(), 0)); + std::vector blockSizes(numberOfBlocks, 0); + for (int factorIndex = 0; factorIndex < order; ++factorIndex) { + const int block = blockForFactor[factorIndex]; + ++blockCounts[block][factorGroups[factorIndex]]; + ++blockSizes[block]; + } + if (std::any_of(blockSizes.begin(), blockSizes.end(), [](int size) { return size == 1; })) { + return; + } + + std::unique_ptr term; + for (int block = 0; block < numberOfBlocks; ++block) { + const PtMomentConfig* blockConfig = &*config; + if (numberOfBlocks > 1) { + const auto found = findMomentConfig(blockCounts[block]); + if (found == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: missing lower-order correlation %s", head, describeTopology(blockCounts[block]).c_str()); + failed = true; + return; + } + blockConfig = &*found; + } + auto* blockMoment = getPtCentralMomentHist(blockConfig->head.c_str(), ind); + if (!blockMoment) { + failed = true; + return; + } + if (!term) { + term.reset(dynamic_cast(blockMoment->Clone())); + term->SetDirectory(nullptr); + } else { + term->Multiply(blockMoment); + } + } + + double coefficient = 1.; + for (int i = 2; i < numberOfBlocks; ++i) { + coefficient *= i; + } + if (numberOfBlocks % ParityDivisor == 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + }; + + blockForFactor.front() = 0; + addPartitions(1, 0); + if (failed) { + delete result; + return nullptr; + } + + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; +} TH1* FlowPtContainer::getCentralMomentHist(int ind, int m) { if (!fCentralMomentList) { @@ -1897,27 +2813,41 @@ void FlowPtContainer::calculateCumulantHists(std::vector inh, int ind) } Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name/function-variable (Keep name consistent with ROOT streamer merging) { - if (!fCorrList || !fCMTermList) { - return 0; - } + delete fCumulantList; + fCumulantList = nullptr; + delete fCentralMomentList; + fCentralMomentList = nullptr; + clearPtPostprocessLists(); + Long64_t nmerged = 0; TIter allPTC(collist); FlowPtContainer* lPTC = nullptr; while ((lPTC = dynamic_cast(allPTC()))) { + if (!lPTC->fPtMomentConfigs.empty()) { + if (fPtMomentConfigs.empty()) { + fPtMomentConfigs = lPTC->fPtMomentConfigs; + } else if (!sameMomentConfigs(fPtMomentConfigs, lPTC->fPtMomentConfigs)) { + LOGF(error, "Cannot merge FlowPtContainer %s: pT postprocessing configurations differ", GetName()); + continue; + } + } else if (!fPtMomentConfigs.empty()) { + LOGF(error, "Cannot merge FlowPtContainer %s: incoming pT postprocessing configuration is missing", GetName()); + continue; + } + TList* tCMTerm = lPTC->fCMTermList; TList* tCorr = lPTC->fCorrList; TList* tCov = lPTC->fCovList; TList* tSub = lPTC->fSubList; TList* tSubCM = lPTC->fSubCMList; - TList* tCum = lPTC->fCumulantList; - TList* tCM = lPTC->fCentralMomentList; + bool merged = false; if (tCMTerm) { if (!fCMTermList) { fCMTermList = dynamic_cast(tCMTerm->Clone()); } else { mergeBSLists(fCMTermList, tCMTerm); } - nmerged++; + merged = true; } if (tCorr) { if (!fCorrList) { @@ -1925,6 +2855,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fCorrList, tCorr); } + merged = true; } if (tCov) { if (!fCovList) { @@ -1932,20 +2863,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fCovList, tCov); } - } - if (tCum) { - if (!fCumulantList) { - fCumulantList = dynamic_cast(tCum->Clone()); - } else { - mergeBSLists(fCumulantList, tCum); - } - } - if (tCM) { - if (!fCentralMomentList) { - fCentralMomentList = dynamic_cast(tCM->Clone()); - } else { - mergeBSLists(fCentralMomentList, tCM); - } + merged = true; } if (tSub) { if (!fSubList) { @@ -1953,6 +2871,7 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fSubList, tSub); } + merged = true; } if (tSubCM) { if (!fSubCMList) { @@ -1960,6 +2879,10 @@ Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name } else { mergeBSLists(fSubCMList, tSubCM); } + merged = true; + } + if (merged) { + ++nmerged; } } return nmerged; diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.h b/PWGCF/GenericFramework/Core/FlowPtContainer.h index 904153b7887..6bd33833f7e 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.h +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.h @@ -16,6 +16,7 @@ #ifndef PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ #define PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ +#include "GFW.h" #include "GFWConfig.h" #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include @@ -42,6 +44,43 @@ enum EventWeight { }; }; +namespace o2::analysis::genericframework::flowptcontainer +{ +struct PtProfileConfig { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::uint64_t activeFactors{}; // Flow, when present, is bit 0; pT terms follow in expression order. + bool usesFullTopology{}; +}; + +struct PtCorrConfig { + std::string head; + std::uint64_t allFactors{}; + bool hasFlow{}; + std::vector profiles; + std::vector meanProfiles; +}; + +struct PtRegionMoments { + int maxOrder{}; + std::vector values; +}; + +struct PtMomentGroup { + std::string region; + int order{}; + std::string meanProfile; + ClassDefNV(PtMomentGroup, 1); +}; + +struct PtMomentConfig { + std::string head; + std::vector groups; + ClassDefNV(PtMomentConfig, 1); +}; +} // namespace o2::analysis::genericframework::flowptcontainer + class FlowPtContainer : public TNamed { public: @@ -50,6 +89,7 @@ class FlowPtContainer : public TNamed explicit FlowPtContainer(const char* name); ~FlowPtContainer() override; FlowPtContainer(const char* name, const char* title); + void initialise(const o2::framework::AxisSpec& axis, const o2::analysis::genericframework::GFWRegions& regions, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(int nbinsx, double* xbins, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); void initialise(int nbinsx, double xlow, double xhigh, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); @@ -58,6 +98,7 @@ class FlowPtContainer : public TNamed void initialiseSubevent(int nbinsx, double* xbins, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); void initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); void fill(const double& w, const double& pt); + void fill(const double& eta, const double& w, const double& pt, int mask); void fillSub(const double& w, const double& pt, int subIndex); void fillSub1(const double& w, const double& pt) { fillSub(w, pt, 0); } void fillSub2(const double& w, const double& pt) { fillSub(w, pt, nSubevents - 1); } @@ -68,6 +109,7 @@ class FlowPtContainer : public TNamed void calculateSubeventCorrelations(); void calculateCMTerms(); void fillPtProfiles(const double& centmult, const double& rn); + void fillPtProfiles(GFW& gfw, const double& centmult, const double& rn); // Book and fill a separate pT observable with a weight calculated by the task. // The task can supply a separate event weight for the same observable. bool addPtProfile(const char* name, int observableOrder); @@ -133,6 +175,9 @@ class FlowPtContainer : public TNamed TH1* getCentralMomentHist(int ind, int m); TH1* getCumulantHist(int ind, int m); TH1* getCorrHist(int ind, int m); + // Postprocess a pure-pT configuration by head. ind = -1 selects the main profile. + TH1* getPtCentralMomentHist(const char* head, int ind = -1); + TH1* getPtCumulantHist(const char* head, int ind = -1); int getMpar() { return mpar; } Long64_t Merge(TCollection* collist); double orderedAddition(std::vector vec); @@ -157,6 +202,7 @@ class FlowPtContainer : public TNamed arr.resize(3 * 3 * 5 * 5, {0.0, 0.0}); warr.clear(); warr.resize(3 * 3 * 5 * 5, 0.0); + clearPtRegionMoments(); }; TList* fCMTermList; @@ -229,10 +275,20 @@ class FlowPtContainer : public TNamed private: static const int centralMomentMaxOrder = 4; - std::vector> subevents; + o2::analysis::genericframework::GFWRegions fPtRegions; //! + std::vector fPtCorrConfigs; //! + std::vector fPtRegionMoments; //! + std::vector fPtMomentConfigs; + TList* fPtCentralMomentList; //! + TList* fPtCumulantList; //! + std::vector> subevents; //! + void clearPtPostprocessLists(); + void clearPtRegionMoments(); + [[nodiscard]] double getPtMoment(int region, int weightPower, int ptPower) const; + double recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const; void getSubevents(int k, int n, std::vector& current, std::vector>& subevents); static constexpr std::array FactorialArray = {1., 1., 2., 6., 24., 120., 720., 5040., 40320.}; static constexpr std::array SignArray = {1, -1, 1, -1, 1, -1, 1, -1, 1}; - ClassDefOverride(FlowPtContainer, 2); + ClassDefOverride(FlowPtContainer, 3); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ diff --git a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h index e3907fec165..a9a3da8c05e 100644 --- a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h +++ b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h @@ -27,3 +27,7 @@ #pragma link C++ class o2::analysis::genericframework::GFWBinningCuts + ; #pragma link C++ class o2::analysis::genericframework::GFWRegions + ; #pragma link C++ class o2::analysis::genericframework::GFWCorrConfigs + ; +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentGroup + ; +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentConfig + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentGroup> + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentConfig> + ; From e473593c1d6d6cfa58d988e903207cced6656fee Mon Sep 17 00:00:00 2001 From: Dukhishyam Mallick <160018357+dmallick2@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:31:20 +0200 Subject: [PATCH 405/648] [PWGDQ] Add GJ axis definition and fill histograms (#18271) --- PWGDQ/Core/HistogramsLibrary.cxx | 34 ++- PWGDQ/Core/VarManager.cxx | 36 +++ PWGDQ/Core/VarManager.h | 284 +++++++++++++++++- PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 53 +++- 4 files changed, 390 insertions(+), 17 deletions(-) diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 662a89f514e..308f81c8425 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -1306,14 +1306,28 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h std::array bins = {50, 30, 10, 20, 10}; std::array xmin = {2., 0., 0, -1., 0.0}; std::array xmax = {4., 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), 0, -1, kFALSE); + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb1-gen") == 0) { + std::array varspTGJB1 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB1, VarManager::kMCPhiGJB1}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varspTGJB1.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb2-gen") == 0) { + std::array varspTGJB2 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB2, VarManager::kMCPhiGJB2}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varspTGJB2.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); } if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-cs-gen") == 0) { std::array varspTCS = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaCS, VarManager::kMCPhiCS}; std::array bins = {50, 30, 10, 20, 10}; std::array xmin = {2., 0., 0, -1., 0.0}; std::array xmax = {4., 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), 0, -1, kFALSE); + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); } if (groupStr.CompareTo("pair") == 0) { if (subGroupStr.Contains("cepf")) { @@ -1422,6 +1436,22 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } + if (subGroupStr.Contains("dielectron-polarization-gjb1-pbpb")) { + std::array varsGJB1pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB1, VarManager::kPhiGJB1}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varsGJB1pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + + if (subGroupStr.Contains("dielectron-polarization-gjb2-pbpb")) { + std::array varsGJB2pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB2, VarManager::kPhiGJB2}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varsGJB2pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + if (subGroupStr.Contains("dielectron-polarization-cs-pbpb")) { std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; std::array binspT = {100, 30, 10, 10, 10}; diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index d1b193ec12c..ce0d01b55c3 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -1273,6 +1273,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCPhiCS] = "rad"; fgVariableNames[kMCPhiTildeCS] = "MC #tilde{#varphi}_{CS}"; fgVariableUnits[kMCPhiTildeCS] = "rad"; + fgVariableNames[kMCCosThetaGJB1] = "MC cos#it{#theta}_{GJ1}"; + fgVariableUnits[kMCCosThetaGJB1] = ""; + fgVariableNames[kMCPhiGJB1] = "MC #varphi_{GJ1}"; + fgVariableUnits[kMCPhiGJB1] = "rad"; + fgVariableNames[kMCPhiTildeGJB1] = "MC #tilde{#varphi}_{GJ1}"; + fgVariableUnits[kMCPhiTildeGJB1] = "rad"; + fgVariableNames[kMCCosThetaGJB2] = "MC cos#it{#theta}_{GJ2}"; + fgVariableUnits[kMCCosThetaGJB2] = ""; + fgVariableNames[kMCPhiGJB2] = "MC #varphi_{GJ2}"; + fgVariableUnits[kMCPhiGJB2] = "rad"; + fgVariableNames[kMCPhiTildeGJB2] = "MC #tilde{#varphi}_{GJ2}"; + fgVariableUnits[kMCPhiTildeGJB2] = "rad"; fgVariableNames[kMCCosThetaPP] = "MC cos(#theta_{PP})"; fgVariableUnits[kMCCosThetaPP] = ""; fgVariableNames[kMCPhiPP] = "MC #varphi_{PP}"; @@ -1715,6 +1727,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kPhiCS] = "rad."; fgVariableNames[kPhiTildeCS] = "#tilde{#varphi}_{CS}"; fgVariableUnits[kPhiTildeCS] = "rad."; + fgVariableNames[kCosThetaGJB1] = "cos#it{#theta}_{GJ1}"; + fgVariableUnits[kCosThetaGJB1] = ""; + fgVariableNames[kPhiGJB1] = "#varphi_{GJ1}"; + fgVariableUnits[kPhiGJB1] = "rad."; + fgVariableNames[kPhiTildeGJB1] = "#tilde{#varphi}_{GJ1}"; + fgVariableUnits[kPhiTildeGJB1] = "rad."; + fgVariableNames[kCosThetaGJB2] = "cos#it{#theta}_{GJ2}"; + fgVariableUnits[kCosThetaGJB2] = ""; + fgVariableNames[kPhiGJB2] = "#varphi_{GJ2}"; + fgVariableUnits[kPhiGJB2] = "rad."; + fgVariableNames[kPhiTildeGJB2] = "#tilde{#varphi}_{GJ2}"; + fgVariableUnits[kPhiTildeGJB2] = "rad."; fgVariableNames[kCosThetaPP] = "cos#it{#theta}_{PP}"; fgVariableUnits[kCosThetaPP] = ""; fgVariableNames[kPhiPP] = "#varphi_{PP}"; @@ -2559,6 +2583,12 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCCosThetaCS"] = kMCCosThetaCS; fgVarNamesMap["kMCPhiCS"] = kMCPhiCS; fgVarNamesMap["kMCPhiTildeCS"] = kMCPhiTildeCS; + fgVarNamesMap["kMCCosThetaGJB1"] = kMCCosThetaGJB1; + fgVarNamesMap["kMCPhiGJB1"] = kMCPhiGJB1; + fgVarNamesMap["kMCPhiTildeGJB1"] = kMCPhiTildeGJB1; + fgVarNamesMap["kMCCosThetaGJB2"] = kMCCosThetaGJB2; + fgVarNamesMap["kMCPhiGJB2"] = kMCPhiGJB2; + fgVarNamesMap["kMCPhiTildeGJB2"] = kMCPhiTildeGJB2; fgVarNamesMap["kMCCosThetaPP"] = kMCCosThetaPP; fgVarNamesMap["kMCPhiPP"] = kMCPhiPP; fgVarNamesMap["kMCPhiTildePP"] = kMCPhiTildePP; @@ -2605,6 +2635,12 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCosThetaCS"] = kCosThetaCS; fgVarNamesMap["kPhiCS"] = kPhiCS; fgVarNamesMap["kPhiTildeCS"] = kPhiTildeCS; + fgVarNamesMap["kCosThetaGJB1"] = kCosThetaGJB1; + fgVarNamesMap["kPhiGJB1"] = kPhiGJB1; + fgVarNamesMap["kPhiTildeGJB1"] = kPhiTildeGJB1; + fgVarNamesMap["kCosThetaGJB2"] = kCosThetaGJB2; + fgVarNamesMap["kPhiGJB2"] = kPhiGJB2; + fgVarNamesMap["kPhiTildeGJB2"] = kPhiTildeGJB2; fgVarNamesMap["kCosThetaPP"] = kCosThetaPP; fgVarNamesMap["kPhiPP"] = kPhiPP; fgVarNamesMap["kPhiTildePP"] = kPhiTildePP; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index ed09b758655..c83f57daa7c 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -776,6 +776,12 @@ class VarManager : public TObject kMCCosThetaCS, kMCPhiCS, kMCPhiTildeCS, + kMCCosThetaGJB1, + kMCPhiGJB1, + kMCPhiTildeGJB1, + kMCCosThetaGJB2, + kMCPhiGJB2, + kMCPhiTildeGJB2, kMCCosThetaPP, kMCPhiPP, kMCPhiTildePP, @@ -831,6 +837,12 @@ class VarManager : public TObject kCosThetaCS, kPhiCS, kPhiTildeCS, + kCosThetaGJB1, + kPhiGJB1, + kPhiTildeGJB1, + kCosThetaGJB2, + kPhiGJB2, + kPhiTildeGJB2, kCosThetaPP, kPhiPP, kPhiTildePP, @@ -3889,10 +3901,12 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) // polarization parameters bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - if (useHE || useCS || usePP || useRM) { + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -3948,6 +3962,70 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -4384,10 +4462,12 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) // polarization parameters bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - if (useHE || useCS || usePP || useRM) { + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -4443,6 +4523,70 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -4746,10 +4890,12 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) // polarization parameters bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kMCCosThetaGJB1] || fgUsedVars[kMCPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kMCCosThetaGJB2] || fgUsedVars[kMCPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame - if (useHE || useCS || usePP || useRM) { + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -4805,6 +4951,70 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB1]) { + values[kMCCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB1]) { + values[kMCPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB1]) { + if (fgUsedVars[kMCCosThetaGJB1] && fgUsedVars[kMCPhiGJB1]) { + if (values[kMCCosThetaGJB1] > 0) { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB2]) { + values[kMCCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB2]) { + values[kMCPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB2]) { + if (fgUsedVars[kMCCosThetaGJB2] && fgUsedVars[kMCPhiGJB2]) { + if (values[kMCCosThetaGJB2] > 0) { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{v12.Vect().Unit()}; @@ -7559,10 +7769,12 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) // polarization parameters bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - if (useHE || useCS || usePP || useRM) { + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -7618,6 +7830,70 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index 9e1364dc5c5..84166d64888 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -1160,6 +1160,8 @@ struct AnalysisSameEventPairing { Configurable fConfigPseudoRMQA{"cfgPseudoRMQA", false, "If true, fill QA histograms with pseudo-proper decay length analysis with polarization RM"}; Configurable fConfigTruthPbPbMIDYHE{"cfgTruthPbPbmidyHE", false, "If true, fill QA histograms with dielectron pairs in helicity frame"}; Configurable fConfigTruthPbPbMIDYCS{"cfgTruthPbPbmidyCS", false, "If true, fill QA histograms with dielectron pairs in collion-soper frame"}; + Configurable fConfigTruthPbPbMIDYGJB1{"cfgTruthPbPbmidyGJB1", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 1 (+z)"}; + Configurable fConfigTruthPbPbMIDYGJB2{"cfgTruthPbPbmidyGJB2", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 2 (-z)"}; Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; @@ -1537,6 +1539,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s;", sig->GetName()); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s;", sig->GetName()); + } fHasTwoProngGenMCsignals = true; } // for these pair level signals, also add histograms for each MCgenAcc cut if specified @@ -1560,6 +1568,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s;", sig->GetName(), cut->GetName()); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s;", sig->GetName(), cut->GetName()); + } } } } @@ -2239,6 +2253,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } if (fUseMCGenAccCut) { for (const auto& cut : fMCGenAccCuts) { @@ -2262,6 +2282,12 @@ struct AnalysisSameEventPairing { if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } } } } @@ -3224,14 +3250,14 @@ struct AnalysisDileptonTrackTrack { Produces DileptonTrackTrackTable; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons // cut name setting TString fTrackCutName1; TString fTrackCutName2; std::vector fTrackCutNames; - uint32_t fTrackCutBitMap1; - uint32_t fTrackCutBitMap2; + uint32_t fTrackCutBitMap1 = 0; + uint32_t fTrackCutBitMap2 = 0; bool fIsSameTrackCut = false; AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; @@ -3246,8 +3272,8 @@ struct AnalysisDileptonTrackTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map - float* fValuesQuadruplet; - HistogramManager* fHistMan; + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; // o2::analysis::QuadMlResponse fQuadMlResponse; // std::vector fOutputMlQuad; @@ -3447,7 +3473,7 @@ struct AnalysisDileptonTrackTrack { } uint32_t mcDecision = static_cast(0); - size_t isig = 0; + // size_t isig = 0; for (auto dilepton : dileptons) { // get full track info of tracks based on the index @@ -3528,13 +3554,12 @@ struct AnalysisDileptonTrackTrack { auto track1MC = track1.mcParticle(); auto track2MC = track2.mcParticle(); mcDecision = 0; - isig = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if ((*sig)->CheckSignal(true, lepton1MC, lepton2MC, track1MC, track2MC)) { - mcDecision |= (static_cast(1) << isig); + // size_t isig = 0; + for (size_t iSignal = 0; iSignal < fRecMCSignals.size(); ++iSignal) { + if (fRecMCSignals[iSignal]->CheckSignal(true, lepton1MC, lepton2MC, track1MC, track2MC)) { + mcDecision |= (static_cast(1) << iSignal); } } - int iCut = 0; uint32_t CutDecision = 0; for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { @@ -3825,6 +3850,12 @@ void DefineHistograms(HistogramManager* histMan, const TString& histClasses, con if (classStr.Contains("MCTruthGenPoldielectronPbPbPairCS")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-cs-gen", histName); } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB1")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb1-gen", histName); + } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB2")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb2-gen", histName); + } if (classStr.Contains("MCTruthGenSelBR")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_triple"); } else if (classStr.Contains("MCTruthGen")) { From 5fa37b524cdda93a615a3534eb9eb9de959093b1 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Fri, 23 Oct 2026 10:57:48 +0200 Subject: [PATCH 406/648] [PWGCF] Add pi-d systematic configurable in HadNucleiFemto.cxx (#18414) --- .../DataModel/HadronNucleiTables.h | 4 ++- .../TableProducer/HadNucleiFemto.cxx | 36 +++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h index 250fb7f2308..98b3e7e14dc 100644 --- a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h +++ b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h @@ -183,6 +183,7 @@ DECLARE_SOA_COLUMN(ZVertex, zVertex, float); DECLARE_SOA_COLUMN(Multiplicity, multiplicity, uint16_t); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); +DECLARE_SOA_COLUMN(MultNTracksPV, multNTracksPV, int32_t); } // namespace hadron_nuclei_tables @@ -268,7 +269,8 @@ DECLARE_SOA_TABLE(HadronNucleiMult, "AOD", "HADNUCLEIMULT", hadron_nuclei_tables::ZVertex, hadron_nuclei_tables::Multiplicity, hadron_nuclei_tables::CentFT0C, - hadron_nuclei_tables::MultiplicityFT0C) + hadron_nuclei_tables::MultiplicityFT0C, + hadron_nuclei_tables::MultNTracksPV) // Reduced hadron-hypertriton pair output. Reconstructable quantities such as // k*, invariant mass and pointing variables are intentionally derived offline. diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 44a92a9ff16..6a77e7bd814 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -89,12 +89,11 @@ using std::array; using CollBracket = o2::math_utils::Bracket; using HyperCandidates = aod::DataHypCandsWColl; using HyperCandidatesMC = aod::MCHypCandsWColl; -using CollisionsFull = soa::Join; -// The standard Femto producer stores multNTracksPV as its collision -// multiplicity. Subscribe to PVMults only in the mixed-event process so the -// same vertex--multiplicity pool definition can optionally be reproduced. -using CollisionsFullWithPVMult = soa::Join; -using CollisionsFullMC = soa::Join; +// Save the PV-track multiplicity alongside numContrib in the event-info output +// for both same-event and mixed-event pairs. +using CollisionsFull = soa::Join; +using CollisionsFullWithPVMult = CollisionsFull; +using CollisionsFullMC = soa::Join; using HadHyperCollisionsFull = soa::Join; using HadHyperCollisionsFullMC = soa::Join; using TrackCandidates = soa::Join; @@ -317,6 +316,7 @@ struct BufferedCollision { uint16_t numContrib{0u}; float centFT0C{0.f}; float multFT0C{0.f}; + int32_t multNTracksPV{0}; std::vector nuclei; std::vector hadrons; }; @@ -403,6 +403,12 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"deuteronPid"}; // Deuteron purity and PID cuts + Configurable settingTPCNClsFoundMinDe{"settingTPCNClsFoundMinDe", 110, "Minimum found TPC clusters for deuterons"}; + Configurable settingTPCCrossedRowsMinDe{"settingTPCCrossedRowsMinDe", 100, "Minimum crossed TPC rows for deuterons"}; + Configurable settingDeDCAxyOffset{"settingDeDCAxyOffset", 0.004f, "Deuteron DCAxy cut offset in cm"}; + Configurable settingDeDCAxyPtCoeff{"settingDeDCAxyPtCoeff", 0.013f, "Deuteron DCAxy inverse-pT cut coefficient"}; + Configurable settingDeDCAzOffset{"settingDeDCAzOffset", 0.004f, "Deuteron DCAz cut offset in cm"}; + Configurable settingDeDCAzPtCoeff{"settingDeDCAzPtCoeff", 0.013f, "Deuteron DCAz inverse-pT cut coefficient"}; Configurable settingCutPinMinDe{"settingCutPinMinDe", 0.0f, "Minimum Pin for De"}; Configurable settingCutDeptMin{"settingCutDeptMin", 0.6f, "Minimum PT cut on De"}; Configurable settingCutDeptMax{"settingCutDeptMax", 1.6f, "Maximum PT cut on De"}; @@ -1230,16 +1236,14 @@ struct HadNucleiFemto { return false; } - constexpr int minTPCNClsFound = 110; - constexpr int minTPCNClsCrossedRows = 100; constexpr float minTPCCrossedRowsOverFound = 0.f; constexpr int maxTPCNClsShared = 160; constexpr float maxSharedTPCFraction = 1.f; constexpr int minITSNClsInnerBarrel = 1; const float tpcCrossedRowsOverFound = candidate.tpcNClsFound() > 0 ? static_cast(candidate.tpcNClsCrossedRows()) / candidate.tpcNClsFound() : 0.f; - return !(candidate.tpcNClsFound() < minTPCNClsFound || - candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || + return !(candidate.tpcNClsFound() < deuteronPid.settingTPCNClsFoundMinDe || + candidate.tpcNClsCrossedRows() < deuteronPid.settingTPCCrossedRowsMinDe || tpcCrossedRowsOverFound < minTPCCrossedRowsOverFound || candidate.tpcNClsShared() > maxTPCNClsShared || candidate.tpcFractionSharedCls() > maxSharedTPCFraction || @@ -1840,8 +1844,8 @@ struct HadNucleiFemto { if (absPt <= 0.f) { return false; } - const float deDCAxyMax = 0.004f + 0.013f / absPt; - const float deDCAzMax = 0.004f + 0.013f / absPt; + const float deDCAxyMax = deuteronPid.settingDeDCAxyOffset + deuteronPid.settingDeDCAxyPtCoeff / absPt; + const float deDCAzMax = deuteronPid.settingDeDCAzOffset + deuteronPid.settingDeDCAzPtCoeff / absPt; if (std::abs(candidate.dcaXY()) > deDCAxyMax || std::abs(candidate.dcaZ()) > deDCAzMax) { return false; } @@ -2645,7 +2649,8 @@ struct HadNucleiFemto { collision.posZ(), collision.numContrib(), collision.centFT0C(), - collision.multFT0C()); + collision.multFT0C(), + collision.multNTracksPV()); } } @@ -2658,7 +2663,8 @@ struct HadNucleiFemto { collision.posZ, collision.numContrib, collision.centFT0C, - collision.multFT0C); + collision.multFT0C, + collision.multNTracksPV); } } @@ -3371,6 +3377,7 @@ struct HadNucleiFemto { currentCollision.numContrib = collision.numContrib(); currentCollision.centFT0C = collision.centFT0C(); currentCollision.multFT0C = collision.multFT0C(); + currentCollision.multNTracksPV = collision.multNTracksPV(); const uint64_t collIdx = collision.globalIndex(); auto tracksThisCollision = tracks.sliceBy(mPerCol, collIdx); @@ -3548,6 +3555,7 @@ struct HadNucleiFemto { current.numContrib = collision.numContrib(); current.centFT0C = collision.centFT0C(); current.multFT0C = collision.multFT0C(); + current.multNTracksPV = collision.multNTracksPV(); row.eventId = current.eventId; auto tracksThisCollision = tracks.sliceBy(mPerCol, collision.globalIndex()); tracksThisCollision.bindExternalIndices(&tracks); From a0332c7e4caf45e83c397684264e276688d37811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:45:06 +0200 Subject: [PATCH 407/648] [PWGJE] Jet finding: Pass input particles as const (#18424) --- PWGJE/Core/JetFinder.cxx | 2 +- PWGJE/Core/JetFinder.h | 4 ++-- PWGJE/Core/JetFindingUtilities.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGJE/Core/JetFinder.cxx b/PWGJE/Core/JetFinder.cxx index 0434e5cfa4a..075c92bbbfe 100644 --- a/PWGJE/Core/JetFinder.cxx +++ b/PWGJE/Core/JetFinder.cxx @@ -60,7 +60,7 @@ void JetFinder::setParams() /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents -fastjet::ClusterSequenceArea JetFinder::findJets(std::vector& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reeference +fastjet::ClusterSequenceArea JetFinder::findJets(std::vector const& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reference { setParams(); jets.clear(); diff --git a/PWGJE/Core/JetFinder.h b/PWGJE/Core/JetFinder.h index 90b9a981f6a..7f281d3de03 100644 --- a/PWGJE/Core/JetFinder.h +++ b/PWGJE/Core/JetFinder.h @@ -46,7 +46,7 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - // fastjet::ClusterSequenceArea findJets(std::vector &inputParticles, std::vector &jets); + // fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); float phiMin = -1. * M_PI; float phiMax = 2. * M_PI; @@ -92,7 +92,7 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - fastjet::ClusterSequenceArea findJets(std::vector& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reeference + fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reference private: ClassDefNV(JetFinder, 1); diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index bf5698e7d22..d770380e9e3 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -284,7 +284,7 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f * @param doHFJetFinding set whether only jets containing a HF candidate are saved */ template -void findJets(JetFinder& jetFinder, std::vector& inputParticles, float jetPtMin, float jetPtMax, std::vector jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) +void findJets(JetFinder& jetFinder, std::vector const& inputParticles, float jetPtMin, float jetPtMax, std::vector jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) { auto jetRValues = static_cast>(std::move(jetRadius)); jetFinder.jetPtMin = jetPtMin; From 0ffbcbb19784f60b45e3ef9ed3db00a599a9b198 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:55:34 +0200 Subject: [PATCH 408/648] [PWGCF] Add stride to event mixing in femto framework (#18430) --- PWGCF/Femto/Core/pairBuilder.h | 194 +++++++++++++---------- PWGCF/Femto/Core/pairHistManager.h | 1 + PWGCF/Femto/Core/pairProcessHelpers.h | 66 ++++++-- PWGCF/Femto/Core/tripletBuilder.h | 98 ++++++------ PWGCF/Femto/Core/tripletHistManager.h | 1 + PWGCF/Femto/Core/tripletProcessHelpers.h | 66 ++++++-- 6 files changed, 278 insertions(+), 148 deletions(-) diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 9ad834301e7..952c9734a97 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -133,6 +133,7 @@ class PairTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -213,17 +214,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -234,17 +235,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -261,17 +262,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -282,17 +283,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -317,6 +318,7 @@ class PairTrackTrackBuilder particlecleaner::ParticleCleaner mTrackCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; @@ -436,6 +438,7 @@ class PairV0V0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary // mDist is bounded at declaration, so it stays valid even if this branch is not taken @@ -515,13 +518,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -529,13 +532,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -549,13 +552,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -563,13 +566,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -592,6 +595,7 @@ class PairV0V0Builder pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), @@ -724,6 +728,7 @@ class PairD0D0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary // mDist is bounded at declaration, so it stays valid even if this branch is not taken if (confMixing.seed.value >= 0) { @@ -800,13 +805,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -814,13 +819,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -834,13 +839,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -848,13 +853,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -877,6 +882,7 @@ class PairD0D0Builder pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), @@ -972,6 +978,7 @@ class PairTrackD0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -993,13 +1000,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1025,13 +1032,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1052,6 +1059,7 @@ class PairTrackD0Builder paircleaner::TrackV0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1151,13 +1160,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1183,13 +1192,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1210,6 +1219,7 @@ class PairTrackLcBuilder paircleaner::TrackLcPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1338,13 +1349,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1356,13 +1367,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1383,6 +1394,7 @@ class PairTrackV0Builder paircleaner::TrackV0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; // Track-TwoPhotonResonance pair builder. @@ -1460,6 +1472,7 @@ class PairTrackTwoPhotonResonanceBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1481,13 +1494,13 @@ class PairTrackTwoPhotonResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1506,6 +1519,7 @@ class PairTrackTwoPhotonResonanceBuilder paircleaner::TrackTrackPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1598,13 +1613,13 @@ class PairTrackTwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1623,6 +1638,7 @@ class PairTrackTwoTrackResonanceBuilder paircleaner::TrackV0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1728,13 +1745,13 @@ class PairV0TwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1753,6 +1770,7 @@ class PairV0TwoTrackResonanceBuilder paircleaner::V0V0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1863,13 +1882,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1881,13 +1900,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1908,6 +1927,7 @@ class PairTrackKinkBuilder paircleaner::TrackKinkPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template (confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -2030,13 +2051,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2048,13 +2069,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2075,6 +2096,7 @@ class PairTrackCascadeBuilder paircleaner::TrackCascadePairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; template @@ -2162,6 +2184,7 @@ class PairMcParticleMcParticleBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -2211,13 +2234,13 @@ class PairMcParticleMcParticleBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2225,13 +2248,13 @@ class PairMcParticleMcParticleBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2253,6 +2276,7 @@ class PairMcParticleMcParticleBuilder particlecleaner::ParticleCleaner mMcParticleCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index 22e018644ff..0d28ce5b99e 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -195,6 +195,7 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis eventPlaneAngle{"eventPlaneAngle", {10, 0.f, 1.f * o2::constants::math::TwoPI}, "Mixing bins - event plane angle"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: multiplicity and centrality, -> 3: centrality and event plane angle"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable sameSpecies{"sameSpecies", false, "Enable if particle 1 and particle 2 are the same"}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1 and particle 2 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable enablePairCorrelationQa{"enablePairCorrelationQa", true, "Enable pair-level correlation QA (same-event + mixed-event)"}; diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 54323079df3..64a4c510b27 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -439,11 +439,16 @@ void processMixedEvent(T1 const& Collisions, T7 const& depth, T8& PairHistManager, T9& CprManager, - T10& PcManager) + T10& PcManager, + int eventStride = 1) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 is fixed across each mixing window, so its track slice is // materialized once per window and reused for every mixing partner, instead @@ -455,13 +460,24 @@ void processMixedEvent(T1 const& Collisions, // --- new window --- if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; @@ -551,11 +567,16 @@ void processMixedEvent(T1 const& Collisions, T13& ParticleCleaner1, T14& ParticleCleaner2, T15& CprManager, - T16& PcManager) + T16& PcManager, + int eventStride = 1) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 is fixed across each mixing window, so its track slice is // materialized once per window and reused for every mixing partner, instead @@ -565,13 +586,24 @@ void processMixedEvent(T1 const& Collisions, for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; @@ -661,24 +693,40 @@ void processMixedEvent(T1 const& Collisions, T11& ParticleCleaner1, T12& ParticleCleaner2, T13& CprManager, - T14& PcManager) + T14& PcManager, + int eventStride = 1) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { if (collision1.globalIndex() != lastCollisionIndex) { - if (lastCollisionIndex != -1) { + if (lastCollisionIndex != -1 && !skipThisWindow) { PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex = collision1.globalIndex(); - sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } ++windowSizeRaw; diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index ec68bd83de1..1d6fbe76e9d 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -165,6 +165,7 @@ class TripletTrackTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -279,13 +280,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -293,13 +294,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -307,13 +308,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -328,13 +329,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -342,13 +343,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -356,13 +357,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -389,6 +390,7 @@ class TripletTrackTrackTrackBuilder bool mTrack1Track2Track3AreSameSpecies = false; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -503,6 +505,7 @@ class TripletTrackTrackV0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -587,13 +590,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -601,13 +604,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -622,13 +625,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -636,13 +639,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -668,6 +671,7 @@ class TripletTrackTrackV0Builder triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -794,6 +798,7 @@ class TripletTrackTrackCascadeBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { uint64_t randomSeed = 0; @@ -877,13 +882,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -891,13 +896,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -912,13 +917,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -926,13 +931,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -958,6 +963,7 @@ class TripletTrackTrackCascadeBuilder triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; @@ -1054,6 +1060,7 @@ class TripletMcParticleMcParticleMcParticleBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -1121,13 +1128,13 @@ class TripletMcParticleMcParticleMcParticleBuilder switch (mMixingPolicy) { case triplethistmanager::MixingPolicy::kVtxMult: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); break; case triplethistmanager::MixingPolicy::kVtxCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); break; case triplethistmanager::MixingPolicy::kVtxMultCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe); + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1150,6 +1157,7 @@ class TripletMcParticleMcParticleMcParticleBuilder particlecleaner::ParticleCleaner mMcParticleCleaner3; triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mParticle1Particle2Particle3AreSameSpecies = false; bool mParticle1Particle2AreSameSpecies = false; bool mMixIdenticalParticles = false; diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index 7743971f40f..1bc69b06eeb 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -111,6 +111,7 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis vtxBins{"vtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: both"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1/2/3 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable particle123AreSameSpecies{"particle123AreSameSpecies", false, "Particle 1,2 and 3 are of the same species"}; o2::framework::Configurable particle12AreSameSpecies{"particle12AreSameSpecies", false, "Particle 1 and 2 are of the same species"}; diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index a5d7b812305..07f74758d1c 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -765,12 +765,17 @@ void processMixedEvent(T1 const& Collisions, T8 const& depth, T9& TripletHistManager, T10& CtrManager, - T11& TcManager) + T11& TcManager, + int eventStride = 1) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy @@ -781,14 +786,25 @@ void processMixedEvent(T1 const& Collisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window @@ -892,12 +908,17 @@ void processMixedEvent(T1 const& Collisions, T15& Cleaner2, T16& Cleaner3, T17& CtrManager, - T18& TcManager) + T18& TcManager, + int eventStride = 1) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy @@ -908,14 +929,25 @@ void processMixedEvent(T1 const& Collisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window @@ -1009,12 +1041,17 @@ void processMixedEventMcTruth(T1 const& McCollisions, T13& ParticleCleaner2, T14& ParticleCleaner3, T15& CtrManager, - T16& TcManager) + T16& TcManager, + int eventStride = 1) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; int windowSizeRaw = 0; int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle2; @@ -1023,14 +1060,25 @@ void processMixedEventMcTruth(T1 const& McCollisions, // outer window if (collision1.globalIndex() != lastCollisionIndex1) { - if (lastCollisionIndex1 != -1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } windowSizeRaw = 0; windowSizeEffective = 0; lastCollisionIndex1 = collision1.globalIndex(); lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below - sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; } // inner sub-window From 9c5b19b14fa423d83b37c7a0fdd249a48e5f2f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:46:14 +0200 Subject: [PATCH 409/648] [PWGCF] TwoParticleCorrelations: Fix abs bugs (#18420) --- .../Core/EventSelectionFilterAndAnalysis.h | 2 +- PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index a746cb96248..28ca101cbf4 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -142,7 +142,7 @@ inline void EventSelectionFilterAndAnalysis::StoreMultiplicities(CollisionToFilt /* we require tracks with pT > 0.15 and |eta| < 0.8 and additionally the TPCout flag */ /* for the time being there is not TPCout flag, we just consider hasTPC */ for (auto track : tracks) { - if (track.pt() > 0.15 and abs(track.eta()) < 0.8 and track.hasTPC()) { + if (track.pt() > 0.15 and std::abs(track.eta()) < 0.8 and track.hasTPC()) { ntracksTPCout++; } } diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 05a4c32be31..aa79964d65a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -548,8 +548,8 @@ class TrackSelectionBrick : public SpecialCutBrick ((mRequireTPCRefit) ? (track.flags() & o2::aod::track::TPCrefit) : true) && ((mRequireGoldenChi2) ? (track.flags() & o2::aod::track::GoldenChi2) : true) && FulfillsITSHitRequirements(track.itsClusterMap()) && - (mCheckMaxDcaXY && (abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && - (mCheckMaxDcaZ && (abs(track.dcaZ()) <= mMaxDcaZ))) { + (mCheckMaxDcaXY && (std::abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && + (mCheckMaxDcaZ && (std::abs(track.dcaZ()) <= mMaxDcaZ))) { this->mState = this->kACTIVE; return true; } else { From df37dd2561bb625a7fffb2e8a4596cc489a08c89 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Fri, 23 Oct 2026 14:48:32 +0200 Subject: [PATCH 410/648] [PWGHF] Fix position of continue in Xic+ tree creator in case of fill gen acc info (#18401) Co-authored-by: ALICE Action Bot --- .../TableProducer/treeCreatorXicToXiPiPi.cxx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index 79eb3e81c13..849b8d93f96 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -31,10 +31,13 @@ #include #include +#include + #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -881,10 +884,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } if (fillGenInfoForAcceptance) { - if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } rowCandidateFullParticles( @@ -953,10 +956,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } if (fillGenInfoForAcceptance) { - if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } rowCandidateFullParticles( @@ -1017,10 +1020,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } if (fillGenInfoForAcceptance) { - if (!TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(particle.flagMcMatchGen(), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } From a83f0dedae4e465d6fe57352fae743f8856a89cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:05:18 +0200 Subject: [PATCH 411/648] [ALICE3] Remove HfHelper instances (#18433) --- ALICE3/TableProducer/alice3-correlatorDDbar.cxx | 2 -- ALICE3/TableProducer/alice3HfSelector3Prong.cxx | 3 +-- .../TableProducer/alice3HfTreeCreator3Prong.cxx | 6 ++---- ALICE3/Tasks/alice3HfTask3Prong.cxx | 17 ++++++++--------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx index 5603fb97348..bb1b54bd03e 100644 --- a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx +++ b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx @@ -89,8 +89,6 @@ struct alice3correlatorddbar { Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots and efficiency"}; Configurable> efficiencyD{"efficiencyD", std::vector{efficiencyDmeson_v}, "Efficiency values for D0 meson"}; - // HfHelper hfHelper; //not needed for now - Partition> selectedCandidates = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); Partition> selectedCandidatesMC = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); diff --git a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx index 3669ffd834d..f502842ef02 100644 --- a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx @@ -93,7 +93,6 @@ struct Alice3HfSelector3Prong { Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - HfHelperAlice3 hfHelper; o2::analysis::HfMlResponse3Prong mlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -236,7 +235,7 @@ struct Alice3HfSelector3Prong { template bool selectionCandidateMass(int const ptBin, const TCandidate& cand) { - const float massCand = hfHelper.getCandMass(cand); + const float massCand = HfHelperAlice3::getCandMass(cand); // cut on mass window if (std::abs(massCand - MassReference) > cuts->get(ptBin, "m")) { return false; diff --git a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx index 7a97f1420ff..bc456974afb 100644 --- a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx @@ -272,8 +272,6 @@ struct Alice3HfTreeCreator3Prong { Configurable downSampleFactor{"downSampleFactor", 1., "Fraction of cands to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - HfHelperAlice3 hfHelper; - using CandsLcRec = soa::Filtered>; using CandsLcRecWMl = soa::Filtered>; using CandsMcGen = soa::Join; @@ -399,7 +397,7 @@ struct Alice3HfTreeCreator3Prong { rowCandKine3Ps( cand.ptProng0(), cand.ptProng1(), cand.ptProng2(), cand.pt(), RecoDecay::p(cand.px(), cand.py(), cand.pz()), cand.eta(), cand.phi(), - hfHelper.getCandMass(cand), hfHelper.getCandY(cand), hfHelper.getCandEnergy(cand)); + HfHelperAlice3::getCandMass(cand), HfHelperAlice3::getCandY(cand), HfHelperAlice3::getCandEnergy(cand)); } if (fillTables.fillDaugKineInfo) { rowDaugMoms( @@ -442,7 +440,7 @@ struct Alice3HfTreeCreator3Prong { { for (const auto& part : parts) { if (fillTables.fillCandGenKineInfo) { - rowCand3PGen(part.pt(), part.eta(), part.phi(), hfHelper.getCandY(part)); + rowCand3PGen(part.pt(), part.eta(), part.phi(), HfHelperAlice3::getCandY(part)); } if (fillTables.fillMcMatchGenInfo) { rowCand3PMcMatchGen(part.flagMcGen(), part.originMcGen()); diff --git a/ALICE3/Tasks/alice3HfTask3Prong.cxx b/ALICE3/Tasks/alice3HfTask3Prong.cxx index 823d054395a..8901ae6e8a9 100644 --- a/ALICE3/Tasks/alice3HfTask3Prong.cxx +++ b/ALICE3/Tasks/alice3HfTask3Prong.cxx @@ -58,7 +58,6 @@ struct Alice3HfTask3Prong { Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - HfHelperAlice3 hfHelper; SliceCache cache; Service ccdb; @@ -271,7 +270,7 @@ struct Alice3HfTask3Prong { for (const auto& candidate : candidates) { registry.fill(HIST("MC/rec/hCandidateCounter"), 1.); /// rapidity selection - if (yCandRecoMax >= 0. && std::abs(hfHelper.getCandY(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelperAlice3::getCandY(candidate)) > yCandRecoMax) { continue; } registry.fill(HIST("MC/rec/hCandidateCounter"), 2.); @@ -311,7 +310,7 @@ struct Alice3HfTask3Prong { if (candidate.isSelMassHypo0()) { registry.fill(HIST("hSelectionStatus"), 0., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, false); if (originType == RecoDecay::OriginType::Prompt) { @@ -334,7 +333,7 @@ struct Alice3HfTask3Prong { if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { registry.fill(HIST("hSelectionStatus"), 1., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, true); if (originType == RecoDecay::OriginType::Prompt) { @@ -357,12 +356,12 @@ struct Alice3HfTask3Prong { } } else { // Background if (candidate.isSelMassHypo0()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, false); } if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, true); } } @@ -385,10 +384,10 @@ struct Alice3HfTask3Prong { static constexpr auto histoSuffix = HIST("Gen") + HIST(SignalSuffixes[SignalType]); registry.fill(histoPrefix + HIST("hPt") + histoSuffix, particle.pt()); registry.fill(histoPrefix + HIST("hEta") + histoSuffix, particle.eta()); - registry.fill(histoPrefix + HIST("hY") + histoSuffix, hfHelper.getCandY(particle)); + registry.fill(histoPrefix + HIST("hY") + histoSuffix, HfHelperAlice3::getCandY(particle)); registry.fill(histoPrefix + HIST("hPhi") + histoSuffix, particle.phi()); registry.fill(histoPrefix + HIST("hEtaVsPt") + histoSuffix, particle.eta(), particle.pt()); - registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, hfHelper.getCandY(particle), particle.pt()); + registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, HfHelperAlice3::getCandY(particle), particle.pt()); registry.fill(histoPrefix + HIST("hPhiVsPt") + histoSuffix, particle.phi(), particle.pt()); if (particle.has_daughters()) { const auto firstDauIdx = particle.daughtersIds().front(); @@ -424,7 +423,7 @@ struct Alice3HfTask3Prong { // MC gen. for (const auto& particle : mcParticles) { if (std::abs(particle.flagMcGen()) == selectedPdg) { - double yGen = hfHelper.getCandY(particle); + double yGen = HfHelperAlice3::getCandY(particle); if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { continue; } From c0083b264d92f91ce6302fa286c7a4d3661d207e Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Fri, 23 Oct 2026 15:58:27 +0200 Subject: [PATCH 412/648] [PWGLF] Nuclei hadron correlation: fill close pair rejection histogram for survivors (#18410) --- PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index d66da98c8ae..71d3dcd51ce 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -674,14 +674,13 @@ struct HadronNucleiCorrelation { pair->SetPair(&part0, &part1); pair->SetIdentical(isIdentical); if (isIdentical && pair->IsClosePair(dEta, dPhi, radiusTPC)) { - registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); return; } if (doClosePairRejection && pair->IsClosePair(dEta, dPhi, radiusTPC)) { - registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); return; } + registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); const float deltaEta = part0.eta() - part1.eta(); const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); From f51af835180026c17bb19497e72535c5c4fb47b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zuzanna=20Chochulska-Gumi=C5=84ska?= <87480906+zchochul@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:16:23 +0200 Subject: [PATCH 413/648] [PWGCF] FemtoUniverse -- invariant mass for all phi daughters candidates (#18441) Co-authored-by: Zuzanna Anna Chochulska Co-authored-by: Zuzanna Anna Chochulska Co-authored-by: ALICE Action Bot --- .../Tasks/femtoUniversePairTaskTrackPhi.cxx | 66 +++++++++++-------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index aa6e8bb7cc8..03c24e77153 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -383,6 +384,7 @@ struct FemtoUniversePairTaskTrackPhi { registryPhiMinvBackground.add("InvariantMassKpKp", "; invariant mass K+K+; Counts", kTH1F, {{6000, 0.9, 4.0}}); registryPhiMinvBackground.add("InvariantMassKmKm", "; invariant mass K-K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); + registryPhiMinvBackground.add("InvariantMassKpKm", "; invariant mass K+K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); qaRegistry.add("Hadron_pos/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("Hadron_pos/nSigmaTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); @@ -405,12 +407,12 @@ struct FemtoUniversePairTaskTrackPhi { registryMCtruth.add("MCtruthKp", "MC truth K+;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKpPt", "MC truth kaons positive;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // K- - registryMCtruth.add("MCtruthKm", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); + registryMCtruth.add("MCtruthKm", "MC truth kaons negative;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKmPt", "MC truth kaons negative;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // p registryMCtruth.add("MCtruthPpos", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPposPt", "MC truth protons;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); - // pbar + // p- registryMCtruth.add("MCtruthPneg", "MC truth antiprotons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPnegPt", "MC truth antiproton;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // phi @@ -423,7 +425,7 @@ struct FemtoUniversePairTaskTrackPhi { // p registryMCreco.add("MCrecoPpos", "MC reco proton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPposPt", "MC reco proton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); - // pbar + // p- registryMCreco.add("MCrecoPneg", "MC reco antiproton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPnegPt", "MC reco antiproton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); // phi @@ -549,31 +551,39 @@ struct FemtoUniversePairTaskTrackPhi { sameEventCont.setPair(track, phicandidate, multCol, confUse3D, mWeight); } - // // Used for better fitting of invariant mass background. - - // TLorentzVector part1Vec; - // TLorentzVector part2Vec; - - // float mMassOne = o2::constants::physics::MassKPlus; - // float mMassTwo = o2::constants::physics::MassKMinus; - - // for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsKaons, groupPartsKaons))) { - // if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); - // } - // if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); - - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); - // } - // } + // Used for better fitting of invariant mass background. + + TLorentzVector part1Vec; + TLorentzVector part2Vec; + + float mMassOne = o2::constants::physics::MassKPlus; + float mMassTwo = o2::constants::physics::MassKMinus; + + for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { + if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); + } + if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); + } + if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKm"), sumVec.M()); + } + } } template From eb0871d37b14b5d77b8f0999593b2ec4353ce1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:41:46 +0200 Subject: [PATCH 414/648] [PWGLF] Utils: Compile ResonanceMlResponse (#18419) --- PWGLF/Utils/CMakeLists.txt | 5 +++-- ...adeMlResponse.cxx => CascadeMlResponse.cxx} | 2 +- PWGLF/Utils/ResonanceMlResponse.cxx | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) rename PWGLF/Utils/{cascadeMlResponse.cxx => CascadeMlResponse.cxx} (96%) create mode 100644 PWGLF/Utils/ResonanceMlResponse.cxx diff --git a/PWGLF/Utils/CMakeLists.txt b/PWGLF/Utils/CMakeLists.txt index afa87d8a86c..c0996bebe49 100644 --- a/PWGLF/Utils/CMakeLists.txt +++ b/PWGLF/Utils/CMakeLists.txt @@ -37,8 +37,9 @@ o2physics_add_library(LfStrangenessBuilderModule SOURCES strangenessBuilderModule.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) -o2physics_add_library(LfCascadeMlResponse - SOURCES cascadeMlResponse.cxx +o2physics_add_library(LfMlResponse + SOURCES CascadeMlResponse.cxx + ResonanceMlResponse.cxx PUBLIC_LINK_LIBRARIES O2Physics::MLCore) # Precompiled header shared across the PWGLF strangeness workflows. Percentages diff --git a/PWGLF/Utils/cascadeMlResponse.cxx b/PWGLF/Utils/CascadeMlResponse.cxx similarity index 96% rename from PWGLF/Utils/cascadeMlResponse.cxx rename to PWGLF/Utils/CascadeMlResponse.cxx index 43cb3236f9b..3be1d105e50 100644 --- a/PWGLF/Utils/cascadeMlResponse.cxx +++ b/PWGLF/Utils/CascadeMlResponse.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file cascadeMlResponse.cxx +/// \file CascadeMlResponse.cxx /// \brief Helper file providing the compilation command for the CascadeMlResponse header. /// /// \author ALICE Collaboration diff --git a/PWGLF/Utils/ResonanceMlResponse.cxx b/PWGLF/Utils/ResonanceMlResponse.cxx new file mode 100644 index 00000000000..71dc6dc9d7b --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.cxx +/// \brief Helper file providing the compilation command for the ResonanceMlResponse header. +/// +/// \author ALICE Collaboration + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "ResonanceMlResponse.h" // IWYU pragma: keep From ea89115007b35d183625ba2df0aedf40f43d8041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:44:41 +0200 Subject: [PATCH 415/648] [Tutorial] Remove HfHelper instances (#18438) --- Tutorials/ML/applyMlSelection.cxx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Tutorials/ML/applyMlSelection.cxx b/Tutorials/ML/applyMlSelection.cxx index 2bee237ab3e..c2dc1662690 100644 --- a/Tutorials/ML/applyMlSelection.cxx +++ b/Tutorials/ML/applyMlSelection.cxx @@ -66,7 +66,6 @@ struct applyMlSelection { Filter filterDsFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_3prong::DecayType::DsToKKPi))) != static_cast(0); - HfHelper hfHelper; o2::ccdb::CcdbApi ccdbApi; int nCandidates = 0; @@ -108,22 +107,22 @@ struct applyMlSelection { continue; } // Check that the candidate rapidity is within the analysis range - if (yCandRecoMax >= 0. && std::abs(hfHelper.yDs(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yDs(candidate)) > yCandRecoMax) { continue; } // Fill mass histogram before ML selections - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToPiKK(candidate)); - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToKKPi(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToPiKK(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToKKPi(candidate)); // Perform ML selections for one mass hypothesis (Ds -> PhiPi -> PiKK) std::vector inputFeaturesPiKK{candidate.cpa(), candidate.cpaXY(), candidate.decayLength(), candidate.decayLengthXY(), - static_cast(hfHelper.deltaMassPhiDsToPiKK(candidate)), + static_cast(HfHelper::deltaMassPhiDsToPiKK(candidate)), candidate.impactParameterXY(), - static_cast(hfHelper.cos3PiKDsToPiKK(candidate)), + static_cast(HfHelper::cos3PiKDsToPiKK(candidate)), candidate.maxNormalisedDeltaIP()}; // Retrieve model output and selection outcome From 8b3d0e0ef6ad43411cc20b3284ba7b601e625f10 Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:37:46 +0200 Subject: [PATCH 416/648] [PWGLF] Add more QA hist in hStrangeCorrelation.cxx (#18444) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 140 ++++++++++++++++-- 1 file changed, 126 insertions(+), 14 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 68f5053ccaf..21e750cb2b9 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -642,6 +642,34 @@ struct HStrangeCorrelation { "Not in table, no cut explains it", "In table, but replica would reject"}; + // Breakdown of the Rec variant 0 -> 1 step of PairLossK0/Comparison ("+ truth-trigger + // match/acceptance"). Every Rec trigger is counted in the All bin and in exactly one + // other bin: Matched if it passes variant 1, otherwise the first condition it fails. + // The order follows the variant 1 lookup -- MC label, MC collision of the label, then + // the truth-trigger acceptance in the order pairLossTruthTriggerRejection checks it -- + // so All = Matched + the sum of the rejection bins. + enum PairLossTriggerMatchQABin : int { + PairLossTriggerMatchAll = 0, + PairLossTriggerMatchMatched, + PairLossTriggerMatchNoMcLabel, + PairLossTriggerMatchOtherMcCollision, + PairLossTriggerMatchFailPdg, + PairLossTriggerMatchFailEta, + PairLossTriggerMatchFailPt, + PairLossTriggerMatchFailCharge, + PairLossTriggerMatchNBins + }; + + static constexpr std::array PairLossTriggerMatchQANames = { + "All Rec (variant 0)", + "Matched (variant 1)", + "No MC label", + "Label in another MC collision", + "Truth PDG not pi/K/p/e/mu", + "|eta truth| > etaSel", + "pT truth outside trigger axis", + "Truth charge"}; + struct PairLossV0Info { int64_t globalIndex = -1; int64_t positiveTrackId = -1; @@ -738,6 +766,9 @@ struct HStrangeCorrelation { std::unordered_set finalTruthPairs; std::unordered_set countedPairs; std::unordered_set countedTriggers; + // PairLossTriggerMatchQABin of the MC particle behind every labelled trigger + // track, keyed by MC particle. Only particles of this MC collision are present. + std::unordered_map triggerTruthRejection; void clear() { @@ -757,6 +788,7 @@ struct HStrangeCorrelation { finalTruthPairs.clear(); countedPairs.clear(); countedTriggers.clear(); + triggerTruthRejection.clear(); } }; static thread_local PairLossComparisonContext pairLossComparison; @@ -836,6 +868,48 @@ struct HStrangeCorrelation { return absPdg == PDG_t::kPiPlus || absPdg == PDG_t::kKPlus || absPdg == PDG_t::kProton || absPdg == PDG_t::kElectron || absPdg == PDG_t::kMuonMinus; } + // Truth-trigger acceptance of the PairLoss Rec comparison (comparisonTruthTriggers): + // species, |eta|, pT and charge, checked in this order. Returns + // PairLossTriggerMatchMatched if the generated particle passes all of them, otherwise + // the PairLossTriggerMatchQABin of the first one it fails. sign is set to the charge + // sign once species, |eta| and pT have passed, and to 0 before. + template + int pairLossTruthTriggerRejection(TMcParticle const& mcParticle, int& sign) + { + sign = 0; + if (!isPairLossTriggerPdg(mcParticle.pdgCode())) { + return PairLossTriggerMatchFailPdg; + } + if (std::abs(mcParticle.eta()) > etaSel) { + return PairLossTriggerMatchFailEta; + } + if (mcParticle.pt() < axisRanges[3][0] || mcParticle.pt() > axisRanges[3][1]) { + return PairLossTriggerMatchFailPt; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if (sign == 0 || (triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0)) { + return PairLossTriggerMatchFailCharge; + } + return PairLossTriggerMatchMatched; + } + + // PairLossTriggerMatchQABin of a Rec trigger track while the PairLoss Rec comparison + // runs: the two lookups of Rec variant 1, resolved into the first one that fails + static int pairLossTriggerMatchBin(int64_t trackGlobalIndex) + { + const auto mcIt = pairLossComparison.trackToMc.find(trackGlobalIndex); + if (mcIt == pairLossComparison.trackToMc.end()) { + return PairLossTriggerMatchNoMcLabel; + } + const auto rejectionIt = pairLossComparison.triggerTruthRejection.find(mcIt->second); + if (rejectionIt == pairLossComparison.triggerTruthRejection.end()) { + return PairLossTriggerMatchOtherMcCollision; + } + return rejectionIt->second; + } + template PairLossTrackInfo makePairLossTrackInfo(TTrack const& track) const { @@ -1376,6 +1450,9 @@ struct HStrangeCorrelation { efficiencyTriggError = 0; } } + // PairLossTriggerMatchQABin of this trigger, set only while the PairLoss Rec + // comparison fills its variants, and reused for every pair of the trigger + int pairLossTriggerMatch = PairLossTriggerMatchAll; if (!mixing) { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesV0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); @@ -1394,6 +1471,9 @@ struct HStrangeCorrelation { histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), variant, fillPtTrigger, fillVtxZ, fillMult, weight); }; fillTriggerVariant(0, trigg.pt(), pvz, mult, triggerWeight); + pairLossTriggerMatch = pairLossTriggerMatchBin(trigg.globalIndex()); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), PairLossTriggerMatchAll, trigg.pt(), triggerWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), pairLossTriggerMatch, trigg.pt(), triggerWeight); const auto triggerLadderMcIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); if (triggerLadderMcIt != pairLossComparison.trackToMc.end()) { const auto triggerLadderTruthIt = pairLossComparison.truthTriggers.find(triggerLadderMcIt->second); @@ -1654,6 +1734,8 @@ struct HStrangeCorrelation { // 0: the exact Rec entry, including reconstructed coordinates // and the ordinary Rec weight. fillWeightedRecVariant(0, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), PairLossTriggerMatchAll, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), pairLossTriggerMatch, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); const auto triggerMcIdIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); if (triggerMcIdIt != pairLossComparison.trackToMc.end()) { @@ -2972,6 +3054,21 @@ struct HStrangeCorrelation { for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); } + + // Where the Rec triggers lost at the variant 0 -> 1 step fail, per trigger and per + // pair, with the weights and reconstructed coordinates of the variants: the All and + // Matched bins of hTrigger integrate to TriggerVariants 0 and 1, those of hPair to + // RecVariants 0 and 1. hPair keeps the angular axes so that the shape of the lost + // pairs (correlated or flat) is visible. + const AxisSpec axisPairLossTriggerMatchQA{PairLossTriggerMatchNBins, -0.5, static_cast(PairLossTriggerMatchNBins) - 0.5, "Rec variant 0 #rightarrow 1 trigger match"}; + histos.add("PairLossK0/Comparison/TriggerMatchQA/hTrigger", "Rec triggers by first failing truth-trigger condition;Rec variant 0 #rightarrow 1 trigger match;#it{p}_{T}^{trigger, reco} (GeV/#it{c})", kTH2D, {axisPairLossTriggerMatchQA, axesConfigurations.axisPtQA}); + histos.add("PairLossK0/Comparison/TriggerMatchQA/hPair", "Rec peak-window h-K0 pairs by first failing truth-trigger condition of their trigger", kTHnF, {axisPairLossTriggerMatchQA, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim}); + auto triggerMatchQATrigger = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger")); + auto triggerMatchQAPair = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair")); + for (int i = 0; i < PairLossTriggerMatchNBins; ++i) { + triggerMatchQATrigger->GetXaxis()->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + triggerMatchQAPair->GetAxis(0)->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + } } } @@ -4965,20 +5062,16 @@ struct HStrangeCorrelation { std::vector comparisonTruthTriggers; std::vector comparisonTruthK0s; for (auto const& mcParticle : mcParticles) { - if (isPairLossTriggerPdg(mcParticle.pdgCode()) && std::abs(mcParticle.eta()) <= etaSel && - mcParticle.pt() >= axisRanges[3][0] && mcParticle.pt() <= axisRanges[3][1]) { - auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); - const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; - const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); - if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { - comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ - .globalIndex = mcParticle.globalIndex(), - .pt = mcParticle.pt(), - .eta = mcParticle.eta(), - .phi = mcParticle.phi(), - .sign = sign, - .physicalPrimary = mcParticle.isPhysicalPrimary()}); - } + // same acceptance as the TriggerMatchQA rejection bins + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched) { + comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .sign = sign, + .physicalPrimary = mcParticle.isPhysicalPrimary()}); } if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || @@ -5323,6 +5416,25 @@ struct HStrangeCorrelation { } } } + // Truth-trigger rejection bin of the MC particle behind every labelled trigger + // track, for PairLossK0/Comparison/TriggerMatchQA. mcParticles holds only the + // particles of this MC collision, so a label missing from the map points to + // another MC collision. + std::unordered_set triggerLabelIds; + for (auto const& collision : recCollisions) { + for (auto const& triggerEntry : triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex())) { + const auto triggerMcIt = pairLossComparison.trackToMc.find(triggerEntry.trackId()); + if (triggerMcIt != pairLossComparison.trackToMc.end()) { + triggerLabelIds.insert(triggerMcIt->second); + } + } + } + for (auto const& mcParticle : mcParticles) { + if (triggerLabelIds.contains(mcParticle.globalIndex())) { + int sign = 0; + pairLossComparison.triggerTruthRejection[mcParticle.globalIndex()] = pairLossTruthTriggerRejection(mcParticle, sign); + } + } for (auto const& truthTrigger : comparisonTruthTriggers) { pairLossComparison.truthTriggers[truthTrigger.globalIndex] = truthTrigger; } From ea9224cea5039af2e2794cd09836fd8bbd33e2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:09:09 +0200 Subject: [PATCH 417/648] [PWGJE] Remove HfHelper instances (#18437) --- PWGJE/Tasks/jetFinderHFQA.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/PWGJE/Tasks/jetFinderHFQA.h b/PWGJE/Tasks/jetFinderHFQA.h index ad1c25a9aa1..1eaa5456bf2 100644 --- a/PWGJE/Tasks/jetFinderHFQA.h +++ b/PWGJE/Tasks/jetFinderHFQA.h @@ -16,7 +16,6 @@ #ifndef PWGJE_TASKS_JETFINDERHFQA_H_ #define PWGJE_TASKS_JETFINDERHFQA_H_ -#include "PWGHF/Core/HfHelper.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" @@ -73,7 +72,6 @@ struct JetFinderHFQATask { o2::framework::Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection applied at the jet finder level, here rejection is applied for collision and track process functions"}; - HfHelper hfHelper; std::vector filledJetR_Both; std::vector filledJetR_Low; std::vector filledJetR_High; From 33159056e80b827028f4af9d772d9f1346b1c7a9 Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:13:52 -0500 Subject: [PATCH 418/648] [PWGHF] Add decay length and normalized decay length to Omegac0 QA tree (#18445) --- PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx index 6d4855fa863..e5c0754aa1c 100644 --- a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx +++ b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx @@ -228,7 +228,9 @@ DECLARE_SOA_TABLE(HfOmegac0ToOmegaPi, "AOD", "HFTOOMEGAPI", full::EtaV0PosDau, full::EtaV0NegDau, full::EtaKaFromCasc, full::EtaPiFromCharmBaryon, full::EtaCharmBaryon, full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, + full::DecLenCharmBaryon, full::ErrorDecayLengthCharmBaryon, full::NormImpParCascade, full::NormImpParPiFromCharmBar, + full::NormDecayLenCharmBar, full::IsPionGlbTrkWoDca, full::PionItsNCls, full::NTpcRowsPion, full::NTpcRowsKaFromCasc, full::NTpcRowsPosV0Dau, full::NTpcRowsNegV0Dau, full::StatusPidLambda, full::StatusPidCascade, full::StatusPidCharmBaryon, @@ -371,9 +373,11 @@ struct HfTreeCreatorToOmegaPiQa { candidate.dcaCascDau(), candidate.dcaV0Dau(), candidate.dcaCharmBaryonDau(), + candidate.decLenCharmBaryon(), candidate.errorDecayLengthCharmBaryon(), candidate.impactParCascXY() / candidate.errImpactParCascXY(), candidate.impactParBachFromCharmBaryonXY() / candidate.errImpactParBachFromCharmBaryonXY(), + candidate.decLenCharmBaryon() / candidate.errorDecayLengthCharmBaryon(), candidate.template bachelorFromCharmBaryon_as().isGlobalTrackWoDCA(), candidate.template bachelorFromCharmBaryon_as().itsNCls(), candidate.template bachelorFromCharmBaryon_as().tpcNClsCrossedRows(), From abc22071c2e3a307320197d77d174cfa90b9d705 Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Fri, 23 Oct 2026 20:28:43 +0200 Subject: [PATCH 419/648] [PWGEM,PWGJE] EMCal TM monitor: MC truth of the track matching chain (#18443) --- PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx | 37 +- PWGJE/Tasks/emcTmMonitor.cxx | 379 ++++++++++++++++++-- 2 files changed, 384 insertions(+), 32 deletions(-) diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx index 6b2e5fd2aa7..e2b366714fe 100644 --- a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -119,6 +119,7 @@ enum class LegMatchStatus : uint8_t { kOutsideWindow, // leg found, but outside the track matching window of the analysis kInsideWindowEoverP, // leg found inside the window, but E/p above the cut (primaries only) kInsideWindow, // leg found inside the window: the cluster should have been vetoed + kNotGlobalTrack, // primaries only: the leg is not a global track, so it is no candidate of the primary matching kNStatus }; @@ -478,12 +479,15 @@ struct TwoPhotonMcTask { const AxisSpec axisDeltaPhi{100, -0.1, 0.1, "#Delta#varphi (cluster - track)"}; const AxisSpec axisDp{200, -1., 1., "(#it{p}_{match} - #it{p}_{leg}) / #it{p}_{leg}"}; static constexpr std::array(SameParticleOrigin::kNOrigins)> OriginLabels = {"leg e^{#pm}", "brems #gamma of leg", "other e^{#pm}", "other"}; - static constexpr std::array(LegMatchStatus::kNStatus)> StatusLabels = {"no match stored", "no match with leg #it{p}", "outside window", "in window, #it{E}/#it{p} #geq cut", "in window"}; + static constexpr std::array(LegMatchStatus::kNStatus)> StatusLabels = {"no match stored", "no match with leg #it{p}", "outside window", "in window, #it{E}/#it{p} #geq cut", "in window", "not a global track"}; auto hOrigin = registry.add("PCMEMC/SameParticle/hOrigin", "same particle pairs: leading contributor of the cluster", HistType::kTH3F, {thnAxisInvMass, thnAxisPtRec, axisOrigin}); for (std::size_t i = 0; i < OriginLabels.size(); ++i) { hOrigin->GetZaxis()->SetBinLabel(static_cast(i) + 1, OriginLabels[i].data()); } + // labels make the axis extendable, and TH1Merger::LabelMerge in ROOT 6.36 reads the z labels from the y axis: + // merged TH3 content would end up in a new, unlabelled z bin. With a fixed axis the merge goes bin by bin. + hOrigin->GetZaxis()->SetCanExtend(false); registry.add("PCMEMC/SameParticle/hLegEoverP", "cluster from a V0 leg: #it{E}_{cls}/#it{p}_{leg}", HistType::kTH2F, {{200, 0., 4., "#it{E}_{cls}/#it{p}_{leg}"}, axisPtLeg}); for (const auto& [type, title] : {std::pair{"Prim", "primary"}, {"Sec", "secondary"}}) { auto hStatus = registry.add(std::format("PCMEMC/SameParticle/hLeg{}Status", type.data()).data(), std::format("cluster from a V0 leg: leg among the matched {} tracks", title.data()).data(), HistType::kTH2F, {axisStatus, axisPtLeg}); @@ -762,6 +766,30 @@ struct TwoPhotonMcTask { return PairBucket::kCrossOtherHadron; } + /// \brief Whether the track of a V0 leg passes the global track selection used by the primary EMCal track matching. + /// Rebuilt from the leg columns of the derived data, following getGlobalTrackSelectionRun3ITSMatch(Run3ITSibAny): + /// the golden chi2 requirement cannot be checked here, and pT/eta are the ones of the leg at the V0 vertex. + /// \param leg V0 leg + template + static bool isLikelyGlobalTrack(TLeg const& leg) + { + constexpr float MinPt = 0.1f, MaxEta = 0.8f; + constexpr int MinCrossedRows = 70; + constexpr float MinCrossedRowsOverFindable = 0.8f, MaxChi2TPC = 4.f, MaxChi2ITS = 36.f, MaxDcaZ = 2.f; + const float pt = leg.pt(); + if (!leg.hasITS() || !leg.hasTPC() || pt < MinPt || std::fabs(leg.eta()) > MaxEta) { + return false; + } + if (leg.itsNClsInnerBarrel() < 1 || leg.itsChi2NCl() > MaxChi2ITS) { // Run3ITSibAny: at least one hit in ITS layers 0-2 + return false; + } + if (leg.tpcNClsCrossedRows() < MinCrossedRows || leg.tpcCrossedRowsOverFindableCls() < MinCrossedRowsOverFindable || leg.tpcChi2NCl() > MaxChi2TPC) { + return false; + } + const float maxDcaXY = 0.0105f + 0.035f / std::pow(pt, 1.1f); // o2-linter: disable=magic-number (global track selection) + return std::fabs(leg.dcaXY()) <= maxDcaXY && std::fabs(leg.dcaZ()) <= MaxDcaZ; + } + /// \brief Look for the V0 leg among the tracks matched to an EMC cluster and classify why it did not veto the cluster. /// The matched track tables store no track index, so the leg is identified by its momentum. /// \tparam isSecondary matches are secondaries (V0 legs, no E/p condition) instead of primaries @@ -865,7 +893,12 @@ struct TwoPhotonMcTask { return; } registry.fill(HIST("PCMEMC/SameParticle/hLegEoverP"), emc.e() / pLeg, ptLeg); - fillLegMatch(matchedPrims.sliceBy(perEMCClusterMT, emc.globalIndex()), emc.e(), pLeg, ptLeg); + // the primary matching only uses global tracks: other legs are no candidate there and get their own status + if (isLikelyGlobalTrack(leg)) { + fillLegMatch(matchedPrims.sliceBy(perEMCClusterMT, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } else { + registry.fill(HIST("PCMEMC/SameParticle/hLegPrimStatus"), static_cast(LegMatchStatus::kNotGlobalTrack), ptLeg); + } fillLegMatch(matchedSeconds.sliceBy(perEMCClusterMS, emc.globalIndex()), emc.e(), pLeg, ptLeg); } diff --git a/PWGJE/Tasks/emcTmMonitor.cxx b/PWGJE/Tasks/emcTmMonitor.cxx index 3d6baefda3b..cef13ebaf16 100644 --- a/PWGJE/Tasks/emcTmMonitor.cxx +++ b/PWGJE/Tasks/emcTmMonitor.cxx @@ -22,7 +22,11 @@ /// Simple event selection using the flag doEventSel is provided, which selects INT7 events if set to 1 /// For pilot beam data, instead of relying on the event selection, one can veto specific BC IDS using the flag /// fDoVetoBCID. +/// The MC process (processMCTruth) follows the leading MC particle of each cluster through the matching chain: +/// is its track reconstructed, extrapolated to the EMCal, a matching candidate (global track or V0 leg), +/// and matched to the cluster as primary or secondary track. +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" // for the V0 legs used in the secondary matching #include "PWGJE/DataModel/EMCALClusters.h" #include "Common/CCDB/TriggerAliases.h" @@ -30,24 +34,40 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include -#include +#include +#include +#include +#include +#include +#include + +#include #include +#include +#include #include #include #include +#include +#include +#include #include using namespace o2::framework; @@ -56,16 +76,55 @@ using CollisionEvSelIt = o2::soa::Join::it using BcEvSelIt = o2::soa::Join::iterator; using SelectedClusters = o2::soa::Filtered; using TracksPID = o2::soa::Join; +using SelectedMcClusters = o2::soa::Filtered>; +using TracksMC = o2::soa::Join; + +/// \brief Truth class of the leading MC particle of a cluster +enum class TmTruthClass : uint8_t { + kConversionElectron = 0, // e+- with a photon as mother, produced in the transport (photon conversion) + kPrimaryElectron, // e+- produced by the generator (Dalitz, heavy flavour, ...) + kOtherElectron, // any other e+- (delta rays, Compton, weak decays in the transport, ...) + kOtherCharged, // charged particle that is not an e+- (hadrons, muons) + kPhoton, + kNeutral, // neutral particle that is not a photon + kOther, // no MC particle or unknown PDG code + kNClasses +}; + +/// \brief What happened to the track of the leading MC particle of a cluster in the track matching +enum class TmTruthStatus : uint8_t { + kNoRecoTrack = 0, // no reconstructed track of this particle in this collision + kNotExtrapolated, // track(s) exist, but none was extrapolated to the EMCal (trackEta/PhiEmcal = -999) + kNoMatchingCandidate, // extrapolated, but neither a global track nor a V0 leg: input to no matching + kCandidateNotMatched, // matching candidate, but not matched to this cluster (dR > maxMatchingDistance or not among the closest tracks) + kMatchedPrimary, // matched to this cluster as primary (global) track + kMatchedSecondary, // matched to this cluster as secondary track (V0 leg) + kNStatus +}; + +/// \brief Type of the reconstructed track +enum class TmTrackType : uint8_t { + kNoTrack = 0, + kGlobal, // passes isGlobalTrack() + kItsTpc, // ITS and TPC, but not a global track + kTpcOnly, + kItsOnly, + kOtherType, + kNTypes +}; + struct EmcTmMonitor { HistogramRegistry mHistManager{"TrackMatchingMonitorHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; o2::emcal::Geometry* mGeometry = nullptr; + Service mPdgDatabase{}; Preslice perCluster = o2::aod::emcalclustercell::emcalclusterId; Preslice perClusterAmb = o2::aod::emcalclustercell::emcalambiguousclusterId; Preslice perClusterMatchedTracks = o2::aod::emcalclustercell::emcalclusterId; + Preslice perClusterMatchedSecondaries = o2::aod::emcalclustercell::emcalclusterId; // configurable parameters // TODO adapt doEventSel switch to also allow selection of other triggers (e.g. EMC7) - Configurable doEventSel{"doEventSel", 0, "demand kINT7"}; + Configurable doEventSel{"doEventSel", false, "demand kINT7"}; Configurable vetoBCID{"vetoBCID", "", "BC ID(s) to be excluded, this should be used as an alternative to the event selection"}; Configurable selectBCID{"selectBCID", "all", "BC ID(s) to be included, this should be used as an alternative to the event selection"}; Configurable vertexCut{"vertexCut", -1, "apply z-vertex cut with value in cm"}; @@ -85,6 +144,7 @@ struct EmcTmMonitor { Configurable minDPhi{"minDPhi", 0.01, "Minimum dPhi between track and cluster"}; Configurable> eOverPRange{"eOverPRange", {0.9, 1.2}, "E/p range where one would search for electrons (first <= E/p <= second)"}; Configurable hasTRD{"hasTRD", false, "demand track to have a hit in TRD."}; + Configurable emcalRadius{"emcalRadius", 440.f, "MC truth: radius (cm) used to move the cluster eta from the vertex to the origin frame of the track extrapolation"}; std::vector mVetoBCIDs; std::vector mSelectBCIDs; @@ -182,10 +242,67 @@ struct EmcTmMonitor { mHistManager.add("clusterTM_EoverP_hadron_ASide", "cluster E/hadron p in A-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in A-Side mHistManager.add("clusterTM_EoverP_hadron_CSide", "cluster E/hadron p in C-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in C-Side - if (vetoBCID->length()) { + // MC truth of the track matching, only booked if processMCTruth is enabled + if (doprocessMCTruth.value) { + static constexpr std::array(TmTruthClass::kNClasses)> ClassLabels = {"conversion e^{#pm}", "primary e^{#pm}", "other e^{#pm}", "charged (non e)", "#gamma", "neutral (non #gamma)", "other"}; + static constexpr std::array(TmTruthStatus::kNStatus)> StatusLabels = {"no reco track", "not extrapolated", "no matching candidate", "candidate not matched", "matched primary", "matched secondary"}; + static constexpr std::array(TmTrackType::kNTypes)> TypeLabels = {"no track", "global", "ITS-TPC non-global", "TPC only", "ITS only", "other"}; + const AxisSpec classAxis{static_cast(TmTruthClass::kNClasses), -0.5, static_cast(TmTruthClass::kNClasses) - 0.5, "leading MC particle"}; + const AxisSpec statusAxis{static_cast(TmTruthStatus::kNStatus), -0.5, static_cast(TmTruthStatus::kNStatus) - 0.5, "track matching status"}; + const AxisSpec typeAxis{static_cast(TmTrackType::kNTypes), -0.5, static_cast(TmTrackType::kNTypes) - 0.5, "track type"}; + const AxisSpec v0LegAxis{2, -0.5, 1.5, "V0 leg"}; + const AxisSpec mcPtAxis{std::vector{0., 0.25, 0.5, 0.75, 1., 1.5, 2., 3., 4., 6., 10., 20., 50.}, "#it{p}_{T,MC} (GeV/#it{c})"}; + // residuals: fine binning around 0, coarse binning up to MaxResidual, so the tails beyond the matching windows are visible + constexpr double MaxResidual = 0.4; // range of the residual axes + constexpr double FineRange = 0.04; // fine binning within +-FineRange + constexpr double FineWidth = 0.002; // bin width within +-FineRange + constexpr double CoarseWidth = 0.01; // bin width outside +-FineRange + const int nCoarse = static_cast(std::lround((MaxResidual - FineRange) / CoarseWidth)); + const int nFine = static_cast(std::lround((FineRange - (-FineRange)) / FineWidth)); + std::vector residualBins; + residualBins.reserve(nCoarse + nFine + nCoarse + 1); // negative coarse, fine, positive coarse edges incl. the upper end + for (int i = 0; i < nCoarse; ++i) { + residualBins.push_back(-MaxResidual + CoarseWidth * i); + } + for (int i = 0; i < nFine; ++i) { + residualBins.push_back(-FineRange + FineWidth * i); + } + for (int i = 0; i <= nCoarse; ++i) { + residualBins.push_back(FineRange + CoarseWidth * i); + } + const AxisSpec mcDEtaAxis{residualBins, "#Delta#eta (track - cluster)"}; + const AxisSpec mcDPhiAxis{residualBins, "#Delta#varphi (track - cluster) (rad)"}; + const AxisSpec radiusAxis{125, 0., 250., "#it{R}_{xy} of the conversion (cm)"}; + + auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(static_cast(i) + 1, labels[i].data()); + } + }; + // histograms with labelled axes are TH2 or THn: the TH3 label merge of ROOT 6.36 is broken + auto hLeadingClass = mHistManager.add("MCTruth/hLeadingClass", "leading MC particle of the cluster", HistType::kTH2F, {classAxis, energyAxis}); + setLabels(hLeadingClass->GetXaxis(), ClassLabels); + auto hStatus = mHistManager.add("MCTruth/hStatus", "track matching status of the leading MC particle", HistType::kTHnF, {statusAxis, classAxis, typeAxis, v0LegAxis, mcPtAxis}); + setLabels(hStatus->GetAxis(0), StatusLabels); + setLabels(hStatus->GetAxis(1), ClassLabels); + setLabels(hStatus->GetAxis(2), TypeLabels); + for (const auto& name : {"MCTruth/hResidualEta", "MCTruth/hResidualPhi"}) { + const bool isEta = std::string_view(name).ends_with("Eta"); + auto hRes = mHistManager.add(name, "residual of the extrapolated track of the leading MC particle (origin frame)", HistType::kTHnF, {isEta ? mcDEtaAxis : mcDPhiAxis, mcPtAxis, classAxis, typeAxis}); + setLabels(hRes->GetAxis(2), ClassLabels); + setLabels(hRes->GetAxis(3), TypeLabels); + } + mHistManager.add("MCTruth/hResidualConversion", "conversion e^{#pm}: residual of the extrapolated track", HistType::kTH3F, {mcDEtaAxis, mcDPhiAxis, mcPtAxis}); + mHistManager.add("MCTruth/hResidualVsStored", "matched tracks: own minus stored residual;#Delta#eta_{own} - #Delta#eta_{stored};#Delta#varphi_{own} - #Delta#varphi_{stored}", HistType::kTH2F, {{200, -0.02, 0.02}, {200, -0.02, 0.02}}); + auto hConvRadius = mHistManager.add("MCTruth/hConvRadius", "conversion e^{#pm}: matching status vs conversion radius", HistType::kTHnF, {statusAxis, radiusAxis, mcPtAxis}); + setLabels(hConvRadius->GetAxis(0), StatusLabels); + mHistManager.add("MCTruth/hEoverPConversion", "conversion e^{#pm}: #it{E}_{cls}/#it{p}_{track}", HistType::kTH2F, {eoverpAxis, trackptAxis}); + } + + if (!vetoBCID->empty()) { std::stringstream parser(vetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -195,7 +312,7 @@ struct EmcTmMonitor { if (selectBCID.value != "all") { std::stringstream parser(selectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -209,19 +326,22 @@ struct EmcTmMonitor { // an integer instead Filter clusterDefinitionSelection = (o2::aod::emcalcluster::definition == clusterDefinition) && (o2::aod::emcalcluster::time >= minTime) && (o2::aod::emcalcluster::time <= maxTime) && (o2::aod::emcalcluster::m02 > minM02) && (o2::aod::emcalcluster::m02 < maxM02); - /// \brief Process EMCAL clusters that are matched to a collisions - void processCollisions(CollisionEvSelIt const& theCollision, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + /// \brief Event selection of the monitor: trigger alias (if doEventSel) and z vertex + /// \param theCollision collision to check + /// \param fillHistos fill the event histograms (only once if several process functions run) + bool isEventSelected(CollisionEvSelIt const& theCollision, const bool fillHistos) { - mHistManager.fill(HIST("eventsAll"), 1); + if (fillHistos) { + mHistManager.fill(HIST("eventsAll"), 1); + } // do event selection if doEventSel is specified // currently the event selection is hard coded to kINT7 // but other selections are possible that are defined in TriggerAliases.h bool isSelected = true; const int beginningRun3 = 300000; - float lowP = 0.75f; - float midP = 1.25f; if (doEventSel) { + // needs aod::BCs in the process function, otherwise the bc() index is not bound if (theCollision.bc().runNumber() < beginningRun3) { if (!theCollision.alias_bit(kINT7)) { isSelected = false; @@ -234,20 +354,34 @@ struct EmcTmMonitor { } if (!isSelected) { LOG(debug) << "Event not selected because it is not kINT7 or does not have EMCAL in readout, skipping"; - return; + return false; + } + if (fillHistos) { + mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); } - mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); if (vertexCut > 0 && std::abs(theCollision.posZ()) > vertexCut) { LOG(debug) << "Event not selected because of z-vertex cut z= " << theCollision.posZ() << " > " << vertexCut << " cm, skipping"; + return false; + } + if (fillHistos) { + mHistManager.fill(HIST("eventsSelected"), 1); + mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + } + return true; + } + + /// \brief Process EMCAL clusters that are matched to a collisions + void processCollisions(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + { + if (!isEventSelected(theCollision, true)) { return; } - mHistManager.fill(HIST("eventsSelected"), 1); - mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + float lowP = 0.75f; + float midP = 1.25f; for (const auto& alltrack : alltracks) { - double trackEta, trackPhi; - trackEta = alltrack.trackEtaEmcal(); - trackPhi = alltrack.trackPhiEmcal(); + double trackEta = alltrack.trackEtaEmcal(); + double trackPhi = alltrack.trackPhiEmcal(); if (alltrack.isGlobalTrack()) { // NSigma of all global tracks without matching mHistManager.fill(HIST("TrackTM_NSigma"), alltrack.tpcNSigmaEl(), alltrack.pt()); mHistManager.fill(HIST("TrackEtaPhi"), trackEta, trackPhi); @@ -284,11 +418,11 @@ struct EmcTmMonitor { // match.track_as() with // using globTracks = o2::soa::Join; // In this example the counter t is just used to only look at the closest match - float dEta, dPhi, pT, abs_p, trackEta, trackPhi, nSigmaEl; - int supermoduleID; + float dEta{0}, dPhi{0}, pT{0}, absP{0}, trackEta{0}, trackPhi{0}, nSigmaEl{0}; + int supermoduleID = -1; try { supermoduleID = mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); - } catch (o2::emcal::InvalidPositionException& e) { + } catch (o2::emcal::InvalidPositionException const& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. continue; @@ -304,7 +438,7 @@ struct EmcTmMonitor { nSigmaEl = match.track_as().tpcNSigmaEl(); // exmple of how to access any property of the matched tracks (tracks are sorted by how close they are to cluster) LOG(debug) << "Pt of match" << match.track_as().pt(); - abs_p = std::abs(match.track_as().p()); + absP = std::abs(match.track_as().p()); // only consider closest match trackEta = match.track_as().trackEtaEmcal(); trackPhi = match.track_as().trackPhiEmcal(); @@ -330,14 +464,14 @@ struct EmcTmMonitor { mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, supermoduleID); mHistManager.fill(HIST("clusterTM_dEtaPt"), dEta, pT, supermoduleID); } - double eOverP = cluster.energy() / abs_p; + double eOverP = cluster.energy() / absP; mHistManager.fill(HIST("clusterTM_dEtaTN"), dEta, t); mHistManager.fill(HIST("clusterTM_dPhiTN"), dPhi, t); mHistManager.fill(HIST("clusterTM_dRTN"), std::sqrt(dPhi * dPhi + dEta * dEta), t); mHistManager.fill(HIST("clusterTM_EoverP_E"), eOverP, cluster.energy(), t); mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, t); mHistManager.fill(HIST("clusterEMatched"), cluster.energy(), t); - mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), abs_p, t); + mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), absP, t); mHistManager.fill(HIST("clusterTM_EoverP_Pt"), eOverP, match.track_as().pt(), t); mHistManager.fill(HIST("clusterTM_NSigma"), nSigmaEl, match.track_as().pt(), t); mHistManager.fill(HIST("MatchedTrackEtaPhi"), trackEta, trackPhi); @@ -357,9 +491,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_PosdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_PosdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -371,9 +505,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_NegdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_NegdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -446,6 +580,191 @@ struct EmcTmMonitor { } PROCESS_SWITCH(EmcTmMonitor, processCollisions, "Process clusters from collision", true); + /// \brief Information of a reconstructed track needed for the MC truth of the matching + struct McTrackInfo { + int64_t id{-1}; + bool isGlobal{false}; + bool hasITS{false}; + bool hasTPC{false}; + float etaEmcal{-999.f}; + float phiEmcal{-999.f}; + float p{0.f}; + float pt{0.f}; + }; + + static constexpr float NotOnEmcal = -900.f; // trackEta/PhiEmcal of tracks that were not extrapolated to the EMCal are -999 + + /// \brief Truth class of an MC particle + template + TmTruthClass truthClassOf(TMcParticle const& mc, TMcParticles const& mcParticles) + { + const int pdg = mc.pdgCode(); + if (std::abs(pdg) == PDG_t::kElectron) { + if (mc.producedByGenerator()) { + return TmTruthClass::kPrimaryElectron; + } + if (mc.getProcess() == TMCProcess::kPPair && mc.has_mothers() && mcParticles.iteratorAt(mc.mothersIds()[0]).pdgCode() == PDG_t::kGamma) { + return TmTruthClass::kConversionElectron; + } + return TmTruthClass::kOtherElectron; + } + if (pdg == PDG_t::kGamma) { + return TmTruthClass::kPhoton; + } + const TParticlePDG* info = mPdgDatabase->GetParticle(pdg); + if (info == nullptr) { + return TmTruthClass::kOther; + } + return info->Charge() != 0. ? TmTruthClass::kOtherCharged : TmTruthClass::kNeutral; + } + + /// \brief Type of a reconstructed track + static TmTrackType trackTypeOf(McTrackInfo const& info) + { + if (info.isGlobal) { + return TmTrackType::kGlobal; + } + if (info.hasITS && info.hasTPC) { + return TmTrackType::kItsTpc; + } + if (info.hasTPC) { + return TmTrackType::kTpcOnly; + } + return info.hasITS ? TmTrackType::kItsOnly : TmTrackType::kOtherType; + } + + /// \brief MC truth of the track matching: follow the leading MC particle of each cluster through the matching chain. + /// Needs emcal-correction-task with processMCWithSecondaries and the photon conversion builder (aod::V0Legs). + void processMCTruth(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedMcClusters const& clusters, o2::aod::EMCALMatchedTracks const& matchedTracks, o2::aod::EMCMatchSecs const& matchedSecondaries, TracksMC const& tracks, o2::aod::V0Legs const& v0Legs, o2::aod::McParticles const& mcParticles) + { + if (!isEventSelected(theCollision, !doprocessCollisions.value)) { + return; + } + + // reconstructed tracks of each MC particle in this collision, and the tracks used as V0 legs (secondary matching) + std::unordered_map> tracksOfMcParticle; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + tracksOfMcParticle[track.mcParticleId()].push_back({track.globalIndex(), track.isGlobalTrack(), track.hasITS(), track.hasTPC(), track.trackEtaEmcal(), track.trackPhiEmcal(), track.p(), track.pt()}); + } + std::unordered_set v0LegTracks; + for (const auto& leg : v0Legs) { + v0LegTracks.insert(leg.trackId()); + } + + for (const auto& cluster : clusters) { + try { + mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); + } catch (o2::emcal::InvalidPositionException const& e) { + continue; // same as in processCollisions + } + const float clusterPt = cluster.energy() / std::cosh(cluster.eta()); + if (m02highPt > 0. && cluster.m02() >= maxM02HighPt && clusterPt >= m02highPt) { + continue; + } + + // leading contributor: the labels are ordered by energy fraction (ClusterLabel::orderLabels in the ClusterFactory) + const auto mcIds = cluster.mcParticleIds(); + const int64_t leadId = mcIds.empty() ? -1 : mcIds[0]; + TmTruthClass truthClass = TmTruthClass::kOther; + float mcPt = 0.f; + float convRadius = -1.f; + if (leadId >= 0) { + const auto mc = mcParticles.iteratorAt(leadId); + mcPt = mc.pt(); + truthClass = truthClassOf(mc, mcParticles); + convRadius = std::hypot(mc.vx(), mc.vy()); + } + mHistManager.fill(HIST("MCTruth/hLeadingClass"), static_cast(truthClass), cluster.energy()); + const bool isConversion = truthClass == TmTruthClass::kConversionElectron; + if (truthClass != TmTruthClass::kConversionElectron && truthClass != TmTruthClass::kPrimaryElectron && truthClass != TmTruthClass::kOtherElectron && truthClass != TmTruthClass::kOtherCharged) { + continue; // neutral leading particle: no track to match + } + + // best reconstructed track of the leading particle: extrapolated and matching candidate first, then highest pT + const McTrackInfo* best = nullptr; + int bestScore = -1; + if (auto it = tracksOfMcParticle.find(leadId); it != tracksOfMcParticle.end()) { + for (const auto& info : it->second) { + const bool isExtrapolated = info.etaEmcal > NotOnEmcal && info.phiEmcal > NotOnEmcal; + const bool isCandidate = info.isGlobal || v0LegTracks.contains(info.id); + const int score = 2 * (isExtrapolated ? 1 : 0) + (isCandidate ? 1 : 0); + if (score > bestScore || (score == bestScore && info.pt > best->pt)) { + best = &info; + bestScore = score; + } + } + } + + TmTruthStatus status = TmTruthStatus::kNoRecoTrack; + TmTrackType type = TmTrackType::kNoTrack; + bool isV0Leg = false; + if (best != nullptr) { + type = trackTypeOf(*best); + isV0Leg = v0LegTracks.contains(best->id); + const bool isExtrapolated = best->etaEmcal > NotOnEmcal && best->phiEmcal > NotOnEmcal; + + // is the track among the primary or secondary matches of this cluster + bool isMatchedPrimary = false, isMatchedSecondary = false; + float storedDEta = 0.f, storedDPhi = 0.f; + for (const auto& match : matchedTracks.sliceBy(perClusterMatchedTracks, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedPrimary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + if (!isMatchedPrimary) { + for (const auto& match : matchedSecondaries.sliceBy(perClusterMatchedSecondaries, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedSecondary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + } + if (isMatchedPrimary) { + status = TmTruthStatus::kMatchedPrimary; + } else if (isMatchedSecondary) { + status = TmTruthStatus::kMatchedSecondary; + } else if (!isExtrapolated) { + status = TmTruthStatus::kNotExtrapolated; + } else if (!best->isGlobal && !isV0Leg) { + status = TmTruthStatus::kNoMatchingCandidate; + } else { + status = TmTruthStatus::kCandidateNotMatched; + } + + if (isExtrapolated) { + // the matching compares to the cluster position seen from the origin, the cluster table has eta seen from the vertex + const float clusterEtaOrigin = std::asinh(std::sinh(cluster.eta()) + theCollision.posZ() / emcalRadius); + const float dEta = best->etaEmcal - clusterEtaOrigin; + const float dPhi = std::remainder(best->phiEmcal - cluster.phi(), o2::constants::math::TwoPI); + mHistManager.fill(HIST("MCTruth/hResidualEta"), dEta, mcPt, static_cast(truthClass), static_cast(type)); + mHistManager.fill(HIST("MCTruth/hResidualPhi"), dPhi, mcPt, static_cast(truthClass), static_cast(type)); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hResidualConversion"), dEta, dPhi, mcPt); + } + if (isMatchedPrimary || isMatchedSecondary) { + mHistManager.fill(HIST("MCTruth/hResidualVsStored"), dEta - storedDEta, dPhi - storedDPhi); + } + } + if (isConversion && best->p > 0.f) { + mHistManager.fill(HIST("MCTruth/hEoverPConversion"), cluster.energy() / best->p, best->pt); + } + } + mHistManager.fill(HIST("MCTruth/hStatus"), static_cast(status), static_cast(truthClass), static_cast(type), static_cast(isV0Leg), mcPt); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hConvRadius"), static_cast(status), convRadius, mcPt); + } + } + } + PROCESS_SWITCH(EmcTmMonitor, processMCTruth, "MC truth of the track matching for clusters from charged particles", false); + /// \brief Create binning for cluster energy axis (variable bin size) /// \return vector with bin limits std::vector makeEnergyBinning() const @@ -493,7 +812,7 @@ struct EmcTmMonitor { result.emplace_back(50. + 2.0 * (i - 190)); } else if (i < 235) { // o2-linter: disable=magic-number (just numbers for binning) result.emplace_back(100. + 5.0 * (i - 215)); - } else if (i < 245) { // o2-linter: disable=magic-number (just numbers for binning) + } else { result.emplace_back(200. + 10.0 * (i - 235)); } } @@ -529,9 +848,9 @@ struct EmcTmMonitor { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; return workflow; } From 401a95cc41323ac6755c150c8af2c4ebc0ff5729 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Fri, 23 Oct 2026 23:07:24 +0200 Subject: [PATCH 420/648] [PWGLF,PWGMM] Optimize histo filling in data and MC (#18448) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 374 +++++++++++++++++------------ 1 file changed, 225 insertions(+), 149 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index fe38f76e522..4509b68ec2c 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -280,9 +280,9 @@ struct DndetaMFTPbPb { ConfigurableAxis ptBins{"ptBins", {11, -0.5, 10.5}, "pT binning (GeV/c)"}; ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; ConfigurableAxis zvtxBins{"zvtxBins", {60, -30., 30.}, "Z-vtx binning (cm)"}; - ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; - ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -2., 2.}, "DCAxy binning (cm)"}; - ConfigurableAxis dcaZBins{"dcaZBins", {800, -2., 2.}, "DCAz binning (cm)"}; + ConfigurableAxis deltaZBins{"deltaZBins", {200, -1., 1.}, "Delta Z-vtx binning (cm)"}; + ConfigurableAxis dcaXYBins{"dcaXYBins", {100, -0.5, 0.5}, "DCAxy binning (cm)"}; + ConfigurableAxis dcaZBins{"dcaZBins", {100, -0.5, 0.5}, "DCAz binning (cm)"}; ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; ConfigurableAxis chiSqPerNclBins{"chiSqPerNclBins", {100, 0, 100}, "#chi^{2} binning"}; @@ -521,16 +521,15 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { registryData.get(HIST("Events/DataEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelDataEvtStatus[iBin].data()); } - registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); if (gConf.fillTrackQaHist) { + registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTH2F, {nclsAxis, occupancyAxis}}}); } if (doprocessDatawBestTracksInclusive) { if (gConf.fillTrackQaHist) { - registryData.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); @@ -552,16 +551,15 @@ struct DndetaMFTPbPb { registryData.get(HIST("Events/Centrality/DataEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelDataEvtStatusCent[iBin].data()); } registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); if (gConf.fillTrackQaHist) { + registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, centralityAxis, occupancyAxis}}}); } if (doprocessDatawBestTracksCentFT0C) { if (gConf.fillTrackQaHist) { - registryData.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); @@ -603,23 +601,28 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Tracks/hMcTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcTrackStatus[iBin].data()); } if (gConf.fillTrackQaHist) { - registryMC.add({"Tracks/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); } } - registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + } + if (doprocessMcBestInclusive) { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEtaGen_t", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); - registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); } @@ -647,23 +650,24 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCentBest[iBin].data()); } if (gConf.fillTrackQaHist) { - registryMC.add({"Tracks/Centrality/DCA3d", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); } } - registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEtaGen_t", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); @@ -678,10 +682,14 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); } - registryMC.add({"Events/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyBestInclusive) { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } + registryMC.add({"Events/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); - registryMC.add({"Tracks/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); @@ -696,11 +704,15 @@ struct DndetaMFTPbPb { for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); } - registryMC.add({"Events/Centrality/hVtxZGen", "; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffGen", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + if (doprocessMcEfficiencyBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Events/Centrality/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRec", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRecClosure", "; p_{T} (GeV/c); #eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", ";#eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); } @@ -1071,15 +1083,6 @@ struct DndetaMFTPbPb { if (!isTrackSelected(track)) { continue; } - if (gConf.fillTrackQaHist) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), occ); - } - } float phi = track.phi(); o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { @@ -1089,6 +1092,15 @@ struct DndetaMFTPbPb { registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); } + if (gConf.fillTrackQaHist) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), c, occ); + } else { + registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), occ); + } + } ++nTrk; } return nTrk; @@ -1107,30 +1119,23 @@ struct DndetaMFTPbPb { if (!isTrackSelected(itrack)) { continue; } - if (gConf.fillTrackQaHist) { - if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), c, occ); - registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), c, occ); - } else { - registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), occ); - registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), occ); - } - } float phi = itrack.phi(); o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, c, occ); registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); } else { - registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, occ); + registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, occ); registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); } if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registryData.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), c, occ); registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { - registryData.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occ); + registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), occ); registryData.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); } if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { @@ -1459,12 +1464,19 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", true); + std::vector nTrkPerCol; + std::vector centPerCol; + std::vector occPerCol; + /// @brief process function for counting tracks template void processData(typename C::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& /*bcs*/) { + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); @@ -1487,22 +1499,26 @@ struct DndetaMFTPbPb { } gCurrentHadronicRate->Fill(seconds, ir); } - if (!gConf.useEvtSel || isGoodEvent(collision)) { auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); } else { registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); } - auto nTrk = countTracks(tracks, z, c, occ); - - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); - } else { - registryData.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + nTrkPerCol.emplace_back(nTrk); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } } } else { if constexpr (has_reco_cent) { @@ -1520,6 +1536,9 @@ struct DndetaMFTPbPb { soa::SmallGroups const& besttracks, CollBCs const& /*bcs*/) { + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); @@ -1542,22 +1561,26 @@ struct DndetaMFTPbPb { } gCurrentHadronicRate->Fill(seconds, ir); } - if (!gConf.useEvtSel || isGoodEvent(collision)) { auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); if constexpr (has_reco_cent) { registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); } else { registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); } - auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); - - if constexpr (has_reco_cent) { - registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nBestTrks, z, c, occ); - } else { - registryData.fill(HIST("Events/NtrkZvtx"), nBestTrks, z, occ); + nTrkPerCol.emplace_back(nBestTrks); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } } } else { if constexpr (has_reco_cent) { @@ -1638,12 +1661,19 @@ struct DndetaMFTPbPb { Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); Preslice perColMcFiltTrk = o2::aod::fwdtrack::collisionId; + std::vector nRecPerCol; + std::vector centRecPerCol; + std::vector occRecPerCol; + template void processMc(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, MftTracksLabeled const& tracks) { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { @@ -1676,14 +1706,16 @@ struct DndetaMFTPbPb { bool gtZeroColl = false; int gtOneColl = 0; for (auto const& collision : collisions) { - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), cRec, occRec); } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } if (!gConf.useEvtSel || isGoodEvent(collision)) { + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); gtZeroColl = true; ++gtOneColl; if constexpr (has_reco_cent) { @@ -1745,11 +1777,15 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + } } else { registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), occRec); registryMC.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + } } ++nTrkRec; } else { @@ -1760,53 +1796,62 @@ struct DndetaMFTPbPb { } } } + nRecPerCol.emplace_back(nTrkRec); if (eventCuts.useZDiffCut) { if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { continue; } } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } } } } - if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto nchrg = countPart(perCollMCsample); - if (gtOneColl > 1) { - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } } else { - registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } } - } else { + auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } - } - auto nCharged = countPart(particles); - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); - } - if (collisions.size() == 0) { - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); - } else { - registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } } fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); @@ -1839,6 +1884,9 @@ struct DndetaMFTPbPb { MftTracksLabeled const& /*tracks*/, MftBestTracksLabeled const& besttracks) { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { @@ -1871,6 +1919,7 @@ struct DndetaMFTPbPb { buildLookupTable(collisions); bool gtZeroColl = false; + int gtOneColl = 0; for (auto const& collision : collisions) { float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); float cRec = getRecoCent(collision); @@ -1880,7 +1929,10 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); } if (!gConf.useEvtSel || isGoodEvent(collision)) { + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); gtZeroColl = true; + ++gtOneColl; if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); } else { @@ -1947,10 +1999,10 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), cRec, occRec); } else { registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), occRec); } if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); @@ -1983,19 +2035,17 @@ struct DndetaMFTPbPb { } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); if (gConf.fillTrackQaHist) { - registryMC.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); } } else { - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(particleOrigin), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), occRec); registryMC.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occRec); - registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); if (gConf.fillTrackQaHist) { - registryMC.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, occRec); + registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); } } @@ -2003,24 +2053,32 @@ struct DndetaMFTPbPb { } else { if constexpr (has_reco_cent) { registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), cRec, occRec); - registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), cRec, occRec); } else { registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), occRec); - registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), occRec); } } } + nRecPerCol.emplace_back(nATrk); if (eventCuts.useZDiffCut) { if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { continue; } } - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nATrk, collision.posZ(), cRec, occRec); - registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nATrk, collision.posZ(), occRec); - registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } } } } @@ -2029,17 +2087,35 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto nCharged = countPart(particles); - if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); - } else { - registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); - } - if (collisions.size() == 0) { + + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } + } + auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); + } + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } } fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); @@ -2116,16 +2192,16 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } @@ -2171,9 +2247,9 @@ struct DndetaMFTPbPb { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { const auto& particle = track.template mcParticle_as(); @@ -2196,17 +2272,17 @@ struct DndetaMFTPbPb { } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); - registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), track.pt(), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } @@ -2285,16 +2361,16 @@ struct DndetaMFTPbPb { } if constexpr (has_reco_cent) { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); } } } else { if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); if (gtOneRec) { - registryMC.fill(HIST("Tracks/hEffGen"), particle.pt(), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); } } } @@ -2334,12 +2410,12 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getOccupancy(collision, eventCuts.occupancyEstimator)); } - auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { if (!isBestTrackSelected(atrack)) { continue; } + const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); if (!isTrackSelected(itrack)) { continue; @@ -2353,9 +2429,9 @@ struct DndetaMFTPbPb { } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { const auto& particle = itrack.template mcParticle_as(); @@ -2388,17 +2464,17 @@ struct DndetaMFTPbPb { } } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); - registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), particle.pt(), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Tracks/hEffRec"), itrack.pt(), itrack.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } } } From 9ca7c71ac3ea0859a289884952f3c2298f8ce54f Mon Sep 17 00:00:00 2001 From: omvazque Date: Fri, 23 Oct 2026 17:49:57 -0600 Subject: [PATCH 421/648] [PWGLF] Added missing histograms (#18450) --- PWGLF/Tasks/Nuspex/piKpRAA.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index c45074ecae0..3288501f999 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -567,9 +567,11 @@ struct PiKpRAA { // USED FOR CALCULATING EVENT LOSS AND SIGNAL LOSS AS IN THE DN/DETA ANALYSIS registry.add("Centrality_FineBins_WithRecoEvt", "Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); registry.add("Centrality_FineBins_WithRecoEvt_BestCollIdx", "Rec. Coll. W/best Coll Indx + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); - registry.add("NchMC_AllGen_Fwd", "MC Nch of ALL events;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_AllGen", "MC Nch of ALL events;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); registry.add("NchMC_Fwd_WithRecoEvt", "True Nch from MC events W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); registry.add("NchMC_Fwd_WithRecoEvt_BestCollIdx", "True Nch from MC events W/best Coll Idx + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV registry.add("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); @@ -1456,7 +1458,7 @@ struct PiKpRAA { //--------------------------- // DENOMINATOR OF THE EVENT LOSS: ALL THE GENERATED EVENTS - registry.fill(HIST("NchMC_AllGen_Fwd"), nChMCForward); + registry.fill(HIST("NchMC_Fwd_AllGen"), nChMCForward); registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); registry.fill(HIST("zPosMCAll"), mccollision.posZ()); @@ -1547,12 +1549,14 @@ struct PiKpRAA { if (hasBcFT0TVX) { // NUMERATOR/DENOMINATOR OF THE EVENT LOSS registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX"), nChMCForward); // TVX-triggered + bestCollisionIndex if (isBestCollIndex) { registry.fill(HIST("EventCounterMC"), 4.5); // NUMERATOR/DENOMINATOR OF THE EVENT LOSS registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCForward); } } From 6747ec3ed56327fb435ddb7bfe244d8b79d9bd70 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sat, 24 Oct 2026 20:55:11 +0800 Subject: [PATCH 422/648] [PWGCF] Improvements on cuts and simplification of init (#18452) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 737 ++++++++---------- 1 file changed, 322 insertions(+), 415 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 02329c116d6..4aa84fc3666 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -107,13 +107,13 @@ enum EMultiplicityTables { enum EEventCuts { eSel8Cut = 0, + eIsGoodITSLayersAll, eNoCollInTimeRangeStandard, eNoCollInRofStandard, - eNoSameBunchPileUp, - eIsVertexITSTPC, - eIsGoodITSLayersAll, eIsGoodZvtxFT0vsPV, eNoHighMultCollInPrevRof, + eNoSameBunchPileUp, + eIsVertexITSTPC, eCentCut, eVertexZCut, eNContribCut, @@ -133,25 +133,25 @@ enum EBeforeAfterCuts { eBeforeAfterCuts_N }; -enum EParticleHistograms { - eHistPt = 0, - eHistPhi, - eHistEta, - eHistCharge, - eParticleHistograms_N -}; - enum EEventHistograms { eHistCentrality = 0, - eHistMultiplicity, + eHistMult, + eHistRefMult, eHistVertexX, eHistVertexY, eHistVertexZ, eHistImpactParameter, - eHistReferenceMultiplicity, eEventHistograms_N }; +enum EParticleHistograms { + eHistPt = 0, + eHistPhi, + eHistEta, + eHistCharge, + eParticleHistograms_N +}; + enum EWeightsHistograms { ePhiRec = 0, ePhiSim, @@ -165,8 +165,10 @@ enum EMiscHistograms { }; enum EObservables { - eProfTwo = 0, - eObservablesHist_N + eV22 = 0, + eV22Gap, + eV24, + eObservables_N }; // *) Lists of names: @@ -174,13 +176,19 @@ static constexpr std::array CentralityEstim static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; +static constexpr std::array ParticleHistTitles = {"p_{T}", "#varphi", "#eta", "Particle charge"}; +static constexpr std::array ParticleHistNames = {"eHistPt", "eHistPhi", "eHistEta", "eHistCharge"}; + +static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter"}; +static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter"}; + static constexpr std::array WeightsNames = {"ePhiRec", "ePhiSim", "ePt"}; static constexpr std::array CutsNames = {"eBefore", "eAfter"}; +static constexpr std::array CutsTitles = {"before", "after"}; -static constexpr std::array EventCutsNames = {"Sel8", "NoCollInTimeRangeStandard", "eNoCollInRofStandard", "eNoSameBunchPileUp", "eIsVertexITSTPC", "eIsGoodITSLayersAll", "eIsGoodZvtxFT0vsPV", "eNoHighMultCollInPrevRof", "Cent", "VertexZ", "NContrib"}; - -static constexpr std::array ParticleCutsNames = {"Pt", "Eta", "Charge"}; +static constexpr std::array ObservablesNames = {"eV22", "eV22Gap", "eV24"}; +static constexpr std::array ObservablesTitles = {"(v_{2}{2})^{2}", "(v_{2}{2})^{2} with eta gap", "(v_{2}{4})^{4}"}; // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root @@ -196,7 +204,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Define configurables: Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; Configurable cfCentralityEstimator{"cfCentralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; - Configurable cfMultiplicityTables{"cfMultiplicityTables", 0, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; + Configurable cfMultiplicityTables{"cfMultiplicityTables", 2, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; // *) External root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; @@ -220,7 +228,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; // event level cuts - Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1Cent", "1VertexZ", "1NContrib"}, "event cuts switch, on and off by the first number before name"}; + Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1IsGoodITSLayersAll", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1Cent", "1VertexZ", "1NContrib"}, "event cuts switch, on and off by the first number before name"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; @@ -242,10 +250,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to float fSigmaInel = 7.71; float fEtaGap = 1.0; - std::vector fEventCutSwitch = {"1Sel8", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1IsGoodITSLayersAll", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1Cent", "1VertexZ", "1NContrib"}; + std::vector fEventCutSwitch{}; std::vector fVertexZCutRange = {-10., 10.}; std::vector fCentCutRange = {0., 80.}; - std::vector fParticleCutSwitch = {"1Pt", "1Eta", "1Charge"}; + std::vector fParticleCutSwitch{}; std::vector fPtCutRange = {0.2, 5.}; std::vector fEtaCutRange = {-0.8, 0.8}; @@ -281,8 +289,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct Observables { TList* fObservablesList = nullptr; - std::array fProfTwo{}; //! [reco,sim] - std::array fProfTwoGap{}; + std::array, eObservables_N> fProfCorr{}; //! [type][reco,sim] } obs; struct QualityAssurance { @@ -294,12 +301,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to struct MiscHistograms { TList* fMiscHistList = nullptr; TProfile* fMiscHistRunNumber = nullptr; - TProfile* fMiscEventCuts = nullptr; - TProfile* fMiscParticleCuts = nullptr; std::array fMiscEventCutsCounter{}; //! [reco,sim] std::array fMiscParticleCutsCounter{}; //! [reco,sim] } misc; + struct RunTimeInfo { + bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash + int collisionCounter = 0; + int thisRunNumber = 0; + static constexpr int RunMessagePeriod = 100; + } rti; + // *) functions and templates std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) { @@ -531,6 +543,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to LOGF(warning, "%s: failed to clone histogram '%s' with run number %s", __FUNCTION__, WeightsNames[i], runNumber); continue; } + ex.fWeights[i]->SetStats(false); ex.fWeights[i]->SetTitle(filePath); ex.fWeights[i]->SetDirectory(nullptr); ex.fExternalHistogramsList->Add(ex.fWeights[i]); @@ -579,38 +592,67 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool eventCuts(T1 const& collision) { - std::vector doNotPassCut(EEventCuts_N, false); - - doNotPassCut[eSel8Cut] = tc.fEventCutSwitch[eSel8Cut].starts_with("1") && !collision.sel8(); - doNotPassCut[eNoCollInTimeRangeStandard] = tc.fEventCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); - doNotPassCut[eNoCollInRofStandard] = tc.fEventCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard); - doNotPassCut[eNoSameBunchPileUp] = tc.fEventCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); - doNotPassCut[eIsVertexITSTPC] = tc.fEventCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); - doNotPassCut[eIsGoodITSLayersAll] = tc.fEventCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); - doNotPassCut[eIsGoodZvtxFT0vsPV] = tc.fEventCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); - doNotPassCut[eNoHighMultCollInPrevRof] = tc.fEventCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); - doNotPassCut[eNContribCut] = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); - - if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { - doNotPassCut[eVertexZCut] = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); - auto thisCent = chooseCent(collision, tc.fCentralityEstimator); - doNotPassCut[eCentCut] = tc.fEventCutSwitch[eCentCut].starts_with("1") && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]); - } - if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { if (!collision.has_mcCollision()) { return false; } - auto thisMCCollision = collision.mcCollision(); - auto impactParameter = thisMCCollision.impactParameter(); - auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; - - doNotPassCut[eVertexZCut] = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (thisMCCollision.posZ() > tc.fVertexZCutRange[1] || thisMCCollision.posZ() < tc.fVertexZCutRange[0]); - doNotPassCut[eCentCut] = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); } for (int i = 0; i < EEventCuts_N; ++i) { - if (doNotPassCut[i]) { + bool doNotPassCut = false; + switch (i) { + default: + doNotPassCut = false; + break; + case eSel8Cut: + doNotPassCut = tc.fEventCutSwitch[eSel8Cut].starts_with("1") && !collision.sel8(); + break; + case eNoCollInTimeRangeStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); + break; + case eNoCollInRofStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard); + break; + case eNoSameBunchPileUp: + doNotPassCut = tc.fEventCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + break; + case eIsVertexITSTPC: + doNotPassCut = tc.fEventCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); + break; + case eIsGoodITSLayersAll: + doNotPassCut = tc.fEventCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + break; + case eIsGoodZvtxFT0vsPV: + doNotPassCut = tc.fEventCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + break; + case eNoHighMultCollInPrevRof: + doNotPassCut = tc.fEventCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); + break; + case eNContribCut: + doNotPassCut = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); + break; + case eVertexZCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.mcCollision().posZ() > tc.fVertexZCutRange[1] || collision.mcCollision().posZ() < tc.fVertexZCutRange[0]); + } + break; + case eCentCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + auto impactParameter = collision.mcCollision().impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); + } + break; + } // end of switch (i) { + + if (doNotPassCut) { if constexpr (rm == eReal) { misc.fMiscEventCutsCounter[eRec]->Fill(i + 0.5, 1); } @@ -619,10 +661,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } return false; } - } + } // end of for (int i = 0; i < EEventCuts_N; ++i) { return true; - } + } // end of bool eventCuts(T1 const& collision) template void eventHistFill(T1 const& collision, T2 const& tracks) @@ -632,17 +674,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); int multiplicityRec = static_cast(tracks.size()); if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHist[eHistMult][eRec][eBefore]->Fill(multiplicityRec); ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHist[eHistRefMult][eRec][eBefore]->Fill(thisRefMult); ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); } if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHist[eHistMult][eRec][eAfter]->Fill(multiplicityRec); ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHist[eHistRefMult][eRec][eAfter]->Fill(thisRefMult); ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); @@ -659,7 +701,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto impactParameter = thisMCCollision.impactParameter(); auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter if constexpr (cuts == eBefore) { - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistMult][eSim][eBefore]->Fill(multiplicitySim); ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); @@ -667,7 +709,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); } if constexpr (cuts == eAfter) { - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistMult][eSim][eAfter]->Fill(multiplicitySim); ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); @@ -675,42 +717,63 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); } } // end of if constexpr (rs == eRecAndSim && rm == eMC) { - } + } // end of void eventHistFill(T1 const& collision, T2 const& tracks) template bool particleCuts(T const& track) { - std::vector doNotPassCut(EParticleCuts_N, false); - - if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { - doNotPassCut[ePtCut] = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]); - doNotPassCut[eEtaCut] = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]); - doNotPassCut[eChargeCut] = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); - } // end of if constexpr (rm == eReal) { + for (int i = 0; i < EParticleCuts_N; ++i) { + bool doNotPassCut = false; + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + doNotPassCut = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); + break; + } + } // end of if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { - if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { - if (!track.has_mcParticle()) { - return false; - } - auto thisMCParticle = track.mcParticle(); - doNotPassCut[ePtCut] = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]); - doNotPassCut[eEtaCut] = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]); - if (tc.fParticleCutSwitch[eChargeCut].starts_with("1")) { - TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); - if (particlePDG) { - const int chargeMCUnit = 3; - auto chargeMC = particlePDG->Charge() / chargeMCUnit; - if (chargeMC != 1 && chargeMC != -1) { - doNotPassCut[eChargeCut] = true; - } - } else { - doNotPassCut[eChargeCut] = true; + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return false; + } + auto thisMCParticle = track.mcParticle(); + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + if (tc.fParticleCutSwitch[eChargeCut].starts_with("1")) { + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + if (particlePDG) { + const int chargeMCUnit = 3; + auto chargeMC = particlePDG->Charge() / chargeMCUnit; + if (chargeMC != 1 && chargeMC != -1) { + doNotPassCut = true; + } + } else { + doNotPassCut = true; + } + } // end of if (tc.fParticleCutSwitch[eChargeCut]) { + break; } - } // end of if (tc.fParticleCutSwitch[eChargeCut]) { - } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { - for (int i = 0; i < EParticleCuts_N; ++i) { - if (doNotPassCut[i]) { + if (doNotPassCut) { if constexpr (rm == eReal) { misc.fMiscParticleCutsCounter[eRec]->Fill(i + 0.5, 1); } @@ -719,10 +782,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } return false; } - } + } // end of for (int i = 0; i < EParticleCuts_N; ++i) { return true; - } + } // end of bool particleCuts(T const& track) template void particleHistFill(T1 const& track) @@ -803,12 +866,6 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - // *) Misc: - bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash - int collisionCounter = 0; - int thisRunNumber = 0; - static constexpr int RunMessagePeriod = 100; - // *) Define all member functions to be called in the main process* functions: template void steer(T1 const& collision, T2 const& tracks) @@ -819,21 +876,21 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } auto thisCollCentReal = chooseCent(collision, tc.fCentralityEstimator); - if (isFirstCollision) { - thisRunNumber = collision.bc().runNumber(); - misc.fMiscHistRunNumber->SetTitle(Form("%d", thisRunNumber)); // Get run number - misc.fMiscHistRunNumber->Fill(0.5, thisRunNumber); + if (rti.isFirstCollision) { + rti.thisRunNumber = collision.bc().runNumber(); + misc.fMiscHistRunNumber->SetTitle(Form("%d", rti.thisRunNumber)); // Get run number + misc.fMiscHistRunNumber->Fill(0.5, rti.thisRunNumber); if (tc.fExternalFileSwitch) { - getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", thisRunNumber)); + getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", rti.thisRunNumber)); } } if (tc.fRunMessageSwitch) { - if (collisionCounter % RunMessagePeriod == 0) { - LOGF(info, "Successfully running, run number is %d", thisRunNumber); - collisionCounter = 0; + if (rti.collisionCounter % rti.RunMessagePeriod == 0) { + LOGF(info, "Successfully running, run number is %d", rti.thisRunNumber); + rti.collisionCounter = 0; } - ++collisionCounter; + ++rti.collisionCounter; } // Fill Quality Assurance before cuts @@ -861,10 +918,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } - std::vector n22 = {-2, 2}; + std::vector harmonic22 = {-2, 2}; + std::vector harmonic24 = {-2, -2, 2, 2}; - auto qVectorsTableReal = initQVectorsTable(2, n22); - auto qVectorsTableMC = initQVectorsTable(2, n22); + auto qVectorsTableReal = initQVectorsTable(4, harmonic24); + auto qVectorsTableMC = initQVectorsTable(4, harmonic24); std::vector qVecABReal(2, TComplex(0., 0.)); std::vector qVecABMC(2, TComplex(0., 0.)); @@ -894,7 +952,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeightReal); auto thisEtaRec = track.eta(); - updateVecForGap(n22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); + updateVecForGap(harmonic22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); } if constexpr (rs == eRecAndSim) { @@ -911,61 +969,76 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto thisWeightSim = thisWeights[ePhiSim] * thisWeights[ePt]; updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeightSim); - updateVecForGap(n22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); + updateVecForGap(harmonic22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); } } } // end of for (auto const& track : tracks) { std::vector result22(2, TComplex(0., 0.)); + std::vector result24(2, TComplex(0., 0.)); std::vector result22Gap(2, 0.f); if (tc.fMasterCutSwitch) { if (passEventCutsReal) { - result22Gap[0] = (qVecABReal[0] * qVecABReal[1]).Re(); - result22Gap[1] = weightABReal[0] * weightABReal[1]; - if (result22Gap[1] != 0) { - obs.fProfTwoGap[eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); + result22 = two(qVectorsTableReal, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVectorsTableReal, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); } - result22 = two(qVectorsTableReal, n22); - if (noneZeroDenom(result22)) { - obs.fProfTwo[eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + result22Gap = {static_cast((qVecABReal[0] * qVecABReal[1]).Re()), weightABReal[0] * weightABReal[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); } - if constexpr (rs == eRecAndSim) { + if constexpr (rs == eSim || rs == eRecAndSim) { auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; - result22Gap[0] = (qVecABMC[0] * qVecABMC[1]).Re(); - result22Gap[1] = weightABMC[0] * weightABMC[1]; - if (result22Gap[1] != 0) { - obs.fProfTwoGap[eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); + result22 = two(qVectorsTableMC, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVectorsTableMC, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); } - result22 = two(qVectorsTableMC, n22); - if (noneZeroDenom(result22)) { - obs.fProfTwo[eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); + result22Gap = {static_cast((qVecABMC[0] * qVecABMC[1]).Re()), weightABMC[0] * weightABMC[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); } } // end of if constexpr (rs == eRecAndSim) { } } // end of if (tc.fMasterCutSwitch) { - isFirstCollision = false; // Now the first collision ends + rti.isFirstCollision = false; // Now the first collision ends } // end of template void steer(T1 const& collision, T2 const& tracks) { // *) Initialize and book all objects: void init(InitContext&) { - // *) Set colors - const int afterCutFillColor = kGreen - 10; - const int afterCutLineColor = kGreen; - const int beforeCutFillColor = kRed - 10; - const int beforeCutLineColor = kRed; - // *) Set automatically what to process, from an implicit variable "doprocessSomEProcessName" within a PROCESS_SWITCH clause: tc.fProcess[eProcessRec] = doprocessRec; tc.fProcess[eProcessRecSim] = doprocessRecSim; tc.fProcess[eProcessSim] = doprocessSim; + // *) Set runtime info + rti.isFirstCollision = true; + rti.collisionCounter = 0; + rti.thisRunNumber = 0; + + // *) Set colors + std::vector fillColor(eBeforeAfterCuts_N); + fillColor[eBefore] = kRed - 10; + fillColor[eAfter] = kGreen - 10; + + std::vector lineColor(eBeforeAfterCuts_N); + lineColor[eBefore] = kRed; + lineColor[eAfter] = kGreen; + // *) Configure your task using configurables in the json file: tc.fDryRun = cfDryRun; tc.fCentralityEstimator = cfCentralityEstimator; @@ -996,8 +1069,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList.setObject(temp); // *) Book and nest all other TLists: + // *) Book External Hist List if (tc.fExternalFileSwitch) { - // *) Book External Hist List ex.fExternalHistogramsList = new TList(); ex.fExternalHistogramsList->SetName("ExternalHistograms"); ex.fExternalHistogramsList->SetOwner(true); @@ -1011,51 +1084,40 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(misc.fMiscHistList); misc.fMiscHistRunNumber = new TProfile("[eRunNumber]", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber->SetStats(false); misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); misc.fMiscHistList->Add(misc.fMiscHistRunNumber); - misc.fMiscEventCuts = new TProfile("[eEventCuts]", "Event Level Cuts Switches", EEventCuts_N, 0, EEventCuts_N); - for (int i = 1; i <= EEventCuts_N; ++i) { - misc.fMiscEventCuts->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); - if (tc.fEventCutSwitch[i - 1].starts_with("1")) { - misc.fMiscEventCuts->Fill(i - 0.5, 1); + if (doprocessRec || doprocessRecSim) { + misc.fMiscEventCutsCounter[eRec] = new TH1D("[eEventCutsCounter][eRec]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); } - } - misc.fMiscHistList->Add(misc.fMiscEventCuts); + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eRec]); - misc.fMiscEventCutsCounter[eRec] = new TH1D("[eEventCutsCounter][eRec]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); - for (int i = 1; i <= EEventCuts_N; ++i) { - misc.fMiscEventCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); - } - misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eRec]); - - misc.fMiscParticleCuts = new TProfile("[eParticleCuts]", "Particle Level Cuts Switches", EParticleCuts_N, 0, EParticleCuts_N); - for (int i = 1; i <= EParticleCuts_N; ++i) { - misc.fMiscParticleCuts->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); - if (tc.fParticleCutSwitch[i - 1].starts_with("1")) { - misc.fMiscParticleCuts->Fill(i - 0.5, 1); + misc.fMiscParticleCutsCounter[eRec] = new TH1D("[eParticleCutsCounter][eRec]", "Particle Level Cuts Counter for Reconstructed Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eRec]); } - misc.fMiscHistList->Add(misc.fMiscParticleCuts); - - misc.fMiscParticleCutsCounter[eRec] = new TH1D("[eParticleCutsCounter][eRec]", "Particle Level Cuts Counter for Reconstructed Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); - for (int i = 1; i <= EParticleCuts_N; ++i) { - misc.fMiscParticleCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); - } - misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eRec]); if (doprocessSim || doprocessRecSim) { + misc.fMiscEventCutsCounter[eSim] = new TH1D("[eEventCutsCounter][eSim]", "Event Level Cuts Counter for Simulated Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eSim]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eSim]); + misc.fMiscParticleCutsCounter[eSim] = new TH1D("[eParticleCutsCounter][eSim]", "Particle Level Cuts Counter for Simulated Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eSim]->SetStats(false); for (int i = 1; i <= EParticleCuts_N; ++i) { - misc.fMiscParticleCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, ParticleCutsNames[i - 1]); + misc.fMiscParticleCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); } misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eSim]); - - misc.fMiscEventCutsCounter[eSim] = new TH1D("[eEventCutsCounter][eSim]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); - for (int i = 1; i <= EEventCuts_N; ++i) { - misc.fMiscEventCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, EventCutsNames[i - 1]); - } - misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eSim]); } // *) Book particle TLists: @@ -1064,253 +1126,100 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to pc.fParticleHistList->SetOwner(true); fBaseList->Add(pc.fParticleHistList); // any nested TList in the base TList appears as a subdir in the output ROOT file - // *) Book pt and phi distribution with binning defined through configurables in the json file: - std::vector lPtBins = cfPtBins.value; - const int nBinsPt = static_cast(lPtBins[0]); - const float minPt = lPtBins[1]; - const float maxPt = lPtBins[2]; - - std::vector lPhiBins = cfPhiBins.value; - const int nBinsPhi = static_cast(lPhiBins[0]); - const float minPhi = lPhiBins[1]; - const float maxPhi = lPhiBins[2]; + std::vector> lParticleHistBins(eParticleHistograms_N); - std::vector lEtaBins = cfEtaBins.value; - const int nBinsEta = static_cast(lEtaBins[0]); - const float minEta = lEtaBins[1]; - const float maxEta = lEtaBins[2]; - - constexpr int NBinsCharge = 5; - constexpr float MinCharge = -2.5; - constexpr float MaxCharge = 2.5; + lParticleHistBins[eHistPt] = cfPtBins.value; + lParticleHistBins[eHistPhi] = cfPhiBins.value; + lParticleHistBins[eHistEta] = cfEtaBins.value; + lParticleHistBins[eHistCharge] = {5, -2.5, 2.5}; if (doprocessRec || doprocessRecSim) { - pc.fParticleHist[eHistPt][eRec][eBefore] = new TH1F("[eHistPt][eRec][eBefore]", "p_{T} distribution for reconstructed particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eRec][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eRec][eBefore] = new TH1F("[eHistPhi][eRec][eBefore]", "#phi distribution for reconstructed particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eRec][eBefore]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eRec][eBefore] = new TH1F("[eHistEta][eRec][eBefore]", "#eta distribution for reconstructed particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eRec][eBefore]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eRec][eBefore] = new TH1F("[eHistCharge][eRec][eBefore]", "charge distribution for reconstructed particles before cuts", NBinsCharge, MinCharge, MaxCharge); - pc.fParticleHist[eHistCharge][eRec][eBefore]->GetXaxis()->SetTitle("Particle charge"); - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eBefore]); - } - - if (tc.fMasterCutSwitch) { - pc.fParticleHist[eHistPt][eRec][eAfter] = new TH1F("[eHistPt][eRec][eAfter]", "p_{T} distribution for reconstructed particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eRec][eAfter]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eRec][eAfter] = new TH1F("[eHistPhi][eRec][eAfter]", "#phi distribution for reconstructed particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eRec][eAfter]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eRec][eAfter] = new TH1F("[eHistEta][eRec][eAfter]", "#eta distribution for reconstructed particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eRec][eAfter]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eRec][eAfter] = new TH1F("[eHistCharge][eRec][eAfter]", "charge distribution for reconstructed particles after cuts", NBinsCharge, MinCharge, MaxCharge); - pc.fParticleHist[eHistCharge][eRec][eAfter]->GetXaxis()->SetTitle("Particle charge"); - - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][eAfter]); - } + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s distribution for reconstructed particles %s cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { } - } + } // end of if (doprocessRec || doprocessRecSim) { if (doprocessSim || doprocessRecSim) { - pc.fParticleHist[eHistPt][eSim][eBefore] = new TH1F("[eHistPt][eSim][eBefore]", "p_{T} distribution for simulated particles before cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eSim][eBefore]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eSim][eBefore] = new TH1F("[eHistPhi][eSim][eBefore]", "#phi distribution for simulated particles before cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eSim][eBefore]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eSim][eBefore] = new TH1F("[eHistEta][eSim][eBefore]", "#eta distribution for simulated particles before cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eSim][eBefore]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eSim][eBefore] = new TH1F("[eHistCharge][eSim][eBefore]", "charge distribution for simulated particles before cuts", NBinsCharge, MinCharge, MaxCharge); - pc.fParticleHist[eHistCharge][eSim][eBefore]->GetXaxis()->SetTitle("Particle charge"); - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eBefore]); - } - - if (tc.fMasterCutSwitch) { - pc.fParticleHist[eHistPt][eSim][eAfter] = new TH1F("[eHistPt][eSim][eAfter]", "p_{T} distribution for simulated particles after cuts", nBinsPt, minPt, maxPt); - pc.fParticleHist[eHistPt][eSim][eAfter]->GetXaxis()->SetTitle("p_{T}"); - - pc.fParticleHist[eHistPhi][eSim][eAfter] = new TH1F("[eHistPhi][eSim][eAfter]", "#phi distribution for simulated particles after cuts", nBinsPhi, minPhi, maxPhi); - pc.fParticleHist[eHistPhi][eSim][eAfter]->GetXaxis()->SetTitle("#varphi"); - - pc.fParticleHist[eHistEta][eSim][eAfter] = new TH1F("[eHistEta][eSim][eAfter]", "#eta distribution for simulated particles after cuts", nBinsEta, minEta, maxEta); - pc.fParticleHist[eHistEta][eSim][eAfter]->GetXaxis()->SetTitle("#eta"); - - pc.fParticleHist[eHistCharge][eSim][eAfter] = new TH1F("[eHistCharge][eSim][eAfter]", "charge distribution for simulated particles after cuts", NBinsCharge, MinCharge, MaxCharge); - pc.fParticleHist[eHistCharge][eSim][eAfter]->GetXaxis()->SetTitle("Particle charge"); - - for (int i = 0; i < eParticleHistograms_N; ++i) { - pc.fParticleHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][eAfter]); - } + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s distribution for simulated particles %s cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { } - } + } // end of if (doprocessSim || doprocessRecSim) { - // Book event-level histograms + // *) Book event-level histograms ec.fEventHistList = new TList(); ec.fEventHistList->SetName("EventHistograms"); ec.fEventHistList->SetOwner(true); fBaseList->Add(ec.fEventHistList); - constexpr std::array DefaultCentBoundaries = {0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f}; - constexpr int NDefaultCentBins = DefaultCentBoundaries.size() - 1; - - std::vector lCent = cfCentBins.value; - const int nBinsCent = static_cast(lCent[0]); - const float minCent = lCent[1]; - const float maxCent = lCent[2]; - - std::vector lMult = cfMultBins.value; - const int nBinsMult = static_cast(lMult[0]); - const float minMult = lMult[1]; - const float maxMult = lMult[2]; - - std::vector lMultRef = cfRefMultBins.value; - const int nBinsMultRef = static_cast(lMultRef[0]); - const float minMultRef = lMultRef[1]; - const float maxMultRef = lMultRef[2]; - - std::vector lVx = cfVxBins.value; - const int nBinsVx = static_cast(lVx[0]); - const float minVx = lVx[1]; - const float maxVx = lVx[2]; - - std::vector lVy = cfVyBins.value; - const int nBinsVy = static_cast(lVy[0]); - const float minVy = lVy[1]; - const float maxVy = lVy[2]; + std::vector> lEventHistBins(eEventHistograms_N); - std::vector lVz = cfVzBins.value; - const int nBinsVz = static_cast(lVz[0]); - const float minVz = lVz[1]; - const float maxVz = lVz[2]; - - std::vector lIp = cfIpBins.value; - const int nBinsIp = static_cast(lIp[0]); - const float minIp = lIp[1]; - const float maxIp = lIp[2]; + lEventHistBins[eHistCentrality] = cfCentBins.value; + lEventHistBins[eHistMult] = cfMultBins.value; + lEventHistBins[eHistRefMult] = cfRefMultBins.value; + lEventHistBins[eHistVertexX] = cfVxBins.value; + lEventHistBins[eHistVertexY] = cfVyBins.value; + lEventHistBins[eHistVertexZ] = cfVzBins.value; + lEventHistBins[eHistImpactParameter] = cfIpBins.value; if (doprocessRec || doprocessRecSim) { - ec.fEventHist[eHistCentrality][eRec][eBefore] = new TH1F("[eHistCentrality][eRec][eBefore]", "Centrality (reconstructed) before cuts", nBinsCent, minCent, maxCent); - ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eRec][eBefore] = new TH1F("[eHistMultiplicity][eRec][eBefore]", "Multiplicity (reconstructed) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle("Multiplicity (track.size())"); - - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore] = new TH1F("[eHistReferenceMultiplicity][eRec][eBefore]", "Reference Multiplicity before cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eBefore]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); - - ec.fEventHist[eHistVertexX][eRec][eBefore] = new TH1F("[eHistVertexX][eRec][eBefore]", "Vertex X (reconstructed) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eRec][eBefore]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eRec][eBefore] = new TH1F("[eHistVertexY][eRec][eBefore]", "Vertex Y (reconstructed) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eRec][eBefore] = new TH1F("[eHistVertexZ][eRec][eBefore]", "Vertex Z (reconstructed) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eRec][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistImpactParameter) { - ec.fEventHist[i][eRec][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eRec][eBefore]); + if (i == eHistImpactParameter) { // no impact parameter for Rec + continue; } - } - - if (tc.fMasterCutSwitch) { - ec.fEventHist[eHistCentrality][eRec][eAfter] = new TH1F("[eHistCentrality][eRec][eAfter]", "Centrality (reconstructed) after cuts", nBinsCent, minCent, maxCent); - ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eRec][eAfter] = new TH1F("[eHistMultiplicity][eRec][eAfter]", "Multiplicity (reconstructed) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle("Multiplicity (track.size())"); - - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter] = new TH1F("[eHistReferenceMultiplicity][eRec][eAfter]", "Reference Multiplicity after cuts", nBinsMultRef, minMultRef, maxMultRef); - ec.fEventHist[eHistReferenceMultiplicity][eRec][eAfter]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); - - ec.fEventHist[eHistVertexX][eRec][eAfter] = new TH1F("[eHistVertexX][eRec][eAfter]", "Vertex X (reconstructed) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eRec][eAfter]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eRec][eAfter] = new TH1F("[eHistVertexY][eRec][eAfter]", "Vertex Y (reconstructed) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eRec][eAfter] = new TH1F("[eHistVertexZ][eRec][eAfter]", "Vertex Z (reconstructed) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eRec][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistImpactParameter) { - ec.fEventHist[i][eRec][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eRec][eAfter]); + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; } - } // end of for (int i = 0; i < eEventHistograms_N; ++i) { - } // end of if (tc.fMasterCutSwitch) { - } + ec.fEventHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (reconstructed) %s cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[cfCentralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[cfCentralityEstimator])); + ec.fEventHist[eHistRefMult][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[cfMultiplicityTables])); + ec.fEventHist[eHistRefMult][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[cfMultiplicityTables])); + } // end of if (doprocessRec || doprocessRecSim) { if (doprocessSim || doprocessRecSim) { - ec.fEventHist[eHistCentrality][eSim][eBefore] = new TH1F("[eHistCentrality][eSim][eBefore]", "Centrality (simulated) before cuts", nBinsCent, minCent, maxCent); - ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eSim][eBefore] = new TH1F("[eHistMultiplicity][eSim][eBefore]", "Multiplicity (simulated) before cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eBefore]->GetXaxis()->SetTitle("Multiplicity (track.size())"); - - ec.fEventHist[eHistVertexX][eSim][eBefore] = new TH1F("[eHistVertexX][eSim][eBefore]", "Vertex X (simulated) before cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eSim][eBefore]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eSim][eBefore] = new TH1F("[eHistVertexY][eSim][eBefore]", "Vertex Y (simulated) before cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eSim][eBefore] = new TH1F("[eHistVertexZ][eSim][eBefore]", "Vertex Z (simulated) before cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eSim][eBefore]->GetXaxis()->SetTitle("Vertex Z"); - - ec.fEventHist[eHistImpactParameter][eSim][eBefore] = new TH1F("[eHistImpactParameter][eSim][eBefore]", "Impact Parameter (simulated) before cuts", nBinsIp, minIp, maxIp); - ec.fEventHist[eHistImpactParameter][eSim][eBefore]->GetXaxis()->SetTitle("Impact Parameter"); - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistReferenceMultiplicity) { - ec.fEventHist[i][eSim][eBefore]->SetColors(beforeCutLineColor, -1, beforeCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eSim][eBefore]); + if (i == eHistRefMult) { // no RefMult for Sim + continue; } - } - - if (tc.fMasterCutSwitch) { - ec.fEventHist[eHistCentrality][eSim][eAfter] = new TH1F("[eHistCentrality][eSim][eAfter]", "Centrality (simulated) after cuts", nBinsCent, minCent, maxCent); - ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - - ec.fEventHist[eHistMultiplicity][eSim][eAfter] = new TH1F("[eHistMultiplicity][eSim][eAfter]", "Multiplicity (simulated) after cuts", nBinsMult, minMult, maxMult); - ec.fEventHist[eHistMultiplicity][eSim][eAfter]->GetXaxis()->SetTitle("Multiplicity (track.size())"); - - ec.fEventHist[eHistVertexX][eSim][eAfter] = new TH1F("[eHistVertexX][eSim][eAfter]", "Vertex X (simulated) after cuts", nBinsVx, minVx, maxVx); - ec.fEventHist[eHistVertexX][eSim][eAfter]->GetXaxis()->SetTitle("Vertex X"); - - ec.fEventHist[eHistVertexY][eSim][eAfter] = new TH1F("[eHistVertexY][eSim][eAfter]", "Vertex Y (simulated) after cuts", nBinsVy, minVy, maxVy); - ec.fEventHist[eHistVertexY][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Y"); - - ec.fEventHist[eHistVertexZ][eSim][eAfter] = new TH1F("[eHistVertexZ][eSim][eAfter]", "Vertex Z (simulated) after cuts", nBinsVz, minVz, maxVz); - ec.fEventHist[eHistVertexZ][eSim][eAfter]->GetXaxis()->SetTitle("Vertex Z"); - - ec.fEventHist[eHistImpactParameter][eSim][eAfter] = new TH1F("[eHistImpactParameter][eSim][eAfter]", "Impact Parameter (simulated) after cuts", nBinsIp, minIp, maxIp); - ec.fEventHist[eHistImpactParameter][eSim][eAfter]->GetXaxis()->SetTitle("Impact Parameter"); - - for (int i = 0; i < eEventHistograms_N; ++i) { - if (i != eHistReferenceMultiplicity) { - ec.fEventHist[i][eSim][eAfter]->SetColors(afterCutLineColor, -1, afterCutFillColor); - ec.fEventHistList->Add(ec.fEventHist[i][eSim][eAfter]); + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; } - } - } - } + ec.fEventHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (simulated) %s cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + } // end of if (doprocessSim || doprocessRecSim) { // *) Book observales TLists: obs.fObservablesList = new TList(); @@ -1318,31 +1227,28 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to obs.fObservablesList->SetOwner(true); fBaseList->Add(obs.fObservablesList); + const std::vector defaultCentBoundaries = {0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f}; + const int nDefaultCentBins = defaultCentBoundaries.size() - 1; + if (doprocessRec || doprocessRecSim) { - obs.fProfTwo[eRec] = new TProfile("fProfTwo[eRec]", "Two particle correlation for reconstructed data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - obs.fProfTwo[eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - obs.fProfTwo[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); - obs.fProfTwo[eRec]->Sumw2(); - obs.fObservablesList->Add(obs.fProfTwo[eRec]); - - obs.fProfTwoGap[eRec] = new TProfile("fProfTwoGap[eRec]", "Two particle correlation for reconstructed data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); - obs.fProfTwo[eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - obs.fProfTwoGap[eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); - obs.fProfTwoGap[eRec]->Sumw2(); - obs.fObservablesList->Add(obs.fProfTwoGap[eRec]); - - if (doprocessRecSim) { - obs.fProfTwo[eSim] = new TProfile("fProfTwo[eSim]", "Two particle correlation for simulated data after cuts", NDefaultCentBins, DefaultCentBoundaries.data()); - obs.fProfTwo[eSim]->GetXaxis()->SetTitle("Centrality"); - obs.fProfTwo[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); - obs.fProfTwo[eSim]->Sumw2(); - obs.fObservablesList->Add(obs.fProfTwo[eSim]); - - obs.fProfTwoGap[eSim] = new TProfile("fProfTwoGap[eSim]", "Two particle correlation for simulated data after cuts with #eta gap", NDefaultCentBins, DefaultCentBoundaries.data()); - obs.fProfTwoGap[eSim]->GetXaxis()->SetTitle("Centrality"); - obs.fProfTwoGap[eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); - obs.fProfTwoGap[eSim]->Sumw2(); - obs.fObservablesList->Add(obs.fProfTwoGap[eSim]); + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eRec] = new TProfile(Form("[%s][eRec]", ObservablesNames[i]), Form("%s for reconstructed data after cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eRec]->SetStats(false); + obs.fProfCorr[i][eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); + obs.fProfCorr[i][eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfCorr[i][eRec]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eRec]); + } + } + + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eSim] = new TProfile(Form("[%s][eSim]", ObservablesNames[i]), Form("%s for simulated data after cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eSim]->SetStats(false); + obs.fProfCorr[i][eSim]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + obs.fProfCorr[i][eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfCorr[i][eSim]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eSim]); } } @@ -1358,16 +1264,18 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to fBaseList->Add(qa.fQualityAssuranceList); if (doprocessRec || doprocessRecSim) { + const auto& refMultBins = lEventHistBins[eHistRefMult]; for (int i = 0; i < eBeforeAfterCuts_N; ++i) { - qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), nBinsMultRef, minMultRef, maxMultRef, nBinsContrib, minContrib, maxContrib); + qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), static_cast(refMultBins[0]), refMultBins[1], refMultBins[2], nBinsContrib, minContrib, maxContrib); qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); } if (doprocessRecSim) { + const auto& centBins = lEventHistBins[eHistCentrality]; for (int i = 0; i < eBeforeAfterCuts_N; ++i) { - qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); @@ -1375,6 +1283,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // end of if (doprocessRecSim) { } // end of if (doprocessRec || doprocessRecSim) { } // end of if (tc.fQualityAssuranceSwitch) { + } // end of void init(InitContext&) { // A) Process only reconstructed data: @@ -1404,7 +1313,5 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) The final touch: WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } // WorkflowSpec... From 211828cc34cbd2a45cdd73ec2d9b3487576c1004 Mon Sep 17 00:00:00 2001 From: alcaliva <32872606+alcaliva@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:50:27 +0200 Subject: [PATCH 423/648] [PWGLF] Remove correlation analysis (#18416) --- PWGLF/Tasks/Nuspex/antinucleiInJets.cxx | 1222 ++--------------------- 1 file changed, 96 insertions(+), 1126 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx index e636fba327a..e4f61645740 100644 --- a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx +++ b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx @@ -17,12 +17,10 @@ #include "PWGJE/Core/JetBkgSubUtils.h" #include "PWGJE/Core/JetUtilities.h" -#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" -#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -30,7 +28,6 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include #include #include #include @@ -64,10 +61,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -83,9 +78,8 @@ using namespace o2::constants::math; using std::array; // Define convenient aliases for commonly used table joins -using SelectedCollisions = soa::Join; -using RecCollisionsMc = soa::Join; -using GenCollisionsMc = soa::Join; +using SelectedCollisions = soa::Join; +using RecCollisionsMc = soa::Join; using AntiNucleiTracks = soa::Join; using AntiNucleiTracksMc = soa::Join; using LorentzVector = ROOT::Math::PxPyPzEVector; @@ -136,12 +130,11 @@ struct AntinucleiInJets { std::mt19937 rng; std::uniform_int_distribution generateRandomNr{0, 1}; - // Histogram registries for data, MC, quality control, multiplicity and correlations + // Histogram registries for data, MC, quality control, and multiplicity HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMult{"registryMult", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryCorr{"registryCorr", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Random generator for subsample assignment TRandom3 mRand; @@ -167,14 +160,13 @@ struct AntinucleiInJets { // Jet selection and event filtering parameters Configurable minJetPt{"minJetPt", 10.0, "Minimum pt of the jet after bkg subtraction"}; - Configurable maxJetPt{"maxJetPt", 1e+06, "Maximum pt of the jet after bkg subtraction"}; + Configurable maxJetPt{"maxJetPt", 1e+08, "Maximum pt of the jet after bkg subtraction"}; Configurable ptLeadingMin{"ptLeadingMin", 5.0, "pt Leading Min"}; Configurable rJet{"rJet", 0.4, "Jet resolution parameter R"}; Configurable zVtx{"zVtx", 10.0, "Maximum zVertex"}; - Configurable applyAreaCut{"applyAreaCut", false, "apply area cut A > f * pi R^2"}; - Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; + Configurable applyAreaCut{"applyAreaCut", true, "apply area cut A > f * pi R^2"}; + Configurable deltaEtaEdge{"deltaEtaEdge", 0.0, "eta gap from the edge"}; Configurable nSyst{"nSyst", 50, "number of systematic variations"}; - Configurable nSubsamples{"nSubsamples", 50, "number of subsamples"}; // Track quality, kinematic, and PID selection parameters Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; @@ -207,13 +199,6 @@ struct AntinucleiInJets { Configurable urlToCcdb{"urlToCcdb", "http://alice-ccdb.cern.ch/", "url of the personal ccdb"}; Configurable pathToFile{"pathToFile", "Users/a/alcaliva/reweightingHistogramsAntinucleiInJets/", "path to file"}; - // Number of events - Configurable shrinkInterval{"shrinkInterval", 1000, "variable that controls how often shrinking happens"}; - - // Range of systematic variations to be processed - Configurable systIndexStart{"systIndexStart", 0, "First systematic index (inclusive)"}; - Configurable systIndexEnd{"systIndexEnd", 50, "Last systematic index (exclusive)"}; - // Coalescence momentum Configurable coalescenceMomentum{"coalescenceMomentum", 0.15, "p0 (GeV/c)"}; @@ -227,13 +212,12 @@ struct AntinucleiInJets { TH1F* antiprotonsPerpCone = nullptr; TH1F* antideuteronsInsideJets = nullptr; TH1F* antideuteronsPerpCone = nullptr; + TH1F* recoJetPtReweightingFactor = nullptr; + TH1F* genJetPtReweightingFactor = nullptr; // CCDB manager service for accessing condition data Service ccdb{}; - // Direct interface to the CCDB API for manual data access - o2::ccdb::CcdbApi ccdbApi; - // Instantiate the main Zorro processing object and define an output to store summary information Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; @@ -273,7 +257,7 @@ struct AntinucleiInJets { rng.seed(12345); // Load reweighting histograms from CCDB if antinuclei efficiency processing is enabled - if (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec) { + if (applyReweighting && (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec)) { ccdb->setURL(urlToCcdb.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -549,38 +533,6 @@ struct AntinucleiInJets { registryMC.add("jetPtResolution", "jet Pt Resolution", HistType::kTH2F, {{1000, 0, 100, "#it{p}^{jet}_{T,true} (GeV/#it{c})"}, {1000, -20, 20, "#Delta #it{p}^{jet}_{T} (GeV/#it{c})"}}); } - // Coalescence and Correlation analysis - if (doprocessCoalescenceCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - - registryMC.add("genEventsCoalescenceCorr", "genEventsCoalescenceCorr", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryMC.add("antideuteron_fullEvent_CoalescenceCorr", "antideuteron_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antiproton_fullEvent_CoalescenceCorr", "antiproton_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); - - // Counter histograms - registryCorr.add("eventCounter_CoalescenceCorr", "number of events in Coalescence simulation", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_CoalescenceCorr", "Number of events per centrality (Full Event) in Coalescence simulation", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_CoalescenceCorr", "rho_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_netP_netD_fullEvent_CoalescenceCorr", "rho_netP_netD_fullEvent_CoalescenceCorr", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_CoalescenceCorr", "q1d_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1p_fullEvent_CoalescenceCorr", "q1p_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1d_square_fullEvent_CoalescenceCorr", "q1d_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - registryCorr.add("q1p_square_fullEvent_CoalescenceCorr", "q1p_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - registryCorr.add("q1d_q1p_fullEvent_CoalescenceCorr", "q1d_q1p_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - } - // Systematic uncertainties (Data) if (doprocessSystData) { registryData.add("number_of_events_data_syst", "event counter", HistType::kTH1F, {{20, 0, 20, "counter"}}); @@ -611,92 +563,13 @@ struct AntinucleiInJets { registryMC.add("antiproton_incl_syst", "antiproton_incl_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_prim_syst", "antiproton_prim_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); } - - // Correlation analysis - if (doprocessCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec subsampleAxis{nSubsamples, 0, static_cast(nSubsamples), "Subsample Index"}; - - // Event counter - registryCorr.add("eventCounter", "number of events", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent", "Number of events per centrality (Full Event)", HistType::kTH2F, {multiplicityAxis, subsampleAxis}); - // registryCorr.add("eventCounter_centrality_jet", "Number of events per centrality (Jet)", HistType::kTH1F, {multiplicityAxis}); - // registryCorr.add("eventCounter_centrality_ue", "Number of events per centrality (Underlying Event)", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms: antiproton vs. antideuteron number vs. event multiplicity - // registryCorr.add("rho_jet", "rho_jet", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - // registryCorr.add("rho_ue", "rho_ue", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_fullEvent", "rho_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, subsampleAxis}); - - // Correlation histograms: net antiproton vs. net antideuteron numbers - // registryCorr.add("rho_netP_netD_jet", "rho_netP_netD_jet", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - // registryCorr.add("rho_netP_netD_ue", "rho_netP_netD_ue", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - registryCorr.add("rho_netP_netD_fullEvent", "rho_netP_netD_fullEvent", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, subsampleAxis}); - - // Efficiency histograms jet - // registryCorr.add("q1d_jet", "q1d_jet", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_jet", "q1p_jet", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_jet", "q1d_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_jet", "q1p_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_jet", "q1d_q1p_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms UE - // registryCorr.add("q1d_ue", "q1d_ue", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_ue", "q1p_ue", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_ue", "q1d_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_ue", "q1p_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_ue", "q1d_q1p_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent", "q1d_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_fullEvent", "q1p_fullEvent", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_square_fullEvent", "q1d_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_square_fullEvent", "q1p_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_q1p_fullEvent", "q1d_q1p_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, subsampleAxis}); - } - - // Systematic uncertainties on correlation analysis - if (doprocessSystCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec systAxis{nSyst, 0, static_cast(nSyst), "Systematic Variation Index"}; - - registryCorr.add("eventCounter_syst", "number of events syst", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_syst", "Number of events per centrality (Full Event) Syst", HistType::kTH2F, {multiplicityAxis, systAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_syst", "rho_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, systAxis}); - registryCorr.add("rho_netP_netD_fullEvent_syst", "rho_netP_netD_fullEvent_syst", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, systAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_syst", "q1d_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_fullEvent_syst", "q1p_fullEvent_syst", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_square_fullEvent_syst", "q1d_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_square_fullEvent_syst", "q1p_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_q1p_fullEvent_syst", "q1d_q1p_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, systAxis}); - } } void getReweightingHistograms(o2::framework::Service const& ccdbObj, TString const& filepath) { TList* list = ccdbObj->get(filepath.Data()); if (!list) { - LOGP(error, "Could not retrieve the list from CCDB"); + LOGP(fatal, "Could not retrieve the list from CCDB"); return; } @@ -708,21 +581,28 @@ struct AntinucleiInJets { primaryAntiOmega = dynamic_cast(list->FindObject("weightsOmega")); if (!primaryAntiprotons || !primaryAntiSigma || !primaryAntiLambda || !primaryAntiXi || !primaryAntiOmega) { - LOGP(error, "Missing one or more reweighting histograms for primary fraction in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for primary fraction in CCDB list"); } // Get reweighting histograms for antiproton efficiency antiprotonsInsideJets = dynamic_cast(list->FindObject("weightsJet")); antiprotonsPerpCone = dynamic_cast(list->FindObject("weightsUe")); if (!antiprotonsInsideJets || !antiprotonsPerpCone) { - LOGP(error, "Missing one or more reweighting histograms for antiproton efficiency in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for antiproton efficiency in CCDB list"); } // Get reweighting histograms for antideuteron efficiency antideuteronsInsideJets = dynamic_cast(list->FindObject("weightsAntidJet")); antideuteronsPerpCone = dynamic_cast(list->FindObject("weightsAntidUe")); if (!antideuteronsInsideJets || !antideuteronsPerpCone) { - LOGP(error, "Missing one or more reweighting histograms for antideuteron efficiency in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for antideuteron efficiency in CCDB list"); + } + + // Get reco jet pt reweighting histogram + recoJetPtReweightingFactor = dynamic_cast(list->FindObject("recoJetPtReweightingFactor")); + genJetPtReweightingFactor = dynamic_cast(list->FindObject("genJetPtReweightingFactor")); + if (!recoJetPtReweightingFactor || !genJetPtReweightingFactor) { + LOGP(fatal, "Missing one of jet pt reweighting histograms in CCDB list"); } LOGP(info, "Successfully loaded reweighting histograms from CCDB path"); @@ -908,17 +788,6 @@ struct AntinucleiInJets { return deltaPhi; } - // Find bin - int findBin(const std::vector& edges, double value) - { - auto it = std::upper_bound(edges.begin(), edges.end(), value); - int index = static_cast(it - edges.begin()) - 1; - if (index < 0 || index >= static_cast(edges.size()) - 1) { - return -1; // value is out of bounds - } - return index; - } - // ITS hit template bool hasITSHit(const TrackIts& track, int layer) @@ -1066,104 +935,6 @@ struct AntinucleiInJets { return false; } - // Selection of (anti)protons - template - bool isProton(const ProtonTrack& track) - { - // Constants - static constexpr double PtThreshold = 0.6; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)deuterons - template - bool isDeuteron(const DeuteronTrack& track) - { - // Constants - static constexpr double PtThreshold = 1.0; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)protons with systematic variations - template - bool isProtonSyst(const ProtonTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 0.6; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - - // Selection of (anti)deuterons with systematic variations - template - bool isDeuteronSyst(const DeuteronTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 1.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - // Process Data void processData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks, aod::BCsWithTimestamps const&) { @@ -1742,7 +1513,7 @@ struct AntinucleiInJets { Preslice mcParticlesPerMcCollision = o2::aod::mcparticle::mcCollisionId; // Antinuclei reconstruction efficiency - void processAntinucleiEfficiency(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processAntinucleiEfficiency(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Loop over generated collisions for (const auto& collision : genCollisions) { @@ -2054,16 +1825,13 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processAntinucleiEfficiency, "process antinuclei efficiency", false); // Generated events - void processJetsMCgen(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + void processJetsMCgen(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { // Define per-event particle containers std::vector fjParticles; std::vector protonMomentum; std::vector deuteronMomentum; - // Event counter - int eventCounter = 0; - // Jet and area definitions fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); @@ -2071,9 +1839,6 @@ struct AntinucleiInJets { // Loop over all simulated collisions for (const auto& collision : collisions) { - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); protonMomentum.clear(); @@ -2155,6 +1920,16 @@ struct AntinucleiInJets { auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + // Get generated jet pt reweighting factor + double genJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= genJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < genJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = genJetPtReweightingFactor->FindBin(jetPt); + genJetPtWeight = genJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + // Fill jet pt distribution registryMC.fill(HIST("jetPtMCgenerated"), jetMinusBkg.pt()); @@ -2189,10 +1964,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_deltay_deltaphi_jet"), particle.eta() - jet.eta(), getDeltaPhi(particle.phi(), jet.phi())); // Calculate weight - double weightJet(1.0); - if (applyReweighting && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(particle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); + double weightJet = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } } // Fill histogram for generated antiprotons @@ -2211,10 +1988,12 @@ struct AntinucleiInJets { // Fill antideuteron spectra if (isAntid) { - double weightJetAntid(1.0); - if (applyReweighting && particle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antideuteronsInsideJets->FindBin(particle.pt()); - weightJetAntid = antideuteronsInsideJets->GetBinContent(ipt); + double weightJetAntid = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(particle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } } registryMC.fill(HIST("antideuteron_gen_jet"), particle.pt(), weightJetAntid); @@ -2250,10 +2029,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_deltay_deltaphi_ue"), protonVec.Eta() - ueAxis2.Eta(), getDeltaPhi(protonVec.Phi(), ueAxis2.Phi())); // Calculate weight - double weightUe(1.0); - if (applyReweighting && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + double weightUe = genJetPtWeight; + if (applyReweighting) { + if (protonVec.Pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histogram for antiprotons in the UE @@ -2286,10 +2067,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightUeAntid(1.0); - if (applyReweighting && deuteronVec.Pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antideuteronsPerpCone->FindBin(deuteronVec.Pt()); - weightUeAntid = antideuteronsPerpCone->GetBinContent(ipt); + double weightUeAntid = genJetPtWeight; + if (applyReweighting) { + if (deuteronVec.Pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && deuteronVec.Pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(deuteronVec.Pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } } // Fill histogram for antideuterons in the UE @@ -2305,12 +2088,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("genEvents"), 5.5); } } - - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(protonMomentum); - } } } PROCESS_SWITCH(AntinucleiInJets, processJetsMCgen, "process jets mc gen", false); @@ -2327,18 +2104,12 @@ struct AntinucleiInJets { fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - // Event counter - int eventCounter = 0; - // Loop over all reconstructed collisions for (const auto& collision : collisions) { // Configuration registryMC.fill(HIST("settingMC"), minJetPt.value, rJet.value); - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); antiprotonTrackIndex.clear(); @@ -2458,6 +2229,16 @@ struct AntinucleiInJets { auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + // Get reconstructed jet pt reweighting factor + double recoJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= recoJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < recoJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = recoJetPtReweightingFactor->FindBin(jetPt); + recoJetPtWeight = recoJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + // Fill jet pt distribution registryMC.fill(HIST("jetPtMCreconstructed"), jetMinusBkg.pt()); @@ -2545,20 +2326,22 @@ struct AntinucleiInJets { } // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_jet"), pt); + registryMC.fill(HIST("antiproton_incl_jet"), pt, recoJetPtWeight); // Select physical primary antiprotons if (!mcparticle.isPhysicalPrimary()) continue; // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_jet"), pt); + registryMC.fill(HIST("antiproton_prim_jet"), pt, recoJetPtWeight); // Calculate weights - double weightJet(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); + double weightJet = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2596,10 +2379,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightJetAntid(1.0); - if (applyReweighting && mcparticle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antideuteronsInsideJets->FindBin(mcparticle.pt()); - weightJetAntid = antideuteronsInsideJets->GetBinContent(ipt); + double weightJetAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(mcparticle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2664,20 +2449,22 @@ struct AntinucleiInJets { } // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_ue"), pt); + registryMC.fill(HIST("antiproton_incl_ue"), pt, recoJetPtWeight); // Select physical primary antiprotons if (!mcparticle.isPhysicalPrimary()) continue; // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_ue"), pt); + registryMC.fill(HIST("antiproton_prim_ue"), pt, recoJetPtWeight); // Calculate weights - double weightUe(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + double weightUe = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2743,10 +2530,12 @@ struct AntinucleiInJets { continue; // Calculate weight - double weightUeAntid(1.0); - if (applyReweighting && mcparticle.pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antideuteronsPerpCone->FindBin(mcparticle.pt()); - weightUeAntid = antideuteronsPerpCone->GetBinContent(ipt); + double weightUeAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(mcparticle.pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2767,12 +2556,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("recEvents"), 11.5); } } - - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(antiprotonTrackIndex); - } } } PROCESS_SWITCH(AntinucleiInJets, processJetsMCrec, "process jets MC rec", false); @@ -2927,7 +2710,7 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processSystData, "Process syst data", false); // Process MC with systematic variations of analysis parameters - void processSystEff(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processSystEff(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Cut settings static std::vector maxDcaxySyst = { @@ -3139,618 +2922,8 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processSystEff, "process syst mc", false); - // Process correlation - void processCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) - { - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter"), 0.5); - - // Apply standard event selection - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; - - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter"), 1.5); - - // Reject events near the ITS Read-Out Frame border - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 2.5); - - // Reject events at the Time Frame border - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 3.5); - - // Require at least one ITS-TPC matched track - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter"), 4.5); - - // Reject events with same-bunch pileup - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter"), 5.5); - - // Require consistent FT0 vs PV z-vertex - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter"), 6.5); - - // Require TOF match for at least one vertex track - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter"), 7.5); - - // Assign event to a random subsample (0-19) - double sampleId = mRand.Integer(nSubsamples) + 0.5; - - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); - - // Fill event counter vs centrality (full Event region) - registryCorr.fill(HIST("eventCounter_centrality_fullEvent"), multiplicity, sampleId); - - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Particle counters - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over reconstructed tracks - for (auto const& track : tracks) { - - // Apply track selection - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; - } - } - } - - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, sampleId); - registryCorr.fill(HIST("rho_netP_netD_fullEvent"), netDeuteronFullEvent, netProtonFullEvent, sampleId); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, sampleId); - registryCorr.fill(HIST("q1p_fullEvent"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, sampleId); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1p_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1d_q1p_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); - } - } - - /* - // Loop over reconstructed tracks (refactoring: this part can be incorporated above) - int id(-1); - std::vector fjParticles; - for (auto const& track : tracks) { - id++; - if (!passedTrackSelectionForJetReconstruction(track)) - continue; - - // 4-momentum representation of a particle - fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(MassPionCharged)); - fourMomentum.set_user_index(id); - fjParticles.emplace_back(fourMomentum); - } - - // Reject empty events - if (fjParticles.empty()) - return; - registryCorr.fill(HIST("eventCounter"), 8.5); - - // Cluster particles using the anti-kt algorithm - fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); - std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); - auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); - - // Loop over reconstructed jets - bool isAtLeastOneJetSelected = false; - for (const auto& jet : jets) { - - // Jet must be fully contained in the acceptance - if ((std::fabs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge)) - continue; - - // Jet pt must be larger than threshold - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - if (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt) - continue; - - // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && normalizedJetArea > maxNormalizedJetArea) - continue; - isAtLeastOneJetSelected = true; - - // Perpendicular cones - double coneRadius = std::sqrt(jet.area() / PI); - TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); - getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); - if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { - continue; - } - - // Get jet constituents - std::vector jetConstituents = jet.constituents(); - - // Particle counters - std::vector nAntiprotonJet(nBins, 0); - std::vector nAntideuteronJet(nBins, 0); - int nTotProtonJet(0); - int nTotDeuteronJet(0); - int nTotAntiprotonJet(0); - int nTotAntideuteronJet(0); - - // Loop over jet constituents - for (const auto& particle : jetConstituents) { - - // Get corresponding track and apply track selection criteria - auto const& track = tracks.iteratorAt(particle.user_index()); - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonJet++; - } else if (track.sign() < 0) { - nTotAntiprotonJet++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonJet[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronJet++; - } else if (track.sign() < 0) { - nTotAntideuteronJet++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronJet[ibin]++; - } - } - } // end of loop over constituents - - // Fill correlation histograms - int netProtonJet = nTotProtonJet - nTotAntiprotonJet; - int netDeuteronJet = nTotDeuteronJet - nTotAntideuteronJet; - registryCorr.fill(HIST("rho_jet"), nTotAntideuteronJet, nTotAntiprotonJet, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_jet"), netDeuteronJet, netProtonJet); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_jet"), nAntideuteronJet[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_jet"), nAntiprotonJet[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntideuteronJet[j], multiplicity); - registryCorr.fill(HIST("q1p_square_jet"), ptAcenteri, ptAcenterj, nAntiprotonJet[i] * nAntiprotonJet[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntiprotonJet[j], multiplicity); - } - } - - // Particle counters - std::vector nAntiprotonUE(nBins, 0); - std::vector nAntideuteronUE(nBins, 0); - int nTotProtonUE(0); - int nTotDeuteronUE(0); - int nTotAntiprotonUE(0); - int nTotAntideuteronUE(0); - - // Loop over tracks in the underlying event - for (auto const& track : tracks) { - - // Get corresponding track and apply track selection criteria - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Calculate the angular distance between the track and underlying event axes in eta-phi space - double deltaEtaUe1 = track.eta() - ueAxis1.Eta(); - double deltaPhiUe1 = getDeltaPhi(track.phi(), ueAxis1.Phi()); - double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); - double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); - double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); - double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonUE++; - } else if (track.sign() < 0) { - nTotAntiprotonUE++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonUE[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronUE++; - } else if (track.sign() < 0) { - nTotAntideuteronUE++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronUE[ibin]++; - } - } - } - - // Fill correlation histograms - int netProtonUE = nTotProtonUE - nTotAntiprotonUE; - int netDeuteronUE = nTotDeuteronUE - nTotAntideuteronUE; - registryCorr.fill(HIST("rho_ue"), nTotAntideuteronUE, nTotAntiprotonUE, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_ue"), netDeuteronUE, netProtonUE); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_ue"), nAntideuteronUE[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_ue"), nAntiprotonUE[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntideuteronUE[j], multiplicity); - registryCorr.fill(HIST("q1p_square_ue"), ptAcenteri, ptAcenterj, nAntiprotonUE[i] * nAntiprotonUE[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntiprotonUE[j], multiplicity); - } - } - // Fill event counter vs centrality (ue region) - registryCorr.fill(HIST("eventCounter_centrality_ue"), multiplicity); - } - // Event counter: events with at least one jet selected - if (isAtLeastOneJetSelected) { - registryCorr.fill(HIST("eventCounter"), 9.5); - registryCorr.fill(HIST("eventCounter_centrality_jet"), multiplicity); - } - */ - } - PROCESS_SWITCH(AntinucleiInJets, processCorr, "Process Correlation analysis", false); - - // Process correlation analysis with systematic variations of analysis parameters - void processSystCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) - { - // cut settings (from processSystData) - static std::vector maxDcaxySyst = { - 0.071, 0.060, 0.066, 0.031, 0.052, 0.078, 0.045, 0.064, 0.036, 0.074, - 0.079, 0.043, 0.067, 0.059, 0.032, 0.070, 0.048, 0.077, 0.062, 0.034, - 0.057, 0.055, 0.073, 0.038, 0.050, 0.075, 0.041, 0.061, 0.033, 0.069, - 0.035, 0.044, 0.076, 0.049, 0.037, 0.054, 0.072, 0.046, 0.058, 0.040, - 0.068, 0.042, 0.056, 0.039, 0.047, 0.065, 0.051, 0.053, 0.063, 0.030}; - - static std::vector maxDcazSyst = { - 0.064, 0.047, 0.032, 0.076, 0.039, 0.058, 0.043, 0.069, 0.050, 0.035, - 0.074, 0.061, 0.045, 0.033, 0.068, 0.055, 0.037, 0.071, 0.042, 0.053, - 0.077, 0.038, 0.065, 0.049, 0.036, 0.059, 0.044, 0.067, 0.041, 0.034, - 0.073, 0.052, 0.040, 0.063, 0.046, 0.031, 0.070, 0.054, 0.037, 0.062, - 0.048, 0.035, 0.075, 0.051, 0.039, 0.066, 0.043, 0.060, 0.032, 0.056}; - - static std::vector nSigmaItsMinSyst = { - -2.9, -2.8, -3.0, -3.4, -2.7, -3.3, -3.0, -3.1, -3.4, -3.1, - -3.0, -2.8, -3.2, -2.6, -2.7, -3.4, -2.9, -3.0, -3.0, -2.7, - -2.9, -3.3, -3.0, -3.1, -3.2, -3.0, -2.9, -2.7, -3.3, -3.0, - -2.8, -3.3, -2.7, -3.3, -2.8, -3.4, -2.8, -3.4, -2.9, -3.1, - -3.2, -2.6, -3.1, -2.9, -3.1, -2.8, -2.9, -3.3, -3.0, -2.8}; - - static std::vector nSigmaItsMaxSyst = { - 2.9, 2.8, 3.0, 3.4, 2.7, 3.3, 3.0, 3.1, 3.4, 3.1, 3.0, 2.8, 3.2, - 2.6, 2.7, 3.4, 2.9, 3.0, 3.0, 2.7, 2.9, 3.3, 3.0, 3.1, 3.2, 3.0, - 2.9, 2.7, 3.3, 3.0, 2.8, 3.3, 2.7, 3.3, 2.8, 3.4, 2.8, 3.4, 2.9, - 3.1, 3.2, 2.6, 3.1, 2.9, 3.1, 2.8, 2.9, 3.3, 3.0, 2.8}; - - static std::vector minNsigmaTpcSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTpcSyst = { - 3.2, 2.9, 3.1, 2.9, 3.5, 2.6, 3.3, 3.0, 3.5, 2.7, 3.0, 2.6, 3.3, - 3.4, 2.8, 3.1, 2.6, 3.2, 3.1, 2.8, 3.4, 2.7, 3.4, 2.9, 3.0, 2.5, - 3.3, 2.8, 3.1, 2.7, 3.4, 2.8, 3.3, 2.6, 3.1, 2.5, 3.4, 3.0, 3.2, - 2.6, 3.4, 2.8, 3.1, 2.6, 3.3, 2.7, 3.2, 2.7, 3.4, 2.9}; - - static std::vector minNsigmaTofSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTofSyst = { - 3.9, 3.6, 3.8, 3.2, 3.2, 3.5, 3.1, 3.8, 3.5, 3.4, 3.9, 3.8, 3.7, - 3.0, 3.6, 3.1, 3.7, 3.4, 4.0, 3.0, 3.7, 3.3, 3.9, 3.1, 3.3, 3.5, - 3.6, 3.2, 3.5, 3.3, 3.9, 3.0, 3.4, 3.2, 3.1, 3.9, 3.6, 3.1, 3.2, - 4.0, 3.1, 3.7, 3.6, 3.1, 3.3, 3.5, 3.3, 3.4, 3.1, 3.8}; - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_syst"), 0.5); - - // Apply standard event selection (Same as processCorr) - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; - - registryCorr.fill(HIST("eventCounter_syst"), 1.5); - - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 2.5); - - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 3.5); - - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 4.5); - - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 5.5); - - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 6.5); - - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 7.5); - - const float multiplicity = collision.centFT0M(); - - // Bins setup - static const std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Clamp systematic index range - const int systStart = std::max(0, static_cast(systIndexStart.value)); - const int systEnd = std::min(static_cast(nSyst), static_cast(systIndexEnd.value)); - - // Loop over systematic variations - for (int isyst = systStart; isyst < systEnd; isyst++) { - - // Fill event counter for this systematic - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_syst"), multiplicity, static_cast(isyst)); - - // Particle counters for this specific cut setting - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over tracks - for (auto const& track : tracks) { - - // Apply track selection (from processSystData) - if (!passedTrackSelectionSyst(track, isyst)) - continue; - - // Apply DCA selections (from processSystData) - if (std::fabs(track.dcaXY()) > maxDcaxySyst[isyst] || std::fabs(track.dcaZ()) > maxDcazSyst[isyst]) - continue; - - // Particle identification using the ITS cluster size (vary also PID ITS) (from processSystData) - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMinSyst[isyst] || nSigmaITSprot > nSigmaItsMaxSyst[isyst])) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMinSyst[isyst] || nSigmaITSdeut > nSigmaItsMaxSyst[isyst])) { - passedItsPidDeut = false; - } - - // Check Identity (Proton/Deuteron) using TPC/TOF - bool isProt = isProtonSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - bool isDeut = isDeuteronSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - - // Kinematic range selection & counting - // (Anti)protons - if (isProt && passedItsPidProt) { - if (track.pt() >= ptOverAbins[0] && track.pt() < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; - } - } - } - - // (Anti)deuterons - if (isDeut && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon >= ptOverAbins[0] && ptPerNucleon < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; - } - } - } - } - - // Fill Correlation Histograms for systematic variations - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_syst"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_syst"), netDeuteronFullEvent, netProtonFullEvent, static_cast(isyst)); - - // Fill efficiency histograms for systematic variations - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_syst"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_fullEvent_syst"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1d_q1p_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processSystCorr, "Process Correlation systematics", false); - // Process coalescence - void processCoalescence(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + void processCoalescence(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { // Deuteron Mass and minimum pt double massDeut = o2::constants::physics::MassDeuteron; @@ -3960,209 +3133,6 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processCoalescence, "process coalescence", false); - // process Coalescence and Correlation Analysis - void processCoalescenceCorr(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) - { - // Deuteron Mass and minimum pt - double massDeut = o2::constants::physics::MassDeuteron; - static constexpr double MinPtPerNucleon = 0.1; // Cut on pt/A - - // Containers for candidates (before coalescence) - std::vector protonCandidates; - std::vector neutronCandidates; - - // Final containers for analysis (after coalescence) - std::vector finalProtons; - std::vector finalDeuterons; - - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Loop over all simulated collisions - for (const auto& collision : collisions) { - - // Clear containers - protonCandidates.clear(); - neutronCandidates.clear(); - finalProtons.clear(); - finalDeuterons.clear(); - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 0.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 0.5); - - // Apply event selection: require vertex position to be within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - continue; - - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 1.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 1.5); - - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); - - // Fill event counter vs centrality - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_CoalescenceCorr"), multiplicity); - - // Get particles in this MC collision - const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - - // Loop over MC particles - for (const auto& particle : mcParticlesThisMcColl) { - - // Monte Carlo index - int mcId = particle.globalIndex(); - int pdg = particle.pdgCode(); - int absPdg = std::abs(pdg); - - // Store Protons - if (particle.isPhysicalPrimary()) { - if (absPdg == PDG_t::kProton) { - protonCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } else if (absPdg == PDG_t::kNeutron) { // Store Neutrons - neutronCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } - } - } - - // Reject empty events - if (protonCandidates.empty() && neutronCandidates.empty()) - continue; - - registryMC.fill(HIST("genEventsCoalescenceCorr"), 2.5); - - // Build deuterons - for (size_t iP = 0; iP < protonCandidates.size(); ++iP) { - if (protonCandidates[iP].used) - continue; - - for (size_t iN = 0; iN < neutronCandidates.size(); ++iN) { - if (neutronCandidates[iN].used) - continue; - - // Physics consistency check - if (protonCandidates[iP].pdgCode * neutronCandidates[iN].pdgCode < 0) - continue; - - if (passDeuteronCoalescence(protonCandidates[iP], neutronCandidates[iN], coalescenceMomentum, mRand)) { - neutronCandidates[iN].used = true; - protonCandidates[iP].used = true; - - int sign = (protonCandidates[iP].pdgCode > 0) ? +1 : -1; - int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; - - double pxDeut = protonCandidates[iP].px + neutronCandidates[iN].px; - double pyDeut = protonCandidates[iP].py + neutronCandidates[iN].py; - double pzDeut = protonCandidates[iP].pz + neutronCandidates[iN].pz; - double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); - LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); - if (pd.Eta() >= minEta && pd.Eta() <= maxEta && (0.5 * pd.Pt()) >= MinPtPerNucleon) { - // Store Deuteron - finalDeuterons.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, protonCandidates[iP].mcIndex, false}); - } - - break; - } - } - } - - // Add unused protons to final vectors - for (const auto& proton : protonCandidates) { - if (!proton.used) { - finalProtons.push_back(proton); - } - } - - // Correlation Analysis - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over final protons - for (const auto& part : finalProtons) { - double pt = std::hypot(part.px, part.py); - - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antiprotons - if (part.pdgCode == PDG_t::kProtonBar) { - registryMC.fill(HIST("antiproton_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (pt < ptOverAbins[0] || pt >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotProtonFullEvent++; - } else { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, pt); - if (ibin >= 0 && ibin < nBins) - nAntiprotonFullEvent[ibin]++; - } - } - - // Loop over final deuterons - for (const auto& part : finalDeuterons) { - double pt = std::hypot(part.px, part.py); - double ptPerNucleon = 0.5 * pt; - - // Apply detector acceptance cuts (to match real data) - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antideuterons - if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { - registryMC.fill(HIST("antideuteron_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotDeuteronFullEvent++; - } else { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - if (ibin >= 0 && ibin < nBins) - nAntideuteronFullEvent[ibin]++; - } - } - - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_CoalescenceCorr"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_CoalescenceCorr"), netDeuteronFullEvent, netProtonFullEvent); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_CoalescenceCorr"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_fullEvent_CoalescenceCorr"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity); - - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - - registryCorr.fill(HIST("q1d_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1p_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1d_q1p_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processCoalescenceCorr, "process coalescence correlation", false); - // Jet Pt resolution void processJetPtResolution(RecCollisionsMc const& collisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { From 00c8b619343ee879636752f814d1a09b6ad816bc Mon Sep 17 00:00:00 2001 From: Yash Patley <52608802+yashpatley@users.noreply.github.com> Date: Sat, 24 Oct 2026 21:26:19 +0530 Subject: [PATCH 424/648] [PWGCF] lambda r2 correlation MC generator process update (#18454) --- .../Tasks/lambdaR2Correlation.cxx | 114 +++++++++++++++--- 1 file changed, 100 insertions(+), 14 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index 1cddee698c9..cb608859637 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -266,9 +266,19 @@ struct LambdaTableProducer { Configurable cSelPrimaryParticle{"cSelPrimaryParticle", false, "Select primary particle"}; Configurable cSelTrueParticle{"cSelTrueParticle", false, "Select MC PDG matched particle"}; + // MC Generator + Configurable cFwdMult{"cFwdMult", 500, "Fwd multiplicity axis config"}; + Configurable cFT0AEtaMin{"cFT0AEtaMin", 3.5, "FT0A Eta Min"}; + Configurable cFT0AEtaMax{"cFT0AEtaMax", 4.9, "FT0A Eta Max"}; + Configurable cFT0CEtaMin{"cFT0CEtaMin", -3.3, "FT0C Eta Min"}; + Configurable cFT0CEtaMax{"cFT0CEtaMax", -2.1, "FT0C Eta Max"}; + Configurable cTPCEtaMin{"cTPCEtaMin", -1., "TPC Eta Min"}; + Configurable cTPCEtaMax{"cTPCEtaMax", 1., "TPC Eta Max"}; + // CCDB Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "ALICE CCDB URL"}; Configurable cPathCCDBRecoEff{"cPathCCDBRecoEff", "Users/y/ypatley/LBF/PP/RecoEfficiency", "Path for ccdb-object for reco efficiency"}; + Configurable cPathCCDBMcCent{"cPathCCDBMcCent", "Users/y/ypatley/LBF/PP/McCent/MultCent", "Path for ccdb-object for MC Gen multiplicity centrality calibration"}; Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; // Initialize CCDB Service @@ -290,14 +300,23 @@ struct LambdaTableProducer { // Initialize Global Variables float cent = 0.; TList* ccdbObjRecoEff = nullptr; - TList* ccdbObjMatchEff = nullptr; + TList* ccdbObjMcCent = nullptr; + TH1F* hMcFwdMult = nullptr; static constexpr auto SubDir = std::array{"QA/Lambda/", "QA/AntiLambda/"}; void init(InitContext const&) { - // initialize axis specifications + // CCDB + // Set CCDB url + ccdb->setURL(cUrlCCDB.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(nolaterthan.value); + + // Initialize axis specifications const AxisSpec axisCols(5, 0.5, 5.5, ""); const AxisSpec axisTrks(15, 0.5, 15.5, ""); + const AxisSpec axisFwdMult(cFwdMult, 0, cFwdMult, "N_{ch}^{FWD}"); const AxisSpec axisEffChecks(3, 0, 3, ""); const AxisSpec axisCent(100, 0, 100, "Centrality(%)"); const AxisSpec axisVarCent(cCentBins, "FT0C%"); @@ -363,7 +382,7 @@ struct LambdaTableProducer { histos.add("QA/Lambda/DCA/h3f_Dca_vs_pT_cent", "DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); // MC Generated Histograms - if (doprocessMCRecoGen || doprocessMCReco || doprocessMCGen) { + if (doprocessMCRecoGen || doprocessMCReco) { // DCA analysis histos.add("QA/Lambda/DCA/h3f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); histos.add("QA/Lambda/DCA/h3f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); @@ -400,14 +419,17 @@ struct LambdaTableProducer { histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassV0TopoSel, "kPassV0TopoSel"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllSelPassed, "kAllSelPassed"); + // MC generator histograms + if (doprocessMCGen) { + histos.add("Events/h1f_fwdmult", "Fwd multiplicity", kTH1F, {axisFwdMult}); + + // MC Gen Mult-Cent Calibration + ccdbObjMcCent = ccdb->getForTimeStamp(cPathCCDBMcCent.value, nolaterthan.value); + hMcFwdMult = dynamic_cast(ccdbObjMcCent->FindObject("hFwdMult")); + } + // Load correction factor if (cGetCorrectionFlag) { - // Set CCDB url - ccdb->setURL(cUrlCCDB.value); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(nolaterthan.value); - // Get CCDB object ccdbObjRecoEff = ccdb->getForTimeStamp(cPathCCDBRecoEff.value, nolaterthan.value); @@ -659,6 +681,59 @@ struct LambdaTableProducer { return effCorrFact; } + // MC generator multiplicity estimator and MB event selection + template + bool multEstimator(P const& particles, int& fwdMult) + { + int multFT0A = 0, multFT0C = 0, multTPC = 0; + + // Select charged particles + for (auto const& particle : particles) { + // Primary + if (!particle.isPhysicalPrimary()) { + continue; + } + + // FT0C + if (particle.eta() > cFT0CEtaMin && particle.eta() < cFT0CEtaMax) { + ++multFT0C; + } + + // FT0A + if (particle.eta() > cFT0AEtaMin && particle.eta() < cFT0AEtaMax) { + ++multFT0A; + } + + // Central barrel (ITS + TPC) + if (particle.eta() > cTPCEtaMin && particle.eta() < cTPCEtaMax) { + ++multTPC; + } + } + + int multFT0M = multFT0A + multFT0C; + fwdMult = multFT0M; + + // Minimum bias trigger (1 track in A side, 1 track in C side, 1 track in central barrel) + if (multFT0A > 0 && multFT0C > 0 && multTPC > 0) { + return true; + } + + return false; + } + + // MC generator centrality + float getMcCentrality(int mult) + { + float totalArea = hMcFwdMult->Integral(); + float area = 0.; + int nx = hMcFwdMult->GetNbinsX(); + int bin = hMcFwdMult->GetXaxis()->FindBin(mult + 0.5); + for (int i = bin; i <= nx; ++i) { + area += hMcFwdMult->GetBinContent(i); + } + return (area / totalArea) * 100.; + } + // Lambda QA template void fillLambdaQAHistos(C const& col, V const& v0, T const&) @@ -905,11 +980,7 @@ struct LambdaTableProducer { void processMCReco(soa::Join::iterator const& collision, aod::McCollisions const&, BCsRun3 const& bc, McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&) { - // check collision - if (!selCollision(collision)) { - return; - } - fillLambdaRecoTables(collision, bc, V0s, tracks); + fillLambdaRecoTables(collision, bc, V0s, tracks); } PROCESS_SWITCH(LambdaTableProducer, processMCReco, "Process for McReco DATA", false); @@ -926,6 +997,21 @@ struct LambdaTableProducer { void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { + // Multiplicity estimation + int fwdMult = 0; + bool eventTrig = multEstimator(mcParticles, fwdMult); + + // MB event selection + if (!eventTrig) { + return; + } + + // Fill fwd multiplicity histogram + histos.fill(HIST("Events/h1f_fwdmult"), fwdMult); + + // Get centrality + cent = getMcCentrality(fwdMult); + // Fill McGen Collision Table lambdaMCGenCollisionTable(cent, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); From f9618d03a40e13a8f96401605f99d51ccc108f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Sat, 24 Oct 2026 17:58:18 +0200 Subject: [PATCH 425/648] [PWGCF] Remove HfHelper instances (#18434) --- PWGCF/Femto/Core/charmHadronBuilder.h | 26 ++++--- .../femtoUniverseProducerTask.cxx | 67 +++++++++---------- .../TableProducer/femtoWorldProducerTask.cxx | 25 ++++--- PWGCF/TableProducer/filter2Prong.cxx | 7 +- 4 files changed, 60 insertions(+), 65 deletions(-) diff --git a/PWGCF/Femto/Core/charmHadronBuilder.h b/PWGCF/Femto/Core/charmHadronBuilder.h index 63a0c22b892..feda7ce2d0e 100644 --- a/PWGCF/Femto/Core/charmHadronBuilder.h +++ b/PWGCF/Femto/Core/charmHadronBuilder.h @@ -289,7 +289,7 @@ class CharmHadronSelection : public baseselection::BaseSelectionevaluateObservable(kImpactParameterProngSqSumMin, candidate.impactParameterProngSqSum()); } else { this->evaluateObservable(kImpactParameterProductMax, candidate.impactParameter0() * candidate.impactParameter1()); - this->evaluateObservable(kCosThetaStarMax, mHfHelper.cosThetaStarD0(candidate)); + this->evaluateObservable(kCosThetaStarMax, HfHelper::cosThetaStarD0(candidate)); } this->template assembleBitmask(); } @@ -359,9 +359,9 @@ class CharmHadronSelection : public baseselection::BaseSelection()) { - y = mHfHelper.yLc(candidate); + y = HfHelper::yLc(candidate); } else { - y = mHfHelper.yD0(candidate); + y = HfHelper::yD0(candidate); } p = y > mYMin; this->template fillFilter(kYMin, p); @@ -387,7 +387,6 @@ class CharmHadronSelection : public baseselection::BaseSelection(collisionProducts, col); int64_t const protonDauIndex = trackBuilder.template getDaughterIndex(protonProng, trackProducts, collisionBuilder); @@ -594,8 +593,8 @@ class CharmHadronBuilder // remap the prongs onto the accepted hypothesis so that the proton is always first auto const& protonProng = isPKPi ? prong0 : prong2; auto const& pionProng = isPKPi ? prong2 : prong0; - float const mass = isPKPi ? mHfHelper.invMassLcToPKPi(candidate) : mHfHelper.invMassLcToPiKP(candidate); - float const massCompeting = isPKPi ? mHfHelper.invMassLcToPiKP(candidate) : mHfHelper.invMassLcToPKPi(candidate); + float const mass = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + float const massCompeting = isPKPi ? HfHelper::invMassLcToPiKP(candidate) : HfHelper::invMassLcToPKPi(candidate); collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); @@ -634,9 +633,9 @@ class CharmHadronBuilder int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, collisionBuilder); if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), mHfHelper.invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); } else { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), mHfHelper.invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); } } @@ -683,9 +682,9 @@ class CharmHadronBuilder int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), mHfHelper.invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); } else { - this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), mHfHelper.invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); } mcBuilder.template fillMcD0WithLabel(candidate, tracks, mcParticles, mcCols, mcProducts); } @@ -711,7 +710,6 @@ class CharmHadronBuilder private: CharmHadronSelection mCharmHadronSelection; - HfHelper mHfHelper; bool mProduceD0s = false; bool mProduceD0Masks = false; diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx index d65918ea5f0..57fba9c23e9 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx @@ -382,7 +382,6 @@ struct FemtoUniverseProducerTask { Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle (track) momentum > Confmom"}; } ConfPIDBitmask; - HfHelper hfHelper; bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { if (mom < ConfPhiSelection.confMomKaon03) { // 0.0-0.3 @@ -829,17 +828,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(-999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -869,17 +868,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(particle.flagMcMatchRec(), -999., -999., -999., -999., -999., -999., -999., -999., originMcReco, -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -1719,7 +1718,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1737,18 +1736,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1842,7 +1841,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1859,18 +1858,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1974,7 +1973,7 @@ struct FemtoUniverseProducerTask { } } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -2000,18 +1999,18 @@ struct FemtoUniverseProducerTask { childIDs[1] = 0; if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx index 0460e749cb0..78758a6c380 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx @@ -240,7 +240,6 @@ struct femtoWorldProducerTask { int mRunNumber; float mMagField; Service ccdb; /// Accessing the CCDB - HfHelper hfHelper; void init(InitContext&) { @@ -1168,7 +1167,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -1297,11 +1296,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, @@ -2115,7 +2114,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -2246,11 +2245,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, diff --git a/PWGCF/TableProducer/filter2Prong.cxx b/PWGCF/TableProducer/filter2Prong.cxx index 2c90a269e9e..e33fc8cae64 100644 --- a/PWGCF/TableProducer/filter2Prong.cxx +++ b/PWGCF/TableProducer/filter2Prong.cxx @@ -179,7 +179,6 @@ struct Filter2Prong { ConfigurableAxis axisVertexMix{"axisVertexMix", {7, -7, 7}, "vertex axis for phi event mixing"}; ConfigurableAxis axisMultiplicityMix{"axisMultiplicityMix", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for phi event mixing"}; - HfHelper hfHelper; Produces output2ProngTracks; Produces output2ProngTrackmls; @@ -246,7 +245,7 @@ struct Filter2Prong { // look-up the collision id if ((c.hfflag() & (1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) == 0) continue; - if (cfgYMax >= 0.0f && std::abs(hfHelper.yD0(c)) > cfgYMax) + if (cfgYMax >= 0.0f && std::abs(HfHelper::yD0(c)) > cfgYMax) continue; if constexpr (std::experimental::is_detected::value) { if (std::abs(c.flagMcMatchRec()) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) @@ -255,7 +254,7 @@ struct Filter2Prong { if (c.isSelD0() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); for (const float val : c.mlProbD0()) @@ -269,7 +268,7 @@ struct Filter2Prong { if (c.isSelD0bar() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0barToKPi(c), + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0barToKPi(c), c.isSelD0() > 0 ? aod::cf2prongtrack::D0barToKPi : aod::cf2prongtrack::D0barToKPiExclusive); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); From 3876db7db98d60cf793fff646826f867ad923380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:55:36 +0200 Subject: [PATCH 426/648] [PWGUD] Fix casts (#18392) --- PWGUD/AQC/udQC.cxx | 2 +- PWGUD/AQC/udQCmidRap.cxx | 2 +- PWGUD/AQC/udQcMuon.cxx | 2 +- PWGUD/TableProducer/DGBCCandProducer.cxx | 14 +++++++------- PWGUD/TableProducer/SGCandProducer.cxx | 4 ++-- PWGUD/TableProducer/UPCCandidateProducer.cxx | 10 +++++----- PWGUD/TableProducer/dgCandProducer.cxx | 2 +- PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx | 6 +++--- PWGUD/TableProducer/upcCandProducerMuon.cxx | 4 ++-- PWGUD/TableProducer/upcCandProducerSemiFwd.cxx | 4 ++-- PWGUD/Tasks/dgCandAnalyzer.cxx | 2 +- PWGUD/Tasks/diffMCDataScanner.cxx | 2 +- PWGUD/Tasks/diffMCQA.cxx | 2 +- PWGUD/Tasks/diffQA.cxx | 2 +- PWGUD/Tasks/flowLongRangeCorrUpc.cxx | 4 ++-- PWGUD/Tasks/upcCandidateAnalyzer.cxx | 10 +++++----- PWGUD/Tasks/upcMft.cxx | 2 +- PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx | 8 ++++---- PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx | 6 +++--- PWGUD/Tasks/upcVmRof.cxx | 2 +- 20 files changed, 45 insertions(+), 45 deletions(-) diff --git a/PWGUD/AQC/udQC.cxx b/PWGUD/AQC/udQC.cxx index 44c2f03a2e8..24117f9d6e2 100644 --- a/PWGUD/AQC/udQC.cxx +++ b/PWGUD/AQC/udQC.cxx @@ -97,7 +97,7 @@ struct UDQC { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/AQC/udQCmidRap.cxx b/PWGUD/AQC/udQCmidRap.cxx index 6a2ea533d4c..1aaff2bb186 100644 --- a/PWGUD/AQC/udQCmidRap.cxx +++ b/PWGUD/AQC/udQCmidRap.cxx @@ -86,7 +86,7 @@ struct UDQCmid { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/AQC/udQcMuon.cxx b/PWGUD/AQC/udQcMuon.cxx index 9f09ee2d2c9..995fcaa3fec 100644 --- a/PWGUD/AQC/udQcMuon.cxx +++ b/PWGUD/AQC/udQcMuon.cxx @@ -98,7 +98,7 @@ struct UdQcMuon { void init(InitContext& context) { - diffCuts = (DGCutparHolder)dgCuts; + diffCuts = DGCutparHolder(dgCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/TableProducer/DGBCCandProducer.cxx b/PWGUD/TableProducer/DGBCCandProducer.cxx index cf62e50c84d..4719805c3e2 100644 --- a/PWGUD/TableProducer/DGBCCandProducer.cxx +++ b/PWGUD/TableProducer/DGBCCandProducer.cxx @@ -131,8 +131,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambTracksSlice.begin().bc().begin().globalBC(); LOGF(debug, "First compatible BC %d Track time %f", firstCompatibleBC, track.trackTime()); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!track.has_collision()) { @@ -147,7 +147,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating tracksInBCList with %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } @@ -215,8 +215,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambFwdTracksSlice.begin().bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!fwdTrack.has_collision()) { @@ -231,7 +231,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating fwdTracksInBCList with %d", closestBC); - fwdTracksInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } @@ -385,7 +385,7 @@ struct DGBCCandProducer { void init(InitContext& context) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); if (context.mOptions.get("processTinBCs")) { registry.add("table/candCase", "#candCase", {HistType::kTH1F, {{4, -0.5, 3.5}}}); diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index 6574d613986..3e407e3c0b5 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -392,8 +392,8 @@ struct SGCandProducer { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); - sameCuts = (SGCutParHolder)SGCuts; - fitCuts = (FITCutParHolder)FITCuts; + sameCuts = SGCutParHolder(SGCuts); + fitCuts = FITCutParHolder(FITCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/UPCCandidateProducer.cxx b/PWGUD/TableProducer/UPCCandidateProducer.cxx index f59c92ca55c..b5bcd1cd848 100644 --- a/PWGUD/TableProducer/UPCCandidateProducer.cxx +++ b/PWGUD/TableProducer/UPCCandidateProducer.cxx @@ -157,7 +157,7 @@ struct UpcCandProducer { fwdSelectors.resize(upchelpers::kNFwdSels - 1, false); barrelSelectors.resize(upchelpers::kNBarrelSels - 1, false); - upcCuts = (UPCCutparHolder)inputCuts; + upcCuts = UPCCutparHolder(inputCuts); // initialize RCT flag checker if (rctLabel.value != "none" && rctLabel.value != "muon" && rctLabel.value != "muon_glo") { @@ -1990,7 +1990,7 @@ struct UpcCandProducer { fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, - (o2::aod::McTrackLabels*)nullptr, (o2::aod::McFwdTrackLabels*)nullptr); + nullptr, nullptr); } // create candidates for central region @@ -2007,7 +2007,7 @@ struct UpcCandProducer { createCandidatesCentral(barrelTracks, ambBarrelTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McTrackLabels*)nullptr); + nullptr); } // MC processors @@ -2076,7 +2076,7 @@ struct UpcCandProducer { createCandidatesFwd(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardMC(ForwardTracks const& fwdTracks, @@ -2116,7 +2116,7 @@ struct UpcCandProducer { createCandidatesFwdGlobal(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardGlobalMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/dgCandProducer.cxx b/PWGUD/TableProducer/dgCandProducer.cxx index 97a3c423c3e..284f48f5e8e 100644 --- a/PWGUD/TableProducer/dgCandProducer.cxx +++ b/PWGUD/TableProducer/dgCandProducer.cxx @@ -456,7 +456,7 @@ struct DgCandProducer { ccdb->setFatalWhenNull(false); // DGCuts - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx index c418fd7c9c1..cdd76286241 100644 --- a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx @@ -124,7 +124,7 @@ struct UpcCandProducerGlobalMuon { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts() || fEnableMFT) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -610,7 +610,7 @@ struct UpcCandProducerGlobalMuon { } // Initialize FwdDCAFitter with magnetic field if (fEnableMFT) { - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); static constexpr double kCenterMFT[3] = {0, 0, -61.4}; float bz = field->getBz(kCenterMFT); LOG(info) << "FwdDCAFitter magnetic field at MFT center: bZ = " << bz; @@ -922,7 +922,7 @@ struct UpcCandProducerGlobalMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/upcCandProducerMuon.cxx b/PWGUD/TableProducer/upcCandProducerMuon.cxx index 0ac8458d60f..b9f5edc7007 100644 --- a/PWGUD/TableProducer/upcCandProducerMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerMuon.cxx @@ -88,7 +88,7 @@ struct UpcCandProducerMuon { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts()) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -520,7 +520,7 @@ struct UpcCandProducerMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx index cb6146aee7b..d99c57f5dc9 100644 --- a/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx +++ b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx @@ -108,7 +108,7 @@ struct UpcCandProducerSemiFwd { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts()) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -653,7 +653,7 @@ struct UpcCandProducerSemiFwd { { fDoMC = false; createCandidates(fwdTracks, ambFwdTracks, barrelTracks, ambBarrelTracks, bcs, collisions, fv0s, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr, (o2::aod::McTrackLabels*)nullptr); + nullptr, nullptr); } void processSemiFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/Tasks/dgCandAnalyzer.cxx b/PWGUD/Tasks/dgCandAnalyzer.cxx index 79b5b6c5f02..60547236485 100644 --- a/PWGUD/Tasks/dgCandAnalyzer.cxx +++ b/PWGUD/Tasks/dgCandAnalyzer.cxx @@ -144,7 +144,7 @@ struct DGCandAnalyzer { // PDG pdg = TDatabasePDG::Instance(); - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); grsel.init(goodRunsFile); diff --git a/PWGUD/Tasks/diffMCDataScanner.cxx b/PWGUD/Tasks/diffMCDataScanner.cxx index 6b945059121..369eca48921 100644 --- a/PWGUD/Tasks/diffMCDataScanner.cxx +++ b/PWGUD/Tasks/diffMCDataScanner.cxx @@ -360,7 +360,7 @@ struct TrackTypes { } for (auto ii = 0; ii < nTypes; ii++) { if ((fwdtrack.trackType() & (1 << ii)) > 0) { - registry.get(HIST("FwdType"))->Fill((Double_t)(ii + 2), 0., 1.); + registry.get(HIST("FwdType"))->Fill(static_cast(ii + 2), 0., 1.); } } } diff --git a/PWGUD/Tasks/diffMCQA.cxx b/PWGUD/Tasks/diffMCQA.cxx index c6b1f650db2..9c32645a1bd 100644 --- a/PWGUD/Tasks/diffMCQA.cxx +++ b/PWGUD/Tasks/diffMCQA.cxx @@ -88,7 +88,7 @@ struct DiffMCQA { { maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMCTruth")) { diff --git a/PWGUD/Tasks/diffQA.cxx b/PWGUD/Tasks/diffQA.cxx index edecda89087..adc79e0c37f 100644 --- a/PWGUD/Tasks/diffQA.cxx +++ b/PWGUD/Tasks/diffQA.cxx @@ -98,7 +98,7 @@ struct DiffQA { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx index 465285bd039..e5766de17f2 100644 --- a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx +++ b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx @@ -115,9 +115,9 @@ struct FlowLongRangeCorrUpc { // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); Filter collisionFilter = (cfgGapSideMerge == true) - ? ((aod::udcollision::gapSide == (uint8_t)0 || aod::udcollision::gapSide == (uint8_t)1) && + ? ((aod::udcollision::gapSide == static_cast(0) || aod::udcollision::gapSide == static_cast(1)) && (aod::upcservice::truegapside == 0 || aod::upcservice::truegapside == 1)) - : ((aod::udcollision::gapSide == (uint8_t)cfgGapSide) && + : ((aod::udcollision::gapSide == static_cast(cfgGapSide)) && (aod::upcservice::truegapside == cfgGapSide)); using UDTracksFull = soa::Filtered>; diff --git a/PWGUD/Tasks/upcCandidateAnalyzer.cxx b/PWGUD/Tasks/upcCandidateAnalyzer.cxx index dcf23797803..50a80cc11dd 100644 --- a/PWGUD/Tasks/upcCandidateAnalyzer.cxx +++ b/PWGUD/Tasks/upcCandidateAnalyzer.cxx @@ -608,7 +608,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, &mcParticles, (o2::aod::UDMcTrackLabels*)nullptr, &mcFwdTrackLabels); + processCandidate<0>(cand, tr1, tr2, &mcParticles, nullptr, &mcFwdTrackLabels); } } @@ -629,7 +629,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<0>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -687,7 +687,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<1>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<1>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -713,7 +713,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, nullptr); } } @@ -734,7 +734,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } diff --git a/PWGUD/Tasks/upcMft.cxx b/PWGUD/Tasks/upcMft.cxx index 6349589e6b4..18bb62bb7ea 100644 --- a/PWGUD/Tasks/upcMft.cxx +++ b/PWGUD/Tasks/upcMft.cxx @@ -127,7 +127,7 @@ struct UpcMftRec { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; diff --git a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx index 6503f39c085..36b8d605947 100644 --- a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx +++ b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx @@ -447,7 +447,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() < cutGapAMyEnergyZNA) && (collision.energyCommonZNC() >= cutGapAMyEnergyZNC))) { return false; } // 0n - A side && Xn - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(0)) { + if (useGapSideVariable && collision.gapSide() != static_cast(0)) { return false; } if (useFV0 && collision.totalFV0AmplitudeA() > cutGapAFV0Amplitude) { @@ -464,7 +464,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() >= cutGapCMyEnergyZNA) && (collision.energyCommonZNC() < cutGapCMyEnergyZNC))) { return false; } // Xn - A side && 0n - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(1)) { + if (useGapSideVariable && collision.gapSide() != static_cast(1)) { return false; } if (useFT0CGapC && (collision.totalFT0AmplitudeC() > cutGapCMyAmplitudeFT0C)) { @@ -1007,8 +1007,8 @@ struct UpcPhotonuclearAnalysisJMG { return; } histos.fill(HIST("Events/hCollisionsFlow"), 11); - bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(0); - bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(1); + bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(0); + bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(1); if (cutGapSideAVariable || cutGapSideCVariable) { return; } diff --git a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx index 7e4996c13ba..7352dafee7c 100644 --- a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx +++ b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx @@ -538,13 +538,13 @@ struct upcQuarkoniaCentralBarrel { // Check the number of Protons and antiProtons // needs at least 1 of each if (!buildSameSignPairs && nProtons >= 1 && nAntiProtons >= 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); } if (buildSameSignPairs && nProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selProtonIndices, selGapSide); } if (buildSameSignPairs && nAntiProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); } } diff --git a/PWGUD/Tasks/upcVmRof.cxx b/PWGUD/Tasks/upcVmRof.cxx index 985a9b1ab1d..715c573afb6 100644 --- a/PWGUD/Tasks/upcVmRof.cxx +++ b/PWGUD/Tasks/upcVmRof.cxx @@ -322,7 +322,7 @@ struct UpcVmRof { auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", sor); rofShift = alppar->roFrameBiasInBC; rofLength = alppar->roFrameLengthInBC; - rofPerOrbit = static_cast(o2::constants::lhc::LHCMaxBunches / rofLength); + rofPerOrbit = (o2::constants::lhc::LHCMaxBunches / rofLength); } //-------------------------------------------------------------------------------- From 4c3d2bdf0e2fc5439474e8b11943d9fae18c0463 Mon Sep 17 00:00:00 2001 From: "Q.Y. Xia" <91366503+huinaibing@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:01:50 +0800 Subject: [PATCH 427/648] [PWGCF] Add ITS PID && tpcNclsPID cut (#18453) --- PWGCF/Flow/Tasks/pidFlowPtCorr.cxx | 793 ++++++++++++----------------- 1 file changed, 332 insertions(+), 461 deletions(-) diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index 60744d63d4a..c73b6ae59f9 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -53,11 +53,10 @@ #include #include #include +#include #include #include -#include - #include #include #include @@ -219,8 +218,8 @@ struct PidFlowPtCorr { Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutChi2prTPCcls{"cfgCutChi2prTPCcls", 2.5, "Chi2 per TPC clusters"}; - Configurable cfgDeltaPhiLocDen{"cfgDeltaPhiLocDen", 3, "Number of delta phi for local density, 200 bins in 2 pi"}; Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgUseMultiplicity{"cfgUseMultiplicity", false, "Use raw PV-contributor count in |eta| < 0.8 instead of centrality for output axes and NUE lookup; configure axisMultiplicity and disable cfgDoMultPVCut"}; struct : ConfigurableGroup { std::string prefix = "trkQualityOpts"; @@ -269,8 +268,6 @@ struct PidFlowPtCorr { Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; } dcaCutOpts; - Configurable> cfgNSigmapid{"cfgNSigmapid", (std::vector{3, 3, 3, 9, 9, 9, 9, 9, 9}), "tpc, tof and its NSigma for Pion Proton Kaon"}; - struct : ConfigurableGroup { std::string prefix = "correctionPathOpts"; Configurable> cfgAcceptancePath{"cfgAcceptancePath", (std::vector{"Users/f/fcui/NUA/NUAREFPartical", "Users/f/fcui/NUA/NUAK0s", "Users/f/fcui/NUA/NUALambda", "Users/f/fcui/NUA/NUAXi", "Users/f/fcui/NUA/NUAOmega"}), "CCDB path to acceptance object"}; @@ -287,14 +284,13 @@ struct PidFlowPtCorr { // switch struct : ConfigurableGroup { std::string prefix = "switchsOpts"; - Configurable cfgDoLocDenCorr{"cfgDoLocDenCorr", false, "do local density corr"}; Configurable cfgOutputrunbyrun{"cfgOutputrunbyrun", false, "OPEN IF USE FUNCTION(fillcorrectiongraph) Fill and output NUA weights run by run"}; Configurable cfgOutPutMC{"cfgOutPutMC", false, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; Configurable cfgOutputQA{"cfgOutputQA", false, "OPEN IF USE FUNCTION(detectorPidQa) do QA"}; Configurable cfgDebugMyCode{"cfgDebugMyCode", false, "output some graph for code debug"}; Configurable cfgOutPutPtSpectra{"cfgOutPutPtSpectra", false, "output pt spectra for data, MC and RECO"}; Configurable cfgCheck2MethodDiff{"cfgCheck2MethodDiff", false, "check difference between v2' && v2''"}; - Configurable cfgClosureTest{"cfgClosureTest", 0, "choose (val) percent particle from charged to pass Pion PID selection"}; + Configurable cfgClosureTest{"cfgClosureTest", 0, "Nonzero enables extra pion mean-pT weight QA profiles"}; Configurable cfgOutPutMC1D{"cfgOutPutMC1D", true, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; Configurable cfgAddPidResponseMatrixHistograms{"cfgAddPidResponseMatrixHistograms", false, "Add PID response matrix histograms; enable together with processPidResponseMatrix"}; Configurable cfgAddC22DeltaPtHistograms{"cfgAddC22DeltaPtHistograms", false, "Add histograms for processDataC22DeltaPt; enable together with processDataC22DeltaPt"}; @@ -313,37 +309,17 @@ struct PidFlowPtCorr { Configurable cfgUseNUAWithPt{"cfgUseNUAWithPt", false, "false: use original GFWWeights (phi,eta,vz) NUA; true: use THnSparse (phi,eta,vz,pt) NUA loaded from cfgAcceptancePathWithPt"}; } switchsOpts; - /** - * @brief cfg for PID pt range - * @details default datas are from run2, note that separate pi-k and k-p needs to use difference pt range - */ - // separate pi and k + // TOF momentum ranges used by circle PID. struct : ConfigurableGroup { std::string prefix = "pidPtRangeOpts"; - Configurable cfgPtMin4ITSPiKa{"cfgPtMin4ITSPiKa", 0.2, "pt min for ITS to separate pi and k"}; - Configurable cfgPtMax4ITSPiKa{"cfgPtMax4ITSPiKa", 0.8, "pt max for ITS to separate pi and k"}; Configurable cfgPtMin4TOFPiKa{"cfgPtMin4TOFPiKa", 0.5, "pt min for TOF to separate pi and k"}; Configurable cfgPtMax4TOFPiKa{"cfgPtMax4TOFPiKa", 3.0, "pt max for TOF to separate pi and k"}; - Configurable cfgPtMin4TPCPiKa{"cfgPtMin4TPCPiKa", 0.2, "pt min for TPC to separate pi and k"}; - Configurable cfgPtMax4TPCPiKa{"cfgPtMax4TPCPiKa", 0.6, "pt max for TPC to separate pi and k"}; - // end separate pi and k - - // separate k-p - Configurable cfgPtMin4ITSKaPr{"cfgPtMin4ITSKaPr", 0.4, "pt min for ITS to separate k and p"}; - Configurable cfgPtMax4ITSKaPr{"cfgPtMax4ITSKaPr", 1.4, "pt max for ITS to separate k and p"}; Configurable cfgPtMin4TOFKaPr{"cfgPtMin4TOFKaPr", 0.6, "pt min for TOF to separate k and p"}; Configurable cfgPtMax4TOFKaPr{"cfgPtMax4TOFKaPr", 3.0, "pt max for TOF to separate k and p"}; - Configurable cfgPtMin4TPCKaPr{"cfgPtMin4TPCKaPr", 0.2, "pt min for TPC to separate k and p"}; - Configurable cfgPtMax4TPCKaPr{"cfgPtMax4TPCKaPr", 1.0, "pt max for TPC to separate k and p"}; } pidPtRangeOpts; - // end separate k-p - // end cfg for PID pt range struct : ConfigurableGroup { std::string prefix = "circleCutOpts"; - // Switch to enable/disable circular cut PID - Configurable cfgUseCircleCutPID{"cfgUseCircleCutPID", true, "Enable circular cut PID method"}; - // TOF pT threshold: above this pt, TOF signal is required Configurable cfgCircleCutTofPtCut{"cfgCircleCutTofPtCut", 0.5, "TOF pT threshold for requiring TOF signal"}; @@ -352,12 +328,19 @@ struct PidFlowPtCorr { Configurable cfgCircleCutSigmaKa{"cfgCircleCutSigmaKa", 4.0, "Circular cut radius squared for Kaon (TPC+TOF)"}; Configurable cfgCircleCutSigmaPr{"cfgCircleCutSigmaPr", 4.0, "Circular cut radius squared for Proton (TPC+TOF)"}; - // TPC-only fallback cut values (|nsigma| < cut) - Configurable cfgCircleCutTPCPi{"cfgCircleCutTPCPi", 2.0, "TPC-only nsigma cut for Pion"}; - Configurable cfgCircleCutTPCKa{"cfgCircleCutTPCKa", 2.0, "TPC-only nsigma cut for Kaon"}; - Configurable cfgCircleCutTPCPr{"cfgCircleCutTPCPr", 2.0, "TPC-only nsigma cut for Proton"}; + // TPC fallback cuts, combined with ITS below cfgCircleCutITSPtMax. + Configurable cfgCircleCutTPCPi{"cfgCircleCutTPCPi", 2.0, "TPC fallback nsigma cut for Pion"}; + Configurable cfgCircleCutTPCKa{"cfgCircleCutTPCKa", 2.0, "TPC fallback nsigma cut for Kaon"}; + Configurable cfgCircleCutTPCPr{"cfgCircleCutTPCPr", 2.0, "TPC fallback nsigma cut for Proton"}; + Configurable cfgCircleCutITSPtMax{"cfgCircleCutITSPtMax", 0.5, "Require ITS PID below this pT (GeV/c); 0 disables"}; + Configurable cfgCircleCutITSPi{"cfgCircleCutITSPi", 2.0, "Low-pT ITS absolute nsigma cut for Pion"}; + Configurable cfgCircleCutITSKa{"cfgCircleCutITSKa", 2.0, "Low-pT ITS absolute nsigma cut for Kaon"}; + Configurable cfgCircleCutITSPr{"cfgCircleCutITSPr", 2.0, "Low-pT ITS absolute nsigma cut for Proton"}; } circleCutOpts; + Configurable cfgTPCPIDClustersMinFunction{"cfgTPCPIDClustersMinFunction", "0", "Minimum TPC PID clusters as a ROOT formula; x = tpcNClsFound(), numeric coefficients, 0 imposes no lower bound"}; + std::unique_ptr fTPCPIDClustersMin; + struct : ConfigurableGroup { std::string prefix = "particleAbundanceOpts"; ConfigurableAxis cfgaxisAbundancePi{"cfgaxisAbundancePi", {100, 0, 1100}, "axis for Abundance Pi"}; @@ -375,22 +358,22 @@ struct PidFlowPtCorr { ConfigurableAxis cfgaxisTPCDedx{"cfgaxisTPCDedx", {500, 0.0f, 1000.0f}, "d#it{E}/d#it{x} axis for detector-PID QA"}; ConfigurableAxis cfgaxisTOFDelta{"cfgaxisTOFDelta", {200, -1000.0f, 1000.0f}, "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} axis for detector-PID QA"}; ConfigurableAxis cfgaxisTOFTime{"cfgaxisTOFTime", {10000, 0.0f, 2.0e6f}, "TOF #it{t} - #it{t}_{ev} (ps) axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTPCPIDClusters{"cfgaxisTPCPIDClusters", {201, -0.5, 200.5}, "TPC clusters used for PID"}; + ConfigurableAxis cfgaxisPIDNSigma{"cfgaxisPIDNSigma", {240, -12., 12.}, "Signed TPC n-sigma axis for PID-quality QA"}; + ConfigurableAxis cfgaxisPIDMomentum{"cfgaxisPIDMomentum", {1000, 0., 10.}, "Total momentum for ITS cluster-size QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSNSigma{"cfgaxisITSNSigma", {161, -12.075, 12.075}, "Signed ITS n-sigma; zero at bin center"}; + ConfigurableAxis cfgaxisITSPt{"cfgaxisITSPt", {300, 0., 3.}, "Fine pT axis for ITS response QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSClusterSize{"cfgaxisITSClusterSize", {101, -0.075, 15.075}, "Truncated mean ITS cluster size divided by cosh(eta)"}; ConfigurableAxis cfgaxisMeanPt{"cfgaxisMeanPt", {300, 0, 3}, "pt (GeV)"}; ConfigurableAxis cfgaxisNch{"cfgaxisNch", {3000, 0.5, 3000.5}, "Nch"}; - ConfigurableAxis cfgaxisLocalDensity{"cfgaxisLocalDensity", {200, 0, 600}, "local density"}; ConfigurableAxis cfgaxisRun{"cfgaxisRun", {7, 0, 7}, "axis of runs in the data"}; - Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.7217476707, 0.7384792571, 0.7542625668, 0.7640680200, 0.7701951667, 0.7755299053, 0.7805901710, 0.7849446786, 0.7957356586, 0.8113039262, 0.8211968966, 0.8280558878, 0.8329342135}, "parameter 0 for track density efficiency correction"}; - Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-2.169488e-05, -2.191913e-05, -2.295484e-05, -2.556538e-05, -2.754463e-05, -2.816832e-05, -2.846502e-05, -2.843857e-05, -2.705974e-05, -2.477018e-05, -2.321730e-05, -2.203315e-05, -2.109474e-05}, "parameter 1 for track density efficiency correction"}; - Configurable> cfgTrackDensityV2P{"cfgTrackDensityV2P", std::vector{0.0186111, 0.00351907, -4.38264e-05, 1.35383e-07, -3.96266e-10}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV3P{"cfgTrackDensityV3P", std::vector{0.0174056, 0.000703329, -1.45044e-05, 1.91991e-07, -1.62137e-09}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV4P{"cfgTrackDensityV4P", std::vector{0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10}, "parameter of v2(cent) for track density efficiency correction"}; struct : ConfigurableGroup { std::string prefix = "meanptC22GraphOpts"; ConfigurableAxis cfgaxisBootstrap{"cfgaxisBootstrap", {30, 0, 30}, "cfgaxisBootstrap"}; } meanptC22GraphOpts; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90}, "Centrality (%)"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90}, "Centrality (%) or raw PV multiplicity when cfgUseMultiplicity is enabled"}; // configurable @@ -451,7 +434,6 @@ struct PidFlowPtCorr { std::vector cfgAcceptanceWithPt; std::vector cfgMultPVCutPara; - std::vector cfgNSigma; std::vector runNumbers; enum MyParticleType { @@ -541,34 +523,199 @@ struct PidFlowPtCorr { TF1* fT0AV0AMean = nullptr; TF1* fT0AV0ASigma = nullptr; - // Declare the pt, mult and phi Axis; + // Index order is pion, kaon, proton; stage 0 is before PID, stage 1 is + // after the current unique PID assignment. The registry owns the objects. + struct PidQualityQAHists { + std::array tpcPIDClustersVsPt{}; + std::array tpcNSigmaVsPIDClusters{}; + std::array itsNSigmaVsPt{}; + std::array tpcVsITSNSigma{}; + TH2* itsClusterSizeVsP = nullptr; + TH2* itsClusterSizeVsPt = nullptr; + TProfile* meanITSClusterSizeVsP = nullptr; + TProfile* meanITSClusterSizeVsPt = nullptr; + }; + std::array pidQualityQAHists{}; - std::vector funcEff; - TH1D* hFindPtBin = nullptr; - TF1* funcV2 = nullptr; - TF1* funcV3 = nullptr; - TF1* funcV4 = nullptr; + const char* getActivityAxisTitle() const + { + return cfgUseMultiplicity.value ? "N_{tracks}^{PV} (|#eta| < 0.8)" : "Centrality (%)"; + } - // hists for QA runbyrun - struct qaHist { - TH1* hPhiRunByRunBefore = nullptr; - TH1* hPhiRunByRunAfter = nullptr; + void initPidQualityQA() + { + const AxisSpec ptAxis{cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec itsPtAxis{cfgaxisITSPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec etaAxis{cfgaxisEta, "#eta"}; + const AxisSpec pAxis{cfgaxisPIDMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec centAxis{axisMultiplicity, getActivityAxisTitle()}; + const AxisSpec clusterAxis{cfgaxisTPCPIDClusters, "TPC clusters used for PID"}; + const AxisSpec foundClusterAxis{cfgaxisTPCPIDClusters, "TPC found clusters"}; + const AxisSpec tpcNSigmaAxis{cfgaxisPIDNSigma, "n#sigma_{TPC}"}; + const AxisSpec itsNSigmaAxis{cfgaxisITSNSigma, "n#sigma_{ITS}"}; + const AxisSpec itsSizeAxis{cfgaxisITSClusterSize, "#LTITS cluster size#GT_{trunc}/cosh(#eta)"}; + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersAll", "Before PID;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified", "PID failed or ambiguous;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound", "Before PID;TPC found clusters;TPC clusters used for PID", {HistType::kTH2F, {foundClusterAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAll", "Before PID;#it{p} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {pAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt", "Before PID;#it{p}_{T} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {itsPtAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta", "Before PID: ITS response versus momentum and eta", {HistType::kTHnSparseF, {pAxis, etaAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsResponseStatus", "ITS QA validity;status;tracks", {HistType::kTH1D, {{4, 0., 4.}}}); + auto* statusAxis = registry.get(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"))->GetXaxis(); + statusAxis->SetBinLabel(1, "Fewer than 2 cluster sizes"); + statusAxis->SetBinLabel(2, "Invalid momentum or eta"); + statusAxis->SetBinLabel(3, "Nonfinite ITS response"); + statusAxis->SetBinLabel(4, "All ITS hypotheses finite"); + + constexpr std::array species{"Pi", "Ka", "Pr"}; + constexpr std::array stages{"BeforePID", "AfterPID"}; + std::array expectedITSResponse{}; + for (std::size_t i = 0; i < species.size(); ++i) { + auto& h = pidQualityQAHists[i]; + for (std::size_t stage = 0; stage < stages.size(); ++stage) { + const std::string path = std::string("DetectorPidQA/PIDQuality/") + stages[stage] + "/" + species[i] + "/"; + h.tpcPIDClustersVsPt[stage] = registry.add(path + "tpcPIDClustersVsPt", "TPC PID cluster count", HistType::kTH2F, {ptAxis, clusterAxis}).get(); + h.tpcNSigmaVsPIDClusters[stage] = registry.add(path + "tpcNSigmaVsPIDClusters", "TPC PID response versus PID cluster count", HistType::kTHnSparseF, {ptAxis, centAxis, clusterAxis, tpcNSigmaAxis}).get(); + h.itsNSigmaVsPt[stage] = registry.add(path + "itsNSigmaVsPt", "ITS response for this mass hypothesis", HistType::kTH2F, {itsPtAxis, itsNSigmaAxis}).get(); + h.tpcVsITSNSigma[stage] = registry.add(path + "tpcVsITSNSigma", "TPC versus ITS response for this mass hypothesis", HistType::kTHnSparseF, {ptAxis, centAxis, tpcNSigmaAxis, itsNSigmaAxis}).get(); + } + const std::string path = std::string("DetectorPidQA/PIDQuality/AfterPID/") + species[i] + "/"; + h.itsClusterSizeVsP = registry.add(path + "itsClusterSizeVsP", "ITS cluster size after current PID", HistType::kTH2F, {pAxis, itsSizeAxis}).get(); + h.itsClusterSizeVsPt = registry.add(path + "itsClusterSizeVsPt", "ITS cluster size after current PID", HistType::kTH2F, {itsPtAxis, itsSizeAxis}).get(); + h.meanITSClusterSizeVsP = registry.add(path + "meanITSClusterSizeVsP", "Measured mean ITS cluster size after current PID;#it{p} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {pAxis}).get(); + h.meanITSClusterSizeVsPt = registry.add(path + "meanITSClusterSizeVsPt", "Measured mean ITS cluster size after current PID;#it{p}_{T} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {itsPtAxis}).get(); + const std::string expectedPath = std::string("DetectorPidQA/PIDQuality/Expected/") + species[i] + "/itsClusterSizeVsP"; + expectedITSResponse[i] = registry.add(expectedPath, "ITS expected mean (model, not measured data)", HistType::kTProfile, {pAxis}).get(); + } + // Identical axes and response parameters preserve model values on merge. + // Entries count model samples per worker, not tracks; do not normalize. + // Sample bin centers; the response is defined only at positive momentum. + for (int bin = 1; bin <= expectedITSResponse[0]->GetNbinsX(); ++bin) { + const float p = expectedITSResponse[0]->GetXaxis()->GetBinCenter(bin); + if (p <= 0.f) { + continue; + } + const std::array expected{ + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p)}; + for (std::size_t i = 0; i < expected.size(); ++i) { + if (std::isfinite(expected[i])) { + expectedITSResponse[i]->Fill(p, expected[i]); + } + } + } + } - TH2* hITSnclsVsPhi = nullptr; - TH2* hITSChi2VsPhi = nullptr; + template + void fillPidQualityQA(TrackObject const& track, float cent, int pid) + { + // Called only by detectorPidQA, once per quality-selected track. All + // counts have unit weight; PID decisions come from getPidConfigurable. + const float pt = track.pt(); + const int nPIDClusters = track.tpcNClsPID(); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersAll"), pt, nPIDClusters); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound"), track.tpcNClsFound(), nPIDClusters); + if (pid < 0) { + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified"), pt, nPIDClusters); + } - qaHist(TH1* hPhiRunByRunBefore, TH1* hPhiRunByRunAfter, TH2* hITSnclsVsPhi, TH2* hITSChi2VsPhi) - : hPhiRunByRunBefore(hPhiRunByRunBefore), hPhiRunByRunAfter(hPhiRunByRunAfter), hITSnclsVsPhi(hITSnclsVsPhi), hITSChi2VsPhi(hITSChi2VsPhi) - { + constexpr std::array species{MyParticleType::kPion, MyParticleType::kKaon, MyParticleType::kProton}; + const std::array tpcNSigma{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + auto& h = pidQualityQAHists[i]; + h.tpcPIDClustersVsPt[stage]->Fill(pt, nPIDClusters); + if (std::isfinite(tpcNSigma[i])) { + const double values[]{pt, cent, static_cast(nPIDClusters), tpcNSigma[i]}; + h.tpcNSigmaVsPIDClusters[stage]->Fill(values); + } + } } - }; - std::vector qaHistVector; - // end hists for QA runbyrun + + // averageClusterSize removes the largest cluster and divides by N-1. + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 0.5); + return; + } + const float momentum = track.p(); + if (!std::isfinite(momentum) || momentum <= 0.f || !std::isfinite(track.eta())) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 1.5); + return; + } + const float clusterSize = o2::aod::ITSResponse::averageClusterSize(clusterSizes) / std::cosh(track.eta()); + // Reuse the response used by the low-pT ITS PID; do not change + // its global parameters or initialize a different data/MC response here. + const std::array itsNSigma{ + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track)}; + const bool allFinite = std::isfinite(clusterSize) && std::all_of(itsNSigma.begin(), itsNSigma.end(), [](float value) { return std::isfinite(value); }); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), allFinite ? 3.5 : 2.5); + if (std::isfinite(clusterSize)) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAll"), momentum, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt"), pt, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta"), momentum, track.eta(), clusterSize); + } + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + auto& h = pidQualityQAHists[i]; + if (pid == species[i] && std::isfinite(clusterSize)) { + h.itsClusterSizeVsP->Fill(momentum, clusterSize); + h.itsClusterSizeVsPt->Fill(pt, clusterSize); + h.meanITSClusterSizeVsP->Fill(momentum, clusterSize); + h.meanITSClusterSizeVsPt->Fill(pt, clusterSize); + } + if (!std::isfinite(itsNSigma[i])) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + h.itsNSigmaVsPt[stage]->Fill(pt, itsNSigma[i]); + if (std::isfinite(tpcNSigma[i])) { + const double values[]{pt, cent, tpcNSigma[i], itsNSigma[i]}; + h.tpcVsITSNSigma[stage]->Fill(values); + } + } + } + } // others void init(InitContext const&) // Initialization { + const AxisSpec activityAxis{axisMultiplicity, getActivityAxisTitle()}; + // FlowContainer's AxisSpec overload expects a full array of bin edges. + // Use its uniform-axis overload for fixed-width axisMultiplicity settings. + const auto initializeFlowContainer = [&](auto& container, TObjArray* profiles) { + if (activityAxis.nBins.has_value()) { + container->Initialize(profiles, activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgFlowNbootstrap); + } else { + container->Initialize(profiles, activityAxis, cfgFlowNbootstrap); + } + container->GetProfile()->GetXaxis()->SetTitle(getActivityAxisTitle()); + if (auto* subprofiles = container->GetSubProfiles()) { + for (auto* profile : *subprofiles) { + static_cast(profile)->GetXaxis()->SetTitle(getActivityAxisTitle()); + } + } + }; + // init and add lots of graphs according to switch ccdb->setURL(cfgurl.value); ccdb->setCaching(true); @@ -577,9 +724,13 @@ struct PidFlowPtCorr { cfgAcceptance = correctionPathOpts.cfgAcceptancePath.value; cfgAcceptanceWithPt = correctionPathOpts.cfgAcceptancePathWithPt.value; - cfgNSigma = cfgNSigmapid; cfgMultPVCutPara = evtSeleOpts.cfgMultPVCut; + fTPCPIDClustersMin = std::make_unique("pidFlowPtCorrTPCPIDClustersMin", cfgTPCPIDClustersMinFunction.value.c_str(), 0., 256.); + if (!fTPCPIDClustersMin->IsValid() || fTPCPIDClustersMin->GetNpar() != 0) { + LOGF(fatal, "Invalid cfgTPCPIDClustersMinFunction: use a ROOT formula in x = tpcNClsFound() with numeric coefficients and no free parameters"); + } + // Set the pt, mult and phi Axis; if (dcaCutOpts.cfgDCAxyNSigma > 0) { @@ -605,8 +756,9 @@ struct PidFlowPtCorr { registry.add("hVtxZ", "", {HistType::kTH1D, {cfgaxisVertex}}); registry.add("hMult", "", {HistType::kTH1D, {cfgaxisNch}}); registry.add("hMultTPC", "", {HistType::kTH1D, {cfgaxisNch}}); - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); - registry.add("hCent", hCentTitle.c_str(), {HistType::kTH1D, {{90, 0, 90}}}); + std::string hCentTitle = cfgUseMultiplicity.value ? "PV multiplicity distribution" : "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); + const AxisSpec hCentAxis = cfgUseMultiplicity.value ? activityAxis : AxisSpec{90, 0., 90., getActivityAxisTitle()}; + registry.add("hCent", hCentTitle.c_str(), {HistType::kTH1D, {hCentAxis}}); registry.add("hPt", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtCorr", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtPi", "", {HistType::kTH1D, {cfgaxisPt}}); @@ -615,12 +767,13 @@ struct PidFlowPtCorr { registry.add("hPtCorrKa", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtPr", "", {HistType::kTH1D, {cfgaxisPt}}); registry.add("hPtCorrPr", "", {HistType::kTH1D, {cfgaxisPt}}); - registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, axisMultiplicity}}); + registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, activityAxis}}); registry.add("hNchUnCorrectedVSNchCorrected", "", {HistType::kTH2D, {cfgaxisNch, cfgaxisNch}}); runNumbers = cfgRunNumbers; // Minimal detector-PID performance QA. if (switchsOpts.cfgOutputQA.value) { + initPidQualityQA(); registry.add("DetectorPidQA/TPC/dEdxPi", "TPC d#it{E}/d#it{x} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxKa", "TPC d#it{E}/d#it{x} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); registry.add("DetectorPidQA/TPC/dEdxPr", "TPC d#it{E}/d#it{x} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); @@ -630,55 +783,33 @@ struct PidFlowPtCorr { registry.add("DetectorPidQA/TOF/deltaKa", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/deltaPr", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); registry.add("DetectorPidQA/TOF/tofTimeAll", "TOF #it{t} - #it{t}_{ev} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFTime}}); - - // run by run QA hists - /** - * @brief create QA hist for each run - * @note include: - * 1. hPhi before collision cut - * 2. hPhi after collision cut - * > graph below are after collision cut < - * 3. ITS ncls vs Phi - * 4. ITS chi2 vs Phi - * @param runNumbers - */ - for (const auto& oneRun : runNumbers) { - TH1* hPhiRunByRunBefore = registry.add(Form("RunByRunQA%d/hPhiBefore", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - TH1* hPhiRunByRunAfter = registry.add(Form("RunByRunQA%d/hPhiAfter", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - - TH2* hITSnclsVsPhi = registry.add(Form("RunByRunQA%d/hITSnclsVsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {8, 0, 8}}}).get(); - TH2* hITSChi2VsPhi = registry.add(Form("RunByRunQA%d/hITSChi2VsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {100, 0, 10}}}).get(); - - qaHistVector.emplace_back(qaHist(hPhiRunByRunBefore, hPhiRunByRunAfter, hITSnclsVsPhi, hITSChi2VsPhi)); - } - // end run by run QA hists } // cfgoutputqa if (switchsOpts.cfgOutPutMC.value) { // hist for NUE correction - registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // ITS only // hist for Pi eff - registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Ka eff - registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Pr eff - registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // Charged-track reconstruction efficiency split by MC true species. // Detector PID is deliberately not used for these numerators. - registry.add("correction/hPtCentMcRecTruePi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTrueKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTruePr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcRecTrueOther", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTruePi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTrueKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTruePr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenTrueOther", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); } // cfgoutputMC if (switchsOpts.cfgOutPutMC1D.value) { @@ -694,10 +825,10 @@ struct PidFlowPtCorr { } if (switchsOpts.cfgOutPutPtSpectra.value) { - registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {activityAxis}}); registry.add("c22PrimeVsc22/Pi", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); registry.add("c22PrimeVsc22/Ka", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); @@ -727,10 +858,10 @@ struct PidFlowPtCorr { registry.add("abundance/hNumOfPrEventCount", "", {HistType::kTH1D, {particleAbundanceOpts.cfgaxisAbundancePr}}); } if (particleAbundanceOpts.cfgOutputAbundanceYields.value) { - registry.add("abundance/hPtCentDataPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("abundance/hPtCentDataUnidentified", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("abundance/hPtCentDataPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataUnidentified", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); } // set bin label for hEventCount @@ -770,24 +901,24 @@ struct PidFlowPtCorr { // Store the ensemble mean pT required for the offline delta[pT]/<[pT]> // transformation. The profile weight is the NUE-corrected particle count. if (switchsOpts.cfgOutputCharged.value) { - registry.add("c22DeltaPt/c22dmeanptCharged", "Charged c_{2}{2} versus charged event [p_{T}];Centrality (%);[p_{T}]_{ch} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/hMeanPtCharged", "Charged mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{ch} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptCharged", Form("Charged c_{2}{2} versus charged event [p_{T}];%s;[p_{T}]_{ch} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtCharged", Form("Charged mean p_{T};%s;Bootstrap subsample;[p_{T}]_{ch} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/hMeanPtPion", "Pion mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{#pi} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptPionRefRef", "Ref-ref c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtPion", Form("Pion mean p_{T};%s;Bootstrap subsample;[p_{T}]_{#pi} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionRefRef", Form("Ref-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/hMeanPtKaon", "Kaon mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{K} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptKaonRefRef", "Ref-ref c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtKaon", Form("Kaon mean p_{T};%s;Bootstrap subsample;[p_{T}]_{K} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonRefRef", Form("Ref-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/hMeanPtProton", "Proton mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{p} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptProtonRefRef", "Ref-ref c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtProton", Form("Proton mean p_{T};%s;Bootstrap subsample;[p_{T}]_{p} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonRefRef", Form("Ref-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/hMeanPtUnidentified", "Unidentified mean p_{T};Centrality (%);Bootstrap subsample;[p_{T}]_{unidentified} (GeV/#it{c})", {HistType::kTProfile2D, {axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("c22DeltaPt/c22dmeanptUnidentifiedRefRef", "Ref-ref c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtUnidentified", Form("Unidentified mean p_{T};%s;Bootstrap subsample;[p_{T}]_{unidentified} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedRefRef", Form("Ref-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } // Ref-ref is required in either PID mode: Pure/ref-ref or POI-ref/ref-ref. @@ -796,30 +927,30 @@ struct PidFlowPtCorr { // profiles are neither allocated nor filled. if (switchsOpts.cfgC22DeltaPtUsePure.value) { if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/c22dmeanptPionPure", "Pion POI-POI c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionPure", Form("Pion POI-POI c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/c22dmeanptKaonPure", "Kaon POI-POI c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonPure", Form("Kaon POI-POI c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/c22dmeanptProtonPure", "Proton POI-POI c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonPure", Form("Proton POI-POI c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/c22dmeanptUnidentifiedPure", "Unidentified POI-POI c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPure", Form("Unidentified POI-POI c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } } else { // For each PID species, compare POI-ref and ref-ref using the same PID mean-pT axis. if (switchsOpts.cfgOutputPion.value) { - registry.add("c22DeltaPt/c22dmeanptPionPOIRef", "Pion POI-ref c_{2}{2} versus pion event [p_{T}];Centrality (%);[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionPOIRef", Form("Pion POI-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputKaon.value) { - registry.add("c22DeltaPt/c22dmeanptKaonPOIRef", "Kaon POI-ref c_{2}{2} versus kaon event [p_{T}];Centrality (%);[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonPOIRef", Form("Kaon POI-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("c22DeltaPt/c22dmeanptProtonPOIRef", "Proton POI-ref c_{2}{2} versus proton event [p_{T}];Centrality (%);[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonPOIRef", Form("Proton POI-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("c22DeltaPt/c22dmeanptUnidentifiedPOIRef", "Unidentified POI-ref c_{2}{2} versus unidentified event [p_{T}];Centrality (%);[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", {HistType::kTProfile3D, {axisMultiplicity, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPOIRef", Form("Unidentified POI-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); } } } @@ -829,7 +960,7 @@ struct PidFlowPtCorr { if (switchsOpts.cfgAddPidResponseMatrixHistograms.value) { registry.add("pidResponseMatrix/hPidResponseMatrix", "PID response matrix;true PID;reconstructed PID", {HistType::kTH2D, {{4, -0.5, 3.5}, {4, -0.5, 3.5}}}); - registry.add("pidResponseMatrix/hPidResponseMatrixRecPtCent", "PID response matrix vs reconstructed pT and centrality;true PID;reconstructed PID;p_{T}^{rec} (GeV/#it{c});Centrality (%)", {HistType::kTHnSparseF, {{4, -0.5, 3.5}, {4, -0.5, 3.5}, cfgaxisPt, axisMultiplicity}}); + registry.add("pidResponseMatrix/hPidResponseMatrixRecPtCent", Form("PID response matrix vs reconstructed pT and event activity;true PID;reconstructed PID;p_{T}^{rec} (GeV/#it{c});%s", getActivityAxisTitle()), {HistType::kTHnSparseF, {{4, -0.5, 3.5}, {4, -0.5, 3.5}, cfgaxisPt, activityAxis}}); auto setPidResponseLabels = [](TAxis* axis, bool includeUnidentified) { axis->SetBinLabel(kPidRespPion + 1, "Pion"); @@ -883,7 +1014,7 @@ struct PidFlowPtCorr { // init fFCCh if (switchsOpts.cfgOutputCharged.value) { fFCCh->SetName("FlowContainerCharged"); - fFCCh->Initialize(oba4Ch, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCCh, oba4Ch); } // end init fFCCh @@ -900,19 +1031,19 @@ struct PidFlowPtCorr { if (switchsOpts.cfgOutputPion.value) { fFCPi->SetName("FlowContainerPi"); - fFCPi->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPi, oba4PID); } if (switchsOpts.cfgOutputKaon.value) { fFCKa->SetName("FlowContainerKa"); - fFCKa->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCKa, oba4PID); } if (switchsOpts.cfgOutputProton.value) { fFCPr->SetName("FlowContainerPr"); - fFCPr->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPr, oba4PID); } if (switchsOpts.cfgOutputUnidentified.value) { fFCUnidentified->SetName("FlowContainerUnidentified"); - fFCUnidentified->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCUnidentified, oba4PID); } // end init fFCPID @@ -937,43 +1068,43 @@ struct PidFlowPtCorr { oba4PidPtCorr->Add(new TNamed("ptKaInKaPrPairs", "ptKaInKaPrPairs")); oba4PidPtCorr->Add(new TNamed("ptPrInKaPrPairs", "ptPrInKaPrPairs")); fFCPidPtCorr->SetName("FlowContainerPidPtCorr"); - fFCPidPtCorr->Initialize(oba4PidPtCorr, axisMultiplicity, cfgFlowNbootstrap); + initializeFlowContainer(fFCPidPtCorr, oba4PidPtCorr); } if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { // TProfile3D axes: event mean pT, centrality, and bootstrap subsample. if (switchsOpts.cfgOutputCharged.value) { - registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputPion.value) { - registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); if (switchsOpts.cfgClosureTest.value != 0) { - registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } } if (switchsOpts.cfgOutputKaon.value) { - registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputProton.value) { - registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProtonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProtonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } if (switchsOpts.cfgOutputUnidentified.value) { - registry.add("meanptCentNbs/hChargedUnidentifiedWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedUnidentifiedFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hUnidentified", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hUnidentifiedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedUnidentifiedWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedUnidentifiedFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentified", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentifiedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); } } @@ -992,10 +1123,10 @@ struct PidFlowPtCorr { // end track QA /// @note evetn QA - registry.add("hProcessQA/centVsMult", "cent Vs Mult;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisMultiplicity, cfgaxisNch}}); + registry.add("hProcessQA/centVsMult", Form("Event activity versus input track count;%s;Input track count", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, cfgaxisNch}}); registry.add("hProcessQA/IR", "", {HistType::kTH1D, {{100, 0, 100}}}); registry.add("hProcessQA/Occupacy", "", {HistType::kTH1D, {{1000, 0, 10000}}}); - registry.add("hProcessQA/occupancyVsCent", "Track occupancy versus centrality;Centrality (%);Track occupancy in time range", {HistType::kTH2D, {axisMultiplicity, {1000, 0, 10000}}}); + registry.add("hProcessQA/occupancyVsCent", Form("Track occupancy versus event activity;%s;Track occupancy in time range", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, {1000, 0, 10000}}}); // end evetn QA } // end init QA plot for processQA @@ -1137,167 +1268,10 @@ struct PidFlowPtCorr { fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); - - std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; - hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, pTEffBins.data()); - funcEff.resize(pTEffBins.size() - 1); - - std::vector f1p0 = cfgTrackDensityP0; - std::vector f1p1 = cfgTrackDensityP1; - for (uint ifunc = 0; ifunc < pTEffBins.size() - 1; ifunc++) { - funcEff[ifunc] = new TF1(Form("funcEff%i", ifunc), "[0]+[1]*x", 0, 3000); - funcEff[ifunc]->SetParameters(f1p0[ifunc], f1p1[ifunc]); - } - std::vector v2para = cfgTrackDensityV2P; - std::vector v3para = cfgTrackDensityV3P; - std::vector v4para = cfgTrackDensityV4P; - funcV2 = new TF1("funcV2", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV2->SetParameters(v2para[0], v2para[1], v2para[2], v2para[3], v2para[4]); - funcV3 = new TF1("funcV3", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV3->SetParameters(v3para[0], v3para[1], v3para[2], v3para[3], v3para[4]); - funcV4 = new TF1("funcV4", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV4->SetParameters(v4para[0], v4para[1], v4para[2], v4para[3], v4para[4]); } // pid utils function - /** - * @brief Identify whether the input track is a Pion - * - * @param track Input track object to be identified - * @return true The track is identified as Pion - * @return false The track is NOT identified as Pion - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isPion(TrackObject const& track) - { - if (switchsOpts.cfgClosureTest.value != 0) { - float rnd4test = fRndm->Rndm() * 100; - if (rnd4test < switchsOpts.cfgClosureTest.value) { - return true; - } - } // closure test - - bool resultPion = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPi()); - const float tpcSigma = std::fabs(track.tpcNSigmaPi()); - const float pt = track.pt(); - - // ITS detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultPion &= (itsSigma < cfgNSigma[6]); - } - // end ITS - - // TOF detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultPion &= (tofSigma < cfgNSigma[3]); - } - // end TOF - - // TPC detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultPion &= (tpcSigma < cfgNSigma[0]); - } - // end TPC - - return resultPion; - } - - /** - * @brief Identify whether the input track is a Proton - * - * @param track Input track object to be identified - * @return true The track is identified as Proton - * @return false The track is NOT identified as Proton - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isProton(TrackObject const& track) - { - bool resultProton = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPr()); - const float tpcSigma = std::fabs(track.tpcNSigmaPr()); - const float pt = track.pt(); - - // ITS detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value) { - resultProton &= (itsSigma < cfgNSigma[7]); - } - // end ITS - - // TOF detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value) { - resultProton &= (tofSigma < cfgNSigma[4]); - } - // end TOF - - // TPC detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value) { - resultProton &= (tpcSigma < cfgNSigma[1]); - } - // end TPC - - return resultProton; - } - - /** - * @brief Identify whether the input track is a Kaon (separate from Pion and Proton) - * - * @param track Input track object (aod::Track) to be identified - * @return true The track is identified as Kaon - * @return false The track is NOT identified as Kaon - * @note The result is the logical AND of valid detector Sigma checks. - * Only pt range that overlaps with both pi-k and k-p separation ranges is checked. - * If the track pt is out of the overlapping range, that detector's check is skipped. - */ - template - bool isKaon(TrackObject const& track) - { - bool resultKaon = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaKa()); - const float tpcSigma = std::fabs(track.tpcNSigmaKa()); - const float pt = track.pt(); - - // ITS detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultKaon &= (itsSigma < cfgNSigma[8]); - } - // end ITS - - // TOF detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultKaon &= (tofSigma < cfgNSigma[5]); - } - // end TOF - - // TPC detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultKaon &= (tpcSigma < cfgNSigma[2]); - } - // end TPC - - return resultKaon; - } - template int getPidWithCircleCut(TrackObject const& track) { @@ -1324,7 +1298,7 @@ struct PidFlowPtCorr { if (pt > ptMin && pt < ptMax) { isPionBool = std::hypot(tpcNsigma, tofNsigma) < circleCutOpts.cfgCircleCutSigmaPi.value; } else { - // Fallback: TPC only cut + // TPC fallback; the low-pT ITS requirement is applied below. isPionBool = std::fabs(tpcNsigma) < circleCutOpts.cfgCircleCutTPCPi.value; } } @@ -1366,6 +1340,24 @@ struct PidFlowPtCorr { } } + // Apply ITS to every low-pT candidate before resolving ambiguities. + if (pt < circleCutOpts.cfgCircleCutITSPtMax.value) { + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2 || !std::isfinite(track.p()) || track.p() <= 0.f || !std::isfinite(track.eta())) { + return -1; + } + const auto passesITS = [](float nsigma, float cut) { + return std::isfinite(nsigma) && std::fabs(nsigma) < cut; + }; + isPionBool = isPionBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPi.value); + isKaonBool = isKaonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSKa.value); + isProtonBool = isProtonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPr.value); + } + // ------------------------------ // Ambiguity rejection // ------------------------------ @@ -1390,7 +1382,7 @@ struct PidFlowPtCorr { } /** - * @brief Get the Pid, combine square cut && circle cut + * @brief Apply the TPC PID cluster cut, then circle PID with low-pT ITS * * @tparam TrackObject * @param track @@ -1399,28 +1391,13 @@ struct PidFlowPtCorr { template int getPidConfigurable(TrackObject const& track) { - int pid = -1; - if (circleCutOpts.cfgUseCircleCutPID.value) { - // Use circular cut (already handles ambiguity rejection) - pid = getPidWithCircleCut(track); - } else { - // Use normal cut (with manual ambiguity rejection) - bool isPi = isPion(track); - bool isKa = isKaon(track); - bool isPr = isProton(track); - int nCandidates = isPi + isKa + isPr; - if (nCandidates == 1) { - if (isPi) { - pid = MyParticleType::kPion; - } else if (isKa) { - pid = MyParticleType::kKaon; - } else if (isPr) { - pid = MyParticleType::kProton; - } - } - // else: pid remains -1 (ambiguous or none) + // PID quality changes the species assignment, not charged track selection. + const double minPIDClusters = fTPCPIDClustersMin->Eval(track.tpcNClsFound()); + if (!std::isfinite(minPIDClusters) || track.tpcNClsPID() < minPIDClusters) { + return -1; } - return pid; + + return getPidWithCircleCut(track); } bool isWithinRefPtRange(float pt) @@ -2353,13 +2330,22 @@ struct PidFlowPtCorr { } /** - * @brief get centrality from the configured estimator - * @note mirrors flowTask.cxx's getCentrality(), allowing this task to - * switch between different Run 3 centrality estimators via - * cfgCentEstimator instead of always using FT0C. + * @brief Get the event activity used for output binning and NUE lookup. + * @note Multiplicity mode uses raw PV contributors in |eta| < 0.8. + * It requires matching multiplicity axes and NUE maps; the caller + * must disable the centrality-calibrated cfgDoMultPVCut. */ template float getCentrality(TCollision const& collision) + { + if (cfgUseMultiplicity.value) { + return collision.multNTracksPV(); + } + return getPercentileCentrality(collision); + } + + template + float getPercentileCentrality(TCollision const& collision) { float cent = -1; switch (cfgCentEstimator) { @@ -2566,33 +2552,6 @@ struct PidFlowPtCorr { registry.fill(HIST("hCent"), cent); // end correction - double psi2Est = 0, psi3Est = 0, psi4Est = 0; - float wEPeff = 1; - double v2 = 0, v3 = 0, v4 = 0; - if (switchsOpts.cfgDoLocDenCorr.value) { - double q2x = 0, q2y = 0; - double q3x = 0, q3y = 0; - double q4x = 0, q4y = 0; - for (const auto& track : tracks) { - // pt cut - bool withinPtRef = isWithinRefPtRange(track.pt()); - if (withinPtRef) { - q2x += std::cos(2 * track.phi()); - q2y += std::sin(2 * track.phi()); - q3x += std::cos(3 * track.phi()); - q3y += std::sin(3 * track.phi()); - q4x += std::cos(4 * track.phi()); - q4y += std::sin(4 * track.phi()); - } - } - psi2Est = std::atan2(q2y, q2x) / 2.; - psi3Est = std::atan2(q3y, q3x) / 3.; - psi4Est = std::atan2(q4y, q4x) / 4.; - v2 = funcV2->Eval(cent); - v3 = funcV3->Eval(cent); - v4 = funcV4->Eval(cent); - } // cfgDoLocDenCorr - double ptSum = 0; double ptSumw2 = 0; double nch = 0; @@ -2719,22 +2678,6 @@ struct PidFlowPtCorr { setParticleNUEWeight(weff, track, cent); // end do NUE && NUA - if (switchsOpts.cfgDoLocDenCorr.value) { - bool withinPtRef = isWithinRefPtRange(track.pt()); - if (withinPtRef) { - double fphi = v2 * std::cos(2 * (track.phi() - psi2Est)) + v3 * std::cos(3 * (track.phi() - psi3Est)) + v4 * std::cos(4 * (track.phi() - psi4Est)); - fphi = (1 + 2 * fphi); - int pTBinForEff = hFindPtBin->FindBin(track.pt()); - if (pTBinForEff >= 1 && pTBinForEff <= hFindPtBin->GetNbinsX()) { - wEPeff = funcEff[pTBinForEff - 1]->Eval(fphi * tracks.size()); - if (wEPeff > 0.) { - wEPeff = 1. / wEPeff; - weff *= wEPeff; - } - } - } - } // cfgDoLocDenCorr - // The reference and POI pT ranges are applied independently below. if (!trackSelectedForFlow(track)) { continue; @@ -2786,7 +2729,7 @@ struct PidFlowPtCorr { } if (pid == -1 && withinPtRefGlobal) { // Unidentified tracks have no PID-specific correction. Reuse exactly - // the charged NUA/NUE (including the optional local-density factor). + // the charged NUA/NUE weights. fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kPOI); fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kOverlap, wacc * weff); } else if (isWithinPOIPtRange(pid, track.pt())) { @@ -3162,42 +3105,8 @@ struct PidFlowPtCorr { loadCorrections(bc.timestamp()); - /// @note local density corr const float vtxz = collision.posZ(); - double psi2Est = 0.; - double psi3Est = 0.; - double psi4Est = 0.; - double v2 = 0.; - double v3 = 0.; - double v4 = 0.; - if (switchsOpts.cfgDoLocDenCorr.value) { - double q2x = 0.; - double q2y = 0.; - double q3x = 0.; - double q3y = 0.; - double q4x = 0.; - double q4y = 0.; - for (const auto& track : tracks) { - if (!isWithinRefPtRange(track.pt())) { - continue; - } - q2x += std::cos(2. * track.phi()); - q2y += std::sin(2. * track.phi()); - q3x += std::cos(3. * track.phi()); - q3y += std::sin(3. * track.phi()); - q4x += std::cos(4. * track.phi()); - q4y += std::sin(4. * track.phi()); - } - psi2Est = std::atan2(q2y, q2x) / 2.; - psi3Est = std::atan2(q3y, q3x) / 3.; - psi4Est = std::atan2(q4y, q4x) / 4.; - v2 = funcV2->Eval(cent); - v3 = funcV3->Eval(cent); - v4 = funcV4->Eval(cent); - } - // end local density corr - // Event-wise mean-pT numerators and NUE-corrected particle counts. Charged // particles use the reference pT range; each PID species uses its own POI pT range. double ptSum = 0.; @@ -3266,18 +3175,6 @@ struct PidFlowPtCorr { const bool withinPtRef = isWithinRefPtRange(track.pt()); if (withinPtRef) { - if (switchsOpts.cfgDoLocDenCorr.value) { - const double fphi = 1. + 2. * (v2 * std::cos(2. * (track.phi() - psi2Est)) + - v3 * std::cos(3. * (track.phi() - psi3Est)) + - v4 * std::cos(4. * (track.phi() - psi4Est))); - const int ptBinForEff = hFindPtBin->FindBin(track.pt()); - if (ptBinForEff >= 1 && ptBinForEff <= hFindPtBin->GetNbinsX()) { - const double localDensityEff = funcEff[ptBinForEff - 1]->Eval(fphi * tracks.size()); - if (localDensityEff > 0.) { - weff /= localDensityEff; - } - } - } /// @note passed selection: global cut + pt cut fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); } @@ -3490,7 +3387,7 @@ struct PidFlowPtCorr { /** * @brief Run the flow calculation on generated MC particles for a closure test. - * @note Reconstructed collisions are only used to obtain the centrality. The selection is + * @note Reconstructed collisions only provide centrality or raw PV multiplicity. The selection is * deliberately truth-level: MC vertex filter, physical-primary particles, kinematics, * and PDG PID. No data event, track-quality, detector-PID, NUA, or NUE cuts are applied. */ @@ -3970,7 +3867,7 @@ struct PidFlowPtCorr { PROCESS_SWITCH(PidFlowPtCorr, processQA, "", true); /** - * @brief Minimal PID-performance QA for TPC dE/dx and TOF time-of-flight. + * @brief PID-performance QA for TPC, TOF, PID clusters and ITS response. * @note open switch outputQA if use it * * @param collision @@ -3989,25 +3886,6 @@ struct PidFlowPtCorr { double interactionRate = getInteractionRate(bc.timestamp(), runNumber); // end init - // loop the vector, find the place to put - int matchedPosition = -1; - for (uint64_t idxPosition = 0; idxPosition < runNumbers.size(); idxPosition++) { - if (runNumbers[idxPosition] == runNumber) { - matchedPosition = idxPosition; - break; - } - } - if (matchedPosition == -1) { - return; - } - // end find place to put run data - - // fill some QA graph before collision cut - for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunBefore->Fill(track.phi()); - } - // end fill some QA graph before collision cut - // collision cut registry.fill(HIST("hEventCount/detectorPidQA"), 0.5); if (nTot < 1) { @@ -4025,14 +3903,6 @@ struct PidFlowPtCorr { // loadCorrections(bc.timestamp()); // end cut and correction - // fill some QA graph after collision cut - for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunAfter->Fill(track.phi()); - qaHistVector[matchedPosition].hITSnclsVsPhi->Fill(track.phi(), track.itsNCls()); - qaHistVector[matchedPosition].hITSChi2VsPhi->Fill(track.phi(), track.itsChi2NCl()); - } - // end fill some QA graph after collision cut - // start filling graphs for (const auto& track : tracks) { // Use the same track-quality selection as processData, but do not @@ -4048,9 +3918,10 @@ struct PidFlowPtCorr { } } - // The species-dependent histograms additionally require an identified - // PID and the corresponding POI pT range. const int pid = getPidConfigurable(track); + fillPidQualityQA(track, cent, pid); + // The existing species-dependent TPC/TOF histograms require the current + // unique PID assignment and its corresponding POI pT range. if (!isWithinPOIPtRange(pid, pt)) { continue; } From 54da3342cd6be089dc8b147e106a5578839c2d7e Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Sat, 24 Oct 2026 19:21:21 +0200 Subject: [PATCH 428/648] [PWGCF] radialFlowDecorr: Edits for QM Prep-->all systematic variations in one wagon (#18449) --- .../Tasks/radialFlowDecorr.cxx | 4030 ++++++++++------- 1 file changed, 2464 insertions(+), 1566 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index e917af7fb09..de6ea4b8ca7 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include #include #include @@ -33,16 +36,22 @@ #include #include +#include +#include #include #include #include +#include #include +#include #include #include #include #include #include +#include + #include #include #include @@ -69,6 +78,10 @@ struct RadialFlowDecorr { static constexpr int KNEtaHalfBinsMax = 8; // 0.8 / 0.1: finest (DATA) bins per side static constexpr int KNEtaMax = 2 * KNEtaHalfBinsMax + 1; // + index-0 full-range reference bin + // Bootstrap: at most KMaxBoot replicas of 8 profiles each, in one registry of at most + // 512 objects. nBoot = min(cfgNBootstrap, KMaxBoot) replicas are filled. + static constexpr int KMaxBoot = 60; + static constexpr float KFloatEpsilon = 1e-6f; static constexpr float KEtaEdgeTolerance = 1e-3f; // slack for cfgCutEta being an integer multiple of the bin width static constexpr float KBinOffset = 0.5f; @@ -91,11 +104,15 @@ struct RadialFlowDecorr { static constexpr float KVzMax = 40.f; static constexpr int KNbinsEtaFine = 20; static constexpr float KEtaFineMax = 1.f; - static constexpr float KCentMax = 90; + // occupancy QA axis + static constexpr int KNbinsOccQA = 150; + static constexpr float KOccQAMax = 15000.f; - // Bootstrap: KMaxBoot is the compile-time storage cap; the number actually - // filled is nBoot = min(cfgNBootstrap, KMaxBoot). - static constexpr int KMaxBoot = 64; + // Forward detectors: FT0 channels 0-95 are A side, 96-207 C side; FV0-A has 48 cells. + static constexpr int KNChFT0A = 96; + static constexpr int KNChFT0 = 208; + static constexpr int KNChFV0 = 48; + static constexpr int KNFwdMax = 32; // detector totals + eta sub-bins enum ECentralityEstimator { kCentFT0C = 1, @@ -109,29 +126,234 @@ struct RadialFlowDecorr { kOO = 3, kpp = 4 }; + enum EITSLayerCut { + kITSLayerAuto = -1, // nominal: pp kIsGoodITSLayer0123, other systems kIsGoodITSLayersAll + kITSLayerNone = 0, + kITSLayer3 = 1, + kITSLayer0123 = 2, + kITSLayersAll = 3 + }; + enum EFwdDet { + kFT0A = 0, + kFT0C, + kFV0A, + kNFwdDet + }; + // Histogram level: DATA uses level 0 only; MC fills truth, reco and efficiency-corrected reco. + enum ELevel { + kGen = 0, + kReco, + kRecoEffCorr, + kNLevels + }; + // Last axis of the multi-dimensional objects. + enum EMeanObs { // Prof_Mean_Nch_etabin_obs + kMeanPt = 0, + kMeanN, + kNMeanObs + }; + enum EFlucObs { // Prof_Obs_Nch_etabin_obs + kObsN = 0, // N(eta), efficiency-corrected + kObsMeanPt, // [pT](eta) + kObsC2, // c2 within the bin + kObsC3, // c3 within the bin + kObsC2Sub, // , mirror pair, at the A bin + kObsC3Sub, // c2_A _C at A (AAB) and c2_C _A at C (ABB), mirror pair + kNFlucObs + }; + enum EEffCat { // efficiency maps h3_* (centrality, pT, eta) + kEffAllPrim = 0, // charged primaries of the MC collision + kEffRecoMatchedPrim, // reco tracks of primaries of the same MC collision, at the MC pT and eta + kEffAllReco, + kEffRecoSec, // secondaries of the same MC collision + kEffRecoOtherColl, // particles of another MC collision (pile-up, wrong association) + kEffRecoFake, // reco tracks without MC label + kNEffCat + }; + inline static const std::array effMapName = { + "h3_AllPrimary", "h3_RecoMatchedToPrimary", "h3_AllReco", "h3_RecoUnMatchedToPrimary_Secondary", + "h3_RecoUnMatchedToPrimary_OtherCollision", "h3_RecoUnMatchedToPrimary_Fake"}; + enum ETrackOrigin { // MCQA/Hist3D_Nch_pT_origin + kOriginPrimary = 0, + kOriginSecondary, + kOriginOtherColl, + kOriginNoLabel, + kNOrigin + }; + enum EVtxQA { // MCQA/Hist2D_dVz_type + kVtxSingle = 0, // the only reco collision of the MC collision + kVtxSplitBest, // the one used among several + kVtxSplitOther, // the others (skipped) + kNVtxQA + }; + enum EFlatType { // hFlatMaps + kFlatRaw = 0, + kFlatEffWtd, // (1 - fake) / eff (Base only) + kFlatWtd, // all weights of the pass + kNFlatType + }; + enum EChanQA { // FwdQA/Prof_Channel_qa + kChAmpRaw = 0, + kChAmpCorr, + kChEta, + kNChQA + }; + // Event cut flow (hEvtCount, x axis); the last two bins count selected events only + enum EEvtCut { + kEvAll = 0, + kEvSel8, + kEvVtxZ, + kEvGoodZvtx, + kEvTFBorder, + kEvITSROFBorder, + kEvVertexITSTPC, + kEvSameBunch, + kEvTimeRange, + kEvITSLayer, + kEvAddPileup, + kEvVariationCut, + kEvCent, + kEvInfoPrevRof, // selected events passing kNoHighMultCollInPrevRof + kEvInfoOccUnknown, // selected events with occupancy < 0 (time-frame border) + kNEvCut + }; + // Track cut flow (hTrkCount, x axis) + enum ETrkCut { + kTrkAll = 0, + kTrkSign, + kTrkHasColl, + kTrkPVContrib, + kTrkPtEta, + kTrkGlobal, + kTrkITS, + kTrkTPC, + kTrkTPCChi2, + kTrkDCAz, + kTrkVariationCut, + kTrkWeight, // valid efficiency (and flattening) weight: used + kNTrkCut + }; + + // Cut QA (processCutQA): each quantity the Base selection cuts on, before and after it. + enum ECutQAStage { + kQABefore = 0, // events: all collisions; tracks: all tracks of the selected events + kQAAfter, // Base-selected events / tracks + kNQAStage + }; + enum ECutQAEvt1D { + kQAVtxZ = 0, + kQACent, + kQAOccupancy, + kNQAEvt1D + }; + enum ECutQAEvt2D { + kQANchNPV = 0, + kQANchCent, + kNQAEvt2D + }; + enum ECutQATrk1D { + kQAPt = 0, + kQAEta, + kQAItsNCls, + kQAItsChi2, + kQATpcNClsFound, + kQATpcCrossedRows, + kQATpcCrossedRowsOverFindable, + kQATpcChi2, + kQAPVContributor, + kQAGlobalTrack, + kNQATrk1D + }; + enum ECutQATrk2D { + kQADcaXYPt = 0, + kQADcaZPt, + kNQATrk2D + }; - // Systematic-variation selector for DATA. Base is the main measurement; each - // variation reads its own DataMean object (systSuffix) for the correlation step. - // kSystEff and kSystEffPtDep scale the efficiency by cfgEffVarPercent; kSystEffPtDep - // always uses the pT-dependent shape. kSystCent is run with a different - // cfgCentralityChoice than the base (base FT0C, variation FT0M). + // Systematic variations, all evaluated in every job. Base is the nominal measurement. + // The cut values are the K* constants below; only the occupancy limits are configurable. + // Cent : FT0M instead of the nominal estimator (cfgCentralityChoice, FT0C) + // Chi2TPC : chi2 per TPC cluster < 2 instead of cfgCutTpcChi2NCl + // DCAxy : |DCAxy| < 5/7 x (globalTrack pT-dependent DCAxy cut), i.e. 5 instead of 7 sigma + // DCAz : |DCAz| < 1 cm instead of cfgCutTracKDcaMaxZ + // DCAxy and DCAz are null checks: with the nominal isPVContributor requirement even + // 5 sigma DCAxy or |DCAz| < 0.05 cm removes only ~0.2% of the selected tracks + // Eff : efficiency scaled by -10% at all pT (cancels in [pT]: a check) + // EffpTDep : efficiency scaled by -10% at cfgPtMin, linearly to 0 at 10 GeV/c + // Flat : no acceptance (flattening) weights + // ITSLayer : ITS good-layer requirement one step looser (All -> 0123, 0123 -> Layer3) + // NITS : itsNCls >= 4 instead of cfgITScluster + // NTPC : tpcNClsFound >= 80 instead of cfgTPCcluster + // Pileup : additionally kNoCollInRofStandard + // Vz : |z_vtx| < 8 cm instead of cfgVtxZCut + // NchBin2 : Base selection and inputs, observables (and its own mean pass) in N_ch bins + // of 2 (PbPb, OO) or 1 (pp) tracks: cross-check of the N_ch binning of the mean subtraction + // Occupancy : 0 <= trackOccupancyInTimeRange < cfgSystOccMax* of the system + // PileupNSigma : n-sigma pile-up bands narrower by 1 (mean and fluctuation passes) + // Since the observables are stored versus N_ch, Cent differs from Base only in the events + // kept (cent < cfgCentMax) and in its (centrality, N_ch) map, which defines its classes. enum ESystType { kSystBase = 0, kSystCent, - kSystDCA, + kSystChi2TPC, + kSystDCAxy, + kSystDCAz, kSystEff, kSystEffPtDep, kSystFlat, + kSystITSLayer, kSystNITS, kSystNTPC, kSystPileup, kSystVz, - kNSystType + kSystNchBin, + kSystOccupancy, + kSystPileupNSigma, + kNSyst }; - // Suffix appended to the DataMean CCDB path per systematic (Base -> no suffix). - inline static const std::vector systSuffix = { - "", "_systCent", "_systDCA", "_systEff", "_systEffpTDep", "_systFlat", - "_systNITS", "_systNTPC", "_systPileup", "_systVz"}; + inline static const std::array systName = { + "Base", "systCent", "systChi2TPC", "systDCAxy", "systDCAz", "systEff", "systEffpTDep", + "systFlat", "systITSLayer", "systNITS", "systNTPC", "systPileup", "systVz", "systNchBin2", + "systOccupancy", "systPileupNSigma"}; + static constexpr uint32_t KAllSystMask = (1 << kNSyst) - 1; + static constexpr int KNSystPerRegistry = 4; // variations 1..15 are spread over 4 registries + // Cut of each variation (fixed; the occupancy limits are configurables, cfgSystOccMax*). + // Pileup adds kNoCollInRofStandard; ITSLayer loosens the nominal ITS good-layer requirement by one step. + static constexpr int KSystCentralityChoice = 2; // Cent: FT0M + static constexpr float KSystChi2TPC = 2.f; // Chi2TPC: chi2 per TPC cluster < 2 (nominal 2.5) + static constexpr float KSystDcaXyScale = 5.f / 7.f; // DCAxy: 5 sigma (nominal 7 sigma) + static constexpr float KSystDcaMaxZ = 1.f; // DCAz: |DCAz| < 1 cm (nominal 2 cm) + static constexpr float KEffVarPercent = -10.f; // Eff, EffpTDep: efficiency scaled by -10% + static constexpr float KEffVarPtZero = 10.f; // EffpTDep: variation falls linearly to 0 at 10 GeV/c + static constexpr int KSystITScluster = 4; // NITS: itsNCls >= 4 (nominal 5) + static constexpr int KSystTPCcluster = 80; // NTPC: tpcNClsFound >= 80 (nominal 70) + static constexpr float KSystVtxZCut = 8.f; // Vz: |z_vtx| < 8 cm (nominal 10 cm) + static constexpr int KSystNchBinWidthPbPb = 2; // NchBin2: N_ch bin width, PbPb (nominal 10) + static constexpr int KSystNchBinWidthOO = 2; // NchBin2: OO and NeNe (nominal 5) + static constexpr int KSystNchBinWidthPP = 1; // NchBin2: pp (nominal 2) + static constexpr float KSystPupnSigStep = 1.f; // PileupNSigma: n-sigma bands narrower by 1 + static constexpr double KMinBandEntries = 50.; // n-sigma band bins with fewer flattening-pass events apply no band cut + + // Output folder of a variation (Base keeps the top level). + static std::string systPrefix(int v) { return v == kSystBase ? std::string{} : systName[v] + "/"; } + // Suffix appended to every CCDB object path of a variation. + static std::string systSuffix(int v) { return v == kSystBase ? std::string{} : "_" + systName[v]; } + // Variation whose efficiency map v uses. Cent, Eff, EffpTDep, Flat, NchBin2 and PileupNSigma + // have the Base selection in the efficiency pass and use the Base map; Eff and EffpTDep scale + // it when applied. (The n-sigma bands are applied only in the mean and fluctuation passes.) + static constexpr int effSource(int v) + { + return (v == kSystCent || v == kSystEff || v == kSystEffPtDep || v == kSystFlat || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } + // Variation whose flattening map v uses. Cent, NchBin2 and PileupNSigma use the Base map; + // Flat uses none (-1). + static constexpr int flatSource(int v) + { + if (v == kSystFlat) { + return -1; + } + return (v == kSystCent || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } static constexpr float KinvalidCentrality = -1.0f; @@ -142,68 +364,118 @@ struct RadialFlowDecorr { int nBoot = 0; // active bootstrap samples (set in init()) bool doBoot = false; // bootstrap active for this run (base data fluc only) + // Variations filled by each pass (set in init()): maskEff in the MC efficiency pass, + // maskFlat in the flattening passes, maskAll in the mean and fluctuation passes. + uint32_t maskAll = 1; + uint32_t maskEff = 1; + uint32_t maskFlat = 1; + int itsLayerNom = kITSLayersAll; + int itsLayerVar = kITSLayer0123; + int occupancyMax = 0; // Occupancy variation cut of this system + float pupNSigma = 5.f; // width of the n-sigma pile-up bands of this system + int nchWidth = 10; // N_ch bin width of the observables of this system + int nchWidthNchBin2 = 2; // N_ch bin width of NchBin2 for this system + + // Forward bins: 0-2 detector totals (FT0A, FT0C, FV0A), then the eta sub-bins of each detector. + int nFwd = kNFwdDet; + std::array, kNFwdDet> fwdEdges; + std::array fwdOffset{}; + std::vector fwdLabels; + // --- configurables ----------------------------------------------------------- - Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "|z_{vtx}| acceptance (cm): collision filter + explicit event/particle vertex checks"}; + // Event selection (nominal) + struct : ConfigurableGroup { + Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "|z_{vtx}| acceptance (cm)"}; + Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; + Configurable cfgCentMax{"cfgCentMax", 99.f, "Events with centrality below this value (%) are kept"}; + Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; + Configurable cfgEvSelNoTimeFrameBorder{"cfgEvSelNoTimeFrameBorder", true, "kNoTimeFrameBorder"}; + Configurable cfgEvSelNoITSROFrameBorder{"cfgEvSelNoITSROFrameBorder", true, "kNoITSROFrameBorder"}; + Configurable cfgEvSelIsVertexITSTPC{"cfgEvSelIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal: kNoSameBunchPileup"}; + Configurable cfgEvSelNoCollInTimeRangeStd{"cfgEvSelNoCollInTimeRangeStd", true, "Pileup removal: kNoCollInTimeRangeStandard"}; + Configurable cfgITSLayerCut{"cfgITSLayerCut", -1, "ITS good-layer cut: -1 auto (pp 0123, else All), 0 none, 1 Layer3, 2 Layer0123, 3 LayersAll"}; + } cfgEvSel; + + // Pile-up cuts on the correlations of N_ch (global tracks) with N_PV and centrality. + // The linear cut is applied in every pass. The flattening passes (MC and DATA) fill + // Hist2D_globalTracks_cent and Hist2D_globalTracks_PVTracks after it; the mean +- n sigma + // bands of N_ch from these maps are then applied in the mean and fluctuation passes. + struct : ConfigurableGroup { + Configurable cfgPupnSigPbPb{"cfgPupnSigPbPb", 5.f, "Width of the n-sigma bands (number of RMS), PbPb"}; + Configurable cfgPupnSigOO{"cfgPupnSigOO", 5.f, "Width of the n-sigma bands (number of RMS), OO and NeNe"}; + Configurable cfgPupnSigPP{"cfgPupnSigPP", 6.f, "Width of the n-sigma bands (number of RMS), pp"}; + Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "Mean/Fluc: n-sigma band of N_ch versus centrality"}; + Configurable cfgApplySigPupCutPV{"cfgApplySigPupCutPV", 0, "Mean/Fluc: n-sigma band of N_ch versus N_PV"}; + Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Linear cut of N_ch versus N_PV (every pass)"}; + Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; + Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; + } cfgPileup; + + // Track selection (nominal), on top of isGlobalTrack (Run 3 globalTrack, incl. its 7 sigma pT-dependent DCAxy cut) Configurable cfgPtMin{"cfgPtMin", 0.2f, "min pT for observables"}; Configurable cfgPtMax{"cfgPtMax", 5.0f, "max pT for observables"}; - Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum DcaZ"}; - Configurable cfgCutTracKDcaMaxXY{"cfgCutTracKDcaMaxXY", 0.2f, "Maximum DcaXY"}; - - Configurable cfgPtDepDCAxy{"cfgPtDepDCAxy", false, "Use pt-dependent DCAxy cut"}; - Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0026f, "p0 for DCAxy"}; - Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.005f, "p1 for DCAxy"}; - Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.01f, "p2 for DCAxy"}; - - Configurable cfgPtDepDCAz{"cfgPtDepDCAz", false, "Use pt-dependent DCAz cut"}; - Configurable cfgDcaZP0{"cfgDcaZP0", 0.0026f, "p0 for DCAz"}; - Configurable cfgDcaZP1{"cfgDcaZP1", 0.005f, "p1 for DCAz"}; - Configurable cfgDcaZP2{"cfgDcaZP2", 1.01f, "p2 for DCAz"}; - - Configurable cfgITScluster{"cfgITScluster", 1, "Minimum Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 80, "Minimum Number of TPC cluster"}; - Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; - Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut (track selection)"}; Configurable cfgCutPtUpper{"cfgCutPtUpper", 10.0f, "Higher pT cut (track selection)"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "absolute Eta cut"}; Configurable cfgEtaBinWidth{"cfgEtaBinWidth", 0.1f, "DATA only: width of each narrow observable eta bin (eta units); cfgCutEta must be an integer multiple so eta=0 is a bin edge. MC is pinned to 0.2."}; - Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; - Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal"}; - Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time interval with dead ITS zone"}; - Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; - - Configurable cfgPupnSig{"cfgPupnSig", 6.0f, "Additional Pileup Cut"}; - Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "nSig Pileup Cut"}; - Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Lin Pileup Cut"}; - Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; - Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; - - Configurable cfgNchPbMax{"cfgNchPbMax", 5000, "Max Nch range for PbPb collisions"}; - Configurable cfgNchOMax{"cfgNchOMax", 800, "Max Nch range for OO collisions"}; + Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum |DCAz| (cm)"}; + Configurable cfgITScluster{"cfgITScluster", 5, "Nominal: itsNCls >= value"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Nominal: tpcNClsFound >= value"}; + Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Nominal: tpcChi2NCl < value"}; + Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + + // Systematic variations (names stay flat in the JSON) + struct : ConfigurableGroup { + Configurable cfgSystMask{"cfgSystMask", static_cast(KAllSystMask), "Bitmask of active variations, bit i = ESystType i: 0 Base (always on), 1 Cent, 2 Chi2TPC, 3 DCAxy, 4 DCAz, 5 Eff, 6 EffpTDep, 7 Flat, 8 ITSLayer, 9 NITS, 10 NTPC, 11 Pileup, 12 Vz, 13 NchBin2, 14 Occupancy, 15 PileupNSigma"}; + Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0105f, "globalTrack DCAxy cut p0 + p1 / pT^p2 (trackselection dcaSetup=0; dcaSetup=1: 0.004, 0.013, 1)"}; + Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.035f, "globalTrack DCAxy cut p1"}; + Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.1f, "globalTrack DCAxy cut p2"}; + Configurable cfgSystOccMaxPbPb{"cfgSystOccMaxPbPb", 2000, "Occupancy: maximum trackOccupancyInTimeRange, PbPb"}; + Configurable cfgSystOccMaxOO{"cfgSystOccMaxOO", 1000, "Occupancy: maximum trackOccupancyInTimeRange, OO and NeNe"}; + Configurable cfgSystOccMaxPP{"cfgSystOccMaxPP", 500, "Occupancy: maximum trackOccupancyInTimeRange, pp"}; + } cfgSyst; + + // N_ch axes. Largest N_ch (global tracks) seen in the data: 3542 (PbPb), 328 (OO), 72 (pp) + struct : ConfigurableGroup { + Configurable cfgNchPbMax{"cfgNchPbMax", 4000, "Max N_ch (global tracks) and N_PV of the axes, PbPb"}; + Configurable cfgNchOMax{"cfgNchOMax", 400, "Max N_ch (global tracks) and N_PV of the axes, OO and NeNe"}; + Configurable cfgNchPPMax{"cfgNchPPMax", 100, "Max N_ch (global tracks) and N_PV of the axes, pp"}; + Configurable cfgNchBinWidthPbPb{"cfgNchBinWidthPbPb", 10, "N_ch bin width (tracks) of the observables, PbPb; Mean and Fluc passes must use the same value"}; + Configurable cfgNchBinWidthOO{"cfgNchBinWidthOO", 5, "N_ch bin width (tracks) of the observables, OO and NeNe"}; + Configurable cfgNchBinWidthPP{"cfgNchBinWidthPP", 2, "N_ch bin width (tracks) of the observables, pp"}; + } cfgNch; Configurable cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; - Configurable cfgSystType{"cfgSystType", 0, "Systematic variation: 0=Base,1=systCent,2=systDCA,3=systEff,4=systEffpTDep,5=systFlat,6=systNITS,7=systNTPC,8=systPileup,9=systVz"}; - Configurable cfgNBootstrap{"cfgNBootstrap", 20, "Number of Poisson bootstrap samples (base data run only)"}; + Configurable cfgNBootstrap{"cfgNBootstrap", 20, "Number of Poisson bootstrap samples (Base, DataFluc only)"}; Configurable cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"}; Configurable cfgFlat{"cfgFlat", false, "Whether to use flattening weights"}; Configurable cfgEff{"cfgEff", false, "Whether to use Efficiency weights"}; - Configurable cfgEffVarPercent{"cfgEffVarPercent", 0.f, "Signed relative efficiency variation (%): +10 or -10"}; - Configurable cfgEffVarPtDep{"cfgEffVarPtDep", false, "false: constant variation; true: linear decrease with pT"}; - Configurable cfgZDC{"cfgZDC", false, "Whether to use ZDC for pileup histograms"}; + + // Forward multiplicity (FT0A, FT0C, FV0A amplitudes) correlated with mid-rapidity [pT] and N + struct : ConfigurableGroup { + Configurable cfgFwd{"cfgFwd", true, "Mean/Fluc: forward amplitudes and their covariance with [pT](eta) and N(eta)"}; + Configurable cfgFwdGainEq{"cfgFwdGainEq", true, "DATA: apply the FT0/FV0 gain equalisation from cfgGainEqPath"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path of the FT0/FV0 gain equalisation (+ /FT0, /FV0); central objects per run for LHC23zzh Pb-Pb and 2025 O-O/Ne-Ne"}; + Configurable cfgFwdVzCorr{"cfgFwdVzCorr", true, "Channel eta relative to the primary vertex (else to z = 0)"}; + Configurable> cfgFwdEtaEdgesFT0A{"cfgFwdEtaEdgesFT0A", {3.5f, 4.2f, 4.9f}, "FT0A eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFT0C{"cfgFwdEtaEdgesFT0C", {-3.3f, -2.7f, -2.1f}, "FT0C eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFV0A{"cfgFwdEtaEdgesFV0A", {2.2f, 2.7f, 3.2f, 3.9f, 4.5f, 5.1f}, "FV0A eta sub-bin edges"}; + } cfgFwdOpt; Configurable cfgCCDBurl{"cfgCCDBurl", "https://alice-ccdb.cern.ch", "ccdb url"}; Configurable cfgCCDBUserPath{"cfgCCDBUserPath", "/Users/s/somadutt", "Base CCDB path"}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {0.0, 1.0, 5.0, 10, 20, 40, 60, 80, 100}, "centrality axis (percentile)"}; - // --- axes -------------------------------------------------------------------- - AxisSpec centAxis{cfgAxisCent, "Centrality (%)"}; AxisSpec centAxis1Per{100, 0.0, 100.0, "Centrality (%)"}; - AxisSpec nChAxis{1, 0., 1., "Nch", "Nch"}; - AxisSpec nChAxis2{1, 0., 1., "Nch", "Nch"}; + AxisSpec centAxisBoot{20, 0.0, 100.0, "Centrality (%)"}; // bootstrap replicas: final 5% classes + AxisSpec multQAAxis{1, 0., 1., "Nch", "Nch"}; // QA (N_PV correlations) + AxisSpec nchAxisObs{1, 0., 1., "N_{ch}"}; // every observable (bins of nchWidth) + AxisSpec nchAxisNchBin2{1, 0., 1., "N_{ch}"}; // NchBin2 (bins of nchWidthNchBin2) + AxisSpec centAxisEff{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "Centrality (%)"}; // efficiency maps AxisSpec vzAxis{5, -12.5, 12.5, "Vz"}; AxisSpec chgAxis{3, -1.5, 1.5}; @@ -220,6 +492,7 @@ struct RadialFlowDecorr { AxisSpec etaBinAxis{10, -0.5, 9.5, "#eta bin Number"}; AxisSpec gapAxis{15, -1.5, 1.5, "#Delta#eta"}; AxisSpec sumAxis{15, -1.5, 1.5, "#Sigma#eta"}; + AxisSpec fwdAxis{kNFwdDet, -0.5, static_cast(kNFwdDet) - 0.5, "forward bin"}; // --- process switches -------------------------------------------------------- Configurable cfgRunGetEff{"cfgRunGetEff", false, "Run MC pass to build efficiency/fake maps"}; @@ -233,174 +506,419 @@ struct RadialFlowDecorr { Service ccdb{}; Service pdg{}; + + // histos: Base and QA; histosBoot: bootstrap replicas; histosSystA-D: the variations, + // 4 per registry (a registry holds at most 512 objects). HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosBoot{"HistosBoot", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystA{"HistosSystA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystB{"HistosSystB", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystC{"HistosSystC", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystD{"HistosSystD", {}, OutputObjHandlingPolicy::AnalysisObject}; + + HistogramRegistry& reg(int v) + { + if (v == kSystBase) { + return histos; + } + switch ((v - 1) / KNSystPerRegistry) { + case 0: + return histosSystA; + case 1: + return histosSystB; + case 2: + return histosSystC; + default: + return histosSystD; + } + } + + const AxisSpec& nchAxisOf(int v) const { return v == kSystNchBin ? nchAxisNchBin2 : nchAxisObs; } + int nchBinWidthOf(int v) const { return v == kSystNchBin ? nchWidthNchBin2 : nchWidth; } TRandom3 rng; // bootstrap Poisson weights - // --- persistent state -------------------------------------------------------- - struct InternalState { - TH3F* hEff = nullptr; - TH3F* hFake = nullptr; - THnSparseF* hFlatWeight = nullptr; - - std::vector> mLimitsNchCent; - float mMinXNchCent = 0, mMaxXNchCent = 0; - - // MC mean maps (per eta bin): truth / reco / reco-eff-corrected - TProfile2D* pmeanTruNchEtabinStep2 = nullptr; - TProfile2D* pmeanRecoNchEtabinStep2 = nullptr; - TProfile2D* pmeanRecoEffcorrNchEtabinStep2 = nullptr; - - // Data mean maps (per eta bin) - TProfile2D* pmeanNchEtabinStep2 = nullptr; - - } state; - - // --- bootstrap replica storage (base data fluc only) ------------------------- - struct BootstrapHists { - std::array, KMaxBoot> multCent{}; - std::array, KMaxBoot> multMult{}; - std::array, KMaxBoot> meanpTCent{}; - std::array, KMaxBoot> meanpTMult{}; - std::array, KMaxBoot> c2Cent{}; - std::array, KMaxBoot> c2Mult{}; - std::array, KMaxBoot> c3Cent{}; - std::array, KMaxBoot> c3Mult{}; - std::array, KMaxBoot> c2SubCent{}; - std::array, KMaxBoot> c2SubMult{}; - std::array, KMaxBoot> c3SubCent{}; - std::array, KMaxBoot> c3SubMult{}; - std::array, KMaxBoot> c2Sub2D{}; - std::array, KMaxBoot> c3Sub2D{}; - std::array, KMaxBoot> gapSum2D{}; - std::array, KMaxBoot> c3GapSum2D{}; - } bs; + // --- per-variation inputs ---------------------------------------------------- + struct SystInputs { + // efficiency and fake rate versus (centrality, pT, eta), and integrated over centrality (one + // centrality bin: used where the cell is empty or not a valid efficiency) + TH3* hEff = nullptr; + TH3* hFake = nullptr; + TH3* hEffPtEta = nullptr; + TH3* hFakePtEta = nullptr; + THnSparseF* hFlat = nullptr; + // mean pass, per level: <[pT]> and vs (N_ch, eta bin), vs (N_ch, forward bin) + std::array pmean{}; + std::array pmeanFwd{}; + }; + std::array inputs{}; + + // mean +- n sigma of N_ch per bin of x (centrality or N_PV), from the flattening pass + struct NSigmaBand { + std::vector> meanRms; // per bin of x; rms < 0 for an empty bin + float xMin = 0.f, xMax = 0.f; + bool applied = false; + }; + enum EPileupBand { + kBandCent = 0, + kBandPV, + kNBands + }; + std::array pileupBands{}; + + // --- per-variation histograms, filled through pointers (HIST() needs names known at compile time) --- + struct EffHists { + std::array, kNEffCat> maps{}; // (centrality, pT, eta), one per EEffCat + }; + struct FlatHists { + std::shared_ptr maps; // (vz, sign, pT, eta, phi, EFlatType) + }; + struct MeanHists { + std::shared_ptr mean; // (N_ch, eta bin, EMeanObs) + std::shared_ptr fwdMean; // (N_ch, forward bin): + }; + struct FlucHists { + std::shared_ptr obs; // (N_ch, eta bin, EFlucObs) + std::shared_ptr c2Sub2D; // (N_ch, eta_A, eta_C) + std::shared_ptr c3Sub2D; // (N_ch, eta_A pair, eta_C single) + std::shared_ptr gapSum2D; // (N_ch, Delta eta, Sigma eta) of the C2 map + std::shared_ptr c3GapSum2D; // (N_ch, eta_pair - eta_single, eta_pair + eta_single) + std::shared_ptr fwdVar; // (N_ch, forward bin): + std::shared_ptr covFwdPt; // (N_ch, eta bin, forward bin): + std::shared_ptr covFwdN; // (N_ch, eta bin, forward bin): + }; + struct { + std::shared_ptr evtCount; // (EEvtCut, variation) + std::shared_ptr trkCount; // (ETrkCut, variation) + std::array, kNSyst> evtCount1D{}; // EEvtCut, one per variation + std::array, kNSyst> trkCount1D{}; // ETrkCut, one per variation + std::shared_ptr nchOccupancy; // (N_ch, occupancy) of the Base events + std::array, kNQAStage>, kNQAEvt1D> qaEvt1D{}; + std::array, kNQAStage>, kNQAEvt2D> qaEvt2D{}; + std::array, kNQAStage>, kNQATrk1D> qaTrk1D{}; + std::array, kNQAStage>, kNQATrk2D> qaTrk2D{}; + std::array, kNSyst> nchCent{}; // (centrality, N_ch) per variation + std::array eff{}; + std::array flat{}; + std::array, kNSyst> mean{}; + std::array, kNSyst> fluc{}; + std::array boot{}; + std::shared_ptr fwdChannel; // (channel: FT0 0-207, FV0 208-255; EChanQA) + std::shared_ptr fwdEta; // amplitude-weighted eta per forward bin + std::shared_ptr mcTrackOrigin; // (N_ch, pT, ETrackOrigin) of the Base tracks + std::shared_ptr mcVtx; // (z_reco - z_true, EVtxQA) + } hists; + + // --- forward detector calibration (per run) ---------------------------------- + struct { + o2::ft0::Geometry ft0Det; + o2::fv0::Geometry* fv0Det = nullptr; + int run = -1; + std::array, KNChFT0> posFT0{}; + std::array, KNChFV0> posFV0{}; + std::array gainFT0{}; + std::array gainFV0{}; + } fwdCalib; + + // --- per-event work arrays --------------------------------------------------- + // Power sums S_{m,k} = sum w^k pT^m per eta bin. + struct PowerSums { + std::array, KIntM>, KNEtaMax> pmwk{}; + std::array, KNEtaMax> wk{}; + void reset() + { + for (auto& a : pmwk) { + for (auto& b : a) { + b.fill(0.); + } + } + for (auto& a : wk) { + a.fill(0.); + } + } + void add(int ieta, double pt, double w) + { + double wPow = 1.; + for (int k = 0; k < KIntK; ++k) { + double ptPow = 1.; + for (int m = 0; m < KIntM; ++m) { + pmwk[ieta][m][k] += wPow * ptPow; + ptPow *= pt; + } + wk[ieta][k] += wPow; + wPow *= w; + } + } + }; + std::array, kNSyst> sums{}; + // track cut flow of the current event, added to hTrkCount once per event + std::array, kNSyst> trkCounts{}; + + struct FwdSignal { + std::array ampl{}; // amplitude (data, MC reco) or number of particles (MC truth) + std::array valid{}; // detector read out in this event + }; + + struct EventSelection { + uint32_t mask = 0; + std::array cent{}; + int occupancy = -1; + }; + + // Per-event observables of one (variation, level). + struct EventObs { + float nCh = 0.f; + std::array nW{}, mean{}, c2{}, c3{}, p1kBar{}, dN{}; + std::array fwd{}, dFwd{}; + std::array fwdValid{}; + }; // =========================================================================== // Selection helpers // =========================================================================== template - bool isEventSelected(const T& col) + static bool passItsLayerCut(int cut, const T& col) { - histos.fill(HIST("hEvtCount"), 0.5); - if (!col.sel8()) { - return false; + switch (cut) { + case kITSLayer3: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer3); + case kITSLayer0123: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123); + case kITSLayersAll: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + default: + return true; } - histos.fill(HIST("hEvtCount"), 1.5); - if (std::abs(col.posZ()) > cfgVtxZCut) { + } + + // N_ch within mean +- nSigma RMS at x (events outside the x range of the band are rejected, + // empty bins of the band accept every event). + static bool insideBand(const NSigmaBand& band, float x, float nCh, float nSigma) + { + const auto& mr = band.meanRms; + int bin = 1 + static_cast((x - band.xMin) / (band.xMax - band.xMin) * (mr.size() - 2)); + if (bin < 1 || bin >= static_cast(mr.size() - 1)) { return false; } - histos.fill(HIST("hEvtCount"), 2.5); - if (cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; + if (mr[bin].second < 0.f) { + return true; } - histos.fill(HIST("hEvtCount"), 3.5); - if (cfgUseGoodITSLayerAllCut && !col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return std::abs(nCh - mr[bin].first) <= nSigma * mr[bin].second; + } + + // Linear cut and n-sigma bands of width nSigma. + bool isPassAddPileup(float multPV, int trksize, float cent, float nSigma) + { + if (pileupBands[kBandCent].applied && !insideBand(pileupBands[kBandCent], cent, trksize, nSigma)) { return false; } - histos.fill(HIST("hEvtCount"), 4.5); - if (cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (pileupBands[kBandPV].applied && !insideBand(pileupBands[kBandPV], multPV, trksize, nSigma)) { return false; } - histos.fill(HIST("hEvtCount"), 5.5); + if (cfgPileup.cfgApplyLinPupCut) { + if (trksize > (cfgPileup.cfgLinPupParam0 + cfgPileup.cfgLinPupParam1 * multPV)) { + return false; + } + if (trksize < (cfgPileup.cfgLinPupParam2 + cfgPileup.cfgLinPupParam3 * multPV)) { + return false; + } + } return true; } - bool isPassAddPileup(float multPV, int trksize, float cent) + // One event at cut-flow stage s of variation v: hEvtCountSyst and that variation's hEvtCount. + void countEvent(int s, int v) { - auto checkLimits = [](float x, float y, const std::vector>& limits, float xM, float xMx) { - if (limits.empty()) { - return true; - } - int bin = 1 + static_cast((x - xM) / (xMx - xM) * (limits.size() - 2)); - if (bin < 1 || bin >= static_cast(limits.size() - 1)) { - return false; + hists.evtCount->Fill(s, v); + if (hists.evtCount1D[v]) { + hists.evtCount1D[v]->Fill(s); + } + } + + // Event selection of all active variations: returns the variations the event passes and its + // centrality for each of them. The cuts common to all variations come first. The event cut + // flow of every active variation is filled unless count is false (cut QA). + template + EventSelection isEventSelected(const T& col, int nCh, uint32_t active, bool count = true) + { + EventSelection ev; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst && count; ++v) { + if (TESTBIT(active, v)) { + countEvent(s, v); + } } - return (y >= limits[bin].first && y <= limits[bin].second); }; - if (cfgApplySigPupCut) { - if (!checkLimits(cent, trksize, state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent)) { - return false; - } - histos.fill(HIST("hEvtCount"), 6.5); + countAll(kEvAll); + if (!col.sel8()) { + return ev; } - if (cfgApplyLinPupCut) { - if (trksize > (cfgLinPupParam0 + cfgLinPupParam1 * multPV)) { - return false; + countAll(kEvSel8); + if (std::abs(col.posZ()) > cfgEvSel.cfgVtxZCut) { + return ev; + } + countAll(kEvVtxZ); + if (cfgEvSel.cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return ev; + } + countAll(kEvGoodZvtx); + if (cfgEvSel.cfgEvSelNoTimeFrameBorder && !col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return ev; + } + countAll(kEvTFBorder); + if (cfgEvSel.cfgEvSelNoITSROFrameBorder && !col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return ev; + } + countAll(kEvITSROFBorder); + if (cfgEvSel.cfgEvSelIsVertexITSTPC && !col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return ev; + } + countAll(kEvVertexITSTPC); + + const float centBase = getCentrality(col, cfgEvSel.cfgCentralityChoice); + const float multPV = col.multNTracksPV(); + ev.occupancy = col.trackOccupancyInTimeRange(); + const bool passAddPileup = isPassAddPileup(multPV, nCh, centBase, pupNSigma); + const bool passAddPileupTight = isPassAddPileup(multPV, nCh, centBase, pupNSigma - KSystPupnSigStep); + + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; } - histos.fill(HIST("hEvtCount"), 7.5); - if (trksize < (cfgLinPupParam2 + cfgLinPupParam3 * multPV)) { - return false; + auto countOne = [&](int s) { + if (count) { + countEvent(s, v); + } + }; + const float cent = (v == kSystCent) ? getCentrality(col, KSystCentralityChoice) : centBase; + ev.cent[v] = cent; + if (cfgEvSel.cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + countOne(kEvSameBunch); + if (cfgEvSel.cfgEvSelNoCollInTimeRangeStd && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } + countOne(kEvTimeRange); + if (!passItsLayerCut(v == kSystITSLayer ? itsLayerVar : itsLayerNom, col)) { + continue; + } + countOne(kEvITSLayer); + if (!(v == kSystPileupNSigma ? passAddPileupTight : passAddPileup)) { + continue; + } + countOne(kEvAddPileup); + // the cut of the event-selection variations + if (v == kSystPileup && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + continue; + } + if (v == kSystVz && !(std::abs(col.posZ()) < KSystVtxZCut)) { + continue; + } + if (v == kSystOccupancy && (ev.occupancy < 0 || ev.occupancy >= occupancyMax)) { + continue; + } + countOne(kEvVariationCut); + if (!(cent < cfgEvSel.cfgCentMax)) { + continue; + } + countOne(kEvCent); + SETBIT(ev.mask, v); + if (col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + countOne(kEvInfoPrevRof); + } + if (ev.occupancy < 0) { + countOne(kEvInfoOccUnknown); } - histos.fill(HIST("hEvtCount"), 8.5); } - return true; + return ev; } + // Track selection of all variations the event passed (evMask): returns the variations the + // track passes. The cuts common to all variations come first. The cut flow is collected in + // trkCounts (unless count is false: cut QA) and added to the track counters by flushTrackCounts(). template - bool isTrackSelected(const T& trk) + uint32_t isTrackSelected(const T& trk, uint32_t evMask, bool count = true) { - histos.fill(HIST("hTrkCount"), 0.5); + const double inc = count ? 1. : 0.; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(evMask, v)) { + trkCounts[v][s] += inc; + } + } + }; + countAll(kTrkAll); if (trk.sign() == 0) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 1.5); + countAll(kTrkSign); if (!trk.has_collision()) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 2.5); + countAll(kTrkHasColl); if (!trk.isPVContributor()) { - return false; - } - histos.fill(HIST("hTrkCount"), 3.5); - if (!(trk.itsNCls() > cfgITScluster)) { - return false; - } - histos.fill(HIST("hTrkCount"), 4.5); - if (!(trk.tpcNClsFound() >= cfgTPCcluster)) { - return false; - } - histos.fill(HIST("hTrkCount"), 5.5); - if (!(trk.tpcNClsCrossedRows() >= cfgTPCnCrossedRows)) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 6.5); + countAll(kTrkPVContrib); if (trk.pt() < cfgCutPtLower || trk.pt() > cfgCutPtUpper || std::abs(trk.eta()) > cfgCutEta) { - return false; + return 0; } - histos.fill(HIST("hTrkCount"), 7.5); - if (!trk.isGlobalTrack()) { - return false; + countAll(kTrkPtEta); + if (!trk.isGlobalTrack() || trk.tpcNClsCrossedRows() < cfgTPCnCrossedRows) { + return 0; } - histos.fill(HIST("hTrkCount"), 8.5); + countAll(kTrkGlobal); - if (cfgPtDepDCAxy) { - float maxDcaXY = cfgDcaXyP0 + cfgDcaXyP1 / std::pow(trk.pt(), cfgDcaXyP2); - if (std::abs(trk.dcaXY()) > maxDcaXY) { - return false; + const float maxDcaXY = KSystDcaXyScale * (cfgSyst.cfgDcaXyP0 + cfgSyst.cfgDcaXyP1 / std::pow(trk.pt(), cfgSyst.cfgDcaXyP2.value)); + uint32_t mask = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(evMask, v)) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } else { - if (std::abs(trk.dcaXY()) > cfgCutTracKDcaMaxXY) { - return false; + if (!(trk.itsNCls() >= (v == kSystNITS ? KSystITScluster : cfgITScluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } - if (cfgPtDepDCAz) { - float maxDcaZ = cfgDcaZP0 + cfgDcaZP1 / std::pow(trk.pt(), cfgDcaZP2); - if (std::abs(trk.dcaZ()) > maxDcaZ) { - return false; + trkCounts[v][kTrkITS] += inc; + if (!(trk.tpcNClsFound() >= (v == kSystNTPC ? KSystTPCcluster : cfgTPCcluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 10.5); - } else { - if (std::abs(trk.dcaZ()) > cfgCutTracKDcaMaxZ) { - return false; + trkCounts[v][kTrkTPC] += inc; + if (!(trk.tpcChi2NCl() < (v == kSystChi2TPC ? KSystChi2TPC : cfgCutTpcChi2NCl.value))) { + continue; + } + trkCounts[v][kTrkTPCChi2] += inc; + if (!(std::abs(trk.dcaZ()) < (v == kSystDCAz ? KSystDcaMaxZ : cfgCutTracKDcaMaxZ.value))) { + continue; + } + trkCounts[v][kTrkDCAz] += inc; + // the cut of the DCAxy variation + if (v == kSystDCAxy && !(std::abs(trk.dcaXY()) < maxDcaXY)) { + continue; + } + trkCounts[v][kTrkVariationCut] += inc; + SETBIT(mask, v); + } + return mask; + } + + // Adds the track cut flow of the event to hTrkCountSyst and hTrkCount. + void flushTrackCounts() + { + for (int v = 0; v < kNSyst; ++v) { + for (int s = 0; s < kNTrkCut; ++s) { + if (trkCounts[v][s] > 0.) { + hists.trkCount->Fill(s, v, trkCounts[v][s]); + if (hists.trkCount1D[v]) { + hists.trkCount1D[v]->Fill(s, trkCounts[v][s]); + } + trkCounts[v][s] = 0.; + } } - histos.fill(HIST("hTrkCount"), 10.5); } - return true; } + // Charged primary in the observable acceptance (event vertex cuts are applied to the reco collision). template bool isParticleSelected(const T& particle) { @@ -414,122 +932,125 @@ struct RadialFlowDecorr { if (particle.pt() < cfgCutPtLower || particle.pt() > cfgCutPtUpper || std::abs(particle.eta()) > cfgCutEta) { return false; } - if (std::abs(particle.vz()) > cfgVtxZCut) { - return false; - } return true; } - float getCentrality(const auto& col) const + // Origin of a reco MC track (ETrackOrigin) with respect to the MC collision of coll. + template + int trackOrigin(const T& track, const C& coll) const + { + if (!track.has_mcParticle()) { + return kOriginNoLabel; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != coll.mcCollisionId()) { + return kOriginOtherColl; + } + return particle.isPhysicalPrimary() ? kOriginPrimary : kOriginSecondary; + } + + float getCentrality(const auto& col, int choice) const { - if (cfgCentralityChoice.value == kCentFT0C) { + if (choice == kCentFT0C) { return col.centFT0C(); } - if (cfgCentralityChoice.value == kCentFT0M) { + if (choice == kCentFT0M) { return col.centFT0M(); } - if (cfgCentralityChoice.value == kCentFDDM) { + if (choice == kCentFDDM) { return col.centFDDM(); } - if (cfgCentralityChoice.value == kCentFV0A) { + if (choice == kCentFV0A) { return col.centFV0A(); } return KinvalidCentrality; } - // Inclusive efficiency/fake lookup (no species dependence). - float getEfficiency(float mult, float pt, float eta, - int effidx, bool useEff) const + // Observable eta bins containing eta: the full-range bin 0 and one narrow bin. + int etaBinsOf(float eta, std::array& bins) const { - if (!useEff) { - return (effidx == 0) ? 1.0f : 0.0f; + int n = 0; + for (int ieta = 0; ieta < nEta && n < 2; ++ieta) { + if (eta > etaLw[ieta] && eta <= etaUp[ieta]) { + bins[n++] = ieta; + } } + return n; + } - const float invalid = - std::numeric_limits::quiet_NaN(); - - TH3F* h = (effidx == 0) ? state.hEff : state.hFake; - - if (!h || !std::isfinite(mult) || - !std::isfinite(pt) || !std::isfinite(eta)) { + // =========================================================================== + // Corrections + // =========================================================================== + // Inclusive efficiency/fake lookup (no species dependence). + static float getEfficiency(const TH3* h, float cent, float pt, float eta, bool isEff) + { + const float invalid = std::numeric_limits::quiet_NaN(); + if (!h || !std::isfinite(cent) || !std::isfinite(pt) || !std::isfinite(eta)) { return invalid; } - - int ibx = h->GetXaxis()->FindFixBin(mult); - - // Reject Nch underflow, clamp overflow. + int ibx = h->GetXaxis()->FindFixBin(cent); + // Reject centrality underflow, clamp overflow. if (ibx < 1) { return invalid; } - ibx = std::min(ibx, h->GetNbinsX()); - int iby = h->GetYaxis()->FindFixBin(pt); int ibz = h->GetZaxis()->FindFixBin(eta); - // Do not extrapolate in pT or eta. - if (iby < 1 || iby > h->GetNbinsY() || - ibz < 1 || ibz > h->GetNbinsZ()) { + if (iby < 1 || iby > h->GetNbinsY() || ibz < 1 || ibz > h->GetNbinsZ()) { return invalid; } - const double val = h->GetBinContent(ibx, iby, ibz); const double err = h->GetBinError(ibx, iby, ibz); - - // A bin without a positive error is not a valid measurement. - if (!std::isfinite(val) || - !std::isfinite(err) || err <= 0.0) { + // A bin without a positive error is not a valid measurement, except a fake rate of 0 + // (no fake track in the bin), which has no error. + if (!std::isfinite(val) || !std::isfinite(err) || (err <= 0.0 && (isEff || val != 0.0))) { return invalid; } - // Physical-domain checks. - if (val < 0.0 || val > 1.0 || - (effidx == 0 && val == 0.0)) { + if (val < 0.0 || val > 1.0 || (isEff && val == 0.0)) { return invalid; } - return static_cast(val); } - float efficiencyVariationFactor(float pt) const + // Nominal efficiency and fake rate of the map of variation v (no variation factor). + bool getValidEffFake(int v, float cent, float pt, float eta, float& eff, float& fake) { - if (cfgSystType.value != kSystEff && cfgSystType.value != kSystEffPtDep) { - return 1.f; - } - // Empirical shape: full variation at cfgPtMin, zero at cfgPtMax. - const bool ptDep = cfgEffVarPtDep.value || cfgSystType.value == kSystEffPtDep; - const float shape = ptDep ? (cfgPtMax.value - pt) / (cfgPtMax.value - cfgPtMin.value) : 1.f; - return 1.f + 0.01f * cfgEffVarPercent.value * shape; + if (!cfgEff) { + eff = 1.f; + fake = 0.f; + return true; + } + const auto& in = inputs[v]; + auto valid = [&]() { return std::isfinite(eff) && std::isfinite(fake) && eff > KFloatEpsilon && eff <= 1.f && fake >= 0.f && fake < 1.f; }; + eff = getEfficiency(in.hEff, cent, pt, eta, true); + fake = getEfficiency(in.hFake, cent, pt, eta, false); + if (valid()) { + return true; + } + // empty or statistically invalid cell, or no valid centrality: the map integrated over centrality + const float centAll = in.hEffPtEta ? in.hEffPtEta->GetXaxis()->GetBinCenter(1) : 0.f; + eff = getEfficiency(in.hEffPtEta, centAll, pt, eta, true); + fake = getEfficiency(in.hFakePtEta, centAll, pt, eta, false); + return valid(); } - bool getValidEffFake(float mult, float pt, float eta, bool useEff, - float& eff, float& fake) const + float efficiencyVariationFactor(int v, float pt) { - eff = getEfficiency(mult, pt, eta, 0, useEff); - fake = getEfficiency(mult, pt, eta, 1, useEff); - - // Validate the nominal corrections before applying any variation. - if (!std::isfinite(eff) || !std::isfinite(fake) || - eff <= KFloatEpsilon || eff > 1.f || - fake < 0.f || fake >= 1.f) { - return false; + if (v == kSystEff) { + return 1.f + 0.01f * KEffVarPercent; } - - // Apply the variation once, in DATA mean/fluctuation jobs. - if (useEff && (cfgRunDataMean.value || cfgRunDataFluc.value)) { - eff *= efficiencyVariationFactor(pt); + if (v == kSystEffPtDep) { + // Linear in pT: full variation at cfgPtMin, none at KEffVarPtZero. + const float shape = std::clamp((KEffVarPtZero - pt) / (KEffVarPtZero - cfgPtMin), 0.f, 1.f); + return 1.f + 0.01f * KEffVarPercent * shape; } - - // The varied efficiency is an effective correction denominator. - return std::isfinite(eff) && eff > KFloatEpsilon; + return 1.f; } - float getFlatteningWeight(float vz, float chg, float pt, float eta, float phi, bool useFlat) const + static float getFlatteningWeight(const THnSparseF* h, float vz, float chg, float pt, float eta, float phi) { - if (!useFlat) { - return 1.0; - } - THnSparseF* h = state.hFlatWeight; if (!h) { return 0.0; } @@ -542,137 +1063,57 @@ struct RadialFlowDecorr { return h->GetBinContent(bins.data()); } - TH3F* rebinNchMap(const TH3F* h, int sysConfig, - const char* newName) const + // Weight w = w_flat (1 - fake) / eff of the track for every variation in mask; a map shared + // by several variations is looked up once. Returns the variations with a usable weight and + // the Base efficiency, fake rate and flattening weight for the QA profiles. + uint32_t getTrackWeights(uint32_t mask, float cent, float vz, float sign, float pt, float eta, float phi, bool useFlat, + std::array& weights, float& effBase, float& fakeBase, float& flatBase) { - if (!h) { - return nullptr; - } - - // PbPb: 100 tracks; NeNe, OO, pp: 10 tracks - const int trackStep = (sysConfig == 1) ? 100 : 10; - - const int nx = h->GetNbinsX(); - const int ny = h->GetNbinsY(); - const int nz = h->GetNbinsZ(); - - const double originalWidth = h->GetXaxis()->GetBinWidth(1); - - const int groupSize = - static_cast(std::lround(trackStep / originalWidth)); - - // Ensure the input binning allows exact 10/100-track groups - if (groupSize < 1 || - std::abs(groupSize * originalWidth - trackStep) > KFloatEpsilon) { - LOGF(fatal, "Nch bin width is incompatible with %d-track rebinning", - trackStep); - return nullptr; - } - - for (int ix = 1; ix <= nx; ++ix) { - if (std::abs(h->GetXaxis()->GetBinWidth(ix) - - originalWidth) > KFloatEpsilon) { - LOGF(fatal, "Input Nch axis must have uniform bin widths"); - return nullptr; + std::array effDone{}; + std::array effS{}, fakeS{}, flatS{}; + std::array flatDone{}; + uint32_t weightMask = 0; + effBase = std::numeric_limits::quiet_NaN(); + fakeBase = std::numeric_limits::quiet_NaN(); + flatBase = 1.f; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; } - } - - // Construct the new Nch bin edges - std::vector xEdges; - xEdges.push_back(h->GetXaxis()->GetBinLowEdge(1)); - - for (int ix = 1; ix <= nx; ix += groupSize) { - const int last = std::min(ix + groupSize - 1, nx); - xEdges.push_back(h->GetXaxis()->GetBinUpEdge(last)); - } - - // Preserve the original pT and eta axes exactly - auto copyEdges = [](const TAxis* axis) { - std::vector edges; - - for (int i = 1; i <= axis->GetNbins(); ++i) { - edges.push_back(axis->GetBinLowEdge(i)); + const int es = effSource(v); + if (effDone[es] == 0) { + effDone[es] = getValidEffFake(es, cent, pt, eta, effS[es], fakeS[es]) ? 1 : -1; } - - edges.push_back(axis->GetBinUpEdge(axis->GetNbins())); - return edges; - }; - - const auto yEdges = copyEdges(h->GetYaxis()); - const auto zEdges = copyEdges(h->GetZaxis()); - - const int nNewX = static_cast(xEdges.size()) - 1; - - auto* rebinned = new TH3F( - newName, h->GetTitle(), - nNewX, xEdges.data(), - ny, yEdges.data(), - nz, zEdges.data()); - - rebinned->SetDirectory(nullptr); - rebinned->Sumw2(); - - // Rebin independently for each (pT, eta) slice - for (int iy = 1; iy <= ny; ++iy) { - for (int iz = 1; iz <= nz; ++iz) { - - int lastValid = 0; - double lastValue = 0.; - double lastError = 0.; - - for (int ib = 1; ib <= nNewX; ++ib) { - - const int first = (ib - 1) * groupSize + 1; - const int last = std::min(first + groupSize - 1, nx); - - double sumW = 0.; - double sumVW = 0.; - - for (int ix = first; ix <= last; ++ix) { - - const double v = h->GetBinContent(ix, iy, iz); - const double e = h->GetBinError(ix, iy, iz); - - // Inverse-variance weighting requires a positive error - if (!std::isfinite(v) || - !std::isfinite(e) || e <= 0.) { - continue; - } - - const double weight = 1. / (e * e); - - sumW += weight; - sumVW += v * weight; - } - - if (sumW > 0.) { - - const double avg = sumVW / sumW; - const double err = std::sqrt(1. / sumW); - - rebinned->SetBinContent(ib, iy, iz, avg); - rebinned->SetBinError(ib, iy, iz, err); - - lastValid = ib; - lastValue = avg; - lastError = err; - } - } - - // Extend the final valid value into trailing empty bins. - // This also allows safe overflow clamping at readout. - for (int ib = lastValid + 1; ib <= nNewX; ++ib) { - if (lastValid == 0) { - break; - } - - rebinned->SetBinContent(ib, iy, iz, lastValue); - rebinned->SetBinError(ib, iy, iz, lastError); + if (effDone[es] < 0) { + continue; + } + const float eff = effS[es] * efficiencyVariationFactor(v, pt); + if (!(eff > KFloatEpsilon)) { + continue; + } + float flat = 1.f; + const int fs = flatSource(v); + if (useFlat && cfgFlat && fs >= 0) { + if (!flatDone[fs]) { + flatS[fs] = getFlatteningWeight(inputs[fs].hFlat, vz, sign, pt, eta, phi); + flatDone[fs] = true; } + flat = flatS[fs]; + } + const float weight = flat * (1.0f - fakeS[es]) / eff; + if (!std::isfinite(weight) || weight <= 0.f) { + continue; + } + weights[v] = weight; + SETBIT(weightMask, v); + trkCounts[v][kTrkWeight] += 1.; + if (v == kSystBase) { + effBase = eff; + fakeBase = fakeS[es]; + flatBase = flat; } } - - return rebinned; + return weightMask; } // Per-event two- and three-particle pT correlators (standard method) from the @@ -721,310 +1162,919 @@ struct RadialFlowDecorr { } // =========================================================================== - // Table joins + // Forward detectors // =========================================================================== - using GeneralCollisions = soa::Join; - - Filter collisionFilter = nabs(aod::collision::posZ) < cfgVtxZCut; - using AodCollisionsSel = soa::Filtered; + // Eta sub-bin of detector det containing eta, -1 outside its edges. + int fwdSubBin(int det, float eta) const + { + const auto& e = fwdEdges[det]; + if (e.size() < 2 || eta < e.front() || eta >= e.back()) { + return -1; + } + const int i = static_cast(std::upper_bound(e.begin(), e.end(), eta) - e.begin()) - 1; + return fwdOffset[det] + i; + } - using UnfilteredTracks = soa::Join< + void addFwd(int det, float eta, float ampl, FwdSignal& fwd) + { + fwd.ampl[det] += ampl; + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[sub] += ampl; + } + if (hists.fwdEta) { + hists.fwdEta->Fill(det, eta, ampl); + if (sub >= 0) { + hists.fwdEta->Fill(sub, eta, ampl); + } + } + } + + void markFwdValid(int det, FwdSignal& fwd) const + { + fwd.valid[det] = true; + for (int i = 0; i + 1 < static_cast(fwdEdges[det].size()); ++i) { + fwd.valid[fwdOffset[det] + i] = true; + } + } + + float channelEta(const std::array& p, float vz) + { + const float r = std::hypot(p[0], p[1]); + const float dz = p[2] - (cfgFwdOpt.cfgFwdVzCorr ? vz : 0.f); + return r > 0.f ? std::asinh(dz / r) : std::numeric_limits::quiet_NaN(); + } + + void fillChannelQA(int ch, float raw, float corr, float eta) + { + if (hists.fwdChannel) { + hists.fwdChannel->Fill(ch, kChAmpRaw, raw); + hists.fwdChannel->Fill(ch, kChAmpCorr, corr); + hists.fwdChannel->Fill(ch, kChEta, eta); + } + } + + // Channel positions with the FT0/FV0 alignment (as in PWGCF/flowDecorrelation) and the + // gain equalisation, loaded at each new run. + template + void loadFwdCalib(const B& bc, bool applyGain) + { + if (bc.runNumber() == fwdCalib.run) { + return; + } + fwdCalib.run = bc.runNumber(); + const uint64_t ts = bc.timestamp(); + + auto* alFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", ts); + auto* alFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", ts); + if (!alFT0 || alFT0->size() < 2 || !alFV0 || alFV0->size() < 2) { + LOGF(fatal, "Could not load FT0/FV0 alignment (FT0/Calib/Align, FV0/Calib/Align) for timestamp %llu", ts); + return; + } + + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + fwdCalib.ft0Det.calculateChannelCenter(); + for (int ch = 0; ch < KNChFT0; ++ch) { + const int side = ch < KNChFT0A ? 0 : 1; + const auto c = fwdCalib.ft0Det.getChannelCenter(ch); + float z = c.Z() + (*alFT0)[side].getZ(); + if (ch >= KNChFT0A) { + z = -z; + } + fwdCalib.posFT0[ch] = {static_cast(c.X() + (*alFT0)[side].getX()), static_cast(c.Y() + (*alFT0)[side].getY()), z}; + } + // offsetFV0[0]: left half, offsetFV0[1]: right half + static constexpr std::array KCellsInLeft = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; + for (int ch = 0; ch < KNChFV0; ++ch) { + const bool left = std::find(KCellsInLeft.begin(), KCellsInLeft.end(), ch) != KCellsInLeft.end(); + const auto& off = (*alFV0)[left ? 0 : 1]; + const auto c = fwdCalib.fv0Det->getReadoutCenter(ch); + fwdCalib.posFV0[ch] = {static_cast(c.x + off.getX()), static_cast(c.y + off.getY()), static_cast(c.z + off.getZ())}; + } + + fwdCalib.gainFT0.fill(1.f); + fwdCalib.gainFV0.fill(1.f); + if (applyGain && cfgFwdOpt.cfgFwdGainEq) { + auto* gFT0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FT0", ts); + auto* gFV0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FV0", ts); + if (gFT0 && gFT0->size() >= static_cast(KNChFT0)) { + std::copy_n(gFT0->begin(), KNChFT0, fwdCalib.gainFT0.begin()); + } else { + LOGF(warning, "No FT0 gain equalisation at %s/FT0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } + if (gFV0 && gFV0->size() >= static_cast(KNChFV0)) { + std::copy_n(gFV0->begin(), KNChFV0, fwdCalib.gainFV0.begin()); + } else { + LOGF(warning, "No FV0 gain equalisation at %s/FV0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } + } + LOGF(info, "Forward-detector calibration loaded for run %d", fwdCalib.run); + } + + // Gain-equalised amplitudes of FT0A, FT0C and FV0A, totals and eta sub-bins. + template + void computeFwdReco(const C& col, float vz, FwdSignal& fwd, bool qa) + { + fwd = {}; + auto corr = [](float a, float g) { return g > 0.f ? a / g : a; }; + if (col.has_foundFT0()) { + const auto& ft0 = col.foundFT0(); + markFwdValid(kFT0A, fwd); + markFwdValid(kFT0C, fwd); + for (std::size_t i = 0; i < ft0.channelA().size(); ++i) { + const int id = ft0.channelA()[i]; + if (id < 0 || id >= KNChFT0A) { + continue; + } + const float a = corr(ft0.amplitudeA()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0A, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeA()[i], a, eta); + } + } + for (std::size_t i = 0; i < ft0.channelC().size(); ++i) { + const int id = ft0.channelC()[i] + KNChFT0A; + if (id < KNChFT0A || id >= KNChFT0) { + continue; + } + const float a = corr(ft0.amplitudeC()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0C, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeC()[i], a, eta); + } + } + } + if (col.has_foundFV0()) { + const auto& fv0 = col.foundFV0(); + markFwdValid(kFV0A, fwd); + for (std::size_t i = 0; i < fv0.channel().size(); ++i) { + const int id = fv0.channel()[i]; + if (id < 0 || id >= KNChFV0) { + continue; + } + const float a = corr(fv0.amplitude()[i], fwdCalib.gainFV0[id]); + const float eta = channelEta(fwdCalib.posFV0[id], vz); + addFwd(kFV0A, eta, a, fwd); + if (qa) { + fillChannelQA(KNChFT0 + id, fv0.amplitude()[i], a, eta); + } + } + } + } + + // Number of charged primaries in the acceptance of each forward detector (MC truth). + template + void computeFwdTruth(const P& particles, FwdSignal& fwd) + { + fwd = {}; + for (int det = 0; det < kNFwdDet; ++det) { + markFwdValid(det, fwd); + } + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const float eta = particle.eta(); + if (std::abs(eta) < 1.5f) { + continue; + } + auto* pd = pdg->GetParticle(particle.pdgCode()); + if (!pd || std::abs(pd->Charge()) == 0) { + continue; + } + for (int det = 0; det < kNFwdDet; ++det) { + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[det] += 1.; + fwd.ampl[sub] += 1.; + } + } + } + } + + // =========================================================================== + // Observables + // =========================================================================== + // Bin content of p at (x, ybin[, zbin]), NaN for an empty bin. + static double profileValue(const TProfile2D* p, double x, int ybin) + { + if (!p) { + return std::numeric_limits::quiet_NaN(); + } + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return p->GetBinContent(gb); + } + static double profileValue(const TProfile3D* p, double x, int ybin, int zbin) + { + if (!p) { + return std::numeric_limits::quiet_NaN(); + } + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin, zbin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return p->GetBinContent(gb); + } + + void computeEventObs(const PowerSums& s, int v, int level, const FwdSignal* fwd, float nCh, EventObs& evObs) + { + const auto& input = inputs[v]; + const double nan = std::numeric_limits::quiet_NaN(); + evObs.nCh = nCh; + for (int ieta = 0; ieta < nEta; ++ieta) { + evObs.nW[ieta] = s.wk[ieta][1]; + evObs.mean[ieta] = (s.wk[ieta][1] > KFloatEpsilon) ? (s.pmwk[ieta][1][1] / s.wk[ieta][1]) : nan; + evObs.c2[ieta] = nan; + evObs.c3[ieta] = nan; + evObs.p1kBar[ieta] = nan; + // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference + const double mmpt = profileValue(input.pmean[level], nCh, ieta + 1, kMeanPt + 1); + if (std::isfinite(mmpt) && mmpt != 0) { + const auto corr = calculateC2C3FromSums(s.pmwk[ieta], s.wk[ieta], mmpt); + evObs.c2[ieta] = corr.c2; + evObs.c3[ieta] = corr.c3; + if (std::isfinite(evObs.mean[ieta])) { + evObs.p1kBar[ieta] = evObs.mean[ieta] - mmpt; + } + } + evObs.dN[ieta] = evObs.nW[ieta] - profileValue(input.pmean[level], nCh, ieta + 1, kMeanN + 1); + } + evObs.fwdValid.fill(false); + if (!fwd || !input.pmeanFwd[level]) { + return; + } + for (int k = 0; k < nFwd; ++k) { + if (!fwd->valid[k]) { + continue; + } + const double m = profileValue(input.pmeanFwd[level], nCh, k + 1); + if (!std::isfinite(m)) { + continue; + } + evObs.fwd[k] = fwd->ampl[k]; + evObs.dFwd[k] = fwd->ampl[k] - m; + evObs.fwdValid[k] = true; + } + } + + // Fill the fluctuation profiles of one (variation, level) at x = N_ch, or of one bootstrap + // replica with weight w at x = centrality. + void fillFluc(const FlucHists& h, const EventObs& evObs, double w, float x) + { + auto fill = [w](const std::shared_ptr& p, double a, double b, double c, double val) { + if (p) { + p->Fill(a, b, c, val, w); + } + }; + + // multiplicity, meanpT, C2, C3 vs eta bin + for (int ieta = 0; ieta < nEta; ++ieta) { + fill(h.obs, x, ieta, kObsN, evObs.nW[ieta]); + if (std::isfinite(evObs.mean[ieta])) { + fill(h.obs, x, ieta, kObsMeanPt, evObs.mean[ieta]); + } + if (std::isfinite(evObs.c2[ieta])) { + fill(h.obs, x, ieta, kObsC2, evObs.c2[ieta]); + } + if (std::isfinite(evObs.c3[ieta])) { + fill(h.obs, x, ieta, kObsC3, evObs.c3[ieta]); + } + } + + // mirror-pair subevent (C2Sub, C3Sub) vs eta bin + for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { + int ietaC = nEta - ietaA; + float c2Sub = evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // Mirror pairs (A, C = -A): both orderings are filled, each at the bin of its pair, + // so bins A < 0 hold AAB and bins C > 0 hold ABB of the same mirror pair. For a + // symmetric collision system the two agree in expectation: eta -> -eta maps + // AAB(a, -a) onto AAB(-a, a) = ABB(a, -a). Comparing bin A with bin nEta - A is + // therefore a forward/backward check, not two independent observables. + float c3SubA = evObs.c2[ietaA] * evObs.p1kBar[ietaC]; // AAB: 2 particles from A, 1 from C + float c3SubC = evObs.c2[ietaC] * evObs.p1kBar[ietaA]; // ABB: 2 particles from C, 1 from A + + if (std::isfinite(c2Sub)) { + fill(h.obs, x, ietaA, kObsC2Sub, c2Sub); + } + if (std::isfinite(c3SubA)) { + fill(h.obs, x, ietaA, kObsC3Sub, c3SubA); + } + if (std::isfinite(c3SubC)) { + fill(h.obs, x, ietaC, kObsC3Sub, c3SubC); + } + } + + // full 2D subevent maps, in (eta_A, eta_C) and in (Delta eta, Sigma eta) + for (int ietaA = 1; ietaA < nEta; ++ietaA) { + for (int ietaC = 1; ietaC < nEta; ++ietaC) { + float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; + float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; + const float bw = etaUp[ietaA] - etaLw[ietaA]; + const float gap = (ietaA - ietaC) * bw; + const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * bw; // = cA + cC on the lattice + + float c2Sub = (ietaA == ietaC) ? static_cast(evObs.c2[ietaA]) : evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, + // two particles from A and one from C ("AAB"). The same pair of bins read the + // other way round, cell (C,A) = c2[C] p1kBar[A], is "ABB" (one from A, two from C). + // Both are filled, so the map carries AAB and ABB of every bin pair: the row at + // eta_A = eta_ref is AAB with the pair at eta_ref, the column is ABB. Unlike C2, + // this map is NOT symmetric under A <-> C, and the two triangles must not be + // averaged or treated as duplicates. Diagonal: the within-bin c3 ("AAA"). + float c3Sub2D = (ietaA == ietaC) ? static_cast(evObs.c3[ietaA]) : evObs.c2[ietaA] * evObs.p1kBar[ietaC]; + + if (std::isfinite(c2Sub)) { + fill(h.c2Sub2D, x, etaValA, etaValB, c2Sub); + fill(h.gapSum2D, x, gap, sum, c2Sub); + } + if (std::isfinite(c3Sub2D)) { + fill(h.c3Sub2D, x, etaValA, etaValB, c3Sub2D); + fill(h.c3GapSum2D, x, gap, sum, c3Sub2D); + } + } + } + + // forward amplitude a_k: , and , with + // da = a - , d[pT] = [pT] - <[pT]>, dN = N - , the means at N_ch from the mean pass + for (int k = 0; k < nFwd; ++k) { + if (!evObs.fwdValid[k]) { + continue; + } + const double da = evObs.dFwd[k]; + if (h.fwdVar) { + h.fwdVar->Fill(x, k, da * da, w); + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (std::isfinite(evObs.p1kBar[ieta])) { + fill(h.covFwdPt, x, ieta, k, evObs.p1kBar[ieta] * da); + } + if (std::isfinite(evObs.dN[ieta])) { + fill(h.covFwdN, x, ieta, k, evObs.dN[ieta] * da); + } + } + } + } + + // =========================================================================== + // Table joins + // =========================================================================== + using GeneralCollisions = soa::Join; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgEvSel.cfgVtxZCut; + using AodCollisionsSel = soa::Filtered; + + using UnfilteredTracks = soa::Join< aod::Tracks, aod::TracksExtra, aod::TrackSelection, aod::TracksDCA>; Filter trackFilter = aod::track::pt > KPtMin&& aod::track::pt < KPtMax&& requireGlobalTrackInFilter(); using AodTracksSel = soa::Filtered; using TCs = soa::Join; using FilteredTCs = soa::Filtered; - using BCsRun3 = soa::Join; using MyRun3MCCollisions = soa::Join< aod::Collisions, aod::EvSels, aod::Mults, aod::MultsExtra, aod::CentFT0Cs, aod::CentFT0Ms, aod::CentFDDMs, aod::CentFV0As, aod::CentNGlobals, aod::McCollisionLabels>; + // reconstructed collisions of one MC collision + using McRecoCollisions = soa::SmallGroups; - PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + Preslice tracksPerCollision = aod::track::collisionId; // =========================================================================== // Histogram declarations // =========================================================================== + void labelFwdAxis(TAxis* ax) const + { + for (int k = 0; k < nFwd && k < ax->GetNbins(); ++k) { + ax->SetBinLabel(k + 1, fwdLabels[k].c_str()); + } + } + + static void labelAxis(TAxis* ax, const std::vector& labels) + { + for (std::size_t i = 0; i < labels.size() && static_cast(i) < ax->GetNbins(); ++i) { + ax->SetBinLabel(i + 1, labels[i]); + } + } + + void labelSystAxis(TAxis* ax) const + { + for (int v = 0; v < kNSyst && v < ax->GetNbins(); ++v) { + ax->SetBinLabel(v + 1, systName[v].c_str()); + } + } + void declareCommonQA() { histos.add("hVtxZ_after_sel", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hVtxZ", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hCentrality", ";centrality (%)", kTH1F, {{centAxis1Per}}); - histos.add("Hist2D_globalTracks_PVTracks", ";N_{global};N_{PV}", kTH2F, {{nChAxis}, {nChAxis}}); - histos.add("Hist2D_cent_nch", ";N_{PV};cent (%)", kTH2F, {{nChAxis}, {centAxis1Per}}); - histos.add("Hist2D_globalTracks_cent", "cent (%);N_{global}", kTH2F, {{centAxis1Per}, {nChAxis}}); - histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {nChAxis}}); + histos.add("Hist2D_globalTracks_PVTracks", ";N_{PV};N_{ch} (global tracks)", kTH2F, {{multQAAxis}, {multQAAxis}}); + histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {multQAAxis}}); + hists.nchOccupancy = histos.add("Hist2D_Nch_occupancy", ";N_{ch} (global tracks);trackOccupancyInTimeRange", kTH2F, {{nchAxisObs}, {KNbinsOccQA, 0., KOccQAMax}}); histos.add("hP", ";p (GeV/c)", kTH1F, {{KNbinsPt, KPMin, KPMax}}); histos.add("hPt", ";p_{T} (GeV/c)", kTH1F, {{KNbinsPt, KPtMin, KPtMax}}); histos.add("hEta", ";#eta", kTH1F, {{KNbinsEtaFine, KEtaMin, KEtaMax}}); histos.add("hPhi", ";#phi", kTH1F, {{KNbinsPhi, KPhiMin, TwoPI}}); - histos.add("hEvtCount", "Number of Event;; Count", kTH1F, {{9, 0, 9}}); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(1, "all Events"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(2, "after sel8"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(3, "after VertexZ Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(4, "after kNoSameBunchPileup"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(5, "after kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(6, "after kIsGoodITSLayersAll"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(7, "after PVTracksCent Pileup Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(8, "after Linear Pileup Cut (Up)"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(9, "after Linear Pileup Cut (Lw)"); - - histos.add("hTrkCount", "Number of Tracks;; Count", kTH1F, {{11, 0, 11}}); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(1, "all Tracks"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(2, "after sign!=0"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(3, "after has_collision"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(4, "after isPVContributor"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(5, "after itsNCls"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(6, "after tpcNClsFound"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(7, "after tpcNClsCrossedRows"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(8, "after pT,#eta selections"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(9, "after isGlobalTrack"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(10, "after dcaXY"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(11, "after dcaZ"); - } - - void declareMCCommonHists() - { - histos.add("h3_AllPrimary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoMatchedToPrimary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_AllReco", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Secondary", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Fake", ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + // cut flows of every variation (y bin 1 = Base); 1D copies per variation in declareCounts() + hists.evtCount = histos.add("hEvtCountSyst", "Number of events;;variation", kTH2D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.evtCount->GetXaxis(), evtCutLabels()); + labelSystAxis(hists.evtCount->GetYaxis()); - histos.add("ptResolution", ";p_{T}^{MC};(p_{T}^{reco}-p_{T}^{MC})/p_{T}^{MC}", kTH2F, {{KNbinsPtRes, KPtMin, KPtMax}, {100, -0.2, 0.2}}); - histos.add("etaResolution", ";#eta^{MC};#eta^{reco}-#eta^{MC}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {100, -0.02, 0.02}}); - histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); - histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); - histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); + hists.trkCount = histos.add("hTrkCountSyst", "Number of tracks;;variation", kTH2D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.trkCount->GetXaxis(), trkCutLabels()); + labelSystAxis(hists.trkCount->GetYaxis()); } - void declareMCGetFlatHists() + static std::vector evtCutLabels() { - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + return {"all", "sel8", "|z_{vtx}|", "kIsGoodZvtxFT0vsPV", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kIsVertexITSTPC", + "kNoSameBunchPileup", "kNoCollInTimeRangeStandard", + "ITS good layers", "n#sigma/linear pile-up", "variation cut", "cent < cfgCentMax (selected)", + "selected: kNoHighMultCollInPrevRof", "selected: occupancy < 0"}; + } + static std::vector trkCutLabels() + { + return {"all", "sign#neq0", "has_collision", "isPVContributor", "p_{T}, #eta", "isGlobalTrack, crossed rows", + "itsNCls", "tpcNClsFound", "tpcChi2NCl", "DCAz", "variation cut (selected)", "valid weight (used)"}; } - void declareMCMeanHists() + // hEvtCount and hTrkCount of each variation of this pass (Base at the top level, the others + // in their folder), so that every variation's output carries its own cut flow. + void declareCounts(uint32_t mask) { - histos.add("Eff_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Eff_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Eff_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Eff_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("Fake_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Fake_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Fake_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Fake_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("wgt_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("wgt_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("wgt_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("wgt_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - - histos.add("MCGen/Prof_Cent_Nchrec", ";cent;#LT N#GT", kTProfile, {centAxis1Per}); - histos.add("MCGen/Prof_Mult_Nchrec", ";mult;#LT N#GT", kTProfile, {nChAxis}); - histos.add("MCGen/Prof_Cent_MeanpT", ";cent;#LT p_{T}#GT", kTProfile, {centAxis1Per}); - histos.add("MCGen/Prof_Mult_MeanpT", ";mult;#LT p_{T}#GT", kTProfile, {nChAxis}); - - histos.add("pmeanTru_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanReco_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanRecoEffcorr_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultTru_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultReco_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMultRecoEffcorr_nch_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - - histos.add("Prof2D_MeanpTSub_Tru", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_Reco", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_RecoEffCorr", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + hists.evtCount1D[v] = r.add((systPrefix(v) + "hEvtCount").c_str(), ("Number of events, " + systName[v]).c_str(), kTH1D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}}); + labelAxis(hists.evtCount1D[v]->GetXaxis(), evtCutLabels()); + hists.trkCount1D[v] = r.add((systPrefix(v) + "hTrkCount").c_str(), ("Number of tracks, " + systName[v]).c_str(), kTH1D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}}); + labelAxis(hists.trkCount1D[v]->GetXaxis(), trkCutLabels()); + } } - void declareMCFlucHists() + void declareCutQA() { - histos.add("MCGen/Prof_Cent_NEta_Nchrec", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_Nchrec", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_Cent_NEta_MeanpT", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_MeanpT", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + const AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; + for (int st = 0; st < kNQAStage; ++st) { + const std::string d = st == kQABefore ? "CutQA/before/" : "CutQA/after/"; + auto h1 = [&](const char* name, const char* title, const AxisSpec& ax) { return histos.add((d + name).c_str(), title, kTH1F, {ax}); }; + auto h2 = [&](const char* name, const char* title, const AxisSpec& ax, const AxisSpec& ay) { return histos.add((d + name).c_str(), title, kTH2F, {ax, ay}); }; + hists.qaEvt1D[kQAVtxZ][st] = h1("hVtxZ", ";z_{vtx} (cm)", {200, -20., 20.}); + hists.qaEvt1D[kQACent][st] = h1("hCent", ";centrality (%)", {centAxis1Per}); + hists.qaEvt1D[kQAOccupancy][st] = h1("hOccupancy", ";trackOccupancyInTimeRange", {KNbinsOccQA, 0., KOccQAMax}); + hists.qaEvt2D[kQANchNPV][st] = h2("hNch_NPV", ";N_{PV};N_{ch} (global tracks)", multQAAxis, multQAAxis); + hists.qaEvt2D[kQANchCent][st] = h2("hNch_cent", ";centrality (%);N_{ch} (global tracks)", centAxis1Per, multQAAxis); + hists.qaTrk1D[kQAPt][st] = h1("hTrkPt", "", ptAxis); + hists.qaTrk1D[kQAEta][st] = h1("hTrkEta", ";#eta", {60, -1.5, 1.5}); + hists.qaTrk1D[kQAItsNCls][st] = h1("hTrkItsNCls", ";ITS clusters", {8, -0.5, 7.5}); + hists.qaTrk1D[kQAItsChi2][st] = h1("hTrkItsChi2NCl", ";ITS #chi^{2}/cluster", {80, 0., 40.}); + hists.qaTrk1D[kQATpcNClsFound][st] = h1("hTrkTpcNClsFound", ";TPC found clusters", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRows][st] = h1("hTrkTpcCrossedRows", ";TPC crossed rows", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st] = h1("hTrkTpcCrossedRowsOverFindable", ";TPC crossed rows / findable clusters", {75, 0., 1.5}); + hists.qaTrk1D[kQATpcChi2][st] = h1("hTrkTpcChi2NCl", ";TPC #chi^{2}/cluster", {100, 0., 10.}); + hists.qaTrk1D[kQAPVContributor][st] = h1("hTrkPVContributor", ";isPVContributor", {2, -0.5, 1.5}); + hists.qaTrk1D[kQAGlobalTrack][st] = h1("hTrkGlobalTrack", ";isGlobalTrack", {2, -0.5, 1.5}); + hists.qaTrk2D[kQADcaXYPt][st] = h2("hTrkDcaXY_pt", ";p_{T} (GeV/c);DCA_{xy} (cm)", ptAxis, {400, -1., 1.}); + hists.qaTrk2D[kQADcaZPt][st] = h2("hTrkDcaZ_pt", ";p_{T} (GeV/c);DCA_{z} (cm)", ptAxis, {300, -3., 3.}); + } + } - histos.add("MCGen/Prof_MeanpT_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + template + void fillCutQAEvent(int st, const C& col, int nCh, float cent) + { + hists.qaEvt1D[kQAVtxZ][st]->Fill(col.posZ()); + hists.qaEvt1D[kQACent][st]->Fill(cent); + hists.qaEvt1D[kQAOccupancy][st]->Fill(col.trackOccupancyInTimeRange()); + hists.qaEvt2D[kQANchNPV][st]->Fill(col.multNTracksPV(), nCh); + hists.qaEvt2D[kQANchCent][st]->Fill(cent, nCh); + } - histos.add("MCGen/Prof_MeanpT_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + template + void fillCutQATrack(int st, const T& trk) + { + hists.qaTrk1D[kQAPt][st]->Fill(trk.pt()); + hists.qaTrk1D[kQAEta][st]->Fill(trk.eta()); + hists.qaTrk1D[kQAItsNCls][st]->Fill(trk.itsNCls()); + hists.qaTrk1D[kQAItsChi2][st]->Fill(trk.itsChi2NCl()); + hists.qaTrk1D[kQATpcNClsFound][st]->Fill(trk.tpcNClsFound()); + hists.qaTrk1D[kQATpcCrossedRows][st]->Fill(trk.tpcNClsCrossedRows()); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st]->Fill(trk.tpcCrossedRowsOverFindableCls()); + hists.qaTrk1D[kQATpcChi2][st]->Fill(trk.tpcChi2NCl()); + hists.qaTrk1D[kQAPVContributor][st]->Fill(trk.isPVContributor()); + hists.qaTrk1D[kQAGlobalTrack][st]->Fill(trk.isGlobalTrack()); + hists.qaTrk2D[kQADcaXYPt][st]->Fill(trk.pt(), trk.dcaXY()); + hists.qaTrk2D[kQADcaZPt][st]->Fill(trk.pt(), trk.dcaZ()); + } + + // (centrality, N_ch) of the selected events of every variation: its event count and the + // centrality of each N_ch bin with the estimator of the variation. + void declareNchCent(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.nchCent[v]) { + continue; + } + hists.nchCent[v] = reg(v).add((systPrefix(v) + "Hist2D_globalTracks_cent").c_str(), "cent (%);N_{ch} (global tracks)", kTH2F, {{centAxis1Per}, {nchAxisOf(v)}}); + } + } + + void declareFwdQA() + { + if (hists.fwdChannel) { + return; + } + hists.fwdChannel = histos.add("FwdQA/Prof_Channel_qa", ";channel (FT0A 0-95, FT0C 96-207, FV0A 208-255);quantity", kTProfile2D, {{KNChFT0 + KNChFV0, -0.5, KNChFT0 + KNChFV0 - 0.5}, {kNChQA, -0.5, static_cast(kNChQA) - 0.5}}); + labelAxis(hists.fwdChannel->GetYaxis(), {"amplitude", "gain-equalised amplitude", "#eta (aligned, vs z_{vtx})"}); + hists.fwdEta = histos.add("FwdQA/Prof_Eta_fwdbin", ";forward bin;amplitude-weighted #LT#eta#GT", kTProfile, {fwdAxis}); + labelFwdAxis(hists.fwdEta->GetXaxis()); + } - histos.add("MCGen/Prof_C3_Cent_etabin", ";cent;eta", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3_Mult_etabin", ";mult;eta", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Cent_etabin", ";cent;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("MCGen/Prof_C3Sub_Mult_etabin", ";mult;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + void declareEff(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int c = 0; c < kNEffCat; ++c) { + hists.eff[v].maps[c] = reg(v).add((systPrefix(v) + effMapName[c]).c_str(), ";centrality (%);p_{T} (GeV/c);#eta", kTH3F, + {{centAxisEff}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + } + } + } - histos.add("MCGen/hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + void declareMCQA() + { + histos.add("ptResolution", ";p_{T}^{MC};(p_{T}^{reco}-p_{T}^{MC})/p_{T}^{MC}", kTH2F, {{KNbinsPtRes, KPtMin, KPtMax}, {100, -0.2, 0.2}}); + histos.add("etaResolution", ";#eta^{MC};#eta^{reco}-#eta^{MC}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {100, -0.02, 0.02}}); + histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); + histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); + histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); + hists.mcTrackOrigin = histos.add("MCQA/Hist3D_Nch_pT_origin", ";N_{ch};p_{T} (GeV/c);origin", kTH3F, {{nchAxisObs}, {KNbinsPtRes, KPtMin, KPtMax}, {kNOrigin, -0.5, static_cast(kNOrigin) - 0.5}}); + labelAxis(hists.mcTrackOrigin->GetZaxis(), {"primary", "secondary", "other MC collision", "no label"}); + } - histos.add("MCGen/Prof_C2Sub2D_Cent_etaA_etaC", ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_GapSum2D", ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("MCGen/Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("MCGen/Prof_C3GapSum2D", ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); + // vertex QA of all MC passes + void declareMCVtxQA() + { + if (hists.mcVtx) { + return; + } + hists.mcVtx = histos.add("MCQA/Hist2D_dVz_type", ";z_{vtx}^{reco} - z_{vtx}^{MC} (cm);reco collision", kTH2F, {{200, -0.5, 0.5}, {kNVtxQA, -0.5, static_cast(kNVtxQA) - 0.5}}); + labelAxis(hists.mcVtx->GetYaxis(), {"single", "split: used", "split: skipped"}); } - void declareDataGetFlatHists() + // (vz, sign, pT, eta, phi) maps, unweighted, efficiency-weighted and with all weights; + // dir = "" (DATA) or "MCReco/". + void declareFlat(uint32_t mask, const std::string& dir) { - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hnTrkPVZDC", ";N_{PV};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 3000}}); - histos.add("hNchZDC", ";N_{trk};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 30000}}); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.flat[v].maps) { + continue; + } + hists.flat[v].maps = reg(v).add((systPrefix(v) + dir + "hFlatMaps").c_str(), ";vz;sign;pt;eta;phi;weight", kTHnSparseF, + {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}, {kNFlatType, -0.5, static_cast(kNFlatType) - 0.5}}); + labelAxis(hists.flat[v].maps->GetAxis(5), {"raw", "(1-fake)/eff", "all weights"}); + } } - void declareDataMeanHists() + // Mean-pass profiles; MC: in MCGen/, MCReco/, MCRecoEffCorr/. + void declareMean(uint32_t mask, bool isMC) { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + for (int l = 0; l < nLev; ++l) { + const std::string p = systPrefix(v) + (isMC ? mcDir[l] : std::string{}); + auto& h = hists.mean[v][l]; + h.mean = r.add((p + "Prof_Mean_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable", kTProfile3D, {{nchAxisOf(v)}, {etaBinAxis}, {kNMeanObs, -0.5, static_cast(kNMeanObs) - 0.5}}); + labelAxis(h.mean->GetZaxis(), {"#LT[p_{T}]#GT", "#LT N#GT"}); + if (cfgFwdOpt.cfgFwd) { + h.fwdMean = r.add((p + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT a#GT", kTProfile2D, {{nchAxisOf(v)}, {fwdAxis}}); + labelFwdAxis(h.fwdMean->GetYaxis()); + } + } + } + } - histos.add("Prof_Cent_Nchrec", ";cent;#LT N_{PV}#GT", kTProfile, {centAxis1Per}); - histos.add("Prof_Mult_Nchrec", ";N_{PV};#LT N_{PV}#GT", kTProfile, {nChAxis}); - histos.add("Prof_Cent_MeanpT", ";cent;#LT p_{T}#GT", kTProfile, {centAxis1Per}); - histos.add("Prof_Mult_MeanpT", ";N_{PV};#LT p_{T}#GT", kTProfile, {nChAxis}); + // Fluctuation-pass profiles named prefix + name + suffix, with x axis ax. + // withGapSum: also the maps in (Delta eta, Sigma eta). + FlucHists declareFluc(HistogramRegistry& r, const std::string& p, const std::string& s, const AxisSpec& ax, bool withGapSum) + { + FlucHists h; + auto n = [&](const char* name) { return p + name + s; }; + h.obs = r.add(n("Prof_Obs_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable", kTProfile3D, {{ax}, {etaBinAxis}, {kNFlucObs, -0.5, static_cast(kNFlucObs) - 0.5}}); + labelAxis(h.obs->GetZaxis(), {"N", "[p_{T}]", "c_{2}", "c_{3}", "C_{2}^{sub} (mirror)", "C_{3}^{sub} (mirror)"}); + h.c2Sub2D = r.add(n("Prof_C2Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A};#eta_{C}", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + h.c3Sub2D = r.add(n("Prof_C3Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + if (withGapSum) { + h.gapSum2D = r.add(n("Prof_GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{A} #minus #eta_{C} (Gap);#eta_{A} + #eta_{C} (Sum)", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + h.c3GapSum2D = r.add(n("Prof_C3GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + } + if (cfgFwdOpt.cfgFwd) { + h.fwdVar = r.add(n("Prof_FwdAmpVar_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT #delta a^{2}#GT", kTProfile2D, {{ax}, {fwdAxis}}); + h.covFwdPt = r.add(n("Prof_CovFwdAmpMeanpT_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta[p_{T}] #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + h.covFwdN = r.add(n("Prof_CovFwdAmpNch_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta N #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + labelFwdAxis(h.fwdVar->GetYaxis()); + labelFwdAxis(h.covFwdPt->GetZaxis()); + labelFwdAxis(h.covFwdN->GetZaxis()); + } + return h; + } - histos.add("pmean_nch_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmeanMult_nch_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("pmean_cent_etabin", ";Centrality (%);#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("pmeanMult_cent_etabin", ";Centrality (%);#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); + // Gap/sum maps for every DATA variation; in MC only for Base, to limit the memory. + void declareFlucAll(uint32_t mask, bool isMC) + { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int l = 0; l < nLev; ++l) { + hists.fluc[v][l] = declareFluc(reg(v), systPrefix(v) + (isMC ? mcDir[l] : std::string{}), "", nchAxisOf(v), !isMC || v == kSystBase); + } + } + } - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + // Poisson-bootstrap replicas of all fluctuation profiles (Base, DataFluc only). Their x axis + // is the Base centrality in 5% classes, the binning in which the errors are evaluated. + void declareBootstrapHists() + { + for (int s = 0; s < nBoot; ++s) { + hists.boot[s] = declareFluc(histosBoot, "Bootstrap/", Form("_sample%d", s), centAxisBoot, true); + } + } - histos.add("Prof2D_MeanpTSub", ";cent;#eta_{A} bin;#eta_{C} bin", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); + // Histograms of each pass, booked after the inputs so that switched-off variations get none. + void declareMCGetEffHists() + { + declareNchCent(maskEff); + declareEff(maskEff); + declareMCQA(); + } - histos.add("pEffWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT eff#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); - histos.add("pFakeWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT fake#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); - histos.add("pFlatWeight_pt_eta_cent", ";p_{T} (GeV/c);#eta;cent;#LT w_{#phi}#GT", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {centAxis1Per}}); + void declareMCGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, "MCReco/"); } - void declareDataFlucHists() + void declareMCMeanHists() { - histos.add("Prof_MeanpT_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_MeanpT_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C2_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C2_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C2Sub_Cent_etabin", ";Centrality;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C2Sub_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); // Base map with all weights, as QA + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareMean(maskAll, true); + histos.add("Prof_Weights_pT_type", ";p_{T};weight", kTProfile2D, {{KNbinsPtRes, KPtMin, KPtMax}, {3, -0.5, 2.5}}); + labelAxis(histos.get(HIST("Prof_Weights_pT_type"))->GetYaxis(), {"eff", "fake", "w"}); + } - histos.add("Prof_C3_Cent_etabin", ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C3_Mult_etabin", ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Cent_etabin", ";Centrality;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - histos.add("Prof_C3Sub_Mult_etabin", ";N_{PV};#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); + void declareMCFlucHists() + { + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, true); + } - histos.add("hEtaPhiReco", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd", ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}}); + void declareDataGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, ""); + } - histos.add("Prof_C2Sub2D_Cent_etaA_etaC", ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_GapSum2D", ";cent;#eta_{A} #minus #eta_{C} (Gap);#eta_{A} + #eta_{C} (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_C3Sub2D_Cent_etaA_etaC", ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_C3GapSum2D", ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); + void declareDataMeanHists() + { + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareMean(maskAll, false); + histos.add("Prof_Weights_pt_eta_type", ";p_{T} (GeV/c);#eta;weight", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {3, -0.5, 2.5}}); + labelAxis(histos.get(HIST("Prof_Weights_pt_eta_type"))->GetZaxis(), {"eff", "fake", "w_{#phi}"}); } - // 30 Poisson-bootstrap replicas of every final fluctuation observable - // (base data run only). Filled by pointer to bypass the compile-time HIST() - // macro, which cannot take a runtime sample index. - void declareBootstrapHists() + void declareDataFlucHists() { - for (int s = 0; s < nBoot; ++s) { - bs.multCent[s] = histos.add(Form("Bootstrap/Prof_Mult_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.multMult[s] = histos.add(Form("Bootstrap/Prof_Mult_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.meanpTCent[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.meanpTMult[s] = histos.add(Form("Bootstrap/Prof_MeanpT_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2Cent[s] = histos.add(Form("Bootstrap/Prof_C2_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c2Mult[s] = histos.add(Form("Bootstrap/Prof_C2_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2SubCent[s] = histos.add(Form("Bootstrap/Prof_C2Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c2SubMult[s] = histos.add(Form("Bootstrap/Prof_C2Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c3Cent[s] = histos.add(Form("Bootstrap/Prof_C3_Cent_etabin_sample%d", s), ";cent;#eta-bin", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c3Mult[s] = histos.add(Form("Bootstrap/Prof_C3_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c3SubCent[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Cent_etabin_sample%d", s), ";cent;#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{centAxis1Per}, {etaBinAxis}}); - bs.c3SubMult[s] = histos.add(Form("Bootstrap/Prof_C3Sub_Mult_etabin_sample%d", s), ";N_{PV};#eta-bin of the pair (single in the mirror bin)", kTProfile2D, {{nChAxis}, {etaBinAxis}}); - bs.c2Sub2D[s] = histos.add(Form("Bootstrap/Prof_C2Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.gapSum2D[s] = histos.add(Form("Bootstrap/Prof_GapSum2D_sample%d", s), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - bs.c3Sub2D[s] = histos.add(Form("Bootstrap/Prof_C3Sub2D_Cent_etaA_etaC_sample%d", s), ";cent;#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - bs.c3GapSum2D[s] = histos.add(Form("Bootstrap/Prof_C3GapSum2D_sample%d", s), ";cent;#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, false); + if (doBoot) { + declareBootstrapHists(); } } // =========================================================================== // CCDB helpers // =========================================================================== - THnSparseF* buildWeightMapFromRaw(THnSparseF* hRaw, const char* mapName) + static void copyAxis(TAxis* dst, const TAxis* src) { - if (!hRaw) { - LOGF(error, "Raw eta-phi map for '%s' is null; no flattening will be applied.", mapName); - return nullptr; + const TArrayD* xb = src->GetXbins(); + if (xb->GetSize() > 0) { + dst->Set(src->GetNbins(), xb->GetArray()); + } else { + dst->Set(src->GetNbins(), src->GetXmin(), src->GetXmax()); } - auto hWMap = dynamic_cast(hRaw->Clone(mapName)); - hWMap->SetTitle(Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName)); - hWMap->Reset(); - auto axV = hRaw->GetAxis(0); - auto axChg = hRaw->GetAxis(1); - auto axPt = hRaw->GetAxis(2); - auto axE = hRaw->GetAxis(3); - auto axP = hRaw->GetAxis(4); + } - std::array bins{}; - for (int iv = 1; iv <= axV->GetNbins(); ++iv) { - bins[0] = iv; - for (int ichg = 1; ichg <= axChg->GetNbins(); ++ichg) { - bins[1] = ichg; - for (int ipt = 1; ipt <= axPt->GetNbins(); ++ipt) { - bins[2] = ipt; - for (int ie = 1; ie <= axE->GetNbins(); ++ie) { - bins[3] = ie; + // Flattening weights w_phi = / N_phi from bin `type` of the last axis of hFlatMaps. + THnSparseF* buildWeightMapFromRaw(const THnSparse* hRaw, int type, const char* mapName) + { + if (!hRaw || hRaw->GetNdimensions() != 6) { + LOGF(error, "Raw eta-phi map for '%s' is null or not 6D; no flattening will be applied.", mapName); + return nullptr; + } + std::array nb{}; + for (int i = 0; i < 5; ++i) { + nb[i] = hRaw->GetAxis(i)->GetNbins(); + } + auto* hWMap = new THnSparseF(mapName, Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName), 5, nb.data()); + for (int i = 0; i < 5; ++i) { + copyAxis(hWMap->GetAxis(i), hRaw->GetAxis(i)); + } + + std::array bins{}; + std::array wbins{}; + bins[5] = type + 1; + for (int iv = 1; iv <= nb[0]; ++iv) { + bins[0] = wbins[0] = iv; + for (int ichg = 1; ichg <= nb[1]; ++ichg) { + bins[1] = wbins[1] = ichg; + for (int ipt = 1; ipt <= nb[2]; ++ipt) { + bins[2] = wbins[2] = ipt; + for (int ie = 1; ie <= nb[3]; ++ie) { + bins[3] = wbins[3] = ie; double sum = 0.0; - int nphi = axP->GetNbins(); + const int nphi = nb[4]; for (int ip = 1; ip <= nphi; ++ip) { bins[4] = ip; sum += hRaw->GetBinContent(bins.data()); } const double avg = (nphi > 0 ? sum / nphi : 0.0); for (int ip = 1; ip <= nphi; ++ip) { - bins[4] = ip; + bins[4] = wbins[4] = ip; const double raw = hRaw->GetBinContent(bins.data()); const double w = (avg > 0.0 && raw > 0.0) ? (avg / raw) : 1.0; - hWMap->SetBinContent(bins.data(), w); + hWMap->SetBinContent(wbins.data(), w); } } } } } - LOGF(info, "Flattening weight map '%s' built.", mapName); - return hWMap; + LOGF(info, "Flattening weight map '%s' built.", mapName); + return hWMap; + } + + template + void loadProfileFromList(TList* src, const char* name, TP*& target) + { + if (!src) { + return; + } + auto* obj = src->FindObject(name); + if (!obj) { + LOGF(error, "Profile %s missing in CCDB TList", name); + return; + } + auto* tp = dynamic_cast(obj); + if (!tp) { + LOGF(error, "%s is not the expected profile type (it is %s)", name, obj->ClassName()); + return; + } + target = dynamic_cast(tp->Clone()); + target->SetDirectory(nullptr); + LOGF(info, "Loaded %s from list", name); + } + + // Switch variation v off because an input is missing; a missing Base input is fatal. + void dropSyst(int v, const std::string& why) + { + if (v == kSystBase) { + LOGF(fatal, "Base input missing: %s", why.c_str()); + return; + } + LOGF(error, "Variation %s disabled: %s", systName[v].c_str(), why.c_str()); + CLRBIT(maskAll, v); + CLRBIT(maskEff, v); + CLRBIT(maskFlat, v); + } + + // Efficiency and fake-rate maps of variation s versus (centrality, pT, eta): + // eff = matched primaries / primaries, fake = (secondaries + other collision + no label) / all reco. + bool loadEffMaps(int s, const std::string& path, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return false; + } + std::array h{}; + for (int c = 0; c < kNEffCat; ++c) { + h[c] = dynamic_cast(lst->FindObject(effMapName[c].c_str())); + if (!h[c]) { + LOGF(error, "Missing efficiency map %s in %s", effMapName[c].c_str(), path.c_str()); + return false; + } + } + auto* eff = dynamic_cast(h[kEffRecoMatchedPrim]->Clone(Form("hEff_%d", s))); + auto* fake = dynamic_cast(h[kEffRecoSec]->Clone(Form("hFake_%d", s))); + eff->SetDirectory(nullptr); + fake->SetDirectory(nullptr); + fake->Add(h[kEffRecoOtherColl]); + fake->Add(h[kEffRecoFake]); + // integrated over centrality (one centrality bin), for empty or invalid cells + const int nCent = eff->GetNbinsX(); + auto ratio = [&](TH3* num, TH3* den, const char* name) { + auto* r = num->RebinX(nCent, name); + std::unique_ptr d(den->RebinX(nCent, Form("%s_den", name))); + r->SetDirectory(nullptr); + r->Divide(d.get()); + return r; + }; + inputs[s].hEffPtEta = ratio(eff, h[kEffAllPrim], Form("hEff_%d_ptEta", s)); + inputs[s].hFakePtEta = ratio(fake, h[kEffAllReco], Form("hFake_%d_ptEta", s)); + eff->Divide(h[kEffAllPrim]); + fake->Divide(h[kEffAllReco]); + inputs[s].hEff = eff; + inputs[s].hFake = fake; + return true; + } + + THnSparseF* loadFlatMap(int s, const std::string& path, const std::string& dir, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return nullptr; + } + const std::string hName = dir + "hFlatMaps"; + const auto* hRaw = dynamic_cast(lst->FindObject(hName.c_str())); + if (!hRaw) { + LOGF(error, "Flattening map '%s' not found in %s", hName.c_str(), path.c_str()); + return nullptr; + } + return buildWeightMapFromRaw(hRaw, cfgEff ? kFlatWtd : kFlatRaw, Form("hFlatWeight_%d", s)); } - template - void loadProfileFromList(TList* src, const char* name, TP*& target) + // Mean-pass objects of variation v for the fluctuation pass. + void loadMeans(int v, const std::string& path, bool isMC, int64_t now) { - if (!src) { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + dropSyst(v, "no mean-pT list at " + path); return; } - auto* obj = src->FindObject(name); - if (!obj) { - LOGF(error, "Profile %s missing in CCDB TList", name); - return; - } - auto* tp = dynamic_cast(obj); - if (!tp) { - LOGF(error, "%s is not the expected profile type (it is %s)", name, obj->ClassName()); - return; + const int nLev = isMC ? kNLevels : 1; + for (int l = 0; l < nLev; ++l) { + const std::string d = isMC ? mcDir[l] : std::string{}; + loadProfileFromList(lst, (d + "Prof_Mean_Nch_etabin_obs").c_str(), inputs[v].pmean[l]); + if (!inputs[v].pmean[l]) { + dropSyst(v, "mean-pT profile missing in " + path); + return; + } + // The fluctuation pass reads its reference at (N_ch bin, eta bin), so the binning of the + // mean pass has to be the same. + const auto* p = inputs[v].pmean[l]; + if (p->GetYaxis()->GetNbins() != nEta + 1 || + p->GetXaxis()->GetNbins() != nchAxisOf(v).getNbins() || + std::abs(p->GetXaxis()->GetBinWidth(1) - nchBinWidthOf(v)) > KFloatEpsilon) { + LOGF(fatal, + "Mean pass %s has %d N_ch bins of width %.1f and %d eta bins; this job uses %d bins of " + "width %d and %d eta bins. Rerun the mean pass with the same binning.", + path.c_str(), p->GetXaxis()->GetNbins(), p->GetXaxis()->GetBinWidth(1), p->GetYaxis()->GetNbins(), + nchAxisOf(v).getNbins(), nchBinWidthOf(v), nEta + 1); + } + if (cfgFwdOpt.cfgFwd) { + loadProfileFromList(lst, (d + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), inputs[v].pmeanFwd[l]); + if (inputs[v].pmeanFwd[l] && inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins() != nFwd) { + LOGF(error, "%s: mean pass has %d forward bins, this job %d; forward covariances not filled", + path.c_str(), inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins(), nFwd); + inputs[v].pmeanFwd[l] = nullptr; + } + } } - target = dynamic_cast(tp->Clone()); - target->SetDirectory(nullptr); - LOGF(info, "Loaded %s from list", name); } // =========================================================================== @@ -1033,16 +2083,27 @@ struct RadialFlowDecorr { void init(InitContext&) { TH1::SetDefaultSumw2(true); - if (cfgSystType == kSystCent && cfgCentralityChoice == kCentFT0C) { - LOGF(warning, "cfgSystType=systCent but cfgCentralityChoice is still FT0C (the base estimator)."); - } - // Nch axes by system + // Multiplicity axes: multQAAxis for the QA, nchAxisObs (nchAxisNchBin2) for the observables. + const int nMax = (cfgSys == kPbPb) ? cfgNch.cfgNchPbMax.value : (cfgSys == kpp ? cfgNch.cfgNchPPMax.value : cfgNch.cfgNchOMax.value); if (cfgSys == kPbPb) { - nChAxis = {cfgNchPbMax / 2, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchPbMax / 4, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + multQAAxis = {nMax / 2, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; } else { - nChAxis = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + multQAAxis = {nMax, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + } + { + // bin k holds k*width ... (k+1)*width - 1 tracks + auto makeNchAxis = [&](int width) { + if (width < 1) { + LOGF(fatal, "N_ch bin width must be >= 1 (got %d)", width); + } + const int nb = (nMax + width - 1) / width; + return AxisSpec{nb, -KBinOffset, nb * width - KBinOffset, "N_{ch}"}; + }; + const bool pp = (cfgSys == kpp); + nchWidth = (cfgSys == kPbPb) ? cfgNch.cfgNchBinWidthPbPb.value : (pp ? cfgNch.cfgNchBinWidthPP.value : cfgNch.cfgNchBinWidthOO.value); + nchWidthNchBin2 = (cfgSys == kPbPb) ? KSystNchBinWidthPbPb : (pp ? KSystNchBinWidthPP : KSystNchBinWidthOO); + nchAxisObs = makeNchAxis(nchWidth); + nchAxisNchBin2 = makeNchAxis(nchWidthNchBin2); } // ---- run type: MC is pinned to the reference binning, DATA is configurable ---- @@ -1106,9 +2167,65 @@ struct RadialFlowDecorr { sumAxis = AxisSpec{nGS, -gsMax, gsMax, "#Sigma#eta"}; } - // bootstrap active only for the base data fluctuation pass - doBoot = cfgRunDataFluc && (cfgSystType == kSystBase) && (cfgNBootstrap > 0); + // ---- forward bins: detector totals, then the eta sub-bins of each detector ---- + { + static const std::array detName = {"FT0A", "FT0C", "FV0A"}; + fwdEdges = {cfgFwdOpt.cfgFwdEtaEdgesFT0A.value, cfgFwdOpt.cfgFwdEtaEdgesFT0C.value, cfgFwdOpt.cfgFwdEtaEdgesFV0A.value}; + fwdLabels.assign(detName.begin(), detName.end()); + nFwd = kNFwdDet; + for (int det = 0; det < kNFwdDet; ++det) { + auto& e = fwdEdges[det]; + if (e.size() < 2 || !std::is_sorted(e.begin(), e.end())) { + LOGF(fatal, "%s eta edges must be >= 2 increasing values", detName[det]); + } + fwdOffset[det] = nFwd; + for (std::size_t i = 0; i + 1 < e.size(); ++i) { + fwdLabels.push_back(Form("%s %.1f<#eta<%.1f", detName[det], e[i], e[i + 1])); + ++nFwd; + } + } + if (nFwd > KNFwdMax) { + LOGF(fatal, "%d forward bins exceed KNFwdMax=%d", nFwd, KNFwdMax); + } + fwdAxis = AxisSpec{nFwd, -0.5, nFwd - 0.5, "forward bin"}; + } + + // ---- variations ---- + const uint32_t active = (static_cast(cfgSyst.cfgSystMask.value) | 1) & KAllSystMask; + maskAll = maskEff = maskFlat = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; + } + SETBIT(maskAll, v); + if (effSource(v) == v) { + SETBIT(maskEff, v); + } + if (flatSource(v) == v) { + SETBIT(maskFlat, v); + } + } + const bool isPP = (cfgSys == kpp); + itsLayerNom = cfgEvSel.cfgITSLayerCut >= 0 ? cfgEvSel.cfgITSLayerCut.value : (isPP ? kITSLayer0123 : kITSLayersAll); + if (itsLayerNom == kITSLayersAll) { + itsLayerVar = kITSLayer0123; + } else if (itsLayerNom == kITSLayer0123) { + itsLayerVar = kITSLayer3; + } else { + itsLayerVar = kITSLayersAll; + } + occupancyMax = (cfgSys == kPbPb) ? cfgSyst.cfgSystOccMaxPbPb.value : (isPP ? cfgSyst.cfgSystOccMaxPP.value : cfgSyst.cfgSystOccMaxOO.value); + pupNSigma = (cfgSys == kPbPb) ? cfgPileup.cfgPupnSigPbPb.value : (isPP ? cfgPileup.cfgPupnSigPP.value : cfgPileup.cfgPupnSigOO.value); + if (cfgEvSel.cfgCentralityChoice == KSystCentralityChoice && TESTBIT(maskAll, kSystCent)) { + LOGF(warning, "systCent uses the same centrality estimator as Base (%d).", cfgEvSel.cfgCentralityChoice.value); + } + + // bootstrap: Base data fluctuation pass only + doBoot = cfgRunDataFluc && (cfgNBootstrap > 0); nBoot = std::min(cfgNBootstrap, KMaxBoot); + if (cfgNBootstrap > KMaxBoot) { + LOGF(warning, "cfgNBootstrap=%d capped at %d", cfgNBootstrap.value, KMaxBoot); + } rng.SetSeed(cfgBootstrapSeed); // ---- CCDB ---- @@ -1117,6 +2234,10 @@ struct RadialFlowDecorr { ccdb->setLocalObjectValidityChecking(); int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); + // a missing object returns nullptr: every lookup is checked (a missing variation input switches + // the variation off, a missing Base input or alignment is fatal, a missing gain gives unit gains) + ccdb->setFatalWhenNull(false); + fwdCalib.fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); std::string sysDir; switch (cfgSys) { @@ -1135,803 +2256,674 @@ struct RadialFlowDecorr { default: LOGF(fatal, "Invalid cfgSys value: %d", cfgSys.value); } - std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; - std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; - std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; - std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; - std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; + const std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; + const std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; + const std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; + const std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; + const std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; - // ---- declarations ---- - declareCommonQA(); - if (cfgRunMCMean || cfgRunMCFluc || cfgRunGetEff) { - declareMCCommonHists(); + // ---- inputs of each variation; a variation without its inputs is switched off ---- + // variations used by the enabled passes + uint32_t used = 0; + if (cfgRunGetEff) { + used |= maskEff; } - if (cfgRunGetMCFlat) { - declareMCGetFlatHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); - } - if (cfgRunMCMean) { - declareMCMeanHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); - } - if (cfgRunMCFluc) { - declareMCFlucHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); + if (cfgRunGetMCFlat || cfgRunGetDataFlat) { + used |= maskFlat; } - if (cfgRunGetDataFlat) { - declareDataGetFlatHists(); - } - if (cfgRunDataMean) { - declareDataMeanHists(); - } - if (cfgRunDataFluc) { - declareDataFlucHists(); - if (doBoot) { - declareBootstrapHists(); - } + if (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc) { + used |= maskAll; } - // ---- efficiency/fake maps (inclusive) ---- - if (!cfgRunGetEff && cfgEff) { - auto* lst = ccdb->getForTimeStamp(pathEff, now); - if (!lst) { - LOGF(fatal, "Efficiency maps required but CCDB list is null at %s!", pathEff.c_str()); - return; - } - - // 1. Process Efficiency Map - auto* hNum = dynamic_cast(lst->FindObject("h3_RecoMatchedToPrimary")); - auto* hDen = dynamic_cast(lst->FindObject("h3_AllPrimary")); - if (hNum && hDen) { - state.hEff = dynamic_cast(hNum->Clone("hEff")); - state.hEff->SetDirectory(nullptr); - state.hEff->Sumw2(); - state.hEff->Divide(hDen); - - TH3F* rebinnedEff = rebinNchMap(state.hEff, cfgSys.value, "hEffRebinned"); - if (!rebinnedEff) { - LOGF(fatal, "Failed to rebin efficiency map"); - return; + // efficiency/fake maps + if (cfgEff && (cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc || isDataRun)) { + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(used, v)) { + continue; } - - delete state.hEff; - state.hEff = rebinnedEff; - - } else { - LOGF(fatal, "Missing CCDB objects for efficiency (h3_RecoMatchedToPrimary / h3_AllPrimary)."); - } - - // 2. Process Fake Map - auto* hNumS = dynamic_cast(lst->FindObject("h3_RecoUnMatchedToPrimary_Secondary")); - auto* hNumF = dynamic_cast(lst->FindObject("h3_RecoUnMatchedToPrimary_Fake")); - auto* hDenF = dynamic_cast(lst->FindObject("h3_AllReco")); - if (hNumS && hNumF && hDenF) { - state.hFake = dynamic_cast(hNumS->Clone("hFake")); - state.hFake->SetDirectory(nullptr); - state.hFake->Sumw2(); - state.hFake->Add(hNumF); // Secondary + fake: add exactly once. - state.hFake->Divide(hDenF); - - TH3F* rebinnedFake = rebinNchMap(state.hFake, cfgSys.value, "hFakeRebinned"); - if (!rebinnedFake) { - LOGF(fatal, "Failed to rebin fake map"); - return; + const int s = effSource(v); + if (loaded[s] == 0) { + loaded[s] = loadEffMaps(s, pathEff + systSuffix(s), now) ? 1 : -1; } - - delete state.hFake; - state.hFake = rebinnedFake; - - } else { - LOGF(fatal, "Missing CCDB objects for fakes."); - } - } - - // ---- flattening maps (inclusive) ---- - if (!cfgRunGetEff && cfgFlat) { - if (cfgRunDataMean || cfgRunDataFluc) { - auto* lstDataFlat = ccdb->getForTimeStamp(pathDataFlat, now); - if (lstDataFlat) { - std::string hName = cfgEff ? "hEtaPhiRecoWtd" : "hEtaPhiReco"; - auto* hRaw = dynamic_cast(lstDataFlat->FindObject(hName.c_str())); - if (hRaw) { - state.hFlatWeight = buildWeightMapFromRaw(hRaw, "hFlatWeight"); - } else { - LOGF(error, "Data flattening map '%s' not found.", hName.c_str()); - } - } else { - LOGF(error, "Could not retrieve Data Flattening TList from: %s", pathDataFlat.c_str()); - } - } - if (cfgRunMCMean || cfgRunMCFluc) { - auto* lstMCFlat = ccdb->getForTimeStamp(pathMCFlat, now); - if (lstMCFlat) { - std::string hName = cfgEff ? "MCReco/hEtaPhiRecoWtd" : "MCReco/hEtaPhiReco"; - auto* hRaw = dynamic_cast(lstMCFlat->FindObject(hName.c_str())); - if (hRaw) { - state.hFlatWeight = buildWeightMapFromRaw(hRaw, "hFlatWeight"); - } else { - LOGF(warning, "MC flattening source '%s' not found.", hName.c_str()); - } - } else { - LOGF(error, "Could not retrieve MC Flattening TList from: %s", pathMCFlat.c_str()); + if (loaded[s] < 0) { + dropSyst(v, "no efficiency maps at " + pathEff + systSuffix(s)); + continue; + } + inputs[v].hEff = inputs[s].hEff; + inputs[v].hFake = inputs[s].hFake; + inputs[v].hEffPtEta = inputs[s].hEffPtEta; + inputs[v].hFakePtEta = inputs[s].hFakePtEta; + } + } + + // flattening maps + if (cfgFlat && (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc)) { + const bool mc = cfgRunMCMean || cfgRunMCFluc; + const std::string& path = mc ? pathMCFlat : pathDataFlat; + const std::string dir = mc ? "MCReco/" : ""; + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + const int s = flatSource(v); + if (!TESTBIT(maskAll, v) || s < 0) { + continue; + } + if (loaded[s] == 0) { + inputs[s].hFlat = loadFlatMap(s, path + systSuffix(s), dir, now); + loaded[s] = inputs[s].hFlat ? 1 : -1; + } + if (loaded[s] < 0) { + dropSyst(v, "no flattening map at " + path + systSuffix(s)); + continue; } + inputs[v].hFlat = inputs[s].hFlat; } } - // ---- sigma-pileup limits ---- - // These were produced by the (now removed) nSigma pass. They are re-sourced - // from the flattening-map file, which also stores Hist2D_globalTracks_cent. - // Loaded only when the sigma-pileup cut is actually requested. - if (cfgApplySigPupCut) { - const bool mcSide = (cfgRunGetEff || cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc); - std::string limPath = mcSide ? pathMCFlat : pathDataFlat; - auto* limList = ccdb->getForTimeStamp(limPath, now); - if (limList) { - auto loadLimits = [&](const char* name, std::vector>& limits, float& xMin, float& xMax) { - auto* h2 = dynamic_cast(limList->FindObject(name)); + // ---- n-sigma pile-up bands, from the Base flattening output of the same production ---- + // The flattening and efficiency passes only apply the linear cut: their maps define the bands. + { + const bool meanOrFluc = cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc; + const bool wantCent = cfgPileup.cfgApplySigPupCut && meanOrFluc; + const bool wantPV = cfgPileup.cfgApplySigPupCutPV && meanOrFluc; + if ((wantCent || wantPV) && (cfgRunGetEff || cfgRunGetMCFlat || cfgRunGetDataFlat)) { + LOGF(fatal, "The n-sigma pile-up bands come from the flattening pass: run it in a separate job before the mean/fluctuation passes."); + } + if (wantCent || wantPV) { + const std::string& bandPath = isMcRun ? pathMCFlat : pathDataFlat; + auto* bandList = ccdb->getForTimeStamp(bandPath, now); + if (!bandList) { + LOGF(fatal, "n-sigma pile-up bands requested but no flattening output at %s", bandPath.c_str()); + } + auto makeBand = [&](const char* name, NSigmaBand& band) { + auto* h2 = dynamic_cast(bandList->FindObject(name)); if (!h2) { + LOGF(fatal, "n-sigma pile-up band: %s missing in %s", name, bandPath.c_str()); return; } - std::unique_ptr prof(h2->ProfileX("ptmp", 1, -1, "S")); - int nBins = prof->GetNbinsX(); - xMin = prof->GetXaxis()->GetXmin(); - xMax = prof->GetXaxis()->GetXmax(); - limits.assign(nBins + 2, {-99999.f, 999999.f}); + std::unique_ptr prof(h2->ProfileX(Form("%s_band", name), 1, -1, "S")); + const int nBins = prof->GetNbinsX(); + band.xMin = prof->GetXaxis()->GetXmin(); + band.xMax = prof->GetXaxis()->GetXmax(); + band.meanRms.assign(nBins + 2, {0.f, -1.f}); for (int i = 1; i <= nBins; ++i) { - float mean = prof->GetBinContent(i); - float rms = prof->GetBinError(i); - limits[i] = {mean - cfgPupnSig * rms, mean + cfgPupnSig * rms}; + // a sparse bin (high-N_PV tail, peripheral edge) gets no band: its RMS is not measured + if (prof->GetBinEntries(i) >= KMinBandEntries) { + band.meanRms[i] = {static_cast(prof->GetBinContent(i)), static_cast(prof->GetBinError(i))}; + } } + band.applied = true; + LOGF(info, "n-sigma pile-up band (%.1f sigma, PileupNSigma %.1f) from %s in %s", pupNSigma, + pupNSigma - KSystPupnSigStep, name, bandPath.c_str()); }; - loadLimits("Hist2D_globalTracks_cent", state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent); - } else { - LOGF(warning, "sigma-pileup limits source list missing at %s; sigma-pileup cut effectively disabled.", limPath.c_str()); - } - } - - // ---- MC mean profiles for MC fluc ---- - if (cfgRunMCFluc) { - LOGF(info, "Loading MC Mean profiles from CCDB path: %s", pathMCMean.c_str()); - auto* lstMCMean = ccdb->getForTimeStamp(pathMCMean, now); - if (lstMCMean) { - loadProfileFromList(lstMCMean, "pmeanTru_nch_etabin", state.pmeanTruNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanReco_nch_etabin", state.pmeanRecoNchEtabinStep2); - loadProfileFromList(lstMCMean, "pmeanRecoEffcorr_nch_etabin", state.pmeanRecoEffcorrNchEtabinStep2); - // MC is pinned to 0.2-wide bins; guard against a stale MC-mean object built - // at a different width (its y-axis would then not have nEta+1 bins). - if (state.pmeanTruNchEtabinStep2 && - state.pmeanTruNchEtabinStep2->GetYaxis()->GetNbins() != nEta + 1) { - LOGF(fatal, - "MC Mean eta binning mismatch: loaded profile has %d y-bins, this job " - "expects nEta+1=%d. Rebuild the MC-mean pass at the current binning.", - state.pmeanTruNchEtabinStep2->GetYaxis()->GetNbins(), nEta + 1); + if (wantCent) { + makeBand("Hist2D_globalTracks_cent", pileupBands[kBandCent]); + } + if (wantPV) { + makeBand("Hist2D_globalTracks_PVTracks", pileupBands[kBandPV]); } - } else { - LOGF(error, "Could not retrieve TList for MC Mean from: %s", pathMCMean.c_str()); } } - // ---- Data mean profiles for data fluc (one per systematic variation) ---- - if (cfgRunDataFluc) { - int st = std::clamp(cfgSystType, 0, kNSystType - 1); - std::string meanPath = pathDataMean + systSuffix[st]; - LOGF(info, "Loading Data Mean profiles for systematic '%s' from: %s", - (st == kSystBase ? "Base" : systSuffix[st].c_str()), meanPath.c_str()); - auto* lstDataMean = ccdb->getForTimeStamp(meanPath, now); - if (lstDataMean) { - loadProfileFromList(lstDataMean, "pmean_nch_etabin", state.pmeanNchEtabinStep2); - // The DataMean and DataFluc passes must share cfgEtaBinWidth, else the - // (ibx, ieta+1) read in processDataFluc maps a fluc bin index onto a - // different physical eta slice, silently. The mean profile y-axis has - // nEta+1 bins by construction (etaBinAxis). - if (state.pmeanNchEtabinStep2 && - state.pmeanNchEtabinStep2->GetYaxis()->GetNbins() != nEta + 1) { - LOGF(fatal, - "Data Mean eta binning mismatch: loaded profile has %d y-bins, this job " - "expects nEta+1=%d -> DataMean and DataFluc used different cfgEtaBinWidth.", - state.pmeanNchEtabinStep2->GetYaxis()->GetNbins(), nEta + 1); + // ---- mean profiles for the fluctuation passes (one per variation) ---- + if (cfgRunMCFluc || cfgRunDataFluc) { + const std::string& path = cfgRunMCFluc ? pathMCMean : pathDataMean; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(maskAll, v)) { + LOGF(info, "Loading mean profiles of %s from %s", systName[v].c_str(), (path + systSuffix(v)).c_str()); + loadMeans(v, path + systSuffix(v), cfgRunMCFluc, now); } - } else { - LOGF(error, "Could not retrieve TList for Data Mean from: %s", meanPath.c_str()); } } - LOGF(info, "CCDB initialization complete for RadialFlowDecorr."); - } - // =========================================================================== - // MC: build efficiency / fake maps (inclusive) - // =========================================================================== - void processGetEffHists(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) - { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; + // ---- declarations ---- + declareCommonQA(); + if (isMcRun) { + declareMCVtxQA(); } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; + declareCounts(cfgRunGetEff ? maskEff : ((cfgRunGetMCFlat || cfgRunGetDataFlat) ? maskFlat : maskAll)); + if (doprocessCutQA) { + declareCutQA(); } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + if (cfgRunGetEff) { + declareMCGetEffHists(); } - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - histos.fill(HIST("h3_AllPrimary"), multPV, particle.pt(), particle.eta()); + if (cfgRunGetMCFlat) { + declareMCGetFlatHists(); } - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - - float pt = track.pt(), eta = track.eta(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("h3_AllReco"), multPV, pt, eta); - - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); - histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); - histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); - histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); - histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); - histos.fill(HIST("h3_RecoMatchedToPrimary"), multPV, mcP.pt(), mcP.eta()); - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake"), multPV, pt, eta); - } + if (cfgRunMCMean) { + declareMCMeanHists(); } - } - PROCESS_SWITCH(RadialFlowDecorr, processGetEffHists, "process MC to calculate EffWeights", cfgRunGetEff); - - // =========================================================================== - // MC: build flattening maps (inclusive) - // =========================================================================== - void processMCFlat(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks) - { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; + if (cfgRunMCFluc) { + declareMCFlucHists(); } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; + if (cfgRunGetDataFlat) { + declareDataGetFlatHists(); } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + if (cfgRunDataMean) { + declareDataMeanHists(); + } + if (cfgRunDataFluc) { + declareDataFlucHists(); } - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; - } - float w = (1.0f - fake) / eff; - if (std::isfinite(w) && w > 0.f) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); + std::string list; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(used & maskAll, v) || (cfgRunGetEff && TESTBIT(maskEff, v)) || ((cfgRunGetMCFlat || cfgRunGetDataFlat) && TESTBIT(maskFlat, v))) { + list += " " + systName[v]; } } + LOGF(info, "RadialFlowDecorr variations in this job:%s (ITS layer cut nominal %d, variation %d; N_ch bins of %d, NchBin2 %d; occupancy cut %d)", + list.c_str(), itsLayerNom, itsLayerVar, nchWidth, nchWidthNchBin2, occupancyMax); + LOGF(info, "CCDB initialization complete for RadialFlowDecorr."); } - PROCESS_SWITCH(RadialFlowDecorr, processMCFlat, "process MC to calculate FlatWeights", cfgRunGetMCFlat); // =========================================================================== - // MC: mean pT (truth / reco / reco-eff-corrected) + // Helpers of the process functions // =========================================================================== - void processMCMean(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + // (vz, sign, pT, eta, phi) map of variation v: raw, with all weights w and, for Base, with + // the efficiency weight wEff = (1 - fake) / eff. + void fillFlat(int v, float vz, float sign, float pt, float eta, float phi, float w, float wEff) { - std::array sumWiTruth{}, sumWiptiTruth{}; - std::array sumWiReco{}, sumWiptiReco{}; - std::array sumWiRecoEffCorr{}, sumWiptiRecoEffCorr{}; - - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; - } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { + const auto& h = hists.flat[v].maps; + if (!h) { return; } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; + std::array x = {vz, sign, pt, eta, phi, static_cast(kFlatRaw)}; + h->Fill(x.data(), 1.); + x[5] = kFlatWtd; + h->Fill(x.data(), w); + if (v == kSystBase) { + x[5] = kFlatEffWtd; + h->Fill(x.data(), wEff); } + } - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - // --- truth --- - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - float pt = particle.pt(), eta = particle.eta(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; - } - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { - continue; - } - sumWiTruth[ieta]++; - sumWiptiTruth[ieta] += pt; + // The reco collision with the most PV contributors; with split vertices the others are + // skipped, so the truth of an MC collision enters once. Fills the vertex QA. + template + int64_t bestRecoCollision(const R& recoCollisions, float mcVz) + { + int64_t best = -1; + int maxContrib = -1; + int nReco = 0; + for (const auto& rc : recoCollisions) { + ++nReco; + if (rc.numContrib() > maxContrib) { + maxContrib = rc.numContrib(); + best = rc.globalIndex(); } } - - histos.fill(HIST("MCGen/Prof_Cent_Nchrec"), cent, sumWiTruth[0]); - histos.fill(HIST("MCGen/Prof_Mult_Nchrec"), multPV, sumWiTruth[0]); - if (sumWiTruth[0] > KFloatEpsilon) { - histos.fill(HIST("MCGen/Prof_Cent_MeanpT"), cent, sumWiptiTruth[0] / sumWiTruth[0]); - histos.fill(HIST("MCGen/Prof_Mult_MeanpT"), multPV, sumWiptiTruth[0] / sumWiTruth[0]); + for (const auto& rc : recoCollisions) { + const int type = (nReco == 1) ? kVtxSingle : (rc.globalIndex() == best ? kVtxSplitBest : kVtxSplitOther); + hists.mcVtx->Fill(rc.posZ() - mcVz, type); } + return best; + } - // --- reco --- - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { + // =========================================================================== + // MC: build efficiency / fake maps (inclusive) + // =========================================================================== + void processGetEffHists(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; } - if (!isTrackSelected(track)) { - continue; + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskEff); + if (ev.mask == 0) { + return; } - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + const float cent = ev.cent[kSystBase]; + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; - } - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - float w = flatW * (1.0 - fake) / eff; - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) { - continue; - } + // one entry in the efficiency map of category cat of every variation in mask + auto fillEffMaps = [&](uint32_t mask, float pt, float eta, int cat) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(mask, v)) { + hists.eff[v].maps[cat]->Fill(cent, pt, eta); + } + } + }; - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + // --- truth: charged primaries of the MC collision --- + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { continue; } - sumWiReco[ieta]++; - sumWiptiReco[ieta] += pt; - sumWiRecoEffCorr[ieta] += w; - sumWiptiRecoEffCorr[ieta] += w * pt; + fillEffMaps(ev.mask, particle.pt(), particle.eta(), kEffAllPrim); } - histos.fill(HIST("Eff_cent"), cent, eff); - histos.fill(HIST("Fake_cent"), cent, fake); - histos.fill(HIST("wgt_cent"), cent, w); - histos.fill(HIST("Eff_Ntrk"), multPV, eff); - histos.fill(HIST("Fake_Ntrk"), multPV, fake); - histos.fill(HIST("wgt_Ntrk"), multPV, w); - histos.fill(HIST("Eff_pT"), pt, eff); - histos.fill(HIST("Fake_pT"), pt, fake); - histos.fill(HIST("wgt_pT"), pt, w); - histos.fill(HIST("Eff_eta"), eta, eff); - histos.fill(HIST("Fake_eta"), eta, fake); - histos.fill(HIST("wgt_eta"), eta, w); - - histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } - - // subevent mean-pT maps - for (int ietaA = 0; ietaA < nEta; ++ietaA) { - for (int ietaC = 0; ietaC < nEta; ++ietaC) { - float nTruAB = sumWiTruth[ietaA] + sumWiTruth[ietaC]; - float nRecoAB = sumWiReco[ietaA] + sumWiReco[ietaC]; - float nCorrAB = sumWiRecoEffCorr[ietaA] + sumWiRecoEffCorr[ietaC]; - - if (sumWiTruth[ietaA] > KFloatEpsilon && sumWiTruth[ietaC] > KFloatEpsilon) { - histos.fill(HIST("Prof2D_MeanpTSub_Tru"), cent, ietaA, ietaC, (sumWiptiTruth[ietaA] + sumWiptiTruth[ietaC]) / nTruAB); + // --- reco --- + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; } - if (sumWiReco[ietaA] > KFloatEpsilon && sumWiReco[ietaC] > KFloatEpsilon) { - histos.fill(HIST("Prof2D_MeanpTSub_Reco"), cent, ietaA, ietaC, (sumWiptiReco[ietaA] + sumWiptiReco[ietaC]) / nRecoAB); + float pt = track.pt(), eta = track.eta(); + const bool isBase = TESTBIT(trackMask, kSystBase); + const int origin = trackOrigin(track, coll); + if (isBase) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + hists.mcTrackOrigin->Fill(nCh, pt, origin); } - if (sumWiRecoEffCorr[ietaA] > KFloatEpsilon && sumWiRecoEffCorr[ietaC] > KFloatEpsilon) { - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr"), cent, ietaA, ietaC, (sumWiptiRecoEffCorr[ietaA] + sumWiptiRecoEffCorr[ietaC]) / nCorrAB); + fillEffMaps(trackMask, pt, eta, kEffAllReco); + if (origin == kOriginPrimary) { + const auto& mcP = track.mcParticle(); + if (isBase) { + histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); + histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); + histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); + histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); + histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); + } + fillEffMaps(trackMask, mcP.pt(), mcP.eta(), kEffRecoMatchedPrim); + } else if (origin == kOriginSecondary) { + fillEffMaps(trackMask, pt, eta, kEffRecoSec); + } else if (origin == kOriginOtherColl) { + fillEffMaps(trackMask, pt, eta, kEffRecoOtherColl); + } else { + fillEffMaps(trackMask, pt, eta, kEffRecoFake); } } - - if (sumWiTruth[ietaA] > KFloatEpsilon) { - histos.fill(HIST("pmeanTru_nch_etabin"), multPV, ietaA, sumWiptiTruth[ietaA] / sumWiTruth[ietaA]); - histos.fill(HIST("pmeanMultTru_nch_etabin"), multPV, ietaA, sumWiTruth[ietaA]); - } - if (sumWiReco[ietaA] > KFloatEpsilon) { - histos.fill(HIST("pmeanReco_nch_etabin"), multPV, ietaA, sumWiptiReco[ietaA] / sumWiReco[ietaA]); - histos.fill(HIST("pmeanMultReco_nch_etabin"), multPV, ietaA, sumWiReco[ietaA]); - } - if (sumWiRecoEffCorr[ietaA] > KFloatEpsilon) { - histos.fill(HIST("pmeanRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiptiRecoEffCorr[ietaA] / sumWiRecoEffCorr[ietaA]); - histos.fill(HIST("pmeanMultRecoEffcorr_nch_etabin"), multPV, ietaA, sumWiRecoEffCorr[ietaA]); - } + flushTrackCounts(); } } - PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); + PROCESS_SWITCH(RadialFlowDecorr, processGetEffHists, "process MC to calculate EffWeights", cfgRunGetEff); // =========================================================================== - // MC: fluctuations (C2, subevent) at three levels + // MC: build flattening maps (inclusive) // =========================================================================== - void processMCFluc(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + void processMCFlat(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, FilteredTCs const& mcTracks) { - if (!state.pmeanTruNchEtabinStep2 || !state.pmeanRecoNchEtabinStep2 || !state.pmeanRecoEffcorrNchEtabinStep2) { - LOGF(warning, "MC fluc: mean pT map missing"); - return; - } - - std::array, KIntM>, KNEtaMax> sumPmwkTru{}; - std::array, KNEtaMax> sumWkTru{}; - std::array, KIntM>, KNEtaMax> sumPmwkReco{}; - std::array, KNEtaMax> sumWkReco{}; - std::array, KIntM>, KNEtaMax> sumPmwkRecoEffCor{}; - std::array, KNEtaMax> sumWkRecoEffCor{}; - - std::array meanTru{}, c2Tru{}, c3Tru{}; - std::array meanReco{}, c2Reco{}, c3Reco{}; - std::array meanRecoEffCor{}, c2RecoEffCor{}, c3RecoEffCor{}; - - std::array p1kBarTru{}, p1kBarReco{}, p1kBarRecoEffCor{}; - - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) { - return; - } - float cent = getCentrality(mcCollision); - if (cent > KCentMax) { - return; - } - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) { - return; - } - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - // --- truth sums --- - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) { - continue; - } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { - continue; - } - float pt = particle.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; - } - float eta = particle.eta(); - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { - continue; - } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkTru[ieta][m][k] += std::pow(pt, m); - } - sumWkTru[ieta][k]++; - } - } - } - - // --- reco sums --- - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; } - if (!isTrackSelected(track)) { - continue; - } - float pt = track.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) { - continue; + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { + return; } - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(multPV, pt, eta, cfgEff, eff, fake)) { - continue; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - float w = flatW * (1.0 - fake) / eff; - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) { - continue; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - for (int ieta = 0; ieta < nEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkReco[ieta][m][k] += std::pow(1.0, k) * std::pow(pt, m); - sumPmwkRecoEffCor[ieta][m][k] += std::pow(w, k) * std::pow(pt, m); + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } + + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); } - sumWkReco[ieta][k] += std::pow(1.0, k); - sumWkRecoEffCor[ieta][k] += std::pow(w, k); } } - - histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); + flushTrackCounts(); } + } + PROCESS_SWITCH(RadialFlowDecorr, processMCFlat, "process MC to calculate FlatWeights", cfgRunGetMCFlat); - for (int ieta = 0; ieta < nEta; ++ieta) { - const int ibx = state.pmeanTruNchEtabinStep2->GetXaxis()->FindBin(multPV); - const int iby = ieta + 1; - - float mmptTru = state.pmeanTruNchEtabinStep2->GetBinContent(ibx, iby); - float mmptReco = state.pmeanRecoNchEtabinStep2->GetBinContent(ibx, iby); - float mmptRecoEffCor = state.pmeanRecoEffcorrNchEtabinStep2->GetBinContent(ibx, iby); - - // truth - meanTru[ieta] = (sumWkTru[ieta][1] > KFloatEpsilon) ? (sumPmwkTru[ieta][1][1] / sumWkTru[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2Tru[ieta] = std::numeric_limits::quiet_NaN(); - c3Tru[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarTru[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptTru) && mmptTru != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkTru[ieta], sumWkTru[ieta], mmptTru); - c2Tru[ieta] = corr.c2; - c3Tru[ieta] = corr.c3; - if (std::isfinite(meanTru[ieta])) { - p1kBarTru[ieta] = meanTru[ieta] - mmptTru; - } - } - // reco - meanReco[ieta] = (sumWkReco[ieta][1] > KFloatEpsilon) ? (sumPmwkReco[ieta][1][1] / sumWkReco[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2Reco[ieta] = std::numeric_limits::quiet_NaN(); - c3Reco[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarReco[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptReco) && mmptReco != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkReco[ieta], sumWkReco[ieta], mmptReco); - c2Reco[ieta] = corr.c2; - c3Reco[ieta] = corr.c3; - if (std::isfinite(meanReco[ieta])) { - p1kBarReco[ieta] = meanReco[ieta] - mmptReco; - } - } - // reco, efficiency-corrected - meanRecoEffCor[ieta] = (sumWkRecoEffCor[ieta][1] > KFloatEpsilon) ? (sumPmwkRecoEffCor[ieta][1][1] / sumWkRecoEffCor[ieta][1]) : std::numeric_limits::quiet_NaN(); - c2RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - c3RecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - p1kBarRecoEffCor[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmptRecoEffCor) && mmptRecoEffCor != 0) { - const auto corr = calculateC2C3FromSums(sumPmwkRecoEffCor[ieta], sumWkRecoEffCor[ieta], mmptRecoEffCor); - c2RecoEffCor[ieta] = corr.c2; - c3RecoEffCor[ieta] = corr.c3; - if (std::isfinite(meanRecoEffCor[ieta])) { - p1kBarRecoEffCor[ieta] = meanRecoEffCor[ieta] - mmptRecoEffCor; - } - } - } - - // per-eta counts & means - for (int ieta = 0; ieta < nEta; ++ieta) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkTru[ieta][1]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkTru[ieta][1]); - histos.fill(HIST("MCReco/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkReco[ieta][1]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkReco[ieta][1]); - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_Nchrec"), cent, ieta, sumWkRecoEffCor[ieta][1]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_Nchrec"), multPV, ieta, sumWkRecoEffCor[ieta][1]); - - if (sumWkTru[ieta][1] > KFloatEpsilon) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_MeanpT"), cent, ieta, meanTru[ieta]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanTru[ieta]); - } - if (sumWkReco[ieta][1] > KFloatEpsilon) { - histos.fill(HIST("MCReco/Prof_Cent_NEta_MeanpT"), cent, ieta, meanReco[ieta]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanReco[ieta]); + // =========================================================================== + // MC: mean pT (truth / reco / reco-eff-corrected) + // =========================================================================== + void processMCMean(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { + continue; } - if (sumWkRecoEffCor[ieta][1] > KFloatEpsilon) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_MeanpT"), cent, ieta, meanRecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_MeanpT"), multPV, ieta, meanRecoEffCor[ieta]); + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; } - } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - // meanpT & C2 vs eta bin - for (int ieta = 0; ieta < nEta; ++ieta) { - if (std::isfinite(meanTru[ieta])) { - histos.fill(HIST("MCGen/Prof_MeanpT_Cent_etabin"), cent, ieta, meanTru[ieta]); - histos.fill(HIST("MCGen/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanTru[ieta]); - } - if (std::isfinite(c2Tru[ieta])) { - histos.fill(HIST("MCGen/Prof_C2_Cent_etabin"), cent, ieta, c2Tru[ieta]); - histos.fill(HIST("MCGen/Prof_C2_Mult_etabin"), multPV, ieta, c2Tru[ieta]); + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (std::isfinite(c3Tru[ieta])) { - histos.fill(HIST("MCGen/Prof_C3_Cent_etabin"), cent, ieta, c3Tru[ieta]); - histos.fill(HIST("MCGen/Prof_C3_Mult_etabin"), multPV, ieta, c3Tru[ieta]); - } - if (std::isfinite(meanReco[ieta])) { - histos.fill(HIST("MCReco/Prof_MeanpT_Cent_etabin"), cent, ieta, meanReco[ieta]); - histos.fill(HIST("MCReco/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanReco[ieta]); - } - if (std::isfinite(c2Reco[ieta])) { - histos.fill(HIST("MCReco/Prof_C2_Cent_etabin"), cent, ieta, c2Reco[ieta]); - histos.fill(HIST("MCReco/Prof_C2_Mult_etabin"), multPV, ieta, c2Reco[ieta]); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - if (std::isfinite(c3Reco[ieta])) { - histos.fill(HIST("MCReco/Prof_C3_Cent_etabin"), cent, ieta, c3Reco[ieta]); - histos.fill(HIST("MCReco/Prof_C3_Mult_etabin"), multPV, ieta, c3Reco[ieta]); + + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); + } + } } - if (std::isfinite(meanRecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Cent_etabin"), cent, ieta, meanRecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Mult_etabin"), multPV, ieta, meanRecoEffCor[ieta]); + + // --- truth: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); } - if (std::isfinite(c2RecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Cent_etabin"), cent, ieta, c2RecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Mult_etabin"), multPV, ieta, c2RecoEffCor[ieta]); + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { + continue; + } + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + sumsTru.add(ieta, pt, 1.); + } } - if (std::isfinite(c3RecoEffCor[ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3_Cent_etabin"), cent, ieta, c3RecoEffCor[ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_C3_Mult_etabin"), multPV, ieta, c3RecoEffCor[ieta]); + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; + } } - } - // mirror-pair subevent (C2Sub, C3Sub) vs eta bin - for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { - int ietaC = nEta - ietaA; + // --- reco --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; + } + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - float c2SubTru = p1kBarTru[ietaA] * p1kBarTru[ietaC]; - float c2SubReco = p1kBarReco[ietaA] * p1kBarReco[ietaC]; - float c2SubRecoEffCor = p1kBarRecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); + } + } + } - float c3SubTruA = c2Tru[ietaA] * p1kBarTru[ietaC]; // 2 from A, 1 from C - float c3SubTruC = c2Tru[ietaC] * p1kBarTru[ietaA]; // 2 from C, 1 from A - float c3SubRecoA = c2Reco[ietaA] * p1kBarReco[ietaC]; - float c3SubRecoC = c2Reco[ietaC] * p1kBarReco[ietaA]; - float c3SubRecoEffCorA = c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; - float c3SubRecoEffCorC = c2RecoEffCor[ietaC] * p1kBarRecoEffCor[ietaA]; + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; + } + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pT_type"), pt, 0, eff); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 1, fake); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 2, w[kSystBase]); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); + } - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubTru); - histos.fill(HIST("MCGen/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubReco); - histos.fill(HIST("MCReco/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Cent_etabin"), cent, ietaA, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Mult_etabin"), multPV, ietaA, c2SubRecoEffCor); + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); + } + } + } } - if (std::isfinite(c3SubTruA)) { - histos.fill(HIST("MCGen/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubTruA); - histos.fill(HIST("MCGen/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubTruA); + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); } - if (std::isfinite(c3SubTruC)) { - histos.fill(HIST("MCGen/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubTruC); - histos.fill(HIST("MCGen/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubTruC); + + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + for (int l = 0; l < kNLevels; ++l) { + const auto& s = sums[v][l]; + const auto& h = hists.mean[v][l]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { + continue; + } + const double mpt = s.pmwk[ieta][1][1] / n; + if (std::isfinite(mpt)) { + h.mean->Fill(nCh, ieta, kMeanPt, mpt); + } + } + const FwdSignal& fwd = (l == kGen) ? fwdTru : fwdReco; + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } + } + } + } } - if (std::isfinite(c3SubRecoA)) { - histos.fill(HIST("MCReco/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubRecoA); - histos.fill(HIST("MCReco/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubRecoA); + } + } + PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); + + // =========================================================================== + // MC: fluctuations (C2, subevent, forward covariances) at three levels + // =========================================================================== + void processMCFluc(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { + continue; } - if (std::isfinite(c3SubRecoC)) { - histos.fill(HIST("MCReco/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubRecoC); - histos.fill(HIST("MCReco/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubRecoC); + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; } - if (std::isfinite(c3SubRecoEffCorA)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubRecoEffCorA); - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Mult_etabin"), multPV, ietaA, c3SubRecoEffCorA); + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (std::isfinite(c3SubRecoEffCorC)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubRecoEffCorC); - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub_Mult_etabin"), multPV, ietaC, c3SubRecoEffCorC); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - } - // full 2D subevent map - for (int ietaA = 1; ietaA < nEta; ++ietaA) { - for (int ietaC = 1; ietaC < nEta; ++ietaC) { - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - const float w = etaUp[ietaA] - etaLw[ietaA]; - const float gap = (ietaA - ietaC) * w; - const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * w; // = cA + cC on the lattice + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); + } + } + } - float c2SubTru = (ietaA == ietaC) ? static_cast(c2Tru[ietaA]) : p1kBarTru[ietaA] * p1kBarTru[ietaC]; - float c2SubReco = (ietaA == ietaC) ? static_cast(c2Reco[ietaA]) : p1kBarReco[ietaA] * p1kBarReco[ietaC]; - float c2SubRecoEffCor = (ietaA == ietaC) ? static_cast(c2RecoEffCor[ietaA]) : p1kBarRecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; + // --- truth sums: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); + } + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { + continue; + } + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + sumsTru.add(ieta, pt, 1.); + } + } + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; + } + } - float c3Sub2DTru = (ietaA == ietaC) ? static_cast(c3Tru[ietaA]) : c2Tru[ietaA] * p1kBarTru[ietaC]; // diag: within-bin c3; off-diag: 2 from A, 1 from C - float c3Sub2DReco = (ietaA == ietaC) ? static_cast(c3Reco[ietaA]) : c2Reco[ietaA] * p1kBarReco[ietaC]; - float c3Sub2DRecoEffCor = (ietaA == ietaC) ? static_cast(c3RecoEffCor[ietaA]) : c2RecoEffCor[ietaA] * p1kBarRecoEffCor[ietaC]; + // --- reco sums --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; + } + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D"), cent, gap, sum, c2SubTru); + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); + } + } } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D"), cent, gap, sum, c2SubReco); + + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D"), cent, gap, sum, c2SubRecoEffCor); + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - if (std::isfinite(c3Sub2DTru)) { - histos.fill(HIST("MCGen/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DTru); - histos.fill(HIST("MCGen/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DTru); + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); + } + } } - if (std::isfinite(c3Sub2DReco)) { - histos.fill(HIST("MCReco/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DReco); - histos.fill(HIST("MCReco/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DReco); + } + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); + } + + // --- c2, c3, subevent correlators and forward covariances of every variation and level --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - if (std::isfinite(c3Sub2DRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2DRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_C3GapSum2D"), cent, gap, sum, c3Sub2DRecoEffCor); + for (int l = 0; l < kNLevels; ++l) { + const FwdSignal* fwd = cfgFwdOpt.cfgFwd ? (l == kGen ? &fwdTru : &fwdReco) : nullptr; + computeEventObs(sums[v][l], v, l, fwd, nCh, evObs); + fillFluc(hists.fluc[v][l], evObs, 1., nCh); } } } @@ -1941,32 +2933,34 @@ struct RadialFlowDecorr { // =========================================================================== // DATA: build flattening maps (inclusive) // =========================================================================== - void processGetDataFlat(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) + void processGetDataFlat(AodCollisionsSel::iterator const& coll, AodTracksSel const& tracks) { histos.fill(HIST("hVtxZ"), coll.posZ()); - if (!isEventSelected(coll)) { + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { return; } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - int ntrk = 0; - float vz = coll.posZ(); - + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float pt = track.pt(); @@ -1974,146 +2968,135 @@ struct RadialFlowDecorr { continue; } float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - if (eta > etaLw[0] && eta < etaUp[0]) { - ntrk++; - } - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { - continue; - } - float w = (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { - continue; - } - - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - } - - if (cfgZDC) { - const auto& foundBC = coll.foundBC_as(); - if (!foundBC.has_zdc()) { - return; + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } + + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); + } } - auto zdc = foundBC.zdc(); - auto zdcAmp = zdc.energyCommonZNA() + zdc.energyCommonZNC(); - histos.fill(HIST("hnTrkPVZDC"), coll.multNTracksPV(), zdcAmp); - histos.fill(HIST("hNchZDC"), ntrk, zdcAmp); } + flushTrackCounts(); } PROCESS_SWITCH(RadialFlowDecorr, processGetDataFlat, "process data to calculate Flattening maps", cfgRunGetDataFlat); // =========================================================================== // DATA: mean pT // =========================================================================== - void processDataMean(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) + void processDataMean(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - std::array sumWi{}, sumWipti{}; - - if (!isEventSelected(coll)) { + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - float vz = coll.posZ(); - + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); + } + } + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float p = track.p(); float pt = track.pt(); - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); if (p < KFloatEpsilon) { continue; } if (pt <= cfgPtMin || pt > cfgPtMax) { continue; } - - histos.fill(HIST("hP"), p); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { - continue; + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - histos.fill(HIST("pEffWeight_pt_eta_cent"), pt, eta, cent, eff); - histos.fill(HIST("pFakeWeight_pt_eta_cent"), pt, eta, cent, fake); - histos.fill(HIST("pFlatWeight_pt_eta_cent"), pt, eta, cent, flatWeight); - - if (eff <= KFloatEpsilon) { + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { continue; } - - float w = flatWeight * (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { - continue; + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 0, eff); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 1, fake); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 2, flatWeight); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - for (int ieta = 0; ieta < nEta; ++ieta) { if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; } - sumWi[ieta] += w; - sumWipti[ieta] += w * pt; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); + } + } } } + flushTrackCounts(); - if (sumWi[0] > KFloatEpsilon) { - histos.fill(HIST("Prof_Cent_Nchrec"), cent, sumWi[0]); - histos.fill(HIST("Prof_Mult_Nchrec"), coll.multNTracksPV(), sumWi[0]); - histos.fill(HIST("Prof_Cent_MeanpT"), cent, sumWipti[0] / sumWi[0]); - histos.fill(HIST("Prof_Mult_MeanpT"), coll.multNTracksPV(), sumWipti[0] / sumWi[0]); + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - for (int ietaA = 0; ietaA < nEta; ++ietaA) { - for (int ietaC = 0; ietaC < nEta; ++ietaC) { - if ((sumWi[ietaA] < KFloatEpsilon) || (sumWi[ietaC] < KFloatEpsilon)) { + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + const auto& s = sums[v][0]; + const auto& h = hists.mean[v][0]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { continue; } - double wCorrAB = sumWi[ietaA] + sumWi[ietaC]; - if (wCorrAB > 0) { - float mptsub = (sumWipti[ietaA] + sumWipti[ietaC]) / wCorrAB; - histos.fill(HIST("Prof2D_MeanpTSub"), cent, ietaA, ietaC, mptsub); + const double mpt = s.pmwk[ieta][1][1] / n; + if (std::isfinite(mpt)) { + h.mean->Fill(nCh, ieta, kMeanPt, mpt); } } - if (sumWi[ietaA] > KFloatEpsilon) { - double mpt = sumWipti[ietaA] / sumWi[ietaA]; - if (std::isfinite(mpt)) { - histos.fill(HIST("pmean_nch_etabin"), coll.multNTracksPV(), ietaA, mpt); - histos.fill(HIST("pmeanMult_nch_etabin"), coll.multNTracksPV(), ietaA, sumWi[ietaA]); - histos.fill(HIST("pmean_cent_etabin"), cent, ietaA, mpt); - histos.fill(HIST("pmeanMult_cent_etabin"), cent, ietaA, sumWi[ietaA]); + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } } } } @@ -2121,230 +3104,145 @@ struct RadialFlowDecorr { PROCESS_SWITCH(RadialFlowDecorr, processDataMean, "process data to calculate mean pT", cfgRunDataMean); // =========================================================================== - // DATA: fluctuations (C2, subevent) + Poisson bootstrap for the base run + // DATA: fluctuations (C2, subevent, forward covariances) + Poisson bootstrap for Base // =========================================================================== - void processDataFluc(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) + void processDataFluc(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - if (!isEventSelected(coll)) { - return; - } - float cent = getCentrality(coll); - if (cent > KCentMax) { - return; - } - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) { + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - if (!state.pmeanNchEtabinStep2) { - LOGF(warning, "Data fluc: mean pT map missing"); - return; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); } - if (cfgEff && (!state.hEff || !state.hFake)) { - LOGF(warning, "Data fluc: Efficiency maps requested but not present."); - return; - } - - if (cfgFlat && !state.hFlatWeight) { - LOGF(warning, "Data fluc: Flattening map requested but not present."); - return; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } } - std::array, KIntM>, KNEtaMax> sumpmwk{}; - std::array, KNEtaMax> sumwk{}; - std::array mean{}, c2{}, c3{}, p1kBar{}; - std::array meanMult{}; - - // --- Poisson bootstrap: one weight per sample per event --- - std::array poisW{}; - if (doBoot) { - for (int s = 0; s < nBoot; ++s) { - poisW[s] = rng.Poisson(1.0); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); } } - auto fillBS2D = [&](std::array, KMaxBoot>& arr, double x, double y, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, y, val, poisW[s]); - } - }; - auto fillBS3D = [&](std::array, KMaxBoot>& arr, double x, double y, double z, double val) { - if (!doBoot) { - return; - } - for (int s = 0; s < nBoot; ++s) { - arr[s]->Fill(x, y, z, val, poisW[s]); - } - }; - - float vz = coll.posZ(); - + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; } float p = track.p(); float pt = track.pt(); - float eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); if (p < KFloatEpsilon) { continue; } if (pt <= cfgPtMin || pt > cfgPtMax) { continue; } - - float eff = 1.f, fake = 0.f; - if (!getValidEffFake(coll.multNTracksPV(), pt, eta, cfgEff, eff, fake)) { - continue; + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, cfgFlat); - - float w = flatWeight * (1.0f - fake) / eff; - if (!std::isfinite(w) || w <= 0.f) { + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { continue; } + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); + } for (int ieta = 0; ieta < nEta; ++ieta) { if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; } - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumpmwk[ieta][m][k] += std::pow(w, k) * std::pow(pt, m); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); } - sumwk[ieta][k] += std::pow(w, k); } } } + flushTrackCounts(); - for (int ieta = 0; ieta < nEta; ++ieta) { - const int ibx = state.pmeanNchEtabinStep2->GetXaxis()->FindBin(coll.multNTracksPV()); - const int iby = ieta + 1; - - float mmpt = state.pmeanNchEtabinStep2->GetBinContent(ibx, iby); - - meanMult[ieta] = sumwk[ieta][1]; - mean[ieta] = (sumwk[ieta][1] > KFloatEpsilon) ? (sumpmwk[ieta][1][1] / sumwk[ieta][1]) : std::numeric_limits::quiet_NaN(); - - // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference - c2[ieta] = std::numeric_limits::quiet_NaN(); - c3[ieta] = std::numeric_limits::quiet_NaN(); - p1kBar[ieta] = std::numeric_limits::quiet_NaN(); - if (std::isfinite(mmpt) && mmpt != 0) { - const auto corr = calculateC2C3FromSums(sumpmwk[ieta], sumwk[ieta], mmpt); - c2[ieta] = corr.c2; - c3[ieta] = corr.c3; - if (std::isfinite(mean[ieta])) { - p1kBar[ieta] = mean[ieta] - mmpt; - } - } + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - // meanpT & C2 vs eta bin - for (int ieta = 0; ieta < nEta; ++ieta) { - if (std::isfinite(meanMult[ieta])) { - fillBS2D(bs.multCent, cent, ieta, meanMult[ieta]); - fillBS2D(bs.multMult, coll.multNTracksPV(), ieta, meanMult[ieta]); + // --- Poisson bootstrap: one weight per sample per event --- + std::array poisW{}; + if (doBoot) { + for (int s = 0; s < nBoot; ++s) { + poisW[s] = rng.Poisson(1.0); } + } - if (std::isfinite(mean[ieta])) { - histos.fill(HIST("Prof_MeanpT_Cent_etabin"), cent, ieta, mean[ieta]); - histos.fill(HIST("Prof_MeanpT_Mult_etabin"), coll.multNTracksPV(), ieta, mean[ieta]); - fillBS2D(bs.meanpTCent, cent, ieta, mean[ieta]); - fillBS2D(bs.meanpTMult, coll.multNTracksPV(), ieta, mean[ieta]); - } - if (std::isfinite(c2[ieta])) { - histos.fill(HIST("Prof_C2_Cent_etabin"), cent, ieta, c2[ieta]); - histos.fill(HIST("Prof_C2_Mult_etabin"), coll.multNTracksPV(), ieta, c2[ieta]); - fillBS2D(bs.c2Cent, cent, ieta, c2[ieta]); - fillBS2D(bs.c2Mult, coll.multNTracksPV(), ieta, c2[ieta]); + // --- c2, c3, subevent correlators and forward covariances of every variation --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - if (std::isfinite(c3[ieta])) { - histos.fill(HIST("Prof_C3_Cent_etabin"), cent, ieta, c3[ieta]); - histos.fill(HIST("Prof_C3_Mult_etabin"), coll.multNTracksPV(), ieta, c3[ieta]); - fillBS2D(bs.c3Cent, cent, ieta, c3[ieta]); - fillBS2D(bs.c3Mult, coll.multNTracksPV(), ieta, c3[ieta]); + computeEventObs(sums[v][0], v, 0, cfgFwdOpt.cfgFwd ? &fwd : nullptr, nCh, evObs); + fillFluc(hists.fluc[v][0], evObs, 1., nCh); + // bootstrap replicas of Base, in the 5% centrality classes + if (v == kSystBase && doBoot) { + for (int s = 0; s < nBoot; ++s) { + if (poisW[s] > 0.) { + fillFluc(hists.boot[s], evObs, poisW[s], ev.cent[kSystBase]); + } + } } } + } + PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); - // mirror-pair subevent (C2Sub, C3Sub) vs eta bin - for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { - int ietaC = nEta - ietaA; - float c2Sub = p1kBar[ietaA] * p1kBar[ietaC]; - // Mirror pairs (A, C = -A): both orderings are filled, each at the bin of its pair, - // so bins A < 0 hold AAB and bins C > 0 hold ABB of the same mirror pair. For a - // symmetric collision system the two agree in expectation: eta -> -eta maps - // AAB(a, -a) onto AAB(-a, a) = ABB(a, -a). Comparing bin A with bin nEta - A is - // therefore a forward/backward check, not two independent observables. - float c3SubA = c2[ietaA] * p1kBar[ietaC]; // AAB: 2 particles from A, 1 from C - float c3SubC = c2[ietaC] * p1kBar[ietaA]; // ABB: 2 particles from C, 1 from A - - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub_Cent_etabin"), cent, ietaA, c2Sub); - histos.fill(HIST("Prof_C2Sub_Mult_etabin"), coll.multNTracksPV(), ietaA, c2Sub); - fillBS2D(bs.c2SubCent, cent, ietaA, c2Sub); - fillBS2D(bs.c2SubMult, coll.multNTracksPV(), ietaA, c2Sub); - } - if (std::isfinite(c3SubA)) { - histos.fill(HIST("Prof_C3Sub_Cent_etabin"), cent, ietaA, c3SubA); - histos.fill(HIST("Prof_C3Sub_Mult_etabin"), coll.multNTracksPV(), ietaA, c3SubA); - fillBS2D(bs.c3SubCent, cent, ietaA, c3SubA); - fillBS2D(bs.c3SubMult, coll.multNTracksPV(), ietaA, c3SubA); - } - if (std::isfinite(c3SubC)) { - histos.fill(HIST("Prof_C3Sub_Cent_etabin"), cent, ietaC, c3SubC); - histos.fill(HIST("Prof_C3Sub_Mult_etabin"), coll.multNTracksPV(), ietaC, c3SubC); - fillBS2D(bs.c3SubCent, cent, ietaC, c3SubC); - fillBS2D(bs.c3SubMult, coll.multNTracksPV(), ietaC, c3SubC); + // =========================================================================== + // Cut QA + // =========================================================================== + // On the unfiltered collisions and tracks (the analysis passes see only the filtered ones): + // every quantity the Base selection cuts on, before and after it. The counters are not + // filled here. N_ch is the number of tracks of the analysis track filter. + void processCutQA(GeneralCollisions::iterator const& col, UnfilteredTracks const& tracks) + { + int nCh = 0; + for (const auto& trk : tracks) { + if (trk.isGlobalTrack() && trk.pt() > KPtMin && trk.pt() < KPtMax) { + ++nCh; } } - - // full 2D subevent map - for (int ietaA = 1; ietaA < nEta; ++ietaA) { - for (int ietaC = 1; ietaC < nEta; ++ietaC) { - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - const float w = etaUp[ietaA] - etaLw[ietaA]; - const float gap = (ietaA - ietaC) * w; - const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * w; // = cA + cC on the lattice - - float c2Sub = (ietaA == ietaC) ? static_cast(c2[ietaA]) : p1kBar[ietaA] * p1kBar[ietaC]; - // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, - // two particles from A and one from C ("AAB"). The same pair of bins read the - // other way round, cell (C,A) = c2[C] p1kBar[A], is "ABB" (one from A, two from C). - // Both are filled, so the map carries AAB and ABB of every bin pair: the row at - // eta_A = eta_ref is AAB with the pair at eta_ref, the column is ABB. Unlike C2, - // this map is NOT symmetric under A <-> C, and the two triangles must not be - // averaged or treated as duplicates. Diagonal: the within-bin c3 ("AAA"). - float c3Sub2D = (ietaA == ietaC) ? static_cast(c3[ietaA]) : c2[ietaA] * p1kBar[ietaC]; - - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D"), cent, gap, sum, c2Sub); - fillBS3D(bs.c2Sub2D, cent, etaValA, etaValB, c2Sub); - fillBS3D(bs.gapSum2D, cent, gap, sum, c2Sub); - } - if (std::isfinite(c3Sub2D)) { - histos.fill(HIST("Prof_C3Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c3Sub2D); - histos.fill(HIST("Prof_C3GapSum2D"), cent, gap, sum, c3Sub2D); - fillBS3D(bs.c3Sub2D, cent, etaValA, etaValB, c3Sub2D); - fillBS3D(bs.c3GapSum2D, cent, gap, sum, c3Sub2D); - } + const float cent = getCentrality(col, cfgEvSel.cfgCentralityChoice); + fillCutQAEvent(kQABefore, col, nCh, cent); + const auto ev = isEventSelected(col, nCh, 1 << kSystBase, false); + if (!TESTBIT(ev.mask, kSystBase)) { + return; + } + fillCutQAEvent(kQAAfter, col, nCh, cent); + for (const auto& trk : tracks) { + fillCutQATrack(kQABefore, trk); + if (TESTBIT(isTrackSelected(trk, 1 << kSystBase, false), kSystBase)) { + fillCutQATrack(kQAAfter, trk); } } } - PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); + PROCESS_SWITCH(RadialFlowDecorr, processCutQA, "QA of the event and track cut quantities before and after the Base selection", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 2825a93165295274aacafc9cb36aaa38abfcc549 Mon Sep 17 00:00:00 2001 From: jaelpark Date: Sun, 25 Oct 2026 11:54:10 +0100 Subject: [PATCH 429/648] [PWGCF] correlations.cxx: prompt + non-prompt separation (#18432) --- PWGCF/Tasks/correlations.cxx | 85 +++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 11 deletions(-) diff --git a/PWGCF/Tasks/correlations.cxx b/PWGCF/Tasks/correlations.cxx index 5a5f41d3de2..0a5c1d4ed6b 100644 --- a/PWGCF/Tasks/correlations.cxx +++ b/PWGCF/Tasks/correlations.cxx @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -105,7 +106,7 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)"); O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON for local efficiency"); - O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used"); O2_DEFINE_CONFIGURABLE(cfgCentBinsForMC, int, 0, "0 = OFF and 1 = ON for data like multiplicity/centrality bins for MC steps"); 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") 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 { O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path to efficiency object for trigger particles") O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path to efficiency object for associated particles") O2_DEFINE_CONFIGURABLE(cfgUseRun3MagField, bool, false, "false: Run 2 GRPObject; true: Run 3 GRPMagField") + O2_DEFINE_CONFIGURABLE(cfgMultWeight, std::string, "", "Multiplicity/Centrality weight") O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") O2_DEFINE_CONFIGURABLE(cfgRejectMixedPhiProngEvents, bool, true, "Reject associated hadrons from either mixed-phi prong event") @@ -132,6 +134,8 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgPtDepMLbkg, std::vector, {}, "pT interval for ML training") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLbkgSel, std::vector, {}, "Bkg ML selection") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLpromptSel, std::vector, {}, "Prompt ML selection") + O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLnonpromptSel, std::vector, {}, "Non-prompt ML selection (maximum cut on the non-prompt score)") + 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") std::unordered_map cfTrackToCFCollision; @@ -183,6 +187,7 @@ struct CorrelationTask { bool mPairCuts = false; THn* mEfficiencyTrigger = nullptr; THn* mEfficiencyAssociated = nullptr; + TH1* mMultWeight = nullptr; bool efficiencyLoaded = false; } cfg; @@ -220,6 +225,14 @@ struct CorrelationTask { if (!cfgPtCentDepMLpromptSel->empty() && cfgPtCentDepMLpromptSel->size() != cfgPtDepMLbkg->size()) { LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLpromptSel must be same size."); } + if (!cfgPtCentDepMLnonpromptSel->empty() && cfgPtCentDepMLnonpromptSel->size() != cfgPtDepMLbkg->size()) + LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLnonpromptSel must be same size."); + if (cfgPtCentDepMLpromptSel->size() > 0) + LOGF(info, "Prompt selection scores in use."); + else + LOGF(info, "Using inclusive selections."); + if (cfgPtCentDepMLnonpromptSel->size() > 0) + LOGF(info, "Non-prompt selection scores in use."); } 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"}}}); 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 { const AxisSpec& a = AxisSpec(axisInvMass); AxisSpec axisSpecMass = {1000, a.binEdges[0], a.binEdges[a.getNbins()]}; registry.add("invMass", "2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); + registry.add("invMassCorrected", "2-prong invariant mass with efficiency correction (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) { registry.add("invMassTwoPart", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisMultiplicity}}); registry.add("invMassTwoPartDPhi", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisDeltaPhi}}); @@ -395,12 +409,16 @@ struct CorrelationTask { return (track.decay() != aod::cf2prongtrack::D0ToPiK || (track.mlProbD0()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)))) && // prompt + track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))) && // not non-prompt ((track.decay() != aod::cf2prongtrack::D0barToKPi && track.decay() != aod::cf2prongtrack::D0barToKPiExclusive) || (track.mlProbD0bar()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)))); // prompt + track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0bar()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))); // not non-prompt } template @@ -428,6 +446,8 @@ struct CorrelationTask { using HasInvMass = decltype(std::declval().invMass()); template using HasPDGCode = decltype(std::declval().pdgCode()); + template + using HasTrackType = decltype(std::declval().trackType()); template void fillQA(const TCollision& collision, float multiplicity, float posZ, const TTracks1& tracks1, const TTracks2& tracks2) @@ -442,7 +462,15 @@ struct CorrelationTask { continue; } } + float triggerWeight = 1.0f; + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, collision.posZ()); + } registry.fill(HIST("invMass"), track1.invMass(), track1.pt(), multiplicity, posZ); + registry.fill(HIST("invMassCorrected"), track1.invMass(), track1.pt(), multiplicity, posZ, triggerWeight); for (const auto& track2 : tracks2) { if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { if (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) { @@ -628,6 +656,9 @@ struct CorrelationTask { } } + static std::set> duplicates; // workaround for old derived datasets involving 2prongs when cfgDecayParticleMask = 0 + duplicates.clear(); + for (const auto& track1 : tracks1) { // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); @@ -637,6 +668,15 @@ struct CorrelationTask { } } + if constexpr (std::experimental::is_detected::value) { + if (cfgDecayParticleMask == 0) { + auto a = std::make_tuple(track1.cfTrackProng0Id(), track1.cfTrackProng1Id()); // workaround allows for use of D0bar in old derived datasets + if (duplicates.contains(a)) + continue; + duplicates.insert(a); + } + } + // sign check and PDG code special cases if constexpr (std::experimental::is_detected::value) { // 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 { float triggerWeight = eventWeight; if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } if (cfg.mEfficiencyTrigger) { triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ); } @@ -777,6 +820,7 @@ struct CorrelationTask { // If decay attributes are found for the second track/particle, we assume 2p-2p correlation if constexpr (std::experimental::is_detected::value) { if ((((track2.mcDecay()) != aod::cf2prongtrack::D0ToPiK) && ((track2.mcDecay()) != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track2.decay() & aod::cf2prongmcpart::Prompt) == 0)) { + continue; } } else if constexpr (std::experimental::is_detected::value) { @@ -929,6 +973,11 @@ struct CorrelationTask { } LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); } + if (cfgMultWeight.value.empty() == false) { + TFile* pf = TFile::Open(cfgMultWeight.value.c_str(), "READ"); + cfg.mMultWeight = reinterpret_cast(pf->Get("hWeight")); + LOGF(info, "Loaded multiplicity weight histogram for associated particles from %s", cfgMultWeight.value.c_str()); + } cfg.efficiencyLoaded = true; } @@ -998,6 +1047,7 @@ struct CorrelationTask { same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); } + if (hasEfficiency) { same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); @@ -1363,6 +1413,19 @@ struct CorrelationTask { } // Primaries p2indexCache.clear(); + std::set signalMC; // mc global index cache + std::set reflectionMC; // mc global index cache + // Resolve the MC class to use for the efficiency denominator/numerator. + // cfgMcClassSel: -1=auto (promptSel empty -> all, else prompt), 0=all, 1=prompt, 2=non-prompt. + int mcClass = cfgMcClassSel.value; + if (mcClass < 0) { + mcClass = cfgPtCentDepMLpromptSel->empty() ? 0 : 1; + } + auto mcClassSel = [&](const auto& mp) -> bool { + bool isP = (mp.decay() & aod::cf2prongmcpart::Prompt) != 0; + bool isNP = (mp.decay() & aod::cf2prongmcpart::NonPrompt) != 0; + return (mcClass == 0) || (mcClass == 1 && isP) || (mcClass == 2 && isNP); + }; for (const auto& mcParticle : mcParticles) { if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { if ((mcParticle.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (mcParticle.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) { @@ -1372,9 +1435,8 @@ struct CorrelationTask { continue; // daughters not found } if constexpr (!reflectionSpec) { - if (cfgPtCentDepMLpromptSel->empty() || (mcParticle.decay() & aod::cf2prongmcpart::Prompt) != 0) { + if (mcClassSel(mcParticle)) same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); - } } p2indexCache.push_back(mcParticle.globalIndex()); } @@ -1409,17 +1471,18 @@ struct CorrelationTask { return false; } const auto& mcParticle = mcParticles.iteratorAt(*m - mcParticles.begin().globalIndex()); + // check MC class and return true if not the target class (wont be a fake) + if (!mcClassSel(mcParticle)) + return true; // a valid candidate but not the target class. Return true so we won't register a fake. if constexpr (!reflectionSpec) { - if (!cfgPtCentDepMLpromptSel->empty() && (mcParticle.decay() & aod::cf2prongmcpart::Prompt) == 0) { - return true; // a valid candidate but not a prompt - } same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); } else { - if ((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)) { + 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))) { registry.fill(HIST("invMassSignal"), p2track.invMass(), p2track.pt(), multiplicity); - } else { // one particle may be filled into both histograms through duplicates + signalMC.insert(mcParticle.globalIndex()); + } 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)))) { registry.fill(HIST("invMassReflected"), p2track.invMass(), p2track.pt(), multiplicity); + reflectionMC.insert(mcParticle.globalIndex()); } } return true; From fcb7d2d2c504441d573edebd4fa6e6457c1cd88b Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:41:51 +0100 Subject: [PATCH 430/648] [PWGCF] Fix process guard in femto producer (#18456) --- PWGCF/Femto/TableProducer/femtoProducer.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 460190c1e54..5b0f7beba3d 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -228,6 +228,7 @@ struct FemtoProducer { if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && + !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc)) { @@ -236,13 +237,15 @@ struct FemtoProducer { if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && - !doprocessTracksV0sCascadesRun3PbPbWithEventShape && + !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPb && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } - if (photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && !doprocessTracksV0sCascadesKinksPhotonsRun3pp)) { + if (photonBuilder.fillAnyTable() && + (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && + !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape)) { LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; } if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && @@ -345,6 +348,7 @@ struct FemtoProducer { static_cast(doprocessTracksV0sCascadesRun3pp) + static_cast(doprocessTracksV0sCascadesRun3PbPb) + static_cast(doprocessTracksV0sCascadesRun3PbPbWithEventShape) + + static_cast(doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape) + static_cast(doprocessTracksKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + From b7daf06b71b9fd1909921671eabdef3d13b8ebf6 Mon Sep 17 00:00:00 2001 From: m-rasa <99010072+m-rasa@users.noreply.github.com> Date: Sun, 25 Oct 2026 17:36:05 +0100 Subject: [PATCH 431/648] [PWGUD] Fixing VertexZ and Occupancy selection cut (#18425) --- PWGUD/Tasks/sgDeuteronSpectra.cxx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PWGUD/Tasks/sgDeuteronSpectra.cxx b/PWGUD/Tasks/sgDeuteronSpectra.cxx index 93c697908f8..64f10e8d23a 100644 --- a/PWGUD/Tasks/sgDeuteronSpectra.cxx +++ b/PWGUD/Tasks/sgDeuteronSpectra.cxx @@ -53,6 +53,8 @@ struct SgDeuteronSpectra { Configurable etaCut{"etaCut", 0.9, "Track Pseudorapidity"}; Configurable ptCut{"ptCut", 0.1, "Track Pt"}; Configurable nsigmatpcPreselCut{"nsigmatpcPreselCut", 3.0, "nsigma TPC preselection cut for TOF analysis"}; + Configurable occupancyCut{"occupancyCut", 1000, "Occupancy cut"}; + Configurable vertexZCut{"vertexZCut", 10, "VertexZ cut"}; // Configurable axis for histograms ConfigurableAxis ptAxis{"ptAxis", {200, 0.0, 10.0}, "p_{T}"}; @@ -68,6 +70,7 @@ struct SgDeuteronSpectra { const AxisSpec axistof{nSigmaAxis, "n#sigma_{TOF}"}; // Collision histograms + registry.add("collisions/VertexZ", "", {HistType::kTH1F, {{800, -20, 20}}}); registry.add("collisions/GapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{3, -0.5, 2.5}}}); registry.add("collisions/TrueGapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{4, -1.5, 2.5}}}); registry.add("tracks/Deut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); @@ -91,12 +94,21 @@ struct SgDeuteronSpectra { } // Define data types - using UDCollisionsFull = soa::Join; // UDCollisions + using UDCollisionsFull = soa::Join; // UDCollisions using UDCollisionFull = UDCollisionsFull::iterator; using UDTracksFull = soa::Join; void process(UDCollisionFull const& coll, UDTracksFull const& tracks) { + registry.fill(HIST("collisions/VertexZ"), coll.posZ()); + // applying event cuts + if (std::abs(coll.posZ()) > vertexZCut) { + return; + } + if (coll.occupancyInTime() > occupancyCut) { + return; + } + // fill gapside registry.fill(HIST("collisions/GapSide"), coll.gapSide(), 1.); std::vector fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; int truegapSide = sgSelector.trueGap(coll, fitCut[0], fitCut[1], fitCut[2], zdcCut); From a8e98f7650aef84d10c296787bc10445eb26d197 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:12:06 +0530 Subject: [PATCH 432/648] [PWGLF] Removed filter to get entries before/after |Vz| selection (#18451) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 21e7db35a50..5017b6b91a8 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -1071,21 +1071,22 @@ struct Kstarqa { // Processed events will be already fulfilling the event selection // requirements // Filter eventFilter = (o2::aod::evsel::sel8 == true); - Filter posZFilter = (nabs(o2::aod::collision::posZ) < configGp.cutzvertex); + // Filter posZFilter = (nabs(o2::aod::collision::posZ) < configGp.cutzvertex); - // Filter acceptanceFilter = (nabs(aod::track::eta) < configGp.cfgCutEtaMax && nabs(aod::track::pt) > configGp.cfgCutPT) && (nabs(aod::track::eta) > configGp.cfgCutEtaMin); Filter acceptanceFilter = (nabs(aod::track::eta) < configGp.cfgCutEtaMax && nabs(aod::track::pt) > configGp.cfgCutPT); - // Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < configGp.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < configGp.cfgCutDCAz) && (nabs(aod::track::dcaXY) > configGp.cfgCutDCAxyMin); Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < configGp.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < configGp.cfgCutDCAz); - using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs - using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + // using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + // using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + + using EventCandidates = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + using EventCandidatesMix = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + using TrackCandidates = soa::Filtered>; using EventCandidatesMC = soa::Join; // using EventCandidatesMC = soa::Filtered>; using TrackCandidatesMC = soa::Filtered>; - // using EventMCGenerated = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs using EventMCGenerated = soa::Join; using McCollisionMults = soa::Join; From 3ce67fa8bc4f17fc7867b96b06313598cd176bd6 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Sun, 25 Oct 2026 18:43:28 +0100 Subject: [PATCH 433/648] [PWGHF] Fix condition for gen tree filling (#18458) --- PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index 849b8d93f96..2bf9c358bcf 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -884,10 +884,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; - if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } rowCandidateFullParticles( @@ -956,10 +956,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; - if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } rowCandidateFullParticles( @@ -1020,10 +1020,10 @@ struct HfTreeCreatorXicToXiPiPi { for (const auto& particle : particles) { float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; - if (!TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && !TESTBIT(std::abs(particle.flagMcMatchGen()), aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table - continue; - } if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); } From 9410f19b32b6695ac335a43a0e2cfc1c89172a78 Mon Sep 17 00:00:00 2001 From: "Q.Y. Xia" <91366503+huinaibing@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:12:50 +0800 Subject: [PATCH 434/648] [PWGCF] Add Time Associate method (#18457) Co-authored-by: ALICE Action Bot --- PWGCF/Flow/Tasks/pidFlowPtCorr.cxx | 945 ++++++++++++++++++++++++++++- 1 file changed, 931 insertions(+), 14 deletions(-) diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index c73b6ae59f9..da6e46f41be 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -21,7 +21,12 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/TableHelper.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" @@ -30,12 +35,18 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -46,6 +57,7 @@ #include #include +#include #include #include #include @@ -57,20 +69,58 @@ #include #include +#include + #include #include #include #include #include #include +#include #include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace o2::aod +{ +namespace pidflowtimetrack +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_INDEX_COLUMN(Track, track); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(HasITS, hasITS, bool); +DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, uint8_t); +DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); +DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, int); +DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, int); +DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); +DECLARE_SOA_COLUMN(NumCompatibleCollisions, numCompatibleCollisions, unsigned int); +} // namespace pidflowtimetrack + +// One row per accepted (target collision, source track), ordered by collision. +// Kinematics/DCA belong to the target vertex; detector quality belongs to the source. +// Baseline selection is applied here; analysis quality/pT cuts stay in the consumers. +DECLARE_SOA_TABLE(PidFlowTimeTracks, "AOD", "PFTIMETRACK", + o2::soa::Index<>, pidflowtimetrack::CollisionId, pidflowtimetrack::TrackId, + pidflowtimetrack::Pt, pidflowtimetrack::Eta, pidflowtimetrack::Phi, + pidflowtimetrack::DcaXY, pidflowtimetrack::DcaZ, + pidflowtimetrack::HasITS, pidflowtimetrack::HasTPC, + pidflowtimetrack::ItsNCls, pidflowtimetrack::ItsChi2NCl, + pidflowtimetrack::TpcNClsCrossedRows, pidflowtimetrack::TpcNClsFound, + pidflowtimetrack::TpcChi2NCl, pidflowtimetrack::NumCompatibleCollisions); +} // namespace o2::aod + namespace { // Keep the corrconfigs vector layout in one named index table. Namespace @@ -210,8 +260,380 @@ enum : int { }; } // namespace unidentified } // namespace gfwBitMask + +// Only target-dependent kinematics change; detector quality and MC labels stay +// on the source row. This view deliberately has no PID-table dependency. +template +struct TimeAssociatedChargedTrack : Track { + explicit TimeAssociatedChargedTrack(Track const& source) + : Track(source), targetPt(source.pt()), targetEta(source.eta()), targetPhi(source.phi()), targetDCAxy(source.dcaXY()), targetDCAz(source.dcaZ()) + { + } + float targetPt, targetEta, targetPhi, targetDCAxy, targetDCAz; + [[nodiscard]] float pt() const { return targetPt; } + [[nodiscard]] float eta() const { return targetEta; } + [[nodiscard]] float phi() const { return targetPhi; } + [[nodiscard]] float dcaXY() const { return targetDCAxy; } + [[nodiscard]] float dcaZ() const { return targetDCAz; } +}; +template +T timeAssociationOption(InitContext& context, std::string const& task, std::string const& option) +{ + T value{}; + if (!o2::common::core::getTaskOptionValue(context, task, option, value, false)) { + LOGF(fatal, "Charged time association needs option %s from producer %s", option.c_str(), task.c_str()); + } + return value; +} + } // namespace +// Shared charged-only producer: public TrackAssoc supplies time compatibility; +// this task evaluates each pair at its target vertex once for flow, NUA and NUE. +struct PidFlowChargedTimeProducer { + Produces timeTracks; + // Original-vertex DCA/eta must not preselect candidates for another vertex. + using SourceTracks = soa::Join; + Preslice timePairsPerCollision = aod::track_association::collisionId; + Service ccdb{}; + HistogramRegistry registry{"registry"}; + OutputObj chargedTimeAssociationConfig{TNamed("ChargedTimeAssociationConfig", "disabled")}; + TrackSelection timeGlobalSelection; + TrackSelection timeSDDSelection; + bool timeIsRun3 = false; + int timeFieldRun = -1; + uint64_t timeFieldTimestamp = 0; + float timeL3Current = 0.f; + float timeDipoleCurrent = 0.f; + bool timeUniformField = false; + bool cfgRequireUniqueTimeAssociation = false; + bool cfgTimeAssociationQA = true; + bool timeUsePVAssociation = true; + float timeNSigma = 4.f; + float timeMargin = 0.f; + float cutEta = 0.8f; + float cutVertex = 10.f; + + void init(InitContext& context) + { + if (doprocessDummy.value == doprocessTimeTracks.value) { + LOGF(fatal, "Enable exactly one of processDummy and processTimeTracks in pid-flow-charged-time-producer"); + } + const bool needed = timeAssociationOption(context, "pid-flow-pt-corr", "processDataTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "fillCorrectionGraphTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processMCTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processTimeAssociationComparison"); + if (needed != doprocessTimeTracks.value) { + LOGF(fatal, "Enable processTimeTracks (and disable processDummy) exactly when a charged time consumer is enabled"); + } + if (!doprocessTimeTracks) { + return; + } + cfgRequireUniqueTimeAssociation = timeAssociationOption(context, "pid-flow-pt-corr", "cfgRequireUniqueTimeAssociation"); + cfgTimeAssociationQA = timeAssociationOption(context, "pid-flow-pt-corr", "cfgTimeAssociationQA"); + timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); + timeNSigma = timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat"); + timeMargin = timeAssociationOption(context, "track-to-collision-association", "timeMargin"); + cutEta = timeAssociationOption(context, "pid-flow-pt-corr", "trkQualityOpts.cfgCutEta"); + cutVertex = timeAssociationOption(context, "pid-flow-pt-corr", "cfgCutVertex"); + ccdb->setURL(timeAssociationOption(context, "pid-flow-pt-corr", "cfgurl")); + ccdb->setCaching(true); + ccdb->setCreatedNotAfter(timeAssociationOption(context, "pid-flow-pt-corr", "cfgnolaterthan")); + if (!timeAssociationOption(context, "track-to-collision-association", "processAssocWithTime") || + timeAssociationOption(context, "track-to-collision-association", "processStandardAssoc") || + timeAssociationOption(context, "track-to-collision-association", "setTrackSelections") != 0) { + LOGF(fatal, "Use track-to-collision-association: processAssocWithTime=true, processStandardAssoc=false, setTrackSelections=0"); + } + + // Inherit the effective upstream selection rather than introducing a + // second set of global-track cuts with different hidden DCA defaults. + timeIsRun3 = timeAssociationOption(context, "track-selection", "isRun3"); + const int matching = timeAssociationOption(context, "track-selection", "itsMatching"); + const int dcaSetup = timeAssociationOption(context, "track-selection", "dcaSetup"); + const bool compatibilityIU = timeAssociationOption(context, "track-selection", "compatibilityIU"); + if (timeIsRun3) { + using Matching = TrackSelection::GlobalTrackRun3ITSMatching; + const std::array modes{Matching::Run3ITSibAny, Matching::Run3ITSibAny, Matching::Run3ITSallAny, Matching::Run3ITSall7Layers, Matching::Run3ITSibFirst}; + if (matching < 0 || matching >= static_cast(modes.size())) { + LOGF(fatal, "Unsupported track-selection itsMatching=%d", matching); + } + timeGlobalSelection = getGlobalTrackSelectionRun3ITSMatch(modes[matching], dcaSetup); + timeGlobalSelection.SetTrackType(compatibilityIU ? aod::track::TrackTypeEnum::TrackIU : aod::track::TrackTypeEnum::Track); + } else { + if (matching != 0) { + LOGF(fatal, "Run 2 time association requires track-selection itsMatching=0"); + } + timeGlobalSelection = getGlobalTrackSelection(); + } + timeSDDSelection = getGlobalTrackSelectionSDD(); + const auto ptMin = timeAssociationOption(context, "track-selection", "ptMin"); + const auto ptMax = timeAssociationOption(context, "track-selection", "ptMax"); + const auto etaMin = timeAssociationOption(context, "track-selection", "etaMin"); + const auto etaMax = timeAssociationOption(context, "track-selection", "etaMax"); + for (auto const& selection : {&timeGlobalSelection, &timeSDDSelection}) { + selection->SetPtRange(ptMin, ptMax); + selection->SetEtaRange(etaMin, etaMax); + } + std::string metadata = "association=TrackAssoc;table=PidFlowTimeTracks;charged-only;PID=none;local-density=none;source=Tracks;propagation=target-DCA,USEMatCorrNONE;PV=not-refitted;unique=" + std::to_string(static_cast(cfgRequireUniqueTimeAssociation)); + metadata += ";isRun3=" + std::to_string(static_cast(timeIsRun3)) + ";itsMatching=" + std::to_string(matching) + ";dcaSetup=" + std::to_string(dcaSetup) + ";compatibilityIU=" + std::to_string(static_cast(compatibilityIU)); + metadata += ";pt=" + std::to_string(ptMin) + "," + std::to_string(ptMax) + ";eta=" + std::to_string(etaMin) + "," + std::to_string(etaMax); + metadata += ";producer.nSigmaForTimeCompat=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat")); + metadata += ";producer.timeMargin=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "timeMargin")); + metadata += ";producer.bcWindowForOneSigma=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "bcWindowForOneSigma")); + metadata += ";producer.usePVAssociation=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"))); + metadata += ";producer.includeUnassigned=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "includeUnassigned"))); + chargedTimeAssociationConfig->SetTitle(metadata.c_str()); + LOG(info) << "Charged time association: " << metadata; + if (!cfgTimeAssociationQA) { + return; + } + registry.add("timeAssociation/candidatesPerTrack", "Time-compatible collisions per source track before event/track cuts;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesPerTrackSourceBaseline", "Source global/SDD + finite kinematics/DCA + eta only;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesVsSourceType", "All source tracks;N_{coll}^{comp};Source type", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsAmbiguousBC", "AmbiguousTracks BC status (also for assigned tracks);N_{coll}^{comp};Ambiguous BC status", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsRawTimeStatus", "Stored track time/resolution before PV substitution;N_{coll}^{comp};Raw time status", {HistType::kTH2D, {{101, -0.5, 100.5}, {4, -0.5, 3.5}}}); + registry.add("timeAssociation/candidatesVsOriginalTimeTest", "Fine time test at original collision; excludes coarse BC/search checks;N_{coll}^{comp};Original time test", {HistType::kTH2D, {{101, -0.5, 100.5}, {6, -0.5, 5.5}}}); + registry.add("timeAssociation/candidatesVsAcceptedTargets", "After unique cut, target vertex cut, propagation and baseline (before consumer cuts);N_{coll}^{comp};N accepted target collisions", {HistType::kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); + registry.add("timeAssociation/zeroCandidatePtEta", "Zero-candidate tracks with finite source pT/eta;Source p_{T} (GeV/c);Source #eta", {HistType::kTH2D, {{200, 0., 20.}, {100, -5., 5.}}}); + const auto setLabels = [](TAxis* axis, auto const& names) { + for (std::size_t bin = 0; bin < names.size(); ++bin) { + axis->SetBinLabel(bin + 1, names[bin]); + } + }; + setLabels(registry.get(HIST("timeAssociation/candidatesVsSourceType"))->GetYaxis(), + std::array{"no original collision", "assigned non-PV", "assigned PV contributor"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsAmbiguousBC"))->GetYaxis(), + std::array{"no ambiguous row", "invalid/empty BC slice", "valid BC slice"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsRawTimeStatus"))->GetYaxis(), + std::array{"finite time, res > 0", "finite time, res = 0", "finite time, res < 0", "nonfinite time or res"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsOriginalTimeTest"))->GetYaxis(), + std::array{"no original collision", "invalid effective time/res", "fails fine time test", "passes, original pair absent", "passes, original pair present", "invalid original collision index"}); + registry.add("timeAssociation/pairStatus", "Target-track pairs (stages overlap);stage;pairs", {HistType::kTH1D, {{9, 0, 9}}}); + const std::array labels{"producer pair", "duplicate pair", "nonunique rejected", "same collision", "different collision", "unassigned source", "propagation failed", "baseline rejected", "baseline accepted"}; + for (std::size_t bin = 0; bin < labels.size(); ++bin) { + registry.get(HIST("timeAssociation/pairStatus"))->GetXaxis()->SetBinLabel(bin + 1, labels[bin]); + } + } + + template + void setChargedTimeField(GRP const* grp) + { + auto* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (!field) { + o2::base::Propagator::initFieldFromGRP(grp); + } else if (!field->TestBit(o2::field::MagneticField::kOverrideGRP) && + (timeFieldRun < 0 || timeL3Current != grp->getL3Current() || timeDipoleCurrent != grp->getDipoleCurrent() || timeUniformField != grp->getFieldUniformity())) { + // Keep the field object alive: Propagator can already hold its pointer. + field->rescaleField(grp->getL3Current(), grp->getDipoleCurrent(), grp->getFieldUniformity()); + } + o2::base::Propagator::Instance()->updateField(); + timeL3Current = grp->getL3Current(); + timeDipoleCurrent = grp->getDipoleCurrent(); + timeUniformField = grp->getFieldUniformity(); + } + + void prepareChargedTimeField(aod::BCsWithTimestamps::iterator const& bc) + { + if (timeFieldRun == bc.runNumber() && timeFieldTimestamp == bc.timestamp()) { + return; + } + if (timeIsRun3) { + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPMagField for charged time association"); + } + setChargedTimeField(grp); + } else { + auto* grp = ccdb->getForTimeStamp("GLO/GRP/GRP", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPObject for charged time association"); + } + setChargedTimeField(grp); + } + timeFieldRun = bc.runNumber(); + timeFieldTimestamp = bc.timestamp(); + } + + template + std::vector chargedTimeMultiplicity(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) + { + std::vector counts(tracks.size(), 0); + std::vector lastCollision(tracks.size(), -1); + std::vector hasOriginalPair(cfgTimeAssociationQA ? tracks.size() : 0, false); + for (auto const& pair : pairs) { + const int64_t index = pair.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size())) { + LOGF(fatal, "TrackAssoc track index is outside the full source track table"); + } + // TrackAssoc is grouped by collision. Count distinct target collisions, + // including those that will later fail event or target-track cuts. + if (lastCollision[index] != pair.collisionId()) { + ++counts[index]; + lastCollision[index] = pair.collisionId(); + } + if (cfgTimeAssociationQA && pair.collisionId() == tracks.rawIteratorAt(index).collisionId()) { + hasOriginalPair[index] = true; + } + } + if (!cfgTimeAssociationQA) { + return counts; + } + // Cache the first ambiguous row, matching the upstream lookup. Do not + // dereference its BC slice until its endpoints have been checked. + std::vector ambiguousBCStatus(tracks.size(), 0); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || ambiguousBCStatus[index] != 0) { + continue; + } + auto const& ids = ambiguous.bcIds(); + const bool valid = ambiguous.has_bc() && ids[0] >= 0 && ids[1] >= ids[0] && ids[1] < bcs.size(); + ambiguousBCStatus[index] = valid ? 2 : 1; + } + // One unweighted entry per source track, including zero candidates, before + // the unique-association cut or any target event/track selection. + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const auto count = counts[index]; + registry.fill(HIST("timeAssociation/candidatesPerTrack"), count); + const int sourceType = !track.has_collision() ? 0 : (track.isPVContributor() ? 2 : 1); + registry.fill(HIST("timeAssociation/candidatesVsSourceType"), count, sourceType); + registry.fill(HIST("timeAssociation/candidatesVsAmbiguousBC"), count, ambiguousBCStatus[index]); + const bool finiteTime = std::isfinite(track.trackTime()) && std::isfinite(track.trackTimeRes()); + const int rawTimeStatus = !finiteTime ? 3 : (track.trackTimeRes() > 0.f ? 0 : (track.trackTimeRes() == 0.f ? 1 : 2)); + registry.fill(HIST("timeAssociation/candidatesVsRawTimeStatus"), count, rawTimeStatus); + const bool finite = std::isfinite(track.pt()) && std::isfinite(track.eta()) && std::isfinite(track.phi()) && std::isfinite(track.dcaXY()) && std::isfinite(track.dcaZ()); + if (finite && (track.isGlobalTrack() || track.isGlobalTrackSDD()) && std::fabs(track.eta()) < cutEta) { + // Diagnostic at the source vertex only; not the final flow selection. + registry.fill(HIST("timeAssociation/candidatesPerTrackSourceBaseline"), count); + } + if (count == 0 && std::isfinite(track.pt()) && std::isfinite(track.eta())) { + registry.fill(HIST("timeAssociation/zeroCandidatePtEta"), track.pt(), track.eta()); + } + int originalTimeStatus = 0; + if (track.has_collision()) { + const int64_t collisionIndex = track.collisionId() - collisions.offset(); + originalTimeStatus = 5; + if (collisionIndex >= 0 && collisionIndex < collisions.size()) { + auto collision = collisions.rawIteratorAt(collisionIndex); + const bool usePVTime = timeUsePVAssociation && track.isPVContributor(); + const float trackTime = usePVTime ? collision.collisionTime() : track.trackTime(); + const float trackRes = usePVTime ? o2::constants::lhc::LHCBunchSpacingNS : track.trackTimeRes(); + const float collisionRes = collision.collisionTimeRes(); + originalTimeStatus = 1; + if (std::isfinite(trackTime) && std::isfinite(trackRes) && trackRes >= 0.f && std::isfinite(collision.collisionTime()) && std::isfinite(collisionRes) && collisionRes >= 0.f) { + float threshold = trackRes; + if (!usePVTime) { + threshold = TESTBIT(track.flags(), aod::track::TrackTimeResIsRange) + ? trackRes + timeNSigma * std::sqrt(collisionRes * collisionRes) + timeMargin + : timeNSigma * std::sqrt(collisionRes * collisionRes + trackRes * trackRes) + timeMargin; + } + // Same BC as the original collision, hence bcOffset = 0. This + // checks only fine time compatibility, not the upstream BC cache + // or iterator window. A pass with no pair needs upstream study. + originalTimeStatus = std::fabs(trackTime - collision.collisionTime()) < threshold ? (hasOriginalPair[index] ? 4 : 3) : 2; + } + } + } + registry.fill(HIST("timeAssociation/candidatesVsOriginalTimeTest"), count, originalTimeStatus); + } + return counts; + } + + template + void fillChargedTimeTracks(TCollision const& collision, aod::TrackAssoc const& pairs, TTracks const& tracks, std::vector const& counts, std::vector& acceptedCounts) + { + auto collisionPairs = pairs.sliceBy(timePairsPerCollision, collision.globalIndex()); + std::unordered_set seen; + bool fieldReady = false; + for (auto const& pair : collisionPairs) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 0.5); + } + if (!seen.insert(pair.trackId()).second) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 1.5); + } + continue; + } + const int64_t index = pair.trackId() - tracks.offset(); + if (cfgRequireUniqueTimeAssociation && counts[index] != 1) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 2.5); + } + continue; + } + auto source = tracks.rawIteratorAt(index); + TimeAssociatedChargedTrack candidate(source); + const bool sameCollision = source.collisionId() == collision.globalIndex(); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), sameCollision ? 3.5 : 4.5); + } + if (cfgTimeAssociationQA && !source.has_collision()) { + registry.fill(HIST("timeAssociation/pairStatus"), 5.5); + } + if (!sameCollision) { + if (!fieldReady) { + prepareChargedTimeField(collision.template bc_as()); + fieldReady = true; + } + auto parameters = getTrackPar(source); + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, parameters, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dca)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 6.5); + } + continue; + } + candidate.targetPt = parameters.getPt(); + candidate.targetEta = parameters.getEta(); + candidate.targetPhi = parameters.getPhi(); + candidate.targetDCAxy = dca[0]; + candidate.targetDCAz = dca[1]; + } + const bool finite = std::isfinite(candidate.pt()) && std::isfinite(candidate.eta()) && std::isfinite(candidate.phi()) && std::isfinite(candidate.dcaXY()) && std::isfinite(candidate.dcaZ()); + const bool baseline = finite && (sameCollision ? (source.isGlobalTrack() || source.isGlobalTrackSDD()) : (timeGlobalSelection.IsSelected(candidate) || (!timeIsRun3 && timeSDDSelection.IsSelected(candidate)))); + if (!baseline || std::fabs(candidate.eta()) >= cutEta) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 7.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 8.5); + ++acceptedCounts[index]; + } + timeTracks(collision.globalIndex(), source.globalIndex(), + candidate.pt(), candidate.eta(), candidate.phi(), candidate.dcaXY(), candidate.dcaZ(), + source.hasITS(), source.hasTPC(), source.itsNCls(), source.itsChi2NCl(), + source.tpcNClsCrossedRows(), source.tpcNClsFound(), source.tpcChi2NCl(), counts[index]); + } + } + + void processTimeTracks(aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, + aod::TrackAssoc const& pairs, SourceTracks const& sources, aod::AmbiguousTracks const& ambiguousTracks) + { + // Count all producer candidates before vertex, event or target-track cuts. + auto counts = chargedTimeMultiplicity(pairs, sources, collisions, bcs, ambiguousTracks); + std::vector acceptedCounts(cfgTimeAssociationQA ? sources.size() : 0, 0); + for (auto const& collision : collisions) { + if (std::fabs(collision.posZ()) < cutVertex) { + fillChargedTimeTracks(collision, pairs, sources, counts, acceptedCounts); + } + } + if (cfgTimeAssociationQA) { + for (std::size_t index = 0; index < counts.size(); ++index) { + registry.fill(HIST("timeAssociation/candidatesVsAcceptedTargets"), counts[index], acceptedCounts[index]); + } + } + } + PROCESS_SWITCH(PidFlowChargedTimeProducer, processTimeTracks, "Build shared charged time-associated track table; no PID", false); + + void processDummy(aod::Collisions const&) {} + PROCESS_SWITCH(PidFlowChargedTimeProducer, processDummy, "Idle entry for original collisionId analysis", true); +}; + struct PidFlowPtCorr { // configurable double minVal4Float = 1e-3; @@ -220,6 +642,12 @@ struct PidFlowPtCorr { Configurable cfgCutChi2prTPCcls{"cfgCutChi2prTPCcls", 2.5, "Chi2 per TPC clusters"}; Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; Configurable cfgUseMultiplicity{"cfgUseMultiplicity", false, "Use raw PV-contributor count in |eta| < 0.8 instead of centrality for output axes and NUE lookup; configure axisMultiplicity and disable cfgDoMultPVCut"}; + Configurable cfgRequireUniqueTimeAssociation{"cfgRequireUniqueTimeAssociation", false, "Time entries: require exactly one producer candidate collision before event/track cuts"}; + Configurable cfgTimeAssociationQA{"cfgTimeAssociationQA", true, "Create and fill time-association producer, comparison and MC QA; keep event counters and flow/NUA/NUE outputs"}; + ConfigurableAxis cfgaxisAssociationNch{"cfgaxisAssociationNch", {601, -5., 6005.}, "Association QA Nch (default bin width: 10 tracks; includes zero)"}; + ConfigurableAxis cfgaxisAssociationChangedNch{"cfgaxisAssociationChangedNch", {1001, -0.5, 1000.5}, "Association QA lost/gained track count"}; + ConfigurableAxis cfgaxisAssociationDeltaNch{"cfgaxisAssociationDeltaNch", {4001, -2000.5, 2000.5}, "Association QA Nch(time) minus Nch(original)"}; + ConfigurableAxis cfgaxisAssociationOccupancy{"cfgaxisAssociationOccupancy", {200, -0.5, 19999.5}, "Association QA track occupancy (-1 goes to underflow)"}; struct : ConfigurableGroup { std::string prefix = "trkQualityOpts"; @@ -391,6 +819,8 @@ struct PidFlowPtCorr { // data collisions filter using AodCollisions = soa::Filtered>; + using ChargedTimeCollisionsMC = soa::Filtered>; + // MC Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex; using FilteredMcCollisions = soa::Filtered; @@ -407,6 +837,8 @@ struct PidFlowPtCorr { // others Preslice perCollision = aod::track::collisionId; + Preslice timeTracksPerCollision = aod::pidflowtimetrack::collisionId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; // Connect to ccdb Service ccdb{}; @@ -424,6 +856,8 @@ struct PidFlowPtCorr { OutputObj fFCPr{FlowContainer("FlowContainerPr")}; OutputObj fFCUnidentified{FlowContainer("FlowContainerUnidentified")}; OutputObj fFCPidPtCorr{FlowContainer("FlowContainerPidPtCorr")}; + bool timeAcceptanceLoaded = false; + bool timeEfficiencyLoaded = false; // end val used for bootstrap // define global variables @@ -497,15 +931,19 @@ struct PidFlowPtCorr { funcProcessPidResponseMatrix, funcProcessDataC22DeltaPt, funcProcessPidPtCorrelations, + funcProcessDataTimeAssociation, + funcFillCorrectionGraphTimeAssociation, + funcProcessMCTimeAssociation, + funcProcessTimeAssociationComparison, funcNumber }; enum PidResponseBin { kPidRespPion = 0, - kPidRespKaon, - kPidRespProton, - kPidRespOther, - kPidRespNTrueBins, + kPidRespKaon = 1, + kPidRespProton = 2, + kPidRespOther = 3, + kPidRespNTrueBins = 4, kPidRespUnidentified = kPidRespOther, kPidRespNRecoBins = kPidRespNTrueBins }; @@ -633,8 +1071,8 @@ struct PidFlowPtCorr { auto& h = pidQualityQAHists[i]; h.tpcPIDClustersVsPt[stage]->Fill(pt, nPIDClusters); if (std::isfinite(tpcNSigma[i])) { - const double values[]{pt, cent, static_cast(nPIDClusters), tpcNSigma[i]}; - h.tpcNSigmaVsPIDClusters[stage]->Fill(values); + const std::array values{pt, cent, static_cast(nPIDClusters), tpcNSigma[i]}; + h.tpcNSigmaVsPIDClusters[stage]->Fill(values.data()); } } } @@ -688,8 +1126,8 @@ struct PidFlowPtCorr { } h.itsNSigmaVsPt[stage]->Fill(pt, itsNSigma[i]); if (std::isfinite(tpcNSigma[i])) { - const double values[]{pt, cent, tpcNSigma[i], itsNSigma[i]}; - h.tpcVsITSNSigma[stage]->Fill(values); + const std::array values{pt, cent, tpcNSigma[i], itsNSigma[i]}; + h.tpcVsITSNSigma[stage]->Fill(values.data()); } } } @@ -697,8 +1135,33 @@ struct PidFlowPtCorr { // others - void init(InitContext const&) // Initialization + void initChargedTimeAssociation(InitContext& context) { + if (doprocessData || dofillCorrectionGraph || doprocessReco || doprocessSim || doprocessReco1D || doprocessSim1D || + doprocessDataC22DeltaPt || doprocessPidPtCorrelations || doprocessMCClosure || doprocessQA || dodetectorPidQA || doprocessPidResponseMatrix) { + LOGF(fatal, "Charged time mode: explicitly disable all original process switches to avoid mixed association/PID samples"); + } + if (!switchsOpts.cfgOutputCharged || switchsOpts.cfgOutputPion || switchsOpts.cfgOutputKaon || switchsOpts.cfgOutputProton || switchsOpts.cfgOutputUnidentified) { + LOGF(fatal, "Charged time mode requires cfgOutputCharged=true and all four PID/unidentified output switches=false"); + } + if (dofillCorrectionGraphTimeAssociation && !switchsOpts.cfgOutputrunbyrun) { + LOGF(fatal, "fillCorrectionGraphTimeAssociation requires switchsOpts.cfgOutputrunbyrun=true"); + } + if (doprocessMCTimeAssociation && !switchsOpts.cfgOutPutMC) { + LOGF(fatal, "processMCTimeAssociation requires switchsOpts.cfgOutPutMC=true"); + } + if (!timeAssociationOption(context, "pid-flow-charged-time-producer", "processTimeTracks") || + timeAssociationOption(context, "pid-flow-charged-time-producer", "processDummy")) { + LOGF(fatal, "Charged time mode requires pid-flow-charged-time-producer: processTimeTracks=true, processDummy=false"); + } + } + + void init(InitContext& context) // Initialization + { + const bool timeMode = doprocessDataTimeAssociation || dofillCorrectionGraphTimeAssociation || doprocessMCTimeAssociation || doprocessTimeAssociationComparison; + if (timeMode) { + initChargedTimeAssociation(context); + } const AxisSpec activityAxis{axisMultiplicity, getActivityAxisTitle()}; // FlowContainer's AxisSpec overload expects a full array of bin edges. // Use its uniform-axis overload for fixed-width axisMultiplicity settings. @@ -726,9 +1189,11 @@ struct PidFlowPtCorr { cfgMultPVCutPara = evtSeleOpts.cfgMultPVCut; - fTPCPIDClustersMin = std::make_unique("pidFlowPtCorrTPCPIDClustersMin", cfgTPCPIDClustersMinFunction.value.c_str(), 0., 256.); - if (!fTPCPIDClustersMin->IsValid() || fTPCPIDClustersMin->GetNpar() != 0) { - LOGF(fatal, "Invalid cfgTPCPIDClustersMinFunction: use a ROOT formula in x = tpcNClsFound() with numeric coefficients and no free parameters"); + if (!timeMode) { + fTPCPIDClustersMin = std::make_unique("pidFlowPtCorrTPCPIDClustersMin", cfgTPCPIDClustersMinFunction.value.c_str(), 0., 256.); + if (!fTPCPIDClustersMin->IsValid() || fTPCPIDClustersMin->GetNpar() != 0) { + LOGF(fatal, "Invalid cfgTPCPIDClustersMinFunction: use a ROOT formula in x = tpcNClsFound() with numeric coefficients and no free parameters"); + } } // Set the pt, mult and phi Axis; @@ -891,6 +1356,34 @@ struct PidFlowPtCorr { registry.addClone("hEventCount/processData", "hEventCount/processSim"); // processPidResponseMatrix registry.addClone("hEventCount/processData", "hEventCount/processPidResponseMatrix"); + if (timeMode) { + auto originalCounter = registry.get(HIST("hEventCount/processData")); + for (auto const& name : {"hEventCount/processDataTimeAssociation", "hEventCount/fillCorrectionGraphTimeAssociation", "hEventCount/processMCTimeAssociation"}) { + auto counter = registry.add(name, "Target collisions;selection;collisions", {HistType::kTH1D, {{15, 0, 15}}}); + for (int bin = 1; bin <= originalCounter->GetNbinsX(); ++bin) { + counter->GetXaxis()->SetBinLabel(bin, originalCounter->GetXaxis()->GetBinLabel(bin)); + } + counter->GetXaxis()->SetBinLabel(15, "has associated baseline track"); + } + if (doprocessMCTimeAssociation && cfgTimeAssociationQA) { + registry.add("timeAssociation/mcEventStatus", "MC event sample;stage;target collisions", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabels{"target collision", "has MC label", "one reco per MC in vertex acceptance", "accepted for reco and gen"}; + for (std::size_t bin = 0; bin < mcLabels.size(); ++bin) { + registry.get(HIST("timeAssociation/mcEventStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabels[bin]); + } + registry.add("timeAssociation/mcTrackStatus", "Selected charged tracks;truth category;tracks", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabelsTrack{"no MC particle", "other MC collision", "same MC outside primary truth sample", "matched primary numerator"}; + for (std::size_t bin = 0; bin < mcLabelsTrack.size(); ++bin) { + registry.get(HIST("timeAssociation/mcTrackStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabelsTrack[bin]); + } + registry.add("timeAssociation/hPtCentMcSelected", "All selected reassociated tracks;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("timeAssociation/hPtCentMcOtherCollision", "Tracks from a different true event;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + } + } + if (doprocessTimeAssociationComparison && cfgTimeAssociationQA) { + registry.addClone("hEventCount/processData", "hEventCount/processTimeAssociationComparison"); + initTimeAssociationComparisonQA(); + } if (switchsOpts.cfgAddPidPtCorrelationProfiles.value) { registry.addClone("hEventCount/processData", "hEventCount/processPidPtCorrelations"); } @@ -2322,6 +2815,18 @@ struct PidFlowPtCorr { case MyFunctionName::funcProcessPidPtCorrelations: registry.fill(HIST("hEventCount/processPidPtCorrelations"), position); break; + case MyFunctionName::funcProcessDataTimeAssociation: + registry.fill(HIST("hEventCount/processDataTimeAssociation"), position); + break; + case MyFunctionName::funcFillCorrectionGraphTimeAssociation: + registry.fill(HIST("hEventCount/fillCorrectionGraphTimeAssociation"), position); + break; + case MyFunctionName::funcProcessMCTimeAssociation: + registry.fill(HIST("hEventCount/processMCTimeAssociation"), position); + break; + case MyFunctionName::funcProcessTimeAssociationComparison: + registry.fill(HIST("hEventCount/processTimeAssociationComparison"), position); + break; default: // LOGF(warning, "could not find event count graph"); @@ -2457,7 +2962,10 @@ struct PidFlowPtCorr { return false; } - registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + // The comparison has its own counter; do not double-fill physics event QA. + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + } if (evtSeleOpts.cfgDoMultPVCut.value) { if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return false; @@ -2482,7 +2990,9 @@ struct PidFlowPtCorr { } fillEventCountHelper(funcName, 11.5); - registry.fill(HIST("hInteractionRate"), interactionRate); + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hInteractionRate"), interactionRate); + } if (evtSeleOpts.cfgCutminIR.value >= 0 && interactionRate > 0 && interactionRate < evtSeleOpts.cfgCutminIR.value) { return false; } @@ -2509,6 +3019,53 @@ struct PidFlowPtCorr { // main functions + void loadChargedTimeCorrections(uint64_t timestamp, bool acceptance = true) + { + // Preserve the existing one/four-path convention, but load only charged. + constexpr std::size_t FullCorrectionSpecies = 4; + if (!timeEfficiencyLoaded) { + auto const& paths = correctionPathOpts.cfgEfficiencyPath.value; + if (paths.size() == 1 || paths.size() == FullCorrectionSpecies) { + auto* efficiency = ccdb->getForTimeStamp(paths.front(), timestamp); + if (!efficiency) { + LOGF(fatal, "Missing charged time-association efficiency: %s", paths.front().c_str()); + } + mEfficiency.push_back(efficiency); + } + timeEfficiencyLoaded = true; + } + if (acceptance && !timeAcceptanceLoaded) { + if (switchsOpts.cfgUseNUAWithPt) { + if (cfgAcceptanceWithPt.size() == 1 || cfgAcceptanceWithPt.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptanceWithPt.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptanceWithPt.front().c_str()); + } + mAcceptanceWithPt.push_back(weights); + } + } else if (cfgAcceptance.size() == 1 || cfgAcceptance.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptance.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptance.front().c_str()); + } + mAcceptance.push_back(weights); + } + timeAcceptanceLoaded = true; + } + } + + template + bool chargedTimeEventSelected(TCollision const& collision, MyFunctionName name) + { + fillEventCountHelper(name, 0.5); + if (!collision.sel8()) { + return false; + } + fillEventCountHelper(name, 1.5); + auto bc = collision.template bc_as(); + return eventSelected(collision, getCentrality(collision), getInteractionRate(bc.timestamp(), bc.runNumber()), name); + } + void processData(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) { // sub region, init @@ -3068,6 +3625,365 @@ struct PidFlowPtCorr { } PROCESS_SWITCH(PidFlowPtCorr, processData, "", true); + void initTimeAssociationComparisonQA() + { + constexpr const char* selection = "Common selected events, final charged selection, unit weights"; + registry.add("associationComparison/hEventAvailability", "Common events including empty track sets;Centrality (%);Track availability", {HistType::kTH2D, {axisMultiplicity, {4, -0.5, 3.5}}}); + registry.add("associationComparison/hNchOriginalVsTime", "Final charged multiplicity;N_{ch} original;N_{ch} time;Event count", {HistType::kTH2D, {cfgaxisAssociationNch, cfgaxisAssociationNch}}); + registry.add("associationComparison/hLostVsGained", "Track membership changes;Lost tracks;Gained tracks;Event count", {HistType::kTH2D, {cfgaxisAssociationChangedNch, cfgaxisAssociationChangedNch}}); + registry.add("associationComparison/hDeltaNchVsCent", "Multiplicity change;Centrality (%);N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {axisMultiplicity, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hDeltaNchVsOccupancy", "Multiplicity change;Track occupancy;N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hSetChangeVsCent", "Nonempty union only;Centrality (%);(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {axisMultiplicity, {101, -0.005, 1.005}}}); + registry.add("associationComparison/hSetChangeVsOccupancy", "Nonempty union only;Track occupancy;(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, {101, -0.005, 1.005}}}); + registry.add("associationComparison/pSetChangeVsCent", "Event mean, nonempty union only;Centrality (%);Mean set-change fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pSetChangeVsOccupancy", "Event mean, nonempty union only;Track occupancy;Mean set-change fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pLostFractionVsCent", "Event mean, original Nch > 0;Centrality (%);Mean lost/original fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pLostFractionVsOccupancy", "Event mean, original Nch > 0;Track occupancy;Mean lost/original fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pGainedFractionVsCent", "Event mean, time Nch > 0;Centrality (%);Mean gained/time fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pGainedFractionVsOccupancy", "Event mean, time Nch > 0;Track occupancy;Mean gained/time fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + // Category projections provide original/time denominators for lost/gained + // spectra ratios. Entries count track-target pairs, not distinct sources. + registry.add("associationComparison/hPtByCategory", "Final tracks per target collision;p_{T} (GeV/c);Track category;Track-target count", {HistType::kTH2D, {cfgaxisPt, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hEtaByCategory", "Final tracks per target collision;#eta;Track category;Track-target count", {HistType::kTH2D, {cfgaxisEta, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hPhiByCategory", "Final tracks per target collision;#varphi;Track category;Track-target count", {HistType::kTH2D, {cfgaxisPhi, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginal", "All source tracks once per dataframe;N selected collisions (original);Source track count", {HistType::kTH1D, {{2, -0.5, 1.5}}}); + registry.add("associationComparison/hFinalCollisionsTime", "All source tracks once per dataframe;N selected collisions (time);Source track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginalVsTime", "All source tracks once per dataframe;N selected collisions (original);N selected collisions (time);Source track count", {HistType::kTH2D, {{2, -0.5, 1.5}, {101, -0.5, 100.5}}}); + const auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t index = 0; index < labels.size(); ++index) { + axis->SetBinLabel(index + 1, labels[index]); + } + }; + setLabels(registry.get(HIST("associationComparison/hEventAvailability"))->GetYaxis(), + std::array{"both empty", "original only", "time only", "both nonempty"}); + const std::array categories{"original", "time", "common", "lost", "gained"}; + setLabels(registry.get(HIST("associationComparison/hPtByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hEtaByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hPhiByCategory"))->GetYaxis(), categories); + LOG(info) << "Association comparison: " << selection << "; original=collisionId, time=PidFlowTimeTracks; unique=" << cfgRequireUniqueTimeAssociation.value; + } + + // Evaluate both associations in the same event loop without PID or correction + // weights. Keep the full source table to include zero-use tracks in usage QA. + void processTimeAssociationComparison(AodCollisions const& collisions, aod::BCsWithTimestamps const&, + PidFlowChargedTimeProducer::SourceTracks const& sources, aod::PidFlowTimeTracks const& associatedTracks) + { + if (!cfgTimeAssociationQA) { + return; + } + std::vector originalUses(sources.size(), 0), timeUses(sources.size(), 0); + const auto sourceIndex = [&](int64_t trackId) { + const int64_t index = trackId - sources.offset(); + if (index < 0 || index >= sources.size()) { + LOGF(fatal, "Association comparison track index is outside the full source track table"); + } + return index; + }; + const auto fillKinematics = [&](auto const& track, int category) { + registry.fill(HIST("associationComparison/hPtByCategory"), track.pt(), category); + registry.fill(HIST("associationComparison/hEtaByCategory"), track.eta(), category); + registry.fill(HIST("associationComparison/hPhiByCategory"), track.phi(), category); + }; + for (auto const& collision : collisions) { + // No track-presence requirement: both methods share this event sample. + if (!chargedTimeEventSelected(collision, funcProcessTimeAssociationComparison)) { + continue; + } + auto originalTracks = sources.sliceBy(perCollision, collision.globalIndex()); + auto timeTracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + std::unordered_set originalIds, timeIds; + for (auto const& track : originalTracks) { + // Reproduce processData's framework filter and final charged cuts, + // without requiring or evaluating any PID tables. + if (!(track.isGlobalTrack() || track.isGlobalTrackSDD() != 0) || !(std::fabs(track.eta()) < trkQualityOpts.cfgCutEta.value) || + !trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (originalIds.insert(track.globalIndex()).second) { + ++originalUses[sourceIndex(track.globalIndex())]; + fillKinematics(track, 0); + } + } + unsigned int common = 0; + for (auto const& track : timeTracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt()) || !timeIds.insert(track.trackId()).second) { + continue; + } + ++timeUses[sourceIndex(track.trackId())]; + fillKinematics(track, 1); + if (originalIds.contains(track.trackId())) { + ++common; + fillKinematics(track, 2); + } else { + fillKinematics(track, 4); + } + } + for (auto const& track : originalTracks) { + if (originalIds.contains(track.globalIndex()) && !timeIds.contains(track.globalIndex())) { + fillKinematics(track, 3); + } + } + const auto nOriginal = originalIds.size(); + const auto nTime = timeIds.size(); + const auto lost = nOriginal - common; + const auto gained = nTime - common; + const auto unionSize = common + lost + gained; + const double cent = getCentrality(collision); + const auto occupancy = collision.trackOccupancyInTimeRange(); + const double deltaNch = static_cast(nTime) - static_cast(nOriginal); + const int availability = (nOriginal > 0 ? 1 : 0) + (nTime > 0 ? 2 : 0); + registry.fill(HIST("associationComparison/hEventAvailability"), cent, availability); + registry.fill(HIST("associationComparison/hNchOriginalVsTime"), nOriginal, nTime); + registry.fill(HIST("associationComparison/hLostVsGained"), lost, gained); + registry.fill(HIST("associationComparison/hDeltaNchVsCent"), cent, deltaNch); + registry.fill(HIST("associationComparison/hDeltaNchVsOccupancy"), occupancy, deltaNch); + if (unionSize > 0) { + const double changeFraction = static_cast(lost + gained) / unionSize; + registry.fill(HIST("associationComparison/hSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/hSetChangeVsOccupancy"), occupancy, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsOccupancy"), occupancy, changeFraction); + } + if (nOriginal > 0) { + const double lostFraction = static_cast(lost) / nOriginal; + registry.fill(HIST("associationComparison/pLostFractionVsCent"), cent, lostFraction); + registry.fill(HIST("associationComparison/pLostFractionVsOccupancy"), occupancy, lostFraction); + } + if (nTime > 0) { + const double gainedFraction = static_cast(gained) / nTime; + registry.fill(HIST("associationComparison/pGainedFractionVsCent"), cent, gainedFraction); + registry.fill(HIST("associationComparison/pGainedFractionVsOccupancy"), occupancy, gainedFraction); + } + } + for (std::size_t index = 0; index < originalUses.size(); ++index) { + registry.fill(HIST("associationComparison/hFinalCollisionsOriginal"), originalUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsTime"), timeUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsOriginalVsTime"), originalUses[index], timeUses[index]); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processTimeAssociationComparison, "Compare original collisionId and time-associated final charged tracks; no PID/NUA/NUE", false); + + // Independent charged-only counterpart of processData. All moment/profile + // definitions below retain the original charged weights and bootstrap draw. + void processDataTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessDataTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessDataTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const float vtxz = collision.posZ(); + const float rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + fGFW->Clear(); + registry.fill(HIST("hVtxZ"), vtxz); + registry.fill(HIST("hMult"), tracks.size()); + registry.fill(HIST("hMultTPC"), collision.multTPC()); + registry.fill(HIST("hCent"), cent); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountData"), cent); + } + + double ptSum = 0., ptSumw2 = 0., nch = 0., nchSquare = 0., ptSquareSum = 0.; + double nchPassedSelection = 0., nchCorrectedPassed = 0.; + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + setParticleNUEWeight(weff, track, cent); + nchPassedSelection += 1.; + nchCorrectedPassed += weff; + if (std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { + nch += weff; + nchSquare += weff * weff; + ptSum += weff * track.pt(); + ptSumw2 += weff * weff * track.pt(); + ptSquareSum += weff * weff * track.pt() * track.pt(); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, vtxz); + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("hPhicorr"), track.phi(), wacc); + registry.fill(HIST("hPhiCorrNUANUE"), track.phi(), wacc * weff); + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPt"), track.pt()); + registry.fill(HIST("hPtCorr"), track.pt(), weff); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hPtCentData"), track.pt(), cent); + } + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); + } + registry.fill(HIST("hNchUnCorrectedVSNchCorrected"), nchPassedSelection, nchCorrectedPassed); + if (nch <= 0.) { + return; + } + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, nch, nch, "c24TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, nch, nch, "c22FullTrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, nch, nch, "c34TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2PtWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, ptSum, nch, "covV3Pt"); + fillFC4PtC22(cent, ptSum, nch, rndm); + fFCCh->FillProfile("hMeanPt", cent, ptSum / nch, nch, rndm); + fFCCh->FillProfile("hMeanPtWeightOne", cent, ptSum / nch, 1., rndm); + const double nchDiff = nch * nch - nchSquare; + if (nchDiff > minVal4Float) { + fFCCh->FillProfile("ptSquareAve", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptSquareAveWeightOne", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, 1., rndm); + fFCCh->FillProfile("ptAve", cent, (nch * ptSum - ptSumw2) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptAveWeightOne", cent, (nch * ptSum - ptSumw2) / nchDiff, 1., rndm); + } + if (switchsOpts.cfgAddC22DeltaPtHistograms) { + registry.fill(HIST("c22DeltaPt/hMeanPtCharged"), cent, rndm * cfgFlowNbootstrap, ptSum / nch, nch); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptCharged"), cent, ptSum, nch, rndm); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processDataTimeAssociation, "Charged flow and mean-pT using time-based TrackAssoc; no PID", false); + + void fillCorrectionGraphTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcFillCorrectionGraphTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcFillCorrectionGraphTimeAssociation, 14.5); + auto bc = collision.bc_as(); + auto run = std::find(runNumbers.begin(), runNumbers.end(), bc.runNumber()); + if (run == runNumbers.end()) { + return; + } + const int runIndex = std::distance(runNumbers.begin(), run); + const double cent = getCentrality(collision); + if (switchsOpts.cfgUseNUAWithPt) { + loadChargedTimeCorrections(bc.timestamp(), false); + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + if (switchsOpts.cfgUseNUAWithPt) { + setParticleNUEWeight(weff, track, cent); + } + registry.fill(HIST("correction/hRunNumberPhiEtaVertex"), runIndex, track.phi(), track.eta(), collision.posZ(), track.pt(), weff); + } + } + PROCESS_SWITCH(PidFlowPtCorr, fillCorrectionGraphTimeAssociation, "Charged NUA source using time-based TrackAssoc; no PID", false); + + // Fill reco numerator and generated denominator together, so they use the + // very same accepted target event. A different true collision is contamination, + // not successful reconstruction of a particle in this denominator. + void processMCTimeAssociation(ChargedTimeCollisionsMC const& collisions, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& associatedTracks, + aod::McCollisions const& mcCollisions, aod::McParticles const& particles, aod::McTrackLabels const& labels) + { + std::vector recoPerMC(mcCollisions.size(), 0); + for (auto const& collision : collisions) { + if (collision.has_mcCollision()) { + ++recoPerMC[collision.mcCollisionId() - mcCollisions.offset()]; + } + } + for (auto const& collision : collisions) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 0.5); + } + if (!collision.has_mcCollision()) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 1.5); + } + const int64_t mcIndex = collision.mcCollisionId() - mcCollisions.offset(); + auto mcCollision = mcCollisions.rawIteratorAt(mcIndex); + if (recoPerMC[mcIndex] != 1 || std::fabs(mcCollision.posZ()) >= cfgCutVertex.value) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 2.5); + } + if (!chargedTimeEventSelected(collision, funcProcessMCTimeAssociation)) { + continue; + } + auto tracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + if (tracks.size() == 0) { + continue; + } + fillEventCountHelper(funcProcessMCTimeAssociation, 14.5); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 3.5); + } + const double cent = getCentrality(collision); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountMcRec"), cent); + registry.fill(HIST("ptSpectra/hCentEventCountMcGen"), cent); + } + for (auto const& particle : particles.sliceBy(particlesPerMCCollision, collision.mcCollisionId())) { + if (particle.isPhysicalPrimary() && particleSelected(particle)) { + registry.fill(HIST("correction/hPtCentMcGen"), particle.pt(), cent); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/hPtCentMcSelected"), track.pt(), cent); + } + const int64_t labelIndex = track.trackId() - labels.offset(); + if (labelIndex < 0 || labelIndex >= labels.size()) { + LOGF(fatal, "Time-associated source track index is outside McTrackLabels"); + } + auto source = labels.rawIteratorAt(labelIndex); + if (!source.has_mcParticle()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 0.5); + } + continue; + } + auto particle = source.mcParticle_as(); + if (particle.mcCollisionId() != collision.mcCollisionId()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 1.5); + registry.fill(HIST("timeAssociation/hPtCentMcOtherCollision"), track.pt(), cent); + } + continue; + } + if (!particle.isPhysicalPrimary() || !particleSelected(particle)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 2.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 3.5); + } + registry.fill(HIST("correction/hPtCentMcRec"), track.pt(), cent); + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processMCTimeAssociation, "Charged NUE reco/gen with common time-association event sample; no detector PID", false); + /** * @brief Fill charged, identified-particle, and unidentified-particle c22 versus event mean pT. * @note Each profile uses the mean pT of the same particle sample on its pT axis: @@ -4298,5 +5214,6 @@ struct PidFlowPtCorr { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } From 6ee91849acd4fb4d6325912e2b9b61bcb76c1819 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Mon, 26 Oct 2026 16:39:46 +0900 Subject: [PATCH 435/648] [PWGLF] Fix Omega2012 and K1 MC efficiency denominators and truth matching (#18431) Co-authored-by: Bong-Hwi Lim --- PWGLF/Core/K1AnalysisMicroCore.h | 57 +++++- PWGLF/Core/Omega2012AnalysisCore.h | 22 ++- PWGLF/DataModel/LFK1MlTables.h | 5 +- PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx | 40 +++-- PWGLF/Tasks/Resonances/k1TrainingTable.cxx | 22 ++- PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 163 ++++++++++++++++-- .../Resonances/omega2012TrainingTable.cxx | 28 +-- 7 files changed, 288 insertions(+), 49 deletions(-) diff --git a/PWGLF/Core/K1AnalysisMicroCore.h b/PWGLF/Core/K1AnalysisMicroCore.h index 4e2cd0e720c..3b024afbf68 100644 --- a/PWGLF/Core/K1AnalysisMicroCore.h +++ b/PWGLF/Core/K1AnalysisMicroCore.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,16 @@ enum class K1TruthChannel { KStarPi = 2 }; +struct K1ParentTruth { + double pt = std::numeric_limits::quiet_NaN(); + double y = std::numeric_limits::quiet_NaN(); + int pdgCode = 0; + K1TruthChannel channel = K1TruthChannel::None; +}; +using K1ParentTruthMap = std::unordered_map; + +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2); + // The value is the species index of the PID configuration in ResoAnalysisSelectionCore. enum class Species : int { Pion = 0, @@ -217,6 +228,49 @@ K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kao return K1TruthChannel::None; } +template +int k1MotherId(K1TruthChannel channel, const Kaon& kaon, const Pion& pion1, const Pion& pion2) +{ + if (channel == K1TruthChannel::RhoK) { + return kaon.motherId(); + } + if (channel == K1TruthChannel::KStarPi) { + return std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() : pion2.motherId(); + } + return -1; +} + +template +K1ParentTruthMap makeK1ParentTruthMap(const ParentsType& parents) +{ + K1ParentTruthMap result; + for (const auto& part : parents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus || part.originalMcParticleId() < 0) { + continue; + } + const auto id = static_cast(part.originalMcParticleId()); + const int charge = part.pdgCode() > 0 ? 1 : -1; + result.emplace(id, K1ParentTruth{part.pt(), part.y(), part.pdgCode(), classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2())}); + } + return result; +} + +inline const K1ParentTruth* findK1ParentTruth(const K1ParentTruthMap& parents, int64_t motherId, + K1TruthChannel channel, int charge) +{ + const auto it = parents.find(motherId); + if (it == parents.end()) { + return nullptr; + } + const auto& parent = it->second; + const int expectedPdg = charge * o2::constants::physics::Pdg::kK1_1270Plus; + if (channel == K1TruthChannel::None || parent.channel != channel || parent.pdgCode != expectedPdg || + !std::isfinite(parent.pt) || parent.pt < 0. || !std::isfinite(parent.y)) { + return nullptr; + } + return &parent; +} + // Immediate decay channel of a generated K1 from the PDG codes of its two daughters. inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2) { @@ -635,7 +689,8 @@ class K1AnalysisMicroCore const int charge = part.pdgCode() > 0 ? 1 : -1; const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2()); histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); - if (part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { + if (!std::isfinite(part.pt()) || part.pt() < 0. || !std::isfinite(part.y()) || + part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { continue; } histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); diff --git a/PWGLF/Core/Omega2012AnalysisCore.h b/PWGLF/Core/Omega2012AnalysisCore.h index d0d3600d7b4..57444555806 100644 --- a/PWGLF/Core/Omega2012AnalysisCore.h +++ b/PWGLF/Core/Omega2012AnalysisCore.h @@ -58,6 +58,17 @@ using o2::analysis::resonance::ResoAnalysisSelectionCore; using o2::analysis::resonance::TrackCuts; using o2::analysis::resonance::TrackStage; +// Omega(2012) analysis and ML export share these event-level selection defaults. +// Keep the generic EventCuts defaults unchanged for other resonance tasks. +inline EventCuts makeOmega2012EventCuts() +{ + EventCuts cuts; + cuts.cRecoINELgt0.value = true; + cuts.cMCINELgt0.value = true; + cuts.cMCVtxIn10.value = true; + return cuts; +} + enum class DecayMode : uint8_t { XiK0s = 0, // Omega(2012)- -> Xi- K0S Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- @@ -932,11 +943,12 @@ class Omega2012AnalysisCore } } - // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The optional callback - // receives (parent, immediate channel) for the parents inside the rapidity window. Split reconstructed collisions - // repeat parent sets: this is not an unconditional generated denominator. + // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The caller supplies + // the denominator rapidity limit so it can remain independent of a wider candidate-export window. The optional + // callback receives (parent, immediate channel) for the parents inside that rapidity window. Split reconstructed + // collisions repeat parent sets: this is not an unconditional generated denominator. template - void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, float rapidityMax, Callback callback = nullptr) { for (const auto& part : resoParents) { if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { @@ -944,7 +956,7 @@ class Omega2012AnalysisCore } const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2()); histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); - if (!(std::abs(part.y()) < mCandidateCuts.cfgRapidityCut.value)) { + if (!(std::abs(part.y()) < rapidityMax)) { continue; } histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h index 6add0d58252..7b2c20c8174 100644 --- a/PWGLF/DataModel/LFK1MlTables.h +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -63,14 +63,15 @@ DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); // 16 = candidate cuts. // Candidates written at the "selected" export stage carry 31. DECLARE_SOA_COLUMN(K1BaselinePassBits, k1BaselinePassBits, uint16_t); +// NOLINTNEXTLINE(modernize-avoid-c-arrays) -- persistent fixed-size AOD column DECLARE_SOA_COLUMN(K1MasterFeatures, k1MasterFeatures, float[125]); //! 125 master features of the K1 ML feature contract (PWGLF/Core/K1MlFeatures.h) DECLARE_SOA_COLUMN(K1FeatureStatus, k1FeatureStatus, uint8_t); //! o2::analysis::k1ml::BuildStatus; only Ok (0) rows are written DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t); DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t); -DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! NaN in K1MlTruth: not filled for reconstructed candidates -DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! NaN in K1MlTruth: not filled for reconstructed candidates +DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! Parent-table pT for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! Parent-table y for a validated reconstructed MC match; NaN otherwise DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t); DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t); DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t); diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index a84ed359767..efd75953055 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -52,6 +52,7 @@ struct HistogramOptions : ConfigurableGroup { Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; Configurable cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"}; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require generated K1 rapidity in the configured window for MC efficiency numerators"}; }; enum BinAnti : unsigned int { @@ -231,12 +232,15 @@ struct K1AnalysisMicro { AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"}; histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); - histos.add("MCReco/channel", "All selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); + // These channel and pT histograms are pre-truth-fiducial QA; MCReco/response is the validated numerator. + histos.add("MCReco/channel", "Pre-truth-fiducial QA: selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso}); - histos.add("MCReco/pt", "Reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCReco/pt", "Pre-truth-fiducial QA: reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho}); histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/response", "Selected MC candidates;reconstructed pT;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); @@ -272,6 +276,8 @@ struct K1AnalysisMicro { AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"}; histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis}); histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCGen/response", "Generated K1 parents;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); } } @@ -380,7 +386,8 @@ struct K1AnalysisMicro { // (or one mixed pair of collisions). The selection itself is the shared K1 core. // dTracks1: bachelor kaons, dTracks2: pions. template - void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + const K1ParentTruthMap* parentTruth = nullptr) { const bool fillQA = !IsMix && histogramOptions.additionalQAplots; const auto multiplicity = collision.cent(); @@ -474,9 +481,17 @@ struct K1AnalysisMicro { pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(), pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]); } - typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); - histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int motherId = k1MotherId(channel, kaon, pion1, pion2); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const auto* parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + const bool truthRapidity = c.isUnlikeSign && parent && (!histogramOptions.cUseTruthRapidity || (parent->y >= candidateCuts.cK1MinRap && parent->y <= candidateCuts.cK1MaxRap)); + if (truthRapidity) { + typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int parentCharge = parent->pdgCode > 0 ? 1 : -1; + histos.fill(HIST("MCReco/response"), c.k1.Pt(), parent->pt, channelBin, parentCharge, multiplicity); + } histos.fill(HIST("QAMC/K1OA"), c.angle); histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym); histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M()); @@ -515,17 +530,18 @@ struct K1AnalysisMicro { PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true); void processMC(ResoMCCols::iterator const& collision, - ResoMCTracks const& resotracks) + ResoMCTracks const& resotracks, ResoMCParents const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } histos.fill(HIST("MCReco/collisions"), 0.5); - fillHistograms(collision, resotracks, resotracks); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, resotracks, resotracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false); - void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { // The modular producer already selected these reconstructed collisions. // Apply precisely the same reconstruction loop as the data baseline. @@ -534,7 +550,8 @@ struct K1AnalysisMicro { } histos.fill(HIST("MCReco/collisions"), 0.5); histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); - fillHistograms(collision, tracks, tracks); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, tracks, tracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false); @@ -548,6 +565,9 @@ struct K1AnalysisMicro { const int charge = part.pdgCode() > 0 ? 1 : -1; histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast(channel)); histos.fill(HIST("MCGen/ptChannel"), static_cast(channel), part.pt()); + if (channel != K1TruthChannel::None) { + histos.fill(HIST("MCGen/response"), part.pt(), static_cast(channel), charge, collision.cent()); + } }); } PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false); diff --git a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx index 8b070a97be1..008984a0112 100644 --- a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx @@ -182,7 +182,7 @@ struct K1TrainingTable { template void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion, - K1TruthChannel channel, uint16_t passBits) + K1TruthChannel channel, uint16_t passBits, const K1ParentTruthMap* parentTruth = nullptr) { using o2::analysis::k1ml::BuildStatus; const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion)); @@ -215,14 +215,17 @@ struct K1TrainingTable { if constexpr (IsMC) { const bool matched = channel != K1TruthChannel::None; int motherId = -1; + const K1ParentTruth* parent = nullptr; if (matched) { - motherId = channel == K1TruthChannel::RhoK ? kaon.motherId() : std::abs(samePion.motherPDG()) == Pdg::kK1_1270Plus ? samePion.motherId() - : oppPion.motherId(); + motherId = k1MotherId(channel, kaon, samePion, oppPion); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; } // Immediate-mother information alone does not prove a physical UID. k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast(channel), - matched ? kaon.sign() * Pdg::kK1_1270Plus : 0, static_cast(motherId), - std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + parent ? parent->pdgCode : (matched ? kaon.sign() * Pdg::kK1_1270Plus : 0), static_cast(motherId), + parent ? static_cast(parent->pt) : std::numeric_limits::quiet_NaN(), + parent ? static_cast(parent->y) : std::numeric_limits::quiet_NaN()); } else { k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1}, std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); @@ -244,7 +247,7 @@ struct K1TrainingTable { } PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true); - void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { // The modular producer already selected these reconstructed collisions. if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { @@ -253,10 +256,11 @@ struct K1TrainingTable { histos.fill(HIST("MCReco/collisions"), 0.5); histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); writeK1MlEvent(collision); + const auto parentTruth = makeK1ParentTruthMap(resoParents); core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, - [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, - K1TruthChannel channel, uint16_t passBits) { - writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + [this, &parentTruth](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits, &parentTruth); }); } PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false); diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 74146abee60..ec08e498c86 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -14,10 +14,10 @@ /// \author Bong-Hwi Lim /// /// Two decay modes, analysed separately (never merged in any histogram or process function): -/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGenerated. +/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGen, processMCGenerated. /// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged). /// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track. -/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMixedMicro. +/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMCGen, processXi1530KMixedMicro. /// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls). /// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h. /// @@ -49,6 +49,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -56,6 +57,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -85,7 +87,7 @@ struct Omega2012Analysis { Configurable cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"}; // Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.") - o2::analysis::resonance::EventCuts eventCuts; + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); XiCuts xiCuts; K0sCuts k0sCuts; Xi1530KCuts xi1530KCuts; @@ -93,6 +95,35 @@ struct Omega2012Analysis { PionPidCuts pionPID; KaonPidCuts kaonPID; CandidateCuts candidateCuts; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require the matched Omega(2012) truth rapidity inside cfgRapidityCut"}; + Configurable cMCInjection{"cMCInjection", false, "Restrict MC numerator and denominator to Omega parents with generator status -11"}; + + struct OmegaParentTruth { + float pt = 0.f; + float y = 0.f; + int pdgCode = 0; + bool injected = false; + }; + using OmegaParentTruthMap = std::unordered_map; + + template + static bool isInjectedOmegaParent(const Parent& parent) + { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(parent.statusCode()) == InjectedGeneratorStatus; + } + + template + OmegaParentTruthMap omegaParentTruth(const Parents& parents) + { + OmegaParentTruthMap parentTruth; + for (const auto& parent : parents) { + if (std::abs(parent.pdgCode()) == kOmega2012Minus) { + parentTruth.emplace(parent.originalMcParticleId(), OmegaParentTruth{parent.pt(), parent.y(), parent.pdgCode(), isInjectedOmegaParent(parent)}); + } + } + return parentTruth; + } // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; @@ -217,6 +248,20 @@ struct Omega2012Analysis { histos.add("MC/hMCGenOmega2012Y", "MC Generated Omega(2012) rapidity", kTH1F, {rapidityAxis}); histos.add("MC/hMCRecOmega2012Pt", "MC Reconstructed Omega(2012) pT", kTH1F, {ptAxis}); histos.add("MC/hMCRecOmega2012PtEta", "MC Reconstructed Omega(2012) pT vs eta", kTH2F, {ptAxis, etaAxis}); + if (doprocessMCGen) { + histos.add("MC/hMCGenSelectedXiK0sPt", "MC generated Omega(2012) #rightarrow #Xi K^{0}_{S} in selected events and rapidity window;p_{T} (GeV/c)", kTH1F, {ptAxis}); + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCGenSelectedXiK0sResponse", "Selected-event generated #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessMC) { + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCRecXiK0sResponse", "Truth-matched selected #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } // MC truth invariant mass (from MC particles) histos.add("MC/hMCTruthInvMassXiK0s", "MC Truth Inv Mass Xi + K^{0}_{S}", kTH1F, {invMassAxis}); @@ -230,7 +275,7 @@ struct Omega2012Analysis { ProcessModes modes; modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC; - modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMCGen || doprocessXi1530KMixedMicro; modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; modes.mcReco = doprocessMC || doprocessXi1530KMCMicro; modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro; @@ -289,6 +334,17 @@ struct Omega2012Analysis { histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis}); histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis}); histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis}); + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCRecXi1530KResponse", "Truth-matched selected #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessXi1530KMCGen) { + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCGenSelectedXi1530KResponse", "Selected-event generated #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } } @@ -449,28 +505,71 @@ struct Omega2012Analysis { // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother void processMC(ResoMCCollisions::iterator const& collision, ResoMCCascades const& resocasc, - ResoMCV0s const& resov0s) + ResoMCV0s const& resov0s, + aod::ResoMCParents_001 const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - // No kinematic cut in the truth-matched spectra (as before the refactoring) + const auto omegaParents = omegaParentTruth(resoParents); auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) { if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) { return; } - if (!c.inRapidity) { + const int motherId = xi.motherId(); + const auto parentIt = omegaParents.find(motherId); + if (parentIt == omegaParents.end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!(std::abs(xi.motherRap()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(xi.motherRap()))) { + return; + } + if ((core.kinCutsEnabled() && !c.passesKinCut) || !c.inRapidity) { return; } histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt()); histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + const double sign = xi.motherPDG() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCRecXiK0sResponse"), c.omega.Pt(), xi.motherPt(), collision.cent(), collision.mcMultiplicity(), sign); }; core.forEachXiK0sCandidate(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate); } PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); + // Acceptance x efficiency denominator, counted per selected reconstructed collision, + // as in the Xi(1530) and Xi(1820) tasks. Split collisions repeat the parent set; + // this is not a unique-MC-event denominator or a signal-loss correction. + void processMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != kOmega2012Minus) { + continue; + } + if (!(std::abs(part.y()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(part.y())) { + continue; + } + if (cMCInjection && !isInjectedOmegaParent(part)) { + continue; + } + // Require the same immediate Xi + K0S channel as processMCGenerated. + if ((std::abs(part.daughterPDG1()) == kXiMinus && part.daughterPDG2() == kK0Short) || + (std::abs(part.daughterPDG2()) == kXiMinus && part.daughterPDG1() == kK0Short)) { + histos.fill(HIST("MC/hMCGenSelectedXiK0sPt"), part.pt()); + const double sign = part.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCGenSelectedXiK0sResponse"), part.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } + } + PROCESS_SWITCH(Omega2012Analysis, processMCGen, "Process MC generated XiK0s parents in selected events", false); + + // Inclusive MC-particle QA. The rapidity-limited source histogram is a legacy all-event QA denominator; + // selected-event efficiency denominators are filled by processMCGen and processXi1530KMCGen. void processMCGenerated(aod::McParticles const& mcParticles) { // Process MC generated particles (no reconstruction requirement) @@ -524,7 +623,11 @@ struct Omega2012Analysis { if ((std::abs(daughter1PDG) == kXiMinus && daughter2PDG == kK0Short) || (std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) { histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM); - histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + // Legacy all-event rapidity-limited QA histogram; selected-event efficiency + // denominators are written by processMCGen and processXi1530KMCGen. + if (std::abs(y) < candidateCuts.cfgRapidityCut.value) { + histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + } const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus; const auto& pXiTruth = isDaughter1Xi ? p1 : p2; @@ -538,7 +641,8 @@ struct Omega2012Analysis { // Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions template - void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds) + void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds, + const OmegaParentTruthMap* omegaParents = nullptr) { // Input QA: same event only (each object once per collision) auto onXi = [&](auto const& xi, bool selected) { @@ -623,6 +727,16 @@ struct Omega2012Analysis { if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) { return; } + if (!omegaParents) { + return; + } + const auto parentIt = omegaParents->find(kaon.motherId()); + if (parentIt == omegaParents->end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!std::isfinite(parentIt->second.y) || !(std::abs(parentIt->second.y) < candidateCuts.cfgRapidityCut.value))) { + return; + } histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt()); histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M()); @@ -630,6 +744,8 @@ struct Omega2012Analysis { histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt()); histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt()); histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt()); + const double sign = parentIt->second.pdgCode > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCRecXi1530KResponse"), c.omega.Pt(), parentIt->second.pt, cent, collision.mcMultiplicity(), sign); } }; @@ -661,15 +777,40 @@ struct Omega2012Analysis { void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision, ResoMCCascades const& resocasc, - ResoMCMicroTracks const& resomicrotracks) + ResoMCMicroTracks const& resomicrotracks, + aod::ResoMCParents_001 const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); + const auto omegaParents = omegaParentTruth(resoParents); + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr, &omegaParents); } PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false); + // Selected-event generated denominator for Omega -> Xi(1530) K, counted once + // per selected reconstructed collision. Split collisions repeat the MC parents. + void processXi1530KMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& parent : resoParents) { + if (std::abs(parent.pdgCode()) != kOmega2012Minus || + !std::isfinite(parent.y()) || !(std::abs(parent.y()) < candidateCuts.cfgRapidityCut.value) || + (cMCInjection && !isInjectedOmegaParent(parent))) { + continue; + } + if (classifyGeneratedOmega2012(parent.pdgCode(), parent.daughterPDG1(), parent.daughterPDG2()) != GeneratedChannel::Xi1530K) { + continue; + } + const double sign = parent.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCGenSelectedXi1530KResponse"), parent.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCGen, "Process generated Xi(1530) K parents in selected events", false); + // Mixed events: Xi from collision 1, pion and kaon from collision 2 void processXi1530KMixedMicro(ResoCollisions const& collisions, aod::ResoCascades const& resocasc, diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx index 3d2a397f6d6..d6c7eb317a3 100644 --- a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -17,9 +17,10 @@ /// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. MC truth of reconstructed MC: processMcTruth. /// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); /// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). -/// Only collisions with written candidates get an event row. In reconstructed MC the daughter labels of the written -/// objects and the pruned McParticles rows (Omega(2012) decay trees, ancestry of the labels) carry the truth, so the -/// output needs no source table. +/// Only collisions with written candidates get an event row. Generated Omega(2012) mothers are written separately to +/// OMMLGENAUDIT for every selected reconstructed MC collision, including collisions with no exported candidates. +/// In reconstructed MC, daughter labels of written objects and pruned McParticles rows carry candidate truth, so the +/// candidate output needs no source table. #include "PWGLF/Core/Omega2012AnalysisCore.h" #include "PWGLF/Core/Omega2012MlFeatures.h" @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -110,7 +112,7 @@ struct Omega2012TrainingTable { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Selection shared with the Omega(2012) histogram task (same JSON keys) - o2::analysis::resonance::EventCuts eventCuts; + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); XiCuts xiCuts; K0sCuts k0sCuts; Xi1530KCuts xi1530KCuts; @@ -123,6 +125,7 @@ struct Omega2012TrainingTable { Configurable omega2012MlLooseAudit{"omega2012MlLooseAudit", true, "Record the loose cut flow and mass/pT/activity spectrum per mode"}; Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + Configurable omega2012MlGenRapidityCut{"omega2012MlGenRapidityCut", 0.5f, "Generated-mother denominator rapidity limit; independent of candidate cfgRapidityCut"}; // Event row written before the first candidate of the collision struct PendingEvent { @@ -169,6 +172,9 @@ struct Omega2012TrainingTable { if (omega2012MlParityRows < 0) { LOG(fatal) << "omega2012MlParityRows must not be negative"; } + if (doprocessMCTrue && (!std::isfinite(omega2012MlGenRapidityCut.value) || omega2012MlGenRapidityCut.value <= 0.f)) { + LOG(fatal) << "omega2012MlGenRapidityCut must be finite and positive when processMCTrue is enabled"; + } if (omega2012MlExportStage.value != "loose" && omega2012MlExportStage.value != "selected") { LOG(fatal) << "omega2012MlExportStage must be loose or selected"; } @@ -268,7 +274,7 @@ struct Omega2012TrainingTable { return -1; } const auto row = trackId - static_cast(mcTrackLabelsDf->offset()); - if (row < 0 || row >= static_cast(mcTrackLabelsDf->size())) { + if (row < 0 || row >= mcTrackLabelsDf->size()) { return -1; } return mcTrackLabelsDf->rawIteratorAt(row).mcParticleId(); @@ -283,7 +289,7 @@ struct Omega2012TrainingTable { int64_t row = label; for (int step = 0; step <= MaxAncestorSteps; ++step) { const auto local = row - static_cast(mcParticlesDf->offset()); - if (row < 0 || local < 0 || local >= static_cast(mcParticlesDf->size())) { + if (row < 0 || local < 0 || local >= mcParticlesDf->size()) { return; } const auto particle = mcParticlesDf->rawIteratorAt(local); @@ -305,7 +311,7 @@ struct Omega2012TrainingTable { if (auto it = mlCascadeRows.find(id); it != mlCascadeRows.end()) { return it->second; } - const auto indices = xi.cascadeIndices(); + const auto& indices = xi.cascadeIndices(); const std::array daughterIds{indices[0], indices[1], indices[2]}; mlCascades(mlEventRow, id, xi.px(), xi.py(), xi.pz(), static_cast(xi.sign()), xi.mXi(), xi.mLambda(), xi.v0CosPA(), xi.cascCosPA(), xi.daughDCA(), xi.cascDaughDCA(), @@ -338,7 +344,7 @@ struct Omega2012TrainingTable { if (auto it = mlV0Rows.find(id); it != mlV0Rows.end()) { return it->second; } - const auto indices = v0.indices(); + const auto& indices = v0.indices(); const std::array daughterIds{indices[0], indices[1]}; mlV0s(mlEventRow, id, v0.px(), v0.py(), v0.pz(), v0.mK0Short(), v0.mLambda(), v0.mAntiLambda(), v0.v0CosPA(), v0.daughDCA(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), v0.transRadius(), @@ -538,7 +544,7 @@ struct Omega2012TrainingTable { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - core.forEachGeneratedOmega2012(histos, resoParents, [&](auto const& part, GeneratedChannel channel) { + core.forEachGeneratedOmega2012(histos, resoParents, omega2012MlGenRapidityCut.value, [&](auto const& part, GeneratedChannel channel) { mlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), part.pt(), part.y()); }); @@ -551,14 +557,14 @@ struct Omega2012TrainingTable { { mlAncestryRows.clear(); const auto offset = static_cast(mcParticles.offset()); - const auto nRows = static_cast(mcParticles.size()); + const auto nRows = mcParticles.size(); std::unordered_set treeRows; std::vector> stack; for (const auto& parent : mcParticles) { if (std::abs(parent.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { continue; } - stack.assign(1, {static_cast(parent.globalIndex()), 0}); + stack.assign(1, {parent.globalIndex(), 0}); while (!stack.empty()) { const auto [row, depth] = stack.back(); stack.pop_back(); From bf977d4de3070db0cc1f19b242c852985b5f3788 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Mon, 26 Oct 2026 09:13:29 +0100 Subject: [PATCH 436/648] [PWGCF] Add SE/ME split for single particles (#18455) --- PWGCF/Femto/Core/cascadeHistManager.h | 6 +- PWGCF/Femto/Core/charmHadronHistManager.h | 11 +- PWGCF/Femto/Core/kinkHistManager.h | 6 +- PWGCF/Femto/Core/mcParticleHistManager.h | 7 +- PWGCF/Femto/Core/pairBuilder.h | 427 +++++++++++------- PWGCF/Femto/Core/pairProcessHelpers.h | 35 +- PWGCF/Femto/Core/photonHistManager.h | 7 + PWGCF/Femto/Core/trackHistManager.h | 65 ++- PWGCF/Femto/Core/tripletBuilder.h | 300 +++++++----- PWGCF/Femto/Core/tripletProcessHelpers.h | 40 +- .../Core/twoPhotonResonanceHistManager.h | 7 + .../Femto/Core/twoTrackResonanceHistManager.h | 9 + PWGCF/Femto/Core/v0HistManager.h | 14 +- PWGCF/Femto/Tasks/femtoDndetaPair.cxx | 24 +- PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx | 33 +- PWGCF/Femto/Tasks/femtoPairD0D0.cxx | 18 +- .../Tasks/femtoPairMcParticleMcParticle.cxx | 6 +- PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 30 +- .../Femto/Tasks/femtoPairTrackCharmHadron.cxx | 27 +- PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 18 +- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 6 +- .../femtoPairTrackTwoPhotonResonance.cxx | 24 +- .../Tasks/femtoPairTrackTwoTrackResonance.cxx | 36 +- PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 24 +- .../Tasks/femtoPairV0TwoTrackResonance.cxx | 108 +++-- PWGCF/Femto/Tasks/femtoPairV0V0.cxx | 54 ++- .../femtoTrackTrackTwoPhotonResonanceQa.cxx | 4 +- ...oTripletMcParticleMcParticleMcParticle.cxx | 9 +- .../Tasks/femtoTripletTrackTrackCascade.cxx | 36 +- .../Tasks/femtoTripletTrackTrackTrack.cxx | 9 +- .../Femto/Tasks/femtoTripletTrackTrackV0.cxx | 15 +- .../Tasks/femtoPairLambdaAntilambda.cxx | 36 +- 32 files changed, 959 insertions(+), 492 deletions(-) diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index 9270b5b0cdc..6d83ee144fe 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -291,9 +291,11 @@ std::map> makeCascadeMcQaHistS #undef CASCADE_HIST_MC_QA_MAP constexpr char PrefixXiQa[] = "XiQA/"; -constexpr char PrefixXi[] = "Xi/"; +constexpr char PrefixXiSe[] = "Xi/SE/"; +constexpr char PrefixXiMe[] = "Xi/ME/"; constexpr char PrefixOmegaQa[] = "OmegaQa/"; -constexpr char PrefixOmega[] = "Omega/"; +constexpr char PrefixOmegaSe[] = "Omega/SE/"; +constexpr char PrefixOmegaMe[] = "Omega/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; diff --git a/PWGCF/Femto/Core/charmHadronHistManager.h b/PWGCF/Femto/Core/charmHadronHistManager.h index e0391aad0ae..b405f028d6b 100644 --- a/PWGCF/Femto/Core/charmHadronHistManager.h +++ b/PWGCF/Femto/Core/charmHadronHistManager.h @@ -291,10 +291,15 @@ auto makeLcMcQaHistSpecMap(const T1& confBinningAnalysis, const T2& confBinningQ #undef LC_HIST_QA_MAP // prefixes for the output directories in the histogram registry -constexpr char PrefixD01[] = "D01/"; -constexpr char PrefixD02[] = "D02/"; +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixD01Se[] = "D01/SE/"; +constexpr char PrefixD02Se[] = "D02/SE/"; +constexpr char PrefixD01Me[] = "D01/ME/"; +constexpr char PrefixD02Me[] = "D02/ME/"; constexpr char PrefixD0Qa[] = "D0QA/"; -constexpr char PrefixLc1[] = "Lc1/"; +constexpr char PrefixLc1Se[] = "Lc1/SE/"; +constexpr char PrefixLc1Me[] = "Lc1/ME/"; constexpr char PrefixLcQa[] = "LcQA/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view McDir = "MC/"; diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index 7bb73ebad68..abebfda31f4 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -255,10 +255,12 @@ std::map> makeKinkMcQaHistSpecMap #undef KINK_HIST_MC_QA_MAP constexpr char PrefixSigmaQa[] = "SigmaQA/"; -constexpr char PrefixSigma1[] = "Sigma1/"; +constexpr char PrefixSigma1Se[] = "Sigma1/SE/"; +constexpr char PrefixSigma1Me[] = "Sigma1/ME/"; constexpr char PrefixSigma2[] = "Sigma2/"; constexpr char PrefixSigmaPlusQa[] = "SigmaPlusQA/"; -constexpr char PrefixSigmaPlus1[] = "SigmaPlus1/"; +constexpr char PrefixSigmaPlus1Se[] = "SigmaPlus1/SE/"; +constexpr char PrefixSigmaPlus1Me[] = "SigmaPlus1/ME/"; constexpr char PrefixSigmaPlus2[] = "SigmaPlus2/"; constexpr std::string_view AnalysisDir = "Analysis/"; diff --git a/PWGCF/Femto/Core/mcParticleHistManager.h b/PWGCF/Femto/Core/mcParticleHistManager.h index 7b5ff24eee2..ce9c8bd4bed 100644 --- a/PWGCF/Femto/Core/mcParticleHistManager.h +++ b/PWGCF/Femto/Core/mcParticleHistManager.h @@ -146,7 +146,12 @@ auto makeMcParticleHistSpecMap(const T& confBinning) inline constexpr char PrefixMcParticle1[] = "McParticle1/"; inline constexpr char PrefixMcParticle2[] = "McParticle2/"; -inline constexpr char PrefixMcParticle3[] = "McParticle3/"; +inline constexpr char PrefixMcParticle1Se[] = "McParticle1Se/"; +inline constexpr char PrefixMcParticle2Se[] = "McParticle2Se/"; +inline constexpr char PrefixMcParticle3Se[] = "McParticle3Se/"; +inline constexpr char PrefixMcParticle1Me[] = "McParticle1Me/"; +inline constexpr char PrefixMcParticle2Me[] = "McParticle2Me/"; +inline constexpr char PrefixMcParticle3Me[] = "McParticle3Me/"; constexpr std::string_view McDir = "MC/"; diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 952c9734a97..dd157bbb0ae 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -52,7 +52,7 @@ namespace o2::analysis::femto::pairbuilder const int64_t nLimitPartitionIdenticalParticles = 2; const int64_t nLimitPartitionParticles = 1; -template +template class PairTrackTrackBuilder { public: @@ -106,7 +106,8 @@ class PairTrackTrackBuilder if (mSameSpecies) { mTrackCleaner1.init(confTrackCleaner1); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -118,8 +119,10 @@ class PairTrackTrackBuilder } else { mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); @@ -165,7 +168,7 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -175,7 +178,7 @@ class PairTrackTrackBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -194,7 +197,7 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -204,7 +207,7 @@ class PairTrackTrackBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); } template @@ -214,17 +217,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -235,17 +238,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -262,17 +265,17 @@ class PairTrackTrackBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -283,17 +286,17 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): if constexpr (utils::HasEventShape) { - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); } else { LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; } @@ -306,8 +309,12 @@ class PairTrackTrackBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + // single-particle histograms for particles that end up in mixed-event pairs -- same config + // objects as the same-event managers above, just a different histogram directory + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackTrack mCprSe; @@ -331,6 +338,12 @@ template (registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mV0HistManagerSe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mV0HistManagerMe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); if (mSameSpecies) { mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection1.pdgCodeAbs.value); @@ -424,7 +438,8 @@ class PairV0V0Builder mPairHistManagerMe.setCharge(1, 1); mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); } else { - mV0HistManager2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mV0HistManagerSe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mV0HistManagerMe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -469,7 +484,7 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -479,7 +494,7 @@ class PairV0V0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -498,7 +513,7 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); } auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -508,7 +523,7 @@ class PairV0V0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); } template @@ -518,13 +533,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -532,13 +547,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -552,13 +567,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -566,13 +581,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -582,8 +597,10 @@ class PairV0V0Builder private: colhistmanager::CollisionHistManager mColHistManager; - v0histmanager::V0HistManager mV0HistManager1; - v0histmanager::V0HistManager mV0HistManager2; + v0histmanager::V0HistManager mV0HistManagerSe1; + v0histmanager::V0HistManager mV0HistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerMe1; + v0histmanager::V0HistManager mV0HistManagerMe2; particlecleaner::ParticleCleaner mV0Cleaner1; particlecleaner::ParticleCleaner mV0Cleaner2; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -608,6 +625,12 @@ template (registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mD0HistManagerSe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mD0HistManagerMe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); auto prongPdgCodes = [](int sign) -> std::pair { if (sign > 0) { @@ -713,7 +737,8 @@ class PairD0D0Builder mPairHistManagerMe.setCharge(1, 1); mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); } else { - mD0HistManager2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mD0HistManagerSe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mD0HistManagerMe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); auto [posDauPdg2, negDauPdg2] = prongPdgCodes(confD0Selection2.sign.value); mPairHistManagerSe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection2.pdgCodeAbs.value, posDauPdg2, negDauPdg2); @@ -758,7 +783,7 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManager1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -768,7 +793,7 @@ class PairD0D0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc @@ -787,7 +812,7 @@ class PairD0D0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); } auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -796,7 +821,7 @@ class PairD0D0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManager1, mD0HistManager2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); } template @@ -805,13 +830,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -819,13 +844,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -839,13 +864,13 @@ class PairD0D0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -853,13 +878,13 @@ class PairD0D0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -869,8 +894,10 @@ class PairD0D0Builder private: colhistmanager::CollisionHistManager mColHistManager; - charmhadronhistmanager::CharmHadronHistManager mD0HistManager1; // the third prong prefix is unused for 2-prong candidates - charmhadronhistmanager::CharmHadronHistManager mD0HistManager2; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe2; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe2; // the third prong prefix is unused for 2-prong candidates particlecleaner::ParticleCleaner mD0Cleaner1; particlecleaner::ParticleCleaner mD0Cleaner2; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -893,6 +920,10 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mD0HistManager.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerSe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerMe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mD0Cleaner.init(confD0Cleaner); @@ -992,7 +1025,7 @@ class PairTrackD0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1000,13 +1033,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1024,7 +1057,7 @@ class PairTrackD0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mD0HistManager, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); } template @@ -1032,13 +1065,13 @@ class PairTrackD0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1047,8 +1080,10 @@ class PairTrackD0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - charmhadronhistmanager::CharmHadronHistManager mD0HistManager; // the third prong prefix is unused for 2-prong candidates + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe; // the third prong prefix is unused for 2-prong candidates + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe; // the third prong prefix is unused for 2-prong candidates particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mD0Cleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1067,6 +1102,11 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mLcHistManager.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerSe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerMe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); mTrackCleaner.init(confTrackCleaner); mLcCleaner.init(confLcCleaner); @@ -1152,7 +1194,7 @@ class PairTrackLcBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1160,13 +1202,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1184,7 +1226,7 @@ class PairTrackLcBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mLcHistManager, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); } template @@ -1192,13 +1234,13 @@ class PairTrackLcBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1207,8 +1249,10 @@ class PairTrackLcBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - charmhadronhistmanager::CharmHadronHistManager mLcHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mLcCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1226,6 +1270,10 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mV0HistManager.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mV0Cleaner.init(confV0Cleaner); @@ -1327,7 +1377,7 @@ class PairTrackV0Builder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1341,7 +1391,7 @@ class PairTrackV0Builder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); } template @@ -1349,13 +1399,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1367,13 +1417,13 @@ class PairTrackV0Builder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1382,8 +1432,10 @@ class PairTrackV0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - v0histmanager::V0HistManager mV0HistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + v0histmanager::V0HistManager mV0HistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mV0Cleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1406,6 +1458,10 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mResonanceHistManager.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -1486,7 +1544,7 @@ class PairTrackTwoPhotonResonanceBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, resonanceSlice, photonTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, resonanceSlice, photonTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1494,13 +1552,13 @@ class PairTrackTwoPhotonResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1509,8 +1567,10 @@ class PairTrackTwoPhotonResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprSe; @@ -1526,6 +1586,10 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mResonanceHistManager.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -1605,7 +1671,7 @@ class PairTrackTwoTrackResonanceBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1613,13 +1679,13 @@ class PairTrackTwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1628,8 +1694,10 @@ class PairTrackTwoTrackResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackV0 mCprSe; // cpr for twotrackresonances and v0 work the same way @@ -1647,6 +1715,12 @@ template (registry, colHistSpec, confCollisionBinning); - mV0HistManager.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); - mResonanceHistManager.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confV0Selection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -1737,7 +1813,7 @@ class PairV0TwoTrackResonanceBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -1745,13 +1821,13 @@ class PairV0TwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1760,8 +1836,10 @@ class PairV0TwoTrackResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - v0histmanager::V0HistManager mV0HistManager; - twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManager; + v0histmanager::V0HistManager mV0HistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionV0V0 mCprSe; // cpr for twotrackresonances and v0 work the same way @@ -1776,6 +1854,9 @@ class PairV0TwoTrackResonanceBuilder template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mKinkHistManager.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerSe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerMe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); mTrackCleaner.init(confTrackCleaner); mKinkCleaner.init(confKinkCleaner); @@ -1860,7 +1943,7 @@ class PairTrackKinkBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -1874,7 +1957,7 @@ class PairTrackKinkBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); } template @@ -1882,13 +1965,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1900,13 +1983,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1915,8 +1998,10 @@ class PairTrackKinkBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - kinkhistmanager::KinkHistManager mKinkHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + kinkhistmanager::KinkHistManager mKinkHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + kinkhistmanager::KinkHistManager mKinkHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mKinkCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -1935,6 +2020,11 @@ template (registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mCascadeCleaner.init(confCascadeCleaner); @@ -2029,7 +2121,7 @@ class PairTrackCascadeBuilder } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } /// \return true if at least one pair passed all pair selections (usable as pair trigger) @@ -2043,7 +2135,7 @@ class PairTrackCascadeBuilder } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); } template @@ -2051,13 +2143,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2069,13 +2161,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2084,8 +2176,10 @@ class PairTrackCascadeBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - cascadehistmanager::CascadeHistManager mCascadeHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mCascadeCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; @@ -2099,7 +2193,7 @@ class PairTrackCascadeBuilder int mEventStride = 1; }; -template +template class PairMcParticleMcParticleBuilder { public: @@ -2157,7 +2251,8 @@ class PairMcParticleMcParticleBuilder if (mSameSpecies) { mMcParticleCleaner1.init(confMcParticleCleaner1); - mMcParticleHistManager1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -2169,8 +2264,10 @@ class PairMcParticleMcParticleBuilder } else { mMcParticleCleaner1.init(confMcParticleCleaner1); mMcParticleCleaner2.init(confMcParticleCleaner2); - mMcParticleHistManager1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); - mMcParticleHistManager2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -2216,7 +2313,7 @@ class PairMcParticleMcParticleBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); + return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); } auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); @@ -2225,7 +2322,7 @@ class PairMcParticleMcParticleBuilder return false; } mColHistManager.template fill(col); - return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManager1, mMcParticleHistManager2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); + return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); } template @@ -2234,13 +2331,13 @@ class PairMcParticleMcParticleBuilder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2248,13 +2345,13 @@ class PairMcParticleMcParticleBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -2264,8 +2361,10 @@ class PairMcParticleMcParticleBuilder private: colhistmanager::CollisionHistManager mColHistManager; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager1; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionMcParticleMcParticle mCprSe; diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 64a4c510b27..8e885dd6d6b 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -429,7 +429,9 @@ template + typename T10, + typename T11, + typename T12> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -440,7 +442,9 @@ void processMixedEvent(T1 const& Collisions, T8& PairHistManager, T9& CprManager, T10& PcManager, - int eventStride = 1) + int eventStride, + T11& ParticleHistManagerMe1, + T12& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; @@ -518,6 +522,11 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event + // pair -- filled once per accepted pair (not deduplicated), so a particle contributes + // here exactly as often as it actually contributes to the mixed-event pair sample. + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); } } @@ -551,7 +560,9 @@ template + typename T16, + typename T17, + typename T18> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -568,7 +579,9 @@ void processMixedEvent(T1 const& Collisions, T14& ParticleCleaner2, T15& CprManager, T16& PcManager, - int eventStride = 1) + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; @@ -650,6 +663,9 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); } } @@ -679,7 +695,9 @@ template + typename T14, + typename T15, + typename T16> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -694,7 +712,9 @@ void processMixedEvent(T1 const& Collisions, T12& ParticleCleaner2, T13& CprManager, T14& PcManager, - int eventStride = 1) + int eventStride, + T15& ParticleHistManagerMe1, + T16& ParticleHistManagerMe2) { int64_t lastCollisionIndex = -1; int windowSizeRaw = 0; @@ -766,6 +786,9 @@ void processMixedEvent(T1 const& Collisions, hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); } } diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h index 23b8688dfa5..5ffe1e390fb 100644 --- a/PWGCF/Femto/Core/photonHistManager.h +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -164,8 +164,15 @@ std::map> makePhotonQaHistSpecM #undef PHOTON_HIST_QA_MAP constexpr char PrefixPhotonQa[] = "PhotonQA/"; +// used only by the standalone QA task constexpr char PrefixTwoPhotonResonanceDau1Qa[] = "TwoPhotonResonanceDau1Qa/"; constexpr char PrefixTwoPhotonResonanceDau2Qa[] = "TwoPhotonResonanceDau2Qa/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Se[] = "TwoPhotonResonanceDau1Se/"; +constexpr char PrefixTwoPhotonResonanceDau2Se[] = "TwoPhotonResonanceDau2Se/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Me[] = "TwoPhotonResonanceDau1Me/"; +constexpr char PrefixTwoPhotonResonanceDau2Me[] = "TwoPhotonResonanceDau2Me/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 36cae95349b..634512c8666 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -558,47 +558,72 @@ auto makeTrackMcQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinni #undef TRACK_HIST_MC_QA_MAP constexpr char PrefixTrackQa[] = "TrackQA/"; -constexpr char PrefixTrack1[] = "Track1/"; -constexpr char PrefixTrack2[] = "Track2/"; +// same-event / mixed-event siblings: single-particle histograms for a given track slot, split by +// whether the particle came from a same-event or mixed-event pair +constexpr char PrefixTrack1Se[] = "Track1/SE/"; +constexpr char PrefixTrack2Se[] = "Track2/SE/"; +constexpr char PrefixTrack3Se[] = "Track3/SE/"; +constexpr char PrefixTrack1Me[] = "Track1/ME/"; +constexpr char PrefixTrack2Me[] = "Track2/ME/"; +constexpr char PrefixTrack3Me[] = "Track3/ME/"; // for dN/deta tracks constexpr char PrefixDndetaTrackGlobal[] = "DndetaTrackGlobal/"; constexpr char PrefixDndetaTrackItsOnly[] = "DndetaTrackItsOnly/"; -constexpr char PrefixTrack3[] = "Track3/"; -constexpr char PrefixResonancePosDaughter[] = "ResonancePosDau/"; -constexpr char PrefixResonanceNegDaughter[] = "ResonanceNegDau/"; +// daughter-track prefixes below: Se/Me siblings for pair-builder single-particle histograms +// (split by whether the daughter came from a same-event or mixed-event candidate), plus the +// original, unsplit Qa variants used by standalone QA tasks (left untouched). +constexpr char PrefixResonancePosDaughterSe[] = "ResonancePosDau/SE/"; +constexpr char PrefixResonanceNegDaughterSe[] = "ResonanceNegDau/SE/"; +constexpr char PrefixResonancePosDaughterMe[] = "ResonancePosDau/ME/"; +constexpr char PrefixResonanceNegDaughterMe[] = "ResonanceNegDau/ME/"; constexpr char PrefixResonancePosDaughterQa[] = "ResonancePosDauQa/"; constexpr char PrefixResonanceNegDaughterQa[] = "ResonanceNegDauQa/"; -constexpr char PrefixD01PosDaughter[] = "D01PosDau/"; -constexpr char PrefixD01NegDaughter[] = "D01NegDau/"; -constexpr char PrefixD02PosDaughter[] = "D02PosDau/"; -constexpr char PrefixD02NegDaughter[] = "D02NegDau/"; +constexpr char PrefixD01PosDaughterSe[] = "D01PosDau/SE/"; +constexpr char PrefixD01NegDaughterSe[] = "D01NegDau/SE/"; +constexpr char PrefixD02PosDaughterSe[] = "D02PosDau/SE/"; +constexpr char PrefixD02NegDaughterSe[] = "D02NegDau/SE/"; +constexpr char PrefixD01PosDaughterMe[] = "D01PosDau/ME/"; +constexpr char PrefixD01NegDaughterMe[] = "D01NegDau/ME/"; +constexpr char PrefixD02PosDaughterMe[] = "D02PosDau/ME/"; +constexpr char PrefixD02NegDaughterMe[] = "D02NegDau/ME/"; constexpr char PrefixD01PosDaughterQa[] = "D01PosDauQa/"; constexpr char PrefixD01NegDaughterQa[] = "D01NegDauQa/"; -constexpr char PrefixLc1ProtonDaughter[] = "Lc1ProtonDau/"; -constexpr char PrefixLc1KaonDaughter[] = "Lc1KaonDau/"; -constexpr char PrefixLc1PionDaughter[] = "Lc1PionDau/"; +constexpr char PrefixLc1ProtonDaughterSe[] = "Lc1ProtonDau/SE/"; +constexpr char PrefixLc1KaonDaughterSe[] = "Lc1KaonDau/SE/"; +constexpr char PrefixLc1PionDaughterSe[] = "Lc1PionDau/SE/"; +constexpr char PrefixLc1ProtonDaughterMe[] = "Lc1ProtonDau/ME/"; +constexpr char PrefixLc1KaonDaughterMe[] = "Lc1KaonDau/ME/"; +constexpr char PrefixLc1PionDaughterMe[] = "Lc1PionDau/ME/"; constexpr char PrefixLc1ProtonDaughterQa[] = "Lc1ProtonDauQa/"; constexpr char PrefixLc1KaonDaughterQa[] = "Lc1KaonDauQa/"; constexpr char PrefixLc1PionDaughterQa[] = "Lc1PionDauQa/"; -constexpr char PrefixV01PosDaughter[] = "V01PosDau/"; -constexpr char PrefixV01NegDaughter[] = "V01NegDau/"; -constexpr char PrefixV02PosDaughter[] = "V02PosDau/"; -constexpr char PrefixV02NegDaughter[] = "V02NegDau/"; +constexpr char PrefixV01PosDaughterSe[] = "V01PosDau/SE/"; +constexpr char PrefixV01NegDaughterSe[] = "V01NegDau/SE/"; +constexpr char PrefixV02PosDaughterSe[] = "V02PosDau/SE/"; +constexpr char PrefixV02NegDaughterSe[] = "V02NegDau/SE/"; +constexpr char PrefixV01PosDaughterMe[] = "V01PosDau/ME/"; +constexpr char PrefixV01NegDaughterMe[] = "V01NegDau/ME/"; +constexpr char PrefixV02PosDaughterMe[] = "V02PosDau/ME/"; +constexpr char PrefixV02NegDaughterMe[] = "V02NegDau/ME/"; constexpr char PrefixV0PosDaughterQa[] = "V0PosDauQa/"; constexpr char PrefixV0NegDaughterQa[] = "V0NegDauQa/"; -constexpr char PrefixCascadePosDaughter[] = "CascadePosDau/"; -constexpr char PrefixCascadeNegDaughter[] = "CascadeNegDau/"; -constexpr char PrefixCascadeBachelor[] = "CascadeBachelor/"; +constexpr char PrefixCascadePosDaughterSe[] = "CascadePosDau/SE/"; +constexpr char PrefixCascadeNegDaughterSe[] = "CascadeNegDau/SE/"; +constexpr char PrefixCascadeBachelorSe[] = "CascadeBachelor/SE/"; +constexpr char PrefixCascadePosDaughterMe[] = "CascadePosDau/ME/"; +constexpr char PrefixCascadeNegDaughterMe[] = "CascadeNegDau/ME/"; +constexpr char PrefixCascadeBachelorMe[] = "CascadeBachelor/ME/"; constexpr char PrefixCascadePosDaughterQa[] = "CascadePosDauQa/"; constexpr char PrefixCascadeNegDaughterQa[] = "CascadeNegDauQa/"; constexpr char PrefixCascadeBachelorQa[] = "CascadeBachelorQa/"; -constexpr char PrefixKinkChaDaughter[] = "KinkChaDau/"; +constexpr char PrefixKinkChaDaughterSe[] = "KinkChaDau/SE/"; +constexpr char PrefixKinkChaDaughterMe[] = "KinkChaDau/ME/"; constexpr char PrefixKinkChaDaughterQa[] = "KinkChaDauQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index 1d6fbe76e9d..8a39f818c8b 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -49,6 +49,9 @@ const int64_t nLimitPartitionParticles = 1; template (registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -134,8 +138,10 @@ class TripletTrackTrackTrackBuilder // Track1 & Track2 & are the same particle species and track 3 is something else mCleaner1.init(confCleaner1); mCleaner3.init(confCleaner3); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -149,9 +155,12 @@ class TripletTrackTrackTrackBuilder mCleaner1.init(confCleaner1); mCleaner2.init(confCleaner2); mCleaner3.init(confCleaner3); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -202,7 +211,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } if (mTrack1Track2AreSameSpecies) { @@ -216,7 +225,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -227,7 +236,7 @@ class TripletTrackTrackTrackBuilder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -246,7 +255,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); } if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -259,7 +268,7 @@ class TripletTrackTrackTrackBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -270,7 +279,7 @@ class TripletTrackTrackTrackBuilder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); } // data @@ -280,13 +289,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -294,13 +303,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -308,13 +317,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -329,13 +338,13 @@ class TripletTrackTrackTrackBuilder if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -343,13 +352,13 @@ class TripletTrackTrackTrackBuilder } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -357,13 +366,13 @@ class TripletTrackTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -373,9 +382,12 @@ class TripletTrackTrackTrackBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - trackhistmanager::TrackHistManager mTrackHistManager3; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + trackhistmanager::TrackHistManager mTrackHistManagerSe3; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + trackhistmanager::TrackHistManager mTrackHistManagerMe3; particlecleaner::ParticleCleaner mCleaner1; particlecleaner::ParticleCleaner mCleaner2; particlecleaner::ParticleCleaner mCleaner3; @@ -402,6 +414,11 @@ template (registry, trackHistSpec1, confTrackSelection1); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); @@ -489,9 +508,12 @@ class TripletTrackTrackV0Builder // all three tracks are different mCleaner1.init(confCleaner1); mCleaner2.init(confCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); @@ -538,7 +560,7 @@ class TripletTrackTrackV0Builder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -549,7 +571,7 @@ class TripletTrackTrackV0Builder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -569,7 +591,7 @@ class TripletTrackTrackV0Builder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -580,7 +602,7 @@ class TripletTrackTrackV0Builder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); } // data @@ -590,13 +612,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -604,13 +626,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -625,13 +647,13 @@ class TripletTrackTrackV0Builder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -639,13 +661,13 @@ class TripletTrackTrackV0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -655,9 +677,12 @@ class TripletTrackTrackV0Builder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - v0histmanager::V0HistManager mV0HistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + v0histmanager::V0HistManager mV0HistManagerMe; particlecleaner::ParticleCleaner mCleaner1; particlecleaner::ParticleCleaner mCleaner2; particlecleaner::ParticleCleaner mV0Cleaner; @@ -684,6 +709,12 @@ template (registry, trackHistSpec1, confTrackSelection1); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); mCtrSe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -782,9 +815,12 @@ class TripletTrackTrackCascadeBuilder // all three tracks are different mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); @@ -830,7 +866,7 @@ class TripletTrackTrackCascadeBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -841,7 +877,7 @@ class TripletTrackTrackCascadeBuilder } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc @@ -861,7 +897,7 @@ class TripletTrackTrackCascadeBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); } auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); @@ -872,7 +908,7 @@ class TripletTrackTrackCascadeBuilder } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mCascadeHistManager, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); } // data @@ -882,13 +918,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -896,13 +932,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -917,13 +953,13 @@ class TripletTrackTrackCascadeBuilder if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -931,13 +967,13 @@ class TripletTrackTrackCascadeBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policy specifed. Breaking..."; @@ -947,9 +983,12 @@ class TripletTrackTrackCascadeBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - cascadehistmanager::CascadeHistManager mCascadeHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner1; particlecleaner::ParticleCleaner mTrackCleaner2; particlecleaner::ParticleCleaner mCascadeCleaner; @@ -974,6 +1013,9 @@ class TripletTrackTrackCascadeBuilder template (mDist(mRng)); } - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mTripletHistManagerSe, mMcParticleCleaner1, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mTripletHistManagerSe, mMcParticleCleaner1, mCtrSe, mTcSe, tripletOrder); } if (mParticle1Particle2AreSameSpecies) { @@ -1104,7 +1152,7 @@ class TripletMcParticleMcParticleMcParticleBuilder if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mMcParticleHistManager3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); } auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); @@ -1114,38 +1162,68 @@ class TripletMcParticleMcParticleMcParticleBuilder return false; } mColHistManager.template fill(mcCol); - return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice2, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManager1, mMcParticleHistManager2, mMcParticleHistManager3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrSe, mTcSe); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice2, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrSe, mTcSe); } template void processMixedEvent(T1 const& mcCols, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& partition3, T8& cache, T9& binsVtxMult, T10& binsVtxCent, T11& binsVtxMultCent) { - // for identical species the same partition is used several times - auto& p2 = (mParticle1Particle2Particle3AreSameSpecies || mParticle1Particle2AreSameSpecies) ? partition1 : partition2; - auto& p3 = mParticle1Particle2Particle3AreSameSpecies ? partition1 : partition3; - auto& cleaner2 = (mParticle1Particle2Particle3AreSameSpecies || mParticle1Particle2AreSameSpecies) ? mMcParticleCleaner1 : mMcParticleCleaner2; - auto& cleaner3 = mParticle1Particle2Particle3AreSameSpecies ? mMcParticleCleaner1 : mMcParticleCleaner3; - - switch (mMixingPolicy) { - case triplethistmanager::MixingPolicy::kVtxMult: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); - break; - case triplethistmanager::MixingPolicy::kVtxCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); - break; - case triplethistmanager::MixingPolicy::kVtxMultCent: - tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, p2, p3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, cleaner2, cleaner3, mCtrMe, mTcMe, mEventStride); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + // mMcParticleHistManagerMeN are distinct types (templated on a per-slot prefix), so -- unlike + // the partitions/cleaners above, which share a single concrete type and can be selected via a + // ternary reference -- the histogram managers must be selected via explicit branching instead + if (mParticle1Particle2Particle3AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else if (mParticle1Particle2AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } private: colhistmanager::CollisionHistManager mColHistManager; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager1; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager2; - mcparticlehistmanager::McParticleHistManager mMcParticleHistManager3; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe3; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe3; triplethistmanager::TripletHistManager mTripletHistManagerSe; triplethistmanager::TripletHistManager mTripletHistManagerMe; closetripletrejection::CloseTripletRejectionMcParticleMcParticleMcParticle mCtrSe; diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index 07f74758d1c..df75ace9ef9 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -754,7 +754,10 @@ template + typename T11, + typename T12, + typename T13, + typename T14> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -766,7 +769,10 @@ void processMixedEvent(T1 const& Collisions, T9& TripletHistManager, T10& CtrManager, T11& TcManager, - int eventStride = 1) + int eventStride, + T12& ParticleHistManagerMe1, + T13& ParticleHistManagerMe2, + T14& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; @@ -855,6 +861,10 @@ void processMixedEvent(T1 const& Collisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } @@ -890,7 +900,10 @@ template + typename T18, + typename T19, + typename T20, + typename T21> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -909,7 +922,10 @@ void processMixedEvent(T1 const& Collisions, T16& Cleaner3, T17& CtrManager, T18& TcManager, - int eventStride = 1) + int eventStride, + T19& ParticleHistManagerMe1, + T20& ParticleHistManagerMe2, + T21& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; @@ -1005,6 +1021,10 @@ void processMixedEvent(T1 const& Collisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } @@ -1025,7 +1045,8 @@ void processMixedEvent(T1 const& Collisions, // mc collisions carry no magnetic field, so there is no magnetic field check here template + typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, + typename T17, typename T18, typename T19> void processMixedEventMcTruth(T1 const& McCollisions, T2& Partition1, T3& Partition2, @@ -1042,7 +1063,10 @@ void processMixedEventMcTruth(T1 const& McCollisions, T14& ParticleCleaner3, T15& CtrManager, T16& TcManager, - int eventStride = 1) + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2, + T19& ParticleHistManagerMe3) { int64_t lastCollisionIndex1 = -1; int64_t lastCollisionIndex2 = -1; @@ -1125,6 +1149,10 @@ void processMixedEventMcTruth(T1 const& McCollisions, hasValidTriplet = true; TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); + ParticleHistManagerMe3.fill(p3, mcMothers, mcPartonicMothers); } } diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h index 834317c1b03..cfb71e8eb37 100644 --- a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -109,8 +109,15 @@ auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) #undef TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP #undef TWOPHOTONRESONANCE_HIST_QA_MAP +// used only by the standalone QA task constexpr char PrefixPi0[] = "Pi0/"; constexpr char PrefixEta[] = "Eta/"; +// same-event sibling, used only by a pair builder's same-event resonance manager +constexpr char PrefixPi0Se[] = "Pi0Se/"; +constexpr char PrefixEtaSe[] = "EtaSe/"; +// mixed-event sibling, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixPi0Me[] = "Pi0Me/"; +constexpr char PrefixEtaMe[] = "EtaMe/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h index 1f9149ddc26..2cc046e92cc 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -122,9 +122,18 @@ auto makeTwoTrackResonanceQaHistSpecMap(const T& confBinningAnalysis) #undef TWOTRACKRESONANCE_HIST_ANALYSIS_MAP #undef TWOTRACKRESONANCE_HIST_QA_MAP +// used only by the standalone QA task constexpr char PrefixRho[] = "Rho0/"; constexpr char PrefixPhi[] = "Phi/"; constexpr char PrefixKstar[] = "Kstar0/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixRhoSe[] = "Rho0Se/"; +constexpr char PrefixPhiSe[] = "PhiSe/"; +constexpr char PrefixKstarSe[] = "Kstar0Se/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixRhoMe[] = "Rho0Me/"; +constexpr char PrefixPhiMe[] = "PhiMe/"; +constexpr char PrefixKstarMe[] = "Kstar0Me/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index d5e8848e434..6d85ccd6747 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -309,11 +309,17 @@ std::map> makeV0McQaHistSpecMap(T1 #undef V0_HIST_MC_QA_MAP constexpr char PrefixLambdaQa[] = "LambdaQA/"; -constexpr char PrefixLambda1[] = "Lambda1/"; -constexpr char PrefixLambda2[] = "Lambda2/"; +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixLambda1Se[] = "Lambda1/SE/"; +constexpr char PrefixLambda2Se[] = "Lambda2/SE/"; +constexpr char PrefixLambda1Me[] = "Lambda1/ME/"; +constexpr char PrefixLambda2Me[] = "Lambda2/ME/"; constexpr char PrefixK0shortQa[] = "K0shortQa/"; -constexpr char PrefixK0short1[] = "K0short1/"; -constexpr char PrefixK0short2[] = "K0short2/"; +constexpr char PrefixK0short1Se[] = "K0short1/SE/"; +constexpr char PrefixK0short2Se[] = "K0short2/SE/"; +constexpr char PrefixK0short1Me[] = "K0short1/ME/"; +constexpr char PrefixK0short2Me[] = "K0short2/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; diff --git a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx index 922e5f59a6e..df35e04f725 100644 --- a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx +++ b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx @@ -170,8 +170,10 @@ struct FemtoDndetaPair { closepairrejection::ConfCprTrackV0Daughter confCprTrackV0; pairbuilder::PairTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, pairhistmanager::PrefixTrackTrackSe, pairhistmanager::PrefixTrackTrackMe, closepairrejection::PrefixTrackTrackSe, @@ -179,10 +181,14 @@ struct FemtoDndetaPair { pairTrackTrackBuilder; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -191,8 +197,10 @@ struct FemtoDndetaPair { pairTrackLambdaBuilder; pairbuilder::PairMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, pairhistmanager::PrefixMcParticleMcParticleSe, pairhistmanager::PrefixMcParticleMcParticleMe, closepairrejection::PrefixMcParticleMcParticleSe, diff --git a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx index 4be3c0f0751..44e830e1aed 100644 --- a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx +++ b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx @@ -183,9 +183,12 @@ struct FemtoDndetaTriplet { closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtrMcParticleMcParticleMcParticle; // generator-level trigger, no cut by default tripletbuilder::TripletTrackTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - trackhistmanager::PrefixTrack3, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, triplethistmanager::PrefixTrackTrackTrackSe, triplethistmanager::PrefixTrackTrackTrackMe, closetripletrejection::PrefixTrack1Track2Se, @@ -198,11 +201,16 @@ struct FemtoDndetaTriplet { tripletbuilder::TripletTrackTrackV0Builder< modes::V0::kLambda, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, triplethistmanager::PrefixTrackTrackLambdaSe, triplethistmanager::PrefixTrackTrackLambdaMe, closetripletrejection::PrefixTrack1Track2Se, @@ -214,9 +222,12 @@ struct FemtoDndetaTriplet { tripletTrackTrackLambdaBuilder; tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, - mcparticlehistmanager::PrefixMcParticle3, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, closetripletrejection::PrefixMcParticle1McParticle2Se, diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 267e8f2c343..6b433496642 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -104,12 +104,18 @@ struct FemtoPairD0D0 { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairD0D0Builder< - charmhadronhistmanager::PrefixD01, - trackhistmanager::PrefixD01PosDaughter, - trackhistmanager::PrefixD01NegDaughter, - charmhadronhistmanager::PrefixD02, - trackhistmanager::PrefixD02PosDaughter, - trackhistmanager::PrefixD02NegDaughter, + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + charmhadronhistmanager::PrefixD02Se, + trackhistmanager::PrefixD02PosDaughterSe, + trackhistmanager::PrefixD02NegDaughterSe, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, + charmhadronhistmanager::PrefixD02Me, + trackhistmanager::PrefixD02PosDaughterMe, + trackhistmanager::PrefixD02NegDaughterMe, pairhistmanager::PrefixD0D0Se, pairhistmanager::PrefixD0D0Me, closepairrejection::PrefixD0D0PosSe, diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index e49b4e5557a..3f623ab411f 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -86,8 +86,10 @@ struct FemtoPairMcParticleMcParticle { closepairrejection::ConfCprTrackTrack confCpr; pairbuilder::PairMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, pairhistmanager::PrefixMcParticleMcParticleSe, pairhistmanager::PrefixMcParticleMcParticleMe, closepairrejection::PrefixMcParticleMcParticleSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 99994021ecb..28185d90673 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -117,11 +117,16 @@ struct FemtoPairTrackCascade { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixXi, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, @@ -132,11 +137,16 @@ struct FemtoPairTrackCascade { pairTrackXiBuilder; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixOmega, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 4a09a585caf..172c3c2ffc8 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -120,10 +120,14 @@ struct FemtoPairTrackCharmHadron { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackD0Builder< - trackhistmanager::PrefixTrack1, - charmhadronhistmanager::PrefixD01, - trackhistmanager::PrefixD01PosDaughter, - trackhistmanager::PrefixD01NegDaughter, + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, pairhistmanager::PrefixTrackD0Se, pairhistmanager::PrefixTrackD0Me, closepairrejection::PrefixTrackD0DaughterSe, @@ -132,11 +136,16 @@ struct FemtoPairTrackCharmHadron { pairTrackD0Builder; pairbuilder::PairTrackLcBuilder< - trackhistmanager::PrefixTrack1, - charmhadronhistmanager::PrefixLc1, - trackhistmanager::PrefixLc1ProtonDaughter, - trackhistmanager::PrefixLc1KaonDaughter, - trackhistmanager::PrefixLc1PionDaughter, + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixLc1Se, + trackhistmanager::PrefixLc1ProtonDaughterSe, + trackhistmanager::PrefixLc1KaonDaughterSe, + trackhistmanager::PrefixLc1PionDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixLc1Me, + trackhistmanager::PrefixLc1ProtonDaughterMe, + trackhistmanager::PrefixLc1KaonDaughterMe, + trackhistmanager::PrefixLc1PionDaughterMe, pairhistmanager::PrefixTrackLcSe, pairhistmanager::PrefixTrackLcMe, closepairrejection::PrefixTrackLcProtonSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index 3352ec015ca..13d722338de 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -116,9 +116,12 @@ struct FemtoPairTrackKink { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigma1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigma1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigma1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, @@ -127,9 +130,12 @@ struct FemtoPairTrackKink { pairTrackSigmaBuilder; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigmaPlus1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlus1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlus1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 6653b0c6612..d1e1e423f4f 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -105,8 +105,10 @@ struct FemtoPairTrackTrack { closepairrejection::ConfCprTrackTrack confCpr; pairbuilder::PairTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, pairhistmanager::PrefixTrackTrackSe, pairhistmanager::PrefixTrackTrackMe, closepairrejection::PrefixTrackTrackSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx index 2ed5afba3c2..66a4cea7412 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -94,10 +94,14 @@ struct FemtoPairTrackTwoPhotonResonance { // setup for track-pi0 pairs pairbuilder::PairTrackTwoPhotonResonanceBuilder< - trackhistmanager::PrefixTrack1, - twophotonresonancehistmanager::PrefixPi0, - photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, - photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixPi0Se, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixPi0Me, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, pairhistmanager::PrefixTrackPi0Se, pairhistmanager::PrefixTrackPi0Me, closepairrejection::PrefixTrackTwoPhotonResonanceSe, @@ -107,10 +111,14 @@ struct FemtoPairTrackTwoPhotonResonance { // setup for track-eta pairs pairbuilder::PairTrackTwoPhotonResonanceBuilder< - trackhistmanager::PrefixTrack1, - twophotonresonancehistmanager::PrefixEta, - photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, - photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixEtaSe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixEtaMe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, pairhistmanager::PrefixTrackEtaSe, pairhistmanager::PrefixTrackEtaMe, closepairrejection::PrefixTrackTwoPhotonResonanceSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index fa6927b65fa..25602952698 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -99,10 +99,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-phi pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -112,10 +116,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-kstar0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -125,10 +133,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-rho0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 6466c67b139..88e844687e8 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -116,10 +116,14 @@ struct FemtoPairTrackV0 { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -128,10 +132,14 @@ struct FemtoPairTrackV0 { pairTrackLambdaBuilder; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index c56750af9f1..bfb115b7d87 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -119,12 +119,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-phi pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -137,12 +143,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-kstar0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -155,12 +167,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for lambda-rho0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -173,12 +191,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-phi pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -191,12 +215,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-kstar0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, @@ -209,12 +239,18 @@ struct FemtoPairV0TwoTrackResonance { // setup for k0short-rho0 pairs pairbuilder::PairV0TwoTrackResonanceBuilder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixV0ResonanceSe, pairhistmanager::PrefixV0ResonanceMe, closepairrejection::PrefixV0TwoTrackResonancePosSe, diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index 8dd3b8559ef..371102a368a 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -125,12 +125,18 @@ struct FemtoPairV0V0 { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -142,12 +148,18 @@ struct FemtoPairV0V0 { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -159,12 +171,18 @@ struct FemtoPairV0V0 { pairK0shortK0shortBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx index ff68ce1f256..59c98e936c2 100644 --- a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -79,8 +79,8 @@ struct FemtoTrackTrackTwoPhotonResonanceQa { trackstwophotonresonancehistmanager::TrackTrackTwoPhotonResonanceHistManager< trackstwophotonresonancehistmanager::PrefixOmegaMeson, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDau, trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau1, trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau2, diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx index 866eaae259f..60dfa3a3411 100644 --- a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -84,9 +84,12 @@ struct FemtoTripletMcParticleMcParticleMcParticle { closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtr; tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< - mcparticlehistmanager::PrefixMcParticle1, - mcparticlehistmanager::PrefixMcParticle2, - mcparticlehistmanager::PrefixMcParticle3, + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, closetripletrejection::PrefixMcParticle1McParticle2Se, diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index a4addee1ce1..319fc73ea14 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -124,12 +124,18 @@ struct FemtoTripletTrackTrackCascade { tripletbuilder::TripletTrackTrackCascadeBuilder< modes::Cascade::kXi, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - cascadehistmanager::PrefixXi, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, triplethistmanager::PrefixTrackTrackCascadeSe, triplethistmanager::PrefixTrackTrackCascadeMe, closetripletrejection::PrefixTrack1Track2Se, @@ -146,12 +152,18 @@ struct FemtoTripletTrackTrackCascade { tripletbuilder::TripletTrackTrackCascadeBuilder< modes::Cascade::kOmega, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - cascadehistmanager::PrefixOmega, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, triplethistmanager::PrefixTrackTrackCascadeSe, triplethistmanager::PrefixTrackTrackCascadeMe, closetripletrejection::PrefixTrack1Track2Se, diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 6cd97fc7262..6c80679fc17 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -99,9 +99,12 @@ struct FemtoTripletTrackTrackTrack { closetripletrejection::ConfCtrTrackTrackTrack confCtr; tripletbuilder::TripletTrackTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - trackhistmanager::PrefixTrack3, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, triplethistmanager::PrefixTrackTrackTrackSe, triplethistmanager::PrefixTrackTrackTrackMe, closetripletrejection::PrefixTrack1Track2Se, diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index 0f67c500627..22dd856be44 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -117,11 +117,16 @@ struct FemtoTripletTrackTrackV0 { tripletbuilder::TripletTrackTrackV0Builder< modes::V0::kLambda, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, triplethistmanager::PrefixTrackTrackLambdaSe, triplethistmanager::PrefixTrackTrackLambdaMe, closetripletrejection::PrefixTrack1Track2Se, diff --git a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx index 74e881296e3..3fd9e35e397 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx @@ -112,12 +112,18 @@ struct FemtoPairLambdaAntilambda { paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -129,12 +135,18 @@ struct FemtoPairLambdaAntilambda { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, From 95f0c85d0a601d746452e7ce2809ddd7bb9dab43 Mon Sep 17 00:00:00 2001 From: bhattdurgesh921-sys Date: Mon, 26 Oct 2026 14:48:15 +0530 Subject: [PATCH 437/648] [PWGLF] reduces the number of bins in different histograms axis (#18460) --- PWGLF/Tasks/Resonances/deltaAnalysis.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx index 2e86337b6bb..81f28e2b513 100644 --- a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -246,15 +246,15 @@ struct DeltaAnalysis { mPionDCAPtEdges = dcaCuts.pionDCAPtBinEdges.value; mPionMaxDCAxy = dcaCuts.pionMaxDCAxyPerPtBin.value; - const AxisSpec ptAxis{2000, 0., 20., "p_{T} (GeV/c)"}; - const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 8.0, "M_{inv} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{50, 0., 20., "p_{T} (GeV/c)"}; + const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 6.0, "M_{inv} (GeV/#it{c}^{2})"}; const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; - const AxisSpec nSigmaTPCaxis{100, -10., 10., "n#sigma^{TPC}"}; - const AxisSpec nSigmaTOFaxis{100, -10., 10., "n#sigma^{TOF}"}; - const AxisSpec momentumAxis{2000, 0., 20., "p (GeV/#it{c})"}; - const AxisSpec ptForPIDAxis{2000, 0., 20., "p_{T} (GeV/#it{c})"}; + const AxisSpec nSigmaTPCaxis{100, -5., 5., "n#sigma^{TPC}"}; + const AxisSpec nSigmaTOFaxis{100, -5., 5., "n#sigma^{TOF}"}; + const AxisSpec momentumAxis{50, 0., 20., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{50, 0., 20., "p_{T} (GeV/#it{c})"}; const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; From 31acdb7bcf9ec0054bf42c35184b0ce5daa72d66 Mon Sep 17 00:00:00 2001 From: Joachim Carlo Kristian Bjerg Hansen <50103987+joachimckh@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:19:05 +0100 Subject: [PATCH 438/648] [PWGJE] Minimum pT hat cut feature in jetSpectraEseTask (#18372) --- PWGJE/Tasks/jetSpectraEseTask.cxx | 40 ++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index a170ea714f3..bdfd26ae586 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -92,6 +92,7 @@ struct JetSpectraEseTask { Configurable pTHatMaxMCD{"pTHatMaxMCD", 999.0, "maximum fraction of hard scattering for jet acceptance in detector MC"}; Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum fraction of hard scattering for jet acceptance in particle MC"}; Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; + Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0f, "minimum value of pTHat"}; Configurable fLeadJetPtCut{"fLeadJetPtCut", false, "Flag for leading jet pT cut"}; Configurable leadJetPtMin{"leadJetPtMin", 20.0, "minimum leading jet pT cut"}; @@ -540,9 +541,13 @@ struct JetSpectraEseTask { registry.add("h3CentdeltapTRndmConePhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); registry.add("h3CentdeltapTRndmConePhi_rhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); } + // Normalize MC spectra with hUsedEventWeights bin 1 content, not GetEntries(). if (doprocessMCParticleLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Particle level"); registry.add("mcp/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcp/hUsedEventWeights", "Used MC collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcp/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcp/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcp/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -560,6 +565,9 @@ struct JetSpectraEseTask { if (doprocessMCDetectorLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Detector level"); registry.add("mcd/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcd/hUsedEventWeights", "Used reconstructed collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcd/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcd/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcd/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcd/hJetPt", "particle level jet pT;#it{p}_{T,jet part} (GeV/#it{c});entries", {HistType::kTH1F, {{jetPtAxis}}}); // registry.add("mcd/hJetSparse", ";Centrality;#it{p}_{T,jet det}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -582,6 +590,9 @@ struct JetSpectraEseTask { registry.add("mcm/hMCEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); registry.add("mcm/hMCDMatchedEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcm/hUsedEventWeights", "Used reconstructed collisions with exactly one reconstructed collision per MC collision;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcm/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcm/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcm/hCentralityAnalyzed", ";Centrality;entries", {HistType::kTH1F, {{centAxis}}}); registry.add("mcm/hMatchedJetsPtDelta", "#it{p}_{T,jet part}; det - part", {HistType::kTH2F, {{jetPtAxis}, {100, -20., 20.0}}}); @@ -1184,6 +1195,9 @@ struct JetSpectraEseTask { registry.fill(HIST("mcp/hCentralitySel"), centrality); const float eventWeight = cfgUseMCEventWeights ? mcCollision.weight() : 1.0f; const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcp/hUsedEventWeights"), 0.5, eventWeight); + } jetLoopMCP(jets, centrality, mcCollision.rho(), eventWeight, pTHat); } PROCESS_SWITCH(JetSpectraEseTask, processMCParticleLevel, "jets on particle level MC", false); @@ -1191,7 +1205,8 @@ struct JetSpectraEseTask { void processMCDetectorLevel(soa::Filtered>::iterator const& collision, ChargedMCDJets const& mcdjets, aod::JetTracks const&, - aod::JetParticles const&) + aod::JetParticles const&, + aod::JetMcCollisions const&) { float counter{0.5f}; registry.fill(HIST("mcd/hEventCounter"), counter++); @@ -1212,6 +1227,17 @@ struct JetSpectraEseTask { auto centrality = cfgisPbPb ? getCentrality(collision) : -1; registry.fill(HIST("mcd/hCentralitySel"), centrality); + float eventWeight = 1.0f; + if (cfgUseMCEventWeights) { + eventWeight = collision.has_mcCollision() ? collision.mcCollision_as().weight() : -1.0f; + } + if (eventWeight <= 0.0f) { + return; + } + const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcd/hUsedEventWeights"), 0.5, eventWeight); + } jetLoopMCD(mcdjets, centrality, collision.rho()); } PROCESS_SWITCH(JetSpectraEseTask, processMCDetectorLevel, "jets on detector level", false); @@ -1264,6 +1290,9 @@ struct JetSpectraEseTask { registry.fill(HIST("mcm/hCentralityAnalyzed"), centrality); float eventWeight = cfgUseMCEventWeights ? mcCol.weight() : 1.0; float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcm/hUsedEventWeights"), 0.5, eventWeight); + } matchedJetLoop(mcdjets.sliceBy(mcdjetsPerJCollision, collision.globalIndex()), centrality, collision.rho(), mcCol.rho(), eventWeight, pTHat); registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); @@ -1914,6 +1943,9 @@ struct JetSpectraEseTask { weight = jet.eventWeight(); } const float pTHat = getPTHat(weight); + if (pTHat < pTHatAbsoluteMin) { + continue; + } if (jet.pt() > pTHatMaxMCD * pTHat) { continue; } @@ -1924,6 +1956,9 @@ struct JetSpectraEseTask { template void jetLoopMCP(const Jets& jets, const float& centrality, const float& rho, float weight = 1.0f, float pTHat = 999.0f) { + if (pTHat < pTHatAbsoluteMin) { + return; + } bool mcLevelIsParticleLevel = true; for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { @@ -1946,6 +1981,9 @@ struct JetSpectraEseTask { template void matchedJetLoop(const Jets& jets, const float& centrality, const float& rho, const float& rho2, float weight = 1.0, float pTHat = 999.0) { + if (pTHat < pTHatAbsoluteMin) { + return; + } for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; From 07181c7b701164188d743b15c41ec0f262875eed Mon Sep 17 00:00:00 2001 From: Sebastian Scheid Date: Mon, 26 Oct 2026 14:13:02 +0100 Subject: [PATCH 439/648] [PWGEM] Initial commit for new task to test CPU/Memory optimisation (#18442) --- PWGEM/Dilepton/Core/DileptonMixingTest.h | 1931 +++++++++++++++++ PWGEM/Dilepton/Tasks/CMakeLists.txt | 5 + PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx | 29 + 3 files changed, 1965 insertions(+) create mode 100644 PWGEM/Dilepton/Core/DileptonMixingTest.h create mode 100644 PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx diff --git a/PWGEM/Dilepton/Core/DileptonMixingTest.h b/PWGEM/Dilepton/Core/DileptonMixingTest.h new file mode 100644 index 00000000000..692ccdfe82c --- /dev/null +++ b/PWGEM/Dilepton/Core/DileptonMixingTest.h @@ -0,0 +1,1931 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code runs loop over leptons. +// Please write to: daiki.sekihata@cern.ch + +#ifndef PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ +#define PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ + +#include "PWGEM/Dilepton/Core/DielectronCut.h" +#include "PWGEM/Dilepton/Core/DimuonCut.h" +#include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/Utils/EMFwdTrack.h" +#include "PWGEM/Dilepton/Utils/EMTrack.h" +#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" +#include "PWGEM/Dilepton/Utils/EventHistograms.h" +#include "PWGEM/Dilepton/Utils/EventMixingHandler.h" +#include "PWGEM/Dilepton/Utils/PairUtilities.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using MyCollisions = o2::soa::Join; +using MyCollision = MyCollisions::iterator; + +using MyCollisionsSWT = o2::soa::Join; +using MyCollisionSWT = MyCollisionsSWT::iterator; + +using MyElectrons = o2::soa::Join; +using MyElectron = MyElectrons::iterator; +using FilteredMyElectrons = o2::soa::Filtered; +using FilteredMyElectron = FilteredMyElectrons::iterator; + +using MyMuons = o2::soa::Join; +using MyMuon = MyMuons::iterator; +using FilteredMyMuons = o2::soa::Filtered; +using FilteredMyMuon = FilteredMyMuons::iterator; + +using MyEMH_electron = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMTrack>; +using MyEMH_muon = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMFwdTrack>; + +template +struct Dilepton { + + // Configurables + o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; + o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; + o2::framework::Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "flag to apply resolution correction for flow analysis"}; + o2::framework::Configurable spresoPath{"spresoPath", "Users/d/dsekihat/PWGEM/dilepton/Qvector/resolution/LHC23zzh/pass3/test", "Path to SP resolution file"}; + o2::framework::Configurable spresoHistName{"spresoHistName", "h1_R2_FT0M_BPos_BNeg", "histogram name of SP resolution file"}; + + o2::framework::Configurable cfgAnalysisType{"cfgAnalysisType", static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC), "kQC:0, kUPC:1, kFlowV2SP:2, kFlowV3SP:3, kPolarization:4, kHFll:5, kBootstrapv2:6, kFlowV2EP:7, kFlowV3EP:8"}; + o2::framework::Configurable cfgEP2Estimator_for_Mix{"cfgEP2Estimator_for_Mix", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; + o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 2, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; + o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; + o2::framework::Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; + o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; + o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; + o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; + o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {16, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; + o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; + o2::framework::Configurable cfgUseSignedDCA{"cfgUseSignedDCA", false, "flag to use signs in the DCA calculation"}; + o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; + + o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity + + // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. + + o2::framework::ConfigurableAxis ConfSPBins{"ConfSPBins", {200, -5, 5}, "SP bins for flow analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {20, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {15, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> + + o2::framework::Configurable cfgNumBootstrapSamples{"cfgNumBootstrapSamples", 1, "Number of Bootstrap Samples"}; + + EMEventCut fEMEventCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "eventcut_group"; + o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; + o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; + o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; + o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; + o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; + o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; + o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; + o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; + o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + + o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; + o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; + o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; + } eventcuts; + + DielectronCut fDielectronCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "dielectroncut_group"; + o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; + o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; + o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; + o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; + o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; + o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; + o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; + o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; + o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", true, "flag to apply phiv cut"}; + o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; + o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; + o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; + o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; + o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; + o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 electrons (elliptic cut)"}; + o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.2, "min dphi between 2 electrons (elliptic cut)"}; + o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; + o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; + // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; + + o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; + o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; + o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; + o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; + o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; + o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; + o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; + o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; + o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; + o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; + o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; + o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; + o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; + o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; + o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; + o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; + // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; + // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; + o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. + o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; + o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; + + o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; + o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; + // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; + // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; + o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; + o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; + o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; + o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; + o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; + + // configuration for PID ML + // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; + // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + } dielectroncuts; + + DimuonCut fDimuonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "dimuoncut_group"; + o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; + o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; + o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; + o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; + o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; + o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; + o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; + o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; + o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; + o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; + o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; + + o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; + o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; + o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; + o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; + o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; + o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; + // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; + o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; + o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; + o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; + o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; + o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; + } dimuoncuts; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "zorroGroup"; + o2::framework::Configurable cfg_swt_name{"cfg_swt_name", "fLMeeIMR", "desired software trigger name. 1 trigger per 1 task."}; // 1 trigger per 1 task + o2::framework::Configurable ccdbPathSoftwareTrigger{"ccdbPathSoftwareTrigger", "EventFiltering/Zorro/", "ccdb path for ZORRO objects"}; + o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; + } zorroGroup; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; + o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; + o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; + o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; + o2::framework::ConfigurableAxis ConfNUAEtaBins{"ConfNUAEtaBins", {40, -1., +1.}, "NUA histogram bins - eta"}; + o2::framework::ConfigurableAxis ConfNUAZVtxBins{"ConfNUAZVtxBins", {40, -10., 10.}, "NUA histogram bins - z-vtx"}; + } flowcorrectionGroup; + + Zorro zorro; + int mToIidx = 0; + int mTOICounter = 0; + int mATCounter = 0; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + // o2::ccdb::CcdbApi ccdbApi; + o2::framework::Service ccdb; + int mRunNumber{0}; + float d_bz{0}; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + // static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; + static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; + + std::mt19937 engine; + std::vector cent_bin_edges; + std::vector zvtx_bin_edges; + std::vector ep_bin_edges; + std::vector occ_bin_edges; + + int nmod = -1; // this is for flow analysis + float leptonM1 = 0.f; + float leptonM2 = 0.f; + + float beamM1 = o2::constants::physics::MassProton; // mass of beam + float beamM2 = o2::constants::physics::MassProton; // mass of beam + float beamE1 = 0.f; // beam energy + float beamE2 = 0.f; // beam energy + float beamP1 = 0.f; // beam momentum + float beamP2 = 0.f; // beam momentum + TH2D* h2sp_resolution = nullptr; + std::vector h3nua_weights; + + void init(o2::framework::InitContext& /*context*/) + { + mRunNumber = 0; + d_bz = 0; + mToIidx = 0; + mTOICounter = 0; + mATCounter = 0; + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); + + if (ConfVtxBins.value[0] == o2::framework::VARIABLE_WIDTH) { + zvtx_bin_edges = std::vector(ConfVtxBins.value.begin(), ConfVtxBins.value.end()); + zvtx_bin_edges.erase(zvtx_bin_edges.begin()); + for (const auto& edge : zvtx_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfVtxBins.value[0]); + float xmin = static_cast(ConfVtxBins.value[1]); + float xmax = static_cast(ConfVtxBins.value[2]); + zvtx_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + zvtx_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); + } + } + + if (ConfCentBins.value[0] == o2::framework::VARIABLE_WIDTH) { + cent_bin_edges = std::vector(ConfCentBins.value.begin(), ConfCentBins.value.end()); + cent_bin_edges.erase(cent_bin_edges.begin()); + for (const auto& edge : cent_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: cent_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfCentBins.value[0]); + float xmin = static_cast(ConfCentBins.value[1]); + float xmax = static_cast(ConfCentBins.value[2]); + cent_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + cent_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); + } + } + + if (ConfEPBins.value[0] == o2::framework::VARIABLE_WIDTH) { + ep_bin_edges = std::vector(ConfEPBins.value.begin(), ConfEPBins.value.end()); + ep_bin_edges.erase(ep_bin_edges.begin()); + for (const auto& edge : ep_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: ep_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfEPBins.value[0]); + float xmin = static_cast(ConfEPBins.value[1]); + float xmax = static_cast(ConfEPBins.value[2]); + ep_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + ep_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); + } + } + + LOGF(info, "cfgOccupancyEstimator = %d", cfgOccupancyEstimator.value); + if (ConfOccupancyBins.value[0] == o2::framework::VARIABLE_WIDTH) { + occ_bin_edges = std::vector(ConfOccupancyBins.value.begin(), ConfOccupancyBins.value.end()); + occ_bin_edges.erase(occ_bin_edges.begin()); + for (const auto& edge : occ_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: occ_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfOccupancyBins.value[0]); + float xmin = static_cast(ConfOccupancyBins.value[1]); + float xmax = static_cast(ConfOccupancyBins.value[2]); + occ_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + occ_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); + } + } + + emh_pos = new TEMH(ndepth); + emh_neg = new TEMH(ndepth); + maxBufferedEvents = static_cast(ndepth.value) * (zvtx_bin_edges.size() - 1) * (cent_bin_edges.size() - 1) * (ep_bin_edges.size() - 1) * (occ_bin_edges.size() - 1); + + DefineEMEventCut(); + addhistograms(); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + DefineDielectronCut(); + leptonM1 = o2::constants::physics::MassElectron; + leptonM2 = o2::constants::physics::MassElectron; + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + DefineDimuonCut(); + leptonM1 = o2::constants::physics::MassMuon; + leptonM2 = o2::constants::physics::MassMuon; + } + + fRegistry.add("Pair/mix/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", o2::framework::HistType::kTH1D, {{10001, -0.5, 10000.5}}, true); + + fRegistry.add("Pair/mix/hEventBufferFillLevel", "Event buffer fill level;Number of processed events;Buffered events / maximum buffered events", o2::framework::HistType::kTProfile, {{1500, 0.5, 15000000.5}}, false); + fRegistry.get(HIST("Pair/mix/hEventBufferFillLevel"))->SetCanExtend(TH1::kXaxis); + + if (doprocessTriggerAnalysis) { + LOGF(info, "Trigger analysis is enabled. Desired trigger name = %s", zorroGroup.cfg_swt_name.value.data()); + fRegistry.add("Event/trigger/hInspectedTVX", "inspected TVX;run number;N_{TVX}", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hScaler", "trigger counter before DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hSelection", "trigger counter after DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hAnalysedTrigger", Form("analysed trigger %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hAnalysedToI", Form("analysed ToI %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + } + + if (doprocessNorm) { + fRegistry.addClone("Event/before/hCollisionCounter", "Event/norm/hCollisionCounter"); + fRegistry.add("Event/norm/hZvtx", "hZvtx;Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{100, -50, +50}}, false); + } + if (doprocessBC) { + auto hTVXCounter = fRegistry.add("BC/hTVXCounter", "TVX counter", o2::framework::HistType::kTH1D, {{6, -0.5f, 5.5f}}); + hTVXCounter->GetXaxis()->SetBinLabel(1, "TVX"); + hTVXCounter->GetXaxis()->SetBinLabel(2, "TVX && NoTFB"); + hTVXCounter->GetXaxis()->SetBinLabel(3, "TVX && NoITSROFB"); + hTVXCounter->GetXaxis()->SetBinLabel(4, "TVX && GoodRCT"); + hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); + hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); + } + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + + // In case override, don't proceed, please - no CCDB access required + if (d_bz_input > -990) { + d_bz = d_bz_input; + o2::parameters::GRPMagField grpmag; + if (std::fabs(d_bz) > 1e-5) { + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + mRunNumber = collision.runNumber(); + return; + } + + auto run3grp_timestamp = collision.timestamp(); + o2::parameters::GRPObject* grpo = 0x0; + o2::parameters::GRPMagField* grpmag = 0x0; + if (!skipGRPOquery) + grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + if (grpo) { + o2::base::Propagator::initFieldFromGRP(grpo); + // Fetch magnetic field from ccdb for current collision + d_bz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; + } else { + grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + // Fetch magnetic field from ccdb for current collision + d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; + } + + // zorro + if constexpr (isTriggerAnalysis) { + zorro.setCCDBpath(zorroGroup.ccdbPathSoftwareTrigger); + zorro.setBCtolerance(zorroGroup.bcMarginForSoftwareTrigger); // this does nothing. + mToIidx = zorro.initCCDB(ccdb.service, collision.runNumber(), collision.timestamp(), zorroGroup.cfg_swt_name.value)[0]; + zorro.populateHistRegistry(fRegistry, collision.runNumber()); + + uint64_t nInspectedTVX = zorro.getInspectedTVX()->GetBinContent(1); + uint64_t nScalers = zorro.getScalers()->GetBinContent(zorro.getScalers()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); + uint64_t nSelections = zorro.getSelections()->GetBinContent(zorro.getSelections()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); + LOGF(info, "run number %d: total inspected TVX events = %llu, scalers = %llu, selections = %llu", collision.runNumber(), nInspectedTVX, nScalers, nSelections); + + fRegistry.fill(HIST("Event/trigger/hInspectedTVX"), collision.runNumber(), nInspectedTVX); + fRegistry.fill(HIST("Event/trigger/hScaler"), collision.runNumber(), nScalers); + fRegistry.fill(HIST("Event/trigger/hSelection"), collision.runNumber(), nSelections); + } + + mRunNumber = collision.runNumber(); + fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); + + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); + int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); + int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); + int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); + int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); + beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); + beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); + beamM1 = o2::constants::physics::MassProton * beamA1; + beamM2 = o2::constants::physics::MassProton * beamA2; + beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); + beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); + LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); + + if (cfgApplySPresolution) { + auto list = ccdb->getForTimeStamp(spresoPath, collision.timestamp()); + h2sp_resolution = reinterpret_cast(list->FindObject(spresoHistName.value.data())); + LOGF(info, "h2sp_resolution.GetBinContent(40, 1) = %f", h2sp_resolution->GetBinContent(40, 1)); + } + + if (flowcorrectionGroup.cfgApplyWeightNUA) { + auto list_nua = ccdb->getForTimeStamp(flowcorrectionGroup.nuaPath, collision.timestamp()); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_uls_NUA"))); + LOGF(info, "h3nua_weights.at(0)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(0)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lspp_NUA"))); + LOGF(info, "h3nua_weights.at(1)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(1)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lsmm_NUA"))); + LOGF(info, "h3nua_weights.at(2)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(2)->GetBinContent(5, 5, 5)); + } + } + + ~Dilepton() + { + delete emh_pos; + emh_pos = 0x0; + delete emh_neg; + emh_neg = 0x0; + + used_trackIds_per_col.clear(); + used_trackIds_per_col.shrink_to_fit(); + map_mixed_eventId_to_globalBC.clear(); + + delete h2sp_resolution; + } + + void addhistograms() + { + const std::string qvec_det_names[7] = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg", "FV0A"}; + + std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; + std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; + std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; + std::string pair_y_axis_title = "y_{ll}"; + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + mass_axis_title = "m_{ee} (GeV/c^{2})"; + pair_pt_axis_title = "p_{T,ee} (GeV/c)"; + pair_y_axis_title = "y_{ee}"; + pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; + if (cfgDCAType == 1) { + pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; + } else if (cfgDCAType == 2) { + pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; + } + if (cfgUseSignedDCA) { + pair_dca_axis_title = "Signed " + pair_dca_axis_title; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; + pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; + pair_dca_axis_title = "DCA_{#mu#mu}^{XY} (#sigma)"; + pair_y_axis_title = "y_{#mu#mu}"; + } + + // pair info + const o2::framework::AxisSpec axis_mass{ConfMllBins, mass_axis_title}; + const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; + const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; + const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; + + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); + fRegistry.add("Pair/same/uls/hDeltaEtaDeltaPhi", "#Delta#eta-#Delta#varphi between 2 tracks;#Delta#varphi (rad.);#Delta#eta;", o2::framework::HistType::kTH2D, {{180, -M_PI, M_PI}, {400, -2, +2}}, false); + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.add("Pair/same/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); // phiv is only for dielectron + fRegistry.add("Pair/same/uls/hMvsOpAng", "m_{ee} vs. angle between 2 tracks;#omega (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); + fRegistry.add("Pair/same/uls/hDCA1vsDCA2", "DCA of leg1 vs. DCA of leg2;DCA1(#sigma);DCA2 (#sigma)", o2::framework::HistType::kTH2D, {{200, -10, 10.0}, {200, -10, 10}}, false); + } + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { + const o2::framework::AxisSpec axis_aco{10, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; + const o2::framework::AxisSpec axis_asym_pt{10, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; + const o2::framework::AxisSpec axis_dphi_l_ll{18, 0, M_PI, "#Delta#varphi = #varphi_{l} - #varphi_{ll} (rad.)"}; + + std::string frameName = "CS"; + if (cfgPolarizationFrame == 0) { + frameName = "CS"; + } else if (cfgPolarizationFrame == 1) { + frameName = "HX"; + } else { + LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; + } + const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_aco, axis_asym_pt, axis_dphi_l_ll, axis_cos_theta}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { + nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + nmod = 3; + } + + const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; + + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); // for NUA correction needs to be filled with weights so sumw2 is true + } + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { + std::string frameName = "CS"; + if (cfgPolarizationFrame == 0) { + frameName = "CS"; + } else if (cfgPolarizationFrame == 1) { + frameName = "HX"; + } else { + LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; + } + + const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; + const o2::framework::AxisSpec axis_phi{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; + const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, false); + + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { + const o2::framework::AxisSpec axis_dphi_ll{36, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll + const o2::framework::AxisSpec axis_deta_ll{40, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_dphi_ll, axis_deta_ll}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { + nmod = 2; + const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; + const o2::framework::AxisSpec axis_bootstrap{cfgNumBootstrapSamples, 0.5, static_cast(cfgNumBootstrapSamples) + 0.5, "sample"}; // for bootstrap samples + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp, axis_bootstrap}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistogramsBootstrap(&fRegistry, cfgNumBootstrapSamples); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { + nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + nmod = 3; + } + + // similar to Eq.1 in PRL 126, 162001 (2021) + const o2::framework::AxisSpec axis_ep{4, 0, 2 * M_PI / nmod, Form("#varphi_{ll} #minus #Psi_{%d}^{%s}", nmod, qvec_det_names[cfgQvecEstimator].data())}; + + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_ep}, true); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + } else { // same as kQC to avoid seg. fault + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } + + // event info + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<2>(&fRegistry); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<3>(&fRegistry); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<-1>(&fRegistry); + } + fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + fRegistry.add("Event/after/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + } + + void DefineEMEventCut() + { + fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void DefineDielectronCut() + { + fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); + + // for pair + fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); + fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); + fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); + fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma + fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); + fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); + fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); + fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); + // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); + + // for track + fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); + fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, dielectroncuts.cfg_max_eta_track); + fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); + fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); + fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); + fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); + fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); + fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); + fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); + fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, cfgDCAType); + fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); + fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); + fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); + fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); + fDielectronCut.SetChi2TOF(0, dielectroncuts.cfg_max_chi2tof); + // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); + fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); + + // for eID + fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); + fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); + // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); + fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); + fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); + fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); + fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); + fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); + + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut + std::vector binsML{}; + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); + } + std::vector thresholdsML{}; + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); + } + fDielectronCut.SetMLThresholds(binsML, thresholdsML); + } // end of PID ML + } + + void DefineDimuonCut() + { + fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); + + // for pair + fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); + fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); + fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); + fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); + fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); + + // for track + fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); + fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); + fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); + fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); + fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); + fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); + fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); + fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); + // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); + fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); + fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); + fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); + fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); + fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); + fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons + fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); + fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); + } + + template + bool isGoodQvector(TQvectors const& qvectors) + { + bool is_good = true; + for (const auto& qn : qvectors[nmod]) { + if (std::fabs(qn[0]) > 20.f || std::fabs(qn[1]) > 20.f) { + is_good = false; + break; + } + } + return is_good; + } + + float getSPresolution(const float centrality, const int occupancy) + { + if (h2sp_resolution == nullptr) { + return 1.f; + } else { + int binId_cen = h2sp_resolution->GetXaxis()->FindBin(centrality); + int binId_occ = h2sp_resolution->GetYaxis()->FindBin(occupancy); + + if (centrality < h2sp_resolution->GetXaxis()->GetXmin()) { + binId_cen = 1; + } + if (h2sp_resolution->GetXaxis()->GetXmax() < centrality) { + binId_cen = h2sp_resolution->GetXaxis()->GetNbins(); + } + + if (occupancy < h2sp_resolution->GetYaxis()->GetXmin()) { + binId_occ = 1; + } + if (h2sp_resolution->GetYaxis()->GetXmax() < occupancy) { + binId_occ = h2sp_resolution->GetYaxis()->GetNbins(); + } + return h2sp_resolution->GetBinContent(binId_cen, binId_occ); + } + } + + float getNUAweight(const int type_int, const float phi, const float eta, const float zVtx) + { + if (h3nua_weights.at(type_int) == nullptr) { + return 1.f; + } + int binId_phi = h3nua_weights.at(type_int)->GetXaxis()->FindBin(phi); + int binId_eta = h3nua_weights.at(type_int)->GetYaxis()->FindBin(eta); + int binId_zVtx = h3nua_weights.at(type_int)->GetZaxis()->FindBin(zVtx); + float nuaWeight = h3nua_weights.at(type_int)->GetBinContent(binId_phi, binId_eta, binId_zVtx); + + if (nuaWeight == 0 || std::isnan(nuaWeight) || std::isinf(nuaWeight)) { + nuaWeight = 1.f; + } + return nuaWeight; + } + + template + bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector& weightvector) + { + if constexpr (ev_id == 0) { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } else { // cut-based + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { + return false; + } + + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { + // return false; + // } + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { + // return false; + // } + } + } + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } + + float weight = 1.f; + if constexpr (ev_id == 0) { + if (cfgApplyWeightTTCA) { + weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; + } + // LOGF(info, "ev_id = %d, t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", ev_id, t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + + float pair_dca = 999.f; + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); + } + if (cfgDCAType == 1) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); + } + } else if (cfgDCAType == 2) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); + } + + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { + float deta = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); + float dphi = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi + + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); + float opAng = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { + float dphi = v1.Phi() - v2.Phi(); + o2::math_utils::bringToPMPi(dphi); + float aco = 1.f - std::fabs(dphi) / M_PI; + float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); + float dphi_l_ll = v1.Phi() - v12.Phi(); + o2::math_utils::bringToPMPi(dphi_l_ll); + + float cos_thetaPol = 999, phiPol = 999.f; + auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; + auto arrD = t1.sign() * t1.pt() > t2.sign() * t2.pt() ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}; + + if (cfgPolarizationFrame == 0) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } else if (cfgPolarizationFrame == 1) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } + + o2::math_utils::bringToPMPi(phiPol); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if constexpr (ev_id == 0) { + // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + if (flowcorrectionGroup.cfgApplyWeightNUA) { + weight *= 1. / getNUAweight(t1.sign() * t2.sign() < 0 ? 0 : (t1.sign() > 0 && t2.sign() > 0 ? 1 : 2), v12.Phi(), v12.Eta(), collision.posZ()); + } + + float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { + float cos_thetaPol = 999, phiPol = 999.f; + auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; + auto random_sign = std::pow(-1, engine() % 2); // -1^0 = +1 or -1^1 = -1; + auto arrD = t1.sign() * t2.sign() < 0 ? (t1.sign() > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}) : (random_sign > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}); + + if (cfgPolarizationFrame == 0) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } else if (cfgPolarizationFrame == 1) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } + + o2::math_utils::bringToPMPi(phiPol); + float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { + float dphi = v1.Phi() - v2.Phi(); + dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf); + float deta = v1.Eta() - v2.Eta(); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } + + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if constexpr (ev_id == 0) { + // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + + float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); + for (int i = 0; i < cfgNumBootstrapSamples; i++) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + if constexpr (ev_id == 0) { + std::vector> eventplanes = { + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 0th harmonics + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 1st harmonics + {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}, // 2nd harmonics + {collision.ep3ft0m(), collision.ep3ft0a(), collision.ep3ft0c(), collision.ep3btot(), collision.ep3bpos(), collision.ep3bneg(), collision.ep3fv0a()}, // 3rd harmonics + }; + + float ep = RecoDecay::constrainAngle(eventplanes[nmod][cfgQvecEstimator], -o2::constants::math::PI / nmod, static_cast(nmod)); + float phi_ll = RecoDecay::constrainAngle(v12.Phi(), -o2::constants::math::PI / nmod, 1U); + float dphi_ll_ep = std::fabs(RecoDecay::constrainAngle(phi_ll - ep, 0.f, static_cast(nmod))); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else { // same as kQC to avoid seg. fault + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + + // store tracks for event mixing without double counting + if constexpr (ev_id == 0) { + std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t1.globalIndex()); + if (cfgDoMix) { + if (t1.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); + } + } + } + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t2.globalIndex()); + if (cfgDoMix) { + if (t2.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); + } + } + } + } else if (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t1.globalIndex()); + if (cfgDoMix) { + if (t1.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); + } + } + } + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t2.globalIndex()); + if (cfgDoMix) { + if (t2.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); + } + } + } + } + } + return true; + } + + o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); + o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; + o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsSWT = o2::soa::Filtered; + + o2::framework::SliceCache cache; + // o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; + o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; + o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); + o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); + + o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfb >= static_cast(0)); + + o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); + o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); + + // o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; + o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); + + o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); + o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); + + TEMH* emh_pos = nullptr; + TEMH* emh_neg = nullptr; + + std::map, uint64_t> map_mixed_eventId_to_globalBC; + std::unordered_map map_best_match_globalmuon; + + std::vector used_trackIds_per_col; + uint64_t nProcessedEvents = 0; + double maxBufferedEvents = 0.; + + void fillEventBufferLevel() + { + ++nProcessedEvents; + if (cfgDoMix && nProcessedEvents % 10000 == 0) { + const double fraction = static_cast(map_mixed_eventId_to_globalBC.size()) / maxBufferedEvents; + fRegistry.fill(HIST("Pair/mix/hEventBufferFillLevel"), nProcessedEvents, fraction); + } + } + + int ndf = 0; + + template + void runPairing(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) + { + for (const auto& collision : collisions) { + initCCDB(collision); + + const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + float centrality = centralities[cfgCentEstimator]; + if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { + fillEventBufferLevel(); + continue; + } + + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + const float eventplanes_2_for_mix[7] = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}; + float ep2 = eventplanes_2_for_mix[cfgEP2Estimator_for_Mix]; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 3>(&fRegistry, collision); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); + } + if (nmod < 0 || isGoodQvector(qvectors)) { + fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated + } + fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); + + if (!fEMEventCut.IsSelected(collision)) { + fillEventBufferLevel(); + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + fillEventBufferLevel(); + continue; + } + + if (nmod > 0 && !isGoodQvector(qvectors)) { + fillEventBufferLevel(); + continue; + } + + if constexpr (isTriggerAnalysis) { + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + if (!collision.triggerMask_bit(emswtId)) { + fillEventBufferLevel(); + continue; + } + + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + + // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected + // auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected + // auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected + + // if (swt_bitset.test(mToIidx)) { + // while (ATcounter > mATCounter) { + // mATCounter++; + // fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); + // } + + // while (TOIcounter > mTOICounter) { + // fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); + // mTOICounter++; // always incremented by 1 in zorro!! + // } + // // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); + // } + } + + std::vector bootstrapweights = {}; + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { // bootstrapping for accepted events + int randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + TRandom3 randomNumber(randomSeed); + for (int i = 0; i < cfgNumBootstrapSamples; i++) { + float poissonweight = 0.; + poissonweight = static_cast(randomNumber.PoissonD(1.0)); + bootstrapweights.push_back(poissonweight); + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfoBootstrap(&fRegistry, collision, i, poissonweight); + } + } else { + bootstrapweights.push_back(1.0); // to pass as non-empyt dummy to use + } + + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 3>(&fRegistry, collision); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); + } + fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated + + fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + // LOGF(info, "collision.globalIndex() = %d , collision.posZ() = %f , collision.numContrib() = %d, centrality = %f , posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", collision.globalIndex(), collision.posZ(), collision.numContrib(), centralities[cfgCentEstimator], posTracks_per_coll.size(), negTracks_per_coll.size()); + + used_trackIds_per_col.reserve(posTracks_per_coll.size() + negTracks_per_coll.size()); + int nuls = 0, nlspp = 0, nlsmm = 0; + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + bool is_pair_ok = fillPairInfo<0>(collision, pos, neg, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nuls++; + } + } + for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + bool is_pair_ok = fillPairInfo<0>(collision, pos1, pos2, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nlspp++; + } + } + for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + bool is_pair_ok = fillPairInfo<0>(collision, neg1, neg2, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nlsmm++; + } + } + used_trackIds_per_col.clear(); + used_trackIds_per_col.shrink_to_fit(); + + if (!cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { + fillEventBufferLevel(); + continue; + } + + // event mixing + int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; + if (zbin < 0) { + zbin = 0; + } else if (static_cast(zvtx_bin_edges.size()) - 2 < zbin) { + zbin = static_cast(zvtx_bin_edges.size()) - 2; + } + + int centbin = lower_bound(cent_bin_edges.begin(), cent_bin_edges.end(), centrality) - cent_bin_edges.begin() - 1; + if (centbin < 0) { + centbin = 0; + } else if (static_cast(cent_bin_edges.size()) - 2 < centbin) { + centbin = static_cast(cent_bin_edges.size()) - 2; + } + + int epbin = lower_bound(ep_bin_edges.begin(), ep_bin_edges.end(), ep2) - ep_bin_edges.begin() - 1; + if (epbin < 0) { + epbin = 0; + } else if (static_cast(ep_bin_edges.size()) - 2 < epbin) { + epbin = static_cast(ep_bin_edges.size()) - 2; + } + + int occbin = -1; + if (cfgOccupancyEstimator == 0) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else if (cfgOccupancyEstimator == 1) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } + + if (occbin < 0) { + occbin = 0; + } else if (static_cast(occ_bin_edges.size()) - 2 < occbin) { + occbin = static_cast(occ_bin_edges.size()) - 2; + } + + // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); + + std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); + std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. + + // make a vector of selected photons in this collision. + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); + + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); + + for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { + int mix_dfId = mix_dfId_collisionId.first; + int mix_collisionId = mix_dfId_collisionId.second; + if (collision.globalIndex() == mix_collisionId && ndf == mix_dfId) { // this never happens. only protection. + continue; + } + + auto globalBC_mix = map_mixed_eventId_to_globalBC[mix_dfId_collisionId]; + uint64_t diffBC = std::max(collision.globalBC(), globalBC_mix) - std::min(collision.globalBC(), globalBC_mix); + fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); + if (diffBC < ndiff_bc_mix) { + continue; + } + + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); + + for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix + for (const auto& neg : negTracks_from_event_pool) { + fillPairInfo<1>(collision, pos, neg, cut, nullptr, bootstrapweights); + } + } + + for (const auto& neg : selected_negTracks_in_this_event) { // ULS mix + for (const auto& pos : posTracks_from_event_pool) { + fillPairInfo<1>(collision, neg, pos, cut, nullptr, bootstrapweights); + } + } + + for (const auto& pos1 : selected_posTracks_in_this_event) { // LS++ mix + for (const auto& pos2 : posTracks_from_event_pool) { + fillPairInfo<1>(collision, pos1, pos2, cut, nullptr, bootstrapweights); + } + } + + for (const auto& neg1 : selected_negTracks_in_this_event) { // LS-- mix + for (const auto& neg2 : negTracks_from_event_pool) { + fillPairInfo<1>(collision, neg1, neg2, cut, nullptr, bootstrapweights); + } + } + } // end of loop over mixed event pool + + if (nuls > 0 || nlspp > 0 || nlsmm > 0) { + map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + + } // end of if pair exist + + fillEventBufferLevel(); + } // end of collision loop + + } // end of DF + + template + bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } else { // cut-based + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { + return false; + } + if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { + return false; + } + } + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } + return true; + } + + std::map, float> map_weight; // -> float + template + void fillPairWeightMap(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) + { + std::vector> passed_pairIds; + passed_pairIds.reserve(posTracks.size() * negTracks.size()); + + for (const auto& collision : collisions) { + initCCDB(collision); + const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { + continue; + } + + // if constexpr (isTriggerAnalysis) { // don't call this here. this causes double counting. + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + // } + + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + if (nmod > 0 && !isGoodQvector(qvectors)) { + continue; + } + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + if (isPairOK(pos, neg, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); + } + } + for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + if (isPairOK(pos1, pos2, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); + } + } + for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + if (isPairOK(neg1, neg2, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); + } + } + } // end of collision loop + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + for (const auto& pairId : passed_pairIds) { + auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); + auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); + + float n = 1.f; // include myself. + for (const auto& ambId1 : t1.ambiguousElectronsIds()) { + for (const auto& ambId2 : t2.ambiguousElectronsIds()) { + if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { + n += 1.f; + } + } + } + map_weight[pairId] = 1.f / n; + } // end of passed_pairIds loop + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + for (const auto& pairId : passed_pairIds) { + auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); + auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); + + float n = 1.f; // include myself. + for (const auto& ambId1 : t1.ambiguousMuonsIds()) { + for (const auto& ambId2 : t2.ambiguousMuonsIds()) { + if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { + n += 1.f; + } + } + } + map_weight[pairId] = 1.f / n; + } // end of passed_pairIds loop + } + passed_pairIds.clear(); + passed_pairIds.shrink_to_fit(); + } + + void processAnalysis(FilteredMyCollisions const& collisions, Types const&... args) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + auto electrons = std::get<0>(std::tie(args...)); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } + runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + auto muons = std::get<0>(std::tie(args...)); + map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + map_weight.clear(); + map_best_match_globalmuon.clear(); + ndf++; + } + PROCESS_SWITCH(Dilepton, processAnalysis, "run dilepton analysis", true); + + void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + auto electrons = std::get<0>(std::tie(args...)); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } + runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + auto muons = std::get<0>(std::tie(args...)); + map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + map_weight.clear(); + map_best_match_globalmuon.clear(); + ndf++; + + // for nomalization + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + for (const auto& counter : countersAT) { + if (counter.isAT_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), mRunNumber); + } + } + for (const auto& counter : countersATOI) { + if (counter.isAToI_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), mRunNumber); + } + } + } + PROCESS_SWITCH(Dilepton, processTriggerAnalysis, "run dilepton analysis on triggered data", false); + + void processNorm(o2::aod::EMEventNormInfos const& collisions) + { + for (const auto& collision : collisions) { + if (collision.centFT0C() < eventcuts.cfgCentMin || eventcuts.cfgCentMax < collision.centFT0C()) { + continue; + } + fRegistry.fill(HIST("Event/norm/hZvtx"), collision.posZ()); + + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 1.0); + if (collision.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 2.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 3.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 4.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoSameBunchPileup)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 5.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodZvtxFT0vsPV)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 6.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexITSTPC)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 7.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexTRDmatched)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 8.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexTOFmatched)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 9.0); + } + if (collision.sel8()) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 10.0); + } + if (std::fabs(collision.posZ()) < 10.0) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 11.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStandard)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 12.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStrict)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 13.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStandard)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 14.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStrict)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 15.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoHighMultCollInPrevRof)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 16.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer3)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 17.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer0123)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 18.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayersAll)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 19.0); + } + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + } // end of collision loop + } + PROCESS_SWITCH(Dilepton, processNorm, "process normalization info", false); + + void processBC(o2::aod::EMBCs const& bcs) + { + for (const auto& bc : bcs) { + if (bc.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 0.f); + + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 1.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 2.f); + } + if (rctChecker(bc)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 3.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 4.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder) && rctChecker(bc)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 5.f); + } + } + } + } + PROCESS_SWITCH(Dilepton, processBC, "process BC counter", false); + + void processDummy(MyCollisions const&) {} + PROCESS_SWITCH(Dilepton, processDummy, "Dummy function", false); +}; + +#endif // PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ diff --git a/PWGEM/Dilepton/Tasks/CMakeLists.txt b/PWGEM/Dilepton/Tasks/CMakeLists.txt index 425ea54f510..845a8fe4d98 100644 --- a/PWGEM/Dilepton/Tasks/CMakeLists.txt +++ b/PWGEM/Dilepton/Tasks/CMakeLists.txt @@ -169,3 +169,8 @@ o2physics_add_dpl_workflow(check-mc-pair-template SOURCES checkMCPairTemplate.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dielectron-mixing-test + SOURCES dielectronMixingTest.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx new file mode 100644 index 00000000000..70ed37ceb05 --- /dev/null +++ b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code is for dielectron analyses. +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/Core/DileptonMixingTest.h" +#include "PWGEM/Dilepton/Utils/PairUtilities.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask>(cfgc, TaskName{"dielectronMixingTests"})}; +} From 71c13094ff377172ec6b6beeac4ddea1e0d13003 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:34:18 +0100 Subject: [PATCH 440/648] [Common] Add NGlobalTracksVsFT0MOuterA needed for mc calibration (#18439) --- Common/Tasks/centralityStudy.cxx | 333 ++++++++++--------------------- 1 file changed, 106 insertions(+), 227 deletions(-) diff --git a/Common/Tasks/centralityStudy.cxx b/Common/Tasks/centralityStudy.cxx index 3746eb622d5..7168d78b5cc 100644 --- a/Common/Tasks/centralityStudy.cxx +++ b/Common/Tasks/centralityStudy.cxx @@ -241,6 +241,16 @@ struct CentralityStudy { return std::get>(histPointers[name]); } + // fill the global histogram and, if enabled, its run-by-run copy with the same values + template + void fillHist(const TName& name, Ts... values) + { + histos.fill(name, values...); + if (studies.doRunByRunHistograms) { + getHist(histPath + TName::str)->Fill(values...); + } + } + void init(InitContext&) { ccdb->setURL(ccdbSettings.ccdbURL); @@ -350,6 +360,7 @@ struct CentralityStudy { histos.add("hNGlobalTracksVsFT0A", "hNGlobalTracksVsFT0A", kTH2F, {axisMultFT0A, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFT0C", "hNGlobalTracksVsFT0C", kTH2F, {axisMultFT0C, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFT0M", "hNGlobalTracksVsFT0M", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFT0MOuterA", "hNGlobalTracksVsFT0MOuterA", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFV0A", "hNGlobalTracksVsFV0A", kTH2F, {axisMultFV0A, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFDDA", "hNGlobalTracksVsFDDA", kTH2F, {axisMultFDDA, axisMultGlobalTracks}); histos.add("hNGlobalTracksVsFDDC", "hNGlobalTracksVsFDDC", kTH2F, {axisMultFDDC, axisMultGlobalTracks}); @@ -555,9 +566,9 @@ struct CentralityStudy { histPointers.insert({histPath + "hFT0C_Collisions", histos.add((histPath + "hFT0C_Collisions").c_str(), "hFT0C_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); histPointers.insert({histPath + "hFT0COuter_Collisions", histos.add((histPath + "hFT0COuter_Collisions").c_str(), "hFT0COuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); histPointers.insert({histPath + "hFT0A_Collisions", histos.add((histPath + "hFT0A_Collisions").c_str(), "hFT0A_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); - histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); histPointers.insert({histPath + "hFV0AOuter_Collisions", histos.add((histPath + "hFV0AOuter_Collisions").c_str(), "hFV0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); - histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFT0M_Collisions", histos.add((histPath + "hFT0M_Collisions").c_str(), "hFT0M_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFV0A_Collisions", histos.add((histPath + "hFV0A_Collisions").c_str(), "hFV0A_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); histPointers.insert({histPath + "hFIT_Collisions", histos.add((histPath + "hFIT_Collisions").c_str(), "hFIT_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); @@ -616,6 +627,7 @@ struct CentralityStudy { histPointers.insert({histPath + "hNGlobalTracksVsFT0A", histos.add((histPath + "hNGlobalTracksVsFT0A").c_str(), "hNGlobalTracksVsFT0A", {kTH2F, {{axisMultFT0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0C", histos.add((histPath + "hNGlobalTracksVsFT0C").c_str(), "hNGlobalTracksVsFT0C", {kTH2F, {{axisMultFT0C, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0M", histos.add((histPath + "hNGlobalTracksVsFT0M").c_str(), "hNGlobalTracksVsFT0M", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); + histPointers.insert({histPath + "hNGlobalTracksVsFT0MOuterA", histos.add((histPath + "hNGlobalTracksVsFT0MOuterA").c_str(), "hNGlobalTracksVsFT0MOuterA", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFV0A", histos.add((histPath + "hNGlobalTracksVsFV0A").c_str(), "hNGlobalTracksVsFV0A", {kTH2F, {{axisMultFV0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNMFTTracks", histos.add((histPath + "hNGlobalTracksVsNMFTTracks").c_str(), "hNGlobalTracksVsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNTPV", histos.add((histPath + "hNGlobalTracksVsNTPV").c_str(), "hNGlobalTracksVsNTPV", {kTH2F, {{axisMultPVContributors, axisMultGlobalTracks}}})}); @@ -721,19 +733,13 @@ struct CentralityStudy { initRun(collision); configureCentrality(collision); - histos.fill(HIST("hCollisionSelection"), 0); // all collisions - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(0); - } + fillHist(HIST("hCollisionSelection"), 0); // all collisions if (evsel.applySel8 && !collision.multSel8()) { return; } - histos.fill(HIST("hCollisionSelection"), 1); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(1); - } + fillHist(HIST("hCollisionSelection"), 1); // calculate vertex-Z-equalized quantities if desired float multFV0A = collision.multFV0A(); @@ -796,8 +802,7 @@ struct CentralityStudy { passRequireIsVertexTOFmatched && passRequireIsVertexTRDmatched && passRejectSameBunchPileup && passRejectITSinROFpileupStandard && passRejectITSinROFpileupStrict && passSelectUPCcollisions && passRejectCollInTimeRangeNarrow && passINELgtZERO) { getHist(histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scale.factorFT0C); - getHist(histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scale.factorFT0C); - getHist(histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scale.factorFT0C); + getHist(histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scale.factorFT0A); getHist(histPath + "hFV0AvsPVz_Collisions")->Fill(collision.multPVz(), multFV0A * scale.factorFV0A); getHist(histPath + "hNGlobalTracksvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksGlobal); getHist(histPath + "hNMFTTracksvsPVz_Collisions")->Fill(collision.multPVz(), mftNtracks); @@ -810,10 +815,7 @@ struct CentralityStudy { return; } - histos.fill(HIST("hCollisionSelection"), 2); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(2); - } + fillHist(HIST("hCollisionSelection"), 2); // _______________________________________________________ // Extra event selections start here @@ -821,53 +823,32 @@ struct CentralityStudy { return; } - histos.fill(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(3); - } + fillHist(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); if (!passRejectTFBorder) { return; } - histos.fill(HIST("hCollisionSelection"), 4 /* Not at TF border */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(4); - } + fillHist(HIST("hCollisionSelection"), 4 /* Not at TF border */); if (!passRequireIsVertexITSTPC) { return; } - histos.fill(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(5); - } + fillHist(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); if (!passRequireIsGoodZvtxFT0VsPV) { return; } - histos.fill(HIST("hCollisionSelection"), 6 /* PV position consistency check */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(6); - } + fillHist(HIST("hCollisionSelection"), 6 /* PV position consistency check */); if (!passRequireIsVertexTOFmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(7); - } + fillHist(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); if (!passRequireIsVertexTRDmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(8); - } + fillHist(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); if (!passRejectSameBunchPileup) { return; } - histos.fill(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(9); - } + fillHist(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); // do this only if information is available if constexpr (requires { collision.timeToNext(); }) { float timeToNeighbour = std::min( @@ -878,39 +859,24 @@ struct CentralityStudy { return; } } - histos.fill(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(10); - } + fillHist(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); if (!passRejectITSinROFpileupStandard) { return; } - histos.fill(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(11); - } + fillHist(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); if (!passRejectITSinROFpileupStrict) { return; } - histos.fill(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(12); - } + fillHist(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); if (!passSelectUPCcollisions) { // if zero then NOT upc, otherwise UPC return; } - histos.fill(HIST("hCollisionSelection"), 13 /* is UPC event */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(13); - } + fillHist(HIST("hCollisionSelection"), 13 /* is UPC event */); if (!passRejectCollInTimeRangeNarrow) { return; } - histos.fill(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(14); - } + fillHist(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); if (evsel.rejectUpc) { if (collision.multFT0C() < cfgFwd.maxFT0CforZNACselection && @@ -927,10 +893,7 @@ struct CentralityStudy { return; } } - histos.fill(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(15); - } + fillHist(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); if (evsel.rejectIsFlangeEvent) { if constexpr (requires { collision.has_multBC(); }) { if (collision.has_multBC()) { @@ -944,18 +907,12 @@ struct CentralityStudy { } } - histos.fill(HIST("hCollisionSelection"), 16 /* reject flange events */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(16); - } + fillHist(HIST("hCollisionSelection"), 16 /* reject flange events */); if (!passINELgtZERO) { return; } - histos.fill(HIST("hCollisionSelection"), 17 /* is INEL > 0 */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(17); - } + fillHist(HIST("hCollisionSelection"), 17 /* is INEL > 0 */); if (evsel.applyBcSel) { if constexpr (requires { collision.has_multBC(); }) { @@ -975,14 +932,11 @@ struct CentralityStudy { } } - histos.fill(HIST("hCollisionSelection"), 18 /* bc selections */); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCollisionSelection")->Fill(18); - } + fillHist(HIST("hCollisionSelection"), 18 /* bc selections */); // if we got here, we also finally fill the FT0C histogram, please histos.fill(HIST("hNPVContributors"), collision.multNTracksPV()); - histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scale.factorFT0C); + histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scale.factorFT0A); histos.fill(HIST("hFT0C_Collisions"), collision.multFT0C() * scale.factorFT0C); histos.fill(HIST("hFT0M_Collisions"), (collision.multFT0A() + collision.multFT0C()) * scale.factorFT0M); histos.fill(HIST("hFV0A_Collisions"), collision.multFV0A() * scale.factorFV0A); @@ -1015,24 +969,14 @@ struct CentralityStudy { } if (studies.do2DPlots) { - histos.fill(HIST("hNContribsVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); - histos.fill(HIST("hNContribsVsFV0A"), collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); - histos.fill(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); + fillHist(HIST("hNContribsVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNContribsVsFV0A"), collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); + fillHist(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); // correlate also FIT detector signals - histos.fill(HIST("hFT0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); - histos.fill(HIST("hFV0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); - histos.fill(HIST("hFDDAVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); - histos.fill(HIST("hFDDCVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNContribsVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); - getHist(histPath + "hNContribsVsFV0A")->Fill(collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); - getHist(histPath + "hMatchedVsITSOnly")->Fill(collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); - // correlate also FIT detector signals - getHist(histPath + "hFT0AVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); - getHist(histPath + "hFV0AVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); - getHist(histPath + "hFDDAVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); - getHist(histPath + "hFDDCVsFT0C")->Fill(collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); - } + fillHist(HIST("hFT0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); + fillHist(HIST("hFV0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); + fillHist(HIST("hFDDAVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); + fillHist(HIST("hFDDCVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); } if (studies.doOccupancyStudyVsCentrality2d) { @@ -1050,91 +994,45 @@ struct CentralityStudy { } if (studies.doNGlobalTracksVsRawSignals) { - histos.fill(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNGlobalTracksVsFT0A")->Fill(multFT0A, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFT0C")->Fill(multFT0C, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFT0M")->Fill(multFT0A + multFT0C, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsFV0A")->Fill(multFV0A, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsNMFTTracks")->Fill(mftNtracks, multNTracksGlobal); - getHist(histPath + "hNGlobalTracksVsNTPV")->Fill(multNTracksPV, multNTracksGlobal); - } + fillHist(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); } if (studies.doNTracksPVeta05VsRawSignals) { - histos.fill(HIST("hNTracksPVeta05VsFT0A"), multFT0A, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFT0C"), multFT0C, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFT0M"), (multFT0A + multFT0C), multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFV0A"), multFV0A, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFDDA"), multFDDA, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsFDDC"), multFDDC, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsNMFTTracks"), mftNtracks, multNTracksPV05); - histos.fill(HIST("hNTracksPVeta05VsNTPV"), multNTracksPV, multNTracksPV05); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta05VsFT0A")->Fill(multFT0A, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFT0C")->Fill(multFT0C, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFV0A")->Fill(multFV0A, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFDDA")->Fill(multFDDA, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsFDDC")->Fill(multFDDC, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsNMFTTracks")->Fill(mftNtracks, multNTracksPV05); - getHist(histPath + "hNTracksPVeta05VsNTPV")->Fill(multNTracksPV, multNTracksPV05); - } + fillHist(HIST("hNTracksPVeta05VsFT0A"), multFT0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0C"), multFT0C, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0M"), (multFT0A + multFT0C), multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFV0A"), multFV0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDA"), multFDDA, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDC"), multFDDC, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNMFTTracks"), mftNtracks, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNTPV"), multNTracksPV, multNTracksPV05); } if (studies.doNTracksPVeta08VsRawSignals) { - histos.fill(HIST("hNTracksPVeta08VsFT0A"), multFT0A, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFT0C"), multFT0C, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFT0M"), (multFT0A + multFT0C), multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFV0A"), multFV0A, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFDDA"), multFDDA, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsFDDC"), multFDDC, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsNMFTTracks"), mftNtracks, multNTracksPV); - histos.fill(HIST("hNTracksPVeta08VsNTPV"), multNTracksPV, multNTracksPV); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta08VsFT0A")->Fill(multFT0A, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFT0C")->Fill(multFT0C, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFV0A")->Fill(multFV0A, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFDDA")->Fill(multFDDA, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsFDDC")->Fill(multFDDC, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsNMFTTracks")->Fill(mftNtracks, multNTracksPV); - getHist(histPath + "hNTracksPVeta08VsNTPV")->Fill(multNTracksPV, multNTracksPV); - } + fillHist(HIST("hNTracksPVeta08VsFT0A"), multFT0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0C"), multFT0C, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0M"), (multFT0A + multFT0C), multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFV0A"), multFV0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDA"), multFDDA, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDC"), multFDDC, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNMFTTracks"), mftNtracks, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNTPV"), multNTracksPV, multNTracksPV); } if (studies.doNTracksPVeta10VsRawSignals) { - histos.fill(HIST("hNTracksPVeta10VsFT0A"), multFT0A, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFT0C"), multFT0C, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFT0M"), (multFT0A + multFT0C), multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFV0A"), multFV0A, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFDDA"), multFDDA, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsFDDC"), multFDDC, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsNMFTTracks"), mftNtracks, multNTracksPV10); - histos.fill(HIST("hNTracksPVeta10VsNTPV"), multNTracksPV, multNTracksPV10); - - // per run - if (studies.doRunByRunHistograms) { - getHist(histPath + "hNTracksPVeta10VsFT0A")->Fill(multFT0A, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFT0C")->Fill(multFT0C, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFT0M")->Fill(multFT0A + multFT0C, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFV0A")->Fill(multFV0A, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFDDA")->Fill(multFDDA, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsFDDC")->Fill(multFDDC, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsNMFTTracks")->Fill(mftNtracks, multNTracksPV10); - getHist(histPath + "hNTracksPVeta10VsNTPV")->Fill(multNTracksPV, multNTracksPV10); - } + fillHist(HIST("hNTracksPVeta10VsFT0A"), multFT0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0C"), multFT0C, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0M"), (multFT0A + multFT0C), multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFV0A"), multFV0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDA"), multFDDA, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDC"), multFDDC, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNMFTTracks"), mftNtracks, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNTPV"), multNTracksPV, multNTracksPV10); } if constexpr (requires { collision.multMCExtraId(); }) { @@ -1158,22 +1056,13 @@ struct CentralityStudy { // if the table has centrality information // process FT0C centrality plots if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { - histos.fill(HIST("hCentrality"), centFT0C); - histos.fill(HIST("hNContribsVsCentrality"), centFT0C, collision.multPVTotalContributors()); - histos.fill(HIST("hNITSTPCTracksVsCentrality"), centFT0C, collision.multNTracksITSTPC()); - histos.fill(HIST("hNITSOnlyTracksVsCentrality"), centFT0C, collision.multNTracksITSOnly()); - histos.fill(HIST("hNGlobalTracksVsCentrality"), centFT0C, collision.multNTracksGlobal()); - histos.fill(HIST("hNMFTTracksVsCentrality"), centFT0C, collision.mftNtracks()); - histos.fill(HIST("hPVChi2VsCentrality"), centFT0C, collision.multPVChi2()); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hCentrality")->Fill(centFT0C); - getHist(histPath + "hNContribsVsCentrality")->Fill(centFT0C, collision.multPVTotalContributors()); - getHist(histPath + "hNITSTPCTracksVsCentrality")->Fill(centFT0C, collision.multNTracksITSTPC()); - getHist(histPath + "hNITSOnlyTracksVsCentrality")->Fill(centFT0C, collision.multNTracksITSOnly()); - getHist(histPath + "hNGlobalTracksVsCentrality")->Fill(centFT0C, collision.multNTracksGlobal()); - getHist(histPath + "hNMFTTracksVsCentrality")->Fill(centFT0C, collision.mftNtracks()); - getHist(histPath + "hPVChi2VsCentrality")->Fill(centFT0C, collision.multPVChi2()); - } + fillHist(HIST("hCentrality"), centFT0C); + fillHist(HIST("hNContribsVsCentrality"), centFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNITSTPCTracksVsCentrality"), centFT0C, collision.multNTracksITSTPC()); + fillHist(HIST("hNITSOnlyTracksVsCentrality"), centFT0C, collision.multNTracksITSOnly()); + fillHist(HIST("hNGlobalTracksVsCentrality"), centFT0C, collision.multNTracksGlobal()); + fillHist(HIST("hNMFTTracksVsCentrality"), centFT0C, collision.mftNtracks()); + fillHist(HIST("hPVChi2VsCentrality"), centFT0C, collision.multPVChi2()); if (studies.doOccupancyStudyVsCentrality2d) { histos.fill(HIST("hNcontribsProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multPVTotalContributors()); histos.fill(HIST("hNGlobalTracksProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multNTracksGlobal()); @@ -1192,50 +1081,40 @@ struct CentralityStudy { if constexpr (requires { collision.has_multBC(); }) { if (collision.has_multBC()) { auto multbc = collision.template multBC_as>(); - histos.fill(HIST("hFV0AOuter_Collisions"), multbc.multFV0AOuter() * scale.factorFV0A); - histos.fill(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); - histos.fill(HIST("hFT0MOuterA_Collisions"), multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); - - histos.fill(HIST("hFT0COuter_Collisions"), multbc.multFT0COuter() * scale.factorFT0C); - histos.fill(HIST("hFIT_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); - histos.fill(HIST("hFIT_FullV0_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); - histos.fill(HIST("hFIT_All_Collisions"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); + fillHist(HIST("hFV0AOuter_Collisions"), multbc.multFV0AOuter() * scale.factorFV0A); + fillHist(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); + fillHist(HIST("hFT0MOuterA_Collisions"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); + + fillHist(HIST("hFT0COuter_Collisions"), multbc.multFT0COuter() * scale.factorFT0C); + fillHist(HIST("hFIT_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + fillHist(HIST("hFIT_FullV0_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + fillHist(HIST("hFIT_All_Collisions"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); if (studies.do2DPlots) { - histos.fill(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); + fillHist(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); + } + if (studies.doNGlobalTracksVsRawSignals) { + fillHist(HIST("hNGlobalTracksVsFT0MOuterA"), multbc.multFT0AOuter() + multbc.multFT0C(), multNTracksGlobal); } const uint64_t bcTimestamp = multbc.timestamp(); const float interactionRate = mRateFetcher.fetch(ccdb.service, bcTimestamp, mRunNumber, ccdbSettings.irSource.value, ccdbSettings.irCrashOnNull) / 1000.; // kHz - histos.fill(HIST("hInteractionRate"), interactionRate); + fillHist(HIST("hInteractionRate"), interactionRate); if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { - histos.fill(HIST("hInteractionRateVsCentrality"), centFT0C, interactionRate); - if (studies.doRunByRunHistograms) { - getHist(histPath + "hInteractionRateVsCentrality")->Fill(centFT0C, interactionRate); - } + fillHist(HIST("hInteractionRateVsCentrality"), centFT0C, interactionRate); } - if (studies.doRunByRunHistograms) { - getHist(histPath + "hInteractionRate")->Fill(interactionRate); - getHist(histPath + "hFT0AOuter_Collisions")->Fill(multbc.multFT0AOuter() * scale.factorFT0A); - getHist(histPath + "hFV0AOuter_Collisions")->Fill(multbc.multFV0AOuter() * scale.factorFV0A); - getHist(histPath + "hFT0MOuterA_Collisions")->Fill(multbc.multFT0AOuter() + multbc.multFT0C() * scale.factorFT0M); - if (studies.do2DPlots) { - getHist(histPath + "hFT0AOuterVsFT0C")->Fill(multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); - } - - if (studies.doTimeStudies) { - const float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; - getHist(histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); - getHist(histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); - getHist(histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); - getHist(histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); - getHist(histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, multbc.multFV0AOuter()); - getHist(histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); - getHist(histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); - getHist(histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); - getHist(histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); - getHist(histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); - getHist(histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); - } + if (studies.doRunByRunHistograms && studies.doTimeStudies) { + const float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; + getHist(histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); + getHist(histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); + getHist(histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); + getHist(histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); + getHist(histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, multbc.multFV0AOuter()); + getHist(histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); + getHist(histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); + getHist(histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); + getHist(histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); + getHist(histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); + getHist(histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); } } } From df4ded4845cefc5383066ab1c7311df0467da406 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:49:03 +0100 Subject: [PATCH 441/648] [PWGCF] Updates in femto framework (#18464) --- PWGCF/Femto/Core/baseSelection.h | 10 +- PWGCF/Femto/Core/selectionContainer.h | 58 ++++++--- PWGCF/Femto/Core/twoTrackResonanceBuilder.h | 116 ++++++++++++++++++ PWGCF/Femto/DataModel/FemtoTables.h | 4 + .../femtoProducerDerivedToDerived.cxx | 42 +++++++ 5 files changed, 209 insertions(+), 21 deletions(-) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 5e5b0deab97..3e58511914b 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -78,7 +78,7 @@ class BaseSelection { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, @@ -112,7 +112,7 @@ class BaseSelection bool isOptionalCut) { if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, lowerLimit, @@ -146,7 +146,7 @@ class BaseSelection { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, @@ -191,7 +191,7 @@ class BaseSelection mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, false); break; default: - LOG(fatal) << "Invalid switch for boolean selection"; + LOG(fatal) << "Selection '" << selectionName << "': invalid switch for boolean selection"; } addSelection(observableIndex); } @@ -577,7 +577,7 @@ class BaseSelection } if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; + LOG(fatal) << "Selection '" << mSelectionContainers.at(observableIndex).getSelectionName() << "': too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; } } diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index 351ee34b674..0017215b718 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -122,10 +123,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (mSelectionValues.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } // values for selection are not necessarily ordered correctly sortSelections(); @@ -157,13 +158,13 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (mLimitType != limits::kInsideRange && mLimitType != limits::kOutsideRange) { - LOG(fatal) << "Range-string selections only support kInsideRange or kOutsideRange"; + LOG(fatal) << "Selection '" << mSelectionName << "': range-string selections only support kInsideRange or kOutsideRange"; } if (rangeStrings.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (auto const& rangeStr : rangeStrings) { T lower, upper; @@ -196,7 +197,7 @@ class SelectionContainer : (mSelectionRanges[i].first <= mSelectionRanges[i + 1].first && mSelectionRanges[i].second >= mSelectionRanges[i + 1].second); if (!nested) { - LOG(fatal) << "Ranges for minimal cut " << selectionName + LOG(fatal) << "Selection '" << selectionName << "': ranges for minimal cut" << " are not nested. Range [" << mSelectionRanges[i].first << ";" << mSelectionRanges[i].second << "] is not " << (mLimitType == limits::kOutsideRange ? "contained in" : "containing") << " [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; @@ -229,10 +230,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (functions.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (std::size_t i = 0; i < functions.size(); i++) { mSelectionFunctions.emplace_back((mSelectionName + std::to_string(i)).c_str(), functions.at(i).c_str(), lowerLimit, upperLimit); @@ -255,7 +256,7 @@ class SelectionContainer // note: threshold values may be reordered by sortSelections() or sortFunctions(), // so comments must be provided in the already-sorted order if (comments.size() != getNSelections()) { - LOG(fatal) << "Number of comments and number of selections are inconsistent"; + LOG(fatal) << "Selection '" << mSelectionName << "': number of comments and number of selections are inconsistent"; } mComments = comments; } @@ -382,7 +383,7 @@ class SelectionContainer void evaluate(std::vector const& values) { if (values.size() != mSelectionValues.size()) { - LOG(fatal) << "Wrong number of values have been passed"; + LOG(fatal) << "Selection '" << mSelectionName << "': wrong number of values have been passed"; } // reset the bitmask before evaluating mBitmask.reset(); @@ -462,7 +463,7 @@ class SelectionContainer T getLoosestSelection() const { if (mSelectionValues.empty()) { - LOG(fatal) << "No selections configured"; + LOG(fatal) << "Selection '" << mSelectionName << "': no selections configured"; } return mSelectionValues.at(0); } @@ -605,7 +606,7 @@ class SelectionContainer [[nodiscard]] int getBitPosition(int selectionIndex) const { if (selectionIndex == 0 && mSkipMostPermissiveBit) { - LOG(fatal) << "Trying to accessed the bit position of a skipped selection. Breaking..."; + LOG(fatal) << "Selection '" << mSelectionName << "': trying to access the bit position of a skipped selection. Breaking..."; return -1; } if (mSkipMostPermissiveBit) { @@ -706,7 +707,10 @@ class SelectionContainer { auto pos = rangeStr.find(';'); if (pos == std::string::npos) { - LOG(fatal) << "Range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + } + if (rangeStr.find(';', pos + 1) != std::string::npos) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has more than one ';' separator. Expected format: 'lower;upper'"; } std::string lowerStr = rangeStr.substr(0, pos); std::string upperStr = rangeStr.substr(pos + 1); @@ -723,14 +727,36 @@ class SelectionContainer return; } - lower = lowerStr.empty() ? -std::numeric_limits::max() : static_cast(std::stod(lowerStr)); - upper = upperStr.empty() ? std::numeric_limits::max() : static_cast(std::stod(upperStr)); + lower = lowerStr.empty() ? -std::numeric_limits::max() : parseBound(rangeStr, lowerStr); + upper = upperStr.empty() ? std::numeric_limits::max() : parseBound(rangeStr, upperStr); if (lower >= upper) { - LOG(fatal) << "Range string '" << rangeStr << "' has lower bound >= upper bound"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has lower bound >= upper bound"; } } + /// \brief Parse a single range bound and make sure the whole string was consumed, so that + /// malformed input such as "3;5" (extra ';') or "-3,5" (comma used as decimal + /// separator) is rejected instead of silently truncated by std::stod (which happily + /// parses just the numeric prefix "3" or "-3" and ignores the rest). + /// \param rangeStr Full "lower;upper" string, only used for the error message. + /// \param boundStr The individual bound substring (lower or upper) to parse. + T parseBound(std::string const& rangeStr, std::string const& boundStr) const + { + std::size_t idx = 0; + double value = 0.; + try { + value = std::stod(boundStr, &idx); + } catch (std::exception const& e) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr << "' that is not a valid number (" << e.what() << ")"; + } + if (idx != boundStr.size()) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr + << "' with trailing characters ('" << boundStr.substr(idx) << "') that could not be parsed"; + } + return static_cast(value); + } + std::string mSelectionName; std::vector mSelectionValues = {}; ///< Threshold values, sorted from most permissive to most restrictive std::vector mSelectionFunctions; ///< TF1 threshold functions (empty for static selections) diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index fece3d0e06f..26228021eea 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -33,6 +33,7 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include namespace o2::analysis::femto::twotrackresonancebuilder @@ -341,5 +342,120 @@ class TwoTrackResonanceBuilder }; // namespace twotrackresonancebuilder +struct ConfTwoTrackResonanceTablesDerivedToDerived : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoTrackResonanceTables"); + o2::framework::Configurable limitRho0{"limitRho0", 1, "Require at least this many Rho0s in the collision. Set to 0 to skip Rho0s entirely (not written to output)."}; + o2::framework::Configurable limitPhi{"limitPhi", 0, "Require at least this many Phis in the collision. Set to 0 to skip Phis entirely (not written to output)."}; +}; + +struct TwoTrackResonanceBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedRho0s; + o2::framework::Produces producedRho0Masks; + o2::framework::Produces producedPhis; + o2::framework::Produces producedPhiMasks; +}; + +class TwoTrackResonanceBuilderDerivedToDerived +{ + public: + template + void init(T& config) + { + mLimitRho0 = config.limitRho0.value; + mLimitPhi = config.limitPhi.value; + + if (mLimitRho0 < 0 || mLimitPhi < 0) { + LOG(fatal) << "Two-track resonance limits must be non-negative (got " << mLimitRho0 << " and " << mLimitPhi << "). Breaking..."; + } + if (mLimitRho0 == 0 && mLimitPhi == 0) { + LOG(fatal) << "Both Rho0 limit and Phi limit are 0. Breaking..."; + } + } + + template + bool collisionHasTooFewRho0s(T1 const& col, T2 const& /*rho0Table*/, T3& partitionRho0, T4& cache) + { + if (mLimitRho0 == 0) { // Rho0s disabled, cannot reject on them + return false; + } + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return rho0Slice.size() < static_cast(mLimitRho0); + } + + template + bool collisionHasTooFewPhis(T1 const& col, T2 const& /*phiTable*/, T3& partitionPhi, T4& cache) + { + if (mLimitPhi == 0) { // Phis disabled, cannot reject on them + return false; + } + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return phiSlice.size() < static_cast(mLimitPhi); + } + + template + void processRho0s(T1 const& col, T2 const& /*rho0Table*/, T3 const& oldTrackTable, T4& partitionRho0, T5& trackBuilder, T6& cache, T7& newRho0Table, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitRho0 == 0) { // Rho0s disabled + return; + } + + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& rho0 : rho0Slice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newRho0Table.producedRho0s(newCollisionTable.producedCollision.lastIndex(), + rho0.pt(), + rho0.eta(), + rho0.phi(), + rho0.mass(), + posDaughterIndex, + negDaughterIndex); + newRho0Table.producedRho0Masks(rho0.maskPosDau(), + rho0.posDauHasHighMomentum(), + rho0.maskNegDau(), + rho0.negDauHasHighMomentum()); + } + } + + template + void processPhis(T1 const& col, T2 const& /*phiTable*/, T3 const& oldTrackTable, T4& partitionPhi, T5& trackBuilder, T6& cache, T7& newPhiTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitPhi == 0) { // Phis disabled + return; + } + + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& phi : phiSlice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newPhiTable.producedPhis(newCollisionTable.producedCollision.lastIndex(), + phi.pt(), + phi.eta(), + phi.phi(), + phi.mass(), + posDaughterIndex, + negDaughterIndex); + newPhiTable.producedPhiMasks(phi.maskPosDau(), + phi.posDauHasHighMomentum(), + phi.maskNegDau(), + phi.negDauHasHighMomentum()); + } + } + + private: + int mLimitRho0 = 0; + int mLimitPhi = 0; +}; + } // namespace o2::analysis::femto::twotrackresonancebuilder #endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 029e5d3632e..60e4527d93a 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -552,12 +552,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhis_001, "FPHI", 1, //! femto phis femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FPhis = FPhis_001; +using StoredFPhis = StoredFPhis_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhiMasks_001, "FPHIMASK", 1, //! mask for phis femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FPhiMasks = FPhiMasks_001; +using StoredFPhiMasks = StoredFPhiMasks_001; // table for kstars DECLARE_SOA_TABLE_STAGED_VERSIONED(FKstar0s_001, "FKSTAR0", 1, //! femto k0star @@ -599,12 +601,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0s_001, "FRHO0", 1, //! femto rho0s femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FRho0s = FRho0s_001; +using StoredFRho0s = StoredFRho0s_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0Masks_001, "FRHO0MASK", 1, //! rho0s masks femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FRho0Masks = FRho0Masks_001; +using StoredFRho0Masks = StoredFRho0Masks_001; namespace femtov0s { diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 3ca85915f55..3ce6b84aca3 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -47,6 +48,8 @@ struct FemtoProducerDerivedToDerived { using FemtoTracksWithMass = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + using FemtoPhis = o2::soa::Join; using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; using FemtoSigma = o2::soa::Join; @@ -93,6 +96,19 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + // two-track resonance builder + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerived resonanceBuilder; + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerivedProducts resonanceBuilderProducts; + twotrackresonancebuilder::ConfTwoTrackResonanceTablesDerivedToDerived confResonanceBuilder; + + twotrackresonancebuilder::ConfRho0Selection rho0Selection; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + + twotrackresonancebuilder::ConfPhiSelection phiSelection; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + // kink builder kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; kinkbuilder::KinkBuilderDerivedToDerivedProducts kinkBuilderProducts; @@ -143,6 +159,7 @@ struct FemtoProducerDerivedToDerived { static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + static_cast(doprocessTracksSigmaPlus) + + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + static_cast(doprocessTracksSigmaPlusMc); if (activeProcesses != 1) { @@ -153,6 +170,7 @@ struct FemtoProducerDerivedToDerived { v0Builder.init(confV0Builder); cascadeBuilder.init(confCascadeBuilder); kinkBuilder.init(confKinkBuilder); + resonanceBuilder.init(confResonanceBuilder); mcBuilder.init(confMcBuilder); } @@ -274,6 +292,30 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); + void processTracksRho0(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoRho0s const& rho0s) + { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { + return; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksRho0, "Process rho0 and tracks", false); + + void processTracksPhi(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoPhis const& phis) + { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { + return; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + resonanceBuilder.processPhis(col, phis, tracks, phiPartition, trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); + // mc processing // // NOTE: unlike the other process functions, this one subscribes to the full // collision table and iterates manually. The MC index maps are keyed by source From 5790a61b307174de7b9bb597339733c25feed432 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:19:07 +0100 Subject: [PATCH 442/648] [PWGLF] Fix MC process function for L1405 (#18446) Co-authored-by: Marcello Di Costanzo Co-authored-by: ALICE Action Bot --- PWGLF/DataModel/LFLambda1405Table.h | 7 +- PWGLF/Tasks/Resonances/lambda1405analysis.cxx | 96 +++++++++++++------ 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/PWGLF/DataModel/LFLambda1405Table.h b/PWGLF/DataModel/LFLambda1405Table.h index 44339682b7c..571399d6b5a 100644 --- a/PWGLF/DataModel/LFLambda1405Table.h +++ b/PWGLF/DataModel/LFLambda1405Table.h @@ -45,6 +45,7 @@ DECLARE_SOA_COLUMN(NSigmaTPCPrKink, nSigmaTPCPrKink, float); //! Number of sigma DECLARE_SOA_COLUMN(NSigmaTOFPrKink, nSigmaTOFPrKink, float); //! Number of sigmas for the proton candidate from Sigma kink in TOF DECLARE_SOA_COLUMN(DcaKinkDauToPv, dcaKinkDauToPv, float); //! DCA of the kink daughter to the primary vertex DECLARE_SOA_COLUMN(DcaSigmaToPv, dcaSigmaToPv, float); //! DCA of the sigma to the primary vertex +DECLARE_SOA_COLUMN(KinkChi2PCA, kinkChi2PCA, float); //! Chi2 DCA fitter at the PCA DECLARE_SOA_COLUMN(NSigmaTPCPiDau, nSigmaTPCPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TPC DECLARE_SOA_COLUMN(NSigmaTOFPiDau, nSigmaTOFPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TOF @@ -111,7 +112,7 @@ DECLARE_SOA_TABLE(Lambda1405Cands, "AOD", "LAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DcaKinkDauToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, lambda1405::ScalarProd, lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PoolBin); @@ -125,7 +126,7 @@ DECLARE_SOA_TABLE(Lambda1405CandsMC, "AOD", "MCLAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DcaKinkDauToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, lambda1405::PtMC, lambda1405::MassMC, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, lambda1405::Centrality, lambda1405::Occupancy); @@ -151,7 +152,7 @@ DECLARE_SOA_TABLE(Lambda1405SigmaEffMC, "AOD", "MCL1405SIGEFF", lambda1405::ZKinkVtx, lambda1405::DeltaZKinkVtx, lambda1405::AlphaAPSigma, lambda1405::QtAPSigma, lambda1405::AlphaAPSigmaRecalc, lambda1405::QtAPSigmaRecalc, - lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PvContrib); diff --git a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx index 87b5655c48c..f186a29e35b 100644 --- a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx @@ -102,6 +102,7 @@ struct lambda1405candidate { float kinkPrNSigTpc = -1; // Number of sigmas for the proton candidate from Sigma kink in Tpc float kinkPrNSigTof = -1; // Number of sigmas for the proton candidate from Sigma kink in Tof float kinkDcaDauToPv = -1; // DCA of the kink daughter to the primary vertex + float chi2KinkPCA = -1; // Chi2 of the Sigma candidate at the PCA float bachPiPt = -1; // pT of the pion daughter float bachPiNSigTpc = -1; // Number of sigmas for the pion candidate @@ -149,6 +150,7 @@ struct lambda1405analysis { Configurable cutUpperMass{"cutUpperMass", 1.6f, "Upper mass cut for Lambda(1405) candidates (GeV/c^2)"}; Configurable cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"}; Configurable cutSigmaMass{"cutSigmaMass", 0.1, "Sigma mass window (MeV/c^2)"}; + Configurable cutSigmaChi2PCA{"cutSigmaChi2PCA", 100.f, "Maximum Chi2 PCA for Sigma candidates"}; Configurable cutItsNClusKinkMin{"cutItsNClusKinkMin", 1, "Minimum number of ITS clusters for kink daughter"}; Configurable cutItsNClusKinkMax{"cutItsNClusKinkMax", 3, "Maximum number of ITS clusters for kink daughter"}; Configurable cutNTpcClus{"cutNTpcClus", 90, "Minimum number of Tpc clusters for pion candidate"}; @@ -220,7 +222,7 @@ struct lambda1405analysis { ConfigurableAxis axisPtResolution{"axisPtResolution", {100, -0.5, 0.5}, ""}; ConfigurableAxis axisMassL1405{"axisMassL1405", {500, 1.3, 1.8}, ""}; ConfigurableAxis axisXi1530Mass{"axisXi1530Mass", {500, 1.3, 1.8}, ""}; - ConfigurableAxis axisMassResolution{"axisMassResolution", {100, -0.1, 0.1}, ""}; + ConfigurableAxis axisMassReso{"axisMassReso", {100, -0.1, 0.1}, ""}; ConfigurableAxis axisNSig{"axisNSig", {100, -5., 5.}, ""}; ConfigurableAxis axisSigmaMass{"axisSigmaMass", {300, 1.1, 1.4}, ""}; ConfigurableAxis axisXiMass{"axisXiMass", {300, 1.2, 1.5}, ""}; @@ -251,7 +253,7 @@ struct lambda1405analysis { const AxisSpec ptResolutionAxis{axisPtResolution, "#it{p}_{T}^{rec} - #it{p}_{T}^{gen} (GeV/#it{c})"}; const AxisSpec lambda1405MassAxis{axisMassL1405, "m(#Sigma#pi) (GeV/#it{c}^{2})"}; const AxisSpec xi1530MassAxis{axisXi1530Mass, "m(#Xi#pi) (GeV/#it{c}^{2})"}; - const AxisSpec massResolutionAxis{axisMassResolution, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; + const AxisSpec massResoAxis{axisMassReso, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; const AxisSpec nSigmaAxis{axisNSig, "n#sigma_{#pi}"}; const AxisSpec sigmaMassAxis{axisSigmaMass, "m(#Sigma) (GeV/#it{c}^{2})"}; const AxisSpec xiMassAxis{axisXiMass, "m(#Xi) (GeV/#it{c}^{2})"}; @@ -367,7 +369,7 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(4, "Upper mass sel"); rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(5, "#it{p}_{T} Correl"); rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(6, "Accepted"); - rSelections.add("hSelectionsSigmaPlus", "hSelectionsSigmaPlus", {HistType::kTH1D, {{8, -0.f, 7.5f}}}); + rSelections.add("hSelectionsSigmaPlus", "hSelectionsSigmaPlus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(2, "Kink"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); @@ -376,7 +378,8 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(6, "DCA PV #pi #leftarrow #Sigma"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); - rSelections.add("hSelectionsSigmaMinus", "hSelectionsSigmaMinus", {HistType::kTH1D, {{8, -0.f, 7.5f}}}); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); + rSelections.add("hSelectionsSigmaMinus", "hSelectionsSigmaMinus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(2, "Kink"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); @@ -385,6 +388,7 @@ struct lambda1405analysis { rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(6, "DCA PV Pi #leftarrow #Sigma"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); rSelections.add("hRecalcSigmaPlusMom", "hRecalcSigmaPlusMom", {HistType::kTH1D, {{5, -0.f, 4.5f}}}); rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(1, "All"); rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(2, "Non-null mom."); @@ -438,18 +442,24 @@ struct lambda1405analysis { rSigmaPlus.add("h2DeltaGenRecoPtSigmaPlus", "h2DeltaGenRecoPtSigmaPlus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); rSigmaPlus.add("h2GenSigmaPlusPvContribPt", "h2GenSigmaPlusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rSigmaPlus.add("hGenSigmaPlusRadius", "hGenSigmaPlusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaMinus.add("hGenSigmaMinusRadius", "hGenSigmaMinusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); rSigmaMinus.add("h2DeltaGenRecoPtSigmaMinus", "h2DeltaGenRecoPtSigmaMinus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); rSigmaMinus.add("h2GenSigmaMinusPvContribPt", "h2GenSigmaMinusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); - rLambda1405.add("hRecoL1405", "hRecoL1405;;Counts", {HistType::kTH2F, {{4, -0.5, 3.5}, ptAxis}}); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(1, "Reco #Lambda(1405)"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(2, "Has bach #pi"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(3, "Has mothers"); - rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(4, "Matched"); + rLambda1405.add("hQARecoL1405", "hQARecoL1405;;Counts", {HistType::kTH2F, {{4, -0.5, 3.5}, ptAxis}}); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(1, "Reco #Lambda(1405)"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(2, "Has bach #pi"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(3, "Has mothers"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(4, "Matched"); rLambda1405.add("hGenL1405", "hGenL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); + rLambda1405.add("hRecoL1405", "hRecoL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); rLambda1405.add("h2GenL1405PvContribPt", "h2GenL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); rLambda1405.add("h2RecL1405PvContribPt", "h2RecL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); rLambda1405.add("h2GenSigmaMinusArmPod", "h2GenSigmaMinusArmPod", {HistType::kTH2F, {alphaAxis, qtAxis}}); @@ -463,10 +473,10 @@ struct lambda1405analysis { rLambda1405.add("h2GenSigmaPtVsKinkPtSigmaMinus", "h2GenSigmaPtVsKinkPtSigmaMinus;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); rLambda1405.add("h2GenSigmaPtVsPiKinkPt", "h2GenSigmaPtVsPiKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); rLambda1405.add("h2GenSigmaPtVsPrKinkPt", "h2GenSigmaPtVsPrKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); - rLambda1405.add("h2MassResolutionFromSigmaMinus", "h2MassResolutionFromSigmaMinus", {HistType::kTH2F, {lambda1405MassAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolutionFromSigmaMinus", "h2PtResolutionFromSigmaMinus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); - rLambda1405.add("h2MassResolutionFromSigmaPlus", "h2MassResolutionFromSigmaPlus", {HistType::kTH2F, {lambda1405MassAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolutionFromSigmaPlus", "h2PtResolutionFromSigmaPlus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2MassResoFromSigmaMinus", "h2MassResoFromSigmaMinus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaMinus", "h2PtResoFromSigmaMinus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2MassResoFromSigmaPlus", "h2MassResoFromSigmaPlus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaPlus", "h2PtResoFromSigmaPlus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); rLambda1405.add("h2PtMassMC", "h2PtMassMC", {HistType::kTH2F, {ptAxis, lambda1405MassAxis}}); } @@ -964,6 +974,15 @@ struct lambda1405analysis { rSelections.fill(HIST("hSelectionsSigmaPlus"), 6); // Passed radius sel } + if (lambda1405Cand.chi2KinkPCA > selections.cutSigmaChi2PCA) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 7); // Passed radius sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 7); // Passed radius sel + } + auto kinkDauMom = std::array{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; // Sigma properties lambda1405Cand.sigmaId = sigmaCand.globalIndex(); @@ -995,6 +1014,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPrNSigTpc = kinkDauTrack.tpcNSigmaPr(); lambda1405Cand.kinkPrNSigTof = kinkDauTrack.tofNSigmaPr(); lambda1405Cand.kinkDcaDauToPv = sigmaCand.dcaDaugPv(); + lambda1405Cand.chi2KinkPCA = sigmaCand.dcaKinkTopo(); rSelections.fill(HIST("hSelectionsL1405"), 1); // Passed Sigma sel @@ -1094,12 +1114,12 @@ struct lambda1405analysis { template bool checkSigmaKinkMC(const mcTrack& mcTrackSigma, const mcTrack& mcTrackKinkDau, float sigmaAbsPDG, float kinkAbsPDG, aod::McParticles const&) { - if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { - return false; // Not a valid Sigma kink decay - } if (!mcTrackKinkDau.has_mothers()) { return false; // No mothers found } + if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { + return false; // Not a valid Sigma kink decay + } // Check if the kink comes from the Sigma bool isKinkFromSigma = false; for (const auto& mcMother : mcTrackKinkDau.template mothers_as()) { @@ -1151,7 +1171,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPt, lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, - lambda1405Cand.kinkDcaDauToPv, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, lambda1405Cand.scalarProd, lambda1405Cand.centMult, lambda1405Cand.occupancy, lambda1405Cand.poolBin); // 24 @@ -1275,6 +1295,7 @@ struct lambda1405analysis { std::vector arrL1405Daugs = {}; std::array finalState; int decayChannel = -1; + bool isMatched = false; // Match L(1405) --> n pi- pi+ final state if (pdgMother > 0) { // Change sign of neutral decay products @@ -1283,6 +1304,7 @@ struct lambda1405analysis { finalState = {-PDG_t::kNeutron, PDG_t::kPiMinus, PDG_t::kPiPlus}; } if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; // Match the intermediate Sigma RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { @@ -1309,6 +1331,7 @@ struct lambda1405analysis { } if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; // Match the intermediate Sigma RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { @@ -1323,6 +1346,10 @@ struct lambda1405analysis { } } + if (!isMatched) { + return decayChannel; // No valid decay channel found + } + std::vector arrSigmaDaugs = {}; auto sigmaDaug = mcParticles.rawIteratorAt(daugsIdxs[1]); RecoDecay::getDaughters(sigmaDaug, &arrSigmaDaugs, std::array{0}, 1); @@ -1366,9 +1393,11 @@ struct lambda1405analysis { auto labelKinkDaug = trackLabelsMC.rawIteratorAt(sigmaCand.trackDaugId()); if (!labelSigma.has_mcParticle()) { rSelections.fill(HIST("hRecoNotMatchedCounter"), 1); // Sigma not matched + continue; // Skip if no valid MC association } if (!labelKinkDaug.has_mcParticle()) { rSelections.fill(HIST("hRecoNotMatchedCounter"), 2); // Kink daughter not matched + continue; // Skip if no valid MC association } auto genSigma = labelSigma.template mcParticle_as(); auto genKinkDaug = labelKinkDaug.template mcParticle_as(); @@ -1390,7 +1419,7 @@ struct lambda1405analysis { const int dummyPoolBin{-1}; // Not used in MC, but required by the function signature constructCollCandidates(collision, sigmaCand, tracksPerCol, selectedCandidates, dummyPoolBin); for (const auto& lambda1405Cand : selectedCandidates) { - rLambda1405.fill(HIST("hRecoL1405"), 0., lambda1405Cand.pt()); // All reconstructed + rLambda1405.fill(HIST("hQARecoL1405"), 0., lambda1405Cand.pt()); // All reconstructed // Do MC association auto labelBachPi = trackLabelsMC.rawIteratorAt(lambda1405Cand.bachPiId); @@ -1403,12 +1432,12 @@ struct lambda1405analysis { if (std::abs(genBachPi.pdgCode()) != PDG_t::kPiPlus) { continue; // Skip if not a valid pion candidate } - rLambda1405.fill(HIST("hRecoL1405"), 1., lambda1405Cand.pt()); // Has bach pi + rLambda1405.fill(HIST("hQARecoL1405"), 1., lambda1405Cand.pt()); // Has bach pi if (!genSigma.has_mothers() || !genBachPi.has_mothers()) { continue; // Skip if no mothers found } - rLambda1405.fill(HIST("hRecoL1405"), 2., lambda1405Cand.pt()); // Has mothers for Sigma and Pi + rLambda1405.fill(HIST("hQARecoL1405"), 2., lambda1405Cand.pt()); // Has mothers for Sigma and Pi // check that labpi and labsigma have the same mother (a lambda1405 candidate) int lambda1405Id = -1; @@ -1423,19 +1452,28 @@ struct lambda1405analysis { if (lambda1405Id == -1) { continue; // Skip if the Sigma and pion do not share the same lambda1405 candidate } - rLambda1405.fill(HIST("hRecoL1405"), 3., lambda1405Cand.pt()); // Has same mother + rLambda1405.fill(HIST("hQARecoL1405"), 3., lambda1405Cand.pt()); // Matched + + std::array idxsSigmaKinkBachPi{-1, -1, -1}; + auto genLambda1405 = particlesMC.rawIteratorAt(lambda1405Id); + int decayChannel = matchGenDecay(genLambda1405, particlesMC, idxsSigmaKinkBachPi); + if (decayChannel == -1) { + continue; // Skip if it doesn't match the decay channels of interest + } + rLambda1405.fill(HIST("hRecoL1405"), decayChannel, genLambda1405.pt()); auto lambda1405Mother = particlesMC.rawIteratorAt(lambda1405Id); float lambda1405Mass = std::sqrt(lambda1405Mother.e() * lambda1405Mother.e() - lambda1405Mother.p() * lambda1405Mother.p()); + rLambda1405.fill(HIST("h2RecL1405PvContribPt"), collision.numContrib(), lambda1405Cand.pt()); if (lambda1405Cand.isSigmaMinus) { rLambda1405.fill(HIST("h2SigmaMinusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2MassResolutionFromSigmaMinus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); - rLambda1405.fill(HIST("h2PtResolutionFromSigmaMinus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2MassResoFromSigmaMinus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaMinus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (lambda1405Cand.isSigmaPlus) { rLambda1405.fill(HIST("h2SigmaPlusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2MassResolutionFromSigmaPlus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); - rLambda1405.fill(HIST("h2PtResolutionFromSigmaPlus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2MassResoFromSigmaPlus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaPlus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (fillOutputTree) { @@ -1446,7 +1484,7 @@ struct lambda1405analysis { lambda1405Cand.kinkPt, lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, - lambda1405Cand.kinkDcaDauToPv, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, lambda1405Mother.pt(), lambda1405Mass, genSigma.pdgCode(), genBachPi.pdgCode(), lambda1405Cand.centMult, lambda1405Cand.occupancy); @@ -1505,18 +1543,21 @@ struct lambda1405analysis { rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaMinus"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaMinusPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsKinkPtSigmaMinus"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaMinus.fill(HIST("hGenSigmaMinusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } if (decayChannel == kSigmaPlusPiToPiPiNeutron) { rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPi"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPiPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsPiKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } if (decayChannel == kSigmaPlusPiToPiPiProton) { rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPr"), mcPart.pt(), bachPi.pt()); rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPrPt"), mcPart.pt(), sigmaDaug.pt()); rLambda1405.fill(HIST("h2GenSigmaPtVsPrKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } } } @@ -1619,7 +1660,6 @@ struct lambda1405analysis { sigmaMomReco[1] *= scale; sigmaMomReco[2] *= scale; - sigmaPt = std::sqrt(sigmaMomReco[0] * sigmaMomReco[0] + sigmaMomReco[1] * sigmaMomReco[1]); if (isSigmaMinusKink) { rSigmaMinus.fill(HIST("hDeltaPxRecalcSigmaMinus"), sigmaMomReco[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); rSigmaMinus.fill(HIST("hDeltaPyRecalcSigmaMinus"), sigmaMomReco[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); @@ -1675,7 +1715,7 @@ struct lambda1405analysis { sigmaCand.zDecVtx(), sigmaCand.zDecVtx() - genKinkDaug.vz(), recoSigmaAlphaAP, recoSigmaQtAP, recoRecalcPtSigmaAlphaAP, recoRecalcPtSigmaQtAP, - sigmaCand.dcaDaugPv(), sigmaCand.dcaMothPv(), + sigmaCand.dcaDaugPv(), sigmaCand.dcaMothPv(), sigmaCand.dcaKinkTopo(), genSigma.pdgCode(), genKinkDaug.pdgCode(), centMult, collision.ft0cOccupancyInTimeRange(), collision.numContrib()); } From e2555793c83bac7fb820cefac7a4f17a695ae5d9 Mon Sep 17 00:00:00 2001 From: mdaniel-14 Date: Mon, 26 Oct 2026 16:25:03 +0200 Subject: [PATCH 443/648] [PWGCF] adding a new task for correlation studies (#18461) --- .../Tasks/CMakeLists.txt | 5 + .../Tasks/multiparticleCorrelationsDm.cxx | 849 ++++++++++++++++++ 2 files changed, 854 insertions(+) create mode 100644 PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx diff --git a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt index a20000206b2..32c916c2275 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt @@ -38,3 +38,8 @@ o2physics_add_dpl_workflow(multiparticle-correlations-mei SOURCES multiparticleCorrelationsMei.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiparticle-correlations-dm + SOURCES multiparticleCorrelationsDm.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx new file mode 100644 index 00000000000..225e59faaf0 --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx @@ -0,0 +1,849 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multiparticleCorrelationsDm.cxx +/// \brief Task to calculate multiparticle correlations and related observables +/// \author molnard@tcd.ie + +// O2: + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" // needed for aod::TracksDCA table + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; // use in json "isMC": "true" for "event-selection-task" +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +// *) ROOT and C++ include's: +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// *) Define enums: +enum ERecSim { ERec = 0, + ESim, + ERecAndSim }; + +enum EProcess { + EProcessRec = 0, // Run 3, only reconstructed + EProcessRecSim, // Run 3, both reconstructed and simulated + EProcessSim, // Run 3, only simulated + EProcess_N +}; + +enum ECentralityEstimator { + ECentFT0C = 0, // default + ECentFT0M, + ECentFT0A, + ECentralityEstimator_N +}; + +enum EReferenceMultiplicityEstimator { + ERefMultTPC = 0, + ERefMultFT0C, + ERefMultFT0M, + EReferenceMultiplicityEstimator_N +}; + +enum EEventHistograms { // this is for cuts + EMultiplicity = 0, + EReferenceMultiplicity, + ECentrality, + EImpactParameter, // only simulated + EVertexX, + EVertexY, + EVertexZ, + EEventHistograms_N +}; + +enum EParticleHistograms { // this is for cuts + EPt = 0, + EPhi, + EParticleHistograms_N +}; + +enum EBeforeAfter { + EBefore = 0, + EAfter, + EBeforeAfter_N +}; + +// *) Main task: +struct StudentO2Physics { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root + + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function + OutputObj fBaseList{sBaseListName.Data(), + OutputObjHandlingPolicy::AnalysisObject, + OutputObjSourceType::OutputObjSource}; + + // *) CCDB: + // Service ccdb; // support for offline callibration data base, not needed for the time being... + // to be able to use ccdb object in the analysis task + Service ccdb; + + // *) Define configurables: + + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfFillQA{"cfFillQA", false, "book and fill QA histograms (only for development and validation)"}; // switch for QA histograms + Configurable cfUseVertexZCut{"cfUseVertexZCut", true, "apply the vertex Z event cut"}; + Configurable cfUsePtCut{"cfUsePtCut", true, "apply the pt particle cut"}; + + Configurable cfCentralityEstimator{"cfCentralityEstimator", "FT0C", "centrality estimator: FT0C, FT0M, FT0A"}; // centrality estimator with valid values: FT0C (default) + Configurable cfReferenceMultiplicityEstimator{"cfReferenceMultiplicityEstimator", "FT0C", "reference multiplicity estimator: TPC, FT0C, FT0M"}; // ref. multiplicity estimator with valid values: TPC (default) FT0C Ft0M + + // sigma_inel in fm^2 (1 b = 100 fm^2): Pb+Pb at 5.36 TeV = 7.71 b = 771 fm^2 (PH2278 Lecture 2, slide 33), change for other system or energy + Configurable cfSigmaInel{"cfSigmaInel", 771., "sigma_inel in fm^2 (1 b = 100 fm^2), Pb+Pb 5.36 TeV = 771"}; + + Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPhiBins{"cfPhiBins", {360, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfMultiplicityBins{"cfMultiplicityBins", {5000, 0., 5000.}, "nMultBins, multMin, multMax"}; + Configurable> cfRefmultiplicityBins{"cfRefmultiplicityBins", {5000, 0., 5000.}, "nRefMultBins, refMultMin, refMultMax"}; // reference multiplicity + Configurable> cfImpactparameterBins{"cfImpactparameterBins", {200, 0., 20.}, "nBBins, bMin, bMax (fm)"}; // impact parameter + Configurable> cfCentralityBins{"cfCentralityBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; + Configurable> cfVertexXBins{"cfVertexXBins", {200, -1., 1.}, "nvxBins, vxMin, vxMax"}; + Configurable> cfVertexYBins{"cfVertexYBins", {200, -1, 1.}, "nvyBins, vyMin, vyMax"}; + Configurable> cfVertexZBins{"cfVertexZBins", {300, -15., 15.}, "nvzBins, vzMin, vzMax"}; + Configurable> cfVertexZ{"cfVertexZ", {-10., 10.}, "{min, max} [cm]"}; + Configurable> cfPt{"cfPt", {0.2, 5.0}, "{min, max} [GeV]"}; + + // for weights (efficiency corrections) + Configurable cfFileWithWeights{"cfFileWithWeights", "/scratch8/go26dek/tutoarials/3/DatasetsO2/weights.root", "path to external ROOT file which holds all particle weights"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + bool fProcess[EProcess_N] = {false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fFillQA = false; // book and fill QA histos + bool fUseVertexZCut = true; + bool fUsePtCut = true; + + int fCentralityEstimator = ECentFT0C; + int fReferenceMultiplicityEstimator = ERefMultTPC; + + float fSigmaInel = 771.; // sigma inel [fm^2] + float fVertexZ[2] = {-10., 10.}; // {min, max} [cm] + float fPt[2] = {0.2, 5.0}; + bool fWeightsLoaded = false; // weights are loaded once, at the first collision + + } tc; // you have to prepend "tc." for all objects name in this group later in the code + + // **) Particle histograms: + struct ParticleHistograms { + TList* fParticleHistogramsList = NULL; //! + float getCentrality(T const& collision) + { + switch (tc.fCentralityEstimator) { + case ECentFT0C: + return collision.centFT0C(); + case ECentFT0M: + return collision.centFT0M(); + case ECentFT0A: + return collision.centFT0A(); + default: + LOGF(fatal, "Unknown centrality estimator"); + return -1.; + } + } + + // returns reference multiplicity from the estimator selected in json + template + float getReferenceMultiplicity(T const& collision) + { + switch (tc.fReferenceMultiplicityEstimator) { + case ERefMultTPC: + return collision.multTPC(); + case ERefMultFT0C: + return collision.multFT0C(); + case ERefMultFT0M: + return collision.multFT0M(); + default: + LOGF(fatal, "Unknown reference multiplicity estimator"); + return -1.; + } + } + + // centrality from impact parameter: slide 33 PH2278 Lecture 2 + float centralityFromImpactParameter(float b) { return 100. * o2::constants::math::PI * b * b / tc.fSigmaInel; } + + // ______________________ weights _________________________ + + TObject* getObjectFromList(TList* list, const char* objectName) + { + // Last update: 20261018 + + // Get TObject pointer from TList, even if it's in some nested TList. + // Foreseen to be used to fetch histograms or profiles from files directly. + + // Some ideas taken from TCollection::ls() + + // If you have added histograms directly to files (without TList's), then you can fetch them directly with + // file->Get("hist-name"). + + // Example usage: TH1D *hist = (TH1D*) getObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return NULL; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (TString(objectIter->ClassName()).EqualTo("TList")) { + objectFinal = getObjectFromList(reinterpret_cast(objectIter), objectName); + if (objectFinal) + return objectFinal; + } + } // while(objectIter = next()) + + return NULL; + + } // TObject* getObjectFromList(...) + + TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber) + { + // Last update: 20261018 + + // *) Return value: + TH1F* hist = NULL; + TList* baseList = NULL; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = NULL; // nested list with run-wise TList's holding run-specific weights + + // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: + + // Algorithm: a) If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; + // b) If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; + // c) For local files both absolute and relative paths are just fine. + + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + if (TString(filePath).BeginsWith("/alice/cern.ch/")) { + bFileIsInAliEn = true; // file is in AliEn + } else if (TString(filePath).BeginsWith("/alice-ccdb.cern.ch/")) { + bFileIsInCCDB = true; // file is in CCDB + } + + if (bFileIsInAliEn) { + + // File you want to access is in your home directory in AliEn. + // In this branch, there is a special code to support file access in that case. + + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + // add #include for it to work + + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + // Finally, from the top-level TList "ccdb_object", get the desired nested TList. + // The technical problem here is that it can be nested at any level, + // to overcome that there is a helper utility function TObject* getObjectFromList(...), + // whose implementation is discussed and provided separately. + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for AliEn case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else if (bFileIsInCCDB) { + + // File you want to access is in your home directory in CCDB. + // In this branch, there is a special code to support file access in that case. + + // Remember that here I do not access the file; instead, I directly access the object in that file. + // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case + ccdb->setURL("http://alice-ccdb.cern.ch"); // to be able to use the "ccdb" object in your analysis task + baseList = reinterpret_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for CCDB, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else { + + // File you want to access is on your local computer. + // In this branch, there is a special code to support file access in that case. + + // Check if the external ROOT file exists at the specified path: + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + weightsFile->GetObject("ccdb_object", baseList); + + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + // baseList->ls(); + LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); // changed runNumber instead of tc.fRunNumber.Data() + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for local case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } // end of else + + // The common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + // listWithRuns->ls(); + + // get pointer to "hist" from "listWithRuns" + hist = reinterpret_cast(getObjectFromList(listWithRuns, "histWithEfficiencyCorrections_1")); + if (!hist) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + hist->SetDirectory(0); // remove ownerhip from an external file + TH1F* histClone = reinterpret_cast(hist->Clone()); // yes, I have to clone here + + if (!bFileIsInCCDB) { // not for CCDB, there the CCDB manager owns the object and deletes it itself + delete baseList; // release back the memory + } + + return histClone; + + } // end of TH1F* getHistogramWithWeights(...) + + //__ EVENT CUTS __ + template + bool eventCuts(T1 const& collision) + { + + // *) Vertex Z cut + if (tc.fUseVertexZCut) { + if (!(tc.fVertexZ[0] <= collision.posZ() && collision.posZ() < tc.fVertexZ[1])) { + return false; // if at least one event cut is not satisfied + } + } + return true; // all event cuts passed + } + + //__ PARTICLE CUTS __ + template + bool particleCuts(T const& track) + { + + // *) pt cut + if (tc.fUsePtCut) { + if (!(tc.fPt[0] <= track.pt() && track.pt() < tc.fPt[1])) { + return false; // if at least one event cut is not satisfied + } + } + + return true; // all particle cuts passed + } + + //__ Fill all event histograms __ #ba = EBefore or EAfter + template + void fillEventHistograms(T1 const& collision, T2 const& tracks, EBeforeAfter ba) + { + + if constexpr (rs == ERec || rs == ERecAndSim) { + + float centRec = getCentrality(collision); + + ec.fEventHistograms[EMultiplicity][ERec][ba]->Fill(tracks.size()); // defined as total number of available particles + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->Fill(getReferenceMultiplicity(collision)); + ec.fEventHistograms[ECentrality][ERec][ba]->Fill(centRec); + ec.fEventHistograms[EVertexX][ERec][ba]->Fill(collision.posX()); + ec.fEventHistograms[EVertexY][ERec][ba]->Fill(collision.posY()); + ec.fEventHistograms[EVertexZ][ERec][ba]->Fill(collision.posZ()); + + if constexpr (rs == ERecAndSim) { + if (!collision.has_mcCollision()) { + LOGF(warning, " No MC collision for this collision, skip simulated part..."); + return; + } + auto mcCollision = collision.mcCollision(); + + ec.fEventHistograms[EMultiplicity][ESim][ba]->Fill(tracks.size()); + ec.fEventHistograms[EVertexX][ESim][ba]->Fill(mcCollision.posX()); + ec.fEventHistograms[EVertexY][ESim][ba]->Fill(mcCollision.posY()); + ec.fEventHistograms[EVertexZ][ESim][ba]->Fill(mcCollision.posZ()); + + // impact parameter and centrality calculated from MC + float b = mcCollision.impactParameter(); + if (b > 0.) { // only if the impact parameter is stored in the MC + float centSim = centralityFromImpactParameter(b); + ec.fEventHistograms[EImpactParameter][ESim][ba]->Fill(b); + ec.fEventHistograms[ECentrality][ESim][ba]->Fill(centSim); + + // QA centrality sim vs centrality rec after the cuts + if (tc.fFillQA && ba == EAfter) { + qa.fHistCentralityRecVsSim->Fill(centSim, centRec); + } // if (tc.fFillQA && ba == EAfter) + + } // if (b > 0.) + + } // if constexpr (rs == ERecAndSim) + + } // if constexpr (rs == ERec || rs == ERecAndSim) + + } // fillEventHistograms + + template + void steer(T1 const& collision, T2 const& tracks) + { + + // Dry run: + if (tc.fDryRun) { + return; + } + + // Load the weights once, at the first collision, because the run number is only known here + if (!tc.fWeightsLoaded && !cfFileWithWeights.value.empty()) { + histWeights = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", collision.bc().runNumber())); + histWeights->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path + // value takes the text as std::string and c_str makes it into a constant char* + + fBaseList->Add(histWeights); // so it is saved in AnalysisResults.root + tc.fWeightsLoaded = true; + } + + // Print current run number: + LOGF(info, "Run number: %d", collision.bc().runNumber()); + + // Print centrality estimated with "FT0M" estimator: + LOGF(info, "Centrality: %f", getCentrality(collision)); + + // Print vertex X position: + LOGF(info, "Vertex X position: %f", collision.posX()); + + // Fill event histograms before event cuts + fillEventHistograms(collision, tracks, EBefore); + + // Event cuts: leave this collision if it doesn't pass + if (!eventCuts(collision)) { + return; + } + + // Main loop over particles: + auto track = tracks.iteratorAt(0); // set the type and scope from one instance + for (int64_t i = 0; i < tracks.size(); i++) { + + track = tracks.iteratorAt(i); + + // Print track azimuthal angle: + // LOGF(info, "Track azimuthal angle: %f", track.phi()); + + // Fill reconstructed ...: + if constexpr (rs == ERec || rs == ERecAndSim) { + // Fill tracks before cuts: + + pc.fParticleHistograms[EPt][ERec][EBefore]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EBefore]->Fill(track.phi()); + + // ... and corresponding MC truth simulated (before cuts): + // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx + // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo + + if constexpr (rs == ERecAndSim) { + if (track.has_mcParticle()) { + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particle + pc.fParticleHistograms[EPt][ESim][EBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EBefore]->Fill(mcparticle.phi()); + } else { + LOGF(warning, " No MC particle for this track..."); + } + } + + // Particle cuts: skip this track, not the whole event + if (!particleCuts(track)) { + continue; + } + + // Fill tracks after cuts: + + pc.fParticleHistograms[EPt][ERec][EAfter]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EAfter]->Fill(track.phi()); + + if constexpr (rs == ERecAndSim) { + if (!track.has_mcParticle()) { + continue; + } + + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particles + pc.fParticleHistograms[EPt][ESim][EAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EAfter]->Fill(mcparticle.phi()); + + } // end of if constexpr (rs == ERecAndSim) { + + } // if constexpr (rs == ERec || rs == ERecAndSim) { + + } // end of for (int64_t i = 0; i < tracks.size(); i++) { + + // Fill event histograms after event and particle cuts + fillEventHistograms(collision, tracks, EAfter); + + } // end of template void steer(T1 const& collision, T2 const& tracks) { + + // *) Initialize and book all objects: + void init(InitContext&) + { + + // ... code to book and initialize all analysis objects ... + + // *) Set automatically what to process, from an implicit variable "doprocessSomeProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[EProcessRec] = doprocessRec; + tc.fProcess[EProcessRecSim] = doprocessRecSim; + tc.fProcess[EProcessSim] = doprocessSim; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + tc.fFillQA = cfFillQA; + tc.fSigmaInel = cfSigmaInel; + + // vertex Z cut copy the swtich and range + tc.fUseVertexZCut = cfUseVertexZCut; + std::vector lVzCut = cfVertexZ.value; + + tc.fVertexZ[0] = lVzCut[0]; + tc.fVertexZ[1] = lVzCut[1]; + + // pt cut copy switch and range + tc.fUsePtCut = cfUsePtCut; + std::vector lPtCut = cfPt.value; + + tc.fPt[0] = lPtCut[0]; + tc.fPt[1] = lPtCut[1]; + + // translate the centrality estimator string into the enum + if (cfCentralityEstimator.value == "FT0C") { + tc.fCentralityEstimator = ECentFT0C; + } else if (cfCentralityEstimator.value == "FT0M") { + tc.fCentralityEstimator = ECentFT0M; + } else if (cfCentralityEstimator.value == "FT0A") { + tc.fCentralityEstimator = ECentFT0A; + } else { + LOGF(fatal, "Unknown centrality estimator in json: %s", cfCentralityEstimator.value.c_str()); + } + + // same for the reference multiplicity estimator + if (cfReferenceMultiplicityEstimator.value == "TPC") { + tc.fReferenceMultiplicityEstimator = ERefMultTPC; + } else if (cfReferenceMultiplicityEstimator.value == "FT0C") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0C; + } else if (cfReferenceMultiplicityEstimator.value == "FT0M") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0M; + } else { + LOGF(fatal, "Unknown reference multiplicity estimator in json: %s", cfReferenceMultiplicityEstimator.value.c_str()); + } + + // *) Book base list: + TList* temp = new TList(); + temp->SetOwner(true); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + pc.fParticleHistogramsList = new TList(); + pc.fParticleHistogramsList->SetName("ParticleHistograms"); + pc.fParticleHistogramsList->SetOwner(true); + fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + ec.fEventHistogramsList = new TList(); + ec.fEventHistogramsList->SetName("EventHistograms"); + ec.fEventHistogramsList->SetOwner(true); + fBaseList->Add(ec.fEventHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + if (tc.fFillQA) { // only if QA switched on + qa.fQAHistogramsList = new TList(); + qa.fQAHistogramsList->SetName("QA"); + qa.fQAHistogramsList->SetOwner(true); + fBaseList->Add(qa.fQAHistogramsList); + } + + // *) Book (pt distribution and azimuthal-angle histograms) + event quantities with binning defined through configurables in the json file: + std::vector lPtBins = cfPtBins.value; // define local array and initialize it from an array set in the configurables + int nBins = static_cast(lPtBins[0]); + float min = lPtBins[1]; + float max = lPtBins[2]; + + std::vector lPhiBins = cfPhiBins.value; // azimuthal angle + int nPhiBins = static_cast(lPhiBins[0]); + float phiMin = lPhiBins[1]; + float phiMax = lPhiBins[2]; + + std::vector lMultBins = cfMultiplicityBins.value; // multiplicity + int nMultBins = static_cast(lMultBins[0]); + float multMin = lMultBins[1]; + float multMax = lMultBins[2]; + + std::vector lRefmultBins = cfRefmultiplicityBins.value; // reference multiplicity + int nRefMultBins = static_cast(lRefmultBins[0]); + float refMultMin = lRefmultBins[1]; + float refMultMax = lRefmultBins[2]; + + std::vector lCentBins = cfCentralityBins.value; // centrality + int nCentBins = static_cast(lCentBins[0]); + float centMin = lCentBins[1]; + float centMax = lCentBins[2]; + + std::vector lBBins = cfImpactparameterBins.value; // impact parameter + int nBBins = static_cast(lBBins[0]); + float bMin = lBBins[1]; + float bMax = lBBins[2]; + + std::vector lVxBins = cfVertexXBins.value; // vertex X + int nvxBins = static_cast(lVxBins[0]); + float vxMin = lVxBins[1]; + float vxMax = lVxBins[2]; + + std::vector lVyBins = cfVertexYBins.value; // vertex y + int nvyBins = static_cast(lVyBins[0]); + float vyMin = lVyBins[1]; + float vyMax = lVyBins[2]; + + std::vector lVzBins = cfVertexZBins.value; // vertex z + int nvzBins = static_cast(lVzBins[0]); + float vzMin = lVzBins[1]; + float vzMax = lVzBins[2]; + + // histogram booked before and after cuts + const char* baNames[EBeforeAfter_N] = {"before", "after"}; + for (int ba = 0; ba < EBeforeAfter_N; ba++) { + + // par + pc.fParticleHistograms[EPt][ERec][ba] = new TH1F(Form("fHistPt[ERec][%s]", baNames[ba]), Form("pt distribution for reconstructed particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ERec][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ERec][ba]); + + pc.fParticleHistograms[EPt][ESim][ba] = new TH1F(Form("fHistPt[ESim][%s]", baNames[ba]), Form("pt distribution for simulated particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ESim][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ESim][ba]); + + pc.fParticleHistograms[EPhi][ERec][ba] = new TH1F(Form("fHistPhi[ERec][%s]", baNames[ba]), Form("Azimuthal-angle distribution for reconstructed particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ERec][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ERec][ba]); + + pc.fParticleHistograms[EPhi][ESim][ba] = new TH1F(Form("fHistPhi[ESim][%s]", baNames[ba]), Form("Azimuthal-angle distribution for simulated particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ESim][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ESim][ba]); + + // ev + ec.fEventHistograms[EMultiplicity][ERec][ba] = new TH1F(Form("fHistMultiplicity[ERec][%s]", baNames[ba]), Form("Multiplicity for reconstructed particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ERec][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ERec][ba]); + + ec.fEventHistograms[EMultiplicity][ESim][ba] = new TH1F(Form("fHistMultiplicity[ESim][%s]", baNames[ba]), Form("Multiplicity for simulated particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ESim][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ESim][ba]); + + // reference multiplicity, only reconstructed + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba] = new TH1F(Form("fHistReferenceMultiplicity[ERec][%s]", baNames[ba]), Form("Reference multiplicity, reconstructed (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax); + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->GetXaxis()->SetTitle("reference multiplicity"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]); + + ec.fEventHistograms[ECentrality][ERec][ba] = new TH1F(Form("fHistCentrality[ERec][%s]", baNames[ba]), Form("Centrality for reconstructed particles (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ERec][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ERec][ba]); + + // sim from impact parameter + ec.fEventHistograms[ECentrality][ESim][ba] = new TH1F(Form("fHistCentrality[ESim][%s]", baNames[ba]), Form("Centrality for simulated particles, from impact parameter (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ESim][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ESim][ba]); + + // impact parameter, only simulated + ec.fEventHistograms[EImpactParameter][ESim][ba] = new TH1F(Form("fHistImpactParameter[ESim][%s]", baNames[ba]), Form("Impact parameter, simulated (%s cuts)", baNames[ba]), nBBins, bMin, bMax); + ec.fEventHistograms[EImpactParameter][ESim][ba]->GetXaxis()->SetTitle("b (fm)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EImpactParameter][ESim][ba]); + + ec.fEventHistograms[EVertexX][ERec][ba] = new TH1F(Form("fHistVertexX[ERec][%s]", baNames[ba]), Form("Vertex X for reconstructed particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ERec][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ERec][ba]); + + ec.fEventHistograms[EVertexX][ESim][ba] = new TH1F(Form("fHistVertexX[ESim][%s]", baNames[ba]), Form("Vertex X for simulated particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ESim][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ESim][ba]); + + ec.fEventHistograms[EVertexY][ERec][ba] = new TH1F(Form("fHistVertexY[ERec][%s]", baNames[ba]), Form("Vertex Y for reconstructed particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ERec][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ERec][ba]); + + ec.fEventHistograms[EVertexY][ESim][ba] = new TH1F(Form("fHistVertexY[ESim][%s]", baNames[ba]), Form("Vertex Y for simulated particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ESim][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ESim][ba]); + + ec.fEventHistograms[EVertexZ][ERec][ba] = new TH1F(Form("fHistVertexZ[ERec][%s]", baNames[ba]), Form("Vertex Z for reconstructed particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ERec][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ERec][ba]); + + ec.fEventHistograms[EVertexZ][ESim][ba] = new TH1F(Form("fHistVertexZ[ESim][%s]", baNames[ba]), Form("Vertex Z for simulated particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ESim][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ESim][ba]); + + } // end of for (int ba = 0; ba < 2; ba++) + + // QA histograms, booked only if cfFillQA is true + if (tc.fFillQA) { + qa.fHistCentralityRecVsSim = new TH2F("fHistCentralityRecVsSim", "Centrality: reconstructed vs. simulated", nCentBins, centMin, centMax, nCentBins, centMin, centMax); + qa.fHistCentralityRecVsSim->GetXaxis()->SetTitle("centrality simulated (%)"); + qa.fHistCentralityRecVsSim->GetYaxis()->SetTitle("centrality reconstructed (%)"); + qa.fQAHistogramsList->Add(qa.fHistCentralityRecVsSim); + } + + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // ... + + // *) steer all analysis steps: + steer(collision, tracks); + } + PROCESS_SWITCH(StudentO2Physics, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // ------------------------------------------- + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + steer(collision, tracks); + } + PROCESS_SWITCH(StudentO2Physics, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // ------------------------------------------- + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(StudentO2Physics, processSim, "process only simulated data", false); + +}; // struct StudentO2Physics { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} // WorkflowSpec... From 0f15e9680d323d1c890c4f3a7d521b06aabbe649 Mon Sep 17 00:00:00 2001 From: bhattdurgesh921-sys Date: Mon, 26 Oct 2026 20:03:14 +0530 Subject: [PATCH 444/648] =?UTF-8?q?[PWGLF]=20fix=20the=20wrong=20order=20o?= =?UTF-8?q?f=20some=20histograms=20and=20also=20fix=20some=20bugs=20in=20e?= =?UTF-8?q?ven=E2=80=A6=20(#18465)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tasks/Resonances/lambda1520analysispo.cxx | 208 +++++++++--------- 1 file changed, 109 insertions(+), 99 deletions(-) diff --git a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx index 8cbe7eb227c..b2e09900706 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx @@ -43,7 +43,6 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; -// FIX name/namespace: use snake_case for namespace namespace lambda_analysis { // Named PDG codes — fixes pdg/explicit-code and magic-number errors @@ -55,13 +54,11 @@ static constexpr int PdgXi0{3322}; static constexpr int PdgOmegaMinus{3334}; } // namespace lambda_analysis -// FIX name/struct: use UpperCamelCase for struct names struct lambda1520analysispo { SliceCache sliceCache; // Preslice helpers - Preslice tracksPerResonanceCollision = - aod::resodaughter::resoCollisionId; + Preslice tracksPerResonanceCollision = aod::resodaughter::resoCollisionId; Preslice tracksPerStandardCollision = aod::track::collisionId; // Pointer to MC parent particle table (used only in MC processing) @@ -192,7 +189,7 @@ struct lambda1520analysispo { const AxisSpec axisTPCdEdx(380, 10, 200, "#frac{dE}{dx}"); const AxisSpec axisVertexZ(120, -12, 12, "v_{z} (cm)"); const AxisSpec axisEventPlaneAngle(120, -3.14, 3.14, "#theta (rad)"); - const AxisSpec axisInvariantMass(numberOfInvMassBins, 1.4, 2.0, "M_{inv} (GeV/c^{2})"); + const AxisSpec axisInvariantMass(numberOfInvMassBins, 1.4, 3.0, "M_{inv} (GeV/c^{2})"); AxisSpec axisOccupancy = {occupancyBins, "Track occupancy [-40,100]"}; AxisSpec axisDCAz = {dcaZMixingBins, "DCA_{z} (cm)"}; @@ -321,7 +318,7 @@ struct lambda1520analysispo { // passesProtonDCASelection() // ============================================================ template - bool passesProtonDCASelection(TrackType const& track, float totalMomentum) + bool passesProtonDCASelection(TrackType const& track, float trackPt) { auto ptBinEdges = static_cast>(protonDCAPtBinEdges); auto maxDCAxyValues = @@ -330,8 +327,8 @@ struct lambda1520analysispo { bool dcaXYPassed = false; for (int iBin = 0; iBin < numberOfBins; iBin++) { - if (totalMomentum >= ptBinEdges[iBin] && - totalMomentum < ptBinEdges[iBin + 1] && + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) dcaXYPassed = true; } @@ -346,7 +343,7 @@ struct lambda1520analysispo { // passesKaonDCASelection() // ============================================================ template - bool passesKaonDCASelection(TrackType const& track, float totalMomentum) + bool passesKaonDCASelection(TrackType const& track, float trackPt) { auto ptBinEdges = static_cast>(kaonDCAPtBinEdges); auto maxDCAxyValues = static_cast>(kaonMaxDCAxyPerPtBin); @@ -354,8 +351,8 @@ struct lambda1520analysispo { bool dcaXYPassed = false; for (int iBin = 0; iBin < numberOfBins; iBin++) { - if (totalMomentum >= ptBinEdges[iBin] && - totalMomentum < ptBinEdges[iBin + 1] && + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) dcaXYPassed = true; } @@ -406,7 +403,7 @@ struct lambda1520analysispo { if (track.tofNSigmaPr() < minTOFNSigmaProton) return false; - if (combinedNSigmaCutProton < 0 && totalMomentum >= minProtonMomentum) { + if (combinedNSigmaCutProton < 0) { for (int i = 0; i < nTOFBins - 1; ++i) { if (totalMomentum >= tofMomBins[i] && totalMomentum < tofMomBins[i + 1] && @@ -423,7 +420,7 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if ((combinedNSigmaCutProton > 0) && totalMomentum >= minProtonMomentum && + if ((combinedNSigmaCutProton > 0) && (combinedNSigProton < circularCutSquared && combinedNSigPion > circularRejCutSquared && combinedNSigKaon > circularRejCutSquared)) { @@ -431,13 +428,9 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if (totalMomentum < minProtonMomentum && - tpcNSigProton < maxTPCNSigmaProton) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - } else { + if (!track.hasTOF() && totalMomentum >= minProtonMomentum) + return false; tofPIDPassed = true; if (track.tpcNSigmaPr() < minTPCNSigmaProton) return false; @@ -490,7 +483,7 @@ struct lambda1520analysispo { if (track.tofNSigmaKa() < minTOFNSigmaKaon) return false; - if (combinedNSigmaCutKaon < 0 && totalMomentum >= minKaonMomentum) { + if (combinedNSigmaCutKaon < 0) { for (int i = 0; i < nTOFBins - 1; ++i) { if (totalMomentum >= tofMomBins[i] && totalMomentum < tofMomBins[i + 1] && @@ -507,7 +500,7 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if ((combinedNSigmaCutKaon > 0) && totalMomentum >= minKaonMomentum && + if ((combinedNSigmaCutKaon > 0) && (combinedNSigKaon < circularCutSquared && combinedNSigPion > circularRejCutSquared && combinedNSigProton > circularRejCutSquared)) { @@ -515,12 +508,9 @@ struct lambda1520analysispo { tpcPIDPassed = true; } - if (totalMomentum < minKaonMomentum && tpcNSigKaon < maxTPCNSigmaKaon) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - } else { + if (!track.hasTOF() && totalMomentum >= minKaonMomentum) + return false; tofPIDPassed = true; if (track.tpcNSigmaKa() < minTPCNSigmaKaon) return false; @@ -537,6 +527,87 @@ struct lambda1520analysispo { return (tpcPIDPassed && tofPIDPassed); } + template + void fillTrackQAHistograms(TrackType const& track, float centralityPercent) + { + if (!passesBasicTrackSelection(track)) + return; + + const float trackPt = track.pt(); + const float totalMomentum = RecoDecay::p(track.px(), track.py(), track.pz()); + + const auto tpcNSigProtonRaw = track.tpcNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + const auto tpcNSigKaonRaw = track.tpcNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + if (track.hasTOF()) { + const auto tofNSigProtonRaw = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProtonRaw); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProtonRaw); + const auto tofNSigKaonRaw = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaonRaw); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaonRaw); + } + + const bool isProtonCandidate = + (!requireTOFForProton || track.hasTOF()) && + passesProtonPID(track, totalMomentum) && + passesProtonDCASelection(track, trackPt); + + if (isProtonCandidate) { + allHistograms.fill(HIST("QAafter/Proton/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Proton/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Proton/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Proton/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), totalMomentum, track.tpcNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), trackPt, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigProton = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), trackPt, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaKaonContamVsMomentum"), totalMomentum, track.tofNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProton); + } + } + + const bool isKaonCandidate = + (!requireTOFForKaon || track.hasTOF()) && + passesKaonPID(track, totalMomentum) && + passesKaonDCASelection(track, trackPt); + + if (isKaonCandidate) { + allHistograms.fill(HIST("QAafter/Kaon/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Kaon/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Kaon/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Kaon/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum"), totalMomentum, track.tpcNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsPt"), trackPt, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Kaon/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigKaon = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsPt"), trackPt, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsCentrality"), centralityPercent, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaProtonContamVsMomentum"), totalMomentum, track.tofNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaon); + } + } + } + // ============================================================ // fillInvariantMassHistograms() // ============================================================ @@ -566,23 +637,6 @@ struct lambda1520analysispo { protonTotalMomentum = RecoDecay::p(pxProton, pyProton, pzProton); kaonTotalMomentum = RecoDecay::p(pxKaon, pyKaon, pzKaon); - if (!isMixedEvent) { - auto tpcNSigProton = protonTrack.tpcNSigmaPr(); - allHistograms.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), protonTotalMomentum, tpcNSigProton); - if (protonTrack.hasTOF()) { - auto tofNSigProton = protonTrack.tofNSigmaPr(); - allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), protonTotalMomentum, tofNSigProton); - allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProton, tofNSigProton); - } - auto tpcNSigKaon = kaonTrack.tpcNSigmaKa(); - allHistograms.fill(HIST("QAbefore/Kaon/tpcNSigmaVsMomentum"), kaonTotalMomentum, tpcNSigKaon); - if (kaonTrack.hasTOF()) { - auto tofNSigKaon = kaonTrack.tofNSigmaKa(); - allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsMomentum"), kaonTotalMomentum, tofNSigKaon); - allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaon, tofNSigKaon); - } - } - if (requireTOFForProton && !protonTrack.hasTOF()) continue; if (requireTOFForKaon && !kaonTrack.hasTOF()) @@ -592,8 +646,8 @@ struct lambda1520analysispo { !passesKaonPID(kaonTrack, kaonTotalMomentum)) continue; - if (!passesProtonDCASelection(protonTrack, protonTotalMomentum) || - !passesKaonDCASelection(kaonTrack, kaonTotalMomentum)) + if (!passesProtonDCASelection(protonTrack, protonTrack.pt()) || + !passesKaonDCASelection(kaonTrack, kaonTrack.pt())) continue; // FIX root/entity: replaced TMath::ACos with std::acos @@ -603,58 +657,6 @@ struct lambda1520analysispo { deepAngleCutValue) continue; - if constexpr (!isMixedEvent) { - auto ptProton = protonTrack.pt(); - auto tpcNSigProton = protonTrack.tpcNSigmaPr(); - - allHistograms.fill(HIST("QAafter/Proton/ptVsCentrality"), ptProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/dcaZVsPt"), ptProton, protonTrack.dcaZ()); - allHistograms.fill(HIST("QAafter/Proton/dcaXYVsPt"), ptProton, protonTrack.dcaXY()); - allHistograms.fill(HIST("QAafter/Proton/tpcDedxVsMomentum"), protonTotalMomentum, protonTrack.tpcSignal()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), protonTotalMomentum, protonTrack.tpcNSigmaPi()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), protonTotalMomentum, protonTrack.tpcNSigmaKa()); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), protonTotalMomentum, tpcNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), tpcNSigProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), ptProton, tpcNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), ptProton, protonTrack.tpcNClsCrossedRows()); - allHistograms.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), ptProton, protonTrack.tpcNClsFound()); - - if (!useTPCOnlyPID && protonTrack.hasTOF()) { - auto tofNSigProton = protonTrack.tofNSigmaPr(); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), protonTotalMomentum, tofNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), tofNSigProton, centralityPercent); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), ptProton, tofNSigProton); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), protonTotalMomentum, protonTrack.tofNSigmaPi()); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaKaonContamVsMomentum"), protonTotalMomentum, protonTrack.tofNSigmaKa()); - allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProton, tofNSigProton); - } - - auto ptKaon = kaonTrack.pt(); - auto tpcNSigKaon = kaonTrack.tpcNSigmaKa(); - - allHistograms.fill(HIST("QAafter/Kaon/ptVsCentrality"), ptKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/dcaZVsPt"), ptKaon, kaonTrack.dcaZ()); - allHistograms.fill(HIST("QAafter/Kaon/dcaXYVsPt"), ptKaon, kaonTrack.dcaXY()); - allHistograms.fill(HIST("QAafter/Kaon/tpcDedxVsMomentum"), kaonTotalMomentum, kaonTrack.tpcSignal()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaPionContamVsPt"), kaonTotalMomentum, kaonTrack.tpcNSigmaPi()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum"), kaonTotalMomentum, kaonTrack.tpcNSigmaPr()); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsMomentum"), kaonTotalMomentum, tpcNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsCentrality"), tpcNSigKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsPt"), ptKaon, tpcNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tpcCrossedRowsVsPt"), ptKaon, kaonTrack.tpcNClsCrossedRows()); - allHistograms.fill(HIST("QAafter/Kaon/tpcClustersFoundVsPt"), ptKaon, kaonTrack.tpcNClsFound()); - - if (!useTPCOnlyPID && kaonTrack.hasTOF()) { - auto tofNSigKaon = kaonTrack.tofNSigmaKa(); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsMomentum"), kaonTotalMomentum, tofNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsPt"), ptKaon, tofNSigKaon); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsCentrality"), tofNSigKaon, centralityPercent); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaPionContamVsMomentum"), kaonTotalMomentum, kaonTrack.tofNSigmaPi()); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaProtonContamVsMomentum"), kaonTotalMomentum, kaonTrack.tofNSigmaPr()); - allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaon, tofNSigKaon); - } - } - if (runQualityChecksOnly) continue; @@ -836,6 +838,9 @@ struct lambda1520analysispo { allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), 100); allHistograms.fill(HIST("Event/primaryVertexZ"), collision.posZ()); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } fillInvariantMassHistograms(tracks, tracks, collision.cent()); } PROCESS_SWITCH(lambda1520analysispo, processData, @@ -900,6 +905,8 @@ struct lambda1520analysispo { if (!passesBasicTrackSelection(track)) continue; + fillTrackQAHistograms(track, centralityPercent); + // FIX root/entity: replaced TMath::Sqrt with RecoDecay::p float totalMom = RecoDecay::p(track.px(), track.py(), track.pz()); @@ -1077,7 +1084,7 @@ struct lambda1520analysispo { // FIX const-ref-in-for-loop: const auto& structured binding for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; + continue; allHistograms.fill(HIST("Event/mixingBins_centralityVsVtxZVsEventPlane"), col1.cent(), col1.posZ(), col1.evtPl()); fillInvariantMassHistograms(tracks1, tracks2, col1.cent()); @@ -1110,6 +1117,9 @@ struct lambda1520analysispo { allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), occupancyValue); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } fillInvariantMassHistograms(tracks, tracks, collision.cent(), occupancyValue); } @@ -1140,7 +1150,7 @@ struct lambda1520analysispo { // FIX const-ref-in-for-loop: const auto& structured binding for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; + continue; auto occupancyValue = 100; if (applyOccupancyInTimeRangeCut) occupancyValue = col1.trackOccupancyInTimeRange(); From 654e2f0be758e897d1dab79a044d31b33d3755a7 Mon Sep 17 00:00:00 2001 From: ahmadtabikh Date: Mon, 26 Oct 2026 16:37:53 +0100 Subject: [PATCH 445/648] [PWGJE] Add Particle Transformer b-jet tagger to jetTaggerHF (#18409) --- PWGJE/Core/JetTaggingUtilities.h | 59 ++++++++++++++++++ PWGJE/Core/MlResponseHfTagging.h | 95 +++++++++++++++++++++++++++++ PWGJE/TableProducer/jetTaggerHF.cxx | 70 ++++++++++++++++++++- 3 files changed, 223 insertions(+), 1 deletion(-) diff --git a/PWGJE/Core/JetTaggingUtilities.h b/PWGJE/Core/JetTaggingUtilities.h index 754c19331dc..0d5fca0a50a 100644 --- a/PWGJE/Core/JetTaggingUtilities.h +++ b/PWGJE/Core/JetTaggingUtilities.h @@ -1155,6 +1155,65 @@ void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /* } } +// Looping over the track info and putting them in the input vectors (for ParT b-jet tagging). +template +void analyzeJetTrackInfo4ParT(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, std::vector>& featConstit, std::vector>& vecConstit, float trackPtMin = 0.5, float trackDcaXYMax = 10.0, float trackDcaZMax = 10.0) +{ + for (const auto& constituent : analysisJet.template tracks_as()) { + + if (constituent.pt() < trackPtMin || !trackAcceptanceWithDca(constituent, trackDcaXYMax, trackDcaZMax)) { + continue; + } + + int sign = getGeoSign(analysisJet, constituent); + + float trackPt = constituent.pt(); + float trackEta = constituent.eta(); + float trackPhi = constituent.phi(); + + float ip2d = std::abs(constituent.dcaXY()) * sign; + float ipz = std::abs(constituent.dcaZ()) * sign; + float ip2dSigma = constituent.sigmadcaXY(); + float ipzSigma = constituent.sigmadcaZ(); + + float dPhi = std::atan2(std::sin(trackPhi - analysisJet.phi()), std::cos(trackPhi - analysisJet.phi())); + + featConstit.emplace_back(std::vector{ + std::log(trackPt), + std::log(trackPt / analysisJet.pt()), + jetutilities::deltaR(analysisJet, constituent), + trackEta - analysisJet.eta(), + dPhi, + std::tanh(ip2d), + std::tanh(ipz), + (ip2dSigma != 0.f) ? ip2d / ip2dSigma : 0.f, + (ipzSigma != 0.f) ? ipz / ipzSigma : 0.f, + static_cast(constituent.sign())}); + + vecConstit.emplace_back(std::vector{ + trackPt * std::cos(trackPhi), + trackPt * std::sin(trackPhi), + trackPt * std::sinh(trackEta), + trackPt * std::cosh(trackEta)}); + } +} + +// Discriminant value. Probability order is assumed to be [b, c, lf]. +template +T getDbFromProb(const std::vector& probs, double fC = 0.018) +{ + // MlResponse::getModelOutput() returns exactly nClassesMl entries, so a mismatched + // nClassesMl silently truncates the model output and would read out of bounds here. + if (probs.size() < 3) { + LOG(fatal) << "getDbFromProb expects 3 class probabilities but received " << probs.size() << ". Set the nClassesMl configurable to 3 (and give cutDirMl 3 entries to match)."; + } + T denominator = fC * probs[1] + (1. - fC) * probs[2]; + if (probs[0] <= 0. || denominator <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(probs[0] / denominator); +} + // Discriminant value for GNN b-jet tagging template T getDb(const std::vector& logits, double fC = 0.018) diff --git a/PWGJE/Core/MlResponseHfTagging.h b/PWGJE/Core/MlResponseHfTagging.h index b144d6e145a..b6cc72ed8d9 100644 --- a/PWGJE/Core/MlResponseHfTagging.h +++ b/PWGJE/Core/MlResponseHfTagging.h @@ -119,6 +119,22 @@ class MlResponseHfTagging : public MlResponse /// @return number of output nodes int getOutputNodes(int imod = 0) const { return this->mModels[imod].getNumOutputNodes(); } + /// @brief Run a model that takes several pre-built input tensors (e.g. ParT: pf_features, pf_vectors, pf_mask inputs). + /// \param input the input tensors, in the order the graph declares them + /// \param nModel index of the model + /// @return the model output, mNClasses entries long + template + std::vector getModelOutputMultiInput(T& input, int nModel = 0) + { + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = this->mModels[nModel].template evalModel(input); + if (output.size() < this->mNClasses) { + LOG(fatal) << "Model " << this->mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(this->mNClasses) << " classes are expected. Please check your configurables."; + } + output.resize(this->mNClasses); + return output; + } + /// Method to fill the inputs of jet, tracks and secondary vertices /// \param jet is the b-jet candidate /// \param tracks is the vector of tracks associated to the jet @@ -483,6 +499,85 @@ class GNNBjetAllocator : public TensorAllocator } }; +// TensorAllocator for the ParT b-jet tagger. +// Inputs, in graph order: pf_features (1, nFeat, nConstit), pf_vectors (1, nVec, nConstit), +// pf_mask (1, 1, nConstit). +class ParTBjetAllocator : public TensorAllocator +{ + private: + int64_t nConstit; + int64_t nFeat; + int64_t nVec; + bool padWrap; + + // Replace non-finite values in a vector, which the model cannot consume + template + static void replaceNonFinite(std::vector& vec, T value) + { + for (auto& el : vec) { // o2-linter: disable=const-ref-in-for-loop + if (std::isnan(el) || std::isinf(el)) { + el = value; + } + } + } + + public: + ParTBjetAllocator() : TensorAllocator(), nConstit(32), nFeat(10), nVec(4), padWrap(true) {} + ParTBjetAllocator(int64_t nConstit, int64_t nFeat, int64_t nVec, bool padWrap = true) + : TensorAllocator(), nConstit(nConstit), nFeat(nFeat), nVec(nVec), padWrap(padWrap) {} + ~ParTBjetAllocator() = default; + + ParTBjetAllocator& operator=(const ParTBjetAllocator& other) + { + nConstit = other.nConstit; + nFeat = other.nFeat; + nVec = other.nVec; + padWrap = other.padWrap; + return *this; + } + + // Allocate and return the ParT input tensors (std::vector). + // featData/vecData/maskData back the tensors + void getParTInput(const std::vector>& featConstit, const std::vector>& vecConstit, std::vector& featData, std::vector& vecData, std::vector& maskData, std::vector& partInput) + { + featData.assign(nFeat * nConstit, 0.f); + vecData.assign(nVec * nConstit, 0.f); + maskData.assign(nConstit, 0.f); + + int64_t nSelected = static_cast(featConstit.size()); + const int64_t nReal = (nSelected < nConstit) ? nSelected : nConstit; // jets longer than nConstit are truncated + + for (int64_t iConstit = 0; iConstit < nConstit; ++iConstit) { + int64_t iSource = iConstit; + if (iConstit < nReal) { + maskData[iConstit] = 1.f; + } else { + if (!padWrap || nReal == 0) { + continue; // zero padding, mask stays 0 + } + iSource = iConstit % nReal; // weaver 'wrap' padding: cyclic repetition, mask stays 0 + } + for (int64_t iFeat = 0; iFeat < nFeat; ++iFeat) { + featData[iFeat * nConstit + iConstit] = featConstit[iSource][iFeat]; + } + for (int64_t iVec = 0; iVec < nVec; ++iVec) { + vecData[iVec * nConstit + iConstit] = vecConstit[iSource][iVec]; + } + } + + replaceNonFinite(featData, 0.f); + replaceNonFinite(vecData, 0.f); + + std::vector featShape{1, nFeat, nConstit}; + std::vector vecShape{1, nVec, nConstit}; + std::vector maskShape{1, 1, nConstit}; + + partInput.emplace_back(createTensor(featData, featShape)); + partInput.emplace_back(createTensor(vecData, vecShape)); + partInput.emplace_back(createTensor(maskData, maskShape)); + } +}; + } // namespace o2::analysis #undef FILL_MAP_BJET diff --git a/PWGJE/TableProducer/jetTaggerHF.cxx b/PWGJE/TableProducer/jetTaggerHF.cxx index bd8625b676c..f2daa6a36e9 100644 --- a/PWGJE/TableProducer/jetTaggerHF.cxx +++ b/PWGJE/TableProducer/jetTaggerHF.cxx @@ -145,6 +145,17 @@ struct JetTaggerHFTask { ConfigurableAxis binTrackProbability{"binTrackProbability", {100, 0.f, 1.f}, ""}; ConfigurableAxis binJetFlavour{"binJetFlavour", {6, -0.5, 5.5}, ""}; + // ParT (Particle Transformer) tagger. Grouped in a ConfigurableGroup because + // StructToTuple.h supports at most 99 task members and this task is near the limit. + struct ParTCfg : ConfigurableGroup { + std::string prefix = "part"; + Configurable nConstit{"nConstit", 32, "fixed number of constituents fed to the ParT model, must match training"}; + Configurable nFeat{"nFeat", 10, "number of pf_features channels"}; + Configurable nVec{"nVec", 4, "number of pf_vectors channels"}; + Configurable padWrap{"padWrap", true, "pad short jets by cyclic repetition (weaver 'wrap' mode); set false for zero padding"}; + Configurable minTracks{"minTracks", 2, "minimum number of selected constituents; training applied jet_nTracks >= 2"}; + } parTCfg; + o2::analysis::MlResponseHfTagging bMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -277,7 +288,7 @@ struct JetTaggerHFTask { } } } - if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { if (jet.pt() >= jetpTMin) { float dbRange; if (scoreML[jet.globalIndex()] < dbMin) { @@ -525,9 +536,24 @@ struct JetTaggerHFTask { bMlResponse.init(); } + if (doprocessAlgorithmParT) { + bMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + bMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + bMlResponse.setModelPathsLocal(onnxFileNames); + } + bMlResponse.init(); + + LOGF(info, "ParT: loaded %s with %d output node(s); %d constituents, %s padding", onnxFileNames.value[0].c_str(), bMlResponse.getOutputNodes(), static_cast(parTCfg.nConstit.value), parTCfg.padWrap.value ? "wrap" : "zero"); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { tensorAlloc = o2::analysis::GNNBjetAllocator(nJetFeat.value, nTrkFeat.value, nClassesMl.value, nTrkOrigin.value, transformFeatureJetMean.value, transformFeatureJetStdev.value, transformFeatureTrkMean.value, transformFeatureTrkStdev.value, nJetConst, tfFuncTypeGNN.value); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { registry.add("h2_count_db", "#it{D}_{b} underflow/overflow;Jet flavour;#it{D}_{b} range", {HistType::kTH2F, {{4, 0., 4.}, {3, 0., 3.}}}); auto h2CountDb = registry.get(HIST("h2_count_db")); h2CountDb->GetXaxis()->SetBinLabel(1, "b-jet"); @@ -669,6 +695,42 @@ struct JetTaggerHFTask { } } + template + void analyzeJetAlgorithmParT(AnyJets const& jets, AnyTracks const& tracks) + { + o2::analysis::ParTBjetAllocator parTTensorAlloc(parTCfg.nConstit.value, parTCfg.nFeat.value, parTCfg.nVec.value, parTCfg.padWrap.value); + + for (const auto& jet : jets) { + std::vector> featConstit; + std::vector> vecConstit; + jettaggingutilities::analyzeJetTrackInfo4ParT(jet, tracks, featConstit, vecConstit, trackPtMin, trackDcaXYMax, trackDcaZMax); + + // Training applied a jet_nTracks >= 2 selection, so jets below it are out of distribution. + if (static_cast(featConstit.size()) < parTCfg.minTracks) { + scoreML[jet.globalIndex()] = -999.; + LOGF(debug, "doprocessAlgorithmParT, too few selected constituents (%d)", jet.globalIndex()); + continue; + } + + std::vector featData; + std::vector vecData; + std::vector maskData; + std::vector parTInput; + parTTensorAlloc.getParTInput(featConstit, vecConstit, featData, vecData, maskData, parTInput); + + // The exported graph applies the softmax itself, so the output is already + // [p_b, p_c, p_udsg] and must not be passed through getDb(), which would softmax again. + auto modelOutput = bMlResponse.getModelOutputMultiInput(parTInput, 0); + float db = jettaggingutilities::getDbFromProb(modelOutput, fC); + if (!std::isnan(db)) { + scoreML[jet.globalIndex()] = db; + } else { + scoreML[jet.globalIndex()] = 999.; + LOGF(debug, "doprocessAlgorithmParT, Db is NaN (%d)", jet.globalIndex()); + } + } + } + void processDummy(aod::JetCollisions const&) { } @@ -725,6 +787,12 @@ struct JetTaggerHFTask { } PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmGNN, "Fill GNN evaluation score (D_b) for charged jets", false); + void processAlgorithmParT(JetTable const& jets, JetTracksExt const& jtracks) + { + analyzeJetAlgorithmParT(jets, jtracks); + } + PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmParT, "Fill ParT evaluation score (D_b) for charged jets", false); + void processFillTables(std::conditional_t, JetTable>::iterator const& jet, JetTracksExt const& tracks) { fillTables(jet, tracks); From 07cb26a141c492e0b435e5d4a044be97d60fffe6 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:24:37 +0530 Subject: [PATCH 446/648] [PWGLF] INEL spectra alterative method checks (#18459) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 5017b6b91a8..cb72f52ecb5 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -351,6 +351,7 @@ struct Kstarqa { if (doprocessEvtLossSigLossMC || doprocessEvtLossSigLossMCPhi) { hInvMass.add("MCcorrections/hSignalLossDenominator", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hSignalLossNumerator", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossNumeratorINEL", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hImpactParameterRec", "Impact parameter in reconstructed MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityRec", "Multiplicity in generated MC with at least 1 reconstruction", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParameterGen", "Impact parameter in generated MC", kTH1F, {impactParAxis}); @@ -1949,8 +1950,8 @@ struct Kstarqa { if (!RecCollision.has_mcCollision()) continue; - if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) - continue; + // if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + // continue; if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) continue; @@ -2005,6 +2006,9 @@ struct Kstarqa { if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGen); } + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/hSignalLossNumeratorINEL"), mcPart.pt(), multiplicityGen); + } } // end loop on gen particles } PROCESS_SWITCH(Kstarqa, processEvtLossSigLossMC, "Process Signal Loss, Event Loss", false); From a29b8ce458c820012fe02b24b33a0ec4a7550a2d Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:40:45 +0100 Subject: [PATCH 447/648] [PWGHF] derivedDataCreatorCorrelationsReduced: fix histogram filling in Mixed Event (#18470) Co-authored-by: Marcello Di Costanzo --- .../derivedDataCreatorCorrelationsReduced.cxx | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index c6a79d8dfaa..8ac8e23acdd 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -442,10 +442,12 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill track tables for mixed-event /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity template void fillTrkMixedEvent(TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { bool first = true; for (const auto& assTrk : assTrks) { @@ -461,15 +463,17 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (!first && downSampleTrksFactor < 1.) { // skip downsampling for the first track to avoid empty tables float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } first = false; registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); rowAssocBases(rowCollisions.lastIndex(), assTrk.phi(), assTrk.eta(), assTrkPt); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); } @@ -506,7 +510,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsDplus); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusMixedEvent, "Process Mixed Event for Dplus candidates", false); @@ -547,7 +551,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsDsToPiKK); fillCharmMixedEvent(candsDsToKKPi); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsMixedEvent, "Process Mixed Event for Ds candidates", false); @@ -588,7 +592,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsD0ToPiK); fillCharmMixedEvent(candsD0ToKPi); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0MixedEvent, "Process Mixed Event for D0 candidates", false); @@ -629,7 +633,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsLcToPKPi); fillCharmMixedEvent(candsLcToPiKP); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcMixedEvent, "Process Mixed Event for Lc candidates", false); @@ -655,7 +659,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronMixedEvent, "Process Mixed Event for hadron candidates", false); }; From 175b110186feb8559f55871e0b29afcffe60b4d1 Mon Sep 17 00:00:00 2001 From: DongJo Kim Date: Mon, 26 Oct 2026 22:58:49 +0200 Subject: [PATCH 448/648] [PWGCF] [PWGCF:Jcorran] J v02 dmeson[hadron efficiency correction] (#18462) --- PWGCF/JCorran/Tasks/CMakeLists.txt | 2 +- PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx | 202 ++++++++++++++++++++- PWGCF/JCorran/Tasks/jV02DmesonToy.cxx | 7 + 3 files changed, 202 insertions(+), 9 deletions(-) diff --git a/PWGCF/JCorran/Tasks/CMakeLists.txt b/PWGCF/JCorran/Tasks/CMakeLists.txt index 4c9c34b46c5..1cd594f8f19 100644 --- a/PWGCF/JCorran/Tasks/CMakeLists.txt +++ b/PWGCF/JCorran/Tasks/CMakeLists.txt @@ -41,7 +41,7 @@ o2physics_add_dpl_workflow(epdzeroflow-analysis o2physics_add_dpl_workflow(j-v02-dmeson-analysis SOURCES jV02DmesonAnalysis.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::JCorran COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(j-v02-dmeson-toy diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx index 06b6d01cf12..e93b7ec3f7a 100644 --- a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -17,7 +17,11 @@ /// X and Y are the corrected Q2 of two forward detectors (default FT0A x FT0C) /// from the combined Qvectors table, with the same indexing as jEPDzeroFlowAnalysis. /// Z_D(pT) = N_D(pT) / N_ch. N_D comes from CF2ProngTracks (D0 and D0bar). -/// N_ch comes from CFTracks. Both derived tables are indexed to CFCollisions, +/// N_ch comes from CFTracks. With JWeights (jflucWeightsLoader, hadron NUE only), +/// sum 1/epsilon_h into N_ch for hadrons and optionally for D0 denominators +/// (processReferenceCorrected / processD0HadronNch). Full D0 candidate efficiency +/// (J2ProngWeights, processD0Corrected) is not wired in the default train yet. +/// Both derived tables are indexed to CFCollisions, /// so the original collision is matched through CFCollRefs (one row per kept /// collision, written by filter-correlations with cfgTransientTables). /// The D azimuth is not part of the Q-vector. @@ -26,6 +30,7 @@ /// Definition: arXiv:2506.18690, Appendix A. #include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" @@ -47,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +105,7 @@ struct JV02DmesonAnalysis { Configurable cfgCutNoTimeFrameBorder{"cfgCutNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; Configurable cfgCutNoITSROFrameBorder{"cfgCutNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; Configurable cfgRejectDaughters{"cfgRejectDaughters", false, "Drop D0 daughter CF tracks from N_ch"}; + Configurable cfgWeightEffIsEfficiency{"cfgWeightEffIsEfficiency", true, "JWeights.weightEff is epsilon (multiply counts by 1/epsilon). Set false if the table already stores 1/epsilon."}; ConfigurableAxis cfgAxisCent{"cfgAxisCent", {8, 0., 80.}, "Centrality axis"}; ConfigurableAxis cfgAxisPtD{"cfgAxisPtD", {VARIABLE_WIDTH, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 12.f, 16.f, 24.f}, "D0 pT axis"}; @@ -115,6 +122,22 @@ struct JV02DmesonAnalysis { std::mt19937 mRng{1}; std::uniform_int_distribution mSampleDist; + template + using hasWeightEff = decltype(std::declval().weightEff()); + + template + float inverseEfficiencyWeight(const TTrack& track) const + { + if constexpr (std::experimental::is_detected::value) { + const float w = track.weightEff(); + if (w <= 0.f || !std::isfinite(w)) { + return 0.f; + } + return cfgWeightEffIsEfficiency.value ? 1.f / w : w; + } + return 1.f; + } + int detectorId(const std::string& name) const { if (name == "FT0C") { @@ -155,7 +178,7 @@ struct JV02DmesonAnalysis { const AxisSpec subsampleAxis{mNSubsample, 0., static_cast(mNSubsample), "subsample"}; const AxisSpec qaAxis{6, 0.5, 6.5, "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted"}; - if (doprocessReference) { + if (doprocessReference || doprocessReferenceCorrected) { histos.add("hadron/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); histos.add("hadron/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); histos.add("hadron/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); @@ -168,7 +191,13 @@ struct JV02DmesonAnalysis { mHadPtAxis = histos.get(HIST("hadron/Prof_Z"))->GetYaxis(); mHadCounts.assign(mHadPtAxis->GetNbins(), 0.f); } - if (doprocessD0) { + if (doprocessReference && doprocessReferenceCorrected) { + LOGF(fatal, "Enable only one of processReference and processReferenceCorrected"); + } + if (doprocessD0 + doprocessD0HadronNch + doprocessD0Corrected > 1) { + LOGF(fatal, "Enable only one of processD0, processD0HadronNch, and processD0Corrected"); + } + if (doprocessD0 || doprocessD0HadronNch || doprocessD0Corrected) { histos.add("D0/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); histos.add("D0/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); histos.add("D0/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); @@ -238,7 +267,7 @@ struct JV02DmesonAnalysis { return std::isfinite(xy); } - template + template float countCharged(const TTracks& tracks, const std::unordered_set& daughters, bool differential) { float nch = 0.f; @@ -253,11 +282,18 @@ struct JV02DmesonAnalysis { if (cfgRejectDaughters && daughters.contains(static_cast(track.globalIndex()))) { continue; } - nch += 1.f; + float w = 1.f; + if constexpr (kApplyEff) { + w = inverseEfficiencyWeight(track); + if (w <= 0.f) { + continue; + } + } + nch += w; if (differential && mHadPtAxis != nullptr) { const int bin = mHadPtAxis->FindBin(pt); if (bin >= 1 && bin <= mHadPtAxis->GetNbins()) { - mHadCounts[bin - 1] += 1.f; + mHadCounts[bin - 1] += w; } } } @@ -291,7 +327,7 @@ struct JV02DmesonAnalysis { return; } const std::unordered_set noDaughters; - const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); if (nch < cfgMinNch) { return; } @@ -313,6 +349,41 @@ struct JV02DmesonAnalysis { } PROCESS_SWITCH(JV02DmesonAnalysis, processReference, "Charged-hadron v02(pT) closure", true); + void processReferenceCorrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged, true>(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReferenceCorrected, "Charged-hadron v02 with JWeights efficiency", false); + void processD0(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, aod::CFTracks const& tracks) { float cent = -1.f; @@ -338,7 +409,7 @@ struct JV02DmesonAnalysis { daughters.insert(static_cast(cand.cfTrackProng1Id())); } } - const float nch = countCharged(eventTracks, daughters, false); + const float nch = countCharged(eventTracks, daughters, false); if (nch < cfgMinNch) { return; } @@ -367,6 +438,121 @@ struct JV02DmesonAnalysis { } } PROCESS_SWITCH(JV02DmesonAnalysis, processD0, "D0 and D0bar v02(pT) from CF2ProngTracks", true); + + void processD0HadronNch(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0HadronNch, "D0 v02 with epsilon_h-weighted N_ch only (no D0 efficiency)", false); + + void processD0Corrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, + soa::Join const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float wInvD = inverseEfficiencyWeight(cand); + if (wInvD <= 0.f) { + continue; + } + const float wRaw = wInvD / nch; + const float wCorr = xy * wInvD / nch; + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0Corrected, "D0 v02 with JWeights and J2ProngWeights efficiency", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx index 46f72929cf6..e4d3f614849 100644 --- a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -21,6 +21,7 @@ /// Expected D0 v02, with an empty sideband, is 0.36. #include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" @@ -56,6 +57,7 @@ struct JV02DmesonToy { Produces cfRefs; Produces cfTracks; Produces cfCandidates; + Produces jWeights; void fill() { @@ -101,6 +103,9 @@ struct JV02DmesonToy { const float pt = itrk < nSignalTracks ? 1.1f : 0.3f; const int8_t sign = itrk % EventPair == 0 ? int8_t{1} : int8_t{-1}; cfTracks(ievt, pt, 0.f, 0.f, sign, uint8_t{1}); + // Closure for hadron efficiency: signal pT bin eps=0.5, rest eps=1 (v02 unchanged if all eps equal). + const float eff = pt > 1.f ? 0.5f : 1.f; + jWeights(1.f, eff); } const int nD0 = high ? 4 : 1; for (int icand = 0; icand < nD0; ++icand) { @@ -127,6 +132,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) OutputForTable::spec(), OutputForTable::spec(), OutputForTable::spec(), + OutputForTable::spec(), OutputSpec{"TFN", "TFNumber"}, OutputSpec{"TFF", "TFFilename"}, }, @@ -142,6 +148,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const&) toy.cfRefs.resetCursor(pc.outputs().make(OutputForTable::ref())); toy.cfTracks.resetCursor(pc.outputs().make(OutputForTable::ref())); toy.cfCandidates.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.jWeights.resetCursor(pc.outputs().make(OutputForTable::ref())); toy.fill(); pc.outputs().make(Output{"TFN", "TFNumber"}) = 1; pc.outputs().make(Output{"TFF", "TFFilename"}) = std::string{"toy"}; From fd8302afd5fb0a75b4c588d256e9d24d6cfc56f6 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Mon, 26 Oct 2026 23:18:40 +0100 Subject: [PATCH 449/648] [DPG] Improve CPU in D0 data calibration task (#18469) Co-authored-by: ALICE Action Bot --- .../derivedDataCreatorD0Calibration.cxx | 148 ++++++++---------- 1 file changed, 61 insertions(+), 87 deletions(-) diff --git a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx index 7a45020ba65..d5700f2f6a9 100644 --- a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx +++ b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx @@ -117,6 +117,14 @@ struct DerivedDataCreatorD0Calibration { std::string prefix = "ml"; } cfgMl; + struct SelectedTrack { + int globalIndex; + o2::dataformats::DCA dca; + o2::track::TrackParametrizationWithError trackParCov; + int pidPion; + int pidKaon; + }; + using TracksWCovExtraPid = soa::Join; using TracksWCovExtraTmoPid = soa::Join; using TracksWCovExtraTmoPidAndQa = soa::Join; @@ -187,7 +195,7 @@ struct DerivedDataCreatorD0Calibration { template void runDataCreation(CollisionsWEvSel const& collisions, aod::TrackAssoc const& trackIndices, - TTracks const&, + TTracks const& tracks, aod::BCsWithTimestamps const&, TTrackMeanOccs const&, TTrackQa const&) @@ -227,87 +235,65 @@ struct DerivedDataCreatorD0Calibration { eventIR.setFromLong(bc.globalBC()); auto groupedTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - std::vector dcaNeg; // we cache the negative track, to avoid the same computation for each positive track - dcaNeg.resize(groupedTrackIndices.size()); - std::vector isSelectedNeg(groupedTrackIndices.size(), true); - std::vector isPropagatedNeg(groupedTrackIndices.size(), false); - std::vector pidTrackNegKaon(groupedTrackIndices.size(), -1); - std::vector pidTrackNegPion(groupedTrackIndices.size(), -1); - - for (auto const& trackIndexPos : groupedTrackIndices) { - auto trackPos = trackIndexPos.template track_as(); + // we cache tracks + std::vector tracksSelectedPos; + std::vector tracksSelectedNeg; + tracksSelectedPos.reserve(groupedTrackIndices.size() / 2); + tracksSelectedNeg.reserve(groupedTrackIndices.size() / 2); + + // first loop on tracks to find the selected ones + for (auto const& trackIndex : groupedTrackIndices) { + auto track = trackIndex.template track_as(); // track selections - if (trackPos.sign() < 0) { // first positive track - continue; - } - if (!trackPos.isGlobalTrackWoDCA()) { + if (!track.isGlobalTrackWoDCA()) { continue; } - if (trackPos.pt() < cfgTrackCuts.ptMin) { + if (track.pt() < cfgTrackCuts.ptMin) { continue; } - if (std::abs(trackPos.eta()) > cfgTrackCuts.absEtaMax) { + if (std::abs(track.eta()) > cfgTrackCuts.absEtaMax) { continue; } - auto trackParCovPos = getTrackParCov(trackPos); - o2::dataformats::DCA dcaPos(trackPos.dcaXY(), trackPos.dcaZ(), trackPos.cYY(), trackPos.cZY(), trackPos.cZZ()); - if (trackPos.collisionId() != collision.globalIndex()) { - trackParCovPos.propagateToDCA(primaryVertex, bz, &dcaPos); + auto trackParCov = getTrackParCov(track); + auto dcaTmp = o2::dataformats::DCA(track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ()); + if (track.collisionId() != collision.globalIndex()) { + trackParCov.propagateToDCA(primaryVertex, bz, &dcaTmp); } - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovPos.getPt(), dcaPos.getY(), dcaPos.getZ())) { + if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCov.getPt(), dcaTmp.getY(), dcaTmp.getZ())) { continue; } - - int pidTrackPosKaon{-1}; - int pidTrackPosPion{-1}; + int pidTrackKaon{-1}; + int pidTrackPion{-1}; if (cfgTrackCuts.usePidTpcOnly) { /// kaon TPC PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpc(trackPos); + pidTrackKaon = selectorKaon.statusTpc(track); /// pion TPC PID positive daughter - pidTrackPosPion = selectorPion.statusTpc(trackPos); + pidTrackPion = selectorPion.statusTpc(track); } else { /// kaon TPC, TOF PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpcAndTof(trackPos); + pidTrackKaon = selectorKaon.statusTpcAndTof(track); /// pion TPC, TOF PID positive daughter - pidTrackPosPion = selectorPion.statusTpcAndTof(trackPos); + pidTrackPion = selectorPion.statusTpcAndTof(track); + } + if (track.sign() > 0) { // first positive track + tracksSelectedPos.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); + } else if (track.sign() < 0) { + tracksSelectedNeg.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); } + } - int iTrack{-1}; - for (auto const& trackIndexNeg : groupedTrackIndices) { - iTrack++; - if (!isSelectedNeg[iTrack]) { - continue; - } - auto trackNeg = trackIndexNeg.template track_as(); - // track selections - if (trackNeg.sign() > 0) { // second negative track - isSelectedNeg[iTrack] = false; - continue; - } - if (!trackNeg.isGlobalTrackWoDCA()) { - isSelectedNeg[iTrack] = false; - continue; - } - if (trackNeg.pt() < cfgTrackCuts.ptMin) { - isSelectedNeg[iTrack] = false; - continue; - } - if (std::abs(trackNeg.eta()) > cfgTrackCuts.absEtaMax) { - isSelectedNeg[iTrack] = false; - continue; - } - auto trackParCovNeg = getTrackParCov(trackNeg); - if (!isPropagatedNeg[iTrack]) { - dcaNeg[iTrack] = o2::dataformats::DCA(trackNeg.dcaXY(), trackNeg.dcaZ(), trackNeg.cYY(), trackNeg.cZY(), trackNeg.cZZ()); - if (trackNeg.collisionId() != collision.globalIndex()) { - trackParCovNeg.propagateToDCA(primaryVertex, bz, &dcaNeg[iTrack]); - } - isPropagatedNeg[iTrack] = true; - } - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovNeg.getPt(), dcaNeg[iTrack].getY(), dcaNeg[iTrack].getZ())) { - isSelectedNeg[iTrack] = false; - continue; - } + for (auto const& trackSelPos : tracksSelectedPos) { + auto trackPos = tracks.rawIteratorAt(trackSelPos.globalIndex); + for (auto const& trackSelNeg : tracksSelectedNeg) { + auto trackNeg = tracks.rawIteratorAt(trackSelNeg.globalIndex); // preselections // pt @@ -337,24 +323,12 @@ struct DerivedDataCreatorD0Calibration { } } - if (cfgTrackCuts.usePidTpcOnly) { - /// kaon TPC PID negative daughter - pidTrackNegKaon[iTrack] = selectorKaon.statusTpc(trackNeg); - /// pion TPC PID negative daughter - pidTrackNegPion[iTrack] = selectorPion.statusTpc(trackNeg); - } else { - /// kaon TPC, TOF PID negative daughter - pidTrackNegKaon[iTrack] = selectorKaon.statusTpcAndTof(trackNeg); - /// pion TPC, TOF PID negative daughter - pidTrackNegPion[iTrack] = selectorPion.statusTpcAndTof(trackNeg); - } - // PID uint8_t massHypo{D0MassHypo::D0AndD0Bar}; // both mass hypotheses a priori - if (pidTrackPosPion == TrackSelectorPID::Rejected || pidTrackNegKaon[iTrack] == TrackSelectorPID::Rejected) { + if (trackSelPos.pidPion == TrackSelectorPID::Rejected || trackSelNeg.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0; // exclude D0 } - if (pidTrackNegPion[iTrack] == TrackSelectorPID::Rejected || pidTrackPosKaon == TrackSelectorPID::Rejected) { + if (trackSelNeg.pidPion == TrackSelectorPID::Rejected || trackSelPos.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0Bar; // exclude D0Bar } if (massHypo == 0) { @@ -362,7 +336,7 @@ struct DerivedDataCreatorD0Calibration { } // d0xd0 - if (dcaPos.getY() * dcaNeg[iTrack].getY() > cuts->get(ptBinNoVtxD0, CutsIds::MaxD0D0)) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinNoVtxD0, CutsIds::MaxD0D0)) { continue; } @@ -384,7 +358,7 @@ struct DerivedDataCreatorD0Calibration { } // reconstruct vertex - if (df.process(trackParCovPos, trackParCovNeg) == 0) { + if (df.process(trackSelPos.trackParCov, trackSelNeg.trackParCov) == 0) { continue; } const auto& secondaryVertex = df.getPCACandidate(); @@ -411,7 +385,7 @@ struct DerivedDataCreatorD0Calibration { } // d0xd0 - if (dcaPos.getY() * dcaNeg[iTrack].getY() > cuts->get(ptBinD0, CutsIds::MaxD0D0)) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinD0, CutsIds::MaxD0D0)) { continue; } // cospa @@ -476,7 +450,7 @@ struct DerivedDataCreatorD0Calibration { } else { // apply BDT models if (cfgMl.apply) { - featuresCandD0 = {dcaPos.getY(), dcaNeg[iTrack].getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg[iTrack].getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; + featuresCandD0 = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0, ptD0, bdtScoresD0)) { massHypo -= D0MassHypo::D0; } else { // selected, we compute cost* @@ -501,7 +475,7 @@ struct DerivedDataCreatorD0Calibration { } else { // apply BDT models if (cfgMl.apply) { - featuresCandD0bar = {dcaPos.getY(), dcaNeg[iTrack].getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg[iTrack].getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; + featuresCandD0bar = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0bar, ptD0, bdtScoresD0bar)) { massHypo -= D0MassHypo::D0Bar; } else { // selected, we compute cost* @@ -694,8 +668,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaPos.getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaPos.getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackPos.tpcNSigmaPi()), getCompressedNumSigmaPid(trackPos.tpcNSigmaKa()), getCompressedNumSigmaPid(trackPos.tofNSigmaPi()), @@ -861,8 +835,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaNeg[iTrack].getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaNeg[iTrack].getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackNeg.tpcNSigmaPi()), getCompressedNumSigmaPid(trackNeg.tpcNSigmaKa()), getCompressedNumSigmaPid(trackNeg.tofNSigmaPi()), From 09176f505223a410f58f5099b4647f695191ff7a Mon Sep 17 00:00:00 2001 From: EmilGorm <50658075+EmilGorm@users.noreply.github.com> Date: Tue, 27 Oct 2026 01:05:03 +0100 Subject: [PATCH 450/648] =?UTF-8?q?[PWGCF]=20accept=20negative=20ZDC=20cha?= =?UTF-8?q?nnel=20energies=20in=20custom=20event-sel=20in=20energyFlo?= =?UTF-8?q?=E2=80=A6=20(#18466)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx index db7d4c1cdb5..41fdfa963ec 100644 --- a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -348,11 +348,11 @@ struct EnergyFlowGfw { registry.add("zdc/energyZnVsNGlobalTracks", "Triggered events with finite ZN energies;N_{global tracks};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); registry.add("zdc/energyZpVsNGlobalTracks", "Triggered events with finite ZP energies;N_{global tracks};E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); registry.add("zdc/energyTotalVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); - registry.add("zdc/spectatorEstimateVsNGlobalTracks", "Triggered events passing current nonnegative-energy requirement;N_{global tracks};current N_{spectators} estimate", HistType::kTH2F, {axisZdcTrackMultiplicity, axisTotalSpectators}); + registry.add("zdc/spectatorEstimateVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};N_{spectators} estimate", HistType::kTH2F, {axisZdcTrackMultiplicity, axisTotalSpectators}); registry.add("zdc/energyZnVsCentrality", "Triggered events with finite ZN energies;centrality (%);E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); registry.add("zdc/energyZpVsCentrality", "Triggered events with finite ZP energies;centrality (%);E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); registry.add("zdc/energyTotalVsCentrality", "Triggered events with finite common energies;centrality (%);#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); - registry.add("zdc/spectatorEstimateVsCentrality", "Triggered events passing current nonnegative-energy requirement;centrality (%);current N_{spectators} estimate", HistType::kTH2F, {axisCentrality, axisTotalSpectators}); + registry.add("zdc/spectatorEstimateVsCentrality", "Triggered events with finite common energies;centrality (%);N_{spectators} estimate", HistType::kTH2F, {axisCentrality, axisTotalSpectators}); registry.add("zdc/meanZNAByNGlobalTracks", "Mean ZNA response;N_{global tracks};#LT E_{ZNA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); registry.add("zdc/meanZNCByNGlobalTracks", "Mean ZNC response;N_{global tracks};#LT E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); registry.add("zdc/meanZPAByNGlobalTracks", "Mean ZPA response;N_{global tracks};#LT E_{ZPA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); @@ -402,12 +402,14 @@ struct EnergyFlowGfw { float totalEnergy = 0.f; for (int index = 0; index < 4; ++index) { const float value = energies[index]; - if (!std::isfinite(value) || value < 0.f) { + if (!std::isfinite(value)) { return -1.f; } totalEnergy += value / responseFactors[index]; } - return totalEnergy / cfgSpectatorEnergyPerNucleon.value; + const float spectators = totalEnergy / cfgSpectatorEnergyPerNucleon.value; + // Preserve signed pedestal fluctuations in the sum, then enforce the physical boundary. + return std::isfinite(spectators) ? std::max(0.f, spectators) : -1.f; } template @@ -557,8 +559,8 @@ struct EnergyFlowGfw { registry.fill(HIST("zdc/energyTotalVsCentrality"), centrality, totalEnergy); registry.fill(HIST("zdc/meanTotalByCentrality"), centrality, totalEnergy); } - if (allNonnegative) { - const float spectators = totalSpectators(zdc); + const float spectators = totalSpectators(zdc); + if (spectators >= 0.f) { registry.fill(HIST("zdc/spectatorEstimateVsNGlobalTracks"), numberOfGlobalTracks, spectators); if (validCentrality) { registry.fill(HIST("zdc/spectatorEstimateVsCentrality"), centrality, spectators); From 246b0624b9812fe91688e82c8431d67f6f94f089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Tiek=C3=B6tter?= Date: Tue, 27 Oct 2026 08:55:14 +0100 Subject: [PATCH 451/648] [PWGDQ] Update CCDB path to general EventFiltering path (#18429) --- PWGDQ/TableProducer/tableMaker.cxx | 2 +- PWGDQ/TableProducer/tableMaker_withAssoc.cxx | 2 +- PWGDQ/Tasks/tableReader_withAssoc_direct.cxx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGDQ/TableProducer/tableMaker.cxx b/PWGDQ/TableProducer/tableMaker.cxx index 15edcac01ec..13969e2641d 100644 --- a/PWGDQ/TableProducer/tableMaker.cxx +++ b/PWGDQ/TableProducer/tableMaker.cxx @@ -202,7 +202,7 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"useCCDBConfigurations.ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"useCCDBConfigurations.ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } useCCDBConfigurations; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; diff --git a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx index 8ba2ee20340..9c7473ee660 100644 --- a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx @@ -286,7 +286,7 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fConfigGrpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index 78fd2b46359..2803c1069bc 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -287,7 +287,7 @@ struct AnalysisEventSelection { Configurable fConfigZorroTrigMask{"cfgZorroTriggerMask", "fDiMuon", "DQ Trigger masks: fSingleE,fLMeeIMR,fLMeeHMR,fDiElectron,fSingleMuLow,fSingleMuHigh,fDiMuon"}; Configurable fConfigRunZorroSel{"cfgRunZorroSel", false, "Select events with trigger mask"}; Configurable fBcTolerance{"cfgBcTolerance", 100, "Number of BCs of margin for software triggers"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } fConfigZorro; // RCT selection From f6e45892dec03c5b925dcdb38acd012ef12a1955 Mon Sep 17 00:00:00 2001 From: Sigurd Nese <32108009+sigurdnese@users.noreply.github.com> Date: Tue, 27 Oct 2026 09:01:18 +0100 Subject: [PATCH 452/648] [PWGDQ] Add mult variables to the varNames map (#18351) --- PWGDQ/Core/VarManager.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index ce0d01b55c3..a42d1701326 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -2202,6 +2202,9 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCEventImpParam"] = kMCEventImpParam; fgVarNamesMap["kMCEventPlaneAngle"] = kMCEventPlaneAngle; fgVarNamesMap["kMCEventCentrFT0C"] = kMCEventCentrFT0C; + fgVarNamesMap["kMultMCNParticlesEta05"] = kMultMCNParticlesEta05; + fgVarNamesMap["kMultMCNParticlesEta08"] = kMultMCNParticlesEta08; + fgVarNamesMap["kMultMCNParticlesEta10"] = kMultMCNParticlesEta10; fgVarNamesMap["kQ1ZNAX"] = kQ1ZNAX; fgVarNamesMap["kQ1ZNAY"] = kQ1ZNAY; fgVarNamesMap["kQ1ZNCX"] = kQ1ZNCX; From 0411a25833f36056d004a122724217ced84d8708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:54:12 +0100 Subject: [PATCH 453/648] [PWGHF] Fix casts (#18388) --- .../candidateSelectorB0ToDPiReduced.cxx | 2 +- .../candidateSelectorBplusToD0PiReduced.cxx | 2 +- .../candidateSelectorBsToDsPiReduced.cxx | 2 +- .../candidateSelectorLbToLcPiReduced.cxx | 2 +- .../dataCreatorJpsiHadReduced.cxx | 4 +- PWGHF/D2H/Tasks/taskB0.cxx | 2 +- PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx | 2 +- PWGHF/D2H/Tasks/taskBplus.cxx | 6 +- PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx | 2 +- PWGHF/D2H/Tasks/taskBs.cxx | 2 +- PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx | 2 +- PWGHF/D2H/Tasks/taskD0.cxx | 2 +- PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx | 2 +- PWGHF/D2H/Tasks/taskLb.cxx | 114 +++++++-------- PWGHF/D2H/Tasks/taskLc.cxx | 2 +- PWGHF/D2H/Tasks/taskUpcLc.cxx | 2 +- PWGHF/D2H/Tasks/taskXic.cxx | 6 +- PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx | 2 +- PWGHF/HFC/Macros/DhCorrelationExtraction.cxx | 136 +++++++++--------- PWGHF/HFC/Macros/FitCorrel.C | 20 +-- PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx | 2 +- .../correlatorD0D0barBarrelFullPid.cxx | 2 +- .../HFC/TableProducer/correlatorD0Hadrons.cxx | 6 +- .../TableProducer/correlatorDplusDminus.cxx | 2 +- .../TableProducer/correlatorDplusHadrons.cxx | 4 +- .../HFC/TableProducer/correlatorDsHadrons.cxx | 4 +- .../correlatorDsHadronsReduced.cxx | 4 +- .../correlatorFlowCharmHadronsReduced.cxx | 4 +- .../HFC/TableProducer/correlatorLcHadrons.cxx | 4 +- .../derivedDataCreatorCorrelationsReduced.cxx | 4 +- .../producerCharmHadronsTrackFemtoDream.cxx | 4 +- .../producerCharmHadronsV0FemtoDream.cxx | 4 +- .../Tasks/taskCharmHadronsTrackFemtoDream.cxx | 16 +-- .../Tasks/taskCharmHadronsV0FemtoDream.cxx | 8 +- PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx | 4 +- .../HFC/Tasks/taskCorrelationDplusHadrons.cxx | 10 +- PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx | 6 +- .../HFC/Tasks/taskCorrelationDstarHadrons.cxx | 2 +- PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx | 8 +- PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx | 2 +- .../computeFonllPlusPythiaPredictions.C | 2 +- .../candidateSelectorB0ToDPi.cxx | 2 +- .../candidateSelectorBplusToD0Pi.cxx | 2 +- .../candidateSelectorBsToDsPi.cxx | 2 +- PWGHF/TableProducer/candidateSelectorCd.cxx | 2 +- .../candidateSelectorDplusToPiKPi.cxx | 2 +- .../candidateSelectorDsToKKPi.cxx | 2 +- .../candidateSelectorDstarToD0Pi.cxx | 2 +- PWGHF/TableProducer/candidateSelectorLc.cxx | 2 +- .../candidateSelectorXicToPKPi.cxx | 2 +- .../candidateSelectorXicToXiPiPi.cxx | 2 +- PWGHF/TableProducer/mcPidTof.cxx | 18 +-- .../TableProducer/treeCreatorXicToXiPiPi.cxx | 12 +- PWGHF/Tasks/taskLcCentrality.cxx | 4 +- PWGHF/Tasks/taskMcValidation.cxx | 2 +- 55 files changed, 236 insertions(+), 236 deletions(-) diff --git a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx index 89299a0136c..93ec3498cd0 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx @@ -144,7 +144,7 @@ struct HfCandidateSelectorB0ToDPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx index a72783a8e12..5673db5971b 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx @@ -139,7 +139,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx index 988df046c06..7a9e71e26bc 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx @@ -131,7 +131,7 @@ struct HfCandidateSelectorBsToDsPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx index 705f23ae97e..80b38931c13 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx @@ -132,7 +132,7 @@ struct HfCandidateSelectorLbToLcPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx index 0bdc38965c7..49ca42dcee4 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx @@ -249,7 +249,7 @@ struct HfDataCreatorJpsiHadReduced { labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {NumBinsSelections, 0.5, NumBinsSelections + 0.5, ""}; - registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < NumBinsSelections; ++iBin) { registry.get(HIST("hSelectionsJpsi"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -266,7 +266,7 @@ struct HfDataCreatorJpsiHadReduced { } registry.add("hMassJpsi", "J/Psi mass;#it{M}_{#mu#mu} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{600, 2.8, 3.4, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hCpaJpsi", "J/Psi cos#theta_{p};J/Psi cos#theta_{p};Counts", {HistType::kTH1D, {{200, -1., 1, "J/Psi cos#theta_{p}"}}}); std::shared_ptr hFitCandidatesJpsi = registry.add("hFitCandidatesJpsi", "Jpsi candidate counter", {HistType::kTH1D, {axisCands}}); std::shared_ptr hFitCandidatesBPlus = registry.add("hFitCandidatesBPlus", "hFitCandidatesBPlus candidate counter", {HistType::kTH1D, {axisCands}}); diff --git a/PWGHF/D2H/Tasks/taskB0.cxx b/PWGHF/D2H/Tasks/taskB0.cxx index ca89758c2b9..b55eaedad4d 100644 --- a/PWGHF/D2H/Tasks/taskB0.cxx +++ b/PWGHF/D2H/Tasks/taskB0.cxx @@ -79,7 +79,7 @@ struct HfTaskB0 { void init(InitContext const&) { static const AxisSpec axisMassB0 = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hMass", "B^{0} candidates;inv. mass D^{#minus}#pi^{#plus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisMassB0, axisPt}}); registry.add("hDecLength", "B^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx index fdcf6be8b80..9431ca25590 100644 --- a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -342,7 +342,7 @@ struct HfTaskB0ToJpsiK0StarReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtK0Star{100, 0.f, 10.f}; registry.add("hMass", b0CandTitle + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskBplus.cxx b/PWGHF/D2H/Tasks/taskBplus.cxx index f3223f55eba..98d12dc90f7 100644 --- a/PWGHF/D2H/Tasks/taskBplus.cxx +++ b/PWGHF/D2H/Tasks/taskBplus.cxx @@ -98,7 +98,7 @@ struct HfTaskBplus { const AxisSpec axisNormDecLength{40, 0., 20}; const AxisSpec axisEta{100, -2., 2.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMass, axisPtB}}); registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); @@ -127,8 +127,8 @@ struct HfTaskBplus { registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); registry.add("hPtGenWithRapidityBelowHalf", "MC particles (generated - |#it{y}^{gen}|<0.5);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); registry.add("hPtGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // histograms MC: Reco Level registry.add("hCPARecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); registry.add("hCPARecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index bdb9a91b64f..6f1ea153985 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -303,7 +303,7 @@ struct HfTaskBplusToJpsiKReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; const AxisSpec axisPtKa{100, 0.f, 10.f}; registry.add("hMass", bPlusCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskBs.cxx b/PWGHF/D2H/Tasks/taskBs.cxx index 46b4dacec14..b6635bf670a 100644 --- a/PWGHF/D2H/Tasks/taskBs.cxx +++ b/PWGHF/D2H/Tasks/taskBs.cxx @@ -79,7 +79,7 @@ struct HfTaskBs { void init(InitContext const&) { static const AxisSpec axisMassBs = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hEta", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); registry.add("hRapidity", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index 4853f6c6035..86d6a7cfbab 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -329,7 +329,7 @@ struct HfTaskBsToJpsiPhiReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; const AxisSpec axisPtPhi{100, 0.f, 10.f}; registry.add("hMass", bsCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskD0.cxx b/PWGHF/D2H/Tasks/taskD0.cxx index 3b0f37b233d..59778af1fa0 100644 --- a/PWGHF/D2H/Tasks/taskD0.cxx +++ b/PWGHF/D2H/Tasks/taskD0.cxx @@ -272,7 +272,7 @@ struct HfTaskD0 { if ((storeZdcEnergy || storeZdcTime) && !(doprocessDataWithDCAFitterNWithUpc || doprocessDataWithDCAFitterNMlWithUpc)) { LOGP(fatal, "Can't enable the storeZdcEnergy and storeZdcTime without UPC process"); } - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0., 5.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassVsPhi", "2-prong candidates vs phi;inv. mass (#pi K) (GeV/#it{c}^{2});phi (rad);entries", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {32, 0, o2::constants::math::TwoPI}}}); registry.add("hDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx index cc0bcff73fb..03c39466c18 100644 --- a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx +++ b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx @@ -137,7 +137,7 @@ struct HfTaskDstarToD0Pi { if ((doprocessDataWoML && doprocessDataWML) || (doprocessMcWoMl && doprocessMcWML) || (doprocessDataWoML && doprocessMcWML) || (doprocessDataWML && doprocessMcWoMl)) { LOGP(fatal, "Only Without-ML or With-ML functions should be enabled at a time! Please check your configuration!"); } - auto vecPtBins = (std::vector)ptBins; + auto vecPtBins = std::vector(ptBins); AxisSpec const axisImpactParam = {binningImpactParam, "impact parameter (cm)"}; AxisSpec const axisDecayLength = {binningDecayLength, " decay length (cm)"}; diff --git a/PWGHF/D2H/Tasks/taskLb.cxx b/PWGHF/D2H/Tasks/taskLb.cxx index 9a605393658..6ad885313c6 100644 --- a/PWGHF/D2H/Tasks/taskLb.cxx +++ b/PWGHF/D2H/Tasks/taskLb.cxx @@ -121,65 +121,65 @@ struct HfTaskLb { void init(InitContext&) { - registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // Now add MC histograms - registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); } void processData(aod::Collisions::iterator const& collision, diff --git a/PWGHF/D2H/Tasks/taskLc.cxx b/PWGHF/D2H/Tasks/taskLc.cxx index c74ef2485ba..68bc5266735 100644 --- a/PWGHF/D2H/Tasks/taskLc.cxx +++ b/PWGHF/D2H/Tasks/taskLc.cxx @@ -204,7 +204,7 @@ struct HfTaskLc { addHistogramsRec("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); addHistogramsGen("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// mass candidate if (isData) { registry.add("Data/hMassVsPtVsNPvContributors", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; Number of PV contributors", {HistType::kTH3F, {{600, 1.98, 2.58}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {5000, 0., 10000.}}}); diff --git a/PWGHF/D2H/Tasks/taskUpcLc.cxx b/PWGHF/D2H/Tasks/taskUpcLc.cxx index d9be942e4e0..9d8d28b9d8a 100644 --- a/PWGHF/D2H/Tasks/taskUpcLc.cxx +++ b/PWGHF/D2H/Tasks/taskUpcLc.cxx @@ -190,7 +190,7 @@ struct HfTaskUpcLc { LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); } - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{200, 0., 200}, {200, 0., 200}}}); registry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); registry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); diff --git a/PWGHF/D2H/Tasks/taskXic.cxx b/PWGHF/D2H/Tasks/taskXic.cxx index 46dfdd64521..e00f8af2153 100644 --- a/PWGHF/D2H/Tasks/taskXic.cxx +++ b/PWGHF/D2H/Tasks/taskXic.cxx @@ -125,7 +125,7 @@ struct HfTaskXic { AxisSpec const axisNSigmaPi = {100, -6.f, 6.f, "n#it{#sigma}_{#pi}"}; AxisSpec const axisNSigmaKa = {100, -6.f, 6.f, "n#it{#sigma}_{K}"}; - auto vbins = (std::vector)binsPt; // histo in pt bins + auto vbins = std::vector(binsPt); // histo in pt bins registry.add("Data/hMassVsPt", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 2., 3.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLength", "3-prong candidates;decay length (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLengthXY", "3-prong candidates;decay length XY (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -175,8 +175,8 @@ struct HfTaskXic { registry.add("Data/hPVsTOFNSigmaKa_Prong2", "3-prong candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{K} TOF", {HistType::kTH2F, {axisPPid, axisNSigmaKa}}); // MC generated - registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // MC reconstructed:signal registry.add("MC/reconstructed/signal/hMassSig", "Invariant mass (matched);m (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx index b6b709c7c55..880952751cf 100644 --- a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx +++ b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx @@ -110,7 +110,7 @@ struct HfTaskXicToXiPiPi { static const AxisSpec axisMassXic = {300, 1.8, 3.0, "inv. mass (GeV/#it{c}^{2})"}; static const AxisSpec axisMassXiRes = {300, 1.4, 2.7, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; static const AxisSpec axisDecLength = {binsDecLength}; static const AxisSpec axisErrDecLength = {binsErrDecLength}; static const AxisSpec axisDCA = {binsDCA}; diff --git a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx index 328c05f9bbd..fd5b2ca77bc 100644 --- a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx +++ b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx @@ -325,11 +325,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t normalizeMePlot(hMeSideb[iPool], hMeSidebSoftPi[iPool]); // Apply Event Mixing Correction - hCorrSign[iPool] = reinterpret_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); + hCorrSign[iPool] = dynamic_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); hCorrSign[iPool]->Sumw2(); hCorrSign[iPool]->Divide(hMeSign[iPool]); - hCorrSideb[iPool] = reinterpret_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); + hCorrSideb[iPool] = dynamic_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); hCorrSideb[iPool]->Sumw2(); hCorrSideb[iPool]->Divide(hMeSideb[iPool]); @@ -374,8 +374,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Pools integration if (iPool == 0) { - h2DSign = reinterpret_cast(hCorrSign[0]->Clone("h2D_Sign")); - h2DSideb = reinterpret_cast(hCorrSideb[0]->Clone("h2D_Sideb")); + h2DSign = dynamic_cast(hCorrSign[0]->Clone("h2D_Sign")); + h2DSideb = dynamic_cast(hCorrSideb[0]->Clone("h2D_Sideb")); h2DSign->Sumw2(); h2DSideb->Sumw2(); } else { @@ -425,7 +425,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Bkg subtraction (2D plot) auto* c2DSub = new TCanvas(Form("c2D_Subtr_IntPools_PtHAd%.0fto%.0f", ptHadMin, ptHadMax), Form("c2D_%s_Subtr_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1500, 800); - h2DSubtr = reinterpret_cast(h2DSign->Clone("h2D_Subtr")); + h2DSubtr = dynamic_cast(h2DSign->Clone("h2D_Subtr")); h2DSubtr->Sumw2(); h2DSubtr->Add(h2DSideb, -1); h2DSubtr->SetEntries(h2DSign->GetEntries() - h2DSideb->GetEntries()); @@ -443,7 +443,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DSideb->Scale(1. / h1DSideb->GetXaxis()->GetBinWidth(1)); // Bkg subtraction (1D plot) - h1DSubtr = reinterpret_cast(h1DSign->Clone("h1D_Subtr")); + h1DSubtr = dynamic_cast(h1DSign->Clone("h1D_Subtr")); h1DSubtr->Sumw2(); h1DSubtr->Add(h1DSideb, -1); h1DSubtr->SetEntries(h1DSign->GetEntries() - h1DSideb->GetEntries()); @@ -462,8 +462,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/h1D_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); if (fDebug >= 1) { - h1DSignNorm = reinterpret_cast(h1DSign->Clone("h1D_Sign_Norm")); - h1DSidebNorm = reinterpret_cast(h1DSideb->Clone("h1D_Sideb_Norm")); + h1DSignNorm = dynamic_cast(h1DSign->Clone("h1D_Sign_Norm")); + h1DSidebNorm = dynamic_cast(h1DSideb->Clone("h1D_Sideb_Norm")); h1DSignNorm->Scale(1. / (fSgnYieldNorm + fBkgYield)); // h1D_SidebNorm -> Scale(1./fBkgYield); h1DSidebNorm->Scale(1. / fBkgScaleFactor); @@ -488,7 +488,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonSignalSidebCorr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); } // Apply normalization to number of triggers - h1DSubtrNorm = reinterpret_cast(h1DSubtr->Clone("h1D_SubtrNorm")); + h1DSubtrNorm = dynamic_cast(h1DSubtr->Clone("h1D_SubtrNorm")); h1DSubtrNorm->Sumw2(); h1DSubtrNorm->Scale(1. / fSgnYieldNorm); h1DSubtrNorm->SetTitle("Signal region after sideb. subt. corr. - Normalized to # of triggers"); @@ -498,7 +498,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hModul = evaluateMcClosModulations(ptCandMin, ptCandMax, ptHadMin, ptHadMax); auto* c1DCorrBbias = new TCanvas(Form("c1D_corrBbias_IntPools_PtCand%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax), Form("c1D_corrBbias_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1600, 500); c1DCorrBbias->cd(); - hBeforeModulCorr = reinterpret_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); + hBeforeModulCorr = dynamic_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); hBeforeModulCorr->SetLineColor(kViolet - 3); hBeforeModulCorr->GetYaxis()->SetRangeUser(0., 5.); hBeforeModulCorr->Draw(); @@ -508,10 +508,10 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1DCorrBbias->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonCorrBiasBtoD_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); auto* file = new TFile(Form("Output_CorrelationExtraction_%s_Root/SystematicCorrBiasBtoD_%s_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax), "RECREATE"); // Open file in write mode - TH1D* h1DSubtrNormClone = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); + TH1D* h1DSubtrNormClone = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); h1DSubtrNormClone = reflectCorrHistogram(h1DSubtrNormClone); hBeforeModulCorr = reflectCorrHistogram(hBeforeModulCorr); - TH1D* hSystematicCorrBiasBtoD = reinterpret_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); + TH1D* hSystematicCorrBiasBtoD = dynamic_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); hSystematicCorrBiasBtoD->Add(h1DSubtrNormClone, hBeforeModulCorr, 1, -1); // Set bin contents to absolute values for (int i = 1; i <= hSystematicCorrBiasBtoD->GetNbinsX(); ++i) { @@ -533,7 +533,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DAllPartCorr->RebinX(fRebinAxisDeltaPhi); std::cout << "Secondary particle histogram rebinned" << std::endl; } - h1DSecPartFrac = reinterpret_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h1DSecPartFrac = dynamic_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); h1DSecPartFrac->Sumw2(); h1DSecPartFrac->Divide(h1DPrimaryPartCorr, h1DAllPartCorr, 1., 1., "B"); @@ -544,7 +544,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_png/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - h1DSubtrNormSecPart = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); + h1DSubtrNormSecPart = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); h1DSubtrNormSecPart->Sumw2(); Int_t const nBinsPhi = h1DSubtrNormSecPart->GetNbinsX(); if (nBinsPhi != h1DSecPartFrac->GetNbinsX()) { @@ -562,7 +562,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DFdTemplatePrompt->Scale(1. / h1DFdTemplatePrompt->GetXaxis()->GetBinWidth(1)); h1DFdTemplateNonPrompt->Scale(1. / h1DFdTemplateNonPrompt->GetXaxis()->GetBinWidth(1)); - h1DTemplateTotal = reinterpret_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); + h1DTemplateTotal = dynamic_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); h1DTemplateTotal->Sumw2(); h1DTemplateTotal->Scale(fdPromptFrac); h1DTemplateTotal->Add(h1DFdTemplateNonPrompt, 1 - fdPromptFrac); @@ -611,16 +611,16 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t std::cout << " " << std::endl; Double_t const baselinediff = baselineData - baselineFd; - TH1D* hBaselineDiff = reinterpret_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); + TH1D* hBaselineDiff = dynamic_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); for (int iBin = 0; iBin < hBaselineDiff->GetNbinsX(); iBin++) { hBaselineDiff->SetBinContent(iBin + 1, baselinediff); } h1DFdTemplateNonPrompt->Add(hBaselineDiff); h1DTemplateTotal->Add(hBaselineDiff); if (fSecPartContamination) { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); } else { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); } h1DFdTemplateNonPrompt->Scale(1 - fdPromptFrac); h1DSubtrFdNorm->Add(h1DFdTemplateNonPrompt, -1); @@ -667,11 +667,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t } if (fFDsubtraction) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else if (fSecPartContamination) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } std::cout << "Analysis steps completed - baseline subtraction missing" << std::endl; @@ -715,7 +715,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Baseline subtraction Double_t baselineData, baselineDataErr; - TH1D* hBaseline = reinterpret_cast(h1DSubtrNorm->Clone("hBaseline")); + TH1D* hBaseline = dynamic_cast(h1DSubtrNorm->Clone("hBaseline")); hBaseline->Sumw2(); if (fFDsubtraction) { baselineData = calculateBaseline(h1DSubtrFdNorm, kTRUE, kFALSE); // introduced kFALSE @@ -724,7 +724,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else if (fSecPartContamination) { baselineData = calculateBaseline(h1DSubtrNormSecPart, kTRUE, kFALSE); @@ -733,7 +733,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else { baselineData = calculateBaseline(h1DSubtrNorm, kTRUE, kFALSE); @@ -742,11 +742,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } - fCorrectedCorrHistoBaselineSubtr = reinterpret_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoBaselineSubtr = dynamic_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); auto* cFinalBaselineSubtr = new TCanvas(Form("cFinal_BaselineSubtr_%.0fto%.0f", ptHadMin, ptHadMax), Form("cFinal_BaselineSubtr_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1200, 700); h1DBaselineSubtr->SetMarkerColor(kOrange + 8); @@ -795,7 +795,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflected->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); // Reflected histograms baseline subtracted - TH1D* hBaselineRefl = reinterpret_cast(h1DReflCorr->Clone("hBaseline_Refl")); + TH1D* hBaselineRefl = dynamic_cast(h1DReflCorr->Clone("hBaseline_Refl")); hBaselineRefl->Sumw2(); baselineData = calculateBaseline(h1DReflCorr, kFALSE, kTRUE); baselineDataErr = calculateBaselineError(h1DReflCorr, kFALSE, kTRUE); @@ -804,7 +804,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaselineRefl->SetBinContent(iBin + 1, baselineData); hBaselineRefl->SetBinError(iBin + 1, baselineDataErr); } - h1DReflCorrBaselineSubtr = reinterpret_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); + h1DReflCorrBaselineSubtr = dynamic_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); h1DReflCorrBaselineSubtr->Sumw2(); h1DReflCorrBaselineSubtr->Add(hBaselineRefl, -1.); @@ -832,8 +832,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_png/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - fCorrectedCorrHistoReflected = reinterpret_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); - fCorrectedCorrHistoReflectedBaselineSubtr = reinterpret_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflected = dynamic_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflectedBaselineSubtr = dynamic_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); return kTRUE; } @@ -853,8 +853,8 @@ Bool_t DhCorrelationExtraction::readInputSeAndMe() return kFALSE; } - fDirSE = reinterpret_cast(fFileSE->Get(fDirNameSE.Data())); - fDirME = reinterpret_cast(fFileME->Get(fDirNameME.Data())); + fDirSE = dynamic_cast(fFileSE->Get(fDirNameSE.Data())); + fDirME = dynamic_cast(fFileME->Get(fDirNameME.Data())); std::cout << "===================== " << std::endl; std::cout << "Read inputs SE and ME" << std::endl; @@ -925,7 +925,7 @@ Bool_t DhCorrelationExtraction::readInputSecondaryPartContamination() return kFALSE; } - fDirSecPart = reinterpret_cast(fFileSecPart->Get(fDirSecPartName.Data())); + fDirSecPart = dynamic_cast(fFileSecPart->Get(fDirSecPartName.Data())); if (fDirSecPart == nullptr) { std::cout << "[ERROR] Directory " << fDirSecPart << " cannot be opened! check your file path!" << std::endl; @@ -957,10 +957,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou } // TODO: generalise this part - TH1D* hRecPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hRecNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); printf("[INFO] Bin cand %d - Bin had %d \n", fBinPtCand, fBinPtHad); @@ -974,10 +974,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou hGenPrompt->Sumw2(); hGenNonPrompt->Sumw2(); - TH1D* hRatioNonPrompt = reinterpret_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); + TH1D* hRatioNonPrompt = dynamic_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); hRatioNonPrompt->Sumw2(); hRatioNonPrompt->Divide(hRecNonPrompt, hGenNonPrompt, 1., 1., "B"); - hModul = reinterpret_cast(hRatioNonPrompt->Clone("hModul")); + hModul = dynamic_cast(hRatioNonPrompt->Clone("hModul")); TF1* funFit = new TF1("funFit", "[0]", TMath::Pi() * 3. / 8., TMath::Pi() * 3 / 2); hRatioNonPrompt->Fit(funFit, "R"); @@ -1026,51 +1026,51 @@ TH2D* DhCorrelationExtraction::getCorrelHisto(Int_t sEorMe, Int_t sorSb, Int_t p THnSparseD* hSparse = nullptr; if (sEorMe == kSE) { // Same Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } else { // Mixed Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } /*else if (fSidebandDivided) { // Mixed Event - if (SorSB == kSign) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); - } else if (!fSidebandDivided) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandsName.Data()); + if (SorSB == kSign) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); + } else if (!fSidebandDivided) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandsName.Data()); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); hSparse -> SetName("hSparse"); - THnSparseD *hSparseRightSideb = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + THnSparseD *hSparseRightSideb = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); hSparse -> Add(hSparseRightSideb, 1.); } } @@ -1140,11 +1140,11 @@ void DhCorrelationExtraction::getSignalAndBackgroundForNorm(Double_t ptCandMin, { // using results obtained from HFInvariantMassFitter.cxx class - TH1F* hMassFitSgnYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSgn.Data())); - TH1F* hMassFitBkgYield = reinterpret_cast(fFileMass->Get(fMassHistoNameBkg.Data())); - TH1F* hMassFitSBsYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSBs.Data())); - TH1F* hMassFitSBLYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandLeft")); - TH1F* hMassFitSBRYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandRight")); + TH1F* hMassFitSgnYield = dynamic_cast(fFileMass->Get(fMassHistoNameSgn.Data())); + TH1F* hMassFitBkgYield = dynamic_cast(fFileMass->Get(fMassHistoNameBkg.Data())); + TH1F* hMassFitSBsYield = dynamic_cast(fFileMass->Get(fMassHistoNameSBs.Data())); + TH1F* hMassFitSBLYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandLeft")); + TH1F* hMassFitSBRYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandRight")); Int_t const ptCandBin = hMassFitSgnYield->FindBin(ptCandMin + 0.01); if (ptCandBin != hMassFitSgnYield->FindBin(ptCandMax - 0.01)) { @@ -1188,9 +1188,9 @@ TH2D* DhCorrelationExtraction::getFdTemplateHisto(Int_t promptOrFd, Double_t ptC TH2D* h2D = nullptr; // pointer to be returned if (promptOrFd == kPrompt) { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } Int_t binExtEtaMin = h2D->GetXaxis()->FindBin(fDeltaEtaMin + 0.000001); @@ -1222,9 +1222,9 @@ TH1D* DhCorrelationExtraction::getCorrelHistoSecondaryPart(Int_t partType, Doubl THnSparseD* hSparse = nullptr; if (partType == kPrimaryPart) { // primary particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); } else { // all selected particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoAllPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoAllPartName.Data())); } Int_t const binExtPtCandMin = hSparse->GetAxis(2)->FindBin(ptCandMin + 0.01); // axis2: ptCand, the 0.01 to avoid bin edges! Int_t const binExtPtCandMax = hSparse->GetAxis(2)->FindBin(ptCandMax - 0.01); @@ -1274,7 +1274,7 @@ TH1D* DhCorrelationExtraction::reflectCorrHistogram(TH1D*& histo) TH1D* h1D = new TH1D("h1D_Reflected", "", nBinsPhiRefl, 0., TMath::Pi()); // pointer to be returned h1D->Sumw2(); - // TH1D* h1D = reinterpret_cast histo -> Clone("h1D_Reflected"); + // TH1D* h1D = dynamic_cast histo -> Clone("h1D_Reflected"); // h1D -> GetXaxis() -> SetRange(bin0Phi, binPiPhi); // reflection @@ -1331,7 +1331,7 @@ TH1D* DhCorrelationExtraction::reflectHistoRun2(TH1D* h, Double_t scale) Double_t DhCorrelationExtraction::getFdPromptFrac(Double_t ptCandMin, Double_t ptCandMax, Double_t /*ptHadMin*/, Double_t /*ptHadMax*/) { - TH1D* h1D = reinterpret_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); + TH1D* h1D = dynamic_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); Int_t const binPtCandMin = h1D->GetXaxis()->FindBin(ptCandMin + 0.01); Int_t const binPtCandMax = h1D->GetXaxis()->FindBin(ptCandMax - 0.01); diff --git a/PWGHF/HFC/Macros/FitCorrel.C b/PWGHF/HFC/Macros/FitCorrel.C index 5d29d451eaf..e5b88cb5bd2 100644 --- a/PWGHF/HFC/Macros/FitCorrel.C +++ b/PWGHF/HFC/Macros/FitCorrel.C @@ -187,12 +187,12 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { for (int iBinPtHad = 0; iBinPtHad < nBinsPtHad; iBinPtHad++) { if (isReflected) { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } else { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } - corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); + corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); // FIXME: invalid cast corrFitter[iBinPtHad][iBinPtCand]->setHistoIsReflected(refl); corrFitter[iBinPtHad][iBinPtCand]->setFixBaseline(fixBase); corrFitter[iBinPtHad][iBinPtCand]->setBaselineUpOrDown(shiftBaseUp, shiftBaseDown); @@ -238,8 +238,8 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") // draw systematic errors int const nBinsPhi = hCorrPhi[iBinPtCand][iBinPtHad]->GetNbinsX(); if (drawSystematicErrors) { - hSystematicErrors[iBinPtCand][iBinPtHad] = reinterpret_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); - hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrors[iBinPtCand][iBinPtHad] = dynamic_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); for (int iPhi = 0; iPhi < nBinsPhi; iPhi++) { if (sameSystematics) { hSystematicErrorsPlot[iBinPtCand][iBinPtHad]->SetBinError(iPhi + 1, hSystematicErrors[iBinPtCand][iBinPtHad]->GetBinContent(1) * hCorrPhi[iBinPtCand][iBinPtHad]->GetBinContent(iPhi + 1)); @@ -391,11 +391,11 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") hBaselin[iBinPtHad]->Draw(); if (drawSystematicErrors) { - TH1F* hBaselinSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hBaselinSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { hBaselinSyst->SetBinError(iBinPtCand + 1, hBaselinSyst->GetBinContent(iBinPtCand + 1) * hBaselin[iBinPtHad]->GetBinContent(iBinPtCand + 1)); diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx index dbef6c1ee60..ba37e499c1e 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx @@ -135,7 +135,7 @@ struct HfCorrelatorD0D0bar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx index ff4bdbba50e..3f1289e4413 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx @@ -136,7 +136,7 @@ struct HfCorrelatorD0D0barBarrelFullPid { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx index eabbaa2fcbd..cd7edd8130a 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx @@ -250,7 +250,7 @@ struct HfCorrelatorD0Hadrons { Filter trackFilter = requireGlobalTrackWoDCAInFilter() && (nabs(aod::track::eta) < etaTrackMax) && (aod::track::pt > ptTrackMin) && (aod::track::pt < ptTrackMax) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); Filter d0Filter = (aod::hf_sel_candidate_d0::isSelD0 >= 1) || (aod::hf_sel_candidate_d0::isSelD0bar >= 1); Filter collisionFilterGen = aod::hf_selection_dmeson_collision::dmesonSel == true; - Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); + Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)) == static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)); Preslice perCol = aod::hf_cand::collisionId; Preslice perCollisionID = aod::track::collisionId; @@ -279,8 +279,8 @@ struct HfCorrelatorD0Hadrons { AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; AxisSpec const axisRapidity = {100, -5., 5., "Rapidity"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx index 14eed44af7f..897d192eaa4 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx @@ -139,7 +139,7 @@ struct HfCorrelatorDplusDminus { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "Dplus,Dminus candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDplus", "Dplus,Dminus candidates;inv. mass Dplus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDminus", "Dplus,Dminus candidates;inv. mass Dminus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index a4d52996823..6f7c3f3c6a1 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -266,8 +266,8 @@ struct HfCorrelatorDplusHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx index 17add0a004d..7404d0f6a61 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx @@ -255,8 +255,8 @@ struct HfCorrelatorDsHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (K^{#pm}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx index 9808b2c627a..d72373f1b9b 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx @@ -88,8 +88,8 @@ struct HfCorrelatorDsHadronsReduced { AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; // Histograms for data analysis if (fillHistoData) { diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index 777eff5c01b..50e99a602e1 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -185,8 +185,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { const AxisSpec axisPoolBin = {poolBins, 0., static_cast(poolBins), "PoolBin"}; const AxisSpec axisDeltaEta = {binsDeltaEta, "#Delta#it{#eta}"}; const AxisSpec axisDeltaPhi = {binsDeltaPhi, "#Delta#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisMlOne{binsMlOne, "bdtScore0"}; const AxisSpec axisMlTwo{binsMlTwo, "bdtScore1"}; diff --git a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx index 1781e20ac59..25a7091e6b8 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx @@ -290,8 +290,8 @@ struct HfCorrelatorLcHadrons { AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtLc = {(std::vector)binsPtLc, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtLc), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec axisPtTrack = {500, 0, 50, "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 8ac8e23acdd..06ae7c8a758 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -178,8 +178,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { const AxisSpec axisPosZ = {binsPosZ, "PosZ"}; const AxisSpec axisEta = {binsEta, "#it{#eta}"}; const AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisDcaXY = {binsDca, "DCA XY (cm)"}; const AxisSpec axisDcaZ = {binsDca, "DCA Z (cm)"}; diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx index dbf1d9deb2d..dc7eeb02b03 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx @@ -435,9 +435,9 @@ struct HfProducerCharmHadronsTrackFemtoDream { void fillDebugParticle(ParticleType const& particle) { tables.outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), + static_cast(particle.tpcNClsFound()), particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), + static_cast(particle.tpcNClsCrossedRows()), particle.tpcNClsShared(), particle.tpcInnerParam(), particle.itsNCls(), diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx index fa5c70e33d0..4b8594f8833 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx @@ -449,9 +449,9 @@ struct HfProducerCharmHadronsV0FemtoDream { { if constexpr (isTrackOrV0) { outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), + static_cast(particle.tpcNClsFound()), particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), + static_cast(particle.tpcNClsCrossedRows()), particle.tpcNClsShared(), particle.tpcInnerParam(), particle.itsNCls(), diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx index d4012fbdba8..cfbd216ec22 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx @@ -202,10 +202,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax; Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmSel.charmHadCandSel; Filter hfMcSelFilter = (nabs(aod::fdhf::flagMc) == charmSel.charmHadMcSel); - Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); - Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); - Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); - Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); + Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); + Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); + Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); + Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); struct : PresliceGroup { Preslice perCol = aod::femtodreamparticle::fdCollisionId; @@ -216,14 +216,14 @@ struct HfTaskCharmHadronsTrackFemtoDream { } preslices; /// Partition for particle 1 - Partition partitionTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); + Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); - Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && + Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); /// Partition for particle 2 Partition partitionCharmHadron3Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx index 8dc31b37116..29538febfce 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx @@ -195,7 +195,7 @@ struct HfTaskCharmHadronsV0FemtoDream { Preslice perHfDstarByCol = aod::femtodreamparticle::fdCollisionId; /// Partitions for particle Lambda - Partition partitionLambda = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0)) && + Partition partitionLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -206,7 +206,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); - Partition partitionMcLambda = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0)) && + Partition partitionMcLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -218,7 +218,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); /// Partitions for particle K0Short - Partition partitionK0Short = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + Partition partitionK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -229,7 +229,7 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); - Partition partitionMcK0Short = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + Partition partitionMcK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && diff --git a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx index e9510c42a1c..873c0bc052c 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx @@ -155,8 +155,8 @@ struct HfTaskCorrelationD0Hadrons { AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index 5ac86bf95f9..3a058914624 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -190,16 +190,16 @@ struct HfTaskCorrelationDplusHadrons { { // Axis definition AxisSpec axisMassD = {binsMassD, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtD = {(std::vector)binsPtEfficiencyD, "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtEfficiencyD), "#it{p}_{T}^{D} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisDplusPrompt = {2, -0.5, 1.5, "Prompt D+"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; - AxisSpec axisPtBMother = {(std::vector)binsPtBMother, "#it{p}_{T}^{B} (GeV/#it{c})"}; + AxisSpec axisPtBMother = {std::vector(binsPtBMother), "#it{p}_{T}^{B} (GeV/#it{c})"}; AxisSpec axisDeltaPhiBMother = {binsDeltaPhiBMother, "#it{#varphi}^{D}-#it{#varphi}^{B} (rad)"}; // Histograms for data analysis @@ -345,7 +345,7 @@ struct HfTaskCorrelationDplusHadrons { const int regionLimits = 6; std::string labels[regionLimits] = {"SigReg Left", "SigReg Right", "Left SB Low", "Left SB Up", "Right SB Low", "Right SB Up"}; static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); + auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iLim = 0; iLim < regionLimits; iLim++) { hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); } diff --git a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx index 61c87645ba4..0df60e1e5de 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx @@ -196,9 +196,9 @@ struct HfTaskCorrelationDsHadrons { AxisSpec axisDetlaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDetlaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisCentrality = {(std::vector)binsCentrality, "centrality FT0M (%)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisCentrality = {std::vector(binsCentrality), "centrality FT0M (%)"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; diff --git a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx index e14bc59a504..a8c1004df3e 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx @@ -124,7 +124,7 @@ struct HfTaskCorrelationDstarHadrons { void init(InitContext&) { - auto axisPtDstar = (std::vector)binsPtEfficiency; + auto axisPtDstar = std::vector(binsPtEfficiency); AxisSpec const axisSpecPtDstar = {axisPtDstar}; AxisSpec const axisSpecDeltaPhi = {nBinsDeltaPhi, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}; AxisSpec const axisSpecDeltaEta = {deltaEtaBinEdges}; diff --git a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx index 6293db1ea25..3261cad3f5b 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx @@ -215,12 +215,12 @@ struct HfTaskCorrelationLcHadrons { { // Axis definition AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; - AxisSpec axisPtLc = {(std::vector)binsPtEfficiencyLc, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtEfficiencyLc), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{#Lambda_c}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{#Lambda_c} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisLcPrompt = {2, -0.5, 1.5, "Prompt #Lambda_c"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; @@ -431,7 +431,7 @@ struct HfTaskCorrelationLcHadrons { "Right SB Low", "Right SB Up"}; static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); + auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iLim = 0; iLim < regionLimits; iLim++) { hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); } diff --git a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx index 78f9ac10578..3248701ba77 100644 --- a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx +++ b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx @@ -838,7 +838,7 @@ struct HfTaskElectronWeakBoson { if (ueCfg.useUEsub) { rho = estimateRhoRC(emcClusters); - energyUE = rho * static_cast(o2::constants::math::PI * rIsolation * rIsolation); + energyUE = rho * (o2::constants::math::PI * rIsolation * rIsolation); registry.fill(HIST("hEnergyUE"), centrality, energyUE); // LOG(info) << "UE = " << energyUE; } diff --git a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C index 9d3dab4d407..c0e92758d40 100644 --- a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C +++ b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C @@ -318,7 +318,7 @@ void computeFonllPlusPythiaPredictions(int nDecays, int seed, std::string inFile for (auto iFonll{0}; iFonll < 3; ++iFonll) { normCrossSec[iFonll] = hFonllBhad[iFonll]->Integral(1, hFonllBhad[iFonll]->GetNbinsX() + 1, "width"); for (auto iChad{0}; iChad < NCharmHadrons; ++iChad) { - hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = reinterpret_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); + hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = dynamic_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll]->Reset(); for (auto iBHad{0}; iBHad < NBeautyHadrons; ++iBHad) { hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][iBHad][iFonll]->Scale(normCrossSec[iFonll] / nDecays, "width"); diff --git a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx index c083d530baf..358df7a0326 100644 --- a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx @@ -136,7 +136,7 @@ struct HfCandidateSelectorB0ToDPi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx index 7b51a915c4c..9c538f544fe 100644 --- a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx @@ -139,7 +139,7 @@ struct HfCandidateSelectorBplusToD0Pi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx index eee6a06b93f..376d485bffd 100644 --- a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx @@ -120,7 +120,7 @@ struct HfCandidateSelectorBsToDsPi { labels[1 + SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorCd.cxx b/PWGHF/TableProducer/candidateSelectorCd.cxx index 7658c95cf11..1fcbdbcfedf 100644 --- a/PWGHF/TableProducer/candidateSelectorCd.cxx +++ b/PWGHF/TableProducer/candidateSelectorCd.cxx @@ -110,7 +110,7 @@ struct HfCandidateSelectorCd { labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {NBinsSelections, 0.5, NBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < NBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx index b66c7ac8540..53e26443b6c 100644 --- a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx @@ -126,7 +126,7 @@ struct HfCandidateSelectorDplusToPiKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx index bbe41e275fe..153c19e3f6d 100644 --- a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx @@ -127,7 +127,7 @@ struct HfCandidateSelectorDsToKKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx index 0d24a58d91b..ee2310e4d34 100644 --- a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx @@ -147,7 +147,7 @@ struct HfCandidateSelectorDstarToD0Pi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPtDstar, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPtDstar), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("QA/hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorLc.cxx b/PWGHF/TableProducer/candidateSelectorLc.cxx index a3155b30e3e..5088a876513 100644 --- a/PWGHF/TableProducer/candidateSelectorLc.cxx +++ b/PWGHF/TableProducer/candidateSelectorLc.cxx @@ -156,7 +156,7 @@ struct HfCandidateSelectorLc { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx index cd7d7028925..525b569b0e1 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx @@ -123,7 +123,7 @@ struct HfCandidateSelectorXicToPKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 070e92a43e2..1abe826680c 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -736,7 +736,7 @@ struct HfCandidateSelectorXicToXiPiPi { } bool isMatchedCandidate = false; - if (hfCandXic.flagMcMatchRec() != int8_t(0)) { + if (hfCandXic.flagMcMatchRec() != static_cast(0)) { isMatchedCandidate = true; } diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 2ab5832ff9e..78dda49a10c 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -573,7 +573,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -639,7 +639,7 @@ struct TofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } @@ -650,7 +650,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -682,14 +682,14 @@ struct TofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision()) { // Track was not assigned, cannot compute event time tableFlags(0); @@ -973,16 +973,16 @@ struct McPidTof { auto* calibList = ccdb->getSpecific(mcRecalib.ccdbPath, timestamp, metadata); std::vector updatedSpecies{}; for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses (tiny) - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); updatedSpecies.push_back(pidId); } for (auto const& pidId : mEnabledParticlesFull) { // Loop on enabled particle hypotheses (full) if (std::find(updatedSpecies.begin(), updatedSpecies.end(), pidId) != updatedSpecies.end()) { continue; } - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); } } diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index 2bf9c358bcf..6ae1bed98dd 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -422,10 +422,10 @@ struct HfTreeCreatorXicToXiPiPi { Filter filterSelectCandidates = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= selectionFlagXic; Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) || fillGenInfoForAcceptance == true); - Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); - Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); + Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); + Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); void init(InitContext const&) { @@ -993,14 +993,14 @@ struct HfTreeCreatorXicToXiPiPi { rowCandidateLiteMl.reserve(candidates.size()); if (fillOnlySignal) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() == int8_t(0)) { + if (candidate.flagMcMatchRec() == static_cast(0)) { continue; } fillCandidateTable(candidate); } } else if (fillOnlyBackground) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() != int8_t(0)) { + if (candidate.flagMcMatchRec() != static_cast(0)) { continue; } float const pseudoRndm = candidate.ptProng1() * 1000. - static_cast(candidate.ptProng1() * 1000); diff --git a/PWGHF/Tasks/taskLcCentrality.cxx b/PWGHF/Tasks/taskLcCentrality.cxx index ce3752b40e1..0a34527690f 100644 --- a/PWGHF/Tasks/taskLcCentrality.cxx +++ b/PWGHF/Tasks/taskLcCentrality.cxx @@ -71,7 +71,7 @@ struct HfTaskLcCentrality { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; centrality", {HistType::kTH3F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, 0., 100.}}}); registry.add("hDecLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -147,7 +147,7 @@ struct HfTaskLcCentralityMc { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigPrompt", "3-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigNonPrompt", "3-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/Tasks/taskMcValidation.cxx b/PWGHF/Tasks/taskMcValidation.cxx index 3167bcd8f69..8fe64b12f51 100644 --- a/PWGHF/Tasks/taskMcValidation.cxx +++ b/PWGHF/Tasks/taskMcValidation.cxx @@ -1037,7 +1037,7 @@ struct HfTaskMcValidationRec { nITSlayers++; } } - uint const index = uint(track.collisionId() >= 0); + uint const index = static_cast(track.collisionId() >= 0); if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); // get corresponding MC particle to check origin const auto& mcCollision = particle.mcCollision_as(); From 76911ce0c6d07fd37ce290ae72a23ccb769139e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:54:56 +0100 Subject: [PATCH 454/648] [Tools] Fix casts (#18393) --- Tools/KFparticle/qaKFEventTrack.cxx | 4 ++-- Tools/KFparticle/qaKFParticleLc.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tools/KFparticle/qaKFEventTrack.cxx b/Tools/KFparticle/qaKFEventTrack.cxx index 7ff14e80c1c..6e026a08f4e 100644 --- a/Tools/KFparticle/qaKFEventTrack.cxx +++ b/Tools/KFparticle/qaKFEventTrack.cxx @@ -247,7 +247,7 @@ struct qaKFEventTrack { template void writeVarTree(const T1& /*kfpTrackPi*/, const T2& KFPion, const T2& KFPV, const T3& track, const int source, const int pVContrib, const int runNumber, const T4& collision) { - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { if (writeTree) { /// Filling the tree @@ -629,7 +629,7 @@ struct qaKFEvent { if (!isSelectedCollision(collisionIndex)) { continue; } - timeColl = uint64_t((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); + timeColl = static_cast((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); writeVarTreeColl(collisionIndex, timeColl, bc.timestamp(), collisionIndex.collisionTime(), tfID); } } diff --git a/Tools/KFparticle/qaKFParticleLc.cxx b/Tools/KFparticle/qaKFParticleLc.cxx index eb7c68eeaa8..f9e5c11e4a6 100644 --- a/Tools/KFparticle/qaKFParticleLc.cxx +++ b/Tools/KFparticle/qaKFParticleLc.cxx @@ -416,7 +416,7 @@ struct qaKFParticleLc { float chi2geo = KFLc.GetChi2() / KFLc.GetNDF(); float normdecayLength = KFLc_PV.GetDecayLength() / KFLc_PV.GetErrDecayLength(); float chi2topo = KFLc_PV.GetChi2() / KFLc_PV.GetNDF(); - const double pseudoRndm = trackKa.pt() * 1000. - (int64_t)(trackKa.pt() * 1000); + const double pseudoRndm = trackKa.pt() * 1000. - static_cast(trackKa.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { /// Filling the D0 tree rowKFLc(runNumber, From 144555ad28d1791b7ac81dcbb022022a6f176248 Mon Sep 17 00:00:00 2001 From: "Q.Y. Xia" <91366503+huinaibing@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:33:08 +0800 Subject: [PATCH 455/648] [PWGCF] Fix PV association option type and mode-dependent QA (#18473) --- PWGCF/Flow/Tasks/pidFlowPtCorr.cxx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index da6e46f41be..4b9427ff61e 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -21,6 +21,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/CollisionAssociation.h" #include "Common/Core/TableHelper.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" @@ -308,7 +309,8 @@ struct PidFlowChargedTimeProducer { bool timeUniformField = false; bool cfgRequireUniqueTimeAssociation = false; bool cfgTimeAssociationQA = true; - bool timeUsePVAssociation = true; + int timeUsePVAssociation = aod::track_association::PVContrReassocOpt::OnlySameBc; + int timeMaxPvContributorsForLowMultReassoc = 10; float timeNSigma = 4.f; float timeMargin = 0.f; float cutEta = 0.8f; @@ -331,7 +333,8 @@ struct PidFlowChargedTimeProducer { } cfgRequireUniqueTimeAssociation = timeAssociationOption(context, "pid-flow-pt-corr", "cfgRequireUniqueTimeAssociation"); cfgTimeAssociationQA = timeAssociationOption(context, "pid-flow-pt-corr", "cfgTimeAssociationQA"); - timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); + timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); + timeMaxPvContributorsForLowMultReassoc = timeAssociationOption(context, "track-to-collision-association", "maxPvContributorsForLowMultReassoc"); timeNSigma = timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat"); timeMargin = timeAssociationOption(context, "track-to-collision-association", "timeMargin"); cutEta = timeAssociationOption(context, "pid-flow-pt-corr", "trkQualityOpts.cfgCutEta"); @@ -380,7 +383,8 @@ struct PidFlowChargedTimeProducer { metadata += ";producer.nSigmaForTimeCompat=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat")); metadata += ";producer.timeMargin=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "timeMargin")); metadata += ";producer.bcWindowForOneSigma=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "bcWindowForOneSigma")); - metadata += ";producer.usePVAssociation=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"))); + metadata += ";producer.usePVAssociation=" + std::to_string(timeUsePVAssociation); + metadata += ";producer.maxPvContributorsForLowMultReassoc=" + std::to_string(timeMaxPvContributorsForLowMultReassoc); metadata += ";producer.includeUnassigned=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "includeUnassigned"))); chargedTimeAssociationConfig->SetTitle(metadata.c_str()); LOG(info) << "Charged time association: " << metadata; @@ -517,7 +521,11 @@ struct PidFlowChargedTimeProducer { originalTimeStatus = 5; if (collisionIndex >= 0 && collisionIndex < collisions.size()) { auto collision = collisions.rawIteratorAt(collisionIndex); - const bool usePVTime = timeUsePVAssociation && track.isPVContributor(); + // Match the public associator's PV-time substitution, including mode 2. + const bool usePVTime = track.isPVContributor() && + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::OnlySameBc || + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::SameBcAndLowMult && + collision.numContrib() > timeMaxPvContributorsForLowMultReassoc)); const float trackTime = usePVTime ? collision.collisionTime() : track.trackTime(); const float trackRes = usePVTime ? o2::constants::lhc::LHCBunchSpacingNS : track.trackTimeRes(); const float collisionRes = collision.collisionTimeRes(); From de6550ea1baab8320b13c27e2c93819b71028a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:59:58 +0100 Subject: [PATCH 456/648] [PWGDQ] Remove HfHelper instances (#18435) --- PWGDQ/Core/VarManager.h | 66 ++++++++++++------------ PWGDQ/TableProducer/tableMakerJpsiHf.cxx | 30 +++++------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index c83f57daa7c..87464d09990 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -23,6 +23,8 @@ #include "PWGUD/Core/UDHelpers.h" #include "PWGUD/Core/UPCHelpers.h" +// +#include "PWGHF/Core/HfHelper.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" @@ -1562,10 +1564,10 @@ class VarManager : public TObject static void FillDileptonPhoton(T1 const& dilepton, T2 const& photon, float* values = nullptr); template static void FillHadron(T const& hadron, float* values = nullptr, float hadronMass = 0.0f); - template - static void FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); - template - static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values = nullptr); template static void FillQVectorFromGFW(C const& collision, A const& compA11, A const& compB11, A const& compC11, A const& compA21, A const& compB21, A const& compC21, A const& compA31, A const& compB31, A const& compC31, A const& compA41, A const& compB41, A const& compC41, A const& compA23, A const& compA42, float S10A = 1.0, float S10B = 1.0, float S10C = 1.0, float S11A = 1.0, float S11B = 1.0, float S11C = 1.0, float S12A = 1.0, float S13A = 1.0, float S14A = 1.0, float S21A = 1.0, float S22A = 1.0, float S31A = 1.0, float S41A = 1.0, float* values = nullptr); template @@ -3899,12 +3901,12 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -4460,12 +4462,12 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -4888,12 +4890,12 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) values[kMCP2] = t2.p(); // polarization parameters - bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame - bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame + bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame + bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame bool useGJB1 = fgUsedVars[kMCCosThetaGJB1] || fgUsedVars[kMCPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) bool useGJB2 = fgUsedVars[kMCCosThetaGJB2] || fgUsedVars[kMCPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) - bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame + bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -6829,8 +6831,8 @@ void VarManager::FillHadron(T const& hadron, float* values, float hadronMass) values[kRap] = vhadron.Rapidity(); } -template -void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values) { if (!values) { values = static_cast(fgValues); @@ -6843,33 +6845,33 @@ void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper values[kRap] = candidate.rap(); } if constexpr (partType == kD0ToPiK) { - values[kMassCharmHadron] = hfHelper.invMassD0ToPiK(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0ToPiK(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } if constexpr (partType == kD0barToKPi) { - values[kMassCharmHadron] = hfHelper.invMassD0barToKPi(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0barToKPi(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } if constexpr (partType == kDplusToPiKPi) { - values[kMassCharmHadron] = hfHelper.invMassDplusToPiKPi(candidate); + values[kMassCharmHadron] = HfHelper::invMassDplusToPiKPi(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yDplus(candidate); + values[kRapCharmHadron] = HfHelper::yDplus(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } } -template -void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values) { - FillSingleDileptonCharmHadron(dilepton, hfHelper, bdtScoreCharmHad, values); - FillSingleDileptonCharmHadron(charmHadron, hfHelper, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(dilepton, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(charmHadron, bdtScoreCharmHad, values); } template @@ -7767,12 +7769,12 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; diff --git a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx index 59b06e3ef64..65f69e394d9 100644 --- a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx +++ b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx @@ -102,8 +102,6 @@ using TracksWithExtra = soa::Join; using MyBarrelTracksSelectedWithColl = soa::Join; using MyMuonTracksSelectedWithColl = soa::Join; -HfHelper hfHelper; - struct tableMakerJpsiHf { // // This task combines dilepton candidates with a open charm hadron @@ -321,9 +319,9 @@ struct tableMakerJpsiHf { double rapDmeson = -999.; if constexpr (TDmeson == DmesonType::D0) { - rapDmeson = hfHelper.yD0(dmeson); + rapDmeson = HfHelper::yD0(dmeson); } else if constexpr (TDmeson == DmesonType::Dplus) { - rapDmeson = hfHelper.yDplus(dmeson); + rapDmeson = HfHelper::yDplus(dmeson); } if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { @@ -332,18 +330,18 @@ struct tableMakerJpsiHf { if constexpr (TDmeson == DmesonType::D0) { if (dmeson.isSelD0() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } if (dmeson.isSelD0bar() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[3], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } } else if constexpr (TDmeson == DmesonType::Dplus) { if (dmeson.isSelDplusToPiKPi() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores_dplus[0], fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dmeson, scores_dplus[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -366,7 +364,7 @@ struct tableMakerJpsiHf { if (configDebug) { float dummyScore{-999.f}; - VarManager::FillSingleDileptonCharmHadron(dilepton, hfHelper, dummyScore, fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dilepton, dummyScore, fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsi", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -391,9 +389,9 @@ struct tableMakerJpsiHf { double rapDmeson = -999.; if constexpr (TDmeson == DmesonType::D0) { - rapDmeson = hfHelper.yD0(dmeson); + rapDmeson = HfHelper::yD0(dmeson); } else if constexpr (TDmeson == DmesonType::Dplus) { - rapDmeson = hfHelper.yDplus(dmeson); + rapDmeson = HfHelper::yDplus(dmeson); } if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { @@ -471,14 +469,14 @@ struct tableMakerJpsiHf { if constexpr (TDmeson == DmesonType::D0) { if (dmeson.isSelD0() >= 1) { - massD0 = hfHelper.invMassD0ToPiK(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); + massD0 = HfHelper::invMassD0ToPiK(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } if (dmeson.isSelD0bar() >= 1) { - massD0bar = hfHelper.invMassD0barToKPi(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); + massD0bar = HfHelper::invMassD0barToKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[3], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } @@ -487,8 +485,8 @@ struct tableMakerJpsiHf { } } else if constexpr (TDmeson == DmesonType::Dplus) { if (dmeson.isSelDplusToPiKPi() >= 1) { - massDplus = hfHelper.invMassDplusToPiKPi(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores_dplus[0], fValuesDileptonCharmHadron); + massDplus = HfHelper::invMassDplusToPiKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores_dplus[0], fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } From c1fd84b077b2a959c17651f545bd5f35237fc592 Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Tue, 27 Oct 2026 13:08:18 +0100 Subject: [PATCH 457/648] [Tutorial] [O2AT6] Potential tutorial task for GPU execution of neural networks (#18471) Co-authored-by: ALICE Action Bot --- Tutorials/ML/CMakeLists.txt | 11 ++ Tutorials/ML/README-benchmark.md | 123 +++++++++++++ Tutorials/ML/benchmarkOnnxModel.cxx | 266 ++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 Tutorials/ML/README-benchmark.md create mode 100644 Tutorials/ML/benchmarkOnnxModel.cxx diff --git a/Tutorials/ML/CMakeLists.txt b/Tutorials/ML/CMakeLists.txt index db28093cd7d..ad8a5477b88 100644 --- a/Tutorials/ML/CMakeLists.txt +++ b/Tutorials/ML/CMakeLists.txt @@ -19,3 +19,14 @@ o2physics_add_dpl_workflow(apply-ml-selection SOURCES applyMlSelection.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME AnalysisTutorial) + +o2physics_add_dpl_workflow(benchmark-onnx-model + SOURCES benchmarkOnnxModel.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::ML O2::GPUCommon ONNXRuntime::ONNXRuntime + TARGETVARNAME benchmarkOnnxModelTarget + COMPONENT_NAME AnalysisTutorial) + +# System libraries are linked directly: the workflow helper requires namespaced targets. +if(CMAKE_DL_LIBS) + target_link_libraries(${benchmarkOnnxModelTarget} PRIVATE ${CMAKE_DL_LIBS}) +endif() diff --git a/Tutorials/ML/README-benchmark.md b/Tutorials/ML/README-benchmark.md new file mode 100644 index 00000000000..65702fc5ede --- /dev/null +++ b/Tutorials/ML/README-benchmark.md @@ -0,0 +1,123 @@ +# ONNX inference benchmark tutorial + +`benchmarkOnnxModel.cxx` registers `o2-analysis-benchmark-onnx-model`. +It runs once with deterministic synthetic data, writes one CSV row, and exits. +It accepts one dense floating-point input of shape `[batch, features]` and one +or more outputs. Static dimensions must match the options; symbolic dimensions +are filled from the options. Output types are left to ONNX Runtime. + +## Build and GPU prerequisites + +Build O2Physics normally after loading its O2 environment. This target links +`O2::ML`, `O2::GPUCommon`, `ONNXRuntime::ONNXRuntime`, and the platform dynamic +loader. No HIP or CUDA headers are required for building the task. The O2 installation +must contain `ML/3rdparty/GPUORTFloat16.h` and its GPUCommon headers. + +For AMD GPU runs, ONNX Runtime must contain **MIGraphXExecutionProvider**, and its +MIGraphX/ROCm shared libraries, including `libamdhip64.so`, must be discoverable +at runtime. The task checks provider availability and disables CPU fallback: +unsupported graphs fail rather than producing a misleading GPU benchmark. +No changes to O2's OrtInterface are needed for this task; the current checked-out +OrtInterface does not attach an AMD execution provider, so this example uses +ORT directly. + +For NVIDIA runs, select `--provider cuda`. ONNX Runtime must contain +**CUDAExecutionProvider**, with matching CUDA/cuDNN libraries and an NVIDIA +driver. Telemetry loads `libcudart.so` (also tries `.so.13`, `.so.12`, `.so.11.0`) +from the library search path. Keep this runtime consistent with the ORT CUDA +build. CUDA provider options require ORT 1.18 or newer: `use_tf32=0` disables +TF32 so the FP32 benchmark does not silently use TF32 matrix multiplication. +Both GPU backends disable CPU fallback and use the same timing/CSV path. + +Check the actual hydra driver, ROCm, MIGraphX and ORT combination first. MI100 +(gfx908) and MI50 (gfx906) do not have identical software support. MI50 is absent +from current supported ROCm hardware lists, so it needs a compatible older +stack or a separately validated build. The old ROCm execution provider was +removed from ONNX Runtime starting with 1.23; this task uses MIGraphX. + +References: +- +- +- +- + +## Run the three comparisons + +Use equivalent FP32 and FP16 exports of the same network. Replace the example +feature count with the actual model input width. Use separate output files; +each invocation overwrites its output CSV. + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cpu --fp16 false --batch-size 1024 --features 5 \ + --cpu-threads 1 --warmup 20 --iterations 200 --output-csv cpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider migraphx --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider migraphx --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp16.csv +``` + +For NVIDIA, replace `--provider migraphx` with `--provider cuda`, for example: + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cuda --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider cuda --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp16.csv +``` + +`gpu-id` is the logical ID after `HIP_VISIBLE_DEVICES` / `ROCR_VISIBLE_DEVICES` +masking on AMD or `CUDA_VISIBLE_DEVICES` masking on NVIDIA. Run one benchmark process at a time on an idle +GPU. Repeat with the same batch sizes and CPU thread count, and compare several +runs. Small batches can be dominated by host overhead and transfers. + +`--fp16 true` selects O2's `o2::OrtDataType::Float16_t` input storage and checks +that the model input is FLOAT16. It does not convert model weights or prove that +every internal operation is FP16. Export/inspect the model accordingly. FP16 +models retaining FP32 input interfaces need an adapted input path; this small +tutorial intentionally rejects them. CPU runs require FP32 input. + +## What the CSV measures + +- `samples_per_second = batch_size * iterations / seconds`. +- Timing covers synchronous `Session::Run`, input/output transfers, and output + allocation/destruction. It excludes session creation, MIGraphX compilation, + warmup, input generation, FP16 conversion, telemetry, and CSV I/O. This is + host-to-host inference throughput, not GPU kernel-only throughput. +- GPU memory fields are **device-wide used bytes**, sampled through + `hipMemGetInfo` (AMD) or `cudaMemGetInfo` (NVIDIA) before session creation, after warmup, and after timed runs. + They include other processes and runtime allocations; they are neither + model-only memory nor a transient peak. The baseline follows GPU runtime/context + initialization. CPU runs leave these fields empty. GPU telemetry failures + fail the run rather than reporting zero usage. +- Precision labels describe the requested model/input variant, not measured + arithmetic precision. Strict CPU fallback rejection can reject graphs that + would otherwise run with some CPU operators. + +This benchmark does not check prediction accuracy. Compare real model outputs +between the exports separately before interpreting performance gains. + +## Adding the AO2D input later + +Planned input on hydra: + +```text +/lustre/alice/users/marin/R3A/TPCTrees/TPCTrees_LHC24as_250224/AO2D_merge_LHC24as_updatedBB_250224.root +``` + +The file has not been inspected here. Do not pass `--aod-file` to this standalone +synthetic task yet. First establish the AO2D tables, feature columns and order, +training normalization, selections, and missing-value policy. Then add an +analysis `process(...)` input with the appropriate table definitions, build +contiguous batches from its rows, and reuse the inference path. Keep session +creation in `init`, aggregate timing/sample counts across batches, and write +results at end of stream. Handle the final partial batch explicitly (dynamic +batch, or pad and count only valid samples). Keep data reading/preprocessing +outside inference timing or report a separate full-pipeline metric. diff --git a/Tutorials/ML/benchmarkOnnxModel.cxx b/Tutorials/ML/benchmarkOnnxModel.cxx new file mode 100644 index 00000000000..67677940346 --- /dev/null +++ b/Tutorials/ML/benchmarkOnnxModel.cxx @@ -0,0 +1,266 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// Benchmark one dense ONNX input [batch, features] using CPU, AMD MIGraphX or NVIDIA CUDA. +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +// Load GPU telemetry at runtime so CPU builds need no HIP or CUDA headers. +// Memory is device-wide, not process-local; samples are not a transient peak. +class GpuMemory +{ + public: + explicit GpuMemory(int device, bool cuda) + { + const std::vector libraries = cuda + ? std::vector{"libcudart.so", "libcudart.so.13", "libcudart.so.12", "libcudart.so.11.0"} + : std::vector{"libamdhip64.so"}; + for (auto name : libraries) { + library = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + if (library) { + break; + } + } + if (!library) { + throw std::runtime_error(cuda ? "GPU telemetry requires libcudart.so (CUDA 11/12/13) on LD_LIBRARY_PATH" + : "GPU telemetry requires libamdhip64.so on LD_LIBRARY_PATH"); + } + auto setDevice = reinterpret_cast(dlsym(library, cuda ? "cudaSetDevice" : "hipSetDevice")); + memoryFunction = cuda ? "cudaMemGetInfo" : "hipMemGetInfo"; + getMemory = reinterpret_cast(dlsym(library, memoryFunction)); + if (!setDevice || !getMemory || setDevice(device) != 0) { + dlclose(library); + library = nullptr; + throw std::runtime_error(cuda ? "Cannot select CUDA device or resolve CUDA memory API" + : "Cannot select HIP device or resolve HIP memory API"); + } + } + ~GpuMemory() + { + if (library) { + dlclose(library); + } + } + GpuMemory(GpuMemory const&) = delete; + GpuMemory& operator=(GpuMemory const&) = delete; + uint64_t used() const + { + size_t free = 0, total = 0; + if (getMemory(&free, &total) != 0 || free > total) { + throw std::runtime_error(std::string(memoryFunction) + " failed"); + } + return total - free; + } + + private: + const char* memoryFunction = nullptr; + void* library = nullptr; + int (*getMemory)(size_t*, size_t*) = nullptr; +}; + +std::string csvQuote(std::string const& value) +{ + std::string result = "\""; + for (char c : value) { + result += c; + if (c == '"') { + result += '"'; + } + } + return result + '"'; +} +} // namespace + +using namespace o2::framework; + +struct BenchmarkOnnxModel { + Configurable modelPath{"model-path", "model.onnx", "Local ONNX model"}; + Configurable provider{"provider", "cpu", "cpu, migraphx or cuda"}; + Configurable gpuId{"gpu-id", 0, "HIP/CUDA logical device ID (after visibility masking)"}; + Configurable fp16{"fp16", false, "Require FP16 input; does not convert model weights"}; + Configurable batchSize{"batch-size", 1024, "Samples per inference"}; + Configurable features{"features", 5, "Features per sample, in model order"}; + Configurable warmup{"warmup", 10, "Untimed warm-up calls"}; + Configurable iterations{"iterations", 100, "Timed inference calls"}; + Configurable cpuThreads{"cpu-threads", 1, "ORT intra-op CPU threads"}; + Configurable outputCsv{"output-csv", "onnx-benchmark.csv", "CSV output (overwritten)"}; + + void run(ProcessingContext& pc) + { + benchmark(); + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + + void benchmark() + { + if (batchSize.value <= 0 || features.value <= 0 || iterations.value <= 0 || warmup.value < 1 || cpuThreads.value <= 0 || gpuId.value < 0) { + throw std::runtime_error("Require positive batch, features, iterations, warmup, threads and nonnegative GPU ID"); + } + bool cuda = provider.value == "cuda"; + bool gpu = cuda || provider.value == "migraphx"; + if (!gpu && provider.value != "cpu") { + throw std::runtime_error("provider must be cpu, migraphx or cuda"); + } + if (!gpu && fp16.value) { + throw std::runtime_error("Use FP32 for the CPU baseline"); + } + std::unique_ptr memory; + uint64_t before = 0, afterWarmup = 0, afterRun = 0; + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "tutorial-benchmark"); + Ort::SessionOptions options; + options.SetIntraOpNumThreads(cpuThreads.value); + options.SetInterOpNumThreads(1); + options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); + if (gpu) { + const std::string providerName = cuda ? "CUDAExecutionProvider" : "MIGraphXExecutionProvider"; + auto providers = Ort::GetAvailableProviders(); + if (std::find(providers.begin(), providers.end(), providerName) == providers.end()) { + throw std::runtime_error("This ONNX Runtime has no " + providerName); + } + memory = std::make_unique(gpuId.value, cuda); + before = memory->used(); + if (cuda) { + // V2 options allow disabling TF32 for the FP32 comparison. + OrtCUDAProviderOptionsV2* raw = nullptr; + Ort::ThrowOnError(Ort::GetApi().CreateCUDAProviderOptions(&raw)); + std::unique_ptr nvidia(raw, Ort::GetApi().ReleaseCUDAProviderOptions); + const std::string device = std::to_string(gpuId.value); + const char* keys[] = {"device_id", "use_tf32"}; + const char* values[] = {device.c_str(), "0"}; + Ort::ThrowOnError(Ort::GetApi().UpdateCUDAProviderOptions(nvidia.get(), keys, values, 2)); + options.AppendExecutionProvider_CUDA_V2(*nvidia); + } else { + OrtMIGraphXProviderOptions amd{}; + amd.device_id = gpuId.value; + amd.migraphx_mem_limit = std::numeric_limits::max(); + // Keep the supplied model precision; do not automatically quantize FP32. + amd.migraphx_fp16_enable = 0; + options.AppendExecutionProvider_MIGraphX(amd); + } + options.AddConfigEntry("session.disable_cpu_ep_fallback", "1"); + } + Ort::Session session(env, modelPath.value.c_str(), options); + if (session.GetInputCount() != 1 || session.GetOutputCount() < 1) { + throw std::runtime_error("Tutorial expects exactly one dense input and at least one output"); + } + auto inputInfo = session.GetInputTypeInfo(0); + auto tensorInfo = inputInfo.GetTensorTypeAndShapeInfo(); + auto shape = tensorInfo.GetShape(); + auto type = fp16.value ? ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16 : ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT; + if (tensorInfo.GetElementType() != type || shape.size() != 2 || + (shape[0] >= 0 && shape[0] != batchSize.value) || (shape[1] >= 0 && shape[1] != features.value)) { + throw std::runtime_error("Input must match fp16 flag and [batch-size, features]; only dynamic dimensions may be overridden"); + } + shape = {batchSize.value, features.value}; + const size_t count = static_cast(batchSize.value) * features.value; + if (count > std::numeric_limits::max() / sizeof(float)) { + throw std::runtime_error("Input size overflow"); + } + // Replace this feature filling with AO2D extraction and training preprocessing later. + std::vector floats(fp16.value ? 0 : count); + std::vector halves(fp16.value ? count : 0); + static_assert(sizeof(o2::OrtDataType::Float16_t) == 2); + for (size_t i = 0; i < count; ++i) { + float value = static_cast(i % 101) / 100.f - 0.5f; + if (fp16.value) { + halves[i] = o2::OrtDataType::Float16_t(value); + } else { + floats[i] = value; + } + } + auto host = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + void* data = fp16.value ? static_cast(halves.data()) : static_cast(floats.data()); + size_t bytes = count * (fp16.value ? sizeof(halves[0]) : sizeof(floats[0])); + auto input = Ort::Value::CreateTensor(host, data, bytes, shape.data(), shape.size(), type); + Ort::AllocatorWithDefaultOptions allocator; + auto inputName = session.GetInputNameAllocated(0, allocator); + const char* inputNames[] = {inputName.get()}; + std::vector names; + names.reserve(session.GetOutputCount()); + for (size_t i = 0; i < session.GetOutputCount(); ++i) { + names.emplace_back(session.GetOutputNameAllocated(i, allocator).get()); + } + std::vector outputNames; + outputNames.reserve(names.size()); + for (auto const& name : names) { + outputNames.push_back(name.c_str()); + } + Ort::RunOptions runOptions; + auto infer = [&]() { + // Default synchronous Run with CPU input/output includes host/device transfers + // and output allocation/destruction. No asynchronous Run option is enabled. + auto outputs = session.Run(runOptions, inputNames, &input, 1, outputNames.data(), outputNames.size()); + }; + for (int i = 0; i < warmup.value; ++i) { + infer(); + } + if (memory) { + afterWarmup = memory->used(); + } + auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < iterations.value; ++i) { + infer(); + } + double seconds = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + if (memory) { + afterRun = memory->used(); + } + std::ofstream csv(outputCsv.value, std::ios::trunc); + if (!csv) { + throw std::runtime_error("Cannot open output-csv"); + } + csv << "model,ort_version,provider,precision,gpu_id,batch_size,features,warmup,iterations,cpu_threads,seconds,samples_per_second,gpu_used_before_bytes,gpu_used_after_warmup_bytes,gpu_used_after_run_bytes\n"; + csv << csvQuote(modelPath.value) << ',' << csvQuote(Ort::GetVersionString()) << ',' << provider.value << ',' + << (fp16.value ? "fp16" : "fp32") << ',' << (gpu ? gpuId.value : -1) << ',' + << batchSize.value << ',' << features.value << ',' << warmup.value << ',' << iterations.value << ',' << cpuThreads.value << ',' + << std::setprecision(12) << seconds << ',' << (static_cast(batchSize.value) * iterations.value / seconds) << ','; + if (memory) { + csv << before << ',' << afterWarmup << ',' << afterRun; + } else { + csv << ",,"; + } + csv << '\n'; + csv.close(); + if (!csv) { + throw std::runtime_error("Failed writing output-csv"); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From e4c37c357b1f769a1ffc91cad31671080edaf2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:10:10 +0100 Subject: [PATCH 458/648] [Common] Fix casts (#18382) --- Common/Core/FFitWeights.cxx | 18 ++++----- Common/Core/PID/PIDTOFParamService.cxx | 2 +- Common/Core/Zorro.cxx | 8 ++-- Common/Core/ZorroSummary.cxx | 2 +- Common/Core/macros/testMetadataHelper.C | 4 +- Common/TableProducer/PID/pidTOFBase.cxx | 10 ++--- Common/TableProducer/PID/pidTOFMerge.cxx | 10 ++--- Common/TableProducer/PID/pidTPCBase.cxx | 4 +- Common/TableProducer/match-mft-ft0.cxx | 22 +++++----- .../TableProducer/match-mft-mch-data-mc.cxx | 6 +-- Common/TableProducer/match-mft-mch-data.cxx | 6 +-- Common/TableProducer/muonRealignment.cxx | 20 +++++----- Common/TableProducer/trackselection.cxx | 4 +- Common/TableProducer/zdcTaskLightIons.cxx | 24 +++++------ Common/Tasks/checkDataModelMC.cxx | 2 +- Common/Tasks/multiplicityQa.cxx | 2 +- Common/Tasks/qaMuon.cxx | 40 +++++++++---------- Common/Tools/Multiplicity/MultModule.h | 24 +++++------ .../Multiplicity/macros/runCalibration.C | 12 +++--- .../Tools/Multiplicity/macros/runGlauberFit.C | 18 ++++----- .../Multiplicity/macros/saveCorrelation.C | 12 +++--- Common/Tools/Multiplicity/multCalibrator.cxx | 2 +- .../Tools/Multiplicity/multMCCalibrator.cxx | 4 +- Common/Tools/PID/checkPidPacking.cxx | 6 +-- Common/Tools/TrackPropagationModule.h | 2 +- 25 files changed, 132 insertions(+), 132 deletions(-) diff --git a/Common/Core/FFitWeights.cxx b/Common/Core/FFitWeights.cxx index f502b4bc3c4..30d4922d13c 100644 --- a/Common/Core/FFitWeights.cxx +++ b/Common/Core/FFitWeights.cxx @@ -86,8 +86,8 @@ void FFitWeights::init() fW_data->Add(new TProfile("pMeanPt", "", centBin, 0, centBin)); fW_data->Add(new TH2D("hPtWeight", "", centBin, 0, centBin, ptBin, ptAxis->GetXmin(), ptAxis->GetXmax())); - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); - h2ptCent = reinterpret_cast(fW_data->FindObject("hPtWeight")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); + h2ptCent = dynamic_cast(fW_data->FindObject("hPtWeight")); }; void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf) @@ -98,10 +98,10 @@ void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject(this->getQName(nh, pf))); + TH2D* th2 = dynamic_cast(tar->FindObject(this->getQName(nh, pf))); if (!th2) { tar->Add(new TH2D(this->getQName(nh, pf), this->getAxisName(nh, pf), centBin, 0, centBin, qAxis->GetNbins(), qAxis->GetXmin(), qAxis->GetXmax())); - th2 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); + th2 = dynamic_cast(tar->At(tar->GetEntries() - 1)); } th2->Fill(centrality, qn); }; @@ -117,7 +117,7 @@ void FFitWeights::fillPt(float centrality, float pt, float weight, bool first) float FFitWeights::getPtMult(float centrality) { if (!ptProfCent) { - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); } return ptProfCent->GetBinContent(ptProfCent->FindBin(centrality)); }; @@ -132,7 +132,7 @@ Long64_t FFitWeights::Merge(TCollection* collist) } FFitWeights* lW = 0; TIter allW(collist); - while ((lW = (reinterpret_cast(allW())))) { + while ((lW = (dynamic_cast(allW())))) { addArray(fW_data, lW->getDataArray()); nmerged++; } @@ -150,7 +150,7 @@ void FFitWeights::addArray(TObjArray* targ, TObjArray* sour) auto* tObj = targ->FindObject(obj->GetName()); if (!tObj) { - auto* clone = static_cast(obj->Clone(obj->GetName())); + auto* clone = obj->Clone(obj->GetName()); if (auto* h = dynamic_cast(clone)) h->SetDirectory(0); targ->Add(clone); @@ -168,7 +168,7 @@ void FFitWeights::mptSel() if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject("hPtWeight")); + TH2D* th2 = dynamic_cast(tar->FindObject("hPtWeight")); if (!th2) { return; } @@ -198,7 +198,7 @@ void FFitWeights::qSelection(const std::vector& nhv, const std::vector(tar->FindObject(this->getQName(nh, pf.c_str())))}; + TH2D* th2{dynamic_cast(tar->FindObject(this->getQName(nh, pf.c_str())))}; if (!th2) { LOGF(info, "FFitWeights qh not found!"); return; diff --git a/Common/Core/PID/PIDTOFParamService.cxx b/Common/Core/PID/PIDTOFParamService.cxx index efd840273fa..e8138e21e28 100644 --- a/Common/Core/PID/PIDTOFParamService.cxx +++ b/Common/Core/PID/PIDTOFParamService.cxx @@ -298,7 +298,7 @@ struct TOFSupport : o2::framework::ServicePlugin { }, .configure = CommonServices::noConfiguration(), .exit = [](ServiceRegistryRef, void* service) { - auto* resp = reinterpret_cast(service); + auto* resp = static_cast(service); delete resp; }, .kind = ServiceKind::Serial}; } diff --git a/Common/Core/Zorro.cxx b/Common/Core/Zorro.cxx index 6eb0b61c118..f110b8a9a85 100644 --- a/Common/Core/Zorro.cxx +++ b/Common/Core/Zorro.cxx @@ -72,7 +72,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, for (int iBin{2}; iBin < mSelections->GetNbinsX(); ++iBin) { // Exclude first and last bins as they are total number of analysed and selected events, respectively mAnalysedTriggers->GetXaxis()->SetBinLabel(iBin - 1, mSelections->GetXaxis()->GetBinLabel(iBin)); } - std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, static_cast(mSelections->GetNbinsX() - 0.5)}}); + std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, (mSelections->GetNbinsX() - 0.5)}}); selections->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mSelections->GetNbinsX(); ++iBin) { selections->GetXaxis()->SetBinLabel(iBin, mSelections->GetXaxis()->GetBinLabel(iBin)); @@ -81,7 +81,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mScalers) { - std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, static_cast(mScalers->GetNbinsX() - 0.5)}}); + std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, (mScalers->GetNbinsX() - 0.5)}}); scalers->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mScalers->GetNbinsX(); ++iBin) { scalers->GetXaxis()->SetBinLabel(iBin, mScalers->GetXaxis()->GetBinLabel(iBin)); @@ -90,7 +90,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mInspectedTVX) { - std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, static_cast(mInspectedTVX->GetNbinsX() - 0.5)}}); + std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, (mInspectedTVX->GetNbinsX() - 0.5)}}); inspectedTVX->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mInspectedTVX->GetNbinsX(); ++iBin) { inspectedTVX->GetXaxis()->SetBinLabel(iBin, mInspectedTVX->GetXaxis()->GetBinLabel(iBin)); @@ -99,7 +99,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mTOIs.size()) { - mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, static_cast(mTOIs.size() - 0.5)}}).get(); + mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, (mTOIs.size() - 0.5)}}).get(); for (size_t i{0}; i < mTOIs.size(); ++i) { mAnalysedTriggersOfInterest->GetXaxis()->SetBinLabel(i + 1, mTOIs[i].data()); } diff --git a/Common/Core/ZorroSummary.cxx b/Common/Core/ZorroSummary.cxx index 0a1012c3edc..bb020dc0924 100644 --- a/Common/Core/ZorroSummary.cxx +++ b/Common/Core/ZorroSummary.cxx @@ -20,7 +20,7 @@ void ZorroSummary::Copy(TObject& c) const { - static_cast(c) = *this; + dynamic_cast(c) = *this; } Long64_t ZorroSummary::Merge(TCollection* list) diff --git a/Common/Core/macros/testMetadataHelper.C b/Common/Core/macros/testMetadataHelper.C index dd20cba9c44..12b29935455 100644 --- a/Common/Core/macros/testMetadataHelper.C +++ b/Common/Core/macros/testMetadataHelper.C @@ -45,7 +45,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vector { // Get the metadata, if any - auto m = (TMap*)currentFile->Get("metaData"); + auto m = dynamic_cast(currentFile->Get("metaData")); if (!m) { return {}; } @@ -59,7 +59,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vectorGetName()); first = false; } - auto objString = (TObjString*)m->GetValue(obj); + auto objString = dynamic_cast(m->GetValue(obj)); std::string key = "aod-metadata-" + std::string(obj->GetName()); LOGP(info, "- {}: {} goes into key {}", obj->GetName(), objString->String().Data(), key); char const* value = strdup(objString->String()); diff --git a/Common/TableProducer/PID/pidTOFBase.cxx b/Common/TableProducer/PID/pidTOFBase.cxx index 2ef09079f5e..257dbf5b6b9 100644 --- a/Common/TableProducer/PID/pidTOFBase.cxx +++ b/Common/TableProducer/PID/pidTOFBase.cxx @@ -376,7 +376,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -408,7 +408,7 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeTOF.mEventTimeMultiplicity); } } } @@ -438,7 +438,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -504,7 +504,7 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); } } } @@ -529,7 +529,7 @@ struct tofEventTime { for (auto const& t : tracks) { // Loop on collisions if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); diff --git a/Common/TableProducer/PID/pidTOFMerge.cxx b/Common/TableProducer/PID/pidTOFMerge.cxx index f5ec6e68a6f..8b1486f94d2 100644 --- a/Common/TableProducer/PID/pidTOFMerge.cxx +++ b/Common/TableProducer/PID/pidTOFMerge.cxx @@ -405,7 +405,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -470,7 +470,7 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } @@ -481,7 +481,7 @@ struct tofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -513,14 +513,14 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); diff --git a/Common/TableProducer/PID/pidTPCBase.cxx b/Common/TableProducer/PID/pidTPCBase.cxx index 1a86f0f70ec..422a6d7c435 100644 --- a/Common/TableProducer/PID/pidTPCBase.cxx +++ b/Common/TableProducer/PID/pidTPCBase.cxx @@ -61,7 +61,7 @@ struct PidMultiplicity { } using TrksIU = soa::Join; - Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > static_cast(0)); void processIU(aod::Collision const& collision, TrksIU const&) { if (!enableTable) { @@ -73,7 +73,7 @@ struct PidMultiplicity { PROCESS_SWITCH(PidMultiplicity, processIU, "Process with IU tracks, faster but works on Run3 only", false); using Trks = soa::Join; - Partition tracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPC = (aod::track::tpcNClsFindable > static_cast(0)); void processStandard(aod::Collision const& collision, Trks const&) { if (!enableTable) { diff --git a/Common/TableProducer/match-mft-ft0.cxx b/Common/TableProducer/match-mft-ft0.cxx index 43d43ebab84..d282c75c6fe 100644 --- a/Common/TableProducer/match-mft-ft0.cxx +++ b/Common/TableProducer/match-mft-ft0.cxx @@ -147,8 +147,8 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& int64_t minBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() < firstBC + deltaBC) { - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() < firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) < firstBC + deltaBC) { minBCId = bcIt.globalIndex(); ++bcIt; @@ -163,11 +163,11 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& } else { // here bcIt.globalBC() >= firstBC + deltaBC - while (bcIt != bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + while (bcIt != bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); --bcIt; } - if (bcIt == bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + if (bcIt == bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); } ++bcIt; // retrieve the pointer which gave minBCId and minGlobalBC @@ -178,12 +178,12 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& int64_t maxBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC) { + if (static_cast(bcIt.globalBC()) > lastBC + deltaBC) { // the previous minimum is actually bigger than the right boundary if (bcIt != bcs.begin()) { - --bcIt; // let's check the previous element in the BC list - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) // if this previous element is smaller than the left boundary + --bcIt; // let's check the previous element in the BC list + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) // if this previous element is smaller than the left boundary { // means that the slice of compatible BCs is empty @@ -192,18 +192,18 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& } } - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { // the previous minimum is actually smaller than the right boundary ++bcIt; - if (bcIt != bcs.end() && ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC)) { + if (bcIt != bcs.end() && (static_cast(bcIt.globalBC()) > lastBC + deltaBC)) { // check the following element return bcs.emptySlice(); } } - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() <= (int64_t)lastBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) <= lastBC + deltaBC) { maxBCId = bcIt.globalIndex(); ++bcIt; @@ -275,7 +275,7 @@ struct matchmftft0 { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; diff --git a/Common/TableProducer/match-mft-mch-data-mc.cxx b/Common/TableProducer/match-mft-mch-data-mc.cxx index 7b3be6fa633..df7a0846fd9 100644 --- a/Common/TableProducer/match-mft-mch-data-mc.cxx +++ b/Common/TableProducer/match-mft-mch-data-mc.cxx @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/match-mft-mch-data.cxx b/Common/TableProducer/match-mft-mch-data.cxx index 4a6b9be135e..c001315b2cd 100644 --- a/Common/TableProducer/match-mft-mch-data.cxx +++ b/Common/TableProducer/match-mft-mch-data.cxx @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/muonRealignment.cxx b/Common/TableProducer/muonRealignment.cxx index 5b1a597a73c..b0585f8ba9f 100644 --- a/Common/TableProducer/muonRealignment.cxx +++ b/Common/TableProducer/muonRealignment.cxx @@ -289,16 +289,16 @@ struct MuonRealignment { fwdTrkCovRealignInfo.sigPhi = TMath::Sqrt(fwdtrack.getCovariances()(2, 2)); fwdTrkCovRealignInfo.sigTgl = TMath::Sqrt(fwdtrack.getCovariances()(3, 3)); fwdTrkCovRealignInfo.sig1Pt = TMath::Sqrt(fwdtrack.getCovariances()(4, 4)); - fwdTrkCovRealignInfo.rhoXY = (Char_t)(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoPhiX = (Char_t)(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoPhiY = (Char_t)(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglX = (Char_t)(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoTglY = (Char_t)(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtX = (Char_t)(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rho1PtY = (Char_t)(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rho1PtPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtTgl = (Char_t)(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); + fwdTrkCovRealignInfo.rhoXY = static_cast(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoPhiX = static_cast(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoPhiY = static_cast(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglX = static_cast(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoTglY = static_cast(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglPhi = static_cast(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtX = static_cast(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rho1PtY = static_cast(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rho1PtPhi = static_cast(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtTgl = static_cast(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); LOGF(debug, "TrackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d", muon.globalIndex(), muon.x(), muon.y(), muon.z(), muon.phi(), muon.tgl(), muon.signed1Pt(), muon.chi2(), muon.nClusters()); LOGF(debug, "Re-aligned trackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d removable:%d", muonRealignId, fwdtrack.getX(), fwdtrack.getY(), fwdtrack.getZ(), fwdtrack.getPhi(), fwdtrack.getTgl(), fwdtrack.getInvQPt(), fwdtrack.getTrackChi2(), convertedTrack.getNClusters(), removable); // Fill refitted track info diff --git a/Common/TableProducer/trackselection.cxx b/Common/TableProducer/trackselection.cxx index e8619bd9ccb..4cd90b0c1c7 100644 --- a/Common/TableProducer/trackselection.cxx +++ b/Common/TableProducer/trackselection.cxx @@ -160,7 +160,7 @@ struct TrackSelectionTask { const o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)0, + filterTable(static_cast(0), trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), @@ -200,7 +200,7 @@ struct TrackSelectionTask { for (const auto& track : tracks) { o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)globalTracksSDD.IsSelected(track), + filterTable(static_cast(globalTracksSDD.IsSelected(track)), trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), diff --git a/Common/TableProducer/zdcTaskLightIons.cxx b/Common/TableProducer/zdcTaskLightIons.cxx index 1cc5c7826d4..7681551328c 100644 --- a/Common/TableProducer/zdcTaskLightIons.cxx +++ b/Common/TableProducer/zdcTaskLightIons.cxx @@ -128,50 +128,50 @@ struct ZdcTaskLightIons { selected = std::fabs(collision.posZ()) < cfgEvSelVtxZ; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_zvtx); + selectionBits |= static_cast(0x1u << evSel_zvtx); registry.fill(HIST("hEventCount"), evSel_zvtx); } selected = collision.sel8(); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_sel8); + selectionBits |= static_cast(0x1u << evSel_sel8); registry.fill(HIST("hEventCount"), evSel_sel8); } auto occupancy = collision.trackOccupancyInTimeRange(); selected = occupancy <= cfgEvSelsMaxOccupancy; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_occupancy); + selectionBits |= static_cast(0x1u << evSel_occupancy); registry.fill(HIST("hEventCount"), evSel_occupancy); } selected = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoSameBunchPileup); + selectionBits |= static_cast(0x1u << evSel_kNoSameBunchPileup); registry.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodZvtxFT0vsPV); + selectionBits |= static_cast(0x1u << evSel_kIsGoodZvtxFT0vsPV); registry.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoCollInTimeRangeStandard); + selectionBits |= static_cast(0x1u << evSel_kNoCollInTimeRangeStandard); registry.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } selected = collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoITSROFrameBorder); + selectionBits |= static_cast(0x1u << evSel_kNoITSROFrameBorder); registry.fill(HIST("hEventCount"), evSel_kNoITSROFrameBorder); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodITSLayersAll); + selectionBits |= static_cast(0x1u << evSel_kIsGoodITSLayersAll); registry.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } @@ -208,16 +208,16 @@ struct ZdcTaskLightIons { uint8_t maskSel = 0; if (bcPatternB[bcInOrbit]) { - maskSel |= (uint8_t)(0x1u << bcMask_beamB); + maskSel |= static_cast(0x1u << bcMask_beamB); } if (bcPatternA[bcInOrbit]) { - maskSel |= (uint8_t)(0x1u << bcMask_beamA); + maskSel |= static_cast(0x1u << bcMask_beamA); } if (bcPatternC[bcInOrbit]) { - maskSel |= (uint8_t)(0x1u << bcMask_beamC); + maskSel |= static_cast(0x1u << bcMask_beamC); } if (bcPatternE[bcInOrbit]) { - maskSel |= (uint8_t)(0x1u << bcMask_beamE); + maskSel |= static_cast(0x1u << bcMask_beamE); } auto tdcZNA = bc.zdc().timeZNA(); diff --git a/Common/Tasks/checkDataModelMC.cxx b/Common/Tasks/checkDataModelMC.cxx index 8eb6efc03f2..105ada03c72 100644 --- a/Common/Tasks/checkDataModelMC.cxx +++ b/Common/Tasks/checkDataModelMC.cxx @@ -57,7 +57,7 @@ void checkDaughters(const T& particlesMC, } } for (const auto& idxDau : particle.daughtersIds()) { - if (idxDau >= 0 && ((uint64_t)idxDau > offset + particlesMC.size() || (uint64_t)idxDau < offset)) { + if (idxDau >= 0 && (static_cast(idxDau) > offset + particlesMC.size() || static_cast(idxDau) < offset)) { if (debugMode) { debugHisto->Fill(1); } else { diff --git a/Common/Tasks/multiplicityQa.cxx b/Common/Tasks/multiplicityQa.cxx index 3c8c1100af8..6a6c09188cf 100644 --- a/Common/Tasks/multiplicityQa.cxx +++ b/Common/Tasks/multiplicityQa.cxx @@ -347,7 +347,7 @@ struct MultiplicityQa { Preslice perColIU = aod::track::collisionId; using Run3Tracks = soa::Join; - Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & (uint32_t)o2::aod::track::PVContributor) == (uint32_t)o2::aod::track::PVContributor); + Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); void processMCCollisions(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, soa::SmallGroups> const& collisions, Run3Tracks const&) { diff --git a/Common/Tasks/qaMuon.cxx b/Common/Tasks/qaMuon.cxx index dd1a9c6d76d..2e5aa56c8e4 100644 --- a/Common/Tasks/qaMuon.cxx +++ b/Common/Tasks/qaMuon.cxx @@ -372,7 +372,7 @@ struct muonQa { // Global muons plots // ====================== int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("global-muons/nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); AxisSpec nCandidatesAxis = {static_cast(fNCandidatesMax), 0.0, static_cast(fNCandidatesMax), "match candidate rank"}; @@ -1104,7 +1104,7 @@ struct muonQa { template void TransformMFT(TTrack& track) { - if constexpr (static_cast(GlobalFwdFillMap)) { + if constexpr (GlobalFwdFillMap) { auto mchTrack = mMatching.FwdtoMCH(track); doTransformMFT(mchTrack); @@ -1604,7 +1604,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Direct info from AO2D without re-propagation fgValues.pDca = muon.pDca(); fgValues.rabs = muon.rAtAbsorberEnd(); @@ -1772,7 +1772,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { o2::dataformats::GlobalFwdTrack track; SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); @@ -1809,7 +1809,7 @@ struct muonQa { o2::dataformats::GlobalFwdTrack track; - if constexpr (static_cast(Scaled)) { + if constexpr (Scaled) { double pMCH = fgValuesMCH.p; int sign = fgValuesMCH.sign; @@ -1897,7 +1897,7 @@ struct muonQa { float dcaXY = std::sqrt(fgValues.dcaX * fgValues.dcaX + fgValues.dcaY * fgValues.dcaY); fgValues.dcaXY = dcaXY; - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { float p = muon.p(); fgValues.pDca = p * dcaXY; } @@ -1920,7 +1920,7 @@ struct muonQa { template void FillMuonHistograms(Var const& fgValuesMCH, Var const& fgValuesMCHpv, Var const& fgValuesMFT, Var const& fgValuesGlobal, VarVector const& fgValuesCandidates) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMuons.fEtaMchLow, configMuons.fEtaMchUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -1996,7 +1996,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("global-muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -2048,7 +2048,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodGlobalMatching(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow, fMatchingChi2MftMchUp)) { @@ -2155,7 +2155,7 @@ struct muonQa { template void FillDCAHistograms(VarT const& fgValuesMCH, VarT const& fgValuesMCHpv, VarT const& fgValuesMFT, VarC const& fgValuesColl, int sign, int quadrant, bool same, bool mixed) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { auto phiDeg = fgValuesMCH.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[1][quadrant][0]["DCA_x"])->Fill(fgValuesMCH.dcaX); @@ -2184,7 +2184,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto phiDeg = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[0][quadrant][0]["DCA_x"])->Fill(fgValuesMFT.dcaX); @@ -2212,7 +2212,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { auto phiDegMCH = fgValuesMCH.phi * 180.f / TMath::Pi(); auto phiDegMFT = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { @@ -2290,7 +2290,7 @@ struct muonQa { std::array yPos{yCls, fgValuesProp.y}; double phiClus = std::atan2(yCls, xCls) * 180 / TMath::Pi(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (same) { std::get>(mchResidualsHistosPerDE[topBottom][posNeg][chamber]["dx_vs_de"])->Fill(deIndex, xPos[0] - xPos[1]); @@ -2303,7 +2303,7 @@ struct muonQa { } } - if constexpr (static_cast(MFTFillMap)) { + if constexpr (MFTFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodMFT(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow)) { if (same) { @@ -2356,9 +2356,9 @@ struct muonQa { base::Propagator::initFieldFromGRP(grpmag); TrackExtrap::setField(); TrackExtrap::useExtrapV2(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT - double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? - Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT + double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? + Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT } else { LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", bcs.begin().timestamp()); } @@ -2424,7 +2424,7 @@ struct muonQa { typename TMCHTrack, typename VarC, typename VarT> void runDCA(TEventMap const& collisions, TMFTTracks const& mfts, TMFTTrack const& mfttrack, TTrack const& muon, TMCHTrack const& mchtrack, mch::Track const& mchrealigned, VarC& fgValuesColl, VarT& fgValuesMCH, VarT& fgValuesMCHpv, VarT& fgValuesMFT) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { @@ -2456,7 +2456,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto mftsThisCollision = mfts.sliceBy(mftPerCollision, fgValuesColl.globalIndex); for (auto const& mft : mftsThisCollision) { @@ -2499,7 +2499,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { diff --git a/Common/Tools/Multiplicity/MultModule.h b/Common/Tools/Multiplicity/MultModule.h index 168a5b4488a..e9a64798915 100644 --- a/Common/Tools/Multiplicity/MultModule.h +++ b/Common/Tools/Multiplicity/MultModule.h @@ -688,14 +688,14 @@ class MultModule } if (lCalibObjects) { - hVtxZFV0A = static_cast(lCalibObjects->FindObject("hVtxZFV0A")); - hVtxZFT0A = static_cast(lCalibObjects->FindObject("hVtxZFT0A")); - hVtxZFT0C = static_cast(lCalibObjects->FindObject("hVtxZFT0C")); - hVtxZFDDA = static_cast(lCalibObjects->FindObject("hVtxZFDDA")); - hVtxZFDDC = static_cast(lCalibObjects->FindObject("hVtxZFDDC")); - hVtxZNTracks = static_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); - hVtxZNMFTTracks = static_cast(lCalibObjects->FindObject("hVtxZMFT")); - hVtxZNGlobalTracks = static_cast(lCalibObjects->FindObject("hVtxZNGlobals")); + hVtxZFV0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFV0A")); + hVtxZFT0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0A")); + hVtxZFT0C = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0C")); + hVtxZFDDA = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDA")); + hVtxZFDDC = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDC")); + hVtxZNTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); + hVtxZNMFTTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZMFT")); + hVtxZNGlobalTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNGlobals")); lCalibLoaded = true; // Capture error if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZFDDA || !hVtxZFDDC || !hVtxZNTracks) { @@ -1098,11 +1098,11 @@ class MultModule Run2CL1Info.mCalibrationStored = false; if (callst != nullptr) { auto getccdb = [callst](const char* ccdbhname) { - TH1* h = reinterpret_cast(callst->FindObject(ccdbhname)); + TH1* h = dynamic_cast(callst->FindObject(ccdbhname)); return h; }; auto getformulaccdb = [callst](const char* ccdbhname) { - TFormula* f = reinterpret_cast(callst->FindObject(ccdbhname)); + TFormula* f = dynamic_cast(callst->FindObject(ccdbhname)); return f; }; @@ -1253,8 +1253,8 @@ class MultModule if (callst != nullptr) { LOGF(info, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); auto getccdb = [callst, bc](struct CalibrationInfo& estimator, const o2::framework::Configurable& generatorName) { // TODO: to consider the name inside the estimator structure - estimator.mhMultSelCalib = reinterpret_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); - estimator.mMCScale = reinterpret_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); + estimator.mhMultSelCalib = dynamic_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); + estimator.mMCScale = dynamic_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); if (estimator.mhMultSelCalib != nullptr) { if (generatorName->length() != 0) { LOGF(info, "Retrieving MC calibration for %d, generator name: %s", bc.runNumber(), generatorName->c_str()); diff --git a/Common/Tools/Multiplicity/macros/runCalibration.C b/Common/Tools/Multiplicity/macros/runCalibration.C index 0620bb5e1db..1e1d4f96b5f 100644 --- a/Common/Tools/Multiplicity/macros/runCalibration.C +++ b/Common/Tools/Multiplicity/macros/runCalibration.C @@ -38,12 +38,12 @@ void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNB TFile* file = new TFile(lInputFileName.Data(), "READ"); file->ls(); - TH1F* hData = (TH1F*)file->Get("hV0MUltraFine"); - TH1F* hGlauberParameters = (TH1F*)file->Get("hGlauberParameters"); - TH1F* hGlauberFitRange = (TH1F*)file->Get("hGlauberFitRange"); + TH1F* hData = dynamic_cast(file->Get("hV0MUltraFine")); + TH1F* hGlauberParameters = dynamic_cast(file->Get("hGlauberParameters")); + TH1F* hGlauberFitRange = dynamic_cast(file->Get("hGlauberFitRange")); hData->SetName("hData"); - TH1F* hStitched = (TH1F*)hData->Clone("hStitched"); - TH1F* hFit = (TH1F*)file->Get("hGlauber"); + TH1F* hStitched = dynamic_cast(hData->Clone("hStitched")); + TH1F* hFit = dynamic_cast(file->Get("hGlauber")); TCanvas* c1 = new TCanvas("c1", "", 800, 600); c1->SetLeftMargin(0.17); @@ -160,7 +160,7 @@ void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNB // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); g->SetNpartNcollCorrelation(hNpNc); g->InitializeNpNc(); diff --git a/Common/Tools/Multiplicity/macros/runGlauberFit.C b/Common/Tools/Multiplicity/macros/runGlauberFit.C index 5c07a000405..9524985b1ff 100644 --- a/Common/Tools/Multiplicity/macros/runGlauberFit.C +++ b/Common/Tools/Multiplicity/macros/runGlauberFit.C @@ -39,9 +39,9 @@ Double_t FastIntegrate(TF1* f1, Double_t a, Double_t b, Int_t n = 5) // Do fast integration with N sampling points const Int_t nc = n; Double_t x[nc], y[nc]; - Double_t lWidth = (b - a) / ((double)(n - 1)); + Double_t lWidth = (b - a) / (static_cast(n - 1)); for (Int_t ii = 0; ii < n; ii++) { - x[ii] = a + ((double)(ii)) * lWidth; + x[ii] = a + (static_cast(ii)) * lWidth; y[ii] = f1->Eval(x[ii]); } // Now go via trapezoids, please (this probably has a name) @@ -98,7 +98,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", std::cout << "Problem with file!" << std::endl; TH1F* hV0Mfine = 0x0; - hV0Mfine = (TH1F*)file->Get(Form("centrality-study/%s", histogramName.Data())); + hV0Mfine = dynamic_cast(file->Get(Form("centrality-study/%s", histogramName.Data()))); // disregard bin zero std::cout << "Received bin zero content: " << hV0Mfine->GetBinContent(0) << ", will set to zero..." << std::endl; @@ -124,8 +124,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", TFile* fOutput = new TFile(outputFile.Data(), "RECREATE"); - TH1F* hV0M = (TH1F*)hV0Mfine->Clone("hV0M"); - TH1F* hV0MUltraFine = (TH1F*)hV0Mfine->Clone("hV0MUltraFine"); + TH1F* hV0M = dynamic_cast(hV0Mfine->Clone("hV0M")); + TH1F* hV0MUltraFine = dynamic_cast(hV0Mfine->Clone("hV0MUltraFine")); hV0M->SetName("hV0M"); hV0M->SetTitle(""); @@ -210,7 +210,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); // return to proper scope fOutput->cd(); g->SetNpartNcollCorrelation(hNpNc); @@ -291,8 +291,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", gStyle->SetOptStat(0); // Do a ratio plot - TH1D* hGlauber = (TH1D*)hV0MUltraFine->Clone("hGlauber"); - TH1D* hRatio = (TH1D*)hV0MUltraFine->Clone("hRatio"); + TH1D* hGlauber = dynamic_cast(hV0MUltraFine->Clone("hGlauber")); + TH1D* hRatio = dynamic_cast(hV0MUltraFine->Clone("hRatio")); hGlauber->Reset(); c1->cd(1); @@ -345,7 +345,7 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", lFitRangeLine->SetLineStyle(2); lFitRangeLine->Draw(); - TH1D* hRatioGrayed = (TH1D*)hRatio->Clone("hRatioGrayed"); + TH1D* hRatioGrayed = dynamic_cast(hRatio->Clone("hRatioGrayed")); hRatioGrayed->SetMarkerColor(kGray + 2); hRatioGrayed->SetLineColor(kGray + 2); hRatioGrayed->Draw("same"); diff --git a/Common/Tools/Multiplicity/macros/saveCorrelation.C b/Common/Tools/Multiplicity/macros/saveCorrelation.C index 73bf8d30f29..04b27fc6c1b 100644 --- a/Common/Tools/Multiplicity/macros/saveCorrelation.C +++ b/Common/Tools/Multiplicity/macros/saveCorrelation.C @@ -29,23 +29,23 @@ void saveCorrelation(const TString& filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0-rc1-smax99.0.root", const TString& outputFile = "basehistos.root") { TFile* fin = new TFile(filename.Data(), "READ"); - TNtuple* ntup = (TNtuple*)fin->Get("nt_Pb_Pb"); + TNtuple* ntup = dynamic_cast(fin->Get("nt_Pb_Pb")); // try other Pb nuclear profiles in case "Pb" - "Pb" not found if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_Pbpn_Pbpn"); + ntup = dynamic_cast(fin->Get("nt_Pbpn_Pbpn")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar1_PbpnVar1"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar1_PbpnVar1")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar2_PbpnVar2"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar2_PbpnVar2")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar3_PbpnVar3"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar3_PbpnVar3")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar4_PbpnVar4"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar4_PbpnVar4")); } if (!ntup) { diff --git a/Common/Tools/Multiplicity/multCalibrator.cxx b/Common/Tools/Multiplicity/multCalibrator.cxx index 964bb468b72..8d25df8e32e 100644 --- a/Common/Tools/Multiplicity/multCalibrator.cxx +++ b/Common/Tools/Multiplicity/multCalibrator.cxx @@ -108,7 +108,7 @@ Bool_t multCalibrator::Calibrate() // Step 1: verify if input file contains desired histograms TH1D* hRaw[kNCentEstim]; for (Int_t iv = 0; iv < kNCentEstim; iv++) { - hRaw[iv] = reinterpret_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); + hRaw[iv] = dynamic_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); if (!hRaw[iv]) { cout << Form("File does not contain histogram h%s, which is necessary for calibration!", fCentEstimName[iv].Data()) << endl; return kFALSE; diff --git a/Common/Tools/Multiplicity/multMCCalibrator.cxx b/Common/Tools/Multiplicity/multMCCalibrator.cxx index be3bd71596f..9567200ed84 100644 --- a/Common/Tools/Multiplicity/multMCCalibrator.cxx +++ b/Common/Tools/Multiplicity/multMCCalibrator.cxx @@ -91,13 +91,13 @@ Bool_t multMCCalibrator::Calibrate() TProfile* hProfSim[multCalibrator::kNCentEstim]; cout << " * acquiring input profiles..." << endl; for (Int_t iv = 0; iv < multCalibrator::kNCentEstim; iv++) { - hProfData[iv] = reinterpret_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfData[iv] = dynamic_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfData[iv]) { cout << Form("Data file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; } hProfData[iv]->SetName(Form("hProfData_%s", multCalibrator::fCentEstimName[iv].Data())); - hProfSim[iv] = reinterpret_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfSim[iv] = dynamic_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfSim[iv]) { cout << Form("Sim file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; diff --git a/Common/Tools/PID/checkPidPacking.cxx b/Common/Tools/PID/checkPidPacking.cxx index dc497240ef3..1147f27be98 100644 --- a/Common/Tools/PID/checkPidPacking.cxx +++ b/Common/Tools/PID/checkPidPacking.cxx @@ -49,14 +49,14 @@ bool process(std::string outputName, int nevents = 100000) LOG(info) << "Bin width = " << T::bin_width << " vs histo " << hgaus->GetXaxis()->GetBinWidth(1); hgaus->SetLineColor(2); hgaus->SetLineStyle(1); - TH1F* hgausPacked = static_cast(hgaus->Clone("hgausPacked")); + TH1F* hgausPacked = dynamic_cast(hgaus->Clone("hgausPacked")); hgausPacked->SetLineColor(4); hgausPacked->SetLineStyle(2); - TH1F* huniform = static_cast(hgaus->Clone("huniform")); + TH1F* huniform = dynamic_cast(hgaus->Clone("huniform")); huniform->SetLineColor(2); huniform->SetLineStyle(1); - TH1F* huniformPacked = static_cast(hgaus->Clone("huniformPacked")); + TH1F* huniformPacked = dynamic_cast(hgaus->Clone("huniformPacked")); huniformPacked->SetLineColor(4); huniformPacked->SetLineStyle(2); diff --git a/Common/Tools/TrackPropagationModule.h b/Common/Tools/TrackPropagationModule.h index d7f7dd6a75d..2583b2c9bed 100644 --- a/Common/Tools/TrackPropagationModule.h +++ b/Common/Tools/TrackPropagationModule.h @@ -316,7 +316,7 @@ class TrackPropagationModule } } // auto trackParCov = getTrackParCov(track); - o2::aod::track::TrackTypeEnum trackType = (o2::aod::track::TrackTypeEnum)track.trackType(); + o2::aod::track::TrackTypeEnum trackType{track.trackType()}; // std::array trackPxPyPz; // std::array trackPxPyPzTuned = {0.0, 0.0, 0.0}; double q2OverPtNew = -9999.; From 63a31be7b55a25d5ddfab6f8b55a0b97c027e3cd Mon Sep 17 00:00:00 2001 From: Mattia Faggin Date: Tue, 27 Oct 2026 15:10:16 +0100 Subject: [PATCH 459/648] [Common] Track-Tuner calibrations for pp 2026 in autodetect. (#18467) Co-authored-by: Mattia Faggin --- Common/Tools/TrackTuner.h | 95 +++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/Common/Tools/TrackTuner.h b/Common/Tools/TrackTuner.h index 4e1fdcd8bf9..5e59241cffd 100644 --- a/Common/Tools/TrackTuner.h +++ b/Common/Tools/TrackTuner.h @@ -85,10 +85,10 @@ struct TrackTuner : o2::framework::ConfigurableGroup { bool updateCurvatureIU = false; // To update the track parameter Q/Pt in trackIU table, particularly used for V0 mass width dependence on Q/Pt bool updatePulls = false; bool isInputFileFromCCDB = false; // query input file from CCDB or local folder - std::string pathInputFile = ""; // Path to file containing DCAxy, DCAz graphs from data and MC - std::string nameInputFile = ""; // Name of the file containing DCAxy, DCAz graphs from data and MC - std::string pathFileQoverPt = ""; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) - std::string nameFileQoverPt = ""; // file name containing Q/Pt correction graphs from data and MC + std::string pathInputFile{}; // Path to file containing DCAxy, DCAz graphs from data and MC + std::string nameInputFile{}; // Name of the file containing DCAxy, DCAz graphs from data and MC + std::string pathFileQoverPt{}; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) + std::string nameFileQoverPt{}; // file name containing Q/Pt correction graphs from data and MC bool usePvRefitCorrections = false; // establish whether to use corrections obtained with or w/o PV refit float qOverPtMC = -1.; // 1/pt MC float qOverPtData = -1.; // 1/pt data @@ -99,7 +99,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPhiBins = 1; int runNumber = 0; // first run number considered in analysis (useful only if autoDetectDcaCalib = true) bool areGraphsConfigured = false; - std::string outputString = ""; + std::string outputString{}; std::map metadata; @@ -191,6 +191,8 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559348 <= runNumber && runNumber <= 559387 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 7]: pp, 5.36 TeV 2024, period LHC24aq: CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559408 <= runNumber && runNumber <= 559456 +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; LOG(info) << ""; @@ -252,6 +254,14 @@ struct TrackTuner : o2::framework::ConfigurableGroup { pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 5.36 TeV 2024, period LHC24aq: CCDB path " << pathInputFile; LOG(info) << " >>> Run list: 559408 <= runNumber && runNumber <= 559456"; + } else if ((563182 <= runNumber && runNumber <= 567592) || (569945 <= runNumber && runNumber <= 571787)) { + /// + /// [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi + /// Run list: 563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026) + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 13.6 TeV 2025, 2026, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) "; } else { LOG(fatal) << "runNumber " << runNumber << " not supported for the autodetection. Please switch to manual configuration of the TrackTuner object. Aborting..."; } @@ -370,8 +380,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner]"; LOG(info) << "[TrackTuner] >>> String slices:"; - for (const std::string& s : slices) + for (const std::string& s : slices) { LOG(info) << "[TrackTuner] " << s; + } /// check if the number of input parameters is correct if (static_cast(slices.size()) != NPars) { @@ -399,9 +410,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// further lambda expression to handle bool initialization auto setBoolFromString = [=](bool& b, const std::string& str) { - if (!str.compare("1") || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { + if (str == "1" || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { b = true; - } else if (!str.compare("0") || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { + } else if (str == "0" || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { b = false; } else { LOG(fatal) << "[TrackTuner] Wrong bool initialization from configuration "; @@ -412,36 +423,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib setBoolFromString(autoDetectDcaCalib, getValueString(AutoDetectDcaCalib)); - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo setBoolFromString(debugInfo, getValueString(DebugInfo)); LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs setBoolFromString(updateTrackDCAs, getValueString(UpdateTrackDCAs)); LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat setBoolFromString(updateTrackCovMat, getValueString(UpdateTrackCovMat)); LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature setBoolFromString(updateCurvature, getValueString(UpdateCurvature)); LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU setBoolFromString(updateCurvatureIU, getValueString(UpdateCurvatureIU)); LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls setBoolFromString(updatePulls, getValueString(UpdatePulls)); LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB setBoolFromString(isInputFileFromCCDB, getValueString(IsInputFileFromCCDB)); LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathInputFile pathFileQoverPt = getValueString(PathFileQoverPt); outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -456,7 +467,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections setBoolFromString(usePvRefitCorrections, getValueString(UsePvRefitCorrections)); - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = std::stof(getValueString(QOverPtMC)); @@ -469,8 +480,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = std::stoi(getValueString(NPhiBins)); outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -505,36 +517,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib autoDetectDcaCalib = cfgAutoDetectDcaCalib; - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo debugInfo = cfgDebugInfo; LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs updateTrackDCAs = cfgUpdateTrackDCAs; LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat updateTrackCovMat = cfgUpdateTrackCovMat; LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature updateCurvature = cfgUpdateCurvature; LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU updateCurvatureIU = cfgUpdateCurvatureIU; LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls updatePulls = cfgUpdatePulls; LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB isInputFileFromCCDB = cfgIsInputFileFromCCDB; LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathQoverPt pathFileQoverPt = cfgPathFileQoverPt; outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -549,7 +561,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections usePvRefitCorrections = cfgUsePvRefitCorrections; - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = cfgQOverPtMC; @@ -562,8 +574,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = cfgNPhiBins; outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -596,9 +609,6 @@ struct TrackTuner : o2::framework::ConfigurableGroup { TList* ccdb_object_dca = nullptr; TList* ccdb_object_qoverpt = nullptr; - std::string grOneOverPtPionNameMC = "sigmaVsPtMc"; - std::string grOneOverPtPionNameData = "sigmaVsPtData"; - if (isInputFileFromCCDB) { /// use input correction file from CCDB @@ -623,10 +633,13 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// open the input correction file - q/pt correction TFile* inputFileQoverPt = TFile::Open(fullNameFileQoverPt.c_str(), "READ"); - if (!inputFileQoverPt && (updateCurvature || updateCurvatureIU)) { - LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + if (!inputFileQoverPt) { + if (updateCurvature || updateCurvatureIU) { + LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + } + } else { + ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } - ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } getDcaGraphs(ccdb_object_dca, ccdb_object_qoverpt); @@ -648,14 +661,15 @@ struct TrackTuner : o2::framework::ConfigurableGroup { if (usePvRefitCorrections) { dir = "withPvRefit"; } - TList* td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); + auto td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); if (!td) { LOG(fatal) << "[TrackTuner] TList " << td << " not found in ccdb_object_dca. Fix it!"; } int inputNphiBins = nPhiBins; - if (inputNphiBins == 0) + if (inputNphiBins == 0) { nPhiBins = 1; // old phi_independent settings + } // reserve memory and initialize vector for needed number of graphs grDcaXYResVsPtPionMC.resize(nPhiBins); @@ -757,8 +771,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // get phibin double phiMC = mcparticle.phi(); - if (phiMC < 0.) + if (phiMC < 0.) { phiMC += o2::constants::math::TwoPI; // 2 * std::numbers::pi;// + } int phiBin = phiMC / (o2::constants::math::TwoPI + 0.0000001) * nPhiBins; // 0.0000001 just a numerical protection dcaXYResMC = evalGraph(ptMC, grDcaXYResVsPtPionMC[phiBin].get()); @@ -1194,10 +1209,12 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPoints = graph->GetN(); double xMin = graph->GetX()[0]; double xMax = graph->GetX()[nPoints - 1]; - if (x > xMax) + if (x > xMax) { return graph->Eval(xMax); - if (x < xMin) + } + if (x < xMin) { return graph->Eval(xMin); + } return graph->Eval(x); } }; From 4aeede39ce1d5dd97daaf6bcec8a9f4783b6a7cb Mon Sep 17 00:00:00 2001 From: m-rasa <99010072+m-rasa@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:26:17 +0100 Subject: [PATCH 460/648] [PWGUD] Fix event selection cuts (#18478) --- PWGUD/Tasks/sgDeuteronSpectra.cxx | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/PWGUD/Tasks/sgDeuteronSpectra.cxx b/PWGUD/Tasks/sgDeuteronSpectra.cxx index 64f10e8d23a..109afa51a79 100644 --- a/PWGUD/Tasks/sgDeuteronSpectra.cxx +++ b/PWGUD/Tasks/sgDeuteronSpectra.cxx @@ -55,6 +55,8 @@ struct SgDeuteronSpectra { Configurable nsigmatpcPreselCut{"nsigmatpcPreselCut", 3.0, "nsigma TPC preselection cut for TOF analysis"}; Configurable occupancyCut{"occupancyCut", 1000, "Occupancy cut"}; Configurable vertexZCut{"vertexZCut", 10, "VertexZ cut"}; + Configurable maxTracks{"maxTracks", 100, "Maximum number of tracks in the event"}; + Configurable useVtxFT0Cut{"useVtxFT0Cut", true, "Use zVtxFT0vPV cut in event selection"}; // Configurable axis for histograms ConfigurableAxis ptAxis{"ptAxis", {200, 0.0, 10.0}, "p_{T}"}; @@ -70,6 +72,7 @@ struct SgDeuteronSpectra { const AxisSpec axistof{nSigmaAxis, "n#sigma_{TOF}"}; // Collision histograms + registry.add("collisions/selectionCuts", "", {HistType::kTH1F, {{8, -0.5, 7.5}}}); registry.add("collisions/VertexZ", "", {HistType::kTH1F, {{800, -20, 20}}}); registry.add("collisions/GapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{3, -0.5, 2.5}}}); registry.add("collisions/TrueGapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{4, -1.5, 2.5}}}); @@ -101,13 +104,44 @@ struct SgDeuteronSpectra { void process(UDCollisionFull const& coll, UDTracksFull const& tracks) { registry.fill(HIST("collisions/VertexZ"), coll.posZ()); + registry.fill(HIST("collisions/selectionCuts"), 0); // applying event cuts + if (!coll.tfb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 1); + if (!coll.itsROFb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 2); + if (!coll.sbp()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 3); + if (useVtxFT0Cut) { + if (!coll.zVtxFT0vPV()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 4); + } else { + registry.fill(HIST("collisions/selectionCuts"), 4); + } + if (!coll.vtxITSTPC()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 5); if (std::abs(coll.posZ()) > vertexZCut) { return; } + registry.fill(HIST("collisions/selectionCuts"), 6); if (coll.occupancyInTime() > occupancyCut) { return; } + registry.fill(HIST("collisions/selectionCuts"), 7); + if (tracks.size() > maxTracks) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 8); // fill gapside registry.fill(HIST("collisions/GapSide"), coll.gapSide(), 1.); std::vector fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; From b854d166ee64f946ea088258d305de59147d4cae Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:46:30 +0100 Subject: [PATCH 461/648] [PWGLF] Add PVQualityStudy in hStrangeCorrelation.cxx (#18480) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 265 +++++++++++++++++- 1 file changed, 262 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 21e750cb2b9..4be547e19af 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -337,12 +337,22 @@ struct HStrangeCorrelation { // reconstruction efficiency, Nch^gen vs Nch^reco and // Nch^reco vs FT0M (percentile and amplitude) responses, // see runEventRecoEfficiencyStudy + // doPVQualityStudy PairLossK0/PVQuality: best-collision PV position and + // reco - MC residuals vs multNTracksPVeta1, and the + // MC-collision reconstruction and selection stages vs + // Nch^gen, split by the truth h-K0 content of the event, + // see runPVQualityStudy Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; Configurable recComparisonBestCollisionOnly{"recComparisonBestCollisionOnly", false, "part 2: run the Rec path of PairLossK0/Comparison only in the best reconstructed collision (most PV contributors) of each MC collision, the one the Gen, Truth and Final stages are selected on. Set false to run it in every reconstructed collision, as the data path does"}; Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude"}; Configurable nchGenResponseBestCollisionOnly{"nchGenResponseBestCollisionOnly", false, "fill PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1 with the best reconstructed collision (most PV contributors) of each MC collision only, instead of with every reconstructed collision; the multNTracksPVeta1 vs FT0M responses stay per reconstructed collision"}; + Configurable doPVQualityStudy{"doPVQualityStudy", false, "fill PairLossK0/PVQuality: best-collision PV x, y and reco - MC residuals vs multNTracksPVeta1, and the MC-collision reconstruction and selection stages vs Nch^gen, split by the truth h-K0 content of the event"}; + // Coarse on purpose: the PV histograms are dense THnF with a fine position axis + ConfigurableAxis axisPVQualityNTracks{"axisPVQualityNTracks", {VARIABLE_WIDTH, -0.5f, 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 8.5f, 10.5f, 12.5f, 15.5f, 20.5f, 25.5f, 30.5f, 40.5f, 50.5f, 70.5f, 100.5f, 150.5f}, "multNTracksPVeta1 classes of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVPosition{"axisPVPosition", {400, -0.2f, 0.2f}, "reconstructed PV x and y (cm) of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVResidual{"axisPVResidual", {200, -0.1f, 0.1f}, "reconstructed minus generated PV x, y and z (cm) of PairLossK0/PVQuality"}; Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; // MC-only event quality cut on the Nch^gen vs multNTracksPVeta1 response @@ -670,6 +680,45 @@ struct HStrangeCorrelation { "pT truth outside trigger axis", "Truth charge"}; + // Truth content of an MC collision for PairLossK0/PVQuality. Trigger and K0 follow + // the acceptance, primary and leading-trigger requirements of PairLossK0/Comparison/Truth, + // and a pair excludes a trigger that is one of the K0 daughters. An MC collision enters + // every class it belongs to, so the classes overlap and must not be summed: + // HasTrigger, HasPair and K0DaughterPVContributor are subsets of All, NearPair and + // AwayPair subsets of HasPair, and one MC collision can be both NearPair and AwayPair. + // K0DaughterPVContributor needs the best reconstructed collision, so it is empty at the + // first stage of hEventStage. + enum PairLossPVQualityClass : int { + PairLossPVQualityAll = 0, + PairLossPVQualityHasTrigger, + PairLossPVQualityHasPair, + PairLossPVQualityNearPair, + PairLossPVQualityAwayPair, + PairLossPVQualityK0DaughterPVContributor, + PairLossPVQualityNClasses + }; + + static constexpr std::array PairLossPVQualityClassNames = { + "all", + "truth trigger", + "truth h-K^{0}_{S} pair", + "near-side pair (|#Delta#varphi| < #pi/3)", + "away-side pair (|#Delta#varphi - #pi| < #pi/3)", + "K^{0}_{S} daughter is a PV contributor"}; + + // MC-collision stages of PairLossK0/PVQuality/hEventStage, each a subset of the previous one + enum PairLossPVQualityStage : int { + PairLossPVQualityStageAll = 0, + PairLossPVQualityStageHasRecoColl, + PairLossPVQualityStageSelected, + PairLossPVQualityNStages + }; + + static constexpr std::array PairLossPVQualityStageNames = { + "MC collision", + "has reconstructed collision", + "best collision selected"}; + struct PairLossV0Info { int64_t globalIndex = -1; int64_t positiveTrackId = -1; @@ -2960,6 +3009,52 @@ struct HStrangeCorrelation { } } + if (doprocessPairLossK0MC && pairLossK0Configurations.doPVQualityStudy) { + // One entry per MC collision with |vz_gen| <= zVertexCut, in every truth-content + // class it belongs to (PairLossPVQualityClass): the classes overlap. + // hEventStage: stages nested as in PairLossK0/EventRecoEfficiency, so + // HasRecoColl/All and Selected/HasRecoColl per class give whether events with a + // trigger and a K0 lose their PV, or have it rejected, more often. + // hPos*: the data-like check -- the beam spot is common to all collisions, so a + // broadening or tail of the best-collision x and y towards low multNTracksPVeta1 + // is a reconstruction effect. hRes*: the same with the generated vertex + // subtracted. Selection bin 0 holds every best collision, bin 1 only those passing + // the event selection of PairLossK0/Comparison, so bin 1 is a subset of bin 0. + const AxisSpec axisPVQualityClass{PairLossPVQualityNClasses, -0.5, static_cast(PairLossPVQualityNClasses) - 0.5, "truth content of the MC collision"}; + const AxisSpec axisPVQualityStage{PairLossPVQualityNStages, -0.5, static_cast(PairLossPVQualityNStages) - 0.5, "MC-collision stage"}; + const AxisSpec axisPVQualitySelection{2, -0.5, 1.5, "best collision"}; + const AxisSpec axisPVQualityNchGen{axesConfigurations.axisNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + const AxisSpec axisPVQualityNTracksPVeta1{pairLossK0Configurations.axisPVQualityNTracks, "multNTracksPVeta1"}; + const AxisSpec axisPVQualityPosX{pairLossK0Configurations.axisPVPosition, "#it{x}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityPosY{pairLossK0Configurations.axisPVPosition, "#it{y}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityResX{pairLossK0Configurations.axisPVResidual, "#it{x}_{PV}^{reco} #minus #it{x}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResY{pairLossK0Configurations.axisPVResidual, "#it{y}_{PV}^{reco} #minus #it{y}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResZ{pairLossK0Configurations.axisPVResidual, "#it{z}_{PV}^{reco} #minus #it{z}_{PV}^{gen} (cm)"}; + histos.add("PairLossK0/PVQuality/hEventStage", "MC collisions by truth content and stage", kTH3D, {axisPVQualityClass, axisPVQualityStage, axisPVQualityNchGen}); + histos.add("PairLossK0/PVQuality/hPosX", "best-collision PV x", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosX}); + histos.add("PairLossK0/PVQuality/hPosY", "best-collision PV y", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosY}); + histos.add("PairLossK0/PVQuality/hResX", "best-collision PV x residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResX}); + histos.add("PairLossK0/PVQuality/hResY", "best-collision PV y residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResY}); + histos.add("PairLossK0/PVQuality/hResZ", "best-collision PV z residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResZ}); + + auto eventStage = histos.get(HIST("PairLossK0/PVQuality/hEventStage")); + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + eventStage->GetXaxis()->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + for (int i = 0; i < PairLossPVQualityNStages; ++i) { + eventStage->GetYaxis()->SetBinLabel(i + 1, PairLossPVQualityStageNames[i].data()); + } + for (auto const& pvHistogram : {histos.get(HIST("PairLossK0/PVQuality/hPosX")), histos.get(HIST("PairLossK0/PVQuality/hPosY")), + histos.get(HIST("PairLossK0/PVQuality/hResX")), histos.get(HIST("PairLossK0/PVQuality/hResY")), + histos.get(HIST("PairLossK0/PVQuality/hResZ"))}) { + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + pvHistogram->GetAxis(0)->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + pvHistogram->GetAxis(1)->SetBinLabel(1, "all"); + pvHistogram->GetAxis(1)->SetBinLabel(2, "event selection"); + } + } + if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse) { // Same histograms and event selection as the multNTracksPVeta1 vs FT0M responses of // PairLossK0/EventRecoEfficiency, filled per reconstructed collision in data @@ -4822,11 +4917,11 @@ struct HStrangeCorrelation { TracksComplete const& tracks) { // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the - // event-reconstruction-efficiency study, the generated-level INEL selection and - // the Nch^gen vs multNTracksPVeta1 band cut + // event-reconstruction-efficiency and PV-quality studies, the generated-level INEL + // selection and the Nch^gen vs multNTracksPVeta1 band cut const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel; const bool applyBandCut = pairLossK0Configurations.applyNchResponseBandCut; - const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel || applyBandCut) ? countNchGenEta1(mcParticles) : -1; + const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || pairLossK0Configurations.doPVQualityStudy || applyGenInel || applyBandCut) ? countNchGenEta1(mcParticles) : -1; // Part 3: a self-contained generator-level study. It deliberately touches no // reconstructed quantity in its event selection or in any of its axes: the @@ -5556,9 +5651,173 @@ struct HStrangeCorrelation { } }; + // PV quality vs midrapidity multiplicity, split by the truth h-K0 content of the MC + // collision (PairLossPVQualityClass). Tests whether PVs built from few tracks are + // biased or badly defined, and whether a trigger and a K0 in the event -- whose + // displaced daughters can enter the vertex fit -- make it worse: in the PV position + // and residuals of the best collision, and in how often that collision is missing + // or fails the event selection of PairLossK0/Comparison + auto runPVQualityStudy = [&]() { + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + + // Truth triggers and K0s as in PairLossK0/Comparison/Truth: same acceptance, + // primary requirements applied before the leading-trigger choice + struct PVQualityTrigger { + int64_t globalIndex = -1; + float pt = 0.0f; + float phi = 0.0f; + }; + struct PVQualityK0 { + float phi = 0.0f; + int64_t positiveDaughterId = -1; + int64_t negativeDaughterId = -1; + }; + std::vector pvTriggers; + std::vector pvK0s; + for (auto const& mcParticle : mcParticles) { + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + pvTriggers.push_back(PVQualityTrigger{mcParticle.globalIndex(), mcParticle.pt(), mcParticle.phi()}); + } + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || + (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + continue; + } + PVQualityK0 pvK0{mcParticle.phi(), -1, -1}; + for (auto const& daughter : mcParticle.daughters_as()) { + if (daughter.pdgCode() == PDG_t::kPiPlus) { + pvK0.positiveDaughterId = daughter.globalIndex(); + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + pvK0.negativeDaughterId = daughter.globalIndex(); + } + } + pvK0s.push_back(pvK0); + } + if (useTheLeadingParticleAsTrigger && pvTriggers.size() > 1) { + const auto leadingTrigger = *std::max_element(pvTriggers.begin(), pvTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + pvTriggers.assign(1, leadingTrigger); + } + + constexpr double PVQualitySideHalfWidth = PI / 3.0; + std::array inClass{}; + inClass[PairLossPVQualityAll] = true; + inClass[PairLossPVQualityHasTrigger] = !pvTriggers.empty(); + for (auto const& trigger : pvTriggers) { + for (auto const& k0 : pvK0s) { + if (trigger.globalIndex == k0.positiveDaughterId || trigger.globalIndex == k0.negativeDaughterId) { + continue; + } + const double deltaPhi = computeDeltaPhi(trigger.phi, k0.phi); + inClass[PairLossPVQualityHasPair] = true; + if (std::abs(deltaPhi) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityNearPair] = true; + } + if (std::abs(deltaPhi - PI) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityAwayPair] = true; + } + } + } + + auto fillEventStage = [&](int stage) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (inClass[eventClass]) { + histos.fill(HIST("PairLossK0/PVQuality/hEventStage"), eventClass, stage, nChGen); + } + } + }; + fillEventStage(PairLossPVQualityStageAll); + if (recCollisions.size() == 0) { + return; + } + + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + int64_t bestCollisionId = -1; + float bestCollisionVtxX = 0.0f; + float bestCollisionVtxY = 0.0f; + float bestCollisionVtxZ = 0.0f; + int bestCollisionNTracksPVeta1 = -1; + bool bestCollisionSelected = false; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionId = collision.globalIndex(); + bestCollisionVtxX = collision.posX(); + bestCollisionVtxY = collision.posY(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + // same event selection as pairLossEventSelected in runRecComparison + bestCollisionSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); + } + if (bestCollisionId < 0) { + return; + } + + // Direct check of the K0 side of the hypothesis: a daughter of a truth K0 above + // was used in the vertex fit of the best collision + std::unordered_set pvK0DaughterIds; + for (auto const& k0 : pvK0s) { + for (const int64_t daughterId : {k0.positiveDaughterId, k0.negativeDaughterId}) { + if (daughterId >= 0) { + pvK0DaughterIds.insert(daughterId); + } + } + } + if (!pvK0DaughterIds.empty()) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); + for (auto const& track : trackSlice) { + if (!track.isPVContributor()) { + continue; + } + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (trackLabel.has_mcParticle() && pvK0DaughterIds.contains(trackLabel.mcParticleId())) { + inClass[PairLossPVQualityK0DaughterPVContributor] = true; + break; + } + } + } + + fillEventStage(PairLossPVQualityStageHasRecoColl); + if (bestCollisionSelected) { + fillEventStage(PairLossPVQualityStageSelected); + } + + const float residualX = bestCollisionVtxX - mcCollision.posX(); + const float residualY = bestCollisionVtxY - mcCollision.posY(); + const float residualZ = bestCollisionVtxZ - mcCollision.posZ(); + auto fillBestCollision = [&](int selectionBin) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (!inClass[eventClass]) { + continue; + } + histos.fill(HIST("PairLossK0/PVQuality/hPosX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxX); + histos.fill(HIST("PairLossK0/PVQuality/hPosY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxY); + histos.fill(HIST("PairLossK0/PVQuality/hResX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualX); + histos.fill(HIST("PairLossK0/PVQuality/hResY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualY); + histos.fill(HIST("PairLossK0/PVQuality/hResZ"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualZ); + } + }; + fillBestCollision(0); + if (bestCollisionSelected) { + fillBestCollision(1); + } + }; + if (pairLossK0Configurations.doEventRecoEfficiencyStudy) { runEventRecoEfficiencyStudy(); } + if (pairLossK0Configurations.doPVQualityStudy) { + runPVQualityStudy(); + } if (pairLossK0Configurations.doGenLevelStudy) { runGenLevelStudy(); } From 9b9a7abbbd9aec8d94cb5c81d2cdf95914cf5497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:11:36 +0100 Subject: [PATCH 462/648] [PWGHF] Remove HfHelper instances (#18436) --- PWGHF/Core/HfHelper.h | 2 ++ .../correlatorDplusDplusReduced.cxx | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/PWGHF/Core/HfHelper.h b/PWGHF/Core/HfHelper.h index e6299b483f8..155dfda35f2 100644 --- a/PWGHF/Core/HfHelper.h +++ b/PWGHF/Core/HfHelper.h @@ -39,6 +39,8 @@ concept IsB0ToDstarPiChannel = requires(T candidate) { }; struct HfHelper { + HfHelper() = delete; // Prevent instances. + // 2-prong // D0(bar) → π± K∓ diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx index 096e5420005..d3530cfff96 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx @@ -104,8 +104,6 @@ struct HfCorrelatorDplusDplusReduced { std::vector outputMl; o2::ccdb::CcdbApi ccdbApi; - HfHelper hfHelper; - Service ccdb; using SelectedCandidates = soa::Filtered>; @@ -158,8 +156,8 @@ struct HfCorrelatorDplusDplusReduced { } for (size_t i = 0; i < cutPtSkimming.value.size() - 1; i++) { if (candidate.pt() <= cutPtSkimming.value.at(i + 1)) { - if (hfHelper.invMassDplusToPiKPi(candidate) < massMin.value.at(i) || - hfHelper.invMassDplusToPiKPi(candidate) > massMax.value.at(i) || + if (HfHelper::invMassDplusToPiKPi(candidate) < massMin.value.at(i) || + HfHelper::invMassDplusToPiKPi(candidate) > massMax.value.at(i) || candidate.cpa() < cosThetaMin.value.at(i) || candidate.decayLength() < decayLengthMin.value.at(i) || std::abs(candidate.nSigTofKa1()) > nSigmaTofMax.value.at(i) || @@ -214,7 +212,7 @@ struct HfCorrelatorDplusDplusReduced { if (fillCandidateTinyTable) { rowCandidateTiny( candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.eta(), candidate.phi(), @@ -257,14 +255,14 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), + HfHelper::yDplus(candidate), cent, localEvIdx, flagMc, @@ -332,17 +330,17 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.p(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), - hfHelper.ctDplus(candidate), + HfHelper::ctDplus(candidate), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), - hfHelper.eDplus(candidate), + HfHelper::yDplus(candidate), + HfHelper::eDplus(candidate), cent, localEvIdx, flagMc, From 902770e3a9305aa83cad78b186c25b7c823563fa Mon Sep 17 00:00:00 2001 From: Zhiyong Lu <71517277+Luzhiyongg@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:11:54 +0800 Subject: [PATCH 463/648] [PWGCF] validation for new version flowPtContainer (#18487) Co-authored-by: ALICE Action Bot --- PWGCF/Flow/Tasks/flowTask.cxx | 120 ++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 27 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index ec583bdabd8..b9f78ad50ec 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -101,6 +101,7 @@ struct FlowTask { Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble[0], 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; O2_DEFINE_CONFIGURABLE(cfgEtaGapPtPtEnabled, bool, false, "switch of subevent pt-pt correlations") Configurable> cfgTrackCuts{"cfgTrackCuts", {TrackCutArray[0], 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAz", "DCAxy Nsigma", "DCAz Nsigma(override)"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + std::array, 7> trackCuts{}; enum TrackCut { // enum for labelledArray track selection kChi2prTPCcls = 0, // max chi2 per TPC clusters @@ -244,6 +245,13 @@ struct FlowTask { std::vector> listNptV2; std::vector> listPtX; O2_DEFINE_CONFIGURABLE(cfgManualCk, bool, false, "Produce pt-pt variance") + O2_DEFINE_CONFIGURABLE(cfgNewPtContainer, bool, false, "Switch to new version of flowPtContainer. This switch will be removed after validation of new container.") + Configurable cfgNpcRegions{"cfgNpcRegions", {{"refN", "refP", "refMid", "refFull"}, {-0.8, 0.4, -0.4, -0.8}, {-0.4, 0.8, 0.4, 0.8}, {0, 0, 0, 0}, {1, 1, 1, 1}}, "Configurations for GFW regions"}; + Configurable cfgNpcPtVnCorrConfig{"cfgNpcPtVnCorrConfig", {{"refP {pt} refN {pt}", "refP {2} refMid {pt} refN {-2}", "refP {2} refMid {pt pt} refN {-2}", "refFull {pt}", "refFull {pt pt}", "refFull {pt pt pt}", "refFull {pt pt pt pt}", "refP {pt} refMid {pt} refN {pt}"}, {"ChGapPtPt", "ChGap22pt", "ChGap22ptpt", "ChFullPt", "ChFullPtPt", "ChFullPtPtPt", "ChFullPtPtPtPt", "ChGapPtPtPtABC"}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for vn-pt correlations based on new version flowPtContainer"}; + Configurable> cfgNpcGFWCorr{"cfgNpcGFWCorr", std::vector{"refN {2} refP {-2}", "refN {3} refP {-3}"}, "GFW corrconfig for new version flowPtContainer"}; + GFWRegions npcRegions; + GFWCorrConfigs npcPtVnCorrConfig; + GFW npcGfw; } cfgAdditionObs; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "pt axis for histograms"}; @@ -667,14 +675,42 @@ struct FlowTask { // Mask 1: vn-[pT], 3: vn-[pT^2], 7: vn-[pT^3], 15: vn-[pT^4] gfwConfigs.SetpTCorrMasks(cfgUserPtVnCorrConfig->GetpTCorrMasks()); gfwConfigs.Print(); - fFCpt->setUseCentralMoments(cfgUseCentralMoments); - fFCpt->setUseGapMethod(true); if (!cfgUserIO.cfgUserPtVnEvWeightEnabled) fFCpt->setEventWeight(EventWeight::UnityWeight); else fFCpt->setEventWeight(EventWeight::TupleWeight); - fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); - if (cfgEtaGapPtPtEnabled) { + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcRegions.SetNames(cfgAdditionObs.cfgNpcRegions->GetNames()); + cfgAdditionObs.npcRegions.SetEtaMin(cfgAdditionObs.cfgNpcRegions->GetEtaMin()); + cfgAdditionObs.npcRegions.SetEtaMax(cfgAdditionObs.cfgNpcRegions->GetEtaMax()); + cfgAdditionObs.npcRegions.SetpTDifs(cfgAdditionObs.cfgNpcRegions->GetpTDifs()); + cfgAdditionObs.npcRegions.SetBitmasks(cfgAdditionObs.cfgNpcRegions->GetBitmasks()); + cfgAdditionObs.npcPtVnCorrConfig.SetCorrs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetCorrs()); + cfgAdditionObs.npcPtVnCorrConfig.SetHeads(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetHeads()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTDifs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTDifs()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTCorrMasks(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTCorrMasks()); + cfgAdditionObs.npcRegions.Print(); + cfgAdditionObs.npcPtVnCorrConfig.Print(); + fFCpt->initialise(axisIndependent, cfgAdditionObs.npcRegions, cfgAdditionObs.npcPtVnCorrConfig, cfgNbootstrap); + + for (int region = 0; region < cfgAdditionObs.npcRegions.GetSize(); ++region) { + cfgAdditionObs.npcGfw.AddRegion(cfgAdditionObs.npcRegions.GetNames()[region], cfgAdditionObs.npcRegions.GetEtaMin()[region], + cfgAdditionObs.npcRegions.GetEtaMax()[region], 1, + cfgAdditionObs.npcRegions.GetBitmasks()[region]); + } + std::vector npcGFWCorr = cfgAdditionObs.cfgNpcGFWCorr; + if (!npcGFWCorr.empty()) { + for (uint i = 0; i < npcGFWCorr.size(); i++) { + cfgAdditionObs.npcGfw.GetCorrelatorConfig(npcGFWCorr.at(i).c_str()); + } + } + cfgAdditionObs.npcGfw.CreateRegions(); + } else { + fFCpt->setUseCentralMoments(cfgUseCentralMoments); + fFCpt->setUseGapMethod(true); + fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); + } + if (!cfgAdditionObs.cfgNewPtContainer && cfgEtaGapPtPtEnabled) { for (int i = 0; i < 4; ++i) { // o2-linter: disable=magic-number (maximum of 4 subevents) if (cfgPtPtGaps->getData()[i][0] < -1. || cfgPtPtGaps->getData()[i][1] < -1.) continue; @@ -756,31 +792,44 @@ struct FlowTask { funcV4->SetParameters(0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]) { + readMatrix(cfgTrackCuts->getData(), trackCuts); + if (trackCuts[kDCAxyNSigma][kTrCutObs]) { cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]); - LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutObs]); + LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]) { + if (trackCuts[kDCAxyNSigma][kTrCutNch]) { cfgFuncParas.fPtDepDCAxyForNch = new TF1("ptDepDCAxyForNch", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]); - LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutNch]); + LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs]) { + if (trackCuts[kDCAzNSigma][kTrCutObs]) { cfgFuncParas.fPtDepDCAz = new TF1("ptDepDCAz", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAz->SetParameter(0, cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs]); - LOGF(info, "DCAz pt-dependence function: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs], cfgFuncParas.cfgDCAzFunc->c_str())); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutObs]); + LOGF(info, "DCAz pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutObs], cfgFuncParas.cfgDCAzFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch]) { + if (trackCuts[kDCAzNSigma][kTrCutNch]) { cfgFuncParas.fPtDepDCAzForNch = new TF1("ptDepDCAzForNch", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch]); - LOGF(info, "DCAz pt-dependence function for Nch: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch], cfgFuncParas.cfgDCAzFunc->c_str())); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutNch]); + LOGF(info, "DCAz pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutNch], cfgFuncParas.cfgDCAzFunc->c_str())); } if (!cfgEvSelRCTflags.value.empty()) { rctChecker.init(cfgEvSelRCTflags.value.c_str()); // override initialzation } } + template + void readMatrix(Array2D const& mat, P& array) + { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -1119,21 +1168,21 @@ struct FlowTask { template bool trackSelected(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAxy != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) return false; - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutObs] && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt()))) + if (trackCuts[kDCAzNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAz != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutObs])); + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutObs])); } template bool trackSelectedForNch(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAxyForNch != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) return false; - if (cfgTrackCuts->getData()[kDCAzNSigma][kTrCutNch] && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt()))) + if (trackCuts[kDCAzNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAzForNch != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutNch])); + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutNch])); } template @@ -1264,6 +1313,9 @@ struct FlowTask { fGFW->Clear(); fFCpt->clearVector(); cfgAdditionObs.nPt.clear(); + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcGfw.Clear(); + } if (cfgGetInteractionRate) { initHadronicRate(bc); double hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), mRunNumber, "ZNC hadronic") * 1.e-3; // @@ -1443,10 +1495,20 @@ struct FlowTask { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); if (withinPtPOI && withinPtRef) fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); - if (cfgUsePtRef && withinPtRef) - fillPtSums(track, weff); - if (!cfgUsePtRef && withinPtPOI) - fillPtSums(track, weff); + if (cfgAdditionObs.cfgNewPtContainer) { + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 1); + if (withinPtPOI) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); + if (withinPtPOI && withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); + } else { + if (cfgUsePtRef && withinPtRef) + fillPtSums(track, weff); + if (!cfgUsePtRef && withinPtPOI) + fillPtSums(track, weff); + } } registry.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); if (cfgUseNch && cfgUseNchCorrected) { @@ -1548,7 +1610,11 @@ struct FlowTask { fillFC(corrconfigs.at(l_ind), independent, lRandom); } // Filling pt Container - fillPtContainers(independent, lRandom); + if (cfgAdditionObs.cfgNewPtContainer) { + fFCpt->fillPtProfiles(cfgAdditionObs.npcGfw, independent, lRandom); + } else { + fillPtContainers(independent, lRandom); + } } PROCESS_SWITCH(FlowTask, processData, "Process analysis for non-derived data", true); From 349206aa8284fb1bf946b3fd87676e9aaed10b3b Mon Sep 17 00:00:00 2001 From: somabhatta <48771326+somabhatta@users.noreply.github.com> Date: Wed, 28 Oct 2026 10:49:16 +0100 Subject: [PATCH 464/648] [PWGCF] radialFlowDecorr: no bin labels on profiles and TH3, so merged outputs keep their binning (#18483) --- .../Tasks/radialFlowDecorr.cxx | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index de6ea4b8ca7..45f4d3b2aa8 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -1549,13 +1549,9 @@ struct RadialFlowDecorr { // =========================================================================== // Histogram declarations // =========================================================================== - void labelFwdAxis(TAxis* ax) const - { - for (int k = 0; k < nFwd && k < ax->GetNbins(); ++k) { - ax->SetBinLabel(k + 1, fwdLabels[k].c_str()); - } - } - + // Bin labels only on TH1/TH2 and THnSparse axes: ROOT merges labelled axes by label, which for + // profiles and TH3 extends the axis and sums all bins into a new one, or drops the entries of + // bins with zero content. Their bins are named in the axis title instead. static void labelAxis(TAxis* ax, const std::vector& labels) { for (std::size_t i = 0; i < labels.size() && static_cast(i) < ax->GetNbins(); ++i) { @@ -1694,10 +1690,8 @@ struct RadialFlowDecorr { if (hists.fwdChannel) { return; } - hists.fwdChannel = histos.add("FwdQA/Prof_Channel_qa", ";channel (FT0A 0-95, FT0C 96-207, FV0A 208-255);quantity", kTProfile2D, {{KNChFT0 + KNChFV0, -0.5, KNChFT0 + KNChFV0 - 0.5}, {kNChQA, -0.5, static_cast(kNChQA) - 0.5}}); - labelAxis(hists.fwdChannel->GetYaxis(), {"amplitude", "gain-equalised amplitude", "#eta (aligned, vs z_{vtx})"}); + hists.fwdChannel = histos.add("FwdQA/Prof_Channel_qa", ";channel (FT0A 0-95, FT0C 96-207, FV0A 208-255);quantity (0 amplitude, 1 gain-equalised amplitude, 2 #eta aligned vs z_{vtx})", kTProfile2D, {{KNChFT0 + KNChFV0, -0.5, KNChFT0 + KNChFV0 - 0.5}, {kNChQA, -0.5, static_cast(kNChQA) - 0.5}}); hists.fwdEta = histos.add("FwdQA/Prof_Eta_fwdbin", ";forward bin;amplitude-weighted #LT#eta#GT", kTProfile, {fwdAxis}); - labelFwdAxis(hists.fwdEta->GetXaxis()); } void declareEff(uint32_t mask) @@ -1720,8 +1714,7 @@ struct RadialFlowDecorr { histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); - hists.mcTrackOrigin = histos.add("MCQA/Hist3D_Nch_pT_origin", ";N_{ch};p_{T} (GeV/c);origin", kTH3F, {{nchAxisObs}, {KNbinsPtRes, KPtMin, KPtMax}, {kNOrigin, -0.5, static_cast(kNOrigin) - 0.5}}); - labelAxis(hists.mcTrackOrigin->GetZaxis(), {"primary", "secondary", "other MC collision", "no label"}); + hists.mcTrackOrigin = histos.add("MCQA/Hist3D_Nch_pT_origin", ";N_{ch};p_{T} (GeV/c);origin (0 primary, 1 secondary, 2 other MC collision, 3 no label)", kTH3F, {{nchAxisObs}, {KNbinsPtRes, KPtMin, KPtMax}, {kNOrigin, -0.5, static_cast(kNOrigin) - 0.5}}); } // vertex QA of all MC passes @@ -1761,11 +1754,9 @@ struct RadialFlowDecorr { for (int l = 0; l < nLev; ++l) { const std::string p = systPrefix(v) + (isMC ? mcDir[l] : std::string{}); auto& h = hists.mean[v][l]; - h.mean = r.add((p + "Prof_Mean_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable", kTProfile3D, {{nchAxisOf(v)}, {etaBinAxis}, {kNMeanObs, -0.5, static_cast(kNMeanObs) - 0.5}}); - labelAxis(h.mean->GetZaxis(), {"#LT[p_{T}]#GT", "#LT N#GT"}); + h.mean = r.add((p + "Prof_Mean_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 #LT[p_{T}]#GT, 1 #LT N#GT)", kTProfile3D, {{nchAxisOf(v)}, {etaBinAxis}, {kNMeanObs, -0.5, static_cast(kNMeanObs) - 0.5}}); if (cfgFwdOpt.cfgFwd) { h.fwdMean = r.add((p + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT a#GT", kTProfile2D, {{nchAxisOf(v)}, {fwdAxis}}); - labelFwdAxis(h.fwdMean->GetYaxis()); } } } @@ -1777,8 +1768,7 @@ struct RadialFlowDecorr { { FlucHists h; auto n = [&](const char* name) { return p + name + s; }; - h.obs = r.add(n("Prof_Obs_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable", kTProfile3D, {{ax}, {etaBinAxis}, {kNFlucObs, -0.5, static_cast(kNFlucObs) - 0.5}}); - labelAxis(h.obs->GetZaxis(), {"N", "[p_{T}]", "c_{2}", "c_{3}", "C_{2}^{sub} (mirror)", "C_{3}^{sub} (mirror)"}); + h.obs = r.add(n("Prof_Obs_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 N, 1 [p_{T}], 2 c_{2}, 3 c_{3}, 4 C_{2}^{sub} mirror, 5 C_{3}^{sub} mirror)", kTProfile3D, {{ax}, {etaBinAxis}, {kNFlucObs, -0.5, static_cast(kNFlucObs) - 0.5}}); h.c2Sub2D = r.add(n("Prof_C2Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A};#eta_{C}", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); h.c3Sub2D = r.add(n("Prof_C3Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); if (withGapSum) { @@ -1789,9 +1779,6 @@ struct RadialFlowDecorr { h.fwdVar = r.add(n("Prof_FwdAmpVar_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT #delta a^{2}#GT", kTProfile2D, {{ax}, {fwdAxis}}); h.covFwdPt = r.add(n("Prof_CovFwdAmpMeanpT_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta[p_{T}] #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); h.covFwdN = r.add(n("Prof_CovFwdAmpNch_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta N #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); - labelFwdAxis(h.fwdVar->GetYaxis()); - labelFwdAxis(h.covFwdPt->GetZaxis()); - labelFwdAxis(h.covFwdN->GetZaxis()); } return h; } @@ -1842,8 +1829,7 @@ struct RadialFlowDecorr { declareFwdQA(); } declareMean(maskAll, true); - histos.add("Prof_Weights_pT_type", ";p_{T};weight", kTProfile2D, {{KNbinsPtRes, KPtMin, KPtMax}, {3, -0.5, 2.5}}); - labelAxis(histos.get(HIST("Prof_Weights_pT_type"))->GetYaxis(), {"eff", "fake", "w"}); + histos.add("Prof_Weights_pT_type", ";p_{T};weight (0 eff, 1 fake, 2 w)", kTProfile2D, {{KNbinsPtRes, KPtMin, KPtMax}, {3, -0.5, 2.5}}); } void declareMCFlucHists() @@ -1870,8 +1856,7 @@ struct RadialFlowDecorr { declareFwdQA(); } declareMean(maskAll, false); - histos.add("Prof_Weights_pt_eta_type", ";p_{T} (GeV/c);#eta;weight", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {3, -0.5, 2.5}}); - labelAxis(histos.get(HIST("Prof_Weights_pt_eta_type"))->GetZaxis(), {"eff", "fake", "w_{#phi}"}); + histos.add("Prof_Weights_pt_eta_type", ";p_{T} (GeV/c);#eta;weight (0 eff, 1 fake, 2 w_{#phi})", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {3, -0.5, 2.5}}); } void declareDataFlucHists() @@ -2188,6 +2173,11 @@ struct RadialFlowDecorr { LOGF(fatal, "%d forward bins exceed KNFwdMax=%d", nFwd, KNFwdMax); } fwdAxis = AxisSpec{nFwd, -0.5, nFwd - 0.5, "forward bin"}; + std::string list; + for (int k = 0; k < nFwd; ++k) { + list += Form("%s%d %s", k > 0 ? ", " : "", k, fwdLabels[k].c_str()); + } + LOGF(info, "RadialFlowDecorr forward bins: %s", list.c_str()); } // ---- variations ---- From cccdb03568e81b0f060b44c19b9aac1b79107dd9 Mon Sep 17 00:00:00 2001 From: Lucia Anna Tarasovicova Date: Wed, 28 Oct 2026 12:50:37 +0100 Subject: [PATCH 465/648] [PWGLF] PWGLF new histograms in derivedcascadeanalysis.cxx (#18488) Co-authored-by: Lucia Anna Tarasovicova --- .../Tasks/Strangeness/derivedcascadeanalysis.cxx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx index 7f5e082d9d5..c356183ffb2 100644 --- a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx @@ -1209,6 +1209,19 @@ struct Derivedcascadeanalysis { } }); } + void fillHistOccupancyCheckMC(double pt, double mass, float occup, float centrality, bool isPos) + { + static_for<0, 9>([&](auto i) { + constexpr int In = i.value; + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruth"), pt, mass, occup); + } + } + }); + } void fillHistPtDepSelectionStudy(double pt, double mass, double sel, uint64_t selectionCheckMask, bool isPos) { static_for<0, 11>([&](auto i) { @@ -1625,6 +1638,9 @@ struct Derivedcascadeanalysis { if (qaFlags.doOccupancyCheck) { fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive); + if (isTrueMCCascadeDecay) { + fillHistOccupancyCheckMC(ptmc, invmass, occupancy, centrality, isPositive); + } } if (isPositive) { histos.fill(HIST("InvMassAfterSel/hPositiveCascade"), recoPt, invmass, centrality); From c072cf5cead72eff73ccd4715498125778f1453f Mon Sep 17 00:00:00 2001 From: mdaniel-14 Date: Wed, 28 Oct 2026 14:47:13 +0200 Subject: [PATCH 466/648] [PWGCF] alibuild fixes and adding switches for efficiency corrections (#18490) --- .../Tasks/multiparticleCorrelationsDm.cxx | 292 ++++++++++-------- 1 file changed, 155 insertions(+), 137 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx index 225e59faaf0..e7c0c6a2753 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx @@ -15,7 +15,6 @@ // O2: -#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -24,10 +23,30 @@ #include #include #include +#include #include -#include +#include +#include +#include #include +// *) ROOT and C++ include's: + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + using namespace o2; using namespace o2::framework; @@ -43,19 +62,6 @@ using TrackRecSim = soa::Join -#include -#include -#include -#include -#include -#include -#include - -#include -#include - // *) Define enums: enum ERecSim { ERec = 0, ESim, @@ -106,7 +112,7 @@ enum EBeforeAfter { }; // *) Main task: -struct StudentO2Physics { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root +struct MultiparticleCorrelationsDm { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root // *) Base TList to hold all output objects: TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function @@ -117,7 +123,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // *) CCDB: // Service ccdb; // support for offline callibration data base, not needed for the time being... // to be able to use ccdb object in the analysis task - Service ccdb; + Service ccdb{}; // *) Define configurables: @@ -145,52 +151,57 @@ struct StudentO2Physics { // this name is used in lower-case format to name the Configurable> cfPt{"cfPt", {0.2, 5.0}, "{min, max} [GeV]"}; // for weights (efficiency corrections) - Configurable cfFileWithWeights{"cfFileWithWeights", "/scratch8/go26dek/tutoarials/3/DatasetsO2/weights.root", "path to external ROOT file which holds all particle weights"}; + Configurable cfUseWeights{"cfUseWeights", false, "load weights from the file in cfFileWithWeights"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "", "path to external ROOT file with weights; on Hyperloop only CCDB paths (/alice-ccdb.cern.ch/...) work"}; + + Configurable cfVerbose{"cfVerbose", false, "print \run number, centrality and vertex for each collision"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { - bool fProcess[EProcess_N] = {false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. - bool fDryRun = false; // book all histos and run without filling and calculating anything - bool fFillQA = false; // book and fill QA histos + std::array fProcess{}; // see enum EProcess + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fFillQA = false; // book and fill QA histos bool fUseVertexZCut = true; bool fUsePtCut = true; int fCentralityEstimator = ECentFT0C; int fReferenceMultiplicityEstimator = ERefMultTPC; - float fSigmaInel = 771.; // sigma inel [fm^2] - float fVertexZ[2] = {-10., 10.}; // {min, max} [cm] - float fPt[2] = {0.2, 5.0}; - bool fWeightsLoaded = false; // weights are loaded once, at the first collision + float fSigmaInel = 771.; // sigma inel [fm^2] + std::array fVertexZ = {-10., 10.}; // {min, max} [cm] + std::array fPt = {0.2, 5.0}; // {min, max} [GeV] + bool fWeightsLoaded = false; // weights are loaded once, at the first collision + bool fUseWeights = false; // load weights from external file + bool fVerbose = false; // print the values / collision } tc; // you have to prepend "tc." for all objects name in this group later in the code // **) Particle histograms: struct ParticleHistograms { - TList* fParticleHistogramsList = NULL; //!, 2>, EParticleHistograms_N> fParticleHistograms{}; // [type][rec, sim][before, after] } pc; // you have to prepend "pc." for all objects name in this group later in the code // **) Event histograms: struct EventHistograms { - TList* fEventHistogramsList = NULL; + TList* fEventHistogramsList = nullptr; - TH1F* fEventHistograms[EEventHistograms_N][2][2] = {{{NULL}}}; //! [ type -see enum EEventHistograms][rec, sim][before, after event cuts] + std::array, 2>, EEventHistograms_N> fEventHistograms{}; // [type][rec, sim][before, after] } ec; // you have to prepend "ec." for all objects name in this group later in the code // **) QA histograms (booked and filled only if cfFillQA is true): struct QAHistograms { - TList* fQAHistogramsList = NULL; - TH2F* fHistCentralityRecVsSim = NULL; // x = centrality sim (from impact parameter), y = centrality rec - } qa; // you have to prepend "qa." for all objects name in this group later in the code + TList* fQAHistogramsList = nullptr; + TH2F* fHistCentralityRecVsSim = nullptr; // x = centrality sim (from impact parameter), y = centrality rec + } qa; // you have to prepend "qa." for all objects name in this group later in the code // **) Histogram with weights from an external file - TH1F* histWeights = NULL; + TH1F* histWeights = nullptr; // *) Define all member functions to be called in the main process* functions: @@ -255,7 +266,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } if (0 == list->GetEntries()) { - return NULL; + return nullptr; } // The object is in the current base list: @@ -265,18 +276,19 @@ struct StudentO2Physics { // this name is used in lower-case format to name the } // Otherwise, search for the object recursively in the nested lists: - TObject* objectIter; // iterator object in the loop below + TObject* objectIter = nullptr; // iterator object in the loop below TIter next(list); while ((objectIter = next())) // double round braces are to silence the warnings { if (TString(objectIter->ClassName()).EqualTo("TList")) { - objectFinal = getObjectFromList(reinterpret_cast(objectIter), objectName); - if (objectFinal) + objectFinal = getObjectFromList(dynamic_cast(objectIter), objectName); + if (objectFinal) { return objectFinal; + } } } // while(objectIter = next()) - return NULL; + return nullptr; } // TObject* getObjectFromList(...) @@ -285,9 +297,9 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // Last update: 20261018 // *) Return value: - TH1F* hist = NULL; - TList* baseList = NULL; // base top-level list in the TFile, e.g. named "ccdb_object" - TList* listWithRuns = NULL; // nested list with run-wise TList's holding run-specific weights + TH1F* hist = nullptr; + TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: @@ -329,11 +341,11 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // The technical problem here is that it can be nested at any level, // to overcome that there is a helper utility function TObject* getObjectFromList(...), // whose implementation is discussed and provided separately. - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -351,17 +363,17 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // Remember that here I do not access the file; instead, I directly access the object in that file. // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case ccdb->setURL("http://alice-ccdb.cern.ch"); // to be able to use the "ccdb" object in your analysis task - baseList = reinterpret_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); if (!baseList) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -394,11 +406,11 @@ struct StudentO2Physics { // this name is used in lower-case format to name the LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { // baseList->ls(); LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); // changed runNumber instead of tc.fRunNumber.Data() @@ -415,13 +427,13 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // listWithRuns->ls(); // get pointer to "hist" from "listWithRuns" - hist = reinterpret_cast(getObjectFromList(listWithRuns, "histWithEfficiencyCorrections_1")); + hist = dynamic_cast(getObjectFromList(listWithRuns, "histWithEfficiencyCorrections_1")); if (!hist) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - hist->SetDirectory(0); // remove ownerhip from an external file - TH1F* histClone = reinterpret_cast(hist->Clone()); // yes, I have to clone here + hist->SetDirectory(nullptr); // remove ownerhip from an external file + TH1F* histClone = dynamic_cast(hist->Clone()); // yes, I have to clone here if (!bFileIsInCCDB) { // not for CCDB, there the CCDB manager owns the object and deletes it itself delete baseList; // release back the memory @@ -478,7 +490,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the if constexpr (rs == ERecAndSim) { if (!collision.has_mcCollision()) { - LOGF(warning, " No MC collision for this collision, skip simulated part..."); + LOGF(debug, " No MC collision for this collision, skip simulated part..."); return; } auto mcCollision = collision.mcCollision(); @@ -518,7 +530,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the } // Load the weights once, at the first collision, because the run number is only known here - if (!tc.fWeightsLoaded && !cfFileWithWeights.value.empty()) { + if (tc.fUseWeights && !tc.fWeightsLoaded) { histWeights = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", collision.bc().runNumber())); histWeights->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path // value takes the text as std::string and c_str makes it into a constant char* @@ -527,14 +539,12 @@ struct StudentO2Physics { // this name is used in lower-case format to name the tc.fWeightsLoaded = true; } - // Print current run number: - LOGF(info, "Run number: %d", collision.bc().runNumber()); - - // Print centrality estimated with "FT0M" estimator: - LOGF(info, "Centrality: %f", getCentrality(collision)); - - // Print vertex X position: - LOGF(info, "Vertex X position: %f", collision.posX()); + // Print info for each collision, only if switched on in the config: + if (tc.fVerbose) { + LOGF(info, "Run number: %d", collision.bc().runNumber()); + LOGF(info, "Centrality: %f", getCentrality(collision)); + LOGF(info, "Vertex X position: %f", collision.posX()); + } // Fill event histograms before event cuts fillEventHistograms(collision, tracks, EBefore); @@ -570,7 +580,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the pc.fParticleHistograms[EPt][ESim][EBefore]->Fill(mcparticle.pt()); pc.fParticleHistograms[EPhi][ESim][EBefore]->Fill(mcparticle.phi()); } else { - LOGF(warning, " No MC particle for this track..."); + LOGF(debug, " No MC particle for this track..."); } } @@ -618,8 +628,14 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // *) Configure your task using configurables in the json file: tc.fDryRun = cfDryRun; tc.fFillQA = cfFillQA; + tc.fVerbose = cfVerbose; tc.fSigmaInel = cfSigmaInel; + tc.fUseWeights = cfUseWeights; + if (tc.fUseWeights && cfFileWithWeights.value.empty()) { + LOGF(fatal, "cfUseWeights is true, but cfFileWithWeights is empty"); + } + // vertex Z cut copy the swtich and range tc.fUseVertexZCut = cfUseVertexZCut; std::vector lVzCut = cfVertexZ.value; @@ -726,78 +742,80 @@ struct StudentO2Physics { // this name is used in lower-case format to name the float vzMax = lVzBins[2]; // histogram booked before and after cuts - const char* baNames[EBeforeAfter_N] = {"before", "after"}; + std::array baNames = {"before", "after"}; + // book rec histograms only if a process with reconstructed data runs, sim histograms only if one with simulated data runs + const bool bookRec = doprocessRec || doprocessRecSim; + const bool bookSim = doprocessRecSim || doprocessSim; for (int ba = 0; ba < EBeforeAfter_N; ba++) { - // par - pc.fParticleHistograms[EPt][ERec][ba] = new TH1F(Form("fHistPt[ERec][%s]", baNames[ba]), Form("pt distribution for reconstructed particles (%s cuts)", baNames[ba]), nBins, min, max); - pc.fParticleHistograms[EPt][ERec][ba]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ERec][ba]); - - pc.fParticleHistograms[EPt][ESim][ba] = new TH1F(Form("fHistPt[ESim][%s]", baNames[ba]), Form("pt distribution for simulated particles (%s cuts)", baNames[ba]), nBins, min, max); - pc.fParticleHistograms[EPt][ESim][ba]->GetXaxis()->SetTitle("p_{T}"); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ESim][ba]); - - pc.fParticleHistograms[EPhi][ERec][ba] = new TH1F(Form("fHistPhi[ERec][%s]", baNames[ba]), Form("Azimuthal-angle distribution for reconstructed particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); - pc.fParticleHistograms[EPhi][ERec][ba]->GetXaxis()->SetTitle("#varphi (rad)"); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ERec][ba]); - - pc.fParticleHistograms[EPhi][ESim][ba] = new TH1F(Form("fHistPhi[ESim][%s]", baNames[ba]), Form("Azimuthal-angle distribution for simulated particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); - pc.fParticleHistograms[EPhi][ESim][ba]->GetXaxis()->SetTitle("#varphi (rad)"); - pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ESim][ba]); - - // ev - ec.fEventHistograms[EMultiplicity][ERec][ba] = new TH1F(Form("fHistMultiplicity[ERec][%s]", baNames[ba]), Form("Multiplicity for reconstructed particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); - ec.fEventHistograms[EMultiplicity][ERec][ba]->GetXaxis()->SetTitle("total number of available particles"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ERec][ba]); - - ec.fEventHistograms[EMultiplicity][ESim][ba] = new TH1F(Form("fHistMultiplicity[ESim][%s]", baNames[ba]), Form("Multiplicity for simulated particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); - ec.fEventHistograms[EMultiplicity][ESim][ba]->GetXaxis()->SetTitle("total number of available particles"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ESim][ba]); - - // reference multiplicity, only reconstructed - ec.fEventHistograms[EReferenceMultiplicity][ERec][ba] = new TH1F(Form("fHistReferenceMultiplicity[ERec][%s]", baNames[ba]), Form("Reference multiplicity, reconstructed (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax); - ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->GetXaxis()->SetTitle("reference multiplicity"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]); - - ec.fEventHistograms[ECentrality][ERec][ba] = new TH1F(Form("fHistCentrality[ERec][%s]", baNames[ba]), Form("Centrality for reconstructed particles (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); - ec.fEventHistograms[ECentrality][ERec][ba]->GetXaxis()->SetTitle("centrality (%)"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ERec][ba]); - - // sim from impact parameter - ec.fEventHistograms[ECentrality][ESim][ba] = new TH1F(Form("fHistCentrality[ESim][%s]", baNames[ba]), Form("Centrality for simulated particles, from impact parameter (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); - ec.fEventHistograms[ECentrality][ESim][ba]->GetXaxis()->SetTitle("centrality (%)"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ESim][ba]); - - // impact parameter, only simulated - ec.fEventHistograms[EImpactParameter][ESim][ba] = new TH1F(Form("fHistImpactParameter[ESim][%s]", baNames[ba]), Form("Impact parameter, simulated (%s cuts)", baNames[ba]), nBBins, bMin, bMax); - ec.fEventHistograms[EImpactParameter][ESim][ba]->GetXaxis()->SetTitle("b (fm)"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EImpactParameter][ESim][ba]); - - ec.fEventHistograms[EVertexX][ERec][ba] = new TH1F(Form("fHistVertexX[ERec][%s]", baNames[ba]), Form("Vertex X for reconstructed particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); - ec.fEventHistograms[EVertexX][ERec][ba]->GetXaxis()->SetTitle("vertex X"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ERec][ba]); - - ec.fEventHistograms[EVertexX][ESim][ba] = new TH1F(Form("fHistVertexX[ESim][%s]", baNames[ba]), Form("Vertex X for simulated particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); - ec.fEventHistograms[EVertexX][ESim][ba]->GetXaxis()->SetTitle("vertex X"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ESim][ba]); - - ec.fEventHistograms[EVertexY][ERec][ba] = new TH1F(Form("fHistVertexY[ERec][%s]", baNames[ba]), Form("Vertex Y for reconstructed particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); - ec.fEventHistograms[EVertexY][ERec][ba]->GetXaxis()->SetTitle("vertex Y"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ERec][ba]); - - ec.fEventHistograms[EVertexY][ESim][ba] = new TH1F(Form("fHistVertexY[ESim][%s]", baNames[ba]), Form("Vertex Y for simulated particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); - ec.fEventHistograms[EVertexY][ESim][ba]->GetXaxis()->SetTitle("vertex Y"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ESim][ba]); - - ec.fEventHistograms[EVertexZ][ERec][ba] = new TH1F(Form("fHistVertexZ[ERec][%s]", baNames[ba]), Form("Vertex Z for reconstructed particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); - ec.fEventHistograms[EVertexZ][ERec][ba]->GetXaxis()->SetTitle("vertex Z"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ERec][ba]); - - ec.fEventHistograms[EVertexZ][ESim][ba] = new TH1F(Form("fHistVertexZ[ESim][%s]", baNames[ba]), Form("Vertex Z for simulated particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); - ec.fEventHistograms[EVertexZ][ESim][ba]->GetXaxis()->SetTitle("vertex Z"); - ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ESim][ba]); - + if (bookRec) { + // par + pc.fParticleHistograms[EPt][ERec][ba] = new TH1F(Form("fHistPt[ERec][%s]", baNames[ba]), Form("pt distribution for reconstructed particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ERec][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ERec][ba]); + + pc.fParticleHistograms[EPhi][ERec][ba] = new TH1F(Form("fHistPhi[ERec][%s]", baNames[ba]), Form("Azimuthal-angle distribution for reconstructed particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ERec][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ERec][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ERec][ba] = new TH1F(Form("fHistMultiplicity[ERec][%s]", baNames[ba]), Form("Multiplicity for reconstructed particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ERec][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ERec][ba]); + + // reference multiplicity (only reconstructed) + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba] = new TH1F(Form("fHistReferenceMultiplicity[ERec][%s]", baNames[ba]), Form("Reference multiplicity, reconstructed (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax); + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->GetXaxis()->SetTitle("reference multiplicity"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]); + + ec.fEventHistograms[ECentrality][ERec][ba] = new TH1F(Form("fHistCentrality[ERec][%s]", baNames[ba]), Form("Centrality for reconstructed particles (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ERec][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ERec][ba]); + + ec.fEventHistograms[EVertexX][ERec][ba] = new TH1F(Form("fHistVertexX[ERec][%s]", baNames[ba]), Form("Vertex X for reconstructed particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ERec][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ERec][ba]); + + ec.fEventHistograms[EVertexY][ERec][ba] = new TH1F(Form("fHistVertexY[ERec][%s]", baNames[ba]), Form("Vertex Y for reconstructed particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ERec][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ERec][ba]); + + ec.fEventHistograms[EVertexZ][ERec][ba] = new TH1F(Form("fHistVertexZ[ERec][%s]", baNames[ba]), Form("Vertex Z for reconstructed particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ERec][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ERec][ba]); + } + if (bookSim) { + pc.fParticleHistograms[EPt][ESim][ba] = new TH1F(Form("fHistPt[ESim][%s]", baNames[ba]), Form("pt distribution for simulated particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ESim][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ESim][ba]); + + pc.fParticleHistograms[EPhi][ESim][ba] = new TH1F(Form("fHistPhi[ESim][%s]", baNames[ba]), Form("Azimuthal-angle distribution for simulated particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ESim][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ESim][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ESim][ba] = new TH1F(Form("fHistMultiplicity[ESim][%s]", baNames[ba]), Form("Multiplicity for simulated particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ESim][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ESim][ba]); + // sim from impact parameter + ec.fEventHistograms[ECentrality][ESim][ba] = new TH1F(Form("fHistCentrality[ESim][%s]", baNames[ba]), Form("Centrality for simulated particles, from impact parameter (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ESim][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ESim][ba]); + // impact parameter, only simulated + ec.fEventHistograms[EImpactParameter][ESim][ba] = new TH1F(Form("fHistImpactParameter[ESim][%s]", baNames[ba]), Form("Impact parameter, simulated (%s cuts)", baNames[ba]), nBBins, bMin, bMax); + ec.fEventHistograms[EImpactParameter][ESim][ba]->GetXaxis()->SetTitle("b (fm)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EImpactParameter][ESim][ba]); + + ec.fEventHistograms[EVertexX][ESim][ba] = new TH1F(Form("fHistVertexX[ESim][%s]", baNames[ba]), Form("Vertex X for simulated particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ESim][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ESim][ba]); + + ec.fEventHistograms[EVertexY][ESim][ba] = new TH1F(Form("fHistVertexY[ESim][%s]", baNames[ba]), Form("Vertex Y for simulated particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ESim][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ESim][ba]); + + ec.fEventHistograms[EVertexZ][ESim][ba] = new TH1F(Form("fHistVertexZ[ESim][%s]", baNames[ba]), Form("Vertex Z for simulated particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ESim][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ESim][ba]); + } } // end of for (int ba = 0; ba < 2; ba++) // QA histograms, booked only if cfFillQA is true @@ -818,7 +836,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the // *) steer all analysis steps: steer(collision, tracks); } - PROCESS_SWITCH(StudentO2Physics, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version // ------------------------------------------- @@ -827,7 +845,7 @@ struct StudentO2Physics { // this name is used in lower-case format to name the { steer(collision, tracks); } - PROCESS_SWITCH(StudentO2Physics, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); // ------------------------------------------- @@ -836,14 +854,14 @@ struct StudentO2Physics { // this name is used in lower-case format to name the { // steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... } - PROCESS_SWITCH(StudentO2Physics, processSim, "process only simulated data", false); + PROCESS_SWITCH(MultiparticleCorrelationsDm, processSim, "process only simulated data", false); -}; // struct StudentO2Physics { +}; // struct MultiparticleCorrelationsDm { // *) The final touch: WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), }; } // WorkflowSpec... From a0adddb4fd979e99a4b3450b9dc6ab89dbadb79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zuzanna=20Chochulska-Gumi=C5=84ska?= <87480906+zchochul@users.noreply.github.com> Date: Wed, 28 Oct 2026 14:36:43 +0100 Subject: [PATCH 467/648] [PWGCF] FemtoUniverse -- K+K+, K-K- inv mass (#18491) Co-authored-by: Zuzanna Anna Chochulska Co-authored-by: Zuzanna Anna Chochulska Co-authored-by: ALICE Action Bot Co-authored-by: Zuzanna Anna Chochulska --- .../femtoUniverseProducerTask.cxx | 157 ++++++++++++++++++ .../Tasks/femtoUniversePairTaskTrackPhi.cxx | 55 +++--- 2 files changed, 186 insertions(+), 26 deletions(-) diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx index 57fba9c23e9..6ef52e5c8bc 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx @@ -355,6 +355,7 @@ struct FemtoUniverseProducerTask { Configurable confPhiKaonNsigmaTOFfrom055to15{"confPhiKaonNsigmaTOFfrom055to15", 3.0, "Reject if Kaons in 0.55-1.5 are have TOF n sigma above this value."}; Configurable confPhiKaonNsigmaTPCfrom15{"confPhiKaonNsigmaTPCfrom15", 3.0, "Reject if Kaons above 1.5 are have TPC n sigma above this value."}; Configurable confPhiKaonNsigmaTOFfrom15{"confPhiKaonNsigmaTOFfrom15", 3.0, "Reject if Kaons above 1.5 are have TOF n sigma above this value."}; + Configurable confDoLikeSign{"confDoLikeSign", false, "Enable like-sign (fake Phi) background calculation"}; } ConfPhiSelection; // PDG codes for fillMCParticle function @@ -2229,6 +2230,156 @@ struct FemtoUniverseProducerTask { } } + template + void fillLikeSignPhi(CollisionType const& col, TrackType const& tracks) + { + std::vector childIDs = {0, 0}; + std::vector tmpIDtrack; + + // Loop over all track combinations using FullIndexPolicy + for (const auto& [p1, p2] : combinations(soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + // 1. Check minimal track quality cuts for both tracks + if (!trackCuts.isSelectedMinimal(p1) || !trackCuts.isSelectedMinimal(p2)) { + continue; + } + + if (p1.sign() != p2.sign()) { // K+K- pairs are already handled in fillPhi, so we skip them here, only K+K+ and K-K- + continue; + } + + // avoid double counting and self-pairing when using FullIndexPolicy + if (p1.globalIndex() >= p2.globalIndex()) { + continue; + } + + // cuts for daughters + if (ConfPhiSelection.confPhiDoLFPID4Kaons) { + if (!p1.isGlobalTrackWoDCA() || !p2.isGlobalTrackWoDCA() || !p1.isPVContributor() || !p2.isPVContributor()) { + continue; + } + if (!(isKaonNSigmaLF(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), p1.hasTOF()))) { + continue; + } + if (!(isKaonNSigmaLF(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), p2.hasTOF()))) { + continue; + } + } else { + if (!(isKaonNSigma(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon)))) { + continue; + } + if (!(isKaonNSigma(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon)))) { + continue; + } + } + + // Reject protons and pions contamination + if (isKaonRejected(p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion))) { + continue; + } + if (isKaonRejected(p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion))) { + continue; + } + + const auto mMassOne = o2::constants::physics::MassKPlus; + const auto mMassTwo = o2::constants::physics::MassKPlus; + + ROOT::Math::LorentzVector> part1Vec(p1.pt(), p1.eta(), p1.phi(), mMassOne); + ROOT::Math::LorentzVector> part2Vec(p2.pt(), p2.eta(), p2.phi(), mMassTwo); + + ROOT::Math::LorentzVector> sumVec(part1Vec); + sumVec += part2Vec; + + // kinematic cuts for phi candidate + float phiEta = sumVec.Eta(); + if (std::abs(phiEta) > ConfPhiSelection.confPhiEtaHighLimit.value) { + continue; + } + + float phiPt = sumVec.Pt(); + if ((phiPt < ConfPhiSelection.confPhiPtLowLimit.value) || (phiPt > ConfPhiSelection.confPhiPtHighLimit.value)) { + continue; + } + + float phiPhi = RecoDecay::constrainAngle(sumVec.Phi(), 0); + float phiM = sumVec.M(); + + if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit)) { + continue; + } + + phiCuts.fillQA(col, p1, p1, p2, 321, 321); + + int postrackID = p1.globalIndex(); + int rowInPrimaryTrackTablePos = -1; + rowInPrimaryTrackTablePos = getRowDaughters(postrackID, tmpIDtrack); + childIDs[0] = rowInPrimaryTrackTablePos; + childIDs[1] = 0; + + // first daughter track + outputParts(outputCollision.lastIndex(), p1.pt(), + p1.eta(), p1.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p1.dcaXY(), + childIDs, + 0, + p1.sign()); + const int rowOfPosTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p1, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + int negtrackID = p2.globalIndex(); + int rowInPrimaryTrackTableNeg = -1; + rowInPrimaryTrackTableNeg = getRowDaughters(negtrackID, tmpIDtrack); + childIDs[0] = 0; + childIDs[1] = rowInPrimaryTrackTableNeg; + + // second daughter track + outputParts(outputCollision.lastIndex(), + p2.pt(), + p2.eta(), + p2.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p2.dcaXY(), + childIDs, + 0, + p2.sign()); + const int rowOfNegTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p2, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + std::vector indexChildID = {rowOfPosTrack, rowOfNegTrack}; + + // Define like-sign flag: +1 for (K+K+), -1 for (K-K-) + int likeSignFlag = p1.sign(); + + // Save the Fake Phi candidate from K+K+ or K-K- combination + outputParts(outputCollision.lastIndex(), + phiPt, + phiEta, + phiPhi, + aod::femtouniverseparticle::ParticleType::kPhi, + likeSignFlag, // Flag: +1 for K+K+, -1 for K-K-, before it was empty slot for cutcontainer + 0, + phiM, // Invariant mass of the fake Phi candidate + indexChildID, + phiM, + 0); + + if (confIsDebug) { + fillDebugParticle(p1); // QA for first daughter + fillDebugParticle(p2); // QA for second daughter + fillDebugParticle(p1); // QA for fake Phi mother + } + } + } + template void fillParticles(TrackType const& tracks, std::optional>> recoMcIds = std::nullopt) { @@ -2452,6 +2603,9 @@ struct FemtoUniverseProducerTask { } if (ConfGeneral.confIsActivatePhi) { fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } return colcheck; @@ -2628,6 +2782,9 @@ struct FemtoUniverseProducerTask { if (colcheck) { fillTracks(tracks); fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } PROCESS_SWITCH(FemtoUniverseProducerTask, processTrackPhiData, diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index 03c24e77153..8b5948e5472 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -153,6 +153,7 @@ struct FemtoUniversePairTaskTrackPhi { Configurable confPhiPtHigh{"confPhiPtHigh", 4.0, "Higher limit of the Phi pT."}; Configurable confInvMassLowLimitPhi{"confInvMassLowLimitPhi", 1.011, "Lower limit of the Phi invariant mass"}; // change that to do invariant mass cut Configurable confInvMassUpLimitPhi{"confInvMassUpLimitPhi", 1.027, "Upper limit of the Phi invariant mass"}; + Configurable confFillMinvBackground{"confFillMinvBackground", false, "Fill background histograms for Phi invariant mass."}; /// Partitions for the Phi meson (particle 2) Partition partsPhi = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && @@ -551,37 +552,39 @@ struct FemtoUniversePairTaskTrackPhi { sameEventCont.setPair(track, phicandidate, multCol, confUse3D, mWeight); } - // Used for better fitting of invariant mass background. + if (confFillMinvBackground) { + // Used for better fitting of invariant mass background. - TLorentzVector part1Vec; - TLorentzVector part2Vec; + TLorentzVector part1Vec; + TLorentzVector part2Vec; - float mMassOne = o2::constants::physics::MassKPlus; - float mMassTwo = o2::constants::physics::MassKMinus; + float mMassOne = o2::constants::physics::MassKPlus; + float mMassTwo = o2::constants::physics::MassKMinus; - for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { - if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { - part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); - part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); - TLorentzVector sumVec(part1Vec); - sumVec += part2Vec; - registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); - } - if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { - part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); - part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { + if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); + } + if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); - TLorentzVector sumVec(part1Vec); - sumVec += part2Vec; - registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); - } - if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { - part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); - part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); + } + if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); - TLorentzVector sumVec(part1Vec); - sumVec += part2Vec; - registryPhiMinvBackground.fill(HIST("InvariantMassKpKm"), sumVec.M()); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKm"), sumVec.M()); + } } } } From 9c152842d0471016575c40408b54d06a7b5ce9c7 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:10:50 +0100 Subject: [PATCH 468/648] [PWGCF] Fixes in femto framework (#18494) --- PWGCF/Femto/Core/collisionBuilder.h | 2 +- PWGCF/Femto/Core/dndetaBuilder.h | 36 +++++++++++++++++++++++------ PWGCF/Femto/Core/trackBuilder.h | 8 +++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 7bd147009e5..344bc562316 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -57,7 +57,7 @@ struct ConfCollisionFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable sphericityMax{"sphericityMax", 1.f, "Maximum sphericity"}; o2::framework::Configurable magFieldMin{"magFieldMin", -5, "Minimum magnetic field strength (kG)"}; o2::framework::Configurable magFieldMax{"magFieldMax", 5, "Maximum magnetic field strength (kG)"}; - o2::framework::Configurable subGeneratorId{"subGeneratorId", 0, "MC ONLY: Sub-generator ID to keep (0 = MB). Only applied if >= 0; set to -1 (default) to disable and keep all sub-generators"}; + o2::framework::Configurable subGeneratorId{"subGeneratorId", -1, "MC ONLY: Sub-generator ID to keep (0 = MB). Only applied if >= 0; set to -1 (default) to disable and keep all sub-generators"}; }; struct ConfCollisionBits : o2::framework::ConfigurableGroup { diff --git a/PWGCF/Femto/Core/dndetaBuilder.h b/PWGCF/Femto/Core/dndetaBuilder.h index 651960283fb..2404c573ec3 100644 --- a/PWGCF/Femto/Core/dndetaBuilder.h +++ b/PWGCF/Femto/Core/dndetaBuilder.h @@ -232,7 +232,8 @@ class DndetaBuilder /// - reco: for every mc collision only the reconstructed collision with the most PV contributors is used /// (emulates bestCollisionIndex); it has to pass the full event selection including the reco trigger /// - gen: efficiency histograms for all generated collisions, loss histograms for the generated event class - /// (vertex z, optional INEL>0, optional generator-level trigger) + /// (optional INEL>0, optional generator-level trigger); the loss block's reco requirement is Sel8 + /// only (no vtxZ, see isSelectedByMaskOnly), the vtxZ cut only gates the efficiency block above template void processMc(T1 const& cols, T2 const& mcCols, @@ -263,11 +264,20 @@ class DndetaBuilder } // reconstructed side + // selectedReco: full "selvtx" selection (Sel8 + |vtxZ| < cut + trigger), feeds the Gen-efficiency block (Ctrk) + // hasSel8Reco: "sel" selection (Sel8 only, no vtxZ, no trigger), feeds the event/signal loss block (Csel); + // per the analysis note, Csel's Gsel/Esel correspond to the Sel8 class only, Cvtx (the vtxZ-related bias) is + // not implemented (every Run3 event with a vertex is already within the stored range), so the loss-block + // reco requirement must not carry the vtxZ cut like the Ctrk-side "selvtx" one does std::vector selectedReco(nMcCols, -1); + std::vector hasSel8Reco(nMcCols, false); for (auto const& col : cols) { if (!col.has_fMcCol() || bestReco[static_cast(col.fMcColId())] != col.globalIndex()) { continue; } + if (isSelectedByMaskOnly(col)) { + hasSel8Reco[static_cast(col.fMcColId())] = true; + } if (!selectCollision(col, [&]() { return recoTrigger(col); })) { @@ -300,13 +310,12 @@ class DndetaBuilder } } - // event and signal loss: generated event class, true centrality + // event and signal loss: generated event class (INEL or INEL>0), true centrality + // Gall/Gsel (and Eall/Esel) are defined by the analysis note without any vtxZ restriction - the vtxZ + // cut belongs only to Ctrk's "selvtx" quantities above, so it is intentionally not applied here if (!mDoMcLoss) { continue; } - if (mcCol.posZ() < mVtxZMin || mcCol.posZ() > mVtxZMax) { - continue; - } int nChMult = 0; int nChEta1 = 0; for (auto const& particle : particles) { @@ -324,13 +333,14 @@ class DndetaBuilder if (mUseGenTrigger && !genTrigger(mcCol)) { continue; } + const bool hasSel8RecoForLoss = hasSel8Reco[mcColIndex]; const auto mult = static_cast(nChMult); - mHistManager.fillLossEvent(mcCol.cent(), mult, hasSelectedReco); + mHistManager.fillLossEvent(mcCol.cent(), mult, hasSel8RecoForLoss); for (auto const& particle : particles) { if (!isGenParticleSelected(particle, pdg)) { continue; } - mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSelectedReco); + mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSel8RecoForLoss); } } } @@ -353,6 +363,18 @@ class DndetaBuilder LOG(info) << name << ": pT range " << conf.ptMin.value << " - " << conf.ptMax.value << " GeV/c, eta range " << conf.etaMin.value << " - " << conf.etaMax.value; } + /// selected by maks only class (no vtxZ, no INEL>0, no trigger; pure predicate, no cutflow side effects), used to + /// build Gsel/Esel of the event/signal loss block - kept separate from selectCollision() below, whose + /// vtxZ-gated "selvtx" result must not be reused for the loss block (see processMc) + template + bool isSelectedByMaskOnly(T const& col) const + { + return col.mult() >= mMultMin && col.mult() <= mMultMax && + col.cent() >= mCentMin && col.cent() <= mCentMax && + col.magField() >= mMagFieldMin && col.magField() <= mMagFieldMax && + (col.mask() & mCollisionMask) == mCollisionMask; + } + /// event selection with cutflow; the trigger is evaluated last, so the trigger QA /// only contains events that passed all other event selections template diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index eaf95a5c033..8150e840011 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -129,13 +129,13 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { }; // define the template structure for TrackSelection -template +template struct ConfTrackSelection : public o2::framework::ConfigurableGroup { std::string prefix = Prefix; // Unique prefix based on the template argument // configuration parameters o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", 2212, "Absolute value of PDG code. Set sign of charge to -1 for antiparticle."}; o2::framework::Configurable chargeAbs{"chargeAbs", 1, "Absolute value of charge (e.g. 1 for most tracks, 2 for He3)"}; - o2::framework::Configurable chargeSign{"chargeSign", 1, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; + o2::framework::Configurable chargeSign{"chargeSign", DefaultChargeSign, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; // filters for kinematics o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; @@ -170,8 +170,8 @@ using ConfTrackSelection1 = ConfTrackSelection; using ConfTrackSelection2 = ConfTrackSelection; using ConfTrackSelection3 = ConfTrackSelection; -using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; -using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; +using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; +using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; // Track selection for resonance daughters (pion+/-, kaon+/-). Charge sign and abs charge are fixed // by the species itself, so they are compile-time constants rather than configurables. No pdg code From 6e3867f63a2e2f1410d54a2c3744a53ae69630a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Tiek=C3=B6tter?= Date: Wed, 28 Oct 2026 18:48:43 +0100 Subject: [PATCH 469/648] [PWGDQ] Use RCT Flags in tableReader_withAssoc and dqEfficiency_withAssoc (#18468) --- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 68 ++++++++++++++++++++++++- PWGDQ/Tasks/tableReader_withAssoc.h | 69 +++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 176d7ce64b2..626252dc2f4 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -24,6 +24,7 @@ #include "PWGDQ/Core/VarManager.h" #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/TableHelper.h" #include @@ -309,6 +310,12 @@ struct AnalysisEventSelection { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; @@ -322,6 +329,10 @@ struct AnalysisEventSelection { std::map fMetadataRCT, fHeader; int fCurrentRun = -1; + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; + void init(o2::framework::InitContext& context) { if (context.mOptions.get("processDummy")) { @@ -378,6 +389,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -406,7 +472,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan->FillHistClass("Event_BeforeCuts", dqefficiency_helpers::varValues()); // automatically fill all the histograms in the class Event } - if (fEventCut->IsSelected(dqefficiency_helpers::varValues())) { + if (fEventCut->IsSelected(dqefficiency_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigQA) { fHistMan->FillHistClass("Event_AfterCuts", dqefficiency_helpers::varValues()); } diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index 88bdbc0fd67..af99435212a 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -30,6 +30,7 @@ #include "PWGDQ/DataModel/ReducedInfoTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" @@ -339,6 +340,12 @@ struct AnalysisEventSelection { o2::framework::Configurable fConfigFetchInteractionRate{"cfgFetchInteractionRate", false, "Fetch event-wise interaction rate from the CCDB"}; o2::framework::Configurable fConfigIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; AnalysisCompositeCut* fEventCut = nullptr; @@ -351,9 +358,12 @@ struct AnalysisEventSelection { std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices int fCurrentRun = -1; + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; + void init(o2::framework::InitContext& context) { - bool isAnyProcessEnabled = context.mOptions.get("processSkimmed") || context.mOptions.get("processSkimmedBasic") || context.mOptions.get("processSkimmedWithZdc") || context.mOptions.get("processSkimmedWithMultExtra") || context.mOptions.get("processSkimmedWithMultExtraNoQvector") || context.mOptions.get("processSkimmedWithMultExtraZdc") || context.mOptions.get("processSkimmedWithMultExtraZdcFit") || context.mOptions.get("processSkimmedWithQvectorCentr") || context.mOptions.get("processSkimmedWithQvectorCentrMerge"); bool isDummyEnabled = context.mOptions.get("processDummy"); @@ -424,6 +434,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -463,7 +528,7 @@ struct AnalysisEventSelection { } // Apply event cuts and fill histograms after selection - if (fEventCut->IsSelected(dqtablereader_helpers::varValues())) { + if (fEventCut->IsSelected(dqtablereader_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigRunZorro) { if (event.tag_bit(56)) { // This is the bit used for the software trigger event selections [TO BE DONE: find a more clear way to use it] if (fConfigQA) { From e798c64ffdbd0f182ccdfd536c1c925bd12e02db Mon Sep 17 00:00:00 2001 From: spolitan <59452587+stefanopolitano@users.noreply.github.com> Date: Wed, 28 Oct 2026 19:18:24 +0100 Subject: [PATCH 470/648] [PWGHF] Add Dstar channel in flow task (#18492) --- PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx index 4738a6cc74e..6ac6453d025 100644 --- a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx @@ -120,7 +120,8 @@ enum DecayChannel { DplusToPiKPi = 0, LcToPiKP, XicToPKPi, XicToPiKP, - Xic0ToXiPi + Xic0ToXiPi, + DstarToD0Pi }; enum RunMode { @@ -182,6 +183,7 @@ struct HfTaskFlowCharmHadrons { using CandXic0Data = soa::Filtered>; using CandXic0DataWMl = soa::Filtered>; using CandD0DataWMl = soa::Filtered>; + using CandDstarDataWMl = soa::Filtered>; using CollsWithSPQvecs = soa::Join; using CollsWithEsEQvecs = soa::Join; using CollsWithSPEsEQvecs = soa::Join; @@ -190,6 +192,7 @@ struct HfTaskFlowCharmHadrons { Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectXicCandidates = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag || aod::hf_sel_candidate_xic::isSelXicToPiKP >= selectionFlag; Filter filterSelectXic0Candidates = aod::hf_sel_toxipi::resultSelections == true; @@ -198,6 +201,7 @@ struct HfTaskFlowCharmHadrons { Partition selectedDsToPiKKWMl = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Partition selectedD0ToPiKWMl = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPiWMl = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Partition selectedDstarToD0PiWMl = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Partition selectedLcToPKPiWMl = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; Partition selectedLcToPiKPWMl = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Partition selectedXicToPKPiWMl = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag; @@ -418,8 +422,10 @@ struct HfTaskFlowCharmHadrons { addProngIfInSubevent(cand.pxProng0(), cand.pyProng0(), cand.pzProng0()); addProngIfInSubevent(cand.pxProng1(), cand.pyProng1(), cand.pzProng1()); - // 3-prong channels - if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + if constexpr (Channel == DecayChannel::DstarToD0Pi) { + addProngIfInSubevent(cand.pxSoftPi(), cand.pySoftPi(), cand.pzSoftPi()); + } else if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + // Other 3-prong channels addProngIfInSubevent(cand.pxProng2(), cand.pyProng2(), cand.pzProng2()); } } @@ -690,6 +696,13 @@ struct HfTaskFlowCharmHadrons { default: break; } + } else if constexpr (std::is_same_v) { + // Use the soft-pion charge to select the D0 or D0bar mass hypothesis. + signCand = candidate.signSoftPi(); + massCand = signCand > 0 ? candidate.invMassDstar() - candidate.invMassD0() : candidate.invMassAntiDstar() - candidate.invMassD0Bar(); + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDstarToD0Pi()[classMl->at(iclass)]; + } } else if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::LcToPKPi: @@ -947,6 +960,15 @@ struct HfTaskFlowCharmHadrons { } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0SPEsEMl, "Process D0 candidates with ML for SP and EsE information", false); + // Dstar with ML + void processDstarMl(CollsWithSPQvecs::iterator const& collision, + CandDstarDataWMl const& candidatesDstar, + TracksWithExtra const& tracks) + { + runFlowAnalysis(collision, candidatesDstar, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDstarMl, "Process Dstar candidates with ML", false); + // Lc with ML void processLcMl(CollsWithSPQvecs::iterator const& collision, CandLcDataWMl const& /*candidatesLc*/, From ecee2d9a31add76dc4d3c024df6aec3053bea043 Mon Sep 17 00:00:00 2001 From: hfribert Date: Wed, 28 Oct 2026 19:26:10 +0100 Subject: [PATCH 471/648] [PWGCF] Femto: Addition of SigmaPlus variables in output tables (#18496) Co-authored-by: Henrik Fribert --- PWGCF/Femto/Core/kinkBuilder.h | 43 ++++++++++++++--- PWGCF/Femto/DataModel/FemtoTables.h | 23 ++++++++- .../femtoProducerDerivedToDerived.cxx | 48 ++++++++++++++++++- 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index e747922663d..c884e325420 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -554,7 +554,7 @@ class KinkBuilder mProduceSigmaPlus = utils::enableTable("FSigmaPlus_001", table.produceSigmaPlus.value, initContext); mProduceLiteSigmaPlus = utils::enableTable("FLiteSigmaPlus_001", table.produceLiteSigmaPlus.value, initContext); mProduceSigmaPlusMasks = utils::enableTable("FSigmaPlusMasks_001", table.produceSigmaPlusMasks.value, initContext); - mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_001", table.produceSigmaPlusExtras.value, initContext); + mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_002", table.produceSigmaPlusExtras.value, initContext); if (mProduceSigmaPlus && mProduceLiteSigmaPlus) { LOG(fatal) << "FSigmaPlus and FLiteSigmaPlus are mutually exclusive -- enable only one. " @@ -615,7 +615,8 @@ class KinkBuilder fillSigma(collisionBuilder, kinkProducts, kink, daughterIndex); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughterIndex); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); } } } @@ -651,7 +652,8 @@ class KinkBuilder mcBuilder.template fillMcSigmaWithLabel(daughter, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughterIndex); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); mcBuilder.template fillMcSigmaPlusWithLabel(daughter, mcParticles, mcCols, mcProducts); } } @@ -695,8 +697,8 @@ class KinkBuilder } } - template - void fillSigmaPlus(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, int64_t daughterIndex) + template + void fillSigmaPlus(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, T4 const& daughter, T5 const& mother, int64_t daughterIndex) { // Mass is calculated from original momentum components stored in kink table float mass = kink.mSigmaPlus(); @@ -728,7 +730,13 @@ class KinkBuilder kink.xDecVtx(), kink.yDecVtx(), kink.zDecVtx(), - mKinkSelection.getKinkTransRadius()); + mKinkSelection.getKinkTransRadius(), + kink.dcaKinkTopo(), + daughter.tpcNSigmaPr(), + daughter.tpcNSigmaPi(), + daughter.tofNSigmaPr(), + kink.ptMoth(), + mother.itsClusterSizes()); } } @@ -759,6 +767,7 @@ struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedSigmaMasks; o2::framework::Produces producedSigmaPluses; o2::framework::Produces producedSigmaPlusMasks; + o2::framework::Produces producedSigmaPlusExtras; }; class KinkBuilderDerivedToDerived @@ -843,6 +852,7 @@ class KinkBuilderDerivedToDerived sigmaPlus.mass(), chaDaughterIndex); newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); } } @@ -894,11 +904,32 @@ class KinkBuilderDerivedToDerived sigmaPlus.mass(), chaDaughterIndex); newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); mcBuilder.fillSigmaPlusWithLabel(sigmaPlus, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); } } private: + template + void fillSigmaPlusExtensions(T1 const& sigmaPlus, T2& newSigmaPlusTable) + { + if constexpr (requires(T1 t) { t.transRadius(); }) { + newSigmaPlusTable.producedSigmaPlusExtras(sigmaPlus.kinkAngle(), + sigmaPlus.dcaDaugToPV(), + sigmaPlus.dcaMothToPV(), + sigmaPlus.decayVtxX(), + sigmaPlus.decayVtxY(), + sigmaPlus.decayVtxZ(), + sigmaPlus.transRadius(), + sigmaPlus.dcaKinkTopo(), + sigmaPlus.dauTpcNSigmaPr(), + sigmaPlus.dauTpcNSigmaPi(), + sigmaPlus.dauTofNSigmaPr(), + sigmaPlus.ptOriginal(), + sigmaPlus.itsClusterSizes()); + } + } + int mLimitSigma = 0; int mLimitSigmaPlus = 0; }; diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 60e4527d93a..6fa5651970f 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -1014,6 +1014,11 @@ DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of decay v DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Transverse decay radius from PV +DECLARE_SOA_COLUMN(DcaKinkTopo, dcaKinkTopo, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPr, dauTpcNSigmaPr, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPi, dauTpcNSigmaPi, float); +DECLARE_SOA_COLUMN(DauTofNSigmaPr, dauTofNSigmaPr, float); +DECLARE_SOA_COLUMN(PtOriginal, ptOriginal, float); // id column for charged daughter track DECLARE_SOA_INDEX_COLUMN_FULL(ChaDau, chaDau, int32_t, FTracks, "_ChaDau"); //! @@ -1167,7 +1172,23 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_001, "FSIGMAPLUSEXTRAS", 1, femtokinks::DecayVtxZ, femtokinks::TransRadius); -using FSigmaPlusExtras = FSigmaPlusExtras_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_002, "FSIGMAPLUSEXTRAS", 2, + femtokinks::KinkAngle, + femtokinks::DcaDaugToPV, + femtokinks::DcaMothToPV, + femtokinks::DecayVtxX, + femtokinks::DecayVtxY, + femtokinks::DecayVtxZ, + femtokinks::TransRadius, + femtokinks::DcaKinkTopo, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTpcNSigmaPi, + femtokinks::DauTofNSigmaPr, + femtokinks::PtOriginal, + femtotracks::ItsClusterSizes); + +using FSigmaPlusExtras = FSigmaPlusExtras_002; +using StoredFSigmaPlusExtras = StoredFSigmaPlusExtras_002; namespace femtocascades { diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 3ce6b84aca3..ea290785cd6 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -54,10 +54,12 @@ struct FemtoProducerDerivedToDerived { using FemtoOmegas = o2::soa::Join; using FemtoSigma = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusExtended = o2::soa::Join; using FemtoMcTracks = o2::soa::Join; using FemtoMcSigma = o2::soa::Join; using FemtoMcSigmaPlus = o2::soa::Join; + using FemtoMcSigmaPlusExtended = o2::soa::Join; using FemtoMcParticles = o2::soa::Join; o2::framework::SliceCache cache; @@ -121,6 +123,8 @@ struct FemtoProducerDerivedToDerived { kinkbuilder::ConfSigmaPlusSelection1 sigmaPlusSelection1; o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; // cascade builder cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; @@ -149,6 +153,8 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; void init(o2::framework::InitContext& /*context*/) { @@ -158,10 +164,10 @@ struct FemtoProducerDerivedToDerived { static_cast(doprocessLambdas) + static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + - static_cast(doprocessTracksSigmaPlus) + + static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + - static_cast(doprocessTracksSigmaPlusMc); + static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc); if (activeProcesses != 1) { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } @@ -292,6 +298,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); + void processTracksSigmaPlusExtended(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlusExtended const& sigmaplus) + { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { + return; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); + void processTracksRho0(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoRho0s const& rho0s) { if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { @@ -396,6 +414,32 @@ struct FemtoProducerDerivedToDerived { } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusMc, "Process sigmaPlus and tracks with MC information", false); + + void processTracksSigmaPlusExtendedMc(FilteredFemtoMcCollisions const& cols, + FemtoMcTracks const& tracks, + FemtoMcSigmaPlusExtended const& sigmaplus, + o2::aod::FMcCols const& mcCols, + FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + mcBuilder.reset(mcCols, mcParticles); + + for (auto const& col : cols) { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { + continue; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusExtendedPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtendedMc, "Process sigmaPlus (with SigmaPlusExtras) and tracks with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) From 3531bdd62e92d4954bfa0717cc7a121a0f713ae9 Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Wed, 28 Oct 2026 21:42:01 +0100 Subject: [PATCH 472/648] [DPG] adding histograms to monitor track quality vs occupancy for tracks reaching specified NrowsThreshold (#18474) --- DPG/Tasks/AOTEvent/CMakeLists.txt | 2 +- DPG/Tasks/AOTEvent/eventSelectionQa.cxx | 66 ++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/DPG/Tasks/AOTEvent/CMakeLists.txt b/DPG/Tasks/AOTEvent/CMakeLists.txt index 59e049e6356..202543fa7c2 100644 --- a/DPG/Tasks/AOTEvent/CMakeLists.txt +++ b/DPG/Tasks/AOTEvent/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(event-selection-qa SOURCES eventSelectionQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT O2::TPCBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lumi-qa diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index 56a4c9a28ac..752a81885ca 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -73,6 +74,8 @@ struct EventSelectionQaTask { Configurable isLowFlux{"isLowFlux", 1, "1 - low flux (pp, pPb), 0 - high flux (PbPb)"}; Configurable fillITSdeadStaveHists{"fillITSdeadStaveHists", 0, "0 - no, 1 - yes"}; Configurable fillTPCnClsVsOccupancyHists{"fillTPCnClsVsOccupancyHists", 0, "0 - no, 1 - yes"}; + Configurable useTestWithTrackTools{"useTestWithTrackTools", 0, "0 - no, 1 - yes"}; // experimental + Configurable thresholdNrowsForTrackToolTest{"thresholdNrowsForTrackToolTest", 120, "threshold for Nrows for TrackTool test"}; // minimum number of TPC rows (experimental) Service ccdb; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -91,6 +94,8 @@ struct EventSelectionQaTask { std::string strPassName = ""; // RecoPassName (for data) or AnchorPassName (for MC) from metadata bool isMC = false; + o2::tpc::recohelpers::TPCSectorEdgeConfig tpcSectorEdgeConfig; // for special selection of tracks within TPC + std::bitset bcPatternA; std::bitset bcPatternC; std::bitset bcPatternB; @@ -121,6 +126,12 @@ struct EventSelectionQaTask { strLPMProductionTag = metadataInfo.get("LPMProductionTag"); // to extract info from ccdb by the tag strPassName = metadataInfo.get(isMC ? "AnchorPassName" : "RecoPassName"); + // for special selection of tracks within TPC (Oct 2026) + tpcSectorEdgeConfig.guardLowerCm = 0.5f; + tpcSectorEdgeConfig.guardUpperCm = 0.5f; + tpcSectorEdgeConfig.zMaxCm = 250.f; + tpcSectorEdgeConfig.maxSnp = 0.9f; + const AxisSpec axisMultV0M{1000, 0., 40000., "V0M multiplicity"}; const AxisSpec axisMultV0A{1000, 0., isLowFlux ? 40000. : 200000., "V0A multiplicity"}; const AxisSpec axisMultV0C{1000, 0., 30000., "V0C multiplicity"}; @@ -390,6 +401,11 @@ struct EventSelectionQaTask { histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); + // requested by TPC experts: check with tracks at given NrowsThreshold + if (useTestWithTrackTools) { + histos.add("occupancyQA/hNumTracksITS_isValidAtNrowsThreshold_EtaPtCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_isValidAtNrowsThreshold_EtaPtSel8VxCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); + } // requested by TPC experts: nTPConly tracks vs occupancy histos.add("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); histos.add("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); @@ -412,7 +428,7 @@ struct EventSelectionQaTask { histos.add("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win", "dE/dx", kTH3F, {{20, 0, 4000, "n PV tracks"}, {60, 0, 15000, "occupancy"}, {800, 0.0, 800.0, "dE/dx (a. u.)"}}); } - } + } // end of init() void processRun2( ColEvSels const& cols, @@ -1185,6 +1201,8 @@ struct EventSelectionQaTask { int nTracksITS = 0; int nTracksITSTPC = 0; + int nTracksITSValidAtNrowsThresholdAfterEtaPtCuts = 0; + int nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts = 0; bool isTVX = col.selection_bit(kIsTriggerTVX); @@ -1202,11 +1220,33 @@ struct EventSelectionQaTask { nTPConlyNoITS++; } + // Oct 2026: test presence of track at 85 cm radius using new tools - https://github.com/AliceO2Group/AliceO2/tree/dev/Common/TrackTools + bool isTrackWithNrowsAvailable = false; + if (useTestWithTrackTools) { + const float bzKG = 5.f; + const float radiusOfInterest = 85.f; // cm + auto trackParLocal = o2::recohelpers::makeTrackParFromLocalParameters( + track.x(), + track.alpha(), + track.y(), + track.z(), + track.snp(), + track.tgl(), + track.signed1Pt()); + auto trackAt85 = o2::recohelpers::getParametersAtRadiusH(trackParLocal, radiusOfInterest, bzKG); + auto l0 = o2::tpc::recohelpers::sectorEdgeStackLinear(trackParLocal, bzKG, tpcSectorEdgeConfig); + + isTrackWithNrowsAvailable = (trackAt85.valid() && l0.nRowsAvailable > thresholdNrowsForTrackToolTest); + } + if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2) { if (track.hasITS()) { nTracksITS++; if (track.hasTPC()) nTracksITSTPC++; + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nTracksITSValidAtNrowsThresholdAfterEtaPtCuts++; + } } } @@ -1234,12 +1274,18 @@ struct EventSelectionQaTask { histos.fill(HIST("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable() - tpcNClsFindableMinusCrossedRowsCorrected, occupancyByTracks); } } // end of hasTPC - if (col.sel8() && fabs(col.posZ()) < 10 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { - nContributorsAfterEtaTPCCuts++; - // ROF border QA - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); - } + if (col.sel8() && fabs(col.posZ()) < 10) { + if (track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { + nContributorsAfterEtaTPCCuts++; + // ROF border QA + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); + } + + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts++; + } + } // end of sel8 and vZ cuts } if (!track.hasTPC()) histos.fill(HIST("hITStrackBcDiff"), trackBcDiff); @@ -1268,6 +1314,12 @@ struct EventSelectionQaTask { histos.fill(HIST("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCLooseCuts, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nTracksITS, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nTracksITSTPC, occupancyByTracks); + + if (useTestWithTrackTools) { + histos.fill(HIST("occupancyQA/hNumTracksITS_isValidAtNrowsThreshold_EtaPtCuts_vs_V0A_vs_occupancy"), multV0A, nTracksITSValidAtNrowsThresholdAfterEtaPtCuts, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPV_isValidAtNrowsThreshold_EtaPtSel8VxCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts, occupancyByTracks); + } + if (col.selection_bit(kNoCollInTimeRangeNarrow)) { histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nPV, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); From 72def595f7f773f3a58899034d1a8e83e5ea2c3d Mon Sep 17 00:00:00 2001 From: Junlee Kim Date: Thu, 29 Oct 2026 09:48:25 +0900 Subject: [PATCH 473/648] [PWGLF] f0980pbpbanalysis.cxx: adding ITS PID (#18486) --- PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx | 138 +++++++++++-------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx index 82b711ede8e..6166c39a314 100644 --- a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx +++ b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx @@ -17,6 +17,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -68,8 +70,9 @@ struct F0980pbpbanalysis { {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; + o2::aod::ITSResponse itsResponse; Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; @@ -130,6 +133,8 @@ struct F0980pbpbanalysis { Configurable cfgPIDMaxTPCnSigmaPion{"cfgPIDMaxTPCnSigmaPion", 5.0, "TPC nSigma cut for Pion"}; // TPC Configurable cfgPIDMaxTPCnSigmaPionS{"cfgPIDMaxTPCnSigmaPionS", 3.0, "TPC nSigma cut for Pion as a standalone"}; Configurable cfgPIDMaxTiednSigmaPion{"cfgPIDMaxTiednSigmaPion", 3.0, "Combined nSigma cut for Pion"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaPion{"cfgPIDMaxITSnSigmaPion", 3.0, "ITS nSigma selection"}; } PIDConfig; // Flow Configurables @@ -198,27 +203,27 @@ struct F0980pbpbanalysis { int nmode = FlowConfig.cfgQvecNMods; static constexpr double QvecAmpMin = 1e-4; - int detId; - int refAId; - int refBId; + int detId = 0; + int refAId = 4; + int refBId = 5; - int qVecDetInd; - int qVecRefAInd; - int qVecRefBInd; + int qVecDetInd = 0; + int qVecRefAInd = 0; + int qVecRefBInd = 0; - double eventPlaneDet; - double eventPlaneRefA; - double eventPlaneRefB; + double eventPlaneDet = 0.; + double eventPlaneRefA = 0.; + double eventPlaneRefB = 0.; - float centrality; + float centrality = 1.; - double angle; - double relPhi; - double relPhiRot; - double relPhiMix; + double angle = -1.; + double relPhi = -1.; + double relPhiRot = -1.; + double relPhiMix = -1.; // double massPi = o2::constants::physics::MassPionCharged; - double massPtl; + double massPtl = 0.; int nTotalEvents = 0; @@ -231,6 +236,7 @@ struct F0980pbpbanalysis { PIDRun3 = 0, PIDRun2 = 1, PIDTest = 2, + PIDAddITS = 3, }; enum PtlList { @@ -262,19 +268,23 @@ struct F0980pbpbanalysis { { if (name.value == "FT0C") { return 0; - } else if (name.value == "FT0A") { + } + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCpos") { + } + if (name.value == "TPCpos") { return 4; - } else if (name.value == "TPCneg") { + } + if (name.value == "TPCneg") { return 5; - } else { - return 0; } + return 0; } template @@ -372,45 +382,45 @@ struct F0980pbpbanalysis { if (std::abs(collision.posZ()) > EventConfig.cfgEventCutVertex) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 1); // Vertex cut - + } if (!collision.sel8()) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 2); // sel8 cut - + } if (EventConfig.cfgEventGoodZvtxSel && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 3); // Good Zvtx cut - + } if (EventConfig.cfgEventNSamePileupSel && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 4); // Same bunch pileup cut - + } if (EventConfig.cfgEventNCollinTRSel && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 5); // Collision time range standard cut - + } if (EventConfig.cfgEventQvecSel && (collision.qvecAmp()[detId] < QvecAmpMin || collision.qvecAmp()[refAId] < QvecAmpMin || collision.qvecAmp()[refBId] < QvecAmpMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 6); // Qvector cut - + } if (EventConfig.cfgEventOccupancySel && (collision.trackOccupancyInTimeRange() > EventConfig.cfgEventOccupancyMax || collision.trackOccupancyInTimeRange() < EventConfig.cfgEventOccupancyMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 7); // Occupancy cut - + } if (EventConfig.cfgEventCentMax < centrality) { return 0; } @@ -423,9 +433,9 @@ struct F0980pbpbanalysis { return 0; } */ - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 8); // Centrality cut - + } if (EventConfig.cfgEventPVSel && std::abs(collision.posZ()) > EventConfig.cfgEventPV) { return 0; } @@ -440,27 +450,31 @@ struct F0980pbpbanalysis { template bool trackSelected(const TrackType& track, const bool QA) { - if (QAConfig.cfgQATrackCut && QA) + if (QAConfig.cfgQATrackCut && QA) { fillQA(false, track, 3); - if (QAConfig.cfgQATrackFlowCut && QA) + } + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 0); // All passed // // Kinematic + } if (std::abs(track.pt()) < TrackConfig.cfgTrackPtMin) { return 0; } if (std::abs(track.eta()) > TrackConfig.cfgTrackEtaMax) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 1); // Kinematic // Primary Vertex(PV) Contributing to the PV fit + } if (TrackConfig.cfgTrackIsPVContributor && !track.isPVContributor()) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 2); // PV Contributor // Global Track without DCA + } if (TrackConfig.cfgTrackIsGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return 0; } @@ -476,9 +490,10 @@ struct F0980pbpbanalysis { if (TrackConfig.cfgTrackITSChi2 > 0 && track.itsChi2NCl() > TrackConfig.cfgTrackITSChi2) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 4); // GlobalWoDCATrack // DCA cuts + } if (TrackConfig.cfgTrackDCArDepPTSel) { if (std::abs(track.dcaXY()) > (TrackConfig.cfgTrackDCArDepPTP0 + (TrackConfig.cfgTrackDCArDepPTExp / track.pt()))) { return 0; @@ -571,6 +586,16 @@ struct F0980pbpbanalysis { return 0; } } + } else if (cfgListPID == PIDList::PIDAddITS) { + if (!track.hasTOF()) { + return 0; + } + if ((getTpcNSigma(track) * getTpcNSigma(track) + getTofNSigma(track) * getTofNSigma(track)) > (PIDConfig.cfgPIDMaxTiednSigmaPion * PIDConfig.cfgPIDMaxTiednSigmaPion)) { + return 0; + } + if (!PIDConfig.cfgPIDITSFlag || std::abs(itsResponse.nSigmaITS(track)) > PIDConfig.cfgPIDMaxITSnSigmaPion) { + return 0; + } } if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 8); // PID @@ -582,15 +607,22 @@ struct F0980pbpbanalysis { template bool pairAngleSelection(const TrackType1& track1, const TrackType2& track2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; + double pt1 = 0.; + double pt2 = 0.; + double pz1 = 0.; + double pz2 = 0.; + double p1 = 0.; + double p2 = 0.; + double angle12 = 0.; + pt1 = track1.pt(); pt2 = track2.pt(); pz1 = track1.pz(); pz2 = track2.pz(); p1 = track1.p(); p2 = track2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (PhiTestConfig.cfgPhiDeepAngleSel && angle < PhiTestConfig.cfgPhiDeepAngle) { + angle12 = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + if (PhiTestConfig.cfgPhiDeepAngleSel && angle12 < PhiTestConfig.cfgPhiDeepAngle) { return 0; } return 1; @@ -601,9 +633,8 @@ struct F0980pbpbanalysis { { if (cfgListPtl == PtlList::PtlPion) { return track.tpcNSigmaPi(); - } else { - return track.tpcNSigmaKa(); } + return track.tpcNSigmaKa(); } template @@ -611,9 +642,8 @@ struct F0980pbpbanalysis { { if (cfgListPtl == PtlList::PtlPion) { return track.tofNSigmaPi(); - } else { - return track.tofNSigmaKa(); } + return track.tofNSigmaKa(); } template @@ -634,9 +664,9 @@ struct F0980pbpbanalysis { if (!trackSelected(trk1, true)) { continue; } - if (QAConfig.cfgQATrackCut) + if (QAConfig.cfgQATrackCut) { fillQA(true, trk1, 3); - + } if (!selectionPID(trk1, true)) { continue; } @@ -721,7 +751,7 @@ struct F0980pbpbanalysis { if (c1.bcId() == c2.bcId()) { continue; } - double eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); + eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); for (const auto& trk1 : t1) { if (!trackSelected(trk1, false)) { From a4323d818df51f821181e5d6cfa5113fa4281db0 Mon Sep 17 00:00:00 2001 From: Youngwoong Seo Date: Thu, 29 Oct 2026 18:42:57 +0900 Subject: [PATCH 474/648] [PWGHF] Add histograms and mass column for Xicp analysis (#18254) Co-authored-by: JaeYoonCHO --- PWGHF/DataModel/CandidateReconstructionTables.h | 4 +++- PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx | 11 ++++++++--- PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx | 3 +++ PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx | 4 ++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index 4f75bb6ddfa..d46d02116a2 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -1529,6 +1529,7 @@ DECLARE_SOA_COLUMN(NSigTofPiFromLambda, nSigTofPiFromLambda, float); DECLARE_SOA_COLUMN(NSigTofPrFromLambda, nSigTofPrFromLambda, float); // MC DECLARE_SOA_COLUMN(DecayLengthMcGen, decayLengthMcGen, float); +DECLARE_SOA_COLUMN(MassMcGen, massMcGen, float); // Residuals and pulls DECLARE_SOA_COLUMN(PtResidual, ptResidual, float); DECLARE_SOA_COLUMN(PResidual, pResidual, float); @@ -1634,7 +1635,8 @@ DECLARE_SOA_TABLE(HfCandXicMcGen, "AOD", "HFCANDXICMCGEN", hf_cand_mc_flag::FlagMcMatchGen, hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + hf_cand_xic_to_xi_pi_pi::MassMcGen); // table with residuals and pulls of PV DECLARE_SOA_TABLE(HfCandXicResid, "AOD", "HFCANDXICRESID", diff --git a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx index 3780b21c1f9..b3b72f70883 100644 --- a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx @@ -1093,6 +1093,7 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { registry.add("hDecayedPions", "hDecayedPions", {HistType::kTH1F, {{5, -0.5, 4.5}}}); registry.add("hInteractionsWithMaterial", "hInteractionsWithMaterial", {HistType::kTH1F, {{6, -0.5, 5.5}}}); registry.add("hDebugRec", "hDebugRec", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("hy_pt_gen", "Generated MC", {HistType::kTH2F, {{20, -1.0, 1.0}, {200, 0, 20}}}); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + TotalRec, "total"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XicToFinalState, "#Xi^{+}_{c} #rightarrow #pi^{#plus} #pi^{#plus} #pi^{#minus} p #pi^{#minus}"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XiToPiPPi, "#Xi^{#minus} #rightarrow #pi^{#minus} p #pi^{#minus}"); @@ -1300,12 +1301,13 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { if (rejectionMask != 0) { // at least one event selection not satisfied --> reject all particles from this collision for (unsigned int i = 0; i < mcParticlesPerMcColl.size(); ++i) { - rowMcMatchGen(-99, -99, -99, decayLengthGen); + rowMcMatchGen(-99, -99, -99, decayLengthGen, -1.f); } continue; } for (const auto& particle : mcParticlesPerMcColl) { + const float massGen = RecoDecay::m(particle.pVector(), particle.e()); sign = 0; flag = 0; origin = RecoDecay::OriginType::None; @@ -1345,6 +1347,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Check whether the charm baryon is non-prompt (from a b quark). if (flag != 0) { + // fill inclusive generated Xic+ + registry.fill(HIST("hy_pt_gen"), particle.y(), particle.pt()); + origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, false, &idxBhadMothers); // Calculate the decay length of the generated particle auto dau0 = particle.template daughters_as().begin(); @@ -1355,9 +1360,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Fill table if (origin == RecoDecay::OriginType::NonPrompt) { auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0]); - rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen); + rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen, massGen); } else { - rowMcMatchGen(flag, origin, 0, decayLengthGen); + rowMcMatchGen(flag, origin, 0, decayLengthGen, massGen); } } // close loop over generated particles } // close loop over McCollisions diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 1abe826680c..8972554209a 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -245,6 +245,8 @@ struct HfCandidateSelectorXicToXiPiPi { registry.add("hNCrossedRowsTpcBachelorPionRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (#pi #leftarrow #Xi);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0PosDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 pos. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0NegDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 neg. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); + /// reconstructed MC + registry.add("hmass_pt_reco_truth", "Reconstructed truth matching MC;inv.mass(#Xi#pi#pi) [GeV/c^{2}];p_{T} [GeV/c]", {HistType::kTH2F, {{40, 2.3, 2.7}, {200, 0, 20}}}); } } @@ -641,6 +643,7 @@ struct HfCandidateSelectorXicToXiPiPi { if constexpr (IsMc) { if (isMatchedSignal) { registry.fill(HIST("hSelCandidatesRecSig"), PidSelected); + registry.fill(HIST("hmass_pt_reco_truth"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); } else { registry.fill(HIST("hSelCandidatesRecBkg"), PidSelected); } diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index 6ae1bed98dd..338fe6ca1fd 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -374,6 +374,7 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullPs, "AOD", "HFXICXI2PIFULLP", hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, full::Pt, + full::M, full::Eta, full::Phi, full::Y, @@ -895,6 +896,7 @@ struct HfTreeCreatorXicToXiPiPi { particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), @@ -967,6 +969,7 @@ struct HfTreeCreatorXicToXiPiPi { particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), @@ -1032,6 +1035,7 @@ struct HfTreeCreatorXicToXiPiPi { particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), From a8630e50ac0bd9727b73ed36fd6571fcc24bd328 Mon Sep 17 00:00:00 2001 From: jaelpark Date: Thu, 29 Oct 2026 11:09:34 +0100 Subject: [PATCH 475/648] [PWGCF] filterCorrelations: optional HepMC table subscription for MC (#18479) --- PWGCF/TableProducer/filterCorrelations.cxx | 59 +++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/PWGCF/TableProducer/filterCorrelations.cxx b/PWGCF/TableProducer/filterCorrelations.cxx index d31f482fa9b..7b91bbfe308 100644 --- a/PWGCF/TableProducer/filterCorrelations.cxx +++ b/PWGCF/TableProducer/filterCorrelations.cxx @@ -437,6 +437,9 @@ struct FilterCF { template using HasMultTables = decltype(std::declval().multNTracksPV()); + template + using HasNMPI = decltype(std::declval().nMPI()); + /// \brief Templetized process data for a given collision and its associated tracks /// \param collision The collision object containing information about the collision /// \param tracks The collection of tracks associated with the collision @@ -634,7 +637,9 @@ struct FilterCF { } } outputMcCollisions(mcCollision.posZ(), multiplicity); - outputMcCollisionExtras(mcCollision.nMPI()); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } } // PASS 2 on collisions: store collisions and tracks @@ -707,7 +712,7 @@ struct FilterCF { Preslice perMcCollision = aod::mcparticle::mcCollisionId; Preslice perCollision = aod::track::collisionId; using McCollisionsWithHepMC = soa::Join; - void processMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMC(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -716,8 +721,17 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processMC, "Process MC", false); + void processMCWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCWithHepMC, "Process MC with HepMC cross-section (nMPI)", false); + // NOTE not filtering collisions here because in that case there can be tracks referring to MC particles which are not part of the selected MC collisions - void processMCPid(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMCPid(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -726,7 +740,16 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processMCPid, "Process MC with PID", false); - void processMCMults(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + void processMCPidWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCPidWithHepMC, "Process MC with PID and HepMC cross-section (nMPI)", false); + + void processMCMults(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) @@ -736,7 +759,18 @@ struct FilterCF { PROCESS_SWITCH(FilterCF, processMCMults, "Process MC with multiplicity sets", false); - void processMCGen(McCollisionsWithHepMC::iterator const& mcCollision, aod::McParticles const& particles) + void processMCMultsWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCMultsWithHepMC, "Process MC with multiplicity sets and HepMC cross-section (nMPI)", false); + + /// \brief Templated MCGen processing; stores nMPI only when the MC collision carries the HepMC cross-section table + template + void processMCGenT(const TMC& mcCollision, aod::McParticles const& particles) { float multiplicity = 0.0f; for (const auto& particle : particles) { @@ -756,9 +790,22 @@ struct FilterCF { sign, particle.pdgCode(), particle.flags()); } outputMcCollisions(mcCollision.posZ(), multiplicity); - outputMcCollisionExtras(mcCollision.nMPI()); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } + } + + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); } PROCESS_SWITCH(FilterCF, processMCGen, "Process MCGen", false); + + void processMCGenWithHepMC(McCollisionsWithHepMC::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); + } + PROCESS_SWITCH(FilterCF, processMCGenWithHepMC, "Process MCGen with HepMC cross-section (nMPI)", false); }; struct MultiplicitySelector { From 3f87474f7acaa20142599ede851da0c4dc9f203f Mon Sep 17 00:00:00 2001 From: Maxim Virta <84773378+MaximVirta@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:29:14 +0100 Subject: [PATCH 476/648] [PWGCF] Derived data for Generic Framework analyses created (#18495) Co-authored-by: Maxim Virta Co-authored-by: ALICE Action Bot --- PWGCF/GenericFramework/DataModel/GfwDerived.h | 146 ++++++ .../TableProducer/CMakeLists.txt | 5 + .../TableProducer/flowGfwDerivedProducer.cxx | 443 +++++++++++++++++ PWGCF/GenericFramework/Tasks/flowGfwV02.cxx | 468 +++++++++++------- PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx | 19 +- 5 files changed, 897 insertions(+), 184 deletions(-) create mode 100644 PWGCF/GenericFramework/DataModel/GfwDerived.h create mode 100644 PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx diff --git a/PWGCF/GenericFramework/DataModel/GfwDerived.h b/PWGCF/GenericFramework/DataModel/GfwDerived.h new file mode 100644 index 00000000000..9fc0edf7f5f --- /dev/null +++ b/PWGCF/GenericFramework/DataModel/GfwDerived.h @@ -0,0 +1,146 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GfwDerived.h +/// \brief Derived collision and track tables for the GFW v02 flow analysis. +/// +/// Selection columns are bit masks. A later analysis keeps a row when every bit +/// set in its mask is also set on the row (`ncheckbit` / `(value & mask) == mask`). +/// PID is stored only as bits. The nominal, tight, and loose windows are fixed +/// at production. +/// +/// \author Maxim Virta, AU, maxim.virta@cern.ch + +#ifndef PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ +#define PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ + +#include +#include + +#include + +#include + +namespace o2::aod +{ +namespace gfwcollision +{ +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality estimator chosen at production +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Global-track multiplicity, multNTracksGlobal +DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); //! Event-selection bitmask, see EventSelection + +/// One bit per event-level decision. Bits record that the decision passed. +/// sel8 is required by the producer and is not stored here. +enum EventSelection : uint16_t { + kNoSameBunchPileup = BIT(0), + kIsGoodZvtxFT0vsPV = BIT(1), + kNoCollInTimeRangeStandard = BIT(2), + kNoCollInRofStandard = BIT(3), + kNoHighMultCollInPrevRof = BIT(4), + kIsVertexITSTPC = BIT(5), + kIsGoodITSLayersAll = BIT(6), + kNoTimeFrameBorder = BIT(7), + kNoITSROFrameBorder = BIT(8), + kOccupancy = BIT(9), //! track occupancy is within the production threshold + kNotTVXinTRD = BIT(10), //! collision is not a TRD-triggered TVX event + kGoodVertex = BIT(11), //! PV contributor and z-resolution quality + kMultCorr = BIT(12) //! multiplicity-correlation cut +}; + +inline constexpr uint16_t DefaultEventMask = 0; +} // namespace gfwcollision + +DECLARE_SOA_TABLE(GfwCollisions, "AOD", "GFWCOLLISION", //! + o2::soa::Index<>, + bc::RunNumber, + collision::PosZ, + gfwcollision::Centrality, + gfwcollision::Multiplicity, + timestamp::Timestamp, + gfwcollision::EventSel); +using GfwCollision = GfwCollisions::iterator; + +namespace gfwtrack +{ +DECLARE_SOA_INDEX_COLUMN(GfwCollision, gfwCollision); //! Parent derived collision +DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuthal angle +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); //! Track has a TOF match +DECLARE_SOA_COLUMN(TrackSel, trackSel, uint32_t); //! Track and PID bitmask, see TrackSelection + +/// Track-quality bits occupy 0-13. TPC PID bits occupy 14-22 and TOF PID bits occupy 23-31. +/// Global-track, ITS-cluster, and ITS-chi2 requirements are applied by the producer and are not stored. +/// DCA bits are |DCA| <= nSigma times a pT-dependent shape. +/// A PID bit is set when low * scale < nSigma < up * scale. +/// Above the TOF pT cut, every PID bit stays unset when the track has no TOF match. +/// Within each detector, PidWindowBits covers π/K/p, followed by the tight window and then the loose window. +enum TrackSelection : uint32_t { + kDCAxy = BIT(0), //! |DCAxy| within the nominal nSigma times f_xy(pT) + kDCAxyTight = BIT(1), + kDCAxyLoose = BIT(2), + kDCAz = BIT(3), //! |DCAz| within the nominal nSigma times f_z(pT) + kDCAzTight = BIT(4), + kDCAzLoose = BIT(5), // DCA bit selections + + kTPCXrows = BIT(6), + kTPCXrowsTight = BIT(7), + kTPCXrowsLoose = BIT(8), + kTPCCls = BIT(9), + kTPCClsTight = BIT(10), + kTPCClsLoose = BIT(11), + kChi2TPC = BIT(12), + kChi2TPCLoose = BIT(13), // TPC bit selections + + // TPC PID selections: nominal, tight, then loose + kTpcPi = BIT(14), + kTpcKa = BIT(15), + kTpcPr = BIT(16), + kTpcPiTight = BIT(17), + kTpcKaTight = BIT(18), + kTpcPrTight = BIT(19), + kTpcPiLoose = BIT(20), + kTpcKaLoose = BIT(21), + kTpcPrLoose = BIT(22), + // TOF PID selections: nominal, tight, then loose + kTofPi = BIT(23), + kTofKa = BIT(24), + kTofPr = BIT(25), + kTofPiTight = BIT(26), + kTofKaTight = BIT(27), + kTofPrTight = BIT(28), + kTofPiLoose = BIT(29), + kTofKaLoose = BIT(30), + kTofPrLoose = BIT(31) +}; + +/// Quality bits corresponding to the default flowGfwV02 track filter plus trackSelected(). +inline constexpr uint32_t NominalMask = static_cast(kDCAxy | kDCAz | kTPCXrows | kTPCCls | kChi2TPC); +/// Loosest quality bits. Production can optionally require these while still leaving the nominal cuts free. +inline constexpr uint32_t LooseSkimMask = static_cast(kDCAxyLoose | kDCAzLoose | kTPCXrowsLoose | kTPCClsLoose | kChi2TPCLoose); +/// Tracks that enter the multiplicity-correlation count. Global track and ITS chi2 are already required by the producer. +inline constexpr uint32_t MultCorrTrackMask = static_cast(kDCAz | kChi2TPC); + +inline constexpr int PidWindowBits = 3; +inline constexpr int PidWindowNominal = 0; +inline constexpr int PidWindowTight = 1; +inline constexpr int PidWindowLoose = 2; +} // namespace gfwtrack + +DECLARE_SOA_TABLE(GfwTracks, "AOD", "GFWTRACK", //! + o2::soa::Index<>, + gfwtrack::GfwCollisionId, + gfwtrack::Pt, gfwtrack::Eta, gfwtrack::Phi, + gfwtrack::HasTOF, gfwtrack::TrackSel); +using GfwTrack = GfwTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ diff --git a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt index bbfd7adac2b..ab012ec889e 100644 --- a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt +++ b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt @@ -8,3 +8,8 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(flow-gfw-derived-producer + SOURCES flowGfwDerivedProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx new file mode 100644 index 00000000000..1bfed843783 --- /dev/null +++ b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx @@ -0,0 +1,443 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowGfwDerivedProducer.cxx +/// \brief Produce the GFW derived dataset used by flowGfwV02. +/// +/// Keeps sel8 collisions and global tracks that pass the ITS cluster and ITS chi2 +/// cuts. Stores the event mask and the combined track and PID bitmask. PID bits +/// stay unset above the TOF pT cut when the track has no TOF match. Run it after +/// event selection, track selection, multiplicity, centrality, and the TPC/TOF PID +/// helpers (pid-tpc, pid-tof-full, pid-tof-beta). +/// +/// \author Maxim Virta, NBI, maxim.virta@cern.ch + +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float VertexResMax = 0.25f; +constexpr int VertexContribMin = 20; +constexpr float DcaShapeMinPt = 0.001f; +constexpr float DcaShapeMaxPt = 100.f; +constexpr float DcaZReferenceNsigma = 7.f; +constexpr float NsigmaScaleNominal = 1.f; +constexpr float NsigmaScaleTight = 2.f / 3.f; +constexpr float NsigmaScaleLoose = 4.f / 3.f; +constexpr uint32_t NsigmaRows = 6; +constexpr uint32_t NsigmaCols = 3; +constexpr int NsigmaUpPi = 0; +constexpr int NsigmaUpKa = 1; +constexpr int NsigmaUpPr = 2; +constexpr int NsigmaLowPi = 3; +constexpr int NsigmaLowKa = 4; +constexpr int NsigmaLowPr = 5; +constexpr int NsigmaColTpc = 0; +constexpr int NsigmaColTof = 1; +constexpr std::array, NsigmaRows> NsigmaDefault = {{{{1.1f, 2.1f, 3.1f}}, {{1.2f, 2.2f, 3.2f}}, {{1.3f, 2.3f, 3.3f}}, {{-1.1f, -2.1f, -3.1f}}, {{-1.2f, -2.2f, -3.2f}}, {{-1.3f, -2.3f, -3.3f}}}}; +} // namespace + +struct FlowGfwDerivedProducer { + using CollisionsWithInfo = soa::Join; + using TracksWithPid = soa::Join; + + enum CentEstimator : int { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT + }; + + Configurable cfgCutVertex{"cfgCutVertex", 10.f, "Maximum |z| of collisions written to the derived dataset (cm)"}; + Configurable cfgPtMin{"cfgPtMin", 0.2f, "Minimum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgPtMax{"cfgPtMax", 10.f, "Maximum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of tracks written to the derived dataset"}; + Configurable cfgSkipEmptyEvents{"cfgSkipEmptyEvents", true, "Do not write collisions that keep no tracks"}; + Configurable cfgRequireLooseTrackBits{"cfgRequireLooseTrackBits", false, "Keep only tracks that pass the loose DCA and TPC-crossed-rows bits"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 2000, "Maximum track occupancy for the occupancy bit; negative disables the cut"}; + + Configurable cfgDCAxyNSigma{"cfgDCAxyNSigma", 7.f, "Nominal pT-dependent DCAxy nSigma. Non-positive disables the nominal bit"}; + Configurable cfgDCAxyNSigmaTight{"cfgDCAxyNSigmaTight", 5.f, "Tight pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxyNSigmaLoose{"cfgDCAxyNSigmaLoose", 10.f, "Loose pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "1-sigma shape of the pT-dependent DCAxy cut; a bit passes when |DCAxy| <= nSigma*shape(pT)"}; + Configurable cfgDCAzNSigma{"cfgDCAzNSigma", 7.f, "Nominal DCAz nSigma relative to the 7-sigma pT shape. Non-positive disables the nominal bit"}; + Configurable cfgDCAzNSigmaTight{"cfgDCAzNSigmaTight", 5.f, "Tight DCAz nSigma"}; + Configurable cfgDCAzNSigmaLoose{"cfgDCAzNSigmaLoose", 10.f, "Loose DCAz nSigma"}; + Configurable cfgDCAz{"cfgDCAz", "(0.0105+0.0350/(x^1.1))", "7-sigma pT-dependent DCAz shape; a bit passes when |DCAz| <= (nSigma/7)*shape(pT)"}; + Configurable cfgNTPCXrows{"cfgNTPCXrows", 70.f, "Nominal minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsTight{"cfgNTPCXrowsTight", 80.f, "Tight minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsLoose{"cfgNTPCXrowsLoose", 60.f, "Loose minimum number of TPC crossed rows"}; + Configurable cfgNTPCCls{"cfgNTPCCls", 50.f, "Nominal minimum number of found TPC clusters"}; + Configurable cfgNTPCClsTight{"cfgNTPCClsTight", 60.f, "Tight minimum number of found TPC clusters"}; + Configurable cfgNTPCClsLoose{"cfgNTPCClsLoose", 40.f, "Loose minimum number of found TPC clusters"}; + Configurable cfgMinNITSCls{"cfgMinNITSCls", 5.f, "Minimum number of ITS clusters required to keep a track"}; + Configurable cfgChi2PrTPCCls{"cfgChi2PrTPCCls", 2.5f, "Nominal maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrTPCClsLoose{"cfgChi2PrTPCClsLoose", 4.f, "Loose maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrITSCls{"cfgChi2PrITSCls", 36.f, "Maximum chi2 per ITS cluster required to keep a track"}; + + Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.223013, 0.715849, 0.664242, 0.0829653, -0.000503733, 1.21185e-06}, "Global vs PV multiplicity cut parameters"}; + Configurable cfgMultCorrHighCutFunction{"cfgMultCorrHighCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Upper multiplicity-correlation cut"}; + Configurable cfgMultCorrLowCutFunction{"cfgMultCorrLowCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Lower multiplicity-correlation cut"}; + Configurable cfgMultGlobalPVCorrCutFunction{"cfgMultGlobalPVCorrCutFunction", "[0] + [1]*x + 3*([2] + [3]*x + [4]*x*x + [5]*x*x*x)", "Global vs PV multiplicity-correlation cut"}; + Configurable cfgTofPtCut{"cfgTofPtCut", 0.5f, "Above this pT, PID bits stay unset when the track has no TOF match"}; + Configurable> nSigmas{"nSigmas", {NsigmaDefault.front().data(), NsigmaRows, NsigmaCols, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Nominal nSigma windows for TPC, TOF, and ITS. ITS is accepted so the analysis JSON can be reused and is not stored as a bit"}; + Configurable cfgNsigmaScaleTight{"cfgNsigmaScaleTight", static_cast(NsigmaScaleTight), "Scale applied to the nominal nSigma bounds for the tight PID bits"}; + Configurable cfgNsigmaScaleLoose{"cfgNsigmaScaleLoose", static_cast(NsigmaScaleLoose), "Scale applied to the nominal nSigma bounds for the loose PID bits"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (aod::track::pt > cfgPtMin) && (aod::track::pt < cfgPtMax) && (nabs(aod::track::eta) < cfgEtaMax); + + Produces outputCollisions; + Produces outputTracks; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::unique_ptr fDcaXyShape; + std::unique_ptr fDcaZShape; + std::array tpcNsigmaCut{}; + std::array tofNsigmaCut{}; + std::unique_ptr fMultPVCutLow; + std::unique_ptr fMultPVCutHigh; + std::unique_ptr fMultCutLow; + std::unique_ptr fMultCutHigh; + std::unique_ptr fMultPVGlobalCutHigh; + + void init(InitContext const&) + { + const AxisSpec axisCounter{1, 0., 1., ""}; + registry.add("events", "Written derived collisions", HistType::kTH1F, {axisCounter}); + registry.add("tracks", "Written derived tracks", HistType::kTH1F, {axisCounter}); + + const std::string dcaXyFormula = "[0]*" + cfgDCAxy.value; + fDcaXyShape = std::make_unique("gfwDerivedDcaXy", dcaXyFormula.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + fDcaXyShape->SetParameter(0, 1.f); + fDcaZShape = std::make_unique("gfwDerivedDcaZ", cfgDCAz.value.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + + const auto nsigma = nSigmas->getData(); + for (uint32_t row = 0; row < NsigmaRows; ++row) { + tpcNsigmaCut[row] = nsigma(row, NsigmaColTpc); + tofNsigmaCut[row] = nsigma(row, NsigmaColTof); + } + + constexpr double MultCorrMaxCent = 100.; + constexpr double MultCorrMaxNch = 3000.; + fMultPVCutLow = std::make_unique("gfwDerivedMultPVCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutLow->SetParameters(cfgMultPVCutPars.value.data()); + fMultPVCutHigh = std::make_unique("gfwDerivedMultPVCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutHigh->SetParameters(cfgMultPVCutPars.value.data()); + fMultCutLow = std::make_unique("gfwDerivedMultCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutLow->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultCutHigh = std::make_unique("gfwDerivedMultCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutHigh->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultPVGlobalCutHigh = std::make_unique("gfwDerivedMultPVGlobalCutHigh", cfgMultGlobalPVCorrCutFunction.value.c_str(), 0., MultCorrMaxNch); + fMultPVGlobalCutHigh->SetParameters(cfgMultGlobalPVCutPars.value.data()); + } + + template + float centrality(TCollision const& collision) const + { + switch (cfgCentEstimator.value) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + + template + bool vertexQuality(TCollision const& collision) const + { + if (collision.numContrib() <= 1) { + return false; + } + const float zRes = std::sqrt(collision.covZZ()); + return !(zRes > VertexResMax && collision.numContrib() < VertexContribMin); + } + + template + bool multiplicityCorrelated(TCollision const& collision, float cent, float multTrk) const + { + const float multPV = collision.multNTracksPV(); + if (multPV < fMultPVCutLow->Eval(cent) || multPV > fMultPVCutHigh->Eval(cent)) { + return false; + } + if (multTrk < fMultCutLow->Eval(cent) || multTrk > fMultCutHigh->Eval(cent)) { + return false; + } + return multTrk <= fMultPVGlobalCutHigh->Eval(multPV); + } + + template + uint16_t packEventSel(TCollision const& collision, float cent, float multTrk) const + { + uint16_t bits = 0; + if (collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + bits |= aod::gfwcollision::kNoSameBunchPileup; + } + if (collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + bits |= aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + bits |= aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (collision.selection_bit(aod::evsel::kNoCollInRofStandard)) { + bits |= aod::gfwcollision::kNoCollInRofStandard; + } + if (collision.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) { + bits |= aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + bits |= aod::gfwcollision::kIsVertexITSTPC; + } + if (collision.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + bits |= aod::gfwcollision::kIsGoodITSLayersAll; + } + if (collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + bits |= aod::gfwcollision::kNoTimeFrameBorder; + } + if (collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + bits |= aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgOccupancyMax.value < 0 || collision.trackOccupancyInTimeRange() <= cfgOccupancyMax.value) { + bits |= aod::gfwcollision::kOccupancy; + } + if (!collision.alias_bit(kTVXinTRD)) { + bits |= aod::gfwcollision::kNotTVXinTRD; + } + if (vertexQuality(collision)) { + bits |= aod::gfwcollision::kGoodVertex; + } + if (multiplicityCorrelated(collision, cent, multTrk)) { + bits |= aod::gfwcollision::kMultCorr; + } + return bits; + } + + template + bool withinDcaXy(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaXY()) <= nSigma * fDcaXyShape->Eval(track.pt()); + } + + template + bool withinDcaZ(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaZ()) <= (nSigma / DcaZReferenceNsigma) * fDcaZShape->Eval(track.pt()); + } + + template + bool passProducerTrackCuts(TTrack const& track) const + { + const bool global = track.isGlobalTrack() || track.isGlobalTrackSDD() != 0; + return global && track.itsNCls() >= cfgMinNITSCls.value && track.itsChi2NCl() <= cfgChi2PrITSCls.value; + } + + template + uint32_t packTrackSel(TTrack const& track) const + { + uint32_t bits = 0; + if (cfgDCAxyNSigma.value <= 0.f || withinDcaXy(track, cfgDCAxyNSigma.value)) { + bits |= aod::gfwtrack::kDCAxy; + } + if (withinDcaXy(track, cfgDCAxyNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAxyTight; + } + if (withinDcaXy(track, cfgDCAxyNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAxyLoose; + } + if (cfgDCAzNSigma.value <= 0.f || withinDcaZ(track, cfgDCAzNSigma.value)) { + bits |= aod::gfwtrack::kDCAz; + } + if (withinDcaZ(track, cfgDCAzNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAzTight; + } + if (withinDcaZ(track, cfgDCAzNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAzLoose; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrows.value) { + bits |= aod::gfwtrack::kTPCXrows; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsTight.value) { + bits |= aod::gfwtrack::kTPCXrowsTight; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsLoose.value) { + bits |= aod::gfwtrack::kTPCXrowsLoose; + } + if (track.tpcNClsFound() >= cfgNTPCCls.value) { + bits |= aod::gfwtrack::kTPCCls; + } + if (track.tpcNClsFound() >= cfgNTPCClsTight.value) { + bits |= aod::gfwtrack::kTPCClsTight; + } + if (track.tpcNClsFound() >= cfgNTPCClsLoose.value) { + bits |= aod::gfwtrack::kTPCClsLoose; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCCls.value) { + bits |= aod::gfwtrack::kChi2TPC; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCClsLoose.value) { + bits |= aod::gfwtrack::kChi2TPCLoose; + } + return bits; + } + + static bool insideNsigma(float nsigma, float up, float low, float scale) + { + return nsigma < up * scale && nsigma > low * scale; + } + + template + uint32_t packPidWindow(TTrack const& track, float scale) const + { + if (track.pt() > cfgTofPtCut.value && !track.hasTOF()) { + return 0; + } + uint32_t bits = 0; + if (insideNsigma(track.tpcNSigmaPi(), tpcNsigmaCut[NsigmaUpPi], tpcNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTpcPi; + } + if (insideNsigma(track.tpcNSigmaKa(), tpcNsigmaCut[NsigmaUpKa], tpcNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTpcKa; + } + if (insideNsigma(track.tpcNSigmaPr(), tpcNsigmaCut[NsigmaUpPr], tpcNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTpcPr; + } + if (insideNsigma(track.tofNSigmaPi(), tofNsigmaCut[NsigmaUpPi], tofNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTofPi; + } + if (insideNsigma(track.tofNSigmaKa(), tofNsigmaCut[NsigmaUpKa], tofNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTofKa; + } + if (insideNsigma(track.tofNSigmaPr(), tofNsigmaCut[NsigmaUpPr], tofNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTofPr; + } + return bits; + } + + template + uint32_t packPidSel(TTrack const& track) const + { + const uint32_t nominal = packPidWindow(track, NsigmaScaleNominal); + const uint32_t tight = packPidWindow(track, cfgNsigmaScaleTight.value); + const uint32_t loose = packPidWindow(track, cfgNsigmaScaleLoose.value); + // Nominal TPC bits start at bit 14 and nominal TOF bits at bit 23. Shifting by one window moves each detector into its tight or loose block. + return nominal | (tight << (aod::gfwtrack::PidWindowTight * aod::gfwtrack::PidWindowBits)) | (loose << (aod::gfwtrack::PidWindowLoose * aod::gfwtrack::PidWindowBits)); + } + + template + void fillTrack(TTrack const& track, uint32_t bits) + { + outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), + track.hasTOF(), bits | packPidSel(track)); + registry.fill(HIST("tracks"), 0.5); + } + + void process(soa::Filtered::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) + { + if (!collision.sel8()) { + return; + } + + int nKept = 0; + int nForMultCorr = 0; + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + ++nKept; + if ((bits & aod::gfwtrack::MultCorrTrackMask) == aod::gfwtrack::MultCorrTrackMask) { + ++nForMultCorr; + } + } + if (nKept == 0 && cfgSkipEmptyEvents) { + return; + } + + const auto bc = collision.bc_as(); + const float cent = centrality(collision); + outputCollisions(bc.runNumber(), collision.posZ(), cent, static_cast(collision.multNTracksGlobal()), bc.timestamp(), packEventSel(collision, cent, static_cast(nForMultCorr))); + registry.fill(HIST("events"), 0.5); + + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + fillTrack(track, bits); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx index 5768bb877f7..327372e0bc3 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx @@ -17,6 +17,7 @@ #include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" @@ -213,8 +214,14 @@ struct FlowGfwV02 { // // Filters to be applied to the received data. // // The analysis assumes the data has been subjected to a QA of its selection, // // and thus only the final distributions of the data for analysis are saved. + // Derived-data bit masks. Every listed track bit is required; see gfwcollision::EventSel and gfwtrack::TrackSelection. + O2_DEFINE_CONFIGURABLE(cfgEventBitMask, uint16_t, static_cast(o2::aod::gfwcollision::DefaultEventMask), "Required event-selection bits for GFW derived data"); + Configurable> cfgBitmaskSelections{"cfgBitmaskSelections", {0, 3, 6, 9, 12}, "Enabled TrackSelection bit indices for GFW derived data. DCA 0-5, TPC track 6-13, TPC PID 14-22, TOF PID 23-31. The default is the nominal quality selection"}; + O2_DEFINE_CONFIGURABLE(cfgPidWindow, int, static_cast(o2::aod::gfwtrack::PidWindowNominal), "PID nSigma window for GFW derived data: 0 nominal, 1 tight, 2 loose"); + o2::framework::expressions::Filter collFilter = (nabs(aod::collision::posZ) < cfgEventCuts.cfgZvtxMax); o2::framework::expressions::Filter trackFilter = (aod::track::pt > cfgTrackCuts.cfgPtMin) && (aod::track::pt < cfgTrackCuts.cfgPtMax) && nabs(aod::track::eta) < cfgTrackCuts.cfgEtaMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; + o2::framework::expressions::Filter gfwTrackFilter = (aod::gfwtrack::pt > cfgTrackCuts.cfgPtMin) && (aod::gfwtrack::pt < cfgTrackCuts.cfgPtMax) && (nabs(aod::gfwtrack::eta) < cfgTrackCuts.cfgEtaMax); // Connect to ccdb Service ccdb{}; @@ -292,6 +299,7 @@ struct FlowGfwV02 { std::unique_ptr fMultPVGlobalCutHigh; std::unique_ptr fPtDepDCAxy; + uint32_t trackBitMask = o2::aod::gfwtrack::NominalMask; using GFWTracks = soa::Filtered>; @@ -313,6 +321,12 @@ struct FlowGfwV02 { void init(InitContext const&) { + trackBitMask = 0; + for (const int bit : cfgBitmaskSelections.value) { + if (bit >= 0 && bit < 32) { + trackBitMask |= static_cast(1u << bit); + } + } projectMatrix(nSigmas->getData(), pidStates.tpcNsigmaCut, pidStates.tofNsigmaCut, pidStates.itsNsigmaCut); projectMatrix(nSigmasHighPurity->getData(), pidStates.highPurityTpcNsigmaCut, pidStates.highPurityTofNsigmaCut, pidStates.highPurityItsNsigmaCut); @@ -605,50 +619,78 @@ struct FlowGfwV02 { template int getNsigmaPID(TTrack const& track) { - // Computing Nsigma arrays for pion, kaon, and protons - std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; - std::array nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - if (cfgUseHighPurityPID && track.pt() > cfgHighPurityPtMin) { - bool isHighPurityPion = nSigmaTPC[IndPionUp] < pidStates.highPurityTpcNsigmaCut[IndPionUp] && nSigmaTPC[IndPionUp] > pidStates.highPurityTpcNsigmaCut[IndPionLow] && nSigmaTOF[IndPionUp] < pidStates.highPurityTofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.highPurityTofNsigmaCut[IndPionLow]; - bool isHighPurityKaon = nSigmaTPC[IndKaonUp] < pidStates.highPurityTpcNsigmaCut[IndKaonUp] && nSigmaTPC[IndKaonUp] > pidStates.highPurityTpcNsigmaCut[IndKaonLow] && nSigmaTOF[IndKaonUp] < pidStates.highPurityTofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.highPurityTofNsigmaCut[IndKaonLow]; - bool isHighPurityProton = nSigmaTPC[IndProtonUp] < pidStates.highPurityTpcNsigmaCut[IndProtonUp] && nSigmaTPC[IndProtonUp] > pidStates.highPurityTpcNsigmaCut[IndProtonLow] && nSigmaTOF[IndProtonUp] < pidStates.highPurityTofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.highPurityTofNsigmaCut[IndProtonLow]; - if (isHighPurityPion) { - pid = PidPions; - } else if (isHighPurityKaon) { - pid = PidKaons; - } else if (isHighPurityProton) { - pid = PidProtons; - } else { + bool isPion = false, isKaon = false, isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + if constexpr (requires { track.trackSel(); }) { + const int window = cfgPidWindow; + const int shift = (window == aod::gfwtrack::PidWindowTight || window == aod::gfwtrack::PidWindowLoose) ? window * aod::gfwtrack::PidWindowBits : aod::gfwtrack::PidWindowNominal; + const uint32_t trackSel = track.trackSel(); + isDetectedPion = (trackSel & (static_cast(aod::gfwtrack::kTpcPi) << shift)) != 0; + isDetectedKaon = (trackSel & (static_cast(aod::gfwtrack::kTpcKa) << shift)) != 0; + isDetectedProton = (trackSel & (static_cast(aod::gfwtrack::kTpcPr) << shift)) != 0; + isTofPion = (trackSel & (static_cast(aod::gfwtrack::kTofPi) << shift)) != 0; + isTofKaon = (trackSel & (static_cast(aod::gfwtrack::kTofKa) << shift)) != 0; + isTofProton = (trackSel & (static_cast(aod::gfwtrack::kTofPr) << shift)) != 0; + } else if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + const std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + const std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + constexpr bool HasItsResponse = requires { + track.itsClusterSizes(); + track.p(); + }; + if (cfgUseHighPurityPID && track.pt() > cfgHighPurityPtMin) { + const bool isHighPurityPion = nSigmaTPC[IndPionUp] < pidStates.highPurityTpcNsigmaCut[IndPionUp] && nSigmaTPC[IndPionUp] > pidStates.highPurityTpcNsigmaCut[IndPionLow] && nSigmaTOF[IndPionUp] < pidStates.highPurityTofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.highPurityTofNsigmaCut[IndPionLow]; + const bool isHighPurityKaon = nSigmaTPC[IndKaonUp] < pidStates.highPurityTpcNsigmaCut[IndKaonUp] && nSigmaTPC[IndKaonUp] > pidStates.highPurityTpcNsigmaCut[IndKaonLow] && nSigmaTOF[IndKaonUp] < pidStates.highPurityTofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.highPurityTofNsigmaCut[IndKaonLow]; + const bool isHighPurityProton = nSigmaTPC[IndProtonUp] < pidStates.highPurityTpcNsigmaCut[IndProtonUp] && nSigmaTPC[IndProtonUp] > pidStates.highPurityTpcNsigmaCut[IndProtonLow] && nSigmaTOF[IndProtonUp] < pidStates.highPurityTofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.highPurityTofNsigmaCut[IndProtonLow]; + if (isHighPurityPion) { + return PidPions; + } + if (isHighPurityKaon) { + return PidKaons; + } + if (isHighPurityProton) { + return PidProtons; + } return -1; } - return pid; - } - - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - - bool isPion = false, isKaon = false, isProton = false; - bool isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; - bool isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; - bool isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; - - bool isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; - bool isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; - bool isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + std::array nSigmaITS{}; + if constexpr (HasItsResponse) { + nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; + } + const bool useItsPid = cfgUseItsPID && HasItsResponse; + const std::array nSigmaToUse = useItsPid ? nSigmaITS : nSigmaTPC; + const std::array detectorNsigmaCut = useItsPid ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; + isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; + isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; + isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; + isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; + isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; + isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; + } else { return -1; } - if (track.pt() > cfgTofPtCut && track.hasTOF()) { - isPion = isTofPion && isDetectedPion; - isKaon = isTofKaon && isDetectedKaon; - isProton = isTofProton && isDetectedProton; + + if constexpr (requires { track.hasTOF(); }) { + if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } } else { - isPion = isDetectedPion; - isKaon = isDetectedKaon; - isProton = isDetectedProton; + return -1; } if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { @@ -712,12 +754,15 @@ struct FlowGfwV02 { cfg.correctionsLoaded = true; } + // JWeights, filled by jflucWeightsLoader from a local file or CCDB, stores the same + // correction used by flowJSPCAnalysis: acceptance is 1/weightNUA and efficiency is weightEff. template double getJTrackAcceptance(TTrack const& track) { double wacc = 1; if constexpr (requires { track.weightNUA(); }) { - wacc = 1. / track.weightNUA(); + const double nua = track.weightNUA(); + wacc = (nua == 0.) ? 1. : 1. / nua; } return wacc; } @@ -735,24 +780,36 @@ struct FlowGfwV02 { template double getAcceptance(TTrack const& track, const double& vtxz) { - double wacc = 1; - if (cfg.mAcceptance) { - wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + if constexpr (requires { track.weightNUA(); }) { + return getJTrackAcceptance(track); + } else { + double wacc = 1; + if (cfg.mAcceptance) { + wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + } + return wacc; } - return wacc; } template double getEfficiency(TTrack const& track, const int& pid = PidCharged) { - double eff = 1.; - if (cfg.mEfficiency[pid]) { - eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); - } - if (eff == 0) { - return -1.; + if constexpr (requires { track.weightEff(); }) { + const double eff = getJTrackEfficiency(track); + if (eff == 0.) { + return -1.; + } + return eff; + } else { + double eff = 1.; + if (cfg.mEfficiency[pid]) { + eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); + } + if (eff == 0) { + return -1.; + } + return 1. / eff; } - return 1. / eff; } template @@ -904,78 +961,83 @@ struct FlowGfwV02 { double v22npt = 0; double ptMeanMid = pidStates.hPtMid[PidCharged]->GetMean(); double ptFractionMid = 0.; - double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB - auto valAB = fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB; + double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB double dnxABAB = fGFW->Calculate(corrconfigs.at(1), 0, kTRUE).real(); // V22 weight for ABAB - auto valABAB = fGFW->Calculate(corrconfigs.at(1), 0, kFALSE).real() / dnxABAB; - if (std::abs(valAB) > Threshold || std::isnan(valAB) || std::isinf(valAB) || dnxAB < MinDnxAB || std::abs(valABAB) > Threshold || std::isnan(valABAB) || std::isinf(valABAB) || dnxABAB < MinDnxAB) { - return; - } - double v22pt = valAB * ptMeanMid; - double weightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; - double weightABAB = (cfgUseMultiplicityFlowWeights) ? dnxABAB : 1.0; - double weightC = 1.0; - // Calculate V02 for each particle type - for (int pid = 0; pid < PidTotal; pid++) { - int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; - if (!(pidStates.hPtMid[normIndex]->Integral() > 0)) { - continue; // Mid pT distribution is not defined - } - if (cfgUseMultiplicityFracWeights) { - weightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight - } - for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { - ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); - v22npt = valAB * ptFractionMid; - switch (pid) { - case PidCharged: - registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); - registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); - } - break; - case PidPions: - registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); - registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); - } - break; - case PidKaons: - registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); - registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); - } - break; - case PidProtons: - registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); - registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); - } - break; - default: - break; + // Check if the V22 weight is valid + const bool validAB = dnxAB >= MinDnxAB; + const bool validABAB = dnxABAB >= MinDnxAB; + const double valAB = validAB ? fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB : 0.; + const double valABAB = validABAB ? fGFW->Calculate(corrconfigs.at(1), 0, kFALSE).real() / dnxABAB : 0.; + const bool acceptAB = validAB && std::isfinite(valAB) && std::abs(valAB) <= Threshold; + const bool acceptABAB = validABAB && std::isfinite(valABAB) && std::abs(valABAB) <= Threshold; + if (acceptAB && acceptABAB) { + double v22pt = valAB * ptMeanMid; + double weightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; + double weightABAB = (cfgUseMultiplicityFlowWeights) ? dnxABAB : 1.0; + double weightC = 1.0; + // Calculate V02 for each particle type + for (int pid = 0; pid < PidTotal; pid++) { + int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; + if (!(pidStates.hPtMid[normIndex]->Integral() > 0)) { + continue; // Mid pT distribution is not defined + } + if (cfgUseMultiplicityFracWeights) { + weightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight + } + for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { + ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); + v22npt = valAB * ptFractionMid; + + switch (pid) { + case PidCharged: + registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidPions: + registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidKaons: + registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidProtons: + registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + default: + break; + } } } - } - // Calculate NCv22pt - if (cfgUseMultiplicityFracWeights) { - weightC = pidStates.hPtMid[PidCharged]->Integral(); - } + // Calculate NCv22pt + if (cfgUseMultiplicityFracWeights) { + weightC = pidStates.hPtMid[PidCharged]->Integral(); + } - registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, weightC * weightAB); - registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, weightAB); - registry.fill(HIST("analysis/charged/v24"), bootstrap, centmult, valABAB, weightABAB); - registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, weightC); - if (cfgUseMultiplicityFracWeights) { - registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, weightC * weightAB); - registry.fill(HIST("analysis/charged/v224pc"), bootstrap, centmult, valAB, weightABAB); - registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, weightC * weightAB); + registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, weightC * weightAB); + registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, weightAB); + registry.fill(HIST("analysis/charged/v24"), bootstrap, centmult, valABAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, weightC * weightAB); + registry.fill(HIST("analysis/charged/v224pc"), bootstrap, centmult, valAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, weightC * weightAB); + } } } // End of V02 @@ -1186,10 +1248,17 @@ struct FlowGfwV02 { template bool trackSelected(TTrack const& track) { - if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { - return false; + if constexpr (requires { track.trackSel(); }) { + const uint32_t mask = trackBitMask; + return (track.trackSel() & mask) == mask; + } else if constexpr (requires { track.dcaXY(); track.tpcNClsCrossedRows(); track.tpcNClsFound(); track.itsNCls(); }) { + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { + return false; + } + return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); + } else { + return true; } - return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); } template @@ -1279,65 +1348,55 @@ struct FlowGfwV02 { } template - inline void fillPidQA(TTrack const& track, const int& pid) + void fillPidDedxQA(TTrack const& track, const int& pid) { - // Fill Nsigma QA - if (!cfgUseItsPID) { - if (ft == kBefore) { - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + if constexpr (requires { track.tpcSignal(); track.tpcExpSigmaPi(); track.tpcExpSigmaKa(); track.tpcExpSigmaPr(); }) { + if (!cfgGetdEdx) { + return; + } + if (ft == kBefore || pid == PidPions) { + const double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); + } + if (ft == kBefore || pid == PidKaons) { + const double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); + } + if (ft == kBefore || pid == PidProtons) { + const double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } + } + } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); - } - } else if (ft == kAfter) { - if (pid == PidPions) { + template + inline void fillPidQA(TTrack const& track, const int& pid) + { + // Fill Nsigma QA. The derived tables store PID bits and have no nSigma columns. + if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + if (!cfgUseItsPID) { + if (ft == kBefore) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - } - if (pid == PidKaons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - } - if (pid == PidProtons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } else if (ft == kAfter) { + if (pid == PidPions) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); + } + if (pid == PidKaons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); + } + if (pid == PidProtons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } } + fillPidDedxQA(track, pid); } } } @@ -1348,13 +1407,17 @@ struct FlowGfwV02 { double wacc = getAcceptance(track, vtxz); registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == kAfter) ? wacc : 1.0); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + if constexpr (requires { track.dcaXY(); track.dcaZ(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + } - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + if constexpr (requires { track.tpcChi2NCl(); track.itsChi2NCl(); track.tpcNClsFound(); track.itsNCls(); track.tpcNClsCrossedRows(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + } if (ft == kAfter) { registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); @@ -1444,9 +1507,56 @@ struct FlowGfwV02 { const XAxis xaxis{.centrality = collision.multiplicity(), .multiplicity = tracks.size()}; registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); - // processCollision(collision, tracks, xaxis); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process CF derived data with JWeights acceptance and efficiency corrections", false); + + void processDerived(aod::GfwCollision const& collision, soa::Filtered const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + loadCorrections(run); + + const uint16_t eventMask = cfgEventBitMask; + if ((collision.eventSel() & eventMask) != eventMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processDerived, "Process analysis for GFW derived data", false); + + void processDerivedCorrected(aod::GfwCollision const& collision, soa::Filtered> const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + + const uint16_t eventMask = cfgEventBitMask; + if ((collision.eventSel() & eventMask) != eventMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); } - PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process analysis for CF derived data with corrections", false); + PROCESS_SWITCH(FlowGfwV02, processDerivedCorrected, "Process GFW derived data with JWeights acceptance and efficiency corrections", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx index 02088eab148..bbc5d528faf 100644 --- a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx +++ b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx @@ -16,6 +16,7 @@ /// The weights are loaded from the local files or CCDB and stored in the JWeights table. #include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include @@ -116,14 +117,16 @@ struct JflucWeightsLoader { void init(InitContext const&) { - if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessJNUACreatorCFDerived) { + if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessLoadWeightsGFW && !doprocessJNUACreatorCFDerived) { return; } - if (doprocessLoadWeights && doprocessLoadWeightsCF) - LOGF(fatal, "Only one of JTracks or CFTracks processing can be enabled at a time."); + const int nWeightProcesses = static_cast(doprocessLoadWeights) + static_cast(doprocessLoadWeightsCF) + static_cast(doprocessLoadWeightsGFW); + if (nWeightProcesses > 1) { + LOGF(fatal, "Only one of JTracks, CFTracks, or GFWTracks weight loading can be enabled at a time."); + } - if (doprocessLoadWeights || doprocessLoadWeightsCF) { + if (doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW) { // NUA corrections from local file or CCDB if (cfgPathPhiWeights.value.substr(0, 8) == "local://") { LOGF(info, "Using NUA corrections locally from: %s", cfgPathPhiWeights.value.substr(8).c_str()); @@ -172,7 +175,7 @@ struct JflucWeightsLoader { ccdb->setFatalWhenNull(false); } } - } // doprocessLoadWeights || doprocessLoadWeightsCF + } // doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW if (doprocessJNUACreatorCFDerived) { qaHistRegistry.add("trackType", "trackType;trackType;counts", HistType::kTH1F, {{65, -0.5, 64.5}}); @@ -276,6 +279,12 @@ struct JflucWeightsLoader { } PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsCF, "Load weights histograms for CF derived data table", true); + void processLoadWeightsGFW(aod::GfwCollision const& collision, aod::GfwTracks const& tracks) + { + loadWeights(output, collision, tracks); + } + PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsGFW, "Load weights histograms for GFW derived data table", false); + Produces output2p; void processLoadWeightsCF2Prong(aod::CFCollision const& collision, aod::CF2ProngTracks const& tracks2p) { From 052b78fe23a886b9ef2d127669b94368a6255115 Mon Sep 17 00:00:00 2001 From: sarjeetagami <162087855+sarjeetagami@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:03:18 +0530 Subject: [PATCH 477/648] [PWGLF] update the efficiency in kstarpbpb task (#18501) Co-authored-by: sarjeeta gami --- PWGLF/Tasks/Resonances/kstarpbpb.cxx | 84 ++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index ab2c7fb4d4d..46c1a5ba640 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -195,7 +195,7 @@ struct Kstarpbpb { std::string prefix = "closure"; Configurable injectPolarization{"injectPolarization", false, "Reweight the true K*/phi with W(cos theta*) so that the sample has rho00 = rho00Inj"}; Configurable rho00Inj{"rho00Inj", 1.0f / 3.0f, "Injected rho00, in [0,1] (1/3 = unpolarized)"}; - Configurable useTrueEP{"useTrueEP", true, "Use the MC true event plane angle as Psi (false: Psi = 0, fixed axis as in processMC)"}; + Configurable useTrueEP{"useTrueEP", true, "Use the MC true event plane angle as Psi (false: Psi = 0, fixed axis as in processMC with mcEffEP.psiSource = 0)"}; Configurable splitSample{"splitSample", true, "Even MC events build Acc x Eff, odd MC events are the test sample (false: same events for both)"}; Configurable fillRotBkg{"fillRotBkg", false, "Fill the rotated-pair background (weight 1), cross-check of the ME background"}; Configurable genAcceptanceCut{"genAcceptanceCut", false, "Daughter pT/eta cut on the generated K*/phi (false: denominator is the full |y| < cut sample, so the correction is the full Acc x Eff in cos theta*)"}; @@ -203,6 +203,19 @@ struct Kstarpbpb { Configurable meRequireSingleReco{"meRequireSingleReco", true, "ME: both events must come from an MC collision with exactly one reco collision, as required in the SE (processMCClosure / processMCClosurePhi)"}; ConfigurableAxis axisCosThetaStar{"axisCosThetaStar", {10, -1.0, 1.0}, "cos(#vartheta*)"}; } closure; + + // Psi of the EP-frame MC efficiency (processMC, processMCPhi). In data the axis follows Psi_FT0C, which is flat in the lab; + // with a fixed Psi = 0 the axis is fixed in the lab, so azimuthal holes of the detector enter Acc x Eff(cos theta*). + // psiSource = 1 follows the thesis (S. Kundu, App. B.10.4): Psi2 from the generated tracks in the forward EP-detector acceptance + // (V0C there, FT0C here), one Psi per MC event, for both the generated and the reconstructed K*/phi. + struct : ConfigurableGroup { + std::string prefix = "mcEffEP"; + Configurable psiSource{"psiSource", 1, "Psi of the EP-frame MC efficiency: 0 = fixed Psi = 0 (old: axis -y for K*, in-plane x for phi), 1 = Psi2 from generated charged primaries in [etaMin, etaMax] (thesis), 2 = MC true event plane (mcCollision.eventPlaneAngle())"}; + Configurable etaMin{"etaMin", -3.3f, "eta min of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable etaMax{"etaMax", -2.1f, "eta max of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable fillPhiVMinusPsi{"fillPhiVMinusPsi", false, "Fill gen/rec Acc x Eff maps vs pT, cos theta* and phi_V - Psi (thesis Fig. B.69 / Table B.1 check)"}; + ConfigurableAxis axisPhiVMinusPsi{"axisPhiVMinusPsi", {6, 0.0, o2::constants::math::PI}, "#varphi_{V} - #Psi"}; + } mcEffEP; Configurable cfgMinTrackPt{"cfgMinTrackPt", 0.15f, "Minimum track pT"}; @@ -321,6 +334,12 @@ struct Kstarpbpb { histos.add("hSparseKstarMCGenCosThetaStar_effy", "hSparseKstarMCGenCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); histos.add("hSparseKstarMCRecSA", "hSparseKstarMCRecSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {configThnAxisCentrality, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("hSparseKstarMCGenCosThetaStarPhiVPsi", "Generated K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecCosThetaStarPhiVPsi", "Reconstructed K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + } } if (doClosureTest) { @@ -463,6 +482,12 @@ struct Kstarpbpb { histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {thnAxisCentralityPhi, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("phi/hSparseV2MCGenCosThetaStarPhiVPsi", "Generated #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStarPhiVPsi", "Reconstructed #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + } } } @@ -881,14 +906,47 @@ struct Kstarpbpb { } // phipbpb.cxx builds the EP-frame MC efficiency with the in-plane vector at Psi = 0 (x axis); kept for identical results + // with mcEffEP.psiSource = 0 only. With an event-by-event Psi the axis must be the plane normal, as in data (getSAAxis). ROOT::Math::XYZVector getSAAxisPhiMC(const ROOT::Math::PxPyPzMVector& mother, double psiSA) { - if (cfgSAFrame.value == kEventPlane) { + if (cfgSAFrame.value == kEventPlane && mcEffEP.psiSource == 0) { return {std::cos(2.0 * psiSA), std::sin(2.0 * psiSA), 0.}; } return getSAAxis(mother, psiSA); } + // Psi2 = atan2(Q2y, Q2x) / 2 from the generated charged physical primaries in [etaMin, etaMax] (thesis Eq. B.16, unit weights) + template + double getGenPsi2(TParticles const& particles) + { + double qx = 0., qy = 0.; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || particle.eta() < mcEffEP.etaMin || particle.eta() > mcEffEP.etaMax) { + continue; + } + const int absPdg = std::abs(particle.pdgCode()); + if (absPdg != PDG_t::kPiPlus && absPdg != PDG_t::kKPlus && absPdg != PDG_t::kProton && absPdg != PDG_t::kElectron && absPdg != PDG_t::kMuonMinus) { + continue; + } + qx += std::cos(2.0 * particle.phi()); + qy += std::sin(2.0 * particle.phi()); + } + return 0.5 * std::atan2(qy, qx); + } + + // Psi of the EP-frame MC efficiency, one per MC event (see mcEffEP) + template + double getMCEffPsi(TMcCollision const& mcCollision, TParticles const& particles) + { + if (mcEffEP.psiSource == 1) { + return getGenPsi2(particles); + } + if (mcEffEP.psiSource == 2) { + return mcCollision.eventPlaneAngle(); + } + return 0.0; + } + template bool isFakeKaon(T const& track, int /*PID*/) { @@ -1258,7 +1316,7 @@ struct Kstarpbpb { } PROCESS_SWITCH(Kstarpbpb, processMixedEvent, "Process Mixed event", true); - void processMC(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMC(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { // scratch vectors: local to this call, never carry state across particles ROOT::Math::PxPyPzMVector kstarMother, kaonPlus, pionMinus; @@ -1274,7 +1332,7 @@ struct Kstarpbpb { return; } for (const auto& RecCollision : RecCollisions) { - auto psiFT0C = 0.0; + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) histos.fill(HIST("hMC"), 3); if (!RecCollision.sel8()) { histos.fill(HIST("hMC"), 4); @@ -1292,6 +1350,7 @@ struct Kstarpbpb { auto centrality = RecCollision.centFT0C(); histos.fill(HIST("CentPercentileMCRecHist"), centrality); auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("hMCEffPsi"), centrality, psiSA); auto oldindex = -999; auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle @@ -1399,6 +1458,9 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCRecSA"), kstarMother.M(), kstarMother.Pt(), saRec, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCRecCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCRecCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarRec, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } if (doClosureTest) { histos.fill(HIST("closure/KstarMCRecTrueCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); } @@ -1471,6 +1533,9 @@ struct Kstarpbpb { histos.fill(HIST("hSparseKstarMCGenSA"), kstarMother.M(), kstarMother.Pt(), saGen, std::abs(kstarMother.Rapidity()), centrality); histos.fill(HIST("hSparseKstarMCGenCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCGenCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarGen, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } if (doClosureTest) { histos.fill(HIST("closure/KstarMCGenCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); } @@ -1826,7 +1891,7 @@ struct Kstarpbpb { } PROCESS_SWITCH(Kstarpbpb, processMEPhi, "Process mixed event phi(1020) spin alignment", false); - void processMCPhi(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMCPhi(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { histos.fill(HIST("phi/hMC"), 0); if (RecCollisions.size() == 0) { @@ -1839,7 +1904,7 @@ struct Kstarpbpb { } ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; for (const auto& RecCollision : RecCollisions) { - auto psiFT0C = 0.0; + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) histos.fill(HIST("phi/hMC"), 3); if (!RecCollision.sel8()) { histos.fill(HIST("phi/hMC"), 4); @@ -1857,6 +1922,7 @@ struct Kstarpbpb { auto centrality = RecCollision.centFT0C(); histos.fill(HIST("phi/CentPercentileMCRecHist"), centrality); auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("phi/hMCEffPsi"), centrality, psiSA); auto oldindex = -999; auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle @@ -1932,6 +1998,9 @@ struct Kstarpbpb { } auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCRecCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } if (doClosureTest) { histos.fill(HIST("closure/PhiMCRecTrueCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); } @@ -1972,6 +2041,9 @@ struct Kstarpbpb { phiMother = kaonPlusPhi + kaonMinusPhi; auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); histos.fill(HIST("phi/hSparseV2MCGenCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCGenCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } if (doClosureTest) { histos.fill(HIST("closure/PhiMCGenCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); } From f63892475896785efd88ef03007dd5c10e76c2d4 Mon Sep 17 00:00:00 2001 From: Junlee Kim Date: Thu, 29 Oct 2026 20:33:38 +0900 Subject: [PATCH 478/648] [PWGLF] cha01710analysis.cxx: adding ITS PID (#18499) --- PWGLF/Tasks/Resonances/cha01710analysis.cxx | 31 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx index 52900b00422..ebda6c3e0e5 100644 --- a/PWGLF/Tasks/Resonances/cha01710analysis.cxx +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -19,6 +19,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -37,6 +38,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -70,6 +72,8 @@ struct Cha01710analysis { Service ccdb{}; + o2::aod::ITSResponse itsResponse; + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; struct : ConfigurableGroup { @@ -110,6 +114,8 @@ struct Cha01710analysis { Configurable cfgDcaZMax{"cfgDcaZMax", 2.f, "maximum |DCAz| (cm)"}; Configurable cfgTPCNSigmaMax{"cfgTPCNSigmaMax", 3.f, "maximum |TPC nSigma(K)| without TOF"}; Configurable cfgCombinedNSigmaMax{"cfgCombinedNSigmaMax", 3.f, "maximum combined TPC-TOF nSigma(K)"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; } kaonCuts; struct : ConfigurableGroup { @@ -193,6 +199,8 @@ struct Cha01710analysis { histos.add("Kaon/hTOFNSigma", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Kaon/hTPCNSigmaSelected", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Kaon/hTOFNSigmaSelected", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigma", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigmaSelected", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); histos.add("Pair/hSignalPlus", "K0S K+ same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); histos.add("Pair/hSignalMinus", "K0S K- same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); @@ -263,9 +271,28 @@ struct Cha01710analysis { histos.fill(HIST("Kaon/hTPCNSigma"), track.pt(), track.tpcNSigmaKa()); if (track.hasTOF()) { histos.fill(HIST("Kaon/hTOFNSigma"), track.pt(), track.tofNSigmaKa()); - return std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()) < kaonCuts.cfgCombinedNSigmaMax; } - return std::abs(track.tpcNSigmaKa()) < kaonCuts.cfgTPCNSigmaMax; + histos.fill(HIST("Kaon/hITSNSigma"), track.pt(), itsResponse.nSigmaITS(track)); + + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; + + if (track.hasTOF()) { + if (std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()) > kaonCuts.cfgCombinedNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; + } + if (std::abs(track.tpcNSigmaKa()) > kaonCuts.cfgTPCNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; } template From 4bccd5b591dd21cd2e1eded72567aed6f0da74f4 Mon Sep 17 00:00:00 2001 From: Rrantu <156880782+Rrantu@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:55:56 +0100 Subject: [PATCH 479/648] [PWGHF] Add input features for Xic0 KF online ML inference (#18497) --- PWGHF/Core/HfMlResponseXic0ToXiPiKf.h | 40 ++++++++++----------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h index 0c4e7fc4d65..2a16648320b 100644 --- a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h +++ b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h @@ -24,13 +24,7 @@ #include #include -// Fill the map of available input features -// the key is the feature's name (std::string) -// the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XIC0TOXIPIKF(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE)} - +// Feature names are registered explicitly with the training-tree branch names. // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value @@ -61,7 +55,7 @@ namespace o2::analysis { enum class InputFeaturesXic0ToXiPiKf : uint8_t { - // Existing feature keys retained for backwards compatibility. + // Internal feature identifiers; configuration names are registered below. tpcNSigmaPiFromLambda, tpcNSigmaPiFromCasc, tpcNSigmaPiFromCharmBaryon, @@ -92,7 +86,10 @@ enum class InputFeaturesXic0ToXiPiKf : uint8_t { dcaV0Dau, dcaXYToPvV0Dau0, dcaXYToPvV0Dau1, - tpcNSigmaPrFromLambda + tpcNSigmaPrFromLambda, + chi2GeoXic, + cTauXic, + chi2GeoCasc }; template @@ -121,6 +118,8 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoXicToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cTauXic); // Bachelor pion from Xic. CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYPiFromXic); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoPiFromXicToPv); @@ -128,6 +127,7 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse // Cascade. CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascChi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToXic); CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaCascToPv, cosPACasc); @@ -149,7 +149,7 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau1); CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPr, tpcNSigmaPrFromLambda, tpcNSigmaPr); CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); - // Legacy features that are not part of the local 28-feature model. + // Additional supported features. CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); } @@ -164,31 +164,23 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse { MlResponse::mAvailableInputFeatures = { FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYXic", decayLenXYXic), - FILL_MAP_XIC0TOXIPIKF(xicChi2OverNdf), FILL_MAP_XIC0TOXIPIKF_AS("fXicChi2OverNdf", xicChi2OverNdf), FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoXicToPv", chi2TopoXicToPv), - FILL_MAP_XIC0TOXIPIKF(dcaCharmBaryonDau), FILL_MAP_XIC0TOXIPIKF_AS("fDcaCharmBaryonDau", dcaCharmBaryonDau), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoXic", chi2GeoXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCtXic", cTauXic), FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYPiFromXic", kfDcaXYPiFromXic), FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoPiFromXicToPv", chi2TopoPiFromXicToPv), FILL_MAP_XIC0TOXIPIKF_AS("fKfptPiFromXic", kfptPiFromXic), - FILL_MAP_XIC0TOXIPIKF(decayLenXYCasc), FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYCasc", decayLenXYCasc), - FILL_MAP_XIC0TOXIPIKF(cascChi2OverNdf), FILL_MAP_XIC0TOXIPIKF_AS("fCascChi2OverNdf", cascChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoCasc", chi2GeoCasc), FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToPv", chi2TopoCascToPv), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToXic), FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToXic", chi2TopoCascToXic), FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToPv", cosPaCascToPv), - FILL_MAP_XIC0TOXIPIKF(cosPaCascToXic), FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToXic", cosPaCascToXic), - FILL_MAP_XIC0TOXIPIKF(dcaCascDau), FILL_MAP_XIC0TOXIPIKF_AS("fDcaCascDau", dcaCascDau), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYCascToPv), FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYCascToPv", kfDcaXYCascToPv), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCasc), FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCasc", tpcNSigmaPiFromCasc), FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvCascDau", dcaXYToPvCascDau), FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYLambda", decayLenXYLambda), @@ -201,17 +193,15 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau0", dcaXYToPvV0Dau0), FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau1", dcaXYToPvV0Dau1), FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPrFromLambda", tpcNSigmaPrFromLambda), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromLambda), FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromLambda", tpcNSigmaPiFromLambda), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCharmBaryon), - FILL_MAP_XIC0TOXIPIKF(cascldl), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCharmBaryon", tpcNSigmaPiFromCharmBaryon), + FILL_MAP_XIC0TOXIPIKF_AS("fCascldl", cascldl), }; } }; } // namespace o2::analysis -#undef FILL_MAP_XIC0TOXIPIKF #undef FILL_MAP_XIC0TOXIPIKF_AS #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF From f4e15cd7d64e209918bc90d2793a58ba017b6ea8 Mon Sep 17 00:00:00 2001 From: YubiaoWang Date: Thu, 29 Oct 2026 13:54:31 +0100 Subject: [PATCH 480/648] [PWGJE] jetChargedV2: optional THnSparse QA for jets, rho and background fluctuations (#18472) --- PWGJE/Tasks/jetChargedV2.cxx | 179 +++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 6 deletions(-) diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 124d8bb6f10..210550c221d 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +74,6 @@ struct JetChargedV2 { Configurable centralityMax{"centralityMax", 999.0, "maximum centrality"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; - Configurable trackDcaZmax{"trackDcaZmax", 99, "additional cut on dcaZ to PV for tracks; uniformTracks in particular don't cut on this at all"}; Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackPtMin{"trackPtMin", 0.15, "minimum pT acceptance for tracks"}; @@ -84,7 +85,6 @@ struct JetChargedV2 { Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable jetPtMin{"jetPtMin", 0.15, "minimum pT acceptance for jets"}; Configurable jetPtMax{"jetPtMax", 200.0, "maximum pT acceptance for jets"}; Configurable jetEtaMin{"jetEtaMin", -0.9, "minimum eta acceptance for jets"}; Configurable jetEtaMax{"jetEtaMax", 0.9, "maximum eta acceptance for jets"}; @@ -120,6 +120,24 @@ struct JetChargedV2 { ConfigurableAxis cfgAxisVnCent{"cfgAxisVnCent", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 50, 70, 100}, " % "}; EventPlaneHelper helperEP; + Configurable fillTHnSparse{"fillTHnSparse", false, "switch to fill the THnSparse"}; + // subsample the sparse filling: 1 = every jet/cone, N = only every N-th. At full statistics the + // sparse costs O(entries) * 40 B, so use e.g. 100 to keep the output at the MB level. + Configurable sparseSubsample{"sparseSubsample", 1, "fill every N-th jet/cone into the THnSparse (1 = all)"}; + // binning of the THnSparse histograms: coarse defaults are safe for a full production, the finer + // binning used for local tests can be set from the command line (or from the train configuration) + // all THnSparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples, in the order pT (also used for rho*A), centrality, rho, Delta phi + Configurable> sparseAxes{"sparseAxes", {105, 0., 210., 55, 0., 110., 110, 0., 110., 60, 0., 6.2831853}, "sparse axes as (nbins, min, max) triples: pT, centrality, rho, Delta phi"}; + // remaining sparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples for delta pT, area and number of tracks + Configurable> sparseExtraAxes{"sparseExtraAxes", {100.f, -200.f, 200.f, 8.f, 0.f, 0.8f, 20.f, 0.f, 100.f}, "extra sparse axes as (nbins, min, max) triples: delta pT, area, N tracks"}; + Configurable doSumw2Sparse{"doSumw2Sparse", true, "Flag whether to set Sumw2() for the sparse histograms"}; + std::shared_ptr hJetQA; //! per-jet QA quantities (pT, eta, phi, area, nTracks, centrality) + std::shared_ptr hRhoMedian; //! per-jet information using the event-averaged (median) rho + std::shared_ptr hRhoLocal; //! per-jet information using the local rho of that jet + std::shared_ptr hFluctQA; //! per-random-cone QA quantities + std::shared_ptr hFluctQAwoLeadingJet; //! per-random-cone QA, leading jet removed from the cone int detId = 0; int refAId = 0; int refBId = 0; @@ -359,6 +377,10 @@ struct JetChargedV2 { registry.add("h2_phi_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); registry.add("h2_centrality_pT", "centrality vs p_{T}; p_{T}; centrality ", {HistType::kTH2F, {{210, -10.0, 200.0}, {100, 0., 100}}}); + registry.add("h2_rho_cent_median_in", "#rho vs centrality, in-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_median_out", "#rho vs centrality, out-of-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_in", "#rho vs centrality, in-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_out", "#rho vs centrality, out-of-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); registry.add("h2_rholocal_cent", "#centrality vs #rho(#varphi); #centrality; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); registry.add("h2_averagerho_cent", "#centrality vs #rho; #centrality; #rho ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); @@ -396,11 +418,13 @@ struct JetChargedV2 { if (fillFluctH3) { registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); } + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); if (fillFluctH3) { registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); } + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); if (checkAbsDeltaPhi) { @@ -493,6 +517,14 @@ struct JetChargedV2 { registry.add("h2_mcp_centrality_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{120, -10.0, 110.0}, {210, -10.0, 200.0}}}); registry.add("h_mcp_jet_pt_rhoareasubtracted", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_mcp_jet_pt_rholocal", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + //< MCP fit quality (independent from the data/MCD QA histograms) >// + registry.add("h_mcp_PvalueCDF_CombinFit", "cDF #chi^{2}; entries", {HistType::kTH1F, {{50, 0, 1}}}); + registry.add("h2_mcp_PvalueCDFCent_CombinFit", "p-value cDF vs centrality; centrality; p-value", {HistType::kTH2F, {{100, 0, 100}, {40, 0, 1}}}); + registry.add("h2_mcp_Chi2Cent_CombinFit", "Chi2 vs centrality; centrality; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 100}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFit", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitA", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitB", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); //< MCP fit test >// registry.add("h_mcp_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h_ep2_evtnum", "fitparameter #rho_{0} vs evtnum ; #eventnumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); @@ -586,6 +618,45 @@ struct JetChargedV2 { registry.add("h_track_pt", "track #it{p}_{T} ; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH1F, {trackPtAxis}}); registry.add("h2_track_eta_track_phi", "track eta vs. track phi; #eta; #phi; counts", {HistType::kTH2F, {trackEtaAxis, phiAxis}}); + + // optional sparse storage of the analysis ingredients (booked always, filled only on request, + // so that the output merging always sees the same object names) + // binning follows the existing 2D outputs of this task, so that the sparses can be compared + const auto& axes = sparseAxes.value; + if (axes.size() < 12) { + LOGF(fatal, "sparseAxes needs 12 numbers (4 x (nbins, min, max)), got %d", static_cast(axes.size())); + } + // bin by bin with h2_rholocal_pt (rho 110 x pT 210), h2_phi_rholocal (phi 100 x rho 110) and + // h2_centrality_deltapT_RandomCornPhi_* (centrality 1100 x delta pT 1000) + const AxisSpec axisQAPt{static_cast(axes[0]), axes[1], axes[2], "#it{p}_{T,jet}^{raw} (GeV/#it{c})"}; + const AxisSpec axisQAEta{20, -1., 1., "#it{#eta}_{jet}"}; + const AxisSpec axisQAPhi{static_cast(axes[9]), axes[10], axes[11], "#it{#varphi}_{jet} (rad)"}; + const auto& extra = sparseExtraAxes.value; + if (extra.size() < 9) { + LOGF(fatal, "sparseExtraAxes needs 9 numbers (3 x (nbins, min, max)), got %d", static_cast(extra.size())); + } + const AxisSpec axisQAArea{static_cast(extra[3]), extra[4], extra[5], "area"}; + const AxisSpec axisQANtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{jet}"}; + const AxisSpec axisQACent{static_cast(axes[3]), axes[4], axes[5], "centrality (%)"}; + hJetQA = registry.add("hJetQA", "per-jet QA", {HistType::kTHnSparseF, {{axisQAPt}, {axisQAEta}, {axisQAPhi}, {axisQAArea}, {axisQANtracks}, {axisQACent}}}, doSumw2Sparse); + + const AxisSpec axisRhoValue{static_cast(axes[6]), axes[7], axes[8], "#it{#rho} (GeV)"}; + const AxisSpec axisRhoArea{static_cast(axes[0]), axes[1], axes[2], "#it{#rho} #upoint A (GeV)"}; + const AxisSpec axisRhoDphi{static_cast(axes[9]), axes[10], axes[11], "#varphi - #Psi_{EP,2} (rad)"}; + hRhoMedian = registry.add("hRhoMedian", "per-jet information, event-averaged (median) rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + hRhoLocal = registry.add("hRhoLocal", "per-jet information, local rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + + const AxisSpec axisFluctDpt{static_cast(extra[0]), extra[1], extra[2], "#delta#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisFluctAbsDphi{static_cast(axes[9]), axes[10], axes[11], "|#varphi_{cone} - #Psi_{EP,2}| (rad)"}; + const AxisSpec axisFluctArea{static_cast(extra[3]), extra[4], extra[5], "area_{cone}"}; + const AxisSpec axisFluctRho{static_cast(axes[6]), axes[7], axes[8], "#it{#rho}_{local}^{RC} (GeV)"}; + const AxisSpec axisFluctNtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{cone}"}; + hFluctQA = registry.add("hFluctQA", "per-random-cone QA", {HistType::kTHnSparseF, {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, doSumw2Sparse); + hFluctQAwoLeadingJet = registry.add( + "hFluctQAwoLeadingJet", "per-random-cone QA, leading jet removed from the cone", + {HistType::kTHnSparseF, + {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, + doSumw2Sparse); } Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); @@ -1050,6 +1121,37 @@ struct JetChargedV2 { registry.fill(HIST("h_mcp_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_mcp_evtnum_centrlity"), evtnum, centrality); + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; + double chi2 = 0.; + for (int i = 0; i < hPtsumSumptFitMCPFill->GetXaxis()->GetNbins(); i++) { + if (hPtsumSumptFitMCPFill->GetBinContent(i + 1) <= 0.) { + continue; // empty bins do not enter the chi2 and must not count in the dof + } + nBinsUsed++; + chi2 += std::pow((hPtsumSumptFitMCPFill->GetBinContent(i + 1) - fFitModulationV2v3P->Eval(hPtsumSumptFitMCPFill->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFitMCPFill->GetBinContent(i + 1); + } + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters + + int evtCentAreaMin = 0; + int evtCentAreaMax = 5; + int evtMidAreaMin = 30; + int evtMidAreaMax = 50; + + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); + registry.fill(HIST("h_mcp_PvalueCDF_CombinFit"), cDF); + registry.fill(HIST("h2_mcp_PvalueCDFCent_CombinFit"), centrality, cDF); + registry.fill(HIST("h2_mcp_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_mcp_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); + if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); + } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); + } + } + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { int nmode = epQvec.cfgnMods->at(i); int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); @@ -1078,8 +1180,8 @@ struct JetChargedV2 { } // phiMinusPsi2 = jet.phi() - ep2; phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); - float absDelta = std::abs(phiMinusPsi2); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_mcp_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), weight); registry.fill(HIST("h2_mcp_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), weight); } else { @@ -1735,6 +1837,14 @@ struct JetChargedV2 { } registry.fill(HIST("h_jet_pt_inclusive_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); + if (fillTHnSparse && sparseSubsample > 0 && (jet.globalIndex() % sparseSubsample) == 0) { + const std::array qaJet = {jet.pt(), jet.eta(), jet.phi(), jet.area(), static_cast(jet.tracksIds().size()), centrality}; + hJetQA->Fill(qaJet.data(), 1.0); + const std::array rhoMedianData = {centrality, jet.pt(), collision.rho(), collision.rho() * jet.area(), phiMinusPsi2, jet.area()}; + hRhoMedian->Fill(rhoMedianData.data(), 1.0); + const std::array rhoLocalData = {centrality, jet.pt(), rholocal, rholocal * jet.area(), phiMinusPsi2, jet.area()}; + hRhoLocal->Fill(rhoLocalData.data(), 1.0); + } if (jet.pt() > bumpLow && jet.pt() < bumpUp) { histosQA.fill(HIST("h2_ep2_FT0C_FT0M_bumpRegion"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); @@ -1760,6 +1870,8 @@ struct JetChargedV2 { registry.fill(HIST("h2_averagerho_pt_inplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_inplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_in"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_in"), centrality, rholocal, 1.0); } else { registry.fill(HIST("h_jet_pt_out_of_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); @@ -1777,6 +1889,8 @@ struct JetChargedV2 { registry.fill(HIST("h2_averagerho_pt_outplane"), jet.pt(), collision.rho(), 1.0); registry.fill(HIST("h2_rholocal_pt_outplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_out"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_out"), centrality, rholocal, 1.0); } if (checkAbsDeltaPhi) { if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { @@ -1824,12 +1938,14 @@ struct JetChargedV2 { // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); + int nTracksRandomCone = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomCone; } } } @@ -1841,6 +1957,12 @@ struct JetChargedV2 { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); } else { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluct = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomCone)}; + hFluctQA->Fill(qaFluct.data(), 1.0); + } } if (checkAbsDeltaPhi) { if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { @@ -1851,6 +1973,7 @@ struct JetChargedV2 { } // removing the leading jet from the random cone + int nTracksRandomConeWoLeadingJet = 0; if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); float dEtaLeadingJet = leadingJetEta - randomConeEta; @@ -1876,6 +1999,7 @@ struct JetChargedV2 { float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomConeWoLeadingJet; } } } @@ -1885,6 +2009,8 @@ struct JetChargedV2 { registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); } + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); @@ -1892,6 +2018,12 @@ struct JetChargedV2 { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); } + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluctWo = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomConeWoLeadingJet)}; + hFluctQAwoLeadingJet->Fill(qaFluctWo.data(), 1.0); + } if (checkAbsDeltaPhi) { if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); @@ -2109,11 +2241,11 @@ struct JetChargedV2 { } // phiMinusPsi2 = jet.phi() - ep2; phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); - float absDelta = std::abs(phiMinusPsi2); + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); } else { @@ -2149,8 +2281,11 @@ struct JetChargedV2 { int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { double rcPhiPsi2 = 0.0; + float absRcPhiPsi2 = 0.0; // rcPhiPsi2 = randomConePhi - ep2; rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { @@ -2164,6 +2299,18 @@ struct JetChargedV2 { if (fillFluctH3) { registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + } // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet @@ -2183,6 +2330,10 @@ struct JetChargedV2 { } if (jetWasInCone) { randomConePt = 0.0; + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); + rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { // if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > randomConeR)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); @@ -2205,6 +2356,22 @@ struct JetChargedV2 { registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + } } else if (nmode == cfgNmodB) { continue; } From 7d051943fdb0f35d964442f8ff3e3a06a4b5b86f Mon Sep 17 00:00:00 2001 From: HANSEO PARK <53218370+hanseopark@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:54:56 +0900 Subject: [PATCH 481/648] [PWGJE] Fix jet matching efficiency and add pair momentum axes (#18500) --- PWGJE/Tasks/baryonPairInJets.cxx | 134 +++++++++++++++++++------------ 1 file changed, 82 insertions(+), 52 deletions(-) diff --git a/PWGJE/Tasks/baryonPairInJets.cxx b/PWGJE/Tasks/baryonPairInJets.cxx index ca47fe594f6..5f17ed90de2 100644 --- a/PWGJE/Tasks/baryonPairInJets.cxx +++ b/PWGJE/Tasks/baryonPairInJets.cxx @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -128,7 +129,7 @@ struct BaryonPairInJets { struct : ConfigurableGroup { Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex (cm)"}; - Configurable eventSelections{"eventSelections", "sel8+NoITSROFrameBorder+NoTimeFrameBorder+NoSameBunchPileup+IsGoodZvtxFT0vsPV", "Event selections"}; + Configurable eventSelections{"eventSelections", "sel8", "Event selections"}; Configurable applyRctSelection{"applyRctSelection", true, "Apply the PWGJE hadron-PID RCT selection"}; Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection label"}; Configurable occupancyMin{"occupancyMin", -999999, "Minimum track occupancy in time range"}; @@ -145,7 +146,7 @@ struct BaryonPairInJets { struct : ConfigurableGroup { Configurable protonPtMin{"protonPtMin", 0.5f, "Minimum proton transverse momentum (GeV/c)"}; - Configurable protonPtMax{"protonPtMax", 2.5f, "Maximum proton transverse momentum (GeV/c)"}; + Configurable protonPtMax{"protonPtMax", 4.0f, "Maximum proton transverse momentum (GeV/c)"}; Configurable protonEtaMax{"protonEtaMax", 0.8f, "Maximum absolute proton pseudorapidity"}; Configurable requirePvContributor{"requirePvContributor", false, "Require the track to be a PV contributor"}; Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 70, "Minimum number of crossed TPC rows"}; @@ -199,6 +200,7 @@ struct BaryonPairInJets { ConfigurableAxis jetPhi{"jetPhi", {72, 0.f, o2::constants::math::TwoPI}, "#varphi_{jet}"}; ConfigurableAxis jetNch{"jetNch", {61, -0.5f, 60.5f}, "#it{N}_{ch}^{jet}"}; ConfigurableAxis baryonPt{"baryonPt", {50, 0.f, 5.f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis pairPt{"pairPt", {VARIABLE_WIDTH, 0.5f, 0.6f, 0.75f, 1.f, 1.25f, 1.5f, 2.f, 2.5f, 3.f, 4.f, 5.f}, "Pair-particle #it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis baryonEta{"baryonEta", {48, -1.2f, 1.2f}, "#eta"}; ConfigurableAxis baryonRapidity{"baryonRapidity", {48, -1.2f, 1.2f}, "#it{y}"}; ConfigurableAxis nSigma{"nSigma", {100, -5.f, 5.f}, "n#sigma"}; @@ -219,6 +221,7 @@ struct BaryonPairInJets { SliceCache mcdMixingCache; SliceCache mcpMixingCache; SliceCache inclusiveMixingCache; + Preslice particleJetsPerMcCollision = aod::jet::mcCollisionId; using MixingBinning = ColumnBinningPolicy; using MCPMixingBinning = ColumnBinningPolicy; using InclusiveSameEventCollisions = soa::Join; @@ -239,6 +242,8 @@ struct BaryonPairInJets { AxisSpec axisJetPhi{axes.jetPhi}; AxisSpec axisJetNch{axes.jetNch}; AxisSpec axisBaryonPt{axes.baryonPt}; + AxisSpec axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; AxisSpec axisBaryonEta{axes.baryonEta}; AxisSpec axisBaryonRapidity{axes.baryonRapidity}; AxisSpec axisNSigma{axes.nSigma}; @@ -294,10 +299,10 @@ struct BaryonPairInJets { addPairHistograms("mixedEvent/nominal", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); addPairHistograms("mixedEvent/loose", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); addPairHistograms("mixedEvent/random", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); - registry.add("sameEvent/chargePairSparse", "Charge-separated baryon pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); - registry.add("mixedEvent/nominal/chargePairSparse", "Charge-separated nominal mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); - registry.add("mixedEvent/loose/chargePairSparse", "Charge-separated loose mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); - registry.add("mixedEvent/random/chargePairSparse", "Charge-separated random mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + registry.add("sameEvent/chargePairSparse", "Charge-separated baryon pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/nominal/chargePairSparse", "Charge-separated nominal mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/loose/chargePairSparse", "Charge-separated loose mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/random/chargePairSparse", "Charge-separated random mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); addInclusivePairHistograms("inclusive/sameEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); addInclusivePairHistograms("inclusive/mixedEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); @@ -332,9 +337,9 @@ struct BaryonPairInJets { registry.add("mc/efficiency/trackSelectedPrimaryProton", "Track-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); registry.add("mc/efficiency/tofMatchedPrimaryProton", "TOF-matched primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); registry.add("mc/efficiency/pidSelectedPrimaryProton", "PID-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); - registry.add("mc/efficiency/generatedPair", "Generated primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); - registry.add("mc/efficiency/trackSelectedPair", "Track-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); - registry.add("mc/efficiency/pidSelectedPair", "PID-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR}); + registry.add("mc/efficiency/generatedPair", "Generated primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/trackSelectedPair", "Track-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/pidSelectedPair", "PID-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); addPairHistograms("mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); addPairHistograms("mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); registry.add("mc/particle/normalizationSameEvent", "Particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); @@ -394,11 +399,13 @@ struct BaryonPairInJets { AxisSpec const& axisDeltaPhi, AxisSpec const& axisDeltaR) { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); registry.add(directory + "/sameSignDeltaR", "Same-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); registry.add(directory + "/oppositeSignDeltaR", "Opposite-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); - registry.add(directory + "/pairSparse", "Baryon-pair distribution;pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR}); + registry.add(directory + "/pairSparse", "Baryon-pair distribution;pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); } void addInclusivePairHistograms(std::string const& directory, @@ -406,9 +413,11 @@ struct BaryonPairInJets { AxisSpec const& axisDeltaEta, AxisSpec const& axisDeltaPhi) { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Inclusive same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Inclusive opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); - registry.add(directory + "/chargePairSparse", "Inclusive charge-separated baryon pairs;charge pair type;#Delta#eta;#Delta#varphi", HistType::kTHnSparseF, {axisChargePairType, axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/chargePairSparse", "Inclusive charge-separated baryon pairs;charge pair type;#Delta#eta;#Delta#varphi;#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisDeltaEta, axisDeltaPhi, axisPairPtLow, axisPairPtHigh}); } void setHistogramLabels() @@ -692,7 +701,7 @@ struct BaryonPairInJets { } template - void fillControlPair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + void fillControlPair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) { auto fillPairHistograms = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& deltaRSameSignName, auto const& deltaROppositeSignName, auto const& sparseName) { if (pairType == PairType::SameSign) { @@ -704,8 +713,8 @@ struct BaryonPairInJets { registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); registry.fill(deltaROppositeSignName, deltaR, weight); } - registry.fill(sparseName, static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(sparseName, static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(sparseName, static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); }; if constexpr (Species == ControlSpecies::Pion && !IsParticleLevel) { @@ -749,7 +758,9 @@ struct BaryonPairInJets { float const deltaEta = particles[first].eta - particles[second].eta; float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); - fillControlPair(false, pairType, jetPt, deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); + fillControlPair(false, pairType, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); if constexpr (IsParticleLevel) { fillControlParticleNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); } else { @@ -782,7 +793,9 @@ struct BaryonPairInJets { float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); PairType const pairType = getPairType(particle1, particle2); - fillControlPair(true, pairType, mixedJetPt, deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(particle1.pt, particle2.pt); + float const pairPtHigh = std::max(particle1.pt, particle2.pt); + fillControlPair(true, pairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); if constexpr (IsParticleLevel) { fillControlParticleNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); } else { @@ -849,6 +862,8 @@ struct BaryonPairInJets { float const deltaEta = baryon1.eta - baryon2.eta; float const deltaPhi = RecoDecay::constrainAngle(baryon1.phi - baryon2.phi, -o2::constants::math::PIHalf); float const reflectedDeltaPhi = RecoDecay::constrainAngle(-deltaPhi, -o2::constants::math::PIHalf); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); if (isMixed) { if (pairType == PairType::SameSign) { registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); @@ -858,8 +873,8 @@ struct BaryonPairInJets { registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); } if (output.fillChargeSeparated.value) { - registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, 0.5f * weight); - registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); } } else { if (pairType == PairType::SameSign) { @@ -870,8 +885,8 @@ struct BaryonPairInJets { registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); } if (output.fillChargeSeparated.value) { - registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, 0.5f * weight); - registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); } } } @@ -893,7 +908,7 @@ struct BaryonPairInJets { } template - void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) { auto const pairTypeValue = static_cast(pairType); auto fillSymmetrized = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& sparseName) { @@ -904,8 +919,8 @@ struct BaryonPairInJets { registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); } - registry.fill(sparseName, pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(sparseName, pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(sparseName, pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); }; if constexpr (Reference == MixedReference::Nominal) { @@ -916,8 +931,8 @@ struct BaryonPairInJets { registry.fill(HIST("mixedEvent/nominal/oppositeSignDeltaR"), deltaR, weight); } if (output.fillChargeSeparated.value) { - registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } else if constexpr (Reference == MixedReference::Loose) { fillSymmetrized(HIST("mixedEvent/loose/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/pairSparse")); @@ -927,8 +942,8 @@ struct BaryonPairInJets { registry.fill(HIST("mixedEvent/loose/oppositeSignDeltaR"), deltaR, weight); } if (output.fillChargeSeparated.value) { - registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } else { fillSymmetrized(HIST("mixedEvent/random/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/pairSparse")); @@ -938,8 +953,8 @@ struct BaryonPairInJets { registry.fill(HIST("mixedEvent/random/oppositeSignDeltaR"), deltaR, weight); } if (output.fillChargeSeparated.value) { - registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } } @@ -956,7 +971,7 @@ struct BaryonPairInJets { } } - void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) { auto const pairTypeValue = static_cast(pairType); if (pairType == PairType::SameSign) { @@ -968,11 +983,11 @@ struct BaryonPairInJets { registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); registry.fill(HIST("sameEvent/oppositeSignDeltaR"), deltaR, weight); } - registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); if (output.fillChargeSeparated.value) { - registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } @@ -1011,7 +1026,7 @@ struct BaryonPairInJets { return selected; } - void fillParticlePair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float weight) + void fillParticlePair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) { if (isMixed) { if (pairType == PairType::SameSign) { @@ -1023,8 +1038,8 @@ struct BaryonPairInJets { registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaR"), deltaR, weight); } - registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } else { if (pairType == PairType::SameSign) { registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); @@ -1035,8 +1050,8 @@ struct BaryonPairInJets { registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaR"), deltaR, weight); } - registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } @@ -1049,15 +1064,17 @@ struct BaryonPairInJets { float const deltaEta = particles[first].eta - particles[second].eta; float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); if constexpr (Stage == EfficiencyStage::Generated) { - registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } else if constexpr (Stage == EfficiencyStage::TrackSelected) { - registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } else { - registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, 0.5f * weight); - registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, 0.5f * weight); + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); } } } @@ -1200,7 +1217,9 @@ struct BaryonPairInJets { float const deltaEta = baryons[first].eta - baryons[second].eta; float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); - fillSameEventPair(pairType, chargePairType, jet.pt(), deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillSameEventPair(pairType, chargePairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); if (output.fillQa.value) { if (pairType == PairType::SameSign) { registry.fill(HIST("jet/sameSignPairsVsPt"), jet.pt(), weight); @@ -1269,7 +1288,9 @@ struct BaryonPairInJets { float const deltaR = std::hypot(deltaEta, deltaPhi); PairType const pairType = getPairType(baryon1, baryon2); ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); - fillPair(pairType, chargePairType, mixedJetPt, deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillPair(pairType, chargePairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); fillMixedNormalization(pairType == PairType::SameSign ? 3.f : 4.f, weight); if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { fillMixedNormalization(pairType == PairType::SameSign ? 5.f : 6.f, weight); @@ -1421,7 +1442,6 @@ struct BaryonPairInJets { if (!isSelectedJet(jet)) { continue; } - registry.fill(HIST("mc/jet/allParticlePt"), jet.pt(), weight); registry.fill(HIST("mc/particle/normalizationSameEvent"), 2.f, weight); auto selectedParticles = collectParticleCandidates(jet, true, weight); auto const& baryons = selectedParticles.baryons; @@ -1431,7 +1451,9 @@ struct BaryonPairInJets { float const deltaEta = baryons[first].eta - baryons[second].eta; float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); - fillParticlePair(false, pairType, jet.pt(), deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillParticlePair(false, pairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); registry.fill(HIST("mc/particle/normalizationSameEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); } } @@ -1489,7 +1511,9 @@ struct BaryonPairInJets { float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); float const deltaR = std::hypot(deltaEta, deltaPhi); PairType const pairType = getPairType(baryon1, baryon2); - fillParticlePair(true, pairType, 0.5f * (jet1.pt() + jet2.pt()), deltaEta, deltaPhi, deltaR, weight); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillParticlePair(true, pairType, 0.5f * (jet1.pt() + jet2.pt()), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); registry.fill(HIST("mc/particle/normalizationMixedEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); } } @@ -1508,7 +1532,7 @@ struct BaryonPairInJets { void processMatched(aod::JetCollisionsMCD::iterator const& collision, aod::JetMcCollisions const&, MatchedChargedMCDJets const& detectorJets, - MatchedChargedMCPJets const&, + MatchedChargedMCPJets const& particleJets, JetTracksWithOriginalAndMC const&, BaryonTracks const&, aod::JetParticles const&) @@ -1517,6 +1541,12 @@ struct BaryonPairInJets { return; } float const weight = collision.mcCollision().weight(); + auto const particleJetsInCollision = particleJets.sliceBy(particleJetsPerMcCollision, collision.mcCollisionId()); + for (auto const& particleJet : particleJetsInCollision) { + if (isSelectedJet(particleJet)) { + registry.fill(HIST("mc/jet/allParticlePt"), particleJet.pt(), weight); + } + } for (auto const& detectorJet : detectorJets) { if (!isSelectedJet(detectorJet)) { continue; @@ -1524,7 +1554,7 @@ struct BaryonPairInJets { registry.fill(HIST("mc/jet/allDetectorPt"), detectorJet.pt(), weight); bool hasAcceptedMatch = false; for (auto const& particleJet : detectorJet.template matchedJetGeo_as()) { - if (!isSelectedJetGeometry(particleJet) || particleJet.mcCollisionId() != collision.mcCollisionId()) { + if (!isSelectedJet(particleJet) || particleJet.mcCollisionId() != collision.mcCollisionId()) { continue; } hasAcceptedMatch = true; From be58c8f5717a673b8d08804f6f9906552b209a15 Mon Sep 17 00:00:00 2001 From: Artem Kotliarov <71133985+KotliarovAr@users.noreply.github.com> Date: Thu, 29 Oct 2026 13:55:09 +0100 Subject: [PATCH 482/648] [PWGJE] Add FT0 event-activity support to track QA (#18477) --- PWGJE/Tasks/trackEfficiency.cxx | 122 +++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 11 deletions(-) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 779f6df4805..4ee3a336db3 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -95,6 +96,32 @@ struct TrackEfficiency { Configurable useTrueTrackWeight{"useTrueTrackWeight", true, "test configurable, should be set to 1 then config removed once well tested"}; + // Normalization FT0 by means + struct FT0A : ConfigurableGroup { + std::string prefix = "ft0a"; + Configurable mean{"mean", 3499., "Mean FT0A signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 2640., "Mean FT0A signal at MC detector level"}; + } ft0a; + + struct FT0C : ConfigurableGroup { + std::string prefix = "ft0c"; + Configurable mean{"mean", 844.6, "Mean FT0C signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 802.9, "Mean FT0C signal at MC detector level"}; + } ft0c; + + // FT0C event activity thresholds + struct Hist : ConfigurableGroup { + std::string prefix = "hist"; + + // Raw data + ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.109421, 0.215022, 0.343214, 0.50515, 0.712277, 0.978245, 1.3189, 1.75744, 2.36001, 3.59696, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.130987, 0.237916, 0.36664, 0.528441, 0.734705, 0.996611, 1.32785, 1.74896, 2.31024, 3.37852, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + + // MC detector-level data + ConfigurableAxis multFT0CThreshMCDetLevel{"multFT0CThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.124992, 0.243769, 0.380291, 0.543998, 0.748078, 1.00424, 1.3237, 1.72648, 2.27373, 3.43144, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThreshMCDetLevel{"multFT0MThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.142622, 0.262095, 0.397069, 0.558622, 0.762008, 1.01685, 1.33219, 1.72377, 2.24008, 3.26309, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + } hist; + // systematics variation - Run 2 guidelines: https://twiki.cern.ch/twiki/bin/view/ALICE/AliDPGtoolsTrackSystematicUncertainty TrackSelection customTrackSelection; Configurable useCustomTrackSelection{"useCustomTrackSelection", false, "whether to use the custom cuts (used for cut variation for tracking efficiency systematics)"}; @@ -117,6 +144,11 @@ struct TrackEfficiency { SplitOkCheckFirstAssocCollOnly // 2 }; + enum class TrackSample { + Data, + MCDetLevel + }; + template bool isAcceptedTrack(TJetTrack const& jetTrack) { @@ -147,19 +179,20 @@ struct TrackEfficiency { return std::abs(charge) >= chargeUnit; } - template + template void fillTrackHistograms(TCollision const& collision, TJetTracks const& jetTracks, float weight = 1.0) { + const float meanFT0A = sample == TrackSample::Data ? ft0a.mean.value : ft0a.meanMCDetLevel.value; + const float meanFT0C = sample == TrackSample::Data ? ft0c.mean.value : ft0c.meanMCDetLevel.value; + const auto ft0Metrics = getFT0Metrics(collision, meanFT0A, meanFT0C); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + for (auto const& track : jetTracks) { if (!isAcceptedTrack(track)) { continue; } - float pTHat = simPtRef / (std::pow(weight, 1.0 / pTHatExponent)); - if (track.pt() > pTHatMaxFractionMCD * pTHat) { - continue; - } - float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); registry.fill(HIST("h2_centrality_track_pt"), centrality, track.pt(), weight); @@ -172,6 +205,14 @@ struct TrackEfficiency { registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), weight); registry.fill(HIST("h3_intrate_centrality_track_pt"), collision.hadronicRate(), centrality, track.pt(), weight); registry.fill(HIST("h3_track_pt_track_eta_track_phi"), track.pt(), track.eta(), track.phi(), weight); + + if constexpr (sample == TrackSample::Data) { + registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); + } else { + registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); + } } } @@ -205,6 +246,12 @@ struct TrackEfficiency { LOGP(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } + constexpr float minFT0Mean = std::numeric_limits::epsilon(); + if (std::fabs(ft0a.mean.value) <= minFT0Mean || std::fabs(ft0c.mean.value) <= minFT0Mean || + std::fabs(ft0a.meanMCDetLevel.value) <= minFT0Mean || std::fabs(ft0c.meanMCDetLevel.value) <= minFT0Mean) { + LOGP(fatal, "FT0A and FT0C means must have magnitude greater than {} to avoid near-zero denominators; stopping workflow", minFT0Mean); + } + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); @@ -254,6 +301,10 @@ struct TrackEfficiency { AxisSpec etaAxisEff{etaEffBinning, "#eta"}; AxisSpec phiAxisEff{phiEffBinning, "#phi"}; AxisSpec centAxis = {centralityBinning, "centrality (%)"}; + AxisSpec scaledFT0C{hist.multFT0CThresh, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0M{hist.multFT0MThresh, "FT0M^{*}"}; + AxisSpec scaledFT0CMCDetLevel{hist.multFT0CThreshMCDetLevel, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0MMCDetLevel{hist.multFT0MThreshMCDetLevel, "FT0M^{*}"}; AxisSpec intRateAxis = {intRateBinning, "int. rate (kHz)"}; AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; @@ -346,6 +397,12 @@ struct TrackEfficiency { registry.add("h2_track_pt_high_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {10000, 0.0, 1.0}}}); registry.add("h3_intrate_centrality_track_pt", "interaction rate vs centrality vs track pT; int. rate; centrality; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH3F, {intRateAxis, centAxis, {200, 0., 200.}}}); registry.add("h3_track_pt_track_eta_track_phi", "track pT vs track #eta vs track #varphi; #it{p}_{T,track} (GeV/#it{c}); #eta_{track}; #varphi_{track}", {HistType::kTH3F, {{200, 0., 200.}, {100, -1.0, 1.0}, {160, -1.0, 7.}}}); + + // EA analysis + registry.add("hScaledFT0CTrackPt", "scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0C, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt", "scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0M, {200, 0., 200.}}}); + registry.add("hScaledFT0CTrackPt_MCDetLevel", "MC detector-level scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0CMCDetLevel, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt_MCDetLevel", "MC detector-level scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0MMCDetLevel, {200, 0., 200.}}}); } if (doprocessParticles || doprocessParticlesWeighted) { @@ -853,7 +910,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromData, "QA for charged tracks in data", false); @@ -881,11 +938,11 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMc, "QA for charged tracks in MC without weights", false); - void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, + void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered> const& jetTracks, soa::Join const&) @@ -893,10 +950,14 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - float eventWeight = collision.mcCollision().weight(); if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } + + if (collision.isOutlier()) { + return; + } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { return; @@ -905,12 +966,13 @@ struct TrackEfficiency { return; } + float eventWeight = collision.mcCollision().weight(); float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } - fillTrackHistograms(collision, jetTracks, eventWeight); + fillTrackHistograms(collision, jetTracks, eventWeight); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMcWeighted, "QA for charged tracks in weighted MC", false); @@ -1711,6 +1773,44 @@ struct TrackEfficiency { } } PROCESS_SWITCH(TrackEfficiency, processQcCheck, "Histograms for QC checks", false); + + // FT0 event activity helpers + struct FT0Metrics { + float multFT0A = 0.f; + float multFT0C = 0.f; + float multFT0M = 0.f; + float scaledFT0A = 0.f; + float scaledFT0C = 0.f; + float scaledFT0M = 0.f; + }; + + float getScaledFT0(const float& multFT0, const float& meanFT0) + { + return multFT0 / meanFT0; + } + + float getScaledFT0M(const float& scaledMultFT0A, const float& scaledMultFT0C) + { + return 0.5 * (scaledMultFT0A + scaledMultFT0C); + } + + FT0Metrics makeFT0Metrics(float multFT0A, float multFT0C, float meanFT0A, float meanFT0C) + { + FT0Metrics values; + values.multFT0A = multFT0A; + values.multFT0C = multFT0C; + values.multFT0M = multFT0A + multFT0C; + values.scaledFT0A = getScaledFT0(values.multFT0A, meanFT0A); + values.scaledFT0C = getScaledFT0(values.multFT0C, meanFT0C); + values.scaledFT0M = getScaledFT0M(values.scaledFT0A, values.scaledFT0C); + return values; + } + + template + FT0Metrics getFT0Metrics(const Collision& collision, float meanFT0A, float meanFT0C) + { + return makeFT0Metrics(collision.multFT0A(), collision.multFT0C(), meanFT0A, meanFT0C); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 010c8f1233d80512a2f62b723c9c540a89251059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E6=99=93=E5=AE=87=28Xiaoyu=20Hu=29?= <13971168438@163.com> Date: Thu, 29 Oct 2026 21:52:53 +0800 Subject: [PATCH 483/648] [PWGDQ] add event level histograms in same-event-pairing (#18489) Co-authored-by: ayatsuji <13807650+asakuratou@user.noreply.gitee.com> --- PWGDQ/Tasks/tableReader_withAssoc.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index af99435212a..f38a93f0e05 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -1444,6 +1444,10 @@ struct AnalysisSameEventPairing { // TODO: Add pair cuts via JSON } fConfigCuts; + // add event level histograms after remove collision splitting candidates + o2::framework::Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + o2::framework::Configurable fConfigAddJSONEventHistograms{"cfgAddJSONEventHistograms", "", "Event histograms in JSON format,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + o2::framework::Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; o2::framework::Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; o2::framework::Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; @@ -1532,6 +1536,8 @@ struct AnalysisSameEventPairing { bool fEnableBarrelMuonHistos = false; bool fEnableBarrelMuonMixingHistos = false; + bool fSplitCheckEnabled = false; + o2::framework::NoBinningPolicy hashBin; o2::framework::Preslice> trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; @@ -1878,12 +1884,18 @@ struct AnalysisSameEventPairing { } } + o2::common::core::getTaskOptionValue(context, "analysis-event-selection", "cfgCheckSplitCollisions", fSplitCheckEnabled, false); + if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy - DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms + if (fConfigCuts.event.value && fSplitCheckEnabled) { + DefineHistograms(fHistMan, "Event_AfterSplitVeto", fConfigAddEventHistogram.value.data()); // define event histograms after removing collision splitting candidates + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONEventHistograms.value.c_str()); + } + DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms if (fEnableBarrelHistos) { DefineHistograms(fHistMan, "PairingSEQA", "sameevent-pairing"); // histograms for QA of the pairing } @@ -2063,6 +2075,11 @@ struct AnalysisSameEventPairing { // VarManager::FillEvent(event, dqtablereader_helpers::varValues()); VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); if (groupedAssocs.size() == 0) { continue; @@ -2929,6 +2946,11 @@ struct AnalysisSameEventPairing { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } + auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); if (groupedAssocs1.size() == 0) { continue; From 82d180b0162b22ee4571dce3fe0ea9c3df31255c Mon Sep 17 00:00:00 2001 From: Mattia Faggin Date: Thu, 29 Oct 2026 16:08:45 +0100 Subject: [PATCH 484/648] [Common] Add Track-Tuner calibrations for p-O. (#18493) --- Common/Tools/TrackTuner.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Common/Tools/TrackTuner.h b/Common/Tools/TrackTuner.h index 5e59241cffd..48ed49cdab8 100644 --- a/Common/Tools/TrackTuner.h +++ b/Common/Tools/TrackTuner.h @@ -193,6 +193,8 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559408 <= runNumber && runNumber <= 559456 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 564250 <= runNumber <= 564297 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; LOG(info) << ""; @@ -262,6 +264,14 @@ struct TrackTuner : o2::framework::ConfigurableGroup { pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 13.6 TeV 2025, 2026, CCDB path " << pathInputFile; LOG(info) << " >>> Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) "; + } else if (564250 <= runNumber && runNumber <= 564297) { + /// + /// [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO + /// Run list: 564250 <= runNumber <= 564297 + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pO"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> p-O, 9.62 TeV 2025, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: 564250 <= runNumber <= 564297 "; } else { LOG(fatal) << "runNumber " << runNumber << " not supported for the autodetection. Please switch to manual configuration of the TrackTuner object. Aborting..."; } From bef2606ca95c344673630de650ab3ab032897a71 Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Thu, 29 Oct 2026 17:18:40 +0100 Subject: [PATCH 485/648] [PWGLF,PWGMM] Add histos for event/signal loss checks (#18502) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 274 ++++++++++++++++++++--------- 1 file changed, 190 insertions(+), 84 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index 4509b68ec2c..e9a24df5e94 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -238,6 +238,14 @@ enum class McRecTrkType { nMcRecTrkStatus }; +struct MultE { + static constexpr int CnoMult = 0; + static constexpr int CmultFT0C = 1; + static constexpr int CmultFT0M = 2; + static constexpr int CmultTPC = 3; + static constexpr int CmultMFT = 4; +}; + struct DndetaMFTPbPb { SliceCache cache; @@ -264,6 +272,9 @@ struct DndetaMFTPbPb { Configurable useInelgt0wMFT{"useInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable fillTrackQaHist{"fillTrackQaHist", false, "fill track QA histograms"}; + Configurable multEstimator{"multEstimator", 1, "0: without multiplicity; 1: MultFT0C; 2: MultFT0M; 3: MultTPC, 4: MultMFT"}; + Configurable useMultMCatMidRapidity{"useMultMCatMidRapidity", true, "use generated Nch in ∣eta∣ < 0.8"}; + Configurable useMultMCatMFTacc{"useMultMCatMFTacc", false, "use generated Nch in MFT acceptance"}; } gConf; struct : ConfigurableGroup { @@ -607,22 +618,25 @@ struct DndetaMFTPbPb { if (gConf.fillTrackQaHist) { registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registryMC.add({"Events/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); } if (doprocessMcBestInclusive) { - registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); } else { - registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); } - registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); registryMC.add({"Tracks/PhiEtaGen_t", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGenRecEvt", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; registryMC.print(); } @@ -656,18 +670,25 @@ struct DndetaMFTPbPb { if (gConf.fillTrackQaHist) { registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Events/Centrality/NtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); } - registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + if (doprocessMcBestCentFT0C) { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/PhiEtaGen_t", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGenRecEvt", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); @@ -683,9 +704,9 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); } if (doprocessMcEfficiencyBestInclusive) { - registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); } else { - registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); recEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); } registryMC.add({"Events/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); registryMC.add({"Tracks/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); @@ -705,14 +726,14 @@ struct DndetaMFTPbPb { registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); } if (doprocessMcEfficiencyBestCentFT0C) { - registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); } else { - registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); } registryMC.add({"Events/Centrality/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registryMC.add({"Tracks/Centrality/hEffRecClosure", ";#eta; #it{z}_{vtx} (cm); recEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; registryMC.print(); } @@ -760,16 +781,14 @@ struct DndetaMFTPbPb { hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); - registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multFT0cAxis, evtLossTypeAxis}}}); - registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentRec", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}}); - registryMC.add({"Events/hMultGenVsCentRecNParticlesEta05", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Events/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}}); - registryMC.add({"Tracks/hEtaVsNchGen", "; #eta; mult gen", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); - registryMC.add({"Tracks/hEtaVsNchGenRecEvt", "; #eta; mult gen w/ Rec evt", {HistType::kTH2F, {etaAxis, multFT0cAxis}}}); + registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multAxis, evtLossTypeAxis}}}); + registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCent", "Gen Nch vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentMFTacc", "Gen Nch (MFT acc) vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvt", "Gen Nch w/ Nrec > 0 vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvtMFTacc", "Gen Nch (MFT acc) w/ Nrec > 0 vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Tracks/hNchGenVsEta", "Gen Nch vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); + registryMC.add({"Tracks/hNchGenVsEtaRecEvt", "Gen Nch w/ Nrec > 0 vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); LOG(info) << "doprocessMcSgnEvtLossCentFT0C -> Size of the MC histograms:"; registryMC.print(); } @@ -779,12 +798,10 @@ struct DndetaMFTPbPb { heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); heff->GetAxis(0)->SetBinLabel(3, "Remove split vertices"); - registryMC.add("Events/hCentBest", "; centrality", HistType::kTH1F, {centralityAxis}); registryMC.add("Events/hGenMult", "Sel rec evt. vs generated mult; mult", HistType::kTH1F, {multAxis}); registryMC.add("Events/hCentRecVsGenMult", "Rec cent vs gen mid-rapidity multiplicity; centrality; mult", HistType::kTH2F, {centralityAxis, multAxis}); registryMC.add("Events/hCentRecFromGenMult", "Centrality from generated mult", HistType::kTH1F, {centralityAxis}); - registryMC.add({"Tracks/Centrality/THnDCAxyBestRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestRecFake", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); @@ -842,6 +859,53 @@ struct DndetaMFTPbPb { /// Particles using ParticlesIdx = soa::Join; + template + float getMult(C const& collision) + { + float val = CInvalid; + switch (gConf.multEstimator.value) { + case MultE::CnoMult: + return val; + case MultE::CmultFT0C: + if constexpr (!isMC) { + return collision.centFT0C(); + } else { + return collision.multMCFT0C(); + } + break; + case MultE::CmultFT0M: + if constexpr (!isMC) { + return collision.centFT0M(); + } else { + return collision.multMCFT0C() + collision.multMCFT0A(); + } + break; + case MultE::CmultTPC: + if constexpr (!isMC) { + return collision.multTPC(); + } else { + return collision.multMCNParticlesEta08(); + } + break; + case MultE::CmultMFT: + if constexpr (!isMC) { + return collision.centMFT(); + } else { + return CInvalid; + // return collision.multMCMFT(); // FIXME: uncomment when MC MFT mult is in available mcCentralityModule.cxx or in aod::MultMCExtras + } + break; + default: + LOGF(fatal, "No valid multiplicity estimator: %s", gConf.multEstimator.value); + return val; + } + } + + float getMultMC(CollsMCExtraMult::iterator const& collision) + { + return getMult(collision); + } + template float getDCAz(const T& track) { @@ -1403,7 +1467,7 @@ struct DndetaMFTPbPb { } template - void fillHistMC(P const& particles, float zvtx, float c, float occ, bool const gtZeroColl) + void fillHistMC(P const& particles, float zvtx, float c, float occ, bool const gtZeroColl, bool const gtOneRec) { for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { @@ -1430,6 +1494,15 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); } } + if (gtOneRec) { + if constexpr (isCent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGenRecEvt"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGenRecEvt"), phi, particle.eta(), c, occ); + } else { + registryMC.fill(HIST("Tracks/EtaZvtxGenRecEvt"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGenRecEvt"), phi, particle.eta(), occ); + } + } } } @@ -1678,9 +1751,11 @@ struct DndetaMFTPbPb { float occGen = -1.; for (const auto& collision : collisions) { if (!gConf.useEvtSel || isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } } } } @@ -1692,9 +1767,11 @@ struct DndetaMFTPbPb { float crecMin = 999.; for (const auto& collision : collisions) { if (!gConf.useEvtSel || isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } @@ -1704,36 +1781,39 @@ struct DndetaMFTPbPb { } bool gtZeroColl = false; + bool gtOneRec = false; int gtOneColl = 0; for (auto const& collision : collisions) { - float cRec = getRecoCent(collision); - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!gConf.useEvtSel || isGoodEvent(collision)) { - centRecPerCol.emplace_back(cRec); - occRecPerCol.emplace_back(occRec); gtZeroColl = true; ++gtOneColl; if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); + if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); @@ -1805,9 +1885,9 @@ struct DndetaMFTPbPb { if (gConf.fillTrackQaHist) { for (auto i = 0U; i < nRecPerCol.size(); ++i) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); } } if constexpr (has_reco_cent) { @@ -1846,6 +1926,13 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } if (collisions.size() == 0) { if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); @@ -1854,7 +1941,7 @@ struct DndetaMFTPbPb { } } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, @@ -1890,10 +1977,12 @@ struct DndetaMFTPbPb { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); float occGen = -1.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } } } } @@ -1904,10 +1993,12 @@ struct DndetaMFTPbPb { if constexpr (has_reco_cent) { float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } @@ -1919,36 +2010,39 @@ struct DndetaMFTPbPb { buildLookupTable(collisions); bool gtZeroColl = false; + bool gtOneRec = false; int gtOneColl = 0; for (auto const& collision : collisions) { - float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); - float cRec = getRecoCent(collision); if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!gConf.useEvtSel || isGoodEvent(collision)) { - centRecPerCol.emplace_back(cRec); - occRecPerCol.emplace_back(occRec); gtZeroColl = true; ++gtOneColl; if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (!collision.has_mcCollision()) { continue; } if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), occRec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); + if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); @@ -2069,9 +2163,9 @@ struct DndetaMFTPbPb { if (gConf.fillTrackQaHist) { for (auto i = 0U; i < nRecPerCol.size(); ++i) { if constexpr (has_reco_cent) { - registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); } else { - registryMC.fill(HIST("Events/NtrkZvtxGen"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); } } if constexpr (has_reco_cent) { @@ -2110,6 +2204,13 @@ struct DndetaMFTPbPb { } else { registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } if (collisions.size() == 0) { if constexpr (has_reco_cent) { registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); @@ -2118,7 +2219,7 @@ struct DndetaMFTPbPb { } } } - fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl); + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } void processMcBestInclusive(CollsMCExtraMult::iterator const& mccollision, @@ -2982,7 +3083,17 @@ struct DndetaMFTPbPb { aod::McParticles const& particles) { LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenAll)); + float multMC{}; + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto multMCNParticlesEtaMFT = countPart(perCollMCsample); + if (gConf.useMultMCatMidRapidity) { + multMC = mcCollision.multMCNParticlesEta08(); + } else if (gConf.useMultMCatMFTacc) { + multMC = multMCNParticlesEtaMFT; + } else { + multMC = getMultMC(mcCollision); + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenAll)); registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtAll)); if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { return; @@ -3000,7 +3111,7 @@ struct DndetaMFTPbPb { return; } registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0wMft)); - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSel)); // Evt loss den + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSel)); // Evt loss den bool gtZeroColl = false; float cRec = CInvalid; @@ -3011,29 +3122,24 @@ struct DndetaMFTPbPb { if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenSplit)); - registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), mcCollision.multMCFT0C()); + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSplit)); + registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), multMC); cRec = getRecoCent(collision); gtZeroColl = true; } - auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 1., cRec); // Evt split den - registryMC.fill(HIST("Events/hMultGenVsCent"), cRec, mcCollision.multMCFT0C()); - registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); - registryMC.fill(HIST("Events/hMultGenVsCentNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); + registryMC.fill(HIST("Events/hMultGenVsCent"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentMFTacc"), multMCNParticlesEtaMFT, cRec); if (gtZeroColl) { if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { return; } - registryMC.fill(HIST("Events/hNchGen"), mcCollision.multMCFT0C(), static_cast(EvtLossType::kGenRecEvt)); // Evt loss num - registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 2., cRec); // Evt split num - registryMC.fill(HIST("Events/hMultGenVsCentRec"), cRec, mcCollision.multMCFT0C()); - registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEta05"), cRec, mcCollision.multMCNParticlesEta05()); - registryMC.fill(HIST("Events/hMultGenVsCentRecNParticlesEtaMFT"), cRec, multMCNParticlesEtaMFT); + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenRecEvt)); // Evt loss num + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/hMultGenVsCentRecEvt"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentRecEvtMFTacc"), multMCNParticlesEtaMFT, cRec); } if (collisions.size() == 0) { registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 3., cRec); @@ -3045,9 +3151,9 @@ struct DndetaMFTPbPb { if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - registryMC.fill(HIST("Tracks/hEtaVsNchGen"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss den + registryMC.fill(HIST("Tracks/hNchGenVsEta"), multMC, particle.eta()); // Sgn loss den if (gtZeroColl) { - registryMC.fill(HIST("Tracks/hEtaVsNchGenRecEvt"), particle.eta(), mcCollision.multMCFT0C()); // Sgn loss num + registryMC.fill(HIST("Tracks/hNchGenVsEtaRecEvt"), multMC, particle.eta()); // Sgn loss num } } } From b0bddaa411d61d47d892681159f8835edd356afe Mon Sep 17 00:00:00 2001 From: Katarzyna <116073883+kgwizdzi@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:47:27 +0100 Subject: [PATCH 486/648] [PWGCF] FU D0 task: bug fixing, unused elements removal, new TrackQA plots (#18498) --- .../Tasks/femtoUniversePairTaskTrackD0.cxx | 379 +++++++++--------- 1 file changed, 179 insertions(+), 200 deletions(-) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index fce3a57e78c..a33284826b7 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -25,8 +25,6 @@ #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/SelectorCuts.h" -#include "Common/Core/RecoDecay.h" - #include #include #include @@ -60,6 +58,8 @@ using namespace o2; using namespace o2::analysis; using namespace o2::analysis::femto_universe; using namespace o2::analysis::femto_universe::efficiency; +using namespace o2::constants::physics; +using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; @@ -70,25 +70,6 @@ const int8_t genPromptD0 = 1; const int8_t genNonPromptD0 = 2; } // namespace -/// Returns deltaPhi value within the range [-pi/2, 3/2*pi] -/// -double getDeltaPhi(double phiD, double phiDbar) -{ - return RecoDecay::constrainAngle(phiDbar - phiD, -o2::constants::math::PIHalf); -} - -/// Returns deltaPhi value within the range [0, pi] -/// -double wrapDeltaPhi0PI(double phiD, double phiDbar) -{ - double deltaPhi = 0.0; - deltaPhi = RecoDecay::constrainAngle(phiDbar - phiD, 0.0); - if (deltaPhi > o2::constants::math::TwoPI) { - deltaPhi = o2::constants::math::TwoPI - deltaPhi; - } - return deltaPhi; -} - struct FemtoUniversePairTaskTrackD0 { Service pdgMC{}; @@ -99,13 +80,13 @@ struct FemtoUniversePairTaskTrackD0 { Preslice perCol = aod::femtouniverseparticle::fdCollisionId; Preslice perColFullSigma = aod::femtouniverseparticle::fdCollisionId; - using FemtoMcRecoParticles = soa::Join; - Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; + using FemtoMcParticles = soa::Join; + Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; /// Table for both particles struct : o2::framework::ConfigurableGroup { Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; - Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This configurable needs to be the same as the one used in the producer task"}; + Configurable confIsDebug{"confIsDebug", true, "Enable filling additional histograms for track and D0"}; Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; Configurable confPhiBins{"confPhiBins", 29, "Number of phi bins in deta dphi"}; Configurable confEtaBins{"confEtaBins", 29, "Number of eta bins in deta dphi"}; @@ -119,7 +100,6 @@ struct FemtoUniversePairTaskTrackD0 { struct : o2::framework::ConfigurableGroup { Configurable confIsSame{"confIsSame", false, "Pairs of the same particle"}; Configurable confPDGCodeTrack{"confPDGCodeTrack", 2212, "Particle 2 - PDG code"}; - Configurable confPIDTrack{"confPIDTrack", 2, "Particle 2 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> Configurable confTrackSign{"confTrackSign", 1, "Track sign"}; Configurable confIsTrackIdentified{"confIsTrackIdentified", true, "Enable PID for the track"}; Configurable confTrackLowPtCut{"confTrackLowPtCut", 0.5, "Low pT cut of the track"}; @@ -181,10 +161,10 @@ struct FemtoUniversePairTaskTrackD0 { Configurable> binsPtFive{"binsPtFive", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; Configurable confChooseD0trackCorr{"confChooseD0trackCorr", 0, "If 0 correlations with D0s, if 1 with D0bars"}; struct : o2::framework::ConfigurableGroup { + Configurable fillTrackQA{"fillTrackQA", false, "Fill additional histograms for track in PurityQA process"}; // Correlated background for D0/D0bar candidates Configurable fillCorrBkgs{"fillCorrBkgs", false, "Fill histograms with correlated background candidates"}; // Configurable to enable filling histograms with BDT scores values - Configurable fillBDTvsPt{"fillBDTvsPt", true, "Fill BDT vs pT histograms for D0/D0bar candidates"}; Configurable fillBDTvsPtVsMass{"fillBDTvsPtVsMass", false, "Fill BDT vs pT vs mass histograms for D0/D0bar candidates"}; } ConfFill; // Efficiency @@ -193,33 +173,29 @@ struct FemtoUniversePairTaskTrackD0 { /// Partitions for particle 1 Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); /// Partitions for particle 2 /// Partition with all D0/D0bar mesons (which pass one and double mass hypothesis) Partition partsAllDmesons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0 mesons only (one and double mass hypothesis) - Partition partsAllD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); - /// Partition with D0 mesons only (one mass hypothesis) - Partition partsD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < 0.0f) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0s selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0sFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bar mesons only (one and double mass hypothesis) - Partition partsAllD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); - /// Partition with D0bar mesons only (one mass hypothesis) - Partition partsD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda < 0.0f) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bars selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0barsFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition for MC Reco prompt D0/D0bar mesons - Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); /// Partition for MC Reco prompt D0/D0bar mesons (sideband candidates) - Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); // MC Truth D0/D0bar candidates - Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); /// Partition for D0/D0bar daughters Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0Child); @@ -232,10 +208,6 @@ struct FemtoUniversePairTaskTrackD0 { /// Histogramming for Event FemtoUniverseEventHisto eventHisto; - /// The configurables need to be passed to an std::vector - int vPIDTrack = 0; - std::vector kNsigma; - /// particle part ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarInvMassBins{"confTempFitVarInvMassBins", {6000, 0.9, 4.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; @@ -244,7 +216,6 @@ struct FemtoUniversePairTaskTrackD0 { /// Correlation part ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task - // ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; @@ -308,9 +279,9 @@ struct FemtoUniversePairTaskTrackD0 { {{"hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0D0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, - {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, + {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, {"hEtaD0D0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, @@ -412,39 +383,32 @@ struct FemtoUniversePairTaskTrackD0 { efficiencyCalculator.init(); eventHisto.init(&qaRegistry); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0bar", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0bar", "#bar{D^{0}} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("D0_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("Hadron/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); @@ -457,6 +421,20 @@ struct FemtoUniversePairTaskTrackD0 { qaRegistry.add("Hadron/nSigmaTOFKa", "; #it{p} (GeV/#it{c}); n#sigma_{TOFKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); qaRegistry.add("Hadron/nSigmaCombKa", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Ka}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + if (doprocessPurityQA && ConfFill.fillTrackQA) { + qaRegistry.add("TrackQA/hCharge", "; Charge; Entries", kTH1F, {{5, -2.5, 2.5}}); + qaRegistry.add("TrackQA/hTPCfindable", "; TPC findable clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfound", "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCcrossedOverFindable", "; TPC ratio findable; Entries", kTH1F, {{150, 0., 1.5}}); + qaRegistry.add("TrackQA/hTPCcrossedRows", "; TPC crossed rows; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfindableVsCrossed", ";TPC findable clusters ; TPC crossed rows;", kTH1F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCshared", "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfractionSharedCls", "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 1.0}}); + qaRegistry.add("TrackQA/hITSclusters", "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hITSclustersIB", "; ITS clusters in IB; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hDCAz", "; #it{p}_{T} (GeV/#it{c}); DCA_{z} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + qaRegistry.add("TrackQA/hTPCdEdX", "; #it{p} (GeV/#it{c}); TPC Signal", kTH2F, {{100, 0, 10}, {1000, 0, 1000}}); + } if (doprocessPurityQA && std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { qaRegistry.add("PurityQA/nSigmaFullTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); qaRegistry.add("PurityQA/nSigmaFullTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); @@ -504,20 +482,18 @@ struct FemtoUniversePairTaskTrackD0 { registry.add("DebugBdt/hBdtScore0", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); registry.add("DebugBdt/hBdtScore1", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); registry.add("DebugBdt/hBdtScore2", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); - if (ConfFill.fillBDTvsPt) { - registry.add("DebugBdtMcReco/hBdtScoresVsPtD0", "Thn for reconstructed D0 candidates", HistType::kTHnSparseD, axesTHnMc); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtD0bar", "Thn for reconstructed D0bar candidates", HistType::kTHnSparseD, axesTHnMc); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0", "Thn for reconstructed prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0", "Thn for reconstructed non-prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar", "Thn for reconstructed prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"))->Sumw2(); - registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar", "Thn for reconstructed non-prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); - registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"))->Sumw2(); - } + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0", "Thn for reconstructed D0 candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0bar", "Thn for reconstructed D0bar candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0", "Thn for reconstructed prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0", "Thn for reconstructed non-prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar", "Thn for reconstructed prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar", "Thn for reconstructed non-prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"))->Sumw2(); if (ConfFill.fillBDTvsPtVsMass) { registry.add("DebugBdt/hBdtScoresVsMassVsPtD0", "Thn for D0 candidates", HistType::kTHnSparseD, axesTHn); registry.get(HIST("DebugBdt/hBdtScoresVsMassVsPtD0"))->Sumw2(); @@ -531,16 +507,16 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.add("hMcRecD0PromptPt", "MC Reco prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0NonPrompt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0NonPromptPt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); mcRecoRegistry.add("hMcRecD0bar", "MC Reco all D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPt", "MC Reco all D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barPrompt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPromptPt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barNonPrompt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barNonPromptPt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); // Inv. mass histograms mcRecoRegistry.add("hMassVsPtD0Sig", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0Refl", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -609,8 +585,8 @@ struct FemtoUniversePairTaskTrackD0 { mcTruthRegistry.add("hMcGenPrPt", "MC Truth proton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); mcTruthRegistry.add("hMcGenAntiPrPt", "MC Truth antiproton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); - trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack); - trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0); + trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack, ConfBothTracks.confIsDebug); + trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0, ConfBothTracks.confIsDebug); mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); @@ -626,9 +602,6 @@ struct FemtoUniversePairTaskTrackD0 { if (ConfCPR.confIsCPR.value) { pairCloseRejection.init(&resultRegistry, &qaRegistry, ConfBothTracks.confDeltaEtaAxis, ConfBothTracks.confDeltaPhiStarAxis, ConfCPR.confCPRdeltaPhiCutMin.value, ConfCPR.confCPRdeltaPhiCutMax.value, ConfCPR.confCPRdeltaEtaCutMin.value, ConfCPR.confCPRdeltaEtaCutMax.value, ConfCPR.confCPRChosenRadii.value, ConfCPR.confCPRPlotPerRadii.value); } - - vPIDTrack = ConfTrack.confPIDTrack.value; - kNsigma = ConfBothTracks.confTrkPIDnSigmaMax.value; } template @@ -638,34 +611,6 @@ struct FemtoUniversePairTaskTrackD0 { eventHisto.fillQA(col); } - void processQAD0D0barSel(o2::aod::FdCollision const& col, FemtoFullParticles const&) - { - auto groupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - - // loop over selected D0 candidates - for (auto const& d0cand : groupPartsD0s) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0"), d0cand.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0"), d0cand.pt()); - } - // loop over selected D0bar candidates - for (auto const& d0barcand : groupPartsD0bars) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0bar"), d0barcand.mAntiLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0bar"), d0barcand.pt()); - } - - // loop over selected D0 candidates from SB regions - for (auto const& d0SB : groupPartsD0sFromSB) { - - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hInvMassD0"), d0SB.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hPtD0"), d0SB.pt()); - } - } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processQAD0D0barSel, "Enable filling QA plots for selected D0/D0bar cand.", true); - void processAllDmesons(o2::aod::FdCollision const& col, FemtoFullParticles const&) { auto groupPartsAllD0D0barCands = partsAllDmesons->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); @@ -863,31 +808,31 @@ struct FemtoUniversePairTaskTrackD0 { } } - /// process function for to call doSameEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// process function to call doSameEvent with Data + /// call this process function if you D0-track pairs /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable - void processSameEvent(o2::aod::FdCollision const& col, - FemtoFullParticles const& parts) + void processSameEventSigD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); - auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - switch (confChooseD0trackCorr) { - case 0: - doSameEvent(theGroupPartsTrack, theGroupPartsD0s, parts, col.magField(), col.multNtr()); - break; - case 1: - doSameEvent(theGroupPartsTrack, theGroupPartsD0bars, parts, col.magField(), col.multNtr()); - break; - default: - break; - } + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEvent, "Enable processing same event", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0, "Enable processing same event for signal D0-track pairs", false); + + void processSameEventSigD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0bar, "Enable processing same event for signal D0bar-track pairs", false); /// process function for to call doSameEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -899,8 +844,8 @@ struct FemtoUniversePairTaskTrackD0 { fillCollision(col); auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -915,17 +860,39 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventWithDoubleHypo, "Enable processing same event", false); - /// process function for to call doSameEvent with Data - /// call this process to obtain the function for D0/D0bar candidates from side-band regions + /// process function to call doSameEvent with Data + /// call processSameEventSideBandD0 to obtain the function for D0 candidates from side-band regions /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEventSideBandD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0, "Enable processing same event for sideband D0-track pairs", false); + + void processSameEventSideBandD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0bar, "Enable processing same event for sideband D0bar-track pairs", false); + void processSameEventSB(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -944,7 +911,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -969,7 +936,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -994,7 +961,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table /// \param parts subscribe to the joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FdMCParticles subscribe to the Monte Carlo truth table - void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); /// !!! needs to be finished -> add possibility to change between D0 and D0bar @@ -1065,20 +1032,18 @@ struct FemtoUniversePairTaskTrackD0 { } /// process function for to call doMixedEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// call this process function if you need D0-track ME function /// @param cols subscribe to the collisions table (Data) /// @param parts subscribe to the femtoUniverseParticleTable - void processMixedEvent(o2::aod::FdCollisions const& cols, - FemtoFullParticles const& parts) + void processMixedEventSigD0(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multNtr(); mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); - auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1086,22 +1051,31 @@ struct FemtoUniversePairTaskTrackD0 { if (magFieldTesla1 != magFieldTesla2) { continue; } - /// \todo before mixing we should check whether both collisions contain a pair of particles! - // if (partsD0.size() == 0 || kNPart2Evt1 == 0 || kNPart1Evt2 == 0 || partsTrack.size() == 0 ) continue; + doMixedEvent(groupedTracks, groupedD0s, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0, "Enable processing signal D0-track mixed events", false); - switch (confChooseD0trackCorr) { - case 0: - doMixedEvent(groupPartsTrack, theGroupPartsD0s, parts, magFieldTesla1, multiplicityCol); - break; - case 1: - doMixedEvent(groupPartsTrack, theGroupPartsD0bars, parts, magFieldTesla1, multiplicityCol); - break; - default: - break; + void processMixedEventSigD0bar(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) + { + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; } + doMixedEvent(groupedTracks, groupedD0bars, parts, magFieldTesla1, multiplicityCol); } } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEvent, "Enable processing mixed events", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0bar, "Enable processing signal D0bar-track mixed events", false); /// process function for to call doMixedEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -1116,8 +1090,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1154,8 +1128,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1183,7 +1157,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1219,7 +1193,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1255,7 +1229,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1287,7 +1261,7 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventMCTruth, "MC Truth - enable processing mixed events for gen. D0/D0bar", false); - void processMcReco(FemtoMcRecoParticles const& recoParts, aod::FdMCParticles const& mcParts) + void processMcReco(FemtoMcParticles const& recoParts, aod::FdMCParticles const& mcParts) { for (auto const& part : recoParts) { auto mcPartId = part.fdMCParticleId(); @@ -1298,40 +1272,40 @@ struct FemtoUniversePairTaskTrackD0 { weight = 1.0f; // filling the histograms for identified hadrons - if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && (std::abs(part.eta()) < ConfTrack.confTrackEtaMax)) { if (part.sign() > 0) { - if ((mcpart.pdgMCTruth() == PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPipPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPipPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKpPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKpPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } } else if (part.sign() < 0) { - if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPimPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPimPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKmPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKmPtGenEtaGen"), mcpart.pt(), mcpart.eta()); @@ -1350,16 +1324,12 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.fill(HIST("hMcRecD0Prompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPhi"), part.phi()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0NonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPhi"), part.phi()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } else if (mcpart.pdgMCTruth() == ConfDmesons.confPDGCodeD0bar) { mcRecoRegistry.fill(HIST("hMcRecD0bar"), part.pt(), part.eta()); @@ -1370,15 +1340,11 @@ struct FemtoUniversePairTaskTrackD0 { if (part.tpcNClsFound() == 0) { // prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barPromptPt"), part.pt()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barNonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barNonPromptPt"), part.pt()); - if (ConfFill.fillBDTvsPt) { - registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); - } + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } } @@ -1386,7 +1352,7 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMcReco, "Process MC reco data", false); - void processMcRecoD0InvMass(FemtoMcRecoParticles const& recoParts) + void processMcRecoD0InvMass(FemtoMcParticles const& recoParts) { for (auto const& part : recoParts) { // filling the histograms for identified hadrons @@ -1432,13 +1398,13 @@ struct FemtoUniversePairTaskTrackD0 { } if (ConfFill.fillCorrBkgs) { if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0) { // D0 -> pi+K-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPi) { // D0 -> pi+pi- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPiPi0) { // D0 -> pi+pi-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToKK) { // D0 -> K+K- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mAntiLambda(), part.pt(), weight); } } if (part.tpcNClsFound() == 0) { // prompt candidates @@ -1538,8 +1504,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable - void processPurityQA(aod::FdCollision const& col, - FemtoFullSigmaParticles const&) + void processPurityQA(aod::FdCollision const& col, FemtoFullSigmaParticles const&) { fillCollision(col); @@ -1555,18 +1520,32 @@ struct FemtoUniversePairTaskTrackD0 { } trackHistoPartTrack.fillQA(part); + if (ConfFill.fillTrackQA) { + qaRegistry.fill(HIST("TrackQA/hCharge"), part.sign()); + qaRegistry.fill(HIST("TrackQA/hTPCfindable"), part.tpcNClsFindable()); + qaRegistry.fill(HIST("TrackQA/hTPCfound"), part.tpcNClsFound()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedOverFindable"), part.tpcCrossedRowsOverFindableCls()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedRows"), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCfindableVsCrossed"), part.tpcNClsFindable(), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCshared"), part.tpcNClsShared()); + qaRegistry.fill(HIST("TrackQA/hTPCfractionSharedCls"), part.tpcFractionSharedCls()); + qaRegistry.fill(HIST("TrackQA/hITSclusters"), part.itsNCls()); + qaRegistry.fill(HIST("TrackQA/hITSclustersIB"), part.itsNClsInnerBarrel()); + qaRegistry.fill(HIST("TrackQA/hDCAz"), part.pt(), part.dcaZ()); + qaRegistry.fill(HIST("TrackQA/hTPCdEdX"), part.p(), part.tpcSignal()); + } if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPr"), part.p(), part.tpcFullNSigmaPr()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPr"), part.p(), part.tofFullNSigmaPr()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPr"), part.p(), std::hypot(part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPr"), part.pt(), part.tpcFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPr"), part.pt(), part.tofFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPr"), part.pt(), std::hypot(part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())); } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kPiPlus) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPi"), part.p(), part.tpcFullNSigmaPi()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPi"), part.p(), part.tofFullNSigmaPi()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPi"), part.p(), std::hypot(part.tpcFullNSigmaPi(), part.tofFullNSigmaPi())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPi"), part.pt(), part.tpcFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPi"), part.pt(), part.tofFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPi"), part.pt(), std::hypot(part.tpcFullNSigmaPi(), part.tofFullNSigmaPi())); } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kKPlus) { - qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCKa"), part.p(), part.tpcFullNSigmaKa()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFKa"), part.p(), part.tofFullNSigmaKa()); - qaRegistry.fill(HIST("PurityQA/nSigmaFullCombKa"), part.p(), std::hypot(part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCKa"), part.pt(), part.tpcFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFKa"), part.pt(), part.tofFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombKa"), part.pt(), std::hypot(part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())); } } } From 32ad63f4166846db6efb7261482f576a88775ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:12:24 +0100 Subject: [PATCH 487/648] [Infrastructure] Demote `deprecated-declarations` (#18504) --- dependencies/O2PhysicsCompileFlags.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/O2PhysicsCompileFlags.cmake b/dependencies/O2PhysicsCompileFlags.cmake index 111ddb1bacd..a68cf793aa3 100644 --- a/dependencies/O2PhysicsCompileFlags.cmake +++ b/dependencies/O2PhysicsCompileFlags.cmake @@ -14,7 +14,7 @@ include_guard() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # Enabled warnings supported by Clang and GCC, not treated as errors -set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "") +set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "deprecated-declarations") # Enabled warnings supported by Clang only, not treated as errors set(O2PHYSICS_WARNINGS_CLANG_NO_ERROR "vla-extension") From 3064f6d772df4201129d980533fd3a765a1950b8 Mon Sep 17 00:00:00 2001 From: spolitan <59452587+stefanopolitano@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:56:42 +0100 Subject: [PATCH 488/648] [PWGHF] Add missing bc dependence in flow task (#18508) --- PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx index 6ac6453d025..c7b099f64ea 100644 --- a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx @@ -963,7 +963,8 @@ struct HfTaskFlowCharmHadrons { // Dstar with ML void processDstarMl(CollsWithSPQvecs::iterator const& collision, CandDstarDataWMl const& candidatesDstar, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { runFlowAnalysis(collision, candidatesDstar, tracks); } From da1f39516ec6099a800a39ee8ed8ba45ef035acd Mon Sep 17 00:00:00 2001 From: Maxim Virta <84773378+MaximVirta@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:11:52 +0100 Subject: [PATCH 489/648] [PWGCF] Bitmask selections updated for the V02 analysis code (#18506) Co-authored-by: Maxim Virta Co-authored-by: ALICE Action Bot --- PWGCF/GenericFramework/Tasks/flowGfwV02.cxx | 64 ++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx index 327372e0bc3..90ae8e00762 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx @@ -21,6 +21,7 @@ #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -214,8 +215,8 @@ struct FlowGfwV02 { // // Filters to be applied to the received data. // // The analysis assumes the data has been subjected to a QA of its selection, // // and thus only the final distributions of the data for analysis are saved. - // Derived-data bit masks. Every listed track bit is required; see gfwcollision::EventSel and gfwtrack::TrackSelection. - O2_DEFINE_CONFIGURABLE(cfgEventBitMask, uint16_t, static_cast(o2::aod::gfwcollision::DefaultEventMask), "Required event-selection bits for GFW derived data"); + // Derived-data track mask. Every listed track bit is required; see gfwtrack::TrackSelection. + // The event mask is built from cfgEventCutFlags when a GFW derived process is enabled. Configurable> cfgBitmaskSelections{"cfgBitmaskSelections", {0, 3, 6, 9, 12}, "Enabled TrackSelection bit indices for GFW derived data. DCA 0-5, TPC track 6-13, TPC PID 14-22, TOF PID 23-31. The default is the nominal quality selection"}; O2_DEFINE_CONFIGURABLE(cfgPidWindow, int, static_cast(o2::aod::gfwtrack::PidWindowNominal), "PID nSigma window for GFW derived data: 0 nominal, 1 tight, 2 loose"); @@ -300,6 +301,7 @@ struct FlowGfwV02 { std::unique_ptr fPtDepDCAxy; uint32_t trackBitMask = o2::aod::gfwtrack::NominalMask; + uint16_t eventBitMask = o2::aod::gfwcollision::DefaultEventMask; using GFWTracks = soa::Filtered>; @@ -319,6 +321,52 @@ struct FlowGfwV02 { IndProtonLow }; + // Required gfwcollision::EventSel bits for derived data, taken from the event-cut configurables. + uint16_t eventSelectionMask() + { + uint16_t mask = 0; + if (cfgEventCutFlags.cfgNoSameBunchPileupCut) { + mask |= o2::aod::gfwcollision::kNoSameBunchPileup; + } + if (cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV) { + mask |= o2::aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (cfgEventCutFlags.cfgNoCollInTimeRangeStandard) { + mask |= o2::aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (cfgEventCutFlags.cfgNoCollInRofStandard) { + mask |= o2::aod::gfwcollision::kNoCollInRofStandard; + } + if (cfgEventCutFlags.cfgNoHighMultCollInPrevRof) { + mask |= o2::aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (cfgEventCutFlags.cfgIsVertexITSTPC) { + mask |= o2::aod::gfwcollision::kIsVertexITSTPC; + } + if (cfgEventCutFlags.cfgIsGoodITSLayersAll) { + mask |= o2::aod::gfwcollision::kIsGoodITSLayersAll; + } + if (cfgEventCutFlags.cfgNoTimeFrameBorder) { + mask |= o2::aod::gfwcollision::kNoTimeFrameBorder; + } + if (cfgEventCutFlags.cfgNoITSROFrameBorder) { + mask |= o2::aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgEventCutFlags.cfgTVXinTRD) { + mask |= o2::aod::gfwcollision::kNotTVXinTRD; + } + if (cfgTrackCuts.cfgOccupancySelection >= 0) { + mask |= o2::aod::gfwcollision::kOccupancy; + } + if (cfgUseAdditionalEventCut) { + mask |= o2::aod::gfwcollision::kGoodVertex; + if (cfgMultCut) { + mask |= o2::aod::gfwcollision::kMultCorr; + } + } + return mask; + } + void init(InitContext const&) { trackBitMask = 0; @@ -327,6 +375,10 @@ struct FlowGfwV02 { trackBitMask |= static_cast(1u << bit); } } + if (doprocessDerived || doprocessDerivedCorrected) { + eventBitMask = eventSelectionMask(); + LOGF(info, "GFW derived event-selection mask: %u", static_cast(eventBitMask)); + } projectMatrix(nSigmas->getData(), pidStates.tpcNsigmaCut, pidStates.tofNsigmaCut, pidStates.itsNsigmaCut); projectMatrix(nSigmasHighPurity->getData(), pidStates.highPurityTpcNsigmaCut, pidStates.highPurityTofNsigmaCut, pidStates.highPurityItsNsigmaCut); @@ -816,7 +868,7 @@ struct FlowGfwV02 { bool eventSelected(TCollision const& collision, const int& multTrk, const float& centrality) { if (cfgEventCutFlags.cfgTVXinTRD) { - if (collision.alias_bit(kTVXinTRD)) { + if (collision.alias_bit(::kTVXinTRD)) { // TRD triggered // "CMTVX-B-NOPF-TRD,minbias_TVX" return 0; @@ -1520,8 +1572,7 @@ struct FlowGfwV02 { } loadCorrections(run); - const uint16_t eventMask = cfgEventBitMask; - if ((collision.eventSel() & eventMask) != eventMask) { + if ((collision.eventSel() & eventBitMask) != eventBitMask) { return; } if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { @@ -1543,8 +1594,7 @@ struct FlowGfwV02 { LOGF(info, "run = %d", run); } - const uint16_t eventMask = cfgEventBitMask; - if ((collision.eventSel() & eventMask) != eventMask) { + if ((collision.eventSel() & eventBitMask) != eventBitMask) { return; } if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { From 8464379a64d254f8bb05f9bfffc5b05308cd7fc9 Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Thu, 29 Oct 2026 20:17:33 +0100 Subject: [PATCH 490/648] [PWGEM] Add initialization for RCT checks (#18507) Co-authored-by: jokonig --- PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx index 3973884823d..172e093488a 100644 --- a/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx +++ b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx @@ -164,6 +164,9 @@ struct MesonSignalLoss { } else if (particleConfig.cfgEnableMultiplicityHistograms) { LOGF(fatal, "Unknown cfgMultiplicityEstimator '%s'; use centralBarrel, FT0A, or FT0C", particleConfig.cfgMultiplicityEstimator.value.c_str()); } + if (rctConfig.cfgRequireGoodRCT) { + rctChecker.init(rctConfig.cfgRCTLabel.value, rctConfig.cfgCheckZDC.value, rctConfig.cfgTreatLimitedAcceptanceAsBad.value); + } Inithistograms(); } From da3e833c75fa04489e42b2c7428f40d03c506f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:36:57 +0100 Subject: [PATCH 491/648] [PWGJE] Fix casts (#18389) --- PWGJE/Core/JetFindingUtilities.h | 5 ++- PWGJE/Core/JetTaggingUtilities.h | 4 +-- PWGJE/JetFinders/jetFinder.h | 16 ++++----- PWGJE/JetFinders/jetFinderHF.h | 14 ++++---- PWGJE/JetFinders/jetFinderHFHFBar.h | 14 ++++---- PWGJE/JetFinders/jetFinderV0.h | 12 +++---- .../eventwiseConstituentSubtractor.cxx | 6 ++-- PWGJE/TableProducer/jetTaggerHF.cxx | 26 +++++++------- PWGJE/TableProducer/mcOutlierRejector.cxx | 2 +- .../TableProducer/quarkGluonJetsProducer.cxx | 2 +- PWGJE/TableProducer/slimTablesProducer.cxx | 4 +-- PWGJE/Tasks/bjetCentMult.cxx | 4 +-- PWGJE/Tasks/bjetTaggingGnn.cxx | 6 ++-- PWGJE/Tasks/bjetTaggingML.cxx | 4 +-- PWGJE/Tasks/bjetTreeCreator.cxx | 8 ++--- PWGJE/Tasks/chargedJetHadron.cxx | 4 +-- PWGJE/Tasks/dijetFinderQA.cxx | 4 +-- PWGJE/Tasks/embeddingQA.cxx | 4 +-- PWGJE/Tasks/fullJetSpectra.cxx | 12 +++---- PWGJE/Tasks/gammaJetTreeProducer.cxx | 4 +-- PWGJE/Tasks/hadronPhotonCorrelation.cxx | 22 ++++++------ PWGJE/Tasks/hfFragmentationFunction.cxx | 2 +- PWGJE/Tasks/jetBackgroundAnalysis.cxx | 36 +++++++++---------- PWGJE/Tasks/jetChargedV2.cxx | 4 +-- PWGJE/Tasks/jetCorrelationD0.cxx | 2 +- PWGJE/Tasks/jetCorrelationDmeson.cxx | 2 +- PWGJE/Tasks/jetCrossSectionEfficiency.cxx | 4 +-- PWGJE/Tasks/jetD0AngSubstructure.cxx | 4 +-- PWGJE/Tasks/jetDebug.cxx | 4 +-- PWGJE/Tasks/jetDsSpecSubs.cxx | 4 +-- PWGJE/Tasks/jetFinderFullQA.cxx | 8 ++--- PWGJE/Tasks/jetFinderHFQA.h | 8 ++--- PWGJE/Tasks/jetFinderQA.cxx | 24 ++++++------- PWGJE/Tasks/jetFinderV0QA.cxx | 6 ++-- PWGJE/Tasks/jetHFAngularity.cxx | 4 +-- PWGJE/Tasks/jetHadronRecoil.cxx | 4 +-- PWGJE/Tasks/jetHadronsPid.cxx | 2 +- PWGJE/Tasks/jetLundReclustering.cxx | 2 +- PWGJE/Tasks/jetOutlierQA.cxx | 34 +++++++++--------- PWGJE/Tasks/jetPlanarFlow.cxx | 6 ++-- PWGJE/Tasks/jetShape.cxx | 10 +++--- PWGJE/Tasks/jetSpectraCharged.cxx | 4 +-- PWGJE/Tasks/jetSpectraEseTask.cxx | 4 +-- PWGJE/Tasks/jetSubstructure.cxx | 2 +- PWGJE/Tasks/jetSubstructureHF.h | 2 +- PWGJE/Tasks/jetSubstructureHFOutput.h | 4 +-- PWGJE/Tasks/jetSubstructureOutput.cxx | 2 +- PWGJE/Tasks/jetTaggerHFQA.cxx | 4 +-- PWGJE/Tasks/jetTriggerChargedQa.cxx | 2 +- PWGJE/Tasks/jetTutorial.cxx | 4 +-- PWGJE/Tasks/jetTutorialSkeleton.cxx | 4 +-- PWGJE/Tasks/jetValidationQA.cxx | 6 ++-- PWGJE/Tasks/mcGeneratorStudies.cxx | 2 +- PWGJE/Tasks/nucleiInJets.cxx | 4 +-- PWGJE/Tasks/photonChargedTriggerProducer.cxx | 4 +-- PWGJE/Tasks/photonIsolationQA.cxx | 4 +-- PWGJE/Tasks/substructureDebug.cxx | 4 +-- PWGJE/Tasks/trackEfficiency.cxx | 4 +-- 58 files changed, 203 insertions(+), 204 deletions(-) diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index d770380e9e3..eeeae31e7c5 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -284,12 +284,11 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f * @param doHFJetFinding set whether only jets containing a HF candidate are saved */ template -void findJets(JetFinder& jetFinder, std::vector const& inputParticles, float jetPtMin, float jetPtMax, std::vector jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) +void findJets(JetFinder& jetFinder, std::vector const& inputParticles, float jetPtMin, float jetPtMax, std::vector const& jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) { - auto jetRValues = static_cast>(std::move(jetRadius)); jetFinder.jetPtMin = jetPtMin; jetFinder.jetPtMax = jetPtMax; - for (auto R : jetRValues) { + for (auto R : jetRadius) { jetFinder.jetR = R; std::vector jets; fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); diff --git a/PWGJE/Core/JetTaggingUtilities.h b/PWGJE/Core/JetTaggingUtilities.h index 0d5fca0a50a..1d07fd40844 100644 --- a/PWGJE/Core/JetTaggingUtilities.h +++ b/PWGJE/Core/JetTaggingUtilities.h @@ -997,8 +997,8 @@ int vertexClustering(AnyCollision const& collision, AnalysisJet const& jet, AnyT trkLabels["trkOrigin"].push_back(0); // mismatched coll track } else { int orig = RecoDecay::getParticleOrigin(particles, particle, searchUpToQuark); - trkLabels["trkOrigin"].push_back((orig != RecoDecay::OriginType::None) ? static_cast(orig) : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 - : 4); // 1: charm, 2: beauty, 3: primary, 4: other secondary + trkLabels["trkOrigin"].push_back((orig != RecoDecay::OriginType::None) ? orig : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 + : 4); // 1: charm, 2: beauty, 3: primary, 4: other secondary } } diff --git a/PWGJE/JetFinders/jetFinder.h b/PWGJE/JetFinders/jetFinder.h index e23d910b9eb..d673c550fe1 100644 --- a/PWGJE/JetFinders/jetFinder.h +++ b/PWGJE/JetFinders/jetFinder.h @@ -127,13 +127,13 @@ struct JetFinderTask { void init(o2::framework::InitContext const&) { - hadronicCorrectionTypesVec = (std::vector)hadronicCorrectionTypes; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + hadronicCorrectionTypesVec = hadronicCorrectionTypes; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; - clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions.value); + clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -164,8 +164,8 @@ struct JetFinderTask { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -173,7 +173,7 @@ struct JetFinderTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/JetFinders/jetFinderHF.h b/PWGJE/JetFinders/jetFinderHF.h index d09db8e1f47..e0a44e52fee 100644 --- a/PWGJE/JetFinders/jetFinderHF.h +++ b/PWGJE/JetFinders/jetFinderHF.h @@ -134,11 +134,11 @@ struct JetFinderHFTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); - clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; + clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -168,8 +168,8 @@ struct JetFinderHFTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -177,7 +177,7 @@ struct JetFinderHFTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/JetFinders/jetFinderHFHFBar.h b/PWGJE/JetFinders/jetFinderHFHFBar.h index e2b90a0f540..f37e8bd62b6 100644 --- a/PWGJE/JetFinders/jetFinderHFHFBar.h +++ b/PWGJE/JetFinders/jetFinderHFHFBar.h @@ -132,10 +132,10 @@ struct JetFinderHFHFBarTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -165,8 +165,8 @@ struct JetFinderHFHFBarTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -174,7 +174,7 @@ struct JetFinderHFHFBarTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -192,7 +192,7 @@ struct JetFinderHFHFBarTask { registry.add("hJetMCP", "sparse for mcp jets", {o2::framework::HistType::kTHnD, {{jetRadiiBins, ""}, {jetPtBinNumber, jetPtMinDouble, jetPtMaxDouble}, {40, -1.0, 1.0}, {18, 0.0, 7.0}}}); } - o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); diff --git a/PWGJE/JetFinders/jetFinderV0.h b/PWGJE/JetFinders/jetFinderV0.h index 6ed805f7b14..29c95e5f5b9 100644 --- a/PWGJE/JetFinders/jetFinderV0.h +++ b/PWGJE/JetFinders/jetFinderV0.h @@ -116,10 +116,10 @@ struct JetFinderV0Task { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -149,8 +149,8 @@ struct JetFinderV0Task { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -165,7 +165,7 @@ struct JetFinderV0Task { candIndex = 1; } - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx index ca50b715514..18e7f854171 100644 --- a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx +++ b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx @@ -103,9 +103,9 @@ struct eventWiseConstituentSubtractorTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; eventWiseConstituentSubtractor.setDoRhoMassSub(doRhoMassSub); eventWiseConstituentSubtractor.setConstSubAlphaRMax(alpha, rMax); diff --git a/PWGJE/TableProducer/jetTaggerHF.cxx b/PWGJE/TableProducer/jetTaggerHF.cxx index f2daa6a36e9..4f70a9f9d31 100644 --- a/PWGJE/TableProducer/jetTaggerHF.cxx +++ b/PWGJE/TableProducer/jetTaggerHF.cxx @@ -387,46 +387,46 @@ struct JetTaggerHFTask { // Set up the resolution function switch (resoFuncMatch) { case 0: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsCharmJetMC = (std::vector)paramsResoFuncCharmJetMC; - vecParamsBeautyJetMC = (std::vector)paramsResoFuncBeautyJetMC; - vecParamsLfJetMC = (std::vector)paramsResoFuncLfJetMC; + vecParamsData = paramsResoFuncData; + vecParamsCharmJetMC = paramsResoFuncCharmJetMC; + vecParamsBeautyJetMC = paramsResoFuncBeautyJetMC; + vecParamsLfJetMC = paramsResoFuncLfJetMC; LOG(info) << "defined parameters of resolution function: custom"; break; case 1: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsIncJetMC = (std::vector)paramsResoFuncIncJetMC; + vecParamsData = paramsResoFuncData; + vecParamsIncJetMC = paramsResoFuncIncJetMC; useResoFuncFromIncJet = true; LOG(info) << "defined parameters of resolution function: custom & use inclusive distribution"; break; case 2: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {282119.753, -0.065, 0.893, 11.608, 0.945, 8.029, 0.131, 6.244, 0.027}; vecParamsBeautyJetMC = {74901.583, -0.082, 0.874, 10.332, 0.941, 7.352, 0.097, 6.220, 0.022}; vecParamsLfJetMC = {1539435.343, -0.061, 0.896, 13.272, 1.034, 5.884, 0.004, 7.843, 0.090}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k"; break; case 3: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {1908803.027, -0.059, 0.895, 13.467, 1.005, 8.867, 0.098, 6.929, 0.011}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 4: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {743719.121, -0.960, -0.240, 13.765, 1.314, 10.761, 0.293, 8.538, 0.052}; vecParamsBeautyJetMC = {88888.418, 0.256, 1.003, 10.185, 0.740, 8.216, 0.147, 7.228, 0.040}; vecParamsLfJetMC = {414860.372, -1.000, 0.285, 14.561, 1.464, 11.693, 0.339, 9.183, 0.052}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4"; break; case 5: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {2211391.862, 0.360, 1.028, 13.019, 0.650, 11.151, 0.215, 9.462, 0.044}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4 & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 6: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncIncCCDB.size(); j++) { std::vector params; if (resoFuncIncCCDB[j]) { @@ -440,7 +440,7 @@ struct JetTaggerHFTask { useResoFuncFromIncJet = true; break; case 7: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncBeautyCCDB.size(); j++) { std::vector params; if (resoFuncBeautyCCDB[j]) { @@ -584,7 +584,7 @@ struct JetTaggerHFTask { int nSVs = analysisJet.template secondaryVertices_as().size(); - jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), static_cast(nSVs), analysisJet.mass()}; + jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), nSVs, analysisJet.mass()}; tracksParams.resize(nJetConst); // resize to the number of inputs of the ML svsParams.resize(nJetConst); // resize to the number of inputs of the ML diff --git a/PWGJE/TableProducer/mcOutlierRejector.cxx b/PWGJE/TableProducer/mcOutlierRejector.cxx index f0b7672c40a..7a149994a4d 100644 --- a/PWGJE/TableProducer/mcOutlierRejector.cxx +++ b/PWGJE/TableProducer/mcOutlierRejector.cxx @@ -47,7 +47,7 @@ struct McOutlierRejectorTask { std::vector collisionFlag; std::vector mcCollisionFlag; - int trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + int trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); void processSetupCollisionSelection(aod::JCollisions const& collisions) { diff --git a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx index 92c2a05d92b..89535fd176e 100644 --- a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx +++ b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx @@ -239,7 +239,7 @@ struct QuarkGluonJetsProducer { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); } template diff --git a/PWGJE/TableProducer/slimTablesProducer.cxx b/PWGJE/TableProducer/slimTablesProducer.cxx index cbaf139646e..5cff154999b 100644 --- a/PWGJE/TableProducer/slimTablesProducer.cxx +++ b/PWGJE/TableProducer/slimTablesProducer.cxx @@ -146,8 +146,8 @@ struct SlimTablesProducer { histos.add("Ntracks_pT", "track pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); histos.add("Nparticles_pT", "particle pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Produces slimCollisions; diff --git a/PWGJE/Tasks/bjetCentMult.cxx b/PWGJE/Tasks/bjetCentMult.cxx index f7d7b2286e2..9188a4bc256 100644 --- a/PWGJE/Tasks/bjetCentMult.cxx +++ b/PWGJE/Tasks/bjetCentMult.cxx @@ -110,8 +110,8 @@ struct BjetCentMultTask { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; diff --git a/PWGJE/Tasks/bjetTaggingGnn.cxx b/PWGJE/Tasks/bjetTaggingGnn.cxx index 6b3196e04d9..35ace3cc4f4 100644 --- a/PWGJE/Tasks/bjetTaggingGnn.cxx +++ b/PWGJE/Tasks/bjetTaggingGnn.cxx @@ -246,16 +246,16 @@ struct BjetTaggingGnn { void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); eventSelectionBitsTVX = jetderiveddatautilities::initialiseEventSelectionBits("TVX"); eventSelectionBitsSel8 = jetderiveddatautilities::initialiseEventSelectionBits("sel8"); eventSelectionBitsSelMC = jetderiveddatautilities::initialiseEventSelectionBits("selMC"); eventSelectionBitsSel8Full = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full"); eventSelectionBitsSel8FullGood = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full+IsGoodZvtxFT0vsPV"); - trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (doprocessDataJetsTrig) { zorroSummary.setObject(zorro.getZorroSummary()); diff --git a/PWGJE/Tasks/bjetTaggingML.cxx b/PWGJE/Tasks/bjetTaggingML.cxx index 002f3b4bf02..ff58cac44e9 100644 --- a/PWGJE/Tasks/bjetTaggingML.cxx +++ b/PWGJE/Tasks/bjetTaggingML.cxx @@ -89,9 +89,9 @@ struct BJetTaggingML { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); const AxisSpec axisDb{300, -10., 20., "#it{D}_{b}"}; const AxisSpec axisScore{120, -0.1, 1.1, "Score"}; diff --git a/PWGJE/Tasks/bjetTreeCreator.cxx b/PWGJE/Tasks/bjetTreeCreator.cxx index 7e3c70b70f5..7ab7bc11bc6 100644 --- a/PWGJE/Tasks/bjetTreeCreator.cxx +++ b/PWGJE/Tasks/bjetTreeCreator.cxx @@ -294,11 +294,11 @@ struct BJetTreeCreator { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; - jetPtBinsReduction = (std::vector)jetPtBins; - jetReductionFactorsPt = (std::vector)jetReductionFactors; + jetRadiiValues = jetRadii; + jetPtBinsReduction = jetPtBins; + jetReductionFactorsPt = jetReductionFactors; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("h_vertexZ", "Vertex Z;#it{Z} (cm)", {HistType::kTH1F, {{40, -20.0, 20.0}}}); diff --git a/PWGJE/Tasks/chargedJetHadron.cxx b/PWGJE/Tasks/chargedJetHadron.cxx index 12440d3bad2..4423f41b081 100644 --- a/PWGJE/Tasks/chargedJetHadron.cxx +++ b/PWGJE/Tasks/chargedJetHadron.cxx @@ -132,8 +132,8 @@ struct ChargedJetHadron { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (cfgCentEstimator == 0) { eventCuts = (aod::jcollision::centFT0C >= centralityMin && aod::jcollision::centFT0C <= centralityMax); diff --git a/PWGJE/Tasks/dijetFinderQA.cxx b/PWGJE/Tasks/dijetFinderQA.cxx index defb8a6c926..6ddbcf44304 100644 --- a/PWGJE/Tasks/dijetFinderQA.cxx +++ b/PWGJE/Tasks/dijetFinderQA.cxx @@ -96,8 +96,8 @@ struct DijetFinderQATask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Add histogram for event counts auto dijetMassTemp = 0.0; diff --git a/PWGJE/Tasks/embeddingQA.cxx b/PWGJE/Tasks/embeddingQA.cxx index f8736d460bb..96952f42055 100644 --- a/PWGJE/Tasks/embeddingQA.cxx +++ b/PWGJE/Tasks/embeddingQA.cxx @@ -65,8 +65,8 @@ struct EmbeddingQATask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Preslice perBCCollisions = aod::collision::bcId; diff --git a/PWGJE/Tasks/fullJetSpectra.cxx b/PWGJE/Tasks/fullJetSpectra.cxx index 7c9fe31090a..c0d12599f50 100644 --- a/PWGJE/Tasks/fullJetSpectra.cxx +++ b/PWGJE/Tasks/fullJetSpectra.cxx @@ -355,11 +355,11 @@ struct FullJetSpectra { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - particleSelection = static_cast(particleSelections); - jetRadiiValues = (std::vector)jetRadii; + particleSelection = particleSelections; + jetRadiiValues = jetRadii; doSumw2 = doMBGapTrigger; /* if (doMcClosure) { // randGen.SetSeed(mcSplitSeed); @@ -391,7 +391,7 @@ struct FullJetSpectra { for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -783,7 +783,7 @@ struct FullJetSpectra { Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); // Filter EMCeventCuts = (nabs(aod::collision::posZ) < vertexZCut && aod::collision::centrality >= centralityMin && aod::collision::centrality < centralityMax); Filter trackCuts = (aod::jtrack::pt >= trackpTMin && aod::jtrack::pt < trackpTMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax && aod::jtrack::phi >= trackPhiMin && aod::jtrack::phi <= trackPhiMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); Preslice JetMCPPerMcCollision = aod::jet::mcCollisionId; PresliceUnsorted> CollisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; diff --git a/PWGJE/Tasks/gammaJetTreeProducer.cxx b/PWGJE/Tasks/gammaJetTreeProducer.cxx index 9cf316f118e..1f8751bcbfa 100644 --- a/PWGJE/Tasks/gammaJetTreeProducer.cxx +++ b/PWGJE/Tasks/gammaJetTreeProducer.cxx @@ -159,9 +159,9 @@ struct GammaJetTreeProducer { using O2HistType = HistType; using O2Axis = AxisSpec; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // for substructure calculation jetReclusterer.isReclustering = true; diff --git a/PWGJE/Tasks/hadronPhotonCorrelation.cxx b/PWGJE/Tasks/hadronPhotonCorrelation.cxx index 7040898d6d1..18e73087ff7 100644 --- a/PWGJE/Tasks/hadronPhotonCorrelation.cxx +++ b/PWGJE/Tasks/hadronPhotonCorrelation.cxx @@ -152,8 +152,8 @@ struct HadronPhotonCorrelation { AxisSpec axisEtaAssoc = {etaBinsAssoc, -etaMaxAssoc, etaMaxAssoc, "#eta"}; // Axis for eta distribution AxisSpec axisDeltaEta = {etaBinsTrig + etaBinsAssoc, -(etaMaxTrig + etaMaxAssoc), etaMaxTrig + etaMaxAssoc, "#Delta #eta"}; // Axis for Delta eta in correlations - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Generated histograms registry.add("generated/events/hEventStats", "Event statistics", kTH1F, {axisEventStats}); @@ -298,10 +298,10 @@ struct HadronPhotonCorrelation { bool isHardQCDgg2gg(const P& particles) { for (const auto& particle : particles) { - if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } - if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } } @@ -396,7 +396,7 @@ struct HadronPhotonCorrelation { return false; } - if ((PDG_t)std::abs(particle->GetPdgCode()) != kGamma) { + if (std::abs(particle->GetPdgCode()) != kGamma) { return false; } @@ -712,7 +712,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc, false)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kGamma) { + if (std::abs(track_assoc.pdgCode()) != kGamma) { continue; } if (!track_assoc.isPhysicalPrimary() && track_assoc.getGenStatusCode() < 0) { @@ -722,7 +722,7 @@ struct HadronPhotonCorrelation { // Iterate through mother particles until original mother is reached auto origPhoton = track_assoc; auto mother = track_assoc.mothers_as().at(0); - while ((PDG_t)mother.pdgCode() == kGamma) { + while (mother.pdgCode() == kGamma) { origPhoton = mother; mother = mother.mothers_as().at(0); } @@ -968,7 +968,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.pdgCode()) != kPiPlus) { continue; } @@ -1050,7 +1050,7 @@ struct HadronPhotonCorrelation { if (!initTrack(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { continue; } @@ -1101,7 +1101,7 @@ struct HadronPhotonCorrelation { if (pdgParticle->Charge() != 0.) { continue; } // remove charged particles - if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || (PDG_t)track_assoc.pdgCode() == kNeutron) { + if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || track_assoc.pdgCode() == kNeutron) { continue; } // remove non-hadrons and neutrons @@ -1121,7 +1121,7 @@ struct HadronPhotonCorrelation { } for (const auto& daughter : daughters) { - if ((PDG_t)std::abs(daughter.pdgCode()) != kGamma) { + if (std::abs(daughter.pdgCode()) != kGamma) { continue; } if (!daughter.isPhysicalPrimary() && daughter.getGenStatusCode() == -1) { diff --git a/PWGJE/Tasks/hfFragmentationFunction.cxx b/PWGJE/Tasks/hfFragmentationFunction.cxx index 054cae8ea42..25ffa86f906 100644 --- a/PWGJE/Tasks/hfFragmentationFunction.cxx +++ b/PWGJE/Tasks/hfFragmentationFunction.cxx @@ -235,7 +235,7 @@ struct HfFragmentationFunction { void init(InitContext const&) { // initialise event selection: - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // create histograms // collision counter histograms diff --git a/PWGJE/Tasks/jetBackgroundAnalysis.cxx b/PWGJE/Tasks/jetBackgroundAnalysis.cxx index b58e698bb09..6a5a8aaaae3 100644 --- a/PWGJE/Tasks/jetBackgroundAnalysis.cxx +++ b/PWGJE/Tasks/jetBackgroundAnalysis.cxx @@ -81,8 +81,8 @@ struct JetBackgroundAnalysisTask { void init(o2::framework::InitContext&) { // selection settings initialisation - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); // Axes definitions @@ -143,7 +143,7 @@ struct JetBackgroundAnalysisTask { float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -156,8 +156,8 @@ struct JetBackgroundAnalysisTask { float randomConePtRandomTrackDirection = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtRandomTrackDirection += track.pt(); } @@ -170,7 +170,7 @@ struct JetBackgroundAnalysisTask { const bool hasSub = jets.size() >= 2; float randomConePtWithoutLeadingJet = randomConePt; if (hasLead) { - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -178,14 +178,14 @@ struct JetBackgroundAnalysisTask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePtWithoutLeadingJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtWithoutLeadingJet += track.pt(); @@ -204,8 +204,8 @@ struct JetBackgroundAnalysisTask { randomConePtWithoutTwoLeadJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); @@ -230,7 +230,7 @@ struct JetBackgroundAnalysisTask { float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -241,8 +241,8 @@ struct JetBackgroundAnalysisTask { // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles float randomConePtRandomTrackDirection = 0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtRandomTrackDirection += track.pt(); } @@ -254,7 +254,7 @@ struct JetBackgroundAnalysisTask { const bool hasSub = jets.size() >= 2; float randomConePtWithoutLeadingJet = randomConePt; if (hasLead) { - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -262,13 +262,13 @@ struct JetBackgroundAnalysisTask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePtWithoutLeadingJet = 0.0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtWithoutLeadingJet += track.pt(); @@ -285,8 +285,8 @@ struct JetBackgroundAnalysisTask { randomConePtWithoutOneLeadJet = 0.0; randomConePtWithoutTwoLeadJet = 0.0; for (auto const& track : tracks) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 210550c221d..17a637da4f4 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -266,8 +266,8 @@ struct JetChargedV2 { AxisSpec axixCent = {20, 0, 100}; AxisSpec axisChID = {220, 0, 220}; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); if (doprocessCollisions || doprocessCollisionsWeighted) { diff --git a/PWGJE/Tasks/jetCorrelationD0.cxx b/PWGJE/Tasks/jetCorrelationD0.cxx index eee51eb4ab4..c5ffa65bdd0 100644 --- a/PWGJE/Tasks/jetCorrelationD0.cxx +++ b/PWGJE/Tasks/jetCorrelationD0.cxx @@ -254,7 +254,7 @@ struct JetCorrelationD0 { } void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // General Axes AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; diff --git a/PWGJE/Tasks/jetCorrelationDmeson.cxx b/PWGJE/Tasks/jetCorrelationDmeson.cxx index 1a1b2406a85..666b88ab04b 100644 --- a/PWGJE/Tasks/jetCorrelationDmeson.cxx +++ b/PWGJE/Tasks/jetCorrelationDmeson.cxx @@ -280,7 +280,7 @@ struct JetCorrelationDmeson { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // General Axes AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; diff --git a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx index 9fce23a2353..35e3693f59b 100644 --- a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx +++ b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx @@ -254,7 +254,7 @@ struct JetCrossSectionEfficiency { rctChecker.init(static_cast(rctSelectionsLabel)); rctMask = rctChecker.value(); - switch (getEventSelectionPreset(static_cast(eventSelections))) { + switch (getEventSelectionPreset(eventSelections.value)) { case PresetSelTvx: applyTFB = false; applyROFB = false; @@ -289,7 +289,7 @@ struct JetCrossSectionEfficiency { isSel8FullPbPb = true; break; default: - LOGF(fatal, "Configurable eventSelections=%s not supported; use selTVX, selMC, selMCFull, sel8, sel8Full, or sel8FullPbPb", static_cast(eventSelections).c_str()); + LOGF(fatal, "Configurable eventSelections=%s not supported; use selTVX, selMC, selMCFull, sel8, sel8Full, or sel8FullPbPb", eventSelections.value.c_str()); break; } diff --git a/PWGJE/Tasks/jetD0AngSubstructure.cxx b/PWGJE/Tasks/jetD0AngSubstructure.cxx index 8a14b780d36..2c0fa7a6562 100644 --- a/PWGJE/Tasks/jetD0AngSubstructure.cxx +++ b/PWGJE/Tasks/jetD0AngSubstructure.cxx @@ -344,8 +344,8 @@ struct JetD0AngSubstructure { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); addHistograms(); } diff --git a/PWGJE/Tasks/jetDebug.cxx b/PWGJE/Tasks/jetDebug.cxx index d384f3a5e36..8d220b5665b 100644 --- a/PWGJE/Tasks/jetDebug.cxx +++ b/PWGJE/Tasks/jetDebug.cxx @@ -107,8 +107,8 @@ struct JetDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processCollisionSelection(aod::JetCollisions const& collisions, soa::Join const& originalCollisions) diff --git a/PWGJE/Tasks/jetDsSpecSubs.cxx b/PWGJE/Tasks/jetDsSpecSubs.cxx index d1a5a634fc1..96da12c3ad6 100644 --- a/PWGJE/Tasks/jetDsSpecSubs.cxx +++ b/PWGJE/Tasks/jetDsSpecSubs.cxx @@ -488,8 +488,8 @@ struct JetDsSpecSubs { void init(InitContext const&) { // Initialise event and track selection criteria - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Prevent simultaneous execution of processes using alternative jet collections if (doprocessCollisions && doprocessCollisionsEWS) { diff --git a/PWGJE/Tasks/jetFinderFullQA.cxx b/PWGJE/Tasks/jetFinderFullQA.cxx index 69b18b6e4af..4802a5a333f 100644 --- a/PWGJE/Tasks/jetFinderFullQA.cxx +++ b/PWGJE/Tasks/jetFinderFullQA.cxx @@ -96,13 +96,13 @@ struct JetFinderFullQATask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -253,7 +253,7 @@ struct JetFinderFullQATask { Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); template diff --git a/PWGJE/Tasks/jetFinderHFQA.h b/PWGJE/Tasks/jetFinderHFQA.h index 1eaa5456bf2..3da47b8f636 100644 --- a/PWGJE/Tasks/jetFinderHFQA.h +++ b/PWGJE/Tasks/jetFinderHFQA.h @@ -88,10 +88,10 @@ struct JetFinderHFQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR_Both.push_back(0.0); @@ -101,7 +101,7 @@ struct JetFinderHFQATask { filledJetHFR_Low.push_back(0.0); filledJetHFR_High.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetFinderQA.cxx b/PWGJE/Tasks/jetFinderQA.cxx index cd28f221ad9..a3114bc39ca 100644 --- a/PWGJE/Tasks/jetFinderQA.cxx +++ b/PWGJE/Tasks/jetFinderQA.cxx @@ -105,16 +105,16 @@ struct JetFinderQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetRBoth.push_back(0.0); filledJetRLow.push_back(0.0); filledJetRHigh.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -819,7 +819,7 @@ struct JetFinderQATask { float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -832,8 +832,8 @@ struct JetFinderQATask { randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } @@ -843,7 +843,7 @@ struct JetFinderQATask { // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -851,14 +851,14 @@ struct JetFinderQATask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePt = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -875,8 +875,8 @@ struct JetFinderQATask { double randomConePtWithoutTwoLeadJet = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { if (!trackIsInJet(track, jets.iteratorAt(0))) { randomConePtWithoutOneLeadJet += track.pt(); diff --git a/PWGJE/Tasks/jetFinderV0QA.cxx b/PWGJE/Tasks/jetFinderV0QA.cxx index 55036241730..dd4dc32a394 100644 --- a/PWGJE/Tasks/jetFinderV0QA.cxx +++ b/PWGJE/Tasks/jetFinderV0QA.cxx @@ -68,10 +68,10 @@ struct JetFinderV0QATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetHFAngularity.cxx b/PWGJE/Tasks/jetHFAngularity.cxx index 6b16ff5af58..28f505e5ce8 100644 --- a/PWGJE/Tasks/jetHFAngularity.cxx +++ b/PWGJE/Tasks/jetHFAngularity.cxx @@ -451,8 +451,8 @@ struct JetHFAngularityTask { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections.value); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections.value); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); massD0MCP = jetcandidateutilities::getTablePDGMass(); diff --git a/PWGJE/Tasks/jetHadronRecoil.cxx b/PWGJE/Tasks/jetHadronRecoil.cxx index 0cfbcd2ea8d..8db28c34a44 100644 --- a/PWGJE/Tasks/jetHadronRecoil.cxx +++ b/PWGJE/Tasks/jetHadronRecoil.cxx @@ -137,8 +137,8 @@ struct JetHadronRecoil { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); jetReclusterer.isReclustering = true; diff --git a/PWGJE/Tasks/jetHadronsPid.cxx b/PWGJE/Tasks/jetHadronsPid.cxx index f1e59447406..e2522ef9c23 100644 --- a/PWGJE/Tasks/jetHadronsPid.cxx +++ b/PWGJE/Tasks/jetHadronsPid.cxx @@ -195,7 +195,7 @@ struct JetHadronsPid { itsResponse.setMCDefaultParameters(); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // --- DATA: PURE TRACKS --- registryData.add("data/pure/general/tpc_signal_vs_p", "TPC n#sigma_{#pi} vs transverse momentum ;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); diff --git a/PWGJE/Tasks/jetLundReclustering.cxx b/PWGJE/Tasks/jetLundReclustering.cxx index 95b5b027931..f85f34497ff 100644 --- a/PWGJE/Tasks/jetLundReclustering.cxx +++ b/PWGJE/Tasks/jetLundReclustering.cxx @@ -67,7 +67,7 @@ struct JetLundReclustering { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("PrimaryLundPlane_kT", "Primary Lund 3D plane;ln(R/Delta);ln(k_{t}/GeV);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, -10, 10}, {20, 0, 200}}}); registry.add("PrimaryLundPlane_z", "Primary Lund 3D plane;ln(R/Delta);ln(1/z);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, 0, 10}, {20, 0, 200}}}); diff --git a/PWGJE/Tasks/jetOutlierQA.cxx b/PWGJE/Tasks/jetOutlierQA.cxx index a9cf7fe6714..d37047e6008 100644 --- a/PWGJE/Tasks/jetOutlierQA.cxx +++ b/PWGJE/Tasks/jetOutlierQA.cxx @@ -93,8 +93,8 @@ struct JetOutlierQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); auto jetPtTemp = 0.0; jetPtBins.push_back(jetPtTemp); @@ -358,10 +358,10 @@ struct JetOutlierQATask { } } registry.fill(HIST("h3_jet_pt_frac_pt_ambiguous_pt_hat"), jet.pt(), pt_amb / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), double(nAmbTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), static_cast(nAmbTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); registry.fill(HIST("h3_jet_pt_frac_pt_unmatched_particle_pt_hat"), jet.pt(), pt_unmatched / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), double(nUnmatchedTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), static_cast(nUnmatchedTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); } void processJetsAmbiguous(soa::Filtered>::iterator const& collision, @@ -607,8 +607,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { float eventWeightOutlier = collisionOutlier.mcCollision().weight(); double pTHatOutlier = collisionOutlier.mcCollision().ptHard(); - registry.fill(HIST("h2_neighbour_pt_hat_outlier"), float(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); - registry.fill(HIST("h2_neighbour_track_pt_outlier"), float(diffColl + 0.1), track.pt(), eventWeightOutlier); + registry.fill(HIST("h2_neighbour_pt_hat_outlier"), static_cast(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_outlier"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } // now match tracks to their MC particle, check the MC collision ID of this particle, and @@ -658,20 +658,20 @@ struct JetOutlierQATask { registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_JJ"), pTHat, nJJdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_MB"), pTHat, nMBdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_same_selected_JJ"), pTHat, nJJsameSelected); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, float(nJJdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, float(nMBdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, float(nJJdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, static_cast(nJJdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, static_cast(nMBdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, static_cast(nJJdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); // now check where outlier comes from // LOG(info) <<"outlier comes from " << (mcCollisionIDOutlier == mcCollisionIDcoll ? "same" : "different") << " event which is a " << (subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap ? " MB-gap" : " jet-jet") << " collision with mcCollisionID = " << mcCollisionIDOutlier; - registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, static_cast(outlierCollisionIDDifference)); // if outlier comes from different collision, check which type and set flags if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentJJCollision = true; float deltaZ = collisionMCOutlier.begin().posZ() - collision.mcCollision().posZ(); registry.fill(HIST("h_DeltaZ_Outlier"), deltaZ); - registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, static_cast(outlierCollisionIDDifference)); } else if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentMBCollision = true; } @@ -683,7 +683,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_same_collision"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_same_collision"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_same_collision"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, static_cast(outlierCollisionIDDifference)); registry.fill(HIST("h_pt_hard_track_pt_same_collision"), pTHat != 0.0 ? track.pt() / pTHat : 0.0, track.pt(), weight); // include selection on pThat of particle @@ -699,7 +699,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_no_JJ_different"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_no_JJ_different"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_no_JJ_different"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, static_cast(outlierCollisionIDDifference)); } else { registry.fill(HIST("h_track_pt_no_JJ_different_rejected"), track.pt(), weight); } @@ -711,8 +711,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { // LOG(info) << "pThat = " << pTHat << "pThat neighbour = "<(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_all"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } } diff --git a/PWGJE/Tasks/jetPlanarFlow.cxx b/PWGJE/Tasks/jetPlanarFlow.cxx index 480dcc69a53..507d4ca7aee 100644 --- a/PWGJE/Tasks/jetPlanarFlow.cxx +++ b/PWGJE/Tasks/jetPlanarFlow.cxx @@ -134,9 +134,9 @@ struct JetPlanarFlowTask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; precisionMask = 0xFFFFFC00; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; diff --git a/PWGJE/Tasks/jetShape.cxx b/PWGJE/Tasks/jetShape.cxx index c18834ab478..9762f2ff1df 100644 --- a/PWGJE/Tasks/jetShape.cxx +++ b/PWGJE/Tasks/jetShape.cxx @@ -85,8 +85,8 @@ struct JetShapeTask { Configurable tpcNSigmaPrMax{"tpcNSigmaPrMax", 2.0f, "Max value of tpcNsigmaProton"}; Configurable tpcNSigmaPiMin{"tpcNSigmaPiMin", -2.0f, "Min value of tpcNsigmaPion"}; Configurable tpcNSigmaPiMax{"tpcNSigmaPiMax", 2.0f, "Max value of tpcNsigmaPion"}; - Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", static_cast(o2::constants::math::PIThird), "Minimum delta phi used only when randomConeFullAzimuth=false"}; - Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", static_cast(2.0f * o2::constants::math::PIThird), "Maximum delta phi used only when randomConeFullAzimuth=false"}; + Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", +o2::constants::math::PIThird, "Minimum delta phi used only when randomConeFullAzimuth=false"}; + Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", 2.0f * o2::constants::math::PIThird, "Maximum delta phi used only when randomConeFullAzimuth=false"}; Configurable enableRandomCone{"enableRandomCone", true, "Fill one accepted random cone per selected jet"}; Configurable randomConeFullAzimuth{"randomConeFullAzimuth", true, "Generate random cones over the full azimuth instead of the old perpendicular-side window"}; Configurable randomConeMaxAttempts{"randomConeMaxAttempts", 100, "Maximum attempts for each random cone"}; @@ -138,8 +138,8 @@ struct JetShapeTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); trackSelectionForDca = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelectionsForDca)); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); randomNumber.SetSeed(randomConeSeed); @@ -504,7 +504,7 @@ struct JetShapeTask { for (int attempt = 0; attempt < randomConeMaxAttempts; ++attempt) { rcEta = randomNumber.Uniform(etaMin, etaMax); if (randomConeFullAzimuth) { - rcPhi = randomNumber.Uniform(0.0f, static_cast(o2::constants::math::TwoPI)); + rcPhi = randomNumber.Uniform(0.0f, o2::constants::math::TwoPI); } else { float dPhi = randomNumber.Uniform(randomConeDeltaPhiMin, randomConeDeltaPhiMax); if (randomNumber.Uniform() < RandomConeNegativeSideProbability) { diff --git a/PWGJE/Tasks/jetSpectraCharged.cxx b/PWGJE/Tasks/jetSpectraCharged.cxx index 44363723bf9..c7b6900977c 100644 --- a/PWGJE/Tasks/jetSpectraCharged.cxx +++ b/PWGJE/Tasks/jetSpectraCharged.cxx @@ -106,8 +106,8 @@ struct JetSpectraCharged { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); doSumw2 = skipMBGapEvents; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index bdfd26ae586..396a9acdef3 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -356,8 +356,8 @@ struct JetSpectraEseTask { int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); LOGF(info, "jetSpectraEse::init()"); diff --git a/PWGJE/Tasks/jetSubstructure.cxx b/PWGJE/Tasks/jetSubstructure.cxx index 5552101896c..6169bd82f5c 100644 --- a/PWGJE/Tasks/jetSubstructure.cxx +++ b/PWGJE/Tasks/jetSubstructure.cxx @@ -130,7 +130,7 @@ struct JetSubstructureTask { jetReclusterer.algorithm = fastjet::JetAlgorithm::cambridge_algorithm; jetReclusterer.ghostRepeatN = 0; - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Preslice TracksPerCollision = aod::jtrack::collisionId; diff --git a/PWGJE/Tasks/jetSubstructureHF.h b/PWGJE/Tasks/jetSubstructureHF.h index 4332f649f84..8ac281fad77 100644 --- a/PWGJE/Tasks/jetSubstructureHF.h +++ b/PWGJE/Tasks/jetSubstructureHF.h @@ -137,7 +137,7 @@ struct JetSubstructureHFTask { candMass = jetcandidateutilities::getTablePDGMass(); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } o2::framework::Preslice TracksPerCollision = o2::aod::jtrack::collisionId; diff --git a/PWGJE/Tasks/jetSubstructureHFOutput.h b/PWGJE/Tasks/jetSubstructureHFOutput.h index 2eb799a6708..4c33d2d989e 100644 --- a/PWGJE/Tasks/jetSubstructureHFOutput.h +++ b/PWGJE/Tasks/jetSubstructureHFOutput.h @@ -139,8 +139,8 @@ struct JetSubstructureHFOutputTask { void init(o2::framework::InitContext const&) { - jetRadiiValues = (std::vector)configs.jetRadii; - recoilJetRadiiValues = (std::vector)configs.recoilJetRadii; + jetRadiiValues = configs.jetRadii.value; + recoilJetRadiiValues = configs.recoilJetRadii.value; } struct : o2::framework::PresliceGroup { diff --git a/PWGJE/Tasks/jetSubstructureOutput.cxx b/PWGJE/Tasks/jetSubstructureOutput.cxx index 6dd90638faa..ef90a7e88c8 100644 --- a/PWGJE/Tasks/jetSubstructureOutput.cxx +++ b/PWGJE/Tasks/jetSubstructureOutput.cxx @@ -96,7 +96,7 @@ struct JetSubstructureOutputTask { void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; } Preslice> splittingsPerJetData = aod::chargedsplitting::jetId; diff --git a/PWGJE/Tasks/jetTaggerHFQA.cxx b/PWGJE/Tasks/jetTaggerHFQA.cxx index b7cbbe76f5d..b05c388d4ef 100644 --- a/PWGJE/Tasks/jetTaggerHFQA.cxx +++ b/PWGJE/Tasks/jetTaggerHFQA.cxx @@ -117,8 +117,8 @@ struct JetTaggerHFQA { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; diff --git a/PWGJE/Tasks/jetTriggerChargedQa.cxx b/PWGJE/Tasks/jetTriggerChargedQa.cxx index 22ee5b8306d..bb491523d27 100644 --- a/PWGJE/Tasks/jetTriggerChargedQa.cxx +++ b/PWGJE/Tasks/jetTriggerChargedQa.cxx @@ -97,7 +97,7 @@ struct JetTriggerChargedQa { { fiducialVolume = static_cast(cfgTPCVolume) - static_cast(cfgJetR); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Basic histos spectra.add("vertexZ", "z vertex", kTH1F, {{60, -12., 12.}}); diff --git a/PWGJE/Tasks/jetTutorial.cxx b/PWGJE/Tasks/jetTutorial.cxx index c5544e14257..25e89677000 100644 --- a/PWGJE/Tasks/jetTutorial.cxx +++ b/PWGJE/Tasks/jetTutorial.cxx @@ -93,8 +93,8 @@ struct JetTutorial { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } diff --git a/PWGJE/Tasks/jetTutorialSkeleton.cxx b/PWGJE/Tasks/jetTutorialSkeleton.cxx index 763f2dc4c0e..e5de1f294d8 100644 --- a/PWGJE/Tasks/jetTutorialSkeleton.cxx +++ b/PWGJE/Tasks/jetTutorialSkeleton.cxx @@ -84,8 +84,8 @@ struct JetTutorialSkeletonTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } diff --git a/PWGJE/Tasks/jetValidationQA.cxx b/PWGJE/Tasks/jetValidationQA.cxx index 18942a5b59d..9eac3ed0651 100644 --- a/PWGJE/Tasks/jetValidationQA.cxx +++ b/PWGJE/Tasks/jetValidationQA.cxx @@ -56,7 +56,7 @@ struct jetTrackCollisionQa { void init(InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; @@ -65,7 +65,7 @@ struct jetTrackCollisionQa { const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; // set trackselections - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms // 1)Jetvalidation on data mHistManager.add("collisionVtxZ", "selected collsion VtxZ ", HistType::kTH1D, {vtxZAxis}); @@ -337,7 +337,7 @@ struct mcJetTrackCollisionQa { void init(InitContext const&) { // set trackselection - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; diff --git a/PWGJE/Tasks/mcGeneratorStudies.cxx b/PWGJE/Tasks/mcGeneratorStudies.cxx index fb7e2f081ec..1dccf464e99 100644 --- a/PWGJE/Tasks/mcGeneratorStudies.cxx +++ b/PWGJE/Tasks/mcGeneratorStudies.cxx @@ -80,7 +80,7 @@ struct MCGeneratorStudies { hBCCounter->GetXaxis()->SetBinLabel(2, "+TVX"); hBCCounter->GetXaxis()->SetBinLabel(3, "+Collision"); - TString mesonLatexString = (TString)mSelectedParticleCode; + TString mesonLatexString = TString(mSelectedParticleCode); switch (mSelectedParticleCode) { case 0: mesonLatexString = "particles"; diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 2abfb2f8fa3..5b57190673e 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -923,8 +923,8 @@ struct nucleiInJets { { std::array PerpendicularConeAxisPhi = {-999.0f, -999.0f}; // build 2 perp cones in phi around the leading jet (right and left of the jet) - PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi - PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi return PerpendicularConeAxisPhi; } diff --git a/PWGJE/Tasks/photonChargedTriggerProducer.cxx b/PWGJE/Tasks/photonChargedTriggerProducer.cxx index 0fdaa6ca5b7..9e16a69e1cc 100644 --- a/PWGJE/Tasks/photonChargedTriggerProducer.cxx +++ b/PWGJE/Tasks/photonChargedTriggerProducer.cxx @@ -207,8 +207,8 @@ struct PhotonChargedTriggerProducer { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); // histograms diff --git a/PWGJE/Tasks/photonIsolationQA.cxx b/PWGJE/Tasks/photonIsolationQA.cxx index d14e523d91e..fe822de10d1 100644 --- a/PWGJE/Tasks/photonIsolationQA.cxx +++ b/PWGJE/Tasks/photonIsolationQA.cxx @@ -104,8 +104,8 @@ struct PhotonIsolationQA { int trackSelection = -1; void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); auto nCells = mGeometry->GetNCells(); diff --git a/PWGJE/Tasks/substructureDebug.cxx b/PWGJE/Tasks/substructureDebug.cxx index 0243cce3c1a..b5943283e73 100644 --- a/PWGJE/Tasks/substructureDebug.cxx +++ b/PWGJE/Tasks/substructureDebug.cxx @@ -84,8 +84,8 @@ struct SubstructureDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processDataCharged(aod::CJetCOs::iterator const& collision, soa::Join const& jets) diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index 4ee3a336db3..05de663ba17 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -252,8 +252,8 @@ struct TrackEfficiency { LOGP(fatal, "FT0A and FT0C means must have magnitude greater than {} to avoid near-zero denominators; stopping workflow", minFT0Mean); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (useCustomTrackSelection) { // Custom track cuts From 93b23d0e7c8a240d0a5715b4eed31bf6444e131b Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:40:45 +0100 Subject: [PATCH 492/648] [PWGCF] Add histograms for purity and fraction extraction to track track task in femto framework (#18512) --- PWGCF/Femto/Core/femtoUtils.h | 53 ++++++++ PWGCF/Femto/Core/modes.h | 5 + PWGCF/Femto/Core/pairBuilder.h | 16 +-- PWGCF/Femto/Core/trackBuilder.h | 113 +++++++++++++++++- PWGCF/Femto/Core/trackHistManager.h | 113 ++++++++++++++++-- PWGCF/Femto/DataModel/FemtoTables.h | 88 ++++++++++++++ .../femtoProducerDerivedToDerived.cxx | 20 +++- PWGCF/Femto/Tasks/femtoMcParticleQa.cxx | 27 +++-- .../Tasks/femtoPairMcParticleMcParticle.cxx | 11 +- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 49 ++++++-- ...oTripletMcParticleMcParticleMcParticle.cxx | 12 +- 11 files changed, 469 insertions(+), 38 deletions(-) diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index 07b6e47705a..22dc4fa37e9 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -356,6 +356,59 @@ float unBinLogUnsigned(T binned, float magMin, float magMax) return mag; } +// --------------------------------------------------------------------------- +// purity/fraction packing: up to 4 species' log-binned nSigma values (see +// binLogSigned) packed bitwise into a single int64_t column. Slot +// assignment (which species occupies which of the 4 slots) is decided at +// producer init time from which requirePidXxx flags are active, and exposed +// via a self-documenting histogram -- these functions only do the fixed-slot +// bit shifting, nothing species-specific. +// --------------------------------------------------------------------------- + +constexpr int PurityNSlots = 4; + +/// pack 4 already-binned uint16_t codes (one per slot) into a single int64_t +inline int64_t packPurity4(uint16_t slot0, uint16_t slot1, uint16_t slot2, uint16_t slot3) +{ + return (static_cast(slot0) << 48) | + (static_cast(slot1) << 32) | + (static_cast(slot2) << 16) | + static_cast(slot3); +} + +/// extract the raw uint16_t code of one of the 4 slots (0..3) from a packed int64_t +inline uint16_t unpackPurity4(int64_t packed, int slot) +{ + switch (slot) { + case 0: + return static_cast((packed >> 48) & 0xFFFF); + case 1: + return static_cast((packed >> 32) & 0xFFFF); + case 2: + return static_cast((packed >> 16) & 0xFFFF); + case 3: + return static_cast(packed & 0xFFFF); + default: + LOG(fatal) << "unpackPurity4: invalid slot " << slot << " (must be 0-3). Breaking..."; + return 0; + } +} + +/// pack 4 raw signed nSigma values (one per slot, unused slots pass 0) directly into a packed int64_t +inline int64_t packPurityNSigma4(float nSigma0, float nSigma1, float nSigma2, float nSigma3, float magMin, float magMax) +{ + return packPurity4(binLogSigned(nSigma0, magMin, magMax), + binLogSigned(nSigma1, magMin, magMax), + binLogSigned(nSigma2, magMin, magMax), + binLogSigned(nSigma3, magMin, magMax)); +} + +/// unpack the nSigma value of one of the 4 slots (0..3) from a packed int64_t +inline float unpackPurityNSigma4(int64_t packed, int slot, float magMin, float magMax) +{ + return unBinLogSigned(unpackPurity4(packed, slot), magMin, magMax); +} + }; // namespace utils }; // namespace o2::analysis::femto // diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index e0a70e644ec..0b0bf75121e 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -53,6 +53,7 @@ enum class Mode : uint32_t { kMc = bit(2), kSe = bit(3), kMe = bit(4), + kFitVars = bit(5), // purity/fraction histograms (tracks: packed nSigma tables; V0s: cpa) -- gates booking at init time kReco_Qa = kReco | kQa, kReco_Mc = kReco | kMc, kReco_Qa_Mc = kReco | kQa | kMc, @@ -62,6 +63,10 @@ enum class Mode : uint32_t { kMe_Reco_Mc = kMe | kReco | kMc, kSe_Mc = kSe | kMc, kMe_Mc = kMe | kMc, + kSe_Reco_FitVars = kSe | kReco | kFitVars, + kMe_Reco_FitVars = kMe | kReco | kFitVars, + kSe_Reco_Mc_FitVars = kSe | kReco | kMc | kFitVars, + kMe_Reco_Mc_FitVars = kMe | kReco | kMc | kFitVars, }; enum class System : uint32_t { diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index dd157bbb0ae..a2c46bbb875 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -91,7 +91,9 @@ class PairTrackTrackBuilder std::map> const& trackHistSpec2, std::map> const& pairHistSpec, std::map> const& cprHistSpec, - std::map> const& pairCleanerHistSpec) + std::map> const& pairCleanerHistSpec, + int puritySlotMask1 = 1, + int puritySlotMask2 = 1) { // check if correlate the same tracks or not @@ -106,8 +108,8 @@ class PairTrackTrackBuilder if (mSameSpecies) { mTrackCleaner1.init(confTrackCleaner1); - mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -119,10 +121,10 @@ class PairTrackTrackBuilder } else { mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); - mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2, puritySlotMask2); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2, puritySlotMask2); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 8150e840011..c98ec68c8e4 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -598,6 +598,9 @@ struct TrackBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedDeuteronPids; o2::framework::Produces producedTritonPids; o2::framework::Produces producedHeliumPids; + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; }; struct ConfTrackTables : o2::framework::ConfigurableGroup { @@ -615,6 +618,9 @@ struct ConfTrackTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceDeuteronPids{"produceDeuteronPids", -1, "Produce DeuteronPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTritonPids{"produceTritonPids", -1, "Produce TritonPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceHeliumPids{"produceHeliumPids", -1, "Produce HeliumPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceItsPurity{"produceItsPurity", -1, "Produce ItsPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTpcPurity{"produceTpcPurity", -1, "Produce TpcPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTofPurity{"produceTofPurity", -1, "Produce TofPurity (-1: auto; 0 off; 1 on)"}; }; template @@ -642,6 +648,52 @@ class TrackBuilder mProduceDeuteronPids = utils::enableTable("FDeuteronPids_001", table.produceDeuteronPids.value, initContext); mProduceTritonPids = utils::enableTable("FTritonPids_001", table.produceTritonPids.value, initContext); mProduceHeliumPids = utils::enableTable("FHeliumPids_001", table.produceHeliumPids.value, initContext); + mProduceItsPurity = utils::enableTable("FItsPurity_001", table.produceItsPurity.value, initContext); + mProduceTpcPurity = utils::enableTable("FTpcPurity_001", table.produceTpcPurity.value, initContext); + mProduceTofPurity = utils::enableTable("FTofPurity_001", table.produceTofPurity.value, initContext); + + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // assign each active species (requirePidXxx == true) to the next free slot (0-3), in + // canonical order (PuritySpeciesName); species with requirePidXxx == false get no slot. + const std::array requirePid = { + config.requirePidElectron.value, config.requirePidPion.value, config.requirePidKaon.value, + config.requirePidProton.value, config.requirePidDeuteron.value, config.requirePidTriton.value, config.requirePidHelium.value}; + + int nextSlot = 0; + for (int i = 0; i < PuritySpeciesLast; i++) { + if (!requirePid[i]) { + continue; + } + if (nextSlot >= utils::PurityNSlots) { + LOG(fatal) << "More than " << utils::PurityNSlots << " species have requirePidXxx enabled, " + << "but the purity tables only have " << utils::PurityNSlots << " slots. " + << "Disable requirePidXxx for species you don't need purity info for. Breaking..."; + } + mPuritySlot[i] = nextSlot; + nextSlot++; + } + + // one bin per species (own SelectionName -> own cutculator group), plus the usual + // trailing "All analyzed"/"All passed" pair that cutculator always skips by position. + // BitPosition___X (unassigned species) is deliberately excluded from cutculator's + // "neutral" category, so nothing is selectable for a species that isn't configured here. + // Picking the one assigned bit for a species gives exactly 1<add("hPuritySlotAssignment", "Purity slot assignment", o2::framework::HistType::kTH1F, {{PuritySpeciesLast + 2, -0.5, PuritySpeciesLast + 2 - 0.5}}); + auto hSlotAssignment = registry->get(HIST("hPuritySlotAssignment")); + for (int i = 0; i < PuritySpeciesLast; i++) { + std::string bitPosition = mPuritySlot[i] >= 0 ? std::to_string(mPuritySlot[i]) : "X"; + std::string value = mPuritySlot[i] >= 0 ? ("Slot " + std::to_string(mPuritySlot[i])) : "Not configured"; + std::string label = std::string("SelectionName___") + PuritySpeciesName[i] + + ":::MinimalCut___0:::OptionalCut___0:::BitPosition___" + bitPosition + + ":::Value___" + value; + hSlotAssignment->GetXaxis()->SetBinLabel(i + 1, label.c_str()); + hSlotAssignment->Fill(i, 1); + } + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 1, "All analyzed"); + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 2, "All passed"); + } if (mProduceTracks && mProduceLiteTracks) { LOG(fatal) << "FTracks and FLiteTracks are mutually exclusive -- enable only one. " @@ -660,7 +712,7 @@ class TrackBuilder << "or switch to FTracks if CollisionTables.produceCollisions is enabled instead."; } - if (mProduceTracks || mProduceLiteTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids) { + if (mProduceTracks || mProduceLiteTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids || mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -797,6 +849,41 @@ class TrackBuilder } trackProducts.producedHeliumPids(itsHe, track.tpcNSigmaHe(), track.tofNSigmaHe()); } + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // raw nSigma per species, same values already used for the FXxxPids blocks above -- + // unused slots (species without a requirePidXxx flag, or not covered by this track + // type) stay at the 0 placeholder, same contract as the missing-Tof case + std::array itsNSigma = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array tpcNSigma = {track.tpcNSigmaEl(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe()}; + std::array tofNSigma = {track.tofNSigmaEl(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe()}; + + if constexpr (requires(T1 t) { t.itsNSigmaEl(); }) { + itsNSigma = {track.itsNSigmaEl(), track.itsNSigmaPi(), track.itsNSigmaKa(), track.itsNSigmaPr(), track.itsNSigmaDe(), track.itsNSigmaTr(), track.itsNSigmaHe()}; + } + + std::array itsSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tpcSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tofSlots = {0.f, 0.f, 0.f, 0.f}; + bool hasTof = track.hasTOF(); + for (int i = 0; i < PuritySpeciesLast; i++) { + if (mPuritySlot[i] < 0) { + continue; + } + itsSlots[mPuritySlot[i]] = itsNSigma[i]; + tpcSlots[mPuritySlot[i]] = tpcNSigma[i]; + tofSlots[mPuritySlot[i]] = hasTof ? tofNSigma[i] : 0.f; // PurityHasTof is the gate; 0 here is just a placeholder + } + + if (mProduceItsPurity) { + trackProducts.producedItsPurity(utils::packPurityNSigma4(itsSlots[0], itsSlots[1], itsSlots[2], itsSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTpcPurity) { + trackProducts.producedTpcPurity(utils::packPurityNSigma4(tpcSlots[0], tpcSlots[1], tpcSlots[2], tpcSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTofPurity) { + trackProducts.producedTofPurity(utils::packPurityNSigma4(tofSlots[0], tofSlots[1], tofSlots[2], tofSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax), hasTof); + } + } return true; } @@ -892,6 +979,16 @@ class TrackBuilder bool mProduceDeuteronPids = false; bool mProduceTritonPids = false; bool mProduceHeliumPids = false; + bool mProduceItsPurity = false; + bool mProduceTpcPurity = false; + bool mProduceTofPurity = false; + + // canonical species order for purity slot assignment -- fixed, not configurable (see + // init() for how requirePidXxx flags map onto slots 0-3) + static constexpr int PuritySpeciesLast = 7; + static constexpr std::array PuritySpeciesName = {"Electron", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "Helium"}; + // mPuritySlot[i] = assigned slot (0-3) for species i (PuritySpeciesName order), or -1 if not assigned + std::array mPuritySlot = {-1, -1, -1, -1, -1, -1, -1}; std::unordered_map indexMap; // for mapping tracks to daughters of lambdas, cascades and resonances ... }; @@ -900,6 +997,12 @@ struct TrackBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedTracks; o2::framework::Produces producedTrackMasks; o2::framework::Produces producedTrackMass; + // purity/fraction, copied through if the input track type has them joined (same + // compile-time-detected, opt-in-by-type pattern as producedTrackMass above) + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; + o2::framework::Produces producedTrackDcas; }; struct ConfTrackTablesDerivedToDerived : o2::framework::ConfigurableGroup { @@ -977,6 +1080,14 @@ class TrackBuilderDerivedToDerived if constexpr (utils::HasMass) { trackProducts.producedTrackMass(track.mass()); } + if constexpr (requires(T1 t) { t.itsPackedNSigma(); }) { + trackProducts.producedItsPurity(track.itsPackedNSigma()); + trackProducts.producedTpcPurity(track.tpcPackedNSigma()); + trackProducts.producedTofPurity(track.tofPackedNSigma(), track.purityHasTof()); + } + if constexpr (requires(T1 t) { t.dcaXY(); }) { + trackProducts.producedTrackDcas(track.dcaXY(), track.dcaZ()); + } const int64_t idx = trackProducts.producedTracks.lastIndex(); indexMap.emplace(track.globalIndex(), idx); diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 634512c8666..b02fc512f4e 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -121,6 +121,13 @@ enum TrackHist { kTpctofDeuteron, kTpctofTriton, kTpctofHelium, + // purity (species picked at runtime via puritySlotMask, see hPuritySlotAssignment) + kItsPurity, + kTpcPurity, + kTofPurity, + kTpcitsPurity, + kTpctofPurity, + kFractionDca, // mc kOrigin, kPdg, @@ -154,6 +161,11 @@ struct ConfTrackBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis mass{"mass", {{200, 0.f, 2.f}}, "Mass (if enabled, plot TOF mass, otherwise PDG mass)"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of selected tracks"}; + o2::framework::Configurable puritySlotMask{"puritySlotMask", 1, "Bitmask (bit N = slot N) selecting which purity slot to plot, read off hPuritySlotAssignment in the producer output."}; + o2::framework::ConfigurableAxis purityNSigma{"purityNSigma", {{3000, -15, 15}}, "nSigma decoded from the packed purity tables (species picked via puritySlotMask); signed, for the raw Its/Tpc/Tof values"}; + o2::framework::ConfigurableAxis purityNSigmaComb{"purityNSigmaComb", {{2000, 0, 20}}, "Combined nSigma (hypot of two raw values); always non-negative, for Tpc+Its/Tpc+Tof"}; + o2::framework::ConfigurableAxis fractionDcaXy{"fractionDcaXy", {{300, -0.3, 0.3}}, "DCA_xy (fraction estimation, needs FTrackDcas joined)"}; + o2::framework::ConfigurableAxis fractionDcaZ{"fractionDcaZ", {{300, -0.3, 0.3}}, "DCA_z (fraction estimation, needs FTrackDcas joined)"}; }; constexpr const char PrefixTrackBinning1[] = "TrackBinning1"; @@ -401,6 +413,12 @@ constexpr std::array, kTrackHistLast> {kTpctofDeuteron, o2::framework::HistType::kTH2F, "hTpctofPidDeuteron", "TPC+TOF PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{TOF,de}^{2}}"}, {kTpctofTriton, o2::framework::HistType::kTH2F, "hTpctofPidTriton", "TPC+TOF PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{TOF,tr}^{2}}"}, {kTpctofHelium, o2::framework::HistType::kTH2F, "hTpctofPidHelium", "TPC+TOF PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{TOF,he}^{2}}"}, + {kItsPurity, o2::framework::HistType::kTH2F, "hItsPurity", "ITS purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{ITS}"}, + {kTpcPurity, o2::framework::HistType::kTH2F, "hTpcPurity", "TPC purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TPC}"}, + {kTofPurity, o2::framework::HistType::kTH2F, "hTofPurity", "TOF purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TOF}"}, + {kTpcitsPurity, o2::framework::HistType::kTH2F, "hTpcitsPurity", "TPC+ITS combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{ITS}^{2}}"}, + {kTpctofPurity, o2::framework::HistType::kTH2F, "hTpctofPurity", "TPC+TOF combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{TOF}^{2}}"}, + {kFractionDca, o2::framework::HistType::kTHnSparseF, "hFractionDca", "DCA for fraction extraction; p_{T} (GeV/#it{c}) ; DCA_{xy} (cm) ; DCA_{z} (cm)"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of selected tracks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of selected tracks; PDG Code; Entries"}, @@ -420,12 +438,18 @@ constexpr std::array, kTrackHistLast> }}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRACK_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kSign, {(conf).sign}}, \ - {kMass, {(conf).mass}}, +#define TRACK_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kSign, {(conf).sign}}, \ + {kMass, {(conf).mass}}, \ + {kItsPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTofPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcitsPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kTpctofPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kFractionDca, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRACK_HIST_QA_MAP(confAnalysis, confQa) \ @@ -630,6 +654,7 @@ constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view PidDir = "PID/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; template class TrackHistManager @@ -642,11 +667,13 @@ class TrackHistManager template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, - T const& ConfTrackSelection) + T const& ConfTrackSelection, + int puritySlotMask = 1) { mHistogramRegistry = registry; mAbsCharge = std::abs(ConfTrackSelection.chargeAbs.value); mPdgCode = std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value; + mPuritySlotMask = puritySlotMask; if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->initAnalysis(Specs); } @@ -656,6 +683,9 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kMc)) { this->initMc(Specs); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->initFitVars(Specs); + } } template @@ -711,6 +741,9 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(track); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(track); + } } template @@ -725,6 +758,9 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(track, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(track); + } } private: @@ -765,6 +801,19 @@ class TrackHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {Specs.at(kMass)}); } + // purity (packed nSigma tables) and fraction (DCA) -- only called when mode carries kFitVars, + // which also implies the track type actually has the relevant tables joined + void initFitVars(std::map> const& Specs) + { + std::string fitVarsDir = std::string(prefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kItsPurity, HistTable), getHistDesc(kItsPurity, HistTable), getHistType(kItsPurity, HistTable), {Specs.at(kItsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcPurity, HistTable), getHistDesc(kTpcPurity, HistTable), getHistType(kTpcPurity, HistTable), {Specs.at(kTpcPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTofPurity, HistTable), getHistDesc(kTofPurity, HistTable), getHistType(kTofPurity, HistTable), {Specs.at(kTofPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionDca, HistTable), getHistDesc(kFractionDca, HistTable), getHistType(kFractionDca, HistTable), {Specs.at(kFractionDca)}); + } + void initQa(std::map> const& Specs) { std::string qaDir = std::string(prefix) + std::string(QaDir); @@ -948,6 +997,55 @@ class TrackHistManager } } + // purity (packed nSigma tables) and fraction (DCA) -- fills whichever of the two is actually + // joined on T, independently of the other; mPuritySlotMask picks the species slot (one bit set, + // e.g. from cutculator reading hPuritySlotAssignment), everything is filled unconditionally + // once that slot is decoded -- no momentum-regime selection, the analyst picks the right + // combination/range downstream + template + void fillFitVars(T const& track) + { + if constexpr (requires(T t) { t.itsNSigmaSlot0(); }) { + float itsNSigma = 0.f; + float tpcNSigma = 0.f; + float tofNSigma = 0.f; + switch (mPuritySlotMask) { + case 1: + itsNSigma = track.itsNSigmaSlot0(); + tpcNSigma = track.tpcNSigmaSlot0(); + tofNSigma = track.tofNSigmaSlot0(); + break; + case 2: + itsNSigma = track.itsNSigmaSlot1(); + tpcNSigma = track.tpcNSigmaSlot1(); + tofNSigma = track.tofNSigmaSlot1(); + break; + case 4: + itsNSigma = track.itsNSigmaSlot2(); + tpcNSigma = track.tpcNSigmaSlot2(); + tofNSigma = track.tofNSigmaSlot2(); + break; + case 8: + itsNSigma = track.itsNSigmaSlot3(); + tpcNSigma = track.tpcNSigmaSlot3(); + tofNSigma = track.tofNSigmaSlot3(); + break; + default: + LOG(fatal) << "puritySlotMask must have exactly one bit set (1, 2, 4 or 8), got " << mPuritySlotMask << ". Breaking..."; + } + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kItsPurity, HistTable)), mAbsCharge * track.pt(), itsNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcPurity, HistTable)), mAbsCharge * track.pt(), tpcNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcitsPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, itsNSigma)); + if (track.purityHasTof()) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTofPurity, HistTable)), mAbsCharge * track.pt(), tofNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpctofPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, tofNSigma)); + } + } + if constexpr (requires(T t) { t.dcaXY(); }) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionDca, HistTable)), mAbsCharge * track.pt(), track.dcaXY(), track.dcaZ()); + } + } + template void fillQa(T const& track) { @@ -1161,6 +1259,7 @@ class TrackHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mAbsCharge = 1; int mPdgCode = 0; + int mPuritySlotMask = 1; bool mPlot2d = false; bool mPlotElectronPid = false; bool mPlotPionPid = false; diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 6fa5651970f..9de9dbe28b3 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -391,6 +391,38 @@ DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaDe, tpctofNSigmaDe, [](float tpc, float t DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaTr, tpctofNSigmaTr, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for triton DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaHe, tpctofNSigmaHe, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for helium3 +// --------------------------------------------------------------------------- +// purity: up to 4 species' nSigma (per detector: Its, Tpc, Tof) packed into a +// single int64_t column each, see utils::packPurity4/unpackPurity4. Which +// species occupies which of the 4 slots is decided at producer init time +// (from the active requirePidXxx flags, in canonical species order) and +// exposed via a self-documenting histogram -- it is not stored per-row. +// --------------------------------------------------------------------------- +constexpr float NSigmaPurityMagMin = 1e-3f; // resolution floor near the signal peak +constexpr float NSigmaPurityMagMax = 50.f; // clamp for the tails + +DECLARE_SOA_COLUMN(ItsPackedNSigma, itsPackedNSigma, int64_t); //! 4 species' Its nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TpcPackedNSigma, tpcPackedNSigma, int64_t); //! 4 species' Tpc nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TofPackedNSigma, tofPackedNSigma, int64_t); //! 4 species' Tof nSigma, log-binned and packed +// if false, all 4 Tof slots are meaningless placeholders (producer writes 0 to all of them) -- +// check this flag before reading any TofNSigmaSlotN; never infer missingness from the slot value +DECLARE_SOA_COLUMN(PurityHasTof, purityHasTof, bool); //! whether the Tof slots are meaningful for this track (species-independent) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +// each dynamic column below is bound to its PackedNSigma column at the table-declaration +// site, e.g. femtotracks::ItsNSigmaSlot0 +#define FEMTO_PURITY_SLOT_COLUMN(Detector, detector) \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot0, detector##NSigmaSlot0, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 0, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot1, detector##NSigmaSlot1, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 1, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot2, detector##NSigmaSlot2, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 2, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot3, detector##NSigmaSlot3, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 3, NSigmaPurityMagMin, NSigmaPurityMagMax); }); + +FEMTO_PURITY_SLOT_COLUMN(Its, its) +FEMTO_PURITY_SLOT_COLUMN(Tpc, tpc) +FEMTO_PURITY_SLOT_COLUMN(Tof, tof) +#undef FEMTO_PURITY_SLOT_COLUMN +// NOLINTEND(cppcoreguidelines-macro-usage) + } // namespace femtotracks // table for basic track information @@ -445,6 +477,7 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackDcas_001, "FTRACKDCAS", 1, //! track dc femtotracks::DcaZ, femtotracks::Dca); using FTrackDcas = FTrackDcas_001; +using StoredFTrackDcas = StoredFTrackDcas_001; // table for extra track information DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track extra information @@ -523,6 +556,39 @@ using FHeliumPids = FHeliumPids_001; using FTrackPids = soa::Join; +// tables for track purity extraction: up to 4 species' nSigma packed per detector (see +// femtotracks::PurityNSlots / utils::packPurity4). Slot assignment is not stored here -- +// it is exposed separately via a self-documenting histogram at producer init time. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FItsPurity_001, "FITSPURITY", 1, //! up to 4 species' Its nSigma, packed + femtotracks::ItsPackedNSigma, + femtotracks::ItsNSigmaSlot0, + femtotracks::ItsNSigmaSlot1, + femtotracks::ItsNSigmaSlot2, + femtotracks::ItsNSigmaSlot3); +using FItsPurity = FItsPurity_001; +using StoredFItsPurity = StoredFItsPurity_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTpcPurity_001, "FTPCPURITY", 1, //! up to 4 species' Tpc nSigma, packed + femtotracks::TpcPackedNSigma, + femtotracks::TpcNSigmaSlot0, + femtotracks::TpcNSigmaSlot1, + femtotracks::TpcNSigmaSlot2, + femtotracks::TpcNSigmaSlot3); +using FTpcPurity = FTpcPurity_001; +using StoredFTpcPurity = StoredFTpcPurity_001; + +// also carries PurityHasTof (species-independent: whether this track reached Tof at all) -- +// the most natural home for it, since it only matters for interpreting the Tof slots +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTofPurity_001, "FTOFPURITY", 1, //! up to 4 species' Tof nSigma, packed + femtotracks::TofPackedNSigma, + femtotracks::PurityHasTof, + femtotracks::TofNSigmaSlot0, + femtotracks::TofNSigmaSlot1, + femtotracks::TofNSigmaSlot2, + femtotracks::TofNSigmaSlot3); +using FTofPurity = FTofPurity_001; +using StoredFTofPurity = StoredFTofPurity_001; + namespace femtotwotrackresonances { // columns for resonance bit masks @@ -753,6 +819,11 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lam using FLambdaExtras = FLambdaExtras_001; +// table for lambda fraction extraction (cpa only -- mass is already in FLambdas, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaFrac_001, "FLAMBDAFRAC", 1, //! lambda cpa for fraction extraction + femtov0s::CosPa); +using FLambdaFrac = FLambdaFrac_001; + // table for basic k0short information DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shorts_001, "FK0SHORT", 1, //! femto k0shorts o2::soa::Index<>, @@ -815,6 +886,11 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k using FK0shortExtras = FK0shortExtras_001; +// table for k0short fraction extraction (cpa only -- mass is already in FK0shorts, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortFrac_001, "FK0SHORTFRAC", 1, //! k0short cpa for fraction extraction + femtov0s::CosPa); +using FK0shortFrac = FK0shortFrac_001; + namespace femtophotons { // columns for bit masks @@ -1323,6 +1399,12 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiExtras_001, "FXIEXTRA", 1, //! xi extra in femtocascades::CascadePa); using FXiExtras = FXiExtras_001; +// table for xi fraction extraction (cascade cpa only -- mass is already in FXis, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiFrac_001, "FXIFRAC", 1, //! xi cpa for fraction extraction + femtocascades::CascadeCosPa); +using FXiFrac = FXiFrac_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegas_001, "FOMEGA", 1, //! femto omegas o2::soa::Index<>, femtobase::stored::FColId, @@ -1389,6 +1471,12 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaExtras_001, "FOMEGAEXTRA", 1, //! omega femtocascades::CascadePa); using FOmegaExtras = FOmegaExtras_001; +// table for omega fraction extraction (cascade cpa only -- mass is already in FOmegas, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaFrac_001, "FOMEGAFRAC", 1, //! omega cpa for fraction extraction + femtocascades::CascadeCosPa); +using FOmegaFrac = FOmegaFrac_001; + namespace femtocharmhadrons { // bitmask column diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index ea290785cd6..b8cbdd436f1 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -46,6 +46,9 @@ struct FemtoProducerDerivedToDerived { using FemtoTracks = o2::soa::Join; using FemtoTracksWithMass = o2::soa::Join; + // opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- + // needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled + using FemtoTracksWithPurity = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; using FemtoRho0s = o2::soa::Join; @@ -85,6 +88,10 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + o2::framework::Preslice perColTracksWithPurity = o2::aod::femtobase::stored::fColId; + // v0 builder v0builder::V0BuilderDerivedToDerived v0Builder; v0builder::V0BuilderDerivedToDerivedProducts v0BuilderProducts; @@ -159,7 +166,7 @@ struct FemtoProducerDerivedToDerived { void init(o2::framework::InitContext& /*context*/) { const int activeProcesses = - static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + + static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + static_cast(doprocessTracksWithPurity) + static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + static_cast(doprocessLambdas) + static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + @@ -203,6 +210,17 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); + void processTracksWithPurity(FilteredFemtoCollision const& col, FemtoTracksWithPurity const& tracks) + { + if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache)) { + return; + } + trackBuilder.reset(tracks); + collisionBuilder.processCollision(col, collisionBuilderProducts); + trackBuilder.processTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithPurity, "Process tracks, carrying through purity/fraction tables", false); + void processTracksLambdas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) { if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { diff --git a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx index f7f0706e16a..ca7560853da 100644 --- a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx +++ b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx @@ -81,18 +81,27 @@ struct FemtoMcParticleQa { hRegistry.print(); }; - void process(FilteredFemtoMcCollision const& col, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() below (see the same workaround in femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); + // FemtoMcParticles must still appear as an argument (even though it's only accessed via the partition) + // so DPL actually subscribes to it -- dropping it entirely leaves mcParticlesPartition1 unbound and + // segfaults on the first sliceByCachedUnsorted() call + void process(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); - if (mcParticleSlice.size() == 0) { - return; - } - colHistManager.fill(col); - for (auto const& mcParticle : mcParticleSlice) { - if (!mcParticleCleaner.isClean(mcParticle, mcMothers, mcPartonicMothers)) { + for (auto const& col : cols) { + auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + if (mcParticleSlice.size() == 0) { continue; } - mcParticleHistManager1.fill(mcParticle, mcMothers, mcPartonicMothers); + colHistManager.fill(col); + for (auto const& mcParticle : mcParticleSlice) { + if (!mcParticleCleaner.isClean(mcParticle, mcMothers, mcPartonicMothers)) { + continue; + } + mcParticleHistManager1.fill(mcParticle, mcMothers, mcPartonicMothers); + } } } }; diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx index 3f623ab411f..f4eb09769b3 100644 --- a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -132,9 +132,16 @@ struct FemtoPairMcParticleMcParticle { hRegistry.print(); }; - void processSameEvent(FilteredFemtoMcCollision const& col, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache); + for (auto const& col : cols) { + pairMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache); + } } PROCESS_SWITCH(FemtoPairMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index d1e1e423f4f..28b9ce92d65 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -65,7 +65,15 @@ struct FemtoPairTrackTrack { // for analysis which require particles at high pt, add tof mass so sidebands can be used using FemtoTracksWithMass = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; + // opt-in: adds the packed purity tables (nSigma, see TrackBinning1/2.puritySlotMask) and FTrackDcas + // (fraction estimation) for the FitVars histograms. Needs the producer's + // TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled. + using FemtoTracksWithPurity = o2::soa::Join; + + // the Mc process functions always carry the purity/fraction tables (not opt-in like the data + // path's WithPurity variant) -- requires the producer's TrackTables.produceItsPurity/ + // produceTpcPurity/produceTofPurity/produceTrackDcas enabled whenever MC processing is used + using FemtoTracksWithLabel = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -93,6 +101,10 @@ struct FemtoPairTrackTrack { o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(confTrackSelections2); o2::framework::Preslice perColtracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracksWithPurity = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; @@ -130,14 +142,15 @@ struct FemtoPairTrackTrack { void init(o2::framework::InitContext&) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventMc)) > 1 || - (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithPurity) + static_cast(doprocessSameEventMc)) > 1 || + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithPurity) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; + bool processPurity = doprocessSameEventWithPurity || doprocessMixedEventWithPurity; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is activated. Breaking..."; + if (static_cast(processData) + static_cast(processPurity) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; } // setup columnpolicy for binning @@ -155,7 +168,13 @@ struct FemtoPairTrackTrack { std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processPurity) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec, confTrackBinning1.puritySlotMask.value, confTrackBinning2.puritySlotMask.value); + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); @@ -166,7 +185,7 @@ struct FemtoPairTrackTrack { trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec, confTrackBinning1.puritySlotMask.value, confTrackBinning2.puritySlotMask.value); } hRegistry.print(); }; @@ -183,6 +202,12 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithMass, "Enable processing same event processing (with track masses)", false); + void processSameEventWithPurity(FilteredFemtoCollision const& col, FemtoTracksWithPurity const& tracks) + { + pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithPurity, "Enable processing same event processing (with purity/fraction tables)", false); + void processSameEventWithEventShape(FilteredFemtoCollisionWithEventShape const& col, FemtoTracks const& tracks) { pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); @@ -191,7 +216,7 @@ struct FemtoPairTrackTrack { void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventMc, "Enable processing same event processing", false); @@ -207,6 +232,12 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithMass, "Enable processing mixed event processing (with track masses)", false); + void processMixedEventWithPurity(FilteredFemtoCollisions const& cols, FemtoTracksWithPurity const& tracks) + { + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithPurity, "Enable processing mixed event processing (with purity/fraction tables)", false); + void processMixedEventWithEventShape(FilteredFemtoCollisionsWithEventShape const& cols, FemtoTracks const& tracks) { pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); @@ -215,7 +246,7 @@ struct FemtoPairTrackTrack { void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventMc, "Enable processing mixed event processing", false); }; diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx index 60dfa3a3411..c2cee7d7321 100644 --- a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -75,6 +75,7 @@ struct FemtoTripletMcParticleMcParticleMcParticle { o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); o2::framework::Partition mcParticlesPartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); o2::framework::Partition mcParticlesPartition3 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection3); + o2::framework::PresliceUnsorted perColParticles = o2::aod::femtomcparticle::fMcColId; // setup triplets triplethistmanager::ConfTripletBinning confTripletBinning; @@ -133,9 +134,16 @@ struct FemtoTripletMcParticleMcParticleMcParticle { hRegistry.print(); }; - void processSameEvent(FilteredFemtoMcCollision const& col, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache); + for (auto const& col : cols) { + tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache); + } } PROCESS_SWITCH(FemtoTripletMcParticleMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); From fc4710c4e2df48b7dda8e6faade8ecc651404fa5 Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Fri, 30 Oct 2026 01:52:48 +0300 Subject: [PATCH 493/648] [PWGUD] New functionality for Pb-Pb analyses (#18509) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 377 +++++++++++++++--- 1 file changed, 312 insertions(+), 65 deletions(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index 4d2a75be18a..c6c439a0c14 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include #include @@ -45,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +62,6 @@ using namespace o2::aod::rctsel; namespace { constexpr int NBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; -constexpr int MaxStoredDistance = 15; constexpr float MaxFITTime = 30.f; constexpr int NoSignalMCRef = -1; @@ -80,7 +83,7 @@ using SignalRef = std::pair; using DistanceMap = std::vector>; DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, - int64_t bcSOR, int64_t nBCsPerTF, + int64_t bcSOR, int32_t nBCsPerTF, std::vector& activeBCs, uint32_t maxDistance, bool skipFirstExactMatch = false) { @@ -95,7 +98,7 @@ DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, size_t nextIndex = 0; for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { const int64_t tfStartBC = bcSOR + tfIDs[iTF] * nBCsPerTF; - for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { + for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { // TODO why int64_t const int64_t currentBC = tfStartBC + bcInTF; while (nextIndex < activeBCs.size() && activeBCs[nextIndex] < currentBC) { ++nextIndex; @@ -118,26 +121,13 @@ DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, return distances; } -void fillVetoHistograms(std::shared_ptr const& hVetoT00, std::shared_ptr const& hVetoTV0, - std::shared_ptr const& hVetoTVD, int bcInOrbit, +void fillVetoHistograms(std::shared_ptr const& hVetoT00, std::shared_ptr const& hVetoTV0, + std::shared_ptr const& hVetoTVD, int bcInOrbit, int tfID, uint32_t distanceFT0, uint32_t distanceFV0, uint32_t distanceFDD) { - hVetoT00->Fill(bcInOrbit, -1); - hVetoTV0->Fill(bcInOrbit, -1); - hVetoTVD->Fill(bcInOrbit, -1); - for (uint32_t threshold = 0; threshold <= MaxStoredDistance; ++threshold) { - if (distanceFT0 <= threshold) { - continue; - } - hVetoT00->Fill(bcInOrbit, threshold); - if (distanceFV0 <= threshold) { - continue; - } - hVetoTV0->Fill(bcInOrbit, threshold); - if (distanceFDD > threshold) { - hVetoTVD->Fill(bcInOrbit, threshold); - } - } + hVetoT00->Fill(bcInOrbit, tfID, distanceFT0); + hVetoTV0->Fill(bcInOrbit, tfID, std::min(distanceFT0, distanceFV0)); + hVetoTVD->Fill(bcInOrbit, tfID, std::min({distanceFT0, distanceFV0, distanceFDD})); } } // namespace @@ -147,6 +137,8 @@ DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(TfId, tfId, uint32_t); DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(Flags, flags, int8_t); +DECLARE_SOA_COLUMN(OccupancyFT0C, occupancyFT0C, float); DECLARE_SOA_COLUMN(PosX, posX, float); DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); @@ -184,6 +176,8 @@ DECLARE_SOA_TABLE(UPCBarrelCands, "AOD", "UPCBARRELCANDS", upc_cand_prod_bar::GlobalBC, upc_cand_prod_bar::TfId, upc_cand_prod_bar::Timestamp, + upc_cand_prod_bar::Flags, + upc_cand_prod_bar::OccupancyFT0C, upc_cand_prod_bar::PosX, upc_cand_prod_bar::PosY, upc_cand_prod_bar::PosZ, @@ -243,12 +237,19 @@ struct UpcCandProducerBarrel { int cachedRunNumber = -1; int64_t bcSOR = 0; - int64_t nBCsPerTF = 0; + int32_t nBCsPerTF = 0; int configuredOrbitsPerTF = -1; int configuredTFStartBorder = -1; int configuredTFEndBorder = -1; + int configuredITSROFStartBorder = -1; + int configuredITSROFEndBorder = -1; int tfStartBorder = 0; int tfEndBorder = 0; + int rofOffset = 0; + int rofLength = 0; + int nRofPerTF = 0; + int rofStartBorderMargin = 0; + int rofEndBorderMargin = 0; std::bitset collidingBCs; Configurable nTracks{"nTracks", 2, "Required N tracks in selected collisions"}; @@ -258,8 +259,19 @@ struct UpcCandProducerBarrel { Configurable vetoFT0{"vetoFT0", 1, "FT0 veto: 0=off, 1=on"}; Configurable vetoFV0{"vetoFV0", 0, "FV0 veto: 0=off, 1=on"}; Configurable vetoFDD{"vetoFDD", 0, "FDD veto: 0=off, 1=on"}; + Configurable useTCE{"useTCE", 0, "Use TCE for lumi accounting: 0=off, 1=on"}; Configurable signalSourceId{"signalSourceId", 1, "MC source ID stored as embedded signal"}; + Configurable minAmpFV0A{"minAmpFV0A", 150.f, "Minimum amplitude for FV0A veto"}; + Configurable minAmpFT0A{"minAmpFT0A", 150.f, "Minimum amplitude for FT0A veto"}; + Configurable minAmpFT0C{"minAmpFT0C", 50.f, "Minimum amplitude for FT0C veto"}; + Configurable minAmpFDDA{"minAmpFDDA", 1000.f, "Minimum amplitude for FDDA veto"}; + Configurable minAmpFDDC{"minAmpFDDC", 1000.f, "Minimum amplitude for FDDC veto"}; + + Configurable maxStoredDistance{"maxStoredDistance", 32, "Maximum stored veto distance"}; + Configurable nBinsOccupancy{"nBinsOccupancy", 25, "Number of occupancy bins"}; + Configurable nBinsTF{"nBinsTF", 100, "Number of TF bins"}; + using CollisionsWithSels = soa::Join; using BCsWithSels = soa::Join; using TracksWithPID = soa::Join -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) + wOccup = 1; + else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + return wOccup; + } + + bool noRofBorder(int64_t bc) + { + int bcInRof = (bc + rofLength - rofOffset) % rofLength; + return bcInRof > rofStartBorderMargin && bcInRof < rofLength - rofEndBorderMargin; + } + + int getRofInTF(int64_t bc) + { + return (bc + rofLength - rofOffset) / rofLength % nRofPerTF; + } + void init(InitContext& initContext) { ccdb->setLocalObjectValidityChecking(); @@ -287,15 +324,49 @@ struct UpcCandProducerBarrel { inheritEventSelectionOption("bcselOpts.NumberOfOrbitsPerTF", configuredOrbitsPerTF); inheritEventSelectionOption("bcselOpts.TimeFrameStartBorderMargin", configuredTFStartBorder); inheritEventSelectionOption("bcselOpts.TimeFrameEndBorderMargin", configuredTFEndBorder); - registry.add("hProcessedTFs", "Processed TFs;;TFs", HistType::kTH1D, {{1, 0., 1.}}); - registry.add("hTVX", "TVX counts per run;run;TVX BCs", HistType::kTH1D, {{NBCsPerOrbit, 0., NBCsPerOrbit}}); - registry.add("hTVXRCT", "TVX counts per run;run;TVX BCs", HistType::kTH1D, {{NBCsPerOrbit, 0., NBCsPerOrbit}}); - registry.add("hVetoT00", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTV0", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTVD", ";BC in orbit;veto range (BC);colliding BC count, no RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoT00RCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTV0RCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); - registry.add("hVetoTVDRCT", ";BC in orbit;veto range (BC);colliding BC count, RCT mask", HistType::kTH2D, {{NBCsPerOrbit, 0., NBCsPerOrbit}, {17, -1.5, 15.5}}); + inheritEventSelectionOption("bcselOpts.ITSROFrameStartBorderMargin", configuredITSROFStartBorder); + inheritEventSelectionOption("bcselOpts.ITSROFrameEndBorderMargin", configuredITSROFEndBorder); + + const AxisSpec axisBCs{NBCsPerOrbit, 0., NBCsPerOrbit, "BC in orbit"}; + const AxisSpec axisVeto{maxStoredDistance + 1, -0.5, maxStoredDistance + 0.5, "veto range in BCs"}; + const AxisSpec axisOccupancy{nBinsOccupancy, 0., 250000., "FT0-based occupancy"}; + const AxisSpec axisTFs{nBinsTF, 0., 4.e7, "TFs"}; // 100 bins -> 400000 TFs per bin (1 bin ~ 1140 seconds for 32 orbits per TF) + + registry.add("hTVX", "TVX counts", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCT", "TVX counts after RCT selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROF", "TVX counts after RCT and ROF pileup selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXupc", "TVX counts in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTupc", "TVX counts after RCT selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROFupc", "TVX counts after RCT and ROF pileup selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + + registry.add("hVetoT00", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVD", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + + registry.add("hVetoT00upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDupc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + LOGF(info, "Veto config: window=%d FT0=%d FV0=%d FDD=%d", vetoBCWindow.value, vetoFT0.value, vetoFV0.value, vetoFDD.value); } @@ -307,7 +378,8 @@ struct UpcCandProducerBarrel { const auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), runNumber); const int64_t orbitsPerTF = configuredOrbitsPerTF < 0 ? runInfo.orbitsPerTF : configuredOrbitsPerTF; const bool needsEventSelectionParams = configuredTFStartBorder < 0 || configuredTFEndBorder < 0; - const auto* eventSelectionParams = needsEventSelectionParams ? ccdb->getForTimeStamp("EventSelection/EventSelectionParams", runInfo.sor / 2 + runInfo.eor / 2) : nullptr; + const auto ts = (runInfo.sor + runInfo.eor) / 2; + const auto* eventSelectionParams = needsEventSelectionParams ? ccdb->getForTimeStamp("EventSelection/EventSelectionParams", ts) : nullptr; if (orbitsPerTF <= 0 || !runInfo.grpLHC || (needsEventSelectionParams && !eventSelectionParams)) { LOGF(fatal, "Incomplete run information for run %d", runNumber); } @@ -316,6 +388,12 @@ struct UpcCandProducerBarrel { tfStartBorder = configuredTFStartBorder < 0 ? eventSelectionParams->fTimeFrameStartBorderMargin : configuredTFStartBorder; tfEndBorder = configuredTFEndBorder < 0 ? eventSelectionParams->fTimeFrameEndBorderMargin : configuredTFEndBorder; collidingBCs = runInfo.grpLHC->getBunchFilling().getBCPattern(); + + // get ITS ROF parameters + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", ts); + rofOffset = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + nRofPerTF = nBCsPerTF / rofLength; cachedRunNumber = runNumber; } @@ -340,7 +418,7 @@ struct UpcCandProducerBarrel { } localTFIndex.push_back(tfIDs.size() - 1); } - registry.fill(HIST("hProcessedTFs"), 0.5, static_cast(tfIDs.size())); + LOGP(info, "Starting new DF. Number of TFs = {}", tfIDs.size()); std::unordered_map signalParticleRefs; if constexpr (isMC) { @@ -356,7 +434,7 @@ struct UpcCandProducerBarrel { for (const auto& mcCollision : *mcCollisions) { bcsWithMCCollision.push_back(static_cast(mcCollision.template bc_as().globalBC())); } - const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, MaxStoredDistance, true); + const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, maxStoredDistance, true); for (const auto& mcCollision : *mcCollisions) { const auto bc = mcCollision.template bc_as(); @@ -416,78 +494,246 @@ struct UpcCandProducerBarrel { } } - auto hTVX = registry.get(HIST("hTVX")); - auto hTVXRCT = registry.get(HIST("hTVXRCT")); + // collect number of all collisions and upc-mode collisions per ROF + LOGP(debug, "Collect number of all collisions and upc-mode collisions per ROF"); + std::vector> vMultTracks(tfIDs.size(), std::map()); + std::vector> vCollisionsPerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + std::vector> vUpcModePerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + const int64_t globalBC = bc.globalBC(); + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const int32_t rofInTF = getRofInTF(bcInTF); + vCollisionsPerROF[iTF][rofInTF]++; + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + if (upcMode) { + vUpcModePerROF[iTF][rofInTF]++; + } + } + + auto hTVX = registry.get(HIST("hTVX")); + auto hTVXRCT = registry.get(HIST("hTVXRCT")); + auto hTVXRCTnoPileupROF = registry.get(HIST("hTVXRCTnoPileupROF")); + + auto hTVXupc = registry.get(HIST("hTVXupc")); + auto hTVXRCTupc = registry.get(HIST("hTVXRCTupc")); + auto hTVXRCTnoPileupROFupc = registry.get(HIST("hTVXRCTnoPileupROFupc")); std::vector bcsWithFT0, bcsWithFV0, bcsWithFDD; bcsWithFT0.reserve(ft0s.size()); bcsWithFV0.reserve(fv0s.size()); bcsWithFDD.reserve(fdds.size()); + LOGP(debug, "Starting lumi accounting"); + // lumi accounting + std::vector> vMultFT0C(tfIDs.size(), std::map()); for (const auto& ft0 : ft0s) { const auto bc = ft0.bc_as(); - const auto gbc = bc.globalBC(); - const auto bcInOrbit = gbc % NBCsPerOrbit; - if (ft0.timeA() < MaxFITTime || ft0.timeC() < MaxFITTime) { - bcsWithFT0.push_back(gbc); + const auto globalBC = bc.globalBC(); + const auto bcInOrbit = globalBC % NBCsPerOrbit; + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const auto tfID = tfIDs[iTF]; + const int32_t rofInTF = getRofInTF(bcInTF); + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + + // Multiplicity for FT0C-based occupancy estimates + if (ft0.timeC() < MaxFITTime && ft0.sumAmpC() >= minAmpFT0C) { + vMultFT0C[iTF][bcInTF] = ft0.sumAmpC(); + } + + if (!collidingBCs[bcInOrbit] || !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitVertex)) { + continue; } - if (!collidingBCs[bcInOrbit] || !(ft0.triggerMask() & (1U << o2::fit::Triggers::bitVertex))) { + + if (useTCE && !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitCen)) { continue; } - hTVX->Fill(bcInOrbit); + + hTVX->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXupc->Fill(bcInOrbit, tfID); + } + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(bc)) { continue; } - hTVXRCT->Fill(bcInOrbit); + + hTVXRCT->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXRCTupc->Fill(bcInOrbit, tfID); + } + + if (pileupInROF) { + continue; + } + + hTVXRCTnoPileupROF->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXRCTnoPileupROFupc->Fill(bcInOrbit, tfID); + } + } + + LOGP(debug, "Filling FT0 activity arrays"); + for (const auto& ft0 : ft0s) { + const float sumAmpFT0A = ft0.sumAmpA(); + const float sumAmpFT0C = ft0.sumAmpC(); + if ((ft0.timeA() < MaxFITTime && sumAmpFT0A >= minAmpFT0A) || (ft0.timeC() < MaxFITTime && sumAmpFT0C >= minAmpFT0C)) { + bcsWithFT0.push_back(ft0.bc_as().globalBC()); + } } + + LOGP(debug, "Filling FV0 activity arrays"); for (const auto& fv0 : fv0s) { - if (fv0.time() < MaxFITTime) { + const float sumAmpFV0A = std::accumulate(fv0.amplitude().begin(), fv0.amplitude().end(), 0.f); + if (fv0.time() < MaxFITTime && sumAmpFV0A >= minAmpFV0A) { bcsWithFV0.push_back(fv0.bc_as().globalBC()); } } + + LOGP(debug, "Filling FDD activity arrays"); for (const auto& fdd : fdds) { - if (fdd.timeA() < MaxFITTime || fdd.timeC() < MaxFITTime) { + const float sumAmpFDDA = std::accumulate(fdd.chargeA(), fdd.chargeA() + 8, 0.f); + const float sumAmpFDDC = std::accumulate(fdd.chargeC(), fdd.chargeC() + 8, 0.f); + if ((fdd.timeA() < MaxFITTime && sumAmpFDDA >= minAmpFDDA) || (fdd.timeC() < MaxFITTime && sumAmpFDDC >= minAmpFDDC)) { bcsWithFDD.push_back(fdd.bc_as().globalBC()); } } - const uint32_t scanDistance = std::max(MaxStoredDistance, vetoBCWindow.value); + LOGP(debug, "Starting occupancy calculation"); + std::vector> vOccupancyFT0C(tfIDs.size(), std::vector(nBCsPerTF, 0.f)); + int deltaBcPast = 1600; + int deltaBcFuture = 4000; + // TODO: calculate weights only once per task + std::vector weights(deltaBcPast + deltaBcFuture + 1); + for (int d = -deltaBcPast; d <= deltaBcFuture; ++d) { + weights[d + deltaBcPast] = calcWeightForOccupancy(d * o2::constants::lhc::LHCBunchSpacingMUS); + } + + for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { + for (const auto& [bc, multFT0C] : vMultFT0C[iTF]) { + LOGP(debug, "iTF={} bc={} mult={}", iTF, bc, multFT0C); + const int bcInTFMin = std::max(bc - deltaBcFuture, 0); + const int bcInTFMax = std::min(bc + deltaBcPast, nBCsPerTF - 1); + for (int bcInTF = bcInTFMin; bcInTF <= bcInTFMax; ++bcInTF) { + vOccupancyFT0C[iTF][bcInTF] += weights[bc - bcInTF + deltaBcPast] * multFT0C; + } + } + } + + LOGP(debug, "Building minimum distance maps"); + const uint32_t scanDistance = std::max(maxStoredDistance.value, vetoBCWindow.value); const DistanceMap minDistanceFT0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFT0, scanDistance); const DistanceMap minDistanceFV0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFV0, scanDistance); const DistanceMap minDistanceFDD = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFDD, scanDistance); - auto hVetoT00 = registry.get(HIST("hVetoT00")); - auto hVetoTV0 = registry.get(HIST("hVetoTV0")); - auto hVetoTVD = registry.get(HIST("hVetoTVD")); - auto hVetoT00RCT = registry.get(HIST("hVetoT00RCT")); - auto hVetoTV0RCT = registry.get(HIST("hVetoTV0RCT")); - auto hVetoTVDRCT = registry.get(HIST("hVetoTVDRCT")); + auto hVetoT00 = registry.get(HIST("hVetoT00")); + auto hVetoTV0 = registry.get(HIST("hVetoTV0")); + auto hVetoTVD = registry.get(HIST("hVetoTVD")); + auto hVetoT00RCT = registry.get(HIST("hVetoT00RCT")); + auto hVetoTV0RCT = registry.get(HIST("hVetoTV0RCT")); + auto hVetoTVDRCT = registry.get(HIST("hVetoTVDRCT")); + auto hVetoT00RCTnoPileupROF = registry.get(HIST("hVetoT00RCTnoPileupROF")); + auto hVetoTV0RCTnoPileupROF = registry.get(HIST("hVetoTV0RCTnoPileupROF")); + auto hVetoTVDRCTnoPileupROF = registry.get(HIST("hVetoTVDRCTnoPileupROF")); + + auto hVetoT00upc = registry.get(HIST("hVetoT00upc")); + auto hVetoTV0upc = registry.get(HIST("hVetoTV0upc")); + auto hVetoTVDupc = registry.get(HIST("hVetoTVDupc")); + auto hVetoT00RCTupc = registry.get(HIST("hVetoT00RCTupc")); + auto hVetoTV0RCTupc = registry.get(HIST("hVetoTV0RCTupc")); + auto hVetoTVDRCTupc = registry.get(HIST("hVetoTVDRCTupc")); + auto hVetoT00RCTnoPileupROFupc = registry.get(HIST("hVetoT00RCTnoPileupROFupc")); + auto hVetoTV0RCTnoPileupROFupc = registry.get(HIST("hVetoTV0RCTnoPileupROFupc")); + auto hVetoTVDRCTnoPileupROFupc = registry.get(HIST("hVetoTVDRCTnoPileupROFupc")); + + auto hOccupancyT00RCTnoPileupROF = registry.get(HIST("hOccupancyT00RCTnoPileupROF")); + auto hOccupancyTV0RCTnoPileupROF = registry.get(HIST("hOccupancyTV0RCTnoPileupROF")); + auto hOccupancyTVDRCTnoPileupROF = registry.get(HIST("hOccupancyTVDRCTnoPileupROF")); + + auto hOccupancyT00RCTnoPileupROFupc = registry.get(HIST("hOccupancyT00RCTnoPileupROFupc")); + auto hOccupancyTV0RCTnoPileupROFupc = registry.get(HIST("hOccupancyTV0RCTnoPileupROFupc")); + auto hOccupancyTVDRCTnoPileupROFupc = registry.get(HIST("hOccupancyTVDRCTnoPileupROFupc")); + + LOGP(debug, "Filling veto efficiency histograms"); for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; - for (int64_t bcInTF = tfStartBorder + 1; bcInTF < nBCsPerTF - tfEndBorder; ++bcInTF) { + const auto tfID = tfIDs[iTF]; + for (int64_t bcInTF = tfStartBorder + 1; bcInTF < nBCsPerTF - tfEndBorder; ++bcInTF) { // TODO why int64_t? const int64_t globalBC = tfStartBC + bcInTF; const int bcInOrbit = globalBC % NBCsPerOrbit; - if (!collidingBCs[bcInOrbit]) { - continue; - } + const int32_t rofInTF = getRofInTF(bcInTF); + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; const auto distanceFT0 = minDistanceFT0[iTF][bcInTF]; const auto distanceFV0 = minDistanceFV0[iTF][bcInTF]; const auto distanceFDD = minDistanceFDD[iTF][bcInTF]; - fillVetoHistograms(hVetoT00, hVetoTV0, hVetoTVD, bcInOrbit, distanceFT0, distanceFV0, distanceFDD); - if (tfPassesRCT[iTF] != 0u) { - fillVetoHistograms(hVetoT00RCT, hVetoTV0RCT, hVetoTVDRCT, bcInOrbit, distanceFT0, distanceFV0, distanceFDD); + + fillVetoHistograms(hVetoT00, hVetoTV0, hVetoTVD, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00upc, hVetoTV0upc, hVetoTVDupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (tfPassesRCT[iTF] == 0u) { + continue; + } + + fillVetoHistograms(hVetoT00RCT, hVetoTV0RCT, hVetoTVDRCT, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTupc, hVetoTV0RCTupc, hVetoTVDRCTupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (pileupInROF) { + continue; + } + + fillVetoHistograms(hVetoT00RCTnoPileupROF, hVetoTV0RCTnoPileupROF, hVetoTVDRCTnoPileupROF, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTnoPileupROFupc, hVetoTV0RCTnoPileupROFupc, hVetoTVDRCTnoPileupROFupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (distanceFT0 == 0) + continue; + float occupancy = vOccupancyFT0C[iTF][bcInTF]; + hOccupancyT00RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyT00RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFV0 == 0) + continue; + hOccupancyTV0RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTV0RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFDD == 0) + continue; + hOccupancyTVDRCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTVDRCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); } } } + LOGP(debug, "Filling upc trees"); for (const auto& collision : collisions) { if (collision.numContrib() != nTracks || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(collision)) { continue; } auto bc = collision.template bc_as(); - auto gbc = bc.globalBC(); + int64_t globalBC = bc.globalBC(); const auto iTF = localTFIndex[bc.globalIndex()]; - const int64_t bcInTF = (static_cast(gbc) - bcSOR) % nBCsPerTF; + const int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const int32_t rofInTF = getRofInTF(bcInTF); + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + const bool noPileupInRof = vCollisionsPerROF[iTF][rofInTF] <= 1; + uint8_t flags = 0; + flags |= upcMode << 0; + flags |= noPileupInRof << 1; + const auto occupancyFT0C = vOccupancyFT0C[iTF][bcInTF]; const auto distFT0 = minDistanceFT0[iTF][bcInTF]; const auto distFV0 = minDistanceFV0[iTF][bcInTF]; const auto distFDD = minDistanceFDD[iTF][bcInTF]; @@ -537,12 +783,13 @@ struct UpcCandProducerBarrel { if (px.size() != static_cast(nTracks)) { continue; } - selectedCandidates(bc.runNumber(), gbc, tfIDs[iTF], bc.timestamp(), collision.posX(), collision.posY(), collision.posZ(), + selectedCandidates(bc.runNumber(), globalBC, tfIDs[iTF], bc.timestamp(), flags, occupancyFT0C, + collision.posX(), collision.posY(), collision.posZ(), px, py, pz, tpcSignal, tpcNSigmaEl, tpcNSigmaPi, tpcNSigmaKa, tpcNSigmaPr, tofNSigmaEl, tofNSigmaPi, tofNSigmaKa, tofNSigmaPr, itsClusterMap, nClusters, sign, - std::min(distFT0, MaxStoredDistance + 1), - std::min(distFV0, MaxStoredDistance + 1), - std::min(distFDD, MaxStoredDistance + 1)); + std::min(distFT0, maxStoredDistance + 1), + std::min(distFV0, maxStoredDistance + 1), + std::min(distFDD, maxStoredDistance + 1)); if constexpr (isMC) { selectedCandidateMCLabels(newMCCollisionIds, newParticleIds); } From d631832946cc9f4da7e377e2b028703669ddfdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 30 Oct 2026 00:18:12 +0100 Subject: [PATCH 494/648] [Infrastructure] Clang-Tidy: Promote bad casts to errors (#18515) --- .clang-tidy | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 9423f438d68..f7f00391f25 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,6 +42,7 @@ Checks: - google-default-arguments # - google-explicit-constructor # triggered for O2 columns - google-global-names-in-headers + - google-readability-casting # same as cpplint:readability/casting # - misc-const-correctness # to be checked - misc-header-include-cycle - misc-include-cleaner @@ -111,8 +112,6 @@ WarningsAsErrors: >- *, -readability-braces-around-statements, -readability-suspicious-call-argument, - -modernize-*, - -readability-*, -bugprone-assignment-in-if-condition, -bugprone-branch-clone, -bugprone-copy-constructor-init, @@ -140,17 +139,20 @@ WarningsAsErrors: >- -cppcoreguidelines-missing-std-forward, -cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, - -cppcoreguidelines-pro-type-const-cast, - -cppcoreguidelines-pro-type-cstyle-cast, -cppcoreguidelines-pro-type-member-init, - -cppcoreguidelines-pro-type-reinterpret-cast, - -cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-slicing, -cppcoreguidelines-virtual-class-destructor, + -modernize-*, + -readability-*, + readability-redundant-casting, # Positives for the following checks are currently being fixed and should be reported as errors. # clang-analyzer-* # clang-diagnostic-* +# cppcoreguidelines-pro-type-const-cast +# cppcoreguidelines-pro-type-cstyle-cast +# cppcoreguidelines-pro-type-reinterpret-cast +# cppcoreguidelines-pro-type-static-cast-downcast # google-default-arguments # misc-include-cleaner # misc-redundant-expression From 0274616c7dde0a360978c50416bdf3e934582287 Mon Sep 17 00:00:00 2001 From: Preet-Bhanjan Date: Fri, 30 Oct 2026 00:27:08 +0100 Subject: [PATCH 495/648] [PWGCF] Addition of MC truth for correlations (#18511) Co-authored-by: Preet Pati --- .../Tasks/pidDiHadron.cxx | 227 +++++++++++++++++- 1 file changed, 218 insertions(+), 9 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx index 5c4102c666d..064272be3d0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx @@ -136,6 +136,8 @@ struct PidDiHadron { Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + O2_DEFINE_CONFIGURABLE(cfgMCTrigIsPhysPrim, bool, false, "Switch for using only physical primary trigger particles in MC correlations"); + O2_DEFINE_CONFIGURABLE(cfgMCAsscIsPhysPrim, bool, false, "Switch for using only physical primary associated particles in MC correlations"); } cfgTrkSel; struct : ConfigurableGroup { @@ -148,6 +150,8 @@ struct PidDiHadron { O2_DEFINE_CONFIGURABLE(cfgPIDUseOnlyTpc, bool, true, "Use only TPC PID for daughter selection") O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 0 for no PID") O2_DEFINE_CONFIGURABLE(cfgPIDUseReso, bool, false, "Switch to turn on resonance analysis") + O2_DEFINE_CONFIGURABLE(cfgPIDNclsPIDCut, int, 70, "Number of clusters used for PID selection") + O2_DEFINE_CONFIGURABLE(cfgPIDUseNclsPID, bool, false, "Switch for using cfgPIDNclsPIDCut") Configurable> cfgPIDnSigmas{"cfgPIDnSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; Configurable> cfgPIDResoCuts{"cfgPIDResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; Configurable> cfgPIDResoSwitches{"cfgPIDResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; @@ -216,17 +220,22 @@ struct PidDiHadron { // make the filters and cuts. Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter mcCollisionFilter = (nabs(aod::mccollision::posZ) < cfgEvSel.cfgGlobalCutVertex); Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz); + Filter mcTrackFilter = (nabs(aod::mcparticle::eta) < cfgTrkSel.cfgGlobalEta) && (aod::mcparticle::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::mcparticle::pt < cfgTrkSel.cfgGlobalPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; - using FilteredMcTracks = soa::Filtered>; + using FilteredTracksMcLabels = soa::Filtered>; using V0TrackCandidate = aod::V0Datas; + using FilteredMcCollisions = soa::Filtered; + using FilteredMcTracks = soa::Filtered; + using SmallGroupMcCollisions = soa::SmallGroups>; Partition asscTracks = (aod::track::pt > cfgGeneral.cfgAsscPtMin) && (aod::track::pt < cfgGeneral.cfgAsscPtMax); + Partition asscTracksMC = (aod::mcparticle::pt > cfgGeneral.cfgAsscPtMin) && (aod::mcparticle::pt < cfgGeneral.cfgAsscPtMax); - Preslice perCollision = aod::track::collisionId; - Preslice perCollisionV0 = aod::v0::collisionId; + PresliceUnsorted collisionPerMCCollision = aod::mccollisionlabel::mcCollisionId; // Corrections TH3D* mEfficiency = nullptr; @@ -427,7 +436,7 @@ struct PidDiHadron { } // Event Counter - if ((doprocessSame || doprocessSameReso || doprocessMC) && cfgEvSel.cfgUseAdditionalEventCut) { + if ((doprocessSame || doprocessSameReso || doprocessMCPID) && cfgEvSel.cfgUseAdditionalEventCut) { histos.add("hEventCount", "Number of Events;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); @@ -454,6 +463,7 @@ struct PidDiHadron { histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetYaxis()->SetTitle("Counts"); histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); @@ -476,6 +486,7 @@ struct PidDiHadron { histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetYaxis()->SetTitle("Counts"); histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); @@ -591,16 +602,31 @@ struct PidDiHadron { histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); } + if (doprocessSameMC) { + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); + } + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + if (doprocessSameReso) { if (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda) { histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); } } - if (doprocessMixed || doprocessMixedReso) { + if (doprocessMixed || doprocessMixedReso || doprocessMixedMC) { histos.add("deltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); } - if (doprocessMC) { + + if (doprocessMCPID) { histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are pions and selected as pions histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are kaons and selected as kaons histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are protons and selected as protons @@ -775,6 +801,10 @@ struct PidDiHadron { template int getNsigmaPID(const TTrack& track) { + if (cfgPIDConfigs.cfgPIDUseNclsPID && track.tpcNClsPID() < cfgPIDConfigs.cfgPIDNclsPIDCut) { + return -1; + } + // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; @@ -1292,6 +1322,91 @@ struct PidDiHadron { } } + template + void fillCorrelationsMC(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms for MC truth + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + + // loop over all tracks + for (auto const& trig : triggers) { + + if (cfgTrkSel.cfgMCTrigIsPhysPrim && !trig.isPhysicalPrimary()) { + continue; + } + + auto getPIDcode = [](int x) { + int pid = -1; + switch (x) { + case PDG_t::kPiPlus: + pid = UsePions; + break; + case PDG_t::kKPlus: + pid = UseKaons; + break; + case PDG_t::kProton: + pid = UseProtons; + break; + default: + return pid; + } // end of switch + return pid; + }; + + int pidIndex = getPIDcode(trig.pdgCode()); + + if (system == SameEvent) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), trig.eta(), UseCharged); + histos.fill(HIST("pTFiner"), trig.pt(), UseCharged, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), pidIndex); + histos.fill(HIST("Eta"), trig.eta(), pidIndex); + histos.fill(HIST("pTFiner"), trig.pt(), pidIndex, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } + } + + for (auto const& assc : assocs) { + + if (cfgTrkSel.cfgMCAsscIsPhysPrim && !assc.isPhysicalPrimary()) { + continue; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } + + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + template bool selectionEvent(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { @@ -1929,7 +2044,101 @@ struct PidDiHadron { } PROCESS_SWITCH(PidDiHadron, processMixedReso, "Process mixed events", true); - void processMC(FilteredCollisions::iterator const& collision, FilteredMcTracks const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) + void processSameMC(FilteredMcCollisions::iterator const& mcCollision, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + float cent = -1.f; + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + histos.fill(HIST("Centrality"), cent); + } + auto multiplicity = static_cast(mcTracks.size()); + histos.fill(HIST("Nch"), multiplicity); + histos.fill(HIST("zVtx"), mcCollision.posZ()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { + return; + } + histos.fill(HIST("eventcount"), SameEvent); + + float eventWeight = 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + + fillCorrelationsMC(mcTracks, mcAssocs, mcCollision.posZ(), SameEvent, eventClass, eventWeight); + + } // end of processSameMC + PROCESS_SWITCH(PidDiHadron, processSameMC, "Process Monte Carlo for same event correlations", true); + + void processMixedMC(FilteredMcCollisions const& mcCollisions, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcTracks, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcTracks.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcTracks, mcTracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, mcTracks1, mcCollision2, mcTracks2] = *it; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, mcCollision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, mcCollision2.globalIndex()); + + float cent1 = -1.f; + float cent2 = -1.f; + + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); + } + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + auto multiplicity1 = static_cast(mcTracks1.size()); + auto multiplicity2 = static_cast(mcTracks2.size()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); + + float eventWeight = (cfgEffWeights.cfgUseEventWeights) ? (1.0f / it.currentWindowNeighbours()) : 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision2.globalIndex(), cache); + + fillCorrelationsMC(mcTracks1, mcAssocs, mcCollision1.posZ(), MixedEvent, eventClass, eventWeight); + } + } + PROCESS_SWITCH(PidDiHadron, processMixedMC, "Process Monte Carlo for mixed event correlations", true); + + void processMCPID(FilteredCollisions::iterator const& collision, FilteredTracksMcLabels const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) { float cent = -1.; if (!cfgEvSel.cfgCentTableUnavailable) { @@ -2024,8 +2233,8 @@ struct PidDiHadron { } } // Proton condition } // end of tracks MC loop - } // end of process MC - PROCESS_SWITCH(PidDiHadron, processMC, "Process Monte Carlo", true); + } // end of process MCPID + PROCESS_SWITCH(PidDiHadron, processMCPID, "Process Monte Carlo for PID", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From bbeda2c7cf7727d2898edabab8aa41e8048eb917 Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:42:32 +0530 Subject: [PATCH 496/648] [PWGLF] Added event histogram in MC (#18503) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 56 ++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index cb72f52ecb5..8d5a393b8e0 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -225,6 +225,8 @@ struct Kstarqa { rEventSelection.add("hEventCut", "No. of event after cuts", kTH1D, {{20, 0, 20}}); std::shared_ptr hEventSelection = rEventSelection.get(HIST("hEventCut")); + rEventSelection.add("hEventCutMC", "No. of event after cuts in MC", kTH1D, {{10, 0, 10}}); + std::shared_ptr hEventSelectionMC = rEventSelection.get(HIST("hEventCutMC")); rEventSelection.add("hTrackCut", "No. of tracks after cuts", kTH1D, {{20, 0, 20}}); std::shared_ptr hTrackSelection = rEventSelection.get(HIST("hTrackCut")); @@ -242,11 +244,24 @@ struct Kstarqa { std::string("IsGoodITSLayersAll") + check(configGp.isGoodITSLayersAll.value), std::string("isVertexITSTPC") + check(configGp.isVertexITSTPC.value), }; + + std::vector eveCutLabelsMC = { + "All Events", + "RecCollision", + "TimeFrameBorder", + "ITSROFrameBorder", + "Vz", + "TriggerTVX"}; + // assign labels for (size_t i = 0; i < eveCutLabels.size(); ++i) { hEventSelection->GetXaxis()->SetBinLabel(i + 1, eveCutLabels[i].c_str()); } + for (size_t i = 0; i < eveCutLabelsMC.size(); ++i) { + hEventSelectionMC->GetXaxis()->SetBinLabel(i + 1, eveCutLabelsMC[i].c_str()); + } + std::vector trackCutLabels = { "All Tracks", std::string("GlobalTracks") + check(configGp.isGlobalTracks.value), @@ -350,6 +365,7 @@ struct Kstarqa { if (doprocessEvtLossSigLossMC || doprocessEvtLossSigLossMCPhi) { hInvMass.add("MCcorrections/hSignalLossDenominator", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossDenominatorRec", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hSignalLossNumerator", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hSignalLossNumeratorINEL", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hImpactParameterRec", "Impact parameter in reconstructed MC", kTH1F, {impactParAxis}); @@ -1914,6 +1930,7 @@ struct Kstarqa { { // auto multiplicityRec = -1; bool isSelectedEvent = false; + bool isRecCollision = false; bool isSelectedEventINEL = false; auto multiplicity1 = -999.; @@ -1927,6 +1944,8 @@ struct Kstarqa { for (const auto& RecCollision : recCollisions) { if (!RecCollision.has_mcCollision()) continue; + isRecCollision = true; + if (!selectionEvent(RecCollision, false)) // don't fill event cut histogram continue; @@ -1962,8 +1981,8 @@ struct Kstarqa { if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) continue; - if (configGp.isSel8 && !RecCollision.sel8()) - continue; + // if (configGp.isSel8 && !RecCollision.sel8()) + // continue; if (rctCut.requireRCTFlagChecker && !rctChecker(RecCollision)) continue; @@ -1971,6 +1990,35 @@ struct Kstarqa { isSelectedEventINEL = true; } + for (const auto& RecCollision : recCollisions) { + rEventSelection.fill(HIST("hEventSelectionMC"), 0.5); + + if (!RecCollision.has_mcCollision()) + continue; + + rEventSelection.fill(HIST("hEventSelectionMC"), 1.5); + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventSelectionMC"), 2.5); + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventSelectionMC"), 3.5); + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + rEventSelection.fill(HIST("hEventSelectionMC"), 4.5); + + if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + continue; + + rEventSelection.fill(HIST("hEventSelectionMC"), 5.5); + } + bool isINELgt0true = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { @@ -2003,6 +2051,10 @@ struct Kstarqa { continue; hInvMass.fill(HIST("MCcorrections/hSignalLossDenominator"), mcPart.pt(), multiplicityGen); + if (isRecCollision) { + hInvMass.fill(HIST("MCcorrections/hSignalLossDenominatorRec"), mcPart.pt(), multiplicityGen); + } + if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGen); } From ff6d5775a5ace832e4f2f3dfc2bd6460dd07b720 Mon Sep 17 00:00:00 2001 From: davidchese <75501409+davidchese@users.noreply.github.com> Date: Fri, 30 Oct 2026 05:17:22 -0500 Subject: [PATCH 497/648] [PWGUD] Add upcPileUp task for pile-up probability in FT0 (#18513) Co-authored-by: David Arevalos --- PWGUD/Tasks/CMakeLists.txt | 5 + PWGUD/Tasks/upcPileUp.cxx | 533 +++++++++++++++++++++++++++++++++++++ 2 files changed, 538 insertions(+) create mode 100644 PWGUD/Tasks/upcPileUp.cxx diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index 9e24487513f..a2566a51d87 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -361,3 +361,8 @@ o2physics_add_dpl_workflow(sg-deuteron-spectra REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-pile-up + SOURCES upcPileUp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/PWGUD/Tasks/upcPileUp.cxx b/PWGUD/Tasks/upcPileUp.cxx new file mode 100644 index 00000000000..2fca0ac06a8 --- /dev/null +++ b/PWGUD/Tasks/upcPileUp.cxx @@ -0,0 +1,533 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file upcPileUp.cxx +/// \brief Pile-up probability in FT0 for UPC analyses, per colliding bunch and time frame +/// +/// For each colliding bunch crossing it counts +/// - denominator A: crossings with no activity on the C side (FT0C, FDDC, ZNC) +/// - numerator A : of those, the ones with a signal in FT0A above threshold +/// and the mirror for the C side (A side empty: FT0A, FV0A, FDDA, ZNA). +/// The ratio numerator / denominator is the pile-up probability. +/// +/// The BCs table only stores crossings with some activity. Completely empty +/// crossings are not in it, but they belong in the denominator. The task +/// therefore computes the number of analysed crossings of every colliding +/// bunch in every time frame, applying the same time-frame and ITS ROF border +/// cuts as the event selection, and the denominator is that number minus the +/// stored crossings with activity on the other side. +/// +/// \author David Arevalos + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BCsTSsSels = soa::Join; + +struct UpcPileUp { + + // services + Service ccdb{}; + + // histograms, booked per run and kept in the maps by name + HistogramRegistry bcTH1Registry{"bcTH1Registry", {}}; + std::map> bcTH1Pointers; + HistogramRegistry bcTH2Registry{"bcTH2Registry", {}}; + std::map> bcTH2Pointers; + + // histograms of the current run, cached so that the loops do no name lookup + std::shared_ptr hBcSel, hTF, hBorderCheck; + std::shared_ptr hCross, hVtx, hVtxCe, hDenA, hDenC; + std::vector> hNumA, hNumC, hNumAZn, hNumCZn; + + // filling scheme + std::bitset bcPatternB; + std::vector bcbIdx; // index of each colliding bunch, -1 otherwise + std::vector bcbList; // bcID within the orbit of each colliding bunch + int nbcB = 0; + + // ITS ROF info + int rofLength = -1; // number of bcs per ROF + int rofShift = -1; // bc shift of the ITS ROF + + // border margins, as in the event selection + int tfStartMargin = 0; + int tfEndMargin = 0; + int rofStartMargin = 0; + int rofEndMargin = 0; + + // run info + int runNumberBc = 0; // run number used to process BCs + int64_t tsMid = 0; // middle of the run, to access run-wise CCDB entries + int64_t orbitsPerTF = 0; + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t nTF = 0; // number of time frames in run + std::set seenTFs; // time frames already counted in this run + + // thresholds used, filled in init from the configurable + std::vector thresholds; + int nThresholds = 0; + + // analysed crossings of each colliding bunch in the current time frame + std::vector nCrossTF; + + // histogram binning constants + static constexpr int NBinsBcSel = 4; + static constexpr int NBinsBorderCheck = 2; + + // FDD channel pairing, as in upcVmRof + static constexpr int NFddPairs = 4; + static constexpr int FddPairOffset = 4; + + // configurables + Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin, 1e4 means some 28 s"}; + Configurable ft0VtxIdx{"ft0VtxIdx", 2, "index of the FT0VTX input in the CTP input mask"}; + Configurable ft0CeIdx{"ft0CeIdx", 4, "index of the FT0CE input in the CTP input mask"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + + // signal and veto definition + Configurable> ampThresholds{"ampThresholds", {100.0f, 150.0f, 200.0f}, "amplitude thresholds defining a signal in FT0"}; + Configurable maxAbsTimeFT0{"maxAbsTimeFT0", 4.0, "max |time| in a FT0 side (ns)"}; + Configurable maxAbsTimeFV0{"maxAbsTimeFV0", 4.0, "max |time| in FV0A (ns)"}; + Configurable maxAbsTimeFDD{"maxAbsTimeFDD", 4.0, "max |time| in a FDD side (ns)"}; + Configurable maxAbsTimeZN{"maxAbsTimeZN", 2.0, "max |time| in a ZN (ns)"}; + Configurable fillZnVariation{"fillZnVariation", true, "also fill the numerators asking in addition for a signal in ZNA or ZNC"}; + + // border margins, must match the ones used by the event selection; -1 takes them from CCDB + Configurable confTFStartBorderMargin{"confTFStartBorderMargin", -1, "bcs cut at the start of the TF, -1 from CCDB"}; + Configurable confTFEndBorderMargin{"confTFEndBorderMargin", -1, "bcs cut at the end of the TF, -1 from CCDB"}; + Configurable confROFStartBorderMargin{"confROFStartBorderMargin", -1, "bcs cut at the start of the ITS ROF, -1 from CCDB"}; + Configurable confROFEndBorderMargin{"confROFEndBorderMargin", -1, "bcs cut at the end of the ITS ROF, -1 from CCDB"}; + + //-------------------------------------------------------------------------------- + // general run information + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + tsMid = runInfo.sor / 2 + runInfo.eor / 2; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = runInfo.orbitSOR * o2::constants::lhc::LHCMaxBunches; + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; + nTF = (runInfo.orbitEOR - runInfo.orbitSOR + orbitsPerTF - 1) / orbitsPerTF; // rounded up + } // end getRunInfo() + + //-------------------------------------------------------------------------------- + // filling scheme + void getFillingScheme() + { + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", tsMid); + auto beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + auto beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = beamPatternA & beamPatternC; + bcbIdx.assign(o2::constants::lhc::LHCMaxBunches, -1); + bcbList.clear(); + nbcB = 0; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bcPatternB.test(i)) { + bcbIdx[i] = nbcB; + bcbList.push_back(i); + nbcB++; + } + } + nCrossTF.assign(nbcB, 0.); + } // end getFillingScheme() + + //-------------------------------------------------------------------------------- + // ITS ROF info and border margins, taken as in the event selection + void getBorderInfo() + { + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", tsMid); + rofShift = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + + auto par = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", tsMid); + tfStartMargin = confTFStartBorderMargin < 0 ? par->fTimeFrameStartBorderMargin : confTFStartBorderMargin.value; + tfEndMargin = confTFEndBorderMargin < 0 ? par->fTimeFrameEndBorderMargin : confTFEndBorderMargin.value; + rofStartMargin = confROFStartBorderMargin < 0 ? par->fITSROFrameStartBorderMargin : confROFStartBorderMargin.value; + rofEndMargin = confROFEndBorderMargin < 0 ? par->fITSROFrameEndBorderMargin : confROFEndBorderMargin.value; + LOGP(info, "border margins: TF {} {}, ITS ROF {} {}", tfStartMargin, tfEndMargin, rofStartMargin, rofEndMargin); + } // end getBorderInfo() + + //-------------------------------------------------------------------------------- + int64_t getBcWithinOrbit(int64_t globalBC) + { + return (globalBC % o2::constants::lhc::LHCMaxBunches); + } + + //-------------------------------------------------------------------------------- + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // same conditions as kNoTimeFrameBorder and kNoITSROFrameBorder in the event selection + bool passesBorders(int64_t globalBC) + { + int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + if (!(bcInTF > tfStartMargin && bcInTF < nBCsPerTF - tfEndMargin)) { + return false; + } + int64_t bcInRof = (globalBC + o2::constants::lhc::LHCMaxBunches - rofShift) % rofLength; + return (bcInRof > rofStartMargin && bcInRof < rofLength - rofEndMargin); + } + + //-------------------------------------------------------------------------------- + // selection flags of a stored bc + bool checkBcFlags(const auto& bc) + { + hBcSel->Fill(0); + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + hBcSel->Fill(1); + if (!bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + hBcSel->Fill(2); + return true; + } // end checkBcFlags() + + //-------------------------------------------------------------------------------- + // analysed crossings of every colliding bunch in a time frame: all of them, + // stored or not, that pass the same border cuts as the stored ones. + // They go into the crossings histogram and, as a start, into both + // denominators; stored crossings with activity on the other side are removed + // later, one by one. + void fillCrossings(int64_t tf) + { + const int64_t tfStartBC = bcSOR + tf * nBCsPerTF; + std::fill(nCrossTF.begin(), nCrossTF.end(), 0.); + for (int64_t orbit = 0; orbit < orbitsPerTF; orbit++) { + for (const auto& bcInOrbit : bcbList) { + int64_t globalBC = tfStartBC + orbit * o2::constants::lhc::LHCMaxBunches + bcInOrbit; + if (passesBorders(globalBC)) { + nCrossTF[bcbIdx[bcInOrbit]] += 1.; + } + } + } + for (int ib = 0; ib < nbcB; ib++) { + if (nCrossTF[ib] > 0.) { + hCross->Fill(tf, ib, nCrossTF[ib]); + hDenA->Fill(tf, ib, nCrossTF[ib]); + hDenC->Fill(tf, ib, nCrossTF[ib]); + } + } + } // end fillCrossings() + + //-------------------------------------------------------------------------------- + // FDD amplitude of a side, using the channel pairing of upcVmRof + // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}} + float sumFddAmplitude(const auto& charges) + { + float amp = 0; + for (int i = 0; i < NFddPairs; i++) { + if (charges[i] > 0 && charges[i + FddPairOffset] > 0) { + amp += 0.5 * (charges[i] + charges[i + FddPairOffset]); + } + } + return amp; + } // end sumFddAmplitude() + + //-------------------------------------------------------------------------------- + // book the histograms of a run + void addBcHistos(int run) + { + if (bcTH1Pointers[Form("bc/%d/bcSel_H", run)]) { + return; + } + + const int nBunches = o2::constants::lhc::LHCMaxBunches; + const double maxBunch = static_cast(nBunches) - 0.5; + const int nBinsTF = static_cast(nTF / tfPerBin) + 1; + const double maxTF = static_cast(nBinsTF * tfPerBin - 1) - 0.5; + const AxisSpec axisTF{nBinsTF, -0.5, maxTF, "TF"}; + const AxisSpec axisBcb{nbcB, -0.5, nbcB - 0.5, "bc-B idx"}; + + // filling scheme and bookkeeping + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternB_H", run), "colliding bunches; bcID;", + {HistType::kTH1D, {{nBunches, -0.5, maxBunch}}}); + bcTH1Pointers[Form("bc/%d/bcSel_H", run)] = bcTH1Registry.add(Form("bc/%d/bcSel_H", run), "bc selection: all, no TF border, no ROF border, colliding; ; Counter", + {HistType::kTH1D, {{NBinsBcSel, -0.5, NBinsBcSel - 0.5}}}); + bcTH1Pointers[Form("bc/%d/tf_H", run)] = bcTH1Registry.add(Form("bc/%d/tf_H", run), "analysed time frames; TF; Counts", + {HistType::kTH1D, {axisTF}}); + // stored bcs where the border cut computed here agrees / disagrees with the event selection + bcTH1Pointers[Form("bc/%d/borderCheck_H", run)] = bcTH1Registry.add(Form("bc/%d/borderCheck_H", run), "border cut vs event selection: agree, disagree; ; Counter", + {HistType::kTH1D, {{NBinsBorderCheck, -0.5, NBinsBorderCheck - 0.5}}}); + + // thresholds, stored as bin labels so that they survive merging + bcTH1Pointers[Form("bc/%d/thresholds_H", run)] = bcTH1Registry.add(Form("bc/%d/thresholds_H", run), "amplitude thresholds; threshold index; ", + {HistType::kTH1D, {{nThresholds, -0.5, nThresholds - 0.5}}}); + for (int i = 0; i < nThresholds; i++) { + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->GetXaxis()->SetBinLabel(i + 1, Form("%g", thresholds[i])); + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->SetBinContent(i + 1, 1); + } + for (const auto& bcInOrbit : bcbList) { + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)]->Fill(bcInOrbit); + } + + // analysed crossings, stored or not + bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/nCross_bcb_H", run), "analysed crossings; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // triggers from the CTP input mask, to compute mu + bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0Vtx_bcb_H", run), "ft0Vtx triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0VtxCe_bcb_H", run), "ft0VtxCe triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // pile-up probability. The denominators are filled with weights, so their + // bin errors are not meaningful: compute the uncertainty of the ratio + // binomially from the contents. + bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenA_bcb_H", run), "crossings empty on the C side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenC_bcb_H", run), "crossings empty on the A side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + for (int i = 0; i < nThresholds; i++) { + bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumA_t%d_bcb_H", run, i), Form("FT0A amplitude > %g, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumC_t%d_bcb_H", run, i), Form("FT0C amplitude > %g, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + // variation asking in addition for a signal in ZNA or ZNC. Since the ZN of + // the other side is part of the empty condition, this is in practice the + // ZN of the same side. + if (fillZnVariation) { + bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumAZn_t%d_bcb_H", run, i), Form("FT0A amplitude > %g and ZN, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumCZn_t%d_bcb_H", run, i), Form("FT0C amplitude > %g and ZN, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + } + } + } // end addBcHistos() + + //-------------------------------------------------------------------------------- + // cache the histograms of a run + void setHistoPointers(int run) + { + hBcSel = bcTH1Pointers[Form("bc/%d/bcSel_H", run)]; + hTF = bcTH1Pointers[Form("bc/%d/tf_H", run)]; + hBorderCheck = bcTH1Pointers[Form("bc/%d/borderCheck_H", run)]; + hCross = bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)]; + hVtx = bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)]; + hVtxCe = bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)]; + hDenA = bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)]; + hDenC = bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)]; + hNumA.assign(nThresholds, nullptr); + hNumC.assign(nThresholds, nullptr); + hNumAZn.assign(nThresholds, nullptr); + hNumCZn.assign(nThresholds, nullptr); + for (int i = 0; i < nThresholds; i++) { + hNumA[i] = bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)]; + hNumC[i] = bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)]; + if (fillZnVariation) { + hNumAZn[i] = bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)]; + hNumCZn[i] = bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)]; + } + } + } // end setHistoPointers() + + //-------------------------------------------------------------------------------- + void init(InitContext&) + { + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + thresholds = static_cast>(ampThresholds); + nThresholds = static_cast(thresholds.size()); + } // end init() + + //-------------------------------------------------------------------------------- + void processBCs(BCsTSsSels const& bcs, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, + aod::Zdcs const&) + { + if (bcs.size() == 0) { + return; + } + + // new run + const auto& bcAt0 = bcs.iteratorAt(0); + if (runNumberBc != bcAt0.runNumber()) { + runNumberBc = bcAt0.runNumber(); + getRunInfo(runNumberBc); + getFillingScheme(); + getBorderInfo(); + addBcHistos(runNumberBc); + setHistoPointers(runNumberBc); + seenTFs.clear(); + } + + for (const auto& bc : bcs) { + int64_t thisBC = getBcWithinOrbit(bc.globalBC()); + int64_t thisTF = getTimeFrame(bc.globalBC()); + + // first time this time frame is seen: count it and add all its analysed + // crossings. The set protects against a time frame split over two + // dataframes being counted twice. + if (seenTFs.insert(thisTF).second) { + hTF->Fill(thisTF); + fillCrossings(thisTF); + } + + // check that the border cut computed here agrees with the event selection + bool passesFlags = bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + hBorderCheck->Fill(passesFlags == passesBorders(bc.globalBC()) ? 0 : 1); + + if (!checkBcFlags(bc)) { + continue; + } + if (!bcPatternB.test(thisBC)) { + continue; + } + hBcSel->Fill(3); + int thisBcb = bcbIdx[thisBC]; + + // triggers from the CTP input mask + std::bitset<64> mask = bc.inputMask(); + if (mask[ft0VtxIdx]) { + hVtx->Fill(thisTF, thisBcb); + if (mask[ft0CeIdx]) { + hVtxCe->Fill(thisTF, thisBcb); + } + } + + //------------------------------------------------------------------------------ + // detector information; amplitudes are only meaningful when in time + + // FT0 + bool ft0aInTime = false; + bool ft0cInTime = false; + float ampFT0A = 0; + float ampFT0C = 0; + if (bc.has_foundFT0()) { + if (bc.foundFT0().isValidTimeA() && std::abs(bc.foundFT0().timeA()) < maxAbsTimeFT0) { + ft0aInTime = true; + ampFT0A = bc.foundFT0().sumAmpA(); + } + if (bc.foundFT0().isValidTimeC() && std::abs(bc.foundFT0().timeC()) < maxAbsTimeFT0) { + ft0cInTime = true; + ampFT0C = bc.foundFT0().sumAmpC(); + } + } + + // FV0A + bool fv0aSignal = false; + if (bc.has_foundFV0() && std::abs(bc.foundFV0().time()) < maxAbsTimeFV0) { + auto amps = bc.foundFV0().amplitude(); + fv0aSignal = (std::accumulate(amps.begin(), amps.end(), 0.f) > 0); + } + + // FDD + bool fddaSignal = false; + bool fddcSignal = false; + if (bc.has_foundFDD()) { + if (std::abs(bc.foundFDD().timeA()) < maxAbsTimeFDD) { + fddaSignal = (sumFddAmplitude(bc.foundFDD().chargeA()) > 0); + } + if (std::abs(bc.foundFDD().timeC()) < maxAbsTimeFDD) { + fddcSignal = (sumFddAmplitude(bc.foundFDD().chargeC()) > 0); + } + } + + // ZN: an entry in time, no requirement on the energy + bool znaSignal = false; + bool zncSignal = false; + if (bc.has_zdc()) { + float tZNA = bc.zdc().timeZNA(); + float tZNC = bc.zdc().timeZNC(); + znaSignal = std::isfinite(tZNA) && std::abs(tZNA) < maxAbsTimeZN; + zncSignal = std::isfinite(tZNC) && std::abs(tZNC) < maxAbsTimeZN; + } + bool znSignal = znaSignal || zncSignal; + + // a side is empty if no detector has a signal in time, at any amplitude above zero + bool ft0aSignal = ft0aInTime && (ampFT0A > 0); + bool ft0cSignal = ft0cInTime && (ampFT0C > 0); + bool noSignalC = !ft0cSignal && !fddcSignal && !zncSignal; + bool noSignalA = !ft0aSignal && !fv0aSignal && !fddaSignal && !znaSignal; + + //------------------------------------------------------------------------------ + // pile-up counters. The denominators already hold all analysed crossings + // of this bunch; a stored crossing with activity on the other side is + // removed from them. + if (noSignalC) { + for (int i = 0; i < nThresholds; i++) { + if (ft0aInTime && ampFT0A > thresholds[i]) { + hNumA[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumAZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenA->Fill(thisTF, thisBcb, -1.); + } + if (noSignalA) { + for (int i = 0; i < nThresholds; i++) { + if (ft0cInTime && ampFT0C > thresholds[i]) { + hNumC[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumCZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenC->Fill(thisTF, thisBcb, -1.); + } + } // loop over bcs + } // end processBCs() + PROCESS_SWITCH(UpcPileUp, processBCs, "pile-up probability in FT0", true); + +}; // end of struct UpcPileUp + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From 66d5a26945038de43681843d0a98ba4b9b6f11b0 Mon Sep 17 00:00:00 2001 From: Tanu Gahlaut <154991749+TGahlaut1@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:55:35 +0530 Subject: [PATCH 498/648] [PWGCF] Adjust nSigma cuts and histogram entries for particles (#18476) --- PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx | 132 ++++++++++-------- 1 file changed, 73 insertions(+), 59 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx index 7f4432ae05e..3605e90661d 100644 --- a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx @@ -72,17 +72,21 @@ struct MeanPtFlucId { Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; - Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 2.0, "nSigma TPC cut for pion selection"}; - Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 2.0, "nSigma TPC cut for kaon selection"}; - Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 2.0, "nSigma TPC cut for proton selection"}; - Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 2.0, "nSigma TOF cut for pion selection"}; - Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 2.0, "nSigma TOF cut for kaon selection"}; - Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 2.0, "nSigma TOF cut for proton selection"}; + Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 3.0, "nSigma TPC cut for pion selection"}; + Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 3.0, "nSigma TPC cut for kaon selection"}; + Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 3.0, "nSigma TPC cut for proton selection"}; + Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 3.0, "nSigma TOF cut for pion selection"}; + Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 3.0, "nSigma TOF cut for kaon selection"}; + Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 3.0, "nSigma TOF cut for proton selection"}; + // Signed TOF nSigma offsets: corrected = raw - shift. Zero preserves the original PID. + Configurable cfgTofShiftPi{"cfgTofShiftPi", 0.f, "Pion TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftKa{"cfgTofShiftKa", 0.f, "Kaon TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftPr{"cfgTofShiftPr", 0.f, "Proton TOF nSigma offset to subtract (not a time shift in ps)"}; Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; - Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.8, "Momentum threshold for requiring kaon TOF PID"}; + Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.55, "Momentum threshold for requiring kaon TOF PID"}; Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; @@ -94,6 +98,7 @@ struct MeanPtFlucId { Configurable cfgP1corr{"cfgP1corr", 0.0001, "p_{1} for corrected N_{TPC} "}; Configurable cfgP2corr{"cfgP2corr", 0.0001, "p_{2} for corrected N_{TPC} "}; Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; + Configurable cfgCentFT0C{"cfgCentFT0C", false, "Use FT0C for centrality instead of FT0M"}; Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; @@ -240,6 +245,10 @@ struct MeanPtFlucId { void init(InitContext const&) { + if (!std::isfinite(cfgTofShiftPi.value) || !std::isfinite(cfgTofShiftKa.value) || !std::isfinite(cfgTofShiftPr.value)) { + LOGF(fatal, "TOF nSigma shifts must be finite: pion=%g, kaon=%g, proton=%g", + cfgTofShiftPi.value, cfgTofShiftKa.value, cfgTofShiftPr.value); + } if (nPtBins <= 0 || cfgCutPtMin >= cfgCutPtMax || cfgCutPiPtMin >= cfgCutPtMax || cfgCutKaPtMin >= cfgCutPtMax || cfgCutPrPtMin >= cfgCutPtMax) { @@ -418,7 +427,7 @@ struct MeanPtFlucId { hist.add("QA/Charged/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtCh}}); hist.add("QA/Charged/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); - hist.add("QA/Charged/h3_Pt_EtaMC_primary_centFT0M", "Selected primary MC particles;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtCh}}); }; addTrackQAHistograms(); @@ -429,7 +438,7 @@ struct MeanPtFlucId { hist.add("QA/Pion/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); - hist.add("QA/Pion/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary pions;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPi}); hist.add("QA/Pion/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPi}); @@ -446,7 +455,7 @@ struct MeanPtFlucId { hist.add("QA/Kaon/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); - hist.add("QA/Kaon/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary kaons;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtKa}); hist.add("QA/Kaon/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtKa}); @@ -463,7 +472,7 @@ struct MeanPtFlucId { hist.add("QA/Proton/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); - hist.add("QA/Proton/h3_Pt_EtaMC_primary_centFT0M", "Correctly selected primary protons;#eta_{MC};p_{T,MC};FT0M (%)", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPr}); hist.add("QA/Proton/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPr}); @@ -591,8 +600,11 @@ struct MeanPtFlucId { template bool selRun3Col(T const& col) { - - centFT0M = col.centFT0M(); + if (cfgCentFT0C) { + centFT0M = col.centFT0C(); + } else { + centFT0M = col.centFT0M(); + } nTPC = col.multNTracksHasTPC(); nFT0M = col.multFT0M(); hist.fill(HIST("QA/after/h_counts_evSelCuts"), kBeforeSelCol); @@ -689,12 +701,25 @@ struct MeanPtFlucId { std::fabs(track.tpcNSigmaPr()) > cfgCutNSig3); } + // Share the corrected hypotheses between PID selection and selected-particle QA. + // The before-PID histograms intentionally retain raw values for calibration. + template + std::array getTofNSigmaForPid(T const& track) const + { + std::array values = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + if (track.hasTOF()) { + values[static_cast(PIDType::kPions)] -= cfgTofShiftPi.value; + values[static_cast(PIDType::kKaons)] -= cfgTofShiftKa.value; + values[static_cast(PIDType::kProtons)] -= cfgTofShiftPr.value; + } + return values; + } + // PID cuts for identified particles template - bool identifyParticle(T const& track, PIDType species, PIDType reject1, PIDType reject2, float p, float momThreshold) + bool identifyParticle(T const& track, PIDType species, PIDType reject1, PIDType reject2, float p, float momThreshold, const std::array& tofNSigma) { const std::array tpcNSigma = {-999.f, track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - const std::array tofNSigma = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; const int selSpecies = static_cast(species); const int rejSpecies1 = static_cast(reject1); @@ -1092,9 +1117,10 @@ struct MeanPtFlucId { float nSigmaTPCPi = track.tpcNSigmaPi(); float nSigmaTPCKa = track.tpcNSigmaKa(); float nSigmaTPCPr = track.tpcNSigmaPr(); - float nSigmaTOFPi = track.tofNSigmaPi(); - float nSigmaTOFKa = track.tofNSigmaKa(); - float nSigmaTOFPr = track.tofNSigmaPr(); + const auto tofNSigma = getTofNSigmaForPid(track); + const float nSigmaTOFPi = tofNSigma[static_cast(PIDType::kPions)]; + const float nSigmaTOFKa = tofNSigma[static_cast(PIDType::kKaons)]; + const float nSigmaTOFPr = tofNSigma[static_cast(PIDType::kProtons)]; float rapPi = track.rapidity(MassPiPlus); float rapKa = track.rapidity(MassKPlus); float rapPr = track.rapidity(MassProton); @@ -1129,16 +1155,13 @@ struct MeanPtFlucId { hist.fill(HIST("QA/after/h_DCAxy_primary"), track.dcaXY()); hist.fill(HIST("QA/after/h_DCAz_primary"), track.dcaZ()); hist.fill(HIST("QA/Charged/h_PtTruth_primary"), track.pt()); - const bool mcInChargedAcceptance = ptMC >= cfgCutPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInChargedAcceptance) { - hist.fill(HIST("QA/Charged/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kNone, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched charged efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - chargedTracksTruthMatchedCorrected.push_back({ptMC, weight}); + hist.fill(HIST("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kNone, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched charged efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + chargedTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/after/h_DCAxy_secondary"), track.dcaXY()); @@ -1169,9 +1192,9 @@ struct MeanPtFlucId { if (cfgRejTrk && rejectTracks(track)) { continue; } - auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, cfgCutPiThrsldP); - auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, cfgCutKaThrsldP); - auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, cfgCutPrThrsldP); + auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, cfgCutPiThrsldP, tofNSigma); + auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, cfgCutKaThrsldP, tofNSigma); + auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, cfgCutPrThrsldP, tofNSigma); if (selIDPion && track.pt() >= cfgCutPiPtMin) { if (cfgApplyEfficiencyCorrection) { @@ -1213,17 +1236,14 @@ struct MeanPtFlucId { fillPtMCHist(false, pt, eta, rapPi, phi, pid, kPiPlus, kPiMinus); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Pion/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Pion/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInPionAcceptance = ptMC >= cfgCutPiPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInPionAcceptance) { - hist.fill(HIST("QA/Pion/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kPions, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched pion efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - pionTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched pion efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + pionTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Pion/h_PtTruth_secondary"), track.pt()); @@ -1236,17 +1256,14 @@ struct MeanPtFlucId { fillPtMCHist(false, pt, eta, rapKa, phi, pid, kKPlus, kKMinus); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Kaon/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Kaon/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInKaonAcceptance = ptMC >= cfgCutKaPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInKaonAcceptance) { - hist.fill(HIST("QA/Kaon/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kKaons, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched kaon efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - kaonTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched kaon efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + kaonTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Kaon/h_PtTruth_secondary"), track.pt()); @@ -1259,17 +1276,14 @@ struct MeanPtFlucId { fillPtMCHist(false, pt, eta, rapPr, phi, pid, kProton, kProtonBar); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/Proton/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Proton/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); - const bool mcInProtonAcceptance = ptMC >= cfgCutPrPtMin && ptMC < cfgCutPtMax && std::abs(etaMC) < cfgCutEta; - if (mcInProtonAcceptance) { - hist.fill(HIST("QA/Proton/h3_Pt_EtaMC_primary_centFT0M"), etaMC, ptMC, centFT0M); - if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { - double weight = 0.; - if (!getEfficiencyWeight(PIDType::kProtons, ptMC, etaMC, weight)) { - LOGF(fatal, "Undefined truth-matched proton efficiency weight at pT=%g, eta=%g, centrality=%g", ptMC, etaMC, centFT0M); - } - protonTracksTruthMatchedCorrected.push_back({ptMC, weight}); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched proton efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); } + protonTracksTruthMatchedCorrected.push_back({pt, weight}); } } else { hist.fill(HIST("QA/Proton/h_PtTruth_secondary"), track.pt()); @@ -1437,8 +1451,8 @@ struct MeanPtFlucId { aod::pidTOFFullPi, aod::pidTPCFullPi, aod::pidTOFFullPr, aod::pidTPCFullPr, aod::pidTOFFullKa, aod::pidTPCFullKa, aod::pidTOFFullEl, aod::pidTPCFullEl, aod::pidTOFbeta, aod::pidTOFmass>; - using MyRun3Collisions = soa::Join; - using MyRun3MCCollisions = soa::Join; + using MyRun3Collisions = soa::Join; + using MyRun3MCCollisions = soa::Join; using MyMCTracks = soa::Join Date: Fri, 30 Oct 2026 13:29:48 +0300 Subject: [PATCH 499/648] [PWGUD] code-checker fixes (#18517) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index c6c439a0c14..63611498087 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -40,8 +40,10 @@ #include #include -#include #include +#include + +#include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -291,14 +294,15 @@ struct UpcCandProducerBarrel { float calcWeightForOccupancy(float dt) // copied from Common/Tools/EventSelectionModule.h { float wOccup = 0; - if (dt > -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) + if (dt > -40 && dt < -5) { // o2-linter: disable=magic-number (collisions in the past) wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) - else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) + } else if (dt >= -5 && dt < 15) { // o2-linter: disable=magic-number (collisions near a given one) wOccup = 1; - else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) + } else if (dt >= 15 && dt < 40) { // o2-linter: disable=magic-number (collisions from the future) wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) - else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) + } else if (dt >= 40 && dt < 100) { // o2-linter: disable=magic-number (collisions from the distant future) wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + } return wOccup; } @@ -595,8 +599,8 @@ struct UpcCandProducerBarrel { LOGP(debug, "Filling FDD activity arrays"); for (const auto& fdd : fdds) { - const float sumAmpFDDA = std::accumulate(fdd.chargeA(), fdd.chargeA() + 8, 0.f); - const float sumAmpFDDC = std::accumulate(fdd.chargeC(), fdd.chargeC() + 8, 0.f); + const float sumAmpFDDA = std::accumulate(std::begin(fdd.chargeA()), std::end(fdd.chargeA()), 0.f); + const float sumAmpFDDC = std::accumulate(std::begin(fdd.chargeC()), std::end(fdd.chargeC()), 0.f); if ((fdd.timeA() < MaxFITTime && sumAmpFDDA >= minAmpFDDA) || (fdd.timeC() < MaxFITTime && sumAmpFDDC >= minAmpFDDC)) { bcsWithFDD.push_back(fdd.bc_as().globalBC()); } @@ -694,23 +698,26 @@ struct UpcCandProducerBarrel { fillVetoHistograms(hVetoT00RCTnoPileupROFupc, hVetoTV0RCTnoPileupROFupc, hVetoTVDRCTnoPileupROFupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); } - if (distanceFT0 == 0) + if (distanceFT0 == 0) { continue; + } float occupancy = vOccupancyFT0C[iTF][bcInTF]; hOccupancyT00RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); if (upcMode) { hOccupancyT00RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); } - if (distanceFV0 == 0) + if (distanceFV0 == 0) { continue; + } hOccupancyTV0RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); if (upcMode) { hOccupancyTV0RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); } - if (distanceFDD == 0) + if (distanceFDD == 0) { continue; + } hOccupancyTVDRCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); if (upcMode) { hOccupancyTVDRCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); From 7a0f25fdd42263874970842692a1a4ac069f317c Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Fri, 30 Oct 2026 13:11:06 +0200 Subject: [PATCH 500/648] [PWGCF] Extend Fox-Wolfram topology selections and associated-pT spectra (#18505) Co-authored-by: Iulian --- .../Tasks/FoxWolframCorrelation.cxx | 944 ++++++++---------- 1 file changed, 429 insertions(+), 515 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index 6634b187869..ba8b5e6b790 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -14,7 +14,7 @@ /// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. /// Classifies events by topology (triangular), fills correlation histograms, /// and produces custom SOA tables for mixed-event background estimation. -/// \since 25 Sept 2026 +/// \since 29 Oct 2026 #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -93,18 +93,23 @@ double computeDeltaPhi(double phi1, double phi2) } // Name the available pair-selection modes. -enum LeadAssocMode : int { +enum LeadAssocMode : int { // 3 distinct modes kAllPairs = 0, - kAllPairsEtaConstrain = 1, - kpTLeadConstrain = 2, - kpTLeadFree = 3 + kpTLeadConstrain = 1, + kpTLeadFree = 2 }; // Name the available event-topology selections. enum TopologyMode : int { - kAllEvents = 0, - kTriEvents = 1, - kIsoEvents = 2 + kAllEvSetNom = 0, + kTriEvSetNom = 1, + kIsoEvSetNom = 2, + kTriEvSet1p = 3, + kTriEvSet2p = 4, + kTriEvSet3p = 5, + kTriEvSet1m = 6, + kTriEvSet2m = 7, + kTriEvSet3m = 8 }; template @@ -112,26 +117,13 @@ template bool passesLeadAssoc(int mode, bool foundPart, Track const& track1, Track const& track2, TLead const& triggTrk, TLead const& triggTrkPart) { - if (mode == kAllPairs) { + if (mode == kAllPairs) { // all i-j pairs return (track1.pt() < 3.f) && (track2.pt() < 3.f); } - if (mode == kAllPairsEtaConstrain) { - - const bool firstPositiveSecondNegative = - track1.eta() > 0.1f && track1.eta() < 0.8f && - track2.eta() > -0.8f && track2.eta() < -0.1f; - - const bool firstNegativeSecondPositive = - track1.eta() > -0.8f && track1.eta() < -0.1f && - track2.eta() > 0.1f && track2.eta() < 0.8f; - - return track1.pt() < 3.f && track2.pt() < 3.f && - (firstPositiveSecondNegative || firstNegativeSecondPositive); - } - if (mode == kpTLeadConstrain) { + if (mode == kpTLeadConstrain) { // L-A: Leading pt in (2,3), Assoc pt < 2 return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < 2.f); } - if (mode == kpTLeadFree) { + if (mode == kpTLeadFree) { // L-A return (track1.globalIndex() == triggTrk.globalIndex()) && (track2.pt() < track1.pt()); } @@ -161,9 +153,7 @@ auto findLeadingPart(T const& tracks) i++; } - return std::make_tuple(tracks.iteratorAt(leadingAllId), - tracks.iteratorAt(leadingPartId), - foundPart); + return std::make_tuple(tracks.iteratorAt(leadingAllId), tracks.iteratorAt(leadingPartId), foundPart); } // Store selected tracks and collisions across data frames. using TrackTuple = std::tuple; @@ -186,20 +176,16 @@ struct FoxWolframCorrelation { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Configure histogram axes, event selection, and topology. - Configurable nBins{"nBins", 100, "no. bins in all histos"}; - Configurable nSelEv{"nSelEv", 200, "no. of mixing Ev."}; Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; Configurable etaRange{"etaRange", 0.8f, "eta range to consider"}; - Configurable dcaZ{"dcaZ", 0.2f, "custom DCA Z cut (ignored if negative)"}; + Configurable nSelEv{"nSelEv", 200, "no. of mixing Ev."}; Configurable doSameEvent{"doSameEvent", true, "Fill same-event TRI correlations"}; - Configurable doFWMCorrelations{"doFWMCorrelations", false, - "Fill H_i-H_j correlation histograms for RANGE1-RANGE3"}; - Configurable topologyMode{"topologyMode", kTriEvents, - "Topology used for selected-event outputs: 0 ALL, 1 TRI, 2 ISO"}; + Configurable doFWMCorrelations{"doFWMCorrelations", false, "Fill H_i-H_j corr. hist"}; + Configurable topologyMode{"topologyMode", kTriEvSetNom, + "SelOpt: 0 AllEv, 1 TriNom, 2 IsoNom, 3 TriSet1p, 4 TriSet2p, 5 TriSet3p, 6 TriSet1m, 7 TriSet2m, 8 TriSet3m"}; // Choose the pair selection for same-event correlations. - Configurable leadAssocMode{"leadAssocMode", 0, - "0: ALL i-j pairs (pT<3); 1: ALL i-j pairs (pT < 3, Eta constrain) 2: pTLead in (2,3) & pTAssoc < 2; 3: pTAssoc < pTLead"}; + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; Configurable isVtxRange{"isVtxRange", true, "isVtxRange"}; Configurable isSel8{"isSel8", true, "isSel8"}; @@ -210,48 +196,53 @@ struct FoxWolframCorrelation { Configurable kIsGoodITSLayer0123{"kIsGoodITSLayer0123", false, "kIsGoodITSLayer0123"}; Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "isNoCollInTimeRangeNarrow"}; - // Set the isotropic event selection thresholds. - Configurable h1IsoMax{"h1IsoMax", 0.02, "h1IsoMax"}; - Configurable h2IsoMax{"h2IsoMax", 0.27, "h2IsoMax"}; - Configurable h3IsoMax{"h3IsoMax", 0.02, "h3IsoMax"}; - Configurable h4IsoMax{"h4IsoMax", 0.16, "h4IsoMax"}; - Configurable h5IsoMax{"h5IsoMax", 0.02, "h5IsoMax"}; - Configurable h6IsoMax{"h6IsoMax", 0.12, "h6IsoMax"}; - Configurable h7IsoMax{"h7IsoMax", 0.02, "h7IsoMax"}; - Configurable h8IsoMax{"h8IsoMax", 0.10, "h8IsoMax"}; - - // Define the three broad multiplicity ranges. - Configurable range1Min{"range1Min", 40, "minRANGE1 "}; - Configurable range1Max{"range1Max", 85, "maxRANGE1"}; - Configurable range2Min{"range2Min", 86, "minRANGE2"}; - Configurable range2Max{"range2Max", 140, "maxRANGE2"}; - Configurable range3Min{"range3Min", 141, "minRANGE3"}; - Configurable range3Max{"range3Max", 280, "maxRANGE3"}; - - // Define the seven finer multiplicity intervals. - Configurable bin4Min{"bin4Min", 40, "minBIN4"}; - Configurable bin4Max{"bin4Max", 60, "maxBIN4"}; - Configurable bin5Min{"bin5Min", 61, "minBIN5"}; - Configurable bin5Max{"bin5Max", 80, "maxBIN5"}; - Configurable bin6Min{"bin6Min", 81, "minBIN6"}; - Configurable bin6Max{"bin6Max", 100, "maxBIN6"}; - Configurable bin7Min{"bin7Min", 101, "minBIN7"}; - Configurable bin7Max{"bin7Max", 120, "maxBIN7"}; - Configurable bin8Min{"bin8Min", 121, "minBIN8"}; - Configurable bin8Max{"bin8Max", 140, "maxBIN8"}; - Configurable bin9Min{"bin9Min", 141, "minBIN9"}; - Configurable bin9Max{"bin9Max", 160, "maxBIN9"}; - Configurable bin10Min{"bin10Min", 161, "minBIN10"}; - Configurable bin10Max{"bin10Max", 280, "maxBIN10"}; - - Configurable noOfMultipBins{"noOfMultipBins", 10, "noOfMultipBins"}; + //// Configurable FWM limits: H1, H2, H3, H4, H5, H6, H7, H8 //// + Configurable> kTriFWMSetNom{"kTriFWMSetNom", {0.05, 0.30, 0.040, 0.190, 0.020, 0.150, 0.016, 0.120}, "kTriFWMSetNom"}; + + Configurable> kTriFWMSet1p{"kTriFWMSet1p", {0.0525, 0.315, 0.0380, 0.1995, 0.0190, 0.1575, 0.0152, 0.1260}, "kTriFWMSet1p"}; + Configurable> kTriFWMSet2p{"kTriFWMSet2p", {0.0550, 0.330, 0.0360, 0.2090, 0.0180, 0.1650, 0.0144, 0.1320}, "kTriFWMSet2p"}; + Configurable> kTriFWMSet3p{"kTriFWMSet3p", {0.0575, 0.345, 0.0340, 0.2185, 0.0170, 0.1725, 0.0136, 0.1380}, "kTriFWMSet3p"}; + + Configurable> kTriFWMSet1m{"kTriFWMSet1m", {0.0475, 0.2850, 0.0420, 0.1805, 0.0210, 0.1425, 0.0168, 0.1140}, "kTriFWMSet1m"}; + Configurable> kTriFWMSet2m{"kTriFWMSet2m", {0.0450, 0.2700, 0.0440, 0.1710, 0.0220, 0.1350, 0.0176, 0.1080}, "kTriFWMSet2m"}; + Configurable> kTriFWMSet3m{"kTriFWMSet3m", {0.0425, 0.2550, 0.0460, 0.1615, 0.0230, 0.1275, 0.0184, 0.1020}, "kTriFWMSet3m"}; + + //// Named TRI selection indices //// + enum TriSelection { + kTriNominal, + kTriSet1p, + kTriSet2p, + kTriSet3p, + kTriSet1m, + kTriSet2m, + kTriSet3m, + kNTriSel + }; + using TriFlags = std::array; + + Configurable> isoFWMnominal{"isoFWMnominal", {0.02, 0.27, 0.02, 0.16, 0.02, 0.12, 0.02, 0.10}, "isoFWMnominal"}; + + // Multiplicity bins: bin i is [multLower[i], multUpper[i]] (inclusive limits). + // The first three entries are the broad ranges (RANGE1-3: FWM_BIN1-3, lead-assoc, mixing buffers), + // the remaining ones are the finer intervals (BIN4...). Bins may overlap. Max 10 bins. + // The same limits must be given to FoxWolframCorrelationMixing. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multiplicity limit of each bin (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multiplicity limit of each bin (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper static constexpr std::array binNames = { "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; // Map each unique pair of Fox-Wolfram moments to its histogram. + + static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; + static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; + static constexpr std::array fwmMultHNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; @@ -259,143 +250,104 @@ struct FoxWolframCorrelation { void init(InitContext const&) { // Reject unsupported topology modes at startup. - if (topologyMode.value < kAllEvents || topologyMode.value > kIsoEvents) { - LOGF(fatal, "Invalid topologyMode=%d. Allowed values: 0 ALL, 1 TRI, 2 ISO", topologyMode.value); + if (topologyMode.value < kAllEvSetNom || topologyMode.value > kTriEvSet3m) { + LOGF(fatal, "Invalid topologyMode=%d. Allowed values: 0 ALL, 1 TRI nominal, 2 ISO, 3 TRI 1p, 4 TRI 2p, 5 TRI 3p, 6 TRI 1m, 7 TRI 2m, 8 TRI 3m", topologyMode.value); + } + + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + for (int i = 0; i < nMultBins; ++i) { + if (multLower.value[i] > multUpper.value[i]) { + LOGF(fatal, "Multiplicity bin %d has multLower (%d) > multUpper (%d)", i + 1, multLower.value[i], multUpper.value[i]); + } } // Define axes shared by the registered histograms. const AxisSpec axispT{100, 0.15, 10, "p_{T}"}; - const AxisSpec axispTL{nBins, 2, 3, "p_{T}^{Lead}"}; - const AxisSpec axispTA{nBins, 0.15, 2, "p_{T}^{Assoc}"}; + const AxisSpec axispTL{100, 2, 3, "p_{T}^{Lead}"}; + const AxisSpec axispTA{100, 0.15, 2, "p_{T}^{Assoc}"}; const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; const AxisSpec axisPhi{100, 0, constants::math::TwoPI, "#phi"}; const AxisSpec axisEta{40, -0.8, 0.8, "#eta"}; - const AxisSpec axisMultip{350, 0, 350, "N_{ch}"}; - const AxisSpec axisHlrec{nBins / 2, 0, 1, "H_{l}^{rec}"}; - const AxisSpec axisHlfine{nBins, 0, 1, "H_{l}^{rec}"}; + const AxisSpec axisMultip{350, 0, 350, "Multiplicity"}; + const AxisSpec axisHlrec{50, 0, 1, "H_{l}"}; + const AxisSpec axisHlfine{100, 0, 1, "H_{l}"}; const AxisSpec axisEvent{10, 0.5, 10.5, "", "EventAxis"}; - const AxisSpec axisRecoil{nBins, 0., 1, "Recoil"}; + const AxisSpec axisRecoil{100, 0., 1, "Recoil"}; // Label same-event pair counts by multiplicity interval. histos.add("SamePairs/SamePairsNoLA", "SamePairsNoLA", kTH1D, {axisEvent}, false); - auto hstatLA = histos.get(HIST("SamePairs/SamePairsNoLA")); - auto* hLA = hstatLA->GetXaxis(); - hLA->SetBinLabel(1, "sPairsLA_RANGE1"); - hLA->SetBinLabel(2, "sPairsLA_RANGE2"); - hLA->SetBinLabel(3, "sPairsLA_RANGE3"); - hLA->SetBinLabel(4, "sPairsLA_BIN4"); - hLA->SetBinLabel(5, "sPairsLA_BIN5"); - hLA->SetBinLabel(6, "sPairsLA_BIN6"); - hLA->SetBinLabel(7, "sPairsLA_BIN7"); - hLA->SetBinLabel(8, "sPairsLA_BIN8"); - hLA->SetBinLabel(9, "sPairsLA_BIN9"); - hLA->SetBinLabel(10, "sPairsLA_BIN10"); - - // Label event counts for all multiplicity intervals. - histos.add("Events_Details/eventsNo", "No of EVENTS", kTH1D, {axisEvent}, false); - auto hstat2 = histos.get(HIST("Events_Details/eventsNo")); - auto* h2 = hstat2->GetXaxis(); - h2->SetBinLabel(1, "RANGE1"); - h2->SetBinLabel(2, "RANGE2"); - h2->SetBinLabel(3, "RANGE3"); - h2->SetBinLabel(4, "BIN4"); - h2->SetBinLabel(5, "BIN5"); - h2->SetBinLabel(6, "BIN6"); - h2->SetBinLabel(7, "BIN7"); - h2->SetBinLabel(8, "BIN8"); - h2->SetBinLabel(9, "BIN9"); - h2->SetBinLabel(10, "BIN10"); - - // Count events passing the nominal triangular selection. - histos.add("Events_Details/eventsNoTRI", "No of TRIANGLE EVENTS", kTH1D, {axisEvent}, false); - auto hstat3 = histos.get(HIST("Events_Details/eventsNoTRI")); - auto* h3 = hstat3->GetXaxis(); - h3->SetBinLabel(1, "RANGE1"); - h3->SetBinLabel(2, "RANGE2"); - h3->SetBinLabel(3, "RANGE3"); - h3->SetBinLabel(4, "BIN4"); - h3->SetBinLabel(5, "BIN5"); - h3->SetBinLabel(6, "BIN6"); - h3->SetBinLabel(7, "BIN7"); - h3->SetBinLabel(8, "BIN8"); - h3->SetBinLabel(9, "BIN9"); - h3->SetBinLabel(10, "BIN10"); - - // Count events passing the first alternate triangular selection. - histos.add("Events_Details/eventsNoTRI1", "No of TRI events selected by SET1", kTH1D, {axisEvent}, false); - auto hstatTRI1 = histos.get(HIST("Events_Details/eventsNoTRI1")); - auto* hTRI1 = hstatTRI1->GetXaxis(); - hTRI1->SetBinLabel(1, "RANGE1"); - hTRI1->SetBinLabel(2, "RANGE2"); - hTRI1->SetBinLabel(3, "RANGE3"); - hTRI1->SetBinLabel(4, "BIN4"); - hTRI1->SetBinLabel(5, "BIN5"); - hTRI1->SetBinLabel(6, "BIN6"); - hTRI1->SetBinLabel(7, "BIN7"); - hTRI1->SetBinLabel(8, "BIN8"); - hTRI1->SetBinLabel(9, "BIN9"); - hTRI1->SetBinLabel(10, "BIN10"); - - // Count events passing the second alternate triangular selection. - histos.add("Events_Details/eventsNoTRI2", "No of TRI events selected by SET2", kTH1D, {axisEvent}, false); - auto hstatTRI2 = histos.get(HIST("Events_Details/eventsNoTRI2")); - auto* hTRI2 = hstatTRI2->GetXaxis(); - hTRI2->SetBinLabel(1, "RANGE1"); - hTRI2->SetBinLabel(2, "RANGE2"); - hTRI2->SetBinLabel(3, "RANGE3"); - hTRI2->SetBinLabel(4, "BIN4"); - hTRI2->SetBinLabel(5, "BIN5"); - hTRI2->SetBinLabel(6, "BIN6"); - hTRI2->SetBinLabel(7, "BIN7"); - hTRI2->SetBinLabel(8, "BIN8"); - hTRI2->SetBinLabel(9, "BIN9"); - hTRI2->SetBinLabel(10, "BIN10"); - - // Count events passing the third alternate triangular selection. - histos.add("Events_Details/eventsNoTRI3", "No of TRI events selected by SET3", kTH1D, {axisEvent}, false); - auto hstatTRI3 = histos.get(HIST("Events_Details/eventsNoTRI3")); - auto* hTRI3 = hstatTRI3->GetXaxis(); - hTRI3->SetBinLabel(1, "RANGE1"); - hTRI3->SetBinLabel(2, "RANGE2"); - hTRI3->SetBinLabel(3, "RANGE3"); - hTRI3->SetBinLabel(4, "BIN4"); - hTRI3->SetBinLabel(5, "BIN5"); - hTRI3->SetBinLabel(6, "BIN6"); - hTRI3->SetBinLabel(7, "BIN7"); - hTRI3->SetBinLabel(8, "BIN8"); - hTRI3->SetBinLabel(9, "BIN9"); - hTRI3->SetBinLabel(10, "BIN10"); - - // Count events passing the tighter auxiliary triangular selection. - histos.add("Events_Details/eventsNoTRIAux", "No of TRI events selected by auxiliary SET", kTH1D, {axisEvent}, false); - auto hstatTRIAux = histos.get(HIST("Events_Details/eventsNoTRIAux")); - auto* hTRIAux = hstatTRIAux->GetXaxis(); - hTRIAux->SetBinLabel(1, "RANGE1"); - hTRIAux->SetBinLabel(2, "RANGE2"); - hTRIAux->SetBinLabel(3, "RANGE3"); - hTRIAux->SetBinLabel(4, "BIN4"); - hTRIAux->SetBinLabel(5, "BIN5"); - hTRIAux->SetBinLabel(6, "BIN6"); - hTRIAux->SetBinLabel(7, "BIN7"); - hTRIAux->SetBinLabel(8, "BIN8"); - hTRIAux->SetBinLabel(9, "BIN9"); - hTRIAux->SetBinLabel(10, "BIN10"); - - // Count events passing the isotropic selection. - histos.add("Events_Details/eventsNoISO", "No of ISO EVENTS", kTH1D, {axisEvent}, false); - auto hstat4 = histos.get(HIST("Events_Details/eventsNoISO")); - auto* h4 = hstat4->GetXaxis(); - h4->SetBinLabel(1, "RANGE1"); - h4->SetBinLabel(2, "RANGE2"); - h4->SetBinLabel(3, "RANGE3"); - h4->SetBinLabel(4, "BIN4"); - h4->SetBinLabel(5, "BIN5"); - h4->SetBinLabel(6, "BIN6"); - h4->SetBinLabel(7, "BIN7"); - h4->SetBinLabel(8, "BIN8"); - h4->SetBinLabel(9, "BIN9"); - h4->SetBinLabel(10, "BIN10"); + auto* samePairsNoLA = histos.get(HIST("SamePairs/SamePairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + samePairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("samePairsLA_RANGE{}", i + 1).c_str() : fmt::format("samePairsLA_BIN{}", i + 1).c_str()); + } + + // ALL Ev per Sel. BIN + histos.add("Events_Details/allEvSetNom", "SetAllNominal", kTH1D, {axisEvent}, false); + auto* allEvSetNom = histos.get(HIST("Events_Details/allEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + allEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // ISO Ev per Sel. BIN + histos.add("Events_Details/eventsNoISO", "SetIsoNominal", kTH1D, {axisEvent}, false); + auto* eventsNoISO = histos.get(HIST("Events_Details/eventsNoISO"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + eventsNoISO->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (NOMINAL) + histos.add("Events_Details/triEvSetNom", "SetTriNominal", kTH1D, {axisEvent}, false); + auto* triEvSetNom = histos.get(HIST("Events_Details/triEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1PLUS) + histos.add("Events_Details/triEvSet1p", "Set1TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet1p = histos.get(HIST("Events_Details/triEvSet1p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2PLUS) + histos.add("Events_Details/triEvSet2p", "Set2TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet2p = histos.get(HIST("Events_Details/triEvSet2p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3PLUS) + histos.add("Events_Details/triEvSet3p", "Set3TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet3p = histos.get(HIST("Events_Details/triEvSet3p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1MINUS) + histos.add("Events_Details/triEvSet1m", "Set1TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet1m = histos.get(HIST("Events_Details/triEvSet1m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2MINUS) + histos.add("Events_Details/triEvSet2m", "Set2TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet2m = histos.get(HIST("Events_Details/triEvSet2m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3MINUS) + histos.add("Events_Details/triEvSet3m", "Set3TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet3m = histos.get(HIST("Events_Details/triEvSet3m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } // Register inclusive multiplicity, track spectra, and recoil histograms. histos.add("Multip/ITSTPC_Multiplicity", "Reconstructed ITS-TPC Multiplicity; trks; Events", kTH1D, {axisMultip}); @@ -407,25 +359,44 @@ struct FoxWolframCorrelation { histos.add("pTSpectra/RecoilTRI", "RecoilTRI", kTH2D, {axisMultip, axisRecoil}); // Register track spectra for each multiplicity interval. - for (int i = 1; i <= noOfMultipBins; i++) { - histos.add(fmt::format("Multip/GlobalTrk_BIN{}", i).c_str(), fmt::format("GlobalTrk_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/GlobalTrkTri_BIN{}", i).c_str(), fmt::format("GlobalTrkTri_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/GlobalTrkTri1_BIN{}", i).c_str(), fmt::format("GlobalTrkTri1_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/GlobalTrkTri2_BIN{}", i).c_str(), fmt::format("GlobalTrkTri2_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/GlobalTrkTri3_BIN{}", i).c_str(), fmt::format("GlobalTrkTri3_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/GlobalTrkTriAux_BIN{}", i).c_str(), fmt::format("GlobalTrkTriAux_BIN{}", i).c_str(), kTH1D, {axisMultip}); - - histos.add(fmt::format("pTSpectra/pT_BIN{}", i).c_str(), fmt::format("pT_BIN{}", i).c_str(), kTH1D, {axispT}); - histos.add(fmt::format("pTSpectra/pTTri_BIN{}", i).c_str(), fmt::format("pTTri_BIN{}", i).c_str(), kTH1D, {axispT}); - - histos.add(fmt::format("Spectra/phi_BIN{}", i).c_str(), fmt::format("phi_BIN{}", i).c_str(), kTH1D, {axisPhi}); - histos.add(fmt::format("Spectra/phiTri_BIN{}", i).c_str(), fmt::format("phiTri_BIN{}", i).c_str(), kTH1D, {axisPhi}); + for (int i = 1; i <= nMultBins; i++) { + histos.add(fmt::format("Multip/nTrkAllEv_BIN{}", i).c_str(), fmt::format("nTrkAllEv_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrktriEvSetNom_BIN{}", i).c_str(), fmt::format("nTrktriEvSetNom_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS1p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS2p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS3p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS1m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1m_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS2m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2m_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS3m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3m_BIN{}", i).c_str(), kTH1D, {axisMultip}); histos.add(fmt::format("Spectra/etaphi_BIN{}", i).c_str(), fmt::format("etaphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); - histos.add(fmt::format("Spectra/etaphiTri_BIN{}", i).c_str(), fmt::format("etaphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); - histos.add(fmt::format("Spectra/ptphi_BIN{}", i).c_str(), fmt::format("ptphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + histos.add(fmt::format("Spectra/etaphiTri_BIN{}", i).c_str(), fmt::format("etaphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); histos.add(fmt::format("Spectra/ptphiTri_BIN{}", i).c_str(), fmt::format("ptphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + // onPeaks and onValleys - ALL EVENTS + histos.add(fmt::format("allEvPkVall/allLRNearPt_BIN{}", i).c_str(), fmt::format("allLRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRNearPt_BIN{}", i).c_str(), fmt::format("allSRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("allEvPkVall/allLRValleyPt_BIN{}", i).c_str(), fmt::format("allLRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRValleyPt_BIN{}", i).c_str(), fmt::format("allSRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("allEvPkVall/allLRAwayPt_BIN{}", i).c_str(), fmt::format("allLRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRAwayPt_BIN{}", i).c_str(), fmt::format("allSRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + // onPeaks and onValleys - TRI EVENTS + histos.add(fmt::format("triEvPkVall/triLRPeak1Pt_BIN{}", i).c_str(), fmt::format("triLRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak1Pt_BIN{}", i).c_str(), fmt::format("triSRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRPeak2Pt_BIN{}", i).c_str(), fmt::format("triLRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak2Pt_BIN{}", i).c_str(), fmt::format("triSRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRPeak3Pt_BIN{}", i).c_str(), fmt::format("triLRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak3Pt_BIN{}", i).c_str(), fmt::format("triSRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRValleysPt_BIN{}", i).c_str(), fmt::format("triLRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRValleysPt_BIN{}", i).c_str(), fmt::format("triSRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); } // Register leading and associated track spectra in the broad ranges. @@ -435,13 +406,10 @@ struct FoxWolframCorrelation { histos.add(fmt::format("pTSpectra/fpT_LA_BIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); histos.add(fmt::format("pTSpectra/fpT_LA_TriBIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); - - histos.add(fmt::format("pTSpectra/peakPT_BIN{}", i).c_str(), fmt::format("peakPT_BIN{}", i).c_str(), kTH1D, {axispT}); - histos.add(fmt::format("pTSpectra/valleyPT_BIN{}", i).c_str(), fmt::format("valleyPT_BIN{}", i).c_str(), kTH1D, {axispT}); } // Register same-event angular correlations. - for (int i = 1; i <= noOfMultipBins; ++i) { + for (int i = 1; i <= nMultBins; ++i) { histos.add(fmt::format("Corelations/sTRI_BIN{}", i).c_str(), fmt::format("sTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); histos.add(fmt::format("Corelations/sProjTRI_BIN{}", i).c_str(), fmt::format("sProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); } @@ -570,8 +538,7 @@ struct FoxWolframCorrelation { // Fill inclusive and topology-selected observables in one multiplicity interval. template void fillHistoMult(int binIndex, int minMult, int maxMult, - bool isSelectedTopology, bool isTRI, bool isTRI1, - bool isTRI2, bool isTRI3, bool isTRIAux, bool isISO, + bool isSelectedTopology, const TriFlags& triFlags, bool isIsoNominal, const T& tracks) { if (tracks.size() < minMult || tracks.size() > maxMult) { @@ -579,28 +546,39 @@ struct FoxWolframCorrelation { } // Count all events and each topology selection in this interval. - histos.fill(HIST("Events_Details/eventsNo"), binIndex); + histos.fill(HIST("Events_Details/allEvSetNom"), binIndex); - if (isISO) { + if (isIsoNominal) { histos.fill(HIST("Events_Details/eventsNoISO"), binIndex); } - if (isTRI) { - histos.fill(HIST("Events_Details/eventsNoTRI"), binIndex); + if (triFlags[kTriNominal]) { + histos.fill(HIST("Events_Details/triEvSetNom"), binIndex); } // The remaining filters - if (isTRI1) { - histos.fill(HIST("Events_Details/eventsNoTRI1"), binIndex); + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Events_Details/triEvSet1p"), binIndex); } - if (isTRI2) { - histos.fill(HIST("Events_Details/eventsNoTRI2"), binIndex); + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Events_Details/triEvSet2p"), binIndex); } - if (isTRI3) { - histos.fill(HIST("Events_Details/eventsNoTRI3"), binIndex); + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Events_Details/triEvSet3p"), binIndex); } - if (isTRIAux) { - histos.fill(HIST("Events_Details/eventsNoTRIAux"), binIndex); + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Events_Details/triEvSet1m"), binIndex); + } + + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Events_Details/triEvSet2m"), binIndex); + } + + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Events_Details/triEvSet3m"), binIndex); } // Fill inclusive spectra and selected-topology spectra for this interval. @@ -609,13 +587,9 @@ struct FoxWolframCorrelation { if (binIndex == idx + 1) { - histos.fill(HIST("Multip/GlobalTrk_") + HIST(binNames[idx]), tracks.size()); + histos.fill(HIST("Multip/nTrkAllEv_") + HIST(binNames[idx]), tracks.size()); for (const auto& track : tracks) { - - histos.fill(HIST("pTSpectra/pT_") + HIST(binNames[idx]), track.pt()); - histos.fill(HIST("Spectra/phi_") + HIST(binNames[idx]), track.phi()); - histos.fill(HIST("Spectra/etaphi_") + HIST(binNames[idx]), track.phi(), track.eta()); histos.fill(HIST("Spectra/ptphi_") + HIST(binNames[idx]), track.phi(), track.pt()); } @@ -623,32 +597,35 @@ struct FoxWolframCorrelation { // Fill spectra for the configured topology selection. if (isSelectedTopology) { - histos.fill(HIST("Multip/GlobalTrkTri_") + HIST(binNames[idx]), tracks.size()); + histos.fill(HIST("Multip/nTrktriEvSetNom_") + HIST(binNames[idx]), tracks.size()); for (const auto& track : tracks) { - histos.fill(HIST("pTSpectra/pTTri_") + HIST(binNames[idx]), track.pt()); - histos.fill(HIST("Spectra/phiTri_") + HIST(binNames[idx]), track.phi()); - histos.fill(HIST("Spectra/etaphiTri_") + HIST(binNames[idx]), track.phi(), track.eta()); histos.fill(HIST("Spectra/ptphiTri_") + HIST(binNames[idx]), track.phi(), track.pt()); } } - // TRI Ev. Multip (the remaining filters) - if (isTRI1) { - histos.fill(HIST("Multip/GlobalTrkTri1_") + HIST(binNames[idx]), tracks.size()); + // TRI Ev. Multip (the remaining filters) - p == permissive and m == restrictive + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Multip/nTrkTriEvS1p_") + HIST(binNames[idx]), tracks.size()); } - if (isTRI2) { - histos.fill(HIST("Multip/GlobalTrkTri2_") + HIST(binNames[idx]), tracks.size()); + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Multip/nTrkTriEvS2p_") + HIST(binNames[idx]), tracks.size()); } - if (isTRI3) { - histos.fill(HIST("Multip/GlobalTrkTri3_") + HIST(binNames[idx]), tracks.size()); + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Multip/nTrkTriEvS3p_") + HIST(binNames[idx]), tracks.size()); } - if (isTRIAux) { - histos.fill(HIST("Multip/GlobalTrkTriAux_") + HIST(binNames[idx]), tracks.size()); + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Multip/nTrkTriEvS1m_") + HIST(binNames[idx]), tracks.size()); + } + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Multip/nTrkTriEvS2m_") + HIST(binNames[idx]), tracks.size()); + } + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Multip/nTrkTriEvS3m_") + HIST(binNames[idx]), tracks.size()); } } }); // per Multip Bin @@ -660,8 +637,6 @@ struct FoxWolframCorrelation { Track const& associatedTrack, double deltaPhi, double deltaEta, - bool onPeaks, - bool onValleys, bool isLeadAssocPair) { @@ -676,17 +651,106 @@ struct FoxWolframCorrelation { return; } - // Record peak and valley track spectra in the broad ranges. - if constexpr (idx < 3) { - if (leadAssocMode.value == 3) { - if (onPeaks) { - histos.fill(HIST("pTSpectra/peakPT_") + HIST(binNames[idx]), associatedTrack.pt()); + const double degToRad = constants::math::PI / 180.; + const double absDeltaEta = std::abs(deltaEta); + + const bool inLongRange = absDeltaEta > 1.25 && absDeltaEta < 1.6; + const bool inShortRange = absDeltaEta < 0.35; + + /// for ALL Ev. + const bool onNearPeak = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; + const bool onBetweenNearAway = deltaPhi > 75. * degToRad && deltaPhi < 105. * degToRad; + const bool onAwayPeak = deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad; + + if (topologyMode.value == kAllEvSetNom && leadAssocMode.value == 2) { // for the case with ALL sel Ev + + if (onNearPeak) { // NEAR SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); } - if (onValleys) { - histos.fill(HIST("pTSpectra/valleyPT_") + HIST(binNames[idx]), associatedTrack.pt()); + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); } } - } + + if (onBetweenNearAway) { // BETWEEN SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onAwayPeak) { // AWAY SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + } // for ALL Events + + /// for TRI sel Ev. + const bool onPeak1 = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; + const bool onPeak2 = deltaPhi > 105. * degToRad && deltaPhi < 135. * degToRad; + const bool onPeak3 = deltaPhi > 225. * degToRad && deltaPhi < 255. * degToRad; + const bool onValleys = (deltaPhi > 45. * degToRad && deltaPhi < 75. * degToRad) || (deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad); + + const bool isAnyTriMode = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; + + if (isAnyTriMode && leadAssocMode.value == 2) { // for the case with TRI sel Ev (any Sel Filter) + + if (onPeak1) { // on Peak at 0 + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onPeak2) { // on Peak at 120 + + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onPeak3) { // on Peak at 240 + + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onValleys) { + // Combined valleys at 60 and 180 degrees // + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + } // for TRI-like Events // Count accepted pairs and fill their angular distributions. histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); @@ -695,9 +759,28 @@ struct FoxWolframCorrelation { }); } + /// Helper template for FWMs, FWMs vs Multip, FWMs i-j corr. + template + void fillFWMRange(const std::array& H, int nTrk) + { + // H[0] = H0 (normalizare), H[1..8] = H1..H8 + static_for<0, 7>([&](auto j) { + constexpr int jj = j.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmHNames[jj]), H[jj + 1]); + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmMultHNames[jj]), nTrk, H[jj + 1]); + }); + + if (doFWMCorrelations) { + static_for<0, 27>([&](auto k) { + constexpr int kk = k.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmCorrelationNames[kk]), H[fwmCorrelationFirst[kk] + 1], H[fwmCorrelationSecond[kk] + 1]); + }); + } + } + std::array, 3> triTuples; - Filter ITSTPCTracks = requireGlobalTrackInFilter(); + Filter itsTpcTracks = requireGlobalTrackInFilter(); using GlobalTracks = soa::Filtered; // Process reconstructed events and buffer selected tracks for mixing. @@ -721,42 +804,60 @@ struct FoxWolframCorrelation { auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks); // Assign the event to broad and fine multiplicity intervals. - bool multRANGE1 = (nGlobalTracks >= range1Min && nGlobalTracks <= range1Max); - bool multRANGE2 = (nGlobalTracks >= range2Min && nGlobalTracks <= range2Max); - bool multRANGE3 = (nGlobalTracks >= range3Min && nGlobalTracks <= range3Max); - bool multBIN4 = (nGlobalTracks >= bin4Min && nGlobalTracks <= bin4Max); - bool multBIN5 = (nGlobalTracks >= bin5Min && nGlobalTracks <= bin5Max); - bool multBIN6 = (nGlobalTracks >= bin6Min && nGlobalTracks <= bin6Max); - bool multBIN7 = (nGlobalTracks >= bin7Min && nGlobalTracks <= bin7Max); - bool multBIN8 = (nGlobalTracks >= bin8Min && nGlobalTracks <= bin8Max); - bool multBIN9 = (nGlobalTracks >= bin9Min && nGlobalTracks <= bin9Max); - bool multBIN10 = (nGlobalTracks >= bin10Min && nGlobalTracks <= bin10Max); + std::array inBin{}; + for (int i = 0; i < nMultBins; ++i) { + inBin[i] = (nGlobalTracks >= multLower.value[i] && nGlobalTracks <= multUpper.value[i]); + } + const bool multRANGE1 = inBin[0]; + const bool multRANGE2 = inBin[1]; + const bool multRANGE3 = inBin[2]; // Compute event moments and evaluate topology selections. auto fwH = computeFWM(tracks); auto [H0, H1, H2, H3, H4, H5, H6, H7, H8] = fwH; - // Use tighter moment limits for the auxiliary triangular sample. - const bool isTRIAux = (H1 < 0.0475) && (H2 < 0.2850) && (H3 > 0.0420) && (H4 < 0.1805) && (H5 > 0.0210) && (H6 < 0.1425) && (H7 > 0.0168) && (H8 < 0.1140); + // FWMs, FWMs vs Multip, FWMs i-j corr. + if (multRANGE1) { + fillFWMRange<0>(fwH, nGlobalTracks); + } - // Apply the nominal triangular moment limits. - const bool isTRI = (H1 < 0.05) && (H2 < 0.30) && (H3 > 0.040) && (H4 < 0.190) && (H5 > 0.020) && (H6 < 0.150) && (H7 > 0.016) && (H8 < 0.120); + if (multRANGE2) { + fillFWMRange<1>(fwH, nGlobalTracks); + } + + if (multRANGE3) { + fillFWMRange<2>(fwH, nGlobalTracks); + } - // Apply three progressively looser triangular selections. - const bool isTRI1 = (H1 < 0.0525) && (H2 < 0.315) && (H3 > 0.0380) && (H4 < 0.1995) && (H5 > 0.0190) && (H6 < 0.1575) && (H7 > 0.0152) && (H8 < 0.1260); + // Apply the nominal triangular moment limits + const bool isTriSetNom = (H1 < kTriFWMSetNom.value[0]) && (H2 < kTriFWMSetNom.value[1]) && (H3 > kTriFWMSetNom.value[2]) && (H4 < kTriFWMSetNom.value[3]) && (H5 > kTriFWMSetNom.value[4]) && (H6 < kTriFWMSetNom.value[5]) && (H7 > kTriFWMSetNom.value[6]) && (H8 < kTriFWMSetNom.value[7]); - const bool isTRI2 = (H1 < 0.0550) && (H2 < 0.330) && (H3 > 0.0360) && (H4 < 0.2090) && (H5 > 0.0180) && (H6 < 0.1650) && (H7 > 0.0144) && (H8 < 0.1320); + // Apply three progressively looser triangular selections + const bool isTriSet1p = (H1 < kTriFWMSet1p.value[0]) && (H2 < kTriFWMSet1p.value[1]) && (H3 > kTriFWMSet1p.value[2]) && (H4 < kTriFWMSet1p.value[3]) && (H5 > kTriFWMSet1p.value[4]) && (H6 < kTriFWMSet1p.value[5]) && (H7 > kTriFWMSet1p.value[6]) && (H8 < kTriFWMSet1p.value[7]); + const bool isTriSet2p = (H1 < kTriFWMSet2p.value[0]) && (H2 < kTriFWMSet2p.value[1]) && (H3 > kTriFWMSet2p.value[2]) && (H4 < kTriFWMSet2p.value[3]) && (H5 > kTriFWMSet2p.value[4]) && (H6 < kTriFWMSet2p.value[5]) && (H7 > kTriFWMSet2p.value[6]) && (H8 < kTriFWMSet2p.value[7]); + const bool isTriSet3p = (H1 < kTriFWMSet3p.value[0]) && (H2 < kTriFWMSet3p.value[1]) && (H3 > kTriFWMSet3p.value[2]) && (H4 < kTriFWMSet3p.value[3]) && (H5 > kTriFWMSet3p.value[4]) && (H6 < kTriFWMSet3p.value[5]) && (H7 > kTriFWMSet3p.value[6]) && (H8 < kTriFWMSet3p.value[7]); - const bool isTRI3 = (H1 < 0.0575) && (H2 < 0.345) && (H3 > 0.0340) && (H4 < 0.2185) && (H5 > 0.0170) && (H6 < 0.1725) && (H7 > 0.0136) && (H8 < 0.1380); + // Apply three restrictively looser triangular selections + const bool isTriSet1m = (H1 < kTriFWMSet1m.value[0]) && (H2 < kTriFWMSet1m.value[1]) && (H3 > kTriFWMSet1m.value[2]) && (H4 < kTriFWMSet1m.value[3]) && (H5 > kTriFWMSet1m.value[4]) && (H6 < kTriFWMSet1m.value[5]) && (H7 > kTriFWMSet1m.value[6]) && (H8 < kTriFWMSet1m.value[7]); + const bool isTriSet2m = (H1 < kTriFWMSet2m.value[0]) && (H2 < kTriFWMSet2m.value[1]) && (H3 > kTriFWMSet2m.value[2]) && (H4 < kTriFWMSet2m.value[3]) && (H5 > kTriFWMSet2m.value[4]) && (H6 < kTriFWMSet2m.value[5]) && (H7 > kTriFWMSet2m.value[6]) && (H8 < kTriFWMSet2m.value[7]); + const bool isTriSet3m = (H1 < kTriFWMSet3m.value[0]) && (H2 < kTriFWMSet3m.value[1]) && (H3 > kTriFWMSet3m.value[2]) && (H4 < kTriFWMSet3m.value[3]) && (H5 > kTriFWMSet3m.value[4]) && (H6 < kTriFWMSet3m.value[5]) && (H7 > kTriFWMSet3m.value[6]) && (H8 < kTriFWMSet3m.value[7]); + + const TriFlags triFlags = {isTriSetNom, isTriSet1p, isTriSet2p, isTriSet3p, isTriSet1m, isTriSet2m, isTriSet3m}; // Apply the configurable isotropic moment limits. - const bool isISO = (H1 < h1IsoMax) && (H2 < h2IsoMax) && (H3 < h3IsoMax) && (H4 < h4IsoMax) && (H5 < h5IsoMax) && (H6 < h6IsoMax) && (H7 < h7IsoMax) && (H8 < h8IsoMax); + const bool isIsoNominal = (H1 < isoFWMnominal.value[0]) && (H2 < isoFWMnominal.value[1]) && (H3 < isoFWMnominal.value[2]) && (H4 < isoFWMnominal.value[3]) && (H5 < isoFWMnominal.value[4]) && (H6 < isoFWMnominal.value[5]) && (H7 < isoFWMnominal.value[6]) && (H8 < isoFWMnominal.value[7]); // Select all, triangular, or isotropic events for correlations. const bool isSelectedTopology = - (topologyMode.value == kAllEvents) || - (topologyMode.value == kTriEvents && isTRI) || - (topologyMode.value == kIsoEvents && isISO); + (topologyMode.value == kAllEvSetNom) || + (topologyMode.value == kTriEvSetNom && isTriSetNom) || + (topologyMode.value == kIsoEvSetNom && isIsoNominal) || + (topologyMode.value == kTriEvSet1p && isTriSet1p) || + (topologyMode.value == kTriEvSet2p && isTriSet2p) || + (topologyMode.value == kTriEvSet3p && isTriSet3p) || + (topologyMode.value == kTriEvSet1m && isTriSet1m) || + (topologyMode.value == kTriEvSet2m && isTriSet2m) || + (topologyMode.value == kTriEvSet3m && isTriSet3m); // Fill inclusive event multiplicity and recoil. histos.fill(HIST("Multip/ITSTPC_Multiplicity"), nGlobalTracks); @@ -766,159 +867,6 @@ struct FoxWolframCorrelation { histos.fill(HIST("pTSpectra/RecoilTRI"), nGlobalTracks, nRecoil); } - // Fill moments and optional moment correlations in the first broad range. - if (multRANGE1) { - - histos.fill(HIST("FWM_BIN1/H1"), H1); - histos.fill(HIST("FWM_BIN1/H2"), H2); - histos.fill(HIST("FWM_BIN1/H3"), H3); - histos.fill(HIST("FWM_BIN1/H4"), H4); - histos.fill(HIST("FWM_BIN1/H5"), H5); - histos.fill(HIST("FWM_BIN1/H6"), H6); - histos.fill(HIST("FWM_BIN1/H7"), H7); - histos.fill(HIST("FWM_BIN1/H8"), H8); - histos.fill(HIST("FWM_BIN1/Multip_H1"), nGlobalTracks, H1); - histos.fill(HIST("FWM_BIN1/Multip_H2"), nGlobalTracks, H2); - histos.fill(HIST("FWM_BIN1/Multip_H3"), nGlobalTracks, H3); - histos.fill(HIST("FWM_BIN1/Multip_H4"), nGlobalTracks, H4); - histos.fill(HIST("FWM_BIN1/Multip_H5"), nGlobalTracks, H5); - histos.fill(HIST("FWM_BIN1/Multip_H6"), nGlobalTracks, H6); - histos.fill(HIST("FWM_BIN1/Multip_H7"), nGlobalTracks, H7); - histos.fill(HIST("FWM_BIN1/Multip_H8"), nGlobalTracks, H8); - if (doFWMCorrelations) { - histos.fill(HIST("FWM_BIN1/H1_H2"), H1, H2); - histos.fill(HIST("FWM_BIN1/H1_H3"), H1, H3); - histos.fill(HIST("FWM_BIN1/H1_H4"), H1, H4); - histos.fill(HIST("FWM_BIN1/H1_H5"), H1, H5); - histos.fill(HIST("FWM_BIN1/H1_H6"), H1, H6); - histos.fill(HIST("FWM_BIN1/H1_H7"), H1, H7); - histos.fill(HIST("FWM_BIN1/H1_H8"), H1, H8); - histos.fill(HIST("FWM_BIN1/H2_H3"), H2, H3); - histos.fill(HIST("FWM_BIN1/H2_H4"), H2, H4); - histos.fill(HIST("FWM_BIN1/H2_H5"), H2, H5); - histos.fill(HIST("FWM_BIN1/H2_H6"), H2, H6); - histos.fill(HIST("FWM_BIN1/H2_H7"), H2, H7); - histos.fill(HIST("FWM_BIN1/H2_H8"), H2, H8); - histos.fill(HIST("FWM_BIN1/H3_H4"), H3, H4); - histos.fill(HIST("FWM_BIN1/H3_H5"), H3, H5); - histos.fill(HIST("FWM_BIN1/H3_H6"), H3, H6); - histos.fill(HIST("FWM_BIN1/H3_H7"), H3, H7); - histos.fill(HIST("FWM_BIN1/H3_H8"), H3, H8); - histos.fill(HIST("FWM_BIN1/H4_H5"), H4, H5); - histos.fill(HIST("FWM_BIN1/H4_H6"), H4, H6); - histos.fill(HIST("FWM_BIN1/H4_H7"), H4, H7); - histos.fill(HIST("FWM_BIN1/H4_H8"), H4, H8); - histos.fill(HIST("FWM_BIN1/H5_H6"), H5, H6); - histos.fill(HIST("FWM_BIN1/H5_H7"), H5, H7); - histos.fill(HIST("FWM_BIN1/H5_H8"), H5, H8); - histos.fill(HIST("FWM_BIN1/H6_H7"), H6, H7); - histos.fill(HIST("FWM_BIN1/H6_H8"), H6, H8); - histos.fill(HIST("FWM_BIN1/H7_H8"), H7, H8); - } - } - - // Fill moments and optional moment correlations in the second broad range. - if (multRANGE2) { - - histos.fill(HIST("FWM_BIN2/H1"), H1); - histos.fill(HIST("FWM_BIN2/H2"), H2); - histos.fill(HIST("FWM_BIN2/H3"), H3); - histos.fill(HIST("FWM_BIN2/H4"), H4); - histos.fill(HIST("FWM_BIN2/H5"), H5); - histos.fill(HIST("FWM_BIN2/H6"), H6); - histos.fill(HIST("FWM_BIN2/H7"), H7); - histos.fill(HIST("FWM_BIN2/H8"), H8); - histos.fill(HIST("FWM_BIN2/Multip_H1"), nGlobalTracks, H1); - histos.fill(HIST("FWM_BIN2/Multip_H2"), nGlobalTracks, H2); - histos.fill(HIST("FWM_BIN2/Multip_H3"), nGlobalTracks, H3); - histos.fill(HIST("FWM_BIN2/Multip_H4"), nGlobalTracks, H4); - histos.fill(HIST("FWM_BIN2/Multip_H5"), nGlobalTracks, H5); - histos.fill(HIST("FWM_BIN2/Multip_H6"), nGlobalTracks, H6); - histos.fill(HIST("FWM_BIN2/Multip_H7"), nGlobalTracks, H7); - histos.fill(HIST("FWM_BIN2/Multip_H8"), nGlobalTracks, H8); - if (doFWMCorrelations) { - histos.fill(HIST("FWM_BIN2/H1_H2"), H1, H2); - histos.fill(HIST("FWM_BIN2/H1_H3"), H1, H3); - histos.fill(HIST("FWM_BIN2/H1_H4"), H1, H4); - histos.fill(HIST("FWM_BIN2/H1_H5"), H1, H5); - histos.fill(HIST("FWM_BIN2/H1_H6"), H1, H6); - histos.fill(HIST("FWM_BIN2/H1_H7"), H1, H7); - histos.fill(HIST("FWM_BIN2/H1_H8"), H1, H8); - histos.fill(HIST("FWM_BIN2/H2_H3"), H2, H3); - histos.fill(HIST("FWM_BIN2/H2_H4"), H2, H4); - histos.fill(HIST("FWM_BIN2/H2_H5"), H2, H5); - histos.fill(HIST("FWM_BIN2/H2_H6"), H2, H6); - histos.fill(HIST("FWM_BIN2/H2_H7"), H2, H7); - histos.fill(HIST("FWM_BIN2/H2_H8"), H2, H8); - histos.fill(HIST("FWM_BIN2/H3_H4"), H3, H4); - histos.fill(HIST("FWM_BIN2/H3_H5"), H3, H5); - histos.fill(HIST("FWM_BIN2/H3_H6"), H3, H6); - histos.fill(HIST("FWM_BIN2/H3_H7"), H3, H7); - histos.fill(HIST("FWM_BIN2/H3_H8"), H3, H8); - histos.fill(HIST("FWM_BIN2/H4_H5"), H4, H5); - histos.fill(HIST("FWM_BIN2/H4_H6"), H4, H6); - histos.fill(HIST("FWM_BIN2/H4_H7"), H4, H7); - histos.fill(HIST("FWM_BIN2/H4_H8"), H4, H8); - histos.fill(HIST("FWM_BIN2/H5_H6"), H5, H6); - histos.fill(HIST("FWM_BIN2/H5_H7"), H5, H7); - histos.fill(HIST("FWM_BIN2/H5_H8"), H5, H8); - histos.fill(HIST("FWM_BIN2/H6_H7"), H6, H7); - histos.fill(HIST("FWM_BIN2/H6_H8"), H6, H8); - histos.fill(HIST("FWM_BIN2/H7_H8"), H7, H8); - } - } - - // Fill moments and optional moment correlations in the third broad range. - if (multRANGE3) { - - histos.fill(HIST("FWM_BIN3/H1"), H1); - histos.fill(HIST("FWM_BIN3/H2"), H2); - histos.fill(HIST("FWM_BIN3/H3"), H3); - histos.fill(HIST("FWM_BIN3/H4"), H4); - histos.fill(HIST("FWM_BIN3/H5"), H5); - histos.fill(HIST("FWM_BIN3/H6"), H6); - histos.fill(HIST("FWM_BIN3/H7"), H7); - histos.fill(HIST("FWM_BIN3/H8"), H8); - histos.fill(HIST("FWM_BIN3/Multip_H1"), nGlobalTracks, H1); - histos.fill(HIST("FWM_BIN3/Multip_H2"), nGlobalTracks, H2); - histos.fill(HIST("FWM_BIN3/Multip_H3"), nGlobalTracks, H3); - histos.fill(HIST("FWM_BIN3/Multip_H4"), nGlobalTracks, H4); - histos.fill(HIST("FWM_BIN3/Multip_H5"), nGlobalTracks, H5); - histos.fill(HIST("FWM_BIN3/Multip_H6"), nGlobalTracks, H6); - histos.fill(HIST("FWM_BIN3/Multip_H7"), nGlobalTracks, H7); - histos.fill(HIST("FWM_BIN3/Multip_H8"), nGlobalTracks, H8); - if (doFWMCorrelations) { - histos.fill(HIST("FWM_BIN3/H1_H2"), H1, H2); - histos.fill(HIST("FWM_BIN3/H1_H3"), H1, H3); - histos.fill(HIST("FWM_BIN3/H1_H4"), H1, H4); - histos.fill(HIST("FWM_BIN3/H1_H5"), H1, H5); - histos.fill(HIST("FWM_BIN3/H1_H6"), H1, H6); - histos.fill(HIST("FWM_BIN3/H1_H7"), H1, H7); - histos.fill(HIST("FWM_BIN3/H1_H8"), H1, H8); - histos.fill(HIST("FWM_BIN3/H2_H3"), H2, H3); - histos.fill(HIST("FWM_BIN3/H2_H4"), H2, H4); - histos.fill(HIST("FWM_BIN3/H2_H5"), H2, H5); - histos.fill(HIST("FWM_BIN3/H2_H6"), H2, H6); - histos.fill(HIST("FWM_BIN3/H2_H7"), H2, H7); - histos.fill(HIST("FWM_BIN3/H2_H8"), H2, H8); - histos.fill(HIST("FWM_BIN3/H3_H4"), H3, H4); - histos.fill(HIST("FWM_BIN3/H3_H5"), H3, H5); - histos.fill(HIST("FWM_BIN3/H3_H6"), H3, H6); - histos.fill(HIST("FWM_BIN3/H3_H7"), H3, H7); - histos.fill(HIST("FWM_BIN3/H3_H8"), H3, H8); - histos.fill(HIST("FWM_BIN3/H4_H5"), H4, H5); - histos.fill(HIST("FWM_BIN3/H4_H6"), H4, H6); - histos.fill(HIST("FWM_BIN3/H4_H7"), H4, H7); - histos.fill(HIST("FWM_BIN3/H4_H8"), H4, H8); - histos.fill(HIST("FWM_BIN3/H5_H6"), H5, H6); - histos.fill(HIST("FWM_BIN3/H5_H7"), H5, H7); - histos.fill(HIST("FWM_BIN3/H5_H8"), H5, H8); - histos.fill(HIST("FWM_BIN3/H6_H7"), H6, H7); - histos.fill(HIST("FWM_BIN3/H6_H8"), H6, H8); - histos.fill(HIST("FWM_BIN3/H7_H8"), H7, H8); - } - } - // Fill inclusive and topology-selected leading-track spectra. for (const auto& track : tracks) { @@ -974,49 +922,13 @@ struct FoxWolframCorrelation { const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); const double deltaEta = track1.eta() - track2.eta(); - const bool onPeaks = (deltaPhi > -0.4 && deltaPhi < 0.4) || - (deltaPhi > 1.6 && deltaPhi < 2.4) || - (deltaPhi > 3.6 && deltaPhi < 4.4); - - const bool onValleys = (deltaPhi > -2.6 && deltaPhi < -1.4) || - (deltaPhi > 0.6 && deltaPhi < 1.4) || - (deltaPhi > 2.6 && deltaPhi < 3.4); - // Apply the configured pair selection to each interval. const bool isLeadAssocPair = passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart); - if (multRANGE1) { - fillSameEventHist(1, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - - if (multRANGE2) { - fillSameEventHist(2, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - - if (multRANGE3) { - fillSameEventHist(3, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - - if (multBIN4) { - fillSameEventHist(4, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN5) { - fillSameEventHist(5, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN6) { - fillSameEventHist(6, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN7) { - fillSameEventHist(7, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN8) { - fillSameEventHist(8, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN9) { - fillSameEventHist(9, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); - } - if (multBIN10) { - fillSameEventHist(10, track2, deltaPhi, deltaEta, onPeaks, onValleys, isLeadAssocPair); + for (int i = 0; i < nMultBins; ++i) { + if (inBin[i]) { + fillSameEventHist(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + } } } } @@ -1046,16 +958,9 @@ struct FoxWolframCorrelation { } // Fill event counts and spectra for all matching multiplicity intervals. - fillHistoMult(1, range1Min, range1Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(2, range2Min, range2Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(3, range3Min, range3Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(4, bin4Min, bin4Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(5, bin5Min, bin5Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(6, bin6Min, bin6Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(7, bin7Min, bin7Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(8, bin8Min, bin8Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(9, bin9Min, bin9Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); - fillHistoMult(10, bin10Min, bin10Max, isSelectedTopology, isTRI, isTRI1, isTRI2, isTRI3, isTRIAux, isISO, tracks); + for (int i = 0; i < nMultBins; ++i) { + fillHistoMult(i + 1, multLower.value[i], multUpper.value[i], isSelectedTopology, triFlags, isIsoNominal, tracks); + } } int countDF = 0; @@ -1103,15 +1008,13 @@ struct FoxWolframCorrelation { // Build mixed-event backgrounds from the selected event tables. struct FoxWolframCorrelationMixing { - SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6; - SliceCache cache_bin7, cache_bin8, cache_bin9, cache_bin10; + SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6, cache_bin7, cache_bin8, cache_bin9, cache_bin10; Preslice triCol = aod::track_tri::triangleCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; static constexpr std::array mixedBinNames = { - "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", - "BIN7", "BIN8", "BIN9", "BIN10"}; + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; Configurable nBin{"nBin", 100, "No of bis per axis"}; ConfigurableAxis axisVtx{"axisVtx", {VARIABLE_WIDTH, -10, 10}, "vertex axis for mixed event histograms"}; @@ -1119,46 +1022,59 @@ struct FoxWolframCorrelationMixing { // Use half-integer edges for inclusive integer multiplicity bins. ConfigurableAxis axisMult1{"axisMult1", {VARIABLE_WIDTH, 39.5, 85.5}, "axisMult1"}; ConfigurableAxis axisMult2{"axisMult2", {VARIABLE_WIDTH, 85.5, 140.5}, "axisMult2"}; - ConfigurableAxis axisMult3{"axisMult3", {VARIABLE_WIDTH, 140.5, 280.5}, "axisMult3"}; + ConfigurableAxis axisMult3{"axisMult3", {VARIABLE_WIDTH, 140.5, 350.5}, "axisMult3"}; ConfigurableAxis axisMult4{"axisMult4", {VARIABLE_WIDTH, 39.5, 60.5}, "axisMult4"}; ConfigurableAxis axisMult5{"axisMult5", {VARIABLE_WIDTH, 60.5, 80.5}, "axisMult5"}; ConfigurableAxis axisMult6{"axisMult6", {VARIABLE_WIDTH, 80.5, 100.5}, "axisMult6"}; ConfigurableAxis axisMult7{"axisMult7", {VARIABLE_WIDTH, 100.5, 120.5}, "axisMult7"}; ConfigurableAxis axisMult8{"axisMult8", {VARIABLE_WIDTH, 120.5, 140.5}, "axisMult8"}; ConfigurableAxis axisMult9{"axisMult9", {VARIABLE_WIDTH, 140.5, 160.5}, "axisMult9"}; - ConfigurableAxis axisMult10{"axisMult10", {VARIABLE_WIDTH, 160.5, 280.5}, "axisMult10"}; + ConfigurableAxis axisMult10{"axisMult10", {VARIABLE_WIDTH, 160.5, 350.5}, "axisMult10"}; Configurable doMixedEvent{"doMixedEvent", true, "Fill mixed-event TRI correlations"}; Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per trigger event"}; + // Same multiplicity limits as in FoxWolframCorrelation (inclusive, same values in both tasks). + // They set how many mixing pools are active and are checked against axisMult1..10. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multip. limits (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multip. limits (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper + // Choose the pair selection for mixed-event correlations. - Configurable leadAssocMode{"leadAssocMode", 0, - "0: ALL i-j pairs (pT<3); 1: ALL i-j pairs (pT < 3, Eta constrain) 2: pTLead in (2,3) & pTAssoc < 2; 3: pTAssoc < pTLead"}; + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; // Register mixed-event pair counts and angular distributions. void init(InitContext const&) { + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + + // The binning policies are fixed objects, so check that each axisMultK matches [multLower-0.5, multUpper+0.5]. + const std::array, MaxMultBins> axisEdges = { + axisMult1.value, axisMult2.value, axisMult3.value, axisMult4.value, axisMult5.value, + axisMult6.value, axisMult7.value, axisMult8.value, axisMult9.value, axisMult10.value}; + + pools = {&pairBIN1, &pairBIN2, &pairBIN3, &pairBIN4, &pairBIN5, + &pairBIN6, &pairBIN7, &pairBIN8, &pairBIN9, &pairBIN10}; + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; const AxisSpec axisEvent{10, 0.5, 10.5, "#Multip. Bin", "EventAxis"}; // Label mixed-event pair counts by multiplicity interval. histos.add("MixingPairs/MixingPairsNoLA", "MixingPairsNoLA", kTH1D, {axisEvent}, false); - auto hstatLA = histos.get(HIST("MixingPairs/MixingPairsNoLA")); - auto* hLA = hstatLA->GetXaxis(); - hLA->SetBinLabel(1, "bPairsLA_BIN1"); - hLA->SetBinLabel(2, "bPairsLA_BIN2"); - hLA->SetBinLabel(3, "bPairsLA_BIN3"); - hLA->SetBinLabel(4, "bPairsLA_BIN4"); - hLA->SetBinLabel(5, "bPairsLA_BIN5"); - hLA->SetBinLabel(6, "bPairsLA_BIN6"); - hLA->SetBinLabel(7, "bPairsLA_BIN7"); - hLA->SetBinLabel(8, "bPairsLA_BIN8"); - hLA->SetBinLabel(9, "bPairsLA_BIN9"); - hLA->SetBinLabel(10, "bPairsLA_BIN10"); + auto* mixingPairsNoLA = histos.get(HIST("MixingPairs/MixingPairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + mixingPairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("mixPairsLA_RANGE{}", i + 1).c_str() : fmt::format("mixPairsLA_BIN{}", i + 1).c_str()); + } // Register mixed-event correlation histograms for all intervals. - for (int i = 1; i <= 10; i++) { + for (int i = 1; i <= nMultBins; i++) { histos.add(fmt::format("Corelations/bTRI_BIN{}", i).c_str(), fmt::format("bTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); histos.add(fmt::format("Corelations/bProjTRI_BIN{}", i).c_str(), fmt::format("bProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); } @@ -1166,6 +1082,7 @@ struct FoxWolframCorrelationMixing { // Mix events in vertex and multiplicity intervals. using BinningType = ColumnBinningPolicy; + BinningType binningOnPosMultip1{{axisVtx, axisMult1}}; BinningType binningOnPosMultip2{{axisVtx, axisMult2}}; BinningType binningOnPosMultip3{{axisVtx, axisMult3}}; @@ -1189,6 +1106,10 @@ struct FoxWolframCorrelationMixing { SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cache_bin9}; SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cache_bin10}; + // Pointers to the pools so they can be processed in a loop (filled in init()). + using MixPair = SameKindPair; + std::array pools{}; + // Fill mixed-event distributions for a selected multiplicity interval. template void fillMixedEventBin(PairType& pair, int binIndex) @@ -1237,17 +1158,10 @@ struct FoxWolframCorrelationMixing { return; } - // Process all ten independent mixing pools. - fillMixedEventBin(pairBIN1, 1); - fillMixedEventBin(pairBIN2, 2); - fillMixedEventBin(pairBIN3, 3); - fillMixedEventBin(pairBIN4, 4); - fillMixedEventBin(pairBIN5, 5); - fillMixedEventBin(pairBIN6, 6); - fillMixedEventBin(pairBIN7, 7); - fillMixedEventBin(pairBIN8, 8); - fillMixedEventBin(pairBIN9, 9); - fillMixedEventBin(pairBIN10, 10); + // Process the active independent mixing pools. + for (int i = 0; i < nMultBins; ++i) { + fillMixedEventBin(*pools[i], i + 1); + } } }; From e1b125c8f32f2ee238d3c6a1c08d1e20b6832303 Mon Sep 17 00:00:00 2001 From: "Q.Y. Xia" <91366503+huinaibing@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:11:34 +0800 Subject: [PATCH 501/648] [PWGCF] Add charged differential flow for qa and resolve time association ownership (#18521) --- PWGCF/Flow/Tasks/pidFlowPtCorr.cxx | 263 +++++++++++++++++++++++++---- 1 file changed, 227 insertions(+), 36 deletions(-) diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index 4b9427ff61e..5189ecd236d 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -106,10 +107,11 @@ DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, int); DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, int); DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); +// Raw public-associator count, before PV locking or closest-target selection. DECLARE_SOA_COLUMN(NumCompatibleCollisions, numCompatibleCollisions, unsigned int); } // namespace pidflowtimetrack -// One row per accepted (target collision, source track), ordered by collision. +// At most one accepted target per source track, ordered by collision. // Kinematics/DCA belong to the target vertex; detector quality belongs to the source. // Baseline selection is applied here; analysis quality/pT cuts stay in the consumers. DECLARE_SOA_TABLE(PidFlowTimeTracks, "AOD", "PFTIMETRACK", @@ -290,12 +292,12 @@ T timeAssociationOption(InitContext& context, std::string const& task, std::stri } // namespace // Shared charged-only producer: public TrackAssoc supplies time compatibility; -// this task evaluates each pair at its target vertex once for flow, NUA and NUE. +// lock PV contributors to their original collision and select the closest-time +// candidate for other tracks, then evaluate the target for flow, NUA and NUE. struct PidFlowChargedTimeProducer { Produces timeTracks; // Original-vertex DCA/eta must not preselect candidates for another vertex. using SourceTracks = soa::Join; - Preslice timePairsPerCollision = aod::track_association::collisionId; Service ccdb{}; HistogramRegistry registry{"registry"}; OutputObj chargedTimeAssociationConfig{TNamed("ChargedTimeAssociationConfig", "disabled")}; @@ -307,7 +309,6 @@ struct PidFlowChargedTimeProducer { float timeL3Current = 0.f; float timeDipoleCurrent = 0.f; bool timeUniformField = false; - bool cfgRequireUniqueTimeAssociation = false; bool cfgTimeAssociationQA = true; int timeUsePVAssociation = aod::track_association::PVContrReassocOpt::OnlySameBc; int timeMaxPvContributorsForLowMultReassoc = 10; @@ -322,6 +323,7 @@ struct PidFlowChargedTimeProducer { LOGF(fatal, "Enable exactly one of processDummy and processTimeTracks in pid-flow-charged-time-producer"); } const bool needed = timeAssociationOption(context, "pid-flow-pt-corr", "processDataTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processChargedDifferentialTimeAssociation") || timeAssociationOption(context, "pid-flow-pt-corr", "fillCorrectionGraphTimeAssociation") || timeAssociationOption(context, "pid-flow-pt-corr", "processMCTimeAssociation") || timeAssociationOption(context, "pid-flow-pt-corr", "processTimeAssociationComparison"); @@ -331,7 +333,6 @@ struct PidFlowChargedTimeProducer { if (!doprocessTimeTracks) { return; } - cfgRequireUniqueTimeAssociation = timeAssociationOption(context, "pid-flow-pt-corr", "cfgRequireUniqueTimeAssociation"); cfgTimeAssociationQA = timeAssociationOption(context, "pid-flow-pt-corr", "cfgTimeAssociationQA"); timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); timeMaxPvContributorsForLowMultReassoc = timeAssociationOption(context, "track-to-collision-association", "maxPvContributorsForLowMultReassoc"); @@ -377,7 +378,7 @@ struct PidFlowChargedTimeProducer { selection->SetPtRange(ptMin, ptMax); selection->SetEtaRange(etaMin, etaMax); } - std::string metadata = "association=TrackAssoc;table=PidFlowTimeTracks;charged-only;PID=none;local-density=none;source=Tracks;propagation=target-DCA,USEMatCorrNONE;PV=not-refitted;unique=" + std::to_string(static_cast(cfgRequireUniqueTimeAssociation)); + std::string metadata = "association=TrackAssoc;table=PidFlowTimeTracks;charged-only;PID=none;local-density=none;source=Tracks;propagation=target-DCA,USEMatCorrNONE;PV=locked-original,not-refitted;nonPV=minimum-absolute-BC-corrected-deltaTime;tie=lowest-collisionId;selection=before-cuts,no-fallback;candidate-count=raw-TrackAssoc"; metadata += ";isRun3=" + std::to_string(static_cast(timeIsRun3)) + ";itsMatching=" + std::to_string(matching) + ";dcaSetup=" + std::to_string(dcaSetup) + ";compatibilityIU=" + std::to_string(static_cast(compatibilityIU)); metadata += ";pt=" + std::to_string(ptMin) + "," + std::to_string(ptMax) + ";eta=" + std::to_string(etaMin) + "," + std::to_string(etaMax); metadata += ";producer.nSigmaForTimeCompat=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat")); @@ -397,7 +398,7 @@ struct PidFlowChargedTimeProducer { registry.add("timeAssociation/candidatesVsAmbiguousBC", "AmbiguousTracks BC status (also for assigned tracks);N_{coll}^{comp};Ambiguous BC status", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); registry.add("timeAssociation/candidatesVsRawTimeStatus", "Stored track time/resolution before PV substitution;N_{coll}^{comp};Raw time status", {HistType::kTH2D, {{101, -0.5, 100.5}, {4, -0.5, 3.5}}}); registry.add("timeAssociation/candidatesVsOriginalTimeTest", "Fine time test at original collision; excludes coarse BC/search checks;N_{coll}^{comp};Original time test", {HistType::kTH2D, {{101, -0.5, 100.5}, {6, -0.5, 5.5}}}); - registry.add("timeAssociation/candidatesVsAcceptedTargets", "After unique cut, target vertex cut, propagation and baseline (before consumer cuts);N_{coll}^{comp};N accepted target collisions", {HistType::kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesVsAcceptedTargets", "Raw candidates vs accepted PV-locked/closest-time target after producer cuts;N_{coll}^{comp} (raw TrackAssoc);N accepted targets (0 or 1)", {HistType::kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); registry.add("timeAssociation/zeroCandidatePtEta", "Zero-candidate tracks with finite source pT/eta;Source p_{T} (GeV/c);Source #eta", {HistType::kTH2D, {{200, 0., 20.}, {100, -5., 5.}}}); const auto setLabels = [](TAxis* axis, auto const& names) { for (std::size_t bin = 0; bin < names.size(); ++bin) { @@ -412,8 +413,8 @@ struct PidFlowChargedTimeProducer { std::array{"finite time, res > 0", "finite time, res = 0", "finite time, res < 0", "nonfinite time or res"}); setLabels(registry.get(HIST("timeAssociation/candidatesVsOriginalTimeTest"))->GetYaxis(), std::array{"no original collision", "invalid effective time/res", "fails fine time test", "passes, original pair absent", "passes, original pair present", "invalid original collision index"}); - registry.add("timeAssociation/pairStatus", "Target-track pairs (stages overlap);stage;pairs", {HistType::kTH1D, {{9, 0, 9}}}); - const std::array labels{"producer pair", "duplicate pair", "nonunique rejected", "same collision", "different collision", "unassigned source", "propagation failed", "baseline rejected", "baseline accepted"}; + registry.add("timeAssociation/pairStatus", "Raw pairs and selected targets (target stages after vertex cut; stages overlap);stage;count", {HistType::kTH1D, {{10, 0, 10}}}); + const std::array labels{"producer pair", "duplicate pair", "same collision", "different collision", "unassigned source", "propagation failed", "baseline rejected", "baseline accepted", "PV locked target", "non-PV closest-time target"}; for (std::size_t bin = 0; bin < labels.size(); ++bin) { registry.get(HIST("timeAssociation/pairStatus"))->GetXaxis()->SetBinLabel(bin + 1, labels[bin]); } @@ -470,6 +471,12 @@ struct PidFlowChargedTimeProducer { if (index < 0 || index >= static_cast(tracks.size())) { LOGF(fatal, "TrackAssoc track index is outside the full source track table"); } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 0.5); + if (lastCollision[index] == pair.collisionId()) { + registry.fill(HIST("timeAssociation/pairStatus"), 1.5); + } + } // TrackAssoc is grouped by collision. Count distinct target collisions, // including those that will later fail event or target-track cuts. if (lastCollision[index] != pair.collisionId()) { @@ -496,7 +503,7 @@ struct PidFlowChargedTimeProducer { ambiguousBCStatus[index] = valid ? 2 : 1; } // One unweighted entry per source track, including zero candidates, before - // the unique-association cut or any target event/track selection. + // ownership resolution or any target event/track selection. for (auto const& track : tracks) { const int64_t index = track.globalIndex() - tracks.offset(); const auto count = counts[index]; @@ -549,37 +556,100 @@ struct PidFlowChargedTimeProducer { return counts; } - template - void fillChargedTimeTracks(TCollision const& collision, aod::TrackAssoc const& pairs, TTracks const& tracks, std::vector const& counts, std::vector& acceptedCounts) + template + std::vector> selectChargedTimeTargets(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) { - auto collisionPairs = pairs.sliceBy(timePairsPerCollision, collision.globalIndex()); - std::unordered_set seen; - bool fieldReady = false; - for (auto const& pair : collisionPairs) { - if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 0.5); + std::vector targetIndices(tracks.size(), -1); + std::vector referenceBC(tracks.size(), -1); + std::vector bestDistance(tracks.size(), std::numeric_limits::infinity()); + const auto globalBC = [&bcs](int64_t id) -> int64_t { + const int64_t index = id - bcs.offset(); + if (index < 0 || index >= static_cast(bcs.size())) { + return -1; } - if (!seen.insert(pair.trackId()).second) { - if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 1.5); - } + return static_cast(bcs.rawIteratorAt(index).globalBC()); + }; + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const int64_t original = track.collisionId() - collisions.offset(); + if (!track.has_collision() || original < 0 || original >= static_cast(collisions.size())) { continue; } + if (track.isPVContributor()) { + // A PV contributor defines this vertex. Restore its original pair even + // when the public time associator supplied zero candidates. + targetIndices[index] = original; + } else { + referenceBC[index] = globalBC(collisions.rawIteratorAt(original).bcId()); + } + } + // Unassigned tracks use the first BC of their first ambiguous row, exactly + // as in the public associator; never replace an invalid first row by another. + std::vector seenAmbiguous(tracks.size(), false); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || seenAmbiguous[index]) { + continue; + } + seenAmbiguous[index] = true; + auto const& ids = ambiguous.bcIds(); + if (!tracks.rawIteratorAt(index).has_collision() && ambiguous.has_bc() && ids[1] >= ids[0] && globalBC(ids[1]) >= 0) { + referenceBC[index] = globalBC(ids[0]); + } + } + for (auto const& pair : pairs) { const int64_t index = pair.trackId() - tracks.offset(); - if (cfgRequireUniqueTimeAssociation && counts[index] != 1) { - if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 2.5); - } + const int64_t target = pair.collisionId() - collisions.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || target < 0 || target >= static_cast(collisions.size())) { + LOGF(fatal, "TrackAssoc index is outside the full source track/collision tables"); + } + auto track = tracks.rawIteratorAt(index); + if (track.isPVContributor() || referenceBC[index] < 0) { + continue; + } + auto collision = collisions.rawIteratorAt(target); + const int64_t collisionBC = globalBC(collision.bcId()); + if (collisionBC < 0) { continue; } + // Compare in a common BC reference. Subtract the integer BCs before the + // floating-point conversion to retain precision for large global BCs. + const double deltaTime = static_cast(track.trackTime()) - collision.collisionTime() + + static_cast(referenceBC[index] - collisionBC) * o2::constants::lhc::LHCBunchSpacingNS; + const double distance = std::abs(deltaTime); + if (std::isfinite(distance) && (distance < bestDistance[index] || (distance == bestDistance[index] && target < targetIndices[index]))) { + targetIndices[index] = target; + bestDistance[index] = distance; + } + } + // Resolve ownership before any event/vertex, propagation or quality cuts. + // A rejected best target has no fallback; each source can appear only once. + std::vector> selected(collisions.size()); + for (std::size_t index = 0; index < targetIndices.size(); ++index) { + if (targetIndices[index] >= 0) { + selected[targetIndices[index]].push_back(index); + } + } + return selected; + } + + template + void fillChargedTimeTracks(TCollision const& collision, std::vector const& selected, TTracks const& tracks, std::vector const& counts, std::vector& acceptedCounts) + { + bool fieldReady = false; + for (const auto index : selected) { auto source = tracks.rawIteratorAt(index); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), source.isPVContributor() ? 8.5 : 9.5); + } TimeAssociatedChargedTrack candidate(source); const bool sameCollision = source.collisionId() == collision.globalIndex(); if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), sameCollision ? 3.5 : 4.5); + registry.fill(HIST("timeAssociation/pairStatus"), sameCollision ? 2.5 : 3.5); } if (cfgTimeAssociationQA && !source.has_collision()) { - registry.fill(HIST("timeAssociation/pairStatus"), 5.5); + registry.fill(HIST("timeAssociation/pairStatus"), 4.5); } if (!sameCollision) { if (!fieldReady) { @@ -590,7 +660,7 @@ struct PidFlowChargedTimeProducer { std::array dca{}; if (!o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, parameters, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dca)) { if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 6.5); + registry.fill(HIST("timeAssociation/pairStatus"), 5.5); } continue; } @@ -604,12 +674,12 @@ struct PidFlowChargedTimeProducer { const bool baseline = finite && (sameCollision ? (source.isGlobalTrack() || source.isGlobalTrackSDD()) : (timeGlobalSelection.IsSelected(candidate) || (!timeIsRun3 && timeSDDSelection.IsSelected(candidate)))); if (!baseline || std::fabs(candidate.eta()) >= cutEta) { if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 7.5); + registry.fill(HIST("timeAssociation/pairStatus"), 6.5); } continue; } if (cfgTimeAssociationQA) { - registry.fill(HIST("timeAssociation/pairStatus"), 8.5); + registry.fill(HIST("timeAssociation/pairStatus"), 7.5); ++acceptedCounts[index]; } timeTracks(collision.globalIndex(), source.globalIndex(), @@ -624,10 +694,11 @@ struct PidFlowChargedTimeProducer { { // Count all producer candidates before vertex, event or target-track cuts. auto counts = chargedTimeMultiplicity(pairs, sources, collisions, bcs, ambiguousTracks); + auto selected = selectChargedTimeTargets(pairs, sources, collisions, bcs, ambiguousTracks); std::vector acceptedCounts(cfgTimeAssociationQA ? sources.size() : 0, 0); for (auto const& collision : collisions) { if (std::fabs(collision.posZ()) < cutVertex) { - fillChargedTimeTracks(collision, pairs, sources, counts, acceptedCounts); + fillChargedTimeTracks(collision, selected[collision.globalIndex() - collisions.offset()], sources, counts, acceptedCounts); } } if (cfgTimeAssociationQA) { @@ -650,7 +721,6 @@ struct PidFlowPtCorr { Configurable cfgCutChi2prTPCcls{"cfgCutChi2prTPCcls", 2.5, "Chi2 per TPC clusters"}; Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; Configurable cfgUseMultiplicity{"cfgUseMultiplicity", false, "Use raw PV-contributor count in |eta| < 0.8 instead of centrality for output axes and NUE lookup; configure axisMultiplicity and disable cfgDoMultPVCut"}; - Configurable cfgRequireUniqueTimeAssociation{"cfgRequireUniqueTimeAssociation", false, "Time entries: require exactly one producer candidate collision before event/track cuts"}; Configurable cfgTimeAssociationQA{"cfgTimeAssociationQA", true, "Create and fill time-association producer, comparison and MC QA; keep event counters and flow/NUA/NUE outputs"}; ConfigurableAxis cfgaxisAssociationNch{"cfgaxisAssociationNch", {601, -5., 6005.}, "Association QA Nch (default bin width: 10 tracks; includes zero)"}; ConfigurableAxis cfgaxisAssociationChangedNch{"cfgaxisAssociationChangedNch", {1001, -0.5, 1000.5}, "Association QA lost/gained track count"}; @@ -717,6 +787,13 @@ struct PidFlowPtCorr { Configurable cfgEtaGap{"cfgEtaGap", 0.4, "Eta-gap half-width for flow particles; 0.4 selects (-etaMax,-0.4) and (0.4,etaMax)"}; Configurable cfgFlowNbootstrap{"cfgFlowNbootstrap", 30, "Number of subsamples for bootstrap"}; + struct : ConfigurableGroup { + std::string prefix = "chargedDifferentialOpts"; + Configurable cfgRefPtMin{"cfgRefPtMin", 0.2f, "Charged differential v2{2}: reference pT lower bound (GeV/c), independent of integrated flow"}; + Configurable cfgRefPtMax{"cfgRefPtMax", 3.0f, "Charged differential v2{2}: reference pT upper bound (GeV/c), independent of integrated flow"}; + ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}, "Charged differential v2{2}: POI pT binning and acceptance [first edge, last edge) in GeV/c"}; + } chargedDifferentialOpts; + // switch struct : ConfigurableGroup { std::string prefix = "switchsOpts"; @@ -859,6 +936,7 @@ struct PidFlowPtCorr { // val used for bootstrap TRandom3* fRndm = new TRandom3(0); OutputObj fFCCh{FlowContainer("FlowContainerCharged")}; + OutputObj fFCChDifferential{FlowContainer("FlowContainerChargedDifferential")}; OutputObj fFCPi{FlowContainer("FlowContainerPi")}; OutputObj fFCKa{FlowContainer("FlowContainerKa")}; OutputObj fFCPr{FlowContainer("FlowContainerPr")}; @@ -871,6 +949,17 @@ struct PidFlowPtCorr { // define global variables GFW* fGFW = new GFW(); // GFW class used from main src std::vector corrconfigs; + // Independent reference pT range and Q-vectors for charged differential v2{2}. + // Group runtime state to stay below the GCC DPL limit of 100 task members. + struct { + std::unique_ptr gfw; + std::unique_ptr ptAxis; + std::vector configs; + } chargedDifferential; + enum ChargedDifferentialMask { + kDifferentialReference = 1, + kDifferentialPOI = 2 + }; std::vector cfgAcceptance; std::vector cfgAcceptanceWithPt; @@ -943,6 +1032,7 @@ struct PidFlowPtCorr { funcFillCorrectionGraphTimeAssociation, funcProcessMCTimeAssociation, funcProcessTimeAssociationComparison, + funcProcessChargedDifferentialTimeAssociation, funcNumber }; @@ -1166,7 +1256,7 @@ struct PidFlowPtCorr { void init(InitContext& context) // Initialization { - const bool timeMode = doprocessDataTimeAssociation || dofillCorrectionGraphTimeAssociation || doprocessMCTimeAssociation || doprocessTimeAssociationComparison; + const bool timeMode = doprocessDataTimeAssociation || doprocessChargedDifferentialTimeAssociation || dofillCorrectionGraphTimeAssociation || doprocessMCTimeAssociation || doprocessTimeAssociationComparison; if (timeMode) { initChargedTimeAssociation(context); } @@ -1366,7 +1456,7 @@ struct PidFlowPtCorr { registry.addClone("hEventCount/processData", "hEventCount/processPidResponseMatrix"); if (timeMode) { auto originalCounter = registry.get(HIST("hEventCount/processData")); - for (auto const& name : {"hEventCount/processDataTimeAssociation", "hEventCount/fillCorrectionGraphTimeAssociation", "hEventCount/processMCTimeAssociation"}) { + for (auto const& name : {"hEventCount/processDataTimeAssociation", "hEventCount/fillCorrectionGraphTimeAssociation", "hEventCount/processMCTimeAssociation", "hEventCount/processChargedDifferentialTimeAssociation"}) { auto counter = registry.add(name, "Target collisions;selection;collisions", {HistType::kTH1D, {{15, 0, 15}}}); for (int bin = 1; bin <= originalCounter->GetNbinsX(); ++bin) { counter->GetXaxis()->SetBinLabel(bin, originalCounter->GetXaxis()->GetBinLabel(bin)); @@ -1519,6 +1609,51 @@ struct PidFlowPtCorr { } // end init fFCCh + if (doprocessChargedDifferentialTimeAssociation) { + const double refPtMin = chargedDifferentialOpts.cfgRefPtMin.value; + const double refPtMax = chargedDifferentialOpts.cfgRefPtMax.value; + const double etaMax = trkQualityOpts.cfgCutEta.value; + const double etaGap = cfgEtaGap.value; + if (!std::isfinite(refPtMin) || !std::isfinite(refPtMax) || refPtMin < 0. || refPtMax <= refPtMin || + !std::isfinite(etaMax) || !std::isfinite(etaGap) || etaGap < 0. || etaGap >= etaMax) { + LOGF(fatal, "Charged differential v2{2} requires 0 <= cfgRefPtMin < cfgRefPtMax and 0 <= cfgEtaGap < cfgCutEta"); + } + const AxisSpec ptAxis{chargedDifferentialOpts.cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + if (ptAxis.binEdges.size() < 2 || ptAxis.binEdges.front() < 0. || + std::any_of(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double edge) { return !std::isfinite(edge); }) || + std::adjacent_find(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double left, double right) { return left >= right; }) != ptAxis.binEdges.end() || + (ptAxis.nBins.has_value() && ptAxis.nBins.value() <= 0)) { + LOGF(fatal, "chargedDifferentialOpts.cfgaxisPt must define a finite, nonnegative, strictly increasing pT axis with at least one bin"); + } + if (ptAxis.nBins.has_value()) { + chargedDifferential.ptAxis = std::make_unique(ptAxis.nBins.value(), ptAxis.binEdges.front(), ptAxis.binEdges.back()); + } else { + chargedDifferential.ptAxis = std::make_unique(static_cast(ptAxis.binEdges.size()) - 1, ptAxis.binEdges.data()); + } + chargedDifferential.ptAxis->SetTitle("#it{p}_{T} (GeV/#it{c})"); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + chargedDifferential.gfw = std::make_unique(); + chargedDifferential.gfw->AddRegion("refN", -etaMax, -etaGap, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("refP", etaGap, etaMax, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("poiN", -etaMax, -etaGap, ptBins, kDifferentialPOI); + chargedDifferential.gfw->AddRegion("poiP", etaGap, etaMax, ptBins, kDifferentialPOI); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("refN {2} refP {-2}", "ChGap22", false)); + // Disjoint eta sides remove self-pairs even when POI and reference pT overlap. + // Both orientations contribute to the same profile with their own pair weight. + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiN {2} refP {-2}", "ChGap22", true)); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiP {2} refN {-2}", "ChGap22", true)); + chargedDifferential.gfw->CreateRegions(); + + TObjArray differentialProfiles; + differentialProfiles.SetOwner(true); + differentialProfiles.Add(new TNamed("ChGap22", "Reference-reference <2>, independent differential reference pT range")); + for (int bin = 1; bin <= ptBins; ++bin) { + differentialProfiles.Add(new TNamed(Form("ChGap22_pt_%d", bin), "Charged POI-reference <2> versus pT")); + } + fFCChDifferential->SetXAxis(chargedDifferential.ptAxis.get()); + initializeFlowContainer(fFCChDifferential, &differentialProfiles); + } + // init fFCPID // note that need to add c22pure and c32pure oba4PID->Add(new TNamed("c22pure", "c22pure")); @@ -2835,6 +2970,9 @@ struct PidFlowPtCorr { case MyFunctionName::funcProcessTimeAssociationComparison: registry.fill(HIST("hEventCount/processTimeAssociationComparison"), position); break; + case MyFunctionName::funcProcessChargedDifferentialTimeAssociation: + registry.fill(HIST("hEventCount/processChargedDifferentialTimeAssociation"), position); + break; default: // LOGF(warning, "could not find event count graph"); @@ -3668,7 +3806,7 @@ struct PidFlowPtCorr { setLabels(registry.get(HIST("associationComparison/hPtByCategory"))->GetYaxis(), categories); setLabels(registry.get(HIST("associationComparison/hEtaByCategory"))->GetYaxis(), categories); setLabels(registry.get(HIST("associationComparison/hPhiByCategory"))->GetYaxis(), categories); - LOG(info) << "Association comparison: " << selection << "; original=collisionId, time=PidFlowTimeTracks; unique=" << cfgRequireUniqueTimeAssociation.value; + LOG(info) << "Association comparison: " << selection << "; original=collisionId, time=PidFlowTimeTracks; PV=locked-original, nonPV=closest-time"; } // Evaluate both associations in the same event loop without PID or correction @@ -3868,6 +4006,59 @@ struct PidFlowPtCorr { } PROCESS_SWITCH(PidFlowPtCorr, processDataTimeAssociation, "Charged flow and mean-pT using time-based TrackAssoc; no PID", false); + // Store raw ensemble correlators, not event-by-event normalized v2. + // FlowContainer: SetIDName("ChGap"), then GetVN2VsPt(2, centMin, centMax). + void processChargedDifferentialTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessChargedDifferentialTimeAssociation) || tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessChargedDifferentialTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const double rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + chargedDifferential.gfw->Clear(); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track)) { + continue; + } + const double pt = track.pt(); + const bool isReference = pt > chargedDifferentialOpts.cfgRefPtMin.value && pt < chargedDifferentialOpts.cfgRefPtMax.value; + const int ptBin = chargedDifferential.ptAxis->FindFixBin(pt) - 1; + const bool isPOI = ptBin >= 0 && ptBin < ptBins; + if (!isReference && !isPOI) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, collision.posZ()); + const double weight = static_cast(weff) * wacc; + if (isReference) { + chargedDifferential.gfw->Fill(track.eta(), 0, track.phi(), weight, kDifferentialReference); + } + if (isPOI) { + chargedDifferential.gfw->Fill(track.eta(), ptBin, track.phi(), weight, kDifferentialPOI); + } + } + for (auto const& config : chargedDifferential.configs) { + const int bins = config.pTDif ? ptBins : 1; + for (int bin = 0; bin < bins; ++bin) { + const double pairs = chargedDifferential.gfw->Calculate(config, bin, true).real(); + if (!std::isfinite(pairs) || pairs <= 0.) { + continue; + } + const double correlator = chargedDifferential.gfw->Calculate(config, bin, false).real() / pairs; + if (std::isfinite(correlator)) { + const std::string name = config.pTDif ? config.Head + "_pt_" + std::to_string(bin + 1) : config.Head; + fFCChDifferential->FillProfile(name.c_str(), cent, correlator, pairs, rndm); + } + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processChargedDifferentialTimeAssociation, "Charged differential v2{2}(pT) using time-based TrackAssoc; independent reference pT and POI bins, no PID", false); + void fillCorrectionGraphTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) { if (!chargedTimeEventSelected(collision, funcFillCorrectionGraphTimeAssociation)) { From 1a48c0580b4eb2ef8ac42e39b902b3982239ccc3 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:27:03 +0100 Subject: [PATCH 502/648] [PWGCF] Fix code check errors in femto framework (#18518) --- PWGCF/Femto/Core/baseSelection.h | 2 +- PWGCF/Femto/Core/collisionBuilder.h | 2 +- PWGCF/Femto/Core/mcBuilder.h | 2 +- PWGCF/Femto/Core/trackBuilder.h | 19 +- PWGCF/Femto/Core/trackHistManager.h | 1 + PWGCF/Femto/TableProducer/femtoProducer.cxx | 365 ++++++++------- .../femtoProducerDerivedToDerived.cxx | 439 +++++++++--------- 7 files changed, 433 insertions(+), 397 deletions(-) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 3e58511914b..a477594bd61 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -292,7 +292,7 @@ class BaseSelection /// \brief Evaluate a single observable against its configured selections. /// \param observableIndex Index of the observable. /// \param values Vector of values of the observable. - void evaluateObservable(int observableIndex, std::vector values) + void evaluateObservable(int observableIndex, std::vector const& values) { // if there are no selections configured, bail out if (mSelectionContainers.at(observableIndex).isEmpty()) { diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 344bc562316..03bda34b671 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -334,7 +334,7 @@ class CollisionSelection : public baseselection::BaseSelection - void setSphericity(T tracks) + void setSphericity(T const& tracks) { mSphericity = computeSphericity(tracks); } diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 6727bbb0f47..d05820904a2 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -556,7 +556,7 @@ class McBuilder int64_t mcMotherRow = -1; if (mProduceMcMothers && mcParticle.has_mothers()) { auto mothers = mcParticle.template mothers_as(); - auto motherParticle = mothers.front(); + auto const& motherParticle = mothers.front(); auto mcMotherIndex = motherParticle.globalIndex(); auto itM = mMcMotherMap.find(mcMotherIndex); diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index c98ec68c8e4..9acba874559 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -27,8 +27,12 @@ #include #include #include +#include #include +#include + +#include #include #include #include @@ -684,10 +688,17 @@ class TrackBuilder auto hSlotAssignment = registry->get(HIST("hPuritySlotAssignment")); for (int i = 0; i < PuritySpeciesLast; i++) { std::string bitPosition = mPuritySlot[i] >= 0 ? std::to_string(mPuritySlot[i]) : "X"; - std::string value = mPuritySlot[i] >= 0 ? ("Slot " + std::to_string(mPuritySlot[i])) : "Not configured"; - std::string label = std::string("SelectionName___") + PuritySpeciesName[i] + - ":::MinimalCut___0:::OptionalCut___0:::BitPosition___" + bitPosition + - ":::Value___" + value; + std::string value = "Not configured"; + if (mPuritySlot[i] >= 0) { + value = "Slot "; + value += std::to_string(mPuritySlot[i]); + } + std::string label = "SelectionName___"; + label += PuritySpeciesName[i]; + label += ":::MinimalCut___0:::OptionalCut___0:::BitPosition___"; + label += bitPosition; + label += ":::Value___"; + label += value; hSlotAssignment->GetXaxis()->SetBinLabel(i + 1, label.c_str()); hSlotAssignment->Fill(i, 1); } diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index b02fc512f4e..296f11ab1aa 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 5b0f7beba3d..41922a24837 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -100,14 +100,38 @@ using Run3McGenParticles = o2::aod::McParticles; struct FemtoProducer { - o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; - o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; - o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; - o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; - o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; - o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; - o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; - o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; + // all Preslice declarations, grouped into a single data member (see o2::framework::PresliceGroup's + // own doc comment) to stay well under the 100-direct-data-member cap on task structs + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; + o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; + o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; + o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; + } preslices; + + // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ + // Preslice/Produces, so the framework never needs to discover them individually -- free to nest) + struct { + collisionbuilder::CollisionBuilder collisionBuilder; + trackbuilder::TrackBuilder trackBuilder; + v0builder::V0Builder k0shortBuilder; + v0builder::V0Builder lambdaBuilder; + v0builder::V0Builder antilambdaBuilder; + photonbuilder::PhotonBuilder photonBuilder; + charmhadronbuilder::CharmHadronBuilder d0Builder; + charmhadronbuilder::CharmHadronBuilder d0barBuilder; + charmhadronbuilder::CharmHadronBuilder lcBuilder; + charmhadronbuilder::CharmHadronBuilder lcBarBuilder; + cascadebuilder::CascadeBuilder xiBuilder; + cascadebuilder::CascadeBuilder omegaBuilder; + kinkbuilder::KinkBuilder sigmaBuilder; + kinkbuilder::KinkBuilder sigmaPlusBuilder; + mcbuilder::McBuilder mcBuilder; + } builders; // ccdb config collisionbuilder::ConfCcdb confCcdb; @@ -118,12 +142,10 @@ struct FemtoProducer { collisionbuilder::ConfCollisionFilters confCollisionFilters; collisionbuilder::ConfCollisionBits confCollisionBits; collisionbuilder::ConfCollisionRctFlags confCollisionRctFlags; - collisionbuilder::CollisionBuilder collisionBuilder; // track builder trackbuilder::TrackBuilderProducts trackBuilderProducts; trackbuilder::ConfTrackTables confTrackTables; - trackbuilder::TrackBuilder trackBuilder; trackbuilder::ConfTrackBits confTrackBits; trackbuilder::ConfTrackFilters confTrackFilters; @@ -132,17 +154,13 @@ struct FemtoProducer { v0builder::ConfV0Tables confV0Tables; v0builder::ConfV0Filters confV0Filters; v0builder::ConfK0shortBits confK0shortBits; - v0builder::V0Builder k0shortBuilder; v0builder::ConfLambdaBits confLambdaBits; - v0builder::V0Builder lambdaBuilder; - v0builder::V0Builder antilambdaBuilder; // photon (PCM) builder photonbuilder::PhotonBuilderProducts photonBuilderProducts; photonbuilder::ConfPhotonTables confPhotonTables; photonbuilder::ConfPhotonFilters confPhotonFilters; photonbuilder::ConfPhotonBits confPhotonBits; - photonbuilder::PhotonBuilder photonBuilder; // charm hadron builder charmhadronbuilder::CharmHadronBuilderProducts charmHadronBuilderProducts; @@ -150,34 +168,25 @@ struct FemtoProducer { charmhadronbuilder::ConfCharmHadronTables confCharmHadronTables; charmhadronbuilder::ConfD0Bits confD0Bits; charmhadronbuilder::ConfLcBits confLcBits; - charmhadronbuilder::CharmHadronBuilder d0Builder; - charmhadronbuilder::CharmHadronBuilder d0barBuilder; - charmhadronbuilder::CharmHadronBuilder lcBuilder; - charmhadronbuilder::CharmHadronBuilder lcBarBuilder; // cascade builder cascadebuilder::CascadeBuilderProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTables confCascadeTables; cascadebuilder::ConfCascadeFilters confCascadeFilters; cascadebuilder::ConfXiBits confXiBits; - cascadebuilder::CascadeBuilder xiBuilder; cascadebuilder::ConfOmegaBits confOmegaBits; - cascadebuilder::CascadeBuilder omegaBuilder; // kink builder kinkbuilder::KinkBuilderProducts kinkBuilderProducts; kinkbuilder::ConfKinkTables confKinkTables; kinkbuilder::ConfKinkFilters confKinkFilters; kinkbuilder::ConfSigmaBits confSigmaBits; - kinkbuilder::KinkBuilder sigmaBuilder; kinkbuilder::ConfSigmaPlusBits confSigmaPlusBits; - kinkbuilder::KinkBuilder sigmaPlusBuilder; // mc builder mcbuilder::ConfMc confMc; mcbuilder::ConfMcTables confMcTables; mcbuilder::McBuilderProducts mcProducts; - mcbuilder::McBuilder mcBuilder; // histogramming o2::framework::HistogramRegistry hRegistry{"FemtoProducer", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -198,34 +207,34 @@ struct FemtoProducer { // ---- builders ----------------------------------------------------------- // Order matters here: CollisionBuilder must init before TrackBuilder (which checks - // collisionBuilder.producingCollisions()/producingLiteCollisions()), and TrackBuilder + // builders.collisionBuilder.producingCollisions()/producingLiteCollisions()), and TrackBuilder // must init before the V0/Cascade/Kink builders (which check - // trackBuilder.producingTracks()/producingLiteTracks()). - collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); - trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context, collisionBuilder); + // builders.trackBuilder.producingTracks()/producingLiteTracks()). + builders.collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); + builders.trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context, builders.collisionBuilder); - k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context, trackBuilder); - lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); - antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); + builders.k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); - photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); + builders.photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); - d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); - d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + builders.d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + builders.d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); - lcBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); - lcBarBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + builders.lcBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + builders.lcBarBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); - sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, trackBuilder); - sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, trackBuilder); + builders.sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); - xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, trackBuilder); - omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, trackBuilder); + builders.xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); + builders.omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); - mcBuilder.init(confMc, confMcTables, context); + builders.mcBuilder.init(confMc, confMcTables, context); // ---- guard: enabled tables vs. enabled process function ------------------ - if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && + if ((builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && @@ -234,7 +243,7 @@ struct FemtoProducer { !doprocessTracksV0sCascadesRun3PbPbMc)) { LOG(fatal) << "At least one cascade table is enabled, but wrong process function is enabled. Breaking..."; } - if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && + if ((builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || builders.k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && @@ -243,27 +252,27 @@ struct FemtoProducer { !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } - if (photonBuilder.fillAnyTable() && + if (builders.photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape)) { LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; } - if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && + if ((builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable()) && (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one kink table is enabled, but wrong process function is enabled. Breaking..."; } - if ((d0Builder.fillAnyTable() || d0barBuilder.fillAnyTable()) && + if ((builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable()) && (!doprocessTracksD0sRun3pp && !doprocessTracksD0sRun3PbPb && !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc)) { LOG(fatal) << "At least one d0 table is enabled, but wrong process function is enabled. Breaking..."; } - if ((lcBuilder.fillAnyTable() || lcBarBuilder.fillAnyTable()) && + if ((builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable()) && (!doprocessTracksLcsRun3pp && !doprocessTracksLcsRun3PbPb && !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc)) { LOG(fatal) << "At least one lc table is enabled, but wrong process function is enabled. Breaking..."; } - if (mcBuilder.fillAnyTable() && + if (builders.mcBuilder.fillAnyTable() && (!doprocessTracksRun3ppMc && !doprocessTracksRun3PbPbMc && !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && @@ -283,21 +292,21 @@ struct FemtoProducer { } }; // substitute the real accessors if they live on the builders rather than the conf groups - add("collision", collisionBuilder.fillAnyTable(), collisionBuilder.isPassThrough()); - add("track", trackBuilder.fillAnyTable(), trackBuilder.isPassThrough()); - add("k0short", k0shortBuilder.fillAnyTable(), k0shortBuilder.isPassThrough()); - add("lambda", lambdaBuilder.fillAnyTable(), lambdaBuilder.isPassThrough()); - add("antilambda", antilambdaBuilder.fillAnyTable(), antilambdaBuilder.isPassThrough()); - add("photon", photonBuilder.fillAnyTable(), photonBuilder.isPassThrough()); - add("d0", d0Builder.fillAnyTable(), d0Builder.isPassThrough()); - add("d0bar", d0barBuilder.fillAnyTable(), d0barBuilder.isPassThrough()); - add("lc", lcBuilder.fillAnyTable(), lcBuilder.isPassThrough()); - add("lcbar", lcBarBuilder.fillAnyTable(), lcBarBuilder.isPassThrough()); - add("sigma", sigmaBuilder.fillAnyTable(), sigmaBuilder.isPassThrough()); - add("sigmaplus", sigmaPlusBuilder.fillAnyTable(), sigmaPlusBuilder.isPassThrough()); - add("xi", xiBuilder.fillAnyTable(), xiBuilder.isPassThrough()); - add("omega", omegaBuilder.fillAnyTable(), omegaBuilder.isPassThrough()); - add("mc", mcBuilder.fillAnyTable(), mcBuilder.isPassThrough()); + add("collision", builders.collisionBuilder.fillAnyTable(), builders.collisionBuilder.isPassThrough()); + add("track", builders.trackBuilder.fillAnyTable(), builders.trackBuilder.isPassThrough()); + add("k0short", builders.k0shortBuilder.fillAnyTable(), builders.k0shortBuilder.isPassThrough()); + add("lambda", builders.lambdaBuilder.fillAnyTable(), builders.lambdaBuilder.isPassThrough()); + add("antilambda", builders.antilambdaBuilder.fillAnyTable(), builders.antilambdaBuilder.isPassThrough()); + add("photon", builders.photonBuilder.fillAnyTable(), builders.photonBuilder.isPassThrough()); + add("d0", builders.d0Builder.fillAnyTable(), builders.d0Builder.isPassThrough()); + add("d0bar", builders.d0barBuilder.fillAnyTable(), builders.d0barBuilder.isPassThrough()); + add("lc", builders.lcBuilder.fillAnyTable(), builders.lcBuilder.isPassThrough()); + add("lcbar", builders.lcBarBuilder.fillAnyTable(), builders.lcBarBuilder.isPassThrough()); + add("sigma", builders.sigmaBuilder.fillAnyTable(), builders.sigmaBuilder.isPassThrough()); + add("sigmaplus", builders.sigmaPlusBuilder.fillAnyTable(), builders.sigmaPlusBuilder.isPassThrough()); + add("xi", builders.xiBuilder.fillAnyTable(), builders.xiBuilder.isPassThrough()); + add("omega", builders.omegaBuilder.fillAnyTable(), builders.omegaBuilder.isPassThrough()); + add("mc", builders.mcBuilder.fillAnyTable(), builders.mcBuilder.isPassThrough()); const bool anyPassThrough = std::any_of(passThroughFlags.begin(), passThroughFlags.end(), [](const auto& f) { return f.second; }); @@ -318,19 +327,19 @@ struct FemtoProducer { // in mc pass-through every generated collision is written, but reco collisions of other // sub-generators are still rejected in checkCollision -> biased event-loss denominator - if (mcBuilder.isPassThrough() && collisionBuilder.isPassThrough() && collisionBuilder.subGeneratorId() >= 0) { + if (builders.mcBuilder.isPassThrough() && builders.collisionBuilder.isPassThrough() && builders.collisionBuilder.subGeneratorId() >= 0) { LOG(warn) << "MC and collision pass-through are enabled, but CollisionFilter.subGeneratorId = " - << collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " + << builders.collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " << "while all generated collisions are written. Set it to -1 for unbiased event/signal-loss corrections."; } // daughter tracks can be written a second time under the collision of their mother candidate; // without FTrackExtras (fillType) these rows cannot be told apart from selected tracks downstream - const bool anyDaughterBuilder = k0shortBuilder.fillAnyTable() || lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || - xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable() || - sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable() || - d0Builder.fillAnyTable() || d0barBuilder.fillAnyTable() || lcBuilder.fillAnyTable() || lcBarBuilder.fillAnyTable(); - if (trackBuilder.isPassThrough() && anyDaughterBuilder && !trackBuilder.producingTrackExtras()) { + const bool anyDaughterBuilder = builders.k0shortBuilder.fillAnyTable() || builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || + builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable() || + builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable() || + builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable() || builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable(); + if (builders.trackBuilder.isPassThrough() && anyDaughterBuilder && !builders.trackBuilder.producingTrackExtras()) { LOG(warn) << "Track pass-through is enabled together with candidates that register daughter tracks, but FTrackExtras is not produced. " << "Daughter-only track rows cannot be identified downstream (enable TrackTables.produceTrackExtras)."; } @@ -381,17 +390,17 @@ struct FemtoProducer { template bool processCollisions(T1 const& col, T2 const& /* bcs*/, T3 const& tracks) { - collisionBuilder.reset(); + builders.collisionBuilder.reset(); auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col)) { return false; } // Normally the collision row is written lazily, once a candidate is found. In // pass-through we need every accepted collision, including those with no candidate, // otherwise event-normalised observables lose exactly those events. - if (collisionBuilder.isPassThrough()) { - collisionBuilder.fillCollision(collisionBuilderProducts, col); + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillCollision(collisionBuilderProducts, col); } return true; } @@ -399,21 +408,21 @@ struct FemtoProducer { template bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks) { - collisionBuilder.reset(); - // mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function + builders.collisionBuilder.reset(); + // builders.mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function // (both pass-through and lazy-fill mode); resetting here on every reco collision would // wipe the dedup maps and duplicate every mc collision/particle referenced by more than // one reco collision (e.g. split-vertex reconstruction under pileup) auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col, mcCols)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col, mcCols)) { return false; } // NOTE: must be fillMcCollision, not fillCollision - fillMcCollision returns early on // mCollisionAlreadyFilled, so filling the collision first would permanently skip // the collision label - if (collisionBuilder.isPassThrough()) { - collisionBuilder.fillMcCollision(collisionBuilderProducts, col, mcCols, mcProducts, mcBuilder); + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillMcCollision(collisionBuilderProducts, col, mcCols, mcProducts, builders.mcBuilder); } return true; } @@ -422,95 +431,95 @@ struct FemtoProducer { template void processTracks(T1 const& col, T2 const& tracksWithItsPid) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillTracks(col, collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); + builders.trackBuilder.reset(tracksWithItsPid); + builders.trackBuilder.fillTracks(col, builders.collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); } template void processMcTracks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& mcParticles) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillMcTracks(col, collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, mcBuilder, mcProducts); + builders.trackBuilder.reset(tracksWithItsPid); + builders.trackBuilder.fillMcTracks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, builders.mcBuilder, mcProducts); } // processing v0s template void processV0s(T1 const& col, T2 const& tracks, T3 const& v0s) { - lambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); - antilambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); - k0shortBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder); + builders.lambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.antilambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.k0shortBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); } template void processMcV0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& v0s, T5 const& mcParticles) { - lambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - antilambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - k0shortBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.lambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.antilambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.k0shortBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing photons (PCM) template void processPhotons(T1 const& col, T2 const& photons, T3 const& v0legs) { - photonBuilder.fillPhotons(col, collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); + builders.photonBuilder.fillPhotons(col, builders.collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); } // processing kinks template void processKinks(T1 const& col, T2 const& tracks, T3 const& kinks) { - sigmaBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder); - sigmaPlusBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder); + builders.sigmaBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); + builders.sigmaPlusBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); } template void processMcKinks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& kinks, T5 const& mcParticles) { - sigmaBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - sigmaPlusBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.sigmaBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.sigmaPlusBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing cascades template void processCascades(T1 const& col, T2 const& tracks, T3 const& cascades) { - xiBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, trackBuilder); - omegaBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, trackBuilder); + builders.xiBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); + builders.omegaBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); } template void processMcCascades(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& cascades, T5 const& mcParticles) { - xiBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - omegaBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.xiBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.omegaBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing D0s template void processD0s(T1 const& col, T2 const& tracks, T3 const& candidates) { - d0Builder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); - d0barBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); + builders.d0Builder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.d0barBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); } template void processMcD0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) { - d0Builder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - d0barBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.d0Builder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.d0barBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing Lcs template void processLcs(T1 const& col, T2 const& tracks, T3 const& candidates) { - lcBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); - lcBarBuilder.fillCharmHadrons(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder); + builders.lcBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.lcBarBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); } template void processMcLcs(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) { - lcBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); - lcBarBuilder.fillMcCharmHadrons(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.lcBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.lcBarBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // ========================================================================== @@ -585,7 +594,7 @@ struct FemtoProducer { processTracks(col, tracksWithItsPid); // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); @@ -602,7 +611,7 @@ struct FemtoProducer { auto tracksWithItsPid = o2::soa::Attach(tracks); processTracks(col, tracksWithItsPid); - auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); @@ -691,7 +700,7 @@ struct FemtoProducer { processCascades(col, tracks, cascades); // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesPhotonsRun3PbPbWithEventShape, "Provide tracks, v0s, cascades and PCM photons in PbPb collisions with event shape information", false); @@ -750,7 +759,7 @@ struct FemtoProducer { processKinks(col, tracks, kinks); // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksPhotonsRun3pp, "Provide tracks, v0s, cascades, kinks and PCM photons", false); @@ -804,13 +813,13 @@ struct FemtoProducer { rawinputs::Run3McRecoTracks const& tracks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -827,13 +836,13 @@ struct FemtoProducer { rawinputs::Run3McRecoTracks const& tracks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -851,13 +860,13 @@ struct FemtoProducer { rawinputs::Run3RecoVzeros const& v0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -865,7 +874,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); } } @@ -878,13 +887,13 @@ struct FemtoProducer { rawinputs::Run3RecoVzeros const& v0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -892,7 +901,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); } } @@ -906,13 +915,13 @@ struct FemtoProducer { rawinputs::Run3RecoCascades const& cascades, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -920,10 +929,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto cascadesThisCol = cascades.sliceBy(perColRecoCascades, col.globalIndex()); + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); } } @@ -937,13 +946,13 @@ struct FemtoProducer { rawinputs::Run3RecoCascades const& cascades, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -951,10 +960,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto cascadesThisCol = cascades.sliceBy(perColRecoCascades, col.globalIndex()); + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); } } @@ -967,13 +976,13 @@ struct FemtoProducer { rawinputs::Run3Kinks const& kinks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -981,7 +990,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto kinksThisCol = kinks.sliceBy(perColRecoKinks, col.globalIndex()); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } } @@ -995,13 +1004,13 @@ struct FemtoProducer { rawinputs::Run3Kinks const& kinks, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1009,10 +1018,10 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto v0sThisCol = v0s.sliceBy(perColRecoV0s, col.globalIndex()); + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); - auto kinksThisCol = kinks.sliceBy(perColRecoKinks, col.globalIndex()); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } } @@ -1025,13 +1034,13 @@ struct FemtoProducer { rawinputs::Run3RecoD0s const& d0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1039,7 +1048,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto d0sThisCol = d0s.sliceBy(perColRecoD0s, col.globalIndex()); + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); } } @@ -1052,13 +1061,13 @@ struct FemtoProducer { rawinputs::Run3RecoD0s const& d0s, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1066,7 +1075,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto d0sThisCol = d0s.sliceBy(perColRecoD0s, col.globalIndex()); + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); } } @@ -1109,13 +1118,13 @@ struct FemtoProducer { rawinputs::Run3RecoLcs const& lcs, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1123,7 +1132,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto lcsThisCol = lcs.sliceBy(perColRecoLcs, col.globalIndex()); + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); } } @@ -1136,13 +1145,13 @@ struct FemtoProducer { rawinputs::Run3RecoLcs const& lcs, rawinputs::Run3McGenParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); - if (mcBuilder.isPassThrough()) { - mcBuilder.fillMcPassThrough(mcCols, mcParticles, perMcCollision, mcProducts); + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } for (const auto& col : cols) { - auto tracksThisCol = tracks.sliceBy(perColRecoTracks, col.globalIndex()); + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { continue; } @@ -1150,7 +1159,7 @@ struct FemtoProducer { o2::aod::pidits::ITSNSigmaPr, o2::aod::pidits::ITSNSigmaDe, o2::aod::pidits::ITSNSigmaTr, o2::aod::pidits::ITSNSigmaHe>(tracksThisCol); processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); - auto lcsThisCol = lcs.sliceBy(perColRecoLcs, col.globalIndex()); + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); } } @@ -1163,12 +1172,12 @@ struct FemtoProducer { // do not preslice the mcParticles table passed to fillMcParticle, otherwise the finding of the partonic mother can fail void processMcOnly(o2::aod::McCollisions const& mcCols, o2::aod::McParticles const& mcParticles) { - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (const auto& mcCol : mcCols) { - auto particlesThisCollision = mcParticles.sliceBy(perMcCollision, mcCol.globalIndex()); - mcBuilder.fillMcCollision(mcCol, particlesThisCollision, mcProducts, pdgDb); + auto particlesThisCollision = mcParticles.sliceBy(preslices.perMcCollision, mcCol.globalIndex()); + builders.mcBuilder.fillMcCollision(mcCol, particlesThisCollision, mcProducts, pdgDb); for (const auto& mcParticle : particlesThisCollision) { - mcBuilder.fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); + builders.mcBuilder.fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); } } } diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index b8cbdd436f1..a2bc14986b2 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -33,135 +33,150 @@ using namespace o2::analysis::femto; -struct FemtoProducerDerivedToDerived { +// kept outside the task struct on purpose: the struct is reflected via a brace-decomposition +// mechanism capped at 100 direct data members (see o2::framework::PresliceGroup for the same +// concern applied to Preslice members below); type aliases don't need to live inside the struct +// at all, so moving them here keeps that budget free for actual data members. +namespace femtotables +{ +using FemtoCollisions = o2::soa::Join; + +using FilteredFemtoCollisions = o2::soa::Filtered; +using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + +using FemtoMcCollisions = o2::soa::Join; +using FilteredFemtoMcCollisions = o2::soa::Filtered; + +using FemtoTracks = o2::soa::Join; +using FemtoTracksWithMass = o2::soa::Join; +// opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- +// needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled +using FemtoTracksWithPurity = o2::soa::Join; +using FemtoLambdas = o2::soa::Join; +using FemtoK0shorts = o2::soa::Join; +using FemtoRho0s = o2::soa::Join; +using FemtoPhis = o2::soa::Join; +using FemtoXis = o2::soa::Join; +using FemtoOmegas = o2::soa::Join; +using FemtoSigma = o2::soa::Join; +using FemtoSigmaPlus = o2::soa::Join; +using FemtoSigmaPlusExtended = o2::soa::Join; + +using FemtoMcTracks = o2::soa::Join; +using FemtoMcSigma = o2::soa::Join; +using FemtoMcSigmaPlus = o2::soa::Join; +using FemtoMcSigmaPlusExtended = o2::soa::Join; +using FemtoMcParticles = o2::soa::Join; +} // namespace femtotables - // setup tables - using FemtoCollisions = o2::soa::Join; - - using FilteredFemtoCollisions = o2::soa::Filtered; - using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; - - using FemtoMcCollisions = o2::soa::Join; - using FilteredFemtoMcCollisions = o2::soa::Filtered; - - using FemtoTracks = o2::soa::Join; - using FemtoTracksWithMass = o2::soa::Join; - // opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- - // needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled - using FemtoTracksWithPurity = o2::soa::Join; - using FemtoLambdas = o2::soa::Join; - using FemtoK0shorts = o2::soa::Join; - using FemtoRho0s = o2::soa::Join; - using FemtoPhis = o2::soa::Join; - using FemtoXis = o2::soa::Join; - using FemtoOmegas = o2::soa::Join; - using FemtoSigma = o2::soa::Join; - using FemtoSigmaPlus = o2::soa::Join; - using FemtoSigmaPlusExtended = o2::soa::Join; - - using FemtoMcTracks = o2::soa::Join; - using FemtoMcSigma = o2::soa::Join; - using FemtoMcSigmaPlus = o2::soa::Join; - using FemtoMcSigmaPlusExtended = o2::soa::Join; - using FemtoMcParticles = o2::soa::Join; +struct FemtoProducerDerivedToDerived { o2::framework::SliceCache cache; + // all fColId Preslice declarations, grouped into a single data member + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithPurity = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + } preslices; + + // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ + // Preslice/Produces, so the framework never needs to discover them individually -- free to nest) + struct { + collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; + trackbuilder::TrackBuilderDerivedToDerived trackBuilder; + v0builder::V0BuilderDerivedToDerived v0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerived resonanceBuilder; + kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; + cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; + mcbuilder::McBuilderDerivedToDerived mcBuilder; + } builders; + // collision builder collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); collisionbuilder::CollisionBuilderDerivedToDerivedProducts collisionBuilderProducts; - collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; // track builder - trackbuilder::TrackBuilderDerivedToDerived trackBuilder; trackbuilder::TrackBuilderDerivedToDerivedProducts trackBuilderProducts; trackbuilder::ConfTrackTablesDerivedToDerived confTrackBuilder; trackbuilder::ConfTrackSelection1 trackSelections1; trackbuilder::ConfTrackSelection2 trackSelections2; - o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); - o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); - o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); + o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); - o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColTracksWithPurity = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(trackSelections2); // v0 builder - v0builder::V0BuilderDerivedToDerived v0Builder; v0builder::V0BuilderDerivedToDerivedProducts v0BuilderProducts; v0builder::ConfV0TablesDerivedToDerived confV0Builder; v0builder::ConfLambdaSelection1 lambdaSelection1; - o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); - o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); v0builder::ConfK0shortSelection1 k0shortSelection1; - o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); - o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); // two-track resonance builder - twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerived resonanceBuilder; twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerivedProducts resonanceBuilderProducts; twotrackresonancebuilder::ConfTwoTrackResonanceTablesDerivedToDerived confResonanceBuilder; twotrackresonancebuilder::ConfRho0Selection rho0Selection; - o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); - o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); twotrackresonancebuilder::ConfPhiSelection phiSelection; - o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); - o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); // kink builder - kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; kinkbuilder::KinkBuilderDerivedToDerivedProducts kinkBuilderProducts; kinkbuilder::ConfKinkTablesDerivedToDerived confKinkBuilder; kinkbuilder::ConfSigmaSelection1 sigmaSelection1; - o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); - o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); kinkbuilder::ConfSigmaPlusSelection1 sigmaPlusSelection1; - o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; - o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); // cascade builder - cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; cascadebuilder::CascadeBuilderDerivedToDerivedProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTablesDerivedToDerived confCascadeBuilder; cascadebuilder::ConfXiSelection xiSelection; - o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(xiSelection); - o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(xiSelection); cascadebuilder::ConfOmegaSelection omegaSelection; - o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(omegaSelection); - o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(omegaSelection); // mc builder - mcbuilder::McBuilderDerivedToDerived mcBuilder; mcbuilder::McBuilderDerivedToDerivedProducts mcBuilderProducts; mcbuilder::ConfMcTablesDerivedToDerived confMcBuilder; - o2::framework::Partition mcTrackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcTrackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Partition mcSigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); - o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcSigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); - o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; - o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); void init(o2::framework::InitContext& /*context*/) { @@ -179,176 +194,176 @@ struct FemtoProducerDerivedToDerived { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } - trackBuilder.init(confTrackBuilder); - v0Builder.init(confV0Builder); - cascadeBuilder.init(confCascadeBuilder); - kinkBuilder.init(confKinkBuilder); - resonanceBuilder.init(confResonanceBuilder); - mcBuilder.init(confMcBuilder); + builders.trackBuilder.init(confTrackBuilder); + builders.v0Builder.init(confV0Builder); + builders.cascadeBuilder.init(confCascadeBuilder); + builders.kinkBuilder.init(confKinkBuilder); + builders.resonanceBuilder.init(confResonanceBuilder); + builders.mcBuilder.init(confMcBuilder); } // process functions - void processTracks(FilteredFemtoCollision const& col, FemtoTracks const& tracks) + void processTracks(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracks, "Process tracks", true); - void processTracksWithMass(FilteredFemtoCollision const& col, FemtoTracksWithMass const& tracks) + void processTracksWithMass(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithMass const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); - void processTracksWithPurity(FilteredFemtoCollision const& col, FemtoTracksWithPurity const& tracks) + void processTracksWithPurity(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithPurity const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithPurity, "Process tracks, carrying through purity/fraction tables", false); - void processTracksLambdas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processTracksLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksLambdas, "Process lambdas and tracks", false); - void processTracksWithMassLambdas(FilteredFemtoCollision const& col, FemtoTracksWithMass const& tracks, FemtoLambdas const& lambdas) + void processTracksWithMassLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithMass const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMassLambdas, "Process lambdas and tracks with mass", false); - void processLambdas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdas, "Process lambdas only (produced track table contains daughers only)", false); - void processTracksXis(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoXis const& xis) + void processTracksXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoXis const& xis) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - cascadeBuilder.processXis(col, xis, tracks, xiPartition, trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksXis, "Process tracks and xis", false); - void processTracksOmegas(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& omegas) + void processTracksOmegas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksOmegas, "Process tracks and omegas", false); - void processTracksK0shorts(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) + void processTracksK0shorts(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksK0shorts, "Process k0short and tracks", false); - void processTracksSigma(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigma const& sigma) + void processTracksSigma(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigma, "Process sigma and tracks", false); - void processTracksSigmaPlus(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlus const& sigmaplus) + void processTracksSigmaPlus(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlus const& sigmaplus) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); - void processTracksSigmaPlusExtended(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlusExtended const& sigmaplus) + void processTracksSigmaPlusExtended(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusExtended const& sigmaplus) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); - void processTracksRho0(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoRho0s const& rho0s) + void processTracksRho0(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksRho0, "Process rho0 and tracks", false); - void processTracksPhi(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoPhis const& phis) + void processTracksPhi(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoPhis const& phis) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { return; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - resonanceBuilder.processPhis(col, phis, tracks, phiPartition, trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processPhis(col, phis, tracks, phiPartition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); @@ -356,105 +371,105 @@ struct FemtoProducerDerivedToDerived { // // NOTE: unlike the other process functions, this one subscribes to the full // collision table and iterates manually. The MC index maps are keyed by source // global index and must live for the whole dataframe - void processTracksMc(FilteredFemtoMcCollisions const& cols, - FemtoMcTracks const& tracks, + void processTracksMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, o2::aod::FMcCols const& mcCols, - FemtoMcParticles const& mcParticles, + femtotables::FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { // once per dataframe - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (auto const& col : cols) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { continue; } // track indexMap is per collision by construction, so it resets here - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); - trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, - trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksMc, "Process tracks with MC information", false); - void processTracksSigmaMc(FilteredFemtoMcCollisions const& cols, - FemtoMcTracks const& tracks, - FemtoMcSigma const& sigma, + void processTracksSigmaMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigma const& sigma, o2::aod::FMcCols const& mcCols, - FemtoMcParticles const& mcParticles, + femtotables::FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (auto const& col : cols) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { continue; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); - trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, - trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); - kinkBuilder.processSigmaWithMc(col, sigma, tracks, mcSigmaPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaWithMc(col, sigma, tracks, mcSigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaMc, "Process sigma and tracks with MC information", false); - void processTracksSigmaPlusMc(FilteredFemtoMcCollisions const& cols, - FemtoMcTracks const& tracks, - FemtoMcSigmaPlus const& sigmaplus, + void processTracksSigmaPlusMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlus const& sigmaplus, o2::aod::FMcCols const& mcCols, - FemtoMcParticles const& mcParticles, + femtotables::FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (auto const& col : cols) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { continue; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); - trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, - trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); - kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusMc, "Process sigmaPlus and tracks with MC information", false); - void processTracksSigmaPlusExtendedMc(FilteredFemtoMcCollisions const& cols, - FemtoMcTracks const& tracks, - FemtoMcSigmaPlusExtended const& sigmaplus, + void processTracksSigmaPlusExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusExtended const& sigmaplus, o2::aod::FMcCols const& mcCols, - FemtoMcParticles const& mcParticles, + femtotables::FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - mcBuilder.reset(mcCols, mcParticles); + builders.mcBuilder.reset(mcCols, mcParticles); for (auto const& col : cols) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { continue; } - trackBuilder.reset(tracks); - collisionBuilder.processCollision(col, collisionBuilderProducts); - mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); - trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, - trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); - kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusExtendedPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, - mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtendedMc, "Process sigmaPlus (with SigmaPlusExtras) and tracks with MC information", false); From 380ac0b0ac11ffd2ae1d4fadf77d8fab637ed779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 30 Oct 2026 16:12:49 +0100 Subject: [PATCH 503/648] [Trigger,PWGHF,PWGJE,PWGUD] Fix casts (#18384) --- EventFiltering/PWGHF/HFFilterHelpers.h | 2 +- EventFiltering/PWGJE/jetFilter.cxx | 2 +- EventFiltering/PWGUD/diffractionBCFilter.cxx | 18 +++++++++--------- EventFiltering/PWGUD/diffractionFilter.cxx | 4 ++-- EventFiltering/macros/cefpOutputChecker.C | 10 +++++----- EventFiltering/macros/checkBCrangesSkimming.C | 14 +++++++------- EventFiltering/macros/checkSkimming.C | 2 +- EventFiltering/macros/getMenu.C | 2 +- EventFiltering/macros/selectivityPlot.C | 2 +- EventFiltering/macros/uploadOTSobjects.C | 14 +++++++------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/EventFiltering/PWGHF/HFFilterHelpers.h b/EventFiltering/PWGHF/HFFilterHelpers.h index 13bcfa93fa1..288cdf8fac5 100644 --- a/EventFiltering/PWGHF/HFFilterHelpers.h +++ b/EventFiltering/PWGHF/HFFilterHelpers.h @@ -2004,7 +2004,7 @@ inline void HfFilterHelper::setTpcRecalibMaps(o2::framework::Service(calibList->FindObject(mapNames[iMap].data())); + mHistMapPiPrKaDe[iMap] = dynamic_cast(calibList->FindObject(mapNames[iMap].data())); if (!mHistMapPiPrKaDe[iMap]) { LOG(fatal) << "Cannot find histogram: " << mapNames[iMap].data(); return; diff --git a/EventFiltering/PWGJE/jetFilter.cxx b/EventFiltering/PWGJE/jetFilter.cxx index d9f9f153333..a74c146e86b 100644 --- a/EventFiltering/PWGJE/jetFilter.cxx +++ b/EventFiltering/PWGJE/jetFilter.cxx @@ -98,7 +98,7 @@ struct jetFilter { void init(o2::framework::InitContext&) { triggerJetR = TMath::Nint(cfgJetR * 100.0f); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); spectra.add("fCollZpos", "collision z position", HistType::kTH1F, diff --git a/EventFiltering/PWGUD/diffractionBCFilter.cxx b/EventFiltering/PWGUD/diffractionBCFilter.cxx index b72c026e0e4..74d4672f981 100644 --- a/EventFiltering/PWGUD/diffractionBCFilter.cxx +++ b/EventFiltering/PWGUD/diffractionBCFilter.cxx @@ -112,11 +112,11 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", atr.bc().size()); if (atr.bc().size() > 0) { auto firstCompatibleBC = atr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } else { // registry.get(HIST("barrelTracks"))->Fill(2., 1.); @@ -128,7 +128,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } LOGF(debug, "track finished.\n"); @@ -195,12 +195,12 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", aftr.bc().size()); if (aftr.bc().size() > 0) { auto firstCompatibleBC = aftr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } else { // registry.get(HIST("forwardTracks"))->Fill(2., 1.); @@ -212,7 +212,7 @@ struct tracksWGTInBCs { // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } LOGF(debug, "FwdTrack finished.\n"); @@ -282,7 +282,7 @@ struct DGBCFilterRun3 { void init(InitContext&) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); } // some general Collisions and Tracks filter diff --git a/EventFiltering/PWGUD/diffractionFilter.cxx b/EventFiltering/PWGUD/diffractionFilter.cxx index b9fed2f424d..83bc848612e 100644 --- a/EventFiltering/PWGUD/diffractionFilter.cxx +++ b/EventFiltering/PWGUD/diffractionFilter.cxx @@ -89,13 +89,13 @@ struct DGFilterRun3 { { // reset collision counter collCounter = 0; - verbose = (int64_t)Verbose; + verbose = static_cast(Verbose); if (verbose <= 0) { verbose = 1000000000000; } // cut holder - diffCuts = (DGCutparHolder)diffCutsHolder; + diffCuts = DGCutparHolder(diffCutsHolder); // create histograms // stat diff --git a/EventFiltering/macros/cefpOutputChecker.C b/EventFiltering/macros/cefpOutputChecker.C index 48295a1746e..f6c4ffd78e5 100644 --- a/EventFiltering/macros/cefpOutputChecker.C +++ b/EventFiltering/macros/cefpOutputChecker.C @@ -21,18 +21,18 @@ void cefpOutputChecker(std::string histoFile = "AnalysisResults.root", std::string treeFile = "AO2D.root") { TFile referenceFile(histoFile.data()); - TH1* refScalers = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mScalers"); - TH1* refFilters = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mFiltered"); + TH1* refScalers = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mScalers")); + TH1* refFilters = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mFiltered")); TFile outputFile("cefpOutputChecker.root", "recreate"); - TH1* newFilters = (TH1*)refFilters->Clone("newFilters"); - TH1* newScalers = (TH1*)refScalers->Clone("newScalers"); + TH1* newFilters = dynamic_cast(refFilters->Clone("newFilters")); + TH1* newScalers = dynamic_cast(refScalers->Clone("newScalers")); newFilters->Reset(); newScalers->Reset(); TFile inputFile(treeFile.data()); for (auto key : *(inputFile.GetListOfKeys())) { - TTree* cefpTree = (TTree*)inputFile.Get(Form("%s/O2cefpdecision", key->GetName())); + TTree* cefpTree = dynamic_cast(inputFile.Get(Form("%s/O2cefpdecision", key->GetName()))); if (!cefpTree) continue; ULong64_t fCefpSelected, fCefpTriggered; diff --git a/EventFiltering/macros/checkBCrangesSkimming.C b/EventFiltering/macros/checkBCrangesSkimming.C index d7379030956..4224297f570 100644 --- a/EventFiltering/macros/checkBCrangesSkimming.C +++ b/EventFiltering/macros/checkBCrangesSkimming.C @@ -620,28 +620,28 @@ void checkBCrangesSkimming(std::string AnaFileName = "AnalysisResults.root", std TH1D *hOriginalSinglesRatio, *hOriginalDoublesRatio, *hOriginalMultiplesRatio; TH1D *hSkimmedSinglesRatio, *hSkimmedDoublesRatio, *hSkimmedMultiplesRatio; - hTriggerEff = reinterpret_cast(hSkimmedTotal.Clone("hTriggerEff")); + hTriggerEff = dynamic_cast(hSkimmedTotal.Clone("hTriggerEff")); hTriggerEff->SetTitle((runNumber + " skimmed efficiency;; Skimmed / Original").data()); hTriggerEff->Divide(&hOriginalTotal); hTriggerMatchesRatio.Divide(&hOriginalSingles); hTriggerSingleMatchesRatio.Divide(&hOriginalSingles); - hOriginalSinglesRatio = reinterpret_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); + hOriginalSinglesRatio = dynamic_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); hOriginalSinglesRatio->SetTitle((runNumber + " Original;;Singles / Total").data()); hOriginalSinglesRatio->Divide(&hOriginalTotal); - hOriginalDoublesRatio = reinterpret_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); + hOriginalDoublesRatio = dynamic_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); hOriginalDoublesRatio->SetTitle((runNumber + " Original;;Doubles / Total").data()); hOriginalDoublesRatio->Divide(&hOriginalTotal); - hOriginalMultiplesRatio = reinterpret_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); + hOriginalMultiplesRatio = dynamic_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); hOriginalMultiplesRatio->SetTitle((runNumber + " Original;;Multiples / Total").data()); hOriginalMultiplesRatio->Divide(&hOriginalTotal); - hSkimmedSinglesRatio = reinterpret_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); + hSkimmedSinglesRatio = dynamic_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); hSkimmedSinglesRatio->SetTitle((runNumber + " Skimmed;;Singles / Total").data()); hSkimmedSinglesRatio->Divide(&hSkimmedTotal); - hSkimmedDoublesRatio = reinterpret_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); + hSkimmedDoublesRatio = dynamic_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); hSkimmedDoublesRatio->SetTitle((runNumber + " Skimmed;;Doubles / Total").data()); hSkimmedDoublesRatio->Divide(&hSkimmedTotal); - hSkimmedMultiplesRatio = reinterpret_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); + hSkimmedMultiplesRatio = dynamic_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); hSkimmedMultiplesRatio->SetTitle((runNumber + " Skimmed;;Multiples / Total").data()); hSkimmedMultiplesRatio->Divide(&hSkimmedTotal); diff --git a/EventFiltering/macros/checkSkimming.C b/EventFiltering/macros/checkSkimming.C index 34d58a803fe..6f1961fb7bd 100644 --- a/EventFiltering/macros/checkSkimming.C +++ b/EventFiltering/macros/checkSkimming.C @@ -124,7 +124,7 @@ void checkSkimming(const std::string& listName = "period.txt", bool skipDownscal std::string original_path = line.substr(0, pos); std::string skimmed_path = line.substr(pos + 1); checkSkimming(original_path, skimmed_path, outputFile, skipDownscaled); - TH1* hRatio = (TH1*)gDirectory->Get("hRatio"); + TH1* hRatio = dynamic_cast(gDirectory->Get("hRatio")); hRatio->Draw(); std::string suffix = counter == 0 ? "(" : (counter == lineCounter - 1 ? ")" : ""); c1.Print((periodName + ".pdf" + suffix).data()); diff --git a/EventFiltering/macros/getMenu.C b/EventFiltering/macros/getMenu.C index 6e7e480f587..2166cf4635d 100644 --- a/EventFiltering/macros/getMenu.C +++ b/EventFiltering/macros/getMenu.C @@ -69,7 +69,7 @@ std::vector getMenuForPeriod(std::string period) TGrid::Connect("alien://"); TFile* scalersFile = TFile::Open((std::string("alien://") + firstLine).data(), "READ"); - TH1D* counters = (TH1D*)scalersFile->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* counters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); TAxis* axis = counters->GetXaxis(); std::vector binLabels(axis->GetNbins() - 2); diff --git a/EventFiltering/macros/selectivityPlot.C b/EventFiltering/macros/selectivityPlot.C index c7f710fe42d..7847c320744 100644 --- a/EventFiltering/macros/selectivityPlot.C +++ b/EventFiltering/macros/selectivityPlot.C @@ -45,7 +45,7 @@ void selectivityPlot(int runNumber = 550781, const TString& inputfile = "Analysi Canvas_1->SetFrameBorderMode(0); TFile* file = new TFile(inputfile.Data()); - TH1D* mFiltered = (TH1D*)file->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* mFiltered = dynamic_cast(file->Get("central-event-filter-task/scalers/mFiltered")); double nEvents = mFiltered->GetBinContent(1); mFiltered->Scale(1. / nEvents); mFiltered->SetLineColor(2); diff --git a/EventFiltering/macros/uploadOTSobjects.C b/EventFiltering/macros/uploadOTSobjects.C index e9982fafaaa..378526c7a3f 100644 --- a/EventFiltering/macros/uploadOTSobjects.C +++ b/EventFiltering/macros/uploadOTSobjects.C @@ -67,15 +67,15 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u auto orbitResetTimestamp = (*ctp)[0]; path = useAlien ? "alien://" + path : path; std::unique_ptr scalersFile{TFile::Open((path + "/AnalysisResults_fullrun.root").data(), "READ")}; - TH1* scalers = static_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); - TH1* filters = static_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); + TH1* scalers = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); + TH1* filters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); api.storeAsTFile(scalers, baseCCDBpath + "FilterCounters", metadata, duration.first, duration.second + 1); api.storeAsTFile(filters, baseCCDBpath + "SelectionCounters", metadata, duration.first, duration.second + 1); - TH1* hCounterTVX = static_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); + TH1* hCounterTVX = dynamic_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("lumi-task/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("lumi-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); if (!hCounterTVX) { std::cout << "No hCounterTVX histogram found in the file, skipping upload for run " << runString << std::endl; continue; @@ -88,7 +88,7 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u std::unique_ptr bcRangesFile{TFile::Open((path + "/bcRanges_fullrun.root").data(), "READ")}; int Nmax = 0; for (auto key : *(bcRangesFile->GetListOfKeys())) { - TTree* cefpTree = static_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); + TTree* cefpTree = dynamic_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); if (!cefpTree) continue; ZorroHelper bci; @@ -134,7 +134,7 @@ void uploadOTSobjects(std::string inputList, const std::string& passName, bool u auto bcNext{nextHelper.bcAOD < nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel}; if (bcNext - bcEnd > 2001 / o2::constants::lhc::LHCBunchSpacingMUS) { /// ensure a gap of 2ms between chunks chunk.insert(chunk.begin(), zorroHelpers.begin() + helperIndex, zorroHelpers.begin() + endIndex + 1); - endTS = (orbitResetTimestamp + int64_t(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; + endTS = (orbitResetTimestamp + static_cast(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; break; } bcEnd = nextHelper.bcAOD > nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel; From 3ba5756d6ccf9ddad6ec7c556246ca30eb955421 Mon Sep 17 00:00:00 2001 From: Sigurd Nese <32108009+sigurdnese@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:09:11 +0100 Subject: [PATCH 504/648] [PWGDQ] Estimate reserve size for ditracks and ditracksextra (#18525) --- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 24 ++++++++++++++++++++++-- PWGDQ/Tasks/tableReader_withAssoc.h | 21 +++++++++++++-------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 626252dc2f4..1e391380003 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -3884,8 +3884,28 @@ struct AnalysisAsymmetricPairing { int sign1 = 0; int sign2 = 0; uint32_t mcDecision = 0; - ditrackList.reserve(1); - ditrackExtraList.reserve(1); + + // estimate reserved size + int64_t reserveSize = 0; + for (auto const& event : events) { + auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); + auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; + } + ditrackList.reserve(reserveSize); + ditrackExtraList.reserve(reserveSize); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0 || (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index f38a93f0e05..ddf579012c2 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -3661,16 +3661,21 @@ struct AnalysisAsymmetricPairing { // Reserve capacity for the output tables int64_t reserveSize = 0; for (auto const& event : events) { - if (!event.isEventSelected_bit(0)) { - continue; - } - if (fConfigRemoveCollSplittingCandidates.value && event.isEventSelected_bit(2)) { - continue; - } auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); - reserveSize += static_cast(groupedLegAAssocs.size()) * - static_cast(groupedLegBAssocs.size()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; } ditrackList.reserve(reserveSize); ditrackExtraList.reserve(reserveSize); From c3d618243e7999e510b5b17ad488946cfab2748f Mon Sep 17 00:00:00 2001 From: Jonathan Witte <64792557+JonathanWitte@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:16:02 +0100 Subject: [PATCH 505/648] [PWGDQ] Reset and fill the event properties in the runMCGen (#18523) --- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 1e391380003..36d4f620d53 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -2704,6 +2704,11 @@ struct AnalysisSameEventPairing { if (!event.has_reducedMCevent()) { continue; } + // Fill the event-wise variables (e.g. centrality) of this reconstructed event, otherwise the generated particles and pairs + // get the values left over from the last event processed in runSameEventPairing + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); FinalStateMcParticleIndices.clear(); From 05c04d0fe3da42f1f9c2aa45b9deaab41382b66b Mon Sep 17 00:00:00 2001 From: hfribert Date: Fri, 30 Oct 2026 17:27:09 +0100 Subject: [PATCH 506/648] [PWGCF,PWGLF] Add Sigma+ via PCM reco to Femto framework (#18510) Co-authored-by: Henrik Fribert --- PWGCF/Femto/Core/kinkBuilder.h | 464 +++++++++++++++++- PWGCF/Femto/Core/kinkHistManager.h | 12 +- PWGCF/Femto/Core/mcBuilder.h | 34 +- PWGCF/Femto/Core/modes.h | 3 +- PWGCF/Femto/Core/particleCleaner.h | 5 + PWGCF/Femto/Core/partitions.h | 15 + PWGCF/Femto/DataModel/FemtoTables.h | 85 ++++ PWGCF/Femto/TableProducer/femtoProducer.cxx | 71 ++- .../femtoProducerDerivedToDerived.cxx | 95 +++- .../femtoProducerLiteConverter.cxx | 12 + PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 71 ++- PWGLF/DataModel/LFKinkDecayTables.h | 17 + .../TableProducer/Strangeness/CMakeLists.txt | 2 +- .../Strangeness/sigmaplusbuilder.cxx | 115 ++++- 14 files changed, 962 insertions(+), 39 deletions(-) diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index c884e325420..34728fbdcec 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -57,6 +57,9 @@ struct ConfKinkFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable massMaxSigma{"massMaxSigma", 1.3f, "Maximum mass for Sigma hypothesis"}; o2::framework::Configurable massMinSigmaPlus{"massMinSigmaPlus", 1.1f, "Minimum mass for SigmaPlus hypothesis"}; o2::framework::Configurable massMaxSigmaPlus{"massMaxSigmaPlus", 1.3f, "Maximum mass for SigmaPlus hypothesis"}; + o2::framework::Configurable massMinSigmaPlusPcm{"massMinSigmaPlusPcm", 1.1f, "Minimum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable massMaxSigmaPlusPcm{"massMaxSigmaPlusPcm", 1.3f, "Maximum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable mlScoreMinSigmaPlusPcm{"mlScoreMinSigmaPlusPcm", -1.f, "Minimum BDT score for SigmaPlus (PCM) (-1: no cut)"}; }; // selections bits for all kinks @@ -98,6 +101,20 @@ struct ConfSigmaPlusBits : o2::framework::ConfigurableGroup { #undef KINK_DEFAULT_BITS +// selection bits for sigma plus reconstructed with a PCM photon +struct ConfSigmaPlusPcmBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("SigmaPlusPcmBits"); + o2::framework::Configurable passThrough{"passThrough", false, "If true, all SigmaPlusPcm are passed through. Bits for all selections are stored."}; + o2::framework::Configurable> flightDirTiltMax{"flightDirTiltMax", {0.05f}, "Maximum tilt of the flight direction (rad)"}; + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; + o2::framework::Configurable> chaDauDcaZMax{"chaDauDcaZMax", {0.3f}, "Maximum |DCAz| of charged daughter from primary vertex (cm)"}; + o2::framework::Configurable> photonDcaZMax{"photonDcaZMax", {2.0f}, "Maximum |DCAz| of the photon from primary vertex (cm)"}; + o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {3.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; +}; + // base selection for analysis task for kinks // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ @@ -128,6 +145,15 @@ struct ConfSigmaPlusSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma mother track (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; }; +// base selection for analysis task for sigma plus reconstructed with a PCM photon +template +struct ConfSigmaPlusPcmSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_SELECTIONS(1.1, 1.3, 3222) + o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma candidate (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; + o2::framework::Configurable mlScoreMin{"mlScoreMin", -1.f, "Minimum BDT score (-1: no cut)"}; +}; + #undef KINK_DEFAULT_SELECTIONS constexpr const char PrefixSigmaSelection1[] = "SigmaSelection1"; @@ -138,6 +164,10 @@ constexpr const char PrefixSigmaPlusSelection1[] = "SigmaPlusSelection1"; constexpr const char PrefixSigmaPlusSelection2[] = "SigmaPlusSelection2"; using ConfSigmaPlusSelection1 = ConfSigmaPlusSelection; using ConfSigmaPlusSelection2 = ConfSigmaPlusSelection; +constexpr const char PrefixSigmaPlusPcmSelection1[] = "SigmaPlusPcmSelection1"; +constexpr const char PrefixSigmaPlusPcmSelection2[] = "SigmaPlusPcmSelection2"; +using ConfSigmaPlusPcmSelection1 = ConfSigmaPlusPcmSelection; +using ConfSigmaPlusPcmSelection2 = ConfSigmaPlusPcmSelection; /// The different selections for kinks enum KinkSeles { @@ -162,11 +192,16 @@ enum KinkSeles { kPtOriginalMin, kPtOriginalMax, + kFlightDirTiltMax, + kChaDauDcaZMax, + kPhotonDcaZMax, + kKinkSelsMax }; constexpr char SigmaSelHistName[] = "hSigmaSelection"; constexpr char SigmaPlusSelHistName[] = "hSigmaPlusSelection"; +constexpr char SigmaPlusPcmSelHistName[] = "hSigmaPlusPcmSelection"; const char kinkSelsName[] = "Kink selection object"; const std::unordered_map kinkSelectionNames = { {kKinkTopoDcaMax, "kinkTopoDcaMax"}, @@ -184,7 +219,10 @@ const std::unordered_map kinkSelectionNames = { {kQtAPMax, "qtAPMax"}, {kCosPointingAngleMin, "cosPointingAngleMin"}, {kPtOriginalMin, "ptOriginalMin"}, - {kPtOriginalMax, "ptOriginalMax"}}; + {kPtOriginalMax, "ptOriginalMax"}, + {kFlightDirTiltMax, "flightDirTiltMax"}, + {kChaDauDcaZMax, "chaDauDcaZMax"}, + {kPhotonDcaZMax, "photonDcaZMax"}}; /// enum for all kink pre-filters (evaluated in checkFilters, before the selection bitmask) enum KinkFilters { @@ -196,11 +234,13 @@ enum KinkFilters { kPhiMax, kMassMin, kMassMax, + kMlScoreMin, kKinkFiltersMax }; constexpr char SigmaFilterHistName[] = "hSigmaFilters"; constexpr char SigmaPlusFilterHistName[] = "hSigmaPlusFilters"; +constexpr char SigmaPlusPcmFilterHistName[] = "hSigmaPlusPcmFilters"; const std::unordered_map kinkFilterNames = { {kPtMin, "ptMin"}, {kPtMax, "ptMax"}, @@ -209,7 +249,8 @@ const std::unordered_map kinkFilterNames = { {kPhiMin, "phiMin"}, {kPhiMax, "phiMax"}, {kMassMin, "massMin"}, - {kMassMax, "massMax"}}; + {kMassMax, "massMax"}, + {kMlScoreMin, "mlScoreMin"}}; /// \class KinkCuts /// \brief Cut class to contain and execute all cuts applied to kinks @@ -501,6 +542,11 @@ struct KinkBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedLiteSigmaPlus; o2::framework::Produces producedSigmaPlusMasks; o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcm; + o2::framework::Produces producedLiteSigmaPlusPcm; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; struct ConfKinkTables : o2::framework::ConfigurableGroup { @@ -513,6 +559,11 @@ struct ConfKinkTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceLiteSigmaPlus{"produceLiteSigmaPlus", -1, "Produce LiteSigmaPlus (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusMasks{"produceSigmaPlusMasks", -1, "Produce SigmaPlusMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusExtras{"produceSigmaPlusExtras", -1, "Produce SigmaPlusExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcm{"produceSigmaPlusPcm", -1, "Produce SigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteSigmaPlusPcm{"produceLiteSigmaPlusPcm", -1, "Produce LiteSigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMasks{"produceSigmaPlusPcmMasks", -1, "Produce SigmaPlusPcmMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMlScores{"produceSigmaPlusPcmMlScores", -1, "Produce SigmaPlusPcmMlScores (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmExtras{"produceSigmaPlusPcmExtras", -1, "Produce SigmaPlusPcmExtras (-1: auto; 0 off; 1 on)"}; }; template @@ -756,10 +807,320 @@ class KinkBuilder bool mProduceSigmaPlusExtras = false; }; +/// \class SigmaPlusPcmSelection +/// \brief Cut class to contain and execute all cuts applied to sigma plus reconstructed with a PCM photon +template +class SigmaPlusPcmSelection : public baseselection::BaseSelection +{ + public: + SigmaPlusPcmSelection() = default; + ~SigmaPlusPcmSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMassLowerLimit = filter.massMinSigmaPlusPcm.value; + mMassUpperLimit = filter.massMaxSigmaPlusPcm.value; + mMlScoreMin = filter.mlScoreMinSigmaPlusPcm.value; + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + + this->addSelection(kFlightDirTiltMax, kinkSelectionNames.at(kFlightDirTiltMax), config.flightDirTiltMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kMothDcaPvMax, kinkSelectionNames.at(kMothDcaPvMax), config.mothDcaPvMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kChaDauDcaZMax, kinkSelectionNames.at(kChaDauDcaZMax), config.chaDauDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPhotonDcaZMax, kinkSelectionNames.at(kPhotonDcaZMax), config.photonDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {kinkFilterNames.at(kPtMin), mPtMin}, + {kinkFilterNames.at(kPtMax), mPtMax}, + {kinkFilterNames.at(kEtaMin), mEtaMin}, + {kinkFilterNames.at(kEtaMax), mEtaMax}, + {kinkFilterNames.at(kPhiMin), mPhiMin}, + {kinkFilterNames.at(kPhiMax), mPhiMax}, + {kinkFilterNames.at(kMassMin), mMassLowerLimit}, + {kinkFilterNames.at(kMassMax), mMassUpperLimit}, + {kinkFilterNames.at(kMlScoreMin), mMlScoreMin}, + }); + }; + + template + void computeKinematics(T const& sigmaPlusPcmCand) + { + std::array momMother = {sigmaPlusPcmCand.pxSigmaPlus(), sigmaPlusPcmCand.pySigmaPlus(), sigmaPlusPcmCand.pzSigmaPlus()}; + mPt = RecoDecay::pt(momMother); + mEta = RecoDecay::eta(momMother); + mPhi = RecoDecay::phi(momMother); + mMass = sigmaPlusPcmCand.massSigmaPlus(); + mMlScore = sigmaPlusPcmCand.mlScore(); + } + + template + bool checkFilters(T const& /*sigmaPlusPcmCand*/) const + { + bool pass = true; + bool p = false; + + p = mPt > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = mPt < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = mEta > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = mEta < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = mPhi > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = mPhi < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = mMass > mMassLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; + + p = mMass < mMassUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; + + p = mMlScore >= mMlScoreMin; + this->template fillFilter(kMlScoreMin, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + template + void applySelections(T1 const& sigmaPlusPcmCand, T2 const& /*tracks*/) + { + this->reset(); + this->evaluateObservable(kFlightDirTiltMax, sigmaPlusPcmCand.flightDirTilt()); + this->evaluateObservable(kMothDcaPvMax, sigmaPlusPcmCand.candDcaToPV()); + this->evaluateObservable(kChaDauDcaZMax, sigmaPlusPcmCand.protonDcaZ()); + this->evaluateObservable(kPhotonDcaZMax, sigmaPlusPcmCand.photonDcaZToPV()); + + auto chaDaughter = sigmaPlusPcmCand.template protonTrack_as(); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); + } else { + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); + } + + this->assembleBitmask(); + }; + + [[nodiscard]] float getPt() const { return mPt; } + [[nodiscard]] float getEta() const { return mEta; } + [[nodiscard]] float getPhi() const { return mPhi; } + [[nodiscard]] float getMass() const { return mMass; } + + private: + float mMassLowerLimit = 1.1f; + float mMassUpperLimit = 1.3f; + float mMlScoreMin = -1.f; + bool mKeepTracksWithoutTof = true; + + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -1.f; + float mEtaMax = 1.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + // mother kinematic + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + float mMlScore = 0.f; +}; + +template +class SigmaPlusPcmBuilder +{ + public: + SigmaPlusPcmBuilder() = default; + ~SigmaPlusPcmBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) + { + LOG(info) << "Initialize femto SigmaPlusPcm builder..."; + mProduceSigmaPlusPcm = utils::enableTable("FSigmaPlusPcm_001", table.produceSigmaPlusPcm.value, initContext); + mProduceLiteSigmaPlusPcm = utils::enableTable("FLiteSigmaPlusPcm_001", table.produceLiteSigmaPlusPcm.value, initContext); + mProduceSigmaPlusPcmMasks = utils::enableTable("FSigmaPlusPcmMasks_001", table.produceSigmaPlusPcmMasks.value, initContext); + mProduceSigmaPlusPcmMlScores = utils::enableTable("FSigmaPlusPcmMlScores_001", table.produceSigmaPlusPcmMlScores.value, initContext); + mProduceSigmaPlusPcmExtras = utils::enableTable("FSigmaPlusPcmExtras_001", table.produceSigmaPlusPcmExtras.value, initContext); + + if (mProduceSigmaPlusPcm && mProduceLiteSigmaPlusPcm) { + LOG(fatal) << "FSigmaPlusPcm and FLiteSigmaPlusPcm are mutually exclusive -- enable only one. " + << "FLiteSigmaPlusPcm is meant to replace FSigmaPlusPcm at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FSigmaPlusPcm from FLiteSigmaPlusPcm downstream."; + } + if (mProduceSigmaPlusPcm && !trackBuilder.producingTracks()) { + LOG(fatal) << "FSigmaPlusPcm is enabled, but the track builder is not producing FTracks (full precision). " + << "FSigmaPlusPcm stores the daughter index into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteSigmaPlusPcm if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteSigmaPlusPcm && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteSigmaPlusPcm is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteSigmaPlusPcm stores the daughter index into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FSigmaPlusPcm if TrackTables.produceTracks is enabled instead."; + } + + if (mProduceSigmaPlusPcm || mProduceLiteSigmaPlusPcm || mProduceSigmaPlusPcmMasks || mProduceSigmaPlusPcmMlScores || mProduceSigmaPlusPcmExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mSigmaPlusPcmSelection.configure(registry, config, filter); + mSigmaPlusPcmSelection.printSelections(kinkSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& kinkProducts, T6 const& sigmaPlusPcms, T7 const& tracks, T8& trackBuilder) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, collisionBuilder); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + } + } + + template + void fillMcSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& kinkProducts, T7 const& sigmaPlusPcms, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + mcBuilder.template fillMcSigmaPlusPcmWithLabel(sigmaPlusPcm, daughter, mcParticles, mcCols, mcProducts); + } + } + + template + void fillSigmaPlusPcm(T1& collisionBuilder, T2& kinkProducts, T3 const& sigmaPlusPcm, int64_t daughterIndex) + { + float signedPt = sigmaPlusPcm.protonSign() * mSigmaPlusPcmSelection.getPt(); + if (mProduceSigmaPlusPcm) { + kinkProducts.producedSigmaPlusPcm(collisionBuilder.collisionIndex(), + signedPt, + mSigmaPlusPcmSelection.getEta(), + mSigmaPlusPcmSelection.getPhi(), + mSigmaPlusPcmSelection.getMass(), + daughterIndex); + } + if (mProduceLiteSigmaPlusPcm) { + kinkProducts.producedLiteSigmaPlusPcm(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(signedPt), + o2::aod::femtobase::lite::binEta(mSigmaPlusPcmSelection.getEta()), + o2::aod::femtobase::lite::binPhi(mSigmaPlusPcmSelection.getPhi()), + o2::aod::femtokinks::lite::binSigmaMass(mSigmaPlusPcmSelection.getMass()), + daughterIndex); + } + if (mProduceSigmaPlusPcmMasks) { + kinkProducts.producedSigmaPlusPcmMasks(mSigmaPlusPcmSelection.getBitmask()); + } + if (mProduceSigmaPlusPcmMlScores) { + kinkProducts.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if (mProduceSigmaPlusPcmExtras) { + kinkProducts.producedSigmaPlusPcmExtras( + sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.candDcaToPV(), + sigmaPlusPcm.protonDcaXY(), + sigmaPlusPcm.protonDcaZ(), + sigmaPlusPcm.nSigmaTPCProton(), + sigmaPlusPcm.nSigmaTOFProton(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaProtonGamma(), + sigmaPlusPcm.radius()); + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mSigmaPlusPcmSelection.isPassThrough(); } + + private: + SigmaPlusPcmSelection mSigmaPlusPcmSelection; + bool mFillAnyTable = false; + bool mProduceSigmaPlusPcm = false; + bool mProduceLiteSigmaPlusPcm = false; + bool mProduceSigmaPlusPcmMasks = false; + bool mProduceSigmaPlusPcmMlScores = false; + bool mProduceSigmaPlusPcmExtras = false; +}; + struct ConfKinkTablesDerivedToDerived : o2::framework::ConfigurableGroup { std::string prefix = std::string("KinkTables"); o2::framework::Configurable limitSigma{"limitSigma", 1, "Require at least this many Sigmas in the collision. Set to 0 to skip Sigmas entirely (not written to output)."}; o2::framework::Configurable limitSigmaPlus{"limitSigmaPlus", 0, "Require at least this many SigmaPlus in the collision. Set to 0 to skip SigmaPlus entirely (not written to output)."}; + o2::framework::Configurable limitSigmaPlusPcm{"limitSigmaPlusPcm", 0, "Require at least this many SigmaPlusPcm in the collision. Set to 0 to skip SigmaPlusPcm entirely (not written to output)."}; }; struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { @@ -768,6 +1129,10 @@ struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedSigmaPluses; o2::framework::Produces producedSigmaPlusMasks; o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcms; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; class KinkBuilderDerivedToDerived @@ -778,12 +1143,13 @@ class KinkBuilderDerivedToDerived { mLimitSigma = config.limitSigma.value; mLimitSigmaPlus = config.limitSigmaPlus.value; + mLimitSigmaPlusPcm = config.limitSigmaPlusPcm.value; - if (mLimitSigma < 0 || mLimitSigmaPlus < 0) { - LOG(fatal) << "Kink limits must be non-negative (got " << mLimitSigma << " and " << mLimitSigmaPlus << "). Breaking..."; + if (mLimitSigma < 0 || mLimitSigmaPlus < 0 || mLimitSigmaPlusPcm < 0) { + LOG(fatal) << "Kink limits must be non-negative (got " << mLimitSigma << ", " << mLimitSigmaPlus << " and " << mLimitSigmaPlusPcm << "). Breaking..."; } - if (mLimitSigma == 0 && mLimitSigmaPlus == 0) { - LOG(fatal) << "Both sigma limit and sigmaplus limit are 0. Breaking..."; + if (mLimitSigma == 0 && mLimitSigmaPlus == 0 && mLimitSigmaPlusPcm == 0) { + LOG(fatal) << "Sigma, sigmaplus and sigmapluspcm limits are all 0. Breaking..."; } } @@ -807,6 +1173,16 @@ class KinkBuilderDerivedToDerived return sigmaPlusSlice.size() < static_cast(mLimitSigmaPlus); } + template + bool collisionHasTooFewSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3& partitionSigmaPlusPcm, T4& cache) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled, cannot reject on them + return false; + } + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return sigmaPlusPcmSlice.size() < static_cast(mLimitSigmaPlusPcm); + } + template void processSigma(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable) { @@ -909,6 +1285,58 @@ class KinkBuilderDerivedToDerived } } + template + void processSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + } + } + + template + void processSigmaPlusPcmWithMc(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlusPcm == 0) { + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + mcBuilder.fillSigmaPlusPcmWithLabel(sigmaPlusPcm, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + private: template void fillSigmaPlusExtensions(T1 const& sigmaPlus, T2& newSigmaPlusTable) @@ -930,8 +1358,32 @@ class KinkBuilderDerivedToDerived } } + template + void fillSigmaPlusPcmExtensions(T1 const& sigmaPlusPcm, T2& newSigmaPlusPcmTable) + { + if constexpr (requires(T1 t) { t.mlScore(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if constexpr (requires(T1 t) { t.photonConvRadius(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmExtras(sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.dcaMothToPV(), + sigmaPlusPcm.dauDcaXY(), + sigmaPlusPcm.dauDcaZ(), + sigmaPlusPcm.dauTpcNSigmaPr(), + sigmaPlusPcm.dauTofNSigmaPr(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaDauPhoton(), + sigmaPlusPcm.transRadius()); + } + } + int mLimitSigma = 0; int mLimitSigmaPlus = 0; + int mLimitSigmaPlusPcm = 0; }; } // namespace o2::analysis::femto::kinkbuilder #endif // PWGCF_FEMTO_CORE_KINKBUILDER_H_ diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index abebfda31f4..2d9eed98b87 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -105,6 +105,11 @@ struct ConfSigmaPlusBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_BINNING(1.1, 1.3) }; +template +struct ConfSigmaPlusPcmBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_BINNING(1.1, 1.3) +}; #undef KINK_DEFAULT_BINNING @@ -114,6 +119,9 @@ using ConfSigmaBinning1 = ConfSigmaBinning; constexpr const char PrefixSigmaPlusBinning1[] = "SigmaPlusBinning1"; using ConfSigmaPlusBinning1 = ConfSigmaPlusBinning; +constexpr const char PrefixSigmaPlusPcmBinning1[] = "SigmaPlusPcmBinning1"; +using ConfSigmaPlusPcmBinning1 = ConfSigmaPlusPcmBinning; + template struct ConfKinkQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; @@ -262,6 +270,8 @@ constexpr char PrefixSigmaPlusQa[] = "SigmaPlusQA/"; constexpr char PrefixSigmaPlus1Se[] = "SigmaPlus1/SE/"; constexpr char PrefixSigmaPlus1Me[] = "SigmaPlus1/ME/"; constexpr char PrefixSigmaPlus2[] = "SigmaPlus2/"; +constexpr char PrefixSigmaPlusPcm1Se[] = "SigmaPlusPcm1/SE/"; +constexpr char PrefixSigmaPlusPcm1Me[] = "SigmaPlusPcm1/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; @@ -508,7 +518,7 @@ class KinkHistManager mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), kinkcandidate.phi()); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), kinkcandidate.mass()); - if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus)) { + if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus) || isEqual(kink, modes::Kink::kSigmaPlusPcm)) { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), kinkcandidate.sign()); } } diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index d05820904a2..8062322c13b 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -66,6 +66,7 @@ struct McBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedLcLabels; o2::framework::Produces producedSigmaLabels; o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; o2::framework::Produces producedXiLabels; o2::framework::Produces producedOmegaLabels; }; @@ -86,6 +87,7 @@ struct ConfMcTables : o2::framework::ConfigurableGroup { o2::framework::Configurable producedLcLabels{"producedLcLabels", -1, "Produce Lc labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce sigma labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce sigma plus labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusPcmLabels{"producedSigmaPlusPcmLabels", -1, "Produce sigma plus (PCM) labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedXiLabels{"producedXiLabels", -1, "Produce xi labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedOmegaLabels{"producedOmegaLabels", -1, "Produce omega labels (-1: auto; 0 off; 1 on)"}; }; @@ -159,6 +161,7 @@ class McBuilder mProduceLcLabels = utils::enableTable("FLcLabels_001", table.producedLcLabels.value, initContext); mProduceSigmaLabels = utils::enableTable("FSigmaLabels_001", table.producedSigmaLabels.value, initContext); mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels_001", table.producedSigmaPlusLabels.value, initContext); + mProduceSigmaPlusPcmLabels = utils::enableTable("FSigmaPlusPcmLabels_001", table.producedSigmaPlusPcmLabels.value, initContext); mProduceXiLabels = utils::enableTable("FXiLabels_001", table.producedXiLabels.value, initContext); mProduceOmegaLabels = utils::enableTable("FOmegaLabels_001", table.producedOmegaLabels.value, initContext); @@ -167,7 +170,7 @@ class McBuilder mProduceMcMothers || mProduceMcPartonicMothers || mProduceTrackLabels || mProduceLambdaLabels || mProduceK0shortLabels || - mProduceSigmaLabels || mProduceSigmaPlusLabels || + mProduceSigmaLabels || mProduceSigmaPlusLabels || mProduceSigmaPlusPcmLabels || mProduceXiLabels || mProduceOmegaLabels || mProduceD0Labels || mProduceLcLabels) { mFillAnyTable = true; @@ -426,6 +429,21 @@ class McBuilder fillMcLabelGeneric(sigmaPlusDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusLabels(p); }, true); } + template + void fillMcSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& chaDaughter, T3 const& mcParticles, T4 const& mcCols, T5& mcProducts) + { + if (!mProduceSigmaPlusPcmLabels) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + // true sigma plus (proton and photon from it): label of the sigma plus, otherwise label of the proton, its mother gives the origin of the background + if (sigmaPlusPcm.has_mcParticle()) { + fillMcLabelGeneric(sigmaPlusPcm, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } else { + fillMcLabelGeneric(chaDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } + } + template void fillMcXiWithLabel(T1 const& xi, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { @@ -741,6 +759,7 @@ class McBuilder bool mProduceLcLabels = false; bool mProduceSigmaLabels = false; bool mProduceSigmaPlusLabels = false; + bool mProduceSigmaPlusPcmLabels = false; bool mProduceXiLabels = false; bool mProduceOmegaLabels = false; bool mProduceMcMotherLabels = false; @@ -767,6 +786,7 @@ struct McBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedTrackLabels; o2::framework::Produces producedSigmaLabels; o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; }; struct ConfMcTablesDerivedToDerived : o2::framework::ConfigurableGroup { @@ -850,6 +870,18 @@ class McBuilderDerivedToDerived this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); } + template + void fillSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlusPcm.has_fMcParticle()) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + auto mcParticle = sigmaPlusPcm.template fMcParticle_as(); + mcProducts.producedSigmaPlusPcmLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + template void reset(T1 const& mcCols, T2 const& mcParticles) { diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index 0b0bf75121e..8276bac97f2 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -224,7 +224,8 @@ enum class V0 : o2::analysis::femto::datatypes::V0Type { enum class Kink : o2::analysis::femto::datatypes::KinkType { kSigma, - kSigmaPlus + kSigmaPlus, + kSigmaPlusPcm }; enum class Cascade : o2::analysis::femto::datatypes::CascadeType { diff --git a/PWGCF/Femto/Core/particleCleaner.h b/PWGCF/Femto/Core/particleCleaner.h index b7387cc3ec8..f284385cce9 100644 --- a/PWGCF/Femto/Core/particleCleaner.h +++ b/PWGCF/Femto/Core/particleCleaner.h @@ -73,6 +73,11 @@ constexpr const char PrefixSigmaPlusCleaner2[] = "SigmaPlusCleaner2"; using ConfSigmaPlusCleaner1 = ConfParticleCleaner; using ConfSigmaPlusCleaner2 = ConfParticleCleaner; +constexpr const char PrefixSigmaPlusPcmCleaner1[] = "SigmaPlusPcmCleaner1"; +constexpr const char PrefixSigmaPlusPcmCleaner2[] = "SigmaPlusPcmCleaner2"; +using ConfSigmaPlusPcmCleaner1 = ConfParticleCleaner; +using ConfSigmaPlusPcmCleaner2 = ConfParticleCleaner; + constexpr const char PrefixXiCleaner1[] = "XiCleaner1"; constexpr const char PrefixXiCleaner2[] = "XiCleaner2"; using ConfXiCleaner1 = ConfParticleCleaner; diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index cb3cfe24b53..6a3a473ecf9 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -244,6 +244,21 @@ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ ncheckbit(o2::aod::femtokinks::mask, (selection).mask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_SIGMAPLUSPCM_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + (o2::aod::femtokinks::mlScore >= (selection).mlScoreMin) && \ + ncheckbit(o2::aod::femtokinks::mask, (selection).mask) + // partition for D0 // sign of signedPt encodes the hypothesis: D0 (+) / D0bar (-) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 9de9dbe28b3..4816b1527b7 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -1096,6 +1096,18 @@ DECLARE_SOA_COLUMN(DauTpcNSigmaPi, dauTpcNSigmaPi, float); DECLARE_SOA_COLUMN(DauTofNSigmaPr, dauTofNSigmaPr, float); DECLARE_SOA_COLUMN(PtOriginal, ptOriginal, float); +// columns for sigma plus reconstructed with a PCM photon +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the flight direction needed for a real solution of the missing photon +DECLARE_SOA_COLUMN(DauDcaXY, dauDcaXY, float); //! DCAxy of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(DauDcaZ, dauDcaZ, float); //! DCAz of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the photon pointing angle to the primary vertex +DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the photon +DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the unbent proton momentum and the flight direction +DECLARE_SOA_COLUMN(DcaDauPhoton, dcaDauPhoton, float); //! DCA between charged daughter and photon at the decay vertex + // id column for charged daughter track DECLARE_SOA_INDEX_COLUMN_FULL(ChaDau, chaDau, int32_t, FTracks, "_ChaDau"); //! @@ -1266,6 +1278,72 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_002, "FSIGMAPLUSEXTRAS", 2, using FSigmaPlusExtras = FSigmaPlusExtras_002; using StoredFSigmaPlusExtras = StoredFSigmaPlusExtras_002; +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcm_001, "FSIGMAPLUSPCM", 1, + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma plus (+1) and anti sigma plus (-1) + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtokinks::ChaDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FSigmaPlusPcm = FSigmaPlusPcm_001; +using FSigmaPlusPcmRow = FSigmaPlusPcm::iterator; +using StoredFSigmaPlusPcm = StoredFSigmaPlusPcm_001; + +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteSigmaPlusPcm_001, "FLITESIGMAPCM", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtokinks::lite::BinnedSigmaMass, + femtokinks::lite::ChaDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtokinks::lite::SigmaMass); +using FLiteSigmaPlusPcm = FLiteSigmaPlusPcm_001; +using FLiteSigmaPlusPcmRow = FLiteSigmaPlusPcm::iterator; +using StoredFLiteSigmaPlusPcm = StoredFLiteSigmaPlusPcm_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMasks_001, "FSIGMAPCMMASKS", 1, + femtokinks::Mask); +using FSigmaPlusPcmMasks = FSigmaPlusPcmMasks_001; +using StoredFSigmaPlusPcmMasks = StoredFSigmaPlusPcmMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMlScores_001, "FSIGMAPCMML", 1, + femtokinks::MlScore); +using FSigmaPlusPcmMlScores = FSigmaPlusPcmMlScores_001; +using StoredFSigmaPlusPcmMlScores = StoredFSigmaPlusPcmMlScores_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmExtras_001, "FSIGMAPCMEXTRA", 1, + femtokinks::FlightDirTilt, + femtokinks::DcaMothToPV, + femtokinks::DauDcaXY, + femtokinks::DauDcaZ, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTofNSigmaPr, + femtokinks::PhotonDcaXYToPV, + femtokinks::PhotonDcaZToPV, + femtokinks::PhotonCosPAToPV, + femtokinks::PhotonConvRadius, + femtokinks::AntiSigmaPointingAngle, + femtokinks::DcaDauPhoton, + femtokinks::TransRadius); +using FSigmaPlusPcmExtras = FSigmaPlusPcmExtras_001; +using StoredFSigmaPlusPcmExtras = StoredFSigmaPlusPcmExtras_001; + namespace femtocascades { // columns for cascade bit masks @@ -1754,6 +1832,13 @@ using FSigmaPlusLabel = FSigmaPlusLabels::iterator; using StoredFSigmaPlusLabels = StoredFSigmaPlusLabels_001; using StoredFSigmaPlusLabel = StoredFSigmaPlusLabels::iterator; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmLabels_001, "FSIGMAPCMLABEL", 1, //! label from femto sigma plus (PCM) to femto mc particle + femtolabels::FMcParticleId); +using FSigmaPlusPcmLabels = FSigmaPlusPcmLabels_001; +using FSigmaPlusPcmLabel = FSigmaPlusPcmLabels::iterator; +using StoredFSigmaPlusPcmLabels = StoredFSigmaPlusPcmLabels_001; +using StoredFSigmaPlusPcmLabel = StoredFSigmaPlusPcmLabels::iterator; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiLabels_001, "FXILABEL", 1, //! label from femto xi to femto mc particle femtolabels::FMcParticleId); using FXiLabels = FXiLabels_001; diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 41922a24837..4b1082941ca 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -94,6 +94,9 @@ using Run3RecoCascades = o2::soa::Join; using Run3Kinks = o2::aod::KinkCands; +using Run3SigmaPlusPcms = o2::aod::SigmaPlusCands; +using Run3RecoSigmaPlusPcms = o2::aod::SigmaPlusCandsMC; + using Run3McGenParticles = o2::aod::McParticles; } // namespace o2::analysis::femto::rawinputs @@ -109,6 +112,7 @@ struct FemtoProducer { o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; + o2::framework::Preslice perColRecoSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; } preslices; @@ -130,6 +134,7 @@ struct FemtoProducer { cascadebuilder::CascadeBuilder omegaBuilder; kinkbuilder::KinkBuilder sigmaBuilder; kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::SigmaPlusPcmBuilder sigmaPlusPcmBuilder; mcbuilder::McBuilder mcBuilder; } builders; @@ -182,6 +187,7 @@ struct FemtoProducer { kinkbuilder::ConfKinkFilters confKinkFilters; kinkbuilder::ConfSigmaBits confSigmaBits; kinkbuilder::ConfSigmaPlusBits confSigmaPlusBits; + kinkbuilder::ConfSigmaPlusPcmBits confSigmaPlusPcmBits; // mc builder mcbuilder::ConfMc confMc; @@ -227,6 +233,7 @@ struct FemtoProducer { builders.sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); builders.sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusPcmBuilder.init(&hRegistry, confSigmaPlusPcmBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); builders.xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); builders.omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); @@ -262,6 +269,10 @@ struct FemtoProducer { !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one kink table is enabled, but wrong process function is enabled. Breaking..."; } + if (builders.sigmaPlusPcmBuilder.fillAnyTable() && + (!doprocessTracksSigmaPlusPcmsRun3pp && !doprocessTracksSigmaPlusPcmsRun3ppMc)) { + LOG(fatal) << "At least one sigma plus (PCM) table is enabled, but wrong process function is enabled. Breaking..."; + } if ((builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable()) && (!doprocessTracksD0sRun3pp && !doprocessTracksD0sRun3PbPb && !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc)) { @@ -277,6 +288,7 @@ struct FemtoProducer { !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPbMc && !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc && + !doprocessTracksSigmaPlusPcmsRun3ppMc && !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc && !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc && !doprocessMcOnly)) { @@ -304,6 +316,7 @@ struct FemtoProducer { add("lcbar", builders.lcBarBuilder.fillAnyTable(), builders.lcBarBuilder.isPassThrough()); add("sigma", builders.sigmaBuilder.fillAnyTable(), builders.sigmaBuilder.isPassThrough()); add("sigmaplus", builders.sigmaPlusBuilder.fillAnyTable(), builders.sigmaPlusBuilder.isPassThrough()); + add("sigmapluspcm", builders.sigmaPlusPcmBuilder.fillAnyTable(), builders.sigmaPlusPcmBuilder.isPassThrough()); add("xi", builders.xiBuilder.fillAnyTable(), builders.xiBuilder.isPassThrough()); add("omega", builders.omegaBuilder.fillAnyTable(), builders.omegaBuilder.isPassThrough()); add("mc", builders.mcBuilder.fillAnyTable(), builders.mcBuilder.isPassThrough()); @@ -337,7 +350,7 @@ struct FemtoProducer { // without FTrackExtras (fillType) these rows cannot be told apart from selected tracks downstream const bool anyDaughterBuilder = builders.k0shortBuilder.fillAnyTable() || builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable() || - builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable() || + builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable() || builders.sigmaPlusPcmBuilder.fillAnyTable() || builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable() || builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable(); if (builders.trackBuilder.isPassThrough() && anyDaughterBuilder && !builders.trackBuilder.producingTrackExtras()) { LOG(warn) << "Track pass-through is enabled together with candidates that register daughter tracks, but FTrackExtras is not produced. " @@ -361,6 +374,7 @@ struct FemtoProducer { static_cast(doprocessTracksKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + + static_cast(doprocessTracksSigmaPlusPcmsRun3pp) + static_cast(doprocessTracksD0sRun3pp) + static_cast(doprocessTracksD0sRun3PbPb) + static_cast(doprocessTracksRun3ppMc) + @@ -371,6 +385,7 @@ struct FemtoProducer { static_cast(doprocessTracksV0sCascadesRun3PbPbMc) + static_cast(doprocessTracksKinksRun3ppMc) + static_cast(doprocessTracksV0sKinksRun3ppMc) + + static_cast(doprocessTracksSigmaPlusPcmsRun3ppMc) + static_cast(doprocessTracksD0sRun3ppMc) + static_cast(doprocessTracksD0sRun3PbPbMc) + static_cast(doprocessTracksLcsRun3pp) + @@ -478,6 +493,18 @@ struct FemtoProducer { builders.sigmaPlusBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } + // processing sigma plus reconstructed with a PCM photon + template + void processSigmaPlusPcms(T1 const& col, T2 const& tracks, T3 const& sigmaPlusPcms) + { + builders.sigmaPlusPcmBuilder.fillSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder); + } + template + void processMcSigmaPlusPcms(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& sigmaPlusPcms, T5 const& mcParticles) + { + builders.sigmaPlusPcmBuilder.fillMcSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + // processing cascades template void processCascades(T1 const& col, T2 const& tracks, T3 const& cascades) @@ -720,6 +747,21 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3pp, "Provide tracks and kinks", false); + void processTracksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + processSigmaPlusPcms(col, tracks, sigmaPlusPcms); + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3pp, "Provide tracks and sigma plus reconstructed with a PCM photon", false); + void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, @@ -996,6 +1038,33 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3ppMc, "Provide reconstructed and generated tracks and kinks", false); + void processTracksSigmaPlusPcmsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoSigmaPlusPcms const& sigmaPlusPcms, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColRecoSigmaPlusPcms, col.globalIndex()); + processMcSigmaPlusPcms(col, mcCols, tracks, sigmaPlusPcmsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks and sigma plus reconstructed with a PCM photon", false); + void processTracksV0sKinksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index a2bc14986b2..2d6b5f1c580 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -61,11 +61,15 @@ using FemtoOmegas = o2::soa::Join; using FemtoSigma = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; using FemtoSigmaPlusExtended = o2::soa::Join; +using FemtoSigmaPlusPcm = o2::soa::Join; +using FemtoSigmaPlusPcmExtended = o2::soa::Join; using FemtoMcTracks = o2::soa::Join; using FemtoMcSigma = o2::soa::Join; using FemtoMcSigmaPlus = o2::soa::Join; using FemtoMcSigmaPlusExtended = o2::soa::Join; +using FemtoMcSigmaPlusPcm = o2::soa::Join; +using FemtoMcSigmaPlusPcmExtended = o2::soa::Join; using FemtoMcParticles = o2::soa::Join; } // namespace femtotables @@ -85,12 +89,16 @@ struct FemtoProducerDerivedToDerived { o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; } preslices; // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ @@ -156,6 +164,10 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + kinkbuilder::ConfSigmaPlusPcmSelection1 sigmaPlusPcmSelection1; + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition sigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + // cascade builder cascadebuilder::CascadeBuilderDerivedToDerivedProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTablesDerivedToDerived confCascadeBuilder; @@ -178,6 +190,9 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition mcSigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition mcSigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + void init(o2::framework::InitContext& /*context*/) { const int activeProcesses = @@ -187,9 +202,11 @@ struct FemtoProducerDerivedToDerived { static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + + static_cast(doprocessTracksSigmaPlusPcm) + static_cast(doprocessTracksSigmaPlusPcmExtended) + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + - static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc); + static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc) + + static_cast(doprocessTracksSigmaPlusPcmMc) + static_cast(doprocessTracksSigmaPlusPcmExtendedMc); if (activeProcesses != 1) { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } @@ -343,6 +360,30 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); + void processTracksSigmaPlusPcm(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcm const& sigmapluspcm) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcm, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks", false); + + void processTracksSigmaPlusPcmExtended(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcmExtended const& sigmapluspcm) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmExtendedPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtended, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks", false); + void processTracksRho0(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { @@ -473,6 +514,58 @@ struct FemtoProducerDerivedToDerived { } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtendedMc, "Process sigmaPlus (with SigmaPlusExtras) and tracks with MC information", false); + + void processTracksSigmaPlusPcmMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcm const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + builders.mcBuilder.reset(mcCols, mcParticles); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmPartition, cache)) { + continue; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks with MC information", false); + + void processTracksSigmaPlusPcmExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcmExtended const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + builders.mcBuilder.reset(mcCols, mcParticles); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtendedMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx index 3d2c5fe0eef..fe68e9c13ad 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx @@ -34,6 +34,7 @@ struct FemtoProducerLiteConverter { o2::framework::Produces producedOmegas; o2::framework::Produces producedSigmas; o2::framework::Produces producedSigmaPlus; + o2::framework::Produces producedSigmaPlusPcm; void init(o2::framework::InitContext&) { @@ -138,6 +139,17 @@ struct FemtoProducerLiteConverter { liteSigmaPlus.chaDauId()); } PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlus, "Convert FLiteSigmaPlus to FSigmaPlus", false); + + void processLiteSigmaPlusPcm(o2::aod::FLiteSigmaPlusPcm::iterator const& liteSigmaPlusPcm) + { + producedSigmaPlusPcm(liteSigmaPlusPcm.fLiteColId(), + liteSigmaPlusPcm.signedPt(), + liteSigmaPlusPcm.eta(), + liteSigmaPlusPcm.phi(), + liteSigmaPlusPcm.sigmaMass(), + liteSigmaPlusPcm.chaDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlusPcm, "Convert FLiteSigmaPlusPcm to FSigmaPlusPcm", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index 13d722338de..affa4a1fcff 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -60,10 +60,12 @@ struct FemtoPairTrackKink { using FemtoTracks = o2::soa::Join; using FemtoSigmas = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; using FemtoSigmasWithLabel = o2::soa::Join; using FemtoSigmaPlusWithLabel = o2::soa::Join; + using FemtoSigmaPlusPcmWithLabel = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -110,6 +112,17 @@ struct FemtoPairTrackKink { o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlusWithLabel = o2::aod::femtobase::stored::fColId; + // setup for sigma plus reconstructed with a PCM photon + kinkbuilder::ConfSigmaPlusPcmSelection1 confSigmaPlusPcmSelection; + kinkhistmanager::ConfSigmaPlusPcmBinning1 confSigmaPlusPcmBinning; + particlecleaner::ConfSigmaPlusPcmCleaner1 confSigmaPlusPcmCleaner; + + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition sigmaPlusPcmWithLabelPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcmWithLabel = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; @@ -143,6 +156,20 @@ struct FemtoPairTrackKink { modes::Kink::kSigmaPlus> pairTrackSigmaPlusBuilder; + pairbuilder::PairTrackKinkBuilder< + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlusPcm1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlusPcm1Me, + trackhistmanager::PrefixKinkChaDaughterMe, + pairhistmanager::PrefixTrackKinkSe, + pairhistmanager::PrefixTrackKinkMe, + closepairrejection::PrefixTrackKinkSe, + closepairrejection::PrefixTrackKinkMe, + modes::Kink::kSigmaPlusPcm> + pairTrackSigmaPlusPcmBuilder; + // setup mixing std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; @@ -159,8 +186,8 @@ struct FemtoPairTrackKink { void init(o2::framework::InitContext&) { - bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent; - bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc; + bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent; + bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; @@ -169,9 +196,14 @@ struct FemtoPairTrackKink { bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc; bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc; + bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc; + if (processSigma && processSigmaPlus) { LOG(fatal) << "Both Sigma-track and SigmaPlus-track processing is enabled. Breaking..."; } + if ((processSigma || processSigmaPlus) && processSigmaPlusPcm) { + LOG(fatal) << "Both Sigma/SigmaPlus-track and SigmaPlusPcm-track processing is enabled. Breaking..."; + } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here @@ -184,6 +216,7 @@ struct FemtoPairTrackKink { std::map> chaDauSpec; std::map> sigmaHistSpec; std::map> sigmaPlusHistSpec; + std::map> sigmaPlusPcmHistSpec; std::map> pairTrackKinkHistSpec; std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); @@ -196,9 +229,12 @@ struct FemtoPairTrackKink { if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaBinning); pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); - } else { + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusBinning); pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -208,9 +244,12 @@ struct FemtoPairTrackKink { if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaBinning); pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); - } else { + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusBinning); pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -263,6 +302,30 @@ struct FemtoPairTrackKink { pairTrackSigmaPlusBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus with MC information", false); + + void processSigmaPlusPcmSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& sigmapluspcm) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, tracks, trackPartition, sigmapluspcm, sigmaPlusPcmPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEvent, "Enable processing same event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& sigmapluspcm, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmapluspcm, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEventMc, "Enable processing same event processing for tracks and sigma plus (PCM) with MC information", false); + + void processSigmaPlusPcmMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEvent, "Enable processing mixed event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& /*sigmapluspcm*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus (PCM) with MC information", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGLF/DataModel/LFKinkDecayTables.h b/PWGLF/DataModel/LFKinkDecayTables.h index db5e3825d43..c636358e20b 100644 --- a/PWGLF/DataModel/LFKinkDecayTables.h +++ b/PWGLF/DataModel/LFKinkDecayTables.h @@ -163,6 +163,12 @@ DECLARE_SOA_TABLE(SlimKinkCandsMC, "AOD", "SLIMKINKCANDSMC", namespace sigmapluscand { +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Collision of the candidate (the proton's collision), -1 for generated-only MC rows +DECLARE_SOA_INDEX_COLUMN_FULL(ProtonTrack, protonTrack, int, TracksIU, "_Proton"); //! Proton track +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonPosTrack, photonPosTrack, int, TracksIU, "_PosDau"); //! Positive daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonNegTrack, photonNegTrack, int, TracksIU, "_NegDau"); //! Negative daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle); //! MC particle of the true Sigma+ if proton and photon come from it, -1 otherwise + DECLARE_SOA_COLUMN(XDecVtx, xDecVtx, float); //! Decay vertex of the candidate (x direction) DECLARE_SOA_COLUMN(YDecVtx, yDecVtx, float); //! Decay vertex of the candidate (y direction) DECLARE_SOA_COLUMN(ZDecVtx, zDecVtx, float); //! Decay vertex of the candidate (z direction) @@ -221,6 +227,7 @@ DECLARE_SOA_COLUMN(PhotonPosTpcNClsFindable, photonPosTpcNClsFindable, int16_t); DECLARE_SOA_COLUMN(PhotonNegTpcNClsFindable, photonNegTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's negative daughter DECLARE_SOA_COLUMN(PhotonPosTpcChi2NCl, photonPosTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's positive daughter DECLARE_SOA_COLUMN(PhotonNegTpcChi2NCl, photonNegTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's negative daughter +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate (signal probability), -1 if not computed // MC columns DECLARE_SOA_COLUMN(CollisionIdCheck, collisionIdCheck, bool); //! True if the proton's collision ID matches the reconstructed collision ID @@ -281,6 +288,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(YSigmaPlusMC, ySigmaPlusMC, //! True rapidity of the } // namespace sigmapluscand DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, sigmapluscand::DcaProtonGamma, sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, @@ -297,6 +305,7 @@ DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, // dynamic columns sigmapluscand::Radius, @@ -308,6 +317,8 @@ DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", sigmapluscand::MassSigmaPlus); DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, sigmapluscand::DcaProtonGamma, sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, @@ -324,6 +335,7 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, sigmapluscand::CollisionIdCheck, sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::XDecVtxMC, sigmapluscand::YDecVtxMC, sigmapluscand::ZDecVtxMC, @@ -344,6 +356,7 @@ DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", sigmapluscand::YSigmaPlusMC); DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, sigmapluscand::TransDecayRadius, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, @@ -358,6 +371,7 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, // dynamic columns sigmapluscand::PxSigmaPlus, @@ -367,6 +381,8 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", sigmapluscand::MassSigmaPlus); DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, sigmapluscand::TransDecayRadius, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, sigmapluscand::DcaProtonGamma, @@ -381,6 +397,7 @@ DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, sigmapluscand::CollisionIdCheck, sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, sigmapluscand::DecayRadiusMC, sigmapluscand::MassMC, diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index e892688ebb4..a0a64e95190 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -106,7 +106,7 @@ o2physics_add_dpl_workflow(sigmaminus-task # o2-linter: disable=name/o2-workflow o2physics_add_dpl_workflow(sigmaplusbuilder SOURCES sigmaplusbuilder.cxx REUSE_FROM LfStrangenessPCH - PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strange-tree-creator diff --git a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx index e9df7b7e324..9d342f942f8 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx @@ -26,8 +26,10 @@ #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include @@ -60,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +119,7 @@ struct Sigmaplusbuilder { Configurable protonRequireTofHit{"protonRequireTofHit", false, "Above protonPtMinRequireTOF, reject a proton with no TOF hit at all"}; Configurable protonMinDcaToPV{"protonMinDcaToPV", 0.005, "Min DCAxy of the proton track to the PV (cm)"}; Configurable protonMaxDcaToPV{"protonMaxDcaToPV", 5.0, "Max DCAxy of the proton track to the PV (cm)"}; + Configurable protonMaxDcaZToPV{"protonMaxDcaZToPV", 0.3, "Max |DCAz| of the proton track to the PV (cm)"}; // proton-photon candidate selection Configurable candVertexProtonWeight{"candVertexProtonWeight", 1.0, "Sigma+ vertex between the proton's (1) and the photon's (0) point of closest approach"}; @@ -134,11 +138,12 @@ struct Sigmaplusbuilder { Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' track momenta"}; Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon momentum and the decay-vertex-to-conversion-point line (rad)"}; Configurable candMaxPhotonDcaToPV{"candMaxPhotonDcaToPV", 250.0, "Max DCA of the photon's flight line to the PV (cm)"}; + Configurable candMaxPhotonDcaZToPV{"candMaxPhotonDcaZToPV", 2.0, "Max |DCAz| of the photon's flight line to the PV at its transverse DCA (cm), applied to the photon before the pairing with the proton"}; Configurable candDeduplicatePhotons{"candDeduplicatePhotons", true, "Per timeframe, write only the candidate with the smallest DCA to PV among candidates sharing the same photon"}; // tilt search: if the measured flight direction gives no real solution for the missing photon, // tilted flight directions are tried starting with the smallest tilt - Configurable candMaxTilt{"candMaxTilt", 0.3, "Max tilt of the flight direction searched for a real root (rad)"}; + Configurable candMaxTilt{"candMaxTilt", 0.05, "Max tilt of the flight direction searched for a real root (rad)"}; Configurable candTiltStep{"candTiltStep", 0.005, "Tilt step between the rings of tried flight directions (rad)"}; Configurable candTiltNAzimuth{"candTiltNAzimuth", 36, "Flight directions tried per ring"}; @@ -148,6 +153,13 @@ struct Sigmaplusbuilder { Configurable fillSlimTables{"fillSlimTables", false, "write the slim candidate tables instead of the full ones"}; + // BDT score (MlScore column) of every written candidate, inputs in the order of the training (see computeMlScore) + Configurable applyMl{"applyMl", false, "Compute the BDT score of every written candidate (otherwise MlScore = -1)"}; + Configurable mlLoadModelFromCcdb{"mlLoadModelFromCcdb", true, "Load the ONNX model from the CCDB instead of a local file"}; + Configurable mlModelPathCcdb{"mlModelPathCcdb", "Users/h/hfribert/SigmaPlusPcm/BDT_nominalB", "CCDB path of the ONNX model (nominal field: .../BDT_nominalB, low field: .../BDT_lowB)"}; + Configurable mlOnnxFileName{"mlOnnxFileName", "model.onnx", "ONNX file name (local file, or name of the file retrieved from the CCDB)"}; + Configurable mlTimestampCcdb{"mlTimestampCcdb", -1, "CCDB timestamp of the ONNX model (-1: now)"}; + Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "CCDB path of the material budget LUT"}; @@ -163,6 +175,7 @@ struct Sigmaplusbuilder { float mBz = 0; o2::base::MatLayerCylSet* mLut = nullptr; o2::aod::common::TPCVDriftManager mVDriftMgr; + o2::analysis::MlResponse mlResponse; // daughter pairing (electrons and positrons with the range of collisions each is compatible with) svPoolCreator svPhotonPoolCreator{PDG_t::kElectron, PDG_t::kPositron}; @@ -173,6 +186,10 @@ struct Sigmaplusbuilder { // values of a candidate, kept until the end of the timeframe and then written to the tables struct SigmaPlusCandidate { uint64_t photonId = 0; + int collisionId = -1; + int protonTrackId = -1; + int photonPosTrackId = -1; + int photonNegTrackId = -1; bool isSignal = false; int photonLegsWithoutIts = 0; int matchedSigmaId = -1; @@ -257,6 +274,19 @@ struct Sigmaplusbuilder { fitter.setUseAbsDCA(true); mVDriftMgr.init(&ccdb->instance()); + if (applyMl) { + // one model with two classes (background, signal) and no cut on the scores + const std::array noCuts{0., 0.}; + mlResponse.configure({0., 1.e6}, LabeledArray(noCuts.data(), 1, 2), {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, 2); + if (mlLoadModelFromCcdb) { + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbPath.value); + mlResponse.setModelPathsCCDB({mlOnnxFileName.value}, ccdbApi, {mlModelPathCcdb.value}, mlTimestampCcdb.value); + } else { + mlResponse.setModelPathsLocal({mlOnnxFileName.value}); + } + mlResponse.init(); + } svPhotonPoolCreator.setTimeMargin(photonPoolTimeMarginNS); if (photonSkipAmbiTracks) { svPhotonPoolCreator.setSkipAmbiTracks(); @@ -302,7 +332,7 @@ struct Sigmaplusbuilder { const AxisSpec axisSigmaSel{6, -0.5, 5.5, "step"}; const std::vector photonSteps{"All", "Neg eta", "Pos eta", "TPC clusters", "TPC nSigma_{el}, p_{T}", "DCA daughters", "Radius", - "Opening angle", "Delta theta", "CosPA", "Rapidity", "Qt", "Alpha", "Mass"}; + "Opening angle", "Delta theta", "CosPA, DCA_{z}", "Rapidity", "Qt", "Alpha", "Mass"}; const std::vector protonSteps{"All", "Pt", "Eta", "TPC clusters", "TPC nSigma", "TOF nSigma", "DCA to PV"}; const std::vector candSteps{"All pairs", "Autocorrelation", "Vertex fit", "DCA(p,#gamma)", "Radius", "Flight distance", "Real root", "Valid root", "DCA to PV", "Mass", "Rapidity", "Photon opening angle", "Photon pointing angle", "Photon DCA to PV", "Written"}; @@ -417,6 +447,14 @@ struct Sigmaplusbuilder { TTrack posTrack; }; + // longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) + static float photonDcaZ(const std::array& conv, const std::array& pGamma, const std::array& pv) + { + float pt2 = pGamma[0] * pGamma[0] + pGamma[1] * pGamma[1]; + float tAtDcaXY = pt2 > 0.f ? ((pv[0] - conv[0]) * pGamma[0] + (pv[1] - conv[1]) * pGamma[1]) / pt2 : 0.f; + return conv[2] + tAtDcaXY * pGamma[2] - pv[2]; + } + // MC counter of generated Sigma+ -> p pi0 in acceptance void fillSigmaCounter(int sigmaId, int step) { @@ -536,7 +574,7 @@ struct Sigmaplusbuilder { std::array pGamma{pNeg[0] + pPos[0], pNeg[1] + pPos[1], pNeg[2] + pPos[2]}; float cosPA = RecoDecay::cpa(pv, secVtx, pGamma); - if (cosPA < photonMinV0cospa) { + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { continue; } fillPhotonStep(9); @@ -923,8 +961,9 @@ struct Sigmaplusbuilder { passStep(8); // opening angle, and delta theta that is cut before the fit std::array pGamma{fit.pNeg[0] + fit.pPos[0], fit.pNeg[1] + fit.pPos[1], fit.pNeg[2] + fit.pPos[2]}; - float cosPA = RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, fit.secVtx, pGamma); - if (cosPA < photonMinV0cospa) { + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + float cosPA = RecoDecay::cpa(pv, fit.secVtx, pGamma); + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(fit.secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { continue; } passStep(9); @@ -1034,7 +1073,7 @@ struct Sigmaplusbuilder { fillProtonStep(5); fillProtonHist(HIST("h2DcaToPVVsPt"), isSignal, track.pt(), std::abs(track.dcaXY())); - if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV) { + if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV || std::abs(track.dcaZ()) > protonMaxDcaZToPV) { return false; } fillProtonStep(6); @@ -1181,7 +1220,7 @@ struct Sigmaplusbuilder { // Build a Sigma+ -> p pi0 candidate from a proton track and a PCM photon template - void buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, const std::array& pv) + void buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, int collisionIndex, const std::array& pv) { auto posTrack = photon.posTrack; auto negTrack = photon.negTrack; @@ -1508,19 +1547,22 @@ struct Sigmaplusbuilder { std::array pvToConvCrossDir = cross3(pvToConv, photonDir); float photonDcaToPV = std::sqrt(dot3(pvToConvCrossDir, pvToConvCrossDir)); fillCandHist(HIST("hPhotonDcaToPV"), isSignal, photonDcaToPV); - if (photonDcaToPV > candMaxPhotonDcaToPV) { - return; - } - fillCandStep(13); // photon DCA to PV - float photonPt2 = photon.px * photon.px + photon.py * photon.py; float tAtDcaXY = photonPt2 > 0.f ? ((pv[0] - photon.x) * photon.px + (pv[1] - photon.y) * photon.py) / photonPt2 : 0.f; float photonDcaXYToPV = std::hypot(photon.x + tAtDcaXY * photon.px - pv[0], photon.y + tAtDcaXY * photon.py - pv[1]); float photonDcaZToPV = photon.z + tAtDcaXY * photon.pz - pv[2]; + if (photonDcaToPV > candMaxPhotonDcaToPV || std::abs(photonDcaZToPV) > candMaxPhotonDcaZToPV) { + return; + } + fillCandStep(13); // photon DCA to PV float psiPair = photonPsiPair(posTrack, negTrack, photon.radius); SigmaPlusCandidate cand; cand.photonId = photonId(posTrack.globalIndex(), negTrack.globalIndex()); + cand.collisionId = collisionIndex; + cand.protonTrackId = protonTrack.globalIndex(); + cand.photonPosTrackId = posTrack.globalIndex(); + cand.photonNegTrackId = negTrack.globalIndex(); cand.isSignal = isSignal; cand.photonLegsWithoutIts = photonLegsWithoutIts; cand.matchedSigmaId = matchedSigmaId; @@ -1579,12 +1621,25 @@ struct Sigmaplusbuilder { mCandidatesOfTimeframe.push_back(cand); } + // BDT score + float computeMlScore(const SigmaPlusCandidate& cand) + { + constexpr float NoTofNSigmaThreshold = -900.f; // tofNSigma is -999 without TOF, NaN for the BDT + std::vector inputs{std::abs(cand.photonDcaZToPV), std::abs(cand.protonDcaXY), std::abs(cand.protonDcaZ), cand.dcaToPV, + cand.protonTpcNSigma, cand.protonTofNSigma < NoTofNSigmaThreshold ? std::numeric_limits::quiet_NaN() : cand.protonTofNSigma, + cand.antiSigmaPointingAngle, cand.dcaProtonGamma, cand.photonDcaXYToPV, cand.photonCosPAToPV, cand.tiltAngle}; + return mlResponse.getModelOutput(inputs, 0)[1]; + } + template void fillCandidateTables(const SigmaPlusCandidate& cand) { + const float mlScore = applyMl ? computeMlScore(cand) : -1.f; if constexpr (IsMC) { if (fillSlimTables) { - slimSigmaPlusCandsMC(cand.radius, + slimSigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.radius, cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, cand.protonSign, cand.protonDcaXY, cand.protonDcaZ, @@ -1597,13 +1652,16 @@ struct Sigmaplusbuilder { cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, cand.collisionIdCheck, cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, cand.decayRadiusMC, cand.massMC, cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2]); return; } - sigmaPlusCandsMC(cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + sigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], cand.flightDistance, cand.dcaProtonGamma, cand.pProton[0], cand.pProton[1], cand.pProton[2], cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], @@ -1619,6 +1677,7 @@ struct Sigmaplusbuilder { cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, cand.collisionIdCheck, cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, cand.decVtxMC[0], cand.decVtxMC[1], cand.decVtxMC[2], @@ -1628,7 +1687,8 @@ struct Sigmaplusbuilder { cand.decayRadiusMC, cand.massMC); } else { if (fillSlimTables) { - slimSigmaPlusCands(cand.radius, + slimSigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.radius, cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, cand.protonSign, cand.protonDcaXY, cand.protonDcaZ, @@ -1640,10 +1700,12 @@ struct Sigmaplusbuilder { cand.photonMass, cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, - cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl); + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); return; } - sigmaPlusCands(cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + sigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], cand.flightDistance, cand.dcaProtonGamma, cand.pProton[0], cand.pProton[1], cand.pProton[2], cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], @@ -1658,11 +1720,12 @@ struct Sigmaplusbuilder { cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, - cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl); + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); } } - // The same photon can be in candidates of several collisions (with candDeduplicatePhotons only the one with the smallest DCA to PV is written) + // The same photon can be in candidates of several collisions template void deduplicateAndWriteCandidates() { @@ -1760,7 +1823,7 @@ struct Sigmaplusbuilder { for (const auto& photon : acceptedPhotons) { for (const auto& proton : acceptedProtons) { - buildSigmaPlusCandidate(proton, photon, pv); + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); } } } @@ -1847,7 +1910,7 @@ struct Sigmaplusbuilder { for (const auto& photon : acceptedPhotons) { for (const auto& proton : acceptedProtons) { - buildSigmaPlusCandidate(proton, photon, pv); + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); } } } @@ -1874,7 +1937,9 @@ struct Sigmaplusbuilder { float genMassMC = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); if (fillSlimTables) { - slimSigmaPlusCandsMC(-999.f, + slimSigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, -999.f, -999.f, -999.f, 0, -999.f, -999.f, @@ -1887,6 +1952,7 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, 0, 0, -999.f, -999.f, + -1.f, false, false, false, false, genDecayRadiusMC, genMassMC, @@ -1894,7 +1960,9 @@ struct Sigmaplusbuilder { continue; } - sigmaPlusCandsMC(-999.f, -999.f, -999.f, + sigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, @@ -1910,6 +1978,7 @@ struct Sigmaplusbuilder { -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, -999.f, 0, 0, -999.f, -999.f, + -1.f, false, false, false, false, genDecVtx[0], genDecVtx[1], genDecVtx[2], From 8cd38b556445a1def4e7233241ffa08f3e747e66 Mon Sep 17 00:00:00 2001 From: glromane <95305986+glromane@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:40:24 +0100 Subject: [PATCH 507/648] [PWGCF] adding TPC rejection cut (#18529) --- PWGCF/Femto3D/Tasks/femto3dPairTask.cxx | 14 +++++++++----- PWGCF/Femto3D/Tasks/femto3dQA.cxx | 12 ++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx index 4d08256aa4b..0d29ebf334f 100644 --- a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx @@ -94,8 +94,9 @@ struct FemtoCorrelations { Configurable> _tofNSigma_2{"tofNSigma_2", std::vector{-3.0f, 3.0f}, "second particle PID: Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual_2{"tpcNSigmaResidual_2", std::vector{-5.0f, 5.0f}, "second particle PID: residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoRejectFromSecond", 0, "applied only if the particles are non-identical and only to the second particle in the pair!!!"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for the particle specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoRejectFromFirst", 211, "if the particles are non-identical then applied only to the first"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs range for the particle specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs range for the particle specified with PDG to be rejected"}; Configurable _dPhiMode{"dPhiMode", 0, "Flag to choose how to calc. dphi*: 0 - at a fixed TPC radius; 1 - average over different TPC radii;"}; Configurable _radiusTPC{"radiusTPC", 1.2, "TPC radius to calculate phi_star for"}; @@ -122,7 +123,7 @@ struct FemtoCorrelations { // P.P.P.P.S this way is still being testing i might be reconsidered; might change in the future, keep looking at the source code Configurable _MEreductionFactor{"MEreductionFactor", 1, "only one (pseudo)randomly choosen event out per pair $value events will be processed and contribute to the final mixing (if < 2 -> all the possible event pairs (per vertex¢ bin) will be processed); implemented for the sake of efficiency; look at the source code;"}; - bool IsIdentical; + bool IsIdentical = true; std::pair> TPCcuts_1; std::pair> TOFcuts_1; @@ -258,7 +259,7 @@ struct FemtoCorrelations { } if (_fillDetaDphi > -1) { - TString suffix[] = {"_nocut", "_cut"}; + const TString suffix[2] = {"_nocut", "_cut"}; for (int f = 0; f < 2; f++) { std::vector> DoubleTrack_SE_histos_perMult; @@ -450,6 +451,9 @@ struct FemtoCorrelations { continue; if (track.sign() == _sign_1 && (track.p() < _PIDtrshld_1 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_1, _itsNSigma_1.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_1, _tpcNSigmaResidual_1.value))) { // filling the map: eventID <-> selected particles1 + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_first->Fill(track.p()); @@ -460,7 +464,7 @@ struct FemtoCorrelations { if (IsIdentical) { continue; - } else if (track.sign() != _sign_2 && !TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) + } else if (track.sign() != _sign_2 && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_second->Fill(track.p()); diff --git a/PWGCF/Femto3D/Tasks/femto3dQA.cxx b/PWGCF/Femto3D/Tasks/femto3dQA.cxx index e55cd3c73ed..5e218e1a938 100644 --- a/PWGCF/Femto3D/Tasks/femto3dQA.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dQA.cxx @@ -75,15 +75,16 @@ struct QAHistograms { Configurable _tpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; Configurable _itsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; Configurable _itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; - Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for (only pion, kaon, proton and deurton are supported now)"}; + Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for"}; Configurable> _tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; Configurable> _itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; Configurable _PIDtrshld{"PIDtrshld", 10.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; Configurable> _tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of perticles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of particles that will be rejected with TPC and/or TOF"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs for particles specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs for particles specified with PDG to be rejected"}; Configurable> _centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep tracks within"}; @@ -235,7 +236,10 @@ struct QAHistograms { registry.fill(HIST("TOFSignal_nocuts"), track.p(), track.beta()); } - if (!TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value))) { + if (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value)) { + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + registry.fill(HIST("eta"), track.eta()); registry.fill(HIST("phi"), track.phi()); registry.fill(HIST("px"), track.px()); From 8caf4edfc89968834de70befbdc3ec488940595e Mon Sep 17 00:00:00 2001 From: Jiabin Geng <997406694@qq.com> Date: Sat, 31 Oct 2026 00:43:29 +0800 Subject: [PATCH 508/648] [PWGLF] Add Event mixing for the closure test in hStrangeCorrelation.cxx (#18530) --- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 193 ++++++++++-------- 1 file changed, 108 insertions(+), 85 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 4be547e19af..d7bc6ed2a1c 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -150,12 +150,13 @@ struct HStrangeCorrelation { Configurable localDensityConeRadius{"localDensityConeRadius", 0.4, "radius of the cone in which the local density is counted"}; Configurable doMultEstimatorResponse{"doMultEstimatorResponse", false, "processSameEventHV0s: fill MultEstimatorResponse, multNTracksPVeta1 vs FT0M percentile and amplitude, with the event selection of PairLossK0/EventRecoEfficiency (no INEL cuts)"}; Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable mixingEventForClosureTest{"mixingEventForClosureTest", false, "if true, mix events for closure test"}; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; } masterConfigurations; // master analysis switches - Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; - Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; - Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; @@ -416,8 +417,6 @@ struct HStrangeCorrelation { float efficiencyError; int type; }; - float pvz = 0.0f; - float mult = 0.0f; std::vector trigParticles; std::vector assocParticles; void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type) @@ -435,6 +434,13 @@ struct HStrangeCorrelation { using ValidCollisions = std::vector>; ValidCollisions validCollisions; + struct ValidTriggerInGen { + double eta; + double phi; + double pt; + }; + using GenLevelCollisions = std::vector>>; + GenLevelCollisions genLevelCollisions; // objects to use for efficiency corrections TH2F* hEfficiencyTrigger = nullptr; TH3F* hEfficiencyTriggerMult = nullptr; @@ -1447,18 +1453,6 @@ struct HStrangeCorrelation { void fillCorrelationsV0(aod::TriggerTracks const& triggers, aod::AssocV0s const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int binMult = 0; - int nBinsMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { @@ -1705,7 +1699,7 @@ struct HStrangeCorrelation { } std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) || (-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) || (+massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma))) { @@ -1719,7 +1713,7 @@ struct HStrangeCorrelation { histos.fill(HIST("sameEvent/InvariantMass/") + HIST(V0names[Index]) + HIST("/hUnderlyingEvent"), ptassoc, pttrigger, getV0InvariantMass(assoc)); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (pairLossComparison.active && Index == IndexK0) { // Exact Rec left sideband, with the same reconstructed @@ -1746,7 +1740,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma))) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma))) { if (masterConfigurations.doCorrelationsHadronV0daughter) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hSameSign")), binFillThnSameSignDaugher, 1, 1, 1, 1, 1); fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); @@ -1921,7 +1915,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (pairLossComparison.active && Index == IndexK0) { // Exact Rec right sideband, with the same reconstructed @@ -1948,14 +1942,14 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { @@ -1966,7 +1960,7 @@ struct HStrangeCorrelation { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { @@ -1977,10 +1971,16 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) { + if (!mixingInBf) { return; } - int binnumb = binMult * nBinsVtxZ + binVtxZ; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + // When 'collisionHasTriggOrAssoc' = 0: + // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] + // When 'collisionHasTriggOrAssoc' = 3 + // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) + // due to avoiding vector occupancy by collisions with no trigger&associated particles + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || @@ -1989,13 +1989,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; - // When 'collisionHasTriggOrAssoc' = 0: - // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] - // When 'collisionHasTriggOrAssoc' = 3 - // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) - // due to avoiding vector occupancy by collisions with no trigger&associated particles - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -2040,18 +2034,6 @@ struct HStrangeCorrelation { void fillCorrelationsCascade(aod::TriggerTracks const& triggers, aod::AssocCascades const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int nBinsMult = 0; - int binMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { @@ -2257,7 +2239,7 @@ struct HStrangeCorrelation { std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index + 3) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2268,7 +2250,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2279,7 +2261,7 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); @@ -2290,21 +2272,21 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { @@ -2315,10 +2297,11 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) { + if (!mixingInBf) { return; } - int binnumb = binMult * nBinsVtxZ + binVtxZ; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || @@ -2327,8 +2310,7 @@ struct HStrangeCorrelation { return; } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -2451,7 +2433,7 @@ struct HStrangeCorrelation { if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(assocTrack.mcMask(), 13)) { continue; } - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); @@ -3511,6 +3493,9 @@ struct HStrangeCorrelation { for (int i = 0; i < AssocParticleTypes; i++) { if (TESTBIT(doCorrelation, i)) { histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + if (masterConfigurations.mixingEventForClosureTest) { + histos.addClone("ClosureTest/sameEvent/", "ClosureTest/mixedEvent/"); + } if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}", Particlenames[i]).c_str(), "truth pairs whose trigger has at least one reconstructed track with a matching MC label", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } @@ -3550,7 +3535,7 @@ struct HStrangeCorrelation { } if (doprocessPrediction) { mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = doAssocPhysicalPrimary.value; + mCounter.mSelectPrimaries = masterConfigurations.doAssocPhysicalPrimary.value; histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); std::array eventSelLabel = {"Read", "INELgt0", "|Z|<10"}; for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { @@ -3999,7 +3984,7 @@ struct HStrangeCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); @@ -4089,7 +4074,7 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if ((TESTBIT(doCorrelation, Index)) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { histos.fill(HIST("h3d") + HIST(V0names[Index]) + HIST("Spectrum"), v0Data.pt(), cent, v0.invMassNSigma(Index), weight); if (masterConfigurations.doMassSpectrumCheck) { @@ -4304,7 +4289,7 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if (TESTBIT(doCorrelation, Index + 3) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { histos.fill(HIST("h3d") + HIST(Cascadenames[Index]) + HIST("Spectrum"), cascData.pt(), cent, casc.invMassNSigma(Index), weight); if (std::abs(cascData.rapidity(Index)) < ySel) { @@ -4400,7 +4385,7 @@ struct HStrangeCorrelation { } histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) { continue; } if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) { @@ -4670,7 +4655,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 0.0f); // step 1: before all selections } @@ -4678,7 +4663,7 @@ struct HStrangeCorrelation { } for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } static_for<0, 7>([&](auto i) { @@ -4740,7 +4725,7 @@ struct HStrangeCorrelation { if (collisions.size() > 1) { for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } if (std::abs(mcParticle.y()) > ySel) { @@ -4807,7 +4792,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 1.0f); // step 2: before all selections } @@ -4819,7 +4804,7 @@ struct HStrangeCorrelation { const auto bestCollisionTracks = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } double geta = mcParticle.eta(); @@ -4840,7 +4825,7 @@ struct HStrangeCorrelation { } } - if (mcParticle.pdgCode() == PDG_t::kLambda0 && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0 && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -4861,7 +4846,7 @@ struct HStrangeCorrelation { } } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -4974,7 +4959,7 @@ struct HStrangeCorrelation { // // Deliberately a local counter rather than a member: the shared mCounter // only gets its PDG database wired up when processPrediction runs, and its - // mSelectPrimaries follows doAssocPhysicalPrimary. Adding a second counter + // mSelectPrimaries follows masterConfigurations.doAssocPhysicalPrimary. Adding a second counter // as a task member is not an option either -- the struct is already at the // member limit that Framework/StructToTuple.h can destructure. The object // is a bool and a pointer, so building it per MC collision costs nothing. @@ -5046,7 +5031,7 @@ struct HStrangeCorrelation { if (mcParticle.pdgCode() == PDG_t::kK0Short && genPt >= axisRanges[2][0] && genPt <= axisRanges[2][1] && - (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { // Same findability definition the stage ladder uses: decayed to // pi+ pi- with both charged daughters inside the tracking acceptance. // A K0 that is not findable could never have been reconstructed, so @@ -5212,7 +5197,7 @@ struct HStrangeCorrelation { truthTriggers.push_back(leadingTriggerCopy); } auto truthK0s = comparisonTruthK0s; - if (doAssocPhysicalPrimary) { + if (masterConfigurations.doAssocPhysicalPrimary) { std::erase_if(truthK0s, [](auto const& k0) { return !k0.physicalPrimary; }); } @@ -5303,7 +5288,7 @@ struct HStrangeCorrelation { if (isPairLossTriggerPdg(mcParticle.pdgCode()) && (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { genTriggerIndices.push_back(iteratorNumber); } - if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short && (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short && (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { genK0Indices.push_back(iteratorNumber); } } @@ -5396,7 +5381,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -5684,7 +5669,7 @@ struct HStrangeCorrelation { } if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || - (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { continue; } PVQualityK0 pvK0{mcParticle.phi(), -1, -1}; @@ -6016,7 +6001,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -6048,7 +6033,7 @@ struct HStrangeCorrelation { if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || - (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { continue; } PairLossTruthK0Info truthK0{ @@ -6555,7 +6540,7 @@ struct HStrangeCorrelation { negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; } passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && - (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; @@ -6761,7 +6746,7 @@ struct HStrangeCorrelation { if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || - (doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { continue; } @@ -6908,7 +6893,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 0.0f); // step 1: no event selection whatsoever } @@ -6999,7 +6984,8 @@ struct HStrangeCorrelation { } } histos.fill(HIST("hClosureTestEventCounter"), 1.5f); - + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({bestCollisionVtxZ, bestCollisionCentpercentile}); for (auto const& mcParticle : mcParticles) { double geta = mcParticle.eta(); if (std::abs(geta) > etaSel) { @@ -7012,7 +6998,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 1.0f); // step 2: after event selection } @@ -7020,6 +7006,9 @@ struct HStrangeCorrelation { } int iteratorNum = -1; + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions[binnumb].emplace_back(); + } for (auto const& mcParticle : mcParticles) { iteratorNum = iteratorNum + 1; double geta = mcParticle.eta(); @@ -7032,18 +7021,25 @@ struct HStrangeCorrelation { if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { triggerIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile, gphi); + if (masterConfigurations.mixingEventForClosureTest) { + ValidTriggerInGen TriggerInGen; + TriggerInGen.pt = gpt; + TriggerInGen.eta = geta; + TriggerInGen.phi = gphi; + genLevelCollisions[binnumb].back().emplace_back(TriggerInGen); + } if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); @@ -7156,6 +7152,33 @@ struct HStrangeCorrelation { } }); } + if (!masterConfigurations.mixingEventForClosureTest) { + return; + } + for (std::size_t iCollision = 0; iCollision < genLevelCollisions[binnumb].size() - 1; iCollision++) { + for (std::size_t iTrigger = 0; iTrigger < genLevelCollisions[binnumb][iCollision].size(); iTrigger++) { + auto triggerParticle = genLevelCollisions[binnumb][iCollision][iTrigger]; + double getatrigger = triggerParticle.eta; + double gphitrigger = triggerParticle.phi; + double pttrigger = triggerParticle.pt; + static_for<0, 8>([&](auto i) { // associated loop + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaeta = getatrigger - getaassoc; + if (TESTBIT(doCorrelation, i)) { + histos.fill(HIST("ClosureTest/mixedEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } + }); + } + } + if (genLevelCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter) + 1) { + genLevelCollisions[binnumb].erase(genLevelCollisions[binnumb].begin()); + } } void processFeedDown(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::McParticles const&, V0DatasWithoutTrackXMC const&, TracksComplete const&, aod::BCsWithTimestamps const&) @@ -7387,13 +7410,13 @@ struct HStrangeCorrelation { } } if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { assocHadronIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); } } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { piIndices.emplace_back(iteratorNum); histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); From d7fbcbf4ef9e9378eaf5e305a3ce757f40a1ae3d Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Sat, 31 Oct 2026 00:47:57 +0800 Subject: [PATCH 509/648] [PWGCF] Add internal validation and new cuts (#18522) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 197 +++++++++++++----- 1 file changed, 148 insertions(+), 49 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index 4aa84fc3666..c91ee896e11 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include @@ -48,13 +50,13 @@ #include #include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::constants; -using namespace std; // Definitions of join tables for Run 3 analysis: using EventSelection = soa::Join; @@ -124,6 +126,9 @@ enum EParticleCuts { ePtCut = 0, eEtaCut, eChargeCut, + eTPCNumClusterCut, + eDCAxyCut, + eDCAzCut, EParticleCuts_N }; @@ -141,6 +146,7 @@ enum EEventHistograms { eHistVertexY, eHistVertexZ, eHistImpactParameter, + eHistNumContrib, eEventHistograms_N }; @@ -153,9 +159,9 @@ enum EParticleHistograms { }; enum EWeightsHistograms { - ePhiRec = 0, - ePhiSim, - ePt, + eWPhiRec = 0, + eWPhiSim, + eWPt, eWeightsHistograms_N }; @@ -171,24 +177,33 @@ enum EObservables { eObservables_N }; +enum EIntValHarmonics { + eIV2 = 0, + eIV3, + eIV4, + eIntValHarmonics_N +}; + // *) Lists of names: static constexpr std::array CentralityEstimatorNames = {"FT0C", "FT0M", "FV0A", "NTPV"}; - static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; static constexpr std::array ParticleHistTitles = {"p_{T}", "#varphi", "#eta", "Particle charge"}; static constexpr std::array ParticleHistNames = {"eHistPt", "eHistPhi", "eHistEta", "eHistCharge"}; -static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter"}; -static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter"}; +static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter", "Number of Contributor"}; +static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter", "eHistNumContrib"}; -static constexpr std::array WeightsNames = {"ePhiRec", "ePhiSim", "ePt"}; +static constexpr std::array WeightsNames = {"eWPhiRec", "eWPhiSim", "eWPt"}; static constexpr std::array CutsNames = {"eBefore", "eAfter"}; static constexpr std::array CutsTitles = {"before", "after"}; static constexpr std::array ObservablesNames = {"eV22", "eV22Gap", "eV24"}; -static constexpr std::array ObservablesTitles = {"(v_{2}{2})^{2}", "(v_{2}{2})^{2} with eta gap", "(v_{2}{4})^{4}"}; +static constexpr std::array ObservablesTitles = {"v_{2}^{2}", "v_{2}^{2} with eta gap", "v_{2}^{4}"}; + +static constexpr std::array IntValNames = {"eIV2", "eIV3", "eIV4"}; +static constexpr std::array IntValTitles = {"v_{2}^{2}", "v_{3}^{2}", "v_{4}^{2}"}; // *) Main task: struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root @@ -214,7 +229,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; Configurable> cfMultBins{"cfMultBins", {400, 0., 25000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfRefMultBins{"cfRefMultBins", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; - Configurable> cfContribBins{"cfContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; + Configurable> cfNumContribBins{"cfNumContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; @@ -234,15 +249,20 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; // particle level cuts - Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge"}, "particle cuts switch, on and off by the first number before name"}; + Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge", "1TPCNumCluster", "1DCAxy", "1DCAz"}, "particle cuts switch, on and off by the first number before name"}; Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}[GeV/c], with convention: min <= pt <= max"}; Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; + Configurable> cfTPCNumClusterCutRange{"cfTPCNumClusterCutRange", {70, 160}, "range of cut on the number of clusters associated with a reconstructed track: {min, max}, with convention: min <= TPCNcls <= max"}; + Configurable> cfDCAxyCutRange{"cfDCAxyCutRange", {-3.2, 3.2}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in x-y plane: {min, max}, with convention: min <= dcaXY <= max"}; + Configurable> cfDCAzCutRange{"cfDCAzCutRange", {-2.4, 2.4}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in z direction: {min, max}, with convention: min <= dcaZ <= max"}; // *) Misc Configurable cfEtaGap{"cfEtaGap", 1.0, "eta gap for two particle correlation"}; Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; Configurable cfQualityAssuranceSwitch{"cfQualityAssuranceSwitch", false, "quality assurance switch"}; Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; + Configurable cfInternalValidationSwitch{"cfInternalValidationSwitch", false, "internal validation switch"}; + Configurable> cfIVHarmonics{"cfIVHarmonics", {0.06, 0.07, 0.08}, "harmonics for internal validation: {v_2, v_3, v_4, ...}"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: @@ -250,12 +270,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to float fSigmaInel = 7.71; float fEtaGap = 1.0; - std::vector fEventCutSwitch{}; + std::vector fEventCutSwitch; std::vector fVertexZCutRange = {-10., 10.}; std::vector fCentCutRange = {0., 80.}; - std::vector fParticleCutSwitch{}; + std::vector fParticleCutSwitch; std::vector fPtCutRange = {0.2, 5.}; std::vector fEtaCutRange = {-0.8, 0.8}; + std::vector fTPCNumClusterCutRange = {70, 160}; + std::vector fDCAxyCutRange = {-3.2, 3.2}; + std::vector fDCAzCutRange = {-2.4, 2.4}; + + std::vector fIVHarmonics; std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; @@ -268,6 +293,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to bool fMasterCutSwitch = true; bool fQualityAssuranceSwitch = false; bool fRunMessageSwitch = false; + bool fInternalValidationSwitch = false; std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. } tc; @@ -305,6 +331,13 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::array fMiscParticleCutsCounter{}; //! [reco,sim] } misc; + struct InternalValidation { + TRandom3* randGen = new TRandom3(0); + TF1* fIntValFun = nullptr; + TList* fIntValList = nullptr; + std::array fProfIntVal{}; + } intval; + struct RunTimeInfo { bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash int collisionCounter = 0; @@ -328,11 +361,9 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 const& weight) { - const int maxHarmonic = QVectorsTable.size(); - const int maxPower = QVectorsTable.empty() ? 0 : QVectorsTable[0].size(); - for (int h = 0; h < maxHarmonic; ++h) { - for (int p = 0; p < maxPower; ++p) { - const auto wp = std::pow(weight, p); + for (std::size_t h = 0; h < QVectorsTable.size(); ++h) { + for (std::size_t p = 0; p < QVectorsTable[h].size(); ++p) { + const auto wp = std::pow(weight, static_cast(p)); QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); } } @@ -528,7 +559,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to for (int i = 0; i < eWeightsHistograms_N; ++i) { if constexpr (rs == eRec) { - if (i == ePhiSim) { + if (i == eWPhiSim) { continue; } } @@ -680,6 +711,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eBefore]->Fill(collision.numContrib()); } if constexpr (cuts == eAfter) { ec.fEventHist[eHistMult][eRec][eAfter]->Fill(multiplicityRec); @@ -688,6 +720,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eAfter]->Fill(collision.numContrib()); } } @@ -738,6 +771,15 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to case eChargeCut: doNotPassCut = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; } } // end of if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { @@ -770,6 +812,15 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } // end of if (tc.fParticleCutSwitch[eChargeCut]) { break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; } } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { @@ -921,36 +972,46 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::vector harmonic22 = {-2, 2}; std::vector harmonic24 = {-2, -2, 2, 2}; - auto qVectorsTableReal = initQVectorsTable(4, harmonic24); - auto qVectorsTableMC = initQVectorsTable(4, harmonic24); + auto qVecTableReal = initQVectorsTable(4, harmonic24); + auto qVecTableMC = initQVectorsTable(4, harmonic24); + auto qVecTableIV = initQVectorsTable(4, harmonic24); std::vector qVecABReal(2, TComplex(0., 0.)); std::vector qVecABMC(2, TComplex(0., 0.)); std::vector weightABReal(2, 0.f); std::vector weightABMC(2, 0.f); + if (tc.fInternalValidationSwitch) { + double psi = intval.randGen->Uniform(0, math::TwoPI); + intval.fIntValFun->SetParameter(0, psi); + } + // Main loop over particles: for (auto const& track : tracks) { float thisPhiRec = track.phi(); float thisPt = track.pt(); + if (tc.fInternalValidationSwitch && passEventCutsReal) { + const float thisPhiIV = intval.fIntValFun->GetRandom(); + updateQVectorsTable(qVecTableIV, thisPhiIV, 1.f); + } + std::array thisPhiAndPt = {thisPhiRec, 0.f, thisPt}; // {thisPhiRec, thisPhiSim, thisPt} std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} particleHistFill(track); if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { particleHistFill(track); - if (tc.fExternalFileSwitch) { for (int k = 0; k < eWeightsHistograms_N; ++k) { - if (k != ePhiSim && ex.fWeights[k]) { + if (k != eWPhiSim && ex.fWeights[k]) { thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); } } } - auto thisWeightReal = thisWeights[ePhiRec] * thisWeights[ePt]; - updateQVectorsTable(qVectorsTableReal, thisPhiRec, thisWeightReal); + float thisWeightReal = thisWeights[eWPhiRec] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableReal, thisPhiRec, thisWeightReal); auto thisEtaRec = track.eta(); updateVecForGap(harmonic22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); } @@ -962,28 +1023,28 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto thisPhiSim = track.mcParticle().phi(); auto thisEtaSim = track.mcParticle().eta(); - thisPhiAndPt[ePhiSim] = thisPhiSim; - if (tc.fExternalFileSwitch && ex.fWeights[ePhiSim]) { - thisWeights[ePhiSim] = ex.fWeights[ePhiSim]->GetBinContent(ex.fWeights[ePhiSim]->FindBin(thisPhiSim)); + thisPhiAndPt[eWPhiSim] = thisPhiSim; + if (tc.fExternalFileSwitch && ex.fWeights[eWPhiSim]) { + thisWeights[eWPhiSim] = ex.fWeights[eWPhiSim]->GetBinContent(ex.fWeights[eWPhiSim]->FindBin(thisPhiSim)); } - auto thisWeightSim = thisWeights[ePhiSim] * thisWeights[ePt]; - updateQVectorsTable(qVectorsTableMC, thisPhiSim, thisWeightSim); + float thisWeightSim = thisWeights[eWPhiSim] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableMC, thisPhiSim, thisWeightSim); updateVecForGap(harmonic22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); } - } + } // end of if constexpr (rs == eRecAndSim) { } // end of for (auto const& track : tracks) { - std::vector result22(2, TComplex(0., 0.)); - std::vector result24(2, TComplex(0., 0.)); - std::vector result22Gap(2, 0.f); if (tc.fMasterCutSwitch) { + std::vector result22(2, TComplex(0., 0.)); + std::vector result24(2, TComplex(0., 0.)); + std::vector result22Gap(2, 0.f); if (passEventCutsReal) { - result22 = two(qVectorsTableReal, harmonic22); + result22 = two(qVecTableReal, harmonic22); if (noneZeroDenom(result22)) { obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); } - result24 = recursion(4, qVectorsTableReal, harmonic24); + result24 = recursion(4, qVecTableReal, harmonic24); if (noneZeroDenom(result24)) { obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); } @@ -997,11 +1058,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to auto impactParam = collision.mcCollision().impactParameter(); auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; - result22 = two(qVectorsTableMC, harmonic22); + result22 = two(qVecTableMC, harmonic22); if (noneZeroDenom(result22)) { obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); } - result24 = recursion(4, qVectorsTableMC, harmonic24); + result24 = recursion(4, qVecTableMC, harmonic24); if (noneZeroDenom(result24)) { obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); } @@ -1011,7 +1072,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); } } // end of if constexpr (rs == eRecAndSim) { - } + + if (tc.fInternalValidationSwitch) { + std::vector> harmonicIV = {{-2, 2}, {-3, 3}, {-4, 4}}; + for (int iv = 0; iv < eIntValHarmonics_N; ++iv) { + std::vector result = two(qVecTableIV, harmonicIV[iv]); + if (noneZeroDenom(result)) { + intval.fProfIntVal[iv]->Fill(thisCollCentReal, (result[0] / result[1].Re()).Re()); + } + } + } // end of if (tc.fInternalValidationSwitch) { + } // end of if (passEventCutsReal) { } // end of if (tc.fMasterCutSwitch) { rti.isFirstCollision = false; // Now the first collision ends @@ -1057,11 +1128,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fParticleCutSwitch = cfParticleCutSwitch.value; tc.fPtCutRange = cfPtCutRange.value; tc.fEtaCutRange = cfEtaCutRange.value; + tc.fTPCNumClusterCutRange = cfTPCNumClusterCutRange.value; + tc.fDCAxyCutRange = cfDCAxyCutRange.value; + tc.fDCAzCutRange = cfDCAzCutRange.value; tc.fEtaGap = cfEtaGap; tc.fSigmaInel = cfSigmaInel; tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; tc.fRunMessageSwitch = cfRunMessageSwitch; + tc.fInternalValidationSwitch = cfInternalValidationSwitch; + tc.fIVHarmonics = cfIVHarmonics.value; // *) Book base list: auto* temp = new TList(); @@ -1140,7 +1216,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!tc.fMasterCutSwitch && ba == eAfter) { continue; } - pc.fParticleHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s distribution for reconstructed particles %s cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Reconstructed Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); pc.fParticleHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); pc.fParticleHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][ba]); @@ -1155,7 +1231,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!tc.fMasterCutSwitch && ba == eAfter) { continue; } - pc.fParticleHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s distribution for simulated particles %s cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Simulated Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); pc.fParticleHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); pc.fParticleHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][ba]); @@ -1178,6 +1254,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to lEventHistBins[eHistVertexY] = cfVyBins.value; lEventHistBins[eHistVertexZ] = cfVzBins.value; lEventHistBins[eHistImpactParameter] = cfIpBins.value; + lEventHistBins[eHistNumContrib] = cfNumContribBins.value; if (doprocessRec || doprocessRecSim) { for (int i = 0; i < eEventHistograms_N; ++i) { @@ -1189,7 +1266,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!tc.fMasterCutSwitch && ba == eAfter) { continue; } - ec.fEventHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (reconstructed) %s cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Reconstructed) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); ec.fEventHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); ec.fEventHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); ec.fEventHistList->Add(ec.fEventHist[i][eRec][ba]); @@ -1203,7 +1280,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessSim || doprocessRecSim) { for (int i = 0; i < eEventHistograms_N; ++i) { - if (i == eHistRefMult) { // no RefMult for Sim + if (i == eHistRefMult || i == eHistNumContrib) { // no RefMult for Sim continue; } const auto& bins = lEventHistBins[i]; @@ -1211,7 +1288,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (!tc.fMasterCutSwitch && ba == eAfter) { continue; } - ec.fEventHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (simulated) %s cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Simulated) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); ec.fEventHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); ec.fEventHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); ec.fEventHistList->Add(ec.fEventHist[i][eSim][ba]); @@ -1232,10 +1309,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessRec || doprocessRecSim) { for (int i = 0; i < eObservables_N; ++i) { - obs.fProfCorr[i][eRec] = new TProfile(Form("[%s][eRec]", ObservablesNames[i]), Form("%s for reconstructed data after cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eRec] = new TProfile(Form("[%s][eRec]", ObservablesNames[i]), Form("%s for Reconstructed Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); obs.fProfCorr[i][eRec]->SetStats(false); obs.fProfCorr[i][eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); - obs.fProfCorr[i][eRec]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfCorr[i][eRec]->GetYaxis()->SetTitle(ObservablesTitles[i]); obs.fProfCorr[i][eRec]->Sumw2(); obs.fObservablesList->Add(obs.fProfCorr[i][eRec]); } @@ -1243,10 +1320,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if (doprocessSim || doprocessRecSim) { for (int i = 0; i < eObservables_N; ++i) { - obs.fProfCorr[i][eSim] = new TProfile(Form("[%s][eSim]", ObservablesNames[i]), Form("%s for simulated data after cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eSim] = new TProfile(Form("[%s][eSim]", ObservablesNames[i]), Form("%s for Simulated Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); obs.fProfCorr[i][eSim]->SetStats(false); obs.fProfCorr[i][eSim]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); - obs.fProfCorr[i][eSim]->GetYaxis()->SetTitle("#LT#LTk#GT#GT"); + obs.fProfCorr[i][eSim]->GetYaxis()->SetTitle(ObservablesTitles[i]); obs.fProfCorr[i][eSim]->Sumw2(); obs.fObservablesList->Add(obs.fProfCorr[i][eSim]); } @@ -1254,7 +1331,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Book and QA TLists: if (tc.fQualityAssuranceSwitch) { - std::vector lContrib = cfContribBins.value; + std::vector lContrib = cfNumContribBins.value; const int nBinsContrib = static_cast(lContrib[0]); const float minContrib = lContrib[1]; const float maxContrib = lContrib[2]; @@ -1284,6 +1361,28 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // end of if (doprocessRec || doprocessRecSim) { } // end of if (tc.fQualityAssuranceSwitch) { + // *) Book InternalValidation TLists: + if (tc.fInternalValidationSwitch) { + intval.fIntValFun = new TF1("fIntValFun", "1 + 2*0.04*cos(1*(x - [0])) + 2*[1]*cos(2*(x - [0])) + 2*[2]*cos(3*(x - [0])) + 2*[3]*cos(4*(x - [0]))", 0., math::TwoPI); + for (std::size_t i = 1; i <= tc.fIVHarmonics.size(); ++i) { + intval.fIntValFun->SetParameter(i, tc.fIVHarmonics[i - 1]); + } + + intval.fIntValList = new TList(); + intval.fIntValList->SetName("InternalValidation"); + intval.fIntValList->SetOwner(true); + fBaseList->Add(intval.fIntValList); + + for (int i = 0; i < eIntValHarmonics_N; ++i) { + intval.fProfIntVal[i] = new TProfile(Form("[%s]", IntValNames[i]), Form("Internal Validation for %s with True Value %f", IntValTitles[i], std::pow(tc.fIVHarmonics[i], 2)), nDefaultCentBins, defaultCentBoundaries.data()); + intval.fProfIntVal[i]->SetStats(false); + intval.fProfIntVal[i]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + intval.fProfIntVal[i]->GetYaxis()->SetTitle(IntValTitles[i]); + intval.fProfIntVal[i]->Sumw2(); + intval.fIntValList->Add(intval.fProfIntVal[i]); + } + } + } // end of void init(InitContext&) { // A) Process only reconstructed data: From 4df99bb57b206e431b79f47e927ad9d0b1fb4a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Tiek=C3=B6tter?= Date: Fri, 30 Oct 2026 19:48:55 +0100 Subject: [PATCH 510/648] [PWGDQ] Added additional D0 topology cuts and variables (#18369) --- PWGDQ/Core/CutsLibrary.cxx | 222 +++++++++++++++++++++++++++++-- PWGDQ/Core/HistogramsLibrary.cxx | 7 + PWGDQ/Core/MCSignalLibrary.cxx | 10 ++ PWGDQ/Core/VarManager.cxx | 6 + PWGDQ/Core/VarManager.h | 20 +++ 5 files changed, 257 insertions(+), 8 deletions(-) diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 50fdb3b019b..3527b285207 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -3702,6 +3702,16 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "pairD0Topology") { + cut->AddCut(GetAnalysisCut("pairD0Topology")); + return cut; + } + + if (nameStr == "pairD0TopologyTight") { + cut->AddCut(GetAnalysisCut("pairD0TopologyTight")); + return cut; + } + if (nameStr == "pairD0HighPt1") { cut->AddCut(GetAnalysisCut("pairLxyzProjected3sigma")); cut->AddCut(GetAnalysisCut("pairPtLow5")); @@ -3923,6 +3933,36 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3StandardTrackD0") { + cut->AddCut(GetAnalysisCut("alice3StandardKineD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3TOFEtaD0") { + cut->AddCut(GetAnalysisCut("alice3TOFEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3RICHEtaD0") { + cut->AddCut(GetAnalysisCut("alice3RICHEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3StandardTrackTOFAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineTOFAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3StandardTrackRICHAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineRICHAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + if (nameStr == "alice3TightDCATrack") { cut->AddCut(GetAnalysisCut("alice3StandardKine")); cut->AddCut(GetAnalysisCut("alice3TrackQualityTightDCA")); @@ -4005,6 +4045,32 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3EitherTOFPIDPi") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDPi", + "iTOF OR oTOF pion PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + + delete cut; + return pidCut; + } + + if (nameStr == "alice3EitherTOFPIDKa") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDKa", + "iTOF OR oTOF kaon PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); + + delete cut; + return pidCut; + } + if (nameStr == "alice3RICHPIDEl") { cut->AddCut(GetAnalysisCut("alice3RICHPIDEl")); return cut; @@ -4108,6 +4174,32 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3TOFPIDPi") { + cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + return cut; + } + + if (nameStr == "alice3TOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonTOFPID")); + return cut; + } + + if (nameStr == "alice3TOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionRICHTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonRICHTOFPID")); + return cut; + } + delete cut; LOGF(fatal, Form("Did not find cut %s. Returning nullptr", cutName)); return nullptr; @@ -7083,6 +7175,35 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "pairD0Topology") { + cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03); + cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); + cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); + cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); + return cut; + } + + if (nameStr == "pairD0TopologyTight") { + cut->AddCut(VarManager::kCosPointingAngle, 0.8, 1.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.85, 1.0, false, VarManager::kPt, 4.0, 50.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.035, 1000.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03, false, VarManager::kPt, 1.0, 50.0); + cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); + cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); + cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); + cut->AddCut(VarManager::kPt1, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kPt1, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); + cut->AddCut(VarManager::kPt1, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); + cut->AddCut(VarManager::kPt1, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); + cut->AddCut(VarManager::kPt2, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kPt2, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); + cut->AddCut(VarManager::kPt2, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); + cut->AddCut(VarManager::kPt2, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); + return cut; + } + if (nameStr == "pairJpsi") { cut->AddCut(VarManager::kMass, 2.8, 3.3); return cut; @@ -7339,6 +7460,35 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "alice3StandardKineD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.5, 2.5); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3TOFEta") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3RICHEta") { + cut->AddCut(VarManager::kEta, -0.8, 0.8); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3KineTOFAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); // TOF acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3KineRICHAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -0.8, 0.8); // TOF acceptance in v3b geomety + return cut; + } + if (nameStr == "alice3LambdaCKine") { cut->AddCut(VarManager::kPt, 0.2, 1000.0); cut->AddCut(VarManager::kEta, -2.0, 2.0); @@ -7398,42 +7548,98 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } if (nameStr == "alice3iTOFPIDEl") { - cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDPi") { - cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDKa") { - cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3iTOFPIDPr") { - cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } if (nameStr == "alice3oTOFPIDEl") { - cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDPi") { - cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDKa") { - cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } if (nameStr == "alice3oTOFPIDPr") { - cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0KaonTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0PionTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + return cut; + } + + if (nameStr == "alice3D0KaonRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.57, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3D0PionRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.57, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, true, VarManager::kP, 2.0, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); return cut; } diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 308f81c8425..8c359f59942 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -1417,6 +1417,13 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } hm->AddHistogram(histClass, "Mass_PtLong", "", false, 500, alice3MassBins.data(), VarManager::kMass, 20, pTBins.data(), VarManager::kPt); } + if (subGroupStr.Contains("alice3pair")) { + hm->AddHistogram(histClass, "CosinePointingAngle", "cos(#theta_{p})", false, 2000.0, -1.0, 1.0, VarManager::kCosPointingAngle); + hm->AddHistogram(histClass, "DCADaughters", "DCA_{dau}", false, 2000.0, -10.0, 10.0, VarManager::kD0DcaDaughters); + hm->AddHistogram(histClass, "ImpParProduct", "d_{k} #times d_{#pi}", false, 2000.0, -100.0, 100.0, VarManager::kD0ImpParProduct); + hm->AddHistogram(histClass, "DCAxy1", "DCA_{xy, 1}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy1); + hm->AddHistogram(histClass, "DCAxy2", "DCA_{xy, 2}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy2); + } if (subGroupStr.Contains("alice3pbpb")) { hm->AddHistogram(histClass, "Mass_Cent", "", false, 125, 0.0, 5.0, VarManager::kMass, 20, 0.0, 100.0, VarManager::kCent); hm->AddHistogram(histClass, "Pt_CentFT0C", "", false, 100, 0.0, 10.0, VarManager::kPt, 20, 0.0, 100.0, VarManager::kCent); diff --git a/PWGDQ/Core/MCSignalLibrary.cxx b/PWGDQ/Core/MCSignalLibrary.cxx index af5677d07d7..e6eae07011b 100644 --- a/PWGDQ/Core/MCSignalLibrary.cxx +++ b/PWGDQ/Core/MCSignalLibrary.cxx @@ -1754,6 +1754,16 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) signal = new MCSignal(name, "Kaons from D+/- decays", {prong}, {-1}); return signal; } + if (nameStr == "KFromD0") { + MCProng prong(2, {321, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Kaons from D0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "PiFromD0") { + MCProng prong(2, {211, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Pions from D0 decays", {prong}, {-1}); + return signal; + } if (nameStr == "LambdaC") { MCProng prong(1, {Pdg::kLambdaCPlus}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Lambda_c", {prong}, {-1}); diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index a42d1701326..af812d90158 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -1359,6 +1359,10 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kCosPointingAngle] = ""; fgVariableNames[kMCCosPointingAngle] = "MC cos(#theta_{pointing})"; fgVariableUnits[kMCCosPointingAngle] = ""; + fgVariableNames[kD0DcaDaughters] = "DCA of D0 daughters"; + fgVariableUnits[kD0DcaDaughters] = ""; + fgVariableNames[kD0ImpParProduct] = "D0 impact parameter product"; + fgVariableUnits[kD0ImpParProduct] = ""; fgVariableNames[kVertexingPz] = "Pz Pair"; fgVariableUnits[kVertexingPz] = "GeV/c"; fgVariableNames[kVertexingSV] = "Secondary Vertexing z"; @@ -2672,6 +2676,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kSignQuadDCAsigXY"] = kSignQuadDCAsigXY; fgVarNamesMap["kCosPointingAngle"] = kCosPointingAngle; fgVarNamesMap["kMCCosPointingAngle"] = kMCCosPointingAngle; + fgVarNamesMap["kD0ImpParProduct"] = kD0ImpParProduct; + fgVarNamesMap["kD0DcaDaughters"] = kD0DcaDaughters; fgVarNamesMap["kImpParXYJpsi"] = kImpParXYJpsi; fgVarNamesMap["kImpParXYK"] = kImpParXYK; fgVarNamesMap["kDCATrackProd"] = kDCATrackProd; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 87464d09990..6200d2f74f1 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -888,6 +888,8 @@ class VarManager : public TObject kQuadDCAsigXYZ, kSignQuadDCAsigXY, kCosPointingAngle, + kD0ImpParProduct, + kD0DcaDaughters, kImpParXYJpsi, kImpParXYK, kDCATrackProd, @@ -7966,6 +7968,14 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) if (fgUsedVars[kPairPhiv]) { values[kPairPhiv] = calculatePhiV(t1, t2); } + + if constexpr (pairType == kDecayToKPi) { + if constexpr ((fillMap & TrackDCA) > 0 || (fillMap & ReducedTrackBarrel) > 0) { + values[kDCAxy1] = t1.dcaXY(); + values[kDCAxy2] = t2.dcaXY(); + values[kD0ImpParProduct] = t1.dcaXY() * t2.dcaXY(); + } + } } template @@ -8070,7 +8080,17 @@ void VarManager::FillPairVertexingAlice3(C const& collision, T const& t1, T cons v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; v12 = v1 + v2; + + const auto pos1 = trackParVar0.getXYZGlo(); + const auto pos2 = trackParVar1.getXYZGlo(); + + const double dx = pos1.X() - pos2.X(); + const double dy = pos1.Y() - pos2.Y(); + const double dz = pos1.Z() - pos2.Z(); + + values[kD0DcaDaughters] = std::sqrt(dx * dx + dy * dy + dz * dz); } + double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); double theta = std::atan2(secondaryVertex[2] - collision.posZ(), std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + From b9fefcfbb84f8146c93801ecc88521edaf4db55b Mon Sep 17 00:00:00 2001 From: Pei-Ying Kuan <128475937+pykuan@users.noreply.github.com> Date: Sat, 31 Oct 2026 07:21:23 +0100 Subject: [PATCH 511/648] [PWGCF] add particle histograms and 2p correlation 2D profile (#18533) Co-authored-by: Pei-Ying Kuan --- .../Tasks/multiparticleCumulants.cxx | 152 ++++++++++-------- 1 file changed, 89 insertions(+), 63 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx index 4ea562dd5b0..83bdeeec514 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -111,13 +111,19 @@ static constexpr std::array EventHistNames = { enum EnParticleHistograms { ePt, + eEta, ePhi, + eDCAXY, + eDCAZ, eParticleHistograms_N }; static constexpr std::array ParticleHistNames = { "Pt", - "Phi"}; + "Eta", + "Phi", + "DCAXY", + "DCAZ"}; enum EnQAHistograms { eQACent, @@ -273,7 +279,11 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // *) Bins Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfEtaBins{"cfEtaBins", {1000, -1., 1.}, "nEtaBins, EtaMin, EtaMax"}; Configurable> cfPhiBins{"cfPhiBins", {1000, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfDCAXYBins{"cfDCAXYBins", {1000, -5., 5.}, "nDCAXYBins, DCAXYMin, DCAXYMax"}; + Configurable> cfDCAZBins{"cfDCAZBins", {1000, -5., 5.}, "nDCAZBins, DCAZMin, DCAZMax"}; + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCenBins, cenMin, cenMax"}; Configurable> cfFT0CMultBins{"cfFT0CMultBins", {100, 0., 60000.}, "nMultBins, MultMin, MultMax"}; Configurable> cfFT0MMultBins{"cfFT0MMultBins", {100, 0., 250000.}, "nMultBins, MultMin, MultMax"}; @@ -283,9 +293,11 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfVerZBins{"cfVerZBins", {100, -50., 50.}, "nVerZBins, VerZMin, VerZMax"}; Configurable> cfNumContribBins{"cfNumContribBins", {100, 0., 5000.}, "nNumContribBins, NumContribMin, NumContribMax"}; Configurable> cfNumTrackBins{"cfNumTrackBins", {100, 0., 5000.}, "nNumTrackBins, NumTrackMin, NumTrackMax"}; + Configurable> cfTwoParticleCorrBins{"cfTwoParticleCorrBins", {100, 0., 2e-3}, "nTwoParticleCorrBins, TwoParticleCorrMin, TwoParticleCorrMax"}; Configurable> cfFourParticleCorrBins{"cfFourParticleCorrBins", {100, 0., 1e-7}, "nFourParticleCorrBins, FourParticleCorrMin, FourParticleCorrMax"}; Configurable> cfSixParticleCorrBins{"cfSixParticleCorrBins", {100, 0., 1e-11}, "nSixParticleCorrBins, SixParticleCorrMin, SixParticleCorrMax"}; + Configurable> cfNumTrackTotalBins{"cfNumTrackTotalBins", {100, 0, 3000}, "nNumTrackTotalBins, NumTrackTotalMin, NumTrackTotalMax"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: @@ -335,7 +347,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fEtaGap = {0.4, 0.8, 1.0}; std::vector fPtBins = {1000, 0., 100.}; + std::vector fEtaBins = {100, -1., 1.}; std::vector fPhiBins = {1000, 0., o2::constants::math::TwoPI}; + std::vector fDCAXYBins = {100, -5., 5.}; + std::vector fDCAZBins = {100, -5., 5.}; std::vector fCentBins = {100, 0., 100.}; std::vector fMultBins = {100, 0, 5000}; @@ -351,6 +366,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fTwoParticleCorrBins = {100, 0, 2e-3}; std::vector fFourParticleCorrBins = {100, 0, 1e-7}; std::vector fSixParticleCorrBins = {100, 0, 1e-11}; + std::vector fNumTrackTotalBins = {100, 0, 3000}; std::string fFileWithWeights = "/scratch3/go52dab/O2tutorial/analysis_code/weights.root"; @@ -428,6 +444,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::array, eBeforeAfter_N>, eBeforeAfter_N> fFourParticleCorrelationHistograms{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fSixParticleCorrelationHistograms{}; std::array, NumEtaGap> fTwoParticleCorrelationGapHistograms{}; + std::array fTwoParticleCorrelationNumTrackProfiles{}; } mc; struct EventByEventQuantities { @@ -992,6 +1009,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fFourParticleCorrelationProfiles[i] = nullptr; mc.fSixParticleCorrelationProfiles[i] = nullptr; mc.fTwoParticleCorrelationGapProfiles[i].fill(nullptr); + mc.fTwoParticleCorrelationNumTrackProfiles[i] = nullptr; for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight mc.fTwoParticleCorrelationHistograms[i][j].fill(nullptr); @@ -1004,6 +1022,8 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fTwoParticleCorrelationProfiles[i] = new TProfile(Form("prof2%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationProfiles[i]->Sumw2(); + mc.fTwoParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof2NumTrack%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + mc.fTwoParticleCorrelationNumTrackProfiles[i]->Sumw2(); for (int n = 0; n < NumEtaGap; n++) { mc.fTwoParticleCorrelationGapProfiles[n][i] = new TProfile(Form("prof2%sCutGapped%.1f", BeforeAfterNames[i + 2], tc.fEtaGap[n]), Form("2-p correlation %s cut, %.1f gapped", BeforeAfterNames[i], tc.fEtaGap[n]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationGapProfiles[n][i]->Sumw2(); @@ -1011,6 +1031,13 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int k = 0; k < NumTwoPCorrBins; k++) { // v2, v3, v4 mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } for (int n = 0; n < NumEtaGap; n++) { mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); if (static_cast(i)) { @@ -1035,6 +1062,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationNumTrackProfiles[i]); for (int n = 0; n < NumEtaGap; n++) { mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationGapProfiles[n][i]); } @@ -1306,7 +1334,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill track histograms before cut: pc.fParticleHistograms[ePt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eBefore]->Fill(track.eta()); pc.fParticleHistograms[ePhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eBefore]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eBefore]->Fill(track.dcaZ()); // Calculating Q-vector before cut: dEta = track.eta(); @@ -1360,7 +1391,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill particle histograms after cut: pc.fParticleHistograms[ePt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eAfter]->Fill(track.eta()); pc.fParticleHistograms[ePhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eAfter]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eAfter]->Fill(track.dcaZ()); // Calculating Q-vector after cut: for (int h = 0; h < mcc.MaxHarmonic; h++) { @@ -1408,23 +1442,13 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill MC particle histograms before cut: pc.fParticleHistograms[ePt][eSim][eBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eBefore]->Fill(mcparticle.eta()); pc.fParticleHistograms[ePhi][eSim][eBefore]->Fill(mcparticle.phi()); if (ctParticleCuts(mcparticle)) { - // Fill pt MC sim histogram with this run number: - double diffPt = std::abs(track.pt() - mcparticle.pt()); - double diffPtMax = (tc.fPtBins[2] - tc.fPtBins[1]) / tc.fPtBins[0]; - if (diffPt < diffPtMax) { - wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); - } - // else { - // if (tc.fPrintSwitch) { - // LOGF(info, "|RecPt - SimPt| = %e > %e", diffPt, diffPtMax); - // } - // } - // Fill MC particle histograms after cut: pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eAfter]->Fill(mcparticle.eta()); pc.fParticleHistograms[ePhi][eSim][eAfter]->Fill(mcparticle.phi()); } } @@ -1435,8 +1459,11 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // Fill real event histograms (NumTrack) before cut: ev.fEventHistograms[eNumTrack][eRec][eBefore]->Fill(nTracksBefore); ev.fEventHistograms[eNumTrack][eRec][eAfter]->Fill(nTracksAfter); - bool bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; - // LOGF(info, "nTracksAfter = %d", nTracksAfter); + + bool bNumTrackCut = true; + if (tc.fNumTrackCutSwitch) { + bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + } // 2-p correlations: for (int i = 0; i < NumTwoPCorrBins; i++) { @@ -1456,6 +1483,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); mc.fTwoParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i]); mc.fTwoParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); + mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eBefore][i] > ebye.fTwoParticleCorrelationMaxEbye[eBefore][i]) { @@ -1482,6 +1510,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); mc.fTwoParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i]); mc.fTwoParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); + mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eAfter][i] > ebye.fTwoParticleCorrelationMaxEbye[eAfter][i]) { @@ -1662,8 +1691,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookParticleHistograms(T1 const& lPcBins) + void bookParticleHistograms(int histType, const std::vector>& lPcBins) { const auto& lPtBins = lPcBins[histType]; // define local array and initialize it from an array set in the configurables int nBinsPt = static_cast(lPtBins[0]); @@ -1691,8 +1719,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookEventHistograms(T1 const& lEvBins) + void bookEventHistograms(int histType, const std::vector>& lEvBins) { const auto& lCentBins = lEvBins[histType]; // define local array and initialize it from an array set in the configurables int nBinsCent = static_cast(lCentBins[0]); @@ -1706,10 +1733,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::string nameRecfull; std::string nameSimfull; - if constexpr (histType == eCent) { + if (histType == eCent) { nameRecfull = Form("%s %s distribution for reconstructed events", tc.fCentEstm.c_str(), EventHistNames[histType]); nameSimfull = Form("%s %s distribution for simulated events", tc.fCentEstm.c_str(), EventHistNames[histType]); - } else if constexpr (histType == eMult) { + } else if (histType == eMult) { nameRecfull = Form("%s %s distribution for reconstructed events", tc.fMultEstm.c_str(), EventHistNames[histType]); nameSimfull = Form("%s %s distribution for simulated events", tc.fMultEstm.c_str(), EventHistNames[histType]); } else { @@ -1724,7 +1751,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } if (doprocessSim || doprocessRecSim) { - if constexpr (histType != eNumContrib && histType != eMult) { + if (histType != eNumContrib && histType != eMult) { ev.fEventHistograms[histType][eSim][ba] = new TH1F(nameSim.c_str(), nameSimfull.c_str(), nBinsCent, minCent, maxCent); ev.fEventHistograms[histType][eSim][ba]->GetXaxis()->SetTitle(EventHistNames[histType]); ev.fEventHistogramsList->Add(ev.fEventHistograms[histType][eSim][ba]); @@ -1733,8 +1760,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookQAHistograms(T1 const& lQABins) + void bookQAHistograms(int histType, const std::vector>& lQABins) { int nBinsCentX = 0; float minCentX = 0.; @@ -1766,15 +1792,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::string name = Form("fHist%s[%s cut]", EventHistNames[histType], BeforeAfterNames[ba]); std::string namefull; - if constexpr (histType == eCent) { + if (histType == eCent) { namefull = Form("Quality assurance of %s %s", tc.fCentEstm.c_str(), EventHistNames[histType]); - } else if constexpr (histType == eMult) { + } else if (histType == eMult) { namefull = Form("Quality assurance of %s %s vs. NContributors", tc.fMultEstm.c_str(), EventHistNames[histType]); } else { namefull = Form("Quality assurance of %s", EventHistNames[histType]); } - if constexpr (histType == eMult) { + if (histType == eMult) { qa.fQAHistograms[histType][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsCentX, minCentX, maxCentX, nBinsCentY, minCentY, maxCentY); qa.fQAHistograms[histType][ba]->GetYaxis()->SetTitle("NContributors"); qa.fQAHistograms[histType][ba]->GetXaxis()->SetTitle("Reference multiplicity"); @@ -1787,24 +1813,22 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } - template - void bookCorrHistograms(T1 const& lCrBins) + void bookCorrHistograms(int histType, const std::vector>& lCrBins) { // (mult)(cent)(NContribMult) offset - constexpr int BoundOffset = histType == eCorrCent ? eCentABBound : histType == eCorrMult ? eMultABBound - : eNumContribMultBound; - + int boundOffset = histType == eCorrCent ? eCentABBound : histType == eCorrMult ? eMultABBound + : eNumContribMultBound; // (mult)(cent)(NContribMult) book functions: - if constexpr (histType == eCorrCent || histType == eCorrMult) { + if (histType == eCorrCent || histType == eCorrMult) { for (int i = 0; i < eEstmCorr_N; i++) { - int indexBound = BoundOffset + i; + int indexBound = boundOffset + i; cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s %s upper bound", EstmCorrName[i], CorrHistNames[histType])); cr.fCorrBounds[indexBound][1]->SetTitle(Form("%s %s lower bound", EstmCorrName[i], CorrHistNames[histType])); } - } else if constexpr (histType == eCorrNumContribMult) { - int indexBound = BoundOffset; + } else if (histType == eCorrNumContribMult) { + int indexBound = boundOffset; cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s", CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s", CorrHistNames[histType]), "[0] * x + [1]"); cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s upper bound", CorrHistNames[histType])); @@ -1828,13 +1852,13 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam float maxNumContrib = 0.; // (NContribMult) set functions and add to list: - if constexpr (histType == eCorrNumContribMult) { + if (histType == eCorrNumContribMult) { int indexBins = static_cast(histType) + static_cast(eMultEstm_N); // (NContribMult) get bins: const auto& lMultXBins = lCrBins[indexBins]; - nBinsXMult = static_cast(lMultXBins[0]); + // nBinsXMult = static_cast(lMultXBins[0]); minXMult = lMultXBins[1]; maxXMult = lMultXBins[2]; const auto& lNumContribBins = lCrBins[indexBins - 1]; @@ -1843,7 +1867,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam maxNumContrib = lNumContribBins[2]; // (NContribMult) set functions and add to list: - int indexBound = BoundOffset; + int indexBound = boundOffset; cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][0]->SetMinimum(0); @@ -1857,15 +1881,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } // (mult)(cent) Book histograms, set functions and add to list: - if constexpr (histType != eCorrNumContribMult) { + if (histType != eCorrNumContribMult) { for (int ba = 0; ba < eBeforeAfter_N; ba++) { std::string name; std::string namefull = Form("%s correlation %s cut", CorrHistNames[histType], BeforeAfterNames[ba]); int nEstm = 0; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { nEstm = eCentEstm_N; - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { nEstm = eMultEstm_N; } @@ -1874,17 +1898,17 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < nEstm; i++) { std::string titleX; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { titleX = Form("%s %s", CentEstmNames[i], CorrHistNames[histType]); - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { titleX = Form("%s %s", MultEstmNames[i], CorrHistNames[histType]); } for (int j = i + 1; j < nEstm; j++) { std::string titleY; - if constexpr (histType == eCorrCent) { + if (histType == eCorrCent) { name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], CentEstmNames[i], CentEstmNames[j], BeforeAfterNames[ba]); titleY = Form("%s %s", CentEstmNames[j], CorrHistNames[histType]); @@ -1900,7 +1924,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (cent) set functions and add to list: if (static_cast(ba)) { - int indexBound = BoundOffset + nComb; + int indexBound = boundOffset + nComb; cr.fCorrBounds[indexBound][0]->SetRange(minXCent, maxXCent); cr.fCorrBounds[indexBound][1]->SetRange(minXCent, maxXCent); cr.fCorrBounds[indexBound][0]->SetParameters(tc.fCentAllCorrCut[i][j][0], tc.fCentAllCorrCut[i][j][1]); @@ -1916,7 +1940,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (cent) book corr 2D histogram: cr.fCorrHistograms[histType][i][j][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsXCent, minXCent, maxXCent, nBinsYCent, minYCent, maxYCent); - } else if constexpr (histType == eCorrMult) { + } else if (histType == eCorrMult) { name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], MultEstmNames[i], MultEstmNames[j], BeforeAfterNames[ba]); titleY = Form("%s %s", MultEstmNames[j], CorrHistNames[histType]); @@ -1932,7 +1956,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (mult) set functions and add to list: if (static_cast(ba)) { - int indexBound = BoundOffset + nComb; + int indexBound = boundOffset + nComb; cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); cr.fCorrBounds[indexBound][0]->SetParameters(tc.fMultAllCorrCut[i][j][0], tc.fMultAllCorrCut[i][j][1]); @@ -2020,7 +2044,10 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fEtaGap = cfEtaGap; tc.fPtBins = cfPtBins; + tc.fEtaBins = cfEtaBins; tc.fPhiBins = cfPhiBins; + tc.fDCAXYBins = cfDCAXYBins; + tc.fDCAZBins = cfDCAZBins; tc.fCentBins = cfCentBins; tc.fFT0CMultBins = cfFT0CMultBins; @@ -2035,6 +2062,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fTwoParticleCorrBins = cfTwoParticleCorrBins; tc.fFourParticleCorrBins = cfFourParticleCorrBins; tc.fSixParticleCorrBins = cfSixParticleCorrBins; + tc.fNumTrackTotalBins = cfNumTrackTotalBins; tc.fPrintSwitch = cfPrintSwitch; tc.fFileWithWeights = cfFileWithWeights; @@ -2088,25 +2116,23 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fMultiparticleCorrelationProfilesList->SetOwner(kTRUE); fBaseList->Add(mc.fMultiparticleCorrelationProfilesList); - std::vector> lPcBins = {tc.fPtBins, tc.fPhiBins}; + std::vector> lPcBins = {tc.fPtBins, tc.fEtaBins, tc.fPhiBins, tc.fDCAXYBins, tc.fDCAZBins}; std::vector> lEvBins = {tc.fCentBins, tc.fMultBins, tc.fVerXBins, tc.fVerYBins, tc.fVerZBins, tc.fNumContribBins, tc.fNumTrackBins}; std::vector> lQABins = {tc.fCentBins, tc.fMultBins, tc.fNumContribBins}; std::vector> lCrBins = {tc.fCentBins, tc.fFT0CMultBins, tc.fFT0MMultBins, tc.fFV0AMultBins, tc.fNumContribBins, tc.fMultBins}; - bookParticleHistograms(lPcBins); - bookParticleHistograms(lPcBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookEventHistograms(lEvBins); - bookQAHistograms(lQABins); - bookQAHistograms(lQABins); - bookCorrHistograms(lCrBins); - bookCorrHistograms(lCrBins); - bookCorrHistograms(lCrBins); + for (int i = 0; i < eParticleHistograms_N; i++) { + bookParticleHistograms(i, lPcBins); + } + for (int i = 0; i < eEventHistograms_N; i++) { + bookEventHistograms(i, lEvBins); + } + for (int i = 0; i < eQAHistograms_N; i++) { + bookQAHistograms(i, lQABins); + } + for (int i = 0; i < eCorrHistograms_N; i++) { + bookCorrHistograms(i, lCrBins); + } wt.fDummyPhiWeightHistogram = new TH1F("fDummyPhiWeightHistogram", "Dummy phi weight histogram", static_cast(tc.fPhiBins[0]), tc.fPhiBins[1], tc.fPhiBins[2]); for (int i = 1; i <= wt.fDummyPhiWeightHistogram->GetNbinsX(); i++) { From a5c0b76af7c0c33cb36bbd0150be7643968df894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Sat, 31 Oct 2026 09:09:31 +0100 Subject: [PATCH 512/648] [Tutorial,PWGCF,PWGEM,PWGLF,PWGUD] Fix casts (#18394) --- Tutorials/ML/applyMlSelection.cxx | 2 +- Tutorials/OpenData/flowAnalysis.cxx | 4 ++-- Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx | 8 ++++---- Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx | 4 ++-- Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx | 4 ++-- Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx | 4 ++-- .../PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx | 2 +- Tutorials/PWGEM/Cocktail/plotLFCocktail.C | 6 +++--- Tutorials/PWGLF/Resonance/resonancesCombine.cxx | 4 ++-- Tutorials/PWGUD/UDTutorial_02b.cxx | 2 +- Tutorials/src/configurableObjects.cxx | 6 +++--- Tutorials/src/extendedTables.cxx | 2 +- Tutorials/src/reweighting.cxx | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Tutorials/ML/applyMlSelection.cxx b/Tutorials/ML/applyMlSelection.cxx index c2dc1662690..b457dfa61ce 100644 --- a/Tutorials/ML/applyMlSelection.cxx +++ b/Tutorials/ML/applyMlSelection.cxx @@ -81,7 +81,7 @@ struct applyMlSelection { void init(InitContext const&) { // Add histograms vs pT (only for selected candidates) - auto vbins = (std::vector)binsPtMl; + auto vbins = std::vector(binsPtMl); registry.add("hMassAfterSelVsPt", "Ds candidates;inv. mass (KK#pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{100, 1.77, 2.17}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPromptScoreAfterSelVsPt", "Prompt score after selection;BDT first score;entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/Tutorials/OpenData/flowAnalysis.cxx b/Tutorials/OpenData/flowAnalysis.cxx index 35bc2c2021c..6f71ff609c5 100644 --- a/Tutorials/OpenData/flowAnalysis.cxx +++ b/Tutorials/OpenData/flowAnalysis.cxx @@ -87,8 +87,8 @@ struct flow_base { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Filter collisionFilter = (aod::collision::flags & (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks) == (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks; - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + Filter collisionFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); void fillAPt(double trackpt, double cent, double vn, double sinHarm, double cosHarm) { diff --git a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx index d99a16fb996..8e2ef19f665 100644 --- a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx +++ b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx @@ -77,7 +77,7 @@ struct SpectatorPlaneTutorial { float dcaZMax = 2.0; Filter collisionFilter = nabs(aod::collision::posZ) < vtxZ; - Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; + Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; using GeneralCollisions = soa::Join; using UnfilteredTracks = soa::Join; @@ -182,9 +182,9 @@ struct SpectatorPlaneTutorial { // Get QQ-correlations from CCDB if (cfgCCDBdir_QQ.value.empty() == false) { TList* list = ccdb->getForTimeStamp(cfgCCDBdir_QQ.value, bc.timestamp()); - TProfile* qAqCX = reinterpret_cast(list->FindObject("qAqCX")); - TProfile* qAqCY = reinterpret_cast(list->FindObject("qAqCY")); - TProfile* qAqCXY = reinterpret_cast(list->FindObject("qAqCXY")); + TProfile* qAqCX = dynamic_cast(list->FindObject("qAqCX")); + TProfile* qAqCY = dynamic_cast(list->FindObject("qAqCY")); + TProfile* qAqCXY = dynamic_cast(list->FindObject("qAqCXY")); // The sum is qAqCXY corrQQx = qAqCX->GetBinContent(centrality); corrQQy = qAqCY->GetBinContent(centrality); diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx index 112b4d053b5..f31e4ad46d0 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask3 { // Partitions for particle 1 and particle 2 // add PID selection to partition definition - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx index fdfcf410147..28db4793c05 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask4 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx index be71808ecf5..24f5edeaf85 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx @@ -73,9 +73,9 @@ struct CFTutorialTask5 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx index 02d8c89d063..49dfc3e4a81 100644 --- a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx +++ b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx @@ -64,7 +64,7 @@ struct GfwTutorial { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90}, "centrality axis for histograms"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); // Connect to ccdb Service ccdb; diff --git a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C index 31974fe47d0..dcb56b9dfa1 100644 --- a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C +++ b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C @@ -34,8 +34,8 @@ void loadHistos(TFile* file, TH1F* hists[], const TString& name_extra, int rebin int nEvents) { for (int i = 0; i < nHists; i++) { - hists[i] = (TH1F*)file->GetDirectory("em-lmee-lf-cocktail") - ->Get(TString("mee") + name_extra + histNames[i]); + hists[i] = dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail") + ->Get(TString("mee") + name_extra + histNames[i])); hists[i]->Rebin(rebin); hists[i]->Scale(1. / nEvents); hists[i]->SetTitle(histLegends[i]); @@ -48,7 +48,7 @@ void plotLFCocktail(const TString& filename = "AnalysisResults.root", int rebin TFile* file = TFile::Open(filename.Data()); TH1I* hNEvents = - (TH1I*)file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents"); + dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents")); int nEvents = hNEvents->GetBinContent(1); loadHistos(file, mee, "", rebin, nEvents); diff --git a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx index 29b34cd6e44..6d45fc3dd7a 100644 --- a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx +++ b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx @@ -66,8 +66,8 @@ struct ResonanceCombine { double massKa = o2::constants::physics::MassKPlus; EventPlaneHelper helperEP; - int evtPlRefAId = static_cast(cfgEvtPl / 10000); - int evtPlRefBId = static_cast((cfgEvtPl - evtPlRefAId * 10000) / 100); + int evtPlRefAId = (cfgEvtPl / 10000); + int evtPlRefBId = ((cfgEvtPl - evtPlRefAId * 10000) / 100); int evtPlDetId = cfgEvtPl - evtPlRefAId * 10000 - evtPlRefBId * 100; void init(o2::framework::InitContext&) diff --git a/Tutorials/PWGUD/UDTutorial_02b.cxx b/Tutorials/PWGUD/UDTutorial_02b.cxx index 1d060b44f27..f80ccb1ccd1 100644 --- a/Tutorials/PWGUD/UDTutorial_02b.cxx +++ b/Tutorials/PWGUD/UDTutorial_02b.cxx @@ -62,7 +62,7 @@ struct UDTutorial02b { void init(InitContext&) { - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); if (verbose) { LOGF(info, ""); diff --git a/Tutorials/src/configurableObjects.cxx b/Tutorials/src/configurableObjects.cxx index a0941834e8d..ad02482613d 100644 --- a/Tutorials/src/configurableObjects.cxx +++ b/Tutorials/src/configurableObjects.cxx @@ -98,11 +98,11 @@ struct ConfigurableObjectDemo { LOGF(info, "max_eta: %f; min_clusters: %d", (float)trackcuts.max_eta, (int)trackcuts.min_clusters); LOGF(info, "Cut1 bins: %s; Cut2 bins: %s", printArray(cut->getBins()), printArray(mutable_cut->getBins())); LOGF(info, "Cut1 labels: %s; Cut2 labels: %s", printArray(cut->getLabels()), printArray(mutable_cut->getLabels())); - auto vec = (std::vector)array; + auto vec = std::vector(array); LOGF(info, "Array: %s", printArray(vec).c_str()); - auto dvec = (std::vector)darray; + auto dvec = std::vector(darray); LOGF(info, "Double array: %s", printArray(dvec).c_str()); - auto fvec = (std::vector)farray; + auto fvec = std::vector(farray); LOGF(info, "Float array: %s", printArray(fvec).c_str()); LOGF(info, "Matrix: %s", printMatrix((Array2D)vmatrix)); LOGF(info, "Labeled float array:\n %s\n %s\n %s", printArray(vla->getLabelsRows()), printArray(vla->getLabelsCols()), printMatrix(vla->getData())); diff --git a/Tutorials/src/extendedTables.cxx b/Tutorials/src/extendedTables.cxx index 96bf680ff28..574d9edb2d0 100644 --- a/Tutorials/src/extendedTables.cxx +++ b/Tutorials/src/extendedTables.cxx @@ -121,7 +121,7 @@ struct SpawnDynamicColumns { void init(InitContext&) { - cextable.projectors[0] = (float)factor * aod::track::p * aod::track::p; + cextable.projectors[0] = factor.value * aod::track::p * aod::track::p; } void process(aod::Tracks const& tracks) diff --git a/Tutorials/src/reweighting.cxx b/Tutorials/src/reweighting.cxx index b2622385cb3..4319a587858 100644 --- a/Tutorials/src/reweighting.cxx +++ b/Tutorials/src/reweighting.cxx @@ -107,7 +107,7 @@ struct CreateWeights { void init(InitContext&) { - auto path = (std::string)onnxModel; + auto path = std::string(onnxModel); /// create session onnxSession = std::make_shared(env, path.c_str(), sessionOptions); /// adjust input shape to use row-by-row model application From c5135fa0ffff9102e71d78a7911a53d939901ce8 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Sat, 31 Oct 2026 16:48:35 +0530 Subject: [PATCH 513/648] [PWGCF] Lambda-Proton-Balance-Function-8 (#18531) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 564 ++++++++++++++++-- 1 file changed, 509 insertions(+), 55 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 51b3a29335b..648464f6973 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -24,10 +24,12 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include +#include #include #include #include @@ -39,13 +41,17 @@ #include #include +#include #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -66,6 +72,9 @@ using MyTracks = soa::Join; +// MC-labelled versions used only by processMC (efficiency maps) +using MyTracksMC = soa::Join; +using MyV0sMC = soa::Join; namespace { @@ -99,6 +108,8 @@ struct LambdaProtonBalanceFunction { // Folder: ProtonVetoQA/ // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // MC efficiency maps (Gen*/Rec* TH3: x=cent, y=pT, z=y), filled only by processMC. Folder: Efficiency/ + HistogramRegistry registryEff{"Efficiency", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // ════════════════════════════════════════════════════════════════════════ // TaskState — ordinary C++ runtime/implementation state. @@ -178,10 +189,18 @@ struct LambdaProtonBalanceFunction { int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins float mRhoYMin = 0.f; float mRhoYMax = 0.f; + + // 1/efficiency maps (TH3: x=cent, y=pt, z=y); loaded once in init() when cApplyEffCorr is true + TH3* invEffL = nullptr; + TH3* invEffAL = nullptr; + TH3* invEffP = nullptr; + TH3* invEffAP = nullptr; }; TaskState state; + Service ccdb{}; + // ───────────────────────────────────────────────────────────────────── // ── Configurables ───────────────────────────────────────────────────── @@ -196,6 +215,11 @@ struct LambdaProtonBalanceFunction { ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; Configurable cMinCentrality{"cMinCentrality", 0.0f, "Reject events with centrality below this value (%)"}; Configurable cMaxCentrality{"cMaxCentrality", 80.0f, "Reject events with centrality above this value (%)"}; + Configurable cApplyEffCorr{"cApplyEffCorr", false, "Weight rho1/rho2 by 1/efficiency"}; + Configurable cEffFile{"cEffFile", "", "ROOT file with hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton (TH3: x=cent, y=pt, z=y). Used only if cEffCcdbPath is empty"}; + Configurable cEffCcdbPath{"cEffCcdbPath", "", "CCDB path of a TList holding hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton; if non-empty it is used instead of cEffFile"}; + Configurable cCcdbUrl{"cCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable cEffCcdbTimestamp{"cEffCcdbTimestamp", 1700000000000, "CCDB timestamp (ms) used to fetch the efficiency maps (must lie inside the object validity)"}; // Track quality cuts Configurable pProtonTPCMinRows{"pProtonTPCMinRows", 70, "Minimum TPC crossed rows"}; @@ -208,7 +232,7 @@ struct LambdaProtonBalanceFunction { Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // tight cut for cleanest primary-proton sample Configurable pProtonMaxDCAz{"pProtonMaxDCAz", 0.1f, "Max |DCAz| (cm)"}; - Configurable pProtonTPCTOFSwitchP{"pProtonTPCTOFSwitchP", 0.700000238f, "p threshold (GeV/c) above which TOF is used if available"}; + Configurable pProtonTPCTOFSwitchPt{"pProtonTPCTOFSwitchPt", 0.700000238f, "pT threshold (GeV/c) above which TOF is used if available"}; // Configurable cPPMinTofBeta{"cPPMinTofBeta", 0.4f, "Min TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented // Configurable cPPMaxTofBeta{"cPPMaxTofBeta", 0.96f, "Max TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented @@ -240,8 +264,6 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── // Proton Kinematic Cuts (from strange9) - Configurable pProtonMinP{"pProtonMinP", 0.5f, "Min p for primary protons (GeV/c)"}; - Configurable pProtonMaxP{"pProtonMaxP", 3.6f, "Max p for primary protons (GeV/c)"}; Configurable pProtonMinPt{"pProtonMinPt", 0.5f, "Min pT for primary protons (GeV/c)"}; Configurable pProtonMaxPt{"pProtonMaxPt", 3.6f, "Max pT for primary protons (GeV/c)"}; @@ -391,25 +413,36 @@ struct LambdaProtonBalanceFunction { } // Fill helper: fills h (if valid) at (a,b) only when both indices are in range. - static void fillIdx(std::shared_ptr const& h, int a, int b) + static void fillIdx(std::shared_ptr const& h, int a, int b, float w = 1.f) { if (h && a >= 0 && b >= 0) { - h->Fill(a + 0.5f, b + 0.5f); + h->Fill(a + 0.5f, b + 0.5f, w); } } + + // Efficiency weight w = 1/eff from the TH3 map (x=cent, y=pt, z=y). 1 when correction is off. + // (TH3* non-const: TH1::FindBin is a non-const method.) + float getEffWeight(TH3* h, float cent, float pt, float y) const + { + if (!cApplyEffCorr.value || !h) { + return 1.f; + } + const float v = h->GetBinContent(h->FindBin(cent, pt, y)); + return v > 0.f ? v : 0.f; // 0 means: particle is dropped (no efficiency info) + } // ───────────────────────────────────────────────────────────────────── // ── Two-region PID selection for (anti)protons ──────────────────────── template bool passesPrimProtonPid(TTrack const& trk) const { - const float pTPC = trk.tpcInnerParam(); // NOTE: pTPC, not trk.p() — matches reference exactly + const float pt = trk.pt(); const float nsPr = trk.tpcNSigmaPr(); if (std::abs(nsPr) >= pProtonTPCNsigma.value) { return false; } - if (pTPC < pProtonTPCTOFSwitchP.value) { + if (pt < pProtonTPCTOFSwitchPt.value) { return true; } if (!trk.hasTOF()) { @@ -420,11 +453,11 @@ struct LambdaProtonBalanceFunction { // Bitmask of contaminant hypotheses that REJECT the track (bit0 = pi, bit1 = K, bit2 = e). // A hypothesis rejects only if the track is inside its veto window AND closer to it than to the proton. - // Detector: TOF at/above the TPC/TOF switch momentum when TOF is available, TPC otherwise. + // Detector: TOF at/above the TPC/TOF switch pT when TOF is available, TPC otherwise. template int contaminantVetoMask(TTrack const& trk, bool& usedTOF) const { - usedTOF = cVetoUseTOF.value && trk.hasTOF() && trk.tpcInnerParam() >= pProtonTPCTOFSwitchP.value; + usedTOF = cVetoUseTOF.value && trk.hasTOF() && trk.pt() >= pProtonTPCTOFSwitchPt.value; const float nsP = std::abs(usedTOF ? trk.tofNSigmaPr() : trk.tpcNSigmaPr()); const float nsPi = std::abs(usedTOF ? trk.tofNSigmaPi() : trk.tpcNSigmaPi()); const float nsK = std::abs(usedTOF ? trk.tofNSigmaKa() : trk.tpcNSigmaKa()); @@ -481,7 +514,7 @@ struct LambdaProtonBalanceFunction { enum class ProtonQAStage : int { RawAfterTrackSel = 0, ///< after track-quality selection (TrackSelection table), before DCA cuts and PID TpcPid = 1, ///< after DCA + TPC PID cut passes (all momenta) - TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchP + TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchPt FinalSelected = 3 ///< entered selectedPrimProtons / selectedPrimAntiProtons }; @@ -1242,6 +1275,19 @@ struct LambdaProtonBalanceFunction { // ── Other / event-level histograms ─────────────────────────────────── registryOther.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); registryOther.add("hFT0MPercentile", ("Centrality percentile (" + centLabel + ")").c_str(), {HistType::kTH1F, {multAxis}}); + // ── MC efficiency maps (processMC): x = centrality (cCentBins), y = pT, z = y (same range/bins as rho1) ── + { + const AxisSpec axCent(cCentBins, "centrality (%)"); + const AxisSpec axPt{60, 0.0, 6.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "y"}; + for (const char* name : {"GenLambda", "GenAntiLambda", "GenProton", "GenAntiProton", + "RecLambda", "RecAntiLambda", "RecProton", "RecAntiProton"}) { + registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); + } + } + registryOther.add("hEffWeightProton", "Efficiency weight 1/#epsilon, protons", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightLambda", "Efficiency weight 1/#epsilon, #Lambda", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightZeroCount", "Particles with efficiency weight 0 (1=p, 2=#bar{p}, 3=#Lambda, 4=#bar{#Lambda})", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); registryOther.add("hNSelectedPrimProtons", "Number of selected primary protons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelectedPrimAntiProtons", "Number of selected primary antiprotons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelLambda", "Number of selected Lambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); @@ -2055,6 +2101,92 @@ struct LambdaProtonBalanceFunction { addY(S.ALAL_y, "h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); } } + + // ── Efficiency correction: load 1/eff maps once ────────────────────── + if (cApplyEffCorr.value) { + if (!cEffCcdbPath.value.empty()) { + // ── load from CCDB (TList of TH3) ── + ccdb->setURL(cCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + TList* effList = ccdb->getForTimeStamp(cEffCcdbPath.value, cEffCcdbTimestamp.value); + if (!effList) { + LOGF(fatal, "Cannot get efficiency TList from CCDB path %s (timestamp %" PRId64 ")", cEffCcdbPath.value.c_str(), static_cast(cEffCcdbTimestamp.value)); + } + auto loadMap = [&](const char* name) -> TH3* { + auto* obj = effList->FindObject(name); + if (!obj) { + LOGF(fatal, "Missing efficiency map %s in CCDB object", name); + } + TH3* h = dynamic_cast(obj->Clone()); + if (!h) { + LOGF(fatal, "Object %s in CCDB could not be cast to TH3*", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + } else { + // ── load from a ROOT file (fallback / local tests) ── + TFile* fEff = TFile::Open(cEffFile.value.c_str(), "READ"); + if (!fEff || fEff->IsZombie()) { + LOGF(fatal, "Cannot open efficiency file %s", cEffFile.value.c_str()); + } + auto loadMap = [&](const char* name) -> TH3* { + TH3* h = dynamic_cast(fEff->Get(name)); + if (!h) { + LOGF(fatal, "Missing efficiency map %s", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + fEff->Close(); + } + + // Sumw2 for all weighted fills (rho1, and every rho2 channel: Lp family, pp family, LL family) + registryRho.get(HIST("h2_rho1_Proton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiProton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_Lambda_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiLambda_y"))->Sumw2(); + state.hRho2_Lp_y_AllPt->Sumw2(); + state.hRho2_LAp_y_AllPt->Sumw2(); + state.hRho2_ALp_y_AllPt->Sumw2(); + state.hRho2_ALAp_y_AllPt->Sumw2(); + state.hRho2_pp_y_AllPt->Sumw2(); + state.hRho2_pAp_y_AllPt->Sumw2(); + state.hRho2_App_y_AllPt->Sumw2(); + state.hRho2_ApAp_y_AllPt->Sumw2(); + state.hRho2_LL_y_AllPt->Sumw2(); + state.hRho2_LAL_y_AllPt->Sumw2(); + state.hRho2_ALL_y_AllPt->Sumw2(); + state.hRho2_ALAL_y_AllPt->Sumw2(); + for (auto& S : state.centSets) { + S.rho1_Proton_y->Sumw2(); + S.rho1_AntiProton_y->Sumw2(); + S.rho1_Lambda_y->Sumw2(); + S.rho1_AntiLambda_y->Sumw2(); + S.Lp_y->Sumw2(); + S.LAp_y->Sumw2(); + S.ALp_y->Sumw2(); + S.ALAp_y->Sumw2(); + S.pp_y->Sumw2(); + S.pAp_y->Sumw2(); + S.App_y->Sumw2(); + S.ApAp_y->Sumw2(); + S.LL_y->Sumw2(); + S.LAL_y->Sumw2(); + S.ALL_y->Sumw2(); + S.ALAL_y->Sumw2(); + } + } } Filter eventFilter = (o2::aod::evsel::sel8 == true); @@ -2080,9 +2212,9 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - void process(soa::Filtered>::iterator const& collision, - aod::V0Datas const& V0s, - MyTracks const& tracks) + void processData(soa::Filtered>::iterator const& collision, + aod::V0Datas const& V0s, + MyTracks const& tracks) { // ── Ordered-pair convention ──────────────────────────────────────────── // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) @@ -2098,7 +2230,7 @@ struct LambdaProtonBalanceFunction { // Every channel fills y-space only (unrolledIndexY). // ───────────────────────────────────────────────────────────────────── - // ── Event cutflow bin 1 — enters process (after sel8 + posZ filters) ── + // ── Event cutflow bin 1 — enters processData (after sel8 + posZ filters) ── registryOther.fill(HIST("hEventCutflow"), 1.0f); if (collision.numContrib() < 1) { @@ -2553,9 +2685,14 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, dauPos); fillDaughterQA(DaughterQAStage::Final, dauNeg); } - registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi()); + const float wL = getEffWeight(state.invEffL, cent, v0.pt(), v0.rapidity(1)); + registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi(), wL); if (cs) { - cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi()); + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi(), wL); + } + registryOther.fill(HIST("hEffWeightLambda"), wL); + if (wL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 3.f); } registryOther.fill(HIST("hPtSelLambda"), v0.pt()); state.hPtSelLambda_AllPt->Fill(v0.pt()); @@ -2571,9 +2708,13 @@ struct LambdaProtonBalanceFunction { fillDaughterQA(DaughterQAStage::Final, dauNeg); fillDaughterQA(DaughterQAStage::Final, dauPos); } - registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi()); + const float wAL = getEffWeight(state.invEffAL, cent, v0.pt(), v0.rapidity(2)); + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi(), wAL); if (cs) { - cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi()); + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi(), wAL); + } + if (wAL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 4.f); } registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); @@ -2639,11 +2780,7 @@ struct LambdaProtonBalanceFunction { fillCutFlow(8.0f, trk); // ───────────────────────────────────────────────────────────────── - // ── p_TPC and pT acceptance cuts ───────────────────────────────────────────── - const float pTPC = trk.tpcInnerParam(); - if (pTPC < pProtonMinP.value || pTPC > pProtonMaxP.value) { - continue; - } + // ── pT acceptance cut ───────────────────────────────────────────── if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { continue; } @@ -2808,8 +2945,8 @@ struct LambdaProtonBalanceFunction { fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); // TOF QA only when track was accepted through the TPC+TOF branch: - // p > PProtonTPC_TOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC >= pProtonTPCTOFSwitchP.value) { + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { fillProtonTofQA(ProtonQAStage::FinalSelected, trk); } @@ -2821,8 +2958,8 @@ struct LambdaProtonBalanceFunction { fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); // TOF QA only when track was accepted through the TPC+TOF branch: - // p > pProtonTPCTOFSwitchP guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (pTPC >= pProtonTPCTOFSwitchP.value) { + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { fillProtonTofQA(ProtonQAStage::FinalSelected, trk); } @@ -2870,16 +3007,25 @@ struct LambdaProtonBalanceFunction { // ── Single-particle density for protons (y) ────────────────── for (auto const& primProton : selectedPrimProtons) { const float yp = protonRapidity(primProton); - registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi()); + const float wP = getEffWeight(state.invEffP, cent, primProton.pt(), yp); + registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi(), wP); if (cs) { - cs->rho1_Proton_y->Fill(yp, primProton.phi()); + cs->rho1_Proton_y->Fill(yp, primProton.phi(), wP); + } + registryOther.fill(HIST("hEffWeightProton"), wP); + if (wP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 1.f); } } for (auto const& primAntiProton : selectedPrimAntiProtons) { const float yap = protonRapidity(primAntiProton); - registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi()); + const float wAP = getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yap); + registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi(), wAP); if (cs) { - cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi()); + cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi(), wAP); + } + if (wAP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 2.f); } } // ───────────────────────────────────────────────────────────────────── @@ -2963,11 +3109,12 @@ struct LambdaProtonBalanceFunction { const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffP, cent, p1.pt(), y1_pp) * getEffWeight(state.invEffP, cent, p2.pt(), y2_pp); if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_pp_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); + state.hRho2_pp_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->pp_y, idxY1, idxY2); + fillIdx(cs->pp_y, idxY1, idxY2, wPair); } } } @@ -2981,11 +3128,12 @@ struct LambdaProtonBalanceFunction { if (failsQinvCut(qinvPAp)) { continue; } + const float wPair = getEffWeight(state.invEffP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_pAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); + state.hRho2_pAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->pAp_y, a.idxY, b.idxY); + fillIdx(cs->pAp_y, a.idxY, b.idxY, wPair); } } } @@ -2999,11 +3147,12 @@ struct LambdaProtonBalanceFunction { if (failsQinvCut(qinvApp)) { continue; } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_App_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); + state.hRho2_App_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->App_y, a.idxY, b.idxY); + fillIdx(cs->App_y, a.idxY, b.idxY, wPair); } } } @@ -3020,11 +3169,12 @@ struct LambdaProtonBalanceFunction { if (failsQinvCut(qinvApAp)) { continue; } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); if (a.idxY >= 0 && b.idxY >= 0) { - state.hRho2_ApAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f); + state.hRho2_ApAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); } if (cs) { - fillIdx(cs->ApAp_y, a.idxY, b.idxY); + fillIdx(cs->ApAp_y, a.idxY, b.idxY, wPair); } } } @@ -3136,11 +3286,12 @@ struct LambdaProtonBalanceFunction { if (idxYL >= 0 && idxYP >= 0 && bin >= 0) { state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP); if (idxYL >= 0 && idxYP >= 0) { - state.hRho2_Lp_y_AllPt->Fill(idxYL + 0.5f, idxYP + 0.5f); + state.hRho2_Lp_y_AllPt->Fill(idxYL + 0.5f, idxYP + 0.5f, wPair); } if (cs) { - fillIdx(cs->Lp_y, idxYL, idxYP); + fillIdx(cs->Lp_y, idxYL, idxYP, wPair); } ++nPairs_Lambda_PrimProton; } @@ -3218,11 +3369,12 @@ struct LambdaProtonBalanceFunction { if (idxYL2 >= 0 && idxYAp >= 0 && bin >= 0) { state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp); if (idxYL2 >= 0 && idxYAp >= 0) { - state.hRho2_LAp_y_AllPt->Fill(idxYL2 + 0.5f, idxYAp + 0.5f); + state.hRho2_LAp_y_AllPt->Fill(idxYL2 + 0.5f, idxYAp + 0.5f, wPair); } if (cs) { - fillIdx(cs->LAp_y, idxYL2, idxYAp); + fillIdx(cs->LAp_y, idxYL2, idxYAp, wPair); } ++nPairs_Lambda_PrimAntiProton; } @@ -3372,11 +3524,12 @@ struct LambdaProtonBalanceFunction { if (idxYAL >= 0 && idxYP2 >= 0 && bin >= 0) { state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP2); if (idxYAL >= 0 && idxYP2 >= 0) { - state.hRho2_ALp_y_AllPt->Fill(idxYAL + 0.5f, idxYP2 + 0.5f); + state.hRho2_ALp_y_AllPt->Fill(idxYAL + 0.5f, idxYP2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALp_y, idxYAL, idxYP2); + fillIdx(cs->ALp_y, idxYAL, idxYP2, wPair); } ++nPairs_AntiLambda_PrimProton; } @@ -3461,11 +3614,12 @@ struct LambdaProtonBalanceFunction { if (idxYAL2 >= 0 && idxYAp2 >= 0 && bin >= 0) { state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp2); if (idxYAL2 >= 0 && idxYAp2 >= 0) { - state.hRho2_ALAp_y_AllPt->Fill(idxYAL2 + 0.5f, idxYAp2 + 0.5f); + state.hRho2_ALAp_y_AllPt->Fill(idxYAL2 + 0.5f, idxYAp2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALAp_y, idxYAL2, idxYAp2); + fillIdx(cs->ALAp_y, idxYAL2, idxYAp2, wPair); } ++nPairs_AntiLambda_PrimAntiProton; } @@ -3575,11 +3729,12 @@ struct LambdaProtonBalanceFunction { const int idxY1 = unrolledIndexY(yv1L, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1L) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_LL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); + state.hRho2_LL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->LL_y, idxY1, idxY2); + fillIdx(cs->LL_y, idxY1, idxY2, wPair); } } } @@ -3625,11 +3780,12 @@ struct LambdaProtonBalanceFunction { const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1LAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_LAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); + state.hRho2_LAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->LAL_y, idxY1, idxY2); + fillIdx(cs->LAL_y, idxY1, idxY2, wPair); } } } @@ -3675,11 +3831,12 @@ struct LambdaProtonBalanceFunction { const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALL) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_ALL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); + state.hRho2_ALL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALL_y, idxY1, idxY2); + fillIdx(cs->ALL_y, idxY1, idxY2, wPair); } } } @@ -3728,11 +3885,12 @@ struct LambdaProtonBalanceFunction { const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); if (idxY1 >= 0 && idxY2 >= 0) { - state.hRho2_ALAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f); + state.hRho2_ALAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); } if (cs) { - fillIdx(cs->ALAL_y, idxY1, idxY2); + fillIdx(cs->ALAL_y, idxY1, idxY2, wPair); } } } @@ -3745,6 +3903,302 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nPairs_AntiLambda_PrimProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nPairs_AntiLambda_PrimAntiProton)); } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processData, "Process for Data", true); + + // ══════════════════════════════════════════════════════════════════════════ + // MC efficiency maps: Gen* from MC particles, Rec* from the reconstructed selection. + // Gen* use MC-truth pT and y (proton mass for p/pbar, Lambda mass for Lambda/AntiLambda). + // Rec* use RECONSTRUCTED pT and y, i.e. the same variables getEffWeight() queries in processData(). + // The loop is driven by MC collisions (exactly one reconstructed collision required) to avoid + // split-vertex double counting of generated particles. + // ══════════════════════════════════════════════════════════════════════════ + Preslice v0sPerCollision = aod::v0data::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; + + template + static float mcRapidity(TMc const& mc, float mass) + { + return RecoDecay::y(std::array{mc.px(), mc.py(), mc.pz()}, mass); + } + // Generator-level acceptance = same pT range and |y| cut as the reco selection of the species. + bool inLambdaAcc(float pt, float y) const + { + return pt > lambdaV0MinPt.value && pt < lambdaV0MaxPt.value && std::abs(y) < lambdaV0MaxY.value; + } + bool inProtonAcc(float pt, float y) const + { + return pt > pProtonMinPt.value && pt < pProtonMaxPt.value && std::abs(y) < pProtonMaxY.value; + } + + void processMC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& mcParticles) + { + // ── 1) Event association + selection (soa::Filtered does not apply to SmallGroups) ── + // Exactly one reconstructed collision per MC collision: removes split-vertex double counting. + if (collisions.size() != 1) { + return; + } + auto collision = collisions.begin(); + if (!collision.has_mcCollision() || collision.mcCollisionId() != mcCollision.globalIndex()) { + return; + } + // Same event selection as the Filters of processData(): sel8 + |posZ| cut + if (!collision.sel8() || std::abs(collision.posZ()) >= cPPandV0ZVertexCut.value) { + return; + } + // Slice V0s and tracks to this reconstructed collision + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + auto tracks = allTracks.sliceBy(tracksPerCollision, collision.globalIndex()); + + if (collision.numContrib() < 1) { + return; + } + const float cent = getCentrality(collision); // FT0C or FT0M according to cUseFT0C + if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { + return; + } + // INEL > 0 (same as processData()): at least one charged track in |eta| < 0.8 + { + int nTracksINEL = 0; + for (auto const& trk : tracks) { + if (std::abs(trk.eta()) < 0.8f) { + ++nTracksINEL; + } + } + if (nTracksINEL < 1) { + return; + } + } + + // ── 2) GENERATED: physical primaries of this MC collision (mcParticles is auto-sliced to mcCollision; + // NO V0-daughter veto at generator level) ── + for (auto const& mc : mcParticles) { + if (!mc.isPhysicalPrimary()) { + continue; + } + const int pdg = mc.pdgCode(); + if (pdg == 3122 || pdg == -3122) { + if (!mc.has_daughters()) { + continue; + } + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassLambda); + if (!inLambdaAcc(pt, y)) { + continue; + } + if (pdg == 3122) { + registryEff.fill(HIST("GenLambda"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiLambda"), cent, pt, y); + } + } else if (pdg == 2212 || pdg == -2212) { + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassProton); + if (!inProtonAcc(pt, y)) { + continue; + } + if (pdg == 2212) { + registryEff.fill(HIST("GenProton"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiProton"), cent, pt, y); + } + } + } + + // ══════════════════════════════════════════════════════════════════════ + // BLOCK COPIED FROM processData() — V0 (Lambda/AntiLambda) selection. + // The selection is embedded in processData(), so it is duplicated here with all QA fills removed. + // KEEP IN SYNC with processData() (Step 2 V0 loop + shared-daughter cleaning). + // ══════════════════════════════════════════════════════════════════════ + constexpr float mLambdaPDG = o2::constants::physics::MassLambda; + constexpr float mK0PDG = o2::constants::physics::MassK0Short; + std::unordered_set v0DaughterTrackIds; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + + for (const auto& v0 : V0s) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + + if (v0.v0Type() != cV0TypeSelection.value) { + continue; + } + if (v0PosDau.pt() <= cV0DauMinPt.value || v0NegDau.pt() <= cV0DauMinPt.value || + std::abs(v0PosDau.eta()) >= cV0DauMaxEta.value || std::abs(v0NegDau.eta()) >= cV0DauMaxEta.value || + v0PosDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value || + v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { + continue; + } + const bool v0DauPID_AsLambda = + (std::abs(v0PosDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value); + const bool v0DauPID_AsAntiLambda = + (std::abs(v0PosDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value); + if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { + continue; + } + if (v0DauPID_AsLambda && std::abs(v0.rapidity(1)) > lambdaV0MaxY.value) { + continue; + } + if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { + continue; + } + if (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value) { + continue; + } + const float dcaProton = v0DauPID_AsLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + if (!passTopoCuts(v0)) { + continue; + } + if (dcaProton < lambdaV0DaughterProtonMinDCAToPV.value || + dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { + continue; + } + if (ctau > lambdaV0MaxCTau.value) { + continue; + } + if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { + continue; + } + const bool selLambda = v0DauPID_AsLambda && (std::abs(v0.mLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + if (selLambda) { + selectedLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + } + if (selAntiLambda) { + selectedAntiLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + } + } + // Shared-daughter cleaning (same as processData()) + std::unordered_map posIdCount; + std::unordered_map negIdCount; + for (const auto& v : selectedLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + for (const auto& v : selectedAntiLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + std::vector cleanLambdas; + std::vector cleanAntiLambdas; + for (const auto& v : selectedLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanLambdas.push_back(v); + } + } + for (const auto& v : selectedAntiLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanAntiLambdas.push_back(v); + } + } + // ══════════════ END OF BLOCK COPIED FROM processData() (V0) ══════════════ + + // ── 4) RECONSTRUCTED Lambda / AntiLambda: true primary (anti)Lambda, filled in RECONSTRUCTED (pT, y) ── + for (const auto& v0 : cleanLambdas) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { + continue; + } + const auto mc = v0.mcParticle_as(); + if (!mc.isPhysicalPrimary() || mc.pdgCode() != 3122) { + continue; + } + registryEff.fill(HIST("RecLambda"), cent, v0.pt(), v0.rapidity(1)); + } + for (const auto& v0 : cleanAntiLambdas) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { + continue; + } + const auto mc = v0.mcParticle_as(); + if (!mc.isPhysicalPrimary() || mc.pdgCode() != -3122) { + continue; + } + registryEff.fill(HIST("RecAntiLambda"), cent, v0.pt(), v0.rapidity(2)); + } + + // ══════════════════════════════════════════════════════════════════════ + // BLOCK COPIED FROM processData() — primary (anti)proton selection (track loop, QA fills removed). + // Reuses passesPrimProtonPid() and passesContaminantVeto(); the remaining cuts are embedded + // in processData() and duplicated here. KEEP IN SYNC with processData(). + // ══════════════════════════════════════════════════════════════════════ + for (auto const& trk : tracks) { + if (!trk.hasTPC()) { + continue; + } + if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { + continue; + } + if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { + continue; + } + if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { + continue; + } + if (trk.itsNCls() < pProtonITSMinClusters.value) { + continue; + } + if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { + continue; + } + if (trk.tpcSignal() < cPPMinTpcSignal.value) { + continue; + } + if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { + continue; + } + if (std::abs(protonRapidity(trk)) > pProtonMaxY.value) { + continue; + } + if (!((std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value))) { + continue; + } + if (!passesPrimProtonPid(trk)) { + continue; + } + if (!passesContaminantVeto(trk)) { + continue; + } + // V0-daughter veto (same set as processData(): built from V0s selected before shared-daughter cleaning) + if (v0DaughterTrackIds.contains(trk.globalIndex())) { + continue; + } + // ══════════════ END OF BLOCK COPIED FROM processData() (protons) ══════════════ + + // ── 3) RECONSTRUCTED protons / antiprotons: true primary p / pbar of this MC collision ── + if (!trk.has_mcParticle()) { + continue; + } + const auto mc = trk.mcParticle_as(); + if (!mc.isPhysicalPrimary()) { + continue; + } + // Fill with RECONSTRUCTED kinematics (same variables as getEffWeight in processData()) + const float yReco = protonRapidity(trk); + if (trk.sign() > 0 && mc.pdgCode() == 2212) { + registryEff.fill(HIST("RecProton"), cent, trk.pt(), yReco); + } else if (trk.sign() < 0 && mc.pdgCode() == -2212) { + registryEff.fill(HIST("RecAntiProton"), cent, trk.pt(), yReco); + } + } + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMC, "Efficiency from MC", false); + // ───────────────────────────────────────────────────────────────────── + // ── Run-level summary printed once at the end of the analysis job ───── // Called automatically by the O2 framework after all events are processed. // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. From 750e0fd4fa20c3eb1019b0a85f7ed90a26047a3b Mon Sep 17 00:00:00 2001 From: Zhiyong Lu <71517277+Luzhiyongg@users.noreply.github.com> Date: Sat, 31 Oct 2026 19:19:14 +0800 Subject: [PATCH 514/648] [PWGCF] specify pT range for PtContainer and add safe check for cfgManualCk (#18534) --- PWGCF/Flow/Tasks/flowTask.cxx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index b9f78ad50ec..339482157d3 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -261,7 +261,7 @@ struct FlowTask { ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex) && (aod::cent::centFT0C > cfgCentFT0CMin) && (aod::cent::centFT0C < cfgCentFT0CMax); - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; // Filter for MCcollisions @@ -448,7 +448,7 @@ struct FlowTask { registry.add("hPtA_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); registry.add("hPtB_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); } - if (cfgAdditionObs.cfgManualCk) { + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { registry.add("ptpt_1sub1_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); registry.add("pt_1sub1_1pc", "", {HistType::kTProfile, {axisIndependent}}); registry.add("pt_1sub1_2pc", "", {HistType::kTProfile, {axisIndependent}}); @@ -1469,7 +1469,7 @@ struct FlowTask { weffEventWithinGap08 += weff; weffEventSquareWithinGap08 += weff * weff; } - if (cfgAdditionObs.cfgManualCk) { + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { if (track.eta() >= etagapsPtPt[0].first && track.eta() <= etagapsPtPt[0].second) { mpt_sub1 += weff * track.pt(); mpt_sub1_weffEv += weff; @@ -1496,7 +1496,10 @@ struct FlowTask { if (withinPtPOI && withinPtRef) fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); if (cfgAdditionObs.cfgNewPtContainer) { - fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (cfgUsePtRef && withinPtRef) + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (!cfgUsePtRef && withinPtPOI) + fFCpt->fill(track.eta(), weff, track.pt(), 1); if (withinPtRef) cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 1); if (withinPtPOI) @@ -1577,7 +1580,7 @@ struct FlowTask { weffEventDiffWithGap08); } - if (cfgAdditionObs.cfgManualCk) { + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { // 2pc event weight: double ev2pc = mpt_sub1_weffEv * mpt_sub3_weffEv; // manual pt-pt variance From aed5e531f0a7deca840a6eb85e5459cbfcbf18e1 Mon Sep 17 00:00:00 2001 From: Jonathan Witte <64792557+JonathanWitte@users.noreply.github.com> Date: Sat, 31 Oct 2026 12:45:42 +0100 Subject: [PATCH 515/648] [PWGDQ] Setup prompt non prompt separation table (#18527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Tiekötter --- PWGDQ/Core/VarManager.cxx | 12 +++++ PWGDQ/Core/VarManager.h | 23 ++++++--- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 64 ++++++++++++++++++++------ PWGDQ/Tasks/tableReader_withAssoc.h | 61 +++++++++++++++++++++--- 4 files changed, 134 insertions(+), 26 deletions(-) diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index af812d90158..445dabf000c 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -1131,6 +1131,14 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kDCAxy2] = "cm"; fgVariableNames[kDCAz2] = "DCA_{z}"; fgVariableUnits[kDCAz2] = "cm"; + fgVariableNames[kDCAxyOverSig1] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig1] = ""; + fgVariableNames[kDCAzOverSig1] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig1] = ""; + fgVariableNames[kDCAxyOverSig2] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig2] = ""; + fgVariableNames[kDCAzOverSig2] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig2] = ""; fgVariableNames[kTPCnSigmaEl] = "n #sigma_{e}^{TPC}"; fgVariableUnits[kTPCnSigmaEl] = ""; fgVariableNames[kTPCnSigmaEl_Corr] = "n #sigma_{e}^{TPC} Corr."; @@ -2389,6 +2397,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCharge1"] = kCharge1; fgVarNamesMap["kDCAxy1"] = kDCAxy1; fgVarNamesMap["kDCAz1"] = kDCAz1; + fgVarNamesMap["kDCAxyOverSig1"] = kDCAxyOverSig1; + fgVarNamesMap["kDCAzOverSig1"] = kDCAzOverSig1; fgVarNamesMap["kITSclusterMap1"] = kITSclusterMap1; fgVarNamesMap["kTPCnSigmaEl1"] = kTPCnSigmaEl1; fgVarNamesMap["kPin_leg1"] = kPin_leg1; @@ -2399,6 +2409,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCharge2"] = kCharge2; fgVarNamesMap["kDCAxy2"] = kDCAxy2; fgVarNamesMap["kDCAz2"] = kDCAz2; + fgVarNamesMap["kDCAxyOverSig2"] = kDCAxyOverSig2; + fgVarNamesMap["kDCAzOverSig2"] = kDCAzOverSig2; fgVarNamesMap["kITSclusterMap2"] = kITSclusterMap2; fgVarNamesMap["kTPCnSigmaEl2"] = kTPCnSigmaEl2; fgVarNamesMap["kPin"] = kPin; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 6200d2f74f1..8712bc76998 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -560,6 +560,8 @@ class VarManager : public TObject kCharge1, kDCAxy1, kDCAz1, + kDCAxyOverSig1, + kDCAzOverSig1, kITSclusterMap1, kTPCnSigmaEl1, kPin_leg1, @@ -570,6 +572,8 @@ class VarManager : public TObject kCharge2, kDCAxy2, kDCAz2, + kDCAxyOverSig2, + kDCAzOverSig2, kITSclusterMap2, kTPCnSigmaEl2, @@ -4106,14 +4110,18 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kDCAxy2] = dca2XY; values[kDCAz2] = dca2Z; - if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { - // Quantities based on the barrel tables + double dca1sigXY = dca1XY / std::sqrt(t1.cYY()); + double dca2sigXY = dca2XY / std::sqrt(t2.cYY()); + double dca1sigZ = dca1Z / std::sqrt(t1.cZZ()); + double dca2sigZ = dca2Z / std::sqrt(t2.cZZ()); - double dca1sigXY = dca1XY / std::sqrt(t1.cYY()); - double dca2sigXY = dca2XY / std::sqrt(t2.cYY()); - double dca1sigZ = dca1Z / std::sqrt(t1.cZZ()); - double dca2sigZ = dca2Z / std::sqrt(t2.cZZ()); + values[kDCAxyOverSig1] = dca1sigXY; + values[kDCAzOverSig1] = dca1sigZ; + values[kDCAxyOverSig2] = dca2sigXY; + values[kDCAzOverSig2] = dca2sigZ; + if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { + // Quantities based on the barrel tables values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2); values[kQuadDCAsigXY] = std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); values[kQuadDCAabsZ] = std::sqrt((dca1Z * dca1Z + dca2Z * dca2Z) / 2); @@ -5237,6 +5245,9 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingLxyErr] = -999.; values[kVertexingLxyzErr] = -999.; values[kVertexingLzErr] = -999.; + values[kVertexingLxyOverErr] = -999.; + values[kVertexingLzOverErr] = -999.; + values[kVertexingLxyzOverErr] = -999.; values[kVertexingTauxy] = -999.; values[kVertexingTauz] = -999.; diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 36d4f620d53..b9fead0a8fd 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -165,8 +165,8 @@ DECLARE_SOA_COLUMN(Etaee, etaee, float); DECLARE_SOA_COLUMN(Rapee, rapee, float); DECLARE_SOA_COLUMN(Phiee, phiee, float); DECLARE_SOA_COLUMN(Ptee, ptee, float); -DECLARE_SOA_COLUMN(Lxyee, lxyee, float); -DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); +DECLARE_SOA_COLUMN(Lxyee, lxyJPsi2ee, float); +DECLARE_SOA_COLUMN(LxyeePoleMass, lxyJPsi2eePoleMass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); DECLARE_SOA_COLUMN(Vx, vx, float); @@ -179,10 +179,26 @@ DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, AmbiguousJpsiPairsInBunch, bool); DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool); DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); +// Additional columns for prompt-non-prompt JPsi separation (BDT training), identical to tableReader_withAssoc +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); // Candidate columns efficiency calculation for prompt-non-prompt JPsi separation @@ -225,10 +241,19 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2, dqanalysisflags::McFlag); DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, - dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, - dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); DECLARE_SOA_TABLE(OniaMCTruth, "AOD", "MCTRUTHONIA", dqanalysisflags::OniaPt, dqanalysisflags::OniaEta, dqanalysisflags::OniaY, dqanalysisflags::OniaPhi, dqanalysisflags::OniaVz, dqanalysisflags::OniaVtxZ, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod @@ -2437,6 +2462,28 @@ struct AnalysisSameEventPairing { } } } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0 && trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0 && mcDecision > 0) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + PromptNonPromptSepTable(twoTrackFilter, mcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, isCorrectAssoc_leg1 && isCorrectAssoc_leg2, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); + } + } } if constexpr (TPairType == VarManager::kDecayToMuMu) { @@ -2535,11 +2582,6 @@ struct AnalysisSameEventPairing { // Fill histograms bool isAmbiInBunch = false; bool isAmbiOutOfBunch = false; - bool isCorrect_pair = false; - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - isCorrect_pair = true; - } - for (int icut = 0; icut < ncuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); @@ -2548,10 +2590,6 @@ struct AnalysisSameEventPairing { fHistMan->FillHistClass(histNames[icut][0].Data(), dqefficiency_helpers::varValues()); // reconstructed, unmatched for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], - VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], - VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], - isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), dqefficiency_helpers::varValues()); // matched signal if (useMiniTree.fConfigMiniTree) { if constexpr (TPairType == VarManager::kDecayToMuMu) { diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index ddf579012c2..4b1ab34b9b9 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -177,7 +177,27 @@ DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool) DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); + +// Additional columns for prompt-non-prompt JPsi separation (BDT training) +DECLARE_SOA_COLUMN(PairSign, pairSign, int8_t); //! t1.sign() + t2.sign() +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); //! correct collision association (MC only, true in data) +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); + // Candidate columns for JPsi/muon correlations DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); @@ -211,12 +231,20 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::MassDileptonCandidate, dqanalysisflags::Ptpair, dqanalysisflags::Etapair, dqanalysisflags::Ptassoc, dqanalysisflags::Etaassoc, dqanalysisflags::Phiassoc, dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2); DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, - dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, - dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod - // Declarations of various short names using MyEvents = o2::soa::Join; using MyEventsBasic = o2::soa::Join; @@ -2234,6 +2262,28 @@ struct AnalysisSameEventPairing { (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 1) ? VarManager::fgValues[VarManager::kBdtPrompt] : -999.f, (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 2) ? VarManager::fgValues[VarManager::kBdtNonprompt] : -999.f); } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr (trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0) { + PromptNonPromptSepTable(twoTrackFilter, dileptonMcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, true, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); + } + } } } @@ -2319,6 +2369,7 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); } + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { if constexpr (eventHasQvector || eventHasQvectorCentr) { dileptonFlowList(event.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], @@ -2375,10 +2426,6 @@ struct AnalysisSameEventPairing { } } if (sign1 * sign2 < 0) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], - VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], - VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], - isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if constexpr (TPairType == VarManager::kDecayToMuMu) { if (fConfigQA) { fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); From 3ebffe165384ffbb52b0806cd6ee025e401340da Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:12:08 +0100 Subject: [PATCH 516/648] [PWGCF] Add FitVars table/processing for tracks, v0s and cascade in femto framework (#18537) --- PWGCF/Femto/Core/cascadeBuilder.h | 18 +- PWGCF/Femto/Core/cascadeHistManager.h | 145 ++++++++++------ PWGCF/Femto/Core/charmHadronHistManager.h | 4 +- PWGCF/Femto/Core/mcBuilder.h | 21 ++- PWGCF/Femto/Core/modes.h | 2 + PWGCF/Femto/Core/pairProcessHelpers.h | 4 +- PWGCF/Femto/Core/photonHistManager.h | 2 +- PWGCF/Femto/Core/trackHistManager.h | 121 ++++++------- .../Core/twoPhotonResonanceHistManager.h | 8 +- .../Femto/Core/twoTrackResonanceHistManager.h | 12 +- PWGCF/Femto/Core/v0Builder.h | 18 +- PWGCF/Femto/Core/v0HistManager.h | 159 +++++++++++------- .../femtoProducerDerivedToDerived.cxx | 73 ++++++-- PWGCF/Femto/Tasks/femtoCascadeQa.cxx | 18 +- PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 81 +++++++-- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 32 ++-- PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 80 +++++++-- PWGCF/Femto/Tasks/femtoTrackQa.cxx | 12 +- PWGCF/Femto/Tasks/femtoV0Qa.cxx | 18 +- 19 files changed, 551 insertions(+), 277 deletions(-) diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index a8ab5f6620c..3dda17bf3a5 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -481,10 +481,12 @@ struct CascadeBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedLiteXis; o2::framework::Produces producedXiMasks; o2::framework::Produces producedXiExtras; + o2::framework::Produces producedXiFrac; o2::framework::Produces producedOmegas; o2::framework::Produces producedLiteOmegas; o2::framework::Produces producedOmegaMasks; o2::framework::Produces producedOmegaExtras; + o2::framework::Produces producedOmegaFrac; }; struct ConfCascadeTables : o2::framework::ConfigurableGroup { @@ -493,10 +495,12 @@ struct ConfCascadeTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceLiteXis{"produceLiteXis", -1, "Produce LiteXis (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceXiMasks{"produceXiMasks", -1, "Produce XiMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceXiExtras{"produceXiExtras", -1, "Produce XiExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceXiFrac{"produceXiFrac", -1, "Produce XiFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegas{"produceOmegas", -1, "Produce Omegas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLiteOmegas{"produceLiteOmegas", -1, "Produce LiteOmegas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaMasks{"produceOmegaMasks", -1, "Produce OmegaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaExtras{"produceOmegaExtras", -1, "Produce OmegaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaFrac{"produceOmegaFrac", -1, "Produce OmegaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; template @@ -515,6 +519,7 @@ class CascadeBuilder mProduceXis = utils::enableTable("FXis_001", table.produceXis.value, initContext); mProduceXiMasks = utils::enableTable("FXiMasks_002", table.produceXiMasks.value, initContext); mProduceXiExtras = utils::enableTable("FXiExtras_001", table.produceXiExtras.value, initContext); + mProduceXiFrac = utils::enableTable("FXiFrac_001", table.produceXiFrac.value, initContext); if (mProduceXis && mProduceLiteXis) { LOG(fatal) << "FXis and FLiteXis are mutually exclusive -- enable only one. " @@ -538,6 +543,7 @@ class CascadeBuilder mProduceLiteOmegas = utils::enableTable("FLiteOmegas_001", table.produceLiteOmegas.value, initContext); mProduceOmegaMasks = utils::enableTable("FOmegaMasks_002", table.produceOmegaMasks.value, initContext); mProduceOmegaExtras = utils::enableTable("FOmegaExtras_001", table.produceOmegaExtras.value, initContext); + mProduceOmegaFrac = utils::enableTable("FOmegaFrac_001", table.produceOmegaFrac.value, initContext); if (mProduceOmegas && mProduceLiteOmegas) { LOG(fatal) << "FOmegas and FLiteOmegas are mutually exclusive -- enable only one. " @@ -556,8 +562,8 @@ class CascadeBuilder } } - if (mProduceXis || mProduceLiteXis || mProduceXiExtras || mProduceXiMasks || - mProduceOmegas || mProduceLiteOmegas || mProduceOmegaMasks || mProduceOmegaExtras) { + if (mProduceXis || mProduceLiteXis || mProduceXiExtras || mProduceXiFrac || mProduceXiMasks || + mProduceOmegas || mProduceLiteOmegas || mProduceOmegaMasks || mProduceOmegaExtras || mProduceOmegaFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -701,6 +707,9 @@ class CascadeBuilder strangeTofPosDau, strangeTofNegDau); } + if (mProduceXiFrac) { + cascadeProducts.producedXiFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + } } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { if (mProduceOmegas) { @@ -741,6 +750,9 @@ class CascadeBuilder strangeTofPosDau, strangeTofNegDau); } + if (mProduceOmegaFrac) { + cascadeProducts.producedOmegaFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + } } } @@ -754,10 +766,12 @@ class CascadeBuilder bool mProduceLiteXis = false; bool mProduceXiMasks = false; bool mProduceXiExtras = false; + bool mProduceXiFrac = false; bool mProduceOmegas = false; bool mProduceLiteOmegas = false; bool mProduceOmegaMasks = false; bool mProduceOmegaExtras = false; + bool mProduceOmegaFrac = false; }; struct ConfCascadeTablesDerivedToDerived : o2::framework::ConfigurableGroup { diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index 6d83ee144fe..73d13b7a6a8 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -46,7 +46,8 @@ enum CascadeHist { kPhi, kMass, kSign, - kPtVsMass, // nice to have during analysis + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassXi, kMassOmega, @@ -66,7 +67,6 @@ enum CascadeHist { kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, kPtVsPa, kPtVsMassXi, kPtVsMassOmega, @@ -107,6 +107,7 @@ struct ConfCascadeBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mass{"mass", {{1000, 1.f, 2.f}}, "Mass"}; o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed cascades"}; + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; }; constexpr const char PrefixXiBinning[] = "XiBinning"; @@ -120,7 +121,7 @@ struct ConfCascadeQaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d Qa histograms"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; @@ -149,6 +150,7 @@ constexpr std::array, kCascadeHistLast> HistT {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> particle, 0 -> self conjugate, +1 -> antiparticle); sign; Entries"}, {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassXi, o2::framework::HistType::kTH1F, "hMassXi", "Mass #Xi; m_{#Lambda#pi} (GeV/#it{c}^{2}); Entries"}, {kMassOmega, o2::framework::HistType::kTH1F, "hMassOmega", "mass #Omega; m_{#LambdaK} (GeV/#it{c}^{2}); Entries"}, {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, @@ -166,7 +168,6 @@ constexpr std::array, kCascadeHistLast> HistT {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle; p_{T} (GeV/#it{c}); #alpha"}, {kPtVsMassXi, o2::framework::HistType::kTH2F, "hPtVsMassXi", "p_{T} vs mass #Xi; p_{T} (GeV/#it{c}); m_{#Lambda#pi} (GeV/#it{c}^{2})"}, {kPtVsMassOmega, o2::framework::HistType::kTH2F, "hPtVsMassOmega", "p_{T} vs mass #Omega; p_{T} (GeV/#it{c}); m_{#LambdaK} (GeV/#it{c}^{2})"}, @@ -194,13 +195,14 @@ constexpr std::array, kCascadeHistLast> HistT }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kMass, {(conf).mass}}, \ - {kSign, {(conf).sign}}, \ - {kPtVsMass, {(conf).pt, (conf).mass}}, +#define CASCADE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CASCADE_HIST_MC_MAP(conf) \ @@ -228,7 +230,6 @@ constexpr std::array, kCascadeHistLast> HistT {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ - {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ {kMassXi, {(confQa).massXi}}, \ {kMassOmega, {(confQa).massOmega}}, \ @@ -240,17 +241,20 @@ constexpr std::array, kCascadeHistLast> HistT {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define CASCADE_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).cosPa}}, +#define CASCADE_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeCascadeHistSpecMap(const T& confBinningAnalysis) @@ -264,7 +268,8 @@ auto makeCascadeMcHistSpecMap(const T& confBinningAnalysis) { return std::map>{ CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) - CASCADE_HIST_MC_MAP(confBinningAnalysis)}; + CASCADE_HIST_MC_MAP(confBinningAnalysis) + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -282,13 +287,13 @@ std::map> makeCascadeMcQaHistS CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) CASCADE_HIST_QA_MAP(confBinningAnalysis, confBinningQa) CASCADE_HIST_MC_MAP(confBinningAnalysis) - CASCADE_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef CASCADE_HIST_ANALYSIS_MAP #undef CASCADE_HIST_MC_MAP #undef CASCADE_HIST_QA_MAP -#undef CASCADE_HIST_MC_QA_MAP +#undef CASCADE_HIST_FITVARS_MC_MAP constexpr char PrefixXiQa[] = "XiQA/"; constexpr char PrefixXiSe[] = "Xi/SE/"; @@ -302,6 +307,7 @@ constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties @@ -374,6 +380,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -443,6 +452,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -461,6 +473,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { fillQa(cascadeCandidate, bachelor, posDaughter, negDaughter); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); + } } template @@ -482,6 +497,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(cascadeCandidate, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); + } } private: @@ -533,7 +551,6 @@ class CascadeHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {cascadeSpecs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {cascadeSpecs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {cascadeSpecs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {cascadeSpecs.at(kPtVsCosPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {cascadeSpecs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMassXi, HistTable), getHistDesc(kPtVsMassXi, HistTable), getHistType(kPtVsMassXi, HistTable), {cascadeSpecs.at(kPtVsMassXi)}); @@ -566,24 +583,36 @@ class CascadeHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& cascadeSpecs) + { + std::string fitVarsDir = std::string(cascadePrefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {cascadeSpecs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {cascadeSpecs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {cascadeSpecs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {cascadeSpecs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {cascadeSpecs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {cascadeSpecs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {cascadeSpecs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {cascadeSpecs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {cascadeSpecs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {cascadeSpecs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -598,6 +627,12 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), cascadeCandidate.pt(), cascadeCandidate.mass()); } + template + void fillFitVars(T const& cascadeCandidate) + { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + } + template void fillQa(T1 const& cascadeCandidate, T2 const& bachelor, T3 const& posDau, T4 const& negDau) { @@ -644,7 +679,6 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), cascadeCandidate.pt(), cascadeCandidate.eta()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), cascadeCandidate.pt(), cascadeCandidate.phi()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), cascadeCandidate.phi(), cascadeCandidate.eta()); - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadePa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassXi, HistTable)), cascadeCandidate.pt(), massXi); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassOmega, HistTable)), cascadeCandidate.pt(), massOmega); @@ -663,9 +697,9 @@ class CascadeHistManager if (!cascadeCandidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } return; @@ -708,21 +742,21 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -730,13 +764,13 @@ class CascadeHistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } break; @@ -751,7 +785,10 @@ class CascadeHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; diff --git a/PWGCF/Femto/Core/charmHadronHistManager.h b/PWGCF/Femto/Core/charmHadronHistManager.h index b405f028d6b..a7f78047ac2 100644 --- a/PWGCF/Femto/Core/charmHadronHistManager.h +++ b/PWGCF/Femto/Core/charmHadronHistManager.h @@ -128,7 +128,7 @@ struct ConfCharmHadronQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis massD0{"massD0", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0"}; o2::framework::ConfigurableAxis massD0bar{"massD0bar", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0bar"}; o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; - o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.9f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; o2::framework::ConfigurableAxis impactParameterProduct{"impactParameterProduct", {{200, -0.001f, 0.001f}}, "Product of daughter impact parameters (cm^2)"}; o2::framework::ConfigurableAxis cosThetaStar{"cosThetaStar", {{100, -1.f, 1.f}}, "Cosine of decay angle in D0 rest frame"}; @@ -144,7 +144,7 @@ struct ConfLcQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis massLcToPKPi{"massLcToPKPi", {{200, 2.15, 2.42}}, "Mass under the p K pi hypothesis"}; o2::framework::ConfigurableAxis massLcToPiKP{"massLcToPiKP", {{200, 2.15, 2.42}}, "Mass under the pi K p hypothesis"}; o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; - o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.9f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; o2::framework::ConfigurableAxis chi2Pca{"chi2Pca", {{200, 0.f, 0.1f}}, "Sum of distances of the secondary vertex to its prongs (cm)"}; o2::framework::ConfigurableAxis impactParameterProngSqSum{"impactParameterProngSqSum", {{200, 0.f, 0.01f}}, "Sum of squared prong impact parameters (cm^2)"}; diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index 8062322c13b..c0f61d477f5 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -654,8 +654,22 @@ class McBuilder } template - int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles) - { + int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles, int depth = 0) + { + // guards against cyclic/malformed mother chains in some MC productions, which would + // otherwise recurse unboundedly and crash with a stack overflow; no physically sensible + // generator history is anywhere close to this deep + constexpr int MaxPartonicMotherDepth = 100; + if (depth >= MaxPartonicMotherDepth) { + // some productions hit this on a large fraction of particles (e.g. a mother-range + // heuristic below misfiring on this production's indexing convention), so warn once per + // device lifetime rather than once per particle + if (!mWarnedPartonicMotherDepth) { + LOG(warn) << "findFirstPartonicMother exceeded max recursion depth (" << MaxPartonicMotherDepth << ") at least once; mother chain may be cyclic or malformed for this MC production. Consider using findLastPartonicMother instead. Further occurrences will be suppressed."; + mWarnedPartonicMotherDepth = true; + } + return -1; + } if (!mcParticle.has_mothers()) { return -1; } @@ -689,7 +703,7 @@ class McBuilder return i; // Found a parton → return index } // Recurse upward - int64_t found = this->findFirstPartonicMother(mother, mcParticles); + int64_t found = this->findFirstPartonicMother(mother, mcParticles, depth + 1); if (found != -1) { return found; } @@ -745,6 +759,7 @@ class McBuilder bool mPassThrough = false; bool mFindLastPartonicMother = false; + bool mWarnedPartonicMotherDepth = false; bool mFillAnyTable = false; bool mProduceMcCollisions = false; bool mProduceMcParticles = false; diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index 8276bac97f2..1f4b9d16b3d 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -57,6 +57,8 @@ enum class Mode : uint32_t { kReco_Qa = kReco | kQa, kReco_Mc = kReco | kMc, kReco_Qa_Mc = kReco | kQa | kMc, + kReco_Qa_FitVars = kReco | kQa | kFitVars, + kReco_Qa_Mc_FitVars = kReco | kQa | kMc | kFitVars, kSe_Reco = kSe | kReco, kMe_Reco = kMe | kReco, kSe_Reco_Mc = kSe | kReco | kMc, diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 8e885dd6d6b..6a41c2095c8 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -664,8 +664,8 @@ void processMixedEvent(T1 const& Collisions, PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); // single-particle histograms for particles that make it into an accepted mixed-event pair - ParticleHistManagerMe1.template fill(p1, TrackTable); - ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe1.template fill(p1, TrackTable, collision1, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.template fill(p2, TrackTable, collision2, mcParticles, mcMothers, mcPartonicMothers); } } diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h index 5ffe1e390fb..96ab98cc711 100644 --- a/PWGCF/Femto/Core/photonHistManager.h +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -75,7 +75,7 @@ struct ConfPhotonBinning : o2::framework::ConfigurableGroup { struct ConfPhotonQaBinning : o2::framework::ConfigurableGroup { std::string prefix = std::string("PhotonQaBinning"); o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.8, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dcaToPv{"dcaToPv", {{200, -1, 1}}, "DCA of the photon to the PV (cm)"}; o2::framework::ConfigurableAxis chi2Ndf{"chi2Ndf", {{100, 0, 50}}, "Chi2/NDF of the KF conversion vertex"}; diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 296f11ab1aa..82fcbbd26de 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -76,7 +76,6 @@ enum TrackHist { kPtVsDcaxy, kPtVsDcaz, kPtVsDca, - kPtVsDcaxyVsDcaz, // DCAxy vs DCAz vs pt for extraction of particle fraction kPVsPTpc, // its pid kItsSignal, @@ -239,7 +238,6 @@ struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plotHeliumPid{"plotHeliumPid", true, "Generate plots for Helium PID"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs DCAxy vs DCAz for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3122}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::Configurable plotDcaCorrelation{"plotDcaCorrelation", true, "Plot pt vs DCAxy vs DCAz"}; o2::framework::ConfigurableAxis itsCluster{"itsCluster", {{8, -0.5, 7.5}}, "ITS cluster"}; o2::framework::ConfigurableAxis itsClusterIb{"itsClusterIb", {{4, -0.5, 3.5}}, "ITS cluster in inner barrel"}; o2::framework::ConfigurableAxis tpcCrossedRows{"tpcCrossedRows", {{161, -0.5, 160.5}}, "TPC cluster"}; @@ -373,7 +371,6 @@ constexpr std::array, kTrackHistLast> {kPtVsDcaxy, o2::framework::HistType::kTH2F, "hPtVsDcaxy", "p_{T} vs DCA_{XY}; p_{T} (GeV/#it{c}); DCA_{XY} (cm)"}, {kPtVsDcaz, o2::framework::HistType::kTH2F, "hPtVsDcaz", "p_{T} vs DCA_{Z}; p_{T} (GeV/#it{c}); DCA_{Z} (cm)"}, {kPtVsDca, o2::framework::HistType::kTH2F, "hPtVsDca", "p_{T} vs DCA; p_{T} (GeV/#it{c}); DCA (cm)"}, - {kPtVsDcaxyVsDcaz, o2::framework::HistType::kTHnSparseF, "hPtVsDcaxyVsDcaz", "Transverse momentum vs DCA_{xy} vs DCA_{z}; p_{T} (GeV/#it{c}); DCA_{XY} (cm); DCA_{Z} (cm);"}, {kPVsPTpc, o2::framework::HistType::kTH2F, "hPVsPTpc", "Correlation p_{global} and p_{TPC}; p_{global} (GeV/#it{c}); p_{tpc} (GeV/#it{c})"}, {kItsSignal, o2::framework::HistType::kTH2F, "hItsSignal", "ITS Signal; p (GeV/#it{c}) ; x "}, {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{ITS,el}"}, @@ -481,7 +478,6 @@ constexpr std::array, kTrackHistLast> {kPtVsDcaxy, {(confAnalysis).pt, (confQa).dcaXy}}, \ {kPtVsDcaz, {(confAnalysis).pt, (confQa).dcaZ}}, \ {kPtVsDca, {(confAnalysis).pt, (confQa).dca}}, \ - {kPtVsDcaxyVsDcaz, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ {kPVsPTpc, {(confQa).p, (confQa).p}}, \ {kItsSignal, {(confQa).p, (confQa).itsSignal}}, \ {kItsElectron, {(confQa).p, (confQa).itsElectron}}, \ @@ -532,17 +528,20 @@ constexpr std::array, kTrackHistLast> {kPdgMother, {(conf).pdgCodes}}, \ {kPdgPartonicMother, {(conf).pdgCodes}}, +// per-origin truth-split variants of the FitVars fraction-estimation DCA histogram; binned on +// fractionDcaXy/fractionDcaZ (not the QA-only dcaXy/dcaZ) so they are available wherever FitVars +// is, including the pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRACK_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).dcaXy, (confQa).dcaZ}}, +#define TRACK_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kPrimary, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kMissidentified, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary1, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary2, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary3, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, template auto makeTrackHistSpecMap(const T& confBinningAnalysis) @@ -556,7 +555,8 @@ auto makeTrackMcHistSpecMap(T const& confBinningAnalysis) { return std::map>{ TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) - TRACK_HIST_MC_MAP(confBinningAnalysis)}; + TRACK_HIST_MC_MAP(confBinningAnalysis) + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; template @@ -574,13 +574,13 @@ auto makeTrackMcQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinni TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) TRACK_HIST_QA_MAP(confBinningAnalysis, confBinningQa) TRACK_HIST_MC_MAP(confBinningAnalysis) - TRACK_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; #undef TRACK_HIST_ANALYSIS_MAP #undef TRACK_HIST_QA_MAP #undef TRACK_HIST_MC_MAP -#undef TRACK_HIST_MC_QA_MAP +#undef TRACK_HIST_FITVARS_MC_MAP constexpr char PrefixTrackQa[] = "TrackQA/"; // same-event / mixed-event siblings: single-particle histograms for a given track slot, split by @@ -685,7 +685,7 @@ class TrackHistManager this->initMc(Specs); } if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { - this->initFitVars(Specs); + this->template initFitVars(Specs); } } @@ -708,6 +708,9 @@ class TrackHistManager if constexpr (isFlagSet(mode, modes::Mode::kMc)) { this->initMc(Specs); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(Specs); + } } // init for analysis and qa and mc @@ -778,8 +781,6 @@ class TrackHistManager mPlotHeliumPid = ConfBinningQa.plotHeliumPid.value; mMomentumType = static_cast(ConfBinningQa.momentumType.value); - mPlotDcaCorrelation = ConfBinningQa.plotDcaCorrelation.value; - mPlotOrigins = ConfBinningQa.plotOrigins.value; mPlotNSecondaries = ConfBinningQa.pdgCodesForMothersOfSecondary.value.size(); @@ -804,6 +805,7 @@ class TrackHistManager // purity (packed nSigma tables) and fraction (DCA) -- only called when mode carries kFitVars, // which also implies the track type actually has the relevant tables joined + template void initFitVars(std::map> const& Specs) { std::string fitVarsDir = std::string(prefix) + std::string(FitVarsDir); @@ -813,6 +815,30 @@ class TrackHistManager mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionDca, HistTable), getHistDesc(kFractionDca, HistTable), getHistType(kFractionDca, HistTable), {Specs.at(kFractionDca)}); + + // per-origin truth-split fraction-DCA templates; need MC truth, so only booked when the mode + // also carries kMc (available in the QA task and in the pair task's SE/ME, consistently, since + // they use the same fractionDcaXy/fractionDcaZ binning as hFractionDca above) + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); + } + } } void initQa(std::map> const& Specs) @@ -861,10 +887,6 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPVsPTpc, HistTable), getHistDesc(kPVsPTpc, HistTable), getHistType(kPVsPTpc, HistTable), {Specs.at(kPVsPTpc)}); } - if (mPlotDcaCorrelation) { - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsDcaxyVsDcaz, HistTable), getHistDesc(kPtVsDcaxyVsDcaz, HistTable), getHistType(kPtVsDcaxyVsDcaz, HistTable), {Specs.at(kPtVsDcaxyVsDcaz)}); - } - std::string pidDir = std::string(prefix) + std::string(PidDir); this->addPidHistogram(pidDir, kItsSignal, Specs); @@ -963,25 +985,6 @@ class TrackHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {Specs.at(kPdgPartonicMother)}); - - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); - - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); - } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); - } } template @@ -1083,10 +1086,6 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPVsPTpc, HistTable)), track.p(), track.tpcInnerParam()); } - if (mPlotDcaCorrelation) { - mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDcaxyVsDcaz, HistTable)), mAbsCharge * track.pt(), track.dcaXY(), track.dcaZ()); - } - float momentum = 0.f; if (mMomentumType == modes::MomentumType::kPt) { momentum = mAbsCharge * track.pt(); @@ -1168,9 +1167,9 @@ class TrackHistManager if (!track.has_fMcParticle()) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } return; @@ -1215,22 +1214,22 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { // check first if particle is from a wrong collision if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -1238,13 +1237,13 @@ class TrackHistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } break; @@ -1269,8 +1268,10 @@ class TrackHistManager bool mPlotDeuteronPid = false; bool mPlotTritonPid = false; bool mPlotHeliumPid = false; - bool mPlotDcaCorrelation = false; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; modes::MomentumType mMomentumType = modes::MomentumType::kPAtPv; diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h index cfb71e8eb37..52e19c78bba 100644 --- a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -113,11 +113,11 @@ auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) constexpr char PrefixPi0[] = "Pi0/"; constexpr char PrefixEta[] = "Eta/"; // same-event sibling, used only by a pair builder's same-event resonance manager -constexpr char PrefixPi0Se[] = "Pi0Se/"; -constexpr char PrefixEtaSe[] = "EtaSe/"; +constexpr char PrefixPi0Se[] = "Pi0/SE/"; +constexpr char PrefixEtaSe[] = "Eta/SE/"; // mixed-event sibling, used only by a pair builder's mixed-event resonance manager -constexpr char PrefixPi0Me[] = "Pi0Me/"; -constexpr char PrefixEtaMe[] = "EtaMe/"; +constexpr char PrefixPi0Me[] = "Pi0/ME/"; +constexpr char PrefixEtaMe[] = "Eta/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h index 2cc046e92cc..95cdd37c01d 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -127,13 +127,13 @@ constexpr char PrefixRho[] = "Rho0/"; constexpr char PrefixPhi[] = "Phi/"; constexpr char PrefixKstar[] = "Kstar0/"; // same-event siblings, used only by a pair builder's same-event resonance manager -constexpr char PrefixRhoSe[] = "Rho0Se/"; -constexpr char PrefixPhiSe[] = "PhiSe/"; -constexpr char PrefixKstarSe[] = "Kstar0Se/"; +constexpr char PrefixRhoSe[] = "Rho0/SE/"; +constexpr char PrefixPhiSe[] = "Phi/SE/"; +constexpr char PrefixKstarSe[] = "Kstar0/SE/"; // mixed-event siblings, used only by a pair builder's mixed-event resonance manager -constexpr char PrefixRhoMe[] = "Rho0Me/"; -constexpr char PrefixPhiMe[] = "PhiMe/"; -constexpr char PrefixKstarMe[] = "Kstar0Me/"; +constexpr char PrefixRhoMe[] = "Rho0/ME/"; +constexpr char PrefixPhiMe[] = "Phi/ME/"; +constexpr char PrefixKstarMe[] = "Kstar0/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index c753fd1edc6..154233ebfa4 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -489,10 +489,12 @@ struct V0BuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedLiteLambdas; o2::framework::Produces producedLambdaMasks; o2::framework::Produces producedLambdaExtras; + o2::framework::Produces producedLambdaFrac; o2::framework::Produces producedK0shorts; o2::framework::Produces producedLiteK0shorts; o2::framework::Produces producedK0shortMasks; o2::framework::Produces producedK0shortExtras; + o2::framework::Produces producedK0shortFrac; }; struct ConfV0Tables : o2::framework::ConfigurableGroup { @@ -501,10 +503,12 @@ struct ConfV0Tables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceLiteLambdas{"produceLiteLambdas", -1, "Produce LiteLambdas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLambdaMasks{"produceLambdaMasks", -1, "Produce LambdaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLambdaExtras{"produceLambdaExtras", -1, "Produce LambdaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLambdaFrac{"produceLambdaFrac", -1, "Produce LambdaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shorts{"produceK0shorts", -1, "Produce K0shorts (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLiteK0shorts{"produceLiteK0shorts", -1, "Produce LiteK0shorts (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortMasks{"produceK0shortMasks", -1, "Produce K0shortMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortExtras{"produceK0shortExtras", -1, "Produce K0shortExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shortFrac{"produceK0shortFrac", -1, "Produce K0shortFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; template @@ -528,6 +532,7 @@ class V0Builder mProduceLiteLambdas = utils::enableTable("FLiteLambdas_001", table.produceLiteLambdas.value, initContext); mProduceLambdaMasks = utils::enableTable("FLambdaMasks_002", table.produceLambdaMasks.value, initContext); mProduceLambdaExtras = utils::enableTable("FLambdaExtras_001", table.produceLambdaExtras.value, initContext); + mProduceLambdaFrac = utils::enableTable("FLambdaFrac_001", table.produceLambdaFrac.value, initContext); if (mProduceLambdas && mProduceLiteLambdas) { LOG(fatal) << "FLambdas and FLiteLambdas are mutually exclusive -- enable only one. " @@ -551,6 +556,7 @@ class V0Builder mProduceLiteK0shorts = utils::enableTable("FLiteK0shorts_001", table.produceLiteK0shorts.value, initContext); mProduceK0shortMasks = utils::enableTable("FK0shortMasks_002", table.produceK0shortMasks.value, initContext); mProduceK0shortExtras = utils::enableTable("FK0shortExtras_001", table.produceK0shortExtras.value, initContext); + mProduceK0shortFrac = utils::enableTable("FK0shortFrac_001", table.produceK0shortFrac.value, initContext); if (mProduceK0shorts && mProduceLiteK0shorts) { LOG(fatal) << "FK0shorts and FLiteK0shorts are mutually exclusive -- enable only one. " @@ -568,8 +574,8 @@ class V0Builder << "or switch to FK0shorts if TrackTables.produceTracks is enabled instead."; } } - if (mProduceLambdas || mProduceLiteLambdas || mProduceLambdaMasks || mProduceLambdaExtras || - mProduceK0shorts || mProduceLiteK0shorts || mProduceK0shortMasks || mProduceK0shortExtras) { + if (mProduceLambdas || mProduceLiteLambdas || mProduceLambdaMasks || mProduceLambdaExtras || mProduceLambdaFrac || + mProduceK0shorts || mProduceLiteK0shorts || mProduceK0shortMasks || mProduceK0shortExtras || mProduceK0shortFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -713,6 +719,9 @@ class V0Builder v0.alpha(), v0.qtarm()); } + if (mProduceLambdaFrac) { + v0Products.producedLambdaFrac(v0.v0cosPA()); + } } template @@ -754,6 +763,9 @@ class V0Builder v0.alpha(), v0.qtarm()); } + if (mProduceK0shortFrac) { + v0Products.producedK0shortFrac(v0.v0cosPA()); + } } [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } @@ -766,10 +778,12 @@ class V0Builder bool mProduceLiteLambdas = false; bool mProduceLambdaMasks = false; bool mProduceLambdaExtras = false; + bool mProduceLambdaFrac = false; bool mProduceK0shorts = false; bool mProduceLiteK0shorts = false; bool mProduceK0shortMasks = false; bool mProduceK0shortExtras = false; + bool mProduceK0shortFrac = false; }; struct ConfV0TablesDerivedToDerived : o2::framework::ConfigurableGroup { diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 6d85ccd6747..058edf2e6c8 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -46,7 +46,8 @@ enum V0Hist { kPhi, kMass, kSign, - kPtVsMass, // nice to have during analysis + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassLambda, kMassAntiLambda, @@ -65,7 +66,6 @@ enum V0Hist { kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, kPtVsPa, kPtVsLambdaMass, kPtVsAntiLambdaMass, @@ -101,13 +101,14 @@ enum V0Hist { constexpr std::size_t MaxSecondary = 3; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; +#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; \ + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; template struct ConfLambdaBinning : o2::framework::ConfigurableGroup { @@ -137,7 +138,7 @@ struct ConfV0QaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{100, 0, 100}}, "Decay vertex"}; @@ -165,6 +166,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassLambda, o2::framework::HistType::kTH1F, "hMassLambda", "#Lambda mass; m_{p#pi^{-}} (GeV/#it{c}^{2}); Entries"}, {kMassAntiLambda, o2::framework::HistType::kTH1F, "hMassAntiLambda", "#bar{#Lambda} mass; m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2}); Entries"}, {kMassK0short, o2::framework::HistType::kTH1F, "hMassK0short", "K^{0}_{s} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); Entries"}, @@ -181,7 +183,6 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs Cosine of pointing angle;p_{T} (GeV/#it{c}); cos(#alpha);"}, {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle;p_{T} (GeV/#it{c}); #alpha"}, {kPtVsLambdaMass, o2::framework::HistType::kTH2F, "hPtVsLambdaMass", "p_{T} vs #Lambda mass; p_{T} (GeV/#it{c}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, {kPtVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hPtVsAntiLambdaMass", "p_{T} vs #bar{#Lambda} mass; p_{T} (GeV/#it{c}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, @@ -211,13 +212,14 @@ constexpr std::array, kV0HistLast> HistTable = { }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_HIST_ANALYSIS_MAP(conf) \ - {kPt, {(conf).pt}}, \ - {kEta, {(conf).eta}}, \ - {kPhi, {(conf).phi}}, \ - {kMass, {(conf).mass}}, \ - {kSign, {(conf).sign}}, \ - {kPtVsMass, {(conf).pt, (conf).mass}}, +#define V0_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define V0_HIST_MC_MAP(conf) \ @@ -243,7 +245,6 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ - {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ {kMassLambda, {(confQa).massLambda}}, \ {kMassAntiLambda, {(confQa).massAntiLambda}}, \ @@ -258,17 +259,20 @@ constexpr std::array, kV0HistLast> HistTable = { {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ {kArmenterosQtVsAlpha, {(confQa).armenterosAlpha, (confQa).armenterosQt}}, +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define V0_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kPrimary, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromWrongCollision, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kFromMaterial, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kMissidentified, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary1, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary2, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondary3, {(confAnalysis).pt, (confQa).cosPa}}, \ - {kSecondaryOther, {(confAnalysis).pt, (confQa).cosPa}}, +#define V0_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeV0HistSpecMap(const T& confBinningAnalysis) @@ -282,7 +286,8 @@ auto makeV0McHistSpecMap(const T& confBinningAnalysis) { return std::map>{ V0_HIST_ANALYSIS_MAP(confBinningAnalysis) - V0_HIST_MC_MAP(confBinningAnalysis)}; + V0_HIST_MC_MAP(confBinningAnalysis) + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -300,13 +305,13 @@ std::map> makeV0McQaHistSpecMap(T1 V0_HIST_ANALYSIS_MAP(confBinningAnalysis) V0_HIST_QA_MAP(confBinningAnalysis, confBinningQa) V0_HIST_MC_MAP(confBinningAnalysis) - V0_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef V0_HIST_ANALYSIS_MAP #undef V0_HIST_QA_MAP #undef V0_HIST_MC_MAP -#undef V0_HIST_MC_QA_MAP +#undef V0_HIST_FITVARS_MC_MAP constexpr char PrefixLambdaQa[] = "LambdaQA/"; // same-event / mixed-event siblings: single-candidate histograms for a given slot, split by @@ -326,6 +331,7 @@ constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties @@ -382,6 +388,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } // init for analysis and qa and and mc @@ -433,6 +442,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } template @@ -449,6 +461,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(v0candidate, posDaughter, negDaughter); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); + } } template @@ -467,6 +482,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->template fillMc(v0candidate, col, mcParticles, mcMothers, mcPartonicMothers); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); + } } private: @@ -515,7 +533,6 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {V0Specs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {V0Specs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {V0Specs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {V0Specs.at(kPtVsCosPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {V0Specs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassLambda, HistTable), getHistDesc(kMassLambda, HistTable), getHistType(kMassLambda, HistTable), {V0Specs.at(kMassLambda)}); @@ -553,24 +570,36 @@ class V0HistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& V0Specs) + { + std::string fitVarsDir = std::string(v0Prefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {V0Specs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -591,6 +620,12 @@ class V0HistManager } } + template + void fillFitVars(T const& v0candidate) + { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); + } + template void fillQa(T1 const& v0candidate, T2 const& posDau, T3 const& negDau) { @@ -641,7 +676,6 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), v0candidate.pt(), v0candidate.eta()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), v0candidate.pt(), v0candidate.phi()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), v0candidate.phi(), v0candidate.eta()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), v0candidate.pt(), v0candidate.pa()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), massLambda); @@ -663,9 +697,9 @@ class V0HistManager if (!v0Candidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } return; @@ -708,22 +742,22 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { // check first if particle is missidentified if (fromWrongCollision) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromSecondaryDecay: if (mcParticle.has_fMcMother()) { @@ -731,13 +765,13 @@ class V0HistManager int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } break; @@ -752,7 +786,10 @@ class V0HistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 2d6b5f1c580..64f6aa88985 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -51,7 +51,7 @@ using FemtoTracks = o2::soa::Join; using FemtoTracksWithMass = o2::soa::Join; // opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- // needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled -using FemtoTracksWithPurity = o2::soa::Join; +using FemtoTracksWithFitVars = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; using FemtoRho0s = o2::soa::Join; @@ -81,7 +81,7 @@ struct FemtoProducerDerivedToDerived { struct : public o2::framework::PresliceGroup { o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; - o2::framework::Preslice perColTracksWithPurity = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; @@ -130,8 +130,8 @@ struct FemtoProducerDerivedToDerived { o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); - o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(trackSelections2); // v0 builder v0builder::V0BuilderDerivedToDerivedProducts v0BuilderProducts; @@ -196,11 +196,12 @@ struct FemtoProducerDerivedToDerived { void init(o2::framework::InitContext& /*context*/) { const int activeProcesses = - static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + static_cast(doprocessTracksWithPurity) + - static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + + static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + static_cast(doprocessTracksWithFitVars) + + static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + static_cast(doprocessTracksWithFitVarsLambdas) + static_cast(doprocessLambdas) + - static_cast(doprocessTracksXis) + static_cast(doprocessTracksOmegas) + - static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksSigma) + + static_cast(doprocessTracksXis) + static_cast(doprocessTracksWithFitVarsXis) + + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksWithFitVarsOmegas) + + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksWithFitVarsK0shorts) + static_cast(doprocessTracksSigma) + static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + static_cast(doprocessTracksSigmaPlusPcm) + static_cast(doprocessTracksSigmaPlusPcmExtended) + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + @@ -242,16 +243,16 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); - void processTracksWithPurity(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithPurity const& tracks) + void processTracksWithFitVars(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache)) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache)) { return; } builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } - PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithPurity, "Process tracks, carrying through purity/fraction tables", false); + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVars, "Process tracks, carrying through FitVars tables", false); void processTracksLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { @@ -277,6 +278,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMassLambdas, "Process lambdas and tracks with mass", false); + void processTracksWithFitVarsLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoLambdas const& lambdas) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsLambdas, "Process lambdas and tracks, carrying through FitVars tables", false); + void processLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { @@ -300,6 +313,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksXis, "Process tracks and xis", false); + void processTracksWithFitVarsXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoXis const& xis) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsXis, "Process tracks and xis, carrying through FitVars tables", false); + void processTracksOmegas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { @@ -312,6 +337,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksOmegas, "Process tracks and omegas", false); + void processTracksWithFitVarsOmegas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoOmegas const& omegas) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsOmegas, "Process tracks and omegas, carrying through FitVars tables", false); + void processTracksK0shorts(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { @@ -324,6 +361,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksK0shorts, "Process k0short and tracks", false); + void processTracksWithFitVarsK0shorts(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoK0shorts const& k0shorts) + { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsK0shorts, "Process k0short and tracks, carrying through FitVars tables", false); + void processTracksSigma(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { diff --git a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx index 63daed2c1a5..fee88a038c6 100644 --- a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx +++ b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx @@ -51,7 +51,7 @@ struct FemtoCascadeQa { using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoXisWithLabel = o2::soa::Join; using FemtoOmegasWithLabel = o2::soa::Join; @@ -147,11 +147,11 @@ struct FemtoCascadeQa { negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXi) { xiHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmega) { omegaHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); @@ -161,11 +161,11 @@ struct FemtoCascadeQa { negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXiMc) { xiHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmegaMc) { omegaHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } hRegistry.print(); @@ -179,7 +179,7 @@ struct FemtoCascadeQa { } colHistManager.fill(col); for (auto const& xi : xiSlice) { - xiHistManager.fill(xi, tracks); + xiHistManager.fill(xi, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processXi, "Process Xis", true); @@ -195,7 +195,7 @@ struct FemtoCascadeQa { if (!xiCleaner.isClean(xi, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - xiHistManager.fill(xi, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + xiHistManager.fill(xi, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processXiMc, "Process Xis with MC information", false); @@ -208,7 +208,7 @@ struct FemtoCascadeQa { } colHistManager.fill(col); for (auto const& omega : omegaSlice) { - omegaHistManager.fill(omega, tracks); + omegaHistManager.fill(omega, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processOmega, "Process Omegas", false); @@ -224,7 +224,7 @@ struct FemtoCascadeQa { if (!omegaCleaner.isClean(omega, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - omegaHistManager.fill(omega, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + omegaHistManager.fill(omega, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processOmegaMc, "Process Omegas with MC information", false); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 28185d90673..0f190a841cf 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -60,9 +60,14 @@ struct FemtoPairTrackCascade { using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoXisWithLabel = o2::soa::Join; - using FemtoOmegasWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoXisWithLabel = o2::soa::Join; + using FemtoOmegasWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoXisWithFitVars = o2::soa::Join; + using FemtoOmegasWithFitVars = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -84,6 +89,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters/bachelor trackhistmanager::ConfCascadePosDauBinning confPosDauBinning; trackhistmanager::ConfCascadeNegDauBinning confNegDauBinning; @@ -100,6 +108,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition xiWithLabelPartition = MAKE_CASCADE_PARTITION(confXiSelection); o2::framework::Preslice perColXisWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition xiWithFitVarsPartition = MAKE_CASCADE_PARTITION(confXiSelection); + o2::framework::Preslice perColXisWithFitVars = o2::aod::femtobase::stored::fColId; + // setup omegas cascadebuilder::ConfOmegaSelection confOmegaSelection; cascadehistmanager::ConfOmegaBinning confOmegaBinning; @@ -111,6 +122,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition omegaWithLabelPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); o2::framework::Preslice perColOmegasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition omegaWithFitVarsPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + o2::framework::Preslice perColOmegasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; @@ -174,14 +188,15 @@ struct FemtoPairTrackCascade { void init(o2::framework::InitContext&) { bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processFitVars = doprocessXiSameEventWithFitVars || doprocessXiMixedEventWithFitVars || doprocessOmegaSameEventWithFitVars || doprocessOmegaMixedEventWithFitVars; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc; if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; @@ -206,7 +221,21 @@ struct FemtoPairTrackCascade { std::map> cprHistSpecV0Daughter = closepairrejection::makeCprHistSpecMap(confCprV0Daughter); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackCascadeHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processXi) { + xiHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confXiBinning); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } else { + omegaHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confOmegaBinning); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); @@ -230,10 +259,10 @@ struct FemtoPairTrackCascade { pairTrackCascadeHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processXi) { xiHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confXiBinning); - pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } else { omegaHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confOmegaBinning); - pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } } hRegistry.print(); @@ -247,7 +276,7 @@ struct FemtoPairTrackCascade { void processXiSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& xis, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); @@ -259,10 +288,22 @@ struct FemtoPairTrackCascade { void processXiMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventMc, "Enable processing mixed event processing for tracks and xis with mc information", false); + void processXiSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& xis) + { + pairTrackXiBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, xis, xiWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventWithFitVars, "Enable processing same event processing for tracks and xis, carrying through FitVars tables", false); + + void processXiMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& /*xis*/) + { + pairTrackXiBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, xiWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithFitVars, "Enable processing mixed event processing for tracks and xis, carrying through FitVars tables", false); + void processOmegaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& omegas) { pairTrackOmegaBuilder.processSameEvent(col, tracks, trackPartition, omegas, omegaPartition, cache); @@ -271,7 +312,7 @@ struct FemtoPairTrackCascade { void processOmegaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& omegas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); @@ -283,9 +324,21 @@ struct FemtoPairTrackCascade { void processOmegaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventMc, "Enable processing mixed event processing for tracks and omegas with mc information", false); + + void processOmegaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& omegas) + { + pairTrackOmegaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, omegas, omegaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventWithFitVars, "Enable processing same event processing for tracks and omegas, carrying through FitVars tables", false); + + void processOmegaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& /*omegas*/) + { + pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, omegaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and omegas, carrying through FitVars tables", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 28b9ce92d65..0b3299d8895 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -68,10 +68,10 @@ struct FemtoPairTrackTrack { // opt-in: adds the packed purity tables (nSigma, see TrackBinning1/2.puritySlotMask) and FTrackDcas // (fraction estimation) for the FitVars histograms. Needs the producer's // TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled. - using FemtoTracksWithPurity = o2::soa::Join; + using FemtoTracksWithFitVars = o2::soa::Join; // the Mc process functions always carry the purity/fraction tables (not opt-in like the data - // path's WithPurity variant) -- requires the producer's TrackTables.produceItsPurity/ + // path's WithFitVars variant) -- requires the producer's TrackTables.produceItsPurity/ // produceTpcPurity/produceTofPurity/produceTrackDcas enabled whenever MC processing is used using FemtoTracksWithLabel = o2::soa::Join; @@ -101,9 +101,9 @@ struct FemtoPairTrackTrack { o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(confTrackSelections2); o2::framework::Preslice perColtracksWithMass = o2::aod::femtobase::stored::fColId; - o2::framework::Partition trackWithPurityPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); - o2::framework::Partition trackWithPurityPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); - o2::framework::Preslice perColtracksWithPurity = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracksWithFitVars = o2::aod::femtobase::stored::fColId; o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); @@ -142,14 +142,14 @@ struct FemtoPairTrackTrack { void init(o2::framework::InitContext&) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithPurity) + static_cast(doprocessSameEventMc)) > 1 || - (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithPurity) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithFitVars) + static_cast(doprocessSameEventMc)) > 1 || + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; - bool processPurity = doprocessSameEventWithPurity || doprocessMixedEventWithPurity; + bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; - if (static_cast(processData) + static_cast(processPurity) + static_cast(processMc) > 1) { + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; } @@ -168,7 +168,7 @@ struct FemtoPairTrackTrack { std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processPurity) { + if (processFitVars) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); @@ -202,11 +202,11 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithMass, "Enable processing same event processing (with track masses)", false); - void processSameEventWithPurity(FilteredFemtoCollision const& col, FemtoTracksWithPurity const& tracks) + void processSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks) { - pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache); + pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache); } - PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithPurity, "Enable processing same event processing (with purity/fraction tables)", false); + PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithFitVars, "Enable processing same event processing (with FitVars tables)", false); void processSameEventWithEventShape(FilteredFemtoCollisionWithEventShape const& col, FemtoTracks const& tracks) { @@ -232,11 +232,11 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithMass, "Enable processing mixed event processing (with track masses)", false); - void processMixedEventWithPurity(FilteredFemtoCollisions const& cols, FemtoTracksWithPurity const& tracks) + void processMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks) { - pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithPurityPartition1, trackWithPurityPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } - PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithPurity, "Enable processing mixed event processing (with purity/fraction tables)", false); + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithFitVars, "Enable processing mixed event processing (with FitVars tables)", false); void processMixedEventWithEventShape(FilteredFemtoCollisionsWithEventShape const& cols, FemtoTracks const& tracks) { diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 88e844687e8..054e8bc40cf 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -60,9 +60,14 @@ struct FemtoPairTrackV0 { using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoLambdasWithLabel = o2::soa::Join; - using FemtoK0shortsWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shortsWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoLambdasWithFitVars = o2::soa::Join; + using FemtoK0shortsWithFitVars = o2::soa::Join; using FemtoMcParticlesWithLabel = o2::soa::Join; @@ -84,6 +89,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters trackhistmanager::ConfV01PosDauBinning confPosDauBinning; trackhistmanager::ConfV01NegDauBinning confNegDauBinning; @@ -99,6 +107,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaWithFitVarsPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); + o2::framework::Preslice perColLambdasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup k0shorts v0builder::ConfK0shortSelection1 k0shortSelection; v0histmanager::ConfK0shortBinning1 confK0shortBinning; @@ -110,6 +121,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition k0shortWithLabelPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); o2::framework::Preslice perColk0shortsWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortWithFitVarsPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); + o2::framework::Preslice perColk0shortsWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; @@ -164,14 +178,15 @@ struct FemtoPairTrackV0 { void init(o2::framework::InitContext&) { bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent; + bool processFitVars = doprocessLambdaSameEventWithFitVars || doprocessLambdaMixedEventWithFitVars || doprocessK0shortSameEventWithFitVars || doprocessK0shortMixedEventWithFitVars; bool processMc = doprocessLambdaSameEventMc || doprocessLambdaMixedEventMc || doprocessK0shortSameEventMc || doprocessK0shortMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventMc; - bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventMc; + bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc; + bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc; if (processLambda && processK0short) { LOG(fatal) << "Both lambda-track and k0short-track processing is enabled. Breaking..."; @@ -193,7 +208,20 @@ struct FemtoPairTrackV0 { std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackV0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processLambda) { + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); @@ -214,10 +242,10 @@ struct FemtoPairTrackV0 { pairTrackV0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processLambda) { lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); - pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } else { k0shortHistSpec = v0histmanager::makeV0McHistSpecMap(confK0shortBinning); - pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -231,7 +259,7 @@ struct FemtoPairTrackV0 { void processLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventMc, "Enable processing same event processing for tracks and lambdas with MC information", false); @@ -243,10 +271,22 @@ struct FemtoPairTrackV0 { void processLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventMc, "Enable processing mixed event processing for tracks and lambdas with MC information", false); + void processLambdaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& lambdas) + { + pairTrackLambdaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, lambdas, lambdaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventWithFitVars, "Enable processing same event processing for tracks and lambdas, carrying through FitVars tables", false); + + void processLambdaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& /*lambas*/) + { + pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, lambdaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and lambdas, carrying through FitVars tables", false); + void processK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) { pairTrackK0shortBuilder.processSameEvent(col, tracks, trackPartition, k0shorts, k0shortPartition, cache); @@ -255,7 +295,7 @@ struct FemtoPairTrackV0 { void processK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventMc, "Enable processing same event processing for tracks and k0shorts with MC information", false); @@ -267,9 +307,21 @@ struct FemtoPairTrackV0 { void processK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventMc, "Enable processing mixed event processing for tracks and k0shorts with mc information", false); + + void processK0shortSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& k0shorts) + { + pairTrackK0shortBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, k0shorts, k0shortWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventWithFitVars, "Enable processing same event processing for tracks and k0shorts, carrying through FitVars tables", false); + + void processK0shortMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& /*k0shorts*/) + { + pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, k0shortWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithFitVars, "Enable processing mixed event processing for tracks and k0shorts, carrying through FitVars tables", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) diff --git a/PWGCF/Femto/Tasks/femtoTrackQa.cxx b/PWGCF/Femto/Tasks/femtoTrackQa.cxx index 5a5f081e954..c247afdb75c 100644 --- a/PWGCF/Femto/Tasks/femtoTrackQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackQa.cxx @@ -53,7 +53,7 @@ struct FemtoTrackQa { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; @@ -100,12 +100,12 @@ struct FemtoTrackQa { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } hRegistry.print(); }; @@ -118,7 +118,7 @@ struct FemtoTrackQa { } colHistManager.fill(col); for (auto const& track : trackSlice) { - trackHistManager.fill(track, tracks); + trackHistManager.fill(track, tracks); } }; PROCESS_SWITCH(FemtoTrackQa, processData, "Track QA in Data", true); @@ -134,7 +134,7 @@ struct FemtoTrackQa { if (!trackCleaner.isClean(track, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - trackHistManager.fill(track, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + trackHistManager.fill(track, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoTrackQa, processMc, "Track QA in Monte Carlo", false); @@ -147,7 +147,7 @@ struct FemtoTrackQa { } colHistManager.fill(col); for (auto const& track : trackSlice) { - trackHistManager.fill(track, tracks); + trackHistManager.fill(track, tracks); } }; PROCESS_SWITCH(FemtoTrackQa, processDataWithEventShape, "Track QA in Data with event shape information", false); diff --git a/PWGCF/Femto/Tasks/femtoV0Qa.cxx b/PWGCF/Femto/Tasks/femtoV0Qa.cxx index 18e6a485e99..083f5bcc849 100644 --- a/PWGCF/Femto/Tasks/femtoV0Qa.cxx +++ b/PWGCF/Femto/Tasks/femtoV0Qa.cxx @@ -52,7 +52,7 @@ struct FemtoV0Qa { using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoLambdasWithLabel = o2::soa::Join; using FemtoK0shortsWithLabel = o2::soa::Join; @@ -143,11 +143,11 @@ struct FemtoV0Qa { negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambda) { lambdaHistSpec = v0histmanager::makeV0QaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0short) { k0shortHistSpec = v0histmanager::makeV0QaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); @@ -156,11 +156,11 @@ struct FemtoV0Qa { negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambdaMc) { lambdaHistSpec = v0histmanager::makeV0McQaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0shortMc) { k0shortHistSpec = v0histmanager::makeV0McQaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } hRegistry.print(); @@ -174,7 +174,7 @@ struct FemtoV0Qa { } colHistManager.fill(col); for (auto const& k0short : k0shortSlice) { - k0shortHistManager.fill(k0short, tracks); + k0shortHistManager.fill(k0short, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processK0short, "Process k0shorts", false); @@ -190,7 +190,7 @@ struct FemtoV0Qa { if (!k0shortCleaner.isClean(k0short, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - k0shortHistManager.fill(k0short, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + k0shortHistManager.fill(k0short, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoV0Qa, processK0shortMc, "Process k0shorts with MC information", false); @@ -203,7 +203,7 @@ struct FemtoV0Qa { } colHistManager.fill(col); for (auto const& lambda : lambdaSlice) { - lambdaHistManager.fill(lambda, tracks); + lambdaHistManager.fill(lambda, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processLambda, "Process lambdas", true); @@ -219,7 +219,7 @@ struct FemtoV0Qa { if (!lambdaCleaner.isClean(lambda, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - lambdaHistManager.fill(lambda, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + lambdaHistManager.fill(lambda, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoV0Qa, processLambdaMc, "Process lambdas with MC informaton", false); From bb6bbefb33d7855047b55fcaccdcbb1e719e66ee Mon Sep 17 00:00:00 2001 From: Zhiyong Lu <71517277+Luzhiyongg@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:52:35 +0800 Subject: [PATCH 517/648] [PWGCF] Update validation histogram with conditional weights (#18539) --- PWGCF/Flow/Tasks/flowTask.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index 339482157d3..2471b77f282 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -1586,16 +1586,16 @@ struct FlowTask { // manual pt-pt variance if (mpt_sub1_weffEv) { mpt_sub1 = mpt_sub1 / mpt_sub1_weffEv; - registry.fill(HIST("pt_1sub1_1pc"), independent, mpt_sub1, mpt_sub1_weffEv); + registry.fill(HIST("pt_1sub1_1pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub1_weffEv : 1.); } if (mpt_sub3_weffEv) { mpt_sub3 = mpt_sub3 / mpt_sub3_weffEv; - registry.fill(HIST("pt_1sub3_1pc"), independent, mpt_sub3, mpt_sub3_weffEv); + registry.fill(HIST("pt_1sub3_1pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub3_weffEv : 1.); } if (mpt_sub1_weffEv && mpt_sub3_weffEv) { - registry.fill(HIST("ptpt_1sub1_1sub3_2pc"), independent, mpt_sub1 * mpt_sub3, ev2pc); - registry.fill(HIST("pt_1sub1_2pc"), independent, mpt_sub1, ev2pc); - registry.fill(HIST("pt_1sub3_2pc"), independent, mpt_sub3, ev2pc); + registry.fill(HIST("ptpt_1sub1_1sub3_2pc"), independent, mpt_sub1 * mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub1_2pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub3_2pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); } } From eb7c5685cd0aeb47012f5fc7b19985888656436a Mon Sep 17 00:00:00 2001 From: Georgios Mantzaridis <62671855+gmantzar@users.noreply.github.com> Date: Sun, 1 Nov 2026 22:11:16 +0100 Subject: [PATCH 518/648] [PWGCF] Adding v0 cascade into the D2D producer (#18532) --- .../TableProducer/femtoProducerDerivedToDerived.cxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 64f6aa88985..2eca98b293c 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -205,6 +205,7 @@ struct FemtoProducerDerivedToDerived { static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + static_cast(doprocessTracksSigmaPlusPcm) + static_cast(doprocessTracksSigmaPlusPcmExtended) + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + + static_cast(doprocessLambdasXis) + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc) + static_cast(doprocessTracksSigmaPlusPcmMc) + static_cast(doprocessTracksSigmaPlusPcmExtendedMc); @@ -457,6 +458,18 @@ struct FemtoProducerDerivedToDerived { } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); + void processLambdasXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas, femtotables::FemtoXis const& xis) + { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + return; + } + builders.trackBuilder.reset(tracks); + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdasXis, "Process lambdas and xis", false); + // mc processing // // NOTE: unlike the other process functions, this one subscribes to the full // collision table and iterates manually. The MC index maps are keyed by source From 222e41f0ff6826f97c36c11fe8cc5b943bdf7023 Mon Sep 17 00:00:00 2001 From: Shirajum Monira <38348689+Eloviyo@users.noreply.github.com> Date: Mon, 2 Nov 2026 00:12:54 +0100 Subject: [PATCH 519/648] [PWGCF] FemtoUniverse Producer - implemented competing mass rejection only for Omegas (#18540) Co-authored-by: smonira --- .../Core/FemtoUniverseCascadeSelection.h | 19 ++++++++++++++++--- .../femtoUniverseProducerTask.cxx | 5 +++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h index 43d3e2cf54b..920cfbdd2b2 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h @@ -86,7 +86,7 @@ class FemtoUniverseCascadeSelection public: FemtoUniverseCascadeSelection() - : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8) /*, fRejectCompetingMass(false), fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ + : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8), fRejectCompetingMass(false) /*, fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ { } @@ -176,6 +176,13 @@ class FemtoUniverseCascadeSelection fInvMassCompetingUpLimit = upLimit; }*/ + /// Set whether to reject competing mass for Omega + /// \param reject Boolean flag + void setRejectCompetingMassOmega(bool reject) + { + fRejectCompetingMass = reject; + } + private: int nPtCascadeMinSel; int nPtCascadeMaxSel; @@ -227,6 +234,8 @@ class FemtoUniverseCascadeSelection // float nSigmaPIDOffsetTPC; + bool fRejectCompetingMass; + FemtoUniverseTrackSelection posDaughTrack; FemtoUniverseTrackSelection negDaughTrack; FemtoUniverseTrackSelection bachTrackSel; @@ -437,11 +446,15 @@ bool FemtoUniverseCascadeSelection::isSelectedMinimal(Col const& col, Casc const const float invMassXi = cascade.mXi(); const float invMassOmega = cascade.mOmega(); + // Check individual mass window hypotheses + const bool isXi = (invMassXi >= fInvMassLowLimitXi && invMassXi <= fInvMassUpLimitXi); + const bool isOmega = (invMassOmega >= fInvMassLowLimitOmega && invMassOmega <= fInvMassUpLimitOmega); + const bool passOmega = isOmega && (!fRejectCompetingMass || !isXi); + if (invMassLambda < fV0InvMassLowLimit || invMassLambda > fV0InvMassUpLimit) { return false; } - // Accepts the cascade candidates as either Xi or Omega but not both - if ((invMassXi < fInvMassLowLimitXi || invMassXi > fInvMassUpLimitXi) == (invMassOmega < fInvMassLowLimitOmega || invMassOmega > fInvMassUpLimitOmega)) { + if (!isXi && !passOmega) { return false; } /*if (fRejectCompetingMass) { diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx index 6ef52e5c8bc..880003d8e81 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx @@ -324,6 +324,7 @@ struct FemtoUniverseProducerTask { Configurable confXiInvMassUpLimit{"confXiInvMassUpLimit", 1.40, "Upper limit of the Xi invariant mass"}; Configurable confOmegaInvMassLowLimit{"confOmegaInvMassLowLimit", 1.60, "Lower limit of the Omega invariant mass"}; Configurable confOmegaInvMassUpLimit{"confOmegaInvMassUpLimit", 1.80, "Upper limit of the Omega invariant mass"}; + Configurable confRejectCompetingMassOmega{"confRejectCompetingMassOmega", false, "Switch to reject competing mass for Omega"}; } ConfCascadeSelection; // PHI @@ -692,15 +693,15 @@ struct FemtoUniverseProducerTask { cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildTPCnClsMin, femto_universe_track_selection::kTPCnClsMin, femto_universe_selection::kLowerLimit); cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDnSigmaMax, femto_universe_track_selection::kPIDnSigmaMax, femto_universe_selection::kAbsUpperLimit); - // TODO cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kPosTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kNegTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDspecies); // check if works correctly for bachelor track cascadeCuts.init(&cascadeQaRegistry); - // invmass cuts + // invmass cuts & competing mass rejection cascadeCuts.setInvMassLimits(ConfCascadeSelection.confXiInvMassLowLimit, ConfCascadeSelection.confOmegaInvMassLowLimit, ConfCascadeSelection.confXiInvMassUpLimit, ConfCascadeSelection.confOmegaInvMassUpLimit); + cascadeCuts.setRejectCompetingMassOmega(ConfCascadeSelection.confRejectCompetingMassOmega); } if (ConfGeneral.confIsActivatePhi) { From efd7340bff92151d0ae383b753992548d8f1fd6b Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Mon, 2 Nov 2026 00:15:00 +0100 Subject: [PATCH 520/648] [PWGCF] Clean up math functions in femto frame work (#18542) --- PWGCF/Femto/Core/femtoMath.h | 284 ++++++++++++++++++++++++++ PWGCF/Femto/Core/femtoSpherHarMath.h | 11 +- PWGCF/Femto/Core/pairHistManager.h | 259 +++-------------------- PWGCF/Femto/Core/tripletHistManager.h | 183 ++++++++--------- 4 files changed, 402 insertions(+), 335 deletions(-) create mode 100644 PWGCF/Femto/Core/femtoMath.h diff --git a/PWGCF/Femto/Core/femtoMath.h b/PWGCF/Femto/Core/femtoMath.h new file mode 100644 index 00000000000..f686b85d9b9 --- /dev/null +++ b/PWGCF/Femto/Core/femtoMath.h @@ -0,0 +1,284 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoMath.h +/// \brief shared kinematic math (kstar, Q3, mt, kt, spherical-harmonics kinematics, ...) +/// used by the pair and triplet histogram managers +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_FEMTOMATH_H_ +#define PWGCF_FEMTO_CORE_FEMTOMATH_H_ + +#include "PWGCF/Femto/Core/modes.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::femtomath +{ + +// SH reference frame selectors for computeShKinematics(), mirrors ConfPairBinning.shFrame +constexpr int ShFrameLcmsNonIdentical = 0; +constexpr int ShFrameLcmsIdentical = 1; +constexpr int ShFramePrf = 2; + +inline float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + auto sum = (part1 + part2); + double kt = 0.5 * sum.Pt(); + return static_cast(kt); +} + +inline float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, modes::TransverseMassType mtType = modes::TransverseMassType::kAveragePdgMass) +{ + auto sum = part1 + part2; + double mt = 0; + double mass = 0; + switch (mtType) { + case modes::TransverseMassType::kAveragePdgMass: + mass = 0.5 * (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kReducedPdgMass: + mass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kMt4Vector: + mt = 0.5 * sum.Mt(); + break; + default: + LOG(fatal) << "Invalid transverse mass type, breaking..."; + } + return static_cast(mt); +} + +inline float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast((part1 + part2).M()); +} + +inline float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + // Use Cartesian 4-vectors: addition/M2() become pure arithmetic + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + + // Mandelstam s = (p1 + p2)^2 + const double s = (p1 + p2).M2(); + const double m1sq = p1.M2(); + const double m2sq = p2.M2(); + + // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 + const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; + + // k* = 0.5 * sqrt(λ/s) + return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); +} + +// Alpha: azimuthal opening angle between the two particles' transverse momenta, +// i.e. phi2 - phi1 wrapped to [-pi, pi). Purely a lab-frame angular variable (no +// boosting involved) -- small |alpha| means the pair sits close together in phi, +// which is where detector effects like track splitting/merging and two-track +// resolution losses tend to show up. +inline float getAlpha(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast(RecoDecay::constrainAngle(part2.phi() - part1.phi(), -o2::constants::math::PI)); +} + +// BetaLab: lab-frame angle between the pair's relative 3-momentum (p1 - p2) and its +// total 3-momentum (p1 + p2, i.e. the kT direction). Note the (p1 - p2) here is the +// raw 3-vector difference, not the Lorentz-invariant k* -- this is a lab-frame proxy +// for how the pair's separation axis is oriented relative to its direction of motion, +// without needing to boost to the pair rest frame. +inline float getBetaLab(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + ROOT::Math::XYZVector p1(part1.Px(), part1.Py(), part1.Pz()); + ROOT::Math::XYZVector p2(part2.Px(), part2.Py(), part2.Pz()); + + ROOT::Math::XYZVector kstar = p1 - p2; + ROOT::Math::XYZVector kt = p1 + p2; + + return static_cast(std::acos(kstar.Dot(kt) / std::sqrt(kstar.Mag2() * kt.Mag2()))); +} + +inline std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector pSum = p1 + p2; + const ROOT::Math::PxPyPzEVector pDiff = p1 - p2; + + const double tPx = pSum.Px(); + const double tPy = pSum.Py(); + const double tPz = pSum.Pz(); + const double tE = pSum.E(); + + const double tPt = std::sqrt(tPx * tPx + tPy * tPy); + const double tMt = std::sqrt(tE * tE - tPz * tPz); + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPt < MinTransverseMomentum || tMt < MinTransverseMomentum) { + return {0.0f, 0.0f, 0.0f}; + } + + const double betaL = tPz / tE; + const double gammaL = tE / tMt; + + // out/side/long are linear projections of the momentum, so projecting the + // difference p1-p2 directly is equivalent to (and half the divisions of) + // computing each particle's out/side/long component and subtracting. + const double qOut = (pDiff.Px() * tPx + pDiff.Py() * tPy) / tPt; + const double qSide = (pDiff.Py() * tPx - pDiff.Px() * tPy) / tPt; + const double qLong = gammaL * (pDiff.Pz() - betaL * pDiff.E()); + + return {static_cast(qOut), static_cast(qSide), static_cast(qLong)}; +} + +// Return the bin index for value given ascending bin edges, or -1 if out of range. +// edges = {e0, e1, ..., eN} defines N bins [e0,e1), [e1,e2), ..., [e_{N-1},eN). +inline int findShBin(double value, std::vector const& edges) +{ + static constexpr std::size_t MinEdgesForOneBin = 2; + if (edges.size() < MinEdgesForOneBin || value < edges.front() || value >= edges.back()) { + return -1; + } + for (std::size_t i = 0; i < edges.size() - 1; ++i) { + if (value >= edges[i] && value < edges[i + 1]) { + return static_cast(i); + } + } + return -1; +} + +// Kinematics feeding the spherical-harmonics decomposition, ported 1:1 from +// FemtoUniverseMath::newpairfunc so results are numerically comparable. +// Returns {kv, out, side, long}, selected by shFrame: +// ShFrameLcmsNonIdentical: {kstar, fDKOut, fDKSide, fDKLong} (LCMS components of particle 1) +// ShFrameLcmsIdentical: {qinv, outLCMS, sideLCMS, longLCMS} (LCMS q-differences) +// ShFramePrf: {|qPRF|, outPRF, sidePRF, longPRF} (matches FemtoUniverse isIdenPRF=true) +inline std::tuple computeShKinematics(ROOT::Math::PtEtaPhiMVector const& part1, + ROOT::Math::PtEtaPhiMVector const& part2, + int shFrame) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector sum = p1 + p2; + + const double tPx = sum.Px(); + const double tPy = sum.Py(); + const double tPz = sum.Pz(); + const double tE = sum.E(); + const double tPtSq = tPx * tPx + tPy * tPy; + const double tMtSq = tE * tE - tPz * tPz; + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPtSq < MinTransverseMomentum || tMtSq < MinTransverseMomentum) { + return {0.f, 0.f, 0.f, 0.f}; + } + const double tPt = std::sqrt(tPtSq); + const double tMt = std::sqrt(tMtSq); + const double tM = std::sqrt(std::max(0.0, tMtSq - tPtSq)); + + const double beta = tPz / tE; + const double gamma = tE / tMt; + + // LCMS components of particle 1 (fDKOut/Side/Long in newpairfunc) + const double e1 = p1.E(); + const double fDKOut = (p1.Px() * tPx + p1.Py() * tPy) / tPt; + const double fDKSide = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; + const double fDKLong = gamma * (p1.Pz() - beta * e1); + const double fDE = gamma * (e1 - beta * p1.Pz()); + + // LCMS components of particle 2 + const double e2 = p2.E(); + const double px2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; + const double py2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; + const double pz2 = gamma * (p2.Pz() - beta * e2); + + const double outLCMS = fDKOut - px2; + const double sideLCMS = fDKSide - py2; + const double longLCMS = fDKLong - pz2; + + if (shFrame == ShFrameLcmsIdentical) { + // LCMS identical: Ylm <- LCMS q-differences, axis <- qinv + const double qinv = std::sqrt(outLCMS * outLCMS + sideLCMS * sideLCMS + longLCMS * longLCMS); + return {static_cast(qinv), + static_cast(outLCMS), static_cast(sideLCMS), static_cast(longLCMS)}; + } + if (shFrame == ShFramePrf) { + // PRF: Ylm <- PRF q-differences, axis <- |q_PRF| (matches FemtoUniverse isIdenPRF=true) + const double pE2LCMS = gamma * (e2 - beta * p2.Pz()); + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double outPRF = gammaOut * (outLCMS - betaOut * (fDE - pE2LCMS)); + const double sidePRF = sideLCMS; + const double longPRF = longLCMS; + const double qPRF = std::sqrt(outPRF * outPRF + sidePRF * sidePRF + longPRF * longPRF); + return {static_cast(qPRF), + static_cast(outPRF), static_cast(sidePRF), static_cast(longPRF)}; + } + // LCMS non-identical (default): Ylm <- LCMS components of particle 1, axis <- k* + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double fKOut = gammaOut * (fDKOut - betaOut * fDE); + const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); + return {static_cast(kstar), + static_cast(fDKOut), static_cast(fDKSide), static_cast(fDKLong)}; +} + +template +inline float getPairPhiFromEventPlaneAngle(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const T1& col) +{ + const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; + float phi = RecoDecay::constrainAngle(trackSum.Phi() - col.eventPlaneAngle()); + return phi; +} + +// triplet-only helpers + +inline ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PxPyPzEVector const& vi, ROOT::Math::PxPyPzEVector const& vj) +{ + auto trackSum = vi + vj; + auto trackDifference = vi - vj; + const double s = trackSum.M2(); + const double scaling = (s != 0.0) ? (vi.M2() - vj.M2()) / s : 0.0; + return trackDifference - scaling * trackSum; +} + +inline float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) +{ + // upfront conversion to PxPyPzEVector + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector p3(part3); + auto q12 = getqij(p1, p2); + auto q23 = getqij(p2, p3); + auto q31 = getqij(p3, p1); + double q = q12.M2() + q23.M2() + q31.M2(); + return static_cast(std::sqrt(std::max(0.0, -q))); +} + +} // namespace o2::analysis::femto::femtomath + +#endif // PWGCF_FEMTO_CORE_FEMTOMATH_H_ diff --git a/PWGCF/Femto/Core/femtoSpherHarMath.h b/PWGCF/Femto/Core/femtoSpherHarMath.h index 717368f830d..65cd7e98dd5 100644 --- a/PWGCF/Femto/Core/femtoSpherHarMath.h +++ b/PWGCF/Femto/Core/femtoSpherHarMath.h @@ -34,6 +34,11 @@ class SpherHarMath static constexpr int TrigCacheSize = MaxSupportedL + 1; // sin/cos powers cache size (indices 0..5) static constexpr double SmallLength = 1e-10; // numerical guard for |r|, |z| + // thresholds for legendreUpToYlm(): the l=N block is only filled once lmax reaches N + static constexpr int MinLmaxForL3Block = 3; + static constexpr int MinLmaxForL4Block = 4; + static constexpr int MinLmaxForL5Block = 5; + /// Values of various coefficients void initializeYlms() { @@ -138,7 +143,7 @@ class SpherHarMath } // Legendre polynomials l=3 - if (lmax > 2) { // o2-linter: disable=magic-number (l index, mirrors lmax>0/1 above) + if (lmax >= MinLmaxForL3Block) { lbuf[6] = sins[3]; lbuf[7] = sins[2] * coss[1]; lbuf[8] = (5 * coss[2] - 1) * sins[1]; @@ -146,7 +151,7 @@ class SpherHarMath } // Legendre polynomials l=4 - if (lmax > 3) { // o2-linter: disable=magic-number (l index) + if (lmax >= MinLmaxForL4Block) { lbuf[10] = sins[4]; lbuf[11] = sins[3] * coss[1]; lbuf[12] = (7 * coss[2] - 1) * sins[2]; @@ -155,7 +160,7 @@ class SpherHarMath } // Legendre polynomials l=5 - if (lmax > 4) { // o2-linter: disable=magic-number (l index) + if (lmax >= MinLmaxForL5Block) { lbuf[15] = sins[5]; lbuf[16] = sins[4] * coss[1]; lbuf[17] = (9 * coss[2] - 1) * sins[3]; diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index 0d28ce5b99e..5d227b7500d 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -16,6 +16,7 @@ #ifndef PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" #include "PWGCF/Femto/Core/femtoSpherHarMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" @@ -29,14 +30,11 @@ #include #include -#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) -#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include #include -#include #include #include #include @@ -762,14 +760,14 @@ class PairHistManager mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); // compute true kinematics - mTrueKt = getKt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMt(mTrueParticle1, mTrueParticle2); - mTrueMinv = getMinv(mTrueParticle1, mTrueParticle2); - mTrueKstar = getKstar(mTrueParticle1, mTrueParticle2); + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); if (mPlotBertschPratt) { std::tie(mTrueQout, mTrueQside, mTrueQlong) = - computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); } } @@ -815,10 +813,10 @@ class PairHistManager mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(particle1.pt(), particle1.eta(), particle1.phi(), mPdgMass1); mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(particle2.pt(), particle2.eta(), particle2.phi(), mPdgMass2); - mTrueKt = getKt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMt(mTrueParticle1, mTrueParticle2); - mTrueMinv = getMinv(mTrueParticle1, mTrueParticle2); - mTrueKstar = getKstar(mTrueParticle1, mTrueParticle2); + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); // in the pure mc-truth path there is no reco counterpart, so the generated values are also // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms @@ -829,7 +827,7 @@ class PairHistManager mKstar = mTrueKstar; if (mPlotBertschPratt) { - std::tie(mTrueQout, mTrueQside, mTrueQlong) = computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + std::tie(mTrueQout, mTrueQside, mTrueQlong) = femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); } if (mPlotDeltaEtaDeltaPhi) { mTrueDeltaEta = particle1.eta() - particle2.eta(); @@ -1314,27 +1312,27 @@ class PairHistManager mParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * particle2.pt(), particle2.eta(), particle2.phi(), mass2); // set kT - mKt = getKt(mParticle1, mParticle2); + mKt = femtomath::getKt(mParticle1, mParticle2); // set mT - mMt = getMt(mParticle1, mParticle2); + mMt = femtomath::getMt(mParticle1, mParticle2, mMtType); // set Minv - mMassInv = getMinv(mParticle1, mParticle2); + mMassInv = femtomath::getMinv(mParticle1, mParticle2); // set kstar - mKstar = getKstar(mParticle1, mParticle2); + mKstar = femtomath::getKstar(mParticle1, mParticle2); if (mPlotBertschPratt) { - std::tie(mQout, mQside, mQlong) = computeBertschPrattLCMS(mParticle1, mParticle2); + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); } if (mPlotEventShape) { if (!mPlotBertschPratt) { - std::tie(mQout, mQside, mQlong) = computeBertschPrattLCMS(mParticle1, mParticle2); + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); } if constexpr (utils::HasEventShape) { - mPairPhiFromEventPlaneAngle = getPairPhiFromEventPlaneAngle(mParticle1, mParticle2, col); + mPairPhiFromEventPlaneAngle = femtomath::getPairPhiFromEventPlaneAngle(mParticle1, mParticle2, col); mQvector = col.qvec(); } else { LOG(fatal) << "plotEventShape is enabled but the collision table has no event-shape columns. Breaking..."; @@ -1342,7 +1340,7 @@ class PairHistManager } if (mPlotSH) { - std::tie(mShKv, mShOut, mShSide, mShLong) = computeShKinematics(mParticle1, mParticle2); + std::tie(mShKv, mShOut, mShSide, mShLong) = femtomath::computeShKinematics(mParticle1, mParticle2, mShFrame); } if (mPlotDeltaEtaDeltaPhi) { @@ -1351,10 +1349,10 @@ class PairHistManager } if (mPlotAlpha || mPlotKstarVsAlphaVsMtVsMult || mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { - mAlpha = getAlpha(mParticle1, mParticle2); + mAlpha = femtomath::getAlpha(mParticle1, mParticle2); } if (mPlotBetaLab || mPlotKstarVsBetaLabVsMtVsMult || mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { - mBetaLab = getBetaLab(mParticle1, mParticle2); + mBetaLab = femtomath::getBetaLab(mParticle1, mParticle2); } if (mPlotDalitz) { @@ -1506,8 +1504,8 @@ class PairHistManager } if (mPlotSH) { const float shCentValue = mShUseCent ? mCent : mMult; - const int iCent = findShBin(shCentValue, mShCentEdges); - const int iKt = findShBin(mKt, mShKtEdges); + const int iCent = femtomath::findShBin(shCentValue, mShCentEdges); + const int iKt = femtomath::findShBin(mKt, mShKtEdges); if (iCent >= 0 && iKt >= 0) { mYlm.doYlmUpToL(mShLMax, mShOut, mShSide, mShLong, mShYlmBuffer.data()); for (std::size_t i = 0; i < mShYlmBuffer.size(); ++i) { @@ -1529,7 +1527,7 @@ class PairHistManager if (mShPlot1D) { mShBinCount[iCent][iKt]->Fill(mShKv, 1.0); // FemtoUniverse h1D = f3d[0]: qinv (=2k*) for identical-LCMS, else k*. - const float sh1DValue = (mShFrame == ShFrameLcmsIdentical) ? (2.0f * mKstar) : mKstar; + const float sh1DValue = (mShFrame == femtomath::ShFrameLcmsIdentical) ? (2.0f * mKstar) : mKstar; mSh1D[iCent][iKt]->Fill(sh1DValue); } } @@ -1606,212 +1604,6 @@ class PairHistManager } } - float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = (part1 + part2); - double kt = 0.5 * sum.Pt(); - return static_cast(kt); - } - - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0; - double averageMass = 0; - double reducedMass = 0; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; - } - return static_cast(mt); - } - - float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - return static_cast((part1 + part2).M()); - } - - float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - // Use Cartesian 4-vectors: addition/M2() become pure arithmetic - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - - // Mandelstam s = (p1 + p2)^2 - const double s = (p1 + p2).M2(); - const double m1sq = p1.M2(); - const double m2sq = p2.M2(); - - // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 - const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; - - // k* = 0.5 * sqrt(λ/s) - return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); - } - - float getAlpha(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - return static_cast(RecoDecay::constrainAngle(part2.phi() - part1.phi(), -o2::constants::math::PI)); - } - - float getBetaLab(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - ROOT::Math::XYZVector p1(part1.Px(), part1.Py(), part1.Pz()); - ROOT::Math::XYZVector p2(part2.Px(), part2.Py(), part2.Pz()); - - ROOT::Math::XYZVector kstar = p1 - p2; - ROOT::Math::XYZVector kt = p1 + p2; - - return static_cast(std::acos(kstar.Dot(kt) / std::sqrt(kstar.Mag2() * kt.Mag2()))); - } - - std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector pSum = p1 + p2; - - const double tPx = pSum.Px(); - const double tPy = pSum.Py(); - const double tPz = pSum.Pz(); - const double tE = pSum.E(); - - const double tPt = std::sqrt(tPx * tPx + tPy * tPy); - const double tMt = std::sqrt(tE * tE - tPz * tPz); - - static constexpr double MinTransverseMomentum = 1e-9; - if (tPt < MinTransverseMomentum || tMt < MinTransverseMomentum) { - return {0.0f, 0.0f, 0.0f}; - } - - const double betaL = tPz / tE; - const double gammaL = tE / tMt; - - const double kout1 = (p1.Px() * tPx + p1.Py() * tPy) / tPt; - const double kside1 = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; - const double klong1 = gammaL * (p1.Pz() - betaL * p1.E()); - - const double kout2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; - const double kside2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; - const double klong2 = gammaL * (p2.Pz() - betaL * p2.E()); - - auto qOut = static_cast(kout1 - kout2); - auto qSide = static_cast(kside1 - kside2); - auto qLong = static_cast(klong1 - klong2); - - return {qOut, qSide, qLong}; - } - - // Return the bin index for value given ascending bin edges, or -1 if out of range. - // edges = {e0, e1, ..., eN} defines N bins [e0,e1), [e1,e2), ..., [e_{N-1},eN). - static int findShBin(double value, std::vector const& edges) - { - static constexpr std::size_t MinEdgesForOneBin = 2; - if (edges.size() < MinEdgesForOneBin || value < edges.front() || value >= edges.back()) { - return -1; - } - for (std::size_t i = 0; i < edges.size() - 1; ++i) { - if (value >= edges[i] && value < edges[i + 1]) { - return static_cast(i); - } - } - return -1; - } - - // Kinematics feeding the spherical-harmonics decomposition, ported 1:1 from - // FemtoUniverseMath::newpairfunc so results are numerically comparable. - // Returns {kv, out, side, long}, selected by mShFrame: - // ShFrameLcmsNonIdentical: {kstar, fDKOut, fDKSide, fDKLong} (LCMS components of particle 1) - // ShFrameLcmsIdentical: {qinv, outLCMS, sideLCMS, longLCMS} (LCMS q-differences) - // ShFramePrf: {|qPRF|, outPRF, sidePRF, longPRF} (matches FemtoUniverse isIdenPRF=true) - std::tuple computeShKinematics(ROOT::Math::PtEtaPhiMVector const& part1, - ROOT::Math::PtEtaPhiMVector const& part2) - { - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector sum = p1 + p2; - - const double tPx = sum.Px(); - const double tPy = sum.Py(); - const double tPz = sum.Pz(); - const double tE = sum.E(); - const double tPtSq = tPx * tPx + tPy * tPy; - const double tMtSq = tE * tE - tPz * tPz; - - static constexpr double MinTransverseMomentum = 1e-9; - if (tPtSq < MinTransverseMomentum || tMtSq < MinTransverseMomentum) { - return {0.f, 0.f, 0.f, 0.f}; - } - const double tPt = std::sqrt(tPtSq); - const double tMt = std::sqrt(tMtSq); - const double tM = std::sqrt(std::max(0.0, tMtSq - tPtSq)); - - const double beta = tPz / tE; - const double gamma = tE / tMt; - - // LCMS components of particle 1 (fDKOut/Side/Long in newpairfunc) - const double e1 = p1.E(); - const double fDKOut = (p1.Px() * tPx + p1.Py() * tPy) / tPt; - const double fDKSide = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; - const double fDKLong = gamma * (p1.Pz() - beta * e1); - const double fDE = gamma * (e1 - beta * p1.Pz()); - - // LCMS components of particle 2 - const double e2 = p2.E(); - const double px2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; - const double py2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; - const double pz2 = gamma * (p2.Pz() - beta * e2); - - const double outLCMS = fDKOut - px2; - const double sideLCMS = fDKSide - py2; - const double longLCMS = fDKLong - pz2; - - if (mShFrame == ShFrameLcmsIdentical) { - // LCMS identical: Ylm <- LCMS q-differences, axis <- qinv - const double qinv = std::sqrt(outLCMS * outLCMS + sideLCMS * sideLCMS + longLCMS * longLCMS); - return {static_cast(qinv), - static_cast(outLCMS), static_cast(sideLCMS), static_cast(longLCMS)}; - } - if (mShFrame == ShFramePrf) { - // PRF: Ylm <- PRF q-differences, axis <- |q_PRF| (matches FemtoUniverse isIdenPRF=true) - const double pE2LCMS = gamma * (e2 - beta * p2.Pz()); - const double betaOut = tPt / tMt; - const double gammaOut = tMt / tM; - const double outPRF = gammaOut * (outLCMS - betaOut * (fDE - pE2LCMS)); - const double sidePRF = sideLCMS; - const double longPRF = longLCMS; - const double qPRF = std::sqrt(outPRF * outPRF + sidePRF * sidePRF + longPRF * longPRF); - return {static_cast(qPRF), - static_cast(outPRF), static_cast(sidePRF), static_cast(longPRF)}; - } - // LCMS non-identical (default): Ylm <- LCMS components of particle 1, axis <- k* - const double betaOut = tPt / tMt; - const double gammaOut = tMt / tM; - const double fKOut = gammaOut * (fDKOut - betaOut * fDE); - const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); - return {static_cast(kstar), - static_cast(fDKOut), static_cast(fDKSide), static_cast(fDKLong)}; - } - - template - float getPairPhiFromEventPlaneAngle(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const T1& col) - { - const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; - float phi = RecoDecay::constrainAngle(trackSum.Phi() - col.eventPlaneAngle()); - return phi; - } - o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; bool mUsePdgMass = true; double mPdgMass1 = 0.; @@ -1929,9 +1721,6 @@ class PairHistManager bool mShUseCent = false; int mShLMax = 1; int mShFrame = 0; - static constexpr int ShFrameLcmsNonIdentical = 0; - static constexpr int ShFrameLcmsIdentical = 1; - static constexpr int ShFramePrf = 2; o2::framework::AxisSpec mShKstarSpec{{60, 0.0f, 0.3f}, "k* (GeV/#it{c})"}; // set in init() o2::framework::AxisSpec mShCentSpec{{1, 0.0f, 200.0f}, "mult/cent"}; diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index 1bc69b06eeb..7ffc306308c 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -16,6 +16,7 @@ #ifndef PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -28,7 +29,6 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include #include #include @@ -67,6 +67,7 @@ enum TripletHist { kQ3VsMtVsMultVsCent, kQ3VsMtVsPt1VsPt2VsPt3VsMult, kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, + kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, // higher dimensions with mass // mc kTrueQ3VsQ3, @@ -131,6 +132,7 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plotQ3VsMtVsMultVsCent{"plotQ3VsMtVsMultVsCent", false, "Enable 4D histogram (Q3 vs Mt vs Mult Vs Cent)"}; o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMult{"plotQ3VsMtVsPt1VsPt2VsPt3VsMult", false, "Enable 6D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult)"}; o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent{"plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", false, "Enable 7D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult vs Cent)"}; + o2::framework::Configurable plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3{"plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", false, "Enable 7D histogram (Q3 vs kstar12 vs kstar13 vs kstar23 vs Pt1 vs Pt2 vs Pt3)"}; o2::framework::ConfigurableAxis q3{"q3", {{600, 0, 6}}, "q3"}; o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar (between pairs of particles)"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; @@ -152,6 +154,12 @@ struct ConfTripletCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable q3Min{"q3Min", -1, "Minimal Q3 (set to -1 to deactivate)"}; o2::framework::Configurable mtMax{"mtMax", -1, "Maximal mt (set to -1 to deactivate)"}; o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; + o2::framework::Configurable kstar12Min{"kstar12Min", -1, "Minimal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar12Max{"kstar12Max", -1, "Maximal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Min{"kstar13Min", -1, "Minimal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Max{"kstar13Max", -1, "Maximal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Min{"kstar23Min", -1, "Minimal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Max{"kstar23Max", -1, "Maximal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; @@ -182,6 +190,7 @@ constexpr std::array, kTripletHistogramLast> // n-D with mass {kQ3VsMtVsPt1VsPt2VsPt3VsMult, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMult", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity;"}, {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity; Centrality (%);"}, + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", "Q_{3} vs k*_{12} vs k*_{13} vs k*_{23} vs p_{T,1} vs p_{T,2} vs p_{T,3}; Q_{3} (GeV/#it{c}); k*_{12} (GeV/#it{c}); k*_{13} (GeV/#it{c}); k*_{23} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c});"}, {kTrueQ3VsQ3, o2::framework::HistType::kTH2F, "hTrueQ3VsQ3", "Q_{3,True} vs Q_{3}; Q_{3,true} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, {kTrueMtVsMt, o2::framework::HistType::kTH2F, "hTrueMtVsMt", "m_{T,True} vs m_{T}; m_{T,True} (GeV/#it{c}^{2}); m_{T} (GeV/#it{c}^{2})"}, {kTrueMultVsMult, o2::framework::HistType::kTH2F, "hTrueMultVsMult", "Multiplicity_{True} vs Multiplicity; Multiplicity_{True} ; Multiplicity"}, @@ -207,24 +216,25 @@ constexpr std::array, kTripletHistogramLast> }}; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TRIPLET_HIST_ANALYSIS_MAP(conf, confMixing) \ - {kQ3, {(conf).q3}}, \ - {kMt, {(conf).mt}}, \ - {kKstar12, {(conf).kstar}}, \ - {kKstar13, {(conf).kstar}}, \ - {kKstar23, {(conf).kstar}}, \ - {kPt1VsQ3, {(conf).pt1, (conf).q3}}, \ - {kPt2VsQ3, {(conf).pt2, (conf).q3}}, \ - {kPt3VsQ3, {(conf).pt3, (conf).q3}}, \ - {kQ3VsMt, {(conf).q3, (conf).mt}}, \ - {kQ3VsMult, {(conf).q3, (conf).multiplicity}}, \ - {kQ3VsCent, {(conf).q3, (conf).centrality}}, \ - {kPt1VsPt2VsPt3, {(conf).pt1, (conf).pt2, (conf).pt3}}, \ - {kQ3VsPt1VsPt2VsPt3, {(conf).q3, (conf).pt1, (conf).pt2, (conf).pt3}}, \ - {kQ3VsMtVsMult, {(conf).q3, (conf).mt, (conf).multiplicity}}, \ - {kQ3VsMtVsMultVsCent, {(conf).q3, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity, (conf).centrality}}, +#define TRIPLET_HIST_ANALYSIS_MAP(conf, confMixing) \ + {kQ3, {(conf).q3}}, \ + {kMt, {(conf).mt}}, \ + {kKstar12, {(conf).kstar}}, \ + {kKstar13, {(conf).kstar}}, \ + {kKstar23, {(conf).kstar}}, \ + {kPt1VsQ3, {(conf).pt1, (conf).q3}}, \ + {kPt2VsQ3, {(conf).pt2, (conf).q3}}, \ + {kPt3VsQ3, {(conf).pt3, (conf).q3}}, \ + {kQ3VsMt, {(conf).q3, (conf).mt}}, \ + {kQ3VsMult, {(conf).q3, (conf).multiplicity}}, \ + {kQ3VsCent, {(conf).q3, (conf).centrality}}, \ + {kPt1VsPt2VsPt3, {(conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsPt1VsPt2VsPt3, {(conf).q3, (conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsMtVsMult, {(conf).q3, (conf).mt, (conf).multiplicity}}, \ + {kQ3VsMtVsMultVsCent, {(conf).q3, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, {(conf).q3, (conf).kstar, (conf).kstar, (conf).kstar, (conf).pt1, (conf).pt2, (conf).pt3}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRIPLET_HIST_MIXING_QA_MAP(confMixing) \ @@ -334,6 +344,12 @@ class TripletHistManager mQ3Max = ConfTripletCuts.q3Max.value; mMtMin = ConfTripletCuts.mtMin.value; mMtMax = ConfTripletCuts.mtMax.value; + mKstar12Min = ConfTripletCuts.kstar12Min.value; + mKstar12Max = ConfTripletCuts.kstar12Max.value; + mKstar13Min = ConfTripletCuts.kstar13Min.value; + mKstar13Max = ConfTripletCuts.kstar13Max.value; + mKstar23Min = ConfTripletCuts.kstar23Min.value; + mKstar23Max = ConfTripletCuts.kstar23Max.value; mPlotKstar = ConfTripletBinning.plotKstar.value; mPlotPt1VsPt2VsPt3 = ConfTripletBinning.plotPt1VsPt2VsPt3.value; @@ -342,6 +358,15 @@ class TripletHistManager mPlotQ3VsMtVsMultVsCent = ConfTripletBinning.plotQ3VsMtVsMultVsCent.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMult.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent.value; + mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3.value; + + // kstar12/13/23 are only computed on demand (they cost 3 extra femtomath::getKstar() calls per + // triplet); anything that needs the value -- the 1D plots, the 7D histogram, or a cut -- + // must be OR'ed in here, otherwise it silently stays at its default of 0 + mRequireKstar = mPlotKstar || mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 || + mKstar12Min > 0.f || mKstar12Max > 0.f || + mKstar13Min > 0.f || mKstar13Max > 0.f || + mKstar23Min > 0.f || mKstar23Max > 0.f; mPairCorrelationQa = ConfMixing.enablePairCorrelationQa.value; mEventMixingQa = ConfMixing.enableEventMixingQa.value; @@ -390,12 +415,12 @@ class TripletHistManager // set mT mMt = getMt(mParticle1, mParticle2, mParticle3); // set Q3 - mQ3 = getQ3(mParticle1, mParticle2, mParticle3); + mQ3 = femtomath::getQ3(mParticle1, mParticle2, mParticle3); - if (mPlotKstar) { - mKstar12 = getKstar(mParticle1, mParticle2); - mKstar13 = getKstar(mParticle1, mParticle3); - mKstar23 = getKstar(mParticle2, mParticle3); + if (mRequireKstar) { + mKstar12 = femtomath::getKstar(mParticle1, mParticle2); + mKstar13 = femtomath::getKstar(mParticle1, mParticle3); + mKstar23 = femtomath::getKstar(mParticle2, mParticle3); } // if one of the particles has a mass getter, we cache the value for the filling later @@ -437,7 +462,7 @@ class TripletHistManager mTrueParticle3 = ROOT::Math::PtEtaPhiMVector(particle3.pt(), particle3.eta(), particle3.phi(), mPdgMass3); mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); - mTrueQ3 = getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); // in the pure mc-truth path there is no reco counterpart, so the generated values are also // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms @@ -445,10 +470,10 @@ class TripletHistManager mMt = mTrueMt; mQ3 = mTrueQ3; - if (mPlotKstar) { - mTrueKstar12 = getKstar(mTrueParticle1, mTrueParticle2); - mTrueKstar13 = getKstar(mTrueParticle1, mTrueParticle3); - mTrueKstar23 = getKstar(mTrueParticle2, mTrueParticle3); + if (mRequireKstar) { + mTrueKstar12 = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + mTrueKstar13 = femtomath::getKstar(mTrueParticle1, mTrueParticle3); + mTrueKstar23 = femtomath::getKstar(mTrueParticle2, mTrueParticle3); mKstar12 = mTrueKstar12; mKstar13 = mTrueKstar13; mKstar23 = mTrueKstar23; @@ -493,7 +518,7 @@ class TripletHistManager // set true mT mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); // set true Q3 - mTrueQ3 = getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); } template @@ -535,7 +560,13 @@ class TripletHistManager return (!(mQ3Min > 0.f) || mTrueQ3 > mQ3Min) && (!(mQ3Max > 0.f) || mTrueQ3 < mQ3Max) && (!(mMtMin > 0.f) || mTrueMt > mMtMin) && - (!(mMtMax > 0.f) || mTrueMt < mMtMax); + (!(mMtMax > 0.f) || mTrueMt < mMtMax) && + (!(mKstar12Min > 0.f) || mTrueKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mTrueKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mTrueKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mTrueKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mTrueKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mTrueKstar23 < mKstar23Max); } bool checkTripletCuts() const @@ -543,7 +574,13 @@ class TripletHistManager return (!(mQ3Min > 0.f) || mQ3 > mQ3Min) && (!(mQ3Max > 0.f) || mQ3 < mQ3Max) && (!(mMtMin > 0.f) || mMt > mMtMin) && - (!(mMtMax > 0.f) || mMt < mMtMax); + (!(mMtMax > 0.f) || mMt < mMtMax) && + (!(mKstar12Min > 0.f) || mKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mKstar23 < mKstar23Max); } template @@ -630,46 +667,6 @@ class TripletHistManager } private: - ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PxPyPzEVector const& vi, ROOT::Math::PxPyPzEVector const& vj) - { - auto trackSum = vi + vj; - auto trackDifference = vi - vj; - const double s = trackSum.M2(); - const double scaling = (s != 0.0) ? (vi.M2() - vj.M2()) / s : 0.0; - return trackDifference - scaling * trackSum; - } - - float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) - { - // upfront conversion to PxPyPzEVector - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - const ROOT::Math::PxPyPzEVector p3(part3); - auto q12 = getqij(p1, p2); - auto q23 = getqij(p2, p3); - auto q31 = getqij(p3, p1); - double q = q12.M2() + q23.M2() + q31.M2(); - return static_cast(std::sqrt(std::max(0.0, -q))); - } - - float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - // Use Cartesian 4-vectors: addition/M2() become pure arithmetic - const ROOT::Math::PxPyPzEVector p1(part1); - const ROOT::Math::PxPyPzEVector p2(part2); - - // Mandelstam s = (p1 + p2)^2 - const double s = (p1 + p2).M2(); - const double m1sq = p1.M2(); - const double m2sq = p2.M2(); - - // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 - const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; - - // k* = 0.5 * sqrt(λ/s) - return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); - } - void initAnalysis(std::map> const& Specs) { std::string analysisDir = std::string(prefix) + std::string(AnalysisDir); @@ -711,6 +708,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistDesc(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistType(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), {Specs.at(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent)}); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistDesc(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistType(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), {Specs.at(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3)}); + } } void initMc(std::map> const& Specs) @@ -827,6 +827,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable)), mQ3, mMt, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt(), mMult, mCent); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable)), mQ3, mKstar12, mKstar13, mKstar23, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt()); + } } void fillMc() @@ -841,33 +844,11 @@ class TripletHistManager } } - double getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0.; - double averageMass = 0.; - double reducedMass = 0.; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M() / (part1.M() + part2.M())); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; - } - return mt; - } - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) { - return static_cast((getMt(part1, part2) + getMt(part2, part3) + getMt(part1, part3)) / 3.); + // all three pairwise combinations are intentional, not a mixed-up argument order + // NOLINTNEXTLINE(readability-suspicious-call-argument) + return (femtomath::getMt(part1, part2, mMtType) + femtomath::getMt(part2, part3, mMtType) + femtomath::getMt(part1, part3, mMtType)) / 3.f; } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; @@ -914,18 +895,26 @@ class TripletHistManager float mQ3Max = -1.f; float mMtMin = -1.f; float mMtMax = -1.f; + float mKstar12Min = -1.f; + float mKstar12Max = -1.f; + float mKstar13Min = -1.f; + float mKstar13Max = -1.f; + float mKstar23Min = -1.f; + float mKstar23Max = -1.f; // flags bool mPlot1d = true; bool mPlot2d = true; bool mPlotKstar = false; + bool mRequireKstar = false; bool mPlotPt1VsPt2VsPt3 = false; bool mPlotQ3VsPt1VsPt2VsPt3 = false; bool mPlotQ3VsMtVsMult = false; bool mPlotQ3VsMtVsMultVsCent = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = false; + bool mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = false; // mixing qa bool mPairCorrelationQa = false; From da193377833d1ae1692d15197651402cd03c81bf Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Mon, 2 Nov 2026 05:53:57 +0100 Subject: [PATCH 521/648] [PWGLF,PWGMM] Add midrapidity tracks for weak decay correction (#18541) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 95 ++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index e9a24df5e94..a3e9d5ed97a 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -26,6 +26,7 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +248,18 @@ struct MultE { static constexpr int CmultMFT = 4; }; +static constexpr TrackSelectionFlags::flagtype TrackSelectionITS = + TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | + TrackSelectionFlags::kITSHits; +static constexpr TrackSelectionFlags::flagtype TrackSelectionTPC = + TrackSelectionFlags::kTPCNCls | + TrackSelectionFlags::kTPCCrossedRowsOverNCls | + TrackSelectionFlags::kTPCChi2NDF; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = + TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCAXYonly = + TrackSelectionFlags::kDCAxy; + struct DndetaMFTPbPb { SliceCache cache; @@ -257,6 +271,7 @@ struct DndetaMFTPbPb { HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { + Configurable dcaZmidrap{"dcaZmidrap", 0.2f, "Custom DCA Z cut (ignored if negative)"}; Configurable useEvtSel{"useEvtSel", true, "Use event selection"}; Configurable useIRFromCCDB{"useIRFromCCDB", false, "Flag to retrieve Interaction rate from CCDB"}; Configurable useIRCut{"useIRCut", false, "Flag to cut on IR rate"}; @@ -296,6 +311,7 @@ struct DndetaMFTPbPb { ConfigurableAxis dcaZBins{"dcaZBins", {100, -0.5, 0.5}, "DCAz binning (cm)"}; ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; + ConfigurableAxis etaMidrapBins{"etaMidrapBins", {40, -2., 2.}, "#eta midrapidity binning"}; ConfigurableAxis chiSqPerNclBins{"chiSqPerNclBins", {100, 0, 100}, "#chi^{2} binning"}; ConfigurableAxis nClBins{"nClBins", {10, 0.5, 10.5}, "number of clusters binning"}; ConfigurableAxis tanLambdaBins{"tanLambdaBins", {100, -25, 0}, "binning for tan(lambda)"}; @@ -311,6 +327,7 @@ struct DndetaMFTPbPb { } rctCuts; struct : ConfigurableGroup { + Configurable etaRangeMidrap{"etaRangeMidrap", 1.0f, "Eta range for midrapidity tracks"}; Configurable usephiCut{"usephiCut", false, "use azimuthal angle cut"}; Configurable phiCut{"phiCut", 0.1f, "Cut on azimuthal angle of MFT tracks"}; Configurable minPhi{"minPhi", 0.f, ""}; @@ -395,6 +412,7 @@ struct DndetaMFTPbPb { const AxisSpec dcazAxis = {binOpt.dcaZBins, "DCA-z axis"}; const AxisSpec phiAxis = {binOpt.phiBins, "#phi axis"}; const AxisSpec etaAxis = {binOpt.etaBins, "#eta axis"}; + const AxisSpec etaMidrapAxis = {binOpt.etaMidrapBins, "#eta midrapidity axis"}; const AxisSpec chiSqAxis = {binOpt.chiSqPerNclBins, "Chi2 axis"}; const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; @@ -630,6 +648,9 @@ struct DndetaMFTPbPb { } else { registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); } + if (doprocessMcInclusive) { + registryMC.add({"TracksMidrap/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); registryMC.add({"Tracks/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); @@ -682,6 +703,9 @@ struct DndetaMFTPbPb { } else { registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); } + if (doprocessMcCentFT0C) { + registryMC.add({"TracksMidrap/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); registryMC.add({"Tracks/Centrality/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); @@ -841,6 +865,14 @@ struct DndetaMFTPbPb { } } + /// Filter (midrapidity selections) + expressions::Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && + ncheckbit(aod::track::trackCutFlag, TrackSelectionITS); + expressions::Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), + ncheckbit(aod::track::trackCutFlag, TrackSelectionTPC), true); + expressions::Filter fTrackSelectionDCA = ifnode(gConf.dcaZmidrap.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDCAXYonly), + ncheckbit(aod::track::trackCutFlag, TrackSelectionDCA)); + /// Joined tables using FullBCs = soa::Join; using CollBCs = soa::Join; @@ -856,6 +888,10 @@ struct DndetaMFTPbPb { /// Tracks using MftTracksLabeled = soa::Join; using MftBestTracksLabeled = soa::Join; + // midrapidity tracks + using MidrapTracksLabeled = soa::Join; + using FiltMidrapTracksLabeled = soa::Filtered; + /// Particles using ParticlesIdx = soa::Join; @@ -1733,6 +1769,7 @@ struct DndetaMFTPbPb { Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); Preslice perColMcFiltTrk = o2::aod::fwdtrack::collisionId; + Preslice perColMcFiltTrkMidrap = aod::track::collisionId; std::vector nRecPerCol; std::vector centRecPerCol; @@ -1742,7 +1779,8 @@ struct DndetaMFTPbPb { void processMc(typename MC::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { nRecPerCol.clear(); centRecPerCol.clear(); @@ -1821,6 +1859,51 @@ struct DndetaMFTPbPb { registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } + // midrapidity tracks for weak deacy correction study + auto midtracksPerCol = midtracks.sliceBy(perColMcFiltTrkMidrap, collision.globalIndex()); + for (auto const& midtrack : midtracksPerCol) { + if (std::abs(midtrack.eta()) >= trackCuts.etaRangeMidrap) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), occRec); + } + if (midtrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (midtrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = midtrack.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), occRec); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } + } + } + auto nTrkRec = 0; auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); for (auto const& track : perColSample) { @@ -1947,9 +2030,10 @@ struct DndetaMFTPbPb { void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMc(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcInclusive, "Count MC particles (inclusive)", false); @@ -1957,9 +2041,10 @@ struct DndetaMFTPbPb { void processMcCentFT0C(CollsMCExtraMult::iterator const& mccollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, - MftTracksLabeled const& tracks) + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMc(mccollision, collisions, particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } PROCESS_SWITCH(DndetaMFTPbPb, processMcCentFT0C, "Count MC particles in FT0C centrality bins", false); From 78fc69b7cf345a3d201af0f9163ae1fd5bc0ae04 Mon Sep 17 00:00:00 2001 From: Pei-Ying Kuan <128475937+pykuan@users.noreply.github.com> Date: Mon, 2 Nov 2026 06:43:36 +0100 Subject: [PATCH 522/648] [PWGCF] add 3p correlation profile and fix weight bugs (#18543) Co-authored-by: Pei-Ying Kuan --- .../Tasks/multiparticleCumulants.cxx | 173 +++++++++++++++--- 1 file changed, 150 insertions(+), 23 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx index 83bdeeec514..0a27a5ef7b4 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -203,9 +204,11 @@ static constexpr std::array BeforeAfterNames = "After"}; static constexpr int NumHarmonics = 3; // n = 2, 3, 4 -static constexpr int NumTwoPCorrBins = NumHarmonics; // , , , in this case 3 -static constexpr int NumFourPCorrBins = (NumHarmonics * (NumHarmonics + 1)) / 2; // , , , , , , with , in this case 6 -static constexpr int NumSixPCorrBins = (NumHarmonics * (NumHarmonics - 1) * (NumHarmonics - 2)) / 6; // , without , in this case 1 +static constexpr int NumTwoPCorrBins = NumHarmonics + 1; // <<2>>_n = , , , , in this case 3 +static constexpr int NumThreePCorrBins = 3; // <<3>>_{n1, n2, n3}, (n1, n2, n3) = (2, -1, -1), (3, -2, -1), (4, -2, -2), in this case 3 +static constexpr int NumFourPCorrBins = (NumHarmonics * (NumHarmonics + 1)) / 2; // <<4>>_{n1, n1, n2, n2} = , , , , , , in this case 6 +static constexpr int NumSixPCorrBins = (NumHarmonics * (NumHarmonics - 1) * (NumHarmonics - 2)) / 6; // <<6>>_{n1, n1, n2, n2, n3, n3} = , in this case 1 +static constexpr std::array, NumThreePCorrBins> ThreePHarmonicIndex{{{2, 1, 1}, {3, 2, 1}, {4, 2, 2}}}; static constexpr std::array, NumFourPCorrBins> FourPHarmonicIndex{{{2, 2}, {2, 3}, {2, 4}, {3, 3}, {3, 4}, {4, 4}}}; static constexpr int NumEtaGap = 3; @@ -295,6 +298,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam Configurable> cfNumTrackBins{"cfNumTrackBins", {100, 0., 5000.}, "nNumTrackBins, NumTrackMin, NumTrackMax"}; Configurable> cfTwoParticleCorrBins{"cfTwoParticleCorrBins", {100, 0., 2e-3}, "nTwoParticleCorrBins, TwoParticleCorrMin, TwoParticleCorrMax"}; + Configurable> cfThreeParticleCorrBins{"cfThreeParticleCorrBins", {100, 0., 2e-4}, "nThreeParticleCorrBins, ThreeParticleCorrMin, ThreeParticleCorrMax"}; Configurable> cfFourParticleCorrBins{"cfFourParticleCorrBins", {100, 0., 1e-7}, "nFourParticleCorrBins, FourParticleCorrMin, FourParticleCorrMax"}; Configurable> cfSixParticleCorrBins{"cfSixParticleCorrBins", {100, 0., 1e-11}, "nSixParticleCorrBins, SixParticleCorrMin, SixParticleCorrMax"}; Configurable> cfNumTrackTotalBins{"cfNumTrackTotalBins", {100, 0, 3000}, "nNumTrackTotalBins, NumTrackTotalMin, NumTrackTotalMax"}; @@ -364,6 +368,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam std::vector fNumContribBins = {100, 0, 5000}; std::vector fNumTrackBins = {100, 0, 5000}; std::vector fTwoParticleCorrBins = {100, 0, 2e-3}; + std::vector fThreeParticleCorrBins = {100, 0., 2e-4}; std::vector fFourParticleCorrBins = {100, 0, 1e-7}; std::vector fSixParticleCorrBins = {100, 0, 1e-11}; std::vector fNumTrackTotalBins = {100, 0, 3000}; @@ -437,14 +442,18 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam TList* fMultiparticleCorrelationBackupList = nullptr; std::map fMultiparticleCorrelationByRunMap; std::array fTwoParticleCorrelationProfiles{}; // [cut] + std::array fThreeParticleCorrelationProfiles{}; std::array fFourParticleCorrelationProfiles{}; std::array fSixParticleCorrelationProfiles{}; std::array, NumEtaGap> fTwoParticleCorrelationGapProfiles{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fTwoParticleCorrelationHistograms{}; // [cut][event weight][n] + std::array, eBeforeAfter_N>, eBeforeAfter_N> fThreeParticleCorrelationHistograms{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fFourParticleCorrelationHistograms{}; std::array, eBeforeAfter_N>, eBeforeAfter_N> fSixParticleCorrelationHistograms{}; std::array, NumEtaGap> fTwoParticleCorrelationGapHistograms{}; std::array fTwoParticleCorrelationNumTrackProfiles{}; + std::array fThreeParticleCorrelationNumTrackProfiles{}; + std::array fFourParticleCorrelationNumTrackProfiles{}; } mc; struct EventByEventQuantities { @@ -455,12 +464,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam float fCentralitySim = 0.; float fImpactParameter = 0.; float fNumContrib = 0.; - std::array, eBeforeAfter_N> fTwoParticleCorrelationEbye{}; // <<2>> [before, after][, , ] + std::array, eBeforeAfter_N> fTwoParticleCorrelationEbye{}; // <<2>> [before, after][, ,, ] std::array, eBeforeAfter_N> fTwoParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fTwoParticleCorrelationMaxEbye{}; std::array, eBeforeAfter_N> fFourParticleCorrelationEbye{}; // <<4>> [before, after][, , , , , ] std::array, eBeforeAfter_N> fFourParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fFourParticleCorrelationMaxEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationEbye{}; // <<3>> [before, after][] + std::array, eBeforeAfter_N> fThreeParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationMaxEbye{}; std::array, eBeforeAfter_N> fSixParticleCorrelationEbye{}; // <<6>> [before, after][] std::array, eBeforeAfter_N> fSixParticleCorrelationMinEbye{}; std::array, eBeforeAfter_N> fSixParticleCorrelationMaxEbye{}; @@ -669,7 +681,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam return TComplex::Conjugate(mcc.fQvectorAfter[-n][p]); } - // TComplex mccTwo(int n1, int n2, EnBeforeAfter eba) + // TComplex mccFour(int n1, int n2, EnBeforeAfter eba) // { // return mccQ(n1, 1, eba) * mccQ(n2, 1, eba) - mccQ(n1 + n2, 2, eba); // } @@ -1006,13 +1018,19 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fTwoParticleCorrelationProfiles[i] = nullptr; + mc.fThreeParticleCorrelationProfiles[i] = nullptr; mc.fFourParticleCorrelationProfiles[i] = nullptr; mc.fSixParticleCorrelationProfiles[i] = nullptr; - mc.fTwoParticleCorrelationGapProfiles[i].fill(nullptr); + for (int k = 0; k < NumEtaGap; k++) { + mc.fTwoParticleCorrelationGapProfiles[k][i] = nullptr; + } mc.fTwoParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fThreeParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fFourParticleCorrelationNumTrackProfiles[i] = nullptr; for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight mc.fTwoParticleCorrelationHistograms[i][j].fill(nullptr); + mc.fThreeParticleCorrelationHistograms[i][j].fill(nullptr); mc.fFourParticleCorrelationHistograms[i][j].fill(nullptr); mc.fSixParticleCorrelationHistograms[i][j].fill(nullptr); } @@ -1023,15 +1041,15 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[i] = new TProfile(Form("prof2%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationProfiles[i]->Sumw2(); mc.fTwoParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof2NumTrack%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); - mc.fTwoParticleCorrelationNumTrackProfiles[i]->Sumw2(); + // mc.fTwoParticleCorrelationNumTrackProfiles[i]->Sumw2(); for (int n = 0; n < NumEtaGap; n++) { mc.fTwoParticleCorrelationGapProfiles[n][i] = new TProfile(Form("prof2%sCutGapped%.1f", BeforeAfterNames[i + 2], tc.fEtaGap[n]), Form("2-p correlation %s cut, %.1f gapped", BeforeAfterNames[i], tc.fEtaGap[n]), NumTwoPCorrBins, 0., 1.); mc.fTwoParticleCorrelationGapProfiles[n][i]->Sumw2(); } - for (int k = 0; k < NumTwoPCorrBins; k++) { // v2, v3, v4 - mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); - mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + for (int k = 0; k < NumTwoPCorrBins; k++) { // v1, v2, v3, v4 + mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; int lbound = m * width; @@ -1039,16 +1057,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); } for (int n = 0; n < NumEtaGap; n++) { - mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 2)); + mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); if (static_cast(i)) { - mc.fTwoParticleCorrelationGapHistograms[n][k] = new TH1D(Form("hist2v%dAfterCutWithGap%.1f", k + 2, tc.fEtaGap[n]), Form("2-p correlation v%d^2 after cut with gap %.1f", k + 2, tc.fEtaGap[n]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + mc.fTwoParticleCorrelationGapHistograms[n][k] = new TH1D(Form("hist2v%dAfterCutWithGap%.1f", k + 1, tc.fEtaGap[n]), Form("2-p correlation v%d^2 after cut with gap %.1f", k + 1, tc.fEtaGap[n]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); mc.fTwoParticleCorrelationGapHistograms[n][k]->Sumw2(); mc.fTwoParticleCorrelationGapHistograms[n][k]->SetOption("HIST"); } } for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight - mc.fTwoParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist2v%d%sCut%sWeight", k + 2, BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("2-p correlation v%d^2 %s cut %s weight", k + 2, BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + mc.fTwoParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist2v%d%sCut%sWeight", k + 1, BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("2-p correlation v%d^2 %s cut %s weight", k + 1, BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); if (static_cast(j)) { mc.fTwoParticleCorrelationHistograms[i][j][k]->Sumw2(); mc.fTwoParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); @@ -1068,13 +1086,52 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } + // Define 3p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fThreeParticleCorrelationProfiles[i] = new TProfile(Form("prof3%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1.); + mc.fThreeParticleCorrelationProfiles[i]->Sumw2(); + mc.fThreeParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof3NumTrack%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fThreeParticleCorrelationNumTrackProfiles[i]->Sumw2(); + + for (int k = 0; k < NumThreePCorrBins; k++) { + mc.fThreeParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fThreeParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist3v%dv%dv%d%sCut%sWeight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("3-p correlation v%dv%dv%d %s cut %s weight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fThreeParticleCorrBins[0]), tc.fThreeParticleCorrBins[1], tc.fThreeParticleCorrBins[2]); + if (static_cast(j)) { + mc.fThreeParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fThreeParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fThreeParticleCorrelationHistograms[i][j][k]); + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationNumTrackProfiles[i]); + } + // Define 4p profiles and histograms: for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut mc.fFourParticleCorrelationProfiles[i] = new TProfile(Form("prof4%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1.); mc.fFourParticleCorrelationProfiles[i]->Sumw2(); + mc.fFourParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof4NumTrack%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fFourParticleCorrelationNumTrackProfiles[i]->Sumw2(); for (int k = 0; k < NumFourPCorrBins; k++) { // v2v2, v2v3, v2v4, v3v3, v3v4, v4v4 mc.fFourParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight mc.fFourParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist4v%dv%d%sCut%sWeight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("4-p correlation v%d^2v%d^2 %s cut %s weight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fFourParticleCorrBins[0]), tc.fFourParticleCorrBins[1], tc.fFourParticleCorrBins[2]); @@ -1086,6 +1143,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationNumTrackProfiles[i]); } // Define 6p profiles and histograms: @@ -1385,7 +1443,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam if (ctParticleCuts(track)) { - // Fill phi/pt real histogram with this run number: + // Fill phi (real rec), pt (MC rec) histogram with this run number: wt.fPhiByRunMap.at(ebye.fRunNumber)->Fill(track.phi()); wt.fPtRealByRunMap.at(ebye.fRunNumber)->Fill(track.pt()); @@ -1446,6 +1504,8 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam pc.fParticleHistograms[ePhi][eSim][eBefore]->Fill(mcparticle.phi()); if (ctParticleCuts(mcparticle)) { + // Fill pt (MC sim) histogram with this run number: + wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); // Fill MC particle histograms after cut: pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); pc.fParticleHistograms[eEta][eSim][eAfter]->Fill(mcparticle.eta()); @@ -1468,8 +1528,8 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // 2-p correlations: for (int i = 0; i < NumTwoPCorrBins; i++) { int k = 2; - mcc.h1 = -(i + 2); - mcc.h2 = i + 2; + mcc.h1 = -(i + 1); + mcc.h2 = i + 1; std::array harmonicsTwoNum = {mcc.h1, mcc.h2}; std::array harmonicsTwoDen = {0, 0}; @@ -1483,16 +1543,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); mc.fTwoParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i]); mc.fTwoParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); - mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); + mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fTwoParticleCorrelationEbye[eBefore][i]); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eBefore][i] > ebye.fTwoParticleCorrelationMaxEbye[eBefore][i]) { ebye.fTwoParticleCorrelationMaxEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; - LOGF(info, "max v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); } if (ebye.fTwoParticleCorrelationEbye[eBefore][i] < ebye.fTwoParticleCorrelationMinEbye[eBefore][i]) { ebye.fTwoParticleCorrelationMinEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; - LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); } } else { if (tc.fPrintSwitch) { @@ -1510,16 +1570,16 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fTwoParticleCorrelationProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); mc.fTwoParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i]); mc.fTwoParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); - mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); + mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fTwoParticleCorrelationEbye[eAfter][i]); // Find max and min: if (ebye.fTwoParticleCorrelationEbye[eAfter][i] > ebye.fTwoParticleCorrelationMaxEbye[eAfter][i]) { ebye.fTwoParticleCorrelationMaxEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; - LOGF(info, "max v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); } if (ebye.fTwoParticleCorrelationEbye[eAfter][i] < ebye.fTwoParticleCorrelationMinEbye[eAfter][i]) { ebye.fTwoParticleCorrelationMinEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; - LOGF(info, "min v%d^2 so far = %e", i + 2, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); } } else { if (tc.fPrintSwitch) { @@ -1562,6 +1622,70 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam } } + // 3-p correlations: + for (int i = 0; i < NumThreePCorrBins; i++) { + int k = 3; + mcc.h1 = ThreePHarmonicIndex[i][0]; + mcc.h2 = -ThreePHarmonicIndex[i][1]; + mcc.h3 = -ThreePHarmonicIndex[i][2]; + std::array harmonicsThreeNum = {mcc.h1, mcc.h2, mcc.h3}; + std::array harmonicsThreeDen = {0, 0, 0}; + + // Before cut: + TComplex threeRecursionBefore = mccRecursion(3, harmonicsThreeNum, eBefore) / mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + double wThreeRecursionBefore = mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + ebye.fThreeParticleCorrelationEbye[eBefore][i] = threeRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wThreeRecursionBefore > 0.) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i]); + mc.fThreeParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] > ebye.fThreeParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] < ebye.fThreeParticleCorrelationMinEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMinEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wThreeRecursionBefore = %e", rlCollisionCent, nTracksBefore, wThreeRecursionBefore); + } + } + + // After cut: + TComplex threeRecursionAfter = mccRecursion(3, harmonicsThreeNum, eAfter) / mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + double wThreeRecursionAfter = mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + ebye.fThreeParticleCorrelationEbye[eAfter][i] = threeRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wThreeRecursionAfter > 0. && bNumTrackCut) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i]); + mc.fThreeParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] > ebye.fThreeParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] < ebye.fThreeParticleCorrelationMinEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMinEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wThreeRecursionAfter = %e", rlCollisionCent, nTracksAfter, wThreeRecursionAfter); + } + } + } + // 4-p correlations: for (int i = 0; i < NumFourPCorrBins; i++) { int k = 4; @@ -1582,6 +1706,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fFourParticleCorrelationProfiles[eBefore]->Fill(mc.fFourParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); mc.fFourParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i]); mc.fFourParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); + mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fFourParticleCorrelationEbye[eBefore][i]); // Find max and min: if (ebye.fFourParticleCorrelationEbye[eBefore][i] > ebye.fFourParticleCorrelationMaxEbye[eBefore][i]) { @@ -1608,6 +1733,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam mc.fFourParticleCorrelationProfiles[eAfter]->Fill(mc.fFourParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); mc.fFourParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i]); mc.fFourParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); + mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fFourParticleCorrelationEbye[eAfter][i]); // Find max and min: if (ebye.fFourParticleCorrelationEbye[eAfter][i] > ebye.fFourParticleCorrelationMaxEbye[eAfter][i]) { @@ -1854,7 +1980,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam // (NContribMult) set functions and add to list: if (histType == eCorrNumContribMult) { - int indexBins = static_cast(histType) + static_cast(eMultEstm_N); + int indexBins = histType + static_cast(eMultEstm_N); // (NContribMult) get bins: const auto& lMultXBins = lCrBins[indexBins]; @@ -2060,6 +2186,7 @@ struct MultiparticleCumulants { // this name is used in lower-case format to nam tc.fNumContribBins = cfNumContribBins; tc.fNumTrackBins = cfNumTrackBins; tc.fTwoParticleCorrBins = cfTwoParticleCorrBins; + tc.fThreeParticleCorrBins = cfThreeParticleCorrBins; tc.fFourParticleCorrBins = cfFourParticleCorrBins; tc.fSixParticleCorrBins = cfSixParticleCorrBins; tc.fNumTrackTotalBins = cfNumTrackTotalBins; From 7a5d7111991a562616b708107489b5e4c43a81aa Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Mon, 2 Nov 2026 11:42:46 +0530 Subject: [PATCH 523/648] [PWGLF] Corrected histogram name while filling from hEventSelectionMC to hEventCutMC (#18535) --- PWGLF/Tasks/Resonances/kstarqa.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 8d5a393b8e0..95ca4f38765 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -1991,32 +1991,32 @@ struct Kstarqa { } for (const auto& RecCollision : recCollisions) { - rEventSelection.fill(HIST("hEventSelectionMC"), 0.5); + rEventSelection.fill(HIST("hEventCutMC"), 0.5); if (!RecCollision.has_mcCollision()) continue; - rEventSelection.fill(HIST("hEventSelectionMC"), 1.5); + rEventSelection.fill(HIST("hEventCutMC"), 1.5); if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) continue; - rEventSelection.fill(HIST("hEventSelectionMC"), 2.5); + rEventSelection.fill(HIST("hEventCutMC"), 2.5); if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) continue; - rEventSelection.fill(HIST("hEventSelectionMC"), 3.5); + rEventSelection.fill(HIST("hEventCutMC"), 3.5); if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) continue; - rEventSelection.fill(HIST("hEventSelectionMC"), 4.5); + rEventSelection.fill(HIST("hEventCutMC"), 4.5); if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) continue; - rEventSelection.fill(HIST("hEventSelectionMC"), 5.5); + rEventSelection.fill(HIST("hEventCutMC"), 5.5); } bool isINELgt0true = false; From 14363b42ee02e4bf2c2e288d11ffc69fffc79e3d Mon Sep 17 00:00:00 2001 From: Jiabin Geng <997406694@qq.com> Date: Mon, 2 Nov 2026 16:34:05 +0800 Subject: [PATCH 524/648] [PWGLF] Fix bug in mixing event in hStrangeCorrelation.cxx (#18546) --- PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index d7bc6ed2a1c..1e67da5fac5 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -3204,6 +3204,13 @@ struct HStrangeCorrelation { validCollisions[i].reserve(masterConfigurations.mixingParameter); } } + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * + histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); + for (size_t i = 0; i < genLevelCollisions.size(); ++i) { + genLevelCollisions[i].reserve(masterConfigurations.mixingParameter); + } + } if (!masterConfigurations.doPPAnalysis) { // event selections in Pb-Pb histos.add("hEventSelection", "hEventSelection", kTH1F, {{11, 0, 11}}); @@ -3494,7 +3501,7 @@ struct HStrangeCorrelation { if (TESTBIT(doCorrelation, i)) { histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); if (masterConfigurations.mixingEventForClosureTest) { - histos.addClone("ClosureTest/sameEvent/", "ClosureTest/mixedEvent/"); + histos.add(fmt::format("ClosureTest/mixedEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); } if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}", Particlenames[i]).c_str(), "truth pairs whose trigger has at least one reconstructed track with a matching MC label", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); @@ -7026,7 +7033,8 @@ struct HStrangeCorrelation { TriggerInGen.pt = gpt; TriggerInGen.eta = geta; TriggerInGen.phi = gphi; - genLevelCollisions[binnumb].back().emplace_back(TriggerInGen); + if (gpt <= axisRanges[3][1] && gpt >= axisRanges[3][0]) + genLevelCollisions[binnumb].back().emplace_back(TriggerInGen); } if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); @@ -7155,7 +7163,7 @@ struct HStrangeCorrelation { if (!masterConfigurations.mixingEventForClosureTest) { return; } - for (std::size_t iCollision = 0; iCollision < genLevelCollisions[binnumb].size() - 1; iCollision++) { + for (std::size_t iCollision = 0; iCollision < genLevelCollisions[binnumb].size(); iCollision++) { for (std::size_t iTrigger = 0; iTrigger < genLevelCollisions[binnumb][iCollision].size(); iTrigger++) { auto triggerParticle = genLevelCollisions[binnumb][iCollision][iTrigger]; double getatrigger = triggerParticle.eta; From bafcff8df8b8392e94a216df46ec18eabb2170da Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Mon, 2 Nov 2026 10:15:16 +0100 Subject: [PATCH 525/648] [PWGDQ] handle track-collision reassociation in alignment QA task (#18536) --- PWGDQ/Tasks/muonGlobalAlignment.cxx | 506 ++++++++++++++++++---------- 1 file changed, 336 insertions(+), 170 deletions(-) diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index c045c1c05ff..c5a9a4656e5 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -19,7 +19,9 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/fwdtrackUtilities.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FwdTrackReAlignTables.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -103,7 +105,7 @@ using namespace o2::aod::rctsel; using MyCollisions = aod::Collisions; using MyBCs = soa::Join; using MyEvents = soa::Join; -using MyMuonsWithCov = soa::Join; +using MyMuonsWithCov = soa::Join; using MyMFTs = aod::MFTTracks; using MyMFTCovariances = aod::MFTTracksCov; @@ -122,7 +124,9 @@ using o2::track::TrackParCovFwd; namespace muonaligncoll { DECLARE_SOA_COLUMN(RunNumber, runNumber, int); -DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) +DECLARE_SOA_COLUMN(CollisionTime, collisionTime, float); //! Collision time in ns relative to BC stored in bc() +DECLARE_SOA_COLUMN(CollisionTimeRes, collisionTimeRes, float); //! Resolution of collision time } // namespace muonaligncoll namespace o2::aod @@ -131,7 +135,8 @@ namespace o2::aod DECLARE_SOA_TABLE(MACollisions, "AOD", "MACOLL", //! Time and vertex information of collision o2::soa::Index<>, muonaligncoll::RunNumber, muonaligncoll::Timestamp, collision::PosX, collision::PosY, collision::PosZ, - collision::CovXX, collision::CovYY, collision::CovZZ); + collision::CovXX, collision::CovYY, collision::CovZZ, + collision::CollisionTime, collision::CollisionTimeRes); using MACollision = MACollisions::iterator; } // namespace o2::aod @@ -237,6 +242,24 @@ DECLARE_SOA_TABLE(MAFwdTrkCls, "AOD", "MAFWDTRKCL", //! Forward Track Cluster in using MAFwdTrkCl = MAFwdTrkCls::iterator; } // namespace o2::aod +namespace o2::aod +{ + +namespace ma_track_association +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Collision index +DECLARE_SOA_ARRAY_INDEX_COLUMN(Collision, compatibleColl); //! Array of collision indices +} // namespace ma_track_association + +DECLARE_SOA_TABLE(MAFwdTrackAssoc, "AOD", "MAFWDTRACKASSOC", //! Table for fwdtrack-to-collision association + ma_track_association::CollisionId, + muonaligncl::FwdTrackId); + +DECLARE_SOA_TABLE(MAFwdTrkCompColls, "AOD", "MAFWDTRKCOMPCOL", //! Table with vectors of collision indices stored per fwdtrack + ma_track_association::CollisionIds, o2::soa::Marker<1>); + +} // namespace o2::aod + namespace o2::aod { // Compact collision + MFT tracks table for DCA analysis @@ -268,6 +291,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Produces collTable; Produces fwdTable; Produces fwdCovTable; + Produces fwdTrackAssoc; + Produces fwdTrkCompColls; Produces mftTable; Produces clusTable; Produces compactMftTable; @@ -276,9 +301,18 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgProduceMFTTable{"cfgProduceMFTTable", false, "flag to produce MFTs table"}; #endif + //// Variables for selecting events + struct : ConfigurableGroup { + Configurable fEvSelRequireGoodRCT{"cfgEvSelRequireGoodRCT", false, "Require good detector flags in Run Condition Table"}; + Configurable fEvSelRequireFrameBordersExclusion{"cfgEvSelRequireFrameBordersExclusion", false, "Exclude events in the TF and ITS ROF borders"}; + Configurable fEvSelRequireNoCollInTimeRange{"cfgEvSelRequireNoCollInTimeRange", false, "Exclude events with multiple collisions in time range (standard)"}; + Configurable fEvSelRequireNoSameBunch{"cfgEvSelRequireNoSameBunch", false, "Exclude events with multiple collisions in the same bunch crossing"}; + Configurable fEvSelVtxZMin{"cfgEvSelVtxZMin", -10.0f, "Minimum accepted value for vertex z position"}; + Configurable fEvSelVtxZMax{"cfgEvSelVtxZMax", 10.0f, "Maximum accepted value for vertex z position"}; + } configEvSel; + //// Variables for selecting MCH and MFT tracks Configurable cfgTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; - Configurable cfgPtMchLow{"cfgPtMchLow", 0.7f, ""}; Configurable cfgEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; Configurable cfgEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; Configurable cfgEtaMftLow{"cfgEtaMftLow", -3.6f, ""}; @@ -297,6 +331,10 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgMftMchResidualsPLow{"cfgMftMchResidualsPLow", 30.f, ""}; Configurable cfgMftMchResidualsPtLow{"cfgMftMchResidualsPtLow", 4.f, ""}; + Configurable cfgDimuonPLow{"cfgDimuonPLow", 0.f, ""}; + Configurable cfgDimuonPtLow{"cfgDimuonPtLow", 0.7f, ""}; + Configurable cfgDimuonMassLow{"cfgDimuonMassLow", 1.8f, ""}; + Configurable cfgMftTracksMultiplicityMax{"cfgMftTracksMultiplicityMax", 0, "Maximum number of MFT tracks to be processed per event (zero means no limit)"}; // Magnetic field position bias @@ -344,8 +382,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - // Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedFix10Fix15ShiftCh1BNew2", "Path of the geometry file"}; - Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedLoczzm4pLHC24anap1sR5a", "Path of the geometry file"}; + Configurable geoPathRealign{"geoPathRealign", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable cfgCcdbNoLaterThanRef{"cfgCcdbNoLaterThanRef", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of reference basis"}; Configurable cfgCcdbNoLaterThanNew{"cfgCcdbNoLaterThanNew", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of new basis"}; } configCCDB; @@ -363,8 +400,6 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc Configurable cfgHistDimuonTauzAxis{"cfgHistDimuonTauzAxis", "", "Di-muon tauz axis configuration"}; } configHistograms; - Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "Require good detector flags in Run Condition Table"}; - Configurable cfgEnableVertexShiftAnalysis{"cfgEnableVertexShiftAnalysis", false, "Enable the analysis of vertex shift"}; Configurable cfgEnableMftDcaAnalysis{"cfgEnableMftDcaAnalysis", false, "Enable the analysis of DCA-based MFT alignment"}; Configurable cfgEnableMftDcaExtraPlots{"cfgEnableMftDcaExtraPlots", false, "Enable additional plots for the analysis of DCA-based MFT alignment"}; @@ -446,8 +481,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc using GlobalMuonPair = std::pair, std::vector>; geo::TransformationCreator transformation; - std::map transformRef; // reference geometry w.r.t track data - std::map transformNew; // new geometry + std::unordered_map transformRef; // reference geometry w.r.t track data + std::unordered_map transformNew; // new geometry TGeoManager* geoNew = nullptr; TGeoManager* geoRef = nullptr; TrackFitter trackFitter; // Track fitter from MCH tracking library @@ -460,7 +495,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc double y{0}; double z{0}; }; - std::map mMchAlignmentCorrections; + std::unordered_map mMchAlignmentCorrections; #if (PROCESS_DERIVED_TABLES == 0) Preslice perMuon = aod::fwdtrkcl::fwdtrackId; @@ -479,7 +514,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // vector of all MFT-MCH(-MID) matching candidates associated to the same MCH(-MID) track, // to be sorted in descending order with respect to the matching quality // the map key is the MCH(-MID) track global index - using MatchingCandidates = std::map>; + using MatchingCandidates = std::unordered_map>; struct CollisionInfo { int runNumber{0}; @@ -493,9 +528,16 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // vector of MCH(-MID) track indexes std::vector mchTracks; // matching candidates - std::map> globalMuonTracks; + std::unordered_map> globalMuonTracks; + // number of ambiguous collisions associated to each muon track + std::unordered_map muonTracksCollMult; }; + // global vector of MCH(-MID) track indexes + std::vector mMchTracks; + // global vector of matching candidates + std::unordered_map> mGlobalMuonTracks; + std::tuple getAxisConfiguration(std::string cfgStr, int nBinsDef, double xMinDef, double xMaxDef) { // replace commas and semi-colons by spaces @@ -1940,76 +1982,114 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc { mMchTrackPars.clear(); mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); // fill collision information for global muon tracks (MFT-MCH-MID matches) for (const auto& muonTrack : muonTracks) { - if (!muonTrack.has_collision()) { - continue; - } + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { - auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - const auto& bc = bcs.rawIteratorAt(collision.bcId()); + auto collision = collisions.rawIteratorAt(collisionId); + const auto& bc = bcs.rawIteratorAt(collision.bcId()); - // remove TF/ROF borders and ambiguous collisions - if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || - !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - continue; - } + if (configEvSel.fEvSelRequireFrameBordersExclusion) { + // remove TF/ROF borders and ambiguous collisions + if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || + !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + } - if (cfgRequireGoodRCT && !rctChecker(collision)) { - continue; - } + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoCollInTimeRange && + !bc.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } - uint64_t collisionIndex = collision.globalIndex(); + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoSameBunch && + !bc.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.runNumber = bc.runNumber(); - collisionInfo.timestamp = bc.timestamp(); - collisionInfo.zVertex = collision.posZ(); + // remove events with bad RCT flags + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { + continue; + } + + if (collision.posZ() < configEvSel.fEvSelVtxZMin || + collision.posZ() > configEvSel.fEvSelVtxZMax) { + continue; + } - if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { - // standalone MCH or MCH-MID tracks - uint64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); + nCompatColl += 1; - // initialize the original MCH track parameters - mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + uint64_t collisionIndex = collision.globalIndex(); - // refit MCH track if requested - if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { - TrackRealigned convertedTrack; - bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = bc.runNumber(); + collisionInfo.timestamp = bc.timestamp(); + collisionInfo.zVertex = collision.posZ(); - // Get the re-aligned track parameters: track param at the first cluster - mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); - auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); - if (mchTrackParIt.second) { - // the insertion succeeded - mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); - if (!convertedTrackOk) { - mchTrackParIt.first->second.setRemovable(); + mMchTracks.push_back(mchTrackIndex); + + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); } } else { - // initialize the new MCH track parameters with the original ones, without refitting - mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } } - } else { - // global muon tracks (MFT-MCH or MFT-MCH-MID) - uint64_t muonTrackIndex = muonTrack.globalIndex(); - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); - uint64_t mchTrackIndex = mchTrack.globalIndex(); - - // check if a vector of global muon candidates is already available for the current MCH index - // if not, initialize a new one and add the current global muon track - // bool globalMuonTrackFound = false; - auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); - if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { - matchingCandidateIterator->second.push_back(muonTrackIndex); - // globalMuonTrackFound = true; - } else { - collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; } } } @@ -2048,7 +2128,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - if (cfgRequireGoodRCT && !rctChecker(collision)) { + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { continue; } @@ -2083,68 +2163,84 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc TMFT const& mftTracks, std::unordered_map& collisionInfos) { - // InitCollisions(collisions, bcs, muonTracks, clusters, collisionInfos); - mMchTrackPars.clear(); mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); // fill collision information for global muon tracks (MFT-MCH-MID matches) for (const auto& muonTrack : muonTracks) { - if (!muonTrack.has_collision()) { - continue; - } + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { - auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - uint64_t collisionIndex = collision.globalIndex(); + auto collision = collisions.rawIteratorAt(collisionId); + uint64_t collisionIndex = collision.globalIndex(); - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.runNumber = collision.runNumber(); - collisionInfo.timestamp = collision.timestamp(); - collisionInfo.zVertex = collision.posZ(); + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = collision.runNumber(); + collisionInfo.timestamp = collision.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + nCompatColl += 1; + + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); - if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { - // standalone MCH or MCH-MID tracks - uint64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); + mMchTracks.push_back(mchTrackIndex); - // initialize the original MCH track parameters - mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); - // refit MCH track if requested - if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { - TrackRealigned convertedTrack; - bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); - // Get the re-aligned track parameters: track param at the first cluster - mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); - auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); - if (mchTrackParIt.second) { - // the insertion succeeded - mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); - if (!convertedTrackOk) { - mchTrackParIt.first->second.setRemovable(); + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); } } else { - // initialize the new MCH track parameters with the original ones, without refitting - mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } } - } else { - // global muon tracks (MFT-MCH or MFT-MCH-MID) - uint64_t muonTrackIndex = muonTrack.globalIndex(); - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); - uint64_t mchTrackIndex = mchTrack.globalIndex(); - - // check if a vector of global muon candidates is already available for the current MCH index - // if not, initialize a new one and add the current global muon track - // bool globalMuonTrackFound = false; - auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); - if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { - matchingCandidateIterator->second.push_back(muonTrackIndex); - // globalMuonTrackFound = true; - } else { - collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; } } } @@ -2205,13 +2301,21 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc COLL const& collisions, TMUON const& muonTracks, TCLS const& clusters, - TMFT const& /*mftTracks*/) + TMFT const& /*mftTracks*/, + aod::FwdTrackAssoc const& fwdTrackAssocs) { - int32_t collId = 0; - int32_t fwdTrkId = 0; - int32_t mftTrkId = 0; + int32_t remappedCollisionIndex = 0; + int32_t remappedFwdTrkIndex = 0; + int32_t remappedMftTrkIndex = 0; + + // remapping tables for collisions and tracks IDs + std::unordered_map collisionIdRemapping; + std::unordered_map muonTrackIdRemapping; + std::vector muonTrackIdInverseMapping; + + // define vector of vectors to store indices of compatible collisions per track + std::vector>> collsPerTrack; - std::unordered_map mchTrackIdRemapping; for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { auto const& c = collisions.rawIteratorAt(collisionIndex); @@ -2221,36 +2325,76 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc collTable(collisionInfo.runNumber, collisionInfo.timestamp, c.posX(), c.posY(), c.posZ(), - c.covXX(), c.covYY(), c.covZZ()); + c.covXX(), c.covYY(), c.covZZ(), + c.collisionTime(), c.collisionTimeRes()); + collisionIdRemapping[collisionIndex] = remappedCollisionIndex; - bool hasOppositeSignMuonPair = false; + bool hasGoodOppositeSignMuonPair = false; std::vector muonPairs; getMuonPairs(collisionInfo, muonPairs); for (auto [mchIndex1, mchIndex2] : muonPairs) { - int sign1 = muonTracks.rawIteratorAt(mchIndex1).sign(); - int sign2 = muonTracks.rawIteratorAt(mchIndex2).sign(); + const auto& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); + const auto& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); + int sign1 = muonTrack1.sign(); + int sign2 = muonTrack2.sign(); - // only consider opposite-sign pairs - if ((sign1 * sign2) < 0) { - hasOppositeSignMuonPair = true; - break; + // skip same-sign muon pairs + if ((sign1 * sign2) >= 0) { + continue; + } + + bool isGoodMuon1 = IsGoodMuon(muonTrack1, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); + if (!goodMuonTracks) { + continue; + } + + // get the pre-stored MCH track parameters + const auto mchTrackParIt1 = mMchTrackPars.find(mchIndex1); + if (mchTrackParIt1 == mMchTrackPars.end()) { + continue; + } + const auto mchTrackParIt2 = mMchTrackPars.find(mchIndex2); + if (mchTrackParIt2 == mMchTrackPars.end()) { + continue; + } + + // compute the track parameters at the vertex + const auto& mchTrackPar1 = mchTrackParIt1->second; + auto mchTrackPar1AtVertex = PropagateMCHToVertex(mchTrackPar1, c); + const auto& mchTrackPar2 = mchTrackParIt2->second; + auto mchTrackPar2AtVertex = PropagateMCHToVertex(mchTrackPar2, c); + + // skip opposite-sign muon pairs with invariant mass below the threshold + double mass = getMuMuInvariantMass(mchTrackPar1AtVertex, mchTrackPar2AtVertex); + if (mass < cfgDimuonMassLow) { + continue; } + + hasGoodOppositeSignMuonPair = true; + break; } // loop over MCH tracks for (const auto& mchIndex : collisionInfo.mchTracks) { auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(mchIndex)) { + continue; + } + // store all MCH tracks when the event contains a muon pair of opposite sign - if (!hasOppositeSignMuonPair) { + if (!hasGoodOppositeSignMuonPair) { // only store good MCH tracks - if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { + if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { continue; } } - fwdTable(collId, + fwdTable(remappedCollisionIndex, mchTrack.trackType(), mchTrack.x(), mchTrack.y(), mchTrack.z(), mchTrack.phi(), mchTrack.tgl(), mchTrack.signed1Pt(), @@ -2267,11 +2411,13 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc // loop over attached clusters auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id for (auto const& cluster : clustersSliced) { - clusTable(fwdTrkId, cluster.x(), cluster.y(), cluster.z(), cluster.clInfo()); + clusTable(remappedFwdTrkIndex, cluster.x(), cluster.y(), cluster.z(), cluster.clInfo()); } - mchTrackIdRemapping[mchTrack.globalIndex()] = fwdTrkId; - fwdTrkId += 1; + muonTrackIdRemapping[mchTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(mchTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; } // loop over global muon tracks @@ -2280,21 +2426,26 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc const auto& mchTrack = muonTrack.template matchMCHTrack_as(); const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(muonTrack.globalIndex())) { + continue; + } + try { - auto remappedMchId = mchTrackIdRemapping.at(mchTrack.globalIndex()); + auto remappedMchId = muonTrackIdRemapping.at(mchTrack.globalIndex()); - mftTable(collId, + mftTable(remappedCollisionIndex, mftTrack.x(), mftTrack.y(), mftTrack.z(), mftTrack.phi(), mftTrack.tgl(), mftTrack.signed1Pt(), mftTrack.mftClusterSizesAndTrackFlags(), mftTrack.chi2()); - fwdTable(collId, + fwdTable(remappedCollisionIndex, muonTrack.trackType(), muonTrack.x(), muonTrack.y(), muonTrack.z(), muonTrack.phi(), muonTrack.tgl(), muonTrack.signed1Pt(), muonTrack.nClusters(), muonTrack.pDca(), muonTrack.rAtAbsorberEnd(), muonTrack.chi2(), muonTrack.chi2MatchMCHMFT(), - mftTrkId, remappedMchId); + remappedMftTrkIndex, remappedMchId); if (cfgProduceMuonAlignmentTablesWithCovariances.value) { fwdCovTable(muonTrack.sigmaX(), muonTrack.sigmaY(), muonTrack.sigmaPhi(), muonTrack.sigmaTgl(), muonTrack.sigma1Pt(), @@ -2302,14 +2453,50 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc muonTrack.rhoTglPhi(), muonTrack.rho1PtX(), muonTrack.rho1PtY(), muonTrack.rho1PtPhi(), muonTrack.rho1PtTgl()); } - fwdTrkId += 1; - mftTrkId += 1; + muonTrackIdRemapping[muonTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(muonTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; + remappedMftTrkIndex += 1; } catch (const std::exception& e) { continue; } } - collId += 1; + remappedCollisionIndex += 1; + } + + // store track-to-collision associations + for (const auto& assoc : fwdTrackAssocs) { + const auto collId = assoc.collisionId(); + const auto trackId = assoc.fwdtrackId(); + + try { + auto remappedCollId = collisionIdRemapping.at(collId); + auto remappedMchId = muonTrackIdRemapping.at(trackId); + fwdTrackAssoc(remappedCollId, remappedMchId); + } catch (const std::exception& e) { + continue; + } + } + + // store reverse track-to-collision associations + for (uint32_t remappedTrackId = 0; remappedTrackId < collsPerTrack.size(); remappedTrackId++) { + // step 1: fill vectors of remapped collision IDs for each of the tracks that is store in the derived tables + auto& remappedCollsVec = collsPerTrack[remappedTrackId]; + const auto trackId = muonTrackIdInverseMapping[remappedTrackId]; + const auto& muonTrack = muonTracks.rawIteratorAt(trackId); + for (const auto& collId : muonTrack.compatibleCollIds()) { + try { + auto remappedCollId = collisionIdRemapping.at(collId); + remappedCollsVec->push_back(remappedCollId); + } catch (const std::exception& e) { + continue; + } + } + + // step 2: output derived table + fwdTrkCompColls(*(remappedCollsVec.get())); } } #endif @@ -2486,7 +2673,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); if (!isGoodMuon) { continue; } @@ -2845,8 +3032,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto const& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); auto const& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); - bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); - bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); if (!goodMuonTracks) { @@ -2910,8 +3097,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto const& fwdTrack1 = muonTracks.rawIteratorAt(fwdIndex1); auto const& fwdTrack2 = muonTracks.rawIteratorAt(fwdIndex2); - bool isGoodGlobalMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); - bool isGoodGlobalMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, 0.f, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodGlobalMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodGlobalMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); bool goodGlobalMuonTracks = (isGoodGlobalMuon1 && isGoodGlobalMuon2); bool isGoodMatch1 = isGoodGlobalMatching(fwdTrack1, cfgDimuonMatchChi2Up.value); @@ -2998,7 +3185,9 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc MyBCs const& bcs, MyMuonsWithCov const& muonTracks, MyMFTs const& mftTracks, - aod::FwdTrkCls const& clusters) + aod::FwdTrkCls const& clusters, + aod::FwdTrackAssoc const& fwdTrackAssocs, + aod::FwdTrkCompColls const& /*fwdTrackCompColls*/) { auto bc = bcs.begin(); if (mRunNumber != bc.runNumber()) { @@ -3012,7 +3201,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc InitCollisions(collisions, bcs, muonTracks, clusters, mftTracks, collisionInfos); if (cfgProduceMuonAlignmentTables) { - StoreCollisions(collisionInfos, collisions, muonTracks, clusters, mftTracks); + StoreCollisions(collisionInfos, collisions, muonTracks, clusters, mftTracks, fwdTrackAssocs); } FillMftPlots(collisions, muonTracks, mftTracks, collisionInfos); @@ -3027,7 +3216,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc #else void processDerivedTables(o2::aod::MACollisions const& collisions, - soa::Join const& muonTracks, + soa::Join const& muonTracks, o2::aod::MAFwdTrkCls const& clusters, o2::aod::MAMFTTracks const& mftTracks) { @@ -3063,29 +3252,6 @@ struct MuonGlobalAlignmentSpawner { Spawns realignFwdTrksCov; Spawns realignMftTrks; void init(InitContext const&) {} - - /*auto process(o2::aod::StoredMAFwdTracks const& storedFwd, - o2::aod::StoredMAFwdTracksCov const& storedFwdCov, - o2::aod::StoredMAMFTTracks const& storedMft) - { - // Evaluate the dynamic kinematic expressions on the fly - auto fwdExtended = o2::soa::Extend(storedFwd); - - auto fwdExtended = o2::soa::Extend(storedFwd); - - auto mftExtended = o2::soa::Extend(storedMft); - - return std::make_tuple(fwdExtended, mftExtended); - }*/ }; #endif From 984c88cb215b35f0fbe5d620ddfc48663ade326d Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Mon, 2 Nov 2026 10:16:13 +0100 Subject: [PATCH 526/648] [DPG] treeCreatorPidTpcDiagnostics: add runnumber, factor out particle-wise configurables (#18516) --- DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx | 6 ++++-- DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx index 66b1d831d3c..8822995d239 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx @@ -91,6 +91,7 @@ struct TreeCreatorPidTpcDiagnostics { Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; struct ParticleWiseCfgs : ConfigurableGroup { + std::string prefix = "particleWiseCfgs"; // JSON group name #define DECLARE_PARTICLE_WISE_CONFIGURABLES(Unused1, ParticleNameLong, Unused2) \ Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ @@ -223,7 +224,8 @@ struct TreeCreatorPidTpcDiagnostics { isFirstCollision = false; const float ft0Occ = collision.ft0cOccupancyInTimeRange(); const float multTPC = collision.multTPC() / MultiplicityNorm; - const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.; + const int runnumber = bc.runNumber(); + const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo : 0.; const auto tracksFromCollision = tracks.sliceBy(perCollisionTracks, static_cast(collision.globalIndex())); @@ -279,7 +281,7 @@ struct TreeCreatorPidTpcDiagnostics { nSigmaTof = o2::aod::pidutils::tofNSigma(track); } - rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof); + rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, runnumber, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof); } // tracksFromCollision } // collisions ++mProcessedParticles; diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h index e75aa5f7e71..235a802aa54 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h @@ -41,6 +41,7 @@ DECLARE_SOA_TABLE(PidTpcDiagnostics, "AOD", "PIDTPCDIAG", tpcskims::NormMultTPC, tpcskims::NormNClustersTPC, tpcskims::NormNClustersTPCPID, + tpcskims::RunNumber, o2::aod::track::Phi, o2::aod::track::Tgl, o2::aod::track::TPCInnerParam, From 86139cb2b463ed58455e9bdfb3d208dd59a74775 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Mon, 2 Nov 2026 11:09:30 +0100 Subject: [PATCH 527/648] [PWGHF] utilsEvSelHf: add nPvContributorsMax configurable (#18514) --- PWGHF/Utils/utilsEvSelHf.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PWGHF/Utils/utilsEvSelHf.h b/PWGHF/Utils/utilsEvSelHf.h index 7ce2b179dad..7eb9d311187 100644 --- a/PWGHF/Utils/utilsEvSelHf.h +++ b/PWGHF/Utils/utilsEvSelHf.h @@ -197,6 +197,7 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable occupancyMax{"occupancyMax", 1000000, "Maximum occupancy"}; o2::framework::Configurable requireINEL0{"requireINEL0", false, "Require INEL > 0"}; o2::framework::Configurable nPvContributorsMin{"nPvContributorsMin", 0, "Minimum number of PV contributors"}; + o2::framework::Configurable nPvContributorsMax{"nPvContributorsMax", 1000000, "Maximum number of PV contributors"}; o2::framework::Configurable chi2PvMax{"chi2PvMax", -1.f, "Maximum PV chi2"}; o2::framework::Configurable zPvPosMin{"zPvPosMin", -10.f, "Minimum PV posZ (cm)"}; o2::framework::Configurable zPvPosMax{"zPvPosMax", 10.f, "Maximum PV posZ (cm)"}; @@ -410,7 +411,7 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { } /// number of PV contributors - if (collision.numContrib() < nPvContributorsMin) { + if (collision.numContrib() < nPvContributorsMin || collision.numContrib() > nPvContributorsMax) { SETBIT(rejectionMask, EventRejection::NContrib); } From b533857c393e39ec85a3cebb7d0c2e5bd7b71e18 Mon Sep 17 00:00:00 2001 From: mintu1999 Date: Mon, 2 Nov 2026 18:09:07 +0530 Subject: [PATCH 528/648] [PWGCF] Add MFT-MFT correlation task (#18485) Co-authored-by: ALICE Action Bot --- PWGCF/Core/CorrelationContainer.cxx | 360 ++-- .../Tasks/CMakeLists.txt | 5 + .../Tasks/mftcorrelations.cxx | 1449 +++++++++++++++++ 3 files changed, 1636 insertions(+), 178 deletions(-) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx diff --git a/PWGCF/Core/CorrelationContainer.cxx b/PWGCF/Core/CorrelationContainer.cxx index 6c4a2e56d0d..e60bdfb219e 100644 --- a/PWGCF/Core/CorrelationContainer.cxx +++ b/PWGCF/Core/CorrelationContainer.cxx @@ -24,11 +24,12 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include #include #include #include @@ -36,6 +37,8 @@ #include #include +#include +#include #include #include #include @@ -46,7 +49,7 @@ using namespace o2::constants::math; ClassImp(CorrelationContainer); -const Int_t CorrelationContainer::fgkCFSteps = 11; +const int CorrelationContainer::fgkCFSteps = 11; CorrelationContainer::CorrelationContainer() : TNamed(), mPairHist(nullptr), @@ -64,9 +67,9 @@ CorrelationContainer::CorrelationContainer() : TNamed(), mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // Default constructor @@ -91,9 +94,9 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // correlationAxis has to provide a 6 length list of AxisSpec which contain: @@ -125,7 +128,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl triggerAxis.insert(triggerAxis.end(), userAxis.begin(), userAxis.end()); mTriggerHist = HistFactory::createHist({"mTriggerHist", "d^{2}N_{ch}/d#varphid#eta", {HistType::kStepTHnF, triggerAxis, fgkCFSteps}}).release(); - mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, {efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}, fgkCFSteps}}).release(); + mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, {efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2], efficiencyAxis[3]}, fgkCFSteps}}).release(); mEventCount = HistFactory::createHist({"mEventCount", ";step;centrality;count", {HistType::kTH2F, {{fgkCFSteps + 2, -2.5, -0.5 + fgkCFSteps, "step"}, correlationAxis[3]}}}).release(); } @@ -146,16 +149,16 @@ CorrelationContainer::CorrelationContainer(const CorrelationContainer& c) : TNam mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // // CorrelationContainer copy constructor // - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } //____________________________________________________________________ @@ -195,7 +198,7 @@ CorrelationContainer& CorrelationContainer::operator=(const CorrelationContainer // assigment operator if (this != &c) { - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } return *this; @@ -206,7 +209,7 @@ void CorrelationContainer::Copy(TObject& c) const { // copy function - CorrelationContainer& target = (CorrelationContainer&)c; + auto& target = dynamic_cast(c); if (mPairHist) { target.mPairHist = dynamic_cast(mPairHist->Clone()); @@ -253,14 +256,14 @@ Long64_t CorrelationContainer::Merge(TCollection* list) TObject* obj = nullptr; // collections of objects - const UInt_t kMaxLists = 4; + const unsigned int kMaxLists = 4; TList** lists = new TList*[kMaxLists]; - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { lists[i] = new TList; } - Int_t count = 0; + int count = 0; while ((obj = iter->Next())) { CorrelationContainer* entry = dynamic_cast(obj); @@ -286,7 +289,7 @@ Long64_t CorrelationContainer::Merge(TCollection* list) mTrackHistEfficiency->Merge(lists[2]); mEventCount->Merge(lists[3]); - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { delete lists[i]; } @@ -317,7 +320,7 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) max_dimension = grid->GetNdimensions(); } - for (Int_t i = 0; i < max_dimension; i++) { + for (int i = 0; i < max_dimension; i++) { if (grid->GetAxis(i)->TestBit(TAxis::kAxisRange)) { grid->GetAxis(i)->SetRange(0, 0); // reset range } @@ -325,14 +328,14 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) } //____________________________________________________________________ -void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // prints the number of empty bins in the track end event histograms in the given step - Int_t binBegin[4]; - Int_t binEnd[4]; + int binBegin[4]; + int binEnd[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { binBegin[i] = 1; binEnd[i] = mPairHist->getTHn(step)->GetAxis(i)->GetNbins(); } @@ -355,11 +358,11 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo // start from multiplicity 1 binBegin[3] = mPairHist->getTHn(step)->GetAxis(3)->FindBin(1); - Int_t total = 0; - Int_t count = 0; - Int_t vars[4]; + int total = 0; + int count = 0; + int vars[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { vars[i] = binBegin[i]; } @@ -375,7 +378,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } vars[3]++; - for (Int_t i = 3; i > 0; i--) { + for (int i = 3; i > 0; i--) { if (vars[i] == binEnd[i] + 1) { vars[i] = binBegin[i]; vars[i - 1]++; @@ -392,7 +395,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } //____________________________________________________________________ -void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, THnBase** trackHist, TH2** eventHist) +void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, THnBase** trackHist, TH2** eventHist) { // Calculates a 4d histogram with deltaphi, deltaeta, zvtx, multiplicity on track level and // a 2d histogram on event level (as fct of zvtx, multiplicity) @@ -415,15 +418,15 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F sparse->GetAxis(1)->SetRangeUser(mPtMin, mPtMax); } - Int_t firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); - Int_t lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); + int firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); + int lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); LOGF(info, "Using trigger pT range %d --> %d", firstBin, lastBin); sparse->GetAxis(2)->SetRange(firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); - Int_t dimensions[] = {4, 0, 5, 3}; + int dimensions[] = {4, 0, 5, 3}; THnBase* tmpTrackHist = sparse->ProjectionND(4, dimensions, "E"); - *eventHist = (TH2*)mTriggerHist->getTHn(step)->Projection(1, 2); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) + *eventHist = static_cast(mTriggerHist->getTHn(step)->Projection(1, 2)); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) // convert to THn *trackHist = changeToThn(tmpTrackHist); delete tmpTrackHist; @@ -432,7 +435,7 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F resetBinLimits(mTriggerHist->getTHn(step), 3); } -TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger) +TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger) { // Calculate per trigger yield without considering mixed event // Sums over all vertex bins, and respects the pT and centrality limits set in the class @@ -472,7 +475,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } TH2* yield = trackSameAll->Projection(1, 0, "E"); - Float_t triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); + float triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); if (normalizePerTrigger) { LOGF(info, "Dividing %f tracks by %f triggers", yield->Integral(), triggers); @@ -482,7 +485,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } // normalizate to dphi width - Float_t normalization = yield->GetXaxis()->GetBinWidth(1); + float normalization = yield->GetXaxis()->GetBinWidth(1); yield->Scale(1.0 / normalization); delete trackSameAll; @@ -491,7 +494,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, return yield; } -TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger, Int_t stepForMixed, Int_t* trigger) +TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger, int stepForMixed, int* trigger) { // Extract 2D per trigger yield with mixed event correction. The quantity is calculated for *each* vertex bin and multiplicity bin and then a sum of ratios is performed: // 1_N [ (same/mixed)_1 + (same/mixed)_2 + (same/mixed)_3 + ... ] @@ -507,8 +510,8 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normalizePerTrigger: divide through number of triggers // do not add this hists to the directory - Bool_t oldStatus = TH1::AddDirectoryStatus(); - TH1::AddDirectory(kFALSE); + bool oldStatus = TH1::AddDirectoryStatus(); + TH1::AddDirectory(false); TH2* totalTracks = nullptr; @@ -519,11 +522,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati TH2* eventMixedAll = nullptr; TH2* eventMixedAllStep6 = nullptr; - Long64_t totalEvents = 0; - Int_t nCorrelationFunctions = 0; + int64_t totalEvents = 0; + int nCorrelationFunctions = 0; getHistsZVtxMult(step, ptTriggerMin, ptTriggerMax, &trackSameAll, &eventSameAll); - mixed->getHistsZVtxMult((stepForMixed == -1) ? step : (CFStep)stepForMixed, ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); + mixed->getHistsZVtxMult((stepForMixed == -1) ? step : static_cast(stepForMixed), ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); // If we ask for histograms from step8 (TTR cut applied) there is a hole at 0,0; so this cannot be used for the // mixed-event normalization. If step6 is available, the normalization factor is read out from that one. @@ -549,15 +552,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati multBinEnd = multAxis->FindBin(mCentralityMax); } - for (Int_t multBin = TMath::Max(1, multBinBegin); multBin <= TMath::Min(multAxis->GetNbins(), multBinEnd); multBin++) { + for (int multBin = std::max(1, multBinBegin); multBin <= std::min(multAxis->GetNbins(), multBinEnd); multBin++) { trackSameAll->GetAxis(3)->SetRange(multBin, multBin); trackMixedAll->GetAxis(3)->SetRange(multBin, multBin); if (trackMixedAllStep6) { trackMixedAllStep6->GetAxis(3)->SetRange(multBin, multBin); } - Double_t mixedNorm = 1; - Double_t mixedNormError = 0; + double mixedNorm = 1; + double mixedNormError = 0; if (!mSkipScaleMixedEvent) { // get mixed normalization correction factor: is independent of vertex bin if scaled with number of triggers @@ -570,40 +573,40 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksMixed = trackMixedAll->Projection(1, 0, "E"); } // Printf("%f", tracksMixed->Integral()); - Float_t binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); + float binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); if (stepForMixed == -1 && step == kCFStepBiasStudy && !trackMixedAllStep6) { // get mixed event normalization by assuming full acceptance at deta at 0 (integrate over dphi), excluding (0, 0) - Float_t phiExclude = 0.41; + float phiExclude = 0.41; mixedNorm = tracksMixed->IntegralAndError(1, tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1, tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError); - Double_t mixedNormError2 = 0; - Double_t mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); + double mixedNormError2 = 0; + double mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); if (mixedNormError == 0 || mixedNormError2 == 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty %f %f %f %f", multBin, mixedNorm, mixedNormError, mixedNorm2, mixedNormError2); continue; } - Int_t nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; - Int_t nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm2 /= nBinsMixedNorm2; mixedNormError2 /= nBinsMixedNorm2; mixedNorm = mixedNorm / mixedNormError / mixedNormError + mixedNorm2 / mixedNormError2 / mixedNormError2; - mixedNormError = TMath::Sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); + mixedNormError = std::sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); mixedNorm *= mixedNormError * mixedNormError; } else { // get mixed event normalization at (0,0) // NOTE if variable bin size is used around (0,0) to reduce two-track effect to limited bins, the normalization gets a bit tricky here (finite bin correction and normalization are made only for fixed size bins). // The normalization factor has to determined in a bin as large as the normal bin size, as a proxy the bin with index (1, 1) is used - Float_t binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); + float binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); mixedNorm = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(-binWidthPhi + 1e-4), tracksMixed->GetXaxis()->FindBin(binWidthPhi - 1e-4), tracksMixed->GetYaxis()->FindBin(-binWidthEta + 1e-4), tracksMixed->GetYaxis()->FindBin(binWidthEta - 1e-4), mixedNormError); - Int_t nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) + int nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; @@ -615,7 +618,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // finite bin correction if (mTrackEtaCut > 0) { - Double_t finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; + double finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; LOGF(info, "Finite bin correction: %f", finiteBinCorrection); mixedNorm /= finiteBinCorrection; mixedNormError /= finiteBinCorrection; @@ -623,7 +626,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati LOGF(error, "ERROR: mTrackEtaCut not set. Finite bin correction cannot be applied. Continuing anyway..."); } - Float_t triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); + float triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); // Printf("%f +- %f | %f | %f", mixedNorm, mixedNormError, triggers, mixedNorm / triggers); if (triggers <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty", multBin); @@ -631,7 +634,6 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } mixedNorm /= triggers; - mixedNormError /= triggers; delete tracksMixed; } else { @@ -648,15 +650,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normParameters->Fill(mixedNorm); TAxis* vertexAxis = trackSameAll->GetAxis(2); - Int_t vertexBinBegin = 1; - Int_t vertexBinEnd = vertexAxis->GetNbins(); + int vertexBinBegin = 1; + int vertexBinEnd = vertexAxis->GetNbins(); if (mZVtxMax > mZVtxMin) { vertexBinBegin = vertexAxis->FindBin(mZVtxMin); vertexBinEnd = vertexAxis->FindBin(mZVtxMax); } - for (Int_t vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { + for (int vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { trackSameAll->GetAxis(2)->SetRange(vertexBin, vertexBin); trackMixedAll->GetAxis(2)->SetRange(vertexBin, vertexBin); @@ -667,13 +669,13 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // TH1* histMixedproj = mixedTwoD->ProjectionY(); // histMixedproj->Scale(1.0 / mixedTwoD->GetNbinsX()); // - // for (Int_t x=1; x<=mixedTwoD->GetNbinsX(); x++) - // for (Int_t y=1; y<=mixedTwoD->GetNbinsY(); y++) + // for (int x=1; x<=mixedTwoD->GetNbinsX(); x++) + // for (int y=1; y<=mixedTwoD->GetNbinsY(); y++) // mixedTwoD->SetBinContent(x, y, histMixedproj->GetBinContent(y)); // delete histMixedproj; - Float_t triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); + float triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); if (triggers2 <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d vertex bin %d because mixed event is empty", multBin, vertexBin); } else { @@ -688,14 +690,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // new TCanvas; tracksMixed->DrawClone("SURF1"); // some code to judge the relative contribution of the different correlation functions to the overall uncertainty - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - Int_t checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); - Int_t checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); + int checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); + int checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[0] += tracksSame->GetBinContent(x, y); errors[0] += tracksSame->GetBinError(x, y); sums[1] += tracksMixed->GetBinContent(x, y); @@ -705,14 +707,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksSame->Divide(tracksMixed); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[2] += tracksSame->GetBinContent(x, y); errors[2] += tracksSame->GetBinError(x, y); } } - for (Int_t x = 0; x < 3; x++) { + for (int x = 0; x < 3; x++) { if (sums[x] > 0) { errors[x] /= sums[x]; } @@ -728,7 +730,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati */ if (!totalTracks) { - totalTracks = (TH2*)tracksSame->Clone("totalTracks"); + totalTracks = dynamic_cast(tracksSame->Clone("totalTracks")); } else { totalTracks->Add(tracksSame); } @@ -746,11 +748,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (totalTracks) { - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - for (Int_t x = 1; x <= totalTracks->GetNbinsX(); x++) { - for (Int_t y = 1; y <= totalTracks->GetNbinsY(); y++) { + for (int x = 1; x <= totalTracks->GetNbinsX(); x++) { + for (int y = 1; y <= totalTracks->GetNbinsY(); y++) { sums[0] += totalTracks->GetBinContent(x, y); errors[0] += totalTracks->GetBinError(x, y); } @@ -760,17 +762,17 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (normalizePerTrigger) { - LOGF(info, "Dividing %f tracks by %lld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); + LOGF(info, "Dividing %f tracks by %ld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); if (totalEvents > 0) { totalTracks->Scale(1.0 / totalEvents); } } if (trigger != nullptr) { - *trigger = (Int_t)totalEvents; + *trigger = static_cast(totalEvents); } // normalizate to dphi width - Float_t normalization = totalTracks->GetXaxis()->GetBinWidth(1); + float normalization = totalTracks->GetXaxis()->GetBinWidth(1); totalTracks->Scale(1.0 / normalization); } @@ -788,14 +790,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati return totalTracks; } -TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // returns the distribution of triggers as function of centrality/multiplicity resetBinLimits(mTriggerHist->getTHn(step), 3); - Int_t firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); - Int_t lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); + int firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); + int lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); LOGF(info, "Using pT range %d --> %d", firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); @@ -827,7 +829,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici sourceContainer->getTHn(step2)->GetAxis(0)->SetRangeUser(mEtaMin, mEtaMax); } - Int_t dimensions[] = {0, 1, 3, 4}; + int dimensions[] = {0, 1, 3, 4}; THnBase* generated = sourceContainer->getTHn(step1)->ProjectionND(4, dimensions); THnBase* measured = sourceContainer->getTHn(step2)->ProjectionND(4, dimensions); @@ -836,7 +838,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici resetBinLimits(sourceContainer->getTHn(step1)); resetBinLimits(sourceContainer->getTHn(step2)); - THnBase* clone = (THnBase*)measured->Clone(); + auto* clone = dynamic_cast(measured->Clone()); clone->Divide(measured, generated, 1, 1, "B"); @@ -847,7 +849,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici } //____________________________________________________________________ -TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Int_t source, Int_t axis3) +TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, int source, int axis3) { // creates a track-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -908,8 +910,8 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } // check for bins with less than 50 entries, print warning - Int_t binBegin[3]; - Int_t binEnd[3]; + int binBegin[3]; + int binEnd[3]; binBegin[0] = 1; binBegin[1] = 1; @@ -936,7 +938,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt if (mPtMax > mPtMin) { // TODO this is just checking up to 15 for now - Float_t ptMax = TMath::Min((Float_t)15., mPtMax); + float ptMax = std::min(static_cast(15.), mPtMax); if (axis1 == 1) { binBegin[0] = generated->GetXaxis()->FindBin(mPtMin); binEnd[0] = generated->GetXaxis()->FindBin(ptMax); @@ -951,15 +953,15 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } } - Int_t total = 0; - Int_t count = 0; - Int_t vars[3]; + int total = 0; + int count = 0; + int vars[3]; - for (Int_t i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { vars[i] = binBegin[i]; } - const Int_t limit = 50; + const int limit = 50; while (1) { if (generated->GetDimension() == 1 && generated->GetBinContent(vars[0]) < limit) { LOGF(info, "Empty bin at %s=%.2f (%.2f entries)", generated->GetXaxis()->GetTitle(), generated->GetXaxis()->GetBinCenter(vars[0]), generated->GetBinContent(vars[0])); @@ -1007,23 +1009,25 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // 2d rebin with variable axis does not exist in root TH1* tmp = measured; - measured = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)measured)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - measured->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* measuredRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + measuredRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + measuredRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + measured = measuredRebinned; delete tmp; tmp = generated; - generated = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)generated)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - generated->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* generatedRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + generatedRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + generatedRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + generated = generatedRebinned; delete tmp; } else { TH1* tmp = measured; @@ -1047,7 +1051,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt LOGF(fatal, "Invalid binning --> %d %d", axis->GetNbins(), measured->GetNbinsX()); } - Double_t newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; + double newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; // reduce binning below 5 GeV/c TH1* tmp = measured; @@ -1057,7 +1061,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = measured; measured = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= measured->GetNbinsX(); bin++) { + for (int bin = 1; bin <= measured->GetNbinsX(); bin++) { measured->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(measured->GetBinCenter(bin)))); measured->SetBinError(bin, tmp->GetBinError(tmp->FindBin(measured->GetBinCenter(bin)))); } @@ -1071,7 +1075,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = generated; generated = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= generated->GetNbinsX(); bin++) { + for (int bin = 1; bin <= generated->GetNbinsX(); bin++) { generated->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(generated->GetBinCenter(bin)))); generated->SetBinError(bin, tmp->GetBinError(tmp->FindBin(generated->GetBinCenter(bin)))); } @@ -1090,7 +1094,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } //____________________________________________________________________ -TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, float ptTriggerMin, float ptTriggerMax) { // creates a event-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -1133,9 +1137,9 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) LOGF(fatal, "Inconsistent binning %d %d", hist1->GetNbinsZ(), hist2->GetNbinsX()); } - for (Int_t x = 1; x <= hist1->GetNbinsX(); x++) { - for (Int_t y = 1; y <= hist1->GetNbinsY(); y++) { - for (Int_t z = 1; z <= hist1->GetNbinsZ(); z++) { + for (int x = 1; x <= hist1->GetNbinsX(); x++) { + for (int y = 1; y <= hist1->GetNbinsY(); y++) { + for (int z = 1; z <= hist1->GetNbinsZ(); z++) { if (hist2->GetBinContent(z) > 0) { hist1->SetBinContent(x, y, z, hist1->GetBinContent(x, y, z) / hist2->GetBinContent(z)); hist1->SetBinError(x, y, z, hist1->GetBinError(x, y, z) / hist2->GetBinContent(z)); @@ -1149,7 +1153,7 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) } //____________________________________________________________________ -TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, Float_t leadPtMin, Float_t leadPtMax, Int_t weighting) +TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, float leadPtMin, float leadPtMax, int weighting) { // extracts the track-level bias (integrating out the multiplicity) between two steps (dividing step2 by step1) // done by weighting the track-level distribution with the number of events as function of leading pT @@ -1170,14 +1174,14 @@ TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, c resetBinLimits(mTriggerHist->getTHn(step2), 3); setBinLimits(tmp->getTHn(step2)); - TH1D* events1 = (TH1D*)mTriggerHist->getTHn(step1)->Projection(0); - TH3D* hist1 = (TH3D*)tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events1 = mTriggerHist->getTHn(step1)->Projection(0); + TH3D* hist1 = tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist1, events1); } - TH1D* events2 = (TH1D*)mTriggerHist->getTHn(step2)->Projection(0); - TH3D* hist2 = (TH3D*)tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events2 = mTriggerHist->getTHn(step2)->Projection(0); + TH3D* hist2 = tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist2, events2); } @@ -1253,7 +1257,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiency(int axis) { // extracts the tracking efficiency for primary particles // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1262,7 +1266,7 @@ TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) } //____________________________________________________________________ -TH1D* CorrelationContainer::getFakeRate(Int_t axis) +TH1D* CorrelationContainer::getFakeRate(int axis) { return dynamic_cast(getTrackEfficiency(EfficiencyStep::RecoAll, EfficiencyStep::Fake, axis)); } @@ -1278,7 +1282,7 @@ TH2D* CorrelationContainer::getTrackingCorrection() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingCorrection(int axis) { // extracts the correction for tracking efficiency and secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1309,7 +1313,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCorrectionCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(int axis) { // extracts the correction for tracking efficiency // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1340,7 +1344,7 @@ TH2D* CorrelationContainer::getTrackingContaminationCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingContamination(Int_t axis) +TH1D* CorrelationContainer::getTrackingContamination(int axis) { // extracts the tracking contamination by secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1386,7 +1390,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) // copies the entries of this object's members from the object to this object // fills using the fill function and thus allows that the objects have different binning - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); THnBase* source = from->mPairHist->getTHn(step); @@ -1395,7 +1399,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { LOGF(info, "Ev: Copying step %d", step); THnBase* target = mTriggerHist->getTHn(step); THnBase* source = from->mTriggerHist->getTHn(step); @@ -1404,7 +1408,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < TMath::Min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { + for (int step = 0; step < std::min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { if (!from->mTrackHistEfficiency->getTHn(step)) { continue; } @@ -1422,7 +1426,7 @@ void CorrelationContainer::symmetrizepTBins() { // copy pt,a < pt,t bins to pt,a > pt,t (inverting deltaphi and delta eta as it should be) including symmetric bins - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); if (target->GetEntries() == 0) { @@ -1430,47 +1434,47 @@ void CorrelationContainer::symmetrizepTBins() } // for symmetric bins - THnBase* source = (THnBase*)target->Clone(); + auto* source = dynamic_cast(target->Clone()); // axes: 0 delta eta; 1 pT,a; 2 pT,t; 3 centrality; 4 delta phi; 5 vtx-z - for (Int_t i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { - for (Int_t i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { - for (Int_t i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { - for (Int_t i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { + for (int i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { + for (int i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { + for (int i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { + for (int i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { // find source bin - Int_t binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); - Int_t binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); + int binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); + int binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); LOGF(info, "(%d %d) Copying from %d %d to %d %d", i3, i5, binA, binT, i1, i2); - for (Int_t i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { - for (Int_t i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { - Int_t binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); - Double_t phi = -target->GetAxis(4)->GetBinCenter(i4); + for (int i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { + for (int i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { + int binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); + double phi = -target->GetAxis(4)->GetBinCenter(i4); if (phi < -PIHalf) { phi += TwoPI; } - Int_t binPhi = target->GetAxis(4)->FindBin(phi); + int binPhi = target->GetAxis(4)->FindBin(phi); - Int_t binSource[] = {binEta, binA, binT, i3, binPhi, i5}; - Int_t binTarget[] = {i0, i1, i2, i3, i4, i5}; + int binSource[] = {binEta, binA, binT, i3, binPhi, i5}; + int binTarget[] = {i0, i1, i2, i3, i4, i5}; - Double_t value = source->GetBinContent(binSource); - Double_t error = source->GetBinError(binSource); + double value = source->GetBinContent(binSource); + double error = source->GetBinError(binSource); if (error == 0) { continue; } - Double_t value2 = target->GetBinContent(binTarget); - Double_t error2 = target->GetBinError(binTarget); + double value2 = target->GetBinContent(binTarget); + double error2 = target->GetBinError(binTarget); - Double_t sum = value; - Double_t err = error; + double sum = value; + double err = error; if (error2 > 0) { sum = value + value2; - err = TMath::Sqrt(error * error + error2 * error2); + err = std::sqrt(error * error + error2 * error2); } // Printf(" Values: %f +- %f; %f +- %f --> %f +- %f", value, error, value2, error2, sum, err); @@ -1489,13 +1493,13 @@ void CorrelationContainer::symmetrizepTBins() } //____________________________________________________________________ -void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) +void CorrelationContainer::extendTrackingEfficiency(bool verbose) { // fits the tracking efficiency at high pT with a constant and fills all bins with this tracking efficiency - Float_t fitRangeBegin = 5.01; - Float_t fitRangeEnd = 14.99; - Float_t extendRangeBegin = 10.01; + float fitRangeBegin = 5.01; + float fitRangeEnd = 14.99; + float extendRangeBegin = 10.01; if (mTrackHistEfficiency->getNVar() == 3) { TH1* obj = getTrackingEfficiency(1); @@ -1507,7 +1511,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingEff = obj->GetFunction("pol0")->GetParameter(0); + float trackingEff = obj->GetFunction("pol0")->GetParameter(0); obj = getTrackingContamination(1); @@ -1518,17 +1522,17 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingCont = obj->GetFunction("pol0")->GetParameter(0); + float trackingCont = obj->GetFunction("pol0")->GetParameter(0); LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitted efficiency between %f and %f and got %f tracking efficiency and %f tracking contamination correction. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, trackingEff, trackingCont, extendRangeBegin, mEtaMin, mEtaMax); // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - Int_t bins[3]; + int bins[3]; bins[0] = x; bins[1] = y; bins[2] = z; @@ -1541,28 +1545,28 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } } else if (mTrackHistEfficiency->getNVar() == 4) { // fit in centrality intervals of 20% for efficiency, one bin for contamination - Float_t* trackingEff = nullptr; - Float_t* trackingCont = nullptr; - Float_t centralityBins[] = {0, 10, 20, 40, 60, 100}; - Int_t nCentralityBins = 5; + float* trackingEff = nullptr; + float* trackingCont = nullptr; + float centralityBins[] = {0, 10, 20, 40, 60, 100}; + int nCentralityBins = 5; LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitting efficiencies between %f and %f. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, extendRangeBegin, mEtaMin, mEtaMax); // 0 = eff; 1 = cont - for (Int_t caseNo = 0; caseNo < 2; caseNo++) { - Float_t* target = nullptr; - Int_t centralityBinsLocal = nCentralityBins; + for (int caseNo = 0; caseNo < 2; caseNo++) { + float* target = nullptr; + int centralityBinsLocal = nCentralityBins; if (caseNo == 0) { - trackingEff = new Float_t[centralityBinsLocal]; + trackingEff = new float[centralityBinsLocal]; target = trackingEff; } else { centralityBinsLocal = 1; - trackingCont = new Float_t[centralityBinsLocal]; + trackingCont = new float[centralityBinsLocal]; target = trackingCont; } - for (Int_t i = 0; i < centralityBinsLocal; i++) { + for (int i = 0; i < centralityBinsLocal; i++) { if (centralityBinsLocal == 1) { setCentralityRange(centralityBins[0] + 0.1, centralityBins[nCentralityBins] - 0.1); } else { @@ -1573,7 +1577,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) new TCanvas; proj->DrawCopy(); } - if ((Int_t)proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd) == 0) { + if (static_cast(proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd)) == 0) { target[i] = proj->GetFunction("pol0")->GetParameter(0); } else { target[i] = 0; @@ -1583,20 +1587,20 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - for (Int_t z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis + for (int z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis { - Int_t bins[4]; + int bins[4]; bins[0] = x; bins[1] = y; bins[2] = z; bins[3] = z2; - Int_t z2Bin = 0; + int z2Bin = 0; while (centralityBins[z2Bin + 1] < mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetBinCenter(z2)) { z2Bin++; } @@ -1623,11 +1627,11 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } /* -void CorrelationContainer::Scale(Double_t factor) +void CorrelationContainer::Scale(double factor) { // scales all contained histograms by the given factor - for (Int_t i=0; i<4; i++) + for (int i=0; i<4; i++) if (mTrackHist[i]) mTrackHist[i]->Scale(factor); @@ -1639,15 +1643,15 @@ void CorrelationContainer::Reset() { // resets all contained histograms - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { mPairHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { mTriggerHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { + for (int step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { mTrackHistEfficiency->getTHn(step)->Reset(); } } @@ -1659,7 +1663,7 @@ THnBase* CorrelationContainer::changeToThn(THnBase* sparse) return THn::CreateHn(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse); } -void CorrelationContainer::fillEvent(Float_t centrality, CFStep step) +void CorrelationContainer::fillEvent(float centrality, CFStep step) { // Fill per-event information mEventCount->Fill(step, centrality); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index 8c283cecc40..83f52b206e0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -78,6 +78,11 @@ o2physics_add_dpl_workflow(long-range-dihadron-cor PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(mft-correlations + SOURCES mftcorrelations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(two-particle-correlation-pp SOURCES twoParticleCorrelationPp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore diff --git a/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx new file mode 100644 index 00000000000..e34bd64f3a9 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx @@ -0,0 +1,1449 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file mftcorrelations.cxx +/// \brief task for two-particle (hadron-hadron) angular correlations built from MFT tracks + +/// \author Mintu Haldar + +#include "PWGCF/Core/CorrelationContainer.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Pair<>, SameKindPair<> +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; + +// Bitmask describing which multiplicity/centrality estimators feed the "multCorrelations" QA +// histogram and, when set, the outlier-rejection formula. +namespace o2::aod +{ +namespace mftmultset +{ +DECLARE_SOA_COLUMN(Multiplicities, multiplicities, std::vector); //! List of auxiliary multiplicities +enum MultiplicityEstimators : uint16_t { + CentFT0C = 1 << 0, + MultFV0A = 1 << 1, + MultNTracksPV = 1 << 2, + MultNTracksGlobal = 1 << 3, + CentFT0M = 1 << 4 +}; +inline constexpr uint32_t NMultiplicityEstimators = __builtin_ctz(CentFT0M) + 1; +} // namespace mftmultset + +DECLARE_SOA_TABLE(MFTMultSets, "AOD", "MFTMULTSET", mftmultset::Multiplicities); //! Auxilary multiplicity set table +using MFTMultSet = MFTMultSets::iterator; +} // namespace o2::aod + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct Mftcorrelations { + SliceCache cache; + + // ------------------------------------------------------------------ Configurables + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + + // Global acceptance + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackEtaCut, float, 0.8f, "Eta cut for global (central-barrel) tracks used as MFT-Global trigger") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMin, float, 0.15f, "Minimal pT for global tracks") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMax, float, 10.0f, "Maximal pT for global tracks") + + // MFT acceptance is roughly -3.6 < eta < -2.5; keep as configurables since it is tag/setup dependent. + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMin, float, -3.6f, "Minimal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMax, float, -2.5f, "Maximal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMin, float, 0.5f, "Minimal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMax, float, 20.0f, "Maximal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTMinNClusters, int, 5, "Minimal number of MFT clusters on track") + + O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)") + O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of the pair: 0 = all; 1 = like sign; -1 = unlike sign") + + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON: read efficiency THn from a local ROOT file instead of CCDB") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for trigger particles") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for associated particles") + // FIX #6d: efficiency for central-barrel (global) tracks, used in MFT-Global (trigger) and Global-Global channels. + O2_DEFINE_CONFIGURABLE(cfgEfficiencyGlobal, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for central-barrel (global) tracks") + + O2_DEFINE_CONFIGURABLE(cfgMultCorrelationsMask, uint16_t, 0, "Bitmask of multiplicity/centrality estimators to fill in multCorrelations QA / use for outlier rejection (see mftmultset enum)") + O2_DEFINE_CONFIGURABLE(cfgMultCutFormula, std::string, "", "Multiplicity outlier-rejection formula. A result > 0 accepts the event. Parameters: [cFT0C], [mFV0A], [mPV], [mGlob], [cFT0M]") + + O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") + O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") + + // ------------------------------------------------------------------ Phi weight + // NOTE: keep OFF (empty file) when the efficiency object already has a phi axis, otherwise phi is corrected twice. + O2_DEFINE_CONFIGURABLE(cfgPhiWeightFile, std::string, "", "Local ROOT file with the phi-weight TH1 (empty = disabled)") + O2_DEFINE_CONFIGURABLE(cfgPhiWeightName, std::string, "", "Name of the phi-weight TH1 object inside cfgPhiWeightFile") + + // ------------------------------------------------------------------ Axes + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -2.2, 2.2}, "delta eta axis for histograms (MFT-MFT so somewhat narrower than TPC-TPC)"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -3.6, -2.5}, "eta axis for efficiency histograms (MFT acceptance)"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt axis for efficiency histograms"}; + ConfigurableAxis axisPhiEfficiency{"axisPhiEfficiency", {36, 0, TwoPI}, "phi axis for efficiency histograms"}; + + ConfigurableAxis axisEtaEfficiencyGlobal{"axisEtaEfficiencyGlobal", {16, -0.8, 0.8}, "eta axis for efficiency histograms (global tracks)"}; + ConfigurableAxis axisPtEfficiencyGlobal{"axisPtEfficiencyGlobal", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 20.0}, "pt axis for efficiency histograms (global tracks)"}; + + ConfigurableAxis axisMultCorrCent{"axisMultCorrCent", {100, 0, 100}, "multiplicity correlation axis for centralities"}; + ConfigurableAxis axisMultCorrV0{"axisMultCorrV0", {1000, 0, 100000}, "multiplicity correlation axis for V0 multiplicities"}; + ConfigurableAxis axisMultCorrMult{"axisMultCorrMult", {1000, 0, 1000}, "multiplicity correlation axis for track multiplicities"}; + + // ------------------------------------------------------------------ QA Axes + ConfigurableAxis axisMultQA{"axisMultQA", {100, 0, 100}, "multiplicity/centrality axis for QA histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 15.0, 20.0}, "pT axis for QA histograms (yields)"}; + ConfigurableAxis axisEtaQA{"axisEtaQA", {50, -4, -2}, "eta axis for QA histograms"}; + ConfigurableAxis axisPhiQA{"axisPhiQA", {200, 0, TwoPI}, "phi axis for QA histograms"}; + + // Global MFT + ConfigurableAxis axisEtaGlobalQA{"axisEtaGlobalQA", {40, -1, 1}, "eta axis for global-track QA histograms"}; + ConfigurableAxis axisDeltaEtaMftGlobal{"axisDeltaEtaMftGlobal", {60, 1.0, 5.0}, "delta eta axis for MFT-Global correlations (deltaEta = etaGlobal - etaMFT is POSITIVE, roughly 1.7-4.4); override via JSON if needed"}; + // FIX #4: delta-eta axis for the global-global (central-barrel hadron-hadron) channel. + ConfigurableAxis axisDeltaEtaGlobal{"axisDeltaEtaGlobal", {32, -1.6, 1.6}, "delta eta axis for global-global (central-barrel hadron-hadron) correlations"}; + + // ------------------------------------------------------------------ Event selection + Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex; + + // ------------------------------------------------------------------ MFT track selection + Filter mftTrackFilter = (aod::fwdtrack::eta > cfgMFTCutEtaMin) && (aod::fwdtrack::eta < cfgMFTCutEtaMax) && + (aod::fwdtrack::pt > cfgMFTCutPtMin) && (aod::fwdtrack::pt < cfgMFTCutPtMax); + + // ------------------------------------------------------------------ Global track selection + Filter globalTrackFilter = (nabs(aod::track::eta) < cfgGlobalTrackEtaCut) && + (aod::track::pt > cfgGlobalTrackPtMin) && (aod::track::pt < cfgGlobalTrackPtMax) && + requireGlobalTrackInFilter(); + + // ------------------------------------------------------------------ Output + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + + OutputObj sameMftGlobal{"sameEventMftGlobal"}; + OutputObj mixedMftGlobal{"mixedEventMftGlobal"}; + + // FIX #4: global-global (central-barrel hadron-hadron) output objects. + OutputObj sameGlobal{"sameEventGlobalGlobal"}; + OutputObj mixedGlobal{"mixedEventGlobalGlobal"}; + + HistogramRegistry registry{"registry"}; + + Service ccdb; + Service pdg; + + struct Config { + THn* mEfficiencyTrigger = nullptr; + THn* mEfficiencyAssociated = nullptr; + THn* mEfficiencyGlobal = nullptr; // FIX #6d + TH1* mPhiWeight = nullptr; + bool efficiencyLoaded = false; + } cfg; + + std::vector efficiencyAssociatedCache; + std::vector phiWeightCache; + std::vector phiWeightCache1; + std::unique_ptr multCutFormula; + std::array multCutFormulaParamIndex{}; + + // Collisions are expected to already carry the joined centrality/multiplicity estimator tables. + using ExtendedCollisions = soa::Join; + using FilteredCollisions = soa::Filtered; + using FilteredMFTTracks = soa::Filtered; + using FilteredGlobalTracks = soa::Filtered>; + + // For MFT MC Efficiency + // NOTE SmallGroups includes soa::Filtered always + using McEffCollisions = soa::SmallGroups>; + using McEffMFTTracks = soa::Filtered>; + + // For Global MC Efficiency + using McEffGlobalTracks = soa::Filtered>; + + void init(o2::framework::InitContext&) + { + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + // FIX #6b: efficiency-corrected QA histograms (weight = efficiency correction factor) + registry.add("yields_corrected", "efficiency-corrected multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi_corrected", "efficiency-corrected multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + registry.add("nClusters", "MFT track number of clusters", {HistType::kTH1F, {{12, -0.5, 11.5, "n clusters"}}}); + registry.add("multiplicity_raw", "event multiplicity (no cuts)", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + + // MC Efficiency + registry.add("MCEff/hNRecoColl", "N reco collisions per MC collision", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEff/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + registry.add("MCEffGlobal/hNRecoColl", "N reco collisions per MC collision (global)", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEffGlobal/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + if (cfgMultCorrelationsMask != 0) { + std::vector multAxes; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + multAxes.emplace_back(axisMultCorrCent, "FT0C centrality"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + multAxes.emplace_back(axisMultCorrV0, "V0A multiplicity"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + multAxes.emplace_back(axisMultCorrMult, "Nch PV"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + multAxes.emplace_back(axisMultCorrMult, "Nch Global"); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + multAxes.emplace_back(axisMultCorrCent, "FT0M centrality"); + registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); + + // FIX #3 + if (!(cfgMultCorrelationsMask & aod::mftmultset::CentFT0C)) { + LOGF(warning, + "cfgMultCorrelationsMask=%d does not include CentFT0C. Mixed-event binning follows " + "getMultiplicity(), so make sure the efficiency objects were produced with the same estimator.", + (int)cfgMultCorrelationsMask); + } + } + + const int maxMixBin = AxisSpec(axisMultiplicity).getNbins() * AxisSpec(axisVertex).getNbins(); + registry.add("eventcount_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + // FIX #1 + registry.add("eventcount_mftglobal_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mftglobal_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_mftglobal_same", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mftglobal_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + if (!cfgMultCutFormula.value.empty()) { + multCutFormula = std::make_unique("multCutFormula", cfgMultCutFormula.value.c_str()); + std::fill_n(multCutFormulaParamIndex.begin(), std::size(multCutFormulaParamIndex), ~0u); + std::array pars = {"cFT0C", "mFV0A", "mPV", "mGlob", "cFT0M"}; + for (uint32_t i = 0, n = multCutFormula->GetNpar(); i < n; ++i) { + auto m = std::find(pars.begin(), pars.end(), multCutFormula->GetParName(i)); + if (m == pars.end()) { + LOGF(warning, "Unknown parameter in cfgMultCutFormula: %s", multCutFormula->GetParName(i)); + continue; + } + multCutFormulaParamIndex[std::distance(pars.begin(), m)] = i; + } + } + + registry.add("mftmft/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + registry.add("mftmft/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + + registry.add("mftGlobal/SE/Trig_etavsphi", "trigger (global) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("mftGlobal/SE/Trig_pt", "trigger (global) pT", {HistType::kTH1F, {{axisPtTrigger, "p_{T}"}}}); + registry.add("mftGlobal/SE/Assoc_etavsphi", "associated (MFT) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaQA, "#eta"}}}); + registry.add("mftGlobal/SE/Assoc_pt", "associated (MFT) pT", {HistType::kTH1F, {{axisPtAssoc, "p_{T}"}}}); + registry.add("mftGlobal/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + registry.add("mftGlobal/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + + // FIX #4: QA histograms for the global-global channel. + registry.add("eventcount_global_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_global_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_global_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_global_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + registry.add("global/SE/etavsphi", "global track eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("global/SE/pt", "global track pT", {HistType::kTH1F, {{axisPtQA, "p_{T}"}}}); + registry.add("global/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + registry.add("global/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + + std::vector corrAxis = {{axisDeltaEta, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + std::vector effAxis = {{axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector effAxisGlobal = {{axisEtaEfficiencyGlobal, "#eta"}, + {axisPtEfficiencyGlobal, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector userAxis; // no extra axis needed for hadron-hadron + + std::vector corrAxisMftGlobal = {{axisDeltaEtaMftGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + // FIX #4 + std::vector corrAxisGlobal = {{axisDeltaEtaGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + sameMftGlobal.setObject(new CorrelationContainer("sameEventMftGlobal", "sameEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + mixedMftGlobal.setObject(new CorrelationContainer("mixedEventMftGlobal", "mixedEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + + same->setTrackEtaCut(cfgMFTCutEtaMax); // upper bound of |eta| cut used internally by CorrelationContainer + + sameGlobal.setObject(new CorrelationContainer("sameEventGlobalGlobal", "sameEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + mixedGlobal.setObject(new CorrelationContainer("mixedEventGlobalGlobal", "mixedEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + sameGlobal->setTrackEtaCut(cfgGlobalTrackEtaCut); + + if (!cfgEfficiencyAssociated.value.empty()) + efficiencyAssociatedCache.reserve(512); + + // FIX #5: load the phi weight once at init(). + loadPhiWeight(); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + // ------------------------------------------------------------------ helpers + + int getMagneticField(uint64_t timestamp) + { + static o2::parameters::GRPObject* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + } + return grpo->getNominalL3Field(); + } + + void loadEfficiency(uint64_t timestamp) + { + if (cfg.efficiencyLoaded) { + return; + } + if (!cfgEfficiencyTrigger.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); + cfg.mEfficiencyTrigger = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); + } + if (cfg.mEfficiencyTrigger == nullptr) { + LOGF(fatal, "Could not load trigger efficiency from %s", cfgEfficiencyTrigger.value.c_str()); + } + LOGF(info, "Loaded MFT trigger efficiency from %s (%p)", cfgEfficiencyTrigger.value.c_str(), static_cast(cfg.mEfficiencyTrigger)); + } + if (!cfgEfficiencyAssociated.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); + cfg.mEfficiencyAssociated = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); + } + if (cfg.mEfficiencyAssociated == nullptr) { + LOGF(fatal, "Could not load associated efficiency from %s", cfgEfficiencyAssociated.value.c_str()); + } + LOGF(info, "Loaded MFT associated efficiency from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); + } + // FIX #6d + if (!cfgEfficiencyGlobal.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyGlobal.value.c_str(), "READ"); + cfg.mEfficiencyGlobal = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyGlobal = ccdb->getForTimeStamp>(cfgEfficiencyGlobal, timestamp); + } + if (cfg.mEfficiencyGlobal == nullptr) { + LOGF(fatal, "Could not load global-track efficiency from %s", cfgEfficiencyGlobal.value.c_str()); + } + LOGF(info, "Loaded global-track efficiency from %s (%p)", cfgEfficiencyGlobal.value.c_str(), static_cast(cfg.mEfficiencyGlobal)); + } + cfg.efficiencyLoaded = true; + } + + // MFT phi is in [-pi,pi], McParticle/global-track phi in [0,2pi). Use [0,2pi) everywhere. + static float wrapPhi(float phi) + { + return RecoDecay::constrainAngle(phi, 0.f); + } + + // FIX #6a: expected axis order of the efficiency object: (eta, pt, multiplicity, posZ [, phi]). + // - phi is wrapped to [0, 2pi) (MFT track.phi() is in [-pi, pi]) + // - 4-dim and 5-dim objects are supported + // - bins outside the axis range (under/overflow) return 0 = track outside efficiency acceptance + double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ, float phi) + { + if (eff == nullptr) { + return 1.0; + } + constexpr int NDimWithoutPhi = 4; // eta, pt, mult, z + constexpr int NDimWithPhi = 5; // eta, pt, mult, z, phi + const int nDim = eff->GetNdimensions(); + if (nDim != NDimWithoutPhi && nDim != NDimWithPhi) { + LOGF(fatal, "Efficiency object has %d dimensions, expected %d (eta,pt,mult,z) or %d (+phi)", nDim, NDimWithoutPhi, NDimWithPhi); + } + const double vals[NDimWithPhi] = {eta, pt, multiplicity, posZ, static_cast(RecoDecay::constrainAngle(phi, 0.f))}; + int bins[NDimWithPhi]; + for (int i = 0; i < nDim; ++i) { + TAxis* ax = eff->GetAxis(i); + bins[i] = ax->FindBin(vals[i]); + if (bins[i] < 1 || bins[i] > ax->GetNbins()) { + return 0.0; + } + } + return eff->GetBinContent(bins); + } + + template + using HasSign = decltype(std::declval().sign()); + template + using HasPdgCode = decltype(std::declval().pdgCode()); + template + using HasIsPhysicalPrimary = decltype(std::declval().isPhysicalPrimary()); + + // Retrieve the multiplicity/centrality estimator selected by cfgMultCorrelationsMask. + // Falls back to FT0C centrality if no mask bit is set. + template + float getMultiplicity(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) { + return collision.centFT0C(); + } + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + return collision.centFT0C(); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + return collision.centFT0M(); + return collision.centFT0C(); + } + + template + bool passOutlier(TCollision const& collision) + { + if (cfgMultCutFormula.value.empty() || multCutFormula == nullptr) + return true; + if (multCutFormulaParamIndex[0] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[0], collision.centFT0C()); + if (multCutFormulaParamIndex[1] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[1], collision.multFV0A()); + if (multCutFormulaParamIndex[2] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[2], collision.multNTracksPV()); + if (multCutFormulaParamIndex[3] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[3], collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (multCutFormulaParamIndex[4] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[4], collision.centFT0M()); + return multCutFormula->Eval() > 0.0f; + } + + template + void fillMultCorrelations(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) + return; + std::vector v; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + v.push_back(collision.centFT0C()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + v.push_back(collision.multFV0A()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + v.push_back(collision.multNTracksPV()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + v.push_back(collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + v.push_back(collision.centFT0M()); + registry.get(HIST("multCorrelations")).get()->Fill(v.data()); + } + + void loadPhiWeight() + { + if (cfg.mPhiWeight != nullptr || cfgPhiWeightFile.value.empty()) { + return; // already loaded, or feature disabled + } + TFile* f = TFile::Open(cfgPhiWeightFile.value.c_str(), "READ"); + if (!f || f->IsZombie()) { + LOGF(fatal, "Could not open phi weight file %s", cfgPhiWeightFile.value.c_str()); + return; + } + cfg.mPhiWeight = dynamic_cast(f->Get(cfgPhiWeightName.value.c_str())); + if (cfg.mPhiWeight) { + cfg.mPhiWeight->SetDirectory(nullptr); // detach so it survives f->Close() + } else { + LOGF(fatal, "Could not find object %s in %s", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str()); + } + f->Close(); + LOGF(info, "Loaded phi weight '%s' from %s (%p)", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str(), static_cast(cfg.mPhiWeight)); + } + + // Looks up a bin-content-based weight from a 1-D phi map. + // Returns 0 if the histogram is missing so the >0 check upstream skips it cleanly. + float getPhiWeight(TH1* hPhiWeight, float phi) + { + if (!hPhiWeight) { + return 0.f; + } + int binX = hPhiWeight->GetXaxis()->FindBin(wrapPhi(phi)); + return hPhiWeight->GetBinContent(binX); + } + + // ------------------------------------------------------------------ QA + // FIX #6b: "yields"/"etaphi" are RAW. "yields_corrected"/"etaphi_corrected" are weighted with the + // efficiency correction factor (associated-particle efficiency object). + template + void fillQA(TCollision const& collision, float multiplicity, TTracks const& tracks) + { + registry.fill(HIST("multiplicity"), multiplicity); + fillMultCorrelations(collision); + const float posZ = collision.posZ(); + + for (const auto& track : tracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("yields"), multiplicity, track.pt(), track.eta()); + + float phiWeight = 1.f; + if (cfg.mPhiWeight) { + float phiWgt = getPhiWeight(cfg.mPhiWeight, track.phi()); + phiWeight = (phiWgt > 0) ? 1.f / phiWgt : 1.f; + } + registry.fill(HIST("etaphi"), multiplicity, track.eta(), wrapPhi(track.phi()), phiWeight); + registry.fill(HIST("nClusters"), track.nClusters()); + + float effWeight = 1.f; + if (cfg.mEfficiencyAssociated) { + effWeight = getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi()); + } + registry.fill(HIST("yields_corrected"), multiplicity, track.pt(), track.eta(), effWeight); + registry.fill(HIST("etaphi_corrected"), multiplicity, track.eta(), wrapPhi(track.phi()), effWeight); + } + } + + // ------------------------------------------------------------------ event-level acceptance + template + bool acceptCollision(TCollision const& collision) + { + if (!collision.sel8()) { + return false; + } + if (!passOutlier(collision)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------ correlation filling + // Hadron-hadron (MFT-MFT). tracks1 = trigger collection, tracks2 = associated collection. + // Same-event: called as fillCorrelations(..., tracks, tracks, ...) -> self-correlation with index check. + // Mixed-event: called as fillCorrelations(..., tracks1, tracks2, ...) from two different collisions. + + template + void fillCorrelations(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, float multiplicity, bool mixing, float posZ, float eventWeight) + { + // ---------------------------------------------------------------- per-call caches + // Indexed by plain position in the loop (NOT filteredIndex()), and built for BOTH + // collections. This is what makes the mixed event correct: tracks1 and tracks2 have the same + // C++ type in SameKindPair, so a type-based "sameCollection" is true in ME as well. + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + efficiencyAssociatedCache.clear(); + if (cfg.mEfficiencyAssociated) { + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi())); + } + } + } + + phiWeightCache.clear(); // associated side (tracks2) + phiWeightCache1.clear(); // trigger side (tracks1) + if (cfg.mPhiWeight) { + phiWeightCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + phiWeightCache.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + phiWeightCache1.reserve(tracks1.size()); + for (const auto& track : tracks1) { + phiWeightCache1.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + } + + // ---------------------------------------------------------------- trigger loop + int i1 = -1; + for (const auto& track1 : tracks1) { + ++i1; // must stay before any 'continue' + if (track1.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { + if (!track1.isPhysicalPrimary()) + continue; + } + } else if constexpr (std::experimental::is_detected::value) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + // phi weight of the trigger: applied BEFORE the trigger histogram is filled, so the + // per-trigger normalisation and the pair weights stay consistent. + if (cfg.mPhiWeight) { + const float w1 = phiWeightCache1[i1]; + if (w1 > 0.f) { + triggerWeight *= 1.f / w1; + } + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + // ---------------------------------------------------------------- associated loop + int i2 = -1; + for (const auto& track2 : tracks2) { + ++i2; // must stay before any 'continue' + + if (!mixing && track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation (same event only) + } + if (track2.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (std::experimental::is_detected::value && + std::experimental::is_detected::value) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; // already contains event, trigger eff, trigger phi weight + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= efficiencyAssociatedCache[i2]; + } + } + if (cfg.mPhiWeight) { + const float w2 = phiWeightCache[i2]; + if (w2 > 0.f) { + associatedWeight *= 1.f / w2; + } + } + + const float deltaEta = track1.eta() - track2.eta(); + const float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + + if (mixing) { + registry.fill(HIST("mftmft/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftmft/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + // FIX #2 + FIX #6d: MFT-Global. Trigger = central-barrel track (cfg.mEfficiencyGlobal), + // associated = MFT track (cfg.mEfficiencyAssociated). + template + void fillCorrMftGlobal(TTarget target, TTriggers const& triggers, TMFTs const& mftTracks, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool TriggerHasSign = std::experimental::is_detected::value; + constexpr bool AssocHasSign = std::experimental::is_detected::value; + + if (!mixing) { + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("mftGlobal/SE/Assoc_etavsphi"), assocTrack.phi(), assocTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Assoc_pt"), assocTrack.pt()); + } + } + + for (const auto& triggerTrack : triggers) { + if constexpr (TriggerHasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * triggerTrack.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, triggerTrack.eta(), triggerTrack.pt(), multiplicity, posZ, triggerTrack.phi()); + } + } + if (!mixing) { + registry.fill(HIST("mftGlobal/SE/Trig_etavsphi"), triggerTrack.phi(), triggerTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Trig_pt"), triggerTrack.pt()); + } + target->getTriggerHist()->Fill(step, triggerTrack.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + if constexpr (AssocHasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * assocTrack.sign() < 0) + continue; + } + if constexpr (TriggerHasSign && AssocHasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * triggerTrack.sign() * assocTrack.sign() < 0) + continue; + } + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyAssociated, assocTrack.eta(), assocTrack.pt(), multiplicity, posZ, assocTrack.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(triggerTrack.phi() - assocTrack.phi(), -o2::constants::math::PIHalf); + float deltaEta = triggerTrack.eta() - assocTrack.eta(); + if (mixing) { + registry.fill(HIST("mftGlobal/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftGlobal/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, assocTrack.pt(), triggerTrack.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // FIX #4 + FIX #6d: Global-Global (central-barrel hadron-hadron). Both tracks use cfg.mEfficiencyGlobal. + // No nClusters() cut: global tracks are quality-selected via FilteredGlobalTracks. + template + void fillCorrGlobalGlobal(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool Track1HasSign = std::experimental::is_detected::value; + constexpr bool Track2HasSign = std::experimental::is_detected::value; + + for (const auto& track1 : tracks1) { + if constexpr (Track1HasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + if (!mixing) { + registry.fill(HIST("global/SE/etavsphi"), track1.phi(), track1.eta()); + registry.fill(HIST("global/SE/pt"), track1.pt()); + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& track2 : tracks2) { + if constexpr (std::is_same::value) { + if (track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation + } + } + + if constexpr (Track2HasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (Track1HasSign && Track2HasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track2.eta(), track2.pt(), multiplicity, posZ, track2.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + float deltaEta = track1.eta() - track2.eta(); + if (mixing) { + registry.fill(HIST("global/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("global/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // ------------------------------------------------------------------ SAME EVENT (data) + void processSameMFT(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredMFTTracks const& tracks) + { + registry.fill(HIST("multiplicity_raw"), collision.centFT0C()); // unconditional, no cuts + if (!acceptCollision(collision)) { + return; + } + + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using BinningType = ColumnBinningPolicy; + BinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processSameMFT: %d MFT tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_same"), bin); + registry.fill(HIST("trackcount_same"), bin, tracks.size()); + fillQA(collision, multiplicity, tracks); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + same->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + if (hasEfficiency) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processSameMFT, "Process same event MFT-MFT correlations", true); + + void processMftGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + registry.fill(HIST("eventcount_mftglobal_same"), bin); + registry.fill(HIST("trackcount_mftglobal_same"), bin, globalTracks.size(), mftTracks.size()); + + // FIX #6d: trigger = global track, associated = MFT track + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalSE, "Process same event MFT-vs-global-track correlations", false); + + // FIX #4: Same-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalSE: %d global tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_global_same"), bin); + registry.fill(HIST("trackcount_global_same"), bin, tracks.size()); + + // FIX #6d + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalSE, "Process same event global-global (central-barrel) correlations", false); + + void processSameMFTMultSet(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const& bcs, FilteredMFTTracks const& tracks) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processSameMFT(collision, bcs, tracks); + } + PROCESS_SWITCH(Mftcorrelations, processSameMFTMultSet, "Process same event MFT-MFT correlations with multi-estimator QA", false); + + // ------------------------------------------------------------------ MIXED EVENT (data) + void processMixedMFT(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const&) + { + // Bin mixed events on the SAME multiplicity/centrality estimator that getMultiplicity() returns + // (driven by cfgMultCorrelationsMask), instead of a hardcoded CentFT0C column. This keeps the + // mixing pool synchronized with whatever estimator is actually filled into the CorrelationContainer, + // and lets you switch estimators (e.g. FT0C -> FT0M) purely from the JSON config. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + BinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processMixedMFT: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + + registry.fill(HIST("eventcount_mixed"), bin); + registry.fill(HIST("trackcount_mixed"), bin, tracks1.size(), tracks2.size()); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow() && fillReco) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (it.isNewWindow() && hasEfficiency) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + + if (fillReco) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + if (hasEfficiency) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFT, "Process mixed event MFT-MFT correlations", false); + + void processMftGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + // Same fix as processMixedMFT: bin on getMultiplicity() (driven by cfgMultCorrelationsMask) + // instead of a hardcoded CentFT0C column, so the mixing pool matches whatever estimator is + // actually filled into the CorrelationContainer. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(globalTracks, mftTracks); + Pair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + int pairCounter = 0; + int acceptedPairCounter = 0; + int newWindowCounter = 0; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, globalTracks1, collision2, mftTracks2] = *it; + pairCounter++; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + acceptedPairCounter++; + + const float multiplicity = getMultiplicity(collision1); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + + if (it.isNewWindow()) { + newWindowCounter++; + loadEfficiency(collision1.template bc_as().timestamp()); + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_mftglobal_mixed"), bin); + } + registry.fill(HIST("trackcount_mftglobal_mixed"), bin, globalTracks1.size(), mftTracks2.size()); + + if (fillReco) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + + if (cfgVerbosity > 0) { + LOGF(info, "processMftGlobalME: collisions %d | pairs iterated %d | accepted %d | new windows %d", + collisions.size(), pairCounter, acceptedPairCounter, newWindowCounter); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalME, "Process mixed event MFT-vs-global-track correlations", false); + + // FIX #4: Mixed-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalME: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_global_mixed"), bin); + } + registry.fill(HIST("trackcount_global_mixed"), bin, tracks1.size(), tracks2.size()); + + if (fillReco) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalME, "Process mixed event global-global (central-barrel) correlations", false); + + void processMixedMFTMultSet(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const& bcs) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processMixedMFT(collisions, tracks, bcs); + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFTMultSet, "Process mixed event MFT-MFT correlations with multi-estimator QA", false); + + template + bool isChargedPrimary(TParticle const& p) + { + if (!p.isPhysicalPrimary()) { + return false; + } + auto* entry = pdg->GetParticle(p.pdgCode()); + return entry != nullptr && entry->Charge() != 0; + } + + // ------------------------------------------------------------------ MC efficiency extraction + Preslice perCollisionMFT = aod::fwdtrack::collisionId; + + void processMCEfficiencyMFT(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffMFTTracks const& tracks) + { + registry.fill(HIST("MCEff/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEff/hCutFlow"), 1); + if (collisions.size() != 1) { + return; + } + registry.fill(HIST("MCEff/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyMFT: ENTER | mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEff/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // driven by cfgMultCorrelationsMask + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || mcParticle.eta() < cfgMFTCutEtaMin || mcParticle.eta() > cfgMFTCutEtaMax || mcParticle.pt() < cfgMFTCutPtMin || mcParticle.pt() > cfgMFTCutPtMax) { + continue; + } + same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + + auto groupedTracks = tracks.sliceBy(perCollisionMFT, collGlobalIndex); + + std::unordered_set countedPrimaries; + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + if (countedAll.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + } else { + // FIX #6e: wrap phi to [0,2pi) like the MC-particle phi used for the other steps + same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyMFT, "MC: Extract MFT tracking efficiency", false); + + //-------------------------------------------------------------------MC Efficiency Extraction for Global + + Preslice perCollisionGlobal = aod::track::collisionId; + + void processMCEfficiencyGlobal(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffGlobalTracks const& tracks) + { + registry.fill(HIST("MCEffGlobal/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEffGlobal/hCutFlow"), 1); + if (collisions.size() != 1) { // exactly one reconstructed collision per MC collision (as in the MFT efficiency) + return; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyGlobal: mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // same estimator as in data (cfgMultCorrelationsMask) + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + // Generated: charged physical primaries in the global-track acceptance + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || + std::abs(mcParticle.eta()) >= cfgGlobalTrackEtaCut || + mcParticle.pt() < cfgGlobalTrackPtMin || mcParticle.pt() > cfgGlobalTrackPtMax) { + continue; + } + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + + // Reconstructed: global tracks of the selected reco collision (globalTrackFilter already applied) + auto groupedTracks = tracks.sliceBy(perCollisionGlobal, collGlobalIndex); + + std::unordered_set countedPrimaries; // count each MC particle once (split tracks) + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + if (countedAll.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + } else { + // fake track + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyGlobal, "MC: Extract central-barrel (global track) tracking efficiency", false); + + // ------------------------------------------------------------------ MC generator-level correlations (truth reference / closure test) + using McRecoCollisions = soa::Join; + + // -1, 0, +1 from the PDG database charge (0 = neutral or unknown code) + template + int getMcCharge(TParticle const& p) + { + auto* entry = pdg->GetParticle(p.pdgCode()); + if (entry == nullptr) { + return 0; + } + const double q = entry->Charge(); + return (q > 0) - (q < 0); + } + + // cheap cuts first, PDG lookup last + template + bool acceptMftTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && p.eta() > cfgMFTCutEtaMin && p.eta() < cfgMFTCutEtaMax && + p.pt() > cfgMFTCutPtMin && p.pt() < cfgMFTCutPtMax && getMcCharge(p) != 0; + } + + template + bool acceptGlobalTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && std::abs(p.eta()) < cfgGlobalTrackEtaCut && + p.pt() > cfgGlobalTrackPtMin && p.pt() < cfgGlobalTrackPtMax && getMcCharge(p) != 0; + } + + template + std::vector collectAccepted(TParticles const& particles, TSelector accept) + { + std::vector idx; + idx.reserve(64); + for (const auto& p : particles) { + if (accept(p)) { + idx.push_back(p.globalIndex()); + } + } + return idx; + } + + template + void fillTruthPairs(TTarget& target, TParticles1 const& particles1, TParticles2 const& particles2, + std::vector const& acc1, std::vector const& acc2, + float multiplicity, float posZ, float weight, bool sameCollection) + { + for (const auto& i1 : acc1) { + const auto& p1 = particles1.iteratorAt(i1 - particles1.begin().globalIndex()); + target->getTriggerHist()->Fill(CorrelationContainer::kCFStepAll, p1.pt(), multiplicity, posZ, weight); + for (const auto& i2 : acc2) { + if (sameCollection && i1 == i2) { + continue; + } + const auto& p2 = particles2.iteratorAt(i2 - particles2.begin().globalIndex()); + if (cfgPtOrder != 0 && p2.pt() >= p1.pt()) { + continue; + } + float deltaPhi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -o2::constants::math::PIHalf); + target->getPairHist()->Fill(CorrelationContainer::kCFStepAll, p1.eta() - p2.eta(), p2.pt(), p1.pt(), multiplicity, deltaPhi, posZ, weight); + } + } + } + + // ---------------- same event + template + void fillTruthSame(TTarget& target, aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, TSelector accept) + { + if (collisions.size() != 1) { // same reco-collision requirement as processMCEfficiencyMFT + return; + } + auto collision = collisions.begin(); + if (!collision.sel8()) { + return; + } + const float multiplicity = getMultiplicity(collision); + const auto acc = collectAccepted(mcParticles, accept); + + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + fillTruthPairs(target, mcParticles, mcParticles, acc, acc, multiplicity, mcCollision.posZ(), 1.0f, true); + } + + void processMCSameMFT(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(same, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameMFT, "Process MC-truth same event MFT-acceptance correlations (charged primaries)", false); + + void processMCSameGlobal(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(sameGlobal, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameGlobal, "Process MC-truth same event global-acceptance correlations (charged primaries)", false); + + // ---------------- mixed event + PresliceUnsorted perMcCollision = aod::mccollisionlabel::mcCollisionId; + + template + void fillTruthMixed(TTarget& target, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, + McRecoCollisions const& collisions, TSelector accept) + { + // multiplicity of an MC collision = that of its single, sel8-passing reco collision; otherwise -1 (skipped by the binning) + auto getMult = [&collisions, this](auto const& mcCol) { + auto grouped = collisions.sliceBy(perMcCollision, mcCol.globalIndex()); + if (grouped.size() != 1) { + return -1.0f; + } + auto c = grouped.begin(); + if (!c.sel8()) { + return -1.0f; + } + return static_cast(getMultiplicity(c)); + }; + + using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMult)>; + BinningTypeMC binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + auto tuple = std::make_tuple(mcParticles); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, mcCollisions, tuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, particles1, mcCollision2, particles2] = *it; + const float eventWeight = 1.0f / it.currentWindowNeighbours(); + const float multiplicity = getMult(mcCollision1); + + if (it.isNewWindow()) { + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + const auto acc1 = collectAccepted(particles1, accept); + const auto acc2 = collectAccepted(particles2, accept); // once per pair, not once per trigger + fillTruthPairs(target, particles1, particles2, acc1, acc2, multiplicity, mcCollision1.posZ(), eventWeight, false); + } + } + + void processMCMixedMFT(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixed, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedMFT, "Process MC-truth mixed event MFT-acceptance correlations (charged primaries)", false); + + void processMCMixedGlobal(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixedGlobal, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedGlobal, "Process MC-truth mixed event global-acceptance correlations (charged primaries)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From e82d63e52936c6efafb22c5571ce7d32b63003d3 Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Mon, 2 Nov 2026 14:24:43 +0100 Subject: [PATCH 529/648] [PWGEM] Add EOI filter to event selection in AssociateMCInfo task (#18550) Co-authored-by: jokonig --- PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx index e89f5c765e6..7248b009e04 100644 --- a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx @@ -57,7 +57,7 @@ using namespace o2::soa; using namespace o2::aod::pwgem::photonmeson::utils::mcutil; using namespace o2::constants::physics; -using MyCollisionsMC = soa::Join; +using MyCollisionsMC = soa::Join; using TracksMC = soa::Join; using FwdTracksMC = soa::Join; using MyEMCClusters = soa::Join; @@ -95,6 +95,7 @@ struct AssociateMCInfoPhoton { Configurable maxY{"maxY", 0.9f, "max |rapidity| for BinnedGenPts table"}; Configurable doStoreAllDaughters{"doStoreAllDaughters", false, "flag to enable storing of all photon, pi0, eta, eta' and omega daughters. This will increase the dervied data size!"}; Configurable useIsSelected{"useIsSelected", true, "flag to enable the usage of isSelected from our EM event selection. Disabling this will increase the derived data size!"}; + Configurable useIsEOI{"useIsEOI", true, "flag to enable the usage of the EOI table"}; HistogramRegistry registry{"EMMCEvent"}; @@ -172,12 +173,16 @@ struct AssociateMCInfoPhoton { template bool passesCollisionGate(TCollision const& collisionIter) { + // event selection same as in createEMEventPhoton if (!collisionIter.has_mcCollision()) { return false; } if (useIsSelected.value && !collisionIter.isSelected()) { return false; } + if (useIsEOI.value && !collisionIter.isEoI()) { + return false; + } return true; } From 452c61a51e66057afeb469a69d4f30eb69f12283 Mon Sep 17 00:00:00 2001 From: RuiqiYin <25110190115@m.fudan.edu.cn> Date: Mon, 2 Nov 2026 16:04:39 +0100 Subject: [PATCH 530/648] [PWGCF,PWGHF] Split kaon DCA secondary templates into HF and LF (#18481) Co-authored-by: Fabrizio Grosa --- PWGCF/DataModel/FemtoDerived.h | 6 +- .../FemtoDream/Core/femtoDreamParticleHisto.h | 28 ++++++ .../producerCharmHadronsTrackFemtoDream.cxx | 89 ++++++++++++------- 3 files changed, 91 insertions(+), 32 deletions(-) diff --git a/PWGCF/DataModel/FemtoDerived.h b/PWGCF/DataModel/FemtoDerived.h index 0b779485244..cac62250bbe 100644 --- a/PWGCF/DataModel/FemtoDerived.h +++ b/PWGCF/DataModel/FemtoDerived.h @@ -743,6 +743,8 @@ enum ParticleOriginMCTruth { kSecondaryDaughterXistar0, //! Daughter from a Xi*^0 decay kSecondaryDaughterXistarMinus, //! Daughter from a Xi*^- decay kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated) + kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain + kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor kNOriginMCTruthTypes }; @@ -762,7 +764,9 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes "_SecondaryDaughterOmegaMinus", "_SecondaryDaughterXistar0", "_SecondaryDaughterXistarMinus", - "_Else"}; + "_Else", + "_SecondaryFromHF", + "_SecondaryFromLF"}; /// Distinguished between reconstructed and truth enum MCType { diff --git a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h index 096b9bb7b28..410e79774a5 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h +++ b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h @@ -233,6 +233,12 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterLambda").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterSigmaplus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterOmegaMinus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromHF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromLF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); @@ -245,6 +251,10 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); } else { mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); @@ -597,6 +607,16 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar(), part.dcaZ(), mult); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromHF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromLF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_Material"), part.fdExtMCParticle().motherPDG()); mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), @@ -645,6 +665,14 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar()); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar()); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx index dc7eeb02b03..0b1102163c7 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx @@ -465,8 +465,30 @@ struct HfProducerCharmHadronsTrackFemtoDream { -999., -999., -999.); } + /// Classify true kaons from the correct collision for the DCA templates. + template + static int getKaonOrigin(T const& particles, typename T::iterator const& particle) + { + using namespace o2::aod::femtodreamMCparticle; + constexpr int GenFromTransport = -1; + if (particle.getProcess() == TMCProcess::kPHInhelastic && particle.getGenStatusCode() == GenFromTransport) { + return kMaterial; + } + // HF decay products can be physical primaries. Check their ancestry first. + if (RecoDecay::getParticleOrigin(particles, particle) != RecoDecay::OriginType::None) { + return kSecondaryFromHF; + } + if (particle.isPhysicalPrimary()) { + return kPrimary; + } + if (particle.getProcess() == TMCProcess::kPDecay && particle.getGenStatusCode() == GenFromTransport && particle.has_mothers()) { + return kSecondaryFromLF; + } + return kElse; + } + template - void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype) + void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype, aod::McParticles const& mcParticles) { if (particle.has_mcParticle()) { // get corresponding MC particle and its info @@ -484,6 +506,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { // check if the collision associated with the particle is the same as the analyzed collision by checking their Ids if ((col.has_mcCollision() && (particleMc.mcCollisionId() != col.mcCollisionId())) || !col.has_mcCollision()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kWrongCollision; + } else if (std::abs(pdgCode) == kKPlus) { + particleOrigin = getKaonOrigin(mcParticles, particleMc); + if (!motherparticlesMc.empty()) { + pdgCodeMother = motherparticlesMc.front().pdgCode(); + } // check if particle is primary } else if (particleMc.isPhysicalPrimary()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary; @@ -537,7 +564,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks) + bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks, aod::McParticles const* mcParticles = nullptr) { std::vector const childIDs = {0, 0}; // these IDs are necessary to keep track of the children @@ -590,14 +617,14 @@ struct HfProducerCharmHadronsTrackFemtoDream { } if constexpr (IsMc) { - fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack); + fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack, *mcParticles); } } return fIsTrackFilled; } template - void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates) + void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -884,7 +911,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2)); } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -908,7 +935,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&) + void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -1030,7 +1057,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -1153,25 +1180,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcD0ToPiK(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateD0Mc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false); void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false); @@ -1208,25 +1235,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDstarToD0Pi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDstarMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false); void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false); @@ -1265,25 +1292,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDplusToPiKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDplusMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false); void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false); @@ -1322,25 +1349,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcLcToPKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateLcMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false); void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false); @@ -1400,11 +1427,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPi, "MC for XicToXiPiPi (DCAFitter)", false); @@ -1412,11 +1439,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiKf, "MC for XicToXiPiPi (KFParticle)", false); @@ -1424,11 +1451,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithML, "MC for XicToXiPiPi with ML (DCAFitter)", false); @@ -1436,11 +1463,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithMLKf, "MC for XicToXiPiPi with ML (KFParticle)", false); From fd3c7c2ffc033f56e00bf87563b7b7f0c4416c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 2 Nov 2026 16:44:45 +0100 Subject: [PATCH 531/648] [PWGDQ] Fix casts (#18386) --- PWGDQ/Core/CutsLibrary.cxx | 4 +- PWGDQ/Core/HistogramsLibrary.cxx | 2 +- PWGDQ/Core/MCSignalLibrary.cxx | 2 +- PWGDQ/Core/VarManager.h | 26 ++--- PWGDQ/Macros/evalMchTrackingEfficiency.cxx | 8 +- PWGDQ/TableProducer/tableMaker.cxx | 110 +++++++++--------- PWGDQ/TableProducer/tableMakerMC.cxx | 64 +++++----- .../TableProducer/tableMakerMC_withAssoc.cxx | 16 +-- .../tableMakerMuonMchTrkEfficiency.cxx | 2 +- PWGDQ/TableProducer/tableMaker_withAssoc.cxx | 2 +- PWGDQ/Tasks/DalitzSelection.cxx | 44 +++---- PWGDQ/Tasks/MIDefficiency.cxx | 2 +- PWGDQ/Tasks/TagAndProbe.cxx | 2 +- PWGDQ/Tasks/dqEfficiency.cxx | 6 +- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 2 +- PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx | 2 +- PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx | 16 +-- PWGDQ/Tasks/dqFlow.cxx | 2 +- PWGDQ/Tasks/global-muon-matcher.cxx | 2 +- PWGDQ/Tasks/muonGlobalAlignment.cxx | 2 +- PWGDQ/Tasks/qaMatching.cxx | 2 +- PWGDQ/Tasks/tableReader.cxx | 2 +- PWGDQ/Tasks/tableReader_withAssoc.h | 2 +- PWGDQ/Tasks/tableReader_withAssoc_direct.cxx | 2 +- PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx | 2 +- PWGDQ/Tasks/v0selector.cxx | 20 ++-- 26 files changed, 173 insertions(+), 173 deletions(-) diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 3527b285207..467b4888b0e 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -7695,7 +7695,7 @@ std::vector o2::aod::dqcuts::GetCutsFromJSON(const char* json) // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return cuts; } @@ -8079,7 +8079,7 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return {}; } diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 8c359f59942..d4e5e3eb2a8 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -2795,7 +2795,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch rapidjson::Document document; rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) TString str = ""; for (int i = ok.Offset() - 30; i < static_cast(ok.Offset()) + 50; i++) { if ((i >= 0) && (i < static_cast(strlen(json)))) { diff --git a/PWGDQ/Core/MCSignalLibrary.cxx b/PWGDQ/Core/MCSignalLibrary.cxx index e6eae07011b..918addd1b29 100644 --- a/PWGDQ/Core/MCSignalLibrary.cxx +++ b/PWGDQ/Core/MCSignalLibrary.cxx @@ -2119,7 +2119,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js str += json[i]; } } - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); // NOLINT(google-readability-casting) return signals; } diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 8712bc76998..7a395a9dcfb 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -1916,7 +1916,7 @@ void VarManager::FillPropagateMuon(const T& muon, const C& collision, float* val } if constexpr ((fillMap & ReducedMuonCov) > 0) { - if (muon.filteringFlags() & (uint8_t(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do + if (muon.filteringFlags() & (static_cast(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do return; } } @@ -3154,26 +3154,26 @@ void VarManager::FillTrack(T const& track, float* values) values[kOrphanTrack] = static_cast((track.flags() & o2::aod::track::OrphanTrack) > 0); // NOTE: This is NOT for Run-2 } if (fgUsedVars[kIsSPDfirst]) { - values[kIsSPDfirst] = static_cast((track.itsClusterMap() & uint8_t(1)) > 0); + values[kIsSPDfirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsSPDboth]) { - values[kIsSPDboth] = static_cast((track.itsClusterMap() & uint8_t(3)) > 0); + values[kIsSPDboth] = static_cast((track.itsClusterMap() & static_cast(3)) > 0); } if (fgUsedVars[kIsSPDany]) { - values[kIsSPDany] = static_cast((track.itsClusterMap() & uint8_t(1)) || (track.itsClusterMap() & uint8_t(2))); + values[kIsSPDany] = static_cast((track.itsClusterMap() & static_cast(1)) || (track.itsClusterMap() & static_cast(2))); } if (fgUsedVars[kITSClusterMap]) { values[kITSClusterMap] = track.itsClusterMap(); } if (fgUsedVars[kIsITSibFirst]) { - values[kIsITSibFirst] = static_cast((track.itsClusterMap() & uint8_t(1)) > 0); + values[kIsITSibFirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsITSibAny]) { - values[kIsITSibAny] = static_cast((track.itsClusterMap() & (1 << uint8_t(0))) > 0 || (track.itsClusterMap() & (1 << uint8_t(1))) > 0 || (track.itsClusterMap() & (1 << uint8_t(2))) > 0); + values[kIsITSibAny] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 || (track.itsClusterMap() & (1 << static_cast(1))) > 0 || (track.itsClusterMap() & (1 << static_cast(2))) > 0); } if (fgUsedVars[kIsITSibAll]) { - values[kIsITSibAll] = static_cast((track.itsClusterMap() & (1 << uint8_t(0))) > 0 && (track.itsClusterMap() & (1 << uint8_t(1))) > 0 && (track.itsClusterMap() & (1 << uint8_t(2))) > 0); + values[kIsITSibAll] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 && (track.itsClusterMap() & (1 << static_cast(1))) > 0 && (track.itsClusterMap() & (1 << static_cast(2))) > 0); } values[kTrackTime] = track.trackTime(); @@ -3282,17 +3282,17 @@ void VarManager::FillTrack(T const& track, float* values) // Quantities based on the dalitz selections if constexpr ((fillMap & DalitzBits) > 0) { for (int i = 0; i < 8; i++) { - values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (uint8_t(1) << i)); + values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (static_cast(1) << i)); } } // Quantities based on the V0 selections if constexpr ((fillMap & TrackV0Bits) > 0) { - values[kIsLegFromGamma] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsConversionLeg)); - values[kIsLegFromK0S] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsK0sLeg)); - values[kIsLegFromLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsLambdaLeg)); - values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsALambdaLeg)); - values[kIsLegFromOmega] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsOmegaLeg)); + values[kIsLegFromGamma] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsConversionLeg)); + values[kIsLegFromK0S] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsK0sLeg)); + values[kIsLegFromLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsLambdaLeg)); + values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsALambdaLeg)); + values[kIsLegFromOmega] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsOmegaLeg)); values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); } diff --git a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx index c59afbb40b8..931b4c168ff 100644 --- a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx +++ b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx @@ -46,8 +46,8 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons { // read ouput from the taskComputeMchEfficiency TFile* file = TFile::Open(inFile, "read"); - TDirectoryFile* dir = reinterpret_cast(file->Get("task-muon-mch-trk-efficiency")); - THnF* hHitsEtaPtPhi = reinterpret_cast(dir->Get("hHitsEtaPtPhi")); + TDirectoryFile* dir = dynamic_cast(file->Get("task-muon-mch-trk-efficiency")); + THnF* hHitsEtaPtPhi = dynamic_cast(dir->Get("hHitsEtaPtPhi")); // retrieve the eta/pt/phi axis binning configuration const int etaAx = 1, ptAx = 2, phiAx = 3; @@ -274,7 +274,7 @@ double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimi hnf->GetAxis(iAxis)->SetRangeUser(binLimits[0], binLimits[1]); // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -322,7 +322,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl } // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; diff --git a/PWGDQ/TableProducer/tableMaker.cxx b/PWGDQ/TableProducer/tableMaker.cxx index 13969e2641d..eaa2e9a2bc2 100644 --- a/PWGDQ/TableProducer/tableMaker.cxx +++ b/PWGDQ/TableProducer/tableMaker.cxx @@ -236,7 +236,7 @@ struct TableMaker { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -445,17 +445,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -471,10 +471,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -534,7 +534,7 @@ struct TableMaker { } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -547,14 +547,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -571,8 +571,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag = static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -715,7 +715,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -738,8 +738,8 @@ struct TableMaker { } } } - fwdFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + fwdFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(muon); @@ -759,14 +759,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -775,7 +775,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -917,17 +917,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -943,10 +943,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -999,7 +999,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -1012,14 +1012,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1036,8 +1036,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT2-6: V0Bits trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -1133,7 +1133,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -1152,7 +1152,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { if (static_cast(muon.isRemovable())) { @@ -1179,14 +1179,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1196,7 +1196,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -1383,10 +1383,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1398,10 +1398,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1447,7 +1447,7 @@ struct TableMaker { soa::Filtered const& tracksBarrel) { for (auto const& track : tracksBarrel) { - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1460,12 +1460,12 @@ struct TableMaker { { for (auto const& track : tracksBarrel) { // check if this event has a Dalitz candidate - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } // check if this event has a electron candidate from V0 - if (track.pidbit() == uint8_t(1)) { + if (track.pidbit() == static_cast(1)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1478,10 +1478,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1500,10 +1500,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1515,10 +1515,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1593,10 +1593,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1710,10 +1710,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } // List of process functions removed because they are not using cov matrix @@ -1752,10 +1752,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1767,10 +1767,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } diff --git a/PWGDQ/TableProducer/tableMakerMC.cxx b/PWGDQ/TableProducer/tableMakerMC.cxx index 41e26e1a374..05baa8e279d 100644 --- a/PWGDQ/TableProducer/tableMakerMC.cxx +++ b/PWGDQ/TableProducer/tableMakerMC.cxx @@ -187,7 +187,7 @@ struct TableMakerMC { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -432,10 +432,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -448,10 +448,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -578,14 +578,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -604,7 +604,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -623,7 +623,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -775,7 +775,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -823,11 +823,11 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -847,7 +847,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -978,11 +978,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1067,10 +1067,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -1083,10 +1083,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -1201,14 +1201,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -1227,7 +1227,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -1246,7 +1246,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1334,7 +1334,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -1379,12 +1379,12 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); fHistMan->FillHistClass(Form("Muons_%s", cut.GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -1404,7 +1404,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1517,11 +1517,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1576,7 +1576,7 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, (float)kNaliases + 1, -0.5, (float)kNaliases + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, static_cast(kNaliases) + 1, -0.5, static_cast(kNaliases) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); @@ -1767,10 +1767,10 @@ struct TableMakerMC { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } PROCESS_SWITCH(TableMakerMC, processFull, "Produce both barrel and muon skims", false); diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index 5d63e2e5e17..838d7e79af3 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -936,7 +936,7 @@ struct TableMakerMC { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { + if (track.pidbit() & (static_cast(1) << iv0)) { (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } @@ -995,7 +995,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (const auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; @@ -1159,8 +1159,8 @@ struct TableMakerMC { } } - trackFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + trackFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { // Check refit flag in case of realigned muons @@ -1207,7 +1207,7 @@ struct TableMakerMC { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut)->IsSelected(dqtablemakermc_helpers::varValues())) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigHistOutput.fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), dqtablemakermc_helpers::varValues()); } @@ -1248,7 +1248,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (const auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; @@ -1291,7 +1291,7 @@ struct TableMakerMC { // So all the information which pertains to collision association or MFT associations should not be taken from the skimmed data, but recomputed at analysis time. uint32_t mchIdx = -1; uint32_t mftIdx = -1; - if (muon.trackType() == uint8_t(0) || muon.trackType() == uint8_t(2)) { // MCH-MID (2) or global (0) + if (muon.trackType() == static_cast(0) || muon.trackType() == static_cast(2)) { // MCH-MID (2) or global (0) if (fFwdTrackIndexMap.find(muon.matchMCHTrackId()) != fFwdTrackIndexMap.end()) { mchIdx = fFwdTrackIndexMap[muon.matchMCHTrackId()]; } @@ -1593,7 +1593,7 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); diff --git a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx index 6d2d711491f..92452eb0c29 100644 --- a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx +++ b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx @@ -187,7 +187,7 @@ struct tableMakerMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{190, 2.3, 4.2, "|#eta|"}; const AxisSpec axisEtaRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisEtaGenRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta| Gen"}; diff --git a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx index 9c7473ee660..a49d29cb830 100644 --- a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx @@ -1397,7 +1397,7 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { + if (track.pidbit() & (static_cast(1) << iv0)) { (dynamic_cast(fStatsList->At(kStatsTracks)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } diff --git a/PWGDQ/Tasks/DalitzSelection.cxx b/PWGDQ/Tasks/DalitzSelection.cxx index e0452279ab2..ee455aae569 100644 --- a/PWGDQ/Tasks/DalitzSelection.cxx +++ b/PWGDQ/Tasks/DalitzSelection.cxx @@ -377,8 +377,8 @@ struct DalitzSelection { { fSkipEvent = true; for (const auto& track1 : tracksBarrel) { - auto filterMap = uint8_t(0); - auto filterMapProbe = uint8_t(0); + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); int fullTrackIdx = 0; if constexpr (isReassoc) { auto const& fullTrack = track1.template track_as(); @@ -417,7 +417,7 @@ struct DalitzSelection { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); ++cut, ++i) { if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { - filterMap |= (uint8_t(1) << i); + filterMap |= (static_cast(1) << i); fSkipEvent = false; } } @@ -425,7 +425,7 @@ struct DalitzSelection { i = 0; for (auto cut = fTrackCutsProbe.begin(); cut != fTrackCutsProbe.end(); ++cut, ++i) { if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { - filterMapProbe |= (uint8_t(1) << i); + filterMapProbe |= (static_cast(1) << i); } } } @@ -532,7 +532,7 @@ struct DalitzSelection { int icut = 0; auto trackCut = fTrackCuts.begin(); for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (!(twoTracksFilterMap & (uint8_t(1) << icut))) { + if (!(twoTracksFilterMap & (static_cast(1) << icut))) { continue; } if ((*pairCut).IsSelected(static_cast(VarManager::fgValues))) { @@ -554,24 +554,24 @@ struct DalitzSelection { if (!isLikeSign) { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { // if we want to remove double-counting, we fill separately the map for this event and for previous ones, in order to check if it was previously selected - fDalitzmapAmbiguity[trackIdx1] |= (uint8_t(1) << icut); + fDalitzmapAmbiguity[trackIdx1] |= (static_cast(1) << icut); } else { - fDalitzmap[trackIdx1] |= (uint8_t(1) << icut); + fDalitzmap[trackIdx1] |= (static_cast(1) << icut); } if (fIsTagAndProbe) { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { - fDalitzmapProbeAmbiguity[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapProbeAmbiguity[trackIdx2] |= (static_cast(1) << icut); } else { - fDalitzmapProbe[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapProbe[trackIdx2] |= (static_cast(1) << icut); } if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(Form("Pair_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } } else { if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { - fDalitzmapAmbiguity[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapAmbiguity[trackIdx2] |= (static_cast(1) << icut); } else { - fDalitzmap[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmap[trackIdx2] |= (static_cast(1) << icut); } if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(Form("Pair_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); @@ -581,8 +581,8 @@ struct DalitzSelection { if (fConfigOptions.fQA && !isPairAlreadySelected) { fHistMan->FillHistClass(fIsTagAndProbe ? Form("PairLS_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("PairLS_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigBitsToPropagate == 2) { - fDalitzmapLS[trackIdx1] |= (uint8_t(1) << icut); - fDalitzmapLS[trackIdx2] |= (uint8_t(1) << icut); + fDalitzmapLS[trackIdx1] |= (static_cast(1) << icut); + fDalitzmapLS[trackIdx2] |= (static_cast(1) << icut); } } } // end if like-sign @@ -594,8 +594,8 @@ struct DalitzSelection { // Fill Hists if (fConfigOptions.fQA && !fSkipEvent) { for (const auto& track : tracks1) { - auto filterMap = uint8_t(0); - auto filterMapProbe = uint8_t(0); + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); if constexpr (isReassoc) { auto const& fullTrack = track.template track_as(); filterMap = fDalitzmap[fullTrack.globalIndex()]; // cppcheck-suppress redundantInitialization @@ -657,12 +657,12 @@ struct DalitzSelection { int icut = 0; auto trackCut = fTrackCuts.begin(); for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (filterMap & (uint8_t(1) << icut)) { - static_cast(fStatsList->At(0))->Fill(fIsTagAndProbe ? 2 * icut + 1 : icut + 1); + if (filterMap & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(fIsTagAndProbe ? 2 * icut + 1 : icut + 1); fHistMan->FillHistClass(fIsTagAndProbe ? Form("TrackBarrelTag_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("TrackBarrel_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } - if (filterMapProbe & (uint8_t(1) << icut)) { - static_cast(fStatsList->At(0))->Fill(2 * icut + 2); + if (filterMapProbe & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(2 * icut + 2); fHistMan->FillHistClass(Form("TrackBarrelProbe_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } } @@ -821,7 +821,7 @@ struct DalitzSelection { bool isEventSelected = fEventCut->IsSelected(static_cast(VarManager::fgValues)); if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); @@ -896,7 +896,7 @@ struct DalitzSelection { if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); @@ -971,7 +971,7 @@ struct DalitzSelection { if (isEventSelected) { - static_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); diff --git a/PWGDQ/Tasks/MIDefficiency.cxx b/PWGDQ/Tasks/MIDefficiency.cxx index 26bca045c2b..3e8d103e4f5 100644 --- a/PWGDQ/Tasks/MIDefficiency.cxx +++ b/PWGDQ/Tasks/MIDefficiency.cxx @@ -78,7 +78,7 @@ struct midEfficiency { o2::mid::Mapping mapping; // Filter only for MCH-MID matched tracks - Filter muonTrackType = aod::fwdtrack::trackType == uint8_t(3); + Filter muonTrackType = aod::fwdtrack::trackType == static_cast(3); void init(o2::framework::InitContext const& /*ic*/) { diff --git a/PWGDQ/Tasks/TagAndProbe.cxx b/PWGDQ/Tasks/TagAndProbe.cxx index 8ee82257af1..9fec1a9907a 100644 --- a/PWGDQ/Tasks/TagAndProbe.cxx +++ b/PWGDQ/Tasks/TagAndProbe.cxx @@ -213,7 +213,7 @@ struct AnalysisTagAndProbe { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms // dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill diff --git a/PWGDQ/Tasks/dqEfficiency.cxx b/PWGDQ/Tasks/dqEfficiency.cxx index dca19a23c13..0495204234c 100644 --- a/PWGDQ/Tasks/dqEfficiency.cxx +++ b/PWGDQ/Tasks/dqEfficiency.cxx @@ -310,7 +310,7 @@ struct AnalysisTrackSelection { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { + if (filterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts @@ -483,7 +483,7 @@ struct AnalysisMuonSelection { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { + if (filterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts @@ -876,7 +876,7 @@ struct AnalysisSameEventPairing { // Loop over all fulfilled cuts and fill pair histograms for (unsigned int icut = 0; icut < ncuts; icut++) { - if (twoTrackFilter & (uint8_t(1) << icut)) { + if (twoTrackFilter & (static_cast(1) << icut)) { if (t1.sign() * t2.sign() < 0) { fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index b9fead0a8fd..9b759f453e9 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -2257,7 +2257,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index 84166d64888..82d99d1ed75 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -1599,7 +1599,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx index 1396420b61a..ed8ea9265bb 100644 --- a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx +++ b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx @@ -238,7 +238,7 @@ struct AnalysisEnergyCorrelator { if (trackCutsJSON != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(trackCutsJSON.Data()); for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); fTrackCutNames.push_back(t->GetName()); trackCutStr += Form(",%s", t->GetName()); } @@ -311,7 +311,7 @@ struct AnalysisEnergyCorrelator { if (hadronCutsJSON != "") { std::vector addHadronCuts = dqcuts::GetCutsFromJSON(hadronCutsJSON.Data()); for (auto& t : addHadronCuts) { - fHadronCuts.push_back(reinterpret_cast(t)); + fHadronCuts.push_back(dynamic_cast(t)); fHadronCutNames.push_back(t->GetName()); hadronCutStr += Form(",%s", t->GetName()); } @@ -501,12 +501,12 @@ struct AnalysisEnergyCorrelator { if (!listAccs) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } - hEfficiency_dilepton = static_cast(listAccs->FindObject("hEfficiency_dilepton")); - hEfficiency_hadron = static_cast(listAccs->FindObject("hEfficiency_hadron")); - hAcceptance_rec = static_cast(listAccs->FindObject("hAcceptance_rec")); - hAcceptance_gen = static_cast(listAccs->FindObject("hAcceptance_gen")); - hMasswindow = static_cast(listAccs->FindObject("hMasswindow")); - hReweighthadron = static_cast(listAccs->FindObject("hReweighthadron")); + hEfficiency_dilepton = dynamic_cast(listAccs->FindObject("hEfficiency_dilepton")); + hEfficiency_hadron = dynamic_cast(listAccs->FindObject("hEfficiency_hadron")); + hAcceptance_rec = dynamic_cast(listAccs->FindObject("hAcceptance_rec")); + hAcceptance_gen = dynamic_cast(listAccs->FindObject("hAcceptance_gen")); + hMasswindow = dynamic_cast(listAccs->FindObject("hMasswindow")); + hReweighthadron = dynamic_cast(listAccs->FindObject("hReweighthadron")); if (!hAcceptance_rec || !hAcceptance_gen || !hEfficiency_dilepton || !hEfficiency_hadron || !hMasswindow || !hReweighthadron) { LOG(fatal) << "Problem getting histograms from the TList object with efficiencies!"; } diff --git a/PWGDQ/Tasks/dqFlow.cxx b/PWGDQ/Tasks/dqFlow.cxx index 323ec4ade4a..31865fcd0d4 100644 --- a/PWGDQ/Tasks/dqFlow.cxx +++ b/PWGDQ/Tasks/dqFlow.cxx @@ -142,7 +142,7 @@ struct DQEventQvector { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100.1}, "multiplicity / centrality axis for histograms"}; // Define the filter for barrel tracks and forward tracks - Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true); + Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true)); Filter fwdFilter = (aod::fwdtrack::eta < -2.45f) && (aod::fwdtrack::eta > -3.6f); // Histograms used for optionnal efficiency and non-uniform acceptance corrections diff --git a/PWGDQ/Tasks/global-muon-matcher.cxx b/PWGDQ/Tasks/global-muon-matcher.cxx index 98863864b8e..4125dabbd9c 100644 --- a/PWGDQ/Tasks/global-muon-matcher.cxx +++ b/PWGDQ/Tasks/global-muon-matcher.cxx @@ -227,7 +227,7 @@ struct GlobalMuonMatching { struct : ConfigurableGroup { Configurable cfgMixingDepth{"cfgMixingDepth", -1, "Maximum number of mixed candidate groups per MCH track (-1: no limit)"}; Configurable cfgMinDeltaBc{"cfgMinDeltaBc", 3564, "Minimum DeltaBc between mixed collisions"}; - Configurable cfgMaxDeltaPhi{"cfgMaxDeltaPhi", static_cast(o2::constants::math::PI / 10), "Maximum DelptaPhi between mixed MCH tracks (rad)"}; + Configurable cfgMaxDeltaPhi{"cfgMaxDeltaPhi", o2::constants::math::PI / 10.f, "Maximum DelptaPhi between mixed MCH tracks (rad)"}; Configurable cfgMaxDeltaR{"cfgMaxDeltaR", 10.f, "Maximum DeltaR between mixed MCH tracks"}; Configurable cfgMaxDeltaAttempts{"cfgMaxDeltaAttempts", 0.1f, "Maximum relative difference in match attempts"}; Configurable cfgMaxDeltaZ{"cfgMaxDeltaZ", 1.f, "Maximum deltaZ between mixed collisions"}; diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index c5a9a4656e5..8337c5f0c25 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -657,7 +657,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc .z = it->value["z"].GetDouble()}; } } else { - LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; + LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; // NOLINT(google-readability-casting) } // two-prong track fitter setup diff --git a/PWGDQ/Tasks/qaMatching.cxx b/PWGDQ/Tasks/qaMatching.cxx index 768cacc25da..104b54741d4 100644 --- a/PWGDQ/Tasks/qaMatching.cxx +++ b/PWGDQ/Tasks/qaMatching.cxx @@ -1282,7 +1282,7 @@ struct QaMatching { } int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); AxisSpec tracksMultiplicityAxis = {cfgMftTrackMultiplicityMax, 0, static_cast(cfgMftTrackMultiplicityMax), "tracks multiplicity"}; diff --git a/PWGDQ/Tasks/tableReader.cxx b/PWGDQ/Tasks/tableReader.cxx index 4d385593a01..dc8095e31a7 100644 --- a/PWGDQ/Tasks/tableReader.cxx +++ b/PWGDQ/Tasks/tableReader.cxx @@ -1308,7 +1308,7 @@ struct AnalysisSameEventPairing { // ccdb->setLocalObjectValidityChecking(); // ccdb->setCreatedNotAfter(nolaterthan.value); - VarManager::SetCollisionSystem((TString)fCollisionSystem, fCenterMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fCollisionSystem), fCenterMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index 4b1ab34b9b9..354e9fe815d 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -1918,7 +1918,7 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy if (fConfigCuts.event.value && fSplitCheckEnabled) { DefineHistograms(fHistMan, "Event_AfterSplitVeto", fConfigAddEventHistogram.value.data()); // define event histograms after removing collision splitting candidates o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONEventHistograms.value.c_str()); diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index 2803c1069bc..47a21cedcc9 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -1551,7 +1551,7 @@ struct AnalysisSameEventPairing { fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON diff --git a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx index 999b46bd148..806adcf79e8 100644 --- a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx +++ b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx @@ -110,7 +110,7 @@ struct taskMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisPt{vbins, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisPhi{nPhiBins, -3.14, 3.14, "#varphi"}; diff --git a/PWGDQ/Tasks/v0selector.cxx b/PWGDQ/Tasks/v0selector.cxx index bc27b0eb06e..807d3295f55 100644 --- a/PWGDQ/Tasks/v0selector.cxx +++ b/PWGDQ/Tasks/v0selector.cxx @@ -387,8 +387,8 @@ struct v0selector { registry.fill(HIST("hV0Psi"), psipair, mGamma); } if (mGamma < v0max_mee && std::abs(V0.posTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(V0.negTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(psipair) < maxpsipair) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kGamma); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kGamma); + pidmap[V0.posTrackId()] |= (static_cast(1) << kGamma); + pidmap[V0.negTrackId()] |= (static_cast(1) << kGamma); storeV0AddID(V0.globalIndex(), kGamma); if (fillhisto) { registry.fill(HIST("hGammaRxy"), V0.x(), V0.y()); @@ -402,8 +402,8 @@ struct v0selector { registry.fill(HIST("hMassK0SPhi"), V0.phi(), mK0S); } if ((0.48 < mK0S && mK0S < 0.51) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kK0S); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kK0S); + pidmap[V0.posTrackId()] |= (static_cast(1) << kK0S); + pidmap[V0.negTrackId()] |= (static_cast(1) << kK0S); storeV0AddID(V0.globalIndex(), kK0S); } } else if (v0id == kLambda) { // L->p + pi- @@ -411,8 +411,8 @@ struct v0selector { registry.fill(HIST("hMassLambda"), V0radius, mLambda); } if (v0id == kLambda && (1.110 < mLambda && mLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kLambda); storeV0AddID(V0.globalIndex(), kLambda); } } else if (v0id == kAntiLambda) { // Lbar -> pbar + pi+ @@ -420,8 +420,8 @@ struct v0selector { registry.fill(HIST("hMassAntiLambda"), V0radius, mAntiLambda); } if ((1.110 < mAntiLambda && mAntiLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kAntiLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kAntiLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kAntiLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kAntiLambda); storeV0AddID(V0.globalIndex(), kAntiLambda); } } @@ -543,7 +543,7 @@ struct v0selector { registry.fill(HIST("hMassOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kOmega); storeCascAddID(casc.globalIndex(), kOmega); } } else if (cascid == kAntiOmega && v0id == kAntiLambda) { @@ -551,7 +551,7 @@ struct v0selector { registry.fill(HIST("hMassAntiOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kAntiOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kAntiOmega); storeCascAddID(casc.globalIndex(), kAntiOmega); } } From 782f7a6d5344d0c9f26fa263e5c66a4f30df83c6 Mon Sep 17 00:00:00 2001 From: alcaliva <32872606+alcaliva@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:02:48 +0100 Subject: [PATCH 532/648] [PWGLF] Fix proton double counting in coalescence and add jet/UE spectrum reweighting (#18524) --- PWGLF/Tasks/Nuspex/antinucleiInJets.cxx | 120 +++++++++++++++++++++--- 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx index e4f61645740..ec1b776da41 100644 --- a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx +++ b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx @@ -661,6 +661,31 @@ struct AntinucleiInJets { return (particle.isPhysicalPrimary() || fromHF); } + // Check if particle is from heavy-flavor hadron decay + bool isFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) + { + // Keep only pi, K, p, d, e, mu + int pdg = std::abs(particle.pdgCode()); + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == o2::constants::physics::Pdg::kDeuteron || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + return false; + + // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes + static constexpr int CharmQuark = 4; + static constexpr int BottomQuark = 5; + static constexpr int Hundreds = 100; + static constexpr int Thousands = 1000; + + // Check if particle is from heavy-flavor decay + bool fromHF = false; + if (particle.has_mothers()) { + auto mother = mcParticles.iteratorAt(particle.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + fromHF = (motherPdg / Hundreds == CharmQuark || motherPdg / Hundreds == BottomQuark || motherPdg / Thousands == CharmQuark || motherPdg / Thousands == BottomQuark); + } + + return fromHF; + } + // Evaluate proton–neutron coalescence for deuteron formation template bool passDeuteronCoalescence(const ReducedPart& p, const ReducedPart& n, double p0, TRandom3& random) @@ -2932,6 +2957,7 @@ struct AntinucleiInJets { // Define per-event particle containers std::vector chargedParticles; std::vector protons; + std::vector protonsFromHF; std::vector neutrons; std::vector fjParticles; @@ -2945,6 +2971,7 @@ struct AntinucleiInJets { // Clear containers at the start of the event loop chargedParticles.clear(); protons.clear(); + protonsFromHF.clear(); neutrons.clear(); fjParticles.clear(); @@ -2972,6 +2999,11 @@ struct AntinucleiInJets { protons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); } + // Store Protons from HF decays + if (isFromHF(particle, mcParticles) && std::abs(particle.pdgCode()) == PDG_t::kProton) { + protonsFromHF.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } + // Store Neutrons if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kNeutron) { neutrons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); @@ -2981,17 +3013,12 @@ struct AntinucleiInJets { if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) continue; - // Select particles within acceptance - if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle) + // Select particles within acceptance and skip protons (will be added after coalescence) + if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle || std::abs(particle.pdgCode()) == PDG_t::kProton) continue; chargedParticles.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); } - // Reject empty events - if (chargedParticles.empty()) - continue; - registryMC.fill(HIST("genEventsCoalescence"), 2.5); - // Build deuterons for (int ip = 0; ip < static_cast(protons.size()); ip++) { auto& proton = protons[ip]; @@ -3005,6 +3032,10 @@ struct AntinucleiInJets { if (passDeuteronCoalescence(proton, neutron, coalescenceMomentum, mRand)) { + // If proton and neutron are used by coalescence they are removed + neutron.used = true; + proton.used = true; + int sign = (proton.pdgCode > 0) ? +1 : -1; int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; @@ -3014,18 +3045,40 @@ struct AntinucleiInJets { double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); if (pd.Eta() < minEta || pd.Eta() > maxEta || pd.Pt() < MinPtParticle) - continue; + break; // Store Deuteron chargedParticles.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, proton.mcIndex, false}); - - neutron.used = true; - proton.used = true; break; } } } + // Add unused protons + for (int ip = 0; ip < static_cast(protons.size()); ip++) { + auto& proton = protons[ip]; + if (proton.used) + continue; + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) + continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); + } + + // Add protons from HF decays + for (int ip = 0; ip < static_cast(protonsFromHF.size()); ip++) { + auto& proton = protonsFromHF[ip]; + if (proton.used) + continue; + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) + continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); + } + + // Reject empty events + if (chargedParticles.empty()) + continue; + registryMC.fill(HIST("genEventsCoalescence"), 2.5); + for (const auto& part : chargedParticles) { if (part.used) continue; @@ -3086,10 +3139,29 @@ struct AntinucleiInJets { // Analyze jet constituents std::vector jetConstituents = jet.constituents(); for (const auto& particle : jetConstituents) { + + // Calculate weight for antiprotons + double wAntipJet = 1.0; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + wAntipJet = antiprotonsInsideJets->GetBinContent(ipt); + } + } + + // Calculate weight for antideuterons + double wAntidJet = 1.0; + if (applyReweighting) { + if (0.5 * particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && 0.5 * particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(0.5 * particle.pt()); + wAntidJet = antiprotonsInsideJets->GetBinContent(ipt) * antiprotonsInsideJets->GetBinContent(ipt); + } + } + if (particle.user_index() == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_jet"), particle.pt()); + registryMC.fill(HIST("antiproton_coal_jet"), particle.pt(), wAntipJet); if (particle.user_index() == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt()); + registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt(), wAntidJet); } // Set up two perpendicular cone axes for underlying event estimation @@ -3119,11 +3191,29 @@ struct AntinucleiInJets { if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; + // Calculate weight for antiprotons + double wAntipUE = 1.0; + if (applyReweighting) { + if (chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(chParticle.pt()); + wAntipUE = antiprotonsPerpCone->GetBinContent(ipt); + } + } + + // Calculate weight for antideuterons + double wAntidUE = 1.0; + if (applyReweighting) { + if (0.5 * chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && 0.5 * chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(0.5 * chParticle.pt()); + wAntidUE = antiprotonsPerpCone->GetBinContent(ipt) * antiprotonsPerpCone->GetBinContent(ipt); + } + } + // Fill histograms for UE if (chParticle.pdgCode == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt(), wAntipUE); if (chParticle.pdgCode == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt(), wAntidUE); } } if (isAtLeastOneJetSelected) { From 0204e49715d7c6992977ccf7686265d4196cb59a Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Mon, 2 Nov 2026 17:09:41 +0100 Subject: [PATCH 533/648] [PWGEM] Add QA histograms to Dalitz Skimmer (#18552) Co-authored-by: jokonig --- .../skimmerPrimaryElectronFromDalitzEE.h | 220 +++++++++++++----- 1 file changed, 161 insertions(+), 59 deletions(-) diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h index 559da2e0d8d..148f1667dd5 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -48,6 +48,8 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include #include #include @@ -121,6 +123,33 @@ struct skimmerPrimaryElectronFromDalitzEE { EpEmPairsAndPhoton = 3 }; + enum RejectReason { + kAllTracks = 0, + kPassed, + kNoMcParticle, + kNotTrueElectron, + kTOFRequired, + kNoITS, + kITSChi2, + kITSNCls, + kITSNClsInnerBarrel, + kNoTPC, + kTPCChi2, + kTPCNClsFound, + kTPCCrossedRows, + kTPCCrossedRowsOverFindable, + kTPCSharedFraction, + kPropagation, + kDCAxyz, + kDCA3D, + kPtEta, + kITSClusterSize, + kITSsaPt, + kNotElectron, + kNRejectReasons + }; + static constexpr std::array RejectReasonLabels = {"all tracks", "accepted tracks", "no MC particle", "not true electron", "TOF required", "no ITS", "ITS chi2", "ITS Ncls", "ITS Ncls IB", "no TPC", "TPC chi2", "TPC Ncls found", "TPC crossed rows", "TPC Ncr/Nf", "TPC shared frac", "propagation", "DCAxy/z", "DCA 3D", "pt/eta", "ITS cluster size", "ITSsa pt", "not electron"}; + // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; @@ -172,9 +201,11 @@ struct skimmerPrimaryElectronFromDalitzEE { Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; + Configurable fillHistogramsBeforeSelection{"fillHistogramsBeforeSelection", false, "Flag to fill track histograms before track selections (only for fillingMode 1)"}; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; static constexpr std::array DileptonSigns = {"uls/", "lspp/", "lsmm/"}; + static constexpr std::array TrackDirs = {"Track/", "TrackTrueElec/", "TrackNoSel/", "TrackNoSelTrueElec/"}; int mRunNumber = 0; float dBz = 0.; @@ -237,6 +268,24 @@ struct skimmerPrimaryElectronFromDalitzEE { fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTrackRejectReason", "track rejection reason vs. p_{T};;p_{T} (GeV/c)", kTH2F, {{static_cast(kNRejectReasons), -0.5, static_cast(kNRejectReasons) - 0.5}, {200, 0, 20}}, false); + auto hRejectReason = fRegistry.get(HIST("Track/hTrackRejectReason")); + for (int i = 0; i < kNRejectReasons; i++) { + hRejectReason->GetXaxis()->SetBinLabel(i + 1, RejectReasonLabels[i]); + } + + if (doprocessMC) { + LOG(info) << "adding TrackTrueElec folder"; + fRegistry.addClone("Track/", "TrackTrueElec/"); + } + + if (fillHistogramsBeforeSelection) { + fRegistry.addClone("Track/", "TrackNoSel/"); + if (doprocessMC) { + fRegistry.addClone("Track/", "TrackNoSelTrueElec/"); + } + } + // pair fRegistry.add("Pair/uls/hTrackMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); fRegistry.add("Pair/uls/hCheckEMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); @@ -354,63 +403,72 @@ struct skimmerPrimaryElectronFromDalitzEE { } template - bool checkTrack(TCollision const& collision, TTrack const& track) + bool checkTrack(TCollision const& collision, TTrack const& track, int* rejectReason = nullptr) { + // Lambda function: Sets the reject reason to the correct value and returns false. + // Used for every return statement in the checkTrack function where false is returned + auto reject = [rejectReason](RejectReason reason) { + if (rejectReason != nullptr) { + *rejectReason = reason; + } + return false; + }; + if constexpr (isMC) { if (!track.has_mcParticle()) { - return false; + return reject(kNoMcParticle); } if (storeOnlyTrueElectronMC) { const auto& mcParticle = track.template mcParticle_as(); - if (std::abs(mcParticle.pdgCode()) != 11) { - return false; + if (std::abs(mcParticle.pdgCode()) != PDG_t::kElectron) { + return reject(kNotTrueElectron); } } } float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; if (requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl)) { - return false; + return reject(kTOFRequired); } if (!track.hasITS()) { - return false; + return reject(kNoITS); } if (track.itsChi2NCl() < minchi2its || maxchi2its < track.itsChi2NCl()) { - return false; + return reject(kITSChi2); } if (track.itsNCls() < min_ncluster_its) { - return false; + return reject(kITSNCls); } if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { - return false; + return reject(kITSNClsInnerBarrel); } if (!includeITSsa && !track.hasTPC()) { - return false; + return reject(kNoTPC); } if (track.hasTPC()) { if (track.tpcChi2NCl() < minchi2tpc || maxchi2tpc < track.tpcChi2NCl()) { - return false; + return reject(kTPCChi2); } if (track.tpcNClsFound() < min_ncluster_tpc) { - return false; + return reject(kTPCNClsFound); } if (track.tpcNClsCrossedRows() < mincrossedrows) { - return false; + return reject(kTPCCrossedRows); } if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { - return false; + return reject(kTPCCrossedRowsOverFindable); } if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { - return false; + return reject(kTPCSharedFraction); } } @@ -422,13 +480,13 @@ struct skimmerPrimaryElectronFromDalitzEE { mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); if (!isPropOK) { - return false; + return reject(kPropagation); } float dcaXY = mDcaInfoCov.getY(); float dcaZ = mDcaInfoCov.getZ(); if (std::fabs(dcaXY) > dca_xy_max || std::fabs(dcaZ) > dca_z_max) { - return false; + return reject(kDCAxyz); } float dca3D = 999.f; @@ -440,11 +498,11 @@ struct skimmerPrimaryElectronFromDalitzEE { dca3D = std::sqrt(std::fabs(chi2) / 2.); } if (dca3D > dca_3d_sigma_max) { - return false; + return reject(kDCA3D); } if (trackParCov.getPt() < minpt || std::fabs(trackParCov.getEta()) > maxeta) { - return false; + return reject(kPtEta); } int total_cluster_size = 0, nl = 0; @@ -457,11 +515,11 @@ struct skimmerPrimaryElectronFromDalitzEE { } if (maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { - return false; + return reject(kITSClusterSize); } if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { - return false; + return reject(kITSsaPt); } // if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { @@ -528,11 +586,24 @@ struct skimmerPrimaryElectronFromDalitzEE { { for (const auto& track : tracks) { - if (!checkTrack(collision, track)) { - continue; + + if (fillHistogramsBeforeSelection) { + fillTrackHistograms(track); } - if (!isElectron(collision, track)) { + fRegistry.fill(HIST("Track/hTrackRejectReason"), kAllTracks, track.pt()); + int rejectReason = kPassed; + if (checkTrack(collision, track, &rejectReason) && !isElectron(collision, track)) { + rejectReason = kNotElectron; + } + fRegistry.fill(HIST("Track/hTrackRejectReason"), rejectReason, track.pt()); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), kAllTracks, track.pt()); + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), rejectReason, track.pt()); + } + } + if (rejectReason != kPassed) { continue; } @@ -544,7 +615,16 @@ struct skimmerPrimaryElectronFromDalitzEE { template void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { - if constexpr (pairtype == 0) { // ULS + if constexpr (pairtype == 0) { // ULS + if (fillHistogramsBeforeSelection) { // we cannot fill the track histograms in the pair loop as they would get filled multiple times. Hence we loop over all tracks here + for (const auto& track : tracks1) { + fillTrackHistograms(track); + } + for (const auto& track : tracks2) { + fillTrackHistograms(track); + } + } + for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { @@ -621,44 +701,66 @@ struct skimmerPrimaryElectronFromDalitzEE { } } - template + template + bool isTruePrimaryElectron(TTrack const& track) + { + if (!track.has_mcParticle()) { + return false; + } + const auto mcParticle = track.template mcParticle_as(); + return std::abs(mcParticle.pdgCode()) == PDG_t::kElectron && mcParticle.isPhysicalPrimary(); // o2-linter: disable=pdg/explicit-code (electron) + } + + template void fillTrackHistograms(TTrack const& track) + { + constexpr int kDirAllTracks = beforeSelection ? 2 : 0; + fillTrackHistogramsImpl(track); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fillTrackHistogramsImpl(track); + } + } + } + + template + void fillTrackHistogramsImpl(TTrack const& track) { float mcTunedTPCSignal = 0.f; if constexpr (isMC) { mcTunedTPCSignal = track.mcTunedTPCSignal(); } - fRegistry.fill(HIST("Track/hPt"), track.pt()); - fRegistry.fill(HIST("Track/hEtaPhi"), track.phi(), track.eta()); - fRegistry.fill(HIST("Track/hQoverPt"), track.sign() / track.pt()); - fRegistry.fill(HIST("Track/hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); - fRegistry.fill(HIST("Track/hDCAxyz"), track.dcaXY(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxy_Pt"), track.pt(), track.dcaXY()); - fRegistry.fill(HIST("Track/hDCAz_Pt"), track.pt(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); - fRegistry.fill(HIST("Track/hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um - fRegistry.fill(HIST("Track/hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um - - fRegistry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); - fRegistry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); - fRegistry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); - fRegistry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); - fRegistry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); - fRegistry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); - fRegistry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); - fRegistry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); - - fRegistry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); - fRegistry.fill(HIST("Track/hTOFbeta"), track.p(), track.beta()); - fRegistry.fill(HIST("Track/hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); - fRegistry.fill(HIST("Track/hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); - - fRegistry.fill(HIST("Track/hNclsITS"), track.itsNCls()); - fRegistry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); - fRegistry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hPt"), track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hEtaPhi"), track.phi(), track.eta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hQoverPt"), track.sign() / track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyz"), track.dcaXY(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxy_Pt"), track.pt(), track.dcaXY()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAz_Pt"), track.pt(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsTPC"), track.tpcNClsFound()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TOF"), track.tofChi2()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFbeta"), track.p(), track.beta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsITS"), track.itsNCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2ITS"), track.itsChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hITSClusterMap"), track.itsClusterMap()); int totalClusterSize = 0, nl = 0; for (unsigned int layer = 0; layer < 7; layer++) { // o2-linter: disable=magic-number (number of ITS layers) @@ -686,9 +788,9 @@ struct skimmerPrimaryElectronFromDalitzEE { } totalClusterSizeOb += clusterSizePerLayer; } - fRegistry.fill(HIST("Track/hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); } template From 977bf457992f054d9898ee71f1c361ed09e3db6a Mon Sep 17 00:00:00 2001 From: amatyja Date: Mon, 2 Nov 2026 17:13:06 +0100 Subject: [PATCH 534/648] [PWGUD] Mimic data selection in MC (#18555) --- PWGUD/DataModel/TauThreeProngEventTables.h | 2 +- .../tauThreeProngEventTableProducer.cxx | 110 ++++++++++++++++-- 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/PWGUD/DataModel/TauThreeProngEventTables.h b/PWGUD/DataModel/TauThreeProngEventTables.h index ca00bd0be6f..95c8fff2a69 100644 --- a/PWGUD/DataModel/TauThreeProngEventTables.h +++ b/PWGUD/DataModel/TauThreeProngEventTables.h @@ -138,7 +138,7 @@ DECLARE_SOA_COLUMN(True6DaugPx, true6DaugPx, float[6]); DECLARE_SOA_COLUMN(True6DaugPy, true6DaugPy, float[6]); DECLARE_SOA_COLUMN(True6DaugPz, true6DaugPz, float[6]); DECLARE_SOA_COLUMN(True6DaugPdgCode, true6DaugPdgCode, int[6]); -DECLARE_SOA_COLUMN(Problem, problem, int8_t); +DECLARE_SOA_COLUMN(Problem, problem, uint8_t); DECLARE_SOA_COLUMN(IsRec, isRec, int8_t); } // namespace tautree diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index 0a5f76df134..510e229b397 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -67,7 +67,10 @@ enum MyRecoProblem { TOO_MANY_DAUGHTERS = 2, // more than 6 daughters from 2 taus TWO_TRACKS = 3, // more than 1 associated track to MC particle (tau daughter) NO_TRACK = 4, // No associated track to MC particle (tau daughter) - MIXED_TRACKS = 5 // 4 (or 6) tracks reconstructed but from 2 collisions + MIXED_TRACKS = 5, // 4 (or 6) tracks reconstructed but from 2 collisions + BAD_GAP = 6, // rapidity gap out of range 0-2 + GAP_NE_2 = 7, // rapidity gap != 2 + FIT_VETO = 8 // FIT not empty (with thresholds) }; enum MyParticle { @@ -185,7 +188,7 @@ struct TauThreeProngEventTableProducer { // Configurable maxNsigmaPrVetocut{"maxNsigmaPrVetocut", 3., "max Nsigma for Proton veto cut"}; // Configurable maxNsigmaKaVetocut{"maxNsigmaKaVetocut", 3., "max Nsigma for Kaon veto cut"}; // Configurable minPtEtrkcut{"minPtEtrkcut", 0.25, "min Pt for El track cut"}; - Configurable minTrkPtcut{"minTrkPtcut", 0.1, "min Pt for each charged track cut, default=100MeV/c"}; + Configurable minTrkPtcut{"minTrkPtcut", 0.10, "min Pt for each charged track cut, default=100MeV/c"}; Configurable nPVtrackscut{"nPVtrackscut", 4, "number of PV contributors, default=4"}; Configurable mFITvetoFlag{"mFITvetoFlag", true, "To apply FIT veto"}; Configurable mFITvetoWindow{"mFITvetoWindow", 2, "FIT veto window"}; @@ -219,7 +222,7 @@ struct TauThreeProngEventTableProducer { ////using FullSGUDCollisions = soa::Join; ////using FullSGUDCollision = FullSGUDCollisions::iterator; using FullMCUDTracks = soa::Join; - using FullMCSGUDCollisions = soa::Join; + using FullMCSGUDCollisions = soa::Join; // using FullMCSGUDCollision = FullMCSGUDCollisions::iterator; // init @@ -1136,6 +1139,11 @@ struct TauThreeProngEventTableProducer { if (verbose) LOGF(info, " UDMcCollision size %d, Collisions size %d, UDtracks %d, UDMcParticles %d", mcCollisions.size(), collisions.size(), tracks.size(), mcParticles.size()); + // temporary variables needed for reconstruction + int gapSide = -10; + int truegapSide = -10; + bool isFITveto = false; + // temporary variables // float tmpRapidity = -999.; float trueTauRapidity[2] = {-999., -999.}; @@ -1388,7 +1396,9 @@ struct TauThreeProngEventTableProducer { float trueDaugZ[6] = {-998., -998., -998., -998., -998., -998.}; int trueDaugPdgCode[6] = {-999, -999, -999, -999, -999, -999}; - MyRecoProblem problem = NO_PROBLEM; + int8_t problemBits[8] = {-99, -99, -99, -99, -99, -99, -99, -99}; + uint8_t problemBitsFlag = 0; + // MyRecoProblem problem = NO_PROBLEM; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_PROBLEM); // tau tau event type @@ -1445,7 +1455,8 @@ struct TauThreeProngEventTableProducer { // LOGF(info, "Truth collision has more than 1 reco collision. Skipping this event."); // histos.get(HIST("Truth/hTroubles"))->Fill(1); // problem = true; - problem = MANY_RECO; + problemBits[MANY_RECO - 1] = true; + // problem = MANY_RECO; registrySkim.get(HIST("skim/problemMC"))->Fill(MANY_RECO); // continue; } else { @@ -1453,12 +1464,77 @@ struct TauThreeProngEventTableProducer { LOGF(info, "---- Truth collision has 1 reco collision. Record event."); } + // To fill bcFlags and problemFlags + const int offset = 1; + // grab reco-level collision for (const auto& collFromMcColl : collFromMcColls) { if (verbose) { LOGF(info, "---- ###### Collision %d #########", collFromMcColl.index()); LOGF(info, "---- Coll GID %d, Vertex (%f,%f,%f)", collFromMcColl.globalIndex(), collFromMcColl.posX(), collFromMcColl.posY(), collFromMcColl.posZ()); } + + // + // part neede to mimic reconstruction selection criteria + // + gapSide = collFromMcColl.gapSide(); + // registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) { // if (gapSide < 0 || gapSide > 2) + problemBits[BAD_GAP - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + truegapSide = sgSelector.trueGap(collFromMcColl, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + // registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) { + problemBits[GAP_NE_2 - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // n PV contributors + // if (PVContributors.size() != nPVtrackscut) // 4 or 6 + // return; + // if (nEtaIn15 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(4., 1.); + // + // if (npT100 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(5., 1.); + // + // registrySkim.get(HIST("skim/nTof"))->Fill(nTofTrk); + // if (nTofTrk < nTofTrkMinCut) + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(6., 1.); + + // + // FIT informaton + // + auto bitMin = 16 - mFITvetoWindow; // default is +- 2 bc (2 bit) + auto bitMax = 16 + mFITvetoWindow; + isFITveto = false; + // check FIT information + for (auto bit = bitMin; bit <= bitMax; bit++) { + if (TESTBIT(collFromMcColl.bbFT0Apf(), bit)) + isFITveto = true; + if (TESTBIT(collFromMcColl.bbFT0Cpf(), bit)) + isFITveto = true; + if (useFV0ForVeto && TESTBIT(collFromMcColl.bbFV0Apf(), bit)) + isFITveto = true; + if (useFDDAForVeto && TESTBIT(collFromMcColl.bbFDDApf(), bit)) + isFITveto = true; + if (useFDDCForVeto && TESTBIT(collFromMcColl.bbFDDCpf(), bit)) + isFITveto = true; + } // end of loop over FIT bits + if (mFITvetoFlag && isFITveto) { + problemBits[FIT_VETO - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // auto const& collFromMcColl = collFromMcColls.iteratorAt(0); // grab tracks from the reco-level collision to get info to match measured data tables (processDataSG function) auto const& trksFromColl = tracks.sliceBy(trackPerCollision, collFromMcColl.globalIndex()); @@ -1498,7 +1574,7 @@ struct TauThreeProngEventTableProducer { bcSels[6] = collFromMcColl.zVtxFT0vPV(); bcSels[7] = collFromMcColl.vtxITSTPC(); - const int offset = 1; + // const int offset = 1; bcSelBits = bcSels[0]; // initialization for (int ibit = 1; ibit < nBitsMax; ibit++) { bcSelBits = (bcSelBits << offset); // shift by 1 position towards left @@ -1556,7 +1632,9 @@ struct TauThreeProngEventTableProducer { LOGF(info, "------ Truth collision has more than 6 charged daughters from 2 taus. Breaking the daughter loop."); // histos.get(HIST("Truth/hTroubles"))->Fill(3); // problem = true; - problem = TOO_MANY_DAUGHTERS; + + problemBits[TOO_MANY_DAUGHTERS - 1] = true; + // problem = TOO_MANY_DAUGHTERS; registrySkim.get(HIST("skim/problemMC"))->Fill(TOO_MANY_DAUGHTERS); break; @@ -1602,7 +1680,9 @@ struct TauThreeProngEventTableProducer { if (counter > 1) { if (verbose) LOGF(info, "------ Daughter has more than 1 associated track. Ntracks = %d. Skipping this daughter.", counter); - problem = TWO_TRACKS; + + problemBits[TWO_TRACKS - 1] = true; + // problem = TWO_TRACKS; registrySkim.get(HIST("skim/problemMC"))->Fill(TWO_TRACKS); continue; } @@ -1610,7 +1690,8 @@ struct TauThreeProngEventTableProducer { // what if no track is associated to mc particle??? if (verbose) LOGF(info, "----- Daughter has no associated track. Ntracks = %d. Skipping this daughter.", tracksFromDaughter.size()); - problem = NO_TRACK; + problemBits[NO_TRACK - 1] = true; + // problem = NO_TRACK; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_TRACK); continue; } @@ -1659,6 +1740,13 @@ struct TauThreeProngEventTableProducer { } // end of loop over MC particles } // end of loop over collisions associated to MC collision + // set up problem bits + problemBitsFlag = problemBits[0]; // initialization + for (int ibit = 1; ibit < nBitsMax; ibit++) { + problemBitsFlag = (problemBitsFlag << offset); // shift by 1 position towards left + problemBitsFlag += problemBits[ibit]; // add next bit to the pool + } + // decide the channel and set the variable. trueChannel = trueChannel + countPi0 * 10 + zerothTau * 100; @@ -1695,7 +1783,7 @@ struct TauThreeProngEventTableProducer { mccoll.posZ(), // trueTauX, trueTauY, trueTauZ, trueDaugX, trueDaugY, trueDaugZ, - trueDaugPdgCode, problem); + trueDaugPdgCode, problemBitsFlag); } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 trueTauSixTracks(runNumber, bc, // is it necessary @@ -1727,7 +1815,7 @@ struct TauThreeProngEventTableProducer { mccoll.posZ(), // trueTauX, trueTauY, trueTauZ, trueDaugX, trueDaugY, trueDaugZ, - trueDaugPdgCode, problem); + trueDaugPdgCode, problemBitsFlag); } // end of saving the output } else { // get only the truth information. The reco-level info is left on default From e907e52a34c3388d21ca93eeee6373eb001da515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jura=C4=8Dka?= <137087737+jjuracka@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:19:53 +0100 Subject: [PATCH 535/648] [PWGUD] updates to upcRhoAnalysis (#18520) --- PWGUD/Tasks/upcRhoAnalysis.cxx | 264 ++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 72 deletions(-) diff --git a/PWGUD/Tasks/upcRhoAnalysis.cxx b/PWGUD/Tasks/upcRhoAnalysis.cxx index db20d94afb3..adb18292a7b 100644 --- a/PWGUD/Tasks/upcRhoAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoAnalysis.cxx @@ -72,7 +72,11 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, float); DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(KNoCollInTimeRangeStandard, kNoCollInTimeRangeStandard, bool); +DECLARE_SOA_COLUMN(KNoCollInRofStandard, kNoCollInRofStandard, bool); +DECLARE_SOA_COLUMN(KNoHighMultCollInPrevRof, kNoHighMultCollInPrevRof, bool); // FIT info DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); @@ -107,9 +111,12 @@ DECLARE_SOA_COLUMN(LeadingTrackKaPID, leadingTrackKaPID, float); DECLARE_SOA_COLUMN(SubleadingTrackKaPID, subleadingTrackKaPID, float); DECLARE_SOA_COLUMN(LeadingTrackPrPID, leadingTrackPrPID, float); DECLARE_SOA_COLUMN(SubleadingTrackPrPID, subleadingTrackPrPID, float); +DECLARE_SOA_COLUMN(LeadingTrackTofHit, leadingTrackTofHit, int); +DECLARE_SOA_COLUMN(SubleadingTrackTofHit, subleadingTrackTofHit, int); } // namespace reco_tree DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", - reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::LocalBC, + reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::GlobalBC, reco_tree::LocalBC, + reco_tree::KNoCollInTimeRangeStandard, reco_tree::KNoCollInRofStandard, reco_tree::KNoHighMultCollInPrevRof, reco_tree::TotalFT0AmplitudeA, reco_tree::TotalFT0AmplitudeC, reco_tree::TotalFV0AmplitudeA, reco_tree::TotalFDDAmplitudeA, reco_tree::TotalFDDAmplitudeC, reco_tree::TimeFT0A, reco_tree::TimeFT0C, reco_tree::TimeFV0A, reco_tree::TimeFDDA, reco_tree::TimeFDDC, reco_tree::EnergyCommonZNA, reco_tree::EnergyCommonZNC, reco_tree::TimeZNA, reco_tree::TimeZNC, reco_tree::NeutronClass, @@ -120,8 +127,8 @@ DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", reco_tree::LeadingTrackPiPID, reco_tree::SubleadingTrackPiPID, reco_tree::LeadingTrackElPID, reco_tree::SubleadingTrackElPID, reco_tree::LeadingTrackKaPID, reco_tree::SubleadingTrackKaPID, - reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID); - + reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID, + reco_tree::LeadingTrackTofHit, reco_tree::SubleadingTrackTofHit); namespace mc_tree { // misc event info @@ -129,6 +136,7 @@ DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(PosX, posX, float); DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); // tracks DECLARE_SOA_COLUMN(LeadingTrackSign, leadingTrackSign, int); @@ -141,7 +149,7 @@ DECLARE_SOA_COLUMN(LeadingTrackPhi, leadingTrackPhi, float); DECLARE_SOA_COLUMN(SubleadingTrackPhi, subleadingTrackPhi, float); } // namespace mc_tree DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", - mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::LocalBC, + mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::GlobalBC, mc_tree::LocalBC, mc_tree::LeadingTrackSign, mc_tree::SubleadingTrackSign, mc_tree::LeadingTrackPt, mc_tree::SubleadingTrackPt, mc_tree::LeadingTrackEta, mc_tree::SubleadingTrackEta, @@ -149,6 +157,12 @@ DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", namespace resolution_tree { +// misc event info +DECLARE_SOA_COLUMN(GenGlobalBC, genGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(GenLocalBC, genLocalBC, int); +DECLARE_SOA_COLUMN(RecoRunNumber, recoRunNumber, int); +DECLARE_SOA_COLUMN(RecoGlobalBC, recoGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(RecoLocalBC, recoLocalBC, int); // vertex info DECLARE_SOA_COLUMN(GenPosX, genPosX, float); DECLARE_SOA_COLUMN(GenPosY, genPosY, float); @@ -156,6 +170,17 @@ DECLARE_SOA_COLUMN(GenPosZ, genPosZ, float); DECLARE_SOA_COLUMN(RecoPosX, recoPosX, float); DECLARE_SOA_COLUMN(RecoPosY, recoPosY, float); DECLARE_SOA_COLUMN(RecoPosZ, recoPosZ, float); +// FIT info +DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeA, totalFDDAmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeC, totalFDDAmplitudeC, float); +DECLARE_SOA_COLUMN(TimeFT0A, timeFT0A, float); +DECLARE_SOA_COLUMN(TimeFT0C, timeFT0C, float); +DECLARE_SOA_COLUMN(TimeFV0A, timeFV0A, float); +DECLARE_SOA_COLUMN(TimeFDDA, timeFDDA, float); +DECLARE_SOA_COLUMN(TimeFDDC, timeFDDC, float); // track info DECLARE_SOA_COLUMN(LeadingSign, leadingSign, int); DECLARE_SOA_COLUMN(LeadingGenPt, leadingGenPt, float); @@ -164,6 +189,7 @@ DECLARE_SOA_COLUMN(LeadingGenPhi, leadingGenPhi, float); DECLARE_SOA_COLUMN(LeadingRecoPt, leadingRecoPt, float); DECLARE_SOA_COLUMN(LeadingRecoEta, leadingRecoEta, float); DECLARE_SOA_COLUMN(LeadingRecoPhi, leadingRecoPhi, float); +DECLARE_SOA_COLUMN(LeadingRecoTofHit, leadingRecoTofHit, int); DECLARE_SOA_COLUMN(SubleadingSign, subleadingSign, int); DECLARE_SOA_COLUMN(SubleadingGenPt, subleadingGenPt, float); DECLARE_SOA_COLUMN(SubleadingGenEta, subleadingGenEta, float); @@ -171,14 +197,20 @@ DECLARE_SOA_COLUMN(SubleadingGenPhi, subleadingGenPhi, float); DECLARE_SOA_COLUMN(SubleadingRecoPt, subleadingRecoPt, float); DECLARE_SOA_COLUMN(SubleadingRecoEta, subleadingRecoEta, float); DECLARE_SOA_COLUMN(SubleadingRecoPhi, subleadingRecoPhi, float); +DECLARE_SOA_COLUMN(SubleadingRecoTofHit, subleadingRecoTofHit, int); } // namespace resolution_tree DECLARE_SOA_TABLE(ResolutionTree, "AOD", "RESOLUTIONTREE", + resolution_tree::GenGlobalBC, resolution_tree::GenLocalBC, + resolution_tree::RecoRunNumber, + resolution_tree::RecoGlobalBC, resolution_tree::RecoLocalBC, resolution_tree::GenPosX, resolution_tree::GenPosY, resolution_tree::GenPosZ, resolution_tree::RecoPosX, resolution_tree::RecoPosY, resolution_tree::RecoPosZ, + resolution_tree::TotalFT0AmplitudeA, resolution_tree::TotalFT0AmplitudeC, resolution_tree::TotalFV0AmplitudeA, resolution_tree::TotalFDDAmplitudeA, resolution_tree::TotalFDDAmplitudeC, + resolution_tree::TimeFT0A, resolution_tree::TimeFT0C, resolution_tree::TimeFV0A, resolution_tree::TimeFDDA, resolution_tree::TimeFDDC, resolution_tree::LeadingSign, resolution_tree::LeadingGenPt, resolution_tree::LeadingGenEta, resolution_tree::LeadingGenPhi, - resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, + resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, resolution_tree::LeadingRecoTofHit, resolution_tree::SubleadingSign, resolution_tree::SubleadingGenPt, resolution_tree::SubleadingGenEta, resolution_tree::SubleadingGenPhi, - resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi); + resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi, resolution_tree::SubleadingRecoTofHit); } // namespace o2::aod struct UpcRhoAnalysis { @@ -188,9 +220,9 @@ struct UpcRhoAnalysis { SGSelector sgSelector; - const float pcEtaCut = 0.9; // physics coordination recommendation - const int nPions = 2; // only study dipion final states - const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544911, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544991, 544992, 545004, 545008, 545009, 545041, 545042, 545044, 545047, 545060, 545062, 545063, 545064, 545066, 545086, 545103, 545117, 545171, 545184, 545185, 545210, 545222, 545223, 545246, 545249, 545262, 545289, 545291, 545294, 545295, 545296, 545311, 545312, 545332, 545345, 545367}; + const float pcEtaCut = 0.9; // physics coordination recommendation + const int nExpectedPions = 2; // only study dipion final states + const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}; AxisSpec runNumberAxis = {static_cast(runNumbers.size()), 0.5, static_cast(runNumbers.size()) + 0.5, "run number"}; Configurable isPO{"isPO", false, "processing p-O data?"}; @@ -207,7 +239,7 @@ struct UpcRhoAnalysis { Configurable useRecoFlag{"useRecoFlag", false, "use UPC/STD reconstruction flag for event selection?"}; Configurable cutRecoFlag{"cutRecoFlag", 1, "0 = std mode, 1 = upc mode"}; Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection?"}; - Configurable cutRctFlag{"cutRctFlag", 1, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + Configurable cutRctFlag{"cutRctFlag", 4, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; Configurable selectRuns{"selectRuns", false, "select runs?"}; Configurable> selectedRuns{"selectedRuns", {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}, "list of selected runs"}; @@ -230,11 +262,12 @@ struct UpcRhoAnalysis { Configurable tracksMaxTpcChi2NClCut{"tracksMaxTpcChi2NClCut", 3.0, "max TPC chi2/Ncls cut"}; Configurable tracksMinTpcNClsCrossedOverFindableCut{"tracksMinTpcNClsCrossedOverFindableCut", 1.0, "min TPC crossed rows / findable clusters cut"}; Configurable tracksMinPtCut{"tracksMinPtCut", 0.1, "min pT cut on tracks"}; + Configurable applyPid{"applyPid", true, "apply PID before creating derived data?"}; Configurable systemMassMinCut{"systemMassMinCut", 0.5, "min M cut for reco system"}; Configurable systemMassMaxCut{"systemMassMaxCut", 1.0, "max M cut for reco system"}; Configurable systemPtCut{"systemPtCut", 0.1, "max pT cut for reco system"}; - Configurable systemYCut{"systemYCut", 0.9, "rapiditiy cut for reco system"}; + Configurable systemYCut{"systemYCut", 0.5, "rapiditiy cut for reco system"}; ConfigurableAxis mAxis{"mAxis", {400, 0.0, 4.0}, "#it{m} (GeV/#it{c}^{2})"}; ConfigurableAxis ptAxis{"ptAxis", {400, 0.0, 4.0}, "#it{p}_{T} (GeV/#it{c})"}; @@ -256,13 +289,15 @@ struct UpcRhoAnalysis { void init(o2::framework::InitContext& context) { - if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata")) { + if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata") || context.mOptions.get("processMcRecoWithTruth")) { // QA // collisions rQC.add("QC/collisions/all/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); rQC.add("QC/collisions/all/hPosZ", ";vertex #it{z} (cm);counts", kTH1D, {{400, -20.0, 20.0}}); rQC.add("QC/collisions/all/hNumContrib", ";number of PV contributors;counts", kTH1D, {{36, -0.5, 35.5}}); rQC.add("QC/collisions/all/hZdcCommonEnergy", ";ZNA common energy (TeV);ZNC common energy (TeV);counts", kTH2D, {znCommonEnergyAxis, znCommonEnergyAxis}); + rQC.add("QC/collisions/all/hZnaCommonEnergyPerRun", ";ZNA common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); + rQC.add("QC/collisions/all/hZncCommonEnergyPerRun", ";ZNC common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); rQC.add("QC/collisions/all/hZdcTime", ";ZNA time (ns);ZNC time (ns);counts", kTH2D, {znTimeAxis, znTimeAxis}); rQC.add("QC/collisions/all/hZNTimeVsZNCommonEnergy", ";ZNA/C common energy (TeV);ZNA/C time (ns);counts", kTH2D, {znCommonEnergyAxis, znTimeAxis}); rQC.add("QC/collisions/all/hTotalFT0AmplitudeA", ";FT0A amplitude;counts", kTH1D, {{160, 0.0, 160.0}}); @@ -276,12 +311,17 @@ struct UpcRhoAnalysis { rQC.add("QC/collisions/all/hTimeFDDA", ";FDDA time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hTimeFDDC", ";FDDC time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hOccupancyInTime", ";occupancy in time;counts", kTH1D, {{1100, 0.0, 1100.0}}); + rQC.add("QC/collisions/all/hRecoMode", ";reconstruction mode;counts", kTH1D, {{2, -0.5, 1.5}}); rQC.add("QC/collisions/hNumContribVsPVTracks", ";number of track.isPVContributor() per collision;collision.numContrib();counts", kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}); + rQC.add("QC/collisions/hLocalBC", ";BC ID;counts", kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}); + rQC.add("QC/collisions/all/hkNoCollInTimeRangeStandard", ";kNoCollInTimeRangeStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoCollInRofStandard", ";kNoCollInRofStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoHighMultCollInPrevRof", ";kNoHighMultCollInPrevRof;counts", kTH1D, {{2, -0.5, 1.5}}); // events with selected rho candidates rQC.addClone("QC/collisions/all/", "QC/collisions/trackSelections/"); rQC.addClone("QC/collisions/all/", "QC/collisions/systemSelections/"); - std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "#it{z} position", "number of contributors", "RCT selections", "reco flag selection", "occupancy selection"}; + std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "vertex #it{z} position", "number of contributors", "RCT selections", "occupancy selection"}; rQC.add("QC/collisions/hSelectionCounter", ";;collisions passing selections", kTH1D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/collisions/hSelectionCounterPerRun", ";;run number;collisions passing selections", kTH2D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); for (int i = 0; i < static_cast(collisionSelectionCounterLabels.size()); ++i) { @@ -292,13 +332,10 @@ struct UpcRhoAnalysis { rQC.get(HIST("QC/collisions/hSelectionCounterPerRun"))->GetYaxis()->SetBinLabel(i + 1, std::to_string(runNumbers[i]).c_str()); // tracks rQC.add("QC/tracks/all/hTpcNSigmaPi", ";TPC #it{n#sigma}(#pi);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaPi", ";TPC #it{n#sigma}(#pi);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaEl", ";TPC #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaEl", ";TPC #it{n#sigma}(e);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaKa", ";TPC #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaKa", ";TPC #it{n#sigma}(K);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); rQC.add("QC/tracks/all/hTpcNSigmaPr", ";TPC #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); - rQC.add("QC/tracks/all/hPtVsEtaVsTpcNSigmaPr", ";TPC #it{n#sigma}(p);#it{p}_{T} (GeV/#it{c});#it{#eta}", kTH3D, {{100, -10.0, 10.0}, {200, 0.0, 4.0}, {18, -0.9, 0.9}}); + rQC.add("QC/tracks/all/hTpcNSigmaMu", ";TPC #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); rQC.add("QC/tracks/all/hDcaXYZ", ";track #it{DCA}_{z} (cm);track #it{DCA}_{xy} (cm);counts", kTH2D, {{1000, -5.0, 5.0}, {400, -2.0, 2.0}}); rQC.add("QC/tracks/all/hItsNCls", ";ITS #it{N}_{cls};counts", kTH1D, {{9, -0.5, 8.5}}); rQC.add("QC/tracks/all/hItsChi2NCl", ";ITS #it{#chi}^{2}/#it{N}_{cls};counts", kTH1D, {{150, 0.0, 15.0}}); @@ -311,18 +348,31 @@ struct UpcRhoAnalysis { rQC.add("QC/tracks/all/hPhi", ";#it{#phi} (rad);counts", kTH1D, {phiAxis}); rQC.add("QC/tracks/all/hTpcSignalVsP", ";|#it{p}| (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); rQC.add("QC/tracks/all/hTpcSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hLength", ";track length (cm);counts", kTH1D, {{1000, 0.0, 1000.0}}); + rQC.add("QC/tracks/all/hTofChi2", ";TOF #it{#chi}^{2};counts", kTH1D, {{100, 0.0, 10.0}}); + rQC.add("QC/tracks/all/hTofSignalVsP", ";|#it{p}| (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofExpMom", ";expected momentum from TOF (GeV/#it{c});counts", kTH1D, {ptAxis}); + rQC.add("QC/tracks/all/hBeta", ";TOF #it{#beta};counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hBetaError", ";TOF #it{#beta} error;counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hTofNSigmaPi", ";TOF #it{n#sigma}(#pi);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaEl", ";TOF #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaKa", ";TOF #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaPr", ";TOF #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaMu", ";TOF #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); // tracks passing selections rQC.addClone("QC/tracks/all/", "QC/tracks/trackSelections/"); rQC.addClone("QC/tracks/all/", "QC/tracks/systemSelections/"); rQC.add("QC/tracks/trackSelections/hRemainingTracks", ";remaining tracks;counts", kTH1D, {{21, -0.5, 20.5}}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2D", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaEl2D", ";TPC #it{n#sigma}(e)_{leading};TPC #it{n#sigma}(e)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaKa2D", ";TPC #it{n#sigma}(K)_{leading};TPC #it{n#sigma}(K)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPr2D", ";TPC #it{n#sigma}(p)_{leading};TPC #it{n#sigma}(p)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaMu2D", ";TPC #it{n#sigma}(#mu)_{leading};TPC #it{n#sigma}(#mu)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); // selection counter std::vector trackSelectionCounterLabels = {"all tracks", "PV contributor", "ITS hit", "ITS #it{N}_{cls}", "itsClusterMap check", "ITS #it{#chi}^{2}/#it{N}_{cls}", "TPC hit", "found TPC #it{N}_{cls}", "TPC #it{#chi}^{2}/#it{N}_{cls}", "TPC crossed rows", "TPC crossed rows/#it{N}_{cls}", - "TOF requirement", "#it{p}_{T}", "#it{DCA}", "#it{#eta}", "exactly 2 tracks", "PID"}; rQC.add("QC/tracks/hSelectionCounter", ";;tracks passing selections", kTH1D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/tracks/hSelectionCounterPerRun", ";;run number;tracks passing selections", kTH2D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); @@ -378,7 +428,7 @@ struct UpcRhoAnalysis { rSystem.addClone("system/selected/AnAn/", "system/selected/XnXn/"); } - if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs")) { + if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs") || context.mOptions.get("processMcRecoWithTruth")) { // MC // collisions rMC.add("MC/collisions/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); @@ -413,6 +463,8 @@ struct UpcRhoAnalysis { rMC.add("MC/system/hPhiCharge", ";#Delta#it{#phi}_{charge} (rad);counts", kTH1D, {deltaPhiAxis}); rMC.add("MC/system/hPhiRandomVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); rMC.add("MC/system/hPhiChargeVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiRandomVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiChargeVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); rMC.addClone("MC/system/", "MC/system/selected/"); } @@ -434,9 +486,11 @@ struct UpcRhoAnalysis { rResolution.add("MC/resolution/system/1D/hM", ";#it{m}_{reco} - #it{m}_{true} (GeV/#it{c}^{2});counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hMVsM", ";#it{m}_{true} (GeV/#it{c}^{2});#it{m}_{reco} (GeV/#it{c}^{2});counts", kTH2D, {mAxis, mAxis}); rResolution.add("MC/resolution/system/1D/hPt", ";1/#it{p}_{T, reco} - 1/#it{p}_{T, true} (1/(GeV/#it{c}));counts", kTH1D, {resolutionAxis}); - rResolution.add("MC/resolution/system/2D/hPtVsPt", ";1/#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); + rResolution.add("MC/resolution/system/2D/hPtVsPt", ";#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); rResolution.add("MC/resolution/system/1D/hY", ";#it{y}_{reco} - #it{y}_{true};counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hYVsY", ";#it{y}_{true};#it{y}_{reco};counts", kTH2D, {yAxis, yAxis}); + rResolution.add("MC/resolution/system/1D/hPhi", ";#it{#phi}_{reco} - #it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); + rResolution.add("MC/resolution/system/2D/hPhiVsPhi", ";#it{#phi}_{true} (rad);#it{#phi}_{reco} (rad);counts", kTH2D, {phiAxis, phiAxis}); rResolution.add("MC/resolution/system/1D/hDeltaPhi", ";#Delta#it{#phi}_{reco} - #Delta#it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi", ";#Delta#it{#phi}_{true} (rad);#Delta#it{#phi}_{reco} (rad);counts", kTH2D, {deltaPhiAxis, deltaPhiAxis}); } @@ -447,11 +501,13 @@ struct UpcRhoAnalysis { static constexpr std::string_view NeutronClass[5] = {"AnAn/", "0n0n/", "Xn0n/", "0nXn/", "XnXn/"}; template - void fillCollisionQcHistos(const C& collision) // fills collision QC histograms before/after cuts + void fillCollisionQcHistos(const C& collision, int runIndex) // fills collision QC histograms before/after cuts { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosXY"), collision.posX(), collision.posY()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosZ"), collision.posZ()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcCommonEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZnaCommonEnergyPerRun"), collision.energyCommonZNA(), runIndex); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZncCommonEnergyPerRun"), collision.energyCommonZNC(), runIndex); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcTime"), collision.timeZNA(), collision.timeZNC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNA(), collision.timeZNA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNC(), collision.timeZNC()); @@ -467,6 +523,10 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDA"), collision.timeFDDA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDC"), collision.timeFDDC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hOccupancyInTime"), collision.occupancyInTime()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hRecoMode"), collision.flags()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInTimeRangeStandard"), collision.trs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInRofStandard"), collision.trofs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoHighMultCollInPrevRof"), collision.hmpr()); } template @@ -476,13 +536,10 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hEta"), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPhi"), phi(track.px(), track.py())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaPi"), track.tpcNSigmaPi()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaPi"), track.tpcNSigmaPi(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaEl"), track.tpcNSigmaEl()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaEl"), track.tpcNSigmaEl(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaKa"), track.tpcNSigmaKa()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaKa"), track.tpcNSigmaKa(), track.pt(), eta(track.px(), track.py(), track.pz())); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaPr"), track.tpcNSigmaPr()); - rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hPtVsEtaVsTpcNSigmaPr"), track.tpcNSigmaPr(), track.pt(), eta(track.px(), track.py(), track.pz())); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaMu"), track.tpcNSigmaMu()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hDcaXYZ"), track.dcaZ(), track.dcaXY()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsNCls"), track.itsNCls()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); @@ -492,6 +549,18 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNClsCrossedRowsOverNClsFindable"), (static_cast(track.tpcNClsCrossedRows()) / static_cast(track.tpcNClsFindable()))); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tpcSignal()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsPt"), track.pt(), track.tpcSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hLength"), track.length()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofChi2"), track.tofChi2()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsPt"), track.pt(), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofExpMom"), track.tofExpMom()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBeta"), track.beta()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBetaError"), track.betaerror()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPi"), track.tofNSigmaPi()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaEl"), track.tofNSigmaEl()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaKa"), track.tofNSigmaKa()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPr"), track.tofNSigmaPr()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaMu"), track.tofNSigmaMu()); } template @@ -612,18 +681,11 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 8, runIndex); } - if (useRecoFlag) { - if (collision.flags() != cutRecoFlag) // check reconstruction mode - return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); - } - if (cutOccupancy) { if (collision.occupancyInTime() > occupancyCut) return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 10); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 10, runIndex); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); } // if all selections passed @@ -686,28 +748,21 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/hSelectionCounter"), 10); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 10, runIndex); - if (requireTof) { - if (!track.hasTOF()) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); - } - if (track.pt() < tracksMinPtCut) return false; + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); + + if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.1))) + return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 12); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 12, runIndex); - if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.01))) + if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 13); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 13, runIndex); - if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); - // if all selections passed return true; } @@ -812,11 +867,11 @@ struct UpcRhoAnalysis { // function to obtain index of run from the run number vector // search for passed run number in the vector and return its index +1 to use in the filling of a histogram - int getRunIndex(int runNumber, const std::vector& runNumbers) + int getRunIndex(int runNumber, const std::vector& runNumberList) { - auto it = std::find(runNumbers.begin(), runNumbers.end(), runNumber); - if (it != runNumbers.end()) { - return std::distance(runNumbers.begin(), it) + 1; // +1 to avoid 0 bin in histogram + auto it = std::find(runNumberList.begin(), runNumberList.end(), runNumber); + if (it != runNumberList.end()) { + return std::distance(runNumberList.begin(), it) + 1; // +1 to avoid 0 bin in histogram } else { return 0; // return 0 if run number not found } @@ -836,8 +891,9 @@ struct UpcRhoAnalysis { nPVTracks++; } rQC.fill(HIST("QC/collisions/hNumContribVsPVTracks"), nPVTracks, collision.numContrib()); + rQC.fill(HIST("QC/collisions/hLocalBC"), collision.globalBC() % o2::constants::lhc::LHCMaxBunches); - fillCollisionQcHistos<0>(collision); // fill QC histograms before cuts + fillCollisionQcHistos<0>(collision, runIndex); // fill QC histograms before cuts if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts return; @@ -881,16 +937,12 @@ struct UpcRhoAnalysis { } rQC.fill(HIST("QC/tracks/trackSelections/hRemainingTracks"), cutTracks.size()); - if (static_cast(cutTracks.size()) != nPions) // further consider only two pion systems + if (static_cast(cutTracks.size()) != nExpectedPions) // further consider only two pion systems return; - for (int i = 0; i < nPions; i++) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); + for (int i = 0; i < nExpectedPions; i++) { + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); } - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), cutTracks[0].tpcNSigmaPi(), cutTracks[1].tpcNSigmaPi()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), cutTracks[0].tpcNSigmaEl(), cutTracks[1].tpcNSigmaEl()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), cutTracks[0].tpcNSigmaKa(), cutTracks[1].tpcNSigmaKa()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), cutTracks[0].tpcNSigmaPr(), cutTracks[1].tpcNSigmaPr()); // create a vector of 4-vectors for selected tracks std::vector cutTracksLVs; @@ -902,6 +954,12 @@ struct UpcRhoAnalysis { auto leadingTrack = momentum(cutTracks[0].px(), cutTracks[0].py(), cutTracks[0].pz()) > momentum(cutTracks[1].px(), cutTracks[1].py(), cutTracks[1].pz()) ? cutTracks[0] : cutTracks[1]; auto subleadingTrack = (leadingTrack == cutTracks[0]) ? cutTracks[1] : cutTracks[0]; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaMu2D"), leadingTrack.tpcNSigmaMu(), subleadingTrack.tpcNSigmaMu()); + float leadingPt = leadingTrack.pt(); float subleadingPt = subleadingTrack.pt(); float leadingEta = eta(leadingTrack.px(), leadingTrack.py(), leadingTrack.pz()); @@ -911,8 +969,12 @@ struct UpcRhoAnalysis { float phiRandom = getPhiRandom(cutTracksLVs); float phiCharge = getPhiCharge(cutTracks, cutTracksLVs); + if (applyPid && !tracksPassPID(cutTracks)) // apply PID cut before creating derived data if desired + return; + // fill recoTree - recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.trs(), collision.trofs(), collision.hmpr(), collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), energyCommonZNA, energyCommonZNC, timeZNA, timeZNC, neutronClass, @@ -923,18 +985,20 @@ struct UpcRhoAnalysis { leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi(), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl(), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa(), - leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr(), + leadingTrack.hasTOF(), subleadingTrack.hasTOF()); if (!tracksPassPID(cutTracks)) // apply PID cut return; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); for (const auto& cutTrack : cutTracks) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 16); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 16, runIndex); + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); fillTrackQcHistos<1>(cutTrack); // fill QC histograms after cuts } rQC.fill(HIST("QC/tracks/hTofHitCheck"), leadingTrack.hasTOF(), subleadingTrack.hasTOF()); - fillCollisionQcHistos<1>(collision); // fill QC histograms after track selections + fillCollisionQcHistos<1>(collision, runIndex); // fill QC histograms after track selections ROOT::Math::PxPyPzMVector system = reconstructSystem(cutTracksLVs); int totalCharge = tracksTotalCharge(cutTracks); @@ -972,7 +1036,7 @@ struct UpcRhoAnalysis { // fill histograms for system passing cuts switch (totalCharge) { case 0: - fillCollisionQcHistos<2>(collision); + fillCollisionQcHistos<2>(collision, runIndex); for (const auto& cutTrack : cutTracks) fillTrackQcHistos<2>(cutTrack); fillTrack2dHistos<2, 0>(leadingPt, subleadingPt, leadingEta, subleadingEta, leadingPhi, subleadingPhi); @@ -1066,7 +1130,7 @@ struct UpcRhoAnalysis { } rMC.fill(HIST("MC/collisions/hNPions"), cutMcParticles.size()); - if (static_cast(cutMcParticles.size()) != 2) + if (static_cast(cutMcParticles.size()) != nExpectedPions) // further consider only two pion systems return; if (mcParticlesLVs.size() != cutMcParticles.size()) // sanity check return; @@ -1098,6 +1162,8 @@ struct UpcRhoAnalysis { rMC.fill(HIST("MC/system/hPhiCharge"), phiCharge); rMC.fill(HIST("MC/system/hPhiRandomVsM"), mass, phiRandom); rMC.fill(HIST("MC/system/hPhiChargeVsM"), mass, phiCharge); + rMC.fill(HIST("MC/system/hPhiRandomVsPt"), pT, phiRandom); + rMC.fill(HIST("MC/system/hPhiChargeVsPt"), pT, phiCharge); if (systemPassesCuts(system)) { rMC.fill(HIST("MC/system/selected/hM"), mass); @@ -1113,7 +1179,7 @@ struct UpcRhoAnalysis { } // fill mcTree - mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, leadingPion.pdgCode() / std::abs(leadingPion.pdgCode()), subleadingPion.pdgCode() / std::abs(subleadingPion.pdgCode()), pt(leadingPion.px(), leadingPion.py()), pt(subleadingPion.px(), subleadingPion.py()), eta(leadingPion.px(), leadingPion.py(), leadingPion.pz()), eta(subleadingPion.px(), subleadingPion.py(), subleadingPion.pz()), @@ -1146,6 +1212,8 @@ struct UpcRhoAnalysis { void processDGdata(FullUdDgCollision const& collision, FullUdTracks const& tracks) { int runIndex = getRunIndex(collision.runNumber(), runNumbers); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 0); // all collisions + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 0, runIndex); rQC.fill(HIST("QC/collisions/hSelectionCounter"), 1); // no single-gap collisions in dataset rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 1, runIndex); @@ -1202,7 +1270,7 @@ struct UpcRhoAnalysis { recoTracks.push_back(track); } - if (truePionLVs.size() != 2 || recoPionLVs.size() != 2) + if (static_cast(truePionLVs.size()) != nExpectedPions || static_cast(recoPionLVs.size()) != nExpectedPions) return; ROOT::Math::PxPyPzMVector trueSystem = reconstructSystem(truePionLVs); @@ -1216,6 +1284,8 @@ struct UpcRhoAnalysis { rResolution.fill(HIST("MC/resolution/system/2D/hPtVsPt"), trueSystem.Pt(), recoSystem.Pt()); rResolution.fill(HIST("MC/resolution/system/1D/hY"), recoSystem.Rapidity() - trueSystem.Rapidity()); rResolution.fill(HIST("MC/resolution/system/2D/hYVsY"), trueSystem.Rapidity(), recoSystem.Rapidity()); + rResolution.fill(HIST("MC/resolution/system/1D/hPhi"), phi(recoSystem.Px(), recoSystem.Py()) - phi(trueSystem.Px(), trueSystem.Py())); + rResolution.fill(HIST("MC/resolution/system/2D/hPhiVsPhi"), phi(trueSystem.Px(), trueSystem.Py()), phi(recoSystem.Px(), recoSystem.Py())); rResolution.fill(HIST("MC/resolution/system/1D/hDeltaPhi"), recoDeltaPhi - trueDeltaPhi); rResolution.fill(HIST("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi"), trueDeltaPhi, recoDeltaPhi); @@ -1224,15 +1294,65 @@ struct UpcRhoAnalysis { auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; - resolutionTree(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), - pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), - pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py())); + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); } PROCESS_SWITCH(UpcRhoAnalysis, processResolution, "check resolution of kinematic variables", false); + void processMcRecoWithTruth(soa::Join::iterator const& collision, soa::Join const& tracks, aod::UDMcCollisions const&, aod::UDMcParticles const&) + { + // basically just runs the analysis as for normal data but also accesses the MC truth information for the tracks and collisions + auto mcCollision = collision.udMcCollision(); + const int runIndex = -1; // we don't care here + if (cutGapSide && collision.gapSide() != gapSide) + return; + if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts + return; + + std::vector recoTracks; // store selected tracks + std::vector trueTracks; + for (const auto& track : tracks) { + fillTrackQcHistos<0>(track); // fill QC histograms before cuts + + if (!trackPassesCuts(track, runIndex)) // apply track cuts + continue; + recoTracks.push_back(track); + if (track.has_udMcParticle()) + trueTracks.push_back(track.udMcParticle()); + } + if (static_cast(recoTracks.size()) != nExpectedPions || static_cast(trueTracks.size()) != nExpectedPions) // further consider only two pion systems + return; + if (applyPid && !tracksPassPID(recoTracks)) // apply PID cut before creating derived data if desired + return; + + auto leadingTruePion = momentum(trueTracks[0].px(), trueTracks[0].py(), trueTracks[0].pz()) > momentum(trueTracks[1].px(), trueTracks[1].py(), trueTracks[1].pz()) ? trueTracks[0] : trueTracks[1]; + auto subleadingTruePion = (leadingTruePion == trueTracks[0]) ? trueTracks[1] : trueTracks[0]; + auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; + auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; + + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), + leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), + subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); + } + PROCESS_SWITCH(UpcRhoAnalysis, processMcRecoWithTruth, "process MC reco with access to MC truth", false); + void processCollisionRecoCheck(aod::UDMcCollision const& /* mcCollision */, soa::SmallGroups> const& collisions) { checkNumberOfCollisionReconstructions(collisions); From a5132d2aa0389c469d4919bbdd44f3e2e3c180af Mon Sep 17 00:00:00 2001 From: sashingo Date: Tue, 3 Nov 2026 01:37:31 +0900 Subject: [PATCH 536/648] [PWGHF] extend taskElectronWeakBoson with W-h correlation and systematics (#18545) --- PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx | 502 +++++++++++++++++----- 1 file changed, 390 insertions(+), 112 deletions(-) diff --git a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx index 3248701ba77..6fa6239f900 100644 --- a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx +++ b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx @@ -125,7 +125,10 @@ struct HfTaskElectronWeakBoson { Configurable applySel8{"applySel8", true, "Apply sel8 filter or not"}; // CCDB service configurations - Configurable cfgCCDBPath{"cfgCCDBPath", "Users/m/mpuccio/EventFiltering/OTS/", "Path to CCDB for trigger data"}; + // Zorro's own default (Common/Core/Zorro.h: mBaseCCDBPath = "EventFiltering/Zorro/"). + // The old value "Users/m/mpuccio/EventFiltering/OTS/" is a personal area from early Run 3 + // and no longer holds FilterCounters, so Zorro::initCCDB aborts the job on it. + Configurable cfgCCDBPath{"cfgCCDBPath", "EventFiltering/Zorro/", "Path to CCDB for trigger data"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; // KFParticle @@ -157,6 +160,35 @@ struct HfTaskElectronWeakBoson { Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; } ueCfg; + // W->e - h correlation. Grouped so that the three options cost one task member instead of + // three: the struct is close to the 99 the framework allows. + struct : ConfigurableGroup { + Configurable enableWeHadron{"enableWeHadron", false, "fill the isolated-electron - hadron correlation"}; + Configurable ptWeMin{"ptWeMin", 20.0f, "minimum pT of the trigger electron"}; + Configurable vetoZeeElectron{"vetoZeeElectron", true, "reject trigger electrons that build a Z->ee candidate"}; + } weCfg; + + // Systematics by cut variation (method C): one extra THnSparse axis whose bin 0 is the + // nominal selection and bins 1..N the variations. + struct : ConfigurableGroup { + Configurable enableSystematics{"enableSystematics", false, "add the cut-variation axis"}; + // associated hadron, checked against dcaxyTrk / nclusterTPC (crossed rows) / nclusterITS + Configurable> varDcaxyMax{"varDcaxyMax", {}, "variations of dcaxyMax"}; + Configurable> varNclCrossTpcMin{"varNclCrossTpcMin", {}, "variations of nclcrossTpcMin"}; + Configurable> varNclItsMin{"varNclItsMin", {}, "variations of nclItsMin"}; + // W trigger electron identification + Configurable> varEopMin{"varEopMin", {}, "variations of eopMin"}; + Configurable> varEopMax{"varEopMax", {}, "variations of eopMax"}; + Configurable> varNsigTpcMin{"varNsigTpcMin", {}, "variations of nsigTpcMin"}; + Configurable> varNsigTpcMax{"varNsigTpcMax", {}, "variations of nsigTpcMax"}; + Configurable> varM02Min{"varM02Min", {}, "variations of m02Min"}; + Configurable> varM02Max{"varM02Max", {}, "variations of m02Max"}; + // Z candidate. Two-sided: reconstructedZ holds every KF chi2-passing pair at any mass, + // so a wider window really adds candidates instead of being a no-op. + Configurable> varMassZMin{"varMassZMin", {}, "variations of massZMin"}; + Configurable> varMassZMax{"varMassZMax", {}, "variations of massZMax"}; + } sysCfg; + // MC : efficiency of the associated hadrons in the Z-h correlation. struct : ConfigurableGroup { std::string prefix = "mcEfficiency"; // JSON group name @@ -232,9 +264,7 @@ struct HfTaskElectronWeakBoson { ConfigurableAxis confaxisIsoEnergy{"confaxisIsoEnergy", {255, 0, 2.0}, "E_{iso}"}; ConfigurableAxis confaxisIsoMomentum{"confaxisIsoMomentum", {255, 0, 2.0}, "p_{iso}"}; ConfigurableAxis confaxisIsoTrack{"confaxisIsoTrack", {25, -0.5, 24.5}, "Isolation Track"}; - // raw TPC dE/dx (arbitrary units, MIP around 50) -- used by hTHnElectrons - ConfigurableAxis confaxisDedxTrack{"confaxisDedxTrack", {200, 0, 200}, "dE/dx"}; - // TPC n-sigma of the associated hadrons -- used by hZHadronDphi + ConfigurableAxis confaxisEtCluster{"confaxisEtCluster", {100, 0, 100}, "E_{T} (GeV)"}; ConfigurableAxis confaxisNsigmaHadron{"confaxisNsigmaHadron", {200, -10, 10}, "n#sigma^{#pi}_{TPC}"}; ConfigurableAxis confaxisInvMassZgamma{"confaxisInvMassZgamma", {150, 0, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisInvMassZ{"confaxisInvMassZ", {130, 20, 150}, "M_{ee} (GeV/c^{2})"}; @@ -294,7 +324,7 @@ struct HfTaskElectronWeakBoson { const AxisSpec axisNsigma{100, -5, 5, "N#sigma"}; const AxisSpec axisNsigmaZneg{100, -5, 5, "N#sigma_{pos}"}; const AxisSpec axisNsigmaZpos{100, -5, 5, "N#sigma_{neg}"}; - const AxisSpec axisDedx{confaxisDedxTrack, "dE/dx"}; + const AxisSpec axisEtCluster{confaxisEtCluster, "E_{T} (GeV)"}; const AxisSpec axisNsigmaHadron{confaxisNsigmaHadron, "n#sigma^{#pi}_{TPC}"}; const AxisSpec axisSignHadron{2, -2, 2, "charge sign (hadron)"}; const AxisSpec axisNMatch{10, -0.5, 9.5, "N_{cluster} within rMatchMax"}; @@ -359,6 +389,8 @@ struct HfTaskElectronWeakBoson { registry.add("hPt", "track pt", kTH1F, {axisPt}); registry.add("hTPCNsigma", "TPC electron Nsigma", kTH2F, {{axisPt}, {axisNsigma}}); registry.add("hEnergy", "EMC cluster energy", kTH1F, {axisE}); + registry.add("hEnergyT", "EMC cluster transverse energy E/cosh(#eta)", kTH1F, {axisE}); + registry.add("hEnergyEt", "EMC cluster energy vs. transverse energy", kTH2F, {{axisE}, {axisE}}); registry.add("hEnergyMult", "EMC cluster energy vs Multiplicity", kTH2F, {{axisCentrality}, {axisE}}); registry.add("hEnergyNcell", "EMC cluster energy and cell", kTH2F, {{axisE}, {axisNcell}}); registry.add("hTrMatchR", "Track EMC Match in radius", kTH2F, {{axisPt}, {axisdR}}); @@ -377,13 +409,41 @@ struct HfTaskElectronWeakBoson { registry.add("hInvMassZeeTrkQAbg", "Track QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); registry.add("hInvMassZeeEmcQA", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); registry.add("hInvMassZeeEmcQAbg", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); - registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisDedx}); + registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisEtCluster}); registry.add("hTHnTrMatch", "Track EMC Match", HistType::kTHnSparseF, {axisPt, axisdPhi, axisdEta}); registry.add("hNMatchPerTrack", "number of matched clusters within rMatchMax per track", kTH2F, {{axisPt}, {axisNMatch}}); // Z-hadron correlation histograms - registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron}); - registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH2F, {{axisSign}, {axisPtZ}}); + const int nSysVar = nCutVariations(); + if (sysCfg.enableSystematics) { + const size_t nVar = static_cast(nSysVar - 1); + if (nVar == 0) { + LOGF(warn, "systematics: enableSystematics is on but every variation vector is empty, so the axis has one bin and nothing is varied."); + } + for (const auto& [values, name] : variationVectorsNamed()) { + if (!values->empty() && values->size() != nVar) { + LOGF(fatal, "systematics: %s has %zu entries but there are %zu variations. Give it either %zu entries or none at all.", name, values->size(), nVar, nVar); + } + } + for (size_t i = 0; i < nVar; ++i) { + const int iv = static_cast(i) + 1; + if (variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax) > dcaxyMax || + variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin) < nclcrossTpcMin || + variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin) < nclItsMin) { + LOGF(warn, "systematics: variation %zu loosens an associated-hadron cut. The container only holds tracks passing the nominal selection, so that variation is identical to the nominal there. Loosen the nominal configurable instead.", i + 1); + } + } + LOGF(info, "systematics: %d bins on the variation axis (bin 0 = nominal)", nSysVar); + } + const AxisSpec axisSysVar{nSysVar, -0.5, nSysVar - 0.5, "cut variation"}; + + registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH3F, {{axisSign}, {axisPtZ}, {axisSysVar}}); + + if (weCfg.enableWeHadron) { + registry.add("hWeHadronDphi", "isolated electron - hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisIsoEnergy, axisIsoMomentum, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hWeSpectrum", "trigger electrons for the e-h correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisIsoEnergy, axisIsoMomentum, axisSysVar}); + } // hisotgram for EMCal trigger registry.add("hEMCalTrigger", "EMCal trigger", kTH1D, {axisTrigger}); @@ -408,13 +468,13 @@ struct HfTaskElectronWeakBoson { if (doprocessMcRec) { registry.add("hMcRecCollCounter", "reconstructed collisions in MC", kTH1D, {axisCounter}); // numerator of the efficiency, in generated kinematics (use with hMcGenHadron) - registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // same tracks, but in reconstructed kinematics (use to correct in measured bins) - registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // purity : all selected tracks and the non-primary / unlabelled sub-samples - registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); - registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); - registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); // momentum resolution registry.add("hMcPtGenRec", "generated vs. reconstructed p_{T}", kTH2F, {{axisPtHadronMc}, {axisPtHadronMc}}); } @@ -616,12 +676,12 @@ struct HfTaskElectronWeakBoson { // Outcome of the track - EMCal cluster matching for one track struct HfEmcMatchResult { - int64_t posBestMatch = -1; // cursor position of the closest match inside the slice, -1 if none - double rMin = 999.9; // smallest sqrt(dPhi^2 + dEta^2) over the matched clusters + int64_t posBestMatch = -1; + double rMin = 999.9; double dPhiMin = 999.9; double dEtaMin = 999.9; - int nMatchInRadius = 0; // number of matched clusters inside rMatchMax - bool isMatched = false; // the closest cluster is inside rMatchMax + int nMatchInRadius = 0; + bool isMatched = false; }; template @@ -647,6 +707,9 @@ struct HfTaskElectronWeakBoson { registry.fill(HIST("hTHnTrMatch"), match.template track_as().pt(), dPhi, dEta); registry.fill(HIST("hEMCtime"), cluster.time()); registry.fill(HIST("hEnergy"), cluster.energy()); + double const energyT = cluster.energy() / std::cosh(cluster.eta()); + registry.fill(HIST("hEnergyT"), energyT); + registry.fill(HIST("hEnergyEt"), cluster.energy(), energyT); registry.fill(HIST("hEnergyMult"), centrality, cluster.energy()); if (std::abs(dPhi) <= rMatchMax && std::abs(dEta) <= rMatchMax) { @@ -740,6 +803,267 @@ struct HfTaskElectronWeakBoson { } } + // ------------------------------------------------------------------------- + // Cut variations (systematics). Bin 0 is always the nominal configurables; bin iv>0 reads + // entry iv-1 of each variation vector. Member FUNCTIONS, so none of this costs a task member. + // ------------------------------------------------------------------------- + int nCutVariations() + { + if (!sysCfg.enableSystematics) { + return 1; + } + size_t n = 0; + for (const auto& [values, name] : variationVectorsNamed()) { + n = std::max(n, values->size()); + } + return 1 + static_cast(n); + } + // The eleven variation vectors, paired with their names for the messages in init(). + std::array*, const char*>, 11> variationVectorsNamed() + { + return {{{&sysCfg.varDcaxyMax.value, "varDcaxyMax"}, + {&sysCfg.varNclCrossTpcMin.value, "varNclCrossTpcMin"}, + {&sysCfg.varNclItsMin.value, "varNclItsMin"}, + {&sysCfg.varEopMin.value, "varEopMin"}, + {&sysCfg.varEopMax.value, "varEopMax"}, + {&sysCfg.varNsigTpcMin.value, "varNsigTpcMin"}, + {&sysCfg.varNsigTpcMax.value, "varNsigTpcMax"}, + {&sysCfg.varM02Min.value, "varM02Min"}, + {&sysCfg.varM02Max.value, "varM02Max"}, + {&sysCfg.varMassZMin.value, "varMassZMin"}, + {&sysCfg.varMassZMax.value, "varMassZMax"}}}; + } + // One variation's value of a cut, falling back to the nominal for bin 0 or a short vector. + static float variedValue(const std::vector& values, int iv, float nominal) + { + if (iv <= 0 || static_cast(iv - 1) >= values.size()) { + return nominal; + } + return values[iv - 1]; + } + + // Associated hadron. dcaxyTrk, nclusterTPC (= crossed rows, see where the container is + // filled) and nclusterITS are the three quantities the candidate carries. + bool passHadronVariation(const HfElectronCandidate& hadron, int iv) + { + if (iv == 0) { + return true; // the container already holds the nominal selection + } + if (std::abs(hadron.dcaxyTrk) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (hadron.nclusterTPC < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (hadron.nclusterITS < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // Same three cuts on a track, for the MC efficiency: the numerator has to be recomputed with + // the very selection the correlation used, or the correction does not match the data. + template + bool passHadronVariationTrack(TTrack const& track, int iv) + { + if (iv == 0) { + return true; + } + if (std::abs(track.dcaXY()) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (track.tpcNClsCrossedRows() < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (track.itsNCls() < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // The Z mass window for one variation. Unlike the associated-hadron cuts this one is + // two-sided: reconstructedZ holds every pair that passed the KF chi2 cut, at any mass, so a + // wider window genuinely adds candidates. iv == 0 uses the nominal configurables. + bool passZVariation(const HfZeeCandidate& zBoson, int iv) + { + if (zBoson.mass < variedValue(sysCfg.varMassZMin.value, iv, massZMin) || + zBoson.mass > variedValue(sysCfg.varMassZMax.value, iv, massZMax)) { + return false; + } + return true; + } + + // W trigger electron. selectedElectronsIso carries no identification cut, so these three can + // be varied in both directions. + bool passElectronVariation(const HfElectronCandidate& ele, int iv) + { + if (ele.eop < variedValue(sysCfg.varEopMin.value, iv, eopMin) || + ele.eop > variedValue(sysCfg.varEopMax.value, iv, eopMax)) { + return false; + } + if (ele.nSigmaTpc < variedValue(sysCfg.varNsigTpcMin.value, iv, nsigTpcMin) || + ele.nSigmaTpc > variedValue(sysCfg.varNsigTpcMax.value, iv, nsigTpcMax)) { + return false; + } + if (ele.m02Emc < variedValue(sysCfg.varM02Min.value, iv, m02Min) || + ele.m02Emc > variedValue(sysCfg.varM02Max.value, iv, m02Max)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------------- + // Z->ee QA. + // ------------------------------------------------------------------------- + template + void fillZeeQaPair(const TCandidate& trackNeg, const TCandidate& trackPos) + { + auto child1 = RecoDecayPtEtaPhi::pVector(trackNeg.pt, trackNeg.eta, trackNeg.phi); + auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); + double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (invMass <= zeeQaCfg.massZMinQA) { + return; + } + float const sectorneg = trackNeg.phi / o2::constants::math::SectorSpanRad; + float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; + + if (zeeQaCfg.enableZeeTrkRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + if (zeeQaCfg.enableZeeEmcRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + } + + void fillZeeQa() + { + if (!zeeQaCfg.enableZeeTrkRecoQA && !zeeQaCfg.enableZeeEmcRecoQA) { + return; + } + + for (const auto& trackEle : selectedElectronsIso) { + for (const auto& trackPos : selectedPositronsIso) { + fillZeeQaPair(trackEle, trackPos); + } + } + // each unordered like-sign pair once + for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { + fillZeeQaPair(selectedElectronsIso[i], selectedElectronsIso[j]); + } + } + for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { + fillZeeQaPair(selectedPositronsIso[i], selectedPositronsIso[j]); + } + } + } + + // ------------------------------------------------------------------------- + // Z - h correlation. + // ------------------------------------------------------------------------- + void fillZHadron(float centrality) + { + const int nVar = nCutVariations(); + for (const auto& zBoson : reconstructedZ) { + // The Z counter is filled per variation, so that a varied mass window moves the + // denominator of the per-trigger yield together with its numerator. + bool anyVariation = false; + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv)) { + continue; + } + anyVariation = true; + registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt, iv); + } + if (!anyVariation) { // outside the widest window of any variation + continue; + } + + for (const auto& trackAss : selectedElectronsAss) { + // remove the two tracks used to build this Z, matched exactly by globalIndex + if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); + double const deltaEta = zBoson.eta - trackAss.eta; + double const ptRatio = trackAss.pt / zBoson.pt; + // one entry per cut variation the pair passes; bin 0 is the nominal selection + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv) || !passHadronVariation(trackAss, iv)) { + continue; + } + registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, + ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } + } + } + + // ------------------------------------------------------------------------- + // W->e - h correlation. + // ------------------------------------------------------------------------- + bool isZeeDaughter(int64_t indexTrk) + { + for (const auto& zBoson : reconstructedZ) { + if (zBoson.charge >= 0) { // like-sign pairs are the combinatorial background, not a Z + continue; + } + if (zBoson.mass < massZMin || zBoson.mass > massZMax) { + continue; + } + if (indexTrk == zBoson.indexChild0 || indexTrk == zBoson.indexChild1) { + return true; + } + } + return false; + } + + void fillWeHadron(const std::vector& electrons, float centrality) + { + for (const auto& ele : electrons) { + if (ele.pt < weCfg.ptWeMin) { + continue; + } + if (ele.eop <= 0.f) { + continue; + } + if (weCfg.vetoZeeElectron && isZeeDaughter(ele.indexTrk)) { + continue; + } + + const int nVar = nCutVariations(); + for (int iv = 0; iv < nVar; ++iv) { + if (!passElectronVariation(ele, iv)) { + continue; + } + registry.fill(HIST("hWeSpectrum"), centrality, ele.sign, ele.pt, ele.energyIso, ele.momIso, iv); + + for (const auto& trackAss : selectedElectronsAss) { + // the trigger electron itself, matched exactly by globalIndex + if (trackAss.indexTrk == ele.indexTrk) { + continue; + } + if (!passHadronVariation(trackAss, iv)) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - ele.phi, -o2::constants::math::PIHalf); + double const deltaEta = ele.eta - trackAss.eta; + registry.fill(HIST("hWeHadronDphi"), centrality, ele.sign, ele.pt, deltaPhi, deltaEta, + ele.energyIso, ele.momIso, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } + } + } + void processData(soa::Filtered::iterator const& collision, aod::BCsWithTimestamps const&, SelectedClusters const& emcClusters, @@ -843,6 +1167,10 @@ struct HfTaskElectronWeakBoson { // LOG(info) << "UE = " << energyUE; } + const float ptIsoStoreMin = weCfg.enableWeHadron + ? std::min(static_cast(ptZeeMin), static_cast(weCfg.ptWeMin)) + : static_cast(ptZeeMin); + // track loop for (const auto& track : tracks) { @@ -921,6 +1249,8 @@ struct HfTaskElectronWeakBoson { const auto& cluster = match.emcalcluster_as(); float const m02Emc = cluster.m02(); float const energyEmc = cluster.energy(); + // transverse energy of the cluster; cosh(eta) >= 1, so energyTEmc <= energyEmc + float const energyTEmc = energyEmc / std::cosh(cluster.eta()); registry.fill(HIST("hTrMatchR"), match.track_as().pt(), rMin); registry.fill(HIST("hEnergyNcell"), energyEmc, cluster.nCells()); @@ -931,7 +1261,7 @@ struct HfTaskElectronWeakBoson { isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { - registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), track.tpcSignal()); + registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), energyTEmc); } // LOG(info) << "E/p" << eop; registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); @@ -971,7 +1301,9 @@ struct HfTaskElectronWeakBoson { // LOG(info) << "R mim = " << rMin; registry.fill(HIST("hTrMatch_mim"), dPhiMin, dEtaMin); } - if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) && track.pt() > ptZeeMin) { + // The e-h correlation reads the same two containers, so they must also be filled when + // only that analysis is enabled, and down to the looser of the two pT thresholds. + if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA || weCfg.enableWeHadron) && track.pt() > ptIsoStoreMin) { if (track.sign() < 0) { selectedElectronsIso.emplace_back( track.pt(), @@ -1010,90 +1342,17 @@ struct HfTaskElectronWeakBoson { } } // end of track loop - // Z-hadron - if (!reconstructedZ.empty()) { - for (const auto& zBoson : reconstructedZ) { - // Z boson selection - if (zBoson.mass < massZMin || zBoson.mass > massZMax) { - continue; - } - registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt); - for (const auto& trackAss : selectedElectronsAss) { - // remove the two tracks used to build this Z, matched exactly by globalIndex - if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { - continue; - } - // calculate Z-h correlation - double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); - double const ptRatio = trackAss.pt / zBoson.pt; - double const deltaEta = zBoson.eta - trackAss.eta; - registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign); - } - } - } // end of Z-hadron correlation + // Z - hadron correlation + fillZHadron(centrality); + + // W->e - h correlation. Electrons and positrons are kept in two containers, so the same + // routine runs twice; the electron charge goes on the sign axis (W- / W+). + if (weCfg.enableWeHadron) { + fillWeHadron(selectedElectronsIso, centrality); + fillWeHadron(selectedPositronsIso, centrality); + } // end of W->e - hadron correlation // Z->ee QA - if (zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA) { - if (!selectedElectronsIso.empty() && !selectedPositronsIso.empty()) { - // signal - for (const auto& trackEle : selectedElectronsIso) { - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (const auto& trackPos : selectedPositronsIso) { - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackEle.pt, trackPos.pt, trackEle.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackEle.nclusterTPC, trackPos.nclusterTPC, trackEle.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackPos.eop, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackEle.pt, trackPos.pt, trackEle.eop, trackPos.eop, trackEle.m02Emc, trackPos.m02Emc, trackEle.nSigmaTpc, trackPos.nSigmaTpc, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); - } - } - } - } - // bg e-e- - for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { - const auto& trackEle = selectedElectronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { - const auto& trackEle2 = selectedElectronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackEle2.pt, trackEle2.eta, trackEle2.phi); - float const sectorpos = trackEle2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.dcaxyTrk, trackEle2.dcaxyTrk, trackEle2.dcazTrk, trackEle.nclusterTPC, trackEle2.nclusterTPC, trackEle.nclusterITS, trackEle2.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackEle2.eop, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.eop, trackEle2.eop, trackEle.m02Emc, trackEle2.m02Emc, trackEle.nSigmaTpc, trackEle2.nSigmaTpc, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); - } - } - } - } - // bg e+e+ - for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { - const auto& trackPos = selectedPositronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - float const sectorneg = trackPos.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { - const auto& trackPos2 = selectedPositronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos2.pt, trackPos2.eta, trackPos2.phi); - float const sectorpos = trackPos2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > zeeQaCfg.massZMinQA) { - if (zeeQaCfg.enableZeeTrkRecoQA) { - registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.dcaxyTrk, trackPos2.dcaxyTrk, trackPos2.dcazTrk, trackPos.nclusterTPC, trackPos2.nclusterTPC, trackPos.nclusterITS, trackPos2.nclusterITS, sectorneg, sectorpos, trackPos.eop, trackPos2.eop, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); - } - if (zeeQaCfg.enableZeeEmcRecoQA) { - registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.eop, trackPos2.eop, trackPos.m02Emc, trackPos2.m02Emc, trackPos.nSigmaTpc, trackPos2.nSigmaTpc, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); - } - } - } - } // end of pos-pos paring - } // check arrays aren't empty - } // end of Z->ee QA + fillZeeQa(); } // processData PROCESS_SWITCH(HfTaskElectronWeakBoson, processData, "process data (Z->ee and Z-h correlation)", true); @@ -1181,27 +1440,46 @@ struct HfTaskElectronWeakBoson { continue; } - registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta()); + // The efficiency has to be recomputed with the same cut variation the correlation used, + // so the numerator histograms carry the variation axis too. hMcGenHadron does not: no + // track cut enters the denominator, so it is common to every variation. + const int nVar = nCutVariations(); + + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta(), iv); + } + } if (!track.has_mcParticle()) { - registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta()); + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta(), iv); + } + } continue; } auto const mcParticle = track.mcParticle(); // reject pileup : the matched particle must come from this MC collision - if (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) { - registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); - continue; - } - if (!isChargedPrimary(mcParticle)) { - registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta()); + const bool isSecondary = (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) || !isChargedPrimary(mcParticle); + if (isSecondary) { + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta(), iv); + } + } continue; } - registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta()); - registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta()); + for (int iv = 0; iv < nVar; ++iv) { + if (!passHadronVariationTrack(track, iv)) { + continue; + } + registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta(), iv); + registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta(), iv); + } registry.fill(HIST("hMcPtGenRec"), mcParticle.pt(), track.pt()); } } // processMcRec From c122ce8a05d00429083b3430efbe19283ab1e52d Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Mon, 2 Nov 2026 18:40:00 +0100 Subject: [PATCH 537/648] [PWGHF] Cut variation: demote efficiency vector non-monotonicity to warning; fix bug and format (#18447) --- PWGHF/D2H/Macros/compute_fraction_cutvar.py | 119 ++++++++++++++------ PWGHF/D2H/Macros/cut_variation.py | 70 +++++++----- 2 files changed, 124 insertions(+), 65 deletions(-) diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 74432688725..d3b3b87111d 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -16,12 +16,13 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') +sys.path.insert(0, "..") from cut_variation import CutVarMinimiser, MinimisationStatus from style_formatter import set_object_style # pylint: disable=no-member,too-many-locals,too-many-statements + class PlotType(IntEnum): Rawy = 0 Eff = auto() @@ -31,6 +32,7 @@ class PlotType(IntEnum): RelUnc = auto() N = auto() + class ObjectToSave(IntEnum): Canvas = 0 RawYield = auto() @@ -44,6 +46,7 @@ class ObjectToSave(IntEnum): MinimisationStatus = auto() N = auto() + def main(config): """ Main function @@ -65,7 +68,7 @@ def main(config): hist_rawy_name = cfg["rawyields"]["histoname"] hist_rawy.append(infile_rawy.Get(hist_rawy_name)) if not isinstance(hist_rawy[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with raw yield \"{hist_rawy_name}\" is absent. Exit.\33[0m") + sys.exit(f'\33[31mFatal error: Histogram with raw yield "{hist_rawy_name}" is absent. Exit.\33[0m') hist_rawy[-1].SetDirectory(0) infile_rawy.Close() @@ -75,14 +78,22 @@ def main(config): hist_effp.append(infile_eff.Get(hist_effp_name)) hist_effnp.append(infile_eff.Get(hist_effnp_name)) if not isinstance(hist_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for prompt \"{hist_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for prompt "{hist_effp_name}" is absent. Exit.\33[0m' + ) if not isinstance(hist_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for nonprompt \"{hist_effnp}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for nonprompt "{hist_effnp}" is absent. Exit.\33[0m' + ) hist_effp[-1].SetDirectory(0) hist_effnp[-1].SetDirectory(0) for i_bin in range(1, hist_effp[-1].GetNbinsX() + 1): - hist_effp[-1].SetBinContent(i_bin, hist_effp[-1].GetBinContent(i_bin) + effp_shift_nsigma*hist_effp[-1].GetBinError(i_bin)) - hist_effnp[-1].SetBinContent(i_bin, hist_effnp[-1].GetBinContent(i_bin) + effnp_shift_nsigma*hist_effnp[-1].GetBinError(i_bin)) + hist_effp[-1].SetBinContent( + i_bin, hist_effp[-1].GetBinContent(i_bin) + effp_shift_nsigma * hist_effp[-1].GetBinError(i_bin) + ) + hist_effnp[-1].SetBinContent( + i_bin, hist_effnp[-1].GetBinContent(i_bin) + effnp_shift_nsigma * hist_effnp[-1].GetBinError(i_bin) + ) if zero_eff_unc: hist_effp[-1].SetBinError(i_bin, 0.0) hist_effnp[-1].SetBinError(i_bin, 0.0) @@ -92,7 +103,9 @@ def main(config): if not isinstance(pt_bin_to_process, int): sys.exit("\33[31mFatal error: pt_bin_to_process must be an integer value. Exit.") if (pt_bin_to_process != -1 and pt_bin_to_process < 1) or pt_bin_to_process > hist_rawy[0].GetNbinsX(): - sys.exit("\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit.") + sys.exit( + "\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit." + ) is_draw_title = [False] * PlotType.N is_draw_title[PlotType.Rawy] = cfg.get("is_draw_title", {}).get("rawy", True) @@ -117,10 +130,16 @@ def main(config): is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", True) is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", True) is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True) - is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get("correlation_matrix", True) + is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get( + "correlation_matrix", True + ) is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", True) - is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get("corrected_fraction", True) - is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get("minimisation_status", True) + is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get( + "corrected_fraction", True + ) + is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get( + "minimisation_status", True + ) if cfg["central_efficiency"]["computerawfrac"]: infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"]) @@ -128,11 +147,15 @@ def main(config): hist_central_effp_name = cfg["central_efficiency"]["histonames"]["prompt"] hist_central_effp = infile_central_eff.Get(hist_central_effp_name) if not isinstance(hist_central_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for prompt \"{hist_central_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for prompt "{hist_central_effp_name}" is absent. Exit.\33[0m' + ) hist_central_effnp_name = cfg["central_efficiency"]["histonames"]["nonprompt"] hist_central_effnp = infile_central_eff.Get(hist_central_effnp_name) if not isinstance(hist_central_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for nonprompt \"{hist_central_effnp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for nonprompt "{hist_central_effnp_name}" is absent. Exit.\33[0m' + ) hist_central_effp.SetDirectory(0) hist_central_effnp.SetDirectory(0) infile_central_eff.Close() @@ -150,8 +173,18 @@ def main(config): hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt") hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt") hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus") - hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") - for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt, hist_minimisation_status, hist_red_chi2: + hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") + for histo in ( + hist_corry_prompt, + hist_corry_nonprompt, + hist_covariance_pnp, + hist_covariance_pp, + hist_covariance_npnp, + hist_corrfrac_prompt, + hist_corrfrac_nonprompt, + hist_minimisation_status, + hist_red_chi2, + ): histo.Reset() hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt") hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt") @@ -164,7 +197,7 @@ def main(config): hist_red_chi2.GetYaxis().SetTitle("#chi^{2}/ndf") hist_minimisation_status_title = "" for min_status in MinimisationStatus: - hist_minimisation_status_title += (str(min_status.value) + " = " + min_status.name + ", ") + hist_minimisation_status_title += str(min_status.value) + " = " + min_status.name + ", " hist_minimisation_status_title = hist_minimisation_status_title[:-2] hist_minimisation_status.SetTitle(hist_minimisation_status_title) set_object_style( @@ -221,17 +254,17 @@ def main(config): if pt_bin_to_process != -1: pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process) - output_name_template = cfg['output']['file'].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" + output_name_template = cfg["output"]["file"].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" output = ROOT.TFile(os.path.join(cfg["output"]["directory"], output_name_template), "recreate") n_sets = len(hist_rawy) pt_axis_title = hist_rawy[0].GetXaxis().GetTitle() for ipt in range(hist_rawy[0].GetNbinsX()): - if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: + if pt_bin_to_process != -1 and ipt + 1 != pt_bin_to_process: continue all_vectors_monotonous = MinimisationStatus.Success pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1) pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1) - print(f"\n\nINFO: processing pt range {ipt+1} from {pt_min} to {pt_max} {pt_axis_title}") + print(f"\n\nINFO: processing pt range {ipt + 1} from {pt_min} to {pt_max} {pt_axis_title}") rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp = (np.zeros(n_sets) for _ in range(6)) for iset, (hrawy, heffp, heffnp) in enumerate(zip(hist_rawy, hist_effp, hist_effnp)): @@ -245,16 +278,24 @@ def main(config): if cfg["minimisation"]["correlated"]: if not (np.all(rawy[1:] > rawy[:-1]) or np.all(rawy[1:] < rawy[:-1])): all_vectors_monotonous = MinimisationStatus.MonotonyViolation - print("\0\33[33mWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\0\33[0m") + print( + "\0\33[33m\nWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield vector elements = {rawy}\n") if not (np.all(unc_rawy[1:] > unc_rawy[:-1]) or np.all(unc_rawy[1:] < unc_rawy[:-1])): all_vectors_monotonous = MinimisationStatus.MonotonyViolation - print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m") + print( + "\0\33[33m\nWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield uncertainties vector elements = {unc_rawy}\n") if not (np.all(effp[1:] > effp[:-1]) or np.all(effp[1:] < effp[:-1])): - sys.exit("\33[31mFatal error: the prompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the prompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"prompt efficiency vector elements = {effp}\n") if not (np.all(effnp[1:] > effnp[:-1]) or np.all(effnp[1:] < effnp[:-1])): - sys.exit("\33[31mFatal error: the nonprompt efficiency vector is not monotonous. Check the input. Exit.\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the nonprompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"nonprompt efficiency vector elements = {effnp}\n") minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp) status = minimiser.minimise_system(cfg["minimisation"]["correlated"]) @@ -290,7 +331,7 @@ def main(config): hist_frac_raw_nonprompt.SetBinContent(ipt + 1, raw_frac_nonprompt[0]) hist_frac_raw_nonprompt.SetBinError(ipt + 1, raw_frac_nonprompt[1]) - hist_bin_title = f"bin # {ipt+1}; {pt_axis_title}#in ({pt_min}; {pt_max})" + hist_bin_title = f"bin # {ipt + 1}; {pt_axis_title}#in ({pt_min}; {pt_max})" hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else "" canv_rawy, histos_rawy, _leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) @@ -298,32 +339,36 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_rawy.Write() if is_save_to_root_file[ObjectToSave.RawYield]: - for _, hist in histos_rawy.values(): + for hist in histos_rawy.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Rawy]: - canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") + canv_rawy.SaveAs(f"canv_rawy_{ipt + 1}.C") hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" - canv_unc, histos_unc, _leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) + canv_unc, histos_unc, _leg_unc = minimiser.plot_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc + ) output.cd() if is_save_to_root_file[ObjectToSave.Canvas]: canv_unc.Write() if is_save_to_root_file[ObjectToSave.Uncertainty]: - for _, hist in histos_unc.values(): + for hist in histos_unc.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Unc]: - canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") + canv_unc.SaveAs(f"canv_unc_{ipt + 1}.C") hist_bin_title_rel_unc = hist_bin_title if is_draw_title[PlotType.RelUnc] else "" - canv_rel_unc, histos_rel_unc, _leg_rel_unc = minimiser.plot_relative_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rel_unc) + canv_rel_unc, histos_rel_unc, _leg_rel_unc = minimiser.plot_relative_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rel_unc + ) output.cd() if is_save_to_root_file[ObjectToSave.Canvas]: canv_rel_unc.Write() if is_save_to_root_file[ObjectToSave.Uncertainty]: - for _, hist in histos_rel_unc.values(): + for hist in histos_rel_unc.values(): hist.Write() if is_save_canvas_as_macro[PlotType.RelUnc]: - canv_rel_unc.SaveAs(f"canv_rel_unc_{ipt+1}.C") + canv_rel_unc.SaveAs(f"canv_rel_unc_{ipt + 1}.C") hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" canv_eff, histos_eff, _leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) @@ -331,10 +376,10 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_eff.Write() if is_save_to_root_file[ObjectToSave.Efficiency]: - for _, hist in histos_eff.values(): + for hist in histos_eff.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Eff]: - canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") + canv_eff.SaveAs(f"canv_eff_{ipt + 1}.C") hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" canv_frac, histos_frac, _leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) @@ -342,10 +387,10 @@ def main(config): if is_save_to_root_file[ObjectToSave.Canvas]: canv_frac.Write() if is_save_to_root_file[ObjectToSave.Fraction]: - for _, hist in histos_frac.values(): + for hist in histos_frac.values(): hist.Write() if is_save_canvas_as_macro[PlotType.Frac]: - canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") + canv_frac.SaveAs(f"canv_frac_{ipt + 1}.C") hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) @@ -355,7 +400,7 @@ def main(config): if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: histo_cov.Write() if is_save_canvas_as_macro[PlotType.Cov]: - canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") + canv_cov.SaveAs(f"canv_cov_{ipt + 1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail) @@ -377,7 +422,7 @@ def main(config): canv_combined.cd(4) canv_cov.DrawClonePad() - output_name_template = output_name_template.replace('.root', '.pdf') + output_name_template = output_name_template.replace(".root", ".pdf") output_name_rawy_pdf = f"Distr_{output_name_template}" output_name_eff_pdf = f"Eff_{output_name_template}" diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index 8a6c31a96b4..a361da090d6 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -13,7 +13,7 @@ import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') +sys.path.insert(0, "..") from style_formatter import set_global_style, set_object_style @@ -23,6 +23,7 @@ class MinimisationStatus(IntEnum): MonotonyViolation = auto() Fail = auto() + # pylint: disable=too-many-instance-attributes class CutVarMinimiser: """ @@ -176,10 +177,9 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) ) if correlated and unc_row > 0 and unc_col > 0: - self.m_cov_sets[i_row, i_col] = min(unc_row **2, unc_col ** 2) + self.m_cov_sets[i_row, i_col] = min(unc_row**2, unc_col**2) else: - self.m_cov_sets[i_row, i_col] = unc_row ** 2 if i_row == i_col else 0.0 - + self.m_cov_sets[i_row, i_col] = unc_row**2 if i_row == i_col else 0.0 self.m_cov_sets = np.matrix(self.m_cov_sets) try: @@ -209,13 +209,17 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) m_corr_yields_old = np.copy(self.m_corr_yields) - print(f"INFO: number of processed iterations = {iteration+1}\n") + print(f"INFO: number of processed iterations = {iteration + 1}\n") minimisation_status = MinimisationStatus.Success if correlated: m_cov_sets_diag = np.diag(self.m_cov_sets) - if not (np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1])): + if not ( + np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1]) + ): minimisation_status = MinimisationStatus.MonotonyViolation - print("\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m") + print( + "\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m" + ) print(f"residual vector uncertainties elements = {np.sqrt(m_cov_sets_diag)}\n") # chi2 @@ -351,8 +355,7 @@ def get_raw_prompt_fraction(self, effacc_p, effacc_np): return f_p, f_p_unc - def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np): """ Helper function to get the raw prompt fraction given the efficiencies @@ -386,11 +389,7 @@ def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, # derivatives of prompt fraction wrt corr yields d_p = (effacc_p * (rawy_p + rawy_np) - effacc_p**2 * corry_p) / (rawy_p + rawy_np) ** 2 d_np = -effacc_np * rawy_p / (rawy_p + rawy_np) ** 2 - f_p_unc = np.sqrt( - d_p**2 * unc_corry_p**2 - + d_np**2 * unc_corry_np**2 - + 2 * d_p * d_np * cov_p_np - ) + f_p_unc = np.sqrt(d_p**2 * unc_corry_p**2 + d_np**2 * unc_corry_np**2 + 2 * d_p * d_np * cov_p_np) return f_p, f_p_unc @@ -417,8 +416,9 @@ def get_raw_nonprompt_fraction(self, effacc_p, effacc_np): return f_np, f_np_unc - def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_nonprompt_fraction_ext( + self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ): """ Helper function to get the raw non-prompt fraction given the efficiencies @@ -446,8 +446,9 @@ def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, """ - f_p, f_np_unc = self.get_raw_prompt_fraction_ext(corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np) + f_p, f_np_unc = self.get_raw_prompt_fraction_ext( + corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ) f_np = 1 - f_p return f_np, f_np_unc @@ -628,7 +629,7 @@ def plot_cov_matrix(self, correlated=True, suffix="", title=""): padleftmargin=0.14, padbottommargin=0.12, padrightmargin=0.12, - padtopmargin = 0.075, + padtopmargin=0.075, palette=ROOT.kRainBow, ) @@ -683,7 +684,7 @@ def plot_efficiencies(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_eff_prompt = ROOT.TH1F( f"hEffPromptVsCut{suffix}", @@ -776,7 +777,7 @@ def plot_fractions(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_f_prompt = ROOT.TH1F( f"hFracPromptVsCut{suffix}", @@ -887,7 +888,7 @@ def plot_uncertainties(self, suffix="", title=""): for i_bin, (unc_rawy, unc_res) in enumerate(zip(self.unc_raw_yields, m_cov_sets_diag)): hist_raw_yield_unc.SetBinContent(i_bin + 1, unc_rawy) - hist_residual_unc.SetBinContent(i_bin+1, unc_res) + hist_residual_unc.SetBinContent(i_bin + 1, unc_res) set_object_style(hist_raw_yield_unc, color=ROOT.kRed + 1, fillstyle=0) set_object_style(hist_residual_unc, color=ROOT.kAzure + 4, fillstyle=0) @@ -923,7 +924,6 @@ def plot_uncertainties(self, suffix="", title=""): return canvas, histos, leg - # pylint: disable=no-member def plot_relative_uncertainties(self, suffix="", title=""): """ @@ -974,10 +974,19 @@ def plot_relative_uncertainties(self, suffix="", title=""): self.n_sets - 0.5, ) - for i_bin, (unc_rawy, rawy, unc_eff_prompt, eff_prompt, unc_eff_nonprompt, eff_nonprompt) in enumerate(zip(self.unc_raw_yields, self.raw_yields, self.unc_eff_prompt, self.eff_prompt, self.unc_eff_nonprompt, self.eff_nonprompt)): + for i_bin, (unc_rawy, rawy, unc_eff_prompt, eff_prompt, unc_eff_nonprompt, eff_nonprompt) in enumerate( + zip( + self.unc_raw_yields, + self.raw_yields, + self.unc_eff_prompt, + self.eff_prompt, + self.unc_eff_nonprompt, + self.eff_nonprompt, + ) + ): hist_raw_yield_rel_unc.SetBinContent(i_bin + 1, unc_rawy / rawy) - hist_eff_prompt_rel_unc.SetBinContent(i_bin+1, unc_eff_prompt / eff_prompt) - hist_eff_nonprompt_rel_unc.SetBinContent(i_bin+1, unc_eff_nonprompt / eff_nonprompt) + hist_eff_prompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_prompt / eff_prompt) + hist_eff_nonprompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_nonprompt / eff_nonprompt) set_object_style(hist_raw_yield_rel_unc, color=ROOT.kBlack, fillstyle=0) set_object_style(hist_eff_prompt_rel_unc, color=ROOT.kRed + 1, fillstyle=0) @@ -988,7 +997,12 @@ def plot_relative_uncertainties(self, suffix="", title=""): -0.5, 0.0, self.n_sets - 0.5, - max(hist_raw_yield_rel_unc.GetMaximum(), hist_eff_prompt_rel_unc.GetMaximum(), hist_eff_nonprompt_rel_unc.GetMaximum()) * 1.2, + max( + hist_raw_yield_rel_unc.GetMaximum(), + hist_eff_prompt_rel_unc.GetMaximum(), + hist_eff_nonprompt_rel_unc.GetMaximum(), + ) + * 1.2, ";cut set;relative unc.", ) leg = ROOT.TLegend(0.2, 0.75, 0.4, 0.85) @@ -1012,7 +1026,7 @@ def plot_relative_uncertainties(self, suffix="", title=""): histos = { "rawy": hist_raw_yield_rel_unc, "prompt": hist_eff_prompt_rel_unc, - "nonprompt": hist_eff_nonprompt_rel_unc + "nonprompt": hist_eff_nonprompt_rel_unc, } return canvas, histos, leg From 43139069ba040f2262b6c272519826b7d86d8f2f Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Mon, 2 Nov 2026 19:25:37 +0100 Subject: [PATCH 538/648] [DPG] update cuts and names of occupancy qa histograms; cleanups (#18558) --- DPG/Tasks/AOTEvent/eventSelectionQa.cxx | 149 +++++++----------------- 1 file changed, 45 insertions(+), 104 deletions(-) diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index 752a81885ca..eb2896efb16 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -73,7 +73,6 @@ struct EventSelectionQaTask { Configurable nGlobalBCs{"nGlobalBCs", 100000, "number of global bcs for detailed monitoring"}; Configurable isLowFlux{"isLowFlux", 1, "1 - low flux (pp, pPb), 0 - high flux (PbPb)"}; Configurable fillITSdeadStaveHists{"fillITSdeadStaveHists", 0, "0 - no, 1 - yes"}; - Configurable fillTPCnClsVsOccupancyHists{"fillTPCnClsVsOccupancyHists", 0, "0 - no, 1 - yes"}; Configurable useTestWithTrackTools{"useTestWithTrackTools", 0, "0 - no, 1 - yes"}; // experimental Configurable thresholdNrowsForTrackToolTest{"thresholdNrowsForTrackToolTest", 120, "threshold for Nrows for TrackTool test"}; // minimum number of TPC rows (experimental) @@ -385,40 +384,20 @@ struct EventSelectionQaTask { histos.add("occupancyQA/hOccupancyByFT0CvsByTracks", "", kTH2D, {{150, 0, 15000}, {150, 0, 150000}}); // 3D histograms: nGlobalTracks with cls567 as y-axis, V0A as x-axis: - const AxisSpec axisNtracksPV{200, -0.5, 5000 - 0.5, "n ITS PV tracks"}; - const AxisSpec axisNtracksPVTPC{160, -0.5, 4000 - 0.5, "n ITS-TPC PV tracks"}; + const AxisSpec axisNtracks{240, -0.5, 6000 - 0.5, "n ITS PV tracks"}; const AxisSpec axisNtracksTPConly{160, -0.5, 8000 - 0.5, "n TPC-only tracks"}; const AxisSpec axisMultV0AForOccup{20, 0., static_cast(200000), "mult V0A"}; const AxisSpec axisOccupancyTracks{150, 0., 15000, "occupancy (n ITS tracks weighted)"}; - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + // requested by TPC experts: check with tracks at given NrowsThreshold if (useTestWithTrackTools) { - histos.add("occupancyQA/hNumTracksITS_isValidAtNrowsThreshold_EtaPtCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_isValidAtNrowsThreshold_EtaPtSel8VxCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - } - // requested by TPC experts: nTPConly tracks vs occupancy - histos.add("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - // request from experts to add track properties vs occupancy, to compare data vs MC - if (fillTPCnClsVsOccupancyHists) { - const AxisSpec axisOccupancyForTrackQA{60, 0., 15000, "occupancy (n ITS tracks weighted)"}; - const AxisSpec axisNTPCcls{150, 0, 150, "n TPC clusters"}; - histos.add("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - const AxisSpec axisChi2TPC{150, 0, 15, "chi2Ncl TPC"}; - histos.add("occupancyQA/tpcChi2_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisChi2TPC, axisOccupancyForTrackQA}); + histos.add("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); } // ITS in-ROF occupancy @@ -1193,16 +1172,13 @@ struct EventSelectionQaTask { // count tracks of different types auto tracksGrouped = tracks.sliceBy(perCollision, colIndex); int nPV = 0; - int nTPConly = 0; - // int nTPConlyWithDeDxCut = 0; - int nTPConlyNoITS = 0; - int nContributorsAfterEtaTPCCuts = 0; - int nContributorsAfterEtaTPCLooseCuts = 0; + int nPVafterTPCcuts = 0; + int nPVhasTPC = 0; - int nTracksITS = 0; - int nTracksITSTPC = 0; - int nTracksITSValidAtNrowsThresholdAfterEtaPtCuts = 0; - int nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts = 0; + int nITS = 0; + int nITSTPC = 0; + int nITSatNTPCrowsThrsh = 0; + int nPVatNTPCrowsThrsh = 0; bool isTVX = col.selection_bit(kIsTriggerTVX); @@ -1212,14 +1188,6 @@ struct EventSelectionQaTask { for (const auto& track : tracksGrouped) { int trackBcDiff = bcDiff + track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS; - if (track.hasTPC() && std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 50 && track.tpcChi2NCl() < 4) { - nTPConly++; - // if (track.tpcSignal() > 20) - // nTPConlyWithDeDxCut++; - if (!track.hasITS()) - nTPConlyNoITS++; - } - // Oct 2026: test presence of track at 85 cm radius using new tools - https://github.com/AliceO2Group/AliceO2/tree/dev/Common/TrackTools bool isTrackWithNrowsAvailable = false; if (useTestWithTrackTools) { @@ -1241,11 +1209,11 @@ struct EventSelectionQaTask { if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2) { if (track.hasITS()) { - nTracksITS++; + nITS++; if (track.hasTPC()) - nTracksITSTPC++; + nITSTPC++; if (useTestWithTrackTools && isTrackWithNrowsAvailable) { - nTracksITSValidAtNrowsThresholdAfterEtaPtCuts++; + nITSatNTPCrowsThrsh++; } } } @@ -1253,40 +1221,28 @@ struct EventSelectionQaTask { if (!track.isPVContributor()) continue; + // high-quality contributors for ROF border QA and occupancy study + if (col.sel8() && fabs(col.posZ()) < 10) { + if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { + nPV++; + if (track.hasTPC()) { + nPVhasTPC++; + if (track.tpcNClsFound() > 50) { + nPVafterTPCcuts++; + // ROF border QA + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); + } + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nPVatNTPCrowsThrsh++; + } + } // end of hasTPC + } + } // end of sel8 and vZ cuts + if (track.itsNCls() >= 5) vTracksITS567perColl[colIndex]++; - // high-quality contributors for ROF border QA and occupancy study - if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { - nPV++; - if (track.hasTPC()) { - nContributorsAfterEtaTPCLooseCuts++; - - if (!isLowFlux && fillTPCnClsVsOccupancyHists && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10 && occupancyByTracks >= 0) { - histos.fill(HIST("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFound(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsShared(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcChi2_vs_V0A_vs_occupancy"), multV0A, track.tpcChi2NCl(), occupancyByTracks); - int tpcNClsFindableMinusCrossedRowsCorrected = track.tpcNClsFindableMinusCrossedRows(); - // correct for a buggy behaviour due to int8 and uint8 difference: - if (tpcNClsFindableMinusCrossedRowsCorrected < -70) - tpcNClsFindableMinusCrossedRowsCorrected += 256; - histos.fill(HIST("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable() - tpcNClsFindableMinusCrossedRowsCorrected, occupancyByTracks); - } - } // end of hasTPC - if (col.sel8() && fabs(col.posZ()) < 10) { - if (track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { - nContributorsAfterEtaTPCCuts++; - // ROF border QA - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); - } - - if (useTestWithTrackTools && isTrackWithNrowsAvailable) { - nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts++; - } - } // end of sel8 and vZ cuts - } if (!track.hasTPC()) histos.fill(HIST("hITStrackBcDiff"), trackBcDiff); if (track.hasTOF()) { @@ -1302,38 +1258,23 @@ struct EventSelectionQaTask { histos.fill(HIST("hSecondsTVXvsBcDif"), bc.timestamp() / 1000., trackBcDiff); } // end of track loop - histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nContributorsAfterEtaTPCCuts); + histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nPVafterTPCcuts); if (!isLowFlux && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10) { histos.fill(HIST("occupancyQA/hOccupancyByTracks"), occupancyByTracks); histos.fill(HIST("occupancyQA/hOccupancyByFT0C"), occupancyByFT0C); if (occupancyByTracks >= 0) { histos.fill(HIST("occupancyQA/hOccupancyByFT0CvsByTracks"), occupancyByTracks, occupancyByFT0C); + histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nITS, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nITSTPC, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCLooseCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nTracksITS, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nTracksITSTPC, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy"), multV0A, nPVhasTPC, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nPVafterTPCcuts, occupancyByTracks); if (useTestWithTrackTools) { - histos.fill(HIST("occupancyQA/hNumTracksITS_isValidAtNrowsThreshold_EtaPtCuts_vs_V0A_vs_occupancy"), multV0A, nTracksITSValidAtNrowsThresholdAfterEtaPtCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPV_isValidAtNrowsThreshold_EtaPtSel8VxCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAtNrowsThresholdAfterEtaPtSel8VxCuts, occupancyByTracks); - } - - if (col.selection_bit(kNoCollInTimeRangeNarrow)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kNoCollInTimeRangeStandard)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kIsGoodITSLayersAll)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nITSatNTPCrowsThrsh, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nPVatNTPCrowsThrsh, occupancyByTracks); } - histos.fill(HIST("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy"), multV0A, nTPConly, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy"), multV0A, nTPConlyNoITS, occupancyByTracks); // dE/dx QA for a narrow pT bin for (const auto& track : tracksGrouped) { @@ -1341,7 +1282,7 @@ struct EventSelectionQaTask { continue; if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { float signedP = track.sign() * track.tpcInnerParam(); - if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { + if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50) { float dEdx = track.tpcSignal(); histos.fill(HIST("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win"), nPV, occupancyByTracks, dEdx); } From 32349d436bb558c3efbdc498e269475ce4f93247 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Tue, 3 Nov 2026 17:13:31 +0900 Subject: [PATCH 539/648] [PWGLF] Omega2012: Use double-precision histograms for event counters and mass-pT-cent (#18560) Co-authored-by: Bong-Hwi Lim --- PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index ec08e498c86..bc7b2dd433e 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -165,13 +165,15 @@ struct Omega2012Analysis { AxisSpec omegaKinPtAxis = {500, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; // Event QA histograms + // Event counters are double precision (kTH1D): a single-precision bin stops counting at 2^24 entries per job, + // which truncated the zero-multiplicity bins of the multiplicity counters in large pp productions. histos.add("Event/posZ", "Event vertex Z position", kTH1F, {{200, -20., 20., "V_{z} (cm)"}}); - histos.add("Event/centrality", "Event centrality distribution", kTH1F, {centAxis}); + histos.add("Event/centrality", "Event centrality distribution", kTH1D, {centAxis}); histos.add("Event/posZvsCent", "Vertex Z vs Centrality", kTH2F, {{200, -20., 20., "V_{z} (cm)"}, centAxis}); - histos.add("Event/nCascades", "Number of cascades per event", kTH1F, {{100, 0., 100., "N_{cascades}"}}); - histos.add("Event/nV0s", "Number of V0s per event", kTH1F, {{200, 0., 200., "N_{V0s}"}}); - histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1F, {{50, 0., 50., "N_{cascades}"}}); - histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1F, {{100, 0., 100., "N_{V0s}"}}); + histos.add("Event/nCascades", "Number of cascades per event", kTH1D, {{100, 0., 100., "N_{cascades}"}}); + histos.add("Event/nV0s", "Number of V0s per event", kTH1D, {{200, 0., 200., "N_{V0s}"}}); + histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1D, {{50, 0., 50., "N_{cascades}"}}); + histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1D, {{100, 0., 100., "N_{V0s}"}}); // Xi QA histograms histos.add("QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); @@ -234,8 +236,8 @@ struct Omega2012Analysis { // Resonance (2-body decay: Xi + K0s) histos.add("omega2012/invmass", "Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); histos.add("omega2012/invmass_Mix", "Mixed event Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); - histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); histos.add("QAbefore/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} before kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); histos.add("QAafter/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} after kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); @@ -288,9 +290,9 @@ struct Omega2012Analysis { // Signal charge pattern: Xi- pi+ K- and Xi+ pi- K+ histos.add("xi1530K/invmass", "Invariant mass of Omega(2012) → #Xi(1530)^{0} K → #Xi #pi K", kTH1F, {invMassAxis}); - histos.add("xi1530K/massPtCent", "Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/massPtCent", "Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); histos.add("xi1530K/invmass_Mix", "Mixed event invariant mass of Omega(2012) → #Xi(1530)^{0} K", kTH1F, {invMassAxis}); - histos.add("xi1530K/massPtCent_Mix", "Mixed event Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/massPtCent_Mix", "Mixed event Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); histos.add("xi1530K/massXiPi", "#Xi #pi mass of selected candidates (before the rapidity cut)", kTH1F, {xiPiMassAxis}); histos.add("xi1530K/massXiPiVsMass", "#Xi #pi mass vs #Xi #pi K mass", kTH2F, {invMassAxis, xiPiMassAxis}); @@ -828,7 +830,7 @@ struct Omega2012Analysis { PROCESS_SWITCH(Omega2012Analysis, processXi1530KMixedMicro, "Process mixed events of the Xi(1530)0 K mode (ResoMicroTracks_001)", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } From 302a5d2cfee9a0d3de2e066a332c60be43e9fc57 Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Tue, 3 Nov 2026 09:59:35 +0100 Subject: [PATCH 540/648] [PWGLF] Enabling PID depependent misidentification studies in MC (#18551) Co-authored-by: Sushanta Tripathy --- PWGLF/Tasks/Resonances/lambdastarproxy.cxx | 400 ++++++++++++++++++++- 1 file changed, 395 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index a900e07e9c3..2ff5ad926a9 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -198,6 +199,19 @@ struct Lambdastarproxy { Configurable lstarSparseMultBins{"lstarSparseMultBins", 100, "Multiplicity-axis bins in all invariant-mass sparse histograms; keep identical across merge inputs"}; Configurable lstarMCPrimaryOnly{"lstarMCPrimaryOnly", 1, "Generated p/K/d fiducial spectra: require physical primaries (strong-decay daughters included)"}; + // Optional diagnostics leave the original selections and histograms intact. + Configurable lstarEnableMCProxySpecies{"lstarEnableMCProxySpecies", 0, "Book selected MC proxy species x TOF/proton/multiplicity states and retained-parent controls"}; + Configurable lstarEnableProxyPIDProbes{"lstarEnableProxyPIDProbes", 0, "Book separate loose-TPC/TOF matched pair probes for PID-versus-proxy-mass fits (data or reconstructed MC)"}; + Configurable lstarProxyProbeTPCDeMax{"lstarProxyProbeTPCDeMax", 6.f, "Loose d-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTPCKaMax{"lstarProxyProbeTPCKaMax", 6.f, "Loose K-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTOFDeMax{"lstarProxyProbeTOFDeMax", 8.f, "Loose d-slot TOF envelope for PID probes; 0 < cut <= 8"}; + Configurable lstarProxyProbeTOFKaMax{"lstarProxyProbeTOFKaMax", 8.f, "Loose K-slot TOF envelope for PID probes; 0 < cut <= 8"}; + + struct RetainedMother { + int64_t id = -1; + int pdg = 0; + }; + struct TruthInfo { int64_t id = -1; int collisionId = -1; @@ -205,10 +219,12 @@ struct Lambdastarproxy { float px = 0.f, py = 0.f, pz = 0.f; bool primary = false; std::vector lambdaMothers; + std::vector diagnosticMothers; }; struct KaonCand { float px = 0.f, py = 0.f, pz = 0.f; + bool hasTOF = false; int charge = 0; int tid = -1; TruthInfo truth{}; @@ -955,6 +971,7 @@ struct Lambdastarproxy { HistType::kTH2F, {ptAxis, dcaZAxis}); } bookMC(); + bookProxyDiagnostics(); } static constexpr int PdgLambdaStar = static_cast(PDG_t::kLambda1520); @@ -964,6 +981,365 @@ struct Lambdastarproxy { std::array, 2>, 4> mcRecoProxy{}; std::shared_ptr mcRecoPKTrue, mcPKResponse, mcProxyResponse; + // Fixed bounded diagnostics: the four analysis pT bins plus ROOT flow bins. + // Existing native 1-MeV/0.1-GeV histograms are not changed or replaced. + static constexpr std::array DiagnosticPairPtEdges{0.6, 1.3, 1.4, 1.9, 3.6}; + static constexpr std::array ProbeMomentumEdges{0., 0.5, 0.8, 1.2, 1.8, 2.5, 3.5, 5., 10.}; + static constexpr std::array SpeciesNames{"e", "mu", "pi", "K", "p", "d", "remaining"}; + static constexpr int SpeciesCount = SpeciesNames.size(); + static constexpr int SpeciesPairs = SpeciesCount * SpeciesCount; + static constexpr int UnmatchedSpeciesTag = SpeciesPairs; + static constexpr int ChargeMismatchSpeciesTag = SpeciesPairs + 1; + static constexpr int SpeciesTagCount = SpeciesPairs + 2; + static constexpr int ProtonSpecies = 4; + static constexpr int DeuteronSpecies = 5; + static constexpr int KaonSpecies = 3; + static constexpr int SelectedPIDStates = 16; + static constexpr int ProbeRegions = 4; + static constexpr int ProbePairPtBins = DiagnosticPairPtEdges.size() - 1; + static constexpr int ProbeMomentumBins = ProbeMomentumEdges.size(); // includes p >= 10 + static constexpr int ProbeTagCount = ProbePairPtBins * ProbeMomentumBins * ProbeRegions; + static constexpr int ParentClasses = 10; + static constexpr int DiagnosticMassBins = 200; + static constexpr int ProbeMassBins = 100; + static constexpr float ProbeTPCMax = 6.f; + static constexpr float ProbeTOFMax = 8.f; + static constexpr double MultMin = 0.; + static constexpr double MultMax = 100.; + static constexpr std::array PairSigns{"Unlike", "Like"}; + static constexpr std::array ProbeRoles{"De", "Ka"}; + static constexpr std::array ParentNames{ + "no retained common mother", "K0S", "Lambda1115", "Kstar", "phi", "Lambda1520", + "other retained common mother", "multiple retained common mothers", "same MC particle", "invalid truth labels"}; + + std::array, 2> mcProxySpecies{}; + std::array, 2> mcProxyOtherParents{}; + std::array, 2>, 2> probeMass2{}, probeTPC{}, probeTOF{}; + std::array, 2> probePairRegions{}, mcProbeSpecies{}; + std::shared_ptr probeTrackStatus; + std::shared_ptr probeEventScope; + + struct PIDProbeCand { + float px = 0.f, py = 0.f, pz = 0.f; + double momentum = 0., mass2 = 0.; + float nsTPC = 0.f, nsTOF = 0.f; + bool nominal = false; + int charge = 0; + int64_t tid = -1; + TruthInfo truth{}; + }; + + static bool regularMultiplicity(float mult) + { + return std::isfinite(mult) && mult >= MultMin && mult < MultMax; + } + + static int speciesGroup(int pdg) + { + switch (std::abs(pdg)) { + case PdgElectron: + return 0; + case PdgMuon: + return 1; + case PdgPion: + return 2; + case PdgKaon: + return KaonSpecies; + case PdgProton: + return ProtonSpecies; + case PdgDeuteron: + return DeuteronSpecies; + default: + return SpeciesCount - 1; + } + } + + int speciesPairTag(TruthInfo const& a, TruthInfo const& b, int chargeA, int chargeB) + { + if (a.id < 0 || b.id < 0) { + return UnmatchedSpeciesTag; + } + if (chargeFromPdg(a.pdg) != chargeA || chargeFromPdg(b.pdg) != chargeB) { + return ChargeMismatchSpeciesTag; + } + return SpeciesCount * speciesGroup(a.pdg) + speciesGroup(b.pdg); + } + + static bool otherSpeciesTag(int tag) + { + return tag != UnmatchedSpeciesTag && tag != SpeciesCount * DeuteronSpecies + KaonSpecies && tag != SpeciesCount * ProtonSpecies + KaonSpecies; + } + + static int retainedParentClass(TruthInfo const& a, TruthInfo const& b) + { + if (a.id < 0 || b.id < 0) { + return ParentClasses - 1; + } + if (a.id == b.id) { + return ParentClasses - 2; + } + int64_t commonId = -1; + int commonPdg = 0; + for (auto const& ma : a.diagnosticMothers) { + for (auto const& mb : b.diagnosticMothers) { + if (ma.id != mb.id) { + continue; + } + if (commonId >= 0 && commonId != ma.id) { + return ParentClasses - 3; + } + commonId = ma.id; + commonPdg = std::abs(ma.pdg); + } + } + if (commonId < 0) { + return 0; // absence of a retained link does not establish absence of ancestry + } + if (commonPdg == static_cast(PDG_t::kK0Short)) { + return 1; + } + if (commonPdg == static_cast(PDG_t::kLambda0)) { + return 2; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kK0Star892) || commonPdg == static_cast(o2::constants::physics::Pdg::kKPlusStar892)) { + return 3; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kPhi)) { + return 4; + } + if (commonPdg == PdgLambdaStar) { + return 5; + } + return 6; + } + + static int probePairPtBin(float pt) + { + if (!std::isfinite(pt) || pt < DiagnosticPairPtEdges.front() || pt >= DiagnosticPairPtEdges.back()) { + return -1; + } + return static_cast(std::upper_bound(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end(), pt) - DiagnosticPairPtEdges.begin()) - 1; + } + + static int probeMomentumBin(double momentum) + { + return std::clamp(static_cast(std::upper_bound(ProbeMomentumEdges.begin(), ProbeMomentumEdges.end(), momentum) - ProbeMomentumEdges.begin()) - 1, 0, ProbeMomentumBins - 1); + } + + static int probeTag(int pairBin, double momentum, int region) + { + return pairBin + ProbePairPtBins * (probeMomentumBin(momentum) + ProbeMomentumBins * region); + } + + static std::string speciesTagName(int tag) + { + if (tag == UnmatchedSpeciesTag) { + return "unmatched"; + } + if (tag == ChargeMismatchSpeciesTag) { + return "charge-inconsistent"; + } + return std::string(SpeciesNames[tag / SpeciesCount]) + ":" + SpeciesNames[tag % SpeciesCount]; + } + + void bookProxyDiagnostics() + { + if (lstarEnableMCProxySpecies.value != 0 && !doprocessMCReco) { + LOGF(fatal, "lstarEnableMCProxySpecies requires processMCReco"); + } + if (lstarEnableProxyPIDProbes.value != 0) { + if (!doprocess && !doprocessMCReco) { + LOGF(fatal, "PID probes require the data or reconstructed-MC process"); + } + for (auto const& cut : {lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTPCMax) { + LOGF(fatal, "Probe TPC limits must lie in (0,6]"); + } + } + for (auto const& cut : {lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTOFMax) { + LOGF(fatal, "Probe TOF limits must lie in (0,8]"); + } + } + } + const AxisSpec pairPt{std::vector(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end()), "Proxy pair pT (GeV/c)"}; + const AxisSpec mcMass{DiagnosticMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + const AxisSpec probeMass{ProbeMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + uint64_t payload = 0; + auto add3 = [&](std::string const& name, std::string const& title, std::vector const& axes) { + auto h = histos.add(name.c_str(), title.c_str(), HistType::kTH3D, axes, true); + payload += static_cast(h->GetNcells()) * 2 * sizeof(double); + return h; + }; + for (size_t sign = 0; sign < PairSigns.size(); ++sign) { + const std::string suffix = PairSigns[sign]; + if (lstarEnableMCProxySpecies.value != 0) { + mcProxySpecies[sign] = add3("hMCProxySpecies" + suffix, "Selected proxy;species+51*(dTOF+2*kTOF+4*pTPC+8*regularMult)", + {mcMass, pairPt, AxisSpec{SpeciesTagCount * SelectedPIDStates, -0.5, SpeciesTagCount * SelectedPIDStates - 0.5, "Species and detector state"}}); + for (int state = 0; state < SelectedPIDStates; ++state) { + for (int tag = 0; tag < SpeciesTagCount; ++tag) { + const std::string label = speciesTagName(tag) + ";state=" + std::to_string(state); + mcProxySpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * state + 1, label.c_str()); + } + } + mcProxyOtherParents[sign] = add3("hMCProxyOtherParents" + suffix, "Selected Other: retained immediate common-mother class only", + {mcMass, pairPt, AxisSpec{ParentClasses, -0.5, ParentClasses - 0.5, "Retained parent class"}}); + for (int parent = 0; parent < ParentClasses; ++parent) { + mcProxyOtherParents[sign]->GetZaxis()->SetBinLabel(parent + 1, ParentNames[parent]); + } + } + if (lstarEnableProxyPIDProbes.value == 0) { + continue; + } + probePairRegions[sign] = add3("hProxyPIDProbeRegions" + suffix, "Loose matched PID probes;regular multiplicity only;region=dNominal+2*kNominal", + {probeMass, pairPt, AxisSpec{ProbeRegions, -0.5, ProbeRegions - 0.5, "Nominal-selection region"}}); + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + const std::string stem = std::string("hProxyPIDProbe") + ProbeRoles[role]; + const AxisSpec m2 = role == 0 ? AxisSpec{120, -1., 7., "Full-track TOF mass2 (GeV2/c4)"} : AxisSpec{80, -1., 2., "Full-track TOF mass2 (GeV2/c4)"}; + const AxisSpec packed{ProbeTagCount, -0.5, ProbeTagCount - 0.5, "pairBin+4*(momentumBin+9*region)"}; + probeMass2[role][sign] = add3(std::string(stem).append("Mass2").append(suffix), "Loose both-TOF proxy probes;TPC/TOF envelopes;regular multiplicity", {probeMass, m2, packed}); + probeTPC[role][sign] = add3(std::string(stem).append("TPC").append(suffix), "Same loose both-TOF proxy probes;TPC nSigma;regular multiplicity", {probeMass, AxisSpec{60, -ProbeTPCMax, ProbeTPCMax, "TPC nSigma for slot hypothesis"}, packed}); + probeTOF[role][sign] = add3(std::string(stem).append("TOF").append(suffix), "Same loose both-TOF proxy probes;TOF nSigma;regular multiplicity", {probeMass, AxisSpec{80, -ProbeTOFMax, ProbeTOFMax, "TOF nSigma for slot hypothesis"}, packed}); + for (int region = 0; region < ProbeRegions; ++region) { + for (int momentum = 0; momentum < ProbeMomentumBins; ++momentum) { + for (int pair = 0; pair < ProbePairPtBins; ++pair) { + const int tag = pair + ProbePairPtBins * (momentum + ProbeMomentumBins * region); + const std::string label = "pair=" + std::to_string(pair) + ";p=" + std::to_string(momentum) + ";region=" + std::to_string(region); + probeMass2[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + probeTPC[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + probeTOF[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + } + } + } + } + if (doprocessMCReco) { + mcProbeSpecies[sign] = add3("hMCProxyPIDProbeSpecies" + suffix, "Same loose probe pairs;species+51*nominalRegion;regular multiplicity", + {probeMass, pairPt, AxisSpec{SpeciesTagCount * ProbeRegions, -0.5, SpeciesTagCount * ProbeRegions - 0.5, "Species plus nominal region"}}); + for (int region = 0; region < ProbeRegions; ++region) { + for (int tag = 0; tag < SpeciesTagCount; ++tag) { + const std::string label = speciesTagName(tag) + ";region=" + std::to_string(region); + mcProbeSpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * region + 1, label.c_str()); + } + } + } + } + if (lstarEnableProxyPIDProbes.value != 0) { + probeTrackStatus = histos.add("hProxyPIDProbeTrackStatus", "After quality and loose TPC;slot;stage", HistType::kTH2D, + {AxisSpec{2, -0.5, 1.5, "Slot: d=0,K=1"}, AxisSpec{5, -0.5, 4.5, "Stage"}}, true); + const std::array labels{"loose TPC", "no TOF", "invalid TOF response", "outside loose TOF", "accepted probe track"}; + for (size_t i = 0; i < labels.size(); ++i) { + probeTrackStatus->GetYaxis()->SetBinLabel(i + 1, labels[i]); + } + probeEventScope = histos.add("hProxyPIDProbeEventScope", "Selected events;regular multiplicity flag;events", HistType::kTH1D, {AxisSpec{2, -0.5, 1.5, "0=outside,1=regular [0,100)"}}, true); + } + if (payload > 0) { + LOGF(info, "Proxy diagnostic TH3 content+Sumw2 payload: %.2f MiB per wagon (fixed bins; object/axis overhead extra)", static_cast(payload) / (1024. * 1024.)); + } + } + + void fillMCProxySpecies(ProxyCand const& d, KaonCand const& k, double mass, float pt, float mult) + { + const int tag = speciesPairTag(d.truth, k.truth, d.charge, k.charge); + const int state = static_cast(d.hasTOF) + 2 * static_cast(k.hasTOF) + 4 * static_cast(d.protonTPCCompatible) + 8 * static_cast(regularMultiplicity(mult)); + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + mcProxySpecies[sign]->Fill(mass, pt, tag + SpeciesTagCount * state); + if (otherSpeciesTag(tag)) { + mcProxyOtherParents[sign]->Fill(mass, pt, retainedParentClass(d.truth, k.truth)); + } + } + + template + void fillProxyPIDProbes(TTracks const& tracks, float mult, int collisionId) + { + // This separate path must not feed nominal candidate vectors or mixing pools. + const bool regular = regularMultiplicity(mult); + probeEventScope->Fill(regular); + if (!regular) { + return; + } + std::array, 2> candidates; + for (auto const& track : tracks) { + if (track.pt() < lstarCutPtMin.value || std::abs(track.eta()) > lstarCutEtaMax.value || !passTrackQuality(track) || track.sign() == 0) { + continue; + } + const std::array tpc{track.tpcNSigmaDe(), track.tpcNSigmaKa()}; + const std::array tof{track.tofNSigmaDe(), track.tofNSigmaKa()}; + const std::array tpcMax{lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}; + const std::array tofMax{lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}; + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + if (!std::isfinite(tpc[role]) || std::abs(tpc[role]) >= tpcMax[role]) { + continue; + } + probeTrackStatus->Fill(role, 0); + if (!track.hasTOF()) { + probeTrackStatus->Fill(role, 1); + continue; + } + const double beta = track.beta(); + const double momentum = static_cast(track.pt()) * std::cosh(static_cast(track.eta())); + if (!std::isfinite(beta) || beta <= TofBetaMin || beta >= TofBetaMax || !std::isfinite(momentum) || momentum <= 0. || !std::isfinite(tof[role])) { + probeTrackStatus->Fill(role, 2); + continue; + } + if (std::abs(tof[role]) >= tofMax[role]) { + probeTrackStatus->Fill(role, 3); + continue; + } + probeTrackStatus->Fill(role, 4); + PIDProbeCand probe; + probe.px = track.pt() * std::cos(track.phi()); + probe.py = track.pt() * std::sin(track.phi()); + probe.pz = track.pt() * std::sinh(track.eta()); + probe.momentum = momentum; + probe.mass2 = momentum * momentum * (1. / (beta * beta) - 1.); + probe.nsTPC = tpc[role]; + probe.nsTOF = tof[role]; + probe.charge = static_cast(track.sign()); + probe.tid = track.globalIndex(); + if (role == 0) { + const bool passProton = passFinalCandidatePID(track.pt(), track.tpcNSigmaPr(), track.tofNSigmaPr(), true, + lstarCutNsigmaTPCPr.value, lstarCutNsigmaTOFPr.value, lstarPidCircularCutPr.value, lstarPidPtRefPr.value); + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCDe.value, lstarCutNsigmaTOFDe.value, lstarPidCircularCutDe.value, lstarPidPtRefDe.value, true) && + passOptionalDeuteronTOFExtras(track) && (lstarProxyUseProtonPIDAsDeuteron.value == 0 || passProton); + } else { + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCKaon.value, lstarCutNsigmaTOFKaon.value, lstarPidCircularCutKaon.value, lstarPidPtRefKaon.value); + } + if constexpr (IsMC) { + probe.truth = trackTruth(track, collisionId); + } + candidates[role].push_back(std::move(probe)); + } + } + for (auto const& d : candidates[0]) { + for (auto const& k : candidates[1]) { + if (d.tid == k.tid) { + continue; + } + const double mass = invariantMass(ProxyMomentumScale * d.px, ProxyMomentumScale * d.py, ProxyMomentumScale * d.pz, o2::constants::physics::MassProton, + k.px, k.py, k.pz, o2::constants::physics::MassKaonCharged); + const float px = ProxyMomentumScale * d.px + k.px, py = ProxyMomentumScale * d.py + k.py, pz = ProxyMomentumScale * d.pz + k.pz; + const float pt = ptFromPxPy(px, py); + const int pairBin = probePairPtBin(pt); + const float y = rapidityFromMomentumAndMass(px, py, pz, mass); + if (pairBin < 0 || !std::isfinite(mass) || !std::isfinite(y) || std::abs(y) > lstarLambdaAbsYMax.value) { + continue; + } + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + const int region = static_cast(d.nominal) + 2 * static_cast(k.nominal); + probePairRegions[sign]->Fill(mass, pt, region); + probeMass2[0][sign]->Fill(mass, d.mass2, probeTag(pairBin, d.momentum, region)); + probeMass2[1][sign]->Fill(mass, k.mass2, probeTag(pairBin, k.momentum, region)); + probeTPC[0][sign]->Fill(mass, d.nsTPC, probeTag(pairBin, d.momentum, region)); + probeTPC[1][sign]->Fill(mass, k.nsTPC, probeTag(pairBin, k.momentum, region)); + probeTOF[0][sign]->Fill(mass, d.nsTOF, probeTag(pairBin, d.momentum, region)); + probeTOF[1][sign]->Fill(mass, k.nsTOF, probeTag(pairBin, k.momentum, region)); + if constexpr (IsMC) { + mcProbeSpecies[sign]->Fill(mass, pt, speciesPairTag(d.truth, k.truth, d.charge, k.charge) + SpeciesTagCount * region); + } + } + } + } + bool mixingEnabled() const { return lstarEnableSparse.value != 0 && lstarNoMixedEvents.value > 0; @@ -1001,7 +1377,7 @@ struct Lambdastarproxy { } template - static TruthInfo particleTruth(TParticle const& particle) + static TruthInfo particleTruth(TParticle const& particle, bool retainDiagnosticMothers = false) { TruthInfo info; info.id = particle.globalIndex(); @@ -1012,6 +1388,9 @@ struct Lambdastarproxy { info.pz = particle.pz(); info.primary = particle.isPhysicalPrimary(); for (auto const& mother : particle.template mothers_as()) { + if (retainDiagnosticMothers && mother.mcCollisionId() == info.collisionId) { + info.diagnosticMothers.push_back({mother.globalIndex(), mother.pdgCode()}); + } if (std::abs(mother.pdgCode()) == PdgLambdaStar && mother.mcCollisionId() == info.collisionId) { info.lambdaMothers.push_back(mother.globalIndex()); } @@ -1020,7 +1399,7 @@ struct Lambdastarproxy { } template - static TruthInfo trackTruth(TTrack const& track, int collisionId) + static TruthInfo trackTruth(TTrack const& track, int collisionId, bool retainDiagnosticMothers = false) { if (collisionId < 0 || !track.has_mcParticle()) { return {}; @@ -1029,7 +1408,7 @@ struct Lambdastarproxy { if (particle.mcCollisionId() != collisionId) { return {}; // pileup or wrong-collision labels do not enter truth components } - return particleTruth(particle); + return particleTruth(particle, retainDiagnosticMothers); } static bool commonLambdaStar(TruthInfo const& proton, TruthInfo const& kaon) @@ -1566,6 +1945,13 @@ struct Lambdastarproxy { // Compute event multiplicity (FT0M or fallback) const float eventMult = eventMultiplicityFT0MOrFallback(collision, eventMultFallback); + if (lstarEnableProxyPIDProbes.value != 0) { + if constexpr (IsMC) { + fillProxyPIDProbes(tracks, eventMult, collision.mcCollisionId()); + } else { + fillProxyPIDProbes(tracks, eventMult, -1); + } + } // Deuteron candidates -> proton-proxy candidates for (auto const& trkD : tracks) { @@ -1725,7 +2111,7 @@ struct Lambdastarproxy { .tid = static_cast(trkD.globalIndex()), .truth = {}}); if constexpr (IsMC) { - proxyCands.back().truth = trackTruth(trkD, collision.mcCollisionId()); + proxyCands.back().truth = trackTruth(trkD, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); } } @@ -1867,11 +2253,12 @@ struct Lambdastarproxy { .px = pxK, .py = pyK, .pz = pzK, + .hasTOF = hasTofK, .charge = static_cast(trkK.sign()), .tid = static_cast(trkK.globalIndex()), .truth = {}}); if constexpr (IsMC) { - kaonCands.back().truth = trackTruth(trkK, collision.mcCollisionId()); + kaonCands.back().truth = trackTruth(trkK, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); } } @@ -1995,6 +2382,9 @@ struct Lambdastarproxy { if constexpr (IsMC) { fillRecoMC(pr, k, mass, ptPair, true); + if (lstarEnableMCProxySpecies.value != 0) { + fillMCProxySpecies(pr, k, mass, ptPair, eventMult); + } } const bool unlikeSignProxy = (pr.charge * k.charge) < 0; From e2a5454a4339aac85310ffaf6c98abfc5c5fb174 Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:13:23 +0100 Subject: [PATCH 541/648] [PWGEM] twoPhotonMcTask: Add prefilter test (#18554) --- .../Tasks/Pi0EtaToGammaGammaPCMEMC.cxx | 5 +- PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx | 281 +++++++++++++++++- 2 files changed, 275 insertions(+), 11 deletions(-) diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx index 8b135111b22..b076a94d3db 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx @@ -14,6 +14,7 @@ /// \author M. Hemmer, marvin.hemmer@cern.ch #include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -27,8 +28,8 @@ using namespace o2::aod; using namespace o2::framework; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyV0Photons = Filtered>; -using MyEMCClusters = soa::Join; +using MyV0Photons = Filtered>; +using MyEMCClusters = soa::Join; WorkflowSpec defineDataProcessing(ConfigContext const& context) { diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx index e2b366714fe..348f54a7868 100644 --- a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -103,6 +104,27 @@ enum class PairBucket : uint8_t { kNBuckets }; +/// \brief Origin of a photon candidate for the pi0 prefilter study: first particle above the EM steps (conversion, bremsstrahlung, shower) +enum class PhotonOrigin : uint8_t { + kFromPi0 = 0, // incl. photons of pi0 from eta -> 3 pi0 + kFromEta, + kElse +}; + +/// \brief Class of a photon pair for the pi0 prefilter study +enum class PrefilterPairClass : uint8_t { + kSameParticle = 0, // one particle seen twice (same particle bucket), never counted as pi0/eta pair + kSamePi0, // both from the same pi0: pi0 signal + kTwoPi0, // both from pi0, but from two different pi0 + kSameEta, // both from the same eta: eta signal + kTwoEta, // both from eta, but from two different eta + kPi0Eta, + kPi0Else, + kEtaElse, + kBothElse, + kNClasses +}; + /// \brief What the leading contributor of the EMC cluster is in a PCM-EMC same particle pair enum class SameParticleOrigin : uint8_t { kLegElectron = 0, // the e+- that was reconstructed as one of the V0 legs, should have been removed by track matching @@ -261,6 +283,11 @@ struct TwoPhotonMcTask { std::string prefix = "mesonConfig"; Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + Configurable prefilterMassMin{"prefilterMassMin", 0.105f, "pi0 prefilter study: lower edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterMassMax{"prefilterMassMax", 0.165f, "pi0 prefilter study: upper edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterAllPairTypes{"prefilterAllPairTypes", false, "pi0 prefilter study, processPcmEmcal: also tag with PCM-PCM and EMC-EMC pairs and fill hPrefilterAll (for PCM-PCM / EMC-EMC use processPcmPrefilterAll / processEmcalPrefilterAll)"}; + ConfigurableAxis thnConfigAxisPrefilterMass{"thnConfigAxisPrefilterMass", {200, 0., 0.8}, "pi0 prefilter study: invariant mass axis"}; + ConfigurableAxis thnConfigAxisPrefilterPt{"thnConfigAxisPrefilterPt", {VARIABLE_WIDTH, 0., 1., 1.5, 2., 2.5, 3., 4., 5., 6., 8., 10., 12., 15., 20.}, "pi0 prefilter study: pT axis"}; } mesonConfig; // same mixing as taskPi0FlowEMC, but without event plane bins: the derived MC has no Q vectors @@ -308,6 +335,12 @@ struct TwoPhotonMcTask { std::vector mChainsEMC; std::vector mTruthPCM; std::vector mTruthEMC; + // pi0 prefilter study: photon forms a pair inside the pi0 mass window with another photon of its event, index = globalIndex + std::vector mPrefilterPCM; + std::vector mPrefilterEMC; + // the same, tagged with all three pair types (PCM-PCM, EMC-EMC, PCM-EMC) + std::vector mPrefilterAllPCM; + std::vector mPrefilterAllEMC; void defineEMEventCut() { @@ -381,6 +414,9 @@ struct TwoPhotonMcTask { void init(InitContext&) { + if ((doprocessPcm.value && doprocessPcmPrefilterAll.value) || (doprocessEmcal.value && doprocessEmcalPrefilterAll.value)) { + LOG(fatal) << "processPcm / processEmcal and their PrefilterAll variants fill the same histograms, enable only one of each"; + } mRunNumber = 0; dBz = 0; @@ -447,6 +483,25 @@ struct TwoPhotonMcTask { } registry.add("PCM/OtherHadron/hAncestorPdg", "other hadron pairs: |PDG| of the common ancestor;|PDG| (last bin: #geq 9999);pairs", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + // pi0 prefilter study for the eta: photon origins of the pair vs number of photons tagged by a pi0 mass window prefilter + const AxisSpec axisPrefilterMass{mesonConfig.thnConfigAxisPrefilterMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisPrefilterPt{mesonConfig.thnConfigAxisPrefilterPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPrefilterClass{static_cast(PrefilterPairClass::kNClasses), -0.5, static_cast(PrefilterPairClass::kNClasses) - 0.5, "photon origins"}; + const AxisSpec axisPrefilterTagged{3, -0.5, 2.5, "photons tagged by the #pi^{0} prefilter"}; + auto hPrefilter = registry.add("PCM/hPrefilter", "minv vs pT vs cent vs photon origins vs photons tagged by the pi0 prefilter", HistType::kTHnF, {axisPrefilterMass, axisPrefilterPt, thnAxisCentOrMult, axisPrefilterClass, axisPrefilterTagged}); + static constexpr std::array(PrefilterPairClass::kNClasses)> PrefilterClassLabels = {"same particle", "same #pi^{0}", "two #pi^{0}", "same #eta", "two #eta", "#pi^{0} + #eta", "#pi^{0} + else", "#eta + else", "both else"}; + for (std::size_t i = 0; i < PrefilterClassLabels.size(); ++i) { + hPrefilter->GetAxis(3)->SetBinLabel(static_cast(i) + 1, PrefilterClassLabels[i].data()); + } + static constexpr std::array TaggedLabels = {"0", "1", "2"}; + for (std::size_t i = 0; i < TaggedLabels.size(); ++i) { + hPrefilter->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TaggedLabels[i].data()); + } + // same, but the photons are tagged with all three pair types, only booked if a process function fills it + if (doprocessPcmPrefilterAll.value || doprocessEmcalPrefilterAll.value || (doprocessPcmEmcal.value && mesonConfig.prefilterAllPairTypes.value)) { + registry.addClone("PCM/hPrefilter", "PCM/hPrefilterAll"); + } + // mixed event, same axes as hPairAncestor without the bucket (pairs from different events share no ancestor) auto hMixed = registry.add("PCM/hMixed", "minv vs pT vs cent vs pair truth, mixed event", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth}); for (std::size_t i = 0; i < TruthLabels.size(); ++i) { @@ -1030,12 +1085,119 @@ struct TwoPhotonMcTask { registry.fill(HIST(HistAncestorPdg[pairType]), std::min(std::abs(pdg), MaxPdgBin)); } + /// \brief Origin of a photon candidate: walk up its chain through the EM steps (gamma, e+-), the first other particle decides + /// \param chain ancestor chain of the candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \return origin and the index of the pi0/eta (-1 for else) + template + static std::pair photonOrigin(Chain const& chain, TMCIter& mcIter) + { + for (const auto& id : chain) { + if (id < 0) { + break; + } + mcIter.setCursor(id); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + continue; // EM step: conversion, bremsstrahlung, shower + } + if (absPdg == PDG_t::kPi0) { + return {PhotonOrigin::kFromPi0, id}; + } + if (absPdg == o2::constants::physics::Pdg::kEta) { + return {PhotonOrigin::kFromEta, id}; + } + break; + } + return {PhotonOrigin::kElse, -1}; + } + + /// \brief Class of a photon pair for the pi0 prefilter study + /// \param bucket closest common ancestor bucket of the pair: same particle pairs get their own class + template + static PrefilterPairClass prefilterClassOf(const PairBucket bucket, Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + if (bucket == PairBucket::kSameParticle) { + return PrefilterPairClass::kSameParticle; + } + const auto [origin1, id1] = photonOrigin(chain1, mcIter); + const auto [origin2, id2] = photonOrigin(chain2, mcIter); + const int nPi0 = (origin1 == PhotonOrigin::kFromPi0 ? 1 : 0) + (origin2 == PhotonOrigin::kFromPi0 ? 1 : 0); + const int nEta = (origin1 == PhotonOrigin::kFromEta ? 1 : 0) + (origin2 == PhotonOrigin::kFromEta ? 1 : 0); + constexpr int Both = 2; + if (nPi0 == Both) { + return id1 == id2 ? PrefilterPairClass::kSamePi0 : PrefilterPairClass::kTwoPi0; + } + if (nEta == Both) { + return id1 == id2 ? PrefilterPairClass::kSameEta : PrefilterPairClass::kTwoEta; + } + if (nPi0 == 1 && nEta == 1) { + return PrefilterPairClass::kPi0Eta; + } + if (nPi0 == 1) { + return PrefilterPairClass::kPi0Else; + } + return nEta == 1 ? PrefilterPairClass::kEtaElse : PrefilterPairClass::kBothElse; + } + + /// \brief Whether a photon pair passes the pair cuts of fillPair and lies inside the pi0 window of the prefilter + template + bool isInPrefilterWindow(TPhoton1 const& g1, TPhoton2 const& g2) + { + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + const float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return false; + } + const float mass = (v1 + v2).M(); + return mesonConfig.prefilterMassMin <= mass && mass <= mesonConfig.prefilterMassMax; + } + + /// \brief pi0 prefilter: tag both photons of every same-kind pair inside the pi0 window (PCM-PCM or EMC-EMC) + /// \param tags tag vector of the photon type, index = globalIndex + template + void tagSameKind(TPhotons const& photonsPerCollision, EMBitFlags const& flags, std::vector& tags) + { + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (flags.test(g1.globalIndex()) && flags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tags[g1.globalIndex()] = 1; + tags[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter: tag both photons of every PCM-EMC pair inside the pi0 window + template + void tagPcmEmc(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags, std::vector& tagsPCM, std::vector& tagsEMC) + { + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(pcmPerCollision, emcPerCollision))) { + if (v0flags.test(g1.globalIndex()) && emcFlags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tagsPCM[g1.globalIndex()] = 1; + tagsEMC[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter with all three pair types: fill mPrefilterAllPCM / mPrefilterAllEMC for one collision + template + void tagAllPairTypes(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags) + { + tagSameKind(pcmPerCollision, v0flags, mPrefilterAllPCM); + tagSameKind(emcPerCollision, emcFlags, mPrefilterAllEMC); + tagPcmEmc(pcmPerCollision, emcPerCollision, v0flags, emcFlags, mPrefilterAllPCM, mPrefilterAllEMC); + } + /// \brief Apply the pair cuts and fill the pair ancestor histogram + /// \param nTagged number of photons of this pair tagged by the pi0 prefilter with the pair type of the process function + /// \param nTaggedAll number of photons of this pair tagged by the pi0 prefilter with all three pair types, -1: not computed /// \return bucket of the pair, std::nullopt if the pair was rejected template - std::optional fillPair(TPhoton1 const& g1, TPhoton2 const& g2, Chain const& chain1, Chain const& chain2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult, TMCIter& mcIter) + std::optional fillPair(TPhoton1 const& g1, TPhoton2 const& g2, Chain const& chain1, Chain const& chain2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult, const int nTagged, const int nTaggedAll, TMCIter& mcIter) { static constexpr std::array HistPairAncestor = {"PCM/hPairAncestor", "EMC/hPairAncestor", "PCMEMC/hPairAncestor"}; + static constexpr std::array HistPrefilter = {"PCM/hPrefilter", "EMC/hPrefilter", "PCMEMC/hPrefilter"}; + static constexpr std::array HistPrefilterAll = {"PCM/hPrefilterAll", "EMC/hPrefilterAll", "PCMEMC/hPrefilterAll"}; ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); @@ -1052,6 +1214,11 @@ struct TwoPhotonMcTask { const PhotonTruth pairTruth = std::max(truth1, truth2); const PairBucket bucket = classifyPair(chain1, chain2, mcIter); registry.fill(HIST(HistPairAncestor[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(pairTruth), static_cast(bucket)); + const int prefilterClass = static_cast(prefilterClassOf(bucket, chain1, chain2, mcIter)); + registry.fill(HIST(HistPrefilter[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTagged); + if (nTaggedAll >= 0) { + registry.fill(HIST(HistPrefilterAll[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTaggedAll); + } if (bucket == PairBucket::kCrossOtherHadron) { diagnoseOtherHadron(chain1, chain2, pairTruth, vMeson, mcIter); } @@ -1088,8 +1255,21 @@ struct TwoPhotonMcTask { registry.fill(HIST(HistMixed[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(std::max(truth1, truth2))); } - // PCM-PCM same event - void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + /// \brief Size of a table that is only subscribed in some process functions, nullptr otherwise + template + static std::size_t optionalTableSize(TTable const& table) + { + if constexpr (std::is_same_v) { + return 0; + } else { + return table.size(); + } + } + + /// \brief PCM-PCM same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with EMC-EMC and PCM-EMC pairs (needs the EMC tables, nullptr otherwise) + template + void runPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles, TClusters const& clusters, TMatchedPrims const& matchedPrims, TMatchedSeconds const& matchedSeconds) { if (photons.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1098,10 +1278,19 @@ struct TwoPhotonMcTask { // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into resetCache(mChainsPCM, mTruthPCM, photons.size()); fillPcmCache(photons, legs, mcParticles); + mPrefilterPCM.assign(photons.size(), 0); EMBitFlags v0flags(photons.size()); fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + // EMC clusters only for the prefilter tagging: no chains, no cut QA + EMBitFlags emcFlags(optionalTableSize(clusters)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + } + auto mcIter = mcParticles.begin(); for (const auto& collision : collisions) { initCCDB(collision); @@ -1111,18 +1300,30 @@ struct TwoPhotonMcTask { const float centOrMult = getCentralityOrMultiplicity(collision); auto photonsPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, v0flags, mPrefilterPCM); + if constexpr (AllPairTypes) { + tagAllPairTypes(photonsPerCollision, clusters.sliceBy(perCollisionEMC, collision.globalIndex()), v0flags, emcFlags); + } + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { continue; } - fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsPCM[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult, mcIter); + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterPCM[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllPCM[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsPCM[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); } } } - PROCESS_SWITCH(TwoPhotonMcTask, processPcm, "Process for pcm photons", false); - // EMCal-EMCal same event - void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + /// \brief EMC-EMC same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with PCM-PCM and PCM-EMC pairs (needs the PCM table, nullptr otherwise) + template + void runEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles, TPcmPhotons const& photons) { if (clusters.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1132,10 +1333,19 @@ struct TwoPhotonMcTask { // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into resetCache(mChainsEMC, mTruthEMC, clusters.size()); fillEmcCache(clusters, mcIter); + mPrefilterEMC.assign(clusters.size(), 0); EMBitFlags emcFlags(clusters.size()); fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + // PCM photons only for the prefilter tagging: no chains, no cut QA + EMBitFlags v0flags(optionalTableSize(photons)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + } + for (const auto& collision : collisions) { initCCDB(collision); if (!isFullEventSelected(collision, true)) { @@ -1144,16 +1354,54 @@ struct TwoPhotonMcTask { const float centOrMult = getCentralityOrMultiplicity(collision); auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, emcFlags, mPrefilterEMC); + if constexpr (AllPairTypes) { + tagAllPairTypes(photons.sliceBy(perCollisionPCM, collision.globalIndex()), photonsPerCollision, v0flags, emcFlags); + } + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { continue; } - fillPair(g1, g2, mChainsEMC[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, mcIter); + const int nTagged = mPrefilterEMC[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllEMC[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsEMC[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); } } } + + // PCM-PCM same event + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, nullptr, nullptr, nullptr); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcm, "Process for pcm photons", false); + + // PCM-PCM same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the EMC tables) + void processPcmPrefilterAll(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, clusters, matchedPrims, matchedSeconds); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmPrefilterAll, "Process for pcm photons, pi0 prefilter also with all pair types (instead of processPcm)", false); + + // EMCal-EMCal same event + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, nullptr); + } PROCESS_SWITCH(TwoPhotonMcTask, processEmcal, "Process for emcal photons", false); + // EMCal-EMCal same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the PCM tables) + void processEmcalPrefilterAll(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, PcmPhotons const& photons, PcmMcLegs const&, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, photons); + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalPrefilterAll, "Process for emcal photons, pi0 prefilter also with all pair types (instead of processEmcal)", false); + // PCM-EMCal same event void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) { @@ -1167,6 +1415,13 @@ struct TwoPhotonMcTask { resetCache(mChainsEMC, mTruthEMC, clusters.size()); fillPcmCache(photons, legs, mcParticles); fillEmcCache(clusters, mcIter); + mPrefilterPCM.assign(photons.size(), 0); + mPrefilterEMC.assign(clusters.size(), 0); + const bool prefilterAll = mesonConfig.prefilterAllPairTypes.value; + if (prefilterAll) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + } EMBitFlags emcFlags(clusters.size()); fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); @@ -1182,12 +1437,20 @@ struct TwoPhotonMcTask { auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagPcmEmc(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags, mPrefilterPCM, mPrefilterEMC); + if (prefilterAll) { + tagAllPairTypes(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags); + } + // PCM - EMC loop, two different tables: all combinations for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCMPerCollision, photonsEMCPerCollision))) { if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { continue; } - const auto bucket = fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, mcIter); + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + const int nTaggedAll = prefilterAll ? mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()] : -1; + const auto bucket = fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); if (bucket == PairBucket::kSameParticle) { diagnoseSameParticle(g1, g2, legs, matchedPrims, matchedSeconds, mcIter); } From cb0826a14191bfb8ae04eb3c274b155e48c58640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zuzanna=20Chochulska-Gumi=C5=84ska?= <87480906+zchochul@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:17:15 +0100 Subject: [PATCH 542/648] [PWGCF] PWGCF:FemtoUniverse -- Configurables for histograms to reduce amount of used resources (#18562) Co-authored-by: Zuzanna Anna Chochulska --- .../Tasks/femtoUniversePairTaskTrackPhi.cxx | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index 8b5948e5472..7aa4e4bc2c4 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -138,12 +138,12 @@ struct FemtoUniversePairTaskTrackPhi { Configurable confTrackUseRun3PIDforKaons{"confTrackUseRun3PIDforKaons", true, "Use Run3 PID for kaons from Veronika Barbasova's AN (https://alice-notes.web.cern.ch/node/1758). If this is on the other PID methods are ignored for kaons."}; /// Partitions for the track (particle 1) - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == confTrackSign) && (aod::femtouniverseparticle::pt > confTrackPtLow) && (aod::femtouniverseparticle::pt < confTrackPtHigh); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == confTrackSign) && (aod::femtouniverseparticle::pt > confTrackPtLow) && (aod::femtouniverseparticle::pt < confTrackPtHigh); @@ -154,27 +154,30 @@ struct FemtoUniversePairTaskTrackPhi { Configurable confInvMassLowLimitPhi{"confInvMassLowLimitPhi", 1.011, "Lower limit of the Phi invariant mass"}; // change that to do invariant mass cut Configurable confInvMassUpLimitPhi{"confInvMassUpLimitPhi", 1.027, "Upper limit of the Phi invariant mass"}; Configurable confFillMinvBackground{"confFillMinvBackground", false, "Fill background histograms for Phi invariant mass."}; + Configurable confFillMinvBackgroundKpKm{"confFillMinvBackgroundKpKm", false, "Fill background histograms for K+K- invariant mass."}; + Configurable confFillMinvBackgroundKpKp{"confFillMinvBackgroundKpKp", false, "Fill background histograms for K+K+ invariant mass."}; + Configurable confFillMinvBackgroundKmKm{"confFillMinvBackgroundKmKm", false, "Fill background histograms for K-K- invariant mass."}; /// Partitions for the Phi meson (particle 2) - Partition partsPhi = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && + Partition partsPhi = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && (aod::femtouniverseparticle::pt > confPhiPtLow) && (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); - Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && + Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && (aod::femtouniverseparticle::pt > confPhiPtLow) && (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); /// Partitions for Phi daughters kPhiChild - Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); - Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); // Partition for K+K- minv - Partition partsKaons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); - Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition partsKaons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -562,14 +565,14 @@ struct FemtoUniversePairTaskTrackPhi { float mMassTwo = o2::constants::physics::MassKMinus; for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { - if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { + if ((confFillMinvBackgroundKpKp) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); TLorentzVector sumVec(part1Vec); sumVec += part2Vec; registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); } - if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { + if ((confFillMinvBackgroundKmKm) && (kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); @@ -577,7 +580,7 @@ struct FemtoUniversePairTaskTrackPhi { sumVec += part2Vec; registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); } - if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { + if ((confFillMinvBackgroundKpKm) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); @@ -681,7 +684,7 @@ struct FemtoUniversePairTaskTrackPhi { void processMCTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; } From 03be1e514a2307ac57f30a494a6a9b1b5dc7b68d Mon Sep 17 00:00:00 2001 From: Junlee Kim Date: Tue, 3 Nov 2026 21:20:06 +0900 Subject: [PATCH 543/648] [PWGLF] adding selections (#18561) Co-authored-by: ALICE Action Bot --- PWGLF/Tasks/Resonances/cha01710analysis.cxx | 51 ++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx index ebda6c3e0e5..6a23c0dec06 100644 --- a/PWGLF/Tasks/Resonances/cha01710analysis.cxx +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -97,10 +97,15 @@ struct Cha01710analysis { Configurable cfgV0DaughterPtMin{"cfgV0DaughterPtMin", 0.1f, "minimum daughter pT"}; Configurable cfgV0DaughterTPCNClsMin{"cfgV0DaughterTPCNClsMin", 70, "minimum daughter TPC clusters"}; Configurable cfgV0DaughterTPCNSigmaPiMax{"cfgV0DaughterTPCNSigmaPiMax", 5.f, "maximum daughter |TPC nSigma(pi)|"}; + Configurable cfgApplyV0DaughterTOFPID{"cfgApplyV0DaughterTOFPID", false, "Apply pion TOF PID to V0 daughters with TOF"}; Configurable cfgV0DaughterTOFNSigmaPiMax{"cfgV0DaughterTOFNSigmaPiMax", 5.f, "maximum daughter |TOF nSigma(pi)|"}; Configurable cfgKsMassWindow{"cfgKsMassWindow", 0.01, "K short mass window"}; Configurable cfgRejectLambda{"cfgRejectLambda", true, "reject Lambda/anti-Lambda competitors"}; Configurable cfgLambdaMassWindow{"cfgLambdaMassWindow", 0.005f, "Lambda rejection window (GeV/c2)"}; + Configurable cfgApplyArmenterosCut{"cfgApplyArmenterosCut", false, "Apply Armenteros-Podolanski cut"}; + Configurable cfgArmenterosCut{"cfgArmenterosCut", 0.2f, "Minimum qT / |alpha|"}; + Configurable cfgApplyV0DaughterDeltaR{"cfgApplyV0DaughterDeltaR", false, "Apply minimum deltaR between V0 daughters"}; + Configurable cfgV0DaughterDeltaRMin{"cfgV0DaughterDeltaRMin", 0.001f, "Minimum deltaR between pion daughters"}; } v0Cuts; struct : ConfigurableGroup { @@ -262,6 +267,9 @@ struct Cha01710analysis { if (!track.isPVContributor() || !track.isGlobalTrackWoDCA() || !track.isPrimaryTrack()) { return false; } + if (track.pt() < kaonCuts.cfgPtMin || std::abs(track.eta()) > kaonCuts.cfgEtaMax) { + return false; + } if (track.tpcNClsCrossedRows() < kaonCuts.cfgTPCCrossedRowsMin || track.tpcCrossedRowsOverFindableCls() < kaonCuts.cfgCrossedRowsRatioMin || track.tpcChi2NCl() > kaonCuts.cfgTPCChi2Max || track.itsChi2NCl() > kaonCuts.cfgITSChi2Max || @@ -298,7 +306,19 @@ struct Cha01710analysis { template bool selectPionDaughter(T const& track) { - return !(track.tpcNClsFound() < v0Cuts.cfgV0DaughterTPCNClsMin || track.pt() < v0Cuts.cfgV0DaughterPtMin || std::abs(track.eta()) > v0Cuts.cfgV0DaughterEtaMax || std::abs(track.tpcNSigmaPi()) > v0Cuts.cfgV0DaughterTPCNSigmaPiMax); + if (track.tpcNClsFound() < v0Cuts.cfgV0DaughterTPCNClsMin) { + return false; + } + if (track.pt() < v0Cuts.cfgV0DaughterPtMin) { + return false; + } + if (std::abs(track.eta()) > v0Cuts.cfgV0DaughterEtaMax) { + return false; + } + if (std::abs(track.tpcNSigmaPi()) > v0Cuts.cfgV0DaughterTPCNSigmaPiMax) { + return false; + } + return true; } template @@ -317,15 +337,44 @@ struct Cha01710analysis { std::abs(v0.mAntiLambda() - constants::physics::MassLambda0) < v0Cuts.cfgLambdaMassWindow)) { return V0MassRegion::kReject; } + if (v0Cuts.cfgApplyArmenterosCut && v0.qtarm() < v0Cuts.cfgArmenterosCut) { + return V0MassRegion::kReject; + } + auto pos = v0.template posTrack_as(); auto neg = v0.template negTrack_as(); if (!selectPionDaughter(pos) || !selectPionDaughter(neg)) { return V0MassRegion::kReject; } + if (v0Cuts.cfgApplyV0DaughterTOFPID) { + if (!v0.positiveHasTOF()) { + return V0MassRegion::kReject; + } + if (!v0.negativeHasTOF()) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiPlus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiMinus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + } + ROOT::Math::PxPyPzMVector k0(v0.px(), v0.py(), v0.pz(), constants::physics::MassK0Short); if (std::abs(k0.Rapidity()) > v0Cuts.cfgV0RapidityMax) { return V0MassRegion::kReject; } + + if (v0Cuts.cfgApplyV0DaughterDeltaR) { + float dEta = pos.eta() - neg.eta(); + float dPhi = TVector2::Phi_mpi_pi(pos.phi() - neg.phi()); + float deltaR = std::hypot(dEta, dPhi); + if (deltaR < v0Cuts.cfgV0DaughterDeltaRMin) { + return V0MassRegion::kReject; + } + } + float dm = std::abs(v0.mK0Short() - constants::physics::MassK0Short); histos.fill(HIST("V0/hMassSelected"), v0.mK0Short(), v0.pt()); From 1b3ff02cfef9f00a2e428168128df65c791d4905 Mon Sep 17 00:00:00 2001 From: Rajdeep-nandi Date: Tue, 3 Nov 2026 18:15:21 +0530 Subject: [PATCH 544/648] [PWGJE] Adding configurables and including a candidate matched sparse (#18547) Co-authored-by: ALICE Action Bot --- PWGJE/Tasks/CMakeLists.txt | 2 +- PWGJE/Tasks/jetHFAngularity.cxx | 246 ++++++++++++++++++++++++++------ 2 files changed, 203 insertions(+), 45 deletions(-) diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index ea882ad0e41..8a0b245b11e 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -527,7 +527,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hf-ang-substructure SOURCES jetHFAngularity.cxx - REUSE_FROM JetDataModelPCH + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-upc-qa diff --git a/PWGJE/Tasks/jetHFAngularity.cxx b/PWGJE/Tasks/jetHFAngularity.cxx index 28f505e5ce8..4850f1819fc 100644 --- a/PWGJE/Tasks/jetHFAngularity.cxx +++ b/PWGJE/Tasks/jetHFAngularity.cxx @@ -253,6 +253,24 @@ struct JetHFAngularityTask { "Event selection string"}; Configurable trackSelections{"trackSelections", "globalTracks", "Track selection string"}; + Configurable rejectReflections{"rejectReflections", true, + "Exclude reflections from response matrices and efficiency numerators"}; + // Shared axes + ConfigurableAxis binsMassD0{"binsMassD0", {500, 1.6f, 2.1f}, "m_{D^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis binsPtD0{"binsPtD0", {200, 0.f, 100.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsZParallel{"binsZParallel", {200, 0.f, 2.f}, "z_{||}"}; + ConfigurableAxis binsDeltaR{"binsDeltaR", {200, 0.f, 1.f}, "#DeltaR"}; + ConfigurableAxis binsLambda11{"binsLambda11", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12{"binsLambda12", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; + + // Jet pT: data sparse vs. MC / response + ConfigurableAxis binsPtJetData{"binsPtJetData", {200, 0.f, 100.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + ConfigurableAxis binsPtJetMC{"binsPtJetMC", {40, 0.f, 200.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + + // Response-matrix axes + ConfigurableAxis binsPtD0Resp{"binsPtD0Resp", {50, 0.f, 50.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsLambda11Resp{"binsLambda11Resp", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12Resp{"binsLambda12Resp", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; Produces objJetTable; Produces objJetMCDTable; @@ -328,8 +346,7 @@ struct JetHFAngularityTask { {"h_d0_mlscore1", ";ML score 1 (prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, {"h_d0_mlscore2", ";ML score 2 (non-prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, - // ----- Sparse: (m_D0, pT_D0, pT_jet, z_parallel, DeltaR) ----- - {"hSparse_d0", ";m_{D^{0}};#it{p}_{T,D^{0}};#it{p}_{T,jet};z_{||};#DeltaR;#lambda_{1}^{1};#lambda_{2}^{1}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {200, 0., 1.}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0 is booked in init() (configurable axes) // MC DETECTOR LEVEL (MCD) @@ -355,15 +372,11 @@ struct JetHFAngularityTask { {"h_d0_origin_mcd", ";origin (0=none,1=prompt,2=non-prompt);entries", {HistType::kTH1F, {{3, 0., 3.}}}}, - {"hSparse_d0_mcd", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};#DeltaR^{det};#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {200, 0., 1.}, {50, 0., 1.}, {50, 0., 1.}}}}, - - {"hSparse_d0_mcd_calib", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0_mcd, hSparse_d0_mcd_calib, hSparse_d0_mcd_reflection are booked in init() {"h_d0_mass_mcd_signal", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, {"h_d0_mass_mcd_reflection", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, - {"hSparse_d0_mcd_reflection", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});#lambda_{1}^{1,det};#lambda_{2}^{1,det}", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {50, 0., 1.}, {50, 0., 1.}}}}, - // MC PARTICLE LEVEL (MCP) {"h_collisions_mcp", "MCP event status;status;entries", {HistType::kTH1F, {{2, 0., 2.}}}}, @@ -406,14 +419,7 @@ struct JetHFAngularityTask { {"h_jet_pt_response_matrix_allcand", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (all geo candidates)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, // Detector-level vs matched particle-level jet pT - - {"h_jet_pt_response_matrix", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - {"h_jet_pt_response_matrix_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (prompt)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - {"h_jet_pt_response_matrix_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (non-prompt)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, - - // Axes: pT_jet^det, pT_D0^det, pT_jet^part, pT_D0^part - {"h_response_d0pt_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 50.}, {40, 0., 200.}, {50, 0., 50.}}}}, - {"h_response_d0pt_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 50.}, {40, 0., 200.}, {50, 0., 50.}}}}, + // Response matrices (h_jet_pt_response_matrix[_pr/_np], h_response_*) are booked in init() {"h_kineff_gen_num_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_gen_den_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, @@ -424,12 +430,6 @@ struct JetHFAngularityTask { {"h_kineff_det_den_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_det_num_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, {"h_kineff_det_den_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, - // ANGULARITY RESPONSE MATRIX - - {"h_response_angularity_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{1}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{1}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_angularity_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{1}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{1}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_girth_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{2}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, - {"h_response_girth_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#lambda_{2}^{1,det};#it{p}_{T,jet}^{part} (GeV/#it{c});#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{40, 0., 200.}, {50, 0., 1.}, {40, 0., 200.}, {50, 0., 1.}}}}, {"h_eff_run2_num_pr", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, {"h_eff_run2_num_np", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, @@ -441,9 +441,7 @@ struct JetHFAngularityTask { {"MC_CollisionCounter", "N_{collisions};", {HistType::kTH1F, {{4, 0., 4.}}}}, {"MC_JetCounter", "N_{jet};", {HistType::kTH1F, {{4, 0., 4.}}}}, - {"hSparse_d0_mcd_eff", ";m_{D^{0}}^{det};#it{p}_{T,D^{0}}^{det};#it{p}_{T,jet}^{det};z_{||}^{det};Origin(D^{0});Matching status;#lambda_{1}^{1,det};#lambda_{2}^{1,det};IsReflection", {HistType::kTHnSparseF, {{300, 1.7, 2.0}, {200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {2, -0.5, 1.5}, {50, 0., 1.}, {50, 0., 1.}, {2, -0.5, 1.5}}}}, - - {"hSparse_d0_mcp_eff", ";#it{p}_{T,D^{0}}^{part};#it{p}_{T,jet}^{part};z_{||}^{part};Origin(D^{0});Matching status;#lambda_{1}^{1,part};#lambda_{2}^{1,part}", {HistType::kTHnSparseF, {{200, 0., 100.}, {200, 0., 100.}, {200, 0., 2.}, {3, -0.5, 2.5}, {2, -0.5, 1.5}, {50, 0., 1.}, {50, 0., 1.}}}}, + // hSparse_d0_mcd_eff and hSparse_d0_mcp_eff are booked in init() }}; @@ -451,11 +449,99 @@ struct JetHFAngularityTask { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections.value); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections.value); massD0MCP = jetcandidateutilities::getTablePDGMass(); + // Histograms with configurable axes + // ---- Configurable axes ---- + const AxisSpec axMass{binsMassD0, "m_{D^{0}} (GeV/#it{c}^{2})"}; + const AxisSpec axPtD0{binsPtD0, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + const AxisSpec axZ{binsZParallel, "z_{||}"}; + const AxisSpec axDR{binsDeltaR, "#DeltaR"}; + const AxisSpec axL11{binsLambda11, "#lambda_{1}^{1}"}; + const AxisSpec axL12{binsLambda12, "#lambda_{2}^{1}"}; + + const AxisSpec axPtJetData{binsPtJetData, "#it{p}_{T,jet} (GeV/#it{c})"}; + const AxisSpec axPtJetDet{binsPtJetMC, "#it{p}_{T,jet}^{det} (GeV/#it{c})"}; + const AxisSpec axPtJetPart{binsPtJetMC, "#it{p}_{T,jet}^{part} (GeV/#it{c})"}; + + const AxisSpec axPtD0Det{binsPtD0Resp, "#it{p}_{T,D^{0}}^{det} (GeV/#it{c})"}; + const AxisSpec axPtD0Part{binsPtD0Resp, "#it{p}_{T,D^{0}}^{part} (GeV/#it{c})"}; + const AxisSpec axL11Det{binsLambda11Resp, "#lambda_{1}^{1,det}"}; + const AxisSpec axL11Part{binsLambda11Resp, "#lambda_{1}^{1,part}"}; + const AxisSpec axL12Det{binsLambda12Resp, "#lambda_{2}^{1,det}"}; + const AxisSpec axL12Part{binsLambda12Resp, "#lambda_{2}^{1,part}"}; + + // ---- Fixed categorical axes (not configurable) ---- + const AxisSpec axOrigin{3, -0.5, 2.5, "Origin(D^{0})"}; + const AxisSpec axMatch{2, -0.5, 1.5, "Matching status"}; + const AxisSpec axRefl{2, -0.5, 1.5, "IsReflection"}; + + // ---- DATA ---- + registry.add("hSparse_d0", "hSparse_d0", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetData, axZ, axDR, axL11, axL12}); + + // ---- MCD ---- + registry.add("hSparse_d0_mcd", "hSparse_d0_mcd", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axDR, axL11, axL12}); + registry.add("hSparse_d0_mcd_calib", "hSparse_d0_mcd_calib", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + registry.add("hSparse_d0_mcd_reflection", "hSparse_d0_mcd_reflection", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + + // ---- Jet pT response (detector level vs. matched particle level) ---- + registry.add("h_jet_pt_response_matrix", "", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_pr", "prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_np", "non-prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + + // ---- D0 pT response. Axes: pT_jet^det, pT_D0^det, pT_jet^part, pT_D0^part ---- + registry.add("h_response_d0pt_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + registry.add("h_response_d0pt_np", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + + // ---- Angularity / girth response matrices ---- + registry.add("h_response_angularity_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_angularity_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_girth_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + registry.add("h_response_girth_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + + // ---- Particle-level efficiency, consistent with the response matrices ---- + registry.add("h_eff_num_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_num_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + + registry.add("h_eff_num_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_num_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + + registry.add("h_eff_num_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_num_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + + // ---- Efficiency sparses ---- + registry.add("hSparse_d0_mcd_eff", "hSparse_d0_mcd_eff", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff", "hSparse_d0_mcp_eff", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // ---- Efficiency sparses, candidate-match version (match axis = candidate-match flag) ---- + registry.add("hSparse_d0_mcd_eff_cand", "hSparse_d0_mcd_eff_cand", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff_cand", "hSparse_d0_mcp_eff_cand", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // Bin labels + // ---- DATA bin labels ---- auto hColl = registry.get(HIST("h_collisions")); hColl->GetXaxis()->SetBinLabel(AllCollisions, "all"); @@ -541,6 +627,23 @@ struct JetHFAngularityTask { hSparseMCPEff->GetAxis(4)->SetBinLabel(1, "Unmatched"); hSparseMCPEff->GetAxis(4)->SetBinLabel(2, "Matched"); + // ---- Candidate-match efficiency sparse bin labels ---- + auto hSparseMCDEffCand = registry.get(HIST("hSparse_d0_mcd_eff_cand")); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(1, "none/bkg"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(2, "prompt"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(3, "non-prompt"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(2, "Matched (cand)"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(1, "Not reflection"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(2, "Reflection"); + + auto hSparseMCPEffCand = registry.get(HIST("hSparse_d0_mcp_eff_cand")); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(1, "none/bkg"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(2, "prompt"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(3, "non-prompt"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(2, "Matched (cand)"); + // ---- Reflection sparse bin labels ---- auto hSparseMCDRefl = registry.get(HIST("hSparse_d0_mcd_reflection")); hSparseMCDRefl->GetAxis(4)->SetBinLabel(1, "none/bkg"); @@ -1100,7 +1203,7 @@ struct JetHFAngularityTask { } if (isCleanMatched) { - registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + // registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_jet_matching_dr_mcd"), matchedDR); } @@ -1151,31 +1254,63 @@ struct JetHFAngularityTask { girth, isReflectionFlag); - if (originMcd == kOriginMcPrompt) { - registry.fill(HIST("h_eff_run2_num_pr"), mcdD0cand.pt()); - registry.fill(HIST("h_kineff_det_den_pr"), mcdjet.pt(), mcdD0cand.pt()); - if (isGeoMatched) { - registry.fill(HIST("h_kineff_det_num_pr"), mcdjet.pt(), mcdD0cand.pt()); - } - } else if (originMcd == kOriginMcNonPrompt) { - registry.fill(HIST("h_eff_run2_num_np"), mcdD0cand.pt()); - registry.fill(HIST("h_kineff_det_den_np"), mcdjet.pt(), mcdD0cand.pt()); - if (isGeoMatched) { - registry.fill(HIST("h_kineff_det_num_np"), mcdjet.pt(), mcdD0cand.pt()); + // Same entry, but the match axis carries the candidate-match flag + registry.fill(HIST("hSparse_d0_mcd_eff_cand"), + mcdD0cand.m(), + mcdD0cand.pt(), + mcdjet.pt(), + zParallelMCD, + originMcd, + static_cast(isCandMatched), + angularity, + girth, + isReflectionFlag); + + // Reflection rows are a second copy of the same true D0: skip them (switchable). + const bool skipReflection = rejectReflections && isReflection; + + if (!skipReflection) { + if (originMcd == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_num_pr"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_pr"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_pr"), mcdjet.pt(), mcdD0cand.pt()); + } + } else if (originMcd == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_num_np"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_np"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_np"), mcdjet.pt(), mcdD0cand.pt()); + } } } - if (isCleanMatched) { + if (isCleanMatched && !skipReflection) { + registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + + // The matched particle-level jet must lie in the range covered by the denominator. + const bool matchedInDenominatorRange = (matchedPt > jetPtMin); + if (originMcd == kOriginMcPrompt) { registry.fill(HIST("h_jet_pt_response_matrix_pr"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_response_angularity_pr"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); registry.fill(HIST("h_response_girth_pr"), mcdjet.pt(), girth, matchedPt, matchedGirth); registry.fill(HIST("h_response_d0pt_pr"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_pr"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_pr"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_pr"), matchedPt, matchedGirth); + } } else if (originMcd == kOriginMcNonPrompt) { registry.fill(HIST("h_jet_pt_response_matrix_np"), mcdjet.pt(), matchedPt); registry.fill(HIST("h_response_angularity_np"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); registry.fill(HIST("h_response_girth_np"), mcdjet.pt(), girth, matchedPt, matchedGirth); registry.fill(HIST("h_response_d0pt_np"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_np"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_np"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_np"), matchedPt, matchedGirth); + } } } @@ -1219,10 +1354,6 @@ struct JetHFAngularityTask { registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetInMCCollision)); - auto mcpD0cand = mcpjet.template candidates_first_as(); - - const int originMcp = mcpD0cand.originMcGen(); // 0 none/bkg, 1 prompt, 2 non-prompt - bool mcpIsCleanMatched = false; if (mcpjet.has_matchedJetGeo() && mcpjet.has_matchedJetCand()) { int mcpGeoTarget = -1; @@ -1242,6 +1373,10 @@ struct JetHFAngularityTask { if (mcpIsCleanMatched) { registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate)); } + // ---- First generated D0 of the jet only: one entry per MCP jet ---- + auto mcpD0cand = mcpjet.template candidates_first_as(); + + const int originMcp = mcpD0cand.originMcGen(); // 0 none/bkg, 1 prompt, 2 non-prompt const std::array jetMomMCP{mcpjet.px(), mcpjet.py(), mcpjet.pz()}; const std::array candMomMCP{mcpD0cand.px(), mcpD0cand.py(), mcpD0cand.pz()}; @@ -1252,6 +1387,10 @@ struct JetHFAngularityTask { const float angularityMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 1.f, 1.f); // λ_1^1 const float girthMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 2.f, 1.f); // λ_2^1 + const int isCandMatchedMCP = mcpjet.has_matchedJetCand() ? 1 : 0; + const bool mcpIsGeoMatched = mcpjet.has_matchedJetGeo(); + + // clean-match version registry.fill(HIST("hSparse_d0_mcp_eff"), mcpD0cand.pt(), mcpjet.pt(), @@ -1261,7 +1400,26 @@ struct JetHFAngularityTask { angularityMCP, girthMCP); - const bool mcpIsGeoMatched = mcpjet.has_matchedJetGeo(); + // candidate-match version + registry.fill(HIST("hSparse_d0_mcp_eff_cand"), + mcpD0cand.pt(), + mcpjet.pt(), + zParallelMCP, + originMcp, + isCandMatchedMCP, + angularityMCP, + girthMCP); + + if (originMcp == kOriginMcPrompt) { + registry.fill(HIST("h_eff_den_d0pt_pr"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_pr"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_pr"), mcpjet.pt(), girthMCP); + } else if (originMcp == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_den_d0pt_np"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_np"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_np"), mcpjet.pt(), girthMCP); + } + if (originMcp == kOriginMcPrompt) { registry.fill(HIST("h_eff_run2_den_pr"), mcpD0cand.pt()); registry.fill(HIST("h_kineff_gen_den_pr"), mcpjet.pt(), mcpD0cand.pt()); From a8fd740a1b5ef61b839138a5a50aa25d6391d230 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Tue, 3 Nov 2026 15:55:44 +0100 Subject: [PATCH 545/648] [PWGLF] Apply width correction in ZDC Q vector estimation (#18566) --- .../Common/zdc2stagecalibration.cxx | 96 +++++++++++++++---- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index c5581b3e3be..6bf3eb98054 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -82,7 +82,8 @@ struct zdc2stagecalibration { Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; - + Configurable useQWidthEqualization{"useQWidthEqualization", false, "equalize Qx/Qy widths after Structured71 recentering"}; + Configurable confQWidthPath{"confQWidthPath", "", "CCDB path to post-recentering Q-width map"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, "number of bins per cross coordinate for spatial calibration"}; @@ -101,6 +102,7 @@ struct zdc2stagecalibration { TH2D* gainprofile = nullptr; TH3D* spatialprofile = nullptr; TH3D* qRecenteringProfile = nullptr; + TH2D* qWidthProfile = nullptr; static constexpr int kQRecenteringNFeatures = 21; static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; @@ -132,7 +134,9 @@ struct zdc2stagecalibration { if (qRecenteringMethod.value != 0 && qRecenteringMethod.value != 1 && qRecenteringMethod.value != 4) { LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression, 1 for the multidimensional cubic regression or 4 for the structured71 regression.", qRecenteringMethod.value); } - + if (useQWidthEqualization.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQWidthPath.value.empty())) { + LOGF(fatal, "Width equalization requires stage=2, useQRecentering=true, method=4 and confQWidthPath"); + } const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; AxisSpec towerAxis = {4, 0.0, 4.0, "tower"}; @@ -150,6 +154,7 @@ struct zdc2stagecalibration { AxisSpec qRecenteringStructuredFlatnessMomentAxis = {kQRecenteringStructuredFlatnessNMoments, 0.0, static_cast(kQRecenteringStructuredFlatnessNMoments), "structured flatness sufficient-statistic index"}; AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; AxisSpec resolutionCentralityAxis = {50, 0.0, 50.0, "centrality (%)"}; + AxisSpec resolutionVzAxis = {20, -10.0, 10.0, "v_{z} (cm)"}; AxisSpec vertexXYAxis = {100, -0.1, 0.1, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; @@ -254,6 +259,10 @@ struct zdc2stagecalibration { histos.add("QRecenteringQA/pQAfterVsVy", "Q after recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentralityVz", "ZDC A-C correlation;centrality (%);v_{z} (cm); <#cos(#Psi_{A}-#Psi_{C})>", kTProfile2D, {resolutionCentralityAxis, resolutionVzAxis}); + histos.add("QWidthQA/pQAfterWidthEqVsCentrality", "Q after width equalization;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNA", "ZNA #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNC", "ZNC #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); @@ -311,6 +320,7 @@ struct zdc2stagecalibration { gainprofile = nullptr; spatialprofile = nullptr; qRecenteringProfile = nullptr; + qWidthProfile = nullptr; if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); if (!gainprofile) { @@ -340,6 +350,18 @@ struct zdc2stagecalibration { } } } + if (calibrationStage.value == 2 && useQWidthEqualization.value) { + qWidthProfile = ccdb->getForTimeStamp(confQWidthPath.value, timestamp); + if (!qWidthProfile || qWidthProfile->GetNbinsX() != 80 || qWidthProfile->GetNbinsY() != 4) { + LOGF(fatal, "Missing/invalid Q-width map for run %d", runNumber); + } + constexpr std::array labels = {"QxA", "QyA", "QxC", "QyC"}; + for (int i = 0; i < 4; ++i) { + if (std::string(qWidthProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect Q-width map component ordering"); + } + } + } } float getTimeFromSOR(uint64_t globalBC) const @@ -468,7 +490,7 @@ struct zdc2stagecalibration { }; if (!bc.has_zdc()) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 1.5); @@ -480,7 +502,7 @@ struct zdc2stagecalibration { const float zncEnergycommon = zdc.energyCommonZNC(); if (!std::isfinite(znaEnergycommon) || !std::isfinite(zncEnergycommon) || znaEnergycommon <= 0.f || zncEnergycommon <= 0.f) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 2.5); @@ -492,25 +514,25 @@ struct zdc2stagecalibration { } } if (!validTowers) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 3.5); if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 4.5); if (!(collision.sel8() && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && std::isfinite(centrality) && centrality > cfgCutCentralityMin && centrality < cfgCutCentralityMax && std::abs(vz) < cfgCutVertex && collision.has_foundFT0())) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 5.5); if (timeFromSOR < 0.f || timeFromSOR >= cfgMaxRunHours.value) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -573,7 +595,7 @@ struct zdc2stagecalibration { zncCorr[i] = getGain(timeFromSOR, i + 4, gainOK) * zncEnergy[i]; } if (!gainOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } histos.fill(HIST("hEvtSelInfo"), 6.5); @@ -655,7 +677,7 @@ struct zdc2stagecalibration { } } if (!spatialOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -746,7 +768,7 @@ struct zdc2stagecalibration { } if (sumA <= 1.e-4 || sumC <= 1.e-4) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -766,7 +788,7 @@ struct zdc2stagecalibration { qyZDCC = cZNC * qyZDCC / sumC; if (!std::isfinite(qxZDCA) || !std::isfinite(qyZDCA) || !std::isfinite(qxZDCC) || !std::isfinite(qyZDCC)) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -989,7 +1011,7 @@ struct zdc2stagecalibration { } } if (!qRecenteringOK) { - fillTable(false, 0.f, 0.f, 0.f, 0.f); + fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -1029,17 +1051,53 @@ struct zdc2stagecalibration { const double phiRecenteringA = std::atan2(qValues[1], qValues[0]); const double phiRecenteringC = std::atan2(qValues[3], qValues[2]); - // ZDC resolution - const double qMagA = std::hypot(qValues[0], qValues[1]); - const double qMagC = std::hypot(qValues[2], qValues[3]); - if (centrality >= 0.f && centrality < 50.f && qMagA > 0. && qMagC > 0.) { - histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentrality"), centrality, std::cos(phiRecenteringA - phiRecenteringC)); - } histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNA"), phiRecenteringA); histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNC"), phiRecenteringC); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNA"), centrality, phiRecenteringA); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNC"), centrality, phiRecenteringC); + if (useQWidthEqualization.value) { + const int centBin = qWidthProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qWidthProfile->GetNbinsX()) { + LOGF(fatal, "Invalid ZDC Q-width centrality bin for run %d: centrality=%.6f", runNumber, static_cast(centrality)); + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + std::array sigma{}; + for (int i = 0; i < 4; ++i) { + sigma[i] = qWidthProfile->GetBinContent(centBin, i + 1); + if (!std::isfinite(sigma[i]) || sigma[i] <= 0.0) { + LOGF(fatal, "Invalid ZDC Q-width sigma for run %d: centrality=%.6f, component=%d, sigma=%.6g", runNumber, static_cast(centrality), i, sigma[i]); + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + const double factorA = std::sqrt(sigma[1] / sigma[0]); + const double factorC = std::sqrt(sigma[3] / sigma[2]); + qValues[0] *= factorA; + qValues[1] /= factorA; + qValues[2] *= factorC; + qValues[3] /= factorC; + for (int i = 0; i < 4; ++i) { + histos.fill(HIST("QWidthQA/pQAfterWidthEqVsCentrality"), centrality, i + 0.5, qValues[i]); + } + } + + const double phiFinalA = std::atan2(qValues[1], qValues[0]); + const double phiFinalC = std::atan2(qValues[3], qValues[2]); + if (useQWidthEqualization.value) { + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNC"), centrality, phiFinalC); + } + // ZDC resolution after all enabled corrections + const double qMagA = std::hypot(qValues[0], qValues[1]); + const double qMagC = std::hypot(qValues[2], qValues[3]); + if (centrality >= 0.f && centrality < 50.f && qMagA > 0. && qMagC > 0.) { + const double correlation = std::cos(phiFinalA - phiFinalC); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentrality"), centrality, correlation); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentralityVz"), centrality, vz, correlation); + } + histos.fill(HIST("hEvtSelInfo"), 7.5); fillTable(true, static_cast(qValues[0]), static_cast(qValues[2]), static_cast(qValues[1]), static_cast(qValues[3])); histos.fill(HIST("hEvtSelInfo"), 8.5); From 8eda4f5662e732a46147b9b8150a2718f53d4c71 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 3 Nov 2026 16:21:26 +0100 Subject: [PATCH 546/648] [PWGCF] Handle the case with more than 3 axis (#18564) --- PWGCF/Core/CorrelationContainer.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PWGCF/Core/CorrelationContainer.cxx b/PWGCF/Core/CorrelationContainer.cxx index e60bdfb219e..4590d9403d8 100644 --- a/PWGCF/Core/CorrelationContainer.cxx +++ b/PWGCF/Core/CorrelationContainer.cxx @@ -105,6 +105,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // // efficiencyAxis has to provide a 3 length list of AxisSpec which contains: // eta (for efficiency), pt (for efficiency), vertex (for efficiency) + // and can provide a fourth one, e.g. phi (for efficiency) // The third axis of correlationAxis (multiplicity/centrality) is used also for the efficiency // // (optional) userAxis can contain optional user axes which are added to pair and trigger histogram and are @@ -128,7 +129,11 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl triggerAxis.insert(triggerAxis.end(), userAxis.begin(), userAxis.end()); mTriggerHist = HistFactory::createHist({"mTriggerHist", "d^{2}N_{ch}/d#varphid#eta", {HistType::kStepTHnF, triggerAxis, fgkCFSteps}}).release(); - mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, {efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2], efficiencyAxis[3]}, fgkCFSteps}}).release(); + std::vector trackEfficiencyAxis({efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}); + if (efficiencyAxis.size() > 3) { + trackEfficiencyAxis.push_back(efficiencyAxis[3]); + } + mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, trackEfficiencyAxis, fgkCFSteps}}).release(); mEventCount = HistFactory::createHist({"mEventCount", ";step;centrality;count", {HistType::kTH2F, {{fgkCFSteps + 2, -2.5, -0.5 + fgkCFSteps, "step"}, correlationAxis[3]}}}).release(); } From 346c9efde12b32436e4da13a774496d897391d00 Mon Sep 17 00:00:00 2001 From: Nazar Burmasov Date: Tue, 3 Nov 2026 19:04:14 +0300 Subject: [PATCH 547/648] [PWGUD] Introduce compact MCH and MCH-MID efficiency table producer (#18569) --- PWGUD/TableProducer/CMakeLists.txt | 5 + .../TableProducer/muonEfficiencyProducer.cxx | 259 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 PWGUD/TableProducer/muonEfficiencyProducer.cxx diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index a26219c2e6f..85923b12bc2 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -100,3 +100,8 @@ o2physics_add_dpl_workflow(upc-cand-producer-barrel SOURCES upcCandProducerBarrel.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::CCDB O2::DataFormatsParameters O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(muon-efficiency-producer + SOURCES muonEfficiencyProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking + COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/muonEfficiencyProducer.cxx b/PWGUD/TableProducer/muonEfficiencyProducer.cxx new file mode 100644 index 00000000000..9a7f0987bde --- /dev/null +++ b/PWGUD/TableProducer/muonEfficiencyProducer.cxx @@ -0,0 +1,259 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file muonEfficiencyProducer.cxx +/// \brief Compact forward-muon skim for offline MCH and MCH-MID efficiency studies +/// \author Nazar Burmasov (JINR) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(ReferenceBC, referenceBC, int64_t); +DECLARE_SOA_COLUMN(FT0AAmplitude, ft0aAmplitude, float); +DECLARE_SOA_COLUMN(FT0CAmplitude, ft0cAmplitude, float); +DECLARE_SOA_COLUMN(FV0AAmplitude, fv0aAmplitude, float); +DECLARE_SOA_COLUMN(CollisionId, collisionId, int32_t); // -1 for ambiguous tracks +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(RAbs, rAbs, float); +DECLARE_SOA_COLUMN(PDca, pDca, float); +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); // -1 if propagation failed +DECLARE_SOA_COLUMN(NClusters, nClusters, int8_t); +DECLARE_SOA_COLUMN(MchBitMap, mchBitMap, uint16_t); +DECLARE_SOA_COLUMN(MidBitMap, midBitMap, uint8_t); +DECLARE_SOA_COLUMN(MidBoards, midBoards, uint32_t); +DECLARE_SOA_COLUMN(Chi2MatchMCHMID, chi2MatchMCHMID, float); // -1 for MCH-only tracks +DECLARE_SOA_COLUMN(TrackTime, trackTime, float); // ns relative to referenceBC +DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffBCs, "AOD", "MCHEFFBC", + o2::soa::Index<>, + mcheffskim::RunNumber, mcheffskim::ReferenceBC, + mcheffskim::FT0AAmplitude, mcheffskim::FT0CAmplitude, + mcheffskim::FV0AAmplitude); +} // namespace o2::aod + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_INDEX_COLUMN(MchEffBC, mchEffBC); // links a muon to the compact BC table +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffSkims, "AOD", "MCHEFFSKIM", + o2::soa::Index<>, mcheffskim::MchEffBCId, + mcheffskim::CollisionId, + mcheffskim::Pt, mcheffskim::Eta, mcheffskim::Phi, mcheffskim::Sign, + mcheffskim::RAbs, mcheffskim::PDca, mcheffskim::Chi2Ndf, + mcheffskim::NClusters, mcheffskim::MchBitMap, + mcheffskim::MidBitMap, mcheffskim::MidBoards, + mcheffskim::Chi2MatchMCHMID, + mcheffskim::TrackTime, mcheffskim::TrackTimeRes); +} // namespace o2::aod + +struct MuonEfficiencyProducer { + static constexpr float MinEta = -4.f; + static constexpr float MaxEta = -2.5f; + static constexpr float NoAmplitude = -1.f; + static constexpr float MaxFITTime = 30.f; + + using Tracks = soa::Join; + using Matrix55 = ROOT::Math::SMatrix>; + using Vector5 = ROOT::Math::SVector; + + Produces outputBCs; + Produces output; + Service ccdb{}; + globaltracking::MatchGlobalFwd matcher; + int cachedFieldRun = -1; + + Configurable minPt{"minPt", 0.4f, "Minimum track pT (Gev/c)"}; + Configurable minRAbs{"minRAbs", 17.6f, "Minimum absorber-end radius (cm)"}; + Configurable maxRAbs{"maxRAbs", 89.5f, "Maximum absorber-end radius (cm)"}; + Configurable maxPDca{"maxPDca", 400.f, "Maximum stored pDCA (GeV cm/c)"}; + Configurable maxChi2{"maxChi2", 100.f, "Maximum MCH chi2/ndf"}; + + void init(InitContext&) + { + ccdb->setLocalObjectValidityChecking(); + } + + void setField(int run) + { + if (cachedFieldRun == run) { + return; + } + const auto runInfo = parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + const auto ts = (runInfo.sor + runInfo.eor) / 2; + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", ts); + if (!grp) { + LOGF(fatal, "GRPMagField not found for run %d", run); + } + base::Propagator::initFieldFromGRP(grp); + if (!base::GeometryManager::isGeometryLoaded()) { + ccdb->get("GLO/Config/GeometryAligned"); + } + mch::TrackExtrap::setField(); + cachedFieldRun = run; + } + + struct Kinematics { + float pt, eta, phi; + }; + + template + bool propagateMCH(const Track& t, const std::array& vtx, const std::array& vtxCov, Kinematics& k) + { + const Vector5 params(t.x(), t.y(), t.phi(), t.tgl(), t.signed1Pt()); + const std::vector covElements{t.cXX(), t.cXY(), t.cYY(), t.cPhiX(), t.cPhiY(), t.cPhiPhi(), + t.cTglX(), t.cTglY(), t.cTglPhi(), t.cTglTgl(), + t.c1PtX(), t.c1PtY(), t.c1PtPhi(), t.c1PtTgl(), t.c1Pt21Pt2()}; + const Matrix55 cov(covElements.begin(), covElements.end()); + dataformats::GlobalFwdTrack source; + source.setParameters(params); + source.setZ(t.z()); + source.setCovariances(cov); + auto mchTrack = matcher.FwdtoMCH(source); + if (!mch::TrackExtrap::extrapToVertex(mchTrack, vtx[0], vtx[1], vtx[2], vtxCov[0], vtxCov[1])) { + return false; + } + const auto propagated = matcher.MCHtoFwd(mchTrack); + k = {.pt = static_cast(propagated.getPt()), .eta = static_cast(propagated.getEta()), .phi = static_cast(propagated.getPhi())}; + return true; + } + + void process(aod::BCs const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, + Tracks const& tracks, aod::Collisions const& collisions, + aod::AmbiguousFwdTracks const& ambiguous) + { + if (bcs.size() == 0) { + return; + } + const int run = bcs.begin().runNumber(); + + std::vector ft0aByBC(bcs.size(), NoAmplitude); + std::vector ft0cByBC(bcs.size(), NoAmplitude); + std::vector fv0aByBC(bcs.size(), NoAmplitude); + std::vector skimBCIds(bcs.size(), -1); + for (const auto& ft0 : ft0s) { + if (ft0.timeA() < MaxFITTime) { + ft0aByBC[ft0.bcId()] = ft0.sumAmpA(); + } + if (ft0.timeC() < MaxFITTime) { + ft0cByBC[ft0.bcId()] = ft0.sumAmpC(); + } + } + for (const auto& fv0 : fv0as) { + if (fv0.time() >= MaxFITTime) { + continue; + } + float amplitude = 0.f; + for (const float a : fv0.amplitude()) { + if (a > 0.f) { + amplitude += a; + } + } + fv0aByBC[fv0.bcId()] = amplitude; + } + + std::vector ambiguousBCIds(tracks.size(), -1); + for (const auto& entry : ambiguous) { + const auto& ids = entry.bcIds(); + if (ids.empty()) { + continue; + } + ambiguousBCIds[entry.fwdtrackId()] = *ids.begin(); + } + + for (const auto& t : tracks) { + const auto type = t.trackType(); + if (type != aod::fwdtrack::MuonStandaloneTrack && type != aod::fwdtrack::MCHStandaloneTrack) { + continue; + } + const float rAbs = t.rAtAbsorberEnd(); + if (rAbs < minRAbs || rAbs > maxRAbs || t.pDca() > maxPDca || t.chi2() > maxChi2) { + continue; + } + Kinematics k{.pt = t.pt(), .eta = t.eta(), .phi = t.phi()}; + std::array vtx{0.f, 0.f, 0.f}; + std::array vtxCov{0.f, 0.f}; + int32_t referenceBCId = ambiguousBCIds[t.globalIndex()]; + if (t.has_collision()) { + const auto col = collisions.iteratorAt(t.collisionId()); + vtx = {col.posX(), col.posY(), col.posZ()}; + vtxCov = {col.covXX(), col.covYY()}; + referenceBCId = col.bcId(); + } + setField(run); + if (!propagateMCH(t, vtx, vtxCov, k)) { + continue; + } + if (k.eta < MinEta || k.eta > MaxEta) { + continue; + } + if (k.pt < minPt) { + continue; + } + int32_t skimBCId = -1; + if (referenceBCId >= 0) { + skimBCId = skimBCIds[referenceBCId]; + if (skimBCId < 0) { + outputBCs(run, bcs.iteratorAt(referenceBCId).globalBC(), + ft0aByBC[referenceBCId], ft0cByBC[referenceBCId], fv0aByBC[referenceBCId]); + skimBCId = outputBCs.lastIndex(); + skimBCIds[referenceBCId] = skimBCId; + } + } + output(skimBCId, t.collisionId(), k.pt, k.eta, k.phi, static_cast(t.signed1Pt() > 0 ? 1 : -1), + rAbs, t.pDca(), t.chi2(), t.nClusters(), t.mchBitMap(), t.midBitMap(), t.midBoards(), + type == aod::fwdtrack::MuonStandaloneTrack ? t.chi2MatchMCHMID() : -1.f, t.trackTime(), t.trackTimeRes()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} From bd4523e776c106e5624cdb8c64439b83a9c2e20f Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Tue, 3 Nov 2026 19:40:19 +0300 Subject: [PATCH 548/648] Added TF counter (#18572) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index 63611498087..4acd05ee3a4 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -336,6 +337,7 @@ struct UpcCandProducerBarrel { const AxisSpec axisOccupancy{nBinsOccupancy, 0., 250000., "FT0-based occupancy"}; const AxisSpec axisTFs{nBinsTF, 0., 4.e7, "TFs"}; // 100 bins -> 400000 TFs per bin (1 bin ~ 1140 seconds for 32 orbits per TF) + registry.add("hTF", "TF counts", HistType::kTH1D, {axisTFs}); registry.add("hTVX", "TVX counts", HistType::kTH2D, {axisBCs, axisTFs}); registry.add("hTVXRCT", "TVX counts after RCT selection", HistType::kTH2D, {axisBCs, axisTFs}); registry.add("hTVXRCTnoPileupROF", "TVX counts after RCT and ROF pileup selection", HistType::kTH2D, {axisBCs, axisTFs}); @@ -414,9 +416,12 @@ struct UpcCandProducerBarrel { std::vector localTFIndex; localTFIndex.reserve(bcs.size()); // Cache the local TF slot for each BC row for collision and MC lookups + auto hTF = registry.get(HIST("hTF")); for (const auto& bc : bcs) { - const auto tfID = static_cast((static_cast(bc.globalBC()) - bcSOR) / nBCsPerTF); + const int64_t globalBC = bc.globalBC(); + const uint32_t tfID = (globalBC - bcSOR) / nBCsPerTF; if (tfIDs.empty() || tfID != tfIDs.back()) { + hTF->Fill(tfID); tfIDs.push_back(tfID); tfPassesRCT.push_back(static_cast(rctChecker(bc))); } From 2c4eccdcd770d7b8167d48a7dc138dd57fa2d1da Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Tue, 3 Nov 2026 17:58:38 +0100 Subject: [PATCH 549/648] [PWGDQ] fixed reassociated collision indices in alignment derived tables (#18568) --- PWGDQ/Tasks/muonGlobalAlignment.cxx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index 8337c5f0c25..650aad95359 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -21,7 +21,6 @@ #include "Common/Core/fwdtrackUtilities.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/FwdTrackReAlignTables.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -124,6 +123,7 @@ using o2::track::TrackParCovFwd; namespace muonaligncoll { DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! Bunch crossing number (globally unique in this run) DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) DECLARE_SOA_COLUMN(CollisionTime, collisionTime, float); //! Collision time in ns relative to BC stored in bc() DECLARE_SOA_COLUMN(CollisionTimeRes, collisionTimeRes, float); //! Resolution of collision time @@ -133,7 +133,8 @@ namespace o2::aod { // Reduced collisions table DECLARE_SOA_TABLE(MACollisions, "AOD", "MACOLL", //! Time and vertex information of collision - o2::soa::Index<>, muonaligncoll::RunNumber, muonaligncoll::Timestamp, + o2::soa::Index<>, muonaligncoll::RunNumber, + muonaligncoll::GlobalBC, muonaligncoll::Timestamp, collision::PosX, collision::PosY, collision::PosZ, collision::CovXX, collision::CovYY, collision::CovZZ, collision::CollisionTime, collision::CollisionTimeRes); @@ -247,8 +248,8 @@ namespace o2::aod namespace ma_track_association { -DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Collision index -DECLARE_SOA_ARRAY_INDEX_COLUMN(Collision, compatibleColl); //! Array of collision indices +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions, "MACOLLs", ""); //! Collision index +DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(Collision, compatibleColl, int32_t, MACollisions, "MACOLLs", ""); //! Array of collision indices } // namespace ma_track_association DECLARE_SOA_TABLE(MAFwdTrackAssoc, "AOD", "MAFWDTRACKASSOC", //! Table for fwdtrack-to-collision association @@ -275,7 +276,7 @@ using CompactMFTTrack = CompactMFTTracks::iterator; // Switch to enable/disable the processing of the derived tables // 0: process standard AO2Ds and produce the derived tables // 1: process derived AO2Ds and disable the derived tables creation -#define PROCESS_DERIVED_TABLES 0 +#define PROCESS_DERIVED_TABLES 0 // NOLINT(cppcoreguidelines-macro-usage) struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struct (exception) @@ -518,6 +519,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc struct CollisionInfo { int runNumber{0}; + uint64_t globalBC{0}; uint64_t timestamp{0}; // z position of the collision double zVertex{0}; @@ -2029,6 +2031,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto& collisionInfo = collisionInfos[collisionIndex]; collisionInfo.runNumber = bc.runNumber(); + collisionInfo.globalBC = bc.globalBC(); collisionInfo.timestamp = bc.timestamp(); collisionInfo.zVertex = collision.posZ(); @@ -2178,6 +2181,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc auto& collisionInfo = collisionInfos[collisionIndex]; collisionInfo.runNumber = collision.runNumber(); + collisionInfo.globalBC = collision.globalBC(); collisionInfo.timestamp = collision.timestamp(); collisionInfo.zVertex = collision.posZ(); @@ -2323,7 +2327,9 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } - collTable(collisionInfo.runNumber, collisionInfo.timestamp, + collTable(collisionInfo.runNumber, + collisionInfo.globalBC, + collisionInfo.timestamp, c.posX(), c.posY(), c.posZ(), c.covXX(), c.covYY(), c.covZZ(), c.collisionTime(), c.collisionTimeRes()); From be2cf8188fda2819e2476b308abc069ac8db92bf Mon Sep 17 00:00:00 2001 From: amatyja Date: Tue, 3 Nov 2026 17:59:54 +0100 Subject: [PATCH 550/648] [PWGUD] Reordering selection criteria (#18571) --- .../tauThreeProngEventTableProducer.cxx | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index 510e229b397..00a0c30e693 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -758,36 +758,10 @@ struct TauThreeProngEventTableProducer { registrySkim.get(HIST("skim/efficiency"))->Fill(0., 1.); - int gapSide = dgcand.gapSide(); - registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); - // if (gapSide < 0 || gapSide > 2) - if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); - - int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); - gapSide = truegapSide; - registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); - if (gapSide != mGapSide) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); - - // zdc information - float energyZNA = dgcand.energyCommonZNA(); - float energyZNC = dgcand.energyCommonZNC(); - if (energyZNA < 0) - energyZNA = -1.; - if (energyZNC < 0) - energyZNC = -1.; - float timeZNA = dgcand.timeZNA(); - float timeZNC = dgcand.timeZNC(); - + // set up PV track features int nTofTrk = 0; int nEtaIn15 = 0; int npT100 = 0; - // // int qtot = 0; - // int8_t qtot = 0; - // TLorentzVector p; ROOT::Math::LorentzVector> p; for (const auto& trk : PVContributors) { // qtot += trk.sign(); @@ -805,8 +779,25 @@ struct TauThreeProngEventTableProducer { nTofTrk++; } // end of loop over PV tracks + // check number of PV tracks if (PVContributors.size() != nPVtrackscut) // 4 or 6 return; + registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + // check gap side + int gapSide = dgcand.gapSide(); + registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + // if (gapSide < 0 || gapSide > 2) + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) + return; + registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // check true gap side + int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) + return; registrySkim.get(HIST("skim/efficiency"))->Fill(3., 1.); if (nEtaIn15 != nPVtrackscut) // 4 or 6 @@ -846,6 +837,16 @@ struct TauThreeProngEventTableProducer { return; registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // zdc information + float energyZNA = dgcand.energyCommonZNA(); + float energyZNC = dgcand.energyCommonZNC(); + if (energyZNA < 0) + energyZNA = -1.; + if (energyZNC < 0) + energyZNC = -1.; + float timeZNA = dgcand.timeZNA(); + float timeZNC = dgcand.timeZNC(); + // RCT variable int rct = 0; rct = isGoodRCTflag(dgcand); From 1caab37d3c713d657b34932f791613bb1ee1f75e Mon Sep 17 00:00:00 2001 From: sawan <124118453+sawankumawat@users.noreply.github.com> Date: Wed, 4 Nov 2026 00:18:44 +0530 Subject: [PATCH 551/648] [PWGLF] Added analysis scripts phiphimctrigger.cxx and phiphimcinjected.cxx for phi-phi trigger and injected MC (#18559) --- PWGLF/Tasks/Resonances/CMakeLists.txt | 10 + PWGLF/Tasks/Resonances/phiphimcinjected.cxx | 508 ++++++++++++++++++++ PWGLF/Tasks/Resonances/phiphimctrigger.cxx | 434 +++++++++++++++++ 3 files changed, 952 insertions(+) create mode 100644 PWGLF/Tasks/Resonances/phiphimcinjected.cxx create mode 100644 PWGLF/Tasks/Resonances/phiphimctrigger.cxx diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index 8c289ef8675..3105aa36c43 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -373,3 +373,13 @@ o2physics_add_dpl_workflow(hresonancecorrelation SOURCES hResonanceCorrelation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-injected + SOURCES phiphimcinjected.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-trigger + SOURCES phiphimctrigger.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/phiphimcinjected.cxx b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx new file mode 100644 index 00000000000..b5791fac21b --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx @@ -0,0 +1,508 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimcinjected.cxx +/// \brief Code for injected doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateInjected { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + + // MC Truth attributes + bool isValidMcPhi = false; + bool isFrom999999 = false; + int mc999999Index = -1; +}; + +struct PhiPhiMCInjected { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + + } config; + + const int injectedMotherPDG = 999999; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + double normFactor = 1.0; + + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kInjectedMother, + kLoop2PhiPairs, + kUniqueTracks, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{25, -0.5, 24.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{5, -0.5, 4.5}}); + // Generated MC Histograms + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h2_InvMassPhiPhi2", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Generated/h2_InvMassPhiFromT4s", "Daughter phi from T4s decay: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + registry.add("Generated/h2_InvMassPhiMinBias", "phi from minimum bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/AllPhiPairs/h2_PhiPhi_All", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s", "Rec Phi-Phi Mother 999999: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias", "Rec Phi-Phi Minimum Bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float RadiusMax = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= RadiusMax) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + for (const auto& mcParticle : mcParticles) { + if (mcParticle.pdgCode() == injectedMotherPDG) { + + auto phiPhiDaughters = mcParticle.daughters_as(); + if (phiPhiDaughters.size() != 2) { + continue; + } + + int phiCount = 0; + for (const auto& kCurrentDaughter : phiPhiDaughters) { + + if (kCurrentDaughter.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + + if (phiCount == 0) { + vecDau1 = ROOT::Math::PxPyPzEVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), kCurrentDaughter.e()); // Phi meson mass + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + registry.fill(HIST("Generated/h2_InvMassPhiFromT4s"), vecDau1.M(), daughter1Gen.Pt()); + } else if (phiCount == 1) { + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + } + } + if (phiCount != 2) { + continue; + } + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.Px(), motherGen.Py(), motherGen.Pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherGen.M(), motherGen.Pt()); + registry.fill(HIST("Generated/h2_InvMassPhiPhi2"), vecGen.M(), mcParticle.pt()); + + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), vecGen.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), vecGen.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), vecGen.Rapidity()); + } + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + } + } + + // Ensure that the mother is not 999999 + auto mothers = mcParticle.mothers_as(); + bool isFrom999999 = false; + for (const auto& mother : mothers) { + if (mother.pdgCode() == injectedMotherPDG) { + isFrom999999 = true; + break; + } + } + + if (passPosKaon && passNegKaon && !isFrom999999) { + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.px(), motherGen.py(), motherGen.pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiMinBias"), vecGen.M(), mcParticle.pt()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processGen, "Process Generated MC", true); + + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (!collision.sel8() && config.isSel8) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + // Phi invariant mass window cut (0.9 - 1.1 GeV/c^2) + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateInjected phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + + for (const auto& gm : KaMotherPhi1.template mothers_as()) { + + if (gm.pdgCode() == injectedMotherPDG) { + phiCand.isFrom999999 = true; + phiCand.mc999999Index = gm.globalIndex(); + registry.fill(HIST("h1_TrackCount"), kInjectedMother); + } + } + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Phis from Mother 999999 + if (phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Minimum Bias Phis (not from 999999) + if (!phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + // All available Phi-Phi pairs + registry.fill(HIST("Reconstructed/AllPhiPairs/h2_PhiPhi_All"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + + // Phi-Phi pairs from same Mother 999999 + if (phi1.isFrom999999 && phi2.isFrom999999 && (phi1.mc999999Index == phi2.mc999999Index)) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + + if (!phi1.isFrom999999 && !phi2.isFrom999999) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phiphimctrigger.cxx b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx new file mode 100644 index 00000000000..52704c1537c --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx @@ -0,0 +1,434 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimctrigger.cxx +/// \brief Code for triggered doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateTrigger { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + int mcPhiIndex = -1; + + // MC Truth attributes + bool isValidMcPhi = false; +}; + +struct PhiCandidateGen { + ROOT::Math::PxPyPzMVector p4; +}; + +struct PhiPhiMCTrigger { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + } config; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2TrackSelected, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kLoop2PhiPairs, + kUniqueTracks, + kDifferentPhiMothers, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{30, -0.5, 29.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{15, -0.5, 14.5}}); + // Generated MC Histograms + registry.add("Generated/h1_GenEventAndParticleCount", "Gen Level Summary;Category;Counts", kTH1F, {{4, -0.5, 3.5}}); + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_InvMassPhi", "Gen Phi: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Generated/h1_PhiPt", "Gen Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Generated/h1_PhiEta", "Gen Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiY", "Gen Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/h2_PhiPhi_InvMass", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float Radius2Max = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= Radius2Max) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + // Fill total generated events counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 0.0); + + std::vector phiCandidatesGen; + for (const auto& mcParticle : mcParticles) { + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + // Fill total phi produced counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 1.0); + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K+ + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 2.0); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K- + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 3.0); + } + } + if (passPosKaon && passNegKaon) { + motherGen = daughter1Gen + daughter2Gen; + PhiCandidateGen phiCand; + phiCand.p4 = motherGen; + phiCandidatesGen.push_back(phiCand); + + registry.fill(HIST("Generated/h1_InvMassPhi"), motherGen.M()); + registry.fill(HIST("Generated/h1_PhiPt"), motherGen.Pt()); + registry.fill(HIST("Generated/h1_PhiEta"), motherGen.Eta()); + registry.fill(HIST("Generated/h1_PhiY"), motherGen.Rapidity()); + } + } + } + + for (size_t i = 0; i < phiCandidatesGen.size(); ++i) { + for (size_t j = i + 1; j < phiCandidatesGen.size(); ++j) { + const auto& phi1 = phiCandidatesGen[i]; + const auto& phi2 = phiCandidatesGen[j]; + + ROOT::Math::PxPyPzMVector motherPhiPhi = phi1.p4 + phi2.p4; + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherPhiPhi.M(), motherPhiPhi.Pt()); + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), motherPhiPhi.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), motherPhiPhi.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), motherPhiPhi.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processGen, "Process Generated MC", true); + + // Process Reconstructed MC + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (config.isSel8 && !collision.sel8()) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + registry.fill(HIST("h1_TrackCount"), kLoop2TrackSelected); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateTrigger phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + phiCand.mcPhiIndex = KaMotherPhi1.globalIndex(); + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // STEP 2: Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // STEP 3: Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (phi1.mcPhiIndex == phi2.mcPhiIndex) + continue; + registry.fill(HIST("h1_TrackCount"), kDifferentPhiMothers); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + registry.fill(HIST("Reconstructed/h2_PhiPhi_InvMass"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From f968ef0f29cbf97889d76ce8c7c5c54535fad22d Mon Sep 17 00:00:00 2001 From: Katarzyna <116073883+kgwizdzi@users.noreply.github.com> Date: Tue, 3 Nov 2026 20:56:57 +0100 Subject: [PATCH 552/648] [PWGCF] FU D0 task: bugs fixing (#18575) --- .../Tasks/femtoUniversePairTaskTrackD0.cxx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index a33284826b7..a1739dbe1e7 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -171,33 +171,33 @@ struct FemtoUniversePairTaskTrackD0 { Configurable doEfficiencyCorr{"doEfficiencyCorr", false, "Apply efficiency corrections"}; /// Partitions for particle 1 - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); /// Partitions for particle 2 /// Partition with all D0/D0bar mesons (which pass one and double mass hypothesis) - Partition partsAllDmesons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partsAllDmesons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0 mesons only (one and double mass hypothesis) - Partition partSigD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0s selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partSidebandD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bar mesons only (one and double mass hypothesis) - Partition partSigD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSigD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bars selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partSidebandD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); + Partition partSidebandD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition for MC Reco prompt D0/D0bar mesons - Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); /// Partition for MC Reco prompt D0/D0bar mesons (sideband candidates) - Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); // MC Truth D0/D0bar candidates - Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); /// Partition for D0/D0bar daughters - Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0Child); + Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0Child); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -427,7 +427,7 @@ struct FemtoUniversePairTaskTrackD0 { qaRegistry.add("TrackQA/hTPCfound", "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); qaRegistry.add("TrackQA/hTPCcrossedOverFindable", "; TPC ratio findable; Entries", kTH1F, {{150, 0., 1.5}}); qaRegistry.add("TrackQA/hTPCcrossedRows", "; TPC crossed rows; Entries", kTH1F, {{163, -0.5, 162.5}}); - qaRegistry.add("TrackQA/hTPCfindableVsCrossed", ";TPC findable clusters ; TPC crossed rows;", kTH1F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfindableVsCrossed", ";TPC findable clusters ; TPC crossed rows;", kTH2F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); qaRegistry.add("TrackQA/hTPCshared", "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); qaRegistry.add("TrackQA/hTPCfractionSharedCls", "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 1.0}}); qaRegistry.add("TrackQA/hITSclusters", "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); @@ -450,8 +450,8 @@ struct FemtoUniversePairTaskTrackD0 { } // D0/D0bar histograms - auto vbins = (std::vector)binsPt; - auto vFivePtBins = (std::vector)binsPtFive; + auto vbins = binsPt.value; + auto vFivePtBins = binsPtFive.value; // Axes for THnSparse const AxisSpec thnAxisNonPromptScore{ConfThnAxes.thnConfigAxisNonPromptScore, "BDT score non-prompt"}; const AxisSpec thnAxisPromptScore{ConfThnAxes.thnConfigAxisPromptScore, "BDT score prompt"}; @@ -1421,7 +1421,7 @@ struct FemtoUniversePairTaskTrackD0 { void processMcTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; } From 249789a20bd330c3274daff6185c9a28cffc61e6 Mon Sep 17 00:00:00 2001 From: Yuanjun Mei Date: Wed, 4 Nov 2026 15:07:26 +0800 Subject: [PATCH 553/648] [PWGCF] Add new cuts and QA (#18565) Co-authored-by: Yuanjun Mei --- .../Tasks/multiparticleCorrelationsMei.cxx | 266 +++++++++++++----- 1 file changed, 189 insertions(+), 77 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx index c91ee896e11..4d4ce5047f1 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -98,6 +98,12 @@ enum ECentralityEstimator { eCentralityEstimator_N }; +enum EOccupancyEstimator { + eOccuTrack = 0, + eOccuFT0C, + eOccupancyEstimator_N +}; + enum EMultiplicityTables { eMultTPC = 0, eMultFV0M, @@ -119,6 +125,9 @@ enum EEventCuts { eCentCut, eVertexZCut, eNContribCut, + eOccupancyCut, + eCorrCentCut, + eMultNContribCut, EEventCuts_N }; @@ -138,6 +147,12 @@ enum EBeforeAfterCuts { eBeforeAfterCuts_N }; +enum EUpperLowerBounds { + eUpper = 0, + eLower, + eUpperLowerBounds_N +}; + enum EEventHistograms { eHistCentrality = 0, eHistMult, @@ -147,6 +162,7 @@ enum EEventHistograms { eHistVertexZ, eHistImpactParameter, eHistNumContrib, + eHistOccupancy, eEventHistograms_N }; @@ -187,12 +203,13 @@ enum EIntValHarmonics { // *) Lists of names: static constexpr std::array CentralityEstimatorNames = {"FT0C", "FT0M", "FV0A", "NTPV"}; static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; +static constexpr std::array OccupancyEstimatorNames = {"track", "FT0C"}; static constexpr std::array ParticleHistTitles = {"p_{T}", "#varphi", "#eta", "Particle charge"}; static constexpr std::array ParticleHistNames = {"eHistPt", "eHistPhi", "eHistEta", "eHistCharge"}; -static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter", "Number of Contributor"}; -static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter", "eHistNumContrib"}; +static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter", "Number of Contributor", "Occupancy"}; +static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter", "eHistNumContrib", "eHistOccupancy"}; static constexpr std::array WeightsNames = {"eWPhiRec", "eWPhiSim", "eWPt"}; @@ -220,6 +237,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; Configurable cfCentralityEstimator{"cfCentralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; Configurable cfMultiplicityTables{"cfMultiplicityTables", 2, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; + Configurable cfOccupancyEstimator{"cfOccupancyEstimator", 0, "occupancy estimator: 0=trackOccupancyInTimeRange, 1=ft0cOccupancyInTimeRange"}; // *) External root files Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; @@ -227,26 +245,33 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Binnings Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; - Configurable> cfMultBins{"cfMultBins", {400, 0., 25000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfMultBins{"cfMultBins", {300, 0., 30000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; Configurable> cfRefMultBins{"cfRefMultBins", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; Configurable> cfNumContribBins{"cfNumContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; Configurable> cfIpBins{"cfIpBins", {100, 0., 20.}, "Impact parameters hist (MC only): nIPBins, ipMin, ipMax"}; + Configurable> cfOccuBins{"cfOccuBins", {300, 0., 10000.}, "Occupancy hist (Rec only): nOccuBins, occuMin, occuMax"}; - Configurable> cfPtBins{"cfPtBins", {2000, 0., 6.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPtBins{"cfPtBins", {4000, 0., 10.}, "nPtBins, ptMin, ptMax"}; Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; + Configurable> cfQASelTrackBins{"cfQASelTrackBins", {200, 0., 15000., 400, 0., 6000.}, "bins for hist of selected tracks of QA: {nQASelTrackBinsBefore, minBefore, MaxBefore, nQASelTrackBinsAfter, minAfter, MaxAfter}"}; + // *) Cuts Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; // event level cuts - Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1IsGoodITSLayersAll", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1Cent", "1VertexZ", "1NContrib"}, "event cuts switch, on and off by the first number before name"}; + Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1IsGoodITSLayersAll", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1Cent", "1VertexZ", "1NContrib", "1Occupancy", "1CorrCent", "1MultNContribCut"}, "event cuts switch, on and off by the first number before name"}; Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; + Configurable cfOccupancyCutSup{"cfOccupancyCutSup", 6000, "Cuts on number of tracks used for the vertex, only occupancy < cfOccupancyCutSup survives"}; + Configurable> cfCorrCent{"cfCorrCent", {0, 2}, "choose the pair of centrality estimators for cut: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfCorrCentCutRatio{"cfCorrCentCutRatio", 0.5, "correlation cut on centrality, only 2*|cent1 - cent2|/(cent1 + cent2) < cfCorrCentCutRatio survives"}; + Configurable> cfMultNContribBound{"cfMultNContribBound", {"0.160 * x + 153.563", "0.095 * x + -157.476"}, "geometric cuts on correlation of mult vs. nContrib switch, convention {upper bound, lower bound}"}; // particle level cuts Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge", "1TPCNumCluster", "1DCAxy", "1DCAz"}, "particle cuts switch, on and off by the first number before name"}; @@ -263,16 +288,21 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; Configurable cfInternalValidationSwitch{"cfInternalValidationSwitch", false, "internal validation switch"}; Configurable> cfIVHarmonics{"cfIVHarmonics", {0.06, 0.07, 0.08}, "harmonics for internal validation: {v_2, v_3, v_4, ...}"}; + Configurable cfRNGSeed{"cfRNGSeed", 0, "seed for random number generator"}; + Configurable> cfQASelTrackCentRange{"cfQASelTrackCentRange", {60., 70.}, "centrality range for hist of selected tracks for QA"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: struct TaskConfiguration { float fSigmaInel = 7.71; float fEtaGap = 1.0; + float fCorrCentCutRatio = 0.5; std::vector fEventCutSwitch; std::vector fVertexZCutRange = {-10., 10.}; std::vector fCentCutRange = {0., 80.}; + std::vector fCorrCent = {0, 2}; + std::vector fMultNContribBound; std::vector fParticleCutSwitch; std::vector fPtCutRange = {0.2, 5.}; std::vector fEtaCutRange = {-0.8, 0.8}; @@ -281,12 +311,16 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::vector fDCAzCutRange = {-2.4, 2.4}; std::vector fIVHarmonics; + std::vector fQASelTrackCentRange; std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; int fCentralityEstimator = 0; int fMultiplicityTables = 0; + int fOccupancyEstimator = 0; int fNContribCutInf = 2; + int fOccupancyCutSup = 6000; + int fRNGSeed = 0; bool fDryRun = false; // book all histos and run without filling and calculating anything bool fExternalFileSwitch = false; @@ -322,6 +356,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to TList* fQualityAssuranceList = nullptr; std::array fHistCentralityRecSim{}; std::array fHistMultNContrib{}; + std::array fHistCorrCent{}; + std::array fHistSelectedTrack{}; } qa; struct MiscHistograms { @@ -331,8 +367,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::array fMiscParticleCutsCounter{}; //! [reco,sim] } misc; + struct GeometricCuts { // eUpperLowerBounds_N + std::array fMultNContribBounds{}; + } geoCuts; + struct InternalValidation { - TRandom3* randGen = new TRandom3(0); + TRandom3* fRandGen = nullptr; TF1* fIntValFun = nullptr; TList* fIntValList = nullptr; std::array fProfIntVal{}; @@ -620,6 +660,20 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + template + float chooseOccu(T1 const& collision, int const& whichEstimator) + { + switch (whichEstimator) { + case eOccuTrack: + return static_cast(collision.trackOccupancyInTimeRange()); + case eOccuFT0C: + return static_cast(collision.ft0cOccupancyInTimeRange()); + default: + LOG(warning) << "Unknown occupancy estimator. Using track as default."; + return static_cast(collision.trackOccupancyInTimeRange()); + } + } + template bool eventCuts(T1 const& collision) { @@ -628,9 +682,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to return false; } } - + bool doNotPassCut = false; for (int i = 0; i < EEventCuts_N; ++i) { - bool doNotPassCut = false; switch (i) { default: doNotPassCut = false; @@ -662,6 +715,11 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to case eNContribCut: doNotPassCut = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); break; + case eOccupancyCut: { + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); + doNotPassCut = tc.fEventCutSwitch[eOccupancyCut].starts_with("1") && (thisOccu > tc.fOccupancyCutSup); + break; + } case eVertexZCut: if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); @@ -681,6 +739,20 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); } break; + case eCorrCentCut: { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); + doNotPassCut = tc.fEventCutSwitch[eCorrCentCut].starts_with("1") && (2 * std::fabsf(cent1 - cent2) / (cent1 + cent2) > tc.fCorrCentCutRatio); + break; + } + case eMultNContribCut: { + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisNContrib = collision.numContrib(); + auto upper = geoCuts.fMultNContribBounds[eUpper]->Eval(thisRefMult); + auto lower = geoCuts.fMultNContribBounds[eLower]->Eval(thisRefMult); + doNotPassCut = tc.fEventCutSwitch[eMultNContribCut].starts_with("1") && (thisNContrib < lower || thisNContrib > upper); + break; + } } // end of switch (i) { if (doNotPassCut) { @@ -703,6 +775,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { // Fill reconstructed-level event histograms auto thisCent = chooseCent(collision, tc.fCentralityEstimator); auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); int multiplicityRec = static_cast(tracks.size()); if constexpr (cuts == eBefore) { ec.fEventHist[eHistMult][eRec][eBefore]->Fill(multiplicityRec); @@ -712,6 +785,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); ec.fEventHist[eHistNumContrib][eRec][eBefore]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->Fill(thisOccu); } if constexpr (cuts == eAfter) { ec.fEventHist[eHistMult][eRec][eAfter]->Fill(multiplicityRec); @@ -721,6 +795,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); ec.fEventHist[eHistNumContrib][eRec][eAfter]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->Fill(thisOccu); } } @@ -755,8 +830,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to template bool particleCuts(T const& track) { + bool doNotPassCut = false; for (int i = 0; i < EParticleCuts_N; ++i) { - bool doNotPassCut = false; if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { switch (i) { default: @@ -887,17 +962,24 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } // end of if constexpr (rs == eRecAndSim && rm == eMC) { } // end of void particleHistFill(T1 const& track) - template - void qaFill(T1 const& collision) + template + void qaFill(T1 const& collision, T2 const& tracks) { auto thisCent = chooseCent(collision, tc.fCentralityEstimator); auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); if constexpr (rm == eReal) { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); if constexpr (cuts == eBefore) { qa.fHistMultNContrib[eBefore]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eBefore]->Fill(cent1, cent2); + if (thisCent > tc.fQASelTrackCentRange[0] && thisCent < tc.fQASelTrackCentRange[1]) { + qa.fHistSelectedTrack[eBefore]->Fill(tracks.size()); + } } if constexpr (cuts == eAfter) { qa.fHistMultNContrib[eAfter]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eAfter]->Fill(cent1, cent2); } } @@ -946,8 +1028,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // Fill Quality Assurance before cuts if (tc.fQualityAssuranceSwitch) { - qaFill(collision); - qaFill(collision); + qaFill(collision, tracks); + qaFill(collision, tracks); } // Fill Event Hist before cuts @@ -958,14 +1040,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // bool passEventCutsMC = eventCuts(collision); // this is currently useless // Fill event hist and qa hist after cuts - if (tc.fMasterCutSwitch) { - if (passEventCutsReal) { - eventHistFill(collision, tracks); - eventHistFill(collision, tracks); - if (tc.fQualityAssuranceSwitch) { - qaFill(collision); - qaFill(collision); - } + if (tc.fMasterCutSwitch && passEventCutsReal) { + eventHistFill(collision, tracks); + eventHistFill(collision, tracks); + if (tc.fQualityAssuranceSwitch) { + qaFill(collision, tracks); + qaFill(collision, tracks); } } @@ -982,11 +1062,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to std::vector weightABMC(2, 0.f); if (tc.fInternalValidationSwitch) { - double psi = intval.randGen->Uniform(0, math::TwoPI); + double psi = intval.fRandGen->Uniform(0, math::TwoPI); intval.fIntValFun->SetParameter(0, psi); } // Main loop over particles: + int nPassedTrack = 0; for (auto const& track : tracks) { float thisPhiRec = track.phi(); float thisPt = track.pt(); @@ -1002,6 +1083,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to particleHistFill(track); if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { particleHistFill(track); + ++nPassedTrack; if (tc.fExternalFileSwitch) { for (int k = 0; k < eWeightsHistograms_N; ++k) { if (k != eWPhiSim && ex.fWeights[k]) { @@ -1034,55 +1116,56 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } // end of if constexpr (rs == eRecAndSim) { } // end of for (auto const& track : tracks) { + if (tc.fMasterCutSwitch && passEventCutsReal && tc.fQualityAssuranceSwitch) { + qa.fHistSelectedTrack[eAfter]->Fill(nPassedTrack); + } - if (tc.fMasterCutSwitch) { + if (tc.fMasterCutSwitch && passEventCutsReal) { std::vector result22(2, TComplex(0., 0.)); std::vector result24(2, TComplex(0., 0.)); std::vector result22Gap(2, 0.f); - if (passEventCutsReal) { - result22 = two(qVecTableReal, harmonic22); + result22 = two(qVecTableReal, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVecTableReal, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); + } + + result22Gap = {static_cast((qVecABReal[0] * qVecABReal[1]).Re()), weightABReal[0] * weightABReal[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); + } + + if constexpr (rs == eSim || rs == eRecAndSim) { + auto impactParam = collision.mcCollision().impactParameter(); + auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; + + result22 = two(qVecTableMC, harmonic22); if (noneZeroDenom(result22)) { - obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); } - result24 = recursion(4, qVecTableReal, harmonic24); + result24 = recursion(4, qVecTableMC, harmonic24); if (noneZeroDenom(result24)) { - obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); + obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); } - result22Gap = {static_cast((qVecABReal[0] * qVecABReal[1]).Re()), weightABReal[0] * weightABReal[1]}; + result22Gap = {static_cast((qVecABMC[0] * qVecABMC[1]).Re()), weightABMC[0] * weightABMC[1]}; if (result22Gap[1] != 0) { - obs.fProfCorr[eV22Gap][eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); + obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); } + } // end of if constexpr (rs == eRecAndSim) { - if constexpr (rs == eSim || rs == eRecAndSim) { - auto impactParam = collision.mcCollision().impactParameter(); - auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; - - result22 = two(qVecTableMC, harmonic22); - if (noneZeroDenom(result22)) { - obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); - } - result24 = recursion(4, qVecTableMC, harmonic24); - if (noneZeroDenom(result24)) { - obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); - } - - result22Gap = {static_cast((qVecABMC[0] * qVecABMC[1]).Re()), weightABMC[0] * weightABMC[1]}; - if (result22Gap[1] != 0) { - obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); + if (tc.fInternalValidationSwitch) { + std::vector> harmonicIV = {{-2, 2}, {-3, 3}, {-4, 4}}; + for (int iv = 0; iv < eIntValHarmonics_N; ++iv) { + std::vector result = two(qVecTableIV, harmonicIV[iv]); + if (noneZeroDenom(result)) { + intval.fProfIntVal[iv]->Fill(thisCollCentReal, (result[0] / result[1].Re()).Re()); } - } // end of if constexpr (rs == eRecAndSim) { - - if (tc.fInternalValidationSwitch) { - std::vector> harmonicIV = {{-2, 2}, {-3, 3}, {-4, 4}}; - for (int iv = 0; iv < eIntValHarmonics_N; ++iv) { - std::vector result = two(qVecTableIV, harmonicIV[iv]); - if (noneZeroDenom(result)) { - intval.fProfIntVal[iv]->Fill(thisCollCentReal, (result[0] / result[1].Re()).Re()); - } - } - } // end of if (tc.fInternalValidationSwitch) { - } // end of if (passEventCutsReal) { + } + } // end of if (tc.fInternalValidationSwitch) { } // end of if (tc.fMasterCutSwitch) { rti.isFirstCollision = false; // Now the first collision ends @@ -1114,6 +1197,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fDryRun = cfDryRun; tc.fCentralityEstimator = cfCentralityEstimator; tc.fMultiplicityTables = cfMultiplicityTables; + tc.fOccupancyEstimator = cfOccupancyEstimator; tc.fExternalFileSwitch = cfExternalFileSwitch; tc.fFileWithWeights = cfFileWithWeights; @@ -1124,6 +1208,10 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fVertexZCutRange = cfVertexZCutRange.value; tc.fCentCutRange = cfCentCutRange.value; tc.fNContribCutInf = cfNContribCutInf; + tc.fOccupancyCutSup = cfOccupancyCutSup; + tc.fCorrCent = cfCorrCent.value; + tc.fCorrCentCutRatio = cfCorrCentCutRatio; + tc.fMultNContribBound = cfMultNContribBound.value; tc.fParticleCutSwitch = cfParticleCutSwitch.value; tc.fPtCutRange = cfPtCutRange.value; @@ -1134,10 +1222,12 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to tc.fEtaGap = cfEtaGap; tc.fSigmaInel = cfSigmaInel; + tc.fRNGSeed = cfRNGSeed; tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; tc.fRunMessageSwitch = cfRunMessageSwitch; tc.fInternalValidationSwitch = cfInternalValidationSwitch; tc.fIVHarmonics = cfIVHarmonics.value; + tc.fQASelTrackCentRange = cfQASelTrackCentRange.value; // *) Book base list: auto* temp = new TList(); @@ -1255,6 +1345,7 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to lEventHistBins[eHistVertexZ] = cfVzBins.value; lEventHistBins[eHistImpactParameter] = cfIpBins.value; lEventHistBins[eHistNumContrib] = cfNumContribBins.value; + lEventHistBins[eHistOccupancy] = cfOccuBins.value; if (doprocessRec || doprocessRecSim) { for (int i = 0; i < eEventHistograms_N; ++i) { @@ -1272,15 +1363,17 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to ec.fEventHistList->Add(ec.fEventHist[i][eRec][ba]); } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { } // end of for (int i = 0; i < eEventHistograms_N; ++i) { - ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[cfCentralityEstimator])); - ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[cfCentralityEstimator])); - ec.fEventHist[eHistRefMult][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[cfMultiplicityTables])); - ec.fEventHist[eHistRefMult][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[cfMultiplicityTables])); + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistRefMult][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistRefMult][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); } // end of if (doprocessRec || doprocessRecSim) { if (doprocessSim || doprocessRecSim) { for (int i = 0; i < eEventHistograms_N; ++i) { - if (i == eHistRefMult || i == eHistNumContrib) { // no RefMult for Sim + if (i == eHistRefMult || i == eHistNumContrib || i == eHistOccupancy) { // no RefMult for Sim continue; } const auto& bins = lEventHistBins[i]; @@ -1331,38 +1424,55 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to // *) Book and QA TLists: if (tc.fQualityAssuranceSwitch) { - std::vector lContrib = cfNumContribBins.value; - const int nBinsContrib = static_cast(lContrib[0]); - const float minContrib = lContrib[1]; - const float maxContrib = lContrib[2]; qa.fQualityAssuranceList = new TList(); qa.fQualityAssuranceList->SetName("QualityAssurance"); qa.fQualityAssuranceList->SetOwner(true); fBaseList->Add(qa.fQualityAssuranceList); if (doprocessRec || doprocessRecSim) { + const auto& nContribBins = lEventHistBins[eHistNumContrib]; const auto& refMultBins = lEventHistBins[eHistRefMult]; for (int i = 0; i < eBeforeAfterCuts_N; ++i) { - qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s cuts", CutsNames[i]), static_cast(refMultBins[0]), refMultBins[1], refMultBins[2], nBinsContrib, minContrib, maxContrib); + qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s Cuts", CutsNames[i]), static_cast(refMultBins[0]), refMultBins[1], refMultBins[2], static_cast(nContribBins[0]), nContribBins[1], nContribBins[2]); qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); + qa.fHistMultNContrib[i]->SetStats(false); qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); } - if (doprocessRecSim) { - const auto& centBins = lEventHistBins[eHistCentrality]; - for (int i = 0; i < eBeforeAfterCuts_N; ++i) { - qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s cuts", CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); - qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); - qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); - qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); - } - } // end of if (doprocessRecSim) { + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCorrCent[i] = new TH2F(Form("fHistCorrCent[%s]", CutsNames[i]), Form("Centrality (%s) vs. Centrality (%s) %s Cuts", CentralityEstimatorNames[tc.fCorrCent[0]], CentralityEstimatorNames[tc.fCorrCent[1]], CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCorrCent[i]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[0]])); + qa.fHistCorrCent[i]->GetYaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[1]])); + qa.fHistCorrCent[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCorrCent[i]); + } + + std::vector lQASelTrackBins = cfQASelTrackBins.value; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistSelectedTrack[i] = new TH1F(Form("fHistSelectedTrack[%s]", CutsNames[i]), Form("Selected Tracks in %.1f-%.1f%% %s cuts", tc.fQASelTrackCentRange[0], tc.fQASelTrackCentRange[1], CutsNames[i]), static_cast(lQASelTrackBins[0 + i * 3]), lQASelTrackBins[1 + i * 3], lQASelTrackBins[2 + i * 3]); + qa.fHistSelectedTrack[i]->GetXaxis()->SetTitle("Number of selected tracks"); + qa.fHistSelectedTrack[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistSelectedTrack[i]); + } } // end of if (doprocessRec || doprocessRecSim) { + + if (doprocessRecSim) { + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s Cuts", CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); + qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); + qa.fHistCentralityRecSim[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); + } + } // end of if (doprocessRecSim) { } // end of if (tc.fQualityAssuranceSwitch) { // *) Book InternalValidation TLists: if (tc.fInternalValidationSwitch) { + intval.fRandGen = new TRandom3(tc.fRNGSeed); intval.fIntValFun = new TF1("fIntValFun", "1 + 2*0.04*cos(1*(x - [0])) + 2*[1]*cos(2*(x - [0])) + 2*[2]*cos(3*(x - [0])) + 2*[3]*cos(4*(x - [0]))", 0., math::TwoPI); for (std::size_t i = 1; i <= tc.fIVHarmonics.size(); ++i) { intval.fIntValFun->SetParameter(i, tc.fIVHarmonics[i - 1]); @@ -1383,6 +1493,8 @@ struct MultiparticleCorrelationsMei // this name is used in lower-case format to } } + geoCuts.fMultNContribBounds[eUpper] = new TF1("fMultNContribBounds[eUpper]", tc.fMultNContribBound[eUpper].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); + geoCuts.fMultNContribBounds[eLower] = new TF1("fMultNContribBounds[eLower]", tc.fMultNContribBound[eLower].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); } // end of void init(InitContext&) { // A) Process only reconstructed data: From f67e4a571e76e5243368d12c786c6c580d0c2bef Mon Sep 17 00:00:00 2001 From: Rocco Liotino Date: Wed, 4 Nov 2026 08:41:25 +0100 Subject: [PATCH 554/648] [DPG] Major upgrade for cross sections tasks (#18567) --- DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt | 10 +- .../PID/HMPID/hmpidAbsCrossSection.cxx | 389 +++++++++++ .../HMPID/hmpidCrossSectionCorrections.cxx | 374 +++++++++++ .../AOTTrack/PID/HMPID/hmpidDeuteron.cxx | 620 ------------------ 4 files changed, 771 insertions(+), 622 deletions(-) create mode 100644 DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx create mode 100644 DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx delete mode 100644 DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt index d33a73eb12f..748576f57eb 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt +++ b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt @@ -20,7 +20,13 @@ o2physics_add_dpl_workflow(hmpid-table-producer PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::HMPIDBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(hmpid-deuteron - SOURCES hmpidDeuteron.cxx +o2physics_add_dpl_workflow(hmpid-abs-cross-section + SOURCES hmpidAbsCrossSection.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hmpid-cross-section-corrections + SOURCES hmpidCrossSectionCorrections.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx new file mode 100644 index 00000000000..aa8a79e6bc2 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx @@ -0,0 +1,389 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief Analysis task for the absorption cross section using HMPID (de,pi,ka,pr) +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidAbsCrossSection { + + HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for PbPb collisions + Configurable collisionsPbPb{"collisionsPbPb", false, "Require PbPb collisions"}; + Configurable centralityCut{"centralityCut", 30.0f, "centrality cut for PbPb collisions"}; + + // Configurables for PID cuts + Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; + Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; + Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; + Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.0, "nsigmaTOFMax"}; + + // Particle species selection + Configurable particleSpecies{"particleSpecies", "de", "Particle species: de (deuteron), pr (proton), pi (pion), ka (kaon)"}; + + // Configurable offset for residuals distributions + Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; // mean value (old) + Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; // mean value (old) + + static constexpr float UnsetOffset = -999.f; + static constexpr int MaxOffsetBins = 35; + static constexpr int OffsetColumns = 8; + + static std::vector makeOffsetRowLabels() + { + std::vector labels; + labels.reserve(MaxOffsetBins); + for (int bin = 0; bin < MaxOffsetBins; ++bin) { + labels.emplace_back("pBin" + std::to_string(bin)); + } + return labels; + } + + Configurable offsetMomMin{"offsetMomMin", 0.5f, "Minimum momentum for residual offset correction"}; + Configurable offsetMomMax{"offsetMomMax", 4.0f, "Maximum momentum for residual offset correction"}; + Configurable offsetMomStep{"offsetMomStep", 0.5f, "Momentum bin width for residual offset correction"}; + + Configurable> residualOffsets{"residualOffsets", LabeledArray{Array2D{std::vector(MaxOffsetBins * OffsetColumns, UnsetOffset), MaxOffsetBins, OffsetColumns}, makeOffsetRowLabels(), {"xPosR2", "yPosR2", "xNegR2", "yNegR2", "xPosR4", "yPosR4", "xNegR4", "yNegR4"}}, "Residual offsets by momentum bin, chamber and charge"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 450, "Momentum bins"}; + Configurable momMin{"momMin", 0.5f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable deltaRNBins{"deltaRNBins", 100, "DeltaR bins"}; + Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; + Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; + + Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 100, "nSigmaTPC bins"}; + Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; + Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; + + Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 100, "nSigmaTOF bins"}; + Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; + Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; + + static constexpr int Rich2 = 2, Rich4 = 4; + // static constexpr Rich0 = 0, Rich1 = 1, Rich3 = 3, Rich5 = 5, Rich6 = 6; + // static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisMom"}; + AxisSpec axisDeltaR{deltaRNBins, deltaRMin, deltaRMax, "#Delta R (cm)", "axisDeltaR"}; + AxisSpec axisNsigmaTOF{nSigmaTOFNBins, nSigmaTOFMinPlot, nSigmaTOFMaxPlot, "n#sigma_{TOF}", "axisNsigmaTOF"}; + AxisSpec axisNsigmaTPC{nSigmaTPCNBins, nSigmaTPCMinPlot, nSigmaTPCMaxPlot, "n#sigma_{TPC}", "axisNsigmaTPC"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + // 1D integrated histograms (quick view) + registryDA.add("incoming_Pos_8cm", "incoming_Pos_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Pos_4cm", "incoming_Pos_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Pos_8cm", "DeltaR_Pos_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Pos_4cm", "DeltaR_Pos_4cm", HistType::kTH1F, {axisDeltaR}); + + registryDA.add("incoming_Neg_8cm", "incoming_Neg_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Neg_4cm", "incoming_Neg_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Neg_8cm", "DeltaR_Neg_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Neg_4cm", "DeltaR_Neg_4cm", HistType::kTH1F, {axisDeltaR}); + + // Positive candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_8cm", "nSigmaTOF_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_4cm", "nSigmaTOF_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Pos_8cm", "DeltaR_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Pos_4cm", "DeltaR_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Pos_8cm", "residualsX_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_8cm", "residualsY_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Pos_4cm", "residualsX_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_4cm", "residualsY_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // Negative candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_8cm", "nSigmaTOF_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_4cm", "nSigmaTOF_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Neg_8cm", "DeltaR_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Neg_4cm", "DeltaR_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Neg_8cm", "residualsX_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_8cm", "residualsY_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Neg_4cm", "residualsX_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_4cm", "residualsY_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // nsigma TPC vs nsigma TOF for candidates + registryDA.add("pidTPC_TOF_p_ch_Pos", "pidTPC_TOF_p_ch_Pos", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + registryDA.add("pidTPC_TOF_p_ch_Neg", "pidTPC_TOF_p_ch_Neg", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + + // ------------------------- + // track filters + // ------------------------- + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const float momHmpid = hmpid.momentumHmpid(); + const float momAbs = std::abs(momHmpid); + + const int chamber = static_cast(hmpid.chamber()); + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + + // Select chambers with absorber + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // Default residual offsets + float usedOffsetX = offsetX.value; + float usedOffsetY = offsetY.value; + + // Momentum-dependent offset correction + if (momAbs >= offsetMomMin.value && momAbs < offsetMomMax.value && (isPos || isNeg)) { + const int pBin = static_cast((momAbs - offsetMomMin.value) / offsetMomStep.value); + if (pBin >= 0 && pBin < static_cast(residualOffsets->rows())) { + const int col = (chamber == Rich2 ? 0 : 4) + (isPos ? 0 : 2); + const float binnedOffsetX = residualOffsets->get(pBin, col); + const float binnedOffsetY = residualOffsets->get(pBin, col + 1); + + if (binnedOffsetX != UnsetOffset) { + usedOffsetX = binnedOffsetX; + } + if (binnedOffsetY != UnsetOffset) { + usedOffsetY = binnedOffsetY; + } + } + } + + // Corrected residuals + const float dxCorr = (hmpid.xMip() - hmpid.xTrack()) - usedOffsetX; + const float dyCorr = (hmpid.yMip() - hmpid.yTrack()) - usedOffsetY; + const float drCorr = std::hypot(dxCorr, dyCorr); + + // check only HMPID chamber with an absorber (only RICH 2 (4cm abs) and RICH4 (8cm abs)) + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // select particle species + + float nSigmaTPC = 0.f; + float nSigmaTOF = 0.f; + + // Select particle species + if (particleSpecies.value == "de") { + nSigmaTPC = hmpid.tpcNSigmaDe(); + nSigmaTOF = hmpid.tofNSigmaDe(); + } else if (particleSpecies.value == "pr") { + nSigmaTPC = hmpid.tpcNSigmaPr(); + nSigmaTOF = hmpid.tofNSigmaPr(); + } else if (particleSpecies.value == "pi") { + nSigmaTPC = hmpid.tpcNSigmaPi(); + nSigmaTOF = hmpid.tofNSigmaPi(); + } else if (particleSpecies.value == "ka") { + nSigmaTPC = hmpid.tpcNSigmaKa(); + nSigmaTOF = hmpid.tofNSigmaKa(); + } else { + LOGF(fatal, "Invalid particle species: %s", + particleSpecies.value.c_str()); + } + + // ------------------------- + // nsigma distributions pre-cuts + // ------------------------- + if (isPos) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Pos"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Neg"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TPC + // ------------------------- + if (nSigmaTPC < nsigmaTPCMin || nSigmaTPC > nsigmaTPCMax) { + continue; + } + + // ------------------------- + // post TPC cut + // ------------------------- + if (isPos) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TOF + // ------------------------- + if (nSigmaTOF < nsigmaTOFMin || nSigmaTOF > nsigmaTOFMax) { + continue; + } + + // Pb-Pb collisions centrality requirement + if (collisionsPbPb) { + if (hmpid.centrality() < centralityCut) { + continue; + } + } + + // Fill distributions for absorption cross section measurement + if (isPos && chamber == Rich4) { + registryDA.fill(HIST("incoming_Pos_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_8cm"), momAbs, drCorr); + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_8cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich4) { + registryDA.fill(HIST("incoming_Neg_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_8cm"), momAbs, drCorr); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_8cm"), momAbs, dyCorr); + } + + if (isPos && chamber == Rich2) { + registryDA.fill(HIST("incoming_Pos_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_4cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich2) { + registryDA.fill(HIST("incoming_Neg_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_4cm"), momAbs, dyCorr); + } + } // end of loop over hmpidTable + } // end of process +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx new file mode 100644 index 00000000000..8ca94444d60 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx @@ -0,0 +1,374 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief Analysis task for the (anti-)deuteron absorption cross section using HMPID +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidCrossSectionCorrections { + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Configurables for main track quality criteria + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for QA selection + Configurable qMipMin{"qMipMin", 200.0f, "minimum MIP charge for QA selection"}; + Configurable qMipMax{"qMipMax", 2000.0f, "maximum MIP charge for QA selection"}; + Configurable cutDistanceMipTrack{"cutDistanceMipTrack", 10.0f, "maximum raw MIP-track distance for QA selection"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 490, "Momentum bins"}; + Configurable momMin{"momMin", 0.1f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable etaNBins{"etaNBins", 100, "Eta bins"}; + Configurable etaMin{"etaMin", -5.0f, "Eta min"}; + Configurable etaMax{"etaMax", 5.0f, "Eta max"}; + + Configurable phiNBins{"phiNBins", 100, "Phi bins"}; + Configurable phiMin{"phiMin", -10.0f, "Phi min"}; + Configurable phiMax{"phiMax", 10.0f, "Phi max"}; + + Configurable mapNBins{"mapNBins", 135, "Map bins (x or y)"}; + Configurable mapMin{"mapMin", 0.0f, "Map min (x or y) in cm"}; + Configurable mapMax{"mapMax", 135.0f, "Map max (x or y) in cm"}; + + Configurable residualsNBins{"residualsNBins", 400, "Residuals bins"}; + Configurable residualsMin{"residualsMin", -20.0f, "Residuals min (x or y) in cm"}; + Configurable residualsMax{"residualsMax", 20.0f, "Residuals max (x or y) in cm"}; + + static constexpr int Rich0 = 0, Rich1 = 1, Rich2 = 2, Rich3 = 3, Rich4 = 4, Rich5 = 5, Rich6 = 6; + static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisHmpidMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisHmpidMom"}; + AxisSpec axisGlobalMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisGlobalMom"}; + AxisSpec axisMipX{mapNBins, mapMin, mapMax, "X_{MIP} (cm)", "axisMipX"}; + AxisSpec axisMipY{mapNBins, mapMin, mapMax, "Y_{MIP} (cm)", "axisMipY"}; + AxisSpec axisGlobalTrackX{mapNBins, mapMin, mapMax, "X_{track} (cm)", "axisGlobalTrackX"}; + AxisSpec axisGlobalTrackY{mapNBins, mapMin, mapMax, "Y_{track} (cm)", "axisGlobalTrackY"}; + AxisSpec axisEta{etaNBins, etaMin, etaMax, "#eta", "axisEta"}; + AxisSpec axisPhi{phiNBins, phiMin, phiMax, "#phi", "axisPhi"}; + AxisSpec axisResiduals{residualsNBins, residualsMin, residualsMax, "Residuals (cm)", "axisResiduals"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + for (int iCh = 0; iCh < Nch; iCh++) { + registry.add(Form("hEtaRich%d", iCh), Form("hEtaRich%d", iCh), HistType::kTH1F, {axisEta}); + registry.add(Form("hPhiRich%d", iCh), Form("hPhiRich%d", iCh), HistType::kTH1F, {axisPhi}); + + registry.add(Form("hmpidXYHmpidMipRich%dPos", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dPos", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + registry.add(Form("hmpidXYHmpidMipRich%dNeg", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dNeg", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + + registry.add(Form("residualsXVsMomentumRich%dPos", iCh), Form("residualsXVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dPos", iCh), Form("residualsYVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsXVsMomentumRich%dNeg", iCh), Form("residualsXVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dNeg", iCh), Form("residualsYVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + + // Momentum and Position correlations + registry.add(Form("hMomTrackVsHmpidRich%dPos", iCh), Form("hMomTrackVsHmpidRich%dPos", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dPos", iCh), Form("hXTrackVsXMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dPos", iCh), Form("hYTrackVsYMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + registry.add(Form("hMomTrackVsHmpidRich%dNeg", iCh), Form("hMomTrackVsHmpidRich%dNeg", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dNeg", iCh), Form("hXTrackVsXMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dNeg", iCh), Form("hYTrackVsYMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + } // end loop over chambers + + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const int chamber = static_cast(hmpid.chamber()); + + const float momHmpid = hmpid.momentumHmpid(); + const float momTrackAbs = hmpid.momentumTrack(); + const float momHmpidAbs = std::abs(momHmpid); + + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + const bool passQ = hmpid.chargeMip() > qMipMin && hmpid.chargeMip() < qMipMax; + + switch (chamber) { + case Rich0: + registry.fill(HIST("hEtaRich0"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich0"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich1: + registry.fill(HIST("hEtaRich1"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich1"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich2: + registry.fill(HIST("hEtaRich2"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich2"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich3: + registry.fill(HIST("hEtaRich3"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich3"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich4: + registry.fill(HIST("hEtaRich4"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich4"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich5: + registry.fill(HIST("hEtaRich5"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich5"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich6: + registry.fill(HIST("hEtaRich6"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich6"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + default: + break; + } // end switch on chamber + } // end of loop over hmpidTable + } // end process +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx deleted file mode 100644 index af8f96dac9a..00000000000 --- a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// \brief Analysis task for the (anti-)deuteron absorption cross section using HMPID -// \authors rocco.liotino@cern.ch - -#include "tableHMPID.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -using namespace o2; -using namespace o2::framework; - -struct HmpidDeuteron { - HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; - - Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; - Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; - Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; - Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.0, "nsigmaTOFMax"}; - Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; - Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; - Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; - Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; - Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; - Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; - Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; - - Configurable qMipThreshold{"qMipThreshold", 120.0f, "minimum MIP charge required for a real signal"}; - Configurable cutDistanceMipTrack{"cutDistanceMipTrack", 6.0f, "cut distance between MIP and track"}; - - Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; - Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; - - Configurable collisionsPbPb{"collisionsPbPb", false, "Require PbPb collisions"}; - - Configurable centralityCut{"centralityCut", 20.0f, "centrality cut for PbPb collisions"}; - - // Configurables for plotting - // Momentum - Configurable momNBins{"momNBins", 490, "Momentum bins"}; - Configurable momMin{"momMin", 0.1f, "Momentum min"}; - Configurable momMax{"momMax", 5.0f, "Momentum max"}; - - // DeltaR - Configurable deltaRNBins{"deltaRNBins", 50, "DeltaR bins"}; - Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; - Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; - - // nSigma TPC - Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 100, "nSigmaTPC bins"}; - Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; - Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; - - // nSigma TOF - Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 100, "nSigmaTOF bins"}; - Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; - Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; - - // Charge - Configurable chargeNBins{"chargeNBins", 1000, "Charge bins"}; - Configurable chargeMin{"chargeMin", 200.0f, "Charge min"}; - Configurable chargeMax{"chargeMax", 2200.0f, "Charge max"}; - - // Eta - Configurable etaNBins{"etaNBins", 100, "Eta bins"}; - Configurable etaMin{"etaMin", -5.0f, "Eta min"}; - Configurable etaMax{"etaMax", 5.0f, "Eta max"}; - - // Phi - Configurable phiNBins{"phiNBins", 100, "Phi bins"}; - Configurable phiMin{"phiMin", -10.0f, "Phi min"}; - Configurable phiMax{"phiMax", 10.0f, "Phi max"}; - - // Mass - Configurable massNBins{"massNBins", 1000, "Mass bins"}; - Configurable massMin{"massMin", -10.0f, "Mass min"}; - Configurable massMax{"massMax", 10.0f, "Mass max"}; - - // variables for chamber_number and HVs/PCs - static constexpr int Rich0 = 0, Rich1 = 1, Rich2 = 2, Rich3 = 3, Rich4 = 4, Rich5 = 5, Rich6 = 6; - static constexpr int Nch = 7; - static constexpr float Nmean = 1.28947; // meanIdxRad(); method from param not working, using value from HMPIDBase/Param.cxx - - void init(o2::framework::InitContext const&) - { - - // Axes for histograms - AxisSpec axisMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisMom"}; - AxisSpec axisDeltaR{deltaRNBins, deltaRMin, deltaRMax, "#Delta R (cm)", "axisDeltaR"}; - AxisSpec axisNsigmaTOF{nSigmaTOFNBins, nSigmaTOFMinPlot, nSigmaTOFMaxPlot, "n#sigma_{TOF}", "axisNsigmaTOF"}; - AxisSpec axisNsigmaTPC{nSigmaTPCNBins, nSigmaTPCMinPlot, nSigmaTPCMaxPlot, "n#sigma_{TPC}", "axisNsigmaTPC"}; - AxisSpec axisCharge{chargeNBins, chargeMin, chargeMax, "Charge (ADC)", "axisCharge"}; - AxisSpec axisEta{etaNBins, etaMin, etaMax, "#eta", "axisEta"}; - AxisSpec axisPhi{phiNBins, phiMin, phiMax, "#phi", "axisPhi"}; - AxisSpec axisMass{massNBins, massMin, massMax, "mass^{2} (GeV/#it{c}^{2})", "axisMass"}; - AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; - - // Deuteron Pos - registryDA.add("incomingDe_Pos_8cm", "incomingDe_Pos_8cm", HistType::kTH1F, {axisMom}); - registryDA.add("incomingDe_Pos_4cm", "incomingDe_Pos_4cm", HistType::kTH1F, {axisMom}); - - registryDA.add("De_Pos_deltaR_8cm", "De_Pos_deltaR_8cm", HistType::kTH1F, {axisDeltaR}); - registryDA.add("De_Pos_deltaR_4cm", "De_Pos_deltaR_4cm", HistType::kTH1F, {axisDeltaR}); - - registryDA.add("nSigmaTOF_vs_momHMPID_DePos_8cm", "nSigmaTOF_vs_momHMPID_DePos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_DePos_4cm", "nSigmaTOF_vs_momHMPID_DePos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("survivingDe_Pos_8cm", "survivingDe_Pos_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("survivingDe_Pos_4cm", "survivingDe_Pos_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); - - registryDA.add("De_Pos_Q_8cm", "De_Pos_Q_8cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Pos_Q_4cm", "De_Pos_Q_4cm", HistType::kTH2F, {axisMom, axisCharge}); - - // nsigma plots - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DePos", "nSigmaTPC_vs_momHMPID_Cut_DePos", HistType::kTH2F, {axisMom, axisNsigmaTPC}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DePos", "nSigmaTOF_vs_momHMPID_Cut_DePos", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - // check residuals distributions - registryDA.add("residualsX_Vs_momentum_Neg_8cm", "residualsX_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Neg_8cm", "residualsY_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - registryDA.add("residualsX_Vs_momentum_Pos_8cm", "residualsX_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Pos_8cm", "residualsY_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - - // Deuteron Neg - registryDA.add("incomingDe_Neg_8cm", "incomingDe_Neg_8cm", HistType::kTH1F, {axisMom}); - registryDA.add("incomingDe_Neg_4cm", "incomingDe_Neg_4cm", HistType::kTH1F, {axisMom}); - - registryDA.add("De_Neg_deltaR_8cm", "De_Neg_deltaR_8cm", HistType::kTH1F, {axisDeltaR}); - registryDA.add("De_Neg_deltaR_4cm", "De_Neg_deltaR_4cm", HistType::kTH1F, {axisDeltaR}); - - registryDA.add("nSigmaTOF_vs_momHMPID_DeNeg_8cm", "nSigmaTOF_vs_momHMPID_DeNeg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_DeNeg_4cm", "nSigmaTOF_vs_momHMPID_DeNeg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("survivingDe_Neg_8cm", "survivingDe_Neg_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("survivingDe_Neg_4cm", "survivingDe_Neg_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); - registryDA.add("De_Neg_Q_8cm", "De_Neg_Q_8cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Neg_Q_4cm", "De_Neg_Q_4cm", HistType::kTH2F, {axisMom, axisCharge}); - registryDA.add("De_Neg_momentum", "De_Neg_momentum", HistType::kTH2F, {{100, 0.0, 5.0, "#it{p}_{vtx} (GeV/#it{c})"}, {100, 0.0, 5.0, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - // check residuals distributions - registryDA.add("residualsX_Vs_momentum_Neg_4cm", "residualsX_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Neg_4cm", "residualsY_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - registryDA.add("residualsX_Vs_momentum_Pos_4cm", "residualsX_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); - registryDA.add("residualsY_Vs_momentum_Pos_4cm", "residualsY_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); - - // nsigma plots - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DeNeg", "nSigmaTPC_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTPC}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DeNeg", "nSigmaTOF_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); - - registryDA.add("pidTPC_TOF_p_ch_DePos", "pidTPC_TOF_p_ch_DePos", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); - registryDA.add("pidTPC_TOF_p_ch_DeNeg", "pidTPC_TOF_p_ch_DeNeg", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); - - // general plots - registryDA.add("hEta", "hEta", kTH1F, {axisEta}); - registryDA.add("hPhi", "hPhi", kTH1F, {axisPhi}); - registryDA.add("hMomentumHmpid", "hMomentumHmpid", kTH1F, {{1000, -10.f, 10.f, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - registryDA.add("hMass", "hMass", kTH1F, {axisMass}); - registryDA.add("hMass_postDeuteron", "hMass_postDeuteron", kTH1F, {axisMass}); - registryDA.add("hMass_Vs_centrality", "hMass_Vs_centrality", kTH2F, {axisMass, {100, 0.0f, 100.0f, "centrality (%)"}}); - - for (int iCh = 0; iCh < Nch; iCh++) { - registryDA.add(Form("De_Pos_deltaR_%d", iCh), Form("De_Pos_deltaR_%d", iCh), HistType::kTH1F, {axisDeltaR}); - registryDA.add(Form("De_Neg_deltaR_%d", iCh), Form("De_Neg_deltaR_%d", iCh), HistType::kTH1F, {axisDeltaR}); - registryDA.add(Form("hEta_%d", iCh), Form("hEta_%d", iCh), HistType::kTH1F, {axisEta}); - registryDA.add(Form("hPhi_%d", iCh), Form("hPhi_%d", iCh), HistType::kTH1F, {axisPhi}); - - // tracking efficiency hmpid - registryDA.add(Form("hAllTracks_%d", iCh), Form("hAllTracks_%d", iCh), HistType::kTH1F, {axisMom}); - registryDA.add(Form("hCutQandD_%d", iCh), Form("hCutQandD_%d", iCh), HistType::kTH1F, {axisMom}); - registryDA.add(Form("hAllTracks_Pos_%d", iCh), Form("hAllTracks_Pos_%d", iCh), HistType::kTH1F, {axisMom}); - registryDA.add(Form("hAllTracks_Neg_%d", iCh), Form("hAllTracks_Neg_%d", iCh), HistType::kTH1F, {axisMom}); - registryDA.add(Form("hCutQandD_Pos_%d", iCh), Form("hCutQandD_Pos_%d", iCh), HistType::kTH1F, {axisMom}); - registryDA.add(Form("hCutQandD_Neg_%d", iCh), Form("hCutQandD_Neg_%d", iCh), HistType::kTH1F, {axisMom}); - } - } - - void process(aod::HmpidAnalysis const& hmpidTable) - { - for (const auto& hmpid : hmpidTable) { - - // ------------------------- - // track filters - // ------------------------- - if (hmpid.itsNCluster() < minReqClusterITS) { - continue; - } - if (hmpid.tpcNCluster() < minTPCnClsFound) { - continue; - } - if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { - continue; - } - if (hmpid.tpcChi2() > maxChi2TPC) { - continue; - } - if (hmpid.itsChi2() > maxChi2ITS) { - continue; - } - if (std::abs(hmpid.dcaXY()) > maxDCAxy) { - continue; - } - if (std::abs(hmpid.dcaZ()) > maxDCAz) { - continue; - } - - // ------------------------- - // derived quantities - // ------------------------- - const float momHmpid = hmpid.momentumHmpid(); - const float momAbs = std::abs(momHmpid); - - const float dx = hmpid.xMip() - hmpid.xTrack(); - const float dy = hmpid.yMip() - hmpid.yTrack(); - const float dr = std::hypot(dx - offsetX, dy - offsetY); - const float dMipTrack = std::hypot(dx, dy); - - const float mass = - std::pow(Nmean * momAbs * std::cos(hmpid.chAngle()), 2) - - std::pow(momAbs, 2); - - const int chamber = static_cast(hmpid.chamber()); - const bool isPos = momHmpid > 0; - const bool isNeg = momHmpid < 0; - - const bool passQandD = hmpid.chargeMip() > qMipThreshold && dMipTrack < cutDistanceMipTrack; - - // ------------------------- - // global distributions - // ------------------------- - registryDA.fill(HIST("hEta"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi"), hmpid.phiTrack()); - registryDA.fill(HIST("hMomentumHmpid"), momHmpid); - - if (hmpid.chAngle() > 0) { - registryDA.fill(HIST("hMass"), mass); - registryDA.fill(HIST("hMass_Vs_centrality"), mass, hmpid.centrality()); - } - - // ------------------------- - // Fill plots per chamber - // ------------------------- - - switch (chamber) { - - case Rich0: - registryDA.fill(HIST("hEta_0"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_0"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_0"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_0"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_0"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_0"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_0"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_0"), momAbs); - } - } - break; - - case Rich1: - registryDA.fill(HIST("hEta_1"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_1"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_1"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_1"), momAbs); - } - - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_1"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_1"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_1"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_1"), momAbs); - } - } - break; - - case Rich2: - registryDA.fill(HIST("hEta_2"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_2"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_2"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_2"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_2"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_2"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_2"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_2"), momAbs); - } - } - break; - - case Rich3: - registryDA.fill(HIST("hEta_3"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_3"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_3"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_3"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_3"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_3"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_3"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_3"), momAbs); - } - } - break; - - case Rich4: - registryDA.fill(HIST("hEta_4"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_4"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_4"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_4"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_4"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_4"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_4"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_4"), momAbs); - } - } - break; - - case Rich5: - registryDA.fill(HIST("hEta_5"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_5"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_5"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_5"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_5"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_5"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_5"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_5"), momAbs); - } - } - break; - - case Rich6: - registryDA.fill(HIST("hEta_6"), hmpid.etaTrack()); - registryDA.fill(HIST("hPhi_6"), hmpid.phiTrack()); - registryDA.fill(HIST("hAllTracks_6"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_6"), momAbs); - } - if (isPos) { - registryDA.fill(HIST("hAllTracks_Pos_6"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Pos_6"), momAbs); - } - } - if (isNeg) { - registryDA.fill(HIST("hAllTracks_Neg_6"), momAbs); - if (passQandD) { - registryDA.fill(HIST("hCutQandD_Neg_6"), momAbs); - } - } - break; - - default: - break; - } - - // ------------------------- - // TPC + TOF PID distributions - // ------------------------- - if (isPos) { - registryDA.fill(HIST("pidTPC_TOF_p_ch_DePos"), momAbs, hmpid.tpcNSigmaDe(), hmpid.tofNSigmaDe(), chamber); - } - if (isNeg) { - registryDA.fill(HIST("pidTPC_TOF_p_ch_DeNeg"), momAbs, hmpid.tpcNSigmaDe(), hmpid.tofNSigmaDe(), chamber); - } - - // ------------------------- - // deuteron candidate cuts - TPC - // ------------------------- - if (hmpid.tpcNSigmaDe() < nsigmaTPCMin || - hmpid.tpcNSigmaDe() > nsigmaTPCMax) { - continue; - } - - // ------------------------- - // post TPC cut - // ------------------------- - if (isPos) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DePos"), momAbs, hmpid.tpcNSigmaDe()); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos"), momAbs, hmpid.tofNSigmaDe()); - } - if (isNeg) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DeNeg"), momAbs, hmpid.tpcNSigmaDe()); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg"), momAbs, hmpid.tofNSigmaDe()); - } - - if (isPos && chamber == Rich2) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_4cm"), momAbs, hmpid.tofNSigmaDe()); - } - if (isPos && chamber == Rich4) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DePos_8cm"), momAbs, hmpid.tofNSigmaDe()); - } - if (isNeg && chamber == Rich2) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_4cm"), momAbs, hmpid.tofNSigmaDe()); - } - if (isNeg && chamber == Rich4) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_DeNeg_8cm"), momAbs, hmpid.tofNSigmaDe()); - } - - registryDA.fill(HIST("hMass_postDeuteron"), mass); - - // ------------------------- - // deuteron candidate cuts - TOF - // ------------------------- - if (hmpid.tofNSigmaDe() < nsigmaTOFMin || - hmpid.tofNSigmaDe() > nsigmaTOFMax) { - continue; - } - - // ------------------------- - // TOF nsigma - // ------------------------- - if (isPos) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DePos"), - momAbs, hmpid.tofNSigmaDe()); - } - - if (isNeg) { - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DeNeg"), - momAbs, hmpid.tofNSigmaDe()); - } - - // ------------------------- - // DEUTERON CANDIDATES AFTER ALL CUTS - // ------------------------- - - // Pb-Pb collisions centrality requirement - if (collisionsPbPb) { - if (hmpid.centrality() < centralityCut) { - continue; - } - } - - // ------------------------- - // deltaR per chamber (Pos/Neg) - // ------------------------- - if (chamber == Rich0) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_0"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_0"), dr); - } - } - if (chamber == Rich1) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_1"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_1"), dr); - } - } - if (chamber == Rich2) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_2"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_2"), dr); - } - } - if (chamber == Rich3) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_3"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_3"), dr); - } - } - if (chamber == Rich4) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_4"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_4"), dr); - } - } - if (chamber == Rich5) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_5"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_5"), dr); - } - } - if (chamber == Rich6) { - if (isPos) { - registryDA.fill(HIST("De_Pos_deltaR_6"), dr); - } - if (isNeg) { - registryDA.fill(HIST("De_Neg_deltaR_6"), dr); - } - } - - // ------------------------- - // absorbers - // ------------------------- - const int abs4cm = 2; - const int abs8cm = 4; - - if (isPos && chamber == abs8cm) { - registryDA.fill(HIST("incomingDe_Pos_8cm"), momAbs); - registryDA.fill(HIST("survivingDe_Pos_8cm"), momAbs, dr); - registryDA.fill(HIST("De_Pos_Q_8cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_deltaR_8cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DePos_8cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Pos_8cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Pos_8cm"), momAbs, dy); - } - - if (isNeg && chamber == abs8cm) { - registryDA.fill(HIST("incomingDe_Neg_8cm"), momAbs); - registryDA.fill(HIST("survivingDe_Neg_8cm"), momAbs, dr); - registryDA.fill(HIST("De_Neg_Q_8cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_deltaR_8cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DeNeg_8cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Neg_8cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Neg_8cm"), momAbs, dy); - } - - if (isPos && chamber == abs4cm) { - registryDA.fill(HIST("incomingDe_Pos_4cm"), momAbs); - registryDA.fill(HIST("survivingDe_Pos_4cm"), momAbs, dr); - registryDA.fill(HIST("De_Pos_Q_4cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_deltaR_4cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DePos_4cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Pos_4cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Pos_4cm"), momAbs, dy); - } - - if (isNeg && chamber == abs4cm) { - registryDA.fill(HIST("incomingDe_Neg_4cm"), momAbs); - registryDA.fill(HIST("survivingDe_Neg_4cm"), momAbs, dr); - registryDA.fill(HIST("De_Neg_Q_4cm"), momAbs, hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_deltaR_4cm"), dr); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_DeNeg_4cm"), momAbs, hmpid.tofNSigmaDe()); - - registryDA.fill(HIST("residualsX_Vs_momentum_Neg_4cm"), momAbs, dx); - registryDA.fill(HIST("residualsY_Vs_momentum_Neg_4cm"), momAbs, dy); - } - } // end of loop over hmpidTable - } // end of process -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; -} From ccb326e95b8bc924a2841fb3c9fd2f78d2c8f5fc Mon Sep 17 00:00:00 2001 From: Fernanda Torres <135931275+mtorresc15@users.noreply.github.com> Date: Wed, 4 Nov 2026 02:19:26 -0600 Subject: [PATCH 555/648] [PWGHF] Add normalized decay length to Omegac0 DCAFitter ML inputs (#18576) --- PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h | 5 ++++- PWGHF/DataModel/CandidateReconstructionTables.h | 6 +++++- PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h index 74633679551..2c3fd60bcae 100644 --- a/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h +++ b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h @@ -70,6 +70,7 @@ enum class InputFeaturesOmegacToOmegaPi : uint8_t { impactParCascZ, cosPACharmBaryon, cosPACasc, + normDecayLenCharmBar, // KFParticle variables cosPaOmegacToPv, @@ -136,6 +137,7 @@ class HfMlResponseOmegacToOmegaPi : public HfMlResponse CHECK_AND_FILL_VEC_OMEGAC0(impactParCascZ); CHECK_AND_FILL_VEC_OMEGAC0(cosPACharmBaryon); CHECK_AND_FILL_VEC_OMEGAC0(cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(normDecayLenCharmBar); } } @@ -192,7 +194,8 @@ class HfMlResponseOmegacToOmegaPi : public HfMlResponse FILL_MAP_OMEGAC0(impactParCascXY), FILL_MAP_OMEGAC0(impactParCascZ), FILL_MAP_OMEGAC0(cosPACharmBaryon), - FILL_MAP_OMEGAC0(cosPACasc)}; + FILL_MAP_OMEGAC0(cosPACasc), + FILL_MAP_OMEGAC0(normDecayLenCharmBar)}; MlResponse::mAvailableInputFeatures.insert(mapDcaFeatures.begin(), mapDcaFeatures.end()); } diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index d46d02116a2..6036b9238bc 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -1222,6 +1222,9 @@ DECLARE_SOA_DYNAMIC_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, DECLARE_SOA_DYNAMIC_COLUMN(PtKaFromCasc, ptKaFromCasc, [](float px, float py) -> float { return RecoDecay::pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, + [](float decLen, float errDecLen) -> float { return decLen / errDecLen; }); + } // end of namespace hf_cand_xic0_omegac0 // declare dedicated Omegac and Xic to Xi Pi candidate table @@ -1288,7 +1291,8 @@ DECLARE_SOA_TABLE(HfCandToOmegaPi, "AOD", "HFCANDTOOMEGAPI", hf_cand_xic0_omegac0::DcaXYToPvV0Dau0, hf_cand_xic0_omegac0::DcaXYToPvV0Dau1, hf_cand_xic0_omegac0::DcaXYToPvCascDau, hf_cand_xic0_omegac0::DcaZToPvV0Dau0, hf_cand_xic0_omegac0::DcaZToPvV0Dau1, hf_cand_xic0_omegac0::DcaZToPvCascDau, hf_cand_xic0_omegac0::DcaCascDau, hf_cand_xic0_omegac0::DcaV0Dau, hf_cand_xic0_omegac0::DcaCharmBaryonDau, - hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag); + hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag, + hf_cand_xic0_omegac0::NormDecayLenCharmBar); DECLARE_SOA_TABLE(HfCandToOmegaK, "AOD", "HFCANDTOOMEGAK", o2::soa::Index<>, diff --git a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx index e5c0754aa1c..55ba2da9efc 100644 --- a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx +++ b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx @@ -377,7 +377,7 @@ struct HfTreeCreatorToOmegaPiQa { candidate.errorDecayLengthCharmBaryon(), candidate.impactParCascXY() / candidate.errImpactParCascXY(), candidate.impactParBachFromCharmBaryonXY() / candidate.errImpactParBachFromCharmBaryonXY(), - candidate.decLenCharmBaryon() / candidate.errorDecayLengthCharmBaryon(), + candidate.normDecayLenCharmBar(), candidate.template bachelorFromCharmBaryon_as().isGlobalTrackWoDCA(), candidate.template bachelorFromCharmBaryon_as().itsNCls(), candidate.template bachelorFromCharmBaryon_as().tpcNClsCrossedRows(), From 2e5cb24e602d54a2708f301e8d8514148419fdb3 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Wed, 4 Nov 2026 10:29:38 +0100 Subject: [PATCH 556/648] [PWGCF] Fixes in femto frame work (#18578) --- PWGCF/Femto/Core/closePairRejection.h | 34 +- PWGCF/Femto/Core/closeTripletRejection.h | 22 +- PWGCF/Femto/Core/histManager.h | 14 + PWGCF/Femto/Core/mcBuilder.h | 41 +- PWGCF/Femto/Core/modes.h | 17 + PWGCF/Femto/Core/pairBuilder.h | 16 +- PWGCF/Femto/Core/pairCleaner.h | 22 +- PWGCF/Femto/Core/pairHistManager.h | 12 +- PWGCF/Femto/Core/trackBuilder.h | 15 + PWGCF/Femto/Core/trackHistManager.h | 197 +++++--- PWGCF/Femto/Core/tripletCleaner.h | 8 +- PWGCF/Femto/Core/tripletHistManager.h | 89 ++-- PWGCF/Femto/TableProducer/femtoProducer.cxx | 438 +++++++++++------- .../femtoProducerDerivedToDerived.cxx | 416 ++++++++++------- PWGCF/Femto/Tasks/femtoCascadeQa.cxx | 2 +- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 6 +- PWGCF/Femto/Tasks/femtoTrackQa.cxx | 2 +- PWGCF/Femto/Tasks/femtoV0Qa.cxx | 2 +- 18 files changed, 828 insertions(+), 525 deletions(-) diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index 67c1e92f532..d7d8b320171 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -17,6 +17,7 @@ #define PWGCF_FEMTO_CORE_CLOSEPAIRREJECTION_H_ #include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" #include "Common/Core/RecoDecay.h" @@ -61,7 +62,8 @@ enum CprHist { // template configurable group for Cpr // DefaultCutAverage: default of cutAverage (e.g. false for pairs of generated particles, where no detector effects are present) -template +// DefaultKinematicVariable: default of kinematicVariable (e.g. Q3 for triplets, kstar for pairs) +template struct ConfCpr : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); o2::framework::Configurable cutAverage{"cutAverage", DefaultCutAverage, "Apply CPR if the average deta-dphistar is below the configured values"}; @@ -71,6 +73,7 @@ struct ConfCpr : o2::framework::ConfigurableGroup { o2::framework::Configurable plotAverage{"plotAverage", true, "Plot average deta dphi distribution"}; o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2)"}; o2::framework::Configurable plotKinematic{"plotKinematic", true, "Plot kinematic (kstar/Q3) distribution of blocked pairs/triplets"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(DefaultKinematicVariable), "Type of kinematic variable (0->kstar, 1->kT, 2->mT, 3->Q3) reported by the kinematic plot"}; o2::framework::Configurable detaMax{"detaMax", 0.01f, "Maximium deta"}; o2::framework::Configurable dphistarMax{"dphistarMax", 0.01f, "Maximum dphistar"}; o2::framework::Configurable detaCenter{"detaCenter", 0.f, "Center of deta cut"}; @@ -241,6 +244,7 @@ class CloseTrackRejection mPlotAngularCorrelation = confCpr.plotAngularCorrelation.value; mPlotKinematic = confCpr.plotKinematic.value; + mKinematicVariable = static_cast(confCpr.kinematicVariable.value); if (confCpr.seed.value >= 0) { uint64_t randomSeed = 0; @@ -259,7 +263,8 @@ class CloseTrackRejection mHistogramRegistry = registry; if (mPlotKinematic) { - mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), getHistDesc(kKinematic, HistTable), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); } if (mPlotAverage) { @@ -301,9 +306,9 @@ class CloseTrackRejection bool isClose = mUseEpisoidalCut ? isClosePairEpisoidalCut() : isClosePairRectengularCut(); if (isClose) { - fillBlocked(pairHistManager.getKinematic()); + fillBlocked(pairHistManager.getKinematic(mKinematicVariable)); } else { - fillPassed(pairHistManager.getKinematic()); + fillPassed(pairHistManager.getKinematic(mKinematicVariable)); } return isClose; @@ -493,6 +498,7 @@ class CloseTrackRejection bool mPlotAverage = false; bool mPlotAngularCorrelation = false; bool mPlotKinematic = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; float mKinematicMin = -1.f; float mKinematicMax = -1.f; @@ -531,10 +537,10 @@ template class ClosePairRejectionTrackTrack { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -597,10 +603,10 @@ template class ClosePairRejectionTrackV0 // can also be used for any particle type that has pos/neg daughters, like resonances { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -719,10 +725,10 @@ template class ClosePairRejectionTrackKink { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -753,10 +759,10 @@ template class ClosePairRejectionTrackTwoPhotonResonance { public: - template + template void init(o2::framework::HistogramRegistry* /*registry*/, std::map> const& /*specs*/, - T const& /*confCpr*/, + T1 const& /*confCpr*/, int /*absChargeTrack*/) { } @@ -774,10 +780,10 @@ template class ClosePairRejectionMcParticleMcParticle { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr) + T1 const& confCpr) { mCtr.init(registry, specs, confCpr, 1, 1); mCtr.setMagField(confCpr.magField.value); diff --git a/PWGCF/Femto/Core/closeTripletRejection.h b/PWGCF/Femto/Core/closeTripletRejection.h index 3a2ad4c6847..a3e2f1b8cc7 100644 --- a/PWGCF/Femto/Core/closeTripletRejection.h +++ b/PWGCF/Femto/Core/closeTripletRejection.h @@ -17,6 +17,7 @@ #define PWGCF_FEMTO_CORE_CLOSETRIPLETREJECTION_H_ #include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/modes.h" #include #include @@ -31,11 +32,12 @@ constexpr const char PrefixCtrTrackTrackV0[] = "CtrTrackTrackV0"; constexpr const char PrefixCtrTrackTrackCascade[] = "CtrTrackTrackCascade"; constexpr const char PrefixCtrMcParticleMcParticleMcParticle[] = "CtrMcParticleMcParticleMcParticle"; -using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; -using ConfCtrTrackTrackV0 = closepairrejection::ConfCpr; -using ConfCtrTrackTrackCascade = closepairrejection::ConfCpr; +// Q3 (not kstar) is the natural kinematic variable for triplets, so it is the default here +using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; +using ConfCtrTrackTrackV0 = closepairrejection::ConfCpr; +using ConfCtrTrackTrackCascade = closepairrejection::ConfCpr; // generated particles, no cut by default (close triplet rejection corrects a detector effect) -using ConfCtrMcParticleMcParticleMcParticle = closepairrejection::ConfCpr; +using ConfCtrMcParticleMcParticleMcParticle = closepairrejection::ConfCpr; // directory names constexpr char PrefixTrack1Track2Se[] = "CPR_Track1Track2/SE/"; @@ -81,10 +83,10 @@ class CloseTripletRejectionTrackTrackTrack CloseTripletRejectionTrackTrackTrack() = default; ~CloseTripletRejectionTrackTrackTrack() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2, int absChargeTrack3) @@ -128,10 +130,10 @@ class CloseTripletRejectionTrackTrackV0 CloseTripletRejectionTrackTrackV0() = default; ~CloseTripletRejectionTrackTrackV0() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -225,10 +227,10 @@ class CloseTripletRejectionMcParticleMcParticleMcParticle CloseTripletRejectionMcParticleMcParticleMcParticle() = default; ~CloseTripletRejectionMcParticleMcParticleMcParticle() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr) + T1 const& confCpr) { mCtr12.init(registry, specs, confCpr); mCtr23.init(registry, specs, confCpr); diff --git a/PWGCF/Femto/Core/histManager.h b/PWGCF/Femto/Core/histManager.h index a0070a08f9f..a5f2aeb6175 100644 --- a/PWGCF/Femto/Core/histManager.h +++ b/PWGCF/Femto/Core/histManager.h @@ -85,5 +85,19 @@ constexpr const char* getHistDesc(EnumType variable, const ArrayType& array) return it != array.end() ? it->histdesc.data() : ""; } + +// Replace the x-axis segment of a "title; x axis; y axis[; z axis...]" histogram description +// with newTitle. Used to patch a static HistTable description at runtime once the actual axis +// quantity (e.g. which momentum type, which kinematic variable) is resolved from a Configurable. +inline std::string replaceFirstAxisTitle(std::string_view desc, std::string_view newTitle) +{ + std::string result(desc); + const auto first = result.find(';'); + const auto second = (first == std::string::npos) ? std::string::npos : result.find(';', first + 1); + if (second != std::string::npos) { + result.replace(first + 1, second - first - 1, " " + std::string(newTitle) + " "); + } + return result; +} } // namespace o2::analysis::femto::histmanager #endif // PWGCF_FEMTO_CORE_HISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index c0f61d477f5..8cde761ad01 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -673,25 +673,11 @@ class McBuilder if (!mcParticle.has_mothers()) { return -1; } - auto motherIds = mcParticle.mothersIds(); - // adapted these checks from MCUtils in PWGEM - const int defaultMotherSize = 2; - std::vector allMotherIds; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - // keep for now, might be needed later - // && ((80 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 90) || (100 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 110))) { - // Pythia: mother range - for (int i = motherIds[0]; i <= motherIds[1]; i++) { - allMotherIds.push_back(i); - } - } else { - // Otherwise just use them as given - for (const int& id : motherIds) { - allMotherIds.push_back(id); - } - } + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on the particle's gen status code) + std::vector allMotherIds; + RecoDecay::getOrderedMotherIds(mcParticle, allMotherIds, /*searchUpToQuark=*/true); // Loop over all mothers - for (const int& i : allMotherIds) { + for (const int64_t& i : allMotherIds) { if (i < 0 || i >= mcParticles.size()) { continue; @@ -722,19 +708,12 @@ class McBuilder if (!current.has_mothers()) { break; } - auto motherIds = current.mothersIds(); - int nextIndex = -1; - const int defaultMotherSize = 2; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - nextIndex = motherIds[0]; - } else { - for (const int& id : motherIds) { - if (id >= 0 && id < mcParticles.size()) { - nextIndex = id; - break; - } - } - } + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on + // the particle's gen status code). This function only ever follows a single branch upward, + // so just the first ordered id is used. + std::vector motherIdsOrdered; + RecoDecay::getOrderedMotherIds(current, motherIdsOrdered, /*searchUpToQuark=*/true); + int64_t nextIndex = motherIdsOrdered.empty() ? -1 : motherIdsOrdered.front(); if (nextIndex < 0 || nextIndex >= mcParticles.size()) { break; diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index 1f4b9d16b3d..1920096a835 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -282,5 +282,22 @@ enum class KinematicVariable : o2::analysis::femto::datatypes::KinematicVariable kKinematicVariableLast = 4 }; +/// axis title for the kinematic variable plotted by CPR / pair- and triplet-cleaner QA histograms +constexpr const char* kinematicVariableToAxisTitle(KinematicVariable type) +{ + switch (type) { + case KinematicVariable::kKstar: + return "k* (GeV/#it{c})"; + case KinematicVariable::kKt: + return "k_{T} (GeV/#it{c})"; + case KinematicVariable::kMt: + return "m_{T} (GeV/#it{c}^{2})"; + case KinematicVariable::kQ3: + return "Q_{3} (GeV/#it{c})"; + default: + return "kinematic variable (GeV/#it{c})"; + } +} + }; // namespace o2::analysis::femto::modes #endif // PWGCF_FEMTO_CORE_MODES_H_ diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index a2c46bbb875..dd157bbb0ae 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -91,9 +91,7 @@ class PairTrackTrackBuilder std::map> const& trackHistSpec2, std::map> const& pairHistSpec, std::map> const& cprHistSpec, - std::map> const& pairCleanerHistSpec, - int puritySlotMask1 = 1, - int puritySlotMask2 = 1) + std::map> const& pairCleanerHistSpec) { // check if correlate the same tracks or not @@ -108,8 +106,8 @@ class PairTrackTrackBuilder if (mSameSpecies) { mTrackCleaner1.init(confTrackCleaner1); - mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); - mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -121,10 +119,10 @@ class PairTrackTrackBuilder } else { mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); - mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2, puritySlotMask2); - mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1, puritySlotMask1); - mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2, puritySlotMask2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); diff --git a/PWGCF/Femto/Core/pairCleaner.h b/PWGCF/Femto/Core/pairCleaner.h index e8655f0ab3b..358ba59962f 100644 --- a/PWGCF/Femto/Core/pairCleaner.h +++ b/PWGCF/Femto/Core/pairCleaner.h @@ -102,6 +102,9 @@ class BasePairCleaner BasePairCleaner() = default; virtual ~BasePairCleaner() = default; + // pairCuts is the pair/triplet histogram manager's own ConfPairCuts/ConfTripletCuts group, which + // carries this cleaner's own kinematicVariable Configurable (independent from CPR's and from + // whatever the pair/triplet histogram manager itself may be plotting elsewhere). template void init(o2::framework::HistogramRegistry* registry, T1 const& specs, @@ -122,7 +125,9 @@ class BasePairCleaner mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kStats, HistTable), getHistDesc(kStats, HistTable), getHistType(kStats, HistTable), {axisStats}); mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(1, "All analyzed reco pairs"); mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(2, "All blocked reco pairs"); - mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), getHistDesc(kKinematic, HistTable), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); + mKinematicVariable = static_cast(pairCuts.kinematicVariable.value); + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); } protected: @@ -206,6 +211,7 @@ class BasePairCleaner bool mMixPairsWithCommonAncestor = false; bool mMixPairsWithNonCommonAncestor = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; private: // require both particles to originate from the same partonic mother @@ -253,7 +259,7 @@ class TrackTrackPairCleaner : public BasePairCleaner this->fillAll(); bool isClean = this->isCleanParticlePair(track1, track2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -294,7 +300,7 @@ class V0V0PairCleaner : public BasePairCleaner // also works for particl this->isCleanParticlePair(posDaughter1, posDaughter2) && this->isCleanParticlePair(negDaughter1, negDaughter2) && this->isCleanParticlePair(posDaughter1, negDaughter2) && this->isCleanParticlePair(negDaughter1, posDaughter2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -331,7 +337,7 @@ class TrackV0PairCleaner : public BasePairCleaner // also works for part auto negDaughter = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -367,7 +373,7 @@ class TrackKinkPairCleaner : public BasePairCleaner auto chaDaughter = trackTable.rawIteratorAt(kink.chaDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(chaDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -405,7 +411,7 @@ class TrackCascadePairCleaner : public BasePairCleaner auto negDaughter = trackTable.rawIteratorAt(cascade.negDauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(bachelor, track) && this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -443,7 +449,7 @@ class TrackLcPairCleaner : public BasePairCleaner auto prong2 = trackTable.rawIteratorAt(lc.prong2DauId() - trackTable.offset()); bool isClean = this->isCleanParticlePair(prong0, track) && this->isCleanParticlePair(prong1, track) && this->isCleanParticlePair(prong2, track); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -478,7 +484,7 @@ class McParticleMcParticlePairCleaner : public BasePairCleaner this->fillAll(); bool isClean = this->isCleanParticlePair(particle1, particle2); if (!isClean) { - this->fillBlocked(pairHistManager.getKinematic()); + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index 5d227b7500d..b6bb3ca1942 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -249,7 +249,6 @@ struct ConfPairBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis dalitzM12{"dalitzM12", {{100, 0, 10}}, "Mass12 binning of darlitz plot"}; o2::framework::ConfigurableAxis dalitzM13{"dalitzM13", {{100, 0, 10}}, "Mass13 binning of darlitz plot"}; o2::framework::Configurable transverseMassType{"transverseMassType", static_cast(modes::TransverseMassType::kAveragePdgMass), "Type of transverse mass (0-> Average Pdg Mass, 1-> Reduced Pdg Mass, 2-> Mt from combined 4 vector)"}; - o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kKstar), "Type of kinematic variable (0->kstar, 1->kT, 2->mT) for PC and CPR plots"}; o2::framework::ConfigurableAxis binningDeltaEta{"binningDeltaEta", {{35, -1.6, 1.6}}, "Delta eta"}; o2::framework::ConfigurableAxis binningDeltaPhi{"binningDeltaPhi", {{35, -o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}}, "Delta phi"}; o2::framework::Configurable plotBertschPratt{"plotBertschPratt", false, "(Reco/Mc) Enable 1D projections and 3D (q_out, q_side, q_long) Bertsch-Pratt histograms in LCMS"}; @@ -282,6 +281,7 @@ struct ConfPairCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kKstar), "Type of kinematic variable (0->kstar, 1->kT, 2->mT) reported by the pair-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -667,9 +667,6 @@ class PairHistManager // transverse mass type mMtType = static_cast(ConfPairBinning.transverseMassType.value); - // kinematic variable - mKinematicVariable = static_cast(ConfPairBinning.kinematicVariable.value); - // values for cuts mKstarMin = ConfPairCuts.kstarMin.value; mKstarMax = ConfPairCuts.kstarMax.value; @@ -943,9 +940,11 @@ class PairHistManager } } - float getKinematic() const + // kinematicVariable is supplied by the caller (CPR/pair cleaner), each of which carries its own + // Configurable for it -- this manager has no opinion of its own on which variable to report + float getKinematic(modes::KinematicVariable kinematicVariable) const { - switch (mKinematicVariable) { + switch (kinematicVariable) { case modes::KinematicVariable::kKstar: return mKstar; break; @@ -1614,7 +1613,6 @@ class PairHistManager double mPdgMassNegDau2 = 0; modes::TransverseMassType mMtType = modes::TransverseMassType::kAveragePdgMass; - modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; int mAbsCharge1 = 1; int mAbsCharge2 = 1; diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 9acba874559..3e703ddd94e 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -157,6 +157,8 @@ struct ConfTrackSelection : public o2::framework::ConfigurableGroup { o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; // momentum threshold for PID usage o2::framework::Configurable pidThres{"pidThres", 1.2f, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; + // which species slot this selection corresponds to in the packed purity tables (FitVars only) + o2::framework::Configurable puritySlotMask{"puritySlotMask", 1, "Bitmask (bit N = slot N) selecting which purity slot to plot, read off hPuritySlotAssignment in the producer output."}; }; // Define unique prefixes as constexpr string literals @@ -761,6 +763,19 @@ class TrackBuilder if (!mProduceTracks && !mProduceLiteTracks) { return false; } + // getDaughterIndex() already checks indexMap and skips calling fillTrack() when the track is + // already present, so reaching this point with an existing entry only happens via the direct + // standalone-track path (fillTracks()/fillMcTracks()). Two distinct causes land here: the track + // was already registered as someone's daughter before its own collision was processed, or the + // track is itself associated with more than one collision and was already filled for an earlier + // one. Either way, keep the earlier row rather than writing a duplicate; its mask/fillType + // reflects whichever collision/role filled it first, not necessarily this one. + if (utils::getIndex(track.globalIndex(), indexMap)) { + LOG(warn) << "Track " << track.globalIndex() << " was already filled (as a daughter, or under another " + << "collision it is associated with); skipping the duplicate standalone fill. Its mask/fillType " + << "reflects whichever fill happened first, not necessarily this collision's selection."; + return true; + } int64_t lastIndex = 0; if (mProduceTracks) { trackProducts.producedTracks(collisionBuilder.collisionIndex(), diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 82fcbbd26de..66e4fe3fab3 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -121,6 +121,22 @@ enum TrackHist { kTpctofDeuteron, kTpctofTriton, kTpctofHelium, + // momentum vs tpc vs its pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsItsElectron, + kTpcVsItsPion, + kTpcVsItsKaon, + kTpcVsItsProton, + kTpcVsItsDeuteron, + kTpcVsItsTriton, + kTpcVsItsHelium, + // momentum vs tpc vs tof pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsTofElectron, + kTpcVsTofPion, + kTpcVsTofKaon, + kTpcVsTofProton, + kTpcVsTofDeuteron, + kTpcVsTofTriton, + kTpcVsTofHelium, // purity (species picked at runtime via puritySlotMask, see hPuritySlotAssignment) kItsPurity, kTpcPurity, @@ -161,7 +177,6 @@ struct ConfTrackBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis mass{"mass", {{200, 0.f, 2.f}}, "Mass (if enabled, plot TOF mass, otherwise PDG mass)"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of selected tracks"}; - o2::framework::Configurable puritySlotMask{"puritySlotMask", 1, "Bitmask (bit N = slot N) selecting which purity slot to plot, read off hPuritySlotAssignment in the producer output."}; o2::framework::ConfigurableAxis purityNSigma{"purityNSigma", {{3000, -15, 15}}, "nSigma decoded from the packed purity tables (species picked via puritySlotMask); signed, for the raw Its/Tpc/Tof values"}; o2::framework::ConfigurableAxis purityNSigmaComb{"purityNSigmaComb", {{2000, 0, 20}}, "Combined nSigma (hypot of two raw values); always non-negative, for Tpc+Its/Tpc+Tof"}; o2::framework::ConfigurableAxis fractionDcaXy{"fractionDcaXy", {{300, -0.3, 0.3}}, "DCA_xy (fraction estimation, needs FTrackDcas joined)"}; @@ -229,13 +244,13 @@ struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable momentumType{"momentumType", static_cast(modes::MomentumType::kPAtPv), "Momentum on x-axis (0->Pt, 1->P at PV, 2->P at TPC inner wall)"}; o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; - o2::framework::Configurable plotElectronPid{"plotElectronPid", true, "Generate plots for Electron PID"}; - o2::framework::Configurable plotPionPid{"plotPionPid", true, "Generate plots for Pion PID"}; - o2::framework::Configurable plotKaonPid{"plotKaonPid", true, "Generate plots for Kaon PID"}; + o2::framework::Configurable plotElectronPid{"plotElectronPid", false, "Generate plots for Electron PID"}; + o2::framework::Configurable plotPionPid{"plotPionPid", false, "Generate plots for Pion PID"}; + o2::framework::Configurable plotKaonPid{"plotKaonPid", false, "Generate plots for Kaon PID"}; o2::framework::Configurable plotProtonPid{"plotProtonPid", true, "Generate plots for Proton PID"}; - o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", true, "Generate plots for Deuteron PID"}; - o2::framework::Configurable plotTritonPid{"plotTritonPid", true, "Generate plots for Triton PID"}; - o2::framework::Configurable plotHeliumPid{"plotHeliumPid", true, "Generate plots for Helium PID"}; + o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", false, "Generate plots for Deuteron PID"}; + o2::framework::Configurable plotTritonPid{"plotTritonPid", false, "Generate plots for Triton PID"}; + o2::framework::Configurable plotHeliumPid{"plotHeliumPid", false, "Generate plots for Helium PID"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs DCAxy vs DCAz for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3122}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; o2::framework::ConfigurableAxis itsCluster{"itsCluster", {{8, -0.5, 7.5}}, "ITS cluster"}; @@ -411,6 +426,20 @@ constexpr std::array, kTrackHistLast> {kTpctofDeuteron, o2::framework::HistType::kTH2F, "hTpctofPidDeuteron", "TPC+TOF PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{TOF,de}^{2}}"}, {kTpctofTriton, o2::framework::HistType::kTH2F, "hTpctofPidTriton", "TPC+TOF PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{TOF,tr}^{2}}"}, {kTpctofHelium, o2::framework::HistType::kTH2F, "hTpctofPidHelium", "TPC+TOF PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{TOF,he}^{2}}"}, + {kTpcVsItsElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidElectron", "TPC vs ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{ITS,el}"}, + {kTpcVsItsPion, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidPion", "TPC vs ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{ITS,pi}"}, + {kTpcVsItsKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidKaon", "TPC vs ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{ITS,ka}"}, + {kTpcVsItsProton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidProton", "TPC vs ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{ITS,pr}"}, + {kTpcVsItsDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidDeuteron", "TPC vs ITS PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{ITS,de}"}, + {kTpcVsItsTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidTriton", "TPC vs ITS PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{ITS,tr}"}, + {kTpcVsItsHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidHelium", "TPC vs ITS PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{ITS,he}"}, + {kTpcVsTofElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidElectron", "TPC vs TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{TOF,el}"}, + {kTpcVsTofPion, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidPion", "TPC vs TOF PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{TOF,pi}"}, + {kTpcVsTofKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidKaon", "TPC vs TOF PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{TOF,ka}"}, + {kTpcVsTofProton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidProton", "TPC vs TOF PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{TOF,pr}"}, + {kTpcVsTofDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidDeuteron", "TPC vs TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{TOF,de}"}, + {kTpcVsTofTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidTriton", "TPC vs TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{TOF,tr}"}, + {kTpcVsTofHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidHelium", "TPC vs TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{TOF,he}"}, {kItsPurity, o2::framework::HistType::kTH2F, "hItsPurity", "ITS purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{ITS}"}, {kTpcPurity, o2::framework::HistType::kTH2F, "hTpcPurity", "TPC purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TPC}"}, {kTofPurity, o2::framework::HistType::kTH2F, "hTofPurity", "TOF purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TOF}"}, @@ -517,7 +546,21 @@ constexpr std::array, kTrackHistLast> {kTpctofProton, {(confQa).p, (confQa).tpctofProton}}, \ {kTpctofDeuteron, {(confQa).p, (confQa).tpctofDeuteron}}, \ {kTpctofTriton, {(confQa).p, (confQa).tpctofTriton}}, \ - {kTpctofHelium, {(confQa).p, (confQa).tpctofHelium}}, + {kTpctofHelium, {(confQa).p, (confQa).tpctofHelium}}, \ + {kTpcVsItsElectron, {(confQa).p, (confQa).tpcElectron, (confQa).itsElectron}}, \ + {kTpcVsItsPion, {(confQa).p, (confQa).tpcPion, (confQa).itsPion}}, \ + {kTpcVsItsKaon, {(confQa).p, (confQa).tpcKaon, (confQa).itsKaon}}, \ + {kTpcVsItsProton, {(confQa).p, (confQa).tpcProton, (confQa).itsProton}}, \ + {kTpcVsItsDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).itsDeuteron}}, \ + {kTpcVsItsTriton, {(confQa).p, (confQa).tpcTriton, (confQa).itsTriton}}, \ + {kTpcVsItsHelium, {(confQa).p, (confQa).tpcHelium, (confQa).itsHelium}}, \ + {kTpcVsTofElectron, {(confQa).p, (confQa).tpcElectron, (confQa).tofElectron}}, \ + {kTpcVsTofPion, {(confQa).p, (confQa).tpcPion, (confQa).tofPion}}, \ + {kTpcVsTofKaon, {(confQa).p, (confQa).tpcKaon, (confQa).tofKaon}}, \ + {kTpcVsTofProton, {(confQa).p, (confQa).tpcProton, (confQa).tofProton}}, \ + {kTpcVsTofDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).tofDeuteron}}, \ + {kTpcVsTofTriton, {(confQa).p, (confQa).tpcTriton, (confQa).tofTriton}}, \ + {kTpcVsTofHelium, {(confQa).p, (confQa).tpcHelium, (confQa).tofHelium}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRACK_HIST_MC_MAP(conf) \ @@ -664,31 +707,24 @@ class TrackHistManager TrackHistManager() = default; ~TrackHistManager() = default; - // init for analysis and mc + // init for analysis, qa, mc and fitvars -- from a selection config object (track-track pairs, + // triplets, standalone QA task's own species). puritySlotMask lives on ConfTrackSelection + // itself (it picks which species slot in the packed purity tables this selection corresponds + // to), so it travels along with the selection object instead of needing its own parameter at + // every layer between the task and here. template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, - T const& ConfTrackSelection, - int puritySlotMask = 1) + T const& ConfTrackSelection) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ConfTrackSelection.chargeAbs.value); - mPdgCode = std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value; - mPuritySlotMask = puritySlotMask; - if constexpr (isFlagSet(mode, modes::Mode::kReco)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { - this->template initFitVars(Specs); - } + this->template initCommon(registry, Specs, + std::abs(ConfTrackSelection.chargeAbs.value), + std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value, + ConfTrackSelection.puritySlotMask.value); } + // same, from raw charge/pdg ints (V0/cascade/resonance daughter sub-managers, which have no + // selection config object of their own -- the parent builder already applied the selection) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -696,24 +732,10 @@ class TrackHistManager int ChargeSign, int PdgCodeAbs) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ChargeAbs); - mPdgCode = std::abs(PdgCodeAbs) * ChargeSign; - if constexpr (isFlagSet(mode, modes::Mode::kReco)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { - this->template initFitVars(Specs); - } + this->template initCommon(registry, Specs, std::abs(ChargeAbs), std::abs(PdgCodeAbs) * ChargeSign, 1); } - // init for analysis and qa and mc + // same as above, but also configure the QA-only optional histograms (species PID toggles, plot2d, ...) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -746,7 +768,7 @@ class TrackHistManager this->fillQa(track); } if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { - this->fillFitVars(track); + this->template fillFitVars(track); } } @@ -763,11 +785,36 @@ class TrackHistManager this->template fillMc(track, col, mcParticles, mcMothers, mcPartonicMothers); } if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { - this->fillFitVars(track); + this->template fillFitVars(track); } } private: + // shared dispatch for all init() overloads above -- the only thing that differs between them + // is how mAbsCharge/mPdgCode/mPuritySlotMask get computed + template + void initCommon(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + int absCharge, int pdgCode, int puritySlotMask) + { + mHistogramRegistry = registry; + mAbsCharge = absCharge; + mPdgCode = pdgCode; + mPuritySlotMask = puritySlotMask; + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(Specs); + } + } + template void enableOptionalHistograms(T const& ConfBinningQa) { @@ -804,16 +851,22 @@ class TrackHistManager } // purity (packed nSigma tables) and fraction (DCA) -- only called when mode carries kFitVars, - // which also implies the track type actually has the relevant tables joined + // which also implies the track type actually has the relevant tables joined. + // Purity itself is skipped in QA mode: the QA task already has full per-species PID + // (Its/Tpc/Tof/Tpcits/Tpctof/TpcVsIts/TpcVsTof in PID/), so the puritySlotMask-selected, + // single-species Purity histograms would be pure redundancy there -- they only earn their + // keep in the pair task, where there is no per-species breakdown to fall back on. template void initFitVars(std::map> const& Specs) { std::string fitVarsDir = std::string(prefix) + std::string(FitVarsDir); - mHistogramRegistry->add(fitVarsDir + getHistNameV2(kItsPurity, HistTable), getHistDesc(kItsPurity, HistTable), getHistType(kItsPurity, HistTable), {Specs.at(kItsPurity)}); - mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcPurity, HistTable), getHistDesc(kTpcPurity, HistTable), getHistType(kTpcPurity, HistTable), {Specs.at(kTpcPurity)}); - mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTofPurity, HistTable), getHistDesc(kTofPurity, HistTable), getHistType(kTofPurity, HistTable), {Specs.at(kTofPurity)}); - mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); - mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); + if constexpr (!isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kItsPurity, HistTable), getHistDesc(kItsPurity, HistTable), getHistType(kItsPurity, HistTable), {Specs.at(kItsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcPurity, HistTable), getHistDesc(kTpcPurity, HistTable), getHistType(kTpcPurity, HistTable), {Specs.at(kTpcPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTofPurity, HistTable), getHistDesc(kTofPurity, HistTable), getHistType(kTofPurity, HistTable), {Specs.at(kTofPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); + } mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionDca, HistTable), getHistDesc(kFractionDca, HistTable), getHistType(kFractionDca, HistTable), {Specs.at(kFractionDca)}); // per-origin truth-split fraction-DCA templates; need MC truth, so only booked when the mode @@ -900,6 +953,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofElectron, Specs); this->addPidHistogram(pidDir, kTpcitsElectron, Specs); this->addPidHistogram(pidDir, kTpctofElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofElectron, Specs); } if (mPlotPionPid) { @@ -908,6 +963,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofPion, Specs); this->addPidHistogram(pidDir, kTpcitsPion, Specs); this->addPidHistogram(pidDir, kTpctofPion, Specs); + this->addPidHistogram(pidDir, kTpcVsItsPion, Specs); + this->addPidHistogram(pidDir, kTpcVsTofPion, Specs); } if (mPlotKaonPid) { @@ -916,6 +973,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofKaon, Specs); this->addPidHistogram(pidDir, kTpcitsKaon, Specs); this->addPidHistogram(pidDir, kTpctofKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsItsKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsTofKaon, Specs); } if (mPlotProtonPid) { @@ -924,6 +983,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofProton, Specs); this->addPidHistogram(pidDir, kTpcitsProton, Specs); this->addPidHistogram(pidDir, kTpctofProton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsProton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofProton, Specs); } if (mPlotDeuteronPid) { @@ -932,6 +993,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofDeuteron, Specs); this->addPidHistogram(pidDir, kTpcitsDeuteron, Specs); this->addPidHistogram(pidDir, kTpctofDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofDeuteron, Specs); } if (mPlotTritonPid) { @@ -940,6 +1003,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofTriton, Specs); this->addPidHistogram(pidDir, kTpcitsTriton, Specs); this->addPidHistogram(pidDir, kTpctofTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofTriton, Specs); } if (mPlotHeliumPid) { @@ -948,6 +1013,8 @@ class TrackHistManager this->addPidHistogram(pidDir, kTofHelium, Specs); this->addPidHistogram(pidDir, kTpcitsHelium, Specs); this->addPidHistogram(pidDir, kTpctofHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsItsHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsTofHelium, Specs); } } @@ -955,13 +1022,7 @@ class TrackHistManager /// with the one matching the configured momentum type void addPidHistogram(std::string const& pidDir, TrackHist hist, std::map> const& Specs) { - // descriptions have the form "title; x axis ; y axis" - std::string desc(getHistDesc(hist, HistTable)); - const auto first = desc.find(';'); - const auto second = (first == std::string::npos) ? std::string::npos : desc.find(';', first + 1); - if (second != std::string::npos) { - desc.replace(first + 1, second - first - 1, " " + std::string(modes::momentumTypeToAxisTitle(mMomentumType)) + " "); - } + std::string desc = histmanager::replaceFirstAxisTitle(getHistDesc(hist, HistTable), modes::momentumTypeToAxisTitle(mMomentumType)); mHistogramRegistry->add(pidDir + getHistNameV2(hist, HistTable), desc.c_str(), getHistType(hist, HistTable), {Specs.at(hist)}); } @@ -1005,11 +1066,11 @@ class TrackHistManager // joined on T, independently of the other; mPuritySlotMask picks the species slot (one bit set, // e.g. from cutculator reading hPuritySlotAssignment), everything is filled unconditionally // once that slot is decoded -- no momentum-regime selection, the analyst picks the right - // combination/range downstream - template + // combination/range downstream. Purity itself is skipped in QA mode, mirroring initFitVars. + template void fillFitVars(T const& track) { - if constexpr (requires(T t) { t.itsNSigmaSlot0(); }) { + if constexpr (!isFlagSet(mode, modes::Mode::kQa) && requires(T t) { t.itsNSigmaSlot0(); }) { float itsNSigma = 0.f; float tpcNSigma = 0.f; float tofNSigma = 0.f; @@ -1109,6 +1170,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofElectron, HistTable)), momentum, track.tofNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsElectron, HistTable)), momentum, track.tpcitsNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofElectron, HistTable)), momentum, track.tpctofNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.itsNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.tofNSigmaEl()); } if (mPlotPionPid) { @@ -1117,6 +1180,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofPion, HistTable)), momentum, track.tofNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsPion, HistTable)), momentum, track.tpcitsNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofPion, HistTable)), momentum, track.tpctofNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsPion, HistTable)), momentum, track.tpcNSigmaPi(), track.itsNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofPion, HistTable)), momentum, track.tpcNSigmaPi(), track.tofNSigmaPi()); } if (mPlotKaonPid) { @@ -1125,6 +1190,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofKaon, HistTable)), momentum, track.tofNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsKaon, HistTable)), momentum, track.tpcitsNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofKaon, HistTable)), momentum, track.tpctofNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.itsNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.tofNSigmaKa()); } if (mPlotProtonPid) { @@ -1133,6 +1200,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofProton, HistTable)), momentum, track.tofNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsProton, HistTable)), momentum, track.tpcitsNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofProton, HistTable)), momentum, track.tpctofNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsProton, HistTable)), momentum, track.tpcNSigmaPr(), track.itsNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofProton, HistTable)), momentum, track.tpcNSigmaPr(), track.tofNSigmaPr()); } if (mPlotDeuteronPid) { @@ -1141,6 +1210,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofDeuteron, HistTable)), momentum, track.tofNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsDeuteron, HistTable)), momentum, track.tpcitsNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofDeuteron, HistTable)), momentum, track.tpctofNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.itsNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.tofNSigmaDe()); } if (mPlotTritonPid) { @@ -1149,6 +1220,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofTriton, HistTable)), momentum, track.tofNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsTriton, HistTable)), momentum, track.tpcitsNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofTriton, HistTable)), momentum, track.tpctofNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.itsNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.tofNSigmaTr()); } if (mPlotHeliumPid) { @@ -1157,6 +1230,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofHelium, HistTable)), momentum, track.tofNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsHelium, HistTable)), momentum, track.tpcitsNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofHelium, HistTable)), momentum, track.tpctofNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.itsNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.tofNSigmaHe()); } } diff --git a/PWGCF/Femto/Core/tripletCleaner.h b/PWGCF/Femto/Core/tripletCleaner.h index 85a89c77ff9..1c7f42f4cce 100644 --- a/PWGCF/Femto/Core/tripletCleaner.h +++ b/PWGCF/Femto/Core/tripletCleaner.h @@ -44,7 +44,7 @@ class TrackTrackTrackTripletCleaner : public paircleaner::BasePairCleanerisCleanParticlePair(track2, track3) && this->isCleanParticlePair(track1, track3); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -90,7 +90,7 @@ class TrackTrackV0TripletCleaner : public paircleaner::BasePairCleaner this->isCleanParticlePair(track2, posDaughter) && this->isCleanParticlePair(track2, negDaughter); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -139,7 +139,7 @@ class TrackTrackCascadeTripletCleaner : public paircleaner::BasePairCleanerisCleanParticlePair(track2, negDaughter) && this->isCleanParticlePair(track2, bachelor); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } @@ -182,7 +182,7 @@ class McParticleMcParticleMcParticleTripletCleaner : public paircleaner::BasePai this->isCleanParticlePair(particle2, particle3) && this->isCleanParticlePair(particle1, particle3); if (!isClean) { - this->fillBlocked(tripletHistManager.getKinematic()); + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); } return isClean; } diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index 7ffc306308c..b7eadec7642 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -44,9 +44,8 @@ namespace o2::analysis::femto::triplethistmanager enum TripletHist { // standard 1D kQ3, - kMt, // average mt of all pairs in the triplet - // kstar betweeen single particles - kKstar12, + kMt, // average mt of all pairs in the triplet + kKstar12, // kstar between single particles, always computed kKstar13, kKstar23, // standard 2D @@ -125,7 +124,6 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { std::string prefix = std::string("TripletBinning"); o2::framework::Configurable plot1D{"plot1D", true, "Enable 1D histograms"}; o2::framework::Configurable plot2D{"plot2D", true, "Enable 2D histograms"}; - o2::framework::Configurable plotKstar{"plotKstar", false, "Enable kstar histograms"}; o2::framework::Configurable plotPt1VsPt2VsPt3{"plotPt1VsPt2VsPt3", false, "Enable 3D histogram (Pt1 vs Pt2 vs Pt3)"}; o2::framework::Configurable plotQ3VsPt1VsPt2VsPt3{"plotQ3VsPt1VsPt2VsPt3", false, "Enable 4D histogram (Q3 vs Pt1 vs Pt2 vs Pt3)"}; o2::framework::Configurable plotQ3VsMtVsMult{"plotQ3VsMtVsMult", false, "Enable 3D histogram (Q3 vs Mt vs Mult)"}; @@ -145,7 +143,6 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mass2{"mass2", {{100, 0, 2}}, "Mass binning for particle 2 (if particle has mass getter, otherwise PDG mass)"}; o2::framework::ConfigurableAxis mass3{"mass3", {{100, 0, 2}}, "Mass binning for particle 3 (if particle has mass getter, otherwise PDG mass)"}; o2::framework::Configurable transverseMassType{"transverseMassType", static_cast(modes::TransverseMassType::kAveragePdgMass), "Type of transverse mass (0-> Average Pdg Mass, 1-> Reduced Pdg Mass, 2-> Mt from combined 4 vector)"}; - o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kQ3), "Type of kinematic variable (2->mT, 3->Q3) for PC and CPR plots"}; }; struct ConfTripletCuts : o2::framework::ConfigurableGroup { @@ -163,6 +160,7 @@ struct ConfTripletCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kQ3), "Type of kinematic variable (2->mT, 3->Q3) reported by the triplet-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -337,7 +335,6 @@ class TripletHistManager // transverse mass type mMtType = static_cast(ConfTripletBinning.transverseMassType.value); - mKinematicVariable = static_cast(ConfTripletBinning.kinematicVariable.value); // values for cuts mQ3Min = ConfTripletCuts.q3Min.value; @@ -351,7 +348,6 @@ class TripletHistManager mKstar23Min = ConfTripletCuts.kstar23Min.value; mKstar23Max = ConfTripletCuts.kstar23Max.value; - mPlotKstar = ConfTripletBinning.plotKstar.value; mPlotPt1VsPt2VsPt3 = ConfTripletBinning.plotPt1VsPt2VsPt3.value; mPlotQ3VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsPt1VsPt2VsPt3.value; mPlotQ3VsMtVsMult = ConfTripletBinning.plotQ3VsMtVsMult.value; @@ -360,14 +356,6 @@ class TripletHistManager mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent.value; mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3.value; - // kstar12/13/23 are only computed on demand (they cost 3 extra femtomath::getKstar() calls per - // triplet); anything that needs the value -- the 1D plots, the 7D histogram, or a cut -- - // must be OR'ed in here, otherwise it silently stays at its default of 0 - mRequireKstar = mPlotKstar || mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 || - mKstar12Min > 0.f || mKstar12Max > 0.f || - mKstar13Min > 0.f || mKstar13Max > 0.f || - mKstar23Min > 0.f || mKstar23Max > 0.f; - mPairCorrelationQa = ConfMixing.enablePairCorrelationQa.value; mEventMixingQa = ConfMixing.enableEventMixingQa.value; @@ -417,11 +405,9 @@ class TripletHistManager // set Q3 mQ3 = femtomath::getQ3(mParticle1, mParticle2, mParticle3); - if (mRequireKstar) { - mKstar12 = femtomath::getKstar(mParticle1, mParticle2); - mKstar13 = femtomath::getKstar(mParticle1, mParticle3); - mKstar23 = femtomath::getKstar(mParticle2, mParticle3); - } + mKstar12 = femtomath::getKstar(mParticle1, mParticle2); + mKstar13 = femtomath::getKstar(mParticle1, mParticle3); + mKstar23 = femtomath::getKstar(mParticle2, mParticle3); // if one of the particles has a mass getter, we cache the value for the filling later if constexpr (utils::HasMass) { @@ -470,14 +456,12 @@ class TripletHistManager mMt = mTrueMt; mQ3 = mTrueQ3; - if (mRequireKstar) { - mTrueKstar12 = femtomath::getKstar(mTrueParticle1, mTrueParticle2); - mTrueKstar13 = femtomath::getKstar(mTrueParticle1, mTrueParticle3); - mTrueKstar23 = femtomath::getKstar(mTrueParticle2, mTrueParticle3); - mKstar12 = mTrueKstar12; - mKstar13 = mTrueKstar13; - mKstar23 = mTrueKstar23; - } + mTrueKstar12 = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + mTrueKstar13 = femtomath::getKstar(mTrueParticle1, mTrueParticle3); + mTrueKstar23 = femtomath::getKstar(mTrueParticle2, mTrueParticle3); + mKstar12 = mTrueKstar12; + mKstar13 = mTrueKstar13; + mKstar23 = mTrueKstar23; } // same event: one (truth) collision for true mult/cent @@ -597,9 +581,15 @@ class TripletHistManager } } - float getKinematic() const + // kinematicVariable is supplied by the caller (CPR/triplet cleaner), each of which carries its + // own Configurable for it -- this manager has no opinion of its own on which variable to report. + // kstar has no single value for a triplet, so kKstar reports the average of the three pair kstars. + float getKinematic(modes::KinematicVariable kinematicVariable) const { - switch (mKinematicVariable) { + switch (kinematicVariable) { + case modes::KinematicVariable::kKstar: + return (mKstar12 + mKstar13 + mKstar23) / 3.f; + break; case modes::KinematicVariable::kMt: return mMt; break; @@ -673,6 +663,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3, HistTable), getHistDesc(kQ3, HistTable), getHistType(kQ3, HistTable), {Specs.at(kQ3)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMt, HistTable), getHistDesc(kMt, HistTable), getHistType(kMt, HistTable), {Specs.at(kMt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar12, HistTable), getHistDesc(kKstar12, HistTable), getHistType(kKstar12, HistTable), {Specs.at(kKstar12)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar13, HistTable), getHistDesc(kKstar13, HistTable), getHistType(kKstar13, HistTable), {Specs.at(kKstar13)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar23, HistTable), getHistDesc(kKstar23, HistTable), getHistType(kKstar23, HistTable), {Specs.at(kKstar23)}); } if (mPlot2d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kPt1VsQ3, HistTable), getHistDesc(kPt1VsQ3, HistTable), getHistType(kPt1VsQ3, HistTable), {Specs.at(kPt1VsQ3)}); @@ -684,12 +677,6 @@ class TripletHistManager // TODO: implement histograms differential im mass of the particles } - if (mPlotKstar) { - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar12, HistTable), getHistDesc(kKstar12, HistTable), getHistType(kKstar12, HistTable), {Specs.at(kKstar12)}); - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar13, HistTable), getHistDesc(kKstar13, HistTable), getHistType(kKstar13, HistTable), {Specs.at(kKstar13)}); - mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar23, HistTable), getHistDesc(kKstar23, HistTable), getHistType(kKstar23, HistTable), {Specs.at(kKstar23)}); - } - if (mPlotPt1VsPt2VsPt3) { mHistogramRegistry->add(analysisDir + getHistNameV2(kPt1VsPt2VsPt3, HistTable), getHistDesc(kPt1VsPt2VsPt3, HistTable), getHistType(kPt1VsPt2VsPt3, HistTable), {Specs.at(kPt1VsPt2VsPt3)}); } @@ -743,7 +730,7 @@ class TripletHistManager } } - // pure mc-truth triplet (kMc without kReco) — reuses the mPlot1d/mPlot2d/mPlotKstar flags, + // pure mc-truth triplet (kMc without kReco) — reuses the mPlot1d/mPlot2d flags, // since these are histogram-content flags, not mode flags void initMcTruth(std::map> const& Specs) { @@ -751,6 +738,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3, HistTable), getHistDesc(kTrueQ3, HistTable), getHistType(kTrueQ3, HistTable), {Specs.at(kTrueQ3)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMt, HistTable), getHistDesc(kTrueMt, HistTable), getHistType(kTrueMt, HistTable), {Specs.at(kTrueMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar12, HistTable), getHistDesc(kTrueKstar12, HistTable), getHistType(kTrueKstar12, HistTable), {Specs.at(kTrueKstar12)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar13, HistTable), getHistDesc(kTrueKstar13, HistTable), getHistType(kTrueKstar13, HistTable), {Specs.at(kTrueKstar13)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar23, HistTable), getHistDesc(kTrueKstar23, HistTable), getHistType(kTrueKstar23, HistTable), {Specs.at(kTrueKstar23)}); } if (mPlot2d) { mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsTrueQ3, HistTable), getHistDesc(kTruePt1VsTrueQ3, HistTable), getHistType(kTruePt1VsTrueQ3, HistTable), {Specs.at(kTruePt1VsTrueQ3)}); @@ -760,11 +750,6 @@ class TripletHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueMult, HistTable), getHistDesc(kTrueQ3VsTrueMult, HistTable), getHistType(kTrueQ3VsTrueMult, HistTable), {Specs.at(kTrueQ3VsTrueMult)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueCent, HistTable), getHistDesc(kTrueQ3VsTrueCent, HistTable), getHistType(kTrueQ3VsTrueCent, HistTable), {Specs.at(kTrueQ3VsTrueCent)}); } - if (mPlotKstar) { - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar12, HistTable), getHistDesc(kTrueKstar12, HistTable), getHistType(kTrueKstar12, HistTable), {Specs.at(kTrueKstar12)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar13, HistTable), getHistDesc(kTrueKstar13, HistTable), getHistType(kTrueKstar13, HistTable), {Specs.at(kTrueKstar13)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar23, HistTable), getHistDesc(kTrueKstar23, HistTable), getHistType(kTrueKstar23, HistTable), {Specs.at(kTrueKstar23)}); - } } void fillMcTruth() @@ -772,6 +757,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3, HistTable)), mTrueQ3); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMt, HistTable)), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar12, HistTable)), mTrueKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar13, HistTable)), mTrueKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar23, HistTable)), mTrueKstar23); } if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsTrueQ3, HistTable)), mTrueParticle1.Pt(), mTrueQ3); @@ -781,11 +769,6 @@ class TripletHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueMult, HistTable)), mTrueQ3, mTrueMult); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueCent, HistTable)), mTrueQ3, mTrueCent); } - if (mPlotKstar) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar12, HistTable)), mTrueKstar12); - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar13, HistTable)), mTrueKstar13); - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar23, HistTable)), mTrueKstar23); - } } void fillAnalysis() @@ -793,6 +776,9 @@ class TripletHistManager if (mPlot1d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3, HistTable)), mQ3); mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kMt, HistTable)), mMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar12, HistTable)), mKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar13, HistTable)), mKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar23, HistTable)), mKstar23); } if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt1VsQ3, HistTable)), mParticle1.Pt(), mQ3); @@ -803,12 +789,6 @@ class TripletHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsCent, HistTable)), mQ3, mCent); } - if (mPlotKstar) { - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar12, HistTable)), mKstar12); - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar13, HistTable)), mKstar13); - mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar23, HistTable)), mKstar23); - } - if (mPlotPt1VsPt2VsPt3) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt1VsPt2VsPt3, HistTable)), mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt()); } @@ -857,7 +837,6 @@ class TripletHistManager double mPdgMass3 = 0.; modes::TransverseMassType mMtType = modes::TransverseMassType::kAveragePdgMass; - modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kQ3; int mAbsCharge1 = 1; int mAbsCharge2 = 1; @@ -906,8 +885,6 @@ class TripletHistManager bool mPlot1d = true; bool mPlot2d = true; - bool mPlotKstar = false; - bool mRequireKstar = false; bool mPlotPt1VsPt2VsPt3 = false; bool mPlotQ3VsPt1VsPt2VsPt3 = false; bool mPlotQ3VsMtVsMult = false; diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 4b1082941ca..711183d7327 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -103,22 +103,26 @@ using Run3McGenParticles = o2::aod::McParticles; struct FemtoProducer { - // all Preslice declarations, grouped into a single data member (see o2::framework::PresliceGroup's - // own doc comment) to stay well under the 100-direct-data-member cap on task structs + // all Preslice declarations, grouped into a single data member struct : public o2::framework::PresliceGroup { o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + o2::framework::Preslice perColTracks = o2::aod::track::collisionId; o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColV0s = o2::aod::v0data::collisionId; o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; + o2::framework::Preslice perColCascades = o2::aod::cascdata::collisionId; o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; + o2::framework::Preslice perColSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; o2::framework::Preslice perColRecoSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; + o2::framework::Preslice perColD0s = o2::aod::hf_cand::collisionId; o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColLcs = o2::aod::hf_cand::collisionId; o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; } preslices; - // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ - // Preslice/Produces, so the framework never needs to discover them individually -- free to nest) + // plain builder helper objects, grouped into a single data member struct { collisionbuilder::CollisionBuilder collisionBuilder; trackbuilder::TrackBuilder trackBuilder; @@ -443,16 +447,19 @@ struct FemtoProducer { } // processing tracks + // NOTE: trackBuilder.reset() is NOT called here. It must happen exactly once per dataframe + // (in the calling processTracksXXX/processTracksXXXMc function, before the reco-collision + // loop), not once per collision, so that fillTrack()'s "protect against double filling" dedup + // (indexMap) can actually catch a track referenced from more than one collision in the same + // dataframe instead of being wiped before it gets the chance. template void processTracks(T1 const& col, T2 const& tracksWithItsPid) { - builders.trackBuilder.reset(tracksWithItsPid); builders.trackBuilder.fillTracks(col, builders.collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); } template void processMcTracks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& mcParticles) { - builders.trackBuilder.reset(tracksWithItsPid); builders.trackBuilder.fillMcTracks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, builders.mcBuilder, mcProducts); } @@ -553,163 +560,211 @@ struct FemtoProducer { // process functions - data // ========================================================================== - void processTracksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3pp, "Provide tracks", true); - void processTracksRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPb, "Provide tracks in PbPb collisions", false); - void processTracksRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + void processTracksRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPbWithEventShape, "Provide tracks in PbPb collisions with event shape information", false); - void processTracksV0sRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Provide tracks and v0s", false); - void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, o2::aod::V0PhotonsKF const& photons, o2::aod::V0Legs const& v0legs) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they - // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); - processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); - void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, o2::aod::V0PhotonsKF const& photons, o2::aod::V0Legs const& v0legs) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); - processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); - void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3PbPb, "Provide tracks and v0s in PbPb collisions", false); - void processTracksV0sCascadesRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3pp, "Provide tracks, v0s and cascades", false); - void processTracksV0sCascadesRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksV0sCascadesRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPb, "Provide tracks, v0s and cascades in PbPb collisions", false); - void processTracksV0sCascadesRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + void processTracksV0sCascadesRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPbWithEventShape, "Provide tracks, v0s and cascades in PbPb collisions with event shape information", false); - void processTracksV0sCascadesPhotonsRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape::iterator const& col, + void processTracksV0sCascadesPhotonsRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, @@ -717,71 +772,94 @@ struct FemtoProducer { o2::aod::V0PhotonsKF const& photons, o2::aod::V0Legs const& v0legs) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); - // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they - // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); - processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesPhotonsRun3PbPbWithEventShape, "Provide tracks, v0s, cascades and PCM photons in PbPb collisions with event shape information", false); - void processTracksKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Kinks const& kinks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processKinks(col, tracks, kinks); } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3pp, "Provide tracks and kinks", false); - void processTracksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColSigmaPlusPcms, col.globalIndex()); + processSigmaPlusPcms(col, tracksThisCol, sigmaPlusPcmsThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processSigmaPlusPcms(col, tracks, sigmaPlusPcms); } PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3pp, "Provide tracks and sigma plus reconstructed with a PCM photon", false); - void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, rawinputs::Run3Cascades const& cascades, rawinputs::Run3Kinks const& kinks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); - processKinks(col, tracks, kinks); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksRun3pp, "Provide tracks, v0s, cascades and kinks", false); - void processTracksV0sCascadesKinksPhotonsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesKinksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Vzeros const& v0s, @@ -790,49 +868,66 @@ struct FemtoProducer { o2::aod::V0PhotonsKF const& photons, o2::aod::V0Legs const& v0legs) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, v0s); - processCascades(col, tracks, cascades); - processKinks(col, tracks, kinks); - // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they - // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx - auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); - processPhotons(col, photonsThisCol, v0legs); } PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksPhotonsRun3pp, "Provide tracks, v0s, cascades, kinks and PCM photons", false); - void processTracksD0sRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksD0sRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3D0s const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processD0s(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3pp, "Provide tracks and D0s", false); - void processTracksD0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksD0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3D0s const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processD0s(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPb, "Provide tracks and D0s in PbPb collisions", false); @@ -843,10 +938,10 @@ struct FemtoProducer { // does no auto-grouping. Tables that are iterated over (tracks, v0s, cascades, // kinks) are sliced per collision; the full track table is passed alongside so // the v0/cascade/kink builders can resolve their daughter indices. - // These functions own the per-timeframe reset of the mc builder maps (once, before the - // reco-collision loop, in both pass-through and lazy-fill mode); processMcCollisions never - // resets them itself, since it runs once per reco collision and the dedup maps have to - // survive across all reco collisions in the timeframe. + // These functions own the per-timeframe reset of the mc builder maps AND of trackBuilder + // (once, before the reco-collision loop, in both pass-through and lazy-fill mode); neither + // processMcCollisions nor processMcTracks resets them, since they run once per reco collision + // and the dedup maps have to survive across all reco collisions in the timeframe. // ========================================================================== void processTracksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, @@ -856,6 +951,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -879,6 +975,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -903,6 +1000,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -930,6 +1028,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -958,6 +1057,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -989,6 +1089,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1019,6 +1120,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1046,6 +1148,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1074,6 +1177,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1104,6 +1208,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1131,6 +1236,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1150,33 +1256,43 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPbMc, "Provide reconstructed and generated tracks and D0s in PbPb collisions", false); - void processTracksLcsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + void processTracksLcsRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Lcs const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processLcs(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3pp, "Provide tracks and Lcs", false); - void processTracksLcsRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + void processTracksLcsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, rawinputs::Run3Lcs const& candidates) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processLcs(col, tracks, candidates); } PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3PbPb, "Provide tracks and Lcs in PbPb collisions", false); @@ -1188,6 +1304,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } @@ -1215,6 +1332,7 @@ struct FemtoProducer { rawinputs::Run3McGenParticles const& mcParticles) { builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); if (builders.mcBuilder.isPassThrough()) { builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); } diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 2eca98b293c..c7c560e0f8e 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -213,267 +213,361 @@ struct FemtoProducerDerivedToDerived { LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; } - builders.trackBuilder.init(confTrackBuilder); - builders.v0Builder.init(confV0Builder); - builders.cascadeBuilder.init(confCascadeBuilder); - builders.kinkBuilder.init(confKinkBuilder); - builders.resonanceBuilder.init(confResonanceBuilder); - builders.mcBuilder.init(confMcBuilder); + // which builders the currently active process function actually exercises -- + // only those need their limit configuration validated (see e.g. TrackBuilderDerivedToDerived::init()); + // an unused builder is free to keep its (possibly all-zero) default limits + const bool needsTrackBuilder = + doprocessTracks || doprocessTracksWithMass || doprocessTracksWithFitVars || + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksRho0 || doprocessTracksPhi || + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsV0Builder = + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessLambdas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessLambdasXis; + + const bool needsCascadeBuilder = + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessLambdasXis; + + const bool needsKinkBuilder = + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || doprocessTracksSigmaPlusExtendedMc || + doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsResonanceBuilder = doprocessTracksRho0 || doprocessTracksPhi; + + const bool needsMcBuilder = + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + // skip init() entirely for builders the active process doesn't touch: their limit + // members default to 0 and are only ever read by the collisionHasTooFew*/process* + // methods that the active process also doesn't call, so leaving them uninitialized + // (i.e. at their 0 default) is harmless -- this also sidesteps each builder's "all + // limits are 0" fatal check, which would otherwise fire for an unrelated species + if (needsTrackBuilder) { + builders.trackBuilder.init(confTrackBuilder); + } + if (needsV0Builder) { + builders.v0Builder.init(confV0Builder); + } + if (needsCascadeBuilder) { + builders.cascadeBuilder.init(confCascadeBuilder); + } + if (needsKinkBuilder) { + builders.kinkBuilder.init(confKinkBuilder); + } + if (needsResonanceBuilder) { + builders.resonanceBuilder.init(confResonanceBuilder); + } + if (needsMcBuilder) { + builders.mcBuilder.init(confMcBuilder); + } } // process functions - void processTracks(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks) + void processTracks(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracks, "Process tracks", true); - void processTracksWithMass(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithMass const& tracks) + void processTracksWithMass(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); - void processTracksWithFitVars(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks) + void processTracksWithFitVars(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVars, "Process tracks, carrying through FitVars tables", false); - void processTracksLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) + void processTracksLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksLambdas, "Process lambdas and tracks", false); - void processTracksWithMassLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithMass const& tracks, femtotables::FemtoLambdas const& lambdas) + void processTracksWithMassLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMassLambdas, "Process lambdas and tracks with mass", false); - void processTracksWithFitVarsLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoLambdas const& lambdas) + void processTracksWithFitVarsLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsLambdas, "Process lambdas and tracks, carrying through FitVars tables", false); - void processLambdas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) + void processLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdas, "Process lambdas only (produced track table contains daughers only)", false); - void processTracksXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoXis const& xis) + void processTracksXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoXis const& xis) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksXis, "Process tracks and xis", false); - void processTracksWithFitVarsXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoXis const& xis) + void processTracksWithFitVarsXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoXis const& xis) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsXis, "Process tracks and xis, carrying through FitVars tables", false); - void processTracksOmegas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) + void processTracksOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksOmegas, "Process tracks and omegas", false); - void processTracksWithFitVarsOmegas(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoOmegas const& omegas) + void processTracksWithFitVarsOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoOmegas const& omegas) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsOmegas, "Process tracks and omegas, carrying through FitVars tables", false); - void processTracksK0shorts(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) + void processTracksK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksK0shorts, "Process k0short and tracks", false); - void processTracksWithFitVarsK0shorts(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoK0shorts const& k0shorts) + void processTracksWithFitVarsK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoK0shorts const& k0shorts) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsK0shorts, "Process k0short and tracks, carrying through FitVars tables", false); - void processTracksSigma(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) + void processTracksSigma(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigma, "Process sigma and tracks", false); - void processTracksSigmaPlus(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlus const& sigmaplus) + void processTracksSigmaPlus(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlus const& sigmaplus) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); - void processTracksSigmaPlusExtended(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusExtended const& sigmaplus) + void processTracksSigmaPlusExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusExtended const& sigmaplus) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); - void processTracksSigmaPlusPcm(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcm const& sigmapluspcm) + void processTracksSigmaPlusPcm(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcm const& sigmapluspcm) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcm, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks", false); - void processTracksSigmaPlusPcmExtended(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcmExtended const& sigmapluspcm) + void processTracksSigmaPlusPcmExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcmExtended const& sigmapluspcm) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmExtendedPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtended, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks", false); - void processTracksRho0(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) + void processTracksRho0(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksRho0, "Process rho0 and tracks", false); - void processTracksPhi(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoPhis const& phis) + void processTracksPhi(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoPhis const& phis) { - if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - builders.resonanceBuilder.processPhis(col, phis, tracks, phiPartition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processPhis(col, phis, tracks, phiPartition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); - void processLambdasXis(femtotables::FilteredFemtoCollision const& col, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas, femtotables::FemtoXis const& xis) + void processLambdasXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas, femtotables::FemtoXis const& xis) { - if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { - return; - } builders.trackBuilder.reset(tracks); - builders.collisionBuilder.processCollision(col, collisionBuilderProducts); - builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); - builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdasXis, "Process lambdas and xis", false); // mc processing - // // NOTE: unlike the other process functions, this one subscribes to the full - // collision table and iterates manually. The MC index maps are keyed by source - // global index and must live for the whole dataframe void processTracksMc(femtotables::FilteredFemtoMcCollisions const& cols, femtotables::FemtoMcTracks const& tracks, o2::aod::FMcCols const& mcCols, @@ -483,13 +577,12 @@ struct FemtoProducerDerivedToDerived { { // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { continue; } - // track indexMap is per collision by construction, so it resets here - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, @@ -507,13 +600,14 @@ struct FemtoProducerDerivedToDerived { o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { + // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { continue; } - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, @@ -533,13 +627,14 @@ struct FemtoProducerDerivedToDerived { o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { + // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { continue; } - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, @@ -559,13 +654,14 @@ struct FemtoProducerDerivedToDerived { o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { + // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { continue; } - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, @@ -585,13 +681,14 @@ struct FemtoProducerDerivedToDerived { o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { + // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmPartition, cache)) { continue; } - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, @@ -611,13 +708,14 @@ struct FemtoProducerDerivedToDerived { o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { + // once per dataframe builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); for (auto const& col : cols) { if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmExtendedPartition, cache)) { continue; } - builders.trackBuilder.reset(tracks); builders.collisionBuilder.processCollision(col, collisionBuilderProducts); builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, diff --git a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx index fee88a038c6..78b8ae337dc 100644 --- a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx +++ b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx @@ -51,7 +51,7 @@ struct FemtoCascadeQa { using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoXisWithLabel = o2::soa::Join; using FemtoOmegasWithLabel = o2::soa::Join; diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 0b3299d8895..660f368891a 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -65,7 +65,7 @@ struct FemtoPairTrackTrack { // for analysis which require particles at high pt, add tof mass so sidebands can be used using FemtoTracksWithMass = o2::soa::Join; - // opt-in: adds the packed purity tables (nSigma, see TrackBinning1/2.puritySlotMask) and FTrackDcas + // opt-in: adds the packed purity tables (nSigma, see TrackSelection1/2.puritySlotMask) and FTrackDcas // (fraction estimation) for the FitVars histograms. Needs the producer's // TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled. using FemtoTracksWithFitVars = o2::soa::Join; @@ -173,7 +173,7 @@ struct FemtoPairTrackTrack { trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec, confTrackBinning1.puritySlotMask.value, confTrackBinning2.puritySlotMask.value); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); @@ -185,7 +185,7 @@ struct FemtoPairTrackTrack { trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec, confTrackBinning1.puritySlotMask.value, confTrackBinning2.puritySlotMask.value); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); } hRegistry.print(); }; diff --git a/PWGCF/Femto/Tasks/femtoTrackQa.cxx b/PWGCF/Femto/Tasks/femtoTrackQa.cxx index c247afdb75c..c3dc0898145 100644 --- a/PWGCF/Femto/Tasks/femtoTrackQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackQa.cxx @@ -53,7 +53,7 @@ struct FemtoTrackQa { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; diff --git a/PWGCF/Femto/Tasks/femtoV0Qa.cxx b/PWGCF/Femto/Tasks/femtoV0Qa.cxx index 083f5bcc849..4ae00099140 100644 --- a/PWGCF/Femto/Tasks/femtoV0Qa.cxx +++ b/PWGCF/Femto/Tasks/femtoV0Qa.cxx @@ -52,7 +52,7 @@ struct FemtoV0Qa { using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::soa::Join; using FemtoLambdasWithLabel = o2::soa::Join; using FemtoK0shortsWithLabel = o2::soa::Join; From 6db87c30dfebb732be2b5f9950d891f1db53cac8 Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Wed, 4 Nov 2026 13:15:08 +0300 Subject: [PATCH 557/648] [PWGUD] Cast to int64_t before multiplication (#18579) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index 4acd05ee3a4..d9beaca9aa7 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -87,7 +87,7 @@ using SignalRef = std::pair; using DistanceMap = std::vector>; DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, - int64_t bcSOR, int32_t nBCsPerTF, + int64_t bcSOR, int64_t nBCsPerTF, std::vector& activeBCs, uint32_t maxDistance, bool skipFirstExactMatch = false) { @@ -101,8 +101,10 @@ DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, // The forward-only cursor makes the scan linear after sorting size_t nextIndex = 0; for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { - const int64_t tfStartBC = bcSOR + tfIDs[iTF] * nBCsPerTF; - for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { // TODO why int64_t + // Cast before multiplication to prevent 32-bit integer overflow. + const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; + // Use int64_t to keep BC arithmetic in 64 bits without additional casts. + for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { const int64_t currentBC = tfStartBC + bcInTF; while (nextIndex < activeBCs.size() && activeBCs[nextIndex] < currentBC) { ++nextIndex; From 8178754bb99f67264b38ad74c14ea1b2e397acd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:04:17 +0100 Subject: [PATCH 558/648] [DPG] Fix casts (#18383) --- DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx | 2 +- DPG/Tasks/AOTEvent/lumiQa.cxx | 4 ++-- DPG/Tasks/AOTEvent/timeDependentQa.cxx | 4 ++-- DPG/Tasks/AOTTrack/MonitorFilterBit.cxx | 18 +++++++++--------- DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx | 10 +++++----- .../AOTTrack/V0Cascades/qaEfficiencyV0s.cxx | 12 ++++++------ .../derivedDataCreatorD0Calibration.cxx | 4 ++-- DPG/Tasks/AOTTrack/qaEfficiency.cxx | 6 +++--- DPG/Tasks/AOTTrack/qaEventTrackLite.cxx | 4 ++-- DPG/Tasks/AOTTrack/qaMatchEff.cxx | 2 +- DPG/Tasks/AOTTrack/qaTrackSplitting.cxx | 4 ++-- DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx | 4 ++-- DPG/Tasks/TPC/tpcTreeCreatorLight.cxx | 2 +- 13 files changed, 38 insertions(+), 38 deletions(-) diff --git a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx index acb75778be9..9da260edcec 100644 --- a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx +++ b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx @@ -162,7 +162,7 @@ struct DetectorOccupancyQaTask { } // track QA counters histos.add("nTrackCounter_after_cuts_QA", "", kTH1D, {{12, -0.5, 11.5, "track QA"}}); - TAxis* axTrackCounters = reinterpret_cast(histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis()); + TAxis* axTrackCounters = histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis(); axTrackCounters->SetBinLabel(1, "all"); axTrackCounters->SetBinLabel(2, "PVcontrib"); axTrackCounters->SetBinLabel(3, "ptCut"); diff --git a/DPG/Tasks/AOTEvent/lumiQa.cxx b/DPG/Tasks/AOTEvent/lumiQa.cxx index f03419fff52..6402fc52769 100644 --- a/DPG/Tasks/AOTEvent/lumiQa.cxx +++ b/DPG/Tasks/AOTEvent/lumiQa.cxx @@ -162,13 +162,13 @@ struct LumiQaTask { return; } - hCalibV0A = reinterpret_cast(callst->FindObject("hCalibZeqFV0")); + hCalibV0A = dynamic_cast(callst->FindObject("hCalibZeqFV0")); if (hCalibV0A == nullptr) { LOGF(info, "hCalibZeqFV0 histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; } - hCalibT0C = reinterpret_cast(callst->FindObject("hCalibZeqFT0C")); + hCalibT0C = dynamic_cast(callst->FindObject("hCalibZeqFT0C")); if (hCalibT0C == nullptr) { LOGF(info, "hCalibZeqFT0C histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; diff --git a/DPG/Tasks/AOTEvent/timeDependentQa.cxx b/DPG/Tasks/AOTEvent/timeDependentQa.cxx index 2599f74018b..194b9f49803 100644 --- a/DPG/Tasks/AOTEvent/timeDependentQa.cxx +++ b/DPG/Tasks/AOTEvent/timeDependentQa.cxx @@ -294,7 +294,7 @@ struct TimeDependentQaTask { // ### QA event selection bits int nEvSelBits = enNumEvSelBits; histos.add("hSecondsEventSelBits", "", kTH2F, {axisSecondsWideBins, {nEvSelBits, -0.5, nEvSelBits - 0.5, "Monitoring of event selection bits"}}); - TAxis* axSelBits = reinterpret_cast(histos.get(HIST("hSecondsEventSelBits"))->GetYaxis()); + TAxis* axSelBits = histos.get(HIST("hSecondsEventSelBits"))->GetYaxis(); axSelBits->SetBinLabel(1 + enCollisionsAll, "collisionsAll"); axSelBits->SetBinLabel(1 + enIsTriggerTVX, "IsTriggerTVX"); axSelBits->SetBinLabel(1 + enNoTimeFrameBorder, "NoTimeFrameBorder"); @@ -330,7 +330,7 @@ struct TimeDependentQaTask { // ### QA RCT flags int nRctFlagsTotal = enNumRctFlagsTotal; histos.add("hSecondsRCTflags", "", kTH2F, {axisSecondsWideBins, {nRctFlagsTotal + 2, -0.5, nRctFlagsTotal + 2 - 0.5, "Monitoring of RCT flags"}}); - axRctFlags = reinterpret_cast(histos.get(HIST("hSecondsRCTflags"))->GetYaxis()); + axRctFlags = histos.get(HIST("hSecondsRCTflags"))->GetYaxis(); axRctFlags->SetBinLabel(1, "NcollisionsSel8"); axRctFlags->SetBinLabel(2, "CcdbNotFound"); axRctFlags->SetBinLabel(3 + kCPVBad, "CPVBad"); diff --git a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx index 648c1258127..2b00c8a2599 100644 --- a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx +++ b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx @@ -103,15 +103,15 @@ struct CheckFilterBit { using Tracksextension = soa::Join; using TracksextensionMC = soa::Join; SliceCache cache; - Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - - Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + + Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; void init(InitContext const&) { diff --git a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx index f9b6f0c620a..eaf04ba94da 100644 --- a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx +++ b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx @@ -436,11 +436,11 @@ struct tofPidCollisionTimeQa { histos.fill(HIST("goodforevtime/tofSignalPerCollision"), ncolls % 6000, trk.tofSignal()); } } - static_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); } } PROCESS_SWITCH(tofPidCollisionTimeQa, processData, "Process data", true); diff --git a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx index ff6236ec9a9..8a5a06a9b07 100644 --- a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx +++ b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx @@ -125,12 +125,12 @@ struct QaEfficiencyV0s { } for (int i = 0; i < nSpecies; i++) { - static_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); } } }; diff --git a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx index d5700f2f6a9..4d1f8c3b0f1 100644 --- a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx +++ b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx @@ -160,7 +160,7 @@ struct DerivedDataCreatorD0Calibration { ccdb->setLocalObjectValidityChecking(); if (cfgDownsampling.apply) { - histDownSampl.setObject(reinterpret_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); + histDownSampl.setObject(static_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); } if (cfgMl.apply) { @@ -511,7 +511,7 @@ struct DerivedDataCreatorD0Calibration { collision.covYZ(), collision.covZZ(), collision.numContrib(), - uint8_t(std::round(collision.centFT0C())), + static_cast(std::round(collision.centFT0C())), getCompressedOccupancy(collision.trackOccupancyInTimeRange()), getCompressedOccupancy(collision.ft0cOccupancyInTimeRange()), eventIR.orbit, diff --git a/DPG/Tasks/AOTTrack/qaEfficiency.cxx b/DPG/Tasks/AOTTrack/qaEfficiency.cxx index 6a998053a8a..22d7b6b5497 100644 --- a/DPG/Tasks/AOTTrack/qaEfficiency.cxx +++ b/DPG/Tasks/AOTTrack/qaEfficiency.cxx @@ -1359,7 +1359,7 @@ struct QaEfficiency { const char* partName = particleName(pdgSign, id); LOG(debug) << "Filling efficiency for particle " << static_cast(id) << " " << partName; - THashList* subList = static_cast(listEfficiencyMC->FindObject(partName)); + THashList* subList = dynamic_cast(listEfficiencyMC->FindObject(partName)); if (!subList) { LOG(warning) << "Cannot find list of efficiency objects for particle " << partName; return; @@ -1367,7 +1367,7 @@ struct QaEfficiency { // Filling 1D efficiencies auto doFillEfficiency = [&](const TString& effname, const auto& num, const auto& den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; @@ -1437,7 +1437,7 @@ struct QaEfficiency { // Filling 2D efficiencies auto fillEfficiency2D = [&](const TString& effname, const auto& num, const auto& den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx index dc1f2bed237..a77a17c11d1 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx @@ -336,8 +336,8 @@ struct qaEventTrackLite { Filter itsChi2 = (bTpcOnly.node() == true) || (chi2ItsMin < o2::aod::track::itsChi2NCl && o2::aod::track::itsChi2NCl < chi2ItsMax); // TPC Filter tpcChi2s = (bItsStandalone.node() == true) || (chi2TpcMin < o2::aod::track::tpcChi2NCl && o2::aod::track::tpcChi2NCl < chi2TpcMax); - Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > (int16_t)nClusterTpcMin); - Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > (int16_t)nCrossedRowsTpcMin); + Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > static_cast(nClusterTpcMin)); + Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > static_cast(nCrossedRowsTpcMin)); Filter tpcNcrossedRowsOverFindableClusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcCrossedRowsOverFindableCls > nCrossedRowsTpcOverFindableClustersTpcMin); // TOF Filter tofChi = o2::aod::track::tofChi2 > chi2TofMin; diff --git a/DPG/Tasks/AOTTrack/qaMatchEff.cxx b/DPG/Tasks/AOTTrack/qaMatchEff.cxx index d34c897ac55..09090795713 100644 --- a/DPG/Tasks/AOTTrack/qaMatchEff.cxx +++ b/DPG/Tasks/AOTTrack/qaMatchEff.cxx @@ -1565,7 +1565,7 @@ struct qaMatchEff { // // here n of clusters of TPC assigned to float for histos (and to hack it if needed) :) // - Float_t clustpc = (Float_t)track.tpcNClsFound(); + Float_t clustpc = static_cast(track.tpcNClsFound()); // Float_t findcltpc = (Float_t)track.tpcNClsFindable(); // Float_t crowstpc = (Float_t)track.tpcNClsCrossedRows(); // Float_t finclusmincrotpc = (Float_t)track.tpcNClsFindableMinusCrossedRows(); diff --git a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx index 5daa92f6b0a..97ba21592f9 100644 --- a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx +++ b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx @@ -99,8 +99,8 @@ struct qaTrackSplitting { using TrackCandidates = o2::soa::Join; Filter trackFilterEta = nabs(aod::track::eta - cfgCustomTrackCuts.windowEta) < cfgCustomTrackCuts.windowEtaWidth; Filter trackFilterPhi = nabs(aod::track::phi - cfgCustomTrackCuts.windowPhi) < cfgCustomTrackCuts.windowPhiWidth; - Filter trackFilterITS = (aod::track::itsClusterSizes > (uint32_t)0); - Filter trackFilterTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Filter trackFilterITS = (aod::track::itsClusterSizes > static_cast(0)); + Filter trackFilterTPC = (aod::track::tpcNClsFindable > static_cast(0)); // Filter trackFilterType = (aod::track::TrackType == aod::track::Track); // Filter filterPt = nabs(aod::track::pt - cfgCustomTrackCuts.windowPt) < cfgCustomTrackCuts.windowPtWidth; void processData(CollisionCandidates const& collisions, diff --git a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx index fc38e305938..f17df748019 100644 --- a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx +++ b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx @@ -56,7 +56,7 @@ struct mcSimpleQc { McParticles const&) { // checks on collisions - registry.fill(HIST("histRecoOverGenCollisions"), float(collisions.size()) / mcCollisions.size()); + registry.fill(HIST("histRecoOverGenCollisions"), static_cast(collisions.size()) / mcCollisions.size()); for (const auto& collision : collisions) { if (collision.has_mcCollision()) { @@ -79,7 +79,7 @@ struct mcSimpleQc { } } registry.fill(HIST("histNumGlobalTracks"), nTracks); - registry.fill(HIST("histFracAmbiguousTracks"), float(nAmbTracks) / nTracks); + registry.fill(HIST("histFracAmbiguousTracks"), static_cast(nAmbTracks) / nTracks); } } }; diff --git a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx index 7d9ae11ea1b..1d44db1e78d 100644 --- a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx +++ b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx @@ -87,7 +87,7 @@ struct TreeWriterTPCTOF { const double p = track.tpcInnerParam(); const int multTPC = collision.multTPC(); - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < dwnSmplFactor) { rowTPCTOFTree(track.tpcSignal(), p, From 96ffdba1c51d87d426a11433237e0da04514507f Mon Sep 17 00:00:00 2001 From: Joshua Koenig Date: Wed, 4 Nov 2026 13:03:43 +0100 Subject: [PATCH 559/648] =?UTF-8?q?[PWGEM]=20Add=20configurable=20for=20mi?= =?UTF-8?q?nimum=20Minv=20a=20photon=20pair=20has=20to=20have=20i=E2=80=A6?= =?UTF-8?q?=20(#18581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jokonig --- .../TableProducer/photonconversionbuilder.h | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index 70ed1809bb0..c74bc6fcbc4 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -283,7 +283,8 @@ struct PhotonConversionBuilder { struct : ConfigurableGroup { std::string prefix = "skim_group"; Configurable minNv0PerCollision{"minNv0PerCollision", -1, "store V0 photons only for collisions with at least this many surviving candidates (event-level skim for derived data). negative = store all"}; - Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv below this (GeV/c^2). negative = disabled"}; + Configurable minMinvPairPerCollision{"minMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) above this (GeV/c^2). negative = disabled"}; + Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) below this (GeV/c^2). negative = disabled"}; } skim; // single track cuts @@ -420,8 +421,8 @@ struct PhotonConversionBuilder { } LOGF(info, "photon-conversion-builder: deduplicationMode = %d (%s), score weight = %.2f", dedup.deduplicationMode.value, DedupNames[dedup.deduplicationMode.value], dedup.deduplicationScoreWeight.value); - LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, maxMinvPairPerCollision = %f", - skim.minNv0PerCollision.value, skim.maxMinvPairPerCollision.value); + LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, minMinvPairPerCollision = %f, maxMinvPairPerCollision = %f", + skim.minNv0PerCollision.value, skim.minMinvPairPerCollision.value, skim.maxMinvPairPerCollision.value); ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -1991,7 +1992,7 @@ struct PhotonConversionBuilder { } std::set skimRejected; - if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f) { + if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); std::unordered_map> candsPerColl; for (const auto& c : vecV0Dedup) { @@ -2004,19 +2005,22 @@ struct PhotonConversionBuilder { skimRejected.insert(colId); continue; } - if (skim.maxMinvPairPerCollision > 0.f) { - bool hasLowMPair = false; - for (size_t i = 0; i < cands.size() && !hasLowMPair; ++i) { + if (skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) + bool hasPairOfInterest = false; + const float minMinv = skim.minMinvPairPerCollision > 0.f ? skim.minMinvPairPerCollision.value : 0.f; + const float maxMinv = skim.maxMinvPairPerCollision > 0.f ? skim.maxMinvPairPerCollision.value : std::numeric_limits::max(); + for (size_t i = 0; i < cands.size() && !hasPairOfInterest; ++i) { for (size_t j = i + 1; j < cands.size(); ++j) { ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); - if ((v1 + v2).M() < skim.maxMinvPairPerCollision) { - hasLowMPair = true; + const double massPair = (v1 + v2).M(); + if (massPair > minMinv && massPair < maxMinv) { + hasPairOfInterest = true; break; } } } - if (!hasLowMPair) { + if (!hasPairOfInterest) { skimRejected.insert(colId); } } From 13ae3dae65789731ee56d22605c216c7fa4151e4 Mon Sep 17 00:00:00 2001 From: AlexianL <123153896+AlexianL@users.noreply.github.com> Date: Wed, 4 Nov 2026 14:06:36 +0100 Subject: [PATCH 560/648] [PWGHF] taskFlow.cxx : implement a true MC generated centrality estimator, add process functions dedicated to MC reconstructed (#18570) --- PWGHF/HFC/Tasks/taskFlow.cxx | 700 +++++++++++++++++++++-------------- 1 file changed, 415 insertions(+), 285 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 11251c85f98..24a375c075a 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -22,12 +22,14 @@ #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGMM/Mult/DataModel/bestCollisionTable.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" @@ -46,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -148,6 +149,11 @@ enum MftTrackAmbiguityStep { NMftAmbiguitySteps }; +enum MftTrackTemplate { + FilteredMftTracks = 0, + FilteredMftTracksWCollsMcLabels +}; + enum MftTrackSelectionStep { NoSelection = 0, Eta, @@ -203,18 +209,6 @@ static constexpr std::array WhatCentralityEstimator = {"cen static constexpr std::array WhatMultiplicityEstimator = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; auto static constexpr MinFt0cCell = 96; -static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = - TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | - TrackSelectionFlags::kITSHits; -static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = - TrackSelectionFlags::kTPCNCls | - TrackSelectionFlags::kTPCCrossedRowsOverNCls | - TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = - TrackSelectionFlags::kDCAxy; - static constexpr std::array, 1> PairCutDefaults{{{{-1, -1, -1, -1, -1}}}}; struct HfTaskFlow { @@ -398,11 +392,15 @@ struct HfTaskFlow { using SmallGroupMcCollisions = soa::SmallGroups>; // using FilteredMcCollisionsWMult = soa::Filtered>; - using FilteredMcCollisionsWMult = soa::Join; - using FilteredMcCollisionsWMultWCollsExtra = soa::Join; + using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMultWCollsExtra = soa::Filtered>; // using FilteredMcParticles = soa::Filtered; using FilteredMcParticles = aod::McParticles; + // MC reconstructed + + // using FilteredMftTracksWCollsMcLabels = soa::Filtered>; + // ========================= // Filters & partitions : DATA // ========================= @@ -424,11 +422,7 @@ struct HfTaskFlow { (aod::track::pt < configCentral.ptCentralTrackMax) && requireGlobalTrackInFilter(); - Filter centralTrackItsTpcMatchingFilter = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true) && - ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && - ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); - - Filter centralTrackDcaFilter = (ifnode(configCentral.dcaZCentralTrackMax.node() > 0.f, nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), ncheckbit(aod::track::trackCutFlag, TrackSelectionDca))); + Filter centralTrackDcaFilter = (nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax); Filter centralTrackChi2TpcClusterFilter = (aod::track::tpcChi2NCl < configCentral.maxChi2TpcClusters); @@ -447,8 +441,8 @@ struct HfTaskFlow { // // Filter for McParticles // Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); - // // Filter for MCcollisions - // Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; + // Filter for MCcollisions + Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; // ========================= // Preslice : DATA @@ -745,7 +739,8 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for TpcMft cases // ========================= - if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous) { + // if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { + if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { if (!configTask.removeQAForSystematics) { addHistograms(); @@ -768,7 +763,8 @@ struct HfTaskFlow { } } - if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { + // if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { + if (doprocessSameTpcMftD0Ch) { if (!configTask.removeQAForSystematics) { addHistograms(); addMftHistograms(); @@ -778,7 +774,8 @@ struct HfTaskFlow { mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } - if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { + // if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { + if (doprocessSameTpcMftLcCh) { if (!configTask.removeQAForSystematics) { addHistograms(); @@ -890,7 +887,8 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for MftFt0a cases // ========================= - if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { + // if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { + if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous || doprocessSameMftFt0aChChReassociated3dMcReconstructed) { if (!configTask.removeQAForSystematics) { addHistograms(); @@ -997,25 +995,39 @@ struct HfTaskFlow { if (doprocessSameMcGen) { - if (!configTask.removeQAForSystematics) { - - if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { - addHistograms(); - } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { - addHistograms(); - } + registry.add(Form("MC/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMultiplicity_multMCFT0C", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + + if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { + addHistograms(); } if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { @@ -1114,23 +1126,23 @@ struct HfTaskFlow { } } - template + template float getCentralityEstimator(TCollision const& collision, bool isSameEvent) { switch (configCollision.centralityEstimator) { case CentralityEstimators::CentFT0C: if (isSameEvent && !configTask.removeQAForSystematics) { - registry.fill(HIST("Data/hCentrality_centFT0C"), collision.centFT0C()); + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0C"), collision.centFT0C()); } return collision.centFT0C(); case CentralityEstimators::CentFT0CVariant1: if (isSameEvent && !configTask.removeQAForSystematics) { - registry.fill(HIST("Data/hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); } return collision.centFT0CVariant1(); case CentralityEstimators::CentFT0M: if (isSameEvent && !configTask.removeQAForSystematics) { - registry.fill(HIST("Data/hCentrality_centFT0M"), collision.centFT0M()); + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0M"), collision.centFT0M()); } return collision.centFT0M(); default: @@ -1318,10 +1330,9 @@ struct HfTaskFlow { registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); } return FT0InnerOrOuter::FT0OuterRing; - } else { - LOGF(fatal, "Channel ID %d out of range for FT0C", channelId); - return FT0InnerOrOuter::FT0Error; } + LOGF(fatal, "Channel ID %d out of range for FT0C", channelId); + return FT0InnerOrOuter::FT0Error; } else if (fitType == isFT0A) { if (channelId >= FT0DetectorChannels::FT0AInnerRingMin && channelId <= FT0DetectorChannels::FT0AInnerRingMax) { if (!configTask.removeQAForSystematics) { @@ -1333,10 +1344,9 @@ struct HfTaskFlow { registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); } return FT0InnerOrOuter::FT0OuterRing; - } else { - LOGF(fatal, "Channel ID %d out of range for FT0A", channelId); - return FT0InnerOrOuter::FT0Error; } + LOGF(fatal, "Channel ID %d out of range for FT0A", channelId); + return FT0InnerOrOuter::FT0Error; } else { LOGF(fatal, "Fit type %d is not valid for FT0", fitType); return FT0InnerOrOuter::FT0Error; @@ -1489,7 +1499,7 @@ struct HfTaskFlow { multiplicity = getMultiplicityEstimator(collision, true); } - centrality = getCentralityEstimator(collision, true); + centrality = getCentralityEstimator(collision, true); if (configCollision.useCentrality) { getCentralityWeight(centralityWeight, centrality); if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { @@ -2006,7 +2016,7 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, float centralityWeight) @@ -2082,10 +2092,20 @@ struct HfTaskFlow { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); } - auto reassociatedMftTrack = track2.template mfttrack_as(); + auto reassociatedMftTrack = track2.template mfttrack_as(); + auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; + if constexpr (std::is_same_v>, TTracksAssoc>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + if constexpr (std::is_same_v, TTracksAssoc>) { reassociatedMftTrackDcaXY = track2.bestDCAXY(); } @@ -2472,7 +2492,7 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsFITReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TFits const&, float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, int fitType, float centralityWeight) @@ -2486,10 +2506,19 @@ struct HfTaskFlow { for (auto const& track1 : tracksTrigger) { loopCounter++; - auto reassociatedMftTrack = track1.template mfttrack_as(); + auto reassociatedMftTrack = track1.template mfttrack_as(); auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; + if constexpr (std::is_same_v>, TTracksAssoc>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + if constexpr (std::is_same_v, TTracksTrig>) { reassociatedMftTrackDcaXY = track1.bestDCAXY(); } @@ -2831,9 +2860,8 @@ struct HfTaskFlow { // auto mult = 0.f; if (configCollision.useMultiplicityFromTracks) { return associatedTracks.size(); - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; // The first one that I call "Data" should work for data and mc rec @@ -2863,8 +2891,8 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { continue; } } @@ -2893,9 +2921,8 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; @@ -2924,8 +2951,8 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { continue; } } @@ -2942,7 +2969,7 @@ struct HfTaskFlow { } } - template + template void mixCollisionsReassociatedMftTracks(TCollisions const& collisions, CorrelationContainer::CFStep step, TTracksTpc const& tracksTpc, TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TPresliceTrigger const& presliceTrigger, TPresliceAssociated const& presliceAssociated, OutputObj& corrContainer, bool cutAmbiguousTracks, aod::BCsWithTimestamps const&) @@ -2957,9 +2984,8 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; @@ -2986,8 +3012,8 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { continue; } } @@ -3002,13 +3028,13 @@ struct HfTaskFlow { auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); corrContainer->fillEvent(multiplicity, step); - fillCorrelationsReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, centralityWeight); + fillCorrelationsReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, centralityWeight); } else if ((collision1.has_foundFT0() && collision2.has_foundFT0())) { // if MFT-FT0A cases auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); const auto& ft0 = collision2.foundFT0(); corrContainer->fillEvent(multiplicity, step); - fillCorrelationsFITReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, isFT0A, centralityWeight); } } // end of for loop } @@ -3028,9 +3054,8 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; @@ -3088,8 +3113,8 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { continue; } } @@ -3120,9 +3145,8 @@ struct HfTaskFlow { auto mult = associatedTracks.size(); if (configCollision.useMultiplicityFromTracks) { return mult; - } else { - return getMultiplicityEstimator(collision, false); } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; @@ -3155,8 +3179,8 @@ struct HfTaskFlow { float centralityWeight = 1.0f; if (configCollision.useCentrality) { - getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, true)); - if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { continue; } } @@ -3327,41 +3351,41 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChCh, "DATA : Process same-event correlations for TPC-MFT h-h case", false); - void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& mftTracks, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } + // void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& mftTracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } - if (!configTask.removeQAForSystematics) { - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); - } + // if (!configTask.removeQAForSystematics) { + // registry.fill(HIST("Data/hNTracks"), tracks.size()); + // registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + // registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - float multiplicity = 0.f; - float centrality = -1.f; - float centralityWeight = 1.f; + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { - return; - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } else { - sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } else { + // sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); void processSameTpcMftChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, @@ -3401,10 +3425,10 @@ struct HfTaskFlow { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); } else { sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3d, "DATA : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); @@ -3437,10 +3461,10 @@ struct HfTaskFlow { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); } else { sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChNonAmbiguous, "DATA : Process same-event correlations for TPC-MFT h-h case with non-ambiguous tracks", false); @@ -3481,31 +3505,31 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0Ch, "DATA : Process same-event correlations for TPC-MFT D0-h case", false); - void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelD0 const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const&, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } + // void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelD0 const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - float multiplicity = 0.f; - float centrality = -1.f; - float centralityWeight = 1.f; + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { - return; - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-MFT HF-h case for Lc @@ -3543,31 +3567,31 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcCh, "DATA : Process same-event correlations for TPC-MFT Lc-h case", false); - void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelLc const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const&, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } + // void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelLc const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); - float multiplicity = 0.f; - float centrality = -1.f; - float centralityWeight = 1.f; + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { - return; - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-FV0A Ch. Part. - Ch. Part @@ -3693,7 +3717,7 @@ struct HfTaskFlow { // } // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); // } // } // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated, "DATA : Process same-event correlations for MFT-FV0a h-h case reassociated", false); @@ -3717,7 +3741,7 @@ struct HfTaskFlow { } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); } } PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated3d, "DATA : Process same-event correlations for MFT-FV0a h-h case 3d reassociated", false); @@ -3741,7 +3765,7 @@ struct HfTaskFlow { // } // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); // } // } // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChNonAmbiguous, "DATA : Process same-event correlations for MFT-FV0a h-h non-ambiguous case", false); @@ -3894,42 +3918,42 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChCh, "DATA : Process same-event correlations for MFT-FT0-A h-h case", false); - void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FT0s const& ft0as, - FilteredTracksWDcaSel const& tracks, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } + // void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredMftTracks const&, + // aod::FT0s const& ft0as, + // FilteredTracksWDcaSel const& tracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } - auto bc = collision.template bc_as(); - loadEfficiencyCorrection(bc.timestamp()); + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); - if (collision.has_foundFT0()) { + // if (collision.has_foundFT0()) { - loadGain(bc); - const auto& ft0 = collision.foundFT0(); - float multiplicity = 0.f; - float centrality = -1.f; - float centralityWeight = 1.f; + // loadGain(bc); + // const auto& ft0 = collision.foundFT0(); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; - if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { - return; - } + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); - } else { - sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); - } - } - } - PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } else { + // sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); void processSameMftFt0aChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, @@ -3959,10 +3983,10 @@ struct HfTaskFlow { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); } else { sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); } } } @@ -3995,10 +4019,10 @@ struct HfTaskFlow { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); } else { sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); } } } @@ -4145,6 +4169,93 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== + void processSameTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& mftTracks, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + auto uncorrectedMultiplicity = multiplicity; + + if (configCollision.useMultiplicityFromTracksCorrected) { + multiplicity = getCorrectedMultiplicity(tracks); + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } + } + PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3dMcReconstructed, "Mc Rec : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); + + void processSameMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::FT0s const& ft0as, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + + if (collision.has_foundFT0()) { + + loadGain(bc); + const auto& ft0 = collision.foundFT0(); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } + } + } + PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process same-event correlations for MFT-FT0-A h-h case reassociated 3d", false); + void processSameMcGen(FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) @@ -4153,7 +4264,6 @@ struct HfTaskFlow { if (mcParticles.size() == 0) { // guard against empty filtered batch return; } - if (collisions.size() == 0) { return; } @@ -4165,21 +4275,14 @@ struct HfTaskFlow { multiplicity += 1; } } + registry.fill(HIST("MC/hMultiplicityTracksSize"), multiplicity); } else { - multiplicity = mcCollision.multMCPVz(); + multiplicity = mcCollision.multMCFT0C(); + registry.fill(HIST("MC/hMultiplicity_multMCFT0C"), multiplicity); } - float centrality = -1.f; - for (const auto& collision : collisions) { - // if (!collision.sel8()) { - // continue; - // } - // if (collision.globalIndex() != mcCollision.bestCollisionIndex()) { - // continue; - // } - centrality = getCentralityEstimator(collision, false); - // break; - } + float centrality = 999.f; + centrality = getCentralityEstimator(mcCollision, true); if (configCollision.useCentrality) { if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { @@ -4192,6 +4295,9 @@ struct HfTaskFlow { } } + registry.fill(HIST("MC/hCentralityUsed"), centrality); + registry.fill(HIST("MC/hMultiplicityUsed"), multiplicity); + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); fillCorrelationsMonteCarlo(sameEvent, CorrelationContainer::CFStep::kCFStepAll, mcParticles, mcParticles, multiplicity, mcCollision.posZ(), true); } @@ -4275,19 +4381,19 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChBis, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); - void processMixedTpcMftChChReassociated2d(FilteredCollisionsWSelMult const& collisions, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& /*mftTracks*/, - soa::SmallGroups const& reassociated2dMftTracks, - aod::BCsWithTimestamps const& bcs) - { - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, false, bcs); - } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, false, bcs); - } - } - PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated2d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + // void processMixedTpcMftChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& /*mftTracks*/, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated2d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); void processMixedTpcMftChChReassociated3d(FilteredCollisionsWSelMult const& collisions, FilteredTracksWDcaSel const& tracks, @@ -4296,9 +4402,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); @@ -4310,9 +4416,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, true, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, true, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChNonAmbiguous, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); @@ -4455,20 +4561,20 @@ struct HfTaskFlow { } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChCh, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); - void processMixedMftFt0aChChReassociated2d(FilteredCollisionsWSelMult const& collisions, - FilteredMftTracks const& /*mftTracks*/, - FilteredTracksWDcaSel const& tracksTpc, - soa::SmallGroups const& reassociated2dMftTracks, - aod::FT0s const& ft0s, - aod::BCsWithTimestamps const& bcs) - { - if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, false, bcs); - } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, false, bcs); - } - } - PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated2d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + // void processMixedMftFt0aChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredMftTracks const& /*mftTracks*/, + // FilteredTracksWDcaSel const& tracksTpc, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::FT0s const& ft0s, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated2d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); void processMixedMftFt0aChChReassociated3d(FilteredCollisionsWSelMult const& collisions, FilteredMftTracks const& /*mftTracks*/, @@ -4478,9 +4584,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); @@ -4493,9 +4599,9 @@ struct HfTaskFlow { aod::BCsWithTimestamps const& bcs) { if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, true, bcs); } else { - mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, true, bcs); + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, true, bcs); } } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChNonAmbiguous, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); @@ -4558,14 +4664,43 @@ struct HfTaskFlow { // MONTE-CARLO // =================================================================================================================================================================================================================================================================== + void processMixedTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for TPC-MFT h-h case", false); + + void processMixedMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + FilteredTracksWDcaSel const& tracksTpc, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for MFT-FT0-A h-h case", false); + void processMixedMcGen(FilteredMcCollisionsWMultWCollsExtra const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) { - auto getTracksSize = [&mcParticles, this](soa::Join::iterator const& mcCollision) { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision) { auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); - float multiplicity = 0.f; + auto multiplicity = 0; if (configCollision.useMultiplicityFromTracks) { for (const auto& track : associatedTracks) { if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { @@ -4573,7 +4708,7 @@ struct HfTaskFlow { } } } else { - multiplicity = mcCollision.multMCPVz(); + multiplicity = mcCollision.multMCFT0C(); } return multiplicity; }; @@ -4588,10 +4723,6 @@ struct HfTaskFlow { for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (tracks1.size() == 0 || tracks2.size() == 0) { // guard against empty filtered batch - continue; - } - auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); if (groupedCollisions1.size() == 0) { @@ -4605,40 +4736,33 @@ struct HfTaskFlow { float multiplicityCollision2 = 0.f; if (configCollision.useMultiplicityFromTracks) { - for (const auto& track : tracks1) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - multiplicityCollision1 += 1; - } - } - for (const auto& track : tracks2) { - if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { - multiplicityCollision2 += 1; - } - } + // for (const auto& track : tracks1) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision1 += 1; + // } + // } + // for (const auto& track : tracks2) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision2 += 1; + // } + // } + multiplicityCollision1 = getTracksSize(collision1); + multiplicityCollision2 = getTracksSize(collision2); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll2"), multiplicityCollision2); } else { - multiplicityCollision1 = collision1.multMCPVz(); - multiplicityCollision2 = collision2.multMCPVz(); + multiplicityCollision1 = collision1.multMCFT0C(); + multiplicityCollision2 = collision2.multMCFT0C(); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll2"), multiplicityCollision2); } float centralityCollision1 = 999.f; float centralityCollision2 = 999.f; - for (const auto& reconstructedCollision1 : groupedCollisions1) { - // if (!collision1.sel8()) { - // continue; - // } - // if (reconstructedCollision1.globalIndex() != collision1.bestCollisionIndex()) { - // continue; - // } - centralityCollision1 = getCentralityEstimator(reconstructedCollision1, false); - // break; - } - for (const auto& reconstructedCollision2 : groupedCollisions2) { - // if (reconstructedCollision2.globalIndex() != collision2.bestCollisionIndex()) { - // continue; - // } - centralityCollision2 = getCentralityEstimator(reconstructedCollision2, false); - // break; - } + centralityCollision1 = getCentralityEstimator(collision1, false); + centralityCollision2 = getCentralityEstimator(collision2, false); + registry.fill(HIST("MC/hMixedCentrality_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentrality_coll2"), centralityCollision2); if (configCollision.useCentrality) { if (centralityCollision1 < configCollision.minCentrality || centralityCollision1 >= configCollision.maxCentrality) { @@ -4649,6 +4773,9 @@ struct HfTaskFlow { } } + registry.fill(HIST("MC/hMixedCentralityUsed_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentralityUsed_coll2"), centralityCollision2); + if (!configCollision.useCentrality) { if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { continue; @@ -4658,6 +4785,9 @@ struct HfTaskFlow { } } + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll2"), multiplicityCollision2); + mixedEvent->fillEvent(multiplicityCollision1, CorrelationContainer::kCFStepAll); fillCorrelationsMonteCarlo(mixedEvent, CorrelationContainer::CFStep::kCFStepAll, tracks1, tracks2, multiplicityCollision1, collision1.posZ(), false); } @@ -4713,7 +4843,7 @@ struct HfTaskFlow { if (configMft.useMftPhiCut) { if (particle.phi() < configMft.phiMftTrackMax && particle.phi() > configMft.phiMftTrackMin && - !(particle.phi() > configMft.phiMftTrackMiddleMin && particle.phi() < configMft.phiMftTrackMiddleMax)) { + (particle.phi() <= configMft.phiMftTrackMiddleMin || particle.phi() >= configMft.phiMftTrackMiddleMax)) { if (hasReconstructedCollision) { registry.fill(HIST("MC/hEfficiencyAssociated"), particle.pt(), particle.eta(), mcCollision.posZ()); } From 5c9b8d07c27eca05689650ecfb56bb7c662c9b19 Mon Sep 17 00:00:00 2001 From: Sigurd Nese <32108009+sigurdnese@users.noreply.github.com> Date: Wed, 4 Nov 2026 15:46:50 +0100 Subject: [PATCH 561/648] [PWGDQ] Add impact parameter variable (d_0) to pair vertexing (#18589) --- PWGDQ/Core/VarManager.cxx | 3 +++ PWGDQ/Core/VarManager.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index 445dabf000c..b85e284dd40 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -1365,6 +1365,8 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCVertexingTauxyzProjected] = "ns"; fgVariableNames[kCosPointingAngle] = "cos(#theta_{pointing})"; fgVariableUnits[kCosPointingAngle] = ""; + fgVariableNames[kVertexingImpactParameter] = "d_{0}"; + fgVariableUnits[kVertexingImpactParameter] = "cm"; fgVariableNames[kMCCosPointingAngle] = "MC cos(#theta_{pointing})"; fgVariableUnits[kMCCosPointingAngle] = ""; fgVariableNames[kD0DcaDaughters] = "DCA of D0 daughters"; @@ -2687,6 +2689,7 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kQuadDCAsigXYZ"] = kQuadDCAsigXYZ; fgVarNamesMap["kSignQuadDCAsigXY"] = kSignQuadDCAsigXY; fgVarNamesMap["kCosPointingAngle"] = kCosPointingAngle; + fgVarNamesMap["kVertexingImpactParameter"] = kVertexingImpactParameter; fgVarNamesMap["kMCCosPointingAngle"] = kMCCosPointingAngle; fgVarNamesMap["kD0ImpParProduct"] = kD0ImpParProduct; fgVarNamesMap["kD0DcaDaughters"] = kD0DcaDaughters; diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 7a395a9dcfb..a3b9d9d367b 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -892,6 +892,7 @@ class VarManager : public TObject kQuadDCAsigXYZ, kSignQuadDCAsigXY, kCosPointingAngle, + kVertexingImpactParameter, kD0ImpParProduct, kD0DcaDaughters, kImpParXYJpsi, @@ -5318,6 +5319,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kCosPointingAngle] = (dx * v12.Px() + dy * v12.Py() + dz * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); + values[kVertexingImpactParameter] = values[VarManager::kVertexingLxyz] * std::sqrt(1 - values[VarManager::kCosPointingAngle] * values[VarManager::kCosPointingAngle]); // Decay length defined as in Run 2 values[kVertexingLzProjected] = (dz * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); values[kVertexingLxyProjected] = (dx * v12.Px()) + (dy * v12.Py()); @@ -5398,6 +5400,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, std::sqrt(dyPair2PV * dyPair2PV) * v12.Py() + std::sqrt(dzPair2PV * dzPair2PV) * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); + values[kVertexingImpactParameter] = values[VarManager::kVertexingLxyz] * std::sqrt(1 - values[VarManager::kCosPointingAngle] * values[VarManager::kCosPointingAngle]); } // As defined in Run 2 (projected onto momentum) if (fgUsedVars[kVertexingLxyProjected] || fgUsedVars[kVertexingLxyzProjected] || fgUsedVars[kVertexingLzProjected]) { From 591ffa8239593159cb8fb17e73d97750cd7f3048 Mon Sep 17 00:00:00 2001 From: fuchuncui <162277233+fuchuncui@users.noreply.github.com> Date: Wed, 4 Nov 2026 23:31:06 +0800 Subject: [PATCH 562/648] [PWGCF] Add EvSel and QA histos in cascDiHadronCorr.cxx- #18580 (#18582) --- .../Tasks/cascDiHadronCorr.cxx | 202 +++++++++++++++++- 1 file changed, 198 insertions(+), 4 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx index 79f8efb4720..d97d425e6b3 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx @@ -83,7 +83,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 0.0f, "Minimum centrality for collision") O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 100.0f, "Maximum centrality for collision") O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 1, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, double, 0.8, "Low radius for merging cut") O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") @@ -104,6 +104,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgNSigmapid, std::vector, (std::vector{3, 3, 3}), "tpc NSigma for Pion Proton Kaon") O2_DEFINE_CONFIGURABLE(cfgOutputXi, bool, true, "Output Xi-charged correlation") O2_DEFINE_CONFIGURABLE(cfgOutputOmega, bool, false, "Output Omega-charged correlation") + O2_DEFINE_CONFIGURABLE(cfgOutputCascQA, bool, true, "Output QA plots for cascades and daughters") struct : ConfigurableGroup { std::string prefix = "cfgEvSel"; O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") @@ -118,6 +119,8 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelHaveXi, bool, false, "reject the collisions without any Xi reconstracted") + O2_DEFINE_CONFIGURABLE(cfgEvSelHaveOmega, bool, false, "reject the collisions without any Xi reconstracted") } cfgEvSel; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -174,6 +177,7 @@ struct CascDiHadronCorr { O2_DEFINE_CONFIGURABLE(cfgMinITSNCls, int, -1, "check minimum number of ITS clusters") O2_DEFINE_CONFIGURABLE(cfgTPCNCls, int, 0, "check minimum number of TPC hits") O2_DEFINE_CONFIGURABLE(cfgTPCCrossedRows, int, 0, "check minimum number of TPC crossed rows") + O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5, "max chi2 per TPC clusters for cascades daughters") } trkQualityOpts; SliceCache cache; @@ -404,6 +408,56 @@ struct CascDiHadronCorr { registry.add("MCRecPri", "MCRecPri", {HistType::kTH1D, {axisPtEfficiency}}); } + if (cfgOutputCascQA) { + registry.add("QAhisto/hqaTPCNClsbefore", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaTPCCrossedRowsbefore", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaChi2prTPCclsbefore", "", {HistType::kTH1D, {{100, 0, 10}}}); + registry.add("QAhisto/hqaTPCNClsafter", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaTPCCrossedRowsafter", "", {HistType::kTH1D, {{100, 0, 200}}}); + registry.add("QAhisto/hqaChi2prTPCclsafter", "", {HistType::kTH1D, {{100, 0, 10}}}); + + registry.add("QAhisto/Xi/hqaCascRadiusbefore", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Xi/hqaCascRadiusafter", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Xi/hqaCasccosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCasccosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCascV0cosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqaCascV0cosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0toPVbefore", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascV0toPVafter", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascBachtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascBachtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLaprtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLaprtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLapitoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascLapitoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Xi/hqadcaCascdaubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascdauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0daubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqadcaCascV0dauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Xi/hqaLambdaMassbefore", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + registry.add("QAhisto/Xi/hqaLambdaMassafter", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + + registry.add("QAhisto/Omega/hqaCascRadiusbefore", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Omega/hqaCascRadiusafter", "", {HistType::kTH1D, {{1000, 0, 100}}}); + registry.add("QAhisto/Omega/hqaCasccosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCasccosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCascV0cosPAbefore", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqaCascV0cosPAafter", "", {HistType::kTH1D, {{1000, 0.95, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0toPVbefore", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascV0toPVafter", "", {HistType::kTH1D, {{1000, 0, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascBachtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascBachtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLaprtoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLaprtoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLapitoPVbefore", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascLapitoPVafter", "", {HistType::kTH1D, {{1000, -10, 10}}}); + registry.add("QAhisto/Omega/hqadcaCascdaubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascdauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0daubefore", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqadcaCascV0dauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); + registry.add("QAhisto/Omega/hqaLambdaMassbefore", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + registry.add("QAhisto/Omega/hqaLambdaMassafter", "", {HistType::kTH1D, {{160, 1.08, 1.16}}}); + } LOGF(info, "Initializing correlation container"); std::vector corrAxis = {{axisSample, "Sample"}, {axisVertex, "z-vtx (cm)"}, @@ -512,6 +566,18 @@ struct CascDiHadronCorr { if (std::fabs(negdau.eta()) > trkQualityOpts.cfgCutEta.value) return false; + if (cfgOutputCascQA) { + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), bachelor.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), bachelor.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), bachelor.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), posdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), posdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), posdau.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsbefore"), negdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsbefore"), negdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsbefore"), negdau.tpcChi2NCl()); + } + // track quality check if (bachelor.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) return false; @@ -538,8 +604,63 @@ struct CascDiHadronCorr { return false; if (negdau.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) return false; + if (bachelor.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; + if (posdau.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; + if (negdau.tpcChi2NCl() >= trkQualityOpts.cfgCutChi2prTPCcls.value) + return false; - if (cfgOutputXi) { + if (cfgOutputCascQA) { + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), bachelor.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), bachelor.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), bachelor.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), posdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), posdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), posdau.tpcChi2NCl()); + registry.fill(HIST("QAhisto/hqaTPCNClsafter"), negdau.tpcNClsFound()); + registry.fill(HIST("QAhisto/hqaTPCCrossedRowsafter"), negdau.tpcNClsCrossedRows()); + registry.fill(HIST("QAhisto/hqaChi2prTPCclsafter"), negdau.tpcChi2NCl()); + } + + if (cfgOutputCascQA) { + if (cfgOutputXi) { + registry.fill(HIST("QAhisto/Xi/hqaCascRadiusbefore"), casc.cascradius()); + registry.fill(HIST("QAhisto/Xi/hqaCasccosPAbefore"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqaCascV0cosPAbefore"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0toPVbefore"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascBachtoPVbefore"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascdaubefore"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0daubefore"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Xi/hqaLambdaMassbefore"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVbefore"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVbefore"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVbefore"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVbefore"), casc.dcapostopv()); + } + } + if (cfgOutputOmega) { + registry.fill(HIST("QAhisto/Omega/hqaCascRadiusbefore"), casc.cascradius()); + registry.fill(HIST("QAhisto/Omega/hqaCasccosPAbefore"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqaCascV0cosPAbefore"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0toPVbefore"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascBachtoPVbefore"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascdaubefore"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0daubefore"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Omega/hqaLambdaMassbefore"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVbefore"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVbefore"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVbefore"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVbefore"), casc.dcapostopv()); + } + } + } + + if (cfgOutputXi || cfgEvSel.cfgEvSelHaveXi.value) { if (casc.mXi() > massup || casc.mXi() < masslow) return false; if (casc.sign() < 0) { @@ -582,7 +703,7 @@ struct CascDiHadronCorr { if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) return false; } - if (cfgOutputOmega) { + if (cfgOutputOmega || cfgEvSel.cfgEvSelHaveOmega.value) { if (casc.mOmega() > massup || casc.mOmega() < masslow) return false; if (casc.sign() < 0) { @@ -625,6 +746,43 @@ struct CascDiHadronCorr { if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) return false; } + + if (cfgOutputCascQA) { + if (cfgOutputXi) { + registry.fill(HIST("QAhisto/Xi/hqaCascRadiusafter"), casc.cascradius()); + registry.fill(HIST("QAhisto/Xi/hqaCasccosPAafter"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqaCascV0cosPAafter"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0toPVafter"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Xi/hqadcaCascBachtoPVafter"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascdauafter"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascV0dauafter"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Xi/hqaLambdaMassafter"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVafter"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVafter"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Xi/hqadcaCascLaprtoPVafter"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Xi/hqadcaCascLapitoPVafter"), casc.dcapostopv()); + } + } + if (cfgOutputOmega) { + registry.fill(HIST("QAhisto/Omega/hqaCascRadiusafter"), casc.cascradius()); + registry.fill(HIST("QAhisto/Omega/hqaCasccosPAafter"), casc.casccosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqaCascV0cosPAafter"), casc.v0cosPA(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0toPVafter"), casc.dcav0topv(posX, posY, posZ)); + registry.fill(HIST("QAhisto/Omega/hqadcaCascBachtoPVafter"), casc.dcabachtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascdauafter"), casc.dcacascdaughters()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascV0dauafter"), casc.dcaV0daughters()); + registry.fill(HIST("QAhisto/Omega/hqaLambdaMassafter"), casc.mLambda()); + if (casc.sign() > 0) { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVafter"), casc.dcapostopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVafter"), casc.dcanegtopv()); + } else { + registry.fill(HIST("QAhisto/Omega/hqadcaCascLaprtoPVafter"), casc.dcanegtopv()); + registry.fill(HIST("QAhisto/Omega/hqadcaCascLapitoPVafter"), casc.dcapostopv()); + } + } + } return true; } @@ -1410,6 +1568,18 @@ struct CascDiHadronCorr { } if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, true)) return; + + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } loadCorrection(bc.timestamp()); if (!cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); @@ -1563,7 +1733,7 @@ struct CascDiHadronCorr { PROCESS_SWITCH(CascDiHadronCorr, processMixedCasc, "Process mixed events", true); // the process for filling the mixed events by buffer - void processMixedBuffer(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) + void processMixedBuffer(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::CascDatas const& cascades, DaughterTracks const&) { ValidCollision currentCollision; int nBinsMult = 0; @@ -1597,6 +1767,18 @@ struct CascDiHadronCorr { if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) return; + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } + float weightCent = 1.0f; if (!cfgCentTableUnavailable) getCentralityWeight(weightCent, cent); @@ -1650,6 +1832,18 @@ struct CascDiHadronCorr { if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) return; + if (cfgEvSel.cfgEvSelHaveXi.value || cfgEvSel.cfgEvSelHaveOmega.value) { + bool haveCasc = false; + for (const auto& cascade : cascades) { + if (cascSelected(cascade, collision.posX(), collision.posY(), collision.posZ())) { + haveCasc = true; + break; + } + } + if (!haveCasc) + return; + } + float weightCent = 1.0f; if (!cfgCentTableUnavailable) getCentralityWeight(weightCent, cent); From cf05461d89d82f77aefd156737d12e4920d0f8f0 Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Wed, 4 Nov 2026 18:44:37 +0200 Subject: [PATCH 563/648] [PWGCF] Add mean-pT profiles and extend spectra in Fox-Wolfram analysis (#18592) Co-authored-by: Iulian --- .../Tasks/FoxWolframCorrelation.cxx | 237 +++++++++--------- 1 file changed, 124 insertions(+), 113 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index ba8b5e6b790..96db156d9ab 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -220,6 +220,9 @@ struct FoxWolframCorrelation { }; using TriFlags = std::array; + ConfigurableAxis cfgAxisPt{"cfgAxisPt", {VARIABLE_WIDTH, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0}, "p_{T} Bins"}; + ConfigurableAxis cfgAxisMultip{"cfgAxisMultip", {351, -0.5, 350.5}, "Multiplicity Bins"}; + Configurable> isoFWMnominal{"isoFWMnominal", {0.02, 0.27, 0.02, 0.16, 0.02, 0.12, 0.02, 0.10}, "isoFWMnominal"}; // Multiplicity bins: bin i is [multLower[i], multUpper[i]] (inclusive limits). @@ -237,12 +240,11 @@ struct FoxWolframCorrelation { "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; // Map each unique pair of Fox-Wolfram moments to its histogram. - static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; static constexpr std::array fwmMultHNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; - static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; @@ -266,14 +268,12 @@ struct FoxWolframCorrelation { } // Define axes shared by the registered histograms. - const AxisSpec axispT{100, 0.15, 10, "p_{T}"}; - const AxisSpec axispTL{100, 2, 3, "p_{T}^{Lead}"}; - const AxisSpec axispTA{100, 0.15, 2, "p_{T}^{Assoc}"}; + const AxisSpec axispT{cfgAxisPt, "p_{T} (GeV/c)"}; + const AxisSpec axisMultip{cfgAxisMultip, "Multiplicity"}; const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; const AxisSpec axisPhi{100, 0, constants::math::TwoPI, "#phi"}; const AxisSpec axisEta{40, -0.8, 0.8, "#eta"}; - const AxisSpec axisMultip{350, 0, 350, "Multiplicity"}; const AxisSpec axisHlrec{50, 0, 1, "H_{l}"}; const AxisSpec axisHlfine{100, 0, 1, "H_{l}"}; const AxisSpec axisEvent{10, 0.5, 10.5, "", "EventAxis"}; @@ -350,66 +350,82 @@ struct FoxWolframCorrelation { } // Register inclusive multiplicity, track spectra, and recoil histograms. - histos.add("Multip/ITSTPC_Multiplicity", "Reconstructed ITS-TPC Multiplicity; trks; Events", kTH1D, {axisMultip}); - - histos.add("pTSpectra/pT", "pT", kTH1D, {axispT}); - histos.add("Spectra/phi", "phi", kTH1D, {axisPhi}); - - histos.add("pTSpectra/RecoilALL", "RecoilALL", kTH2D, {axisMultip, axisRecoil}); - histos.add("pTSpectra/RecoilTRI", "RecoilTRI", kTH2D, {axisMultip, axisRecoil}); + histos.add("Multip/multipAllEv", "multipAllEv; trks; Events", kTH1D, {axisMultip}); + histos.add("Multip/multipTriEv", "multipTriEv; trks; Events", kTH1D, {axisMultip}); + + histos.add("Multip/nTrkTriEvS1p", "nTrkTriEvS1p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS2p", "nTrkTriEvS2p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS3p", "nTrkTriEvS3p", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS1m", "nTrkTriEvS1m", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS2m", "nTrkTriEvS2m", kTH1D, {axisMultip}); + histos.add("Multip/nTrkTriEvS3m", "nTrkTriEvS3m", kTH1D, {axisMultip}); + + // Assoc. pT spectra + histos.add("pTSpectra/pTAssocAllEv", "pTAssocAllEv", kTH1D, {axispT}); + histos.add("pTSpectra/pTAssocTriEv", "pTAssocTriEv", kTH1D, {axispT}); + histos.add("pTSpectra/meanpTAssocMultipAllEv", "meanpTAssocMultipAllEv", kTProfile, {axisMultip}); + histos.add("pTSpectra/meanpTAssocMultipTriEv", "meanpTAssocMultipTriEv", kTProfile, {axisMultip}); + + // Lead. pT spectra + histos.add("pTSpectra/pTLeadAllEv", "pTLeadAllEv", kTH1D, {axispT}); + histos.add("pTSpectra/pTLeadTriEv", "pTLeadTriEv", kTH1D, {axispT}); + histos.add("pTSpectra/ptLeadMultipAllEv", "ptLeadMultipAllEv", kTH2D, {axisMultip, axispT}); + histos.add("pTSpectra/ptLeadMultipTriEv", "ptLeadMultipTriEv", kTH2D, {axisMultip, axispT}); + histos.add("pTSpectra/meanptLeadMultipAllEv", "meanptLeadMultipAllEv", kTProfile, {axisMultip}); + histos.add("pTSpectra/meanptLeadMultipTriEv", "meanptLeadMultipTriEv", kTProfile, {axisMultip}); + + histos.add("pTSpectra/RecoilAllEv", "RecoilAllEv", kTH2D, {axisMultip, axisRecoil}); + histos.add("pTSpectra/RecoilTriEv", "RecoilTriEv", kTH2D, {axisMultip, axisRecoil}); // Register track spectra for each multiplicity interval. for (int i = 1; i <= nMultBins; i++) { - histos.add(fmt::format("Multip/nTrkAllEv_BIN{}", i).c_str(), fmt::format("nTrkAllEv_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrktriEvSetNom_BIN{}", i).c_str(), fmt::format("nTrktriEvSetNom_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS1p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1p_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS2p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2p_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS3p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3p_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS1m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1m_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS2m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2m_BIN{}", i).c_str(), kTH1D, {axisMultip}); - histos.add(fmt::format("Multip/nTrkTriEvS3m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3m_BIN{}", i).c_str(), kTH1D, {axisMultip}); - - histos.add(fmt::format("Spectra/etaphi_BIN{}", i).c_str(), fmt::format("etaphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); - histos.add(fmt::format("Spectra/ptphi_BIN{}", i).c_str(), fmt::format("ptphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); - - histos.add(fmt::format("Spectra/etaphiTri_BIN{}", i).c_str(), fmt::format("etaphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); - histos.add(fmt::format("Spectra/ptphiTri_BIN{}", i).c_str(), fmt::format("ptphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); - - // onPeaks and onValleys - ALL EVENTS + + // eta-phi and pt-phi + histos.add(fmt::format("Spectra/etaphiAllEv_BIN{}", i).c_str(), fmt::format("etaphiAllEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphiAllEv_BIN{}", i).c_str(), fmt::format("ptphiAllEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + histos.add(fmt::format("Spectra/etaphiTriEv_BIN{}", i).c_str(), fmt::format("etaphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphiTriEv_BIN{}", i).c_str(), fmt::format("ptphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + // onPeaks and onValleys pt spectra - ALL EVENTS histos.add(fmt::format("allEvPkVall/allLRNearPt_BIN{}", i).c_str(), fmt::format("allLRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRNearPtMultip_BIN{}", i).c_str(), fmt::format("allLRNearPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); + histos.add(fmt::format("allEvPkVall/allSRNearPt_BIN{}", i).c_str(), fmt::format("allSRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRNearPtMultip_BIN{}", i).c_str(), fmt::format("allSRNearPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("allEvPkVall/allLRValleyPt_BIN{}", i).c_str(), fmt::format("allLRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRValleyPtMultip_BIN{}", i).c_str(), fmt::format("allLRValleyPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("allEvPkVall/allSRValleyPt_BIN{}", i).c_str(), fmt::format("allSRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRValleyPtMultip_BIN{}", i).c_str(), fmt::format("allSRValleyPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("allEvPkVall/allLRAwayPt_BIN{}", i).c_str(), fmt::format("allLRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allLRAwayPtMultip_BIN{}", i).c_str(), fmt::format("allLRAwayPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("allEvPkVall/allSRAwayPt_BIN{}", i).c_str(), fmt::format("allSRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRAwayPtMultip_BIN{}", i).c_str(), fmt::format("allSRAwayPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); - // onPeaks and onValleys - TRI EVENTS + // onPeaks and onValleys pt spectra - TRI EVENTS histos.add(fmt::format("triEvPkVall/triLRPeak1Pt_BIN{}", i).c_str(), fmt::format("triLRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak1PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak1PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triSRPeak1Pt_BIN{}", i).c_str(), fmt::format("triSRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak1PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak1PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triLRPeak2Pt_BIN{}", i).c_str(), fmt::format("triLRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak2PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak2PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triSRPeak2Pt_BIN{}", i).c_str(), fmt::format("triSRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak2PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak2PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triLRPeak3Pt_BIN{}", i).c_str(), fmt::format("triLRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRPeak3PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak3PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triSRPeak3Pt_BIN{}", i).c_str(), fmt::format("triSRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak3PtMultip_BIN{}", i).c_str(), fmt::format("triSRPeak3PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triLRValleysPt_BIN{}", i).c_str(), fmt::format("triLRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triLRValleysPtMultip_BIN{}", i).c_str(), fmt::format("triLRValleysPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triSRValleysPt_BIN{}", i).c_str(), fmt::format("triSRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); - } - - // Register leading and associated track spectra in the broad ranges. - for (int i = 1; i <= 3; i++) { - histos.add(fmt::format("pTSpectra/pT_LA_BIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); - histos.add(fmt::format("pTSpectra/pT_LA_TriBIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); - - histos.add(fmt::format("pTSpectra/fpT_LA_BIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); - histos.add(fmt::format("pTSpectra/fpT_LA_TriBIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); - } + histos.add(fmt::format("triEvPkVall/triSRValleysPtMultip_BIN{}", i).c_str(), fmt::format("triSRValleysPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); - // Register same-event angular correlations. - for (int i = 1; i <= nMultBins; ++i) { + // Same Even Fct histos.add(fmt::format("Corelations/sTRI_BIN{}", i).c_str(), fmt::format("sTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); histos.add(fmt::format("Corelations/sProjTRI_BIN{}", i).c_str(), fmt::format("sProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); } @@ -587,45 +603,17 @@ struct FoxWolframCorrelation { if (binIndex == idx + 1) { - histos.fill(HIST("Multip/nTrkAllEv_") + HIST(binNames[idx]), tracks.size()); - for (const auto& track : tracks) { - histos.fill(HIST("Spectra/etaphi_") + HIST(binNames[idx]), track.phi(), track.eta()); - histos.fill(HIST("Spectra/ptphi_") + HIST(binNames[idx]), track.phi(), track.pt()); - } - // Fill spectra for the configured topology selection. - if (isSelectedTopology) { + histos.fill(HIST("Spectra/etaphiAllEv_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiAllEv_") + HIST(binNames[idx]), track.phi(), track.pt()); - histos.fill(HIST("Multip/nTrktriEvSetNom_") + HIST(binNames[idx]), tracks.size()); - - for (const auto& track : tracks) { - histos.fill(HIST("Spectra/etaphiTri_") + HIST(binNames[idx]), track.phi(), track.eta()); - histos.fill(HIST("Spectra/ptphiTri_") + HIST(binNames[idx]), track.phi(), track.pt()); - } - } - - // TRI Ev. Multip (the remaining filters) - p == permissive and m == restrictive - if (triFlags[kTriSet1p]) { - histos.fill(HIST("Multip/nTrkTriEvS1p_") + HIST(binNames[idx]), tracks.size()); - } + if (!isSelectedTopology) { + continue; + } // TRI Ev. - if (triFlags[kTriSet2p]) { - histos.fill(HIST("Multip/nTrkTriEvS2p_") + HIST(binNames[idx]), tracks.size()); - } - - if (triFlags[kTriSet3p]) { - histos.fill(HIST("Multip/nTrkTriEvS3p_") + HIST(binNames[idx]), tracks.size()); - } - - if (triFlags[kTriSet1m]) { - histos.fill(HIST("Multip/nTrkTriEvS1m_") + HIST(binNames[idx]), tracks.size()); - } - if (triFlags[kTriSet2m]) { - histos.fill(HIST("Multip/nTrkTriEvS2m_") + HIST(binNames[idx]), tracks.size()); - } - if (triFlags[kTriSet3m]) { - histos.fill(HIST("Multip/nTrkTriEvS3m_") + HIST(binNames[idx]), tracks.size()); + histos.fill(HIST("Spectra/etaphiTriEv_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiTriEv_") + HIST(binNames[idx]), track.phi(), track.pt()); } } }); // per Multip Bin @@ -634,6 +622,7 @@ struct FoxWolframCorrelation { // Fill same-event pair distributions for one multiplicity interval. template void fillSameEventHist(int binIndex, + int nTracks, Track const& associatedTrack, double deltaPhi, double deltaEta, @@ -667,27 +656,33 @@ struct FoxWolframCorrelation { if (onNearPeak) { // NEAR SIDE if (inLongRange) { histos.fill(HIST("allEvPkVall/allLRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allLRNearPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("allEvPkVall/allSRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allSRNearPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } if (onBetweenNearAway) { // BETWEEN SIDE if (inLongRange) { histos.fill(HIST("allEvPkVall/allLRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allLRValleyPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("allEvPkVall/allSRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allSRValleyPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } if (onAwayPeak) { // AWAY SIDE if (inLongRange) { histos.fill(HIST("allEvPkVall/allLRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allLRAwayPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("allEvPkVall/allSRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("allEvPkVall/allSRAwayPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } } // for ALL Events @@ -711,9 +706,11 @@ struct FoxWolframCorrelation { if (onPeak1) { // on Peak at 0 if (inLongRange) { histos.fill(HIST("triEvPkVall/triLRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triLRPeak1PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("triEvPkVall/triSRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triSRPeak1PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } @@ -721,10 +718,12 @@ struct FoxWolframCorrelation { if (inLongRange) { histos.fill(HIST("triEvPkVall/triLRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triLRPeak2PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("triEvPkVall/triSRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triSRPeak2PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } @@ -732,10 +731,12 @@ struct FoxWolframCorrelation { if (inLongRange) { histos.fill(HIST("triEvPkVall/triLRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triLRPeak3PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("triEvPkVall/triSRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triSRPeak3PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } @@ -743,16 +744,18 @@ struct FoxWolframCorrelation { // Combined valleys at 60 and 180 degrees // if (inLongRange) { histos.fill(HIST("triEvPkVall/triLRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triLRValleysPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } if (inShortRange) { histos.fill(HIST("triEvPkVall/triSRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + histos.fill(HIST("triEvPkVall/triSRValleysPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); } } } // for TRI-like Events - // Count accepted pairs and fill their angular distributions. + // Same Event - Corr fct histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); @@ -859,53 +862,61 @@ struct FoxWolframCorrelation { (topologyMode.value == kTriEvSet2m && isTriSet2m) || (topologyMode.value == kTriEvSet3m && isTriSet3m); - // Fill inclusive event multiplicity and recoil. - histos.fill(HIST("Multip/ITSTPC_Multiplicity"), nGlobalTracks); - histos.fill(HIST("pTSpectra/RecoilALL"), nGlobalTracks, nRecoil); + // TRI Ev. Multip (the remaining filters) - p == permissive and m == restrictive + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Multip/nTrkTriEvS1p"), nGlobalTracks); + } + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Multip/nTrkTriEvS2p"), nGlobalTracks); + } + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Multip/nTrkTriEvS3p"), nGlobalTracks); + } + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Multip/nTrkTriEvS1m"), nGlobalTracks); + } + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Multip/nTrkTriEvS2m"), nGlobalTracks); + } + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Multip/nTrkTriEvS3m"), nGlobalTracks); + } + + // Fill inclusive event multiplicity, recoil and average pt lead. + + histos.fill(HIST("Multip/multipAllEv"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilAllEv"), nGlobalTracks, nRecoil); + histos.fill(HIST("pTSpectra/pTLeadAllEv"), triggTrk.pt()); + histos.fill(HIST("pTSpectra/ptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); + histos.fill(HIST("pTSpectra/meanptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); - if (isSelectedTopology) { - histos.fill(HIST("pTSpectra/RecoilTRI"), nGlobalTracks, nRecoil); + if (isSelectedTopology) { // for Tri-like Ev. + histos.fill(HIST("Multip/multipTriEv"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilTriEv"), nGlobalTracks, nRecoil); + histos.fill(HIST("pTSpectra/pTLeadTriEv"), triggTrk.pt()); + histos.fill(HIST("pTSpectra/ptLeadMultipTriEv"), nGlobalTracks, triggTrk.pt()); + histos.fill(HIST("pTSpectra/meanptLeadMultipTriEv"), nGlobalTracks, triggTrk.pt()); } // Fill inclusive and topology-selected leading-track spectra. for (const auto& track : tracks) { - histos.fill(HIST("pTSpectra/pT"), track.pt()); - histos.fill(HIST("Spectra/phi"), track.phi()); - if (track.globalIndex() == triggTrk.globalIndex()) { continue; - } + } // just the assoc. particles - if (multRANGE1) { - histos.fill(HIST("pTSpectra/pT_LA_BIN1"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_BIN1"), triggTrk.pt(), track.pt()); - } - if (multRANGE2) { - histos.fill(HIST("pTSpectra/pT_LA_BIN2"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_BIN2"), triggTrk.pt(), track.pt()); - } - if (multRANGE3) { - histos.fill(HIST("pTSpectra/pT_LA_BIN3"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_BIN3"), triggTrk.pt(), track.pt()); - } + histos.fill(HIST("pTSpectra/pTAssocAllEv"), track.pt()); + histos.fill(HIST("pTSpectra/meanpTAssocMultipAllEv"), nGlobalTracks, track.pt()); if (!isSelectedTopology) { continue; - } + } // for Tri-like Ev. - if (multRANGE1) { - histos.fill(HIST("pTSpectra/pT_LA_TriBIN1"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_TriBIN1"), triggTrk.pt(), track.pt()); - } - if (multRANGE2) { - histos.fill(HIST("pTSpectra/pT_LA_TriBIN2"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_TriBIN2"), triggTrk.pt(), track.pt()); - } - if (multRANGE3) { - histos.fill(HIST("pTSpectra/pT_LA_TriBIN3"), triggTrk.pt(), track.pt()); - histos.fill(HIST("pTSpectra/fpT_LA_TriBIN3"), triggTrk.pt(), track.pt()); - } + histos.fill(HIST("pTSpectra/pTAssocTriEv"), track.pt()); + histos.fill(HIST("pTSpectra/meanpTAssocMultipTriEv"), nGlobalTracks, track.pt()); } // Build same-event angular correlations for the selected topology. @@ -927,7 +938,7 @@ struct FoxWolframCorrelation { for (int i = 0; i < nMultBins; ++i) { if (inBin[i]) { - fillSameEventHist(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + fillSameEventHist(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); } } } From 5397143e834151f3f2ae55a0090be10c2eb38857 Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Wed, 4 Nov 2026 22:56:09 +0530 Subject: [PATCH 564/648] [PWGCF] Lambda-Proton-BF-Update 9 (#18586) Co-authored-by: ALICE Action Bot --- .../Tasks/lambdaProtonBalanceFunction.cxx | 1413 ++++++++++++----- 1 file changed, 1050 insertions(+), 363 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 648464f6973..cfb211bebe2 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -72,7 +71,7 @@ using MyTracks = soa::Join; -// MC-labelled versions used only by processMC (efficiency maps) +// MC-labelled versions used only by processMCEfficiency (efficiency maps) using MyTracksMC = soa::Join; using MyV0sMC = soa::Join; @@ -108,7 +107,7 @@ struct LambdaProtonBalanceFunction { // Folder: ProtonVetoQA/ // Contains per-event distributions of N_before, N_after, N_removed, fraction. HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // MC efficiency maps (Gen*/Rec* TH3: x=cent, y=pT, z=y), filled only by processMC. Folder: Efficiency/ + // MC efficiency maps (Gen*/Rec* TH3: x=cent, y=pT, z=y), filled only by processMCEfficiency. Folder: Efficiency/ HistogramRegistry registryEff{"Efficiency", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // ════════════════════════════════════════════════════════════════════════ @@ -189,6 +188,9 @@ struct LambdaProtonBalanceFunction { int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins float mRhoYMin = 0.f; float mRhoYMax = 0.f; + // Centrality range of the analysis = first and last edge of cCentBins (set in init()). + float mCentMin = 0.f; + float mCentMax = 0.f; // 1/efficiency maps (TH3: x=cent, y=pt, z=y); loaded once in init() when cApplyEffCorr is true TH3* invEffL = nullptr; @@ -206,15 +208,16 @@ struct LambdaProtonBalanceFunction { // ── Configurables ───────────────────────────────────────────────────── Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cPileupReject{"cPileupReject", false, "Reject same-bunch pileup (kNoSameBunchPileup)"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "Require FT0 vs PV z-vertex agreement (kIsGoodZvtxFT0vsPV)"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Require all ITS layers good (kIsGoodITSLayersAll)"}; Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; - Configurable cQinvCutValue{"cQinvCutValue", 0.01f, "q_inv cut value (GeV/c)"}; - Configurable cQinvQABins{"cQinvQABins", 6000, "Number of bins for QA q_inv histograms"}; - Configurable cQinvQAMax{"cQinvQAMax", 6.0f, "Max q_inv for QA histograms (GeV/c)"}; + Configurable cQinvCutValue{"cQinvCutValue", 0.001f, "q_inv cut value (GeV/c)"}; + Configurable cQinvQABins{"cQinvQABins", 1000, "Number of bins for QA q_inv histograms"}; + Configurable cQinvQAMax{"cQinvQAMax", 1.0f, "Max q_inv for QA histograms (GeV/c)"}; Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; Configurable cUseFT0C{"cUseFT0C", false, "Use FT0C instead of FT0M as centrality estimator"}; ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; - Configurable cMinCentrality{"cMinCentrality", 0.0f, "Reject events with centrality below this value (%)"}; - Configurable cMaxCentrality{"cMaxCentrality", 80.0f, "Reject events with centrality above this value (%)"}; Configurable cApplyEffCorr{"cApplyEffCorr", false, "Weight rho1/rho2 by 1/efficiency"}; Configurable cEffFile{"cEffFile", "", "ROOT file with hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton (TH3: x=cent, y=pt, z=y). Used only if cEffCcdbPath is empty"}; Configurable cEffCcdbPath{"cEffCcdbPath", "", "CCDB path of a TList holding hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton; if non-empty it is used instead of cEffFile"}; @@ -232,13 +235,13 @@ struct LambdaProtonBalanceFunction { Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // tight cut for cleanest primary-proton sample Configurable pProtonMaxDCAz{"pProtonMaxDCAz", 0.1f, "Max |DCAz| (cm)"}; - Configurable pProtonTPCTOFSwitchPt{"pProtonTPCTOFSwitchPt", 0.700000238f, "pT threshold (GeV/c) above which TOF is used if available"}; + Configurable pProtonTPCTOFSwitchPt{"pProtonTPCTOFSwitchPt", 0.7f, "pT threshold (GeV/c) above which TOF is used if available"}; // Configurable cPPMinTofBeta{"cPPMinTofBeta", 0.4f, "Min TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented // Configurable cPPMaxTofBeta{"cPPMaxTofBeta", 0.96f, "Max TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented // V0 topology cuts + V0 DCA Cuts Configurable cV0MaxDcaDaughters{"cV0MaxDcaDaughters", 1.0f, "DCA V0 daughters < (cm)"}; - Configurable lambdaV0DaughterProtonMinDCAToPV{"lambdaV0DaughterProtonMinDCAToPV", 0.01f, "DCA proton daughter to PV > (cm)"}; + Configurable lambdaV0DaughterProtonMinDCAToPV{"lambdaV0DaughterProtonMinDCAToPV", 0.05f, "DCA proton daughter to PV > (cm)"}; Configurable lambdaV0DaughterPionMinDCAToPV{"lambdaV0DaughterPionMinDCAToPV", 0.10f, "DCA pion daughter to PV > (cm)"}; Configurable lambdaV0MinCosPA{"lambdaV0MinCosPA", 0.995, "V0 CosPA >"}; Configurable lambdaV0MinDecayRadius{"lambdaV0MinDecayRadius", 0.5f, "V0 radius > (cm)"}; @@ -253,9 +256,9 @@ struct LambdaProtonBalanceFunction { // Contaminant veto for primary (anti)protons: reject track if compatible with pi/K/e Configurable cUseContaminantVeto{"cUseContaminantVeto", true, "Reject primary (anti)protons compatible with pi/K/e hypotheses"}; Configurable cVetoUseTOF{"cVetoUseTOF", true, "Also apply TOF veto whenever the track has TOF"}; - Configurable pProtonVetoPionNsigma{"pProtonVetoPionNsigma", 3.0f, "Reject if |nSigma_pi| < this"}; - Configurable pProtonVetoKaonNsigma{"pProtonVetoKaonNsigma", 3.0f, "Reject if |nSigma_K| < this"}; - Configurable pProtonVetoElectronNsigma{"pProtonVetoElectronNsigma", 3.0f, "Reject if |nSigma_e| < this"}; + Configurable pProtonVetoPionNsigma{"pProtonVetoPionNsigma", 2.0f, "Reject if |nSigma_pi| < this"}; + Configurable pProtonVetoKaonNsigma{"pProtonVetoKaonNsigma", 2.0f, "Reject if |nSigma_K| < this"}; + Configurable pProtonVetoElectronNsigma{"pProtonVetoElectronNsigma", 2.0f, "Reject if |nSigma_e| < this"}; // // V0 daughter beta cuts (particle-dependent) //BETACUTCommented // Configurable cV0protonDauMinBeta{"cV0protonDauMinBeta", 0.4f, "Min TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented @@ -267,7 +270,7 @@ struct LambdaProtonBalanceFunction { Configurable pProtonMinPt{"pProtonMinPt", 0.5f, "Min pT for primary protons (GeV/c)"}; Configurable pProtonMaxPt{"pProtonMaxPt", 3.6f, "Max pT for primary protons (GeV/c)"}; - Configurable pProtonMaxY{"pProtonMaxY", 0.5f, "Max |y| for primary protons"}; + Configurable pProtonMaxY{"pProtonMaxY", 0.49f, "Max |y| for primary protons"}; // Lambda Kinematic Cuts (from strange9) Configurable lambdaV0MinPt{"lambdaV0MinPt", 0.60f, "Min pT for Lambdas"}; @@ -287,8 +290,8 @@ struct LambdaProtonBalanceFunction { // ── Rho1/Rho2 grid — configurable at runtime (Hyperloop-friendly) ──────── // These replace the former static constexpr k* constants. Default values // reproduce the original hardcoded grid (y: 10×72). - Configurable cRhoPhiBins{"cRhoPhiBins", 72, "Number of phi bins for rho1/rho2"}; - Configurable cRhoYBins{"cRhoYBins", 10, "Number of y bins for rho1/rho2"}; + Configurable cRhoPhiBins{"cRhoPhiBins", 36, "Number of phi bins for rho1/rho2"}; + Configurable cRhoYBins{"cRhoYBins", 12, "Number of y bins for rho1/rho2"}; Configurable cRhoYMin{"cRhoYMin", -0.5f, "Min y for rho1/rho2"}; Configurable cRhoYMax{"cRhoYMax", 0.5f, "Max y for rho1/rho2"}; // ───────────────────────────────────────────────────────────────────────── @@ -401,6 +404,23 @@ struct LambdaProtonBalanceFunction { return cUseFT0C.value ? col.centFT0C() : col.centFT0M(); } + // Extra event selection switches (all default false = no extra cut). + // sel8 and |posZ| are NOT handled here: they stay as the Filters of processData and as the explicit check in processMCEfficiency. + template + bool passesExtraEventSelection(TCollision const& col) const + { + if (cPileupReject.value && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (cZVtxTimeDiff.value && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (cIsGoodITSLayers.value && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + return true; + } + // Returns index of centrality bin for cent, or -1 if outside all bins. int centBinIndex(float cent) const { @@ -1230,8 +1250,69 @@ struct LambdaProtonBalanceFunction { return p; } + template + PartInfo makeMcInfo(TMc const& mc, float mass) const + { + PartInfo p; + p.gid = mc.globalIndex(); + p.px = mc.px(); + p.py = mc.py(); + p.pz = mc.pz(); + p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + mass * mass); + p.pt = mc.pt(); + p.y = RecoDecay::y(std::array{p.px, p.py, p.pz}, mass); + p.phi = mc.phi(); + // Keep the reco [0, 2pi) convention even if an input truth phi needs wrapping. + if (p.phi < 0.f || p.phi >= o2::constants::math::TwoPI) { + p.phi = RecoDecay::constrainAngle(p.phi, 0.f); + } + p.idxY = unrolledIndexY(p.y, p.phi); + return p; + } + + // One ordered-pair traversal for all twelve generated channels. The returned + // count follows reco: after qinv selection, before the rho-grid validity check. + int64_t fillGeneratedPairs(std::vector const& listA, + std::vector const& listB, bool sameSpecies, + std::shared_ptr const& integrated, + std::shared_ptr const& centrality) const + { + int64_t count = 0; + for (auto const& a : listA) { + for (auto const& b : listB) { + if (sameSpecies && a.gid == b.gid) { + continue; + } + if (failsQinvCut(computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E))) { + continue; + } + ++count; + // Generated particles always have unit weight, with no efficiency lookup. + fillIdx(integrated, a.idxY, b.idxY, 1.f); + fillIdx(centrality, a.idxY, b.idxY, 1.f); + } + } + return count; + } + void init(InitContext const&) { + if (doprocessMCComposition.value) { + if (doprocessData.value || doprocessMCRecBF.value || doprocessMCGenBF.value || doprocessMCEfficiency.value) { + LOGF(fatal, "processMCComposition requires processData, processMCRecBF, processMCGenBF and processMCEfficiency=false. processData defaults to true and must be disabled explicitly."); + } + if (cApplyEffCorr.value) { + LOGF(fatal, "processMCComposition requires cApplyEffCorr=false: composition stores unweighted counts and must not load efficiency maps."); + } + } + if (static_cast(doprocessData.value) + static_cast(doprocessMCRecBF.value) + + static_cast(doprocessMCGenBF.value) > + 1) { + LOGF(fatal, "BF modes share histograms: enable only one of processData, processMCRecBF, processMCGenBF. processData defaults to true; explicitly set processData=false for matched-event BF runs."); + } + if (doprocessMCGenBF.value && cApplyEffCorr.value) { + LOGF(fatal, "processMCGenBF requires cApplyEffCorr=false: generated weights must be exactly one."); + } // ── Populate rho1/rho2 grid members from Configurables ────────────────── state.mRhoPhiBins = cRhoPhiBins.value; state.mRhoYBins = cRhoYBins.value; @@ -1275,7 +1356,7 @@ struct LambdaProtonBalanceFunction { // ── Other / event-level histograms ─────────────────────────────────── registryOther.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); registryOther.add("hFT0MPercentile", ("Centrality percentile (" + centLabel + ")").c_str(), {HistType::kTH1F, {multAxis}}); - // ── MC efficiency maps (processMC): x = centrality (cCentBins), y = pT, z = y (same range/bins as rho1) ── + // ── MC efficiency maps (processMCEfficiency): x = centrality (cCentBins), y = pT, z = y (same range/bins as rho1) ── { const AxisSpec axCent(cCentBins, "centrality (%)"); const AxisSpec axPt{60, 0.0, 6.0, "#it{p}_{T} (GeV/#it{c})"}; @@ -1285,6 +1366,9 @@ struct LambdaProtonBalanceFunction { registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); } } + if (doprocessMCComposition.value) { + bookMCComposition(); + } registryOther.add("hEffWeightProton", "Efficiency weight 1/#epsilon, protons", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); registryOther.add("hEffWeightLambda", "Efficiency weight 1/#epsilon, #Lambda", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); registryOther.add("hEffWeightZeroCount", "Particles with efficiency weight 0 (1=p, 2=#bar{p}, 3=#Lambda, 4=#bar{#Lambda})", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); @@ -1515,8 +1599,8 @@ struct LambdaProtonBalanceFunction { registryQaDetector.add("Lambda/hCutFlow", "#Lambda candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); registryQaDetector.add("AntiLambda/hCutFlow", "#bar{#Lambda} candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); { - const char* v0CfLabels[10] = {"All V0s", "V0 Type", "Daughter Kinematics", "Unambiguous TPC PID", "Rapidity", - "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; + const std::array v0CfLabels = {"All V0s", "V0 Type", "Daughter Kinematics", "Unambiguous TPC PID", "Rapidity", + "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; auto hCFL = registryQaDetector.get(HIST("Lambda/hCutFlow")); auto hCFAL = registryQaDetector.get(HIST("AntiLambda/hCutFlow")); for (int ib = 0; ib < 10; ++ib) { @@ -1530,10 +1614,10 @@ struct LambdaProtonBalanceFunction { registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton", "Proton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton", "AntiProton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); { - const char* cfLabels2D[21] = {"All tracks", "hasTPC", "TPC crossed rows", "TPC rows/findable", "TPC chi2", "ITS Ncls", "ITS chi2", - "TPC signal", "p_TPC & pT window", "|y| window", "DCA xy,z", "PID (TPC/TOF)", "pi/K/e veto", - "V0-daughter veto", "Selected", "veto: TPC pi", "veto: TPC K", "veto: TPC e", - "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; + const std::array cfLabels2D = {"All tracks", "hasTPC", "TPC crossed rows", "TPC rows/findable", "TPC chi2", "ITS Ncls", "ITS chi2", + "TPC signal", "p_TPC & pT window", "|y| window", "DCA xy,z", "PID (TPC/TOF)", "pi/K/e veto", + "V0-daughter veto", "Selected", "veto: TPC pi", "veto: TPC K", "veto: TPC e", + "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; auto hCFp = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton")); auto hCFa = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton")); for (int ib = 0; ib < 21; ++ib) { @@ -1548,13 +1632,13 @@ struct LambdaProtonBalanceFunction { // -- Event cutflow -- registryOther.add("hEventCutflow", "Event Cutflow;Step;Counts", - {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + {HistType::kTH1F, {{5, 0.5f, 5.5f}}}); { auto hEC = registryOther.get(HIST("hEventCutflow")); hEC->GetXaxis()->SetBinLabel(1, "sel8 + |z|GetXaxis()->SetBinLabel(2, "numContrib >= 1"); - hEC->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f]", cUseFT0C.value ? "centFT0C" : "centFT0M", cMinCentrality.value, cMaxCentrality.value)); hEC->GetXaxis()->SetBinLabel(4, "INEL > 0"); + hEC->GetXaxis()->SetBinLabel(5, "extra event selection (pileup/zvtx/ITS flags)"); } // ── ProtonVetoQA histograms ─────────────────────────────────────────── @@ -2057,6 +2141,9 @@ struct LambdaProtonBalanceFunction { if (state.centEdgesLocal.size() < 2) { LOGF(fatal, "cCentBins needs at least 2 edges"); } + state.mCentMin = state.centEdgesLocal.front(); + state.mCentMax = state.centEdgesLocal.back(); + registryOther.get(HIST("hEventCutflow"))->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f)", cUseFT0C.value ? "centFT0C" : "centFT0M", state.mCentMin, state.mCentMax)); for (const float e : state.centEdgesLocal) { if (e != std::floor(e)) { LOGF(fatal, "cCentBins edges must be integers (folder names use integer edges)"); @@ -2102,6 +2189,58 @@ struct LambdaProtonBalanceFunction { } } + // Matched-event guards and generated-population diagnostic (no extra task members). + auto hMatched = registryOther.add("hMatchedEventQA", "Matched-event BF guards;Stage;Counts", + {HistType::kTH1F, {{7, 0.5f, 7.5f}}}); + const std::array matchedLabels = {"MC collisions seen", "reco-collision count != 1", "MC-label mismatch", + "failed sel8/|z|", "failed numContrib/centrality/INEL/extra selection", + "accepted", "McParticle MC-collision mismatch (must be 0)"}; + for (int i = 0; i < 7; ++i) { + hMatched->GetXaxis()->SetBinLabel(i + 1, matchedLabels[i]); + } + auto hDaughters = registryOther.add("hGenLambdaDaughters", "Primary generated Lambdas in accepted events, before acceptance;Population;Particles", + {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + const std::array daughterLabels = {"primary Lambda with daughters", "primary Lambda without daughters", + "primary AntiLambda with daughters", "primary AntiLambda without daughters"}; + for (int i = 0; i < 4; ++i) { + hDaughters->GetXaxis()->SetBinLabel(i + 1, daughterLabels[i]); + } + const std::string mode = doprocessData.value ? "processData" : doprocessMCRecBF.value ? "processMCRecBF" + : doprocessMCGenBF.value ? "processMCGenBF" + : doprocessMCComposition.value ? "processMCComposition" + : "none"; + std::vector configLabels = { + "activeBFMode=" + mode, + "cUseFT0C=" + std::to_string(static_cast(cUseFT0C.value)), + "cApplyEffCorr=" + std::to_string(static_cast(cApplyEffCorr.value)), + "cUseQinvCut=" + std::to_string(static_cast(cUseQinvCut.value)), + "cQinvCutValue=" + std::to_string(cQinvCutValue.value), + "cPileupReject=" + std::to_string(static_cast(cPileupReject.value)), + "cZVtxTimeDiff=" + std::to_string(static_cast(cZVtxTimeDiff.value)), + "cIsGoodITSLayers=" + std::to_string(static_cast(cIsGoodITSLayers.value)), + "cPPandV0ZVertexCut=" + std::to_string(cPPandV0ZVertexCut.value), + "cRhoYBins=" + std::to_string(cRhoYBins.value), + "cRhoPhiBins=" + std::to_string(cRhoPhiBins.value), + "cRhoYMin=" + std::to_string(cRhoYMin.value), + "cRhoYMax=" + std::to_string(cRhoYMax.value), + "mCentMin=" + std::to_string(state.mCentMin), + "mCentMax=" + std::to_string(state.mCentMax), + "cFillCentHists=" + std::to_string(static_cast(cFillCentHists.value))}; + std::string centBinsLabel = "cCentBins="; + for (const float edge : state.centEdgesLocal) { + if (centBinsLabel != "cCentBins=") { + centBinsLabel += ","; + } + centBinsLabel += std::to_string(edge); + } + configLabels.push_back(centBinsLabel); + const int nConfig = static_cast(configLabels.size()); + auto hConfig = registryOther.add("hRunConfig", "BF configuration;Setting;Accepted events", + {HistType::kTH1F, {{nConfig, 0.5f, nConfig + 0.5f}}}); + for (int i = 0; i < nConfig; ++i) { + hConfig->GetXaxis()->SetBinLabel(i + 1, configLabels[i].c_str()); + } + // ── Efficiency correction: load 1/eff maps once ────────────────────── if (cApplyEffCorr.value) { if (!cEffCcdbPath.value.empty()) { @@ -2110,7 +2249,7 @@ struct LambdaProtonBalanceFunction { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - TList* effList = ccdb->getForTimeStamp(cEffCcdbPath.value, cEffCcdbTimestamp.value); + auto* effList = ccdb->getForTimeStamp(cEffCcdbPath.value, cEffCcdbTimestamp.value); if (!effList) { LOGF(fatal, "Cannot get efficiency TList from CCDB path %s (timestamp %" PRId64 ")", cEffCcdbPath.value.c_str(), static_cast(cEffCcdbTimestamp.value)); } @@ -2212,54 +2351,60 @@ struct LambdaProtonBalanceFunction { } // ───────────────────────────────────────────────────────────────────── - void processData(soa::Filtered>::iterator const& collision, - aod::V0Datas const& V0s, - MyTracks const& tracks) + // Shared BF event stages. Entry follows sel8 and the vertex cut in every mode. + template + bool selectBFEvent(TCollision const& collision, TTracks const& tracks, float& cent, int& nTracksINEL) { - // ── Ordered-pair convention ──────────────────────────────────────────── - // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) - // pAp : trigger=proton, assoc=antiproton - // App : trigger=antiproton,assoc=proton - // ApAp : trigger=antiproton1,assoc=antiproton2 (self-pairs rejected) - // Lp : trigger=Lambda, assoc=proton (daughter-track overlap rejected) - // LAp : trigger=Lambda, assoc=antiproton - // ALp : trigger=AntiLambda,assoc=proton - // ALAp : trigger=AntiLambda,assoc=antiproton - // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) - // - // Every channel fills y-space only (unrolledIndexY). - // ───────────────────────────────────────────────────────────────────── - // ── Event cutflow bin 1 — enters processData (after sel8 + posZ filters) ── registryOther.fill(HIST("hEventCutflow"), 1.0f); if (collision.numContrib() < 1) { - return; + return false; } // ── Event cutflow bin 2 — after numContrib >= 1 ────────────────────── registryOther.fill(HIST("hEventCutflow"), 2.0f); - const float cent = getCentrality(collision); - if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { - return; + cent = getCentrality(collision); + if (cent < state.mCentMin || cent >= state.mCentMax) { + return false; } - // ── Event cutflow bin 3 — after centrality in [cMinCentrality, cMaxCentrality) ── + // ── Event cutflow bin 3 — after centrality in [first, last edge of cCentBins) ── registryOther.fill(HIST("hEventCutflow"), 3.0f); // INEL > 0: require at least one charged track in |eta| < 0.8 static constexpr float kMaxInelEta = 0.8f; - int nTracksINEL = 0; + nTracksINEL = 0; for (auto const& trk : tracks) { if (std::abs(trk.eta()) < kMaxInelEta) { ++nTracksINEL; } } if (nTracksINEL < 1) { - return; + return false; } // ── Event cutflow bin 4 — after INEL > 0 ───────────────────────────── registryOther.fill(HIST("hEventCutflow"), 4.0f); + if (!passesExtraEventSelection(collision)) { + return false; + } + // ── Event cutflow bin 5 — after extra event selection (pileup / zvtx / ITS flags) ── + registryOther.fill(HIST("hEventCutflow"), 5.0f); + + return true; + } + + void fillRunConfig() + { + auto hConfig = registryOther.get(HIST("hRunConfig")); + for (int bin = 1; bin <= hConfig->GetNbinsX(); ++bin) { + hConfig->Fill(static_cast(bin)); + } + } + + template + CentRho2Set* bookBFEvent(TCollision const& collision, float cent, int nTracksINEL) + { registryRho.fill(HIST("hEventCounter"), 0.5); // ── Centrality bin for the centrality-differential histograms ───────── @@ -2302,42 +2447,34 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); // ───────────────────────────────────────────────────────────────────── - // ====== Step 1: Initialise containers ====================================== - std::unordered_set v0DaughterTrackIds; - v0DaughterTrackIds.reserve(64); - std::vector selectedPrimProtons; - std::vector selectedPrimAntiProtons; - std::vector selectedLambdas; - std::vector selectedAntiLambdas; - - int nSelLambda = 0; - int nSelAntiLambda = 0; - int nPairs_Lambda_PrimProton = 0; - int nPairs_Lambda_PrimAntiProton = 0; - int nPairs_AntiLambda_PrimProton = 0; - int nPairs_AntiLambda_PrimAntiProton = 0; - - // ── Veto QA event counters (filled once per event) ─────────────────── - int nProtonBeforeVeto = 0; - int nProtonRemovedVeto = 0; - int nAntiPBeforeVeto = 0; - int nAntiPRemovedVeto = 0; - // ───────────────────────────────────────────────────────────────────── + fillRunConfig(); + return cs; + } + // Shared reconstructed selection: truth labels never participate in acceptance. + // QA calls stay at their original selection stages for the BF instantiation. + template + void selectRecoV0s(TCollision const& collision, TV0s const& V0s, + std::vector& selectedLambdas, + std::vector& selectedAntiLambdas, + std::unordered_set& v0DaughterTrackIds) + { // ====== Step 2: V0 validation loop (BEFORE track loop) ================= // Validates each V0, builds the veto set from validated daughters only, - // fills single-particle histograms, and populates selectedLambdas/AntiLambdas. - // Pairing with protons happens in Step 3 (after the track loop). + // fills QA only when requested, and populates selectedLambdas/AntiLambdas. + // BF single-particle fills and pairing remain in runRecoAnalysis(). constexpr float mLambdaPDG = o2::constants::physics::MassLambda; constexpr float mK0PDG = o2::constants::physics::MassK0Short; for (const auto& v0 : V0s) { - const auto& v0PosDauCached = v0.posTrack_as(); - const auto& v0NegDauCached = v0.negTrack_as(); + const auto& v0PosDauCached = v0.template posTrack_as(); + const auto& v0NegDauCached = v0.template negTrack_as(); // V0 cutflow bin 1: all V0s (both hypotheses) - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 1.0f); - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 1.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 1.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 1.0f); + } // ── TRULY RAW QA Fills — before any V0 selection cut ───────────── // Placed here at the very top of the loop: before v0Type, daughter @@ -2346,7 +2483,7 @@ struct LambdaProtonBalanceFunction { // We double-fill under both the Lambda hypothesis (pos=proton, neg=pion) // and the AntiLambda hypothesis (pos=pion, neg=proton) since no PID // decision has been made yet. This gives the true combinatorial view. - { + if constexpr (fillQA) { const auto& rawPosDau = v0PosDauCached; const auto& rawNegDau = v0NegDauCached; const float rawCtau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; @@ -2373,8 +2510,10 @@ struct LambdaProtonBalanceFunction { continue; } // V0 cutflow bin 2: V0 type (both hypotheses) - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 2.0f); - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 2.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 2.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 2.0f); + } // ───────────────────────────────────────────────────────────────── const auto& v0PosDau = v0PosDauCached; @@ -2388,8 +2527,10 @@ struct LambdaProtonBalanceFunction { continue; } // V0 cutflow bin 3: daughter kinematics (both hypotheses) - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 3.0f); - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 3.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 3.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 3.0f); + } // ───────────────────────────────────────────────────────────────── // ── PID hypothesis ──────────────────────────────────────────────── @@ -2406,9 +2547,13 @@ struct LambdaProtonBalanceFunction { } // V0 cutflow bin 4: unambiguous TPC PID — from here on, fill only the chosen hypothesis if (v0DauPID_AsLambda) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 4.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 4.0f); + } } else { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 4.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 4.0f); + } } // ───────────────────────────────────────────────────────────────── @@ -2432,9 +2577,13 @@ struct LambdaProtonBalanceFunction { } // V0 cutflow bin 5: rapidity if (v0DauPID_AsLambda) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 5.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 5.0f); + } } else { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 5.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 5.0f); + } } // ───────────────────────────────────────────────────────────────── @@ -2452,9 +2601,13 @@ struct LambdaProtonBalanceFunction { } // V0 cutflow bin 6: pT acceptance if (v0DauPID_AsLambda) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 6.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 6.0f); + } } else { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 6.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 6.0f); + } } // ───────────────────────────────────────────────────────────────── @@ -2482,9 +2635,13 @@ struct LambdaProtonBalanceFunction { } // V0 cutflow bin 7: topology (passTopoCuts) and daughter DCA-to-PV if (v0DauPID_AsLambda) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 7.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 7.0f); + } } else { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 7.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 7.0f); + } } // ───────────────────────────────────────────────────────────────── @@ -2497,9 +2654,13 @@ struct LambdaProtonBalanceFunction { } // V0 cutflow bin 8: cTau and K0s rejection if (v0DauPID_AsLambda) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 8.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 8.0f); + } } else { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 8.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 8.0f); + } } // ───────────────────────────────────────────────────────────────── @@ -2508,61 +2669,66 @@ struct LambdaProtonBalanceFunction { const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); // ── Item 2: K0S mass fill after K0S rejection + after Lambda/AntiLambda selection ── if (selLambda) { - registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + } } if (selAntiLambda) { - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); - } - // ───────────────────────────────────────────────────────────────── - - // ── pT bin index for per-bin histograms ────────────────────────── - const float pt = v0.pt(); - // VS Code: commented out unused variable. - // const float eta = v0.eta(); - // VS Code: commented out unused variable. - // const float pz = pt * std::sinh(eta); - int bin = -1; - for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { - if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { - bin = static_cast(i); - break; + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); } } // ───────────────────────────────────────────────────────────────── - // ── NoMassCut QA fills (PID + topo only) ───────────────────────── - // Note: fillLambdaQA/fillDaughterQA for 01_Raw were moved earlier in the - // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. - if (v0DauPID_AsLambda) { - if (bin >= 0) { - state.hMassLambdaPtBins[bin]->Fill(v0.mLambda()); - } - state.hMassLambdaMerged->Fill(v0.mLambda()); - registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); - // ── Item 2: mass vs eta/phi acceptance uniformity ── - registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); - registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mLambda()); - // ────────────────────────────────────────────────── - if (selLambda) { - registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + if constexpr (fillQA) { + // ── pT bin index for per-bin histograms ────────────────────────── + const float pt = v0.pt(); + // VS Code: commented out unused variable. + // const float eta = v0.eta(); + // VS Code: commented out unused variable. + // const float pz = pt * std::sinh(eta); + int bin = -1; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { + bin = static_cast(i); + break; + } } - } - if (v0DauPID_AsAntiLambda) { - if (bin >= 0) { - state.hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); + // ───────────────────────────────────────────────────────────────── + + // ── NoMassCut QA fills (PID + topo only) ───────────────────────── + // Note: fillLambdaQA/fillDaughterQA for 01_Raw were moved earlier in the + // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. + if (v0DauPID_AsLambda) { + if (bin >= 0) { + state.hMassLambdaPtBins[bin]->Fill(v0.mLambda()); + } + state.hMassLambdaMerged->Fill(v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mLambda()); + // ────────────────────────────────────────────────── + if (selLambda) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + } } - state.hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); - // ── Item 2: mass vs eta/phi acceptance uniformity ── - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mAntiLambda()); - // ────────────────────────────────────────────────── - if (selAntiLambda) { - registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + if (v0DauPID_AsAntiLambda) { + if (bin >= 0) { + state.hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); + } + state.hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mAntiLambda()); + // ────────────────────────────────────────────────── + if (selAntiLambda) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + } } + // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // // ── Daughter beta window veto (after NoMassCut fills, before Final) ─── //BETACUTCommented // // Raw and NoMassCut histograms above are filled for ALL topology-passing //BETACUTCommented // // candidates regardless of daughter beta. The veto here prevents V0s with //BETACUTCommented @@ -2592,7 +2758,9 @@ struct LambdaProtonBalanceFunction { // ── Step 3: Selected Lambda — populate veto set + rho1 + pT histos ─ if (selLambda) { // V0 cutflow bin 9: mass window (pre-cleaning) - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 9.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 9.0f); + } selectedLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); @@ -2602,7 +2770,9 @@ struct LambdaProtonBalanceFunction { if (selAntiLambda) { // V0 cutflow bin 9: mass window (pre-cleaning) - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 9.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 9.0f); + } selectedAntiLambdas.push_back(v0); v0DaughterTrackIds.insert(v0.posTrackId()); v0DaughterTrackIds.insert(v0.negTrackId()); @@ -2616,8 +2786,8 @@ struct LambdaProtonBalanceFunction { // ====== Shared-daughter cleaning (symmetric, over the Lambda ∪ AntiLambda union) ====== // Two candidates sharing a positive OR a negative daughter track are BOTH rejected. // v0DaughterTrackIds is intentionally left untouched (conservative primary veto). - const int nRawLambda = static_cast(selectedLambdas.size()); - const int nRawAntiLambda = static_cast(selectedAntiLambdas.size()); + [[maybe_unused]] const int nRawLambda = static_cast(selectedLambdas.size()); + [[maybe_unused]] const int nRawAntiLambda = static_cast(selectedAntiLambdas.size()); std::unordered_map posIdCount; std::unordered_map negIdCount; @@ -2630,8 +2800,8 @@ struct LambdaProtonBalanceFunction { ++negIdCount[v.negTrackId()]; } - std::vector cleanLambdas; - std::vector cleanAntiLambdas; + std::vector cleanLambdas; + std::vector cleanAntiLambdas; for (const auto& v : selectedLambdas) { if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { cleanLambdas.push_back(v); @@ -2647,144 +2817,132 @@ struct LambdaProtonBalanceFunction { // V0 cutflow bin 10: survivors of shared-daughter cleaning for ([[maybe_unused]] const auto& v : selectedLambdas) { - registryQaDetector.fill(HIST("Lambda/hCutFlow"), 10.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 10.0f); + } } for ([[maybe_unused]] const auto& v : selectedAntiLambdas) { - registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 10.0f); + if constexpr (fillQA) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 10.0f); + } } - nSelLambda = static_cast(selectedLambdas.size()); - nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + [[maybe_unused]] const int nSelLambda = static_cast(selectedLambdas.size()); + [[maybe_unused]] const int nSelAntiLambda = static_cast(selectedAntiLambdas.size()); // Cleaning diagnostics - registryOther.fill(HIST("h1f_nLambda_beforeClean"), static_cast(nRawLambda)); - registryOther.fill(HIST("h1f_nLambda_rejectedClean"), static_cast(nRawLambda - nSelLambda)); - registryOther.fill(HIST("h1f_nLambda_afterClean"), static_cast(nSelLambda)); - registryOther.fill(HIST("h1f_nAntiLambda_beforeClean"), static_cast(nRawAntiLambda)); - registryOther.fill(HIST("h1f_nAntiLambda_rejectedClean"), static_cast(nRawAntiLambda - nSelAntiLambda)); - registryOther.fill(HIST("h1f_nAntiLambda_afterClean"), static_cast(nSelAntiLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 1.0f, static_cast(nRawLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 2.0f, static_cast(nRawLambda - nSelLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 3.0f, static_cast(nSelLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 4.0f, static_cast(nRawAntiLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 5.0f, static_cast(nRawAntiLambda - nSelAntiLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 6.0f, static_cast(nSelAntiLambda)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 7.0f, static_cast(nSelLambda) * static_cast(nSelLambda - 1)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 8.0f, static_cast(nSelAntiLambda) * static_cast(nSelAntiLambda - 1)); - registryOther.fill(HIST("hSharedDauCleaningSums"), 9.0f, static_cast(nSelLambda) * static_cast(nSelAntiLambda)); - - // rho1 and pT spectra from the cleaned sample (same sample as rho2) - for (const auto& v0 : selectedLambdas) { - // Final QA on the cleaned sample (same sample as rho1 / rho2) - { - const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; - const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; - const auto& dauPos = v0.posTrack_as(); - const auto& dauNeg = v0.negTrack_as(); - fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); - fillDaughterQA(DaughterQAStage::Final, dauPos); - fillDaughterQA(DaughterQAStage::Final, dauNeg); - } - const float wL = getEffWeight(state.invEffL, cent, v0.pt(), v0.rapidity(1)); - registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi(), wL); - if (cs) { - cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi(), wL); - } - registryOther.fill(HIST("hEffWeightLambda"), wL); - if (wL == 0.f) { - registryOther.fill(HIST("hEffWeightZeroCount"), 3.f); - } - registryOther.fill(HIST("hPtSelLambda"), v0.pt()); - state.hPtSelLambda_AllPt->Fill(v0.pt()); + if constexpr (fillQA) { + registryOther.fill(HIST("h1f_nLambda_beforeClean"), static_cast(nRawLambda)); + registryOther.fill(HIST("h1f_nLambda_rejectedClean"), static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("h1f_nLambda_afterClean"), static_cast(nSelLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_beforeClean"), static_cast(nRawAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_rejectedClean"), static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_afterClean"), static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 1.0f, static_cast(nRawLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 2.0f, static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 3.0f, static_cast(nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 4.0f, static_cast(nRawAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 5.0f, static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 6.0f, static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 7.0f, static_cast(nSelLambda) * static_cast(nSelLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 8.0f, static_cast(nSelAntiLambda) * static_cast(nSelAntiLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 9.0f, static_cast(nSelLambda) * static_cast(nSelAntiLambda)); } - for (const auto& v0 : selectedAntiLambdas) { - // Final QA on the cleaned sample (same sample as rho1 / rho2) - { - const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; - const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; - const auto& dauPos = v0.posTrack_as(); - const auto& dauNeg = v0.negTrack_as(); - fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); - fillDaughterQA(DaughterQAStage::Final, dauNeg); - fillDaughterQA(DaughterQAStage::Final, dauPos); - } - const float wAL = getEffWeight(state.invEffAL, cent, v0.pt(), v0.rapidity(2)); - registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi(), wAL); - if (cs) { - cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi(), wAL); - } - if (wAL == 0.f) { - registryOther.fill(HIST("hEffWeightZeroCount"), 4.f); - } - registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); - state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); - } - // ====== End of shared-daughter cleaning =============================== + } - // ── Veto QA event counters (filled once per event) ─────────────────── + template + void selectRecoProtons(TTracks const& tracks, + std::unordered_set const& v0DaughterTrackIds, + std::vector& selectedPrimProtons, + std::vector& selectedPrimAntiProtons) + { + // Local veto counters retain the BF counting convention. + int nProtonBeforeVeto = 0; + int nProtonRemovedVeto = 0; + int nAntiPBeforeVeto = 0; + int nAntiPRemovedVeto = 0; for (auto const& trk : tracks) { - // ── RAW Stage QA Fills (truly before any selection — kinematics, detector quality, - // TPC, and TOF all filled here before any cut fires) ─────────────────────────── - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); - fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) { - fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - } - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); - } - } else { - fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); - fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); - if (trk.hasTPC()) { - fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); - } - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + if constexpr (fillQA) { + // ── RAW Stage QA Fills (truly before any selection — kinematics, detector quality, + // TPC, and TOF all filled here before any cut fires) ─────────────────────────── + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } } + // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // ── Track quality cuts ──────────────────────────────────────────── - fillCutFlow(1.0f, trk); + if constexpr (fillQA) { + fillCutFlow(1.0f, trk); + } if (!trk.hasTPC()) { continue; } - fillCutFlow(2.0f, trk); + if constexpr (fillQA) { + fillCutFlow(2.0f, trk); + } if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { continue; } - fillCutFlow(3.0f, trk); + if constexpr (fillQA) { + fillCutFlow(3.0f, trk); + } if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { continue; } - fillCutFlow(4.0f, trk); + if constexpr (fillQA) { + fillCutFlow(4.0f, trk); + } if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { continue; } - fillCutFlow(5.0f, trk); + if constexpr (fillQA) { + fillCutFlow(5.0f, trk); + } if (trk.itsNCls() < pProtonITSMinClusters.value) { continue; } - fillCutFlow(6.0f, trk); + if constexpr (fillQA) { + fillCutFlow(6.0f, trk); + } if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { continue; } - fillCutFlow(7.0f, trk); + if constexpr (fillQA) { + fillCutFlow(7.0f, trk); + } if (trk.tpcSignal() < cPPMinTpcSignal.value) { continue; } - fillCutFlow(8.0f, trk); + if constexpr (fillQA) { + fillCutFlow(8.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── pT acceptance cut ───────────────────────────────────────────── if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { continue; } - fillCutFlow(9.0f, trk); + if constexpr (fillQA) { + fillCutFlow(9.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── Proton kinematic acceptance ─────────────────────────────────── @@ -2794,7 +2952,9 @@ struct LambdaProtonBalanceFunction { if (std::abs(y) > pProtonMaxY.value) { continue; } - fillCutFlow(10.0f, trk); + if constexpr (fillQA) { + fillCutFlow(10.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── DCA cuts ───────────────────────────────────────────────────── const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && @@ -2802,68 +2962,73 @@ struct LambdaProtonBalanceFunction { if (!passDCA) { continue; } - fillCutFlow(11.0f, trk); + if constexpr (fillQA) { + fillCutFlow(11.0f, trk); + } // ───────────────────────────────────────────────────────────────── - // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) - if (trk.sign() > 0) { - const float pt = trk.pt(); - const float nsTPC = std::abs(trk.tpcNSigmaPr()); + if constexpr (fillQA) { + // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) + if (trk.sign() > 0) { + const float pt = trk.pt(); + const float nsTPC = std::abs(trk.tpcNSigmaPr()); - if (nsTPC < pProtonTPCNsigma.value) { - state.h_TPC->Fill(pt); - } - if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && - std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - state.h_TPCandTOF->Fill(pt); - } - if (nsTPC < pProtonTPCNsigma.value) { - if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - state.h_TPCorTOF->Fill(pt); + if (nsTPC < pProtonTPCNsigma.value) { + state.h_TPC->Fill(pt); + } + if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && + std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCandTOF->Fill(pt); + } + if (nsTPC < pProtonTPCNsigma.value) { + if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCorTOF->Fill(pt); + } } } - } - // ───────────────────────────────────────────────────────────────── + // ───────────────────────────────────────────────────────────────── - // TPC PID pass QA fill (intermediate stage, before full selection) - if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value) { - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::TpcPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::TpcPid, trk); - } - } else { - fillProtonKinematics(ProtonQAStage::TpcPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcPid, trk); - if (trk.hasTOF()) { - fillProtonTofQA(ProtonQAStage::TpcPid, trk); + // TPC PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } } } - } - // TPC+TOF PID pass QA fill (intermediate stage, before full selection) - if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value && trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { - if (trk.sign() > 0) { - fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); - fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); - } else { - fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); - fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); - fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); - fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + // TPC+TOF PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value && trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } else { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } } } - // ── Main PID selection ──────────────────────────────────────────── if (!passesPrimProtonPid(trk)) { continue; } - fillCutFlow(12.0f, trk); + if constexpr (fillQA) { + fillCutFlow(12.0f, trk); + } // pi/K/e contaminant veto if (!passesContaminantVeto(trk)) { @@ -2871,46 +3036,51 @@ struct LambdaProtonBalanceFunction { const int vetoMask = contaminantVetoMask(trk, vetoUsedTOF); for (int ib = 0; ib < 3; ++ib) { if (vetoMask & (1 << ib)) { - fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); + if constexpr (fillQA) { + fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); + } } } continue; } - fillCutFlow(13.0f, trk); + if constexpr (fillQA) { + fillCutFlow(13.0f, trk); + } - // TOF Matching Fraction check on selected protons - registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); - // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── - registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi"), - trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); - // ───────────────────────────────────────────────────────────────── + if constexpr (fillQA) { + // TOF Matching Fraction check on selected protons + registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); + // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + // ───────────────────────────────────────────────────────────────── - // ── PID dEdx monitoring ─────────────────────────────────────────── - // vs total-p: unchanged — intentional total-momentum view - // vs pTPC (tpcInnerParam): added alongside for proton-selection QA comparison - if (trk.sign() > 0) { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); - if (trk.hasTOF()) { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); - } else { - registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); - } - } else { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); - if (trk.hasTOF()) { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + // ── PID dEdx monitoring ─────────────────────────────────────────── + // vs total-p: unchanged — intentional total-momentum view + // vs pTPC (tpcInnerParam): added alongside for proton-selection QA comparison + if (trk.sign() > 0) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } } else { - registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); - registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } } + // ───────────────────────────────────────────────────────────────── } - // ───────────────────────────────────────────────────────────────── - // ── V0-daughter veto: reject tracks shared with any V0 daughter ─── // Count before veto, check veto set, then classify or skip. // This MUST appear before push_back so that every pp histogram @@ -2931,51 +3101,61 @@ struct LambdaProtonBalanceFunction { } continue; } - fillCutFlow(14.0f, trk); + if constexpr (fillQA) { + fillCutFlow(14.0f, trk); + } // ───────────────────────────────────────────────────────────────── // ── Classify into proton / antiproton lists ─────────────────────── - fillCutFlow(15.0f, trk); + if constexpr (fillQA) { + fillCutFlow(15.0f, trk); + } // VS Code: commented out unused variables. // const float pz = trk.pt() * std::sinh(trk.eta()); // const float eta = trk.eta(); if (trk.sign() > 0) { selectedPrimProtons.push_back(trk); - fillProtonKinematics(ProtonQAStage::FinalSelected, trk); - fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); - fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); - // TOF QA only when track was accepted through the TPC+TOF branch: - // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { - fillProtonTofQA(ProtonQAStage::FinalSelected, trk); - } + if constexpr (fillQA) { + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } - registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); - state.hPtSelectedPrimProton_AllPt->Fill(trk.pt()); + registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); + state.hPtSelectedPrimProton_AllPt->Fill(trk.pt()); + } } else { selectedPrimAntiProtons.push_back(trk); - fillProtonKinematics(ProtonQAStage::FinalSelected, trk); - fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); - fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); - // TOF QA only when track was accepted through the TPC+TOF branch: - // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented - if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { - fillProtonTofQA(ProtonQAStage::FinalSelected, trk); - } + if constexpr (fillQA) { + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } - registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); - state.hPtSelectedPrimAntiProton_AllPt->Fill(trk.pt()); + registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); + state.hPtSelectedPrimAntiProton_AllPt->Fill(trk.pt()); + } } // ───────────────────────────────────────────────────────────────── } // ── Fill veto QA histograms (once per event, after the track loop) ── - registryOther.fill(HIST("hNPrimProtonBeforeVeto"), static_cast(nProtonBeforeVeto)); - registryOther.fill(HIST("hNPrimProtonRemovedByVeto"), static_cast(nProtonRemovedVeto)); - registryOther.fill(HIST("hNPrimProtonAfterVeto"), static_cast(nProtonBeforeVeto - nProtonRemovedVeto)); - registryOther.fill(HIST("hNPrimAntiProtonBeforeVeto"), static_cast(nAntiPBeforeVeto)); - registryOther.fill(HIST("hNPrimAntiProtonRemovedByVeto"), static_cast(nAntiPRemovedVeto)); - registryOther.fill(HIST("hNPrimAntiProtonAfterVeto"), static_cast(nAntiPBeforeVeto - nAntiPRemovedVeto)); + if constexpr (fillQA) { + registryOther.fill(HIST("hNPrimProtonBeforeVeto"), static_cast(nProtonBeforeVeto)); + registryOther.fill(HIST("hNPrimProtonRemovedByVeto"), static_cast(nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimProtonAfterVeto"), static_cast(nProtonBeforeVeto - nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonBeforeVeto"), static_cast(nAntiPBeforeVeto)); + registryOther.fill(HIST("hNPrimAntiProtonRemovedByVeto"), static_cast(nAntiPRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonAfterVeto"), static_cast(nAntiPBeforeVeto - nAntiPRemovedVeto)); + } // ───────────────────────────────────────────────────────────────────── // ── ProtonVetoQA: fill four dedicated per-event histograms ─────────── @@ -2985,7 +3165,7 @@ struct LambdaProtonBalanceFunction { // N_after_veto = nProtonBeforeVeto - nProtonRemovedVeto // = size of selectedPrimProtons, used by rho1(p), rho2(pp), rho2(p pbar) // fraction = N_removed / N_before (0 when N_before = 0, avoids divide-by-zero) - { + if constexpr (fillQA) { const int nBefore = nProtonBeforeVeto; const int nRemoved = nProtonRemovedVeto; const int nAfter = nBefore - nRemoved; @@ -3003,6 +3183,83 @@ struct LambdaProtonBalanceFunction { state.runTotalAntiPRemovedVeto += nAntiPRemovedVeto; } // ───────────────────────────────────────────────────────────────────── + } + + // Reconstructed candidates are compared with generated primary particles; + // residual background and feed-down can prevent full closure. + template + void runRecoAnalysis(TCollision const& collision, aod::V0Datas const& V0s, + MyTracks const& tracks, float cent, CentRho2Set* cs) + { + // ====== Step 1: Initialise containers ====================================== + std::unordered_set v0DaughterTrackIds; + v0DaughterTrackIds.reserve(64); + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + + int nSelLambda = 0; + int nSelAntiLambda = 0; + int nPairs_Lambda_PrimProton = 0; + int nPairs_Lambda_PrimAntiProton = 0; + int nPairs_AntiLambda_PrimProton = 0; + int nPairs_AntiLambda_PrimAntiProton = 0; + + selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); + nSelLambda = static_cast(selectedLambdas.size()); + nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + constexpr float mLambdaPDG = o2::constants::physics::MassLambda; + + // rho1 and pT spectra from the cleaned sample (same sample as rho2) + for (const auto& v0 : selectedLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauPos); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + } + const float wL = getEffWeight(state.invEffL, cent, v0.pt(), v0.rapidity(1)); + registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi(), wL); + if (cs) { + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi(), wL); + } + registryOther.fill(HIST("hEffWeightLambda"), wL); + if (wL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 3.f); + } + registryOther.fill(HIST("hPtSelLambda"), v0.pt()); + state.hPtSelLambda_AllPt->Fill(v0.pt()); + } + for (const auto& v0 : selectedAntiLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + fillDaughterQA(DaughterQAStage::Final, dauPos); + } + const float wAL = getEffWeight(state.invEffAL, cent, v0.pt(), v0.rapidity(2)); + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi(), wAL); + if (cs) { + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi(), wAL); + } + if (wAL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 4.f); + } + registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); + state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); + } + // ====== End of shared-daughter cleaning =============================== + + selectRecoProtons(tracks, v0DaughterTrackIds, selectedPrimProtons, selectedPrimAntiProtons); // ── Single-particle density for protons (y) ────────────────── for (auto const& primProton : selectedPrimProtons) { @@ -3038,12 +3295,12 @@ struct LambdaProtonBalanceFunction { std::vector antiProtonInfo; protonInfo.reserve(selectedPrimProtons.size()); antiProtonInfo.reserve(selectedPrimAntiProtons.size()); - std::transform(selectedPrimProtons.begin(), selectedPrimProtons.end(), - std::back_inserter(protonInfo), - [this](auto const& t) { return makeProtonInfo(t); }); - std::transform(selectedPrimAntiProtons.begin(), selectedPrimAntiProtons.end(), - std::back_inserter(antiProtonInfo), - [this](auto const& t) { return makeProtonInfo(t); }); + for (auto const& t : selectedPrimProtons) { + protonInfo.push_back(makeProtonInfo(t)); + } + for (auto const& t : selectedPrimAntiProtons) { + antiProtonInfo.push_back(makeProtonInfo(t)); + } // ── pp rho2 fills (y) ─────────────────────────────────────────────────── for (auto const& p1 : selectedPrimProtons) { @@ -3903,6 +4160,32 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nPairs_AntiLambda_PrimProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nPairs_AntiLambda_PrimAntiProton)); } + void processData(soa::Filtered>::iterator const& collision, + aod::V0Datas const& V0s, + MyTracks const& tracks) + { + // ── Ordered-pair convention ──────────────────────────────────────────── + // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) + // pAp : trigger=proton, assoc=antiproton + // App : trigger=antiproton,assoc=proton + // ApAp : trigger=antiproton1,assoc=antiproton2 (self-pairs rejected) + // Lp : trigger=Lambda, assoc=proton (daughter-track overlap rejected) + // LAp : trigger=Lambda, assoc=antiproton + // ALp : trigger=AntiLambda,assoc=proton + // ALAp : trigger=AntiLambda,assoc=antiproton + // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) + // + // Every channel fills y-space only (unrolledIndexY). + // ───────────────────────────────────────────────────────────────────── + + float cent = 0.f; + int nTracksINEL = 0; + if (!selectBFEvent(collision, tracks, cent, nTracksINEL)) { + return; + } + auto* cs = bookBFEvent(collision, cent, nTracksINEL); + runRecoAnalysis(collision, V0s, tracks, cent, cs); + } PROCESS_SWITCH(LambdaProtonBalanceFunction, processData, "Process for Data", true); // ══════════════════════════════════════════════════════════════════════════ @@ -3930,12 +4213,12 @@ struct LambdaProtonBalanceFunction { return pt > pProtonMinPt.value && pt < pProtonMaxPt.value && std::abs(y) < pProtonMaxY.value; } - void processMC(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - MyV0sMC const& allV0s, - MyTracksMC const& allTracks, - aod::McParticles const& mcParticles) + void processMCEfficiency(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& mcParticles) { // ── 1) Event association + selection (soa::Filtered does not apply to SmallGroups) ── // Exactly one reconstructed collision per MC collision: removes split-vertex double counting. @@ -3958,7 +4241,7 @@ struct LambdaProtonBalanceFunction { return; } const float cent = getCentrality(collision); // FT0C or FT0M according to cUseFT0C - if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { + if (cent < state.mCentMin || cent >= state.mCentMax) { return; } // INEL > 0 (same as processData()): at least one charged track in |eta| < 0.8 @@ -3974,6 +4257,11 @@ struct LambdaProtonBalanceFunction { } } + // Same extra event selection as processData(). It MUST be identical, otherwise the efficiency does not match the data. + if (!passesExtraEventSelection(collision)) { + return; + } + // ── 2) GENERATED: physical primaries of this MC collision (mcParticles is auto-sliced to mcCollision; // NO V0-daughter veto at generator level) ── for (auto const& mc : mcParticles) { @@ -4164,7 +4452,8 @@ struct LambdaProtonBalanceFunction { if (std::abs(protonRapidity(trk)) > pProtonMaxY.value) { continue; } - if (!((std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value))) { + const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value); + if (!passDCA) { continue; } if (!passesPrimProtonPid(trk)) { @@ -4196,14 +4485,412 @@ struct LambdaProtonBalanceFunction { } } } - PROCESS_SWITCH(LambdaProtonBalanceFunction, processMC, "Efficiency from MC", false); + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCEfficiency, "Produce MC single-particle efficiency inputs", false); // ───────────────────────────────────────────────────────────────────── + // ══ Matched-event BF selection (shared by reconstructed and generated modes) ══ + using MatchedCollisions = soa::Join; + Preslice v0sPerCollisionData = aod::v0data::collisionId; + Preslice tracksPerCollisionData = aod::track::collisionId; + + // This reference is conditional on accepted reconstructed events and excludes + // MC collisions with missing or split reconstructed collisions. + // Select once for BF and composition; only BF books BF event histograms. + template + void withMatchedEvent(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + TTracks const& allTracks, TPreslice const& trackPreslice, TAnalysis&& analyze) + { + registryOther.fill(HIST("hMatchedEventQA"), 1.f); + if (collisions.size() != 1) { + registryOther.fill(HIST("hMatchedEventQA"), 2.f); + return; + } + auto collision = collisions.begin(); + if (!collision.has_mcCollision() || collision.mcCollisionId() != mcCollision.globalIndex()) { + registryOther.fill(HIST("hMatchedEventQA"), 3.f); + return; + } + // SmallGroups do not inherit processData's framework Filters. + if (!collision.sel8() || !(std::abs(collision.posZ()) < cPPandV0ZVertexCut.value)) { + registryOther.fill(HIST("hMatchedEventQA"), 4.f); + return; + } + auto tracks = allTracks.sliceBy(trackPreslice, collision.globalIndex()); + float cent = 0.f; + int nTracksINEL = 0; + if (!selectBFEvent(collision, tracks, cent, nTracksINEL)) { + registryOther.fill(HIST("hMatchedEventQA"), 5.f); + return; + } + CentRho2Set* cs = nullptr; + if constexpr (fillBF) { + cs = bookBFEvent(collision, cent, nTracksINEL); + } + registryOther.fill(HIST("hMatchedEventQA"), 6.f); + std::forward(analyze)(collision, tracks, cent, cs); + } + + template + void withMatchedBFEvent(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyTracks const& allTracks, TAnalysis&& analyze) + { + withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollisionData, + std::forward(analyze)); + } + + // ══ Reconstructed BF in matched selected MC events ══ + void processMCRecBF(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::V0Datas const& allV0s, + MyTracks const& allTracks) + { + // Unlabelled candidate tables preserve the complete data-like reco sample. + withMatchedBFEvent(mcCollision, collisions, allTracks, + [&](auto const& collision, MyTracks const& tracks, float cent, CentRho2Set* cs) { + auto V0s = allV0s.sliceBy(v0sPerCollisionData, collision.globalIndex()); + runRecoAnalysis(collision, V0s, tracks, cent, cs); + }); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCRecBF, + "Reconstructed correlations in matched selected MC events", false); + + // ══ Generated BF in the identical matched selected MC events ══ + void processMCGenBF(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyTracks const& allTracks, + aod::McParticles const& mcParticles) + { + withMatchedBFEvent(mcCollision, collisions, allTracks, + [&](auto const&, MyTracks const&, float, CentRho2Set* cs) { + // mcParticles is auto-sliced by the MC collision; lists live only for this event. + std::vector lambdas, antiLambdas, protons, antiProtons; + for (auto const& mc : mcParticles) { + if (mc.mcCollisionId() != mcCollision.globalIndex()) { + registryOther.fill(HIST("hMatchedEventQA"), 7.f); + continue; + } + if (!mc.isPhysicalPrimary()) { + continue; + } + const int pdg = mc.pdgCode(); + const bool isLambda = pdg == 3122 || pdg == -3122; + const bool isProton = pdg == 2212 || pdg == -2212; + if (!isLambda && !isProton) { + continue; + } + if (isLambda) { + // Count primary Lambdas before acceptance, in accepted events. + registryOther.fill(HIST("hGenLambdaDaughters"), + (pdg == 3122 ? 1.f : 3.f) + (mc.has_daughters() ? 0.f : 1.f)); + if (!mc.has_daughters()) { + continue; + } + } + auto p = makeMcInfo(mc, isLambda ? kMassLambda : kMassProton); + // Inclusive acceptance edges match reconstructed selection, not the efficiency helpers. + const float minPt = isLambda ? lambdaV0MinPt.value : pProtonMinPt.value; + const float maxPt = isLambda ? lambdaV0MaxPt.value : pProtonMaxPt.value; + const float maxY = isLambda ? lambdaV0MaxY.value : pProtonMaxY.value; + if (p.pt < minPt || p.pt > maxPt || std::abs(p.y) > maxY) { + continue; + } + // No decay-channel/daughter-acceptance requirement and no proton daughter veto. + if (pdg == 3122) { + lambdas.push_back(p); + } else if (pdg == -3122) { + antiLambdas.push_back(p); + } else if (pdg == 2212) { + protons.push_back(p); + } else { + antiProtons.push_back(p); + } + } + for (auto const& p : lambdas) { + registryRho.fill(HIST("h2_rho1_Lambda_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_Lambda_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelLambda"), p.pt); + state.hPtSelLambda_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelLambda"), static_cast(lambdas.size())); + for (auto const& p : antiLambdas) { + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_AntiLambda_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelAntiLambda"), p.pt); + state.hPtSelAntiLambda_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelAntiLambda"), static_cast(antiLambdas.size())); + for (auto const& p : protons) { + registryRho.fill(HIST("h2_rho1_Proton_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_Proton_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelectedPrimProton"), p.pt); + state.hPtSelectedPrimProton_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelectedPrimProtons"), static_cast(protons.size())); + for (auto const& p : antiProtons) { + registryRho.fill(HIST("h2_rho1_AntiProton_y"), p.y, p.phi, 1.f); + if (cs) { + cs->rho1_AntiProton_y->Fill(p.y, p.phi, 1.f); + } + registryOther.fill(HIST("hPtSelectedPrimAntiProton"), p.pt); + state.hPtSelectedPrimAntiProton_AllPt->Fill(p.pt); + } + registryOther.fill(HIST("hNSelectedPrimAntiProtons"), static_cast(antiProtons.size())); + const auto nLp = fillGeneratedPairs(lambdas, protons, false, + state.hRho2_Lp_y_AllPt, cs ? cs->Lp_y : nullptr); + registryOther.fill(HIST("hNPairs_Lambda_PrimProton"), static_cast(nLp)); + const auto nLAp = fillGeneratedPairs(lambdas, antiProtons, false, + state.hRho2_LAp_y_AllPt, cs ? cs->LAp_y : nullptr); + registryOther.fill(HIST("hNPairs_Lambda_PrimAntiProton"), static_cast(nLAp)); + const auto nALp = fillGeneratedPairs(antiLambdas, protons, false, + state.hRho2_ALp_y_AllPt, cs ? cs->ALp_y : nullptr); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nALp)); + const auto nALAp = fillGeneratedPairs(antiLambdas, antiProtons, false, + state.hRho2_ALAp_y_AllPt, cs ? cs->ALAp_y : nullptr); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nALAp)); + fillGeneratedPairs(protons, protons, true, + state.hRho2_pp_y_AllPt, cs ? cs->pp_y : nullptr); + fillGeneratedPairs(protons, antiProtons, false, + state.hRho2_pAp_y_AllPt, cs ? cs->pAp_y : nullptr); + fillGeneratedPairs(antiProtons, protons, false, + state.hRho2_App_y_AllPt, cs ? cs->App_y : nullptr); + fillGeneratedPairs(antiProtons, antiProtons, true, + state.hRho2_ApAp_y_AllPt, cs ? cs->ApAp_y : nullptr); + fillGeneratedPairs(lambdas, lambdas, true, + state.hRho2_LL_y_AllPt, cs ? cs->LL_y : nullptr); + fillGeneratedPairs(lambdas, antiLambdas, false, + state.hRho2_LAL_y_AllPt, cs ? cs->LAL_y : nullptr); + fillGeneratedPairs(antiLambdas, lambdas, false, + state.hRho2_ALL_y_AllPt, cs ? cs->ALL_y : nullptr); + fillGeneratedPairs(antiLambdas, antiLambdas, true, + state.hRho2_ALAL_y_AllPt, cs ? cs->ALAL_y : nullptr); + }); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCGenBF, + "Generated correlations in matched selected MC events", false); + + // Book only for the exclusive composition mode; reuse Other_Hists. + void bookMCComposition() + { + // Identical axes to the efficiency maps: reconstructed centrality, pT, rapidity. + // Equal binning does not imply equal selections or establish valid closure. + const AxisSpec axCent(cCentBins, "reconstructed centrality (%)"); + const AxisSpec axPt{60, 0.0, 6.0, "reconstructed p_{T} (GeV/c)"}; + const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "reconstructed y"}; + // First/last bins are reserved for PDG codes below -10000 / above +10000. + const AxisSpec axMother{20003, -10001.5, 10001.5, "signed direct-mother PDG (out-of-range codes mapped to endpoints)"}; + const std::array categories = {"AllCandidates", "PrimarySignal", "SecondarySignal", + "WrongSpeciesOrSign", "Unmatched", "OtherMCCollision"}; + for (const char* species : {"Lambda", "AntiLambda", "Proton", "AntiProton"}) { + const std::string base = std::string("MCComposition/") + species + "/"; + for (const char* category : categories) { + const std::string name = base + "h3_" + category; + registryOther.add(name.c_str(), name.c_str(), {HistType::kTH3F, {axCent, axPt, axY}}); + } + auto totals = registryOther.add((base + "hCategoryTotals").c_str(), + "Selected reconstructed candidates;Category;Candidates", + {HistType::kTH1F, {{6, 0.5, 6.5}}}); + for (int i = 0; i < 6; ++i) { + totals->GetXaxis()->SetBinLabel(i + 1, categories[i]); + } + for (const char* signalClass : {"PrimarySignal", "SecondarySignal"}) { + const std::string name = base + "hMotherPDG_" + signalClass; + registryOther.add(name.c_str(), "Direct-mother associations (not exclusive candidate sources)", + {HistType::kTHnSparseF, {axMother, axPt, axCent}}); + auto multiplicity = registryOther.add((base + "hMotherMultiplicity_" + signalClass).c_str(), + "Valid recorded direct mothers per signal candidate;Mothers;Candidates", + {HistType::kTH1F, {{3, -0.5, 2.5}}}); + multiplicity->GetXaxis()->SetBinLabel(1, "0"); + multiplicity->GetXaxis()->SetBinLabel(2, "1"); + multiplicity->GetXaxis()->SetBinLabel(3, "2 or more"); + } + } + for (const char* species : {"Proton", "AntiProton"}) { + const std::string name = std::string("MCComposition/") + species + "/hMcMask"; + registryOther.add(name.c_str(), "Selected track MC mask (diagnostic only);mcMask;Candidates", + {HistType::kTH1F, {{65536, -0.5, 65535.5}}}); + } + registryOther.add("MCComposition/hAcceptedEvents", "Accepted composition events, including zero candidates;Events;Counts", + {HistType::kTH1F, {{1, 0.0, 1.0}}}); + // Store raw counts only, for merging and later bin-by-bin fractions: + // fSecondaryCandidates = Secondary / All + // fSecondarySignal = Secondary / (Primary + Secondary) + // fPrimaryCandidates = Primary / All + // fSignal = (Primary + Secondary) / All + // These denominators are not interchangeable; no fractions are calculated here. + } + + template + void fillCompositionCategory(TPrefix prefix, int category, float cent, float pt, float y) + { + // Bins 2..6 match the five exclusive categories in hCategoryTotals. + registryOther.fill(prefix + HIST("hCategoryTotals"), static_cast(category + 1)); + switch (category) { + case 1: + registryOther.fill(prefix + HIST("h3_PrimarySignal"), cent, pt, y); + break; + case 2: + registryOther.fill(prefix + HIST("h3_SecondarySignal"), cent, pt, y); + break; + case 3: + registryOther.fill(prefix + HIST("h3_WrongSpeciesOrSign"), cent, pt, y); + break; + case 4: + registryOther.fill(prefix + HIST("h3_Unmatched"), cent, pt, y); + break; + case 5: + registryOther.fill(prefix + HIST("h3_OtherMCCollision"), cent, pt, y); + break; + default: + break; + } + } + + static bool usableMCIndex(aod::McParticles const* table, int64_t id) + { + // has_mcParticle() tests only id >= 0 in ASoA; also check the bound table range. + return table != nullptr && id >= 0 && id >= static_cast(table->offset()) && + id - static_cast(table->offset()) < table->size(); + } + + template + void fillCompositionMothers(TPrefix prefix, TMc const& mc, bool primary, float pt, float cent) + { + int nMothers = 0; + // MothersIds is the actual direct-mother array in AnalysisDataModel.h. + // mothers_as() dereferences every stored index without range checks. + // Read mothersIds() and its bound table instead, validating each index first. + const auto* mothers = static_cast(mc) + .getCurrentRaw() + .template get(); + if (mc.has_mothers() && mothers) { + for (const auto motherId : mc.mothersIds()) { + if (!usableMCIndex(mothers, motherId)) { + continue; + } + const auto mother = mothers->rawIteratorAt(motherId - static_cast(mothers->offset())); + const int pdg = mother.pdgCode(); + const int recordedPDG = pdg < -10000 ? -10001 : pdg > 10000 ? 10001 + : pdg; + if (primary) { + registryOther.fill(prefix + HIST("hMotherPDG_PrimarySignal"), recordedPDG, pt, cent); + } else { + registryOther.fill(prefix + HIST("hMotherPDG_SecondarySignal"), recordedPDG, pt, cent); + } + ++nMothers; + } + } + // Count mother associations, not exclusive candidate sources: totals can exceed + // particle counts. No recorded mother does not imply primary; having a mother + // does not imply secondary. Only isPhysicalPrimary() defines that classification. + if (primary) { + registryOther.fill(prefix + HIST("hMotherMultiplicity_PrimarySignal"), std::min(nMothers, 2)); + } else { + registryOther.fill(prefix + HIST("hMotherMultiplicity_SecondarySignal"), std::min(nMothers, 2)); + } + } + + template + void fillMCCompositionCandidate(TPrefix prefix, TCandidate const& candidate, + int expectedPDG, int64_t mcCollisionId, + float cent, float pt, float y) + { + // Count candidates as selected, without deduplicating by truth ID. Ordinary + // TH3 under/overflow is retained; no extra acceptance or rho-grid cut is applied. + registryOther.fill(prefix + HIST("h3_AllCandidates"), cent, pt, y); + registryOther.fill(prefix + HIST("hCategoryTotals"), 1.f); + if (!candidate.has_mcParticle()) { + fillCompositionCategory(prefix, 4, cent, pt, y); + return; + } + // The label column binds the full truth table, whereas the process argument + // can be sliced to the current MC collision. Use the binding so a valid label + // pointing to another collision is OtherMCCollision rather than Unmatched. + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + fillCompositionCategory(prefix, 4, cent, pt, y); + return; + } + const auto mc = truth->rawIteratorAt(id - static_cast(truth->offset())); + if (mc.mcCollisionId() != mcCollisionId) { + fillCompositionCategory(prefix, 5, cent, pt, y); + return; + } + if (mc.pdgCode() != expectedPDG) { + fillCompositionCategory(prefix, 3, cent, pt, y); + return; + } + const bool primary = mc.isPhysicalPrimary(); + // SecondarySignal means correctly identified and non-primary, not material-produced. + fillCompositionCategory(prefix, primary ? 1 : 2, cent, pt, y); + fillCompositionMothers(prefix, mc, primary, pt, cent); + // One All entry and exactly one exclusive category entry per selected candidate: + // All = Primary + Secondary + WrongSpeciesOrSign + Unmatched + OtherMCCollision. + } + + void processMCComposition(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& /*mcParticles*/) + { + // The McParticles input supplies framework label bindings; no truth filtering + // enters either shared reconstructed selection. All fills have unit weight. + withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollision, + [&](auto const& collision, MyTracksMC const& tracks, float cent, CentRho2Set*) { + registryOther.fill(HIST("MCComposition/hAcceptedEvents"), 0.5f); + fillRunConfig(); + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + std::unordered_set v0DaughterTrackIds; + v0DaughterTrackIds.reserve(64); + std::vector lambdas, antiLambdas; + std::vector protons, antiProtons; + selectRecoV0s(collision, V0s, lambdas, antiLambdas, v0DaughterTrackIds); + // Reuse the pre-cleaning daughter set, including rejected shared-daughter V0s. + selectRecoProtons(tracks, v0DaughterTrackIds, protons, antiProtons); + for (const auto& v0 : lambdas) { + fillMCCompositionCandidate(HIST("MCComposition/Lambda/"), + v0, 3122, mcCollision.globalIndex(), cent, v0.pt(), v0.rapidity(1)); + } + for (const auto& v0 : antiLambdas) { + fillMCCompositionCandidate(HIST("MCComposition/AntiLambda/"), + v0, -3122, mcCollision.globalIndex(), cent, v0.pt(), v0.rapidity(2)); + } + // McTrackLabels::mcMask() is uint16_t: set bits indicate mismatches; + // bits 0..6 ITS layers, 12 ITSAB tracklet, 13 ITS-TPC, 14 noise, 15 fake. + // Record the complete mask for every selected track, never use it as a cut. + for (const auto& trk : protons) { + registryOther.fill(HIST("MCComposition/Proton/hMcMask"), trk.mcMask()); + fillMCCompositionCandidate(HIST("MCComposition/Proton/"), + trk, 2212, mcCollision.globalIndex(), cent, trk.pt(), protonRapidity(trk)); + } + for (const auto& trk : antiProtons) { + registryOther.fill(HIST("MCComposition/AntiProton/hMcMask"), trk.mcMask()); + fillMCCompositionCandidate(HIST("MCComposition/AntiProton/"), + trk, -2212, mcCollision.globalIndex(), cent, trk.pt(), protonRapidity(trk)); + } + }); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCComposition, + "Reconstructed MC candidate composition and mother origins", false); + // ── Run-level summary printed once at the end of the analysis job ───── // Called automatically by the O2 framework after all events are processed. // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. void endOfStream(EndOfStreamContext const&) { + if (doprocessMCComposition.value) { + return; // Composition persists raw counts only; no BF veto-fraction summary. + } const int64_t totalBefore = state.runTotalProtonBeforeVeto; const int64_t totalRemoved = state.runTotalProtonRemovedVeto; const int64_t totalAfter = totalBefore - totalRemoved; From 7558e7e6a45cf65f21acbc0ad6b592f5caf0b446 Mon Sep 17 00:00:00 2001 From: Yash Patley <52608802+yashpatley@users.noreply.github.com> Date: Thu, 5 Nov 2026 00:03:05 +0530 Subject: [PATCH 565/648] [PWGCF] Lambda R2 correlation update on close lambda pairs (#18585) --- .../Tasks/lambdaR2Correlation.cxx | 289 ++++++++++-------- 1 file changed, 156 insertions(+), 133 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index cb608859637..4a5bf4ce66a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -128,11 +128,15 @@ namespace lambdatrackext DECLARE_SOA_COLUMN(LambdaSharingDaughter, lambdaSharingDaughter, bool); DECLARE_SOA_COLUMN(LambdaSharingDauIds, lambdaSharingDauIds, std::vector); DECLARE_SOA_COLUMN(TrueLambdaFlag, trueLambdaFlag, bool); +DECLARE_SOA_COLUMN(CloseLambdaTrackIds, closeLambdaTrackIds, std::vector); +DECLARE_SOA_COLUMN(CloseLambdaFlag, closeLambdaFlag, bool); } // namespace lambdatrackext DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", lambdatrackext::LambdaSharingDaughter, lambdatrackext::LambdaSharingDauIds, - lambdatrackext::TrueLambdaFlag); + lambdatrackext::TrueLambdaFlag, + lambdatrackext::CloseLambdaTrackIds, + lambdatrackext::CloseLambdaFlag); using LambdaTrackExt = LambdaTracksExt::iterator; @@ -160,6 +164,11 @@ using LambdaMcGenTrack = LambdaMcGenTracks::iterator; enum CollisionLabels { kTotColBeforeHasMcCollision = 1, kTotCol, + kPassVzCut, + kPassSel8, + kPassPileUpRej, + kPassGoodZVtx, + kPassGoodITSLayerAll, kPassSelCol }; @@ -182,7 +191,8 @@ enum EffCorrType { enum CentEstType { kCentFT0M = 0, - kCentFT0C + kCentFT0C, + kCentFV0A }; enum ParticleType { @@ -222,7 +232,7 @@ struct LambdaTableProducer { ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Centrality Bins"}; // Collisions - Configurable cCentEstimator{"cCentEstimator", 1, "Centrality Estimator : 0-FT0M, 1-FT0C"}; + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator : 0-FT0M, 1-FT0C, 2-FV0A"}; Configurable cZVtxCut{"cZVtxCut", 10.0, "Z-Vtx cut"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; @@ -314,7 +324,7 @@ struct LambdaTableProducer { ccdb->setCreatedNotAfter(nolaterthan.value); // Initialize axis specifications - const AxisSpec axisCols(5, 0.5, 5.5, ""); + const AxisSpec axisCols(10, 0.5, 10.5, ""); const AxisSpec axisTrks(15, 0.5, 15.5, ""); const AxisSpec axisFwdMult(cFwdMult, 0, cFwdMult, "N_{ch}^{FWD}"); const AxisSpec axisEffChecks(3, 0, 3, ""); @@ -329,6 +339,7 @@ struct LambdaTableProducer { const AxisSpec axisV0Rap(48, -1.2, 1.2, "y"); const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); const AxisSpec axisV0Phi(36, 0., TwoPI, "#phi (rad)"); + const AxisSpec axisK0SMass(200, 0.4, 0.6, "M_{#pi#pi} (GeV/#it{c}^{2})"); const AxisSpec axisRadius(5000, 0, 5, "r(cm)"); const AxisSpec axisCosPA(100, 0.99, 1.0, "cos(#theta_{PA})"); @@ -358,6 +369,8 @@ struct LambdaTableProducer { histos.add("Tracks/h1f_effcorr_info", "Efficiency correction info", kTH1F, {axisEffChecks}); histos.add("Tracks/h2f_armpod_before_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); histos.add("Tracks/h2f_armpod_after_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h1f_mass_k0s", "M_{#pi#pi}", kTH1F, {axisK0SMass}); + histos.add("Tracks/h1f_mass_rejk0s", "M_{#pi#pi}", kTH1F, {axisK0SMass}); // QA Lambda histos.add("QA/Lambda/h3f_centmasspt", "Invariant Mass", kTH3F, {axisCent, axisV0Pt, axisV0Mass}); @@ -379,14 +392,14 @@ struct LambdaTableProducer { histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisTrackNsigma}); // Dca analysis histogram - histos.add("QA/Lambda/DCA/h3f_Dca_vs_pT_cent", "DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Dca_vs_pT_cent", "DCA", kTH2F, {axisV0Pt, axisDcaLambda}); // MC Generated Histograms if (doprocessMCRecoGen || doprocessMCReco) { // DCA analysis - histos.add("QA/Lambda/DCA/h3f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); - histos.add("QA/Lambda/DCA/h3f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); - histos.add("QA/Lambda/DCA/h3f_Mat_Dca_vs_pT_cent", "Material DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH2F, {axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH2F, {axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h2f_Mat_Dca_vs_pT_cent", "Material DCA", kTH2F, {axisV0Pt, axisDcaLambda}); // McGen Histos histos.add("McGen/h1f_collision_recgen", "# of Reco Collision Associated to One Mc Generator Collision", kTH1F, {axisMult}); @@ -411,6 +424,13 @@ struct LambdaTableProducer { // Set bin labels histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassVzCut, "kPassVzCut"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSel8, "kPassSel8"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassPileUpRej, "kPassPileUpRej"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassGoodZVtx, "kPassGoodZVtx"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassGoodITSLayerAll, "kPassGoodITSLayerAll"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); + histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllV0Tracks, "kAllV0Tracks"); histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassK0ShortMassRej, "kPassK0ShortMassRej"); @@ -486,33 +506,40 @@ struct LambdaTableProducer { if (std::abs(col.posZ()) >= cZVtxCut) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassVzCut); // Run 3 Min-Bias Trigger if (cSel8Trig && !col.sel8()) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassSel8); // Centrality estimator if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); } else if (cCentEstimator == kCentFT0C) { cent = col.centFT0C(); + } else if (cCentEstimator == kCentFV0A) { + cent = col.centFV0A(); } // Pileup rejection if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassPileUpRej); // Zvtx from FT0 time difference if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassGoodZVtx); // Hit all layers in ITS if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { return false; } + histos.fill(HIST("Events/h1f_collisions_info"), kPassGoodITSLayerAll); return true; } @@ -607,6 +634,7 @@ struct LambdaTableProducer { } histos.fill(HIST("Tracks/h1f_lambda_info"), kPassV0TopoSel); + histos.fill(HIST("Tracks/h1f_mass_k0s"), v0.mK0Short()); // K0s mass rejection if (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow) { @@ -714,11 +742,7 @@ struct LambdaTableProducer { fwdMult = multFT0M; // Minimum bias trigger (1 track in A side, 1 track in C side, 1 track in central barrel) - if (multFT0A > 0 && multFT0C > 0 && multTPC > 0) { - return true; - } - - return false; + return (multFT0A > 0 && multFT0C > 0 && multTPC > 0); } // MC generator centrality @@ -745,6 +769,7 @@ struct LambdaTableProducer { // Decay length float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + histos.fill(HIST("Tracks/h1f_mass_rejk0s"), v0.mK0Short()); histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), v0.qtarm()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), v0.dcaV0daughters()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_pos_to_PV"), v0.dcapostopv()); @@ -768,17 +793,17 @@ struct LambdaTableProducer { void getDcaHist(T const& track, float const& dca) { // Fill histograms - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Dca_vs_pT_cent"), track.pt(), dca); if constexpr (dmc == kMC) { auto mcpart = track.mcParticle(); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Prm_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Prm_Dca_vs_pT_cent"), track.pt(), dca); } else { if (mcpart.getProcess() == cWeakDecayProcess) { // Weak decay - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Scd_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Scd_Dca_vs_pT_cent"), track.pt(), dca); } else { // Material - histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Mat_Dca_vs_pT_cent"), cent, track.pt(), dca); + histos.fill(HIST(SubDir[part]) + HIST("DCA/h2f_Mat_Dca_vs_pT_cent"), track.pt(), dca); } } } @@ -871,7 +896,7 @@ struct LambdaTableProducer { // Fill Lambda/AntiLambda Table lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.pt(), v0.eta(), v0.phi(), v0.yLambda(), v0.px(), v0.py(), v0.pz(), lambdaMass, v0.template posTrack_as().index(), v0.template negTrack_as().index(), posTrackKin, negTrackKin, - (int8_t)partType, lambdaCorrFact); + static_cast(partType), lambdaCorrFact); } } @@ -926,7 +951,7 @@ struct LambdaTableProducer { } // Fill table lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), - daughterIDs[0], daughterIDs[1], (int8_t)partType, 1.); + daughterIDs[0], daughterIDs[1], static_cast(partType), 1.); } } @@ -957,7 +982,7 @@ struct LambdaTableProducer { // BC, Collision, tracks and V0s using BCsRun3 = soa::Join; - using Collisions = soa::Join; + using Collisions = soa::Join; using Tracks = soa::Join; using TracksMC = soa::Join; using McV0Tracks = soa::Join; @@ -1038,7 +1063,7 @@ struct LambdaTableProducer { // Fill table lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), - -1, -1, (int8_t)partType, 1.); + -1, -1, static_cast(partType), 1.); } } @@ -1052,10 +1077,16 @@ struct LambdaTracksExtProducer { // Configurables Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all Lambda"}; Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject all Lambda sharing daughters"}; + Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; + Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Objects + float magField = 0.; + std::array tpcRadii = {0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4}; + void init(InitContext const&) { // Axis Specifications @@ -1073,18 +1104,69 @@ struct LambdaTracksExtProducer { histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{SharePair}", kTH2D, {axisDEta, axisDPhi}); } + template + bool checkClosePair(A const& v1, A const& v2, int charge1, int charge2) + { + // DPhiStar average over different TPC radius + float dphistar = 0., n = 0.; + for (auto const& radius : tpcRadii) { + float arg1 = 0.15 * magField * radius / v1[0]; + float arg2 = 0.15 * magField * radius / v2[0]; + if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { + dphistar += v1[2] - v2[2] - (charge1 * std::abs(std::asin(arg1))) + (charge2 * std::abs(std::asin(arg2))); + ++n; + } + } + + // Inf check + if (n == 0) { + return false; + } + + // DPhiStar + dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); + + // DEta + float deta = v1[1] - v2[1]; + + // Return + return (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); + } + + template + bool isClosePair(V const& l1, V const& l2) + { + // Assign kinematics + std::array posTrackKinLambda1 = {l1.posTrackKin()[0], l1.posTrackKin()[1], l1.posTrackKin()[2]}; + std::array negTrackKinLambda1 = {l1.negTrackKin()[0], l1.negTrackKin()[1], l1.negTrackKin()[2]}; + std::array posTrackKinLambda2 = {l2.posTrackKin()[0], l2.posTrackKin()[1], l2.posTrackKin()[2]}; + std::array negTrackKinLambda2 = {l2.negTrackKin()[0], l2.negTrackKin()[1], l2.negTrackKin()[2]}; + + // Check close pair + bool retFlag = checkClosePair(posTrackKinLambda1, posTrackKinLambda2, 1, 1) && checkClosePair(negTrackKinLambda1, negTrackKinLambda2, -1, -1); + + // Pair reject + return retFlag; + } + void processDummy(aod::LambdaCollisions::iterator const&) {} PROCESS_SWITCH(LambdaTracksExtProducer, processDummy, "Dummy Process", true); - void processLambdaTrackExt(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) + void processLambdaTrackExt(aod::LambdaCollisions::iterator const& collision, aod::LambdaTracks const& tracks) { int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; + magField = collision.bField(); for (auto const& lambda : tracks) { + // Shared daughter objects bool lambdaSharingDauFlag = false, trueLambdaFlag = false; std::vector vSharedDauLambdaIndex; + // Index of lambda track close to current lambda + bool isCloseLambdaFlag = false; + std::vector vCloseLambdaTrackIndex; + if (lambda.partType() == kLambda) { ++nTotLambda; } else if (lambda.partType() == kAntiLambda) { @@ -1092,12 +1174,12 @@ struct LambdaTracksExtProducer { } for (auto const& track : tracks) { - // check lambda index (don't analyze same lambda track !!!) + // Check lambda index (don't analyze same lambda track !!!) if (lambda.index() == track.index()) { continue; } - // check if lambda shares daughters with any other track + // Check if lambda shares daughters with any other lambda track if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; @@ -1111,6 +1193,13 @@ struct LambdaTracksExtProducer { histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } } + + // Check if this lambda is close to lambda track + // Keep the index of this lambda track + if (isClosePair(lambda, track)) { + isCloseLambdaFlag = true; + vCloseLambdaTrackIndex.push_back(track.index()); + } } // Accept / Reject @@ -1126,7 +1215,7 @@ struct LambdaTracksExtProducer { } // Fill LambdaTrackExt table - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag); + lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag, vCloseLambdaTrackIndex, isCloseLambdaFlag); } // fill multiplicity histograms @@ -1165,14 +1254,8 @@ struct LambdaR2Correlation { Configurable cAnaPairs{"cAnaPairs", false, "Analyze Pairs Flag"}; Configurable cAnaEff{"cAnaEff", false, "Analyze efficiency flag"}; - // Lambda Kaon femtoscopic correction - Configurable cApplyFemtoSel{"cApplyFemtoSel", false, "Femto qinv selection"}; - Configurable cFemtoCut{"cFemtoCut", 0.1, "Femto qinv cut"}; - - // Lambda Kaon two-track cuts - Configurable cApplyTwoTrackCut{"cApplyTwoTrackCut", false, "Flag for two track cut"}; - Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; - Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; + // Lambda close pair rejection + Configurable cRejCloseLambda{"cRejCloseLambda", false, "Flag close Lambda rej"}; // Centrality Axis ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Mult-Bins"}; @@ -1192,8 +1275,10 @@ struct LambdaR2Correlation { float phibinwidth = 0.; float q = 0., e = 0., qinv = 0.; float cent = 0., posZ = 0.; - float magField = 0.; - std::array tpcRadii = {0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4}; + + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + static constexpr auto SubDirHist = std::array{"LaP", "LaM"}; + static constexpr auto SubDirPairHist = std::array{"LaP_LaM", "LaP_LaP", "LaM_LaM"}; void init(InitContext const&) { @@ -1228,11 +1313,17 @@ struct LambdaR2Correlation { histos.add("Event/Reco/h1f_collision_posz", "V_{Z} Distribution", kTH1F, {axisPosZ}); histos.add("Event/Reco/h1f_ft0m_mult_percentile", "FT0M (%)", kTH1F, {axisCent}); - // Two track cut - histos.add("QA/TwoTrackCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/TwoTrackCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/FemtoCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); - histos.add("QA/FemtoCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + // Close pair checks + histos.add("QA/CloseLambda/Before/h2d_n1_centpt_LaP", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/Before/h2d_n1_centpt_LaM", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaP_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaP_LaP", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/Before/h2d_n2_detadphi_LaM_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n1_centpt_LaP", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/After/h2d_n1_centpt_LaM", "#rho_{1}", kTH2D, {axisCent, axisPtLambda}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaP_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaP_LaP", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/CloseLambda/After/h2d_n2_detadphi_LaM_LaM", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); // Efficiency Histograms // Single Particle Efficiencies @@ -1270,86 +1361,9 @@ struct LambdaR2Correlation { } } - template - bool checkClosePair(A const& v1, A const& v2, int charge1, int charge2) - { - // DPhiStar average over different TPC radius - float dphistar = 0., n = 0.; - for (auto const& radius : tpcRadii) { - float arg1 = 0.15 * magField * radius / v1[0]; - float arg2 = 0.15 * magField * radius / v2[0]; - if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { - dphistar += v1[2] - v2[2] - (charge1 * std::abs(std::asin(arg1))) + (charge2 * std::abs(std::asin(arg2))); - ++n; - } - } - - // Inf check - if (n == 0) { - return false; - } - - // DPhiStar - dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); - - // DEta - float deta = v1[1] - v2[1]; - - // Return - return (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); - } - - template - bool isClosePair(V const& l1, V const& l2) - { - // Before - histos.fill(HIST("QA/TwoTrackCut/Before/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); - - // Assign kinematics - std::array posTrackKinLambda1 = {l1.posTrackKin()[0], l1.posTrackKin()[1], l1.posTrackKin()[2]}; - std::array negTrackKinLambda1 = {l1.negTrackKin()[0], l1.negTrackKin()[1], l1.negTrackKin()[2]}; - std::array posTrackKinLambda2 = {l2.posTrackKin()[0], l2.posTrackKin()[1], l2.posTrackKin()[2]}; - std::array negTrackKinLambda2 = {l2.negTrackKin()[0], l2.negTrackKin()[1], l2.negTrackKin()[2]}; - - // Check close pair - bool retFlag = checkClosePair(posTrackKinLambda1, posTrackKinLambda2, 1, 1) && checkClosePair(negTrackKinLambda1, negTrackKinLambda2, -1, -1); - - // Fill QA - if (!retFlag) { // Pair accept - histos.fill(HIST("QA/TwoTrackCut/After/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); - } - - // Pair reject - return retFlag; - } - - template - bool isCloseQinv(V const& p1, T const& p2) - { - // Before - histos.fill(HIST("QA/FemtoCut/Before/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); - - // Calculate qinv - q = RecoDecay::p((p1.px() - p2.px()), (p1.py() - p2.py()), (p1.pz() - p2.pz())); - e = RecoDecay::e(p1.px(), p1.py(), p1.pz(), MassLambda0) - RecoDecay::e(p2.px(), p2.py(), p2.pz(), MassKaonCharged); - qinv = std::sqrt(-RecoDecay::m2(q, e)); - - // Pair accept - if (qinv > cFemtoCut) { - histos.fill(HIST("QA/FemtoCut/After/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); - return false; - } - - // Pair reject - return true; - } - template void fillPairHistos(T1& p1, T2& p2) { - static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; - static constexpr auto SubDirHist = std::array{"LaP_LaM", "LaP_LaP", "LaM_LaM"}; - const auto rapbin1 = static_cast((p1.rap() - kminrap) / rapbinwidth); const auto rapbin2 = static_cast((p2.rap() - kminrap) / rapbinwidth); @@ -1363,17 +1377,25 @@ struct LambdaR2Correlation { const int rapphix = rapbin1 * nphibins + phibin1; const int rapphiy = rapbin2 * nphibins + phibin2; - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_") + HIST(SubDirHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_") + HIST(SubDirPairHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); } } template void analyzeSingles(T const& tracks) { - static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; - static constexpr auto SubDirHist = std::array{"LaP", "LaM"}; - for (auto const& track : tracks) { + if constexpr (rec_gen == kRec) { + // Close pair lambda + histos.fill(HIST("QA/CloseLambda/Before/h2d_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); + if (track.closeLambdaFlag()) { + histos.fill(HIST("QA/CloseLambda/After/h2d_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); + if (cRejCloseLambda) { + continue; + } + } + } + // Efficiency Plots if (cAnaEff) { histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h2f_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); @@ -1395,19 +1417,21 @@ struct LambdaR2Correlation { { for (auto const& trk_1 : trks_1) { for (auto const& trk_2 : trks_2) { - // check for same index for Lambda-Lambda / AntiLambda-AntiLambda + // Check for same index for Lambda-Lambda / AntiLambda-AntiLambda if (same && ((trk_1.index() == trk_2.index()))) { continue; } - // Lambda close pair rejection + // Close pair if constexpr (rec_gen == kRec) { - // Close pair - if (cApplyTwoTrackCut && isClosePair(trk_1, trk_2)) { - continue; + // Close pair histograms + histos.fill(HIST("QA/CloseLambda/Before/h2d_n2_detadphi_") + HIST(SubDirPairHist[partpair]), trk_1.eta() - trk_2.eta(), RecoDecay::constrainAngle(trk_1.phi() - trk_2.phi(), -PIHalf)); + if (trk_1.closeLambdaFlag() && trk_2.closeLambdaFlag()) { + histos.fill(HIST("QA/CloseLambda/After/h2d_n2_detadphi_") + HIST(SubDirPairHist[partpair]), trk_1.eta() - trk_2.eta(), RecoDecay::constrainAngle(trk_1.phi() - trk_2.phi(), -PIHalf)); } - // Femto selection - if (cApplyFemtoSel && isCloseQinv(trk_1, trk_2)) { + + // Close pair lambda rejection + if (cRejCloseLambda && (trk_1.closeLambdaFlag() || trk_2.closeLambdaFlag())) { continue; } } @@ -1422,8 +1446,8 @@ struct LambdaR2Correlation { using LambdaTracks = soa::Join; SliceCache cache; - Partition partLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true); - Partition partAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true); + Partition partLambdaTracks = (aod::lambdatrack::partType == static_cast(kLambda)) && (aod::lambdatrackext::trueLambdaFlag == true); + Partition partAntiLambdaTracks = (aod::lambdatrack::partType == static_cast(kAntiLambda)) && (aod::lambdatrackext::trueLambdaFlag == true); void processDummy(aod::LambdaCollisions::iterator const&) {} @@ -1436,7 +1460,6 @@ struct LambdaR2Correlation { posZ = collision.posZ(); cent = collision.cent(); - magField = collision.bField(); auto lambdaTracks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); auto antiLambdaTracks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); @@ -1458,8 +1481,8 @@ struct LambdaR2Correlation { using LambdaMcGenTracks = aod::LambdaMcGenTracks; SliceCache cachemc; - Partition partMcLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda); - Partition partMcAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda); + Partition partMcLambdaTracks = (aod::lambdatrack::partType == static_cast(kLambda)); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::partType == static_cast(kAntiLambda)); void processMCGen(LambdaMcGenCollisions::iterator const& mcgencol, LambdaMcGenTracks const&) { From 1f1e9806d3566c2e26162bf5f8fa777251aa8581 Mon Sep 17 00:00:00 2001 From: amatyja Date: Wed, 4 Nov 2026 20:44:21 +0100 Subject: [PATCH 566/648] [PWGUD] Fix type of problemBit variable and cast (#18587) --- PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index 00a0c30e693..37583d9a938 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -1397,7 +1397,7 @@ struct TauThreeProngEventTableProducer { float trueDaugZ[6] = {-998., -998., -998., -998., -998., -998.}; int trueDaugPdgCode[6] = {-999, -999, -999, -999, -999, -999}; - int8_t problemBits[8] = {-99, -99, -99, -99, -99, -99, -99, -99}; + bool problemBits[8] = {false, false, false, false, false, false, false, false}; uint8_t problemBitsFlag = 0; // MyRecoProblem problem = NO_PROBLEM; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_PROBLEM); @@ -1742,10 +1742,10 @@ struct TauThreeProngEventTableProducer { } // end of loop over collisions associated to MC collision // set up problem bits - problemBitsFlag = problemBits[0]; // initialization + problemBitsFlag = static_cast(problemBits[0]); // initialization for (int ibit = 1; ibit < nBitsMax; ibit++) { - problemBitsFlag = (problemBitsFlag << offset); // shift by 1 position towards left - problemBitsFlag += problemBits[ibit]; // add next bit to the pool + problemBitsFlag = (problemBitsFlag << offset); // shift by 1 position towards left + problemBitsFlag += static_cast(problemBits[ibit]); // add next bit to the pool } // decide the channel and set the variable. From d423bdad696e1d39abe0074d29bff76fd4b70c08 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Wed, 4 Nov 2026 21:11:54 +0100 Subject: [PATCH 567/648] [PWGLF] Add twist correction to zdc qvector (#18596) --- .../Common/zdc2stagecalibration.cxx | 78 ++++++++++++++++++- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 6bf3eb98054..7462f157c7f 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -84,6 +84,8 @@ struct zdc2stagecalibration { Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; Configurable useQWidthEqualization{"useQWidthEqualization", false, "equalize Qx/Qy widths after Structured71 recentering"}; Configurable confQWidthPath{"confQWidthPath", "", "CCDB path to post-recentering Q-width map"}; + Configurable useQTwist{"useQTwist", false, "apply same-side Qx-Qy twist correction after width equalization"}; + Configurable confQTwistPath{"confQTwistPath", "", "CCDB path to the run-wise Q-twist map"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, "number of bins per cross coordinate for spatial calibration"}; @@ -103,6 +105,7 @@ struct zdc2stagecalibration { TH3D* spatialprofile = nullptr; TH3D* qRecenteringProfile = nullptr; TH2D* qWidthProfile = nullptr; + TH2D* qTwistProfile = nullptr; static constexpr int kQRecenteringNFeatures = 21; static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; @@ -137,6 +140,9 @@ struct zdc2stagecalibration { if (useQWidthEqualization.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQWidthPath.value.empty())) { LOGF(fatal, "Width equalization requires stage=2, useQRecentering=true, method=4 and confQWidthPath"); } + if (useQTwist.value && (!useQWidthEqualization.value || confQTwistPath.value.empty())) { + LOGF(fatal, "Twist correction requires width equalization and confQTwistPath"); + } const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; AxisSpec towerAxis = {4, 0.0, 4.0, "tower"}; @@ -158,6 +164,7 @@ struct zdc2stagecalibration { AxisSpec vertexXYAxis = {100, -0.1, 0.1, "vertex x/y (cm)"}; AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; + AxisSpec qTwistMomentAxis = {10, 0.0, 10.0, "Q moments (A: 0-4, C: 5-9)"}; AxisSpec qCorrelationComponentAxis = {4, 0.0, 4.0, "A-C correlation component"}; AxisSpec qValueAxis = {200, -4.0, 4.0, "Q"}; @@ -261,6 +268,10 @@ struct zdc2stagecalibration { histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); histos.add("ResolutionQA/pCosPsiAPsiCVsCentralityVz", "ZDC A-C correlation;centrality (%);v_{z} (cm); <#cos(#Psi_{A}-#Psi_{C})>", kTProfile2D, {resolutionCentralityAxis, resolutionVzAxis}); histos.add("QWidthQA/pQAfterWidthEqVsCentrality", "Q after width equalization;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + histos.add("QTwistQA/pMomentsBeforeVsCentrality", "Q moments before twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("QTwistQA/pMomentsAfterVsCentrality", "Q moments after twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("PhiQA/hPhiAfterTwistVsCentralityZNA", "ZNA #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterTwistVsCentralityZNC", "ZNC #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNA", "ZNA #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNC", "ZNC #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); @@ -321,6 +332,7 @@ struct zdc2stagecalibration { spatialprofile = nullptr; qRecenteringProfile = nullptr; qWidthProfile = nullptr; + qTwistProfile = nullptr; if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); if (!gainprofile) { @@ -362,6 +374,18 @@ struct zdc2stagecalibration { } } } + if (calibrationStage.value == 2 && useQTwist.value) { + qTwistProfile = ccdb->getForTimeStamp(confQTwistPath.value, timestamp); + if (!qTwistProfile || qTwistProfile->GetNbinsX() != 80 || qTwistProfile->GetNbinsY() != 6) { + LOGF(fatal, "Missing/invalid ZDC twist map for run %d", runNumber); + } + constexpr std::array labels = {"MxxA", "MxyA", "MyyA", "MxxC", "MxyC", "MyyC"}; + for (int i = 0; i < 6; ++i) { + if (std::string(qTwistProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect ZDC twist map component ordering for run %d", runNumber); + } + } + } } float getTimeFromSOR(uint64_t globalBC) const @@ -1083,11 +1107,59 @@ struct zdc2stagecalibration { } } + const double phiWidthA = std::atan2(qValues[1], qValues[0]); + const double phiWidthC = std::atan2(qValues[3], qValues[2]); + if (useQWidthEqualization.value) { + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNA"), centrality, phiWidthA); + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNC"), centrality, phiWidthC); + } + const auto makeTwistMoments = [](const std::array& q) { + return std::array{q[0], q[1], q[0] * q[0], q[1] * q[1], q[0] * q[1], + q[2], q[3], q[2] * q[2], q[3] * q[3], q[2] * q[3]}; + }; + const auto momentsBeforeTwist = makeTwistMoments(qValues); + if (useQWidthEqualization.value) { + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("QTwistQA/pMomentsBeforeVsCentrality"), centrality, i + 0.5, momentsBeforeTwist[i]); + } + } + if (useQTwist.value) { + const int centBin = qTwistProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qTwistProfile->GetNbinsX()) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + for (int side = 0; side < 2; ++side) { + const int offset = 3 * side; + const double mxx = qTwistProfile->GetBinContent(centBin, offset + 1); + const double mxy = qTwistProfile->GetBinContent(centBin, offset + 2); + const double myy = qTwistProfile->GetBinContent(centBin, offset + 3); + if (!std::isfinite(mxx) || !std::isfinite(mxy) || !std::isfinite(myy) || mxx <= 0.0 || myy <= 0.0 || mxx * myy <= mxy * mxy) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + const int ix = 2 * side; + const double qx = qValues[ix]; + const double qy = qValues[ix + 1]; + qValues[ix] = mxx * qx + mxy * qy; + qValues[ix + 1] = mxy * qx + myy * qy; + } + for (int i = 0; i < 4; ++i) { + if (!std::isfinite(qValues[i]) || !std::isfinite(static_cast(qValues[i]))) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + const auto momentsAfterTwist = makeTwistMoments(qValues); + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("QTwistQA/pMomentsAfterVsCentrality"), centrality, i + 0.5, momentsAfterTwist[i]); + } + } const double phiFinalA = std::atan2(qValues[1], qValues[0]); const double phiFinalC = std::atan2(qValues[3], qValues[2]); - if (useQWidthEqualization.value) { - histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNA"), centrality, phiFinalA); - histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNC"), centrality, phiFinalC); + if (useQTwist.value) { + histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNC"), centrality, phiFinalC); } // ZDC resolution after all enabled corrections const double qMagA = std::hypot(qValues[0], qValues[1]); From 5727b9e8ddc662984defc6ce519e63fd1c90186e Mon Sep 17 00:00:00 2001 From: Gyula Bencedi Date: Wed, 4 Nov 2026 22:56:13 +0100 Subject: [PATCH 568/648] [PWGLF,PWGMM] dndetaMFTPbPb.cxx: Fix collision table in MC DCA checks (#18595) --- PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index a3e9d5ed97a..8eaef0ae1c8 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -524,7 +524,7 @@ struct DndetaMFTPbPb { doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyBestCentFT0C || doprocessMcEfficiencyIdxBestInlusive || doprocessMcEfficiencyIdxBestCentFT0C || doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || - doprocessDataCorrelationwBestTracksInclusive) { + doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ "All", @@ -881,7 +881,6 @@ struct DndetaMFTPbPb { using Colls = soa::Join; using CollsCentFT0C = soa::Join; using CollsGenCentFT0C = soa::Join; - using CollsGenCentFT0CExtra = soa::Join; using CollsCorr = soa::Join; using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; @@ -977,10 +976,10 @@ struct DndetaMFTPbPb { std::unordered_map mapRecToMc; std::unordered_map mapMcToRec; - PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; template - void createMCIds(MC const& mcCollisions, CollsGenCentFT0CExtra const& collisions, P const& particles) + void createMCIds(MC const& mcCollisions, CollsGenCentFT0C const& collisions, P const& particles) { const auto& nMcColls = mcCollisions.size(); LOG(info) << "MC collisions: " << nMcColls; @@ -3245,7 +3244,7 @@ struct DndetaMFTPbPb { PROCESS_SWITCH(DndetaMFTPbPb, processMcSgnEvtLossCentFT0C, "Process Signal/event loss based on MC (in FT0C centrality bins)", false); - void processMcReassocDCA(CollsGenCentFT0CExtra const& collisions, + void processMcReassocDCA(CollsGenCentFT0C const& collisions, CollsMCExtra const& mcCollisions, aod::McParticles const& particles, MftBestTracksLabeled const& besttracks, @@ -3256,7 +3255,7 @@ struct DndetaMFTPbPb { int nNoMC{0}; for (const auto& collision : collisions) { auto crec = getRecoCent(collision); - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 1., crec); if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } @@ -3270,7 +3269,7 @@ struct DndetaMFTPbPb { nNoMC++; continue; } - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); if (gConf.rmSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { continue; } @@ -3278,7 +3277,7 @@ struct DndetaMFTPbPb { if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { continue; } - registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 4., crec); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); for (auto const& atrack : perCollisionASample) { @@ -3287,7 +3286,6 @@ struct DndetaMFTPbPb { } const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { continue; } From e8ac07979f5c110372218433d6cbddc3511dedf9 Mon Sep 17 00:00:00 2001 From: "M. Monalisa Melo Paulino" <71565863+monamelop@users.noreply.github.com> Date: Thu, 5 Nov 2026 10:58:38 +0100 Subject: [PATCH 569/648] [PWGHF] Support generator-only derived data without MC centrality (#18557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vít Kučera <26327373+vkucera@users.noreply.github.com> --- PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx | 3 ++- PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx | 3 ++- PWGHF/Utils/utilsDerivedData.h | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx index ad8e52744e1..212756317ee 100644 --- a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx @@ -105,6 +105,7 @@ struct HfDerivedDataCreatorD0ToKPi { using SelectedCandidatesMcKfMl = soa::Filtered>; using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; + using TypeMcGenCollisions = aod::McCollisions; Filter filterSelectCandidates = aod::hf_sel_candidate_d0::isSelD0 >= 1 || aod::hf_sel_candidate_d0::isSelD0bar >= 1; Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); @@ -533,7 +534,7 @@ struct HfDerivedDataCreatorD0ToKPi { } PROCESS_SWITCH(HfDerivedDataCreatorD0ToKPi, processMcWithKFParticleMlAll, "Process MC with KFParticle and ML", false); - void processMcGenOnly(TypeMcCollisions const& mcCollisions, + void processMcGenOnly(TypeMcGenCollisions const& mcCollisions, MatchedGenCandidatesMc const& mcParticles) { rowsCommon.processMcParticles(mcCollisions, mcParticlesPerMcCollision, mcParticles, Mass); diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index c30f037d976..8d7a2fd1b63 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -100,6 +100,7 @@ struct HfDerivedDataCreatorDsToKKPi { using SelectedCandidatesMcMl = soa::Filtered>; using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; + using TypeMcGenCollisions = aod::McCollisions; Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0 || (aod::hf_sel_candidate_ds::isSelDsToPiKK & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); @@ -411,7 +412,7 @@ struct HfDerivedDataCreatorDsToKKPi { } PROCESS_SWITCH(HfDerivedDataCreatorDsToKKPi, processMcMlAll, "Process MC with ML", false); - void processMcGenOnly(TypeMcCollisions const& mcCollisions, + void processMcGenOnly(TypeMcGenCollisions const& mcCollisions, MatchedGenCandidatesMc const& mcParticles) { rowsCommon.processMcParticles(mcCollisions, mcParticlesPerMcCollision, mcParticles, Mass); diff --git a/PWGHF/Utils/utilsDerivedData.h b/PWGHF/Utils/utilsDerivedData.h index f2b63291665..12238d7eb65 100644 --- a/PWGHF/Utils/utilsDerivedData.h +++ b/PWGHF/Utils/utilsDerivedData.h @@ -168,11 +168,16 @@ struct HfProducesDerivedData : o2::framework::ProducesGroup { void fillTablesMcCollision(TMcCollision const& mcCollision) { if (conf->fillMcCollBase.value) { + float centFT0M{-1.f}; // Input without centrality: e.g. generator-only + if constexpr (requires { mcCollision.centFT0M(); }) { + // MC general: input includes centrality + centFT0M = mcCollision.centFT0M(); + } rowMcCollBase( mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), - mcCollision.centFT0M()); + centFT0M); } if (conf->fillMcCollId.value) { rowMcCollId( From 586e49cda3d8761d1e7fa417d20b5de6add73f1b Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Thu, 5 Nov 2026 13:50:15 +0100 Subject: [PATCH 570/648] [PWGDQ] add track information in derived alignment tables (#18601) --- PWGDQ/Tasks/muonGlobalAlignment.cxx | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index 650aad95359..e8a85c3555c 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -172,6 +172,10 @@ DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions DECLARE_SOA_SELF_INDEX_COLUMN_FULL(MAMCHTrack, matchMCHTrack, int, "MAFWDTRACKs_MatchMCHTrack"); // o2-linter: disable=name/o2-column (to match naming in official tables) // ID of matching MFT track for GlobalMuonTracks and GlobalForwardTracks DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(MAMFTTrack, matchMFTTrack, int32_t, MAMFTTracks, "MAMFTTRKs", ""); // o2-linter: disable=name/o2-column (to match naming in official tables) +// Track time information +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! Bunch crossing number (globally unique in this run) +DECLARE_SOA_COLUMN(TrackTime, trackTime, float); //! Estimated time of the track in ns wrt collision().bc() or ambiguoustrack.bcSlice()[0] +DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); //! Resolution of the track time in ns } // namespace muonalignfwdtrk namespace o2::aod @@ -185,6 +189,8 @@ DECLARE_SOA_TABLE(StoredMAFwdTracks, "AOD", "MAFWDTRACK", fwdtrack::Py, fwdtrack::Pz, fwdtrack::Sign, + muonalignfwdtrk::GlobalBC, + muonalignfwdtrk::TrackTime, muonalignfwdtrk::TrackTimeRes, fwdtrack::Chi2, fwdtrack::Chi2MatchMCHMFT, muonalignfwdtrk::MAMFTTrackId, muonalignfwdtrk::MAMCHTrackId); @@ -2300,9 +2306,10 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc } #if (PROCESS_DERIVED_TABLES == 0) - template + template void StoreCollisions(const std::unordered_map& collisionInfos, COLL const& collisions, + BC const& bcs, TMUON const& muonTracks, TCLS const& clusters, TMFT const& /*mftTracks*/, @@ -2392,6 +2399,13 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } + // skip tracks without associated collision (this should never be true here) + if (!mchTrack.has_collision()) { + continue; + } + const auto& trackColl = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& trackBc = bcs.rawIteratorAt(trackColl.bcId()); + // store all MCH tracks when the event contains a muon pair of opposite sign if (!hasGoodOppositeSignMuonPair) { // only store good MCH tracks @@ -2405,6 +2419,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc mchTrack.x(), mchTrack.y(), mchTrack.z(), mchTrack.phi(), mchTrack.tgl(), mchTrack.signed1Pt(), mchTrack.nClusters(), mchTrack.pDca(), mchTrack.rAtAbsorberEnd(), + trackBc.globalBC(), + mchTrack.trackTime(), mchTrack.trackTimeRes(), mchTrack.chi2(), mchTrack.chi2MatchMCHMFT(), mchTrack.matchMFTTrackId(), mchTrack.matchMCHTrackId()); @@ -2437,9 +2453,16 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc continue; } + // skip tracks without associated collision (this should never be true here) + if (!mchTrack.has_collision()) { + continue; + } try { auto remappedMchId = muonTrackIdRemapping.at(mchTrack.globalIndex()); + const auto& trackColl = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& trackBc = bcs.rawIteratorAt(trackColl.bcId()); + mftTable(remappedCollisionIndex, mftTrack.x(), mftTrack.y(), mftTrack.z(), mftTrack.phi(), mftTrack.tgl(), mftTrack.signed1Pt(), @@ -2450,6 +2473,8 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc muonTrack.x(), muonTrack.y(), muonTrack.z(), muonTrack.phi(), muonTrack.tgl(), muonTrack.signed1Pt(), muonTrack.nClusters(), muonTrack.pDca(), muonTrack.rAtAbsorberEnd(), + trackBc.globalBC(), + mchTrack.trackTime(), mchTrack.trackTimeRes(), muonTrack.chi2(), muonTrack.chi2MatchMCHMFT(), remappedMftTrkIndex, remappedMchId); @@ -3207,7 +3232,7 @@ struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struc InitCollisions(collisions, bcs, muonTracks, clusters, mftTracks, collisionInfos); if (cfgProduceMuonAlignmentTables) { - StoreCollisions(collisionInfos, collisions, muonTracks, clusters, mftTracks, fwdTrackAssocs); + StoreCollisions(collisionInfos, collisions, bcs, muonTracks, clusters, mftTracks, fwdTrackAssocs); } FillMftPlots(collisions, muonTracks, mftTracks, collisionInfos); From 558feec9f5ba6b83d35432d4ef2211834d21287e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Tiek=C3=B6tter?= Date: Thu, 5 Nov 2026 13:50:47 +0100 Subject: [PATCH 571/648] [PWGDQ] Update ALICE 3 heavy-flavor cuts (#18603) --- PWGDQ/Core/CutsLibrary.cxx | 160 +++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 32 deletions(-) diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 467b4888b0e..8c617a96e59 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -3707,11 +3707,6 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (nameStr == "pairD0TopologyTight") { - cut->AddCut(GetAnalysisCut("pairD0TopologyTight")); - return cut; - } - if (nameStr == "pairD0HighPt1") { cut->AddCut(GetAnalysisCut("pairLxyzProjected3sigma")); cut->AddCut(GetAnalysisCut("pairPtLow5")); @@ -3939,15 +3934,15 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (nameStr == "alice3TOFEtaD0") { + if (nameStr == "alice3TOFEta") { cut->AddCut(GetAnalysisCut("alice3TOFEta")); - cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3TrackQualityHF")); return cut; } - if (nameStr == "alice3RICHEtaD0") { + if (nameStr == "alice3RICHEta") { cut->AddCut(GetAnalysisCut("alice3RICHEta")); - cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3TrackQualityHF")); return cut; } @@ -4119,6 +4114,30 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3KaonTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3KaonTOFRICH")); + return cut; + } + + if (nameStr == "alice3PionTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3PionTOFRICH")); + return cut; + } + + if (nameStr == "alice3ProtonTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3ProtonTOFRICH")); + return cut; + } + + if (nameStr == "alice3ElectronTOFRICH") { + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + cut->AddCut(GetAnalysisCut("alice3ElectronTOFRICH")); + return cut; + } + if (nameStr == "alice3DielectronPID") { cut->AddCut(GetAnalysisCut("alice3JpsiKine")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); @@ -7176,31 +7195,27 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } if (nameStr == "pairD0Topology") { - cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0); - cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03); - cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); - cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); - cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); - return cut; - } - - if (nameStr == "pairD0TopologyTight") { cut->AddCut(VarManager::kCosPointingAngle, 0.8, 1.0, false, VarManager::kPt, 0.0, 1.0); cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0, false, VarManager::kPt, 1.0, 4.0); cut->AddCut(VarManager::kCosPointingAngle, 0.85, 1.0, false, VarManager::kPt, 4.0, 50.0); - cut->AddCut(VarManager::kD0DcaDaughters, 0.035, 1000.0, false, VarManager::kPt, 0.0, 1.0); - cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03, false, VarManager::kPt, 1.0, 50.0); - cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); - cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); - cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); - cut->AddCut(VarManager::kPt1, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); - cut->AddCut(VarManager::kPt1, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); - cut->AddCut(VarManager::kPt1, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); - cut->AddCut(VarManager::kPt1, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); - cut->AddCut(VarManager::kPt2, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); - cut->AddCut(VarManager::kPt2, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); - cut->AddCut(VarManager::kPt2, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); - cut->AddCut(VarManager::kPt2, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.01, false, VarManager::kPt, 0.0, 50.0); + cut->AddCut(VarManager::kVertexingLxy, 0.01, 10.0); + cut->AddCut(VarManager::kRap, -2.0, 2.0); + return cut; + } + + if (nameStr == "pairLambdaCTopology") { + cut->AddCut(VarManager::kPt, 0.0, 50.0); + cut->AddCut(VarManager::kRap, -2.0, 2.0); + cut->AddCut(VarManager::kVertexingProcCode, 0.5, 1.e6); + cut->AddCut(VarManager::kCosPointingAngle, 0.80, 1.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.90, 1.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.95, 1.0, false, VarManager::kPt, 4.0, 50.0); + cut->AddCut(VarManager::kVertexingLxy, 0.0, 10.0); + cut->AddCut(VarManager::kVertexingLxy, 0.002, 10.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kVertexingLxy, 0.003, 10.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kVertexingLxy, 0.005, 10.0, false, VarManager::kPt, 4.0, 50.0); + return cut; } @@ -7467,12 +7482,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } if (nameStr == "alice3TOFEta") { - cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kPt, 0.3, 1000.0); cut->AddCut(VarManager::kEta, -2.0, 2.0); // Total tracker acceptance in v3b geomety return cut; } if (nameStr == "alice3RICHEta") { + cut->AddCut(VarManager::kPt, 0.3, 1000.0); cut->AddCut(VarManager::kEta, -0.8, 0.8); // Total tracker acceptance in v3b geomety return cut; } @@ -7519,6 +7535,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "alice3TrackQualityHF") { + cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); + cut->AddCut(VarManager::kNSiliconHits, 5.0, 12.0); + cut->AddCut(VarManager::kTrackDCAxy, -0.05, 0.05); + cut->AddCut(VarManager::kTrackDCAz, -0.05, 0.05); + return cut; + } + if (nameStr == "alice3TrackQualityTightDCA") { cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); cut->AddCut(VarManager::kNSiliconHits, 6.0, 12.0); @@ -7643,6 +7667,78 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "alice3KaonTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0, 30.0, false, VarManager::kP, 0.0, 2.0); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0f, 2.0); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0, 2.0, true, VarManager::kP, 0.0, 1.9); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0, 2.0, true, VarManager::kP, 0.0, 3.2); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 2.0); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaKa, 0.5, 1.5, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0); + + return cut; + } + + if (nameStr == "alice3PionTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0, 30.0, false, VarManager::kP, 0.0, 2.0); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.0); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0, 2.0, true, VarManager::kP, 0.0, 1.9); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 0.55); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPi, 0.5, 1.5, false, VarManager::kP, 2.0, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0); + + return cut; + } + + if (nameStr == "alice3ProtonTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3f, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0f, 30.0f, false, VarManager::kP, 0.0f, 4.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0f, 3.0f, false, VarManager::kP, 0.0f, 4.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0, 2.0, true, VarManager::kP, 0.0, 3.2); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 2.0, true, VarManager::kP, 0.0, 3.8); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 4.5, 1000.0); + cut->AddCut(VarManager::kHasRICHSigPr, 0.5f, 1.5f, false, VarManager::kP, 4.5, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0f, 3.0f, false, VarManager::kP, 4.5, 1000.0); + + return cut; + } + + if (nameStr == "alice3ElectronTOFRICH") { + cut->AddCut(VarManager::kEta, -2.0, 2.0); + cut->AddCut(VarManager::kP, 0.3f, 1000.0); + + cut->AddCut(VarManager::kTOFEventTimeErr, 0.0f, 30.0f, false, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0f, 3.0f, false, VarManager::kP, 0.0f, 0.6); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -2.0f, 2.0f, true, VarManager::kP, 0.0f, 0.6); + + cut->AddCut(VarManager::kEta, -0.8, 0.8, false, VarManager::kP, 0.6, 1000.0); + cut->AddCut(VarManager::kHasRICHSigEl, 0.5f, 1.5f, false, VarManager::kP, 0.6, 1000.0); + cut->AddCut(VarManager::kRICHnSigmaEl, -3.0f, 3.0f, false, VarManager::kP, 0.6, 1000.0); + + return cut; + } + if (nameStr == "alice3CharmoniumPID") { cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 3.0, false, VarManager::kP, 0.0, 1.2); cut->AddCut(VarManager::kRICHnSigmaEl, -2.0, 3.0, false, VarManager::kHasRICHSigEl, 0.5, 1.5, false, VarManager::kP, 0.7, 1000.0); From 0d346bc92259a56f47d246f81cae3c9b540ad6bc Mon Sep 17 00:00:00 2001 From: YazhenLin Date: Thu, 5 Nov 2026 21:00:55 +0800 Subject: [PATCH 572/648] [PWGDQ] Add some code for the energy correlator study (#18600) --- PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx index ed8ea9265bb..7bcc2b70076 100644 --- a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx +++ b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx @@ -116,6 +116,8 @@ struct AnalysisEnergyCorrelator { Configurable fConfigJpsiPtMin{"cfgJpsiPtMin", 0.0, "J/psi pt minimum"}; Configurable fConfigJpsiPtMax{"cfgJpsiPtMax", 100.0, "J/psi pt maximum"}; Configurable fConfigJpsiRapMax{"cfgJpsiRapMax", 0.9, "J/psi rapidity maximum"}; + Configurable fConfigJpsiTauxyMin{"cfgJpsiTauxyMin", -10000, "J/psi tauxy minimum"}; + Configurable fConfigJpsiTauxyMax{"cfgJpsiTauxyMax", 10000.0, "J/psi tauxy maximum"}; Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; Configurable recSignals{"cfgMCRecSignals", "eeFromJpsi", "Comma separated list of MC signals (reconstructed)"}; Configurable recSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; @@ -627,8 +629,8 @@ struct AnalysisEnergyCorrelator { } // Template function to run same event pairing (barrel-barrel, muon-muon, barrel-muon) - template - void runSameEventPairing(TTrack1 const& t1, TTrack2 const& t2, int iEleCut, aod::McParticles const& /*mcParticles*/) + template + void runSameEventPairing(TTrack1 const& t1, TTrack2 const& t2, int iEleCut, TEvent const& event, aod::McParticles const& /*mcParticles*/) { std::map> histNames = fTrackHistNames; std::map> histNamesMC = fBarrelHistNamesMCmatched; @@ -647,6 +649,7 @@ struct AnalysisEnergyCorrelator { } // end loop over MC signals VarManager::FillPair(t1, t2); + VarManager::FillPairVertexing(event, t1, t2, VarManager::fgValues); if (sign1 * sign2 < 0) { // +- pairs fHistMan->FillHistClass(histNames[iEleCut][0].Data(), VarManager::fgValues); // reconstructed, unmatched @@ -802,18 +805,20 @@ struct AnalysisEnergyCorrelator { // Fill pair histograms for all cuts that both tracks pass for (size_t iCut = 0; iCut < fTrackCuts.size(); iCut++) { if (twoTrackFilter & (static_cast(1) << iCut)) { - runSameEventPairing(t1, t2, iCut, mcParticles); + runSameEventPairing(t1, t2, iCut, event, mcParticles); } } float mass = VarManager::fgValues[VarManager::kMass]; float pt = VarManager::fgValues[VarManager::kPt]; float rap = VarManager::fgValues[VarManager::kRap]; + float tauxy = VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass]; // Apply J/psi cuts if (mass < fConfigPairOptions.fConfigJpsiMassMin.value || mass > fConfigPairOptions.fConfigJpsiMassMax.value || pt < fConfigPairOptions.fConfigJpsiPtMin.value || pt > fConfigPairOptions.fConfigJpsiPtMax.value || - std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value) { + std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value || + tauxy < fConfigPairOptions.fConfigJpsiTauxyMin.value || tauxy > fConfigPairOptions.fConfigJpsiTauxyMax.value) { continue; } @@ -928,13 +933,17 @@ struct AnalysisEnergyCorrelator { } // Fill pair variables for cut VarManager::FillPair(t1, t2, VarManager::fgValues); + VarManager::FillPairVertexing(event1, t1, t2, VarManager::fgValues); float mass = VarManager::fgValues[VarManager::kMass]; float pt = VarManager::fgValues[VarManager::kPt]; float rap = VarManager::fgValues[VarManager::kRap]; + float tauxy = VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass]; + // Apply J/psi cuts if (mass < fConfigPairOptions.fConfigJpsiMassMin.value || mass > fConfigPairOptions.fConfigJpsiMassMax.value || pt < fConfigPairOptions.fConfigJpsiPtMin.value || pt > fConfigPairOptions.fConfigJpsiPtMax.value || - std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value) { + std::abs(rap) > fConfigPairOptions.fConfigJpsiRapMax.value || + tauxy < fConfigPairOptions.fConfigJpsiTauxyMin.value || tauxy > fConfigPairOptions.fConfigJpsiTauxyMax.value) { continue; } if (t1.sign() * t2.sign() >= 0) { From 8a4077f23dbe79e5803e6abe0a7bf2d0a8c9a7b4 Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Thu, 5 Nov 2026 14:12:33 +0100 Subject: [PATCH 573/648] [Common] occupancy calculation fix at ROF borders; bunch pattern from AggregatedRunInfo (#18574) --- Common/Tools/EventSelectionModule.h | 92 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/Common/Tools/EventSelectionModule.h b/Common/Tools/EventSelectionModule.h index 09f114364c7..99691db4420 100644 --- a/Common/Tools/EventSelectionModule.h +++ b/Common/Tools/EventSelectionModule.h @@ -225,7 +225,7 @@ class BcSelectionModule // first bc of the first orbit bcSOR = static_cast((sorTimestamp * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS) * nBCsPerOrbit; // duration of TF in bcs - nBCsPerTF = 32; // hard-coded for Run3 MC (no info from ccdb at the moment) + nBCsPerTF = 32 * nBCsPerOrbit; // hard-coded 32 orbits per TF for Run3 MC (no info from ccdb at the moment) } else { auto runInfo = (!isMC) ? o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run) : o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run, strLPMProductionTag); @@ -626,7 +626,6 @@ class EventSelectionModule int run3min = 500000; int lastRun = -1; // last run number (needed to access ccdb only if run!=lastRun) std::bitset bcPatternB; // bc pattern of colliding bunches - std::vector bcsPattern; // pattern of colliding BCs int64_t bcSOR = -1; // global bc of the start of the first orbit int64_t nBCsPerTF = -1; // duration of TF in bcs, should be 128*3564 or 32*3564 @@ -640,18 +639,17 @@ class EventSelectionModule std::vector diffVzParMean; // parameterization for mean of diff vZ by FT0 vs by tracks std::vector diffVzParSigma; // parameterization for stddev of diff vZ by FT0 vs by tracks - int32_t findClosest(const int64_t globalBC, const std::map& bcs) + // first colliding BC (in ascending order) within +/- maxDiff of localBC, or -1 if none + int32_t findBCinPattern(int32_t localBC, int32_t maxDiff = 20) const // o2-linter: disable=magic-number (max BC difference to match pattern) { - auto it = bcs.lower_bound(globalBC); - int64_t bc1 = it->first; - int32_t index1 = it->second; - if (it != bcs.begin()) - --it; - int64_t bc2 = it->first; - int32_t index2 = it->second; - int64_t dbc1 = std::abs(bc1 - globalBC); - int64_t dbc2 = std::abs(bc2 - globalBC); - return (dbc1 <= dbc2) ? index1 : index2; + int32_t first = std::max(0, localBC - maxDiff); + int32_t last = std::min(nBCsPerOrbit - 1, localBC + maxDiff); + for (int32_t b = first; b <= last; b++) { + if (bcPatternB[b]) { + return b; + } + } + return -1; } // helper function to find median time in the vector of TOF or TRD-track times @@ -765,14 +763,24 @@ class EventSelectionModule // colliding bc pattern int64_t ts = timestamps[0]; - // getForTimeStamp replaced with getSpecific to set metadata to zero - // avoids crash related to specific run number - auto grplhcif = ccdb->template getSpecific("GLO/Config/GRPLHCIF", ts); - bcPatternB = grplhcif->getBunchFilling().getBCPattern(); - bcsPattern = grplhcif->getBunchFilling().getFilledBCs(); + // GRPLHCIF is taken from AggregatedRunInfo (fetched at mid-run, without metadata) + bcPatternB = runInfo.grpLHC->getBunchFilling().getBCPattern(); if (runLightIons >= 0) { - for (uint32_t i = 0; i < bcsPattern.size(); i++) - LOGP(debug, "bcsPattern: i={} bc={}", i, bcsPattern.at(i)); + for (int32_t b = 0; b < nBCsPerOrbit; b++) { + if (bcPatternB[b]) { + LOGP(debug, "bcPatternB: colliding bc={}", b); + } + } + } + + // TEMPORARY cross-check: compare with GRPLHCIF fetched at the timestamp of the first BC (previous approach). + // Must stay after the copy above: this fetch may replace the cached object runInfo.grpLHC points to. + // getSpecific (instead of getForTimeStamp) sets metadata to zero, avoids crash related to specific run number + auto grplhcifTs = ccdb->template getSpecific("GLO/Config/GRPLHCIF", ts); + if (grplhcifTs->getBunchFilling().getBCPattern() != bcPatternB) { + LOGP(error, "EventSelectionModule: bcPatternB mismatch for run {}: runInfo.grpLHC (mid-run) vs GRPLHCIF at ts={}", run, ts); + } else { + LOGP(info, "EventSelectionModule: bcPatternB cross-check OK for run {} (ts={})", run, ts); } // extract ITS ROF parameters @@ -974,7 +982,7 @@ class EventSelectionModule float sumTime = 0, sumW = 0, sumHighPtTime = 0, sumHighPtW = 0; for (const auto& track : colPvTracks) { float trackTime = track.trackTime(); - if (track.itsNCls() >= 5) // o2-linter: disable=magic-number (indeed counting layers 5 6 7) + if (track.itsNCls() >= 5) // o2-linter: disable=magic-number (number of ITS clusters >=5) vTracksITS567perColl[colIndex]++; if (track.hasTRD()) vIsVertexTRDmatched[colIndex] = 1; @@ -1019,15 +1027,10 @@ class EventSelectionModule // alternative collision-BC matching (currently: test mode, the aim is to improve pileup rejection) if (runLightIons >= 0) { foundGlobalBC = globalBC; - // find closest nominal bc in pattern - for (uint32_t i = 0; i < bcsPattern.size(); i++) { - int32_t localBC = globalBC % nBCsPerOrbit; - int32_t bcFromPattern = bcsPattern.at(i); - int64_t bcDiff = bcFromPattern - localBC; - if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) - foundGlobalBC = (globalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPattern; - break; // the bc in pattern is found - } + // find nominal bc in pattern + int32_t bcFromPattern = findBCinPattern(globalBC % nBCsPerOrbit); + if (bcFromPattern >= 0) { // the bc in pattern is found + foundGlobalBC = (globalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPattern; } // matched with TOF --> precise time, match to TVX, but keep the nominal foundGlobalBC from pattern @@ -1053,18 +1056,14 @@ class EventSelectionModule // end of if TOF-matched vertex } else { // for non-TOF and low-mult vertices, consider nearby nominal bcs - int64_t meanBC = globalBC + TMath::Nint(sumHighPtTime / sumHighPtW / bcNS); + int64_t meanBC = globalBC + (sumHighPtW > 0 ? TMath::Nint(sumHighPtTime / sumHighPtW / bcNS) : 0); int64_t bestGlobalBC = findBestGlobalBC(meanBC, evselOpts.confSigmaBCforHighPtTracks, vNcontributors[colIndex], col.posZ(), mapGlobalBcVtxZ); if (bestGlobalBC > 0) { foundGlobalBC = bestGlobalBC; - // find closest nominal bc in pattern - for (uint32_t j = 0; j < bcsPattern.size(); j++) { - int32_t bcFromPatternBest = bcsPattern.at(j); - int64_t bcDiff = bcFromPatternBest - (bestGlobalBC % nBCsPerOrbit); - if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) - foundGlobalBC = (bestGlobalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPatternBest; - break; // the bc in pattern is found - } + // find nominal bc in pattern + int32_t bcFromPatternBest = findBCinPattern(bestGlobalBC % nBCsPerOrbit); + if (bcFromPatternBest >= 0) { // the bc in pattern is found + foundGlobalBC = (bestGlobalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPatternBest; } foundBCindex = mapGlobalBcWithTVX[bestGlobalBC]; } else { // failed to find a proper TVX with small vZ difference @@ -1098,7 +1097,7 @@ class EventSelectionModule } else if (nPvTracksHighPtTPCnoTOFnoTRD > 0) { // for collisions with high-pt ITSTPC-nonTOF-nonTRD tracks // search in 3*confSigmaBCforHighPtTracks range (3*4 bcs by default) - int64_t meanBC = globalBC + TMath::Nint(sumHighPtTime / sumHighPtW / bcNS); + int64_t meanBC = globalBC + (sumHighPtW > 0 ? TMath::Nint(sumHighPtTime / sumHighPtW / bcNS) : 0); int64_t bestGlobalBC = findBestGlobalBC(meanBC, evselOpts.confSigmaBCforHighPtTracks, vNcontributors[colIndex], col.posZ(), mapGlobalBcVtxZ); if (bestGlobalBC > 0) { foundGlobalBC = bestGlobalBC; @@ -1155,6 +1154,7 @@ class EventSelectionModule const float timeWinOccupancyCalcMinNS = evselOpts.confTimeIntervalForOccupancyCalculationMin * 1e3; // ns const float timeWinOccupancyCalcMaxNS = evselOpts.confTimeIntervalForOccupancyCalculationMax * 1e3; // ns + // loop over collisions to calculate occupancy-related quantities for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; @@ -1163,6 +1163,11 @@ class EventSelectionModule int32_t foundBC = vFoundBCindex[colIndex]; auto bcselEntry = bcselbuffer[foundBC]; + if (bcselEntry.foundFT0Id > -1) { + // required: explicit ft0s table + auto foundFT0 = ft0s.rawIteratorAt(bcselEntry.foundFT0Id); + vAmpFT0CperColl[colIndex] = foundFT0.sumAmpC(); + } // check if we are close to ITS ROF borders => N ITS tracks is not reliable, and FT0C ampl can be used for occupancy estimation // denominator for vAmpFT0CperColl is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts vProxyForCollNtracks[colIndex] = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? vTracksITS567perColl[colIndex] : vAmpFT0CperColl[colIndex] / 10.; @@ -1183,12 +1188,6 @@ class EventSelectionModule for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - auto bcselEntr = bcselbuffer[vFoundBCindex[colIndex]]; - if (bcselEntr.foundFT0Id > -1) { - // required: explicit ft0s table - auto foundFT0 = ft0s.rawIteratorAt(bcselEntr.foundFT0Id); - vAmpFT0CperColl[colIndex] = foundFT0.sumAmpC(); - } int64_t tfId = (foundGlobalBC - bcSOR) / nBCsPerTF; int64_t rofId = (foundGlobalBC + nBCsPerOrbit - rofOffset) / rofLength; @@ -1286,6 +1285,7 @@ class EventSelectionModule // calculation of the median time for the occupancy in a given time window std::vector> pairsDeltaTimeMult; + pairsDeltaTimeMult.reserve(vCollsTimeDeltaWrtGivenColl.size()); for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) pairsDeltaTimeMult.emplace_back(vCollsTimeDeltaWrtGivenColl[iCol], vProxyNtracksAssocColls[iCol]); std::sort(pairsDeltaTimeMult.begin(), pairsDeltaTimeMult.end()); // sorts by first element by default From 739358264e7fd9fc53aaa0531cf171dbb6fabd32 Mon Sep 17 00:00:00 2001 From: Jesper Karlsson Gumprecht <113693781+jesgum@users.noreply.github.com> Date: Thu, 5 Nov 2026 14:13:36 +0100 Subject: [PATCH 574/648] [Common] Add LPMProduction to mc generator map to autodetect generator from metadata in the MultModule (#18549) --- Common/Core/MetadataHelper.cxx | 29 ++++++++++++++++++++++++++ Common/Core/MetadataHelper.h | 4 ++++ Common/Tools/Multiplicity/MultModule.h | 16 +++++--------- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Common/Core/MetadataHelper.cxx b/Common/Core/MetadataHelper.cxx index c1712c3f3b6..7530e2a9900 100644 --- a/Common/Core/MetadataHelper.cxx +++ b/Common/Core/MetadataHelper.cxx @@ -25,6 +25,7 @@ #include #include +#include #include using namespace o2::common::core; @@ -153,6 +154,34 @@ std::string MetadataHelper::getO2Version() const return get("O2Version"); } +std::string MetadataHelper::getGenerator() const +{ + if (!isMC()) { + return {}; + } + + // MC production tag (LPMProductionTag) -> generator name, for AO2Ds without the generator in the metadata + static const std::map mcGeneratorMap = { + {"LHC26f3", "EPOS"}, + {"LHC26k3", "EPOS"}}; + + // FIXME: use the "Generator" key first once it is available in AO2Ds (it must also be added to the key list in the constructor) + // if (isKeyDefined("Generator")) { + // return get("Generator"); + // } + + // older AO2Ds: rely on the LPMProductionTag to generator map + if (isKeyDefined("LPMProductionTag")) { + auto it = mcGeneratorMap.find(get("LPMProductionTag")); + if (it != mcGeneratorMap.end()) { + return it->second; + } + } + + // no generator found in the metadata, fall back to PYTHIA + return "PYTHIA"; +} + bool MetadataHelper::isCommitInSoftwareTag(const std::string& commitHash, const std::string& ccdbUrl) const { const std::string softwareTag = getO2Version(); diff --git a/Common/Core/MetadataHelper.h b/Common/Core/MetadataHelper.h index 7b871084f9a..45e511a01a9 100644 --- a/Common/Core/MetadataHelper.h +++ b/Common/Core/MetadataHelper.h @@ -55,6 +55,10 @@ struct MetadataHelper { /// @return the O2 version from the metadata std::string getO2Version() const; + /// @brief Function to get the MC generator name from the metadata + /// @return the generator name (e.g. "PYTHIA", "EPOS"), "PYTHIA" for MC if it cannot be determined, or an empty string for data + std::string getGenerator() const; + /// @brief Function to get the metadata value for a given key /// @param key the key of the metadata /// @return the value of the metadata. Throws an exception if the key is not found diff --git a/Common/Tools/Multiplicity/MultModule.h b/Common/Tools/Multiplicity/MultModule.h index e9a64798915..b63af1bc3c5 100644 --- a/Common/Tools/Multiplicity/MultModule.h +++ b/Common/Tools/Multiplicity/MultModule.h @@ -346,7 +346,7 @@ struct standardConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable reconstructionPass{"reconstructionPass", "", {"Apass to use when fetching the calibration tables. Empty (default) does not check for any pass. Use `metadata` to fetch it from the AO2D metadata. Otherwise it will override the metadata."}}; // centrality operation - o2::framework::Configurable generatorName{"generatorName", "", {"Specify if and only if this is MC. Typical: PYTHIA"}}; + o2::framework::Configurable generatorName{"generatorName", "", {"MC generator name for centrality calibration (e.g. PYTHIA, EPOS). Empty: autodetect from metadata in Run 3, falling back to PYTHIA. Specify if and only if this is MC to override autodetect from metadata"}}; o2::framework::Configurable embedINELgtZEROselection{"embedINELgtZEROselection", false, {"Option to do percentile 100.5 if not INELgtZERO"}}; }; @@ -1199,16 +1199,10 @@ class MultModule mRunNumberCentrality = bc.runNumber(); // mark that this run has been attempted already regardless of outcome LOGF(info, "centrality loading procedure for timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); - // capture the need for PYTHIA calibration in Pb-Pb runs - if (metadataInfo.isMC() && mRunNumber >= 544013 && mRunNumber <= 545367 && internalOpts.generatorName.value.empty()) { - LOGF(info, "This is MC for Pb-Pb. Setting generatorName automatically to PYTHIA"); - internalOpts.generatorName.value = "PYTHIA"; - } - - // capture the need for PYTHIA calibration in light ion runs automatically - if (metadataInfo.isMC() && mRunNumber >= 564250 && mRunNumber <= 564472 && internalOpts.generatorName.value.empty()) { - LOGF(info, "This is MC for light ion runs. Setting generatorName automatically to PYTHIA"); - internalOpts.generatorName.value = "PYTHIA"; + // autodetect the generator from the metadata if not explicitly configured + if (metadataInfo.isMC() && internalOpts.generatorName.value.empty()) { + internalOpts.generatorName.value = metadataInfo.getGenerator(); + LOGF(info, "Setting generatorName automatically from metadata to %s", internalOpts.generatorName.value); } LOGF(info, "centrality loading procedure for timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); From e3063c0c861628e1a5b70bd1fa1ec9a29fdfb698 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Thu, 5 Nov 2026 14:21:51 +0100 Subject: [PATCH 575/648] [Tools] MlResponse: cast uint8_t to int before printing (#18597) --- Tools/ML/MlResponse.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tools/ML/MlResponse.h b/Tools/ML/MlResponse.h index 413534f48cb..f7cd001ea96 100644 --- a/Tools/ML/MlResponse.h +++ b/Tools/ML/MlResponse.h @@ -109,10 +109,10 @@ class MlResponse void setModelPathsCCDB(const std::vector& onnxFiles, const o2::ccdb::CcdbApi& ccdbApi, const std::vector& pathsCCDB, int64_t timestampCCDB) { if (onnxFiles.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; } if (pathsCCDB.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the number of CCDB paths (" << pathsCCDB.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the number of CCDB paths (" << pathsCCDB.size() << ")! Please check your configurables."; } // check that the path is unique for each BDT model (otherwise CCDB download does not work as expected) @@ -140,7 +140,7 @@ class MlResponse void setModelPathsLocal(const std::vector& onnxFiles) { if (onnxFiles.size() != mNModels) { - LOG(fatal) << "Number of expected models (" << mNModels << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; + LOG(fatal) << "Number of expected models (" << static_cast(mNModels) << ") different from the one set (" << onnxFiles.size() << ")! Please check your configurables."; } mPaths = onnxFiles; } From a3f61c821c5ce7b8c770155e044afc19051ec336 Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Thu, 5 Nov 2026 14:51:31 +0100 Subject: [PATCH 576/648] [PWGEM] PhotonMeson: merge EventHistograms into EMPhotonEventCut (#18590) --- PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx | 45 ++++ PWGEM/PhotonMeson/Core/EMPhotonEventCut.h | 225 ++++++++++++++++++ PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h | 18 +- PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h | 21 +- PWGEM/PhotonMeson/Core/TaggingPi0.h | 18 +- PWGEM/PhotonMeson/Core/TaggingPi0MC.h | 26 +- .../TableProducer/photonconversionbuilder.h | 15 +- PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx | 49 +--- PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx | 21 +- PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx | 78 +++--- PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx | 15 +- PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx | 15 +- PWGEM/PhotonMeson/Tasks/emcalQC.cxx | 14 +- PWGEM/PhotonMeson/Tasks/pairQcTask.cxx | 18 +- PWGEM/PhotonMeson/Tasks/photonResoTask.cxx | 15 +- PWGEM/PhotonMeson/Tasks/photonhbt.cxx | 19 +- PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx | 158 +----------- PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx | 157 +----------- PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx | 177 +------------- PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx | 15 +- PWGEM/PhotonMeson/Utils/EventHistograms.h | 114 --------- PWGEM/PhotonMeson/Utils/EventUtilities.h | 178 ++++++++++++++ 22 files changed, 626 insertions(+), 785 deletions(-) delete mode 100644 PWGEM/PhotonMeson/Utils/EventHistograms.h create mode 100644 PWGEM/PhotonMeson/Utils/EventUtilities.h diff --git a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx index f7eaea2fd44..97791f3d4d9 100644 --- a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx +++ b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.cxx @@ -19,6 +19,9 @@ #include +#include +#include + ClassImp(EMPhotonEventCut); void EMPhotonEventCut::SetRequireSel8(bool flag) @@ -134,3 +137,45 @@ void EMPhotonEventCut::SetRequireEMCHardwareTriggered(bool flag) mRequireEMCHardwareTriggered = flag; LOG(info) << "EM Photon Event Cut, require the EMC to be triggered by requiring kEMC7 or kDMC7: " << mRequireEMCHardwareTriggered; } + +void EMPhotonEventCut::SetTrackOccupancyRange(float min, float max) +{ + mMinTrackOcc = min; + mMaxTrackOcc = max; + LOG(info) << "EM Photon Event Cut, set track occupancy range: " << mMinTrackOcc << " - " << mMaxTrackOcc; +} + +void EMPhotonEventCut::SetFT0COccupancyRange(float min, float max) +{ + mMinFT0COcc = min; + mMaxFT0COcc = max; + LOG(info) << "EM Photon Event Cut, set FT0C occupancy range: " << mMinFT0COcc << " - " << mMaxFT0COcc; +} + +void EMPhotonEventCut::SetRequireGoodQvec(bool flag) +{ + mRequireGoodQvec = flag; + LOG(info) << "EM Photon Event Cut, require good Q vectors (only applied if the collision table has Q vectors): " << mRequireGoodQvec; +} + +void EMPhotonEventCut::SetQvecHarmonic(int harmonic) +{ + mQvecHarmonic = harmonic; + LOG(info) << "EM Photon Event Cut, set Q vector harmonic: " << mQvecHarmonic; +} + +void EMPhotonEventCut::SetMaxQvec(float max) +{ + mMaxQvec = max; + LOG(info) << "EM Photon Event Cut, set max. absolute Q vector component: " << mMaxQvec; +} + +void EMPhotonEventCut::SetQvecDetectors(std::vector const& detectors) +{ + mQvecDetectors = detectors; + std::string list; + for (const auto& detector : mQvecDetectors) { + list += std::to_string(detector) + " "; + } + LOG(info) << "EM Photon Event Cut, set Q vector detectors for Q vector cut: " << list; +} diff --git a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h index 26b1f2c9490..9c03e498789 100644 --- a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h +++ b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h @@ -16,10 +16,26 @@ #ifndef PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ #define PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" + #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include #include +#include +#include class EMPhotonEventCut { @@ -47,9 +63,31 @@ class EMPhotonEventCut kIsGoodITSLayersAll, kEMCReadoutInMB, kEMCHardwareTriggered, + kTrackOccupancy, + kFT0COccupancy, + kGoodQvec, kNCuts }; + /// bins of the Event/{before,after}/hCollisionCounter histogram + enum class EventCounterBin : uint8_t { + kAll = 1, + kNoTFBorder = 2, + kNoITSROFBorder = 3, + kNoSameBunchPileup = 4, + kVertexITSTPC = 5, + kGoodZVtxFT0vsPV = 6, + kFT0AND = 7, + kSel8 = 8, + kZVtx = 9, + kTrackOccupancy = 10, + kFT0COccupancy = 11, + kEMCMBReadout = 12, + kEMCL0Trigger = 13, + kGoodQvec = 14, + kAccepted = 15 + }; + [[nodiscard]] const std::string& getName() const { return name; } [[nodiscard]] const std::string& getTitle() const { return title; } @@ -113,6 +151,15 @@ class EMPhotonEventCut if (mRequireEMCHardwareTriggered && !IsSelected(collision, EMPhotonEventCuts::kEMCHardwareTriggered)) { return false; } + if (!IsSelected(collision, EMPhotonEventCuts::kTrackOccupancy)) { + return false; + } + if (!IsSelected(collision, EMPhotonEventCuts::kFT0COccupancy)) { + return false; + } + if (mRequireGoodQvec && !IsSelected(collision, EMPhotonEventCuts::kGoodQvec)) { + return false; + } return true; } @@ -177,11 +224,153 @@ class EMPhotonEventCut case EMPhotonEventCuts::kEMCHardwareTriggered: return (collision.alias_bit(kEMC7) || collision.alias_bit(kDMC7)); + case EMPhotonEventCuts::kTrackOccupancy: + if constexpr (o2::aod::pwgem::pm::utils::event::HasTrackOccupancy) { + return mMinTrackOcc <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < mMaxTrackOcc; + } else { + // the event table has no track occupancy, the cut can not be applied + warnOccupancyNotAvailable(isTrackOccupancyCutSet(), "track"); + return true; + } + + case EMPhotonEventCuts::kFT0COccupancy: + if constexpr (o2::aod::pwgem::pm::utils::event::HasFT0COccupancy) { + return mMinFT0COcc <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < mMaxFT0COcc; + } else { + // the event table has no FT0C occupancy, the cut can not be applied + warnOccupancyNotAvailable(isFT0COccupancyCutSet(), "FT0C"); + return true; + } + + case EMPhotonEventCuts::kGoodQvec: + if constexpr (o2::aod::pwgem::pm::utils::event::HasQvec) { + return o2::aod::pwgem::pm::utils::event::isQvecGood(collision, mQvecDetectors, mQvecHarmonic, mMaxQvec); + } else { + // the event table has no Q vectors, the cut can not be applied + if (mRequireGoodQvec && !mWarnedNoQvec) { + LOG(warning) << "EM Photon Event Cut: Q vector cut requested, but the collision table has no Q vectors. Q vector cut is not applied!"; + mWarnedNoQvec = true; + } + return true; + } + default: return true; } } + /// \brief add the event QA histograms to the given registry. Call this after all cut values are set! + /// \param fRegistry pointer to the histogram registry of the task + /// \param useWeight whether the histograms should store weights (Sumw2) + void addEventHistograms(o2::framework::HistogramRegistry* fRegistry, bool useWeight = false) const + { + auto hCollisionCounter = fRegistry->add("Event/before/hCollisionCounter", "collision counter;;Number of events", o2::framework::HistType::kTH1D, {{static_cast(EventCounterBin::kAccepted), 0.5, static_cast(EventCounterBin::kAccepted) + 0.5f}}, useWeight); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kAll), "all"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoTFBorder), "No TF border"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoITSROFBorder), "No ITS ROF border"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kNoSameBunchPileup), "No Same Bunch Pileup"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kVertexITSTPC), "Is Vertex ITSTPC"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kGoodZVtxFT0vsPV), "Is Good Zvtx FT0vsPV"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kFT0AND), "FT0AND"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kSel8), "sel8"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kZVtx), std::format("{:g} < Z_{{vtx}} < {:g} cm", mMinZvtx, mMaxZvtx).c_str()); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kTrackOccupancy), "track occupancy"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kFT0COccupancy), "FT0C occupancy"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kEMCMBReadout), "EMC MB Readout"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kEMCL0Trigger), "EMC L0 Triggered"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kGoodQvec), "good Q vector"); + hCollisionCounter->GetXaxis()->SetBinLabel(static_cast(EventCounterBin::kAccepted), "accepted"); + + fRegistry->add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{220, -11, +11}}, useWeight); + fRegistry->add("Event/before/hMultNTracksPV", "hMultNTracksPV; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); + fRegistry->add("Event/before/hMultNTracksPVeta1", "hMultNTracksPVeta1; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); + fRegistry->add("Event/before/hMultFT0", "hMultFT0;mult. FT0A;mult. FT0C", o2::framework::HistType::kTH2F, {{200, 0, 200000}, {60, 0, 60000}}, useWeight); + fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", o2::framework::HistType::kTH2F, {{110, 0, 110}, {500, 0, 5000}}, useWeight); + fRegistry->add("Event/before/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 5000}}, useWeight); + fRegistry->add("Event/before/hMultFT0CvsOccupancy", "hMultFT0CvsOccupancy;mult. FT0C;N_{track} in time range", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 10000}}, useWeight); + fRegistry->addClone("Event/before/", "Event/after/"); + } + + /// \brief fill the event QA histograms + /// \tparam ev_id 0 for before the event selection, 1 for after the event selection + /// \param fRegistry pointer to the histogram registry of the task + /// \param collision collision to fill + /// \param weight weight of the collision + template + void fillEventInfo(o2::framework::HistogramRegistry* fRegistry, TCollision const& collision, float weight = 1.f) const + { + static constexpr std::array EventTypes = {"before/", "after/"}; + auto fillCounter = [&](EventCounterBin bin) { + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), static_cast(bin), weight); + }; + + fillCounter(EventCounterBin::kAll); + if (IsSelected(collision, EMPhotonEventCuts::kNoTFB)) { + fillCounter(EventCounterBin::kNoTFBorder); + } + if (IsSelected(collision, EMPhotonEventCuts::kNoITSROFB)) { + fillCounter(EventCounterBin::kNoITSROFBorder); + } + if (IsSelected(collision, EMPhotonEventCuts::kNoSameBunchPileup)) { + fillCounter(EventCounterBin::kNoSameBunchPileup); + } + if (IsSelected(collision, EMPhotonEventCuts::kIsVertexITSTPC)) { + fillCounter(EventCounterBin::kVertexITSTPC); + } + if (IsSelected(collision, EMPhotonEventCuts::kIsGoodZvtxFT0vsPV)) { + fillCounter(EventCounterBin::kGoodZVtxFT0vsPV); + } + if (IsSelected(collision, EMPhotonEventCuts::kFT0AND)) { + fillCounter(EventCounterBin::kFT0AND); + } + if (IsSelected(collision, EMPhotonEventCuts::kSel8)) { + fillCounter(EventCounterBin::kSel8); + } + if (IsSelected(collision, EMPhotonEventCuts::kZvtx)) { + fillCounter(EventCounterBin::kZVtx); + } + if (IsSelected(collision, EMPhotonEventCuts::kTrackOccupancy)) { + fillCounter(EventCounterBin::kTrackOccupancy); + } + if (IsSelected(collision, EMPhotonEventCuts::kFT0COccupancy)) { + fillCounter(EventCounterBin::kFT0COccupancy); + } + if (IsSelected(collision, EMPhotonEventCuts::kEMCReadoutInMB)) { + fillCounter(EventCounterBin::kEMCMBReadout); + } + if (IsSelected(collision, EMPhotonEventCuts::kEMCHardwareTriggered)) { + fillCounter(EventCounterBin::kEMCL0Trigger); + } + if constexpr (o2::aod::pwgem::pm::utils::event::HasQvec) { + if (IsSelected(collision, EMPhotonEventCuts::kGoodQvec)) { + fillCounter(EventCounterBin::kGoodQvec); + } + } + + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hZvtx"), collision.posZ(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPV"), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPVeta1"), collision.multNTracksPVeta1(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0"), collision.multFT0A(), collision.multFT0C(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0A"), collision.centFT0A(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0C"), collision.centFT0C(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0M"), collision.centFT0M(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0CvsMultNTracksPV"), collision.centFT0C(), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV(), weight); + fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsOccupancy"), collision.multFT0C(), collision.trackOccupancyInTimeRange(), weight); + } + + /// \brief fill the "accepted" bin of the before and after collision counter + /// \param fRegistry pointer to the histogram registry of the task + /// \param weight weight of the collision + void fillAccepted(o2::framework::HistogramRegistry* fRegistry, float weight = 1.f) const + { + fRegistry->fill(HIST("Event/before/hCollisionCounter"), static_cast(EventCounterBin::kAccepted), weight); + fRegistry->fill(HIST("Event/after/hCollisionCounter"), static_cast(EventCounterBin::kAccepted), weight); + } + // Setters void SetRequireSel8(bool flag); void SetRequireFT0AND(bool flag); @@ -202,8 +391,32 @@ class EMPhotonEventCut void SetRequireGoodITSLayersAll(bool flag); void SetRequireEMCReadoutInMB(bool flag); void SetRequireEMCHardwareTriggered(bool flag); + void SetTrackOccupancyRange(float min, float max); + void SetFT0COccupancyRange(float min, float max); + void SetRequireGoodQvec(bool flag); + void SetQvecHarmonic(int harmonic); + void SetMaxQvec(float max); + void SetQvecDetectors(std::vector const& detectors); private: + [[nodiscard]] bool isTrackOccupancyCutSet() const + { + return mMinTrackOcc != std::numeric_limits::lowest() || mMaxTrackOcc != std::numeric_limits::max(); + } + + [[nodiscard]] bool isFT0COccupancyCutSet() const + { + return mMinFT0COcc != std::numeric_limits::lowest() || mMaxFT0COcc != std::numeric_limits::max(); + } + + void warnOccupancyNotAvailable(bool cutSet, const char* estimator) const + { + if (cutSet && !mWarnedNoOccupancy) { + LOG(warning) << "EM Photon Event Cut: " << estimator << " occupancy cut set, but the collision table has no " << estimator << " occupancy. Occupancy cut is not applied!"; + mWarnedNoOccupancy = true; + } + } + std::string name; std::string title; bool mRequireSel8{false}; @@ -225,6 +438,18 @@ class EMPhotonEventCut bool mRequireGoodITSLayersAll{false}; bool mRequireEMCReadoutInMB{false}; bool mRequireEMCHardwareTriggered{false}; + + // occupancy cuts, by default no cut is applied + float mMinTrackOcc{std::numeric_limits::lowest()}, mMaxTrackOcc{std::numeric_limits::max()}; + float mMinFT0COcc{std::numeric_limits::lowest()}, mMaxFT0COcc{std::numeric_limits::max()}; + + // Q vector cut, off by default and only applied if the collision table has Q vectors + bool mRequireGoodQvec{false}; + int mQvecHarmonic{o2::aod::pwgem::pm::utils::event::kElliptic}; + float mMaxQvec{20.f}; + std::vector mQvecDetectors; + mutable bool mWarnedNoQvec{false}; + mutable bool mWarnedNoOccupancy{false}; }; #endif // PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index 22ee95e1bc3..20fdfc1bb59 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -26,7 +26,6 @@ #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EMPhoton.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/NMHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // Dilepton headers @@ -282,8 +281,8 @@ struct Pi0EtaToGammaGamma { o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; - o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; - o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>* emh1 = nullptr; o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>* emh2 = nullptr; @@ -425,7 +424,8 @@ struct Pi0EtaToGammaGamma { emh1 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); emh2 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); std::vector occBinsForHs(occ_bin_edges.begin(), occ_bin_edges.end()); std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); @@ -434,7 +434,6 @@ struct Pi0EtaToGammaGamma { } else { o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } - DefineEMEventCut(); DefinePCMCut(); mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); @@ -518,6 +517,8 @@ struct Pi0EtaToGammaGamma { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -769,13 +770,12 @@ struct Pi0EtaToGammaGamma { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; if (zbin < 0) { diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index e8c2333ccd3..989196fd278 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -25,7 +25,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/NMHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -59,7 +58,6 @@ #include #include #include -#include #include #include @@ -277,13 +275,13 @@ struct Pi0EtaToGammaGammaMC { void init(o2::framework::InitContext&) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } - DefineEMEventCut(); DefinePCMCut(); mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); mPhotonClassSelB = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassB); @@ -291,7 +289,7 @@ struct Pi0EtaToGammaGammaMC { DefineEMCCut(); DefinePHOSCut(); - f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", TString(fd_k0s_to_pi0), 0.f, 100.f); + f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", fd_k0s_to_pi0.value.c_str(), 0.f, 100.f); fRegistry.add("Event/hNrecPerMCCollision", "Nrec per mc collision;N_{rec} collisions per MC collision", o2::framework::HistType::kTH1F, {{21, -0.5f, 20.5f}}, false); if (cfgGGContaCheck) { @@ -360,6 +358,8 @@ struct Pi0EtaToGammaGammaMC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -482,8 +482,8 @@ struct Pi0EtaToGammaGammaMC { o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; - o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; - o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; + o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; //--------------------------------------------------------------------------- // In the following are tags defined which help to select the correct preslice and cuts @@ -631,13 +631,12 @@ struct Pi0EtaToGammaGammaMC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int photonid1 = -1, photonid2 = -1, pi0id = -1, etaid = -1; if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { // same kinds pairing and PCM-EMC diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0.h b/PWGEM/PhotonMeson/Core/TaggingPi0.h index 64080cd2a2d..c058fb110ff 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0.h @@ -23,7 +23,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EMPhoton.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // #include "PWGEM/Dilepton/Utils/EventMixingHandler.h" @@ -244,10 +243,10 @@ struct TaggingPi0 { emh1 = new MyEMH(ndepth); emh2 = new MyEMH(ndepth); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); addHistogrms(); - DefineEMEventCut(); DefinePCMCut(); DefineDileptonCut(); DefineEMCCut(); @@ -336,6 +335,8 @@ struct TaggingPi0 { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -429,8 +430,8 @@ struct TaggingPi0 { // o2::framework::Preslice perCollision_phos = o2::aod::phoscluster::pmeventId; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); - o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); MyEMH* emh1 = nullptr; MyEMH* emh2 = nullptr; @@ -463,13 +464,12 @@ struct TaggingPi0 { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; if (zbin < 0) { diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h index 4588f70182d..9173ac293cf 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h @@ -23,7 +23,6 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" // @@ -55,11 +54,11 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include -#include #include #include #include +#include #include #include #include @@ -221,9 +220,9 @@ struct TaggingPi0MC { void init(o2::framework::InitContext&) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); - addHistogrms(); DefineEMEventCut(); + fEMEventCut.addEventHistograms(&fRegistry); + addHistogrms(); DefinePCMCut(); DefineDileptonCut(); DefineEMCCut(); @@ -231,7 +230,7 @@ struct TaggingPi0MC { mRunNumber = 0; d_bz = 0; - f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", TString(fd_k0s_to_pi0), 0.f, 100.f); + f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", fd_k0s_to_pi0.value.c_str(), 0.f, 100.f); ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -275,14 +274,14 @@ struct TaggingPi0MC { void addHistogrms() { - TString mggTitle = "ee#gamma"; + std::string mggTitle = "ee#gamma"; if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { mggTitle = "ee#gamma"; } else { mggTitle = "#gamma#gamma"; } - const o2::framework::AxisSpec axis_m{200, 0, 0.4, Form("m_{%s} (GeV/c^{2})", mggTitle.Data())}; + const o2::framework::AxisSpec axis_m{200, 0, 0.4, std::format("m_{} (GeV/c^{{2}})", mggTitle)}; const o2::framework::AxisSpec axis_pt{ConfPtBins, "p_{T,#gamma} (GeV/c)"}; fRegistry.add("Photon/candidate/hPt", "photon candidates;p_{T,#gamma} (GeV/c)", o2::framework::HistType::kTH1D, {axis_pt}, true); // for purity @@ -311,6 +310,8 @@ struct TaggingPi0MC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -404,8 +405,8 @@ struct TaggingPi0MC { // o2::framework::Preslice perCollision_phos = o2::aod::phoscluster::pmeventId; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); - o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); + o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < static_cast(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); template void runTruePairing(TCollisions const& collisions, @@ -432,13 +433,12 @@ struct TaggingPi0MC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, weight); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, weight); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, weight); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, weight); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, weight); + fEMEventCut.fillAccepted(&fRegistry, weight); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { auto photons1_per_collision = photons1.sliceBy(perCollision1, collision.globalIndex()); diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index c74bc6fcbc4..d4edc620927 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -1151,7 +1151,7 @@ struct PhotonConversionBuilder { const float gap = circleGap(o2::track::TrackAuxPar(pTrack, d_bz), o2::track::TrackAuxPar(nTrack, d_bz)); const float score = dedupScore(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), gap); if (opt.probe != nullptr) { - *opt.probe = {true, score}; + *opt.probe = {.ok = true, .score = score}; return; } const float purity = usePurity() ? purityScore(pos, ele, cospa_kf, v0photoncandidate.getPCA(), meeSV, gap, v0photoncandidate.getPsiPair()) : -999.f; @@ -1275,7 +1275,7 @@ struct PhotonConversionBuilder { // bookkeeping of candidates and stored photons static CandKey keyOf(V0CandidateHelper const& c) { - return CandKey{c.v0ID, c.colID, c.posID, c.eleID}; + return CandKey{.v0Id = c.v0ID, .collisionId = c.colID, .posId = c.posID, .eleId = c.eleID}; } std::map candidateIndexByKey() const @@ -1612,8 +1612,8 @@ struct PhotonConversionBuilder { for (const auto& s : swaps) { const int64_t colId = s.keyI.collisionId; const int64_t posI = s.keyI.posId, eleI = s.keyI.eleId, posJ = s.keyJ.posId, eleJ = s.keyJ.eleId; - const CandKey a1 = CandKey{-1, colId, posI, eleJ}; - const CandKey a2 = CandKey{-1, colId, posJ, eleI}; + const CandKey a1 = CandKey{.v0Id = -1, .collisionId = colId, .posId = posI, .eleId = eleJ}; + const CandKey a2 = CandKey{.v0Id = -1, .collisionId = colId, .posId = posJ, .eleId = eleI}; if (storedBefore.contains(a1) || storedBefore.contains(a2)) { continue; } @@ -1708,7 +1708,7 @@ struct PhotonConversionBuilder { // fillV0Table(v0, true); stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); - stored_fullv0Ids.push_back(CandKey{v0Id, collisionId, posId, eleId}); + stored_fullv0Ids.push_back(CandKey{.v0Id = v0Id, .collisionId = collisionId, .posId = posId, .eleId = eleId}); nv0_map[collisionId]++; } } // end of pca_map loop @@ -2080,7 +2080,7 @@ struct PhotonConversionBuilder { vecV0Dedup.clear(); } // end of build - o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; + o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > static_cast(0); using FilteredV0s = o2::soa::Filtered; enum PairFate { @@ -2186,7 +2186,8 @@ struct PhotonConversionBuilder { } int choice = 3; // o2-linter: disable=magic-number (bin 3 = lost) if (f.alive) { - const bool storedRight = std::any_of(f.aliveColls.begin(), f.aliveColls.end(), [&](int64_t c) { return isRightColl(c, mid); }); + const auto& midRef = mid; // ordinary variable, not a structured binding + const bool storedRight = std::any_of(f.aliveColls.begin(), f.aliveColls.end(), [&](int64_t c) { return isRightColl(c, midRef); }); choice = storedRight ? 0 : (rightAvailable ? 1 : 2); } const int nCopies = static_cast(copyColls.size()); diff --git a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx index b2395f2fe65..6ae54d84dc1 100644 --- a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx +++ b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h" #include "Common/Core/RecoDecay.h" @@ -72,16 +71,6 @@ using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; -enum QvecEstimator { - FT0M = 0, - FT0A, - FT0C, - TPCPos, - TPCNeg, - TPCTot, - FV0A -}; - enum CentralityEstimator { None = 0, CFT0A, @@ -90,18 +79,6 @@ enum CentralityEstimator { NCentralityEstimators }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -294,6 +271,7 @@ struct CalibTaskEmc { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -363,7 +341,7 @@ struct CalibTaskEmc { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; @@ -587,25 +565,20 @@ struct CalibTaskEmc { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } @@ -714,10 +687,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; @@ -887,10 +856,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; @@ -1033,10 +998,6 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx index 43f82e92041..c18b6e319a6 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -52,7 +51,6 @@ #include #include #include -#include #include using namespace o2; @@ -136,7 +134,7 @@ struct DalitzEEQC { float d_bz = 0; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::array event_cut_types = {"before/", "after/"}; + // static constexpr std::array event_cut_types = {"before/", "after/"}; // unused void init(InitContext& /*context*/) { @@ -198,7 +196,7 @@ struct DalitzEEQC { void addhistograms() { // event info - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); // pair info const AxisSpec axis_mass{200, 0, 0.2, "m_{ee} (GeV/c^{2})"}; @@ -246,6 +244,7 @@ struct DalitzEEQC { fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void DefineDileptonCut() @@ -356,8 +355,8 @@ struct DalitzEEQC { Filter trackFilter = dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track && o2::aod::track::tpcChi2NCl < dileptoncuts.cfg_max_chi2tpc && o2::aod::track::itsChi2NCl < dileptoncuts.cfg_max_chi2its && nabs(o2::aod::track::dcaXY) < dileptoncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dileptoncuts.cfg_max_dcaz; Filter pidFilter = dileptoncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dileptoncuts.cfg_max_TPCNsigmaEl && (o2::aod::pidtpc::tpcNSigmaPi < dileptoncuts.cfg_min_TPCNsigmaPi || dileptoncuts.cfg_max_TPCNsigmaPi < o2::aod::pidtpc::tpcNSigmaPi); using FilteredMyTracks = soa::Filtered; - Partition posTracks = o2::aod::emprimaryelectron::sign > int8_t(0); - Partition negTracks = o2::aod::emprimaryelectron::sign < int8_t(0); + Partition posTracks = o2::aod::emprimaryelectron::sign > static_cast(0); + Partition negTracks = o2::aod::emprimaryelectron::sign < static_cast(0); std::vector used_trackIds; void processQC(FilteredMyCollisions const& collisions, FilteredMyTracks const& /*tracks*/) @@ -369,16 +368,12 @@ struct DalitzEEQC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision); if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision); + fEMEventCut.fillAccepted(&fRegistry); auto posTracks_per_coll = posTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); auto negTracks_per_coll = negTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx index c0dd75f3545..064dc2d0633 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -149,7 +148,7 @@ struct DalitzEEQCMC { } mctrackcuts; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::array event_cut_types = {"before/", "after/"}; + // static constexpr std::array event_cut_types = {"before/", "after/"}; // unused static constexpr std::array track_types = {"primary/", "secondary/"}; ~DalitzEEQCMC() = default; @@ -157,7 +156,7 @@ struct DalitzEEQCMC { void addhistograms() { // event info - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); const AxisSpec axis_mass{200, 0, 0.2, "m_{ee} (GeV/c^{2})"}; const AxisSpec axis_pt{200, 0, 2, "p_{T,ee} (GeV/c)"}; @@ -279,6 +278,7 @@ struct DalitzEEQCMC { fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void DefineDileptonCut() @@ -362,7 +362,8 @@ struct DalitzEEQCMC { } float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - if (mother_id > -1 && t1mc.pdgCode() * t2mc.pdgCode() < 0) { + // if (mother_id > -1 && t1mc.pdgCode() * t2mc.pdgCode() < 0) { + if (t1mc.pdgCode() * t2mc.pdgCode() < 0) { auto mcmother = mcparticles.iteratorAt(mother_id); if (cfgRequireTrueAssociation && (mcmother.emmceventId() != collision.emmceventId())) { return false; @@ -506,8 +507,8 @@ struct DalitzEEQCMC { Filter trackFilter = dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track && o2::aod::track::tpcChi2NCl < dileptoncuts.cfg_max_chi2tpc && o2::aod::track::itsChi2NCl < dileptoncuts.cfg_max_chi2its && nabs(o2::aod::track::dcaXY) < dileptoncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dileptoncuts.cfg_max_dcaz; Filter pidFilter = dileptoncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dileptoncuts.cfg_max_TPCNsigmaEl && (o2::aod::pidtpc::tpcNSigmaPi < dileptoncuts.cfg_min_TPCNsigmaPi || dileptoncuts.cfg_max_TPCNsigmaPi < o2::aod::pidtpc::tpcNSigmaPi); using FilteredMyMCTracks = soa::Filtered; - Partition posTracks = o2::aod::emprimaryelectron::sign > int8_t(0); - Partition negTracks = o2::aod::emprimaryelectron::sign < int8_t(0); + Partition posTracks = o2::aod::emprimaryelectron::sign > static_cast(0); + Partition negTracks = o2::aod::emprimaryelectron::sign < static_cast(0); Filter collisionFilter_centrality = (cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < cfgCentMax); using FilteredMyCollisions = soa::Filtered; @@ -524,16 +525,12 @@ struct DalitzEEQCMC { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision); if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision); + fEMEventCut.fillAccepted(&fRegistry); auto posTracks_per_coll = posTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); auto negTracks_per_coll = negTracks->sliceByCached(o2::aod::emprimaryelectronda::pmeventId, collision.globalIndex(), cache); @@ -567,9 +564,6 @@ struct DalitzEEQCMC { if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - continue; - } auto mccollision = collision.emmcevent_as(); auto posTracks_per_coll = posTracksMC->sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); @@ -601,34 +595,34 @@ struct DalitzEEQCMC { continue; } - if (mother_id > -1) { - auto mcmother = mcparticles.iteratorAt(mother_id); - if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { - - switch (std::abs(mcmother.pdgCode())) { - case 111: - fRegistry.fill(HIST("Generated/sm/Pi0/hMvsPt"), v12.M(), v12.Pt()); - break; - case 221: - fRegistry.fill(HIST("Generated/sm/Eta/hMvsPt"), v12.M(), v12.Pt()); - break; - case 331: - fRegistry.fill(HIST("Generated/sm/EtaPrime/hMvsPt"), v12.M(), v12.Pt()); - break; - case 113: - fRegistry.fill(HIST("Generated/sm/Rho/hMvsPt"), v12.M(), v12.Pt()); - break; - case 223: - fRegistry.fill(HIST("Generated/sm/Omega/hMvsPt"), v12.M(), v12.Pt()); - break; - case 333: - fRegistry.fill(HIST("Generated/sm/Phi/hMvsPt"), v12.M(), v12.Pt()); - break; - default: - break; - } + // if (mother_id > -1) { // not needed because of + auto mcmother = mcparticles.iteratorAt(mother_id); + if (mcmother.isPhysicalPrimary() || mcmother.producedByGenerator()) { + + switch (std::abs(mcmother.pdgCode())) { + case 111: + fRegistry.fill(HIST("Generated/sm/Pi0/hMvsPt"), v12.M(), v12.Pt()); + break; + case 221: + fRegistry.fill(HIST("Generated/sm/Eta/hMvsPt"), v12.M(), v12.Pt()); + break; + case 331: + fRegistry.fill(HIST("Generated/sm/EtaPrime/hMvsPt"), v12.M(), v12.Pt()); + break; + case 113: + fRegistry.fill(HIST("Generated/sm/Rho/hMvsPt"), v12.M(), v12.Pt()); + break; + case 223: + fRegistry.fill(HIST("Generated/sm/Omega/hMvsPt"), v12.M(), v12.Pt()); + break; + case 333: + fRegistry.fill(HIST("Generated/sm/Phi/hMvsPt"), v12.M(), v12.Pt()); + break; + default: + break; } } + // } } // end of true ULS pair loop } // end of collision loop } diff --git a/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx index 073479c2c9e..2ccf37fd92b 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include #include @@ -166,6 +165,7 @@ struct EmcalMcTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -198,7 +198,7 @@ struct EmcalMcTask { defineEMEventCut(); defineEMCCut(); fEMCCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisERec{thnConfigAxisE, "#it{E}_{Rec} (GeV)"}; @@ -272,25 +272,20 @@ struct EmcalMcTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx index 9d233f75ca0..f3f90bd7e44 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx @@ -22,7 +22,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/ParticleOrigin.h" @@ -361,6 +360,7 @@ struct EmcalPhotonMcTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -393,7 +393,7 @@ struct EmcalPhotonMcTask { defineEMEventCut(); defineEMCCut(); fEMCCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; @@ -568,25 +568,20 @@ struct EmcalPhotonMcTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx index def4442d5d5..b8007742c20 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx @@ -20,7 +20,6 @@ #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/ClusterHistograms.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" @@ -118,6 +117,7 @@ struct EmcalQC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgOccupancyMin, eventcuts.cfgOccupancyMax); } void defineEMCCut() @@ -159,7 +159,7 @@ struct EmcalQC { const AxisSpec thAxisdEta{200, -0.06, 0.06, "#Delta#eta"}; const AxisSpec thAxisdPhi{200, -0.06, 0.06, "#Delta#varphi (rad)"}; - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); auto hEMCCollisionCounter = fRegistry.add("Event/hEMCCollisionCounter", "Number of collisions after event cuts", HistType::kTH1D, {{7, 0.5, 7.5}}, false); hEMCCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); hEMCCollisionCounter->GetXaxis()->SetBinLabel(2, "+TVX"); // TVX @@ -210,17 +210,13 @@ struct EmcalQC { } } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, collision.weight()); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, collision.weight()); if (!fEMEventCut.IsSelected(collision)) { return false; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { - return false; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, collision.weight()); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0, collision.weight()); // accepted - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, collision.weight()); // accepted + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, collision.weight()); + fEMEventCut.fillAccepted(&fRegistry, collision.weight()); return true; } diff --git a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx index d2d05d32ae5..5844fab6531 100644 --- a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx @@ -18,7 +18,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/AnalyticV0.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -389,7 +388,7 @@ struct PairQCTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); fRegistry.add("Photon/hPtEtaPhi", "selected V0 photons;p_{T} (GeV/c);#eta;#varphi (rad)", kTH3F, {{100, 0.f, 2.f}, {40, -0.8f, 0.8f}, {72, 0.f, 6.2832f}}, true); // o2-linter: disable=magic-number (axis definition) fRegistry.add("Photon/hNPhotonsPerEvent", "selected V0 photons per event;N_{#gamma};events", kTH1F, {{21, -0.5f, 20.5f}}, true); fRegistry.add("Photon/hV0LegClass", "leg classes of the selected V0s;;V0s", kTH1F, {makeAxisEnum(kNV0Classes, kV0ClassTitle)}, true); @@ -442,6 +441,8 @@ struct PairQCTask { fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void definePCMCut() @@ -1004,7 +1005,7 @@ struct PairQCTask { } if (mcutil::classifyDupPair(a.mc, b.mc) == mcutil::DupClass::SameMcPhoton) { // 0 no split leg, 1 split e+, 2 split e-, 3 both - const float splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) + const auto splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType"), splitType); fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged"), splitType, flagged ? 1.f : 0.f); fRegistry.fill(HIST("Dedup/MC/hDupTrackClassMatrix"), static_cast(a.dedupCand.nITSTPC), static_cast(b.dedupCand.nITSTPC)); @@ -1413,13 +1414,12 @@ struct PairQCTask { centralitySelection.cfgCentMax < cent[centralitySelection.cfgCentEstimator]) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); auto photonsColl = photons.sliceBy(perCollisionPCM, collision.globalIndex()); collectPhotons(photonsColl, mcParticles, collision.posZ(), cands); @@ -1454,7 +1454,7 @@ struct PairQCTask { PROCESS_SWITCH(PairQCTask, processMC, "pair QC on MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx index a51c8533f95..1e8e04cee93 100644 --- a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx @@ -21,7 +21,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "Common/Core/RecoDecay.h" @@ -211,6 +210,7 @@ struct PhotonResoTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -283,7 +283,7 @@ struct PhotonResoTask { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; @@ -405,25 +405,20 @@ struct PhotonResoTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx index 23cb1db4a70..fa490de325d 100644 --- a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx @@ -19,7 +19,6 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -809,6 +808,8 @@ struct Photonhbt { fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void DefinePCMCut() @@ -973,7 +974,7 @@ struct Photonhbt { { static constexpr std::array det = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg"}; - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fEMEventCut.addEventHistograms(&fRegistry); fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); @@ -2657,13 +2658,12 @@ struct Photonhbt { collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); const float occupancy = (mixing.cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) @@ -2888,13 +2888,12 @@ struct Photonhbt { collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + fEMEventCut.fillEventInfo<0>(&fRegistry, collision, 1.f); if (!fEMEventCut.IsSelected(collision)) { continue; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); - fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); + fEMEventCut.fillEventInfo<1>(&fRegistry, collision, 1.f); + fEMEventCut.fillAccepted(&fRegistry); fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); const float occupancy = (mixing.cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) diff --git a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx index 6c4b0ecaed8..dcd322c2ef7 100644 --- a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx @@ -16,7 +16,7 @@ #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -35,9 +35,7 @@ #include -#include #include -#include #include #include #include @@ -48,16 +46,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A, - FT0C, - TPCPos, - TPCNeg, - TPCTot, - FV0A -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -67,18 +56,6 @@ enum CentralityEstimator { NCentralityEstimators }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - struct TaskFlowReso { // configurable for flow @@ -123,8 +100,6 @@ struct TaskFlowReso { using CollsWithQvecs = soa::Join; using CollWithQvec = CollsWithQvecs::iterator; - static constexpr std::size_t NQVecEntries = 6; - HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; void defineEMEventCut() @@ -140,6 +115,12 @@ struct TaskFlowReso { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetTrackOccupancyRange(eventcuts.cfgTrackOccupancyMin, eventcuts.cfgTrackOccupancyMax); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void init(InitContext&) @@ -149,7 +130,7 @@ struct TaskFlowReso { } defineEMEventCut(); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; const AxisSpec thnAxisCosNPhi{thnConfigAxisCosNPhi, Form("cos(%d#varphi)", harmonic.value)}; @@ -262,142 +243,29 @@ struct TaskFlowReso { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } // Resolution void processResolution(CollWithQvec const& collision) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); if (!(fEMEventCut.IsSelected(collision))) { // no selection on the centrality is applied on purpose to allow for the resolution study in post-processing return; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange()) || !(collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - return; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return; - } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); float centrality = getCentrality(collision); // centrality not updated in the rejection mask function float xQVecFT0a = -999.f; diff --git a/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx index 41f47171448..9ef5ba0e761 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx @@ -20,7 +20,7 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -48,7 +48,6 @@ #include #include -#include #include #include #include @@ -61,16 +60,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A = 1, - FT0C = 2, - TPCPos = 3, - TPCNeg = 4, - TPCTot = 5, - FV0A = 6 -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -80,18 +70,6 @@ enum CentralityEstimator { NCentralityEstimators = 4 }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -220,8 +198,6 @@ struct TaskPhotonFlow { Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; - static constexpr std::size_t NQVecEntries = 6; - PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; @@ -274,6 +250,11 @@ struct TaskPhotonFlow { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void defineEMCCut() @@ -342,7 +323,7 @@ struct TaskPhotonFlow { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; @@ -428,117 +409,12 @@ struct TaskPhotonFlow { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) @@ -638,29 +514,20 @@ struct TaskPhotonFlow { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return false; - } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index 230ad975aec..5e518f8f507 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -20,7 +20,7 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/EventUtilities.h" #include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" @@ -59,7 +59,6 @@ #include #include -#include #include #include #include @@ -73,16 +72,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; - -enum QvecEstimator { - FT0M = 0, - FT0A = 1, - FT0C = 2, - TPCPos = 3, - TPCNeg = 4, - TPCTot = 5, - FV0A = 6 -}; +using namespace o2::aod::pwgem::pm::utils::event; enum CentralityEstimator { None = 0, @@ -92,18 +82,6 @@ enum CentralityEstimator { NCentralityEstimators = 4 }; -enum Harmonics { - kNone = 0, - kDirect = 1, - kElliptic = 2, - kTriangluar = 3, - kQuadrangular = 4, - kPentagonal = 5, - kHexagonal = 6, - kHeptagonal = 7, - kOctagonal = 8 -}; - enum class MapLevel { kGood = 1, kNoBad = 2, @@ -275,8 +253,6 @@ struct TaskPi0FlowEMC { Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; - static constexpr std::size_t NQVecEntries = 6; - PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; @@ -335,6 +311,11 @@ struct TaskPi0FlowEMC { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + fEMEventCut.SetRequireGoodQvec(true); + fEMEventCut.SetQvecHarmonic(harmonic); + fEMEventCut.SetMaxQvec(cfgMaxQVector); + fEMEventCut.SetQvecDetectors({qvecDetector.value, qvecSubADetector.value, qvecSubBDetector.value}); } void defineEMCCut() @@ -403,7 +384,7 @@ struct TaskPi0FlowEMC { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; @@ -618,117 +599,12 @@ struct TaskPi0FlowEMC { return cent; } - /// Get all used Q vector - /// \param collision is the collision with the Q vector information - template - std::array getAllQvec(TCollision const& collision) - { - // Retrieve the Q vectors using the helper function for each detector - auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); - auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); - auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); - - return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; - } - /// Get the Q vector /// \param collision is the collision with the Q vector information template std::pair getQvec(TCollision const& collision, int detector) { - float xQVec = -999.f; - float yQVec = -999.f; - - switch (detector) { - case QvecEstimator::FT0M: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - case QvecEstimator::FT0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0a(); - yQVec = collision.q2yft0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0a(); - yQVec = collision.q3yft0a(); - } - break; - case QvecEstimator::FT0C: - if (harmonic == kElliptic) { - xQVec = collision.q2xft0c(); - yQVec = collision.q2yft0c(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0c(); - yQVec = collision.q3yft0c(); - } - break; - case QvecEstimator::TPCPos: - if (harmonic == kElliptic) { - xQVec = collision.q2xbpos(); - yQVec = collision.q2ybpos(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbpos(); - yQVec = collision.q3ybpos(); - } - break; - case QvecEstimator::TPCNeg: - if (harmonic == kElliptic) { - xQVec = collision.q2xbneg(); - yQVec = collision.q2ybneg(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbneg(); - yQVec = collision.q3ybneg(); - } - break; - case QvecEstimator::TPCTot: - if (harmonic == kElliptic) { - xQVec = collision.q2xbtot(); - yQVec = collision.q2ybtot(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xbtot(); - yQVec = collision.q3ybtot(); - } - break; - case QvecEstimator::FV0A: - if (harmonic == kElliptic) { - xQVec = collision.q2xfv0a(); - yQVec = collision.q2yfv0a(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xfv0a(); - yQVec = collision.q3yfv0a(); - } - break; - default: - LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; - if (harmonic == kElliptic) { - xQVec = collision.q2xft0m(); - yQVec = collision.q2yft0m(); - } else if (harmonic == kTriangluar) { - xQVec = collision.q3xft0m(); - yQVec = collision.q3yft0m(); - } - break; - } - return {xQVec, yQVec}; - } - - /// Check if the QVector values are within reasonable range - /// \param collision is the collision with the Q vector information - bool isQvecGood(std::array const& QVecs) - { - bool isgood = true; - for (const auto& QVec : QVecs) { - if (std::fabs(QVec) > cfgMaxQVector) { - isgood = false; - break; - } - } - return isgood; + return o2::aod::pwgem::pm::utils::event::getQvec(collision, detector, harmonic); } bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) @@ -1068,29 +944,20 @@ struct TaskPi0FlowEMC { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float cent = getCentrality(collision); if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { // event selection return false; } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector - return false; - } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } @@ -1237,18 +1104,10 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; } - if (!isQvecGood(getAllQvec(c1)) || !isQvecGood(getAllQvec(c2))) { - // selection based on QVector - continue; - } runNow = c1.runNumber(); if (runNow != runBefore) { initCCDB(c1); @@ -1456,18 +1315,10 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; } - if (!isQvecGood(getAllQvec(c1)) || !isQvecGood(getAllQvec(c2))) { - // selection based on QVector - continue; - } runNow = c1.runNumber(); if (runNow != runBefore) { initCCDB(c1); @@ -1673,10 +1524,6 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } if (getCentrality(c1) < eventcuts.cfgMinCent || getCentrality(c1) > eventcuts.cfgMaxCent || getCentrality(c2) < eventcuts.cfgMinCent || getCentrality(c2) > eventcuts.cfgMaxCent) { // event selection continue; diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx index 348f54a7868..9f080d9a02d 100644 --- a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -21,7 +21,6 @@ #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "Common/DataModel/Centrality.h" @@ -355,6 +354,7 @@ struct TwoPhotonMcTask { fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); } void defineEMCCut() @@ -425,7 +425,7 @@ struct TwoPhotonMcTask { fEMCCut.addQAHistograms(®istry); definePCMCut(); fV0PhotonCut.addQAHistograms(®istry); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; @@ -620,25 +620,20 @@ struct TwoPhotonMcTask { bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) { if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + fEMEventCut.fillEventInfo<0>(®istry, collision); } if (!(fEMEventCut.IsSelected(collision))) { // general event selection return false; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - return false; - } float centOrMult = getCentralityOrMultiplicity(collision); if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { // event selection return false; } if (fillHisto) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); } return true; } diff --git a/PWGEM/PhotonMeson/Utils/EventHistograms.h b/PWGEM/PhotonMeson/Utils/EventHistograms.h deleted file mode 100644 index f9f9fcdec0b..00000000000 --- a/PWGEM/PhotonMeson/Utils/EventHistograms.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EventHistograms.h -/// \brief header file for event histograms -/// \author daiki.sekihata@cern.ch - -#ifndef PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ -#define PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" - -#include -#include -#include - -#include - -#include -#include - -namespace o2::aod::pwgem::photonmeson::utils::eventhistogram -{ -inline void addEventHistograms(o2::framework::HistogramRegistry* fRegistry, bool useWeight = false) -{ - // event info - auto hCollisionCounter = fRegistry->add("Event/before/hCollisionCounter", "collision counter;;Number of events", o2::framework::HistType::kTH1D, {{12, 0.5, 12.5}}, useWeight); - hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); - hCollisionCounter->GetXaxis()->SetBinLabel(2, "No TF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(3, "No ITS ROF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(4, "No Same Bunch Pileup"); - hCollisionCounter->GetXaxis()->SetBinLabel(5, "Is Vertex ITSTPC"); - hCollisionCounter->GetXaxis()->SetBinLabel(6, "Is Good Zvtx FT0vsPV"); - hCollisionCounter->GetXaxis()->SetBinLabel(7, "FT0AND"); - hCollisionCounter->GetXaxis()->SetBinLabel(8, "sel8"); - hCollisionCounter->GetXaxis()->SetBinLabel(9, "|Z_{vtx}| < 10 cm"); - hCollisionCounter->GetXaxis()->SetBinLabel(10, "EMC MB Readout"); - hCollisionCounter->GetXaxis()->SetBinLabel(11, "EMC L0 Triggered"); - hCollisionCounter->GetXaxis()->SetBinLabel(12, "accepted"); - - fRegistry->add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{220, -11, +11}}, useWeight); - fRegistry->add("Event/before/hMultNTracksPV", "hMultNTracksPV; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); - fRegistry->add("Event/before/hMultNTracksPVeta1", "hMultNTracksPVeta1; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); - fRegistry->add("Event/before/hMultFT0", "hMultFT0;mult. FT0A;mult. FT0C", o2::framework::HistType::kTH2F, {{200, 0, 200000}, {60, 0, 60000}}, useWeight); - fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); - fRegistry->add("Event/before/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", o2::framework::HistType::kTH2F, {{110, 0, 110}, {500, 0, 5000}}, useWeight); - fRegistry->add("Event/before/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 5000}}, useWeight); - fRegistry->add("Event/before/hMultFT0CvsOccupancy", "hMultFT0CvsOccupancy;mult. FT0C;N_{track} in time range", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 10000}}, useWeight); - fRegistry->addClone("Event/before/", "Event/after/"); -} - -template -void fillEventInfo(o2::framework::HistogramRegistry* fRegistry, TCollision const& collision, float weight = 1.) -{ - const float maxZ = 10.f; - static constexpr std::array EventTypes = {"before/", "after/"}; - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 1.0, weight); - if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 2.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 3.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 4.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 5.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 6.0, weight); - } - if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 7.0, weight); - } - if (collision.sel8()) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 8.0, weight); - } - if (std::abs(collision.posZ()) < maxZ) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 9.0, weight); - } - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hZvtx"), collision.posZ(), weight); - if (collision.alias_bit(kTVXinEMC)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 10.0, weight); - } - if (collision.alias_bit(kEMC7) || collision.alias_bit(kDMC7)) { - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 11.0, weight); - } - - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPV"), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultNTracksPVeta1"), collision.multNTracksPVeta1(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0"), collision.multFT0A(), collision.multFT0C(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0A"), collision.centFT0A(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0C"), collision.centFT0C(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0M"), collision.centFT0M(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCentFT0CvsMultNTracksPV"), collision.centFT0C(), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV(), weight); - fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hMultFT0CvsOccupancy"), collision.multFT0C(), collision.trackOccupancyInTimeRange(), weight); -} - -} // namespace o2::aod::pwgem::photonmeson::utils::eventhistogram - -#endif // PWGEM_PHOTONMESON_UTILS_EVENTHISTOGRAMS_H_ diff --git a/PWGEM/PhotonMeson/Utils/EventUtilities.h b/PWGEM/PhotonMeson/Utils/EventUtilities.h new file mode 100644 index 00000000000..bffd220a945 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/EventUtilities.h @@ -0,0 +1,178 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file EventUtilities.h +/// \brief Utilities for event level variables (e.g. Q vectors) used in PWGEM/PhotonMeson +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ +#define PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ + +#include +#include + +#include +#include +#include +#include + +namespace o2::aod::pwgem::pm::utils::event +{ + +/// Detectors that provide a Q vector in the EMEventsQvec tables +enum QvecEstimator { + FT0M = 0, + FT0A = 1, + FT0C = 2, + TPCPos = 3, + TPCNeg = 4, + TPCTot = 5, + FV0A = 6 +}; + +/// Supported flow harmonics +enum Harmonics { + kNone = 0, + kDirect = 1, + kElliptic = 2, + kTriangluar = 3, + kQuadrangular = 4, + kPentagonal = 5, + kHexagonal = 6, + kHeptagonal = 7, + kOctagonal = 8 +}; + +/// Value returned for Q vectors that are not available +constexpr float InvalidQvec = -999.f; + +// Compile-time checks for the availability of Q vector columns. +// EMEventsQvec_000 has q2 and q3 without FV0A, EMEventsQvec_001 has q2 and q3 with FV0A, +// EMEventsQvec2 has only q2 (with FV0A) and EMEventsQvec3 has only q3 (with FV0A). +template +concept HasQvec2 = requires(T const& c) { c.q2xft0m(); }; + +template +concept HasQvec3 = requires(T const& c) { c.q3xft0m(); }; + +template +concept HasQvec2FV0A = requires(T const& c) { c.q2xfv0a(); }; + +template +concept HasQvec3FV0A = requires(T const& c) { c.q3xfv0a(); }; + +template +concept HasQvec = HasQvec2 || HasQvec3; + +// Compile-time checks for the availability of the occupancy columns (evsel::NumTracksInTimeRange and evsel::SumAmpFT0CInTimeRange) +template +concept HasTrackOccupancy = requires(T const& c) { c.trackOccupancyInTimeRange(); }; + +template +concept HasFT0COccupancy = requires(T const& c) { c.ft0cOccupancyInTimeRange(); }; + +/// \brief Get the Q vector of a given detector and harmonic +/// \param collision collision with the Q vector information +/// \param detector detector used for the Q vector (see QvecEstimator) +/// \param harmonic harmonic of the Q vector (see Harmonics) +/// \return pair of (Qx, Qy). {InvalidQvec, InvalidQvec} if not available in the table or for an unsupported harmonic +template +std::pair getQvec(TCollision const& collision, int detector, int harmonic) +{ + if (harmonic == kElliptic) { + if constexpr (HasQvec2) { + switch (detector) { + case QvecEstimator::FT0M: + return {collision.q2xft0m(), collision.q2yft0m()}; + case QvecEstimator::FT0A: + return {collision.q2xft0a(), collision.q2yft0a()}; + case QvecEstimator::FT0C: + return {collision.q2xft0c(), collision.q2yft0c()}; + case QvecEstimator::TPCPos: + return {collision.q2xbpos(), collision.q2ybpos()}; + case QvecEstimator::TPCNeg: + return {collision.q2xbneg(), collision.q2ybneg()}; + case QvecEstimator::TPCTot: + return {collision.q2xbtot(), collision.q2ybtot()}; + case QvecEstimator::FV0A: + if constexpr (HasQvec2FV0A) { + return {collision.q2xfv0a(), collision.q2yfv0a()}; + } else { + return {InvalidQvec, InvalidQvec}; + } + default: + LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; + return {collision.q2xft0m(), collision.q2yft0m()}; + } + } + } else if (harmonic == kTriangluar) { + if constexpr (HasQvec3) { + switch (detector) { + case QvecEstimator::FT0M: + return {collision.q3xft0m(), collision.q3yft0m()}; + case QvecEstimator::FT0A: + return {collision.q3xft0a(), collision.q3yft0a()}; + case QvecEstimator::FT0C: + return {collision.q3xft0c(), collision.q3yft0c()}; + case QvecEstimator::TPCPos: + return {collision.q3xbpos(), collision.q3ybpos()}; + case QvecEstimator::TPCNeg: + return {collision.q3xbneg(), collision.q3ybneg()}; + case QvecEstimator::TPCTot: + return {collision.q3xbtot(), collision.q3ybtot()}; + case QvecEstimator::FV0A: + if constexpr (HasQvec3FV0A) { + return {collision.q3xfv0a(), collision.q3yfv0a()}; + } else { + return {InvalidQvec, InvalidQvec}; + } + default: + LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; + return {collision.q3xft0m(), collision.q3yft0m()}; + } + } + } + return {InvalidQvec, InvalidQvec}; +} + +/// \brief Check if all given Q vector components are within a reasonable range +/// \param qvecs Q vector components (e.g. {Qx_1, Qy_1, Qx_2, Qy_2, ...}) +/// \param maxQvec maximum allowed absolute value of each component +inline bool isQvecGood(std::span qvecs, float maxQvec) +{ + for (const auto& qvec : qvecs) { + if (std::fabs(qvec) > maxQvec) { + return false; + } + } + return true; +} + +/// \brief Check if the Q vectors of all given detectors are within a reasonable range +/// \param collision collision with the Q vector information +/// \param detectors detectors to check (see QvecEstimator) +/// \param harmonic harmonic of the Q vectors (see Harmonics) +/// \param maxQvec maximum allowed absolute value of Qx and Qy +template +bool isQvecGood(TCollision const& collision, std::vector const& detectors, int harmonic, float maxQvec) +{ + for (const auto& detector : detectors) { + auto [xQvec, yQvec] = getQvec(collision, detector, harmonic); + if (std::fabs(xQvec) > maxQvec || std::fabs(yQvec) > maxQvec) { + return false; + } + } + return true; +} + +} // namespace o2::aod::pwgem::pm::utils::event + +#endif // PWGEM_PHOTONMESON_UTILS_EVENTUTILITIES_H_ From cc0fcac8b484b2e0c1fea0fb95e48077638ee14c Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Thu, 5 Nov 2026 19:53:18 +0530 Subject: [PATCH 577/648] [PWGCF] LP BF Update 10 - Update in table (#18602) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index cfb211bebe2..7dc7595998b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -67,7 +67,7 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using MyTracks = soa::Join; From 2004fc98faf3b1f609ad74e39a018776207d2b2c Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Thu, 5 Nov 2026 16:46:12 +0100 Subject: [PATCH 578/648] =?UTF-8?q?[PWGJE]=20EMCal:=20event=20selection=20?= =?UTF-8?q?for=20emcTmMonitor,=20track=20matching=20optio=E2=80=A6=20(#186?= =?UTF-8?q?06)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGJE/TableProducer/emcalCorrectionTask.cxx | 13 +- PWGJE/Tasks/emcTmMonitor.cxx | 276 ++++++++++++++++++-- 2 files changed, 256 insertions(+), 33 deletions(-) diff --git a/PWGJE/TableProducer/emcalCorrectionTask.cxx b/PWGJE/TableProducer/emcalCorrectionTask.cxx index 985e622f7fa..7ccafbfef24 100644 --- a/PWGJE/TableProducer/emcalCorrectionTask.cxx +++ b/PWGJE/TableProducer/emcalCorrectionTask.cxx @@ -133,7 +133,6 @@ struct EmcalCorrectionTask { Configurable useWeightExotic{"useWeightExotic", false, "States if weights should be used for exotic cell cut"}; Configurable isMC{"isMC", false, "States if run over MC"}; Configurable applyCellTimeCorrection{"applyCellTimeCorrection", true, "apply a correction to the cell time for data and MC: Shift both average cell times to 0 and smear MC time distribution to fit data better. For MC requires isMC to be true"}; - Configurable trackMinPt{"trackMinPt", 0.3, "Minimum pT for tracks to perform track matching, to reduce computing time. Tracks below a certain pT will be loopers anyway."}; Configurable fillQA{"fillQA", false, "Switch to turn on QA histograms."}; Configurable useCCDBAlignment{"useCCDBAlignment", false, "EXPERTS ONLY! Switch to use the alignment object stored in CCDB instead of using the default alignment from the global geometry object."}; Configurable applyTempCalib{"applyTempCalib", false, "Switch to turn on Temperature calibration."}; @@ -147,6 +146,12 @@ struct EmcalCorrectionTask { Configurable storePerDFInfo{"storePerDFInfo", false, "store addition information per DF."}; ConfigurableAxis thConfigAxisClusters{"thConfigAxisClusters", {1000, 0.5f, 1000.5f}, ""}; ConfigurableAxis thConfigAxisCells{"thConfigAxisCells", {1000, 0.5f, 1000.5f}, ""}; + // Track matching config + struct : ConfigurableGroup { + std::string prefix = "trackmatching"; + Configurable trackMinPt{"trackMinPt", 0.3, "Minimum pT (GeV) for tracks to perform track matching, to reduce computing time. Tracks below a certain pT will be loopers anyway."}; + Configurable useAllTracks{"useAllTracks", false, "Flag to use all tracks instead of just global tracks."}; + } trackmatching; // cross talk emulation configs EmcCrossTalkConf emcCrossTalkConf; @@ -1494,7 +1499,7 @@ struct EmcalCorrectionTask { if (trackEtaEmcal < TrackNotOnEMCal || trackPhiEmcal < TrackNotOnEMCal) { continue; } - if (trackMinPt > 0 && track.pt() < trackMinPt) { + if (trackmatching.trackMinPt > 0 && track.pt() < trackmatching.trackMinPt) { continue; } trackPhi.emplace_back(RecoDecay::constrainAngle(trackPhiEmcal)); @@ -1508,7 +1513,7 @@ struct EmcalCorrectionTask { void fillTrackInfo(Tracks const& tracks, std::vector& trackPhi, std::vector& trackEta, std::vector& trackGlobalIndex) { for (const auto& track : tracks) { - if (!track.isGlobalTrack()) { // only global tracks + if (!trackmatching.useAllTracks && !track.isGlobalTrack()) { // only global tracks depending on trackmatching.useAllTracks flag continue; } // Tracks that do not point to the EMCal/DCal/PHOS get default values of -999 @@ -1516,7 +1521,7 @@ struct EmcalCorrectionTask { if (track.trackEtaEmcal() < TrackNotOnEMCal || track.trackPhiEmcal() < TrackNotOnEMCal) { continue; } - if (trackMinPt > 0 && track.pt() < trackMinPt) { + if (trackmatching.trackMinPt > 0 && track.pt() < trackmatching.trackMinPt) { continue; } trackPhi.emplace_back(RecoDecay::constrainAngle(track.trackPhiEmcal())); diff --git a/PWGJE/Tasks/emcTmMonitor.cxx b/PWGJE/Tasks/emcTmMonitor.cxx index cef13ebaf16..c1af9d514da 100644 --- a/PWGJE/Tasks/emcTmMonitor.cxx +++ b/PWGJE/Tasks/emcTmMonitor.cxx @@ -19,9 +19,10 @@ /// - cluster energy over track momentum /// - difference in eta /// - difference in phi -/// Simple event selection using the flag doEventSel is provided, which selects INT7 events if set to 1 -/// For pilot beam data, instead of relying on the event selection, one can veto specific BC IDS using the flag -/// fDoVetoBCID. +/// The event selection (eventCuts) applies the standard cuts of the event selection task: sel8, z vertex, the evsel +/// flags (pile-up, ITS/TF frame borders, vertex quality, ITS layers), occupancy, RCT flags and a centrality or +/// multiplicity range with a configurable estimator. Optionally EMCal in the readout (Run 2: kINT7, Run 3: kTVXinEMC). +/// For pilot beam data, specific BC IDs can be vetoed (vetoBCID) or selected (selectBCID). /// The MC process (processMCTruth) follows the leading MC particle of each cluster through the matching chain: /// is its track reconstructed, extrapolated to the EMCal, a matching candidate (global track or V0 leg), /// and matched to the cluster as primary or secondary track. @@ -29,11 +30,16 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" // for the V0 legs used in the secondary matching #include "PWGJE/DataModel/EMCALClusters.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include @@ -58,6 +64,7 @@ #include #include +#include #include #include #include @@ -68,11 +75,12 @@ #include #include #include +#include #include using namespace o2::framework; using namespace o2::framework::expressions; -using CollisionEvSelIt = o2::soa::Join::iterator; +using CollisionEvSelIt = o2::soa::Join::iterator; using BcEvSelIt = o2::soa::Join::iterator; using SelectedClusters = o2::soa::Filtered; using TracksPID = o2::soa::Join; @@ -113,6 +121,47 @@ enum class TmTrackType : uint8_t { kNTypes }; +/// \brief Steps of the event selection, bins of the cut flow histogram (a disabled cut passes every event) +enum class EventCut : uint8_t { + kAll = 0, + kBC, + kSel8, + kEmcalReadout, + kZVertex, + kNoTimeFrameBorder, + kNoITSROFrameBorder, + kNoSameBunchPileup, + kGoodZvtxFT0vsPV, + kVertexITSTPC, + kVertexTOFmatched, + kNoCollInTimeRangeStandard, + kNoCollInTimeRangeNarrow, + kNoCollInRofStandard, + kNoHighMultCollInPrevRof, + kGoodITSLayer3, + kGoodITSLayer0123, + kGoodITSLayersAll, + kTrackOccupancy, + kFT0COccupancy, + kRCT, + kCentMult, + kSelected, + kNCuts +}; + +/// \brief Estimator of the centrality / multiplicity cut +enum class CentMultEstimator : int { + kOff = 0, + kCentFT0C, + kCentFT0M, + kCentFT0A, + kMultFT0C, + kMultFT0A, + kMultFT0M, + kMultNTracksPV, + kNEstimators +}; + struct EmcTmMonitor { HistogramRegistry mHistManager{"TrackMatchingMonitorHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; o2::emcal::Geometry* mGeometry = nullptr; @@ -123,11 +172,10 @@ struct EmcTmMonitor { Preslice perClusterMatchedTracks = o2::aod::emcalclustercell::emcalclusterId; Preslice perClusterMatchedSecondaries = o2::aod::emcalclustercell::emcalclusterId; // configurable parameters - // TODO adapt doEventSel switch to also allow selection of other triggers (e.g. EMC7) - Configurable doEventSel{"doEventSel", false, "demand kINT7"}; + Configurable doEventSel{"doEventSel", false, "deprecated, use eventCuts.requireEmcalReadout: demand kINT7 (Run 2) / kTVXinEMC (Run 3)"}; Configurable vetoBCID{"vetoBCID", "", "BC ID(s) to be excluded, this should be used as an alternative to the event selection"}; Configurable selectBCID{"selectBCID", "all", "BC ID(s) to be included, this should be used as an alternative to the event selection"}; - Configurable vertexCut{"vertexCut", -1, "apply z-vertex cut with value in cm"}; + Configurable vertexCut{"vertexCut", -1, "deprecated, use eventCuts.zVertexMax: if > 0, overrides it"}; Configurable clusterDefinition{"clusterDefinition", 10, "cluster definition to be selected, e.g. 10=kV3Default"}; ConfigurableAxis clusterTimeBinning{"clusterTimeBinning", {1500, -600, 900}, ""}; Configurable usePionRejection{"usePionRejection", false, "demand pion rection for electron signal with TPC PID"}; @@ -146,8 +194,40 @@ struct EmcTmMonitor { Configurable hasTRD{"hasTRD", false, "demand track to have a hit in TRD."}; Configurable emcalRadius{"emcalRadius", 440.f, "MC truth: radius (cm) used to move the cluster eta from the vertex to the origin frame of the track extrapolation"}; + struct : ConfigurableGroup { + std::string prefix = "eventCuts"; + Configurable requireSel8{"requireSel8", true, "require sel8"}; + Configurable requireEmcalReadout{"requireEmcalReadout", false, "require EMCal in the readout: kINT7 (Run 2) / kTVXinEMC (Run 3)"}; + Configurable zVertexMax{"zVertexMax", 10.f, "maximum |z| of the primary vertex (cm), <= 0: off"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", false, "require kNoTimeFrameBorder"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", false, "require kNoITSROFrameBorder"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "require kNoSameBunchPileup"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "require kIsGoodZvtxFT0vsPV"}; + Configurable requireVertexITSTPC{"requireVertexITSTPC", false, "require kIsVertexITSTPC"}; + Configurable requireVertexTOFmatched{"requireVertexTOFmatched", false, "require kIsVertexTOFmatched"}; + Configurable requireNoCollInTimeRangeStandard{"requireNoCollInTimeRangeStandard", false, "require kNoCollInTimeRangeStandard"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "require kNoCollInTimeRangeNarrow"}; + Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", false, "require kNoCollInRofStandard"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "require kNoHighMultCollInPrevRof"}; + Configurable requireGoodITSLayer3{"requireGoodITSLayer3", false, "require kIsGoodITSLayer3"}; + Configurable requireGoodITSLayer0123{"requireGoodITSLayer0123", false, "require kIsGoodITSLayer0123"}; + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "require kIsGoodITSLayersAll"}; + Configurable trackOccupancyMin{"trackOccupancyMin", -1.f, "minimum track occupancy (trackOccupancyInTimeRange), < 0: off"}; + Configurable trackOccupancyMax{"trackOccupancyMax", -1.f, "maximum track occupancy (trackOccupancyInTimeRange), < 0: off"}; + Configurable ft0cOccupancyMin{"ft0cOccupancyMin", -1.f, "minimum FT0C occupancy (ft0cOccupancyInTimeRange), < 0: off"}; + Configurable ft0cOccupancyMax{"ft0cOccupancyMax", -1.f, "maximum FT0C occupancy (ft0cOccupancyInTimeRange), < 0: off"}; + Configurable applyRCT{"applyRCT", false, "apply the RCT flags of rctLabel"}; + Configurable rctLabel{"rctLabel", "CBT_calo", "RCT selection label: CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo"}; + Configurable rctCheckZDC{"rctCheckZDC", false, "RCT: also require good ZDC quality (Pb-Pb)"}; + Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "RCT: reject runs flagged LimitedAcceptanceMCReproducible"}; + Configurable centMultEstimator{"centMultEstimator", 0, "centrality / multiplicity cut: 0 = off, 1 = cent FT0C, 2 = cent FT0M, 3 = cent FT0A, 4 = mult FT0C, 5 = mult FT0A, 6 = mult FT0M, 7 = mult NTracksPV"}; + Configurable centMultMin{"centMultMin", 0.f, "minimum centrality (%) / multiplicity"}; + Configurable centMultMax{"centMultMax", 100.f, "maximum centrality (%) / multiplicity"}; + } eventCuts; + std::vector mVetoBCIDs; std::vector mSelectBCIDs; + o2::aod::rctsel::RCTFlagsChecker mRctChecker; /// \brief Create output histograms and initialize geometry void init(InitContext const&) @@ -182,6 +262,37 @@ struct EmcTmMonitor { mHistManager.add("eventsSelected", "Number of events", HistType::kTH1F, {{1, 0.5, 1.5}}); mHistManager.add("eventVertexZAll", "z-vertex of event (all events)", HistType::kTH1F, {{200, -20, 20}}); mHistManager.add("eventVertexZSelected", "z-vertex of event (selected events)", HistType::kTH1F, {{200, -20, 20}}); + auto hCutFlow = mHistManager.add("Event/hCutFlow", "events after each step of the event selection (a disabled cut passes every event);;events", HistType::kTH1D, {{static_cast(EventCut::kNCuts), -0.5, static_cast(EventCut::kNCuts) - 0.5}}); + static constexpr std::array(EventCut::kNCuts)> CutLabels = {"all", "BC ID", "sel8", "EMCal readout", "z vertex", "NoTimeFrameBorder", "NoITSROFrameBorder", "NoSameBunchPileup", "GoodZvtxFT0vsPV", "VertexITSTPC", "VertexTOFmatched", "NoCollInTimeRangeStandard", "NoCollInTimeRangeNarrow", "NoCollInRofStandard", "NoHighMultCollInPrevRof", "GoodITSLayer3", "GoodITSLayer0123", "GoodITSLayersAll", "track occupancy", "FT0C occupancy", "RCT", "cent/mult", "selected"}; + for (std::size_t i = 0; i < CutLabels.size(); ++i) { + hCutFlow->GetXaxis()->SetBinLabel(static_cast(i) + 1, CutLabels[i].data()); + } + const AxisSpec trackOccupancyAxis{200, 0., 20000., "track occupancy"}; + const AxisSpec ft0cOccupancyAxis{200, 0., 200000., "FT0C occupancy"}; + const bool isCentrality = eventCuts.centMultEstimator.value <= static_cast(CentMultEstimator::kCentFT0A); + const AxisSpec centMultAxis{isCentrality ? AxisSpec{110, -5., 105., "centrality (%)"} : AxisSpec{500, 0., 50000., "multiplicity"}}; + mHistManager.add("Event/hTrackOccupancyBefore", "track occupancy (before the event selection);;events", HistType::kTH1D, {trackOccupancyAxis}); + mHistManager.add("Event/hTrackOccupancyAfter", "track occupancy (after the event selection);;events", HistType::kTH1D, {trackOccupancyAxis}); + mHistManager.add("Event/hFT0COccupancyBefore", "FT0C occupancy (before the event selection);;events", HistType::kTH1D, {ft0cOccupancyAxis}); + mHistManager.add("Event/hFT0COccupancyAfter", "FT0C occupancy (after the event selection);;events", HistType::kTH1D, {ft0cOccupancyAxis}); + if (eventCuts.centMultEstimator.value != static_cast(CentMultEstimator::kOff)) { + mHistManager.add("Event/hCentMultBefore", "centrality / multiplicity of the cut (before the event selection);;events", HistType::kTH1D, {centMultAxis}); + mHistManager.add("Event/hCentMultAfter", "centrality / multiplicity of the cut (after the event selection);;events", HistType::kTH1D, {centMultAxis}); + } + mHistManager.add("Event/hBCId", "BC ID of the selected events;BC ID;events", HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5}}); + + if (eventCuts.centMultEstimator.value < static_cast(CentMultEstimator::kOff) || eventCuts.centMultEstimator.value >= static_cast(CentMultEstimator::kNEstimators)) { + LOG(fatal) << "eventCuts.centMultEstimator " << eventCuts.centMultEstimator.value << " unknown, see the configurable description"; + } + if (eventCuts.applyRCT.value) { + mRctChecker.init(eventCuts.rctLabel.value, eventCuts.rctCheckZDC.value, eventCuts.rctTreatLimitedAcceptanceAsBad.value); + } + if (doEventSel.value) { + LOG(warning) << "doEventSel is deprecated, use eventCuts.requireEmcalReadout. Requiring EMCal in the readout."; + } + if (vertexCut.value > 0.) { + LOG(warning) << "vertexCut is deprecated, use eventCuts.zVertexMax. Using |z| < " << vertexCut.value << " cm instead of eventCuts.zVertexMax."; + } // cluster properties (matched clusters) mHistManager.add("TrackEtaPhi", "#eta vs #varphi of all selected tracks", HistType::kTH2F, {etaAxis, phiAxis}); // eta vs phi of all selected tracks @@ -326,46 +437,153 @@ struct EmcTmMonitor { // an integer instead Filter clusterDefinitionSelection = (o2::aod::emcalcluster::definition == clusterDefinition) && (o2::aod::emcalcluster::time >= minTime) && (o2::aod::emcalcluster::time <= maxTime) && (o2::aod::emcalcluster::m02 > minM02) && (o2::aod::emcalcluster::m02 < maxM02); - /// \brief Event selection of the monitor: trigger alias (if doEventSel) and z vertex + /// \brief Centrality / multiplicity of the collision for the estimator of eventCuts.centMultEstimator + static float getCentMult(CollisionEvSelIt const& collision, const CentMultEstimator estimator) + { + switch (estimator) { + case CentMultEstimator::kCentFT0C: + return collision.centFT0C(); + case CentMultEstimator::kCentFT0M: + return collision.centFT0M(); + case CentMultEstimator::kCentFT0A: + return collision.centFT0A(); + case CentMultEstimator::kMultFT0C: + return collision.multFT0C(); + case CentMultEstimator::kMultFT0A: + return collision.multFT0A(); + case CentMultEstimator::kMultFT0M: + return collision.multFT0M(); + case CentMultEstimator::kMultNTracksPV: + return collision.multNTracksPV(); + default: + return 0.f; + } + } + + /// \brief Whether a value lies in [min, max], a negative limit is off + static bool isInRange(const float value, const float min, const float max) + { + return (min < 0.f || value >= min) && (max < 0.f || value <= max); + } + + /// \brief BC ID veto / selection of vetoBCID and selectBCID + bool isBCSelected(const int bcId) const + { + if (std::find(mVetoBCIDs.begin(), mVetoBCIDs.end(), bcId) != mVetoBCIDs.end()) { + return false; + } + return mSelectBCIDs.empty() || std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), bcId) != mSelectBCIDs.end(); + } + + /// \brief Event selection of the monitor, see eventCuts. The cuts are applied in the order of EventCut. /// \param theCollision collision to check /// \param fillHistos fill the event histograms (only once if several process functions run) bool isEventSelected(CollisionEvSelIt const& theCollision, const bool fillHistos) { + // fill the cut flow bin of a passed step + auto passed = [&](const EventCut cut) { + if (fillHistos) { + mHistManager.fill(HIST("Event/hCutFlow"), static_cast(cut)); + } + }; + const auto estimator = static_cast(eventCuts.centMultEstimator.value); + const bool applyCentMult = estimator != CentMultEstimator::kOff; if (fillHistos) { mHistManager.fill(HIST("eventsAll"), 1); + mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); + mHistManager.fill(HIST("Event/hTrackOccupancyBefore"), theCollision.trackOccupancyInTimeRange()); + mHistManager.fill(HIST("Event/hFT0COccupancyBefore"), theCollision.ft0cOccupancyInTimeRange()); + if (applyCentMult) { + mHistManager.fill(HIST("Event/hCentMultBefore"), getCentMult(theCollision, estimator)); + } } + passed(EventCut::kAll); - // do event selection if doEventSel is specified - // currently the event selection is hard coded to kINT7 - // but other selections are possible that are defined in TriggerAliases.h - bool isSelected = true; - const int beginningRun3 = 300000; - if (doEventSel) { - // needs aod::BCs in the process function, otherwise the bc() index is not bound - if (theCollision.bc().runNumber() < beginningRun3) { - if (!theCollision.alias_bit(kINT7)) { - isSelected = false; - } - } else { - if (!theCollision.alias_bit(kTVXinEMC)) { - isSelected = false; - } + // BC ID veto / selection (pilot beams); needs aod::BCs in the process function, otherwise the bc() index is not bound + const auto bc = theCollision.bc(); + const int bcId = static_cast(bc.globalBC() % o2::constants::lhc::LHCMaxBunches); + if (!isBCSelected(bcId)) { + return false; + } + passed(EventCut::kBC); + + if (eventCuts.requireSel8.value && !theCollision.sel8()) { + return false; + } + passed(EventCut::kSel8); + + // EMCal in the readout: kINT7 in Run 2, kTVXinEMC in Run 3 + if (eventCuts.requireEmcalReadout.value || doEventSel.value) { + constexpr int BeginningRun3 = 300000; + const bool hasEmcal = bc.runNumber() < BeginningRun3 ? theCollision.alias_bit(kINT7) : theCollision.alias_bit(kTVXinEMC); + if (!hasEmcal) { + return false; } } - if (!isSelected) { - LOG(debug) << "Event not selected because it is not kINT7 or does not have EMCAL in readout, skipping"; + passed(EventCut::kEmcalReadout); + + const double zVertexMax = vertexCut.value > 0. ? vertexCut.value : static_cast(eventCuts.zVertexMax.value); + if (zVertexMax > 0. && std::abs(theCollision.posZ()) > zVertexMax) { return false; } - if (fillHistos) { - mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); + passed(EventCut::kZVertex); + + // evsel flags, in the order of EventCut + const std::array, static_cast(EventCut::kGoodITSLayersAll) - static_cast(EventCut::kNoTimeFrameBorder) + 1> flags = {{ + {eventCuts.requireNoTimeFrameBorder.value, o2::aod::evsel::kNoTimeFrameBorder}, + {eventCuts.requireNoITSROFrameBorder.value, o2::aod::evsel::kNoITSROFrameBorder}, + {eventCuts.requireNoSameBunchPileup.value, o2::aod::evsel::kNoSameBunchPileup}, + {eventCuts.requireGoodZvtxFT0vsPV.value, o2::aod::evsel::kIsGoodZvtxFT0vsPV}, + {eventCuts.requireVertexITSTPC.value, o2::aod::evsel::kIsVertexITSTPC}, + {eventCuts.requireVertexTOFmatched.value, o2::aod::evsel::kIsVertexTOFmatched}, + {eventCuts.requireNoCollInTimeRangeStandard.value, o2::aod::evsel::kNoCollInTimeRangeStandard}, + {eventCuts.requireNoCollInTimeRangeNarrow.value, o2::aod::evsel::kNoCollInTimeRangeNarrow}, + {eventCuts.requireNoCollInRofStandard.value, o2::aod::evsel::kNoCollInRofStandard}, + {eventCuts.requireNoHighMultCollInPrevRof.value, o2::aod::evsel::kNoHighMultCollInPrevRof}, + {eventCuts.requireGoodITSLayer3.value, o2::aod::evsel::kIsGoodITSLayer3}, + {eventCuts.requireGoodITSLayer0123.value, o2::aod::evsel::kIsGoodITSLayer0123}, + {eventCuts.requireGoodITSLayersAll.value, o2::aod::evsel::kIsGoodITSLayersAll}, + }}; + for (std::size_t i = 0; i < flags.size(); ++i) { + if (flags[i].first && !theCollision.selection_bit(flags[i].second)) { + return false; + } + passed(static_cast(static_cast(EventCut::kNoTimeFrameBorder) + static_cast(i))); } - if (vertexCut > 0 && std::abs(theCollision.posZ()) > vertexCut) { - LOG(debug) << "Event not selected because of z-vertex cut z= " << theCollision.posZ() << " > " << vertexCut << " cm, skipping"; + + // occupancy, track and FT0C based, each with its own range + if (!isInRange(static_cast(theCollision.trackOccupancyInTimeRange()), eventCuts.trackOccupancyMin.value, eventCuts.trackOccupancyMax.value)) { return false; } + passed(EventCut::kTrackOccupancy); + if (!isInRange(theCollision.ft0cOccupancyInTimeRange(), eventCuts.ft0cOccupancyMin.value, eventCuts.ft0cOccupancyMax.value)) { + return false; + } + passed(EventCut::kFT0COccupancy); + + if (eventCuts.applyRCT.value && !mRctChecker.checkTable(theCollision)) { + return false; + } + passed(EventCut::kRCT); + + if (applyCentMult) { + const float centMult = getCentMult(theCollision, estimator); + if (centMult < eventCuts.centMultMin.value || centMult > eventCuts.centMultMax.value) { + return false; + } + } + passed(EventCut::kCentMult); + + passed(EventCut::kSelected); if (fillHistos) { mHistManager.fill(HIST("eventsSelected"), 1); mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + mHistManager.fill(HIST("Event/hTrackOccupancyAfter"), theCollision.trackOccupancyInTimeRange()); + mHistManager.fill(HIST("Event/hFT0COccupancyAfter"), theCollision.ft0cOccupancyInTimeRange()); + if (applyCentMult) { + mHistManager.fill(HIST("Event/hCentMultAfter"), getCentMult(theCollision, estimator)); + } + mHistManager.fill(HIST("Event/hBCId"), bcId); } return true; } From 5c4c4ccef8113c8c5b2e429aac91b2ae04647460 Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Thu, 5 Nov 2026 16:52:44 +0100 Subject: [PATCH 579/648] [PWGLF] Enabling multiplicity dependent studies (#18605) Co-authored-by: Sushanta Tripathy --- PWGLF/Tasks/Resonances/f0phiproxy.cxx | 270 ++++++++++++++++++--- PWGLF/Tasks/Resonances/lambdastarproxy.cxx | 60 ++++- 2 files changed, 293 insertions(+), 37 deletions(-) diff --git a/PWGLF/Tasks/Resonances/f0phiproxy.cxx b/PWGLF/Tasks/Resonances/f0phiproxy.cxx index b83cdee0f7c..1792f20da65 100644 --- a/PWGLF/Tasks/Resonances/f0phiproxy.cxx +++ b/PWGLF/Tasks/Resonances/f0phiproxy.cxx @@ -13,6 +13,8 @@ /// \brief Dipion-based half-momentum kaon proxies with kaon/phi and pion/K*0 bachelors, in data and MC. /// \author Sushanta Tripathy +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -34,7 +36,9 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include +#include #include #include #include @@ -46,10 +50,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -57,7 +63,7 @@ using namespace o2; using namespace o2::framework; struct F0phiproxy { - using Collisions = soa::Join; + using Collisions = soa::Join; using Tracks = soa::Join; @@ -72,6 +78,10 @@ struct F0phiproxy { Configurable vertexMax{"vertexMax", 10.f, "Maximum absolute vertex z (cm)"}; Configurable requireSel8{"requireSel8", true, "Require sel8"}; Configurable requireINELgt0{"requireINELgt0", true, "Require at least one PV track in |eta|<1; MC gen uses charged physical primaries"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "Require kNoSameBunchPileup, as in Lambda-star trigger mode 4"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV, as in Lambda-star trigger mode 4"}; + Configurable useFT0M{"useFT0M", true, "Use calibrated FT0M percentiles for reconstructed data/MC; false retains legacy NPV axes and mixing"}; + ConfigurableAxis ft0mAxis{"ft0mAxis", {0., 0., 1., 5., 10., 15., 20., 30., 40., 50., 70., 100.}, "FT0M percentile classes; first zero selects variable-width bins; must cover [0,100]"}; Configurable trackPtMin{"trackPtMin", 0.15f, "Minimum track pT (GeV/c)"}; Configurable trackEtaMax{"trackEtaMax", 0.8f, "Maximum track |eta|"}; Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track selection"}; @@ -88,6 +98,7 @@ struct F0phiproxy { Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables ME booking and mixing"}; Configurable mixZWidth{"mixZWidth", 2.f, "Vertex bin width (cm)"}; Configurable mixMultWidth{"mixMultWidth", 20.f, "PV multiplicity bin width"}; + Configurable mixFT0MWidth{"mixFT0MWidth", 1.f, "FT0M mixing width in percentage points; pools also respect analysis-class boundaries"}; Configurable enablePhiProxy{"enablePhiProxy", true, "Dipion/2 as on-shell K plus bachelor K"}; Configurable enableKstarProxy{"enableKstarProxy", false, "Dipion/2 as on-shell K plus bachelor pion; use the dedicated Kstar configuration"}; @@ -119,7 +130,11 @@ struct F0phiproxy { static constexpr int IonCodeThreshold = 1000000000; static constexpr int IonZDivisor = 10000; static constexpr int IonZModulo = 1000; + static constexpr float FT0MPercentileMax = 100.f; + static constexpr size_t MinAxisSpecificationSize = 3; + static constexpr double MaxUniformFT0MBins = 10000.; int resolvedF0Code = 0; + std::vector ft0mEdges{}; struct TruthInfo { int64_t id = -1; @@ -141,12 +156,28 @@ struct F0phiproxy { std::vector pions{}, kaons{}; }; + using MixingKey = std::tuple; + enum EventStage : int { Received = 1, + VertexAccepted, + Sel8Accepted, + InelAccepted, + QualityAccepted, + ActivityAccepted, + NEventStages = ActivityAccepted }; + std::shared_ptr eventSelection{}, eventStages{}, selectedEvents{}, ft0mStatusQA{}, mixPartners{}; + std::shared_ptr eventStagesByActivity{}, activityVsNPV{}, activityVsVertex{}, mixPartnersByActivity{}; + std::array, 4> pidQA{}; + std::array, 4> pidQAByActivity{}; + std::array, NMixClasses> dipionSpectra{}; std::array, NMixClasses>, NChannels> compactProxy{}, fullProxy{}; std::array, NCharges>, NMixClasses>, NChannels> controls{}; std::array, NTruthClasses>, NChannels> recoComponents{}; std::array, NChannels> recoTrueParent{}, recoPtResponse{}, proxyPtResponse{}; std::shared_ptr recoTrueF0{}; + std::array, NTruthClasses>, NChannels> recoComponentsByActivity{}; + std::array, NChannels> recoTrueParentByActivity{}, recoPtResponseByActivity{}, proxyPtResponseByActivity{}; + std::shared_ptr recoTrueF0ByActivity{}; enum GenSpectrum : size_t { GenDipion, GenF0, GenPhi, @@ -166,18 +197,97 @@ struct F0phiproxy { static double bachelorMass(size_t channel) { return channel == Phi ? constants::physics::MassKaonCharged : constants::physics::MassPionCharged; } static int bachelorPdg(size_t channel) { return channel == Phi ? PdgKaon : PdgPion; } + std::string recoName(std::string const& name) const + { + return useFT0M.value ? std::string("FT0M/").append(name) : name; + } + + static int ft0mStatus(float percentile) + { + if (!std::isfinite(percentile)) { + return 1; + } + if (percentile < 0.f) { + return 2; + } + return percentile < FT0MPercentileMax ? 0 : 3; + } + + static std::vector resolveFT0MEdges(std::vector const& specification) + { + if (specification.size() < MinAxisSpecificationSize || + !std::all_of(specification.begin(), specification.end(), [](double x) { return std::isfinite(x); })) { + throw std::runtime_error("Invalid FT0M axis specification"); + } + std::vector edges; + if (specification.front() == 0.) { + edges.assign(specification.begin() + 1, specification.end()); + } else { + const double bins = specification.front(); + if (specification.size() != MinAxisSpecificationSize || bins < 1. || bins > MaxUniformFT0MBins || std::floor(bins) != bins) { + throw std::runtime_error("Invalid uniform FT0M axis specification"); + } + for (int i = 0; i <= static_cast(bins); ++i) { + edges.push_back(specification[1] + (specification[2] - specification[1]) * i / bins); + } + } + if (edges.front() != 0. || edges.back() != FT0MPercentileMax || + std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) != edges.end()) { + throw std::runtime_error("FT0M classes must have increasing edges covering [0,100]"); + } + return edges; + } + + AxisSpec activityAxis(bool compact = false) const + { + if (useFT0M.value) { + return AxisSpec{ft0mEdges, "FT0M percentile (MB>0), calibrated"}; + } + return compact ? AxisSpec{proxyMultiplicityAxis, "N_{PV}(|#eta|<1), grouped"} : AxisSpec{multiplicityBins.value, 0., 200., "N_{PV}(|#eta|<1)"}; + } + + int ft0mClass(float percentile) const + { + if (ft0mStatus(percentile) != 0) { + return -1; + } + return static_cast(std::upper_bound(ft0mEdges.begin(), ft0mEdges.end(), percentile) - ft0mEdges.begin()) - 1; + } + + MixingKey mixingKey(int run, float vertex, float mult) const + { + const int activityClass = useFT0M.value ? ft0mClass(mult) : 0; + if (useFT0M.value && activityClass < 0) { + throw std::runtime_error("Invalid FT0M event cannot enter a mixing pool"); + } + const float width = useFT0M.value ? mixFT0MWidth.value : mixMultWidth.value; + auto bin = [](float value, float binWidth) { + const double index = std::floor(value / binWidth); + if (!std::isfinite(index) || index < std::numeric_limits::min() || index > std::numeric_limits::max()) { + throw std::runtime_error("Mixing coordinate cannot be represented by the configured bin width"); + } + return static_cast(index); + }; + return {run, bin(vertex, mixZWidth.value), activityClass, bin(mult, width)}; + } + void init(InitContext const&) { if (doprocess && doprocessMCReco) { throw std::runtime_error("Enable either process (data) or processMCReco, never both"); } - if (mixDepth.value < 0 || mixZWidth.value <= 0.f || mixMultWidth.value <= 0.f || + if (mixDepth.value < 0 || !std::isfinite(mixZWidth.value) || mixZWidth.value <= 0.f || + !std::isfinite(mixMultWidth.value) || mixMultWidth.value <= 0.f || + !std::isfinite(mixFT0MWidth.value) || mixFT0MWidth.value <= 0.f || vertexMax.value <= 0.f || trackPtMin.value < 0.f || trackEtaMax.value <= 0.f || pionTPC.value <= 0.f || pionTOF.value <= 0.f || kaonTPC.value <= 0.f || kaonTOF.value <= 0.f || dipionMassMin.value >= dipionMassMax.value || dipionYMax.value <= 0.f || proxyYMax.value <= 0.f || multiplicityBins.value <= 0 || proxyDipionMassBins.value <= 0 || kstarMassBins.value <= 0 || mcF0PdgCode.value < 0) { throw std::runtime_error("Invalid F0PhiProxy configuration"); } + if (useFT0M.value) { + ft0mEdges = resolveFT0MEdges(ft0mAxis.value); + } if (doprocess || doprocessMCReco) { if ((enablePhiProxy.value || enableKstarProxy.value) && !enableCompactProxy.value && !enableFullProxySparse.value) { throw std::runtime_error("Enable compact or full proxy output when a proxy channel is enabled"); @@ -202,28 +312,57 @@ struct F0phiproxy { const AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; const AxisSpec compactMassPi{proxyDipionMassBins.value, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; const AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; - const AxisSpec mult{multiplicityBins.value, 0., 200., "N_{PV}(|#eta|<1)"}; - const AxisSpec compactMult{proxyMultiplicityAxis, "N_{PV}(|#eta|<1), grouped"}; + const AxisSpec mult = activityAxis(); + const AxisSpec compactMult = activityAxis(true); const AxisSpec compactPt{proxyPtAxis, "proxy pair p_{T} (GeV/c), grouped"}; const AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; const AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; const AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; - histos.add("hEventSelection", "Received; selected", HistType::kTH1D, {{2, 0.5, 2.5}}); + eventSelection = histos.add(recoName("hEventSelection").c_str(), "Received; selected including activity validity", HistType::kTH1D, {{2, 0.5, 2.5}}); + eventSelection->GetXaxis()->SetBinLabel(1, "Received"); + eventSelection->GetXaxis()->SetBinLabel(2, "Selected"); + const AxisSpec stages{NEventStages, 0.5, static_cast(NEventStages) + 0.5, "Event selection stage"}; + eventStages = histos.add(recoName("hEventSelectionSteps").c_str(), "Reconstructed event cut flow", HistType::kTH1D, {stages}); + eventStagesByActivity = histos.add(recoName("hEventSelectionVsMultiplicity").c_str(), "Valid-activity collisions;activity;selection stage", HistType::kTH2D, {compactMult, stages}); + const std::array stageNames{"Received", "Vertex", "sel8 or disabled", "INEL>0 or disabled", "Quality flags or disabled", "Valid activity selected"}; + for (int i = 0; i < NEventStages; ++i) { + eventStages->GetXaxis()->SetBinLabel(i + 1, stageNames[i]); + eventStagesByActivity->GetYaxis()->SetBinLabel(i + 1, stageNames[i]); + } + selectedEvents = histos.add(recoName("hSelectedEvents").c_str(), "Selected collisions counted once, including zero-candidate events;activity;Events", HistType::kTH1D, {compactMult}); + activityVsNPV = histos.add(recoName("hMultiplicityVsNPV").c_str(), "Selected collisions;activity;N_{PV}(|#eta|<1), overflow retained", HistType::kTH2D, {compactMult, {400, 0., 400.}}); + activityVsVertex = histos.add(recoName("hMultiplicityVsVertex").c_str(), "Selected collisions;activity;vertex z (cm)", HistType::kTH2D, {compactMult, {100, -vertexMax.value, vertexMax.value}}); + auto estimator = histos.add("hMultiplicityEstimator", "Configured reconstructed estimator;mode;Workers", HistType::kTH1D, {{2, -0.5, 1.5}}); + estimator->GetXaxis()->SetBinLabel(1, "NPV"); + estimator->GetXaxis()->SetBinLabel(2, "FT0M"); + estimator->Fill(useFT0M.value ? 1. : 0.); + if (useFT0M.value) { + ft0mStatusQA = histos.add("FT0M/hPercentileStatus", "After base event selection;FT0M status;Events", HistType::kTH1D, {{4, -0.5, 3.5}}); + const std::array statusNames{"Valid [0,100)", "Nonfinite", "Below zero", "At or above 100"}; + for (size_t i = 0; i < statusNames.size(); ++i) { + ft0mStatusQA->GetXaxis()->SetBinLabel(static_cast(i + 1), statusNames[i]); + } + } if (mixingEnabled()) { - histos.add("hMixPartners", "Mixing partners;N", HistType::kTH1D, {{101, -0.5, 100.5}}); + mixPartnersByActivity = histos.add(recoName("hMixPartnersVsMultiplicity").c_str(), "Available previous events;activity;mixing partners", HistType::kTH2D, {compactMult, {101, -0.5, 100.5}}); + mixPartners = histos.add(recoName("hMixPartners").c_str(), "Mixing partners;N", HistType::kTH1D, {{101, -0.5, 100.5}}); } if (enablePidQA.value) { - histos.add("hPionTPC", "Quality-selected tracks;pT;nSigmaTPC pion", HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hPionTOF", "TOF-matched quality tracks;pT;nSigmaTOF pion", HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTPC", "Quality-selected tracks;pT;nSigmaTPC kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); - histos.add("hKaonTOF", "TOF-matched quality tracks;pT;nSigmaTOF kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); + const std::array names{"hPionTPC", "hPionTOF", "hKaonTPC", "hKaonTOF"}; + for (size_t i = 0; i < names.size(); ++i) { + pidQA[i] = histos.add(recoName(names[i]).c_str(), "Quality-selected tracks, before PID;pT;nSigma", HistType::kTH2F, {pt, {100, -10., 10.}}); + if (useFT0M.value) { + const std::string name = std::string(names[i]).append("ByMultiplicity"); + pidQAByActivity[i] = histos.add(recoName(name).c_str(), "Quality-selected tracks, before PID;pT;nSigma;FT0M percentile", HistType::kTH3F, {pt, {100, -10., 10.}, compactMult}); + } + } } const std::array dipionNames{"hDipionSE", "hDipionME"}; for (size_t mix = 0; mix < dipionNames.size(); ++mix) { if (mix != 0 && !mixingEnabled()) { continue; } - dipionSpectra[mix] = histos.add(dipionNames[mix], "Dipion SE or raw ME", HistType::kTHnSparseF, {massPi, pt, mult, charge}); + dipionSpectra[mix] = histos.add(recoName(dipionNames[mix]).c_str(), "Dipion SE or raw ME", HistType::kTHnSparseF, {massPi, pt, mult, charge}); } const std::array, NChannels> compactNames{{{"hProxyCompactSE", "hProxyCompactME"}, {"hKstarProxyCompactSE", "hKstarProxyCompactME"}}}; const std::array, NChannels> fullNames{{{"hProxySE", "hProxyME"}, {"hKstarProxySE", "hKstarProxyME"}}}; @@ -238,15 +377,15 @@ struct F0phiproxy { if (channelEnabled(channel)) { // The measured dipion mass is retained; a mass window is not an f0 tag. if (enableCompactProxy.value) { - compactProxy[channel][mix] = histos.add(compactNames[channel][mix], "Grouped-pT/multiplicity proxy; no constituent charge is measured", HistType::kTHnSparseF, {mass, compactPt, compactMassPi, compactMult, charge, bachelorCharge, tof}); + compactProxy[channel][mix] = histos.add(recoName(compactNames[channel][mix]).c_str(), "Grouped-pT/multiplicity proxy; no constituent charge is measured", HistType::kTHnSparseF, {mass, compactPt, compactMassPi, compactMult, charge, bachelorCharge, tof}); } if (enableFullProxySparse.value) { - fullProxy[channel][mix] = histos.add(fullNames[channel][mix], "Diagnostic full proxy", HistType::kTHnSparseF, {mass, pt, massPi, mult, charge, bachelorCharge, tof}); + fullProxy[channel][mix] = histos.add(recoName(fullNames[channel][mix]).c_str(), "Diagnostic full proxy", HistType::kTHnSparseF, {mass, pt, massPi, mult, charge, bachelorCharge, tof}); } } if (controlEnabled(channel)) { for (size_t sign = 0; sign < NCharges; ++sign) { - controls[channel][mix][sign] = histos.add(controlNames[channel][mix][sign], "Standard control SE or raw ME", HistType::kTHnSparseF, {mass, pt, mult}); + controls[channel][mix][sign] = histos.add(recoName(controlNames[channel][mix][sign]).c_str(), "Standard control SE or raw ME", HistType::kTHnSparseF, {mass, pt, mult}); } } } @@ -263,27 +402,40 @@ struct F0phiproxy { const std::array names{"hMCGenDipion", "hMCGenTrueF0", "hMCGenPhiKK", "hMCGenTruePhi", "hMCGenKstarKPi", "hMCGenTrueKstar", "hMCGenProxyPhi", "hMCGenProxyPhiTrueF0", "hMCGenProxyKstar", "hMCGenProxyKstarTrueF0"}; for (size_t i = 0; i < names.size(); ++i) { const AxisSpec axis = i <= GenF0 ? pionMass : (i == GenKstar || i == GenKstarTrue || i >= GenProxyKstar ? mass[Kstar] : mass[Phi]); - generated[i] = histos.add(names[i], "Generated unweighted fiducial spectrum", HistType::kTH2D, {axis, pt}, true); + generated[i] = histos.add(names[i], "Generated unweighted fiducial spectrum; no detector FT0M selection", HistType::kTH2D, {axis, pt}, true); } } if (doprocessMCReco) { - recoTrueF0 = histos.add("hMCRecoTrueF0", "Selected common-parent f0 -> pi pi", HistType::kTH2D, {pionMass, pt}, true); + recoTrueF0 = histos.add(recoName("hMCRecoTrueF0").c_str(), "Selected common-parent f0 -> pi pi", HistType::kTH2D, {pionMass, pt}, true); + if (useFT0M.value) { + recoTrueF0ByActivity = histos.add("FT0M/hMCRecoTrueF0ByMultiplicity", "Selected common-parent f0;dipion mass;pair pT;FT0M percentile", HistType::kTHnSparseD, {pionMass, pt, activityAxis(true)}, true); + } const std::array channelNames{"Phi", "Kstar"}; const std::array classNames{"TrueF0", "Combinatorial", "MisID", "Unmatched"}; for (size_t channel = 0; channel < NChannels; ++channel) { if (channelEnabled(channel)) { for (size_t category = 0; category < classNames.size(); ++category) { - const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + classNames[category]; - recoComponents[channel][category] = histos.add(name.c_str(), "Exclusive proxy truth component; not a coalescence ancestry tag", HistType::kTH2D, {mass[channel], pt}, true); + const std::string name = std::string("hMCRecoProxy").append(channelNames[channel]).append(classNames[category]); + recoComponents[channel][category] = histos.add(recoName(name).c_str(), "Exclusive proxy truth component; not a coalescence ancestry tag", HistType::kTH2D, {mass[channel], pt}, true); + if (useFT0M.value) { + recoComponentsByActivity[channel][category] = histos.add(recoName(std::string(name).append("ByMultiplicity")).c_str(), "Exclusive proxy truth component; not coalescence ancestry", HistType::kTHnSparseD, {mass[channel], pt, activityAxis(true)}, true); + } + } + const std::string name = std::string("hMCRecoProxy").append(channelNames[channel]).append("PtResponse"); + proxyPtResponse[channel] = histos.add(recoName(name).c_str(), "Primary true f0 daughters and true bachelor;generated proxy pair pT;reconstructed proxy pair pT", HistType::kTH2D, {pt, pt}, true); + if (useFT0M.value) { + proxyPtResponseByActivity[channel] = histos.add(recoName(std::string(name).append("ByMultiplicity")).c_str(), "Selected true-f0 proxy response;generated pair pT;reconstructed pair pT;FT0M percentile", HistType::kTHnSparseD, {pt, pt, activityAxis(true)}, true); } - const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + "PtResponse"; - proxyPtResponse[channel] = histos.add(name.c_str(), "Primary true f0 daughters and true bachelor;generated proxy pair pT;reconstructed proxy pair pT", HistType::kTH2D, {pt, pt}, true); } if (controlEnabled(channel)) { - const std::string truthName = std::string("hMCRecoTrue") + channelNames[channel]; - const std::string responseName = std::string("hMCReco") + channelNames[channel] + "PtResponse"; - recoTrueParent[channel] = histos.add(truthName.c_str(), "Selected exact common-parent standard control", HistType::kTH2D, {mass[channel], pt}, true); - recoPtResponse[channel] = histos.add(responseName.c_str(), "Selected standard control;generated pair pT;reconstructed pair pT", HistType::kTH2D, {pt, pt}, true); + const std::string truthName = std::string("hMCRecoTrue").append(channelNames[channel]); + const std::string responseName = std::string("hMCReco").append(channelNames[channel]).append("PtResponse"); + recoTrueParent[channel] = histos.add(recoName(truthName).c_str(), "Selected exact common-parent standard control", HistType::kTH2D, {mass[channel], pt}, true); + recoPtResponse[channel] = histos.add(recoName(responseName).c_str(), "Selected standard control;generated pair pT;reconstructed pair pT", HistType::kTH2D, {pt, pt}, true); + if (useFT0M.value) { + recoTrueParentByActivity[channel] = histos.add(recoName(std::string(truthName).append("ByMultiplicity")).c_str(), "Selected exact common-parent standard control", HistType::kTHnSparseD, {mass[channel], pt, activityAxis(true)}, true); + recoPtResponseByActivity[channel] = histos.add(recoName(std::string(responseName).append("ByMultiplicity")).c_str(), "Selected standard response;generated pair pT;reconstructed pair pT;FT0M percentile", HistType::kTHnSparseD, {pt, pt, activityAxis(true)}, true); + } } } } @@ -398,11 +550,13 @@ struct F0phiproxy { if (mc && !mixed) { const size_t truthClass = proxyTruthClass(first, second, bachelor, channel); recoComponents[channel][truthClass]->Fill(pair.M(), pair.Pt()); + fillSparse(recoComponentsByActivity[channel][truthClass], {pair.M(), pair.Pt(), mult}); if (truthClass == 0 && first.truth.primary && second.truth.primary && bachelor.truth.primary && bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id) { const double px = ProxyMomentumScale * (first.truth.px + second.truth.px) + bachelor.truth.px; const double py = ProxyMomentumScale * (first.truth.py + second.truth.py) + bachelor.truth.py; proxyPtResponse[channel]->Fill(std::hypot(px, py), pair.Pt()); + fillSparse(proxyPtResponseByActivity[channel], {std::hypot(px, py), pair.Pt(), mult}); } } } @@ -422,6 +576,8 @@ struct F0phiproxy { if (mc && !mixed && first.sign != second.sign && identified(first, PdgKaon) && identified(second, bachelorPdg(channel)) && commonParent(first.truth, second.truth, parent)) { recoTrueParent[channel]->Fill(pair.M(), pair.Pt()); recoPtResponse[channel]->Fill(truthPairPt(first.truth, second.truth), pair.Pt()); + fillSparse(recoTrueParentByActivity[channel], {pair.M(), pair.Pt(), mult}); + fillSparse(recoPtResponseByActivity[channel], {truthPairPt(first.truth, second.truth), pair.Pt(), mult}); } } void fillControls(Event const& event, std::deque const& pool, float mult, bool mc) @@ -467,21 +623,56 @@ struct F0phiproxy { void processReco(TCollisions const& collisions, TTracks const& tracks, TSlice const& slice) { // Pools reset at each dataframe; the key also separates runs, z and multiplicity. - std::map>, std::deque> pools; + std::map> pools; const std::deque emptyPool; for (auto const& collision : collisions) { - histos.fill(HIST("hEventSelection"), 1.); - if (std::abs(collision.posZ()) >= vertexMax.value || (requireSel8.value && !collision.sel8()) || - (requireINELgt0.value && collision.multNTracksPVeta1() < 1)) { + const float npv = collision.multNTracksPVeta1(); + const float mult = useFT0M.value ? collision.centFT0M() : npv; + const bool validActivity = useFT0M.value ? ft0mStatus(mult) == 0 : std::isfinite(mult) && mult >= 0.f; + auto stage = [&](int step) { + eventStages->Fill(step); + if (validActivity) { + eventStagesByActivity->Fill(mult, step); + } + }; + eventSelection->Fill(1.); + stage(Received); + if (!std::isfinite(collision.posZ()) || std::abs(collision.posZ()) >= vertexMax.value) { + continue; + } + stage(VertexAccepted); + if (requireSel8.value && !collision.sel8()) { + continue; + } + stage(Sel8Accepted); + if (requireINELgt0.value && npv < 1.f) { continue; } - histos.fill(HIST("hEventSelection"), 2.); - const float mult = collision.multNTracksPVeta1(); + stage(InelAccepted); + if ((requireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) || + (requireGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + continue; + } + stage(QualityAccepted); + if (useFT0M.value) { + // Missing calibration (105) and non-INEL markers (100.5) are not percentiles. + // Never substitute NPV in an FT0M histogram or pool. + ft0mStatusQA->Fill(ft0mStatus(mult)); + } + if (!validActivity) { + continue; + } + stage(ActivityAccepted); + eventSelection->Fill(2.); + selectedEvents->Fill(mult); + activityVsNPV->Fill(mult, npv); + activityVsVertex->Fill(mult, collision.posZ()); std::deque* activePool = nullptr; if (mixingEnabled()) { const int run = collision.template bc_as().runNumber(); - activePool = &pools[{run, {static_cast(std::floor(collision.posZ() / mixZWidth.value)), static_cast(std::floor(mult / mixMultWidth.value))}}]; - histos.fill(HIST("hMixPartners"), activePool->size()); + activePool = &pools[mixingKey(run, collision.posZ(), mult)]; + mixPartners->Fill(activePool->size()); + mixPartnersByActivity->Fill(mult, activePool->size()); } auto const& pool = activePool ? *activePool : emptyPool; Event event; @@ -493,11 +684,17 @@ struct F0phiproxy { } const bool tof = track.hasTOF(); if (enablePidQA.value) { - histos.fill(HIST("hPionTPC"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hKaonTPC"), track.pt(), track.tpcNSigmaKa()); + auto fillPID = [&](size_t index, float value) { + pidQA[index]->Fill(track.pt(), value); + if (pidQAByActivity[index]) { + pidQAByActivity[index]->Fill(track.pt(), value, mult); + } + }; + fillPID(0, track.tpcNSigmaPi()); + fillPID(2, track.tpcNSigmaKa()); if (tof) { - histos.fill(HIST("hPionTOF"), track.pt(), track.tofNSigmaPi()); - histos.fill(HIST("hKaonTOF"), track.pt(), track.tofNSigmaKa()); + fillPID(1, track.tofNSigmaPi()); + fillPID(3, track.tofNSigmaKa()); } } TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof, .truth = {}}; @@ -543,6 +740,7 @@ struct F0phiproxy { fillSparse(dipionSpectra[0], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); if (mc && trueF0(first, second)) { recoTrueF0->Fill(pair.M(), pair.Pt()); + fillSparse(recoTrueF0ByActivity, {pair.M(), pair.Pt(), mult}); } if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { continue; diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index 2ff5ad926a9..625403f7c0e 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -74,6 +74,16 @@ struct Lambdastarproxy { static constexpr float CutPtMinDefault = 0.5f; static constexpr float CutEtaMaxDefault = 0.8f; + static constexpr int FT0MPercentileBins = 100; + static constexpr float FT0MPercentileMax = 100.f; + enum class FT0MEventStatus : int { + Valid = 1, + Negative, + OutOfRange, + NonFinite, + Unavailable + }; + static constexpr float CutMCPtMinDefault = 0.1f; static constexpr float CutMCEtaMaxDefault = 0.8f; static constexpr int FillMCTruthDefault = 1; @@ -352,6 +362,37 @@ struct Lambdastarproxy { return fallback; } + template + void fillEventMultiplicityCounters(TCollision const& collision, float eventMult) + { + // Use the exact estimator of the pair maps, including the legacy track-count + // fallback. Nonfinite values have no percentile and are counted in overflow. + histos.fill(HIST("hEventMultiplicity"), std::isfinite(eventMult) ? eventMult : FT0MPercentileMax); + + // Keep the raw FT0M denominator separate: fallback track counts must never + // acquire an FT0M percentile label. Missing/nonfinite FT0M goes to overflow; + // finite negative and >=100 values retain their underflow/overflow meaning. + float ft0m = FT0MPercentileMax; + auto status = FT0MEventStatus::Unavailable; + if constexpr (requires { collision.centFT0M(); }) { + const float value = collision.centFT0M(); + if (!std::isfinite(value)) { + status = FT0MEventStatus::NonFinite; + } else { + ft0m = value; + if (value < 0.f) { + status = FT0MEventStatus::Negative; + } else if (value >= FT0MPercentileMax) { + status = FT0MEventStatus::OutOfRange; + } else { + status = FT0MEventStatus::Valid; + } + } + } + histos.fill(HIST("hEventFT0M"), ft0m); + histos.fill(HIST("hEventFT0MStatus"), static_cast(status)); + } + int chargeFromPdg(int pdg) const { const int apdg = std::abs(pdg); @@ -529,13 +570,28 @@ struct Lambdastarproxy { AxisSpec dcaXYAxis{200, -0.2, 0.2, "DCA_{xy} (cm)"}; AxisSpec dcaZAxis{200, -0.2, 0.2, "DCA_{z} (cm)"}; - // Count reconstructed collisions received by the data process, before any + // Count reconstructed collisions received by the data or reco-MC process, before any // task event cuts and after all of them, independently of track candidates. histos.add("hEventSelection", "Proxy event selection;Selection stage;Events", HistType::kTH1D, {AxisSpec{2, 0.5, 2.5, "Selection stage"}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "Before event selection"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "After event selection"); + // Small event-level denominators, independent of candidate and QA switches. + // Double storage and Sumw2 preserve large merged event counts and errors. + histos.add("hEventFT0M", "Selected reconstructed events;Raw FT0M percentile;Events", + HistType::kTH1D, {AxisSpec{FT0MPercentileBins, 0., FT0MPercentileMax, "Raw FT0M percentile"}}, true); + histos.add("hEventMultiplicity", "Selected reconstructed events (same estimator as pair maps);Multiplicity/centrality;Events", + HistType::kTH1D, {centAxis}, true); + histos.add("hEventFT0MStatus", "Selected reconstructed events;Raw FT0M status;Events", + HistType::kTH1D, {AxisSpec{static_cast(FT0MEventStatus::Unavailable), 0.5, static_cast(FT0MEventStatus::Unavailable) + 0.5, "Raw FT0M status"}}, true); + auto* ft0mStatusAxis = histos.get(HIST("hEventFT0MStatus"))->GetXaxis(); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Valid), "Finite FT0M [0,100)"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Negative), "Negative FT0M"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::OutOfRange), "FT0M >= 100"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::NonFinite), "Nonfinite FT0M"); + ft0mStatusAxis->SetBinLabel(static_cast(FT0MEventStatus::Unavailable), "FT0M unavailable"); + // Invariant-mass spectra histos.add("hInvMassPKUnlike", "pK invariant mass (unlike-sign);M_{pK} (GeV/c^{2});Counts", @@ -1945,6 +2001,8 @@ struct Lambdastarproxy { // Compute event multiplicity (FT0M or fallback) const float eventMult = eventMultiplicityFT0MOrFallback(collision, eventMultFallback); + // Exactly once after event cuts, before PID probes or candidate requirements. + fillEventMultiplicityCounters(collision, eventMult); if (lstarEnableProxyPIDProbes.value != 0) { if constexpr (IsMC) { fillProxyPIDProbes(tracks, eventMult, collision.mcCollisionId()); From 51576a784f83bff43e7f2d441aa0253bf6339f30 Mon Sep 17 00:00:00 2001 From: amatyja Date: Thu, 5 Nov 2026 17:59:09 +0100 Subject: [PATCH 580/648] [PWGUD] Control histograms added (#18608) --- .../tauThreeProngEventTableProducer.cxx | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index 37583d9a938..8afc2b486c8 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -260,9 +260,6 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/gapSide", ";Gap;events", {HistType::kTH1D, {{10, -1., 9.}}}); registrySkim.add("skim/trueGapSide", ";TrueGap;events", {HistType::kTH1D, {{10, -1., 9.}}}); - registrySkim.add("skim/etaTrk", ";#eta^{trk};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); - registrySkim.add("skim/ptTrk", ";p_{T}^{trk};events", {HistType::kTH1F, {{100, 0., 5.}}}); - registrySkim.add("skim/phiTrk", ";#phi^{trk};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); registrySkim.add("skim/nTof", ";N_{TOFtrk};events", {HistType::kTH1F, {{10, -1., 9.}}}); } if (doprocessMonteCarlo) { @@ -298,6 +295,23 @@ struct TauThreeProngEventTableProducer { registrySkim.add("skim/daughterEtaMC", ";#eta^{daughter};events", {HistType::kTH1F, {{100, -4., 4.}}}); registrySkim.add("skim/daughterPtMC", ";p_{T}^{daughter};events", {HistType::kTH1F, {{100, 0, 5.0}}}); } + // common histos for doprocessDoSkim and doprocessMonteCarlo + if (doprocessDoSkim || doprocessMonteCarlo) { + registrySkim.add("rec/zvertex_SGP", ";#it{V}_{z};events", {HistType::kTH1D, {{100, -15., 15.}}}); + registrySkim.add("rec/nPVtrack_SGP", ";#it{N}_{PVtrack};events", {HistType::kTH1D, {{100, 0., 100.}}}); + registrySkim.add("rec/qtot_SGP", ";#it{Q}_{tot};events", {HistType::kTH1D, {{30, -15., 15.}}}); + // registrySkim.add("skim/etaTrk", ";#eta^{trk};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); + // registrySkim.add("skim/ptTrk", ";p_{T}^{trk};events", {HistType::kTH1F, {{100, 0., 5.}}}); + // registrySkim.add("skim/phiTrk", ";#phi^{trk};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); + registrySkim.add("rec/phiPVtrack_SGP", ";#phi^{PVtrack};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); + registrySkim.add("rec/etaPVtrack_SGP", ";#eta^{PVtrack};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); + registrySkim.add("rec/ptPVtrack_SGP", ";#it{p}_{T}^{PVtrack};events", {HistType::kTH1F, {{100, 0, 5.0}}}); + registrySkim.add("rec/nTOFtrack_SGP", ";#it{N}_{TOFtrack};events", {HistType::kTH1F, {{50, 0, 50}}}); + registrySkim.add("rec/FT0A_SGP", ";#it{E}_{FT0A};events", {HistType::kTH1F, {{100, 0, 500}}}); + registrySkim.add("rec/FT0C_SGP", ";#it{E}_{FT0C};events", {HistType::kTH1F, {{100, 0, 500}}}); + registrySkim.add("rec/FV0A_SGP", ";#it{E}_{FV0A};events", {HistType::kTH1F, {{100, 0, 500}}}); + } + if (doprocessGenerated) { registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); @@ -762,14 +776,15 @@ struct TauThreeProngEventTableProducer { int nTofTrk = 0; int nEtaIn15 = 0; int npT100 = 0; + int qtot = 0; ROOT::Math::LorentzVector> p; for (const auto& trk : PVContributors) { - // qtot += trk.sign(); + qtot += trk.sign(); // p.SetXYZM(trk.px(), trk.py(), trk.pz(), MassPiPlus); p.SetXYZT(trk.px(), trk.py(), trk.pz(), RecoDecay::e(trk.px(), trk.py(), trk.pz(), MassPiPlus)); - registrySkim.get(HIST("skim/etaTrk"))->Fill(p.Eta()); - registrySkim.get(HIST("skim/ptTrk"))->Fill(trk.pt()); - registrySkim.get(HIST("skim/phiTrk"))->Fill(p.Phi()); + registrySkim.get(HIST("rec/etaPVtrack_SGP"))->Fill(p.Eta()); + registrySkim.get(HIST("rec/ptPVtrack_SGP"))->Fill(trk.pt()); + registrySkim.get(HIST("rec/phiPVtrack_SGP"))->Fill(p.Phi()); if (std::abs(p.Eta()) < trkEtacut) nEtaIn15++; // 0.9 is a default @@ -779,6 +794,20 @@ struct TauThreeProngEventTableProducer { nTofTrk++; } // end of loop over PV tracks + // + // plots after Single Gap producer without any further cuts + // + registrySkim.get(HIST("rec/zvertex_SGP"))->Fill(dgcand.posZ()); + registrySkim.get(HIST("rec/nPVtrack_SGP"))->Fill(PVContributors.size()); + registrySkim.get(HIST("rec/qtot_SGP"))->Fill(qtot); + registrySkim.get(HIST("rec/nTOFtrack_SGP"))->Fill(nTofTrk); + registrySkim.get(HIST("rec/FT0A_SGP"))->Fill(dgcand.totalFT0AmplitudeA()); + registrySkim.get(HIST("rec/FT0C_SGP"))->Fill(dgcand.totalFT0AmplitudeC()); + registrySkim.get(HIST("rec/FV0A_SGP"))->Fill(dgcand.totalFV0AmplitudeA()); + // + // end of plots after Single Gap producer + // + // check number of PV tracks if (PVContributors.size() != nPVtrackscut) // 4 or 6 return; @@ -1596,6 +1625,20 @@ struct TauThreeProngEventTableProducer { // timesFIT[1] = collFromMcColl.timeFT0C(); // timesFIT[2] = collFromMcColl.timeFV0A(); + // + // plots after Single Gap producer without any further cuts + // + registrySkim.get(HIST("rec/zvertex_SGP"))->Fill(zVertex); + registrySkim.get(HIST("rec/nPVtrack_SGP"))->Fill(nPVcontrib); + // registrySkim.get(HIST("rec/qtot_SGP"))->Fill(qtot); + // registrySkim.get(HIST("rec/nTOFtrack_SGP"))->Fill(nTofTrk); + registrySkim.get(HIST("rec/FT0A_SG"))->Fill(amplitudesFIT[0]); + registrySkim.get(HIST("rec/FT0C_SGP"))->Fill(amplitudesFIT[1]); + registrySkim.get(HIST("rec/FV0A_SGP"))->Fill(amplitudesFIT[2]); + // + // end of plots after Single Gap producer + // + // get particles associated to generated collision auto const& partsFromMcColl = mcParticles.sliceBy(partPerMcCollision, mccoll.globalIndex()); if (verbose) From 7b06eaf589c30a50acec3161712fa453e2bc8b9d Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:03:13 +0100 Subject: [PATCH 581/648] [PWGCF] Implement collision tagger in femto framework (#18607) --- PWGCF/Femto/Core/collisionTagger.h | 1016 +++++++++++++++++ PWGCF/Femto/DataModel/FemtoTables.h | 12 + PWGCF/Femto/TableProducer/CMakeLists.txt | 5 + .../femtoProducerCollisionTagger.cxx | 704 ++++++++++++ PWGCF/Femto/Tasks/femtoPairD0D0.cxx | 37 +- PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 65 +- .../Femto/Tasks/femtoPairTrackCharmHadron.cxx | 62 +- PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 85 +- PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 97 +- .../femtoPairTrackTwoPhotonResonance.cxx | 68 +- .../Tasks/femtoPairTrackTwoTrackResonance.cxx | 91 +- PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 74 +- .../Tasks/femtoPairV0TwoTrackResonance.cxx | 165 ++- PWGCF/Femto/Tasks/femtoPairV0V0.cxx | 92 +- .../Tasks/femtoTripletTrackTrackCascade.cxx | 69 +- .../Tasks/femtoTripletTrackTrackTrack.cxx | 39 +- .../Femto/Tasks/femtoTripletTrackTrackV0.cxx | 39 +- 17 files changed, 2658 insertions(+), 62 deletions(-) create mode 100644 PWGCF/Femto/Core/collisionTagger.h create mode 100644 PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx diff --git a/PWGCF/Femto/Core/collisionTagger.h b/PWGCF/Femto/Core/collisionTagger.h new file mode 100644 index 00000000000..9d2bf28a0c5 --- /dev/null +++ b/PWGCF/Femto/Core/collisionTagger.h @@ -0,0 +1,1016 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file collisionTagger.h +/// \brief Shared machinery for the self-configuring collision tagger (TableProducer/femtoProducerCollisionTagger.cxx) +/// and for pair/triplet tasks that want to read back their own assigned tag bit(s). +/// +/// The tagger walks the running workflow's device list once, and for every matching pair/triplet +/// task instance it finds, assigns the next free bit(s) of FColTags::collisionTag to that +/// instance's particle role(s), in discovery order. A consuming task cannot read the tagger's +/// bit assignment back out of it directly: RunningWorkflowInfo only exposes each device's +/// CLI/JSON-resolved option values, a static snapshot taken once at workflow construction -- it is +/// never updated by what another device's init() computes at runtime, and there is no ordering +/// guarantee between different devices' init() calls regardless. So instead, discoverTaggedRoles() +/// is the single canonical implementation of the bit-assignment walk, included and called +/// identically by the tagger (to assign bits and fill FColTags) and by every consuming task (to +/// recompute, from the same static device list, which bit(s) ended up belonging to its own device +/// name). Keeping this walk in one place is what keeps the two sides from silently drifting apart. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ +#define PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::collisiontagger +{ + +constexpr int MaxTagBits = 8 * sizeof(datatypes::CollisionTagType); + +// Supporting an arbitrary number of same-type "subwagons" (same task, different cuts, all running +// in one train) rules out reusing Partition for the cut check itself: it is a compile-time +// construct, permanently bound to one specific Configurable object's fields. So instead, each +// matching sibling device's configured cut VALUES are copied into a plain value struct (no +// Configurable, no Partition), one push_back per discovered subwagon, and checked by hand against +// each particle -- the same approach femtoDreamCollisionMasker.cxx uses for its own multi-variant +// support. The check functions below are a direct, faithful port of the corresponding +// MAKE_*_PARTITION macro in Core/partitions.h, just expressed as a runtime check instead of a +// compiled Gandiva expression. +struct TrackSelectionValues { + int chargeAbs = 1; + int chargeSign = 1; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + datatypes::TrackMaskType maskLowMomentum = 0; + datatypes::TrackMaskType maskHighMomentum = 0; + datatypes::TrackMaskType rejectionMaskLowMomentum = 0; + datatypes::TrackMaskType rejectionMaskHighMomentum = 0; + float pidThres = 1e6f; +}; + +// Despite the name, this shape (sign + pt + eta + phi + mass window + flat mask) is NOT +// V0-specific: MAKE_CASCADE_PARTITION, MAKE_SIGMA_PARTITION, MAKE_SIGMAPLUS_PARTITION, +// MAKE_D0_PARTITION, and MAKE_CHARM3PRONG_PARTITION in Core/partitions.h are all byte-for-byte +// identical to MAKE_LAMBDA_PARTITION, and CascadeMaskType/KinkMaskType/CharmHadronMaskType are +// all, like V0MaskType, uint32_t -- so this one struct (and lambdaPasses()/readV0Selection() +// below) is reused verbatim for Xi/Omega (cascade), Sigma/SigmaPlus (kink), and D0/Lc +// (charm hadron) selections too, not just Lambda. Only SigmaPlusPcm needs its own struct +// (KinkPcmSelectionValues below), for the one extra mlScoreMin field it has beyond this shape. +struct V0SelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::V0MaskType mask = 0; + int sign = 0; // only used for lambdas; k0shorts carry a dummy value for compatibility +}; + +// one bit = one pair type for one task instance: both sides' selections, read together, since the +// bit means "both are present", not "particle 1 is present" or "particle 2 is present" separately +struct TrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// sel1/sel2 (not trackSel/v0Sel) for the same reason TrackTrackVariant above uses sel1/sel2: so +// tagCrossVariants() below can be written once, generically, for any cross-type pair -- naming +// the fields after what they ARE (track selection, v0 selection) would tie the generic helper's +// code to this one particle-type combination. +struct TrackV0Variant { + TrackSelectionValues sel1; + V0SelectionValues sel2; + int bit = 0; +}; + +// SigmaPlusPcm's selection is V0SelectionValues plus one extra field (mlScoreMin), so it needs +// its own value struct, check function (kinkPcmPasses, below readV0Selection), and variant type +// -- everything else about it (sign/pt/eta/phi/mass/mask) is identical to V0SelectionValues. +struct KinkPcmSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::KinkMaskType mask = 0; + int sign = 0; + float mlScoreMin = -1.f; +}; + +struct TrackKinkPcmVariant { + TrackSelectionValues sel1; + KinkPcmSelectionValues sel2; + int bit = 0; +}; + +// Same-type pair where BOTH legs use the V0SelectionValues shape: covers v0-v0 (lambda-lambda, +// k0short-k0short, lambda-k0short) and d0-d0 (same shape, see V0SelectionValues above), checked +// with tagPairVariants() when sel1/sel2 are the same species (sameSpecies-aware) or tagCrossVariants() +// when they are not (e.g. lambda-k0short: sameSpecies is meaningless there and left false/unused). +struct V0V0Variant { + V0SelectionValues sel1; + V0SelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// Phi -> K+K-, Rho0 -> pi+pi-: both daughters are the same species, so MAKE_RESONANCE_0_PARTITON +// (Core/partitions.h) only needs one pair of momentum-threshold-gated daughter masks, applied to +// whichever of the two stored columns (maskPosDau/maskNegDau) belongs to the daughter that was +// above/below threshold at build time (posDauHasHighMomentum/negDauHasHighMomentum, read straight +// off the candidate -- these switches are baked in at production, not re-derived here). +struct TwoTrackResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType dauMaskBelowThres = 0; + datatypes::TrackMaskType dauMaskAboveThres = 0; +}; + +// Kstar0 -> K+pi-, Kstar0bar -> K-pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign (see MAKE_RESONANCE_1_PARTITON in Core/partitions.h), so the daughter +// masks are named by species (kaon/pion) rather than by charge, and sign picks which hypothesis a +// given candidate's signedPt must match. +struct Kstar0SelectionValues { + int sign = 0; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType kaonDauMaskBelowThres = 0; + datatypes::TrackMaskType kaonDauMaskAboveThres = 0; + datatypes::TrackMaskType pionDauMaskBelowThres = 0; + datatypes::TrackMaskType pionDauMaskAboveThres = 0; +}; + +// Pi0/Eta -> gamma gamma: photon daughters have no momentum-dependent PID switch and no pos/neg +// distinction, so (unlike TwoTrackResonanceSelectionValues above) there is only one flat mask, +// already the AND of both legs' own masks at production time (see femtotwophotonresonances::MaskDau +// in FemtoTables.h) -- same shape as V0SelectionValues/k0shortPasses, just plain pt (no sign) and a +// PhotonMaskType mask instead of V0MaskType. +struct TwoPhotonResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::PhotonMaskType mask = 0; +}; + +// Cross-type pair variants for the three resonance-pairing tasks: track-twoTrackResonance (Phi/Rho0 +// share one shape, Kstar0 needs its own because of the kaon/pion mask swap), track-twoPhotonResonance +// (Pi0/Eta), and v0-twoTrackResonance (lambda/k0short x phi/rho0/kstar0 -- same split as track-side). +struct TrackTwoTrackResonanceVariant { + TrackSelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct TrackKstar0Variant { + TrackSelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +struct TrackTwoPhotonResonanceVariant { + TrackSelectionValues sel1; + TwoPhotonResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0TwoTrackResonanceVariant { + V0SelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0Kstar0Variant { + V0SelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +// Triplet variants, mirroring the "123 same, 12 same, or all different" pattern +// TripletTrackTrackTrackBuilder/TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder use +// (Core/tripletBuilder.h, confMixing.particle123AreSameSpecies / particle12AreSameSpecies -- only +// these two combinations are supported, never e.g. "1&3 same"). Same-type (track-track-track): +// all three roles share one table, so same123 needs 3 distinct entries passing sel1 (sel2/sel3 +// ignored, mirrored onto sel1 at discovery time -- same trick as TrackTrackVariant's sameSpecies +// above), same12 needs 2 distinct passing sel1 plus 1 more passing sel3 (sel2 ignored). +struct TrackTrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + TrackSelectionValues sel3; + bool same123 = false; + bool same12 = false; + int bit = 0; +}; + +// Cross-type (track-track-v0, track-track-cascade): roles 1&2 share the track table, role 3 is a +// different table (v0 or cascade, hence V0SelectionValues shape) -- only same12 is possible here +// (no same123 since role 3 is never the same type as roles 1&2). +struct TrackTrackV0Variant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + V0SelectionValues sel3; + bool same12 = false; + int bit = 0; +}; + +inline TrackSelectionValues readTrackSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TrackSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".chargeAbs") { + v.chargeAbs = option.defaultValue.get(); + } else if (option.name == prefix + ".chargeSign") { + v.chargeSign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".maskLowMomentum") { + v.maskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".maskHighMomentum") { + v.maskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskLowMomentum") { + v.rejectionMaskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskHighMomentum") { + v.rejectionMaskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".pidThres") { + v.pidThres = option.defaultValue.get(); + } + } + return v; +} + +inline V0SelectionValues readV0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + V0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } + } + return v; +} + +inline KinkPcmSelectionValues readKinkPcmSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + KinkPcmSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".mlScoreMin") { + v.mlScoreMin = option.defaultValue.get(); + } + } + return v; +} + +inline TwoTrackResonanceSelectionValues readTwoTrackResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoTrackResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskBelowThres") { + v.dauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskAboveThres") { + v.dauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline Kstar0SelectionValues readKstar0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + Kstar0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskBelowThres") { + v.kaonDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskAboveThres") { + v.kaonDauMaskAboveThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskBelowThres") { + v.pionDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskAboveThres") { + v.pionDauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline TwoPhotonResonanceSelectionValues readTwoPhotonResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoPhotonResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMask") { + v.mask = option.defaultValue.get(); + } + } + return v; +} + +// reads the current value of a plain (non-group-prefixed) boolean option on a sibling device, +// e.g. one of its doprocessXXX-backed PROCESS_SWITCH flags +inline bool hasEnabledOption(o2::framework::DeviceSpec const& device, std::string const& name) +{ + for (auto const& option : device.options) { + if (option.name == name) { + return option.defaultValue.get(); + } + } + return false; +} + +// true if ANY enabled option on this device has a name starting with `prefix` -- used instead of +// hasEnabledOption() where a pair/triplet task has several *WithTagging* process functions that +// all consume the same tag bit (processMixedEventWithTagging, processMixedEventWithTaggingAndMass, +// ...AndEventShape, ...AndFitVars, ...): matching the shared prefix means enabling any one of +// those variants correctly triggers bit assignment, instead of only the base one happening to +// have the exact name this check was written against. +inline bool hasAnyEnabledOptionStartingWith(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + for (auto const& option : device.options) { + if (option.name.rfind(prefix, 0) == 0 && option.defaultValue.get()) { + return true; + } + } + return false; +} + +// mirrors MAKE_TRACK_PARTITION(selection) in Core/partitions.h +template +bool trackPasses(T const& track, TrackSelectionValues const& sel) +{ + if (sel.chargeSign != 0 && ((sel.chargeSign > 0) != (track.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(sel.chargeAbs * track.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(track.eta() > sel.etaMin && track.eta() < sel.etaMax)) { + return false; + } + if (!(track.phi() > sel.phiMin && track.phi() < sel.phiMax)) { + return false; + } + float momentum = absPt * std::cosh(track.eta()); + if (momentum <= sel.pidThres) { + return (track.mask() & sel.maskLowMomentum) == sel.maskLowMomentum && + (track.mask() & sel.rejectionMaskLowMomentum) == static_cast(0); + } + return (track.mask() & sel.maskHighMomentum) == sel.maskHighMomentum && + (track.mask() & sel.rejectionMaskHighMomentum) == static_cast(0); +} + +// mirrors MAKE_LAMBDA_PARTITION(selection) in Core/partitions.h -- also mirrors +// MAKE_CASCADE_PARTITION/MAKE_SIGMA_PARTITION/MAKE_SIGMAPLUS_PARTITION/MAKE_D0_PARTITION/ +// MAKE_CHARM3PRONG_PARTITION, which are byte-for-byte identical, so this same function is reused +// as-is for Xi/Omega, Sigma/SigmaPlus, and D0/Lc candidates too (see the comment on +// V0SelectionValues above). +template +bool lambdaPasses(T const& v0, V0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (v0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(v0.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_SIGMAPLUSPCM_PARTITION(selection) in Core/partitions.h -- lambdaPasses() above +// plus the one extra mlScoreMin check. +template +bool kinkPcmPasses(T const& kink, KinkPcmSelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kink.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kink.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(kink.eta() > sel.etaMin && kink.eta() < sel.etaMax)) { + return false; + } + if (!(kink.phi() > sel.phiMin && kink.phi() < sel.phiMax)) { + return false; + } + if (!(kink.mass() > sel.massMin && kink.mass() < sel.massMax)) { + return false; + } + if (!(kink.mlScore() >= sel.mlScoreMin)) { + return false; + } + return (kink.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_K0SHORT_PARTITION(selection) in Core/partitions.h +template +bool k0shortPasses(T const& v0, V0SelectionValues const& sel) +{ + if (!(v0.pt() > sel.ptMin && v0.pt() < sel.ptMax)) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_RESONANCE_0_PARTITON(selection) in Core/partitions.h (Phi, Rho0) +template +bool twoTrackResonancePasses(T const& res, TwoTrackResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + datatypes::TrackMaskType posMask = res.posDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + if ((res.maskPosDau() & posMask) != posMask) { + return false; + } + datatypes::TrackMaskType negMask = res.negDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + return (res.maskNegDau() & negMask) == negMask; +} + +// mirrors MAKE_RESONANCE_1_PARTITON(selection) in Core/partitions.h (Kstar0/Kstar0bar) -- pos/neg +// daughter <-> kaon/pion resolved from the candidate's own signedPt, same as the real builder. +template +bool kstar0Passes(T const& kstar0, Kstar0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kstar0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kstar0.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(kstar0.eta() > sel.etaMin && kstar0.eta() < sel.etaMax)) { + return false; + } + if (!(kstar0.phi() > sel.phiMin && kstar0.phi() < sel.phiMax)) { + return false; + } + if (!(kstar0.mass() > sel.massMin && kstar0.mass() < sel.massMax)) { + return false; + } + bool posIsKaon = kstar0.signedPt() > 0.f; + datatypes::TrackMaskType posMaskThres = posIsKaon + ? (kstar0.posDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres) + : (kstar0.posDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres); + if ((kstar0.maskPosDau() & posMaskThres) != posMaskThres) { + return false; + } + datatypes::TrackMaskType negMaskThres = posIsKaon + ? (kstar0.negDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres) + : (kstar0.negDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres); + return (kstar0.maskNegDau() & negMaskThres) == negMaskThres; +} + +// mirrors MAKE_TWOPHOTONRESONANCE_PARTITION(selection) in Core/partitions.h (Pi0, Eta) +template +bool twoPhotonResonancePasses(T const& res, TwoPhotonResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + return (res.maskDau() & sel.mask) == sel.mask; +} + +using CollisionTagBitset = std::bitset; + +// true if at least `needed` entries in `range` satisfy `passes(entry, sel)`. The one primitive +// every per-collision tagging check below is built from; a new particle type only ever needs a +// new `passes` function (trackPasses/lambdaPasses/k0shortPasses above are examples), never a new +// copy of this loop. +template +inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn&& passes, int needed = 1) +{ + int count = 0; + for (auto const& entry : range) { + if (passes(entry, sel) && ++count >= needed) { + return true; + } + } + return false; +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE pair +// (both v.sel1 and v.sel2 satisfied -- or, for a same-species pair, 2 distinct entries passing +// the one shared selection v.sel2). Reusable for any future same-type pair (V0V0, D0D0, ...) -- +// just a different `variants`/`range`/`passes` combination, the counting logic doesn't change. +template +inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + for (auto const& v : variants) { + bool ok1 = hasAtLeast(range, v.sel1, passes, 1); + bool ok2 = hasAtLeast(range, v.sel2, passes, v.sameSpecies ? 2 : 1); + if (ok1 && ok2) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 has what it takes for role 1 (v.sel1, +// checked with passes1) AND range2 has what it takes for role 2 (v.sel2, checked with passes2). +// Reusable for any future cross-type pair (track-cascade, track-kink, track-D0, ...) -- just a +// different `variants`/ranges/passes combination. +template +inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +{ + for (auto const& v : variants) { + if (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range2, v.sel2, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// One-call convenience for the common case of a single tagPairVariants() check immediately +// followed by producing the tag row -- covers any task type whose process function only ever +// needs one same-type-pair check per collision (e.g. processTrackTrack). +template +inline void tagAndProduce(Produces& producedTags, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + CollisionTagBitset tag; + tagPairVariants(tag, variants, range, passes); + producedTags(static_cast(tag.to_ullong())); +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE +// triplet (track-track-track): same123 needs 3 distinct entries passing sel1; same12 needs 2 +// distinct passing sel1 plus 1 more passing sel3; otherwise sel1/sel2/sel3 each need their own +// independent match. Mirrors TripletTrackTrackTrackBuilder exactly (see TrackTrackTrackVariant). +template +inline void tagTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + for (auto const& v : variants) { + bool ok = false; + if (v.same123) { + ok = hasAtLeast(range, v.sel1, passes, 3); + } else if (v.same12) { + ok = hasAtLeast(range, v.sel1, passes, 2) && hasAtLeast(range, v.sel3, passes, 1); + } else { + ok = hasAtLeast(range, v.sel1, passes, 1) && hasAtLeast(range, v.sel2, passes, 1) && hasAtLeast(range, v.sel3, passes, 1); + } + if (ok) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 (roles 1&2, one table) and range2 (role 3, a +// different table) have what it takes to form a CROSS-TYPE triplet (track-track-v0, +// track-track-cascade): same12 needs 2 distinct entries in range1 passing sel1; otherwise role 1 +// and role 2 each need their own independent match in range1. Mirrors +// TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder exactly (see TrackTrackV0Variant). +template +inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +{ + for (auto const& v : variants) { + bool ok = v.same12 ? hasAtLeast(range1, v.sel1, passes1, 2) + : (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range1, v.sel2, passes1, 1)); + if (ok && hasAtLeast(range2, v.sel3, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// Role names and device-name substrings, declared once: a consuming task needs the exact same +// role spelling discoverTaggedRoles() below uses, to look its own bit back up via findOwnBit() -- +// fed in as ConfTagging's template argument below, so a task's role is fixed at compile time +// rather than a loose string repeated at every call site. Declared as named `char` arrays, not +// `const char*` pointing at string literals: a pointer non-type template argument must be the +// address of a named object with static storage duration, and the standard specifically +// disallows forming that address from a string literal directly (two identical literals aren't +// guaranteed the same address). A named array has none of that ambiguity, and decays to +// `const char*` on its own wherever a plain pointer is expected (ConfTagging +// below, the std::string construction in discoverTaggedRoles(), etc.). `inline` (not just +// `constexpr`) matters too: without it, a namespace-scope variable has internal linkage, so each +// translation unit would get its own copy at its own address -- fine for a value, but the same +// template instantiation must denote the same entity everywhere it's used. `inline` guarantees +// one shared address. The device-name substrings are, unavoidably, the literal +// o2physics_add_dpl_workflow() names from the respective CMakeLists.txt -- there is no way to +// derive those at compile time, so they are hardcoded here, exactly once, rather than repeated at +// every call site. +namespace role +{ +inline constexpr char TrackTrackPair[] = "TrackTrackPair"; +inline constexpr char TrackLambdaPair[] = "TrackLambdaPair"; +inline constexpr char TrackK0shortPair[] = "TrackK0shortPair"; +inline constexpr char TrackXiPair[] = "TrackXiPair"; +inline constexpr char TrackOmegaPair[] = "TrackOmegaPair"; +inline constexpr char TrackSigmaPair[] = "TrackSigmaPair"; +inline constexpr char TrackSigmaPlusPair[] = "TrackSigmaPlusPair"; +inline constexpr char TrackSigmaPlusPcmPair[] = "TrackSigmaPlusPcmPair"; +inline constexpr char TrackD0Pair[] = "TrackD0Pair"; +inline constexpr char TrackLcPair[] = "TrackLcPair"; +inline constexpr char LambdaLambdaPair[] = "LambdaLambdaPair"; +inline constexpr char K0shortK0shortPair[] = "K0shortK0shortPair"; +inline constexpr char LambdaK0shortPair[] = "LambdaK0shortPair"; +inline constexpr char D0D0Pair[] = "D0D0Pair"; +inline constexpr char TrackPhiPair[] = "TrackPhiPair"; +inline constexpr char TrackKstar0Pair[] = "TrackKstar0Pair"; +inline constexpr char TrackRho0Pair[] = "TrackRho0Pair"; +inline constexpr char TrackPi0Pair[] = "TrackPi0Pair"; +inline constexpr char TrackEtaPair[] = "TrackEtaPair"; +inline constexpr char LambdaPhiPair[] = "LambdaPhiPair"; +inline constexpr char LambdaKstar0Pair[] = "LambdaKstar0Pair"; +inline constexpr char LambdaRho0Pair[] = "LambdaRho0Pair"; +inline constexpr char K0shortPhiPair[] = "K0shortPhiPair"; +inline constexpr char K0shortKstar0Pair[] = "K0shortKstar0Pair"; +inline constexpr char K0shortRho0Pair[] = "K0shortRho0Pair"; +inline constexpr char TrackTrackTrackTriplet[] = "TrackTrackTrackTriplet"; +inline constexpr char TrackTrackLambdaTriplet[] = "TrackTrackLambdaTriplet"; +inline constexpr char TrackTrackXiTriplet[] = "TrackTrackXiTriplet"; +inline constexpr char TrackTrackOmegaTriplet[] = "TrackTrackOmegaTriplet"; +} // namespace role + +namespace devicename +{ +constexpr const char* FemtoPairTrackTrack = "femto-pair-track-track"; +constexpr const char* FemtoPairTrackV0 = "femto-pair-track-v0"; +constexpr const char* FemtoPairTrackCascade = "femto-pair-track-cascade"; +constexpr const char* FemtoPairTrackKink = "femto-pair-track-kink"; +constexpr const char* FemtoPairTrackCharmHadron = "femto-pair-track-charm-hadron"; +constexpr const char* FemtoPairV0V0 = "femto-pair-v0-v0"; +constexpr const char* FemtoPairD0D0 = "femto-pair-d0-d0"; +constexpr const char* FemtoPairTrackTwoTrackResonance = "femto-pair-track-two-track-resonance"; +constexpr const char* FemtoPairTrackTwoPhotonResonance = "femto-pair-track-two-photon-resonance"; +constexpr const char* FemtoPairV0TwoTrackResonance = "femto-pair-v0-two-track-resonance"; +constexpr const char* FemtoTripletTrackTrackTrack = "femto-triplet-track-track-track"; +constexpr const char* FemtoTripletTrackTrackV0 = "femto-triplet-track-track-v0"; +constexpr const char* FemtoTripletTrackTrackCascade = "femto-triplet-track-track-cascade"; +} // namespace devicename + +// One assigned bit of FColTags::collisionTag: `deviceName` is the matched sibling device's own +// (unique) DPL device name, `role` names which particle-selection group of that device the bit +// covers (e.g. "TrackSelection1", "LambdaSelection1"), and `bit` is its position in collisionTag. +// One assigned bit means "this collision has what it takes to form a pair of this TYPE": both the +// particle-1-role candidate and the particle-2-role candidate (or, for a same-species pair, at +// least 2 distinct candidates passing the one shared selection) are present. One bit per pair type +// per task instance -- not one bit per particle role -- since during mixing the same bit is +// checked identically on both the anchor and the partner collision (either one could end up +// supplying either role across the full mixing sweep). +struct TaggedRole { + std::string deviceName; + std::string role; + int bit = 0; +}; + +// The single canonical bit-assignment walk: scans the workflow's device list in order and, for +// every recognized pair/triplet task instance found, consumes the next free bit(s) for its +// role(s) -- exactly what femtoProducerCollisionTagger.cxx uses to decide what to tag, and what a +// consuming task calls to recompute (from the same static device list) which bit(s) ended up +// being its own. Keep this in sync with femtoProducerCollisionTagger.cxx's supported task types: whichever +// task types are added there must be added here identically, in the same order, or the tagger and +// its consumers will silently disagree about which bit means what. +inline std::vector discoverTaggedRoles(o2::framework::RunningWorkflowInfo const& workflows) +{ + std::vector roles; + int counter = 0; + for (o2::framework::DeviceSpec const& device : workflows.devices) { + if (device.name.find(devicename::FemtoPairTrackTrack) != std::string::npos) { + // Gated on whether ANY of the *WithTagging* process functions that actually consume this + // bit is enabled (processMixedEventWithTagging itself, or one of the + // ...AndMass/...AndEventShape/...AndFitVars combined variants -- all share the same role + // and tag bit, since the tag-bit check and the extra joined columns are independent + // concerns). An instance that enables none of them will never look this bit up, so + // assigning it one anyway would just waste a slot out of the hard-capped budget + // (MaxTagBits below) for nothing. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackV0) != std::string::npos) { + // Each species gates independently on its own *MixedEventWithTagging* switch family -- + // Lambda and K0short are activated completely independently of each other (e.g. + // same-event-only for one species, tagged-mixed-event for the other is a legitimate + // combination), and, same as TrackTrackPair above, same-event/Mc/WithFitVars-without-tagging + // variants are irrelevant here since none of them ever consume a tag bit. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackLambdaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackK0shortPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if + // both are enabled), but gating independently costs nothing and needs no special-casing + // here. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackXiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackOmegaPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackKink) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processSigmaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPlusPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusPcmMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPlusPcmPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processD0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackD0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLcMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackLcPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0V0) != std::string::npos) { + // Lambda-lambda, k0short-k0short and lambda-k0short are mutually exclusive on a single + // instance (the task itself fatals if more than one is enabled), but gating independently + // costs nothing and needs no special-casing here. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaLambdaMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaLambdaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortK0shortPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaK0shortPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairD0D0) != std::string::npos) { + // Only one pair type possible per instance (sign on D0Selection1/2 picks the hypothesis), so + // this task's WithTagging process function is just "processMixedEventWithTagging", same local + // name as femtoPairTrackTrack.cxx's -- fine since option names are scoped per-device. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::D0D0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + // Phi, Kstar0 and Rho0 are mutually exclusive on a single instance (the task itself fatals + // if more than one is enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackRho0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + // Pi0 and Eta are mutually exclusive on a single instance, same as above. + if (hasAnyEnabledOptionStartingWith(device, "processPi0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackPi0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processEtaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackEtaPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + // All six v0 x resonance combinations are mutually exclusive on a single instance (the task + // itself fatals if more than one is enabled), same as above. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaRho0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortRho0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackTrackTriplet, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackLambdaTriplet, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if both + // are enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackXiTriplet, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackOmegaTriplet, counter++}); + } + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the same + // pattern. Whatever is added here must also be added to femtoProducerCollisionTagger.cxx's + // init(), in the same order, since that is where the selection VALUES behind each of these + // bits actually get read and checked. + } + return roles; +} + +// Convenience for a consuming task: find the bit this device's own `role` was assigned, or -1 if +// it was never reached (e.g. this device's own process switch wasn't enabled on the tagger, so it +// never requested this role at all). +inline int findOwnBit(std::vector const& roles, std::string const& ownDeviceName, std::string const& role) +{ + for (auto const& r : roles) { + if (r.deviceName == ownDeviceName && r.role == role) { + return r.bit; + } + } + return -1; +} + +// Carries a consuming task's own, fixed pair-type role (Role, one of the role:: constants above, +// supplied at the instantiation site -- e.g. ConfTagging confTagging;) and +// the bit that role was assigned, discovered once in init() into `internalTagMask` (not read from +// the JSON config). Bundling both together means a call like findOwnBit(roles, ownDevice.name, +// confTagging.Role) always uses the exact role this same object's mask gets set for, instead of +// the role being a second, independently-spelled literal at the call site. Every task that opts +// into tagger-filtered mixing instantiates one of these; wrapped in a ConfigurableGroup (rather +// than a bare Configurable) so more fields can be added here later without having to touch every +// consumer. `prefix` folds in Role itself: a task with more than one pair type (e.g. +// femtoPairTrackV0.cxx's Lambda and K0short, each needing their own tag bit) instantiates +// ConfTagging twice, and without this, both would register the exact same CLI/JSON option name +// ("Tagging.internalTagMask" twice over), which DPL rejects at startup as an ambiguous option. +template +struct ConfTagging : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Tagging") + role; + static constexpr char const* Role = role; + o2::framework::Configurable internalTagMask{"internalTagMask", 0, "internal: discovered collision tagger bitmask, set automatically in init() -- do not set manually"}; +}; + +} // namespace o2::analysis::femto::collisiontagger + +#endif // PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 4816b1527b7..651e591d3d6 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -143,6 +143,18 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMasks_001, "FCOLMASK", 1, //! collision m using FColMasks = FColMasks_001; using StoredFColMasks = StoredFColMasks_001; +// table for collision tags: one bit per (wagon, pair type) combination the collision tagger was +// self-configured to check, set if the collision has what it takes to form at least one pair of +// that type (both particle-1-role and particle-2-role candidates present, or, for a same-species +// pair, at least 2 distinct candidates passing the one shared selection). Used to pre-filter the +// mixed-event pool instead of discovering empty collisions only after slicing. Which bit means +// what is assigned at self-configuration time and logged by the tagger; see +// PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColTags_001, "FCOLTAG", 1, //! collision tags + femtocollisions::CollisionTag); +using FColTags = FColTags_001; +using StoredFColTags = StoredFColTags_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FColSphericities_001, "FCOLSPHERICITY", 1, //! sphericity femtocollisions::Sphericity); using FColSphericities = FColSphericities_001; diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 9658d1b354f..d804d6e39fa 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -14,6 +14,11 @@ o2physics_add_dpl_workflow(femto-producer PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-producer-collision-tagger + SOURCES ./femtoProducerCollisionTagger.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-producer-derived-to-derived SOURCES ./femtoProducerDerivedToDerived.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx new file mode 100644 index 00000000000..2fca898ea81 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx @@ -0,0 +1,704 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerCollisionTagger.cxx +/// \brief Self-configuring task that tags femto collisions with a bit per (pair/triplet task +/// instance, pair type) found in the same workflow, set if the collision has what it +/// takes to form at least one pair of that type: both the particle-1-role and +/// particle-2-role candidate present (or, for a same-species pair, at least 2 distinct +/// candidates passing the one shared selection). Used to pre-filter the mixed-event pool +/// instead of discovering empty collisions only after slicing. Supports any number of +/// same-type "subwagons" (the same task run several times with different cuts in one +/// train) -- bits are assigned in discovery order. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerCollisionTagger { + o2::framework::Produces producedTags; + + // Process functions below take a single collision ITERATOR (not the whole table) as their + // first argument, with the track/v0 tables as later arguments -- the same auto-grouping-by- + // fColId mechanism every processSameEvent() in this codebase already relies on, so tracks/ + // lambdas/k0shorts arrive pre-sliced to that one collision with no manual Preslice/SliceCache/ + // sliceBy() needed here at all. + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoTracks = o2::soa::Join; + using FemtoLambdas = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + using FemtoXis = o2::soa::Join; + using FemtoOmegas = o2::soa::Join; + using FemtoSigmas = o2::soa::Join; + using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; + using FemtoD0s = o2::soa::Join; + using FemtoLcs = o2::soa::Join; + using FemtoPhis = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + // --- femto-pair-track-track: one bit per instance, "a track-track pair is possible here" --- + std::vector mTrackTrackVariants; + + // --- femto-pair-track-v0: one bit per instance per enabled v0 species --- + std::vector mTrackLambdaVariants; + std::vector mTrackK0shortVariants; + + // --- femto-pair-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackXiVariants; + std::vector mTrackOmegaVariants; + + // --- femto-pair-track-kink: one bit per instance per enabled kink species --- + std::vector mTrackSigmaVariants; + std::vector mTrackSigmaPlusVariants; + std::vector mTrackSigmaPlusPcmVariants; + + // --- femto-pair-track-charm-hadron: one bit per instance per enabled charm hadron species --- + std::vector mTrackD0Variants; + std::vector mTrackLcVariants; + + // --- femto-pair-v0-v0: one bit per instance per enabled v0-v0 combination --- + std::vector mLambdaLambdaVariants; + std::vector mK0shortK0shortVariants; + std::vector mLambdaK0shortVariants; + + // --- femto-pair-d0-d0: one bit per instance --- + std::vector mD0D0Variants; + + // --- femto-pair-track-two-track-resonance: one bit per instance per enabled species --- + std::vector mTrackPhiVariants; + std::vector mTrackKstar0Variants; + std::vector mTrackRho0Variants; + + // --- femto-pair-track-two-photon-resonance: one bit per instance per enabled species --- + std::vector mTrackPi0Variants; + std::vector mTrackEtaVariants; + + // --- femto-pair-v0-two-track-resonance: one bit per instance per enabled combination --- + std::vector mLambdaPhiVariants; + std::vector mLambdaKstar0Variants; + std::vector mLambdaRho0Variants; + std::vector mK0shortPhiVariants; + std::vector mK0shortKstar0Variants; + std::vector mK0shortRho0Variants; + + // --- femto-triplet-track-track-track: one bit per instance --- + std::vector mTrackTrackTrackVariants; + + // --- femto-triplet-track-track-v0: one bit per instance --- + std::vector mTrackTrackLambdaVariants; + + // --- femto-triplet-track-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackTrackXiVariants; + std::vector mTrackTrackOmegaVariants; + + void init(o2::framework::InitContext& context) + { + // FColTags gets exactly one row written per collision; if more than one process function were + // enabled, each would try to write its own row for the same collision. Until FColTags is + // extended to carry one column per wagon type, only one process function may run at a time -- + // run separate tagger instances (with separate output files/merges) for separate wagon types. + int activeProcesses = static_cast(doprocessTrackTrack) + static_cast(doprocessTrackV0) + static_cast(doprocessTrackCascade) + static_cast(doprocessTrackKink) + static_cast(doprocessTrackSigmaPlusPcm) + static_cast(doprocessTrackD0) + static_cast(doprocessTrackLc) + static_cast(doprocessV0V0) + static_cast(doprocessD0D0) + static_cast(doprocessTrackTwoTrackResonance) + static_cast(doprocessTrackTwoPhotonResonance) + static_cast(doprocessV0TwoTrackResonance) + static_cast(doprocessTripletTrackTrackTrack) + static_cast(doprocessTripletTrackTrackV0) + static_cast(doprocessTripletTrackTrackCascade); + if (activeProcesses != 1) { + LOG(fatal) << "Exactly one process function must be enabled on the collision tagger, found " << activeProcesses << ". Breaking..."; + } + + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + LOG(info) << " Collision tagger self-configuration "; + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + + // discoverTaggedRoles() is the single canonical bit-assignment walk, shared with every + // consuming pair/triplet task (see Core/collisionTagger.h) -- it assigns bits to every + // recognized task instance found in the workflow, regardless of which of THIS tagger + // instance's own process functions is enabled, so that bit numbers stay globally consistent + // even when multiple tagger instances (one per wagon type) run side by side. + auto& workflows = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + + auto findDevice = [&workflows](std::string const& name) -> o2::framework::DeviceSpec const& { + for (auto const& device : workflows.devices) { + if (device.name == name) { + return device; + } + } + LOG(fatal) << "Device " << name << " vanished between discovery and lookup. Breaking..."; + return workflows.devices.front(); // unreachable, silences -Wreturn-type + }; + + for (auto const& r : roles) { + LOG(info) << " bit " << r.bit << " = " << r.deviceName << " " << r.role; + if (doprocessTrackTrack && r.role == collisiontagger::role::TrackTrackPair && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // When sameSpecies is on, the real pair task's own dispatcher (PairTrackTrackBuilder:: + // processMixedEvent in pairBuilder.h) uses partition1 for BOTH roles and never consults + // TrackSelection2 at all -- mirror that here instead of reading TrackSelection2 off the + // device, since the user is only expected to configure TrackSelection1 in that mode and + // TrackSelection2 may be left at an unrelated default. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.bit = r.bit; + mTrackTrackVariants.push_back(v); + } else if (doprocessTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackLambdaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackLambdaVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackK0shortPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.bit = r.bit; + mTrackK0shortVariants.push_back(v); + } + } else if (doprocessTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackXiPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackXiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackOmegaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackOmegaVariants.push_back(v); + } + } else if ((doprocessTrackKink || doprocessTrackSigmaPlusPcm) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackSigmaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); + v.bit = r.bit; + mTrackSigmaVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackSigmaPlusPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); + v.bit = r.bit; + mTrackSigmaPlusVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackSigmaPlusPcmPair) { + collisiontagger::TrackKinkPcmVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKinkPcmSelection(device, "SigmaPlusPcmSelection1"); + v.bit = r.bit; + mTrackSigmaPlusPcmVariants.push_back(v); + } + } else if ((doprocessTrackD0 || doprocessTrackLc) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackD0Pair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "D0Selection1"); + v.bit = r.bit; + mTrackD0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackLcPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LcSelection1"); + v.bit = r.bit; + mTrackLcVariants.push_back(v); + } + } else if (doprocessV0V0 && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::LambdaLambdaPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // Mirror PairV0V0Builder::processMixedEvent(), which uses partition1 for BOTH roles and + // never consults LambdaSelection2 at all when sameSpecies -- same override as + // TrackTrackPair above. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); + v.bit = r.bit; + mLambdaLambdaVariants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortK0shortPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mK0shortK0shortVariants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaK0shortPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mLambdaK0shortVariants.push_back(v); + } + } else if (doprocessD0D0 && r.deviceName.find(collisiontagger::devicename::FemtoPairD0D0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "D0Selection1"); + collisiontagger::V0SelectionValues sel2Raw = collisiontagger::readV0Selection(device, "D0Selection2"); + bool confSameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // PairD0D0Builder::init() forces sameSpecies off when the two signs disagree (see + // pairBuilder.h) -- mirror that override here too, otherwise a sign-flip-only d0-d0bar + // config would be mis-tagged as a same-species pair. + v.sameSpecies = confSameSpecies && (v.sel1.sign == sel2Raw.sign); + v.sel2 = v.sameSpecies ? v.sel1 : sel2Raw; + v.bit = r.bit; + mD0D0Variants.push_back(v); + } else if (doprocessTrackTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackPhiPair) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mTrackPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackKstar0Pair) { + collisiontagger::TrackKstar0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mTrackKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackRho0Pair) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mTrackRho0Variants.push_back(v); + } + } else if (doprocessTrackTwoPhotonResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackPi0Pair) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "Pi0Selection1"); + v.bit = r.bit; + mTrackPi0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackEtaPair) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "EtaSelection1"); + v.bit = r.bit; + mTrackEtaVariants.push_back(v); + } + } else if (doprocessV0TwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::LambdaPhiPair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mLambdaPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaKstar0Pair) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mLambdaKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaRho0Pair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mLambdaRho0Variants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortPhiPair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mK0shortPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortKstar0Pair) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mK0shortKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortRho0Pair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mK0shortRho0Variants.push_back(v); + } + } else if (doprocessTripletTrackTrackTrack && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + collisiontagger::TrackSelectionValues sel3Raw = collisiontagger::readTrackSelection(device, "TrackSelection3"); + v.same123 = collisiontagger::hasEnabledOption(device, "Mixing.particle123AreSameSpecies"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + // Mirror TripletTrackTrackTrackBuilder::processMixedEvent(), which ignores + // TrackSelection2/3 entirely when same123 (uses partition1 for all three roles), and + // ignores TrackSelection2 (but not 3) when same12 -- same override trick as + // TrackTrackVariant's sameSpecies above. + v.sel2 = (v.same123 || v.same12) ? v.sel1 : sel2Raw; + v.sel3 = v.same123 ? v.sel1 : sel3Raw; + v.bit = r.bit; + mTrackTrackTrackVariants.push_back(v); + } else if (doprocessTripletTrackTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + v.sel2 = v.same12 ? v.sel1 : sel2Raw; + v.sel3 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackTrackLambdaVariants.push_back(v); + } else if (doprocessTripletTrackTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + collisiontagger::TrackSelectionValues sel2 = same12 ? sel1 : sel2Raw; + if (r.role == collisiontagger::role::TrackTrackXiTriplet) { + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = sel2; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackTrackXiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackTrackOmegaTriplet) { + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = sel2; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackTrackOmegaVariants.push_back(v); + } + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the + // same pattern. + } + + if (doprocessTrackTrack && mTrackTrackVariants.empty()) { + LOG(fatal) << "processTrackTrack is enabled, but no femto-pair-track-track device was found in this workflow."; + } + if (doprocessTrackV0 && mTrackLambdaVariants.empty() && mTrackK0shortVariants.empty()) { + LOG(fatal) << "processTrackV0 is enabled, but no femto-pair-track-v0 device (with processLambdaMixedEventWithTagging " + << "or processK0shortMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackCascade && mTrackXiVariants.empty() && mTrackOmegaVariants.empty()) { + LOG(fatal) << "processCascade is enabled, but no femto-pair-track-cascade device (with processXiMixedEventWithTagging " + << "or processOmegaMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackKink && mTrackSigmaVariants.empty() && mTrackSigmaPlusVariants.empty()) { + LOG(fatal) << "processTrackKink is enabled, but no femto-pair-track-kink device (with processSigmaMixedEventWithTagging/" + << "processSigmaPlusMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackSigmaPlusPcm && mTrackSigmaPlusPcmVariants.empty()) { + LOG(fatal) << "processTrackSigmaPlusPcm is enabled, but no femto-pair-track-kink device (with " + << "processSigmaPlusPcmMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackD0 && mTrackD0Variants.empty()) { + LOG(fatal) << "processTrackD0 is enabled, but no femto-pair-track-charm-hadron device (with processD0MixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackLc && mTrackLcVariants.empty()) { + LOG(fatal) << "processTrackLc is enabled, but no femto-pair-track-charm-hadron device (with processLcMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessV0V0 && mLambdaLambdaVariants.empty() && mK0shortK0shortVariants.empty() && mLambdaK0shortVariants.empty()) { + LOG(fatal) << "processV0V0 is enabled, but no femto-pair-v0-v0 device (with processLambdaLambdaMixedEventWithTagging/" + << "processK0shortK0shortMixedEventWithTagging/processLambdaK0shortMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessD0D0 && mD0D0Variants.empty()) { + LOG(fatal) << "processD0D0 is enabled, but no femto-pair-d0-d0 device (with processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackTwoTrackResonance && mTrackPhiVariants.empty() && mTrackKstar0Variants.empty() && mTrackRho0Variants.empty()) { + LOG(fatal) << "processTrackTwoTrackResonance is enabled, but no femto-pair-track-two-track-resonance device (with " + << "processPhiMixedEventWithTagging/processKstar0MixedEventWithTagging/processRho0MixedEventWithTagging enabled) " + << "was found in this workflow."; + } + if (doprocessTrackTwoPhotonResonance && mTrackPi0Variants.empty() && mTrackEtaVariants.empty()) { + LOG(fatal) << "processTrackTwoPhotonResonance is enabled, but no femto-pair-track-two-photon-resonance device (with " + << "processPi0MixedEventWithTagging/processEtaMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessV0TwoTrackResonance && mLambdaPhiVariants.empty() && mLambdaKstar0Variants.empty() && mLambdaRho0Variants.empty() && + mK0shortPhiVariants.empty() && mK0shortKstar0Variants.empty() && mK0shortRho0Variants.empty()) { + LOG(fatal) << "processV0TwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of the " + << "processXxxMixedEventWithTagging switches enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackTrack && mTrackTrackTrackVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackTrack is enabled, but no femto-triplet-track-track-track device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackV0 && mTrackTrackLambdaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackV0 is enabled, but no femto-triplet-track-track-v0 device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackCascade && mTrackTrackXiVariants.empty() && mTrackTrackOmegaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackCascade is enabled, but no femto-triplet-track-track-cascade device (with " + << "processXiMixedEventWithTagging/processOmegaMixedEventWithTagging enabled) was found in this workflow."; + } + if (!roles.empty() && roles.back().bit >= collisiontagger::MaxTagBits) { + LOG(fatal) << "Too many tag bits requested (" << (roles.back().bit + 1) << "); collision tagger supports up to " << collisiontagger::MaxTagBits << "."; + } + } + + // `tracksThisCol` arrives already sliced to `col` (see FemtoCollision comment above) -- the + // entire per-collision tagging step is one call to tagAndProduce(), which builds the tag + // bitset, runs the same-type-pair check (both track roles' selections, or 2 distinct entries + // for a same-species pair) against every variant, and produces the row. + void processTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::tagAndProduce(producedTags, mTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTrack, "Tag collisions for femto-pair-track-track wagon(s)", false); + + // Two species checked independently, both contributing to the one tag this collision gets -- + // can't collapse to a single tagAndProduce() call like processTrackTrack above (that helper + // assumes exactly one check per row), but each cross-type check (track role + v0 role) is + // still just one call to tagCrossVariants(). + void processTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackLambdaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackLambdaVariants, tracksThisCol, trackPassesFn, lambdasThisCol, + [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); + } + if (!mTrackK0shortVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackK0shortVariants, tracksThisCol, trackPassesFn, k0shortsThisCol, + [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackV0, "Tag collisions for femto-pair-track-v0 wagon(s)", false); + + void processTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; + if (!mTrackXiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); + } + if (!mTrackOmegaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackCascade, "Tag collisions for femto-pair-track-cascade wagon(s)", false); + + // Sigma and SigmaPlus (kink) tables are always filled together by the producer, but SigmaPlusPcm + // comes from a mutually exclusive producer process function (processTracksSigmaPlusPcmsRun3pp/Mc) -- + // so it gets its own process function below instead of joining this one, otherwise enabling this + // process would force the producer to supply a table combination it can never actually produce. + void processTrackKink(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmas const& sigmasThisCol, FemtoSigmaPlus const& sigmaPlusThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackSigmaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackSigmaVariants, tracksThisCol, trackPassesFn, sigmasThisCol, + [](auto const& sigma, auto const& sel) { return collisiontagger::lambdaPasses(sigma, sel); }); + } + if (!mTrackSigmaPlusVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusVariants, tracksThisCol, trackPassesFn, sigmaPlusThisCol, + [](auto const& sigmaPlus, auto const& sel) { return collisiontagger::lambdaPasses(sigmaPlus, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackKink, "Tag collisions for femto-pair-track-kink wagon(s) (Sigma/SigmaPlus)", false); + + void processTrackSigmaPlusPcm(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlusPcm const& sigmaPlusPcmThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusPcmVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmaPlusPcmThisCol, [](auto const& sigmaPlusPcm, auto const& sel) { return collisiontagger::kinkPcmPasses(sigmaPlusPcm, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigmaPlusPcm, "Tag collisions for femto-pair-track-kink wagon(s) (SigmaPlusPcm)", false); + + // D0 and Lc are also mutually exclusive on the producer side (separate process functions, counted + // together in its own "exactly one process enabled" check), so each gets its own process function + // here too, for the same reason as SigmaPlusPcm above. + void processTrackD0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoD0s const& d0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackD0Variants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, d0sThisCol, [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackD0, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (D0)", false); + + void processTrackLc(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLcs const& lcsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackLcVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lcsThisCol, [](auto const& lc, auto const& sel) { return collisiontagger::lambdaPasses(lc, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLc, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (Lc)", false); + + // No track table involved here at all -- v0-v0 is checked purely between the two v0 tables + // themselves (lambda/k0short), same as D0D0 below is checked purely within FemtoD0s. + void processV0V0(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + if (!mLambdaLambdaVariants.empty()) { + collisiontagger::tagPairVariants(tag, mLambdaLambdaVariants, lambdasThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }); + } + if (!mK0shortK0shortVariants.empty()) { + collisiontagger::tagPairVariants(tag, mK0shortK0shortVariants, k0shortsThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + } + if (!mLambdaK0shortVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaK0shortVariants, lambdasThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }, k0shortsThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0V0, "Tag collisions for femto-pair-v0-v0 wagon(s)", false); + + void processD0D0(FemtoCollision const& /*col*/, FemtoD0s const& d0sThisCol) + { + collisiontagger::tagAndProduce(producedTags, mD0D0Variants, d0sThisCol, + [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processD0D0, "Tag collisions for femto-pair-d0-d0 wagon(s)", false); + + // femto-producer-resonances has no mutual-exclusivity gate between Phi/Kstar0/Rho0 (all three + // default to enabled, built from the same track pool with different PID hypotheses), so they can + // safely be combined into one process function here too. + void processTrackTwoTrackResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPhiVariants, tracksThisCol, trackPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mTrackKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackKstar0Variants, tracksThisCol, trackPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mTrackRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackRho0Variants, tracksThisCol, trackPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoTrackResonance, "Tag collisions for femto-pair-track-two-track-resonance wagon(s)", false); + + // femto-producer-two-photon-resonances also has no mutual-exclusivity gate between Pi0/Eta. + void processTrackTwoPhotonResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPi0s const& pi0sThisCol, FemtoEtas const& etasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPi0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPi0Variants, tracksThisCol, trackPassesFn, pi0sThisCol, + [](auto const& pi0, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(pi0, sel); }); + } + if (!mTrackEtaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackEtaVariants, tracksThisCol, trackPassesFn, etasThisCol, + [](auto const& eta, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(eta, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoPhotonResonance, "Tag collisions for femto-pair-track-two-photon-resonance wagon(s)", false); + + // No track table involved here at all -- same as processV0V0 above, checked purely between the + // v0 tables (lambda/k0short) and the resonance tables (phi/kstar0/rho0) themselves. + void processV0TwoTrackResonance(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto lambdaPassesFn = [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }; + auto k0shortPassesFn = [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }; + if (!mLambdaPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaPhiVariants, lambdasThisCol, lambdaPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mLambdaKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaKstar0Variants, lambdasThisCol, lambdaPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mLambdaRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaRho0Variants, lambdasThisCol, lambdaPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + if (!mK0shortPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortPhiVariants, k0shortsThisCol, k0shortPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mK0shortKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortKstar0Variants, k0shortsThisCol, k0shortPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mK0shortRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortRho0Variants, k0shortsThisCol, k0shortPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0TwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s)", false); + + void processTripletTrackTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagTripletVariants(tag, mTrackTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackTrack, "Tag collisions for femto-triplet-track-track-track wagon(s)", false); + + void processTripletTrackTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackLambdaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lambdasThisCol, [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackV0, "Tag collisions for femto-triplet-track-track-v0 wagon(s)", false); + + void processTripletTrackTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; + if (!mTrackTrackXiVariants.empty()) { + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); + } + if (!mTrackTrackOmegaVariants.empty()) { + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackCascade, "Tag collisions for femto-triplet-track-track-cascade wagon(s)", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + o2::framework::adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 6b433496642..28dada11562 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +71,10 @@ struct FemtoPairD0D0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +82,9 @@ struct FemtoPairD0D0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingD0D0; + // setup for daughters // separate binning per d0 slot, so that both hypotheses can be binned independently trackhistmanager::ConfD01PosDauBinning confD01PosDauBinning; @@ -141,9 +151,9 @@ struct FemtoPairD0D0 { closepairrejection::ConfCprD0DaugherD0DaughterPos confCprPos; closepairrejection::ConfCprD0DaugherD0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { @@ -153,6 +163,19 @@ struct FemtoPairD0D0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0D0.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processD0D0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0D0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0D0): " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -208,6 +231,16 @@ struct FemtoPairD0D0 { } PROCESS_SWITCH(FemtoPairD0D0, processSameEventMc, "Enable processing same event processing for d0-d0 with mc information", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0D0.internalTagMask); + taggedCollisions.bindTable(cols); + pairD0D0Builder.processMixedEvent(taggedCollisions, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairD0D0, processMixedEventWithTagging, "Enable processing mixed event processing for d0-d0, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairD0D0Builder.processMixedEvent(cols, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 0f190a841cf..dc27468a21b 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,6 +59,10 @@ struct FemtoPairTrackCascade { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in FColTags , only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; @@ -78,6 +85,11 @@ struct FemtoPairTrackCascade { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging -- + // two separate instances since Xi and Omega are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -185,9 +197,9 @@ struct FemtoPairTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging; bool processFitVars = doprocessXiSameEventWithFitVars || doprocessXiMixedEventWithFitVars || doprocessOmegaSameEventWithFitVars || doprocessOmegaMixedEventWithFitVars; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; @@ -195,8 +207,33 @@ struct FemtoPairTrackCascade { LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc || doprocessXiMixedEventWithTagging; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc || doprocessOmegaMixedEventWithTagging; + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; @@ -280,6 +317,17 @@ struct FemtoPairTrackCascade { } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires + // a femto-collision-tagger device (processTrackCascade enabled) in the same workflow -- see init(). + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { pairTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -316,6 +364,15 @@ struct FemtoPairTrackCascade { } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 172c3c2ffc8..2e0370d99f9 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -60,6 +63,10 @@ struct FemtoPairTrackCharmHadron { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracksWithLabel = o2::soa::Join; using FemtoD0sWithLabel = o2::soa::Join; using FemtoLcsWithLabel = o2::soa::Join; @@ -72,6 +79,11 @@ struct FemtoPairTrackCharmHadron { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging -- two + // separate instances since D0 and Lc are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingD0; + collisiontagger::ConfTagging confTaggingLc; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -174,11 +186,11 @@ struct FemtoPairTrackCharmHadron { closepairrejection::ConfCprTrackLcKaon confCprLcKaon; closepairrejection::ConfCprTrackLcPion confCprLcPion; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent; + bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent || doprocessD0MixedEventWithTagging; bool processD0Mc = doprocessD0SameEventMc || doprocessD0MixedEventMc; - bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent; + bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent || doprocessLcMixedEventWithTagging; bool processLcMc = doprocessLcSameEventMc || doprocessLcMixedEventMc; if ((static_cast(processD0) + static_cast(processD0Mc) + static_cast(processLc) + static_cast(processLcMc)) > 1) { @@ -187,6 +199,31 @@ struct FemtoPairTrackCharmHadron { bool processData = processD0 || processLc; + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessD0MixedEventWithTagging || doprocessLcMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessD0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0.Role); + if (bit < 0) { + LOG(fatal) << "processD0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackD0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0): " << bit; + } + if (doprocessLcMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLc.Role); + if (bit < 0) { + LOG(fatal) << "processLcMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackLc enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLc.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lc): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -255,6 +292,16 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0SameEvent, "Enable processing same event processing for tracks and D0s", true); + // Opt-in counterpart of processD0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processD0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackD0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0MixedEventWithTagging, "Enable processing mixed event processing for tracks and D0s, pre-filtered using the collision tagger", false); + void processD0MixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairTrackD0Builder.processMixedEvent(cols, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -279,6 +326,15 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcSameEvent, "Enable processing same event processing for tracks and Lcs", false); + // Opt-in counterpart of processLcMixedEvent() below -- same idiom as processD0MixedEventWithTagging above. + void processLcMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLc.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLcBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcMixedEventWithTagging, "Enable processing mixed event processing for tracks and Lcs, pre-filtered using the collision tagger", false); + void processLcMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) { pairTrackLcBuilder.processMixedEvent(cols, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index affa4a1fcff..3c1385d945d 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/kinkHistManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -57,6 +60,10 @@ struct FemtoPairTrackKink { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoSigmas = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; @@ -76,6 +83,12 @@ struct FemtoPairTrackKink { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Sigma/SigmaPlus/SigmaPlusPcm are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingSigma; + collisiontagger::ConfTagging confTaggingSigmaPlus; + collisiontagger::ConfTagging confTaggingSigmaPlusPcm; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -184,19 +197,19 @@ struct FemtoPairTrackKink { // setup cpr closepairrejection::ConfCprTrackKinkDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent; + bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging; bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; } - bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc; - bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc; + bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc || doprocessSigmaMixedEventWithTagging; + bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusMixedEventWithTagging; - bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc; + bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc || doprocessSigmaPlusPcmMixedEventWithTagging; if (processSigma && processSigmaPlus) { LOG(fatal) << "Both Sigma-track and SigmaPlus-track processing is enabled. Breaking..."; @@ -205,6 +218,40 @@ struct FemtoPairTrackKink { LOG(fatal) << "Both Sigma/SigmaPlus-track and SigmaPlusPcm-track processing is enabled. Breaking..."; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessSigmaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigma.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigma.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Sigma): " << bit; + } + if (doprocessSigmaPlusMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlus.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlus.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlus): " << bit; + } + if (doprocessSigmaPlusPcmMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlusPcm.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusPcmMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackSigmaPlusPcm enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlusPcm.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlusPcm): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -267,6 +314,16 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaSameEventMc, "Enable processing same event processing for tracks and sigmas with MC information", false); + // Opt-in counterpart of processSigmaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processSigmaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigma.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigmas, pre-filtered using the collision tagger", false); + void processSigmaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) { pairTrackSigmaBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -291,6 +348,15 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusSameEventMc, "Enable processing same event processing for tracks and sigma plus with MC information", false); + // Opt-in counterpart of processSigmaPlusMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlus.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus, pre-filtered using the collision tagger", false); + void processSigmaPlusMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) { pairTrackSigmaPlusBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -315,6 +381,15 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEventMc, "Enable processing same event processing for tracks and sigma plus (PCM) with MC information", false); + // Opt-in counterpart of processSigmaPlusPcmMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusPcmMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlusPcm.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusPcmBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus (PCM), pre-filtered using the collision tagger", false); + void processSigmaPlusPcmMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) { pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 660f368891a..11379064023 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -55,11 +58,26 @@ struct FemtoPairTrackTrack { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processMixedEventWithTagging. collisionFilter below (declared on the base FemtoCollisions + // columns) auto-applies to this joined/filtered type too, exactly as it already does for + // FilteredFemtoCollisionsWithLabel above; the extra tag-bit condition is then applied via a + // Partition declared locally inside processMixedEventWithTagging itself (see there), the same + // "Partition declared and bound inside process()" idiom as Tutorials/src/eventMixing.cxx's + // MixedEventsPartitionedTracks -- so nothing here needs a second top-level Filter. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + // collision table with event shape, same filter as standard collisions using FemtoCollisionsWithEventShape = o2::soa::Join; using FilteredFemtoCollisionsWithEventShape = o2::soa::Filtered; using FilteredFemtoCollisionWithEventShape = FilteredFemtoCollisionsWithEventShape::iterator; + // opt-in: carries both FColShapes and FColTags, for processMixedEventWithTaggingAndEventShape -- + // same collisionFilter auto-apply reasoning as FemtoCollisionsWithTags above. + using FemtoCollisionsWithEventShapeAndTags = o2::soa::Join; + using FilteredFemtoCollisionsWithEventShapeAndTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; // for analysis which require particles at high pt, add tof mass so sidebands can be used @@ -84,6 +102,10 @@ struct FemtoPairTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging -- see processMixedEventWithTagging for + // how it's used. + collisiontagger::ConfTagging confTagging; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; particlecleaner::ConfTrackCleaner1 confTrackCleaner1; @@ -140,14 +162,32 @@ struct FemtoPairTrackTrack { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithFitVars) + static_cast(doprocessSameEventMc)) > 1 || - (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc)) > 1) { + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventWithTaggingAndMass) + static_cast(doprocessMixedEventWithTaggingAndEventShape) + static_cast(doprocessMixedEventWithTaggingAndFitVars)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; - bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars; + + if (doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape || doprocessMixedEventWithTaggingAndFitVars) { + // discoverTaggedRoles() is the same canonical device-list walk femtoProducerCollisionTagger.cxx uses + // to assign bits -- replaying it here (rather than asking the tagger directly, which has no + // way to communicate a runtime-computed value back out through RunningWorkflowInfo) is how + // this task finds out which bit(s) ended up being its own. + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTagging.Role); + if (bit < 0) { + LOG(fatal) << "A processMixedEventWithTagging* function is enabled, but no femto-collision-tagger " + << "device (with processTrackTrack enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTagging.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape || doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape; + bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars || doprocessMixedEventWithTaggingAndFitVars; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; @@ -220,6 +260,55 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below: `cols` already gets the standard + // collisionFilter applied (Filtered picks it up automatically, exactly + // as FilteredFemtoCollisionsWithLabel above does). On top of that, a Partition is declared and + // bound right here, locally, to pick out only the collisions the tagger found a qualifying pair + // in -- the same "declare + bindTable() inside process()" idiom as + // Tutorials/src/eventMixing.cxx's MixedEventsPartitionedTracks, so this needs no change to + // pairProcessHelpers.h/pairBuilder.h: processMixedEvent is called exactly as below, just with a + // further-filtered collision view. Requires a femto-collision-tagger device (processTrackTrack + // enabled) in the same workflow -- see init(). + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithMass + // instead of plain FemtoTracks -- the tag bit means the same thing regardless of which track + // table variant is used downstream, since it's computed from the same TrackSelection1/2 cut + // values either way. + void processMixedEventWithTaggingAndMass(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithMass const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndMass, "Enable processing mixed event processing, pre-filtered using the collision tagger (with track masses)", false); + + // Same tagging filter as processMixedEventWithTagging above, on a collision table that also + // carries FColShapes, needed for the kVtxCentEventPlaneAngle mixing policy. + void processMixedEventWithTaggingAndEventShape(FilteredFemtoCollisionsWithEventShapeAndTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndEventShape, "Enable processing mixed event processing, pre-filtered using the collision tagger, with event shape information", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithFitVars + // instead of plain FemtoTracks -- same rationale as processMixedEventWithTaggingAndMass above. + void processMixedEventWithTaggingAndFitVars(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithFitVars const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndFitVars, "Enable processing mixed event processing, pre-filtered using the collision tagger (with FitVars tables)", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx index 66a4cea7412..24fa1adee36 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,10 +34,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -58,6 +61,10 @@ struct FemtoPairTrackTwoPhotonResonance { using FemtoPi0s = o2::soa::Join; using FemtoEtas = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -65,6 +72,12 @@ struct FemtoPairTrackTwoPhotonResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Pi0/Eta are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingPi0; + collisiontagger::ConfTagging confTaggingEta; + // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -141,13 +154,40 @@ struct FemtoPairTrackTwoPhotonResonance { // table, so there is no trajectory to compare against a track's -- see closePairRejection.h) closepairrejection::ConfCprTrackTwoPhotonResonanceDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent) + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent) > 1) { + if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) + + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) > + 1) { LOG(fatal) << "Can only process SE/ME for pi0-tracks or eta-tracks"; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPi0MixedEventWithTagging || doprocessEtaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPi0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPi0.Role); + if (bit < 0) { + LOG(fatal) << "processPi0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPi0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Pi0): " << bit; + } + if (doprocessEtaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingEta.Role); + if (bit < 0) { + LOG(fatal) << "processEtaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingEta.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Eta): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -162,14 +202,14 @@ struct FemtoPairTrackTwoPhotonResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for pi0 - if (doprocessPi0SameEvent || doprocessPi0MixedEvent) { + if (doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) { auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confPi0Binning); auto pairTrackPi0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackPi0Builder.init(&hRegistry, confCollisionBinning, trackSelection, pi0Selection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, pi0HistSpec, dauHistSpec, pairTrackPi0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for eta - if (doprocessEtaSameEvent || doprocessEtaMixedEvent) { + if (doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) { auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confEtaBinning); auto pairTrackEtaHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackEtaBuilder.init(&hRegistry, confCollisionBinning, trackSelection, etaSelection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, etaHistSpec, dauHistSpec, pairTrackEtaHistSpec, cprHistSpec, pairCleanerHistSpec); @@ -184,6 +224,16 @@ struct FemtoPairTrackTwoPhotonResonance { } PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0SameEvent, "Enable same event processing for tracks and pi0s", true); + // Opt-in counterpart of processPi0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPi0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPi0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPi0Builder.processMixedEvent(taggedCollisions, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEventWithTagging, "Enable mixed event processing for tracks and pi0s, pre-filtered using the collision tagger", false); + void processPi0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) { pairTrackPi0Builder.processMixedEvent(cols, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -196,6 +246,16 @@ struct FemtoPairTrackTwoPhotonResonance { } PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaSameEvent, "Enable same event processing for tracks and etas", false); + // Opt-in counterpart of processEtaMixedEvent() below -- same idiom as + // processPi0MixedEventWithTagging above. + void processEtaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingEta.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackEtaBuilder.processMixedEvent(taggedCollisions, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEventWithTagging, "Enable mixed event processing for tracks and etas, pre-filtered using the collision tagger", false); + void processEtaMixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) { pairTrackEtaBuilder.processMixedEvent(cols, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 25602952698..bb46e8cbc1a 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,10 +33,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -57,6 +60,10 @@ struct FemtoPairTrackTwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -64,6 +71,13 @@ struct FemtoPairTrackTwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Phi/Kstar0/Rho0 are mutually exclusive on one instance, but tagged + // independently (see init()). + collisiontagger::ConfTagging confTaggingPhi; + collisiontagger::ConfTagging confTaggingKstar0; + collisiontagger::ConfTagging confTaggingRho0; + // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -162,13 +176,50 @@ struct FemtoPairTrackTwoTrackResonance { // setup cpr closepairrejection::ConfCprTrackResonanceDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent) + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent)) + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent) > 1) { + if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) + + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging)) + + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) > + 1) { LOG(fatal) << "Can only process SE/ME for phi-tracks, rho-tracks or k0*-tracks"; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPhiMixedEventWithTagging || doprocessKstar0MixedEventWithTagging || doprocessRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPhi.Role); + if (bit < 0) { + LOG(fatal) << "processPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Phi): " << bit; + } + if (doprocessKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Kstar0): " << bit; + } + if (doprocessRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingRho0.Role); + if (bit < 0) { + LOG(fatal) << "processRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Rho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -184,21 +235,21 @@ struct FemtoPairTrackTwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for phi - if (doprocessPhiSameEvent || doprocessPhiMixedEvent) { + if (doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) { auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); auto pairTrackPhiHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackPhiBuilder.init(&hRegistry, confCollisionBinning, trackSelection, phiSelection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, phiHistSpec, posDauSpec, negDauSpec, pairTrackPhiHistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent) { + if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging) { auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); auto pairTrackKstar0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackKstar0Builder.init(&hRegistry, confCollisionBinning, trackSelection, kstar0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, kstar0HistSpec, posDauSpec, negDauSpec, pairTrackKstar0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessRho0SameEvent || doprocessRho0MixedEvent) { + if (doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) { auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); auto pairTrackRho0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackRho0Builder.init(&hRegistry, confCollisionBinning, trackSelection, rho0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, rho0HistSpec, posDauSpec, negDauSpec, pairTrackRho0HistSpec, cprHistSpec, pairCleanerHistSpec); @@ -213,6 +264,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiSameEvent, "Enable processing same event processing for tracks and phis", true); + // Opt-in counterpart of processPhiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPhiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiMixedEventWithTagging, "Enable processing mixed event processing for tracks and phis, pre-filtered using the collision tagger", false); + void processPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) { pairTrackPhiBuilder.processMixedEvent(cols, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -225,6 +286,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0SameEvent, "Enable processing same event processing for tracks and kstar0s", false); + // Opt-in counterpart of processKstar0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackKstar0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0MixedEventWithTagging, "Enable processing mixed event processing for tracks and kstar0s, pre-filtered using the collision tagger", false); + void processKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) { pairTrackKstar0Builder.processMixedEvent(cols, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -237,6 +308,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0SameEvent, "Enable processing same event processing for tracks and rho0s", false); + // Opt-in counterpart of processRho0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackRho0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0MixedEventWithTagging, "Enable processing mixed event processing for tracks and rho0s, pre-filtered using the collision tagger", false); + void processRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) { pairTrackRho0Builder.processMixedEvent(cols, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 054e8bc40cf..de4eba27609 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,6 +59,12 @@ struct FemtoPairTrackV0 { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging -- same + // collisionFilter auto-apply reasoning as in femtoPairTrackTrack.cxx. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; @@ -78,6 +87,12 @@ struct FemtoPairTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging + // -- two separate instances since Lambda and K0short are tagged independently (see init()) and + // each gets its own tag bit. + collisiontagger::ConfTagging confTaggingLambda; + collisiontagger::ConfTagging confTaggingK0short; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -175,9 +190,9 @@ struct FemtoPairTrackV0 { // setup cpr closepairrejection::ConfCprTrackV0Daughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent; + bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent || doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging; bool processFitVars = doprocessLambdaSameEventWithFitVars || doprocessLambdaMixedEventWithFitVars || doprocessK0shortSameEventWithFitVars || doprocessK0shortMixedEventWithFitVars; bool processMc = doprocessLambdaSameEventMc || doprocessLambdaMixedEventMc || doprocessK0shortSameEventMc || doprocessK0shortMixedEventMc; @@ -185,13 +200,42 @@ struct FemtoPairTrackV0 { LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc; - bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc; + bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc || doprocessLambdaMixedEventWithTagging; + bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc || doprocessK0shortMixedEventWithTagging; if (processLambda && processK0short) { LOG(fatal) << "Both lambda-track and k0short-track processing is enabled. Breaking..."; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h): + // same-event-only for one species and tagged-mixed-event for the other is a legitimate + // combination, so neither discovery is gated on the other's process switch. + if (doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lambda): " << bit; + } + if (doprocessK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -263,6 +307,17 @@ struct FemtoPairTrackV0 { } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventMc, "Enable processing same event processing for tracks and lambdas with MC information", false); + // Opt-in counterpart of processLambdaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithTagging, "Enable processing mixed event processing for tracks and lambdas, pre-filtered using the collision tagger", false); + void processLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) { pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -299,6 +354,17 @@ struct FemtoPairTrackV0 { } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventMc, "Enable processing same event processing for tracks and k0shorts with MC information", false); + // Opt-in counterpart of processK0shortMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackK0shortBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithTagging, "Enable processing mixed event processing for tracks and k0shorts, pre-filtered using the collision tagger", false); + void processK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index bfb115b7d87..6bf172bbb95 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -34,10 +35,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -63,6 +66,10 @@ struct FemtoPairV0TwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -70,6 +77,16 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- six separate + // instances since all six v0-resonance combinations are mutually exclusive on one instance, but + // tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaPhi; + collisiontagger::ConfTagging confTaggingLambdaKstar0; + collisiontagger::ConfTagging confTaggingLambdaRho0; + collisiontagger::ConfTagging confTaggingK0shortPhi; + collisiontagger::ConfTagging confTaggingK0shortKstar0; + collisiontagger::ConfTagging confTaggingK0shortRho0; + // setup for daughters for v0s trackhistmanager::ConfV01PosDauBinning confV0PosDauBinning; trackhistmanager::ConfV01NegDauBinning confV0NegDauBinning; @@ -277,20 +294,82 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::HistogramRegistry hRegistry{"FemtoV0TwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { // all pair combinations share the same histogram prefixes, so only one combination can be processed per workflow instance - if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) + - static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) + - static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) + - static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) + - static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) + - static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) > + if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) + + static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) + + static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) + + static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) + + static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) + + static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) > 1) { LOG(fatal) << "Can only process one v0-resonance combination (lambda/k0short x phi/kstar0/rho0)"; } + // Each combination discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaPhiMixedEventWithTagging || doprocessLambdaKstar0MixedEventWithTagging || doprocessLambdaRho0MixedEventWithTagging || + doprocessK0shortPhiMixedEventWithTagging || doprocessK0shortKstar0MixedEventWithTagging || doprocessK0shortRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaPhi.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaPhi): " << bit; + } + if (doprocessLambdaKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaKstar0): " << bit; + } + if (doprocessLambdaRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaRho0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaRho0): " << bit; + } + if (doprocessK0shortPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortPhi.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortPhi): " << bit; + } + if (doprocessK0shortKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortKstar0): " << bit; + } + if (doprocessK0shortRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortRho0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortRho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -314,42 +393,42 @@ struct FemtoPairV0TwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for lambda-phi - if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) { + if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairLambdaPhiBuilder.init(&hRegistry, confCollisionBinning, lambdaSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-kstar0 - if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) { + if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairLambdaKstar0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-rho0 - if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) { + if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairLambdaRho0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-phi - if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) { + if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairK0shortPhiBuilder.init(&hRegistry, confCollisionBinning, k0shortSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-kstar0 - if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) { + if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairK0shortKstar0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-rho0 - if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) { + if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairK0shortRho0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); @@ -365,6 +444,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiSameEvent, "Enable processing same event processing for lambdas and phis", true); + // Opt-in counterpart of processLambdaPhiMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaPhiBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiMixedEventWithTagging, "Enable processing mixed event processing for lambdas and phis, pre-filtered using the collision tagger", false); + void processLambdaPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) { pairLambdaPhiBuilder.processMixedEvent(cols, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -378,6 +467,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0SameEvent, "Enable processing same event processing for lambdas and kstar0s", false); + // Opt-in counterpart of processLambdaKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaKstar0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and kstar0s, pre-filtered using the collision tagger", false); + void processLambdaKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) { pairLambdaKstar0Builder.processMixedEvent(cols, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -391,6 +490,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0SameEvent, "Enable processing same event processing for lambdas and rho0s", false); + // Opt-in counterpart of processLambdaRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaRho0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and rho0s, pre-filtered using the collision tagger", false); + void processLambdaRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) { pairLambdaRho0Builder.processMixedEvent(cols, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -404,6 +513,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiSameEvent, "Enable processing same event processing for k0shorts and phis", false); + // Opt-in counterpart of processK0shortPhiMixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortPhiBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiMixedEventWithTagging, "Enable processing mixed event processing for k0shorts and phis, pre-filtered using the collision tagger", false); + void processK0shortPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) { pairK0shortPhiBuilder.processMixedEvent(cols, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -417,6 +536,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0SameEvent, "Enable processing same event processing for k0shorts and kstar0s", false); + // Opt-in counterpart of processK0shortKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortKstar0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and kstar0s, pre-filtered using the collision tagger", false); + void processK0shortKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) { pairK0shortKstar0Builder.processMixedEvent(cols, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -430,6 +559,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0SameEvent, "Enable processing same event processing for k0shorts and rho0s", false); + // Opt-in counterpart of processK0shortRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortRho0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and rho0s, pre-filtered using the collision tagger", false); + void processK0shortRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) { pairK0shortRho0Builder.processMixedEvent(cols, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index 371102a368a..9d9f7b8698b 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -72,6 +75,10 @@ struct FemtoPairV0V0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -79,6 +86,13 @@ struct FemtoPairV0V0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since lambda-lambda/k0short-k0short/lambda-k0short are mutually exclusive on one + // instance, but tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaLambda; + collisiontagger::ConfTagging confTaggingK0shortK0short; + collisiontagger::ConfTagging confTaggingLambdaK0short; + // setup for daughters // separate binning per v0 slot, so that e.g. lambda (proton/pion daughters) and // k0short (pion/pion daughters) can be binned independently @@ -208,11 +222,11 @@ struct FemtoPairV0V0 { closepairrejection::ConfCprV0DaugherV0DaughterPos confCprPos; closepairrejection::ConfCprV0DaugherV0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || - doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || - doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent; + bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || + doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || + doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging; bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; @@ -224,17 +238,51 @@ struct FemtoPairV0V0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } - bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; - bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; - bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || + bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; if (static_cast(processLambdaLambda) + static_cast(processK0shortK0short) + static_cast(processLambdaK0short) > 1) { LOG(fatal) << "Only one of lambda-lambda, k0short-k0short or lambda-k0short processing can be enabled. Breaking..."; } + // Each pair type discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaLambdaMixedEventWithTagging || doprocessK0shortK0shortMixedEventWithTagging || doprocessLambdaK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaLambda): " << bit; + } + if (doprocessK0shortK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortK0short): " << bit; + } + if (doprocessLambdaK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaK0short.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaK0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -322,6 +370,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-lambda with mc information", false); + // Opt-in counterpart of processLambdaLambdaMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaLambdaBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaMixedEventWithTagging, "Enable processing mixed event processing for lambda-lambda, pre-filtered using the collision tagger", false); + void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -346,6 +404,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortSameEventMc, "Enable processing same event processing for k0short-k0short with mc information", false); + // Opt-in counterpart of processK0shortK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processK0shortK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortK0shortBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortMixedEventWithTagging, "Enable processing mixed event processing for k0short-k0short, pre-filtered using the collision tagger", false); + void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -370,6 +438,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortSameEventMc, "Enable processing same event processing for lambda-k0short with mc information", false); + // Opt-in counterpart of processLambdaK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processLambdaK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaK0shortBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortMixedEventWithTagging, "Enable processing mixed event processing for lambda-k0short, pre-filtered using the collision tagger", false); + void processLambdaK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) { pairLambdaK0shortBuilder.processMixedEvent(cols, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index 319fc73ea14..b1ddcf9426c 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -66,12 +69,22 @@ struct FemtoTripletTrackTrackCascade { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Xi and Omega are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -193,23 +206,49 @@ struct FemtoTripletTrackTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventMc)) > 1) { + if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventWithTagging) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventWithTagging) + static_cast(doprocessOmegaMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging || doprocessOmegaMixedEventMc; if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; } + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -280,6 +319,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { tripletTrackTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -304,6 +353,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as + // processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { tripletTrackTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 6c80679fc17..04660e2e342 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -61,6 +64,10 @@ struct FemtoTripletTrackTrackTrack { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -68,6 +75,9 @@ struct FemtoTripletTrackTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -126,17 +136,30 @@ struct FemtoTripletTrackTrackTrack { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackTrack enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -183,6 +206,16 @@ struct FemtoTripletTrackTrackTrack { } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { tripletTrackTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index 22dd856be44..f2b821e2f26 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +71,10 @@ struct FemtoTripletTrackTrackV0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +82,9 @@ struct FemtoTripletTrackTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -148,17 +158,30 @@ struct FemtoTripletTrackTrackV0 { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackV0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -211,6 +234,16 @@ struct FemtoTripletTrackTrackV0 { } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackV0, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { tripletTrackTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); From 59c3010a641d9e085971b05b669855971ced64bf Mon Sep 17 00:00:00 2001 From: Kai Cui <129373281+kcui1@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:34:13 +0100 Subject: [PATCH 582/648] [PWGLF] Add TPC explicitly in hStrangeCorrelation.cxx (#18609) --- .../Strangeness/hStrangeCorrelationFilter.cxx | 25 ++++++++-------- .../Tasks/Strangeness/hStrangeCorrelation.cxx | 29 ++++++++++--------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx index 7643c9ab979..0f32bc668d0 100644 --- a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx @@ -451,7 +451,8 @@ struct HStrangeCorrelationFilter { if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { return false; } - if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + // hasTPC() is needed: since O2-7038 (Oct 2026) tracks without TPC report 256 crossed rows + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { return false; // crossed rows } if (!track.hasITS() && trackSelections.triggerRequireITS) { @@ -481,7 +482,7 @@ struct HStrangeCorrelationFilter { if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { return false; } - if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!assoc.hasTPC() || assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { return false; // crossed rows } if (!assoc.hasITS() && trackSelections.assocRequireITS) { @@ -848,10 +849,10 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negdau.hasTPC() || negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posdau.hasTPC() || posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { @@ -987,10 +988,10 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negdau.hasTPC() || negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posdau.hasTPC() || posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { @@ -1145,13 +1146,13 @@ struct HStrangeCorrelationFilter { auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!bachTrackCast.hasTPC() || bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posTrackCast.hasTPC() || posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negTrackCast.hasTPC() || negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { @@ -1318,13 +1319,13 @@ struct HStrangeCorrelationFilter { auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!bachTrackCast.hasTPC() || bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!posTrackCast.hasTPC() || posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (!negTrackCast.hasTPC() || negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; } if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 1e67da5fac5..9d1960bd563 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -974,7 +974,7 @@ struct HStrangeCorrelation { .eta = track.eta(), .phi = track.phi(), .sign = track.sign(), - .tpcCrossedRows = track.tpcNClsCrossedRows(), + .tpcCrossedRows = track.hasTPC() ? static_cast(track.tpcNClsCrossedRows()) : 0, // 0 without TPC, so classifyTriggerTracksFailure rejects it as the filter does .tpcSharedClusters = track.tpcNClsShared(), .itsClusters = track.itsNCls(), .hasLayer0 = static_cast(TESTBIT(track.itsClusterMap(), 0)), @@ -1258,7 +1258,8 @@ struct HStrangeCorrelation { template bool isValidTrigger(TTrack const& track, bool isLeading) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { + // hasTPC() is needed: since O2-7038 (Oct 2026) tracks without TPC report 256 crossed rows + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { return false; // crossed rows } if (!track.hasITS() && trackSelection.triggerRequireITS) { @@ -1298,7 +1299,7 @@ struct HStrangeCorrelation { template bool isValidAssocHadron(TTrack const& track) { - if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!track.hasTPC() || track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { return false; // crossed rows } if (!track.hasITS() && trackSelection.assocRequireITS) { @@ -1584,7 +1585,7 @@ struct HStrangeCorrelation { } //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -2152,7 +2153,7 @@ struct HStrangeCorrelation { } std::array assocForDeltaPhiStar = {phiProton, ptProton, signProton}; //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !bachtrack.hasTPC() || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -4043,7 +4044,7 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -4268,7 +4269,7 @@ struct HStrangeCorrelation { auto postrack = cascData.posTrack_as(); auto negtrack = cascData.negTrack_as(); auto bachtrack = cascData.bachelor_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !bachtrack.hasTPC() || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { @@ -5377,8 +5378,8 @@ struct HStrangeCorrelation { const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -5997,8 +5998,8 @@ struct HStrangeCorrelation { const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -6536,8 +6537,8 @@ struct HStrangeCorrelation { v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; } - passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && - negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + passesFinalSelection = passesFinalSelection && positiveTrack.hasTPC() && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.hasTPC() && negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; if (trackSelection.checksRequireTPCChi2) { passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; @@ -7204,7 +7205,7 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { + if (!postrack.hasTPC() || postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || !negtrack.hasTPC() || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; } if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { From 5481f3d67383ac965017a6e89647ccc3b0a7047d Mon Sep 17 00:00:00 2001 From: Alexandra <105586261+mulewicz@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:34:13 +0100 Subject: [PATCH 583/648] [PWGJE] add truth-level jet tables and reco-truth matching (#18593) Co-authored-by: Aleksandra Mulewicz --- .../TableProducer/quarkGluonJetsProducer.cxx | 222 +++++++++++++++++- 1 file changed, 220 insertions(+), 2 deletions(-) diff --git a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx index 89535fd176e..96305941c12 100644 --- a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx +++ b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include #include namespace o2::aod @@ -94,7 +97,7 @@ using QGMLJet = QGMLJets::iterator; namespace qgmlconst { -DECLARE_SOA_INDEX_COLUMN(QGMLJet, qGMLJet); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLJet, qGMLJet, "QGMLJET"); DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(P, p, float); DECLARE_SOA_COLUMN(Eta, eta, float); @@ -117,6 +120,7 @@ DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); +DECLARE_SOA_COLUMN(HasTruthMatch, hasTruthMatch, bool); DECLARE_SOA_COLUMN(TruthPdg, truthPdg, int32_t); DECLARE_SOA_COLUMN(TruthPt, truthPt, float); DECLARE_SOA_COLUMN(TruthEta, truthEta, float); @@ -148,11 +152,96 @@ DECLARE_SOA_TABLE(QGMLConstituents, "AOD", "QGMLCONSTS", qgmlconst::TofNSigmaKa, qgmlconst::TofNSigmaPr, qgmlconst::TofBeta, + qgmlconst::HasTruthMatch, qgmlconst::TruthPdg, qgmlconst::TruthPt, qgmlconst::TruthEta, qgmlconst::TruthPhi, qgmlconst::IsPhysicalPrimary); + +namespace qgmltruthjet +{ +DECLARE_SOA_COLUMN(SourceMcCollisionId, sourceMcCollisionId, int64_t); +DECLARE_SOA_COLUMN(SourceJetId, sourceJetId, int64_t); +DECLARE_SOA_COLUMN(FlavorLabel, flavorLabel, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPdg, leadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPt, leadingPartonPt, float); +DECLARE_SOA_COLUMN(LeadingPartonDeltaR, leadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(SubleadingPartonPdg, subleadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(SubleadingPartonPt, subleadingPartonPt, float); +DECLARE_SOA_COLUMN(SubleadingPartonDeltaR, subleadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(NPartonsInCone, nPartonsInCone, int32_t); +DECLARE_SOA_COLUMN(LabelAmbiguous, labelAmbiguous, bool); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(Radius, radius, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int32_t); +DECLARE_SOA_COLUMN(NPions, nPions, int32_t); +DECLARE_SOA_COLUMN(NKaons, nKaons, int32_t); +DECLARE_SOA_COLUMN(NProtons, nProtons, int32_t); +} // namespace qgmltruthjet +DECLARE_SOA_TABLE(QGMLTruthJets, "AOD", "QGMLTJET", + o2::soa::Index<>, + qgmltruthjet::SourceMcCollisionId, + qgmltruthjet::SourceJetId, + qgmltruthjet::FlavorLabel, + qgmltruthjet::LeadingPartonPdg, + qgmltruthjet::LeadingPartonPt, + qgmltruthjet::LeadingPartonDeltaR, + qgmltruthjet::SubleadingPartonPdg, + qgmltruthjet::SubleadingPartonPt, + qgmltruthjet::SubleadingPartonDeltaR, + qgmltruthjet::NPartonsInCone, + qgmltruthjet::LabelAmbiguous, + qgmltruthjet::Pt, + qgmltruthjet::Eta, + qgmltruthjet::Phi, + qgmltruthjet::Area, + qgmltruthjet::Radius, + qgmltruthjet::NConstituents, + qgmltruthjet::NPions, + qgmltruthjet::NKaons, + qgmltruthjet::NProtons); + +namespace qgmltruthconst +{ +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLTruthJet, qGMLTruthJet, "QGMLTJET"); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +DECLARE_SOA_COLUMN(PtFraction, ptFraction, float); +DECLARE_SOA_COLUMN(Pdg, pdg, int32_t); +DECLARE_SOA_COLUMN(Charge, charge, float); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); +} // namespace qgmltruthconst +DECLARE_SOA_TABLE(QGMLTruthConstituents, "AOD", "QGMLTCONST", + qgmltruthconst::QGMLTruthJetId, + qgmltruthconst::Pt, + qgmltruthconst::Eta, + qgmltruthconst::Phi, + qgmltruthconst::DeltaEta, + qgmltruthconst::DeltaPhi, + qgmltruthconst::DeltaR, + qgmltruthconst::PtFraction, + qgmltruthconst::Pdg, + qgmltruthconst::Charge, + qgmltruthconst::IsPhysicalPrimary); + +namespace qgmlmatch +{ +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLJet, qGMLJet, "QGMLJET"); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(QGMLTruthJet, qGMLTruthJet, "QGMLTJET"); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +} // namespace qgmlmatch +DECLARE_SOA_TABLE(QGMLJetMatches, "AOD", "QGMLMATCH", + qgmlmatch::QGMLJetId, + qgmlmatch::QGMLTruthJetId, + qgmlmatch::DeltaR); } // namespace o2::aod using namespace o2; @@ -162,6 +251,11 @@ using namespace o2::framework; using MCDJetEvents = aod::JetCollisionsMCD; using ChargedMCDJets = soa::Join; +using ChargedMCPJets = soa::Join; +using MatchedChargedMCDJets = soa::Join; using JetTrackRefs = soa::Join; using HadronTracksMC = soa::Join qgMLJets; Produces qgMLConstituents; + Produces qgMLTruthJets; + Produces qgMLTruthConstituents; + Produces qgMLJetMatches; static constexpr double JetRadiusTolerance = 1.e-3; @@ -220,6 +317,11 @@ struct QuarkGluonJetsProducer { Configurable dropAmbiguousLabels{"dropAmbiguousLabels", false, "drop ambiguous labels"}; Preslice mcParticlesPerCollision = aod::jmcparticle::mcCollisionId; + Preslice truthJetsPerCollision = aod::jet::mcCollisionId; + + Configurable minTruthJetPt{"minTruthJetPt", 10.0, "minimum truth jet pT"}; + Configurable maxTruthJetPt{"maxTruthJetPt", 1000.0, "maximum truth jet pT"}; + Configurable truthPhysicalPrimariesOnly{"truthPhysicalPrimariesOnly", true, "retain physical primaries only"}; enum JetFlavor { UnknownJet = 0, QuarkJet = 1, @@ -340,7 +442,8 @@ struct QuarkGluonJetsProducer { } void process(MCDJetEvents::iterator const& collision, - ChargedMCDJets const& detectorLevelJets, + MatchedChargedMCDJets const& detectorLevelJets, + ChargedMCPJets const& truthJets, JetTrackRefs const&, HadronTracksMC const&, aod::JetParticles const& jetParticles, @@ -358,6 +461,9 @@ struct QuarkGluonJetsProducer { return; } + const int64_t mcCollisionId = collision.mcCollisionId(); + std::vector> pendingMatches; + for (auto const& jet : detectorLevelJets) { const double jetRadius = static_cast(jet.r()) / 100.0; if (std::abs(jetRadius - static_cast(rJet)) > JetRadiusTolerance) { @@ -427,6 +533,32 @@ struct QuarkGluonJetsProducer { const auto qgMLJetId = qgMLJets.lastIndex(); + auto geoMatches = jet.matchedJetGeo_as(); + auto ptMatches = jet.matchedJetPt_as(); + float bestDR = std::numeric_limits::infinity(); + int64_t bestTruthSourceId = -1; + for (auto const& candidate : geoMatches) { + bool sharedMatch = false; + for (auto const& ptCandidate : ptMatches) { + if (candidate.globalIndex() == ptCandidate.globalIndex()) { + sharedMatch = true; + break; + } + } + if (!sharedMatch || candidate.mcCollisionId() != mcCollisionId) { + continue; + } + const auto dPhi = RecoDecay::constrainAngle(candidate.phi() - jet.phi(), -PI); + const float dR = std::hypot(candidate.eta() - jet.eta(), dPhi); + if (dR < bestDR) { + bestDR = dR; + bestTruthSourceId = candidate.globalIndex(); + } + } + if (bestTruthSourceId >= 0) { + pendingMatches.emplace_back(qgMLJetId, bestTruthSourceId, bestDR); + } + for (auto const& jtrack : jet.tracks_as()) { auto track = jtrack.track_as(); @@ -477,6 +609,7 @@ struct QuarkGluonJetsProducer { track.hasTOF() ? static_cast(track.tofNSigmaKa()) : missingTOF, track.hasTOF() ? static_cast(track.tofNSigmaPr()) : missingTOF, track.hasTOF() ? static_cast(track.beta()) : missingTOF, + track.has_mcParticle(), truthPdg, truthPt, truthEta, @@ -484,6 +617,91 @@ struct QuarkGluonJetsProducer { isPhysicalPrimary); } } + + auto truthJetsInCollision = truthJets.sliceBy(truthJetsPerCollision, mcCollisionId); + std::unordered_map truthOutputIndex; + for (auto const& truthJet : truthJetsInCollision) { + const double radius = static_cast(truthJet.r()) / 100.0; + if (std::abs(radius - static_cast(rJet)) > JetRadiusTolerance) { + continue; + } + if (isppRefAnalysis && std::abs(truthJet.eta()) > cfgEtaJetMax) { + continue; + } + if (!isppRefAnalysis && std::abs(truthJet.eta()) + radius > maxEta - deltaEtaEdge) { + continue; + } + if (truthJet.pt() < minTruthJetPt || truthJet.pt() > maxTruthJetPt) { + continue; + } + if (applyAreaCut && truthJet.area() / (PI * radius * radius) < minNormalizedJetArea) { + continue; + } + + struct SelectedTruthParticle { + float pt, eta, phi, charge; + int32_t pdg; + bool primary; + }; + std::vector selected; + int32_t nPions = 0, nKaons = 0, nProtons = 0; + for (auto const& particle : truthJet.tracks_as()) { + if (truthPhysicalPrimariesOnly && !particle.isPhysicalPrimary()) { + continue; + } + if (particle.pt() < minPt || particle.pt() > maxPt || + particle.eta() < minEta || particle.eta() > maxEta) { + continue; + } + auto* pdgInfo = TDatabasePDG::Instance()->GetParticle(particle.pdgCode()); + if (!pdgInfo) { + continue; + } + const float charge = static_cast(pdgInfo->Charge() / 3.0); + if (std::abs(charge) < 0.5f) { + continue; + } + const int apdg = std::abs(particle.pdgCode()); + nPions += (apdg == 211); + nKaons += (apdg == 321); + nProtons += (apdg == 2212); + selected.push_back({particle.pt(), particle.eta(), particle.phi(), charge, + particle.pdgCode(), particle.isPhysicalPrimary()}); + } + if (selected.empty()) { + continue; + } + auto collisionParticles = jetParticles.sliceBy(mcParticlesPerCollision, mcCollisionId); + auto flavor = getJetFlavorTag(truthJet, collisionParticles); + if (dropUnknownLabels && flavor.flavor == UnknownJet) { + continue; + } + if (dropAmbiguousLabels && flavor.ambiguous) { + continue; + } + qgMLTruthJets(mcCollisionId, truthJet.globalIndex(), + flavor.flavor, flavor.leadingPartonPdg, flavor.leadingPartonPt, + flavor.leadingPartonDeltaR, flavor.subleadingPartonPdg, + flavor.subleadingPartonPt, flavor.subleadingPartonDeltaR, + flavor.nPartonsInCone, flavor.ambiguous, + truthJet.pt(), truthJet.eta(), truthJet.phi(), truthJet.area(), + radius, static_cast(selected.size()), nPions, nKaons, nProtons); + const auto truthOutputId = qgMLTruthJets.lastIndex(); + truthOutputIndex.emplace(truthJet.globalIndex(), truthOutputId); + for (auto const& part : selected) { + const float dEta = part.eta - truthJet.eta(); + const float dPhi = RecoDecay::constrainAngle(part.phi - truthJet.phi(), -PI); + qgMLTruthConstituents(truthOutputId, part.pt, part.eta, part.phi, + dEta, dPhi, std::hypot(dEta, dPhi), + truthJet.pt() > 0 ? part.pt / truthJet.pt() : 0.f, + part.pdg, part.charge, part.primary); + } + } + for (auto const& [recoOutputId, sourceTruthId, dR] : pendingMatches) { + if (auto it = truthOutputIndex.find(sourceTruthId); it != truthOutputIndex.end()) { + qgMLJetMatches(recoOutputId, it->second, dR); + } + } } }; From 57d5013f11bc27fd484007ad7a3ab27fb7470147 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:37:46 +0100 Subject: [PATCH 584/648] [PWGHF] Implement MC closure and efficiency reweight for 2PC (#18610) Co-authored-by: Marcello Di Costanzo Co-authored-by: ALICE Action Bot --- .../correlatorFlowCharmHadronsReduced.cxx | 660 ++++++++++-------- .../derivedDataCreatorCorrelationsReduced.cxx | 587 +++++++++------- PWGHF/HFC/Tasks/CMakeLists.txt | 5 + .../Tasks/taskCorrelationsEfficiencies.cxx | 438 ++++++++++++ PWGHF/HFC/Utils/utilsFlow.h | 227 ++++++ 5 files changed, 1389 insertions(+), 528 deletions(-) create mode 100644 PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx create mode 100644 PWGHF/HFC/Utils/utilsFlow.h diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index 50e99a602e1..9740b182ae1 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -17,6 +17,7 @@ #include "Common/Core/RecoDecay.h" +#include #include #include #include @@ -33,9 +34,17 @@ #include #include +#include +#include +#include +#include +#include + +#include #include #include #include +#include #include #include @@ -82,20 +91,18 @@ double getPhi(const TTrack& track) } } +enum HistoEffType { + kTH1 = 0, + kTH2, + kTH3, + kTHnSparse +}; + struct HfCorrelatorFlowCharmHadronsReduced { - // Produces rowPairSECharmHads; //! Correlation pairs information Same Event - // Produces rowPairMECharmHads; //! Correlation pairs information Mixed Event - // Produces rowPairSEHadHads; //! Correlation pairs information Same Event - // Produces rowPairMEHadHads; //! Correlation pairs information Mixed Event - // Produces rowCollInfos; //! Collision info - - Configurable fillSparses{"fillSparses", true, "Fill sparse histograms"}; - Configurable fillTables{"fillTables", false, "Fill tables"}; + Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; - Configurable> binsPtTrig{"binsPtTrig", std::vector{0., 3., 5., 8., 16., 36.}, "pT bin limits for trigger candidates"}; Configurable> bdtScore0PtMaxs{"bdtScore0PtMaxs", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential maximum score 0 for charm candidates"}; Configurable> bdtScore1PtMins{"bdtScore1PtMins", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential minimum score 1 for charm candidates"}; - Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.3, 1., 2., 50.}, "pT bin limits for associated particles"}; Configurable selCentrality{"selCentrality", false, "Use centrality to select events"}; Configurable selMultiplicity{"selMultiplicity", false, "Use multiplicity to select events"}; Configurable centMin{"centMin", 0, "min. centrality"}; @@ -112,10 +119,26 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; Configurable centMultMaxForDownSample{"centMultMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; + // Efficiency correction + Configurable doEfficiencyCorrection{"doEfficiencyCorrection", false, "Flag for applying efficiency corrections"}; + Configurable effHistTypeCharm{"effHistTypeCharm", 0, "Histogram type for charm efficiency (0=TH1, 1=TH2, 2=TH3, 3=THnSparse)"}; + Configurable effHistTypeAssoc{"effHistTypeAssoc", 0, "Histogram type for associated efficiency (0=TH1, 1=TH2, 2=TH3, 3=THnSparse)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable assocEffCcdbPath{"assocEffCcdbPath", "", "CCDB path for associated efficiency"}; + Configurable promptEffCcdbPath{"promptEffCcdbPath", "", "CCDB path for trigger efficiency"}; + Configurable timestampCcdb{"timestampCcdb", -1, "timestamp of the efficiency files used to query in CCDB"}; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + SliceCache cache; + Service ccdb{}; + std::shared_ptr hEffPrompt = nullptr; + std::shared_ptr hEffAssoc = nullptr; + int poolBins{0}; + std::vector mPtEdgesTrig; + using SameEvtPairsChHadWEffInfo = soa::Filtered>; using SameEvtPairsChHad = soa::Filtered>; using SameEvtPairsHadHad = soa::Filtered>; using AssocTracks = soa::Filtered>; @@ -128,9 +151,11 @@ struct HfCorrelatorFlowCharmHadronsReduced { Preslice assocTracksPerCol = aod::hf_correl_charm_had_reduced::hfcRedCorrCollId; Preslice trigCharmCandsPerCol = aod::hf_correl_charm_had_reduced::hfcRedCorrCollId; - ConfigurableAxis zPoolBins{"zPoolBins", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "Z vertex position pools"}; - ConfigurableAxis multPoolBins{"multPoolBins", {VARIABLE_WIDTH, 0., 900., 1800., 6000.}, "Event multiplicity pools (FT0M)"}; - ConfigurableAxis centPoolBins{"centPoolBins", {VARIABLE_WIDTH, 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}, "Event centrality pools"}; + ConfigurableAxis binsPtTrig{"binsPtTrig", {VARIABLE_WIDTH, 0., 1., 2., 3., 4., 5., 6., 8., 10., 12., 16., 24.}, "Trigger candidate pT bins"}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {VARIABLE_WIDTH, 0.3, 1., 2., 50.}, "Associated particle pT bins"}; + ConfigurableAxis binsZPool{"binsZPool", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "Z vertex position pools"}; + ConfigurableAxis binsMultPool{"binsMultPool", {VARIABLE_WIDTH, 0., 900., 1800., 6000.}, "Event multiplicity pools (FT0M)"}; + ConfigurableAxis binsCentPool{"binsCentPool", {VARIABLE_WIDTH, 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}, "Event centrality pools"}; ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, "Invariant mass bins"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; @@ -142,6 +167,34 @@ struct HfCorrelatorFlowCharmHadronsReduced { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::shared_ptr loadEfficiencyHistogram(const std::string& path, int histoType) + { + TObject* object = nullptr; + + switch (histoType) { + case kTH1: + object = ccdb->getForTimeStamp(Form("%s/TH1/", path.c_str()), timestampCcdb); + break; + case kTH2: + object = ccdb->getForTimeStamp(Form("%s/TH2/", path.c_str()), timestampCcdb); + break; + case kTH3: + object = ccdb->getForTimeStamp(Form("%s/TH3/", path.c_str()), timestampCcdb); + break; + case kTHnSparse: + object = ccdb->getForTimeStamp(Form("%s/THnSparse/", path.c_str()), timestampCcdb); + break; + default: + LOGF(fatal, "Invalid efficiency histogram type: %d", histoType); + } + + if (!object) { + LOGF(fatal, "Could not load efficiency histogram from %s", path.c_str()); + } + + return std::shared_ptr(object); + } + void init(InitContext&) { if (selCentrality && selMultiplicity) { @@ -151,31 +204,26 @@ struct HfCorrelatorFlowCharmHadronsReduced { LOGP(fatal, "You must select events based on either centrality or multiplicity! Please check your configuration!"); } - // Fatal if selCentrality and process functions using multiplicity mixing, or selMultiplicity and process functions using centrality mixing - if (selCentrality && (doprocessSameEventCharmHadWMultMix || doprocessMixedEventCharmHadWMultMix || doprocessSameEventHadHadWMultMix || doprocessMixedEventHadHadWMultMix)) { - LOGP(fatal, "You cannot select events based on centrality and process functions using multiplicity mixing! Please check your configuration!"); - } - if (selMultiplicity && (doprocessSameEventCharmHadWCentMix || doprocessMixedEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix || doprocessMixedEventHadHadWCentMix)) { - LOGP(fatal, "You cannot select events based on multiplicity and process functions using centrality mixing! Please check your configuration!"); - } - - if ((doprocessSameEventCharmHadWCentMix && doprocessMixedEventCharmHadWMultMix) || - (doprocessSameEventCharmHadWMultMix && doprocessMixedEventCharmHadWCentMix) || - (doprocessSameEventHadHadWCentMix && doprocessMixedEventHadHadWMultMix) || - (doprocessSameEventHadHadWMultMix && doprocessMixedEventHadHadWCentMix)) { + if ((doprocessSECharmHadWCentMix && doprocessMECharmHadWMultMix) || + (doprocessSECharmHadWMultMix && doprocessMECharmHadWCentMix) || + (doprocessSEHadHadWCentMix && doprocessMEHadHadWMultMix) || + (doprocessSEHadHadWMultMix && doprocessMEHadHadWCentMix)) { LOGP(fatal, "You cannot mix centrality and multiplicity mixing in the same processing! Please check your configuration!"); } - if (!fillSparses && !fillTables) { - LOGP(fatal, "At least one of fillSparses or fillTables must be true!"); - } - if ((binsPtTrig.value.size() != (bdtScore0PtMaxs.value.size() + 1) || binsPtTrig.value.size() != (bdtScore1PtMins.value.size() + 1))) { + + mPtEdgesTrig.assign(binsPtTrig.value.begin() + 1, binsPtTrig.value.end()); + if ((mPtEdgesTrig.size() != (bdtScore0PtMaxs.value.size() + 1) || + mPtEdgesTrig.size() != (bdtScore1PtMins.value.size() + 1))) { + LOG(info) << "mPtEdgesTrig.size(): " << mPtEdgesTrig.size(); + LOG(info) << "bdtScore0PtMaxs.value.size(): " << bdtScore0PtMaxs.value.size(); + LOG(info) << "bdtScore1PtMins.value.size(): " << bdtScore1PtMins.value.size(); LOGP(fatal, "The size of bdtScore0PtMaxs and bdtScore1PtMins must be the one of binsPtTrig minus one!"); } - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix || doprocessMixedEventCharmHadWCentMix || doprocessMixedEventHadHadWCentMix || doprocessSameEventCharmHadWCentMixBase || doprocessMixedEventCharmHadWCentMixBase) { - poolBins = (centPoolBins->size() - 2) * (zPoolBins->size() - 2); + if (doprocessSECharmHadWCentMix || doprocessSEHadHadWCentMix || doprocessMECharmHadWCentMix || doprocessMEHadHadWCentMix) { + poolBins = (binsCentPool->size() - 2) * (binsZPool->size() - 2); } else { - poolBins = (multPoolBins->size() - 2) * (zPoolBins->size() - 2); + poolBins = (binsMultPool->size() - 2) * (binsZPool->size() - 2); } const AxisSpec axisInvMass{binsInvMass, "Inv. mass (GeV/#it{c}^{2})"}; @@ -185,19 +233,19 @@ struct HfCorrelatorFlowCharmHadronsReduced { const AxisSpec axisPoolBin = {poolBins, 0., static_cast(poolBins), "PoolBin"}; const AxisSpec axisDeltaEta = {binsDeltaEta, "#Delta#it{#eta}"}; const AxisSpec axisDeltaPhi = {binsDeltaPhi, "#Delta#it{#varphi}"}; - const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisMlOne{binsMlOne, "bdtScore0"}; const AxisSpec axisMlTwo{binsMlTwo, "bdtScore1"}; // Histograms for data analysis - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix) { + if (doprocessSECharmHadWCentMix || doprocessSECharmHadWCentMixWEffInfo || doprocessSEHadHadWCentMix) { registry.add("hCentPoolBinSE", "Centrality SE", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); - } else if (doprocessSameEventCharmHadWMultMix || doprocessSameEventHadHadWMultMix) { + } else if (doprocessSECharmHadWMultMix || doprocessSECharmHadWMultMixWEffInfo || doprocessSEHadHadWMultMix) { registry.add("hMultFT0MPoolBinSE", "Multiplicity FT0M SE", {HistType::kTH2F, {{axisMultFT0M}, {axisPoolBin}}}); - } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventHadHadWCentMix) { + } else if (doprocessMECharmHadWCentMix || doprocessMECharmHadWCentMixWEffInfo || doprocessMEHadHadWCentMix) { registry.add("hCentPoolBinME", "Centrality ME", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); - } else if (doprocessMixedEventCharmHadWMultMix || doprocessMixedEventHadHadWMultMix) { + } else if (doprocessMECharmHadWMultMix || doprocessMECharmHadWMultMixWEffInfo || doprocessMEHadHadWMultMix) { registry.add("hMultFT0MPoolBinME", "Multiplicity FT0M ME", {HistType::kTH2F, {{axisMultFT0M}, {axisPoolBin}}}); } registry.add("hZVtxPoolBinSE", "z vertex SE", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); @@ -208,28 +256,116 @@ struct HfCorrelatorFlowCharmHadronsReduced { registry.add("hPoolBinTrigME", "Trigger candidates pool bin ME", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocSE", "Associated particles pool bin SE", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocME", "Associated particles pool bin ME", {HistType::kTH1F, {axisPoolBin}}); - if (fillSparses) { - std::vector axesTrigger = {axisInvMass, axisPtTrig, axisMlOne, axisMlTwo}; - std::vector axes = {axisPoolBin, axisPtTrig, axisPtAssoc, axisDeltaEta, axisDeltaPhi}; - if (doprocessSameEventHadHadWCentMix || doprocessSameEventHadHadWMultMix) { - registry.add("hSparseCorrelationsSEHadHad", "THn for SE Had-Had correlations", HistType::kTHnSparseF, axes); - } else if (doprocessMixedEventHadHadWCentMix || doprocessMixedEventHadHadWMultMix) { - registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); - } else { - // axes.insert(axes.end(), {axisInvMass}); - axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); - if (doprocessSameEventCharmHadWCentMix || doprocessSameEventCharmHadWMultMix || doprocessSameEventCharmHadWCentMixBase) { - registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); - } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventCharmHadWMultMix || doprocessMixedEventCharmHadWCentMixBase) { - registry.add("hSparseCorrelationsMECharmHad", "THn for ME Charm-Had correlations", HistType::kTHnSparseF, axes); + + std::vector axesTrigger = {axisInvMass, axisPtTrig, axisMlOne, axisMlTwo}; + std::vector axes = {axisPoolBin, axisPtTrig, axisPtAssoc, axisDeltaEta, axisDeltaPhi}; + if (doprocessSEHadHadWCentMix || doprocessSEHadHadWMultMix) { + registry.add("hSparseCorrelationsSEHadHad", "THn for SE Had-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsSEHadHad"))->Sumw2(); + } + } else if (doprocessMEHadHadWCentMix || doprocessMEHadHadWMultMix) { + registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsMEHadHad"))->Sumw2(); + } + } else { + axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); + if (doprocessSECharmHadWCentMix || doprocessSECharmHadWCentMixWEffInfo || doprocessSECharmHadWMultMix || doprocessSECharmHadWMultMixWEffInfo) { + registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsSECharmHad"))->Sumw2(); + } + } else if (doprocessMECharmHadWCentMix || doprocessMECharmHadWMultMixWEffInfo || doprocessMECharmHadWMultMix || doprocessMECharmHadWCentMixWEffInfo) { + registry.add("hSparseCorrelationsMECharmHad", "THn for ME Charm-Had correlations", HistType::kTHnSparseF, axes); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseCorrelationsMECharmHad"))->Sumw2(); + } + } + if (doprocessCharmTriggers || doprocessCharmTriggersWEffInfo) { + registry.add("hSparseTrigCandsCharm", "THn for Charm trigger candidates", HistType::kTHnSparseF, axesTrigger); + if (doEfficiencyCorrection) { + registry.get(HIST("hSparseTrigCandsCharm"))->Sumw2(); + } + } + } + + if (doEfficiencyCorrection) { + hEffPrompt = loadEfficiencyHistogram(promptEffCcdbPath, effHistTypeCharm); + hEffAssoc = loadEfficiencyHistogram(assocEffCcdbPath, effHistTypeAssoc); + } + } + + template + double getEfficiency(TObject* hist, int histoType, const TCand& cand) //, const std::vector& values) + { + if (!hist) { + LOG(debug) << "Candidate does not have required efficiency histogram."; + return 0.; + } + + std::vector kineFeatures{}; + + // Charm triggers + if constexpr (IsCharm) { + kineFeatures = {cand.ptTrig(), cand.bdtScore0Trig(), cand.bdtScore1Trig(), cand.etaTrig(), cand.phiTrig()}; + } else { + auto collision = cand.template hfcRedCorrColl_as(); + kineFeatures = {cand.ptAssoc(), cand.etaAssoc(), cand.phiAssoc(), collision.posZ()}; + } + + switch (histoType) { + + case kTH1: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 1) { + return 0.; + } + return h->GetBinContent(h->FindBin(kineFeatures[0])); + } + + case kTH2: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 2) { + return 0.; + } + return h->GetBinContent(h->FindBin(kineFeatures[0], kineFeatures[1])); + } + + case kTH3: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < 3) { + return 0.; } - if (doprocessCharmTriggers) { - registry.add("hSparseTrigCandsCharm", "THn for Charm trigger candidates", HistType::kTHnSparseF, axesTrigger); + return h->GetBinContent( + h->FindBin(kineFeatures[0], kineFeatures[1], kineFeatures[2])); + } + + case kTHnSparse: { + auto* h = dynamic_cast(hist); + if (!h || kineFeatures.size() < static_cast(h->GetNdimensions())) { + return 0.; } + + std::vector bins(h->GetNdimensions()); + + for (int i = 0; i < h->GetNdimensions(); ++i) { + bins[i] = h->GetAxis(i)->FindBin(kineFeatures[i]); + } + + return h->GetBinContent(bins.data()); } + + default: + return 0.; } } + bool isValidEfficiency(double efficiency) + { + return std::isfinite(efficiency) && efficiency > 0.; + } + template bool isSelCollision(const TColl& collision) { @@ -277,8 +413,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { bool isSelBdtScoreCut(TCand const& cand, double ptTrig) { - for (size_t iPt = 0; iPt < binsPtTrig.value.size() - 1; iPt++) { - if (ptTrig >= binsPtTrig.value[iPt] && ptTrig < binsPtTrig.value[iPt + 1]) { + for (size_t iPt = 0; iPt < mPtEdgesTrig.size() - 1; iPt++) { + if (ptTrig >= mPtEdgesTrig[iPt] && ptTrig < mPtEdgesTrig[iPt + 1]) { return (cand.bdtScore0Trig() < bdtScore0PtMaxs.value[iPt]) && (cand.bdtScore1Trig() > bdtScore1PtMins.value[iPt]); } } @@ -290,7 +426,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { /// \param trigCands are the selected trigger candidates /// \param assocTracks are the selected associated tracks /// \param binPolicy is the binning policy for the correlation - template + template void fillSameEvent(TPair const& pair, TTrigCand const& trigCand, const TBinningType& binPolicy) @@ -318,33 +454,61 @@ struct HfCorrelatorFlowCharmHadronsReduced { int const poolBin = getPoolBin(collision, binPolicy); registry.fill(HIST("hPoolBinTrigSE"), poolBin); registry.fill(HIST("hPoolBinAssocSE"), poolBin); - // if constexpr (FillTables) { - // if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - // rowPairSECharmHads(poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), - // trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig(), - // pair.nTpcCrossedRowsAssoc(), pair.itsClsMapAssoc(), pair.itsNClsAssoc(), pair.dcaXYAssoc(), pair.dcaZAssoc()); - // } else { - // rowPairSEHadHads(poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), - // trigCand.nTpcCrossedRowsTrig(), trigCand.itsClsMapTrig(), trigCand.itsNClsTrig(), trigCand.dcaXYTrig(), trigCand.dcaZTrig(), - // pair.nTpcCrossedRowsAssoc(), pair.itsClsMapAssoc(), pair.itsNClsAssoc(), pair.dcaXYAssoc(), pair.dcaZAssoc()); - // } - // rowCollInfos(collision.multiplicity(), collision.numPvContrib(), collision.centrality()); - // } - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), - pair.deltaPhi(), trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); - } else { - registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()); + + double effAssoc{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effAssoc = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, pair); + if (!isValidEfficiency(effAssoc)) { + LOGF(warn, "Invalid efficiency value for associated track: effAssoc = %f", effAssoc); + return; + } } } + + if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases + + double effTrig{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + return; + } + } + } + + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; + + std::vector values = {static_cast(poolBin), ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi(), trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + + registry.get(HIST("hSparseCorrelationsSECharmHad"))->Fill(values.data(), weight); + } else { + + double effTrig{1.f}; + if constexpr (CorrectEff) { + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + return; + } + } + } + + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; + + std::vector valuesHadHad = {static_cast(poolBin), ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()}; + registry.get(HIST("hSparseCorrelationsSEHadHad"))->Fill(valuesHadHad.data(), weight); + } } /// Save info for Mixed Event pairs /// \param collisions are the selected collisions /// \param pairs are the mixed event pairs of trigger candidates and associated tracks /// \param binPolicy is the binning policy for the correlation - template + template void fillMixedEvent(TPairs const& pairs, const TBinningType& binPolicy) { @@ -392,248 +556,174 @@ struct HfCorrelatorFlowCharmHadronsReduced { } } double const deltaPhi = RecoDecay::constrainAngle(getPhi(assocTrack) - getPhi(trigCand), -o2::constants::math::PIHalf); - // if constexpr (FillTables) { - // if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - // rowPairMECharmHads(poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi, - // trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig(), - // assocTrack.nTpcCrossedRowsAssoc(), assocTrack.itsClsMapAssoc(), assocTrack.itsNClsAssoc(), assocTrack.dcaXYAssoc(), assocTrack.dcaZAssoc()); - // } else { - // rowPairMEHadHads(poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi, - // trigCand.nTpcCrossedRowsAssoc(), trigCand.itsClsMapAssoc(), trigCand.itsNClsAssoc(), trigCand.dcaXYAssoc(), trigCand.dcaZAssoc(), - // assocTrack.nTpcCrossedRowsAssoc(), assocTrack.itsClsMapAssoc(), assocTrack.itsNClsAssoc(), assocTrack.dcaXYAssoc(), assocTrack.dcaZAssoc()); - // } - // rowCollInfos(trigColl.multiplicity(), trigColl.numPvContrib(), trigColl.centrality()); - // } - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, - deltaPhi, trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); - } else { - registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi); - } - } - } - } - } - /// Correlations for Same Event pairs - /// \param poolBin collision pool bin based on multiplicity and z-vertex position - /// \param trigCandsThisColl are the selected trigger candidates in the collision - /// \param assocTracksThisColl are the selected associated tracks in the collision - template - void doCorrelationsSameEvent(int poolBin, - const TTrigCand& trigCandsThisColl, - const TTrackAssoc& assocTracksThisColl) - { - for (const auto& trigCand : trigCandsThisColl) { - double const ptTrig = trigCand.ptTrig(); - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // ML selection on bkg score for Charm-Had case - if (!isSelBdtScoreCut(trigCand, ptTrig)) { - continue; - } - } - - for (const auto& assTrk : assocTracksThisColl) { - // TODO: Remove Ds daughters - /*if (assTrk.originTrackId() == candidate.prong0Id() || - assTrk.originTrackId() == candidate.prong1Id() || - assTrk.originTrackId() == candidate.prong2Id()) { - continue; - }*/ - // TODO: DCA cut - double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); - double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, assTrk.ptAssoc(), deltaEta, - deltaPhi, trigCand.invMassTrig()); - } else { - registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, assTrk.ptAssoc(), deltaEta, deltaPhi); + double effAssoc{1.f}; + if (doEfficiencyCorrection) { + effAssoc = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, assocTrack); + if (!isValidEfficiency(effAssoc)) { + LOGF(warn, "Invalid efficiency value for associated track: effAssoc = %f", effAssoc); + continue; } } - } - } - } - /// Correlations for Mixed Event pairs - /// \param collisions are the selected collisions - /// \param trigCands are the trigger candidates - /// \param assocTracks are the associated tracks - /// \param binPolicy is the binning policy for the correlation - template - void doCorrelationsMixedEvent(const TCollision& collisions, - const TTrigCand& trigCands, - const TTrackAssoc& assocTracks, - const TBinningType& binPolicy) - { - auto tracksTuple = std::make_tuple(trigCands, assocTracks); + if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - Pair pairData{binPolicy, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + double effTrig{1.f}; + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } + } - for (const auto& [c1, tracks1, c2, tracks2] : pairData) { - if (!isSelCollision(c1) || !isSelCollision(c2)) { - continue; - } - if (tracks1.size() == 0) { - continue; - } + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; - int poolBin = binPolicy.getBin({c2.posZ(), selCentrality ? c2.centrality() : c2.multiplicity()}); - int poolBinTrigCand = binPolicy.getBin({c1.posZ(), selCentrality ? c1.centrality() : c1.multiplicity()}); + std::vector values = {static_cast(poolBinTrig), ptTrig, ptAssoc, deltaEta, deltaPhi, trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + registry.get(HIST("hSparseCorrelationsMECharmHad"))->Fill(values.data(), weight); + } else { - if (poolBin != poolBinTrigCand) { - LOGF(info, "Error, poolBins are different"); - continue; - } + double effTrig{1.f}; + if (doEfficiencyCorrection) { + effTrig = getEfficiency(hEffAssoc.get(), effHistTypeAssoc.value, trigCand); + if (!isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } + } - for (const auto& [trigCand, assTrk] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { - continue; - } + double weight = doEfficiencyCorrection ? 1 / (effTrig * effAssoc) : 1.f; - double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); - double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); - if constexpr (FillSparses) { - if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases - registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBin, trigCand.ptTrig(), assTrk.ptAssoc(), deltaEta, - deltaPhi, trigCand.invMassTrig()); - } else { - registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBin, trigCand.ptTrig(), assTrk.ptAssoc(), deltaEta, deltaPhi); - } + std::vector valuesHadHad = {static_cast(poolBinTrig), ptTrig, ptAssoc, deltaEta, deltaPhi}; + registry.get(HIST("hSparseCorrelationsMEHadHad"))->Fill(valuesHadHad.data(), weight); } } } } - void processSameEventCharmHadWMultMix(SameEvtPairsChHad::iterator const& pair, - aod::HfcRedTrigCharms const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWMultMix(SameEvtPairsChHad::iterator const& pair, + aod::HfcRedTrigCharms const&, + aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto trigCand = pair.template hfcRedTrigCharm_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } + fillSameEvent(pair, trigCand, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", false); - void processSameEventHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, - aod::HfcRedTrigTracks const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWMultMixWEffInfo(SameEvtPairsChHadWEffInfo::iterator const& pair, + TrigCharmCands const&, + aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; + auto trigCand = pair.template hfcRedTrigCharm_as(); + fillSameEvent(pair, trigCand, binPolicyPosZMult); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWMultMixWEffInfo, "Process Same Event for Charm-Had with multiplicity pools", false); + + void processSEHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, + aod::HfcRedTrigTracks const&, + aod::HfcRedCorrColls const&) + { + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto trigCand = pair.template hfcRedTrigTrack_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZMult); - } + fillSameEvent(pair, trigCand, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventHadHadWMultMix, "Process Same Event for Had-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSEHadHadWMultMix, "Process Same Event for Had-Had with multiplicity pools", false); - void processSameEventCharmHadWCentMix(SameEvtPairsChHad::iterator const& pair, - aod::HfcRedTrigCharms const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWCentMix(SameEvtPairsChHad::iterator const& pair, + aod::HfcRedTrigCharms const&, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto trigCand = pair.template hfcRedTrigCharm_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } + fillSameEvent(pair, trigCand, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWCentMix, "Process Same Event for Charm-Had with centrality pools", true); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWCentMix, "Process Same Event for Charm-Had with centrality pools", true); - void processSameEventHadHadWCentMix(SameEvtPairsHadHad::iterator const& pair, - aod::HfcRedTrigTracks const&, - aod::HfcRedCorrColls const&) + void processSECharmHadWCentMixWEffInfo(SameEvtPairsChHadWEffInfo::iterator const& pair, + TrigCharmCands const&, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; + auto trigCand = pair.template hfcRedTrigCharm_as(); + fillSameEvent(pair, trigCand, binPolicyPosZCent); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSECharmHadWCentMixWEffInfo, "Process Same Event for Charm-Had with centrality pools", false); + + void processSEHadHadWCentMix(SameEvtPairsHadHad::iterator const& pair, + aod::HfcRedTrigTracks const&, + aod::HfcRedCorrColls const&) + { + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto trigCand = pair.template hfcRedTrigTrack_as(); - if (fillSparses && fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillSparses) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } else if (fillTables) { - fillSameEvent(pair, trigCand, binPolicyPosZCent); - } + fillSameEvent(pair, trigCand, binPolicyPosZCent); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSEHadHadWCentMix, "Process Same Event for Had-Had with centrality pools", false); + + void processMECharmHadWCentMix(aod::HfcRedCorrColls const& collisions, + TrigCharmCands const& candidates, + AssocTracks const& tracks) + { + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; + auto pairsTuple = std::make_tuple(candidates, tracks); + Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + fillMixedEvent(pairs, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventHadHadWCentMix, "Process Same Event for Had-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWCentMix, "Process Mixed Event for Charm-Had with centrality pools", false); - void processMixedEventCharmHadWCentMix(aod::HfcRedCorrColls const& collisions, + void processMECharmHadWCentMixWEffInfo(aod::HfcRedCorrColls const& collisions, TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } + fillMixedEvent(pairs, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWCentMix, "Process Mixed Event for Charm-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWCentMixWEffInfo, "Process Mixed Event for Charm-Had with centrality pools and efficiency information", false); - void processMixedEventCharmHadWMultMix(aod::HfcRedCorrColls const& collisions, + void processMECharmHadWMultMix(aod::HfcRedCorrColls const& collisions, + TrigCharmCands const& candidates, + AssocTracks const& tracks) + { + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; + auto pairsTuple = std::make_tuple(candidates, tracks); + Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + fillMixedEvent(pairs, binPolicyPosZMult); + } + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWMultMix, "Process Mixed Event for Charm-Had with multiplicity pools", false); + + void processMECharmHadWMultMixWEffInfo(aod::HfcRedCorrColls const& collisions, TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } + fillMixedEvent(pairs, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWMultMix, "Process Mixed Event for Charm-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMECharmHadWMultMixWEffInfo, "Process Mixed Event for Charm-Had with multiplicity pools and efficiency information", false); - void processMixedEventHadHadWCentMix(aod::HfcRedCorrColls const& collisions, - AssocTracks const& tracks) + void processMEHadHadWCentMix(aod::HfcRedCorrColls const& collisions, + AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; + BinningCentPosZ binPolicyPosZCent{{binsZPool, binsCentPool}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, tracksTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZCent); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZCent); - } + fillMixedEvent(pairs, binPolicyPosZCent); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventHadHadWCentMix, "Process Mixed Event for Had-Had with centrality pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMEHadHadWCentMix, "Process Mixed Event for Had-Had with centrality pools", false); - void processMixedEventHadHadWMultMix(aod::HfcRedCorrColls const& collisions, - AssocTracks const& tracks) + void processMEHadHadWMultMix(aod::HfcRedCorrColls const& collisions, + AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; + BinningMultPosZ binPolicyPosZMult{{binsZPool, binsMultPool}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, tracksTuple, &cache}; - if (fillSparses && fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillSparses) { - fillMixedEvent(pairs, binPolicyPosZMult); - } else if (fillTables) { - fillMixedEvent(pairs, binPolicyPosZMult); - } + fillMixedEvent(pairs, binPolicyPosZMult); } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventHadHadWMultMix, "Process Mixed Event for Had-Had with multiplicity pools", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMEHadHadWMultMix, "Process Mixed Event for Had-Had with multiplicity pools", false); void processCharmTriggers(aod::HfcRedTrigCharms const& trigCands, aod::HfcRedCorrColls const&) @@ -647,41 +737,37 @@ struct HfCorrelatorFlowCharmHadronsReduced { if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { continue; } - registry.fill(HIST("hSparseTrigCandsCharm"), trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + + std::vector values = {trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + registry.get(HIST("hSparseTrigCandsCharm"))->Fill(values.data()); } } PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processCharmTriggers, "Process charm trigger info", false); - void processSameEventCharmHadWCentMixBase(aod::HfcRedCorrColls const& collisions, - TrigCharmCands const& candidates, - aod::HfcRedAssBases const& tracks) + void processCharmTriggersWEffInfo(TrigCharmCands const& trigCands, + aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; - for (const auto& collision : collisions) { + for (const auto& trigCand : trigCands) { + auto collision = trigCand.template hfcRedCorrColl_as(); if (!isSelCollision(collision)) { continue; } - int poolBin = binPolicyPosZCent.getBin({collision.posZ(), collision.centrality()}); - - auto thisCollId = collision.globalIndex(); - auto candsThisColl = candidates.sliceBy(trigCharmCandsPerCol, thisCollId); - auto tracksThisColl = tracks.sliceBy(assocTracksPerCol, thisCollId); + if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { + continue; + } - doCorrelationsSameEvent(poolBin, candsThisColl, tracksThisColl); + double effTrig = doEfficiencyCorrection ? getEfficiency(hEffPrompt.get(), effHistTypeCharm.value, trigCand) : 1.; + if (doEfficiencyCorrection && !isValidEfficiency(effTrig)) { + LOGF(warn, "Invalid efficiency value for trigger candidate: effTrig = %f", effTrig); + continue; + } + std::vector values = {trigCand.invMassTrig(), trigCand.ptTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()}; + double weight = doEfficiencyCorrection ? 1. / effTrig : 1.; + registry.get(HIST("hSparseTrigCandsCharm"))->Fill(values.data(), weight); } } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWCentMixBase, "Process Same Event base", false); - - void processMixedEventCharmHadWCentMixBase(aod::HfcRedCorrColls const& collisions, - TrigCharmCands const& candidates, - aod::HfcRedAssBases const& tracks) - { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; - - doCorrelationsMixedEvent(collisions, candidates, tracks, binPolicyPosZCent); - } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processMixedEventCharmHadWCentMixBase, "Process Mixed Event base", false); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processCharmTriggersWEffInfo, "Process charm trigger info with efficiency", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 06ae7c8a758..3efa36a6d9f 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -16,11 +16,13 @@ /// \author Wu Chuntai , CCNU, INFN Padova, and Padova University #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsFlow.h" #include "PWGHF/Utils/utilsEvSelHf.h" #include "Common/Core/RecoDecay.h" @@ -49,6 +51,7 @@ #include #include #include +#include #include #include @@ -57,19 +60,10 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_evsel; +using namespace o2::hf_decay; +using namespace o2::hfc_utils_flow; -enum CandidateType { - DplusToPiKPi = 0, - DsToKKPi, - DsToPiKK, - D0ToPiK, - D0ToKPi, - LcToPKPi, - LcToPiKP, - Hadron -}; - -/// Code to select collisions with at least one Ds meson +/// Code to produce self-contained derived datasets for v2 analyses of charm hadrons via 2PC struct HfDerivedDataCreatorCorrelationsReduced { Produces rowCollisions; // Table with reduced collision info Produces rowSECharmHadPairs; // Table with same-event pairs info @@ -83,6 +77,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; Configurable selectionFlag{"selectionFlag", 15, "Selection Flag for hadron (ML score tables are required to run the task)"}; Configurable forceCharmInCollision{"forceCharmInCollision", true, "Flag to force charm in collision"}; + Configurable acceptOnlyPrompt{"acceptOnlyPrompt", true, "Flag to accept only prompt charm in the MC process functions"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."}; Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; @@ -102,84 +97,95 @@ struct HfDerivedDataCreatorCorrelationsReduced { Configurable downSampleTrksFactor{"downSampleTrksFactor", 1., "Fraction of associated tracks to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; Configurable centMaxForDownSample{"centMaxForDownSample", 101., "Maximum centrality for the application of the downsampling factor"}; - Configurable> binsPtTrig{"binsPtTrig", std::vector{0., 1., 2., 3., 5., 8., 12., 24., 36.}, "pT bin limits for trigger candidates"}; - Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.2, 1., 2., 50.}, "pT bin limits for associated particles"}; HfEventSelection hfEvSel; // event selection and monitoring + HfcFlowHelper hfcFlowHelper; // flow utilities o2::framework::Service ccdb{}; SliceCache cache; - double massCharm{0.}; - TF1* funcDcaXYPtCutPrimTrk = nullptr; + std::unique_ptr funcDcaXYPtCutPrimTrk{nullptr}; - using CollsWithCentMult = soa::Join; - using CandDsData = soa::Filtered>; - using CandDplusData = soa::Filtered>; + using DataCollsWithCentMult = soa::Join; + using McCollsWithCentMult = soa::Join; + + // Data using CandD0Data = soa::Filtered>; + using CandDplusData = soa::Filtered>; + using CandDsData = soa::Filtered>; using CandLcData = soa::Filtered>; - using TracksData = soa::Filtered>; + using TracksData = soa::Filtered>; //, aod::pidTPCFullMu, aod::pidTPCFullEl, aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr>>; + // MC + using CandD0Mc = soa::Filtered>; + using CandDplusMc = soa::Filtered>; + using CandDsMc = soa::Filtered>; + using CandLcMc = soa::Filtered>; + using TracksMc = soa::Filtered>; //, aod::pidTPCFullMu, aod::pidTPCFullEl, aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr>>; - Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; - Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; + Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectTrkData = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); - Preslice candsDsPerColl = aod::hf_cand::collisionId; - Preslice candsDplusPerColl = aod::hf_cand::collisionId; - Preslice candsD0PerColl = aod::hf_cand::collisionId; - Preslice candsLcPerColl = aod::hf_cand::collisionId; - Preslice trackIndicesPerColl = aod::track::collisionId; - - Partition selectedDsToKKPi = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; - Partition selectedDsToPiKK = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; - Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; - Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Partition selectedLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; - Partition selectedLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; - - ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, ""}; + Partition selD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; + Partition selD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Partition selDsToKKPi = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; + Partition selDsToPiKK = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; + Partition selLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Partition selLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; + + Partition recoSelD0ToPiK = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_2prong::DecayChannelMain::D0ToPiK)) && aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelD0ToKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_2prong::DecayChannelMain::D0ToPiK)) && aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDPlusToPiKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) && aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDsToKKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DsToPiKK) && nabs(aod::hf_cand_mc_flag::flagMcDecayChanRec) == static_cast(hf_cand_3prong::DecayChannelResonant::DsToPhiPi)) && aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelDsToPiKK = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::DsToPiKK) && nabs(aod::hf_cand_mc_flag::flagMcDecayChanRec) == static_cast(hf_cand_3prong::DecayChannelResonant::DsToPhiPi)) && aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelLcToPKPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::LcToPKPi)) && aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + Partition recoSelLcToPiKP = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_cand_3prong::DecayChannelMain::LcToPKPi)) && aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag && ((!acceptOnlyPrompt) || (acceptOnlyPrompt && aod::hf_cand_mc_flag::originMcRec == static_cast(RecoDecay::OriginType::Prompt))); + + ConfigurableAxis binsTpcSignal{"binsTpcSignal", {500, 0., 500.}, "TPC signal bins"}; + ConfigurableAxis binsPtTrig{"binsPtTrig", {24, 0, 24}, ""}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {20, 0, 10}, ""}; ConfigurableAxis binsMult{"binsMult", {100, 0., 10000.}, "Multiplicity bins"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; ConfigurableAxis binsPhi{"binsPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Phi bins"}; - ConfigurableAxis binsDeltaEta{"binsDeltaEta", {100, -2., 2.}, "Delta Eta bins"}; - ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -3., 3.}, "Delta Phi bins"}; - ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, ""}; - ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, ""}; ConfigurableAxis binsDca{"binsDca", {200, -1., 1.}, ""}; HistogramRegistry registry{"registry", {}}; void init(InitContext&) { - if (doprocessDplusSameEvent || doprocessDplusMixedEvent) { - massCharm = o2::constants::physics::MassDPlus; - } else if (doprocessDsSameEvent || doprocessDsMixedEvent) { - massCharm = o2::constants::physics::MassDS; - } else if (doprocessD0SameEvent || doprocessD0MixedEvent) { - massCharm = o2::constants::physics::MassD0; - } else if (doprocessLcSameEvent || doprocessLcMixedEvent) { - massCharm = o2::constants::physics::MassLambdaCPlus; - } else if (doprocessHadronHadronSameEvent || doprocessHadronHadronMixedEvent) { - LOG(info) << "Charm mass not set, processing Hadron-Hadron case"; - } else { - LOG(fatal) << "No decay channel selected to process"; - } hfEvSel.addHistograms(registry); // collision monitoring ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); + std::vector classesMl; + for (const auto scoreClass : classMl.value) { + if (scoreClass < 0) { + LOG(fatal) << "ML class indexes must be non-negative"; + return; + } + classesMl.push_back(static_cast(scoreClass)); + } + + if (classesMl.size() > 2) { + LOG(fatal) << "At most two ML classes can be selected"; + return; + } + + hfcFlowHelper.init(ccdb, registry, classesMl, yCandMax.value); + + const AxisSpec axisTpcSignal = {binsTpcSignal, "TPC Signal"}; const AxisSpec axisCent = {binsCent, "Centrality"}; const AxisSpec axisMult = {binsMult, "Multiplicity"}; const AxisSpec axisPosZ = {binsPosZ, "PosZ"}; const AxisSpec axisEta = {binsEta, "#it{#eta}"}; const AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisDcaXY = {binsDca, "DCA XY (cm)"}; const AxisSpec axisDcaZ = {binsDca, "DCA Z (cm)"}; @@ -197,105 +203,12 @@ struct HfDerivedDataCreatorCorrelationsReduced { // Setup pt-dependent DCAxy cut function if (usePtDiffDcaXYCut) { - funcDcaXYPtCutPrimTrk = new TF1("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100); + funcDcaXYPtCutPrimTrk = std::make_unique("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100.); funcDcaXYPtCutPrimTrk->SetParameter(0, dcaXYTrkNSigmaMax); LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data())); } }; // end init - /// Get charm hadron candidate mass - /// \param candidate is the charm hadron candidate - template - double getCandMass(const TCand& candidate) - { - if constexpr (CandType == CandidateType::DsToKKPi) { - return HfHelper::invMassDsToKKPi(candidate); - } - if constexpr (CandType == CandidateType::DsToPiKK) { - return HfHelper::invMassDsToPiKK(candidate); - } - if constexpr (CandType == CandidateType::DplusToPiKPi) { - return HfHelper::invMassDplusToPiKPi(candidate); - } - if constexpr (CandType == CandidateType::D0ToPiK) { - return HfHelper::invMassD0ToPiK(candidate); - } - if constexpr (CandType == CandidateType::D0ToKPi) { - return HfHelper::invMassD0barToKPi(candidate); - } - if constexpr (CandType == CandidateType::LcToPKPi) { - return HfHelper::invMassLcToPKPi(candidate); - } - if constexpr (CandType == CandidateType::LcToPiKP) { - return HfHelper::invMassLcToPiKP(candidate); - } - return -1.; - } - - /// Get charm hadron bdt scores - /// \param candidate is the charm hadron candidate - template - std::array getCandMlScores(const TCand& candidate) - { - std::array outputMl{-999.f, -999.f}; - if constexpr (CandType == CandidateType::DsToKKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::DsToPiKK) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::DplusToPiKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::D0ToPiK) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::D0ToKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::LcToPKPi) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; - } - } - if constexpr (CandType == CandidateType::LcToPiKP) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl->at(iclass)]; - } - } - return outputMl; - } - - /// Cut on rapidity of the candidate - /// \param candidate is the charm hadron candidate - template - bool cutCandRapidity(const TCand& candidate) - { - double y = 0.0; - if constexpr (CandType == CandidateType::DsToKKPi || CandType == CandidateType::DsToPiKK) { - y = HfHelper::yDs(candidate); - } else if constexpr (CandType == CandidateType::DplusToPiKPi) { - y = HfHelper::yDplus(candidate); - } else if constexpr (CandType == CandidateType::D0ToPiK || CandType == CandidateType::D0ToKPi) { - y = HfHelper::yD0(candidate); - } else if constexpr (CandType == CandidateType::LcToPKPi || CandType == CandidateType::LcToPiKP) { - y = HfHelper::yLc(candidate); - } else { - return true; - } - return std::fabs(y) < yCandMax; - } - /// Check event selections for collision and fill the collision table /// \param collision is the collision template @@ -325,7 +238,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Checks if the trigger cand-associated track pair can be accepted for SE correlation /// \param assTrk is the associated track /// \param cand is the trigger candidate - template + template bool acceptSameEvtPair(TAssocTrk const& assTrk, TCand const& cand, double deltaEta) { if (std::abs(deltaEta) <= deltaEtaAbsMin || std::abs(deltaEta) > deltaEtaAbsMax) { @@ -337,7 +250,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } int const trackGlobalIndex = assTrk.globalIndex(); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { if (!cand.isGlobalTrackWoDCA() || cand.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) { return false; } @@ -363,24 +276,25 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// \param assTrks are the associated tracks /// \param cent is the collision centrality /// \param mult is the collision multiplicity - template + template void fillSameEvent(TTrigCands const& trigCands, TAssocTrks const& assTrks, const float cent, const float mult) { for (const auto& trigCand : trigCands) { - if (!cutCandRapidity(trigCand)) { + if (!hfcFlowHelper.cutCandRapidity(trigCand)) { continue; } double trigCandPt = trigCand.pt(); registry.fill(HIST("hPhiVsPtTrig"), RecoDecay::constrainAngle(trigCand.phi(), -o2::constants::math::PIHalf), trigCandPt); registry.fill(HIST("hEtaVsPtTrig"), trigCand.eta(), trigCandPt); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { rowTrigHads(rowCollisions.lastIndex(), trigCandPt, trigCand.tpcNClsCrossedRows(), trigCand.itsClusterMap(), trigCand.itsNCls(), trigCand.dcaXY(), trigCand.dcaZ()); } else { - std::array outputMl = getCandMlScores(trigCand); - rowTrigCharms(rowCollisions.lastIndex(), trigCandPt, getCandMass(trigCand), outputMl[0], outputMl[1]); + std::array outputMl = hfcFlowHelper.getCandMlScores(trigCand); + rowTrigCharms(rowCollisions.lastIndex(), trigCandPt, hfcFlowHelper.getCandMass(trigCand), outputMl[0], outputMl[1]); + rowTrigBases(trigCand.phi(), trigCand.eta()); } for (const auto& assTrk : assTrks) { @@ -407,13 +321,14 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); - registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); double deltaPhi = RecoDecay::constrainAngle(assTrk.phi() - trigCand.phi(), -o2::constants::math::PIHalf); + rowAssocBases(rowCollisions.lastIndex(), assTrk.phi(), assTrk.eta(), assTrkPt); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); - if constexpr (CandType == CandidateType::Hadron) { + if constexpr (CandType == TriggerChannels::Hadron) { rowSEHadHadPairs(rowCollisions.lastIndex(), rowTrigHads.lastIndex(), assTrkPt, deltaEta, deltaPhi); } else { rowSECharmHadPairs(rowCollisions.lastIndex(), rowTrigCharms.lastIndex(), assTrkPt, deltaEta, deltaPhi); @@ -424,19 +339,19 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill charm hadron tables for mixed-event /// \param trigCands are the charm trigger candidates - template + template void fillCharmMixedEvent(TTrigCands const& trigCands) { for (const auto& trigCand : trigCands) { - if (!cutCandRapidity(trigCand)) { + if (!hfcFlowHelper.cutCandRapidity(trigCand)) { continue; } registry.fill(HIST("hPhiVsPtTrig"), RecoDecay::constrainAngle(trigCand.phi(), -o2::constants::math::PIHalf), trigCand.pt()); registry.fill(HIST("hEtaVsPtTrig"), trigCand.eta(), trigCand.pt()); - std::array outputMl = getCandMlScores(trigCand); + std::array outputMl = hfcFlowHelper.getCandMlScores(trigCand); rowTrigBases(trigCand.phi(), trigCand.eta()); - rowTrigCharms(rowCollisions.lastIndex(), trigCand.pt(), getCandMass(trigCand), outputMl[0], outputMl[1]); + rowTrigCharms(rowCollisions.lastIndex(), trigCand.pt(), hfcFlowHelper.getCandMass(trigCand), outputMl[0], outputMl[1]); } } @@ -444,7 +359,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// \param assTrks are the associated tracks /// \param cent is the collision centrality /// \param mult is the collision multiplicity - template + template void fillTrkMixedEvent(TAssocTrks const& assTrks, const float cent, const float mult) @@ -479,10 +394,11 @@ struct HfDerivedDataCreatorCorrelationsReduced { } } - // Dplus with ML selections - void processDplusSameEvent(CollsWithCentMult::iterator const& coll, - CandDplusData const& candsDplus, - TracksData const& tracks) + // CHARM-HADRON CORRELATIONS PROCESS FUNCTIONS --- DATA + // Dplus data + void processDataDplusSE(DataCollsWithCentMult::iterator const& coll, + CandDplusData const& candsDplus, + TracksData const& tracks) { if (forceCharmInCollision && candsDplus.size() < 1) { return; @@ -492,14 +408,14 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDplus, tracks, cent, mult); + fillSameEvent(candsDplus, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusSameEvent, "Process Same Event for Dplus candidates", true); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDplusSE, "Process Same Event for Dplus candidates", true); - // Dplus with ML selections - void processDplusMixedEvent(CollsWithCentMult::iterator const& coll, - CandDplusData const& candsDplus, - TracksData const& tracks) + // Dplus data + void processDataDplusME(DataCollsWithCentMult::iterator const& coll, + CandDplusData const& candsDplus, + TracksData const& tracks) { if (forceCharmInCollision && candsDplus.size() < 1) { return; @@ -509,18 +425,18 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsDplus); - fillTrkMixedEvent(tracks, cent, mult); + fillCharmMixedEvent(candsDplus); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusMixedEvent, "Process Mixed Event for Dplus candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDplusME, "Process Mixed Event for Dplus candidates", false); - // Ds with ML selections - void processDsSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandDsData const&) + // Ds data + void processDataDsSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandDsData const&) { - auto candsDsToPiKK = selectedDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsDsToKKPi = selectedDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToPiKK = selDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToKKPi = selDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsDsToPiKK.size() < 1 && candsDsToKKPi.size() < 1) { return; } @@ -529,18 +445,18 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDsToPiKK, tracks, cent, mult); - fillSameEvent(candsDsToKKPi, tracks, cent, mult); + fillSameEvent(candsDsToPiKK, tracks, cent, mult); + fillSameEvent(candsDsToKKPi, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsSameEvent, "Process Same Event for Ds candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDsSE, "Process Same Event for Ds candidates", false); - // Ds with ML selections - void processDsMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandDsData const&) + // Ds data + void processDataDsME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandDsData const&) { - auto candsDsToPiKK = selectedDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsDsToKKPi = selectedDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToPiKK = selDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsDsToKKPi = selDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsDsToPiKK.size() < 1 && candsDsToKKPi.size() < 1) { return; } @@ -549,19 +465,19 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsDsToPiKK); - fillCharmMixedEvent(candsDsToKKPi); - fillTrkMixedEvent(tracks, cent, mult); + fillCharmMixedEvent(candsDsToPiKK); + fillCharmMixedEvent(candsDsToKKPi); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsMixedEvent, "Process Mixed Event for Ds candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataDsME, "Process Mixed Event for Ds candidates", false); - // D0 with ML selections - void processD0SameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandD0Data const&) + // D0 data + void processDataD0SE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandD0Data const&) { - auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToPiK = selD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToKPi = selD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsD0ToPiK.size() < 1 && candsD0ToKPi.size() < 1) { return; } @@ -570,18 +486,18 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsD0ToPiK, tracks, cent, mult); - fillSameEvent(candsD0ToKPi, tracks, cent, mult); + fillSameEvent(candsD0ToPiK, tracks, cent, mult); + fillSameEvent(candsD0ToKPi, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0SameEvent, "Process Same Event for D0 candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataD0SE, "Process Same Event for D0 candidates", false); - // D0 with ML selections - void processD0MixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandD0Data const&) + // D0 data + void processDataD0ME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandD0Data const&) { - auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToPiK = selD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsD0ToKPi = selD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsD0ToPiK.size() < 1 && candsD0ToKPi.size() < 1) { return; } @@ -590,19 +506,19 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsD0ToPiK); - fillCharmMixedEvent(candsD0ToKPi); - fillTrkMixedEvent(tracks, cent, mult); + fillCharmMixedEvent(candsD0ToPiK); + fillCharmMixedEvent(candsD0ToKPi); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0MixedEvent, "Process Mixed Event for D0 candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataD0ME, "Process Mixed Event for D0 candidates", false); - // Lc with ML selections - void processLcSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandLcData const&) + // Lc data + void processDataLcSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandLcData const&) { - auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPKPi = selLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPiKP = selLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } @@ -611,18 +527,18 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsLcToPKPi, tracks, cent, mult); - fillSameEvent(candsLcToPiKP, tracks, cent, mult); + fillSameEvent(candsLcToPKPi, tracks, cent, mult); + fillSameEvent(candsLcToPiKP, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcSameEvent, "Process Same Event for Lc candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataLcSE, "Process Same Event for Lc candidates", false); - // Lc with ML selections - void processLcMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks, - CandLcData const&) + // Lc data + void processDataLcME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks, + CandLcData const&) { - auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPKPi = selLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsLcToPiKP = selLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } @@ -631,37 +547,226 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillCharmMixedEvent(candsLcToPKPi); - fillCharmMixedEvent(candsLcToPiKP); - fillTrkMixedEvent(tracks, cent, mult); + fillCharmMixedEvent(candsLcToPKPi); + fillCharmMixedEvent(candsLcToPiKP); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataLcME, "Process Mixed Event for Lc candidates", false); + + // CHARM-HADRON CORRELATIONS PROCESS FUNCTIONS --- MC + // Dplus MC + void processMcDplusSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDplusMc const&) + { + auto candsRecoDplus = recoSelDPlusToPiKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDplus.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoDplus, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDplusSE, "Process Same Event for Dplus candidates", false); + + // Dplus MC + void processMcDplusME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDplusMc const&) + { + auto candsRecoDplus = recoSelDPlusToPiKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDplus.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoDplus); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDplusME, "Process Mixed Event for Dplus candidates", false); + + // Ds MC + void processMcDsSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDsMc const&) + { + auto candsRecoDsToPiKK = recoSelDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoDsToKKPi = recoSelDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDsToPiKK.size() < 1 && candsRecoDsToKKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoDsToPiKK, tracks, cent, mult); + fillSameEvent(candsRecoDsToKKPi, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDsSE, "Process Same Event for Ds candidates", false); + + // Ds MC + void processMcDsME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandDsMc const&) + { + auto candsRecoDsToPiKK = recoSelDsToPiKK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoDsToKKPi = recoSelDsToKKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoDsToPiKK.size() < 1 && candsRecoDsToKKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoDsToPiKK); + fillCharmMixedEvent(candsRecoDsToKKPi); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcDsME, "Process Mixed Event for Ds candidates", false); + + // D0 MC + void processMcD0SE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandD0Mc const&) + { + auto candsRecoD0ToPiK = recoSelD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoD0ToKPi = recoSelD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoD0ToPiK.size() < 1 && candsRecoD0ToKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoD0ToPiK, tracks, cent, mult); + fillSameEvent(candsRecoD0ToKPi, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcD0SE, "Process Same Event for D0 candidates", false); + + // D0 MC + void processMcD0ME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandD0Mc const&) + { + auto candsRecoD0ToPiK = recoSelD0ToPiK->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoD0ToKPi = recoSelD0ToKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoD0ToPiK.size() < 1 && candsRecoD0ToKPi.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoD0ToPiK); + fillCharmMixedEvent(candsRecoD0ToKPi); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcD0ME, "Process Mixed Event for D0 candidates", false); + + // Lc MC + void processMcLcSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandLcMc const&) + { + auto candsRecoLcToPKPi = recoSelLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoLcToPiKP = recoSelLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoLcToPKPi.size() < 1 && candsRecoLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(candsRecoLcToPKPi, tracks, cent, mult); + fillSameEvent(candsRecoLcToPiKP, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcLcSE, "Process Same Event for Lc candidates", false); + + // Lc MC + void processMcLcME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks, + CandLcMc const&) + { + auto candsRecoLcToPKPi = recoSelLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + auto candsRecoLcToPiKP = recoSelLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsRecoLcToPKPi.size() < 1 && candsRecoLcToPiKP.size() < 1) { + return; + } + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillCharmMixedEvent(candsRecoLcToPKPi); + fillCharmMixedEvent(candsRecoLcToPiKP); + fillTrkMixedEvent(tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcLcME, "Process Mixed Event for Lc candidates", false); + + // HADRON-HADRON CORRELATIONS PROCESS FUNCTIONS --- DATA + // Hadron Hadron Same Event + void processDataHadronHadronSE(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks) + { + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillSameEvent(tracks, tracks, cent, mult); + } + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataHadronHadronSE, "Process Same Event for hadron candidates", false); + + // Hadron Hadron Mixed Event + void processDataHadronHadronME(DataCollsWithCentMult::iterator const& coll, + TracksData const& tracks) + { + float cent{-1.}, mult{-1.}; + if (!checkCollision(coll, cent, mult)) { + return; + } + rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcMixedEvent, "Process Mixed Event for Lc candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDataHadronHadronME, "Process Mixed Event for hadron candidates", false); + // HADRON-HADRON CORRELATIONS PROCESS FUNCTIONS --- MC // Hadron Hadron Same Event - void processHadronHadronSameEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks) + void processMcHadronHadronSE(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks) { float cent{-1.}, mult{-1.}; if (!checkCollision(coll, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(tracks, tracks, cent, mult); + fillSameEvent(tracks, tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronSameEvent, "Process Same Event for hadron candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcHadronHadronSE, "Process Same Event for hadron candidates", false); // Hadron Hadron Mixed Event - void processHadronHadronMixedEvent(CollsWithCentMult::iterator const& coll, - TracksData const& tracks) + void processMcHadronHadronME(McCollsWithCentMult::iterator const& coll, + TracksMc const& tracks) { float cent{-1.}, mult{-1.}; if (!checkCollision(coll, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillTrkMixedEvent(tracks, cent, mult); + fillTrkMixedEvent(tracks, cent, mult); } - PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronMixedEvent, "Process Mixed Event for hadron candidates", false); + PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcHadronHadronME, "Process Mixed Event for hadron candidates", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 4551ccd2b0f..8c60c81b29d 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -74,6 +74,11 @@ o2physics_add_dpl_workflow(task-flow PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-correlations-efficiencies + SOURCES taskCorrelationsEfficiencies.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PWGCF/Core) o2physics_add_dpl_workflow(task-charm-hadrons-charm-femto-dream diff --git a/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx new file mode 100644 index 00000000000..8407dcbde82 --- /dev/null +++ b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx @@ -0,0 +1,438 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskCorrelationsEfficiencies.cxx +/// \brief Charm hadrons and associated tracks efficiency evaluation for correlations studies +/// \author Marcello Di Costanzo , Politecnico and INFN Torino + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsFlow.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; +using namespace o2::hfc_utils_flow; + +namespace o2::aod +{ +namespace hf_correlations_eff +{ +// Charm-hadron candidate columns +DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(PvContrib, pvContrib, int); //! Number of primary vertex contributors +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity of candidate +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuthal angle of candidate +DECLARE_SOA_COLUMN(Origin, origin, int); //! Origin of the candidate (prompt or non-prompt) +DECLARE_SOA_COLUMN(MlScore0, mlScore0, float); //! ML score of the first configured index +DECLARE_SOA_COLUMN(MlScore1, mlScore1, float); //! ML score of the second configured index + +// Associated track columns +DECLARE_SOA_COLUMN(PtAssoc, ptAssoc, float); //! Transverse momentum of associated track (GeV/c) +DECLARE_SOA_COLUMN(EtaAssoc, etaAssoc, float); //! Pseudorapidity of associated track +DECLARE_SOA_COLUMN(PhiAssoc, phiAssoc, float); //! Azimuthal angle of associated track +DECLARE_SOA_COLUMN(ZVtx, zVtx, float); //! Primary vertex z position (cm) +DECLARE_SOA_COLUMN(AssTrkSpecie, assTrkSpecie, int); //! Associated track species (0=pi, 1=K, 2=p, 3=el, 4=mu) +DECLARE_SOA_COLUMN(AssTrkOrigin, assTrkOrigin, int); //! Associated track origin (0=primary, 1=secondary) +} // namespace hf_correlations_eff + +DECLARE_SOA_TABLE(HfcMcrigInfo, "AOD", "HFCMCTRIGINFO", + hf_correlations_eff::Pt, + hf_correlations_eff::PvContrib, + hf_correlations_eff::Eta, + hf_correlations_eff::Phi, + hf_correlations_eff::Origin, + hf_correlations_eff::MlScore0, + hf_correlations_eff::MlScore1); + +DECLARE_SOA_TABLE(HfcMcAssocInfo, "AOD", "HFCMCASSOCINFO", + hf_correlations_eff::PtAssoc, + hf_correlations_eff::EtaAssoc, + hf_correlations_eff::PhiAssoc, + hf_correlations_eff::ZVtx, + hf_correlations_eff::AssTrkSpecie, + hf_correlations_eff::AssTrkOrigin); +} // namespace o2::aod + +/// Code to obtain efficiency correction factors for (Delta Eta, Delta Phi) correlation maps +struct HfTaskCorrelationsEfficiencies { + + Produces rowTrigMc; + Produces rowAssocMc; + Produces rowAssocTrkSels; + + Configurable selectionFlag{"selectionFlag", 15, "Selection Flag for hadron (ML score tables are required to run the task)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 70, "min. TPC crossed rows for associated tracks"}; + Configurable etaTrkMax{"etaTrkMax", 1., "max. track eta"}; + Configurable ptTrkMin{"ptTrkMin", 0.3, "min. track pT"}; + Configurable ptTrkMax{"ptTrkMax", 3., "max. track pT"}; + Configurable dcaXYTrkMax{"dcaXYTrkMax", 1., "max. track DCA XY"}; + Configurable dcaZTrkMax{"dcaZTrkMax", 1., "max. track DCA Z"}; + Configurable usePtDiffDcaXYCut{"usePtDiffDcaXYCut", false, "Use pt-differential DCAxy cut for associated tracks"}; + Configurable dcaXYTrkNSigmaMax{"dcaXYTrkNSigmaMax", 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"}; + Configurable dcaXYPtPrimTrkFunc{"dcaXYPtPrimTrkFunc", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; + + // Configurables for tree filling + Configurable fillRecoTrees{"fillRecoTrees", false, "Fill trees of reconstructed charmed hadrons and associated tracks"}; + Configurable downSampleTracks{"downSampleTracks", 1., "Fraction of tracks to keep"}; + Configurable ptMaxDownSampleTracks{"ptMaxDownSampleTracks", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable downSampleCharm{"downSampleCharm", 1., "Fraction of pairs to keep"}; + Configurable ptMaxDownSampleCharm{"ptMaxDownSampleCharm", 10., "Maximum pt for the application of the downsampling factor"}; + + HfcFlowHelper hfcFlowHelper; // flow utilities + o2::framework::Service ccdb{}; + SliceCache cache; + + std::unique_ptr funcDcaXYPtCutPrimTrk{nullptr}; + + using RecoCollisions = soa::Join; + using McCandsDs = soa::Filtered>; + using McCandsDplus = soa::Filtered>; + using McCandsD0 = soa::Filtered>; + using McCandsLc = soa::Filtered>; + using McTracks = soa::Filtered>; + + Filter filterSelectDsCands = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; + Filter filterSelectDplusCands = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; + Filter filterSelectD0Cands = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectLcCands = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; + Filter filterSelectTrk = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); + + Preslice candsDsPerColl = aod::hf_cand::collisionId; + Preslice candsDplusPerColl = aod::hf_cand::collisionId; + Preslice candsD0PerColl = aod::hf_cand::collisionId; + Preslice candsLcPerColl = aod::hf_cand::collisionId; + Preslice trackIndicesPerColl = aod::track::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + ConfigurableAxis binsPtCharm{"binsPtCharm", {VARIABLE_WIDTH, 0., 2., 4., 6., 8., 24.}, ""}; + ConfigurableAxis binsPtAssoc{"binsPtAssoc", {20, 0, 10}, ""}; + ConfigurableAxis binsZVtx{"binsZVtx", {120, -11, 11}, ""}; + ConfigurableAxis binsPvContrib{"binsPvContrib", {5001, -0.5, 5000.5}, ""}; + ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, ""}; + ConfigurableAxis binsPhi{"binsPhi", {64, 0, 6.28}, ""}; + ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, ""}; + ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, ""}; + + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + std::vector classesMl; + for (const auto scoreClass : classMl.value) { + if (scoreClass < 0) { + LOG(fatal) << "ML class indexes must be non-negative"; + return; + } + classesMl.push_back(static_cast(scoreClass)); + } + + if (classesMl.size() > 2) { + LOG(fatal) << "At most two ML classes can be selected"; + return; + } + + hfcFlowHelper.init(ccdb, registry, classesMl, yCandMax.value); + + // Setup pt-dependent DCAxy cut function + if (usePtDiffDcaXYCut) { + funcDcaXYPtCutPrimTrk = std::make_unique("funcDcaXYPtCutPrimTrk", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data()), 0.001, 100.); + funcDcaXYPtCutPrimTrk->SetParameter(0, dcaXYTrkNSigmaMax); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaXYPtPrimTrkFunc.value.data())); + } + + // Add efficiency sparses + // Axes: mass, pt, pv contributors, pseudo-rapidity, ml_score_0, ml_score_1 + const AxisSpec axisPtTrig = {binsPtCharm, "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPvContrib = {binsPvContrib, "PV contributors"}; + const AxisSpec axisEtaTrig = {binsEta, "#it{#eta}"}; + const AxisSpec axisPhiTrig = {binsPhi, "#it{#varphi}"}; + const AxisSpec axisMlScore0 = {binsMlOne, "ML score 0"}; + const AxisSpec axisMlScore1 = {binsMlTwo, "ML score 1"}; + std::vector axesCharmReco = {axisPtTrig, axisPvContrib, axisEtaTrig, axisPhiTrig, axisMlScore0, axisMlScore1}; + + registry.add("hCharmPromptGen", "Generated prompt charm-hadron candidates;;", HistType::kTHnSparseF, {{axisPtTrig}, {axisEtaTrig}, {axisPhiTrig}}); + registry.add("hCharmFdGen", "Generated non-prompt charm-hadron candidates;;", HistType::kTHnSparseF, {{axisPtTrig}, {axisEtaTrig}, {axisPhiTrig}}); + registry.add("hCharmPromptReco", "Reconstructed prompt charm-hadron candidates;;", HistType::kTHnSparseF, axesCharmReco); + registry.add("hCharmFdReco", "Reconstructed non-prompt charm-hadron candidates;;", HistType::kTHnSparseF, axesCharmReco); + + // Axes: pt, eta, phi, z_vtx, specie + const AxisSpec axisPtAssoc = {binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisEtaAssoc = {binsEta, "#it{#eta}"}; + const AxisSpec axisPhiAssoc = {binsPhi, "#it{#varphi}"}; + const AxisSpec axisZVtx = {binsZVtx, "#it{z}_{vtx}"}; + const AxisSpec axisAssTrkSpecie = {static_cast(NAssTracksTypes), -0.5, static_cast(NAssTracksTypes) - 0.5, "Particle species"}; + const AxisSpec axisTrackOrigin = {2, -0.5, 1.5, "Particle origin"}; + std::vector axesAssocTracks = {axisPtAssoc, axisEtaAssoc, axisPhiAssoc, axisZVtx, axisAssTrkSpecie, axisTrackOrigin}; + registry.add("hAssTrackReco", "Reconstructed associated tracks;;", HistType::kTHnSparseF, axesAssocTracks); + registry.add("hAssTrackGen", "Generated associated tracks;;", HistType::kTHnSparseF, axesAssocTracks); + } + + /// Match charm-hadron based on its reconstruction flag + /// \param assTrk is the associated track + /// \return the associated track specie index + template + bool isMcMatched(TCharmHad const& cand) + { + int flagMc{-1}; + if constexpr (IsReco) { + flagMc = std::abs(cand.flagMcMatchRec()); + } else { + flagMc = std::abs(cand.flagMcMatchGen()); + } + + if constexpr (CandType == TriggerChannels::D0ToPiK) { + return flagMc == static_cast(hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + } else if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + } else if constexpr (CandType == TriggerChannels::LcToPKPi) { + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + } else { // Ds + int8_t reso{0}; + if constexpr (IsReco) { + reso = std::abs(cand.flagMcDecayChanRec()); + } else { + reso = std::abs(cand.flagMcDecayChanGen()); + } + return flagMc == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) && + reso == static_cast(hf_decay::hf_cand_3prong::DecayChannelResonant::DsToPhiPi); + } + } + + /// Fill info for charm-hadron efficiencies + /// \param trigCands are the trigger candidates + /// \param assTrks are the associated tracks + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity + template + void fillCharmEfficiencies(TCharmCands const& charmCands, int numContrib) + { + for (const auto& cand : charmCands) { + if (!hfcFlowHelper.cutCandRapidity(cand)) { + continue; + } + + if (!isMcMatched(cand)) { + continue; + } + + // Fill the sparse + std::array outputMl = hfcFlowHelper.getCandMlScores(cand); + if (cand.originMcRec() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hCharmPromptReco"), cand.pt(), numContrib, cand.eta(), cand.phi(), outputMl[0], outputMl[1]); + } + if (cand.originMcRec() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCharmFdReco"), cand.pt(), numContrib, cand.eta(), cand.phi(), outputMl[0], outputMl[1]); + } + + if (fillRecoTrees) { + // Downsample charm candidates based on pt + if (downSampleCharm < 1.) { + float const pseudoRndm = cand.pt() * 1000. - static_cast(cand.pt() * 1000); + if (cand.pt() < ptMaxDownSampleCharm && pseudoRndm >= downSampleCharm) { + continue; + } + } + rowTrigMc(cand.pt(), numContrib, cand.eta(), cand.phi(), cand.originMcRec(), outputMl[0], outputMl[1]); + } + } + } + + // Dplus efficiency + void processEffDplus(RecoCollisions const& recoCollisions, + McCandsDplus const& candsDplus, + soa::Join const& genParticles, + aod::McCollisions const&) + { + for (const auto& coll : recoCollisions) { + auto candsInColl = candsDplus.sliceBy(candsDplusPerColl, coll.globalIndex()); + if (candsInColl.size() < 1) { + continue; + } + float cent{-1.}, mult{-1.}; + if (!hfcFlowHelper.checkCollision(coll, cent, mult)) { + continue; + } + fillCharmEfficiencies(candsInColl, coll.numContrib()); + } + + for (const auto& genCand : genParticles) { + if (!genCand.has_mcCollision()) { + continue; + } + if (std::abs(genCand.y()) > yCandMax) { + continue; + } + if (!isMcMatched(genCand)) { + continue; + } + + if (genCand.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hCharmPromptGen"), genCand.pt(), genCand.eta(), genCand.phi()); + } + if (genCand.originMcGen() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCharmFdGen"), genCand.pt(), genCand.eta(), genCand.phi()); + } + } + } + PROCESS_SWITCH(HfTaskCorrelationsEfficiencies, processEffDplus, "Process efficiencies for Dplus candidates", false); + + /// Match associated tracks based on their PDG code + /// \param assTrk is the associated track + /// \return the associated track specie index + template + int getAssocTrkSpecie(TAssocTrk const& assTrk) + { + switch (std::abs(assTrk.pdgCode())) { + case kPiPlus: + return static_cast(AssTracksTypes::Pi); + case kKPlus: + return static_cast(AssTracksTypes::Ka); + case kProton: + return static_cast(AssTracksTypes::Pr); + case kElectron: + return static_cast(AssTracksTypes::El); + case kMuonMinus: + return static_cast(AssTracksTypes::Mu); + default: + return -1; + } + } + + // Associated Tracks + void processEffAssTracks(RecoCollisions const& recoCollisions, + McTracks const& recoTracks, + aod::McParticles const& genParticles, + aod::McCollisions const&) + { + for (const auto& coll : recoCollisions) { + float cent{-1.}, mult{-1.}; + if (!hfcFlowHelper.checkCollision(coll, cent, mult)) { + continue; + } + if (!coll.has_mcCollision()) { + continue; + } + auto mcCollision = coll.template mcCollision_as(); + + auto tracksInColl = recoTracks.sliceBy(trackIndicesPerColl, coll.globalIndex()); + for (const auto& assTrk : tracksInColl) { + if (!assTrk.has_mcParticle()) { + continue; + } + auto mcParticle = assTrk.template mcParticle_as(); + int matchedSpecie = getAssocTrkSpecie(mcParticle); + if (matchedSpecie < 0) { + continue; + } + + // DCA selection + double assTrkPt = assTrk.pt(); + if (usePtDiffDcaXYCut) { + float const dcaXYTrkCut = funcDcaXYPtCutPrimTrk->Eval(assTrkPt); + if (std::fabs(assTrk.dcaXY()) > dcaXYTrkCut) { + continue; + } + } + int assTrkOrigin = mcParticle.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary + + std::vector values = {assTrk.pt(), assTrk.eta(), assTrk.phi(), mcCollision.posZ(), static_cast(matchedSpecie), static_cast(assTrkOrigin)}; + + // Fill histograms for associated tracks + registry.get(HIST("hAssTrackReco"))->Fill(values.data()); + + if (fillRecoTrees) { + // Downsample tracks based on pt + if (downSampleTracks < 1.) { + float const pseudoRndm = assTrk.pt() * 1000. - static_cast(assTrk.pt() * 1000); + if (assTrk.pt() < ptMaxDownSampleTracks && pseudoRndm >= downSampleTracks) { + continue; + } + } + rowAssocMc(assTrk.pt(), assTrk.eta(), assTrk.phi(), mcCollision.posZ(), matchedSpecie, assTrkOrigin); + rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); + } + } + } + + // Fill histograms for generated associated tracks + for (const auto& genTrk : genParticles) { + int matchedSpecie = getAssocTrkSpecie(genTrk); + if (matchedSpecie < 0) { + continue; + } + if (!genTrk.has_mcCollision()) { + continue; + } + + // Pt and eta selection + if (genTrk.pt() < ptTrkMin || genTrk.pt() > ptTrkMax || std::fabs(genTrk.eta()) > etaTrkMax) { + continue; + } + + int genTrkOrigin = genTrk.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary + auto genCollision = genTrk.template mcCollision_as(); + registry.fill(HIST("hAssTrackGen"), genTrk.pt(), genTrk.eta(), genTrk.phi(), genCollision.posZ(), matchedSpecie, genTrkOrigin); + } + } + PROCESS_SWITCH(HfTaskCorrelationsEfficiencies, processEffAssTracks, "Process efficiencies for associated tracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/HFC/Utils/utilsFlow.h b/PWGHF/HFC/Utils/utilsFlow.h new file mode 100644 index 00000000000..52b4885bfe4 --- /dev/null +++ b/PWGHF/HFC/Utils/utilsFlow.h @@ -0,0 +1,227 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file utilsFlow.h +/// \brief Utilities for HFC analyses +/// \author Marcello Di Costanzo + +#ifndef PWGHF_HFC_UTILS_UTILSFLOW_H_ +#define PWGHF_HFC_UTILS_UTILSFLOW_H_ + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::hfc_utils_flow +{ + +enum TriggerChannels { + DplusToPiKPi = 0, + DsToKKPi, + DsToPiKK, + D0ToPiK, + D0ToKPi, + LcToPKPi, + LcToPiKP, + Hadron +}; + +enum AssTracksTypes { + Pi = 0, + Ka, + Pr, + El, + Mu, + NAssTracksTypes +}; + +class HfcFlowHelper +{ + + public: + HfcFlowHelper() = default; + + /// Initialize the HFC flow helper + /// \param ccdbService is the CCDB service + /// \param histRegistry is the histogram registry + /// \param mlClasses are the machine learning classes + /// \param maxRapidity is the maximum rapidity + void init(o2::framework::Service& ccdbService, + o2::framework::HistogramRegistry& histRegistry, + std::vector mlClasses, + float maxRapidity) + { + + LOG(info) << "Initializing HFC flow helper!"; + this->hfEvSel.addHistograms(histRegistry); + this->ccdb = &ccdbService; + this->registry = &histRegistry; + this->classMl = std::move(mlClasses); + this->yCandMax = maxRapidity; + } + + /// Get charm hadron candidate mass + /// \param candidate is the charm hadron candidate + template + double getCandMass(const TCand& candidate) + { + if constexpr (CandType == TriggerChannels::DsToKKPi) { + return HfHelper::invMassDsToKKPi(candidate); + } + if constexpr (CandType == TriggerChannels::DsToPiKK) { + return HfHelper::invMassDsToPiKK(candidate); + } + if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + return HfHelper::invMassDplusToPiKPi(candidate); + } + if constexpr (CandType == TriggerChannels::D0ToPiK) { + return HfHelper::invMassD0ToPiK(candidate); + } + if constexpr (CandType == TriggerChannels::D0ToKPi) { + return HfHelper::invMassD0barToKPi(candidate); + } + if constexpr (CandType == TriggerChannels::LcToPKPi) { + return HfHelper::invMassLcToPKPi(candidate); + } + if constexpr (CandType == TriggerChannels::LcToPiKP) { + return HfHelper::invMassLcToPiKP(candidate); + } + return -1.; + } + + /// Get charm hadron bdt scores + /// \param candidate is the charm hadron candidate + template + std::array getCandMlScores(const TCand& candidate) + { + std::array outputMl{-999.f, -999.f}; + if constexpr (CandType == TriggerChannels::DsToKKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::DsToPiKK) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::D0ToPiK) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::D0ToKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0bar()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::LcToPKPi) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl.at(iclass)]; + } + } + if constexpr (CandType == TriggerChannels::LcToPiKP) { + for (unsigned int iclass = 0; iclass < classMl.size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl.at(iclass)]; + } + } + return outputMl; + } + + /// Cut on rapidity of the candidate + /// \param candidate is the charm hadron candidate + template + bool cutCandRapidity(const TCand& candidate) + { + double y = 0.0; + if constexpr (CandType == TriggerChannels::DsToKKPi || CandType == TriggerChannels::DsToPiKK) { + y = HfHelper::yDs(candidate); + } else if constexpr (CandType == TriggerChannels::DplusToPiKPi) { + y = HfHelper::yDplus(candidate); + } else if constexpr (CandType == TriggerChannels::D0ToPiK || CandType == TriggerChannels::D0ToKPi) { + y = HfHelper::yD0(candidate); + } else if constexpr (CandType == TriggerChannels::LcToPKPi || CandType == TriggerChannels::LcToPiKP) { + y = HfHelper::yLc(candidate); + } else { + return true; + } + return std::fabs(y) < yCandMax; + } + + /// Check event selections for collision and fill the collision table + /// \param collision is the collision + template + bool checkCollision(Coll const& collision, float& cent, float& mult) + { + o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; + if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0A) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); + mult = collision.multFT0A(); + } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0C) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); + mult = collision.multFT0C(); + } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0M) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); + mult = collision.multFT0M(); + } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FV0A) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); + mult = collision.multFV0A(); + } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::None) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); + mult = -1.f; + } else { + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + std::abort(); + } + hfEvSel.fillHistograms(collision, collRejMask, cent); + return collRejMask == 0; + } + + private: + o2::hf_evsel::HfEventSelection hfEvSel; + o2::framework::Service* ccdb{nullptr}; + o2::framework::HistogramRegistry* registry{nullptr}; + std::vector classMl{}; + float yCandMax{-999.f}; +}; + +} // namespace o2::hfc_utils_flow +#endif // PWGHF_HFC_UTILS_UTILSFLOW_H_ From ac98774b12c1ffb7fe05a112908ba375056fda02 Mon Sep 17 00:00:00 2001 From: SCHOTTER Romain <47983209+romainschotter@users.noreply.github.com> Date: Fri, 6 Nov 2026 08:08:57 +0800 Subject: [PATCH 585/648] [PWGLF] Fix IsITSAfterburner dynamic column in LFStrangenessTables (#18611) --- PWGLF/DataModel/LFStrangenessTables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/DataModel/LFStrangenessTables.h b/PWGLF/DataModel/LFStrangenessTables.h index 56322a0e47c..e057f10550a 100644 --- a/PWGLF/DataModel/LFStrangenessTables.h +++ b/PWGLF/DataModel/LFStrangenessTables.h @@ -585,7 +585,7 @@ DECLARE_SOA_TABLE_VERSIONED(DauTrackExtras_003, "AOD", "DAUTRACKEXTRA", 3, //! d track::v001::ITSClsSizeInLayer, track::v001::ITSClusterMap, track::v001::ITSNCls, - track::v001::IsITSAfterburner, + track::v001::IsITSAfterburner, /*compatibility*/ dautrack::HasITSTracker, /*compatibility*/ dautrack::HasITSAfterburner, From 9f15f8026f0933804d910955b4e9ab9f56053a6a Mon Sep 17 00:00:00 2001 From: Cristian-Moscatelli Date: Fri, 6 Nov 2026 09:23:22 +0100 Subject: [PATCH 586/648] [PWGLF] Applied new track cuts in deuteronInTriggeredEvents.cxx (#18604) Co-authored-by: Cristian Moscatelli --- .../Nuspex/deuteronInTriggeredEvents.cxx | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx index 22cca32068a..59b015ffb81 100644 --- a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx +++ b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx @@ -40,7 +40,9 @@ #include "Common/Core/ZorroSummary.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -272,6 +274,9 @@ struct DeuteronInTriggeredEvents { Zorro zorro; // Definition of Zorro: helpful for skimmed data OutputObj zorroSummary{"zorroSummary"}; + // Initialize ITS PID Response object + o2::aod::ITSResponse itsResponse; + Configurable cfgCompensatePIDinTracking{"cfgCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; struct : o2::framework::ConfigurableGroup { @@ -287,6 +292,12 @@ struct DeuteronInTriggeredEvents { Configurable tpcNClsMin{"tpcNClsMin", 80, "Minimum number of TPC clusters"}; Configurable tpcRigidityMin{"tpcRigidityMin", 0.5f, "Minimum TPC rigidity for tracks"}; Configurable> tpcNSigmaMax{"tpcNSigmaMax", {nuclei::NSigmaTPCdefault[0], 5, 2, nuclei::names, nuclei::nSigmaConfigName}, "TPC nsigma selection for light nuclei"}; + // Part related to ITS cut + Configurable applyItsPid{"applyItsPid", false, "Apply ITS PID"}; + Configurable setMCDefaultParams{"setMCDefaultParams", false, "Use params in MC ITS PID"}; + Configurable ptMaxItsPid{"ptMaxItsPid", 1.0, "Max pt for ITS PID"}; + Configurable nSigmaItsMin{"nSigmaItsMin", -3.0, "Min nsigma in ITS"}; + Configurable nSigmaItsMax{"nSigmaItsMax", 3.0, "Max nsigma in ITS"}; } cfgTrackCut; Configurable cfgCutPtMinTree{"cfgCutPtMinTree", 0.2f, "Minimum track transverse momentum for tree saving"}; @@ -303,6 +314,8 @@ struct DeuteronInTriggeredEvents { Configurable cfgFillGenSecondaries{"cfgFillGenSecondaries", 0, "Fill generated secondaries (0: no, 1: only weak decays, 2: all of them)"}; Configurable> cfgDCAHists{"cfgDCAHists", {nuclei::DCAHistDefault[0], 5, 2, nuclei::names, nuclei::dcaConfigNames}, "DCA hist configuration"}; + Configurable cfgRemoveTOFtrack{"cfgRemoveTOFtrack", false, "Remove TOF tracks with TPC PID incorrect"}; + Configurable cfgNsigmaTPCvetoLF{"cfgNsigmaTPCvetoLF", 3.f, "TPC nsigma cut for tracks compatible with light flavor hadrons"}; Configurable cfgNsigmaTPCcutDCAhists{"cfgNsigmaTPCcutDCAhists", 3., "TPC nsigma cut for DCA hists"}; Configurable cfgDeltaTOFmassCutDCAhists{"cfgDeltaTOFmassCutDCAhists", 0.2, "Delta TOF mass cut for DCA hists"}; Configurable cfgNsigmaTPCcutTOFhists{"cfgNsigmaTPCcutTOFhists", 3., "TPC nsigma cut for TOF analysis"}; @@ -351,7 +364,7 @@ struct DeuteronInTriggeredEvents { // Utility object for jet background subtraction methods JetBkgSubUtils backgroundSub; - using TrackCandidates = soa::Join; + using TrackCandidates = soa::Join; HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -473,6 +486,11 @@ struct DeuteronInTriggeredEvents { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); + // Set default MC parametrization for ITS response + if (cfgTrackCut.setMCDefaultParams) { + itsResponse.setMCDefaultParameters(); + } + const AxisSpec nSigmaAxes[2]{{cfgNsigmaTPCbins, "n#sigma_{TPC}"}, {cfgNsigmaTOFbins, "n#sigma_{TOF}"}}; const AxisSpec tofMassAxis{cfgTOFmassBins, "TOF mass - PDG mass"}; const AxisSpec ptResAxis{cfgMomResBins, "#Delta#it{p}_{T}/#it{p}_{T}"}; @@ -525,6 +543,7 @@ struct DeuteronInTriggeredEvents { spectra.add("hTpcSignalData", "Specific energy loss", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("hTpcSignalDataSelected", "Specific energy loss for selected particles", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); spectra.add("hTofSignalData", "TOF beta", HistType::kTH2F, {{500, 0., 5., "#it{p} (GeV/#it{c})"}, {750, 0, 1.5, "TOF #beta"}}); + spectra.add("hItsNsigmaDeuteron", "n#sigma_{ITS}", HistType::kTH2F, {{1000, 0., 5., "#it{p}_{T} (GeV/#it{c})"}, {600, -6., 6., "n#sigma_{ITS}"}}); for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { nuclei::hGloTOFtracks[iC] = spectra.add(fmt::format("hTPCTOFtracks{}", nuclei::matter[iC]).data(), fmt::format("Global vs TOF matched {} tracks in a collision", nuclei::chargeLabelNames[iC]).data(), HistType::kTH2D, {{300, -0.5, 300.5, "Number of global tracks"}, {300, -0.5, 300.5, "Number of TOF matched tracks"}}); @@ -781,7 +800,16 @@ struct DeuteronInTriggeredEvents { track.itsChi2NCl() > cfgTrackCut.itsChi2ClusMax) { continue; } - // temporary fix: tpcInnerParam() returns the momentum in all the software tags before --- CM: what does it mean + + double nSigmaIts = static_cast(itsResponse.nSigmaITS(track)); + spectra.fill(HIST("hItsNsigmaDeuteron"), track.pt(), nSigmaIts); + + // ITS Logic for PID Selection + if (cfgTrackCut.applyItsPid && track.pt() < cfgTrackCut.ptMaxItsPid && (nSigmaIts < cfgTrackCut.nSigmaItsMin || nSigmaIts > cfgTrackCut.nSigmaItsMax)) { + continue; + } + + // temporary fix: tpcInnerParam() returns the momentum in all the software tags before bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; float correctedTpcInnerParam = (heliumPID && cfgCompensatePIDinTracking) ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); @@ -799,6 +827,7 @@ struct DeuteronInTriggeredEvents { bool selectedTPC[5]{false}, goodToAnalyse{false}; std::array nSigmaTPC; + // This part apply a selection on tracks: // if the nsigma of the track is less than a certain value, it is accepted for (int iS{0}; iS < nuclei::Species; ++iS) { // The first index here is set to 0 because it is TPC analysis @@ -844,6 +873,15 @@ struct DeuteronInTriggeredEvents { flag |= kITSrof; } + // Part for identifying tracks which are compatible to proton, kaons and pions + bool isCompatibleWithLF{false}; + double nSigmaTpcPion = track.tpcNSigmaPi(); + double nSigmaTpcKaon = track.tpcNSigmaKa(); + double nSigmaTpcProt = track.tpcNSigmaPr(); + + if (cfgRemoveTOFtrack && (std::abs(nSigmaTpcPion) < cfgNsigmaTPCvetoLF || std::abs(nSigmaTpcKaon) < cfgNsigmaTPCvetoLF || std::abs(nSigmaTpcProt) < cfgNsigmaTPCvetoLF)) + isCompatibleWithLF = true; + for (int iS{0}; iS < nuclei::Species; ++iS) { bool selectedTOF{false}; if (std::abs(dcaInfo[1]) > cfgTrackCut.dcaMax->get(iS, 1)) { @@ -870,7 +908,7 @@ struct DeuteronInTriggeredEvents { nuclei::hNsigma[iPID][iS][iC]->Fill(fvector.pt(), nSigma[iPID][iS]); nuclei::hNsigmaEta[iPID][iS][iC]->Fill(fvector.eta(), fvector.pt(), nSigma[iPID][iS]); } - if (iPID && std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutTOFhists) { // Apply nsigmaTPC cut on TOF tracks + if (iPID && std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutTOFhists && !isCompatibleWithLF) { // Apply nsigmaTPC cut on TOF tracks nuclei::hTOFmass[iS][iC]->Fill(fvector.pt(), tofMasses[iS]); nuclei::hTOFmassEta[iS][iC]->Fill(fvector.eta(), fvector.pt(), tofMasses[iS]); } @@ -994,7 +1032,7 @@ struct DeuteronInTriggeredEvents { std::vector isReconstructed(particlesMC.size(), false); for (auto& c : nuclei::candidates) { // o2-linter: disable=const-ref-in-for-loop (candidate is modified in loop) auto label = tracks.iteratorAt(c.globalIndex); - if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + if (label.mcParticleId() < 0 || label.mcParticleId() >= particlesMC.size()) { continue; } auto particle = particlesMC.iteratorAt(label.mcParticleId()); @@ -1002,7 +1040,7 @@ struct DeuteronInTriggeredEvents { for (int iS{0}; iS < nuclei::Species; ++iS) { if (std::abs(particle.pdgCode()) == nuclei::Codes[iS]) { if (c.fillTree && !storeIt) { - nuclei::hMomRes[iS][particle.pdgCode() < 0]->Fill(1., std::abs(c.pt * nuclei::Charges[iS]), 1. - std::abs(c.pt * nuclei::Charges[iS]) / particle.pt()); + nuclei::hMomRes[iS][particle.pdgCode() < 0]->Fill(std::abs(c.pt * nuclei::Charges[iS]), 1. - std::abs(c.pt * nuclei::Charges[iS]) / particle.pt()); storeIt = cfgTreeConfig->get(iS, 0u) || cfgTreeConfig->get(iS, 1u); /// store only the particles of interest } auto coll = collisions.iteratorAt(c.collTrackIndex); @@ -1071,7 +1109,7 @@ struct DeuteronInTriggeredEvents { float motherDecRadius = -1; if (particle.isPhysicalPrimary()) { flags |= kIsPhysicalPrimary; - nuclei::hGenNuclei[iS][particle.pdgCode() < 0]->Fill(1., particle.pt()); + nuclei::hGenNuclei[iS][particle.pdgCode() < 0]->Fill(particle.pt()); // antinuclei from B hadrons are classified as physical primaries if (particle.has_mothers()) { for (const auto& motherparticle : particle.mothers_as()) { @@ -1142,7 +1180,7 @@ struct DeuteronInTriggeredEvents { if (c.fillTree) { auto label = tracks.iteratorAt(c.globalIndex); - if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + if (label.mcParticleId() < 0 || label.mcParticleId() >= particlesMC.size()) { continue; } From 762029b4fda0fc8f4874281cc6bd9b61d5b65f4d Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 6 Nov 2026 10:07:45 +0100 Subject: [PATCH 587/648] [ALICE3] Remove unused FairRoot link from alice3-lutmaker (#18612) The task no longer uses anything from FairRoot: the link was needed for the MCUtils.h include, which was removed in #15379. O2 is about to stop providing the FairRoot targets (AliceO2Group/AliceO2#16054), so this link would break the build. --- ALICE3/Tasks/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index e86859ec374..3c05a20d372 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -21,7 +21,7 @@ o2physics_add_dpl_workflow(alice3-qa-singleparticle o2physics_add_dpl_workflow(alice3-lutmaker SOURCES alice3-lutmaker.cxx - PUBLIC_LINK_LIBRARIES O2::Framework FairRoot::Base O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-pid-separation-power From 1a03cba7db4eabdec35a7c6b00c54bc32fa92087 Mon Sep 17 00:00:00 2001 From: mdaniel-14 Date: Fri, 6 Nov 2026 11:45:31 +0200 Subject: [PATCH 588/648] [PWGCF] Added technical event cuts and QA histograms (#18615) --- .../Tasks/multiparticleCorrelationsDm.cxx | 234 ++++++++++++++++-- 1 file changed, 212 insertions(+), 22 deletions(-) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx index e7c0c6a2753..587248abc75 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx @@ -15,6 +15,7 @@ // O2: +#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -39,10 +40,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -129,8 +132,17 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; Configurable cfFillQA{"cfFillQA", false, "book and fill QA histograms (only for development and validation)"}; // switch for QA histograms + + // Define cuts: Configurable cfUseVertexZCut{"cfUseVertexZCut", true, "apply the vertex Z event cut"}; Configurable cfUsePtCut{"cfUsePtCut", true, "apply the pt particle cut"}; + Configurable cfUseTimeRange{"cfUseTimeRange", true, "rejects events with overlapping collisions"}; + Configurable cfUseRof{"cfUseRof", true, "removes events containing multiple collisions in the same readout frame"}; + Configurable cfUsePileUp{"cfUsePileUp", true, "apply pile up cuts"}; + Configurable cfUseITSTPC{"cfUseITSTPC", true, "use reconstructed from the ITS (Inner Tracking System) and TPC (Time Porjection Chamber) tracks"}; + Configurable cfUseITSLayers{"cfUseITSLayers", true, "use the ITS layers cut"}; + Configurable cfUseZFT0vsPV{"cfUseZFT0vsPV", true, "use this cut"}; + Configurable cfUsePrevRof{"cfUsePrevRof", true, " use this cut"}; Configurable cfCentralityEstimator{"cfCentralityEstimator", "FT0C", "centrality estimator: FT0C, FT0M, FT0A"}; // centrality estimator with valid values: FT0C (default) Configurable cfReferenceMultiplicityEstimator{"cfReferenceMultiplicityEstimator", "FT0C", "reference multiplicity estimator: TPC, FT0C, FT0M"}; // ref. multiplicity estimator with valid values: TPC (default) FT0C Ft0M @@ -147,14 +159,19 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t Configurable> cfVertexXBins{"cfVertexXBins", {200, -1., 1.}, "nvxBins, vxMin, vxMax"}; Configurable> cfVertexYBins{"cfVertexYBins", {200, -1, 1.}, "nvyBins, vyMin, vyMax"}; Configurable> cfVertexZBins{"cfVertexZBins", {300, -15., 15.}, "nvzBins, vzMin, vzMax"}; + Configurable> cfNContribBins{"cfNContribBins", {500, 0., 5000.}, "nContribBins, contribMin, contribMax"}; Configurable> cfVertexZ{"cfVertexZ", {-10., 10.}, "{min, max} [cm]"}; Configurable> cfPt{"cfPt", {0.2, 5.0}, "{min, max} [GeV]"}; + Configurable cfUseEtaCut{"cfUseEtaCut", true, "apply the eta particle cut (pseudorapidity"}; + Configurable> cfEta{"cfEta", {-0.8, 0.8}, "eta range: {min, max}, with convention: min <= eta < max"}; // for weights (efficiency corrections) - Configurable cfUseWeights{"cfUseWeights", false, "load weights from the file in cfFileWithWeights"}; + Configurable cfUsePhiWeights{"cfUsePhiWeights", false, "use phi weights from the file in cfFileWithWeights"}; + Configurable cfUsePtWeights{"cfUsePtWeights", false, "use pt weights from the file in cfFileWithWeights"}; + Configurable cfUseEtaWeights{"cfUseEtaWeights", false, "use eta weights from the file in cfFileWithWeights"}; Configurable cfFileWithWeights{"cfFileWithWeights", "", "path to external ROOT file with weights; on Hyperloop only CCDB paths (/alice-ccdb.cern.ch/...) work"}; - Configurable cfVerbose{"cfVerbose", false, "print \run number, centrality and vertex for each collision"}; + Configurable cfVerbose{"cfVerbose", false, "print run number, centrality and vertex for each collision"}; // *) Define and initialize all data members to be called in the main process* functions: // **) Task configuration: @@ -164,6 +181,13 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t bool fFillQA = false; // book and fill QA histos bool fUseVertexZCut = true; bool fUsePtCut = true; + bool fUseTimeRange = true; + bool fUseRof = true; + bool fUsePileUp = true; + bool fUseITSTPC = true; + bool fUseITSLayers = true; + bool fUseZFT0vsPV = true; + bool fUsePrevRof = true; int fCentralityEstimator = ECentFT0C; int fReferenceMultiplicityEstimator = ERefMultTPC; @@ -171,9 +195,13 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t float fSigmaInel = 771.; // sigma inel [fm^2] std::array fVertexZ = {-10., 10.}; // {min, max} [cm] std::array fPt = {0.2, 5.0}; // {min, max} [GeV] - bool fWeightsLoaded = false; // weights are loaded once, at the first collision - bool fUseWeights = false; // load weights from external file - bool fVerbose = false; // print the values / collision + bool fUseEtaCut = true; + std::array fEta = {-0.8, 0.8}; // {min, max} + int fCurrentRunNumber = -1; // run number of the weights currently in use + bool fUsePhiWeights = false; // load phi weights + bool fUsePtWeights = false; // load pt weights + bool fUseEtaWeights = false; // load eta weights + bool fVerbose = false; // print the values / collision } tc; // you have to prepend "tc." for all objects name in this group later in the code @@ -197,11 +225,20 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t struct QAHistograms { TList* fQAHistogramsList = nullptr; - TH2F* fHistCentralityRecVsSim = nullptr; // x = centrality sim (from impact parameter), y = centrality rec - } qa; // you have to prepend "qa." for all objects name in this group later in the code + TH2F* fHistCentralityRecVsSim = nullptr; // x = centrality sim (from impact parameter), y = centrality rec + std::array fHistRefMultVsNContrib{}; // x = reference multiplicity, y = number of vertex contributors [before, after] + } qa; // you have to prepend "qa." for all objects name in this group later in the code + + // **) Correlations: + struct Correlations { + TList* fCorrelationsList = nullptr; + TProfile* fProfTwo = nullptr; // <<2>> = v2{2}^2 vs. centrality (rec) + } cc; // **) Histogram with weights from an external file - TH1F* histWeights = nullptr; + TH1F* histPhiWeights = nullptr; // phi weights for the current run + TH1F* histPtWeights = nullptr; // pt weights for the current run + TH1F* histEtaWeights = nullptr; // eta weights for the current run // *) Define all member functions to be called in the main process* functions: @@ -291,8 +328,7 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t return nullptr; } // TObject* getObjectFromList(...) - - TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber) + TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* histName) { // Last update: 20261018 @@ -427,7 +463,7 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t // listWithRuns->ls(); // get pointer to "hist" from "listWithRuns" - hist = dynamic_cast(getObjectFromList(listWithRuns, "histWithEfficiencyCorrections_1")); + hist = dynamic_cast(getObjectFromList(listWithRuns, histName)); if (!hist) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -443,6 +479,36 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t } // end of TH1F* getHistogramWithWeights(...) + // load one kind of weights for this run (or reuse it if already loaded), and save it in the output + TH1F* loadWeights(const char* nameInFile, const char* nameInOutput, int runNumber) + { + TString name = Form("%s_%d", nameInOutput, runNumber); + TH1F* hist = dynamic_cast(fBaseList->FindObject(name.Data())); + if (!hist) { + hist = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", runNumber), nameInFile); + hist->SetName(name.Data()); // e.g. histPhiWeights_544116 + hist->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path + fBaseList->Add(hist); // saved in AnalysisResults.root + } + return hist; + } + + // weight of one particle: product of all enabled weights, 1 if none is enabled + float getParticleWeight(float phi, float pt, float eta) + { + float weight = 1.; + if (tc.fUsePhiWeights && histPhiWeights) { + weight *= histPhiWeights->GetBinContent(histPhiWeights->FindBin(phi)); + } + if (tc.fUsePtWeights && histPtWeights) { + weight *= histPtWeights->GetBinContent(histPtWeights->FindBin(pt)); + } + if (tc.fUseEtaWeights && histEtaWeights) { + weight *= histEtaWeights->GetBinContent(histEtaWeights->FindBin(eta)); + } + return weight; + } + //__ EVENT CUTS __ template bool eventCuts(T1 const& collision) @@ -454,6 +520,55 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t return false; // if at least one event cut is not satisfied } } + // *) No collision in time range cut + if (tc.fUseTimeRange) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No collision in the same readout frame cut + if (tc.fUseRof) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No same-bunch pile-up cut + if (tc.fUsePileUp) { + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) Vertex from ITS and TPC tracks cut + if (tc.fUseITSTPC) { + if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) All ITS layers good cut + if (tc.fUseITSLayers) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) z vertex from FT0 and from tracks agree cut + if (tc.fUseZFT0vsPV) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; // if at least one event cut is not satisfied + } + } + + // *) No high-multiplicity collision in previous readout frame cut + if (tc.fUsePrevRof) { + if (!collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; // if at least one event cut is not satisfied + } + } + return true; // all event cuts passed } @@ -469,6 +584,13 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t } } + // *) eta cut + if (tc.fUseEtaCut) { + if (!(tc.fEta[0] <= track.eta() && track.eta() < tc.fEta[1])) { + return false; // if at least one particle cut is not satisfied + } + } + return true; // all particle cuts passed } @@ -488,6 +610,11 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t ec.fEventHistograms[EVertexY][ERec][ba]->Fill(collision.posY()); ec.fEventHistograms[EVertexZ][ERec][ba]->Fill(collision.posZ()); + // QA: reference multiplicity vs. number of contributors + if (tc.fFillQA) { + qa.fHistRefMultVsNContrib[ba]->Fill(getReferenceMultiplicity(collision), collision.numContrib()); + } + if constexpr (rs == ERecAndSim) { if (!collision.has_mcCollision()) { LOGF(debug, " No MC collision for this collision, skip simulated part..."); @@ -529,14 +656,19 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t return; } - // Load the weights once, at the first collision, because the run number is only known here - if (tc.fUseWeights && !tc.fWeightsLoaded) { - histWeights = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", collision.bc().runNumber())); - histWeights->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path - // value takes the text as std::string and c_str makes it into a constant char* - - fBaseList->Add(histWeights); // so it is saved in AnalysisResults.root - tc.fWeightsLoaded = true; + // weights are run-specific: load them again when the run number changes + int runNumber = collision.bc().runNumber(); + if (runNumber != tc.fCurrentRunNumber) { + if (tc.fUsePhiWeights) { + histPhiWeights = loadWeights("histWithEfficiencyCorrections_1", "histPhiWeights", runNumber); + } + if (tc.fUsePtWeights) { + histPtWeights = loadWeights("histWithEfficiencyCorrections_2", "histPtWeights", runNumber); + } + if (tc.fUseEtaWeights) { + histEtaWeights = loadWeights("histWithEfficiencyCorrections_3", "histEtaWeights", runNumber); + } + tc.fCurrentRunNumber = runNumber; } // Print info for each collision, only if switched on in the config: @@ -555,6 +687,9 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t } // Main loop over particles: + // Q-vector of harmonic 2 and sums of weights, for <2> of this event + double q2Re = 0., q2Im = 0., sumW = 0., sumW2 = 0.; + auto track = tracks.iteratorAt(0); // set the type and scope from one instance for (int64_t i = 0; i < tracks.size(); i++) { @@ -594,6 +729,13 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t pc.fParticleHistograms[EPt][ERec][EAfter]->Fill(track.pt()); pc.fParticleHistograms[EPhi][ERec][EAfter]->Fill(track.phi()); + // add this particle to the Q-vector + const double w = getParticleWeight(track.phi(), track.pt(), track.eta()); + q2Re += w * std::cos(2. * track.phi()); + q2Im += w * std::sin(2. * track.phi()); + sumW += w; + sumW2 += w * w; + if constexpr (rs == ERecAndSim) { if (!track.has_mcParticle()) { continue; @@ -610,6 +752,15 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t } // end of for (int64_t i = 0; i < tracks.size(); i++) { // Fill event histograms after event and particle cuts + // <2> of this event, filled vs. centrality with event weight sumW^2 - sumW2 (= M(M-1) for unit weights) + if constexpr (rs == ERec || rs == ERecAndSim) { + const double denominator = sumW * sumW - sumW2; + if (denominator > 0.) { + const double two = (q2Re * q2Re + q2Im * q2Im - sumW2) / denominator; + cc.fProfTwo->Fill(getCentrality(collision), two, denominator); + } + } + fillEventHistograms(collision, tracks, EAfter); } // end of template void steer(T1 const& collision, T2 const& tracks) { @@ -631,9 +782,11 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t tc.fVerbose = cfVerbose; tc.fSigmaInel = cfSigmaInel; - tc.fUseWeights = cfUseWeights; - if (tc.fUseWeights && cfFileWithWeights.value.empty()) { - LOGF(fatal, "cfUseWeights is true, but cfFileWithWeights is empty"); + tc.fUsePhiWeights = cfUsePhiWeights; + tc.fUsePtWeights = cfUsePtWeights; + tc.fUseEtaWeights = cfUseEtaWeights; + if ((tc.fUsePhiWeights || tc.fUsePtWeights || tc.fUseEtaWeights) && cfFileWithWeights.value.empty()) { + LOGF(fatal, "weights are switched on, but cfFileWithWeights is empty"); } // vertex Z cut copy the swtich and range @@ -650,6 +803,22 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t tc.fPt[0] = lPtCut[0]; tc.fPt[1] = lPtCut[1]; + // eta cut copy switch and range + tc.fUseEtaCut = cfUseEtaCut; + std::vector lEtaCut = cfEta.value; + + tc.fEta[0] = lEtaCut[0]; + tc.fEta[1] = lEtaCut[1]; + + // technical event cuts: copy the switches + tc.fUseTimeRange = cfUseTimeRange; + tc.fUseRof = cfUseRof; + tc.fUsePileUp = cfUsePileUp; + tc.fUseITSTPC = cfUseITSTPC; + tc.fUseITSLayers = cfUseITSLayers; + tc.fUseZFT0vsPV = cfUseZFT0vsPV; + tc.fUsePrevRof = cfUsePrevRof; + // translate the centrality estimator string into the enum if (cfCentralityEstimator.value == "FT0C") { tc.fCentralityEstimator = ECentFT0C; @@ -688,6 +857,18 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t ec.fEventHistogramsList->SetOwner(true); fBaseList->Add(ec.fEventHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + // correlations: <<2>> vs. centrality, in the centrality classes of the reference paper + cc.fCorrelationsList = new TList(); + cc.fCorrelationsList->SetName("Correlations"); + cc.fCorrelationsList->SetOwner(true); + fBaseList->Add(cc.fCorrelationsList); + const std::array centBoundaries = {0., 5., 10., 20., 30., 40., 50., 60., 70., 80.}; + cc.fProfTwo = new TProfile("fProfTwo", "<<2>> = v_{2}{2}^{2} vs. centrality", centBoundaries.size() - 1, centBoundaries.data()); + cc.fProfTwo->GetXaxis()->SetTitle("centrality (%)"); + cc.fProfTwo->GetYaxis()->SetTitle("#LT#LT2#GT#GT"); + cc.fProfTwo->Sumw2(); + cc.fCorrelationsList->Add(cc.fProfTwo); + if (tc.fFillQA) { // only if QA switched on qa.fQAHistogramsList = new TList(); qa.fQAHistogramsList->SetName("QA"); @@ -824,6 +1005,15 @@ struct MultiparticleCorrelationsDm { // this name is used in lower-case format t qa.fHistCentralityRecVsSim->GetXaxis()->SetTitle("centrality simulated (%)"); qa.fHistCentralityRecVsSim->GetYaxis()->SetTitle("centrality reconstructed (%)"); qa.fQAHistogramsList->Add(qa.fHistCentralityRecVsSim); + + // reference multiplicity vs. number of contributors, before and after cuts + std::vector lContribBins = cfNContribBins.value; + for (int ba = 0; ba < EBeforeAfter_N; ba++) { + qa.fHistRefMultVsNContrib[ba] = new TH2F(Form("fHistRefMultVsNContrib[%s]", baNames[ba]), Form("reference multiplicity vs. number of contributors (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax, static_cast(lContribBins[0]), lContribBins[1], lContribBins[2]); + qa.fHistRefMultVsNContrib[ba]->GetXaxis()->SetTitle("reference multiplicity"); + qa.fHistRefMultVsNContrib[ba]->GetYaxis()->SetTitle("number of contributors"); + qa.fQAHistogramsList->Add(qa.fHistRefMultVsNContrib[ba]); + } } } // end of void init(InitContext&) { From f928d6209fd9782c181a12881891036bbee5c3d9 Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Fri, 6 Nov 2026 10:54:29 +0100 Subject: [PATCH 589/648] [PWGHF] D+-h task: Decouple pT limits in ML cuts and in correl. (#18365) --- .../HFC/Tasks/taskCorrelationDplusHadrons.cxx | 299 ++++++++++-------- 1 file changed, 164 insertions(+), 135 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index 3a058914624..c410c0efd0c 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -46,7 +46,6 @@ #include #include -#include #include #include @@ -103,23 +102,25 @@ struct HfTaskCorrelationDplusHadrons { Configurable fillHistoData{"fillHistoData", true, "Flag for filling histograms in data processes"}; Configurable fillHistoMcRec{"fillHistoMcRec", true, "Flag for filling histograms in MC Rec processes"}; Configurable fillHistoMcGen{"fillHistoMcGen", true, "Flag for filling histograms in MC Gen processes"}; - Configurable fillHistoMcEff{"fillHistoMcEff", true, "Flag for filling histograms in efficiency processes"}; Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying efficiency weights"}; Configurable loadAccXEffFromCCDB{"loadAccXEffFromCCDB", false, "Flag for loading efficiency distributions from CCDB"}; Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for D+"}; // 7 corresponds to topo+PID cuts Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; + // Machine Learning options and selections Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; - Configurable> mlScorePromptOrNonPromptMin{"mlScorePromptOrNonPromptMin", {0.5, 0.5, 0.5, 0.5}, "Minimum Machine learning scores for prompt or Feed-down"}; - Configurable> mlScorePromptOrNonPromptMax{"mlScorePromptOrNonPromptMax", {1.0, 1.0, 1.0, 1.0}, "Maximum Machine learning scores for prompt or Feed-down"}; - Configurable> mlScoreBkg{"mlScoreBkg", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for bkg"}; - // pT ranges for correlation plots: the default values are those embedded in hf_cuts_dplus_to_pi_k_pi (i.e. the mass pT bins), but can be redefined via json files - Configurable> binsPtCorrelations{"binsPtCorrelations", std::vector{ptBinsCorrelationsVec}, "pT bin limits for correlation plots"}; + // default ML cuts defined in the default pT bins of the ML cuts (binsPtEfficiencyD, since binsPtMl is empty by default) + Configurable> mlScorePromptOrNonPromptMin{"mlScorePromptOrNonPromptMin", std::vector(npTBinsEfficiency, 0.), "Minimum Machine learning scores for prompt or Feed-down, one per pT bin of the ML cuts"}; + Configurable> mlScorePromptOrNonPromptMax{"mlScorePromptOrNonPromptMax", std::vector(npTBinsEfficiency, 1.0), "Maximum Machine learning scores for prompt or Feed-down, one per pT bin of the ML cuts"}; + Configurable> mlScoreBkg{"mlScoreBkg", std::vector(npTBinsEfficiency, 0.5), "Machine learning scores for bkg, one per pT bin of the ML cuts"}; + Configurable> binsPtMl{"binsPtMl", std::vector{}, "pT bin limits for the ML cuts, defining also the accepted D+ pT range (if empty, binsPtEfficiencyD is used)"}; + // pT ranges of the signal and sideband regions (the pT axis of all the histograms is binsPtEfficiencyD), can be redefined via json files + Configurable> binsPtCorrelations{"binsPtCorrelations", std::vector{ptBinsCorrelationsVec}, "pT bin limits for the signal and sideband regions"}; Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle efficiency"}; - Configurable> binsPtEfficiencyD{"binsPtEfficiencyD", std::vector{o2::analysis::hf_cuts_dplus_to_pi_k_pi::vecBinsPt}, "pT bin limits for efficiency"}; + Configurable> binsPtEfficiencyD{"binsPtEfficiencyD", std::vector{o2::analysis::hf_cuts_dplus_to_pi_k_pi::vecBinsPt}, "pT bin limits for efficiency and for the D+ pT axis of all the histograms"}; Configurable> binsPtEfficiencyHad{"binsPtEfficiencyHad", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for associated particle efficiency"}; - Configurable> efficiencyD{"efficiencyD", {1., 1., 1., 1., 1., 1.}, "efficiency values for prompt D+ meson"}; - Configurable> efficiencyFdD{"efficiencyFdD", {1., 1., 1., 1., 1., 1.}, "efficiency values for beauty feed-down D+ meson"}; - Configurable> efficiencyHad{"efficiencyHad", {1., 1., 1., 1., 1., 1.}, "efficiency values for associated particles"}; + Configurable> efficiencyD{"efficiencyD", std::vector(npTBinsEfficiency, 1.f), "efficiency values for prompt D+ meson, one per pT bin of binsPtEfficiencyD"}; + Configurable> efficiencyFdD{"efficiencyFdD", std::vector(npTBinsEfficiency, 1.f), "efficiency values for beauty feed-down D+ meson, one per pT bin of binsPtEfficiencyD"}; + Configurable> efficiencyHad{"efficiencyHad", {1., 1., 1., 1., 1.}, "efficiency values for associated particles, one per pT bin of binsPtEfficiencyHad"}; // signal and sideband region edges, to be defined via json file (initialised to empty) Configurable> signalRegionInner{"signalRegionInner", std::vector{signalRegionInnerVec}, "Inner values of signal region vs pT"}; Configurable> signalRegionOuter{"signalRegionOuter", std::vector{signalRegionOuterVec}, "Outer values of signal region vs pT"}; @@ -137,8 +138,7 @@ struct HfTaskCorrelationDplusHadrons { Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; Configurable ptDaughterMin{"ptDaughterMin", 0.1, "min. daughter pT"}; - Configurable activateQA{"activateQA", false, "Flag to enable debug histogram"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; Configurable storeMassAxis{"storeMassAxis", false, "Store Mass axis in THnSparse"}; Configurable storeBMotherInfo{"storeBMotherInfo", false, "Store generated pT(B mother) and phi(D+) - phi(B mother) of non-prompt D+ in MC Rec histograms"}; @@ -155,8 +155,7 @@ struct HfTaskCorrelationDplusHadrons { std::shared_ptr mEfficiencyPrompt = nullptr; std::shared_ptr mEfficiencyFD = nullptr; std::shared_ptr mEfficiencyAssociated = nullptr; - std::shared_ptr effD = nullptr; - int idxBdtScore = 1; // Index BDTScore 1 for Prompt and 2 for FD Analysis + std::vector binsPtMlD; // pT bin limits for the ML cuts (binsPtMl, or binsPtEfficiencyD if binsPtMl is empty) enum CandidateStep { kCandidateStepMcGenAll = 0, kCandidateStepMcGenDplusToPiKPi, @@ -190,7 +189,6 @@ struct HfTaskCorrelationDplusHadrons { { // Axis definition AxisSpec axisMassD = {binsMassD, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{D} (GeV/#it{c})"}; AxisSpec axisPtD = {std::vector(binsPtEfficiencyD), "#it{p}_{T}^{D} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; @@ -209,17 +207,17 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hMassDplusVsPtWoEff", "D+ candidates massVsPt without efficiency", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); if (fillHistoData) { if (storeMassAxis) { - registry.add("hCorrel2DVsPtVsMass", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}, {axisMassD}}}); + registry.add("hCorrel2DVsPtVsMass", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}, {axisMassD}}}); registry.get(HIST("hCorrel2DVsPtVsMass"))->Sumw2(); } else { registry.add("hDeltaEtaPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebands", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hDeltaEtaPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); @@ -241,12 +239,12 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hDeltaPhiPtIntSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hDeltaEtaPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hCorrel2DPtIntSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtBkgMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtBkgMcRec", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaPhiPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebandsMcRec", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtPhysicalPrimaryMcRec", stringDHadron + "(only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisDplusPrompt}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebandLeftMcRec", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebandLeftMcRec", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaPhi}}); @@ -255,7 +253,7 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hDeltaPhiPtIntSidebandRightMcRec", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DVsPtSidebandRightMcRec", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionPromptDplusPromptHadronMcRec", stringDHadron + "signal region PromptD - Prompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec", stringDHadron + " signal region PromptD - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec", stringDHadron + " signal region NonPrompt - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandsMcRec"))->Sumw2(); @@ -263,7 +261,6 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtBkgMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandLeftMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandRightMcRec"))->Sumw2(); - registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"))->Sumw2(); // beauty-mother information of non-prompt D+ (generated B, reconstructed D+) @@ -271,7 +268,7 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hMassNonPromptDplusVsPtVsPtBMother", "D+ non prompt candidates mass Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisPtBMother}}}); registry.add("hDeltaPhiBMotherVsPtVsPtBMother", "D+ non prompt candidates #varphi^{D}-#varphi^{B} Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisDeltaPhiBMother}, {axisPtD}, {axisPtBMother}}}); registry.add("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother", stringDHadron + "non-prompt D (only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); - registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother", stringDHadron + "signal region NonPromptD - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother", stringDHadron + "NonPrompt D - Non Prompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); registry.get(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"))->Sumw2(); @@ -281,8 +278,7 @@ struct HfTaskCorrelationDplusHadrons { if (fillHistoMcGen) { registry.add("hDeltaEtaPtIntMcGen", stringMCParticles + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntMcGen", stringMCParticles + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hCorrel2DPtIntMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + stringPtD + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtMcGen", stringMCParticles + stringDeltaPhi + stringDeltaEta + stringPtD + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenPrompt", stringDHadron + " Prompt MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenPromptDPromptHadron", stringDHadron + "prompt D prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtMcGenNonPromptDNonPromptHadron", stringDHadron + " non prompt D non prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); @@ -338,28 +334,107 @@ struct HfTaskCorrelationDplusHadrons { } LOGF(info, "Loaded associated efficiency histogram from %s", associatedEffCcdbPath.value.c_str()); } - effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; - idxBdtScore = isPromptAnalysis ? 1 : 2; - - if (activateQA) { - const int regionLimits = 6; - std::string labels[regionLimits] = {"SigReg Left", "SigReg Right", "Left SB Low", "Left SB Up", "Right SB Low", "Right SB Up"}; - static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); - for (int iLim = 0; iLim < regionLimits; iLim++) { - hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); - } - for (size_t iPtD = 0; iPtD < binsPtCorrelations->size() - 1; iPtD++) { - hSigSidebandLimits->SetBinContent(1, iPtD + 1, signalRegionInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(2, iPtD + 1, signalRegionOuter->at(iPtD)); - hSigSidebandLimits->SetBinContent(3, iPtD + 1, sidebandLeftOuter->at(iPtD)); - hSigSidebandLimits->SetBinContent(4, iPtD + 1, sidebandLeftInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(5, iPtD + 1, sidebandRightInner->at(iPtD)); - hSigSidebandLimits->SetBinContent(6, iPtD + 1, sidebandRightOuter->at(iPtD)); + + constexpr std::size_t NBinLimitsMin{2u}; // at least one pT bin + + // efficiency values from configurables: one entry per pT bin + if ((applyEfficiency != 0) && !loadAccXEffFromCCDB) { + if (binsPtEfficiencyD->size() < NBinLimitsMin || binsPtEfficiencyHad->size() < NBinLimitsMin) { + LOGF(fatal, "binsPtEfficiencyD and binsPtEfficiencyHad must contain at least two pT bin limits"); + } + std::size_t const nBinsPtEffD = binsPtEfficiencyD->size() - 1; + if (efficiencyD->size() != nBinsPtEffD || efficiencyFdD->size() != nBinsPtEffD) { + LOGF(fatal, "efficiencyD (%zu entries) and efficiencyFdD (%zu entries) must have one entry per pT bin of binsPtEfficiencyD (%zu bins)", efficiencyD->size(), efficiencyFdD->size(), nBinsPtEffD); } + std::size_t const nBinsPtEffHad = binsPtEfficiencyHad->size() - 1; + if (efficiencyHad->size() != nBinsPtEffHad) { + LOGF(fatal, "efficiencyHad (%zu entries) must have one entry per pT bin of binsPtEfficiencyHad (%zu bins)", efficiencyHad->size(), nBinsPtEffHad); + } + } + + // pT bins of the ML cuts, decoupled from the efficiency binning + binsPtMlD = binsPtMl->empty() ? binsPtEfficiencyD.value : binsPtMl.value; + if (binsPtMlD.size() < NBinLimitsMin) { + LOGF(fatal, "binsPtMl (or binsPtEfficiencyD if binsPtMl is empty) must contain at least two pT bin limits"); + } + std::size_t const nBinsPtMl = binsPtMlD.size() - 1; + if (mlScorePromptOrNonPromptMin->size() != nBinsPtMl || mlScorePromptOrNonPromptMax->size() != nBinsPtMl || mlScoreBkg->size() != nBinsPtMl) { + LOGF(fatal, "mlScorePromptOrNonPromptMin, mlScorePromptOrNonPromptMax and mlScoreBkg must have one entry per pT bin of the ML cuts (%zu bins)", nBinsPtMl); } } + /// ML selection of the D+ candidate, in the pT bins of the ML cuts + /// \param ptD is the D+ transverse momentum + /// \param bdtScoreBkg is the BDT score for the background hypothesis (outputMl[0]) + /// \param bdtScorePrompt is the BDT score for the prompt hypothesis (outputMl[1]) + /// \param bdtScoreNonPrompt is the BDT score for the non-prompt hypothesis (outputMl[2]) + /// \return true if the candidate is inside the pT range of the ML cuts and passes them + bool isSelectedMl(float ptD, float bdtScoreBkg, float bdtScorePrompt, float bdtScoreNonPrompt) + { + int const mlBinD = o2::analysis::findBin(&binsPtMlD, ptD); + if (mlBinD < 0) { + return false; + } + float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; + return bdtScorePromptOrNonPrompt >= mlScorePromptOrNonPromptMin->at(mlBinD) && bdtScorePromptOrNonPrompt <= mlScorePromptOrNonPromptMax->at(mlBinD) && bdtScoreBkg <= mlScoreBkg->at(mlBinD); + } + + /// D+ efficiency, from the CCDB histograms or from the configurable vectors + /// \param ptD is the D+ transverse momentum + /// \param isPrompt selects the prompt (true) or non-prompt (false) efficiency + /// \return the efficiency, or a non-positive value if it is not available for this pT + double getEfficiencyDplus(float ptD, bool isPrompt) + { + if (loadAccXEffFromCCDB) { + auto const& hEfficiency = isPrompt ? mEfficiencyPrompt : mEfficiencyFD; + return hEfficiency->GetBinContent(hEfficiency->FindBin(ptD)); + } + int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); + if (effBinD < 0) { + return -1.; + } + return isPrompt ? efficiencyD->at(effBinD) : efficiencyFdD->at(effBinD); + } + + /// Associated-hadron efficiency, from the CCDB histogram or from the configurable vector + /// \param ptHadron is the associated-hadron transverse momentum + /// \return the efficiency, or a non-positive value if it is not available for this pT + double getEfficiencyHadron(float ptHadron) + { + if (loadAccXEffFromCCDB) { + return mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron)); + } + int const effBinHadron = o2::analysis::findBin(binsPtEfficiencyHad, ptHadron); + if (effBinHadron < 0) { + return -1.; + } + return efficiencyHad->at(effBinHadron); + } + + /// Efficiency weight of the D+ candidate, or of the D+-hadron pair if ptHadron is given + /// \param ptD is the D+ transverse momentum + /// \param isPrompt selects the prompt (true) or non-prompt (false) D+ efficiency + /// \param ptHadron is the associated-hadron transverse momentum (negative for the D+ only) + /// \return 1/efficiency, 1 if the efficiencies are not applied, or -1 if the efficiency is not available + double getEfficiencyWeight(float ptD, bool isPrompt, float ptHadron = -1.f) + { + if (applyEfficiency == 0) { + return 1.; + } + double efficiency = getEfficiencyDplus(ptD, isPrompt); + if (efficiency <= 0.) { + return -1.; + } + if (ptHadron >= 0.f) { + double const efficiencyHadron = getEfficiencyHadron(ptHadron); + if (efficiencyHadron <= 0.) { + return -1.; + } + efficiency *= efficiencyHadron; + } + return 1. / efficiency; + } + void processData(DplusHadronPairFullWithMl const& pairEntries, aod::DplusRecoInfo const& candidates) { for (const auto& candidate : candidates) { @@ -368,23 +443,14 @@ struct HfTaskCorrelationDplusHadrons { float const bdtScorePrompt = candidate.mlScorePrompt(); float const bdtScoreNonPrompt = candidate.mlScoreNonPrompt(); float const bdtScoreBkg = candidate.mlScoreBkg(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { - continue; - } - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { + // reject candidates outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - double efficiencyWeightD = 1.; - if (applyEfficiency != 0) { - efficiencyWeightD = 1. / efficiencyD->at(o2::analysis::findBin(binsPtEfficiencyD, ptD)); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / effD->GetBinContent(effD->FindBin(ptD)); - } + double const efficiencyWeightD = getEfficiencyWeight(ptD, isPromptAnalysis); + if (efficiencyWeightD < 0.) { + continue; // efficiency not available for this pT } registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); @@ -406,32 +472,26 @@ struct HfTaskCorrelationDplusHadrons { int const trackTpcCrossedRows = pairEntry.trackTPCNClsCrossedRows(); int const poolBin = pairEntry.poolBin(); double const massD = pairEntry.mD(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { - continue; - } - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { + // reject entries outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } - double efficiencyWeight = 1.; - if (applyEfficiency != 0) { - efficiencyWeight = 1. / (efficiencyD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (effD->GetBinContent(effD->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } + double const efficiencyWeight = getEfficiencyWeight(ptD, isPromptAnalysis, ptHadron); + if (efficiencyWeight < 0.) { + continue; // efficiency not available for this pT } if (storeMassAxis) { registry.fill(HIST("hCorrel2DVsPtVsMass"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, massD, efficiencyWeight); continue; } + int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); + if (pTBinD < 0) { + continue; // outside the pT range of the signal and sideband regions + } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots if (massD > signalRegionInner->at(pTBinD) && massD < signalRegionOuter->at(pTBinD)) { // in signal region @@ -474,44 +534,27 @@ struct HfTaskCorrelationDplusHadrons { float const bdtScorePrompt = candidate.mlScorePrompt(); float const bdtScoreNonPrompt = candidate.mlScoreNonPrompt(); float const bdtScoreBkg = candidate.mlScoreBkg(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); bool const isDplusPrompt = candidate.isPrompt(); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject candidates outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { - continue; + double const efficiencyWeightD = getEfficiencyWeight(ptD, isDplusPrompt); + if (efficiencyWeightD < 0.) { + continue; // efficiency not available for this pT } - double efficiencyWeightD = 1.; - if (applyEfficiency != 0) { - if (isDplusPrompt) { - efficiencyWeightD = 1. / efficiencyD->at(effBinD); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / mEfficiencyPrompt->GetBinContent(mEfficiencyPrompt->FindBin(ptD)); - } - registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); - registry.fill(HIST("hMassPromptDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); - registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); - } else { - efficiencyWeightD = 1. / efficiencyFdD->at(effBinD); - if (loadAccXEffFromCCDB) { - efficiencyWeightD = 1. / mEfficiencyFD->GetBinContent(mEfficiencyFD->FindBin(ptD)); - } - registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); - registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); - registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); - registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); - if (storeBMotherInfo) { - registry.fill(HIST("hMassNonPromptDplusVsPtVsPtBMother"), massD, ptD, candidate.ptB(), efficiencyWeightD); - registry.fill(HIST("hDeltaPhiBMotherVsPtVsPtBMother"), candidate.deltaPhiBtoD(), ptD, candidate.ptB(), efficiencyWeightD); - } + registry.fill(HIST("hMassDplusVsPt"), massD, ptD, efficiencyWeightD); + registry.fill(HIST("hMassDplusVsPtWoEff"), massD, ptD); + registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); + registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); + if (isDplusPrompt) { + registry.fill(HIST("hMassPromptDplusVsPt"), massD, ptD, efficiencyWeightD); + } else { + registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); + if (storeBMotherInfo) { + registry.fill(HIST("hMassNonPromptDplusVsPtVsPtBMother"), massD, ptD, candidate.ptB(), efficiencyWeightD); + registry.fill(HIST("hDeltaPhiBMotherVsPtVsPtBMother"), candidate.deltaPhiBtoD(), ptD, candidate.ptB(), efficiencyWeightD); } } } @@ -533,35 +576,17 @@ struct HfTaskCorrelationDplusHadrons { bool const isDplusPrompt = pairEntry.isPrompt(); int const originHadron = pairEntry.trackOrigin(); int const poolBin = pairEntry.poolBin(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); - int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); - float const bdtScorePromptOrNonPrompt = isPromptAnalysis ? bdtScorePrompt : bdtScoreNonPrompt; - // reject entries outside pT ranges of interest - if (ptD < binsPtEfficiencyD->front() || ptD > binsPtEfficiencyD->back()) { + // reject entries outside the pT range of the ML cuts or not passing them + if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - - if (bdtScorePromptOrNonPrompt < mlScorePromptOrNonPromptMin->at(effBinD) || bdtScorePromptOrNonPrompt > mlScorePromptOrNonPromptMax->at(effBinD) || bdtScoreBkg > mlScoreBkg->at(effBinD)) { + if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { - continue; - } - double efficiencyWeight = 1.; - - if (applyEfficiency != 0) { - if (isDplusPrompt) { - efficiencyWeight = 1. / (efficiencyD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (mEfficiencyPrompt->GetBinContent(mEfficiencyPrompt->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } - } else { - efficiencyWeight = 1. / (efficiencyFdD->at(effBinD) * efficiencyHad->at(o2::analysis::findBin(binsPtEfficiencyHad, ptHadron))); - if (loadAccXEffFromCCDB) { - efficiencyWeight = 1. / (mEfficiencyFD->GetBinContent(mEfficiencyFD->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); - } - } + double const efficiencyWeight = getEfficiencyWeight(ptD, isDplusPrompt, ptHadron); + if (efficiencyWeight < 0.) { + continue; // efficiency not available for this pT } // fill correlation plots for signal/bagkground correlations @@ -570,8 +595,11 @@ struct HfTaskCorrelationDplusHadrons { } else { registry.fill(HIST("hCorrel2DVsPtBkgMcRec"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } - // reject entries outside pT ranges of interest + int const pTBinD = o2::analysis::findBin(binsPtCorrelations, ptD); + if (pTBinD < 0) { + continue; // outside the pT range of the signal and sideband regions + } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots if (massD > signalRegionInner->at(pTBinD) && massD < signalRegionOuter->at(pTBinD)) { // in signal region @@ -717,7 +745,8 @@ struct HfTaskCorrelationDplusHadrons { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } - if (outputMl[0] > mlScoreBkg->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMl[idxBdtScore] < mlScorePromptOrNonPromptMin->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMl[idxBdtScore] > mlScorePromptOrNonPromptMax->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt()))) { + // same ML selection as in the correlation analysis (outputMl: 0 = bkg, 1 = prompt, 2 = non-prompt) + if (!isSelectedMl(candidate.pt(), outputMl[0], outputMl[1], outputMl[2])) { continue; } auto collision = candidate.template collision_as>(); From fe0adc3dbe9c3cb0484fdbbe2292897f0a222853 Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Fri, 6 Nov 2026 12:55:22 +0100 Subject: [PATCH 590/648] [PWGCF] Fix code-check errors and warnings in femto framework (#18613) --- PWGCF/Femto/Core/collisionTagger.h | 82 +++++++++---------- .../femtoProducerCollisionTagger.cxx | 52 ++++++------ PWGCF/Femto/Tasks/femtoPairD0D0.cxx | 1 + PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx | 1 + .../Femto/Tasks/femtoPairTrackCharmHadron.cxx | 1 + PWGCF/Femto/Tasks/femtoPairTrackKink.cxx | 1 + PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx | 1 + .../femtoPairTrackTwoPhotonResonance.cxx | 1 + .../Tasks/femtoPairTrackTwoTrackResonance.cxx | 1 + PWGCF/Femto/Tasks/femtoPairTrackV0.cxx | 1 + .../Tasks/femtoPairV0TwoTrackResonance.cxx | 1 + PWGCF/Femto/Tasks/femtoPairV0V0.cxx | 1 + .../Tasks/femtoTripletTrackTrackCascade.cxx | 1 + .../Tasks/femtoTripletTrackTrackTrack.cxx | 1 + .../Femto/Tasks/femtoTripletTrackTrackV0.cxx | 1 + 15 files changed, 80 insertions(+), 67 deletions(-) diff --git a/PWGCF/Femto/Core/collisionTagger.h b/PWGCF/Femto/Core/collisionTagger.h index 9d2bf28a0c5..931be1a0f02 100644 --- a/PWGCF/Femto/Core/collisionTagger.h +++ b/PWGCF/Femto/Core/collisionTagger.h @@ -467,7 +467,7 @@ inline bool hasEnabledOption(o2::framework::DeviceSpec const& device, std::strin inline bool hasAnyEnabledOptionStartingWith(o2::framework::DeviceSpec const& device, std::string const& prefix) { for (auto const& option : device.options) { - if (option.name.rfind(prefix, 0) == 0 && option.defaultValue.get()) { + if (option.name.starts_with(prefix) && option.defaultValue.get()) { return true; } } @@ -482,7 +482,7 @@ bool trackPasses(T const& track, TrackSelectionValues const& sel) return false; } float absPt = std::fabs(sel.chargeAbs * track.signedPt()); - if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { return false; } if (!(track.eta() > sel.etaMin && track.eta() < sel.etaMax)) { @@ -512,7 +512,7 @@ bool lambdaPasses(T const& v0, V0SelectionValues const& sel) return false; } float absPt = std::fabs(v0.signedPt()); - if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { return false; } if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { @@ -536,7 +536,7 @@ bool kinkPcmPasses(T const& kink, KinkPcmSelectionValues const& sel) return false; } float absPt = std::fabs(kink.signedPt()); - if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { return false; } if (!(kink.eta() > sel.etaMin && kink.eta() < sel.etaMax)) { @@ -606,7 +606,7 @@ bool kstar0Passes(T const& kstar0, Kstar0SelectionValues const& sel) return false; } float absPt = std::fabs(kstar0.signedPt()); - if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + if (absPt <= sel.ptMin || absPt >= sel.ptMax) { return false; } if (!(kstar0.eta() > sel.etaMin && kstar0.eta() < sel.etaMax)) { @@ -657,7 +657,7 @@ using CollisionTagBitset = std::bitset; // new `passes` function (trackPasses/lambdaPasses/k0shortPasses above are examples), never a new // copy of this loop. template -inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn&& passes, int needed = 1) +inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn const& passes, int needed = 1) { int count = 0; for (auto const& entry : range) { @@ -673,7 +673,7 @@ inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn&& pass // the one shared selection v.sel2). Reusable for any future same-type pair (V0V0, D0D0, ...) -- // just a different `variants`/`range`/`passes` combination, the counting logic doesn't change. template -inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn const& passes) { for (auto const& v : variants) { bool ok1 = hasAtLeast(range, v.sel1, passes, 1); @@ -689,7 +689,7 @@ inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& // Reusable for any future cross-type pair (track-cascade, track-kink, track-D0, ...) -- just a // different `variants`/ranges/passes combination. template -inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1 const& passes1, Range2 const& range2, PassesFn2 const& passes2) { for (auto const& v : variants) { if (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range2, v.sel2, passes2, 1)) { @@ -702,7 +702,7 @@ inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const // followed by producing the tag row -- covers any task type whose process function only ever // needs one same-type-pair check per collision (e.g. processTrackTrack). template -inline void tagAndProduce(Produces& producedTags, std::vector const& variants, Range const& range, PassesFn&& passes) +inline void tagAndProduce(Produces& producedTags, std::vector const& variants, Range const& range, PassesFn const& passes) { CollisionTagBitset tag; tagPairVariants(tag, variants, range, passes); @@ -714,7 +714,7 @@ inline void tagAndProduce(Produces& producedTags, std::vector const& va // distinct passing sel1 plus 1 more passing sel3; otherwise sel1/sel2/sel3 each need their own // independent match. Mirrors TripletTrackTrackTrackBuilder exactly (see TrackTrackTrackVariant). template -inline void tagTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +inline void tagTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn const& passes) { for (auto const& v : variants) { bool ok = false; @@ -737,7 +737,7 @@ inline void tagTripletVariants(CollisionTagBitset& tag, std::vector con // and role 2 each need their own independent match in range1. Mirrors // TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder exactly (see TrackTrackV0Variant). template -inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1 const& passes1, Range2 const& range2, PassesFn2 const& passes2) { for (auto const& v : variants) { bool ok = v.same12 ? hasAtLeast(range1, v.sel1, passes1, 2) @@ -757,7 +757,7 @@ inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector -// below, the std::string construction in discoverTaggedRoles(), etc.). `inline` (not just +// below, the explicit static_cast(...) in discoverTaggedRoles(), etc.). `inline` (not just // `constexpr`) matters too: without it, a namespace-scope variable has internal linkage, so each // translation unit would get its own copy at its own address -- fine for a value, but the same // template instantiation must denote the same entity everywhere it's used. `inline` guarantees @@ -851,7 +851,7 @@ inline std::vector discoverTaggedRoles(o2::framework::RunningWorkflo // assigning it one anyway would just waste a slot out of the hard-capped budget // (MaxTagBits below) for nothing. if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackTrackPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackTrackPair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackV0) != std::string::npos) { // Each species gates independently on its own *MixedEventWithTagging* switch family -- @@ -860,115 +860,115 @@ inline std::vector discoverTaggedRoles(o2::framework::RunningWorkflo // combination), and, same as TrackTrackPair above, same-event/Mc/WithFitVars-without-tagging // variants are irrelevant here since none of them ever consume a tag bit. if (hasAnyEnabledOptionStartingWith(device, "processLambdaMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackLambdaPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackLambdaPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processK0shortMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackK0shortPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackK0shortPair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackCascade) != std::string::npos) { // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if // both are enabled), but gating independently costs nothing and needs no special-casing // here. if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackXiPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackXiPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackOmegaPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackOmegaPair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackKink) != std::string::npos) { if (hasAnyEnabledOptionStartingWith(device, "processSigmaMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackSigmaPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackSigmaPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackSigmaPlusPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackSigmaPlusPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusPcmMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackSigmaPlusPcmPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackSigmaPlusPcmPair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackCharmHadron) != std::string::npos) { if (hasAnyEnabledOptionStartingWith(device, "processD0MixedEventWithTagging")) { - roles.push_back({device.name, role::TrackD0Pair, counter++}); + roles.push_back({device.name, static_cast(role::TrackD0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processLcMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackLcPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackLcPair), counter++}); } } else if (device.name.find(devicename::FemtoPairV0V0) != std::string::npos) { // Lambda-lambda, k0short-k0short and lambda-k0short are mutually exclusive on a single // instance (the task itself fatals if more than one is enabled), but gating independently // costs nothing and needs no special-casing here. if (hasAnyEnabledOptionStartingWith(device, "processLambdaLambdaMixedEventWithTagging")) { - roles.push_back({device.name, role::LambdaLambdaPair, counter++}); + roles.push_back({device.name, static_cast(role::LambdaLambdaPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processK0shortK0shortMixedEventWithTagging")) { - roles.push_back({device.name, role::K0shortK0shortPair, counter++}); + roles.push_back({device.name, static_cast(role::K0shortK0shortPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processLambdaK0shortMixedEventWithTagging")) { - roles.push_back({device.name, role::LambdaK0shortPair, counter++}); + roles.push_back({device.name, static_cast(role::LambdaK0shortPair), counter++}); } } else if (device.name.find(devicename::FemtoPairD0D0) != std::string::npos) { // Only one pair type possible per instance (sign on D0Selection1/2 picks the hypothesis), so // this task's WithTagging process function is just "processMixedEventWithTagging", same local // name as femtoPairTrackTrack.cxx's -- fine since option names are scoped per-device. if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { - roles.push_back({device.name, role::D0D0Pair, counter++}); + roles.push_back({device.name, static_cast(role::D0D0Pair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { // Phi, Kstar0 and Rho0 are mutually exclusive on a single instance (the task itself fatals // if more than one is enabled), but gating independently costs nothing here either. if (hasAnyEnabledOptionStartingWith(device, "processPhiMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackPhiPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackPhiPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processKstar0MixedEventWithTagging")) { - roles.push_back({device.name, role::TrackKstar0Pair, counter++}); + roles.push_back({device.name, static_cast(role::TrackKstar0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processRho0MixedEventWithTagging")) { - roles.push_back({device.name, role::TrackRho0Pair, counter++}); + roles.push_back({device.name, static_cast(role::TrackRho0Pair), counter++}); } } else if (device.name.find(devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { // Pi0 and Eta are mutually exclusive on a single instance, same as above. if (hasAnyEnabledOptionStartingWith(device, "processPi0MixedEventWithTagging")) { - roles.push_back({device.name, role::TrackPi0Pair, counter++}); + roles.push_back({device.name, static_cast(role::TrackPi0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processEtaMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackEtaPair, counter++}); + roles.push_back({device.name, static_cast(role::TrackEtaPair), counter++}); } } else if (device.name.find(devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { // All six v0 x resonance combinations are mutually exclusive on a single instance (the task // itself fatals if more than one is enabled), same as above. if (hasAnyEnabledOptionStartingWith(device, "processLambdaPhiMixedEventWithTagging")) { - roles.push_back({device.name, role::LambdaPhiPair, counter++}); + roles.push_back({device.name, static_cast(role::LambdaPhiPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processLambdaKstar0MixedEventWithTagging")) { - roles.push_back({device.name, role::LambdaKstar0Pair, counter++}); + roles.push_back({device.name, static_cast(role::LambdaKstar0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processLambdaRho0MixedEventWithTagging")) { - roles.push_back({device.name, role::LambdaRho0Pair, counter++}); + roles.push_back({device.name, static_cast(role::LambdaRho0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processK0shortPhiMixedEventWithTagging")) { - roles.push_back({device.name, role::K0shortPhiPair, counter++}); + roles.push_back({device.name, static_cast(role::K0shortPhiPair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processK0shortKstar0MixedEventWithTagging")) { - roles.push_back({device.name, role::K0shortKstar0Pair, counter++}); + roles.push_back({device.name, static_cast(role::K0shortKstar0Pair), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processK0shortRho0MixedEventWithTagging")) { - roles.push_back({device.name, role::K0shortRho0Pair, counter++}); + roles.push_back({device.name, static_cast(role::K0shortRho0Pair), counter++}); } } else if (device.name.find(devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackTrackTrackTriplet, counter++}); + roles.push_back({device.name, static_cast(role::TrackTrackTrackTriplet), counter++}); } } else if (device.name.find(devicename::FemtoTripletTrackTrackV0) != std::string::npos) { if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackTrackLambdaTriplet, counter++}); + roles.push_back({device.name, static_cast(role::TrackTrackLambdaTriplet), counter++}); } } else if (device.name.find(devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if both // are enabled), but gating independently costs nothing here either. if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackTrackXiTriplet, counter++}); + roles.push_back({device.name, static_cast(role::TrackTrackXiTriplet), counter++}); } if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { - roles.push_back({device.name, role::TrackTrackOmegaTriplet, counter++}); + roles.push_back({device.name, static_cast(role::TrackTrackOmegaTriplet), counter++}); } } // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the same diff --git a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx index 2fca898ea81..dc618f158d7 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx @@ -155,7 +155,7 @@ struct FemtoProducerCollisionTagger { for (auto const& r : roles) { LOG(info) << " bit " << r.bit << " = " << r.deviceName << " " << r.role; - if (doprocessTrackTrack && r.role == collisiontagger::role::TrackTrackPair && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTrack) != std::string::npos) { + if (doprocessTrackTrack && r.role == static_cast(collisiontagger::role::TrackTrackPair) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTrack) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); collisiontagger::TrackTrackVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); @@ -170,13 +170,13 @@ struct FemtoProducerCollisionTagger { mTrackTrackVariants.push_back(v); } else if (doprocessTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackLambdaPair) { + if (r.role == static_cast(collisiontagger::role::TrackLambdaPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.bit = r.bit; mTrackLambdaVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackK0shortPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackK0shortPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); @@ -185,13 +185,13 @@ struct FemtoProducerCollisionTagger { } } else if (doprocessTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackXiPair) { + if (r.role == static_cast(collisiontagger::role::TrackXiPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); v.bit = r.bit; mTrackXiVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackOmegaPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackOmegaPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); @@ -200,19 +200,19 @@ struct FemtoProducerCollisionTagger { } } else if ((doprocessTrackKink || doprocessTrackSigmaPlusPcm) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackSigmaPair) { + if (r.role == static_cast(collisiontagger::role::TrackSigmaPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); v.bit = r.bit; mTrackSigmaVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackSigmaPlusPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); v.bit = r.bit; mTrackSigmaPlusVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackSigmaPlusPcmPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPcmPair)) { collisiontagger::TrackKinkPcmVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readKinkPcmSelection(device, "SigmaPlusPcmSelection1"); @@ -221,13 +221,13 @@ struct FemtoProducerCollisionTagger { } } else if ((doprocessTrackD0 || doprocessTrackLc) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCharmHadron) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackD0Pair) { + if (r.role == static_cast(collisiontagger::role::TrackD0Pair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "D0Selection1"); v.bit = r.bit; mTrackD0Variants.push_back(v); - } else if (r.role == collisiontagger::role::TrackLcPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackLcPair)) { collisiontagger::TrackV0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "LcSelection1"); @@ -236,7 +236,7 @@ struct FemtoProducerCollisionTagger { } } else if (doprocessV0V0 && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::LambdaLambdaPair) { + if (r.role == static_cast(collisiontagger::role::LambdaLambdaPair)) { collisiontagger::V0V0Variant v; v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); @@ -246,14 +246,14 @@ struct FemtoProducerCollisionTagger { v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); v.bit = r.bit; mLambdaLambdaVariants.push_back(v); - } else if (r.role == collisiontagger::role::K0shortK0shortPair) { + } else if (r.role == static_cast(collisiontagger::role::K0shortK0shortPair)) { collisiontagger::V0V0Variant v; v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); v.bit = r.bit; mK0shortK0shortVariants.push_back(v); - } else if (r.role == collisiontagger::role::LambdaK0shortPair) { + } else if (r.role == static_cast(collisiontagger::role::LambdaK0shortPair)) { collisiontagger::V0V0Variant v; v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); @@ -275,19 +275,19 @@ struct FemtoProducerCollisionTagger { mD0D0Variants.push_back(v); } else if (doprocessTrackTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackPhiPair) { + if (r.role == static_cast(collisiontagger::role::TrackPhiPair)) { collisiontagger::TrackTwoTrackResonanceVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); v.bit = r.bit; mTrackPhiVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackKstar0Pair) { + } else if (r.role == static_cast(collisiontagger::role::TrackKstar0Pair)) { collisiontagger::TrackKstar0Variant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); v.bit = r.bit; mTrackKstar0Variants.push_back(v); - } else if (r.role == collisiontagger::role::TrackRho0Pair) { + } else if (r.role == static_cast(collisiontagger::role::TrackRho0Pair)) { collisiontagger::TrackTwoTrackResonanceVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); @@ -296,13 +296,13 @@ struct FemtoProducerCollisionTagger { } } else if (doprocessTrackTwoPhotonResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::TrackPi0Pair) { + if (r.role == static_cast(collisiontagger::role::TrackPi0Pair)) { collisiontagger::TrackTwoPhotonResonanceVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "Pi0Selection1"); v.bit = r.bit; mTrackPi0Variants.push_back(v); - } else if (r.role == collisiontagger::role::TrackEtaPair) { + } else if (r.role == static_cast(collisiontagger::role::TrackEtaPair)) { collisiontagger::TrackTwoPhotonResonanceVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "EtaSelection1"); @@ -311,37 +311,37 @@ struct FemtoProducerCollisionTagger { } } else if (doprocessV0TwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == collisiontagger::role::LambdaPhiPair) { + if (r.role == static_cast(collisiontagger::role::LambdaPhiPair)) { collisiontagger::V0TwoTrackResonanceVariant v; v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); v.bit = r.bit; mLambdaPhiVariants.push_back(v); - } else if (r.role == collisiontagger::role::LambdaKstar0Pair) { + } else if (r.role == static_cast(collisiontagger::role::LambdaKstar0Pair)) { collisiontagger::V0Kstar0Variant v; v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); v.bit = r.bit; mLambdaKstar0Variants.push_back(v); - } else if (r.role == collisiontagger::role::LambdaRho0Pair) { + } else if (r.role == static_cast(collisiontagger::role::LambdaRho0Pair)) { collisiontagger::V0TwoTrackResonanceVariant v; v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); v.bit = r.bit; mLambdaRho0Variants.push_back(v); - } else if (r.role == collisiontagger::role::K0shortPhiPair) { + } else if (r.role == static_cast(collisiontagger::role::K0shortPhiPair)) { collisiontagger::V0TwoTrackResonanceVariant v; v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); v.bit = r.bit; mK0shortPhiVariants.push_back(v); - } else if (r.role == collisiontagger::role::K0shortKstar0Pair) { + } else if (r.role == static_cast(collisiontagger::role::K0shortKstar0Pair)) { collisiontagger::V0Kstar0Variant v; v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); v.bit = r.bit; mK0shortKstar0Variants.push_back(v); - } else if (r.role == collisiontagger::role::K0shortRho0Pair) { + } else if (r.role == static_cast(collisiontagger::role::K0shortRho0Pair)) { collisiontagger::V0TwoTrackResonanceVariant v; v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); @@ -380,7 +380,7 @@ struct FemtoProducerCollisionTagger { collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); collisiontagger::TrackSelectionValues sel2 = same12 ? sel1 : sel2Raw; - if (r.role == collisiontagger::role::TrackTrackXiTriplet) { + if (r.role == static_cast(collisiontagger::role::TrackTrackXiTriplet)) { collisiontagger::TrackTrackV0Variant v; v.sel1 = sel1; v.sel2 = sel2; @@ -388,7 +388,7 @@ struct FemtoProducerCollisionTagger { v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); v.bit = r.bit; mTrackTrackXiVariants.push_back(v); - } else if (r.role == collisiontagger::role::TrackTrackOmegaTriplet) { + } else if (r.role == static_cast(collisiontagger::role::TrackTrackOmegaTriplet)) { collisiontagger::TrackTrackV0Variant v; v.sel1 = sel1; v.sel2 = sel2; diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 28dada11562..340ace0914e 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index dc27468a21b..290d910e1aa 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 2e0370d99f9..69ec4fecb24 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index 3c1385d945d..4e760f037d4 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/kinkHistManager.h" #include "PWGCF/Femto/Core/modes.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 11379064023..3bd8334ab7c 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx index 24fa1adee36..b2ee1418fd4 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index bb46e8cbc1a..01c9d41b769 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index de4eba27609..d7af5e129dd 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index 6bf172bbb95..b155fabf58b 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index 9d9f7b8698b..4602b00fff4 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index b1ddcf9426c..07a67e9ee76 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -20,6 +20,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 04660e2e342..bbd1bc6e439 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index f2b821e2f26..df29c51daf0 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" From 6d586d0c463f9fab805d67695ac254c31cf022dd Mon Sep 17 00:00:00 2001 From: Lucia Anna Tarasovicova Date: Fri, 6 Nov 2026 14:47:16 +0100 Subject: [PATCH 591/648] [ALICE3] Align ACTSO2 Translator to the new output (#18463) Co-authored-by: Lucia Anna Tarasovicova --- ALICE3/DataModel/tracksAlice3.h | 24 +- ALICE3/TableProducer/OTF/onTheFlyTracker.cxx | 13 +- .../alice3TrackingTranslator.cxx | 303 ++++++++-------- .../TableProducer/alice3strangenessFinder.cxx | 328 ++++++++++++++---- ALICE3/Tasks/alice3Strangeness.cxx | 123 +++++-- 5 files changed, 538 insertions(+), 253 deletions(-) diff --git a/ALICE3/DataModel/tracksAlice3.h b/ALICE3/DataModel/tracksAlice3.h index 327d0587b64..37c53e6e38d 100644 --- a/ALICE3/DataModel/tracksAlice3.h +++ b/ALICE3/DataModel/tracksAlice3.h @@ -26,6 +26,7 @@ namespace o2::aod { namespace track_alice3 { + DECLARE_SOA_COLUMN(IsReconstructed, isReconstructed, bool); //! is reconstructed or not DECLARE_SOA_COLUMN(NSiliconHits, nSiliconHits, int); //! number of silicon hits DECLARE_SOA_COLUMN(NTPCHits, nTPCHits, int); //! number of tpc hits @@ -34,6 +35,11 @@ DECLARE_SOA_COLUMN(TrackType, trackType, int); //! Type of the trac DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! Has track contributed to the PV fit? DECLARE_SOA_COLUMN(TrackIU, trackIU, float); //! Innermost update: radius (cm) of the innermost hit assigned to the track DECLARE_SOA_COLUMN(TrackOU, trackOU, float); //! Outermost update: radius (cm) of the outermost hit assigned to the track +DECLARE_SOA_COLUMN(SecondaryVertexRadius, secondaryVertexRadius, float); //! Radius of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexX, secondaryVertexX, float); //! X position of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexY, secondaryVertexY, float); //! Y position of the secondary vertex associated with the track +DECLARE_SOA_COLUMN(SecondaryVertexZ, secondaryVertexZ, float); //! Z position of the secondary vertex associated with the track + } // namespace track_alice3 DECLARE_SOA_TABLE(TracksAlice3, "AOD", "TRACKSALICE3", track_alice3::IsReconstructed, @@ -45,23 +51,35 @@ DECLARE_SOA_TABLE(TracksAlice3Pdg, "AOD", "TRACKSALICE3PDG", track_alice3::PdgCode); using TrackAlice3Pdg = TracksAlice3Pdg::iterator; -DECLARE_SOA_TABLE(TracksExtraA3, "AOD", "TracksExtraA3", +DECLARE_SOA_TABLE(TracksExtraA3_000, "AOD", "TracksExtraA3", track_alice3::NSiliconHits, track_alice3::NTPCHits, track_alice3::TrackType, track_alice3::IsPVContributor); +DECLARE_SOA_TABLE_VERSIONED(TracksExtraA3_001, "AOD", "TracksExtraA3", 1, + track_alice3::NSiliconHits, + track_alice3::NTPCHits, + track_alice3::TrackType, + track_alice3::IsPVContributor, + track_alice3::SecondaryVertexRadius, + track_alice3::SecondaryVertexX, + track_alice3::SecondaryVertexY, + track_alice3::SecondaryVertexZ); +using TracksExtraA3 = TracksExtraA3_001; using TrackExtraA3 = TracksExtraA3::iterator; - namespace mcparticle_alice3 { + DECLARE_SOA_COLUMN(NHits, nHits, int); //! number of silicon hits DECLARE_SOA_COLUMN(Charge, charge, float); //! particle charge DECLARE_SOA_COLUMN(DecayRadius, decayRadius, float); //! Radius for decayed particle produced by decayer (-1 if not decayed) DECLARE_SOA_BITMAP_COLUMN(DecayerBits, decayerBits, 8); //! Bit mask for particle produced by the OTF decayer +DECLARE_SOA_COLUMN(SecondaryVertexRadius, secondaryVertexRadius, float); //! Radius of the secondary vertex associated with the track } // namespace mcparticle_alice3 DECLARE_SOA_TABLE(MCParticlesExtraA3, "AOD", "MCParticlesExtraA3", mcparticle_alice3::NHits, - mcparticle_alice3::Charge); + mcparticle_alice3::Charge, + mcparticle_alice3::SecondaryVertexRadius); using MCParticleExtraA3 = MCParticlesExtraA3::iterator; DECLARE_SOA_TABLE(OTFParticleExtras, "AOD", "OTFPARTICLEEXTRA", diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index 7e84ec9c223..d958418952d 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -98,7 +98,6 @@ #include #include #include -#include #include #include @@ -259,7 +258,7 @@ struct OnTheFlyTracker { // V0 names for filling histograms static constexpr int NtypesV0 = 3; - static constexpr std::string_view NameV0s[NtypesV0] = {"K0", "Lambda", "AntiLambda"}; + static constexpr std::array NameV0s = {"K0", "Lambda", "AntiLambda"}; // Class to hold the track information for the O2 vertexing class TrackAlice3 : public o2::track::TrackParCov @@ -1473,7 +1472,7 @@ struct OnTheFlyTracker { continue; } for (int indexDetector = 0; indexDetector < mGeoContainer.getNumberOfConfigurations(); indexDetector++) { - std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0].data()); + std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0]); fillHist(path + "hGen", v0DecayRadius2D, mcParticle.pt()); fillHist(path + "hGenNegDaughterFromV0", v0DecayRadius2D, v0DecayProducts[0].Pt()); fillHist(path + "hGenPosDaughterFromV0", v0DecayRadius2D, v0DecayProducts[1].Pt()); @@ -1560,21 +1559,21 @@ struct OnTheFlyTracker { fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 1.0f); for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, mcParticle.pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0]), v0DecayRadius2D, mcParticle.pt()); } } } if (isV0Reco[0]) { for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[0].Pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0]), v0DecayRadius2D, v0DecayProducts[0].Pt()); } } } if (isV0Reco[1]) { for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { if (mcParticle.pdgCode() == V0PDGs[indexV0]) { - fillHist(Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[1].Pt()); + fillHist(Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0]), v0DecayRadius2D, v0DecayProducts[1].Pt()); } } } @@ -1817,7 +1816,7 @@ struct OnTheFlyTracker { trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), trackParCov.getSigma1Pt2()); tableMcTrackLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits, trackParCov.trackType, trackParCov.isPVContributor); + tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits, trackParCov.trackType, trackParCov.isPVContributor, -1, -1, -1, -1); // populate extra tables if required to do so if (populateTracksExtra) { diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index e0e742fc9d0..18f0386323e 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -100,13 +101,8 @@ struct Alice3TrackingTranslator { LOG(info) << "Alice3TrackingTranslator init called"; } -#define SETADDRESS(branchname, branchvar) \ - if (mTree->SetBranchAddress(branchname, &branchvar)) { \ - LOG(fatal) << "Could not set branch address for " << branchname; \ - } - struct FileStruct { - FileStruct(const std::string& filename, const std::string& treename) : mFile(filename.c_str(), "READ") + FileStruct(const std::string& filename, const std::string& treename) : mFile(filename.c_str(), "READ"), mTree(nullptr) { if (mFile.IsZombie()) { LOG(fatal) << "Could not open file '" << filename << "'"; @@ -118,6 +114,13 @@ struct Alice3TrackingTranslator { LOG(fatal) << treename << " tree not found in " << filename; } } + template + void setBranchAddress(const char* branchname, T& branchvar) + { + if (mTree->SetBranchAddress(branchname, &branchvar)) { + LOG(fatal) << "Could not set branch address for " << branchname; + } + } void setEventEntry(Long64_t entry) { if (mTree->GetEntry(entry) < 0) { @@ -126,30 +129,30 @@ struct Alice3TrackingTranslator { } Long64_t getEntries() const { return mTree->GetEntries(); } TFile mFile; - TTree* mTree; + TTree* mTree{nullptr}; }; struct ParticleStruct : public FileStruct { - ParticleStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + ParticleStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { // mTree->Print(); - SETADDRESS("particle_type", m_particle_type); - SETADDRESS("vx", m_vx); - SETADDRESS("vy", m_vy); - SETADDRESS("vz", m_vz); - SETADDRESS("vt", m_vt); - SETADDRESS("px", m_px); - SETADDRESS("py", m_py); - SETADDRESS("pz", m_pz); - SETADDRESS("m", m_m); - SETADDRESS("p", m_p); - SETADDRESS("q", m_q); - SETADDRESS("number_of_hits", m_number_of_hits); - SETADDRESS("vertex_primary", m_vertex_primary); - SETADDRESS("vertex_secondary", m_vertex_secondary); - SETADDRESS("generation", m_generation); - SETADDRESS("sub_particle", m_sub_particle); - SETADDRESS("particle", m_particle); + setBranchAddress("particle_type", m_particle_type); + setBranchAddress("vx", m_vx); + setBranchAddress("vy", m_vy); + setBranchAddress("vz", m_vz); + setBranchAddress("vt", m_vt); + setBranchAddress("px", m_px); + setBranchAddress("py", m_py); + setBranchAddress("pz", m_pz); + setBranchAddress("m", m_m); + setBranchAddress("p", m_p); + setBranchAddress("q", m_q); + setBranchAddress("number_of_hits", m_number_of_hits); + setBranchAddress("vertex_primary", m_vertex_primary); + setBranchAddress("vertex_secondary", m_vertex_secondary); + setBranchAddress("generation", m_generation); + setBranchAddress("sub_particle", m_sub_particle); + setBranchAddress("particle", m_particle); } std::vector* m_particle_type = nullptr; std::vector* m_vx = nullptr; @@ -175,20 +178,20 @@ struct Alice3TrackingTranslator { struct VertexStruct : public FileStruct { VertexStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { - SETADDRESS("vx", m_x); - SETADDRESS("vy", m_y); - SETADDRESS("vz", m_z); - SETADDRESS("vt", m_t); - SETADDRESS("incoming_particles_vertex_primary", m_incoming_particles_vertex_primary); - SETADDRESS("incoming_particles_vertex_secondary", m_incoming_particles_vertex_secondary); - SETADDRESS("incoming_particles_generation", m_incoming_particles_generation); - SETADDRESS("incoming_particles_sub_particle", m_incoming_particles_sub_particle); - SETADDRESS("incoming_particles_particle", m_incoming_particles_particle); - SETADDRESS("outgoing_particles_vertex_primary", m_outgoing_particles_vertex_primary); - SETADDRESS("outgoing_particles_vertex_secondary", m_outgoing_particles_vertex_secondary); - SETADDRESS("outgoing_particles_generation", m_outgoing_particles_generation); - SETADDRESS("outgoing_particles_sub_particle", m_outgoing_particles_sub_particle); - SETADDRESS("outgoing_particles_particle", m_outgoing_particles_particle); + setBranchAddress("vx", m_x); + setBranchAddress("vy", m_y); + setBranchAddress("vz", m_z); + setBranchAddress("vt", m_t); + setBranchAddress("incoming_particles_vertex_primary", m_incoming_particles_vertex_primary); + setBranchAddress("incoming_particles_vertex_secondary", m_incoming_particles_vertex_secondary); + setBranchAddress("incoming_particles_generation", m_incoming_particles_generation); + setBranchAddress("incoming_particles_sub_particle", m_incoming_particles_sub_particle); + setBranchAddress("incoming_particles_particle", m_incoming_particles_particle); + setBranchAddress("outgoing_particles_vertex_primary", m_outgoing_particles_vertex_primary); + setBranchAddress("outgoing_particles_vertex_secondary", m_outgoing_particles_vertex_secondary); + setBranchAddress("outgoing_particles_generation", m_outgoing_particles_generation); + setBranchAddress("outgoing_particles_sub_particle", m_outgoing_particles_sub_particle); + setBranchAddress("outgoing_particles_particle", m_outgoing_particles_particle); } std::vector* m_x = nullptr; std::vector* m_y = nullptr; @@ -207,58 +210,58 @@ struct Alice3TrackingTranslator { }; struct TrackStruct : public FileStruct { - TrackStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + TrackStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { mTree->Print(); // Set branch addresses for ACTS track parameters - SETADDRESS("event_nr", m_event_nr); - SETADDRESS("nMeasurements", m_nMeasurements); - SETADDRESS("nStates", m_nStates); - SETADDRESS("nHoles", m_nHoles); - SETADDRESS("chi2Sum", m_chi2Sum); - SETADDRESS("NDF", m_NDF); - SETADDRESS("nMajorityHits", m_nMajorityHits); - SETADDRESS("t_charge", m_t_charge); - SETADDRESS("t_vx", m_t_vx); - SETADDRESS("t_vy", m_t_vy); - SETADDRESS("t_vz", m_t_vz); - SETADDRESS("t_px", m_t_px); - SETADDRESS("t_py", m_t_py); - SETADDRESS("t_pz", m_t_pz); - SETADDRESS("majorityParticleId_vertex_primary", m_majorityParticleId_vertex_primary); - SETADDRESS("majorityParticleId_vertex_secondary", m_majorityParticleId_vertex_secondary); - SETADDRESS("majorityParticleId_generation", m_majorityParticleId_generation); - SETADDRESS("majorityParticleId_sub_particle", m_majorityParticleId_sub_particle); - SETADDRESS("majorityParticleId_particle", m_majorityParticleId_particle); - SETADDRESS("eQOP_fit", m_eQOP_fit); - SETADDRESS("eX_fit", m_eX_fit); - SETADDRESS("eY_fit", m_eY_fit); - SETADDRESS("eZ_fit", m_eZ_fit); - SETADDRESS("ePX_fit", m_ePX_fit); - SETADDRESS("ePY_fit", m_ePY_fit); - SETADDRESS("ePZ_fit", m_ePZ_fit); - SETADDRESS("cov_eX_eX", m_cov_eX_eX); - // SETADDRESS("cov_eY_eY", m_cov_eY_eY); - SETADDRESS("cov_eZ_eZ", m_cov_eZ_eZ); - SETADDRESS("cov_ePX_ePX", m_cov_ePX_ePX); - SETADDRESS("cov_ePY_ePY", m_cov_ePY_ePY); - SETADDRESS("cov_ePZ_ePZ", m_cov_ePZ_ePZ); - SETADDRESS("cov_eX_ePX", m_cov_eX_ePX); - SETADDRESS("cov_eY_ePY", m_cov_eY_ePY); - SETADDRESS("cov_eZ_ePZ", m_cov_eZ_ePZ); - SETADDRESS("cov_eX_eY", m_cov_eX_eY); - SETADDRESS("cov_eX_eZ", m_cov_eX_eZ); - SETADDRESS("cov_eY_eZ", m_cov_eY_eZ); - SETADDRESS("cov_ePX_ePY", m_cov_ePX_ePY); - SETADDRESS("cov_ePX_ePZ", m_cov_ePX_ePZ); - SETADDRESS("cov_ePY_ePZ", m_cov_ePY_ePZ); - SETADDRESS("cov_eX_ePY", m_cov_eX_ePY); - SETADDRESS("cov_eX_ePZ", m_cov_eX_ePZ); - SETADDRESS("cov_eY_ePX", m_cov_eY_ePX); - SETADDRESS("cov_eY_ePZ", m_cov_eY_ePZ); - SETADDRESS("cov_eZ_ePX", m_cov_eZ_ePX); - SETADDRESS("cov_eZ_ePY", m_cov_eZ_ePY); + setBranchAddress("event_nr", m_event_nr); + setBranchAddress("nMeasurements", m_nMeasurements); + setBranchAddress("nStates", m_nStates); + setBranchAddress("nHoles", m_nHoles); + setBranchAddress("chi2Sum", m_chi2Sum); + setBranchAddress("NDF", m_NDF); + setBranchAddress("nMajorityHits", m_nMajorityHits); + setBranchAddress("t_charge", m_t_charge); + setBranchAddress("t_vx", m_t_vx); + setBranchAddress("t_vy", m_t_vy); + setBranchAddress("t_vz", m_t_vz); + setBranchAddress("t_px", m_t_px); + setBranchAddress("t_py", m_t_py); + setBranchAddress("t_pz", m_t_pz); + setBranchAddress("majorityParticleId_vertex_primary", m_majorityParticleId_vertex_primary); + setBranchAddress("majorityParticleId_vertex_secondary", m_majorityParticleId_vertex_secondary); + setBranchAddress("majorityParticleId_generation", m_majorityParticleId_generation); + setBranchAddress("majorityParticleId_sub_particle", m_majorityParticleId_sub_particle); + setBranchAddress("majorityParticleId_particle", m_majorityParticleId_particle); + setBranchAddress("eQOP_fit", m_eQOP_fit); + setBranchAddress("eX_fit", m_eX_fit); + setBranchAddress("eY_fit", m_eY_fit); + setBranchAddress("eZ_fit", m_eZ_fit); + setBranchAddress("ePX_fit", m_ePX_fit); + setBranchAddress("ePY_fit", m_ePY_fit); + setBranchAddress("ePZ_fit", m_ePZ_fit); + setBranchAddress("cov_eX_eX", m_cov_eX_eX); + setBranchAddress("cov_eY_eY", m_cov_eY_eY); + setBranchAddress("cov_eZ_eZ", m_cov_eZ_eZ); + setBranchAddress("cov_ePX_ePX", m_cov_ePX_ePX); + setBranchAddress("cov_ePY_ePY", m_cov_ePY_ePY); + setBranchAddress("cov_ePZ_ePZ", m_cov_ePZ_ePZ); + setBranchAddress("cov_eX_ePX", m_cov_eX_ePX); + setBranchAddress("cov_eY_ePY", m_cov_eY_ePY); + setBranchAddress("cov_eZ_ePZ", m_cov_eZ_ePZ); + setBranchAddress("cov_eX_eY", m_cov_eX_eY); + setBranchAddress("cov_eX_eZ", m_cov_eX_eZ); + setBranchAddress("cov_eY_eZ", m_cov_eY_eZ); + setBranchAddress("cov_ePX_ePY", m_cov_ePX_ePY); + setBranchAddress("cov_ePX_ePZ", m_cov_ePX_ePZ); + setBranchAddress("cov_ePY_ePZ", m_cov_ePY_ePZ); + setBranchAddress("cov_eX_ePY", m_cov_eX_ePY); + setBranchAddress("cov_eX_ePZ", m_cov_eX_ePZ); + setBranchAddress("cov_eY_ePX", m_cov_eY_ePX); + setBranchAddress("cov_eY_ePZ", m_cov_eY_ePZ); + setBranchAddress("cov_eZ_ePX", m_cov_eZ_ePX); + setBranchAddress("cov_eZ_ePY", m_cov_eZ_ePY); } // Define track-related members here UInt_t* m_event_nr = nullptr; @@ -278,8 +281,8 @@ struct Alice3TrackingTranslator { std::vector* m_ePZ_fit = nullptr; // global momentum pz // covariance matrices for fitted track parameters (if available) - std::vector* m_cov_eX_eX = nullptr; // covariance of global position x - // std::vector* m_cov_eY_eY = nullptr; // covariance of global position y + std::vector* m_cov_eX_eX = nullptr; // covariance of global position x + std::vector* m_cov_eY_eY = nullptr; // covariance of global position y std::vector* m_cov_eZ_eZ = nullptr; // covariance of global position z std::vector* m_cov_ePX_ePX = nullptr; // covariance of global momentum px std::vector* m_cov_ePY_ePY = nullptr; // covariance of global momentum py @@ -319,34 +322,35 @@ struct Alice3TrackingTranslator { }; struct HitsStruct : public FileStruct { - HitsStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) + HitsStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) { mTree->Print(); - SETADDRESS("barcode", barcode); + setBranchAddress("barcode", barcode); } std::vector* barcode = nullptr; }; - void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int secondDaughter, int numberOfHits) + void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int secondDaughter, int numberOfHits, float secondaryVertexRadius) { - int mothers[2] = {firstMother, firstMother}; - int daughters[2] = {firstDaughter, secondDaughter}; + std::array mothers = {firstMother, firstMother}; + std::array daughters = {firstDaughter, secondDaughter}; tableStoredMcParticles(collIndex, // mcCollisionId fileParticles.m_particle_type->at(iParticle), // pdgCode 0, // statusCode flags, // flags mothers, // mothersIds - daughters, // daughtersIdSlice + daughters.data(), // daughtersIdSlice 1.0f, // weight fileParticles.m_px->at(iParticle), // m_px fileParticles.m_py->at(iParticle), // m_py fileParticles.m_pz->at(iParticle), // m_pz std::hypot(fileParticles.m_p->at(iParticle), fileParticles.m_m->at(iParticle)), // e - fileParticles.m_vx->at(iParticle), // m_vx - fileParticles.m_vy->at(iParticle), // m_vy - fileParticles.m_vz->at(iParticle), // m_vz - fileParticles.m_vt->at(iParticle)); // m_vt + fileParticles.m_vx->at(iParticle) * 0.1, // m_vx + fileParticles.m_vy->at(iParticle) * 0.1, // m_vy + fileParticles.m_vz->at(iParticle) * 0.1, // m_vz + fileParticles.m_vt->at(iParticle) * 0.1); // m_vt tableMCParticlesExtraA3(numberOfHits, // number_of_hits - fileParticles.m_q->at(iParticle)); // charge + fileParticles.m_q->at(iParticle), // charge + secondaryVertexRadius); // secondary_vertex_radius }; void process(o2::aod::BCs const&) @@ -359,7 +363,7 @@ struct Alice3TrackingTranslator { TList* filesList = dir.GetListOfFiles(); if (filesList) { TIter next(filesList); - TSystemFile* file; + TSystemFile* file = nullptr; while ((file = dynamic_cast(next()))) { TString fname = file->GetName(); LOG(debug) << "Found file: " << fname.Data(); @@ -426,11 +430,13 @@ struct Alice3TrackingTranslator { // barcode -> global AO2D index (used later for track labels) std::map barcodeToGlobalIdx; - std::map> barcodeToVertexPosition; + std::map> globalToVertexPosition; std::map GlobalIdxToMotherIdx; + std::map GlobalIdxSecVertexRadiusIncoming; std::map GlobalIdxToPDGCode; std::map GlobalToLocalIdx; std::map> GlobalIdxToDaughterIdxs; + for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { Key key = keyOf( fileParticlesSim.m_vertex_primary->at(iPart), @@ -465,6 +471,7 @@ struct Alice3TrackingTranslator { auto iteratorMother = barcodeToGlobalIdx.find(kd); if (iteratorMother != barcodeToGlobalIdx.end()) { int motherIdx = iteratorMother->second; + GlobalIdxSecVertexRadiusIncoming[motherIdx] = std::hypot(fileVertices.m_x->at(j) * 0.1, fileVertices.m_y->at(j) * 0.1); for (size_t d2 = 0; d2 < (*fileVertices.m_outgoing_particles_vertex_secondary)[j].size(); ++d2) { Key kd2 = keyOf( (*fileVertices.m_outgoing_particles_vertex_primary)[j][d2], @@ -472,12 +479,12 @@ struct Alice3TrackingTranslator { (*fileVertices.m_outgoing_particles_particle)[j][d2], (*fileVertices.m_outgoing_particles_generation)[j][d2], (*fileVertices.m_outgoing_particles_sub_particle)[j][d2]); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_x->at(j)); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_y->at(j)); - barcodeToVertexPosition[kd2].push_back(fileVertices.m_z->at(j)); auto iteratorDaughter = barcodeToGlobalIdx.find(kd2); if (iteratorDaughter != barcodeToGlobalIdx.end()) { int daughterIdx = iteratorDaughter->second; + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_x->at(j)); + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_y->at(j)); + globalToVertexPosition[daughterIdx].push_back(fileVertices.m_z->at(j)); GlobalIdxToMotherIdx[daughterIdx] = motherIdx; GlobalIdxToDaughterIdxs[motherIdx].push_back(daughterIdx); } @@ -486,21 +493,29 @@ struct Alice3TrackingTranslator { } } for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { + float secondaryVertexRadius = -1.0f; int globalIdx = firstIdxThisEvent + static_cast(iPart); int motherIdx = -1; - if (GlobalIdxToMotherIdx.find(globalIdx) != GlobalIdxToMotherIdx.end()) { + if (GlobalIdxToMotherIdx.contains(globalIdx)) { motherIdx = GlobalIdxToMotherIdx[globalIdx]; } int firstDaughter = -1; int secondDaughter = -1; - if (GlobalIdxToDaughterIdxs.find(globalIdx) != GlobalIdxToDaughterIdxs.end()) { + if (GlobalIdxToDaughterIdxs.contains(globalIdx)) { const auto& daughters = GlobalIdxToDaughterIdxs[globalIdx]; - if (daughters.size() > 0) { + if (!daughters.empty()) { firstDaughter = daughters[0]; } if (daughters.size() > 1) { secondDaughter = daughters[1]; } + if (globalToVertexPosition.contains(globalIdx)) { + const auto& vertexPos = globalToVertexPosition[globalIdx]; + secondaryVertexRadius = std::hypot(vertexPos[0] * 0.1, vertexPos[1] * 0.1); + } + if (secondaryVertexRadius == -1) { + secondaryVertexRadius = GlobalIdxSecVertexRadiusIncoming[globalIdx]; + } } // Determine flags for the MC particle // TODO: For now primary are only those without mother, but all based on PhysicalPrimary definition should be considered. @@ -513,24 +528,24 @@ struct Alice3TrackingTranslator { } GlobalIdxToPDGCode[globalIdx] = fileParticlesSim.m_particle_type->at(iPart); GlobalToLocalIdx[globalIdx] = iPart; - addMCParticle(tableMcCollisions.lastIndex(), fileParticlesSim, iPart, flags, motherIdx, firstDaughter, secondDaughter, fileParticlesSim.m_number_of_hits->at(iPart)); + addMCParticle(tableMcCollisions.lastIndex(), fileParticlesSim, iPart, flags, motherIdx, firstDaughter, secondDaughter, fileParticlesSim.m_number_of_hits->at(iPart), secondaryVertexRadius); } - - tableCollisions(0, // bcId - collisionX, // posX - collisionY, // posY - collisionZ, // posZ - 0.0f, // covXX - 0.0f, // covXY - 0.0f, // covXZ - 0.0f, // covYY - 0.0f, // covYZ - 0.0f, // covZZ - 0, // flags - 0.0f, // m_chi2Sum - 0, // numContrib - 0.0f, // collisionTime - 0.0f); // collisionTimeRes + const float scale = 0.1f; + tableCollisions(0, // bcId + collisionX * scale, // posX + collisionY * scale, // posY + collisionZ * scale, // posZ + 0.0f, // covXX + 0.0f, // covXY + 0.0f, // covXZ + 0.0f, // covYY + 0.0f, // covYZ + 0.0f, // covZZ + 0, // flags + 0.0f, // m_chi2Sum + 0, // numContrib + 0.0f, // collisionTime + 0.0f); // collisionTimeRes tableMcCollisionLabels(iEvent, // mcCollisionId 0); // mcMask @@ -585,7 +600,7 @@ struct Alice3TrackingTranslator { // Fill covariance matrices float cxx = fileTracksummary.m_cov_eX_eX->at(iTrack); - float cyy = cxx; // fileTracksummary.cov_eY_eY->at(iTrack); + float cyy = fileTracksummary.m_cov_eY_eY->at(iTrack); float czz = fileTracksummary.m_cov_eZ_eZ->at(iTrack); float cxy = fileTracksummary.m_cov_eX_eY->at(iTrack); float cxz = fileTracksummary.m_cov_eX_eZ->at(iTrack); @@ -606,7 +621,8 @@ struct Alice3TrackingTranslator { float czpy = fileTracksummary.m_cov_eZ_ePY->at(iTrack); float czpz = fileTracksummary.m_cov_eZ_ePZ->at(iTrack); // Create TrackParCov object with covariance matrix - std::array position = {x, y, z}; + + std::array position = {x * scale, y * scale, z * scale}; std::array momentum = {px, py, pz}; std::array trackCov = {cxx, cxy, cyy, cxz, cyz, czz, cxpx, cypx, czpx, cpxpx, cxpy, cypy, czpy, cpxpy, cpypy, cxpz, cypz, czpz, cpxpz, cpypz, cpzpz}; o2::track::TrackParCov trackParCov(position, momentum, trackCov, charge); @@ -669,15 +685,28 @@ struct Alice3TrackingTranslator { tableTracksAlice3(true, // isReconstructed -1.f, // todo: track innermost update -1.f); // todo: track outermost update - if (mcParticleIdx > 0) + if (mcParticleIdx > 0) { tableTracksAlice3Pdg(GlobalIdxToPDGCode[mcParticleIdx]); // PdgCode to the linked MC truth particle - else + } else { tableTracksAlice3Pdg(0); // No linked MC truth particle + } - tableTracksExtraA3(m_nMeasurements, // nSiliconHits (using m_nMeasurements as proxy) - 0, // nTPCHits - 0, // trackType - false); // isPVContributor + float secondaryVertexRadius = 0; + std::array vertexPos = {0.0, 0.0, 0.0}; + if (mcParticleIdx > 0) { + vertexPos[0] = fileParticlesSim.m_vx->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + vertexPos[1] = fileParticlesSim.m_vy->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + vertexPos[2] = fileParticlesSim.m_vz->at(GlobalToLocalIdx[mcParticleIdx]) * 0.1; + secondaryVertexRadius = std::hypot(vertexPos[0], vertexPos[1]); + } + tableTracksExtraA3(m_nMeasurements, // nSiliconHits (using m_nMeasurements as proxy) + 0, // nTPCHits + 0, // trackType + false, // isPVContributor + secondaryVertexRadius, // secondaryVertexRadius + vertexPos[0], + vertexPos[1], + vertexPos[2]); // Fill extra track info tableStoredTracksExtra(0.f, // TPCInnerParam static_cast(0), // Flags @@ -730,17 +759,17 @@ struct Alice3TrackingTranslator { } }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec w; - if (cfgc.options().hasOption("aod-file")) { - std::string inputFile = cfgc.options().get("aod-file"); + if (context.options().hasOption("aod-file")) { + auto inputFile = context.options().get("aod-file"); if (!inputFile.empty()) { LOG(info) << " " << inputFile; TString tinputFile(inputFile.c_str()); inputPath = gSystem->DirName(tinputFile); } } - w.push_back(adaptAnalysisTask(cfgc)); + w.push_back(adaptAnalysisTask(context)); return w; } diff --git a/ALICE3/TableProducer/alice3strangenessFinder.cxx b/ALICE3/TableProducer/alice3strangenessFinder.cxx index 8d8216c4c5d..27c69102c36 100644 --- a/ALICE3/TableProducer/alice3strangenessFinder.cxx +++ b/ALICE3/TableProducer/alice3strangenessFinder.cxx @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -64,7 +65,7 @@ using namespace o2::framework; using namespace o2::constants::physics; using Alice3TracksWPid = soa::Join; -using Alice3TracksACTS = soa::Join; +using Alice3TracksACTS = soa::Join; using Alice3TracksOTF = soa::Join; using Alice3MCParticles = soa::Join; @@ -90,7 +91,7 @@ struct Alice3strangenessFinder { ConfigurableAxis axisDCA{"axisDCA", {200, 0, 200}, "DCA axis"}; ConfigurableAxis axisEta{"axisEta", {80, -4.f, 4.f}, "Eta axis"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.025f, 0.05f, 0.075f, 0.1f, 0.125f, 0.15f, 0.175f, 0.2f, 0.225f, 0.25f, 0.275f, 0.3f, 0.325f, 0.35f, 0.375f, 0.4f, 0.425f, 0.45f, 0.475f, 0.5f, 0.525f, 0.55f, 0.575f, 0.6f, 0.625f, 0.65f, 0.675f, 0.7f, 0.725f, 0.75f, 0.775f, 0.8f, 0.82f, 0.85f, 0.875f, 0.9f, 0.925f, 0.95f, 0.975f, 1.0f, 1.05f, 1.1f}, "pt axis for QA histograms"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.5f, 0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.25f, 2.5, 2.75, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.f, 15.f}, "pt axis for QA histograms"}; // DCA Fitter struct : ConfigurableGroup { @@ -133,11 +134,24 @@ struct Alice3strangenessFinder { Configurable cascMaxEta{"cascMaxEta", 2.5f, "DCA between cascade daughters (cm)"}; } presel; + // Daughter Track Eff calculation + struct : ConfigurableGroup { + std::string prefix = "daughEff"; + Configurable analyseK0Daughters{"analyseK0Daughters", true, ""}; + Configurable analyseLambdaDaughters{"analyseLambdaDaughters", false, ""}; + Configurable analyseAntiLambdaDaughters{"analysAntiLambdaDaughters", false, ""}; + Configurable analyseXiMinusDaughters{"analyseXiMinusDaughters", false, ""}; + Configurable analyseXiPlusDaughters{"analyseXiPlusDaughters", false, ""}; + Configurable analysOmegaMinusDaughters{"analysOmegaMinusDaughters", false, ""}; + Configurable analyseOmegaPlusDaughters{"analyseOmegaPlusDaughters", false, ""}; + } daughEff; + // Operation Configurable magneticField{"magneticField", 20.0f, "Magnetic field (in kilogauss)"}; Configurable mcSameMotherCheck{"mcSameMotherCheck", true, "check if tracks come from the same MC mother"}; // for the ACTS study + Configurable useGeneratedSVposition{"useGeneratedSVposition", false, "use the MC truth position of V0 decay"}; Configurable isK0Gun{"isK0Gun", false, "is K0s Monte Carlo gun used"}; Configurable isLambdaGun{"isLambdaGun", true, "is Lambda Monte Carlo gun used"}; Configurable skipFitter{"skipFitter", false, "calculate V0 properties without calling the DCA fitter, using only the track parameters "}; @@ -183,6 +197,8 @@ struct Alice3strangenessFinder { // decay properties float dcaDau{}; + float posDauDCAxy{}; + float negDauDCAxy{}; float eta{}; std::array p{}; std::array posSV{}; @@ -250,8 +266,18 @@ struct Alice3strangenessFinder { hV0Counter->GetXaxis()->SetBinLabel(3, "AntiLambda"); hV0Counter->GetXaxis()->SetBinLabel(4, "Misidentified"); - histos.add("hRadiusVsHitsNeg", "", kTH2D, {{400, 0, 400}, {12, 0.5, 12.5}}); // radius vs hist for MC studies - histos.add("hRadiusVsHitsPos", "", kTH2D, {{400, 0, 400}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + auto hPtResolutionPositive = histos.add("hPtResolutionPositive", "hPtResolutionPositive", kTH2D, {{axisPt}, {axisPt}}); + hPtResolutionPositive->GetXaxis()->SetTitle("p_{T}^{reco} (GeV/c)"); + hPtResolutionPositive->GetYaxis()->SetTitle("p_{T}^{true} (GeV/c)"); + + auto hPtResolutionNegative = histos.add("hPtResolutionNegative", "hPtResolutionNegative", kTH2D, {{axisPt}, {axisPt}}); + hPtResolutionNegative->GetXaxis()->SetTitle("p_{T}^{reco} (GeV/c)"); + hPtResolutionNegative->GetYaxis()->SetTitle("p_{T}^{true} (GeV/c)"); + + auto hPtRes1DNegDau = histos.add("hPtRes1DNegDau", "hPtRes1DNegDau", kTH1D, {{100, -0.2, 0.2}}); + hPtRes1DNegDau->GetXaxis()->SetTitle("(p_{T}^{gen} - p_{T}^{reco})/p_{T}^{gen}"); + auto hPtRes1DPosDau = histos.add("hPtRes1DPosDau", "hPtRes1DPosDau", kTH1D, {{100, -0.2, 0.2}}); + hPtRes1DPosDau->GetXaxis()->SetTitle("(p_{T}^{gen} - p_{T}^{reco})/p_{T}^{gen}"); auto hV0Building = histos.add("hV0Building", "hV0Building", kTH1D, {{10, 0.5, 10.5}}); hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); @@ -279,18 +305,42 @@ struct Alice3strangenessFinder { histos.add("CascadeBuilding/hXiMass", "", kTH1D, {axisXiMass}); histos.add("CascadeBuilding/hOmegaMass", "", kTH1D, {axisOmegaMass}); } - histos.add("hPx", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPy", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPz", "", kTH1D, {{200, -10.f, 10.f}}); - - histos.add("hPxPairs", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPyPairs", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPzPairs", "", kTH1D, {{200, -10.f, 10.f}}); - - histos.add("hPxV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPyV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); - histos.add("hPzV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPxPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPyPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPzPosRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + + histos.add("hPxNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPyNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hPzNegRepropagated", "", kTH2D, {{200, -10.f, 10.f}, {200, -10.f, 10.f}}); + histos.add("hRadius", "", kTH1D, {{100, 0, 100}}); + histos.add("hRadiusGenNeg", "", kTH1D, {{100, 0, 100}}); + histos.add("hRadiusGenPos", "", kTH1D, {{100, 0, 100}}); + + histos.add("hMomentumPosRepropagated", "", kTH2D, {{500, 0.f, 15.f}, {500, -0.f, 15.f}}); + histos.add("hMomentumNegRepropagated", "", kTH2D, {{500, 0.f, 15.f}, {500, -0.f, 15.f}}); + + histos.add("hCosPhiRepropagated", "", kTH2D, {{100, 0.8f, 1.f}, {100, 0.8f, 1.f}}); + + if (doprocessDaughterTracksForEffACTS || doprocessDaughterTracksForEffOTF) { + histos.add("K0Short/hNegDaughterRecoTruePtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hNegDaughterRecoPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterRecoTruePtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterRecoPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.addClone("K0Short/", "Lambda"); + histos.addClone("K0Short/", "AntiLambda"); + } + if (doprocessMCTrueFromACTS) { + histos.add("K0Short/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("K0Short/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Lambda/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Lambda/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("AntiLambda/hNegDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + histos.add("AntiLambda/hPosDaughterGenPtEta", "", kTH2D, {{axisPt}, {axisEta}}); + + histos.add("hRadiusVsHitsNeg", "", kTH2D, {{100, 0, 100}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + histos.add("hRadiusVsHitsPos", "", kTH2D, {{100, 0, 100}, {12, 0.5, 12.5}}); // radius vs hist for MC studies + } histos.print(); } @@ -343,6 +393,19 @@ struct Alice3strangenessFinder { } // end association check return returnValue; } + template + bool checkSameMotherGen(TMCParticle const& mcParticle1, TMCParticle const& mcParticle2) + { + bool returnValue = false; + if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { + auto motherIds1 = mcParticle1.mothersIds(); + auto motherIds2 = mcParticle2.mothersIds(); + if (!motherIds1.empty() && !motherIds2.empty() && motherIds1[0] == motherIds2[0]) { + returnValue = true; + } + } // end association check + return returnValue; + } template bool buildDecayCandidateTwoBody(TTrackType const& t0, TTrackType const& t1, std::array vtx, Candidate& thisCandidate) @@ -392,9 +455,15 @@ struct Alice3strangenessFinder { thisCandidate.p[1] = thisCandidate.pDau0[1] + thisCandidate.pDau1[1]; thisCandidate.p[2] = thisCandidate.pDau0[2] + thisCandidate.pDau1[2]; const auto posSV = fitter.getPCACandidatePos(); - thisCandidate.posSV[0] = posSV[0]; - thisCandidate.posSV[1] = posSV[1]; - thisCandidate.posSV[2] = posSV[2]; + if (useOriginalTrackParams) { + thisCandidate.posSV[0] = t0.getX(); + thisCandidate.posSV[1] = t0.getY(); + thisCandidate.posSV[2] = t0.getZ(); + } else { + thisCandidate.posSV[0] = posSV[0]; + thisCandidate.posSV[1] = posSV[1]; + thisCandidate.posSV[2] = posSV[2]; + } std::array covA = {0}; std::array covB = {0}; @@ -427,6 +496,9 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); + std::array dcaInfo = {999.0f, 999.0f}; // by default, take large value to make sure candidate accepted, TODO implement proper DCA calculation + thisCandidate.posDauDCAxy = dcaInfo[0]; + thisCandidate.negDauDCAxy = dcaInfo[1]; } else { t0.getPxPyPzGlo(thisCandidate.pDau0); t1.getPxPyPzGlo(thisCandidate.pDau1); @@ -443,6 +515,8 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); + thisCandidate.posDauDCAxy = 999.0f; + thisCandidate.negDauDCAxy = 999.0f; } return true; } @@ -450,21 +524,39 @@ struct Alice3strangenessFinder { void processGenerated(aod::McParticles const&) { for (const auto& mcParticle : trueK0s) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedK0s"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueLambda) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiLambda) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedAntiLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueXi) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiXi) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedAntiXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueOmega) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } histos.fill(HIST("Generated/hGeneratedOmega"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiOmega) { @@ -484,18 +576,12 @@ struct Alice3strangenessFinder { } o2::track::TrackParCov pos = getTrackParCov(posTrack); - // if constexpr (requires { posTrack.pdgCode(); }) { - // if (posTrack.pdgCode() == kPiPlus) { - histos.fill(HIST("hPx"), posTrack.px()); - histos.fill(HIST("hPy"), posTrack.py()); - histos.fill(HIST("hPz"), posTrack.pz()); - // } - // } for (auto const& negTrack : negTracksGrouped) { if (!negTrack.isReconstructed()) { continue; // no ghost tracks } + o2::track::TrackParCov neg = getTrackParCov(negTrack); histos.fill(HIST("hV0Building"), 1.0); if (mcSameMotherCheck && !checkSameMother(posTrack, negTrack)) { @@ -503,68 +589,65 @@ struct Alice3strangenessFinder { } if (isK0Gun || isLambdaGun) { - // ACTS: pdg code attached to track - if constexpr (requires { posTrack.pdgCode(); }) { - if ((posTrack.pdgCode() != kPiPlus && negTrack.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((posTrack.pdgCode() != kProton && negTrack.pdgCode() != kPiMinus) && isLambdaGun) { - continue; - } - } else { - // OTF: pdg code from mcParticle table - if (!posTrack.has_mcParticle() && !negTrack.has_mcParticle()) { - continue; - } - auto mcParticlePos = posTrack.template mcParticle_as(); - auto mcParticleNeg = negTrack.template mcParticle_as(); - if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { - continue; - } + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + continue; + } + auto mcParticlePos = posTrack.template mcParticle_as(); + auto mcParticleNeg = negTrack.template mcParticle_as(); + if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { + continue; + } + if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { + continue; } } - Candidate v0Cand; histos.fill(HIST("hV0Building"), 2.0); - o2::track::TrackParCov neg = getTrackParCov(negTrack); if (!buildDecayCandidateTwoBody(pos, neg, vtx, v0Cand)) { continue; // failed at building candidate } histos.fill(HIST("hV0Building"), 3.0); - // TODO: not all ACTS tracks have MC association, so this check is not possible for all candidates, fix is needed - // auto mcParticle1 = posTrack.template mcParticle_as(); - // if (mcParticle1.pdgCode() == kK0Short) { - // histos.fill(HIST("hV0Counter"), 0.5); - // } else if (mcParticle1.pdgCode() == kLambda0) { - // histos.fill(HIST("hV0Counter"), 1.5); - // } else if (mcParticle1.pdgCode() == kLambda0Bar) { - // histos.fill(HIST("hV0Counter"), 2.5); - // } else { - // histos.fill(HIST("hV0Counter"), 3.5); - // } - histos.fill(HIST("hPxPairs"), posTrack.px()); - histos.fill(HIST("hPyPairs"), posTrack.py()); - histos.fill(HIST("hPzPairs"), posTrack.pz()); - - histos.fill(HIST("hPxV0CandDaughter"), v0Cand.pDau0[0]); - histos.fill(HIST("hPyV0CandDaughter"), v0Cand.pDau0[1]); - histos.fill(HIST("hPzV0CandDaughter"), v0Cand.pDau0[2]); - + const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); + std::array posSV = {v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2]}; + double p_pos_orig = sqrt(posTrack.px() * posTrack.px() + posTrack.py() * posTrack.py() + posTrack.pz() * posTrack.pz()); + double p_neg_orig = sqrt(negTrack.px() * negTrack.px() + negTrack.py() * negTrack.py() + negTrack.pz() * negTrack.pz()); + double p_pos_reprop = sqrt(v0Cand.pDau0[0] * v0Cand.pDau0[0] + v0Cand.pDau0[1] * v0Cand.pDau0[1] + v0Cand.pDau0[2] * v0Cand.pDau0[2]); + double p_neg_reprop = sqrt(v0Cand.pDau1[0] * v0Cand.pDau1[0] + v0Cand.pDau1[1] * v0Cand.pDau1[1] + v0Cand.pDau1[2] * v0Cand.pDau1[2]); + double cosOpeningAngleReprop = (v0Cand.pDau0[0] * v0Cand.pDau1[0] + v0Cand.pDau0[1] * v0Cand.pDau1[1] + v0Cand.pDau0[2] * v0Cand.pDau1[2]) / (sqrt(v0Cand.pDau0[0] * v0Cand.pDau0[0] + v0Cand.pDau0[1] * v0Cand.pDau0[1] + v0Cand.pDau0[2] * v0Cand.pDau0[2]) * sqrt(v0Cand.pDau1[0] * v0Cand.pDau1[0] + v0Cand.pDau1[1] * v0Cand.pDau1[1] + v0Cand.pDau1[2] * v0Cand.pDau1[2])); + double cosOpeningAngleOrig = (posTrack.px() * negTrack.px() + posTrack.py() * negTrack.py() + posTrack.pz() * negTrack.pz()) / (sqrt(posTrack.px() * posTrack.px() + posTrack.py() * posTrack.py() + posTrack.pz() * posTrack.pz()) * sqrt(negTrack.px() * negTrack.px() + negTrack.py() * negTrack.py() + negTrack.pz() * negTrack.pz())); + + if constexpr (requires { negTrack.secondaryVertexRadius(); }) { + histos.fill(HIST("hRadiusGenNeg"), negTrack.secondaryVertexRadius()); + histos.fill(HIST("hRadiusGenPos"), posTrack.secondaryVertexRadius()); + if (useGeneratedSVposition) { + posSV[0] = posTrack.secondaryVertexX(); + posSV[1] = posTrack.secondaryVertexY(); + posSV[2] = posTrack.secondaryVertexZ(); + } + } v0CandidateIndices(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), -1); - v0CandidateCores(v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2], + v0CandidateCores(posSV[0], posSV[1], posSV[2], v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2], v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2], - v0Cand.dcaDau, posTrack.dcaXY(), negTrack.dcaXY(), + v0Cand.dcaDau, v0Cand.posDauDCAxy, v0Cand.negDauDCAxy, v0Cand.cosPA, v0Cand.dcaToPV); + histos.fill(HIST("hRadius"), radiusV0); + histos.fill(HIST("hPxPosRepropagated"), posTrack.px(), v0Cand.pDau0[0]); + histos.fill(HIST("hPyPosRepropagated"), posTrack.py(), v0Cand.pDau0[1]); + histos.fill(HIST("hPzPosRepropagated"), posTrack.pz(), v0Cand.pDau0[2]); + histos.fill(HIST("hPxNegRepropagated"), negTrack.px(), v0Cand.pDau1[0]); + histos.fill(HIST("hPyNegRepropagated"), negTrack.py(), v0Cand.pDau1[1]); + histos.fill(HIST("hPzNegRepropagated"), negTrack.pz(), v0Cand.pDau1[2]); + + histos.fill(HIST("hCosPhiRepropagated"), cosOpeningAngleOrig, cosOpeningAngleReprop); + histos.fill(HIST("hMomentumPosRepropagated"), p_pos_orig, p_pos_reprop); + histos.fill(HIST("hMomentumNegRepropagated"), p_neg_orig, p_neg_reprop); const float lambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2]}, std::array{v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2]}}, @@ -627,7 +710,7 @@ struct Alice3strangenessFinder { continue; // candidate outside of acceptance } - const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); + // const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); const float radiusCasc = std::hypot(cascCand.posSV[0], cascCand.posSV[1]); // tracks without update information (IU < 0) are not checked if (posTrack.trackIU() > 0.f && posTrack.trackIU() < radiusV0) { @@ -695,7 +778,7 @@ struct Alice3strangenessFinder { } // end posTrack } - void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const&) + void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const&, aod::McParticles const&) { auto negativeMCParticlesGrouped = negativeMCParticles->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); @@ -730,6 +813,19 @@ struct Alice3strangenessFinder { if (!isLambda && isLambdaGun) { continue; } + if (isK0s && checkSameMotherGen(posParticle, negParticle)) { + histos.fill(HIST("K0Short/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + histos.fill(HIST("K0Short/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (isLambda && checkSameMotherGen(posParticle, negParticle)) { + histos.fill(HIST("Lambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + histos.fill(HIST("Lambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (isAntiLambda && checkSameMotherGen(posParticle, negParticle)) { + histos.fill(HIST("AntiLambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + histos.fill(HIST("AntiLambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + Candidate v0cand; std::vector v0DecayVertex; v0DecayVertex.push_back(negParticle.vx()); @@ -747,7 +843,7 @@ struct Alice3strangenessFinder { v0CandidateIndices(collision.globalIndex(), posParticle.globalIndex(), negParticle.globalIndex(), - 0); + -1); v0CandidateCores(v0cand.posSV[0], v0cand.posSV[1], v0cand.posSV[2], v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2], v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2], @@ -768,6 +864,69 @@ struct Alice3strangenessFinder { iPosPart++; } } + template + void processDaughterTrack(const TTracksGrouped& tracksGrouped, int daughterType) + { + // for the daughter track efficiency calculation + for (auto const& track : tracksGrouped) { + if (!track.isReconstructed()) { + continue; // no ghost tracks + } + if (!track.has_mcParticle()) { + continue; + } + float pt = std::hypot(track.px(), track.py()); + float eta = RecoDecay::eta(std::array{track.px(), track.py(), track.pz()}); + auto mcParticle = track.template mcParticle_as(); + if (mcParticle.has_mothers()) { + for (const auto& m1 : mcParticle.template mothers_as()) { + if (daughEff.analyseK0Daughters && m1.pdgCode() == kK0Short) { + if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { + histos.fill(HIST("K0Short/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("K0Short/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kPiPlus) { + histos.fill(HIST("K0Short/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("K0Short/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), std::hypot(track.px(), track.py()), mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + if (daughEff.analyseLambdaDaughters && m1.pdgCode() == kLambda0) { + if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { + histos.fill(HIST("Lambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Lambda/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kProton) { + histos.fill(HIST("Lambda/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Lambda/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + if (daughEff.analyseAntiLambdaDaughters && m1.pdgCode() == kLambda0Bar) { + if (daughterType == 0 && mcParticle.pdgCode() == kProtonBar) { + histos.fill(HIST("AntiLambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("AntiLambda/hNegDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionNegative"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DNegDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + if (daughterType == 1 && mcParticle.pdgCode() == kPiPlus) { + histos.fill(HIST("AntiLambda/hPosDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("AntiLambda/hPosDaughterRecoPtEta"), pt, eta); + histos.fill(HIST("hPtResolutionPositive"), pt, mcParticle.pt()); + histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); + } + } + } + } + } + } + // void processFindV0CandidateWithPid(aod::Collision const& collision, aod::McParticles const& mcParticles, Alice3TracksWPid const&) // { // auto negativeSecondaryPionsGrouped = negativeSecondaryPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); @@ -792,14 +951,35 @@ struct Alice3strangenessFinder { processFindV0CandidateNoPid(collision, negTracksGrouped, posTracksGrouped, bachTracksGrouped); } + void processDaughterTracksForEffACTS(aod::Collision const& collision, Alice3TracksACTS const&, aod::McParticles const&) + { + auto negTracksGrouped = negativeSecondaryTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto posTracksGrouped = positiveSecondaryTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + // auto bachTracksGrouped = bachelorTracksACTS->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + processDaughterTrack(negTracksGrouped, 0); + processDaughterTrack(posTracksGrouped, 1); + // processDaughterTrack(bachTracksGrouped,2); + } + void processDaughterTracksForEffOTF(aod::Collision const& collision, Alice3TracksOTF const&, aod::McParticles const&) + { + auto negTracksGrouped = negativeSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto posTracksGrouped = positiveSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + // auto bachTracksGrouped = bachelorTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + processDaughterTrack(negTracksGrouped, 0); + processDaughterTrack(posTracksGrouped, 1); + // processDaughterTrack(bachTracksGrouped,2); + } + PROCESS_SWITCH(Alice3strangenessFinder, processGenerated, "process generated information", true); PROCESS_SWITCH(Alice3strangenessFinder, processFindV0CandidateACTS, "find V0 without PID from ACTS reconstruction", true); PROCESS_SWITCH(Alice3strangenessFinder, processFindV0CandidateOTF, "find V0 without PID from OTF simulations", false); PROCESS_SWITCH(Alice3strangenessFinder, processMCTrueFromACTS, "process MC truth from ACTS", false); + PROCESS_SWITCH(Alice3strangenessFinder, processDaughterTracksForEffACTS, "process daughter trakcs from ACTS", true); + PROCESS_SWITCH(Alice3strangenessFinder, processDaughterTracksForEffOTF, "process daughter trakcs from ACTS", false); // PROCESS_SWITCH(alice3strangenessFinder, processFindV0CandidateWithPid, "find V0 with PID", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/ALICE3/Tasks/alice3Strangeness.cxx b/ALICE3/Tasks/alice3Strangeness.cxx index 4733c899d16..0a4543e4a34 100644 --- a/ALICE3/Tasks/alice3Strangeness.cxx +++ b/ALICE3/Tasks/alice3Strangeness.cxx @@ -22,6 +22,7 @@ #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFStrangeness.h" #include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -101,6 +102,7 @@ struct Alice3Strangeness { Configurable cosPAngleSelection{"cosPAngleSelection", 0.97f, "cosine of pointing angle selection"}; Configurable dcaDaughtersToPVSelection{"dcaDaughtersToPVSelection", 0.05f, "DCA daughters to primary vertex selection"}; Configurable v0RadiusSelection{"v0RadiusSelection", 0.5f, "V0 radius selection"}; + Configurable v0MaxRadiusSelection{"v0MaxRadiusSelection", 3.0f, "V0 maximum radius selection"}; Configurable armenterosSelection{"armenterosSelection", 0.2f, "Armenteros-Podolanski selection in [0]* alpha"}; Configurable competingMassRejectionK0{"competingMassRejectionK0", 0.005f, "competing mass rejection for K0"}; Configurable competingMassRejectionLambda{"competingMassRejectionLambda", 0.01f, "competing mass rejection for Lambda"}; @@ -166,11 +168,11 @@ struct Alice3Strangeness { Configurable tofOuter{"tofOuter", false, "enable particle identification selection"}; } cascadeFlags; - uint16_t appliedSelectionCheckMask; - double selectionCheck; - double selectionCheckPos; - const int posDaugDCAselIDx = 3; - static constexpr std::string_view KSelectionNames[] = {"DCAV0Daughters", "PointingAngle", "DCAtoPVNegDaughter", "DCAtoPVPosDaughter", "V0Radius", "ProperLifeTime"}; + uint16_t appliedSelectionCheckMask{}; + double selectionCheck{}; + double selectionCheckPos{}; + static constexpr int posDaugDCAselIDx = 3; + static constexpr std::array KSelectionNames = {"DCAV0Daughters", "PointingAngle", "DCAtoPVNegDaughter", "DCAtoPVPosDaughter", "V0Radius", "ProperLifeTime"}; static constexpr float ToMicrons = 1e+4f; static constexpr float CtauXi = 4.91f; @@ -214,7 +216,7 @@ struct Alice3Strangeness { template void configureNSigmas(const TCascade& cascade) { - const Cascade::Type analysedCascadeType = static_cast(cascadeFlags.analyseCascade.value); + const auto analysedCascadeType = static_cast(cascadeFlags.analyseCascade.value); switch (analysedCascadeType) { case Cascade::Xi: nSigmaBachInnerTof = cascade.nSigmaInnerTofXiBachPi(); @@ -295,8 +297,10 @@ struct Alice3Strangeness { histos.add("reconstructedCandidates/hEtaDaughters", "hEtaDaughters", kTH1F, {histAxes.axisEta}); histos.add("reconstructedCandidates/K0/hMass", "hMass", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisEta}); histos.add("reconstructedCandidates/K0/hMass1D", "hMass1D", kTH1D, {histAxes.axisK0Mass}); + histos.add("reconstructedCandidates/K0/hMassRadius", "hMassRadius", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, {200, 0, 200}}); histos.add("reconstructedCandidates/Lambda/hMass", "hMass", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisEta}); histos.add("reconstructedCandidates/Lambda/hMass1D", "hMass1D", kTH1D, {histAxes.axisLambdaMass}); + histos.add("reconstructedCandidates/Lambda/hMassRadius", "hMassRadius", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, {200, 0, 200}}); histos.add("reconstructedCandidates/hArmeterosBeforeAllSelections", "hArmeterosBeforeAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); histos.add("reconstructedCandidates/hArmeterosAfterAllSelections", "hArmeterosAfterAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); histos.add("reconstructedCandidates/hPtEtaPosDaughter", "hPtEtaPosDaughter", kTH2D, {histAxes.axisPt, histAxes.axisEta}); @@ -402,6 +406,12 @@ struct Alice3Strangeness { } histos.addClone("reconstructedCandidates/Lambda/", "reconstructedCandidates/AntiLambda/"); + if (doprocessMCTruth) { + histos.add("mcTruth/K0Short/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("mcTruth/Lambda/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("mcTruth/AntiLambda/hPtRadius", "hPtRadius", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + } + appliedSelectionCheckMask = 0; if (!v0SelectionFlags.applyDCAdaughterSelection) { SETBIT(appliedSelectionCheckMask, 0); @@ -440,20 +450,25 @@ struct Alice3Strangeness { histos.fill(HIST("K0/hSelections"), 0); // all candidates histos.fill(HIST("K0/hDCANegDaughter"), negV0Daughter.dcaXY()); histos.fill(HIST("K0/hDCAPosDaughter"), posV0Daughter.dcaXY()); - if (std::abs(negV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) + if (std::abs(negV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 1); // dcaXY cut - if (std::abs(posV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) + if (std::abs(posV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 2); // dcaXY cut - if (v0Cand.dcaV0Daughters() > v0SelectionValues.dcaDaughterSelection) + if (v0Cand.dcaV0Daughters() > v0SelectionValues.dcaDaughterSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 3); // dca between daughters - if (v0Cand.v0Radius() < v0SelectionValues.v0RadiusSelection) + if (v0Cand.v0Radius() < v0SelectionValues.v0RadiusSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 4); // radius cut - if (std::abs(negV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection) + if (std::abs(negV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection) { continue; + } histos.fill(HIST("K0/hSelections"), 5); // eta cut histos.fill(HIST("K0/hMassSelected"), v0Cand.mK0Short(), v0Cand.pt()); } @@ -486,78 +501,92 @@ struct Alice3Strangeness { histos.fill(HIST("reconstructedCandidates/hArmeterosBeforeAllSelections"), v0.alpha(), v0.qtArm()); histos.fill(HIST("hV0CandidateCounter"), 0.5); if (v0SelectionFlags.applyRapiditySelection) { - if (isK0 && std::abs(v0.yK0Short()) > v0SelectionValues.yK0Selection) + if (isK0 && std::abs(v0.yK0Short()) > v0SelectionValues.yK0Selection) { continue; - if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > v0SelectionValues.yLambdaSelection) + } + if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > v0SelectionValues.yLambdaSelection) { continue; + } } histos.fill(HIST("hV0CandidateCounter"), 1.5); if (v0SelectionFlags.applyDCAdaughterSelection) { - if (std::abs(v0.dcaV0Daughters()) > v0SelectionValues.dcaDaughterSelection) + if (std::abs(v0.dcaV0Daughters()) > v0SelectionValues.dcaDaughterSelection) { continue; + } } else { selectionCheck = v0.dcaV0Daughters(); } histos.fill(HIST("hV0CandidateCounter"), 2.5); if (v0SelectionFlags.applyCosOfPAngleSelection) { - if (v0.cosPA() < v0SelectionValues.cosPAngleSelection) + if (v0.cosPA() < v0SelectionValues.cosPAngleSelection) { continue; + } } else { selectionCheck = std::acos(v0.cosPA()); } histos.fill(HIST("hV0CandidateCounter"), 3.5); if (v0SelectionFlags.applyDCADaughtersToPVSelection) { if ((std::abs(v0.dcaNegToPV()) < v0SelectionValues.dcaDaughtersToPVSelection) || - (std::abs(v0.dcaPosToPV()) < v0SelectionValues.dcaDaughtersToPVSelection)) + (std::abs(v0.dcaPosToPV()) < v0SelectionValues.dcaDaughtersToPVSelection)) { continue; + } } else { selectionCheckPos = std::abs(v0.dcaPosToPV()); selectionCheck = std::abs(v0.dcaNegToPV()); } histos.fill(HIST("hV0CandidateCounter"), 4.5); if (v0SelectionFlags.applyV0RadiusSelection) { - if (v0.v0radius() < v0SelectionValues.v0RadiusSelection) + if (v0.v0radius() < v0SelectionValues.v0RadiusSelection || v0.v0radius() > v0SelectionValues.v0MaxRadiusSelection) { continue; + } } else { selectionCheck = v0.v0radius(); } histos.fill(HIST("hV0CandidateCounter"), 5.5); if (isK0) { if (v0SelectionFlags.applyArmenterosSelection) { - if (v0.qtArm() < v0SelectionValues.armenterosSelection * std::abs(v0.alpha())) + if (v0.qtArm() < v0SelectionValues.armenterosSelection * std::abs(v0.alpha())) { continue; + } } } histos.fill(HIST("hV0CandidateCounter"), 6.5); if (isK0 && v0SelectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) { continue; - if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) + } + if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) { continue; + } } if ((isLambda || isAntiLambda) && v0SelectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.competingMassRejectionLambda) + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.competingMassRejectionLambda) { continue; + } } histos.fill(HIST("hV0CandidateCounter"), 7.5); if (v0SelectionFlags.applyLifetimeSelection) { - if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > v0SelectionValues.lifetimecutak0) + if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > v0SelectionValues.lifetimecutak0) { continue; - if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0SelectionValues.lifetimecutambda) + } + if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0SelectionValues.lifetimecutambda) { continue; + } } else { - if (isK0) + if (isK0) { selectionCheck = v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short; - else + } else { selectionCheck = v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + } } histos.fill(HIST("hV0CandidateCounter"), 8.5); if (v0SelectionFlags.analyseRecoV0Candidates) { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); if (v0SelectionFlags.applyEtaDaughterSelection) { - if (std::abs(posTrack.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > v0SelectionValues.etaDaughterSelection) + if (std::abs(posTrack.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > v0SelectionValues.etaDaughterSelection) { continue; + } } histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), posTrack.eta()); histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), negTrack.eta()); @@ -581,14 +610,18 @@ struct Alice3Strangeness { static_for<0, 5>([&](auto i) { constexpr int In = i.value; if (TESTBIT(appliedSelectionCheckMask, In)) { - if (In == posDaugDCAselIDx) + if (In == posDaugDCAselIDx) { selectionCheck = selectionCheckPos; - if (isK0) + } + if (isK0) { histos.fill(HIST("reconstructedCandidates/K0/h") + HIST(KSelectionNames[In]), v0.mK0Short(), v0.pt(), selectionCheck); - if (isLambda) + } + if (isLambda) { histos.fill(HIST("reconstructedCandidates/Lambda/h") + HIST(KSelectionNames[In]), v0.mLambda(), v0.pt(), selectionCheck); - if (isAntiLambda) + } + if (isAntiLambda) { histos.fill(HIST("reconstructedCandidates/AntiLambda/h") + HIST(KSelectionNames[In]), v0.mAntiLambda(), v0.pt(), selectionCheck); + } } }); } @@ -596,14 +629,17 @@ struct Alice3Strangeness { if (isK0) { histos.fill(HIST("reconstructedCandidates/K0/hMass"), v0.mK0Short(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/K0/hMass1D"), v0.mK0Short()); + histos.fill(HIST("reconstructedCandidates/K0/hMassRadius"), v0.mK0Short(), v0.pt(), v0.v0radius()); } if (isLambda) { histos.fill(HIST("reconstructedCandidates/Lambda/hMass"), v0.mLambda(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/Lambda/hMass1D"), v0.mLambda()); + histos.fill(HIST("reconstructedCandidates/Lambda/hMassRadius"), v0.mLambda(), v0.pt(), v0.v0radius()); } if (isAntiLambda) { histos.fill(HIST("reconstructedCandidates/AntiLambda/hMass"), v0.mAntiLambda(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/AntiLambda/hMass1D"), v0.mAntiLambda()); + histos.fill(HIST("reconstructedCandidates/AntiLambda/hMassRadius"), v0.mAntiLambda(), v0.pt(), v0.v0radius()); } } } @@ -850,14 +886,37 @@ struct Alice3Strangeness { } } } + void processMCTruth(soa::Join const& mcParticles) + { + + for (auto const& mcPart : mcParticles) { + + if (!mcPart.isPhysicalPrimary()) { + continue; + } + + float ptmc = RecoDecay::sqrtSumOfSquares(mcPart.px(), mcPart.py()); + + if (mcPart.pdgCode() == 310) { + histos.fill(HIST("mcTruth/K0Short/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + if (mcPart.pdgCode() == 3122) { + histos.fill(HIST("mcTruth/Lambda/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + if (mcPart.pdgCode() == -3122) { + histos.fill(HIST("mcTruth/AntiLambda/hPtRadius"), ptmc, mcPart.secondaryVertexRadius()); + } + } + } PROCESS_SWITCH(Alice3Strangeness, processAllFindableCandidates, "", false); PROCESS_SWITCH(Alice3Strangeness, processFoundV0Candidates, "", true); + PROCESS_SWITCH(Alice3Strangeness, processMCTruth, "", true); PROCESS_SWITCH(Alice3Strangeness, processFoundCascadeCandidates, "", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } From c1c0c08cbebe824d46da732d623888f5fb34df79 Mon Sep 17 00:00:00 2001 From: Antonio Palasciano <52152842+apalasciano@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:04:00 +0100 Subject: [PATCH 592/648] [PWGHF] HF-h correl tasks: fix DCA selections on associated tracks (#18618) --- PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx | 4 ++-- PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx | 12 ++++++------ PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index c410c0efd0c..fcce20ad9ad 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -477,7 +477,7 @@ struct HfTaskCorrelationDplusHadrons { if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } double const efficiencyWeight = getEfficiencyWeight(ptD, isPromptAnalysis, ptHadron); @@ -581,7 +581,7 @@ struct HfTaskCorrelationDplusHadrons { if (!isSelectedMl(ptD, bdtScoreBkg, bdtScorePrompt, bdtScoreNonPrompt)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } double const efficiencyWeight = getEfficiencyWeight(ptD, isDplusPrompt, ptHadron); diff --git a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx index 0df60e1e5de..2d93fe6408d 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx @@ -105,7 +105,7 @@ struct HfTaskCorrelationDsHadrons { // Configurable doMcCollisionCheck{"doMcCollisionCheck", false, "Flag for applying the collision check and selection based on MC collision info"}; Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; Configurable selectionFlagDs{"selectionFlagDs", 7, "Selection Flag for Ds (avoid the case of flag = 0, no outputMlScore)"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; // Configurable eventGeneratorType{"eventGeneratorType", -1, "If positive, enable event selection using subGeneratorId information. The value indicates which events to keep (0 = MB, 4 = charm triggered, 5 = beauty triggered)"}; Configurable decayChannel{"decayChannel", 1, "Resonant decay channels: 1 for Ds->PhiPi->KKpi, 2 for Ds->K0*K->KKPi"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; @@ -535,7 +535,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -645,7 +645,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -762,7 +762,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -901,7 +901,7 @@ struct HfTaskCorrelationDsHadrons { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -1246,7 +1246,7 @@ struct HfTaskCorrelationDsHadrons { // reconstructed track loop for (const auto& track : groupedTracks) { - if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRaws) { + if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRows) { continue; } if (track.has_mcParticle()) { diff --git a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx index 3261cad3f5b..e977e1d3524 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx @@ -162,7 +162,7 @@ struct HfTaskCorrelationLcHadrons { Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; Configurable ptDaughterMin{"ptDaughterMin", 0.1, "min. daughter pT"}; Configurable activateQA{"activateQA", false, "Flag to enable debug histogram"}; - Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; + Configurable nTpcCrossedRows{"nTpcCrossedRows", 70, "Number of crossed TPC Rows"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analyses"}; // sign and invMasss Configurable fillSignAndMass{"fillSignAndMass", false, "flag to select Lc-h corr with Lc invarient mass and sign of pairs"}; @@ -505,7 +505,7 @@ struct HfTaskCorrelationLcHadrons { if (bdtScorePrompt < mlOutputPrompt->at(effBinLc) || bdtScoreBkg > mlOutputBkg->at(effBinLc)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) >= dcaXYTrackMax || std::abs(trackDcaZ) >= dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } @@ -673,7 +673,7 @@ struct HfTaskCorrelationLcHadrons { if (bdtScorePrompt < mlOutputPrompt->at(effBinLc) || bdtScoreBkg > mlOutputBkg->at(effBinLc)) { continue; } - if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { + if (std::abs(trackDcaXY) > dcaXYTrackMax || std::abs(trackDcaZ) > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRows) { continue; } double efficiencyWeight = 1.; From 5fec492b1d480c7d457bec71ef136f88a18979ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrea=20Tavira=20Garc=C3=ADa?= <118979672+atavirag@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:43:13 +0100 Subject: [PATCH 593/648] [PWGDQ] Remove reinterpret_cast and add extra protections in MCH tracking eff macro (#18379) --- PWGDQ/Macros/evalMchTrackingEfficiency.cxx | 39 +++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx index 931b4c168ff..c66c63a1172 100644 --- a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx +++ b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx @@ -27,12 +27,13 @@ #include +#include #include double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimits[2]); double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, double binLimits[3][2]); -double computeStationEfficiency(double effPerChamber[4], int iStation); -double computeTotalEfficiency(double effPerStation[5]); +double computeStationEfficiency(double const effPerChamber[4], int iStation); +double computeTotalEfficiency(double const effPerStation[5]); void getBinLimits(THnF* hnf, int nBins, int iAxis, std::vector& limits); void setHistoMinPt(THnF* hnf, int iAxis, float minPt); @@ -46,8 +47,26 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons { // read ouput from the taskComputeMchEfficiency TFile* file = TFile::Open(inFile, "read"); - TDirectoryFile* dir = dynamic_cast(file->Get("task-muon-mch-trk-efficiency")); - THnF* hHitsEtaPtPhi = dynamic_cast(dir->Get("hHitsEtaPtPhi")); + if (!file || file->IsZombie()) { + std::cerr << "Cannot open input file: " << inFile << std::endl; + return; + } + + TDirectoryFile* dir = nullptr; + file->GetObject("task-muon-mch-trk-efficiency", dir); + + THnF* hHitsEtaPtPhi = nullptr; + if (dir) { + dir->GetObject("hHitsEtaPtPhi", hHitsEtaPtPhi); + } else { + std::cerr << "Directory task-muon-mch-trk-efficiency not found in file " << inFile << std::endl; + return; + } + + if (!hHitsEtaPtPhi) { + std::cerr << "Histogram hHitsEtaPtPhi not found in directory." << std::endl; + return; + } // retrieve the eta/pt/phi axis binning configuration const int etaAx = 1, ptAx = 2, phiAx = 3; @@ -63,8 +82,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons getBinLimits(hHitsEtaPtPhi, nBinsPt, ptAx, limitsPt); // these are values! getBinLimits(hHitsEtaPtPhi, nBinsPhi, phiAx, limitsPhi); // these are values! - int nBinMinPt = hHitsEtaPtPhi->GetAxis(ptAx)->FindBin(minPt); - // Define the output file TFile* outFile = new TFile(outFileName, "recreate"); @@ -83,7 +100,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons // as well as the one per station // the total integrated value is stored in both histos as last quantity for reference TH1F* hEffIntegratedChamber = new TH1F("hEffIntegratedChamber", "integrated efficiency per chamber; ;Efficiency", kChamber + 1, -0.5, kChamber + 0.5); - const char* hChNames[kChamber + 1]; for (int i = 0; i < kChamber; i++) { hEffIntegratedChamber->GetXaxis()->SetBinLabel(i + 1, Form("Chamber %d", i)); } @@ -91,7 +107,6 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons // Define histogram for integrated efficiency per station TH1F* hEffIntegratedStation = new TH1F("hEffIntegratedStation", "integrated efficiency per station; ;Efficiency", kStation, -0.5, kStation + 0.5); - const char* hStNames[kStation]; for (int i = 0; i < 3; i++) { hEffIntegratedStation->GetXaxis()->SetBinLabel(i + 1, Form("Station %d", i)); } @@ -274,7 +289,7 @@ double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimi hnf->GetAxis(iAxis)->SetRangeUser(binLimits[0], binLimits[1]); // Project onto the Nhits axis - TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast + TH1D* htmp = hnf->Projection(0); double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -322,7 +337,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl } // Project onto the Nhits axis - TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast + TH1D* htmp = hnf->Projection(0); double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -365,7 +380,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl // Compute the efficiency per station // Stations 1, 2, 3: Eff = 1 - ( 1 - E(i) ) * ( 1 - (E(j) ) // Stations 4 & 5 together: Eff = product(i=6...9) E(i) + sum(i=6...9) [ ( 1 - E(i) )* product(j=6...9, i!=i) E(j) ] -double computeStationEfficiency(double effPerChamber[4], int iStation) +double computeStationEfficiency(double const effPerChamber[4], int iStation) { double effSt = 0.; if (iStation < 3) { // Calculation for Station 1, 2, 3 @@ -390,7 +405,7 @@ double computeStationEfficiency(double effPerChamber[4], int iStation) } // Total efficiency corresponds to the product of the efficiency per station -double computeTotalEfficiency(double effPerStation[5]) +double computeTotalEfficiency(double const effPerStation[5]) { double effTot = 1.; for (int i = 0; i < 4; i++) { From aab5599beb93d1e0b74909c40a81a0d55fb9c416 Mon Sep 17 00:00:00 2001 From: Lucia Anna Tarasovicova Date: Fri, 6 Nov 2026 17:26:45 +0100 Subject: [PATCH 594/648] [PWGLF] PWGLF change in derivedcascadeanalysis.cxx (#18619) Co-authored-by: Lucia Anna Tarasovicova --- .../Strangeness/derivedcascadeanalysis.cxx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx index c356183ffb2..5e70a91ec90 100644 --- a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx @@ -264,10 +264,10 @@ struct Derivedcascadeanalysis { TH1F* hOmegaLambdaPointingAngleSelection = nullptr; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; + Service ccdb{}; ctpRateFetcher rateFetcher; - Service pdgDB; + Service pdgDB{}; uint16_t selectionCheckMask = 0; double selectionCheck = -1; @@ -581,7 +581,7 @@ struct Derivedcascadeanalysis { } } } - void initCosPASelectionHistograms(float timeStamp) + void initCosPASelectionHistograms(long timeStamp) { auto* listHistograms = ccdb->getForTimeStamp(histoCCDBPath, timeStamp); @@ -1170,15 +1170,23 @@ struct Derivedcascadeanalysis { return true; } - void fillHistOccupancyCheck(double pt, double mass, float occup, float centrality, bool isPos) + void fillHistOccupancyCheck(double pt, double mass, float occup, float centrality, bool isPos, bool isMC) { static_for<0, 9>([&](auto i) { constexpr int In = i.value; if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { if (isPos) { - histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); + if (isMC) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); + } } else { - histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + if (isMC) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + } } } }); @@ -1637,9 +1645,9 @@ struct Derivedcascadeanalysis { } if (qaFlags.doOccupancyCheck) { - fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive); + fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive, false); if (isTrueMCCascadeDecay) { - fillHistOccupancyCheckMC(ptmc, invmass, occupancy, centrality, isPositive); + fillHistOccupancyCheck(ptmc, invmass, occupancy, centrality, isPositive, true); } } if (isPositive) { @@ -1909,8 +1917,8 @@ struct Derivedcascadeanalysis { PROCESS_SWITCH(Derivedcascadeanalysis, processCascadesMCforEffRun2, "cascade analysis, run2 rec MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } From a33cdd7144512a7fe2ade2dee1524217247f7e0e Mon Sep 17 00:00:00 2001 From: lramona Date: Fri, 6 Nov 2026 17:43:28 +0100 Subject: [PATCH 595/648] [PWGLF] Update the MC closure test function (#18620) --- PWGLF/Tasks/Nuspex/spectraTOF.cxx | 80 ++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/spectraTOF.cxx b/PWGLF/Tasks/Nuspex/spectraTOF.cxx index 6ab1dd8a82d..39e0f6c027e 100644 --- a/PWGLF/Tasks/Nuspex/spectraTOF.cxx +++ b/PWGLF/Tasks/Nuspex/spectraTOF.cxx @@ -1534,10 +1534,9 @@ struct tofSpectra { continue; } // trackwoCut++; - if (std::abs(track.dcaXY()) > 0.05) { // Skipping tracks that don't pass the standard cuts - return; + if (!passesDCAxyCut(track)) { // Skipping tracks that don't pass the standard cuts + continue; } - // trackwCut++; const auto& mcLabel = mcTrackLabels.iteratorAt(track.globalIndex()); const auto& mcParticle = mcParticles.iteratorAt(mcLabel.mcParticleId()); @@ -1562,7 +1561,7 @@ struct tofSpectra { double rapidityKa = track.rapidity(PID::getMass(3)); double rapidityPr = track.rapidity(PID::getMass(4)); if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { - return; + continue; } if (mcParticle.isPhysicalPrimary()) { if (isTPCPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { @@ -1603,6 +1602,70 @@ struct tofSpectra { } // TOF Selection and Histogram Filling + // RL TOF selections: same cut for TRD as inclusive analysis. + if (!track.hasTOF()) { + continue; + } + + if (trkselOptions.requireTrdOnly && !track.hasTRD()) { + continue; + } + + if (trkselOptions.requireNoTrd && track.hasTRD()) { + continue; + } + + if (track.hasTRD() && trkselOptions.lastRequiredTrdCluster > 0) { + int lastLayer = 0; + for (int layer = 7; layer >= 0; --layer) { + if (track.trdPattern() & (1 << layer)) { + lastLayer = layer; + break; + } + } + + if (lastLayer < trkselOptions.lastRequiredTrdCluster) { + continue; + } + } + + // RL: add an selection in the selection event time + + switch (trkselOptions.selectEvTime) { + case 0: + break; // No selection + case 1: + if (!track.isEvTimeDefined()) { + continue; + } + break; + case 2: + if (!track.isEvTimeTOF()) { + continue; + } + break; + case 3: + if (!track.isEvTimeT0AC()) { + continue; + } + break; + case 4: + if (!track.isEvTimeTOFT0AC()) { + continue; + } + break; + case 5: + if (track.isEvTimeDefined()) { + continue; + } + break; + default: + LOG(fatal) << "Valore selectEvTime non riconosciuto: " + << trkselOptions.selectEvTime; + } + + // + if (isTOFPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kPiPlus) { @@ -2149,9 +2212,7 @@ struct tofSpectra { if (!mcParticle.isPhysicalPrimary()) { // Is not physical primary if (mcParticle.getProcess() == kPDecay) { // Is from decay if (includeCentralityMC) { - if (includeCentralityMC) { - histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity, track.dcaXY()); - } + histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity, track.dcaXY()); } else { histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity); } @@ -2686,8 +2747,6 @@ struct tofSpectra { } histos.fill(HIST("MC/Multiplicity"), getMultiplicityMC(mcCollision)); const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - bool hasParticleInFT0C = false; - bool hasParticleInFT0A = false; if (evselOptions.cfgINELCut.value == EvSelInelGt0Cut) { if (!o2::pwglf::isINELgt0mc(particlesInCollision, pdgDB)) { continue; @@ -2714,9 +2773,6 @@ struct tofSpectra { if (mcCollision.isInelGt1()) { histos.fill(HIST("MC/GenRecoCollisions"), 4.f); } - if (hasParticleInFT0C && hasParticleInFT0A) { - histos.fill(HIST("MC/GenRecoCollisions"), 5.f); - } } } PROCESS_SWITCH(tofSpectra, processMC, "Process MC", false); From 42b110bbd4696501a345694eda6a1d5e8e4a60ac Mon Sep 17 00:00:00 2001 From: Francesca Ercolessi Date: Fri, 6 Nov 2026 17:43:35 +0100 Subject: [PATCH 596/648] [PWGJE] Fix physical primary V0 selection in MC and add V0 jet spectra vs multiplicity task (#18594) --- PWGJE/Core/JetFindingUtilities.h | 3 + PWGJE/Tasks/CMakeLists.txt | 5 + PWGJE/Tasks/lfJetSpectra.cxx | 1346 ++++++++++++++++++++++++++++++ 3 files changed, 1354 insertions(+) create mode 100644 PWGJE/Tasks/lfJetSpectra.cxx diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index eeeae31e7c5..9f0d76c9c08 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -239,6 +239,9 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f int nSelectedV0s = 0; for (auto const& v0 : v0s) { if constexpr (jetv0utilities::isV0McTable()) { + if (!v0.isPhysicalPrimary()) { + continue; + } v0Mass = v0.m(); v0Y = v0.y(); } else { diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 8a0b245b11e..a580f482409 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -237,6 +237,11 @@ if(FastJet_FOUND) REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lf-jet-spectra + SOURCES lfJetSpectra.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-charged-qa SOURCES jetFinderQA.cxx REUSE_FROM JetSubstructureHFPCH diff --git a/PWGJE/Tasks/lfJetSpectra.cxx b/PWGJE/Tasks/lfJetSpectra.cxx new file mode 100644 index 00000000000..72fb9c3346f --- /dev/null +++ b/PWGJE/Tasks/lfJetSpectra.cxx @@ -0,0 +1,1346 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lfJetSpectra.cxx +/// \brief pT spectra of K0s and Lambda/AntiLambda in jets and in the underlying event +/// +/// \author Francesca Ercolessi + +#include "PWGJE/Core/FastJetUtilities.h" +#include "PWGJE/Core/JetCandidateUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetFinder.h" +#include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TableHelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// Jets reconstructed by the PWGJE V0 jet finders (jet-finder-v0-{data,mcd,mcp}-charged): charged tracks (particles) + V0 candidates, +// where the V0 daughters are removed from the track (particle) list and the V0 enters the clustering as a single constituent +using V0ChargedJetsWithConstituents = soa::Join; +using MCDV0JetsWithConstituents = soa::Join; +using MCPV0JetsWithConstituents = soa::Join; +// Jets geometrically matched between detector and particle level (PWGJE jet-matching-mc-v0-ch), for the jet pT response +using MatchedMCDV0Jets = soa::Join; +using MatchedMCPV0JetsWithConstituents = soa::Join; + +struct LfJetSpectra { + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + enum V0Species { kK0s = 0, + kLambda, + kAntiLambda }; + + // Steps of the V0 selection, in the order in which they are applied (bins of the cut flow histogram) + enum V0SelectionStep { kStepAll = 0, + kStepJetFinderAcceptance, + kStepNotRejected, + kStepSignalFlag, + kStepRadius, + kStepDcaDauToPV, + kStepCosPA, + kStepDcaV0Dau, + kStepDauEta, + kStepCompetingMass, + kNSelectionSteps }; + static constexpr std::array StepLabels = {"all", "jet finder acc.", "not rejected", "signal flag", "radius", "DCA dau-PV", "cos PA", "DCA V0 dau", "dau #eta", "competing mass"}; + + // Folders of the V0 QA histograms: all the stored V0s (before selections) and the selected V0s of each species. + // The first three entries are also the species names used in the MC histograms + enum V0QAFolder { kQAK0s = 0, + kQALambda, + kQAAntiLambda, + kQAAllV0s }; + static constexpr std::array QAFolders = {"K0s", "Lambda", "AntiLambda", "AllV0s"}; + + // MC histograms (one per species, axes: multiplicity, pT). + // Reconstructed V0s: true V0s (PDG of the V0 and of the daughters) passing the selections, at reconstructed pT. + // Generated V0s: physical primary V0s, at generated pT. In particle-level jets (kGenJet, kGenUE) they are the V0s + // clustered by the particle-level jet finder (aod::CandidatesV0MCP, all decay channels with v0ChargedDecaysOnly = false), + // with the same in-jet / UE definition as in data. Otherwise they are taken from aod::JetParticles (all decay channels, + // so the efficiency includes the branching ratio) and, for the reco jets, "in jet" means within R from the jet axis + enum MCHistogram { kRecJet = 0, // reco primary V0s, constituents of a reco jet (efficiency numerator, jet) + kRecUE, // reco primary V0s in the UE of a reco jet (efficiency numerator, UE) + kRecJetIncl, // as kRecJet, primaries + secondaries (feed-down / primary fraction) + kRecUEIncl, // as kRecUE, primaries + secondaries (feed-down / primary fraction) + kRecFullEvent, // reco primary V0s in all the reconstructed events (inclusive efficiency numerator) + kRecEventWithJet, // reco primary V0s in the reconstructed events with at least one selected jet + kGenRecoJetJet, // generated V0s within R from the axis of a reco jet (efficiency denominator, jet) + kGenRecoJetUE, // generated V0s in the UE of a reco jet (efficiency denominator, UE) + kGenRecoEvent, // generated V0s in the reconstructed events (inclusive efficiency denominator) + kGenEventWithJet, // generated V0s in the reconstructed events with at least one selected jet + kGenJet, // generated V0s, constituents of a particle-level jet + kGenUE, // generated V0s in the UE of a particle-level jet + kGenFullEvent, // generated V0s in all the generated events (signal loss) + kNMCHistograms }; + static constexpr std::array MCHistSuffixes = {"_reconstructed_jet", "_reconstructed_ue", "_reconstructed_jet_incl", "_reconstructed_ue_incl", "_reconstructed_fullevent", "_reconstructed_eventwjet", + "_generated_recojet_jet", "_generated_recojet_ue", "_generated_recoevent", "_generated_eventwjet", + "_generated_jet", "_generated_ue", "_generated_fullevent"}; + + // MC origin of a reconstructed V0, from the PDG code of the common mother of the two daughters (0 if there is none). + // "Fake" V0s (not a K0s, Lambda or AntiLambda) are clustered in the jets as the real ones and add their momentum to the jet + enum V0Origin { kOriginK0s = 0, + kOriginLambda, + kOriginAntiLambda, + kOriginGammaConversion, + kOriginOtherMother, + kOriginNoCommonMother, + kNV0Origins }; + static constexpr std::array OriginLabels = {"K^{0}_{S}", "#Lambda", "#bar{#Lambda}", "#gamma conversion", "other common mother", "no common mother"}; + + // Source of a real V0 (K0s, Lambda, AntiLambda) clustered in a reconstructed jet: physical primary or secondary, classified + // by the PDG code of the mother. At particle level only the physical primary V0s are clustered: the momentum + // of the V0s from Xi+- and Omega+- is carried by their mother, a charged primary, while the V0s from Xi0 and from the + // interactions in the material have no counterpart in the particle-level jets + enum V0Source { kSourcePrimary = 0, + kSourceXiCharged, + kSourceXi0, + kSourceOmega, + kSourceOtherSecondary, + kNV0Sources }; + static constexpr std::array SourceLabels = {"primary", "from #Xi^{#pm}", "from #Xi^{0}", "from #Omega^{#pm}", "other secondary"}; + + Configurable fillV0QAHistograms{"fillV0QAHistograms", true, "fill the V0 QA histograms (cut flow and topological variables before/after selections)"}; + + // Event selection + Configurable evSel{"evSel", "sel8WithoutTimeFrameBorderCut", "choose event selection"}; + Configurable vertexZCut{"vertexZCut", 10.f, "vertex z cut"}; + + // Jet selection. The anti-kT algorithm and the resolution parameter are set in the V0 jet finders + // (jetAlgorithm = 2, jetRadius = {0.4}); here only the jets with R = rJet are analysed + Configurable jetFinderTaskName{"jetFinderTaskName", "jet-finder-v0-data-charged", "name of the V0 jet finder task in the workflow for data, from which the event, track and V0 selections are read"}; + Configurable jetFinderMCDTaskName{"jetFinderMCDTaskName", "jet-finder-v0-mcd-charged", "name of the V0 jet finder task in the workflow for MC detector level"}; + Configurable jetFinderMCPTaskName{"jetFinderMCPTaskName", "jet-finder-v0-mcp-charged", "name of the V0 jet finder task in the workflow for MC particle level"}; + Configurable derivedDataProducerTaskName{"derivedDataProducerTaskName", "jet-deriveddata-producer", "name of the PWGJE derived data producer in the MC workflow (producing the particle-level V0 table)"}; + Configurable rJet{"rJet", 0.4, "Jet resolution parameter (R)"}; + Configurable minJetPt{"minJetPt", 10.0, "Minimum background-subtracted pt of the jet (GeV/c)"}; + Configurable maxJetEta{"maxJetEta", 0.5, "Maximum |eta| of the jet axis (should be <= 0.9 - R so that jet and UE cones are fully in acceptance)"}; + + // V0 selections, applied in the same way to the V0s in jets and in the UE + struct : ConfigurableGroup { + std::string prefix = "v0Cuts"; + Configurable requireV0SignalFlags{"requireV0SignalFlags", false, "require also the species flag of the v0-selector (K0S/Lambda/AntiLambda candidate)"}; + // topological selections + Configurable minimumV0Radius{"minimumV0Radius", 0.9, "Minimum V0 transverse decay radius (cm)"}; + Configurable maximumV0Radius{"maximumV0Radius", 40.0, "Maximum V0 transverse decay radius (cm)"}; + Configurable dcaDaughterToPVmin{"dcaDaughterToPVmin", 0.06, "Minimum DCA of the V0 daughters to the primary vertex (cm)"}; + Configurable v0cospaMin{"v0cospaMin", 0.99, "Minimum V0 cosine of pointing angle"}; + Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5, "Maximum DCA between the V0 daughters (cm)"}; + Configurable daughterEtaMax{"daughterEtaMax", 0.8, "Maximum |eta| of the V0 daughters"}; + // competing V0 mass rejection + Configurable doCompetingMassRejection{"doCompetingMassRejection", true, "Apply the competing V0 mass rejection"}; + Configurable lambdaRejWindow{"lambdaRejWindow", 0.005, "Lambda mass window rejected for K0s candidates (GeV/c^2)"}; + Configurable k0sRejWindow{"k0sRejWindow", 0.010, "K0s mass window rejected for (Anti)Lambda candidates (GeV/c^2)"}; + } v0Cuts; + + // MC options + Configurable inclusiveEtaMax{"inclusiveEtaMax", 0.8, "Maximum |eta| (generated) of the V0s in the inclusive (full event) MC histograms"}; + + // Multiplicity binning + ConfigurableAxis multBinning{"multBinning", {VARIABLE_WIDTH, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Binning of multiplicity axis"}; + + Service pdgDatabase; + + // phiExclusionMin at or below this value: no phi exclusion region (same convention as the V0 jet finder) + static constexpr float PhiExclusionUnset = -998.f; + + // Selections of a V0 jet finder, read from its configuration in init() so that the jet finder input + // (used for the rho estimate), the selected events and the V0s tested for the UE are exactly the ones of the clustering + struct JetFinderConfig { + std::string eventSelections = "sel8"; + bool skipMBGapEvents = true; + bool applyRCTSelections = true; + std::string trackSelections = "globalTracks"; + std::string particleSelections = "PhysicalPrimary"; + float trackPtMin = 0.15; + float trackPtMax = 1000.0; + float trackEtaMin = -0.9; + float trackEtaMax = 0.9; + float trackPhiMin = -999; + float trackPhiMax = 999; + float phiExclusionMin = -999; + float phiExclusionMax = 999; + bool isEmbedding = false; + float candPtMin = 0.0; + float candPtMax = 100.0; + float candYMin = -0.8; + float candYMax = 0.8; + float candPDG = static_cast(PDG_t::kK0Short); + bool useV0SignalFlags = true; + std::vector eventSelectionBits; + }; + JetFinderConfig jetFinderConfigDet; // data or MC detector level + JetFinderConfig jetFinderConfigGen; // MC particle level + int trackSelection = -1; + int candIndex = 0; + + std::vector eventSelectionBits; + std::vector inputParticles; + + Filter jetCollisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; + Filter jetMcCollisionFilter = nabs(aod::jmccollision::posZ) < vertexZCut; + + // Particle-level jet finder input and jets of the MC collision associated to a reconstructed collision (jet pT response) + Preslice v0MCPPerMcCollision = aod::jv0indices::mcCollisionId; + Preslice particlesPerMcCollision = aod::jmcparticle::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + + // Read the configuration of a V0 jet finder running in the same workflow + void readJetFinderConfig(InitContext& initContext, const std::string& taskName, JetFinderConfig& config, bool isParticleLevel) + { + auto readJetFinderOption = [&](const std::string& optName, auto& value) { + if (!o2::common::core::getTaskOptionValue(initContext, taskName, optName, value, false)) { + LOG(fatal) << "Option '" << optName << "' not found in task '" << taskName << "': the V0 jet finder must run in the same workflow"; + } + }; + readJetFinderOption("eventSelections", config.eventSelections); + readJetFinderOption("skipMBGapEvents", config.skipMBGapEvents); + readJetFinderOption("applyRCTSelections", config.applyRCTSelections); + readJetFinderOption("trackPtMin", config.trackPtMin); + readJetFinderOption("trackPtMax", config.trackPtMax); + readJetFinderOption("trackEtaMin", config.trackEtaMin); + readJetFinderOption("trackEtaMax", config.trackEtaMax); + readJetFinderOption("trackPhiMin", config.trackPhiMin); + readJetFinderOption("trackPhiMax", config.trackPhiMax); + readJetFinderOption("phiExclusionMin", config.phiExclusionMin); + readJetFinderOption("phiExclusionMax", config.phiExclusionMax); + readJetFinderOption("candPtMin", config.candPtMin); + readJetFinderOption("candPtMax", config.candPtMax); + readJetFinderOption("candYMin", config.candYMin); + readJetFinderOption("candYMax", config.candYMax); + if (isParticleLevel) { + readJetFinderOption("particleSelections", config.particleSelections); + } else { + readJetFinderOption("trackSelections", config.trackSelections); + readJetFinderOption("isEmbedding", config.isEmbedding); + readJetFinderOption("candPDG", config.candPDG); + readJetFinderOption("useV0SignalFlags", config.useV0SignalFlags); + } + config.eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(config.eventSelections); + + // Sanity checks on the jet finder settings needed by this analysis + int jetAlgorithm = -1; + readJetFinderOption("jetAlgorithm", jetAlgorithm); + if (jetAlgorithm != static_cast(fastjet::antikt_algorithm)) { + LOG(fatal) << taskName << " must use the anti-kT algorithm (jetAlgorithm = 2), found " << jetAlgorithm; + } + // jetRadius is read by hand: an array option is stored in the Variant as a pointer + size, so + // getTaskOptionValue with a std::vector reinterprets the pointer as a vector (std::bad_alloc) + std::vector jetRadii; + for (const auto& device : initContext.services().get().devices) { + if (device.name != taskName) { + continue; + } + for (const auto& option : device.options) { + if (option.name == "jetRadius") { + const double* radii = option.defaultValue.get(); + jetRadii.assign(radii, radii + option.defaultValue.size()); + } + } + } + if (jetRadii.empty()) { + LOG(fatal) << "Option 'jetRadius' not found in task '" << taskName << "': the V0 jet finder must run in the same workflow"; + } + if (std::none_of(jetRadii.begin(), jetRadii.end(), [&](double r) { return std::round(r * 100.0) == std::round(rJet * 100.0f); })) { + LOG(fatal) << "R = " << rJet.value << " is not among the jetRadius values of " << taskName; + } + bool saveJetsWithCandidatesOnly = true; + readJetFinderOption("saveJetsWithCandidatesOnly", saveJetsWithCandidatesOnly); + if (saveJetsWithCandidatesOnly) { + LOG(fatal) << taskName << " must run with saveJetsWithCandidatesOnly = false, otherwise jets without V0s are not stored and the jet and UE yields are biased"; + } + int ghostRepeat = 0; + readJetFinderOption("ghostRepeat", ghostRepeat); + if (ghostRepeat == 0) { + LOG(fatal) << taskName << " must run with ghostRepeat > 0, the jet area is needed for the background subtraction"; + } + } + + void init(InitContext& initContext) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); + + if (doprocessData && doprocessMCReco) { + LOG(fatal) << "processData and processMCReco cannot be enabled at the same time"; + } + if (doprocessData) { + readJetFinderConfig(initContext, jetFinderTaskName.value, jetFinderConfigDet, false); + } + if (doprocessData && (doprocessMCResponse || doprocessMCResponseGen)) { + LOG(fatal) << "processData and processMCResponse(Gen) cannot be enabled at the same time"; + } + if (doprocessMCReco || doprocessMCResponse || doprocessMCResponseGen) { + readJetFinderConfig(initContext, jetFinderMCDTaskName.value, jetFinderConfigDet, false); + } + if (doprocessMCGen || doprocessMCResponse || doprocessMCResponseGen) { + readJetFinderConfig(initContext, jetFinderMCPTaskName.value, jetFinderConfigGen, true); + // The particle-level V0 table must contain all the decay channels, so that all the generated V0s are clustered by + // the particle-level jet finder and the generated V0 constituents of the jets include the branching ratio + bool v0ChargedDecaysOnly = true; + if (!o2::common::core::getTaskOptionValue(initContext, derivedDataProducerTaskName.value, "v0ChargedDecaysOnly", v0ChargedDecaysOnly, false)) { + LOG(fatal) << "Option 'v0ChargedDecaysOnly' not found in task '" << derivedDataProducerTaskName.value << "': the PWGJE derived data producer must run in the same workflow"; + } + if (v0ChargedDecaysOnly) { + LOG(fatal) << derivedDataProducerTaskName.value << " must run with v0ChargedDecaysOnly = false, otherwise the V0s decaying to neutral particles are not clustered in the particle-level jets"; + } + } + trackSelection = jetderiveddatautilities::initialiseTrackSelection(jetFinderConfigDet.trackSelections); + candIndex = (jetFinderConfigDet.candPDG == static_cast(PDG_t::kLambda0)) ? 1 : 0; // same convention as the V0 jet finder + + const AxisSpec multAxis{multBinning, "FT0M percentile"}; + const AxisSpec ptAxis{100, 0.0, 10.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec invMassK0sAxis{200, 0.44, 0.56, "m_{#pi#pi} (GeV/#it{c}^{2})"}; + const AxisSpec invMassLambdaAxis{200, 1.09, 1.14, "m_{p#pi} (GeV/#it{c}^{2})"}; + const AxisSpec rhoAxis{200, 0., 20., "#rho (GeV/#it{c})"}; + + if (doprocessData) { + // Normalisation and jet QA + registryData.add("number_of_events_vsmultiplicity", "number of events in data vs multiplicity", HistType::kTH1D, {multAxis}); + registryData.add("number_of_jets_vsmultiplicity", "number of jets in data vs multiplicity", HistType::kTH1D, {multAxis}); + registryData.add("rhoPerpCone", "Perpendicular cone #rho", HistType::kTH2F, {multAxis, rhoAxis}); + registryData.add("jetPtRawVsSub", "Jets; #it{p}_{T}^{raw} (GeV/#it{c}); #it{p}_{T}^{sub} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, {220, -20., 200.}}); + registryData.add("jetPtEtaPhi", "Jets; #it{p}_{T}^{sub} (GeV/#it{c}); #eta; #phi", HistType::kTH3D, {{200, 0., 200.}, {20, -1.f, 1.f}, {18 * 8, 0.f, o2::constants::math::TwoPI}}); + + // V0s in jets and in the underlying event + registryData.add("Lambda_in_jet", "Lambda_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("AntiLambda_in_jet", "AntiLambda_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("Lambda_in_ue", "Lambda_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("AntiLambda_in_ue", "AntiLambda_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("K0s_in_jet", "K0s_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + registryData.add("K0s_in_ue", "K0s_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + } + + // V0 content of the selected jets (number of V0 constituents per jet vs background-subtracted jet pT) + const AxisSpec jetPtAxis{200, 0., 200., "#it{p}_{T,jet}^{sub} (GeV/#it{c})"}; + const AxisSpec nV0Axis{30, -0.5, 29.5, "number of V0s per jet"}; + if (doprocessData || doprocessMCReco) { + auto& registry = doprocessData ? registryData : registryMC; + registry.add("jetV0s/nV0Constituents", "V0 constituents (all V0s clustered by the jet finder)", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nK0sSelected", "V0 constituents passing the K^{0}_{S} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nLambdaSelected", "V0 constituents passing the #Lambda selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registry.add("jetV0s/nAntiLambdaSelected", "V0 constituents passing the #bar{#Lambda} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + } + if (doprocessMCReco) { + const AxisSpec originAxis{kNV0Origins, -0.5, static_cast(kNV0Origins) - 0.5, "MC origin"}; + const AxisSpec hypothesisAxis{3, -0.5, 2.5, "selection hypothesis"}; + const AxisSpec fractionAxis{100, 0., 1., "#Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw}"}; + registryMC.add("jetV0s/nRealV0Constituents", "real V0 constituents (K^{0}_{S}, #Lambda, #bar{#Lambda})", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nFakeV0Constituents", "fake V0 constituents", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueK0sSelected", "true K^{0}_{S} constituents passing the K^{0}_{S} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueLambdaSelected", "true #Lambda constituents passing the #Lambda selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/nTrueAntiLambdaSelected", "true #bar{#Lambda} constituents passing the #bar{#Lambda} selection", HistType::kTH2F, {jetPtAxis, nV0Axis}); + registryMC.add("jetV0s/fakeV0PtFraction", "fraction of the jet pT carried by fake V0 constituents", HistType::kTH2F, {jetPtAxis, fractionAxis}); + registryMC.add("jetV0s/fakeSelectedV0PtFraction", "fraction of the jet pT carried by fake V0 constituents passing at least one selection", HistType::kTH2F, {jetPtAxis, fractionAxis}); + // Mean fraction vs jet pT, filled with the exact value (not limited by the binning of the fraction axis) + registryMC.add("jetV0s/fakeV0PtFractionProfile", "mean fraction of the jet pT carried by fake V0 constituents;#it{p}_{T,jet}^{sub} (GeV/#it{c});#LT #Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile, {jetPtAxis}); + registryMC.add("jetV0s/fakeSelectedV0PtFractionProfile", "mean fraction of the jet pT carried by fake V0 constituents passing at least one selection;#it{p}_{T,jet}^{sub} (GeV/#it{c});#LT #Sigma#it{p}_{T}^{fake V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile, {jetPtAxis}); + registryMC.add("jetV0s/V0ConstituentOrigin", "MC origin of the V0 constituents", HistType::kTH3F, {jetPtAxis, ptAxis, originAxis}); + registryMC.add("jetV0s/SelectedV0ConstituentOrigin", "MC origin of the V0 constituents passing the selection", HistType::kTH3F, {hypothesisAxis, ptAxis, originAxis}); + auto hOrigin = registryMC.get(HIST("jetV0s/V0ConstituentOrigin")); + auto hSelectedOrigin = registryMC.get(HIST("jetV0s/SelectedV0ConstituentOrigin")); + hSelectedOrigin->GetXaxis()->SetBinLabel(1, "K^{0}_{S}"); + hSelectedOrigin->GetXaxis()->SetBinLabel(2, "#Lambda"); + hSelectedOrigin->GetXaxis()->SetBinLabel(3, "#bar{#Lambda}"); + for (int i = 0; i < kNV0Origins; i++) { + hOrigin->GetZaxis()->SetBinLabel(i + 1, std::string(OriginLabels[i]).c_str()); + hSelectedOrigin->GetZaxis()->SetBinLabel(i + 1, std::string(OriginLabels[i]).c_str()); + } + + // Real V0 constituents by source (primary / secondary from Xi+-, Xi0, Omega+-, other) and fraction of the (raw) jet pT + // they carry: the secondary ones are the detector-level counterpart of the momentum of their mother + const AxisSpec sourceAxis{kNV0Sources, -0.5, static_cast(kNV0Sources) - 0.5, "V0 source"}; + const AxisSpec sourceFractionAxis{100, 0., 1., "#Sigma#it{p}_{T}^{V0} / #it{p}_{T,jet}^{raw}"}; + registryMC.add("jetV0s/V0ConstituentSource", "source of the real V0 constituents", HistType::kTH3F, {jetPtAxis, ptAxis, sourceAxis}); + registryMC.add("jetV0s/V0SourcePtFraction", "fraction of the jet pT carried by the real V0 constituents of each source", HistType::kTH3F, {jetPtAxis, sourceAxis, sourceFractionAxis}); + registryMC.add("jetV0s/V0SourcePtFractionProfile", "mean fraction of the jet pT carried by the real V0 constituents of each source;#it{p}_{T,jet}^{sub} (GeV/#it{c});V0 source;#LT #Sigma#it{p}_{T}^{V0} / #it{p}_{T,jet}^{raw} #GT", HistType::kTProfile2D, {jetPtAxis, sourceAxis}); + for (const auto& axis : {registryMC.get(HIST("jetV0s/V0ConstituentSource"))->GetZaxis(), registryMC.get(HIST("jetV0s/V0SourcePtFraction"))->GetYaxis(), + registryMC.get(HIST("jetV0s/V0SourcePtFractionProfile"))->GetYaxis()}) { + for (int i = 0; i < kNV0Sources; i++) { + axis->SetBinLabel(i + 1, std::string(SourceLabels[i]).c_str()); + } + } + } + + // Jet pT response (detector vs particle level, geometrically matched jets with R = rJet and |eta| <= maxJetEta), with the + // background subtracted at both levels as in the analysis. The residual is also studied vs the fraction of the + // particle-level jet pT carried by Xi+- and Omega+-, whose momentum is carried by secondary V0s at detector level + const AxisSpec jetPtSubDetAxis{220, -20., 200., "#it{p}_{T,jet}^{sub, det} (GeV/#it{c})"}; + const AxisSpec jetPtSubGenAxis{220, -20., 200., "#it{p}_{T,jet}^{sub, gen} (GeV/#it{c})"}; + const AxisSpec residualAxis{200, -2., 2., "(#it{p}_{T,jet}^{det} - #it{p}_{T,jet}^{gen}) / #it{p}_{T,jet}^{gen}"}; + if (doprocessMCResponse) { + registryMC.add("jetResponse/ptSubDetVsGen", "jet pT response, background subtracted", HistType::kTH2F, {jetPtSubDetAxis, jetPtSubGenAxis}); + registryMC.add("jetResponse/ptRawDetVsGen", "jet pT response, no background subtraction;#it{p}_{T,jet}^{raw, det} (GeV/#it{c});#it{p}_{T,jet}^{raw, gen} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}); + registryMC.add("jetResponse/residualSub", "jet pT residual, background subtracted", HistType::kTH2F, {jetPtSubGenAxis, residualAxis}); + registryMC.add("jetResponse/residualRaw", "jet pT residual, no background subtraction;#it{p}_{T,jet}^{raw, gen} (GeV/#it{c})", HistType::kTH2F, {{200, 0., 200.}, residualAxis}); + registryMC.add("jetResponse/residualRawVsMultiStrangeFraction", "jet pT residual (no background subtraction) vs fraction of the particle-level jet pT carried by #Xi^{#pm} and #Omega^{#pm};#it{p}_{T,jet}^{raw, gen} (GeV/#it{c});#Sigma#it{p}_{T}^{#Xi^{#pm}, #Omega^{#pm}} / #it{p}_{T,jet}^{raw, gen}", + HistType::kTH3F, {{20, 0., 200.}, {50, 0., 0.5}, residualAxis}); + registryMC.add("jetResponse/detJets", "detector-level jets in acceptance", HistType::kTH1F, {jetPtSubDetAxis}); + registryMC.add("jetResponse/detJetsMatched", "detector-level jets in acceptance matched to a particle-level jet in acceptance", HistType::kTH1F, {jetPtSubDetAxis}); + } + if (doprocessMCResponseGen) { + registryMC.add("jetResponse/genJets", "particle-level jets in acceptance", HistType::kTH1F, {jetPtSubGenAxis}); + registryMC.add("jetResponse/genJetsMatched", "particle-level jets in acceptance matched to a detector-level jet in acceptance", HistType::kTH1F, {jetPtSubGenAxis}); + } + + // MC histograms for efficiency and corrections. The multiplicity is the generated FT0M percentile of the MC collision + if (doprocessMCReco) { + registryMC.add("number_of_events_vsmultiplicity_rec", "number of reconstructed events vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_events_wjet_vsmultiplicity_rec", "number of reconstructed events with at least one selected jet vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_jets_vsmultiplicity_rec", "number of reconstructed jets vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("rhoPerpCone_rec", "Perpendicular cone #rho, detector level", HistType::kTH2F, {multAxis, rhoAxis}); + } + if (doprocessMCGen) { + registryMC.add("number_of_events_vsmultiplicity_gen", "number of generated events vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("number_of_jets_vsmultiplicity_gen", "number of generated jets vs multiplicity", HistType::kTH1D, {multAxis}); + registryMC.add("rhoPerpCone_gen", "Perpendicular cone #rho, particle level", HistType::kTH2F, {multAxis, rhoAxis}); + } + for (int iHist = 0; iHist < kNMCHistograms; iHist++) { + const bool isGenLevelHistogram = (iHist == kGenJet || iHist == kGenUE || iHist == kGenFullEvent); + if ((isGenLevelHistogram && !doprocessMCGen) || (!isGenLevelHistogram && !doprocessMCReco)) { + continue; + } + for (int iSpecies = kK0s; iSpecies <= kAntiLambda; iSpecies++) { + const std::string name = std::string(QAFolders[iSpecies]) + std::string(MCHistSuffixes[iHist]); + registryMC.add(name.c_str(), name.c_str(), HistType::kTH2F, {multAxis, ptAxis}); + } + } + + // V0 QA + if (fillV0QAHistograms && (doprocessData || doprocessMCReco)) { + const std::array flagLabels = {"all", "K0S", "#Lambda", "#bar{#Lambda}", "rejected"}; + const int nFlagBins = static_cast(flagLabels.size()); + registryQC.add("QA/V0SignalFlags", "v0-selector flags of the stored V0s", HistType::kTH1D, {{nFlagBins, -0.5, nFlagBins - 0.5}}); + auto hFlags = registryQC.get(HIST("QA/V0SignalFlags")); + for (int i = 0; i < nFlagBins; i++) { + hFlags->GetXaxis()->SetBinLabel(i + 1, flagLabels[i].c_str()); + } + + registryQC.add("QA/V0CutFlow", "V0 cut flow", HistType::kTH2D, {{3, -0.5, 2.5}, {kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + auto hCutFlow = registryQC.get(HIST("QA/V0CutFlow")); + hCutFlow->GetXaxis()->SetBinLabel(1, "K^{0}_{S}"); + hCutFlow->GetXaxis()->SetBinLabel(2, "#Lambda"); + hCutFlow->GetXaxis()->SetBinLabel(3, "#bar{#Lambda}"); + for (int step = 0; step < kNSelectionSteps; step++) { + hCutFlow->GetYaxis()->SetBinLabel(step + 1, std::string(StepLabels[step]).c_str()); + } + + const AxisSpec radiusAxis{240, 0., 60., "V0 radius (cm)"}; + const AxisSpec dcaToPVAxis{200, 0., 1., "|DCA| to PV (cm)"}; + const AxisSpec cosPAAxis{250, 0.95, 1., "cos PA"}; + const AxisSpec dcaDauAxis{200, 0., 2., "DCA V0 daughters (cm)"}; + const AxisSpec etaAxis{100, -1., 1., "#eta_{daughter}"}; + const AxisSpec alphaAxis{200, -1., 1., "#alpha^{Arm}"}; + const AxisSpec qtAxis{150, 0., 0.3, "#it{q}_{T}^{Arm} (GeV/#it{c})"}; + const AxisSpec deltaMassAxis{200, 0., 0.1, "|m_{competing} - m_{PDG}| (GeV/#it{c}^{2})"}; + for (const auto& folder : QAFolders) { + const std::string dir = "QA/" + std::string(folder) + "/"; + registryQC.add((dir + "V0Radius").c_str(), "V0 radius", HistType::kTH2F, {ptAxis, radiusAxis}); + registryQC.add((dir + "DcaPosToPV").c_str(), "DCA positive daughter to PV", HistType::kTH2F, {ptAxis, dcaToPVAxis}); + registryQC.add((dir + "DcaNegToPV").c_str(), "DCA negative daughter to PV", HistType::kTH2F, {ptAxis, dcaToPVAxis}); + registryQC.add((dir + "V0CosPA").c_str(), "V0 cosine of pointing angle", HistType::kTH2F, {ptAxis, cosPAAxis}); + registryQC.add((dir + "DcaV0Daughters").c_str(), "DCA between V0 daughters", HistType::kTH2F, {ptAxis, dcaDauAxis}); + registryQC.add((dir + "PosDaughterEta").c_str(), "#eta of the positive daughter", HistType::kTH2F, {ptAxis, etaAxis}); + registryQC.add((dir + "NegDaughterEta").c_str(), "#eta of the negative daughter", HistType::kTH2F, {ptAxis, etaAxis}); + registryQC.add((dir + "Armenteros").c_str(), "Armenteros-Podolanski", HistType::kTH2F, {alphaAxis, qtAxis}); + } + registryQC.add("QA/K0s/Mass", "K^{0}_{S} invariant mass", HistType::kTH2F, {ptAxis, invMassK0sAxis}); + registryQC.add("QA/Lambda/Mass", "#Lambda invariant mass", HistType::kTH2F, {ptAxis, invMassLambdaAxis}); + registryQC.add("QA/AntiLambda/Mass", "#bar{#Lambda} invariant mass", HistType::kTH2F, {ptAxis, invMassLambdaAxis}); + registryQC.add("QA/K0s/CompetingMassDelta", "K^{0}_{S}: distance of m_{#Lambda} from the #Lambda mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + registryQC.add("QA/Lambda/CompetingMassDelta", "#Lambda: distance of m_{K^{0}_{S}} from the K^{0}_{S} mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + registryQC.add("QA/AntiLambda/CompetingMassDelta", "#bar{#Lambda}: distance of m_{K^{0}_{S}} from the K^{0}_{S} mass", HistType::kTH2F, {ptAxis, deltaMassAxis}); + } + } + + // --------------------------------------------------------------------------------------------------------------- + // Jet finder input, background and jet selection (common to data, MC detector level and MC particle level) + + // Event selection: the one of this task plus the one of the jet finder, so that events without jets are only + // the ones in which the jet finder did not find any jet + template + bool isSelectedCollision(T const& collision, JetFinderConfig const& config) + { + return jetderiveddatautilities::selectCollision(collision, eventSelectionBits) && + jetderiveddatautilities::selectCollision(collision, config.eventSelectionBits, config.skipMBGapEvents, config.applyRCTSelections); + } + + template + bool passedJetFinderKinematics(T const& particle, JetFinderConfig const& config) + { + if (particle.pt() < config.trackPtMin || particle.pt() >= config.trackPtMax || + particle.eta() < config.trackEtaMin || particle.eta() > config.trackEtaMax || + particle.phi() < config.trackPhiMin || particle.phi() > config.trackPhiMax) { + return false; + } + if (config.phiExclusionMin > PhiExclusionUnset && particle.phi() > config.phiExclusionMin && particle.phi() < config.phiExclusionMax) { + return false; + } + return true; + } + + // Track selection of the V0 jet finder (kinematic filter + track selection + removal of the V0 daughters) + template + bool isJetFinderTrack(T const& track, U const& v0s) + { + if (!passedJetFinderKinematics(track, jetFinderConfigDet)) { + return false; + } + if (!jetderiveddatautilities::selectTrack(track, trackSelection, jetFinderConfigDet.isEmbedding)) { + return false; + } + for (const auto& v0 : v0s) { + if (jetcandidateutilities::isDaughterTrack(track, v0)) { + return false; + } + } + return true; + } + + // Collect the indices of the V0 pseudojets (status candidate) in the jet finder input + std::unordered_set getJetFinderV0s() + { + std::unordered_set jetFinderV0s; + for (const auto& particle : inputParticles) { + if (particle.user_info().getStatus() == JetConstituentStatus::candidate) { + jetFinderV0s.insert(particle.user_info().getIndex()); + } + } + return jetFinderV0s; + } + + // Rebuild the detector-level jet finder input (V0s + tracks without V0 daughters), with the same selections used in the clustering. + // Returns the V0s accepted by the jet finder: only these can be jet constituents, so only these are tested for the UE + template + std::unordered_set buildDetectorLevelInput(TV0s const& v0s, TTracks const& tracks) + { + inputParticles.clear(); + jetfindingutilities::analyseV0s(inputParticles, v0s, jetFinderConfigDet.candPtMin, jetFinderConfigDet.candPtMax, jetFinderConfigDet.candYMin, jetFinderConfigDet.candYMax, candIndex, jetFinderConfigDet.useV0SignalFlags); + const auto jetFinderV0s = getJetFinderV0s(); + for (const auto& track : tracks) { + if (isJetFinderTrack(track, v0s)) { + fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); + } + } + return jetFinderV0s; + } + + // Rebuild the particle-level jet finder input (V0s + charged particles without V0 daughters), with the same selections used in the clustering. + // The secondary V0s (e.g. from Xi/Omega decays) are not clustered (analyseV0s keeps only the physical primary MC V0s): their + // momentum is already carried by the charged primary mother + template + std::unordered_set buildParticleLevelInput(TV0s const& v0s, TParticles const& particles) + { + inputParticles.clear(); + jetfindingutilities::analyseV0s(inputParticles, v0s, jetFinderConfigGen.candPtMin, jetFinderConfigGen.candPtMax, jetFinderConfigGen.candYMin, jetFinderConfigGen.candYMax, 0, false); + const auto jetFinderV0s = getJetFinderV0s(); + std::vector chargedParticles; + jetfindingutilities::analyseParticles(chargedParticles, jetFinderConfigGen.particleSelections, static_cast(JetType::charged), particles, pdgDatabase, &v0s); + for (const auto& particle : chargedParticles) { + if (passedJetFinderKinematics(particle, jetFinderConfigGen)) { + inputParticles.push_back(particle); + } + } + return jetFinderV0s; + } + + // Background: rho from the two cones perpendicular to the leading jet (PWGJE jetutilities::estimateRhoPerpCone), + // computed on the jet finder input. Returns a negative value if there are no jets with R = rJet + template + float estimateRho(TJets const& jets) + { + float leadingJetPt = -1.; + float leadingJetEta = 0.; + float leadingJetPhi = 0.; + for (const auto& jet : jets) { + if (jet.r() == std::round(rJet * 100.0f) && jet.pt() > leadingJetPt) { + leadingJetPt = jet.pt(); + leadingJetEta = jet.eta(); + leadingJetPhi = jet.phi(); + } + } + if (leadingJetPt < 0.) { + return -1.; + } + const auto [rho, rhoM] = jetutilities::estimateRhoPerpCone(inputParticles, fastjet::PtYPhiM(leadingJetPt, leadingJetEta, leadingJetPhi), rJet); + return rho; + } + + // Jet pT corrected for the background as pT,sub = pT,raw - rho * A + template + float subtractedPt(TJet const& jet, float rho) + { + return jet.pt() - rho * jet.area(); + } + + template + bool isJetAboveThreshold(TJet const& jet, float rho) + { + return jet.r() == std::round(rJet * 100.0f) && subtractedPt(jet, rho) >= minJetPt; + } + + template + bool isSelectedJet(TJet const& jet, float rho) + { + return isJetAboveThreshold(jet, rho) && std::abs(jet.eta()) <= maxJetEta; + } + + // Jet acceptance of the jet pT response (no pT threshold) + template + bool isInJetAcceptance(TJet const& jet) + { + return jet.r() == std::round(rJet * 100.0f) && std::abs(jet.eta()) <= maxJetEta; + } + + // Jets above the pT threshold after subtraction: their V0 constituents and their axes are used to exclude from the UE + // of all the other jets the V0s inside another jet (independently of the jet eta cut, to also catch jets partially + // overlapping with the UE cones) + template + void collectJetsAboveThreshold(TJets const& jets, float rho, std::unordered_set& v0sInJets, std::vector>& jetAxes) + { + for (const auto& jet : jets) { + if (!isJetAboveThreshold(jet, rho)) { + continue; + } + jetAxes.push_back({jet.eta(), jet.phi()}); + for (const auto& v0Id : jet.candidatesIds()) { + v0sInJets.insert(v0Id); + } + } + } + + // UE region: two cones of radius R centred on the axes perpendicular to the jet in azimuth (same eta, phi_jet +- pi/2) + template + bool isInUE(float eta, float phi, TJet const& jet) + { + return jetutilities::deltaR(eta, phi, jet.eta(), jet.phi() + o2::constants::math::PIHalf) < rJet || + jetutilities::deltaR(eta, phi, jet.eta(), jet.phi() - o2::constants::math::PIHalf) < rJet; + } + + bool isInAnyJetCone(float eta, float phi, std::vector> const& jetAxes) + { + return std::any_of(jetAxes.begin(), jetAxes.end(), [&](const auto& axis) { return jetutilities::deltaR(eta, phi, axis[0], axis[1]) < rJet; }); + } + + // --------------------------------------------------------------------------------------------------------------- + // V0 selection + + // Distance of the competing mass hypothesis from the PDG mass, used in the competing V0 mass rejection + template + float competingMassDelta(T const& v0, V0Species species) + { + if (species == kK0s) { + return std::min(std::abs(v0.mLambda() - o2::constants::physics::MassLambda), std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda)); + } + return std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short); + } + + // V0 selection for a given species hypothesis, applied step by step. Returns the last step passed + // (kStepCompetingMass = all the selections passed). It is used both for the selection and for the cut flow QA + template + int lastPassedSelectionStep(T const& v0, V0Species species, bool isJetFinderV0) + { + if (!isJetFinderV0) { + return kStepAll; + } + if (v0.isRejectedCandidate()) { + return kStepJetFinderAcceptance; + } + if (v0Cuts.requireV0SignalFlags) { + const bool hasFlag = (species == kK0s) ? v0.isK0SCandidate() : (species == kLambda) ? v0.isLambdaCandidate() + : v0.isAntiLambdaCandidate(); + if (!hasFlag) { + return kStepNotRejected; + } + } + if (v0.v0radius() < v0Cuts.minimumV0Radius || v0.v0radius() > v0Cuts.maximumV0Radius) { + return kStepSignalFlag; + } + if (std::abs(v0.dcapostopv()) < v0Cuts.dcaDaughterToPVmin || std::abs(v0.dcanegtopv()) < v0Cuts.dcaDaughterToPVmin) { + return kStepRadius; + } + if (v0.v0cosPA() < v0Cuts.v0cospaMin) { + return kStepDcaDauToPV; + } + if (std::abs(v0.dcaV0daughters()) > v0Cuts.dcaV0DaughtersMax) { + return kStepCosPA; + } + if (std::abs(v0.positiveeta()) > v0Cuts.daughterEtaMax || std::abs(v0.negativeeta()) > v0Cuts.daughterEtaMax) { + return kStepDcaV0Dau; + } + if (v0Cuts.doCompetingMassRejection && competingMassDelta(v0, species) < ((species == kK0s) ? v0Cuts.lambdaRejWindow : v0Cuts.k0sRejWindow)) { + return kStepDauEta; + } + return kStepCompetingMass; + } + + // Full selection of a V0 (accepted by the jet finder) for a given species hypothesis + template + bool passedV0Selection(T const& v0, V0Species species) + { + return lastPassedSelectionStep(v0, species, true) == kStepCompetingMass; + } + + // --------------------------------------------------------------------------------------------------------------- + // V0 QA + + // QA of the topological variables, for all the V0s (folder "AllV0s") or for the selected V0s of one species + template + void fillV0QAVariables(T const& v0) + { + constexpr auto Folder = HIST("QA/") + HIST(QAFolders[iFolder]); + registryQC.fill(Folder + HIST("/V0Radius"), v0.pt(), v0.v0radius()); + registryQC.fill(Folder + HIST("/DcaPosToPV"), v0.pt(), std::abs(v0.dcapostopv())); + registryQC.fill(Folder + HIST("/DcaNegToPV"), v0.pt(), std::abs(v0.dcanegtopv())); + registryQC.fill(Folder + HIST("/V0CosPA"), v0.pt(), v0.v0cosPA()); + registryQC.fill(Folder + HIST("/DcaV0Daughters"), v0.pt(), std::abs(v0.dcaV0daughters())); + registryQC.fill(Folder + HIST("/PosDaughterEta"), v0.pt(), v0.positiveeta()); + registryQC.fill(Folder + HIST("/NegDaughterEta"), v0.pt(), v0.negativeeta()); + registryQC.fill(Folder + HIST("/Armenteros"), v0.alpha(), v0.qtarm()); + if constexpr (iFolder == kQAK0s) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mK0Short()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kK0s)); + } else if constexpr (iFolder == kQALambda) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mLambda()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kLambda)); + } else if constexpr (iFolder == kQAAntiLambda) { + registryQC.fill(Folder + HIST("/Mass"), v0.pt(), v0.mAntiLambda()); + registryQC.fill(Folder + HIST("/CompetingMassDelta"), v0.pt(), competingMassDelta(v0, kAntiLambda)); + } + } + + // Inclusive V0 QA (once per V0 and per event, independently of the jets): v0-selector flags, topological variables + // before the selections, cut flow and topological variables after the selections for each species + template + void fillV0QA(T const& v0, bool isJetFinderV0) + { + registryQC.fill(HIST("QA/V0SignalFlags"), 0); + if (v0.isK0SCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 1); + } + if (v0.isLambdaCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 2); + } + if (v0.isAntiLambdaCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 3); + } + if (v0.isRejectedCandidate()) { + registryQC.fill(HIST("QA/V0SignalFlags"), 4); + } + fillV0QAVariables(v0); + + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + const int lastStep = lastPassedSelectionStep(v0, species, isJetFinderV0); + for (int step = kStepAll; step <= lastStep; step++) { + registryQC.fill(HIST("QA/V0CutFlow"), species, step); + } + if (lastStep != kStepCompetingMass) { + continue; + } + if (species == kK0s) { + fillV0QAVariables(v0); + } else if (species == kLambda) { + fillV0QAVariables(v0); + } else { + fillV0QAVariables(v0); + } + } + } + + // --------------------------------------------------------------------------------------------------------------- + // Data + + // Fill the V0 histograms for one region (in-jet or UE). The same selection is applied in the two regions. + // A V0 can satisfy more than one species hypothesis + template + void fillV0(T const& v0, float multiplicity) + { + if (passedV0Selection(v0, kK0s)) { + if constexpr (isInJet) { + registryData.fill(HIST("K0s_in_jet"), multiplicity, v0.pt(), v0.mK0Short()); + } else { + registryData.fill(HIST("K0s_in_ue"), multiplicity, v0.pt(), v0.mK0Short()); + } + } + if (passedV0Selection(v0, kLambda)) { + if constexpr (isInJet) { + registryData.fill(HIST("Lambda_in_jet"), multiplicity, v0.pt(), v0.mLambda()); + } else { + registryData.fill(HIST("Lambda_in_ue"), multiplicity, v0.pt(), v0.mLambda()); + } + } + if (passedV0Selection(v0, kAntiLambda)) { + if constexpr (isInJet) { + registryData.fill(HIST("AntiLambda_in_jet"), multiplicity, v0.pt(), v0.mAntiLambda()); + } else { + registryData.fill(HIST("AntiLambda_in_ue"), multiplicity, v0.pt(), v0.mAntiLambda()); + } + } + } + + void processData(soa::Filtered::iterator const& jcoll, V0ChargedJetsWithConstituents const& v0jets, aod::CandidatesV0Data const& v0s, aod::JetTracks const& tracks) + { + if (!isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const float multiplicity = jcoll.centFT0M(); + registryData.fill(HIST("number_of_events_vsmultiplicity"), multiplicity); + + const auto jetFinderV0s = buildDetectorLevelInput(v0s, tracks); + + // Inclusive V0 QA, filled once per V0 for all the selected events (also without jets) + if (fillV0QAHistograms) { + for (const auto& v0 : v0s) { + fillV0QA(v0, jetFinderV0s.contains(v0.globalIndex())); + } + } + + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryData.fill(HIST("rhoPerpCone"), multiplicity, rho); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + for (const auto& jet : v0jets) { + if (jet.r() == std::round(rJet * 100.0f)) { + registryData.fill(HIST("jetPtRawVsSub"), jet.pt(), subtractedPt(jet, rho)); + } + if (!isSelectedJet(jet, rho)) { + continue; + } + registryData.fill(HIST("number_of_jets_vsmultiplicity"), multiplicity); + registryData.fill(HIST("jetPtEtaPhi"), subtractedPt(jet, rho), jet.eta(), jet.phi()); + fillJetV0Content(jet, subtractedPt(jet, rho)); + + // V0-jet matching, in-jet region: a V0 is "in jet" if it is a constituent of the jet. + // The V0 jet finder clusters each V0 as a single pseudojet and removes its daughters from the track list, + // so the V0 is assigned to at most one jet and its daughters are never counted as separate jet constituents + for (const auto& v0 : jet.candidates_as()) { + fillV0(v0, multiplicity); + } + + // V0-jet matching, UE region: V0s in the perpendicular cones. The V0s are those accepted by the jet finder + // (same selection as the in-jet V0s), excluding the V0s that are constituents of another jet above threshold + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillV0(v0, multiplicity); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processData, "V0s in jets and in the UE in data", true); + + // --------------------------------------------------------------------------------------------------------------- + // MC + + template + void fillMCHistogram(float multiplicity, float pt, float weight) + { + registryMC.fill(HIST(QAFolders[iSpecies]) + HIST(MCHistSuffixes[iHist]), multiplicity, pt, weight); + } + + template + void fillMCHistogram(V0Species species, float multiplicity, float pt, float weight) + { + if (species == kK0s) { + fillMCHistogram(multiplicity, pt, weight); + } else if (species == kLambda) { + fillMCHistogram(multiplicity, pt, weight); + } else { + fillMCHistogram(multiplicity, pt, weight); + } + } + + // Reconstructed V0 matched to a true V0 of the given species: PDG code of the V0 and of the two daughters + template + bool isTrueV0(T const& v0, V0Species species) + { + switch (species) { + case kK0s: + return v0.pdgCode() == PDG_t::kK0Short && v0.pdgCodePositive() == PDG_t::kPiPlus && v0.pdgCodeNegative() == PDG_t::kPiMinus; + case kLambda: + return v0.pdgCode() == PDG_t::kLambda0 && v0.pdgCodePositive() == PDG_t::kProton && v0.pdgCodeNegative() == PDG_t::kPiMinus; + case kAntiLambda: + return v0.pdgCode() == PDG_t::kLambda0Bar && v0.pdgCodePositive() == PDG_t::kPiPlus && v0.pdgCodeNegative() == PDG_t::kProtonBar; + } + return false; + } + + // Species of a generated V0 (-1 if not a K0s, Lambda or AntiLambda) + template + int genV0Species(T const& v0) + { + switch (v0.pdgCode()) { + case PDG_t::kK0Short: + return kK0s; + case PDG_t::kLambda0: + return kLambda; + case PDG_t::kLambda0Bar: + return kAntiLambda; + } + return -1; + } + + // MC origin of a reconstructed V0, from the PDG code of the common mother of the two daughters (0 if there is none) + template + int v0Origin(T const& v0) + { + switch (v0.pdgCode()) { + case PDG_t::kK0Short: + return kOriginK0s; + case PDG_t::kLambda0: + return kOriginLambda; + case PDG_t::kLambda0Bar: + return kOriginAntiLambda; + case PDG_t::kGamma: + return kOriginGammaConversion; + case 0: + return kOriginNoCommonMother; // the two daughters have no common mother, e.g. one of them is a primary particle and the other is a secondary from a weak decay + } + return kOriginOtherMother; // the two daughters have a common mother, but it is not a K0s, Lambda, AntiLambda or gamma, e.g. material or multi-particle decay or + } + + // Source of a real V0 (see V0Source), from the PDG code of the mother of the V0 + template + int v0Source(T const& v0) + { + if (v0.isPhysicalPrimary()) { + return kSourcePrimary; + } + switch (std::abs(v0.pdgCodeMother())) { + case PDG_t::kXiMinus: + return kSourceXiCharged; + case o2::constants::physics::Pdg::kXi0: + return kSourceXi0; + case PDG_t::kOmegaMinus: + return kSourceOmega; + } + return kSourceOtherSecondary; + } + + // V0 content of a selected jet: number of V0 constituents (all, and passing the selection of each species). In MC also + // the number of real / fake constituents, the true selected constituents, the MC origin of the constituents and the + // fraction of the (raw) jet pT carried by fake V0s, which are clustered by the jet finder as the real ones + template + void fillJetV0Content(TJet const& jet, float ptSub, float weight = 1.) + { + int nV0s = 0; + int nReal = 0; + int nFake = 0; + std::array nSelected{0, 0, 0}; + std::array nTrueSelected{0, 0, 0}; + float fakePt = 0.; + float fakeSelectedPt = 0.; + std::array sourcePt{}; + for (const auto& v0 : jet.template candidates_as()) { + nV0s++; + bool passedAnySelection = false; + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + if (!passedV0Selection(v0, species)) { + continue; + } + passedAnySelection = true; + nSelected[species]++; + if constexpr (isMC) { + if (isTrueV0(v0, species)) { + nTrueSelected[species]++; + } + registryMC.fill(HIST("jetV0s/SelectedV0ConstituentOrigin"), species, v0.pt(), v0Origin(v0), weight); + } + } + if constexpr (isMC) { + const int origin = v0Origin(v0); + registryMC.fill(HIST("jetV0s/V0ConstituentOrigin"), ptSub, v0.pt(), origin, weight); + if (origin <= kOriginAntiLambda) { + nReal++; + const int source = v0Source(v0); + sourcePt[source] += v0.pt(); + registryMC.fill(HIST("jetV0s/V0ConstituentSource"), ptSub, v0.pt(), source, weight); + } else { + nFake++; + fakePt += v0.pt(); + if (passedAnySelection) { + fakeSelectedPt += v0.pt(); + } + } + } + } + + if constexpr (isMC) { + registryMC.fill(HIST("jetV0s/nV0Constituents"), ptSub, nV0s, weight); + registryMC.fill(HIST("jetV0s/nK0sSelected"), ptSub, nSelected[kK0s], weight); + registryMC.fill(HIST("jetV0s/nLambdaSelected"), ptSub, nSelected[kLambda], weight); + registryMC.fill(HIST("jetV0s/nAntiLambdaSelected"), ptSub, nSelected[kAntiLambda], weight); + registryMC.fill(HIST("jetV0s/nRealV0Constituents"), ptSub, nReal, weight); + registryMC.fill(HIST("jetV0s/nFakeV0Constituents"), ptSub, nFake, weight); + registryMC.fill(HIST("jetV0s/nTrueK0sSelected"), ptSub, nTrueSelected[kK0s], weight); + registryMC.fill(HIST("jetV0s/nTrueLambdaSelected"), ptSub, nTrueSelected[kLambda], weight); + registryMC.fill(HIST("jetV0s/nTrueAntiLambdaSelected"), ptSub, nTrueSelected[kAntiLambda], weight); + registryMC.fill(HIST("jetV0s/fakeV0PtFraction"), ptSub, fakePt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeSelectedV0PtFraction"), ptSub, fakeSelectedPt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeV0PtFractionProfile"), ptSub, fakePt / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/fakeSelectedV0PtFractionProfile"), ptSub, fakeSelectedPt / jet.pt(), weight); + for (int source = 0; source < kNV0Sources; source++) { + registryMC.fill(HIST("jetV0s/V0SourcePtFraction"), ptSub, source, sourcePt[source] / jet.pt(), weight); + registryMC.fill(HIST("jetV0s/V0SourcePtFractionProfile"), ptSub, source, sourcePt[source] / jet.pt(), weight); + } + } else { + registryData.fill(HIST("jetV0s/nV0Constituents"), ptSub, nV0s); + registryData.fill(HIST("jetV0s/nK0sSelected"), ptSub, nSelected[kK0s]); + registryData.fill(HIST("jetV0s/nLambdaSelected"), ptSub, nSelected[kLambda]); + registryData.fill(HIST("jetV0s/nAntiLambdaSelected"), ptSub, nSelected[kAntiLambda]); + } + } + + // Reconstructed true V0s passing the selections: primaries in iHist, primaries + secondaries in iHistIncl (if >= 0) + template + void fillMCRecoV0(T const& v0, float multiplicity, float weight) + { + for (const auto& species : {kK0s, kLambda, kAntiLambda}) { + if (!isTrueV0(v0, species) || !passedV0Selection(v0, species)) { + continue; + } + if constexpr (iHistIncl >= 0) { + fillMCHistogram(species, multiplicity, v0.pt(), weight); + } + if (v0.isPhysicalPrimary()) { + fillMCHistogram(species, multiplicity, v0.pt(), weight); + } + } + } + + // Generated physical primary V0s from the PWGJE reduced MC particle table + template + void fillMCGenV0(T const& v0, float multiplicity, float weight) + { + const int species = genV0Species(v0); + if (species < 0 || !v0.isPhysicalPrimary()) { + return; + } + fillMCHistogram(static_cast(species), multiplicity, v0.pt(), weight); + } + + // MC detector level: reconstructed V0s (efficiency numerators, feed-down) and generated V0s in the reconstructed + // events (efficiency denominators), in jets and in the UE of the reconstructed jets + void processMCReco(soa::Filtered::iterator const& jcoll, aod::JetMcCollisions const&, MCDV0JetsWithConstituents const& v0jets, + aod::CandidatesV0MCD const& v0s, aod::JetTracks const& tracks, aod::JetParticles const& jetParticles) + { + if (!jcoll.has_mcCollision() || !isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const auto mcCollision = jcoll.mcCollision_as(); + const float multiplicity = mcCollision.centFT0M(); + const float weight = mcCollision.weight(); + registryMC.fill(HIST("number_of_events_vsmultiplicity_rec"), multiplicity, weight); + + // Generated particles of the MC collision associated to the reconstructed collision + const auto genV0s = jetParticles.sliceBy(particlesPerMcCollision, mcCollision.globalIndex()); + + const auto jetFinderV0s = buildDetectorLevelInput(v0s, tracks); + + if (fillV0QAHistograms) { + for (const auto& v0 : v0s) { + fillV0QA(v0, jetFinderV0s.contains(v0.globalIndex())); + } + } + + // Inclusive efficiency: reconstructed and generated V0s with |eta_gen| < inclusiveEtaMax, in all the reconstructed events + for (const auto& v0 : v0s) { + if (std::abs(RecoDecay::eta(std::array{v0.pxMC(), v0.pyMC(), v0.pzMC()})) < inclusiveEtaMax) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryMC.fill(HIST("rhoPerpCone_rec"), multiplicity, rho, weight); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + bool hasSelectedJet = false; + for (const auto& jet : v0jets) { + if (!isSelectedJet(jet, rho)) { + continue; + } + hasSelectedJet = true; + registryMC.fill(HIST("number_of_jets_vsmultiplicity_rec"), multiplicity, weight); + fillJetV0Content(jet, subtractedPt(jet, rho), weight); + + // Reconstructed V0s, in-jet region: constituents of the reconstructed jet (same definition as in data) + for (const auto& v0 : jet.candidates_as()) { + fillMCRecoV0(v0, multiplicity, weight); + } + // Reconstructed V0s, UE region: same definition as in data + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + + // Generated V0s placed with respect to the reconstructed jet axis, using the generated momentum. The generated V0s + // are not jet constituents, so "in jet" means within R from the jet axis. In the UE, the generated V0s within R + // from the axis of another jet above threshold are excluded (as the reconstructed V0s constituents of another jet) + for (const auto& genV0 : genV0s) { + if (jetutilities::deltaR(genV0.eta(), genV0.phi(), jet.eta(), jet.phi()) < rJet) { + fillMCGenV0(genV0, multiplicity, weight); + } else if (isInUE(genV0.eta(), genV0.phi(), jet) && !isInAnyJetCone(genV0.eta(), genV0.phi(), jetAxes)) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + } + + // Inclusive efficiency in the events with at least one selected jet + if (hasSelectedJet) { + registryMC.fill(HIST("number_of_events_wjet_vsmultiplicity_rec"), multiplicity, weight); + for (const auto& v0 : v0s) { + if (std::abs(RecoDecay::eta(std::array{v0.pxMC(), v0.pyMC(), v0.pzMC()})) < inclusiveEtaMax) { + fillMCRecoV0(v0, multiplicity, weight); + } + } + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCReco, "V0s in jets and in the UE, MC detector level (efficiency and feed-down)", false); + + // MC particle level: generated V0s in particle-level jets and in their UE (defined as in data), and in the full event + void processMCGen(soa::Filtered::iterator const& mcCollision, MCPV0JetsWithConstituents const& v0jets, + aod::CandidatesV0MCP const& v0s, aod::JetParticles const& particles) + { + if (!jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float multiplicity = mcCollision.centFT0M(); + const float weight = mcCollision.weight(); + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen"), multiplicity, weight); + + const auto genV0s = particles.sliceBy(particlesPerMcCollision, mcCollision.globalIndex()); + for (const auto& genV0 : genV0s) { + if (std::abs(genV0.eta()) < inclusiveEtaMax) { + fillMCGenV0(genV0, multiplicity, weight); + } + } + + const auto jetFinderV0s = buildParticleLevelInput(v0s, particles); + const float rho = estimateRho(v0jets); + if (rho < 0.) { + return; + } + registryMC.fill(HIST("rhoPerpCone_gen"), multiplicity, rho, weight); + + std::unordered_set v0sInJets; + std::vector> jetAxes; + collectJetsAboveThreshold(v0jets, rho, v0sInJets, jetAxes); + + for (const auto& jet : v0jets) { + if (!isSelectedJet(jet, rho)) { + continue; + } + registryMC.fill(HIST("number_of_jets_vsmultiplicity_gen"), multiplicity, weight); + + // Generated V0s, in-jet region: constituents of the particle-level jet (anti-kT clustering of the V0s as single + // pseudojets, daughters removed from the particle list, as at detector level) + for (const auto& v0 : jet.candidates_as()) { + fillMCGenV0(v0, multiplicity, weight); + } + // Generated V0s, UE region: accepted by the particle-level jet finder and not constituents of another jet above threshold + for (const auto& v0 : v0s) { + if (!jetFinderV0s.contains(v0.globalIndex()) || v0sInJets.contains(v0.globalIndex())) { + continue; + } + if (isInUE(v0.eta(), v0.phi(), jet)) { + fillMCGenV0(v0, multiplicity, weight); + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCGen, "V0s in jets and in the UE, MC particle level", false); + + // --------------------------------------------------------------------------------------------------------------- + // Jet pT response (requires jet-matching-mc-v0-ch) + + // Fraction of the (raw) pT of a particle-level jet carried by Xi+- and Omega+- (charged primaries, clustered as single + // particles at particle level, while at detector level their momentum is carried by the secondary V0 and the bachelor) + template + float multiStrangePtFraction(TJet const& mcpJet) + { + float pt = 0.; + for (const auto& particle : mcpJet.template tracks_as()) { + const int pdg = std::abs(particle.pdgCode()); + if (pdg == PDG_t::kXiMinus || pdg == PDG_t::kOmegaMinus) { + pt += particle.pt(); + } + } + return pt / mcpJet.pt(); + } + + // Detector-level jets of the reconstructed collision and particle-level jets matched to them. The background is + // estimated at each level from the jet finder input of that level (particle level: MC collision associated to the + // reconstructed collision), as in processMCReco and processMCGen. Detector-level side: response and matching purity + void processMCResponse(soa::Filtered::iterator const& jcoll, aod::JetMcCollisions const&, MatchedMCDV0Jets const& mcdJets, + aod::CandidatesV0MCD const& v0s, aod::JetTracks const& tracks, + MatchedMCPV0JetsWithConstituents const& mcpJets, aod::CandidatesV0MCP const& v0sMCP, aod::JetParticles const& particles) + { + if (!jcoll.has_mcCollision() || !isSelectedCollision(jcoll, jetFinderConfigDet)) { + return; + } + const auto mcCollision = jcoll.mcCollision_as(); + if (std::abs(mcCollision.posZ()) >= vertexZCut || + !jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float weight = mcCollision.weight(); + + buildDetectorLevelInput(v0s, tracks); + const float rhoDet = estimateRho(mcdJets); + if (rhoDet < 0.) { + return; + } + const auto mcpJetsOfMcCollision = mcpJets.sliceBy(mcpJetsPerMcCollision, jcoll.mcCollisionId()); + buildParticleLevelInput(v0sMCP.sliceBy(v0MCPPerMcCollision, jcoll.mcCollisionId()), particles.sliceBy(particlesPerMcCollision, jcoll.mcCollisionId())); + const float rhoGen = estimateRho(mcpJetsOfMcCollision); // < 0 only if there are no particle-level jets, i.e. no matches + + for (const auto& mcdJet : mcdJets) { + if (!isInJetAcceptance(mcdJet)) { + continue; + } + const float ptSubDet = subtractedPt(mcdJet, rhoDet); + registryMC.fill(HIST("jetResponse/detJets"), ptSubDet, weight); + for (const auto& mcpJet : mcdJet.template matchedJetGeo_as()) { + if (!isInJetAcceptance(mcpJet)) { + continue; + } + const float ptSubGen = subtractedPt(mcpJet, rhoGen); + const float residualRaw = (mcdJet.pt() - mcpJet.pt()) / mcpJet.pt(); + registryMC.fill(HIST("jetResponse/detJetsMatched"), ptSubDet, weight); + registryMC.fill(HIST("jetResponse/ptSubDetVsGen"), ptSubDet, ptSubGen, weight); + registryMC.fill(HIST("jetResponse/ptRawDetVsGen"), mcdJet.pt(), mcpJet.pt(), weight); + if (ptSubGen > 0.) { + registryMC.fill(HIST("jetResponse/residualSub"), ptSubGen, (ptSubDet - ptSubGen) / ptSubGen, weight); + } + registryMC.fill(HIST("jetResponse/residualRaw"), mcpJet.pt(), residualRaw, weight); + registryMC.fill(HIST("jetResponse/residualRawVsMultiStrangeFraction"), mcpJet.pt(), multiStrangePtFraction(mcpJet), residualRaw, weight); + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCResponse, "jet pT response (detector-level side), requires jet-matching-mc-v0-ch", false); + + // Particle-level side of the jet pT response: matching efficiency of the particle-level jets in acceptance (matched to a + // detector-level jet in acceptance, in a reconstructed collision passing the event selection of processMCResponse) + void processMCResponseGen(soa::Filtered::iterator const& mcCollision, MatchedMCPV0JetsWithConstituents const& mcpJets, + aod::CandidatesV0MCP const& v0s, aod::JetParticles const& particles, + soa::SmallGroups const& collisions, MatchedMCDV0Jets const&) + { + if (!jetderiveddatautilities::selectCollision(mcCollision, jetFinderConfigGen.eventSelectionBits, jetFinderConfigGen.skipMBGapEvents, jetFinderConfigGen.applyRCTSelections)) { + return; + } + const float weight = mcCollision.weight(); + + // SmallGroups contains the reconstructed collisions linked to this JetMcCollision. + std::unordered_set selectedCollisions; + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) < vertexZCut && isSelectedCollision(collision, jetFinderConfigDet)) { + selectedCollisions.insert(collision.globalIndex()); + } + } + + buildParticleLevelInput(v0s, particles); + const float rho = estimateRho(mcpJets); + if (rho < 0.) { + return; + } + for (const auto& mcpJet : mcpJets) { + if (!isInJetAcceptance(mcpJet)) { + continue; + } + const float ptSub = subtractedPt(mcpJet, rho); + registryMC.fill(HIST("jetResponse/genJets"), ptSub, weight); + for (const auto& mcdJet : mcpJet.template matchedJetGeo_as()) { + if (isInJetAcceptance(mcdJet) && selectedCollisions.contains(mcdJet.collisionId())) { + registryMC.fill(HIST("jetResponse/genJetsMatched"), ptSub, weight); + break; + } + } + } + } + PROCESS_SWITCH(LfJetSpectra, processMCResponseGen, "jet pT response (particle-level side: matching efficiency), requires jet-matching-mc-v0-ch", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From ac6bd1b1010292271d80d59b0b4406e2a43eb630 Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Fri, 6 Nov 2026 17:47:59 +0100 Subject: [PATCH 597/648] [PWGHF] XicToXiPiPi: enable BDT selection in KFParticle mode (#18598) --- PWGHF/Core/HfMlResponseXicToXiPiPi.h | 83 +++++++++++++++++-- .../candidateSelectorXicToXiPiPi.cxx | 83 ++++++++++++++----- 2 files changed, 140 insertions(+), 26 deletions(-) diff --git a/PWGHF/Core/HfMlResponseXicToXiPiPi.h b/PWGHF/Core/HfMlResponseXicToXiPiPi.h index 4bb4a4d023f..6e8b598a166 100644 --- a/PWGHF/Core/HfMlResponseXicToXiPiPi.h +++ b/PWGHF/Core/HfMlResponseXicToXiPiPi.h @@ -17,19 +17,21 @@ #define PWGHF_CORE_HFMLRESPONSEXICTOXIPIPI_H_ #include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "Tools/ML/MlResponse.h" #include +#include +#include #include // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XICTOXIPIPI(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXicToXiPiPi::FEATURE) \ - } +#define FILL_MAP_XICTOXIPIPI(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesXicToXiPiPi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -88,10 +90,31 @@ enum class InputFeaturesXicToXiPiPi : uint8_t { nSigTofPiFromXicPlus1, nSigTofBachelorPi, nSigTofPiFromLambda, - nSigTofPrFromLambda + nSigTofPrFromLambda, + // kf-specific variables + kfDecayLength, + kfDecayLengthNormalised, + kfDecayLengthXY, + kfDecayLengthXYNormalised, + kfCascadeChi2, + kfV0Chi2, + chi2XicPlusToPV, + chi2TopoXicPlusToPV, + chi2XiToPV, + chi2Pi0ToPV, + chi2Pi1ToPV, + chi2DevPi0Pi1, + chi2DevPi0Xi, + chi2DevPi1Xi, + dcaPi0Pi1, + dcaPi0Xi, + dcaPi1Xi, + dcaXYPi0Pi1, + dcaXYPi0Xi, + dcaXYPi1Xi }; -template +template class HfMlResponseXicToXiPiPi : public HfMlResponse { public: @@ -147,6 +170,30 @@ class HfMlResponseXicToXiPiPi : public HfMlResponse CHECK_AND_FILL_VEC_XICTOXIPIPI(nSigTofPiFromLambda); CHECK_AND_FILL_VEC_XICTOXIPIPI(nSigTofPrFromLambda); } + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { + switch (idx) { + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLength); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthNormalised); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthXY); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfDecayLengthXYNormalised); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfCascadeChi2); + CHECK_AND_FILL_VEC_XICTOXIPIPI(kfV0Chi2); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2XicPlusToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2TopoXicPlusToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2XiToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2Pi0ToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2Pi1ToPV); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(chi2DevPi1Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaPi1Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi0Pi1); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi0Xi); + CHECK_AND_FILL_VEC_XICTOXIPIPI(dcaXYPi1Xi); + } + } } return inputFeatures; @@ -193,6 +240,30 @@ class HfMlResponseXicToXiPiPi : public HfMlResponse FILL_MAP_XICTOXIPIPI(nSigTofBachelorPi), FILL_MAP_XICTOXIPIPI(nSigTofPiFromLambda), FILL_MAP_XICTOXIPIPI(nSigTofPrFromLambda)}; + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { + std::map mapKfFeatures{ + FILL_MAP_XICTOXIPIPI(kfDecayLength), + FILL_MAP_XICTOXIPIPI(kfDecayLengthNormalised), + FILL_MAP_XICTOXIPIPI(kfDecayLengthXY), + FILL_MAP_XICTOXIPIPI(kfDecayLengthXYNormalised), + FILL_MAP_XICTOXIPIPI(kfCascadeChi2), + FILL_MAP_XICTOXIPIPI(kfV0Chi2), + FILL_MAP_XICTOXIPIPI(chi2XicPlusToPV), + FILL_MAP_XICTOXIPIPI(chi2TopoXicPlusToPV), + FILL_MAP_XICTOXIPIPI(chi2XiToPV), + FILL_MAP_XICTOXIPIPI(chi2Pi0ToPV), + FILL_MAP_XICTOXIPIPI(chi2Pi1ToPV), + FILL_MAP_XICTOXIPIPI(chi2DevPi0Pi1), + FILL_MAP_XICTOXIPIPI(chi2DevPi0Xi), + FILL_MAP_XICTOXIPIPI(chi2DevPi1Xi), + FILL_MAP_XICTOXIPIPI(dcaPi0Pi1), + FILL_MAP_XICTOXIPIPI(dcaPi0Xi), + FILL_MAP_XICTOXIPIPI(dcaPi1Xi), + FILL_MAP_XICTOXIPIPI(dcaXYPi0Pi1), + FILL_MAP_XICTOXIPIPI(dcaXYPi0Xi), + FILL_MAP_XICTOXIPIPI(dcaXYPi1Xi)}; + MlResponse::mAvailableInputFeatures.insert(mapKfFeatures.begin(), mapKfFeatures.end()); + } } }; diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 8972554209a..588dd15a1bb 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -92,7 +92,8 @@ struct HfCandidateSelectorXicToXiPiPi { Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::analysis::HfMlResponseXicToXiPiPi hfMlResponse; + o2::analysis::HfMlResponseXicToXiPiPi hfMlResponseDca; + o2::analysis::HfMlResponseXicToXiPiPi hfMlResponseKf; std::vector outputMlXicToXiPiPi; o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; @@ -121,9 +122,13 @@ struct HfCandidateSelectorXicToXiPiPi { void init(InitContext&) { - if ((doprocessData + doprocessMc) != 1) { + if ((doprocessDataWithDCAFitter + doprocessDataWithKFParticle + doprocessMcWithDCAFitter + doprocessMcWithKFParticle) != 1) { LOGP(fatal, "Enable exactly one process function at a time."); } + const bool doprocessData = doprocessDataWithDCAFitter || doprocessDataWithKFParticle; + const bool doprocessMc = doprocessMcWithDCAFitter || doprocessMcWithKFParticle; + const bool doprocessDCAFitter = doprocessDataWithDCAFitter || doprocessMcWithDCAFitter; + [[maybe_unused]] const bool doprocessKFParticle = doprocessDataWithKFParticle || doprocessMcWithKFParticle; if (usePid) { // pion @@ -251,15 +256,22 @@ struct HfCandidateSelectorXicToXiPiPi { } if (applyMl) { - hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); - if (loadModelsFromCCDB) { - ccdbApi.init(ccdbUrl); - hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + auto initMlResponse = [&](auto& hfMlResponseGeneric) { + hfMlResponseGeneric.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseGeneric.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseGeneric.setModelPathsLocal(onnxFileNames); + } + hfMlResponseGeneric.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseGeneric.init(); + }; + if (doprocessDCAFitter) { + initMlResponse(hfMlResponseDca); } else { - hfMlResponse.setModelPathsLocal(onnxFileNames); + initMlResponse(hfMlResponseKf); } - hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); - hfMlResponse.init(); } } @@ -669,7 +681,7 @@ struct HfCandidateSelectorXicToXiPiPi { /// \param hfCandXic Xic candidate /// \param statusXicToXiPiPi Flag to store selection status as defined in hf_sel_candidate_xic::XicToXiPiPiSelectionStep /// \param isMatchedSignal Flag to indicate if the candidate is matched to a genereated XiCplus MC particle - template + template void isBdtSelected(XicCandidate const& hfCandXic, int& statusXicToXiPiPi, bool const isMatchedSignal = false) @@ -677,8 +689,13 @@ struct HfCandidateSelectorXicToXiPiPi { bool isSelectedMlXicToXiPiPi = false; float const ptCandXic = hfCandXic.pt(); - std::vector inputFeaturesXicToXiPiPi = hfMlResponse.getInputFeatures(hfCandXic); - isSelectedMlXicToXiPiPi = hfMlResponse.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + if constexpr (ReconstructionType == aod::hf_cand::VertexerType::DCAFitter) { + std::vector inputFeaturesXicToXiPiPi = hfMlResponseDca.getInputFeatures(hfCandXic); + isSelectedMlXicToXiPiPi = hfMlResponseDca.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + } else { + std::vector inputFeaturesXicToXiPiPi = hfMlResponseKf.getInputFeatures(hfCandXic); + isSelectedMlXicToXiPiPi = hfMlResponseKf.isSelectedMl(inputFeaturesXicToXiPiPi, ptCandXic, outputMlXicToXiPiPi); + } hfMlXicToXiPiPiCandidate(outputMlXicToXiPiPi); @@ -699,8 +716,8 @@ struct HfCandidateSelectorXicToXiPiPi { } } - void processData(aod::HfCandXic const& hfCandsXic, - TracksExtraWPid const& tracks) + template + void runData(CandType const& hfCandsXic, TracksExtraWPid const& tracks) { for (const auto& hfCandXic : hfCandsXic) { int statusXicToXiPiPi = 0; @@ -719,7 +736,7 @@ struct HfCandidateSelectorXicToXiPiPi { // ML selection if (applyMl) { - isBdtSelected(hfCandXic, statusXicToXiPiPi); + isBdtSelected(hfCandXic, statusXicToXiPiPi); } else { registry.fill(HIST("hSelCandidates"), BdtSelected); } @@ -727,10 +744,9 @@ struct HfCandidateSelectorXicToXiPiPi { hfSelXicToXiPiPiCandidate(statusXicToXiPiPi); } } - PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processData, "Select candidates without MC matching information", true); - void processMc(soa::Join const& hfCandsXic, - TracksExtraWPid const& tracks) + template + void runMc(CandType const& hfCandsXic, TracksExtraWPid const& tracks) { for (const auto& hfCandXic : hfCandsXic) { int statusXicToXiPiPi = 0; @@ -754,7 +770,7 @@ struct HfCandidateSelectorXicToXiPiPi { // ML selection if (applyMl) { - isBdtSelected(hfCandXic, statusXicToXiPiPi, isMatchedCandidate); + isBdtSelected(hfCandXic, statusXicToXiPiPi, isMatchedCandidate); } else if (isMatchedCandidate) { registry.fill(HIST("hSelCandidatesRecSig"), BdtSelected); } else { @@ -764,7 +780,34 @@ struct HfCandidateSelectorXicToXiPiPi { hfSelXicToXiPiPiCandidate(statusXicToXiPiPi); } } - PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMc, "Select candidates with MC matching information", false); + + void processDataWithDCAFitter(aod::HfCandXic const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runData(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processDataWithDCAFitter, "Select candidates without MC matching information using DCAFitter", true); + + void processDataWithKFParticle(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runData(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processDataWithKFParticle, "Select candidates without MC matching information using KFParticle", false); + + void processMcWithDCAFitter(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runMc(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMcWithDCAFitter, "Select candidates with MC matching information using DCAFitter", false); + + void processMcWithKFParticle(soa::Join const& hfCandsXic, + TracksExtraWPid const& tracks) + { + runMc(hfCandsXic, tracks); + } + PROCESS_SWITCH(HfCandidateSelectorXicToXiPiPi, processMcWithKFParticle, "Select candidates with MC matching information using KFParticle", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 028908221c3c2b9c51228d97c637f1d55b72695a Mon Sep 17 00:00:00 2001 From: lucamicheletti93 <38209984+lucamicheletti93@users.noreply.github.com> Date: Fri, 6 Nov 2026 17:52:46 +0100 Subject: [PATCH 598/648] [Common] Fix for FwdTracksReAlign association (#18614) --- Common/Core/CollisionAssociation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/Core/CollisionAssociation.h b/Common/Core/CollisionAssociation.h index b0b3748817a..383a1ec10ce 100644 --- a/Common/Core/CollisionAssociation.h +++ b/Common/Core/CollisionAssociation.h @@ -273,8 +273,8 @@ class CollisionAssociation // then the track is an MFT track, or an MFT track with additionnal joined info // in this case TrackTimeResIsRange thresholdTime = trackTimeRes + mNumSigmaForTimeCompat * std::sqrt(collTimeRes2) + mTimeMargin; - } else if constexpr (TTracks::template contains()) { - // the track is a fwd track, with a gaussian time resolution + } else { + // the track is FwdTrack or FwdTracksReAlign, with a gaussian time resolution thresholdTime = mNumSigmaForTimeCompat * std::sqrt(sigmaTimeRes2) + mTimeMargin; } } From 900a17fe1f338bda95476b9de7f5afe3225b8ba6 Mon Sep 17 00:00:00 2001 From: Lucia Anna Tarasovicova Date: Fri, 6 Nov 2026 18:11:50 +0100 Subject: [PATCH 599/648] [ALICE3] Strangeness finder: gen daughter particle histograms (#18622) Co-authored-by: Lucia Anna Tarasovicova --- .../alice3TrackingTranslator.cxx | 1 - .../TableProducer/alice3strangenessFinder.cxx | 105 +++++++++++++----- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index 18f0386323e..e82d7f663de 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/ALICE3/TableProducer/alice3strangenessFinder.cxx b/ALICE3/TableProducer/alice3strangenessFinder.cxx index 27c69102c36..e70931f7c43 100644 --- a/ALICE3/TableProducer/alice3strangenessFinder.cxx +++ b/ALICE3/TableProducer/alice3strangenessFinder.cxx @@ -280,9 +280,17 @@ struct Alice3strangenessFinder { hPtRes1DPosDau->GetXaxis()->SetTitle("(p_{T}^{gen} - p_{T}^{reco})/p_{T}^{gen}"); auto hV0Building = histos.add("hV0Building", "hV0Building", kTH1D, {{10, 0.5, 10.5}}); - hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); - hV0Building->GetXaxis()->SetBinLabel(2, "Pdg check"); - hV0Building->GetXaxis()->SetBinLabel(3, "DCA Fitter"); + if (doprocessFindV0CandidateOTF || doprocessFindV0CandidateACTS) { + hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); + hV0Building->GetXaxis()->SetBinLabel(2, "Pdg check"); + hV0Building->GetXaxis()->SetBinLabel(3, "DCA Fitter"); + } + if (doprocessMCTrueFromACTS) { + hV0Building->GetXaxis()->SetBinLabel(1, "Pair"); + hV0Building->GetXaxis()->SetBinLabel(2, "PID"); + hV0Building->GetXaxis()->SetBinLabel(3, "Mother Check"); + hV0Building->GetXaxis()->SetBinLabel(4, "DCA Fitter"); + } auto hCascadeBuilding = histos.add("hCascadeBuilding", "hCascadeBuilding", kTH1D, {{10, 0.5, 10.5}}); hCascadeBuilding->GetXaxis()->SetBinLabel(1, "Attempts"); @@ -394,14 +402,17 @@ struct Alice3strangenessFinder { return returnValue; } template - bool checkSameMotherGen(TMCParticle const& mcParticle1, TMCParticle const& mcParticle2) + bool checkSameMotherGen(TMCParticle const& mcParticle1, TMCParticle const& mcParticle2, Alice3MCParticles const& mcParticles) { bool returnValue = false; if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { auto motherIds1 = mcParticle1.mothersIds(); auto motherIds2 = mcParticle2.mothersIds(); if (!motherIds1.empty() && !motherIds2.empty() && motherIds1[0] == motherIds2[0]) { - returnValue = true; + auto mother = mcParticles.rawIteratorAt(motherIds1[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + returnValue = true; + } } } // end association check return returnValue; @@ -778,7 +789,7 @@ struct Alice3strangenessFinder { } // end posTrack } - void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const&, aod::McParticles const&) + void processMCTrueFromACTS(aod::McCollision const& collision, Alice3MCParticles const& mcParticles) { auto negativeMCParticlesGrouped = negativeMCParticles->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); @@ -791,21 +802,66 @@ struct Alice3strangenessFinder { bool isLambda = false; bool isAntiLambda = false; int iPosPart = 0; + auto posMotherPdgCode = 0; + auto negMotherPdgCode = 0; for (auto const& posParticle : positiveMCParticlesGrouped) { - radiusPos = std::hypot(posParticle.vx(), posParticle.vy()); - histos.fill(HIST("hRadiusVsHitsPos"), radiusPos, posParticle.nHits()); + if (posParticle.pdgCode() != kPiPlus && posParticle.pdgCode() != kProton) { + continue; + } + + if (posParticle.has_mothers()) { + auto motherIds = posParticle.mothersIds(); + if (!motherIds.empty()) { + auto mother = mcParticles.rawIteratorAt(motherIds[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + radiusPos = std::hypot(posParticle.vx(), posParticle.vy()); + histos.fill(HIST("hRadiusVsHitsPos"), radiusPos, posParticle.nHits()); + posMotherPdgCode = mother.pdgCode(); + if (posMotherPdgCode == kK0Short) { + histos.fill(HIST("K0Short/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + if (posMotherPdgCode == kLambda0) { + histos.fill(HIST("Lambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + if (posMotherPdgCode == kLambda0Bar) { + histos.fill(HIST("AntiLambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); + } + } + } + } + for (auto const& negParticle : negativeMCParticlesGrouped) { if (negParticle.pdgCode() != kPiMinus && negParticle.pdgCode() != kProtonBar) { continue; } radiusNeg = std::hypot(negParticle.vx(), negParticle.vy()); if (iPosPart == 0) { - histos.fill(HIST("hRadiusVsHitsNeg"), radiusNeg, negParticle.nHits()); + if (negParticle.has_mothers()) { + auto motherIds = negParticle.mothersIds(); + if (!motherIds.empty()) { + auto mother = mcParticles.rawIteratorAt(motherIds[0] - mcParticles.rawIteratorAt(0).globalIndex()); + if (mother.isPhysicalPrimary()) { + radiusNeg = std::hypot(negParticle.vx(), negParticle.vy()); + histos.fill(HIST("hRadiusVsHitsNeg"), radiusNeg, negParticle.nHits()); + negMotherPdgCode = mother.pdgCode(); + if (negMotherPdgCode == kK0Short) { + histos.fill(HIST("K0Short/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (negMotherPdgCode == kLambda0) { + histos.fill(HIST("Lambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + if (negMotherPdgCode == kLambda0Bar) { + histos.fill(HIST("AntiLambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + } + } + } + } } if (radiusPos == radiusNeg) { - isK0s = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kPiMinus); - isLambda = (posParticle.pdgCode() == kProton && negParticle.pdgCode() == kPiMinus); - isAntiLambda = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kProtonBar); + histos.fill(HIST("hV0Building"), 1.0); + isK0s = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kPiMinus && posMotherPdgCode == kK0Short && negMotherPdgCode == kK0Short); + isLambda = (posParticle.pdgCode() == kProton && negParticle.pdgCode() == kPiMinus && posMotherPdgCode == kLambda0 && negMotherPdgCode == kLambda0); + isAntiLambda = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kProtonBar && posMotherPdgCode == kLambda0Bar && negMotherPdgCode == kLambda0Bar); if (isK0s || isLambda || isAntiLambda) { if (!isK0s && isK0Gun) { continue; @@ -813,19 +869,11 @@ struct Alice3strangenessFinder { if (!isLambda && isLambdaGun) { continue; } - if (isK0s && checkSameMotherGen(posParticle, negParticle)) { - histos.fill(HIST("K0Short/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); - histos.fill(HIST("K0Short/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); - } - if (isLambda && checkSameMotherGen(posParticle, negParticle)) { - histos.fill(HIST("Lambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); - histos.fill(HIST("Lambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); - } - if (isAntiLambda && checkSameMotherGen(posParticle, negParticle)) { - histos.fill(HIST("AntiLambda/hPosDaughterGenPtEta"), posParticle.pt(), posParticle.eta()); - histos.fill(HIST("AntiLambda/hNegDaughterGenPtEta"), negParticle.pt(), negParticle.eta()); + histos.fill(HIST("hV0Building"), 2.0); + if (mcSameMotherCheck && !checkSameMotherGen(posParticle, negParticle, mcParticles)) { + continue; } - + histos.fill(HIST("hV0Building"), 3.0); Candidate v0cand; std::vector v0DecayVertex; v0DecayVertex.push_back(negParticle.vx()); @@ -840,6 +888,7 @@ struct Alice3strangenessFinder { if (!buildDecayCandidateTwoBody(posParCov, negParCov, vtx, v0cand)) { continue; } + histos.fill(HIST("hV0Building"), 4.0); v0CandidateIndices(collision.globalIndex(), posParticle.globalIndex(), negParticle.globalIndex(), @@ -860,8 +909,10 @@ struct Alice3strangenessFinder { } } } + negMotherPdgCode = 0; } iPosPart++; + posMotherPdgCode = 0; } } template @@ -880,7 +931,7 @@ struct Alice3strangenessFinder { auto mcParticle = track.template mcParticle_as(); if (mcParticle.has_mothers()) { for (const auto& m1 : mcParticle.template mothers_as()) { - if (daughEff.analyseK0Daughters && m1.pdgCode() == kK0Short) { + if (daughEff.analyseK0Daughters && m1.pdgCode() == kK0Short && m1.isPhysicalPrimary()) { if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { histos.fill(HIST("K0Short/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); histos.fill(HIST("K0Short/hNegDaughterRecoPtEta"), pt, eta); @@ -894,7 +945,7 @@ struct Alice3strangenessFinder { histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); } } - if (daughEff.analyseLambdaDaughters && m1.pdgCode() == kLambda0) { + if (daughEff.analyseLambdaDaughters && m1.pdgCode() == kLambda0 && m1.isPhysicalPrimary()) { if (daughterType == 0 && mcParticle.pdgCode() == kPiMinus) { histos.fill(HIST("Lambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); histos.fill(HIST("Lambda/hNegDaughterRecoPtEta"), pt, eta); @@ -908,7 +959,7 @@ struct Alice3strangenessFinder { histos.fill(HIST("hPtRes1DPosDau"), (mcParticle.pt() - pt) / mcParticle.pt()); } } - if (daughEff.analyseAntiLambdaDaughters && m1.pdgCode() == kLambda0Bar) { + if (daughEff.analyseAntiLambdaDaughters && m1.pdgCode() == kLambda0Bar && m1.isPhysicalPrimary()) { if (daughterType == 0 && mcParticle.pdgCode() == kProtonBar) { histos.fill(HIST("AntiLambda/hNegDaughterRecoTruePtEta"), mcParticle.pt(), mcParticle.eta()); histos.fill(HIST("AntiLambda/hNegDaughterRecoPtEta"), pt, eta); From f19536b1ae294f777adbfd862ddc1eb6748b78d5 Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Fri, 6 Nov 2026 19:40:40 +0100 Subject: [PATCH 600/648] [PWGHF] Fix chi2 selection in candidateCreatorBToJpsiReduced (#18628) --- PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx index 25430a1ce18..b2b550aefb1 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx @@ -129,7 +129,7 @@ struct HfCandidateCreatorBToJpsiReduced { df4.setMaxDZIni(maxDZIni); df4.setMinParamChange(minParamChange); df4.setMinRelChi2Change(minRelChi2Change); - df3.setMaxChi2(maxChi2BhadVtx); + df4.setMaxChi2(maxChi2BhadVtx); df4.setUseAbsDCA(useAbsDCA); df4.setWeightedFinalPCA(useWeightedFinalPCA); df4.setMatCorrType(noMatCorr); From 608f8c604a0c89bb1bdcd8ab106657a6240361dc Mon Sep 17 00:00:00 2001 From: Anton Riedel <85537041+ariedel-cern@users.noreply.github.com> Date: Fri, 6 Nov 2026 20:39:59 +0100 Subject: [PATCH 601/648] [PWGCF] Fix process functions in collision tagger in femto framework (#18631) Co-authored-by: ALICE Action Bot --- PWGCF/Femto/Core/collisionTagger.h | 8 +- .../femtoProducerCollisionTagger.cxx | 442 ++++++++++-------- 2 files changed, 264 insertions(+), 186 deletions(-) diff --git a/PWGCF/Femto/Core/collisionTagger.h b/PWGCF/Femto/Core/collisionTagger.h index 931be1a0f02..4372aadbd15 100644 --- a/PWGCF/Femto/Core/collisionTagger.h +++ b/PWGCF/Femto/Core/collisionTagger.h @@ -75,7 +75,7 @@ struct TrackSelectionValues { // V0-specific: MAKE_CASCADE_PARTITION, MAKE_SIGMA_PARTITION, MAKE_SIGMAPLUS_PARTITION, // MAKE_D0_PARTITION, and MAKE_CHARM3PRONG_PARTITION in Core/partitions.h are all byte-for-byte // identical to MAKE_LAMBDA_PARTITION, and CascadeMaskType/KinkMaskType/CharmHadronMaskType are -// all, like V0MaskType, uint32_t -- so this one struct (and lambdaPasses()/readV0Selection() +// all, like V0MaskType, uint32_t -- so this one struct (and signedV0Passes()/readV0Selection() // below) is reused verbatim for Xi/Omega (cascade), Sigma/SigmaPlus (kink), and D0/Lc // (charm hadron) selections too, not just Lambda. Only SigmaPlusPcm needs its own struct // (KinkPcmSelectionValues below), for the one extra mlScoreMin field it has beyond this shape. @@ -506,7 +506,7 @@ bool trackPasses(T const& track, TrackSelectionValues const& sel) // as-is for Xi/Omega, Sigma/SigmaPlus, and D0/Lc candidates too (see the comment on // V0SelectionValues above). template -bool lambdaPasses(T const& v0, V0SelectionValues const& sel) +bool signedV0Passes(T const& v0, V0SelectionValues const& sel) { if (sel.sign != 0 && ((sel.sign > 0) != (v0.signedPt() > 0.f))) { return false; @@ -527,7 +527,7 @@ bool lambdaPasses(T const& v0, V0SelectionValues const& sel) return (v0.mask() & sel.mask) == sel.mask; } -// mirrors MAKE_SIGMAPLUSPCM_PARTITION(selection) in Core/partitions.h -- lambdaPasses() above +// mirrors MAKE_SIGMAPLUSPCM_PARTITION(selection) in Core/partitions.h -- signedV0Passes() above // plus the one extra mlScoreMin check. template bool kinkPcmPasses(T const& kink, KinkPcmSelectionValues const& sel) @@ -654,7 +654,7 @@ using CollisionTagBitset = std::bitset; // true if at least `needed` entries in `range` satisfy `passes(entry, sel)`. The one primitive // every per-collision tagging check below is built from; a new particle type only ever needs a -// new `passes` function (trackPasses/lambdaPasses/k0shortPasses above are examples), never a new +// new `passes` function (trackPasses/signedV0Passes/k0shortPasses above are examples), never a new // copy of this loop. template inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn const& passes, int needed = 1) diff --git a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx index dc618f158d7..788a8588cd9 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx @@ -126,7 +126,7 @@ struct FemtoProducerCollisionTagger { // enabled, each would try to write its own row for the same collision. Until FColTags is // extended to carry one column per wagon type, only one process function may run at a time -- // run separate tagger instances (with separate output files/merges) for separate wagon types. - int activeProcesses = static_cast(doprocessTrackTrack) + static_cast(doprocessTrackV0) + static_cast(doprocessTrackCascade) + static_cast(doprocessTrackKink) + static_cast(doprocessTrackSigmaPlusPcm) + static_cast(doprocessTrackD0) + static_cast(doprocessTrackLc) + static_cast(doprocessV0V0) + static_cast(doprocessD0D0) + static_cast(doprocessTrackTwoTrackResonance) + static_cast(doprocessTrackTwoPhotonResonance) + static_cast(doprocessV0TwoTrackResonance) + static_cast(doprocessTripletTrackTrackTrack) + static_cast(doprocessTripletTrackTrackV0) + static_cast(doprocessTripletTrackTrackCascade); + int activeProcesses = static_cast(doprocessTrackTrack) + static_cast(doprocessTrackLambda) + static_cast(doprocessTrackK0short) + static_cast(doprocessTrackXi) + static_cast(doprocessTrackOmega) + static_cast(doprocessTrackSigma) + static_cast(doprocessTrackSigmaPlus) + static_cast(doprocessTrackSigmaPlusPcm) + static_cast(doprocessTrackD0) + static_cast(doprocessTrackLc) + static_cast(doprocessLambdaLambda) + static_cast(doprocessK0shortK0short) + static_cast(doprocessLambdaK0short) + static_cast(doprocessD0D0) + static_cast(doprocessTrackTwoTrackResonance) + static_cast(doprocessTrackTwoPhotonResonance) + static_cast(doprocessLambdaTwoTrackResonance) + static_cast(doprocessK0shortTwoTrackResonance) + static_cast(doprocessTripletTrackTrackTrack) + static_cast(doprocessTripletTrackTrackV0) + static_cast(doprocessTripletTrackTrackXi) + static_cast(doprocessTripletTrackTrackOmega); if (activeProcesses != 1) { LOG(fatal) << "Exactly one process function must be enabled on the collision tagger, found " << activeProcesses << ". Breaking..."; } @@ -168,51 +168,51 @@ struct FemtoProducerCollisionTagger { v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readTrackSelection(device, "TrackSelection2"); v.bit = r.bit; mTrackTrackVariants.push_back(v); - } else if (doprocessTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos) { + } else if (doprocessTrackLambda && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackLambdaPair)) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == static_cast(collisiontagger::role::TrackLambdaPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); - v.bit = r.bit; - mTrackLambdaVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::TrackK0shortPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); - v.bit = r.bit; - mTrackK0shortVariants.push_back(v); - } - } else if (doprocessTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackLambdaVariants.push_back(v); + } else if (doprocessTrackK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackK0shortPair)) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == static_cast(collisiontagger::role::TrackXiPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); - v.bit = r.bit; - mTrackXiVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::TrackOmegaPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); - v.bit = r.bit; - mTrackOmegaVariants.push_back(v); - } - } else if ((doprocessTrackKink || doprocessTrackSigmaPlusPcm) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.bit = r.bit; + mTrackK0shortVariants.push_back(v); + } else if (doprocessTrackXi && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackXiPair)) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == static_cast(collisiontagger::role::TrackSigmaPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); - v.bit = r.bit; - mTrackSigmaVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPair)) { - collisiontagger::TrackV0Variant v; - v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); - v.bit = r.bit; - mTrackSigmaPlusVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPcmPair)) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackXiVariants.push_back(v); + } else if (doprocessTrackOmega && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackOmegaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackOmegaVariants.push_back(v); + } else if (doprocessTrackSigma && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackSigmaPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); + v.bit = r.bit; + mTrackSigmaVariants.push_back(v); + } else if (doprocessTrackSigmaPlus && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackSigmaPlusPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); + v.bit = r.bit; + mTrackSigmaPlusVariants.push_back(v); + } else if (doprocessTrackSigmaPlusPcm && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::TrackSigmaPlusPcmPair)) { collisiontagger::TrackKinkPcmVariant v; v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); v.sel2 = collisiontagger::readKinkPcmSelection(device, "SigmaPlusPcmSelection1"); @@ -234,32 +234,32 @@ struct FemtoProducerCollisionTagger { v.bit = r.bit; mTrackLcVariants.push_back(v); } - } else if (doprocessV0V0 && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos) { + } else if (doprocessLambdaLambda && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::LambdaLambdaPair)) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); - if (r.role == static_cast(collisiontagger::role::LambdaLambdaPair)) { - collisiontagger::V0V0Variant v; - v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); - v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); - // Mirror PairV0V0Builder::processMixedEvent(), which uses partition1 for BOTH roles and - // never consults LambdaSelection2 at all when sameSpecies -- same override as - // TrackTrackPair above. - v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); - v.bit = r.bit; - mLambdaLambdaVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::K0shortK0shortPair)) { - collisiontagger::V0V0Variant v; - v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); - v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); - v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); - v.bit = r.bit; - mK0shortK0shortVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::LambdaK0shortPair)) { - collisiontagger::V0V0Variant v; - v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); - v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); - v.bit = r.bit; - mLambdaK0shortVariants.push_back(v); - } + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // Mirror PairV0V0Builder::processMixedEvent(), which uses partition1 for BOTH roles and + // never consults LambdaSelection2 at all when sameSpecies -- same override as + // TrackTrackPair above. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); + v.bit = r.bit; + mLambdaLambdaVariants.push_back(v); + } else if (doprocessK0shortK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::K0shortK0shortPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mK0shortK0shortVariants.push_back(v); + } else if (doprocessLambdaK0short && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos && r.role == static_cast(collisiontagger::role::LambdaK0shortPair)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mLambdaK0shortVariants.push_back(v); } else if (doprocessD0D0 && r.deviceName.find(collisiontagger::devicename::FemtoPairD0D0) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); collisiontagger::V0V0Variant v; @@ -309,7 +309,7 @@ struct FemtoProducerCollisionTagger { v.bit = r.bit; mTrackEtaVariants.push_back(v); } - } else if (doprocessV0TwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + } else if (doprocessLambdaTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); if (r.role == static_cast(collisiontagger::role::LambdaPhiPair)) { collisiontagger::V0TwoTrackResonanceVariant v; @@ -329,7 +329,10 @@ struct FemtoProducerCollisionTagger { v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); v.bit = r.bit; mLambdaRho0Variants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::K0shortPhiPair)) { + } + } else if (doprocessK0shortTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == static_cast(collisiontagger::role::K0shortPhiPair)) { collisiontagger::V0TwoTrackResonanceVariant v; v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); @@ -374,29 +377,30 @@ struct FemtoProducerCollisionTagger { v.sel3 = collisiontagger::readV0Selection(device, "LambdaSelection1"); v.bit = r.bit; mTrackTrackLambdaVariants.push_back(v); - } else if (doprocessTripletTrackTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + } else if (doprocessTripletTrackTrackXi && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackTrackXiTriplet)) { o2::framework::DeviceSpec const& device = findDevice(r.deviceName); collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); - collisiontagger::TrackSelectionValues sel2 = same12 ? sel1 : sel2Raw; - if (r.role == static_cast(collisiontagger::role::TrackTrackXiTriplet)) { - collisiontagger::TrackTrackV0Variant v; - v.sel1 = sel1; - v.sel2 = sel2; - v.same12 = same12; - v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); - v.bit = r.bit; - mTrackTrackXiVariants.push_back(v); - } else if (r.role == static_cast(collisiontagger::role::TrackTrackOmegaTriplet)) { - collisiontagger::TrackTrackV0Variant v; - v.sel1 = sel1; - v.sel2 = sel2; - v.same12 = same12; - v.sel3 = collisiontagger::readV0Selection(device, "OmegaSelection"); - v.bit = r.bit; - mTrackTrackOmegaVariants.push_back(v); - } + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = same12 ? sel1 : sel2Raw; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackTrackXiVariants.push_back(v); + } else if (doprocessTripletTrackTrackOmega && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos && r.role == static_cast(collisiontagger::role::TrackTrackOmegaTriplet)) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = same12 ? sel1 : sel2Raw; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackTrackOmegaVariants.push_back(v); } // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the // same pattern. @@ -405,17 +409,29 @@ struct FemtoProducerCollisionTagger { if (doprocessTrackTrack && mTrackTrackVariants.empty()) { LOG(fatal) << "processTrackTrack is enabled, but no femto-pair-track-track device was found in this workflow."; } - if (doprocessTrackV0 && mTrackLambdaVariants.empty() && mTrackK0shortVariants.empty()) { - LOG(fatal) << "processTrackV0 is enabled, but no femto-pair-track-v0 device (with processLambdaMixedEventWithTagging " - << "or processK0shortMixedEventWithTagging enabled) was found in this workflow."; + if (doprocessTrackLambda && mTrackLambdaVariants.empty()) { + LOG(fatal) << "processTrackLambda is enabled, but no femto-pair-track-v0 device (with processLambdaMixedEventWithTagging " + << "enabled) was found in this workflow."; } - if (doprocessTrackCascade && mTrackXiVariants.empty() && mTrackOmegaVariants.empty()) { - LOG(fatal) << "processCascade is enabled, but no femto-pair-track-cascade device (with processXiMixedEventWithTagging " - << "or processOmegaMixedEventWithTagging enabled) was found in this workflow."; + if (doprocessTrackK0short && mTrackK0shortVariants.empty()) { + LOG(fatal) << "processTrackK0short is enabled, but no femto-pair-track-v0 device (with processK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; } - if (doprocessTrackKink && mTrackSigmaVariants.empty() && mTrackSigmaPlusVariants.empty()) { - LOG(fatal) << "processTrackKink is enabled, but no femto-pair-track-kink device (with processSigmaMixedEventWithTagging/" - << "processSigmaPlusMixedEventWithTagging enabled) was found in this workflow."; + if (doprocessTrackXi && mTrackXiVariants.empty()) { + LOG(fatal) << "processTrackXi is enabled, but no femto-pair-track-cascade device (with processXiMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackOmega && mTrackOmegaVariants.empty()) { + LOG(fatal) << "processTrackOmega is enabled, but no femto-pair-track-cascade device (with processOmegaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackSigma && mTrackSigmaVariants.empty()) { + LOG(fatal) << "processTrackSigma is enabled, but no femto-pair-track-kink device (with processSigmaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackSigmaPlus && mTrackSigmaPlusVariants.empty()) { + LOG(fatal) << "processTrackSigmaPlus is enabled, but no femto-pair-track-kink device (with processSigmaPlusMixedEventWithTagging " + << "enabled) was found in this workflow."; } if (doprocessTrackSigmaPlusPcm && mTrackSigmaPlusPcmVariants.empty()) { LOG(fatal) << "processTrackSigmaPlusPcm is enabled, but no femto-pair-track-kink device (with " @@ -429,9 +445,17 @@ struct FemtoProducerCollisionTagger { LOG(fatal) << "processTrackLc is enabled, but no femto-pair-track-charm-hadron device (with processLcMixedEventWithTagging " << "enabled) was found in this workflow."; } - if (doprocessV0V0 && mLambdaLambdaVariants.empty() && mK0shortK0shortVariants.empty() && mLambdaK0shortVariants.empty()) { - LOG(fatal) << "processV0V0 is enabled, but no femto-pair-v0-v0 device (with processLambdaLambdaMixedEventWithTagging/" - << "processK0shortK0shortMixedEventWithTagging/processLambdaK0shortMixedEventWithTagging enabled) was found in this workflow."; + if (doprocessLambdaLambda && mLambdaLambdaVariants.empty()) { + LOG(fatal) << "processLambdaLambda is enabled, but no femto-pair-v0-v0 device (with processLambdaLambdaMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessK0shortK0short && mK0shortK0shortVariants.empty()) { + LOG(fatal) << "processK0shortK0short is enabled, but no femto-pair-v0-v0 device (with processK0shortK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessLambdaK0short && mLambdaK0shortVariants.empty()) { + LOG(fatal) << "processLambdaK0short is enabled, but no femto-pair-v0-v0 device (with processLambdaK0shortMixedEventWithTagging " + << "enabled) was found in this workflow."; } if (doprocessD0D0 && mD0D0Variants.empty()) { LOG(fatal) << "processD0D0 is enabled, but no femto-pair-d0-d0 device (with processMixedEventWithTagging enabled) was found in this workflow."; @@ -445,10 +469,15 @@ struct FemtoProducerCollisionTagger { LOG(fatal) << "processTrackTwoPhotonResonance is enabled, but no femto-pair-track-two-photon-resonance device (with " << "processPi0MixedEventWithTagging/processEtaMixedEventWithTagging enabled) was found in this workflow."; } - if (doprocessV0TwoTrackResonance && mLambdaPhiVariants.empty() && mLambdaKstar0Variants.empty() && mLambdaRho0Variants.empty() && - mK0shortPhiVariants.empty() && mK0shortKstar0Variants.empty() && mK0shortRho0Variants.empty()) { - LOG(fatal) << "processV0TwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of the " - << "processXxxMixedEventWithTagging switches enabled) was found in this workflow."; + if (doprocessLambdaTwoTrackResonance && mLambdaPhiVariants.empty() && mLambdaKstar0Variants.empty() && mLambdaRho0Variants.empty()) { + LOG(fatal) << "processLambdaTwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of " + << "processLambdaPhiMixedEventWithTagging/processLambdaKstar0MixedEventWithTagging/" + << "processLambdaRho0MixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessK0shortTwoTrackResonance && mK0shortPhiVariants.empty() && mK0shortKstar0Variants.empty() && mK0shortRho0Variants.empty()) { + LOG(fatal) << "processK0shortTwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of " + << "processK0shortPhiMixedEventWithTagging/processK0shortKstar0MixedEventWithTagging/" + << "processK0shortRho0MixedEventWithTagging enabled) was found in this workflow."; } if (doprocessTripletTrackTrackTrack && mTrackTrackTrackVariants.empty()) { LOG(fatal) << "processTripletTrackTrackTrack is enabled, but no femto-triplet-track-track-track device (with " @@ -458,9 +487,13 @@ struct FemtoProducerCollisionTagger { LOG(fatal) << "processTripletTrackTrackV0 is enabled, but no femto-triplet-track-track-v0 device (with " << "processMixedEventWithTagging enabled) was found in this workflow."; } - if (doprocessTripletTrackTrackCascade && mTrackTrackXiVariants.empty() && mTrackTrackOmegaVariants.empty()) { - LOG(fatal) << "processTripletTrackTrackCascade is enabled, but no femto-triplet-track-track-cascade device (with " - << "processXiMixedEventWithTagging/processOmegaMixedEventWithTagging enabled) was found in this workflow."; + if (doprocessTripletTrackTrackXi && mTrackTrackXiVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackXi is enabled, but no femto-triplet-track-track-cascade device (with " + << "processXiMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackOmega && mTrackTrackOmegaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackOmega is enabled, but no femto-triplet-track-track-cascade device (with " + << "processOmegaMixedEventWithTagging enabled) was found in this workflow."; } if (!roles.empty() && roles.back().bit >= collisiontagger::MaxTagBits) { LOG(fatal) << "Too many tag bits requested (" << (roles.back().bit + 1) << "); collision tagger supports up to " << collisiontagger::MaxTagBits << "."; @@ -478,60 +511,79 @@ struct FemtoProducerCollisionTagger { } PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTrack, "Tag collisions for femto-pair-track-track wagon(s)", false); - // Two species checked independently, both contributing to the one tag this collision gets -- - // can't collapse to a single tagAndProduce() call like processTrackTrack above (that helper - // assumes exactly one check per row), but each cross-type check (track role + v0 role) is - // still just one call to tagCrossVariants(). - void processTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + // Lambda and K0short are split into two separate process functions (rather than one combined + // processTrackV0 reading both FemtoLambdas and FemtoK0shorts unconditionally) because the + // consuming femto-pair-track-v0 task itself enforces that a single instance only ever tags + // Lambda OR K0short, never both (see its init(): "Both lambda-track and k0short-track + // processing is enabled" is fatal there) -- so requiring both tables as DPL inputs here would + // make the tagger unusable against any input file that only has one of the two V0 species + // derived (e.g. Lambda-only productions with no FK0shorts table at all: DPL subscribes to a + // process function's full argument list regardless of whether the branch using it is actually + // reached at runtime, so the AOD reader fails outright looking for a tree that was never + // produced, instead of this cleanly tagging zero K0short pairs). + void processTrackLambda(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) { collisiontagger::CollisionTagBitset tag; auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; - if (!mTrackLambdaVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackLambdaVariants, tracksThisCol, trackPassesFn, lambdasThisCol, - [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); - } - if (!mTrackK0shortVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackK0shortVariants, tracksThisCol, trackPassesFn, k0shortsThisCol, - [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }); - } + collisiontagger::tagCrossVariants(tag, mTrackLambdaVariants, tracksThisCol, trackPassesFn, lambdasThisCol, + [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }); producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackV0, "Tag collisions for femto-pair-track-v0 wagon(s)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLambda, "Tag collisions for femto-pair-track-v0 wagon(s) (Lambda)", false); - void processTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + void processTrackK0short(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoK0shorts const& k0shortsThisCol) { collisiontagger::CollisionTagBitset tag; auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; - auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; - if (!mTrackXiVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); - } - if (!mTrackOmegaVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); - } + collisiontagger::tagCrossVariants(tag, mTrackK0shortVariants, tracksThisCol, trackPassesFn, k0shortsThisCol, + [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackK0short, "Tag collisions for femto-pair-track-v0 wagon(s) (K0short)", false); + + // Xi and Omega are split into separate process functions (rather than one combined + // processTrackCascade reading both FemtoXis and FemtoOmegas unconditionally): same reasoning as + // processTrackLambda/processTrackK0short above -- femtoProducerDerivedToDerived.cxx's + // processTracksOmegas has no corresponding "always comes with Xi" guarantee (Xi and Omega are + // each their own independently-switchable, default-off producer process function), so a + // Xi-only (or Omega-only) derived file would otherwise make the AOD reader fail looking for a + // tree that was never produced. + void processTrackXi(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackXiVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, xisThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackCascade, "Tag collisions for femto-pair-track-cascade wagon(s)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackXi, "Tag collisions for femto-pair-track-cascade wagon(s) (Xi)", false); - // Sigma and SigmaPlus (kink) tables are always filled together by the producer, but SigmaPlusPcm - // comes from a mutually exclusive producer process function (processTracksSigmaPlusPcmsRun3pp/Mc) -- - // so it gets its own process function below instead of joining this one, otherwise enabling this - // process would force the producer to supply a table combination it can never actually produce. - void processTrackKink(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmas const& sigmasThisCol, FemtoSigmaPlus const& sigmaPlusThisCol) + void processTrackOmega(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoOmegas const& omegasThisCol) { collisiontagger::CollisionTagBitset tag; - auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; - if (!mTrackSigmaVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackSigmaVariants, tracksThisCol, trackPassesFn, sigmasThisCol, - [](auto const& sigma, auto const& sel) { return collisiontagger::lambdaPasses(sigma, sel); }); - } - if (!mTrackSigmaPlusVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusVariants, tracksThisCol, trackPassesFn, sigmaPlusThisCol, - [](auto const& sigmaPlus, auto const& sel) { return collisiontagger::lambdaPasses(sigmaPlus, sel); }); - } + collisiontagger::tagCrossVariants(tag, mTrackOmegaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, omegasThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackOmega, "Tag collisions for femto-pair-track-cascade wagon(s) (Omega)", false); + + // Sigma and SigmaPlus are likewise split: femtoProducerDerivedToDerived.cxx's processTracksSigma + // and processTracksSigmaPlus are each their own independently-switchable, default-off process + // function, not bundled -- same risk as Xi/Omega and Lambda/K0short above. SigmaPlusPcm already + // had its own process function below (comes from a genuinely separate, mutually exclusive + // producer process function), unaffected by this split. + void processTrackSigma(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmas const& sigmasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmasThisCol, [](auto const& sigma, auto const& sel) { return collisiontagger::signedV0Passes(sigma, sel); }); producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackKink, "Tag collisions for femto-pair-track-kink wagon(s) (Sigma/SigmaPlus)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigma, "Tag collisions for femto-pair-track-kink wagon(s) (Sigma)", false); + + void processTrackSigmaPlus(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlus const& sigmaPlusThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmaPlusThisCol, [](auto const& sigmaPlus, auto const& sel) { return collisiontagger::signedV0Passes(sigmaPlus, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigmaPlus, "Tag collisions for femto-pair-track-kink wagon(s) (SigmaPlus)", false); void processTrackSigmaPlusPcm(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlusPcm const& sigmaPlusPcmThisCol) { @@ -547,7 +599,7 @@ struct FemtoProducerCollisionTagger { void processTrackD0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoD0s const& d0sThisCol) { collisiontagger::CollisionTagBitset tag; - collisiontagger::tagCrossVariants(tag, mTrackD0Variants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, d0sThisCol, [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + collisiontagger::tagCrossVariants(tag, mTrackD0Variants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, d0sThisCol, [](auto const& d0, auto const& sel) { return collisiontagger::signedV0Passes(d0, sel); }); producedTags(static_cast(tag.to_ullong())); } PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackD0, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (D0)", false); @@ -555,35 +607,48 @@ struct FemtoProducerCollisionTagger { void processTrackLc(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLcs const& lcsThisCol) { collisiontagger::CollisionTagBitset tag; - collisiontagger::tagCrossVariants(tag, mTrackLcVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lcsThisCol, [](auto const& lc, auto const& sel) { return collisiontagger::lambdaPasses(lc, sel); }); + collisiontagger::tagCrossVariants(tag, mTrackLcVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lcsThisCol, [](auto const& lc, auto const& sel) { return collisiontagger::signedV0Passes(lc, sel); }); producedTags(static_cast(tag.to_ullong())); } PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLc, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (Lc)", false); // No track table involved here at all -- v0-v0 is checked purely between the two v0 tables // themselves (lambda/k0short), same as D0D0 below is checked purely within FemtoD0s. - void processV0V0(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + // Split by which table(s) are actually needed: LambdaLambda only needs FemtoLambdas, + // K0shortK0short only needs FemtoK0shorts -- same reasoning as the other splits above + // (femtoProducerDerivedToDerived.cxx's processTracksLambdas/processTracksK0shorts are each + // independently switchable, default off). LambdaK0short is a genuine cross-species pair, so it + // legitimately needs both tables and stays as-is. + void processLambdaLambda(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol) { collisiontagger::CollisionTagBitset tag; - if (!mLambdaLambdaVariants.empty()) { - collisiontagger::tagPairVariants(tag, mLambdaLambdaVariants, lambdasThisCol, - [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }); - } - if (!mK0shortK0shortVariants.empty()) { - collisiontagger::tagPairVariants(tag, mK0shortK0shortVariants, k0shortsThisCol, - [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); - } - if (!mLambdaK0shortVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mLambdaK0shortVariants, lambdasThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }, k0shortsThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); - } + collisiontagger::tagPairVariants(tag, mLambdaLambdaVariants, lambdasThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::signedV0Passes(v0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaLambda, "Tag collisions for femto-pair-v0-v0 wagon(s) (Lambda-Lambda)", false); + + void processK0shortK0short(FemtoCollision const& /*col*/, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagPairVariants(tag, mK0shortK0shortVariants, k0shortsThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processK0shortK0short, "Tag collisions for femto-pair-v0-v0 wagon(s) (K0short-K0short)", false); + + void processLambdaK0short(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mLambdaK0shortVariants, lambdasThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::signedV0Passes(v0, sel); }, k0shortsThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0V0, "Tag collisions for femto-pair-v0-v0 wagon(s)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaK0short, "Tag collisions for femto-pair-v0-v0 wagon(s) (Lambda-K0short)", false); void processD0D0(FemtoCollision const& /*col*/, FemtoD0s const& d0sThisCol) { collisiontagger::tagAndProduce(producedTags, mD0D0Variants, d0sThisCol, - [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + [](auto const& d0, auto const& sel) { return collisiontagger::signedV0Passes(d0, sel); }); } PROCESS_SWITCH(FemtoProducerCollisionTagger, processD0D0, "Tag collisions for femto-pair-d0-d0 wagon(s)", false); @@ -627,25 +692,36 @@ struct FemtoProducerCollisionTagger { } PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoPhotonResonance, "Tag collisions for femto-pair-track-two-photon-resonance wagon(s)", false); - // No track table involved here at all -- same as processV0V0 above, checked purely between the - // v0 tables (lambda/k0short) and the resonance tables (phi/kstar0/rho0) themselves. - void processV0TwoTrackResonance(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + // No track table involved here at all -- checked purely between the v0 tables (lambda/k0short) + // and the resonance tables (phi/kstar0/rho0) themselves. Split by V0 species (Lambda vs + // K0short) for the same reason as processLambdaLambda/processK0shortK0short above -- each + // needs only its own V0 table. Phi/Kstar0/Rho0 stay combined within each (that axis is safe: + // femtoProducerResonances.cxx builds all three by default with no mutual-exclusivity gate, see + // processTrackTwoTrackResonance below). + void processLambdaTwoTrackResonance(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) { collisiontagger::CollisionTagBitset tag; - auto lambdaPassesFn = [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }; - auto k0shortPassesFn = [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }; + auto signedV0PassesFn = [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }; if (!mLambdaPhiVariants.empty()) { - collisiontagger::tagCrossVariants(tag, mLambdaPhiVariants, lambdasThisCol, lambdaPassesFn, phisThisCol, + collisiontagger::tagCrossVariants(tag, mLambdaPhiVariants, lambdasThisCol, signedV0PassesFn, phisThisCol, [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); } if (!mLambdaKstar0Variants.empty()) { - collisiontagger::tagCrossVariants(tag, mLambdaKstar0Variants, lambdasThisCol, lambdaPassesFn, kstar0sThisCol, + collisiontagger::tagCrossVariants(tag, mLambdaKstar0Variants, lambdasThisCol, signedV0PassesFn, kstar0sThisCol, [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); } if (!mLambdaRho0Variants.empty()) { - collisiontagger::tagCrossVariants(tag, mLambdaRho0Variants, lambdasThisCol, lambdaPassesFn, rho0sThisCol, + collisiontagger::tagCrossVariants(tag, mLambdaRho0Variants, lambdasThisCol, signedV0PassesFn, rho0sThisCol, [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processLambdaTwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s) (Lambda)", false); + + void processK0shortTwoTrackResonance(FemtoCollision const& /*col*/, FemtoK0shorts const& k0shortsThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto k0shortPassesFn = [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }; if (!mK0shortPhiVariants.empty()) { collisiontagger::tagCrossVariants(tag, mK0shortPhiVariants, k0shortsThisCol, k0shortPassesFn, phisThisCol, [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); @@ -660,7 +736,7 @@ struct FemtoProducerCollisionTagger { } producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0TwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processK0shortTwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s) (K0short)", false); void processTripletTrackTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) { @@ -674,25 +750,27 @@ struct FemtoProducerCollisionTagger { void processTripletTrackTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) { collisiontagger::CollisionTagBitset tag; - collisiontagger::tagCrossTripletVariants(tag, mTrackTrackLambdaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lambdasThisCol, [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackLambdaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lambdasThisCol, [](auto const& lambda, auto const& sel) { return collisiontagger::signedV0Passes(lambda, sel); }); producedTags(static_cast(tag.to_ullong())); } PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackV0, "Tag collisions for femto-triplet-track-track-v0 wagon(s)", false); - void processTripletTrackTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + // Split by Xi/Omega for the same reason as processTrackXi/processTrackOmega above. + void processTripletTrackTrackXi(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol) { collisiontagger::CollisionTagBitset tag; - auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; - auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; - if (!mTrackTrackXiVariants.empty()) { - collisiontagger::tagCrossTripletVariants(tag, mTrackTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); - } - if (!mTrackTrackOmegaVariants.empty()) { - collisiontagger::tagCrossTripletVariants(tag, mTrackTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); - } + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackXiVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, xisThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackXi, "Tag collisions for femto-triplet-track-track-cascade wagon(s) (Xi)", false); + + void processTripletTrackTrackOmega(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackOmegaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, omegasThisCol, [](auto const& cascade, auto const& sel) { return collisiontagger::signedV0Passes(cascade, sel); }); producedTags(static_cast(tag.to_ullong())); } - PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackCascade, "Tag collisions for femto-triplet-track-track-cascade wagon(s)", false); + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackOmega, "Tag collisions for femto-triplet-track-track-cascade wagon(s) (Omega)", false); }; o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) From 1c2600a546c044a4c4eb78dbb085065f956f518b Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Fri, 6 Nov 2026 22:41:49 +0300 Subject: [PATCH 602/648] [PWGUD] Change condition for pileup selection (#18632) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index d9beaca9aa7..dc023703a9c 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -547,7 +547,7 @@ struct UpcCandProducerBarrel { const auto iTF = localTFIndex[bc.globalIndex()]; const auto tfID = tfIDs[iTF]; const int32_t rofInTF = getRofInTF(bcInTF); - bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] >= 1; // TODO investigate other options bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; // Multiplicity for FT0C-based occupancy estimates @@ -677,7 +677,7 @@ struct UpcCandProducerBarrel { const int bcInOrbit = globalBC % NBCsPerOrbit; const int32_t rofInTF = getRofInTF(bcInTF); bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; - bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] >= 1; const auto distanceFT0 = minDistanceFT0[iTF][bcInTF]; const auto distanceFV0 = minDistanceFV0[iTF][bcInTF]; const auto distanceFDD = minDistanceFDD[iTF][bcInTF]; From d561ab2e1c3e5695accc89923aa686bee78f00de Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Fri, 6 Nov 2026 22:16:14 +0100 Subject: [PATCH 603/648] [PWGLF] Fixing binning and label issues for misidentification studies (#18627) Co-authored-by: Sushanta Tripathy --- PWGLF/Tasks/Resonances/lambdastarproxy.cxx | 34 ++++------------------ 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index 625403f7c0e..e4fbed337ad 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -1064,9 +1064,9 @@ struct Lambdastarproxy { static constexpr double MultMax = 100.; static constexpr std::array PairSigns{"Unlike", "Like"}; static constexpr std::array ProbeRoles{"De", "Ka"}; - static constexpr std::array ParentNames{ - "no retained common mother", "K0S", "Lambda1115", "Kstar", "phi", "Lambda1520", - "other retained common mother", "multiple retained common mothers", "same MC particle", "invalid truth labels"}; + // Parent tags: 0=no common mother, 1=K0S, 2=Lambda1115, 3=Kstar, 4=phi, + // 5=Lambda1520, 6=other common mother, 7=multiple common mothers, + // 8=same MC particle, 9=invalid truth labels. std::array, 2> mcProxySpecies{}; std::array, 2> mcProxyOtherParents{}; @@ -1222,6 +1222,8 @@ struct Lambdastarproxy { const AxisSpec mcMass{DiagnosticMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; const AxisSpec probeMass{ProbeMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; uint64_t payload = 0; + // Keep these numerically filled TH3 axes unlabelled. + // For SpeciesNames see comment above, the parent-tag comments and packed-axis formulas define the decoder. auto add3 = [&](std::string const& name, std::string const& title, std::vector const& axes) { auto h = histos.add(name.c_str(), title.c_str(), HistType::kTH3D, axes, true); payload += static_cast(h->GetNcells()) * 2 * sizeof(double); @@ -1232,17 +1234,8 @@ struct Lambdastarproxy { if (lstarEnableMCProxySpecies.value != 0) { mcProxySpecies[sign] = add3("hMCProxySpecies" + suffix, "Selected proxy;species+51*(dTOF+2*kTOF+4*pTPC+8*regularMult)", {mcMass, pairPt, AxisSpec{SpeciesTagCount * SelectedPIDStates, -0.5, SpeciesTagCount * SelectedPIDStates - 0.5, "Species and detector state"}}); - for (int state = 0; state < SelectedPIDStates; ++state) { - for (int tag = 0; tag < SpeciesTagCount; ++tag) { - const std::string label = speciesTagName(tag) + ";state=" + std::to_string(state); - mcProxySpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * state + 1, label.c_str()); - } - } mcProxyOtherParents[sign] = add3("hMCProxyOtherParents" + suffix, "Selected Other: retained immediate common-mother class only", {mcMass, pairPt, AxisSpec{ParentClasses, -0.5, ParentClasses - 0.5, "Retained parent class"}}); - for (int parent = 0; parent < ParentClasses; ++parent) { - mcProxyOtherParents[sign]->GetZaxis()->SetBinLabel(parent + 1, ParentNames[parent]); - } } if (lstarEnableProxyPIDProbes.value == 0) { continue; @@ -1256,27 +1249,10 @@ struct Lambdastarproxy { probeMass2[role][sign] = add3(std::string(stem).append("Mass2").append(suffix), "Loose both-TOF proxy probes;TPC/TOF envelopes;regular multiplicity", {probeMass, m2, packed}); probeTPC[role][sign] = add3(std::string(stem).append("TPC").append(suffix), "Same loose both-TOF proxy probes;TPC nSigma;regular multiplicity", {probeMass, AxisSpec{60, -ProbeTPCMax, ProbeTPCMax, "TPC nSigma for slot hypothesis"}, packed}); probeTOF[role][sign] = add3(std::string(stem).append("TOF").append(suffix), "Same loose both-TOF proxy probes;TOF nSigma;regular multiplicity", {probeMass, AxisSpec{80, -ProbeTOFMax, ProbeTOFMax, "TOF nSigma for slot hypothesis"}, packed}); - for (int region = 0; region < ProbeRegions; ++region) { - for (int momentum = 0; momentum < ProbeMomentumBins; ++momentum) { - for (int pair = 0; pair < ProbePairPtBins; ++pair) { - const int tag = pair + ProbePairPtBins * (momentum + ProbeMomentumBins * region); - const std::string label = "pair=" + std::to_string(pair) + ";p=" + std::to_string(momentum) + ";region=" + std::to_string(region); - probeMass2[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); - probeTPC[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); - probeTOF[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); - } - } - } } if (doprocessMCReco) { mcProbeSpecies[sign] = add3("hMCProxyPIDProbeSpecies" + suffix, "Same loose probe pairs;species+51*nominalRegion;regular multiplicity", {probeMass, pairPt, AxisSpec{SpeciesTagCount * ProbeRegions, -0.5, SpeciesTagCount * ProbeRegions - 0.5, "Species plus nominal region"}}); - for (int region = 0; region < ProbeRegions; ++region) { - for (int tag = 0; tag < SpeciesTagCount; ++tag) { - const std::string label = speciesTagName(tag) + ";region=" + std::to_string(region); - mcProbeSpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * region + 1, label.c_str()); - } - } } } if (lstarEnableProxyPIDProbes.value != 0) { From e62d7861d25cc28e029923b6f76568ff78a65df2 Mon Sep 17 00:00:00 2001 From: Stefanie Mrozinski <63045530+Steffimro@users.noreply.github.com> Date: Sat, 7 Nov 2026 16:50:27 +0100 Subject: [PATCH 604/648] [PWGEM] Add new overlay mixed event (#18621) Co-authored-by: Stefanie Mrozinski --- PWGEM/PhotonMeson/Core/V0PhotonCut.h | 24 +- PWGEM/PhotonMeson/DataModel/gammaTables.h | 247 ++ .../PhotonMeson/TableProducer/CMakeLists.txt | 10 + .../TableProducer/createEMEventPhoton.cxx | 24 +- .../photonMixedEventProducer.cxx | 787 +++++ .../TableProducer/photonconversionbuilder.cxx | 2 +- .../TableProducer/photonconversionbuilder.h | 2523 ++++++++++++++++- .../photonconversionbuilderMixed.cxx | 28 + .../photonconversionbuilderTmpTable.cxx | 2 +- PWGEM/PhotonMeson/Tasks/photonhbt.cxx | 1273 ++++++++- 10 files changed, 4743 insertions(+), 177 deletions(-) create mode 100644 PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx create mode 100644 PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx diff --git a/PWGEM/PhotonMeson/Core/V0PhotonCut.h b/PWGEM/PhotonMeson/Core/V0PhotonCut.h index 1921a71c79b..d9c442f499b 100644 --- a/PWGEM/PhotonMeson/Core/V0PhotonCut.h +++ b/PWGEM/PhotonMeson/Core/V0PhotonCut.h @@ -246,8 +246,10 @@ class V0PhotonCut : public TNamed fRegistry->add("QA/V0Photon/before/hConvZR", "Conversion point ZR", o2::framework::HistType::kTH2F, {thAxisConvZ, thAxisConvR}, true); fRegistry->add("QA/V0Photon/before/hChi2", "Chi2/ndf from KFParticle;#chi^{2}_{KF}/ndf;counts", o2::framework::HistType::kTH1D, {thAxisChi2}, true); - // TODO: add psi_pair once available - // fRegistry->add("QA/V0Photon/before/hPsiPair", "Psi pair;#Psi_{pair};counts", o2::framework::HistType::kTH1D, {thAxisPsiPair}, true); + // psi_pair (V0PhotonsPhiVPsi) and the KF chi2 split (V0PhotonsKFChi2), filled only if the photon table joins them + fRegistry->add("QA/V0Photon/before/hPsiPair", "Psi pair;#Psi_{pair};counts", o2::framework::HistType::kTH1D, {thAxisPsiPair}, true); + fRegistry->add("QA/V0Photon/before/hChi2Vtx", "KF chi2/ndf of the conversion-vertex fit;#chi^{2}_{vtx}/ndf;counts", o2::framework::HistType::kTH1D, {thAxisChi2}, true); + fRegistry->add("QA/V0Photon/before/hChi2PV", "KF chi2 to the primary vertex;#chi^{2}_{PV};counts", o2::framework::HistType::kTH1D, {{100, 0., 200.}}, true); fRegistry->add("QA/V0Photon/before/Pos/NSigmaE", "NSigmaE of pos leg vs momentum", o2::framework::HistType::kTH2F, {thAxisMomentum, thAxisNSigmaE}, true); fRegistry->add("QA/V0Photon/before/Pos/NSigmaPi", "NSigmaE of pos leg vs momentum", o2::framework::HistType::kTH2F, {thAxisMomentum, thAxisNSigmaPi}, true); @@ -316,8 +318,13 @@ class V0PhotonCut : public TNamed fRegistry->fill(HIST("QA/V0Photon/before/hConvZR"), v0.vz(), v0.v0radius()); fRegistry->fill(HIST("QA/V0Photon/before/hChi2"), v0.chiSquareNDF()); - // TODO: add psi_pair once available - // fRegistry->fill(HIST("QA/V0Photon/before/hPsiPair"), v0.psiPair()); + if constexpr (requires { v0.psipair(); }) { + fRegistry->fill(HIST("QA/V0Photon/before/hPsiPair"), v0.psipair()); + } + if constexpr (requires { v0.chi2PV(); }) { + fRegistry->fill(HIST("QA/V0Photon/before/hChi2Vtx"), v0.chi2NDFVtx()); + fRegistry->fill(HIST("QA/V0Photon/before/hChi2PV"), v0.chi2PV()); + } fRegistry->fill(HIST("QA/V0Photon/before/Pos/NSigmaE"), pos.p(), pos.tpcNSigmaEl()); fRegistry->fill(HIST("QA/V0Photon/before/Pos/NSigmaPi"), pos.p(), pos.tpcNSigmaPi()); @@ -346,8 +353,13 @@ class V0PhotonCut : public TNamed fRegistry->fill(HIST("QA/V0Photon/after/hConvZR"), v0.vz(), v0.v0radius()); fRegistry->fill(HIST("QA/V0Photon/after/hChi2"), v0.chiSquareNDF()); - // TODO: add psi_pair once available - // fRegistry->fill(HIST("QA/V0Photon/after/hPsiPair"), v0.psiPair()); + if constexpr (requires { v0.psipair(); }) { + fRegistry->fill(HIST("QA/V0Photon/after/hPsiPair"), v0.psipair()); + } + if constexpr (requires { v0.chi2PV(); }) { + fRegistry->fill(HIST("QA/V0Photon/after/hChi2Vtx"), v0.chi2NDFVtx()); + fRegistry->fill(HIST("QA/V0Photon/after/hChi2PV"), v0.chi2PV()); + } fRegistry->fill(HIST("QA/V0Photon/after/Pos/NSigmaE"), pos.p(), pos.tpcNSigmaEl()); fRegistry->fill(HIST("QA/V0Photon/after/Pos/NSigmaPi"), pos.p(), pos.tpcNSigmaPi()); diff --git a/PWGEM/PhotonMeson/DataModel/gammaTables.h b/PWGEM/PhotonMeson/DataModel/gammaTables.h index f0e0bdf8d85..454bcea9740 100644 --- a/PWGEM/PhotonMeson/DataModel/gammaTables.h +++ b/PWGEM/PhotonMeson/DataModel/gammaTables.h @@ -292,6 +292,31 @@ DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMCTmp, "AOD", "V0LEGDEDXMCTMP", 0, mcpidtp // iterators using V0LegDeDxMCTmp = V0LegsDeDxMCTmp::iterator; +namespace v0legcov +{ +DECLARE_SOA_COLUMN(X, x, float); //! x of the leg parameters given to the V0 fit (innermost update, cm) +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! frame angle +DECLARE_SOA_COLUMN(Y, y, float); //! local y (cm) +DECLARE_SOA_COLUMN(Z, z, float); //! z (cm), TPC-only legs after the drift correction to the V0's collision +DECLARE_SOA_COLUMN(Snp, snp, float); //! sin(phi) in the local frame +DECLARE_SOA_COLUMN(Tgl, tgl, float); //! tan(lambda) +DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! q/pT (c/GeV) +DECLARE_SOA_COLUMN(Cov, cov, float[15]); // NOLINT(modernize-avoid-c-arrays) //! covariance matrix, o2::track::TrackParCov order +} // namespace v0legcov + +// leg track parameters as used in the V0 fit, one row per V0Legs row (optional builder output, joinable with V0Legs) +DECLARE_SOA_TABLE_VERSIONED(V0LegsCov_000, "AOD", "V0LEGCOV", 0, //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); +using V0LegsCov = V0LegsCov_000; +// iterators +using V0LegCov = V0LegsCov::iterator; + +// copy of V0LegsCov table +DECLARE_SOA_TABLE_VERSIONED(V0LegsCovTmp, "AOD", "V0LEGCOVTMP", 0, //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); +// iterators +using V0LegCovTmp = V0LegsCovTmp::iterator; + namespace emevent { DECLARE_SOA_COLUMN(NgPCM, ngpcm, int); @@ -540,6 +565,17 @@ DECLARE_SOA_INDEX_COLUMN(PMEvent, pmevent); //! DECLARE_SOA_COLUMN(PhiV, phiv, float); //! DECLARE_SOA_COLUMN(PsiPair, psipair, float); } // namespace v0photonsphivpsi +// KF quality of the photon: the stored v0photonkf::ChiSquareNDF is the chi2/ndf AFTER the topological constraint to the PV; +// these two separate the conversion-vertex fit from the pointing to the PV (row-by-row extension of the photon table, join by index) +namespace v0photonkfchi2 +{ +DECLARE_SOA_COLUMN(Chi2NDFVtx, chi2NDFVtx, float); //! chi2/ndf of the two-leg conversion-vertex fit (incl. the mass constraint if applied), before the PV constraint +DECLARE_SOA_COLUMN(Chi2PV, chi2PV, float); //! chi2 deviation of the photon from the primary vertex (KFParticle::GetDeviationFromVertex) +} // namespace v0photonkfchi2 +DECLARE_SOA_TABLE(V0PhotonsKFChi2, "AOD", "V0PHKFCHI2", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! +DECLARE_SOA_TABLE(V0PhotonsKFChi2Tmp, "AOD", "V0PHKFCHI2TMP", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! +DECLARE_SOA_TABLE(V0PhotonsKFChi2Mix, "AOD", "V0PHKFCHI2MIX", o2::soa::Index<>, v0photonkfchi2::Chi2NDFVtx, v0photonkfchi2::Chi2PV); //! builder run 2 + DECLARE_SOA_TABLE(V0PhotonsPhiVPsi, "AOD", "V0PHOTONPHIVPSI", //! o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); // iterators @@ -551,6 +587,217 @@ DECLARE_SOA_TABLE(V0PhotonsPhiVPsiTmp, "AOD", "V0PHOTONPHIVTMP", //! // iterators using V0PhotonsPhiVPsiTmp = V0PhotonsPhiVPsiTmp; +// co-reconstructed mixed event: artificial events made by photon-mixed-event-producer from the photons of builder run 1, +// rebuilt from their legs by builder run 2 (photon-conversion-builder-mixed) +namespace pcmmixcol +{ +DECLARE_SOA_COLUMN(IsMixed, isMixed, bool); //! false: the photons of one event, true: N photons from N different events +DECLARE_SOA_COLUMN(SourceCollisionId, sourceCollisionId, int); //! collision (this DF) whose vertex the artificial event has +DECLARE_SOA_COLUMN(SourceEventUid, sourceEventUid, int64_t); //! mixer event counter of that collision +DECLARE_SOA_COLUMN(ZBin, zBin, int); //! pool z bin +DECLARE_SOA_COLUMN(OccBin, occBin, int); //! pool occupancy bin +DECLARE_SOA_COLUMN(Occupancy, occupancy, float); //! occupancy of the source collision +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); //! centralities of the source collision (V0 fit input) +DECLARE_SOA_COLUMN(CentFT0A, centFT0A, float); //! +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); //! +DECLARE_SOA_COLUMN(FirstLeg, firstLeg, int); //! first PCMMixLegs row of this artificial event +DECLARE_SOA_COLUMN(NLegs, nLegs, int); //! number of PCMMixLegs rows (2 per photon, contiguous) +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); //! run of the source collision (CCDB: B field, TPC drift velocity) +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! CCDB timestamp of the source collision (builder run 2 on derived data: no BCs) +DECLARE_SOA_COLUMN(MultNTracksPVeta1, multNTracksPVeta1, int); //! PV contributors with |eta| < 1 of the source collision +DECLARE_SOA_COLUMN(MultFT0M, multFT0M, float); //! FT0A + FT0C amplitude of the source collision +} // namespace pcmmixcol +DECLARE_SOA_TABLE(PCMMixCollisions, "AOD", "PCMMIXCOL", //! + o2::soa::Index<>, collision::PosX, collision::PosY, collision::PosZ, + collision::CovXX, collision::CovXY, collision::CovXZ, collision::CovYY, collision::CovYZ, collision::CovZZ, + collision::Chi2, collision::NumContrib, + pcmmixcol::CentFT0M, pcmmixcol::CentFT0A, pcmmixcol::CentFT0C, + pcmmixcol::IsMixed, pcmmixcol::SourceCollisionId, pcmmixcol::SourceEventUid, + pcmmixcol::ZBin, pcmmixcol::OccBin, pcmmixcol::Occupancy, pcmmixcol::FirstLeg, pcmmixcol::NLegs, + pcmmixcol::RunNumber, pcmmixcol::MultNTracksPVeta1, pcmmixcol::MultFT0M, pcmmixcol::Timestamp); +using PCMMixCollision = PCMMixCollisions::iterator; + +namespace pcmmixleg +{ +DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! PCMMixCollisions row +DECLARE_SOA_COLUMN(Pt, pt, float); //! pT of the AO2D track (track-like interface for the builder) +DECLARE_SOA_COLUMN(Phi, phi, float); //! phi of the AO2D track +DECLARE_SOA_COLUMN(CYY, cYY, float); //! covariance, o2::track::TrackParCov order (from V0LegsCov) +DECLARE_SOA_COLUMN(CZY, cZY, float); //! +DECLARE_SOA_COLUMN(CZZ, cZZ, float); //! +DECLARE_SOA_COLUMN(CSnpY, cSnpY, float); //! +DECLARE_SOA_COLUMN(CSnpZ, cSnpZ, float); //! +DECLARE_SOA_COLUMN(CSnpSnp, cSnpSnp, float); //! +DECLARE_SOA_COLUMN(CTglY, cTglY, float); //! +DECLARE_SOA_COLUMN(CTglZ, cTglZ, float); //! +DECLARE_SOA_COLUMN(CTglSnp, cTglSnp, float); //! +DECLARE_SOA_COLUMN(CTglTgl, cTglTgl, float); //! +DECLARE_SOA_COLUMN(C1PtY, c1PtY, float); //! +DECLARE_SOA_COLUMN(C1PtZ, c1PtZ, float); //! +DECLARE_SOA_COLUMN(C1PtSnp, c1PtSnp, float); //! +DECLARE_SOA_COLUMN(C1PtTgl, c1PtTgl, float); //! +DECLARE_SOA_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float); //! +DECLARE_SOA_COLUMN(SourceEventUid, sourceEventUid, int64_t); //! mixer event counter of the event the leg comes from +DECLARE_SOA_COLUMN(SourcePhotonUid, sourcePhotonUid, int64_t); //! mixer photon counter of the run-1 photon the leg belonged to +DECLARE_SOA_COLUMN(SourceV0Id, sourceV0Id, int); //! v0Id of that photon in run 1 (< 0: not an SVertexer V0) +DECLARE_SOA_COLUMN(SourcePx, sourcePx, float); //! run-1 photon momentum at the PV +DECLARE_SOA_COLUMN(SourcePy, sourcePy, float); //! +DECLARE_SOA_COLUMN(SourcePz, sourcePz, float); //! +DECLARE_SOA_COLUMN(SourceVx, sourceVx, float); //! run-1 conversion point +DECLARE_SOA_COLUMN(SourceVy, sourceVy, float); //! +DECLARE_SOA_COLUMN(SourceVz, sourceVz, float); //! +DECLARE_SOA_COLUMN(SourcePvX, sourcePvX, float); //! primary vertex of the source event (run 2: refit of the original at its own vertex) +DECLARE_SOA_COLUMN(SourcePvY, sourcePvY, float); //! +DECLARE_SOA_COLUMN(SourcePvZ, sourcePvZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXX, sourcePvCovXX, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXY, sourcePvCovXY, float); //! +DECLARE_SOA_COLUMN(SourcePvCovXZ, sourcePvCovXZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovYY, sourcePvCovYY, float); //! +DECLARE_SOA_COLUMN(SourcePvCovYZ, sourcePvCovYZ, float); //! +DECLARE_SOA_COLUMN(SourcePvCovZZ, sourcePvCovZZ, float); //! +DECLARE_SOA_COLUMN(SourcePvChi2, sourcePvChi2, float); //! +DECLARE_SOA_COLUMN(SourcePvNContrib, sourcePvNContrib, uint16_t); //! +DECLARE_SOA_COLUMN(OrigTrackId, origTrackId, int); //! AO2D track (DF of the source event) +DECLARE_SOA_COLUMN(McParticleId, mcParticleId, int); //! MC: particle of the leg (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McMotherId, mcMotherId, int); //! MC: first mother (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McPdg, mcPdg, int); //! MC: PDG of the leg +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); //! MC: PDG of the mother +DECLARE_SOA_COLUMN(McMotherPx, mcMotherPx, float); //! MC: true momentum of the mother +DECLARE_SOA_COLUMN(McMotherPy, mcMotherPy, float); //! +DECLARE_SOA_COLUMN(McMotherPz, mcMotherPz, float); //! +DECLARE_SOA_COLUMN(McGrandMotherId, mcGrandMotherId, int); //! MC: first mother of the mother (DF of the source event), -1 without +DECLARE_SOA_COLUMN(McGrandMotherPdg, mcGrandMotherPdg, int); //! MC: PDG of the grandmother +DECLARE_SOA_COLUMN(McAncestorId, mcAncestorId, int); //! MC: first physical primary along the mothers of the leg's mother (itself if primary), -1 without +DECLARE_SOA_COLUMN(McGGMotherId, mcGGMotherId, int); //! MC: mother of the grandmother (e.g. the eta of eta -> 3 pi0), -1 without +DECLARE_SOA_COLUMN(McGGMotherPdg, mcGGMotherPdg, int); //! MC: PDG of that particle +DECLARE_SOA_COLUMN(McRootId, mcRootId, int); //! MC: last particle along the first mothers of the leg's mother (no mother), -1 without +DECLARE_SOA_COLUMN(McRootPdg, mcRootPdg, int); //! MC: PDG of the root +DECLARE_SOA_COLUMN(McRootDepth, mcRootDepth, int); //! MC: generations from the leg's mother to the root +DECLARE_SOA_COLUMN(McVx, mcVx, float); //! MC: production vertex of the leg = true conversion point (lab frame of the source event; translated with the photon), 0 without +DECLARE_SOA_COLUMN(McVy, mcVy, float); //! +DECLARE_SOA_COLUMN(McVz, mcVz, float); //! +} // namespace pcmmixleg +// one row per leg of an artificial event; the columns the builder reads from a track carry the same names as in the AO2D +DECLARE_SOA_TABLE(PCMMixLegs, "AOD", "PCMMIXLEG", //! + o2::soa::Index<>, pcmmixleg::CollisionId, v0leg::Sign, + track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, pcmmixleg::Pt, pcmmixleg::Phi, + pcmmixleg::CYY, pcmmixleg::CZY, pcmmixleg::CZZ, pcmmixleg::CSnpY, pcmmixleg::CSnpZ, pcmmixleg::CSnpSnp, + pcmmixleg::CTglY, pcmmixleg::CTglZ, pcmmixleg::CTglSnp, pcmmixleg::CTglTgl, + pcmmixleg::C1PtY, pcmmixleg::C1PtZ, pcmmixleg::C1PtSnp, pcmmixleg::C1PtTgl, pcmmixleg::C1Pt21Pt2, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + pcmmixleg::SourceEventUid, pcmmixleg::SourcePhotonUid, pcmmixleg::SourceV0Id, + pcmmixleg::SourcePx, pcmmixleg::SourcePy, pcmmixleg::SourcePz, pcmmixleg::SourceVx, pcmmixleg::SourceVy, pcmmixleg::SourceVz, + pcmmixleg::SourcePvX, pcmmixleg::SourcePvY, pcmmixleg::SourcePvZ, pcmmixleg::SourcePvCovXX, pcmmixleg::SourcePvCovXY, pcmmixleg::SourcePvCovXZ, + pcmmixleg::SourcePvCovYY, pcmmixleg::SourcePvCovYZ, pcmmixleg::SourcePvCovZZ, pcmmixleg::SourcePvChi2, pcmmixleg::SourcePvNContrib, + pcmmixleg::OrigTrackId, pcmmixleg::McParticleId, pcmmixleg::McMotherId, pcmmixleg::McPdg, pcmmixleg::McMotherPdg, + pcmmixleg::McMotherPx, pcmmixleg::McMotherPy, pcmmixleg::McMotherPz, pcmmixleg::McGrandMotherId, pcmmixleg::McGrandMotherPdg, pcmmixleg::McAncestorId, + pcmmixleg::McGGMotherId, pcmmixleg::McGGMotherPdg, pcmmixleg::McRootId, pcmmixleg::McRootPdg, pcmmixleg::McRootDepth, pcmmixleg::McVx, pcmmixleg::McVy, pcmmixleg::McVz, + + // dynamic column + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF); +using PCMMixLeg = PCMMixLegs::iterator; + +// free electron legs of builder run 1 (fillFreeLegs): tracks that pass the leg cuts and are the leg of no stored photon; the +// parameters as the V0 fit would use them (TPC-only legs moved to their own collision). The mixer pools them with the photons +// (cfgPoolFreeLegs), so that run 2 can pair them in the same and the mixed event alike. +namespace pcmfreeleg +{ +DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! collision of the track +DECLARE_SOA_COLUMN(TrackId, trackId, int); //! AO2D track +} // namespace pcmfreeleg +DECLARE_SOA_TABLE(PCMFreeLegs, "AOD", "PCMFREELEG", //! + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreeleg::TrackId, v0leg::Sign, + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + track::HasITS, track::HasTPC); +using PCMFreeLeg = PCMFreeLegs::iterator; + +// free legs as AO2D track indices only (linked derived data): run 1 writes (collision, track) of every free leg of a selected +// collision; associate-photon-to-emevent adds the derived event row. The mixer on linked derived data reads the tracks from the parent AO2D. +namespace pcmfreelegid +{ +DECLARE_SOA_INDEX_COLUMN(PMEvent, pmevent); //! derived event of the free leg (-1: collision not in PMEvents) +DECLARE_SOA_COLUMN(TrackIdDelta, trackIdDelta, int); //! AO2D track index minus the one of the previous row of this DF (first row: the index): small numbers, ~5x smaller on disk +} // namespace pcmfreelegid +DECLARE_SOA_TABLE(PCMFreeLegIds, "AOD", "PCMFREELEGID", //! rows in increasing track order; track index = running sum of TrackIdDelta over the DF + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreelegid::TrackIdDelta); +using PCMFreeLegId = PCMFreeLegIds::iterator; +DECLARE_SOA_TABLE(PCMFreeLegEMEventIds, "AOD", "PCMFREELEGEVID", pcmfreelegid::PMEventId); //! to be joined with PCMFreeLegIds + +// builder run 2 output (same columns as the run-1 tables; collisionId = PCMMixCollisions row, trackId = PCMMixLegs row, +// v0Id >= 0: original pairing of run 1, -1: crossed pairing) +DECLARE_SOA_TABLE(V0LegsMix, "AOD", "V0LEGMIX", //! + o2::soa::Index<>, v0leg::CollisionId, v0leg::TrackId, v0leg::Sign, + v0leg::Px, v0leg::Py, v0leg::Pz, + track::DcaXY, track::DcaZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + v0leg::P, + v0leg::Pt, + v0leg::Eta, + v0leg::Phi, + v0leg::Tgl, + + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + v0leg::MeanClusterSizeITS, + v0leg::MeanClusterSizeITSib, + v0leg::MeanClusterSizeITSob); +using V0LegMix = V0LegsMix::iterator; + +namespace v0photonkfmix +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, V0LegsMix, "_Pos"); //! +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, V0LegsMix, "_Neg"); //! +DECLARE_SOA_DYNAMIC_COLUMN(PMEventId, pmeventId, [](int collisionId) -> int { return collisionId; }); //! event index as in V0KFEMEventIds (= PCMMixCollisions row), for V0PhotonCut +} // namespace v0photonkfmix +DECLARE_SOA_TABLE(V0PhotonsKFMix, "AOD", "V0PHOTONKFMIX", //! + o2::soa::Index<>, v0photonkf::CollisionId, v0photonkf::V0Id, v0photonkfmix::PosTrackId, v0photonkfmix::NegTrackId, + v0photonkf::Vx, v0photonkf::Vy, v0photonkf::Vz, + v0photonkf::Px, v0photonkf::Py, v0photonkf::Pz, + v0photonkf::MGamma, + v0photonkf::DCAxyToPV, v0photonkf::DCAzToPV, + v0photonkf::CosPA, v0photonkf::CosPAXY, v0photonkf::CosPARZ, v0photonkf::PCA, + v0photonkf::Alpha, v0photonkf::QtArm, + v0photonkf::ChiSquareNDF, + + // dynamic column + v0photonkf::E, + v0photonkf::Pt, + v0photonkf::Eta, + v0photonkf::Phi, + v0photonkf::P, + v0photonkf::V0Radius, + v0photonkfmix::PMEventId); +using V0PhotonKFMix = V0PhotonsKFMix::iterator; + +DECLARE_SOA_TABLE(V0LegsXYZMix, "AOD", "V0LEGXYZMIX", track::X, track::Y, track::Z); //! +DECLARE_SOA_TABLE(V0LegsDeDxMCMix, "AOD", "V0LEGDEDXMCMIX", mcpidtpc::DeDxTunedMc, o2::soa::Marker<3>); //! stays empty (run 2 runs without MC) +DECLARE_SOA_TABLE(V0PhotonsPhiVPsiMix, "AOD", "V0PHOTONPHIVMIX", o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); //! +DECLARE_SOA_TABLE(V0LegsCovMix, "AOD", "V0LEGCOVMIX", //! + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov); + namespace dalitzee { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! diff --git a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt index b7eaafafcaf..9f787c4bb06 100644 --- a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt +++ b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt @@ -21,6 +21,16 @@ o2physics_add_dpl_workflow(photon-conversion-builder-tmptable PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(photon-conversion-builder-mixed + SOURCES photonconversionbuilderMixed.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(photon-mixed-event-producer + SOURCES photonMixedEventProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(create-emevent-photon SOURCES createEMEventPhoton.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore diff --git a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx index 03aba7421de..85bf97981e0 100644 --- a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx @@ -41,6 +41,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -67,6 +68,8 @@ struct CreateEMEventPhoton { Produces emswtbit; Produces event_norm_info; Produces eventWeights; + Produces eventXY; // filled only with fillVertex + Produces eventCov; // filled only with fillVertex enum class EMEventType : int { kEvent = 0, @@ -75,6 +78,7 @@ struct CreateEMEventPhoton { kEvent_JJ = 3, }; + Configurable fillVertex{"fillVertex", false, "fill EMEventsXY and EMEventsCov (primary vertex x, y, covariance, chi2; the tables are written in any case, empty if false), joinable to PMEvents: needed by the co-reconstructed mixed event on derived data (photon-mixed-event-producer processDerived)"}; Configurable needEMCTrigger{"needEMCTrigger", false, "flag to only save events which have kTVXinEMC trigger bit. To reduce PbPb derived data size"}; Configurable needPHSTrigger{"needPHSTrigger", false, "flag to only save events which have kTVXinPHOS trigger bit. To reduce PbPb derived data size"}; Configurable enableJJHistograms{"enableJJHistograms", false, "flag to fill JJ QA histograms for outlier rejection"}; @@ -149,7 +153,10 @@ struct CreateEMEventPhoton { collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); eventalias(collision.alias_raw()); - // eventCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + if (fillVertex) { + eventXY(collision.posX(), collision.posY()); + eventCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + } eventMult(collision.multFT0A(), collision.multFT0C(), collision.multNTracksPV(), collision.multNTracksPVeta1(), collision.multNTracksPVetaHalf()); @@ -279,6 +286,7 @@ struct CreateEMEventPhoton { }; struct AssociatePhotonToEMEvent { Produces v0kfeventid; + Produces freelegeventid; Produces prmeleventid; Produces phoseventid; Produces emceventid; @@ -312,6 +320,19 @@ struct AssociatePhotonToEMEvent { fillEventId(collisions, photons, v0kfeventid, perCollisionPCM); } + // free legs of run 1 (PCMFreeLegIds, linked derived data): one row per free leg, in its order; -1 when the collision has no PMEvent + void processPCMFreeLegs(aod::PMEvents const& collisions, aod::PCMFreeLegIds const& freeLegs) + { + std::unordered_map eventOf; + for (const auto& collision : collisions) { + eventOf[collision.collisionId()] = collision.globalIndex(); + } + for (const auto& leg : freeLegs) { + const auto it = eventOf.find(leg.collisionId()); + freelegeventid(it == eventOf.end() ? -1 : it->second); + } + } + void processElectronFromDalitz(aod::PMEvents const& collisions, aod::EMPrimaryElectronsFromDalitz const& tracks) { fillEventId(collisions, tracks, prmeleventid, perCollisionEl); @@ -335,6 +356,7 @@ struct AssociatePhotonToEMEvent { void processDummy(aod::PMEvents const&) {} PROCESS_SWITCH(AssociatePhotonToEMEvent, processPCM, "process pcm-event indexing", false); + PROCESS_SWITCH(AssociatePhotonToEMEvent, processPCMFreeLegs, "free legs of run 1 (PCMFreeLegIds) -> derived event index, for the mixer on linked derived data", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processElectronFromDalitz, "process dalitzee-event indexing", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processPHOS, "process phos-event indexing", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processEMC, "process emc-event indexing", false); diff --git a/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx b/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx new file mode 100644 index 00000000000..5e873e46102 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonMixedEventProducer.cxx @@ -0,0 +1,787 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonMixedEventProducer.cxx +/// \brief This task implements a new mixed event method and is the necessary step between builder run 1 and run 2. +// At first it pools of the run-1 events (photons and free legs,copies with their legs) in z x occupancy bins; +// per event one same-event artificial event (the complete event) and cfgOverlay mixed artificial events +// (the complete event + one complete partner event of its pool, translated to the +/// current vertex), written as PCMMixCollisions / PCMMixLegs for photon-conversion-builder-mixed. Needs builder run 1 with +/// fillLegCov = true and fillFreeLegs = true. +/// \author Stefanie Mrozinski + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace +{ +constexpr int kMaxAncestorDepth = 100; // mother-chain steps searched for the physical primary + +// copy of one leg +struct LegCopy { + int8_t sign = 0; + float x = 0.f, alpha = 0.f, y = 0.f, z = 0.f, snp = 0.f, tgl = 0.f, signed1Pt = 0.f, pt = 0.f, phi = 0.f; + std::array cov{}; + uint8_t tpcNClsFindable = 0; + int8_t tpcNClsFindableMinusFound = 0, tpcNClsFindableMinusCrossedRows = 0; + uint8_t tpcNClsShared = 0; + float tpcChi2NCl = 0.f, tpcInnerParam = 0.f, tpcSignal = 0.f, tpcNSigmaEl = 0.f, tpcNSigmaPi = 0.f; + uint32_t itsClusterSizes = 0; + float itsChi2NCl = 0.f; + uint8_t detectorMap = 0; + int origTrackId = -1, mcParticleId = -1, mcMotherId = -1, mcPdg = 0, mcMotherPdg = 0, mcGrandMotherId = -1, mcGrandMotherPdg = 0, mcAncestorId = -1; + int mcGGMotherId = -1, mcGGMotherPdg = 0, mcRootId = -1, mcRootPdg = 0, mcRootDepth = -1; + float mcMotherPx = 0.f, mcMotherPy = 0.f, mcMotherPz = 0.f; + float mcVx = 0.f, mcVy = 0.f, mcVz = 0.f; // MC production vertex of the leg = true conversion point +}; + +struct PhotonCopy { + int64_t eventUid = -1, photonUid = -1; + int v0Id = -1; + float px = 0.f, py = 0.f, pz = 0.f, vx = 0.f, vy = 0.f, vz = 0.f; + float pvX = 0.f, pvY = 0.f, pvZ = 0.f, pvCovXX = 0.f, pvCovXY = 0.f, pvCovXZ = 0.f, pvCovYY = 0.f, pvCovYZ = 0.f, pvCovZZ = 0.f, pvChi2 = 0.f; // primary vertex of its event + uint16_t pvNContrib = 0; + std::vector legs; + int timesUsed = 0; // in mixed artificial events +}; + +struct EventCopy { + int64_t eventUid = -1; + float x = 0.f, y = 0.f, z = 0.f, occupancy = 0.f, cent = 0.f; + std::vector photons; + std::vector freeLegs; +}; + +// vertex and centralities of the event whose vertex an artificial event gets +struct VertexCopy { + float x = 0.f, y = 0.f, z = 0.f, covXX = 0.f, covXY = 0.f, covXZ = 0.f, covYY = 0.f, covYZ = 0.f, covZZ = 0.f, chi2 = 0.f; + uint16_t numContrib = 0; + float centFT0M = 0.f, centFT0A = 0.f, centFT0C = 0.f; + int runNumber = 0, multNTracksPVeta1 = 0; // run and multiplicities of the event whose vertex it is (photonhbt: CCDB, 2D CF axes) + float multFT0M = 0.f; + uint64_t timestamp = 0; // CCDB timestamp +}; +} // namespace + +struct PhotonMixedEventProducer { + using MyCollisions = soa::Join; + using MyTracksMC = soa::Join; + using MyEvents = soa::Join; + using MyPhotonsDerived = soa::Join; + using MyLegsMCDerived = soa::Join; + + Produces mixCollisions; + Produces mixLegs; + + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "centrality estimator: 0 FT0M, 1 FT0A, 2 FT0C"}; + Configurable cfgCentMin{"cfgCentMin", 10.f, "min centrality (one class)"}; + Configurable cfgCentMax{"cfgCentMax", 20.f, "max centrality"}; + Configurable cfgZMax{"cfgZMax", 10.f, "max |z_vtx| (cm)"}; + Configurable cfgZBinWidth{"cfgZBinWidth", 2.f, "pool z bin width (cm)"}; + Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "occupancy: 0 trackOccupancyInTimeRange, 1 ft0cOccupancyInTimeRange"}; + Configurable> cfgOccupancyBins{"cfgOccupancyBins", {-1.f, 1.e9f}, "pool occupancy bin edges (events outside are not used)"}; + Configurable cfgPoolDepth{"cfgPoolDepth", 50, "events per pool (has to exceed the photon multiplicity N)"}; + Configurable cfgNMixPerEvent{"cfgNMixPerEvent", 1, "mixed artificial events per event"}; + Configurable cfgRequireFullPool{"cfgRequireFullPool", true, "skip an event completely (same and mixed) if its pool has fewer than N events; false: write the same-event one and no mixed one"}; + Configurable cfgSeed{"cfgSeed", 12345, "random seed (choice of partner events and photons)"}; + Configurable cfgAlignToVertex{"cfgAlignToVertex", true, "translate the legs and the conversion point of each partner photon by (current vertex - partner vertex): every photon of the mixed artificial event originates from its vertex, as in the same event"}; + Configurable cfgPoolGroups{"cfgPoolGroups", false, "pool conflict groups instead of photons (run 1 with deduplicationMode 3 = keep all): candidates sharing a track form one group with all its tracks once; the decision is taken only in run 2, after mixing, identically for the same and the mixed event"}; + Configurable cfgPhotonWeighted{"cfgPhotonWeighted", true, "partner events drawn with probability proportional to their photon number (each pooled photon equally likely, still N distinct events); false: events equally likely"}; + Configurable cfgInject{"cfgInject", 0, "injection mixed event: instead of N photons from N partner events, K mixed artificial events per event, each = the COMPLETE current event (its photons and, with cfgPoolFreeLegs, its free legs) + ONE photon of a distinct partner event (translated with cfgAlignToVertex). K = cfgInject, -1: K = N (mixed pairs = 2 x same-event pairs), -2: K = N/2 rounded up (mixed pairs = same-event pairs, half the CPU); the pool needs K partner events. Run 2 with mixed_group.skipSameSourceCrossed: the current event's pairings stay as run 1 decided them, only the injected legs form new candidates (also with the free legs, which the N-photon mixed event cannot offer)"}; + Configurable cfgOverlay{"cfgOverlay", 1, "two-event leg overlay: K mixed artificial events per event, each = the COMPLETE current event + the COMPLETE partner event k (all its photons and, with cfgPoolFreeLegs, all its free legs, translated with cfgAlignToVertex). Every leg configuration of the same event exists across the two events, incl. true pairings that failed a run-1 cut. Run 2 with mixed_group.skipSameSourceCrossed: pairings inside one source event stay as run 1 decided them, only cross-event pairings are new. 0 = off (not with cfgInject)"}; + Configurable cfgPoolFreeLegs{"cfgPoolFreeLegs", true, "pool the free legs of run 1 (PCMFreeLegs, builder fillFreeLegs) with the photons: the same artificial event gets all M free legs of the event, the mixed one M free legs drawn from the N partner events, so that run 2 pairs free legs identically in both"}; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + std::map, std::deque> mPools; + std::mt19937 mRandom; + int mRunNumber = -1; + int64_t mEventUid = 0; + int64_t mPhotonUid = 0; + int64_t mNPooledPhotons = 0; + + enum CutFlow { kAll = 0, + kSelected, + kCent, + kZ, + kOccupancy, + kHasPhotons, + kPoolTooSmall, + kWritten }; + + void init(InitContext&) + { + mRandom.seed(cfgSeed.value); + if (cfgOccupancyBins->size() < 2) { + LOG(fatal) << "cfgOccupancyBins needs at least two edges"; + } + const AxisSpec axType{2, -0.5f, 1.5f, "artificial event (0 same, 1 mixed)"}; + const AxisSpec axN{101, -0.5f, 100.5f, "photons per event N"}; + const int nZ = static_cast(std::lround(2.f * cfgZMax / cfgZBinWidth)); + registry.add("Event/hCutFlow", "events;0 all, 1 selected, 2 centrality, 3 z, 4 occupancy, 5 with photons, 6 pool too small (skipped), 7 written", kTH1D, {{8, -0.5f, 7.5f}}); + registry.add("Event/hNPhotons", "accepted events;photons per event N", kTH1D, {axN}); + registry.add("Event/hNFreeLegs", "accepted events (cfgPoolFreeLegs);free legs per event M", kTH1D, {{300, -0.5f, 299.5f}}); + registry.add("Mix/hFreeLegsMixedVsCurrent", "cfgPoolFreeLegs;M of the current event;free legs in the mixed artificial event", kTH2D, {{300, -0.5f, 299.5f}, {300, -0.5f, 299.5f}}); + registry.add("Event/hNPhotonsMixedVsCurrent", "mixed artificial events (must be the diagonal);N of the current event;photons in the mixed event", kTH2D, {axN, axN}); + registry.add("Event/hZ", "accepted events;z_{vtx} (cm)", kTH1D, {{nZ, -cfgZMax, cfgZMax}}); + registry.add("Event/hOccupancy", "accepted events;occupancy", kTH1D, {{200, 0.f, 20000.f}}); + registry.add("Pool/hDepthVsZ", "pool size when used;z_{vtx} (cm);events in the pool", kTH2D, {{nZ, -cfgZMax, cfgZMax}, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hDepthVsOccBin", "pool size when used;occupancy bin;events in the pool", kTH2D, {{static_cast(cfgOccupancyBins->size()) - 1, -0.5f, cfgOccupancyBins->size() - 1.5f}, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hPartnersVsN", "partner events available vs needed;N of the current event;events in the pool", kTH2D, {axN, {cfgPoolDepth + 1, -0.5f, cfgPoolDepth + 0.5f}}); + registry.add("Pool/hNPooledPhotons", "photons in all pools (memory), per event;photons", kTH1D, {{500, 0.f, 5.e5f}}); + registry.add("Pool/hNPools", "filled pools, per event;pools", kTH1D, {{1000, 0.f, 10000.f}}); + registry.add("Pool/hResets", "pool resets (run change);resets", kTH1D, {{1, -0.5f, 0.5f}}); + registry.add("Pool/hTimesUsed", "pooled photons leaving the pool;times used in a mixed event", kTH1D, {{51, -0.5f, 50.5f}}); + registry.add("Mix/hDz", "mixed photons;z_{vtx}^{partner} - z_{vtx}^{current} (cm)", kTH1D, {{200, -0.5f, 0.5f}}); + registry.add("Mix/hDOccupancy", "mixed photons;occupancy^{partner} - occupancy^{current}", kTH1D, {{200, -5000.f, 5000.f}}); + registry.add("Mix/hDCent", "mixed photons;centrality^{partner} - centrality^{current} (%)", kTH1D, {{200, -10.f, 10.f}}); + registry.add("Mix/hPartnerAge", "mixed photons;position of the partner event in the pool (0 = newest)", kTH1D, {{cfgPoolDepth, -0.5f, cfgPoolDepth - 0.5f}}); + registry.add("Mix/hDistinctSourcesMinusN", "mixed artificial events (must be 0);distinct source events - N", kTH1D, {{21, -10.5f, 10.5f}}); + registry.add("Mix/hSourceIsCurrent", "mixed photons from the current event (must be 0);0 other event, 1 current event", kTH1D, {{2, -0.5f, 1.5f}}); + registry.add("Mix/hGroupLegs", "cfgPoolGroups: tracks per conflict group;tracks;groups", kTH1D, {{30, 0.5f, 30.5f}}); + registry.add("Event/hNCandidates", "cfgPoolGroups: run-1 candidates per event (before grouping);candidates;events", kTH1D, {{200, -0.5f, 199.5f}}); + registry.add("Mix/hLegsMinus2N", "artificial events (must be 0);artificial event;legs written - 2 N", kTH2D, {axType, {21, -10.5f, 10.5f}}); + registry.add("Mix/hPhotonPtEta", "photons of the artificial events;artificial event;p_{T} (GeV/c);#eta", kTH3D, {axType, {100, 0.f, 5.f}, {40, -1.f, 1.f}}); + registry.add("Mix/hLegTypes", "legs of the artificial events;artificial event;0 ITS-TPC, 1 TPC-only, 2 ITS-only, 3 other", kTH2D, {axType, {4, -0.5f, 3.5f}}); + // AO2D input: leg pT and phi of the track vs the values computed from V0LegsCov (what the derived-data mode uses), must be 0 + registry.add("Leg/hPtPhiFromCov", "legs (AO2D input);(p_{T}^{track} - p_{T}^{cov}) / p_{T}^{track};#varphi^{track} - #varphi^{cov} (rad)", kTH2D, {{100, -1.e-4f, 1.e-4f}, {100, -1.e-4f, 1.e-4f}}); + if (static_cast(doprocessRec.value) + static_cast(doprocessMC.value) + static_cast(doprocessDerived.value) + static_cast(doprocessDerivedMC.value) != 1) { + LOG(fatal) << "enable exactly one of processRec, processMC (AO2D input) and processDerived, processDerivedMC (derived data)"; + } + if (cfgOverlay != 0 && cfgInject != 0) { + LOG(fatal) << "cfgOverlay and cfgInject are two different mixed events, enable one"; + } + if (cfgOverlay != 0 && !cfgPoolFreeLegs) { + LOG(warning) << "cfgOverlay without cfgPoolFreeLegs: the partner event brings its stored photons only, not its free legs"; + } + if (cfgPoolFreeLegs && (doprocessDerived || doprocessDerivedMC)) { + LOG(fatal) << "cfgPoolFreeLegs needs PCMFreeLegs, which are not in the derived data"; + } + } + + template + float centrality(TCollision const& collision) const + { + switch (cfgCentEstimator.value) { + case 0: + return collision.centFT0M(); + case 1: + return collision.centFT0A(); + default: + return collision.centFT0C(); + } + } + + template + float occupancy(TCollision const& collision) const + { + return cfgOccupancyEstimator.value == 1 ? collision.ft0cOccupancyInTimeRange() : static_cast(collision.trackOccupancyInTimeRange()); + } + + int occupancyBin(const float occ) const + { + const auto& edges = cfgOccupancyBins.value; + for (size_t i = 0; i + 1 < edges.size(); ++i) { + if (edges[i] <= occ && occ < edges[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + // leg pT and phi from the fit-input parameters + template + static std::pair ptPhiOf(TLegCov const& legCov) + { + const float pt = legCov.signed1Pt() != 0.f ? 1.f / std::fabs(legCov.signed1Pt()) : 0.f; + float phi = std::asin(std::clamp(legCov.snp(), -1.f, 1.f)) + legCov.alpha(); + phi = std::fmod(phi, o2::constants::math::TwoPI); + if (phi < 0.f) { + phi += o2::constants::math::TwoPI; + } + return {pt, phi}; + } + + // MC history of a leg + template + static void fillLegMC(LegCopy& c, TMCParticle const& mcLeg, TMCParticles const& mcParticles) + { + c.mcParticleId = mcLeg.globalIndex(); + c.mcPdg = mcLeg.pdgCode(); + c.mcVx = mcLeg.vx(); + c.mcVy = mcLeg.vy(); + c.mcVz = mcLeg.vz(); + if (!mcLeg.has_mothers()) { + return; + } + const auto mother = mcParticles.rawIteratorAt(mcLeg.mothersIds()[0]); + c.mcMotherId = mother.globalIndex(); + c.mcMotherPdg = mother.pdgCode(); + c.mcMotherPx = mother.px(); + c.mcMotherPy = mother.py(); + c.mcMotherPz = mother.pz(); + if (mother.has_mothers()) { // e.g. the e+- that radiated a bremsstrahlung photon + const auto grandMother = mcParticles.rawIteratorAt(mother.mothersIds()[0]); + c.mcGrandMotherId = grandMother.globalIndex(); + c.mcGrandMotherPdg = grandMother.pdgCode(); + if (grandMother.has_mothers()) { // e.g. the eta of eta -> 3 pi0 + const auto ggMother = mcParticles.rawIteratorAt(grandMother.mothersIds()[0]); + c.mcGGMotherId = ggMother.globalIndex(); + c.mcGGMotherPdg = ggMother.pdgCode(); + } + } + auto root = mother; + int rootDepth = 0; + for (; root.has_mothers() && rootDepth < kMaxAncestorDepth; ++rootDepth) { + root = mcParticles.rawIteratorAt(root.mothersIds()[0]); + } + c.mcRootId = static_cast(root.globalIndex()); + c.mcRootPdg = root.pdgCode(); + c.mcRootDepth = rootDepth; + auto anc = mother; + for (int depth = 0; !anc.isPhysicalPrimary() && anc.has_mothers() && depth < kMaxAncestorDepth; ++depth) { + anc = mcParticles.rawIteratorAt(anc.mothersIds()[0]); + } + c.mcAncestorId = anc.isPhysicalPrimary() ? static_cast(anc.globalIndex()) : -1; + } + + template + LegCopy copyLeg(TLeg const& leg, TLegCov const& legCov, [[maybe_unused]] TTracks const* tracks, [[maybe_unused]] TMCParticles const* mcParticles) + { + LegCopy c; + c.sign = leg.sign(); + c.x = legCov.x(); + c.alpha = legCov.alpha(); + c.y = legCov.y(); + c.z = legCov.z(); + c.snp = legCov.snp(); + c.tgl = legCov.tgl(); + c.signed1Pt = legCov.signed1Pt(); + const auto& cov = legCov.cov(); + for (size_t i = 0; i < c.cov.size(); ++i) { + c.cov[i] = cov[i]; + } + c.tpcNClsFindable = leg.tpcNClsFindable(); + c.tpcNClsFindableMinusFound = leg.tpcNClsFindableMinusFound(); + c.tpcNClsFindableMinusCrossedRows = leg.tpcNClsFindableMinusCrossedRows(); + c.tpcNClsShared = leg.tpcNClsShared(); + c.tpcChi2NCl = leg.tpcChi2NCl(); + c.tpcInnerParam = leg.tpcInnerParam(); + c.tpcSignal = leg.tpcSignal(); + c.tpcNSigmaEl = leg.tpcNSigmaEl(); + c.tpcNSigmaPi = leg.tpcNSigmaPi(); + c.itsClusterSizes = leg.itsClusterSizes(); + c.itsChi2NCl = leg.itsChi2NCl(); + c.detectorMap = leg.detectorMap(); + c.origTrackId = leg.trackId(); + const auto [ptCov, phiCov] = ptPhiOf(legCov); + if constexpr (isDerived) { + c.pt = ptCov; + c.phi = phiCov; + if constexpr (isMC) { + if (leg.has_emmcparticle()) { + fillLegMC(c, leg.template emmcparticle_as(), *mcParticles); + } + } + } else { + const auto track = tracks->rawIteratorAt(leg.trackId()); + c.pt = track.pt(); // as the builder reads them from the AO2D track + c.phi = track.phi(); + registry.fill(HIST("Leg/hPtPhiFromCov"), c.pt > 0.f ? (c.pt - ptCov) / c.pt : 0.f, RecoDecay::constrainAngle(c.phi - phiCov, -o2::constants::math::PI)); + if constexpr (isMC) { + if (track.has_mcParticle()) { + fillLegMC(c, track.template mcParticle_as(), *mcParticles); + } + } + } + return c; + } + + void writeLeg(LegCopy const& l, PhotonCopy const& p) + { + mixLegs(mixCollisions.lastIndex(), l.sign, + l.x, l.alpha, l.y, l.z, l.snp, l.tgl, l.signed1Pt, l.pt, l.phi, + l.cov[0], l.cov[1], l.cov[2], l.cov[3], l.cov[4], l.cov[5], l.cov[6], l.cov[7], l.cov[8], l.cov[9], l.cov[10], l.cov[11], l.cov[12], l.cov[13], l.cov[14], + l.tpcNClsFindable, l.tpcNClsFindableMinusFound, l.tpcNClsFindableMinusCrossedRows, l.tpcNClsShared, + l.tpcChi2NCl, l.tpcInnerParam, l.tpcSignal, l.tpcNSigmaEl, l.tpcNSigmaPi, + l.itsClusterSizes, l.itsChi2NCl, l.detectorMap, + p.eventUid, p.photonUid, p.v0Id, p.px, p.py, p.pz, p.vx, p.vy, p.vz, + p.pvX, p.pvY, p.pvZ, p.pvCovXX, p.pvCovXY, p.pvCovXZ, p.pvCovYY, p.pvCovYZ, p.pvCovZZ, p.pvChi2, p.pvNContrib, + l.origTrackId, l.mcParticleId, l.mcMotherId, l.mcPdg, l.mcMotherPdg, l.mcMotherPx, l.mcMotherPy, l.mcMotherPz, l.mcGrandMotherId, l.mcGrandMotherPdg, l.mcAncestorId, + l.mcGGMotherId, l.mcGGMotherPdg, l.mcRootId, l.mcRootPdg, l.mcRootDepth, l.mcVx, l.mcVy, l.mcVz); + } + + static PhotonCopy translated(PhotonCopy const& p, const float dx, const float dy, const float dz) + { + PhotonCopy t = p; + t.vx += dx; + t.vy += dy; + t.vz += dz; + t.pvX += dx; // its own vertex moves with it + t.pvY += dy; + t.pvZ += dz; + for (auto& l : t.legs) { + const float ca = std::cos(l.alpha), sa = std::sin(l.alpha); + l.x += dx * ca + dy * sa; + l.y += -dx * sa + dy * ca; + l.z += dz; + l.mcVx += dx; // the true conversion point moves with the photon + l.mcVy += dy; + l.mcVz += dz; + } + return t; + } + + void writeArtificialEvent(VertexCopy const& v, EventCopy const& current, const bool isMixed, const int zBin, const int occBin, + std::vector const& photons, const int currentCollisionId) + { + const float type = isMixed ? 1.f : 0.f; + const int firstLeg = mixLegs.lastIndex() + 1; + mixCollisions(v.x, v.y, v.z, v.covXX, v.covXY, v.covXZ, v.covYY, v.covYZ, v.covZZ, v.chi2, v.numContrib, + v.centFT0M, v.centFT0A, v.centFT0C, isMixed, currentCollisionId, current.eventUid, + zBin, occBin, current.occupancy, firstLeg, nLegsOf(photons), v.runNumber, v.multNTracksPVeta1, v.multFT0M, v.timestamp); + for (const auto* p : photons) { + if (p->legs.size() > 1) { // not a free-leg stub + registry.fill(HIST("Mix/hPhotonPtEta"), type, std::hypot(p->px, p->py), std::asinh(p->pz / std::hypot(p->px, p->py))); + } + for (const auto& l : p->legs) { + const bool hasITS = (l.detectorMap & o2::aod::track::ITS) != 0; + const bool hasTPC = (l.detectorMap & o2::aod::track::TPC) != 0; + registry.fill(HIST("Mix/hLegTypes"), type, hasITS && hasTPC ? 0.f : (hasTPC ? 1.f : (hasITS ? 2.f : 3.f))); + writeLeg(l, *p); + } + } + registry.fill(HIST("Mix/hLegsMinus2N"), type, (mixLegs.lastIndex() + 1 - firstLeg) - nLegsOf(photons)); + } + + static int nLegsOf(std::vector const& photons) + { + int n = 0; + for (const auto* p : photons) { + n += static_cast(p->legs.size()); + } + return n; + } + + std::vector toGroups(std::vector const& cands) + { + const int n = cands.size(); + std::vector parent(n); + std::iota(parent.begin(), parent.end(), 0); + std::function find = [&](int i) { return parent[i] == i ? i : parent[i] = find(parent[i]); }; + std::unordered_map firstOfTrack; + for (int i = 0; i < n; ++i) { + for (const auto& l : cands[i].legs) { + const auto [it, inserted] = firstOfTrack.emplace(l.origTrackId, i); + if (!inserted) { + parent[find(i)] = find(it->second); + } + } + } + std::map groups; // root -> group + std::map> tracksOfGroup; + for (int i = 0; i < n; ++i) { + const int r = find(i); + auto [it, inserted] = groups.try_emplace(r, cands[i]); + if (inserted) { + it->second.legs.clear(); + it->second.photonUid = mPhotonUid++; + } + for (const auto& l : cands[i].legs) { + if (tracksOfGroup[r].insert(l.origTrackId).second) { + it->second.legs.push_back(l); + } + } + } + std::vector out; + for (auto& [r, g] : groups) { + registry.fill(HIST("Mix/hGroupLegs"), static_cast(g.legs.size())); + out.push_back(std::move(g)); + } + return out; + } + + template + void runMixer(TCollisions const& collisions, TPhotons const& photons, TLegs const& legs, aod::V0LegsCov const& legsCov, + aod::PCMFreeLegs const* freeLegs, TTracks const* tracks, TMCParticles const* mcParticles) + { + if (legs.size() != 2 * photons.size() || legsCov.size() != legs.size()) { + LOG(fatal) << "V0LegsCov (" << legsCov.size() << ") / V0Legs (" << legs.size() << ") / V0PhotonsKF (" << photons.size() << ") do not match: run builder run 1 with fillLegCov = true"; + } + if (cfgPoolFreeLegs && (freeLegs == nullptr || (freeLegs->size() == 0 && photons.size() > 0))) { + LOG(fatal) << "cfgPoolFreeLegs: PCMFreeLegs is empty, run builder run 1 with fillFreeLegs = true"; + } + std::unordered_map> photonsOfCollision, freeLegsOfCollision; + for (const auto& photon : photons) { + if constexpr (isDerived) { + photonsOfCollision[photon.pmeventId()].push_back(photon.globalIndex()); + } else { + photonsOfCollision[photon.collisionId()].push_back(photon.globalIndex()); + } + } + if (cfgPoolFreeLegs) { + for (const auto& leg : *freeLegs) { + freeLegsOfCollision[leg.collisionId()].push_back(leg.globalIndex()); + } + } + + for (const auto& collision : collisions) { + registry.fill(HIST("Event/hCutFlow"), kAll); + int runNumber = 0; + uint64_t timestamp = 0; + if constexpr (isDerived) { + runNumber = collision.runNumber(); + timestamp = collision.timestamp(); + } else { + const auto bc = collision.template foundBC_as(); + runNumber = bc.runNumber(); + timestamp = bc.timestamp(); + } + if (runNumber != mRunNumber) { + if (mRunNumber >= 0) { + registry.fill(HIST("Pool/hResets"), 0.f); + } + mPools.clear(); + mNPooledPhotons = 0; + mRunNumber = runNumber; + } + if constexpr (!isDerived) { + if (!collision.isSelected()) { + continue; + } + } + registry.fill(HIST("Event/hCutFlow"), kSelected); + const float cent = centrality(collision); + if (cent < cfgCentMin || cfgCentMax <= cent) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kCent); + if (std::fabs(collision.posZ()) >= cfgZMax) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kZ); + const float occ = occupancy(collision); + const int occBin = occupancyBin(occ); + if (occBin < 0) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kOccupancy); + const auto it = photonsOfCollision.find(collision.globalIndex()); + if (it == photonsOfCollision.end()) { + continue; + } + registry.fill(HIST("Event/hCutFlow"), kHasPhotons); + const int zBin = static_cast(std::floor((collision.posZ() + cfgZMax) / cfgZBinWidth)); + registry.fill(HIST("Event/hZ"), collision.posZ()); + registry.fill(HIST("Event/hOccupancy"), occ); + + // copies of this event's photons + EventCopy current; + current.eventUid = mEventUid++; + current.x = collision.posX(); + current.y = collision.posY(); + current.z = collision.posZ(); + current.occupancy = occ; + current.cent = cent; + for (const auto& photonId : it->second) { + const auto photon = photons.rawIteratorAt(photonId); + PhotonCopy p; + p.eventUid = current.eventUid; + p.photonUid = mPhotonUid++; + p.v0Id = photon.v0Id(); + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.pvX = collision.posX(); + p.pvY = collision.posY(); + p.pvZ = collision.posZ(); + p.pvCovXX = collision.covXX(); + p.pvCovXY = collision.covXY(); + p.pvCovXZ = collision.covXZ(); + p.pvCovYY = collision.covYY(); + p.pvCovYZ = collision.covYZ(); + p.pvCovZZ = collision.covZZ(); + p.pvChi2 = collision.chi2(); + p.pvNContrib = collision.numContrib(); + p.legs.push_back(copyLeg(legs.rawIteratorAt(photon.posTrackId()), legsCov.rawIteratorAt(photon.posTrackId()), tracks, mcParticles)); + p.legs.push_back(copyLeg(legs.rawIteratorAt(photon.negTrackId()), legsCov.rawIteratorAt(photon.negTrackId()), tracks, mcParticles)); + current.photons.push_back(p); + } + if (cfgPoolGroups) { + registry.fill(HIST("Event/hNCandidates"), static_cast(current.photons.size())); + current.photons = toGroups(current.photons); + } + if constexpr (!isDerived) { + if (cfgPoolFreeLegs) { // one-leg stubs: event and vertex of this event, no photon momentum, v0Id -2 + if (const auto itF = freeLegsOfCollision.find(collision.globalIndex()); itF != freeLegsOfCollision.end()) { + for (const auto& legId : itF->second) { + const auto leg = freeLegs->rawIteratorAt(legId); + PhotonCopy f; + f.eventUid = current.eventUid; + f.photonUid = mPhotonUid++; + f.v0Id = -2; + f.pvX = collision.posX(); + f.pvY = collision.posY(); + f.pvZ = collision.posZ(); + f.pvCovXX = collision.covXX(); + f.pvCovXY = collision.covXY(); + f.pvCovXZ = collision.covXZ(); + f.pvCovYY = collision.covYY(); + f.pvCovYZ = collision.covYZ(); + f.pvCovZZ = collision.covZZ(); + f.pvChi2 = collision.chi2(); + f.pvNContrib = collision.numContrib(); + f.legs.push_back(copyLeg(leg, leg, tracks, mcParticles)); + current.freeLegs.push_back(f); + } + } + registry.fill(HIST("Event/hNFreeLegs"), static_cast(current.freeLegs.size())); + } + } + const int nPhotons = static_cast(current.photons.size()); + registry.fill(HIST("Event/hNPhotons"), nPhotons); + + auto& pool = mPools[{zBin, occBin}]; + const int depth = static_cast(pool.size()); + registry.fill(HIST("Pool/hPartnersVsN"), nPhotons, depth); + registry.fill(HIST("Pool/hDepthVsZ"), collision.posZ(), depth); + registry.fill(HIST("Pool/hDepthVsOccBin"), occBin, depth); + + VertexCopy v{collision.posX(), collision.posY(), collision.posZ(), collision.covXX(), collision.covXY(), collision.covXZ(), + collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2(), collision.numContrib(), + collision.centFT0M(), collision.centFT0A(), collision.centFT0C(), runNumber, collision.multNTracksPVeta1(), collision.multFT0M(), timestamp}; + const int nPartnersNeeded = cfgOverlay > 0 ? cfgOverlay.value : (cfgInject > 0 ? cfgInject.value : (cfgInject == -2 ? std::max(1, (nPhotons + 1) / 2) : nPhotons)); // overlay / injection: K partner events + const bool poolFull = depth >= nPartnersNeeded; + if (!poolFull) { + registry.fill(HIST("Event/hCutFlow"), kPoolTooSmall); + } + if (poolFull || !cfgRequireFullPool) { + registry.fill(HIST("Event/hCutFlow"), kWritten); + } + std::vector host; // the complete current event: its photons and (cfgPoolFreeLegs) its free legs + host.reserve(current.photons.size() + current.freeLegs.size()); + for (const auto& p : current.photons) { + host.push_back(&p); + } + for (const auto& f : current.freeLegs) { + host.push_back(&f); + } + if (poolFull || !cfgRequireFullPool) { + writeArtificialEvent(v, current, false, zBin, occBin, host, collision.globalIndex()); + } + if (poolFull) { + std::vector order(depth); + auto drawOrder = [&]() { // partner events in random order (cfgPhotonWeighted: weighted sampling without replacement, largest log(u) / w first) + std::iota(order.begin(), order.end(), 0); + if (cfgPhotonWeighted) { + std::uniform_real_distribution uni(0., 1.); + std::vector key(depth); + for (int k = 0; k < depth; ++k) { + key[k] = std::log(std::max(uni(mRandom), 1e-300)) / static_cast(pool[k].photons.size()); // o2-linter: disable=magic-number (log floor) + } + std::sort(order.begin(), order.end(), [&](int x, int y) { return key[x] > key[y]; }); + } else { + std::shuffle(order.begin(), order.end(), mRandom); + } + }; + auto fillPartnerQA = [&](EventCopy const& partner, int age) { + registry.fill(HIST("Mix/hDz"), partner.z - current.z); + registry.fill(HIST("Mix/hDOccupancy"), partner.occupancy - current.occupancy); + registry.fill(HIST("Mix/hDCent"), partner.cent - current.cent); + registry.fill(HIST("Mix/hPartnerAge"), age); + registry.fill(HIST("Mix/hSourceIsCurrent"), partner.eventUid == current.eventUid ? 1.f : 0.f); + }; + if (cfgOverlay > 0) { // overlay: K artificial events = the complete current event + the complete partner event k + for (int iMix = 0; iMix < cfgNMixPerEvent; ++iMix) { + drawOrder(); + for (int k = 0; k < std::min(nPartnersNeeded, depth); ++k) { + auto& partner = pool[order[k]]; + fillPartnerQA(partner, order[k]); + std::vector overlay(host); + std::vector> aligned; // translated copies; reserved, so the pointers stay valid + aligned.reserve(partner.photons.size() + partner.freeLegs.size()); + for (auto* src : {&partner.photons, &partner.freeLegs}) { + for (auto& p : *src) { + if (src == &partner.photons) { + p.timesUsed++; + } + if (cfgAlignToVertex) { + aligned.push_back(std::make_unique(translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z))); + overlay.push_back(aligned.back().get()); + } else { + overlay.push_back(&p); + } + } + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), 2 - nPhotons); // overlay: the current event + one partner + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, nPhotons + static_cast(partner.photons.size())); + writeArtificialEvent(v, current, true, zBin, occBin, overlay, collision.globalIndex()); + } + } + } + if (cfgInject != 0) { + for (int iMix = 0; iMix < cfgNMixPerEvent; ++iMix) { + drawOrder(); + for (int k = 0; k < std::min(nPartnersNeeded, depth); ++k) { + auto& partner = pool[order[k]]; + std::uniform_int_distribution pick(0, partner.photons.size() - 1); + auto& p = partner.photons[pick(mRandom)]; + p.timesUsed++; + fillPartnerQA(partner, order[k]); + std::vector injected(host); + PhotonCopy alignedCopy; + if (cfgAlignToVertex) { + alignedCopy = translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z); + injected.push_back(&alignedCopy); + } else { + injected.push_back(&p); + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), 2 - nPhotons); + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, nPhotons + 1); + writeArtificialEvent(v, current, true, zBin, occBin, injected, collision.globalIndex()); + } + } + } + for (int iMix = 0; iMix < ((cfgInject != 0 || cfgOverlay > 0) ? 0 : cfgNMixPerEvent.value); ++iMix) { + drawOrder(); + std::vector mixedPhotons; + std::vector> aligned; // translated copies (cfgAlignToVertex); reserved, so the pointers stay valid + aligned.reserve(nPhotons + (cfgPoolFreeLegs ? static_cast(current.freeLegs.size()) : 0)); + std::set sources; + for (int k = 0; k < nPhotons; ++k) { // N partner events, one random photon each + auto& partner = pool[order[k]]; + std::uniform_int_distribution pick(0, partner.photons.size() - 1); + auto& p = partner.photons[pick(mRandom)]; + p.timesUsed++; + if (cfgAlignToVertex) { + aligned.push_back(std::make_unique(translated(p, current.x - partner.x, current.y - partner.y, current.z - partner.z))); + // cppcheck-suppress invalidContainer + mixedPhotons.push_back(aligned.back().get()); + } else { + mixedPhotons.push_back(&p); + } + sources.insert(partner.eventUid); + fillPartnerQA(partner, order[k]); + } + registry.fill(HIST("Mix/hDistinctSourcesMinusN"), static_cast(sources.size()) - nPhotons); + registry.fill(HIST("Event/hNPhotonsMixedVsCurrent"), nPhotons, mixedPhotons.size()); + if (cfgPoolFreeLegs) { // M free legs of the current event -> M drawn without replacement from the free legs of the N partner events + std::vector pooledFree; + for (int k = 0; k < nPhotons; ++k) { + for (const auto& f : pool[order[k]].freeLegs) { + pooledFree.push_back(&f); + } + } + std::shuffle(pooledFree.begin(), pooledFree.end(), mRandom); + const size_t nTake = std::min(pooledFree.size(), current.freeLegs.size()); + for (size_t k = 0; k < nTake; ++k) { + if (cfgAlignToVertex) { + const auto& partner = *pooledFree[k]; + aligned.push_back(std::make_unique(translated(partner, current.x - partner.pvX, current.y - partner.pvY, current.z - partner.pvZ))); + // cppcheck-suppress invalidContainer + mixedPhotons.push_back(aligned.back().get()); + } else { + mixedPhotons.push_back(pooledFree[k]); + } + } + registry.fill(HIST("Mix/hFreeLegsMixedVsCurrent"), static_cast(current.freeLegs.size()), static_cast(nTake)); + } + writeArtificialEvent(v, current, true, zBin, occBin, mixedPhotons, collision.globalIndex()); + } + } + + // this event joins its pool after it was mixed (never mixed with itself) + mNPooledPhotons += nPhotons; + pool.push_front(std::move(current)); + if (static_cast(pool.size()) > cfgPoolDepth) { + for (const auto& p : pool.back().photons) { + registry.fill(HIST("Pool/hTimesUsed"), p.timesUsed); + } + mNPooledPhotons -= static_cast(pool.back().photons.size()); + pool.pop_back(); + } + registry.fill(HIST("Pool/hNPooledPhotons"), mNPooledPhotons); + registry.fill(HIST("Pool/hNPools"), mPools.size()); + } + } + + // AO2D input (builder run 1 in the same workflow) + void processRec(MyCollisions const& collisions, aod::BCsWithTimestamps const&, aod::V0PhotonsKF const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov, aod::PCMFreeLegs const& freeLegs, aod::TracksIU const& tracks) + { + runMixer(collisions, photons, legs, legsCov, &freeLegs, &tracks, static_cast(nullptr)); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processRec, "data, AO2D input", true); + + void processMC(MyCollisions const& collisions, aod::BCsWithTimestamps const&, aod::V0PhotonsKF const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov, aod::PCMFreeLegs const& freeLegs, MyTracksMC const& tracks, aod::McParticles const& mcParticles) + { + runMixer(collisions, photons, legs, legsCov, &freeLegs, &tracks, &mcParticles); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processMC, "MC, AO2D input (leg truth into PCMMixLegs)", false); + + // derived data (EM skim with V0LegsCov and, from create-emevent-photon fillVertex, EMEventsXY + EMEventsCov) + void processDerived(MyEvents const& collisions, MyPhotonsDerived const& photons, aod::V0Legs const& legs, aod::V0LegsCov const& legsCov) + { + runMixer(collisions, photons, legs, legsCov, nullptr, static_cast(nullptr), static_cast(nullptr)); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processDerived, "data, derived-data input", false); + + void processDerivedMC(MyEvents const& collisions, MyPhotonsDerived const& photons, MyLegsMCDerived const& legs, aod::V0LegsCov const& legsCov, aod::EMMCParticles const& mcParticles) + { + runMixer(collisions, photons, legs, legsCov, nullptr, static_cast(nullptr), &mcParticles); + } + PROCESS_SWITCH(PhotonMixedEventProducer, processDerivedMC, "MC, derived-data input (leg truth from V0LegMCLabels / EMMCParticles)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"photon-mixed-event-producer"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx index 6ee033f2817..0f210615c7c 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx @@ -23,5 +23,5 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ - adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h index d4edc620927..3c77efa0efd 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -61,8 +61,10 @@ #include #include #include +#include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -76,15 +78,20 @@ #include #include +#include #include #include #include +#include #include #include #include +#include +#include #include #include #include +#include #include #include #include @@ -156,6 +163,9 @@ struct V0CandidateHelper { float svX = 0.f; float svY = 0.f; float svZ = 0.f; + float pxAtSV = 0.f; // momentum at the conversion point without the PV constraint (pointing residuals) + float pyAtSV = 0.f; + float pzAtSV = 0.f; float purity = -999.f; float cosPAxy = -2.f; // transverse pointing angle float cosPA3D = -2.f; // 3D pointing angle @@ -173,6 +183,7 @@ struct V0CandidateHelper { }; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) +// Identifies a V0 candidate by collision, positive-track and negative track IDs, and the SVertexer V0 index (or -1 for finder and resolver photons) struct CandKey { int64_t v0Id = -1; // SVertexer V0 index, -1 for finder and resolver photons int64_t collisionId = -1; @@ -181,37 +192,101 @@ struct CandKey { auto operator<=>(const CandKey&) const = default; }; -// result of a probe fit (FitOptions::probe): leg pair passed the cuts, and its dedup score +// Stores whether a trial fit succeded and the score of the candidate (for the ambiguity resolver, the score is used to decide which of two crossed candidates to keep) struct PairFit { bool ok = false; float score = 999.f; }; // options for fitting a leg pair that was not built by the S-Vertexer (finder, ambiguity resolver) +struct SourcePV { + float x = 0.f, y = 0.f, z = 0.f, cXX = 0.f, cXY = 0.f, cXZ = 0.f, cYY = 0.f, cYZ = 0.f, cZZ = 0.f, c2 = 0.f; + uint16_t nContrib = 0; + [[nodiscard]] float posX() const { return x; } + [[nodiscard]] float posY() const { return y; } + [[nodiscard]] float posZ() const { return z; } + [[nodiscard]] float covXX() const { return cXX; } + [[nodiscard]] float covXY() const { return cXY; } + [[nodiscard]] float covXZ() const { return cXZ; } + [[nodiscard]] float covYY() const { return cYY; } + [[nodiscard]] float covYZ() const { return cYZ; } + [[nodiscard]] float covZZ() const { return cZZ; } + [[nodiscard]] float chi2() const { return c2; } + [[nodiscard]] uint16_t numContrib() const { return nContrib; } +}; + +struct LegDcaKey { + int64_t trackId = -1, collisionId = -1; + float x = 0.f, y = 0.f, z = 0.f; + bool sideBased = false; + bool operator==(LegDcaKey const& o) const { return trackId == o.trackId && collisionId == o.collisionId && x == o.x && y == o.y && z == o.z && sideBased == o.sideBased; } +}; +struct LegDcaKeyHash { + size_t operator()(LegDcaKey const& k) const + { + size_t h = std::hash{}(k.trackId) * 1000003u ^ std::hash{}(k.collisionId); + h = h * 1000003u ^ std::hash{}(k.x); + h = h * 1000003u ^ std::hash{}(k.y); + return (h * 1000003u ^ std::hash{}(k.z)) * 2u + (k.sideBased ? 1u : 0u); + } +}; +// Caches a propagated leg (electron PID) at its DCA to the vertex, for reuse in the ambiguity resolver and the mixed-event builder +struct LegAtDca { + std::array dca{}; + o2::track::TrackParCov trk; // leg (electron PID) propagated to its DCA to the vertex +}; + struct FitOptions { - bool nonSVertexer = false; // finder PCA cut for mixed legs, side-based TPC drift correction - bool relaxAP = false; // no Armenteros-Podolanski cut - bool purityGate = false; // finder: require purity score > finderMinScore - PairFit* probe = nullptr; // only return the dedup score, do not add a candidate + bool nonSVertexer = false; // finder PCA cut for mixed legs, side-based TPC drift correction + bool relaxAP = false; // no Armenteros-Podolanski cut + bool purityGate = false; // finder: require purity score > finderMinScore + float minPurity = -999.f; // purity gate threshold for this fit; below -998 the configured finderMinScore + PairFit* probe = nullptr; // only return the dedup score, do not add a candidate + const SourcePV* pv = nullptr; // run 2: fit against this vertex instead of the collision's (original pairings at their own source vertex) }; +// twin of PCMFreeLegs for the mixed instance of the builder +namespace o2::aod +{ +DECLARE_SOA_TABLE(PCMFreeLegsMix, "AOD", "PCMFREELEGMIX", //! + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreeleg::TrackId, v0leg::Sign, + v0legcov::X, v0legcov::Alpha, v0legcov::Y, v0legcov::Z, v0legcov::Snp, v0legcov::Tgl, v0legcov::Signed1Pt, v0legcov::Cov, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap); +DECLARE_SOA_TABLE(PCMFreeLegIdsMix, "AOD", "PCMFREELEGIDMIX", //! twin of PCMFreeLegIds for the mixed instance (never written) + o2::soa::Index<>, pcmfreeleg::CollisionId, pcmfreelegid::TrackIdDelta); +} // namespace o2::aod + // one tested pair of stored photons in the ambiguity resolver (also read by the MC diagnostics) struct SwapRecord { CandKey keyI{}, keyJ{}; float q = -1.f; bool complete = false; bool swapped = false; + float gain = -999.f; + int fail = 0; // bits: 1 stored I refit, 2 stored J refit, 4 crossed I prefilter, 8 crossed J prefilter, 16 crossed I fit, 32 crossed J fit + int line = 0; // rejection line in fitV0Candidate of the first crossed fit that failed +}; + +// MC truth of the legs of a genuine photon, and the SVertexer and finder results for this leg pair +struct TrueLegTrace { + int svLine = -1; // SVertexer V0 of these legs: -1 none, 0 accepted as candidate, > 0 source line of the rejection in fitV0Candidate + int finderStep = -9; // finder: -9 pair not tested (tgl window, leg not offered, finder off), -8 both legs with ITS, -7 pair already built, else evalFinderPair + int finderLine = -1; // finder fit: -1 not fitted, 0 accepted, > 0 source line of the rejection in fitV0Candidate }; struct DedupDiag { - std::map> blockersByKey; // rejected candidate -> candidates that took one of its legs - std::vector snapshot; // all candidates, before deduplication - std::vector stored; // surviving candidates + std::map> blockersByKey; // rejected candidate -> candidates that took one of its legs + std::set finderKeys; // candidates built by the run-1 finder (the other v0Id = -1 ones come from the swap resolver) + std::map, TrueLegTrace> trueLegTrace; // MC: leg pairs of genuine photons, SVertexer fit and finder result + std::vector snapshot; // all candidates, before deduplication + std::vector stored; // surviving candidates std::map> lossMargin; std::vector swaps; }; -template +template struct PhotonConversionBuilder { // using namespace o2; @@ -226,6 +301,10 @@ struct PhotonConversionBuilder { Produces v0legsXYZ; Produces v0legsDeDxMC; Produces v0photonsphivpsi; + Produces v0legsCov; + Produces v0photonskfchi2; // KF chi2 of the conversion-vertex fit and to the PV + Produces> pcmfreelegs; + Produces> pcmfreelegids; // linked derived data: free legs as track indices // Produces v0photonskfcov; // Produces events_ngpcm; @@ -241,6 +320,9 @@ struct PhotonConversionBuilder { Configurable modeTrackPropagation{"modeTrackPropagation", 0, "0: use real track propagation, including material, 1: use fast approximation using only geometry, 2: Use real track propagation and make comparison to fast propagation (only for debugging and testing)"}; Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; o2::framework::Configurable useSideBasedCorrection{"useSideBasedCorrection", false, "when moving TPC-only tracks, use TPC side flags (with legacy fallback) instead of tgl sign"}; + Configurable fillLegCov{"fillLegCov", false, "fill V0LegsCov (the table is written in any case, empty if false): the leg track parameters and covariance given to the V0 fit (TPC-only legs moved to the collision), one row per V0Legs row"}; + Configurable fillFreeLegIds{"fillFreeLegIds", false, "fill PCMFreeLegIds (the table is written in any case, empty if false): the free legs (as for fillFreeLegs) as AO2D (collision, track) indices only, for the mixer on linked derived data (the leg parameters are read from the parent AO2D); only selected collisions"}; + Configurable fillFreeLegs{"fillFreeLegs", false, "fill PCMFreeLegs (the table is written in any case, empty if false): tracks that pass the leg cuts (as the finder takes them) and are the leg of no stored photon, with the parameters the V0 fit would use (TPC-only legs moved to their own collision); pooled by the mixer with cfgPoolFreeLegs"}; // deduplication settings struct : ConfigurableGroup { @@ -256,6 +338,7 @@ struct PhotonConversionBuilder { Configurable resolveSplitLegs{"resolveSplitLegs", false, "after the dedup, remove photons that share a split track with a better photon - avoids duplicates for photon candidates"}; Configurable splitLegMaxDTgl{"splitLegMaxDTgl", 0.03f, "split legs: max |tgl1 - tgl2|"}; Configurable splitLegMaxChi2{"splitLegMaxChi2", 15.f, "split legs: max chi2 of two track pieces"}; + Configurable dedupHalfSwapRule{"dedupHalfSwapRule", false, "dedup mode 2, after the choice of a conflict group: a non-selected candidate whose two legs went to two different winners replaces both if it scores better than either and the two remaining legs of the winners form no candidate (two fakes on the legs of one photon cannot outweigh it through the score floor); same rule in run 2"}; Configurable cfgExtendedDiag{"cfgExtendedDiag", false, "MC: truth of the stored photons, pairs and swaps"}; } dedup; @@ -274,11 +357,32 @@ struct PhotonConversionBuilder { Configurable finderTimeMarginFwd{"finderTimeMarginFwd", 3000.f, "finder: extra late margin for TPC-only legs (ns)"}; Configurable finderMaxPCAMixed{"finderMaxPCAMixed", -1.f, "finder: max PCA for an ITS and a non-ITS leg (cm), < 0 builder cuts"}; Configurable finderFreeCollinearLegs{"finderFreeCollinearLegs", 0.f, "finder: two candidates of one collision with q_inv below this (GeV/c) count as collinear; their legs stay available to the finder (leg pairs already built are not refitted). Recovers true photons next to a close partner photon. 0 = off"}; + Configurable finderUniformFit{"finderUniformFit", false, "finder candidates (run 1) and finder-origin originals (run 2) are fitted exactly like SVertexer candidates: no side-based drift correction, AP cut applied, no purity gate (symmetric contest between finder-built and SVertexer-built photons)"}; + Configurable svertexerLegRule{"svertexerLegRule", false, "run 1: an SVertexer V0 candidate with a leg without ITS is fitted like a finder candidate (no AP cut, purity gate; the TPC drift correction of moved legs is useSideBasedCorrection), so that run 1 and run 2 (uniformPairFit) apply one leg-based rule to every pairing"}; + Configurable finderFreeAllLegs{"finderFreeAllLegs", false, "finder: no leg blocking at all, legs of accepted candidates stay available (leg pairs already built are not refitted), so the candidate set does not depend on the partner photons"}; Configurable runSwapResolver{"runSwapResolver", false, "ambiguity resolver: for close stored photon pairs, fit the crossed leg pairing (no AP cut) and keep the pairing with the lower dedup score"}; Configurable swapMaxQ{"swapMaxQ", 0.1f, "swap resolver: max q_inv of the two photons (GeV/c)"}; Configurable swapMaxConvDist{"swapMaxConvDist", 20.f, "swap resolver: max distance of the conversion points (cm)"}; } recovery; + // builder run 2 of the co-reconstructed mixed event (processMixed) + struct : ConfigurableGroup { + std::string prefix = "mixed_group"; + Configurable prefilterCrossed{"prefilterCrossed", true, "crossed leg pairs (legs of two different run-1 photons): fit only if |dtgl| < recovery finderMaxDeltaTgl and |circle gap| < finderMaxCircleGap (CPU; validation: off)"}; + Configurable skipSameSourcePairs{"skipSameSourcePairs", true, "mixed event: do not count pairs whose 4 legs all come from the same source event (with pooled conflict groups a group can give two photons of one partner event: a same-event pair inside the mixed event). Data-safe (mixer event counter), pair counting only"}; + Configurable freeLegRuleMask{"freeLegRuleMask", 5, "with freeLegRun1Rules: which run-1 rules a crossed pairing with a pooled free leg gets: bit 0 AP cut, bit 1 purity gate (pairings with a leg without ITS), bit 2 finder fit options (side-based drift correction, finder PCA; pairings with a leg without ITS); 7 = all run-1 rules, 0 = the lenient crossed fit"}; + Configurable freeLegMinPurity{"freeLegMinPurity", -999.f, "with freeLegRun1Rules and bit 1 of freeLegRuleMask: purity threshold for crossed pairings with a pooled free leg; below -998 recovery.finderMinScore (as run 1)"}; + Configurable mixedDiagnostics{"mixedDiagnostics", false, "MC diagnostics of run 2 (hCrossedFit, four-leg decisions, matched configurations, mixed pair classes, fake classes, hQSym, swap/brems pointing, alternative pairings): CPU on data, no effect on the stored photons"}; + Configurable diagFreeLegFits{"diagFreeLegFits", false, "diagnostic: every crossed pairing with a pooled free leg is fitted twice as a trial, with the lenient crossed options and with the run-1 rules, and Mixed/Decision/hFreeLegFit records both results (accepted or the rejection line), split by MC truth; the trial candidates are removed again, the real fit follows the configuration (CPU and QA side effect: V0 QA histograms count the trial fits)"}; + Configurable freeLegRun1Rules{"freeLegRun1Rules", true, "crossed pairings of a pooled free leg (sourceV0Id -2) with a stored leg are fitted as run 1 would have fitted them: with a leg without ITS as a finder candidate (side-based drift correction, AP cut, purity gate), with both legs with ITS as an SVertexer V0 (AP cut, no purity gate); stored x stored pairings keep crossedRelaxAP (overlay mixed event)"}; + Configurable freeFreeFinderOnly{"freeFreeFinderOnly", false, "with skipFreeFreeCrossed off: a crossed pairing of two pooled free legs is formed only if at least one leg has no ITS (the run-1 finder domain); free x free with both legs with ITS could only have been an SVertexer V0 in run 1 and is skipped (overlay mixed event)"}; + Configurable skipFreeFreeCrossed{"skipFreeFreeCrossed", true, "crossed pairings of two pooled free legs (sourceV0Id -2 on both) are not formed: run 1 rejected these legs (finder purity gate, AP cut), the run-2 crossed fit would accept them as fakes (overlay mixed event)"}; + Configurable skipSameSourceCrossed{"skipSameSourceCrossed", true, "crossed pairings of two legs from one source event are not formed (injection mixed event, mixer cfgInject: the current event's pairings were decided by run 1, only the injected legs form new candidates; the same-event artificial event is then exactly the run-1 output). Candidate formation and the hMatched configurations of the mixed event"}; + Configurable crossedRelaxAP{"crossedRelaxAP", true, "crossed leg pairs are fitted without the AP cut, like the originals without an SVertexer V0 (symmetric fit options, control)"}; + Configurable refitOriginalsAtSourceVertex{"refitOriginalsAtSourceVertex", true, "the original pairings are refitted against the primary vertex of their own source event (position and covariance carried in PCMMixLegs), so a mixed photon enters the decision and the output exactly as it left run 1; the crossed pairings use the vertex of the artificial event"}; + Configurable uniformPairFit{"uniformPairFit", false, "decide in run 2 only: every leg pairing of an artificial event (original or crossed, inside or across pooled conflict groups) passes the same prefilter and is fitted with the same options, chosen by the legs as the run-1 finder does (a pair with a leg without ITS gets the finder options: side-based drift correction, no AP cut, purity gate); for run 1 keep-all + mixer cfgPoolGroups, so that the same and the mixed event are decided by identical code on identical kinds of candidates"}; + } mixed; + // event-level skim for derived data struct : ConfigurableGroup { std::string prefix = "skim_group"; @@ -312,7 +416,7 @@ struct PhotonConversionBuilder { struct : ConfigurableGroup { std::string prefix = "v0cut_group"; Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; @@ -373,11 +477,23 @@ struct PhotonConversionBuilder { o2::aod::common::TPCVDriftManager mVDriftMgr; uint64_t mLastCCDBTimestamp{0}; std::set mRelaxedKeys; + std::unordered_map mLegDcaCache; // leg DCA to the vertex per DF (fitV0Candidate) + std::map> mGroupOfKey; + std::vector mGroupSizeOfCand; + struct PermuteResult { + int colID = -1; + int nMembers = 0; + bool changed = false; + bool greedy = false; + }; + std::vector mPermuteResults; // run 2, mode 2: one entry per conflict group with >= 2 candidates + int mNAnomalyDump = 0; o2::framework::HistogramRegistry registry{ "registry", { {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, + {"hNFreeLegs", "free legs filled per DF (fillFreeLegs);legs;DFs", {HistType::kTH1F, {{200, 0.f, 20000.f}}}}, {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, @@ -532,6 +648,7 @@ struct PhotonConversionBuilder { LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; } + registry.add("V0/hSVFitReject", "SVertexer V0 fits (run 1, dedup pass): source line of the rejecting cut, 0 = accepted (CPU order of the cuts);line;V0s", kTH1F, {{2400, -0.5f, 2399.5f}}); if (v0cuts.conversionPointMethod == 1) { registry.add("V0/hConvPointProfile", "conversionPointMethod 1;0 fallback to the touching point, 1 profile minimum used;candidates", kTH1F, {{2, -0.5f, 1.5f}}); registry.add("V0/hConvPointProfileShift", "conversionPointMethod 1;R_{profile} - R_{touching point} (cm);candidates", kTH1F, {{200, -10.f, 10.f}}); @@ -542,6 +659,9 @@ struct PhotonConversionBuilder { if (dedup.resolveSplitLegs) { registry.add("Dedup/hSplitLegRemoved", "split legs: photons removed per DF;photons;DFs", kTH1F, {{100, -0.5f, 99.5f}}); } + if (dedup.dedupHalfSwapRule) { + registry.add("Dedup/hHalfSwapRule", "half-swap rule: two winners replaced by the candidate on their legs;replacements per DF;DFs", kTH1F, {{100, -0.5f, 99.5f}}); + } if (recovery.runFinder) { registry.add("Finder/hSteps", "finder;step passed;pairs", kTH1D, {{FinderStep::kNFindStep, -0.5f, static_cast(FinderStep::kNFindStep) - 0.5f}}); registry.add("Finder/hScore", "finder;purity score;pairs", kTH1F, {{100, -20.f, 10.f}}); @@ -568,6 +688,17 @@ struct PhotonConversionBuilder { const AxisSpec axBlocker{4, -0.5f, 3.5f, "0 = true, 1 = cross-leg fake, 2 = other fake, 3 = no blocker (cut, not dedup)"}; registry.add("MCDedup/Photons/hFate", "fate of the true photons;0 = alive, 1 = lost (blocked), 2 = lost (no blocker);photons", kTH1F, {{3, -0.5f, 2.5f}}, true); + registry.add("MCDedup/Photons/hFateProps", "true photons with a candidate;0 alive, 1 lost to a cross-leg fake, 2 lost to another candidate, 3 lost without blocker;p_{T} (GeV/c);R_{xy} (cm);legs with ITS;pointing angle (rad);PCA (cm);other candidates sharing a leg", kTHnSparseF, {{4, -0.5f, 3.5f}, {20, 0.f, 2.f}, {18, 0.f, 90.f}, {3, -0.5f, 2.5f}, {16, 0.f, 0.08f}, {15, 0.f, 3.f}, {11, -0.5f, 10.5f}}); + registry.add("MCDedup/FourLeg/hRun1", "run 1: two true photon candidates whose crossed pairings are candidates too (MC);q_{inv} of the true photons (GeV/c);outcome: 0 both true stored, 1 both crossed stored (swap), 2 one true stored, 3 one crossed stored, 4 none, 5 other;isolated 4-candidate decision: 0 true, 1 crossed, 2 other;other candidates using one of the 4 legs;photons related (same mother, or mother e^{#pm});one true candidate is a resolver/finder photon (no SVertexer V0)", kTHnSparseF, {{10, 0.f, 0.05f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {6, -0.5f, 5.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/HalfSwap/hSource", "run 1, true photons with a candidate (MC);0 stored, 1 both legs in two other stored candidates (half-swap source), 2 one leg used, 3 no leg used, 4 both legs in one other stored candidate;score S of the best true candidate;objective: c(S_{w1}) + c(S_{w2}) - c(S_{true}), c = max(s_{0} - S, 0.001);(S_{w1} + S_{w2})/2 - S_{true};p_{T} of the true candidate (GeV/c)", kTHnSparseF, {{5, -0.5f, 4.5f}, {40, 0.f, 2.f}, {40, -0.5f, 0.5f}, {40, -1.f, 1.f}, {10, 0.f, 2.f}}); + registry.add("MCDedup/FourLeg/hRun1Origin", "run 1: as hRun1, with the origin of the candidates (MC);q_{inv} of the true photons (GeV/c);outcome: 0 both true stored, 1 swap, 2 one true stored, 3 one crossed stored, 4 none, 5 other;crossed candidates: 0 both SVertexer V0s, 1 one built by the swap resolver, 2 both built by the resolver;true candidates: 0 both SVertexer V0s, 1 one non-SVertexer, 2 both non-SVertexer;other candidates on the 4 legs (0, 1+);photons related", kTHnSparseF, {{10, 0.f, 0.05f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/Inventory/hPhoton", "eligible true photons (MC);#it{p} (GeV/c);R_{conv} (cm);legs with ITS;fate: 0 no true candidate, 1 candidate lost, 2 stored, 3 lost but a leg in a stored crossed candidate;q_{true} to the closest other eligible photon (GeV/c)", kTHnSparseF, {{20, 0.f, 2.f}, {18, 0.f, 90.f}, {3, -0.5f, 2.5f}, {4, -0.5f, 3.5f}, {20, 0.f, 0.1f}}); + registry.add("MCDedup/Inventory/hPair", "eligible pairs of true photons (MC);q_{true} (GeV/c);|E_{1} - E_{2}| / (E_{1} + E_{2});k_{T} (GeV/c);fate: 0 both stored, 1 one stored, 2 none stored and no crossed stored, 3 both crossed stored (swap), 4 one crossed stored, 5 other;true candidates of the pair (0, 1, 2);crossed candidates of the pair (0, 1, 2)", kTHnSparseF, {{20, 0.f, 0.1f}, {10, 0.f, 1.f}, {6, 0.f, 1.5f}, {6, -0.5f, 5.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}); + registry.add("MCDedup/Inventory/hSwapOrigin", "eligible pairs of true photons with a stored crossed candidate (MC);q_{true} (GeV/c);fate: 0 both crossed stored (swap), 1 one crossed stored;true candidates of the pair (0, 1, 2);stored crossed: 0 SV+SV, 1 SV+finder, 2 finder+finder, 3 SV+resolver, 4 finder+resolver, 5 resolver+resolver;history of photon 1 (the higher code);history of photon 2", kTHnSparseF, {{20, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}, {6, -0.5f, 5.5f}, {7, -0.5f, 6.5f}, {7, -0.5f, 6.5f}}); + registry.add("MCDedup/Inventory/hNeverDetail", "true photons without a true candidate in eligible pairs with q_{true} < 20 MeV (MC, entries = pairs);pair fate (as hPair);history (as hSwapOrigin);detail: SVertexer or finder fit rejection line (history 1, 5), finder step (history 2-4: -8 both ITS, -7 built, -9 not tested, else evalFinderPair)", kTH3F, {{6, -0.5f, 5.5f}, {7, -0.5f, 6.5f}, {2010, -10.5f, 1999.5f}}); + registry.add("MCDedup/Inventory/hSwapLoss", "same-event full swaps of two true photons (MC);q_{true} (GeV/c);true candidates of the pair (0, 1, 2);resolver: 0 crossed in different collisions, 1 not tested q_{rec} > swapMaxQ, 2 not tested conv. distance > swapMaxConvDist, 3 true pairing fails the prefilter, 4 true pairing rejected by a fit cut, 5 stored refit failed, 6 complete, crossed score better, 7 complete, gain > 0 but leg taken by another swap, 8 other;gain = S_{crossed} - S_{true} (resolver sign: S_{stored} - S_{alternative})", kTHnSparseF, {{20, 0.f, 0.1f}, {3, -0.5f, 2.5f}, {9, -0.5f, 8.5f}, {40, -2.f, 2.f}}); + registry.add("MCDedup/Inventory/hSwapLossCut", "same-event full swaps whose true pairing the resolver fit rejects (MC, q_{true} < 20 MeV);true candidates of the pair (0, 1, 2);rejection line in fitV0Candidate", kTH2F, {{3, -0.5f, 2.5f}, {2000, -0.5f, 1999.5f}}); + registry.add("MCDedup/Swap/hResolverTruth", "swap resolver, every tested pair of close stored photons (MC);true photons among the 2 stored;true photons among the 2 alternatives;outcome: 0 incomplete (prefilter), 1 incomplete (fit cut), 2 incomplete (stored refit), 3 complete kept, 4 swapped;gain S_{stored} - S_{alternative};q_{inv} of the stored pair (GeV/c)", kTHnSparseF, {{3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {5, -0.5f, 4.5f}, {40, -2.f, 2.f}, {20, 0.f, 0.1f}}); registry.add("MCDedup/Photons/hBlockerClass", "blockers of a killed true photon (ENTRIES = blockers, not photons);class of the blocker;blocker entries", kTH1F, {axBlocker}, true); registry.add("MCDedup/Photons/hKillMargin", "mode 0/1 only;S_{victim} - S_{winner};kills", kTH1F, {{200, 0.f, 1.f}}, true); registry.add("MCDedup/Photons/hVictimVsWinnerPCA", "PCA of the two;PCA_{victim} (cm);PCA_{winner} (cm)", kTH2F, {{60, 0.f, 3.f}, {60, 0.f, 3.f}}, true); @@ -587,6 +718,7 @@ struct PhotonConversionBuilder { const AxisSpec axQrec{50, 0.f, 0.1f, "q_{inv} (GeV/c)"}; registry.add("MCDedup/Stored/hClassSource", "stored photons;class;0 SVertexer, 1 finder or swap;p_{T,#gamma} (GeV/c)", kTH3F, {axClass, {2, -0.5f, 1.5f}, axPt}); registry.add("MCDedup/Stored/hPairClass", "stored photon pairs, same collision;0 true-true, 1 crossed doublet, 2 other;q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); + registry.add("MCDedup/Stored/hSwapTrueCands", "stored crossed doublets (full swaps);true pairings among the dedup candidates (0, 1, 2);q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); registry.add("MCDedup/Copies/hChoice", "true photons with a candidate in the dedup sample;collisions with a true candidate;0 = right coll., 1 = wrong (right available), 2 = wrong (no right copy), 3 = lost", kTH2F, {{5, 0.5f, 5.5f}, {4, -0.5f, 3.5f}}); registry.add("MCDedup/Copies/hRule", "true photons, >= 2 copies, right copy available;0 = current dedup choice, 1 = smallest |#Deltaz| of the photon line, 2 = largest xy cos(PA) (master), 3 = largest 3D cos(PA);0 = wrong, 1 = right", kTH2F, {{4, -0.5f, 3.5f}, {2, -0.5f, 1.5f}}); registry.add("MCDedup/Copies/hPAxy", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;transverse pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); @@ -595,15 +727,93 @@ struct PhotonConversionBuilder { } } + if (doprocessMixed && doprocessMixedDerived) { + LOG(fatal) << "enable processMixed (AO2D input) or processMixedDerived (derived data), not both"; + } + if (doprocessMixed || doprocessMixedDerived) { + if (doprocessRec || doprocessMC || doprocessRec_OnlyIfDielectron) { + LOG(fatal) << "processMixed (builder run 2) has to run in its own instance (photon-conversion-builder-mixed) without processRec / processMC"; + } + addMixedHistograms(); + } + mVDriftMgr.setUseSideBasedCorrection(useSideBasedCorrection.value); } + void addMixedHistograms() + { + const AxisSpec axType{2, -0.5f, 1.5f, "artificial event (0 same, 1 mixed)"}; + registry.add("Mixed/hEvents", "artificial events", kTH1F, {axType}); + registry.add("Mixed/hNPhotonsIn", "run-1 photons given to run 2;artificial event;photons per event", kTH2F, {axType, {201, -0.5f, 200.5f}}); + registry.add("Mixed/hCandidates", "candidate cut flow;artificial event;0 original pairs, 1 original refit ok, 2 crossed pairs, 3 crossed after pre-cut, 4 crossed fit ok", kTH2F, {axType, {5, -0.5f, 4.5f}}); + registry.add("Mixed/hStored", "stored photons;artificial event;0 original pairing, 1 crossed pairing", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hNStoredVsIn", "photons per artificial event;artificial event;photons in (run 1);photons out (run 2)", kTH3F, {axType, {101, -0.5f, 100.5f}, {101, -0.5f, 100.5f}}); + registry.add("Mixed/hNOriginalLost", "run-1 photons not stored again;artificial event;lost originals per event", kTH2F, {axType, {21, -0.5f, 20.5f}}); + registry.add("Mixed/hNCrossedStored", "crossed photons stored;artificial event;crossed photons per event", kTH2F, {axType, {21, -0.5f, 20.5f}}); + registry.add("Mixed/hOrigRelDPt", "stored original pairing vs run 1;artificial event;p_{T}^{run 2} / p_{T}^{run 1} - 1", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigDPhi", "stored original pairing vs run 1;artificial event;#varphi^{run 2} - #varphi^{run 1} (rad)", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigDEta", "stored original pairing vs run 1;artificial event;#eta^{run 2} - #eta^{run 1}", kTH2F, {axType, {400, -0.02f, 0.02f}}); + registry.add("Mixed/hOrigExact", "stored original pairing vs run 1;artificial event;0 differs, 1 identical p (float)", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hCrossedSourceQ", "stored crossed photons: the two run-1 photons of its legs;artificial event;q_{inv} (GeV/c)", kTH2F, {axType, {200, 0.f, 0.2f}}); + registry.add("Mixed/hCrossedSourceDist", "stored crossed photons: the two run-1 photons of its legs;artificial event;distance of the conversion points (cm)", kTH2F, {axType, {200, 0.f, 40.f}}); + registry.add("Mixed/hCrossedSourceEvents", "stored crossed photons;artificial event;legs from 0 the same event, 1 two events", kTH2F, {axType, {2, -0.5f, 1.5f}}); + registry.add("Mixed/hStoredTruth", "MC, stored photons;artificial event;0 original true, 1 original fake, 2 crossed true, 3 crossed fake, 4 no label", kTH2F, {axType, {5, -0.5f, 4.5f}}); + registry.add("Mixed/hTimeCandidates", "CPU of the candidate fits;artificial event;time per event (ms)", kTH2F, {axType, {1000, 0.f, 1000.f}}); + registry.add("Mixed/hTimeDF", "CPU of run 2 per DF;time (ms)", kTH1F, {{1000, 0.f, 100000.f}}); + const AxisSpec axPairTruth{5, -0.5f, 4.5f, "pair (0 all, 1 two true photons, 2 full swap of two true photons, 3 other with a fake, 4 no label)"}; + const AxisSpec axQ{500, 0.f, 0.5f, "q_{inv} (GeV/c)"}; + registry.add("Mixed/Pairs/hQBefore", "photon pairs before run 2 (run-1 photons);artificial event", kTH3F, {axType, axPairTruth, axQ}); + registry.add("Mixed/Pairs/hQAfter", "photon pairs after run 2;artificial event", kTH3F, {axType, axPairTruth, axQ}); + const AxisSpec axAlt{9, -0.5f, 8.5f, "3 #times alternatives passing the fit + (0 kept, 1 changed, 2 other)"}; + const AxisSpec axQLow{100, 0.f, 0.1f, "q_{inv} of the input pair (GeV/c)"}; + registry.add("Mixed/Pairs/hAltTrueTrue", "input pairs of two true photons;artificial event", kTH3F, {axType, axAlt, axQLow}); + registry.add("Mixed/Pairs/hFakeClass", "photon pairs by MC class (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);legs with ITS;crossed pairing passed the run-2 fit", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {200, 0.f, 0.2f}, {5, -0.5f, 4.5f}, {2, -0.5f, 1.5f}}); + const AxisSpec axDR{std::vector{0., 0.5, 1., 2., 3., 5., 10., 20., 50., 200.}, "|R_{A} - R_{B}| (cm)"}; + registry.add("Mixed/Pairs/hFakeClassOrder", "photon pairs by MC class and conversion order (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);0 softer photon at the smaller radius (kept), 1 softer photon at the larger radius;|R_{A} - R_{B}| (cm)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {40, 0.f, 0.2f}, {2, -0.5f, 1.5f}, axDR}); + registry.add("Mixed/Pairs/hAltSwap", "input pairs = full swap of two true photons;artificial event", kTH3F, {axType, axAlt, axQLow}); + registry.add("Mixed/Pairs/hFakeClassGeo", "photon pairs by MC class and geometry (MC);artificial event;0 before / 1 after run 2;class (see mixFakeClass);q_{inv} (GeV/c);crossed pairing passed the run-2 fit;same physical-primary ancestor;conversion-point distance (cm);log_{10} closest same-sign legs #sqrt{#Delta#varphi^{2}+#Deltatan#lambda^{2}};max TPC shared-cluster fraction", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {50, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}, {40, 0.f, 40.f}, {30, -5.f, 1.f}, {10, 0.f, 1.f}}); + registry.add("Mixed/Pairs/hGroupCtx", "photon pairs after run 2, conflict group of the run-2 dedup (MC);artificial event;class (see mixFakeClass);q_{inv} (GeV/c);crossed pairing passed the run-2 fit;same conflict group;candidates in the group;legs in the group;photons stored from the group", kTHnSparseF, {axType, {18, -0.5f, 17.5f}, {20, 0.f, 0.1f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}, {20, 0.5f, 20.5f}, {15, 3.5f, 33.5f}, {10, 0.5f, 10.5f}}); + registry.add("Mixed/Decision/hFourLeg", "pairs of run-1 photons whose two crossed pairings also passed the fit (MC), selected before the decision;artificial event;MC: 0 original assignment = two conversions, 1 crossed assignment = two conversions, 2 neither;isolated 4-candidate decision: 0 original, 1 crossed, 2 other;actual decision: 0 original stored, 1 crossed stored, 2 other;q_{inv} original assignment (GeV/c);q_{inv} crossed assignment (GeV/c);candidates in the conflict group;S_{crossed} - S_{original};an original was fitted without the AP cut", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {50, 0.f, 0.1f}, {50, 0.f, 0.1f}, {12, 3.5f, 15.5f}, {80, -1.f, 1.f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Decision/hFourLegKin", "4-leg configurations with min(q_{orig}, q_{crossed}) < 50 MeV (MC);artificial event;MC: 0 original = two conversions, 1 crossed = two conversions, 2 neither;isolated decision: 0 original, 1 crossed, 2 other;#Sigma PCA_{crossed} - #Sigma PCA_{original} (cm);#Sigma #theta_{PA, crossed} - #Sigma #theta_{PA, original} (rad);|#DeltaR_{xy}| of the conversions (cm);|#Deltaz| of the conversions (cm);momentum asymmetry of the more similar same-sign legs;photons related by MC history (brems, siblings)", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {40, -2.f, 2.f}, {40, -0.1f, 0.1f}, {20, 0.f, 20.f}, {20, 0.f, 20.f}, {10, 0.f, 1.f}, {2, -0.5f, 1.5f}}); + { + const AxisSpec axTruth2{2, -0.5f, 1.5f, "MC: 0 original = two conversions, 1 crossed = two conversions"}; + const AxisSpec axChoice{3, -0.5f, 2.5f, "isolated decision: 0 genuine assignment, 1 wrong assignment, 2 other"}; + const AxisSpec axRel2{2, -0.5f, 1.5f, "photons related by MC history"}; + registry.add("Mixed/Decision/hFourLegTruth", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC);artificial event", kTHnSparseF, {axType, axTruth2, axChoice, {4, -0.5f, 3.5f, "MC relation: 0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg"}, {10, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {9, -0.5f, 8.5f, "3 #times (genuine photons with S #geq s_{0}) + wrong candidates with S #geq s_{0}"}, {24, -60.f, 60.f, "#Sigma#theta_{PA} wrong - genuine (mrad)"}, {20, -1.f, 1.f, "#Sigma PCA wrong - genuine (cm)"}, {std::vector{0., 0.005, 0.05, 0.1, 0.2, 0.4, 1.}, "max TPC shared-cluster fraction of the 4 legs"}}); + registry.add("Mixed/Decision/hFourLegTrack", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC);artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {15, -2.f, 1.5f, "log_{10} min distance in the TPC of the legs the wrong assignment pairs (cm)"}, {15, -2.f, 1.5f, "log_{10} min distance in the TPC of the same-sign legs (cm)"}, {5, -0.5f, 4.5f, "legs with ITS"}, {std::vector{-10., 722., 2228., 1.e6}, "occupancy of the artificial event (terciles)"}}); + registry.add("Mixed/Decision/hFourLegResid", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), residuals signed toward the partner;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {20, -0.01f, 0.01f, "#varphi residual photon 1 (rad)"}, {20, -0.01f, 0.01f, "#varphi residual photon 2 (rad)"}, {20, -0.01f, 0.01f, "#eta residual photon 1"}, {20, -0.01f, 0.01f, "#eta residual photon 2"}}); + registry.add("Mixed/Decision/hFourLegAlt", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), isolated decision with other scores;artificial event", kTHnSparseF, {axType, axTruth2, axRel2, {3, -0.5f, 2.5f, "baseline: 0 genuine, 1 wrong, 2 other"}, {3, -0.5f, 2.5f, "pointing only (w = 1)"}, {3, -0.5f, 2.5f, "PCA only (w = 0)"}, {3, -0.5f, 2.5f, "baseline score, raw-sum objective"}, {3, -0.5f, 2.5f, "baseline weight without gap term"}}); + registry.add("Mixed/Decision/hFourLegOrigin", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), common origin of the two genuine photons;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, {4, -0.5f, 3.5f, "MC relation: 0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg"}, {10, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {5, -1.5f, 3.5f, "common origin: -1 n/a, 0 none, 1 same chain root, 2 same great-grandmother, 3 same grandmother"}, {8, -0.5f, 7.5f, "PDG of the chain root: 0 other, 1 pi0, 2 eta, 3 omega, 4 eta', 5 rho/K*, 6 K0, 7 baryon"}}); + registry.add("Mixed/Decision/hFourLegPoint", "4-leg configurations with a genuine assignment, min(q) < 50 MeV (MC), pointing to the PV split into transverse (#varphi) and longitudinal (#lambda) parts;artificial event", kTHnSparseF, {axType, axTruth2, axChoice, axRel2, {5, 0.f, 0.05f, "q_{inv} of the genuine assignment (GeV/c)"}, {24, -30.f, 30.f, "#Sigma|#Delta#varphi| wrong - genuine (mrad)"}, {24, -30.f, 30.f, "#Sigma|#Delta#lambda| wrong - genuine (mrad)"}, {20, -20.f, 20.f, "#Delta#lambda genuine photon 1 (mrad)"}, {20, -20.f, 20.f, "#Delta#lambda genuine photon 2 (mrad)"}, {20, -20.f, 20.f, "#Delta#varphi genuine photon 1 (mrad)"}, {20, -20.f, 20.f, "#Delta#varphi genuine photon 2 (mrad)"}}); + } + registry.add("Mixed/Decision/hFreeLegFit", "crossed pairings with a pooled free leg (run 2, mixed_group.diagFreeLegFits): trial fits with both option sets (MC);artificial event;truth: 0 legs of two genuine photons with q_{true} < 50 MeV (swap ingredient), 1 other;lenient crossed fit: 0 accepted, else rejection line;run-1 rules: 0 accepted, else rejection line;legs with ITS;free legs in the pairing (1, 2)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {2000, -0.5f, 1999.5f}, {2000, -0.5f, 1999.5f}, {3, -0.5f, 2.5f}, {2, 0.5f, 2.5f}}); + registry.add("Mixed/Decision/hCrossedFit", "crossed leg pairings in run 2: result of the fit (MC);artificial event;two genuine photons, q_{true} < 50 MeV;0 accepted, 1 prefilter (only pairs inside the tgl window are visited), else source line of the rejection in fitV0Candidate;legs with ITS;q_{true} of the two photons (GeV/c)", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {1600, -0.5f, 1599.5f}, {3, -0.5f, 2.5f}, {5, 0.f, 0.05f}}); + registry.add("Mixed/Decision/hMatched", "two genuine MC photons whose 4 legs are all in the artificial event (same definition for SE and ME, q from the MC photons);artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped (both crossed), 2 other;genuine pairings fitted in run 2 (0-2);crossed pairings fitted in run 2 (0-2);run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related (same mother, or one radiated by a leg of the other);PV contributors of the event (charged multiplicity);photons in the artificial event;occupancy of the event;occupancy of the source event - current event (signed, the photon with the larger difference; 0 for SE);TPC-only legs among the 4", kTHnSparseF, {axType, {10, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {std::vector{0., 50., 100., 200., 400., 800., 1200., 1600., 2000., 2500., 3000., 5000.}, "PV contributors"}, {12, 0.f, 60.f, "photons in the artificial event"}, {std::vector{-10., 722., 2228., 1.e6}, "occupancy"}, {std::vector{-20000., -3000., -2000., -1000., -500., -250., 250., 500., 1000., 2000., 3000., 20000.}, "occupancy partner - current"}, {5, -0.5f, 4.5f, "TPC-only legs"}}); + registry.add("Mixed/Decision/hMatchedCE", "two genuine MC photons whose 4 legs are all in the artificial event, central-electrode variables;artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped, 2 other;run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related;min |#eta| of the two MC photons;min |z| of the two true conversion points (cm);legs crossing the central electrode in the TPC (of 4);TPC-only legs", kTHnSparseF, {axType, {5, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {std::vector{0., 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.6, 0.9, 2.}, "min |#eta|"}, {std::vector{0., 1., 2., 5., 10., 20., 50., 300.}, "min |z_{conv}| (cm)"}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}}); + registry.add("Mixed/Decision/hMatchedLegs", "two genuine MC photons whose 4 legs are all in the artificial event, partner of each leg in the stored photons;artificial event;q_{true} (GeV/c);run-1 input pairing: 0 both genuine photons, 1 swapped, 2 other;run-2 outcome: 0 both genuine stored, 1 both crossed stored, 2 other;photons related;legs not stored;legs stored with their own genuine partner;legs stored with the crossed partner (other photon of the pair);legs stored with a leg of a third genuine photon (both legs in the event);legs stored with a leg of an incomplete MC photon (arrived inside a stored fake);legs stored with a leg without photon mother", kTHnSparseF, {axType, {5, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}}); + registry.add("Mixed/Decision/hFourLegAll", "pairs of run-1 photons whose two crossed pairings are candidates INCLUDING those appended by the run-2 resolvers (MC);artificial event;MC: 0 original = two conversions, 1 crossed = two conversions, 2 neither;actual decision: 0 original stored, 1 crossed stored, 2 other;q_{inv} of the genuine (truth 0: original, 1: crossed) assignment (GeV/c);crossed candidates: 0 both from the run-2 fit, 1 one appended by a resolver, 2 both appended;original photons: 0 both SVertexer V0s in run 1, 1 one not, 2 both not", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {10, 0.f, 0.05f}, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}); + registry.add("Mixed/Decision/hPermute", "conflict groups of the run-2 dedup with >= 2 candidates;artificial event;candidates;reversed order selects other candidates;greedy (no enumeration)", kTHnSparseF, {axType, {30, 0.5f, 30.5f}, {2, -0.5f, 1.5f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Pairs/hPairOrigin", "photon pairs after run 2 by MC class and common origin of the two MC photons (MC);artificial event;MC class (see mixFakeClass);q_{inv} (GeV/c);common origin: -1 same photon / no label, 0 none, 1 same chain root, 2 same great-grandmother, 3 same grandmother", kTHnSparseF, {axType, {18, -0.5f, 17.5f}, {50, 0.f, 0.1f}, {5, -1.5f, 3.5f}}); + registry.add("Mixed/Pairs/hSameSource", "mixed event after run 2: pairs whose 4 legs all come from one source event (same-event pairs inside the mixed event, from pooled groups);q_{inv} (GeV/c);MC class (see mixFakeClass)", kTH2F, {{200, 0.f, 0.2f}, {18, -0.5f, 17.5f}}); + registry.add("Mixed/Pairs/hQSym", "photon pairs after run 2, swap-symmetric counting: a stored pair whose two crossed pairings (same 4 legs) also passed the run-2 fit counts 1/2 at its q and 1/2 at q of the crossed pairing;artificial event;MC class of the stored pair (see mixFakeClass);q_{inv} (GeV/c)", kTH3F, {axType, {18, -0.5f, 17.5f}, {500, 0.f, 0.5f}}); + registry.add("Mixed/Pairs/hQClean", "photon pairs, all legs TPC shared fraction < 0.4;artificial event;0 before / 1 after run 2;q_{inv} (GeV/c)", kTH3F, {axType, {2, -0.5f, 1.5f}, {500, 0.f, 0.5f}}); + registry.add("Mixed/Pairs/hBrems", "photon pairs, bremsstrahlung pointing;artificial event;0 before / 1 after run 2;class (see mixFakeClass);MC relation (0 none, 1 radiated by a leg, 2 siblings, 3 leg daughter of a leg);q_{inv} (GeV/c);log_{10} d_{min} (cm);log_{10} angle to the leg (rad);radius of the closest point (cm);B converts further out;p_{T} of B (GeV/c);all legs TPC shared fraction < 0.4", kTHnSparseF, {axType, {2, -0.5f, 1.5f}, {18, -0.5f, 17.5f}, {4, -0.5f, 3.5f}, {50, 0.f, 0.1f}, {40, -3.f, 2.f}, {40, -4.f, 0.5f}, {45, 0.f, 90.f}, {2, -0.5f, 1.5f}, {20, 0.f, 1.f}, {2, -0.5f, 1.5f}}); + registry.add("Mixed/Pairs/hSwapDecision", "full swaps after run 2 (classes 14 and 17);artificial event;0 created in run 2, 1 carried from run 1, 2 one of each;0 true pairings scored better, 1 swap scored better, 2 a true pairing failed the fit;q_{inv} (GeV/c);all legs TPC shared fraction < 0.4", kTHnSparseF, {axType, {3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}, {50, 0.f, 0.1f}, {2, -0.5f, 1.5f}}); + } + void initCCDB(MyBCs::iterator const& bc) { - if (mRunNumber == bc.runNumber()) { + initCCDBFor(bc.runNumber(), bc.timestamp(), bc.grpMagField()); + } + + // run number, timestamp and GRPMagField of a BC (AO2D) or of a PMEvents row with EmMagFields (derived data, builder run 2) + template + void initCCDBFor(const int runNumber, const uint64_t timestamp, TGrpMagField const& grpMagField) + { + if (mRunNumber == runNumber) { return; } - mRunNumber = bc.runNumber(); + mRunNumber = runNumber; // In case override, don't proceed, please - no CCDB access required if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; @@ -616,10 +826,11 @@ struct PhotonConversionBuilder { return; } - auto run3grp_timestamp = bc.timestamp(); - o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + auto run3grp_timestamp = timestamp; + o2::parameters::GRPMagField grp = grpMagField; // copy: getNominalL3Field is not const + o2::base::Propagator::initFieldFromGRP(&grp); // Fetch magnetic field from ccdb for current collision - d_bz = bc.grpMagField().getNominalL3Field(); + d_bz = grp.getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; if (useMatCorrType == 2) { // o2-linter: disable=magic-number (material budget correction) @@ -635,8 +846,11 @@ struct PhotonConversionBuilder { void updateCCDB(MyBCs::iterator const& bc) { - auto timestamp = bc.timestamp(); + updateCCDBAt(bc.timestamp()); + } + void updateCCDBAt(const uint64_t timestamp) + { mVDriftMgr.update(timestamp); mLastCCDBTimestamp = timestamp; } @@ -762,13 +976,76 @@ struct PhotonConversionBuilder { } } + void fillLegCovTable(o2::track::TrackParCov const& trackParCov) + { + const auto cov = trackParCov.getCov(); + v0legsCov(trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), + trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt(), cov.data()); + } + + // free legs (fillFreeLegs): after the tables are written; a track that passes the leg cuts, has a collision and the TPC (as the + // finder takes them) and is the leg of no stored photon; TPC-only legs moved to their own collision as in the fit + template + void writeFreeLegs(TCollisions const& collisions, TTracks const& tracks, std::set const& skimRejected) + { + std::set usedLegs; + for (const auto& key : stored_fullv0Ids) { + usedLegs.insert(key.posId); + usedLegs.insert(key.eleId); + } + int nFree = 0; + int64_t lastFreeLegTrack = 0; // PCMFreeLegIds: track index stored as the difference to the previous row of this DF + for (const auto& track : tracks) { + if (track.collisionId() < 0 || !track.hasTPC() || usedLegs.contains(track.globalIndex()) || skimRejected.contains(track.collisionId()) || + track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + const auto collision = collisions.rawIteratorAt(track.collisionId()); + if (!collision.isSelected()) { // the mixer only takes selected events (PMEvents): free legs of other collisions are never used + continue; + } + auto par = getTrackParCov(track); + if constexpr (!std::is_void_v) { + if (trackcuts.moveTPCTracks && o2::pwgem::photonmeson::isTPConlyTrack(track) && !mVDriftMgr.moveTPCTrack(collision, track, par)) { + continue; + } + } + if (fillFreeLegIds) { // tracks are visited in increasing index order: store the difference to the previous free leg of this DF + pcmfreelegids(track.collisionId(), static_cast(track.globalIndex() - lastFreeLegTrack)); + lastFreeLegTrack = track.globalIndex(); + } + if (!fillFreeLegs) { + ++nFree; + continue; + } + const auto cov = par.getCov(); + pcmfreelegs(track.collisionId(), track.globalIndex(), track.sign(), + par.getX(), par.getAlpha(), par.getY(), par.getZ(), par.getSnp(), par.getTgl(), par.getQ2Pt(), cov.data(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); + ++nFree; + } + registry.fill(HIST("hNFreeLegs"), static_cast(nFree)); + } + template void fillV0Table(TV0 const& v0, const bool filltable) { const auto& pos = v0.template posTrack_as(); const auto& ele = v0.template negTrack_as(); - const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. - fitV0Candidate(collision, pos, ele, v0.globalIndex(), filltable); + const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. + const bool finderLike = recovery.svertexerLegRule.value && !(pos.hasITS() && ele.hasITS()); // one leg-based rule for every pairing + fitV0Candidate(collision, pos, ele, v0.globalIndex(), filltable, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = finderLike && recovery.runFinder.value}); + if (!filltable) { + registry.fill(HIST("V0/hSVFitReject"), static_cast(mLastFitReject)); + } + if constexpr (isMC) { + if (auto* t = filltable ? nullptr : trueLegTrace(pos.globalIndex(), ele.globalIndex()); t != nullptr) { + t->svLine = t->svLine == 0 ? 0 : mLastFitReject; // accepted in any collision wins + } + } } // the purity score (finder LLR tables) is only needed for the finder gate and the split-leg order @@ -792,35 +1069,42 @@ struct PhotonConversionBuilder { return score; } - // fit of one leg pair with the V0 cuts; filltable = false adds a dedup candidate, - // true writes the tables; opt.probe only returns the score + // fit of one leg pair with the V0 cuts template void fitV0Candidate(TCollision const& collision, TTrack const& pos, TTrack const& ele, const int64_t v0Id, const bool filltable, FitOptions const& opt = {}) { using namespace o2::pwgem::photonmeson; mVDriftMgr.setUseSideBasedCorrection(opt.nonSVertexer || useSideBasedCorrection.value); + mLastFitReject = 0; + const float pvX = opt.pv ? opt.pv->posX() : collision.posX(), pvY = opt.pv ? opt.pv->posY() : collision.posY(), pvZ = opt.pv ? opt.pv->posZ() : collision.posZ(); if (pos.sign() * ele.sign() > 0) { // reject same sign pair + mLastFitReject = __LINE__; // rejection point return; } if (pos.pt() < trackcuts.min_pt_trackiu || ele.pt() < trackcuts.min_pt_trackiu) { + mLastFitReject = __LINE__; // rejection point return; } if (pos.globalIndex() == ele.globalIndex()) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(pos) && !ele.hasITS()) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && !pos.hasITS()) { + mLastFitReject = __LINE__; // rejection point return; } if (!checkV0leg(pos) || !checkV0leg(ele)) { + mLastFitReject = __LINE__; // rejection point return; } @@ -835,25 +1119,44 @@ struct PhotonConversionBuilder { std::array dcaInfo{}; auto pTrack = getTrackParCov(pos); - if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { + bool failedMovePos = false; + if constexpr (!std::is_void_v) { // TBCs = void: legs of builder run 2, already moved in run 1 + failedMovePos = trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack); + } + if (failedMovePos) { if (opt.probe == nullptr) { LOGP(error, "failed correction for positive tpc track"); } + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } + const bool sideBased = opt.nonSVertexer || useSideBasedCorrection.value; + const bool cacheDca = modeTrackPropagation == TrackPropMode::kProper; + std::optional posAtDca, eleAtDca; // legs at their DCA to the vertex (reused for phi_V / psi_pair) + const LegDcaKey posKey{pos.globalIndex(), collision.globalIndex(), pvX, pvY, pvZ, sideBased}; + const LegAtDca* posCached = nullptr; + if (cacheDca) { + if (const auto it = mLegDcaCache.find(posKey); it != mLegDcaCache.end()) { + posCached = &it->second; + } + } auto pTrackC = pTrack; - o2::math_utils::Point3D vtxPrim{ - collision.posX(), - collision.posY(), - collision.posZ()}; + o2::math_utils::Point3D vtxPrim{pvX, pvY, pvZ}; if (modeTrackPropagation != TrackPropMode::kProper) { dcaInfo = CalculateDCAFast(pTrackC, vtxPrim, d_bz); } pTrackC.setPID(o2::track::PID::Electron); std::array dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); + if (posCached != nullptr) { + dcaInfo = posCached->dca; + posAtDca = posCached->trk; + } else if (modeTrackPropagation != TrackPropMode::kFast) { + const bool propOk = o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, pTrackC, 2.f, matCorr, &dcaInfo); + if (cacheDca && propOk) { // a failed propagation leaves dcaInfo as it was: not cached + mLegDcaCache.emplace(posKey, LegAtDca{dcaInfo, pTrackC}); + posAtDca = pTrackC; + } if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); @@ -863,12 +1166,24 @@ struct PhotonConversionBuilder { auto posdcaZ = dcaInfo[1]; auto nTrack = getTrackParCov(ele); - if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { + bool failedMoveEle = false; + if constexpr (!std::is_void_v) { // TBCs = void: legs of builder run 2, already moved in run 1 + failedMoveEle = trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack); + } + if (failedMoveEle) { if (opt.probe == nullptr) { LOGP(error, "failed correction for negative tpc track"); } + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } + const LegDcaKey eleKey{ele.globalIndex(), collision.globalIndex(), pvX, pvY, pvZ, sideBased}; + const LegAtDca* eleCached = nullptr; + if (cacheDca) { + if (const auto it = mLegDcaCache.find(eleKey); it != mLegDcaCache.end()) { + eleCached = &it->second; + } + } auto nTrackC = nTrack; if (modeTrackPropagation != TrackPropMode::kProper) { dcaInfo = CalculateDCAFast(nTrackC, vtxPrim, d_bz); @@ -876,8 +1191,15 @@ struct PhotonConversionBuilder { nTrackC.setPID(o2::track::PID::Electron); dcaInfoFast = dcaInfo; - if (modeTrackPropagation != TrackPropMode::kFast) { - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); + if (eleCached != nullptr) { + dcaInfo = eleCached->dca; + eleAtDca = eleCached->trk; + } else if (modeTrackPropagation != TrackPropMode::kFast) { + const bool propOk = o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, nTrackC, 2.f, matCorr, &dcaInfo); + if (cacheDca && propOk) { // a failed propagation leaves dcaInfo as it was: not cached + mLegDcaCache.emplace(eleKey, LegAtDca{dcaInfo, nTrackC}); + eleAtDca = nTrackC; + } if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); @@ -887,10 +1209,22 @@ struct PhotonConversionBuilder { auto eledcaZ = dcaInfo[1]; if (std::fabs(posdcaXY) < trackcuts.dcapostopv || std::fabs(eledcaXY) < trackcuts.dcanegtopv) { + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } std::array xyz = {0.f, 0.f, 0.f}; + if (v0cuts.conversionPointMethod != 1) { + // Calculate the conversion point as the weighted average of the two legs' closest approach points -- avoids expensice KFParticle construction for every candidate + const float bzNominal = o2::base::Propagator::Instance()->getNominalBz(); + const o2::track::TrackAuxPar cP(pTrack, bzNominal), cN(nTrack, bzNominal); + const float xC = (cP.xC * cN.rC + cN.xC * cP.rC) / (cP.rC + cN.rC); + const float yC = (cP.yC * cN.rC + cN.yC * cP.rC) / (cP.rC + cN.rC); + if (RecoDecay::sqrtSumOfSquares(xC, yC) > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point + return; + } + } Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); if (v0cuts.conversionPointMethod == 1) { LegScan scanP, scanN; @@ -907,78 +1241,12 @@ struct PhotonConversionBuilder { } float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut - } - - float phiv = 999.f; - float psipair = 999.f; - float phivFast = 999.f; - float psipairFast = 999.f; - float baseR = std::hypot(xyz[0], xyz[1]); - // This method uses the track Helix instead of the full propagation. - // Hence, it is only an approximation but much faster - if (modeTrackPropagation != TrackPropMode::kProper) { - - o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); - o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); - - float diffX = helixPosEle.xC - helixPosPos.xC; - float diffY = helixPosEle.yC - helixPosPos.yC; - auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); - - // Electron - float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); - // Positron - float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) - auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); - - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); - - // Store values for later comparison - phivFast = phiv; - psipairFast = psipair; - } - // This uses the full propagation including material effects - if (modeTrackPropagation != TrackPropMode::kFast) { - std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; - bool pPropagatedSuccess = false; - bool nPropagatedSuccess = false; - auto pTrackProp = pTrack; - auto nTrackProp = nTrack; - for (const auto& offsetR : offsetsR) { - pTrackProp = pTrack; - pTrackProp.setPID(o2::track::PID::Electron); - nTrackProp = nTrack; - nTrackProp.setPID(o2::track::PID::Electron); - - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); - - pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); - nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); - - if (pPropagatedSuccess && nPropagatedSuccess) { - KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); - break; - } - LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; - } - if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { - registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); - registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); - } - } - - if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) - LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + mLastFitReject = __LINE__; // rejection point (diagnostic) // RZ line cut + return; } KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); @@ -993,8 +1261,10 @@ struct PhotonConversionBuilder { if (v0cuts.kfMassConstrain > -0.1) { // o2-linter: disable=magic-number (nonsensical default value) gammaKF.SetNonlinearMassConstraint(v0cuts.kfMassConstrain); } - KFPVertex kfpVertex = createKFPVertexFromCollision(collision); + KFPVertex kfpVertex = opt.pv ? createKFPVertexFromCollision(*opt.pv) : createKFPVertexFromCollision(collision); KFParticle KFPV(kfpVertex); + const float chi2NDFVtx = gammaKF.GetNDF() > 0 ? gammaKF.GetChi2() / gammaKF.GetNDF() : -1.f; // before the PV constraint + const float chi2PV = gammaKF.GetDeviationFromVertex(KFPV); // Transport the gamma to the recalculated decay vertex KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) @@ -1003,22 +1273,27 @@ struct PhotonConversionBuilder { float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); if (!ele.hasITS() && !pos.hasITS()) { if (cospa_kf < v0cuts.min_v0cospa_tpconly) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (cospa_kf < v0cuts.min_v0cospa_its) { + mLastFitReject = __LINE__; // rejection point return; } } float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); if (rxy > trackcuts.maxX + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point return; } if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { - return; // RZ line cut + mLastFitReject = __LINE__; // rejection point // RZ line cut + return; } if (rxy < v0cuts.min_v0radius || v0cuts.max_v0radius < rxy) { + mLastFitReject = __LINE__; // rejection point return; } @@ -1040,27 +1315,33 @@ struct PhotonConversionBuilder { if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly if (rxy > std::min(pTrack.getX(), nTrack.getX()) + v0cuts.margin_r_its) { + mLastFitReject = __LINE__; // rejection point return; } } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC if (rxy > std::min(83.f, nTrack.getX()) + v0cuts.margin_r_itstpc_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC if (rxy > std::min(pTrack.getX(), 83.f) + v0cuts.margin_r_itstpc_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC if (rxy > std::min(83.f, 83.f) + v0cuts.margin_r_tpc) { + mLastFitReject = __LINE__; // rejection point return; } } if ((!pos.hasITS() || !ele.hasITS()) && rxy < v0cuts.max_r_req_its) { // conversion points smaller than v0cuts.max_r_req_its have to be detected with ITS hits. + mLastFitReject = __LINE__; // rejection point return; } if ((!pos.hasITS() && !ele.hasITS()) && rxy < v0cuts.min_r_tpconly) { // TPConly tracks can detect conversion points larger than v0cuts.min_r_tpconly. + mLastFitReject = __LINE__; // rejection point return; } @@ -1079,13 +1360,91 @@ struct PhotonConversionBuilder { // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); if (std::fabs(v0eta) > v0cuts.max_eta_v0 || v0pt < v0cuts.min_pt_v0) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > v0cuts.max_pt_v0_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } + // phi_V and psi_pair (4 material propagations) are only stored with the candidate: computed after the cuts (CPU; identical values) + float phiv = 999.f; + float psipair = 999.f; + float phivFast = 999.f; + float psipairFast = 999.f; + float baseR = std::hypot(xyz[0], xyz[1]); + // This method uses the track Helix instead of the full propagation. + // Hence, it is only an approximation but much faster + if (modeTrackPropagation != TrackPropMode::kProper) { + + o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); + o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); + + float diffX = helixPosEle.xC - helixPosPos.xC; + float diffY = helixPosEle.yC - helixPosPos.yC; + auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); + + // Electron + float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); + // Positron + float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); + + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); + + // Store values for later comparison + phivFast = phiv; + psipairFast = psipair; + } + // This uses the full propagation including material effects + if (modeTrackPropagation != TrackPropMode::kFast) { + std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; + bool pPropagatedSuccess = false; + bool nPropagatedSuccess = false; + auto pTrackProp = pTrack; + auto nTrackProp = nTrack; + for (const auto& offsetR : offsetsR) { + // the legs at their DCA to the vertex: the same propagation as for the DCA cut above (reused when it succeeded there) + if (posAtDca) { + pTrackProp = *posAtDca; + } else { + pTrackProp = pTrack; + pTrackProp.setPID(o2::track::PID::Electron); + o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, pTrackProp, 2.f, matCorr, &dcaInfo); + } + if (eleAtDca) { + nTrackProp = *eleAtDca; + } else { + nTrackProp = nTrack; + nTrackProp.setPID(o2::track::PID::Electron); + o2::base::Propagator::Instance()->propagateToDCABxByBz({pvX, pvY, pvZ}, nTrackProp, 2.f, matCorr, &dcaInfo); + } + + pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); + nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); + + if (pPropagatedSuccess && nPropagatedSuccess) { + KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); + break; + } + LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; + } + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); + registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); + } + } + + if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) + LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + } KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex kfp_pos_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex @@ -1100,46 +1459,56 @@ struct PhotonConversionBuilder { if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpc_inner_fc) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpconly) { + mLastFitReject = __LINE__; // rejection point return; } } } else if (opt.nonSVertexer && recovery.finderMaxPCAMixed > 0.f && pos.hasITS() != ele.hasITS()) { if (v0photoncandidate.getPCA() > recovery.finderMaxPCAMixed) { + mLastFitReject = __LINE__; // rejection point return; } } else { // V0s with ITS hits if (rxy < v0cuts.max_r_req_its) { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { + mLastFitReject = __LINE__; // rejection point return; } } else { if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_its) { + mLastFitReject = __LINE__; // rejection point return; } } } if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > trackcuts.maxpt_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > trackcuts.maxpt_itsonly) { + mLastFitReject = __LINE__; // rejection point return; } if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. // o2-linter: disable=magic-number (protection for uint16) + mLastFitReject = __LINE__; // rejection point return; } if (std::fabs(v0photoncandidate.getDcaXYToPV()) > v0cuts.max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > v0cuts.max_dcatopv_z_v0) { + mLastFitReject = __LINE__; // rejection point return; } if (!opt.relaxAP && !checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + mLastFitReject = __LINE__; // rejection point return; } @@ -1152,12 +1521,14 @@ struct PhotonConversionBuilder { const float score = dedupScore(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), gap); if (opt.probe != nullptr) { *opt.probe = {.ok = true, .score = score}; + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } const float purity = usePurity() ? purityScore(pos, ele, cospa_kf, v0photoncandidate.getPCA(), meeSV, gap, v0photoncandidate.getPsiPair()) : -999.f; if (opt.purityGate) { registry.fill(HIST("Finder/hScore"), purity); - if (purity <= recovery.finderMinScore) { + if (purity <= (opt.minPurity > -998.f ? opt.minPurity : recovery.finderMinScore.value)) { // o2-linter: disable=magic-number (unset marker) + mLastFitReject = __LINE__; // rejection point (diagnostic) return; } } @@ -1173,6 +1544,9 @@ struct PhotonConversionBuilder { v0Helper.svX = gammaKF_DecayVtx.GetX(); v0Helper.svY = gammaKF_DecayVtx.GetY(); v0Helper.svZ = gammaKF_DecayVtx.GetZ(); + v0Helper.pxAtSV = gammaKF_DecayVtx.GetPx(); + v0Helper.pyAtSV = gammaKF_DecayVtx.GetPy(); + v0Helper.pzAtSV = gammaKF_DecayVtx.GetPz(); v0Helper.purity = purity; v0Helper.cosPAxy = cospaXYKF; v0Helper.cosPA3D = cospa_kf; @@ -1199,6 +1573,7 @@ struct PhotonConversionBuilder { } } if (!isSelectedML) { + mLastFitReject = __LINE__; // rejection point return; } if (filltable) { @@ -1264,11 +1639,16 @@ struct PhotonConversionBuilder { cospa_kf, cospaXYKF, cospaRZKF, v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); + v0photonskfchi2(chi2NDFVtx, chi2PV); // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second + if (fillLegCov) { + fillLegCovTable(pTrack); + fillLegCovTable(nTrack); + } } // end of fill table } @@ -1278,6 +1658,20 @@ struct PhotonConversionBuilder { return CandKey{.v0Id = c.v0ID, .collisionId = c.colID, .posId = c.posID, .eleId = c.eleID}; } + // MC diagnostics: the trace of a leg pair of one genuine photon, nullptr for any other pair or without diagnostics + TrueLegTrace* trueLegTrace(const int64_t posId, const int64_t eleId) + { + if (mDiag == nullptr) { + return nullptr; + } + const auto ip = mLegMother.find(posId); + const auto ie = mLegMother.find(eleId); + if (ip == mLegMother.end() || ie == mLegMother.end() || ip->second != ie->second) { + return nullptr; + } + return &mDiag->trueLegTrace[{posId, eleId}]; + } + std::map candidateIndexByKey() const { std::map index; @@ -1302,7 +1696,6 @@ struct PhotonConversionBuilder { } } - // photon finder // finder leg: helix circle and the time window in which the track can belong to a collision (TPC-only legs: from the drift-time asymmetry) template bool makeFinderLeg(TTrack const& track, TCollisions const& collisions, const int64_t bcRef, FinderLeg& leg) @@ -1345,9 +1738,10 @@ struct PhotonConversionBuilder { { std::set usedLegs; std::set> builtPairs; - // legs of accepted candidates are not free; with finderFreeCollinearLegs, legs of collinear candidate pairs stay free - std::vector collinear(vecV0Dedup.size(), false); - if (recovery.finderFreeCollinearLegs > 0.f) { + // legs of accepted candidates are not free; with finderFreeCollinearLegs, legs of collinear candidate pairs stay free; with + // finderFreeAllLegs no leg is blocked (only pairs already built are skipped) + std::vector collinear(vecV0Dedup.size(), recovery.finderFreeAllLegs.value); + if (recovery.finderFreeCollinearLegs > 0.f && !recovery.finderFreeAllLegs) { std::map> candsOfColl; for (size_t i = 0; i < vecV0Dedup.size(); ++i) { candsOfColl[vecV0Dedup[i].colID].push_back(i); @@ -1368,9 +1762,13 @@ struct PhotonConversionBuilder { } } } + std::vector inCandidate(tracks.size(), 0); // CPU: builtPairs is looked up only when both legs are in a candidate for (size_t i = 0; i < vecV0Dedup.size(); ++i) { const auto& c = vecV0Dedup[i]; builtPairs.emplace(c.posID, c.eleID); + if (c.posID >= 0 && c.posID < static_cast(inCandidate.size()) && c.eleID >= 0 && c.eleID < static_cast(inCandidate.size())) { + inCandidate[c.posID] = inCandidate[c.eleID] = 1; + } if (!collinear[i]) { usedLegs.insert(c.posID); usedLegs.insert(c.eleID); @@ -1423,25 +1821,100 @@ struct PhotonConversionBuilder { const FinderCuts cuts{recovery.finderMaxDeltaTgl, recovery.finderMaxCircleGap, v0cuts.min_r_tpconly, v0cuts.max_r_req_its, recovery.finderRMargin, trackcuts.maxX + v0cuts.margin_r_tpc, recovery.finderMinCosXY, recovery.finderMaxDeltaZ, recovery.finderMaxDz, d_bz}; - const FitOptions finderFit{.nonSVertexer = true, .purityGate = true}; + const FitOptions finderFit{.nonSVertexer = !recovery.finderUniformFit.value, .purityGate = !recovery.finderUniformFit.value}; const size_t nBefore = vecV0Dedup.size(); - for (const auto& p : posLegs) { - for (auto itE = std::lower_bound(eleLegs.begin(), eleLegs.end(), p.tgl - recovery.finderMaxDeltaTgl, [](const FinderLeg& c, float v) { return c.tgl < v; }); - itE != eleLegs.end() && itE->tgl <= p.tgl + recovery.finderMaxDeltaTgl; ++itE) { - if ((!p.noITS && !itE->noITS) || builtPairs.contains({p.id, itE->id})) { - continue; + std::array stepCount{}; // filled into Finder/hSteps once per DF (CPU: one fill per pair dominated on data) + auto isBuilt = [&](int64_t a, int64_t b) { return inCandidate[a] && inCandidate[b] && builtPairs.contains({a, b}); }; + // one leg pair, in the order of the original loop (positive legs in track order, negative legs in tgl order) + auto tryPair = [&](FinderLeg const& p, FinderLeg const& e) { + TrueLegTrace* trace = nullptr; + if constexpr (isMC) { + trace = trueLegTrace(p.id, e.id); + } + if ((!p.noITS && !e.noITS) || isBuilt(p.id, e.id)) { + if (trace != nullptr) { + trace->finderStep = (!p.noITS && !e.noITS) ? -8 : -7; // o2-linter: disable=magic-number (trace codes) + } + return; + } + int64_t colId = -1; + const int step = evalFinderPair(p, e, colls, cuts, xB, yB, vDrift, colId); + if (step >= 0 && step < FinderStep::kNFindStep) { + stepCount[step] += 1.; + } + if (trace != nullptr) { + trace->finderStep = step; + } + if (step == FinderStep::kFindCollision) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(p.id), tracks.rawIteratorAt(e.id), -1, false, finderFit); + if (trace != nullptr) { + trace->finderLine = mLastFitReject; + } + } + }; + const auto tglLess = [](const FinderLeg& c, float v) { return c.tgl < v; }; + // time-binning of the negative legs: the time window of a positive leg is intersected with the time windows of the negative legs in the same bin. Reason for implementation: faster than checking all negative legs for each positive leg + const bool timeBinned = (!isMC || mDiag == nullptr) && !eleLegs.empty(); + std::vector> eleInBin; + double tBin0 = 0.; + const double binWidth = 5000.; // ns + if (timeBinned) { + double tLoAll = eleLegs.front().tMin, tHiAll = eleLegs.front().tMax; + for (const auto& e : eleLegs) { + tLoAll = std::min(tLoAll, e.tMin); + tHiAll = std::max(tHiAll, e.tMax); + } + tBin0 = tLoAll; + eleInBin.resize(static_cast((tHiAll - tLoAll) / binWidth) + 1); + for (uint32_t k = 0; k < eleLegs.size(); ++k) { // k increasing: every bin stays sorted by index = tgl order + const auto b0 = static_cast((eleLegs[k].tMin - tBin0) / binWidth); + const auto b1 = std::min(static_cast((eleLegs[k].tMax - tBin0) / binWidth), eleInBin.size() - 1); + for (size_t b = b0; b <= b1; ++b) { + eleInBin[b].push_back(k); } - int64_t colId = -1; - const int step = evalFinderPair(p, *itE, colls, cuts, xB, yB, vDrift, colId); - if (step >= 0) { - registry.fill(HIST("Finder/hSteps"), static_cast(step)); + } + } + std::vector tried; + for (const auto& p : posLegs) { + const auto lo = static_cast(std::lower_bound(eleLegs.begin(), eleLegs.end(), p.tgl - recovery.finderMaxDeltaTgl, tglLess) - eleLegs.begin()); + if (!timeBinned) { + for (uint32_t k = lo; k < eleLegs.size() && eleLegs[k].tgl <= p.tgl + recovery.finderMaxDeltaTgl; ++k) { + tryPair(p, eleLegs[k]); } - if (step == FinderStep::kFindCollision) { - fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(p.id), tracks.rawIteratorAt(itE->id), -1, false, finderFit); + continue; + } + const auto hi = static_cast(std::upper_bound(eleLegs.begin(), eleLegs.end(), p.tgl + recovery.finderMaxDeltaTgl, [](float v, const FinderLeg& c) { return v < c.tgl; }) - eleLegs.begin()); + if (lo >= hi || p.tMax < tBin0) { + continue; + } + tried.clear(); + const auto b0 = static_cast(std::max(0., (p.tMin - tBin0) / binWidth)); + const auto b1 = std::min(static_cast((p.tMax - tBin0) / binWidth), eleInBin.size() - 1); + for (size_t b = b0; b <= b1 && b < eleInBin.size(); ++b) { + const auto& v = eleInBin[b]; + for (auto it = std::lower_bound(v.begin(), v.end(), lo); it != v.end() && *it < hi; ++it) { + tried.push_back(*it); } } + if (b1 > b0) { + std::sort(tried.begin(), tried.end()); + tried.erase(std::unique(tried.begin(), tried.end()), tried.end()); + } + for (const auto k : tried) { + tryPair(p, eleLegs[k]); + } + } + for (int st = 0; st < FinderStep::kNFindStep; ++st) { + if (stepCount[st] > 0.) { + registry.fill(HIST("Finder/hSteps"), static_cast(st), stepCount[st]); + } } registry.fill(HIST("Finder/hNAdded"), static_cast(vecV0Dedup.size() - nBefore)); + if (mDiag != nullptr) { + for (size_t i = nBefore; i < vecV0Dedup.size(); ++i) { + mDiag->finderKeys.insert(keyOf(vecV0Dedup[i])); + } + } } // two stored photons that use two pieces of one track (same tgl, compatible parameters): keep the better one @@ -1468,21 +1941,39 @@ struct PhotonConversionBuilder { const float chi2 = splitPieceChi2(trA, trB, d_bz); return chi2 >= 0.f && chi2 < dedup.splitLegMaxChi2; }; - std::vector> keptPos, keptEle; + // the keptPosOf and keptEleOf maps are keyed by collisionId (or 0 for mixed instances) and contain a map of tgl to the kept leg information + struct KeptLeg { + int64_t id; + size_t owner; + size_t order; + }; + std::unordered_map> keptPosOf, keptEleOf; + std::unordered_map tglOf; + auto tgl = [&](int64_t id) { + const auto it = tglOf.find(id); + return it != tglOf.end() ? it->second : (tglOf[id] = tracks.rawIteratorAt(id).tgl()); + }; + auto firstOwner = [&](std::multimap const& kept, int64_t id) { + const float t = tgl(id); + size_t bestOrder = std::numeric_limits::max(); + int64_t owner = -1; + for (auto it = kept.lower_bound(t - dedup.splitLegMaxDTgl); it != kept.end() && it->first <= t + dedup.splitLegMaxDTgl; ++it) { + if (it->second.order < bestOrder && samePiece(id, it->second.id)) { + bestOrder = it->second.order; + owner = static_cast(it->second.owner); + } + } + return owner; + }; std::vector removed(stored_fullv0Ids.size(), 0); int nRemoved = 0; for (const auto& [rank, k] : order) { const auto& key = stored_fullv0Ids[k]; - int64_t owner = -1; - for (size_t m = 0; m < keptPos.size() && owner < 0; ++m) { - if (samePiece(key.posId, keptPos[m].first)) { - owner = static_cast(keptPos[m].second); - } - } - for (size_t m = 0; m < keptEle.size() && owner < 0; ++m) { - if (samePiece(key.eleId, keptEle[m].first)) { - owner = static_cast(keptEle[m].second); - } + auto& keptPos = keptPosOf[kMixedInstance ? static_cast(key.collisionId) : 0]; + auto& keptEle = keptEleOf[kMixedInstance ? static_cast(key.collisionId) : 0]; + int64_t owner = firstOwner(keptPos, key.posId); + if (owner < 0) { + owner = firstOwner(keptEle, key.eleId); } if (owner >= 0) { removed[k] = 1; @@ -1492,8 +1983,8 @@ struct PhotonConversionBuilder { } continue; } - keptPos.emplace_back(key.posId, k); - keptEle.emplace_back(key.eleId, k); + keptPos.emplace(tgl(key.posId), KeptLeg{key.posId, k, keptPos.size()}); + keptEle.emplace(tgl(key.eleId), KeptLeg{key.eleId, k, keptEle.size()}); } if (nRemoved > 0) { std::vector kept; @@ -1521,14 +2012,24 @@ struct PhotonConversionBuilder { storedOfColl[key.collisionId].push_back(it->second); } } - // the AP cut is the main reason the true pairing of a crossed group was not built - auto fitPair = [&](int64_t colId, int64_t posId, int64_t eleId, PairFit* probe) { - fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(posId), tracks.rawIteratorAt(eleId), -1, false, - FitOptions{.nonSVertexer = true, .relaxAP = true, .probe = probe}); + auto fitPair = [&](int64_t colId, int64_t posId, int64_t eleId, PairFit* probe, bool original = false) { + const auto pos = tracks.rawIteratorAt(posId); + SourcePV srcPv; + const SourcePV* pv = nullptr; + if constexpr (requires { pos.sourcePvX(); }) { + if (original && mixed.refitOriginalsAtSourceVertex) { + srcPv = SourcePV{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + pv = &srcPv; + } + } + const auto ele = tracks.rawIteratorAt(eleId); + const bool finderLike = mixed.uniformPairFit ? !(pos.hasITS() && ele.hasITS()) : !recovery.finderUniformFit.value; // uniformPairFit (run 2): options by the legs + fitV0Candidate(collisions.rawIteratorAt(colId), pos, ele, -1, false, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = mixed.uniformPairFit && finderLike && recovery.runFinder.value, .probe = probe, .pv = pv}); }; - auto probeFit = [&](int64_t colId, int64_t posId, int64_t eleId) { + auto probeFit = [&](int64_t colId, int64_t posId, int64_t eleId, bool original = false) { PairFit out; - fitPair(colId, posId, eleId, &out); + fitPair(colId, posId, eleId, &out, original); return out; }; auto crossedPrefilter = [&](int64_t posId, int64_t eleId) { @@ -1546,7 +2047,7 @@ struct PhotonConversionBuilder { auto it = storedFit.find(idx); if (it == storedFit.end()) { const auto& c = vecV0Dedup[idx]; - it = storedFit.emplace(idx, probeFit(c.colID, c.posID, c.eleID)).first; + it = storedFit.emplace(idx, probeFit(c.colID, c.posID, c.eleID, true)).first; } return it->second; }; @@ -1572,12 +2073,19 @@ struct PhotonConversionBuilder { } const PairFit storedI = storedFitOf(members[a]); const PairFit storedJ = storedFitOf(members[b]); - const PairFit crossedI = crossedPrefilter(ci.posID, cj.eleID) ? probeFit(colId, ci.posID, cj.eleID) : PairFit{}; - const PairFit crossedJ = crossedPrefilter(cj.posID, ci.eleID) ? probeFit(colId, cj.posID, ci.eleID) : PairFit{}; + const bool preI = crossedPrefilter(ci.posID, cj.eleID); + const PairFit crossedI = preI ? probeFit(colId, ci.posID, cj.eleID) : PairFit{}; + const int lineI = preI && !crossedI.ok ? mLastFitReject : 0; + const bool preJ = crossedPrefilter(cj.posID, ci.eleID); + const PairFit crossedJ = preJ ? probeFit(colId, cj.posID, ci.eleID) : PairFit{}; + const int lineJ = preJ && !crossedJ.ok ? mLastFitReject : 0; Test t; t.rec = {keyOf(ci), keyOf(cj), q, storedI.ok && storedJ.ok && crossedI.ok && crossedJ.ok, false}; + t.rec.fail = (storedI.ok ? 0 : 1) | (storedJ.ok ? 0 : 2) | (preI ? 0 : 4) | (preJ ? 0 : 8) | (preI && !crossedI.ok ? 16 : 0) | (preJ && !crossedJ.ok ? 32 : 0); // o2-linter: disable=magic-number (bit mask) + t.rec.line = lineI > 0 ? lineI : lineJ; if (t.rec.complete) { // gain > 0: the crossed pairing has the lower (better) summed dedup score t.gain = (storedI.score + storedJ.score) - (crossedI.score + crossedJ.score); + t.rec.gain = t.gain; } tests.push_back(t); } @@ -1653,6 +2161,10 @@ struct PhotonConversionBuilder { Preslice perCollision = o2::aod::v0::collisionId; std::map pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca std::map cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa + int mLastFitReject = 0; // source line of the last rejection in fitV0Candidate, 0 = accepted (diagnostic) + DedupDiag* mDiag = nullptr; // run 1 with MC diagnostics: where the finder and fillV0Table record the genuine leg pairs + std::unordered_map mLegMother; // MC: track -> first mother of its MC electron (genuine leg pairs share it) + std::unordered_map mOccOfSourceEvent; // run 2: mixer event uid -> occupancy (from its same-event artificial event) std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) std::vector stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) @@ -1744,6 +2256,11 @@ struct PhotonConversionBuilder { stored_v0Ids.clear(); stored_fullv0Ids.clear(); nv0_map.clear(); + mGroupOfKey.clear(); + mPermuteResults.clear(); + if constexpr (kMixedInstance) { + mGroupSizeOfCand.assign(nCand, 0); + } int nGreedyFallback = 0; // conflict groups: candidates connected through a shared leg @@ -1813,9 +2330,22 @@ struct PhotonConversionBuilder { std::make_pair(sol.bestCount - sol.bestWithCount[k], sol.bestWithScore[k] - sol.bestScore); } } + if constexpr (kMixedInstance) { // permutation test: the same group in reversed order must select the same candidates + const std::vector legMaskRev(legMask.rbegin(), legMask.rend()); + const std::vector scoresRev(scores.rbegin(), scores.rend()); + const auto solRev = solveConflictGroup(legMaskRev, scoresRev, dedup.groupMatchingScoreThreshold.value, false); + bool changed = false; + for (int k = 0; k < nMembers; ++k) { + changed = changed || (((solRev.bestMask & (1u << (nMembers - 1 - k))) != 0) != (selected[k] != 0)); + } + mPermuteResults.push_back({vecV0Dedup[members[0]].colID, nMembers, changed, false}); + } } else { // Too large to enumerate: greedy by score ++nGreedyFallback; + if constexpr (kMixedInstance) { + mPermuteResults.push_back({vecV0Dedup[members[0]].colID, nMembers, false, true}); + } std::vector usedTracks; usedTracks.reserve(2 * nMembers); for (int k = 0; k < nMembers; ++k) { @@ -1830,15 +2360,76 @@ struct PhotonConversionBuilder { } } - // store the winners, book the blockers of the losers - for (int k = 0; k < nMembers; ++k) { - const auto& cand = vecV0Dedup[members[k]]; - if (selected[k] != 0) { - stored_v0Ids.emplace_back(cand.posID, cand.eleID); - stored_fullv0Ids.push_back(keyOf(cand)); - nv0_map[cand.colID]++; - continue; - } + // run 1 (MC diagnostic of the swap decision): half-swap rule: if a candidate is worse than two others that share its legs, and the crossed candidate is not present, replace it + if (dedup.dedupHalfSwapRule && nMembers >= 3) { + int nReplaced = 0; + for (int t = 0; t < nMembers; ++t) { + if (selected[t] != 0) { + continue; + } + const auto& cT = vecV0Dedup[members[t]]; + int w1 = -1, w2 = -1; + for (int k = 0; k < nMembers; ++k) { + if (selected[k] == 0 || k == t) { + continue; + } + const auto& cW = vecV0Dedup[members[k]]; + if (cW.posID == cT.posID) { + w1 = k; + } + if (cW.eleID == cT.eleID) { + w2 = k; + } + } + if (w1 < 0 || w2 < 0 || w1 == w2) { + continue; + } + const auto& cW1 = vecV0Dedup[members[w1]]; + const auto& cW2 = vecV0Dedup[members[w2]]; + if (!(cT.score < std::min(cW1.score, cW2.score))) { + continue; + } + bool complementIsCandidate = false; + for (int k = 0; k < nMembers; ++k) { + const auto& cC = vecV0Dedup[members[k]]; + if (cC.posID == cW2.posID && cC.eleID == cW1.eleID) { + complementIsCandidate = true; + break; + } + } + if (complementIsCandidate) { + continue; + } + selected[w1] = 0; + selected[w2] = 0; + selected[t] = 1; + ++nReplaced; + } + registry.fill(HIST("Dedup/hHalfSwapRule"), static_cast(nReplaced)); + } + + std::array groupInfo{root, nMembers, 0, static_cast(std::count(selected.begin(), selected.end(), 1))}; + if constexpr (kMixedInstance) { + std::set legsOfGroup; + for (const int& m : members) { + legsOfGroup.insert(vecV0Dedup[m].posID); + legsOfGroup.insert(vecV0Dedup[m].eleID); + mGroupSizeOfCand[m] = nMembers; + } + groupInfo[2] = static_cast(legsOfGroup.size()); + } + // store the winners, book the blockers of the losers + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (selected[k] != 0) { + stored_v0Ids.emplace_back(cand.posID, cand.eleID); + stored_fullv0Ids.push_back(keyOf(cand)); + nv0_map[cand.colID]++; + if constexpr (kMixedInstance) { + mGroupOfKey[keyOf(cand)] = groupInfo; + } + continue; + } if (diag == nullptr) { continue; } @@ -1919,7 +2510,22 @@ struct PhotonConversionBuilder { void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) { mRelaxedKeys.clear(); - // step 1: SVertexer V0s -> dedup candidates + mLegDcaCache.clear(); + mDiag = diag; + mLegMother.clear(); + if constexpr (isMC) { + if (diag != nullptr) { // genuine leg pairs = opposite-sign MC electrons with the same first mother (MCDedup/Inventory/hSwapOrigin) + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto mc = track.template mcParticle_as(); + if (std::abs(mc.pdgCode()) == kElectron && mc.has_mothers()) { + mLegMother[track.globalIndex()] = mc.mothersIds()[0]; + } + } + } + } for (const auto& collision : collisions) { if constexpr (isMC) { if (!collision.has_mcCollision()) { @@ -1954,7 +2560,6 @@ struct PhotonConversionBuilder { } // end of v0 loop } // end of collision loop - // photon finder if (recovery.runFinder) { runPhotonFinder(collisions, tracks); } @@ -1962,7 +2567,6 @@ struct PhotonConversionBuilder { stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF - // step 3: deduplication switch (dedup.deduplicationMode.value) { case V0DeduplicationMode::Pairwise: selectPairwise(diag); @@ -1978,7 +2582,6 @@ struct PhotonConversionBuilder { break; } - // split legs, ambiguity resolver (on the stored photons only) if (dedup.resolveSplitLegs) { resolveSplitLegConflicts(tracks, diag); } @@ -2027,7 +2630,6 @@ struct PhotonConversionBuilder { } } - // refit the stored photons and write the tables (SVertexer V0s through fillV0Table, the others directly) for (const auto& fullv0Id : stored_fullv0Ids) { auto v0Id = fullv0Id.v0Id; const auto collisionId = fullv0Id.collisionId; @@ -2058,8 +2660,11 @@ struct PhotonConversionBuilder { } } fitV0Candidate(collision, tracks.rawIteratorAt(fullv0Id.posId), tracks.rawIteratorAt(fullv0Id.eleId), v0Id, true, - FitOptions{.nonSVertexer = true, .relaxAP = mRelaxedKeys.contains(fullv0Id)}); + FitOptions{.nonSVertexer = !recovery.finderUniformFit.value, .relaxAP = mRelaxedKeys.contains(fullv0Id) && !recovery.finderUniformFit.value}); } // end of fullv0Id loop + if (fillFreeLegs || fillFreeLegIds) { + writeFreeLegs(collisions, tracks, skimRejected); + } // for (const auto& collision : collisions) { // if constexpr (isMC) { @@ -2080,6 +2685,1235 @@ struct PhotonConversionBuilder { vecV0Dedup.clear(); } // end of build + // MC photon pair information for the deduplication diagnostics (run 1) and the mixer (run 2) + struct MixPairPhoton { + float px = 0.f, py = 0.f, pz = 0.f; + std::pair posKey{-1, -1}, eleKey{-1, -1}; + bool posTrue = false, eleTrue = false; + int64_t posRow = -1, eleRow = -1; // PCMMixLegs rows + bool original = true; // leg pairing of a run-1 photon (false: crossed pairing built in run 2) + std::array group{-1, 0, 0, 0}; // run-2 conflict group (id, candidates, legs, stored photons), id -1: unknown + float vx = 0.f, vy = 0.f, vz = 0.f; // conversion point (run 1 before, run 2 after) + struct LegMC { // MC of one leg for the fake-pair classes (ids within the DF of the source event) + int64_t evt = -1; + int pid = -1, mid = -1, gmid = -1, pdg = 0, mpdg = 0, trk = -1, anc = -1; + int ggid = -1, root = -1, rootPdg = 0; // mother of the grandmother, root of the decay chain of the photon + bool its = false; + float phi = 0.f, tgl = 0.f, sharedFrac = 0.f; // track direction and TPC shared-cluster fraction (data-computable) + float vx = 0.f, vy = 0.f, vz = 0.f; // run-1 conversion point of the photon the leg belonged to + }; + LegMC posMC, eleMC; + }; + template + static void setMixPairLegs(MixPairPhoton& p, TLeg const& pos, TLeg const& ele) + { + p.posKey = {pos.sourceEventUid(), pos.mcMotherId()}; + p.eleKey = {ele.sourceEventUid(), ele.mcMotherId()}; + p.posTrue = pos.mcMotherId() >= 0 && pos.mcMotherPdg() == kGamma; + p.eleTrue = ele.mcMotherId() >= 0 && ele.mcMotherPdg() == kGamma; + using LegMC = typename MixPairPhoton::LegMC; + auto legMC = [](TLeg const& l) { + return LegMC{l.sourceEventUid(), l.mcParticleId(), l.mcMotherId(), l.mcGrandMotherId(), l.mcPdg(), l.mcMotherPdg(), l.origTrackId(), l.mcAncestorId(), + l.mcGGMotherId(), l.mcRootId(), l.mcRootPdg(), static_cast(l.hasITS()), l.phi(), l.tgl(), l.tpcFractionSharedCls(), l.sourceVx(), l.sourceVy(), l.sourceVz()}; + }; + p.posMC = legMC(pos); + p.eleMC = legMC(ele); + } + + static int mixCommonOrigin(MixPairPhoton::LegMC const& x, MixPairPhoton::LegMC const& y) + { + if (x.mid < 0 || y.mid < 0 || (x.evt == y.evt && x.mid == y.mid)) { + return -1; + } + if (x.evt != y.evt) { + return 0; + } + if (x.gmid >= 0 && x.gmid == y.gmid) { + return 3; + } + if (x.ggid >= 0 && x.ggid == y.ggid) { + return 2; + } + return (x.root >= 0 && x.root == y.root) ? 1 : 0; + } + // common origin of the two MC photons whose legs form a photon pair (true pair, full or half swap): first leg vs the first leg of + // another MC photon among the remaining three + static int mixPairOrigin(MixPairPhoton const& a, MixPairPhoton const& b) + { + const auto& x = a.posMC; + for (const auto* y : {&a.eleMC, &b.posMC, &b.eleMC}) { + if (y->mid >= 0 && x.mid >= 0 && !(x.evt == y->evt && x.mid == y->mid)) { + return mixCommonOrigin(x, *y); + } + } + return -1; + } + + static bool mixRelated(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto radiatedBy = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; }; + auto siblings = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; }; + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (radiatedBy(*x, *y) || radiatedBy(*y, *x) || siblings(*x, *y)) { + return true; + } + } + } + return false; + } + + static int mixFakeClass(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + const std::array la{&a.posMC, &a.eleMC}, lb{&b.posMC, &b.eleMC}; + auto samePart = [](L const& x, L const& y) { return x.evt == y.evt && x.pid >= 0 && x.pid == y.pid; }; + auto sameMother = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.mid; }; + auto daughterOf = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; }; + auto truePhoton = [&](MixPairPhoton const& p) { return p.posMC.mpdg == kGamma && p.eleMC.mpdg == kGamma && sameMother(p.posMC, p.eleMC); }; + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return 12; + } + } + } + if (truePhoton(a) && truePhoton(b) && sameMother(a.posMC, b.posMC)) { + return 1; + } + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (samePart(*la[i], *lb[j])) { + return (la[1 - i]->mpdg == kGamma && lb[1 - j]->mpdg == kGamma) ? 2 : 3; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->mpdg == kGamma && sameMother(*x, *y)) { + return 4; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (daughterOf(*x, *y) || daughterOf(*y, *x)) { + return 5; + } + } + } + if (mixRelated(a, b)) { + return 5; + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (sameMother(*x, *y)) { + return 6; + } + } + } + const bool aFake = !truePhoton(a), bFake = !truePhoton(b); + if (aFake && bFake) { + return 7; + } + const auto& f = aFake ? a : b; + if (sameMother(f.posMC, f.eleMC)) { + return 8; + } + if (std::abs(f.posMC.pdg) != kElectron || std::abs(f.eleMC.pdg) != kElectron) { + return 9; + } + if (f.posMC.mpdg == kGamma && f.eleMC.mpdg == kGamma) { + return 10; + } + return 11; + } + // two photons come from one shower: a leg of each has the same physical-primary ancestor (photons of one primary photon/e+-) + static bool mixSameAncestor(MixPairPhoton const& a, MixPairPhoton const& b) + { + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (x->evt == y->evt && x->anc >= 0 && x->anc == y->anc) { + return true; + } + } + } + return false; + } + + static std::array mixPairGeometry(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto mid = [](L const& p, L const& e) { return std::array{0.5f * (p.vx + e.vx), 0.5f * (p.vy + e.vy), 0.5f * (p.vz + e.vz)}; }; + const auto ca = mid(a.posMC, a.eleMC), cb = mid(b.posMC, b.eleMC); + auto legDist = [](L const& x, L const& y) { return std::hypot(RecoDecay::constrainAngle(x.phi - y.phi, -o2::constants::math::PI), x.tgl - y.tgl); }; + return {std::hypot(ca[0] - cb[0], ca[1] - cb[1], ca[2] - cb[2]), std::min(legDist(a.posMC, b.posMC), legDist(a.eleMC, b.eleMC)), + std::max({a.posMC.sharedFrac, a.eleMC.sharedFrac, b.posMC.sharedFrac, b.eleMC.sharedFrac})}; + } + // MC photon pair relationship: 0 unrelated, 1 radiated by the other, 2 siblings (same grandmother), 3 daughter of the other + static int mixRelKind(MixPairPhoton const& a, MixPairPhoton const& b) + { + using L = MixPairPhoton::LegMC; + auto radiatedBy = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; }; + auto siblings = [](L const& x, L const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; }; + auto daughterOf = [](L const& x, L const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; }; + int kind = 0; + for (const auto* x : {&a.posMC, &a.eleMC}) { + for (const auto* y : {&b.posMC, &b.eleMC}) { + if (radiatedBy(*x, *y) || radiatedBy(*y, *x)) { + return 1; + } + if (daughterOf(*x, *y) || daughterOf(*y, *x)) { + kind = 3; + } else if (kind == 0 && siblings(*x, *y)) { + kind = 2; + } + } + } + return kind; + } + struct BremsPointing { + float dMin = 999.f, angle = 3.15f, rEmit = -1.f, outward = 0.f, ptB = 0.f; + }; + template + BremsPointing mixBremsPointing(MixPairPhoton const& a, MixPairPhoton const& b, TLegs const& legs) const + { + constexpr float StepCm = 0.25f, MaxArcCm = 100.f, MarginCm = 2.f; + BremsPointing best; + for (int order = 0; order < 2; ++order) { + const auto& emitter = order == 0 ? a : b; + const auto& brems = order == 0 ? b : a; + const float pB = std::hypot(brems.px, brems.py, brems.pz); + if (pB <= 0.f) { + continue; + } + const std::array u{brems.px / pB, brems.py / pB, brems.pz / pB}; + const float rA = std::hypot(emitter.vx, emitter.vy), rB = std::hypot(brems.vx, brems.vy); + for (const int64_t row : {emitter.posRow, emitter.eleRow}) { + const auto leg = legs.rawIteratorAt(row); + const o2::track::TrackPar par(leg.x(), leg.alpha(), std::array{leg.y(), leg.z(), leg.snp(), leg.tgl(), leg.signed1Pt()}); + o2::math_utils::CircleXYf_t circle; + float sna = 0.f, csa = 0.f; + par.getCircleParams(d_bz, circle, sna, csa); + std::array x0{}, p0{}; + par.getXYZGlo(x0); + par.getPxPyPzGlo(p0); + if (circle.rC <= 0.f) { + continue; + } + const float psi0 = std::atan2(x0[1] - circle.yC, x0[0] - circle.xC); + const float sense = ((x0[0] - circle.xC) * p0[1] - (x0[1] - circle.yC) * p0[0]) > 0.f ? 1.f : -1.f; // direction of motion on the circle + const float tgl = par.getTgl(), norm = std::sqrt(1.f + tgl * tgl); + constexpr int NSteps = static_cast((MaxArcCm + MaxArcCm) / StepCm); + for (int iStep = 0; iStep <= NSteps; ++iStep) { + const float s = -MaxArcCm + static_cast(iStep) * StepCm; + const float psi = psi0 + sense * s / circle.rC; + const std::array h{circle.xC + circle.rC * std::cos(psi), circle.yC + circle.rC * std::sin(psi), x0[2] + tgl * s}; + const float rH = std::hypot(h[0], h[1]); + if (rH < rA - MarginCm || rH > rB + MarginCm) { + continue; // emission between the two conversions + } + const std::array d{h[0] - brems.vx, h[1] - brems.vy, h[2] - brems.vz}; + const float t = d[0] * u[0] + d[1] * u[1] + d[2] * u[2]; + if (t > 0.f) { + continue; // in front of B's conversion point + } + const float dist = std::hypot(d[0] - t * u[0], d[1] - t * u[1], d[2] - t * u[2]); + if (dist < best.dMin) { + const std::array tan{-sense * std::sin(psi) / norm, sense * std::cos(psi) / norm, tgl / norm}; + best.dMin = dist; + best.angle = std::acos(std::clamp(tan[0] * u[0] + tan[1] * u[1] + tan[2] * u[2], -1.f, 1.f)); + best.rEmit = rH; + best.outward = rB > rA ? 1.f : 0.f; + best.ptB = std::hypot(brems.px, brems.py); + } + } + } + } + return best; + } + // skip pairs of photons that come from the same source event (MC truth) when mixing, if requested + bool skipSameSourcePair(const float type, MixPairPhoton const& a, MixPairPhoton const& b) + { + if (type < 0.5f) { + return false; + } + const int64_t e = a.posMC.evt; + const bool same = e >= 0 && a.eleMC.evt == e && b.posMC.evt == e && b.eleMC.evt == e; + return same && mixed.skipSameSourcePairs.value; + } + void fillSameSourcePairs(const float type, std::vector const& photons) + { + if (type < 0.5f) { + return; + } + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + const int64_t e = a.posMC.evt; + if (e >= 0 && a.eleMC.evt == e && b.posMC.evt == e && b.eleMC.evt == e) { + registry.fill(HIST("Mixed/Pairs/hSameSource"), qInv(a.px, a.py, a.pz, b.px, b.py, b.pz), mixPairClass(a, b)); + } + } + } + } + void fillMixFakeClasses(const float type, const float stage, const int64_t colId, std::vector const& photons, + std::set> const& candLegs) + { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (stage > 0.5f && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q < 0.5f && mixPairGeometry(a, b)[2] < 0.4f) { // o2-linter: disable=magic-number (normalisation range, analysis leg cut) + registry.fill(HIST("Mixed/Pairs/hQClean"), type, stage, q); + } + if (q >= 0.2f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const auto nIts = static_cast(a.posMC.its + a.eleMC.its + b.posMC.its + b.eleMC.its); + const float amb = (candLegs.contains({colId, a.posRow, b.eleRow}) || candLegs.contains({colId, b.posRow, a.eleRow})) ? 1.f : 0.f; + const float cls = mixPairClass(a, b); + registry.fill(HIST("Mixed/Pairs/hFakeClass"), type, stage, 0.f, q, nIts, amb); + registry.fill(HIST("Mixed/Pairs/hFakeClass"), type, stage, cls, q, nIts, amb); + { + const float rA = std::hypot(a.vx, a.vy), rB = std::hypot(b.vx, b.vy); + const bool aSofter = std::hypot(a.px, a.py, a.pz) < std::hypot(b.px, b.py, b.pz); + const float rSoft = aSofter ? rA : rB, rHard = aSofter ? rB : rA; + registry.fill(HIST("Mixed/Pairs/hFakeClassOrder"), type, stage, cls, q, rSoft < rHard ? 0.f : 1.f, std::fabs(rA - rB)); + } + if (q < 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + const auto g = mixPairGeometry(a, b); + registry.fill(HIST("Mixed/Pairs/hFakeClassGeo"), type, stage, cls, q, amb, mixSameAncestor(a, b) ? 1.f : 0.f, g[0], + std::log10(std::max(g[1], 1e-6f)), g[2]); // o2-linter: disable=magic-number (log floor) + if (stage > 0.5f) { + registry.fill(HIST("Mixed/Pairs/hPairOrigin"), type, cls, q, static_cast(mixPairOrigin(a, b))); + } + if (stage > 0.5f && a.group[0] >= 0 && b.group[0] >= 0) { + // same group: its context; different groups: the larger one + const bool sameGroup = a.group[0] == b.group[0]; + const auto& gr = (sameGroup || a.group[1] >= b.group[1]) ? a.group : b.group; + registry.fill(HIST("Mixed/Pairs/hGroupCtx"), type, cls, q, amb, sameGroup ? 1.f : 0.f, static_cast(std::min(gr[1], 20)), + static_cast(std::min(gr[2], 33)), static_cast(std::min(gr[3], 10))); // o2-linter: disable=magic-number (last bins) + } + } + } + } + } + // MC class of a photon pair (see fillMixFakeClasses) + static float mixPairClass(MixPairPhoton const& a, MixPairPhoton const& b) + { + const int truth = mixPairTruth(a, b); + if (truth == 3) { + return static_cast(mixFakeClass(a, b)); + } + if (truth == 1 || truth == 2) { + return (truth == 1 ? 13.f : 14.f) + (mixRelated(a, b) ? 3.f : 0.f); + } + return 15.f; + } + + template + void fillMixSwapBrems(const float type, const float stage, const int64_t colId, std::vector const& photons, + std::map, float> const& candScore, TLegs const& legs) + { + constexpr float MaxQ = 0.1f, MaxShared = 0.4f; + auto score = [&](int64_t pos, int64_t ele) { + const auto it = candScore.find({colId, pos, ele}); + return it == candScore.end() ? -1.f : it->second; + }; + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (stage > 0.5f && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= MaxQ) { + continue; + } + const float cls = mixPairClass(a, b); + const float clean = mixPairGeometry(a, b)[2] < MaxShared ? 1.f : 0.f; + const auto bp = mixBremsPointing(a, b, legs); + registry.fill(HIST("Mixed/Pairs/hBrems"), type, stage, cls, static_cast(mixRelKind(a, b)), q, + std::log10(std::max(bp.dMin, 1e-3f)), std::log10(std::max(bp.angle, 1e-4f)), bp.rEmit, bp.outward, bp.ptB, clean); // o2-linter: disable=magic-number (log floors) + if (stage < 0.5f || (cls != 14.f && cls != 17.f)) { // o2-linter: disable=magic-number (swap classes) + continue; + } + const float sa = score(a.posRow, a.eleRow), sb = score(b.posRow, b.eleRow); + const float t1 = score(a.posRow, b.eleRow), t2 = score(b.posRow, a.eleRow); + float decision = 2.f; + if (t1 >= 0.f && t2 >= 0.f) { + decision = (sa + sb < t1 + t2) ? 1.f : 0.f; + } + const float origin = (a.original && b.original) ? 1.f : ((!a.original && !b.original) ? 0.f : 2.f); + registry.fill(HIST("Mixed/Pairs/hSwapDecision"), type, origin, decision, q, clean); + } + } + } + // truth of a photon pair: 1 two true photons, 2 the legs of two true photons crossed (full swap), 3 other with a fake, 4 no label + static int mixPairTruth(MixPairPhoton const& a, MixPairPhoton const& b) + { + if (a.posKey.second < 0 || a.eleKey.second < 0 || b.posKey.second < 0 || b.eleKey.second < 0) { + return 4; + } + const bool allTrue = a.posTrue && a.eleTrue && b.posTrue && b.eleTrue; + if (allTrue && a.posKey == a.eleKey && b.posKey == b.eleKey && a.posKey != b.posKey) { + return 1; + } + if (allTrue && a.posKey == b.eleKey && a.eleKey == b.posKey && a.posKey != a.eleKey) { + return 2; + } + return 3; + } + template + void fillMixPairs(const float type, std::vector const& photons) + { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + if (after && skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + const auto truth = static_cast(mixPairTruth(a, b)); + if constexpr (after) { + registry.fill(HIST("Mixed/Pairs/hQAfter"), type, 0.f, q); + registry.fill(HIST("Mixed/Pairs/hQAfter"), type, truth, q); + } else { + registry.fill(HIST("Mixed/Pairs/hQBefore"), type, 0.f, q); + registry.fill(HIST("Mixed/Pairs/hQBefore"), type, truth, q); + } + } + } + } + + // fill the occupancy of the source events and the deduplication diagnostics for the mixed-event photon pairs + template + void fillMatchedConfigs(TMixCollisions const& collisions, TMixLegs const& legs) + { + for (const auto& collision : collisions) { // same-event artificial events carry the occupancy of their source event + if (!collision.isMixed()) { + mOccOfSourceEvent[collision.sourceEventUid()] = collision.occupancy(); + } + } + using Key3 = std::tuple; + std::set cands, stored; + for (const auto& c : vecV0Dedup) { + cands.insert(Key3{c.colID, c.posID, c.eleID}); + } + std::unordered_map storedPartner; + for (const auto& k : stored_fullv0Ids) { + stored.insert(Key3{k.collisionId, k.posId, k.eleId}); + storedPartner.try_emplace(k.posId, k.eleId); + storedPartner.try_emplace(k.eleId, k.posId); + } + for (const auto& collision : collisions) { + const int64_t colId = collision.globalIndex(); + const float type = collision.isMixed() ? 1.f : 0.f; + // MC photons with both legs present: (source event, mother) -> (pos row, ele row) + std::map, std::array> photonLegs; + for (int i = collision.firstLeg(); i < collision.firstLeg() + collision.nLegs(); ++i) { + const auto& l = mLegIt[i]; + if (l.mcMotherId() < 0 || l.mcMotherPdg() != kGamma) { + continue; + } + auto [it, inserted] = photonLegs.try_emplace(std::make_pair(static_cast(l.sourceEventUid()), l.mcMotherId()), std::array{-1, -1}); + it->second[l.sign() > 0 ? 0 : 1] = i; + } + std::vector> photons; + for (const auto& [k, rows] : photonLegs) { + if (rows[0] >= 0 && rows[1] >= 0) { + photons.push_back(rows); + } + } + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& ap = mLegIt[photons[i][0]]; + const auto& am = mLegIt[photons[i][1]]; + const auto& bp = mLegIt[photons[j][0]]; + const auto& bm = mLegIt[photons[j][1]]; + // related: same mother (siblings) or one photon radiated by a leg particle of the other (bremsstrahlung), as mixRelated + const bool sameEvt = ap.sourceEventUid() == bp.sourceEventUid(); + if (type > 0.5f && sameEvt && mixed.skipSameSourceCrossed) { + continue; // injection: a configuration of two current-event photons is a same-event one + } + const float qTrue = qInv(ap.mcMotherPx(), ap.mcMotherPy(), ap.mcMotherPz(), bp.mcMotherPx(), bp.mcMotherPy(), bp.mcMotherPz()); + if (qTrue >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const int gA = ap.mcGrandMotherId(), gB = bp.mcGrandMotherId(); + const bool related = sameEvt && ((gA >= 0 && gA == gB) || (gA >= 0 && (gA == bp.mcParticleId() || gA == bm.mcParticleId())) || + (gB >= 0 && (gB == ap.mcParticleId() || gB == am.mcParticleId()))); + const int64_t A = photons[i][0], a = photons[i][1], B = photons[j][0], b = photons[j][1]; // pos / ele rows + float input = 2.f; + if (ap.sourcePhotonUid() == am.sourcePhotonUid() && bp.sourcePhotonUid() == bm.sourcePhotonUid() && ap.sourcePhotonUid() != bp.sourcePhotonUid()) { + input = 0.f; + } else if (ap.sourcePhotonUid() == bm.sourcePhotonUid() && bp.sourcePhotonUid() == am.sourcePhotonUid() && ap.sourcePhotonUid() != bp.sourcePhotonUid()) { + input = 1.f; + } + const int nGen = static_cast(cands.contains(Key3{colId, A, a})) + static_cast(cands.contains(Key3{colId, B, b})); + const int nCross = static_cast(cands.contains(Key3{colId, A, b})) + static_cast(cands.contains(Key3{colId, B, a})); + const bool sGen = stored.contains(Key3{colId, A, a}) && stored.contains(Key3{colId, B, b}); + const bool sCross = stored.contains(Key3{colId, A, b}) && stored.contains(Key3{colId, B, a}); + float dOcc = 0.f; // ME: occupancy of the photon's source event - current event, the photon with the larger |difference| + if (type > 0.5f) { + float best = 0.f; + for (const int64_t uid : {static_cast(ap.sourceEventUid()), static_cast(bp.sourceEventUid())}) { + const auto it = mOccOfSourceEvent.find(uid); + const float d = it == mOccOfSourceEvent.end() ? -19999.f : it->second - collision.occupancy(); + if (std::fabs(d) > std::fabs(best)) { + best = d; + } + } + dOcc = best; + } + const int nTpcOnly = static_cast(!ap.hasITS()) + static_cast(!am.hasITS()) + static_cast(!bp.hasITS()) + static_cast(!bm.hasITS()); + registry.fill(HIST("Mixed/Decision/hMatched"), type, qTrue, input, static_cast(nGen), static_cast(nCross), sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, static_cast(collision.numContrib()), static_cast(collision.nLegs()) / 2.f, collision.occupancy(), std::clamp(dOcc, -19999.f, 19999.f), static_cast(nTpcOnly)); + // central electrode: true conversion point (ME partners shifted by the alignment, < 2 cm) and the leg's tan lambda (unchanged by it) + constexpr float kTpcRin = 85.f, kTpcRout = 247.f; // o2-linter: disable=magic-number (TPC drift volume, diagnostic) + auto crossesCE = [&](auto const& l) { + const float rConv = std::hypot(l.mcVx(), l.mcVy()); + if (rConv > kTpcRout) { + return 0; + } + const float z1 = l.mcVz() + l.tgl() * (std::max(rConv, kTpcRin) - rConv), z2 = l.mcVz() + l.tgl() * (kTpcRout - rConv); + return z1 * z2 <= 0.f ? 1 : 0; + }; + auto absEta = [](float px, float py, float pz) { return std::fabs(std::asinh(pz / std::max(std::hypot(px, py), 1.e-6f))); }; + const float minEta = std::min(absEta(ap.mcMotherPx(), ap.mcMotherPy(), ap.mcMotherPz()), absEta(bp.mcMotherPx(), bp.mcMotherPy(), bp.mcMotherPz())); + const float minZ = std::min(std::fabs(ap.mcVz()), std::fabs(bp.mcVz())); + const int nCE = crossesCE(ap) + crossesCE(am) + crossesCE(bp) + crossesCE(bm); + registry.fill(HIST("Mixed/Decision/hMatchedCE"), type, qTrue, input, sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, std::min(minEta, 1.99f), std::min(minZ, 299.f), static_cast(nCE), static_cast(nTpcOnly)); + // leg fates: 0 not stored, 1 own genuine partner, 2 crossed partner, 3 leg of a third complete photon, 4 leg of an incomplete MC photon, 5 no photon mother + std::array nFate{}; + const std::array rows{A, a, B, b}; + const std::array own{a, A, b, B}, crossed{b, B, a, A}; + for (int k = 0; k < 4; ++k) { + const auto it = storedPartner.find(rows[k]); + if (it == storedPartner.end()) { + ++nFate[0]; + } else if (it->second == own[k]) { + ++nFate[1]; + } else if (it->second == crossed[k]) { + ++nFate[2]; + } else { + const auto p = legs.rawIteratorAt(it->second); + if (p.mcMotherId() < 0 || p.mcMotherPdg() != kGamma) { + ++nFate[5]; + } else { + const auto pIt = photonLegs.find(std::make_pair(static_cast(p.sourceEventUid()), p.mcMotherId())); + const bool complete = pIt != photonLegs.end() && pIt->second[0] >= 0 && pIt->second[1] >= 0; + ++nFate[complete ? 3 : 4]; + } + } + } + registry.fill(HIST("Mixed/Decision/hMatchedLegs"), type, qTrue, input, sGen ? 0.f : (sCross ? 1.f : 2.f), related ? 1.f : 0.f, + static_cast(nFate[0]), static_cast(nFate[1]), static_cast(nFate[2]), static_cast(nFate[3]), + static_cast(nFate[4]), static_cast(nFate[5])); + } + } + } + } + + // truth of a photon pair: 0 both genuine, 1 crossed, 2 other (at least one leg not from a true photon), for all candidates of a collision + template + void fillFourLegAll(TMixCollisions const& collisions, TMixLegs const& legs, const size_t nFit) + { + using Key3 = std::tuple; + std::map firstCand; + std::map> originalsOfColl; + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + const auto& c = vecV0Dedup[i]; + if (firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, static_cast(i)).second && c.v0ID >= 0 && i < nFit) { + originalsOfColl[c.colID].push_back(static_cast(i)); + } + } + std::set storedSet; + for (const auto& k : stored_fullv0Ids) { + storedSet.insert(Key3{k.collisionId, k.posId, k.eleId}); + } + auto truePhoton = [&](int64_t posRow, int64_t eleRow) { + const auto p = legs.rawIteratorAt(posRow); + const auto e = legs.rawIteratorAt(eleRow); + return p.mcMotherId() >= 0 && p.sourceEventUid() == e.sourceEventUid() && p.mcMotherId() == e.mcMotherId() && p.mcMotherPdg() == kGamma; + }; + for (const auto& [colId, orig] : originalsOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < orig.size(); ++i) { + for (size_t j = i + 1; j < orig.size(); ++j) { + const auto& a = vecV0Dedup[orig[i]]; + const auto& b = vecV0Dedup[orig[j]]; + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; + } + const auto& x1 = vecV0Dedup[itX1->second]; + const auto& x2 = vecV0Dedup[itX2->second]; + const auto ap = legs.rawIteratorAt(a.posID); + const auto bp = legs.rawIteratorAt(b.posID); + const bool distinctMothers = ap.sourceEventUid() != bp.sourceEventUid() || ap.mcMotherId() != bp.mcMotherId(); + const bool physO = truePhoton(a.posID, a.eleID) && truePhoton(b.posID, b.eleID) && distinctMothers; + const bool physX = truePhoton(a.posID, b.eleID) && truePhoton(b.posID, a.eleID) && distinctMothers; + const float truth = physO ? 0.f : (physX ? 1.f : 2.f); + const float qGen = physX ? qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV) : qInv(a.pxAtPV, a.pyAtPV, a.pzAtPV, b.pxAtPV, b.pyAtPV, b.pzAtPV); + if (qGen >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const bool storedO = storedSet.contains(Key3{colId, a.posID, a.eleID}) && storedSet.contains(Key3{colId, b.posID, b.eleID}); + const bool storedX = storedSet.contains(Key3{colId, a.posID, b.eleID}) && storedSet.contains(Key3{colId, b.posID, a.eleID}); + const float actual = storedO ? 0.f : (storedX ? 1.f : 2.f); + const int crossedOrigin = (static_cast(itX1->second) >= nFit ? 1 : 0) + (static_cast(itX2->second) >= nFit ? 1 : 0); + const int origOrigin = (ap.sourceV0Id() < 0 ? 1 : 0) + (bp.sourceV0Id() < 0 ? 1 : 0); + registry.fill(HIST("Mixed/Decision/hFourLegAll"), type, truth, actual, qGen, static_cast(crossedOrigin), static_cast(origOrigin)); + } + } + } + } + + template + void fillFourLegTruthAligned(TMixCollisions const& collisions, TMixLegs const& legs, const int64_t colId, const float type, const float truth, + const float isolated, const float actual, V0CandidateHelper const& a, V0CandidateHelper const& b, + V0CandidateHelper const& x1, V0CandidateHelper const& x2, const int groupSize) + { + const bool origTrue = truth < 0.5f; + const auto& t1 = origTrue ? a : x1; + const auto& t2 = origTrue ? b : x2; + const auto& w1 = origTrue ? x1 : a; + const auto& w2 = origTrue ? x2 : b; + // decision in truth terms: 0 genuine assignment, 1 wrong assignment, 2 other + auto inTruth = [&](float decision) { return decision > 1.5f ? 2.f : ((decision < 0.5f) == origTrue ? 0.f : 1.f); }; + const float choice = inTruth(isolated); + MixPairPhoton p1, p2; + setMixPairLegs(p1, legs.rawIteratorAt(t1.posID), legs.rawIteratorAt(t1.eleID)); + setMixPairLegs(p2, legs.rawIteratorAt(t2.posID), legs.rawIteratorAt(t2.eleID)); + const int relKind = mixRelKind(p1, p2); + const auto rel = static_cast(relKind); + const float rel2 = relKind > 0 ? 1.f : 0.f; + const float s0 = dedup.groupMatchingScoreThreshold.value; + const float qTrue = qInv(t1.pxAtPV, t1.pyAtPV, t1.pzAtPV, t2.pxAtPV, t2.pyAtPV, t2.pzAtPV); + const int nClipT = (t1.score >= s0 ? 1 : 0) + (t2.score >= s0 ? 1 : 0); + const int nClipW = (w1.score >= s0 ? 1 : 0) + (w2.score >= s0 ? 1 : 0); + auto angle = [](float cpa) { return std::acos(std::clamp(cpa, -1.f, 1.f)); }; + const float dTheta = 1e3f * ((angle(w1.cosPA) + angle(w2.cosPA)) - (angle(t1.cosPA) + angle(t2.cosPA))); // mrad + const float dPCA = (w1.pca + w2.pca) - (t1.pca + t2.pca); + const std::array rows{t1.posID, t1.eleID, t2.posID, t2.eleID}; + float fshMax = 0.f; + int nITS = 0; + for (const auto& r : rows) { + const auto l = legs.rawIteratorAt(r); + fshMax = std::max(fshMax, static_cast(l.tpcFractionSharedCls())); + nITS += l.hasITS() ? 1 : 0; + } + registry.fill(HIST("Mixed/Decision/hFourLegTruth"), type, truth, choice, rel, qTrue, static_cast(3 * nClipT + nClipW), dTheta, dPCA, fshMax); + { + const int origin = mixCommonOrigin(p1.posMC, p2.posMC); + auto rootClass = [](int pdg) { + switch (std::abs(pdg)) { + case 111: + return 1.f; // o2-linter: disable=pdg/explicit-code (diagnostic classes) + case 221: + return 2.f; // o2-linter: disable=pdg/explicit-code + case 223: + return 3.f; // o2-linter: disable=pdg/explicit-code + case 331: + return 4.f; // o2-linter: disable=pdg/explicit-code + case 113: + case 213: + case 313: + case 323: + return 5.f; // o2-linter: disable=pdg/explicit-code + case 310: + case 130: + case 311: + return 6.f; // o2-linter: disable=pdg/explicit-code + default: + return std::abs(pdg) > 1000 ? 7.f : 0.f; // o2-linter: disable=magic-number (baryons) + } + }; + registry.fill(HIST("Mixed/Decision/hFourLegOrigin"), type, truth, choice, rel, qTrue, static_cast(origin), origin > 0 ? rootClass(p1.posMC.rootPdg) : 0.f); + } + + // TPC leg proximity: the minimum distance of the two legs of one photon to the two legs of the other photon, at 17 radii in the TPC + constexpr int NRadii = 17; + auto positions = [&](int64_t row) { + std::array, NRadii> xyz{}; + std::array ok{}; + const auto tp0 = getTrackPar(legs.rawIteratorAt(row)); + for (int k = 0; k < NRadii; ++k) { + auto tp = tp0; + float x = 0.f; + ok[k] = tp.getXatLabR(85.f + 10.f * k, x, d_bz) && tp.propagateParamTo(x, d_bz); // o2-linter: disable=magic-number (TPC radii) + if (ok[k]) { + tp.getXYZGlo(xyz[k]); + } + } + return std::make_pair(xyz, ok); + }; + const auto pT1p = positions(t1.posID), pT1e = positions(t1.eleID), pT2p = positions(t2.posID), pT2e = positions(t2.eleID); + auto minDist = [&](auto const& u, auto const& v) { + float d = 999.f; + for (int k = 0; k < NRadii; ++k) { + if (u.second[k] && v.second[k]) { + d = std::min(d, std::hypot(u.first[k][0] - v.first[k][0], u.first[k][1] - v.first[k][1], u.first[k][2] - v.first[k][2])); + } + } + return d; + }; + const float dOS = std::min(minDist(pT1p, pT2e), minDist(pT2p, pT1e)); // legs that the wrong assignment pairs + const float dSS = std::min(minDist(pT1p, pT2p), minDist(pT1e, pT2e)); // control: same-sign legs + const float occ = collisions.rawIteratorAt(colId).occupancy(); + registry.fill(HIST("Mixed/Decision/hFourLegTrack"), type, truth, choice, rel2, std::log10(std::max(dOS, 1e-3f)), std::log10(std::max(dSS, 1e-3f)), + static_cast(nITS), occ); // o2-linter: disable=magic-number (log floor) + + auto mother = [&](int64_t row) { + const auto l = legs.rawIteratorAt(row); + return std::array{l.mcMotherPx(), l.mcMotherPy(), l.mcMotherPz()}; + }; + const auto m1 = mother(t1.posID), m2 = mother(t2.posID); + auto phi = [](float px, float py) { return std::atan2(py, px); }; + auto eta = [](float px, float py, float pz) { return std::asinh(pz / std::max(std::hypot(px, py), 1e-6f)); }; + const float uPhi = RecoDecay::constrainAngle(phi(m2[0], m2[1]) - phi(m1[0], m1[1]), -o2::constants::math::PI) > 0.f ? 1.f : -1.f; + const float uEta = eta(m2[0], m2[1], m2[2]) - eta(m1[0], m1[1], m1[2]) > 0.f ? 1.f : -1.f; + const float rPhi1 = uPhi * RecoDecay::constrainAngle(phi(t1.pxAtPV, t1.pyAtPV) - phi(m1[0], m1[1]), -o2::constants::math::PI); + const float rPhi2 = -uPhi * RecoDecay::constrainAngle(phi(t2.pxAtPV, t2.pyAtPV) - phi(m2[0], m2[1]), -o2::constants::math::PI); + const float rEta1 = uEta * (eta(t1.pxAtPV, t1.pyAtPV, t1.pzAtPV) - eta(m1[0], m1[1], m1[2])); + const float rEta2 = -uEta * (eta(t2.pxAtPV, t2.pyAtPV, t2.pzAtPV) - eta(m2[0], m2[1], m2[2])); + registry.fill(HIST("Mixed/Decision/hFourLegResid"), type, truth, choice, rel2, rPhi1, rPhi2, rEta1, rEta2); + + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + auto decide = [&](std::vector const& sc, float thr) { + const auto sol = solveConflictGroup(legMask, sc, thr, false); + return inTruth(sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f)); // o2-linter: disable=magic-number (subset masks) + }; + auto scores = [&](float w) { + return std::vector{getScoreV0(a.cosPA, a.pca, w), getScoreV0(b.cosPA, b.pca, w), getScoreV0(x1.cosPA, x1.pca, w), getScoreV0(x2.cosPA, x2.pca, w)}; + }; + const std::vector baseScores{a.score, b.score, x1.score, x2.score}; + registry.fill(HIST("Mixed/Decision/hFourLegAlt"), type, truth, rel2, choice, decide(scores(1.f), s0), decide(scores(0.f), s0), decide(baseScores, -1.f), + decide(scores(dedup.deduplicationScoreWeight.value), s0)); + + const auto col = collisions.rawIteratorAt(colId); + auto pointing = [&](V0CandidateHelper const& c) { + const float dx = c.svX - col.posX(), dy = c.svY - col.posY(), dz = c.svZ - col.posZ(); + const float dPhi = RecoDecay::constrainAngle(std::atan2(c.pyAtSV, c.pxAtSV) - std::atan2(dy, dx), -o2::constants::math::PI); + const float dLam = std::atan2(c.pzAtSV, std::hypot(c.pxAtSV, c.pyAtSV)) - std::atan2(dz, std::hypot(dx, dy)); + return std::make_pair(1e3f * dPhi, 1e3f * dLam); // mrad + }; + const auto pt1 = pointing(t1), pt2 = pointing(t2), pw1 = pointing(w1), pw2 = pointing(w2); + const float dPhiSum = (std::fabs(pw1.first) + std::fabs(pw2.first)) - (std::fabs(pt1.first) + std::fabs(pt2.first)); + const float dLamSum = (std::fabs(pw1.second) + std::fabs(pw2.second)) - (std::fabs(pt1.second) + std::fabs(pt2.second)); + registry.fill(HIST("Mixed/Decision/hFourLegPoint"), type, truth, choice, rel2, qTrue, dPhiSum, dLamSum, pt1.second, pt2.second, pt1.first, pt2.first); + + // log dump of the few cases where the isolated decision is wrong, but the actual decision is correct and genuine + if (type < 0.5f && origTrue && isolated > 0.5f && isolated < 1.5f && actual < 0.5f && mNAnomalyDump < 200) { // o2-linter: disable=magic-number (cap) + ++mNAnomalyDump; + LOGF(info, "[4leg-anom] col %ld group %d scores A %.4f B %.4f X1 %.4f X2 %.4f s0 %.2f q %.4f rel %d", colId, groupSize, a.score, b.score, x1.score, x2.score, s0, qTrue, relKind); + } + } + + template + void fillFourLegDecisions(TMixCollisions const& collisions, TMixLegs const& legs, const size_t nFit) + { + fillFourLegAll(collisions, legs, nFit); + using Key3 = std::tuple; + std::map firstCand; // first fit of a leg pair = its dedup candidate + std::map> originalsOfColl; + for (size_t i = 0; i < nFit; ++i) { + const auto& c = vecV0Dedup[i]; + if (firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, static_cast(i)).second && c.v0ID >= 0) { + originalsOfColl[c.colID].push_back(static_cast(i)); + } + } + std::set storedSet; + for (const auto& k : stored_fullv0Ids) { + storedSet.insert(Key3{k.collisionId, k.posId, k.eleId}); + } + auto truePhoton = [&](int64_t posRow, int64_t eleRow) { + const auto p = legs.rawIteratorAt(posRow); + const auto e = legs.rawIteratorAt(eleRow); + return p.mcMotherId() >= 0 && p.sourceEventUid() == e.sourceEventUid() && p.mcMotherId() == e.mcMotherId() && p.mcMotherPdg() == kGamma; + }; + const float s0 = dedup.groupMatchingScoreThreshold.value; + for (const auto& [colId, orig] : originalsOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < orig.size(); ++i) { + for (size_t j = i + 1; j < orig.size(); ++j) { + const auto& a = vecV0Dedup[orig[i]]; + const auto& b = vecV0Dedup[orig[j]]; + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; // not both assignments available + } + const auto& x1 = vecV0Dedup[itX1->second]; + const auto& x2 = vecV0Dedup[itX2->second]; + const float qO = qInv(a.pxAtPV, a.pyAtPV, a.pzAtPV, b.pxAtPV, b.pyAtPV, b.pzAtPV); + const float qX = qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV); + if (std::min(qO, qX) >= 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + // the isolated decision is the dedup objective on only these four candidates, the actual decision is the stored set after the dedup and the resolvers + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + const std::vector scores{a.score, b.score, x1.score, x2.score}; + const auto sol = solveConflictGroup(legMask, scores, s0, false); + const float isolated = sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f); // o2-linter: disable=magic-number (subset masks) + const bool storedO = storedSet.contains(Key3{colId, a.posID, a.eleID}) && storedSet.contains(Key3{colId, b.posID, b.eleID}); + const bool storedX = storedSet.contains(Key3{colId, a.posID, b.eleID}) && storedSet.contains(Key3{colId, b.posID, a.eleID}); + const float actual = storedO ? 0.f : (storedX ? 1.f : 2.f); + const auto ap = legs.rawIteratorAt(a.posID); + const auto bp = legs.rawIteratorAt(b.posID); + const bool distinctMothers = ap.sourceEventUid() != bp.sourceEventUid() || ap.mcMotherId() != bp.mcMotherId(); + const bool physO = truePhoton(a.posID, a.eleID) && truePhoton(b.posID, b.eleID) && distinctMothers; + const bool physX = truePhoton(a.posID, b.eleID) && truePhoton(b.posID, a.eleID) && distinctMothers; + const float truth = physO ? 0.f : (physX ? 1.f : 2.f); + const int groupSize = std::max({mGroupSizeOfCand[orig[i]], mGroupSizeOfCand[orig[j]], mGroupSizeOfCand[itX1->second], mGroupSizeOfCand[itX2->second]}); + // originals without an SVertexer V0 are fitted without the AP cut, the crossed pairings with it (asymmetric options) + const bool relaxedOriginal = ap.sourceV0Id() < 0 || bp.sourceV0Id() < 0; + registry.fill(HIST("Mixed/Decision/hFourLeg"), type, truth, isolated, actual, qO, qX, static_cast(std::min(groupSize, 15)), // o2-linter: disable=magic-number (last bin) + (x1.score + x2.score) - (a.score + b.score), relaxedOriginal ? 1.f : 0.f); + if (std::min(qO, qX) < 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + // what tips the 4-leg decision: score components, geometry and kinematics of the two conversions, MC relation + auto angle = [](float cpa) { return std::acos(std::clamp(cpa, -1.f, 1.f)); }; + const float dPCA = (x1.pca + x2.pca) - (a.pca + b.pca); + const float dAngle = (angle(x1.cosPA) + angle(x2.cosPA)) - (angle(a.cosPA) + angle(b.cosPA)); + const float dR = std::fabs(std::hypot(a.svX, a.svY) - std::hypot(b.svX, b.svY)); + const float dZ = std::fabs(a.svZ - b.svZ); + auto pLeg = [&](int64_t row) { + const auto l = legs.rawIteratorAt(row); + return std::fabs(1.f / l.signed1Pt()) * std::sqrt(1.f + l.tgl() * l.tgl()); + }; + auto asym = [](float p1, float p2) { return std::fabs(p1 - p2) / std::max(p1 + p2, 1e-6f); }; + const float sameSignSim = std::min(asym(pLeg(a.posID), pLeg(b.posID)), asym(pLeg(a.eleID), pLeg(b.eleID))); + MixPairPhoton pa, pb; + setMixPairLegs(pa, legs.rawIteratorAt(a.posID), legs.rawIteratorAt(a.eleID)); + setMixPairLegs(pb, legs.rawIteratorAt(b.posID), legs.rawIteratorAt(b.eleID)); + registry.fill(HIST("Mixed/Decision/hFourLegKin"), type, truth, isolated, dPCA, dAngle, dR, dZ, sameSignSim, mixRelated(pa, pb) ? 1.f : 0.f); + } + if (truth < 1.5f && std::min(qO, qX) < 0.05f) { // o2-linter: disable=magic-number (genuine assignment exists; diagnostic range) + fillFourLegTruthAligned(collisions, legs, colId, type, truth, isolated, actual, a, b, x1, x2, groupSize); + } + } + } + } + for (const auto& r : mPermuteResults) { + registry.fill(HIST("Mixed/Decision/hPermute"), collisions.rawIteratorAt(r.colID).isMixed() ? 1.f : 0.f, static_cast(std::min(r.nMembers, 30)), // o2-linter: disable=magic-number (last bin) + r.changed ? 1.f : 0.f, r.greedy ? 1.f : 0.f); + } + } + + // run 2: build the mixed event, fill the crossed pairings, and fill the diagnostics of the run-1 photons (pooled free legs) before the dedup and resolvers + template + void buildMixed(TMixCollisions const& collisions, TMixLegs const& legs) + { + const auto tDF = std::chrono::steady_clock::now(); + mRelaxedKeys.clear(); + mLegDcaCache.clear(); + // build the leg iterator cache for the current event + mLegIt.clear(); + mLegIt.reserve(legs.size()); + for (int64_t i = 0; i < static_cast(legs.size()); ++i) { + mLegIt.push_back(legs.rawIteratorAt(i)); + } + std::unordered_map> inputOfColl; // artificial event -> input photons (pair diagnostics) + auto crossedPrefilter = [&](auto const& pos, auto const& ele) { + if (std::fabs(pos.tgl() - ele.tgl()) > recovery.finderMaxDeltaTgl) { + return false; + } + const o2::track::TrackAuxPar cp(getTrackPar(pos), d_bz); + const o2::track::TrackAuxPar ce(getTrackPar(ele), d_bz); + return std::fabs(circleGap(cp, ce)) < recovery.finderMaxCircleGap; + }; + for (const auto& collision : collisions) { + const float type = collision.isMixed() ? 1.f : 0.f; + const auto t0 = std::chrono::steady_clock::now(); + registry.fill(HIST("Mixed/hEvents"), type); + std::vector posLegs, eleLegs; + for (int i = collision.firstLeg(); i < collision.firstLeg() + collision.nLegs(); ++i) { + (mLegIt[i].sign() > 0 ? posLegs : eleLegs).push_back(i); + } + std::unordered_map circleOf; + std::unordered_map eleOfPhoton; + std::unordered_map nEleOfSource; + std::vector> eleByTgl; + eleByTgl.reserve(eleLegs.size()); + for (const auto& ie : eleLegs) { + eleOfPhoton.emplace(mLegIt[ie].sourcePhotonUid(), ie); + nEleOfSource[mLegIt[ie].sourceEventUid()]++; + eleByTgl.emplace_back(mLegIt[ie].tgl(), ie); + } + std::sort(eleByTgl.begin(), eleByTgl.end()); + const bool tglWindow = mixed.prefilterCrossed && !mixed.uniformPairFit; + if (mixed.prefilterCrossed) { + for (const auto& i : posLegs) { + circleOf.emplace(i, o2::track::TrackAuxPar(getTrackPar(mLegIt[i]), d_bz)); + } + for (const auto& i : eleLegs) { + circleOf.emplace(i, o2::track::TrackAuxPar(getTrackPar(mLegIt[i]), d_bz)); + } + } + auto crossedPrefilterCached = [&](int64_t ip, int64_t ie) { + return std::fabs(mLegIt[ip].tgl() - mLegIt[ie].tgl()) <= recovery.finderMaxDeltaTgl && std::fabs(circleGap(circleOf.at(ip), circleOf.at(ie))) < recovery.finderMaxCircleGap; + }; + std::vector before; // crossed pairings of this artificial event, before the dedup and resolvers + for (const auto& ip : posLegs) { + const auto& pos = mLegIt[ip]; + if (const auto itE = eleOfPhoton.find(pos.sourcePhotonUid()); itE != eleOfPhoton.end()) { + const auto ie = itE->second; + const auto& ele = mLegIt[ie]; + { + MixPairPhoton p; + p.px = pos.sourcePx(); + p.py = pos.sourcePy(); + p.pz = pos.sourcePz(); + setMixPairLegs(p, pos, ele); + p.posRow = ip; + p.eleRow = ie; + p.vx = pos.sourceVx(); + p.vy = pos.sourceVy(); + p.vz = pos.sourceVz(); + before.push_back(p); + } + } + } + registry.fill(HIST("Mixed/hNPhotonsIn"), type, static_cast(before.size())); // run-1 photons (pooled free legs are single legs) + fillMixPairs(type, before); + inputOfColl[collision.globalIndex()] = before; + std::vector visit; // negative legs paired with this positive leg, in the original (row) order + for (const auto& ip : posLegs) { + const auto& pos = mLegIt[ip]; + visit.clear(); + if (tglWindow) { // crossed pairings are prefiltered by tgl, then by circle gap + const float tglP = pos.tgl(); + for (auto it = std::lower_bound(eleByTgl.begin(), eleByTgl.end(), std::make_pair(tglP - recovery.finderMaxDeltaTgl - 1e-4f, int64_t{-1})); it != eleByTgl.end() && it->first <= tglP + recovery.finderMaxDeltaTgl + 1e-4f; ++it) { + visit.push_back(it->second); + } + if (const auto itE = eleOfPhoton.find(pos.sourcePhotonUid()); itE != eleOfPhoton.end()) { + visit.push_back(itE->second); + } + std::sort(visit.begin(), visit.end()); + visit.erase(std::unique(visit.begin(), visit.end()), visit.end()); + // crossed pairings outside the window: counted in the cut flow as prefilter rejections (not split by MC in hCrossedFit) + const bool hasPartner = eleOfPhoton.contains(pos.sourcePhotonUid()); + int nOutside = mixed.skipSameSourceCrossed ? static_cast(eleLegs.size()) - nEleOfSource[pos.sourceEventUid()] : static_cast(eleLegs.size()) - (hasPartner ? 1 : 0); + for (const auto& ie : visit) { + const auto& ele = mLegIt[ie]; + if (ele.sourcePhotonUid() != pos.sourcePhotonUid() && !(mixed.skipSameSourceCrossed && pos.sourceEventUid() == ele.sourceEventUid())) { + nOutside--; + } + } + if (nOutside > 0) { + registry.fill(HIST("Mixed/hCandidates"), type, 2.f, static_cast(nOutside)); + } + } else { + visit.assign(eleLegs.begin(), eleLegs.end()); + } + for (const auto& ie : visit) { + const auto& ele = mLegIt[ie]; + const size_t nBefore = vecV0Dedup.size(); + const bool originalPairing = pos.sourcePhotonUid() == ele.sourcePhotonUid(); + if (!originalPairing && mixed.skipSameSourceCrossed && pos.sourceEventUid() == ele.sourceEventUid()) { + continue; // injection: same-source crossed pairings were decided by run 1 + } + if (!originalPairing && mixed.skipFreeFreeCrossed && pos.sourceV0Id() == -2 && ele.sourceV0Id() == -2) { // o2-linter: disable=magic-number (pooled free leg) + continue; // overlay: two free legs, rejected by run 1 + } + if (!originalPairing && mixed.freeFreeFinderOnly && pos.sourceV0Id() == -2 && ele.sourceV0Id() == -2 && pos.hasITS() && ele.hasITS()) { // o2-linter: disable=magic-number (pooled free leg) + continue; // overlay: two free legs with ITS, only an SVertexer V0 in run 1 + } + if (mixed.uniformPairFit) { + // the crossed pairings are fitted with the same options as the originals, but without the AP cut if they were not found by an SVertexer V0 + const bool finderLike = !(pos.hasITS() && ele.hasITS()); + registry.fill(HIST("Mixed/hCandidates"), type, originalPairing ? 0.f : 2.f); + if (mixed.prefilterCrossed && !crossedPrefilter(pos, ele)) { + continue; + } + if (!originalPairing) { + registry.fill(HIST("Mixed/hCandidates"), type, 3.f); + } + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + fitV0Candidate(collision, pos, ele, originalPairing ? ip : -1, false, + FitOptions{.nonSVertexer = finderLike, .relaxAP = finderLike, .purityGate = finderLike && recovery.runFinder.value, .pv = (originalPairing && mixed.refitOriginalsAtSourceVertex) ? &srcPv : nullptr}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, originalPairing ? 1.f : 4.f); + } + continue; + } + if (originalPairing) { // the original pairings are fitted with the same options as in run 1, but without the AP cut if they were not found by an SVertexer V0 + registry.fill(HIST("Mixed/hCandidates"), type, 0.f); + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + fitV0Candidate(collision, pos, ele, ip, false, + FitOptions{.nonSVertexer = pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value, .relaxAP = pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value, .pv = mixed.refitOriginalsAtSourceVertex ? &srcPv : nullptr}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, 1.f); + } + continue; + } + registry.fill(HIST("Mixed/hCandidates"), type, 2.f); + // crossed pairings: the same prefilter and the same leg-based fit options for every pairing (original pairings keep v0Id = row of the positive leg) + const bool twoPhotons = pos.mcMotherPdg() == kGamma && ele.mcMotherPdg() == kGamma && pos.mcMotherId() >= 0 && ele.mcMotherId() >= 0 && + (pos.sourceEventUid() != ele.sourceEventUid() || pos.mcMotherId() != ele.mcMotherId()); + const float qTwo = twoPhotons ? qInv(pos.mcMotherPx(), pos.mcMotherPy(), pos.mcMotherPz(), ele.mcMotherPx(), ele.mcMotherPy(), ele.mcMotherPz()) : 1.f; + const float genuineClose = (twoPhotons && qTwo < 0.05f) ? 1.f : 0.f; // o2-linter: disable=magic-number (diagnostic range) + const auto nITSx = static_cast((pos.hasITS() ? 1 : 0) + (ele.hasITS() ? 1 : 0)); + if (mixed.prefilterCrossed && !crossedPrefilterCached(ip, ie)) { + if (mixed.mixedDiagnostics) { + registry.fill(HIST("Mixed/Decision/hCrossedFit"), type, genuineClose, 1.f, nITSx, std::min(qTwo, 0.0499f)); + } + continue; + } + registry.fill(HIST("Mixed/hCandidates"), type, 3.f); + const bool anyFree = pos.sourceV0Id() == -2 || ele.sourceV0Id() == -2; // o2-linter: disable=magic-number (pooled free leg) + const bool freeLegPair = mixed.freeLegRun1Rules && anyFree; + const bool finderLikeFree = freeLegPair && !(pos.hasITS() && ele.hasITS()); + if (mixed.diagFreeLegFits && anyFree) { // trial fits with both option sets, candidates removed again + const bool finderLike = !(pos.hasITS() && ele.hasITS()); + auto trial = [&](FitOptions const& o) { + const size_t n0 = vecV0Dedup.size(); + fitV0Candidate(collision, pos, ele, -1, false, o); + // cppcheck-suppress knownConditionTrueFalse + if (vecV0Dedup.size() > n0) { + const auto k = keyOf(vecV0Dedup.back()); + pca_map.erase(k); + cospa_map.erase(k); + vecV0Dedup.pop_back(); + return 0; + } + return std::max(mLastFitReject, 2); + }; + const int lenient = trial(FitOptions{.relaxAP = mixed.crossedRelaxAP.value}); + const int run1 = trial(FitOptions{.nonSVertexer = finderLike, .purityGate = finderLike && recovery.runFinder.value}); + const auto nFree = static_cast((pos.sourceV0Id() == -2 ? 1 : 0) + (ele.sourceV0Id() == -2 ? 1 : 0)); // o2-linter: disable=magic-number (pooled free leg) + registry.fill(HIST("Mixed/Decision/hFreeLegFit"), type, genuineClose > 0.5f ? 0.f : 1.f, static_cast(std::min(lenient, 1999)), static_cast(std::min(run1, 1999)), nITSx, nFree); + } + const int mask = mixed.freeLegRuleMask.value; + const FitOptions freeOpt{.nonSVertexer = finderLikeFree && (mask & 4) != 0, .relaxAP = (mask & 1) == 0 && mixed.crossedRelaxAP.value, // o2-linter: disable=magic-number (bit mask) + .purityGate = finderLikeFree && (mask & 2) != 0 && recovery.runFinder.value, + .minPurity = mixed.freeLegMinPurity.value}; // o2-linter: disable=magic-number (bit mask) + fitV0Candidate(collision, pos, ele, -1, false, freeLegPair ? freeOpt : FitOptions{.relaxAP = mixed.crossedRelaxAP.value}); + if (vecV0Dedup.size() > nBefore) { + registry.fill(HIST("Mixed/hCandidates"), type, 4.f); + } + if (mixed.mixedDiagnostics) { + registry.fill(HIST("Mixed/Decision/hCrossedFit"), type, genuineClose, vecV0Dedup.size() > nBefore ? 0.f : static_cast(mLastFitReject), nITSx, std::min(qTwo, 0.0499f)); + } + } + } + registry.fill(HIST("Mixed/hTimeCandidates"), type, std::chrono::duration(std::chrono::steady_clock::now() - t0).count()); + } + + const size_t nFit = vecV0Dedup.size(); // candidates of the run-2 fit (the resolvers may append more) + switch (dedup.deduplicationMode.value) { + case V0DeduplicationMode::Pairwise: + selectPairwise(nullptr); + break; + case V0DeduplicationMode::KeepAll: + keepAll(); + break; + case V0DeduplicationMode::GroupMatching: + selectByConflictGroup(nullptr); + break; + default: + selectGreedy(legs.size(), nullptr); + break; + } + if (dedup.resolveSplitLegs) { + resolveSplitLegConflicts(legs, nullptr); + } + if (recovery.runSwapResolver) { + runSwapResolver(collisions, legs, nullptr); + } + if (mixed.mixedDiagnostics && dedup.deduplicationMode.value == static_cast(V0DeduplicationMode::GroupMatching)) { + fillFourLegDecisions(collisions, legs, nFit); + } + if (mixed.mixedDiagnostics) { + fillMatchedConfigs(collisions, legs); + } + + std::unordered_map> nStored; // artificial event -> (originals, crossed) + std::unordered_map> after; // artificial event -> stored photons + for (const auto& key : stored_fullv0Ids) { + const auto collision = collisions.rawIteratorAt(key.collisionId); + const auto pos = legs.rawIteratorAt(key.posId); + const auto ele = legs.rawIteratorAt(key.eleId); + const bool original = key.v0Id >= 0; + const float type = collision.isMixed() ? 1.f : 0.f; + const auto nWritten = v0photonskf.lastIndex(); + const SourcePV srcPv{pos.sourcePvX(), pos.sourcePvY(), pos.sourcePvZ(), pos.sourcePvCovXX(), pos.sourcePvCovXY(), pos.sourcePvCovXZ(), + pos.sourcePvCovYY(), pos.sourcePvCovYZ(), pos.sourcePvCovZZ(), pos.sourcePvChi2(), pos.sourcePvNContrib()}; + const bool finderLike = mixed.uniformPairFit && !(pos.hasITS() && ele.hasITS()); // uniformPairFit: options by the legs, as in the candidate fit + fitV0Candidate(collision, pos, ele, key.v0Id, true, + FitOptions{.nonSVertexer = mixed.uniformPairFit ? finderLike : (original && pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value), + .relaxAP = mRelaxedKeys.contains(key) || (mixed.uniformPairFit ? finderLike : (original ? (pos.sourceV0Id() < 0 && !recovery.finderUniformFit.value) : mixed.crossedRelaxAP.value)), + .purityGate = finderLike && recovery.runFinder.value, + .pv = (original && mixed.refitOriginalsAtSourceVertex) ? &srcPv : nullptr}); + if (v0photonskf.lastIndex() == nWritten) { + continue; // rejected by the ML cut (not a dedup candidate then either) + } + const auto& c = vecV0Dedup.back(); + nStored[key.collisionId][original ? 0 : 1]++; + registry.fill(HIST("Mixed/hStored"), type, original ? 0.f : 1.f); + if (original) { + const float pt1 = std::hypot(pos.sourcePx(), pos.sourcePy()); + const float pt2 = std::hypot(c.pxAtPV, c.pyAtPV); + registry.fill(HIST("Mixed/hOrigRelDPt"), type, pt2 / pt1 - 1.f); + registry.fill(HIST("Mixed/hOrigDPhi"), type, RecoDecay::constrainAngle(RecoDecay::phi(c.pxAtPV, c.pyAtPV) - RecoDecay::phi(pos.sourcePx(), pos.sourcePy()), -o2::constants::math::PI)); + registry.fill(HIST("Mixed/hOrigDEta"), type, RecoDecay::eta(std::array{c.pxAtPV, c.pyAtPV, c.pzAtPV}) - RecoDecay::eta(std::array{pos.sourcePx(), pos.sourcePy(), pos.sourcePz()})); + registry.fill(HIST("Mixed/hOrigExact"), type, (c.pxAtPV == pos.sourcePx() && c.pyAtPV == pos.sourcePy() && c.pzAtPV == pos.sourcePz()) ? 1.f : 0.f); + } else if (pos.sourceV0Id() != -2 && ele.sourceV0Id() != -2) { // -2: pooled free leg, no run-1 photon + registry.fill(HIST("Mixed/hCrossedSourceQ"), type, qInv(pos.sourcePx(), pos.sourcePy(), pos.sourcePz(), ele.sourcePx(), ele.sourcePy(), ele.sourcePz())); + registry.fill(HIST("Mixed/hCrossedSourceDist"), type, std::hypot(pos.sourceVx() - ele.sourceVx(), pos.sourceVy() - ele.sourceVy(), pos.sourceVz() - ele.sourceVz())); + registry.fill(HIST("Mixed/hCrossedSourceEvents"), type, pos.sourceEventUid() == ele.sourceEventUid() ? 0.f : 1.f); + } + float truth = 4.f; // no label + if (pos.mcMotherId() >= 0 && ele.mcMotherId() >= 0) { + const bool truePhoton = pos.sourceEventUid() == ele.sourceEventUid() && pos.mcMotherId() == ele.mcMotherId() && pos.mcMotherPdg() == kGamma; + truth = (original ? 0.f : 2.f) + (truePhoton ? 0.f : 1.f); + } + registry.fill(HIST("Mixed/hStoredTruth"), type, truth); + MixPairPhoton stored; + stored.px = c.pxAtPV; + stored.py = c.pyAtPV; + stored.pz = c.pzAtPV; + setMixPairLegs(stored, pos, ele); + stored.posRow = key.posId; + stored.eleRow = key.eleId; + stored.original = original; + if (const auto itGroup = mGroupOfKey.find(key); itGroup != mGroupOfKey.end()) { + stored.group = itGroup->second; + } + stored.vx = c.svX; + stored.vy = c.svY; + stored.vz = c.svZ; + after[key.collisionId].push_back(stored); + } + for (const auto& collision : collisions) { + const float type = collision.isMixed() ? 1.f : 0.f; + const auto it = nStored.find(collision.globalIndex()); + const std::array n = it == nStored.end() ? std::array{0, 0} : it->second; + const auto itIn = inputOfColl.find(collision.globalIndex()); + const int nIn = itIn == inputOfColl.end() ? 0 : static_cast(itIn->second.size()); // run-1 photons (not the pooled free legs) + registry.fill(HIST("Mixed/hNStoredVsIn"), type, nIn, n[0] + n[1]); + registry.fill(HIST("Mixed/hNOriginalLost"), type, nIn - n[0]); + registry.fill(HIST("Mixed/hNCrossedStored"), type, n[1]); + if (const auto itA = after.find(collision.globalIndex()); mixed.mixedDiagnostics && itA != after.end()) { + fillMixPairs(type, itA->second); + fillSameSourcePairs(type, itA->second); + } + } + + if (mixed.mixedDiagnostics) { // MC diagnostics of the pairings (cost on data) + // alternative pairings of the input pairs: passed the run-2 fit (candidate) / stored + std::set> candLegs, storedLegs; + for (const auto& c : vecV0Dedup) { + candLegs.emplace(c.colID, c.posID, c.eleID); + } + for (const auto& k : stored_fullv0Ids) { + storedLegs.emplace(k.collisionId, k.posId, k.eleId); + } + for (const auto& [colId, input] : inputOfColl) { + fillMixFakeClasses(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 0.f, colId, input, candLegs); + } + for (const auto& [colId, out] : after) { + fillMixFakeClasses(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 1.f, colId, out, candLegs); + } + // swap-symmetric counting (data-computable): the decision between the two pairings of an ambiguous 4-leg configuration no longer + // enters the pair count, in the same and the mixed event alike + { + std::map, std::array> candMom; // first fit of a leg pair = its dedup candidate + for (const auto& c : vecV0Dedup) { + candMom.emplace(std::make_tuple(static_cast(c.colID), static_cast(c.posID), static_cast(c.eleID)), std::array{c.pxAtPV, c.pyAtPV, c.pzAtPV}); + } + for (const auto& [colId, out] : after) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < out.size(); ++i) { + for (size_t j = i + 1; j < out.size(); ++j) { + const auto& a = out[i]; + const auto& b = out[j]; + if (skipSameSourcePair(type, a, b)) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + const auto itX1 = candMom.find(std::make_tuple(static_cast(colId), a.posRow, b.eleRow)); + const auto itX2 = candMom.find(std::make_tuple(static_cast(colId), b.posRow, a.eleRow)); + const bool ambiguous = itX1 != candMom.end() && itX2 != candMom.end(); + const float qX = ambiguous ? qInv(itX1->second[0], itX1->second[1], itX1->second[2], itX2->second[0], itX2->second[1], itX2->second[2]) : 999.f; + if (q >= 0.5f && (!ambiguous || qX >= 0.5f)) { // o2-linter: disable=magic-number (histogram range) + continue; + } + const float cls = mixPairClass(a, b); + if (ambiguous) { + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, q, 0.5f); + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, qX, 0.5f); + } else { + registry.fill(HIST("Mixed/Pairs/hQSym"), type, cls, q); + } + } + } + } + } + std::map, float> candScore; // first fit of a leg pair = its dedup candidate + for (const auto& c : vecV0Dedup) { + candScore.emplace(std::make_tuple(static_cast(c.colID), static_cast(c.posID), static_cast(c.eleID)), c.score); + } + for (const auto& [colId, input] : inputOfColl) { + fillMixSwapBrems(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 0.f, colId, input, candScore, legs); + } + for (const auto& [colId, out] : after) { + fillMixSwapBrems(collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f, 1.f, colId, out, candScore, legs); + } + for (const auto& [colId, input] : inputOfColl) { + const float type = collisions.rawIteratorAt(colId).isMixed() ? 1.f : 0.f; + for (size_t i = 0; i < input.size(); ++i) { + for (size_t j = i + 1; j < input.size(); ++j) { + const auto& a = input[i]; + const auto& b = input[j]; + const int truth = mixPairTruth(a, b); + if (truth != 1 && truth != 2) { + continue; + } + const float q = qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const auto alt1 = std::make_tuple(colId, a.posRow, b.eleRow); + const auto alt2 = std::make_tuple(colId, b.posRow, a.eleRow); + const int nAlt = static_cast(candLegs.contains(alt1)) + static_cast(candLegs.contains(alt2)); + int outcome = 2; + if (storedLegs.contains({colId, a.posRow, a.eleRow}) && storedLegs.contains({colId, b.posRow, b.eleRow})) { + outcome = 0; + } else if (storedLegs.contains(alt1) && storedLegs.contains(alt2)) { + outcome = 1; + } + if (truth == 1) { + registry.fill(HIST("Mixed/Pairs/hAltTrueTrue"), type, static_cast(3 * nAlt + outcome), q); + } else { + registry.fill(HIST("Mixed/Pairs/hAltSwap"), type, static_cast(3 * nAlt + outcome), q); + } + } + } + } + } + + pca_map.clear(); + cospa_map.clear(); + nv0_map.clear(); + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + vecV0Dedup.clear(); + registry.fill(HIST("Mixed/hTimeDF"), std::chrono::duration(std::chrono::steady_clock::now() - tDF).count()); + } + o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > static_cast(0); using FilteredV0s = o2::soa::Filtered; @@ -2221,6 +4055,80 @@ struct PhotonConversionBuilder { } } + template + void fillRun1FourLeg(TMCParticles const& mcparticles, DedupDiag const& diag, std::vector const& cls, std::vector const& motherPos, + std::set const& storedSet, std::map& nCandsOfTrack) + { + using Key3 = std::tuple; + const int nCand = static_cast(diag.snapshot.size()); + std::map firstCand; // first fit of a leg pair = its candidate + std::map, int> trueOfPhoton; // (collision, MC photon) -> first true candidate + for (int i = 0; i < nCand; ++i) { + const auto& c = diag.snapshot[i]; + firstCand.emplace(Key3{c.colID, c.posID, c.eleID}, i); + if (cls[i] == kV0True) { + trueOfPhoton.emplace(std::make_pair(static_cast(c.colID), motherPos[i]), i); + } + } + std::map>> photonsOfColl; + for (const auto& [k, i] : trueOfPhoton) { + photonsOfColl[k.first].emplace_back(k.second, i); + } + auto stored = [&](int i) { return storedSet.contains(keyOf(diag.snapshot[i])); }; + const std::vector legMask{0b0011, 0b1100, 0b1001, 0b0110}; + for (const auto& [colId, phs] : photonsOfColl) { + for (size_t i = 0; i < phs.size(); ++i) { + for (size_t j = i + 1; j < phs.size(); ++j) { + const auto& a = diag.snapshot[phs[i].second]; + const auto& b = diag.snapshot[phs[j].second]; + if (a.posID == b.posID || a.eleID == b.eleID) { + continue; // two MC photons sharing a track (should not happen for true candidates) + } + const auto itX1 = firstCand.find(Key3{colId, a.posID, b.eleID}); + const auto itX2 = firstCand.find(Key3{colId, b.posID, a.eleID}); + if (itX1 == firstCand.end() || itX2 == firstCand.end()) { + continue; + } + const auto mA = mcparticles.rawIteratorAt(phs[i].first); + const auto mB = mcparticles.rawIteratorAt(phs[j].first); + const float qTrue = qInv(mA.px(), mA.py(), mA.pz(), mB.px(), mB.py(), mB.pz()); + if (qTrue >= 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + continue; + } + const int iA = phs[i].second, iB = phs[j].second, iX1 = itX1->second, iX2 = itX2->second; + const int nT = (stored(iA) ? 1 : 0) + (stored(iB) ? 1 : 0), nX = (stored(iX1) ? 1 : 0) + (stored(iX2) ? 1 : 0); + float outcome = 5.f; + if (nT == 2 && nX == 0) { + outcome = 0.f; + } else if (nT == 0 && nX == 2) { + outcome = 1.f; + } else if (nT == 1 && nX == 0) { + outcome = 2.f; + } else if (nT == 0 && nX == 1) { + outcome = 3.f; + } else if (nT == 0 && nX == 0) { + outcome = 4.f; + } + const std::vector scores{a.score, b.score, diag.snapshot[iX1].score, diag.snapshot[iX2].score}; + const auto sol = solveConflictGroup(legMask, scores, dedup.groupMatchingScoreThreshold.value, false); + const float isolated = sol.bestMask == 0b0011 ? 0.f : (sol.bestMask == 0b1100 ? 1.f : 2.f); // o2-linter: disable=magic-number (subset masks) + const int nThird = nCandsOfTrack[a.posID] + nCandsOfTrack[a.eleID] + nCandsOfTrack[b.posID] + nCandsOfTrack[b.eleID] - 8; // o2-linter: disable=magic-number (each leg is in 2 of the 4) + bool related = false; + if (mA.has_mothers() && mB.has_mothers()) { + const auto gA = mcparticles.rawIteratorAt(mA.mothersIds()[0]); + const auto gB = mcparticles.rawIteratorAt(mB.mothersIds()[0]); + related = gA.globalIndex() == gB.globalIndex() || std::abs(gA.pdgCode()) == kElectron || std::abs(gB.pdgCode()) == kElectron; + } + const bool finder = a.v0ID < 0 || b.v0ID < 0; + registry.fill(HIST("MCDedup/FourLeg/hRun1"), qTrue, outcome, isolated, static_cast(std::clamp(nThird, 0, 5)), related ? 1.f : 0.f, finder ? 1.f : 0.f); // o2-linter: disable=magic-number (last bin) + const int crossedOrigin = (diag.snapshot[iX1].v0ID < 0 ? 1 : 0) + (diag.snapshot[iX2].v0ID < 0 ? 1 : 0); // finder off: non-SVertexer = swap resolver + const int trueOrigin = (a.v0ID < 0 ? 1 : 0) + (b.v0ID < 0 ? 1 : 0); + registry.fill(HIST("MCDedup/FourLeg/hRun1Origin"), qTrue, outcome, static_cast(crossedOrigin), static_cast(trueOrigin), nThird > 0 ? 1.f : 0.f, related ? 1.f : 0.f); + } + } + } + } + template void fillDedupTruthDiagnostics(TCollisions const& collisions, TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) { @@ -2262,10 +4170,25 @@ struct PhotonConversionBuilder { } } + std::map nCandsOfTrack; + for (int i = 0; i < nCand; ++i) { + nCandsOfTrack[diag.snapshot[i].posID]++; + nCandsOfTrack[diag.snapshot[i].eleID]++; + } + auto fillFateProps = [&](int ci, float fateBin) { + const auto& c = diag.snapshot[ci]; + const int nITS = (tracks.rawIteratorAt(c.posID).hasITS() ? 1 : 0) + (tracks.rawIteratorAt(c.eleID).hasITS() ? 1 : 0); + const int nOther = nCandsOfTrack[c.posID] + nCandsOfTrack[c.eleID] - 2; + registry.fill(HIST("MCDedup/Photons/hFateProps"), fateBin, std::hypot(c.pxAtPV, c.pyAtPV), std::hypot(c.svX, c.svY), static_cast(nITS), + std::acos(std::clamp(c.cosPA, -1.f, 1.f)), c.pca, static_cast(std::min(nOther, 10))); // o2-linter: disable=magic-number (last bin) + }; + std::map> blockersOfPhoton; for (const auto& [mid, f] : fate) { if (f.alive) { registry.fill(HIST("MCDedup/Photons/hFate"), 0.f); + const auto itStored = std::find_if(f.cands.begin(), f.cands.end(), [&](int ci) { return storedSet.contains(key[ci]); }); + fillFateProps(itStored != f.cands.end() ? *itStored : f.best, 0.f); continue; } auto& blockers = blockersOfPhoton[mid]; @@ -2304,6 +4227,10 @@ struct PhotonConversionBuilder { } // hVictimBlockerClass below counts one entry per blocker and a victim can have two. registry.fill(HIST("MCDedup/Photons/hFate"), blockers.empty() ? 2.f : 1.f); + const bool lostToCross = std::any_of(blockers.begin(), blockers.end(), [&](int w) { return cls[w] == kV0CrossLegFake; }); + if (f.best >= 0) { + fillFateProps(f.best, blockers.empty() ? 3.f : (lostToCross ? 1.f : 2.f)); + } if (blockers.empty()) { registry.fill(HIST("MCDedup/Photons/hBlockerClass"), 3.f); // o2-linter: disable=magic-number (bin 3 = no blocker) @@ -2321,6 +4248,49 @@ struct PhotonConversionBuilder { } } + fillRun1FourLeg(mcparticles, diag, cls, motherPos, storedSet, nCandsOfTrack); + + { + std::map, int> storedOfTrack; // (collision, track) -> stored candidate + for (int i = 0; i < nCand; ++i) { + if (storedSet.contains(key[i])) { + const auto& c = diag.snapshot[i]; + storedOfTrack[{c.colID, c.posID}] = i; + storedOfTrack[{c.colID, c.eleID}] = i; + } + } + const float s0 = dedup.groupMatchingScoreThreshold.value; + auto contrib = [&](float sc) { return std::max(s0 - sc, 1e-3f); }; // o2-linter: disable=magic-number (objective floor) + for (const auto& [mid, f] : fate) { + if (f.best < 0) { + continue; + } + const auto& t = diag.snapshot[f.best]; + const float pt = std::hypot(t.pxAtPV, t.pyAtPV); + if (f.alive) { + registry.fill(HIST("MCDedup/HalfSwap/hSource"), 0.f, t.score, 0.f, 0.f, pt); + continue; + } + const auto ip = storedOfTrack.find({t.colID, t.posID}); + const auto ie = storedOfTrack.find({t.colID, t.eleID}); + const bool hp = ip != storedOfTrack.end(), he = ie != storedOfTrack.end(); + float cat = 3.f; + if (hp && he) { + cat = ip->second != ie->second ? 1.f : 4.f; + } else if (hp || he) { + cat = 2.f; + } + float dObj = 0.f, dRaw = 0.f; + if (cat == 1.f) { + const auto& w1 = diag.snapshot[ip->second]; + const auto& w2 = diag.snapshot[ie->second]; + dObj = contrib(w1.score) + contrib(w2.score) - contrib(t.score); + dRaw = 0.5f * (w1.score + w2.score) - t.score; + } + registry.fill(HIST("MCDedup/HalfSwap/hSource"), cat, t.score, std::clamp(dObj, -0.499f, 0.499f), std::clamp(dRaw, -0.999f, 0.999f), pt); + } + } + if (dedup.cfgExtendedDiag) { fillCopyDiagnostics(collisions, mcparticles, diag, fate); } @@ -2434,9 +4404,199 @@ struct PhotonConversionBuilder { } } } + + { + struct Elig { + int64_t pos = -1, ele = -1; + int nIts = 0; + float vx = 0.f, vy = 0.f; + }; + std::map elig; + for (const auto& track : tracks) { + if (track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + const int64_t mid = getPhotonMotherId(track, mcparticles); + if (mid < 0) { + continue; + } + auto& e = elig[mid]; + if (track.sign() > 0) { + e.pos = track.globalIndex(); + } else { + e.ele = track.globalIndex(); + } + if (track.hasITS()) { + e.nIts++; + } + const auto mc = track.template mcParticle_as(); + e.vx = mc.vx(); + e.vy = mc.vy(); + } + std::map crossedStoredOfPhoton; + std::map, std::pair> crossedOfPair; // (offered, stored) + std::map, std::vector> crossedOriginOfPair; // stored crossed: 0 SVertexer, 1 finder, 2 swap resolver + std::map, std::vector> crossedIndexOfPair; // stored crossed: candidate index + std::map, const SwapRecord*> swapRecordOf; + for (const auto& r : diag.swaps) { + swapRecordOf[std::minmax(r.keyI, r.keyJ)] = &r; + } + for (int i = 0; i < nCand; ++i) { + if (cls[i] != kV0CrossLegFake) { + continue; + } + auto& cp = crossedOfPair[std::minmax(motherPos[i], motherEle[i])]; + cp.first++; + if (storedSet.contains(key[i])) { + cp.second++; + crossedOriginOfPair[std::minmax(motherPos[i], motherEle[i])].push_back(key[i].v0Id >= 0 ? 0 : (diag.finderKeys.contains(key[i]) ? 1 : 2)); + crossedIndexOfPair[std::minmax(motherPos[i], motherEle[i])].push_back(i); + crossedStoredOfPhoton[motherPos[i]]++; + crossedStoredOfPhoton[motherEle[i]]++; + } + } + std::map> eligByMcColl; + for (const auto& [mid, e] : elig) { + if (e.pos >= 0 && e.ele >= 0) { + eligByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); + } + } + auto history = [&](int64_t mid, int& detail) { // run-1 history of one true photon, see MCDedup/Inventory/hSwapOrigin + detail = -10; + if (fate.contains(mid)) { + return 0; + } + const auto& e = elig.at(mid); + const auto it = diag.trueLegTrace.find({e.pos, e.ele}); + const TrueLegTrace t = it == diag.trueLegTrace.end() ? TrueLegTrace{} : it->second; + if (t.svLine > 0) { + detail = t.svLine; + return 1; + } + if (t.svLine == 0) { + return 6; // o2-linter: disable=magic-number (history codes) + } + detail = t.finderStep; + if (e.nIts == 2 || t.finderStep == -8) { // o2-linter: disable=magic-number (both legs with ITS) + detail = -8; // o2-linter: disable=magic-number (trace code) + return 2; + } + if (t.finderStep == FinderStep::kFindCollision) { + detail = t.finderLine; + return t.finderLine > 0 ? 5 : 6; // o2-linter: disable=magic-number (history codes) + } + return t.finderStep >= -1 ? 4 : 3; // o2-linter: disable=magic-number (history codes) + }; + auto photonFate = [&](int64_t mid) { // 0 no true candidate, 1 candidate lost, 2 stored, 3 lost but a leg in a stored crossed candidate + const auto it = fate.find(mid); + const bool inCrossed = crossedStoredOfPhoton.contains(mid); + if (it == fate.end()) { + return inCrossed ? 3 : 0; + } + if (it->second.alive) { + return 2; + } + return inCrossed ? 3 : 1; + }; + for (const auto& [mcCol, mids] : eligByMcColl) { + std::vector closest(mids.size(), 0.0999f); + for (size_t a = 0; a < mids.size(); ++a) { + const auto ga = mcparticles.iteratorAt(mids[a]); + for (size_t b = a + 1; b < mids.size(); ++b) { + const auto gb = mcparticles.iteratorAt(mids[b]); + const float qTrue = qInv(ga.px(), ga.py(), ga.pz(), gb.px(), gb.py(), gb.pz()); + closest[a] = std::min(closest[a], qTrue); + closest[b] = std::min(closest[b], qTrue); + if (qTrue >= 0.1f) { + continue; + } + const float e1 = ga.p(), e2 = gb.p(); + const float asym = (e1 + e2) > 0.f ? std::fabs(e1 - e2) / (e1 + e2) : 0.f; + const float kt = 0.5f * std::hypot(ga.px() + gb.px(), ga.py() + gb.py()); + const int fa = photonFate(mids[a]), fb = photonFate(mids[b]); + const auto itx = crossedOfPair.find(std::minmax(mids[a], mids[b])); + const int nCrossOff = itx == crossedOfPair.end() ? 0 : std::min(itx->second.first, 2); + const int nCrossSt = itx == crossedOfPair.end() ? 0 : itx->second.second; + const int nStoredTrue = (fa == 2 ? 1 : 0) + (fb == 2 ? 1 : 0); + int pairFate = 5; + if (nStoredTrue == 2) { + pairFate = 0; + } else if (nStoredTrue == 1 && nCrossSt == 0) { + pairFate = 1; + } else if (nStoredTrue == 0 && nCrossSt == 0) { + pairFate = 2; + } else if (nStoredTrue == 0 && nCrossSt == 2) { + pairFate = 3; + } else if (nCrossSt == 1) { + pairFate = 4; + } + const int nTrueCand = (fate.contains(mids[a]) ? 1 : 0) + (fate.contains(mids[b]) ? 1 : 0); + registry.fill(HIST("MCDedup/Inventory/hPair"), qTrue, std::min(asym, 0.999f), std::min(kt, 1.499f), static_cast(pairFate), static_cast(nTrueCand), static_cast(nCrossOff)); + int detA = -10, detB = -10; + int hA = history(mids[a], detA), hB = history(mids[b], detB); + if (qTrue < 0.02f) { // o2-linter: disable=magic-number (close pairs) + if (hA > 0) { + registry.fill(HIST("MCDedup/Inventory/hNeverDetail"), static_cast(pairFate), static_cast(hA), static_cast(detA)); + } + if (hB > 0) { + registry.fill(HIST("MCDedup/Inventory/hNeverDetail"), static_cast(pairFate), static_cast(hB), static_cast(detB)); + } + } + if (pairFate == 3 || pairFate == 4) { // o2-linter: disable=magic-number (swap, one crossed stored) + auto o = crossedOriginOfPair.at(std::minmax(mids[a], mids[b])); + const int o1 = std::min(o[0], o.size() > 1 ? o[1] : o[0]), o2 = std::max(o[0], o.size() > 1 ? o[1] : o[0]); + static constexpr std::array, 3> OriginCode{{{0, 1, 3}, {1, 2, 4}, {3, 4, 5}}}; + if (hA < hB) { + std::swap(hA, hB); + } + registry.fill(HIST("MCDedup/Inventory/hSwapOrigin"), qTrue, pairFate == 3 ? 0.f : 1.f, static_cast(nTrueCand), static_cast(OriginCode[o1][o2]), static_cast(hA), static_cast(hB)); + } + if (pairFate == 3) { // o2-linter: disable=magic-number (full swap): what the swap resolver did with the two stored crossed photons + const auto& ix = crossedIndexOfPair.at(std::minmax(mids[a], mids[b])); + const auto& x1 = diag.snapshot[ix[0]]; + const auto& x2 = diag.snapshot[ix.size() > 1 ? ix[1] : ix[0]]; + int outcome = 8; // o2-linter: disable=magic-number (outcome codes) + float gain = -1.999f; + int line = 0; + const float qRec = qInv(x1.pxAtPV, x1.pyAtPV, x1.pzAtPV, x2.pxAtPV, x2.pyAtPV, x2.pzAtPV); + if (x1.colID != x2.colID) { + outcome = 0; + } else if (!recovery.runSwapResolver.value) { + outcome = 8; // o2-linter: disable=magic-number (outcome codes) + } else if (qRec > recovery.swapMaxQ) { + outcome = 1; + } else if (std::hypot(x1.svX - x2.svX, x1.svY - x2.svY, x1.svZ - x2.svZ) > recovery.swapMaxConvDist) { + outcome = 2; + } else if (const auto it = swapRecordOf.find(std::minmax(keyOf(x1), keyOf(x2))); it != swapRecordOf.end()) { + const auto& r = *it->second; + if (r.complete) { + gain = std::clamp(-r.gain, -1.999f, 1.999f); // crossed (stored) minus true (alternative) + outcome = r.gain > 0.f ? 7 : 6; // o2-linter: disable=magic-number (outcome codes) + } else if ((r.fail & 3) != 0) { // o2-linter: disable=magic-number (bit mask) + outcome = 5; // o2-linter: disable=magic-number (outcome codes) + } else if ((r.fail & 48) != 0) { // o2-linter: disable=magic-number (bit mask) + outcome = 4; // o2-linter: disable=magic-number (outcome codes) + line = r.line; + } else { + outcome = 3; + } + } + registry.fill(HIST("MCDedup/Inventory/hSwapLoss"), qTrue, static_cast(nTrueCand), static_cast(outcome), gain); + if (outcome == 4 && qTrue < 0.02f) { // o2-linter: disable=magic-number (close pairs) + registry.fill(HIST("MCDedup/Inventory/hSwapLossCut"), static_cast(nTrueCand), static_cast(line)); + } + } + } + } + for (size_t a = 0; a < mids.size(); ++a) { + const auto ga = mcparticles.iteratorAt(mids[a]); + const auto& e = elig.at(mids[a]); + registry.fill(HIST("MCDedup/Inventory/hPhoton"), std::min(ga.p(), 1.999f), std::min(std::hypot(e.vx, e.vy), 89.9f), static_cast(e.nIts), static_cast(photonFate(mids[a])), closest[a]); + } + } + } } - // MC, cfgExtendedDiag: class of the stored photons by source, stored photon pairs at low q, outcome of the resolver tests template void fillExtendedDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) { @@ -2448,8 +4608,10 @@ struct PhotonConversionBuilder { std::array p{}; }; std::map candOfKey; + std::set> candLegs; // (collision, pos, ele) of all dedup candidates for (const auto& c : diag.snapshot) { candOfKey[keyOf(c)] = &c; + candLegs.emplace(c.colID, c.posID, c.eleID); } std::map> storedOfColl; for (const auto& key : diag.stored) { @@ -2478,6 +4640,8 @@ struct PhotonConversionBuilder { cat = 0; } else if (a.cls == kV0CrossLegFake && b.cls == kV0CrossLegFake && a.mPos == b.mEle && a.mEle == b.mPos) { cat = 1; + const int nTrue = static_cast(candLegs.contains({col, a.key.posId, b.key.eleId})) + static_cast(candLegs.contains({col, b.key.posId, a.key.eleId})); + registry.fill(HIST("MCDedup/Stored/hSwapTrueCands"), static_cast(nTrue), q); } registry.fill(HIST("MCDedup/Stored/hPairClass"), static_cast(cat), q); } @@ -2491,9 +4655,17 @@ struct PhotonConversionBuilder { isTrue[k] = classifyV0Truth(tracks.rawIteratorAt(legs[k].first), tracks.rawIteratorAt(legs[k].second), mcparticles, mPos, mEle) == kV0True ? 1 : 0; } registry.fill(HIST("MCDedup/Swap/hCategory"), static_cast(3 * (isTrue[0] + isTrue[1]) + isTrue[2] + isTrue[3]), s.swapped ? 1.f : 0.f, s.q); + int outcome = 3; // o2-linter: disable=magic-number (outcome codes) + if (s.swapped) { + outcome = 4; // o2-linter: disable=magic-number (outcome codes) + } else if (!s.complete) { + outcome = (s.fail & 3) != 0 ? 2 : ((s.fail & 48) != 0 ? 1 : 0); // o2-linter: disable=magic-number (bit mask) + } + registry.fill(HIST("MCDedup/Swap/hResolverTruth"), static_cast(isTrue[0] + isTrue[1]), static_cast(isTrue[2] + isTrue[3]), static_cast(outcome), s.complete ? std::clamp(s.gain, -1.999f, 1.999f) : -1.999f, std::min(s.q, 0.0999f)); } } + // ===== process functions ===== void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) { build(collisions, v0s, tracks, bcs); @@ -2524,6 +4696,45 @@ struct PhotonConversionBuilder { } PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); + using MixCollisionsIn = std::conditional_t; + using MixLegsIn = std::conditional_t; + std::vector().rawIteratorAt(0))> mLegIt; // run 2: one iterator per PCMMixLegs row of the DF (buildMixed) + void processMixed(MixCollisionsIn const& collisions, MixLegsIn const& legs, MyBCs const& bcs) + { + if constexpr (kMixedInstance) { + if (collisions.size() == 0 || bcs.size() == 0) { + return; + } + const auto bc = bcs.begin(); // B field / material of this DF's run + initCCDB(bc); + updateCCDB(bc); + buildMixed(collisions, legs); + } else { + LOG(fatal) << "processMixed only in photon-conversion-builder-mixed (o2-analysis-em-photon-conversion-builder-mixed)"; + } + } + PROCESS_SWITCH(PhotonConversionBuilder, processMixed, "builder run 2 of the co-reconstructed mixed event (input from photon-mixed-event-producer), own instance only", false); + + void processMixedDerived(MixCollisionsIn const& collisions, MixLegsIn const& legs) + { + if constexpr (kMixedInstance) { + if (collisions.size() == 0) { + return; + } + const auto event = collisions.begin(); // B field / material of this DF's run + const auto* grp = ccdb->template getForTimeStamp("GLO/Config/GRPMagField", event.timestamp()); + if (grp == nullptr) { + LOG(fatal) << "processMixedDerived: no GRPMagField for timestamp " << event.timestamp(); + } + initCCDBFor(event.runNumber(), event.timestamp(), *grp); + updateCCDBAt(event.timestamp()); + buildMixed(collisions, legs); + } else { + LOG(fatal) << "processMixedDerived only in photon-conversion-builder-mixed (o2-analysis-em-photon-conversion-builder-mixed)"; + } + } + PROCESS_SWITCH(PhotonConversionBuilder, processMixedDerived, "builder run 2 on derived data (input from photon-mixed-event-producer processDerived), own instance only", false); + // void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) // { // build(collisions, v0s, tracks, bcs); diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx new file mode 100644 index 00000000000..5514fb86c31 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderMixed.cxx @@ -0,0 +1,28 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file photonconversionbuilderMixed.cxx +/// \brief builder run 2 of the mixed event: rebuilds the photons of the artificial events of photon-mixed-event-producer +/// from their legs (set processRec = false, processMixed = true) +/// \author Stefanie Mrozinski + +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" + +#include +#include +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-mixed"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx index 0346617358c..1c873c197ed 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx @@ -23,5 +23,5 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return o2::framework::WorkflowSpec{ - adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx index fa490de325d..9c89b99bd94 100644 --- a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx @@ -21,6 +21,7 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Utils/V0DedupUtilities.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" @@ -68,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +108,9 @@ using MyV0Photon = MyV0Photons::iterator; using MyMCV0Legs = soa::Join; using MyMCV0Leg = MyMCV0Legs::iterator; +// output of builder run 2 (co-reconstructed mixed event, processCoReco): photons of the artificial events of PCMMixCollisions +using MyV0PhotonsMix = soa::Join; + static constexpr float kMinMagnitude = 1e-12f; static constexpr float kMinCosine = 1e-12f; static constexpr float kMinSigma = 1e-9f; @@ -272,6 +277,16 @@ struct Photonhbt { // m(e+e-) of the two leg-swapped combinations std::array mee{999.f, 999.f}; float meeOverQ{999.f}; + // geometry of the two leg-swapped combinations (a's e+ with b's e-, b's e+ with a's e-): would the swapped legs make a photon? + // helix circles from the conversion point and the leg momentum; gap = |c+ - c-| - (r+ + r-) (0 for a conversion), |tgl+ - tgl-|, + // z+ - z- of the two legs at the touching point of the circles (vertex-subtracted z, so mixed pairs are treated like same pairs) + std::array gap{999.f, 999.f}; + std::array dTgl{999.f, 999.f}; + std::array dz{999.f, 999.f}; + int bestCombo{-1}; // the more photon-like combination (smaller |gap| / gapMax + |dz| / dzMax) + float gapBest{999.f}, dTglBest{999.f}, dzBest{999.f}; + bool geomPhotonLike{false}; // a combination inside the windows crosspair_group.cfgCrossMax{Gap,DTgl,Dz} + float qSwap{-1.f}; // q_inv of the two leg-swapped "photons" (a's e+ with b's e-, b's e+ with a's e-): the q this pair would have if the legs had been swapped }; struct TruthGamma { @@ -360,6 +375,12 @@ struct Photonhbt { Configurable cfgFillLegSimilaritySparse{"cfgFillLegSimilaritySparse", true, "book/fill the like-sign leg similarity sparse (duplicate diagnostic: DeltaEta, DeltaPhi, pT ratio, dNsigma_e vs qinv)"}; Configurable cfgFillLegSimilarityMixSparse{"cfgFillLegSimilarityMixSparse", false, "book/fill the MIXED-EVENT leg similarity sparse (kinematic baseline for the SE-ME duplicate excess; needs cfgFillLegSimilaritySparse)"}; Configurable cfgFillR1R2Sparse{"cfgFillR1R2Sparse", true, "book/fill the FullRange (R1, R2, q_inv) sparse: fix one conversion radius, see where the partner sits, per q_inv"}; + // pair-class QA on the co-reconstructed mixed event (processPairClassMix) + Configurable cfgPairClassMaxQ{"cfgPairClassMaxQ", 0.5f, "pair-class QA: max q_inv of the pairs (GeV/c)"}; + Configurable cfgPairClassMixDepth{"cfgPairClassMixDepth", 10, "pair-class QA, standard mixed event: same-event artificial events kept per pool"}; + Configurable cfgPairClassMixZBinWidth{"cfgPairClassMixZBinWidth", 2.f, "pair-class QA, standard mixed event: pool z-vertex bin width (cm)"}; + Configurable cfgPairClassMixCentBinWidth{"cfgPairClassMixCentBinWidth", 10.f, "pair-class QA, standard mixed event: pool centrality (FT0C) bin width (%)"}; + Configurable cfgPairClassMaxSharedFrac{"cfgPairClassMaxSharedFrac", 0.4f, "pair-class QA, clean flag: all four legs with TPC shared-cluster fraction below this"}; } qaflags; // ─── HBT analysis mode ─────────────────────────────────────────────────────────── @@ -419,6 +440,10 @@ struct Photonhbt { ConfigurableAxis confCentBins{"confCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 999.f}, "Mixing bins - centrality"}; ConfigurableAxis confEPBinsBins{"confEPBinsBins", {16, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}, "Mixing bins - EP angle"}; ConfigurableAxis confOccupancyBins{"confOccupancyBins", {VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + // processCoReco (output of builder run 2): which mixed event fills Pair/mix + Configurable cfgCoRecoMixMode{"cfgCoRecoMixMode", 0, "processCoReco: 0 co-reconstructed mixed event (pairs inside the mixed artificial events), 1 standard mixed event built here from the same-event artificial events (pools as for processAnalysis, no BC gap), for old vs new on the same photons"}; + Configurable cfgCoRecoSkipOneSource{"cfgCoRecoSkipOneSource", true, "processCoReco: skip pairs of a mixed artificial event whose four legs all come from one source event (same-event pairs by construction)"}; + Configurable cfgCoRecoMC{"cfgCoRecoMC", false, "processCoReco: MC diagnostics from the leg labels carried in PCMMixLegs (truth-type CFs as processMC, pair-class decomposition)"}; } mixing; // ─── Centrality slection ───────────────────────────────────────────────── @@ -500,6 +525,10 @@ struct Photonhbt { Configurable cfgDoCrossPairCut{"cfgDoCrossPairCut", false, "reject pairs with a photon-like crossed combination; applied to SE AND ME"}; Configurable cfgCrossMaxMeeRatio{"cfgCrossMaxMeeRatio", 0.2f, "veto: min(m_ee^cross)/q_inv below this; genuine pairs sit near 0.5, leg swaps near 0"}; Configurable cfgCrossMaxQinvQA{"cfgCrossMaxQinvQA", 0.3f, "fill crossed QA sparses only below this qinv"}; + Configurable cfgDoCrossGeomCut{"cfgDoCrossGeomCut", false, "reject pairs whose leg-swapped combination is geometrically a photon: helix circles of the swapped legs touching (|gap| < cfgCrossMaxGap), |tgl+ - tgl-| < cfgCrossMaxDTgl and the legs' z at the touching point within cfgCrossMaxDz (vertex-subtracted); applied to SE AND ME. A stored leg-swapped pair (two crossed fakes of two collinear photons) always has such a combination (the true photons), a genuine pair only when the two photons are collinear and convert on one line"}; + Configurable cfgCrossMaxGap{"cfgCrossMaxGap", 1.5f, "geometric crossed veto: max |circle gap| of the swapped legs (cm)"}; + Configurable cfgCrossMaxDTgl{"cfgCrossMaxDTgl", 0.1f, "geometric crossed veto: max |tgl+ - tgl-| of the swapped legs"}; + Configurable cfgCrossMaxDz{"cfgCrossMaxDz", 2.f, "geometric crossed veto: max |z+ - z-| of the swapped legs at the touching point (cm)"}; } crosspair; struct : ConfigurableGroup { @@ -542,6 +571,7 @@ struct Photonhbt { Configurable cfgMaxPCA{"cfgMaxPCA", 3.0, "max distance between 2 legs"}; Configurable cfgMaxChi2KF{"cfgMaxChi2KF", 1e+10, "max chi2/ndf with KF"}; Configurable cfgRejectV0OnITSIB{"cfgRejectV0OnITSIB", true, "reject V0s on ITSib"}; + Configurable cfgMinNCrossedRowsOverFindable{"cfgMinNCrossedRowsOverFindable", 0.8f, "min. TPC crossed rows / findable clusters of the legs (was hard-coded 0.8)"}; Configurable cfgDisableITSOnlyTrack{"cfgDisableITSOnlyTrack", false, "disable ITS-only tracks"}; Configurable cfgDisableTPCOnlyTrack{"cfgDisableTPCOnlyTrack", false, "disable TPC-only tracks"}; Configurable cfgMinNClusterTPC{"cfgMinNClusterTPC", 70, "min ncluster TPC"}; @@ -674,9 +704,19 @@ struct Photonhbt { void init(InitContext& context) { isMC = context.mOptions.get("processMC"); + const bool doCoReco = context.mOptions.get("processCoReco"); + if (doCoReco && (isMC || context.mOptions.get("processAnalysis"))) { + LOGF(fatal, "processCoReco fills the same Pair/same and Pair/mix histograms as processAnalysis / processMC: run it in its own photonhbt instance"); + } + if (doCoReco) { + isMC = mixing.cfgCoRecoMC.value; + } if (context.mOptions.get("processMCAOD")) { addMCAODHistograms(); } + if (context.mOptions.get("processPairClassMix")) { + addPairClassMixHistograms(); + } if (pairsep.cfgCloseThrCm.value.size() != 4) { // o2-linter: disable=magic-number (four thresholds) LOGF(fatal, "cfgCloseThrCm must contain exactly four thresholds, got %zu", pairsep.cfgCloseThrCm.value.size()); @@ -698,7 +738,7 @@ struct Photonhbt { (ggpaircuts.cfgDoEllipseCut.value || ggpaircuts.cfgDoRCut.value || ggpaircuts.cfgDoZCut.value || ggpaircuts.cfgMinDRCosOA.value > 0.f || ggpaircuts.cfgMaxAsymmetry.value >= 0.f || pairsep.cfgDoPairMergeCut.value || - crosspair.cfgDoCrossPairCut.value || cfgDoPhotonClassPairCut.value); + crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value || cfgDoPhotonClassPairCut.value); LOGF(info, "photonhbt QA level %d -> pairQA %d, singlePhotonQA %d, legPairQA %d, pairSep %d, dRdZ sparse %d, R1R2 sparse %d, pairSep afterCut %d", qaLevel, mQa.pairQa, mQa.singlePhotonQa, mQa.legPairQa, mQa.pairSepQa, mQa.fillDRDZSparse, mQa.fillR1R2Sparse, mQa.pairSepAfterCut); mRunNumber = 0; @@ -713,6 +753,9 @@ struct Photonhbt { mPhotonClassSelA = pairutil::buildV0PhotonClassSelection(photonclassA); mPhotonClassSelB = pairutil::buildV0PhotonClassSelection(photonclassB); addhistograms(); + if (doCoReco) { + addCoRecoHistograms(); + } std::random_device seedGen; engine = std::mt19937(seedGen()); dist01 = std::uniform_int_distribution(0, 1); @@ -750,9 +793,10 @@ struct Photonhbt { LOGF(info, "photonhbt pair merging cut: %s, D_pair > %.2f, N_close < %d (0 = off); control window %.2f < q_inv < %.2f", pairsep.cfgDoPairMergeCut.value ? "ON" : "off", pairsep.cfgDPairCut.value, pairsep.cfgNCloseCut.value, pairsep.cfgCondQinvMin.value, pairsep.cfgCondQinvMax.value); - LOGF(info, "photonhbt: crossed-pair QA %s, cut %s (min(m_ee)/q_inv > %.2f)", + LOGF(info, "photonhbt: crossed-pair QA %s, cut %s (min(m_ee)/q_inv > %.2f), geometric cut %s (|gap| < %.2f cm, |dtgl| < %.3f, |dz| < %.2f cm)", crosspair.cfgDoCrossPairQA.value ? "on" : "off", - crosspair.cfgDoCrossPairCut.value ? "ON" : "off", crosspair.cfgCrossMaxMeeRatio.value); + crosspair.cfgDoCrossPairCut.value ? "ON" : "off", crosspair.cfgCrossMaxMeeRatio.value, + crosspair.cfgDoCrossGeomCut.value ? "ON" : "off", crosspair.cfgCrossMaxGap.value, crosspair.cfgCrossMaxDTgl.value, crosspair.cfgCrossMaxDz.value); LOGF(info, "photonhbt: q_inv gates -- QA < %.3f, FullRange < %.3f, PairSep < %.3f", qaflags.cfgMaxQinvForQA.value, qaflags.cfgMaxQinvForFullRange.value, pairsep.cfgPairSepMaxQinv.value); @@ -825,7 +869,7 @@ struct Photonhbt { fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); - fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.cfgMinNCrossedRowsOverFindable); fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); @@ -1113,13 +1157,17 @@ struct Photonhbt { void addCrossPairHistograms() { - if (!(crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value)) { + if (!(crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value)) { return; } for (const auto& sm : {std::string("Pair/same/CrossPair/"), std::string("Pair/mix/CrossPair/")}) { fRegistryCF.add((sm + "hSparse_MeeRatio_dR_Qinv").c_str(), "crossed-hypothesis QA;min(m_{ee}^{cross})/q_{inv};|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", kTHnSparseF, {{100, 0.f, 2.f}, {80, 0.f, 80.f}, {60, 0.f, 0.3f}}, true); + fRegistryCF.add((sm + "hSparse_CrossGeom_Qinv").c_str(), + "geometry of the more photon-like leg-swapped combination;circle gap (cm);|z_{+} - z_{-}| at the touching point (cm);|tgl_{+} - tgl_{-}|;q_{inv} (GeV/c)", + kTHnSparseF, {{100, -5.f, 5.f}, {60, 0.f, 6.f}, {50, 0.f, 0.5f}, {30, 0.f, 0.15f}}, true); + fRegistryCF.add((sm + "hSwapTemplate").c_str(), "swap template: q_inv the pair would have with swapped legs;q_{inv} of the pair (GeV/c);q_{inv} of the leg-swapped pairing (GeV/c);0 swapped combination not photon-like, 1 photon-like (in the geometric window)", kTH3F, {{60, 0.f, 0.3f}, {60, 0.f, 0.3f}, {2, -0.5f, 1.5f}}, true); auto h = fRegistryCF.add((sm + "hVetoCounter").c_str(), "crossed-pair veto;;pairs", kTH1D, {{2, -0.5f, 1.5f}}, true); h->GetXaxis()->SetBinLabel(1, "evaluated"); h->GetXaxis()->SetBinLabel(2, "vetoed"); @@ -1244,6 +1292,8 @@ struct Photonhbt { kTHnSparseF, {axisPsiPair, axisPhiV, axisQinv, axisTruthType, axisIsTruePhoton}, true); const AxisSpec axisMeeRatio{100, 0.f, 2.f, "min(m_{ee}^{cross})/q_{inv}"}; + fRegistryPairMC.add("Pair/same/MC/hSparse_SwapTemplate_Type", "swap template per truth type;q_{inv} of the pair (GeV/c);q_{inv} of the leg-swapped pairing (GeV/c);swapped combination photon-like;truth type", kTHnSparseF, {{60, 0.f, 0.3f}, {60, 0.f, 0.3f}, {2, -0.5f, 1.5f}, {7, -0.5f, 6.5f}}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_CrossGeom_Qinv_Type", "geometry of the more photon-like leg-swapped combination per truth type;circle gap (cm);|z_{+} - z_{-}| at the touching point (cm);q_{inv} (GeV/c);truth type", kTHnSparseF, {{100, -5.f, 5.f}, {60, 0.f, 6.f}, {30, 0.f, 0.15f}, {7, -0.5f, 6.5f}}, true); fRegistryPairMC.add("Pair/same/MC/hSparse_MeeRatio_Qinv_Type", "crossed-pair hypothesis per truth type;" "min(m_{ee}^{cross})/q_{inv};q_{inv} (GeV/c);truth type", @@ -1281,6 +1331,8 @@ struct Photonhbt { const AxisSpec axisFakeSubtype{3, -0.5f, 2.5f, "0=ordinary, 1=swap doublet, 2=partial swap"}; fRegistryPairMC.add("Pair/same/MC/hFakeSubtypeVsQinv", "fake pair subtype vs q_{inv};q_{inv} (GeV/c);subtype", kTH2D, {axisQinv, axisFakeSubtype}, true); + fRegistryPairMC.add("Pair/same/MC/hQrecVsQtrueVsKt", "distinct true photons;q_{inv}^{true} (GeV/c);q_{inv}^{rec} (GeV/c);k_{T}^{rec} (GeV/c)", kTH3F, + {{300, 0.f, 0.3f}, {300, 0.f, 0.3f}, {std::vector{0.1, 0.2, 0.3, 0.4, 0.6, 1.0, 2.0}}}, false); fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_truePairs", "true reco pairs (TrueTrueDistinct+SamePhoton+Pi0);" "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", @@ -1376,6 +1428,13 @@ struct Photonhbt { fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted", "denominator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected", "numerator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); + const AxisSpec axSpPt{std::vector{0.1, 0.125, 0.15, 0.175, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0}, "p_{T}^{true} (GeV/c)"}; + const AxisSpec axSpR{90, 0.f, 180.f, "R_{conv}^{true} (cm)"}; + const AxisSpec axSpEta{16, -0.8f, 0.8f, "#eta^{true}"}; + for (const auto& stage : {"converted", "built", "selected"}) { + fRegistryTruthMC.add((std::string("MC/SinglePhotonEff/hPtREta_") + stage).c_str(), (std::string("true primary converted photons, ") + stage + ";p_{T}^{true} (GeV/c);R_{conv}^{true} (cm);#eta^{true}").c_str(), kTH3F, {axSpPt, axSpR, axSpEta}, true); + } + // ─── Stage summary fRegistryTruthMC.add("MC/TruthAO2D/hStage_vs_kT", "efficiency waterfall;k_{T} (GeV/c);stage (0=converted,1=all4legs,2=bothBuilt,3=bothSel)", kTH2D, {axisKt, AxisSpec{4, -0.5f, 3.5f, "stage"}}, true); fRegistryTruthMC.add("MC/TruthAO2D/hStageConsistency", "stage consistency (expect all at 0);N(V0 built but legs not found);counts", kTH1D, {AxisSpec{20, -0.5f, 19.5f, "N_{bad}"}}, true); @@ -1899,15 +1958,70 @@ struct Photonhbt { if (qinv > 0.f) { c.meeOverQ = std::min(c.mee[0], c.mee[1]) / qinv; } + { // swap template: q_inv of the two leg-swapped combinations (the leg-level combinatorics of a crossed pair) + const auto f1 = legVec(a, 0) + legVec(b, 1); + const auto f2 = legVec(b, 0) + legVec(a, 1); + const auto dq = f1 - f2; + c.qSwap = static_cast(std::sqrt(std::max(0., -dq.M2()))); + } + // geometry: helix circle of leg i (cm) from the conversion point and the leg momentum, as legHelixAt + auto circle = [&](PhotonWithLegs const& p, int i, float& xc, float& yc, float& r) { + r = p.fLegPt[i] / (0.3f * std::fabs(mBzT)) * kCmPerM; // o2-linter: disable=magic-number (0.3 GeV/c per T m) + const float sgn = (mBzT >= 0.f ? 1.f : -1.f) * (i == 0 ? 1.f : -1.f); + xc = p.fVx + sgn * r * std::sin(p.fLegPhi[i]); + yc = p.fVy - sgn * r * std::cos(p.fLegPhi[i]); + }; + // z of leg i at the point of its circle closest to (xT, yT): signed arc along the motion from the conversion point, local z + auto zAt = [&](PhotonWithLegs const& p, int i, float xc, float yc, float r, float xT, float yT) { + const float phiStart = std::atan2(p.fVy - yc, p.fVx - xc); + const float phiT = std::atan2(yT - yc, xT - xc); + const bool ccw = std::cos(p.fLegPhi[i]) * (-(p.fVy - yc)) + std::sin(p.fLegPhi[i]) * (p.fVx - xc) > 0.f; // momentum along the counterclockwise tangent + const float dphi = RecoDecay::constrainAngle(phiT - phiStart, -o2::constants::math::PI); + return (p.fVz - p.fVtxZ) + r * (ccw ? dphi : -dphi) * std::sinh(p.fLegEta[i]); + }; + const float gapMax = std::max(crosspair.cfgCrossMaxGap.value, kMinSigma), dzMax = std::max(crosspair.cfgCrossMaxDz.value, kMinSigma); + float bestScore = 999.f; + if (std::fabs(mBzT) > kMinSigma) { + for (int combo = 0; combo < 2; ++combo) { // o2-linter: disable=magic-number (the two leg-swap combinations) + PhotonWithLegs const& pPos = (combo == 0) ? a : b; + PhotonWithLegs const& pEle = (combo == 0) ? b : a; + float xP = 0.f, yP = 0.f, rP = 0.f, xE = 0.f, yE = 0.f, rE = 0.f; + circle(pPos, 0, xP, yP, rP); + circle(pEle, 1, xE, yE, rE); + const float dC = std::hypot(xE - xP, yE - yP); + if (!(dC > kMinSigma) || !(rP + rE > kMinSigma)) { + continue; + } + c.gap[combo] = dC - (rP + rE); + c.dTgl[combo] = std::fabs(std::sinh(pPos.fLegEta[0]) - std::sinh(pEle.fLegEta[1])); + const float w = rP / (rP + rE); + const float xT = xP + w * (xE - xP), yT = yP + w * (yE - yP); // touching point of the circles + c.dz[combo] = zAt(pPos, 0, xP, yP, rP, xT, yT) - zAt(pEle, 1, xE, yE, rE, xT, yT); + const float score = std::fabs(c.gap[combo]) / gapMax + std::fabs(c.dz[combo]) / dzMax; + if (score < bestScore) { + bestScore = score; + c.bestCombo = combo; + c.gapBest = c.gap[combo]; + c.dTglBest = c.dTgl[combo]; + c.dzBest = c.dz[combo]; + } + if (std::fabs(c.gap[combo]) < crosspair.cfgCrossMaxGap.value && c.dTgl[combo] < crosspair.cfgCrossMaxDTgl.value && std::fabs(c.dz[combo]) < crosspair.cfgCrossMaxDz.value) { + c.geomPhotonLike = true; + } + } + } return c; } [[nodiscard]] inline bool passCrossPairVeto(CrossObs const& c) const { - if (!crosspair.cfgDoCrossPairCut.value) { - return true; + if (crosspair.cfgDoCrossPairCut.value && c.meeOverQ <= crosspair.cfgCrossMaxMeeRatio.value) { + return false; + } + if (crosspair.cfgDoCrossGeomCut.value && c.geomPhotonLike) { + return false; } - return c.meeOverQ > crosspair.cfgCrossMaxMeeRatio.value; + return true; } template @@ -1915,10 +2029,16 @@ struct Photonhbt { { constexpr auto dir = (ev_id == 0) ? "Pair/same/CrossPair/" : "Pair/mix/CrossPair/"; fRegistryCF.fill(HIST(dir) + HIST("hVetoCounter"), vetoed ? 1.0 : 0.0); + if (c.qSwap >= 0.f) { + fRegistryCF.fill(HIST(dir) + HIST("hSwapTemplate"), qinv, c.qSwap, c.geomPhotonLike ? 1.f : 0.f); + } if (qinv > crosspair.cfgCrossMaxQinvQA.value) { return; } fRegistryCF.fill(HIST(dir) + HIST("hSparse_MeeRatio_dR_Qinv"), c.meeOverQ, deltaR, qinv); + if (c.bestCombo >= 0) { + fRegistryCF.fill(HIST(dir) + HIST("hSparse_CrossGeom_Qinv"), c.gapBest, std::fabs(c.dzBest), c.dTglBest, qinv); + } } /*************************************************/ @@ -2737,7 +2857,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); @@ -2837,7 +2957,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(g1, g2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); @@ -2988,7 +3108,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); @@ -3044,6 +3164,13 @@ struct Photonhbt { } } + if (mc1.sameMother && mc1.isTruePhoton && mc2.sameMother && mc2.isTruePhoton && mc1.motherId != mc2.motherId) { + const auto& t1 = mcParticles.iteratorAt(mc1.motherId); + const auto& t2 = mcParticles.iteratorAt(mc2.motherId); + const ROOT::Math::PtEtaPhiMVector vt1(t1.pt(), t1.eta(), t1.phi(), 0.), vt2(t2.pt(), t2.eta(), t2.phi(), 0.); + const float qTrue = static_cast(std::sqrt(std::max(0., -(vt1 - vt2).M2()))); + fRegistryPairMC.fill(HIST("Pair/same/MC/hQrecVsQtrueVsKt"), qTrue, obs.qinv, obs.kt); + } fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g1.psipair()), g1.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc1.sameMother && mc1.isTruePhoton) ? 1.f : 0.f); fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g2.psipair()), g2.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc2.sameMother && mc2.isTruePhoton) ? 1.f : 0.f); if (truthType == PairTruthType::FakeFake || truthType == PairTruthType::TrueFake) { @@ -3055,6 +3182,12 @@ struct Photonhbt { if (crossMC.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_MeeRatio_Qinv_Type"), crossMC.meeOverQ, obs.qinv, static_cast(static_cast(truthType))); } + if (crossMC.bestCombo >= 0) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_CrossGeom_Qinv_Type"), crossMC.gapBest, std::fabs(crossMC.dzBest), obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.qSwap >= 0.f) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_SwapTemplate_Type"), obs.qinv, crossMC.qSwap, crossMC.geomPhotonLike ? 1.f : 0.f, static_cast(static_cast(truthType))); + } switch (truthType) { case PairTruthType::TrueTrueDistinct: @@ -3153,7 +3286,7 @@ struct Photonhbt { if (isInsideEllipse(obs.deta, obs.dphi)) { continue; } - if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { const auto cross = computeCrossObs(g1, g2, obs.qinv); const bool vetoed = !passCrossPairVeto(cross); fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); @@ -3400,6 +3533,18 @@ struct Photonhbt { fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStageConsistency"), static_cast(nBad)); } + // single-photon efficiency: every true converted primary photon of this collision, built (V0 of its own legs) / selected (HBT cut) + for (const auto& tg : trueGammas) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_converted"), tg.pt, tg.rTrue, tg.eta); + const auto it = gammaRecoMap.find(tg.id); + if (it != gammaRecoMap.end() && it->second.hasV0) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_built"), tg.pt, tg.rTrue, tg.eta); + if (it->second.passesCut) { + fRegistryTruthMC.fill(HIST("MC/SinglePhotonEff/hPtREta_selected"), tg.pt, tg.rTrue, tg.eta); + } + } + } + for (const auto& tg : trueGammas) { const bool posFound = legIdsThisCollision.contains(tg.posId) > 0; const bool negFound = legIdsThisCollision.contains(tg.negId) > 0; @@ -4204,6 +4349,1110 @@ struct Photonhbt { } PROCESS_SWITCH(Photonhbt, processMCAOD, "truth/SVertexer pair QA directly on AO2Ds", false); + + static constexpr int kPcGamma = 22, kPcElectron = 11; + struct PcLeg { // MC of one leg (ids within the DF of the source event), as the builder's MixPairPhoton::LegMC + int64_t evt = -1; + int pid = -1, mid = -1, gmid = -1, pdg = 0, mpdg = 0, gpdg = 0, trk = -1, anc = -1, ggid = -1, root = -1, ggpdg = 0, rootPdg = 0; + float sharedFrac = 0.f; + float vx = 0.f, vy = 0.f, vz = 0.f; // MC production vertex = true conversion point (valid for pid >= 0) + float px = 0.f, py = 0.f, pz = 0.f; // reconstructed leg momentum at the fitted vertex, before the mass constraint (V0LegsMix) + float mpx = 0.f, mpy = 0.f, mpz = 0.f; // MC momentum of the mother (the true photon for a conversion leg) + bool its = false; // leg has ITS hits + bool free = false; // pooled free leg (PCMMixLegs sourceV0Id -2, overlay mixed event) + int64_t srcPhoton = -1; // run-1 photon the leg came from (PCMMixLegs sourcePhotonUid; own uid for a free leg) + }; + struct PcPhoton { + float px = 0.f, py = 0.f, pz = 0.f; + float vx = 0.f, vy = 0.f, vz = 0.f; // reconstructed conversion point + bool original = true; // run-1 pairing (false: crossed pairing built in run 2) + bool svertexer = true; // run-1 photon was an SVertexer V0 (false: built by the finder) + float chi2 = 0.f, dcaxy = 0.f, dcaz = 0.f, cospa = 1.f; // KF chi2/ndf, DCA to the PV (cm), cos of the pointing angle to the PV + float chi2Vtx = -1.f, chi2PV = -1.f, psipair = 0.f; // KF chi2/ndf of the vertex fit alone, chi2 to the PV, psi_pair (processPairClassMix) + int nFree = 0; // legs that were pooled free legs (PCMMixLegs sourceV0Id -2, overlay): 0, 1, 2 + PcLeg pos, ele; + }; + static inline std::map>> mPcPools; // standard mixed event: (z bin, centrality bin) -> last cfgMixDepth same-event artificial events + + void addPairClassMixHistograms() + { + const int nq = std::max(1, static_cast(std::lround(qaflags.cfgPairClassMaxQ.value * 1000.f))); + const AxisSpec axSample{4, -0.5f, 3.5f, "0 same event, 1 standard mixed event, 2 co-reconstructed mixed event, 3 co-reco ME, one source event"}; + const AxisSpec axClass{21, -0.5f, 20.5f, "pair class: 13 true distinct, 14 full swap, 4 half swap, 1-3 duplicates, 16 bremsstrahlung (both true), 5 bremsstrahlung with a fake, 18 photons of the same mother (pi0/eta, both true), 19 swap of two such photons, 20 same mother with a fake, 17 related swap, 15 no label, 6-12 other fakes"}; + const AxisSpec axQ{nq, 0.f, qaflags.cfgPairClassMaxQ.value, "#it{q}_{inv} (GeV/#it{c})"}; + const AxisSpec axOrigin{4, -0.5f, 3.5f, "stored photons: 0 both SVertexer originals, 1 one finder original, 2 both finder, 3 a crossed pairing"}; + const AxisSpec axClean{2, -0.5f, 1.5f, "all four legs below the TPC shared-cluster cut"}; + fRegistryPairMC.add("PairClass/Pairs/hPairClass", "photon pairs by MC class", kTHnSparseF, {axSample, axClass, axQ, axOrigin, axClean}); + fRegistryPairMC.add("PairClass/Pairs/hDEQinv", "photon pairs: |#Delta E| vs q_{inv} by MC class", kTHnSparseF, {axSample, axClass, {100, 0.f, 0.5f, "#it{q}_{inv} (GeV/#it{c})"}, {100, 0.f, 0.5f, "|#it{E}_{1} #minus #it{E}_{2}| (GeV)"}, {std::vector{0., 0.2, 0.4, 0.6, 0.9, 1.5, 3.}, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hFreeLegPair", "photon pairs by MC class: photons built with a pooled free leg", kTHnSparseF, {axSample, axClass, axQ, {3, -0.5f, 2.5f, "photons of the pair with a free leg: 0, 1, 2"}, {3, -0.5f, 2.5f, "crossed photons (run-2 pairings) in the pair: 0, 1, 2"}}); + fRegistryPairMC.add("PairClass/Pairs/hHalfSwapLegs", "half swaps: run-1 origin of the two legs of the shared true photon", kTHnSparseF, {axSample, axQ, {4, -0.5f, 3.5f, "legs of the shared photon: 0 both free legs, 1 one stored photon (taken apart), 2 two different stored photons, 3 one free one stored"}, {3, -0.5f, 2.5f, "photons of the pair with a free leg: 0, 1, 2"}}); + fRegistryPairMC.add("PairClass/Pairs/hKtQ", "photon pairs: k_{T} vs q_{inv} by MC class", kTHnSparseF, {axSample, axClass, axQ, {100, 0.f, 2.f, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hQinvQout", "photon pairs: q_{inv} vs |q_{out}| (LCMS) by MC class", kTHnSparseF, {axSample, axClass, axQ, {60, 0.f, 0.3f, "|#it{q}_{out}| (LCMS) (GeV/#it{c})"}, {std::vector{0., 0.2, 0.4, 0.6, 0.9, 1.5, 3.}, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairOpening", "photon pairs with q_{inv} < 50 MeV: orientation of the opening by MC class", kTHnSparseF, {axSample, axClass, {10, 0.f, 0.05f, "#it{q}_{inv} (GeV/#it{c})"}, {30, 0.f, 0.15f, "|#it{q}_{side}| (LCMS) (GeV/#it{c})"}, {30, 0.f, 0.15f, "|#it{q}_{long}| (LCMS) (GeV/#it{c})"}, {18, 0.f, 1.5708f, "#psi = atan(|#Delta#eta| / |#Delta#varphi|) (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hSoftHard", "photon pairs with q_{inv} < 100 MeV: soft vs hard photon (reconstructed) by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {20, 0.f, 1.f, "#it{E}_{soft} / #it{E}_{hard}"}, {60, -60.f, 60.f, "#it{R}_{xy}(soft) #minus #it{R}_{xy}(hard) (cm)"}, {18, 0.f, 90.f, "#it{R}_{xy}(hard) (cm)"}, {18, 0.f, 1.5708f, "#psi (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairRes", "pairs of two true photons, q_{inv} < 100 MeV: resolution by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {40, -0.02f, 0.02f, "#it{q}_{inv}^{rec} #minus #it{q}_{inv}^{true} (GeV/#it{c})"}, {40, -0.1f, 0.1f, "#Delta#it{E}^{rec} #minus #Delta#it{E}^{true} (GeV)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairDist", "photon pairs: conversion-point distances by MC class", kTHnSparseF, {axSample, axClass, {50, 0.f, 0.5f, "#it{q}_{inv} (GeV/#it{c})"}, {100, 0.f, 50.f, "reconstructed: distance of the two conversion points (cm)"}, {104, -2.f, 50.f, "true: largest distance of the legs' production vertices (cm)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairQ", "photon pairs: fitted q, four-leg invariant mass, true q by MC class", kTHnSparseF, {axSample, axClass, {100, 0.f, 0.2f, "#it{q}_{inv} of the fitted photons (GeV/#it{c})"}, {100, 0.f, 0.2f, "#it{M} of the four legs (GeV/#it{c}^{2})"}, {100, 0.f, 0.2f, "#it{q}_{inv} of the two true photons (GeV/#it{c})"}, {6, 0.f, 1.5f, "#it{k}_{T} (GeV/#it{c})"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairAlpha", "photon pairs: signed energy sharing of the two photons by MC class", kTHnSparseF, {axSample, axClass, {50, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {20, -1.f, 1.f, "#alpha_{1}"}, {20, -1.f, 1.f, "#alpha_{2}"}, {10, 0.f, 1.f, "|#it{E}_{1} #minus #it{E}_{2}| / (#it{E}_{1} + #it{E}_{2}) of the mother photons"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairLegs", "photon pairs: leg diagnostics by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {15, 0.f, 0.3f, "max over the two photons of |tan#lambda_{+} #minus tan#lambda_{#minus}|"}, {10, 0.f, 1.f, "softest leg #it{p} (GeV/#it{c})"}, {5, -0.5f, 4.5f, "legs with ITS (of 4)"}, axOrigin}); + fRegistryPairMC.add("PairClass/Pairs/hPairCE", "photon pairs: central-electrode variables by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {std::vector{0., 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.6, 0.9, 2.}, "min |#eta| of the two photons"}, {std::vector{0., 1., 2., 5., 10., 20., 50., 300.}, "min |z_{conv}^{true}| (cm)"}, {5, -0.5f, 4.5f, "legs crossing the central electrode in the TPC (of 4)"}, {5, -0.5f, 4.5f, "legs with ITS (of 4)"}}); + fRegistryPairMC.add("PairClass/Pairs/hPairR", "photon pairs: conversion radii by MC class", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {90, 0.f, 90.f, "#it{R}_{xy} of the inner photon (cm)"}, {90, 0.f, 90.f, "#it{R}_{xy} of the outer photon (cm)"}, {91, -1.f, 90.f, "true #it{R}_{xy} of the inner photon (cm), -1 without MC"}}); + fRegistryPairMC.add("PairClass/Pairs/hBrems", "bremsstrahlung pairs (classes 16 and 5, MC)", kTHnSparseF, {axSample, {2, -0.5f, 1.5f, "0 class 16 (both true), 1 class 5 (with a fake)"}, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {5, -0.5f, 4.5f, "radiating electron from: 0 photon conversion, 1 #pi^{0} Dalitz, 2 #eta Dalitz, 3 other, 4 not found"}, {6, -0.5f, 5.5f, "mother of the parent photon: 0 #pi^{0}, 1 #eta, 2 #omega, 3 e^{#pm} (bremsstrahlung), 4 other, 5 none / not a photon"}, {50, 0.f, 0.25f, "#theta(#gamma_{brems}, radiating e^{#pm} rec.) (rad)"}, {50, 0.f, 0.25f, "#theta(#gamma_{brems}, parent #gamma) (rad), MC"}, {60, -40.f, 80.f, "true #it{R}_{xy}(#gamma_{brems}) #minus #it{R}_{xy}(parent) (cm)"}, {20, 0.f, 1.f, "#it{E}(#gamma_{brems}) / #it{E}(parent), MC"}}); + fRegistryPairMC.add("PairClass/Pairs/hPhotonQuality", "photons of the pairs by MC class (each photon once per pair)", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {2, -0.5f, 1.5f, "0 photon radiated by an e^{#pm} (MC mother e^{#pm}), 1 other"}, {50, 0.f, 50.f, "KF #chi^{2}/ndf"}, {60, 0.f, 0.3f, "pointing angle to the PV (rad)"}, {50, 0.f, 5.f, "|DCA_{xy}| to the PV (cm)"}, {50, 0.f, 5.f, "|DCA_{z}| to the PV (cm)"}}); + { + const char* anc = "0 #pi^{0}, 1 #eta, 2 #omega, 3 #eta', 4 #rho/K*, 5 K^{0}, 6 baryon, 7 none (primary), 8 other"; + fRegistryPairMC.add("PairClass/Pairs/hBremsAncestry", "bremsstrahlung pairs (classes 16 and 5, MC): ancestry of the parent photon (the photon whose electron radiated)", kTHnSparseF, {axSample, {2, -0.5f, 1.5f, "0 class 16 (both true), 1 class 5 (with a fake)"}, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {9, -0.5f, 8.5f, Form("mother of the parent photon: %s", anc)}, {9, -0.5f, 8.5f, Form("mother of that particle: %s", anc)}, {9, -0.5f, 8.5f, Form("root of the decay chain: %s", anc)}}); + } + fRegistryPairMC.add("PairClass/Pairs/hPhotonKF", "photons of the pairs by MC class (each photon once per pair)", kTHnSparseF, {axSample, axClass, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {2, -0.5f, 1.5f, "0 photon radiated by an e^{#pm} (MC mother e^{#pm}), 1 other"}, {50, 0.f, 25.f, "KF #chi^{2}/ndf of the conversion-vertex fit"}, {60, 0.f, 300.f, "KF #chi^{2} to the primary vertex"}, {40, -0.2f, 0.2f, "#psi_{pair} (rad)"}}); + fRegistryPairMC.add("PairClass/Pairs/hHiddenBrems", "true distinct pairs (class 13) with a photon radiated by an e^{#pm} (MC)", kTHnSparseF, {axSample, {20, 0.f, 0.1f, "#it{q}_{inv} (GeV/#it{c})"}, {3, -0.5f, 2.5f, "photons of the pair radiated by an e^{#pm}"}, {4, -0.5f, 3.5f, "partner vs the radiating e^{#pm}: 0 unrelated, 1 same mother (e.g. Dalitz #gamma), 2 same root ancestor, 3 partner is itself radiated"}, {6, -0.5f, 5.5f, "radiating e^{#pm} from: 0 photon conversion, 1 #pi^{0} Dalitz, 2 #eta Dalitz, 3 heavy flavour (D, B), 4 other hadron, 5 none / unknown"}, {3, -0.5f, 2.5f, "partner vs the radiating e^{#pm}: 0 different first physical primary, 1 same first physical primary, 2 n/a"}}); + fRegistryPairMC.add("PairClass/Photons/hLegTrueDist", "stored photons: distance of the true production vertices of the two legs;0 same event, 1 mixed event;0 SVertexer original, 1 finder original, 2 crossed pairing;distance (cm)", kTH3F, {{2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}, {104, -2.f, 50.f}}); + auto hEv = fRegistryPairMC.add("PairClass/Events/hArtificialEvents", "artificial events;;events", kTH1F, {{4, -0.5f, 3.5f}}); + hEv->GetXaxis()->SetBinLabel(1, "same event"); + hEv->GetXaxis()->SetBinLabel(2, "mixed event"); + hEv->GetXaxis()->SetBinLabel(3, "standard mixing: partner events used"); + hEv->GetXaxis()->SetBinLabel(4, "standard mixing: events without a partner"); + fRegistryPairMC.add("PairClass/Events/hNPhotons", "photons per artificial event;0 same event, 1 mixed event;photons", kTH2F, {{2, -0.5f, 1.5f}, {100, -0.5f, 99.5f}}); + fRegistryPairMC.add("PairClass/Photons/hOrigin", "stored photons;0 same event, 1 mixed event;0 SVertexer original, 1 finder original, 2 crossed pairing", kTH2F, {{2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}}); + fRegistryPairMC.add("PairClass/Legs/hNSigma", "legs of stored photons (same-event artificial events)", kTHnSparseF, {{3, -0.5f, 2.5f, "MC: 0 electron from a photon, 1 electron other, 2 not an electron"}, {60, 0.f, 3.f, "#it{p}_{TPC in} (GeV/#it{c})"}, {100, -5.f, 5.f, "n#sigma_{e}^{TPC}"}, {120, -6.f, 6.f, "n#sigma_{#pi}^{TPC}"}, {2, -0.5f, 1.5f, "has ITS"}}); + } + + static bool pcSamePart(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.pid >= 0 && x.pid == y.pid; } + static bool pcSameMother(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.mid; } + static bool pcDaughterOf(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.mid >= 0 && x.mid == y.pid; } + static bool pcRadiatedBy(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.pid; } + static bool pcSiblings(PcLeg const& x, PcLeg const& y) { return x.evt == y.evt && x.gmid >= 0 && x.gmid == y.gmid && x.mid != y.mid; } + static bool pcTruePhoton(PcPhoton const& p) { return p.pos.mpdg == kPcGamma && p.ele.mpdg == kPcGamma && pcSameMother(p.pos, p.ele); } + // bremsstrahlung: a leg of one photon radiated the other photon (the photon's grandmother is that electron) + static bool pcRelated(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (pcRadiatedBy(*x, *y) || pcRadiatedBy(*y, *x)) { + return true; + } + } + } + return false; + } + static bool pcSiblingPair(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (pcSiblings(*x, *y)) { + return true; + } + } + } + return false; + } + // 1 two true photons, 2 the legs of two true photons crossed (full swap), 3 other with a fake, 4 no label + static int pcPairTruth(PcPhoton const& a, PcPhoton const& b) + { + if (a.pos.mid < 0 || a.ele.mid < 0 || b.pos.mid < 0 || b.ele.mid < 0) { + return 4; + } + auto key = [](PcLeg const& l) { return std::make_pair(l.evt, l.mid); }; + auto isTrue = [](PcLeg const& l) { return l.mid >= 0 && l.mpdg == kPcGamma; }; + const bool allTrue = isTrue(a.pos) && isTrue(a.ele) && isTrue(b.pos) && isTrue(b.ele); + if (allTrue && key(a.pos) == key(a.ele) && key(b.pos) == key(b.ele) && key(a.pos) != key(b.pos)) { + return 1; + } + if (allTrue && key(a.pos) == key(b.ele) && key(a.ele) == key(b.pos) && key(a.pos) != key(a.ele)) { + return 2; + } + return 3; + } + + static int pcFakeClass(PcPhoton const& a, PcPhoton const& b) + { + const std::array la{&a.pos, &a.ele}, lb{&b.pos, &b.ele}; + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return 12; + } + } + } + if (pcTruePhoton(a) && pcTruePhoton(b) && pcSameMother(a.pos, b.pos)) { + return 1; + } + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (pcSamePart(*la[i], *lb[j])) { + return (la[1 - i]->mpdg == kPcGamma && lb[1 - j]->mpdg == kPcGamma) ? 2 : 3; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (x->mpdg == kPcGamma && pcSameMother(*x, *y)) { + return 4; + } + } + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (pcDaughterOf(*x, *y) || pcDaughterOf(*y, *x)) { + return 5; + } + } + } + if (pcRelated(a, b)) { + return 5; + } + if (pcSiblingPair(a, b)) { + return 20; + } + for (const auto* x : la) { + for (const auto* y : lb) { + if (pcSameMother(*x, *y)) { + return 6; + } + } + } + const bool aFake = !pcTruePhoton(a), bFake = !pcTruePhoton(b); + if (aFake && bFake) { + return 7; + } + const auto& f = aFake ? a : b; + if (pcSameMother(f.pos, f.ele)) { + return 8; + } + if (std::abs(f.pos.pdg) != kPcElectron || std::abs(f.ele.pdg) != kPcElectron) { + return 9; + } + if (f.pos.mpdg == kPcGamma && f.ele.mpdg == kPcGamma) { + return 10; + } + return 11; + } + static int pcPairClass(PcPhoton const& a, PcPhoton const& b) + { + const int truth = pcPairTruth(a, b); + if (truth == 3) { + return pcFakeClass(a, b); + } + if (truth == 1 || truth == 2) { + if (pcRelated(a, b)) { + return truth == 1 ? 16 : 17; // bremsstrahlung + } + if (pcSiblingPair(a, b)) { + return truth == 1 ? 18 : 19; // photons of the same mother + } + return truth == 1 ? 13 : 14; + } + return 15; + } + // --- end of the copied classification --- + static int pcPairOrigin(PcPhoton const& a, PcPhoton const& b) + { + if (!a.original || !b.original) { + return 3; + } + return (a.svertexer ? 0 : 1) + (b.svertexer ? 0 : 1); + } + void fillPcPair(const float sample, PcPhoton const& a, PcPhoton const& b) + { + const float q = o2::pwgem::photonmeson::qInv(a.px, a.py, a.pz, b.px, b.py, b.pz); + if (q >= qaflags.cfgPairClassMaxQ.value) { + return; + } + const float clean = std::max({a.pos.sharedFrac, a.ele.sharedFrac, b.pos.sharedFrac, b.ele.sharedFrac}) < qaflags.cfgPairClassMaxSharedFrac.value ? 1.f : 0.f; + const auto cls = static_cast(pcPairClass(a, b)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairClass"), sample, cls, q, static_cast(pcPairOrigin(a, b)), clean); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hKtQ"), sample, cls, q, 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + if (cls == 4.f) { // o2-linter: disable=magic-number (half swap) + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (x->mpdg == kPcGamma && pcSameMother(*x, *y)) { + const float src = (x->free && y->free) ? 0.f : (x->free || y->free) ? 3.f + : (x->srcPhoton == y->srcPhoton ? 1.f : 2.f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hHalfSwapLegs"), sample, q, src, static_cast((a.nFree > 0 ? 1 : 0) + (b.nFree > 0 ? 1 : 0))); + } + } + } + } + { + const float eA = std::sqrt(a.px * a.px + a.py * a.py + a.pz * a.pz), eB = std::sqrt(b.px * b.px + b.py * b.py + b.pz * b.pz); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hDEQinv"), sample, cls, q, std::fabs(eA - eB), 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hFreeLegPair"), sample, cls, q, static_cast((a.nFree > 0 ? 1 : 0) + (b.nFree > 0 ? 1 : 0)), static_cast((a.original ? 0 : 1) + (b.original ? 0 : 1))); + { + const ROOT::Math::PxPyPzMVector v1(a.px, a.py, a.pz, 0.f), v2(b.px, b.py, b.pz, 0.f); + const auto sum = v1 + v2; + const ROOT::Math::Boost bstZ(0., 0., -sum.Pz() / sum.E()); + const auto q3 = bstZ(ROOT::Math::PxPyPzEVector(v1 - v2)).Vect(); + const float qout = std::fabs(q3.Dot(ROOT::Math::XYZVector(sum.Px(), sum.Py(), 0.).Unit())); + const float kt = 0.5f * std::hypot(a.px + b.px, a.py + b.py); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hQinvQout"), sample, cls, q, qout, std::min(kt, 2.99f)); + const auto ktUnit = ROOT::Math::XYZVector(sum.Px(), sum.Py(), 0.).Unit(); + const float qside = std::fabs(q3.Dot(ROOT::Math::XYZVector(-ktUnit.Y(), ktUnit.X(), 0.))); + const float qlong = std::fabs(q3.Z()); + auto etaOf = [](PcPhoton const& p) { return std::asinh(p.pz / std::max(std::hypot(p.px, p.py), 1.e-6f)); }; + const float dEta = std::fabs(etaOf(a) - etaOf(b)); + const float dPhi = std::fabs(RecoDecay::constrainAngle(std::atan2(a.py, a.px) - std::atan2(b.py, b.px), -o2::constants::math::PI)); + const float psi = std::atan2(dEta, dPhi); + if (q < 0.05f) { // o2-linter: disable=magic-number (diagnostic range) + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairOpening"), sample, cls, q, std::min(qside, 0.149f), std::min(qlong, 0.149f), std::min(psi, 1.57f)); + } + if (q < 0.1f) { // o2-linter: disable=magic-number (diagnostic range) + const float eA = std::hypot(a.px, a.py, a.pz), eB = std::hypot(b.px, b.py, b.pz); + PcPhoton const& soft = eA <= eB ? a : b; + PcPhoton const& hard = eA <= eB ? b : a; + const float rSoft = std::hypot(soft.vx, soft.vy), rHard = std::hypot(hard.vx, hard.vy); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hSoftHard"), sample, cls, q, std::min(std::min(eA, eB) / std::max(std::max(eA, eB), 1.e-6f), 0.999f), std::clamp(rSoft - rHard, -59.9f, 59.9f), std::min(rHard, 89.9f), std::min(psi, 1.57f)); + if (pcTruePhoton(a) && pcTruePhoton(b)) { + const float tA = std::hypot(a.pos.mpx, a.pos.mpy, a.pos.mpz), tB = std::hypot(b.pos.mpx, b.pos.mpy, b.pos.mpz); + const float qT = o2::pwgem::photonmeson::qInv(a.pos.mpx, a.pos.mpy, a.pos.mpz, b.pos.mpx, b.pos.mpy, b.pos.mpz); + const float dERec = eA >= eB ? eA - eB : eB - eA; + const float dETrue = eA >= eB ? tA - tB : tB - tA; // same photon order as the reconstructed one + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairRes"), sample, cls, q, std::clamp(q - qT, -0.0199f, 0.0199f), std::clamp(dERec - dETrue, -0.0999f, 0.0999f)); + } + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairDist"), sample, cls, q, std::hypot(a.vx - b.vx, a.vy - b.vy, a.vz - b.vz), pcTrueDist({&a.pos, &a.ele, &b.pos, &b.ele})); + ROOT::Math::PxPyPzMVector sum4; + const std::array legs4{&a.pos, &a.ele, &b.pos, &b.ele}; + for (const auto* l : legs4) { + sum4 += ROOT::Math::PxPyPzMVector(l->px, l->py, l->pz, o2::constants::physics::MassElectron); + } + float qTrue = -1.f; + { + std::vector mothers; + for (const auto* l : legs4) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() == 2) { + qTrue = o2::pwgem::photonmeson::qInv(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz, mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairQ"), sample, cls, q, static_cast(sum4.M()), qTrue, 0.5f * std::hypot(a.px + b.px, a.py + b.py)); + if (q < 0.1f) { + auto dtgl = [](PcPhoton const& p) { const float ptp = std::hypot(p.pos.px, p.pos.py), pte = std::hypot(p.ele.px, p.ele.py); return (ptp > 0.f && pte > 0.f) ? std::fabs(p.pos.pz / ptp - p.ele.pz / pte) : 0.3f; }; + const float pmin = std::min({std::hypot(a.pos.px, a.pos.py, a.pos.pz), std::hypot(a.ele.px, a.ele.py, a.ele.pz), std::hypot(b.pos.px, b.pos.py, b.pos.pz), std::hypot(b.ele.px, b.ele.py, b.ele.pz)}); + const auto nIts = static_cast((a.pos.its ? 1 : 0) + (a.ele.its ? 1 : 0) + (b.pos.its ? 1 : 0) + (b.ele.its ? 1 : 0)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairLegs"), sample, cls, q, std::min(std::max(dtgl(a), dtgl(b)), 0.299f), std::min(pmin, 0.999f), nIts, static_cast(pcPairOrigin(a, b))); + constexpr float kTpcRin = 85.f, kTpcRout = 247.f; // o2-linter: disable=magic-number (TPC drift volume, diagnostic) + auto crossesCE = [&](PcLeg const& l) { + const float pt = std::hypot(l.px, l.py), rConv = std::hypot(l.vx, l.vy); + if (l.pid < 0 || pt <= 0.f || rConv > kTpcRout) { + return 0; + } + const float tgl = l.pz / pt, z1 = l.vz + tgl * (std::max(rConv, kTpcRin) - rConv), z2 = l.vz + tgl * (kTpcRout - rConv); + return z1 * z2 <= 0.f ? 1 : 0; + }; + auto absEta = [](PcPhoton const& p) { return std::fabs(std::asinh(p.pz / std::max(std::hypot(p.px, p.py), 1.e-6f))); }; + auto absZ = [](PcPhoton const& p) { return p.pos.pid >= 0 ? std::fabs(p.pos.vz) : (p.ele.pid >= 0 ? std::fabs(p.ele.vz) : 299.f); }; + const int nCE = crossesCE(a.pos) + crossesCE(a.ele) + crossesCE(b.pos) + crossesCE(b.ele); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairCE"), sample, cls, q, std::min(std::min(absEta(a), absEta(b)), 1.99f), std::min(std::min(absZ(a), absZ(b)), 299.f), static_cast(nCE), nIts); + { + const float rA = std::hypot(a.vx, a.vy), rB = std::hypot(b.vx, b.vy); + PcPhoton const& inner = rA <= rB ? a : b; + const float rTrue = inner.pos.pid >= 0 ? std::hypot(inner.pos.vx, inner.pos.vy) : (inner.ele.pid >= 0 ? std::hypot(inner.ele.vx, inner.ele.vy) : -0.5f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairR"), sample, cls, q, std::min(std::min(rA, rB), 89.9f), std::min(std::max(rA, rB), 89.9f), std::min(rTrue, 89.9f)); + } + { + auto radiated = [](PcPhoton const& p) { return p.pos.mpdg == kPcGamma && p.pos.mid >= 0 && std::abs(p.pos.gpdg) == kPcElectron; }; + for (const auto* p : {&a, &b}) { + const float ang = std::acos(std::clamp(p->cospa, -1.f, 1.f)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPhotonQuality"), sample, cls, q, radiated(*p) ? 0.f : 1.f, std::min(p->chi2, 49.9f), std::min(ang, 0.299f), std::min(std::fabs(p->dcaxy), 4.99f), std::min(std::fabs(p->dcaz), 4.99f)); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPhotonKF"), sample, cls, q, radiated(*p) ? 0.f : 1.f, std::clamp(p->chi2Vtx, 0.f, 24.99f), std::clamp(p->chi2PV, 0.f, 299.f), std::clamp(p->psipair, -0.199f, 0.199f)); + } + if (cls == 13.f && (radiated(a) || radiated(b))) { // o2-linter: disable=magic-number (true distinct) + const PcPhoton& r = radiated(a) ? a : b; + const PcPhoton& o = radiated(a) ? b : a; + float rel = 0.f; + if (radiated(o)) { + rel = 3.f; + } else if (o.pos.evt == r.pos.evt && o.pos.gmid >= 0 && o.pos.gmid == r.pos.ggid) { + rel = 1.f; // partner photon and the radiating electron have the same mother + } else if (o.pos.evt == r.pos.evt && o.pos.root >= 0 && o.pos.root == r.pos.root) { + rel = 2.f; + } + const int em = std::abs(r.pos.ggpdg); + float radFrom = 5.f; // o2-linter: disable=magic-number (category codes) + if (em == kPcGamma) { + radFrom = 0.f; + } else if (em == 111) { // o2-linter: disable=pdg/explicit-code (pi0) + radFrom = 1.f; + } else if (em == 221) { // o2-linter: disable=pdg/explicit-code (eta) + radFrom = 2.f; + } else if ((em >= 400 && em < 600) || (em >= 4000 && em < 6000)) { // o2-linter: disable=magic-number (charm and beauty hadrons) + radFrom = 3.f; + } else if (em > 100) { // o2-linter: disable=magic-number (hadrons) + radFrom = 4.f; + } + // first physical primary of the brems photon + const float sameAnc = (r.pos.anc < 0 || o.pos.anc < 0 || r.pos.evt != o.pos.evt) ? 2.f : (r.pos.anc == o.pos.anc ? 1.f : 0.f); + fRegistryPairMC.fill(HIST("PairClass/Pairs/hHiddenBrems"), sample, q, static_cast((radiated(a) ? 1 : 0) + (radiated(b) ? 1 : 0)), rel, radFrom, sameAnc); + } + } + if (cls == 16.f || cls == 5.f) { // o2-linter: disable=magic-number (bremsstrahlung classes) + // x = leg of the bremsstrahlung photon, y = the radiating electron (a leg of the partner photon): x's grandmother is y + const PcLeg *x = nullptr, *y = nullptr; + const std::array, 2> orders{{{&a, &b}, {&b, &a}}}; + for (const auto& [p1, p2] : orders) { + for (const auto* l1 : {&p1->pos, &p1->ele}) { + for (const auto* l2 : {&p2->pos, &p2->ele}) { + if (!x && pcRadiatedBy(*l1, *l2)) { + x = l1; + y = l2; + } + } + } + } + auto pdgClass = [](int pdg) { pdg = std::abs(pdg); return pdg == 111 ? 0 : (pdg == 221 ? 1 : (pdg == 223 ? 2 : (pdg == 11 ? 3 : (pdg == 0 ? 5 : 4)))); }; // o2-linter: disable=magic-number (PDG codes) + auto angle = [](float ax, float ay, float az, float bx, float by, float bz) { + const float n = std::sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz)); + return n > 0.f ? std::acos(std::clamp((ax * bx + ay * by + az * bz) / n, -1.f, 1.f)) : 0.249f; + }; + float origin = 4.f, parentMother = 5.f, thE = 0.249f, thG = 0.249f, dR = -39.9f, eFrac = 0.999f; + if (x && y) { + const int mp = std::abs(y->mpdg); + origin = mp == kPcGamma ? 0.f : (mp == 111 ? 1.f : (mp == 221 ? 2.f : 3.f)); // o2-linter: disable=magic-number (PDG codes) + parentMother = mp == kPcGamma ? static_cast(pdgClass(y->gpdg)) : 5.f; + thE = std::min(angle(x->mpx, x->mpy, x->mpz, y->px, y->py, y->pz), 0.249f); + if (mp == kPcGamma) { + thG = std::min(angle(x->mpx, x->mpy, x->mpz, y->mpx, y->mpy, y->mpz), 0.249f); + const float eB = std::hypot(x->mpx, x->mpy, x->mpz), eA = std::hypot(y->mpx, y->mpy, y->mpz); + eFrac = eA > 0.f ? std::min(eB / eA, 0.999f) : 0.999f; + } + dR = std::clamp(std::hypot(x->vx, x->vy) - std::hypot(y->vx, y->vy), -39.9f, 79.9f); + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hBrems"), sample, cls == 16.f ? 0.f : 1.f, q, origin, parentMother, thE, thG, dR, eFrac); // o2-linter: disable=magic-number (class) + if (x && y && std::abs(y->mpdg) == kPcGamma) { + auto ancClass = [](int pdg) { // o2-linter: disable=pdg/explicit-code (diagnostic classes) + pdg = std::abs(pdg); + switch (pdg) { + case 111: + return 0.f; + case 221: + return 1.f; + case 223: + return 2.f; + case 331: + return 3.f; + case 113: + case 213: + case 313: + case 323: + return 4.f; + case 310: + case 130: + case 311: + return 5.f; + default: + break; + } + if (pdg == 11 || pdg == 13 || pdg == 22) { + return 8.f; // o2-linter: disable=magic-number (leptons, photon: other) + } + if (pdg > 1000) { + return 6.f; // o2-linter: disable=magic-number (baryons) + } + if (pdg < 100) { + return 7.f; // o2-linter: disable=magic-number (no hadron mother: quark, gluon, string, cluster, none) + } + return 8.f; + }; + fRegistryPairMC.fill(HIST("PairClass/Pairs/hBremsAncestry"), sample, cls == 16.f ? 0.f : 1.f, q, ancClass(y->gpdg), ancClass(y->ggpdg), ancClass(y->rootPdg)); // o2-linter: disable=magic-number (class) + } + } + auto alphaOf = [](PcPhoton const& p) { + const float lp = p.pos.px * p.px + p.pos.py * p.py + p.pos.pz * p.pz, le = p.ele.px * p.px + p.ele.py * p.py + p.ele.pz * p.pz; + return (lp + le) != 0.f ? std::clamp((lp - le) / (lp + le), -0.999f, 0.999f) : 0.f; + }; + float asym = -1.f; + { + std::vector mothers; + for (const auto* l : legs4) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() == 2) { + const float e1 = std::hypot(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz), e2 = std::hypot(mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + asym = (e1 + e2) > 0.f ? std::min(std::fabs(e1 - e2) / (e1 + e2), 0.999f) : -1.f; + } + } + fRegistryPairMC.fill(HIST("PairClass/Pairs/hPairAlpha"), sample, cls, q, alphaOf(a), alphaOf(b), asym); + } + } + static float pcTrueDist(std::initializer_list legs) + { + float d = 0.f; + for (auto x = legs.begin(); x != legs.end(); ++x) { + if ((*x)->pid < 0) { + return -1.f; + } + for (auto y = std::next(x); y != legs.end(); ++y) { + d = std::max(d, std::hypot((*x)->vx - (*y)->vx, (*x)->vy - (*y)->vy, (*x)->vz - (*y)->vz)); + } + } + return d; + } + + void processPairClassMix(aod::PCMMixCollisions const& collisions, aod::V0PhotonsKFMix const& photons, aod::V0LegsMix const& legs, aod::PCMMixLegs const& mixLegs, + aod::V0PhotonsKFChi2Mix const& kfChi2, aod::V0PhotonsPhiVPsiMix const& phiVPsi) + { + std::map> perCollision; + std::map collisionIsMixed; + for (const auto& collision : collisions) { + collisionIsMixed[collision.globalIndex()] = collision.isMixed(); + } + auto legOf = [&](int legRow, bool sameEvent) { + const auto l = legs.rawIteratorAt(legRow); + const auto m = mixLegs.rawIteratorAt(l.trackId()); + const PcLeg out = pcLegFrom(l, m); + if (sameEvent) { + const float ident = std::abs(out.pdg) == kPcElectron ? (out.mpdg == kPcGamma ? 0.f : 1.f) : 2.f; + fRegistryPairMC.fill(HIST("PairClass/Legs/hNSigma"), ident, m.tpcInnerParam(), m.tpcNSigmaEl(), m.tpcNSigmaPi(), m.hasITS() ? 1.f : 0.f); + } + return std::make_pair(out, m.sourceV0Id()); + }; + for (const auto& photon : photons) { + PcPhoton p; + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.original = photon.v0Id() >= 0; + const bool sameEvent = !collisionIsMixed[photon.collisionId()]; + const auto [pos, posV0] = legOf(photon.posTrackId(), sameEvent); + const auto [ele, eleV0] = legOf(photon.negTrackId(), sameEvent); + p.pos = pos; + p.ele = ele; + p.svertexer = p.original && posV0 >= 0; + p.nFree = (posV0 == -2 ? 1 : 0) + (eleV0 == -2 ? 1 : 0); // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + p.chi2 = photon.chiSquareNDF(); + p.dcaxy = photon.dcaXYtopv(); + p.dcaz = photon.dcaZtopv(); + p.cospa = photon.cospa(); + if (photon.globalIndex() < kfChi2.size()) { + const auto kc = kfChi2.rawIteratorAt(photon.globalIndex()); + p.chi2Vtx = kc.chi2NDFVtx(); + p.chi2PV = kc.chi2PV(); + } + if (photon.globalIndex() < phiVPsi.size()) { + p.psipair = phiVPsi.rawIteratorAt(photon.globalIndex()).psipair(); + } + perCollision[photon.collisionId()].push_back(p); + } + for (const auto& collision : collisions) { + const auto it = perCollision.find(collision.globalIndex()); + const std::vector empty; + const auto& list = it == perCollision.end() ? empty : it->second; + const float type = collision.isMixed() ? 1.f : 0.f; + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), type); + fRegistryPairMC.fill(HIST("PairClass/Events/hNPhotons"), type, static_cast(list.size())); + for (const auto& p : list) { + const float origin = p.original ? (p.svertexer ? 0.f : 1.f) : 2.f; + fRegistryPairMC.fill(HIST("PairClass/Photons/hOrigin"), type, origin); + fRegistryPairMC.fill(HIST("PairClass/Photons/hLegTrueDist"), type, origin, pcTrueDist({&p.pos, &p.ele})); + } + for (size_t i = 0; i < list.size(); ++i) { + for (size_t j = i + 1; j < list.size(); ++j) { + const auto& a = list[i]; + const auto& b = list[j]; + float sample = 0.f; + if (collision.isMixed()) { + const bool oneSource = a.pos.evt == a.ele.evt && a.pos.evt == b.pos.evt && a.pos.evt == b.ele.evt; + sample = oneSource ? 3.f : 2.f; + } + fillPcPair(sample, a, b); + } + } + if (collision.isMixed()) { + continue; + } + // standard mixed event: this same-event artificial event against the pooled ones of the same z and centrality bin + const int zBin = static_cast(std::floor((collision.posZ() + 10.f) / qaflags.cfgPairClassMixZBinWidth.value)); + const int cBin = static_cast(std::floor(collision.centFT0C() / qaflags.cfgPairClassMixCentBinWidth.value)); + auto& pool = mPcPools[zBin * 1000 + cBin]; + if (pool.empty()) { + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), 3.f); + } + for (const auto& partner : pool) { + fRegistryPairMC.fill(HIST("PairClass/Events/hArtificialEvents"), 2.f); + for (const auto& a : list) { + for (const auto& b : partner) { + fillPcPair(1.f, a, b); + } + } + } + pool.push_back(list); + if (static_cast(pool.size()) > qaflags.cfgPairClassMixDepth.value) { + pool.pop_front(); + } + } + } + PROCESS_SWITCH(Photonhbt, processPairClassMix, "pair classes vs q_inv on the output of builder run 2 (artificial events): same event, standard mixed event, co-reconstructed mixed event", false); + + template + static PcLeg pcLegFrom(TLeg const& l, TMixLeg const& m) + { + PcLeg out; + out.evt = m.sourceEventUid(); + out.pid = m.mcParticleId(); + out.mid = m.mcMotherId(); + out.gmid = m.mcGrandMotherId(); + out.pdg = m.mcPdg(); + out.mpdg = m.mcMotherPdg(); + out.gpdg = m.mcGrandMotherPdg(); + out.trk = m.origTrackId(); + out.anc = m.mcAncestorId(); + out.ggid = m.mcGGMotherId(); + out.root = m.mcRootId(); + out.ggpdg = m.mcGGMotherPdg(); + out.rootPdg = m.mcRootPdg(); + out.sharedFrac = l.tpcFractionSharedCls(); + out.vx = m.mcVx(); + out.vy = m.mcVy(); + out.vz = m.mcVz(); + out.px = l.px(); + out.py = l.py(); + out.pz = l.pz(); + out.mpx = m.mcMotherPx(); + out.mpy = m.mcMotherPy(); + out.mpz = m.mcMotherPz(); + out.its = m.hasITS(); + out.free = m.sourceV0Id() == -2; // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + out.srcPhoton = m.sourcePhotonUid(); + return out; + } + + template + static PcPhoton pcPhotonFrom(TPhoton const& photon, TLegs const& legs, TMixLegs const& mixLegs) + { + PcPhoton p; + p.px = photon.px(); + p.py = photon.py(); + p.pz = photon.pz(); + p.vx = photon.vx(); + p.vy = photon.vy(); + p.vz = photon.vz(); + p.original = photon.v0Id() >= 0; + const auto lp = legs.rawIteratorAt(photon.posTrackId()); + const auto ln = legs.rawIteratorAt(photon.negTrackId()); + const auto mp = mixLegs.rawIteratorAt(lp.trackId()); + p.pos = pcLegFrom(lp, mp); + p.ele = pcLegFrom(ln, mixLegs.rawIteratorAt(ln.trackId())); + p.svertexer = p.original && mp.sourceV0Id() >= 0; + p.nFree = (mp.sourceV0Id() == -2) + (mixLegs.rawIteratorAt(ln.trackId()).sourceV0Id() == -2); // o2-linter: disable=magic-number (free-leg stub v0Id of the mixer) + p.chi2 = photon.chiSquareNDF(); + p.dcaxy = photon.dcaXYtopv(); + p.dcaz = photon.dcaZtopv(); + p.cospa = photon.cospa(); + return p; + } + + // the two photons share an AO2D track of the same source event (the trackId check of processAnalysis) + static bool pcSharedTrack(PcPhoton const& a, PcPhoton const& b) + { + for (const auto* x : {&a.pos, &a.ele}) { + for (const auto* y : {&b.pos, &b.ele}) { + if (x->evt == y->evt && x->trk >= 0 && x->trk == y->trk) { + return true; + } + } + } + return false; + } + static bool pcHasLabels(PcPhoton const& p) { return p.pos.pid >= 0 && p.ele.pid >= 0; } + // PhotonMCInfo of processMC from the labels in PCMMixLegs (same-event artificial events: all MC ids are of one event) + static PhotonMCInfo pcMCInfo(PcPhoton const& p) + { + PhotonMCInfo info{}; + if (!pcHasLabels(p)) { + return info; + } + info.hasMC = true; + info.mcPosId = p.pos.pid; + info.mcNegId = p.ele.pid; + if (p.pos.mid < 0 || p.ele.mid < 0) { + return info; + } + info.posMotherId = p.pos.mid; + info.negMotherId = p.ele.mid; + info.posMotherIsPhoton = p.pos.mpdg == kPcGamma; + info.negMotherIsPhoton = p.ele.mpdg == kPcGamma; + if (!pcSameMother(p.pos, p.ele)) { + return info; + } + info.sameMother = true; + info.motherId = p.pos.mid; + info.motherPdg = p.pos.mpdg; + info.isTruePhoton = info.motherPdg == kPcGamma; + return info; + } + // isPi0DaughterPair of processMC: two true photons of the same pi0 + static bool pcPi0Daughters(PcPhoton const& a, PcPhoton const& b) + { + return pcTruePhoton(a) && pcTruePhoton(b) && a.pos.evt == b.pos.evt && a.pos.mid != b.pos.mid && a.pos.gmid >= 0 && + a.pos.gmid == b.pos.gmid && std::abs(a.pos.gpdg) == kPi0; + } + // q_inv of the two MC photons behind the four legs, -1 unless the four legs come from exactly two photons + static float pcTrueQ(PcPhoton const& a, PcPhoton const& b) + { + std::vector mothers; + for (const auto* l : {&a.pos, &a.ele, &b.pos, &b.ele}) { + if (l->mid < 0 || l->mpdg != kPcGamma) { + continue; + } + bool seen = false; + for (const auto* m : mothers) { + seen = seen || pcSameMother(*l, *m); + } + if (!seen) { + mothers.push_back(l); + } + } + if (mothers.size() != 2) { + return -1.f; + } + return o2::pwgem::photonmeson::qInv(mothers[0]->mpx, mothers[0]->mpy, mothers[0]->mpz, mothers[1]->mpx, mothers[1]->mpy, mothers[1]->mpz); + } + // signed energy sharing: which charge carries more of the photon momentum (data-computable swap tag, status 13ac) + static float pcAlpha(PcPhoton const& p) + { + const float lp = p.pos.px * p.px + p.pos.py * p.py + p.pos.pz * p.pz, le = p.ele.px * p.px + p.ele.py * p.py + p.ele.pz * p.pz; + return (lp + le) != 0.f ? (lp - le) / (lp + le) : 0.f; + } + // |q_out|, |q_side|, |q_long| in the LCMS, as in fillPairHistogram + static std::array lcmsQ(ROOT::Math::PtEtaPhiMVector const& v1, ROOT::Math::PtEtaPhiMVector const& v2) + { + const auto k12 = 0.5 * (v1 + v2); + const ROOT::Math::XYZVector uvOut(k12.Px() / k12.Pt(), k12.Py() / k12.Pt(), 0), uvLong(0, 0, 1); + const ROOT::Math::XYZVector uvSide = uvOut.Cross(uvLong); + const ROOT::Math::PxPyPzEVector v1c(v1), v2c(v2); + const float betaZ = (v1 + v2).Beta() * std::cos((v1 + v2).Theta()); + const ROOT::Math::Boost bstZ(0, 0, -betaZ); + const auto q3 = bstZ(v1c - v2c).Vect(); + return {static_cast(std::fabs(q3.Dot(uvOut))), static_cast(std::fabs(q3.Dot(uvSide))), static_cast(std::fabs(q3.Dot(uvLong)))}; + } + + void addCoRecoHistograms() + { + const AxisSpec axSample{2, -0.5f, 1.5f, "0 same event, 1 mixed event"}; + const AxisSpec axOrigin{4, -0.5f, 3.5f, "stored photons: 0 both SVertexer originals, 1 one finder original, 2 both finder, 3 a crossed pairing"}; + const AxisSpec axSign{2, -0.5f, 1.5f, "#alpha_{1}#alpha_{2} > 0 (same charge carries more momentum)"}; + auto hEv = fRegistry.add("CoReco/hArtificialEvents", "artificial events (PCMMixCollisions);;events", kTH1F, {{4, -0.5f, 3.5f}}); + hEv->GetXaxis()->SetBinLabel(1, "same event"); + hEv->GetXaxis()->SetBinLabel(2, "same event, centrality"); + hEv->GetXaxis()->SetBinLabel(3, "mixed event"); + hEv->GetXaxis()->SetBinLabel(4, "mixed event, centrality"); + fRegistry.add("CoReco/hNPhotons", "photons per artificial event (before the photon cut);0 same event, 1 mixed event;photons", kTH2F, {axSample, {100, -0.5f, 99.5f}}); + fRegistry.add("CoReco/hOneSourcePairs", "mixed artificial events: pairs with all four legs from one source event;q_{inv} (GeV/c);pairs", kTH1F, {axisQinv}); + fRegistry.add("CoReco/hPairOrigin", "accepted pairs by origin of the stored photons", kTHnSparseF, {axSample, axOrigin, axisQinv, axisKt, axSign}); + if (!isMC) { + return; + } + const AxisSpec axClass{21, -0.5f, 20.5f, "pair class: 13 true distinct, 14 full swap, 4 half swap, 1-3 duplicates, 16 bremsstrahlung (both true), 5 bremsstrahlung with a fake, 18 photons of the same mother (pi0/eta, both true), 19 swap of two such photons, 20 same mother with a fake, 17 related swap, 15 no label, 6-12 other fakes"}; + const AxisSpec axQFine{200, 0.f, 0.2f, "#it{q}_{inv} (GeV/#it{c})"}; + const AxisSpec axQTrue{201, -0.001f, 0.2f, "#it{q}_{inv} of the two MC photons (GeV/#it{c}), -1 if not two photons"}; + const AxisSpec axKtCoarse{std::vector{0., 0.2, 0.4, 0.6, 1.0, 2.0}, "#it{k}_{T} (GeV/#it{c})"}; + fRegistryPairMC.add("CoReco/MC/hClass3D", "accepted pairs by MC class: LCMS q components (CF binning)", kTHnSparseF, {axSample, axClass, axisQout, axisQside, axisQlong, axisKt}); + fRegistryPairMC.add("CoReco/MC/hClassQ", "accepted pairs by MC class: reconstructed vs true q", kTHnSparseF, {axSample, axClass, axQFine, axQTrue, axKtCoarse}); + fRegistryPairMC.add("CoReco/MC/hClassTag", "accepted pairs by MC class: photon origin and energy-sharing sign", kTHnSparseF, {axSample, axClass, axQFine, axOrigin, axSign}); + } + + // processMC's mixed-event truth fill (both photons labelled): both true / one fake / both fake + template + void fillMixTruth(TCollision const& collision, const int true1, const int true2, PairQAObservables const& obs) + { + if (true1 < 0 || true2 < 0) { + return; + } + const int nTrue = true1 + true2; + if (nTrue == 2) { // o2-linter: disable=magic-number (both photons true) + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueTrue"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); + } else if (nTrue == 1) { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueFake>(collision, obs.v1, obs.v2); + } else { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_FakeFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::FakeFake>(collision, obs.v1, obs.v2); + } + } + + // processMC's same-event truth block for an accepted pair, with the MC information from PCMMixLegs + template + void fillCoRecoSameTruth(TCollision const& collision, TPhoton const& g1, TPhoton const& g2, PcPhoton const& a, PcPhoton const& b, + PairQAObservables const& obs, LegPairObservables const& legObs, PhotonWithLegs const& pwl1, PhotonWithLegs const& pwl2, + const bool doQA, const bool doFR) + { + if (!pcHasLabels(a) || !pcHasLabels(b)) { + fillPairHistogramNoLabel(collision, obs.v1, obs.v2); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hDEtaDPhi"), obs.deta, obs.dphi); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hKt"), obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hQinv"), obs.qinv); + return; + } + const auto mc1 = pcMCInfo(a), mc2 = pcMCInfo(b); + auto truthType = classifyPairTruth(mc1, mc2); + if (truthType == PairTruthType::TrueTrueDistinct && pcPi0Daughters(a, b)) { + truthType = PairTruthType::Pi0Daughters; + } + const bool doMCQA = passQinvMCQAGate(obs.qinv); + fillMCPairQA(truthType, obs, doQA, doMCQA); + if (mQa.legPairQa) { + fillLegPairMC(truthType, legObs, obs.kt); + } + if (doFR) { + fillMCPairQAFullRange(truthType, obs); + } + const bool isTruePair = (truthType == PairTruthType::TrueTrueDistinct || truthType == PairTruthType::TrueTrueSamePhoton || + truthType == PairTruthType::Pi0Daughters); + if (isTruePair) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hDEtaDPhi_truePairs"), obs.deta, obs.dphi); + if (doMCQA) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_kT_truePairs"), obs.deta, obs.dphi, obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_qinv_truePairs"), obs.deta, obs.dphi, obs.qinv); + } + } else { + fRegistryPairMC.fill(HIST("Pair/same/MC/hDEtaDPhi_fakePairs"), obs.deta, obs.dphi); + if (doMCQA) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_kT_fakePairs"), obs.deta, obs.dphi, obs.kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_DEtaDPhi_qinv_fakePairs"), obs.deta, obs.dphi, obs.qinv); + } + } + if (mc1.sameMother && mc1.isTruePhoton && mc2.sameMother && mc2.isTruePhoton && mc1.motherId != mc2.motherId) { + const ROOT::Math::PxPyPzMVector t1(a.pos.mpx, a.pos.mpy, a.pos.mpz, 0.), t2(b.pos.mpx, b.pos.mpy, b.pos.mpz, 0.); + const float qTrue = static_cast(std::sqrt(std::max(0., -(t1 - t2).M2()))); + fRegistryPairMC.fill(HIST("Pair/same/MC/hQrecVsQtrueVsKt"), qTrue, obs.qinv, obs.kt); + } + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g1.psipair()), g1.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc1.sameMother && mc1.isTruePhoton) ? 1.f : 0.f); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g2.psipair()), g2.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc2.sameMother && mc2.isTruePhoton) ? 1.f : 0.f); + if (truthType == PairTruthType::FakeFake || truthType == PairTruthType::TrueFake) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hFakeSubtypeVsQinv"), obs.qinv, static_cast(classifyFakeSubtype(mc1, mc2))); + } + const auto crossMC = computeCrossObs(pwl1, pwl2, obs.qinv); + if (crossMC.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_MeeRatio_Qinv_Type"), crossMC.meeOverQ, obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.bestCombo >= 0) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_CrossGeom_Qinv_Type"), crossMC.gapBest, std::fabs(crossMC.dzBest), obs.qinv, static_cast(static_cast(truthType))); + } + if (crossMC.qSwap >= 0.f) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_SwapTemplate_Type"), obs.qinv, crossMC.qSwap, crossMC.geomPhotonLike ? 1.f : 0.f, static_cast(static_cast(truthType))); + } + switch (truthType) { + case PairTruthType::TrueTrueDistinct: + fillPairHistogramMC<0, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); + break; + case PairTruthType::TrueTrueSamePhoton: + fillPairHistogramMC<0, PairTruthType::TrueTrueSamePhoton>(collision, obs.v1, obs.v2); + break; + case PairTruthType::SharedMcLeg: + fillPairHistogramMC<0, PairTruthType::SharedMcLeg>(collision, obs.v1, obs.v2); + break; + case PairTruthType::TrueFake: + fillPairHistogramMC<0, PairTruthType::TrueFake>(collision, obs.v1, obs.v2); + break; + case PairTruthType::FakeFake: + fillPairHistogramMC<0, PairTruthType::FakeFake>(collision, obs.v1, obs.v2); + break; + case PairTruthType::Pi0Daughters: + fillPairHistogramMC<0, PairTruthType::Pi0Daughters>(collision, obs.v1, obs.v2); + break; + default: + break; + } + } + + template + int pairCoRecoEvent(TCollision const& collision, std::vector const& ids, MyV0PhotonsMix const& photons, + std::vector const& pc, const float centForQA, const float occupancy, const bool pool) + { + constexpr bool kSame = ev_id == 0; + constexpr float kSample = kSame ? 0.f : 1.f; + std::vector selected(ids.size(), false); + for (size_t i = 0; i < ids.size(); ++i) { + const auto g = photons.rawIteratorAt(ids[i]); + selected[i] = fV0PhotonCut.IsSelected(g); + if (kSame && selected[i] && mQa.singlePhotonQa) { + fillSinglePhotonQAStep<0>(g); + } + } + const auto keyDFCollision = std::make_pair(ndf, static_cast(collision.globalIndex())); + std::unordered_set idsAfterPairCuts; + int ndiphoton = 0; + for (size_t i = 0; i < ids.size(); ++i) { + if (!selected[i]) { + continue; + } + for (size_t j = i + 1; j < ids.size(); ++j) { + if (!selected[j]) { + continue; + } + const auto g1 = photons.rawIteratorAt(ids[i]); + const auto g2 = photons.rawIteratorAt(ids[j]); + const auto& a = pc[ids[i]]; + const auto& b = pc[ids[j]]; + if (pcSharedTrack(a, b)) { + continue; + } + if constexpr (!kSame) { + if (a.pos.evt == a.ele.evt && a.pos.evt == b.pos.evt && a.pos.evt == b.ele.evt) { + fRegistry.fill(HIST("CoReco/hOneSourcePairs"), o2::pwgem::photonmeson::qInv(a.px, a.py, a.pz, b.px, b.py, b.pz)); + if (mixing.cfgCoRecoSkipOneSource.value) { + continue; + } + } + } + if (!passAsymmetryCut(g1.pt(), g2.pt())) { + continue; + } + const auto pos1 = g1.template posTrack_as(), ele1 = g1.template negTrack_as(); + const auto pos2 = g2.template posTrack_as(), ele2 = g2.template negTrack_as(); + auto obs = buildPairQAObservables(g1, g2); + if (!obs.valid) { + continue; + } + const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); + const auto legObs = buildLegPairObservables(g1, g2, pos1, ele1, pos2, ele2); + auto pwl1 = makePhotonWithLegs(g1, pos1, ele1, collision.posZ()); + auto pwl2 = makePhotonWithLegs(g2, pos2, ele2, collision.posZ()); + if (isMC) { + pwl1.fIsTruePhoton = pcHasLabels(a) ? (pcTruePhoton(a) ? 1 : 0) : -1; + pwl2.fIsTruePhoton = pcHasLabels(b) ? (pcTruePhoton(b) ? 1 : 0) : -1; + } + const auto sep = computePairSep(pwl1, pwl2); + + // ─── before pair cuts ───────────────────────────────────── + fillPairSep(sep, obs); + if (doQA) { + fillPairQAStep(obs, centForQA, occupancy); + if constexpr (kSame) { + fillLegPairQAStep<0>(legObs, obs.kt); + } + } + if (doFR) { + fillFullRangeDeltaRCosOA(obs.qinv, obs.drOverCosOA); + } + if constexpr (kSame) { + fillLegSimilarity(pos1, pos2, obs.qinv); + fillLegSimilarity(ele1, ele2, obs.qinv); + } else { + fillLegSimilarityMix(pwl1, pwl2, obs.qinv); + } + + // ─── pair cuts ──────────────────────────────────────────── + if (!passPhotonClassPairCut(pwl1.legCounts(), pwl2.legCounts())) { + continue; + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { + continue; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + continue; + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { + const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + // ─── after pair cuts ────────────────────────────────────── + fillPairSep(sep, obs); + idsAfterPairCuts.insert(ids[i]); + idsAfterPairCuts.insert(ids[j]); + if (doQA) { + fillPairQAStep(obs, centForQA, occupancy); + if constexpr (kSame) { + fillLegPairQAStep<1>(legObs, obs.kt); + } + } + if (doFR) { + fillFullRangeQA(obs, centForQA, occupancy); + } + fillPairHistogram(collision, obs.v1, obs.v2, 1.f); + if constexpr (kSame) { + fRegistryCF.fill(HIST("Pair/same/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } else { + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } + ndiphoton++; + const auto origin = static_cast(pcPairOrigin(a, b)); + const float sameSign = pcAlpha(a) * pcAlpha(b) > 0.f ? 1.f : 0.f; + fRegistry.fill(HIST("CoReco/hPairOrigin"), kSample, origin, obs.qinv, obs.kt, sameSign); + + if (isMC) { + if constexpr (kSame) { + fillCoRecoSameTruth(collision, g1, g2, a, b, obs, legObs, pwl1, pwl2, doQA, doFR); + } else { + fillMixTruth(collision, pwl1.fIsTruePhoton, pwl2.fIsTruePhoton, obs); + } + const auto cls = static_cast(pcPairClass(a, b)); + const auto q3 = lcmsQ(obs.v1, obs.v2); + fRegistryPairMC.fill(HIST("CoReco/MC/hClass3D"), kSample, cls, q3[0], q3[1], q3[2], obs.kt); + fRegistryPairMC.fill(HIST("CoReco/MC/hClassQ"), kSample, cls, obs.qinv, pcTrueQ(a, b), obs.kt); + fRegistryPairMC.fill(HIST("CoReco/MC/hClassTag"), kSample, cls, obs.qinv, origin, sameSign); + } + if (pool) { + if (usedPhotonIdsPerCol.insert(static_cast(ids[i])).second) { + emh1->AddTrackToEventPool(keyDFCollision, pwl1); + } + if (usedPhotonIdsPerCol.insert(static_cast(ids[j])).second) { + emh1->AddTrackToEventPool(keyDFCollision, pwl2); + } + } + } + } + if (kSame && mQa.singlePhotonQa) { + for (size_t i = 0; i < ids.size(); ++i) { + if (selected[i] && idsAfterPairCuts.contains(ids[i])) { + fillSinglePhotonQAStep<1>(photons.rawIteratorAt(ids[i])); + } + } + } + usedPhotonIdsPerCol.clear(); + return ndiphoton; + } + + template + void mixStandardCoReco(TCollision const& collision, std::tuple const& keyBin, const float centForQA, const float occupancy) + { + const auto keyDFCollision = std::make_pair(ndf, static_cast(collision.globalIndex())); + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + for (const auto& mixID : poolIDs) { + if (mixID == keyDFCollision) { + continue; + } + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); + for (const auto& g1 : selectedPhotons) { + for (const auto& g2 : poolPhotons) { + if (!passAsymmetryCut(g1.pt(), g2.pt())) { + continue; + } + if (!passFastQinvGate(g1, g2)) { + continue; + } + auto obs = buildPairQAObservables(g1, g2); + if (!obs.valid) { + continue; + } + const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); + const auto sep = computePairSep(g1, g2); + fillPairSep<1>(sep, obs); + if (doQA) { + fillPairQAStep<1, 0>(obs, centForQA, occupancy); + } + if (doFR) { + fillFullRangeDeltaRCosOA<1>(obs.qinv, obs.drOverCosOA); + } + fillLegSimilarityMix(g1, g2, obs.qinv); + if (!passPhotonClassPairCut(g1.legCounts(), g2.legCounts())) { + continue; + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { + continue; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + continue; + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value || crosspair.cfgDoCrossGeomCut.value) { + const auto cross = computeCrossObs(g1, g2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + fillPairSep<1, true>(sep, obs); + if (doQA) { + fillPairQAStep<1, 1>(obs, centForQA, occupancy); + } + if (doFR) { + fillFullRangeQA<1>(obs, centForQA, occupancy); + } + fillPairHistogram<1>(collision, obs.v1, obs.v2, 1.f); + if (isMC) { + fillMixTruth(collision, g1.fIsTruePhoton, g2.fIsTruePhoton, obs); + } + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + } + } + } + emh1->AddCollisionIdAtLast(keyBin, keyDFCollision); + } + + void processCoReco(aod::PCMMixCollisions const& collisions, MyV0PhotonsMix const& photons, aod::V0LegsMix const& legs, aod::PCMMixLegs const& mixLegs) + { + // run-2 photons per artificial event, and the leg view of every photon (source event, AO2D track, MC labels, run-1 origin) + std::vector> photonsOf(collisions.size()); + std::vector pc(photons.size()); + for (const auto& photon : photons) { + if (photon.collisionId() >= 0 && photon.collisionId() < static_cast(collisions.size())) { + photonsOf[photon.collisionId()].push_back(photon.globalIndex()); + } + pc[photon.globalIndex()] = pcPhotonFrom(photon, legs, mixLegs); + } + const bool standardMix = mixing.cfgCoRecoMixMode.value == 1; + for (const auto& collision : collisions) { + const bool mixedEvent = collision.isMixed(); + const auto& ids = photonsOf[collision.globalIndex()]; + fRegistry.fill(HIST("CoReco/hArtificialEvents"), mixedEvent ? 2.f : 0.f); + fRegistry.fill(HIST("CoReco/hNPhotons"), mixedEvent ? 1.f : 0.f, static_cast(ids.size())); + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const float centForQA = cent[mixing.cfgCentEstimator]; + if (centForQA < centralitySelection.cfgCentMin || centralitySelection.cfgCentMax < centForQA) { + continue; + } + fRegistry.fill(HIST("CoReco/hArtificialEvents"), mixedEvent ? 3.f : 1.f); + if (mixedEvent && standardMix) { + continue; + } + initCCDB(collision); + const float occupancy = collision.occupancy(); + if (mixedEvent) { + pairCoRecoEvent<1>(collision, ids, photons, pc, centForQA, occupancy, false); + continue; + } + const int ndiphoton = pairCoRecoEvent<0>(collision, ids, photons, pc, centForQA, occupancy, standardMix && mixing.cfgDoMix.value); + if (standardMix && mixing.cfgDoMix.value && ndiphoton > 0) { + const auto keyBin = std::make_tuple(binOf(ztxBinEdges, collision.posZ()), binOf(centBinEdges, centForQA), 0, binOf(occBinEdges, occupancy)); + mixStandardCoReco(collision, keyBin, centForQA, occupancy); + } + } + ndf++; + } + PROCESS_SWITCH(Photonhbt, processCoReco, "analysis (CF, QA, MC truth types) on the output of builder run 2: same event and co-reconstructed mixed event from the artificial events", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) From a8dac416ea658634ad0c92a1805c17bdd57f5f4d Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Sun, 8 Nov 2026 14:55:21 +0530 Subject: [PATCH 605/648] [PWGCF] LP Balance Function Update 11: Refactor MC composition checks and add unweighted counts (#18636) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 406 +++++++----------- 1 file changed, 167 insertions(+), 239 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 7dc7595998b..4c1c3be98df 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -1297,14 +1298,6 @@ struct LambdaProtonBalanceFunction { void init(InitContext const&) { - if (doprocessMCComposition.value) { - if (doprocessData.value || doprocessMCRecBF.value || doprocessMCGenBF.value || doprocessMCEfficiency.value) { - LOGF(fatal, "processMCComposition requires processData, processMCRecBF, processMCGenBF and processMCEfficiency=false. processData defaults to true and must be disabled explicitly."); - } - if (cApplyEffCorr.value) { - LOGF(fatal, "processMCComposition requires cApplyEffCorr=false: composition stores unweighted counts and must not load efficiency maps."); - } - } if (static_cast(doprocessData.value) + static_cast(doprocessMCRecBF.value) + static_cast(doprocessMCGenBF.value) > 1) { @@ -1366,9 +1359,6 @@ struct LambdaProtonBalanceFunction { registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); } } - if (doprocessMCComposition.value) { - bookMCComposition(); - } registryOther.add("hEffWeightProton", "Efficiency weight 1/#epsilon, protons", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); registryOther.add("hEffWeightLambda", "Efficiency weight 1/#epsilon, #Lambda", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); registryOther.add("hEffWeightZeroCount", "Particles with efficiency weight 0 (1=p, 2=#bar{p}, 3=#Lambda, 4=#bar{#Lambda})", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); @@ -1376,6 +1366,18 @@ struct LambdaProtonBalanceFunction { registryOther.add("hNSelectedPrimAntiProtons", "Number of selected primary antiprotons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelLambda", "Number of selected Lambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); registryOther.add("hNSelAntiLambda", "Number of selected AntiLambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_reco1", "Stage 1: selected reconstructed Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_reco1", "Stage 1: selected reconstructed AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_reco1", "Stage 1: selected reconstructed Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_reco1", "Stage 1: selected reconstructed AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_PrimSec_reco", "Stage 2: primary and secondary truth-matched Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_PrimSec_reco", "Stage 2: primary and secondary truth-matched AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_PrimSec_reco", "Stage 2: primary and secondary truth-matched Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_PrimSec_reco", "Stage 2: primary and secondary truth-matched AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Lambda_prim_reco", "Stage 3: physical-primary truth-matched Lambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiLambda_prim_reco", "Stage 3: physical-primary truth-matched AntiLambda per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_Proton_prim_reco", "Stage 3: physical-primary truth-matched Proton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hN_AntiProton_prim_reco", "Stage 3: physical-primary truth-matched AntiProton per event (unweighted);N;Events", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); // ── V0-daughter veto QA counters ───────────────────────────────────── // Proton veto counters registryOther.add("hNPrimProtonBeforeVeto", "Proton candidates before V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); @@ -2205,10 +2207,9 @@ struct LambdaProtonBalanceFunction { for (int i = 0; i < 4; ++i) { hDaughters->GetXaxis()->SetBinLabel(i + 1, daughterLabels[i]); } - const std::string mode = doprocessData.value ? "processData" : doprocessMCRecBF.value ? "processMCRecBF" - : doprocessMCGenBF.value ? "processMCGenBF" - : doprocessMCComposition.value ? "processMCComposition" - : "none"; + const std::string mode = doprocessData.value ? "processData" : doprocessMCRecBF.value ? "processMCRecBF" + : doprocessMCGenBF.value ? "processMCGenBF" + : "none"; std::vector configLabels = { "activeBFMode=" + mode, "cUseFT0C=" + std::to_string(static_cast(cUseFT0C.value)), @@ -3185,19 +3186,141 @@ struct LambdaProtonBalanceFunction { // ───────────────────────────────────────────────────────────────────── } + void fillStage23Counts(int64_t /*mcCollisionId*/, + std::vector const& /*selectedLambdas*/, + std::vector const& /*selectedAntiLambdas*/, + std::vector const& /*selectedPrimProtons*/, + std::vector const& /*selectedPrimAntiProtons*/) + { + } + + void fillStage23Counts(int64_t mcCollisionId, + std::vector const& selectedLambdas, + std::vector const& selectedAntiLambdas, + std::vector const& selectedPrimProtons, + std::vector const& selectedPrimAntiProtons) + { + // Stage 2 and Stage 3 counting; the selected reconstructed containers remain unchanged. + int nLambdaPrimSec = 0; + int nAntiLambdaPrimSec = 0; + int nProtonPrimSec = 0; + int nAntiProtonPrimSec = 0; + int nLambdaPrim = 0; + int nAntiLambdaPrim = 0; + int nProtonPrim = 0; + int nAntiProtonPrim = 0; + + for (const auto& candidate : selectedLambdas) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kLambda0) { + continue; + } + ++nLambdaPrimSec; + if (mc.isPhysicalPrimary()) { + ++nLambdaPrim; + } + } + + for (const auto& candidate : selectedAntiLambdas) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kLambda0Bar) { + continue; + } + ++nAntiLambdaPrimSec; + if (mc.isPhysicalPrimary()) { + ++nAntiLambdaPrim; + } + } + + for (const auto& candidate : selectedPrimProtons) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kProton) { + continue; + } + ++nProtonPrimSec; + if (mc.isPhysicalPrimary()) { + ++nProtonPrim; + } + } + + for (const auto& candidate : selectedPrimAntiProtons) { + if (!candidate.has_mcParticle()) { + continue; + } + const auto* truth = static_cast(candidate).getCurrent(); + const int64_t id = candidate.mcParticleId(); + if (!usableMCIndex(truth, id)) { + continue; + } + const auto mc = truth->rawIteratorAt(id - truth->offset()); + if (mc.mcCollisionId() != mcCollisionId) { + continue; + } + if (mc.pdgCode() != PDG_t::kProtonBar) { + continue; + } + ++nAntiProtonPrimSec; + if (mc.isPhysicalPrimary()) { + ++nAntiProtonPrim; + } + } + + registryOther.fill(HIST("hN_Lambda_PrimSec_reco"), static_cast(nLambdaPrimSec)); + registryOther.fill(HIST("hN_AntiLambda_PrimSec_reco"), static_cast(nAntiLambdaPrimSec)); + registryOther.fill(HIST("hN_Proton_PrimSec_reco"), static_cast(nProtonPrimSec)); + registryOther.fill(HIST("hN_AntiProton_PrimSec_reco"), static_cast(nAntiProtonPrimSec)); + registryOther.fill(HIST("hN_Lambda_prim_reco"), static_cast(nLambdaPrim)); + registryOther.fill(HIST("hN_AntiLambda_prim_reco"), static_cast(nAntiLambdaPrim)); + registryOther.fill(HIST("hN_Proton_prim_reco"), static_cast(nProtonPrim)); + registryOther.fill(HIST("hN_AntiProton_prim_reco"), static_cast(nAntiProtonPrim)); + } + // Reconstructed candidates are compared with generated primary particles; // residual background and feed-down can prevent full closure. - template - void runRecoAnalysis(TCollision const& collision, aod::V0Datas const& V0s, - MyTracks const& tracks, float cent, CentRho2Set* cs) + template + void runRecoAnalysis(TCollision const& collision, TV0s const& V0s, + TTracks const& tracks, float cent, CentRho2Set* cs, [[maybe_unused]] int64_t mcCollisionId = -1) { // ====== Step 1: Initialise containers ====================================== std::unordered_set v0DaughterTrackIds; v0DaughterTrackIds.reserve(64); - std::vector selectedPrimProtons; - std::vector selectedPrimAntiProtons; - std::vector selectedLambdas; - std::vector selectedAntiLambdas; + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; int nSelLambda = 0; int nSelAntiLambda = 0; @@ -3206,7 +3329,7 @@ struct LambdaProtonBalanceFunction { int nPairs_AntiLambda_PrimProton = 0; int nPairs_AntiLambda_PrimAntiProton = 0; - selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); + selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); nSelLambda = static_cast(selectedLambdas.size()); nSelAntiLambda = static_cast(selectedAntiLambdas.size()); constexpr float mLambdaPDG = o2::constants::physics::MassLambda; @@ -3217,8 +3340,8 @@ struct LambdaProtonBalanceFunction { { const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; - const auto& dauPos = v0.posTrack_as(); - const auto& dauNeg = v0.negTrack_as(); + const auto& dauPos = v0.template posTrack_as(); + const auto& dauNeg = v0.template negTrack_as(); fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); fillDaughterQA(DaughterQAStage::Final, dauPos); fillDaughterQA(DaughterQAStage::Final, dauNeg); @@ -3240,8 +3363,8 @@ struct LambdaProtonBalanceFunction { { const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; - const auto& dauPos = v0.posTrack_as(); - const auto& dauNeg = v0.negTrack_as(); + const auto& dauPos = v0.template posTrack_as(); + const auto& dauNeg = v0.template negTrack_as(); fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); fillDaughterQA(DaughterQAStage::Final, dauNeg); fillDaughterQA(DaughterQAStage::Final, dauPos); @@ -3442,7 +3565,7 @@ struct LambdaProtonBalanceFunction { // now fully populated. Veto set was built from validated daughters only. for (const auto& v0 : selectedLambdas) { - const auto& v0PosDau = v0.posTrack_as(); + const auto& v0PosDau = v0.template posTrack_as(); const float pt = v0.pt(); int bin = -1; for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { @@ -3697,7 +3820,7 @@ struct LambdaProtonBalanceFunction { } for (const auto& v0 : selectedAntiLambdas) { - const auto& v0NegDau = v0.negTrack_as(); + const auto& v0NegDau = v0.template negTrack_as(); const float pt = v0.pt(); int bin = -1; for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { @@ -4159,6 +4282,13 @@ struct LambdaProtonBalanceFunction { registryOther.fill(HIST("hNPairs_Lambda_PrimAntiProton"), static_cast(nPairs_Lambda_PrimAntiProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nPairs_AntiLambda_PrimProton)); registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nPairs_AntiLambda_PrimAntiProton)); + + // Stage 1 counting. + registryOther.fill(HIST("hN_Lambda_reco1"), static_cast(selectedLambdas.size())); + registryOther.fill(HIST("hN_AntiLambda_reco1"), static_cast(selectedAntiLambdas.size())); + registryOther.fill(HIST("hN_Proton_reco1"), static_cast(selectedPrimProtons.size())); + registryOther.fill(HIST("hN_AntiProton_reco1"), static_cast(selectedPrimAntiProtons.size())); + fillStage23Counts(mcCollisionId, selectedLambdas, selectedAntiLambdas, selectedPrimProtons, selectedPrimAntiProtons); } void processData(soa::Filtered>::iterator const& collision, aod::V0Datas const& V0s, @@ -4544,15 +4674,15 @@ struct LambdaProtonBalanceFunction { // ══ Reconstructed BF in matched selected MC events ══ void processMCRecBF(aod::McCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, - aod::V0Datas const& allV0s, - MyTracks const& allTracks) + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& /*mcParticles*/) { - // Unlabelled candidate tables preserve the complete data-like reco sample. - withMatchedBFEvent(mcCollision, collisions, allTracks, - [&](auto const& collision, MyTracks const& tracks, float cent, CentRho2Set* cs) { - auto V0s = allV0s.sliceBy(v0sPerCollisionData, collision.globalIndex()); - runRecoAnalysis(collision, V0s, tracks, cent, cs); - }); + withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollision, + [&](auto const& collision, MyTracksMC const& tracks, float cent, CentRho2Set* cs) { + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + runRecoAnalysis(collision, V0s, tracks, cent, cs, mcCollision.globalIndex()); + }); } PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCRecBF, "Reconstructed correlations in matched selected MC events", false); @@ -4677,83 +4807,6 @@ struct LambdaProtonBalanceFunction { PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCGenBF, "Generated correlations in matched selected MC events", false); - // Book only for the exclusive composition mode; reuse Other_Hists. - void bookMCComposition() - { - // Identical axes to the efficiency maps: reconstructed centrality, pT, rapidity. - // Equal binning does not imply equal selections or establish valid closure. - const AxisSpec axCent(cCentBins, "reconstructed centrality (%)"); - const AxisSpec axPt{60, 0.0, 6.0, "reconstructed p_{T} (GeV/c)"}; - const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "reconstructed y"}; - // First/last bins are reserved for PDG codes below -10000 / above +10000. - const AxisSpec axMother{20003, -10001.5, 10001.5, "signed direct-mother PDG (out-of-range codes mapped to endpoints)"}; - const std::array categories = {"AllCandidates", "PrimarySignal", "SecondarySignal", - "WrongSpeciesOrSign", "Unmatched", "OtherMCCollision"}; - for (const char* species : {"Lambda", "AntiLambda", "Proton", "AntiProton"}) { - const std::string base = std::string("MCComposition/") + species + "/"; - for (const char* category : categories) { - const std::string name = base + "h3_" + category; - registryOther.add(name.c_str(), name.c_str(), {HistType::kTH3F, {axCent, axPt, axY}}); - } - auto totals = registryOther.add((base + "hCategoryTotals").c_str(), - "Selected reconstructed candidates;Category;Candidates", - {HistType::kTH1F, {{6, 0.5, 6.5}}}); - for (int i = 0; i < 6; ++i) { - totals->GetXaxis()->SetBinLabel(i + 1, categories[i]); - } - for (const char* signalClass : {"PrimarySignal", "SecondarySignal"}) { - const std::string name = base + "hMotherPDG_" + signalClass; - registryOther.add(name.c_str(), "Direct-mother associations (not exclusive candidate sources)", - {HistType::kTHnSparseF, {axMother, axPt, axCent}}); - auto multiplicity = registryOther.add((base + "hMotherMultiplicity_" + signalClass).c_str(), - "Valid recorded direct mothers per signal candidate;Mothers;Candidates", - {HistType::kTH1F, {{3, -0.5, 2.5}}}); - multiplicity->GetXaxis()->SetBinLabel(1, "0"); - multiplicity->GetXaxis()->SetBinLabel(2, "1"); - multiplicity->GetXaxis()->SetBinLabel(3, "2 or more"); - } - } - for (const char* species : {"Proton", "AntiProton"}) { - const std::string name = std::string("MCComposition/") + species + "/hMcMask"; - registryOther.add(name.c_str(), "Selected track MC mask (diagnostic only);mcMask;Candidates", - {HistType::kTH1F, {{65536, -0.5, 65535.5}}}); - } - registryOther.add("MCComposition/hAcceptedEvents", "Accepted composition events, including zero candidates;Events;Counts", - {HistType::kTH1F, {{1, 0.0, 1.0}}}); - // Store raw counts only, for merging and later bin-by-bin fractions: - // fSecondaryCandidates = Secondary / All - // fSecondarySignal = Secondary / (Primary + Secondary) - // fPrimaryCandidates = Primary / All - // fSignal = (Primary + Secondary) / All - // These denominators are not interchangeable; no fractions are calculated here. - } - - template - void fillCompositionCategory(TPrefix prefix, int category, float cent, float pt, float y) - { - // Bins 2..6 match the five exclusive categories in hCategoryTotals. - registryOther.fill(prefix + HIST("hCategoryTotals"), static_cast(category + 1)); - switch (category) { - case 1: - registryOther.fill(prefix + HIST("h3_PrimarySignal"), cent, pt, y); - break; - case 2: - registryOther.fill(prefix + HIST("h3_SecondarySignal"), cent, pt, y); - break; - case 3: - registryOther.fill(prefix + HIST("h3_WrongSpeciesOrSign"), cent, pt, y); - break; - case 4: - registryOther.fill(prefix + HIST("h3_Unmatched"), cent, pt, y); - break; - case 5: - registryOther.fill(prefix + HIST("h3_OtherMCCollision"), cent, pt, y); - break; - default: - break; - } - } - static bool usableMCIndex(aod::McParticles const* table, int64_t id) { // has_mcParticle() tests only id >= 0 in ASoA; also check the bound table range. @@ -4761,136 +4814,11 @@ struct LambdaProtonBalanceFunction { id - static_cast(table->offset()) < table->size(); } - template - void fillCompositionMothers(TPrefix prefix, TMc const& mc, bool primary, float pt, float cent) - { - int nMothers = 0; - // MothersIds is the actual direct-mother array in AnalysisDataModel.h. - // mothers_as() dereferences every stored index without range checks. - // Read mothersIds() and its bound table instead, validating each index first. - const auto* mothers = static_cast(mc) - .getCurrentRaw() - .template get(); - if (mc.has_mothers() && mothers) { - for (const auto motherId : mc.mothersIds()) { - if (!usableMCIndex(mothers, motherId)) { - continue; - } - const auto mother = mothers->rawIteratorAt(motherId - static_cast(mothers->offset())); - const int pdg = mother.pdgCode(); - const int recordedPDG = pdg < -10000 ? -10001 : pdg > 10000 ? 10001 - : pdg; - if (primary) { - registryOther.fill(prefix + HIST("hMotherPDG_PrimarySignal"), recordedPDG, pt, cent); - } else { - registryOther.fill(prefix + HIST("hMotherPDG_SecondarySignal"), recordedPDG, pt, cent); - } - ++nMothers; - } - } - // Count mother associations, not exclusive candidate sources: totals can exceed - // particle counts. No recorded mother does not imply primary; having a mother - // does not imply secondary. Only isPhysicalPrimary() defines that classification. - if (primary) { - registryOther.fill(prefix + HIST("hMotherMultiplicity_PrimarySignal"), std::min(nMothers, 2)); - } else { - registryOther.fill(prefix + HIST("hMotherMultiplicity_SecondarySignal"), std::min(nMothers, 2)); - } - } - - template - void fillMCCompositionCandidate(TPrefix prefix, TCandidate const& candidate, - int expectedPDG, int64_t mcCollisionId, - float cent, float pt, float y) - { - // Count candidates as selected, without deduplicating by truth ID. Ordinary - // TH3 under/overflow is retained; no extra acceptance or rho-grid cut is applied. - registryOther.fill(prefix + HIST("h3_AllCandidates"), cent, pt, y); - registryOther.fill(prefix + HIST("hCategoryTotals"), 1.f); - if (!candidate.has_mcParticle()) { - fillCompositionCategory(prefix, 4, cent, pt, y); - return; - } - // The label column binds the full truth table, whereas the process argument - // can be sliced to the current MC collision. Use the binding so a valid label - // pointing to another collision is OtherMCCollision rather than Unmatched. - const auto* truth = static_cast(candidate).getCurrent(); - const int64_t id = candidate.mcParticleId(); - if (!usableMCIndex(truth, id)) { - fillCompositionCategory(prefix, 4, cent, pt, y); - return; - } - const auto mc = truth->rawIteratorAt(id - static_cast(truth->offset())); - if (mc.mcCollisionId() != mcCollisionId) { - fillCompositionCategory(prefix, 5, cent, pt, y); - return; - } - if (mc.pdgCode() != expectedPDG) { - fillCompositionCategory(prefix, 3, cent, pt, y); - return; - } - const bool primary = mc.isPhysicalPrimary(); - // SecondarySignal means correctly identified and non-primary, not material-produced. - fillCompositionCategory(prefix, primary ? 1 : 2, cent, pt, y); - fillCompositionMothers(prefix, mc, primary, pt, cent); - // One All entry and exactly one exclusive category entry per selected candidate: - // All = Primary + Secondary + WrongSpeciesOrSign + Unmatched + OtherMCCollision. - } - - void processMCComposition(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups const& collisions, - MyV0sMC const& allV0s, - MyTracksMC const& allTracks, - aod::McParticles const& /*mcParticles*/) - { - // The McParticles input supplies framework label bindings; no truth filtering - // enters either shared reconstructed selection. All fills have unit weight. - withMatchedEvent(mcCollision, collisions, allTracks, tracksPerCollision, - [&](auto const& collision, MyTracksMC const& tracks, float cent, CentRho2Set*) { - registryOther.fill(HIST("MCComposition/hAcceptedEvents"), 0.5f); - fillRunConfig(); - auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); - std::unordered_set v0DaughterTrackIds; - v0DaughterTrackIds.reserve(64); - std::vector lambdas, antiLambdas; - std::vector protons, antiProtons; - selectRecoV0s(collision, V0s, lambdas, antiLambdas, v0DaughterTrackIds); - // Reuse the pre-cleaning daughter set, including rejected shared-daughter V0s. - selectRecoProtons(tracks, v0DaughterTrackIds, protons, antiProtons); - for (const auto& v0 : lambdas) { - fillMCCompositionCandidate(HIST("MCComposition/Lambda/"), - v0, 3122, mcCollision.globalIndex(), cent, v0.pt(), v0.rapidity(1)); - } - for (const auto& v0 : antiLambdas) { - fillMCCompositionCandidate(HIST("MCComposition/AntiLambda/"), - v0, -3122, mcCollision.globalIndex(), cent, v0.pt(), v0.rapidity(2)); - } - // McTrackLabels::mcMask() is uint16_t: set bits indicate mismatches; - // bits 0..6 ITS layers, 12 ITSAB tracklet, 13 ITS-TPC, 14 noise, 15 fake. - // Record the complete mask for every selected track, never use it as a cut. - for (const auto& trk : protons) { - registryOther.fill(HIST("MCComposition/Proton/hMcMask"), trk.mcMask()); - fillMCCompositionCandidate(HIST("MCComposition/Proton/"), - trk, 2212, mcCollision.globalIndex(), cent, trk.pt(), protonRapidity(trk)); - } - for (const auto& trk : antiProtons) { - registryOther.fill(HIST("MCComposition/AntiProton/hMcMask"), trk.mcMask()); - fillMCCompositionCandidate(HIST("MCComposition/AntiProton/"), - trk, -2212, mcCollision.globalIndex(), cent, trk.pt(), protonRapidity(trk)); - } - }); - } - PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCComposition, - "Reconstructed MC candidate composition and mother origins", false); - // ── Run-level summary printed once at the end of the analysis job ───── // Called automatically by the O2 framework after all events are processed. // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. void endOfStream(EndOfStreamContext const&) { - if (doprocessMCComposition.value) { - return; // Composition persists raw counts only; no BF veto-fraction summary. - } const int64_t totalBefore = state.runTotalProtonBeforeVeto; const int64_t totalRemoved = state.runTotalProtonRemovedVeto; const int64_t totalAfter = totalBefore - totalRemoved; From 01f99a246b3ac47b2affc6721bcc9367f7b8dca6 Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Sun, 8 Nov 2026 11:32:51 +0200 Subject: [PATCH 606/648] =?UTF-8?q?[PWGCF]=20Extend=20the=20Fox=E2=80=93Wo?= =?UTF-8?q?lfram=20analysis=20with=20associated-particle=20pT=20spectra=20?= =?UTF-8?q?and=20mean-pT=20profiles=20vs=20multip=20(#18650)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Iulian --- .../Tasks/FoxWolframCorrelation.cxx | 343 ++++++++++++------ 1 file changed, 229 insertions(+), 114 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx index 96db156d9ab..7cd6f5fdf87 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -14,7 +14,7 @@ /// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. /// Classifies events by topology (triangular), fills correlation histograms, /// and produces custom SOA tables for mixed-event background estimation. -/// \since 29 Oct 2026 +/// \since 06 Nov 2026 #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -141,10 +141,13 @@ auto findLeadingPart(T const& tracks) bool foundPart = false; for (const auto& track : tracks) { + // pT Leading (no constraint) if (track.pt() > ptMaxAll) { ptMaxAll = track.pt(); leadingAllId = i; } + + // pT Leading in (2,3) if (track.pt() > 2.f && track.pt() < 3.f && track.pt() > ptMaxPart) { ptMaxPart = track.pt(); leadingPartId = i; @@ -155,6 +158,7 @@ auto findLeadingPart(T const& tracks) return std::make_tuple(tracks.iteratorAt(leadingAllId), tracks.iteratorAt(leadingPartId), foundPart); } + // Store selected tracks and collisions across data frames. using TrackTuple = std::tuple; using CollisionTuple = std::tuple>; @@ -174,6 +178,8 @@ struct FoxWolframCorrelation { Produces triTracks; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + //// Separate the 120 folded spectra to stay below the registry capacity //// + HistogramRegistry foldedHistos{"foldedHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; // Configure histogram axes, event selection, and topology. Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; @@ -239,6 +245,27 @@ struct FoxWolframCorrelation { "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + //// Class-level names for compile-time HIST lookups in nested loops //// + static constexpr std::array nearNames = { + "nearPtD0_", "nearPtD1_", "nearPtD2_", + "nearPtD3_", "nearPtD4_", "nearPtD5_"}; + + static constexpr std::array awayNames = { + "awayPtD0_", "awayPtD1_", "awayPtD2_", + "awayPtD3_", "awayPtD4_", "awayPtD5_"}; + + static constexpr std::array allLRNames = { + "allLRNear", "allLRValley", "allLRAway"}; + + static constexpr std::array allSRNames = { + "allSRNear", "allSRValley", "allSRAway"}; + + static constexpr std::array triLRNames = { + "triLRPeak1", "triLRPeak2", "triLRPeak3", "triLRValleys"}; + + static constexpr std::array triSRNames = { + "triSRPeak1", "triSRPeak2", "triSRPeak3", "triSRValleys"}; + // Map each unique pair of Fox-Wolfram moments to its histogram. static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; @@ -261,6 +288,9 @@ struct FoxWolframCorrelation { LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); } nMultBins = static_cast(multLower.value.size()); + if (nMultBins < 3) { + LOGF(fatal, "At least three broad multiplicity ranges are required"); + } for (int i = 0; i < nMultBins; ++i) { if (multLower.value[i] > multUpper.value[i]) { LOGF(fatal, "Multiplicity bin %d has multLower (%d) > multUpper (%d)", i + 1, multLower.value[i], multUpper.value[i]); @@ -387,7 +417,13 @@ struct FoxWolframCorrelation { histos.add(fmt::format("Spectra/etaphiTriEv_BIN{}", i).c_str(), fmt::format("etaphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); histos.add(fmt::format("Spectra/ptphiTriEv_BIN{}", i).c_str(), fmt::format("ptphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); - // onPeaks and onValleys pt spectra - ALL EVENTS + //// Six near spectra and six combined 120/240-degree spectra per multiplicity bin //// + for (int angularBin = 0; angularBin < 6; ++angularBin) { + foldedHistos.add(fmt::format("triEvFoldedLR/nearPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR near: {}-{} deg from 0", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); + foldedHistos.add(fmt::format("triEvFoldedLR/awayPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR away: {}-{} deg from 120 and 240", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); + } + + //// ALL: pT spectra (TH1D) and mean associated pT versus multiplicity (TProfile) //// histos.add(fmt::format("allEvPkVall/allLRNearPt_BIN{}", i).c_str(), fmt::format("allLRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); histos.add(fmt::format("allEvPkVall/allLRNearPtMultip_BIN{}", i).c_str(), fmt::format("allLRNearPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); @@ -404,7 +440,7 @@ struct FoxWolframCorrelation { histos.add(fmt::format("allEvPkVall/allSRAwayPt_BIN{}", i).c_str(), fmt::format("allSRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); histos.add(fmt::format("allEvPkVall/allSRAwayPtMultip_BIN{}", i).c_str(), fmt::format("allSRAwayPtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); - // onPeaks and onValleys pt spectra - TRI EVENTS + //// TRI: three peaks and two valleys combined into one spectrum/profile //// histos.add(fmt::format("triEvPkVall/triLRPeak1Pt_BIN{}", i).c_str(), fmt::format("triLRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); histos.add(fmt::format("triEvPkVall/triLRPeak1PtMultip_BIN{}", i).c_str(), fmt::format("triLRPeak1PtMultip_BIN{}", i).c_str(), kTProfile, {axisMultip}); histos.add(fmt::format("triEvPkVall/triSRPeak1Pt_BIN{}", i).c_str(), fmt::format("triSRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); @@ -619,16 +655,66 @@ struct FoxWolframCorrelation { }); // per Multip Bin } - // Fill same-event pair distributions for one multiplicity interval. + //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// + //// Avoids the GCC 14 ICE in tsubst_expr caused by nested generic static_for lambdas //// + template + void fillFoldedBin(double deltaPhi, double pt) + { + const double degToRad = constants::math::PI / 180.; + + //// Adjacent windows include the lower edge and exclude the upper edge //// + static_for<0, 5>([&](auto j) { + constexpr int angularBin = j.value; + constexpr double lo = 10. * angularBin; + constexpr double hi = 10. * (angularBin + 1); + + //// Combine the two sides of the near peak at zero //// + const bool inNear = (deltaPhi > -hi * degToRad && deltaPhi < -lo * degToRad) || (deltaPhi > lo * degToRad && deltaPhi < hi * degToRad); + + if (inNear) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(nearNames[angularBin]) + HIST(binNames[Idx]), pt); + } + + //// Combine both sides of 120 and 240 degrees //// + //// The final window maps angles above 270 degrees to negative deltaPhi //// + const bool inAway = (deltaPhi > (120. - hi) * degToRad && deltaPhi < (120. - lo) * degToRad) || (deltaPhi > (120. + lo) * degToRad && deltaPhi < (120. + hi) * degToRad) || + (deltaPhi > (240. - hi) * degToRad && deltaPhi < (240. - lo) * degToRad) || (deltaPhi > (240. + lo) * degToRad && deltaPhi < (240. + hi) * degToRad) || + (deltaPhi > (240. + lo - 360.) * degToRad && deltaPhi < (240. + hi - 360.) * degToRad); + + if (inAway) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(awayNames[angularBin]) + HIST(binNames[Idx]), pt); + } + }); + } + + //// TRI LR pT spectra in six symmetric angular intervals //// template - void fillSameEventHist(int binIndex, - int nTracks, - Track const& associatedTrack, - double deltaPhi, - double deltaEta, - bool isLeadAssocPair) + void fillSameEventFoldedLR(int binIndex, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) { + //// Event topology is already selected by the caller //// + const bool isAnyTriMode = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; + + if (!isAnyTriMode || leadAssocMode.value != kpTLeadFree || !isLeadAssocPair) { + return; + } + //// Keep only 1.25 < |deltaEta| < 1.60 //// + const double absEta = std::abs(deltaEta); + if (absEta <= 1.25 || absEta >= 1.60) { + return; + } + + //// Fill only the requested multiplicity interval //// static_for<0, 9>([&](auto i) { constexpr int idx = i.value; @@ -636,126 +722,137 @@ struct FoxWolframCorrelation { return; } - if (!isLeadAssocPair) { - return; - } - - const double degToRad = constants::math::PI / 180.; - const double absDeltaEta = std::abs(deltaEta); + fillFoldedBin(deltaPhi, associatedTrack.pt()); + }); + } - const bool inLongRange = absDeltaEta > 1.25 && absDeltaEta < 1.6; - const bool inShortRange = absDeltaEta < 0.35; + //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// + template + void fillPtBin(bool isAll, bool isTri, + const std::array& allWindows, const std::array& triWindows, + bool inLR, bool inSR, int nTracks, double pt) + { + //// Fill ALL spectra and profiles with one entry per accepted pair //// + if (isAll) { + static_for<0, 2>([&](auto j) { + constexpr int region = j.value; - /// for ALL Ev. - const bool onNearPeak = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; - const bool onBetweenNearAway = deltaPhi > 75. * degToRad && deltaPhi < 105. * degToRad; - const bool onAwayPeak = deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad; + if (!allWindows[region]) { + return; + } - if (topologyMode.value == kAllEvSetNom && leadAssocMode.value == 2) { // for the case with ALL sel Ev + if (inLR) { + histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + } - if (onNearPeak) { // NEAR SIDE - if (inLongRange) { - histos.fill(HIST("allEvPkVall/allLRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allLRNearPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - if (inShortRange) { - histos.fill(HIST("allEvPkVall/allSRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allSRNearPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + if (inSR) { + histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); } + }); + } - if (onBetweenNearAway) { // BETWEEN SIDE - if (inLongRange) { - histos.fill(HIST("allEvPkVall/allLRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allLRValleyPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - if (inShortRange) { - histos.fill(HIST("allEvPkVall/allSRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allSRValleyPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + //// Fill TRI spectra and profiles with one entry per accepted pair //// + if (isTri) { + static_for<0, 3>([&](auto j) { + constexpr int region = j.value; + + if (!triWindows[region]) { + return; } - if (onAwayPeak) { // AWAY SIDE - if (inLongRange) { - histos.fill(HIST("allEvPkVall/allLRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allLRAwayPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - if (inShortRange) { - histos.fill(HIST("allEvPkVall/allSRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("allEvPkVall/allSRAwayPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + if (inLR) { + histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); } - } // for ALL Events - - /// for TRI sel Ev. - const bool onPeak1 = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; - const bool onPeak2 = deltaPhi > 105. * degToRad && deltaPhi < 135. * degToRad; - const bool onPeak3 = deltaPhi > 225. * degToRad && deltaPhi < 255. * degToRad; - const bool onValleys = (deltaPhi > 45. * degToRad && deltaPhi < 75. * degToRad) || (deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad); - - const bool isAnyTriMode = topologyMode.value == kTriEvSetNom || - topologyMode.value == kTriEvSet1p || - topologyMode.value == kTriEvSet2p || - topologyMode.value == kTriEvSet3p || - topologyMode.value == kTriEvSet1m || - topologyMode.value == kTriEvSet2m || - topologyMode.value == kTriEvSet3m; - - if (isAnyTriMode && leadAssocMode.value == 2) { // for the case with TRI sel Ev (any Sel Filter) - - if (onPeak1) { // on Peak at 0 - if (inLongRange) { - histos.fill(HIST("triEvPkVall/triLRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triLRPeak1PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - if (inShortRange) { - histos.fill(HIST("triEvPkVall/triSRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triSRPeak1PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + + if (inSR) { + histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); } + }); + } + } - if (onPeak2) { // on Peak at 120 + //// Fixed-window pT spectra and mean-pT profiles for ALL and TRI //// + template + void fillSameEventPtHist(int binIndex, + int nTracks, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + if (!isLeadAssocPair || leadAssocMode.value != kpTLeadFree) { + return; + } - if (inLongRange) { - histos.fill(HIST("triEvPkVall/triLRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triLRPeak2PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + //// Associated pT and LR/SR pair selections //// + const double pt = associatedTrack.pt(); + const double absEta = std::abs(deltaEta); + const double degToRad = constants::math::PI / 180.; - if (inShortRange) { - histos.fill(HIST("triEvPkVall/triSRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triSRPeak2PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - } + const bool inLR = absEta > 1.25 && absEta < 1.60; + const bool inSR = absEta < 0.35; - if (onPeak3) { // on Peak at 240 + //// Preserve the original open angular windows //// + const auto inPhi = [&](double lo, double hi) { + return deltaPhi > lo * degToRad && deltaPhi < hi * degToRad; + }; - if (inLongRange) { - histos.fill(HIST("triEvPkVall/triLRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triLRPeak3PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + //// Event topology //// + const bool isAll = topologyMode.value == kAllEvSetNom; - if (inShortRange) { - histos.fill(HIST("triEvPkVall/triSRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triSRPeak3PtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - } + const bool isTri = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; - if (onValleys) { - // Combined valleys at 60 and 180 degrees // - if (inLongRange) { - histos.fill(HIST("triEvPkVall/triLRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triLRValleysPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } + //// ALL: near, between, away //// + const std::array allWindows = { + inPhi(-15., 15.), inPhi(75., 105.), inPhi(165., 195.)}; - if (inShortRange) { - histos.fill(HIST("triEvPkVall/triSRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); - histos.fill(HIST("triEvPkVall/triSRValleysPtMultip_") + HIST(binNames[idx]), nTracks, associatedTrack.pt()); - } - } + //// TRI: peaks at 0, 120, 240 degrees and the two combined valleys //// + const std::array triWindows = { + inPhi(-15., 15.), inPhi(105., 135.), inPhi(225., 255.), inPhi(45., 75.) || inPhi(165., 195.)}; + + //// Fill only the requested multiplicity interval //// + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } - } // for TRI-like Events + fillPtBin(isAll, isTri, allWindows, triWindows, inLR, inSR, nTracks, pt); + }); + } - // Same Event - Corr fct + //// Same-event pair counts and angular correlations only //// + template + void fillSameEventHist(int binIndex, + Track const& /*associatedTrack*/, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + if (!isLeadAssocPair) { + return; + } + + //// Count the pair and fill its original angular coordinates //// histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); @@ -840,7 +937,7 @@ struct FoxWolframCorrelation { const bool isTriSet2p = (H1 < kTriFWMSet2p.value[0]) && (H2 < kTriFWMSet2p.value[1]) && (H3 > kTriFWMSet2p.value[2]) && (H4 < kTriFWMSet2p.value[3]) && (H5 > kTriFWMSet2p.value[4]) && (H6 < kTriFWMSet2p.value[5]) && (H7 > kTriFWMSet2p.value[6]) && (H8 < kTriFWMSet2p.value[7]); const bool isTriSet3p = (H1 < kTriFWMSet3p.value[0]) && (H2 < kTriFWMSet3p.value[1]) && (H3 > kTriFWMSet3p.value[2]) && (H4 < kTriFWMSet3p.value[3]) && (H5 > kTriFWMSet3p.value[4]) && (H6 < kTriFWMSet3p.value[5]) && (H7 > kTriFWMSet3p.value[6]) && (H8 < kTriFWMSet3p.value[7]); - // Apply three restrictively looser triangular selections + // Apply three progressively tighter triangular selections const bool isTriSet1m = (H1 < kTriFWMSet1m.value[0]) && (H2 < kTriFWMSet1m.value[1]) && (H3 > kTriFWMSet1m.value[2]) && (H4 < kTriFWMSet1m.value[3]) && (H5 > kTriFWMSet1m.value[4]) && (H6 < kTriFWMSet1m.value[5]) && (H7 > kTriFWMSet1m.value[6]) && (H8 < kTriFWMSet1m.value[7]); const bool isTriSet2m = (H1 < kTriFWMSet2m.value[0]) && (H2 < kTriFWMSet2m.value[1]) && (H3 > kTriFWMSet2m.value[2]) && (H4 < kTriFWMSet2m.value[3]) && (H5 > kTriFWMSet2m.value[4]) && (H6 < kTriFWMSet2m.value[5]) && (H7 > kTriFWMSet2m.value[6]) && (H8 < kTriFWMSet2m.value[7]); const bool isTriSet3m = (H1 < kTriFWMSet3m.value[0]) && (H2 < kTriFWMSet3m.value[1]) && (H3 > kTriFWMSet3m.value[2]) && (H4 < kTriFWMSet3m.value[3]) && (H5 > kTriFWMSet3m.value[4]) && (H6 < kTriFWMSet3m.value[5]) && (H7 > kTriFWMSet3m.value[6]) && (H8 < kTriFWMSet3m.value[7]); @@ -893,7 +990,8 @@ struct FoxWolframCorrelation { histos.fill(HIST("pTSpectra/ptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); histos.fill(HIST("pTSpectra/meanptLeadMultipAllEv"), nGlobalTracks, triggTrk.pt()); - if (isSelectedTopology) { // for Tri-like Ev. + //// Inclusive spectra for the configured selected topology //// + if (isSelectedTopology) { histos.fill(HIST("Multip/multipTriEv"), nGlobalTracks); histos.fill(HIST("pTSpectra/RecoilTriEv"), nGlobalTracks, nRecoil); histos.fill(HIST("pTSpectra/pTLeadTriEv"), triggTrk.pt()); @@ -913,7 +1011,7 @@ struct FoxWolframCorrelation { if (!isSelectedTopology) { continue; - } // for Tri-like Ev. + } histos.fill(HIST("pTSpectra/pTAssocTriEv"), track.pt()); histos.fill(HIST("pTSpectra/meanpTAssocMultipTriEv"), nGlobalTracks, track.pt()); @@ -938,7 +1036,12 @@ struct FoxWolframCorrelation { for (int i = 0; i < nMultBins; ++i) { if (inBin[i]) { - fillSameEventHist(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); + //// Angular correlations for the selected topology //// + fillSameEventHist(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + //// Fixed-window spectra and mean-pT profiles //// + fillSameEventPtHist(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); + //// TRI LR spectra in symmetric 10-degree windows //// + fillSameEventFoldedLR(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); } } } @@ -1070,6 +1173,18 @@ struct FoxWolframCorrelationMixing { axisMult1.value, axisMult2.value, axisMult3.value, axisMult4.value, axisMult5.value, axisMult6.value, axisMult7.value, axisMult8.value, axisMult9.value, axisMult10.value}; + //// Each mixing axis must cover its configured integer multiplicity interval //// + for (int i = 0; i < nMultBins; ++i) { + const auto& edges = axisEdges[i]; + const double lo = multLower.value[i] - 0.5; + const double hi = multUpper.value[i] + 0.5; + if (multLower.value[i] > multUpper.value[i] || edges.size() < 2 || + std::abs(edges[edges.size() - 2] - lo) > 1.e-6 || + std::abs(edges.back() - hi) > 1.e-6) { + LOGF(fatal, "axisMult%d must match multiplicity limits [%.1f, %.1f]", i + 1, lo, hi); + } + } + pools = {&pairBIN1, &pairBIN2, &pairBIN3, &pairBIN4, &pairBIN5, &pairBIN6, &pairBIN7, &pairBIN8, &pairBIN9, &pairBIN10}; From 88eb4931f50f414741dd31d6e1846d4978d854bd Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo <96481191+Marcellocosti@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:28:02 +0100 Subject: [PATCH 607/648] [PWGHF] taskCorrelationsEfficiencies: apply selections on generated tracks and collisions (#18633) Co-authored-by: Marcello Di Costanzo Co-authored-by: ALICE Action Bot --- .../derivedDataCreatorCorrelationsReduced.cxx | 97 ++++++------ .../Tasks/taskCorrelationsEfficiencies.cxx | 85 +++++++---- PWGHF/HFC/Utils/utilsFlow.h | 141 ++++++++++++------ 3 files changed, 199 insertions(+), 124 deletions(-) diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 3efa36a6d9f..73157c06d08 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -17,7 +17,6 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" -#include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -33,7 +32,6 @@ #include #include -#include #include #include #include @@ -157,7 +155,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { void init(InitContext&) { - hfEvSel.addHistograms(registry); // collision monitoring + hfEvSel.init(registry); + ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -176,7 +175,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } - hfcFlowHelper.init(ccdb, registry, classesMl, yCandMax.value); + hfcFlowHelper.init(classesMl, yCandMax.value); const AxisSpec axisTpcSignal = {binsTpcSignal, "TPC Signal"}; const AxisSpec axisCent = {binsCent, "Centrality"}; @@ -209,32 +208,6 @@ struct HfDerivedDataCreatorCorrelationsReduced { } }; // end init - /// Check event selections for collision and fill the collision table - /// \param collision is the collision - template - bool checkCollision(Coll const& collision, float& cent, float& mult) - { - o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; - if (centEstimator == CentralityEstimator::FT0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0A(); - } else if (centEstimator == CentralityEstimator::FT0C) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0C(); - } else if (centEstimator == CentralityEstimator::FT0M) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFT0M(); - } else if (centEstimator == CentralityEstimator::FV0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); - mult = collision.multFV0A(); - } else { - LOG(fatal) << "Centrality estimator not recognized for collision selection"; - std::abort(); - } - hfEvSel.fillHistograms(collision, collRejMask, cent); - return collRejMask == 0; - } - /// Checks if the trigger cand-associated track pair can be accepted for SE correlation /// \param assTrk is the associated track /// \param cand is the trigger candidate @@ -404,7 +377,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -421,7 +395,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -441,7 +416,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -461,7 +437,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -482,7 +459,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -502,7 +480,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -523,7 +502,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -543,7 +523,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -564,7 +545,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -582,7 +564,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -602,7 +585,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -622,7 +606,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -643,7 +628,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -663,7 +649,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -684,7 +671,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -704,7 +692,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -720,7 +709,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { TracksData const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -733,7 +723,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { TracksData const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -747,7 +738,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { TracksMc const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -760,7 +752,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { TracksMc const& tracks) { float cent{-1.}, mult{-1.}; - if (!checkCollision(coll, cent, mult)) { + auto centDetector = static_cast(centEstimator.value); + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); @@ -769,7 +762,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processMcHadronHadronME, "Process Mixed Event for hadron candidates", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx index 8407dcbde82..365920f243b 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationsEfficiencies.cxx @@ -14,13 +14,14 @@ /// \author Marcello Di Costanzo , Politecnico and INFN Torino #include "PWGHF/Core/CentralityEstimation.h" -#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" #include "PWGHF/HFC/Utils/utilsFlow.h" #include "PWGHF/Utils/utilsEvSelHf.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -29,20 +30,22 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include -#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include +#include +#include #include #include @@ -127,13 +130,17 @@ struct HfTaskCorrelationsEfficiencies { Configurable downSampleCharm{"downSampleCharm", 1., "Fraction of pairs to keep"}; Configurable ptMaxDownSampleCharm{"ptMaxDownSampleCharm", 10., "Maximum pt for the application of the downsampling factor"}; + HfEventSelection hfEvSel; // event selection and monitoring + HfEventSelectionMc hfEvSelMc; HfcFlowHelper hfcFlowHelper; // flow utilities o2::framework::Service ccdb{}; SliceCache cache; std::unique_ptr funcDcaXYPtCutPrimTrk{nullptr}; - using RecoCollisions = soa::Join; + using BCsInfo = soa::Join; + using McCollisionsWMult = soa::Join; + using RecoCollisions = soa::Join; using McCandsDs = soa::Filtered>; using McCandsDplus = soa::Filtered>; using McCandsD0 = soa::Filtered>; @@ -152,6 +159,7 @@ struct HfTaskCorrelationsEfficiencies { Preslice candsLcPerColl = aod::hf_cand::collisionId; Preslice trackIndicesPerColl = aod::track::collisionId; + PresliceUnsorted mcPartsPerMcCollision = aod::mcparticle::mcCollisionId; PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; ConfigurableAxis binsPtCharm{"binsPtCharm", {VARIABLE_WIDTH, 0., 2., 4., 6., 8., 24.}, ""}; @@ -165,9 +173,18 @@ struct HfTaskCorrelationsEfficiencies { HistogramRegistry registry{"registry", {}}; - void init(InitContext&) + void init(InitContext& initContext) { + hfEvSel.init(registry); + const auto& workflows = initContext.services().get(); + for (const DeviceSpec& device : workflows.devices) { + if (device.name == "hf-task-correlations-efficiencies") { + // init HF event selection helper + hfEvSelMc.init(device, registry); + break; + } + } ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -186,7 +203,7 @@ struct HfTaskCorrelationsEfficiencies { return; } - hfcFlowHelper.init(ccdb, registry, classesMl, yCandMax.value); + hfcFlowHelper.init(classesMl, yCandMax.value); // Setup pt-dependent DCAxy cut function if (usePtDiffDcaXYCut) { @@ -304,7 +321,8 @@ struct HfTaskCorrelationsEfficiencies { continue; } float cent{-1.}, mult{-1.}; - if (!hfcFlowHelper.checkCollision(coll, cent, mult)) { + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { continue; } fillCharmEfficiencies(candsInColl, coll.numContrib()); @@ -357,20 +375,27 @@ struct HfTaskCorrelationsEfficiencies { void processEffAssTracks(RecoCollisions const& recoCollisions, McTracks const& recoTracks, aod::McParticles const& genParticles, - aod::McCollisions const&) + McCollisionsWMult const& genCollisions, + BCsInfo const&) { for (const auto& coll : recoCollisions) { float cent{-1.}, mult{-1.}; - if (!hfcFlowHelper.checkCollision(coll, cent, mult)) { + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectRecoCollision(hfEvSel, ccdb, registry, coll, centDetector, cent, mult)) { continue; } if (!coll.has_mcCollision()) { continue; } - auto mcCollision = coll.template mcCollision_as(); + auto mcCollision = coll.template mcCollision_as(); auto tracksInColl = recoTracks.sliceBy(trackIndicesPerColl, coll.globalIndex()); for (const auto& assTrk : tracksInColl) { + + if (!assTrk.isGlobalTrackWoDCA() || assTrk.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) { + continue; + } + if (!assTrk.has_mcParticle()) { continue; } @@ -410,29 +435,39 @@ struct HfTaskCorrelationsEfficiencies { } // Fill histograms for generated associated tracks - for (const auto& genTrk : genParticles) { - int matchedSpecie = getAssocTrkSpecie(genTrk); - if (matchedSpecie < 0) { - continue; - } - if (!genTrk.has_mcCollision()) { - continue; - } + for (const auto& mcColl : genCollisions) { - // Pt and eta selection - if (genTrk.pt() < ptTrkMin || genTrk.pt() > ptTrkMax || std::fabs(genTrk.eta()) > etaTrkMax) { + float cent{-1.}, mult{-1.}; + const auto recoColls = recoCollisions.sliceBy(colPerMcCollision, mcColl.globalIndex()); + CentralityEstimator centDetector = CentralityEstimator::None; + if (!hfcFlowHelper.selectGenCollision(hfEvSelMc, mcColl, recoColls, centDetector, cent, mult)) { continue; } - int genTrkOrigin = genTrk.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary - auto genCollision = genTrk.template mcCollision_as(); - registry.fill(HIST("hAssTrackGen"), genTrk.pt(), genTrk.eta(), genTrk.phi(), genCollision.posZ(), matchedSpecie, genTrkOrigin); + const auto groupedMcParticles = genParticles.sliceBy(mcPartsPerMcCollision, mcColl.globalIndex()); + for (const auto& genTrk : groupedMcParticles) { + int matchedSpecie = getAssocTrkSpecie(genTrk); + if (matchedSpecie < 0) { + continue; + } + if (!genTrk.has_mcCollision()) { + // Should never happen because we group on the generated collisions + continue; + } + + // Pt and eta selection + if (genTrk.pt() < ptTrkMin || genTrk.pt() > ptTrkMax || std::fabs(genTrk.eta()) > etaTrkMax) { + continue; + } + int genTrkOrigin = genTrk.isPhysicalPrimary() ? 0 : 1; // 0 for primary, 1 for secondary + registry.fill(HIST("hAssTrackGen"), genTrk.pt(), genTrk.eta(), genTrk.phi(), mcColl.posZ(), matchedSpecie, genTrkOrigin); + } } } PROCESS_SWITCH(HfTaskCorrelationsEfficiencies, processEffAssTracks, "Process efficiencies for associated tracks", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGHF/HFC/Utils/utilsFlow.h b/PWGHF/HFC/Utils/utilsFlow.h index 52b4885bfe4..8d345728769 100644 --- a/PWGHF/HFC/Utils/utilsFlow.h +++ b/PWGHF/HFC/Utils/utilsFlow.h @@ -17,31 +17,20 @@ #define PWGHF_HFC_UTILS_UTILSFLOW_H_ #include "PWGHF/Core/CentralityEstimation.h" -#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Utils/utilsEvSelHf.h" -#include "Common/Core/RecoDecay.h" - #include -#include -#include #include #include #include -#include -#include #include -#include -#include #include #include -#include - #include -#include -#include +#include +#include #include #include @@ -79,18 +68,12 @@ class HfcFlowHelper /// \param histRegistry is the histogram registry /// \param mlClasses are the machine learning classes /// \param maxRapidity is the maximum rapidity - void init(o2::framework::Service& ccdbService, - o2::framework::HistogramRegistry& histRegistry, - std::vector mlClasses, + void init(std::vector mlClasses, float maxRapidity) { - - LOG(info) << "Initializing HFC flow helper!"; - this->hfEvSel.addHistograms(histRegistry); - this->ccdb = &ccdbService; - this->registry = &histRegistry; this->classMl = std::move(mlClasses); this->yCandMax = maxRapidity; + LOG(info) << "HFC flow helper initialized"; } /// Get charm hadron candidate mass @@ -168,6 +151,7 @@ class HfcFlowHelper /// Cut on rapidity of the candidate /// \param candidate is the charm hadron candidate + /// \return true if the candidate passes the rapidity cut, false otherwise template bool cutCandRapidity(const TCand& candidate) { @@ -186,40 +170,103 @@ class HfcFlowHelper return std::fabs(y) < yCandMax; } - /// Check event selections for collision and fill the collision table + /// Runtime dispatch to the compile-time checkCollision /// \param collision is the collision - template - bool checkCollision(Coll const& collision, float& cent, float& mult) + /// \param centEst is the centrality estimator + /// \param cent is the centrality value + /// \param mult is the multiplicity value + template + bool selectGenCollision(o2::hf_evsel::HfEventSelectionMc& hfEvSelMc, + GenColl const& genColl, + RecoCollSlice const& recoCollsSlice, + o2::hf_centrality::CentralityEstimator centEst, + float& cent, + float& mult) { + int nSplitColl = recoCollsSlice.size(); o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; - if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); - mult = collision.multFT0A(); - } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0C) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); - mult = collision.multFT0C(); - } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FT0M) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); - mult = collision.multFT0M(); - } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::FV0A) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); - mult = collision.multFV0A(); - } else if constexpr (CentEst == o2::hf_centrality::CentralityEstimator::None) { - collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, *ccdb, *registry); - mult = -1.f; - } else { - LOG(fatal) << "Centrality estimator not recognized for collision selection"; - std::abort(); + switch (centEst) { + case o2::hf_centrality::CentralityEstimator::FT0A: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0A); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FT0C: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0C); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FT0M: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FT0M); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::FV0A: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = o2::hf_centrality::getCentralityGenColl(recoCollsSlice, o2::hf_centrality::CentralityEstimator::FV0A); + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + case o2::hf_centrality::CentralityEstimator::None: + collRejMask = hfEvSelMc.getHfMcCollisionRejectionMask(genColl, recoCollsSlice, cent); + mult = -1.f; + hfEvSelMc.fillHistograms(genColl, collRejMask, nSplitColl); + break; + default: + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + std::abort(); } + + return (collRejMask == 0); + } + + /// Apply event selection on reconstructed collisions + /// \param collision is the collision + /// \param centEst is the centrality estimator + /// \param cent is the centrality value + /// \param mult is the multiplicity value + template + bool selectRecoCollision(o2::hf_evsel::HfEventSelection& hfEvSel, + o2::framework::Service& ccdb, + o2::framework::HistogramRegistry& registry, + Coll const& collision, + o2::hf_centrality::CentralityEstimator centEst, + float& cent, + float& mult) + { + o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; + switch (centEst) { + case o2::hf_centrality::CentralityEstimator::FT0A: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0A(); + break; + case o2::hf_centrality::CentralityEstimator::FT0C: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0C(); + break; + case o2::hf_centrality::CentralityEstimator::FT0M: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFT0M(); + break; + case o2::hf_centrality::CentralityEstimator::FV0A: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = collision.multFV0A(); + break; + case o2::hf_centrality::CentralityEstimator::None: + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + mult = -1.f; + break; + default: + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + std::abort(); + } + hfEvSel.fillHistograms(collision, collRejMask, cent); - return collRejMask == 0; + + return (collRejMask == 0); } private: - o2::hf_evsel::HfEventSelection hfEvSel; - o2::framework::Service* ccdb{nullptr}; - o2::framework::HistogramRegistry* registry{nullptr}; - std::vector classMl{}; + std::vector classMl; float yCandMax{-999.f}; }; From c87ee2d2da63af2f60aeba0b051143c9f34b3018 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Sun, 8 Nov 2026 15:29:23 +0100 Subject: [PATCH 608/648] [PWGLF] Add additional QA plot for zdc calibration (#18653) --- .../Common/zdc2stagecalibration.cxx | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index 7462f157c7f..effa33e1b0a 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -166,6 +166,7 @@ struct zdc2stagecalibration { AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; AxisSpec qTwistMomentAxis = {10, 0.0, 10.0, "Q moments (A: 0-4, C: 5-9)"}; AxisSpec qCorrelationComponentAxis = {4, 0.0, 4.0, "A-C correlation component"}; + AxisSpec phiHarmonicAxis = {8, 0.0, 8.0, "cos1,sin1,...,cos4,sin4"}; AxisSpec qValueAxis = {200, -4.0, 4.0, "Q"}; histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0.0, 10.0}}); @@ -311,6 +312,33 @@ struct zdc2stagecalibration { histos.add("QCorrelationQA/pQACorrelationBeforeVsCentrality", "A-C Q correlations before recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); histos.add("QCorrelationQA/pQACorrelationAfterVsCentrality", "A-C Q correlations after recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); + // Final-calibration QA: only filled after recentering, width and twist. + histos.add("final_calibration/hQxAVsCentrality", "Final QxA vs centrality;centrality (%);Q_{x}^{A}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQyAVsCentrality", "Final QyA vs centrality;centrality (%);Q_{y}^{A}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQxCVsCentrality", "Final QxC vs centrality;centrality (%);Q_{x}^{C}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/hQyCVsCentrality", "Final QyC vs centrality;centrality (%);Q_{y}^{C}", kTH2F, {centralityAxis, qValueAxis}); + histos.add("final_calibration/pQxAVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxCVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyCVsCentrality", "Final vs centrality;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/hPhiAVsCentrality", "Final ZNA #phi vs centrality;centrality (%);#phi_{A}", kTH2F, {centralityAxis, phiAxis}); + histos.add("final_calibration/hPhiCVsCentrality", "Final ZNC #phi vs centrality;centrality (%);#phi_{C}", kTH2F, {centralityAxis, phiAxis}); + histos.add("final_calibration/pCosPsiAPsiCVsCentrality", "Final A-C plane correlation;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pCosPsiAPsiCVsCentralityVz", "Final A-C plane correlation vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQxAQxCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAQyCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxAQyCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQyAQxCVsCentrality", "Final ;centrality (%);", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQxAVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQyAVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQxCVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQyCVsCentralityVz", "Final vs centrality and v_{z};centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, resolutionVzAxis}); + histos.add("final_calibration/pQMomentsVsCentrality", "Final Q moments: Qx,Qy,Qx^{2},Qy^{2},QxQy for A then C;centrality (%);moment index;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); + histos.add("final_calibration/pPhiHarmonicsAVsCentrality", "Final ZNA #phi harmonics: cos(n#phi),sin(n#phi) for n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("final_calibration/pPhiHarmonicsCVsCentrality", "Final ZNC #phi harmonics: cos(n#phi),sin(n#phi) for n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("final_calibration/pQMagAVsCentrality", "Final |Q_{A}| vs centrality;centrality (%);<|Q_{A}|>", kTProfile, {centralityAxis}); + histos.add("final_calibration/pQMagCVsCentrality", "Final |Q_{C}| vs centrality;centrality (%);<|Q_{C}|>", kTProfile, {centralityAxis}); + ccdb->setURL(cfgCcdbParam.cfgURL); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -1170,6 +1198,42 @@ struct zdc2stagecalibration { histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentralityVz"), centrality, vz, correlation); } + if (calibrationStage.value == 2 && useQRecentering.value && useQWidthEqualization.value && useQTwist.value && qMagA > 0. && qMagC > 0.) { + histos.fill(HIST("final_calibration/hQxAVsCentrality"), centrality, qValues[0]); + histos.fill(HIST("final_calibration/hQyAVsCentrality"), centrality, qValues[1]); + histos.fill(HIST("final_calibration/hQxCVsCentrality"), centrality, qValues[2]); + histos.fill(HIST("final_calibration/hQyCVsCentrality"), centrality, qValues[3]); + histos.fill(HIST("final_calibration/pQxAVsCentrality"), centrality, qValues[0]); + histos.fill(HIST("final_calibration/pQyAVsCentrality"), centrality, qValues[1]); + histos.fill(HIST("final_calibration/pQxCVsCentrality"), centrality, qValues[2]); + histos.fill(HIST("final_calibration/pQyCVsCentrality"), centrality, qValues[3]); + histos.fill(HIST("final_calibration/hPhiAVsCentrality"), centrality, phiFinalA); + histos.fill(HIST("final_calibration/hPhiCVsCentrality"), centrality, phiFinalC); + const double finalCorrelation = std::cos(phiFinalA - phiFinalC); + histos.fill(HIST("final_calibration/pCosPsiAPsiCVsCentrality"), centrality, finalCorrelation); + histos.fill(HIST("final_calibration/pCosPsiAPsiCVsCentralityVz"), centrality, vz, finalCorrelation); + histos.fill(HIST("final_calibration/pQxAQxCVsCentrality"), centrality, qValues[0] * qValues[2]); + histos.fill(HIST("final_calibration/pQyAQyCVsCentrality"), centrality, qValues[1] * qValues[3]); + histos.fill(HIST("final_calibration/pQxAQyCVsCentrality"), centrality, qValues[0] * qValues[3]); + histos.fill(HIST("final_calibration/pQyAQxCVsCentrality"), centrality, qValues[1] * qValues[2]); + histos.fill(HIST("final_calibration/pQxAVsCentralityVz"), centrality, vz, qValues[0]); + histos.fill(HIST("final_calibration/pQyAVsCentralityVz"), centrality, vz, qValues[1]); + histos.fill(HIST("final_calibration/pQxCVsCentralityVz"), centrality, vz, qValues[2]); + histos.fill(HIST("final_calibration/pQyCVsCentralityVz"), centrality, vz, qValues[3]); + const auto finalMoments = makeTwistMoments(qValues); + for (int i = 0; i < 10; ++i) { + histos.fill(HIST("final_calibration/pQMomentsVsCentrality"), centrality, i + 0.5, finalMoments[i]); + } + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("final_calibration/pPhiHarmonicsAVsCentrality"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("final_calibration/pPhiHarmonicsAVsCentrality"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("final_calibration/pPhiHarmonicsCVsCentrality"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("final_calibration/pPhiHarmonicsCVsCentrality"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } + histos.fill(HIST("final_calibration/pQMagAVsCentrality"), centrality, qMagA); + histos.fill(HIST("final_calibration/pQMagCVsCentrality"), centrality, qMagC); + } + histos.fill(HIST("hEvtSelInfo"), 7.5); fillTable(true, static_cast(qValues[0]), static_cast(qValues[2]), static_cast(qValues[1]), static_cast(qValues[3])); histos.fill(HIST("hEvtSelInfo"), 8.5); From 17ec4d0b60ff0abaa8024c5e51928b9606a971f8 Mon Sep 17 00:00:00 2001 From: Nazar Burmasov Date: Sun, 8 Nov 2026 20:00:20 +0300 Subject: [PATCH 609/648] [PWGUD] MUON efficiency producer: add simple luminosity counters (#18652) --- .../TableProducer/muonEfficiencyProducer.cxx | 81 +++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/PWGUD/TableProducer/muonEfficiencyProducer.cxx b/PWGUD/TableProducer/muonEfficiencyProducer.cxx index 9a7f0987bde..267c9bb55fd 100644 --- a/PWGUD/TableProducer/muonEfficiencyProducer.cxx +++ b/PWGUD/TableProducer/muonEfficiencyProducer.cxx @@ -13,8 +13,13 @@ /// \brief Compact forward-muon skim for offline MCH and MCH-MID efficiency studies /// \author Nazar Burmasov (JINR) +#include "Common/CCDB/EventSelectionParams.h" + #include +#include +#include #include +#include #include #include #include @@ -24,8 +29,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -34,9 +42,13 @@ #include #include #include +#include +#include #include +#include #include +#include #include using namespace o2; @@ -98,6 +110,7 @@ struct MuonEfficiencyProducer { static constexpr float MaxEta = -2.5f; static constexpr float NoAmplitude = -1.f; static constexpr float MaxFITTime = 30.f; + static constexpr int BCsPerOrbit = constants::lhc::LHCMaxBunches; using Tracks = soa::Join; using Matrix55 = ROOT::Math::SMatrix>; @@ -105,9 +118,13 @@ struct MuonEfficiencyProducer { Produces outputBCs; Produces output; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Service ccdb{}; globaltracking::MatchGlobalFwd matcher; - int cachedFieldRun = -1; + bool conditionsInitialized = false; + std::bitset collidingBCs; + float zncBBLower = 0.f; + float zncBBUpper = 0.f; Configurable minPt{"minPt", 0.4f, "Minimum track pT (Gev/c)"}; Configurable minRAbs{"minRAbs", 17.6f, "Minimum absorber-end radius (cm)"}; @@ -118,11 +135,19 @@ struct MuonEfficiencyProducer { void init(InitContext&) { ccdb->setLocalObjectValidityChecking(); + const AxisSpec runAxis{1, 0.5, 1.5, "Run"}; + const AxisSpec bcAxis{BCsPerOrbit, -0.5, BCsPerOrbit - 0.5, "BC in orbit"}; + histos.add("hCounterTVX", "TVX;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.add("hCounterTCE", "TCE;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.add("hCounterZNC", "ZNC;Run;BC in orbit;Counts", HistType::kTH2D, {runAxis, bcAxis}); + histos.get(HIST("hCounterTVX"))->SetCanExtend(TH1::kXaxis); + histos.get(HIST("hCounterTCE"))->SetCanExtend(TH1::kXaxis); + histos.get(HIST("hCounterZNC"))->SetCanExtend(TH1::kXaxis); } - void setField(int run) + void initRunConditions(int run) { - if (cachedFieldRun == run) { + if (conditionsInitialized) { return; } const auto runInfo = parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); @@ -136,7 +161,21 @@ struct MuonEfficiencyProducer { ccdb->get("GLO/Config/GeometryAligned"); } mch::TrackExtrap::setField(); - cachedFieldRun = run; + if (!runInfo.grpLHC) { + LOGF(fatal, "GRPLHCIFData not found for run %d", run); + } + collidingBCs = runInfo.grpLHC->getBunchFilling().getBCPattern(); + auto* params = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", ts); + if (!params) { + LOGF(fatal, "EventSelectionParams not found for run %d", run); + } + zncBBLower = params->fZNCBBlower; + zncBBUpper = params->fZNCBBupper; + const auto label = std::to_string(run); + histos.get(HIST("hCounterTVX"))->GetXaxis()->SetBinLabel(1, label.c_str()); + histos.get(HIST("hCounterTCE"))->GetXaxis()->SetBinLabel(1, label.c_str()); + histos.get(HIST("hCounterZNC"))->GetXaxis()->SetBinLabel(1, label.c_str()); + conditionsInitialized = true; } struct Kinematics { @@ -165,6 +204,7 @@ struct MuonEfficiencyProducer { } void process(aod::BCs const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, + aod::Zdcs const& zdcs, Tracks const& tracks, aod::Collisions const& collisions, aod::AmbiguousFwdTracks const& ambiguous) { @@ -172,12 +212,15 @@ struct MuonEfficiencyProducer { return; } const int run = bcs.begin().runNumber(); - + initRunConditions(run); + std::vector ft0TriggersByBC(bcs.size(), 0); + std::vector zncIsBBByBC(bcs.size(), 0); std::vector ft0aByBC(bcs.size(), NoAmplitude); std::vector ft0cByBC(bcs.size(), NoAmplitude); std::vector fv0aByBC(bcs.size(), NoAmplitude); std::vector skimBCIds(bcs.size(), -1); for (const auto& ft0 : ft0s) { + ft0TriggersByBC[ft0.bcId()] |= ft0.triggerMask(); if (ft0.timeA() < MaxFITTime) { ft0aByBC[ft0.bcId()] = ft0.sumAmpA(); } @@ -198,6 +241,33 @@ struct MuonEfficiencyProducer { fv0aByBC[fv0.bcId()] = amplitude; } + for (const auto& zdc : zdcs) { + if (zdc.timeZNC() > zncBBLower && zdc.timeZNC() < zncBBUpper) { + zncIsBBByBC[zdc.bcId()] = 1; + } + } + + auto hTVX = histos.get(HIST("hCounterTVX")); + auto hTCE = histos.get(HIST("hCounterTCE")); + auto hZNC = histos.get(HIST("hCounterZNC")); + for (const auto& bc : bcs) { + const auto slot = bc.globalBC() % BCsPerOrbit; + if (!collidingBCs[slot]) { + continue; + } + const auto mask = ft0TriggersByBC[bc.globalIndex()]; + const bool tvx = (mask & (1u << o2::ft0::Triggers::bitVertex)) != 0; + if (tvx) { + hTVX->Fill(1., static_cast(slot)); + if ((mask & (1u << o2::ft0::Triggers::bitCen)) != 0) { + hTCE->Fill(1., static_cast(slot)); + } + } + if (zncIsBBByBC[bc.globalIndex()] != 0u) { + hZNC->Fill(1., static_cast(slot)); + } + } + std::vector ambiguousBCIds(tracks.size(), -1); for (const auto& entry : ambiguous) { const auto& ids = entry.bcIds(); @@ -226,7 +296,6 @@ struct MuonEfficiencyProducer { vtxCov = {col.covXX(), col.covYY()}; referenceBCId = col.bcId(); } - setField(run); if (!propagateMCH(t, vtx, vtxCov, k)) { continue; } From 9f6e4a9bde9971dba2b498f24a48bf1474433abb Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Sun, 8 Nov 2026 21:54:49 +0100 Subject: [PATCH 610/648] [PWGCF] Add HM trigger selection in HadNucleiFemto.cxx (#18630) --- .../TableProducer/HadNucleiFemto.cxx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 6a77e7bd814..a6e71890aed 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -623,7 +623,9 @@ struct HadNucleiFemto { // cppcheck-suppress unusedStructMember std::string prefix{"zorro"}; // Zorro - Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable settingEnableTriggerSelection{"settingEnableTriggerSelection", false, "Enable Zorro event selection for the configured software triggers"}; + Configurable settingSoftwareTriggerNames{"settingSoftwareTriggerNames", "fHighFt0Mult", "Software trigger names, separated by commas (OR selection)"}; + Configurable settingBaseCCDBPathForTriggers{"settingBaseCCDBPathForTriggers", "EventFiltering/Zorro/", "CCDB path for Zorro trigger information"}; } zorro; struct : o2::framework::ConfigurableGroup { @@ -1041,8 +1043,9 @@ struct HadNucleiFemto { return; } if constexpr (!isMC) { - if (zorro.settingSkimmedProcessing) { - mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorroTriggerMask()); + if (useZorroSelection()) { + mZorro.setBaseCCDBPath(zorro.settingBaseCCDBPathForTriggers.value); + mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorro.settingSoftwareTriggerNames.value); mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); } } @@ -1113,7 +1116,7 @@ struct HadNucleiFemto { template bool passesZorroSelection(const Tcollision& collision) { - if (!zorro.settingSkimmedProcessing) { + if (!useZorroSelection()) { return true; } auto bc = collision.template bc_as(); @@ -1132,7 +1135,7 @@ struct HadNucleiFemto { mQaRegistry.fill(HIST("hEvents"), 1); if constexpr (!isMC) { - if (zorro.settingSkimmedProcessing) { + if (useZorroSelection()) { if (!passesZorroSelection(collision)) { return false; } @@ -1287,12 +1290,9 @@ struct HadNucleiFemto { return species.settingNuPDGCode.value == He3PDG; } - const char* zorroTriggerMask() const + bool useZorroSelection() const { - if (useTritonNucleus()) { - return "fTritonFemto"; - } - return useHelium3Nucleus() ? "fHe" : "fDe"; + return zorro.settingEnableTriggerSelection.value; } const char* nucleusBetheBlochLabel() const From d94a0a9b9fad26e14b9ee17d1934e75f9ccb7e26 Mon Sep 17 00:00:00 2001 From: Rahul Verma <110929992+rahulverma012@users.noreply.github.com> Date: Mon, 9 Nov 2026 10:25:49 +0530 Subject: [PATCH 611/648] [PWGCF] Added centrality classification of trigger particles. (#18637) --- .../Tasks/hParticleCorrelation.cxx | 2634 ++++++++++++++++- 1 file changed, 2633 insertions(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx index 032a5e988f9..9370e407843 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -14,6 +14,8 @@ /// /// \author Rahul Verma (rahul.verma@iitb.ac.in) :: Durgesh Bhatt (durgesh.bhatt@cern.ch) :: Sadhana Dash (sadhana@phy.iitb.ac.in) +#include "PWGLF/DataModel/mcCentrality.h" + #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -32,9 +34,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -42,14 +47,21 @@ #include #include #include +#include +#include #include #include #include +#include #include +#include #include #include #include +#include +#include +#include #include using namespace o2; @@ -57,6 +69,35 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; // for constants +namespace +{ +constexpr float ProtonMass = o2::constants::physics::MassProton; +constexpr float KaonMass = o2::constants::physics::MassKaonCharged; +constexpr float LambdaMass = o2::constants::physics::MassLambda0; +constexpr float XiMass = o2::constants::physics::MassXiMinus; +constexpr float OmegaMass = o2::constants::physics::MassOmegaMinus; +constexpr float Lambda1520Mass = o2::constants::physics::MassLambda1520; +constexpr float MinAbsCharge = 1e-3f; +constexpr float InvalidCentrality = -999.0f; +const std::array kProtonKaonMasses{ProtonMass, KaonMass}; +} // namespace + +namespace lambda1520_analysis +{ +static constexpr int PdgProton{2212}; +static constexpr int PdgKaon{321}; +static constexpr int PdgLambda0{3122}; +static constexpr int PdgXiMinus{3312}; +static constexpr int PdgOmegaMinus{3334}; + +enum CentEstimator : int { + kFT0M = 0, + kFT0A = 1, + kFT0C = 2 +}; + +} // namespace lambda1520_analysis + namespace o2::aod { namespace resonancecndt @@ -1051,6 +1092,7 @@ struct HParticleCorrelation1ResonanceProducer { static constexpr float ResonancePidNSigmaMax = 3.5f; + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; struct : ConfigurableGroup { Configurable c0001printDebugMessages{"c0001printDebugMessages", false, "Print debug messages"}; } cfgDebug; @@ -1109,6 +1151,9 @@ struct HParticleCorrelation1ResonanceProducer { void init(InitContext const&) { + if (!a0000EnableStruct) { + return; + } const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; @@ -1362,6 +1407,8 @@ struct HParticleCorrelation2SameEvent { HistogramRegistry resonanceMassSpectra{"resonanceMassSpectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry mixingQA{"mixingQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + struct : ConfigurableGroup { Configurable c1001printDebugMessages{"c1001printDebugMessages", false, "Print debug messages"}; } cfgDebug; @@ -1634,6 +1681,10 @@ struct HParticleCorrelation2SameEvent { void init(InitContext const&) { + if (!a0000EnableStruct) { + return; + } + if (cfgDebug.c1001printDebugMessages) { LOGF(info, "Starting init"); } @@ -1835,6 +1886,10 @@ struct HParticleCorrelation2SameEvent { histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); }; + auto addTriggerPtVsCentrality = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "PtVsCentrality").c_str(), "Selected triggers;#it{p}_{T}^{trig} (GeV/#it{c});Centrality (%)", HistType::kTH2D, {axisCorrSparseTriggerPt, axisCorrCentrality}); + }; + auto addFullTrackPIDQAHistos = [axisP, axisTPCNSigma, axisTOFNSigma](auto& histReg, const std::string& basePath) { histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); @@ -1903,6 +1958,7 @@ struct HParticleCorrelation2SameEvent { addTrackQAHistos(hhCorrelation, "SE/hh/QA/Trigger/"); hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocLowPt/"); hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocHighPt/"); + addTriggerPtVsCentrality(hhCorrelation, "SE/hh/QA/Trigger/"); addCorrHistos(hhCorrelation, "SE/hh/Corr/AssocLowPt/"); hhCorrelation.addClone("SE/hh/Corr/AssocLowPt/", "SE/hh/Corr/AssocHighPt/"); @@ -1911,6 +1967,7 @@ struct HParticleCorrelation2SameEvent { const std::string base = "SE/" + pairName + "/"; addTrackQAHistos(histReg, base + "QA/Trigger/"); + addTriggerPtVsCentrality(histReg, base + "QA/Trigger/"); addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); @@ -1930,6 +1987,7 @@ struct HParticleCorrelation2SameEvent { const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; addTrackQAHistos(histReg, base + "QA/Trigger/"); + addTriggerPtVsCentrality(histReg, base + "QA/Trigger/"); addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); addResoDauQAHistos(histReg, qaPeak + "PosDau/"); histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); @@ -3251,6 +3309,7 @@ struct HParticleCorrelation2SameEvent { } // fillTrigQA fillCorrParticleQA(histReg, trigger); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[kTrigger]) + HIST("PtVsCentrality"), trigger.pt(), currentCentrality); markTrackUsed(trigger, TriggerUsedFlag); // ++nTrigger; @@ -3639,6 +3698,8 @@ struct HParticleCorrelation3MixedEvent { ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality (estimator chosen by c1138mixingEstimator in the same-event task)"}; } cfgMixing; + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + // Re-built in init() with the configured axes (as a data member it holds the default axes) using BinningTypeMixing = ColumnBinningPolicy; BinningTypeMixing colBinning{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; @@ -3646,6 +3707,10 @@ struct HParticleCorrelation3MixedEvent { void init(InitContext const&) { + if (!a0000EnableStruct) { + return; + } + if (cfgDebug.c2001printDebugMessages) { LOGF(info, "Starting init"); } @@ -4089,10 +4154,2577 @@ struct HParticleCorrelation3MixedEvent { PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Mixed-event correlations from the minimal derived data", true); }; +struct HParticleCorrelation4Block4 { + + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service pdgDB{}; + + Configurable a0000EnableStruct{"a0000EnableStruct", true, "enables filling of declared registries"}; + Configurable a0001PrintMessages{"a0001PrintMessages", false, "enables printing of debug messages"}; + + struct : ConfigurableGroup { + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted |z-vertex| range [cm]"}; + Configurable cfgUseNoITSROFrameBorderCut{"cfgUseNoITSROFrameBorderCut", false, "Apply kNoITSROFrameBorder selection"}; + Configurable cfgUseSel8{"cfgUseSel8", false, "Apply sel8 selection"}; + Configurable cfgRequireRecoINELgt0{"cfgRequireRecoINELgt0", true, "Require reconstructed INEL>0 event class (applied in ALL processes)"}; + Configurable cfgUseNoSameBunchPileupCut{"cfgUseNoSameBunchPileupCut", false, "Apply kNoSameBunchPileup event selection"}; + Configurable cfgUseGoodZvtxFT0vsPVCut{"cfgUseGoodZvtxFT0vsPVCut", false, "Apply kIsGoodZvtxFT0vsPV event selection"}; + Configurable cfgUseGoodITSLayersAll{"cfgUseGoodITSLayersAll", false, "Apply kIsGoodITSLayersAll event selection"}; + Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "Apply occupancy-in-time-range cut"}; + Configurable cfgOccupancyMin{"cfgOccupancyMin", 0, "Minimum track occupancy in time range"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 9999, "Maximum track occupancy in time range"}; + Configurable cfgCentralityEstimator{"cfgCentralityEstimator", 0, "Centrality estimator: 0=FT0M 1=FT0A 2=FT0C"}; + Configurable cfgUseMCTruthCentrality{"cfgUseMCTruthCentrality", false, "Use MC truth centrality for generated Lambda(1520) histograms (otherwise the centrality of the accepted reconstructed collision is used when there is one)"}; + Configurable cfgApplyMCTruthINELgt0{"cfgApplyMCTruthINELgt0", true, "Apply truth-level INEL>0 selection to MC generated events (event factor, signal loss, generated QA)"}; + } evSel; + + struct : ConfigurableGroup { + Configurable cfgCutPt{"cfgCutPt", 0.15f, "Minimum pT of daughter track [GeV/c]"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Maximum |eta| of daughter track"}; + Configurable cfgMinY{"cfgMinY", -0.5f, "Minimum rapidity of the Lambda(1520) (reconstructed, generated and signal-loss particles)"}; + Configurable cfgMaxY{"cfgMaxY", 0.5f, "Maximum rapidity of the Lambda(1520) (reconstructed, generated and signal-loss particles)"}; + Configurable cfgMinTPCCrossedRows{"cfgMinTPCCrossedRows", 70, "Minimum TPC crossed pad rows"}; + Configurable cfgMinTPCClusters{"cfgMinTPCClusters", 120, "Minimum TPC clusters found"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + Configurable cfgMaxTPCSharedClusters{"cfgMaxTPCSharedClusters", 0, "Maximum TPC shared clusters"}; + Configurable cfgMinCrossedRowsOverFindable{"cfgMinCrossedRowsOverFindable", 0.8f, "Minimum ratio crossedRows/findableClusters"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "Maximum TPC chi2/NCl"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "Maximum ITS chi2/NCl"}; + Configurable applyTPCCrossedRowsCut{"applyTPCCrossedRowsCut", true, "Enable/disable minimum TPC crossed rows cut"}; + Configurable applyTPCFoundClusterCut{"applyTPCFoundClusterCut", false, "Enable/disable minimum TPC clusters found cut"}; + Configurable applyITSClusterCut{"applyITSClusterCut", false, "Enable/disable minimum ITS clusters cut"}; + Configurable applyTPCSharedClusterCut{"applyTPCSharedClusterCut", false, "Enable/disable maximum TPC shared clusters cut"}; + Configurable applyTPCCrossedRowsOverFindableCut{"applyTPCCrossedRowsOverFindableCut", false, "Enable/disable crossed rows / findable clusters cut"}; + Configurable applyTPCChi2NClCut{"applyTPCChi2NClCut", false, "Enable/disable maximum TPC chi2/NCl cut"}; + Configurable applyITSChi2NClCut{"applyITSChi2NClCut", false, "Enable/disable maximum ITS chi2/NCl cut"}; + Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require isPrimaryTrack flag"}; + Configurable requireGlobalTrackWoDCA{"requireGlobalTrackWoDCA", true, "Require isGlobalTrackWoDCA flag"}; + Configurable requireGlobalTrack{"requireGlobalTrack", false, "Require isGlobalTrack flag"}; + Configurable requirePVContributor{"requirePVContributor", false, "Require PV-contributor flag"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable cfgCutDCAz{"cfgCutDCAz", 0.1f, "Maximum |DCAz| for all tracks [cm]"}; + Configurable> protonDCAPtBinEdges{"protonDCAPtBinEdges", {0.0f, 1000.f}, "Proton pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.1f}, "Max |DCAxy| for proton per pT bin [cm]"}; + Configurable> kaonDCAPtBinEdges{"kaonDCAPtBinEdges", {0.0f, 1000.f}, "Kaon pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> kaonMaxDCAxyPerPtBin{"kaonMaxDCAxyPerPtBin", {0.1f}, "Max |DCAxy| for kaon per pT bin [cm]"}; + Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Additionally apply the continuous pT-dependent DCAxy cut"}; + Configurable dcaXYConst{"dcaXYConst", 0.004f, "Constant term of pT-dependent DCAxy cut [cm] (7-sigma: 0.004)"}; + Configurable dcaXYPtCoeff{"dcaXYPtCoeff", 0.013f, "pT-dependent coefficient of DCAxy cut [cm] (7-sigma: 0.013)"}; + Configurable dcaXYPtExponent{"dcaXYPtExponent", 1.0f, "pT exponent of DCAxy cut"}; + } dcaCuts; + + struct : ConfigurableGroup { + Configurable useTPCOnlyPIDAtAllMomenta{"useTPCOnlyPIDAtAllMomenta", false, "Bypass TOF: TPC-only PID (|nSigma| < max TPC nSigma) at all momenta"}; + Configurable applyKaonPionRejection{"applyKaonPionRejection", false, "Reject pion-like kaon candidates (no TOF, pT window, small pion nSigma)"}; + Configurable pionRejectionMinPt{"pionRejectionMinPt", 1.0f, "Pion rejection: minimum pT [GeV/c]"}; + Configurable pionRejectionMaxPt{"pionRejectionMaxPt", 2.0f, "Pion rejection: maximum pT [GeV/c]"}; + Configurable pionRejectionMaxTPCNSigma{"pionRejectionMaxTPCNSigma", 2.0f, "Pion rejection: |TPC nSigma pion| below this is rejected"}; + } pidShared; + + struct : ConfigurableGroup { + Configurable requireTOFForProton{"requireTOFForProton", false, "Require TOF signal for proton candidates"}; + Configurable maxTPCNSigmaProton{"maxTPCNSigmaProton", 2.0f, "Maximum |TPC nSigma| (used only with useTPCOnlyPIDAtAllMomenta)"}; + Configurable protonThresholdMomentum{"protonThresholdMomentum", 0.9f, "Proton momentum p [GeV/c] for tracks WITHOUT TOF: below it TPC-only PID, at/above it the track is rejected (tracks with TOF always use the TPC+TOF cut)"}; + Configurable> protonTPCPIDMomentumBins{"protonTPCPIDMomentumBins", {0.f, 0.9f}, "momentum edges p for TPC-only proton PID [GeV/c]"}; + Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {2.f}, "Max |TPC nSigma| per momentum bin, TPC-only proton PID"}; + Configurable> protonCombinedPIDMomentumBins{"protonCombinedPIDMomentumBins", {0.f, 999.f}, "momentum edges p for circular TPC+TOF proton PID [GeV/c]"}; + Configurable> protonCombinedNSigmaCutPerBin{"protonCombinedNSigmaCutPerBin", {2.f}, "Max combined nSigma radius per momentum bin, circular cut"}; + } protonPID; + + struct : ConfigurableGroup { + Configurable requireTOFForKaon{"requireTOFForKaon", false, "Require TOF signal for kaon candidates"}; + Configurable maxTPCNSigmaKaon{"maxTPCNSigmaKaon", 2.0f, "Maximum |TPC nSigma| (used only with useTPCOnlyPIDAtAllMomenta)"}; + Configurable kaonThresholdMomentum{"kaonThresholdMomentum", 0.5f, "Kaon momentum p [GeV/c] for tracks WITHOUT TOF: below it TPC-only PID, at/above it the track is rejected (tracks with TOF always use the TPC+TOF cut)"}; + Configurable> kaonTPCPIDMomentumBins{"kaonTPCPIDMomentumBins", {0.f, 0.5f}, "momentum edges p for TPC-only kaon PID [GeV/c]"}; + Configurable> kaonTPCNSigmaCutPerBin{"kaonTPCNSigmaCutPerBin", {2.f}, "Max |TPC nSigma| per momentum bin, TPC-only kaon PID"}; + Configurable> kaonCombinedPIDMomentumBins{"kaonCombinedPIDMomentumBins", {0.f, 999.f}, "momentum edges p for circular TPC+TOF kaon PID [GeV/c]"}; + Configurable> kaonCombinedNSigmaCutPerBin{"kaonCombinedNSigmaCutPerBin", {2.f}, "Max combined nSigma radius per momentum bin, circular cut"}; + } kaonPID; + + struct : ConfigurableGroup { + Configurable applyDaughterDeltaEtaPhiCuts{"applyDaughterDeltaEtaPhiCuts", false, "Apply |deltaEta| and |deltaPhi| cuts between daughters"}; + Configurable maxDaughterDeltaEta{"maxDaughterDeltaEta", 0.7f, "Maximum |deltaEta| between daughters"}; + Configurable maxDaughterDeltaPhi{"maxDaughterDeltaPhi", 1.5f, "Maximum |deltaPhi| between daughters"}; + Configurable applyDeepAngleCut{"applyDeepAngleCut", false, "Apply minimum opening-angle cut (removes split-track background)"}; + Configurable deepAngleCutValue{"deepAngleCutValue", 0.04f, "Minimum opening angle between proton and kaon [rad]"}; + Configurable applyOpeningAngleVeto{"applyOpeningAngleVeto", false, "Reject pairs whose opening angle lies INSIDE [veto min, veto max]"}; + Configurable openingAngleVetoMin{"openingAngleVetoMin", 1.4f, "Opening-angle veto window: lower edge [rad]"}; + Configurable openingAngleVetoMax{"openingAngleVetoMax", 2.4f, "Opening-angle veto window: upper edge [rad]"}; + Configurable applyPairPtMassUpperLimits{"applyPairPtMassUpperLimits", false, "Exclude pairs with pT or mass above the limits below (overflow bins)"}; + Configurable cfgMaxPairPt{"cfgMaxPairPt", 10.0f, "Maximum pair pT [GeV/c]"}; + Configurable cfgMaxPairMass{"cfgMaxPairMass", 3.0f, "Maximum pair mass [GeV/c^2]"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 10, "Number of mixed events per signal event"}; + Configurable cfgFillMixLikeSign{"cfgFillMixLikeSign", false, "Also fill like-sign pairs in mixed events"}; + } mixingCfg; + + struct : ConfigurableGroup { + Configurable enableRotationalBackground{"enableRotationalBackground", false, "Compute rotational background (unlike-sign pairs only)"}; + Configurable numberOfRotations{"numberOfRotations", 10, "Number of rotations per pair (evenly spaced, weight 1/N)"}; + Configurable rotationAngleWindow{"rotationAngleWindow", 10.f, "Rotation angles within PI +/- PI/rotationAngleWindow"}; + Configurable rotateProtonInsteadOfKaon{"rotateProtonInsteadOfKaon", true, "Rotate the proton phi (false = rotate the kaon phi)"}; + } rotBkg; + + struct : ConfigurableGroup { + Configurable cfgLambda1520PdgCode{"cfgLambda1520PdgCode", static_cast(o2::constants::physics::Pdg::kLambda1520_Py), "PDG code of the Lambda(1520) in the MC sample (change it for injected samples that use a different code). Sign is ignored: +code = Lambda(1520), -code = anti-Lambda(1520)"}; + Configurable applyGeneratedEtaCut{"applyGeneratedEtaCut", false, "Apply |eta| cut to generated particles and to truth-matched reconstructed pairs"}; + Configurable generatedEtaMax{"generatedEtaMax", 0.5f, "MC |eta| cut"}; + Configurable applyGeneratedDaughterEtaCut{"applyGeneratedDaughterEtaCut", true, "Require both generated daughters within |eta| < trackCuts.cfgCutEta"}; + Configurable applyMtScaleWeight{"applyMtScaleWeight", false, "Weight mT-scaled signal-loss histograms by mtScaledPt/pt"}; + Configurable useGeneratedMultiplicityCentrality{"useGeneratedMultiplicityCentrality", false, "Derive MC centrality from generated multiplicity"}; + Configurable> generatedMultiplicityLowerEdges{"generatedMultiplicityLowerEdges", std::vector{500, 300, 200, 120, 80, 50, 30, 10, 0}, "Generated multiplicity lower cuts"}; + Configurable> centralityPerGeneratedMultiplicityClass{"centralityPerGeneratedMultiplicityClass", std::vector{2.5f, 7.5f, 15.0f, 25.0f, 35.0f, 45.0f, 55.0f, 75.0f, 95.0f}, "Centrality bin centres"}; + Configurable generatedMultiplicityEtaMax{"generatedMultiplicityEtaMax", 0.5f, "Max eta for generated mid-rapidity multiplicity"}; + Configurable generatedMultiplicityEtaMin{"generatedMultiplicityEtaMin", -0.5f, "Min eta for generated mid-rapidity multiplicity"}; + } mcCfg; + + struct : ConfigurableGroup { + Configurable cfgFillTrackQA{"cfgFillTrackQA", true, "Fill track QA histograms (before/after selection)"}; + Configurable cfgFillPairQA{"cfgFillPairQA", false, "Fill pair QA histograms (opening angle, deltaEta, deltaPhi)"}; + Configurable cfgFillMultQA{"cfgFillMultQA", false, "Fill centrality vs track multiplicity correlation"}; + Configurable cfgFillMixQA{"cfgFillMixQA", false, "Fill mixed-event pool QA histograms"}; + } qa; + + struct : ConfigurableGroup { + Configurable referenceFrameDaughter{"referenceFrameDaughter", 0, "Reference daughter for cos(theta*): 0 = proton, 1 = kaon"}; + Configurable activateHelicityFrame{"activateHelicityFrame", false, "Compute cos(theta*) in the Helicity frame (quantization axis = Lambda(1520) flight direction)"}; + Configurable activateCollinsSoperFrame{"activateCollinsSoperFrame", false, "Compute cos(theta*) in the Collins-Soper frame (beam bisector)"}; + Configurable activateProductionFrame{"activateProductionFrame", false, "Compute cos(theta*) wrt the production-plane normal"}; + Configurable activateBeamAxisFrame{"activateBeamAxisFrame", false, "Compute cos(theta*) in the beam-axis (Gottfried-Jackson) frame"}; + Configurable activateRandomFrame{"activateRandomFrame", false, "Compute cos(theta*) wrt a random axis (null/control test, should be flat)"}; + Configurable fillRotatedBackgroundPolarization{"fillRotatedBackgroundPolarization", false, "Also fill rotated-background polarization histograms"}; + Configurable fillMixedEventPolarization{"fillMixedEventPolarization", false, "Also fill mixed-event-background polarization histograms"}; + Configurable beamEnergy{"beamEnergy", 13600.0f, "sqrt(s) beam energy in GeV (used for Collins-Soper/beam-axis/production frames)"}; + ConfigurableAxis cosThetaBins{"cosThetaBins", {20, -1.0f, 1.0f}, "cos(theta*) binning"}; + } spinAlignment; + + struct : ConfigurableGroup { + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {VARIABLE_WIDTH, 0.0, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.25, 1.3, 1.4, 1.5, 1.6, 1.7, 1.75, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.5, 4.6, 4.8, 4.9, 5.0, 5.5, 5.6, 6.0, 6.4, 6.5, 7.0, 7.2, 8.0, 9.0, 9.5, 9.6, 10.0, 11.0, 11.5, 12.0, 13.0, 14.0, 14.4, 15.0, 16.0, 18.0, 19.2, 20.0}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis cfgMassAxis{"cfgMassAxis", {70, 1.3f, 2.0f}, "Invariant mass (GeV/#it{c}^{2})"}; + ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f}, "Centrality (%)"}; + ConfigurableAxis cfgRapAxis{"cfgRapAxis", {20, -1.0, 1.0}, "Rapidity y"}; + ConfigurableAxis cfgVtxAxis{"cfgVtxAxis", {VARIABLE_WIDTH, -10.0f, -8.0f, -6.0f, -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f}, "Vertex z [cm] (histograms and event-mixing bins)"}; + } axes; + + struct DaughterTrackCandidate { + float px = 0.f, py = 0.f, pz = 0.f; + float pt = 0.f, eta = 0.f, phi = 0.f; + float mom = 0.f; + float dcaXY = 0.f, dcaZ = 0.f; + int sign = 0; + bool hasTOF = false; + float tpcNSigmaPr = 0.f, tpcNSigmaKa = 0.f; + float tofNSigmaPr = -999.f, tofNSigmaKa = -999.f; + int64_t globalIndex = 0; + bool hasMcLabel = false; + int mcPdgCode = 0; + int64_t mcLambda1520MotherIndex = -1; + int mcLambda1520MotherPdgCode = 0; + }; + + struct SpeciesPIDSettings { + std::vector tpcOnlyBins, tpcOnlyCuts; + std::vector combinedBins, combinedCuts; + float maxTPCNSigma = 2.f; + float thresholdMomentum = 0.f; + }; + + SpeciesPIDSettings mProtonPIDCuts; + SpeciesPIDSettings mKaonPIDCuts; + std::vector mProtonDCAPtEdges; + std::vector mProtonMaxDCAxy; + std::vector mKaonDCAPtEdges; + std::vector mKaonMaxDCAxy; + int mLambda1520AbsPdgCode = 0; + std::vector mProtonCandidates; + std::vector mKaonCandidates; + + float mBeam1Energy = 0.f; + std::array mBeam1Momentum{}; + float mBeam2Energy = 0.f; + std::array mBeam2Momentum{}; + std::mt19937 mRandomEngine{std::random_device{}()}; + std::uniform_real_distribution mUniform01{0., 1.}; + + template + static void setHistogramBinLabels(HistPtr const& hist, std::vector const& labels) + { + for (size_t i = 0; i < labels.size(); ++i) { + hist->GetXaxis()->SetBinLabel(static_cast(i) + 1, labels[i]); + } + } + + using BinningTypeFT0M = ColumnBinningPolicy; + using BinningTypeFT0A = ColumnBinningPolicy; + using BinningTypeFT0C = ColumnBinningPolicy; + + BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + + void init(InitContext const&) + { + if (!a0000EnableStruct) { + return; + } + + // Re-build the mixing binnings now that the configurables are set (as data members they hold the default axes) + binningFT0M = BinningTypeFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + binningFT0A = BinningTypeFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + binningFT0C = BinningTypeFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + + auto checkEdgesMatchCuts = [](auto const& cuts, auto const& bins, const char* label) { + const auto& cutsVec = cuts.value; + const auto& binsVec = bins.value; + if (cutsVec.size() != binsVec.size() - 1) { + LOG(fatal) << "PID bin-size mismatch for: " << label + << " (cuts=" << cutsVec.size() + << ", bins=" << binsVec.size() << ")"; + } + }; + checkEdgesMatchCuts(protonPID.protonTPCNSigmaCutPerBin, protonPID.protonTPCPIDMomentumBins, "proton TPC-only PID"); + checkEdgesMatchCuts(protonPID.protonCombinedNSigmaCutPerBin, protonPID.protonCombinedPIDMomentumBins, "proton combined PID"); + checkEdgesMatchCuts(kaonPID.kaonTPCNSigmaCutPerBin, kaonPID.kaonTPCPIDMomentumBins, "kaon TPC-only PID"); + checkEdgesMatchCuts(kaonPID.kaonCombinedNSigmaCutPerBin, kaonPID.kaonCombinedPIDMomentumBins, "kaon combined PID"); + checkEdgesMatchCuts(dcaCuts.protonMaxDCAxyPerPtBin, dcaCuts.protonDCAPtBinEdges, "proton DCAxy pT bins"); + checkEdgesMatchCuts(dcaCuts.kaonMaxDCAxyPerPtBin, dcaCuts.kaonDCAPtBinEdges, "kaon DCAxy pT bins"); + if (mcCfg.useGeneratedMultiplicityCentrality && + mcCfg.generatedMultiplicityLowerEdges.value.size() != mcCfg.centralityPerGeneratedMultiplicityClass.value.size()) { + LOG(fatal) << "generatedMultiplicityLowerEdges and centralityPerGeneratedMultiplicityClass must have the same size"; + } + + mLambda1520AbsPdgCode = std::abs(mcCfg.cfgLambda1520PdgCode.value); + if (mLambda1520AbsPdgCode == 0) { + LOG(fatal) << "cfgLambda1520PdgCode must be non-zero"; + } + if (doprocessMC || doprocessMCGen || doprocessSignalLoss) { + LOG(info) << "Lambda(1520) PDG code used for MC truth: " << mLambda1520AbsPdgCode; + } + + mProtonDCAPtEdges = dcaCuts.protonDCAPtBinEdges.value; + mProtonMaxDCAxy = dcaCuts.protonMaxDCAxyPerPtBin.value; + mKaonDCAPtEdges = dcaCuts.kaonDCAPtBinEdges.value; + mKaonMaxDCAxy = dcaCuts.kaonMaxDCAxyPerPtBin.value; + + mProtonPIDCuts.tpcOnlyBins = protonPID.protonTPCPIDMomentumBins.value; + mProtonPIDCuts.tpcOnlyCuts = protonPID.protonTPCNSigmaCutPerBin.value; + mProtonPIDCuts.combinedBins = protonPID.protonCombinedPIDMomentumBins.value; + mProtonPIDCuts.combinedCuts = protonPID.protonCombinedNSigmaCutPerBin.value; + mProtonPIDCuts.maxTPCNSigma = protonPID.maxTPCNSigmaProton; + mProtonPIDCuts.thresholdMomentum = protonPID.protonThresholdMomentum; + + mKaonPIDCuts.tpcOnlyBins = kaonPID.kaonTPCPIDMomentumBins.value; + mKaonPIDCuts.tpcOnlyCuts = kaonPID.kaonTPCNSigmaCutPerBin.value; + mKaonPIDCuts.combinedBins = kaonPID.kaonCombinedPIDMomentumBins.value; + mKaonPIDCuts.combinedCuts = kaonPID.kaonCombinedNSigmaCutPerBin.value; + mKaonPIDCuts.maxTPCNSigma = kaonPID.maxTPCNSigmaKaon; + mKaonPIDCuts.thresholdMomentum = kaonPID.kaonThresholdMomentum; + + auto checkTPCOnlyMomentumCoverage = [](std::vector const& edges, float threshold, const char* label) { + if (threshold > 0.f && (edges.empty() || edges.front() > 0.f || edges.back() < threshold)) { + LOG(warning) << label << ": TPC-only momentum bins do not cover [0, " << threshold + << ") GeV/c - tracks WITHOUT TOF in the uncovered range are rejected"; + } + }; + if (!pidShared.useTPCOnlyPIDAtAllMomenta) { + checkTPCOnlyMomentumCoverage(mProtonPIDCuts.tpcOnlyBins, mProtonPIDCuts.thresholdMomentum, "proton PID"); + checkTPCOnlyMomentumCoverage(mKaonPIDCuts.tpcOnlyBins, mKaonPIDCuts.thresholdMomentum, "kaon PID"); + } + + { + const auto sqrtS = static_cast(spinAlignment.beamEnergy); + double beamMomentum = std::sqrt(sqrtS * sqrtS / 4. - static_cast(ProtonMass) * static_cast(ProtonMass)); + mBeam1Energy = static_cast(sqrtS * 0.5); + mBeam1Momentum = {0.f, 0.f, static_cast(-beamMomentum)}; + mBeam2Energy = static_cast(sqrtS * 0.5); + mBeam2Momentum = {0.f, 0.f, static_cast(beamMomentum)}; + } + + { + const int nActiveFrames = static_cast(spinAlignment.activateHelicityFrame) + + static_cast(spinAlignment.activateCollinsSoperFrame) + + static_cast(spinAlignment.activateProductionFrame) + + static_cast(spinAlignment.activateBeamAxisFrame) + + static_cast(spinAlignment.activateRandomFrame); + if (nActiveFrames > 1) { + LOG(fatal) << "Multiple polarization frames activated simultaneously. " + << "Please activate only ONE frame at a time to prevent data mixing in the cos(theta*) THnSparse."; + } + } + + const AxisSpec ptAxis{axes.cfgPtAxis, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec massAxis{axes.cfgMassAxis, "M_{inv} (GeV/#it{c}^{2})"}; + const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; + const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; + const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; + const AxisSpec nSigmaTPCaxis{100, -5., 5., "n#sigma^{TPC}"}; + const AxisSpec nSigmaTOFaxis{100, -5., 5., "n#sigma^{TOF}"}; + const AxisSpec momentumAxis{100, 0., 10., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{100, 0., 10., "p_{T} (GeV/#it{c})"}; + const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; + const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; + const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; + const AxisSpec tpcRowsAxis{200, 0, 200, "Crossed rows^{TPC}"}; + const AxisSpec tpcSignalAxis{500, 0, 500, "TPC signal (a.u.)"}; + const AxisSpec occupancyAxis{200, 0, 10000, "Track occupancy [-40, 100] ns"}; + const AxisSpec etaAxis{150, -1.5, 1.5, "#eta"}; + const AxisSpec phiAxis{72, 0., o2::constants::math::TwoPI, "#varphi (rad)"}; + const AxisSpec openAngleAxis{100, 0., o2::constants::math::PI, "Opening angle (rad)"}; + const AxisSpec deltaEtaAxis{160, 0., 1.6, "|#Delta#eta|"}; + const AxisSpec deltaPhiAxis{100, 0., o2::constants::math::PI, "|#Delta#varphi| (rad)"}; + const std::vector eventCutLabels{"All", "Vz", "kNoITSROFrameBorder", "sel8", "INEL>0", "Occupancy", "NoSameBunchPileup", "GoodZvtxFT0vsPV", "GoodITSLayersAll", "Accepted"}; + + if (doprocessSameEvent) { + histos.add("EventSelection/SameEvent/hCutFlow", "Data event cut flow", kTH1F, {{10, -0.5f, 9.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/SameEvent/hCutFlow")), eventCutLabels); + + histos.add("EventQA/SameEvent/hVertexZ", "Vertex z; z (cm)", kTH1F, {{400, -20., 20.}}); + histos.add("EventQA/SameEvent/hPrimaryVertexContributors", "PV contributors; N", kTH1F, {{2001, -0.5f, 2000.5f}}); + histos.add("EventQA/SameEvent/hCentralityPercent", "Centrality", kTH1F, {centAxis}); + histos.add("EventQA/SameEvent/hTrackOccupancy", "Occupancy in time range", kTH1F, {occupancyAxis}); + histos.add("EventQA/SameEvent/hCentralityVsVertexZ", "Centrality vs vertex z", kTH2F, {vtxAxis, centAxis}); + histos.add("EventQA/SameEvent/hCentralityVsTrackOccupancy", "Centrality vs occupancy", kTH2F, {occupancyAxis, centAxis}); + + if (qa.cfgFillMultQA) { + histos.add("EventQA/SameEvent/hCentralityVsFilteredTracksVsPVTracks", "Centrality vs filtered tracks vs PV tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Filtered tracks"}, {500, 0, 5000, "PV tracks"}}); + } + + if (qa.cfgFillTrackQA) { + histos.add("TrackQA/BeforeSelection/Proton/hTPCNSigmaVsMomentum", "TPC n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsMomentum", "TOF n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma", "TOF vs TPC n#sigma proton (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTPCNSigmaVsMomentum", "TPC n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsMomentum", "TOF n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma", "TOF vs TPC n#sigma kaon (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + + histos.add("TrackQA/BeforeSelection/AllTracks/hEta", "Reco dN/d#eta; #eta; Tracks", kTH1F, {{50, -1.0, 1.0}}); + histos.add("TrackQA/BeforeSelection/AllTracks/hPt", "Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/BeforeSelection/AllTracks/hPhi", "Reco #varphi; #varphi (rad); Tracks", kTH1F, {phiAxis}); + histos.add("TrackQA/BeforeSelection/AllTracks/hEtaPhi", "Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, phiAxis}); + + histos.add("TrackQA/AfterSelection/Proton/hDCAxyVsPt", "Proton DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/AfterSelection/Proton/hDCAzVsPt", "Proton DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsMomentum", "Proton TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsPt", "Proton TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaVsCentrality", "Proton TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsMomentum", "Proton TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsPt", "Proton TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsCentrality", "Proton TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma", "Proton TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt", "Proton track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt", "Proton track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCdEdxVsMomentum", "Proton TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCClustersVsPhi", "Proton TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCCrossedRowsVsPt", "Proton TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/AfterSelection/Proton/hTPCFoundClustersVsPt", "Proton TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/AfterSelection/Proton/hPt", "Proton p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/AfterSelection/Proton/hEta", "Proton #eta; #eta; Tracks", kTH1F, {etaAxis}); + + histos.add("TrackQA/AfterSelection/Kaon/hDCAxyVsPt", "Kaon DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hDCAzVsPt", "Kaon DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsMomentum", "Kaon TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsPt", "Kaon TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsCentrality", "Kaon TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsMomentum", "Kaon TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsPt", "Kaon TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsCentrality", "Kaon TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma", "Kaon TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt", "Kaon track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt", "Kaon track: TPC n#sigma proton contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCdEdxVsMomentum", "Kaon TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCClustersVsPhi", "Kaon TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCCrossedRowsVsPt", "Kaon TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hTPCFoundClustersVsPt", "Kaon TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hPt", "Kaon p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/AfterSelection/Kaon/hEta", "Kaon #eta; #eta; Tracks", kTH1F, {etaAxis}); + } + + if (qa.cfgFillPairQA) { + histos.add("PairQA/hOpeningAngleBeforePairCuts", "Opening angle (before pair cuts)", kTH1F, {openAngleAxis}); + histos.add("PairQA/hOpeningAngleAfterPairCuts", "Opening angle (after pair cuts)", kTH1F, {openAngleAxis}); + histos.add("PairQA/hDeltaEtaBeforePairCuts", "|#Delta#eta| (before pair cuts)", kTH1F, {deltaEtaAxis}); + histos.add("PairQA/hDeltaEtaAfterPairCuts", "|#Delta#eta| (after pair cuts)", kTH1F, {deltaEtaAxis}); + histos.add("PairQA/hDeltaPhiBeforePairCuts", "|#Delta#varphi| (before pair cuts)", kTH1F, {deltaPhiAxis}); + histos.add("PairQA/hDeltaPhiAfterPairCuts", "|#Delta#varphi| (after pair cuts)", kTH1F, {deltaPhiAxis}); + } + + if (!isPolarizationActive()) { + histos.add("Lambda1520/SameEvent/hUnlikeSignLambda1520", "THnSparse #Lambda(1520) same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hUnlikeSignAntiLambda1520", "THnSparse #bar{#Lambda}(1520) same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hLikeSignPlusPlus", "THnSparse like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hLikeSignMinusMinus", "THnSparse like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + if (rotBkg.enableRotationalBackground) { + histos.add("Lambda1520/SameEvent/hRotatedBackgroundLambda1520", "THnSparse #Lambda(1520) rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520", "THnSparse #bar{#Lambda}(1520) rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.get(HIST("Lambda1520/SameEvent/hRotatedBackgroundLambda1520"))->Sumw2(); + histos.get(HIST("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520"))->Sumw2(); + } + } + + if (isPolarizationActive()) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/SameEvent/hCosThetaStarLambda1520", "cos#theta* #Lambda(1520) unlike-sign", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarAntiLambda1520", "cos#theta* #bar{#Lambda}(1520) unlike-sign", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarLikeSignPP", "cos#theta* like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarLikeSignMM", "cos#theta* like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + + if (spinAlignment.fillRotatedBackgroundPolarization && rotBkg.enableRotationalBackground) { + histos.add("SpinAlignment/SameEvent/hCosThetaStarRotatedLambda1520", "cos#theta* rotated bkg #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/SameEvent/hCosThetaStarRotatedAntiLambda1520", "cos#theta* rotated bkg #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + } + } + + if (doprocessMixedEvent) { + if (!isPolarizationActive()) { + histos.add("Lambda1520/MixedEvent/hMixedEventLambda1520", "THnSparse #Lambda(1520) event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MixedEvent/hMixedEventAntiLambda1520", "THnSparse #bar{#Lambda}(1520) event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + if (mixingCfg.cfgFillMixLikeSign) { + histos.add("Lambda1520/MixedEvent/hMixedEventLikeSignPlusPlus", "THnSparse like-sign p K^{+} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MixedEvent/hMixedEventLikeSignMinusMinus", "THnSparse like-sign #bar{p} K^{-} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + } + + if (qa.cfgFillMixQA) { + histos.add("MixingQA/hMixedEventPoolVertexZ", "Mixed event pool: vertex z; z (cm); Pairs", kTH1F, {vtxAxis}); + histos.add("MixingQA/hMixedEventPoolCentrality", "Mixed event pool: centrality; Centrality (%); Pairs", kTH1F, {centAxis}); + histos.add("MixingQA/hMixedEventPoolVertexZVsCentrality", "Mixed event pool: vertex z vs centrality", kTH2F, {vtxAxis, centAxis}); + histos.add("MixingQA/hMixedEventCollisionPairs", "Number of mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); + } + } + + if (isPolarizationActive() && spinAlignment.fillMixedEventPolarization) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLambda1520", "cos#theta* mixed-event #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarAntiLambda1520", "cos#theta* mixed-event #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLikeSignPP", "cos#theta* mixed-event like-sign p K^{+}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MixedEvent/hCosThetaStarLikeSignMM", "cos#theta* mixed-event like-sign #bar{p} K^{-}", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + } + + if (doprocessMC) { + histos.add("EventSelection/MCReco/hCutFlow", "MC reconstructed event cut flow", kTH1F, {{10, -0.5f, 9.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/MCReco/hCutFlow")), eventCutLabels); + + histos.add("EventQA/MCReco/hSelectedRecoEvents", "Reconstructed INEL>0 events (Nrec, MC reco)", kTH1F, {centAxis}); + histos.add("EventQA/MCReco/hVertexZ", "MC reco vertex z; z (cm)", kTH1F, {{400, -20., 20.}}); + histos.add("EventQA/MCReco/hPrimaryVertexContributors", "MC reco PV contributors; N", kTH1F, {{2001, -0.5f, 2000.5f}}); + + histos.add("Lambda1520/MCReco/hAllPairsLambda1520", "THnSparse #Lambda(1520) MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCReco/hAllPairsAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("Lambda1520/MCReco/hTruthMatchedLambda1520", "THnSparse #Lambda(1520) MC reconstructed (truth-matched)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCReco/hTruthMatchedAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed (truth-matched)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("MCRecoQA/hRecoProtonPtFromLambda1520", "Rec proton from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoProtonPtFromAntiLambda1520", "Rec proton from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoKaonPtFromLambda1520", "Rec kaon from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("MCRecoQA/hRecoKaonPtFromAntiLambda1520", "Rec kaon from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + + histos.add("TrackQA/MCReco/AllTracks/hEta", "MC Reco dN/d#eta; #eta; Tracks", kTH1F, {{50, -1.0, 1.0}}); + histos.add("TrackQA/MCReco/AllTracks/hPt", "MC Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AllTracks/hPhi", "MC Reco #varphi; #varphi (rad); Tracks", kTH1F, {phiAxis}); + histos.add("TrackQA/MCReco/AllTracks/hEtaPhi", "MC Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, phiAxis}); + histos.add("PairQA/MCReco/hTruthMatchedRapidityVsPt", "Truth-matched reconstructed #Lambda(1520): y vs p_{T}", kTH2F, {ptAxis, rapAxis}); + + if (qa.cfgFillTrackQA) { + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTPCNSigmaVsMomentum", "MC reco: TPC n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsMomentum", "MC reco: TOF n#sigma proton vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma", "MC reco: TOF vs TPC n#sigma proton (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTPCNSigmaVsMomentum", "MC reco: TPC n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsMomentum", "MC reco: TOF n#sigma kaon vs p (before PID)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma", "MC reco: TOF vs TPC n#sigma kaon (before PID)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hDCAxyVsPt", "MC reco: Proton DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hDCAzVsPt", "MC reco: Proton DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsMomentum", "MC reco: Proton TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsPt", "MC reco: Proton TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsCentrality", "MC reco: Proton TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsMomentum", "MC reco: Proton TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsPt", "MC reco: Proton TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsCentrality", "MC reco: Proton TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma", "MC reco: Proton TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt", "MC reco: Proton track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt", "MC reco: Proton track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCdEdxVsMomentum", "MC reco: Proton TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCClustersVsPhi", "MC reco: Proton TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCCrossedRowsVsPt", "MC reco: Proton TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hTPCFoundClustersVsPt", "MC reco: Proton TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hPt", "MC reco: Proton p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Proton/hEta", "MC reco: Proton #eta; #eta; Tracks", kTH1F, {etaAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hDCAxyVsPt", "MC reco: Kaon DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hDCAzVsPt", "MC reco: Kaon DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsMomentum", "MC reco: Kaon TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsPt", "MC reco: Kaon TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsCentrality", "MC reco: Kaon TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsMomentum", "MC reco: Kaon TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsPt", "MC reco: Kaon TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsCentrality", "MC reco: Kaon TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma", "MC reco: Kaon TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt", "MC reco: Kaon track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt", "MC reco: Kaon track: TPC n#sigma proton contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCdEdxVsMomentum", "MC reco: Kaon TPC signal vs p (after cuts)", kTH2F, {momentumAxis, tpcSignalAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCClustersVsPhi", "MC reco: Kaon TPC clusters vs #varphi", kTH2F, {tpcClusAxis, phiAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCCrossedRowsVsPt", "MC reco: Kaon TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hTPCFoundClustersVsPt", "MC reco: Kaon TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hPt", "MC reco: Kaon p_{T}; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("TrackQA/MCReco/AfterSelection/Kaon/hEta", "MC reco: Kaon #eta; #eta; Tracks", kTH1F, {etaAxis}); + } + + histos.add("TrackQA/MCReco/TruthMatchedProton/hDCAxyVsPt", "Truth-matched proton DCA_{xy} vs p_{T}", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hDCAzVsPt", "Truth-matched proton DCA_{z} vs p_{T}", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hTPCNSigmaVsPt", "Truth-matched proton TPC n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/TruthMatchedProton/hTOFNSigmaVsPt", "Truth-matched proton TOF n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hDCAxyVsPt", "Truth-matched kaon DCA_{xy} vs p_{T}", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hDCAzVsPt", "Truth-matched kaon DCA_{z} vs p_{T}", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hTPCNSigmaVsPt", "Truth-matched kaon TPC n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("TrackQA/MCReco/TruthMatchedKaon/hTOFNSigmaVsPt", "Truth-matched kaon TOF n#sigma vs p_{T}", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + + histos.add("MCRecoQA/hGeneratedToReconstructedCutFlow", "Generated-to-reconstructed #Lambda(1520) cut flow (efficiency QA)", kTH1F, {{19, -0.5f, 18.5f}}); + setHistogramBinLabels(histos.get(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow")), + {"Generated Lambda(1520) (event accepted)", "Lambda(1520) -> p + K", "Rapidity accepted", + "Proton acceptance", "Kaon acceptance", "Both daughters acceptance", + "Proton reconstructed", "Kaon reconstructed", "Both daughters reconstructed", + "Proton track quality", "Kaon track quality", "Both track quality", + "Proton PID", "Kaon PID", "Both PID", + "Proton DCA", "Kaon DCA", "Both DCA", + "Pair cuts (final reconstructed)"}); + + histos.add("MCRecoQA/hTruthMatchingCutFlow", "MC truth-matching cut flow (efficiency QA)", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("MCRecoQA/hTruthMatchingCutFlow")), + {"Reco pair", "Proton has MC label", "Kaon has MC label", "Proton is proton", "Kaon is kaon", "Common Lambda(1520) mother"}); + + if (isPolarizationActive()) { + const AxisSpec cosThetaAxis{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCReco/hCosThetaStarTruthMatchedLambda1520", "cos#theta* truth-matched #Lambda(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + histos.add("SpinAlignment/MCReco/hCosThetaStarTruthMatchedAntiLambda1520", "cos#theta* truth-matched #bar{#Lambda}(1520)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxis}); + } + + const AxisSpec massResolutionAxis{200, -0.05f, 0.05f, "M_{reco} - M_{gen} (GeV/c^{2})"}; + histos.add("Lambda1520/MCReco/hMassResolutionLambda1520", "(M_{reco} - M_{gen}) truth-matched #Lambda(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis}); + histos.add("Lambda1520/MCReco/hMassResolutionAntiLambda1520", "(M_{reco} - M_{gen}) truth-matched #bar{#Lambda}(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis}); + if (isPolarizationActive()) { + const AxisSpec cosThetaAxisRes{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCReco/hMassResolutionCosThetaLambda1520", "(M_{reco} - M_{gen}) vs cos#theta* #Lambda(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis, cosThetaAxisRes}); + histos.add("SpinAlignment/MCReco/hMassResolutionCosThetaAntiLambda1520", "(M_{reco} - M_{gen}) vs cos#theta* #bar{#Lambda}(1520)", kTHnSparseF, {massResolutionAxis, ptAxis, centAxis, cosThetaAxisRes}); + } + } + + if (doprocessMCEventMixing) { + histos.add("Lambda1520/MCRecoMixedEvent/hMixedEventLambda1520", "THnSparse #Lambda(1520) MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCRecoMixedEvent/hMixedEventAntiLambda1520", "THnSparse #bar{#Lambda}(1520) MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MixingQA/MCReco/hMixedEventCollisionPairs", "Number of MC mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("MixingQA/MCReco/hMixedEventCollisionPairsVsCentrality", "MC mixed-event collision pairs vs centrality", kTH1F, {centAxis}); + } + + if (doprocessMCGen) { + histos.add("EventSelection/MCGen/hCutFlow", "MC generated event cut flow", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/MCGen/hCutFlow")), + {"All", "Vz", "INEL>0", "Centrality", "Final generated event", "Associated reco event accepted"}); + + histos.add("Lambda1520/MCGen/hGeneratedLambda1520", "THnSparse #Lambda(1520) generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCGen/hGeneratedAntiLambda1520", "THnSparse #bar{#Lambda}(1520) generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("Lambda1520/MCGen/hGeneratedLambda1520InAcceptedEvents", "THnSparse #Lambda(1520) generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("Lambda1520/MCGen/hGeneratedAntiLambda1520InAcceptedEvents", "THnSparse #bar{#Lambda}(1520) generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("GeneratedQA/hProtonDaughterPtFromLambda1520", "Gen proton from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hProtonDaughterPtFromAntiLambda1520", "Gen proton from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hKaonDaughterPtFromLambda1520", "Gen kaon from #Lambda(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hKaonDaughterPtFromAntiLambda1520", "Gen kaon from #bar{#Lambda}(1520)", kTH1F, {ptAxis}); + histos.add("GeneratedQA/hLambda1520GeneratedCutFlow", "Generated #Lambda(1520) cut flow", kTH1F, {{8, -0.5f, 7.5f}}); + setHistogramBinLabels(histos.get(HIST("GeneratedQA/hLambda1520GeneratedCutFlow")), + {"All particles", "producedByGenerator", "Lambda(1520) PDG", "Decay p + K", "Rapidity", "Daughter eta", "Eta", "Final"}); + histos.add("GeneratedQA/hEtaVsPtBeforeCuts", "Generated #Lambda(1520) #eta-p_{T} (before kinematic cuts)", kTH2F, {etaAxis, ptAxis}); + histos.add("GeneratedQA/hPhiVsRapidityBeforeCuts", "Generated #Lambda(1520) #varphi-y (before kinematic cuts)", kTH2F, {phiAxis, rapAxis}); + histos.add("GeneratedQA/hEtaVsPtAfterCuts", "Generated #Lambda(1520) #eta-p_{T} (after kinematic cuts)", kTH2F, {etaAxis, ptAxis}); + histos.add("GeneratedQA/hPhiVsRapidityAfterCuts", "Generated #Lambda(1520) #varphi-y (after kinematic cuts)", kTH2F, {phiAxis, rapAxis}); + histos.add("GeneratedQA/hRapidityVsPtFinal", "Generated #Lambda(1520) y vs p_{T} (final)", kTH2F, {ptAxis, rapAxis}); + } + + if (doprocessMCGenPol) { + if (!isPolarizationActive()) { + LOG(fatal) << "processMCGenPol requires at least one polarization frame to be active"; + } + const AxisSpec cosThetaAxisGenPol{spinAlignment.cosThetaBins, "cos#theta*"}; + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520", "cos#theta* generated #Lambda(1520) (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520", "cos#theta* generated #bar{#Lambda}(1520) (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520InAcceptedEvents", "cos#theta* generated #Lambda(1520), event-accepted (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + histos.add("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520InAcceptedEvents", "cos#theta* generated #bar{#Lambda}(1520), event-accepted (pol process)", kTHnSparseF, {massAxis, ptAxis, centAxis, cosThetaAxisGenPol}); + } + + if (doprocessEventFactor) { + histos.add("EventLoss/hAcceptedRecoEventsOfGeneratedInelGt0", "Accepted reconstructed events of generated INEL>0 events (Nrec, MC-truth centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsVzAndInelGt0", "Generated events passing truth |Zvtx| + truth INEL>0 cuts", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsAcceptedTruthCentrality", "Generated events with >=1 accepted reconstructed collision (truth centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hGeneratedEventsAcceptedRecoCentrality", "Generated events with >=1 accepted reconstructed collision (reconstructed centrality)", kTH1F, {centAxis}); + histos.add("EventLoss/hRecoCollisionsPerGeneratedCollision", "Number of reconstructed collisions per generated collision", kTH1F, {{21, -0.5f, 20.5f}}); + histos.add("EventSelection/EventLoss/hGeneratedEventCutFlow", "Event Loss / Event Splitting: generated-event cut flow", kTH1F, {{6, -0.5f, 5.5f}}); + setHistogramBinLabels(histos.get(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow")), + {"All", "Vz", "INEL>0", "HasRecoColl", "RecoCollAccepted", "EventAccepted"}); + } + + if (doprocessSignalLoss) { + histos.add("SignalLoss/Lambda1520/hPt_denominator", "Generated #Lambda(1520) (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/AntiLambda1520/hPt_denominator", "Generated #bar{#Lambda}(1520) (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda1520/hPt_numerator", "Generated #Lambda(1520) (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/AntiLambda1520/hPt_numerator", "Generated #bar{#Lambda}(1520) (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Kaon/hTruePt_denominator", "Kaon, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hMtScaledPt_denominator", "Kaon, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hTruePt_numerator", "Kaon, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Kaon/hMtScaledPt_numerator", "Kaon, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Proton/hTruePt_denominator", "Proton, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hMtScaledPt_denominator", "Proton, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hTruePt_numerator", "Proton, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Proton/hMtScaledPt_numerator", "Proton, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Lambda0/hTruePt_denominator", "Lambda, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hMtScaledPt_denominator", "Lambda, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hTruePt_numerator", "Lambda, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Lambda0/hMtScaledPt_numerator", "Lambda, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Xi/hTruePt_denominator", "Xi, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hMtScaledPt_denominator", "Xi, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hTruePt_numerator", "Xi, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Xi/hMtScaledPt_numerator", "Xi, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + + histos.add("SignalLoss/Omega/hTruePt_denominator", "Omega, true pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hMtScaledPt_denominator", "Omega, mT-scaled pT (den)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hTruePt_numerator", "Omega, true pT (num)", kTH2F, {ptAxis, centAxis}); + histos.add("SignalLoss/Omega/hMtScaledPt_numerator", "Omega, mT-scaled pT (num)", kTH2F, {ptAxis, centAxis}); + } + } + + template + float getCentrality(CollisionType const& collision) + { + switch (evSel.cfgCentralityEstimator) { + case lambda1520_analysis::kFT0M: + return collision.centFT0M(); + case lambda1520_analysis::kFT0A: + return collision.centFT0A(); + case lambda1520_analysis::kFT0C: + return collision.centFT0C(); + default: + return collision.centFT0M(); + } + } + + template + bool isRecoInelGt0(CollisionType const& collision) + { + return collision.isInelGt0(); + } + + template + bool isGeneratedChargedPrimary(McParticleType const& mcPart) + { + if (!mcPart.isPhysicalPrimary()) { + return false; + } + auto* pdgParticle = pdgDB->GetParticle(mcPart.pdgCode()); + if (!pdgParticle) { + return false; + } + return std::abs(pdgParticle->Charge()) > MinAbsCharge; + } + + template + bool isTruthInelGt0(McParticleTableType const& mcParticlesInCollision) + { + for (auto const& mcPart : mcParticlesInCollision) { + if (std::abs(mcPart.eta()) < 1.f && isGeneratedChargedPrimary(mcPart)) { + return true; + } + } + return false; + } + + template + int countGeneratedChargedPrimaries(McPartsT const& partsThisMc) + { + int nChargedPrimaries = 0; + for (auto const& part : partsThisMc) { + if (!part.isPhysicalPrimary()) { + continue; + } + if (part.eta() > mcCfg.generatedMultiplicityEtaMax || part.eta() < mcCfg.generatedMultiplicityEtaMin) { + continue; + } + auto* pdgParticle = pdgDB->GetParticle(part.pdgCode()); + if (!pdgParticle || std::abs(pdgParticle->Charge()) < MinAbsCharge) { + continue; + } + ++nChargedPrimaries; + } + return nChargedPrimaries; + } + + float getCentralityFromGeneratedMultiplicity(int nChargedPrimaries) + { + const auto& cuts = mcCfg.generatedMultiplicityLowerEdges.value; + const auto& centres = mcCfg.centralityPerGeneratedMultiplicityClass.value; + for (size_t i = 0; i < cuts.size(); ++i) { + if (nChargedPrimaries >= cuts[i]) { + return centres[i]; + } + } + return InvalidCentrality; + } + + template + float getTruthCentrality(McCollisionType const& mcCollision, McPartsT const& partsThisMc) + { + if (mcCfg.useGeneratedMultiplicityCentrality) { + return getCentralityFromGeneratedMultiplicity(countGeneratedChargedPrimaries(partsThisMc)); + } + return mcCollision.centFT0M(); + } + + struct NoCutFlowTag { + }; + struct DataCutFlowTag { + }; + struct McRecoCutFlowTag { + }; + + template + void fillEventSelectionCutFlowBin(float bin) + { + if constexpr (std::is_same_v) { + histos.fill(HIST("EventSelection/SameEvent/hCutFlow"), bin); + } else if constexpr (std::is_same_v) { + histos.fill(HIST("EventSelection/MCReco/hCutFlow"), bin); + } + } + + template + bool passesEventSelectionImpl(CollisionType const& collision) + { + fillEventSelectionCutFlowBin(0.f); + + if (std::abs(collision.posZ()) > evSel.cfgCutVertex) { + return false; + } + fillEventSelectionCutFlowBin(1.f); + + if (evSel.cfgUseNoITSROFrameBorderCut && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + fillEventSelectionCutFlowBin(2.f); + + if (evSel.cfgUseSel8 && !collision.sel8()) { + return false; + } + fillEventSelectionCutFlowBin(3.f); + + if (evSel.cfgRequireRecoINELgt0 && !isRecoInelGt0(collision)) { + return false; + } + fillEventSelectionCutFlowBin(4.f); + + if (evSel.applyOccupancyInTimeRangeCut) { + const int trackOccupancy = collision.trackOccupancyInTimeRange(); + if (trackOccupancy < evSel.cfgOccupancyMin || trackOccupancy > evSel.cfgOccupancyMax) { + return false; + } + } + fillEventSelectionCutFlowBin(5.f); + + if (evSel.cfgUseNoSameBunchPileupCut && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + fillEventSelectionCutFlowBin(6.f); + + if (evSel.cfgUseGoodZvtxFT0vsPVCut && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + fillEventSelectionCutFlowBin(7.f); + + if (evSel.cfgUseGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + fillEventSelectionCutFlowBin(8.f); + + fillEventSelectionCutFlowBin(9.f); + return true; + } + + template + bool passesEventSelection(CollisionType const& collision) + { + return passesEventSelectionImpl(collision); + } + + template + bool passesBasicTrackSelection(TrackType const& track) + { + if (trackCuts.applyTPCCrossedRowsCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinTPCCrossedRows) { + return false; + } + if (trackCuts.applyTPCFoundClusterCut && + track.tpcNClsFound() < trackCuts.cfgMinTPCClusters) { + return false; + } + if (trackCuts.applyITSClusterCut && + track.itsNCls() < trackCuts.cfgMinITSClusters) { + return false; + } + if (trackCuts.applyTPCSharedClusterCut && + track.tpcNClsShared() > trackCuts.cfgMaxTPCSharedClusters) { + return false; + } + if (trackCuts.applyTPCCrossedRowsOverFindableCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinCrossedRowsOverFindable * track.tpcNClsFindable()) { + return false; + } + if (trackCuts.applyTPCChi2NClCut && + track.tpcChi2NCl() > trackCuts.cfgMaxTPCChi2NCl) { + return false; + } + if (trackCuts.applyITSChi2NClCut && + track.itsChi2NCl() > trackCuts.cfgMaxITSChi2NCl) { + return false; + } + if (trackCuts.requirePrimaryTrack && !track.isPrimaryTrack()) { + return false; + } + if (trackCuts.requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + return false; + } + if (trackCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return false; + } + if (trackCuts.requirePVContributor && !track.isPVContributor()) { + return false; + } + return true; + } + + template + bool passesDCAxyDCAzSelection(TrackType const& track, std::vector const& ptEdges, std::vector const& maxDCAxyPerBin) + { + if (std::abs(track.dcaZ()) >= dcaCuts.cfgCutDCAz) { + return false; + } + const float pt = track.pt(); + const float dcaXY = std::abs(track.dcaXY()); + const int bin = findBinIndex(ptEdges, pt); + if (bin < 0 || dcaXY >= maxDCAxyPerBin[bin]) { + return false; + } + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxPtDepDCAxy = dcaConst + dcaCoeff / std::pow(pt, dcaExp); + if (dcaXY > maxPtDepDCAxy) { + return false; + } + } + return true; + } + + template + bool passesProtonDCASelection(TrackType const& track) + { + return passesDCAxyDCAzSelection(track, mProtonDCAPtEdges, mProtonMaxDCAxy); + } + + template + bool passesKaonDCASelection(TrackType const& track) + { + return passesDCAxyDCAzSelection(track, mKaonDCAPtEdges, mKaonMaxDCAxy); + } + + static int findBinIndex(std::vector const& edges, float x) + { + for (size_t i = 0; i + 1 < edges.size(); ++i) { + if (x >= edges[i] && x < edges[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + bool passesMomentumDependentPID(float p, bool hasTOF, float tpcNSigma, float tofNSigma, SpeciesPIDSettings const& cuts) + { + const float absTPC = std::abs(tpcNSigma); + if (pidShared.useTPCOnlyPIDAtAllMomenta) { + return absTPC < cuts.maxTPCNSigma; + } + if (!hasTOF) { + if (p >= cuts.thresholdMomentum) { + return false; + } + const int bin = findBinIndex(cuts.tpcOnlyBins, p); + return bin >= 0 && absTPC < cuts.tpcOnlyCuts[bin]; + } + const float absTOF = std::abs(tofNSigma); + const int bin = findBinIndex(cuts.combinedBins, p); + return bin >= 0 && (absTPC * absTPC + absTOF * absTOF) < cuts.combinedCuts[bin] * cuts.combinedCuts[bin]; + } + + template + bool passesProtonPID(TrackType const& track, float totalMomentum) + { + if (protonPID.requireTOFForProton && !track.hasTOF()) { + return false; + } + const bool hasTOF = track.hasTOF(); + const float tofNSigma = hasTOF ? track.tofNSigmaPr() : -999.f; + return passesMomentumDependentPID(totalMomentum, hasTOF, track.tpcNSigmaPr(), tofNSigma, mProtonPIDCuts); + } + + template + bool isPionContaminatedKaonCandidate(TrackType const& track) + { + return track.pt() > pidShared.pionRejectionMinPt && + track.pt() < pidShared.pionRejectionMaxPt && + !track.hasTOF() && + std::abs(track.tpcNSigmaPi()) < pidShared.pionRejectionMaxTPCNSigma; + } + + template + bool passesKaonPID(TrackType const& track, float totalMomentum) + { + if (kaonPID.requireTOFForKaon && !track.hasTOF()) { + return false; + } + if (pidShared.applyKaonPionRejection && isPionContaminatedKaonCandidate(track)) { + return false; + } + const bool hasTOF = track.hasTOF(); + const float tofNSigma = hasTOF ? track.tofNSigmaKa() : -999.f; + return passesMomentumDependentPID(totalMomentum, hasTOF, track.tpcNSigmaKa(), tofNSigma, mKaonPIDCuts); + } + + template + DaughterTrackCandidate makeTrackCandidate(TrackType const& track) + { + DaughterTrackCandidate cand{}; + cand.px = track.px(); + cand.py = track.py(); + cand.pz = track.pz(); + cand.pt = track.pt(); + cand.eta = track.eta(); + cand.phi = track.phi(); + cand.mom = RecoDecay::p(track.px(), track.py(), track.pz()); + cand.dcaXY = track.dcaXY(); + cand.dcaZ = track.dcaZ(); + cand.sign = track.sign(); + cand.hasTOF = track.hasTOF(); + cand.tpcNSigmaPr = track.tpcNSigmaPr(); + cand.tpcNSigmaKa = track.tpcNSigmaKa(); + if (cand.hasTOF) { + cand.tofNSigmaPr = track.tofNSigmaPr(); + cand.tofNSigmaKa = track.tofNSigmaKa(); + } + cand.globalIndex = track.globalIndex(); + return cand; + } + + template + void attachMCTruthToCandidate(DaughterTrackCandidate& cand, TrackType const& track) + { + if (!track.has_mcParticle()) { + return; + } + auto const mcPart = track.mcParticle(); + cand.hasMcLabel = true; + cand.mcPdgCode = mcPart.pdgCode(); + if (!mcPart.has_mothers()) { + return; + } + for (auto const& mother : mcPart.template mothers_as()) { + if (std::abs(mother.pdgCode()) == mLambda1520AbsPdgCode) { + cand.mcLambda1520MotherIndex = mother.globalIndex(); + cand.mcLambda1520MotherPdgCode = mother.pdgCode(); + break; + } + } + } + + template + void buildProtonKaonCandidatePools(TrackCollection const& tracks, std::vector& protons, std::vector& kaons) + { + protons.clear(); + kaons.clear(); + for (auto const& track : tracks) { + if (!passesBasicTrackSelection(track)) { + continue; + } + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + const bool isProton = passesProtonPID(track, mom) && passesProtonDCASelection(track); + const bool isKaon = passesKaonPID(track, mom) && passesKaonDCASelection(track); + if (!isProton && !isKaon) { + continue; + } + DaughterTrackCandidate cand = makeTrackCandidate(track); + if constexpr (IsMC) { + attachMCTruthToCandidate(cand, track); + } + if (isProton) { + protons.push_back(cand); + } + if (isKaon) { + kaons.push_back(cand); + } + } + } + + void computeDaughterPairGeometry(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float& deltaEta, float& deltaPhi, float& openingAngle) + { + deltaEta = std::abs(pr.eta - ka.eta); + deltaPhi = std::abs(pr.phi - ka.phi); + if (deltaPhi > o2::constants::math::PI) { + deltaPhi = o2::constants::math::TwoPI - deltaPhi; + } + const float dot = pr.px * ka.px + pr.py * ka.py + pr.pz * ka.pz; + openingAngle = std::acos(std::clamp(dot / (pr.mom * ka.mom), -1.f, 1.f)); + } + + bool passesDaughterAngularCuts(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + if (!pairCuts.applyDaughterDeltaEtaPhiCuts && !pairCuts.applyDeepAngleCut && !pairCuts.applyOpeningAngleVeto) { + return true; + } + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + if (pairCuts.applyDaughterDeltaEtaPhiCuts) { + const float maxDaughterDeltaEtaValue = pairCuts.maxDaughterDeltaEta; + const float maxDaughterDeltaPhiValue = pairCuts.maxDaughterDeltaPhi; + if (deltaEta >= maxDaughterDeltaEtaValue || deltaPhi >= maxDaughterDeltaPhiValue) { + return false; + } + } + if (pairCuts.applyDeepAngleCut) { + const float minOpeningAngle = pairCuts.deepAngleCutValue; + if (openingAngle < minOpeningAngle) { + return false; + } + } + if (pairCuts.applyOpeningAngleVeto) { + const float openingAngleVetoLow = pairCuts.openingAngleVetoMin; + const float openingAngleVetoHigh = pairCuts.openingAngleVetoMax; + if (openingAngle > openingAngleVetoLow && openingAngle < openingAngleVetoHigh) { + return false; + } + } + return true; + } + + bool buildProtonKaonPair(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float& pairMass, float& pairPt, float& pairY) + { + if (!passesDaughterAngularCuts(pr, ka)) { + return false; + } + const std::array, 2> momenta = { + std::array{pr.px, pr.py, pr.pz}, + std::array{ka.px, ka.py, ka.pz}}; + pairMass = RecoDecay::m(momenta, kProtonKaonMasses); + pairPt = RecoDecay::pt(std::array{pr.px + ka.px, pr.py + ka.py}); + pairY = RecoDecay::y(std::array{pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}, pairMass); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (pairY < minRapidity || pairY > maxRapidity) { + return false; + } + if (pairCuts.applyPairPtMassUpperLimits) { + const float maxPairPt = pairCuts.cfgMaxPairPt; + const float maxPairMass = pairCuts.cfgMaxPairMass; + if (pairPt >= maxPairPt || pairMass >= maxPairMass) { + return false; + } + } + return true; + } + + void fillPairQABefore(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + histos.fill(HIST("PairQA/hOpeningAngleBeforePairCuts"), openingAngle); + histos.fill(HIST("PairQA/hDeltaEtaBeforePairCuts"), deltaEta); + histos.fill(HIST("PairQA/hDeltaPhiBeforePairCuts"), deltaPhi); + } + + void fillPairQAAfter(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka) + { + float deltaEta = 0.f, deltaPhi = 0.f, openingAngle = 0.f; + computeDaughterPairGeometry(pr, ka, deltaEta, deltaPhi, openingAngle); + histos.fill(HIST("PairQA/hOpeningAngleAfterPairCuts"), openingAngle); + histos.fill(HIST("PairQA/hDeltaEtaAfterPairCuts"), deltaEta); + histos.fill(HIST("PairQA/hDeltaPhiAfterPairCuts"), deltaPhi); + } + + static float vecDot(const std::array& a, const std::array& b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + static std::array vecSub(const std::array& a, const std::array& b) + { + return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; + } + + static std::array vecCross(const std::array& a, const std::array& b) + { + return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]}; + } + + static constexpr float KVecUnitEpsilon = 1e-9f; + static std::array vecUnit(const std::array& a) + { + float mag = std::sqrt(vecDot(a, a)); + if (mag < KVecUnitEpsilon) { + return {0.f, 0.f, 0.f}; + } + return {a[0] / mag, a[1] / mag, a[2] / mag}; + } + + static constexpr float KBoostMomentumEpsilon = 1e-6f; + static std::array boostToRestFrame(float motherE, const std::array& motherP, + float dE, const std::array& dP) + { + float pMag2 = vecDot(motherP, motherP); + float pMag = std::sqrt(pMag2); + if (pMag < KBoostMomentumEpsilon) { + return dP; + } + + std::array nHat = {motherP[0] / pMag, motherP[1] / pMag, motherP[2] / pMag}; + float beta = pMag / motherE; + float motherMass2 = motherE * motherE - pMag2; + float gamma = motherMass2 > 0.f ? motherE / std::sqrt(motherMass2) : 1.f; + + float pParallel = vecDot(dP, nHat); + std::array pPerp = {dP[0] - pParallel * nHat[0], dP[1] - pParallel * nHat[1], dP[2] - pParallel * nHat[2]}; + float pParallelBoosted = gamma * (pParallel - beta * dE); + + return {pPerp[0] + pParallelBoosted * nHat[0], pPerp[1] + pParallelBoosted * nHat[1], pPerp[2] + pParallelBoosted * nHat[2]}; + } + + enum class PolarizationBackgroundMode { + Signal, + LikeSign, + Rotated, + Mixed, + LikeSignMixed, + TruthMatched + }; + + bool isPolarizationActive() const + { + return spinAlignment.activateHelicityFrame.value || + spinAlignment.activateCollinsSoperFrame.value || + spinAlignment.activateProductionFrame.value || + spinAlignment.activateBeamAxisFrame.value || + spinAlignment.activateRandomFrame.value; + } + + template + void fillCosThetaStarHistograms(float candidateMass, float candidatePt, + const std::array& motherMom, float motherE, + const std::array& protonMom, float protonE, + const std::array& kaonMom, float kaonE, + float centrality, bool isParticle) + { + if (!isPolarizationActive()) { + return; + } + + std::array protonCM = boostToRestFrame(motherE, motherMom, protonE, protonMom); + std::array kaonCM = boostToRestFrame(motherE, motherMom, kaonE, kaonMom); + std::array refCM = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCM : kaonCM); + + auto fillFrame = [&](float cosTheta) { + if constexpr (Mode == PolarizationBackgroundMode::Signal) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::LikeSign) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLikeSignPP"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarLikeSignMM"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::Rotated) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarRotatedLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/SameEvent/hCosThetaStarRotatedAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::Mixed) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::LikeSignMixed) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLikeSignPP"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MixedEvent/hCosThetaStarLikeSignMM"), candidateMass, candidatePt, centrality, cosTheta); + } + } else if constexpr (Mode == PolarizationBackgroundMode::TruthMatched) { + if (isParticle) { + histos.fill(HIST("SpinAlignment/MCReco/hCosThetaStarTruthMatchedLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCReco/hCosThetaStarTruthMatchedAntiLambda1520"), candidateMass, candidatePt, centrality, cosTheta); + } + } + }; + + if (spinAlignment.activateHelicityFrame) { + std::array zAxisHE = vecUnit(motherMom); + fillFrame(vecDot(refCM, zAxisHE)); + } + + if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CM = vecUnit(boostToRestFrame(motherE, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CM = vecUnit(boostToRestFrame(motherE, motherMom, mBeam2Energy, mBeam2Momentum)); + + if (spinAlignment.activateCollinsSoperFrame) { + std::array zAxisCS = vecUnit(vecSub(beam1CM, beam2CM)); + fillFrame(vecDot(refCM, zAxisCS)); + } + if (spinAlignment.activateProductionFrame) { + std::array motherDir = vecUnit(motherMom); + std::array yAxisPR = vecUnit(vecCross(beam1CM, motherDir)); + fillFrame(vecDot(refCM, yAxisPR)); + } + if (spinAlignment.activateBeamAxisFrame) { + fillFrame(vecDot(refCM, beam1CM)); + } + } + + if (spinAlignment.activateRandomFrame) { + double randCos = mUniform01(mRandomEngine) * 2. - 1.; + double randPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double randSin = std::sqrt(1. - randCos * randCos); + std::array zAxisRD = {static_cast(randSin * std::cos(randPhi)), + static_cast(randSin * std::sin(randPhi)), + static_cast(randCos)}; + fillFrame(vecDot(refCM, zAxisRD)); + } + } + + void fillLambda1520HistogramSameEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (isPolarizationActive()) { + return; + } + if (protonSign * kaonSign < 0) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hUnlikeSignLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hUnlikeSignAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hLikeSignPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hLikeSignMinusMinus"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillLambda1520HistogramMixedEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (isPolarizationActive()) { + return; + } + if (protonSign * kaonSign < 0) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } else if (mixingCfg.cfgFillMixLikeSign) { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLikeSignPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MixedEvent/hMixedEventLikeSignMinusMinus"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillLambda1520HistogramMCRecoAllPairs(int protonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MCReco/hAllPairsLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCReco/hAllPairsAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillLambda1520HistogramTruthMatched(int motherPdg, float pairPt, float pairMass, float centrality, float rapidity) + { + if (motherPdg > 0) { + histos.fill(HIST("Lambda1520/MCReco/hTruthMatchedLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCReco/hTruthMatchedAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillLambda1520HistogramMCRecoMixedEvent(int protonSign, int kaonSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign * kaonSign >= 0) { + return; + } + if (protonSign > 0) { + histos.fill(HIST("Lambda1520/MCRecoMixedEvent/hMixedEventLambda1520"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Lambda1520/MCRecoMixedEvent/hMixedEventAntiLambda1520"), pairMass, pairPt, centrality, rapidity); + } + } + + void fillRotatedPairBackground(DaughterTrackCandidate const& pr, DaughterTrackCandidate const& ka, float centrality) + { + const int nRot = rotBkg.numberOfRotations; + if (nRot <= 0) { + return; + } + const float weight = 1.f / static_cast(nRot); + const float rotationHalfWindow = o2::constants::math::PI / static_cast(rotBkg.rotationAngleWindow); + const bool rotateProtonInsteadOfKaon = rotBkg.rotateProtonInsteadOfKaon; + const DaughterTrackCandidate& rotated = rotateProtonInsteadOfKaon ? pr : ka; + + for (int iRot = 0; iRot < nRot; ++iRot) { + float rotationAngle = o2::constants::math::PI; + if (nRot > 1) { + rotationAngle = (o2::constants::math::PI - rotationHalfWindow) + + static_cast(iRot) * (2.f * rotationHalfWindow / static_cast(nRot - 1)); + } + const float rotatedPhi = RecoDecay::constrainAngle(rotated.phi + rotationAngle, 0.f); + const float rotatedPx = rotated.pt * std::cos(rotatedPhi); + const float rotatedPy = rotated.pt * std::sin(rotatedPhi); + + const std::array momPr = rotateProtonInsteadOfKaon ? std::array{rotatedPx, rotatedPy, pr.pz} + : std::array{pr.px, pr.py, pr.pz}; + const std::array momKa = rotateProtonInsteadOfKaon ? std::array{ka.px, ka.py, ka.pz} + : std::array{rotatedPx, rotatedPy, ka.pz}; + const std::array, 2> rotMomenta = {momPr, momKa}; + + const float rotatedMass = RecoDecay::m(rotMomenta, kProtonKaonMasses); + const float pairPxSum = momPr[0] + momKa[0]; + const float pairPySum = momPr[1] + momKa[1]; + const float pairPzSum = momPr[2] + momKa[2]; + const float rotatedPt = RecoDecay::pt(std::array{pairPxSum, pairPySum}); + const float rotatedRapidity = RecoDecay::y(std::array{pairPxSum, pairPySum, pairPzSum}, rotatedMass); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (rotatedRapidity < minRapidity || rotatedRapidity > maxRapidity) { + continue; + } + if (pairCuts.applyPairPtMassUpperLimits) { + const float maxPairPt = pairCuts.cfgMaxPairPt; + const float maxPairMass = pairCuts.cfgMaxPairMass; + if (rotatedPt >= maxPairPt || rotatedMass >= maxPairMass) { + continue; + } + } + + if (!isPolarizationActive()) { + if (pr.sign > 0) { + histos.fill(HIST("Lambda1520/SameEvent/hRotatedBackgroundLambda1520"), rotatedMass, rotatedPt, centrality, rotatedRapidity, weight); + } else { + histos.fill(HIST("Lambda1520/SameEvent/hRotatedBackgroundAntiLambda1520"), rotatedMass, rotatedPt, centrality, rotatedRapidity, weight); + } + } + + if (isPolarizationActive() && spinAlignment.fillRotatedBackgroundPolarization) { + const float protonE = std::sqrt(momPr[0] * momPr[0] + momPr[1] * momPr[1] + momPr[2] * momPr[2] + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(momKa[0] * momKa[0] + momKa[1] * momKa[1] + momKa[2] * momKa[2] + KaonMass * KaonMass); + const std::array rotMotherMom = {pairPxSum, pairPySum, pairPzSum}; + const float rotMotherE = protonE + kaonE; + fillCosThetaStarHistograms( + rotatedMass, rotatedPt, rotMotherMom, rotMotherE, momPr, protonE, momKa, kaonE, centrality, pr.sign > 0); + } + } + } + + template + void fillInvariantMassFromCandidatePools( + std::vector const& protonPool, + std::vector const& kaonPool, + float centrality, + bool fillPairQA = false) + { + for (auto const& pr : protonPool) { + for (auto const& ka : kaonPool) { + + if constexpr (!IsMixed) { + if (pr.globalIndex == ka.globalIndex) { + continue; + } + if (fillPairQA) { + fillPairQABefore(pr, ka); + } + } + + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (!buildProtonKaonPair(pr, ka, pairMass, pairPt, pairY)) { + continue; + } + + if constexpr (!IsMixed) { + if (fillPairQA) { + fillPairQAAfter(pr, ka); + } + } + + if constexpr (IsMixed) { + if constexpr (IsMC) { + fillLambda1520HistogramMCRecoMixedEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + } else { + fillLambda1520HistogramMixedEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + + if (isPolarizationActive() && spinAlignment.fillMixedEventPolarization) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + + if (pr.sign * ka.sign < 0) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } else if (mixingCfg.cfgFillMixLikeSign) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } + } + } + } else { + fillLambda1520HistogramSameEvent(pr.sign, ka.sign, pairPt, pairMass, centrality, pairY); + + if (isPolarizationActive()) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + + if (pr.sign * ka.sign < 0) { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } else { + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.sign > 0); + } + } + + if (rotBkg.enableRotationalBackground && pr.sign * ka.sign < 0) { + fillRotatedPairBackground(pr, ka, centrality); + } + } + } + } + } + + template + void fillSelectedProtonQA(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsMomentum"), mom, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaVsCentrality"), centrality, tpcNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hPt"), pt); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr(); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsMomentum"), mom, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsCentrality"), centrality, tofNSigPr); + histos.fill(HIST("TrackQA/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + } + } + + template + void fillSelectedKaonQA(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigKa = track.tpcNSigmaKa(); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsMomentum"), mom, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsPt"), pt, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaVsCentrality"), centrality, tpcNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hPt"), pt); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigKa = track.tofNSigmaKa(); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsMomentum"), mom, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsPt"), pt, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsCentrality"), centrality, tofNSigKa); + histos.fill(HIST("TrackQA/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma"), tpcNSigKa, tofNSigKa); + } + } + + template + void fillSelectedProtonQAMC(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsMomentum"), mom, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaVsCentrality"), centrality, tpcNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCNSigmaKaonHypothesisVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hPt"), pt); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsMomentum"), mom, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsCentrality"), centrality, tofNSigPr); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Proton/hTOFNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + } + } + + template + void fillSelectedKaonQAMC(TrackType const& track, float totalMomentum, float centrality) + { + const float pt = track.pt(); + const float mom = totalMomentum; + const float tpcNSigKa = track.tpcNSigmaKa(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hDCAxyVsPt"), pt, track.dcaXY()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hDCAzVsPt"), pt, track.dcaZ()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsMomentum"), mom, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsPt"), pt, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaVsCentrality"), centrality, tpcNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaPionHypothesisVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCNSigmaProtonHypothesisVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCdEdxVsMomentum"), track.tpcInnerParam(), track.tpcSignal()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCClustersVsPhi"), track.tpcNClsFound(), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTPCFoundClustersVsPt"), pt, track.tpcNClsFound()); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hPt"), pt); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hEta"), track.eta()); + if (track.hasTOF()) { + const float tofNSigKa = track.tofNSigmaKa(); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsMomentum"), mom, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsPt"), pt, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsCentrality"), centrality, tofNSigKa); + histos.fill(HIST("TrackQA/MCReco/AfterSelection/Kaon/hTOFNSigmaVsTPCNSigma"), tpcNSigKa, tofNSigKa); + } + } + + template + bool findProtonKaonDaughters(McPart const& mother, McPart& proton, McPart& kaon) + { + if (!mother.has_daughters()) { + return false; + } + bool hasProtonDaughter = false, hasKaonDaughter = false; + for (auto const& d : mother.template daughters_as()) { + const int absPdgCode = std::abs(d.pdgCode()); + if (absPdgCode == lambda1520_analysis::PdgProton) { + hasProtonDaughter = true; + proton = d; + } else if (absPdgCode == lambda1520_analysis::PdgKaon) { + hasKaonDaughter = true; + kaon = d; + } + } + return hasProtonDaughter && hasKaonDaughter; + } + + struct GeneratedDaughterMatch { + bool found = false; + bool trackQuality = false; + bool pid = false; + bool dca = false; + DaughterTrackCandidate cand{}; + }; + + template + GeneratedDaughterMatch findRecoTrackOfGeneratedDaughter(TracksSlice const& tracksSlice, McPart const& mcDaughter, bool isProton) + { + GeneratedDaughterMatch result; + for (auto const& trk : tracksSlice) { + if (!trk.has_mcParticle() || trk.mcParticle().globalIndex() != mcDaughter.globalIndex()) { + continue; + } + result.found = true; + result.trackQuality = passesBasicTrackSelection(trk); + const float mom = RecoDecay::p(trk.px(), trk.py(), trk.pz()); + if (isProton) { + result.pid = passesProtonPID(trk, mom); + result.dca = passesProtonDCASelection(trk); + } else { + result.pid = passesKaonPID(trk, mom); + result.dca = passesKaonDCASelection(trk); + } + result.cand = makeTrackCandidate(trk); + break; + } + return result; + } + + template + void fillGeneratedToReconstructedCutFlow(TracksSlice const& tracksOfCollision, aod::McParticles const& mcParticles, int64_t mcCollisionId) + { + auto genParts = mcParticles.sliceBy(perMcCollision, mcCollisionId); + for (auto const& gen : genParts) { + if (!gen.producedByGenerator() || std::abs(gen.pdgCode()) != mLambda1520AbsPdgCode) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 0.f); + + auto genProton = gen; + auto genKaon = gen; + if (!findProtonKaonDaughters(gen, genProton, genKaon)) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 1.f); + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + if (gen.y() < minRapidity || gen.y() > maxRapidity) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 2.f); + + const float maxTrackEta = trackCuts.cfgCutEta; + const float minTrackPt = trackCuts.cfgCutPt; + const bool protonInAcceptance = std::abs(genProton.eta()) < maxTrackEta && genProton.pt() > minTrackPt; + const bool kaonInAcceptance = std::abs(genKaon.eta()) < maxTrackEta && genKaon.pt() > minTrackPt; + if (protonInAcceptance) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 3.f); + } + if (kaonInAcceptance) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 4.f); + } + if (!protonInAcceptance || !kaonInAcceptance) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 5.f); + + const auto protonMatch = findRecoTrackOfGeneratedDaughter(tracksOfCollision, genProton, true); + const auto kaonMatch = findRecoTrackOfGeneratedDaughter(tracksOfCollision, genKaon, false); + if (protonMatch.found) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 6.f); + } + if (kaonMatch.found) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 7.f); + } + if (!protonMatch.found || !kaonMatch.found) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 8.f); + + if (protonMatch.trackQuality) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 9.f); + } + if (kaonMatch.trackQuality) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 10.f); + } + if (!protonMatch.trackQuality || !kaonMatch.trackQuality) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 11.f); + + if (protonMatch.pid) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 12.f); + } + if (kaonMatch.pid) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 13.f); + } + if (!protonMatch.pid || !kaonMatch.pid) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 14.f); + + if (protonMatch.dca) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 15.f); + } + if (kaonMatch.dca) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 16.f); + } + if (!protonMatch.dca || !kaonMatch.dca) { + continue; + } + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 17.f); + + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (buildProtonKaonPair(protonMatch.cand, kaonMatch.cand, pairMass, pairPt, pairY)) { + histos.fill(HIST("MCRecoQA/hGeneratedToReconstructedCutFlow"), 18.f); + } + } + } + + static float computeMtScaledPt(float pt, float referenceMass) + { + const float mtScaledPtSquared = pt * pt + referenceMass * referenceMass - Lambda1520Mass * Lambda1520Mass; + return (mtScaledPtSquared > 0.f) ? std::sqrt(mtScaledPtSquared) : -1.f; + } + + template + void fillSignalLossProxySpecies(McPartType const& part, float centrality) + { + const int absPdgCode = std::abs(part.pdgCode()); + const float pt = part.pt(); + if (pt <= 0.f) { + return; + } + + if (absPdgCode == lambda1520_analysis::PdgKaon) { + const float mtScaledPt = computeMtScaledPt(pt, KaonMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Kaon/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Kaon/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Kaon/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Kaon/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgProton) { + const float mtScaledPt = computeMtScaledPt(pt, ProtonMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Proton/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Proton/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Proton/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Proton/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgLambda0) { + const float mtScaledPt = computeMtScaledPt(pt, LambdaMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Lambda0/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Lambda0/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Lambda0/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Lambda0/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgXiMinus) { + const float mtScaledPt = computeMtScaledPt(pt, XiMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Xi/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Xi/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Xi/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Xi/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } else if (absPdgCode == lambda1520_analysis::PdgOmegaMinus) { + const float mtScaledPt = computeMtScaledPt(pt, OmegaMass); + const float weight = (mcCfg.applyMtScaleWeight && mtScaledPt > 0.f) ? mtScaledPt / pt : 1.f; + if constexpr (IsNumerator) { + histos.fill(HIST("SignalLoss/Omega/hTruePt_numerator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Omega/hMtScaledPt_numerator"), mtScaledPt, centrality, weight); + } + } else { + histos.fill(HIST("SignalLoss/Omega/hTruePt_denominator"), pt, centrality); + if (mtScaledPt > 0.f) { + histos.fill(HIST("SignalLoss/Omega/hMtScaledPt_denominator"), mtScaledPt, centrality, weight); + } + } + } + } + + Filter acceptanceFilter = (nabs(aod::track::eta) < trackCuts.cfgCutEta && nabs(aod::track::pt) > trackCuts.cfgCutPt); + + using EventCandidates = soa::Join; + using TrackBase = soa::Join; + using TrackCandidates = soa::Filtered; + using TrackCandidatesMC = soa::Filtered>; + using EventCandidatesMC = soa::Join; + using McCollisionsCent = soa::Join; + + Preslice perCol = aod::track::collisionId; + Preslice perColMC = aod::track::collisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + + SameKindPair pairFT0M{binningFT0M, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0A{binningFT0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0C{binningFT0C, mixingCfg.cfgNoMixedEvents, -1, &cache}; + + void processNothing(aod::Origins const& origins) + { + if (a0001PrintMessages) { + LOG(info) << "DEBUG :: Process Nothing :: origins = " << origins.size(); + } + // Intentionally empty. Keeps the task alive when the struct is not supposed to run. + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processNothing, "Dummy process for analysis", true); + + void processSameEvent(EventCandidates const& collisions, + TrackCandidates const& tracks, + aod::BCs const&) + { + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + const float centrality = getCentrality(collision); + const int occupancy = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("EventQA/SameEvent/hVertexZ"), collision.posZ()); + histos.fill(HIST("EventQA/SameEvent/hPrimaryVertexContributors"), collision.numContrib()); + histos.fill(HIST("EventQA/SameEvent/hCentralityPercent"), centrality); + histos.fill(HIST("EventQA/SameEvent/hTrackOccupancy"), occupancy); + histos.fill(HIST("EventQA/SameEvent/hCentralityVsVertexZ"), collision.posZ(), centrality); + histos.fill(HIST("EventQA/SameEvent/hCentralityVsTrackOccupancy"), occupancy, centrality); + + const int64_t collisionIndex = collision.globalIndex(); + auto tracksOfCollision = tracks.sliceBy(perCol, collisionIndex); + tracksOfCollision.bindExternalIndices(&tracks); + + if (qa.cfgFillMultQA) { + histos.fill(HIST("EventQA/SameEvent/hCentralityVsFilteredTracksVsPVTracks"), centrality, tracksOfCollision.size(), collision.multNTracksPV()); + } + + if (qa.cfgFillTrackQA) { + for (auto const& track : tracksOfCollision) { + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaKa()); + histos.fill(HIST("TrackQA/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hEta"), track.eta()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hPt"), track.pt()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hPhi"), track.phi()); + histos.fill(HIST("TrackQA/BeforeSelection/AllTracks/hEtaPhi"), track.eta(), track.phi()); + + if (!passesBasicTrackSelection(track)) { + continue; + } + if (passesProtonPID(track, mom) && passesProtonDCASelection(track)) { + fillSelectedProtonQA(track, mom, centrality); + } + if (passesKaonPID(track, mom) && passesKaonDCASelection(track)) { + fillSelectedKaonQA(track, mom, centrality); + } + } + } + + buildProtonKaonCandidatePools(tracksOfCollision, mProtonCandidates, mKaonCandidates); + fillInvariantMassFromCandidatePools(mProtonCandidates, mKaonCandidates, centrality, qa.cfgFillPairQA); + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processSameEvent, "Process same event (data)", true); + + template + void loopOverMixedEventPairs(PairType& mixingPair) + { + for (auto const& [c1, tracks1, c2, tracks2] : mixingPair) { + if (!passesEventSelection(c1) || !passesEventSelection(c2)) { + continue; + } + const float centrality = getCentrality(c1); + + if constexpr (IsMC) { + histos.fill(HIST("MixingQA/MCReco/hMixedEventCollisionPairs"), 1.f); + histos.fill(HIST("MixingQA/MCReco/hMixedEventCollisionPairsVsCentrality"), centrality); + } else { + if (qa.cfgFillMixQA) { + histos.fill(HIST("MixingQA/hMixedEventPoolVertexZ"), c1.posZ()); + histos.fill(HIST("MixingQA/hMixedEventPoolCentrality"), centrality); + histos.fill(HIST("MixingQA/hMixedEventPoolVertexZVsCentrality"), c1.posZ(), centrality); + histos.fill(HIST("MixingQA/hMixedEventCollisionPairs"), 1.f); + } + } + + std::vector protonPool1, kaonPool1, protonPool2, kaonPool2; + buildProtonKaonCandidatePools(tracks1, protonPool1, kaonPool1); + buildProtonKaonCandidatePools(tracks2, protonPool2, kaonPool2); + fillInvariantMassFromCandidatePools(protonPool1, kaonPool2, centrality); + fillInvariantMassFromCandidatePools(protonPool2, kaonPool1, centrality); + } + } + + template + void runMixedEventForSelectedEstimator() + { + switch (evSel.cfgCentralityEstimator) { + case lambda1520_analysis::kFT0M: + loopOverMixedEventPairs(pairFT0M); + break; + case lambda1520_analysis::kFT0A: + loopOverMixedEventPairs(pairFT0A); + break; + case lambda1520_analysis::kFT0C: + loopOverMixedEventPairs(pairFT0C); + break; + default: + loopOverMixedEventPairs(pairFT0M); + break; + } + } + + void processMixedEvent(EventCandidates const&, TrackCandidates const&) + { + runMixedEventForSelectedEstimator(); + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMixedEvent, "Process mixed event (data)", true); + + void processMCEventMixing(EventCandidates const&, TrackCandidates const&) + { + runMixedEventForSelectedEstimator(); + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCEventMixing, "Process MC reconstructed event mixing (reuses the mixing pairs; no truth matching)", false); + + void processMC(EventCandidatesMC const& collisions, + aod::BCs const&, + TrackCandidatesMC const& tracks, + aod::McParticles const& mcParticles) + { + std::unordered_set mcCollisionsWithEfficiencyQA; + + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + if (!collision.has_mcCollision()) { + continue; + } + const float centrality = getCentrality(collision); + + histos.fill(HIST("EventQA/MCReco/hSelectedRecoEvents"), centrality); + histos.fill(HIST("EventQA/MCReco/hVertexZ"), collision.posZ()); + histos.fill(HIST("EventQA/MCReco/hPrimaryVertexContributors"), collision.numContrib()); + + const int64_t collisionIndex = collision.globalIndex(); + auto tracksOfCollision = tracks.sliceBy(perColMC, collisionIndex); + tracksOfCollision.bindExternalIndices(&tracks); + tracksOfCollision.bindExternalIndices(&mcParticles); + + for (auto const& track : tracksOfCollision) { + histos.fill(HIST("TrackQA/MCReco/AllTracks/hEta"), track.eta()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hPt"), track.pt()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hPhi"), track.phi()); + histos.fill(HIST("TrackQA/MCReco/AllTracks/hEtaPhi"), track.eta(), track.phi()); + + if (qa.cfgFillTrackQA) { + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTPCNSigmaVsMomentum"), mom, track.tpcNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Proton/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsMomentum"), mom, track.tofNSigmaKa()); + histos.fill(HIST("TrackQA/MCReco/BeforeSelection/Kaon/hTOFNSigmaVsTPCNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + if (passesBasicTrackSelection(track)) { + if (passesProtonPID(track, mom) && passesProtonDCASelection(track)) { + fillSelectedProtonQAMC(track, mom, centrality); + } + if (passesKaonPID(track, mom) && passesKaonDCASelection(track)) { + fillSelectedKaonQAMC(track, mom, centrality); + } + } + } + } + + buildProtonKaonCandidatePools(tracksOfCollision, mProtonCandidates, mKaonCandidates); + + for (auto const& pr : mProtonCandidates) { + if (pr.mcLambda1520MotherIndex >= 0 && std::abs(pr.mcPdgCode) == lambda1520_analysis::PdgProton) { + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("MCRecoQA/hRecoProtonPtFromLambda1520"), pr.pt); + } else { + histos.fill(HIST("MCRecoQA/hRecoProtonPtFromAntiLambda1520"), pr.pt); + } + } + } + for (auto const& ka : mKaonCandidates) { + if (ka.mcLambda1520MotherIndex >= 0 && std::abs(ka.mcPdgCode) == lambda1520_analysis::PdgKaon) { + if (ka.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("MCRecoQA/hRecoKaonPtFromLambda1520"), ka.pt); + } else { + histos.fill(HIST("MCRecoQA/hRecoKaonPtFromAntiLambda1520"), ka.pt); + } + } + } + + for (auto const& pr : mProtonCandidates) { + for (auto const& ka : mKaonCandidates) { + if (pr.globalIndex == ka.globalIndex) { + continue; + } + if (pr.sign * ka.sign >= 0) { + continue; + } + float pairMass = 0.f, pairPt = 0.f, pairY = 0.f; + if (!buildProtonKaonPair(pr, ka, pairMass, pairPt, pairY)) { + continue; + } + + fillLambda1520HistogramMCRecoAllPairs(pr.sign, pairPt, pairMass, centrality, pairY); + + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 0.f); + if (pr.hasMcLabel) { + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 1.f); + } + if (ka.hasMcLabel) { + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 2.f); + } + if (!pr.hasMcLabel || !ka.hasMcLabel) { + continue; + } + if (std::abs(pr.mcPdgCode) != lambda1520_analysis::PdgProton) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 3.f); + if (std::abs(ka.mcPdgCode) != lambda1520_analysis::PdgKaon) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 4.f); + if (pr.mcLambda1520MotherIndex < 0 || pr.mcLambda1520MotherIndex != ka.mcLambda1520MotherIndex) { + continue; + } + histos.fill(HIST("MCRecoQA/hTruthMatchingCutFlow"), 5.f); + + if (mcCfg.applyGeneratedEtaCut) { + const float pairEta = RecoDecay::eta(std::array{pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}); + if (std::abs(pairEta) > mcCfg.generatedEtaMax) { + continue; + } + } + + fillLambda1520HistogramTruthMatched(pr.mcLambda1520MotherPdgCode, pairPt, pairMass, centrality, pairY); + + { + auto const mcMother = mcParticles.iteratorAt(pr.mcLambda1520MotherIndex); + const float genE = mcMother.e(); + const float genPx = mcMother.px(); + const float genPy = mcMother.py(); + const float genPz = mcMother.pz(); + const float genMass2 = genE * genE - genPx * genPx - genPy * genPy - genPz * genPz; + const float genMass = std::sqrt(std::max(0.f, genMass2)); + const float massResolution = pairMass - genMass; + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("Lambda1520/MCReco/hMassResolutionLambda1520"), massResolution, pairPt, centrality); + } else { + histos.fill(HIST("Lambda1520/MCReco/hMassResolutionAntiLambda1520"), massResolution, pairPt, centrality); + } + } + + if (isPolarizationActive()) { + const float protonE = std::sqrt(pr.px * pr.px + pr.py * pr.py + pr.pz * pr.pz + ProtonMass * ProtonMass); + const float kaonE = std::sqrt(ka.px * ka.px + ka.py * ka.py + ka.pz * ka.pz + KaonMass * KaonMass); + const std::array motherMom = {pr.px + ka.px, pr.py + ka.py, pr.pz + ka.pz}; + const float motherE = protonE + kaonE; + const std::array protonMom = {pr.px, pr.py, pr.pz}; + const std::array kaonMom = {ka.px, ka.py, ka.pz}; + fillCosThetaStarHistograms( + pairMass, pairPt, motherMom, motherE, protonMom, protonE, kaonMom, kaonE, centrality, pr.mcLambda1520MotherPdgCode > 0); + + { + auto const mcMother = mcParticles.iteratorAt(pr.mcLambda1520MotherIndex); + const float genE2 = mcMother.e(); + const float genPx2 = mcMother.px(); + const float genPy2 = mcMother.py(); + const float genPz2 = mcMother.pz(); + const float genM2 = genE2 * genE2 - genPx2 * genPx2 - genPy2 * genPy2 - genPz2 * genPz2; + const float genMassVal = std::sqrt(std::max(0.f, genM2)); + const float massRes = pairMass - genMassVal; + + std::array protonCMRes = boostToRestFrame(motherE, motherMom, protonE, protonMom); + std::array kaonCMRes = boostToRestFrame(motherE, motherMom, kaonE, kaonMom); + std::array refCMRes = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCMRes : kaonCMRes); + float cosThetaRes = 0.f; + if (spinAlignment.activateHelicityFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(motherMom)); + } else if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CMRes = vecUnit(boostToRestFrame(motherE, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CMRes = vecUnit(boostToRestFrame(motherE, motherMom, mBeam2Energy, mBeam2Momentum)); + if (spinAlignment.activateCollinsSoperFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(vecSub(beam1CMRes, beam2CMRes))); + } else if (spinAlignment.activateProductionFrame) { + cosThetaRes = vecDot(refCMRes, vecUnit(vecCross(beam1CMRes, vecUnit(motherMom)))); + } else { + cosThetaRes = vecDot(refCMRes, beam1CMRes); + } + } else if (spinAlignment.activateRandomFrame) { + double rCos = mUniform01(mRandomEngine) * 2. - 1.; + double rPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double rSin = std::sqrt(1. - rCos * rCos); + std::array zRD = {static_cast(rSin * std::cos(rPhi)), + static_cast(rSin * std::sin(rPhi)), + static_cast(rCos)}; + cosThetaRes = vecDot(refCMRes, zRD); + } + if (pr.mcLambda1520MotherPdgCode > 0) { + histos.fill(HIST("SpinAlignment/MCReco/hMassResolutionCosThetaLambda1520"), massRes, pairPt, centrality, cosThetaRes); + } else { + histos.fill(HIST("SpinAlignment/MCReco/hMassResolutionCosThetaAntiLambda1520"), massRes, pairPt, centrality, cosThetaRes); + } + } + } + + histos.fill(HIST("PairQA/MCReco/hTruthMatchedRapidityVsPt"), pairPt, pairY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hDCAxyVsPt"), pr.pt, pr.dcaXY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hDCAzVsPt"), pr.pt, pr.dcaZ); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hTPCNSigmaVsPt"), pr.pt, pr.tpcNSigmaPr); + if (pr.hasTOF) { + histos.fill(HIST("TrackQA/MCReco/TruthMatchedProton/hTOFNSigmaVsPt"), pr.pt, pr.tofNSigmaPr); + } + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hDCAxyVsPt"), ka.pt, ka.dcaXY); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hDCAzVsPt"), ka.pt, ka.dcaZ); + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hTPCNSigmaVsPt"), ka.pt, ka.tpcNSigmaKa); + if (ka.hasTOF) { + histos.fill(HIST("TrackQA/MCReco/TruthMatchedKaon/hTOFNSigmaVsPt"), ka.pt, ka.tofNSigmaKa); + } + } + } + + if (mcCollisionsWithEfficiencyQA.insert(collision.mcCollisionId()).second) { + fillGeneratedToReconstructedCutFlow(tracksOfCollision, mcParticles, collision.mcCollisionId()); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMC, "Process MC reconstructed (truth matching)", false); + + void processMCGen(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 0.f); + + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 1.f); + + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 2.f); + + float genCentrality = getTruthCentrality(mcCollision, mcParticles); + if (genCentrality == InvalidCentrality) { + return; + } + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 3.f); + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 4.f); + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + if (!evSel.cfgUseMCTruthCentrality) { + genCentrality = getCentrality(collision); + } + break; + } + } + if (hasAcceptedReco) { + histos.fill(HIST("EventSelection/MCGen/hCutFlow"), 5.f); + } + + for (auto const& mcParticle : mcParticles) { + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 0.f); + + if (!mcParticle.producedByGenerator()) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 1.f); + + const int pdg = mcParticle.pdgCode(); + if (std::abs(pdg) != mLambda1520AbsPdgCode) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 2.f); + + auto genProton = mcParticle; + auto genKaon = mcParticle; + if (!findProtonKaonDaughters(mcParticle, genProton, genKaon)) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 3.f); + + const float genPt = mcParticle.pt(); + const float genY = mcParticle.y(); + histos.fill(HIST("GeneratedQA/hEtaVsPtBeforeCuts"), mcParticle.eta(), genPt); + histos.fill(HIST("GeneratedQA/hPhiVsRapidityBeforeCuts"), mcParticle.phi(), genY); + + if (genY < trackCuts.cfgMinY || genY > trackCuts.cfgMaxY) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 4.f); + + const float maxTrackEta = trackCuts.cfgCutEta; + if (mcCfg.applyGeneratedDaughterEtaCut && (std::abs(genProton.eta()) >= maxTrackEta || std::abs(genKaon.eta()) >= maxTrackEta)) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 5.f); + + if (mcCfg.applyGeneratedEtaCut && std::abs(mcParticle.eta()) > mcCfg.generatedEtaMax) { + continue; + } + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 6.f); + histos.fill(HIST("GeneratedQA/hLambda1520GeneratedCutFlow"), 7.f); + + histos.fill(HIST("GeneratedQA/hEtaVsPtAfterCuts"), mcParticle.eta(), genPt); + histos.fill(HIST("GeneratedQA/hPhiVsRapidityAfterCuts"), mcParticle.phi(), genY); + histos.fill(HIST("GeneratedQA/hRapidityVsPtFinal"), genPt, genY); + + const float motherEnergy = mcParticle.e(); + const float motherPx = mcParticle.px(); + const float motherPy = mcParticle.py(); + const float motherPz = mcParticle.pz(); + const float mass2 = motherEnergy * motherEnergy - motherPx * motherPx - motherPy * motherPy - motherPz * motherPz; + const float genMass = std::sqrt(std::max(0.f, mass2)); + const float ptPr = genProton.pt(); + const float ptKa = genKaon.pt(); + + if (pdg > 0) { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedLambda1520"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("GeneratedQA/hProtonDaughterPtFromLambda1520"), ptPr); + histos.fill(HIST("GeneratedQA/hKaonDaughterPtFromLambda1520"), ptKa); + } else { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedAntiLambda1520"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("GeneratedQA/hProtonDaughterPtFromAntiLambda1520"), ptPr); + histos.fill(HIST("GeneratedQA/hKaonDaughterPtFromAntiLambda1520"), ptKa); + } + + if (hasAcceptedReco) { + if (pdg > 0) { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, genY); + } else { + histos.fill(HIST("Lambda1520/MCGen/hGeneratedAntiLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, genY); + } + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCGen, "Process MC truth: generated-level Lambda(1520) spectra and QA", false); + + void processMCGenPol(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + float genCentrality = getTruthCentrality(mcCollision, mcParticles); + if (genCentrality == InvalidCentrality) { + return; + } + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + if (!evSel.cfgUseMCTruthCentrality) { + genCentrality = getCentrality(collision); + } + break; + } + } + + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.producedByGenerator()) { + continue; + } + const int pdg = mcParticle.pdgCode(); + if (std::abs(pdg) != mLambda1520AbsPdgCode) { + continue; + } + auto genProton = mcParticle; + auto genKaon = mcParticle; + if (!findProtonKaonDaughters(mcParticle, genProton, genKaon)) { + continue; + } + + const float genPt = mcParticle.pt(); + const float genY = mcParticle.y(); + if (genY < trackCuts.cfgMinY || genY > trackCuts.cfgMaxY) { + continue; + } + + const float maxTrackEta = trackCuts.cfgCutEta; + if (mcCfg.applyGeneratedDaughterEtaCut && (std::abs(genProton.eta()) >= maxTrackEta || std::abs(genKaon.eta()) >= maxTrackEta)) { + continue; + } + if (mcCfg.applyGeneratedEtaCut && std::abs(mcParticle.eta()) > mcCfg.generatedEtaMax) { + continue; + } + + const float motherEnergy = mcParticle.e(); + const float motherPx = mcParticle.px(); + const float motherPy = mcParticle.py(); + const float motherPz = mcParticle.pz(); + const float mass2 = motherEnergy * motherEnergy - motherPx * motherPx - motherPy * motherPy - motherPz * motherPz; + const float genMass = std::sqrt(std::max(0.f, mass2)); + + const std::array motherMom = {motherPx, motherPy, motherPz}; + const std::array protonMom = {genProton.px(), genProton.py(), genProton.pz()}; + const std::array kaonMom = {genKaon.px(), genKaon.py(), genKaon.pz()}; + const float protonE = genProton.e(); + const float kaonE = genKaon.e(); + + std::array protonCM = boostToRestFrame(motherEnergy, motherMom, protonE, protonMom); + std::array kaonCM = boostToRestFrame(motherEnergy, motherMom, kaonE, kaonMom); + std::array refCM = vecUnit(spinAlignment.referenceFrameDaughter == 0 ? protonCM : kaonCM); + + float cosTheta = 0.f; + if (spinAlignment.activateHelicityFrame) { + cosTheta = vecDot(refCM, vecUnit(motherMom)); + } else if (spinAlignment.activateCollinsSoperFrame || spinAlignment.activateProductionFrame || spinAlignment.activateBeamAxisFrame) { + std::array beam1CM = vecUnit(boostToRestFrame(motherEnergy, motherMom, mBeam1Energy, mBeam1Momentum)); + std::array beam2CM = vecUnit(boostToRestFrame(motherEnergy, motherMom, mBeam2Energy, mBeam2Momentum)); + if (spinAlignment.activateCollinsSoperFrame) { + cosTheta = vecDot(refCM, vecUnit(vecSub(beam1CM, beam2CM))); + } else if (spinAlignment.activateProductionFrame) { + cosTheta = vecDot(refCM, vecUnit(vecCross(beam1CM, vecUnit(motherMom)))); + } else { + cosTheta = vecDot(refCM, beam1CM); + } + } else if (spinAlignment.activateRandomFrame) { + double rCos = mUniform01(mRandomEngine) * 2. - 1.; + double rPhi = mUniform01(mRandomEngine) * o2::constants::math::TwoPI; + double rSin = std::sqrt(1. - rCos * rCos); + std::array zRD = {static_cast(rSin * std::cos(rPhi)), + static_cast(rSin * std::sin(rPhi)), + static_cast(rCos)}; + cosTheta = vecDot(refCM, zRD); + } + + if (pdg > 0) { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520"), genMass, genPt, genCentrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520"), genMass, genPt, genCentrality, cosTheta); + } + + if (hasAcceptedReco) { + if (pdg > 0) { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, cosTheta); + } else { + histos.fill(HIST("SpinAlignment/MCGenPol/hCosThetaStarGeneratedAntiLambda1520InAcceptedEvents"), genMass, genPt, genCentrality, cosTheta); + } + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processMCGenPol, "Process MC truth: generated-level Lambda(1520) polarization cos(theta*) with same cuts as processMCGen", false); + + void processEventFactor(EventCandidatesMC const& collisions, + McCollisionsCent const& mcCollisions, + aod::McParticles const& mcParticles) + { + std::unordered_map nRecoCollisionsPerMc; + std::unordered_map acceptedMcCollisionRecoCent; + for (auto const& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + ++nRecoCollisionsPerMc[collision.mcCollisionId()]; + if (!passesEventSelection(collision)) { + continue; + } + acceptedMcCollisionRecoCent.emplace(collision.mcCollisionId(), getCentrality(collision)); + + const auto& mcCollision = collision.mcCollision_as(); + auto mcParticlesOfRecoCollision = mcParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticlesOfRecoCollision)) { + continue; + } + const float truthCentrality = getTruthCentrality(mcCollision, mcParticlesOfRecoCollision); + if (truthCentrality == InvalidCentrality) { + continue; + } + histos.fill(HIST("EventLoss/hAcceptedRecoEventsOfGeneratedInelGt0"), truthCentrality); + } + + for (auto const& mcCollision : mcCollisions) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 0.f); + + auto mcParticlesOfGenCollision = mcParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); + const bool isInsideVertexRange = std::abs(mcCollision.posZ()) <= evSel.cfgCutVertex; + const bool truthInelGt0 = isTruthInelGt0(mcParticlesOfGenCollision); + const float truthCentrality = getTruthCentrality(mcCollision, mcParticlesOfGenCollision); + + if (!isInsideVertexRange) { + continue; + } + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 1.f); + + if (evSel.cfgApplyMCTruthINELgt0 && !truthInelGt0) { + continue; + } + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 2.f); + + if (truthCentrality == InvalidCentrality) { + continue; + } + histos.fill(HIST("EventLoss/hGeneratedEventsVzAndInelGt0"), truthCentrality); + + const auto recoCollisionCountEntry = nRecoCollisionsPerMc.find(mcCollision.globalIndex()); + const int nRecoTotal = (recoCollisionCountEntry != nRecoCollisionsPerMc.end()) ? recoCollisionCountEntry->second : 0; + histos.fill(HIST("EventLoss/hRecoCollisionsPerGeneratedCollision"), nRecoTotal); + + if (nRecoTotal > 0) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 3.f); + } + + const auto acceptedRecoCentralityEntry = acceptedMcCollisionRecoCent.find(mcCollision.globalIndex()); + if (acceptedRecoCentralityEntry != acceptedMcCollisionRecoCent.end()) { + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 4.f); + histos.fill(HIST("EventSelection/EventLoss/hGeneratedEventCutFlow"), 5.f); + histos.fill(HIST("EventLoss/hGeneratedEventsAcceptedTruthCentrality"), truthCentrality); + histos.fill(HIST("EventLoss/hGeneratedEventsAcceptedRecoCentrality"), acceptedRecoCentralityEntry->second); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processEventFactor, "Process Event Loss / Event Splitting factors", false); + + void processSignalLoss(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcParticles)) { + return; + } + const float centrality = getTruthCentrality(mcCollision, mcParticles); + if (centrality == InvalidCentrality) { + return; + } + + bool hasAcceptedReco = false; + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + break; + } + } + + const float minRapidity = trackCuts.cfgMinY; + const float maxRapidity = trackCuts.cfgMaxY; + const float maxAbsGeneratedEta = mcCfg.generatedEtaMax; + + for (auto const& part : mcParticles) { + const int absPdgCode = std::abs(part.pdgCode()); + const bool isLambda1520 = absPdgCode == mLambda1520AbsPdgCode; + const bool isProxySpecies = absPdgCode == lambda1520_analysis::PdgKaon || + absPdgCode == lambda1520_analysis::PdgProton || + absPdgCode == lambda1520_analysis::PdgLambda0 || + absPdgCode == lambda1520_analysis::PdgXiMinus || + absPdgCode == lambda1520_analysis::PdgOmegaMinus; + if (!isLambda1520 && !isProxySpecies) { + continue; + } + + if (part.y() < minRapidity || part.y() > maxRapidity) { + continue; + } + if (mcCfg.applyGeneratedEtaCut && std::abs(part.eta()) > maxAbsGeneratedEta) { + continue; + } + + if (isLambda1520) { + if (!part.producedByGenerator()) { + continue; + } + auto genProton = part; + auto genKaon = part; + if (!findProtonKaonDaughters(part, genProton, genKaon)) { + continue; + } + if (part.pdgCode() > 0) { + histos.fill(HIST("SignalLoss/Lambda1520/hPt_denominator"), part.pt(), centrality); + if (hasAcceptedReco) { + histos.fill(HIST("SignalLoss/Lambda1520/hPt_numerator"), part.pt(), centrality); + } + } else { + histos.fill(HIST("SignalLoss/AntiLambda1520/hPt_denominator"), part.pt(), centrality); + if (hasAcceptedReco) { + histos.fill(HIST("SignalLoss/AntiLambda1520/hPt_numerator"), part.pt(), centrality); + } + } + continue; + } + + if (!part.isPhysicalPrimary()) { + continue; + } + fillSignalLossProxySpecies(part, centrality); + if (hasAcceptedReco) { + fillSignalLossProxySpecies(part, centrality); + } + } + } + PROCESS_SWITCH(HParticleCorrelation4Block4, processSignalLoss, "Process signal loss (K, p, Lambda, Xi, Omega proxies and Lambda(1520))", false); +}; + WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ adaptAnalysisTask(context), adaptAnalysisTask(context), - adaptAnalysisTask(context)}; + adaptAnalysisTask(context), + adaptAnalysisTask(context), + }; } From 9bc458513ca62d7382b1efb838967d177238d1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Nov 2026 07:27:34 +0100 Subject: [PATCH 612/648] [PWGUD] Fix unused variables, parameters, values (#18647) --- PWGUD/TableProducer/UPCCandidateProducer.cxx | 2 +- PWGUD/TableProducer/tauEventTableProducer.cxx | 2 +- PWGUD/TableProducer/twoTracksEventTableProducer.cxx | 2 +- PWGUD/Tasks/diffQA.cxx | 2 +- PWGUD/Tasks/exclusivePentaquark.cxx | 2 +- PWGUD/Tasks/exclusivePhiLeptons.cxx | 6 +++--- PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx | 6 +++--- PWGUD/Tasks/exclusiveTwoProtons.cxx | 4 ++-- PWGUD/Tasks/exclusiveTwoProtonsSG.cxx | 4 ++-- PWGUD/Tasks/sgExclOmega.cxx | 2 +- PWGUD/Tasks/sgSpectraAnalyzer.cxx | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/PWGUD/TableProducer/UPCCandidateProducer.cxx b/PWGUD/TableProducer/UPCCandidateProducer.cxx index b5bcd1cd848..670e6b1f158 100644 --- a/PWGUD/TableProducer/UPCCandidateProducer.cxx +++ b/PWGUD/TableProducer/UPCCandidateProducer.cxx @@ -876,7 +876,7 @@ struct UpcCandProducer { ++curit; if (curit == v.end()) break; - curbc = curit->first; + // curbc = curit->first; // FIXME: unused } uint32_t size = curit->second.size(); if (size > 1) // too many tracks per BC -> possibly another event diff --git a/PWGUD/TableProducer/tauEventTableProducer.cxx b/PWGUD/TableProducer/tauEventTableProducer.cxx index 2538415540f..d7e15959b85 100644 --- a/PWGUD/TableProducer/tauEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauEventTableProducer.cxx @@ -513,7 +513,7 @@ struct TauEventTableProducer { if (verboseInfo) printLargeMessage("Truth collision has more than 1 reco collision. Skipping this event."); histos.get(HIST("Truth/hTroubles"))->Fill(1); - problem = true; + // problem = true; // FIXME: unused continue; } // grap reco-level collision diff --git a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx index cf5f91a34a6..3526401d72c 100644 --- a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx +++ b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx @@ -577,7 +577,7 @@ struct TwoTracksEventTableProducer { if (verboseInfo) printLargeMessage("Truth collision has more than 1 reco collision. Skipping this event."); histos.get(HIST("Truth/hTroubles"))->Fill(1); - problem = true; + // problem = true; // FIXME: unused continue; } // grap reco-level collision diff --git a/PWGUD/Tasks/diffQA.cxx b/PWGUD/Tasks/diffQA.cxx index adc79e0c37f..b97b7e3eeb6 100644 --- a/PWGUD/Tasks/diffQA.cxx +++ b/PWGUD/Tasks/diffQA.cxx @@ -950,7 +950,7 @@ struct DiffQA { for (const auto& col : collisions) { bc1 = -1; bc2 = -2; - bc3 = -3; + // bc3 = -3; // FIXME: unused if (col.has_foundBC()) { auto bc = col.foundBC_as(); bc1 = bc.globalBC(); diff --git a/PWGUD/Tasks/exclusivePentaquark.cxx b/PWGUD/Tasks/exclusivePentaquark.cxx index ed5b440add0..a37d73f1096 100644 --- a/PWGUD/Tasks/exclusivePentaquark.cxx +++ b/PWGUD/Tasks/exclusivePentaquark.cxx @@ -116,7 +116,7 @@ struct ExclusivePentaquark { int truegapSide = sgSelector.trueGap(collision, FV0_cut, FT0A_cut, FT0C_cut, ZDC_cut); registry.fill(HIST("GapSide"), gapSide); registry.fill(HIST("TrueGapSide"), truegapSide); - gapSide = truegapSide; + // gapSide = truegapSide; // if (gapSide != gap_Side) { // return; // } diff --git a/PWGUD/Tasks/exclusivePhiLeptons.cxx b/PWGUD/Tasks/exclusivePhiLeptons.cxx index 9576ece02b5..383273a34cc 100644 --- a/PWGUD/Tasks/exclusivePhiLeptons.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptons.cxx @@ -259,7 +259,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracks[1].Eta()); resonance += onlyElectronTracks[0]; resonance += onlyElectronTracks[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigma[1] * onlyElectronSigma[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigma[1]); @@ -285,7 +285,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracksTOF[0].Eta()); resonance += onlyElectronTracks[0]; resonance += onlyElectronTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigmaTOF[0]); @@ -311,7 +311,7 @@ struct ExclusivePhiLeptons { registry.fill(HIST("hEta1"), onlyElectronTracksTOF[1].Eta()); resonance += onlyElectronTracksTOF[0]; resonance += onlyElectronTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0] + onlyElectronSigmaTOF[1] * onlyElectronSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigmaTOF[0], onlyElectronSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx index 5a7ccd7a315..3bf485b55da 100644 --- a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx @@ -237,7 +237,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracks[1]; a += onlyElectronTracks[0]; b += onlyElectronTracks[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigma[1] * onlyElectronSigma[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigma[1]); @@ -254,7 +254,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracksTOF[0]; a += onlyElectronTracks[0]; b += onlyElectronTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigma[0] * onlyElectronSigma[0] + onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigma[0], onlyElectronSigmaTOF[0]); @@ -271,7 +271,7 @@ struct ExclusivePhiLeptonsTrees { resonance += onlyElectronTracksTOF[1]; a += onlyElectronTracksTOF[0]; b += onlyElectronTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyElectronSigmaTOF[0] * onlyElectronSigmaTOF[0] + onlyElectronSigmaTOF[1] * onlyElectronSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyElectronSigmaTOF[0], onlyElectronSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusiveTwoProtons.cxx b/PWGUD/Tasks/exclusiveTwoProtons.cxx index 3b0b081dc86..5fea2cb18e7 100644 --- a/PWGUD/Tasks/exclusiveTwoProtons.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtons.cxx @@ -284,7 +284,7 @@ struct ExclusiveTwoProtons { resonance += onlyProtonTracksTOF[0]; a = onlyProtonTracks[0]; b = onlyProtonTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigma[0] * onlyProtonSigma[0] + onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigma[0], onlyProtonSigmaTOF[0]); @@ -327,7 +327,7 @@ struct ExclusiveTwoProtons { resonance += onlyProtonTracksTOF[1]; a = onlyProtonTracksTOF[0]; b = onlyProtonTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0] + onlyProtonSigmaTOF[1] * onlyProtonSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigmaTOF[0], onlyProtonSigmaTOF[1]); diff --git a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx index def67ca1bd0..a28473e7c41 100644 --- a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx @@ -311,7 +311,7 @@ struct ExclusiveTwoProtonsSG { resonance += onlyProtonTracksTOF[0]; a = onlyProtonTracks[0]; b = onlyProtonTracksTOF[0]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigma[0] * onlyProtonSigma[0] + onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigma[0], onlyProtonSigmaTOF[0]); @@ -355,7 +355,7 @@ struct ExclusiveTwoProtonsSG { resonance += onlyProtonTracksTOF[1]; a = onlyProtonTracksTOF[0]; b = onlyProtonTracksTOF[1]; - sigmaTotal = 0; + // sigmaTotal = 0; // FIXME: unused sigmaTotal = onlyProtonSigmaTOF[0] * onlyProtonSigmaTOF[0] + onlyProtonSigmaTOF[1] * onlyProtonSigmaTOF[1]; ; registry.fill(HIST("hNsigEvsKa1"), onlyProtonSigmaTOF[0], onlyProtonSigmaTOF[1]); diff --git a/PWGUD/Tasks/sgExclOmega.cxx b/PWGUD/Tasks/sgExclOmega.cxx index cb8c298ce0f..14da74f4411 100644 --- a/PWGUD/Tasks/sgExclOmega.cxx +++ b/PWGUD/Tasks/sgExclOmega.cxx @@ -120,7 +120,7 @@ struct SGExclOmega { int truegapSide = sgSelector.trueGap(collision, FIT_cut[0], FIT_cut[1], FIT_cut[2], ZDC_cut); registry.fill(HIST("GapSide"), gapSide); registry.fill(HIST("TrueGapSide"), truegapSide); - gapSide = truegapSide; + // gapSide = truegapSide; // if (gapSide!=2) return; int pvtracks = 0; int esign = 0; diff --git a/PWGUD/Tasks/sgSpectraAnalyzer.cxx b/PWGUD/Tasks/sgSpectraAnalyzer.cxx index 2d0d577bb84..bd041e7269b 100644 --- a/PWGUD/Tasks/sgSpectraAnalyzer.cxx +++ b/PWGUD/Tasks/sgSpectraAnalyzer.cxx @@ -234,7 +234,7 @@ struct SGSpectraAnalyzer { int goodtracks = 0; int alltracks = 0; sum.SetXYZM(0, 0, 0, 0); - int gapSide = collision.gapSide(); + int gapSide{}; // = collision.gapSide(); // FIXME: unused float FIT_cut[5] = {FV0_cut, FT0A_cut, FT0C_cut, FDDA_cut, FDDC_cut}; std::vector parameters = {PV_cut, dcaZ_cut, dcaXY_cut, tpcChi2_cut, tpcNClsFindable_cut, itsChi2_cut, eta_cut, pt_cut}; int truegapSide = sgSelector.trueGap(collision, FIT_cut[0], FIT_cut[1], FIT_cut[2], ZDC_cut); From 5a0ad2d090f0b8530fc3fb10ff9edf969da2f828 Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Mon, 9 Nov 2026 08:40:26 +0100 Subject: [PATCH 613/648] [PWGDQ] improve track extrapolation in ML training data producer (#18626) --- PWGDQ/Tasks/mftMchMatcher.cxx | 50 +++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/PWGDQ/Tasks/mftMchMatcher.cxx b/PWGDQ/Tasks/mftMchMatcher.cxx index 6149433f586..69b0c669945 100644 --- a/PWGDQ/Tasks/mftMchMatcher.cxx +++ b/PWGDQ/Tasks/mftMchMatcher.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include @@ -279,6 +281,8 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::parameters::GRPMagField* fGrpMag = nullptr; + double mBzAtMftCenter{0}; + std::uniform_real_distribution mDistribution{0.0, 1.0}; std::mt19937 mGenerator; @@ -302,6 +306,7 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::track::TrackParCovFwd trackparCov{track.z(), tpars, tcovs, chi2}; o2::dataformats::GlobalFwdTrack fwdtrack; fwdtrack.setParameters(trackparCov.getParameters()); + fwdtrack.setZ(trackparCov.getZ()); fwdtrack.setCovariances(trackparCov.getCovariances()); return fwdtrack; } @@ -422,6 +427,12 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::base::Propagator::initFieldFromGRP(fGrpMag); VarManager::SetMagneticField(fGrpMag->getNominalL3Field()); VarManager::SetupMuonMagField(); + + auto* fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (fieldB) { + const std::array centerMft{0, 0, -61.4}; // Field at center of MFT + mBzAtMftCenter = fieldB->getBz(centerMft.data()); + } } mRunNumber = bc.runNumber(); } @@ -647,6 +658,42 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) return attempts; } + void propagateToZ(o2::track::TrackParCovFwd& mft, o2::dataformats::GlobalFwdTrack& muon, const double z) + { + auto mchTrack = mExtrap.FwdtoMCH(muon); + + // MCH track extrapolation + // The multiple scattering effect is fully associated to the MCH track + // independently of the position of the matching plane + constexpr float absFront = -90.f; + constexpr float absBack = -505.f; + if (muon.getZ() < absBack) { + // the track origin is downstream of the absorber + if (z > absFront) { + // the destination is upstream of the absorber + // full extrapolation through the absorber in the upstream direction + o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, z); + } else { + // the destination is inside or downstream of the absorber + // 1. do a full extrapolation through the absorber to estimate the MS effect + o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, absFront + 0.1f); + // 2. extrapolate from there back to the destination z + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } + } else { + // all other cases + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } + + auto proptrack = mExtrap.MCHtoFwd(mchTrack); + muon.setParameters(proptrack.getParameters()); + muon.setZ(proptrack.getZ()); + muon.setCovariances(proptrack.getCovariances()); + + // MFT extrapolation without MS effects, as they are fully included in the MCH track extrapolation + mft.propagateToZhelix(z, mBzAtMftCenter); + } + template void fillTable(TCOLLS const& collisions, TBCS const& bcs, @@ -707,8 +754,7 @@ struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) o2::track::TrackParCovFwd mftprop = VarManager::FwdToTrackPar(mfttrack, mfttrackcov); o2::dataformats::GlobalFwdTrack muonprop = trackToGlobalFwd(muontrack, muontrack); if (cfgzMatching.value < 0.) { - mftprop = VarManager::PropagateFwd(mfttrack, mfttrackcov, cfgzMatching.value); - muonprop = VarManager::PropagateMuon(muontrack, collision, VarManager::kToMatching); + propagateToZ(mftprop, muonprop, cfgzMatching.value); } auto muonpropCov = muonprop.getCovariances(); auto mftpropCov = mftprop.getCovariances(); From b0eedb8412956c9140d59a1acdb2790cc9985826 Mon Sep 17 00:00:00 2001 From: Igor Altsybeev Date: Mon, 9 Nov 2026 09:02:06 +0100 Subject: [PATCH 614/648] [Common] factorize the occupancy calculation into OccupancyCalculator (#18623) --- Common/Tools/EventSelectionModule.h | 619 ++++++++++++++-------------- 1 file changed, 307 insertions(+), 312 deletions(-) diff --git a/Common/Tools/EventSelectionModule.h b/Common/Tools/EventSelectionModule.h index 99691db4420..0d4fda380b0 100644 --- a/Common/Tools/EventSelectionModule.h +++ b/Common/Tools/EventSelectionModule.h @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -615,6 +614,281 @@ class BcSelectionModule } // end processRun3 }; // end BcSelectionModule +// Occupancy calculation (used by EventSelectionModule) +// The occupancy wrt a reference point is calculated from other collisions ("sources") nearby in time: +// in the same ITS ROF, in the previous ITS ROF and in a time window around the reference. + +class OccupancyCalculator +{ + public: + // per-collision input for the occupancy calculation + struct Source { + int64_t globalBC = 0; // global BC of the collision (after the collision-BC matching) + int nTracksITS567 = 0; // number of PV contributors with >= 5 ITS clusters + int proxyNtracks = 0; // nTracksITS567, or FT0C/10 if the collision is close to an ITS ROF border + float ampFT0C = 0; // FT0C amplitude in the found BC + float vZ = 0; // z of the primary vertex + bool isRejectedByTFborderCut = false; // the found BC is close to a TF border (kNoTimeFrameBorder is not set) + }; + + // occupancy estimators and flags wrt a given reference + struct Result { + int nTracksITS567inTimeWin = 0; // (weighted) number of ITS tracks from other collisions in the time window, -1 if undefined (too close to TF borders) + float sumAmpFT0CinTimeWin = 0; // (weighted) sum of FT0C amplitudes from other collisions in the time window, -1 if undefined + float medianTime = 0; // delta time (us) at which the occupancy in the time window crosses 1/2 of its full value + bool noCollInTimeRangeNarrow = false; // no collisions within +/- TimeRangeVetoOnCollNarrow + bool noCollInTimeRangeStrict = false; // no collisions within +/- TimeRangeVetoOnCollStrict + bool noCollInTimeRangeStandard = false; // no high-mult collisions within [-4, 2] us and no collisions within the narrow range + bool noCollInRofStrict = false; // no other collisions in the same ITS ROF + bool noCollInRofStandard = false; // no collisions in the same ITS ROF with FT0C above FT0CamplPerCollCutVetoOnCollInROF + bool noCollInRofWithCloseVz = false; // no collisions in the same ITS ROF with close vZ + bool noHighMultCollInPrevRof = false; // total FT0C amplitude in the previous ITS ROF below FT0CamplPerCollCutVetoOnCollInROF + }; + + // prepare n sources (one per collision, same order as in the collision table) with all fields reset + void resetSources(size_t n) { mSources.assign(n, Source{}); } + // source i (for filling) + Source& source(int32_t i) { return mSources[i]; } + + // set run-dependent constants and configurables + template + void configure(int64_t bcSOR, int64_t nBCsPerTF, int rofOffset, int rofLength, TEvSelOpts const& opts) + { + mBcSOR = bcSOR; + mNBCsPerTF = nBCsPerTF; + mRofOffset = rofOffset; + mRofLength = rofLength; + mTimeWinMinNS = opts.confTimeIntervalForOccupancyCalculationMin.value * 1e3; // ns + mTimeWinMaxNS = opts.confTimeIntervalForOccupancyCalculationMax.value * 1e3; // ns + mTimeRangeVetoOnCollNarrow = opts.confTimeRangeVetoOnCollNarrow.value; + mTimeRangeVetoOnCollStrict = opts.confTimeRangeVetoOnCollStrict.value; + mFT0CamplCutVetoOnCollInTimeRange = opts.confFT0CamplCutVetoOnCollInTimeRange.value; + mFT0CamplCutVetoOnCollInROF = opts.confFT0CamplCutVetoOnCollInROF.value; + mEpsilonVzDiffVetoInROF = opts.confEpsilonVzDiffVetoInROF.value; + mUseWeightsForOccupancy = opts.confUseWeightsForOccupancyVariable.value; + } + + static float calcWeightForOccupancy(float dt) + { + float wOccup = 0; + if (dt >= -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) + wOccup = 1; + else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + return wOccup; + } + + // occupancy wrt mSources[iRef] (the source itself is excluded) + template + Result calculateForSource(int32_t iRef, std::map const& mapGlobalBcWithTVX, TFT0CofBC const& getFT0CofBC) + { + return calculate(mSources[iRef].globalBC, mSources[iRef].vZ, iRef - 1, iRef + 1, mapGlobalBcWithTVX, getFT0CofBC); + } + + // occupancy wrt refGlobalBC (refVz is used for the in-ROF veto on collisions with close vZ) + // sources are scanned backward starting from index iBefore and forward starting from index iAfter + // mapGlobalBcWithTVX: global BC -> BC index for colliding BCs with TVX, used for the FT0C-based estimate near TF borders + // getFT0CofBC(bcIndex, ampFT0C): returns false if there is no FT0 in this BC, otherwise sets ampFT0C + template + Result calculate(int64_t refGlobalBC, float refVz, int32_t iBefore, int32_t iAfter, std::map const& mapGlobalBcWithTVX, TFT0CofBC const& getFT0CofBC) + { + Result res; + const int32_t nSources = mSources.size(); + const int64_t tfId = getTFid(refGlobalBC); + const int64_t rofId = getRofId(refGlobalBC); + const int bcInTF = (refGlobalBC - mBcSOR) % mNBCsPerTF; + // info for occupancy in +/- windows is available (i.e. the reference is not too close to the TF borders), 10 BCs is margin + const bool isFullInfoForOccupancy = ((bcInTF - 10) * bcNS > -mTimeWinMinNS) && ((mNBCsPerTF - 10 - bcInTF) * bcNS > mTimeWinMaxNS); // o2-linter: disable=magic-number (margin in BCs) + // collisions within the time window can be close to the TF end => switch to FT0C-based estimate for them + const bool canHaveAssocCollsWithinLastDriftTime = (mNBCsPerTF - bcInTF < 4000 * 2); // o2-linter: disable=magic-number (BCs within last drift time window) + + // ### in-ROF occupancy + int nITS567tracksForSameRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF + int nCollsInRofWithFT0CAboveVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold + int nITS567tracksForRofVetoOnCloseVz = 0; // to veto events with nearby collisions with close vZ + auto addCollInSameRof = [&](const Source& src) { + nITS567tracksForSameRofVetoStrict += src.nTracksITS567; + if (src.ampFT0C > mFT0CamplCutVetoOnCollInROF) + nCollsInRofWithFT0CAboveVetoStandard++; + if (std::fabs(src.vZ - refVz) < mEpsilonVzDiffVetoInROF) + nITS567tracksForRofVetoOnCloseVz += src.nTracksITS567; + }; + // collisions in the same TF and ROF before and after the reference + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId || getRofId(thisBC) != rofId) + break; + addCollInSameRof(mSources[i]); + } + for (int32_t i = iAfter; i < nSources; i++) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId || getRofId(thisBC) != rofId) + break; + addCollInSameRof(mSources[i]); + } + res.noCollInRofStrict = (nITS567tracksForSameRofVetoStrict == 0); + res.noCollInRofStandard = (nCollsInRofWithFT0CAboveVetoStandard == 0); + res.noCollInRofWithCloseVz = (nITS567tracksForRofVetoOnCloseVz == 0); + + // ### occupancy in the previous ROF + float totalFT0amplInPrevROF = 0; + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + int64_t thisRofId = getRofId(thisBC); + if (thisRofId == rofId - 1) + totalFT0amplInPrevROF += mSources[i].ampFT0C; + else if (thisRofId < rofId - 1) + break; + } + // veto events if FT0C amplitude in previous ITS ROF is above threshold + res.noHighMultCollInPrevRof = (totalFT0amplInPrevROF < mFT0CamplCutVetoOnCollInROF); + + // ### occupancy in the time window + int nITS567tracksInFullTimeWindow = 0; + float sumAmpFT0CInFullTimeWindow = 0; + int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrow range) + int nITS567tracksForVetoStrict = 0; // to veto events with nearby collisions + int nCollsWithFT0CAboveVetoStandard = 0; // to veto events with nearby collisions that have per-collision multiplicity above threshold + int iFirstRejectedByTFborderCut = -1; + int proxyTotalMultInTimeWin = 0; + mDeltaTimeMult.clear(); // (delta time in ns, proxy for N tracks) of collisions in the time window, for the median time + auto addCollInTimeWin = [&](int32_t i, float dtNS) { + const Source& src = mSources[i]; + mDeltaTimeMult.emplace_back(dtNS, src.proxyNtracks); + proxyTotalMultInTimeWin = proxyTotalMultInTimeWin + static_cast(src.proxyNtracks); // int + float, as in the original std::accumulate over floats + + float dt = dtNS / 1e3; // ns -> us + // counting tracks from other collisions in fixed time windows + if (std::fabs(dt) < mTimeRangeVetoOnCollNarrow) + nITS567tracksForVetoNarrow += src.proxyNtracks; + if (std::fabs(dt) < mTimeRangeVetoOnCollStrict) + nITS567tracksForVetoStrict += src.proxyNtracks; + + // veto on high-mult collisions nearby, where artificial structures in the dt-occupancy plots are observed + if (dt > -4.0 && dt < 2.0 && src.ampFT0C > mFT0CamplCutVetoOnCollInTimeRange) { // o2-linter: disable=magic-number (dt in us, time window for high-mult veto) + nCollsWithFT0CAboveVetoStandard++; + } + + // check if we are close to TF borders => N ITS tracks is not reliable, and FT0C ampl will be used for occupancy estimation (see below) + if (src.isRejectedByTFborderCut) { + if (iFirstRejectedByTFborderCut == -1) + iFirstRejectedByTFborderCut = i; + return; + } + + // weighted occupancy calc: + if (isFullInfoForOccupancy) { + float wOccup = 1.; + if (mUseWeightsForOccupancy) { + wOccup = calcWeightForOccupancy(dt); + } + nITS567tracksInFullTimeWindow += wOccup * src.proxyNtracks; + sumAmpFT0CInFullTimeWindow += wOccup * src.ampFT0C; + } + }; + // collisions in the time window before and after the reference + for (int32_t i = iBefore; i >= 0; i--) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + float dt = (thisBC - refGlobalBC) * bcNS; // ns + if (dt < mTimeWinMinNS) + break; + addCollInTimeWin(i, dt); + } + for (int32_t i = iAfter; i < nSources; i++) { + int64_t thisBC = mSources[i].globalBC; + if (getTFid(thisBC) != tfId) + break; + float dt = (thisBC - refGlobalBC) * bcNS; // ns + if (dt > mTimeWinMaxNS) + break; + addCollInTimeWin(i, dt); + } + + // median time for the occupancy in the time window + std::sort(mDeltaTimeMult.begin(), mDeltaTimeMult.end()); // sorts by delta time + float sumMult = 0.0; + for (const auto& [dtNS, mult] : mDeltaTimeMult) { + sumMult += mult; + if (sumMult > proxyTotalMultInTimeWin / 2.0) { + res.medianTime = dtNS / 1e3; // ns -> us + break; + } + } + for (const auto& dtMult : mDeltaTimeMult) { + LOGP(debug, "dt={} mult={}", dtMult.first, dtMult.second); + } + LOGP(debug, " --> median time = {}", res.medianTime); + + // if some collisions in the time window are close to the TF border - take FT0C amplitude instead of nTracks, using TVX BCs + if (isFullInfoForOccupancy && canHaveAssocCollsWithinLastDriftTime && iFirstRejectedByTFborderCut >= 0) { + for (auto it = mapGlobalBcWithTVX.find(mSources[iFirstRejectedByTFborderCut].globalBC); it != mapGlobalBcWithTVX.end(); ++it) { + int64_t thisFoundGlobalBC = it->first; + if (getTFid(thisFoundGlobalBC) != tfId) + break; + float dt = (thisFoundGlobalBC - refGlobalBC) * bcNS; // ns + if (dt > mTimeWinMaxNS) + break; + float multT0C = -1; + if (getFT0CofBC(it->second, multT0C)) { + float wOccup = 1.; + if (mUseWeightsForOccupancy) { + wOccup = calcWeightForOccupancy(dt / 1e3); // ns -> us + } + if (multT0C > 50.) // o2-linter: disable=magic-number (multiplicity threshold for occupancy calc) + { + nITS567tracksInFullTimeWindow += wOccup * multT0C / 10.; + sumAmpFT0CInFullTimeWindow += wOccup * multT0C; + } + } + } + } + + // protection against TF borders + if (!isFullInfoForOccupancy) { // occupancy in undefined (too close to TF borders) + nITS567tracksInFullTimeWindow = -1; + sumAmpFT0CInFullTimeWindow = -1; + } + res.nTracksITS567inTimeWin = nITS567tracksInFullTimeWindow; // occupancy by a sum of number of ITS tracks (without the reference collision) + res.sumAmpFT0CinTimeWin = sumAmpFT0CInFullTimeWindow; // occupancy by a sum of FT0C amplitudes (without the reference collision) + + // occupancy flags based on nearby collisions + res.noCollInTimeRangeNarrow = (nITS567tracksForVetoNarrow == 0); + res.noCollInTimeRangeStrict = (nITS567tracksForVetoStrict == 0); + res.noCollInTimeRangeStandard = (nCollsWithFT0CAboveVetoStandard == 0 && nITS567tracksForVetoNarrow == 0); + return res; + } + + private: + std::vector mSources; // one entry per collision, same order as in the collision table + + int64_t mBcSOR = -1; // global bc of the start of the first orbit + int64_t mNBCsPerTF = -1; // duration of TF in bcs + int mRofOffset = -1; // ITS ROF offset, in bc + int mRofLength = -1; // ITS ROF length, in bc + float mTimeWinMinNS = 0; // time window for the occupancy calculation, ns + float mTimeWinMaxNS = 0; // time window for the occupancy calculation, ns + float mTimeRangeVetoOnCollNarrow = 0; // us + float mTimeRangeVetoOnCollStrict = 0; // us + int mFT0CamplCutVetoOnCollInTimeRange = 0; // max FT0C amplitude for each nearby collision in the time range + float mFT0CamplCutVetoOnCollInROF = 0; // max FT0C amplitude for each nearby collision in the ITS ROF + float mEpsilonVzDiffVetoInROF = 0; // min distance in z to nearby collisions inside the ITS ROF, cm + bool mUseWeightsForOccupancy = true; // use delta-time weights for the occupancy estimators + + std::vector> mDeltaTimeMult; // buffer for the median time calculation (reused to avoid allocations) + + int64_t getTFid(int64_t globalBC) const { return (globalBC - mBcSOR) / mNBCsPerTF; } + int64_t getRofId(int64_t globalBC) const { return (globalBC + nBCsPerOrbit - mRofOffset) / mRofLength; } +}; // end OccupancyCalculator + +// EventSelectionModule class EventSelectionModule { public: @@ -690,24 +964,12 @@ class EventSelectionModule return bestGlobalBC; } - float calcWeightForOccupancy(float dt) - { - float wOccup = 0; - if (dt >= -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) - wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) - else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) - wOccup = 1; - else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) - wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) - else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) - wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) - return wOccup; - } - // declaration of structs here // (N.B.: will be invisible to the outside, create your own copies) o2::common::eventselection::EvselConfigurables evselOpts; + OccupancyCalculator occupancyCalc; // occupancy estimators and flags (keeps its buffers between DFs) + template void init(TContext& context, TEvSelOpts const& external_evselopts, THistoRegistry& histos, TMetadataInfo const& metadataInfo) { @@ -944,9 +1206,7 @@ class EventSelectionModule } return; } - std::vector vTracksITS567perColl(cols.size(), 0); // counter of tracks per collision for occupancy studies - std::vector vAmpFT0CperColl(cols.size(), 0); // amplitude FT0C per collision - std::vector vCollVz(cols.size(), 0); // vector with vZ positions for each collision + occupancyCalc.resetSources(cols.size()); // per-collision inputs for the occupancy calculation std::vector vIsVertexITSTPC(cols.size(), 0); // at least one of vertex contributors is ITS-TPC track std::vector vIsVertexTOFmatched(cols.size(), 0); // at least one of vertex contributors is matched to TOF std::vector vIsVertexTRDmatched(cols.size(), 0); // at least one of vertex contributors is matched to TRD @@ -972,7 +1232,7 @@ class EventSelectionModule // first loop to match collisions to TVX, also extract other per-collision information for further use for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); - vCollVz[colIndex] = col.posZ(); + occupancyCalc.source(colIndex).vZ = col.posZ(); vTrackTimesTOF.clear(); vTrackTimesTRDnoTOF.clear(); @@ -983,7 +1243,7 @@ class EventSelectionModule for (const auto& track : colPvTracks) { float trackTime = track.trackTime(); if (track.itsNCls() >= 5) // o2-linter: disable=magic-number (number of ITS clusters >=5) - vTracksITS567perColl[colIndex]++; + occupancyCalc.source(colIndex).nTracksITS567++; if (track.hasTRD()) vIsVertexTRDmatched[colIndex] = 1; if (track.hasTPC()) @@ -1144,301 +1404,33 @@ class EventSelectionModule } } - // pre-loop for occupancy calculation - std::vector vIsFullInfoForOccupancy(cols.size(), 0); // info for occupancy in +/- windows is available (i.e. a given coll is not too close to the TF borders) - std::vector vIsCollRejectedByTFborderCut(cols.size(), 0); // helper vector with - std::vector vCanHaveAssocCollsWithinLastDriftTime(cols.size(), 0); // to see if for some collisions in the occupancy calc (that are close to TF border) we will switch to FT0C based occupancy estimation - std::vector vProxyForCollNtracks(cols.size(), 0); // n tracks, or - if a collision is close to ITS ROF border - scaled FT0C mult - std::vector vMedianTimeForOccupancy(cols.size(), 0); // median time for the occupancy in a defined time window - - const float timeWinOccupancyCalcMinNS = evselOpts.confTimeIntervalForOccupancyCalculationMin * 1e3; // ns - const float timeWinOccupancyCalcMaxNS = evselOpts.confTimeIntervalForOccupancyCalculationMax * 1e3; // ns - - // loop over collisions to calculate occupancy-related quantities + // fill remaining per-collision inputs for the occupancy calculation for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - int bcInTF = (foundGlobalBC - bcSOR) % nBCsPerTF; - vIsFullInfoForOccupancy[colIndex] = ((bcInTF - 10) * bcNS > -timeWinOccupancyCalcMinNS) && ((nBCsPerTF - 10 - bcInTF) * bcNS > timeWinOccupancyCalcMaxNS) ? true : false; // 10 BCs is margin - - int32_t foundBC = vFoundBCindex[colIndex]; - auto bcselEntry = bcselbuffer[foundBC]; + OccupancyCalculator::Source& occupSrc = occupancyCalc.source(colIndex); + occupSrc.globalBC = vFoundGlobalBC[colIndex]; + const auto& bcselEntry = bcselbuffer[vFoundBCindex[colIndex]]; if (bcselEntry.foundFT0Id > -1) { // required: explicit ft0s table auto foundFT0 = ft0s.rawIteratorAt(bcselEntry.foundFT0Id); - vAmpFT0CperColl[colIndex] = foundFT0.sumAmpC(); + occupSrc.ampFT0C = foundFT0.sumAmpC(); } // check if we are close to ITS ROF borders => N ITS tracks is not reliable, and FT0C ampl can be used for occupancy estimation - // denominator for vAmpFT0CperColl is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts - vProxyForCollNtracks[colIndex] = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? vTracksITS567perColl[colIndex] : vAmpFT0CperColl[colIndex] / 10.; - - if (!BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder)) { - vIsCollRejectedByTFborderCut[colIndex] = true; - } - if (nBCsPerTF - bcInTF < 4000 * 2) { // o2-linter: disable=magic-number (BCs within last drift time window) - vCanHaveAssocCollsWithinLastDriftTime[colIndex] = true; - } - } - - // save indices of collisions for occupancy calculation (both in ROF and in time range) - std::vector> vCollsInSameITSROF; - std::vector> vCollsInPrevITSROF; - std::vector> vCollsInTimeWin; - std::vector> vTimeDeltaForColls; // delta time wrt a given collision - for (const auto& col : cols) { - int32_t colIndex = col.globalIndex(); - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - - int64_t tfId = (foundGlobalBC - bcSOR) / nBCsPerTF; - int64_t rofId = (foundGlobalBC + nBCsPerOrbit - rofOffset) / rofLength; - - // ### for in-ROF occupancy - std::vector vAssocCollInSameROF; - // find all collisions in the same ROF before a given collision - int32_t minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - // int thisRofIdInTF = (thisBC - rofOffset) / rofLength; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - - // check if we are within the same ROF - if (thisRofId != rofId) - break; - vAssocCollInSameROF.push_back(minColIndex); - minColIndex--; - } - // find all collisions in the same ROF after the current one - int32_t maxColIndex = colIndex + 1; - while (maxColIndex < cols.size()) { - int64_t thisBC = vFoundGlobalBC[maxColIndex]; - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - if (thisRofId != rofId) - break; - vAssocCollInSameROF.push_back(maxColIndex); - maxColIndex++; - } - vCollsInSameITSROF.push_back(vAssocCollInSameROF); - - // ### bookkeep collisions in previous ROF - std::vector vAssocCollInPrevROF; - minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - int64_t thisRofId = (thisBC + nBCsPerOrbit - rofOffset) / rofLength; - if (thisRofId == rofId - 1) - vAssocCollInPrevROF.push_back(minColIndex); - else if (thisRofId < rofId - 1) - break; - minColIndex--; - } - vCollsInPrevITSROF.push_back(vAssocCollInPrevROF); - - // ### for occupancy in time windows - std::vector vAssocToThisCol; - std::vector vCollsTimeDeltaWrtGivenColl; - std::vector vProxyNtracksAssocColls; // mult of associated collisions, for the median time calc - // find all collisions in time window before the current one - minColIndex = colIndex - 1; - while (minColIndex >= 0) { - int64_t thisBC = vFoundGlobalBC[minColIndex]; - // check if this is still the same TF - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - float dt = (thisBC - foundGlobalBC) * bcNS; // ns - // check if we are within the chosen time range - if (dt < timeWinOccupancyCalcMinNS) - break; - vAssocToThisCol.push_back(minColIndex); - vCollsTimeDeltaWrtGivenColl.push_back(dt); - vProxyNtracksAssocColls.push_back(vProxyForCollNtracks[minColIndex]); - minColIndex--; - } - // find all collisions in time window after the current one - maxColIndex = colIndex + 1; - while (maxColIndex < cols.size()) { - int64_t thisBC = vFoundGlobalBC[maxColIndex]; - int64_t thisTFid = (thisBC - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - float dt = (thisBC - foundGlobalBC) * bcNS; // ns - if (dt > timeWinOccupancyCalcMaxNS) - break; - vAssocToThisCol.push_back(maxColIndex); - vCollsTimeDeltaWrtGivenColl.push_back(dt); - vProxyNtracksAssocColls.push_back(vProxyForCollNtracks[maxColIndex]); - maxColIndex++; - } - vCollsInTimeWin.push_back(vAssocToThisCol); - vTimeDeltaForColls.push_back(vCollsTimeDeltaWrtGivenColl); - - // calculation of the median time for the occupancy in a given time window - std::vector> pairsDeltaTimeMult; - pairsDeltaTimeMult.reserve(vCollsTimeDeltaWrtGivenColl.size()); - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) - pairsDeltaTimeMult.emplace_back(vCollsTimeDeltaWrtGivenColl[iCol], vProxyNtracksAssocColls[iCol]); - std::sort(pairsDeltaTimeMult.begin(), pairsDeltaTimeMult.end()); // sorts by first element by default - int proxyTotalMultInTimeWin = std::accumulate(vProxyNtracksAssocColls.begin(), vProxyNtracksAssocColls.end(), 0); - - float sumMult = 0.0; - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) { - sumMult += pairsDeltaTimeMult[iCol].second; - if (sumMult > proxyTotalMultInTimeWin / 2.0) { - vMedianTimeForOccupancy[colIndex] = pairsDeltaTimeMult[iCol].first / 1e3; // ns -> us - break; - } - } - for (size_t iCol = 0; iCol < vCollsTimeDeltaWrtGivenColl.size(); iCol++) { - LOGP(debug, "dt={} mult={}", pairsDeltaTimeMult[iCol].first, pairsDeltaTimeMult[iCol].second); - } - LOGP(debug, " --> median time = {}", vMedianTimeForOccupancy[colIndex]); + // denominator for ampFT0C is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts + occupSrc.proxyNtracks = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? occupSrc.nTracksITS567 : occupSrc.ampFT0C / 10.; + occupSrc.isRejectedByTFborderCut = !BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder); } + occupancyCalc.configure(bcSOR, nBCsPerTF, rofOffset, rofLength, evselOpts); - // perform the occupancy calculation per ITS ROF and also in the pre-defined time window - std::vector vNumTracksITS567inFullTimeWin(cols.size(), 0); // counter of tracks in full time window for occupancy studies (excluding given event) - std::vector vSumAmpFT0CinFullTimeWin(cols.size(), 0); // sum of FT0C of tracks in full time window for occupancy studies (excluding given event) - - std::vector vNoCollInTimeRangeStrict(cols.size(), 0); // no collisions in a specified time range - std::vector vNoCollInTimeRangeNarrow(cols.size(), 0); // no collisions in a specified time range (narrow) - std::vector vNoHighMultCollInTimeRange(cols.size(), 0); // no high-mult collisions in a specified time range - - std::vector vNoCollInSameRofStrict(cols.size(), 0); // to veto events with other collisions in the same ITS ROF - std::vector vNoCollInSameRofStandard(cols.size(), 0); // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold - std::vector vNoCollInSameRofWithCloseVz(cols.size(), 0); // to veto events with nearby collisions with close vZ - std::vector vNoHighMultCollInPrevRof(cols.size(), 0); // veto events if FT0C amplitude in previous ITS ROF is above threshold - - for (const auto& col : cols) { - int32_t colIndex = col.globalIndex(); - float vZ = col.posZ(); - - // ### in-ROF occupancy - const std::vector& vAssocCollInSameROF = vCollsInSameITSROF[colIndex]; - int nITS567tracksForSameRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF - int nCollsInRofWithFT0CAboveVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold - int nITS567tracksForRofVetoOnCloseVz = 0; // to veto events with nearby collisions with close vZ - for (uint32_t iCol = 0; iCol < vAssocCollInSameROF.size(); iCol++) { - int thisColIndex = vAssocCollInSameROF[iCol]; - nITS567tracksForSameRofVetoStrict += vTracksITS567perColl[thisColIndex]; - if (vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInROF) - nCollsInRofWithFT0CAboveVetoStandard++; - if (std::fabs(vCollVz[thisColIndex] - vZ) < evselOpts.confEpsilonVzDiffVetoInROF) - nITS567tracksForRofVetoOnCloseVz += vTracksITS567perColl[thisColIndex]; - } - // in-ROF occupancy flags - vNoCollInSameRofStrict[colIndex] = (nITS567tracksForSameRofVetoStrict == 0); - vNoCollInSameRofStandard[colIndex] = (nCollsInRofWithFT0CAboveVetoStandard == 0); - vNoCollInSameRofWithCloseVz[colIndex] = (nITS567tracksForRofVetoOnCloseVz == 0); - - // ### occupancy in previous ROF - const std::vector& vAssocCollInPrevROF = vCollsInPrevITSROF[colIndex]; - float totalFT0amplInPrevROF = 0; - for (uint32_t iCol = 0; iCol < vAssocCollInPrevROF.size(); iCol++) { - int thisColIndex = vAssocCollInPrevROF[iCol]; - totalFT0amplInPrevROF += vAmpFT0CperColl[thisColIndex]; + // FT0C amplitude in a given BC, for the occupancy estimation near TF borders + auto getFT0CofBC = [&bcs](int32_t bcIndex, float& ampFT0C) { + auto bc = bcs.iteratorAt(bcIndex); + if (!bc.has_ft0()) { + return false; } - // veto events if FT0C amplitude in previous ITS ROF is above threshold - vNoHighMultCollInPrevRof[colIndex] = (totalFT0amplInPrevROF < evselOpts.confFT0CamplCutVetoOnCollInROF); - - // ### occupancy in time windows - const std::vector& vAssocToThisCol = vCollsInTimeWin[colIndex]; - const std::vector& vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; - int nITS567tracksInFullTimeWindow = 0; - float sumAmpFT0CInFullTimeWindow = 0; - int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrow range) with per-collision multiplicity above threshold - int nITS567tracksForVetoStrict = 0; // to veto events with nearby collisions - int nCollsWithFT0CAboveVetoStandard = 0; // to veto events with nearby collisions that have per-collision multiplicity above threshold - int colIndexFirstRejectedByTFborderCut = -1; - for (uint32_t iCol = 0; iCol < vAssocToThisCol.size(); iCol++) { - int thisColIndex = vAssocToThisCol[iCol]; - float dt = vCollsTimeDeltaWrtGivenColl[iCol] / 1e3; // ns -> us - // counting tracks from other collisions in fixed time windows - if (std::fabs(dt) < evselOpts.confTimeRangeVetoOnCollNarrow) - nITS567tracksForVetoNarrow += vProxyForCollNtracks[thisColIndex]; - if (std::fabs(dt) < evselOpts.confTimeRangeVetoOnCollStrict) - nITS567tracksForVetoStrict += vProxyForCollNtracks[thisColIndex]; - - // veto on high-mult collisions nearby, where artificial structures in the dt-occupancy plots are observed - if (dt > -4.0 && dt < 2.0 && vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInTimeRange) { // o2-linter: disable=magic-number (dt in us, time window for high-mult veto) - nCollsWithFT0CAboveVetoStandard++; - } - - // check if we are close to TF borders => N ITS tracks is not reliable, and FT0C ampl will be used for occupancy estimation (a loop below) - if (vIsCollRejectedByTFborderCut[thisColIndex]) { - if (colIndexFirstRejectedByTFborderCut == -1) - colIndexFirstRejectedByTFborderCut = thisColIndex; - continue; - } - - // weighted occupancy calc: - if (vIsFullInfoForOccupancy[colIndex]) { - float wOccup = 1.; - if (evselOpts.confUseWeightsForOccupancyVariable) { - // weighted occupancy - wOccup = calcWeightForOccupancy(dt); - } - nITS567tracksInFullTimeWindow += wOccup * vProxyForCollNtracks[thisColIndex]; - sumAmpFT0CInFullTimeWindow += wOccup * vAmpFT0CperColl[thisColIndex]; - } - } - - // if some associated collisions are close to TF border - take FT0C amplitude instead of nTracks, using BC table - if (vIsFullInfoForOccupancy[colIndex] && vCanHaveAssocCollsWithinLastDriftTime[colIndex] && colIndexFirstRejectedByTFborderCut >= 0) { - int64_t foundGlobalBC = vFoundGlobalBC[colIndex]; - int64_t tfId = (foundGlobalBC - bcSOR) / nBCsPerTF; - std::map::iterator it = mapGlobalBcWithTVX.find(vFoundGlobalBC[colIndexFirstRejectedByTFborderCut]); - while (it != mapGlobalBcWithTVX.end()) { - int64_t thisFoundGlobalBC = it->first; - int32_t thisFoundBCindex = it->second; - auto bc = bcs.iteratorAt(thisFoundBCindex); - int64_t thisTFid = (bc.globalBC() - bcSOR) / nBCsPerTF; - if (thisTFid != tfId) - break; - - float dt = (thisFoundGlobalBC - foundGlobalBC) * bcNS; // ns - if (dt > timeWinOccupancyCalcMaxNS) - break; - - float multT0C = -1; - if (bc.has_ft0()) { - multT0C = bc.ft0().sumAmpC(); - float wOccup = 1.; - if (evselOpts.confUseWeightsForOccupancyVariable) { - wOccup = calcWeightForOccupancy(dt / 1e3); // ns -> us - } - if (multT0C > 50.) // o2-linter: disable=magic-number (multiplicity threshold for occupancy calc) - { - nITS567tracksInFullTimeWindow += wOccup * multT0C / 10.; - sumAmpFT0CInFullTimeWindow += wOccup * multT0C; - } - } - it++; - } - } - - // protection against TF borders - if (!vIsFullInfoForOccupancy[colIndex]) { // occupancy in undefined (too close to TF borders) - nITS567tracksInFullTimeWindow = -1; - sumAmpFT0CInFullTimeWindow = -1; - } - - vNumTracksITS567inFullTimeWin[colIndex] = nITS567tracksInFullTimeWindow; // occupancy by a sum of number of ITS tracks (without a current collision) - vSumAmpFT0CinFullTimeWin[colIndex] = sumAmpFT0CInFullTimeWindow; // occupancy by a sum of FT0C amplitudes (without a current collision) - // occupancy flags based on nearby collisions - vNoCollInTimeRangeNarrow[colIndex] = (nITS567tracksForVetoNarrow == 0); - vNoCollInTimeRangeStrict[colIndex] = (nITS567tracksForVetoStrict == 0); - vNoHighMultCollInTimeRange[colIndex] = (nCollsWithFT0CAboveVetoStandard == 0 && nITS567tracksForVetoNarrow == 0); - } // end of the occupancy calculation + ampFT0C = bc.ft0().sumAmpC(); + return true; + }; for (const auto& col : cols) { int32_t colIndex = col.globalIndex(); @@ -1450,6 +1442,9 @@ class EventSelectionModule int32_t foundFDD = bcselEntry.foundFDDId; int32_t foundZDC = bcselEntry.foundZDCId; + // occupancy estimators and flags wrt this collision + const OccupancyCalculator::Result occ = occupancyCalc.calculateForSource(colIndex, mapGlobalBcWithTVX, getFT0CofBC); + // compare zVtx from FT0 and from PV bool isGoodZvtxFT0vsPV = 0; if (bcselEntry.foundFT0Id > -1) { @@ -1488,14 +1483,14 @@ class EventSelectionModule selection |= isGoodZvtxFT0vsPV ? BIT(aod::evsel::kIsGoodZvtxFT0vsPV) : 0; // selection bits based on occupancy time pattern - selection |= vNoCollInTimeRangeNarrow[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeNarrow) : 0; - selection |= vNoCollInTimeRangeStrict[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeStrict) : 0; - selection |= vNoHighMultCollInTimeRange[colIndex] ? BIT(aod::evsel::kNoCollInTimeRangeStandard) : 0; + selection |= occ.noCollInTimeRangeNarrow ? BIT(aod::evsel::kNoCollInTimeRangeNarrow) : 0; + selection |= occ.noCollInTimeRangeStrict ? BIT(aod::evsel::kNoCollInTimeRangeStrict) : 0; + selection |= occ.noCollInTimeRangeStandard ? BIT(aod::evsel::kNoCollInTimeRangeStandard) : 0; // selection bits based on ITS in-ROF occupancy - selection |= vNoCollInSameRofStrict[colIndex] ? BIT(aod::evsel::kNoCollInRofStrict) : 0; - selection |= (vNoCollInSameRofStandard[colIndex] && vNoCollInSameRofWithCloseVz[colIndex]) ? BIT(aod::evsel::kNoCollInRofStandard) : 0; - selection |= vNoHighMultCollInPrevRof[colIndex] ? BIT(aod::evsel::kNoHighMultCollInPrevRof) : 0; + selection |= occ.noCollInRofStrict ? BIT(aod::evsel::kNoCollInRofStrict) : 0; + selection |= (occ.noCollInRofStandard && occ.noCollInRofWithCloseVz) ? BIT(aod::evsel::kNoCollInRofStandard) : 0; + selection |= occ.noHighMultCollInPrevRof ? BIT(aod::evsel::kNoHighMultCollInPrevRof) : 0; // copy rct flags from bcsel table uint32_t rct = bcselEntry.rct; @@ -1522,7 +1517,7 @@ class EventSelectionModule } evsel(alias, selection, rct, sel7, sel8, foundBC, foundFT0, foundFV0, foundFDD, foundZDC, - vNumTracksITS567inFullTimeWin[colIndex], vSumAmpFT0CinFullTimeWin[colIndex], vMedianTimeForOccupancy[colIndex]); + occ.nTracksITS567inTimeWin, occ.sumAmpFT0CinTimeWin, occ.medianTime); } } // end processRun3 }; // end EventSelectionModule From 9f9c33d6f721df79d6d009f61062fe642ead2f16 Mon Sep 17 00:00:00 2001 From: Youngwoong Seo Date: Mon, 9 Nov 2026 18:16:48 +0900 Subject: [PATCH 615/648] [PWGHF] Fix the unit mismatch in QA histogram filling (#18577) --- .../candidateCreatorXicToXiPiPi.cxx | 24 ++++++++++--------- .../candidateSelectorXicToXiPiPi.cxx | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx index b3b72f70883..73667ffe7b8 100644 --- a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx @@ -151,6 +151,8 @@ struct HfCandidateCreatorXicToXiPiPi { HistogramRegistry registry{"registry"}; OutputObj zorroSummary{"zorroSummary"}; + static constexpr float cmToMicrometer{10000.}; + void init(InitContext const&) { if ((doprocessNoCentXicplusWithDcaFitter + doprocessCentFT0CXicplusWithDcaFitter + doprocessCentFT0MXicplusWithDcaFitter + doprocessNoCentXicplusWithKFParticle + doprocessCentFT0CXicplusWithKFParticle + doprocessCentFT0MXicplusWithKFParticle) != 1) { @@ -541,12 +543,12 @@ struct HfCandidateCreatorXicToXiPiPi { registry.fill(HIST("hCovSVXZ"), covMatrixSV[3]); registry.fill(HIST("hCovSVZZ"), covMatrixSV[5]); // DCAs of prongs - registry.fill(HIST("hDcaXYProngs"), trackCasc.getPt(), impactParameterCasc.getY()); - registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor0.pt(), impactParameter0.getY()); - registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor1.pt(), impactParameter1.getY()); - registry.fill(HIST("hDcaZProngs"), trackCasc.getPt(), impactParameterCasc.getZ()); - registry.fill(HIST("hDcaZProngs"), trackCharmBachelor0.pt(), impactParameter0.getZ()); - registry.fill(HIST("hDcaZProngs"), trackCharmBachelor1.pt(), impactParameter1.getZ()); + registry.fill(HIST("hDcaXYProngs"), trackCasc.getPt(), impactParameterCasc.getY() * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor0.pt(), impactParameter0.getY() * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), trackCharmBachelor1.pt(), impactParameter1.getY() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCasc.getPt(), impactParameterCasc.getZ() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCharmBachelor0.pt(), impactParameter0.getZ() * cmToMicrometer); + registry.fill(HIST("hDcaZProngs"), trackCharmBachelor1.pt(), impactParameter1.getZ() * cmToMicrometer); } //---------------------------------fill candidate table rows------------------------------------------------------------------------------------------- @@ -873,9 +875,9 @@ struct HfCandidateCreatorXicToXiPiPi { registry.fill(HIST("hCovSVXZ"), covMatrixXicPlus[3]); registry.fill(HIST("hCovSVZZ"), covMatrixXicPlus[5]); // DCAs of prongs - registry.fill(HIST("hDcaXYProngs"), kfXi.GetPt(), impactParameterXiXY); - registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor0.GetPt(), impactParameterPi0XY); - registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor1.GetPt(), impactParameterPi1XY); + registry.fill(HIST("hDcaXYProngs"), kfXi.GetPt(), impactParameterXiXY * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor0.GetPt(), impactParameterPi0XY * cmToMicrometer); + registry.fill(HIST("hDcaXYProngs"), kfCharmBachelor1.GetPt(), impactParameterPi1XY * cmToMicrometer); } //------------------------------fill candidate table rows-------------------------------------- @@ -1093,7 +1095,7 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { registry.add("hDecayedPions", "hDecayedPions", {HistType::kTH1F, {{5, -0.5, 4.5}}}); registry.add("hInteractionsWithMaterial", "hInteractionsWithMaterial", {HistType::kTH1F, {{6, -0.5, 5.5}}}); registry.add("hDebugRec", "hDebugRec", {HistType::kTH1F, {{4, -0.5, 3.5}}}); - registry.add("hy_pt_gen", "Generated MC", {HistType::kTH2F, {{20, -1.0, 1.0}, {200, 0, 20}}}); + registry.add("hYPtGen", "Generated MC", {HistType::kTH2F, {{20, -1.0, 1.0}, {200, 0, 20}}}); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + TotalRec, "total"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XicToFinalState, "#Xi^{+}_{c} #rightarrow #pi^{#plus} #pi^{#plus} #pi^{#minus} p #pi^{#minus}"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XiToPiPPi, "#Xi^{#minus} #rightarrow #pi^{#minus} p #pi^{#minus}"); @@ -1348,7 +1350,7 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Check whether the charm baryon is non-prompt (from a b quark). if (flag != 0) { // fill inclusive generated Xic+ - registry.fill(HIST("hy_pt_gen"), particle.y(), particle.pt()); + registry.fill(HIST("hYPtGen"), particle.y(), particle.pt()); origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, false, &idxBhadMothers); // Calculate the decay length of the generated particle diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 588dd15a1bb..2732191f859 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -251,7 +251,7 @@ struct HfCandidateSelectorXicToXiPiPi { registry.add("hNCrossedRowsTpcV0PosDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 pos. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0NegDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 neg. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); /// reconstructed MC - registry.add("hmass_pt_reco_truth", "Reconstructed truth matching MC;inv.mass(#Xi#pi#pi) [GeV/c^{2}];p_{T} [GeV/c]", {HistType::kTH2F, {{40, 2.3, 2.7}, {200, 0, 20}}}); + registry.add("hMassPtRecSig", "Reconstructed truth matching MC;inv.mass(#Xi#pi#pi) [GeV/c^{2}];p_{T} [GeV/c]", {HistType::kTH2F, {{40, 2.3, 2.7}, {200, 0, 20}}}); } } @@ -655,7 +655,7 @@ struct HfCandidateSelectorXicToXiPiPi { if constexpr (IsMc) { if (isMatchedSignal) { registry.fill(HIST("hSelCandidatesRecSig"), PidSelected); - registry.fill(HIST("hmass_pt_reco_truth"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); + registry.fill(HIST("hMassPtRecSig"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); } else { registry.fill(HIST("hSelCandidatesRecBkg"), PidSelected); } From 40a7f63741f2bd9b75f0e0e845c64ee900333bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:25:24 +0100 Subject: [PATCH 616/648] [PWGJE] Fix unused variables, parameters, values (#18644) --- PWGJE/Tasks/fullJetSpectra.cxx | 6 +++--- PWGJE/Tasks/statPromptPhoton.cxx | 4 ++-- PWGJE/Tasks/v0QA.cxx | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/PWGJE/Tasks/fullJetSpectra.cxx b/PWGJE/Tasks/fullJetSpectra.cxx index c0d12599f50..5774d8d8fe0 100644 --- a/PWGJE/Tasks/fullJetSpectra.cxx +++ b/PWGJE/Tasks/fullJetSpectra.cxx @@ -1381,16 +1381,16 @@ struct FullJetSpectra { if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 13.5); // EMCAcceptedDetTrigCollWithLowChargedJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 14.5); // EMCAcceptedDetTrigCollWithHighChargedJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetFullLowPt) && jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetFullHighPt)) { registry.fill(HIST("hDetTrigcollisionCounter"), 15.5); // EMCAcceptedDetTrigCollWithLow+HighFullJetTriggers - eventAccepted = true; + // eventAccepted = true; // FIXME: unused } for (auto const& jet : jets) { diff --git a/PWGJE/Tasks/statPromptPhoton.cxx b/PWGJE/Tasks/statPromptPhoton.cxx index a09df380079..01aea57b2ac 100644 --- a/PWGJE/Tasks/statPromptPhoton.cxx +++ b/PWGJE/Tasks/statPromptPhoton.cxx @@ -1552,7 +1552,7 @@ struct statPromptPhoton { while (chase) { if (visited.count(chaseindex)) { - chase = false; + // chase = false; // FIXME: unused break; } visited.insert(chaseindex); @@ -1677,7 +1677,7 @@ struct statPromptPhoton { while (chase) { if (visited.count(chaseindex)) { - chase = false; + // chase = false; // FIXME: unused break; } visited.insert(chaseindex); diff --git a/PWGJE/Tasks/v0QA.cxx b/PWGJE/Tasks/v0QA.cxx index e32d56bc590..b448ce6b7c2 100644 --- a/PWGJE/Tasks/v0QA.cxx +++ b/PWGJE/Tasks/v0QA.cxx @@ -1601,7 +1601,7 @@ struct V0QA { if (!v0.has_mcParticle()) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) @@ -1612,7 +1612,7 @@ struct V0QA { auto pv0 = v0.mcParticle(); auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/XiMinusPtYLambdaPt"), mother.pt(), mother.y(), pv0.pt(), weight); @@ -1638,7 +1638,7 @@ struct V0QA { if (!v0.has_mcParticle()) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) @@ -1649,7 +1649,7 @@ struct V0QA { auto pv0 = v0.mcParticle(); auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/JetPtXiMinusPtLambdaPt"), mcdjet.pt(), mother.pt(), pv0.pt(), weight); @@ -1683,14 +1683,14 @@ struct V0QA { if (!v0sAreMatched(v0, pv0, jTracks)) continue; - int pdg = v0.mcParticle().pdgCode(); + // int pdg = v0.mcParticle().pdgCode(); // FIXME: unused // Check V0 decay kinematics if (v0.isRejectedCandidate()) continue; auto mother = v0.mcMotherParticle(); - pdg = mother.pdgCode(); + auto pdg = mother.pdgCode(); if (pdg == PDG_t::kXiMinus) { registry.fill(HIST("feeddown/JetsPtXiMinusPtLambdaPt"), mcpjet.pt(), mcdjet.pt(), mother.pt(), pv0.pt(), weight); } From 5cb1069470cfb1a1b20a41764aac70c4986787eb Mon Sep 17 00:00:00 2001 From: amikals <111276362+amikals@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:26:48 +0100 Subject: [PATCH 617/648] [PWGJE] Add J/psi fragmentation function workflow (#18624) Co-authored-by: Andreas Mikalsen --- PWGJE/Tasks/CMakeLists.txt | 5 + PWGJE/Tasks/DqFragmentationFunction.cxx | 828 ++++++++++++++++++++++++ 2 files changed, 833 insertions(+) create mode 100644 PWGJE/Tasks/DqFragmentationFunction.cxx diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index a580f482409..d3726eb2ed2 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -418,6 +418,11 @@ if(FastJet_FOUND) REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(dq-fragmentation-function + SOURCES DqFragmentationFunction.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-planarflow SOURCES jetPlanarFlow.cxx REUSE_FROM JetSubstructureHFPCH diff --git a/PWGJE/Tasks/DqFragmentationFunction.cxx b/PWGJE/Tasks/DqFragmentationFunction.cxx new file mode 100644 index 00000000000..18f03f8116f --- /dev/null +++ b/PWGJE/Tasks/DqFragmentationFunction.cxx @@ -0,0 +1,828 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Fragmentation function task for Jpsi mesons in Jets. +// +/// \author Andreas Ulvseth Mikalsen +// +#include "PWGJE/Core/JetCandidateUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct DqFragmentationFunctionTask { + // Jet Pt bins for multidimension histograms, not affected by masterswitch unlike other configurables. + // The first variable is not a bin edge, but a flag for variable width bins, the rest are bin edges. LEAVE AT 0 IN CONFIG! + ConfigurableAxis jetPtBins{"jetPtBins", {VARIABLE_WIDTH, 0., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 25., 30., 35., 40.}, "Jet pT bin edges (GeV/c), used for multidimensional histograms"}; + + // Master switch + Configurable useCuts{"useCuts", true, "Enable or disable: eventselection, jetselection and dielectron selection cuts"}; + Configurable useMCPCuts{"useMCPCuts", false, "Enable or disable (truth only): eventselection, jetselection and dielectron selection cuts"}; + + // Event selection + Configurable eventSelections{"eventSelections", "NoTimeFrameBorder+NoITSROFrameBorder", "List of available event cuts JetDerivedDataUtilities.h line 113"}; + Configurable skipMBgapEvents{"skipMBgapEvents", true, "Skip MB gap events"}; + Configurable applyRCTflag{"applyRCTflag", true, "Apply RCT flag"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT label to use for event selection"}; + Configurable vertexZCut{"vertexZCut", 10.f, "Maximum absolute primary vertex z (cm)"}; + + // Electron kinematics + Configurable electronPMin{"electronPMin", 1.f, "Minimum electron momentum (GeV/c)"}; + Configurable electronPairPtMin{"electronPairPtMin", 0.f, "Minimum electron pair transverse momentum (GeV/c)"}; + Configurable electronEtaMax{"electronEtaMax", 0.8f, "Maximum absolute electron pseudorapidity"}; + Configurable electronPairEtaMax{"electronPairEtaMax", 0.8f, "Maximum absolute electron pair pseudorapidity"}; + + // Electron TPC PID + Configurable nSigmaElLowPMin{"nSigmaElLowPMin", -2.5f, "Minimum electron n-sigma for p below threshold"}; + Configurable nSigmaElHighPMin{"nSigmaElHighPMin", -1.5f, "Minimum electron n-sigma for p above threshold"}; + Configurable nSigmaElMax{"nSigmaElMax", 3.f, "Maximum electron n-sigma"}; + Configurable electronPIDMomentumThreshold{"electronPIDMomentumThreshold", 5.f, "Momentum threshold for electron PID (GeV/c)"}; + + // Pion and proton rejection (low momentum only) + Configurable pionNSigmaAbsMin{"pionNSigmaAbsMin", 2.5f, "Minimum absolute pion TPC n-sigma"}; + Configurable protonNSigmaAbsMin{"protonNSigmaAbsMin", 3.f, "Minimum absolute proton TPC n-sigma"}; + + // Track DCA + Configurable dcaXYMax{"dcaXYMax", 0.5f, "Maximum absolute DCAxy (cm)"}; + Configurable dcaZMax{"dcaZMax", 0.5f, "Maximum absolute DCAz (cm)"}; + + // Track quality + Configurable tpcChi2Max{"tpcChi2Max", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable tpcClustersMin{"tpcClustersMin", 70, "Minimum number of TPC clusters"}; + Configurable tpcClustersMax{"tpcClustersMax", 161, "Maximum number of TPC clusters"}; + + // ITS inner-barrel selection + Configurable nITSHitsFirst3{"nITSHitsFirst3", 1, "Minimum number of ITS inner-barrel hits (layers 0-2)"}; + Configurable nITSHitsAll7{"nITSHitsAll7", 1, "Minimum number of ITS inner-barrel hits (layers 0-6)"}; + Configurable requireITSFirstLayer{"requireITSFirstLayer", false, "Require a hit in the first ITS layer (bit 0=1)"}; + + // Jet selection + Configurable jetPtMin{"jetPtMin", 0.f, "Minimum jet transverse momentum (GeV/c)"}; + Configurable jetPtMax{"jetPtMax", 1.e6f, "Maximum jet transverse momentum (GeV/c)"}; + Configurable jetEtaMax{"jetEtaMax", 1.e6f, "Maximum absolute jet pseudorapidity"}; + + HistogramRegistry registry{"registry", + {{"Data/Jet/h_dielectron_jet_pt", ";p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/Jet/h_constituent_multiplicity_vs_jet_pt", "Jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"Data/Jet/h_sum_constituent_pt_vs_jet_pt", "Scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"Data/Jet/h_dielectron_jet_eta", ";#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"Data/Jet/h_dielectron_jet_phi", ";#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/Jet/h_dielectron_jet_Fragmentation_z", ";z^{e^{+}e^{-},jet};dN/dz^{e^{+}e^{-},jet}", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"Data/Jet/h_dielectron_jet_distance", ";#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"Data/Jet/h_dielectron_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"Data/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"Data/Dielectron/h_dielectron_pt", ";p_{T,e^{+}e^{-}};dN/dp_{T,e^{+}e^{-}}", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/Dielectron/h_dielectron_pt_vs_jet_pt", "Dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"Data/Dielectron/h_dielectron_eta", ";#eta_{e^{+}e^{-}};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"Data/Dielectron/h_dielectron_phi", ";#phi_{e^{+}e^{-}};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/Dielectron/h_dielectron_mass", ";m_{e^{+}e^{-}} (GeV/c^{2});dN/dm_{e^{+}e^{-}}", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"Data/Dielectron/h_dielectron_dcaxy", "Electron daughter DCA_{xy};DCA_{xy} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"Data/Dielectron/h_dielectron_dcaz", "Electron daughter DCA_{z};DCA_{z} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"Data/Dielectron/h_dielectron_Lxy", ";L_{xy};dN/dL_{xy}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Lz", ";L_{z};dN/dL_{z}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Tau_xy", ";#tau_{xy};dN/d#tau_{xy}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/Dielectron/h_dielectron_Tau_z", ";#tau_{z};dN/d#tau_{z}", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"Data/SingleTrack/h_electron_pt", "Electron daughter p_{T};p_{T,e} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 20.}}}}, + {"Data/SingleTrack/h_electron_eta", "Electron daughter #eta;#eta_{e};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"Data/SingleTrack/h_electron_phi", "Electron daughter #phi;#phi_{e};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"Data/SingleTrack/h_electron_tpc_nsigma", "Electron daughter TPC PID;TPC n#sigma_{e};Counts", {HistType::kTH1F, {{160, -8., 8.}}}}, + {"Data/SingleTrack/h_electron_tpc_nsigma_vs_pt", "Electron daughter TPC PID;p_{T,e} (GeV/c);TPC n#sigma_{e}", {HistType::kTH2F, {{200, 0., 20.}, {160, -8., 8.}}}}, + {"Data/SingleTrack/h_electron_tpc_ncls_found", "Electron daughter TPC clusters found;N_{TPC clusters};Tracks", {HistType::kTH1F, {{162, -0.5, 161.5}}}}, + {"Data/SingleTrack/h_electron_tpc_chi2_per_cluster", "Electron daughter TPC #chi^{2}/cluster;TPC #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"Data/SingleTrack/h_electron_its_chi2_per_cluster", "Electron daughter ITS #chi^{2}/cluster;ITS #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"Data/SingleTrack/h_jet_track_pt", "Jet constituent p_{T};p_{T,track} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"Data/SingleTrack/h_jet_track_eta", "Jet constituent #eta;#eta_{track};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"Data/SingleTrack/h_n_tracks_per_jet", "Charged constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + + {"Matching/h_collision_counter", "Collision counter;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_jet_counter", "Jet counter;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_candidate_counter", "Candidate matching stages;Stage;Counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"Matching/h_2D_prompt_z", "Prompt z comparison;z^{MCD};z^{MCP}", {HistType::kTH2F, {{20, 0., 1.}, {20, 0., 1.}}}}, + {"Matching/h_2D_prompt_jetpt", "Prompt jetpt;p_{T,jet}^{MCD};p_{T,jet}^{MCP};", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_prompt_jpsipt", "Prompt J/#psi pT comparison;p_{T,J/#psi}^{MCD} (GeV/c);p_{T,J/#psi}^{MCP} (GeV/c)", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_prompt_tauxy", "Prompt dielectron #tau_{xy};#tau_{xy}^{MCD};#tau_{xy}^{MCP}", {HistType::kTH2F, {{200, -2., 2.}, {200, -2., 2.}}}}, + {"Matching/h_2D_nonprompt_z", "Nonprompt z comparison;z^{MCD};z^{MCP}", {HistType::kTH2F, {{20, 0., 1.}, {20, 0., 1.}}}}, + {"Matching/h_2D_nonprompt_jetpt", "Nonprompt jetpt;p_{T,jet}^{MCD};p_{T,jet}^{MCP}", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_nonprompt_jpsipt", "Nonprompt J/#psi pt;p_{T,J/#psi}^{MCD};p_{T,J/#psi}^{MCP}", {HistType::kTH2F, {{40, 0., 40.}, {40, 0., 40.}}}}, + {"Matching/h_2D_nonprompt_tauxy", "Nonprompt dielectron #tau_{xy};#tau_{xy}^{MCD};#tau_{xy}^{MCP}", {HistType::kTH2F, {{200, -2., 2.}, {200, -2., 2.}}}}, + + {"MCD/Jet/h_MCD_jet_pt", "MCD jet p_{T};p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/Jet/h_constituent_multiplicity_vs_jet_pt", "MCD jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"MCD/Jet/h_sum_constituent_pt_vs_jet_pt", "MCD scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"MCD/Jet/h_MCD_jet_eta", "MCD jet #eta;#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"MCD/Jet/h_MCD_jet_phi", "MCD jet #phi;#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCD/Jet/h_MCD_z", "MCD z;z;Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCD/Jet/h_MCD_jet_distance", "MCD J/#psi-jet distance;#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCD/Jet/h_MCD_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"MCD/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_pt", "MCD J/#psi p_{T};p_{T,J/#psi} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/Dielectron/h_dielectron_pt_vs_jet_pt", "MCD dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_eta", "MCD dielectron #eta;#eta_{ee};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCD/Dielectron/h_MCD_dielectron_mass", "MCD J/#psi mass;m_{ee} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_dcaxy", "MCD electron daughter DCA_{xy};DCA_{xy} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_dcaz", "MCD electron daughter DCA_{z};DCA_{z} (cm);Tracks", {HistType::kTH1F, {{200, -1.5, 1.5}}}}, + {"MCD/Dielectron/h_MCD_dielectron_origin", "MCD J/#psi origin;origin;Counts", {HistType::kTH1F, {{3, 0., 3.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_Lxy", "MCD J/#psi L_{xy};L_{xy} (cm);Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_Lz", "MCD J/#psi L_{z};L_{z};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_tau_xy", "MCD J/#psi #tau_{xy};#tau_{xy};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/Dielectron/h_MCD_dielectron_tau_z", "MCD J/#psi #tau_{z};#tau_{z};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCD/SingleTrack/h_MCD_electron_pt", "MCD electron daughter p_{T};p_{T,e} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 20.}}}}, + {"MCD/SingleTrack/h_MCD_electron_eta", "MCD electron daughter #eta;#eta_{e};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCD/SingleTrack/h_MCD_electron_phi", "MCD electron daughter #phi;#phi_{e};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_nsigma", "MCD electron daughter TPC PID;TPC n#sigma_{e};Counts", {HistType::kTH1F, {{160, -8., 8.}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt", "MCD electron daughter TPC PID;p_{T,e} (GeV/c);TPC n#sigma_{e}", {HistType::kTH2F, {{200, 0., 20.}, {160, -8., 8.}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_ncls_found", "MCD electron daughter TPC clusters found;N_{TPC clusters};Tracks", {HistType::kTH1F, {{162, -0.5, 161.5}}}}, + {"MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster", "MCD electron daughter TPC #chi^{2}/cluster;TPC #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster", "MCD electron daughter ITS #chi^{2}/cluster;ITS #chi^{2}/cluster;Tracks", {HistType::kTH1F, {{200, 0., 10.}}}}, + {"MCD/SingleTrack/h_MCD_jet_track_pt", "MCD jet constituent p_{T};p_{T,track} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCD/SingleTrack/h_MCD_jet_track_eta", "MCD jet constituent #eta;#eta_{track};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MCD/SingleTrack/h_MCD_n_tracks_per_jet", "MCD charged constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + + {"MCP/Jet/h_MCP_jet_pt", "MCP jet p_{T};p_{T,jet} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/Jet/h_constituent_multiplicity_vs_jet_pt", "MCP jet constituent multiplicity vs p_{T};p_{T,jet} (GeV/c);N_{constituents}", {HistType::kTH2F, {{200, 0., 80.}, {101, -0.5, 100.5}}}}, + {"MCP/Jet/h_sum_constituent_pt_vs_jet_pt", "MCP scalar constituent p_{T} sum vs jet p_{T};p_{T,jet} (GeV/c);#Sigma p_{T,constituent} (GeV/c)", {HistType::kTH2F, {{100, 0., 40.}, {100, 0., 40.}}}}, + {"MCP/Jet/h_MCP_jet_eta", "MCP jet #eta;#eta_{jet};Counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"MCP/Jet/h_MCP_jet_phi", "MCP jet #phi;#phi_{jet};Counts", {HistType::kTH1F, {{128, 0., o2::constants::math::TwoPI}}}}, + {"MCP/Jet/h_MCP_z", "MCP z;z;Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCP/Jet/h_MCP_jet_distance", "MCP J/#psi-jet distance;#DeltaR_{e^{+}e^{-},jet};Counts", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"MCP/Jet/h_MCP_jet_distance_vs_projection", ";#DeltaR_{e^{+}e^{-},jet};z^{e^{+}e^{-},jet}", {HistType::kTH2F, {{100, 0., 1.}, {100, 0., 1.}}}}, + {"MCP/Jet/h_electron_pairs_per_jet", "Number of accepted electron pairs per jet;Electron pairs per jet;Jets", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + {"MCP/Dielectron/h_MCP_jpsi_pt", "MCP J/#psi p_{T};p_{T,J/#psi} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/Dielectron/h_dielectron_pt_vs_jet_pt", "MCP dielectron p_{T} vs jet p_{T};p_{T,jet} (GeV/c);p_{T,e^{+}e^{-}} (GeV/c)", {HistType::kTH2F, {{200, 0., 80.}, {200, 0., 80.}}}}, + {"MCP/Dielectron/h_MCP_dielectron_eta", "MCP dielectron #eta;#eta_{ee};Counts", {HistType::kTH1F, {{160, -0.8, 0.8}}}}, + {"MCP/Dielectron/h_MCP_dielectron_mass", "MCP dielectron mass;m_{ee} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0., 10.}}}}, + {"MCP/Dielectron/h_MCP_dielectron_origin", "MCP J/#psi origin;origin;Counts", {HistType::kTH1F, {{3, 0., 3.}}}}, + {"MCP/Dielectron/h_MCP_jpsi_lxy", "MCP L_{xy};L_{xy} (cm);Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCP/Dielectron/h_MCP_jpsi_tauxy", "MCP #tau_{xy};#tau_{xy};Counts", {HistType::kTH1F, {{1000, -2., 2.}}}}, + {"MCP/SingleTrack/h_MCP_jet_particle_pt_ElectronPairInfoMissing", "MCP jet constituent p_{T};p_{T,particle} (GeV/c);Counts", {HistType::kTH1F, {{200, 0., 80.}}}}, + {"MCP/SingleTrack/h_MCP_jet_particle_eta_ElectronPairInfoMissing", "MCP jet constituent #eta;#eta_{particle};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MCP/SingleTrack/h_MCP_n_particles_per_jet", "MCP constituents per jet;N_{constituents};Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}}}; + + std::vector eventSelectionBits; + + void init(InitContext const&) + { + // Add histograms using configurable jet pT binning, placed in init so jetPtBins are applied correctly. + registry.add("Data/Multidimensional/h_4d_histogram", "Dielectron fragmentation;p_{T,jet} (GeV/c);z;m_{ee} (GeV/c^{2});#tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("Matching/h_response_prompt", "Prompt J/#psi response;p_{T,jet}^{MCD};z^{MCD};p_{T,jet}^{MCP};z^{MCP}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, AxisSpec{jetPtBins}, {20, 0., 1.}}); + registry.add("Matching/h_response_nonprompt", "Nonprompt J/#psi response;p_{T,jet}^{MCD};z^{MCD};p_{T,jet}^{MCP};z^{MCP}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, AxisSpec{jetPtBins}, {20, 0., 1.}}); + registry.add("MCD/Multidimensional/h_MCD_5d_allParticles", "MCD J/#psi fragmentation;p_{T,jet};z;m_{ee};Tau_{xy};origin", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}, {3, 0., 3.}}); + registry.add("MCD/Multidimensional/h_MCD_4d_PromptJpsi", "MCD J/#psi fragmentation promptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCD/Multidimensional/h_MCD_4d_NonPromptJpsi", "MCD J/#psi fragmentation NonPromptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_allParticles", "MCP J/#psi fragmentation;p_{T,jet};z;Tau_{xy};origin", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {250, -1., 1.}, {3, 0., 3.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_PromptJpsi", "MCP J/#psi fragmentation promptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + registry.add("MCP/Multidimensional/h_MCP_4d_NonPromptJpsi", "MCP J/#psi fragmentation NonPromptJpsi;p_{T,jet};z;m_{ee};Tau_{xy}", HistType::kTHnF, {AxisSpec{jetPtBins}, {20, 0., 1.}, {150, 2.5, 3.7}, {250, -1., 1.}}); + + // Initialize event selection + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits( + static_cast(eventSelections)); + + // Set matching counter labels + auto collisionCounter = registry.get(HIST("Matching/h_collision_counter")); + collisionCounter->GetXaxis()->SetBinLabel(1, "MCP collisions"); + collisionCounter->GetXaxis()->SetBinLabel(2, "MCP collisions after collision cuts (if enabled)"); + collisionCounter->GetXaxis()->SetBinLabel(3, "matched MCDcollisions to MCP collisions"); + collisionCounter->GetXaxis()->SetBinLabel(4, "matched MCD collisions after cut (if enabled)"); + + auto jetCounter = registry.get(HIST("Matching/h_jet_counter")); + jetCounter->GetXaxis()->SetBinLabel(1, "MCD jets"); + jetCounter->GetXaxis()->SetBinLabel(2, "MCD jets after jet cuts"); + jetCounter->GetXaxis()->SetBinLabel(3, "MCD jets matched to MCP"); + jetCounter->GetXaxis()->SetBinLabel(4, "MCD jets matched to MCP after MCP cut (if enabled)"); + + auto candidateCounter = registry.get(HIST("Matching/h_candidate_counter")); + candidateCounter->GetXaxis()->SetBinLabel(1, "MCD candidates in matched jets"); + candidateCounter->GetXaxis()->SetBinLabel(2, "Candidates passing z and optional e cuts"); + candidateCounter->GetXaxis()->SetBinLabel(3, "MCD candidates matched to MCP"); + candidateCounter->GetXaxis()->SetBinLabel(4, "MCD candidates matched to MCP after MCP cut (if enabled)"); + } + + using DielectronMCDJets = soa::Join< + aod::DielectronChargedMCDetectorLevelJets, + aod::DielectronChargedMCDetectorLevelJetConstituents, + aod::DielectronChargedMCDetectorLevelJetsMatchedToDielectronChargedMCParticleLevelJets>; + + using DielectronMCPJets = soa::Join< + aod::DielectronChargedMCParticleLevelJets, + aod::DielectronChargedMCParticleLevelJetConstituents, + aod::DielectronChargedMCParticleLevelJetsMatchedToDielectronChargedMCDetectorLevelJets>; + + // Preslice detector-level collisions belonging to each MC collision + PresliceUnsorted collisionsPerMCCollisionPreslice = aod::jmccollisionlb::mcCollisionId; + // Preslice detector-level jets belonging to each detector-level collision + Preslice mcdJetsPerCollisionPreslice = aod::jet::collisionId; + + template + double calculateZ( + JetType const& jet, + JpsiType const& jpsi) + { + double z = (jet.pt() > 0.) ? (jpsi.pt()) / jet.pt() : -1.; + // avoiding z=1.0 overflowing into next bin. + if (z > 0.999) { + z = 0.9991; + } + return z; + } + + template + bool passesElectronCuts(const Candidate& candidate, int electronIndex) + { + // electronIndex = 1: first electron + // electronIndex = 2: second electron + const double pt = electronIndex == 1 + ? candidate.pt1() + : candidate.pt2(); + + const double eta = electronIndex == 1 + ? candidate.eta1() + : candidate.eta2(); + + // Momentum magnitude: p = pT * cosh(eta) (because dqtrigger 2025 uses p not pt) + const double p = pt * std::cosh(eta); + + const double nSigmaEl = electronIndex == 1 + ? candidate.tpcNSigmaEl1() + : candidate.tpcNSigmaEl2(); + + const double tpcChi2NCl = electronIndex == 1 + ? candidate.tpcChi2NCl1() + : candidate.tpcChi2NCl2(); + + const int tpcNClsFound = electronIndex == 1 + ? candidate.tpcNClsFound1() + : candidate.tpcNClsFound2(); + + const double dcaXY = electronIndex == 1 + ? candidate.dcaXY1() + : candidate.dcaXY2(); + + const double dcaZ = electronIndex == 1 + ? candidate.dcaZ1() + : candidate.dcaZ2(); + + const double nSigmaPi = electronIndex == 1 + ? candidate.tpcNSigmaPi1() + : candidate.tpcNSigmaPi2(); + + const double nSigmaPr = electronIndex == 1 + ? candidate.tpcNSigmaPr1() + : candidate.tpcNSigmaPr2(); + + const unsigned char itsClusterMap = electronIndex == 1 + ? candidate.itsClusterMap1() + : candidate.itsClusterMap2(); + + // Electron kinematic selection + if (p < electronPMin.value || + std::abs(eta) > electronEtaMax.value) { + return false; + } + + // Momentum-dependent electron TPC PID + const double nSigmaElMin = + p < electronPIDMomentumThreshold.value + ? nSigmaElLowPMin.value + : nSigmaElHighPMin.value; + + if (nSigmaEl < nSigmaElMin || + nSigmaEl > nSigmaElMax.value) { + return false; + } + + // Pion and proton rejection: low momentum only + if (p < electronPIDMomentumThreshold.value) { + + if (std::abs(nSigmaPi) < pionNSigmaAbsMin.value) { + return false; + } + + if (std::abs(nSigmaPr) < protonNSigmaAbsMin.value) { + return false; + } + } + + // TPC track quality + if (tpcChi2NCl > tpcChi2Max.value) { + return false; + } + + if (tpcNClsFound < tpcClustersMin.value || + tpcNClsFound > tpcClustersMax.value) { + return false; + } + + // Distance of closest approach + if (std::abs(dcaXY) > dcaXYMax.value) { + return false; + } + + if (std::abs(dcaZ) > dcaZMax.value) { + return false; + } + + // Hits in ITS + // Count hits in the first three ITS layers (bits 0, 1, 2). + const int nITSIBHits = + ((itsClusterMap & (1u << 0)) != 0) + + ((itsClusterMap & (1u << 1)) != 0) + + ((itsClusterMap & (1u << 2)) != 0); + + if (nITSIBHits < nITSHitsFirst3.value) { + return false; + } + // Count hits in the all 7 ITS layers (bits 0, 1, 2, 3, 4, 5, 6). + const int nITSIBHitsAllLayers = + ((itsClusterMap & (1u << 0)) != 0) + + ((itsClusterMap & (1u << 1)) != 0) + + ((itsClusterMap & (1u << 2)) != 0) + + ((itsClusterMap & (1u << 3)) != 0) + + ((itsClusterMap & (1u << 4)) != 0) + + ((itsClusterMap & (1u << 5)) != 0) + + ((itsClusterMap & (1u << 6)) != 0); + + if (nITSIBHitsAllLayers < nITSHitsAll7.value) { + return false; + } + + // Require a hit in the first ITS layer (bit 0). + if (requireITSFirstLayer.value && + (itsClusterMap & (1u << 0)) == 0) { + return false; + } + + // All cuts passed + return true; + } + template + bool passesPairCuts(const Candidate& candidate) + { + if (candidate.pt() < electronPairPtMin.value || + std::abs(candidate.eta()) > electronPairEtaMax.value) { + return false; + } + + return true; + } + + void FragmentationFunctionData(aod::JetCollision const& collision, + soa::Join const& jets, + aod::CandidatesDielectronData const&, + aod::JetTracks const&) + { + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + return; + } + for (const auto& jet : jets) { + + if (useCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + + const auto jetTracks = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + double sumConstituentPt = 0.; + int electronPairs = 0; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useCuts && + (!passesPairCuts(dielectronCandidate) || + !passesElectronCuts(dielectronCandidate, 1) || + !passesElectronCuts(dielectronCandidate, 2))) { + continue; + } + + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + ++electronPairs; + sumConstituentPt += dielectronCandidate.pt1(); + sumConstituentPt += dielectronCandidate.pt2(); + + // calculating angular distance in eta-phi plane + const double axisDistance = + jetutilities::deltaR(jet, dielectronCandidate); + + // filling candidate histograms + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_Fragmentation_z"), z); + registry.fill(HIST("Data/Jet/h_dielectron_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("Data/Jet/h_dielectron_jet_distance"), axisDistance); + + registry.fill(HIST("Data/Dielectron/h_dielectron_mass"), dielectronCandidate.mass()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaxy"), dielectronCandidate.dcaXY1()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaxy"), dielectronCandidate.dcaXY2()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaz"), dielectronCandidate.dcaZ1()); + registry.fill(HIST("Data/Dielectron/h_dielectron_dcaz"), dielectronCandidate.dcaZ2()); + registry.fill(HIST("Data/Dielectron/h_dielectron_pt"), dielectronCandidate.pt()); + registry.fill(HIST("Data/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("Data/Dielectron/h_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("Data/Dielectron/h_dielectron_phi"), dielectronCandidate.phi()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Lz"), dielectronCandidate.lz()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Lxy"), dielectronCandidate.lxy()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Tau_z"), dielectronCandidate.tauz()); + registry.fill(HIST("Data/Dielectron/h_dielectron_Tau_xy"), dielectronCandidate.tauxy()); + registry.fill(HIST("Data/SingleTrack/h_electron_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("Data/SingleTrack/h_electron_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("Data/SingleTrack/h_electron_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("Data/SingleTrack/h_electron_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("Data/SingleTrack/h_electron_phi"), dielectronCandidate.phi1()); + registry.fill(HIST("Data/SingleTrack/h_electron_phi"), dielectronCandidate.phi2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt1(), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt2(), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound2()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl2()); + registry.fill(HIST("Data/SingleTrack/h_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl1()); + registry.fill(HIST("Data/SingleTrack/h_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl2()); + registry.fill(HIST("Data/Multidimensional/h_4d_histogram"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } // end of loop over dielectron candidates + registry.fill(HIST("Data/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& track : jetTracks) { + sumConstituentPt += track.pt(); + registry.fill(HIST("Data/SingleTrack/h_jet_track_pt"), track.pt()); + registry.fill(HIST("Data/SingleTrack/h_jet_track_eta"), track.eta()); + } + registry.fill(HIST("Data/Jet/h_dielectron_jet_pt"), jet.pt()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_eta"), jet.eta()); + registry.fill(HIST("Data/Jet/h_dielectron_jet_phi"), jet.phi()); + const int constituentMultiplicity = static_cast(jetTracks.size()) + 2 * electronPairs; // adding 2 for the dielectron candidate + registry.fill(HIST("Data/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("Data/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("Data/SingleTrack/h_n_tracks_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionData, "fragmentation function data", false); + + void FragmentationFunctionMCD( + aod::JetCollisionMCD const& collision, + soa::Join const& jets, + aod::CandidatesDielectronMCD const&, + aod::JetTracksMCD const&) + { + // Event selection + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + return; + } + + // Loop over detector-level jets + for (const auto& jet : jets) { + // Optional jet selection + if (useCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + const auto jetTracks = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + double sumConstituentPt = 0.; + int electronPairs = 0; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useCuts && + (!passesPairCuts(dielectronCandidate) || + !passesElectronCuts(dielectronCandidate, 1) || + !passesElectronCuts(dielectronCandidate, 2))) { + continue; + } + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + sumConstituentPt += dielectronCandidate.pt1(); + sumConstituentPt += dielectronCandidate.pt2(); + ++electronPairs; + + const double axisDistance = jetutilities::deltaR(jet, dielectronCandidate); + + // Fill candidate histograms + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("MCD/Jet/h_MCD_z"), z); + registry.fill(HIST("MCD/Jet/h_MCD_jet_distance"), axisDistance); + registry.fill(HIST("MCD/Jet/h_MCD_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_mass"), dielectronCandidate.mass()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaxy"), dielectronCandidate.dcaXY1()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaxy"), dielectronCandidate.dcaXY2()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaz"), dielectronCandidate.dcaZ1()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_dcaz"), dielectronCandidate.dcaZ2()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_pt"), dielectronCandidate.pt()); + registry.fill(HIST("MCD/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_Lxy"), dielectronCandidate.lxy()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_Lz"), dielectronCandidate.lz()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_tau_xy"), dielectronCandidate.tauxy()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_tau_z"), dielectronCandidate.tauz()); + registry.fill(HIST("MCD/Dielectron/h_MCD_dielectron_origin"), dielectronCandidate.mcDecision()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_pt"), dielectronCandidate.pt1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_pt"), dielectronCandidate.pt2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_eta"), dielectronCandidate.eta1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_eta"), dielectronCandidate.eta2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_phi"), dielectronCandidate.phi1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_phi"), dielectronCandidate.phi2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma"), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt1(), dielectronCandidate.tpcNSigmaEl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_nsigma_vs_pt"), dielectronCandidate.pt2(), dielectronCandidate.tpcNSigmaEl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_ncls_found"), dielectronCandidate.tpcNClsFound2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_tpc_chi2_per_cluster"), dielectronCandidate.tpcChi2NCl2()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl1()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_electron_its_chi2_per_cluster"), dielectronCandidate.itsChi2NCl2()); + registry.fill(HIST("MCD/Multidimensional/h_MCD_5d_allParticles"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy(), dielectronCandidate.mcDecision()); + if (dielectronCandidate.mcDecision() == 1) { + registry.fill(HIST("MCD/Multidimensional/h_MCD_4d_PromptJpsi"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } else if (dielectronCandidate.mcDecision() == 2) { + registry.fill(HIST("MCD/Multidimensional/h_MCD_4d_NonPromptJpsi"), jet.pt(), z, dielectronCandidate.mass(), dielectronCandidate.tauxy()); + } + } // end of loop over dielectron candidates + registry.fill(HIST("MCD/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& track : jetTracks) { + sumConstituentPt += track.pt(); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_pt"), track.pt()); + registry.fill(HIST("MCD/SingleTrack/h_MCD_jet_track_eta"), track.eta()); + } + registry.fill(HIST("MCD/Jet/h_MCD_jet_pt"), jet.pt()); + registry.fill(HIST("MCD/Jet/h_MCD_jet_eta"), jet.eta()); + registry.fill(HIST("MCD/Jet/h_MCD_jet_phi"), jet.phi()); + const int constituentMultiplicity = static_cast(jetTracks.size()) + 2 * electronPairs; // adding 2 for the dielectron candidate + registry.fill(HIST("MCD/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("MCD/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("MCD/SingleTrack/h_MCD_n_tracks_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionMCD, "Jpsi fragmentation function detector-level MC", false); + + void FragmentationFunctionMCP( + aod::JetMcCollision const& mccollision, + soa::Join const& jets, + aod::CandidatesDielectronMCP const&, + aod::JetParticles const&) + { + // Event selection + if (useMCPCuts && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(mccollision.posZ()) < vertexZCut))) { + return; + } + for (const auto& jet : jets) { + if (useMCPCuts) { + if (jet.pt() < jetPtMin || jet.pt() > jetPtMax || + std::abs(jet.eta()) > jetEtaMax) { + continue; + } + } + const auto jetParticles = jet.template tracks_as(); + const auto dielectronCandidates = jet.template candidates_as(); + int electronPairs = 0; + double sumConstituentPt = 0.; + for (const auto& dielectronCandidate : dielectronCandidates) { + if (useMCPCuts && !passesPairCuts(dielectronCandidate)) { + continue; + } + const double z = calculateZ(jet, dielectronCandidate); + if (z < 0.) { + continue; + } + ++electronPairs; + + // Because I dont have access to the individual electron pt, I will use the dielectron candidate pt as a proxy for the sum of the two electrons' pt. + sumConstituentPt += dielectronCandidate.pt(); + + const double axisDistance = jetutilities::deltaR(jet, dielectronCandidate); + + // Calculate Lxy for the J/psi candidate + const double dx = dielectronCandidate.vx() - mccollision.posX(); + const double dy = dielectronCandidate.vy() - mccollision.posY(); + + const double pt = std::sqrt( + dielectronCandidate.px() * dielectronCandidate.px() + + dielectronCandidate.py() * dielectronCandidate.py()); + + double lxyMC = 0.; + double tauxyProjected = 0.; + + if (pt > 0.) { + lxyMC = (dx * dielectronCandidate.px() + + dy * dielectronCandidate.py()) / + pt; + + tauxyProjected = lxyMC * dielectronCandidate.m() / pt; + } + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_pt"), dielectronCandidate.pt()); + registry.fill(HIST("MCP/Dielectron/h_dielectron_pt_vs_jet_pt"), jet.pt(), dielectronCandidate.pt()); + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_lxy"), lxyMC); + registry.fill(HIST("MCP/Dielectron/h_MCP_jpsi_tauxy"), tauxyProjected); + registry.fill(HIST("MCP/Jet/h_MCP_z"), z); + registry.fill(HIST("MCP/Jet/h_MCP_jet_distance"), axisDistance); + registry.fill(HIST("MCP/Jet/h_MCP_jet_distance_vs_projection"), axisDistance, z); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_mass"), dielectronCandidate.m()); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_eta"), dielectronCandidate.eta()); + registry.fill(HIST("MCP/Dielectron/h_MCP_dielectron_origin"), dielectronCandidate.origin()); + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_allParticles"), jet.pt(), z, tauxyProjected, dielectronCandidate.origin()); + if (dielectronCandidate.origin() == 1) { + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_PromptJpsi"), jet.pt(), z, dielectronCandidate.m(), tauxyProjected); + } else if (dielectronCandidate.origin() == 2) { + registry.fill(HIST("MCP/Multidimensional/h_MCP_4d_NonPromptJpsi"), jet.pt(), z, dielectronCandidate.m(), tauxyProjected); + } + } // end of loop over dielectron candidates + registry.fill(HIST("MCP/Jet/h_electron_pairs_per_jet"), electronPairs); + if (electronPairs == 0) { + continue; + } + for (const auto& particle : jetParticles) { + sumConstituentPt += particle.pt(); + registry.fill(HIST("MCP/SingleTrack/h_MCP_jet_particle_pt_ElectronPairInfoMissing"), particle.pt()); // named ElectronPairInfoMissing, because for MCP, I don't have access to the individual electron pt. + registry.fill(HIST("MCP/SingleTrack/h_MCP_jet_particle_eta_ElectronPairInfoMissing"), particle.eta()); + } + const int constituentMultiplicity = static_cast(jetParticles.size()) + 2 * electronPairs; + registry.fill(HIST("MCP/Jet/h_constituent_multiplicity_vs_jet_pt"), jet.pt(), constituentMultiplicity); + registry.fill(HIST("MCP/Jet/h_sum_constituent_pt_vs_jet_pt"), jet.pt(), sumConstituentPt); + registry.fill(HIST("MCP/Jet/h_MCP_jet_pt"), jet.pt()); + registry.fill(HIST("MCP/Jet/h_MCP_jet_eta"), jet.eta()); + registry.fill(HIST("MCP/Jet/h_MCP_jet_phi"), jet.phi()); + registry.fill(HIST("MCP/SingleTrack/h_MCP_n_particles_per_jet"), constituentMultiplicity); + } // end of loop over jets + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, FragmentationFunctionMCP, "Jpsi fragmentation function particle-level MC", false); + + void processMcMatching( + aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + DielectronMCDJets const& mcdjets, + DielectronMCPJets const&, + aod::CandidatesDielectronMCD const&, + aod::CandidatesDielectronMCP const&, + aod::JetTracksMCD const& tracksMCD, + aod::JetParticles const& particlesMCP) + { + for (const auto& mccollision : mccollisions) { + registry.fill(HIST("Matching/h_collision_counter"), 0.5); + + if (useMCPCuts && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(mccollision.posZ()) < vertexZCut))) { + continue; + } + registry.fill(HIST("Matching/h_collision_counter"), 1.5); + + const auto collisionsPerMCCollision = + collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + for (const auto& collision : collisionsPerMCCollision) { + registry.fill(HIST("Matching/h_collision_counter"), 2.5); + // Event selection + if (useCuts && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBgapEvents.value, applyRCTflag.value, rctLabel.value) || !(std::abs(collision.posZ()) < vertexZCut))) { + continue; + } + registry.fill(HIST("Matching/h_collision_counter"), 3.5); + const auto mcdJetsPerCollision = + mcdjets.sliceBy(mcdJetsPerCollisionPreslice, collision.globalIndex()); + for (const auto& mcdjet : mcdJetsPerCollision) { + registry.fill(HIST("Matching/h_jet_counter"), 0.5); + if (useCuts) { + if (mcdjet.pt() < jetPtMin || mcdjet.pt() > jetPtMax || + std::abs(mcdjet.eta()) > jetEtaMax) { + continue; + } + } + registry.fill(HIST("Matching/h_jet_counter"), 1.5); + + if (!mcdjet.has_matchedJetCand()) { + continue; + } + registry.fill(HIST("Matching/h_jet_counter"), 2.5); + + const auto matchedMCPJets = + mcdjet.template matchedJetCand_as(); + for (const auto& mcpjet : matchedMCPJets) { + if (useMCPCuts) { + if (mcpjet.pt() < jetPtMin || mcpjet.pt() > jetPtMax || + std::abs(mcpjet.eta()) > jetEtaMax) { + continue; + } + } + registry.fill(HIST("Matching/h_jet_counter"), 3.5); + for (const auto& mcdCandidate : + mcdjet.template candidates_as()) { + registry.fill(HIST("Matching/h_candidate_counter"), 0.5); + + const double zMCD = calculateZ(mcdjet, mcdCandidate); + if (zMCD < 0.) { + continue; + } + if (useCuts && + (!passesPairCuts(mcdCandidate) || + !passesElectronCuts(mcdCandidate, 1) || + !passesElectronCuts(mcdCandidate, 2))) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 1.5); + + const auto matchedParticleId = + jetcandidateutilities::matchedParticleId( + mcdCandidate, tracksMCD, particlesMCP); + + for (const auto& mcpCandidate : + mcpjet.template candidates_as()) { + if (mcpCandidate.mcParticleId() != matchedParticleId) { + continue; + } + + const double zMCP = calculateZ(mcpjet, mcpCandidate); + if (zMCP < 0.) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 2.5); + if (useMCPCuts && !passesPairCuts(mcpCandidate)) { + continue; + } + registry.fill(HIST("Matching/h_candidate_counter"), 3.5); + const double mcpCandidatePt = std::sqrt(mcpCandidate.px() * mcpCandidate.px() + mcpCandidate.py() * mcpCandidate.py()); + double tauxyMCP = 0.; + if (mcpCandidatePt > 0.) { + const double dxMCP = mcpCandidate.vx() - mccollision.posX(); + const double dyMCP = mcpCandidate.vy() - mccollision.posY(); + const double lxyMCP = (dxMCP * mcpCandidate.px() + dyMCP * mcpCandidate.py()) / mcpCandidatePt; + tauxyMCP = lxyMCP * mcpCandidate.m() / mcpCandidatePt; + } + + if (mcpCandidate.origin() == 1) { + registry.fill(HIST("Matching/h_response_prompt"), mcdjet.pt(), zMCD, mcpjet.pt(), zMCP); + registry.fill(HIST("Matching/h_2D_prompt_z"), zMCD, zMCP); + registry.fill(HIST("Matching/h_2D_prompt_jetpt"), mcdjet.pt(), mcpjet.pt()); + registry.fill(HIST("Matching/h_2D_prompt_jpsipt"), mcdCandidate.pt(), mcpCandidate.pt()); + registry.fill(HIST("Matching/h_2D_prompt_tauxy"), mcdCandidate.tauxy(), tauxyMCP); + + } else if (mcpCandidate.origin() == 2) { + registry.fill(HIST("Matching/h_response_nonprompt"), mcdjet.pt(), zMCD, mcpjet.pt(), zMCP); + registry.fill(HIST("Matching/h_2D_nonprompt_z"), zMCD, zMCP); + registry.fill(HIST("Matching/h_2D_nonprompt_jetpt"), mcdjet.pt(), mcpjet.pt()); + registry.fill(HIST("Matching/h_2D_nonprompt_jpsipt"), mcdCandidate.pt(), mcpCandidate.pt()); + registry.fill(HIST("Matching/h_2D_nonprompt_tauxy"), mcdCandidate.tauxy(), tauxyMCP); + } + } + } + } + } + } + } + } + + PROCESS_SWITCH(DqFragmentationFunctionTask, processMcMatching, "MC collision, jet and candidate matching", false); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"dq-fragmentation-function"})}; +} From c28c6edcae3e734bdf53f125c26329535d01d3c3 Mon Sep 17 00:00:00 2001 From: ynishida-style Date: Mon, 9 Nov 2026 19:27:13 +0900 Subject: [PATCH 618/648] [PWGJE] jetShape: add 2D (TPC x TOF) PID maps for template fits (#18616) --- PWGJE/Tasks/jetShape.cxx | 596 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 584 insertions(+), 12 deletions(-) diff --git a/PWGJE/Tasks/jetShape.cxx b/PWGJE/Tasks/jetShape.cxx index 9762f2ff1df..335ced51b66 100644 --- a/PWGJE/Tasks/jetShape.cxx +++ b/PWGJE/Tasks/jetShape.cxx @@ -38,8 +38,12 @@ #include #include +#include #include #include +#include +#include +#include #include #include @@ -92,6 +96,23 @@ struct JetShapeTask { Configurable randomConeMaxAttempts{"randomConeMaxAttempts", 100, "Maximum attempts for each random cone"}; Configurable randomConeSeed{"randomConeSeed", 0u, "TRandom3 seed; 0 lets ROOT choose a unique seed"}; Configurable fillDetailedQA{"fillDetailedQA", false, "Fill high-volume dE/dx, beta and distance QA histograms"}; + Configurable fillPidNsigmaQA{"fillPidNsigmaQA", true, "Fill the light centrality / TOF-matching / nSigma_TOF histograms used to validate the normalisation and the PID efficiency on data"}; + Configurable fillPid2D{"fillPid2D", true, "Fill the dense (p, nSigma_TPC^p, nSigma_TOF^p) map of inclusive tracks with TOF of ALL selected events (no jet bias), without any PID cut"}; + Configurable fillPid2DJet{"fillPid2DJet", true, "Fill dense (p, nSigma_TPC^p, nSigma_TOF^p, r x jet pT) maps for signal and perpendicular cones, plus the inclusive map of the same events, without any PID cut (shared-shape 2D fit)"}; + Configurable pid2DCentMin{"pid2DCentMin", 0.f, "Lower centrality edge for the pid2D jet maps"}; + Configurable pid2DCentMax{"pid2DCentMax", 10.f, "Upper centrality edge for the pid2D jet maps"}; + Configurable pid2DPMin{"pid2DPMin", 2.0f, "Lower momentum edge of all pid2D maps (GeV/c)"}; + Configurable pid2DPMax{"pid2DPMax", 7.4f, "Upper momentum edge of all pid2D maps (GeV/c); p_T <= 5 at |eta| < 0.9 needs p <= 7.2"}; + Configurable fillPid2DMismatch{"fillPid2DMismatch", true, "Fill TOF-mismatch templates: nSigma_TOF^p recomputed with the TOF time of another (random / nearest) TOF track of the same collision"}; + Configurable fillPid2DTpcOnly{"fillPid2DTpcOnly", true, "Fill the TPC-only (p, nSigma_TPC^p, hasTOF, region) map without TOF requirement (TOF matching of p+K relative to pi on data)"}; + Configurable fillPid2DMc{"fillPid2DMc", true, "MC: fill the pid2D maps split by true species, origin and TOF-match quality (closure of the 2D fit)"}; + Configurable pid2DMcTofBadNSigma{"pid2DMcTofBadNSigma", 5.f, "MC: flag the TOF hit as mismatched when |nSigma_TOF| under the true mass hypothesis exceeds this"}; + Configurable fillTofMismatchQA{"fillTofMismatchQA", true, "Fill the wide-range nSigma_TOF histograms used to measure the TOF-mismatch pedestal; the data side also needs fillPidNsigmaQA"}; + Configurable nSigmaWideMid{"nSigmaWideMid", 100.0f, "Boundary between the medium and the coarse part of the wide nSigma_TOF axis"}; + Configurable nSigmaWideMax{"nSigmaWideMax", 1000.0f, "Outer edge of the wide nSigma_TOF axis; one bunch crossing is about 625 sigma"}; + Configurable nBinsNSigmaWideMid{"nBinsNSigmaWideMid", 18, "Bins per side between nSigmaMax and nSigmaWideMid"}; + Configurable nBinsNSigmaWideOuter{"nBinsNSigmaWideOuter", 18, "Bins per side between nSigmaWideMid and nSigmaWideMax"}; + Configurable nBinsPForWide{"nBinsPForWide", 80, "Momentum bins of the wide nSigma_TOF histograms; nSigma depends on p, not on p_T, so these have to be fine"}; Configurable applyCentralityCut{"applyCentralityCut", true, "Apply centralityMinForCut <= centrality < centralityMaxForCut inside this task"}; Configurable useRapiditySelection{"useRapiditySelection", false, "Use species-dependent |y| cut in addition to the detector |eta| acceptance (cross-check only)"}; @@ -103,6 +124,8 @@ struct JetShapeTask { Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; Configurable trackSelections{"trackSelections", "globalTracks", "nominal track selection for yields and tracking efficiency"}; + Configurable skipMbGapEvents{"skipMbGapEvents", true, "reject events whose sub-generator id is the MB gap one"}; + Configurable applyRctSelection{"applyRctSelection", true, "apply the run-condition-table quality selection (has no meaning in MC)"}; Configurable trackSelectionsForDca{"trackSelectionsForDca", "QualityTracksWDCA", "wide-DCA track selection used only for data DCA distributions and MC DCA templates"}; Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; @@ -132,6 +155,7 @@ struct JetShapeTask { TRandom3 randomNumber; std::vector eventSelectionBits; + std::vector emptyEventSelectionBits; int trackSelection = -1; int trackSelectionForDca = -1; std::vector triggerMaskBits; @@ -144,6 +168,36 @@ struct JetShapeTask { triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); randomNumber.SetSeed(randomConeSeed); + std::vector nSigmaWideEdges; + { + const double fineEdge = nSigmaMax.value; + const double midEdge = nSigmaWideMid.value; + const double outerEdge = nSigmaWideMax.value; + const int nFine = nBinsNSigma.value; + const int nMid = nBinsNSigmaWideMid.value; + const int nOuter = nBinsNSigmaWideOuter.value; + nSigmaWideEdges.reserve(2 * (nOuter + nMid) + nFine + 1); + for (int i = 0; i <= nOuter; ++i) { + nSigmaWideEdges.push_back(-outerEdge + (outerEdge - midEdge) * i / nOuter); + } + for (int i = 1; i <= nMid; ++i) { + nSigmaWideEdges.push_back(-midEdge + (midEdge - fineEdge) * i / nMid); + } + for (int i = 1; i <= nFine; ++i) { + nSigmaWideEdges.push_back(-fineEdge + 2.0 * fineEdge * i / nFine); + } + for (int i = 1; i <= nMid; ++i) { + nSigmaWideEdges.push_back(fineEdge + (midEdge - fineEdge) * i / nMid); + } + for (int i = 1; i <= nOuter; ++i) { + nSigmaWideEdges.push_back(midEdge + (outerEdge - midEdge) * i / nOuter); + } + } + const AxisSpec axisNSigmaWide{nSigmaWideEdges, "n#sigma_{TOF}"}; + const AxisSpec axisPWide{nBinsPForWide.value, 0., pMax.value, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPtWide{nBinsPt.value, 0., ptMax.value, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisCentWide{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value, "centrality (%)"}; + // Histograms definition registry.add("tpcTofPi", "tpcTofPi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("tpcTofPr", "tpcTofPr", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); @@ -166,6 +220,85 @@ struct JetShapeTask { registry.add("trackTpcNClsFound", "trackTpcNClsFound", HistType::kTH1F, {{100, 0, 200}}); registry.add("trackItsNCls", "trackItsNCls", HistType::kTH1F, {{10, 0, 10}}); + if (fillPidNsigmaQA) { + // (1) Normalisation QA: every collision reaching processInclusiveProductionRatio, + // BEFORE passCentrality(). A percentile estimator must be flat here. + registry.add("centralityAllInc", "centFT0M of every collision entering the inclusive process (before the centrality cut);centrality (%);events", HistType::kTH1F, {{110, -5., 105.}}); + registry.add("eventCounterIncAll", "collisions entering the inclusive process, before the centrality cut", HistType::kTH1F, {{1, 0., 1.}}); + + // (2) PID-free charged-track yield and the data-driven TOF-matching efficiency. + registry.add("trackPtAllInc", "all nominal tracks (no PID, no TOF requirement);p_{T} (GeV/c);centrality (%)", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("trackPtTofInc", "nominal tracks with a TOF hit;p_{T} (GeV/c);centrality (%)", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // (3) Species-tagged TOF status: 0 = no TOF, 1 = finite nSigma_TOF, 2 = sentinel (no event time). + // Same definition as mcTofNSigmaStatus* on the MC side. + registry.add("tofStatusPi", "TPC-tagged pion candidates;p_{T} (GeV/c);TOF status;centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {3, 0., 3.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tofStatusPr", "TPC-tagged proton candidates;p_{T} (GeV/c);TOF status;centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {3, 0., 3.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // (4) nSigma_TOF of TPC-tagged candidates, WITHOUT the TOF window -> eps_TOFwin on data. + registry.add("tofNSigmaTaggedPi", "TPC-tagged pion candidates with TOF;p_{T} (GeV/c);n#sigma_{TOF}^{#pi};centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tofNSigmaTaggedPr", "TPC-tagged proton candidates with TOF;p_{T} (GeV/c);n#sigma_{TOF}^{p};centrality (%)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + } + + if (fillTofMismatchQA) { + // (5) Same selection as (4) but on the wide axis: the mismatch pedestal is measured + // where there is no signal and interpolated under the core, and the species + // contamination peaks (whose positions follow from p_T and the path length) + // become visible instead of piling up in the underflow. + registry.add("tofNSigmaWidePi", "TPC-tagged pion candidates with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("tofNSigmaWidePr", "TPC-tagged proton candidates with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + // Response matrix p -> p_T for exactly the same tracks, needed to bring the yields + // extracted in p bins back onto the p_T axis. pVsPtFor* cannot be used for this: + // it is filled only for tracks that passed both nSigma windows. + registry.add("pVsPtWidePi", "TPC-tagged pion candidates with TOF;#it{p};#it{p}_{T};centrality (%)", HistType::kTH3F, {axisPWide, axisPtWide, axisCentWide}); + registry.add("pVsPtWidePr", "TPC-tagged proton candidates with TOF;#it{p};#it{p}_{T};centrality (%)", HistType::kTH3F, {axisPWide, axisPtWide, axisCentWide}); + } + + // ---- 2D PID maps (nSigma_TPC^p x nSigma_TOF^p), dense THnF, no PID cut ---- + // Binned in p, not p_T, because both nSigma depend on p. One proton-hypothesis map holds p, K and pi + // (pi lies at nSigma_TOF^p > -12 for p > 2.8 GeV/c). Dense on purpose: at full statistics a THnSparse + // of this size is filled to >50 % and costs far more per bin. Centrality is a window + // (pid2DCentMin/Max), not an axis: a THn keeps under/overflow bins on every axis. + const AxisSpec axisP2D{pid2DPEdges(), "#it{p} (GeV/#it{c})"}; + const AxisSpec axisTpc2D{44, -4., 7., "n#sigma_{TPC}^{p}"}; + const AxisSpec axisTof2D{72, -12., 6., "n#sigma_{TOF}^{p}"}; + const AxisSpec axisRJet2D{28, 0., 28., "r bin + 7 #times jet p_{T} bin"}; + const bool anyPid2D = fillPid2D || fillPid2DJet; + + if (fillPid2D) { + registry.add("pid2DPr", "inclusive tracks with TOF, all events, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p}", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D}); + } + + if (fillPid2DJet) { + registry.add("pid2DJetPr", "signal cone, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DPerpPr", "perpendicular cones, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DRandPr", "random cones, tracks with TOF, no PID cut;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};r bin + 7 reference-jet bin", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisRJet2D}); + registry.add("pid2DIncPr", "all tracks with TOF in events with an accepted jet;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p}", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D}); + } + + if (fillPid2DMismatch && anyPid2D) { + registry.add("pid2DMisPr", "TOF-mismatch template;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p} (borrowed time);source + 2 region", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, {10, 0., 10., "source + 2 #times region"}}); + } + + if (fillPid2DTpcOnly && anyPid2D) { + registry.add("pid2DTpcOnlyPr", "TPC only, no TOF requirement;#it{p};n#sigma_{TPC}^{p};hasTOF;region", HistType::kTHnF, {axisP2D, axisTpc2D, {2, 0., 2., "hasTOF"}, {3, 0., 3., "region"}}); + } + + if (anyPid2D) { + constexpr double PtMin2D = 1.0; + constexpr double PtBinWidth2D = 0.1; + const int nPt2D = static_cast(std::lround((pid2DPMax.value - PtMin2D) / PtBinWidth2D)); + registry.add("pid2DPVsPt", "tracks entering the pid2D maps;#it{p};#it{p}_{T};region", HistType::kTHnF, {axisP2D, {nPt2D, PtMin2D, PtMin2D + nPt2D * PtBinWidth2D, "#it{p}_{T} (GeV/#it{c})"}, {5, 0., 5., "region"}}); + } + + if (fillPid2DMc && doprocessEfficiencyAndPurity) { + const AxisSpec axisTruthInc2DMc{24, 0., 24., "species + 6 #times (origin + 2 #times tofBad)"}; + const AxisSpec axisTruthCone2DMc{24, 0., 24., "species4 + 4 #times (tofBad + 2 #times r bin)"}; + registry.add("mcPid2DIncPr", "MC: all tracks with TOF in events with a matched jet;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthInc2DMc}); + registry.add("mcPid2DJetPr", "MC: signal cone, tracks with TOF;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthCone2DMc}); + registry.add("mcPid2DPerpPr", "MC: perpendicular cones, tracks with TOF;#it{p};n#sigma_{TPC}^{p};n#sigma_{TOF}^{p};truth index", HistType::kTHnF, {axisP2D, axisTpc2D, axisTof2D, axisTruthCone2DMc}); + } + registry.add("jetTpcPi", "jetTpcPi", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); registry.add("jetTofPi", "jetTofPi", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); registry.add("jetTpcPr", "jetTpcPr", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); @@ -226,6 +359,10 @@ struct JetShapeTask { registry.add("ptVsCentrality", "ptvscentrality", HistType::kTH2F, {{100, 0, 100}, {300, 0, 300}}); registry.add("ptResolution", "ptResolution", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {100, -1.0, +1.0}}); registry.add("mcCentralityReco", "mcCentralityReco", HistType::kTH1F, {{100, 0, 100}}); + registry.add("mcCutFlow", "processEfficiencyAndPurity cut flow: 0 = called, 1 = selectCollision, 2 = has_mcCollision, 3 = passCentrality", HistType::kTH1F, {{4, 0., 4.}}); + registry.add("mcSelDetail", "selectCollision breakdown: 0 = called, 1 = not MB gap, 2 = RCT, 3 = event selection bits", HistType::kTH1F, {{4, 0., 4.}}); + registry.add("mcSubGeneratorId", "sub-generator id of the associated MC collision", HistType::kTH1F, {{6, -2.5, 3.5}}); + registry.add("mcCentralityRaw", "centFT0M seen by processEfficiencyAndPurity, before the cut", HistType::kTH1F, {{124, -12., 112.}}); // MC Efficiency Denominators // Jet (In-cone) @@ -256,6 +393,28 @@ struct JetShapeTask { registry.add("effNumPidPiInc", "True primary pion reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("effNumPidPrInc", "True primary proton reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + // nSigma window efficiency breakdown (inclusive only, ~0.3 MB each) + registry.add("effNumKinePiInc", "True primary pion, TOF matched, passing the species kinematics (denominator of the nSigma windows)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcWinPiInc", "effNumKinePiInc and the |nSigma_TPC^pi| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofWinPiInc", "effNumKinePiInc and the |nSigma_TOF^pi| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumKinePrInc", "True primary proton, TOF matched, passing the species kinematics (denominator of the nSigma windows)", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcWinPrInc", "effNumKinePrInc and the |nSigma_TPC^p| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofWinPrInc", "effNumKinePrInc and the |nSigma_TOF^p| window ONLY", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // nSigma response of true primary pions and protons in MC (~1.5 MB each) + registry.add("mcTpcNSigmaPi", "True primary pion: p vs nSigma_TPC^pi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaPi", "True primary pion: pT vs nSigma_TOF^pi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTpcNSigmaPr", "True primary proton: p vs nSigma_TPC^p", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaPr", "True primary proton: pT vs nSigma_TOF^p", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("mcTofNSigmaStatusPi", "True primary pion: 0 = no TOF, 1 = finite nSigma_TOF, 2 = unphysical nSigma_TOF", HistType::kTH1F, {{3, 0., 3.}}); + registry.add("mcTofNSigmaStatusPr", "True primary proton: 0 = no TOF, 1 = finite nSigma_TOF, 2 = unphysical nSigma_TOF", HistType::kTH1F, {{3, 0., 3.}}); + + if (fillTofMismatchQA) { + registry.add("mcTofNSigmaWidePi", "True primary pion with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("mcTofNSigmaWidePr", "True primary proton with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + registry.add("mcTofNSigmaWideKa", "True primary kaon with TOF, wide axis", HistType::kTH3F, {axisPWide, axisNSigmaWide, axisCentWide}); + } + // Jet (In-cone) registry.add("effNumTpcPiJet", "effNumTpcPiJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); registry.add("effNumTpcKaJet", "effNumTpcKaJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); @@ -420,6 +579,133 @@ struct JetShapeTask { return std::asinh(pz / mt); } + std::vector pid2DPEdges() const + { + constexpr double FineWidth = 0.2; + constexpr double CoarseWidth = 0.4; + constexpr double FineUpTo = 5.0; + constexpr double Tolerance = 1e-6; + constexpr double Rounding = 1000.; + std::vector edges{static_cast(pid2DPMin.value)}; + double p = pid2DPMin.value; + while (p < pid2DPMax.value - Tolerance) { + p += (p < FineUpTo - Tolerance) ? FineWidth : CoarseWidth; + p = std::min(std::round(p * Rounding) / Rounding, static_cast(pid2DPMax.value)); + edges.push_back(p); + } + return edges; + } + + // TOF hits of one collision, used to build the mismatch templates (8). + struct TofHit2D { + int64_t index; + float eta; + float phi; + float time; // t - t0 = L / (beta c), ps + }; + static constexpr float SpeedOfLightCmPerPs = 0.0299792458f; + + template + static bool tofTimeOfFlight(TrackT const& track, float& time) + { + if (!track.hasTOF() || track.beta() <= 0.f || track.length() <= 0.f) { + return false; + } + time = track.length() / (track.beta() * SpeedOfLightCmPerPs); + return true; + } + + template + void collectTofHits(JetTracksT const& tracks, std::vector& hits) + { + hits.clear(); + for (const auto& jetTrack : tracks) { + const auto track = jetTrack.template track_as(); + float time = 0.f; + if (tofTimeOfFlight(track, time)) { + hits.push_back({track.globalIndex(), track.eta(), track.phi(), time}); + } + } + } + + template + std::array mismatchNSigmaPr(TrackT const& track, std::vector const& hits) + { + constexpr int MaxAttempts = 10; + constexpr size_t MinHits = 2; + std::array out{std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; + float time = 0.f; + const float sigma = track.tofExpSigmaPr(); + if (hits.size() < MinHits || sigma <= 0.f || !tofTimeOfFlight(track, time)) { + return out; + } + const int64_t self = track.globalIndex(); + const float nSigma = track.tofNSigmaPr(); + for (int attempt = 0; attempt < MaxAttempts; ++attempt) { + const auto& hit = hits[randomNumber.Integer(static_cast(hits.size()))]; + if (hit.index != self) { + out[0] = nSigma + (hit.time - time) / sigma; + break; + } + } + float best = std::numeric_limits::max(); + const TofHit2D* nearest = nullptr; + for (const auto& hit : hits) { + if (hit.index == self) { + continue; + } + const float dEta = hit.eta - track.eta(); + const float dPhi = absDeltaPhi(hit.phi, track.phi()); + const float d2 = dEta * dEta + dPhi * dPhi; + if (d2 < best) { + best = d2; + nearest = &hit; + } + } + if (nearest != nullptr) { + out[1] = nSigma + (nearest->time - time) / sigma; + } + return out; + } + + // Regions of the pid2D maps (int constants, not an enum: they are combined with float bin centres). + static constexpr int RegIncAll = 0; // inclusive, all events + static constexpr int RegIncJet = 1; // inclusive, events with an accepted jet + static constexpr int RegSignal = 2; + static constexpr int RegPerp = 3; + static constexpr int RegRand = 4; + + void fillMismatchTemplate(float p, float tpcPr, std::array const& mis, int region) + { + constexpr int NSources = 2; + for (int src = 0; src < NSources; ++src) { + if (std::isfinite(mis[src])) { + registry.fill(HIST("pid2DMisPr"), p, tpcPr, mis[src], src + NSources * region + 0.5f); + } + } + } + + // Folded (r, jet pT) index of the pid2D jet maps; -1 if outside. + static int rJetIndex2D(float r, float jetPt) + { + constexpr int NRBins2D = 7; + constexpr int NJetPtBins2D = 4; + constexpr float RBinWidth2D = 0.1f; + constexpr float RMax2D = NRBins2D * RBinWidth2D; + constexpr int Outside = -1; + static constexpr std::array JetPtEdges2D = {20.f, 40.f, 60.f, 80.f, 140.f}; + if (r < 0.f || r >= RMax2D) { + return Outside; + } + const int ir = std::min(NRBins2D - 1, static_cast(r / RBinWidth2D)); + for (int k = 0; k < NJetPtBins2D; ++k) { + if (jetPt >= JetPtEdges2D[k] && jetPt < JetPtEdges2D[k + 1]) { + return ir + NRBins2D * k; + } + } + return Outside; + } + bool passSpeciesKinematics(float pt, float eta, float mass) const { if (std::abs(eta) >= etaTrUp.value) { @@ -792,9 +1078,11 @@ struct JetShapeTask { const float maxR2 = distanceMax * distanceMax; - // The jet finder remains configured with globalTracks. The nominal-yield - // sample and the wide-DCA sample are selected independently relative to - // the same reconstructed jet axes. + std::vector tofHits; + if (fillPid2DJet && fillPid2DMismatch) { + collectTofHits(tracks, tofHits); + } + for (const auto& jetTrack : tracks) { const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); @@ -832,6 +1120,22 @@ struct JetShapeTask { const bool isTpcPiRange = (tpcPi > tpcNSigmaPiMin && tpcPi < tpcNSigmaPiMax); const bool isTpcPrRange = (tpcPr > tpcNSigmaPrMin && tpcPr < tpcNSigmaPrMax); + const bool inPid2DWindow = fillPid2DJet && passesNominal && passPrKinematics && + centrality >= pid2DCentMin && centrality < pid2DCentMax && + trkP >= pid2DPMin && trkP < pid2DPMax; + const bool fillPid2DTrack = inPid2DWindow && track.hasTOF(); + const bool fillPid2DTpcOnlyTrack = inPid2DWindow && fillPid2DTpcOnly; + const float hasTofBin = track.hasTOF() ? 1.5f : 0.5f; + std::array misNSigma{std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()}; + if (fillPid2DTrack) { + registry.fill(HIST("pid2DIncPr"), trkP, tpcPr, tofPr); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegIncJet + 0.5f); + if (fillPid2DMismatch) { + misNSigma = mismatchNSigmaPr(track, tofHits); + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegIncJet); + } + } + if (passesNominal && fillDetailedQA) { registry.fill(HIST("jetTpcPi"), trkP, tpcPi); registry.fill(HIST("jetTofPi"), trkPt, tofPi); @@ -857,6 +1161,22 @@ struct JetShapeTask { } const float distBg = std::sqrt(distBgSq); + if (fillPid2DTpcOnlyTrack || fillPid2DTrack) { + const int idx2D = rJetIndex2D(distBg, jet.ptCorr); + if (idx2D >= 0) { + if (fillPid2DTpcOnlyTrack) { + registry.fill(HIST("pid2DTpcOnlyPr"), trkP, tpcPr, hasTofBin, 2.5f); + } + if (fillPid2DTrack) { + registry.fill(HIST("pid2DPerpPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegPerp + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegPerp); + } + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("dcaPrPerpJet"), trkPt, track.dcaXY(), distBg, jet.ptCorr, centrality); @@ -893,6 +1213,22 @@ struct JetShapeTask { } const float distance = std::sqrt(distanceSq); + if (fillPid2DTpcOnlyTrack || fillPid2DTrack) { + const int idx2D = rJetIndex2D(distance, jet.ptCorr); + if (idx2D >= 0) { + if (fillPid2DTpcOnlyTrack) { + registry.fill(HIST("pid2DTpcOnlyPr"), trkP, tpcPr, hasTofBin, 1.5f); + } + if (fillPid2DTrack) { + registry.fill(HIST("pid2DJetPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegSignal + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegSignal); + } + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("jetDcaPr"), trkPt, track.dcaXY(), distance, jet.ptCorr, centrality); @@ -924,8 +1260,6 @@ struct JetShapeTask { } } - // Random cones are outside the jet loop, so every track-cone pair is - // filled exactly once. Each cone is associated with one selected jet. for (const auto& cone : randomCones) { const float dEtaRC = trkEta - cone.eta; if (std::abs(dEtaRC) >= distanceMax) { @@ -938,6 +1272,17 @@ struct JetShapeTask { } const float distRC = std::sqrt(distRCSq); + if (fillPid2DTrack) { + const int idx2D = rJetIndex2D(distRC, cone.referenceJetPtCorr); + if (idx2D >= 0) { + registry.fill(HIST("pid2DRandPr"), trkP, tpcPr, tofPr, idx2D + 0.5f); + registry.fill(HIST("pid2DPVsPt"), trkP, trkPt, RegRand + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(trkP, tpcPr, misNSigma, RegRand); + } + } + } + if (passesWideDca) { if (hasTofPr && isTpcPrRange) { registry.fill(HIST("dcaPrRandCone"), trkPt, track.dcaXY(), distRC, cone.referenceJetPtCorr, centrality); @@ -991,11 +1336,20 @@ struct JetShapeTask { return; } const float centrality = collision.centFT0M(); + if (fillPidNsigmaQA) { + registry.fill(HIST("centralityAllInc"), centrality); + registry.fill(HIST("eventCounterIncAll"), 0.5); + } if (!passCentrality(centrality)) { return; } registry.fill(HIST("eventCounterInc"), centrality); + std::vector tofHits; + if (fillPid2D && fillPid2DMismatch) { + collectTofHits(tracks, tofHits); + } + for (const auto& jetTrack : tracks) { const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); @@ -1044,6 +1398,59 @@ struct JetShapeTask { continue; } + if (fillPidNsigmaQA) { + constexpr float MaxSaneNSigma = 10.f; + constexpr float NoTofBin = 0.5f; + constexpr float FiniteNSigmaBin = 1.5f; + constexpr float UnphysicalNSigmaBin = 2.5f; + const bool trackHasTof = track.hasTOF(); + + registry.fill(HIST("trackPtAllInc"), track.pt(), centrality); + if (trackHasTof) { + registry.fill(HIST("trackPtTofInc"), track.pt(), centrality); + } + if (passPiKinematics && isTpcPiRange) { + registry.fill(HIST("tofStatusPi"), track.pt(), + !trackHasTof ? NoTofBin + : (std::abs(track.tofNSigmaPi()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin), + centrality); + if (trackHasTof) { + registry.fill(HIST("tofNSigmaTaggedPi"), track.pt(), track.tofNSigmaPi(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("tofNSigmaWidePi"), track.p(), track.tofNSigmaPi(), centrality); + registry.fill(HIST("pVsPtWidePi"), track.p(), track.pt(), centrality); + } + } + } + if (passPrKinematics && isTpcPrRange) { + registry.fill(HIST("tofStatusPr"), track.pt(), + !trackHasTof ? NoTofBin + : (std::abs(track.tofNSigmaPr()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin), + centrality); + if (trackHasTof) { + registry.fill(HIST("tofNSigmaTaggedPr"), track.pt(), track.tofNSigmaPr(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("tofNSigmaWidePr"), track.p(), track.tofNSigmaPr(), centrality); + registry.fill(HIST("pVsPtWidePr"), track.p(), track.pt(), centrality); + } + } + } + } + + if (fillPid2D && passPrKinematics && centrality >= pid2DCentMin && centrality < pid2DCentMax && + track.p() >= pid2DPMin && track.p() < pid2DPMax) { + if (fillPid2DTpcOnly) { + registry.fill(HIST("pid2DTpcOnlyPr"), track.p(), track.tpcNSigmaPr(), track.hasTOF() ? 1.5f : 0.5f, 0.5f); + } + if (track.hasTOF()) { + registry.fill(HIST("pid2DPr"), track.p(), track.tpcNSigmaPr(), track.tofNSigmaPr()); + registry.fill(HIST("pid2DPVsPt"), track.p(), track.pt(), RegIncAll + 0.5f); + if (fillPid2DMismatch) { + fillMismatchTemplate(track.p(), track.tpcNSigmaPr(), mismatchNSigmaPr(track, tofHits), RegIncAll); + } + } + } + if (fillDetailedQA) { registry.fill(HIST("tpcPi"), track.p(), track.tpcNSigmaPi()); registry.fill(HIST("tofPi"), track.pt(), track.tofNSigmaPi()); @@ -1070,6 +1477,99 @@ struct JetShapeTask { } PROCESS_SWITCH(JetShapeTask, processInclusiveProductionRatio, "inclusive Production ratio", false); + template + void fillPid2DMcTrack(JetTrackT const& jetTrack, TrackT const& track, std::vector const& jets, float maxR2) + { + constexpr int SpPi = 0; + constexpr int SpKa = 1; + constexpr int SpPr = 2; + constexpr int SpEl = 3; + constexpr int SpMu = 4; + constexpr int SpOther = 5; + constexpr int NSpecies = 6; + constexpr int NSpeciesCone = 4; + constexpr float JetPtMin2D = 20.f; + constexpr float JetPtMax2D = 140.f; + constexpr float RMax2D = 0.7f; + constexpr float REdge1 = 0.2f; + constexpr float REdge2 = 0.4f; + + const float trkP = track.p(); + const float tpcPr = track.tpcNSigmaPr(); + const float tofPr = track.tofNSigmaPr(); + + int species = SpOther; + int origin = 1; + float tofTrue = 0.f; + if (jetTrack.has_mcParticle()) { + const auto mcPart = jetTrack.template mcParticle_as(); + origin = mcPart.isPhysicalPrimary() ? 0 : 1; + switch (std::abs(mcPart.pdgCode())) { + case PDG_t::kPiPlus: + species = SpPi; + tofTrue = track.tofNSigmaPi(); + break; + case PDG_t::kKPlus: + species = SpKa; + tofTrue = track.tofNSigmaKa(); + break; + case PDG_t::kProton: + species = SpPr; + tofTrue = tofPr; + break; + case PDG_t::kElectron: // beta ~ 1 at p > 2 GeV/c: the pion hypothesis is within 0.1 sigma + species = SpEl; + tofTrue = track.tofNSigmaPi(); + break; + case PDG_t::kMuonMinus: + species = SpMu; + tofTrue = track.tofNSigmaPi(); + break; + default: + break; + } + } + const int tofBad = (species != SpOther && std::abs(tofTrue) > pid2DMcTofBadNSigma) ? 1 : 0; + registry.fill(HIST("mcPid2DIncPr"), trkP, tpcPr, tofPr, species + NSpecies * (origin + 2 * tofBad) + 0.5f); + + const int speciesCone = std::min(species, NSpeciesCone - 1); + auto coneIndex = [&](float r) { + const int ir = r < REdge1 ? 0 : (r < REdge2 ? 1 : 2); + return speciesCone + NSpeciesCone * (tofBad + 2 * ir) + 0.5f; + }; + + const float trkEta = track.eta(); + const float trkPhi = track.phi(); + for (const auto& jet : jets) { + if (jet.detPtCorr < JetPtMin2D || jet.detPtCorr >= JetPtMax2D) { + continue; + } + const float dEta = trkEta - jet.detEta; + if (std::abs(dEta) >= RMax2D) { + continue; + } + const float dPhi = absDeltaPhi(trkPhi, jet.detPhi); + const float distSq = dEta * dEta + dPhi * dPhi; + if (distSq < maxR2) { + const float r = std::sqrt(distSq); + if (r < RMax2D) { + registry.fill(HIST("mcPid2DJetPr"), trkP, tpcPr, tofPr, coneIndex(r)); + } + } + // Both perpendicular cones, as for pid2DPerpPr in data. + for (const auto& conePhi : {jet.detPhiBg1, jet.detPhiBg2}) { + const float dPhiBg = absDeltaPhi(trkPhi, conePhi); + const float distBgSq = dEta * dEta + dPhiBg * dPhiBg; + if (distBgSq < maxR2) { + const float rBg = std::sqrt(distBgSq); + if (rBg < RMax2D) { + registry.fill(HIST("mcPid2DPerpPr"), trkP, tpcPr, tofPr, coneIndex(rBg)); + } + } + } + } + } + void processEfficiencyAndPurity( soa::Filtered>::iterator const& collision, JetTracksWithPIMC const& jetTracks, @@ -1078,20 +1578,47 @@ struct JetShapeTask { aod::ChargedMCParticleLevelJets const& mcpJets, aod::JetParticles const& mcParticles) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + constexpr float CutFlowCalled = 0.5f; + constexpr float CutFlowEventSelection = 1.5f; + constexpr float CutFlowHasMcCollision = 2.5f; + constexpr float CutFlowCentrality = 3.5f; + + constexpr float SelDetailCalled = 0.5f; + constexpr float SelDetailNotMbGap = 1.5f; + constexpr float SelDetailRct = 2.5f; + constexpr float SelDetailEventBits = 3.5f; + + registry.fill(HIST("mcCutFlow"), CutFlowCalled); + registry.fill(HIST("mcSelDetail"), SelDetailCalled); + registry.fill(HIST("mcSubGeneratorId"), collision.getSubGeneratorId()); + if (collision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + registry.fill(HIST("mcSelDetail"), SelDetailNotMbGap); + } + if (jetderiveddatautilities::selectCollision(collision, emptyEventSelectionBits, false, true)) { + registry.fill(HIST("mcSelDetail"), SelDetailRct); + } + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, false, false)) { + registry.fill(HIST("mcSelDetail"), SelDetailEventBits); + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMbGapEvents, applyRctSelection)) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowEventSelection); if (!collision.has_mcCollision()) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowHasMcCollision); const auto reducedMcCollisionId = collision.mcCollisionId(); (void)mcpJets; const float centrality = collision.centFT0M(); + registry.fill(HIST("mcCentralityRaw"), centrality); if (!passCentrality(centrality)) { return; } + registry.fill(HIST("mcCutFlow"), CutFlowCentrality); const float rho = collision.rho(); const float maxR2 = distanceMax * distanceMax; @@ -1255,6 +1782,13 @@ struct JetShapeTask { continue; } + if (fillPid2DMc && passesNominal && track.hasTOF() && !validJets.empty() && + centrality >= pid2DCentMin && centrality < pid2DCentMax && + track.p() >= pid2DPMin && track.p() < pid2DPMax && + passSpeciesKinematics(track.pt(), track.eta(), ProtonMass)) { + fillPid2DMcTrack(jetTrack, track, validJets, maxR2); + } + if (!jetTrack.has_mcParticle()) { continue; } @@ -1284,12 +1818,12 @@ struct JetShapeTask { track.tpcNSigmaPi() < tpcNSigmaPiMax; const bool passTpcPr = track.tpcNSigmaPr() > tpcNSigmaPrMin && track.tpcNSigmaPr() < tpcNSigmaPrMax; - const bool isRecoPi = passSpeciesKinematics(recoPt, track.eta(), PionMass) && - passTpcPi && hasTof && - std::abs(track.tofNSigmaPi()) < nSigmaTofCut; - const bool isRecoPr = passSpeciesKinematics(recoPt, track.eta(), ProtonMass) && - passTpcPr && hasTof && - std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + const bool passKineRecoPi = passSpeciesKinematics(recoPt, track.eta(), PionMass); + const bool passKineRecoPr = passSpeciesKinematics(recoPt, track.eta(), ProtonMass); + const bool passTofPi = hasTof && std::abs(track.tofNSigmaPi()) < nSigmaTofCut; + const bool passTofPr = hasTof && std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + const bool isRecoPi = passKineRecoPi && passTpcPi && passTofPi; + const bool isRecoPr = passKineRecoPr && passTpcPr && passTofPr; float originBin = 2.5f; if (isPrimary) { @@ -1478,10 +2012,30 @@ struct JetShapeTask { registry.fill(HIST("ptResolution"), track.pt(), track.pt() - mcPt); + constexpr float MaxSaneNSigma = 10.f; // nSigma_TOF is filled with a sentinel value when the event time is missing + constexpr float NoTofBin = 0.5f; + constexpr float FiniteNSigmaBin = 1.5f; + constexpr float UnphysicalNSigmaBin = 2.5f; + if (isTruePi) { registry.fill(HIST("effNumTpcPiInc"), mcPt, centrality); + registry.fill(HIST("mcTofNSigmaStatusPi"), !hasTof ? NoTofBin : (std::abs(track.tofNSigmaPi()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin)); if (hasTof) { registry.fill(HIST("effNumTofPiInc"), mcPt, centrality); + registry.fill(HIST("mcTpcNSigmaPi"), track.p(), track.tpcNSigmaPi(), centrality); + registry.fill(HIST("mcTofNSigmaPi"), mcPt, track.tofNSigmaPi(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWidePi"), track.p(), track.tofNSigmaPi(), centrality); + } + if (passKineRecoPi) { + registry.fill(HIST("effNumKinePiInc"), mcPt, centrality); + if (passTpcPi) { + registry.fill(HIST("effNumTpcWinPiInc"), mcPt, centrality); + } + if (passTofPi) { + registry.fill(HIST("effNumTofWinPiInc"), mcPt, centrality); + } + } } if (isRecoPi) { registry.fill(HIST("effNumPidPiInc"), mcPt, centrality); @@ -1490,11 +2044,29 @@ struct JetShapeTask { registry.fill(HIST("effNumTpcKaInc"), mcPt, centrality); if (hasTof) { registry.fill(HIST("effNumTofKaInc"), mcPt, centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWideKa"), track.p(), track.tofNSigmaKa(), centrality); + } } } else { registry.fill(HIST("effNumTpcPrInc"), mcPt, centrality); + registry.fill(HIST("mcTofNSigmaStatusPr"), !hasTof ? NoTofBin : (std::abs(track.tofNSigmaPr()) < MaxSaneNSigma ? FiniteNSigmaBin : UnphysicalNSigmaBin)); if (hasTof) { registry.fill(HIST("effNumTofPrInc"), mcPt, centrality); + registry.fill(HIST("mcTpcNSigmaPr"), track.p(), track.tpcNSigmaPr(), centrality); + registry.fill(HIST("mcTofNSigmaPr"), mcPt, track.tofNSigmaPr(), centrality); + if (fillTofMismatchQA) { + registry.fill(HIST("mcTofNSigmaWidePr"), track.p(), track.tofNSigmaPr(), centrality); + } + if (passKineRecoPr) { + registry.fill(HIST("effNumKinePrInc"), mcPt, centrality); + if (passTpcPr) { + registry.fill(HIST("effNumTpcWinPrInc"), mcPt, centrality); + } + if (passTofPr) { + registry.fill(HIST("effNumTofWinPrInc"), mcPt, centrality); + } + } } if (isRecoPr) { registry.fill(HIST("effNumPidPrInc"), mcPt, centrality); From 9a618533ada921c2a215bab16d45e3034dd5fda6 Mon Sep 17 00:00:00 2001 From: YoshiniBailung <69502501+YoshiniBailung@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:31:58 +0100 Subject: [PATCH 619/648] [PWGDQ] update qaMatching remove MFTMult joining (#18526) --- PWGDQ/Tasks/qaMatching.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGDQ/Tasks/qaMatching.cxx b/PWGDQ/Tasks/qaMatching.cxx index 104b54741d4..67860f8343b 100644 --- a/PWGDQ/Tasks/qaMatching.cxx +++ b/PWGDQ/Tasks/qaMatching.cxx @@ -198,7 +198,7 @@ namespace qamatching DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Candidate, candidate, int32_t, o2::aod::QaMatchingCandidates, "QAMCANDs", ""); } // namespace qamatching -using MyEvents = soa::Join; +using MyEvents = soa::Join; using MyMuons = soa::Join; using MyMuonsReAlign = soa::Join; using MyMuonsMC = soa::Join; From d5c58b189012ea5491726fef4634f48eaaafbf4a Mon Sep 17 00:00:00 2001 From: arvindkhuntia <31609955+arvindkhuntia@users.noreply.github.com> Date: Mon, 9 Nov 2026 12:40:19 +0100 Subject: [PATCH 620/648] [PWGJE] Add ITS PID veto and pO rapidity QA to nuclei-in-jets analysis (#18649) Co-authored-by: Arvind Khuntia --- PWGJE/Tasks/nucleiInJets.cxx | 222 ++++++++++++++++++++++++++--------- 1 file changed, 165 insertions(+), 57 deletions(-) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index 5b57190673e..88c47017811 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -42,12 +43,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -90,9 +93,9 @@ struct nucleiInJets { case -321: // k- return -Particle::kKaon; - case 2212: // p + case PDG_t::kProton: // p return Particle::kProton; - case -2212: // antip + case PDG_t::kProtonBar: // antip return -Particle::kProton; case Pdg::kDeuteron: // Deuteron @@ -165,6 +168,17 @@ struct nucleiInJets { Configurable cfgnTPCPIDHeTOF{"cfgnTPCPIDHeTOF", 3, "nTPC PID He"}; Configurable cfgnTPCPIDTrTOF{"cfgnTPCPIDTrTOF", 3, "nTPC PID Tr"}; + // Enable an ITS nSigma veto on inclusive TPC PID selections; disabled by default. + struct ItsPid : ConfigurableGroup { + std::string prefix = "itsPid"; + Configurable applyItsPidInc{"applyItsPidInc", false, "apply ITS PID veto to inclusive TPC selections"}; + Configurable ptMaxItsPidProt{"ptMaxItsPidProt", 1.0, "maximum pT for proton ITS PID"}; + Configurable ptMaxItsPidDeut{"ptMaxItsPidDeut", 1.0, "maximum pT for deuteron ITS PID"}; + Configurable ptMaxItsPidHel{"ptMaxItsPidHel", 1.0, "maximum pT for helium ITS PID"}; + Configurable nSigmaItsMin{"nSigmaItsMin", -3.0, "minimum ITS nSigma"}; + Configurable nSigmaItsMax{"nSigmaItsMax", 3.0, "maximum ITS nSigma"}; + } itsPid; + Configurable cEnableProtonQA{"cEnableProtonQA", true, "nTPC PID Pr"}; Configurable cEnableDeuteronQA{"cEnableDeuteronQA", true, "nTPC PID De"}; Configurable cEnableHeliumQA{"cEnableHeliumQA", true, "nTPC PID He"}; @@ -284,10 +298,14 @@ struct nucleiInJets { void init(o2::framework::InitContext&) { + if (isMC) { + o2::aod::ITSResponse::setMCDefaultParameters(); + } + if (doprocessJetTracksData && doprocessJetTracksDataLfPid) { LOGP(fatal, "only one process function should be enabled!!!"); } - if (!(mcClosureTrainingFraction >= 0.f && mcClosureTrainingFraction <= 1.f)) { + if (!(mcClosureTrainingFraction >= 0.f) || !(mcClosureTrainingFraction <= 1.f)) { LOGP(fatal, "mcClosureTrainingFraction must be between 0 and 1, inclusive"); } if (mcClosureSplitSeed == 0) { @@ -308,6 +326,8 @@ struct nucleiInJets { const AxisSpec dedxAxis{binsdEdx, "d#it{E}/d#it{x} A.U."}; const AxisSpec vzAxis{300, -15.f, 15.f, "Vz (cm)"}; const AxisSpec EtaAxis{40, -1.f, 1.f, "#eta"}; + const AxisSpec RapidityQAAxis{60, -2.f, 2.f, "y"}; + const AxisSpec PtRapidityQAAxis{50, 0.f, 10.f, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec ParticleOriginAxis{3, 0.5, 3.5, "origin: 1 primary, 2 secondary/material, 3 weak decay"}; const AxisSpec betaAxis{binsBeta, "TOF #beta"}; @@ -346,6 +366,31 @@ struct nucleiInJets { jetHist.add("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt", "pT(d) vs NSigmaTPC (d) vs centrality; #it{p}_{T} (GeV/#it{c}); NSigmaTPC; centrality", HistType::kTH3F, {PtAxis, {200, -10, 10}, CentAxis}); jetHist.add("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt", "pT(#bar{d}) vs NSigmaTPC (#bar{d}) vs centrality; #it{p}_{T} (GeV/#it{c}); NSigmaTPC; centrality", HistType::kTH3F, {PtAxis, {200, -10, 10}, CentAxis}); + // Compare lab and shifted rapidity before the rapidity selection, for pO QA. + if (isPOCollision) { + for (const auto& sample : {std::string("tracksInc"), std::string("recInc")}) { + for (const auto& species : {std::string("proton"), std::string("antiproton"), std::string("deuteron"), std::string("antideuteron")}) { + auto base = sample; + base += "/qa/rapidity/"; + base += species; + const bool isMcTruth = sample == "recInc"; + const char* yLabTitle = isMcTruth ? "matched primary MC truth; #it{p}_{T} (GeV/#it{c}); #eta; y_{lab}" : "inclusive data; #it{p}_{T} (GeV/#it{c}); #eta; y_{lab}"; + const char* yCmsTitle = isMcTruth ? "matched primary MC truth; #it{p}_{T} (GeV/#it{c}); #eta; y_{CMS}" : "inclusive data; #it{p}_{T} (GeV/#it{c}); #eta; y_{CMS}"; + jetHist.add(base + "YLabVsPtEta", yLabTitle, HistType::kTH3F, {PtRapidityQAAxis, EtaAxis, RapidityQAAxis}); + jetHist.add(base + "YCmsVsPtEta", yCmsTitle, HistType::kTH3F, {PtRapidityQAAxis, EtaAxis, RapidityQAAxis}); + } + } + } + + if (itsPid.applyItsPidInc) { + jetHist.add("tracksInc/qa/h2ITSNsigmaProtonVsPt", "inclusive proton ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(p)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("tracksInc/qa/h2ITSNsigmaDeuteronVsPt", "inclusive deuteron ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(d)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("tracksInc/qa/h2ITSNsigmaHeliumVsPt", "inclusive helium ITS PID before veto; #it{p}_{T}/|z| (GeV/#it{c}); n#sigma_{ITS}(He)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaProtonVsPt", "reconstructed MC proton ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(p)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaDeuteronVsPt", "reconstructed MC deuteron ITS PID before veto; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(d)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + jetHist.add("recInc/qa/h2ITSNsigmaHeliumVsPt", "reconstructed MC helium ITS PID before veto; #it{p}_{T}/|z| (GeV/#it{c}); n#sigma_{ITS}(He)", HistType::kTH2F, {PtAxis, {200, -10, 10}}); + } + // jet property jetHist.add("jet/h1JetPt", "jet_{p_{T}}", kTH1F, {PtJetAxis}); jetHist.add("jet/h1JetPtBkgSub", "jet_{p_{T}} background subtracted", kTH1F, {PtJetAxis}); @@ -988,10 +1033,7 @@ struct nucleiInJets { bool isOccupancyAccepted(const coll& collision) { auto occupancy{collision.trackOccupancyInTimeRange()}; - if (occupancy < cfgOccupancyRange->at(0) || occupancy > cfgOccupancyRange->at(1)) - return false; - else - return true; + return !(occupancy < cfgOccupancyRange->at(0) || occupancy > cfgOccupancyRange->at(1)); } int nEvents = 0; @@ -2078,21 +2120,36 @@ struct nucleiInJets { continue; } - auto rapidityData = [&](float m2z) { - return isRapiditySelectedForPID(trk.rapidity(m2z)); - }; + double nSigmaITSPr = 0.0; + double nSigmaITSDe = 0.0; + double nSigmaITSHe = 0.0; + if (itsPid.applyItsPidInc) { + nSigmaITSPr = o2::aod::ITSResponse::nSigmaITS(trk); + nSigmaITSDe = o2::aod::ITSResponse::nSigmaITS(trk); + nSigmaITSHe = o2::aod::ITSResponse::nSigmaITS(trk); + } + const bool passItsPidPr = !itsPid.applyItsPidInc || trk.pt() >= itsPid.ptMaxItsPidProt || (nSigmaITSPr >= itsPid.nSigmaItsMin && nSigmaITSPr <= itsPid.nSigmaItsMax); + const bool passItsPidDe = !itsPid.applyItsPidInc || trk.pt() >= itsPid.ptMaxItsPidDeut || (nSigmaITSDe >= itsPid.nSigmaItsMin && nSigmaITSDe <= itsPid.nSigmaItsMax); + const bool passItsPidHe = !itsPid.applyItsPidInc || 2.0 * trk.pt() >= itsPid.ptMaxItsPidHel || (nSigmaITSHe >= itsPid.nSigmaItsMin && nSigmaITSHe <= itsPid.nSigmaItsMax); - auto prRapidityWithinRange = rapidityData(o2::constants::physics::MassProton); - auto deRapidityWithinRange = rapidityData(o2::constants::physics::MassDeuteron); + const double yLabPr = trk.rapidity(o2::constants::physics::MassProton); + const double yLabDe = trk.rapidity(o2::constants::physics::MassDeuteron); + auto prRapidityWithinRange = isRapiditySelectedForPID(yLabPr); + auto deRapidityWithinRange = isRapiditySelectedForPID(yLabDe); if (std::fabs(trk.eta()) > cfgtrkMaxEta) continue; - bool hasTOF = trk.hasTOF(); + if (itsPid.applyItsPidInc) { + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaProtonVsPt"), trk.pt(), nSigmaITSPr); + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaDeuteronVsPt"), trk.pt(), nSigmaITSDe); + jetHist.fill(HIST("tracksInc/qa/h2ITSNsigmaHeliumVsPt"), 2.0 * trk.pt(), nSigmaITSHe); + } + bool hasTOF = trk.hasTOF(); if (trk.sign() > 0) { // particles - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("tracksInc/dcaxy/rec/proton/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { @@ -2104,7 +2161,7 @@ struct nucleiInJets { } } // proton - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/deuteron/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { @@ -2128,7 +2185,7 @@ struct nucleiInJets { } } // Triton - if (std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/helium/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaHe()) < cfgnTPCPIDHeTOF) { @@ -2142,7 +2199,7 @@ struct nucleiInJets { } else { // antiparticles - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiProton/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { @@ -2154,7 +2211,7 @@ struct nucleiInJets { } } // proton - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiDeuteron/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { @@ -2178,7 +2235,7 @@ struct nucleiInJets { } } // Triton - if (std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracksInc/dcaxy/rec/antiHelium/tpcPtVsDcaxy3D"), trk.pt(), centrality, trk.dcaXY()); if (useTOFVeto && hasTOF) { if (std::abs(trk.tofNSigmaHe()) < cfgnTPCPIDHeTOF) { @@ -2198,38 +2255,52 @@ struct nucleiInJets { if (std::fabs(trk.dcaXY()) > dcaXYPtDepCut(trk.pt()) && useDcaxyPtDepCut) continue; + if (isPOCollision) { + if (trk.sign() > 0) { + jetHist.fill(HIST("tracksInc/qa/rapidity/protonYLabVsPtEta"), trk.pt(), trk.eta(), yLabPr); + jetHist.fill(HIST("tracksInc/qa/rapidity/protonYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabPr)); + jetHist.fill(HIST("tracksInc/qa/rapidity/deuteronYLabVsPtEta"), trk.pt(), trk.eta(), yLabDe); + jetHist.fill(HIST("tracksInc/qa/rapidity/deuteronYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabDe)); + } else { + jetHist.fill(HIST("tracksInc/qa/rapidity/antiprotonYLabVsPtEta"), trk.pt(), trk.eta(), yLabPr); + jetHist.fill(HIST("tracksInc/qa/rapidity/antiprotonYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabPr)); + jetHist.fill(HIST("tracksInc/qa/rapidity/antideuteronYLabVsPtEta"), trk.pt(), trk.eta(), yLabDe); + jetHist.fill(HIST("tracksInc/qa/rapidity/antideuteronYCmsVsPtEta"), trk.pt(), trk.eta(), rapidityInSelectedFrame(yLabDe)); + } + } + if (trk.sign() > 0) { // particle info if (useTOFNsigmaPreSel && trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else if (!useTOFNsigmaPreSel && !useTOFVeto) { - if (!useRapidityCutForPID || prRapidityWithinRange) { + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } - if (!useRapidityCutForPID || deRapidityWithinRange) { + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else if (!useTOFNsigmaPreSel && useTOFVeto) { if (trk.hasTOF()) { if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF) { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } } else { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/proton/h3PtVsProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } if (trk.hasTOF()) { if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF) { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/deuteron/h3PtVsDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } @@ -2247,12 +2318,12 @@ struct nucleiInJets { jetHist.fill(HIST("tracksInc/deuteron/h2TofNsigmaDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); } } else { - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/h2TOFmassProtonVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/h2TOFmass2ProtonVsPt"), massTOF * massTOF - MassProton * MassProton, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/h2TofNsigmaProtonVsPt"), trk.tofNSigmaPr(), trk.pt(), centrality); } - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/h2TOFmassDeuteronVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/h2TOFmass2DeuteronVsPt"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/h2TofNsigmaDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); @@ -2260,41 +2331,41 @@ struct nucleiInJets { } } - if (cEnableProtonQA && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (cEnableProtonQA && passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/proton/dca/after/hDCAxyVsPtProton"), trk.dcaXY(), trk.pt(), centrality); jetHist.fill(HIST("tracksInc/proton/dca/after/hDCAzVsPtProton"), trk.dcaZ(), trk.pt(), centrality); } - if (cEnableDeuteronQA && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (cEnableDeuteronQA && passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/deuteron/dca/after/hDCAxyVsPtDeuteron"), trk.dcaXY(), trk.pt(), centrality); jetHist.fill(HIST("tracksInc/deuteron/dca/after/hDCAzVsPtDeuteron"), trk.dcaZ(), trk.pt(), centrality); } } else { // anti-particle info if (useTOFNsigmaPreSel && trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } else if (!useTOFNsigmaPreSel && !useTOFVeto) { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } else if (!useTOFNsigmaPreSel && useTOFVeto) { if (trk.hasTOF()) { - if (std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } } else { - if (!useRapidityCutForPID || prRapidityWithinRange) + if (passItsPidPr && (!useRapidityCutForPID || prRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiProton/h3PtVsantiProtonNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaPr(), centrality); } if (trk.hasTOF()) { - if (std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tofNSigmaDe()) < cfgnTPCPIDDeTOF && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } else { - if (!useRapidityCutForPID || deRapidityWithinRange) + if (passItsPidDe && (!useRapidityCutForPID || deRapidityWithinRange)) jetHist.fill(HIST("tracksInc/antiDeuteron/h3PtVsantiDeuteronNSigmaTPCVsPt"), trk.pt(), trk.tpcNSigmaDe(), centrality); } } @@ -2312,12 +2383,12 @@ struct nucleiInJets { jetHist.fill(HIST("tracksInc/antiDeuteron/h2TofNsigmaantiDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); } } else { - if (std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { + if (passItsPidPr && std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr && (!useRapidityCutForPID || prRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiProton/h2TOFmassantiProtonVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiProton/h2TOFmass2antiProtonVsPt"), massTOF * massTOF - MassProton * MassProton, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiProton/h2TofNsigmaantiProtonVsPt"), trk.tofNSigmaPr(), trk.pt(), centrality); } - if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { + if (passItsPidDe && std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe && (!useRapidityCutForPID || deRapidityWithinRange)) { jetHist.fill(HIST("tracksInc/antiDeuteron/h2TOFmassantiDeuteronVsPt"), massTOF, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiDeuteron/h2TOFmass2antiDeuteronVsPt"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), centrality); jetHist.fill(HIST("tracksInc/antiDeuteron/h2TofNsigmaantiDeuteronVsPt"), trk.tofNSigmaDe(), trk.pt(), centrality); @@ -2692,7 +2763,7 @@ struct nucleiInJets { int origin = 4; if (track.has_mcParticle()) { const auto mcTrack = track.mcParticle_as(); - if (mcTrack.pdgCode() == -2212) + if (mcTrack.pdgCode() == PDG_t::kProtonBar) origin = getParticleOriginType(mcTrack); } const bool tofPassed = completeTrack.hasTOF() && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; @@ -3253,11 +3324,28 @@ struct nucleiInJets { if (!isTrackSelectedWithoutDcaxy(track)) { continue; } - if (!track.has_mcParticle()) - continue; if (std::fabs(track.eta()) > cfgtrkMaxEta) continue; + double nSigmaITSPr = 0.0; + double nSigmaITSDe = 0.0; + double nSigmaITSHe = 0.0; + if (itsPid.applyItsPidInc) { + nSigmaITSPr = o2::aod::ITSResponse::nSigmaITS(track); + nSigmaITSDe = o2::aod::ITSResponse::nSigmaITS(track); + nSigmaITSHe = o2::aod::ITSResponse::nSigmaITS(track); + } + const bool passItsPidPr = !itsPid.applyItsPidInc || track.pt() >= itsPid.ptMaxItsPidProt || (nSigmaITSPr >= itsPid.nSigmaItsMin && nSigmaITSPr <= itsPid.nSigmaItsMax); + const bool passItsPidDe = !itsPid.applyItsPidInc || track.pt() >= itsPid.ptMaxItsPidDeut || (nSigmaITSDe >= itsPid.nSigmaItsMin && nSigmaITSDe <= itsPid.nSigmaItsMax); + const bool passItsPidHe = !itsPid.applyItsPidInc || 2.0 * track.pt() >= itsPid.ptMaxItsPidHel || (nSigmaITSHe >= itsPid.nSigmaItsMin && nSigmaITSHe <= itsPid.nSigmaItsMax); + if (itsPid.applyItsPidInc) { + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaProtonVsPt"), track.pt(), nSigmaITSPr); + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaDeuteronVsPt"), track.pt(), nSigmaITSDe); + jetHist.fill(HIST("recInc/qa/h2ITSNsigmaHeliumVsPt"), 2.0 * track.pt(), nSigmaITSHe); + } + if (!track.has_mcParticle()) + continue; + auto mcTrack = track.mcParticle_as(); // require mc getProcess to get Decay and Material secondaries @@ -3281,7 +3369,7 @@ struct nucleiInJets { // Fill DCAxy histograms jetHist.fill(HIST("recInc/dcaxy/rec/tpcPtVsDcaxy3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality, track.dcaXY(), particleOriginType); - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) jetHist.fill(HIST("recInc/dcaxy/rec/tpcPtVsDcaxy3DPIDselected"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality, track.dcaXY(), particleOriginType); if (!isMcPrimary) @@ -3293,16 +3381,36 @@ struct nucleiInJets { if (std::fabs(track.dcaXY()) > dcaXYPtDepCut(track.pt()) && useDcaxyPtDepCut) continue; + if (isPOCollision && isMcPrimary) { + if (mcTrack.pdgCode() == PDG_t::kProton) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/protonYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/protonYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == PDG_t::kProtonBar) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/antiprotonYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/antiprotonYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == Pdg::kDeuteron) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/deuteronYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/deuteronYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } else if (mcTrack.pdgCode() == -Pdg::kDeuteron) { + const double yLab = mcTrack.y(); + jetHist.fill(HIST("recInc/qa/rapidity/antideuteronYLabVsPtEta"), mcTrack.pt(), mcTrack.eta(), yLab); + jetHist.fill(HIST("recInc/qa/rapidity/antideuteronYCmsVsPtEta"), mcTrack.pt(), mcTrack.eta(), rapidityInSelectedFrame(yLab)); + } + } + // auto mass = TDatabasePDG::Instance()->GetParticle(abs(mcTrack.pdgCode()))->Mass(); // auto rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, mass); if (!isRapiditySelectedForPID(mcTrack.y())) continue; // Proton - if (std::abs(mcTrack.pdgCode()) == 2212) { // Proton + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { // Proton jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } @@ -3311,7 +3419,7 @@ struct nucleiInJets { jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } @@ -3320,49 +3428,49 @@ struct nucleiInJets { jetHist.fill(HIST("recInc/eff/tpcTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (track.hasTOF()) jetHist.fill(HIST("recInc/eff/tpcTofTrack3D"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } // TPCTOF and TPCTOFVeto histograms // Proton - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr && track.hasTOF()) { - if (std::abs(mcTrack.pdgCode()) == 2212) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr && track.hasTOF()) { + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaPr()) < cfgnTPCPIDPrTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { - if (std::abs(mcTrack.pdgCode()) == 2212) { + if (passItsPidPr && std::abs(track.tpcNSigmaPr()) < cfgnTPCPIDPr) { + if (std::abs(mcTrack.pdgCode()) == PDG_t::kProton) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } } // Deuteron - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe && track.hasTOF()) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe && track.hasTOF()) { if (std::abs(mcTrack.pdgCode()) == Pdg::kDeuteron) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaDe()) < cfgnTPCPIDDeTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { + if (passItsPidDe && std::abs(track.tpcNSigmaDe()) < cfgnTPCPIDDe) { if (std::abs(mcTrack.pdgCode()) == Pdg::kDeuteron) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } } // Helium - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe && track.hasTOF()) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe && track.hasTOF()) { if (std::abs(mcTrack.pdgCode()) == Pdg::kHelium3) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); if (std::abs(track.tofNSigmaHe()) < cfgnTPCPIDHeTOF) jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } } else { - if (std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (passItsPidHe && std::abs(track.tpcNSigmaHe()) < cfgnTPCPIDHe) { if (std::abs(mcTrack.pdgCode()) == Pdg::kHelium3) { jetHist.fill(HIST("recInc/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode()), centrality); } From de095ddfcb70dcfe9b3ccfeb9d191d3c071398d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Nov 2026 13:05:31 +0100 Subject: [PATCH 621/648] [Common] Fix unused variables, parameters, values (#18638) --- Common/Core/macros/testCollisionTypeHelper.C | 2 +- Common/TableProducer/PID/pidTOFBase.cxx | 2 +- Common/TableProducer/PID/pidTOFMerge.cxx | 2 +- Common/TableProducer/caloClusterProducer.cxx | 12 ++++++------ Common/TableProducer/match-mft-ft0.cxx | 2 +- Common/Tools/Multiplicity/macros/runCalibration.C | 2 +- Common/Tools/Multiplicity/macros/runGlauberFit.C | 4 ++-- Common/Tools/Multiplicity/macros/saveCorrelation.C | 1 + 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Common/Core/macros/testCollisionTypeHelper.C b/Common/Core/macros/testCollisionTypeHelper.C index f84970a46a3..345fc01ceaa 100644 --- a/Common/Core/macros/testCollisionTypeHelper.C +++ b/Common/Core/macros/testCollisionTypeHelper.C @@ -26,5 +26,5 @@ void testCollisionTypeHelper(int runNumber = 544124) o2::parameters::GRPLHCIFData* grpo = ccdb.getSpecificForRun("GLO/Config/GRPLHCIF", runNumber); grpo->print(); - int collsys = o2::common::core::CollisionSystemType::getCollisionTypeFromGrp(grpo); + [[maybe_unused]] int collsys = o2::common::core::CollisionSystemType::getCollisionTypeFromGrp(grpo); } diff --git a/Common/TableProducer/PID/pidTOFBase.cxx b/Common/TableProducer/PID/pidTOFBase.cxx index 257dbf5b6b9..733da06b1ea 100644 --- a/Common/TableProducer/PID/pidTOFBase.cxx +++ b/Common/TableProducer/PID/pidTOFBase.cxx @@ -469,7 +469,7 @@ struct tofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (removeTOFEvTimeBias) { evTimeTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); diff --git a/Common/TableProducer/PID/pidTOFMerge.cxx b/Common/TableProducer/PID/pidTOFMerge.cxx index 8b1486f94d2..337e828630c 100644 --- a/Common/TableProducer/PID/pidTOFMerge.cxx +++ b/Common/TableProducer/PID/pidTOFMerge.cxx @@ -435,7 +435,7 @@ struct tofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (kRemoveTOFEvTimeBias) { evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); diff --git a/Common/TableProducer/caloClusterProducer.cxx b/Common/TableProducer/caloClusterProducer.cxx index 5227b133335..c7707eb3652 100644 --- a/Common/TableProducer/caloClusterProducer.cxx +++ b/Common/TableProducer/caloClusterProducer.cxx @@ -1116,9 +1116,9 @@ struct CaloClusterProducer { if (cpvdist != 99.) { // was evaluated cpvdist = std::sqrt(cpvdist); // was squared } - if (trackdist != 99.) { // was evaluated - trackdist = std::sqrt(trackdist); // was squared - } + // if (trackdist != 99.) { // was evaluated + // trackdist = std::sqrt(trackdist); // was squared // FIXME: unused + // } float lambdaShort = 0., lambdaLong = 0.; clu.getElipsAxis(lambdaShort, lambdaLong); @@ -1544,9 +1544,9 @@ struct CaloClusterProducer { if (cpvdist != 99.) { // was evaluated cpvdist = std::sqrt(cpvdist); // was squared } - if (trackdist != 99.) { // was evaluated - trackdist = std::sqrt(trackdist); // was squared - } + // if (trackdist != 99.) { // was evaluated + // trackdist = std::sqrt(trackdist); // was squared // FIXME: unused + // } float lambdaShort = 0., lambdaLong = 0.; clu.getElipsAxis(lambdaShort, lambdaLong); diff --git a/Common/TableProducer/match-mft-ft0.cxx b/Common/TableProducer/match-mft-ft0.cxx index d282c75c6fe..05bd98f6952 100644 --- a/Common/TableProducer/match-mft-ft0.cxx +++ b/Common/TableProducer/match-mft-ft0.cxx @@ -367,7 +367,7 @@ struct matchmftft0 { auto ft0s = bc.ft0s(); i = 0; // reinitialise - D = 0.0; + // D = 0.0; // FIXME: unused minD = 999.9; for (auto const& ft0 : ft0s) { // printf("---------ft0.bcId %d\n", ft0.bcId()); diff --git a/Common/Tools/Multiplicity/macros/runCalibration.C b/Common/Tools/Multiplicity/macros/runCalibration.C index 1e1d4f96b5f..2264df723db 100644 --- a/Common/Tools/Multiplicity/macros/runCalibration.C +++ b/Common/Tools/Multiplicity/macros/runCalibration.C @@ -40,7 +40,7 @@ void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNB TH1F* hData = dynamic_cast(file->Get("hV0MUltraFine")); TH1F* hGlauberParameters = dynamic_cast(file->Get("hGlauberParameters")); - TH1F* hGlauberFitRange = dynamic_cast(file->Get("hGlauberFitRange")); + [[maybe_unused]] TH1F* hGlauberFitRange = dynamic_cast(file->Get("hGlauberFitRange")); hData->SetName("hData"); TH1F* hStitched = dynamic_cast(hData->Clone("hStitched")); TH1F* hFit = dynamic_cast(file->Get("hGlauber")); diff --git a/Common/Tools/Multiplicity/macros/runGlauberFit.C b/Common/Tools/Multiplicity/macros/runGlauberFit.C index 9524985b1ff..ea4b33c8a48 100644 --- a/Common/Tools/Multiplicity/macros/runGlauberFit.C +++ b/Common/Tools/Multiplicity/macros/runGlauberFit.C @@ -357,8 +357,8 @@ int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", c1->cd(1); TLatex* lat = new TLatex(); lat->SetNDC(); - Float_t lPosText = 0.76; - Float_t lYShift = 0.25; + // Float_t lPosText = 0.76; // FIXME: unused + // Float_t lYShift = 0.25; // FIXME: unused lat->SetTextSize(0.042); // save the glauber parameters explicitly diff --git a/Common/Tools/Multiplicity/macros/saveCorrelation.C b/Common/Tools/Multiplicity/macros/saveCorrelation.C index 04b27fc6c1b..ed0376fab54 100644 --- a/Common/Tools/Multiplicity/macros/saveCorrelation.C +++ b/Common/Tools/Multiplicity/macros/saveCorrelation.C @@ -59,6 +59,7 @@ void saveCorrelation(const TString& filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0- // 2D correlation plot necessary for Glauber + NBD fitting // The provided range should be enough for Pb-Pb + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores,clang-diagnostic-unused-variable) TH2D* hNpNc = new TH2D("hNpNc", "", 500, -0.5, 499.5, 2500, -0.5, 2499.5); // let's draw this on screen for inspection From 960a52975878f2f4779d2a7a6375f01ae390a8fc Mon Sep 17 00:00:00 2001 From: Maria Calmon Behling <115636585+calmonbehling@users.noreply.github.com> Date: Mon, 9 Nov 2026 13:29:43 +0100 Subject: [PATCH 622/648] [PWGLF] updated primary and secondary selections, fix multiplicity determination, include histos for decay mothers (#18625) Co-authored-by: Maria Calmon Behling --- .../Nuspex/particleCompositionCorrection.cxx | 153 +++++++---- PWGLF/Tasks/Nuspex/pccQa.cxx | 243 +++++++++++++++--- 2 files changed, 304 insertions(+), 92 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx index b2f5e201476..d9e868afca9 100644 --- a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx +++ b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx @@ -38,6 +38,8 @@ #include #include +// #include "Framework/Logger.h" + using namespace o2; using namespace o2::framework; using namespace o2::ml; @@ -64,6 +66,7 @@ struct ParticleCompositionCorrection { Configurable skipAll{"skipAll", false, "run table producer in dummy mode, i.e. skip all computations and fill with 1"}; Configurable skipSec{"skipSec", false, "dont calculate weights for secondaries"}; + Configurable skipNeutralSec{"skipNeutralSec", true, "dont calculate weights for neutral secondaries"}; Configurable skipNonPhysicalPrim{"skipNonPhysicalPrim", true, "dont calculate weights for particles that are not (originating from) physical primaries; i.e. reject (decays of) pi0 etc."}; Configurable etaCut{"etaCut", 0.8f, "eta cut"}; Configurable ptMinCut{"ptMinCut", 0.15f, "pt min cut"}; @@ -118,6 +121,7 @@ void ParticleCompositionCorrection::init(InitContext const&) const int maxMult = 100; const int nBinsMult = maxMult + 1; const AxisSpec multAxis = {nBinsMult, -0.5, nBinsMult - 0.5, "#it{N}_{ch}", "mult"}; + const AxisSpec processAxis = {51, -0.5, 50.5, "process", "process"}; histos.add("multDist_INELg0", "", kTH1D, {multAxis}); histos.add("multDist_fid", "", kTH1D, {multAxis}); @@ -127,25 +131,15 @@ void ParticleCompositionCorrection::init(InitContext const&) histos.add("frac/data/proton", "", kTProfile, {ptAxis}); histos.add("frac/data/sigma", "", kTProfile, {ptAxis}); - histos.add("frac/data/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("frac/data/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("frac/data/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.addClone("frac/data/", "frac/mc/"); histos.add("weight/kaon", "", kTProfile, {ptAxis}); histos.add("weight/proton", "", kTProfile, {ptAxis}); histos.add("weight/sigma", "", kTProfile, {ptAxis}); - histos.add("weight/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); - + histos.add("weight/prim", "", kTProfile, {ptAxis}); histos.add("weight/secDec", "", kTProfile, {ptAxis}); histos.add("weight/secMat", "", kTProfile, {ptAxis}); - - histos.add("weight/secDec_mult", "", kTProfile2D, {multAxis, ptAxis}); - histos.add("weight/secMat_mult", "", kTProfile2D, {multAxis, ptAxis}); } } @@ -153,21 +147,48 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M { static const std::tuple noWeights = {1.f, 1.f, 1.f}; - if (skipAll || std::abs(particle.eta()) > etaCut || particle.pt() < ptMinCut || particle.pt() > ptMaxCut) { + static constexpr int kXi0PDG = 3322; + + /* if (skipAll || std::abs(particle.eta()) > etaCut || particle.pt() < ptMinCut || particle.pt() > ptMaxCut) { + return noWeights; + } */ + + if (skipAll) { return noWeights; } - if (particle.producedByGenerator()) { - if (skipNonPhysicalPrim && !particle.isPhysicalPrimary()) { - return noWeights; - } + if (!particle.has_mothers()) { + return noWeights; + } + + auto particleMother = particle.mothersIds()[0] - particles.offset(); + + // if (particle.producedByGenerator()) { + + if (particleMother == 0. || particleMother == 1. || + (particle.isPhysicalPrimary() && skipNonPhysicalPrim)) { + auto absPDGCode = std::abs(particle.pdgCode()); - // translate abs PDG code to PID variable of neural networks (0: pion, 1: kaon, 2: proton, 3: sigma) if (absPDGCode == PDG_t::kPiPlus || absPDGCode == PDG_t::kPi0) { + + return noWeights; + } + + if (skipNonPhysicalPrim && !particle.isPhysicalPrimary()) { return noWeights; } + if (!particle.has_daughters()) { + + if (particle.eta() > etaCut || particle.pt() > ptMaxCut || particle.pt() < ptMinCut) { + + return noWeights; + } + } + + // translate abs PDG code to PID variable of neural networks (0: pion, 1: kaon, 2: proton, 3: sigma) + static const std::map mapPID = { {PDG_t::kPiPlus, 0.f}, {PDG_t::kPi0, 0.f}, @@ -181,79 +202,106 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M {PDG_t::kLambda0, 3.f}, {PDG_t::kSigma0, 3.f}, {PDG_t::kXiMinus, 3.f}, - // TODO: potentially extend by xi0/eta/omega/rho/phi/Delta... + {kXi0PDG, 3.f}, // Xi0 + {PDG_t::kOmegaMinus, 3.f}, + // TODO: potentially extend by eta/omega/rho/phi/Delta... // pdg codes defined in AliceO2/Common/Constants/include/CommonConstants/PhysicsConstants.h // e.g. o2::constants::physics::Pdg::kEta }; if (auto iterMapPID = mapPID.find(absPDGCode); iterMapPID != mapPID.end()) { - // LOGP(info, "scaling a {} with status code {} from process {}", particle.pdgCode(), particle.getGenStatusCode(), particle.getProcess()); - float pt = particle.pt(); // calculate particle fractions and corresponding weight for given reference particle - std::vector> input = {{dNdEta}, {pt}, {iterMapPID->second}}; + std::vector> input = {{dNdEta}, {particle.pt()}, {iterMapPID->second}}; float fracData = particleFractionsData.evalModel(input)[0]; float fracMC = particleFractionsMC.evalModel(input)[0]; - float weight = (fracMC) ? fracData / fracMC : 1.f; + float weight = (fracMC != 0.f) ? fracData / fracMC : 1.f; std::tuple weights = {weight, weight, weight}; if (!skipSec && particle.has_daughters()) { storedWeights[particle.index()] = weights; } - if (enableQAHistos && particle.isPhysicalPrimary() && std::abs(particle.eta()) < 0.8) { // o2-linter: disable=magic-number (usual range of charged-partilce measurements) + if (enableQAHistos && particle.isPhysicalPrimary() && std::abs(particle.eta()) < etaCut) { if (iterMapPID->first == PDG_t::kKPlus) { - histos.fill(HIST("frac/data/kaon"), pt, fracData); - histos.fill(HIST("frac/mc/kaon"), pt, fracMC); - histos.fill(HIST("frac/data/kaon_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/kaon_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/kaon"), pt, weight); - histos.fill(HIST("weight/kaon_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/kaon"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/kaon"), particle.pt(), fracMC); + histos.fill(HIST("weight/kaon"), particle.pt(), weight); } if (iterMapPID->first == PDG_t::kProton) { - histos.fill(HIST("frac/data/proton"), pt, fracData); - histos.fill(HIST("frac/mc/proton"), pt, fracMC); - histos.fill(HIST("frac/data/proton_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/proton_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/proton"), pt, weight); - histos.fill(HIST("weight/proton_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/proton"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/proton"), particle.pt(), fracMC); + histos.fill(HIST("weight/proton"), particle.pt(), weight); } if (iterMapPID->first == PDG_t::kSigmaPlus || iterMapPID->first == PDG_t::kSigmaMinus) { - histos.fill(HIST("frac/data/sigma"), pt, fracData); - histos.fill(HIST("frac/mc/sigma"), pt, fracMC); - histos.fill(HIST("frac/data/sigma_mult"), dNdEta, pt, fracData); - histos.fill(HIST("frac/mc/sigma_mult"), dNdEta, pt, fracMC); - histos.fill(HIST("weight/sigma"), pt, weight); - histos.fill(HIST("weight/sigma_mult"), dNdEta, pt, weight); + histos.fill(HIST("frac/data/sigma"), particle.pt(), fracData); + histos.fill(HIST("frac/mc/sigma"), particle.pt(), fracMC); + histos.fill(HIST("weight/sigma"), particle.pt(), weight); } + + histos.fill(HIST("weight/prim"), particle.pt(), weight); } + return weights; } + } else if (!skipSec) { + auto refParticleID = particle.index(); - // LOGP(error, "Particle [{}] {} from process {}", refParticleID, particle.pdgCode(), particle.getProcess()); - while (!particles.iteratorAt(refParticleID).producedByGenerator() && particles.iteratorAt(refParticleID).has_mothers()) { + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + + if (!pdgParticle) { + // LOGP(info, "Unknown PDG code: {}", particle.pdgCode()); + // return noWeights; + } else { + + if (skipNeutralSec && pdgParticle->Charge() == 0.) { + return noWeights; + } + } + + if (particle.eta() > etaCut || particle.pt() > ptMaxCut || particle.pt() < ptMinCut) { + return noWeights; + } + + while (particles.iteratorAt(refParticleID).has_mothers()) { + auto motherID = particles.iteratorAt(refParticleID).mothersIds()[0] - particles.offset(); - // LOGP(error, "-> mom [{}] {} from process {}", motherID, particles.iteratorAt(motherID).pdgCode(), particles.iteratorAt(motherID).getProcess()); + + auto mom = particles.iteratorAt(motherID); + + if (!mom.has_mothers()) { + break; + } + refParticleID = motherID; + + if (mom.isPhysicalPrimary() && skipNonPhysicalPrim) { + break; + } } - if (storedWeights.find(refParticleID) == storedWeights.end()) { - // LOGP(error, " no ref particle stored for particle {} from process {}!!", particle.pdgCode(), particle.getProcess()); + if (refParticleID == 0 || refParticleID == 1) { + LOGF(error, "GLOBAL MOTHER IS INCIDING PROTON"); + } + + if (!storedWeights.contains(refParticleID)) { + // LOGP(info, "MCB: no ref stored for mom {}, PDG: {}!!", particles.iteratorAt(refParticleID).index(),particles.iteratorAt(refParticleID).pdgCode()); return noWeights; } if (enableQAHistos) { + float weight = get<0>(storedWeights.at(refParticleID)); - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle && pdgParticle->Charge() != 0.) { - if (particle.getProcess() == TMCProcess::kPDecay) { + if (particle.getProcess() == TMCProcess::kPDecay || particle.getProcess() == TMCProcess::kPPrimary) { histos.fill(HIST("weight/secDec"), particle.pt(), weight); - histos.fill(HIST("weight/secDec_mult"), dNdEta, particle.pt(), weight); - } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { + // } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { + } else { histos.fill(HIST("weight/secMat"), particle.pt(), weight); - histos.fill(HIST("weight/secMat_mult"), dNdEta, particle.pt(), weight); } } } + return storedWeights.at(refParticleID); } return noWeights; @@ -266,6 +314,7 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, float dNdEtafid = 0.f; for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { continue; } @@ -288,6 +337,8 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, histos.fill(HIST("multDist_fidVsINELg0"), dNdEta, dNdEtafid); } + // LOGP(info, "MCB: ------- New event with dNdeta = {} in |eta| < 0.8 -------", dNdEtafid); + std::map> storedWeights; for (const auto& particle : particles) { auto [weight, weightSysUp, weightSysDown] = getWeights(particles, particle, storedWeights, dNdEta); diff --git a/PWGLF/Tasks/Nuspex/pccQa.cxx b/PWGLF/Tasks/Nuspex/pccQa.cxx index fe04b0ffc4e..4469c8cee2d 100644 --- a/PWGLF/Tasks/Nuspex/pccQa.cxx +++ b/PWGLF/Tasks/Nuspex/pccQa.cxx @@ -29,19 +29,83 @@ #include #include +#include +#include #include +#include + +#include #include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using aod::track::TrackSelectionFlags; +int getMotherCategory(int pdg) +{ + switch (std::abs(pdg)) { + case 310: + return 1; // K0S + case 130: + return 2; // K0L + case 3122: + return 3; // Lambda + + case 3222: // Sigma+ + case 3112: + return 4; // Sigma- + + case 3312: + return 5; // Xi- + + case 3334: + return 6; // Omega + + case 211: + return 7; // Pion + case 321: + return 8; // Kaon + case 2112: + return 9; // Neutron + case 13: + return 10; // Muon + + default: + return 11; // Other + } +} + +static std::string getMotherName(int category) +{ + static const std::vector names = { + "K0S", + "K0L", + "Lambda", + "SigmaCharged", + "XiCharged", + "Omega", + "Pi", + "K", + "n", + "Muon", + "Other"}; + + return names.at(category - 1); +} + struct PccQa { HistogramRegistry histos; Service pdg; + std::vector> motherDCA; + std::vector> motherDCAWeighted; + std::vector> motherWeights; + static constexpr float MaxVtxZ = 10.f; Configurable maxMult{"maxMult", 100, "max multiplicity"}; @@ -73,44 +137,76 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) void PccQa::init(InitContext const&) { - histos.add("eventCounter", "", kTH1D, {{1, 0.5, 1.5}}); - const AxisSpec dcaAxis = {1000, -1., 1., "#it{DCA}_{xy}", "dca"}; + // const AxisSpec dcaAxis = {1000, -1., 1., "#it{DCA}_{xy}", "dca"}; + const AxisSpec dcaAxis = {99, -1., 1., "#it{DCA}_{xy}", "dca"}; std::vector ptBinEdges = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0}; const AxisSpec ptAxis{ptBinEdges, "#it{p}_{T} (GeV/#it{c})", "pt"}; - const int nBinsMult = 30; - const AxisSpec nchAxis = {nBinsMult, 0., static_cast(maxMult), "#it{N}_{ch}", "nch"}; + const int nBinsMult = 25; + const AxisSpec nchAxis = {nBinsMult, 1., static_cast(maxMult), "#it{N}_{ch}", "nch"}; + + histos.add("multDist", "", kTH1D, {nchAxis}); - histos.add("DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); if (doprocessMC) { - histos.add("DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("prim/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("prim/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); - histos.add("sec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("sec/dec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/dec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/dec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/dec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - histos.add("sec/mat/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/mat/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); - - histos.add("sec/mat/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); histos.add("sec/mat/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + + histos.add("prim/weights", "", kTProfile, {ptAxis}); + histos.add("sec/weights", "", kTProfile, {ptAxis}); + histos.add("sec/dec/weights", "", kTProfile, {ptAxis}); + histos.add("sec/mat/weights", "", kTProfile, {ptAxis}); + + std::vector labels = { + "K^{0}_{S}", "K^{0}_{L}", "#Lambda", + "#Sigma^{#pm}", + "#Xi^{#pm}", + "#Omega^{#pm}", + "#pi^{#pm}", "K^{#pm}", "n", + "#mu^{#pm}", + "Other"}; + + const int nMotherCategories = static_cast(labels.size()); + + histos.add( + "sec/dec/motherPDG", + "Mother particles of decay secondaries", + kTH1D, + {{nMotherCategories, 0.5, nMotherCategories + 0.5}}); + + auto hMother = histos.get(HIST("sec/dec/motherPDG")); + + for (int i = 0; i < nMotherCategories; ++i) { + hMother->GetXaxis()->SetBinLabel(i + 1, labels[i].c_str()); + } + + for (int category = 1; category <= nMotherCategories; ++category) { + const auto mother = getMotherName(category); + + motherDCA.push_back(histos.add( + fmt::format("sec/dec/{}/DCAxyVsPt", mother), + "", kTH2D, {ptAxis, dcaAxis})); + + motherDCAWeighted.push_back(histos.add( + fmt::format("sec/dec/{}/DCAxyVsPt_weighted", mother), + "", kTH2D, {ptAxis, dcaAxis})); + + motherWeights.push_back(histos.add( + fmt::format("sec/dec/{}/weights", mother), + "", kTProfile, {ptAxis})); + } } } @@ -135,44 +231,109 @@ void PccQa::processMeas(const C& collision, const T& tracks) if ((std::abs(collision.posZ()) > MaxVtxZ) || !collision.sel8()) { return; } - histos.fill(HIST("eventCounter"), 1); + + auto MultCounterMeas = 0; + + for (const auto& track : tracks) { + + if (!TrackSelectionFlags::checkFlag(track.trackCutFlag(), TrackSelectionFlags::kGlobalTrackWoDCA)) { + continue; + } + + MultCounterMeas++; + } + + /* LOGP(info, "MCB: --------------------------"); + LOGP(info, "MCB: ++ New event with Nch = {} ",MultCounterMeas); + LOGP(info, "MCB: --------------------------"); */ + + histos.fill(HIST("multDist"), MultCounterMeas); for (const auto& track : tracks) { if (!TrackSelectionFlags::checkFlag(track.trackCutFlag(), TrackSelectionFlags::kGlobalTrackWoDCA)) { continue; } - histos.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + + histos.fill(HIST("DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); if constexpr (IS_MC) { if (!track.has_mcParticle()) { continue; } + const auto& particle = track.template mcParticle_as(); - histos.fill(HIST("DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); if (particle.isPhysicalPrimary()) { - histos.fill(HIST("prim/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("prim/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("prim/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("prim/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + + histos.fill(HIST("prim/weights"), particle.pt(), particle.pccWeight()); + histos.fill(HIST("prim/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("prim/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); + } else { - histos.fill(HIST("sec/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); - if (particle.getProcess() == TMCProcess::kPDecay) { - histos.fill(HIST("sec/dec/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/dec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/dec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/dec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); - } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { - histos.fill(HIST("sec/mat/DCAxyVsPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/mat/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); - histos.fill(HIST("sec/mat/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); - histos.fill(HIST("sec/mat/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); + + if (!particle.has_mothers()) { + continue; + } + + histos.fill(HIST("sec/weights"), particle.pt(), particle.pccWeight()); + + histos.fill(HIST("sec/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("sec/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); + + if (particle.getProcess() == TMCProcess::kPDecay || particle.getProcess() == TMCProcess::kPPrimary) { + + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult"), + MultCounterMeas, track.pt(), track.dcaXY()); + + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult_weighted"), + MultCounterMeas, track.pt(), track.dcaXY(), + particle.pccWeight()); + + histos.fill(HIST("sec/dec/weights"), + particle.pt(), particle.pccWeight()); + + /* LOGP(info, + " -> Particle={}, PDG={}, process={}, " + "nMoms={}, 1. mom ID={}", + particle.globalIndex(), + particle.pdgCode(), + particle.getProcess(), + particle.mothersIds().size(), + particle.mothersIds()[0]); */ + + // Identify the immediate mother + // auto mother = particle.template mothers_first_as(); + auto mother = particle.template mothers_first_as(); + + const int motherCategory = getMotherCategory(mother.pdgCode()); + // const std::string motherName = getMotherName(motherCategory); + + histos.fill( + HIST("sec/dec/motherPDG"), + motherCategory); + + const int i = motherCategory - 1; + + /* LOGP(info, + " --> Mother PDG={}", + mother.pdgCode() + ); */ + + motherDCA[i]->Fill(track.pt(), track.dcaXY()); + + motherDCAWeighted[i]->Fill( + track.pt(), track.dcaXY(), particle.pccWeight()); + + motherWeights[i]->Fill( + particle.pt(), particle.pccWeight()); + + } else { + + histos.fill(HIST("sec/mat/weights"), particle.pt(), particle.pccWeight()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult"), MultCounterMeas, track.pt(), track.dcaXY()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult_weighted"), MultCounterMeas, track.pt(), track.dcaXY(), particle.pccWeight()); } } } From 8df01f1f627ad5a7b649196b61d11cd29d7ddfee Mon Sep 17 00:00:00 2001 From: Soumyadip Ghosh Date: Mon, 9 Nov 2026 19:08:54 +0530 Subject: [PATCH 623/648] [PWGCF] Long range correlation update on MFT and added purity correction (#18648) --- .../DataModel/LongRangeDerived.h | 37 - .../TableProducer/longrangeMaker.cxx | 340 +++----- .../Tasks/longrangecorrDerived.cxx | 753 ++++++++++-------- 3 files changed, 522 insertions(+), 608 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h index a6c9e2286ce..7435a99ecfb 100644 --- a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h +++ b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h @@ -18,8 +18,6 @@ #ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ #define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" - #include "Common/DataModel/Multiplicity.h" #include @@ -76,14 +74,6 @@ DECLARE_SOA_TABLE(LRFt0cMcTracks, "AOD", "LRFT0CMCTRACK", lrcorrmctrktable::Phi); using LRFt0cMcTrack = LRFt0cMcTracks::iterator; -DECLARE_SOA_TABLE(LRMftMcTracks, "AOD", "LRMFTMCTRACK", - o2::soa::Index<>, - lrcorrmctrktable::LRMcCollisionId, - lrcorrmctrktable::Pt, - lrcorrmctrktable::Eta, - lrcorrmctrktable::Phi); -using LRMftMcTrack = LRMftMcTracks::iterator; - namespace lrcorrcolltable { DECLARE_SOA_INDEX_COLUMN(LRMcCollision, lrMcCollision); @@ -221,20 +211,6 @@ DECLARE_SOA_TABLE(LRV0Tracks, "AOD", "LRV0TRACK", lrcorrtrktable::V0Type); using LRV0Track = LRV0Tracks::iterator; -DECLARE_SOA_TABLE(LRMftTracks, "AOD", "LRMFTTRACK", - o2::soa::Index<>, - lrcorrtrktable::LRCollisionId, - lrcorrtrktable::AmbDegree, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi, - fwdtrack::NClusters, - fwdtrack::BestDCAXY, - fwdtrack::BestDCAZ, - lrcorrtrktable::IsCA, - lrcorrtrktable::IsReassigned); -using LRMftTrack = LRMftTracks::iterator; - DECLARE_SOA_TABLE(UpcLRMidTracks, "AOD", "UPCLRMIDTRACK", o2::soa::Index<>, lrcorrtrktable::UpcLRCollisionId, @@ -280,19 +256,6 @@ DECLARE_SOA_TABLE(UpcLRV0Tracks, "AOD", "UPCLRV0TRACK", lrcorrtrktable::V0Type); using UpcLRV0Track = UpcLRV0Tracks::iterator; -DECLARE_SOA_TABLE(UpcLRMftTracks, "AOD", "UPCLRMFTTRACK", - o2::soa::Index<>, - lrcorrtrktable::UpcLRCollisionId, - lrcorrtrktable::AmbDegree, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi, - fwdtrack::NClusters, - fwdtrack::BestDCAXY, - fwdtrack::BestDCAZ, - lrcorrtrktable::IsCA, - lrcorrtrktable::IsReassigned); -using UpcLRMftTrack = UpcLRMftTracks::iterator; } // namespace o2::aod #endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx index d9d528c64b0..c31496536fd 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx @@ -18,7 +18,6 @@ #include "PWGCF/Core/CorrelationContainer.h" #include "PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h" #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" #include "PWGUD/Core/SGCutParHolder.h" #include "PWGUD/Core/SGSelector.h" #include "PWGUD/Core/UDHelpers.h" @@ -53,14 +52,12 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -78,7 +75,6 @@ using namespace o2::aod::fwdtrack; using namespace o2::aod::evsel; using namespace o2::constants::math; -auto static constexpr CintZero = 0; auto static constexpr KminFt0cCell = 96; auto static constexpr TotFt0Channels = 208; auto static constexpr MinFt0aDeadChannelOuter = 60; @@ -92,8 +88,7 @@ auto static constexpr MinFt0cMirrorChannelOuter = 144; auto static constexpr MaxFt0cMirrorChannelOuter = 147; auto static constexpr MinFt0cOuterRing = 144; -AxisSpec axisEvent{20, 0.5, 20.5, "#Event", "EventAxis"}; -AxisSpec axisTrackSel{10, 0.5, 10.5, "#Track", "TrackAxis"}; +const AxisSpec axisEvent{20, 0.5, 20.5, "#Event", "EventAxis"}; auto static constexpr KminCharge = 3.0f; enum KindOfV0 { @@ -152,20 +147,6 @@ struct LongrangeMaker { Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; } cfgtrksel; - struct : ConfigurableGroup { - std::string prefix = "MftTrackSelection_group"; - Configurable cfgUseChi2Cut{"cfgUseChi2Cut", false, "Use condition on MFT track: chi2/Nclusters"}; - Configurable useMftPtCut{"useMftPtCut", true, "Choose to apply MFT track pT cut"}; - Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; - Configurable cfgMftEtaMax{"cfgMftEtaMax", -2.4f, "Maximum MFT eta cut"}; - Configurable cfgMftEtaMin{"cfgMftEtaMin", -3.6f, "Minimum MFT eta cut"}; - Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; - Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; - Configurable cfgMftPtCutMin{"cfgMftPtCutMin", 0.f, "minimum accepted MFT track pT"}; - Configurable cfgMftPtCutMax{"cfgMftPtCutMax", 10.f, "maximum accepted MFT track pT"}; - Configurable cfgMaxChi2NCl{"cfgMaxChi2NCl", 1.f, "Cut on MFT track: chi2/Nclusters"}; - } cfgmfttrksel; - struct : ConfigurableGroup { std::string prefix = "FitTrackSelection_group"; Configurable cfgFt0aEtaMax{"cfgFt0aEtaMax", 4.9f, "Maximum FT0A eta cut"}; @@ -214,7 +195,6 @@ struct LongrangeMaker { std::string prefix = "ConfigAxis_group"; ConfigurableAxis axisAmplitude{"axisAmplitude", {5000, 0, 10000}, "FT0 amplitude"}; ConfigurableAxis axisChannel{"axisChannel", {208, 0, 208}, "FT0 channel"}; - ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; ConfigurableAxis axisEta = {"axisEta", {100, -5, 5}, "eta axis"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "#phi axis"}; @@ -245,8 +225,8 @@ struct LongrangeMaker { Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.3f, "Minimum pt to use TOF N-sigma"}; Configurable isUseItsPid{"isUseItsPid", false, "Use ITS PID for particle identification"}; - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; std::vector* offsetFT0{nullptr}; @@ -261,7 +241,7 @@ struct LongrangeMaker { // Create instance of the selector class which runs the gap selection algorithm SGSelector sgSelector; // Create instance of cut holder class to contain the user defined cuts - SGCutParHolder cfgSgCuts = SGCutParHolder(); + SGCutParHolder cfgSgCuts; Configurable sgCuts{"sgCuts", {}, "SG event cuts"}; Configurable cfgGapSide{"cfgGapSide", 2, "cut on UPC events"}; @@ -302,25 +282,6 @@ struct LongrangeMaker { histos.add("hSelectionResult", "hSelectionResult", kTH1I, {{5, -0.5, 4.5}}); - histos.add("hMftTrkSel", "hMftTrkSel", kTH1D, {axisTrackSel}, false); - auto hMftTrkStat = histos.get(HIST("hMftTrkSel")); - auto* xMftTrk = hMftTrkStat->GetXaxis(); - xMftTrk->SetBinLabel(1, "All"); - xMftTrk->SetBinLabel(2, "Cluster selection"); - xMftTrk->SetBinLabel(3, "Chi2/NCluster selection"); - xMftTrk->SetBinLabel(4, "eta selection"); - xMftTrk->SetBinLabel(5, "pT selection"); - xMftTrk->SetBinLabel(6, "CA track selection"); - - histos.add("hMftBestTrkSel", "hMftBestTrkSel", kTH1D, {axisTrackSel}, false); - auto hMftBestTrkStat = histos.get(HIST("hMftBestTrkSel")); - auto* xMftBestTrk = hMftBestTrkStat->GetXaxis(); - xMftBestTrk->SetBinLabel(1, "All"); - xMftBestTrk->SetBinLabel(2, "BestSelCollID selection"); - xMftBestTrk->SetBinLabel(3, "Orphan track selection"); - xMftBestTrk->SetBinLabel(4, "DCAxy selection"); - xMftBestTrk->SetBinLabel(5, "DCAz selection"); - histos.add("FT0A_Amp", "FT0A_Amp", kTH1D, {cfgAxis.axisAmplitude}); histos.add("FT0A_Amp_gaincorrected", "FT0A_Amp_gaincorrected", kTH1D, {cfgAxis.axisAmplitude}); histos.add("FT0A_Channel_vs_Amp", "FT0A_Channel_vs_Amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); @@ -354,9 +315,9 @@ struct LongrangeMaker { TrackSelection::GlobalTrackRun3DCAxyCut::Default); myTrackFilter.SetPtRange(cfgtrksel.cfgPtCutMin, cfgtrksel.cfgPtCutMax); myTrackFilter.SetEtaRange(-cfgtrksel.cfgEtaCut, cfgtrksel.cfgEtaCut); - myTrackFilter.SetMinNCrossedRowsTPC(cfgtrksel.minNCrossedRowsTPC); + myTrackFilter.SetMinNCrossedRowsTPC(static_cast(cfgtrksel.minNCrossedRowsTPC)); myTrackFilter.SetMinNCrossedRowsOverFindableClustersTPC(cfgtrksel.minNCrossedRowsOverFindableClustersTPC); - myTrackFilter.SetMinNClustersTPC(cfgtrksel.minTPCNClsFound); + myTrackFilter.SetMinNClustersTPC(static_cast(cfgtrksel.minTPCNClsFound)); myTrackFilter.SetMaxChi2PerClusterTPC(cfgtrksel.maxChi2PerClusterTPC); myTrackFilter.SetMaxChi2PerClusterITS(cfgtrksel.maxChi2PerClusterITS); myTrackFilter.SetMaxDcaZ(cfgtrksel.maxDcaZ); @@ -367,21 +328,19 @@ struct LongrangeMaker { itsNsigmaCut = itsNsigmaPidCut; tpcNsigmaCut = tpcNsigmaPidCut; - cfgSgCuts = (SGCutParHolder)sgCuts; + cfgSgCuts = SGCutParHolder(sgCuts); } Produces lrmccollision; Produces lrmidmctracks; Produces lrft0amctracks; Produces lrft0cmctracks; - Produces lrmftmctracks; Produces lrcollision; Produces lrcollisionMcLabel; Produces lrmidtracks; Produces lrft0atracks; Produces lrft0ctracks; - Produces lrmfttracks; Produces lrv0tracks; Produces upclrcollision; @@ -391,7 +350,6 @@ struct LongrangeMaker { Produces upclrmidtracks; Produces upclrft0atracks; Produces upclrft0ctracks; - Produces upclrmfttracks; Produces upclrv0tracks; Filter fTracksEta = nabs(aod::track::eta) < cfgtrksel.cfgEtaCut; @@ -399,13 +357,12 @@ struct LongrangeMaker { using CollTable = soa::Join; using TrksTable = soa::Filtered>; - using MftTrkTable = aod::MFTTracks; using BCs = soa::Join; - std::vector ft0gainvalues{}; - void processData(CollTable::iterator const& col, TrksTable const& tracks, - aod::FT0s const&, MftTrkTable const&, - soa::SmallGroups const& reassoMftTracks, + std::vector ft0gainvalues; + void processData(CollTable::iterator const& col, + TrksTable const& tracks, + aod::FT0s const&, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { if (!isEventSelected(col)) { @@ -442,10 +399,12 @@ struct LongrangeMaker { // track loop for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } auto pid = getTrackPID(track); lrmidtracks(lrcollision.lastIndex(), track.tpcNClsFound(), @@ -548,28 +507,6 @@ struct LongrangeMaker { } } - // mft tracks - for (const auto& reassoMftTrack : reassoMftTracks) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - // v0 loop for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { // Quality selection for V0 prongs @@ -624,9 +561,7 @@ struct LongrangeMaker { void processUpc(CollTable::iterator const& col, BCs const& bcs, TrksTable const& tracks, aod::Zdcs const&, aod::FV0As const& fv0as, aod::FT0s const& ft0s, - aod::FDDs const& fdds, MftTrkTable const&, - soa::SmallGroups const& reassoMftTracks, - aod::V0Datas const& V0s) + aod::FDDs const& fdds, aod::V0Datas const& V0s) { if (!isEventSelected(col)) { return; @@ -646,9 +581,10 @@ struct LongrangeMaker { histos.fill(HIST("hSelectionResult"), isSGEvent.value); if (issgevent <= cfgGapSide) { - if (cfgSgCuts.minRgtrwTOF()) { - if (udhelpers::rPVtrwTOF(tracks, col.numContrib()) < cfgSgCuts.minRgtrwTOF()) + if (cfgSgCuts.minRgtrwTOF() != 0.0f) { + if (udhelpers::rPVtrwTOF(tracks, col.numContrib()) < cfgSgCuts.minRgtrwTOF()) { return; + } } upchelpers::FITInfo fitInfo{}; @@ -665,10 +601,12 @@ struct LongrangeMaker { // track loop for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } auto pid = getTrackPID(track); upclrmidtracks(upclrcollision.lastIndex(), track.tpcNClsFound(), @@ -728,27 +666,6 @@ struct LongrangeMaker { } } - for (const auto& reassoMftTrack : reassoMftTracks) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - upclrmfttracks(upclrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - // v0 loop for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { // Quality selection for V0 prongs @@ -804,18 +721,20 @@ struct LongrangeMaker { using ColMCTrueTable = soa::Join; using ColMCRecTable = soa::SmallGroups>; using TrksMCRecTable = soa::Filtered>; - using MftTrkMCRecTable = soa::Join; Preslice perColMidtrack = aod::track::collisionId; - PresliceUnsorted perColMftTrack = aod::fwdtrack::bestCollisionId; - void processMCRec(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, TrksMCRecTable const& RecTracks, - aod::FT0s const&, MftTrkMCRecTable const&, aod::BestCollisionsFwd3d const& reassoMftTracks, - aod::McParticles const& mcparticles, aod::BCsWithTimestamps const&) + void processMCRec(ColMCTrueTable::iterator const& mcCollision, + ColMCRecTable const& RecCols, + TrksMCRecTable const& RecTracks, + aod::FT0s const&, + aod::McParticles const& mcparticles, + aod::BCsWithTimestamps const&) { auto multiplicity = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMinForMult || particle.pt() > cfgtrksel.cfgPtCutMaxForMult) + if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMinForMult || particle.pt() > cfgtrksel.cfgPtCutMaxForMult) { continue; + } multiplicity++; } lrmccollision(mcCollision.posZ(), multiplicity, mcCollision.multMCFT0A(), mcCollision.multMCFT0C()); @@ -853,15 +772,19 @@ struct LongrangeMaker { // track loop for (const auto& track : recTracksPart) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - if (!track.has_mcParticle()) + } + if (!track.has_mcParticle()) { continue; + } auto mcpart = track.mcParticle(); - if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) + if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) { continue; + } auto pid = getTrackPID(track); lrmidtracks(lrcollision.lastIndex(), track.tpcNClsFound(), @@ -958,43 +881,21 @@ struct LongrangeMaker { } } - auto recMftTracksPart = reassoMftTracks.sliceBy(perColMftTrack, RecCol.globalIndex()); - for (const auto& reassoMftTrack : recMftTracksPart) { - if (!isMftBestTrackSelected(reassoMftTrack)) - continue; - auto itrack = reassoMftTrack.template mfttrack_as(); - if (!isMftTrackSelected(itrack)) { - continue; - } - auto phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), - reassoMftTrack.ambDegree(), - itrack.pt(), - itrack.eta(), - phi, - itrack.nClusters(), - reassoMftTrack.bestDCAXY(), - reassoMftTrack.bestDCAZ(), - itrack.isCA(), - itrack.collisionId() != reassoMftTrack.bestCollisionId()); - } - for (const auto& particle : mcparticles) { if (!isGenPartSelected(particle)) { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) { lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); + } // Fill FT0 tracks - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - // Fill MFT tracks - if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) - lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); + } } } } @@ -1005,14 +906,18 @@ struct LongrangeMaker { auto multMCFT0A = 0; auto multMCFT0C = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle)) + if (!isGenPartSelected(particle)) { continue; - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMinForMult && particle.pt() < cfgtrksel.cfgPtCutMaxForMult) + } + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMinForMult && particle.pt() < cfgtrksel.cfgPtCutMaxForMult) { multiplicity++; - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { multMCFT0C++; - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + } + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { multMCFT0A++; + } } lrmccollision(mcCollision.posZ(), multiplicity, multMCFT0A, multMCFT0C); @@ -1021,16 +926,16 @@ struct LongrangeMaker { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) { lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); + } // Fill FT0 tracks - if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) { lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + } + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) { lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - // Fill MFT tracks - if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) - lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); + } } } @@ -1045,9 +950,9 @@ struct LongrangeMaker { multiplicity = countNTracks(tracks, col.posZ()); } - bool checkK0s = (cfgv0trksel.cfgQAV0Mask & 1); - bool checkLam = (cfgv0trksel.cfgQAV0Mask & 2); - bool checkALam = (cfgv0trksel.cfgQAV0Mask & 4); + bool checkK0s = (cfgv0trksel.cfgQAV0Mask & 1) != 0; + bool checkLam = (cfgv0trksel.cfgQAV0Mask & 2) != 0; + bool checkALam = (cfgv0trksel.cfgQAV0Mask & 4) != 0; for (const auto& v0 : V0s) { if (!isSelectV0Track(v0)) { @@ -1201,16 +1106,19 @@ struct LongrangeMaker { { float nTrk = 0.f; for (const auto& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; + } if (track.pt() < cfgtrksel.cfgPtCutMinForMult || track.pt() > cfgtrksel.cfgPtCutMaxForMult) { continue; } float trkeff = 1.0f; - if (cfgtrksel.applyEffCorr) + if (cfgtrksel.applyEffCorr) { trkeff = getTrkEffCorr(vz, track.eta(), track.pt()); + } nTrk += trkeff; } return nTrk; @@ -1244,7 +1152,9 @@ struct LongrangeMaker { std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; std::array nSigmaToUse = isUseItsPid ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS std::vector detectorNsigmaCut = isUseItsPid ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - bool isPion, isKaon, isProton; + bool isPion = false; + bool isKaon = false; + bool isProton = false; bool isDetectedPion = nSigmaToUse[0] < detectorNsigmaCut[0] && nSigmaToUse[0] > detectorNsigmaCut[0 + 3]; bool isDetectedKaon = nSigmaToUse[1] < detectorNsigmaCut[1] && nSigmaToUse[1] > detectorNsigmaCut[1 + 3]; bool isDetectedProton = nSigmaToUse[2] < detectorNsigmaCut[2] && nSigmaToUse[2] > detectorNsigmaCut[2 + 3]; @@ -1255,7 +1165,8 @@ struct LongrangeMaker { if (track.pt() > cfgTofPidPtCut && !track.hasTOF()) { return aod::lrcorrtrktable::kNoPid; - } else if (track.pt() > cfgTofPidPtCut && track.hasTOF()) { + } + if (track.pt() > cfgTofPidPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -1271,31 +1182,33 @@ struct LongrangeMaker { if (isPion) { return aod::lrcorrtrktable::kSpPion; - } else if (isKaon) { + } + if (isKaon) { return aod::lrcorrtrktable::kSpKaon; - } else if (isProton) { + } + if (isProton) { return aod::lrcorrtrktable::kSpProton; - } else { - return aod::lrcorrtrktable::kNoPid; // no particle satisfies the criteria } + return aod::lrcorrtrktable::kNoPid; // no particle satisfies the criteria } int getFt0aDeadChannelId(int channelId) { - if (channelId >= MinFt0aMirrorChannelOuter && channelId <= MaxFt0aMirrorChannelOuter) + if (channelId >= MinFt0aMirrorChannelOuter && channelId <= MaxFt0aMirrorChannelOuter) { return MinFt0aDeadChannelOuter + (channelId - MinFt0aMirrorChannelOuter); - else - return -1; + } + return -1; } int getFt0cDeadChannelId(int channelId) { - if (channelId >= MinFt0cMirrorChannelOuter && channelId <= MaxFt0cMirrorChannelOuter) + if (channelId >= MinFt0cMirrorChannelOuter && channelId <= MaxFt0cMirrorChannelOuter) { return MinFt0cDeadChannelOuter + (channelId - MinFt0cMirrorChannelOuter); - else if (channelId == Ft0cMirrorChannelInner) + } + if (channelId == Ft0cMirrorChannelInner) { return Ft0cDeadChannelInner; - else - return -1; + } + return -1; } double getPhiFT0(uint chno, int i) @@ -1312,85 +1225,14 @@ struct LongrangeMaker { auto x = chPos.X() + (*offsetFT0)[i].getX(); auto y = chPos.Y() + (*offsetFT0)[i].getY(); auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - if (chno >= KminFt0cCell) + if (chno >= KminFt0cCell) { z = -z; + } auto r = std::sqrt(x * x + y * y); auto theta = std::atan2(r, z); return -std::log(std::tan(0.5 * theta)); } - template - bool isMftTrackSelected(CheckMftTrack const& track) - { - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 1); - } - if (track.nClusters() < cfgmfttrksel.cfgMftCluster) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 2); - } - if (cfgmfttrksel.cfgUseChi2Cut) { - float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); - float mftChi2NCl = track.chi2() / nclMft; - if (mftChi2NCl > cfgmfttrksel.cfgMaxChi2NCl) - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 3); - } - if (track.eta() > cfgmfttrksel.cfgMftEtaMax || track.eta() < cfgmfttrksel.cfgMftEtaMin) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 4); - } - if (cfgmfttrksel.useMftPtCut && (track.pt() < cfgmfttrksel.cfgMftPtCutMin || track.pt() > cfgmfttrksel.cfgMftPtCutMax)) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 5); - } - if constexpr (fillHis) { - histos.fill(HIST("hMftTrkSel"), 6); - } - return true; - } - - template - bool isMftBestTrackSelected(CheckMftTrack const& mfttrack) - { - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 1); - } - if (mfttrack.bestCollisionId() < CintZero) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 2); - } - if (mfttrack.ambDegree() == CintZero) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 3); - } - if (std::abs(mfttrack.bestDCAXY()) >= cfgmfttrksel.cfgMftDcaxy) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 4); - } - if (std::abs(mfttrack.bestDCAZ()) >= cfgmfttrksel.cfgMftDcaz) { - return false; - } - if constexpr (fillHis) { - histos.fill(HIST("hMftBestTrkSel"), 5); - } - return true; - } - template bool isSelectV0Track(const T1& v0) { @@ -1519,7 +1361,7 @@ struct LongrangeMaker { if (fLoadTrkEffCorr) { return; } - if (cfgtrksel.cfgEffccdbPath.value.empty() == false) { + if (!cfgtrksel.cfgEffccdbPath.value.empty()) { hTrkEff = ccdb->getForTimeStamp(cfgtrksel.cfgEffccdbPath, timestamp); if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgtrksel.cfgEffccdbPath.value.c_str()); @@ -1551,8 +1393,8 @@ struct LongrangeMaker { PROCESS_SWITCH(LongrangeMaker, processV0QA, "process V0 QA histograms", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index 9745762a695..22eeaebfdf6 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Core/CorrelationContainer.h" #include "PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGUD/Core/SGSelector.h" #include "Common/CCDB/EventSelectionParams.h" @@ -31,6 +32,7 @@ #include #include +#include #include #include #include @@ -91,6 +93,8 @@ struct LongrangecorrDerived { Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "Eta range to consider"}; Configurable cfgPtCutMin{"cfgPtCutMin", 0.2f, "minimum accepted track pT"}; Configurable cfgPtCutMax{"cfgPtCutMax", 10.0f, "maximum accepted track pT"}; + Configurable cfgPtCutMinForMult{"cfgPtCutMinForMult", 0.2f, "minimum track pT for fallback multiplicity classification"}; + Configurable cfgPtCutMaxForMult{"cfgPtCutMaxForMult", 3.0f, "maximum track pT for fallback multiplicity classification"}; Configurable cfgTpcMinNclsFound{"cfgTpcMinNclsFound", 50.0f, ""}; Configurable cfgTpcMinNCrossedRows{"cfgTpcMinNCrossedRows", 70.0f, ""}; Configurable cfgTpcMaxChi2PerCluster{"cfgTpcMaxChi2PerCluster", 4.0f, ""}; @@ -100,13 +104,6 @@ struct LongrangecorrDerived { Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; Configurable cfgAccccdbPath{"cfgAccccdbPath", "Users/a/abmodak/Acceptance/OO/default", "Browse track eff object from CCDB"}; - Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; - Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; - Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; - Configurable cfgRejectAmbTrk{"cfgRejectAmbTrk", false, "Condition to reject Ambiguous tracks"}; - Configurable cfgRejectNonAmbTrk{"cfgRejectNonAmbTrk", false, "Condition to reject Non-Ambiguous tracks"}; - Configurable cfgRequireCA{"cfgRequireCA", false, "Use Cellular Automaton track-finding algorithm"}; - Configurable cfgRequireLTF{"cfgRequireLTF", false, "Use LTF track-finding algorithm"}; Configurable cfgRequireFt0aOuterRing{"cfgRequireFt0aOuterRing", false, "Consider FT0A Outer Ring"}; Configurable cfgRequireFt0aInnerRing{"cfgRequireFt0aInnerRing", false, "Consider FT0A Inner Ring"}; Configurable cfgRequireFt0cOuterRing{"cfgRequireFt0cOuterRing", false, "Consider FT0C Outer Ring"}; @@ -115,13 +112,37 @@ struct LongrangecorrDerived { Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.4f, "Minimum pt to use TOF N-sigma"}; Configurable cfgPidNsigmaMax{"cfgPidNsigmaMax", 1.5f, "Maximum n-sigma for PID"}; Configurable cfgPidNsigmaMin{"cfgPidNsigmaMin", -1.5f, "Minimum n-sigma for PID"}; - Configurable cfgCleanNsigmaTpcMax{"cfgCleanNsigmaTpcMax", 1.5f, "Max TPC n-sigma for clean templates"}; - Configurable cfgCleanNsigmaTpcMin{"cfgCleanNsigmaTpcMin", -1.5f, "Min TPC n-sigma for clean templates"}; + Configurable cfgCleanNsigmaTpcMax{"cfgCleanNsigmaTpcMax", 10.0f, "Max TPC n-sigma for clean templates (wide to avoid dE/dx truncation)"}; + Configurable cfgCleanNsigmaTpcMin{"cfgCleanNsigmaTpcMin", -10.0f, "Min TPC n-sigma for clean templates (wide to avoid dE/dx truncation)"}; Configurable cfgCleanNsigmaTofMax{"cfgCleanNsigmaTofMax", 1.5f, "Max TOF n-sigma for clean templates"}; Configurable cfgCleanNsigmaTofMin{"cfgCleanNsigmaTofMin", -1.5f, "Min TOF n-sigma for clean templates"}; + Configurable cfgCleanTofVetoNsigma{"cfgCleanTofVetoNsigma", 3.0f, "Veto other species within this |nSigmaTOF| for track-level TOF-clean templates"}; Configurable cfgGetNsigmaQA{"cfgGetNsigmaQA", true, "Get QA histograms for PID selection"}; Configurable cfgGetdEdx{"cfgGetdEdx", true, "Get dEdx histograms for TPC signal"}; - + Configurable cfgGetAllSpeciesQA{"cfgGetAllSpeciesQA", true, "Fill unified all-species dE/dx and expected sigma histograms"}; + Configurable cfgGetV0CleanSample{"cfgGetV0CleanSample", true, "Fill clean V0 daughter (K0s -> pi pi, Lambda -> p pi) dE/dx templates"}; + Configurable cfgFillAfterCutWithV0{"cfgFillAfterCutWithV0", true, "Fill TpcdEdx_ptwise_afterCut and TofTpcNsigma_after for pi and p using V0 clean daughters"}; + Configurable cfgRequireTofForV0AfterCut{"cfgRequireTofForV0AfterCut", true, "Require hasTOF and |nSigmaTOF| < cfgCleanNsigmaTofMax when filling afterCut from V0s"}; + Configurable cfgIncludeLambdaPiInCleanPi{"cfgIncludeLambdaPiInCleanPi", true, "Also include clean pions from Lambda -> p pi in TpcdEdx_ptwise_afterCut for pion wagon"}; + Configurable cfgAllowFallbackMultInPIDQA{"cfgAllowFallbackMultInPIDQA", false, "If true and LRCollisions is empty, use fallback multiplicity in processPIDQA"}; + Configurable cfgRequireTofInMCdEdx{"cfgRequireTofInMCdEdx", true, "Require hasTOF and |nSigmaTOF| < 1.5 in MC_TpcdEdx_ptwise"}; + + // V0 topological cuts for clean daughter template extraction (tag-and-probe without TPC cut on probe daughter) + Configurable cfgCleanV0MinCosPA{"cfgCleanV0MinCosPA", 0.995f, "Min cos(PA) for clean V0 daughters"}; + Configurable cfgCleanV0MaxDcaDau{"cfgCleanV0MaxDcaDau", 0.8f, "Max DCA between V0 daughters (cm)"}; + Configurable cfgCleanV0MinRadius{"cfgCleanV0MinRadius", 1.2f, "Min V0 2D decay radius (cm)"}; + Configurable cfgCleanV0MaxRadius{"cfgCleanV0MaxRadius", 30.0f, "Max V0 2D decay radius (cm)"}; + Configurable cfgCleanK0sMassWin{"cfgCleanK0sMassWin", 0.008f, "Tight K0s mass window around PDG mass (GeV/c^2)"}; + Configurable cfgCleanK0sMinArmQt{"cfgCleanK0sMinArmQt", 0.2f, "Min Armenteros qT/|alpha| for clean K0s"}; + Configurable cfgCleanK0sMaxCtau{"cfgCleanK0sMaxCtau", 20.0f, "Max proper lifetime c*tau for clean K0s (cm)"}; + Configurable cfgCleanK0sMinDcaToPv{"cfgCleanK0sMinDcaToPv", 0.1f, "Min pion DCA to PV for clean K0s (cm)"}; + Configurable cfgCleanLambdaMassWin{"cfgCleanLambdaMassWin", 0.005f, "Tight Lambda mass window around PDG mass (GeV/c^2)"}; + Configurable cfgCleanLambdaRejK0sWin{"cfgCleanLambdaRejK0sWin", 0.012f, "Competing K0s mass rejection window for clean Lambda (GeV/c^2)"}; + Configurable cfgCleanLambdaMinArmQt{"cfgCleanLambdaMinArmQt", 0.02f, "Min Armenteros qT for clean Lambda to reject photon conversions (GeV/c)"}; + Configurable cfgCleanLambdaMaxCtau{"cfgCleanLambdaMaxCtau", 30.0f, "Max proper lifetime c*tau for clean Lambda (cm)"}; + Configurable cfgCleanLambdaMinDcaPr{"cfgCleanLambdaMinDcaPr", 0.07f, "Min proton DCA to PV for clean Lambda (cm)"}; + Configurable cfgCleanLambdaMinDcaPi{"cfgCleanLambdaMinDcaPi", 0.2f, "Min pion DCA to PV for clean Lambda (cm)"}; + Configurable cfgCleanV0TagTpcNsigma{"cfgCleanV0TagTpcNsigma", 3.0f, "Max |nSigmaTPC| on companion tag daughter"}; } cfgSel; struct : ConfigurableGroup { @@ -132,7 +153,7 @@ struct LongrangecorrDerived { ConfigurableAxis axisPhi{"axisPhi", {96, 0, TwoPI}, "#phi axis"}; ConfigurableAxis axisEtaTrig{"axisEtaTrig", {40, -1., 1.}, "#eta trig axis"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtQA{"axisPtQA", {50, 0.0, 8.0}, "pt axis for PID QA histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {80, 0.0, 8.0}, "pt axis for PID QA histograms"}; ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20, 20}, "vertex axis"}; ConfigurableAxis axisEtaAssoc{"axisEtaAssoc", {96, 3.5, 4.9}, "#eta assoc axis"}; @@ -151,11 +172,6 @@ struct LongrangecorrDerived { ConfigurableAxis axisTPCChi2NCl{"axisTPCChi2NCl", {20, 0.0, 20.0}, "TPC Chi2/NCl axis"}; ConfigurableAxis axisTPCdcaZ{"axisTPCdcaZ", {200, -10.0, 10.0}, "TPC dcaZ axis"}; - ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; - ConfigurableAxis axisMFTNClusters{"axisMFTNClusters", {200, -0.5, 199.5}, "MFT Cluster axis"}; - ConfigurableAxis axisMFTbestDCAXY{"axisMFTbestDCAXY", {200, -10.0, 10.0}, "MFT dcaXY axis"}; - ConfigurableAxis axisMFTbestDCAZ{"axisMFTbestDCAZ", {200, -10.0, 10.0}, "MFT dcaZ axis"}; - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0.5, 8.0}, "pt axis for efficiency histograms"}; @@ -163,6 +179,7 @@ struct LongrangecorrDerived { ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisTpcExpSigma{"axisTpcExpSigma", {200, 0, 20}, "Expected sigma axis for TPC dE/dx"}; ConfigurableAxis axisMcPt{"axisMcPt", {100, 0.2, 10.0}, "p_{T} (GeV/c)"}; ConfigurableAxis axisMcEta{"axisMcEta", {100, -0.8, 0.8}, "#eta"}; @@ -185,38 +202,32 @@ struct LongrangecorrDerived { using CollsTable = aod::LRCollisions; using TrksTable = aod::LRMidTracks; - using MftTrksTable = aod::LRMftTracks; using Ft0aTrksTable = aod::LRFt0aTracks; using Ft0cTrksTable = aod::LRFt0cTracks; using V0TrksTable = aod::LRV0Tracks; using McCollsTable = aod::LRMcCollisions; using McTrksTable = aod::LRMidMcTracks; - using McMftTrksTable = aod::LRMftMcTracks; using McFt0aTrksTable = aod::LRFt0aMcTracks; using McFt0cTrksTable = aod::LRFt0cMcTracks; using UpcCollsTable = soa::Join; using TrksUpcTable = aod::UpcLRMidTracks; - using MftTrksUpcTable = aod::UpcLRMftTracks; using Ft0aTrksUpcTable = aod::UpcLRFt0aTracks; using Ft0cTrksUpcTable = aod::UpcLRFt0cTracks; using V0TrksUpcTable = aod::UpcLRV0Tracks; Preslice perColTpc = aod::lrcorrtrktable::lrCollisionId; - Preslice perColMft = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0a = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0c = aod::lrcorrtrktable::lrCollisionId; Preslice perColV0 = aod::lrcorrtrktable::lrCollisionId; Preslice perUpcColTpc = aod::lrcorrtrktable::upcLRCollisionId; - Preslice perUpcColMft = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0a = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0c = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColV0 = aod::lrcorrtrktable::upcLRCollisionId; Preslice perMcColTpc = aod::lrcorrmctrktable::lrMcCollisionId; - Preslice perMcColMft = aod::lrcorrmctrktable::lrMcCollisionId; Preslice perMcColFt0a = aod::lrcorrmctrktable::lrMcCollisionId; Preslice perMcColFt0c = aod::lrcorrmctrktable::lrMcCollisionId; @@ -271,13 +282,6 @@ struct LongrangecorrDerived { histos.add("TPCNClsCrossedRows", "TPCNClsCrossedRows", kTH1D, {cfgAxis.axisTPCNClsCrossedRows}); histos.add("TPCChi2NCl", "TPCChi2NCl", kTH1D, {cfgAxis.axisTPCChi2NCl}); histos.add("TPCdcaZ", "TPCdcaZ", kTH1D, {cfgAxis.axisTPCdcaZ}); - - histos.add("MFTNClusters", "MFTNClusters", kTH1D, {cfgAxis.axisMFTNClusters}); - histos.add("MFTbestDCAXY", "MFTbestDCAXY", kTH1D, {cfgAxis.axisMFTbestDCAXY}); - histos.add("MFTbestDCAZ", "MFTbestDCAZ", kTH1D, {cfgAxis.axisMFTbestDCAZ}); - - histos.add("ReassignedMFTtrackAmbDegree", "ReassignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); - histos.add("AssignedMFTtrackAmbDegree", "AssignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); } if (doprocessTPCtrackEff) { @@ -290,7 +294,12 @@ struct LongrangecorrDerived { } if (cfgSel.cfgGetdEdx) { - histos.add("MC_TpcdEdx_ptwise", "MC True TPC dE/dx;Particle Species;p_{T} (GeV/c);TPC dE/dx", kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal}); + histos.add("MC_TpcdEdx_ptwise", "MC True TPC dE/dx;Particle Species;p_{T} (GeV/c);TPC dE/dx", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal}); + histos.add("MC_TpcdEdx_TofNsigma_ptwise", "MC True TPC dE/dx vs TOF n#sigma;Particle Species;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("MC_ExpSigma_ptwise", "MC Expected TPC #sigma;Particle Species;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); } } @@ -304,36 +313,62 @@ struct LongrangecorrDerived { myTrackFilter.SetMaxDcaZ(cfgSel.cfgTpcMaxDcaZ); myTrackFilter.print(); - if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + if (doprocessPIDQA) { + const AxisSpec axisEvtClass = cfgSel.isUseCentEst ? AxisSpec{cfgAxis.axisCentrality, "Centrality (%)"} + : AxisSpec{cfgAxis.axisMultiplicity, "Multiplicity (N_{ch})"}; + const AxisSpec axisSpecies{3, 0.5, 3.5, "Particle Species (1=#pi, 2=K, 3=p)"}; + const AxisSpec axisV0CleanType{3, 0.5, 3.5, "V0 Clean Daughter (1=#pi from K_{S}^{0}, 2=p from #Lambda, 3=#pi from #Lambda)"}; - if (cfgSel.isUseCentEst) { // CENTRALITY MODE - histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + histos.add("hPIDQA_EventCount", "PIDQA Event Matching Counter;;Events", kTH1D, {{4, 0.5, 4.5}}); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(1, "Raw collisions"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(2, "Pass basic EvSel"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(3, "Matched to LRCollisions"); + histos.get(HIST("hPIDQA_EventCount"))->GetXaxis()->SetBinLabel(4, "Unmatched (skipped)"); - histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Event Class;p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Event Class;p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); if (cfgSel.cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Clean TOF Cuts;Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("ExpTpcdEdx_ptwise", "Expected TPC dE/dx;Event Class;p_{T} (GeV/c);Expected TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("ExpSigma_ptwise", "Expected TPC #sigma;Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); } - } else { // MULTIPLICITY MODE - histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + } - histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + if (cfgSel.cfgGetAllSpeciesQA && cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_allSpecies_ptwise", "All-Species TPC dE/dx;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - if (cfgSel.cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + histos.add("TpcdEdx_allSpecies_afterCut", "All-Species Clean TOF TPC dE/dx;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", - kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); - } + histos.add("ExpSigma_allSpecies_ptwise", "All-Species Expected TPC #sigma;Species (1=#pi,2=K,3=p);Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisSpecies, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); + } + + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.add("V0_TpcdEdx_ptwise", "Clean V0 Daughter TPC dE/dx;Daughter Type (1=#pi(K_{S}^{0}), 2=p(#Lambda), 3=#pi(#Lambda));Event Class;p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {axisV0CleanType, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("V0_ExpSigma_ptwise", "Clean V0 Daughter Expected TPC #sigma;Daughter Type (1=#pi(K_{S}^{0}), 2=p(#Lambda), 3=#pi(#Lambda));Event Class;p_{T} (GeV/c);#sigma_{exp}^{TPC};n#sigma_{TOF}", + kTHnSparseD, {axisV0CleanType, axisEvtClass, cfgAxis.axisPtQA, cfgAxis.axisTpcExpSigma, cfgAxis.axisNsigmaTOF}); + + histos.add("hCleanK0s_MassVsPt", "Selected Clean K_{S}^{0};p_{T} (GeV/c);M(#pi^{+}#pi^{-}) (GeV/c^{2})", + kTH2D, {cfgAxis.axisPtQA, {100, 0.45, 0.55}}); + histos.add("hCleanLambda_MassVsPt", "Selected Clean #Lambda/#bar{#Lambda};p_{T} (GeV/c);M(p#pi) (GeV/c^{2})", + kTH2D, {cfgAxis.axisPtQA, {100, 1.09, 1.14}}); } } } @@ -343,7 +378,7 @@ struct LongrangecorrDerived { if (fLoadTrkEffCorr) { return; } - if (cfgSel.cfgEffccdbPath.value.empty() == false) { + if (!cfgSel.cfgEffccdbPath.value.empty()) { hTrkEff = ccdb->getForTimeStamp(cfgSel.cfgEffccdbPath, timestamp); if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgSel.cfgEffccdbPath.value.c_str()); @@ -351,7 +386,7 @@ struct LongrangecorrDerived { LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); } - if (cfgSel.cfgAccccdbPath.value.empty() == false) { + if (!cfgSel.cfgAccccdbPath.value.empty()) { hTrkAcc = ccdb->getForTimeStamp(cfgSel.cfgAccccdbPath, timestamp); if (hTrkAcc == nullptr) { LOGF(fatal, "Could not load NUA histogram for trigger particles from %s", cfgSel.cfgAccccdbPath.value.c_str()); @@ -396,30 +431,18 @@ struct LongrangecorrDerived { bool isTrackSelected(TTrack const& track) { if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cfgSel.cfgTpcMinNclsFound) + if (track.tpcNClsFound() < cfgSel.cfgTpcMinNclsFound) { return false; - if (track.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) - return false; - if (track.tpcChi2NCl() > cfgSel.cfgTpcMaxChi2PerCluster) - return false; - if (std::abs(track.dcaZ()) > cfgSel.cfgTpcMaxDcaZ) - return false; - return true; - } else if constexpr (requires { track.nClusters(); }) { - if (track.nClusters() < cfgSel.cfgMftCluster) - return false; - if (std::abs(track.bestDCAXY()) >= cfgSel.cfgMftDcaxy) - return false; - if (std::abs(track.bestDCAZ()) >= cfgSel.cfgMftDcaz) - return false; - if (cfgSel.cfgRejectAmbTrk && track.ambDegree() > 1) - return false; - if (cfgSel.cfgRejectNonAmbTrk && track.ambDegree() == 1) + } + if (track.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) { return false; - if (cfgSel.cfgRequireCA && !track.isCA()) + } + if (track.tpcChi2NCl() > cfgSel.cfgTpcMaxChi2PerCluster) { return false; - if (cfgSel.cfgRequireLTF && track.isCA()) + } + if (std::abs(track.dcaZ()) > cfgSel.cfgTpcMaxDcaZ) { return false; + } return true; } else { return true; @@ -460,16 +483,6 @@ struct LongrangecorrDerived { histos.fill(HIST("TPCChi2NCl"), track.tpcChi2NCl()); histos.fill(HIST("TPCdcaZ"), track.dcaZ()); } - if constexpr (requires { track.nClusters(); }) { - histos.fill(HIST("MFTNClusters"), track.nClusters()); - histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); - histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); - if (track.isReassigned()) { - histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); - } else { - histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); - } - } } template @@ -482,16 +495,6 @@ struct LongrangecorrDerived { histos.fill(HIST("Assoc_amp"), assoAmpl); histos.fill(HIST("Channel_vs_Assoc_amp"), track.channelID(), assoAmpl); } - if constexpr (requires { track.nClusters(); }) { - histos.fill(HIST("MFTNClusters"), track.nClusters()); - histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); - histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); - if (track.isReassigned()) { - histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); - } else { - histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); - } - } } template @@ -504,8 +507,9 @@ struct LongrangecorrDerived { if constexpr (fillHist) { histos.fill(HIST("hTrueGapSide"), truegapSide); } - if (truegapSide != cfgGapSideCut) + if (truegapSide != cfgGapSideCut) { return false; + } if constexpr (fillHist) { histos.fill(HIST("hTrueGapSide_AfterSel"), truegapSide); } @@ -521,22 +525,27 @@ struct LongrangecorrDerived { auto trkeff = 1.0f; auto trkAcc = 1.0f; - if (!isTrackSelected(triggerTrack)) + if (!isTrackSelected(triggerTrack)) { continue; + } if constexpr (requires { triggerTrack.trackType(); }) { - if (cfgSel.cfgPidMask != 0 && (cfgSel.cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) + if (cfgSel.cfgPidMask != 0 && (cfgSel.cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) { continue; + } } else if constexpr (requires { triggerTrack.v0Type(); }) { - if (cfgSel.cfgV0Mask != 0 && (cfgSel.cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) + if (cfgSel.cfgV0Mask != 0 && (cfgSel.cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) { continue; + } } if constexpr (requires { triggerTrack.channelID(); }) { - if (cfgSel.cfgRequireFt0aOuterRing && !triggerTrack.isTrackFT0Outer()) + if (cfgSel.cfgRequireFt0aOuterRing && !triggerTrack.isTrackFT0Outer()) { continue; - if (cfgSel.cfgRequireFt0aInnerRing && triggerTrack.isTrackFT0Outer()) + } + if (cfgSel.cfgRequireFt0aInnerRing && triggerTrack.isTrackFT0Outer()) { continue; + } trigAmpl = triggerTrack.amplitude(); } else { trigAmpl = 1.0; @@ -569,14 +578,17 @@ struct LongrangecorrDerived { for (auto const& assoTrack : assocs) { auto assoAmpl = 1.0f; - if (!isTrackSelected(assoTrack)) + if (!isTrackSelected(assoTrack)) { continue; + } if constexpr (requires { assoTrack.channelID(); }) { - if (cfgSel.cfgRequireFt0cOuterRing && !assoTrack.isTrackFT0Outer()) + if (cfgSel.cfgRequireFt0cOuterRing && !assoTrack.isTrackFT0Outer()) { continue; - if (cfgSel.cfgRequireFt0cInnerRing && assoTrack.isTrackFT0Outer()) + } + if (cfgSel.cfgRequireFt0cInnerRing && assoTrack.isTrackFT0Outer()) { continue; + } assoAmpl = assoTrack.amplitude(); } else { assoAmpl = 1.0f; @@ -611,10 +623,11 @@ struct LongrangecorrDerived { fillCollQA(col); auto multiplicity = 1.0f; if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = col.centrality(); - else + } else { multiplicity = col.multiplicity(); + } } else { multiplicity = col.multiplicity(); } @@ -638,17 +651,18 @@ struct LongrangecorrDerived { } auto multiplicity = 1.0f; if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = col.centrality(); - else + } else { multiplicity = col.multiplicity(); + } } else { multiplicity = col.multiplicity(); } return multiplicity; }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; @@ -690,10 +704,11 @@ struct LongrangecorrDerived { auto multiplicity = mccollision.multiplicity(); if (cfgSel.isUseDataLikeMult > 0) { for (const auto& collision : collisions) { - if (cfgSel.isUseCentEst) + if (cfgSel.isUseCentEst) { multiplicity = collision.centrality(); - else + } else { multiplicity = collision.multiplicity(); + } } } fillCorrHist(same, triggers, assocs, false, mccollision.posZ(), multiplicity, 1.0); @@ -705,19 +720,21 @@ struct LongrangecorrDerived { bool useMCMultiplicity = (cfgSel.isUseDataLikeMult == 0); auto getMultiplicity = [&collisions, &useMCMultiplicity, this](auto& col) { - if (useMCMultiplicity) + if (useMCMultiplicity) { return col.multiplicity(); + } auto groupedCollisions = collisions.sliceByCached(aod::lrcorrcolltable::lrMcCollisionId, col.globalIndex(), this->cache); - if (groupedCollisions.size() == 0) + if (groupedCollisions.size() == 0) { return -1.0f; - if (cfgSel.isUseCentEst) + } + if (cfgSel.isUseCentEst) { return groupedCollisions.begin().centrality(); - else - return groupedCollisions.begin().multiplicity(); + } + return groupedCollisions.begin().multiplicity(); }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; @@ -749,7 +766,7 @@ struct LongrangecorrDerived { return collision.multiplicity(); }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}, true}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; @@ -772,16 +789,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processTpcmftSE(CollsTable::iterator const& col, TrksTable const& tracks, MftTrksTable const& mfts) - { - processSame(col, tracks, mfts); - } - - void processMftft0aSE(CollsTable::iterator const& col, MftTrksTable const& mfts, Ft0aTrksTable const& ft0as) - { - processSame(col, mfts, ft0as); - } - void processV0ft0aSE(CollsTable::iterator const& col, V0TrksTable const& tracks, Ft0aTrksTable const& ft0as) { processSame(col, tracks, ft0as); @@ -797,11 +804,6 @@ struct LongrangecorrDerived { processSame(col, v0s, tracks); } - void processV0mftSE(CollsTable::iterator const& col, V0TrksTable const& tracks, MftTrksTable const& mfts) - { - processSame(col, tracks, mfts); - } - void processFt0aft0cSE(CollsTable::iterator const& col, Ft0aTrksTable const& ft0as, Ft0cTrksTable const& ft0cs) { processSame(col, ft0as, ft0cs); @@ -817,16 +819,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processTpcmftME(CollsTable const& cols, TrksTable const& tracks, MftTrksTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - - void processMftft0aME(CollsTable const& cols, MftTrksTable const& mfts, Ft0aTrksTable const& ft0as) - { - processMixed(cols, mfts, ft0as); - } - void processV0ft0aME(CollsTable const& cols, V0TrksTable const& tracks, Ft0aTrksTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -842,11 +834,6 @@ struct LongrangecorrDerived { processMixed(cols, v0s, tracks); } - void processV0mftME(CollsTable const& cols, V0TrksTable const& tracks, MftTrksTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - void processFt0aft0cME(CollsTable const& cols, Ft0aTrksTable const& ft0as, Ft0cTrksTable const& ft0cs) { processMixed(cols, ft0as, ft0cs); @@ -868,22 +855,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processUpcTpcmftSE(UpcCollsTable::iterator const& col, TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - - void processUpcMftft0aSE(UpcCollsTable::iterator const& col, MftTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, mfts, ft0as); - } - void processUpcV0ft0aSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { if (!isUpcEventSelected(col)) { @@ -897,14 +868,6 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0cs); } - void processUpcV0mftSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - void processUpcTpcft0aME(UpcCollsTable const& cols, TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -915,16 +878,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processUpcTpcmftME(UpcCollsTable const& cols, TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - - void processUpcMftft0aME(UpcCollsTable const& cols, MftTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - processMixed(cols, mfts, ft0as); - } - void processUpcV0ft0aME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, Ft0aTrksUpcTable const& ft0as) { processMixed(cols, tracks, ft0as); @@ -935,11 +888,6 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0cs); } - void processUpcV0mftME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - void processMcTpcft0aSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcSame(mccollision, collisions, tracks, ft0as); @@ -950,16 +898,6 @@ struct LongrangecorrDerived { processMcSame(mccollision, collisions, tracks, ft0cs); } - void processMcTpcmftSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcSame(mccollision, collisions, tracks, mfts); - } - - void processMcMftft0aSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcSame(mccollision, collisions, mfts, ft0as); - } - void processMcFt0aft0cSE(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) { processMcSame(mccollision, collisions, ft0as, ft0cs); @@ -975,16 +913,6 @@ struct LongrangecorrDerived { processMcMixed(mccollisions, collisions, tracks, ft0cs); } - void processMcTpcmftME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcMixed(mccollisions, collisions, tracks, mfts); - } - - void processMcMftft0aME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcMixed(mccollisions, collisions, mfts, ft0as); - } - void processMcFt0aft0cME(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) { processMcMixed(mccollisions, collisions, ft0as, ft0cs); @@ -1000,16 +928,6 @@ struct LongrangecorrDerived { processMcGenSame(mccollision, tracks, ft0cs); } - void processMcGenTpcmftSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcGenSame(mccollision, tracks, mfts); - } - - void processMcGenMftft0aSE(McCollsTable::iterator const& mccollision, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcGenSame(mccollision, mfts, ft0as); - } - void processMcGenV0ft0aSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcGenSame(mccollision, tracks, ft0as); @@ -1035,16 +953,6 @@ struct LongrangecorrDerived { processMcGenMixed(mccollisions, tracks, ft0cs); } - void processMcGenTpcmftME(McCollsTable const& mccollisions, McTrksTable const& tracks, McMftTrksTable const& mfts) - { - processMcGenMixed(mccollisions, tracks, mfts); - } - - void processMcGenMftft0aME(McCollsTable const& mccollisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) - { - processMcGenMixed(mccollisions, mfts, ft0as); - } - void processMcGenV0ft0aME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) { processMcGenMixed(mccollisions, tracks, ft0as); @@ -1068,29 +976,37 @@ struct LongrangecorrDerived { template bool isEventSelected(CheckCol const& col) { - if (!col.sel8()) + if (!col.sel8()) { return false; - if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + } + if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; - if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + } + if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; - if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + } + if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; - if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) + } + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { return false; + } return true; } template bool isGenPartSelected(CheckGenPart const& particle) { - if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) { return false; + } auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle == nullptr) + if (pdgParticle == nullptr) { return false; - if (std::abs(pdgParticle->Charge()) < KminCharge) + } + if (std::abs(pdgParticle->Charge()) < KminCharge) { return false; + } return true; } @@ -1102,18 +1018,21 @@ struct LongrangecorrDerived { } bool atLeastOne = false; for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) + if (!isEventSelected(RecCol)) { continue; - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; + } atLeastOne = true; } for (const auto& particle : mcparticles) { if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgSel.cfgEtaCut || particle.pt() < cfgSel.cfgPtCutMin || - particle.pt() > cfgSel.cfgPtCutMax) + particle.pt() > cfgSel.cfgPtCutMax) { continue; + } if (atLeastOne) { histos.fill(HIST("hGenMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); @@ -1132,37 +1051,60 @@ struct LongrangecorrDerived { } for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) + if (!isEventSelected(RecCol)) { continue; - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; + } auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); for (const auto& track : recTracksPart) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - if (!track.has_mcParticle()) + } + if (!track.has_mcParticle()) { continue; + } auto particle = track.mcParticle(); - if (RecCol.mcCollisionId() != particle.mcCollisionId()) + if (RecCol.mcCollisionId() != particle.mcCollisionId()) { continue; + } if (particle.isPhysicalPrimary()) { histos.fill(HIST("hRecMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); if (cfgSel.cfgGetdEdx) { int pdgIdx = 0; + float tofNsigMC = -999.0f; + float expSigmaMC = 0.0f; auto absPdg = std::abs(particle.pdgCode()); - if (absPdg == PDG_t::kPiPlus) + if (absPdg == PDG_t::kPiPlus) { pdgIdx = 1; // o2-linter: disable=pdg/explicit-code (histogram species index) - else if (absPdg == PDG_t::kKPlus) + tofNsigMC = track.tofNSigmaPi(); + expSigmaMC = track.tpcExpSigmaPi(); + } else if (absPdg == PDG_t::kKPlus) { pdgIdx = 2; // o2-linter: disable=pdg/explicit-code (histogram species index) - else if (absPdg == PDG_t::kProton) + tofNsigMC = track.tofNSigmaKa(); + expSigmaMC = track.tpcExpSigmaKa(); + } else if (absPdg == PDG_t::kProton) { pdgIdx = 3; // o2-linter: disable=pdg/explicit-code (histogram species index) + tofNsigMC = track.tofNSigmaPr(); + expSigmaMC = track.tpcExpSigmaPr(); + } if (pdgIdx > 0) { - histos.fill(HIST("MC_TpcdEdx_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal()); + if (track.hasTOF()) { + histos.fill(HIST("MC_TpcdEdx_TofNsigma_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal(), tofNsigMC); + histos.fill(HIST("MC_ExpSigma_ptwise"), static_cast(pdgIdx), track.pt(), expSigmaMC, tofNsigMC); + } + bool passTofForMC = !cfgSel.cfgRequireTofInMCdEdx || + (track.hasTOF() && tofNsigMC > cfgSel.cfgCleanNsigmaTofMin && tofNsigMC < cfgSel.cfgCleanNsigmaTofMax); + if (passTofForMC) { + histos.fill(HIST("MC_TpcdEdx_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal()); + } } } @@ -1180,7 +1122,7 @@ struct LongrangecorrDerived { isPion = isTofPion && isTpcPion; isKaon = isTofKaon && isTpcKaon; isProton = isTofProton && isTpcProton; - } else if (!(track.pt() > cfgSel.cfgTofPidPtCut && !track.hasTOF())) { + } else if (track.pt() <= cfgSel.cfgTofPidPtCut || track.hasTOF()) { isPion = isTpcPion; isKaon = isTpcKaon; isProton = isTpcProton; @@ -1209,82 +1151,269 @@ struct LongrangecorrDerived { using CollStandardTable = soa::Join; using TrksStandardTable = soa::Join; - void processPIDQA(CollsTable const& lrCollisions, TrksStandardTable const& standardTracks) + template + float countFallbackMultiplicity(TTracks const& tracks, float vz) { - for (auto const& col : lrCollisions) { - - if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { + float nTrk = 0.0f; + for (auto const& trk : tracks) { + if (!trk.isGlobalTrack()) { continue; } + if (!myTrackFilter.IsSelected(trk)) { + continue; + } + if (trk.pt() < cfgSel.cfgPtCutMinForMult || trk.pt() > cfgSel.cfgPtCutMaxForMult) { + continue; + } + nTrk += getTrkEffCorr(vz, trk.eta(), trk.pt()); + } + return nTrk; + } - // EXTRACT CENTRALITY/MULTIPLICITY - float multiplicity = 1.0f; - if constexpr (requires { col.centrality(); }) { - if (cfgSel.isUseCentEst) - multiplicity = col.centrality(); - else - multiplicity = col.multiplicity(); + template + void fillCleanV0Daughters(TCollision const& rawCol, TV0 const& v0, TTrack const& posTrack, TTrack const& negTrack, float multiplicity) + { + if (!posTrack.hasTPC() || !negTrack.hasTPC()) { + return; + } + if (posTrack.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows || negTrack.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) { + return; + } + if (std::abs(v0.positiveeta()) > cfgSel.cfgEtaCut || std::abs(v0.negativeeta()) > cfgSel.cfgEtaCut) { + return; + } + if (v0.v0cosPA() < cfgSel.cfgCleanV0MinCosPA) { + return; + } + if (v0.dcaV0daughters() > cfgSel.cfgCleanV0MaxDcaDau) { + return; + } + if (v0.v0radius() < cfgSel.cfgCleanV0MinRadius || v0.v0radius() > cfgSel.cfgCleanV0MaxRadius) { + return; + } + + float distOverP = v0.distovertotmom(rawCol.posX(), rawCol.posY(), rawCol.posZ()); + + auto fillV0Probe = [&](double v0CleanType, TTrack const& probeTrack, float tpcNsig, float tofNsigReal, float expSigma, bool isPionProbe, bool isProtonProbe) { + if (!probeTrack.isGlobalTrack() || !myTrackFilter.IsSelected(probeTrack)) { + return; + } + + const float tofVal = probeTrack.hasTOF() ? tofNsigReal : -999.0f; + + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("V0_TpcdEdx_ptwise"), v0CleanType, multiplicity, probeTrack.pt(), probeTrack.tpcSignal(), tofVal); + histos.fill(HIST("V0_ExpSigma_ptwise"), v0CleanType, multiplicity, probeTrack.pt(), expSigma, tofVal); + } + + if (cfgSel.cfgFillAfterCutWithV0 && cfgSel.cfgGetNsigmaQA) { + const bool passTofForAfterCut = !cfgSel.cfgRequireTofForV0AfterCut || + (probeTrack.hasTOF() && tofNsigReal > cfgSel.cfgCleanNsigmaTofMin && tofNsigReal < cfgSel.cfgCleanNsigmaTofMax); + const bool matchWagon = (((cfgSel.cfgPidMask & KPidMaskPion) != 0) && isPionProbe) || + (((cfgSel.cfgPidMask & KPidMaskProton) != 0) && isProtonProbe); + if (matchWagon && passTofForAfterCut) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, probeTrack.pt(), tpcNsig, tofVal); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, probeTrack.pt(), probeTrack.tpcSignal(), tofVal); + } + } + } + }; + + float ctauK0s = distOverP * o2::constants::physics::MassK0; + bool isK0sTopo = (std::abs(v0.mK0Short() - o2::constants::physics::MassK0) < cfgSel.cfgCleanK0sMassWin) && + (v0.qtarm() >= cfgSel.cfgCleanK0sMinArmQt * std::abs(v0.alpha())) && + (std::abs(ctauK0s) <= cfgSel.cfgCleanK0sMaxCtau) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanK0sMinDcaToPv) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanK0sMinDcaToPv); + + if (isK0sTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanK0s_MassVsPt"), v0.pt(), v0.mK0Short()); + } + if (std::abs(negTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(1.0, posTrack, posTrack.tpcNSigmaPi(), posTrack.tofNSigmaPi(), posTrack.tpcExpSigmaPi(), true, false); + } + if (std::abs(posTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(1.0, negTrack, negTrack.tpcNSigmaPi(), negTrack.tofNSigmaPi(), negTrack.tpcExpSigmaPi(), true, false); + } + } + + float ctauLambda = distOverP * o2::constants::physics::MassLambda; + bool passK0sRej = std::abs(v0.mK0Short() - o2::constants::physics::MassK0) >= cfgSel.cfgCleanLambdaRejK0sWin; + bool passLambdaArm = v0.qtarm() >= cfgSel.cfgCleanLambdaMinArmQt; + bool passLambdaCtau = std::abs(ctauLambda) <= cfgSel.cfgCleanLambdaMaxCtau; + + if (passK0sRej && passLambdaArm && passLambdaCtau) { + bool isLambdaTopo = (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < cfgSel.cfgCleanLambdaMassWin) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanLambdaMinDcaPr) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanLambdaMinDcaPi); + if (isLambdaTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanLambda_MassVsPt"), v0.pt(), v0.mLambda()); + } + if (std::abs(negTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(2.0, posTrack, posTrack.tpcNSigmaPr(), posTrack.tofNSigmaPr(), posTrack.tpcExpSigmaPr(), false, true); + } + if (std::abs(posTrack.tpcNSigmaPr()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(3.0, negTrack, negTrack.tpcNSigmaPi(), negTrack.tofNSigmaPi(), negTrack.tpcExpSigmaPi(), cfgSel.cfgIncludeLambdaPiInCleanPi, false); + } + } + + bool isAntiLambdaTopo = (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < cfgSel.cfgCleanLambdaMassWin) && + (std::abs(v0.dcapostopv()) >= cfgSel.cfgCleanLambdaMinDcaPi) && + (std::abs(v0.dcanegtopv()) >= cfgSel.cfgCleanLambdaMinDcaPr); + if (isAntiLambdaTopo) { + if (cfgSel.cfgGetV0CleanSample && cfgSel.cfgGetdEdx) { + histos.fill(HIST("hCleanLambda_MassVsPt"), v0.pt(), v0.mAntiLambda()); + } + if (std::abs(posTrack.tpcNSigmaPi()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(2.0, negTrack, negTrack.tpcNSigmaPr(), negTrack.tofNSigmaPr(), negTrack.tpcExpSigmaPr(), false, true); + } + if (std::abs(negTrack.tpcNSigmaPr()) < cfgSel.cfgCleanV0TagTpcNsigma) { + fillV0Probe(3.0, posTrack, posTrack.tpcNSigmaPi(), posTrack.tofNSigmaPi(), posTrack.tpcExpSigmaPi(), cfgSel.cfgIncludeLambdaPiInCleanPi, false); + } + } + } + } + + void processPIDQA(CollStandardTable const& rawCollisions, CollsTable const& lrCollisions, + TrksStandardTable const& standardTracks, aod::V0Datas const& V0s) + { + int64_t lrIdx = 0; + const int64_t nLrCols = lrCollisions.size(); + + for (auto const& rawCol : rawCollisions) { + histos.fill(HIST("hPIDQA_EventCount"), 1.0); + + bool matched = false; + float multiplicity = -1.0f; + if (nLrCols > 0 && lrIdx < nLrCols) { + if (std::abs(lrCollisions.iteratorAt(lrIdx).posZ() - rawCol.posZ()) <= 1e-5f) { + auto const& lrCol = lrCollisions.iteratorAt(lrIdx); + multiplicity = cfgSel.isUseCentEst ? lrCol.centrality() : lrCol.multiplicity(); + lrIdx++; + matched = true; + } + } + + if (!isEventSelected(rawCol)) { + continue; + } + histos.fill(HIST("hPIDQA_EventCount"), 2.0); + + if (matched) { + histos.fill(HIST("hPIDQA_EventCount"), 3.0); + } else if (nLrCols == 0 && cfgSel.cfgAllowFallbackMultInPIDQA) { + auto tracksForMult = standardTracks.sliceByCached(aod::track::collisionId, rawCol.globalIndex(), this->cache); + multiplicity = countFallbackMultiplicity(tracksForMult, rawCol.posZ()); + histos.fill(HIST("hPIDQA_EventCount"), 3.0); } else { - multiplicity = col.multiplicity(); + histos.fill(HIST("hPIDQA_EventCount"), 4.0); + continue; } - auto tracks = standardTracks.sliceByCached(aod::track::collisionId, col.globalIndex(), this->cache); + auto tracks = standardTracks.sliceByCached(aod::track::collisionId, rawCol.globalIndex(), this->cache); for (auto const& track : tracks) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { continue; - if (!myTrackFilter.IsSelected(track)) + } + if (!myTrackFilter.IsSelected(track)) { continue; - - if (!track.hasTOF()) + } + if (!track.hasTOF()) { continue; + } - float tpcNsig = 0.0f, tofNsig = 0.0f; - if (cfgSel.cfgPidMask & KPidMaskPion) { - tpcNsig = track.tpcNSigmaPi(); - tofNsig = track.tofNSigmaPi(); - } else if (cfgSel.cfgPidMask & KPidMaskKaon) { - tpcNsig = track.tpcNSigmaKa(); - tofNsig = track.tofNSigmaKa(); - } else if (cfgSel.cfgPidMask & KPidMaskProton) { - tpcNsig = track.tpcNSigmaPr(); - tofNsig = track.tofNSigmaPr(); + const float tpcNsigPi = track.tpcNSigmaPi(), tofNsigPi = track.tofNSigmaPi(), expSigPi = track.tpcExpSigmaPi(); + const float tpcNsigKa = track.tpcNSigmaKa(), tofNsigKa = track.tofNSigmaKa(), expSigKa = track.tpcExpSigmaKa(); + const float tpcNsigPr = track.tpcNSigmaPr(), tofNsigPr = track.tofNSigmaPr(), expSigPr = track.tpcExpSigmaPr(); + + const bool isTpcCleanPi = (tpcNsigPi > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigPi < cfgSel.cfgCleanNsigmaTpcMax); + const bool isTpcCleanKa = (tpcNsigKa > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigKa < cfgSel.cfgCleanNsigmaTpcMax); + const bool isTpcCleanPr = (tpcNsigPr > cfgSel.cfgCleanNsigmaTpcMin && tpcNsigPr < cfgSel.cfgCleanNsigmaTpcMax); + + const bool isTofCleanPi = (tofNsigPi > cfgSel.cfgCleanNsigmaTofMin && tofNsigPi < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigKa) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigPr) > cfgSel.cfgCleanTofVetoNsigma); + const bool isTofCleanKa = (tofNsigKa > cfgSel.cfgCleanNsigmaTofMin && tofNsigKa < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigPi) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigPr) > cfgSel.cfgCleanTofVetoNsigma); + const bool isTofCleanPr = (tofNsigPr > cfgSel.cfgCleanNsigmaTofMin && tofNsigPr < cfgSel.cfgCleanNsigmaTofMax) && + (std::abs(tofNsigPi) > cfgSel.cfgCleanTofVetoNsigma) && + (std::abs(tofNsigKa) > cfgSel.cfgCleanTofVetoNsigma); + + const bool isCleanPi = isTofCleanPi && isTpcCleanPi; + const bool isCleanKa = isTofCleanKa && isTpcCleanKa; + const bool isCleanPr = isTofCleanPr && isTpcCleanPr; + + if (cfgSel.cfgGetAllSpeciesQA && cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 1.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPi); + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 2.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigKa); + histos.fill(HIST("TpcdEdx_allSpecies_ptwise"), 3.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPr); + + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 1.0, multiplicity, track.pt(), expSigPi, tofNsigPi); + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 2.0, multiplicity, track.pt(), expSigKa, tofNsigKa); + histos.fill(HIST("ExpSigma_allSpecies_ptwise"), 3.0, multiplicity, track.pt(), expSigPr, tofNsigPr); + + if (isCleanPi) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 1.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPi); + } + if (isCleanKa) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 2.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigKa); + } + if (isCleanPr) { + histos.fill(HIST("TpcdEdx_allSpecies_afterCut"), 3.0, multiplicity, track.pt(), track.tpcSignal(), tofNsigPr); + } } - // FILL BEFORE CUTS if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + float tpcNsig = 0.0f, tofNsig = 0.0f, expSig = 0.0f; + bool isTargetClean = false; + + if ((cfgSel.cfgPidMask & KPidMaskPion) != 0) { + tpcNsig = tpcNsigPi; + tofNsig = tofNsigPi; + expSig = expSigPi; + isTargetClean = isCleanPi; + } else if ((cfgSel.cfgPidMask & KPidMaskKaon) != 0) { + tpcNsig = tpcNsigKa; + tofNsig = tofNsigKa; + expSig = expSigKa; + isTargetClean = isCleanKa; + } else if ((cfgSel.cfgPidMask & KPidMaskProton) != 0) { + tpcNsig = tpcNsigPr; + tofNsig = tofNsigPr; + expSig = expSigPr; + isTargetClean = isCleanPr; + } + + const float expSignal = track.tpcSignal() - (tpcNsig * expSig); + histos.fill(HIST("TofTpcNsigma_before"), multiplicity, track.pt(), tpcNsig, tofNsig); if (cfgSel.cfgGetdEdx) { histos.fill(HIST("TpcdEdx_ptwise"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + histos.fill(HIST("ExpTpcdEdx_ptwise"), multiplicity, track.pt(), expSignal, tofNsig); + histos.fill(HIST("ExpSigma_ptwise"), multiplicity, track.pt(), expSig, tofNsig); } - } - - bool isTpcPion = (track.tpcNSigmaPi() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPi() < cfgSel.cfgCleanNsigmaTpcMax); - bool isTpcKaon = (track.tpcNSigmaKa() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaKa() < cfgSel.cfgCleanNsigmaTpcMax); - bool isTpcProton = (track.tpcNSigmaPr() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPr() < cfgSel.cfgCleanNsigmaTpcMax); - - bool isTofPion = (track.tofNSigmaPi() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPi() < cfgSel.cfgCleanNsigmaTofMax); - bool isTofKaon = (track.tofNSigmaKa() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaKa() < cfgSel.cfgCleanNsigmaTofMax); - bool isTofProton = (track.tofNSigmaPr() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPr() < cfgSel.cfgCleanNsigmaTofMax); - - bool isPion = isTofPion && isTpcPion; - bool isKaon = isTofKaon && isTpcKaon; - bool isProton = isTofProton && isTpcProton; - if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { - isPion = isKaon = isProton = false; + const bool useTrackCleanHere = !cfgSel.cfgFillAfterCutWithV0 || (cfgSel.cfgPidMask == KPidMaskKaon); + if (useTrackCleanHere && isTargetClean) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } } + } - bool isTargetParticle = ((cfgSel.cfgPidMask & KPidMaskPion) && isPion) || - ((cfgSel.cfgPidMask & KPidMaskKaon) && isKaon) || - ((cfgSel.cfgPidMask & KPidMaskProton) && isProton); - - // FILL AFTER CUTS - if (cfgSel.cfgGetNsigmaQA && isTargetParticle) { - histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); - if (cfgSel.cfgGetdEdx) { - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); - } + if ((cfgSel.cfgGetV0CleanSample || cfgSel.cfgFillAfterCutWithV0) && cfgSel.cfgGetdEdx) { + auto v0sThisCol = V0s.sliceByCached(aod::v0data::collisionId, rawCol.globalIndex(), this->cache); + for (auto const& v0 : v0sThisCol) { + auto const& posTrack = v0.template posTrack_as(); + auto const& negTrack = v0.template negTrack_as(); + fillCleanV0Daughters(rawCol, v0, posTrack, negTrack, multiplicity); } } } @@ -1294,52 +1423,32 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aME, "mixed event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cSE, "same event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cME, "mixed event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftSE, "same event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftME, "mixed event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftft0aSE, "same event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftft0aME, "mixed event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aSE, "same event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aME, "mixed event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cSE, "same event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cME, "mixed event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processV0tpcSE, "same event V0 vs TPC", false); PROCESS_SWITCH(LongrangecorrDerived, processV0tpcME, "mixed event V0 vs TPC", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftSE, "same event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftME, "mixed event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cSE, "same event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cME, "mixed event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0aSE, "same UPC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0aME, "mixed UPC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0cSE, "same UPC event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0cME, "mixed UPC event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftSE, "same UPC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftME, "mixed UPC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftft0aSE, "same UPC event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftft0aME, "mixed UPC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aSE, "same UPC event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aME, "mixed UPC event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cSE, "same UPC event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cME, "mixed UPC event V0 vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftSE, "same UPC event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftME, "mixed UPC event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aSE, "same MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aME, "mixed MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0cSE, "same MC event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0cME, "mixed MC event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcTpcmftSE, "same MC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcTpcmftME, "mixed MC event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcMftft0aSE, "same MC event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcMftft0aME, "mixed MC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cSE, "same MC event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cME, "mixed MC event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aSE, "same MC gen event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aME, "mixed MC gen event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cSE, "same MC gen event TPC vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cME, "mixed MC gen event TPC vs FT0C", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftSE, "same MC gen event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftME, "mixed MC gen event TPC vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aSE, "same MC gen event MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aME, "mixed MC gen event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aSE, "same MC gen event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aME, "mixed MC gen event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0cSE, "same MC gen event V0 vs FT0C", false); @@ -1350,8 +1459,8 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processPIDQA, "process PID QA on standard AODs", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } From a5b2e35477ff909c0c480ff5aaa20d4835080eb4 Mon Sep 17 00:00:00 2001 From: Tanu Gahlaut <154991749+TGahlaut1@users.noreply.github.com> Date: Mon, 9 Nov 2026 19:34:22 +0530 Subject: [PATCH 624/648] [PWGCF] add centrality configurable and Gen function (#18617) --- PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx | 559 +++++++++++++----- 1 file changed, 399 insertions(+), 160 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx index 3605e90661d..9af2293e4e3 100644 --- a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx @@ -61,51 +61,71 @@ struct MeanPtFlucId { Configurable nPBins{"nPBins", 300, ""}; Configurable nPhiBins{"nPhiBins", 100, ""}; Configurable nPtBins{"nPtBins", 50, "Number of pT bins"}; - Configurable cfgCutPtMax{"cfgCutPtMax", 2.0, "maximum pT"}; - Configurable cfgCutPtMin{"cfgCutPtMin", 0.2, "minimum pT"}; - Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut"}; - Configurable cfgDcaPtDepOffset{"cfgDcaPtDepOffset", 0.004, "Offset of the pT-dependent DCA cut (cm)"}; - Configurable cfgDcaPtDepScale{"cfgDcaPtDepScale", 0.013, "1/pT coefficient of the pT-dependent DCA cut (cm GeV/c)"}; - Configurable cfgCutPosZ{"cfgCutPosZ", 7.0, "cut for vertex Z"}; - Configurable cfgPosZ{"cfgPosZ", true, "Position Z"}; - Configurable cfgCutNSig2{"cfgCutNSig2", 2.0, "nSigma cut: 2"}; - Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; - Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; - Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; - Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 3.0, "nSigma TPC cut for pion selection"}; - Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 3.0, "nSigma TPC cut for kaon selection"}; - Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 3.0, "nSigma TPC cut for proton selection"}; - Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 3.0, "nSigma TOF cut for pion selection"}; - Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 3.0, "nSigma TOF cut for kaon selection"}; - Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 3.0, "nSigma TOF cut for proton selection"}; - // Signed TOF nSigma offsets: corrected = raw - shift. Zero preserves the original PID. - Configurable cfgTofShiftPi{"cfgTofShiftPi", 0.f, "Pion TOF nSigma offset to subtract (not a time shift in ps)"}; - Configurable cfgTofShiftKa{"cfgTofShiftKa", 0.f, "Kaon TOF nSigma offset to subtract (not a time shift in ps)"}; - Configurable cfgTofShiftPr{"cfgTofShiftPr", 0.f, "Proton TOF nSigma offset to subtract (not a time shift in ps)"}; - Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; - Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; - Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; - Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; - Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.55, "Momentum threshold for requiring kaon TOF PID"}; - Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; - Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP2U{"cfgP2U", -0.0118451, "p_{2} for upper cut for N_{TPC} vs N{sim}"}; - Configurable cfgP0L{"cfgP0L", 23.8213, "p_{0} for lower cut for N_{TPC} vs N{sim}"}; - Configurable cfgP1L{"cfgP1L", -0.402328, "p_{1} for lower cut for N_{TPC} vs N{sim}"}; - Configurable cfgP2L{"cfgP2L", 0.0128423, "p_{2} for lower cut for N_{TPC} vs N{sim}"}; + struct : ConfigurableGroup { + Configurable cfgCutPtMax{"cfgCutPtMax", 2.0, "maximum pT"}; + Configurable cfgCutPtMin{"cfgCutPtMin", 0.2, "minimum pT"}; + Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut"}; + Configurable cfgCutITSChi2NCl{"cfgCutITSChi2NCl", 36.0, "ITS Chi2/NCl cut"}; + Configurable cfgCutTPCChi2NCl{"cfgCutTPCChi2NCl", 4.0, "TPC Chi2/NCl cut"}; + Configurable cfgCutTPCNCls{"cfgCutTPCNCls", 70.0, "TPC NCls cut"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 0.1, "DCAxy cut"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 0.2, "DCAz cut"}; + Configurable cfgDcaPtDepOffset{"cfgDcaPtDepOffset", 0.004, "Offset of the pT-dependent DCA cut (cm)"}; + Configurable cfgDcaPtDepScale{"cfgDcaPtDepScale", 0.013, "1/pT coefficient of the pT-dependent DCA cut (cm GeV/c)"}; + Configurable cfgCutNCrossedRowsTPC{"cfgCutNCrossedRowsTPC", 70, "Minimum number of crossed TPC rows"}; + Configurable cfgGlobalTrack{"cfgGlobalTrack", true, "Require the standard global-track selection (includes its DCA cuts)"}; + Configurable cfgGlobalTrackWoDCA{"cfgGlobalTrackWoDCA", false, "Require global-track quality without built-in DCA cuts; disable cfgGlobalTrack to use only this selection"}; + Configurable cfgDcaXYPtDep{"cfgDcaXYPtDep", true, "Apply the pT-dependent absolute DCAxy cut; takes precedence over cfgDcaXY"}; + Configurable cfgNTPCCrossedRows{"cfgNTPCCrossedRows", false, "Apply the minimum number of crossed TPC rows"}; + Configurable cfgTPCChi2NCl{"cfgTPCChi2NCl", false, "Chi2 per N_{TPC} Clusters Cut "}; + Configurable cfgITSChi2NCl{"cfgITSChi2NCl", false, "Chi2 per N_{ITS} Clusters Cut "}; + Configurable cfgDcaXY{"cfgDcaXY", false, "DCA XY Cut "}; + Configurable cfgDcaZ{"cfgDcaZ", false, "DCA Z Cut "}; + Configurable cfgDcaZPtDep{"cfgDcaZPtDep", false, "DCA Z pT dependent Cut "}; + } trackCuts; + struct : ConfigurableGroup { + Configurable cfgCutPosZ{"cfgCutPosZ", 7.0, "cut for vertex Z"}; + Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2U{"cfgP2U", -0.0118451, "p_{2} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP0L{"cfgP0L", 23.8213, "p_{0} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1L{"cfgP1L", -0.402328, "p_{1} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2L{"cfgP2L", 0.0128423, "p_{2} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; + Configurable cfgPosZ{"cfgPosZ", true, "Position Z"}; + Configurable cfgCentFT0C{"cfgCentFT0C", false, "Use FT0C for centrality instead of FT0M"}; + Configurable cfgCentFV0A{"cfgCentFV0A", false, "Use FV0A for centrality instead of FT0M"}; + Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; + Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; + Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "kIsGoodITSLayersAll"}; + Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "kIsGoodZvtxFT0vsPV"}; + Configurable cfgNtpcEventCut{"cfgNtpcEventCut", false, "N_{TPC} Event Cut "}; + } eventCuts; + struct : ConfigurableGroup { + Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; + Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; + Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; + Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 3.0, "nSigma TPC cut for pion selection"}; + Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 3.0, "nSigma TPC cut for kaon selection"}; + Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 3.0, "nSigma TPC cut for proton selection"}; + Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 3.0, "nSigma TOF cut for pion selection"}; + Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 3.0, "nSigma TOF cut for kaon selection"}; + Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 3.0, "nSigma TOF cut for proton selection"}; + Configurable cfgTofShiftPi{"cfgTofShiftPi", 0.f, "Pion TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftKa{"cfgTofShiftKa", 0.f, "Kaon TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftPr{"cfgTofShiftPr", 0.f, "Proton TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; + Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; + Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; + Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; + Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.55, "Momentum threshold for requiring kaon TOF PID"}; + Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; + Configurable cfgRejTrk{"cfgRejTrk", true, "Rejected Tracks"}; + } pidConfig; Configurable cfgP0corr{"cfgP0corr", 0.0001, "p_{0} for corrected N_{TPC} "}; Configurable cfgP1corr{"cfgP1corr", 0.0001, "p_{1} for corrected N_{TPC} "}; Configurable cfgP2corr{"cfgP2corr", 0.0001, "p_{2} for corrected N_{TPC} "}; - Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; - Configurable cfgCentFT0C{"cfgCentFT0C", false, "Use FT0C for centrality instead of FT0M"}; - Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; - Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; - Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; - Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "kIsGoodITSLayersAll"}; - Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "kIsGoodZvtxFT0vsPV"}; - Configurable cfgRejTrk{"cfgRejTrk", true, "Rejected Tracks"}; - Configurable cfgNtpcEventCut{"cfgNtpcEventCut", true, "N_{TPC} Event Cut for reco"}; Configurable cfgApplyEfficiencyCorrection{"cfgApplyEfficiencyCorrection", false, "Apply measured track-by-track efficiency weights"}; Configurable cfgUseCombinedPurityEfficiencyWeight{"cfgUseCombinedPurityEfficiencyWeight", false, "Use the legacy Ngen/Nselected weight instead of the efficiency-only 1/epsilon weight"}; Configurable cfgEfficiencyCCDBUrl{"cfgEfficiencyCCDBUrl", "http://ccdb-test.cern.ch:8080", "Efficiency CCDB URL"}; @@ -122,6 +142,16 @@ struct MeanPtFlucId { Configurable> ptBins{"ptBins", {0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00}, "p_{T} bins"}; Configurable> etaBins{"etaBins", {-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, "#eta bins"}; + struct : ConfigurableGroup { + Configurable cfgGenCutPosZ{"cfgGenCutPosZ", false, "Apply a generated collision vertex-Z cut in generator-only mode"}; + Configurable cfgGenMaxPosZ{"cfgGenMaxPosZ", 7.f, "Maximum absolute generated vertex Z (cm)"}; + Configurable cfgGenCentralityCalibration{"cfgGenCentralityCalibration", false, "Collect forward multiplicity only; derive percentile lookup before analysis"}; + Configurable cfgGenForwardMultMax{"cfgGenForwardMultMax", 10000, "Largest integer forward multiplicity in calibration histogram"}; + Configurable> cfgGenCentralityLookup{"cfgGenCentralityLookup", {}, "Percentile indexed by integer generated forward multiplicity (fixed reference sample)"}; + Configurable cfgGenCentralityEstimator{"cfgGenCentralityEstimator", "", "Estimator of the supplied lookup: FT0M, FT0C or FV0A"}; + Configurable cfgGenUseEventWeight{"cfgGenUseEventWeight", false, "Use positive mcCollision.weight() consistently for generator calibration, events and matrices"}; + } genConfig; + Service pdg{}; Service ccdb{}; @@ -172,10 +202,14 @@ struct MeanPtFlucId { Gen_Charged, Gen_Pion, Gen_Kaon, - Gen_Proton + Gen_Proton, + Model_Charged, + Model_Pion, + Model_Kaon, + Model_Proton }; - static constexpr std::array Directories{ + static constexpr std::array Directories{ "QA/after/", "QA/Pion/", "QA/Kaon/", @@ -187,7 +221,11 @@ struct MeanPtFlucId { "Gen/Charged/", "Gen/Pion/", "Gen/Kaon/", - "Gen/Proton/"}; + "Gen/Proton/", + "Model/Charged/", + "Model/Pion/", + "Model/Kaon/", + "Model/Proton/"}; static constexpr auto const& dire = Directories; enum CorrectedMode { @@ -245,20 +283,20 @@ struct MeanPtFlucId { void init(InitContext const&) { - if (!std::isfinite(cfgTofShiftPi.value) || !std::isfinite(cfgTofShiftKa.value) || !std::isfinite(cfgTofShiftPr.value)) { + if (!std::isfinite(pidConfig.cfgTofShiftPi.value) || !std::isfinite(pidConfig.cfgTofShiftKa.value) || !std::isfinite(pidConfig.cfgTofShiftPr.value)) { LOGF(fatal, "TOF nSigma shifts must be finite: pion=%g, kaon=%g, proton=%g", - cfgTofShiftPi.value, cfgTofShiftKa.value, cfgTofShiftPr.value); + pidConfig.cfgTofShiftPi.value, pidConfig.cfgTofShiftKa.value, pidConfig.cfgTofShiftPr.value); } - if (nPtBins <= 0 || cfgCutPtMin >= cfgCutPtMax || cfgCutPiPtMin >= cfgCutPtMax || - cfgCutKaPtMin >= cfgCutPtMax || cfgCutPrPtMin >= cfgCutPtMax) { + if (nPtBins <= 0 || trackCuts.cfgCutPtMin >= trackCuts.cfgCutPtMax || pidConfig.cfgCutPiPtMin >= trackCuts.cfgCutPtMax || + pidConfig.cfgCutKaPtMin >= trackCuts.cfgCutPtMax || pidConfig.cfgCutPrPtMin >= trackCuts.cfgCutPtMax) { LOGF(fatal, "Invalid pT matrix binning: nPtBins=%d, max=%g, minima=(%g,%g,%g,%g)", - static_cast(nPtBins), static_cast(cfgCutPtMax), - static_cast(cfgCutPtMin), static_cast(cfgCutPiPtMin), - static_cast(cfgCutKaPtMin), static_cast(cfgCutPrPtMin)); + static_cast(nPtBins), static_cast(trackCuts.cfgCutPtMax), + static_cast(trackCuts.cfgCutPtMin), static_cast(pidConfig.cfgCutPiPtMin), + static_cast(pidConfig.cfgCutKaPtMin), static_cast(pidConfig.cfgCutPrPtMin)); } - if (cfgApplyEfficiencyCorrection) { + if (cfgApplyEfficiencyCorrection && (doprocessRun3 || doprocessMCRecoSimRun3)) { ccdb->setURL(cfgEfficiencyCCDBUrl.value); ccdb->setCaching(true); auto* efficiencyList = ccdb->getForTimeStamp(cfgEfficiencyCCDBPath.value, cfgEfficiencyTimestamp.value); @@ -282,10 +320,10 @@ struct MeanPtFlucId { auto validateEfficiencyMap = [&](const TH3* map, const char* name, double minPt) { constexpr double AxisTolerance = 1.e-6; constexpr double MaximumCentrality = 100.; - const bool coversEta = map->GetXaxis()->GetXmin() <= -cfgCutEta + AxisTolerance && - map->GetXaxis()->GetXmax() >= cfgCutEta - AxisTolerance; + const bool coversEta = map->GetXaxis()->GetXmin() <= -trackCuts.cfgCutEta + AxisTolerance && + map->GetXaxis()->GetXmax() >= trackCuts.cfgCutEta - AxisTolerance; const bool coversPt = map->GetYaxis()->GetXmin() <= minPt + AxisTolerance && - map->GetYaxis()->GetXmax() >= cfgCutPtMax - AxisTolerance; + map->GetYaxis()->GetXmax() >= trackCuts.cfgCutPtMax - AxisTolerance; const bool coversCentrality = map->GetZaxis()->GetXmin() <= AxisTolerance && map->GetZaxis()->GetXmax() >= MaximumCentrality - AxisTolerance; if (!coversEta || !coversPt || !coversCentrality) { @@ -297,20 +335,20 @@ struct MeanPtFlucId { map->GetXaxis()->GetXmin(), map->GetXaxis()->GetXmax(), map->GetYaxis()->GetXmin(), map->GetYaxis()->GetXmax(), map->GetZaxis()->GetXmin(), map->GetZaxis()->GetXmax(), - -static_cast(cfgCutEta), static_cast(cfgCutEta), - minPt, static_cast(cfgCutPtMax)); + -static_cast(trackCuts.cfgCutEta), static_cast(trackCuts.cfgCutEta), + minPt, static_cast(trackCuts.cfgCutPtMax)); } }; - validateEfficiencyMap(efficiencyWeightCh, chargedMapName, cfgCutPtMin); - validateEfficiencyMap(efficiencyWeightPi, pionMapName, cfgCutPiPtMin); - validateEfficiencyMap(efficiencyWeightKa, kaonMapName, cfgCutKaPtMin); - validateEfficiencyMap(efficiencyWeightPr, protonMapName, cfgCutPrPtMin); + validateEfficiencyMap(efficiencyWeightCh, chargedMapName, trackCuts.cfgCutPtMin); + validateEfficiencyMap(efficiencyWeightPi, pionMapName, pidConfig.cfgCutPiPtMin); + validateEfficiencyMap(efficiencyWeightKa, kaonMapName, pidConfig.cfgCutKaPtMin); + validateEfficiencyMap(efficiencyWeightPr, protonMapName, pidConfig.cfgCutPrPtMin); } - const AxisSpec axisPtCh{nPtBins, cfgCutPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtPi{nPtBins, cfgCutPiPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtKa{nPtBins, cfgCutKaPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; - const AxisSpec axisPtPr{nPtBins, cfgCutPrPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtCh{nPtBins, trackCuts.cfgCutPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPi{nPtBins, pidConfig.cfgCutPiPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtKa{nPtBins, pidConfig.cfgCutKaPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPr{nPtBins, pidConfig.cfgCutPrPtMin, trackCuts.cfgCutPtMax, "p_{T} (GeV/c)"}; const AxisSpec axisCol{3, 1, 4, ""}; const AxisSpec axisTrack{5, 1, 6, ""}; @@ -325,8 +363,10 @@ struct MeanPtFlucId { const AxisSpec axisMultCorr{multCorrBins, "N_{Corr} "}; const AxisSpec axisMultMC{multMCBins, "N_{Gen} "}; const AxisSpec axisMultFT0M{multFT0MBins, "N_{FT0M}"}; - const AxisSpec axisCentFT0M{101, 0, 101, "FT0M (%)"}; - const AxisSpec axisCent{centBins, "FT0M (%)"}; + // Match the estimator precedence used by getConfiguredCentrality(). + const std::string centralityLabel = eventCuts.cfgCentFT0C ? "FT0C (%)" : (eventCuts.cfgCentFV0A ? "FV0A (%)" : "FT0M (%)"); + const AxisSpec axisCentFT0M{101, 0, 101, centralityLabel}; + const AxisSpec axisCent{centBins, centralityLabel}; const AxisSpec axisVtxZ{80, -20., 20., "V_{Z} (cm)"}; const AxisSpec axisDCAz{dcaZBins, "DCA_{Z} (cm)"}; const AxisSpec axisDCAxy{dcaXYBins, "DCA_{XY} (cm)"}; @@ -353,7 +393,7 @@ struct MeanPtFlucId { hist.add("QA/before/h_VtxZ", "V_{Z}", kTH1D, {axisVtxZ}); hist.add("QA/before/h_NTPC", "N_{TPC}", kTH1D, {axisMultTPC}); hist.add("QA/before/h_NFT0M", "FT0M Multiplicity", kTH1D, {axisMultFT0M}); - hist.add("QA/before/h_CentM", "FT0M (%)", kTH1D, {axisCentFT0M}); + hist.add("QA/before/h_CentM", centralityLabel.c_str(), kTH1D, {axisCentFT0M}); hist.add("QA/before/h2_TPCSignal", "TPC Signal", tpcSignalHist); hist.add("QA/before/h2_TOFSignal", "TOF Signal", tofSignalHist); @@ -374,10 +414,10 @@ struct MeanPtFlucId { hist.add("QA/after/h_TPCChi2perCluster", "TPC #Chi^{2}/Cluster", kTH1D, {axisChi2}); hist.add("QA/after/h_ITSChi2perCluster", "ITS #Chi^{2}/Cluster", kTH1D, {axisChi2}); hist.add("QA/after/h_crossedTPC", "Crossed TPC", kTH1D, {axisCrossedTPC}); - hist.add("QA/after/h2_NTPC_CentM", "N_{TPC} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultTPC}}); + hist.add("QA/after/h2_NTPC_CentM", ("N_{TPC} vs " + centralityLabel).c_str(), kTH2D, {{axisCentFT0M}, {axisMultTPC}}); hist.add("QA/after/h2_NTPC_NFT0M", "N_{TPC} vs N_{FT0M}", kTH2D, {{axisMultFT0M}, {axisMultTPC}}); hist.add("QA/after/p_NTPC_NFT0M", "N_{TPC} vs N_{FT0M} (Profile)", kTProfile, {axisMultFT0M}); - hist.add("QA/after/p_NTPC_CentM", "N_{TPC} vs FT0M(%) (Profile)", kTProfile, {axisCentFT0M}); + hist.add("QA/after/p_NTPC_CentM", ("N_{TPC} vs " + centralityLabel + " (Profile)").c_str(), kTProfile, {axisCentFT0M}); hist.add("QA/after/h_DCAxy_primary", "DCA_{XY} (Primary)", kTH1D, {axisDCAxy}); hist.add("QA/after/h_DCAz_primary", "DCA_{Z} (Primary)", kTH1D, {axisDCAz}); hist.add("QA/after/h_DCAxy_secondary", "DCA_{XY} (Secondary)", kTH1D, {axisDCAxy}); @@ -387,7 +427,7 @@ struct MeanPtFlucId { hist.add("QA/after/h2_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTH2D, {{axisMultTPC}, {axisMultMC}}); hist.add("QA/after/p_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTProfile, {axisMultTPC}); hist.add("QA/after/h2_Ncorr_NSim", "Truth vs Corrected Multiplicty TPC", kTH2D, {{axisMultCorr}, {axisMultMC}}); - hist.add("QA/after/h2_Ncorr_CentM", "N_{corr} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultCorr}}); + hist.add("QA/after/h2_Ncorr_CentM", ("N_{corr} vs " + centralityLabel).c_str(), kTH2D, {{axisCentFT0M}, {axisMultCorr}}); }; addEventQAHistograms(); @@ -427,7 +467,7 @@ struct MeanPtFlucId { hist.add("QA/Charged/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtCh}}); hist.add("QA/Charged/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); - hist.add("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); hist.add("QA/Charged/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtCh}}); }; addTrackQAHistograms(); @@ -438,7 +478,7 @@ struct MeanPtFlucId { hist.add("QA/Pion/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); - hist.add("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); hist.add("QA/Pion/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPi}}); hist.add("QA/Pion/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPi}); hist.add("QA/Pion/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPi}); @@ -455,7 +495,7 @@ struct MeanPtFlucId { hist.add("QA/Kaon/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); - hist.add("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); hist.add("QA/Kaon/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtKa}}); hist.add("QA/Kaon/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtKa}); hist.add("QA/Kaon/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtKa}); @@ -472,7 +512,7 @@ struct MeanPtFlucId { hist.add("QA/Proton/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); - hist.add("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M", ("Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};" + centralityLabel).c_str(), kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); hist.add("QA/Proton/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPr}}); hist.add("QA/Proton/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPr}); hist.add("QA/Proton/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPr}); @@ -567,6 +607,53 @@ struct MeanPtFlucId { }; addGeneratedHistograms(); + if (doprocessMCGenOnly) { + if (genConfig.cfgGenCutPosZ && (!std::isfinite(genConfig.cfgGenMaxPosZ.value) || genConfig.cfgGenMaxPosZ <= 0.f)) { + LOGF(fatal, "cfgGenMaxPosZ must be finite and positive"); + } + const std::string estimator = eventCuts.cfgCentFT0C ? "FT0C" : (eventCuts.cfgCentFV0A ? "FV0A" : "FT0M"); + constexpr size_t MinimumCentralityLookupEntries = 2; + constexpr double MinimumCentralityPercentile = 0.; + constexpr double MaximumCentralityPercentile = 100.; + if (genConfig.cfgGenForwardMultMax < 1) { + LOGF(fatal, "cfgGenForwardMultMax must be positive"); + } + if (!genConfig.cfgGenCentralityCalibration) { + const auto& lookup = genConfig.cfgGenCentralityLookup.value; + if (genConfig.cfgGenCentralityEstimator.value != estimator || lookup.size() < MinimumCentralityLookupEntries) { + LOGF(fatal, "Supply a centrality lookup for %s, or enable cfgGenCentralityCalibration for the first pass", estimator.c_str()); + } + for (size_t i = 0; i < lookup.size(); ++i) { + if (!std::isfinite(lookup[i]) || lookup[i] < MinimumCentralityPercentile || lookup[i] > MaximumCentralityPercentile || (i != 0 && lookup[i] > lookup[i - 1])) { + LOGF(fatal, "Generator centrality lookup must be finite, within [0,100] and non-increasing"); + } + } + } + const AxisSpec axisModelCent{centBins, "Generated " + estimator + " percentile (%)"}; + const AxisSpec axisForward{genConfig.cfgGenForwardMultMax.value + 1, -0.5, genConfig.cfgGenForwardMultMax.value + 0.5, "Generated " + estimator + " multiplicity"}; + hist.add("Model/h_ForwardMult", "Reference forward multiplicity after generator event cuts", kTH1D, {axisForward}); + hist.add("Model/h_Centrality", "Events used for matrices (including zero selected particles)", kTH1D, {axisModelCent}); + hist.add("Model/h_CentralityStatus", "Centrality lookup;Status;Events", kTH1D, {{2, 0.5, 2.5}}); + hist.get(HIST("Model/h_CentralityStatus"))->GetXaxis()->SetBinLabel(1, "Calibrated"); + hist.get(HIST("Model/h_CentralityStatus"))->GetXaxis()->SetBinLabel(2, "Outside lookup"); + const AxisSpec axisModelMult{multMCBins, "N_{ch}^{gen} (physical primaries in |#eta| acceptance, all p_{T})"}; + hist.add("Model/h_EventCount", "Generated events;Selection;Events", kTH1D, {{2, 0.5, 2.5}}); + hist.get(HIST("Model/h_EventCount"))->GetXaxis()->SetBinLabel(1, "All generated"); + hist.get(HIST("Model/h_EventCount"))->GetXaxis()->SetBinLabel(2, "Accepted"); + hist.add("Model/h_VtxZ", "Generated vertex Z", kTH1D, {axisVtxZ}); + hist.add("Model/h_Nch", "Accepted events (including zero selected particles)", kTH1D, {axisModelMult}); + auto addModelSpecies = [&](const std::string& directory, const AxisSpec& axisPtId) { + hist.add(directory + "h_Pt", "Generated physical primaries", kTH1D, {axisPtId}); + hist.add(directory + "h_Eta", "Generated physical primaries", kTH1D, {axisEta}); + hist.add(directory + "hN1Matrix", "Generated single-particle density", kTH2D, {axisModelCent, axisPtId}); + hist.add(directory + "hN2Matrix", "Generated ordered-pair density", kTH3D, {axisModelCent, axisPtId, axisPtId}); + }; + addModelSpecies("Model/Charged/", axisPtCh); + addModelSpecies("Model/Pion/", axisPtPi); + addModelSpecies("Model/Kaon/", axisPtKa); + addModelSpecies("Model/Proton/", axisPtPr); + } + hist.add("QA/h_collisions_info", "Collisions info", kTH1D, {axisCol}); hist.add("Gen/h_collisions_info", "Collisions info", kTH1D, {axisCol}); hist.add("Gen/h_collision_recgen", "Number of Collisions ", kTH1D, {axisCol}); @@ -586,7 +673,7 @@ struct MeanPtFlucId { hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColSel8, "kSelColSel8"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColNoSameBunchPileup, "kSelColNoSameBunchPileup"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsVertexITSTPC, "kSelColIsVertexITSTPC"); - if (cfgLightIonCuts) { + if (eventCuts.cfgLightIonCuts) { hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodITSLayersAll, "kSelColIsGoodITSLayersAll"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodZvtxFT0vsPV, "kSelColIsGoodZvtxFT0vsPV"); } @@ -596,54 +683,62 @@ struct MeanPtFlucId { int nTPC = 0, nFT0M = 0; int nSim = 0; + template + float getConfiguredCentrality(T const& col) const + { + if (eventCuts.cfgCentFT0C.value) { + return col.centFT0C(); + } + if (eventCuts.cfgCentFV0A.value) { + return col.centFV0A(); + } + return col.centFT0M(); + } + // Event selection cuts: template bool selRun3Col(T const& col) { - if (cfgCentFT0C) { - centFT0M = col.centFT0C(); - } else { - centFT0M = col.centFT0M(); - } + centFT0M = getConfiguredCentrality(col); nTPC = col.multNTracksHasTPC(); nFT0M = col.multFT0M(); hist.fill(HIST("QA/after/h_counts_evSelCuts"), kBeforeSelCol); - if (cfgPosZ) { - if (std::abs(col.posZ()) > cfgCutPosZ) { + if (eventCuts.cfgPosZ) { + if (std::abs(col.posZ()) > eventCuts.cfgCutPosZ) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColPosZ); } - if (cfgSel8) { + if (eventCuts.cfgSel8) { if (!col.sel8()) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColSel8); } - if (cfgNoSameBunchPileup) { + if (eventCuts.cfgNoSameBunchPileup) { if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColNoSameBunchPileup); } - if (cfgIsVertexITSTPC) { + if (eventCuts.cfgIsVertexITSTPC) { if (!col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsVertexITSTPC); } - if (cfgLightIonCuts) { - if (cfgIsGoodITSLayersAll) { + if (eventCuts.cfgLightIonCuts) { + if (eventCuts.cfgIsGoodITSLayersAll) { if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsGoodITSLayersAll); } - if (cfgIsGoodZvtxFT0vsPV) { + if (eventCuts.cfgIsGoodZvtxFT0vsPV) { if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } @@ -658,15 +753,19 @@ struct MeanPtFlucId { template bool selTrack(T const& track) { - if (!track.isGlobalTrack()) { + if (trackCuts.cfgGlobalTrack && !track.isGlobalTrack()) { return false; } - if (track.pt() < cfgCutPtMin) { + if (trackCuts.cfgGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { return false; } - if (track.pt() >= cfgCutPtMax) { + if (track.pt() < trackCuts.cfgCutPtMin) { + return false; + } + + if (track.pt() >= trackCuts.cfgCutPtMax) { return false; } @@ -674,16 +773,41 @@ struct MeanPtFlucId { return false; } - const float maxDca = cfgDcaPtDepOffset + cfgDcaPtDepScale / track.pt(); - // if (std::abs(track.dcaXY()) >= maxDca) { - // return false; - // } + const float maxDca = trackCuts.cfgDcaPtDepOffset + trackCuts.cfgDcaPtDepScale / track.pt(); + + if (trackCuts.cfgDcaXYPtDep) { + if (std::abs(track.dcaXY()) >= maxDca) { + return false; + } + } else if (trackCuts.cfgDcaXY) { + if (std::abs(track.dcaXY()) >= trackCuts.cfgCutDCAxy) { + return false; + } + } + + if (trackCuts.cfgDcaZPtDep) { + if (std::abs(track.dcaZ()) >= maxDca) { + return false; + } + } else if (trackCuts.cfgDcaZ) { + if (std::abs(track.dcaZ()) >= trackCuts.cfgCutDCAz) { + return false; + } + } + + if (std::abs(track.eta()) >= trackCuts.cfgCutEta) { + return false; + } + + if (trackCuts.cfgTPCChi2NCl && track.tpcChi2NCl() > trackCuts.cfgCutTPCChi2NCl) { + return false; + } - if (std::abs(track.dcaZ()) >= maxDca) { + if (trackCuts.cfgITSChi2NCl && track.itsChi2NCl() > trackCuts.cfgCutITSChi2NCl) { return false; } - if (std::abs(track.eta()) >= cfgCutEta) { + if (trackCuts.cfgNTPCCrossedRows && track.tpcNClsCrossedRows() < trackCuts.cfgCutNCrossedRowsTPC) { return false; } @@ -694,11 +818,11 @@ struct MeanPtFlucId { template bool rejectTracks(T const& track) { - return ((track.tpcNSigmaEl()) > -cfgCutNSig3 && - (track.tpcNSigmaEl()) < cfgCutNSig5) && - (std::fabs(track.tpcNSigmaPi()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaKa()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaPr()) > cfgCutNSig3); + return ((track.tpcNSigmaEl()) > -pidConfig.cfgCutNSig3 && + (track.tpcNSigmaEl()) < pidConfig.cfgCutNSig5) && + (std::fabs(track.tpcNSigmaPi()) > pidConfig.cfgCutNSig3 && + std::fabs(track.tpcNSigmaKa()) > pidConfig.cfgCutNSig3 && + std::fabs(track.tpcNSigmaPr()) > pidConfig.cfgCutNSig3); } // Share the corrected hypotheses between PID selection and selected-particle QA. @@ -708,9 +832,9 @@ struct MeanPtFlucId { { std::array values = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; if (track.hasTOF()) { - values[static_cast(PIDType::kPions)] -= cfgTofShiftPi.value; - values[static_cast(PIDType::kKaons)] -= cfgTofShiftKa.value; - values[static_cast(PIDType::kProtons)] -= cfgTofShiftPr.value; + values[static_cast(PIDType::kPions)] -= pidConfig.cfgTofShiftPi.value; + values[static_cast(PIDType::kKaons)] -= pidConfig.cfgTofShiftKa.value; + values[static_cast(PIDType::kProtons)] -= pidConfig.cfgTofShiftPr.value; } return values; } @@ -730,16 +854,16 @@ struct MeanPtFlucId { switch (species) { case PIDType::kPions: - tpcSelectionCut = cfgSelCutNSigTpcPi; - tofSelectionCut = cfgSelCutNSigTofPi; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcPi; + tofSelectionCut = pidConfig.cfgSelCutNSigTofPi; break; case PIDType::kKaons: - tpcSelectionCut = cfgSelCutNSigTpcKa; - tofSelectionCut = cfgSelCutNSigTofKa; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcKa; + tofSelectionCut = pidConfig.cfgSelCutNSigTofKa; break; case PIDType::kProtons: - tpcSelectionCut = cfgSelCutNSigTpcPr; - tofSelectionCut = cfgSelCutNSigTofPr; + tpcSelectionCut = pidConfig.cfgSelCutNSigTpcPr; + tofSelectionCut = pidConfig.cfgSelCutNSigTofPr; break; default: return false; @@ -750,7 +874,7 @@ struct MeanPtFlucId { const bool passesTOF = selectedTofNSigma < tofSelectionCut && std::abs(tofNSigma[rejSpecies1]) > selectedTofNSigma && std::abs(tofNSigma[rejSpecies2]) > selectedTofNSigma; - const bool passesTPC = std::abs(tpcNSigma[selSpecies]) < cfgCutNSigTpcTof; + const bool passesTPC = std::abs(tpcNSigma[selSpecies]) < pidConfig.cfgCutNSigTpcTof; return passesTPC && passesTOF; } @@ -780,7 +904,7 @@ struct MeanPtFlucId { hist.fill(HIST("QA/before/h_VtxZ"), col.posZ()); hist.fill(HIST("QA/before/h_Counts"), 2); hist.fill(HIST("QA/before/h_NTPC"), col.multNTracksHasTPC()); - hist.fill(HIST("QA/before/h_CentM"), col.centFT0M()); + hist.fill(HIST("QA/before/h_CentM"), getConfiguredCentrality(col)); hist.fill(HIST("QA/before/h_NFT0M"), col.multFT0M()); } @@ -943,10 +1067,10 @@ struct MeanPtFlucId { } template - void fillMatrices(const std::vector& tracks, double ptMin) + void fillMatrices(const std::vector& tracks, double ptMin, double eventCoordinate, double eventWeight = 1.) { const int nBins = nPtBins; - const double binWidth = (cfgCutPtMax - ptMin) / nBins; + const double binWidth = (trackCuts.cfgCutPtMax - ptMin) / nBins; matrixNInBin.assign(nBins, 0); matrixOccupiedBins.clear(); matrixOccupiedBins.reserve(std::min(static_cast(nBins), tracks.size())); @@ -964,7 +1088,7 @@ struct MeanPtFlucId { for (const int& ptBin : matrixOccupiedBins) { const double ptCenter = ptMin + (ptBin + 0.5) * binWidth; - hist.fill(HIST(dire[Mode]) + HIST("hN1Matrix"), centFT0M, ptCenter, static_cast(matrixNInBin[ptBin])); + hist.fill(HIST(dire[Mode]) + HIST("hN1Matrix"), eventCoordinate, ptCenter, eventWeight * static_cast(matrixNInBin[ptBin])); } for (size_t i = 0; i < matrixOccupiedBins.size(); ++i) { @@ -972,19 +1096,25 @@ struct MeanPtFlucId { const double ptCenter1 = ptMin + (ptBin1 + 0.5) * binWidth; const double nPairDiagonal = static_cast(matrixNInBin[ptBin1]) * (matrixNInBin[ptBin1] - 1); if (nPairDiagonal > 0.0) { - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter1, nPairDiagonal); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter1, ptCenter1, eventWeight * nPairDiagonal); } for (size_t j = i + 1; j < matrixOccupiedBins.size(); ++j) { const int ptBin2 = matrixOccupiedBins[j]; const double ptCenter2 = ptMin + (ptBin2 + 0.5) * binWidth; const double nPair = static_cast(matrixNInBin[ptBin1]) * matrixNInBin[ptBin2]; - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter2, nPair); - hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter2, ptCenter1, nPair); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter1, ptCenter2, eventWeight * nPair); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), eventCoordinate, ptCenter2, ptCenter1, eventWeight * nPair); } } } + template + void fillMatrices(const std::vector& tracks, double ptMin) + { + fillMatrices(tracks, ptMin, centFT0M); + } + bool getEfficiencyWeight(PIDType species, double pt, double eta, double& weight) const { TH3* weightMap = nullptr; @@ -1023,7 +1153,7 @@ struct MeanPtFlucId { void fillCorrectedMatrices(const std::vector& tracks, double ptMin) { const int nBins = nPtBins; - const double binWidth = (cfgCutPtMax - ptMin) / nBins; + const double binWidth = (trackCuts.cfgCutPtMax - ptMin) / nBins; matrixWInBin.assign(nBins, 0.); matrixW2InBin.assign(nBins, 0.); matrixOccupiedBins.clear(); @@ -1189,14 +1319,14 @@ struct MeanPtFlucId { fillBeforePIDQAHistos(track); - if (cfgRejTrk && rejectTracks(track)) { + if (pidConfig.cfgRejTrk && rejectTracks(track)) { continue; } - auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, cfgCutPiThrsldP, tofNSigma); - auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, cfgCutKaThrsldP, tofNSigma); - auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, cfgCutPrThrsldP, tofNSigma); + auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, pidConfig.cfgCutPiThrsldP, tofNSigma); + auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, pidConfig.cfgCutKaThrsldP, tofNSigma); + auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, pidConfig.cfgCutPrThrsldP, tofNSigma); - if (selIDPion && track.pt() >= cfgCutPiPtMin) { + if (selIDPion && track.pt() >= pidConfig.cfgCutPiPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { @@ -1206,7 +1336,7 @@ struct MeanPtFlucId { } fillIdParticleQAHistos(track, ptMC, etaMC, rapPi, nSigmaTPCPi, nSigmaTOFPi, pionTracks); } - if (selIDKaon && track.pt() >= cfgCutKaPtMin) { + if (selIDKaon && track.pt() >= pidConfig.cfgCutKaPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { @@ -1216,7 +1346,7 @@ struct MeanPtFlucId { } fillIdParticleQAHistos(track, ptMC, etaMC, rapKa, nSigmaTPCKa, nSigmaTOFKa, kaonTracks); } - if (selIDProton && track.pt() >= cfgCutPrPtMin) { + if (selIDProton && track.pt() >= pidConfig.cfgCutPrPtMin) { if (cfgApplyEfficiencyCorrection) { double weight = 0.; if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { @@ -1230,7 +1360,7 @@ struct MeanPtFlucId { if constexpr (RecoFlag) { auto mc = track.template mcParticle_as(); int pid = mc.pdgCode(); - if (selIDPion && track.pt() >= cfgCutPiPtMin) { + if (selIDPion && track.pt() >= pidConfig.cfgCutPiPtMin) { if (std::abs(pid) == kPiPlus) { hist.fill(HIST("QA/Pion/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapPi, phi, pid, kPiPlus, kPiMinus); @@ -1250,7 +1380,7 @@ struct MeanPtFlucId { } } } - if (selIDKaon && track.pt() >= cfgCutKaPtMin) { + if (selIDKaon && track.pt() >= pidConfig.cfgCutKaPtMin) { if (std::abs(pid) == kKPlus) { hist.fill(HIST("QA/Kaon/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapKa, phi, pid, kKPlus, kKMinus); @@ -1270,7 +1400,7 @@ struct MeanPtFlucId { } } } - if (selIDProton && track.pt() >= cfgCutPrPtMin) { + if (selIDProton && track.pt() >= pidConfig.cfgCutPrPtMin) { if (std::abs(pid) == kProton) { hist.fill(HIST("QA/Proton/h_PtTruth"), track.pt()); fillPtMCHist(false, pt, eta, rapPr, phi, pid, kProton, kProtonBar); @@ -1292,22 +1422,22 @@ struct MeanPtFlucId { } } } - fillMatrices(chargedTracks, cfgCutPtMin); - fillMatrices(pionTracks, cfgCutPiPtMin); - fillMatrices(kaonTracks, cfgCutKaPtMin); - fillMatrices(protonTracks, cfgCutPrPtMin); + fillMatrices(chargedTracks, trackCuts.cfgCutPtMin); + fillMatrices(pionTracks, pidConfig.cfgCutPiPtMin); + fillMatrices(kaonTracks, pidConfig.cfgCutKaPtMin); + fillMatrices(protonTracks, pidConfig.cfgCutPrPtMin); if (cfgApplyEfficiencyCorrection) { - fillCorrectedMatrices(chargedTracksCorrected, cfgCutPtMin); - fillCorrectedMatrices(pionTracksCorrected, cfgCutPiPtMin); - fillCorrectedMatrices(kaonTracksCorrected, cfgCutKaPtMin); - fillCorrectedMatrices(protonTracksCorrected, cfgCutPrPtMin); + fillCorrectedMatrices(chargedTracksCorrected, trackCuts.cfgCutPtMin); + fillCorrectedMatrices(pionTracksCorrected, pidConfig.cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksCorrected, pidConfig.cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksCorrected, pidConfig.cfgCutPrPtMin); if constexpr (RecoFlag) { if (!cfgUseCombinedPurityEfficiencyWeight) { - fillCorrectedMatrices(chargedTracksTruthMatchedCorrected, cfgCutPtMin); - fillCorrectedMatrices(pionTracksTruthMatchedCorrected, cfgCutPiPtMin); - fillCorrectedMatrices(kaonTracksTruthMatchedCorrected, cfgCutKaPtMin); - fillCorrectedMatrices(protonTracksTruthMatchedCorrected, cfgCutPrPtMin); + fillCorrectedMatrices(chargedTracksTruthMatchedCorrected, trackCuts.cfgCutPtMin); + fillCorrectedMatrices(pionTracksTruthMatchedCorrected, pidConfig.cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksTruthMatchedCorrected, pidConfig.cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksTruthMatchedCorrected, pidConfig.cfgCutPrPtMin); } } } @@ -1318,7 +1448,7 @@ struct MeanPtFlucId { { int multiplicity = 0; for (auto const& mcPart : mcParticles) { - if (!mcPart.isPhysicalPrimary() || std::abs(mcPart.eta()) >= cfgCutEta) { + if (!mcPart.isPhysicalPrimary() || std::abs(mcPart.eta()) >= trackCuts.cfgCutEta) { continue; } auto* particle = pdg->GetParticle(mcPart.pdgCode()); @@ -1331,11 +1461,11 @@ struct MeanPtFlucId { bool passesNtpcEventCut() { - if (!cfgNtpcEventCut) { + if (!eventCuts.cfgNtpcEventCut) { return true; } - const float lower = cfgP2L * nSim * nSim + cfgP1L * nSim - cfgP0L; - const float upper = cfgP2U * nSim * nSim + cfgP1U * nSim + cfgP0U; + const float lower = eventCuts.cfgP2L * nSim * nSim + eventCuts.cfgP1L * nSim - eventCuts.cfgP0L; + const float upper = eventCuts.cfgP2U * nSim * nSim + eventCuts.cfgP1U * nSim + eventCuts.cfgP0U; return nTPC >= lower && nTPC <= upper; } @@ -1368,11 +1498,11 @@ struct MeanPtFlucId { eta = mcPart.eta(); phi = mcPart.phi(); - if (std::abs(eta) < cfgCutEta) { + if (std::abs(eta) < trackCuts.cfgCutEta) { nSim++; } - if (pt >= cfgCutPtMin && pt < cfgCutPtMax && std::abs(eta) < cfgCutEta) { + if (pt >= trackCuts.cfgCutPtMin && pt < trackCuts.cfgCutPtMax && std::abs(eta) < trackCuts.cfgCutEta) { chargedTracksGen.push_back({pt}); hist.fill(HIST("Gen/Charged/h_PtTruth"), pt); hist.fill(HIST("Gen/Charged/h_PtTruth_e"), pt); @@ -1385,25 +1515,25 @@ struct MeanPtFlucId { rap = mcPart.y(); auto pid = mcPart.pdgCode(); - if (std::abs(pid) == kPiPlus && pt >= cfgCutPiPtMin) { + if (std::abs(pid) == kPiPlus && pt >= pidConfig.cfgCutPiPtMin) { pionTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kPiPlus, kPiMinus); } - if (std::abs(pid) == kKPlus && pt >= cfgCutKaPtMin) { + if (std::abs(pid) == kKPlus && pt >= pidConfig.cfgCutKaPtMin) { kaonTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kKPlus, kKMinus); } - if (std::abs(pid) == kProton && pt >= cfgCutPrPtMin) { + if (std::abs(pid) == kProton && pt >= pidConfig.cfgCutPrPtMin) { protonTracksGen.push_back({pt}); fillPtMCHist(true, pt, eta, rap, phi, pid, kProton, kProtonBar); } } } - fillMatrices(chargedTracksGen, cfgCutPtMin); - fillMatrices(pionTracksGen, cfgCutPiPtMin); - fillMatrices(kaonTracksGen, cfgCutKaPtMin); - fillMatrices(protonTracksGen, cfgCutPrPtMin); + fillMatrices(chargedTracksGen, trackCuts.cfgCutPtMin); + fillMatrices(pionTracksGen, pidConfig.cfgCutPiPtMin); + fillMatrices(kaonTracksGen, pidConfig.cfgCutKaPtMin); + fillMatrices(protonTracksGen, pidConfig.cfgCutPrPtMin); hist.fill(HIST("Gen/h_Counts"), 2); hist.fill(HIST("Gen/h_VtxZ"), mcCol.posZ()); @@ -1451,14 +1581,14 @@ struct MeanPtFlucId { aod::pidTOFFullPi, aod::pidTPCFullPi, aod::pidTOFFullPr, aod::pidTPCFullPr, aod::pidTOFFullKa, aod::pidTPCFullKa, aod::pidTOFFullEl, aod::pidTPCFullEl, aod::pidTOFbeta, aod::pidTOFmass>; - using MyRun3Collisions = soa::Join; - using MyRun3MCCollisions = soa::Join; + using MyRun3Collisions = soa::Join; + using MyRun3MCCollisions = soa::Join; using MyMCTracks = soa::Join; SliceCache cache; - Preslice perCollision = aod::track::collisionId; + PresliceOptional perCollision = aod::track::collisionId; void processRun3(MyRun3Collisions::iterator const& col, MyAllTracks const& tracks) { @@ -1472,6 +1602,115 @@ struct MeanPtFlucId { analyzeMC(mcCol, cols, tracks, mcParts); } PROCESS_SWITCH(MeanPtFlucId, processMCRecoSimRun3, "process MC Reconstructed & Truth Run-3", true); + + template + int countGenForwardMultiplicity(M const& particles) + { + constexpr double MinimumChargedParticleCharge = 1.e-3; + constexpr double Ft0aEtaMin = 3.5; + constexpr double Ft0aEtaMax = 4.9; + constexpr double Ft0cEtaMin = -3.3; + constexpr double Ft0cEtaMax = -2.1; + constexpr double Fv0aEtaMin = 2.2; + constexpr double Fv0aEtaMax = 5.0; + int ft0a = 0, ft0c = 0, fv0a = 0; + // Match Common/Tools/Multiplicity/MultModule.h::collisionProcessMonteCarlo. + // No analysis pT/eta cuts: these are forward charged physical primaries. + for (auto const& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || std::abs(definition->Charge()) < MinimumChargedParticleCharge) { + continue; + } + const double eta = particle.eta(); + ft0a += (Ft0aEtaMin < eta && eta < Ft0aEtaMax); + ft0c += (Ft0cEtaMin < eta && eta < Ft0cEtaMax); + fv0a += (Fv0aEtaMin < eta && eta < Fv0aEtaMax); + } + return eventCuts.cfgCentFT0C ? ft0c : (eventCuts.cfgCentFV0A ? fv0a : ft0a + ft0c); + } + + double getGenCentrality(int forwardMultiplicity) const + { + const auto& lookup = genConfig.cfgGenCentralityLookup.value; + if (forwardMultiplicity < 0 || static_cast(forwardMultiplicity) >= lookup.size()) { + return -1.; // Do not extrapolate beyond the reference distribution. + } + return std::min(lookup[forwardMultiplicity], std::nextafter(100., 0.)); + } + + // Only MC tables are inputs: no reconstructed event, track, centrality or PID selection. + // The framework groups mcParts by the current mcCol through mcCollisionId. + void processMCGenOnly(aod::McCollisions::iterator const& mcCol, aod::McParticles const& mcParts) + { + const double eventWeight = genConfig.cfgGenUseEventWeight ? mcCol.weight() : 1.; + if (!std::isfinite(eventWeight) || eventWeight <= 0.) { + LOGF(fatal, "Generator percentile analysis requires finite positive event weights"); + } + hist.fill(HIST("Model/h_EventCount"), 1, eventWeight); + if (genConfig.cfgGenCutPosZ && std::abs(mcCol.posZ()) > genConfig.cfgGenMaxPosZ) { + return; + } + hist.fill(HIST("Model/h_EventCount"), 2, eventWeight); + hist.fill(HIST("Model/h_VtxZ"), mcCol.posZ(), eventWeight); + const int forwardMultiplicity = countGenForwardMultiplicity(mcParts); + hist.fill(HIST("Model/h_ForwardMult"), forwardMultiplicity, eventWeight); + if (genConfig.cfgGenCentralityCalibration) { + return; + } + const double centrality = getGenCentrality(forwardMultiplicity); + if (centrality < 0.) { + hist.fill(HIST("Model/h_CentralityStatus"), 2, eventWeight); + return; + } + hist.fill(HIST("Model/h_CentralityStatus"), 1, eventWeight); + hist.fill(HIST("Model/h_Centrality"), centrality, eventWeight); + const int multiplicity = countSimMultiplicity(mcParts); + hist.fill(HIST("Model/h_Nch"), multiplicity, eventWeight); + + std::array, 4> modelTracks; + for (auto const& particle : mcParts) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || definition->Charge() == 0.) { + continue; + } + const float pt = particle.pt(); + const float eta = particle.eta(); + if (!std::isfinite(pt) || !std::isfinite(eta) || + pt < trackCuts.cfgCutPtMin || pt >= trackCuts.cfgCutPtMax || std::abs(eta) >= trackCuts.cfgCutEta) { + continue; + } + modelTracks[0].push_back({pt}); + hist.fill(HIST("Model/Charged/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Charged/h_Eta"), eta, eventWeight); + const int code = std::abs(particle.pdgCode()); + if (code == kPiPlus && pt >= pidConfig.cfgCutPiPtMin) { + modelTracks[1].push_back({pt}); + hist.fill(HIST("Model/Pion/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Pion/h_Eta"), eta, eventWeight); + } + if (code == kKPlus && pt >= pidConfig.cfgCutKaPtMin) { + modelTracks[2].push_back({pt}); + hist.fill(HIST("Model/Kaon/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Kaon/h_Eta"), eta, eventWeight); + } + if (code == kProton && pt >= pidConfig.cfgCutPrPtMin) { + modelTracks[3].push_back({pt}); + hist.fill(HIST("Model/Proton/h_Pt"), pt, eventWeight); + hist.fill(HIST("Model/Proton/h_Eta"), eta, eventWeight); + } + } + fillMatrices(modelTracks[0], trackCuts.cfgCutPtMin, centrality, eventWeight); + fillMatrices(modelTracks[1], pidConfig.cfgCutPiPtMin, centrality, eventWeight); + fillMatrices(modelTracks[2], pidConfig.cfgCutKaPtMin, centrality, eventWeight); + fillMatrices(modelTracks[3], pidConfig.cfgCutPrPtMin, centrality, eventWeight); + } + PROCESS_SWITCH(MeanPtFlucId, processMCGenOnly, "Independent generator-only model comparison", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& context) From 30e1261dfd12f7902ba387d58b48a82949b637cf Mon Sep 17 00:00:00 2001 From: Marvin Hemmer <53471402+mhemmer-cern@users.noreply.github.com> Date: Mon, 9 Nov 2026 15:09:31 +0100 Subject: [PATCH 625/648] [PWGEM] Add MC task for photon efficiency and purity studies (#18658) --- PWGEM/PhotonMeson/Tasks/CMakeLists.txt | 5 + PWGEM/PhotonMeson/Tasks/photonMcTask.cxx | 649 +++++++++++++++++++++++ 2 files changed, 654 insertions(+) create mode 100644 PWGEM/PhotonMeson/Tasks/photonMcTask.cxx diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index cd9513dcbe3..850a578b3e4 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -181,6 +181,11 @@ o2physics_add_dpl_workflow(photon-reso-task PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::EMCALBase O2::EMCALCalib O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(photon-mc-task + SOURCES photonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::EMCALBase O2::EMCALCalib O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(photonhbt SOURCES photonhbt.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore diff --git a/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx new file mode 100644 index 00000000000..6378574b705 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/photonMcTask.cxx @@ -0,0 +1,649 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonMcTask.cxx +/// \brief Analysis task for to obtain photon acceptance, efficiency and purity for photons from PCM and/or EMCal +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +enum class MapLevel { + kGood = 1, + kNoBad = 2, + kInEMC = 3, + kAll = 4 +}; + +// all values here have a little bit of headroom +constexpr float EmcalMaxEta = 0.75f; // it is actuall 0.7 +constexpr float DcalMinEta = 0.2f; // it is actuall 0.23 + +constexpr float EmcalLowerPhi = 1.4f - 0.01f; +constexpr float EmcalUpperPhi = 3.26f + 0.01f; +constexpr float DcalLowerPhi = 4.54f + 0.01f; +constexpr float DcalUpperPhosGap = 5.58f + 0.01f; +constexpr float DcalUpperPhi = 5.70f + 0.01f; + +struct PhotonMcTask { + Configurable cfgMaxEta{"cfgMaxEta", 0.9f, "max. eta for generated particles"}; + + // configurable axis + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {400, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisERelative{"thnConfigAxisERelative", {600, -1., 5.}, "(E rec - E true) / E true axis"}; + ConfigurableAxis thnConfigAxisPRelative{"thnConfigAxisPRelative", {300, -1., 2.}, "(P rec - P true) / P true axis"}; + ConfigurableAxis thnConfigAxisEtaRelative{"thnConfigAxisEtaRelative", {300, -1., 2.}, "(eta rec - eta true) / eta true axis"}; + ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal primary track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal primary track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal secondary track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal secondary track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal primary track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "Flag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + using PcmPhotons = soa::Join; + + using PcmMcLegs = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + Partition mcPhotons = aod::mcparticle::pdgCode == static_cast(PDG_t::kGamma); + PresliceUnsorted perMcEvent = aod::emmcparticle::emmceventId; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + o2::framework::Service ccdb{}; + int mRunNumber{-1}; + float dBz{0.f}; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + fEMEventCut.SetFT0COccupancyRange(eventcuts.cfgFT0COccupancyMin, eventcuts.cfgFT0COccupancyMax); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + fEMEventCut.addEventHistograms(®istry); + + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPGen{thnConfigAxisPt, "#it{p}_{Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPRec{thnConfigAxisPt, "#it{p}_{Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPRelative{thnConfigAxisPRelative, "#it{p}_{Rec} - #it{p}_{Gen} / #it{p}_{Gen}"}; + const AxisSpec thnAxisEGen{thnConfigAxisPt, "#it{E}_{Gen} (GeV)"}; + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisERelative{thnConfigAxisERelative, "#it{E}_{Rec} - #it{E}_{Gen} / #it{E}_{Gen}"}; + + const AxisSpec thnAxisEtaRelative{thnConfigAxisEtaRelative, "#it{#eta}_{Rec} - #it{#eta}_{Gen} / #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiRelative{thnConfigAxisPhiRelative, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen} / #it{#varphi}_{Gen}"}; + + const AxisSpec thnAxisEtaGen{280, -0.7, 0.7, "#it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiGen{360, 0., o2::constants::math::TwoPI, "#it{#varphi}_{Gen} (rad)"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + registry.add("hGenerated", "generated photons pT vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + + if (doprocessEmcal || doprocessPcmEmcal) { + registry.add("EMCal/hAccepted", "EMCal accepted photon pt vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("EMCal/hMeasured", "EMCal measured photon candidate pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("EMCal/hMeasuredTruePhoton", "EMCal measured and validated photon pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + } + + if (doprocessPcm || doprocessPcmEmcal) { + registry.add("PCM/hAccepted", "PCM accepted photon pt vs cent/mult", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("PCM/hMeasured", "PCM measured photon candidate pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + registry.add("PCM/hMeasuredTruePhoton", "PCM measured and validated photon pt vs cent/mult ", HistType::kTH2D, {thnAxisPt, thnAxisCentOrMult}); + } + + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; + fV0PhotonCut.SetD_Bz(dBz); + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + fEMEventCut.fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + + if (collision.emmceventId() < 0) { + return false; + } + + if (fillHisto) { + fEMEventCut.fillEventInfo<1>(®istry, collision); + fEMEventCut.fillAccepted(®istry); + } + + return true; + } + + /// \brief check if particle is within the EMCal acceptance + /// \param particle particle will be checked + /// \return true if particle is within the EMCal, false otherwise + template + bool inEMCalAcceptance(TParticle const& particle) + { + // general EMCal eta acceptance + if (std::abs(particle.eta()) > EmcalMaxEta) { + return false; + } + + // before EMCal or after DCal + if (particle.phi() < EmcalLowerPhi || particle.phi() > DcalUpperPhi) { + return false; + } + + // between EMCal and DCal + if (EmcalUpperPhi < particle.phi() && particle.phi() < DcalLowerPhi) { + return false; + } + + // PHOS GAP in DCal + if (particle.phi() >= DcalLowerPhi && particle.phi() <= DcalUpperPhosGap) { + if (std::abs(particle.eta()) < DcalMinEta) { + return false; + } + } + + return true; + } + + // TODO: + // - efficiency: number of found photon candidates / number of photons within accpetance + // - acceptance: number of photons in acceptance / number of photons generated + // - photon Purity: number of actual photons / number of photon candidates + // - How to handle EMCal cluster in this regard? Sometimes both conversion leptons can end up in the same cluster. + // What is a good check when to start treating them as a photon? + // When they both deposite at least X% of their momentum into the cluster? + // - electron "purity": number of actual electrons/ number of photon candidates + + // PCM + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, EMMCParticles const& mcParticles) + { + EMBitFlags v0flags(photons.size()); + if (photons.size() > 0) { + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + } + + // leg iterators for PCM + auto pos1 = legs.begin(); + auto ele1 = legs.begin(); + + // MC leg iterators for PCM + auto pos1mc = mcParticles.begin(); + auto ele1mc = mcParticles.begin(); + + for (const auto& collision : collisions) { + + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + auto centOrMult = getCentralityOrMultiplicity(collision); + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + // trivial case I guess for PCM + registry.fill(HIST("PCM/hAccepted"), truePhoton.pt(), centOrMult); + } + } + + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + for (const auto& photon : photonsPCMPerCollision) { + if (!(v0flags.test(photon.globalIndex()))) { + continue; + } + registry.fill(HIST("PCM/hMeasured"), photon.pt(), centOrMult); + + pos1.setCursor(photon.posTrackId()); + ele1.setCursor(photon.negTrackId()); + + pos1mc.setCursor(pos1.emmcparticleId()); + ele1mc.setCursor(ele1.emmcparticleId()); + + const auto photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + + if (photonid1 < 0) { + continue; + } + registry.fill(HIST("PCM/hMeasuredTruePhoton"), photon.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processPcm, "Process for pcm photons", false) + + // EMCal + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + EMBitFlags emcFlags(clusters.size()); + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + + auto mcPhoton = mcParticles.begin(); + + for (const auto& collision : collisions) { + + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + auto centOrMult = getCentralityOrMultiplicity(collision); + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + + if (inEMCalAcceptance(truePhoton)) { + registry.fill(HIST("EMCal/hAccepted"), truePhoton.pt(), centOrMult); + } + } + } + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + for (const auto& photonEMC : photonsEMCPerCollision) { + if (!(emcFlags.test(photonEMC.globalIndex()))) { + continue; + } + + registry.fill(HIST("EMCal/hMeasured"), photonEMC.pt(), centOrMult); + + if (photonEMC.emmcparticleIds().empty()) { + // this is a cluster with just noise, skip + continue; + } + // we only want to look at the largest contribution + mcPhoton.setCursor(photonEMC.emmcparticleIds()[0]); + + // if the largest contribution is photon, just fill the photon histograms + if (std::abs(mcPhoton.pdgCode()) != PDG_t::kGamma) { + continue; + } + + registry.fill(HIST("EMCal/hMeasuredTruePhoton"), photonEMC.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processEmcal, "Process for emcal photons", false) + + // PCM-EMCal same event + void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + EMBitFlags emcFlags(clusters.size()); + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + EMBitFlags v0flags(photons.size()); + if (photons.size() > 0) { + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + } + + // create iterators for photon mc particles + auto mcPhoton = mcParticles.begin(); + + // leg iterators for PCM + auto pos1 = legs.begin(); + auto ele1 = legs.begin(); + + // MC leg iterators for PCM + auto pos1mc = mcParticles.begin(); + auto ele1mc = mcParticles.begin(); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + float centOrMult = getCentralityOrMultiplicity(collision); + + auto photonsThisEvent = mcPhotons->sliceByCachedUnsorted(aod::emmcparticle::emmceventId, collision.emmceventId(), cache); + + for (const auto& truePhoton : photonsThisEvent) { + if (std::abs(truePhoton.eta()) <= cfgMaxEta) { + registry.fill(HIST("hGenerated"), truePhoton.pt(), centOrMult); + registry.fill(HIST("PCM/hAccepted"), truePhoton.pt(), centOrMult); + } + if (inEMCalAcceptance(truePhoton)) { + registry.fill(HIST("EMCal/hAccepted"), truePhoton.pt(), centOrMult); + } + } + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + for (const auto& photonEMC : photonsEMCPerCollision) { + if (!(emcFlags.test(photonEMC.globalIndex()))) { + continue; + } + + registry.fill(HIST("EMCal/hMeasured"), photonEMC.pt(), centOrMult); + + if (photonEMC.emmcparticleIds().empty()) { + // this is a cluster with just noise, skip + continue; + } + // we only want to look at the largest contribution + mcPhoton.setCursor(photonEMC.emmcparticleIds()[0]); + + // if the largest contribution is photon, just fill the photon histograms + if (std::abs(mcPhoton.pdgCode()) != PDG_t::kGamma) { + continue; + } + + registry.fill(HIST("EMCal/hMeasuredTruePhoton"), photonEMC.pt(), centOrMult); + } + + for (const auto& photon : photonsPCMPerCollision) { + if (!(v0flags.test(photon.globalIndex()))) { + continue; + } + registry.fill(HIST("PCM/hMeasured"), photon.pt(), centOrMult); + + pos1.setCursor(photon.posTrackId()); + ele1.setCursor(photon.negTrackId()); + + pos1mc.setCursor(pos1.emmcparticleId()); + ele1mc.setCursor(ele1.emmcparticleId()); + + const auto photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + + if (photonid1 < 0) { + continue; + } + registry.fill(HIST("PCM/hMeasuredTruePhoton"), photon.pt(), centOrMult); + } + } + } + PROCESS_SWITCH(PhotonMcTask, processPcmEmcal, "Process for pcm and emcal photons", true); + +}; // End struct PhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} From df135d6331ef8569ba5215119f9f1035e099e74a Mon Sep 17 00:00:00 2001 From: Youngwoong Seo Date: Mon, 9 Nov 2026 23:20:44 +0900 Subject: [PATCH 626/648] [PWGHF] candidateCreatorXicToXiPiPi, candidateSelectorXicToXiPiPi: fixed bug caused by missing condition (#18657) --- PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx | 3 ++- PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx index 73667ffe7b8..3ad4779009a 100644 --- a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx @@ -1350,7 +1350,8 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Check whether the charm baryon is non-prompt (from a b quark). if (flag != 0) { // fill inclusive generated Xic+ - registry.fill(HIST("hYPtGen"), particle.y(), particle.pt()); + if (fillMcHistograms) + registry.fill(HIST("hYPtGen"), particle.y(), particle.pt()); origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, false, &idxBhadMothers); // Calculate the decay length of the generated particle diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 2732191f859..a2cd6957b92 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -655,7 +655,8 @@ struct HfCandidateSelectorXicToXiPiPi { if constexpr (IsMc) { if (isMatchedSignal) { registry.fill(HIST("hSelCandidatesRecSig"), PidSelected); - registry.fill(HIST("hMassPtRecSig"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); + if (fillQAHistograms) + registry.fill(HIST("hMassPtRecSig"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); } else { registry.fill(HIST("hSelCandidatesRecBkg"), PidSelected); } From db4aa349562bc10f3acd7c046485fe034515493a Mon Sep 17 00:00:00 2001 From: Anoop1379 Date: Mon, 9 Nov 2026 20:31:09 +0530 Subject: [PATCH 627/648] [PWGCF] LP Balance Function - Edit 12 (#18659) --- .../Tasks/lambdaProtonBalanceFunction.cxx | 304 +++++------------- 1 file changed, 74 insertions(+), 230 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx index 4c1c3be98df..f076baf3e3c 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -72,7 +72,7 @@ using MyTracks = soa::Join; -// MC-labelled versions used only by processMCEfficiency (efficiency maps) +// MC-labelled versions used by processMCEfficiency and processMCRecBF. using MyTracksMC = soa::Join; using MyV0sMC = soa::Join; @@ -112,7 +112,7 @@ struct LambdaProtonBalanceFunction { HistogramRegistry registryEff{"Efficiency", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // ════════════════════════════════════════════════════════════════════════ - // TaskState — ordinary C++ runtime/implementation state. + // TaskState — ordinary C++ runtime/implementation state.. // Grouped into ONE nested object so that the number of direct data members of // this task struct stays well below the O2 StructToTuple / structured-binding // reflection limit. Contains NO O2 Framework objects (no Configurable, @@ -1354,7 +1354,7 @@ struct LambdaProtonBalanceFunction { const AxisSpec axCent(cCentBins, "centrality (%)"); const AxisSpec axPt{60, 0.0, 6.0, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "y"}; - for (const char* name : {"GenLambda", "GenAntiLambda", "GenProton", "GenAntiProton", + for (const auto& name : {"GenLambda", "GenAntiLambda", "GenProton", "GenAntiProton", "RecLambda", "RecAntiLambda", "RecProton", "RecAntiProton"}) { registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); } @@ -1605,7 +1605,7 @@ struct LambdaProtonBalanceFunction { "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; auto hCFL = registryQaDetector.get(HIST("Lambda/hCutFlow")); auto hCFAL = registryQaDetector.get(HIST("AntiLambda/hCutFlow")); - for (int ib = 0; ib < 10; ++ib) { + for (int ib = 0; ib < static_cast(v0CfLabels.size()); ++ib) { hCFL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); hCFAL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); } @@ -1622,7 +1622,7 @@ struct LambdaProtonBalanceFunction { "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; auto hCFp = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton")); auto hCFa = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton")); - for (int ib = 0; ib < 21; ++ib) { + for (int ib = 0; ib < static_cast(cfLabels2D.size()); ++ib) { hCFp->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); hCFa->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); } @@ -1859,8 +1859,9 @@ struct LambdaProtonBalanceFunction { // ── QA3: SplitTrackQA — q_inv and shared-cluster fraction ───────────── float qinvMax = cQinvQAMax.value; int qinvBins = cQinvQABins.value; - if (qinvMax / qinvBins < 0.001f) { - qinvBins = static_cast(qinvMax / 0.001f); + constexpr float MinQinvQABinWidth = 0.001f; + if (qinvMax / qinvBins < MinQinvQABinWidth) { + qinvBins = static_cast(qinvMax / MinQinvQABinWidth); } // "Before_qinvCut": filled for every accepted pair, prior to the q_inv @@ -2140,13 +2141,14 @@ struct LambdaProtonBalanceFunction { } state.centEdgesLocal.push_back(static_cast(ax->GetBinUpEdge(ax->GetNbins()))); } - if (state.centEdgesLocal.size() < 2) { + constexpr size_t MinCentralityEdges = 2; + if (state.centEdgesLocal.size() < MinCentralityEdges) { LOGF(fatal, "cCentBins needs at least 2 edges"); } state.mCentMin = state.centEdgesLocal.front(); state.mCentMax = state.centEdgesLocal.back(); registryOther.get(HIST("hEventCutflow"))->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f)", cUseFT0C.value ? "centFT0C" : "centFT0M", state.mCentMin, state.mCentMax)); - for (const float e : state.centEdgesLocal) { + for (const float& e : state.centEdgesLocal) { if (e != std::floor(e)) { LOGF(fatal, "cCentBins edges must be integers (folder names use integer edges)"); } @@ -2197,14 +2199,14 @@ struct LambdaProtonBalanceFunction { const std::array matchedLabels = {"MC collisions seen", "reco-collision count != 1", "MC-label mismatch", "failed sel8/|z|", "failed numContrib/centrality/INEL/extra selection", "accepted", "McParticle MC-collision mismatch (must be 0)"}; - for (int i = 0; i < 7; ++i) { + for (int i = 0; i < static_cast(matchedLabels.size()); ++i) { hMatched->GetXaxis()->SetBinLabel(i + 1, matchedLabels[i]); } auto hDaughters = registryOther.add("hGenLambdaDaughters", "Primary generated Lambdas in accepted events, before acceptance;Population;Particles", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); const std::array daughterLabels = {"primary Lambda with daughters", "primary Lambda without daughters", "primary AntiLambda with daughters", "primary AntiLambda without daughters"}; - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < static_cast(daughterLabels.size()); ++i) { hDaughters->GetXaxis()->SetBinLabel(i + 1, daughterLabels[i]); } const std::string mode = doprocessData.value ? "processData" : doprocessMCRecBF.value ? "processMCRecBF" @@ -2228,7 +2230,7 @@ struct LambdaProtonBalanceFunction { "mCentMax=" + std::to_string(state.mCentMax), "cFillCentHists=" + std::to_string(static_cast(cFillCentHists.value))}; std::string centBinsLabel = "cCentBins="; - for (const float edge : state.centEdgesLocal) { + for (const float& edge : state.centEdgesLocal) { if (centBinsLabel != "cCentBins=") { centBinsLabel += ","; } @@ -2308,7 +2310,7 @@ struct LambdaProtonBalanceFunction { state.hRho2_LAL_y_AllPt->Sumw2(); state.hRho2_ALL_y_AllPt->Sumw2(); state.hRho2_ALAL_y_AllPt->Sumw2(); - for (auto& S : state.centSets) { + for (const auto& S : state.centSets) { S.rho1_Proton_y->Sumw2(); S.rho1_AntiProton_y->Sumw2(); S.rho1_Lambda_y->Sumw2(); @@ -3035,7 +3037,8 @@ struct LambdaProtonBalanceFunction { if (!passesContaminantVeto(trk)) { bool vetoUsedTOF = false; const int vetoMask = contaminantVetoMask(trk, vetoUsedTOF); - for (int ib = 0; ib < 3; ++ib) { + constexpr int NumContaminantSpecies = 3; + for (int ib = 0; ib < NumContaminantSpecies; ++ib) { if (vetoMask & (1 << ib)) { if constexpr (fillQA) { fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); @@ -3194,13 +3197,17 @@ struct LambdaProtonBalanceFunction { { } + template void fillStage23Counts(int64_t mcCollisionId, std::vector const& selectedLambdas, std::vector const& selectedAntiLambdas, std::vector const& selectedPrimProtons, - std::vector const& selectedPrimAntiProtons) + std::vector const& selectedPrimAntiProtons, + [[maybe_unused]] float cent = 0.f) { // Stage 2 and Stage 3 counting; the selected reconstructed containers remain unchanged. + // Efficiency mode fills only Rec* at the exact Stage 3 acceptance point, + // with reconstructed kinematics and unit weight, without filling BF counters. int nLambdaPrimSec = 0; int nAntiLambdaPrimSec = 0; int nProtonPrimSec = 0; @@ -3229,6 +3236,9 @@ struct LambdaProtonBalanceFunction { ++nLambdaPrimSec; if (mc.isPhysicalPrimary()) { ++nLambdaPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecLambda"), cent, candidate.pt(), candidate.rapidity(1)); + } } } @@ -3251,6 +3261,9 @@ struct LambdaProtonBalanceFunction { ++nAntiLambdaPrimSec; if (mc.isPhysicalPrimary()) { ++nAntiLambdaPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecAntiLambda"), cent, candidate.pt(), candidate.rapidity(2)); + } } } @@ -3273,6 +3286,9 @@ struct LambdaProtonBalanceFunction { ++nProtonPrimSec; if (mc.isPhysicalPrimary()) { ++nProtonPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecProton"), cent, candidate.pt(), protonRapidity(candidate)); + } } } @@ -3295,17 +3311,22 @@ struct LambdaProtonBalanceFunction { ++nAntiProtonPrimSec; if (mc.isPhysicalPrimary()) { ++nAntiProtonPrim; + if constexpr (fillEfficiency) { + registryEff.fill(HIST("RecAntiProton"), cent, candidate.pt(), protonRapidity(candidate)); + } } } - registryOther.fill(HIST("hN_Lambda_PrimSec_reco"), static_cast(nLambdaPrimSec)); - registryOther.fill(HIST("hN_AntiLambda_PrimSec_reco"), static_cast(nAntiLambdaPrimSec)); - registryOther.fill(HIST("hN_Proton_PrimSec_reco"), static_cast(nProtonPrimSec)); - registryOther.fill(HIST("hN_AntiProton_PrimSec_reco"), static_cast(nAntiProtonPrimSec)); - registryOther.fill(HIST("hN_Lambda_prim_reco"), static_cast(nLambdaPrim)); - registryOther.fill(HIST("hN_AntiLambda_prim_reco"), static_cast(nAntiLambdaPrim)); - registryOther.fill(HIST("hN_Proton_prim_reco"), static_cast(nProtonPrim)); - registryOther.fill(HIST("hN_AntiProton_prim_reco"), static_cast(nAntiProtonPrim)); + if constexpr (!fillEfficiency) { + registryOther.fill(HIST("hN_Lambda_PrimSec_reco"), static_cast(nLambdaPrimSec)); + registryOther.fill(HIST("hN_AntiLambda_PrimSec_reco"), static_cast(nAntiLambdaPrimSec)); + registryOther.fill(HIST("hN_Proton_PrimSec_reco"), static_cast(nProtonPrimSec)); + registryOther.fill(HIST("hN_AntiProton_PrimSec_reco"), static_cast(nAntiProtonPrimSec)); + registryOther.fill(HIST("hN_Lambda_prim_reco"), static_cast(nLambdaPrim)); + registryOther.fill(HIST("hN_AntiLambda_prim_reco"), static_cast(nAntiLambdaPrim)); + registryOther.fill(HIST("hN_Proton_prim_reco"), static_cast(nProtonPrim)); + registryOther.fill(HIST("hN_AntiProton_prim_reco"), static_cast(nAntiProtonPrim)); + } } // Reconstructed candidates are compared with generated primary particles; @@ -3647,13 +3668,7 @@ struct LambdaProtonBalanceFunction { computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, primProton.px(), primProton.py(), primProton.pz(), kMassProton)); // q_inv vs DeltaPhi diagnostic (filled BEFORE applying q_inv rejection) - float dPhi_Lp = primProton.phi() - v0.phi(); - while (dPhi_Lp > TMath::Pi()) { - dPhi_Lp -= 2.0f * TMath::Pi(); - } - while (dPhi_Lp < -TMath::Pi()) { - dPhi_Lp += 2.0f * TMath::Pi(); - } + const float dPhi_Lp = RecoDecay::constrainAngle(primProton.phi() - v0.phi(), -o2::constants::math::PI); registryCorrelationQA.fill(HIST("h2_qinv_dPhi_Lp"), dPhi_Lp, qinvLP); // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) @@ -4336,11 +4351,11 @@ struct LambdaProtonBalanceFunction { // Generator-level acceptance = same pT range and |y| cut as the reco selection of the species. bool inLambdaAcc(float pt, float y) const { - return pt > lambdaV0MinPt.value && pt < lambdaV0MaxPt.value && std::abs(y) < lambdaV0MaxY.value; + return pt >= lambdaV0MinPt.value && pt <= lambdaV0MaxPt.value && std::abs(y) <= lambdaV0MaxY.value; } bool inProtonAcc(float pt, float y) const { - return pt > pProtonMinPt.value && pt < pProtonMaxPt.value && std::abs(y) < pProtonMaxY.value; + return pt >= pProtonMinPt.value && pt <= pProtonMaxPt.value && std::abs(y) <= pProtonMaxY.value; } void processMCEfficiency(aod::McCollisions::iterator const& mcCollision, @@ -4360,7 +4375,7 @@ struct LambdaProtonBalanceFunction { return; } // Same event selection as the Filters of processData(): sel8 + |posZ| cut - if (!collision.sel8() || std::abs(collision.posZ()) >= cPPandV0ZVertexCut.value) { + if (!collision.sel8() || !(std::abs(collision.posZ()) < cPPandV0ZVertexCut.value)) { return; } // Slice V0s and tracks to this reconstructed collision @@ -4376,9 +4391,10 @@ struct LambdaProtonBalanceFunction { } // INEL > 0 (same as processData()): at least one charged track in |eta| < 0.8 { + constexpr float MaxInelEta = 0.8f; int nTracksINEL = 0; for (auto const& trk : tracks) { - if (std::abs(trk.eta()) < 0.8f) { + if (std::abs(trk.eta()) < MaxInelEta) { ++nTracksINEL; } } @@ -4393,13 +4409,13 @@ struct LambdaProtonBalanceFunction { } // ── 2) GENERATED: physical primaries of this MC collision (mcParticles is auto-sliced to mcCollision; - // NO V0-daughter veto at generator level) ── + // Lambda/AntiLambda require daughter records; no decay-channel requirement or V0-daughter veto) ── for (auto const& mc : mcParticles) { - if (!mc.isPhysicalPrimary()) { + if (mc.mcCollisionId() != mcCollision.globalIndex() || !mc.isPhysicalPrimary()) { continue; } const int pdg = mc.pdgCode(); - if (pdg == 3122 || pdg == -3122) { + if (pdg == PDG_t::kLambda0 || pdg == PDG_t::kLambda0Bar) { if (!mc.has_daughters()) { continue; } @@ -4408,18 +4424,18 @@ struct LambdaProtonBalanceFunction { if (!inLambdaAcc(pt, y)) { continue; } - if (pdg == 3122) { + if (pdg == PDG_t::kLambda0) { registryEff.fill(HIST("GenLambda"), cent, pt, y); } else { registryEff.fill(HIST("GenAntiLambda"), cent, pt, y); } - } else if (pdg == 2212 || pdg == -2212) { + } else if (pdg == PDG_t::kProton || pdg == PDG_t::kProtonBar) { const float pt = mc.pt(); const float y = mcRapidity(mc, kMassProton); if (!inProtonAcc(pt, y)) { continue; } - if (pdg == 2212) { + if (pdg == PDG_t::kProton) { registryEff.fill(HIST("GenProton"), cent, pt, y); } else { registryEff.fill(HIST("GenAntiProton"), cent, pt, y); @@ -4427,193 +4443,21 @@ struct LambdaProtonBalanceFunction { } } - // ══════════════════════════════════════════════════════════════════════ - // BLOCK COPIED FROM processData() — V0 (Lambda/AntiLambda) selection. - // The selection is embedded in processData(), so it is duplicated here with all QA fills removed. - // KEEP IN SYNC with processData() (Step 2 V0 loop + shared-daughter cleaning). - // ══════════════════════════════════════════════════════════════════════ - constexpr float mLambdaPDG = o2::constants::physics::MassLambda; - constexpr float mK0PDG = o2::constants::physics::MassK0Short; + // Use the identical reconstructed selection and cleaning as processMCRecBF, + // including the conservative daughter veto built before shared-daughter cleaning. std::unordered_set v0DaughterTrackIds; std::vector selectedLambdas; std::vector selectedAntiLambdas; - - for (const auto& v0 : V0s) { - const auto& v0PosDau = v0.posTrack_as(); - const auto& v0NegDau = v0.negTrack_as(); - - if (v0.v0Type() != cV0TypeSelection.value) { - continue; - } - if (v0PosDau.pt() <= cV0DauMinPt.value || v0NegDau.pt() <= cV0DauMinPt.value || - std::abs(v0PosDau.eta()) >= cV0DauMaxEta.value || std::abs(v0NegDau.eta()) >= cV0DauMaxEta.value || - v0PosDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value || - v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { - continue; - } - const bool v0DauPID_AsLambda = - (std::abs(v0PosDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value) && - (std::abs(v0NegDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value); - const bool v0DauPID_AsAntiLambda = - (std::abs(v0PosDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value) && - (std::abs(v0NegDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value); - if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { - continue; - } - if (v0DauPID_AsLambda && std::abs(v0.rapidity(1)) > lambdaV0MaxY.value) { - continue; - } - if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { - continue; - } - if (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value) { - continue; - } - const float dcaProton = v0DauPID_AsLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); - const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); - const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; - if (!passTopoCuts(v0)) { - continue; - } - if (dcaProton < lambdaV0DaughterProtonMinDCAToPV.value || - dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { - continue; - } - if (ctau > lambdaV0MaxCTau.value) { - continue; - } - if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { - continue; - } - const bool selLambda = v0DauPID_AsLambda && (std::abs(v0.mLambda() - mLambdaPDG) < lambdaV0MassWindow.value); - const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); - if (selLambda) { - selectedLambdas.push_back(v0); - v0DaughterTrackIds.insert(v0.posTrackId()); - v0DaughterTrackIds.insert(v0.negTrackId()); - } - if (selAntiLambda) { - selectedAntiLambdas.push_back(v0); - v0DaughterTrackIds.insert(v0.posTrackId()); - v0DaughterTrackIds.insert(v0.negTrackId()); - } - } - // Shared-daughter cleaning (same as processData()) - std::unordered_map posIdCount; - std::unordered_map negIdCount; - for (const auto& v : selectedLambdas) { - ++posIdCount[v.posTrackId()]; - ++negIdCount[v.negTrackId()]; - } - for (const auto& v : selectedAntiLambdas) { - ++posIdCount[v.posTrackId()]; - ++negIdCount[v.negTrackId()]; - } - std::vector cleanLambdas; - std::vector cleanAntiLambdas; - for (const auto& v : selectedLambdas) { - if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { - cleanLambdas.push_back(v); - } - } - for (const auto& v : selectedAntiLambdas) { - if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { - cleanAntiLambdas.push_back(v); - } - } - // ══════════════ END OF BLOCK COPIED FROM processData() (V0) ══════════════ - - // ── 4) RECONSTRUCTED Lambda / AntiLambda: true primary (anti)Lambda, filled in RECONSTRUCTED (pT, y) ── - for (const auto& v0 : cleanLambdas) { - const auto& v0PosDau = v0.posTrack_as(); - const auto& v0NegDau = v0.negTrack_as(); - if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { - continue; - } - const auto mc = v0.mcParticle_as(); - if (!mc.isPhysicalPrimary() || mc.pdgCode() != 3122) { - continue; - } - registryEff.fill(HIST("RecLambda"), cent, v0.pt(), v0.rapidity(1)); - } - for (const auto& v0 : cleanAntiLambdas) { - const auto& v0PosDau = v0.posTrack_as(); - const auto& v0NegDau = v0.negTrack_as(); - if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { - continue; - } - const auto mc = v0.mcParticle_as(); - if (!mc.isPhysicalPrimary() || mc.pdgCode() != -3122) { - continue; - } - registryEff.fill(HIST("RecAntiLambda"), cent, v0.pt(), v0.rapidity(2)); - } - - // ══════════════════════════════════════════════════════════════════════ - // BLOCK COPIED FROM processData() — primary (anti)proton selection (track loop, QA fills removed). - // Reuses passesPrimProtonPid() and passesContaminantVeto(); the remaining cuts are embedded - // in processData() and duplicated here. KEEP IN SYNC with processData(). - // ══════════════════════════════════════════════════════════════════════ - for (auto const& trk : tracks) { - if (!trk.hasTPC()) { - continue; - } - if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { - continue; - } - if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { - continue; - } - if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { - continue; - } - if (trk.itsNCls() < pProtonITSMinClusters.value) { - continue; - } - if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { - continue; - } - if (trk.tpcSignal() < cPPMinTpcSignal.value) { - continue; - } - if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { - continue; - } - if (std::abs(protonRapidity(trk)) > pProtonMaxY.value) { - continue; - } - const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value); - if (!passDCA) { - continue; - } - if (!passesPrimProtonPid(trk)) { - continue; - } - if (!passesContaminantVeto(trk)) { - continue; - } - // V0-daughter veto (same set as processData(): built from V0s selected before shared-daughter cleaning) - if (v0DaughterTrackIds.contains(trk.globalIndex())) { - continue; - } - // ══════════════ END OF BLOCK COPIED FROM processData() (protons) ══════════════ - - // ── 3) RECONSTRUCTED protons / antiprotons: true primary p / pbar of this MC collision ── - if (!trk.has_mcParticle()) { - continue; - } - const auto mc = trk.mcParticle_as(); - if (!mc.isPhysicalPrimary()) { - continue; - } - // Fill with RECONSTRUCTED kinematics (same variables as getEffWeight in processData()) - const float yReco = protonRapidity(trk); - if (trk.sign() > 0 && mc.pdgCode() == 2212) { - registryEff.fill(HIST("RecProton"), cent, trk.pt(), yReco); - } else if (trk.sign() < 0 && mc.pdgCode() == -2212) { - registryEff.fill(HIST("RecAntiProton"), cent, trk.pt(), yReco); - } - } + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + selectRecoV0s(collision, V0s, selectedLambdas, selectedAntiLambdas, v0DaughterTrackIds); + selectRecoProtons(tracks, v0DaughterTrackIds, selectedPrimProtons, selectedPrimAntiProtons); + + // RecSpecies is the unweighted Stage 3 count in reconstructed (cent, pT, y). + // GenSpecies above uses truth (pT, y) in the same accepted event; their binwise + // ratio includes migration. Rec/Gen division is performed by external map macros. + fillStage23Counts(mcCollision.globalIndex(), selectedLambdas, selectedAntiLambdas, + selectedPrimProtons, selectedPrimAntiProtons, cent); } PROCESS_SWITCH(LambdaProtonBalanceFunction, processMCEfficiency, "Produce MC single-particle efficiency inputs", false); // ───────────────────────────────────────────────────────────────────── @@ -4706,15 +4550,15 @@ struct LambdaProtonBalanceFunction { continue; } const int pdg = mc.pdgCode(); - const bool isLambda = pdg == 3122 || pdg == -3122; - const bool isProton = pdg == 2212 || pdg == -2212; + const bool isLambda = pdg == PDG_t::kLambda0 || pdg == PDG_t::kLambda0Bar; + const bool isProton = pdg == PDG_t::kProton || pdg == PDG_t::kProtonBar; if (!isLambda && !isProton) { continue; } if (isLambda) { // Count primary Lambdas before acceptance, in accepted events. registryOther.fill(HIST("hGenLambdaDaughters"), - (pdg == 3122 ? 1.f : 3.f) + (mc.has_daughters() ? 0.f : 1.f)); + (pdg == PDG_t::kLambda0 ? 1.f : 3.f) + (mc.has_daughters() ? 0.f : 1.f)); if (!mc.has_daughters()) { continue; } @@ -4728,11 +4572,11 @@ struct LambdaProtonBalanceFunction { continue; } // No decay-channel/daughter-acceptance requirement and no proton daughter veto. - if (pdg == 3122) { + if (pdg == PDG_t::kLambda0) { lambdas.push_back(p); - } else if (pdg == -3122) { + } else if (pdg == PDG_t::kLambda0Bar) { antiLambdas.push_back(p); - } else if (pdg == 2212) { + } else if (pdg == PDG_t::kProton) { protons.push_back(p); } else { antiProtons.push_back(p); From d997c4d93d660c6128f32bc07699fc61e80b4ebc Mon Sep 17 00:00:00 2001 From: hfribert Date: Mon, 9 Nov 2026 16:40:12 +0100 Subject: [PATCH 628/648] [PWGCF] Femto: Use mom. at PV for Sigma kstar calculation (#18661) Co-authored-by: Henrik Fribert --- PWGCF/Femto/Core/femtoUtils.h | 14 +++++ PWGCF/Femto/Core/kinkBuilder.h | 28 +++++++-- PWGCF/Femto/TableProducer/femtoProducer.cxx | 64 ++++++++++++++++++++- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index 22dc4fa37e9..9b9c16c4884 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -16,8 +16,10 @@ #ifndef PWGCF_FEMTO_CORE_FEMTOUTILS_H_ #define PWGCF_FEMTO_CORE_FEMTOUTILS_H_ +#include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" +#include #include #include #include @@ -173,6 +175,18 @@ concept HasEventShapeRow = requires(T row) { template concept HasEventShape = HasEventShapeRow> || (requires { typename std::decay_t::iterator; } && HasEventShapeRow::iterator>); +/// Azimuth of the momentum of a charged particle at the primary vertex, from its azimuth at the decay vertex. +/// The decay vertex (xDecay, yDecay) has to be given relative to the primary vertex. In a solenoidal field the transverse +/// trajectory is a circle through both points, and the tangents at the two ends of a chord make the same angle with it: +/// phiPV = 2*phiChord - phiDecay (independent of polarity). +inline float phiAtPrimaryVertex(float phiDecay, float xDecay, float yDecay) +{ + if (std::abs(xDecay) < o2::constants::math::Epsilon && std::abs(yDecay) < o2::constants::math::Epsilon) { + return phiDecay; + } + return RecoDecay::constrainAngle(2.f * std::atan2(yDecay, xDecay) - phiDecay); +} + /// Recalculate the pT of a kink mother from its direction and the charged daughter momentum inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter, float massMother, float massChargedDaughter, float massNeutralDaughter) diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 34728fbdcec..84d518dbe15 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -53,6 +53,7 @@ struct ConfKinkFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable kinkPhiAtPv{"kinkPhiAtPv", true, "Kinks and PCM SigmaPlus: store the azimuth of the Sigma momentum at the primary vertex"}; o2::framework::Configurable massMinSigma{"massMinSigma", 1.1f, "Minimum mass for Sigma hypothesis"}; o2::framework::Configurable massMaxSigma{"massMaxSigma", 1.3f, "Maximum mass for Sigma hypothesis"}; o2::framework::Configurable massMinSigmaPlus{"massMinSigmaPlus", 1.1f, "Minimum mass for SigmaPlus hypothesis"}; @@ -234,6 +235,7 @@ enum KinkFilters { kPhiMax, kMassMin, kMassMax, + kPhiAtPv, kMlScoreMin, kKinkFiltersMax }; @@ -250,6 +252,7 @@ const std::unordered_map kinkFilterNames = { {kPhiMax, "phiMax"}, {kMassMin, "massMin"}, {kMassMax, "massMax"}, + {kPhiAtPv, "kinkPhiAtPv"}, {kMlScoreMin, "mlScoreMin"}}; /// \class KinkCuts @@ -272,6 +275,7 @@ class KinkSelection : public baseselection::BaseSelection(mKinkPhiAtPv)}, }); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { @@ -331,6 +336,7 @@ class KinkSelection : public baseselection::BaseSelection(mKinkPhiAtPv)}, }); } }; @@ -421,6 +427,9 @@ class KinkSelection : public baseselection::BaseSelectiontemplate fillFilter(kPhiMax, p); pass &= p; + this->template fillFilter(kPhiAtPv, mKinkPhiAtPv); + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { float sigmaMass = kinkCand.mSigmaMinus(); @@ -517,6 +528,7 @@ class KinkSelection : public baseselection::BaseSelection(mPhiAtPv)}, {kinkFilterNames.at(kMlScoreMin), mMlScoreMin}, }); }; - template - void computeKinematics(T const& sigmaPlusPcmCand) + template + void computeKinematics(T1 const& sigmaPlusPcmCand, T2 const& col) { std::array momMother = {sigmaPlusPcmCand.pxSigmaPlus(), sigmaPlusPcmCand.pySigmaPlus(), sigmaPlusPcmCand.pzSigmaPlus()}; mPt = RecoDecay::pt(momMother); mEta = RecoDecay::eta(momMother); mPhi = RecoDecay::phi(momMother); + if (mPhiAtPv) { + mPhi = utils::phiAtPrimaryVertex(mPhi, sigmaPlusPcmCand.xDecVtx() - col.posX(), sigmaPlusPcmCand.yDecVtx() - col.posY()); + } mMass = sigmaPlusPcmCand.massSigmaPlus(); mMlScore = sigmaPlusPcmCand.mlScore(); } @@ -897,6 +914,8 @@ class SigmaPlusPcmSelection : public baseselection::BaseSelectiontemplate fillFilter(kPhiMax, p); pass &= p; + this->template fillFilter(kPhiAtPv, mPhiAtPv); + p = mMass > mMassLowerLimit; this->template fillFilter(kMassMin, p); pass &= p; @@ -951,6 +970,7 @@ class SigmaPlusPcmSelection : public baseselection::BaseSelection(doprocessTracksV0sCascadesKinksRun3pp) + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + static_cast(doprocessTracksSigmaPlusPcmsRun3pp) + + static_cast(doprocessTracksKinksSigmaPlusPcmsRun3pp) + static_cast(doprocessTracksD0sRun3pp) + static_cast(doprocessTracksD0sRun3PbPb) + static_cast(doprocessTracksRun3ppMc) + @@ -390,6 +393,7 @@ struct FemtoProducer { static_cast(doprocessTracksKinksRun3ppMc) + static_cast(doprocessTracksV0sKinksRun3ppMc) + static_cast(doprocessTracksSigmaPlusPcmsRun3ppMc) + + static_cast(doprocessTracksKinksSigmaPlusPcmsRun3ppMc) + static_cast(doprocessTracksD0sRun3ppMc) + static_cast(doprocessTracksD0sRun3PbPbMc) + static_cast(doprocessTracksLcsRun3pp) + @@ -833,6 +837,29 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3pp, "Provide tracks and sigma plus reconstructed with a PCM photon", false); + void processTracksKinksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColSigmaPlusPcms, col.globalIndex()); + processSigmaPlusPcms(col, tracksThisCol, sigmaPlusPcmsThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksKinksSigmaPlusPcmsRun3pp, "Provide tracks, kinks and sigma plus reconstructed with a PCM photon", false); + void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, @@ -1168,6 +1195,37 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks and sigma plus reconstructed with a PCM photon", false); + void processTracksKinksSigmaPlusPcmsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3RecoSigmaPlusPcms const& sigmaPlusPcms, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColRecoSigmaPlusPcms, col.globalIndex()); + processMcSigmaPlusPcms(col, mcCols, tracks, sigmaPlusPcmsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksKinksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks, kinks and sigma plus reconstructed with a PCM photon", false); + void processTracksV0sKinksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, From da6fb45cf4e1c41c5737fd30583dbb452e24bad1 Mon Sep 17 00:00:00 2001 From: YoshiniBailung <69502501+YoshiniBailung@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:17:37 +0100 Subject: [PATCH 629/648] [PWGDQ] qaMatching added UPC Mode (#18528) --- PWGDQ/Tasks/qaMatching.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/PWGDQ/Tasks/qaMatching.cxx b/PWGDQ/Tasks/qaMatching.cxx index 67860f8343b..4c875ffbeae 100644 --- a/PWGDQ/Tasks/qaMatching.cxx +++ b/PWGDQ/Tasks/qaMatching.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include @@ -102,6 +104,7 @@ DECLARE_SOA_COLUMN(MatchType, matchType, int8_t); DECLARE_SOA_COLUMN(MatchChi2, matchChi2, float); DECLARE_SOA_COLUMN(MatchScore, matchScore, float); DECLARE_SOA_COLUMN(MatchRanking, matchRanking, int32_t); +DECLARE_SOA_COLUMN(UPCMode, upcMode, bool); DECLARE_SOA_COLUMN(MftMultiplicity, mftMultiplicity, int32_t); DECLARE_SOA_COLUMN(TrackType, trackType, int8_t); DECLARE_SOA_COLUMN(MftMatchAttempts, mftMatchAttempts, int32_t); @@ -125,6 +128,7 @@ namespace o2::aod DECLARE_SOA_TABLE(QaMatchingEvents, "AOD", "QAMEVT", o2::soa::Index<>, qamatching::MftMultiplicity, + qamatching::UPCMode, qamatching::ColX, qamatching::ColY, qamatching::ColZ); @@ -3249,9 +3253,11 @@ struct QaMatching { int32_t reducedEventId, int& debugCounter) { + const bool upcMode = (collision.flags() & o2::dataformats::Vertex>::Flags::UPCMode) != 0; int32_t mftMultiplicity = static_cast(collisionInfo.mftTracks.size()); qaMatchingEvents( mftMultiplicity, + upcMode, static_cast(collision.posX()), static_cast(collision.posY()), static_cast(collision.posZ())); From 35f1c4f52949a90272eeb8dbafe805ff2f96661d Mon Sep 17 00:00:00 2001 From: AlexianL <123153896+AlexianL@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:32:25 +0100 Subject: [PATCH 630/648] [PWGHF] taskFlow.cxx : change one line of code to solve MFT-FT0A for McRec (#18664) --- PWGHF/HFC/Tasks/taskFlow.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 24a375c075a..0113ba3b3af 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -2510,7 +2510,7 @@ struct HfTaskFlow { auto reassociatedMftTrackDcaXY = 0.f; auto reassociatedMftTrackDcaZ = 0.f; - if constexpr (std::is_same_v>, TTracksAssoc>) { + if constexpr (std::is_same_v>, TTracksTrig>) { if (!reassociatedMftTrack.has_mcParticle()) { continue; } From cdd37b74652093b31b90805015e76ca5b826c6e1 Mon Sep 17 00:00:00 2001 From: hfribert Date: Mon, 9 Nov 2026 18:26:49 +0100 Subject: [PATCH 631/648] [PWGLF] sigmaplusbuilder: reduce CPU of the photon self-pairing (#18663) Co-authored-by: Henrik Fribert --- .../Strangeness/sigmaplusbuilder.cxx | 292 ++++++++++-------- 1 file changed, 161 insertions(+), 131 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx index 9d342f942f8..6d8ba51c21f 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -103,11 +104,12 @@ struct Sigmaplusbuilder { Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon daughter tracks"}; // photon daughters paired by this task instead of taken from V0Datas (V0Datas assumes photons from primary vertex) - Configurable useCustomVertexer{"useCustomVertexer", false, "Pair the photon's e+/e- daughter tracks in this task instead of reading V0Datas"}; Configurable photonSkipAmbiTracks{"photonSkipAmbiTracks", false, "Skip ambiguous tracks when pairing photon daughters"}; Configurable photonPoolTimeMarginNS{"photonPoolTimeMarginNS", 800., "Time margin (ns) added to a daughter track's time range when matching it to collisions"}; Configurable photonMaxDXYIni{"photonMaxDXYIni", 4., "Max xy distance (cm) between the two daughter tracks at the start of the photon vertex fit"}; Configurable photonMaxCircleTouchDist{"photonMaxCircleTouchDist", 4., "Max |circle centre distance - (R1 + R2)| (cm) of the two daughter tracks, applied before the fit"}; + Configurable photonPrefitWithoutMatCorr{"photonPrefitWithoutMatCorr", true, "Before the photon vertex fit with material correction, fit without it and reject the pair if that DCA is above photonPrefitMaxDca"}; + Configurable photonPrefitMaxDca{"photonPrefitMaxDca", 3., "Max daughter DCA (cm) of the fit without material correction (photonPrefitWithoutMatCorr)"}; // proton selection Configurable protonMinPt{"protonMinPt", 0.4, "Minimum proton pT (GeV/c)"}; @@ -383,10 +385,11 @@ struct Sigmaplusbuilder { histos.add("Candidate/Inclusive/hPhotonPointingAngle", "hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); histos.add("Candidate/Inclusive/hPhotonDcaToPV", "hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); - if (doprocessMc || doprocessFindable) { + const bool doMc = doprocessMc || doprocessMcCustomVertexer; + if (doMc || doprocessFindable) { histos.addClone("Photon/Inclusive/", "Photon/True/"); } - if (doprocessMc) { + if (doMc) { histos.addClone("Proton/Inclusive/", "Proton/True/"); histos.addClone("Candidate/Inclusive/", "Candidate/True/"); } @@ -394,7 +397,7 @@ struct Sigmaplusbuilder { histos.add("Photon/Inclusive/hTpcTimeRangeNColl", "hTpcTimeRangeNColl", kTH1F, {axisTpcTimeRangeNColl}); histos.add("Candidate/Inclusive/hCandidatesPerPhoton", "hCandidatesPerPhoton", kTH1F, {axisCandidatesPerPhoton}); - if (doprocessMc) { + if (doMc) { histos.add("MC/hGenSigmaPlusPt", "MC/hGenSigmaPlusPt", kTH1F, {axisSigmaPt}); setBinLabels(histos.add("MC/hSigmaPlusCounter", "MC/hSigmaPlusCounter", kTH1D, {axisSigmaSel})->GetXaxis(), sigmaSteps); } @@ -701,8 +704,11 @@ struct Sigmaplusbuilder { return TrackCand{.Idxtr = static_cast(track.globalIndex()), .collBracket = {firstCollIdx, lastCollIdx}}; } - // electron-positron pairs with |theta+ - theta-| <= photonMaxDeltaTheta that share at least one collision (theta should be similar) - std::vector findPairsByPolarAngle(const std::vector& thetaOfTrack, int nCollisions) + // Finds electron-positron pairs with similar polar angles (|theta+ - theta-| <= photonMaxDeltaTheta) that are compatible with + // at least one common collision. Only pairs accepted by acceptPair (the cut before the fit) are returned. + // nPairsInWindow counts all pairs in the theta window, also the ones acceptPair rejects. + template + std::vector findPairsByPolarAngle(const std::vector& thetaOfTrack, int nCollisions, const TAccept& acceptPair, uint64_t& nPairsInWindow) { // electrons of each collision sorted by polar angle, so a positron only looks at those inside its theta window std::vector>> electronsByCollision(nCollisions); @@ -732,6 +738,10 @@ struct Sigmaplusbuilder { } lastPositronPaired[iElectron] = iPositron; const auto& electron = mElectronPool[iElectron]; + ++nPairsInWindow; + if (!acceptPair(electron.Idxtr, positron.Idxtr)) { + continue; + } pairs.push_back(SVCand{.tr0Idx = electron.Idxtr, .tr1Idx = positron.Idxtr, .collBracket = electron.collBracket.getOverlap(positron.collBracket)}); } } @@ -739,22 +749,12 @@ struct Sigmaplusbuilder { return pairs; } - // cuts before the fit (similar polar angles, and the circles of the two tracks in xy touching at the conversion point) - template - bool passesPreFitCuts(const TTrack& posTrack, const TTrack& negTrack, float deltaTheta, bool isSignal) + // Cut before the fit: in the transverse plane the circles of the two daughter tracks must touch at the conversion point. + // The circles are computed once per track. Similar polar angles are already required by the pairing. + bool passesCircleTouch(const o2::math_utils::CircleXYf_t& posCircle, const o2::math_utils::CircleXYf_t& negCircle) const { - fillPhotonHist(HIST("hDeltaTheta"), isSignal, deltaTheta); - if (std::abs(deltaTheta) > photonMaxDeltaTheta) { - return false; - } - float sna = 0.f; - float csa = 0.f; - o2::math_utils::CircleXYf_t posCircle; - o2::math_utils::CircleXYf_t negCircle; - getTrackParCov(posTrack).getCircleParams(mBz, posCircle, sna, csa); - getTrackParCov(negTrack).getCircleParams(mBz, negCircle, sna, csa); float circleTouchDist = std::abs(std::hypot(posCircle.xC - negCircle.xC, posCircle.yC - negCircle.yC) - (posCircle.rC + negCircle.rC)); - return circleTouchDist <= photonMaxCircleTouchDist; + return circleTouchDist <= photonMaxCircleTouchDist.value; } static constexpr int LastDaughterCutStep = 4; @@ -785,10 +785,24 @@ struct Sigmaplusbuilder { negTrackParCov.setPID(o2::track::PID::Electron); // photon fit settings, reset afterwards since the fitter is shared with the Sigma+ vertex fit - fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); fitter.setMaxDXYIni(photonMaxDXYIni); fitter.setCollinear(collinear); int nCand = 0; + if (photonPrefitWithoutMatCorr) { + // Quick fit without material correction. Pairs that are clearly too far apart are rejected before the full fit. + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + try { + nCand = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + nCand = 0; + } + if (nCand == 0 || std::sqrt(fitter.getChi2AtPCACandidate()) > photonPrefitMaxDca) { + fitter.setMaxDXYIni(1e9); + fitter.setCollinear(false); + return false; + } + } + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); try { nCand = fitter.process(posTrackParCov, negTrackParCov); } catch (...) { @@ -832,8 +846,9 @@ struct Sigmaplusbuilder { // select the daughter tracks and find the collisions each is compatible with std::vector thetaOfTrack(tracks.size(), 0.f); - std::vector mcPhotonOfTrack(tracks.size(), -1); // MC: Sigma+ photon the track comes from - std::vector mcSigmaOfTrack(tracks.size(), -1); // MC: Sigma+ the track comes from + std::vector mcPhotonOfTrack(tracks.size(), -1); // MC: Sigma+ photon the track comes from + std::vector mcSigmaOfTrack(tracks.size(), -1); // MC: Sigma+ the track comes from + std::vector circleOfTrack(tracks.size()); // xy circle of each selected daughter, for the pre-fit cut for (const auto& track : tracks) { int mcPhoton = -1; int mcSigma = -1; @@ -846,6 +861,9 @@ struct Sigmaplusbuilder { continue; } thetaOfTrack[track.globalIndex()] = 2.f * std::atan(std::exp(-track.eta())); + float sna = 0.f; + float csa = 0.f; + getTrackParCov(track).getCircleParams(mBz, circleOfTrack[track.globalIndex()], sna, csa); mcPhotonOfTrack[track.globalIndex()] = mcPhoton; mcSigmaOfTrack[track.globalIndex()] = mcSigma; @@ -870,27 +888,36 @@ struct Sigmaplusbuilder { mElectronPool.insert(mElectronPool.end(), timeMatchedPools[1].begin(), timeMatchedPools[1].end()); mPositronPool.insert(mPositronPool.end(), timeMatchedPools[2].begin(), timeMatchedPools[2].end()); - auto pairs = findPairsByPolarAngle(thetaOfTrack, collisions.size()); + // MC: Sigma+ of a pair whose two tracks come from the same Sigma+ photon, -1 otherwise + auto sigmaOfPair = [&](int electronIdx, int positronIdx) { + if constexpr (IsMC) { + if (mcPhotonOfTrack[electronIdx] >= 0 && mcPhotonOfTrack[electronIdx] == mcPhotonOfTrack[positronIdx]) { + return mcSigmaOfTrack[electronIdx]; + } + } + return -1; + }; + uint64_t nTruePairs = 0; + uint64_t nPairsInWindow = 0; + auto acceptPair = [&](int electronIdx, int positronIdx) { + bool isSignal = sigmaOfPair(electronIdx, positronIdx) >= 0; + if (isSignal) { + nTruePairs++; + } + fillPhotonHist(HIST("hDeltaTheta"), isSignal, thetaOfTrack[positronIdx] - thetaOfTrack[electronIdx]); + return passesCircleTouch(circleOfTrack[positronIdx], circleOfTrack[electronIdx]); + }; + auto pairs = findPairsByPolarAngle(thetaOfTrack, collisions.size(), acceptPair, nPairsInWindow); // fit and select each pair in every good collision it is compatible with - uint64_t nTruePairs = 0; for (const auto& pair : pairs) { int electronIdx = pair.tr0Idx; int positronIdx = pair.tr1Idx; - int sigmaId = -1; - if constexpr (IsMC) { - if (mcPhotonOfTrack[electronIdx] >= 0 && mcPhotonOfTrack[electronIdx] == mcPhotonOfTrack[positronIdx]) { - sigmaId = mcSigmaOfTrack[electronIdx]; - nTruePairs++; - } - } + int sigmaId = sigmaOfPair(electronIdx, positronIdx); bool isSignal = sigmaId >= 0; auto negTrack = tracks.rawIteratorAt(electronIdx); auto posTrack = tracks.rawIteratorAt(positronIdx); - if (!passesPreFitCuts(posTrack, negTrack, thetaOfTrack[positronIdx] - thetaOfTrack[electronIdx], isSignal)) { - continue; - } // both daughters with only a TPC time on the same TPC side: moving to another collision shifts both by the same z, // so the pair is fitted once and the vertex is only shifted in z for the other collisions @@ -1014,9 +1041,9 @@ struct Sigmaplusbuilder { } } - // steps 0-4 are the daughter-track cuts applied before the pairing, so they count all pairs + // steps 0-4 are the daughter-track cuts applied before the pairing, so they count all pairs in the theta window for (int step = 0; step <= LastDaughterCutStep; ++step) { - histos.fill(HIST("Photon/Inclusive/hSelectionCounter"), step, static_cast(pairs.size())); + histos.fill(HIST("Photon/Inclusive/hSelectionCounter"), step, static_cast(nPairsInWindow)); if constexpr (IsMC) { histos.fill(HIST("Photon/True/hSelectionCounter"), step, static_cast(nTruePairs)); } @@ -1785,53 +1812,9 @@ struct Sigmaplusbuilder { LOG(info) << "Task initialized for run " << mRunNumber << " with magnetic field " << mBz << " kZG"; } - void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) - { - markGoodCollisions(collisions); - std::vector>> photonsByCollision; - if (useCustomVertexer && collisions.size() > 0) { - auto firstBC = collisions.begin().bc_as(); - initCCDB(firstBC); - mVDriftMgr.update(firstBC.timestamp()); - photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); - } - - for (const auto& collision : collisions) { - if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { - continue; - } - initCCDB(collision.bc_as()); - histos.fill(HIST("hVertexZ"), collision.posZ()); - std::array pv{collision.posX(), collision.posY(), collision.posZ()}; - - auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - - std::vector> acceptedPhotons; - if (useCustomVertexer) { - acceptedPhotons = photonsByCollision[collision.globalIndex()]; - } else { - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); - } - - std::vector acceptedProtons; - for (const auto& track : tracksThisCollision) { - if (selectProton(track)) { - acceptedProtons.push_back(track); - } - } - - for (const auto& photon : acceptedPhotons) { - for (const auto& proton : acceptedProtons) { - buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); - } - } - } - deduplicateAndWriteCandidates(); - } - PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data", true); - - void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + // MC: generated Sigma+ -> p pi0 in acceptance and reconstructed daughters, for the Sigma+ counter + template + void countGeneratedMc(TTracks const& tracks, aod::McParticles const& mcParticles) { // generated Sigma+ -> p pi0 in acceptance mGenSigmaWritten.clear(); @@ -1870,53 +1853,11 @@ struct Sigmaplusbuilder { fillSigmaCounter(legs.first, 3); } } + } - markGoodCollisions(collisions); - std::vector>> photonsByCollision; - if (useCustomVertexer && collisions.size() > 0) { - auto firstBC = collisions.begin().bc_as(); - initCCDB(firstBC); - mVDriftMgr.update(firstBC.timestamp()); - photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); - } - - for (const auto& collision : collisions) { - if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { - continue; - } - initCCDB(collision.bc_as()); - histos.fill(HIST("hVertexZ"), collision.posZ()); - std::array pv{collision.posX(), collision.posY(), collision.posZ()}; - - auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); - - std::vector> acceptedPhotons; - if (useCustomVertexer) { - acceptedPhotons = photonsByCollision[collision.globalIndex()]; - } else { - auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); - acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); - } - - std::vector acceptedProtons; - for (const auto& track : tracksThisCollision) { - if (selectProton(track)) { - acceptedProtons.push_back(track); - if (track.has_mcParticle()) { - fillSigmaCounter(findSigmaPlusMotherOfProton(track.template mcParticle_as()), 2); - } - } - } - - for (const auto& photon : acceptedPhotons) { - for (const auto& proton : acceptedProtons) { - buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); - } - } - } - deduplicateAndWriteCandidates(); - - // a table row with the true values for each generated Sigma+ in acceptance without a written candidate + // MC: a table row with the true values for each generated Sigma+ in acceptance without a written candidate + void writeGeneratedOnlyMc(aod::McParticles const& mcParticles) + { for (const auto& mcPart : mcParticles) { auto genIt = mGenSigmaWritten.find(mcPart.globalIndex()); if (genIt == mGenSigmaWritten.end() || genIt->second) { @@ -1988,7 +1929,96 @@ struct Sigmaplusbuilder { genDecayRadiusMC, genMassMC); } } - PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC", false); + + // Collision loop shared by all process functions. With UseV0Datas the photons come from V0Datas, otherwise from pairing + // the daughter tracks in this task. + template + void runCollisions(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + using TTrack = typename TTracks::iterator; + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if constexpr (!UseV0Datas) { + if (collisions.size() > 0) { + auto firstBC = collisions.begin().template bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + initCCDB(collision.template bc_as()); + histos.fill(HIST("hVertexZ"), collision.posZ()); + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + + auto tracksThisCollision = [&]() { + if constexpr (IsMC) { + return tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); + } else { + return tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + } + }(); + + std::vector> acceptedPhotons; + if constexpr (UseV0Datas) { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + } else { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } + + std::vector acceptedProtons; + for (const auto& track : tracksThisCollision) { + if (selectProton(track)) { + acceptedProtons.push_back(track); + if constexpr (IsMC) { + if (track.has_mcParticle()) { + fillSigmaCounter(findSigmaPlusMotherOfProton(track.template mcParticle_as()), 2); + } + } + } + } + + for (const auto& photon : acceptedPhotons) { + for (const auto& proton : acceptedProtons) { + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); + } + } + } + deduplicateAndWriteCandidates(); + } + + void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + runCollisions(collisions, v0s, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data, photons from V0Datas", false); + + void processDataCustomVertexer(CollisionsFull const& collisions, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + runCollisions(collisions, nullptr, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(Sigmaplusbuilder, processDataCustomVertexer, "Process data, photons from pairing the daughter tracks in this task (no V0Datas)", true); + + void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + { + countGeneratedMc(tracks, mcParticles); + runCollisions(collisions, v0s, tracks, ambiguousTracks, bcs); + writeGeneratedOnlyMc(mcParticles); + } + PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC, photons from V0Datas", false); + + void processMcCustomVertexer(CollisionsFullMC const& collisions, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + { + countGeneratedMc(tracks, mcParticles); + runCollisions(collisions, nullptr, tracks, ambiguousTracks, bcs); + writeGeneratedOnlyMc(mcParticles); + } + PROCESS_SWITCH(Sigmaplusbuilder, processMcCustomVertexer, "Process MC, photons from pairing the daughter tracks in this task (no V0Datas)", false); void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) { From de9957a8b4c1cf03a84fd2de8a6a1c90e5a6476d Mon Sep 17 00:00:00 2001 From: baasingh <142407539+baasingh@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:37:56 +0530 Subject: [PATCH 632/648] [PWGCF] Update track selection in FactorialMomentsTask (#18651) --- .../Tasks/FactorialMomentsTask.cxx | 165 ++++++++++-------- 1 file changed, 97 insertions(+), 68 deletions(-) diff --git a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx index e34f44ecf46..3cd857c3655 100644 --- a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx @@ -8,6 +8,7 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + /// \file FactorialMomentsTask.cxx /// \brief This task is for Normalized Factorial Moments Analysis: Hwa and Yang, Phys. Rev. C 85, 044914 (2012) /// \author Salman Malik @@ -53,6 +54,11 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct FactorialMomentsTask { + Configurable useDcaCut{"useDcaCut", true, "Enable DCA cut"}; + Configurable usePVContributor{"usePVContributor", true, + "Require the track to contribute to the primary-vertex fit (Run 3)"}; + Configurable useTPCChi2{"useTPCChi2", true, "Enable TPC chi2 cut"}; + Configurable useTPCCluster{"useTPCCluster", false, "Select tracks with TPCCluster"}; Configurable useITS{"useITS", false, "Select tracks with ITS"}; Configurable useTPC{"useTPC", false, "Select tracks with TPC"}; Configurable useGlobal{"useGlobal", true, "Select global tracks"}; @@ -66,7 +72,7 @@ struct FactorialMomentsTask { Configurable ptMin{"ptMin", 0.2f, "lower pT cut"}; Configurable dcaXY{"dcaXY", 0.1f, "DCA xy cut"}; Configurable dcaZ{"dcaZ", 1.0f, "DCA z cut"}; - Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 4.0f, "Maximum TPCchi2NCl"}; Configurable maxITSChi2NCl{"maxITSChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; Configurable minTPCNCls{"minTPCNCls", 70.0f, "minimum number of TPC clusters"}; Configurable> centLimits{"centLimits", {0, 5}, "centrality min and max"}; @@ -85,9 +91,9 @@ struct FactorialMomentsTask { Configurable includeITSTracks{"includeITSTracks", false, "ITS Tracks"}; Configurable nSlots{"nSlots", 1250, "Number of hash slots for merge-safe subsampling (slot = splitmix64(globalBC) % nSlots); must be identical for every job"}; Configurable useMC{"useMC", false, "Use MC information"}; - Configurable useGlobalTrack{"useGlobalTrack", true, "Require global track in filter"}; + Configurable useCrossedRows{"useCrossedRows", true, "Require minimum number of TPC crossed rows"}; Configurable minITSClusters{"minITSClusters", 6, "Minimum Number of ITS cluster"}; - Configurable minTPCClusters{"minTPCClusters", 80, "Minimum Number of TPC cluster"}; + Configurable minTPCClusters{"minTPCClusters", 70, "Minimum Number of TPC cluster"}; Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; Configurable minTPCCrossedRowsOverFindableCls{"minTPCCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable reduceOutput{"reduceOutput", 0, "Suppress info level output (0 = all output, 1 = per collision, 2 = none)"}; @@ -133,23 +139,28 @@ struct FactorialMomentsTask { OutputObjHandlingPolicy::AnalysisObject, true}; float collisionZ = 0.f; - static constexpr double kDcaXY0 = 0.0105; - static constexpr double kDcaXY1 = 0.035; - static constexpr double kDcaXY2 = 1.1; + static constexpr double DcaXY0 = 0.0105; + static constexpr double DcaXY1 = 0.035; + static constexpr double DcaXY2 = 1.1; static const int nBins = 52; - static constexpr double kMinCharge = 1e-6; + static const int nCentLim = 2; + static const int nPtVal = 2; + static const int nVertexXYZ = 3; + static constexpr int NPt = 5; + static constexpr double MinCharge = 1e-6; + static constexpr int MaxSlots = 1000000; static const int nfqOrder = 6; - static constexpr int kEvAll = 1; - static constexpr int kEvSel8 = 2; - static constexpr int kEvITSROFrame = 3; - static constexpr int kEvTimeFrame = 4; - static constexpr int kEvSameBunch = 5; - static constexpr int kEvGoodITS = 6; - static constexpr int kEvGoodZvtx = 7; - static constexpr int kEvVertexITSTPC = 8; - static constexpr int kEvCent = 9; - static constexpr int kEvAccepted = 10; - static constexpr int kNEventSteps = 10; + static constexpr int EvAll = 1; + static constexpr int EvSel8 = 2; + static constexpr int EvITSROFrame = 3; + static constexpr int EvTimeFrame = 4; + static constexpr int EvSameBunch = 5; + static constexpr int EvGoodITS = 6; + static constexpr int EvGoodZvtx = 7; + static constexpr int EvVertexITSTPC = 8; + static constexpr int EvCent = 9; + static constexpr int EvAccepted = 10; + static constexpr int NEventSteps = 10; std::array binningM{}; std::array countTracks{}; std::array, 5>, nfqOrder> fqEvent{}; @@ -170,18 +181,18 @@ struct FactorialMomentsTask { void init(o2::framework::InitContext&) { - if (numPt.value < 1 || numPt.value > 5) { - LOG(fatal) << "numPt must be between 1 and 5, got " << numPt.value; + if (numPt.value < 1 || numPt.value > NPt) { + LOG(fatal) << "numPt must be between 1 and " << NPt << ", got " << numPt.value; } - if (static_cast(ptCuts.value.size()) < 2 * numPt.value) { - LOG(fatal) << "ptCuts needs " << 2 * numPt.value << " values for numPt=" << numPt.value + if (static_cast(ptCuts.value.size()) < nPtVal * numPt.value) { + LOG(fatal) << "ptCuts needs " << nPtVal * numPt.value << " values for numPt=" << numPt.value << ", got " << ptCuts.value.size(); } - if (nSlots.value < 1 || nSlots.value > 1000000) { - LOG(fatal) << "nSlots must be in [1, 1000000], got " << nSlots.value; + if (nSlots.value < 1 || nSlots.value > MaxSlots) { + LOG(fatal) << "nSlots must be in [1, " << MaxSlots << "], got " << nSlots.value; } - if (centLimits.value.size() != 2 || vertexXYZ.value.size() != 3) { - LOG(fatal) << "centLimits needs 2 and vertexXYZ needs 3 values"; + if (centLimits.value.size() != nCentLim || vertexXYZ.value.size() != nVertexXYZ) { + LOG(fatal) << "centLimits needs " << nCentLim << " and vertexXYZ needs " << nVertexXYZ << " values"; } if (applyCheckPtForRec.value == applyCheckPtForMC.value) { LOG(fatal) << "exactly one of applyCheckPtForRec / applyCheckPtForMC must be enabled"; @@ -194,10 +205,16 @@ struct FactorialMomentsTask { for (int i = 0; i < static_cast(ptCuts.value.size()); i++) { cfg += Form("%s%g", i > 0 ? "," : "", ptCuts.value[i]); } + cfg += Form(" useDcaCut=%d usePVContributor=%d useTPCChi2=%d useTPCCluster=%d useCrossedRows=%d", + useDcaCut.value ? 1 : 0, usePVContributor.value ? 1 : 0, useTPCChi2.value ? 1 : 0, + useTPCCluster.value ? 1 : 0, useCrossedRows.value ? 1 : 0); + cfg += Form(" useITS=%d useTPC=%d useGlobal=%d maxTPCChi2NCl=%g minTPCClusters=%d minTPCCrossedRows=%d", + useITS.value ? 1 : 0, useTPC.value ? 1 : 0, useGlobal.value ? 1 : 0, + maxTPCChi2NCl.value, minTPCClusters.value, minTPCCrossedRows.value); histos.add("metaConfig", cfg.Data(), HistType::kTH1D, {{1, 0.5, 1.5}}); - auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{kNEventSteps, 0.5, kNEventSteps + 0.5}})); - const std::array evLabels = {"all", "sel8", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "kNoSameBunchPileup", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "centrality", "accepted"}; - for (int iStep = 0; iStep < kNEventSteps; ++iStep) { + auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{NEventSteps, 0.5, NEventSteps + 0.5}})); + const std::array evLabels = {"all", "sel8", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "kNoSameBunchPileup", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "centrality", "accepted"}; + for (int iStep = 0; iStep < NEventSteps; ++iStep) { mEventSelected->GetXaxis()->SetBinLabel(iStep + 1, evLabels[iStep]); } // merge-safe slot output: additive per-slot sums, hadd-exact (see slotpool.C) @@ -326,7 +343,7 @@ struct FactorialMomentsTask { template bool passDcaCut(const T& track) const { - return std::fabs(track.dcaXY()) < (kDcaXY0 + kDcaXY1 / std::pow(track.pt(), kDcaXY2)); + return std::fabs(track.dcaXY()) < (DcaXY0 + DcaXY1 / std::pow(track.pt(), DcaXY2)); } template @@ -368,7 +385,7 @@ struct FactorialMomentsTask { double charge = pdgInfo->Charge(); double physCharge = charge / 3.0; histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { + if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= MinCharge) { histos.fill(HIST("mChargeAfter"), physCharge); histos.fill(HIST("mEta"), mc.eta()); histos.fill(HIST("mPt"), mc.pt()); @@ -383,66 +400,96 @@ struct FactorialMomentsTask { template bool selectEventRun3(const T& coll) { - histos.fill(HIST("mEventSelected"), kEvAll); + histos.fill(HIST("mEventSelected"), EvAll); if (!coll.sel8()) { return false; } - histos.fill(HIST("mEventSelected"), kEvSel8); + histos.fill(HIST("mEventSelected"), EvSel8); if (evSelNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { return false; } - histos.fill(HIST("mEventSelected"), kEvITSROFrame); + histos.fill(HIST("mEventSelected"), EvITSROFrame); if (evSelNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { return false; } - histos.fill(HIST("mEventSelected"), kEvTimeFrame); + histos.fill(HIST("mEventSelected"), EvTimeFrame); if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - histos.fill(HIST("mEventSelected"), kEvSameBunch); + histos.fill(HIST("mEventSelected"), EvSameBunch); if (evSelGoodITSLayersAll && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { return false; } - histos.fill(HIST("mEventSelected"), kEvGoodITS); + histos.fill(HIST("mEventSelected"), EvGoodITS); if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + histos.fill(HIST("mEventSelected"), EvGoodZvtx); if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { return false; } collisionZ = coll.posZ(); - histos.fill(HIST("mEventSelected"), kEvCent); - histos.fill(HIST("mEventSelected"), kEvAccepted); + histos.fill(HIST("mEventSelected"), EvCent); + histos.fill(HIST("mEventSelected"), EvAccepted); return true; } template bool selectEventMCRec(const T& coll) { - histos.fill(HIST("mEventSelected"), kEvAll); + histos.fill(HIST("mEventSelected"), EvAll); if (!coll.sel8()) { return false; } - histos.fill(HIST("mEventSelected"), kEvSel8); + histos.fill(HIST("mEventSelected"), EvSel8); if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - histos.fill(HIST("mEventSelected"), kEvSameBunch); + histos.fill(HIST("mEventSelected"), EvSameBunch); if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + histos.fill(HIST("mEventSelected"), EvGoodZvtx); if (evSelVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - histos.fill(HIST("mEventSelected"), kEvVertexITSTPC); + histos.fill(HIST("mEventSelected"), EvVertexITSTPC); if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { return false; } collisionZ = coll.posZ(); - histos.fill(HIST("mEventSelected"), kEvCent); - histos.fill(HIST("mEventSelected"), kEvAccepted); + histos.fill(HIST("mEventSelected"), EvCent); + histos.fill(HIST("mEventSelected"), EvAccepted); + return true; + } + + template + bool selectTrack(const T& track) + { + if (useITS && !track.hasITS()) { + return false; + } + if (useTPC && !track.hasTPC()) { + return false; + } + if (useGlobal && !track.isGlobalTrack()) { + return false; + } + if (useCrossedRows && track.tpcNClsCrossedRows() < minTPCCrossedRows) { + return false; + } + if (useDcaCut && !passDcaCut(track)) { + return false; + } + if (usePVContributor && !track.isPVContributor()) { + return false; + } + if (useTPCChi2 && track.tpcChi2NCl() > maxTPCChi2NCl) { + return false; + } + if (useTPCCluster && track.tpcNClsFound() < minTPCClusters) { + return false; + } return true; } @@ -488,16 +535,7 @@ struct FactorialMomentsTask { histos.fill(HIST("mCentFT0C"), coll.centFT0C()); beginEvent(); for (auto const& track : tracks) { - if (useITS && !track.hasITS()) { - continue; - } - if (useTPC && !track.hasTPC()) { - continue; - } - if (useGlobal && !track.isGlobalTrack()) { - continue; - } - if (!passDcaCut(track)) { + if (!selectTrack(track)) { continue; } fillTrackQA(track); @@ -523,16 +561,7 @@ struct FactorialMomentsTask { histos.fill(HIST("mCentFT0C"), coll.centFT0C()); beginEvent(); for (auto const& track : colltracks) { - if (useITS && !track.hasITS()) { - continue; - } - if (useTPC && !track.hasTPC()) { - continue; - } - if (useGlobal && !track.isGlobalTrack()) { - continue; - } - if (!passDcaCut(track)) { + if (!selectTrack(track)) { continue; } fillTrackQA(track); @@ -572,7 +601,7 @@ struct FactorialMomentsTask { beginEvent(); for (auto const& track : tracks) { double recoCharge = (track.sign() != 0) ? track.sign() : 0.; - if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= kMinCharge) { + if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= MinCharge) { fillTrackQA(track); if (applyCheckPtForRec && !applyCheckPtForMC) { checkpT(track); From f91ab49599d1814e98e2251318e4dfa74c052e3b Mon Sep 17 00:00:00 2001 From: Beomkyu Kim Date: Tue, 10 Nov 2026 03:52:17 +0900 Subject: [PATCH 633/648] [PWGLF] Fix dndetaHi matching and update correction functionality (#18655) Co-authored-by: Beomkyu Kim <21291182+kimbyumkyu@users.noreply.github.com> --- .../Tasks/GlobalEventProperties/dndetaHi.cxx | 760 +++++++++++++++++- 1 file changed, 718 insertions(+), 42 deletions(-) diff --git a/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx index 2da575aed40..de366208ddb 100644 --- a/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx @@ -14,11 +14,13 @@ /// \author hyungjun lee , joonsuk bae #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGMM/Mult/DataModel/Index.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -40,9 +42,12 @@ #include #include +#include #include #include #include +#include +#include #include using namespace o2; @@ -51,13 +56,15 @@ using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod; -using BCsRun3 = soa::Join; +using BCsRun3Cent = soa::Join; using MyCollisions = soa::Join; using MyCollisionsCent = soa::Join; using FullBCs = soa::Join; using DaughterTrack = soa::Join; using ExTracks = soa::Join; using FiTracks = soa::Filtered; +using ExDataTracks = soa::Join; +using FiDataTracks = soa::Filtered; using Particles = soa::Join; using LabeledTracks = soa::Join; using LabeledTracksEx = soa::Join; @@ -74,6 +81,12 @@ enum EventCategory { EvtDd, EvtSd, EvtNd, + // Append only: preserve existing category indices for nominal results. + // Combined with TrigSel8g0 these give MB>0 process-resolved corrections + // with the same generated INEL>0 target as the nominal correction. + EvtInelg0Dd, + EvtInelg0Sd, + EvtInelg0Nd, EvtEnd }; @@ -153,6 +166,67 @@ constexpr float PtWeightInterceptDown = 0.5f; // Sentinel value used as centrality when no centrality information is available constexpr float NoCentrality = -1.0f; +// Source used to assign the detector-level centrality of an MC collision. +// The BC fallback is deliberately restricted to generated collisions with no +// reconstructed collision. All other nominal entries retain the official +// collision-level (vertex-z equalized) FT0M percentile. +enum CentralitySource { + CentSourceRecoFT0M = 1, + CentSourceZeroRecoBCFT0M, + CentSourceZeroRecoNoFT0, + CentSourceZeroRecoInvalidBCFT0M, + CentSourceZeroRecoNoBC, + CentSourceInvalidRecoFT0M +}; + +enum ZeroRecoCentralityClass { + ZeroRecoCent0To1 = 1, + ZeroRecoCent1To5, + ZeroRecoCent5To10, + ZeroRecoCent10To15, + ZeroRecoCent15To20, + ZeroRecoCent20To30, + ZeroRecoCent30To40, + ZeroRecoCent40To50, + ZeroRecoCent50To70, + ZeroRecoCent70To100, + ZeroRecoCentNoFT0, + ZeroRecoCentInvalid, + ZeroRecoCentNoBC +}; + +ZeroRecoCentralityClass getZeroRecoCentralityClass(float cent) +{ + if (cent < 1.f) { + return ZeroRecoCent0To1; + } + if (cent < 5.f) { + return ZeroRecoCent1To5; + } + if (cent < 10.f) { + return ZeroRecoCent5To10; + } + if (cent < 15.f) { + return ZeroRecoCent10To15; + } + if (cent < 20.f) { + return ZeroRecoCent15To20; + } + if (cent < 30.f) { + return ZeroRecoCent20To30; + } + if (cent < 40.f) { + return ZeroRecoCent30To40; + } + if (cent < 50.f) { + return ZeroRecoCent40To50; + } + if (cent < 70.f) { + return ZeroRecoCent50To70; + } + return ZeroRecoCent70To100; +} + // Bin indices of the "Selection" counter histogram — values must match SetBinLabel calls in init() enum SelectionStep { SelBegin = 0, @@ -191,13 +265,16 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = struct DndetaHi { SliceCache cache; Preslice perMCCol = aod::mcparticle::mcCollisionId; - Preslice perCol = aod::track::collisionId; + Preslice perDataCol = aod::track::collisionId; Service pdg; Configurable estimatorEta{"estimatorEta", 2.0, "eta range for INEL>0 sample definition"}; Configurable useEvSel{"useEvSel", true, "use event selection"}; Configurable isPbPb{"isPbPb", false, "Is Pb-Pb"}; + Configurable bookFactorizedCorrections{"bookFactorizedCorrections", true, "Additional event/signal-loss inputs in MCCounting; leaves nominal corrections unchanged"}; + Configurable bookMultiplicityCalibration{"bookMultiplicityCalibration", false, "Independent raw generated-FT0 multiplicity calibration and correction inputs; no change to nominal classes"}; + ConfigurableAxis calibrationRecoCentrality{"calibrationRecoCentrality", {1000, 0., 100.}, "Data FT0M percentile axis for offline class boundaries"}; Configurable dcaV0Dau{"dcaV0Dau", 1.5, "DCA V0 Daughters"}; Configurable dcaNegToPV{"dcaNegToPV", 0.06, "DCA Neg To PV"}; @@ -208,6 +285,10 @@ struct DndetaHi { Configurable v0Rapidity{"v0Rapidity", 0.5, "V0 rapidity"}; ConfigurableAxis centrality{"centrality", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}, ""}; Configurable zvtxcut{"zvtxcut", false, "z vtx cut < 10cm"}; + // Only used by processMCUnfolding. Neither the nominal centrality nor any + // nominal histogram depends on these options or on McCentFT0Ms. + ConfigurableAxis unfoldingCentrality{"unfoldingCentrality", {VARIABLE_WIDTH, 0, 1, 5, 10, 15, 20, 30, 40, 50, 70, 100}, "Independent generator/reconstructed FT0M response binning"}; + Configurable unfoldingZMax{"unfoldingZMax", 10.f, "Truth AND measured |z| limit for the separate unfolding sample"}; HistogramRegistry registry{ "registry", @@ -245,12 +326,91 @@ struct DndetaHi { Partition mcSample = nabs(aod::mcparticle::eta) < estimatorEta; Partition tSample = nabs(aod::track::eta) < estimatorEta; - Partition tSample3 = nabs(aod::track::eta) < estimatorEta; + Partition tSampleData = nabs(aod::track::eta) < estimatorEta; void init(InitContext&) { + if (doprocessMCCounting && doprocessMCCountingPVCalibrated) { + LOGP(fatal, "Choose either detector-level MCCounting or PV-calibrated MCCounting, never both"); + } + if (doprocessMCCountingPVCalibrated) { + if (isPbPb || doprocessGen || doprocessMCUnfolding) { + LOGP(fatal, "PV-matched classes support pp corrections only; disable processGen and quantile unfolding"); + } + registry.add("pvMatched/hGenClassINELgt0", "PV-matched class, generated INEL>0;class;events", HistType::kTH1D, {{centrality, "PV-matched FT0M class (%)"}}); + } + if (bookMultiplicityCalibration) { + if (isPbPb) { + LOGP(fatal, "bookMultiplicityCalibration currently supports pp FT0M only"); + } + const AxisSpec nForwardAxis{5001, -0.5, 5000.5, "N_{FT0M,gen} (charged physical primaries)"}; + const AxisSpec nPVAxis{1001, -0.5, 1000.5, "Reconstructed PV contributors"}; + const AxisSpec calibCentAxis{calibrationRecoCentrality, "Reconstructed FT0M percentile (%)"}; + const AxisSpec recoCountAxis{11, -0.5, 10.5, "Number of reconstructed collisions"}; + // Raw integer N_forward, NOT a preassigned percentile. Retain all event + // classes (including non-INEL>0) so event contamination can be audited. + if (doprocessCounting) { + registry.add("multCalib/hDataEvents", "Selected data;trigger;z_rec;cent_rec;N_PV", HistType::kTHnSparseD, {trigClassAxis, zAxis, calibCentAxis, nPVAxis}); + registry.add("multCalib/hDataTracks", "Selected data tracks;trigger;z_rec;eta_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, calibCentAxis}); + } + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + registry.add("multCalib/hGenEvents", "All truth including zero reco;eventClass;z_gen;N_forward;N_reco", HistType::kTHnSparseD, {evtClassAxis, zAxis, nForwardAxis, recoCountAxis}); + registry.add("multCalib/hGenParticles", "All charged primaries;eventClass;z_gen;eta_gen;N_forward", HistType::kTHnSparseD, {evtClassAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hSelectedEvents", "Selected best reco;eventClass;trigger;z_gen;z_rec;N_forward;N_PV", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, nForwardAxis, nPVAxis}); + registry.add("multCalib/hSelectedGenParticles", "All charged primaries in selected event;eventClass;trigger;z_gen;z_rec;eta_gen;N_forward", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hSelectedRecTracks", "All filtered tracks in selected best reco;eventClass;trigger;z_gen;z_rec;eta_rec;N_forward", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, zAxis, etaAxis, nForwardAxis}); + registry.add("multCalib/hClassCorrelation", "Selected best reco;trigger;z_gen;z_rec;N_forward;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, nForwardAxis, calibCentAxis}); + } + } + if (doprocessMCUnfoldingCalib) { + const AxisSpec trueMultAxis{5001, -0.5, 5000.5, "Generated charged-primary FT0A+FT0C multiplicity"}; + registry.add("unfoldingCalib/hFT0MInelGt0", "Generated INEL>0, all z", HistType::kTH1D, {trueMultAxis}); + registry.add("unfoldingCalib/hFT0MInelGt0Z10", "Generated INEL>0, |z_gen|<10 cm", HistType::kTH1D, {trueMultAxis}); + } + if (doprocessMCCounting && doprocessGen) { + LOGP(fatal, "processMCCounting and processGen both fill the nominal generator histograms; enable only processMCCounting for MC corrections"); + } + if (doprocessMCUnfolding) { + if (isPbPb || !std::isfinite(unfoldingZMax.value) || unfoldingZMax <= 0.f || unfoldingZMax > 30.f || + !std::isfinite(estimatorEta.value) || estimatorEta <= 0.f || estimatorEta > 4.f) { + LOGP(fatal, "processMCUnfolding requires pp, 0 < unfoldingZMax <= 30 cm and 0 < estimatorEta <= 4"); + } + const AxisSpec responseCentAxis = {unfoldingCentrality, "FT0M percentile (%)"}; + const AxisSpec extraReasonAxis = {9, 0.5, 9.5, "1:no collision label;2:outside truth event sample;3:split non-best;4:invalid reco percentile;5:no particle label;6:other MC collision;7:not charged primary;8:outside truth eta;9:duplicate track"}; + // Full truth denominators include zero-reco and failed-selection events. + // Primary particles use unit weights; this is an independent nominal + // unfolding input, not a replacement for the legacy pT-variation maps. + registry.add("unfolding/hGenEvents", "Generated INEL>0;z_gen;cent_gen", HistType::kTHnSparseD, {zAxis, responseCentAxis}); + registry.add("unfolding/hGenParticles", "Generated charged primaries;z_gen;eta_gen;cent_gen;species", HistType::kTHnSparseD, {zAxis, etaAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hRecEvents", "All selected MC reconstructed collisions;trigger;z_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis}); + registry.add("unfolding/hRecParticles", "All selected MC tracks;trigger;z_rec;eta_rec;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis}); + registry.add("unfolding/hEventResponse", "One best collision per generated event;trigger;z_gen;z_rec;cent_gen;cent_rec", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, responseCentAxis, responseCentAxis}); + registry.add("unfolding/hParticleResponse", "Unique primary track matches;trigger;z_gen;z_rec;eta_gen;eta_rec;cent_gen;cent_rec;species", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, etaAxis, responseCentAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hMissEvents", "Truth events without a selected match;trigger;z_gen;cent_gen", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis}); + registry.add("unfolding/hMissParticles", "Truth particles without a selected match;trigger;z_gen;eta_gen;cent_gen;species", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis, particleTypeAxis}); + registry.add("unfolding/hExtraEvents", "Measured collisions outside the one-to-one signal response;trigger;z_rec;cent_rec;reason", HistType::kTHnSparseD, {trigClassAxis, zAxis, responseCentAxis, extraReasonAxis}); + registry.add("unfolding/hExtraParticles", "Measured tracks outside the one-to-one signal response;trigger;z_rec;eta_rec;cent_rec;reason", HistType::kTHnSparseD, {trigClassAxis, zAxis, etaAxis, responseCentAxis, extraReasonAxis}); + } AxisSpec centAxis = {centrality, "", "centrality"}; AxisSpec centAxisPbPb = {centrality, "", "centrality"}; + if ((doprocessMCCounting || doprocessMCCountingPVCalibrated) && bookFactorizedCorrections) { + // Independent inputs for detector correction followed by f_event/f_signal. + // All truth includes zero-reco. Selected truth counts ALL charged primaries + // in the selected best collision's MC event, not only track-matched ones. + // Keep both vertex coordinates to permit explicit, consistent acceptance. + const AxisSpec lossCentAxis = {centrality, "Nominal detector percentile (%)"}; + registry.add("factorized/hGenEvents", "INEL>0 before selection;z_gen;cent", HistType::kTHnSparseD, {zAxis, lossCentAxis}); + registry.add("factorized/hGenParticles", "Charged primaries before selection;z_gen;eta_gen;cent", HistType::kTHnSparseD, {zAxis, etaAxis, lossCentAxis}); + registry.add("factorized/hSelectedEvents", "Unique INEL>0 events with selected best reco;trigger;z_gen;z_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, lossCentAxis}); + registry.add("factorized/hSelectedGenParticles", "ALL charged primaries in selected events;trigger;z_gen;z_rec;eta_gen;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, lossCentAxis}); + registry.add("factorized/hSelectedRecParticles", "ALL filtered tracks in selected events;trigger;z_gen;z_rec;eta_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, lossCentAxis}); + } + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + registry.add("selectionLoss/hAllEvents", "All INEL>0;z_gen;cent", HistType::kTHnSparseD, {zAxis, centAxis}); + registry.add("selectionLoss/hSelectedEvents", "Selected INEL>0;trigger;z_gen;z_rec;cent", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, centAxis}); + registry.add("selectionLoss/hAllParticles", "All INEL>0 primaries;z_gen;eta;cent;species;ptvar;phi", HistType::kTHnSparseD, {zAxis, etaAxis, centAxis, particleTypeAxis, ptVarAxis, phiBin}); + registry.add("selectionLoss/hSelectedParticles", "All primaries in selected INEL>0 events;trigger;z_gen;z_rec;eta;cent;species;ptvar;phi", HistType::kTHnSparseD, {trigClassAxis, zAxis, zAxis, etaAxis, centAxis, particleTypeAxis, ptVarAxis, phiBin}); + } registry.add({"hetaresponse", ";etaresponse", {HistType::kTH2D, {{80, -4, 4}, {80, -4, 4}}}}); registry.add({"hft0multiplicity", ";multiplicity", {HistType::kTH1D, {{10000, 0, 100000}}}}); registry.add({"hcentrality", isPbPb ? " ; centrality_FT0C (%) " : "; centrality_FT0M", {HistType::kTH1F, {{10000, 0, 100}}}}); @@ -274,6 +434,15 @@ struct DndetaHi { registry.add({"genzINELg0", ";z", {HistType::kTH1D, {zAxis}}}); registry.add({"hcentmult", ";status;events", {HistType::kTH1D, {{100, 0, 100}}}}); + // MC-only bookkeeping for the zero-reconstructed-vertex FT0M fallback. + // These histograms are filled for generated INEL>0 collisions with + // |z_gen| < 10 cm, i.e. the event sample relevant to the correction. + registry.add({"hMCRecoCollisionsPerGenINELg0", ";N_{reco collisions};events", {HistType::kTH1D, {{6, -0.5, 5.5}}}}); + registry.add({"hMCCentralitySourceINELg0", ";centrality source;events", {HistType::kTH1D, {{6, 0.5, 6.5}}}}); + registry.add({"hZeroRecoBCFT0MPercentileINELg0", ";BC FT0M percentile;events", {HistType::kTH1D, {{103, -1.5, 101.5}}}}); + registry.add({"hZeroRecoBCFT0MClassINELg0", ";BC FT0M class;events", {HistType::kTH1D, {{13, 0.5, 13.5}}}}); + registry.add({"hBCFT0MvsRecoFT0MINELg0", ";collision FT0M percentile;BC FT0M percentile", {HistType::kTH2D, {{100, 0, 100}, {102, 0, 102}}}}); + constexpr int NLogBins = 50; constexpr double PtLogLow = 0.01; constexpr double PtLogHigh = 10.0; @@ -284,6 +453,17 @@ struct DndetaHi { } AxisSpec ptBins = {logBins, "pT (GeV/c)", "pt bin"}; + if (doprocessMCCounting || doprocessMCCountingPVCalibrated) { + std::vector trackingPtEdges; + for (int i = 0; i <= 100; ++i) { + trackingPtEdges.push_back(0.01 * std::pow(200.0 / 0.01, i / 100.0)); + } + const AxisSpec trackingPtAxis{trackingPtEdges, "p_{T}^{gen} (GeV/c)"}; + registry.add("trackingPt/hAllGenerated", "Charged physical primaries;eventClass;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + registry.add("trackingPt/hSelectedGenerated", "Denominator: same selected best collision;eventClass;trigger;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + registry.add("trackingPt/hMatchedPrimary", "Numerator: unique matched physical primaries;eventClass;trigger;FT0M;pt_gen", HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, isPbPb ? centAxisPbPb : centAxis, trackingPtAxis}); + } + registry.add({"hrecdndpt", " pt", {HistType::kTH1D, {ptBins}}}); registry.add({"hdndptefficiency", " pt", {HistType::kTH1D, {ptBins}}}); registry.add({"hgendndpt", " pt", {HistType::kTH1D, {ptBins}}}); @@ -309,6 +489,29 @@ struct DndetaHi { x->SetBinLabel(SelInelSel8Recz10, "INELSel8recz10"); x->SetBinLabel(SelInelg0Sel8g0Recz10, "INELg0Sel8g0recz10"); x->SetBinLabel(SelK0sz10Eta05, "K0Sz10eta05"); + + auto* centSourceAxis = registry.get(HIST("hMCCentralitySourceINELg0"))->GetXaxis(); + centSourceAxis->SetBinLabel(CentSourceRecoFT0M, "reco FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoBCFT0M, "zero reco: BC FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoNoFT0, "zero reco: no FT0"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoInvalidBCFT0M, "zero reco: invalid BC FT0M"); + centSourceAxis->SetBinLabel(CentSourceZeroRecoNoBC, "zero reco: no BC"); + centSourceAxis->SetBinLabel(CentSourceInvalidRecoFT0M, "invalid reco FT0M"); + + auto* zeroRecoClassAxis = registry.get(HIST("hZeroRecoBCFT0MClassINELg0"))->GetXaxis(); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent0To1, "0-1%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent1To5, "1-5%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent5To10, "5-10%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent10To15, "10-15%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent15To20, "15-20%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent20To30, "20-30%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent30To40, "30-40%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent40To50, "40-50%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent50To70, "50-70%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCent70To100, "70-100%"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentNoFT0, "no FT0"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentInvalid, "invalid percentile"); + zeroRecoClassAxis->SetBinLabel(ZeroRecoCentNoBC, "no associated BC"); } void processEventStat( @@ -346,13 +549,13 @@ struct DndetaHi { template void runCounting( const C& collisions, - const FiTracks& /*tracks*/) + const FiDataTracks& /*tracks*/) { for (const auto& collision : collisions) { BoolVector btrigc(TrigEnd, false); registry.fill(HIST("Selection"), SelAll); auto z = collision.posZ(); - auto pertracks = tSample3->sliceBy(perCol, collision.globalIndex()); + auto pertracks = tSampleData->sliceBy(perDataCol, collision.globalIndex()); auto nTrk = 0; if (collision.sel8()) { @@ -392,6 +595,19 @@ struct DndetaHi { } } + if (bookMultiplicityCalibration) { + const auto calibCent = std::isfinite(cent) && cent >= 0.f && cent <= 100.f ? std::min(cent, std::nextafter(100.f, 0.f)) : -1.f; + for (auto trigger = 1u; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) { + continue; + } + registry.fill(HIST("multCalib/hDataEvents"), trigger, z, calibCent, collision.multNTracksPV()); + for (const auto& track : pertracks) { + registry.fill(HIST("multCalib/hDataTracks"), trigger, z, track.eta(), calibCent); + } + } + } + if (isPbPb) { if (std::abs(z) < CutZ && btrigc[TrigSel8]) { registry.fill(HIST("hcentrality"), cent); @@ -428,11 +644,21 @@ struct DndetaHi { PresliceUnsorted> perMcCol = o2::aod::mccollisionlabel::mcCollisionId; Preslice perMCColparticles = aod::mcparticle::mcCollisionId; Preslice perColFiLTracks = aod::track::collisionId; - using MCex = soa::Join; - - void processMCCounting( - const MCex& mcCollisions, + // McCollsExtra provides one deterministic reconstructed collision for split + // MC collisions (the collision with the largest number of contributors). + // Select exactly one correction mode through the process switches below: + // processMCCounting: official reconstructed FT0M + zero-reco BC fallback. + // processMCCountingPVCalibrated: McCentFT0Ms for both truth and reco. + // Both modes always fill ND/SD/DD INEL>0 categories, selectionLoss and + // trackingPt inputs; no separate booking switches are required. + using MCex = soa::Join; + + template + void runMCCounting( + const MCCollisions& mcCollisions, const soa::Join& collisions, + const BCsRun3Cent& /*bcs*/, + const aod::FT0s& /*ft0s*/, const Particles& mcParticles, const FiLTracks& tracks) { @@ -452,6 +678,7 @@ struct DndetaHi { auto mcz = mcCollision.posZ(); auto genz = mcz; auto nTrkGen = 0; + int nForwardGen = 0; auto particles = mcParticles.sliceBy(perMCCol, mcCollision.globalIndex()); for (const auto& particle : particles) { @@ -461,6 +688,9 @@ struct DndetaHi { auto kp = pdg->GetParticle(particle.pdgCode()); if (kp != nullptr) { if (std::abs(kp->Charge()) >= CutCharge) { + if (bookMultiplicityCalibration && ((particle.eta() > 3.5f && particle.eta() < 4.9f) || (particle.eta() > -3.3f && particle.eta() < -2.1f))) { + ++nForwardGen; + } if (std::abs(particle.eta()) < CutEta1) { nTrkGen++; } @@ -470,6 +700,10 @@ struct DndetaHi { if (nTrkGen > 0) { registry.fill(HIST("Selection"), SelGeneratedInelGt0); bevtc[EvtInelg0] = true; + // Mandatory process-resolved INEL>0 correction categories in BOTH modes. + bevtc[EvtInelg0Dd] = bevtc[EvtDd]; + bevtc[EvtInelg0Sd] = bevtc[EvtSd]; + bevtc[EvtInelg0Nd] = bevtc[EvtNd]; } if (bevtc[EvtInel]) { registry.fill(HIST("Selection"), SelInel); @@ -506,30 +740,119 @@ struct DndetaHi { } auto collisionsample = collisions.sliceBy(perMcCol, mcCollision.globalIndex()); - auto centMC = -1.f; - if (collisionsample.size() != 1) { - centMC = -1.0; - } else { + const auto nRecoCollisions = collisionsample.size(); + const bool hasRecoCollision = nRecoCollisions > 0; + const bool isCorrectionSample = bevtc[EvtInelg0] && std::abs(mcz) < CutZ; + + if (isCorrectionSample) { + registry.fill(HIST("hMCRecoCollisionsPerGenINELg0"), nRecoCollisions); + } + + // Detector-level definition: + // * >=1 reconstructed collision: official collision FT0M percentile; + // * 0 reconstructed collisions: use valid FT0M information from the + // associated BC, so these generated events and particles remain in + // a detector-estimator class even though reconstruction failed. + // If no usable BC FT0M exists, retain underflow and record the reason. + // BC FT0M and collision FT0M have different vertex-equalization inputs; + // the fallback applies only to zero-reco events and is monitored below. + // For split vertices only the deterministic best reconstructed collision + // defines the nominal class and contributes to reconstructed histograms. + auto centForCorrection = NoCentrality; + auto centSource = CentSourceInvalidRecoFT0M; + auto zeroRecoClass = ZeroRecoCentInvalid; + auto rawZeroRecoBCCent = NoCentrality; + + if (hasRecoCollision && mcCollision.bestCollisionIndex() >= 0) { for (const auto& collision : collisionsample) { - if (isPbPb) { - if constexpr (MyCollisionsCent::template contains()) { - centMC = collision.centFT0C(); + if (collision.globalIndex() == mcCollision.bestCollisionIndex()) { + if (isPbPb) { + if constexpr (MyCollisionsCent::template contains()) { + centForCorrection = collision.centFT0C(); + centSource = CentSourceRecoFT0M; + } + } else { + if constexpr (MyCollisionsCent::template contains()) { + const auto centBest = collision.centFT0M(); + if (std::isfinite(centBest) && centBest >= 0.f && centBest <= 100.f) { + centForCorrection = std::min(centBest, std::nextafter(100.f, 0.f)); + centSource = CentSourceRecoFT0M; + } else { + LOGP(warn, "Invalid reconstructed FT0M percentile {} for best collision {}; keeping this event in the centrality underflow", centBest, collision.globalIndex()); + } + } } + break; + } + } + } else if (!hasRecoCollision && !isPbPb) { + // As in the Run-2 event-level estimator approach, a failed vertex + // reconstruction must not discard otherwise available detector + // percentile information from the generated correction denominator. + // Use the BC estimator only when it has a matched FT0 and a valid + // percentile; do not invent a class for missing detector information. + centSource = CentSourceZeroRecoNoBC; + zeroRecoClass = ZeroRecoCentNoBC; + if (mcCollision.has_bc()) { + const auto mcBC = mcCollision.template bc_as(); + rawZeroRecoBCCent = mcBC.centFT0M(); + if (!mcBC.has_foundFT0()) { + centSource = CentSourceZeroRecoNoFT0; + zeroRecoClass = ZeroRecoCentNoFT0; + } else if (std::isfinite(rawZeroRecoBCCent) && rawZeroRecoBCCent >= 0.f && rawZeroRecoBCCent <= 100.f) { + centForCorrection = std::min(rawZeroRecoBCCent, std::nextafter(100.f, 0.f)); + centSource = CentSourceZeroRecoBCFT0M; + zeroRecoClass = getZeroRecoCentralityClass(rawZeroRecoBCCent); } else { - if constexpr (MyCollisionsCent::template contains()) { - centMC = collision.centFT0M(); - } + centSource = CentSourceZeroRecoInvalidBCFT0M; + zeroRecoClass = ZeroRecoCentInvalid; + } + } + } + + if (isCorrectionSample) { + registry.fill(HIST("hMCCentralitySourceINELg0"), centSource); + if (!hasRecoCollision) { + if (std::isfinite(rawZeroRecoBCCent)) { + registry.fill(HIST("hZeroRecoBCFT0MPercentileINELg0"), rawZeroRecoBCCent); + } + registry.fill(HIST("hZeroRecoBCFT0MClassINELg0"), zeroRecoClass); + } else if (!isPbPb && centSource == CentSourceRecoFT0M && mcCollision.has_bc()) { + const auto mcBC = mcCollision.template bc_as(); + const auto bcCent = mcBC.centFT0M(); + if (mcBC.has_foundFT0() && std::isfinite(bcCent) && bcCent >= 0.f && bcCent <= 100.f) { + registry.fill(HIST("hBCFT0MvsRecoFT0MINELg0"), centForCorrection, std::min(bcCent, std::nextafter(100.f, 0.f))); } } } + // Preserve detector QA and raw calibration correlation. In the optional + // PV-matched branch, ALL correction inputs use one MC-table class, + // including events with no reconstructed collision. Nominal path unchanged. + const auto detectorCentrality = centForCorrection; + if constexpr (MCCollisions::template contains()) { + const auto calibrated = mcCollision.centFT0M(); + if (!std::isfinite(calibrated) || calibrated < 0.f || calibrated >= 100.f) { + LOGP(fatal, "Invalid PV-matched MC centrality {} for MC collision {}", calibrated, mcCollision.globalIndex()); + } + centForCorrection = calibrated; + if (bevtc[EvtInelg0]) { + registry.fill(HIST("pvMatched/hGenClassINELgt0"), calibrated); + } + } + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { if (bevtc[ievtc]) { - registry.fill(HIST("hgenzvtx"), ievtc, genz, centMC); + registry.fill(HIST("hgenzvtx"), ievtc, genz, centForCorrection); } } int pid = 0; + struct LossParticle { + double eta, phi, pt; + int species; + }; + std::vector lossParticles; std::vector particleetas; for (const auto& particle : particles) { auto p = pdg->GetParticle(particle.pdgCode()); @@ -558,34 +881,73 @@ struct DndetaHi { if (std::abs(p->Charge()) >= CutCharge) { for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { if (bevtc[ievtc]) { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarNone, particle.phi()); + if (std::abs(genz) < CutZ && std::abs(particle.eta()) < CutEta08) { + registry.fill(HIST("trackingPt/hAllGenerated"), ievtc, centForCorrection, particle.pt()); + } + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarNone, particle.phi()); if (particle.pt() < CutPt) { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi(), PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp); - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi(), PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown); + const auto weightUp = PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp; + const auto weightDown = PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown; + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi(), weightUp); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi(), weightDown); } else { - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi()); - registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi()); } } } if (pid >= PartPion && pid <= PartOther) { particleetas.push_back(particle.eta()); + if (bevtc[EvtInelg0]) { + lossParticles.push_back({particle.eta(), particle.phi(), particle.pt(), pid}); + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarNone, particle.phi()); + const double up = particle.pt() < CutPt ? PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp : 1.; + const double down = particle.pt() < CutPt ? PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown : 1.; + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarUp, particle.phi(), up); + registry.fill(HIST("selectionLoss/hAllParticles"), genz, particle.eta(), centForCorrection, pid, PtVarDown, particle.phi(), down); + } } } } } - for (const auto& collision : collisionsample) { - auto centRec = -1.f; - if (isPbPb) { - if constexpr (MyCollisionsCent::template contains()) { - centRec = collision.centFT0C(); + // Full generated INEL>0 denominator. This must not depend on whether the + // event has zero, one, or several reconstructed collisions. + if (bookMultiplicityCalibration) { + for (auto eventClass = 1u; eventClass < static_cast(EvtEnd); ++eventClass) { + if (!bevtc[eventClass]) { + continue; } - } else { - if constexpr (MyCollisionsCent::template contains()) { - centRec = collision.centFT0M(); + registry.fill(HIST("multCalib/hGenEvents"), eventClass, genz, nForwardGen, nRecoCollisions); + for (const auto& eta : particleetas) { + registry.fill(HIST("multCalib/hGenParticles"), eventClass, genz, eta, nForwardGen); + } + } + } + if (bevtc[EvtInelg0]) { + for (const auto& eta : particleetas) { + registry.fill(HIST("genetaINELg0genz10"), eta, mcz); + } + registry.fill(HIST("genzINELg0"), genz); + registry.fill(HIST("selectionLoss/hAllEvents"), genz, centForCorrection); + if (bookFactorizedCorrections) { + registry.fill(HIST("factorized/hGenEvents"), genz, centForCorrection); + for (const auto& eta : particleetas) { + registry.fill(HIST("factorized/hGenParticles"), genz, eta, centForCorrection); } } + } + + for (const auto& collision : collisionsample) { + // A generated collision contributes at most one reconstructed + // collision to the correction sample. + const bool isBestCollision = collision.globalIndex() == mcCollision.bestCollisionIndex(); + if (!isBestCollision) { + continue; + } + // The loop contains only the best reconstructed collision, so use the + // same sanitized official collision centrality as the MC denominator. + const auto centRec = centForCorrection; BoolVector btrigc(TrigEnd, false); auto z = collision.posZ(); @@ -600,7 +962,7 @@ struct DndetaHi { registry.fill(HIST("hft0multiplicity"), collision.multFT0C()); registry.fill(HIST("Selection"), SelInelSel8Mcz10); } - if (collisionsample.size() == 1 && bevtc[EvtInelg0] && btrigc[TrigSel8]) { + if (isBestCollision && bevtc[EvtInelg0] && btrigc[TrigSel8]) { for (const auto& eta : particleetas) { registry.fill(HIST("genetaINELg0Sel8recz10"), eta, z); registry.fill(HIST("genetaINELg0Sel8genz10"), eta, mcz); @@ -608,12 +970,6 @@ struct DndetaHi { registry.fill(HIST("reczINELg0Sel8"), z); registry.fill(HIST("genzINELg0Sel8"), genz); } - if (collisionsample.size() == 1 && bevtc[EvtInelg0]) { - for (const auto& eta : particleetas) { - registry.fill(HIST("genetaINELg0genz10"), eta, mcz); - } - registry.fill(HIST("genzINELg0"), genz); - } auto nTrkRec = 0; auto trackspart = tracks.sliceBy(perColFiLTracks, collision.globalIndex()); @@ -628,6 +984,101 @@ struct DndetaHi { btrigc[TrigSel8g0] = true; } } + if (bookMultiplicityCalibration) { + for (auto trigger = 1u; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) { + continue; + } + registry.fill(HIST("multCalib/hClassCorrelation"), trigger, genz, z, nForwardGen, detectorCentrality); + for (auto eventClass = 1u; eventClass < static_cast(EvtEnd); ++eventClass) { + if (!bevtc[eventClass]) { + continue; + } + registry.fill(HIST("multCalib/hSelectedEvents"), eventClass, trigger, genz, z, nForwardGen, collision.multNTracksPV()); + for (const auto& eta : particleetas) { + registry.fill(HIST("multCalib/hSelectedGenParticles"), eventClass, trigger, genz, z, eta, nForwardGen); + } + for (const auto& track : trackspart) { + registry.fill(HIST("multCalib/hSelectedRecTracks"), eventClass, trigger, genz, z, track.eta(), nForwardGen); + } + } + } + } + + if (bevtc[EvtInelg0]) { + for (unsigned trigger = 1; trigger < static_cast(TrigEnd); ++trigger) { + if (!btrigc[trigger]) + continue; + registry.fill(HIST("selectionLoss/hSelectedEvents"), trigger, genz, z, centRec); + for (const auto& p : lossParticles) { + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarNone, p.phi); + const double up = p.pt < CutPt ? PtWeightSlopeUp * p.pt + PtWeightInterceptUp : 1.; + const double down = p.pt < CutPt ? PtWeightSlopeDown * p.pt + PtWeightInterceptDown : 1.; + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarUp, p.phi, up); + registry.fill(HIST("selectionLoss/hSelectedParticles"), trigger, genz, z, p.eta, centRec, p.species, PtVarDown, p.phi, down); + } + } + } + + // Independent diagnostic: identical selected event and truth fiducial + // denominator for every reconstructed numerator particle. Deduplicate + // only these new counts; do not change nominal track categorization. + if (std::abs(genz) < CutZ && std::abs(z) < CutZ) { + std::unordered_set matchedPrimaryIds; + for (const auto& track : trackspart) { + if (!track.has_mcParticle() || std::abs(track.eta()) >= CutEta08) { + continue; + } + const auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != mcCollision.globalIndex() || !particle.isPhysicalPrimary() || std::abs(particle.eta()) >= CutEta08) { + continue; + } + const auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp != nullptr && std::abs(kp->Charge()) >= CutCharge) { + matchedPrimaryIds.insert(particle.globalIndex()); + } + } + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= CutEta08) { + continue; + } + const auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp == nullptr || std::abs(kp->Charge()) < CutCharge) { + continue; + } + const bool matched = matchedPrimaryIds.count(particle.globalIndex()) != 0; + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ++ievtc) { + if (!bevtc[ievtc]) + continue; + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); ++itrigc) { + if (!btrigc[itrigc]) + continue; + registry.fill(HIST("trackingPt/hSelectedGenerated"), ievtc, itrigc, centRec, particle.pt()); + if (matched) { + registry.fill(HIST("trackingPt/hMatchedPrimary"), ievtc, itrigc, centRec, particle.pt()); + } + } + } + } + } + if (bookFactorizedCorrections && bevtc[EvtInelg0]) { + // Best-collision guard above ensures each generated event is counted + // at most once per trigger. No additional vertex/centrality cuts here. + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); ++itrigc) { + if (!btrigc[itrigc]) { + continue; + } + registry.fill(HIST("factorized/hSelectedEvents"), itrigc, genz, z, centRec); + for (const auto& eta : particleetas) { + registry.fill(HIST("factorized/hSelectedGenParticles"), itrigc, genz, z, eta, centRec); + } + // Include backgrounds, duplicates and unlabelled tracks, as in raw + // data. Their net effect belongs in the detector correction ratio. + for (const auto& track : trackspart) { + registry.fill(HIST("factorized/hSelectedRecParticles"), itrigc, genz, z, track.eta(), centRec); + } + } + } if (isPbPb) { if (std::abs(z) < CutZ && btrigc[TrigSel8]) { registry.fill(HIST("hcentrality"), centRec); @@ -724,7 +1175,232 @@ struct DndetaHi { } } - PROCESS_SWITCH(DndetaHi, processMCCounting, "MC Count tracks", false); + void processMCCounting(const MCex& mcCollisions, + const soa::Join& collisions, + const BCsRun3Cent& bcs, const aod::FT0s& ft0s, + const Particles& particles, const FiLTracks& tracks) + { + runMCCounting(mcCollisions, collisions, bcs, ft0s, particles, tracks); + } + PROCESS_SWITCH(DndetaHi, processMCCounting, "MC Count tracks (detector FT0M with BC fallback)", false); + + using MCWithPVCalibration = soa::Join; + void processMCCountingPVCalibrated(const MCWithPVCalibration& mcCollisions, + const soa::Join& collisions, + const BCsRun3Cent& bcs, const aod::FT0s& ft0s, + const Particles& particles, const FiLTracks& tracks) + { + runMCCounting(mcCollisions, collisions, bcs, ft0s, particles, tracks); + } + PROCESS_SWITCH(DndetaHi, processMCCountingPVCalibrated, "MC corrections using PV-matched McCentFT0Ms for both generated and reconstructed entries", false); + + // Optional second-pass branch. Enabling this alongside processMCCounting + // records BOTH methods' inputs without filling any nominal histogram twice. + // Only this signature requests the generator-percentile table/calibration. + using MCWithUnfoldingCent = soa::Join; + + // First pass: independent of reconstructed vertices and percentile tables. + void processMCUnfoldingCalib(aod::McCollisions const& collisions, aod::McParticles const& particles) + { + std::unordered_map> counts; + for (const auto& collision : collisions) { + counts.emplace(collision.globalIndex(), std::array{0, 0}); + } + for (const auto& particle : particles) { + auto event = counts.find(particle.mcCollisionId()); + if (event == counts.end() || !particle.isPhysicalPrimary()) { + continue; + } + const auto* definition = pdg->GetParticle(particle.pdgCode()); + if (!definition || std::abs(definition->Charge()) < CutCharge) { + continue; + } + const auto eta = particle.eta(); + if (std::abs(eta) < CutEta1) { + ++event->second[0]; + } + if ((eta > 3.5f && eta < 4.9f) || (eta > -3.3f && eta < -2.1f)) { + ++event->second[1]; + } + } + for (const auto& collision : collisions) { + const auto& count = counts.at(collision.globalIndex()); + if (count[0] > 0) { + registry.fill(HIST("unfoldingCalib/hFT0MInelGt0"), count[1]); + if (std::abs(collision.posZ()) < CutZ) { + registry.fill(HIST("unfoldingCalib/hFT0MInelGt0Z10"), count[1]); + } + } + } + } + PROCESS_SWITCH(DndetaHi, processMCUnfoldingCalib, "First pass: generated INEL>0 FT0 multiplicity without percentile calibration", false); + + void processMCUnfolding( + const MCWithUnfoldingCent& mcCollisions, + const soa::Join& collisions, + const Particles& mcParticles, + const FiLTracks& tracks) + { + struct TruthEvent { + float z = 0.f; + float cent = NoCentrality; + int64_t bestCollision = -1; + bool inelg0 = false; + bool fiducial = false; + std::array matched{}; + }; + std::unordered_map truth; + truth.reserve(mcCollisions.size()); + std::array, TrigEnd> matchedParticles; + + auto isChargedPrimary = [this](const auto& particle) { + if (!particle.isPhysicalPrimary()) { + return false; + } + const auto* definition = pdg->GetParticle(particle.pdgCode()); + return definition != nullptr && std::abs(definition->Charge()) >= CutCharge; + }; + auto species = [](const auto& particle) { + switch (std::abs(particle.pdgCode())) { + case kPiPlus: + return PartPion; + case kKPlus: + return PartKaon; + case kProton: + return PartProton; + default: + return PartOther; + } + }; + auto validPercentile = [](float percentile) { + return std::isfinite(percentile) && percentile >= 0.f && percentile <= 100.f; + }; + auto insidePercentile = [](float percentile) { + return std::min(percentile, std::nextafter(100.f, 0.f)); + }; + + // Use global IDs, not row numbers: input slices may have nonzero offsets. + for (const auto& collision : mcCollisions) { + truth.emplace(collision.globalIndex(), TruthEvent{collision.posZ(), collision.centFT0M(), collision.bestCollisionIndex()}); + } + for (const auto& particle : mcParticles) { + auto event = truth.find(particle.mcCollisionId()); + if (event != truth.end() && isChargedPrimary(particle) && std::abs(particle.eta()) < CutEta1) { + event->second.inelg0 = true; + } + } + for (auto& [id, event] : truth) { + event.fiducial = event.inelg0 && std::abs(event.z) < unfoldingZMax; + if (!event.fiducial) { + continue; + } + if (!validPercentile(event.cent)) { + LOGP(fatal, "Invalid generated INEL>0 percentile {} for MC collision {}: processMCUnfolding requires the FIRST-PASS FT0M INEL>0 calibration, not reconstructed/BC fallback percentiles", event.cent, id); + } + event.cent = insidePercentile(event.cent); + registry.fill(HIST("unfolding/hGenEvents"), event.z, event.cent); + } + + // Traverse ALL measured collisions, including non-best split collisions + // and collisions with no MC label. Data cannot apply the MC best label. + for (const auto& collision : collisions) { + const auto z = collision.posZ(); + if (!collision.sel8() || !(std::abs(z) < unfoldingZMax)) { + continue; + } + const auto perTracks = tracks.sliceBy(perColFiLTracks, collision.globalIndex()); + bool hasTrackEta1 = false; + for (const auto& track : perTracks) { + if (std::abs(track.eta()) < estimatorEta && std::abs(track.eta()) < CutEta1) { + hasTrackEta1 = true; + break; + } + } + const auto rawCent = collision.centFT0M(); + const bool validRecoCent = validPercentile(rawCent); + const auto centRec = validRecoCent ? insidePercentile(rawCent) : NoCentrality; + auto event = truth.find(collision.mcCollisionId()); + int eventExtraReason = 0; + if (!collision.has_mcCollision() || event == truth.end()) { + eventExtraReason = 1; + } else if (!event->second.fiducial) { + eventExtraReason = 2; + } else if (collision.globalIndex() != event->second.bestCollision) { + eventExtraReason = 3; + } else if (!validRecoCent) { + eventExtraReason = 4; + } + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (trigger == TrigSel8g0 && !hasTrackEta1) { + continue; + } + registry.fill(HIST("unfolding/hRecEvents"), trigger, z, centRec); + if (eventExtraReason != 0) { + registry.fill(HIST("unfolding/hExtraEvents"), trigger, z, centRec, eventExtraReason); + } else { + event->second.matched[trigger] = true; + registry.fill(HIST("unfolding/hEventResponse"), trigger, event->second.z, z, event->second.cent, centRec); + } + for (const auto& track : perTracks) { + // Identical measured eta acceptance to runCounting/tSampleData. + if (!(std::abs(track.eta()) < estimatorEta)) { + continue; + } + registry.fill(HIST("unfolding/hRecParticles"), trigger, z, track.eta(), centRec); + int extraReason = eventExtraReason; + if (extraReason == 0 && !track.has_mcParticle()) { + extraReason = 5; + } + if (extraReason == 0) { + const auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != collision.mcCollisionId()) { + extraReason = 6; + } else if (!isChargedPrimary(particle)) { + extraReason = 7; + } else if (!(std::abs(particle.eta()) < estimatorEta)) { + extraReason = 8; + } else if (!matchedParticles[trigger].insert(particle.globalIndex()).second) { + extraReason = 9; + } else { + registry.fill(HIST("unfolding/hParticleResponse"), trigger, event->second.z, z, particle.eta(), track.eta(), event->second.cent, centRec, species(particle)); + } + } + if (extraReason != 0) { + registry.fill(HIST("unfolding/hExtraParticles"), trigger, z, track.eta(), centRec, extraReason); + } + } + } + } + + // Explicit miss histograms make the identities testable for EACH trigger: + // truth = response projected on truth + misses; + // measured MC = response projected on reco + extras. + for (const auto& [id, event] : truth) { + if (!event.fiducial) { + continue; + } + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (!event.matched[trigger]) { + registry.fill(HIST("unfolding/hMissEvents"), trigger, event.z, event.cent); + } + } + } + for (const auto& particle : mcParticles) { + const auto event = truth.find(particle.mcCollisionId()); + if (event == truth.end() || !event->second.fiducial || !isChargedPrimary(particle) || !(std::abs(particle.eta()) < estimatorEta)) { + continue; + } + const auto pid = species(particle); + registry.fill(HIST("unfolding/hGenParticles"), event->second.z, particle.eta(), event->second.cent, pid); + for (int trigger = TrigSel8; trigger < TrigEnd; ++trigger) { + if (matchedParticles[trigger].find(particle.globalIndex()) == matchedParticles[trigger].end()) { + registry.fill(HIST("unfolding/hMissParticles"), trigger, event->second.z, particle.eta(), event->second.cent, pid); + } + } + } + } + + PROCESS_SWITCH(DndetaHi, processMCUnfolding, "Also store independent generated-INEL>0/reconstructed-FT0M unfolding inputs (requires McCentFT0Ms)", false); // void processTrackEfficiencyGeneral( // MyCollisionsMcLabels::iterator const& collision, @@ -746,7 +1422,7 @@ struct DndetaHi { void processCounting( const MyCollisionsCent& collisions, - const FiTracks& tracks) + const FiDataTracks& tracks) { runCounting(collisions, tracks); } From 0862996abc48ba96940914f8d39e788079604cc2 Mon Sep 17 00:00:00 2001 From: skundu692 <86804743+skundu692@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:38:26 +0100 Subject: [PATCH 634/648] [PWGLF] Remove width correction and add shift correction in zdc calibrtaion (#18670) --- .../Common/zdc2stagecalibration.cxx | 160 +++++++++++------- 1 file changed, 95 insertions(+), 65 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx index effa33e1b0a..7b87fecc482 100644 --- a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -82,10 +82,10 @@ struct zdc2stagecalibration { Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; - Configurable useQWidthEqualization{"useQWidthEqualization", false, "equalize Qx/Qy widths after Structured71 recentering"}; - Configurable confQWidthPath{"confQWidthPath", "", "CCDB path to post-recentering Q-width map"}; - Configurable useQTwist{"useQTwist", false, "apply same-side Qx-Qy twist correction after width equalization"}; + Configurable useQTwist{"useQTwist", false, "apply same-side Qx-Qy correlation de-twist after recentering (no width equalization)"}; Configurable confQTwistPath{"confQTwistPath", "", "CCDB path to the run-wise Q-twist map"}; + Configurable useQShift{"useQShift", false, "apply ZDC azimuthal shift correction after twist"}; + Configurable confQShiftPath{"confQShiftPath", "", "CCDB path to the run-wise Q-shift Fourier map"}; Configurable cfgSpatialCalibBins{ "cfgSpatialCalibBins", 30, "number of bins per cross coordinate for spatial calibration"}; @@ -104,8 +104,8 @@ struct zdc2stagecalibration { TH2D* gainprofile = nullptr; TH3D* spatialprofile = nullptr; TH3D* qRecenteringProfile = nullptr; - TH2D* qWidthProfile = nullptr; TH2D* qTwistProfile = nullptr; + TH2D* qShiftProfile = nullptr; static constexpr int kQRecenteringNFeatures = 21; static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; @@ -114,7 +114,7 @@ struct zdc2stagecalibration { static constexpr int kQRecenteringMultidimNFeatures = 35; static constexpr int kQRecenteringMultidimNMatrixMoments = kQRecenteringMultidimNFeatures * (kQRecenteringMultidimNFeatures + 1) / 2; static constexpr int kQRecenteringMultidimNMoments = kQRecenteringMultidimNMatrixMoments + kQRecenteringNComponents * kQRecenteringMultidimNFeatures; - // Additional sufficient statistics for the flatness-constrained multidimensional fit. + // Per conditioning bin store 35 sums of the cubic features followed by // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N. static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents; @@ -128,7 +128,6 @@ struct zdc2stagecalibration { static constexpr int kQRecenteringStructuredNFeatures = kQRecenteringMultidimNFeatures + 4 * kQRecenteringStructuredNKnots; static constexpr int kQRecenteringStructuredNMatrixMoments = kQRecenteringStructuredNFeatures * (kQRecenteringStructuredNFeatures + 1) / 2; static constexpr int kQRecenteringStructuredNMoments = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + kQRecenteringNComponents; - // Per conditioning bin store sum(features), sum(Q) and sum(Q^2). static constexpr int kQRecenteringStructuredFlatnessNMoments = kQRecenteringStructuredNFeatures + 2 * kQRecenteringNComponents; void init(o2::framework::InitContext&) @@ -137,11 +136,11 @@ struct zdc2stagecalibration { if (qRecenteringMethod.value != 0 && qRecenteringMethod.value != 1 && qRecenteringMethod.value != 4) { LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression, 1 for the multidimensional cubic regression or 4 for the structured71 regression.", qRecenteringMethod.value); } - if (useQWidthEqualization.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQWidthPath.value.empty())) { - LOGF(fatal, "Width equalization requires stage=2, useQRecentering=true, method=4 and confQWidthPath"); + if (useQTwist.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQTwistPath.value.empty())) { + LOGF(fatal, "Twist correction requires stage=2, Structured71 recentering and confQTwistPath"); } - if (useQTwist.value && (!useQWidthEqualization.value || confQTwistPath.value.empty())) { - LOGF(fatal, "Twist correction requires width equalization and confQTwistPath"); + if (useQShift.value && (!useQTwist.value || confQShiftPath.value.empty())) { + LOGF(fatal, "Shift correction requires twist correction and confQShiftPath"); } const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; @@ -151,6 +150,7 @@ struct zdc2stagecalibration { AxisSpec spatialMomentAxis = {6, 0.0, 6.0, "normalized pair regression moment"}; AxisSpec spatialCalibAxis = {cfgSpatialCalibBins.value, -1.0, 1.0, "cross asymmetry"}; AxisSpec phiAxis = {72, -3.14159265358979323846, 3.14159265358979323846, "#phi"}; + AxisSpec phiDerivationAxis = {720, -3.14159265358979323846, 3.14159265358979323846, "#phi"}; AxisSpec crossCoordAxis = {2, 0.0, 2.0, "coordinate"}; AxisSpec ratioAxis = {240, 0.0, 2.4, "#Sigma tower/common"}; AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; @@ -268,13 +268,20 @@ struct zdc2stagecalibration { histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); histos.add("ResolutionQA/pCosPsiAPsiCVsCentralityVz", "ZDC A-C correlation;centrality (%);v_{z} (cm); <#cos(#Psi_{A}-#Psi_{C})>", kTProfile2D, {resolutionCentralityAxis, resolutionVzAxis}); - histos.add("QWidthQA/pQAfterWidthEqVsCentrality", "Q after width equalization;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + // Fine-bin recentered azimuths allow both twist and shift maps to be derived + // offline from a single recentering-only pass. No Q-magnitude normalization. + histos.add("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNA", "Recentered ZNA #phi for twist/shift derivation;centrality (%);#phi", kTH2F, {centralityAxis, phiDerivationAxis}); + histos.add("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNC", "Recentered ZNC #phi for twist/shift derivation;centrality (%);#phi", kTH2F, {centralityAxis, phiDerivationAxis}); histos.add("QTwistQA/pMomentsBeforeVsCentrality", "Q moments before twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); histos.add("QTwistQA/pMomentsAfterVsCentrality", "Q moments after twist;centrality (%);moment;", kTProfile2D, {centralityAxis, qTwistMomentAxis}); histos.add("PhiQA/hPhiAfterTwistVsCentralityZNA", "ZNA #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); histos.add("PhiQA/hPhiAfterTwistVsCentralityZNC", "ZNC #phi after twist;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); - histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNA", "ZNA #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); - histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNC", "ZNC #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/hPhiAfterShiftVsCentralityZNA", "ZNA #phi after shift;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/hPhiAfterShiftVsCentralityZNC", "ZNC #phi after shift;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA", "ZNA harmonics before shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC", "ZNC harmonics before shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA", "ZNA harmonics after shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); + histos.add("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC", "ZNC harmonics after shift: cos,sin n=1..4;centrality (%);harmonic index;", kTProfile2D, {centralityAxis, phiHarmonicAxis}); histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); @@ -312,7 +319,7 @@ struct zdc2stagecalibration { histos.add("QCorrelationQA/pQACorrelationBeforeVsCentrality", "A-C Q correlations before recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); histos.add("QCorrelationQA/pQACorrelationAfterVsCentrality", "A-C Q correlations after recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); - // Final-calibration QA: only filled after recentering, width and twist. + // Final-calibration QA: after recentering, correlation-only twist and optional shift. histos.add("final_calibration/hQxAVsCentrality", "Final QxA vs centrality;centrality (%);Q_{x}^{A}", kTH2F, {centralityAxis, qValueAxis}); histos.add("final_calibration/hQyAVsCentrality", "Final QyA vs centrality;centrality (%);Q_{y}^{A}", kTH2F, {centralityAxis, qValueAxis}); histos.add("final_calibration/hQxCVsCentrality", "Final QxC vs centrality;centrality (%);Q_{x}^{C}", kTH2F, {centralityAxis, qValueAxis}); @@ -359,8 +366,8 @@ struct zdc2stagecalibration { gainprofile = nullptr; spatialprofile = nullptr; qRecenteringProfile = nullptr; - qWidthProfile = nullptr; qTwistProfile = nullptr; + qShiftProfile = nullptr; if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); if (!gainprofile) { @@ -390,30 +397,31 @@ struct zdc2stagecalibration { } } } - if (calibrationStage.value == 2 && useQWidthEqualization.value) { - qWidthProfile = ccdb->getForTimeStamp(confQWidthPath.value, timestamp); - if (!qWidthProfile || qWidthProfile->GetNbinsX() != 80 || qWidthProfile->GetNbinsY() != 4) { - LOGF(fatal, "Missing/invalid Q-width map for run %d", runNumber); - } - constexpr std::array labels = {"QxA", "QyA", "QxC", "QyC"}; - for (int i = 0; i < 4; ++i) { - if (std::string(qWidthProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { - LOGF(fatal, "Incorrect Q-width map component ordering"); - } - } - } if (calibrationStage.value == 2 && useQTwist.value) { qTwistProfile = ccdb->getForTimeStamp(confQTwistPath.value, timestamp); if (!qTwistProfile || qTwistProfile->GetNbinsX() != 80 || qTwistProfile->GetNbinsY() != 6) { LOGF(fatal, "Missing/invalid ZDC twist map for run %d", runNumber); } - constexpr std::array labels = {"MxxA", "MxyA", "MyyA", "MxxC", "MxyC", "MyyC"}; + constexpr std::array labels = {"ShearMxxA", "ShearMxyA", "ShearMyyA", "ShearMxxC", "ShearMxyC", "ShearMyyC"}; for (int i = 0; i < 6; ++i) { if (std::string(qTwistProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { LOGF(fatal, "Incorrect ZDC twist map component ordering for run %d", runNumber); } } } + if (calibrationStage.value == 2 && useQShift.value) { + qShiftProfile = ccdb->getForTimeStamp(confQShiftPath.value, timestamp); + if (!qShiftProfile || qShiftProfile->GetNbinsX() != 80 || qShiftProfile->GetNbinsY() != 16) { + LOGF(fatal, "Missing/invalid ZDC shift map for run %d", runNumber); + } + constexpr std::array labels = {"cos1A", "sin1A", "cos2A", "sin2A", "cos3A", "sin3A", "cos4A", "sin4A", + "cos1C", "sin1C", "cos2C", "sin2C", "cos3C", "sin3C", "cos4C", "sin4C"}; + for (int i = 0; i < 16; ++i) { + if (std::string(qShiftProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect ZDC shift map component ordering for run %d", runNumber); + } + } + } } float getTimeFromSOR(uint64_t globalBC) const @@ -1107,46 +1115,19 @@ struct zdc2stagecalibration { histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNC"), phiRecenteringC); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNA"), centrality, phiRecenteringA); histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNC"), centrality, phiRecenteringC); - - if (useQWidthEqualization.value) { - const int centBin = qWidthProfile->GetXaxis()->FindFixBin(centrality); - if (centBin < 1 || centBin > qWidthProfile->GetNbinsX()) { - LOGF(fatal, "Invalid ZDC Q-width centrality bin for run %d: centrality=%.6f", runNumber, static_cast(centrality)); - fillTable(false, -999.f, -999.f, -999.f, -999.f); - return; - } - std::array sigma{}; - for (int i = 0; i < 4; ++i) { - sigma[i] = qWidthProfile->GetBinContent(centBin, i + 1); - if (!std::isfinite(sigma[i]) || sigma[i] <= 0.0) { - LOGF(fatal, "Invalid ZDC Q-width sigma for run %d: centrality=%.6f, component=%d, sigma=%.6g", runNumber, static_cast(centrality), i, sigma[i]); - fillTable(false, -999.f, -999.f, -999.f, -999.f); - return; - } - } - const double factorA = std::sqrt(sigma[1] / sigma[0]); - const double factorC = std::sqrt(sigma[3] / sigma[2]); - qValues[0] *= factorA; - qValues[1] /= factorA; - qValues[2] *= factorC; - qValues[3] /= factorC; - for (int i = 0; i < 4; ++i) { - histos.fill(HIST("QWidthQA/pQAfterWidthEqVsCentrality"), centrality, i + 0.5, qValues[i]); - } + if (calibrationStage.value == 2 && useQRecentering.value) { + histos.fill(HIST("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNA"), centrality, phiRecenteringA); + histos.fill(HIST("QTwistQA/hPhiBeforeTwistHighResVsCentralityZNC"), centrality, phiRecenteringC); } - const double phiWidthA = std::atan2(qValues[1], qValues[0]); - const double phiWidthC = std::atan2(qValues[3], qValues[2]); - if (useQWidthEqualization.value) { - histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNA"), centrality, phiWidthA); - histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNC"), centrality, phiWidthC); - } const auto makeTwistMoments = [](const std::array& q) { return std::array{q[0], q[1], q[0] * q[0], q[1] * q[1], q[0] * q[1], q[2], q[3], q[2] * q[2], q[3] * q[3], q[2] * q[3]}; }; const auto momentsBeforeTwist = makeTwistMoments(qValues); - if (useQWidthEqualization.value) { + // Correlation moments are collected directly after recentering, BEFORE twist. + // Derive the run-wise shear map from these same-side moments (no width map). + if (calibrationStage.value == 2 && useQRecentering.value) { for (int i = 0; i < 10; ++i) { histos.fill(HIST("QTwistQA/pMomentsBeforeVsCentrality"), centrality, i + 0.5, momentsBeforeTwist[i]); } @@ -1162,7 +1143,9 @@ struct zdc2stagecalibration { const double mxx = qTwistProfile->GetBinContent(centBin, offset + 1); const double mxy = qTwistProfile->GetBinContent(centBin, offset + 2); const double myy = qTwistProfile->GetBinContent(centBin, offset + 3); - if (!std::isfinite(mxx) || !std::isfinite(mxy) || !std::isfinite(myy) || mxx <= 0.0 || myy <= 0.0 || mxx * myy <= mxy * mxy) { + // Enforce the new shear-map format. Old covariance-whitening maps are incompatible. + if (!std::isfinite(mxx) || !std::isfinite(mxy) || !std::isfinite(myy) || + std::abs(mxx - 1.0) > 1.e-10 || std::abs(myy - 1.0) > 1.e-10 || std::abs(mxy) >= 1.0) { fillTable(false, -999.f, -999.f, -999.f, -999.f); return; } @@ -1183,11 +1166,58 @@ struct zdc2stagecalibration { histos.fill(HIST("QTwistQA/pMomentsAfterVsCentrality"), centrality, i + 0.5, momentsAfterTwist[i]); } } - const double phiFinalA = std::atan2(qValues[1], qValues[0]); - const double phiFinalC = std::atan2(qValues[3], qValues[2]); + double phiFinalA = std::atan2(qValues[1], qValues[0]); + double phiFinalC = std::atan2(qValues[3], qValues[2]); if (useQTwist.value) { histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNA"), centrality, phiFinalA); histos.fill(HIST("PhiQA/hPhiAfterTwistVsCentralityZNC"), centrality, phiFinalC); + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNA"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsBeforeVsCentralityZNC"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } + } + if (useQShift.value) { + const int centBin = qShiftProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qShiftProfile->GetNbinsX()) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + for (int side = 0; side < 2; ++side) { + const int ix = 2 * side; + const double phi = std::atan2(qValues[ix + 1], qValues[ix]); + const double qMag = std::hypot(qValues[ix], qValues[ix + 1]); + double deltaPhi = 0.0; + for (int n = 1; n <= 4; ++n) { + const double cosMean = qShiftProfile->GetBinContent(centBin, 8 * side + 2 * n - 1); + const double sinMean = qShiftProfile->GetBinContent(centBin, 8 * side + 2 * n); + if (!std::isfinite(cosMean) || !std::isfinite(sinMean)) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + deltaPhi += (2.0 / n) * (cosMean * std::sin(n * phi) - sinMean * std::cos(n * phi)); + } + const double phiNew = phi + deltaPhi; + // Preserve |Q| and rotate both Cartesian components to the shifted angle. + qValues[ix] = qMag * std::cos(phiNew); + qValues[ix + 1] = qMag * std::sin(phiNew); + if (!std::isfinite(qValues[ix]) || !std::isfinite(qValues[ix + 1]) || + !std::isfinite(static_cast(qValues[ix])) || !std::isfinite(static_cast(qValues[ix + 1]))) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + phiFinalA = std::atan2(qValues[1], qValues[0]); + phiFinalC = std::atan2(qValues[3], qValues[2]); + histos.fill(HIST("QShiftQA/hPhiAfterShiftVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("QShiftQA/hPhiAfterShiftVsCentralityZNC"), centrality, phiFinalC); + for (int n = 1; n <= 4; ++n) { + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA"), centrality, 2 * n - 1.5, std::cos(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNA"), centrality, 2 * n - 0.5, std::sin(n * phiFinalA)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC"), centrality, 2 * n - 1.5, std::cos(n * phiFinalC)); + histos.fill(HIST("QShiftQA/pPhiHarmonicsAfterVsCentralityZNC"), centrality, 2 * n - 0.5, std::sin(n * phiFinalC)); + } } // ZDC resolution after all enabled corrections const double qMagA = std::hypot(qValues[0], qValues[1]); @@ -1198,7 +1228,7 @@ struct zdc2stagecalibration { histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentralityVz"), centrality, vz, correlation); } - if (calibrationStage.value == 2 && useQRecentering.value && useQWidthEqualization.value && useQTwist.value && qMagA > 0. && qMagC > 0.) { + if (calibrationStage.value == 2 && useQRecentering.value && useQTwist.value && qMagA > 0. && qMagC > 0.) { histos.fill(HIST("final_calibration/hQxAVsCentrality"), centrality, qValues[0]); histos.fill(HIST("final_calibration/hQyAVsCentrality"), centrality, qValues[1]); histos.fill(HIST("final_calibration/hQxCVsCentrality"), centrality, qValues[2]); From 4091c09dc1e5df827ff66f6bcbdba5e3b2d857c6 Mon Sep 17 00:00:00 2001 From: Sushanta Tripathy Date: Mon, 9 Nov 2026 21:33:47 +0100 Subject: [PATCH 635/648] [PWGLF] Improve TOF nsigma cut check for deuterons (#18671) --- PWGLF/Tasks/Resonances/lambdastarproxy.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx index e4fbed337ad..b923950622e 100644 --- a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -1739,7 +1739,7 @@ struct Lambdastarproxy { // the track has a valid TOF match. Tracks without TOF are kept with // the TPC-only decision, avoiding an additional TOF-matching efficiency loss. if (isDeuteron) { - if (std::abs(nsTPC) >= tpcCut) { + if (!std::isfinite(nsTPC) || std::abs(nsTPC) >= tpcCut) { return false; } if (lstarEnableTOFNsigmaCutDe.value != 0 && hasTof) { From ae478811529b86b8bd0b25eb80c4bd6c5ca9670c Mon Sep 17 00:00:00 2001 From: Ionut Cristian Arsene Date: Tue, 10 Nov 2026 01:07:23 +0200 Subject: [PATCH 636/648] =?UTF-8?q?[PWGDQ]=20Refactor=20the=20AnalysisDile?= =?UTF-8?q?ptonTrackTrack=20and=20add=20process=20function=20for=20?= =?UTF-8?q?=E2=80=A6=20(#18673)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ionut Cristian Arsene Co-authored-by: ALICE Action Bot --- PWGDQ/Core/VarManager.h | 24 +- PWGDQ/Tasks/tableReader_withAssoc.h | 347 ++++++++++++++++++++-------- 2 files changed, 270 insertions(+), 101 deletions(-) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index a3b9d9d367b..99c049374e8 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -156,9 +156,11 @@ class VarManager : public TObject enum PairCandidateType { // TODO: need to agree on a scheme to incorporate all various hypotheses (e.g. e - mu, jpsi - K+, Jpsi - pipi,...) - kDecayToEE = 0, // e.g. J/psi -> e+ e- - kDecayToMuMu, // e.g. J/psi -> mu+ mu- - kDecayToPiPi, + kDecayToEE = 0, // e.g. J/psi -> e+ e- + kDecayToMuMu, // e.g. J/psi -> mu+ mu- + kDecayToPiPi, // decay channel to pi+ pi- + kDecayToKchKch, // decay channel to K+ K- + kDecayToPrAPr, // decay channel to p+ p- kElectronMuon, // e.g. Electron - muon correlations kBcToThreeMuons, // e.g. Bc -> mu+ mu- mu+ kBtoJpsiEEK, // e.g. B+ -> e+ e- K+ @@ -166,6 +168,7 @@ class VarManager : public TObject kXtoJpsiPiPi, // e.g. X(3872) -> J/psi pi+ pi- kPsi2StoJpsiPiPi, // e.g. Psi(2S) -> J/psi pi+ pi- kChictoJpsiEE, // e.g. Chi_c1 -> J/psi e+ e- + kNonResonantToKpKmPiPi, // e.g. J/psi -> K+ K- pi+ pi+ kDstarToD0KPiPi, // e.g. D*+ -> D0 pi+ -> K- pi+ pi+ kTripleCandidateToEEPhoton, // e.g. chi_c -> e+ e- photon or pi0 -> e+ e- photon kDecayToKPi, // e.g. D0 -> K+ pi- or cc. @@ -3841,6 +3844,16 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) m2 = o2::constants::physics::MassPionCharged; } + if constexpr (pairType == kDecayToKchKch) { + m1 = o2::constants::physics::MassKaonCharged; + m2 = o2::constants::physics::MassKaonCharged; + } + + if constexpr (pairType == kDecayToPrAPr) { + m1 = o2::constants::physics::MassProton; + m2 = o2::constants::physics::MassProton; + } + if constexpr (pairType == kDecayToKPi) { m1 = o2::constants::physics::MassKaonCharged; m2 = o2::constants::physics::MassPionCharged; @@ -6910,6 +6923,11 @@ void VarManager::FillDileptonTrackTrack(T1 const& dilepton, T2 const& hadron1, T hadronMass1 = o2::constants::physics::MassElectron; hadronMass2 = o2::constants::physics::MassElectron; } + if (candidateType == kNonResonantToKpKmPiPi) { + defaultDileptonMass = dilepton.mass(); + hadronMass1 = o2::constants::physics::MassPionCharged; + hadronMass2 = o2::constants::physics::MassPionCharged; + } ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.mass()); ROOT::Math::PtEtaPhiMVector v2(hadron1.pt(), hadron1.eta(), hadron1.phi(), hadronMass1); diff --git a/PWGDQ/Tasks/tableReader_withAssoc.h b/PWGDQ/Tasks/tableReader_withAssoc.h index 354e9fe815d..1aff98249a4 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.h +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -815,12 +815,13 @@ struct AnalysisTrackSelection { fTrackCuts.push_back(o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } + // Extra cuts via JSON TString addTrackCutsStr = fConfigCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto const& t : addTrackCuts) { - fTrackCuts.push_back(dynamic_cast(t)); + fTrackCuts.push_back(reinterpret_cast(t)); } } @@ -1573,7 +1574,7 @@ struct AnalysisSameEventPairing { void init(o2::framework::InitContext& context) { - fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed"); + fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed") || context.mOptions.get("processBarrelOnlyDiKaons") || context.mOptions.get("processBarrelOnlyDiProtons"); fEnableBarrelMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingBarrelSkimmed") || context.mOptions.get("processMixingBarrelSkimmedFlow") || context.mOptions.get("processMixingBarrelWithQvectorCentrSkimmedNoCov"); fEnableBarrelMixingHistos |= fConfigRunMixingAcrossTFs; fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); @@ -2048,7 +2049,7 @@ struct AnalysisSameEventPairing { auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); size_t nGood = 0; for (auto const& t : groupedAssocs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (t.isBarrelSelected_raw()) { nGood++; } @@ -2126,7 +2127,7 @@ struct AnalysisSameEventPairing { bool isFirst = true; for (auto const& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue @@ -2411,7 +2412,7 @@ struct AnalysisSameEventPairing { isUnambiguous = !(isAmbiInBunch || isAmbiOutOfBunch); isLeg1Ambi = (twoTrackFilter & (static_cast(1) << 28) || (twoTrackFilter & (static_cast(1) << 30))); isLeg2Ambi = (twoTrackFilter & (static_cast(1) << 29) || (twoTrackFilter & (static_cast(1) << 31))); - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (isLeg1Ambi && isLeg2Ambi) { std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); if (fAmbiguousPairs.contains(iPair)) { @@ -2461,7 +2462,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { if (fConfigQA) { fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { @@ -2485,7 +2486,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(histNames[icut][1].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); @@ -2506,7 +2507,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(histNames[icut][2].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); @@ -2537,7 +2538,7 @@ struct AnalysisSameEventPairing { // edit // rotation 20 times if (fConfigTRPairs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue @@ -2568,40 +2569,35 @@ struct AnalysisSameEventPairing { isUnambiguous = !(isAmbiInBunch || isAmbiOutOfBunch); isLeg1Ambi = (twoTrackFilter & (static_cast(1) << 28) || (twoTrackFilter & (static_cast(1) << 30))); isLeg2Ambi = (twoTrackFilter & (static_cast(1) << 29) || (twoTrackFilter & (static_cast(1) << 31))); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (isLeg1Ambi && isLeg2Ambi) { - std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); - if (fAmbiguousPairs.contains(iPair)) { - if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut - isAmbiExtra = true; - } else { - fAmbiguousPairs[iPair] |= static_cast(1) << icut; - } + if (isLeg1Ambi && isLeg2Ambi) { + std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); + if (fAmbiguousPairs.contains(iPair)) { + if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut + isAmbiExtra = true; } else { - fAmbiguousPairs[iPair] = static_cast(1) << icut; + fAmbiguousPairs[iPair] |= static_cast(1) << icut; } + } else { + fAmbiguousPairs[iPair] = static_cast(1) << icut; } } + if (sign1 * sign2 < 0) { if (fConfigNRotations.value == 1) { VarManager::FillPairRotation(t1, t2, fConfigNRotations.value); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - } + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } } else if (fConfigNRotations.value == 3) { for (int irot = 1; irot <= fConfigNRotations.value; irot++) { VarManager::FillPairRotation(t1, t2, irot); - if constexpr (TPairType == VarManager::kDecayToEE) { - if (fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); - } + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } } @@ -2629,7 +2625,7 @@ struct AnalysisSameEventPairing { MixingHandler::MixingEvent mixingEvent; uint32_t trackFilterForMixing = 0; for (auto const& assoc : groupedAssocs) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { trackFilterForMixing = assoc.isBarrelSelected_raw() & assoc.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!trackFilterForMixing) { continue; @@ -2761,7 +2757,7 @@ struct AnalysisSameEventPairing { auto twoTrackFilter = static_cast(0); for (auto const& a1 : assocs1) { for (auto const& a2 : assocs2) { - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue continue; @@ -2876,7 +2872,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { @@ -2895,7 +2891,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { @@ -2913,7 +2909,7 @@ struct AnalysisSameEventPairing { } } } - if constexpr (TPairType == VarManager::kDecayToEE) { + if constexpr (TPairType == VarManager::kDecayToEE || TPairType == VarManager::kDecayToPiPi || TPairType == VarManager::kDecayToKchKch || TPairType == VarManager::kDecayToPrAPr) { fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } @@ -3252,6 +3248,20 @@ struct AnalysisSameEventPairing { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } + void processBarrelOnlyDiKaons(MyEventsVtxCovSelected const& events, + o2::soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + + void processBarrelOnlyDiProtons(MyEventsVtxCovSelected const& events, + o2::soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { @@ -3341,6 +3351,8 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmedNoCov, "Run barrel only pairing (no covariances), with skimmed tracks, with Qvector from central framework", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmed, "Run barrel only pairing (no covariances), with skimmed tracks, with collision information, with Qvector from central framework", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmedFlow, "Run barrel only pairing, with skimmed tracks and with flow", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyDiKaons, "Run barrel only pairing for di-kaons", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyDiProtons, "Run barrel only pairing for di-protons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedMultExtra, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedFlow, "Run muon only pairing, with skimmed tracks and flow", false); @@ -4645,6 +4657,7 @@ struct AnalysisDileptonTrack { o2::framework::Preslice dielectronsPerCollision = o2::aod::reducedpair::reducedeventId; o2::framework::Preslice ditracksPerCollision = o2::aod::reducedpair::reducedeventId; + // Charged B mesons to J/psi+Kch void processBarrelSkimmed(o2::soa::Filtered const& events, o2::soa::Filtered> const& assocs, MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) @@ -4871,8 +4884,7 @@ struct AnalysisDileptonTrack { struct AnalysisDileptonTrackTrack { o2::framework::OutputObj fOutputList{"output"}; - o2::framework::Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks - o2::framework::Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + o2::framework::Configurable fConfigTrackCuts{"cfgTrackCuts", "", "doublets of track cuts in the format cutName1:cutName2,cutName3:cutName4"}; // used for select the tracks from SelectedTracks o2::framework::Configurable fConfigDileptonCut{"cfgDiLeptonCut", "pairJpsi2", "Dilepton cut"}; o2::framework::Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; o2::framework::Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; @@ -4885,14 +4897,17 @@ struct AnalysisDileptonTrackTrack { o2::framework::Produces DileptonTrackTrackTable; int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. - // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons - // cut name setting - TString fTrackCutName1; - TString fTrackCutName2; - bool fIsSameTrackCut = false; + uint32_t fTrackCutMask1 = 0; // track cut bit mask to be used in the selection of tracks + uint32_t fTrackCutMask2 = 0; // track cut bit mask to be used in the selection of tracks + uint32_t fNDoubletCuts = 0; // number of doublet track cuts + std::vector fDoubletCutNames; + std::vector> fDoubletCutMasks; + AnalysisCompositeCut fDileptonCut{}; + bool fApplyDileptonCut = false; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; + uint32_t fNQuadrupletCuts = 0; // number of quadruplet track cuts o2::framework::Service fCCDB{}; @@ -4908,48 +4923,127 @@ struct AnalysisDileptonTrackTrack { void init(o2::framework::InitContext& context) { - // bool isBarrel = context.mOptions.get("processJpsiPiPi"); - if (context.mOptions.get("processDummy")) { return; } + // if the processKKPiPi function is enabled, then the analysis-same-event-pairing must have the processBarrelOnlyDiKaons enabled as well + bool processKKPiPi = context.mOptions.get("processKKPiPi"); + bool processBarrelOnlyDiKaons = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "processBarrelOnlyDiKaons", processBarrelOnlyDiKaons, false); + bool processDummy = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "processDummy", processDummy, false); + if (processKKPiPi) { + if (processDummy) { + LOGF(fatal, "processKKPiPi is enabled, but processDummy should not be enabled in analysis-same-event-pairing!"); + } + if (!processBarrelOnlyDiKaons) { + LOGF(fatal, "processKKPiPi is enabled, but processBarrelOnlyDiKaons is not enabled in analysis-same-event-pairing"); + } + } + // the cfgFlatTables option must be enabled in the analysis-same-event-pairing task because the dileptonsAll table has to be filled + bool flatTables = false; + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgFlatTables", flatTables, false); + if (!flatTables) { + LOGF(fatal, "Flat tables are not enabled in analysis-same-event-pairing! Enable the cfgFlatTables option!"); + } + + // check that there are track cuts defined otherwise throw a FATAL error + TString tempCutsStr = fConfigTrackCuts.value; + if (tempCutsStr.IsNull() || tempCutsStr.IsWhitespace()) { + LOGF(fatal, "No track cuts defined in analysis-track-selection task"); + } + VarManager::SetDefaultVarNames(); + + // get the list of track cuts computed in the AnalysisTrackSelection task + std::string tempCuts; + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + tempCutsStr = tempCuts; + // check also the cuts added via JSON and add them to the string of cuts + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + TString addTrackCutsStr = tempCuts; + if (addTrackCutsStr != "") { + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { + tempCutsStr += Form(",%s", t->GetName()); + } + } + std::unique_ptr trackSelectionsUpstream(tempCutsStr.Tokenize(",")); + + // interpret the list of track cuts and build the filter mask for each of the two prongs + TString trackCutsStr = fConfigTrackCuts.value; + std::unique_ptr doubletTrackSelections(trackCutsStr.Tokenize(",")); + for (Int_t icut = 0; icut < doubletTrackSelections->GetEntries(); ++icut) { + TString cutName = doubletTrackSelections->At(icut)->GetName(); + std::unique_ptr doubletCuts(cutName.Tokenize(":")); + if (doubletCuts->GetEntries() != 2) { + LOGF(fatal, "Doublet cut %s does not have exactly 2 prongs", cutName.Data()); + } + TObject* obj = trackSelectionsUpstream->FindObject(doubletCuts->At(0)->GetName()); + if (!obj) { + LOGF(fatal, "Track cut %s not found in upstream track selections", doubletCuts->At(0)->GetName()); + } + uint32_t mask1 = (static_cast(1) << trackSelectionsUpstream->IndexOf(obj)); + obj = trackSelectionsUpstream->FindObject(doubletCuts->At(1)->GetName()); + if (!obj) { + LOGF(fatal, "Track cut %s not found in upstream track selections", doubletCuts->At(1)->GetName()); + } + uint32_t mask2 = (static_cast(1) << trackSelectionsUpstream->IndexOf(obj)); + + fTrackCutMask1 |= mask1; + fTrackCutMask2 |= mask2; + fDoubletCutMasks.push_back(std::make_pair(mask1, mask2)); + fDoubletCutNames.push_back(cutName); + fNDoubletCuts++; + } + fCurrentRun = 0; fValuesQuadruplet = new float[VarManager::kNVars]; - VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); - // define cuts - fTrackCutName1 = fConfigTrackCut1.value; - fTrackCutName2 = fConfigTrackCut2.value; - if (fTrackCutName1 == fTrackCutName2) { - fIsSameTrackCut = true; - } + // get the dilepton cut TString configDileptonCutNamesStr = fConfigDileptonCut.value; - fDileptonCut = *o2::aod::dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + if (!configDileptonCutNamesStr.IsNull()) { + fApplyDileptonCut = true; + fDileptonCut = *o2::aod::dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + } + TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { TString cutName = objArray->At(icut)->GetName(); fQuadrupletCutNames.push_back(cutName); fQuadrupletCuts.push_back(*o2::aod::dqcuts::GetCompositeCut(cutName.Data())); + fNQuadrupletCuts++; } + // define histogram directories + // One directory for the selected pairs (dileptons) if a cut was requested + // One directory for each combination of track doublet and quadruplet cuts if (!context.mOptions.get("processDummy")) { - DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); - if (!configQuadruletCutNamesStr.IsNull()) { - for (std::size_t icut = 0; icut < fQuadrupletCutNames.size(); ++icut) { - if (fIsSameTrackCut) { - DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - } else { - DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + if (fApplyDileptonCut) { + DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); + } + for (uint32_t idcut = 0; idcut < fNDoubletCuts; ++idcut) { + for (uint32_t icut = 0; icut < fNQuadrupletCuts; ++icut) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + // if the doublet cut is asymmetric, add also the opposite ordering of the doublet cut + if (fDoubletCutMasks[idcut].first != fDoubletCutMasks[idcut].second) { + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); } - DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); - DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s_%s", fDoubletCutNames[idcut].Data(), fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + if (fNQuadrupletCuts == 0) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + if (fDoubletCutMasks[idcut].first != fDoubletCutMasks[idcut].second) { + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fDoubletCutNames[idcut].Data()), fConfigAddQuadrupletHistogram.value.data()); } } } @@ -4983,12 +5077,9 @@ struct AnalysisDileptonTrackTrack { { VarManager::ResetValues(0, VarManager::kNVars, fValuesQuadruplet); VarManager::FillEvent(event, fValuesQuadruplet); - // int indexOffset = -999; - // std::vector trackGlobalIndexes; for (auto const& dilepton : dileptons) { // get full track info of tracks based on the index - int indexLepton1 = dilepton.index0Id(); int indexLepton2 = dilepton.index1Id(); auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); @@ -4998,25 +5089,24 @@ struct AnalysisDileptonTrackTrack { continue; } VarManager::FillTrack(dilepton, fValuesQuadruplet); - // LOGP(info, "is dilepton selected: {}", fDileptonCut.IsSelected(fValuesQuadruplet)); // apply the dilepton cut - if (!fDileptonCut.IsSelected(fValuesQuadruplet)) { + if (fApplyDileptonCut && !fDileptonCut.IsSelected(fValuesQuadruplet)) { continue; } - fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + if (fApplyDileptonCut) { + fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + } - // loop over hadrons pairs + // loop over track pairs for (auto const& [a1, a2] : o2::soa::combinations(assocs, assocs)) { - uint32_t trackSelection = 0; - if (fIsSameTrackCut) { - trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) || (a2.isBarrelSelected_raw() & (static_cast(1) << 1))); - } else { - trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) && (a2.isBarrelSelected_raw() & (static_cast(1) << 2))); - } - // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); - if (trackSelection == 0) { + uint32_t trackSelection1 = 0; + uint32_t trackSelection2 = 0; + trackSelection1 = (a1.isBarrelSelected_raw() & fTrackCutMask1) | (a2.isBarrelSelected_raw() & fTrackCutMask1); + trackSelection2 = (a2.isBarrelSelected_raw() & fTrackCutMask2) | (a1.isBarrelSelected_raw() & fTrackCutMask2); + // skip if no tracks were selected + if (trackSelection1 == 0 || trackSelection2 == 0) { continue; } @@ -5031,39 +5121,81 @@ struct AnalysisDileptonTrackTrack { // fill variables VarManager::FillDileptonTrackTrack(dilepton, track1, track2, fValuesQuadruplet); if (fConfigSetupFourProngFitter || fConfigUseKFVertexing) { - // LOGP(info, "Using KF or DCA fitter for secondary vertexing"); VarManager::FillDileptonTrackTrackVertexing(event, lepton1, lepton2, track1, track2, fValuesQuadruplet); } - int iCut = 0; + // first loop over the doublet track cuts and make a decision map + int idCut = 0; + uint32_t idDoubletCutDecision = 0; + for (auto cutmask = fDoubletCutMasks.begin(); cutmask != fDoubletCutMasks.end(); cutmask++, idCut++) { + // check the doublet cuts + if ((trackSelection1 & cutmask->first) && (trackSelection2 & cutmask->second)) { + idDoubletCutDecision |= (static_cast(1) << idCut); + } + // check the reverse double cuts + if ((trackSelection2 & cutmask->first) && (trackSelection1 & cutmask->second)) { + idDoubletCutDecision |= (static_cast(1) << idCut); + } + } + uint32_t CutDecision = 0; - for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { + for (uint32_t iCut = 0; iCut < fNQuadrupletCuts; iCut++) { // apply dilepton-track-track cut if (fQuadrupletCuts[iCut].IsSelected(fValuesQuadruplet)) { - CutDecision |= (1 << iCut); - if (fIsSameTrackCut) { - if (track1.sign() * track2.sign() < 0) { - fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + CutDecision |= (static_cast(1) << iCut); + } + } + if (fNQuadrupletCuts > 0 && !CutDecision) { + continue; + } + + // now make a double loop over doublet and quadruplet cuts and fill histograms + for (uint32_t iDoubletCut = 0; iDoubletCut < fNDoubletCuts; iDoubletCut++) { + if (!(idDoubletCutDecision & (static_cast(1) << iDoubletCut))) { + continue; + } + // check if the prong1 and prong2 cuts are the same + bool isSameTrackCut = fDoubletCutMasks[iDoubletCut].first == fDoubletCutMasks[iDoubletCut].second; + for (uint32_t iQuadrupletCut = 0; iQuadrupletCut < fNQuadrupletCuts; iQuadrupletCut++) { + if (CutDecision & (static_cast(1) << iQuadrupletCut)) { + if (isSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } } - } else { - if ((track1.sign() < 0) && (track2.sign() > 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); - } else if ((track1.sign() > 0) && (track2.sign() < 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s_%s", fDoubletCutNames[iDoubletCut].Data(), fQuadrupletCutNames[iQuadrupletCut].Data()), fValuesQuadruplet); } } - if ((track1.sign() > 0) && (track2.sign() > 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); - } else if ((track1.sign() < 0) && (track2.sign() < 0)) { - fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } // end of loop over quadruplet cuts + // in case there are no quadruplet cuts specified by the user + if (isSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); } } - } // loop over dilepton-track-track cuts + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fDoubletCutNames[iDoubletCut].Data()), fValuesQuadruplet); + } + } // end of loop over doublet cuts // fill table - if (!CutDecision) { - continue; - } DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), @@ -5099,12 +5231,31 @@ struct AnalysisDileptonTrackTrack { } } + void processKKPiPi(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) + { + if (events.size() == 0) { + return; + } + if (fCurrentRun != events.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(events.begin().timestamp()); + fCurrentRun = events.begin().runNumber(); + } // end: runNumber + for (auto const& event : events) { + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, groupedBarrelAssocs, tracks, groupedDielectrons); + } + } + void processDummy(MyEventsBasic const&) { // do nothing } PROCESS_SWITCH(AnalysisDileptonTrackTrack, processJpsiPiPi, "Run barrel pairing of J/psi with pion candidate", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processKKPiPi, "Run barrel pairing of non-resonant K+ K- pi+ pi+ candidate", false); PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); }; From 86166b43b6139c6550b6b18cdd3cd313dd6b14c9 Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Tue, 10 Nov 2026 09:34:00 +0300 Subject: [PATCH 637/648] [PWGUD] Remove unused lumi histograms (#18669) --- PWGUD/TableProducer/upcCandProducerBarrel.cxx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx index dc023703a9c..135fdd887ac 100644 --- a/PWGUD/TableProducer/upcCandProducerBarrel.cxx +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -525,11 +525,9 @@ struct UpcCandProducerBarrel { auto hTVX = registry.get(HIST("hTVX")); auto hTVXRCT = registry.get(HIST("hTVXRCT")); - auto hTVXRCTnoPileupROF = registry.get(HIST("hTVXRCTnoPileupROF")); auto hTVXupc = registry.get(HIST("hTVXupc")); auto hTVXRCTupc = registry.get(HIST("hTVXRCTupc")); - auto hTVXRCTnoPileupROFupc = registry.get(HIST("hTVXRCTnoPileupROFupc")); std::vector bcsWithFT0, bcsWithFV0, bcsWithFDD; bcsWithFT0.reserve(ft0s.size()); @@ -547,7 +545,6 @@ struct UpcCandProducerBarrel { const auto iTF = localTFIndex[bc.globalIndex()]; const auto tfID = tfIDs[iTF]; const int32_t rofInTF = getRofInTF(bcInTF); - bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] >= 1; // TODO investigate other options bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; // Multiplicity for FT0C-based occupancy estimates @@ -576,15 +573,6 @@ struct UpcCandProducerBarrel { if (upcMode) { hTVXRCTupc->Fill(bcInOrbit, tfID); } - - if (pileupInROF) { - continue; - } - - hTVXRCTnoPileupROF->Fill(bcInOrbit, tfID); - if (upcMode) { - hTVXRCTnoPileupROFupc->Fill(bcInOrbit, tfID); - } } LOGP(debug, "Filling FT0 activity arrays"); From 33795e363fb059033709d364130b576f75a6c1db Mon Sep 17 00:00:00 2001 From: nkaratze Date: Tue, 10 Nov 2026 07:31:02 +0000 Subject: [PATCH 638/648] [PWGLF] Fixed derived processes and Signal Splitting Feedown (#18674) --- PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx | 249 +++++++++++++++---- 1 file changed, 197 insertions(+), 52 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx index fb66f7e07e8..de414884670 100644 --- a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx +++ b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx @@ -68,6 +68,9 @@ static std::vector antilambdaPtBins; std::vector> kaonSplit; std::vector> lambdaSplit; std::vector> antilambdaSplit; +std::vector> kaonSplitFeeddown; +std::vector> lambdaSplitFeeddown; +std::vector> antilambdaSplitFeeddown; } // namespace pthistos using namespace o2; using namespace o2::framework; @@ -224,12 +227,15 @@ struct V0PtInvMassPlots { int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; - pthistos::kaonPt.resize(nKaonHistograms); // number of Kaon Pt histograms to expect - pthistos::lambdaPt.resize(nLambdaHistograms); // number of Lambda histograms to expect - pthistos::antilambdaPt.resize(nAntilambdaHistograms); // number of AntiLambda histograms to expect - pthistos::kaonSplit.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect - pthistos::lambdaSplit.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect - pthistos::antilambdaSplit.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect + pthistos::kaonPt.resize(nKaonHistograms); // number of Kaon Pt histograms to expect + pthistos::lambdaPt.resize(nLambdaHistograms); // number of Lambda histograms to expect + pthistos::antilambdaPt.resize(nAntilambdaHistograms); // number of AntiLambda histograms to expect + pthistos::kaonSplit.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect + pthistos::lambdaSplit.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect + pthistos::antilambdaSplit.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect + pthistos::kaonSplitFeeddown.resize(nKaonHistograms); // number of Kaon Split Pt histograms to expect + pthistos::lambdaSplitFeeddown.resize(nLambdaHistograms); // number of Lambda Split Pt histograms to expect + pthistos::antilambdaSplitFeeddown.resize(nAntilambdaHistograms); // number of AntiLambda Split Pt histograms to expect // initialize and convert tokenized strings into vector of doubles for AxisSpec std::vector kaonptedgevalues(pthistos::kaonPtBins.size()); @@ -310,6 +316,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nKaonHistograms; i++) { pthistos::kaonPt[i] = rK0shMassPlotsPerPtBin.add(fmt::format("hK0shPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); pthistos::kaonSplit[i] = rK0shSplitMassPlotsPerPtBin.add(fmt::format("hK0shSplitPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("Split K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); + pthistos::kaonSplitFeeddown[i] = rK0shSplitMassPlotsPerPtBin.add(fmt::format("hK0shSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), fmt::format("Feeddown Split K0s mass vs centrality, pT from {0} to {1}", kaonhistvalue[i], kaonhistvalue[i + 1]).c_str(), {HistType::kTH2D, {{k0ShortMassAxis}, {centAxis}}}); } rFeeddownMatrices.add("hK0shFeeddownMatrix", "hK0shFeeddownMatrix", {HistType::kTH3D, {k0ShortPtAxis, k0ShortPtAxis, centAxis}}); rFeeddownMatrices.add("hK0shPhiFeeddownMatrix", "hK0shPhiFeeddownMatrix", {HistType::kTH3D, {k0ShortPtAxis, k0ShortPtAxis, centAxis}}); @@ -317,7 +324,7 @@ struct V0PtInvMassPlots { // Adding Lambda Histograms if (lambdaAnalysis == true) { // same method as in K0sh above - rPtAnalysis.add("hMassLambdavsCuts", "hMassLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {k0ShortMassAxis}}}); + rPtAnalysis.add("hMassLambdavsCuts", "hMassLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {lambdaMassAxis}}}); rPtAnalysis.add("hArmenterosPodolanskiPlotLambda", "hArmenterosPodolanskiPlotLambda", {HistType::kTH2F, {{armenterosasymAxis}, {armenterosQtAxis}}}); rPtAnalysis.add("hNSigmaPosProtonFromLambdas", "hNSigmaPosProtonFromLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {lambdaPtAxis}}}); rPtAnalysis.add("hNSigmaNegPionFromLambdas", "hNSigmaNegPionFromLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {lambdaPtAxis}}}); @@ -330,6 +337,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nLambdaHistograms; i++) { pthistos::lambdaPt[i] = rLambdaMassPlotsPerPtBin.add(fmt::format("hLambdaPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); pthistos::lambdaSplit[i] = rLambdaSplitMassPlotsPerPtBin.add(fmt::format("hLambdaSplitPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Split Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); + pthistos::lambdaSplitFeeddown[i] = rLambdaSplitMassPlotsPerPtBin.add(fmt::format("hLambdaSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), fmt::format("Feeddown Split Lambda mass vs centrality, pT from {0} to {1}", lambdahistvalue[i], lambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{lambdaMassAxis}, {centAxis}}}); } // lambdafeeddown matrices rFeeddownMatrices.add("hLambdaFeeddownMatrix", "hLambdaFeeddownMatrix", {HistType::kTH3D, {lambdaPtAxis, lambdaPtAxis, centAxis}}); @@ -340,7 +348,7 @@ struct V0PtInvMassPlots { // Adding AntiLambda Histograms if (antiLambdaAnalysis == true) { // same method as in Lambda and K0sh above - rPtAnalysis.add("hMassAntiLambdavsCuts", "hMassAntiLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {k0ShortMassAxis}}}); + rPtAnalysis.add("hMassAntiLambdavsCuts", "hMassAntiLambdavsCuts", {HistType::kTH2F, {{partCutsAxis}, {antiLambdaMassAxis}}}); rPtAnalysis.add("hArmenterosPodolanskiPlotAntiLambda", "hArmenterosPodolanskiPlotAntiLambda", {HistType::kTH2F, {{armenterosasymAxis}, {armenterosQtAxis}}}); rPtAnalysis.add("hNSigmaPosPionFromAntiLambdas", "hNSigmaPosPionFromAntiLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {antilambdaPtAxis}}}); rPtAnalysis.add("hNSigmaNegProtonFromAntiLambdas", "hNSigmaNegProtonFromAntiLambdas", {HistType::kTH2F, {{100, -5.f, 5.f}, {antilambdaPtAxis}}}); @@ -352,6 +360,7 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms; i++) { pthistos::antilambdaPt[i] = rAntilambdaMassPlotsPerPtBin.add(fmt::format("hAntiLambdaPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); pthistos::antilambdaSplit[i] = rAntilambdaSplitMassPlotsPerPtBin.add(fmt::format("hAntiLambdaSplitPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("Split AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); + pthistos::antilambdaSplitFeeddown[i] = rAntilambdaSplitMassPlotsPerPtBin.add(fmt::format("hAntiLambdaSplitFeeddownPt_vs_Cent_from_{0}_to_{1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), fmt::format("Feeddown Split AntiLambda mass vs centrality, pT from {0} to {1}", antilambdahistvalue[i], antilambdahistvalue[i + 1]).c_str(), {HistType::kTH2D, {{antiLambdaMassAxis}, {centAxis}}}); } // antilambdafeeddown matrices rFeeddownMatrices.add("hAntiLambdaFeeddownMatrix", "hAntiLambdaFeeddownMatrix", {HistType::kTH3D, {antilambdaPtAxis, antilambdaPtAxis, centAxis}}); @@ -1012,7 +1021,7 @@ struct V0PtInvMassPlots { pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } - // K0sh Signla Split Numerator End + // K0sh Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched @@ -1024,6 +1033,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonSplitFeeddown[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1058,6 +1074,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaSplitFeeddown[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1080,7 +1103,7 @@ struct V0PtInvMassPlots { // antilambda analysis if (antiLambdaAnalysis == true) { if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // AntiLambda Selections - // AntiLambda Signal Split Numerator End + // AntiLambda Signal Split Numerator Start for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); @@ -1098,6 +1121,13 @@ struct V0PtInvMassPlots { } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaSplitFeeddown[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mothers = v0mcParticle.mothers_as(); // Get mothers if (!v0mothers.empty()) { auto& v0mcParticleMother = v0mothers.front(); // First mother @@ -1271,7 +1301,8 @@ struct V0PtInvMassPlots { } void recMCProcessDerived(soa::Join::iterator const& collision, soa::Join const& /*mcCollisions*/, - soa::Join const& V0s, + soa::Join const& V0s, + aod::V0MCCores const&, DaughterTracksDerived const&) { // tokenise strings into individual values @@ -1312,16 +1343,16 @@ struct V0PtInvMassPlots { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track const auto& negDaughterTrack = v0.template negTrackExtra_as(); // Negative Daughter track - if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selections + if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // V0 Selections continue; } // K0sh Analysis if (kzeroAnalysis == true) { - if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // K0sh Selection // K0sh Signal Split Numerator Start for (int i = 0; i < nKaonHistograms; i++) { - if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator - pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator + pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } // K0sh SignaL Split Numerator End @@ -1330,12 +1361,19 @@ struct V0PtInvMassPlots { if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { - if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges - pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonSplitFeeddown[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers rFeeddownMatrices.fill(HIST("hK0shPhiFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kPhi) { // Phi Mother Matched @@ -1348,10 +1386,10 @@ struct V0PtInvMassPlots { } // Lambda analysis if (lambdaAnalysis == true) { - if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selections + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // Lambda Selections // Lambda Signal Split Numerator Start for (int i = 0; i < nLambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { pthistos::lambdaSplit[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } @@ -1361,12 +1399,19 @@ struct V0PtInvMassPlots { if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaSplitFeeddown[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiMinus) { // Xi Minus Mother Matched @@ -1385,10 +1430,10 @@ struct V0PtInvMassPlots { } // AntiLambda analysis if (antiLambdaAnalysis == true) { - if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selections - // AntiLambda Signal Split Numerator End + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, mcCollision)) { // AntiLambda Selections + // AntiLambda Signal Split Numerator Start for (int i = 0; i < nAntilambdaHistograms; i++) { - if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } @@ -1398,12 +1443,19 @@ struct V0PtInvMassPlots { if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { - if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { + // Split Feeddown Start + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaSplitFeeddown[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + // Split Feeddown End auto v0mother = v0.motherMCPart(); // Get mothers rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiPlusBar) { // Xi Plus Mother Matched @@ -1424,12 +1476,10 @@ struct V0PtInvMassPlots { } // This ii the process for the MC generated derived data void genMCProcessDerived( - soa::Join::iterator const& mcCollision, - // soa::SmallGroups> const& collision, - soa::SmallGroups> const& collisions, - aod::V0MCCores const& V0s, - aod::CascMCCores const& cascs, - DaughterTracksDerived const&) + soa::Join::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + soa::SmallGroups> const& V0s, + soa::SmallGroups> const& cascs) { // Event Efficiency, Event Split and V0 Signal Loss Corrections rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events @@ -1506,38 +1556,46 @@ struct V0PtInvMassPlots { } } // End of MCParticle Loop For Signal Loss Denominator - // Signal Loss Numenator Loop + bool hasSelectedInelGt0Collision = false; for (const auto& collision : collisions) { rMCCorrections.fill(HIST("hNEvents_Corrections"), 2.5, mcCollision.centFT0M()); // Number of Events Reconsctructed if (!acceptEvent(collision)) { // Event Selection continue; } - rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Event Split Denomimator and Event Efficiency Numenator - for (const auto& v0 : V0s) { - if (!v0.isPhysicalPrimary()) { - continue; - } - if (v0.pdgCode() == kK0Short) // kzero matched - { - if (std::abs(v0.rapidityMC(0)) < rapidityCut) { - rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); - } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Selected reconstructed collisions + if (collision.multNTracksPVeta1() > 0) { + hasSelectedInelGt0Collision = true; + } + } + + if (!hasSelectedInelGt0Collision) { + return; + } + + // Fill the signal-loss numerator once per MC collision. + for (const auto& v0 : V0s) { + if (!v0.isPhysicalPrimary()) { + continue; + } + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); } - if (v0.pdgCode() == kLambda0) // lambda matched - { - if (std::abs(v0.rapidityMC(1)) < rapidityCut) { - rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); - } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); } - if (v0.pdgCode() == kLambda0Bar) // antilambda matched - { - if (std::abs(v0.rapidityMC(2)) < rapidityCut) { - rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); - } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); } } } - // End of Signal Loss Numenator Loop } // This is the process for Real Data MC Closure Test void dataProcessClosureTest(soa::Join::iterator const& collision, @@ -1618,6 +1676,92 @@ struct V0PtInvMassPlots { } } } + // This is the process for Real Derived Data MC Closure Test + void dataProcessDerivedClosureTest( + soa::Join::iterator const& collision, + soa::Join const& /*mcCollisions*/, + soa::Join const& V0s, + DaughterTracksDerived const&) + { + // Tokenise strings into individual values + pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); + pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); + pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); + + // Calculate number of histograms for each particle type + int nKaonHistograms = pthistos::kaonPtBins.size() - 1; + int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; + int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; + + // Initialize and convert tokenized strings into vector of doubles for Pt Bin Edges + std::vector kaonptedgevalues(nKaonHistograms + 1); + std::vector lambdaptedgevalues(nLambdaHistograms + 1); + std::vector antilambdaptedgevalues(nAntilambdaHistograms + 1); + + for (int i = 0; i < nKaonHistograms + 1; i++) { + kaonptedgevalues[i] = std::stod(pthistos::kaonPtBins[i]); + } + for (int i = 0; i < nLambdaHistograms + 1; i++) { + lambdaptedgevalues[i] = std::stod(pthistos::lambdaPtBins[i]); + } + for (int i = 0; i < nAntilambdaHistograms + 1; i++) { + antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); + } + + // For MC centrality estimation + const auto& mcCollision = collision.straMCCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut + + for (const auto& v0 : V0s) { + // Checking the V0 candidates and filling the parameter histograms and invariant mass plots + const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track + const auto& negDaughterTrack = v0.template negTrackExtra_as(); // Negative Daughter track + + if (!acceptV0Derived(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selection + continue; + } + + // K0sh analysis + if (kzeroAnalysis == true) { + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); + } + } + } + } + + // Lambda analysis + if (lambdaAnalysis == true) { + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selection + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + } + } + + // Anti-Lambda analysis + if (antiLambdaAnalysis == true) { + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + } + } + } + } PROCESS_SWITCH(V0PtInvMassPlots, genMCProcess, "Process Run 3 MC Generated", false); PROCESS_SWITCH(V0PtInvMassPlots, recMCProcess, "Process Run 3 MC Reconstructed", false); PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data", false); @@ -1625,6 +1769,7 @@ struct V0PtInvMassPlots { PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed Derived", false); PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data Derived", false); PROCESS_SWITCH(V0PtInvMassPlots, dataProcessClosureTest, "Process Run 3 Data Closure Test", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerivedClosureTest, "Process Run 3 Data Derived Closure Test", false) }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 0a4195eddf53776dbd10e779f16a03f1d243bb05 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Tue, 10 Nov 2026 11:58:51 +0100 Subject: [PATCH 639/648] [PWGCF] add triton QA in HadNucleiFemto.cxx (#18665) --- .../TableProducer/HadNucleiFemto.cxx | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index a6e71890aed..c375e7ed193 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -96,13 +96,13 @@ using CollisionsFullWithPVMult = CollisionsFull; using CollisionsFullMC = soa::Join; using HadHyperCollisionsFull = soa::Join; using HadHyperCollisionsFullMC = soa::Join; -using TrackCandidates = soa::Join; -using TrackCandidatesMC = soa::Join; +using TrackCandidates = soa::Join; +using TrackCandidatesMC = soa::Join; // The DCA-template process explicitly subscribes to the propagated covariance // table. This makes the propagation service enter the branch where TrackTuner // corrections can be applied when enabled, without adding that dependency to // unrelated MC process switches. -using TrackCandidatesMCDca = soa::Join; +using TrackCandidatesMCDca = soa::Join; namespace { @@ -435,6 +435,8 @@ struct HadNucleiFemto { std::string prefix{"tritonPid"}; // Triton track-quality, purity and PID cuts Configurable settingITSNClsMinTr{"settingITSNClsMinTr", 5, "Minimum number of ITS clusters for triton candidates"}; + Configurable settingTPCChi2NClMaxTr{"settingTPCChi2NClMaxTr", 4.f, "Maximum TPC chi2 per cluster for triton candidates"}; + Configurable settingITSChi2NClMaxTr{"settingITSChi2NClMaxTr", 36.f, "Maximum ITS chi2 per cluster for triton candidates"}; Configurable settingPIDMomentumSplitTr{"settingPIDMomentumSplitTr", 2.f, "Momentum separating low- and high-momentum triton TPC PID"}; Configurable settingCutTPCNsigmaLowPTr{"settingCutTPCNsigmaLowPTr", 3.f, "Maximum absolute TPC n-sigma for tritons below the momentum split"}; Configurable settingCutITSNsigmaLowPTr{"settingCutITSNsigmaLowPTr", 3.f, "Maximum absolute ITS n-sigma for tritons below the momentum split"}; @@ -442,6 +444,7 @@ struct HadNucleiFemto { Configurable settingCutTPCNsigmaHighPMaxTr{"settingCutTPCNsigmaHighPMaxTr", 3.f, "Maximum TPC n-sigma for tritons above the momentum split"}; Configurable settingTOFMassMomentumMinTr{"settingTOFMassMomentumMinTr", 1.2f, "Minimum momentum to apply the triton TOF mass cut"}; Configurable settingTOFMassMinTr{"settingTOFMassMinTr", 2.5f, "Minimum triton TOF mass"}; + Configurable settingUseTOFMassTableTr{"settingUseTOFMassTableTr", false, "Use pidTOFmass mass for triton PID and purity instead of computeTOFMass"}; Configurable settingTOFMassMaxTr{"settingTOFMassMaxTr", 3.4f, "Maximum triton TOF mass"}; Configurable settingTPCRejectNsig{"settingTPCRejectNsig", 3.f, "Minimum absolute TPC n-sigma from deuteron, proton and pion hypotheses"}; } tritonPid; @@ -836,6 +839,8 @@ struct HadNucleiFemto { {"h2NsigmaHadKaTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(K)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, // Purity + {"h2MassTOFTrComputedVsTable", "Triton TOF mass comparison before mass cut;computed mass (GeV/#it{c}^{2});table mass (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, 0.f, 7.f}, {280, 0.f, 7.f}}}}, + {"purity/h2MassTOFTrComputedVsTable", "Triton TOF mass comparison before mass cut;computed mass (GeV/#it{c}^{2});table mass (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, 0.f, 7.f}, {280, 0.f, 7.f}}}}, {"purity/h2MassTOFTr_preselection", "Triton TOF mass before mass cut; signed #it{p}_{T} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {280, 0.0f, 7.0f}}}}, {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, @@ -1261,18 +1266,16 @@ struct HadNucleiFemto { constexpr float maxAbsDcaXY = 0.2f; constexpr float maxAbsDcaZ = 0.2f; constexpr int minTPCCrossedRows = 70; - constexpr float maxTPCChi2NCl = 5.f; constexpr float maxTPCFractionSharedCls = 0.3f; - constexpr float maxITSChi2NCl = 10.f; return !(std::abs(candidate.eta()) >= maxAbsEta || std::abs(candidate.dcaXY()) >= maxAbsDcaXY || std::abs(candidate.dcaZ()) >= maxAbsDcaZ || candidate.tpcNClsCrossedRows() < minTPCCrossedRows || - candidate.tpcChi2NCl() >= maxTPCChi2NCl || + candidate.tpcChi2NCl() >= tritonPid.settingTPCChi2NClMaxTr || candidate.tpcFractionSharedCls() >= maxTPCFractionSharedCls || candidate.itsNCls() < tritonPid.settingITSNClsMinTr || - candidate.itsChi2NCl() >= maxITSChi2NCl); + candidate.itsChi2NCl() >= tritonPid.settingITSChi2NClMaxTr); } bool useDeuteronNucleus() const @@ -1925,6 +1928,26 @@ struct HadNucleiFemto { return std::abs(candidate.tpcInnerParam()) * std::sqrt(1.f / (beta * beta) - 1.f); } + template + float tritonTOFMass(const Ttrack& candidate) const + { + return tritonPid.settingUseTOFMassTableTr.value ? candidate.mass() : computeTOFMass(candidate); + } + + template + void fillTritonTOFMassComparison(const Ttrack& candidate, bool isPurity) + { + const float computedMass = computeTOFMass(candidate); + const float tableMass = candidate.mass(); + if (std::isfinite(computedMass) && std::isfinite(tableMass)) { + if (isPurity) { + mQaRegistry.fill(HIST("purity/h2MassTOFTrComputedVsTable"), computedMass, tableMass); + } else { + mQaRegistry.fill(HIST("h2MassTOFTrComputedVsTable"), computedMass, tableMass); + } + } + } + template bool selectionPIDTr(const Ttrack& candidate) { @@ -1957,9 +1980,10 @@ struct HadNucleiFemto { if (!candidate.hasTOF()) { return false; } - const float massTOFTr = computeTOFMass(candidate); + fillTritonTOFMassComparison(candidate, false); + const float massTOFTr = tritonTOFMass(candidate); mQaRegistry.fill(HIST("h2MassTOFTr"), candidate.sign() * absP, massTOFTr); - if (massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { + if (!std::isfinite(massTOFTr) || massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { return false; } } @@ -4109,7 +4133,8 @@ struct HadNucleiFemto { mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtTr, tpcNSigmaTr); // Apply the triton TPC PID cut but keep TOF mass uncut to retain its sidebands. if (passTPCTr && absPTr > tritonPid.settingTOFMassMomentumMinTr && track.hasTOF()) { - const float massTOFTr = computeTOFMass(track); + fillTritonTOFMassComparison(track, true); + const float massTOFTr = tritonTOFMass(track); if (std::isfinite(massTOFTr)) { mQaRegistry.fill(HIST("purity/h2MassTOFTr_preselection"), signedPtTr, massTOFTr); } From 6443e946eac2b07900edc9f30f20a6afecab7689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 10 Nov 2026 13:53:45 +0100 Subject: [PATCH 640/648] [PWGHF] Fix unused variables, parameters, values (#18643) --- PWGHF/HFC/Macros/FitCorrel.C | 4 ++-- PWGHF/TableProducer/mcPidTof.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGHF/HFC/Macros/FitCorrel.C b/PWGHF/HFC/Macros/FitCorrel.C index e5b88cb5bd2..328227900ee 100644 --- a/PWGHF/HFC/Macros/FitCorrel.C +++ b/PWGHF/HFC/Macros/FitCorrel.C @@ -259,13 +259,13 @@ void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") corrFitter[iBinPtHad][iBinPtCand]->setFuncType(static_cast(fitFunc[iBinPtCand])); corrFitter[iBinPtHad][iBinPtCand]->fitting(kTRUE, kTRUE); // the first term is for drawing the fit functions, the second argument is useExternalParams - TF1* fFit = corrFitter[iBinPtHad][iBinPtCand]->getFitFunction(); + // TF1* fFit = corrFitter[iBinPtHad][iBinPtCand]->getFitFunction(); // FIXME: unused // Title of the histogram auto* pttext = new TPaveText(0.15, 0.9, 0.85, 0.95, "NDC"); pttext->SetFillStyle(0); pttext->SetBorderSize(0); - TText* tpT = pttext->AddText(0., 0.8, Form("%.0f < p_{T}^{D_{s}} < %.0f GeV/c, p_{T}^{assoc} > %.1f GeV/c", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad])); + pttext->AddText(0., 0.8, Form("%.0f < p_{T}^{D_{s}} < %.0f GeV/c, p_{T}^{assoc} > %.1f GeV/c", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad])); // pttext -> Draw("same"); // Fill the histograms with the fit parameters diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 78dda49a10c..0462d3a35f6 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -604,7 +604,7 @@ struct TofEventTime { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (RemoveTofEvTimeBias) { evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); From 0927591a01070a66cb4603822a4979d441cd655e Mon Sep 17 00:00:00 2001 From: Oleksii Lubynets Date: Tue, 10 Nov 2026 14:00:13 +0100 Subject: [PATCH 641/648] [Common,DPG] TPC PID workflows: provide reproducibility of randomization (#18634) --- Common/Tools/PID/pidTPCModule.h | 6 ++- DPG/Tasks/TPC/tpcSkimsTableCreator.cxx | 44 ++++++++++++------- .../TPC/treeCreatorPidTpcDiagnostics.cxx | 10 +++++ 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 802d17a9b06..44234ed5dfa 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -723,7 +723,11 @@ class pidTPCModule } auto trackiterator = tracks.begin(); if constexpr (requires { trackiterator.mcParticleId(); }) { - gRandom->SetSeed(0); // Ensure unique seed from UUID for each process call + if (bcs.size() != 0) { + const auto timestamp = bcs.begin().timestamp(); + LOG(info) << "gRandom is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + gRandom->SetSeed(timestamp); + } } // preparatory step: we need the multiplicities for each collision diff --git a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx index 3aaa024874e..ec4c98f2d6d 100644 --- a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx +++ b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx @@ -166,7 +166,7 @@ struct TreeWriterTpcV0 { o2::aod::rctsel::RCTFlagsChecker rctChecker{}; - TRandom3* fRndm = new TRandom3(0); + TRandom3* fRndm = new TRandom3(); using Trks = soa::Join; using TrksWithDEdxCorrection = soa::Join; @@ -420,6 +420,12 @@ struct TreeWriterTpcV0 { template void runV0(Colls const& collisions, TrksType const& myTracks, V0sWithID const& myV0s, CascsWithID const& myCascs, TrkQAType const& tracksQA) { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "fRndm is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + fRndm->SetSeed(timestamp); + } + constexpr bool IsWithTrackQa = ModeId != ModeStandard; std::vector labelTrack2TrackQA; @@ -692,20 +698,20 @@ struct TreeWriterTpcTof { HistogramRegistry registry{"registry", {}}; struct TofTrack { - bool isApplyHardCutOnly; - double maxMomHardCutOnly; - double maxMomTPCOnly; - double tpcNSigma; - double nSigmaTPCOnly; - double downsamplingTsalis; - double mass; - double tofNSigma; - double itsNSigma; - double tpcExpSignal; - o2::track::PID::ID pid; - double dwnSmplFactor; - double nSigmaTofTpctof; - double nSigmaTpcTpctof; + bool isApplyHardCutOnly{false}; + double maxMomHardCutOnly{UndefValueDouble}; + double maxMomTPCOnly{UndefValueDouble}; + double tpcNSigma{UndefValueDouble}; + double nSigmaTPCOnly{UndefValueDouble}; + double downsamplingTsalis{UndefValueDouble}; + double mass{UndefValueDouble}; + double tofNSigma{UndefValueDouble}; + double itsNSigma{UndefValueDouble}; + double tpcExpSignal{UndefValueDouble}; + o2::track::PID::ID pid{0}; + double dwnSmplFactor{UndefValueDouble}; + double nSigmaTofTpctof{UndefValueDouble}; + double nSigmaTpcTpctof{UndefValueDouble}; }; Service ccdb{}; @@ -714,7 +720,7 @@ struct TreeWriterTpcTof { o2::aod::rctsel::RCTFlagsChecker rctChecker{}; - TRandom3* fRndm = new TRandom3(0); + TRandom3* fRndm = new TRandom3(); using Trks = soa::Join void runTof(Colls const& collisions, TrksType const& myTracks, TrkQAType const& tracksQA, Preslice const& perCollisionTracksType, TMOType const& trackMeanOccs) { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "fRndm is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + fRndm->SetSeed(timestamp); + } + constexpr bool IsWithTrackQa = ModeId != ModeStandard; std::vector labelTrack2TrackQA; diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx index 8822995d239..8b20cb6889a 100644 --- a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx @@ -293,6 +293,16 @@ struct TreeCreatorPidTpcDiagnostics { } } + void process(const aod::Collisions& collisions, + const aod::BCsWithTimestamps&) + { + if (collisions.size() != 0) { + const auto timestamp = collisions.begin().bc_as().timestamp(); + LOG(info) << "mRnd is seeded with timestamp of the 0-th bunch crossing (" << timestamp << ")"; + mRnd.SetSeed(timestamp); + } + } + #define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong, Unused) \ void process##ParticleNameLong(CollisionsExtra const& collisions, \ soa::Join const& tracks, \ From 47e1cf93701f7b0e830c000efd80a148913cc302 Mon Sep 17 00:00:00 2001 From: Mingze Li Date: Tue, 10 Nov 2026 14:31:15 +0100 Subject: [PATCH 642/648] [PWGHF] Dstar spin alignment: Add rotation and extend the MC derived data (#18591) Co-authored-by: ALICE Action Bot --- .../treeCreatorDstarSpinAlignMixing.cxx | 218 +++++++++++++++++- PWGHF/D2H/Tasks/taskCharmPolarisation.cxx | 19 +- 2 files changed, 232 insertions(+), 5 deletions(-) diff --git a/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx index e89d601aa3b..325b21a502e 100644 --- a/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx +++ b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx @@ -17,6 +17,7 @@ /// \author Mingze li , CCNU/UniTo #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -24,8 +25,10 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" +#include #include #include #include @@ -35,6 +38,16 @@ #include #include +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include #include #include @@ -44,6 +57,7 @@ using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_occupancy; using namespace o2::analysis::hf_flow_utils; +using namespace o2::constants::physics; namespace o2::aod { @@ -70,6 +84,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(CosThetaStarEP, cosThetaStarEP, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float); DECLARE_SOA_COLUMN(MlProbDstarToD0PiNonPrompt, mlProbDstarToD0PiNonPrompt, float); @@ -112,24 +127,45 @@ DECLARE_SOA_TABLE(HfCandDstMix, "AOD", "HFCANDDSTMIX", mixing_dstar::MinNumTpcCls, mixing_dstar::GIndexCol, mixing_dstar::TimeStamp); +DECLARE_SOA_TABLE(HfMcParticles, "AOD", "HFMCPARTICLES", + mixing_dstar::Pt, + mixing_dstar::Eta, + mixing_dstar::Phi, + mixing_dstar::Y, + mixing_dstar::CosThetaStarEP, + mixing_dstar::Centrality); } // namespace o2::aod /// Writes the full information in an output TTree struct HfTreeCreatorDstarSpinAlignMixing { Produces rowCandidateMix; + Produces rowGenMcParticles; Configurable qVecDetector{"qVecDetector", 2, "Detector for Q vector estimation (FV0A: 0, FT0M: 1, FT0C: 2)"}; Configurable centEstimator{"centEstimator", 2, "Centrality estimator ((None: 0, FT0C: 2, FT0M: 3))"}; Configurable occEstimator{"occEstimator", 2, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"}; + Configurable activateAxisRotation{"activateAxisRotation", false, "Activate the rotation of the quantisation axis for cos(Theta*) calculation for phi dependence studies"}; + Configurable axisRotationAngle{"axisRotationAngle", 0.f, "Rotation angle of the quantisation axis for cos(Theta*) calculation for phi dependence studies (in rad)"}; + Configurable activatePartRecoDstar{"activatePartRecoDstar", false, "Activate the study of partly reconstructed D*+ -> D0 (-> KPiPi0) Pi decays"}; using CollsWithQVecs = soa::Join; using TracksWithExtra = soa::Join; using CandDstarWSelFlag = soa::Join; + + using CollisionsWithMcLabels = soa::SmallGroups>; + using CollisionsWithMcLabelsAndCent = soa::SmallGroups>; + using McParticlesDstarMatched = soa::Join; + using BCsInfo = soa::Join; + using FilteredCandDstarWSelFlagAndMl = soa::Filtered>; + using FilteredCandDstarWSelFlagAndMcAndMl = soa::Filtered>; Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Preslice dstarWithMlPerCollision = aod::hf_cand::collisionId; + Preslice dstarWithMcAndMlPerCollision = aod::hf_cand::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; void init(InitContext const&) { @@ -159,9 +195,72 @@ struct HfTreeCreatorDstarSpinAlignMixing { } } - template + /// \param ptCharmHad is the pt of the candidate + double sampleDeltaPhi(float ptCharmHad) + { + + double y0 = 6.49e-2; + double cons = 8.42e-1; + double coef = -3.78e-1; + double x0 = -6.37e-1; + double norm = -3.64e-1; + double mu = 4.68e-2; + double sigma = 1.513; + + double sigmoid = y0 + (cons - y0) / (1 + std::exp(-coef * (ptCharmHad - x0))); + double peak = norm * std::exp(-(ptCharmHad - mu) * (ptCharmHad - mu) / (2 * sigma * sigma)); + + double v2 = sigmoid + peak; + const double funcMax = 1.0 + 2.0 * std::abs(v2); + + while (true) { + double deltaPhi = gRandom->Uniform(0, constants::math::TwoPI); + double funcVal = gRandom->Uniform(0, funcMax); + + double funcDeltaPhi = 1.0 + 2.0 * v2 * std::cos(2.0 * deltaPhi); + + if (funcVal < funcDeltaPhi) + return deltaPhi; + } + return -1.; + } + + /// \param daughter is the daughter particle + /// \param ptMin is the minimum pt + /// \param etaMax is the maximum eta + /// \return true if daughter is in acceptance + template + bool isDaughterInAcceptance(Part const& daughter, float ptMin, float etaMax) + { + if (daughter.pt() < ptMin) { + return false; + } + if (std::abs(daughter.eta()) > etaMax) { + return false; + } + + return true; + } + + template void fillCandidateTable(CollType const& collision, const T& candidate, Trk const& /*tracks*/, BcType const& /*bcs*/) { + int8_t origin{RecoDecay::OriginType::None}; + bool partRecoDstar{false}; + if constexpr (DoMc) { + partRecoDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0 && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0; + bool const signalDstar = std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi && std::abs(candidate.flagMcMatchRecD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; + if (!signalDstar && (!partRecoDstar || !activatePartRecoDstar)) { // this candidate is not signal and not partially reconstructed signal, skip + return; + } + origin = candidate.originMcRec(); + int const pdgBhadMother = candidate.pdgBhadMotherPart(); + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (origin == RecoDecay::OriginType::NonPrompt && (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557)) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + return; + } + } + const auto bc = collision.template bc_as(); const int64_t timeStamp = bc.timestamp(); @@ -186,7 +285,12 @@ struct HfTreeCreatorDstarSpinAlignMixing { auto trackProng1 = candidate.template prong1_as(); auto trackProng2 = candidate.template prongPi_as(); getTrackingInfos(std::vector{trackProng0, trackProng1, trackProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); - std::array const Qvector = getQvec(collision, qVecDetector.value); + std::array Qvector = getQvec(collision, qVecDetector.value); + if constexpr (DoMc) { + double const deltaPhi = sampleDeltaPhi(candidate.pt()); + double psi = candidate.phi() - deltaPhi; + Qvector = {static_cast(-std::sin(psi)), static_cast(std::cos(psi)), 0.f}; + } rowCandidateMix( massD0, @@ -214,6 +318,90 @@ struct HfTreeCreatorDstarSpinAlignMixing { timeStamp); } + /// \param mcParticle is the Mc particle + /// \param mcParticles is the table of Mc particles + /// \param centrality is the centrality of the collision + template + void fillGenParticleTable(Part const& mcParticle, Particles const& mcParticles, float const centrality) + { + int8_t origin{RecoDecay::OriginType::None}; + bool partRecoDstar{false}; + partRecoDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPiPi0) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0); + bool const signalDstar = (std::abs(mcParticle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) && (std::abs(mcParticle.flagMcMatchGenD0()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + + if (!signalDstar && (!activatePartRecoDstar || !partRecoDstar)) { // this particle is not signal and not partially reconstructed signal, skip + return; + } + origin = mcParticle.originMcGen(); + float massDau{0.f}, massCharmHad{0.f}; + if (origin == RecoDecay::OriginType::NonPrompt) { + auto bHadMother = mcParticles.rawIteratorAt(mcParticle.idxBhadMotherPart() - mcParticles.offset()); + int const pdgBhadMother = std::abs(bHadMother.pdgCode()); + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + return; + } + } + std::vector listDaughters{}; + std::array const dauPdgs = {kPiPlus, o2::constants::physics::Pdg::kD0}; + RecoDecay::getDaughters(mcParticle, &listDaughters, dauPdgs, 1); + massDau = MassPiPlus; + massCharmHad = MassDStar; + + float const rapidity = mcParticle.y(); + if (std::abs(rapidity) > 1.f) { // we do not keep particles with |y| > 1 + return; + } + + bool areDauInAcc{true}; + float pxDau{-1000.f}, pyDau{-1000.f}, pzDau{-1000.f}; + for (const auto& dauIdx : listDaughters) { + auto dauPart = mcParticles.rawIteratorAt(dauIdx - mcParticles.offset()); + if (std::abs(dauPart.pdgCode()) == kPiPlus) { + pxDau = dauPart.px(); + pyDau = dauPart.py(); + pzDau = dauPart.pz(); + if (areDauInAcc) { + areDauInAcc = isDaughterInAcceptance(dauPart, 0.1, 0.8); + } + } else if (areDauInAcc) { // check also D0 daughters + std::vector listDaughtersD0{}; + std::array const dauPdgsD0 = {kPiPlus, -kKPlus}; + RecoDecay::getDaughters(mcParticle, &listDaughtersD0, dauPdgsD0, 1); + for (const auto& dauIdxD0 : listDaughtersD0) { + auto dauPartD0 = mcParticles.rawIteratorAt(dauIdxD0 - mcParticles.offset()); + areDauInAcc = isDaughterInAcceptance(dauPartD0, 0.3, 0.8); + } + } + } + + float const pxCharmHad = mcParticle.px(); + float const pyCharmHad = mcParticle.py(); + float const pzCharmHad = mcParticle.pz(); + ROOT::Math::PxPyPzMVector const fourVecDau = ROOT::Math::PxPyPzMVector(pxDau, pyDau, pzDau, massDau); + ROOT::Math::PxPyPzMVector const fourVecMother = ROOT::Math::PxPyPzMVector(pxCharmHad, pyCharmHad, pzCharmHad, massCharmHad); + ROOT::Math::Boost const boost{fourVecMother.BoostToCM()}; + ROOT::Math::PxPyPzMVector const fourVecDauCM = boost(fourVecDau); + ROOT::Math::XYZVector const threeVecDauCM = fourVecDauCM.Vect(); + + // EP analysis for MC + double const deltaPhi = sampleDeltaPhi(mcParticle.pt()); + double psi = mcParticle.phi() - deltaPhi; + ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } + float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); + + rowGenMcParticles( + mcParticle.pt(), + mcParticle.eta(), + mcParticle.phi(), + mcParticle.y(), + cosThetaStarEP, + centrality); + } + void processData(CollsWithQVecs const& collisions, FilteredCandDstarWSelFlagAndMl const& dstarCandidates, TracksWithExtra const& tracks, @@ -223,11 +411,35 @@ struct HfTreeCreatorDstarSpinAlignMixing { auto thisCollId = collision.globalIndex(); auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMlPerCollision, thisCollId); for (const auto& dstarCandidate : groupedDstarCandidates) { - fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); + fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); } } } PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processData, "Process data", true); + + void processDstarMcWithMlInPbPb(aod::McCollisions::iterator const&, + McParticlesDstarMatched const& mcParticles, + CollisionsWithMcLabelsAndCent const& collisions, // this is grouped with SmallGroupsCollisionsWithMcLabels const& collisions, + FilteredCandDstarWSelFlagAndMcAndMl const& dstarCandidates, + TracksWithExtra const& tracks, + BCsInfo const& bcWithTimeStamps) + { + for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision + auto thisCollId = collision.globalIndex(); + auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMcAndMlPerCollision, thisCollId); + for (const auto& dstarCandidate : groupedDstarCandidates) { + fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); + } + } + rowGenMcParticles.reserve(mcParticles.size()); + + for (const auto& mcParticle : mcParticles) { + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcParticle.mcCollision().globalIndex()); + const auto centrality = getCentralityGenColl(recoCollsPerMcColl, centEstimator); + fillGenParticleTable(mcParticle, mcParticles, centrality); + } + } + PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processDstarMcWithMlInPbPb, "Process Dstar candidates in PbPb MC with ML", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx index 0691e76b104..db34b9de314 100644 --- a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx +++ b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx @@ -47,6 +47,7 @@ #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -60,6 +61,7 @@ #include #include #include +#include #include using namespace o2; @@ -161,6 +163,8 @@ struct HfTaskCharmPolarisation { Configurable centralityMin{"centralityMin", 30, "Minimum centrality (0-100) to be considered in the analysis"}; Configurable centralityMax{"centralityMax", 50, "Maximum centrality (0-100) to be considered in the analysis"}; Configurable occEstimator{"occEstimator", 0, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"}; + Configurable activateAxisRotation{"activateAxisRotation", false, "Activate the rotation of the quantisation axis for cos(Theta*) calculation for phi dependence studies"}; + Configurable axisRotationAngle{"axisRotationAngle", 0.f, "Rotation angle of the quantisation axis for cos(Theta*) calculation for phi dependence studies (in rad)"}; /// activate rotational background Configurable nBkgRotations{"nBkgRotations", 0, "Number of rotated copies (background) per each original candidate"}; @@ -357,7 +361,9 @@ struct HfTaskCharmPolarisation { const AxisSpec thnAxisCosThetaStarProduction{configThnAxisCosThetaStarProduction, "cos(#vartheta_{production})"}; const AxisSpec thnAxisCosThetaStarRandom{configThnAxisCosThetaStarRandom, "cos(#vartheta_{random})"}; const AxisSpec thnAxisCosThetaStarBeam{configThnAxisCosThetaStarBeam, "cos(#vartheta_{beam})"}; - const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, "cos(#vartheta_{EP})"}; // reaction plane + std::string hAxisName = "Rotated cos(#vartheta_{EP}) with" + std::to_string(axisRotationAngle) + " rad"; + const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, activateAxisRotation ? hAxisName.c_str() : "cos(#vartheta_{EP})"}; // reaction plane + // const AxisSpec thnAxisCosThetaStarEP{configThnAxisCosThetaStarEP, "cos(#vartheta_{EP})"}; // reaction plane const AxisSpec thnAxisCosPhiStarEP{configThnAxisCosPhiStarEP, "cos(2#varphi_{EP})"}; // reaction plane const AxisSpec thnAxisCosSqThetaStarEP{configThnAxisCosSqThetaStarEP, "cos^{2}(#vartheta_{EP})"}; // reaction plane const AxisSpec thnAxisMlBkg{configThnAxisMlBkg, "ML bkg"}; @@ -2344,7 +2350,10 @@ struct HfTaskCharmPolarisation { // EP analysis for data float const xQvec = (*qVecs).at(0); float const yQvec = (*qVecs).at(1); - ROOT::Math::XYZVector const qVecNorm = ROOT::Math::XYZVector(yQvec, -xQvec, 0.f); + ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(yQvec, -xQvec, 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const phiStarBeam = std::atan2(threeVecDauCM.Y(), threeVecDauCM.X()); float const psiAngle = epHelper.GetEventPlane(xQvec, yQvec, 2); float const deltaPhiStarEP = RecoDecay::constrainAngle(phiStarBeam - psiAngle, 0., 2); @@ -2356,6 +2365,9 @@ struct HfTaskCharmPolarisation { double const deltaPhi = sampleDeltaPhi(ptCharmHad); double psi = candidate.phi() - deltaPhi; ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); fillRecoHistos(invMassCharmHadForSparse, ptCharmHad, numPvContributors, rapidity, invMassD0, invMassKPiLc, cosThetaStarEP, -99.f, outputMl, isRotatedCandidate, origin, ptBhadMother, resoChannelLc, absEtaTrackMin, numItsClsMin, numTpcClsMin, charge, nMuons, partRecoDstar, centrality); } @@ -2721,6 +2733,9 @@ struct HfTaskCharmPolarisation { double const deltaPhi = sampleDeltaPhi(ptCharmHad); double psi = mcParticle.phi() - deltaPhi; ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); + if (activateAxisRotation) { + qVecNorm = ROOT::Math::RotationZ(axisRotationAngle)(qVecNorm); + } float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()); if constexpr (WithCent) { fillGenHistos(ptCharmHad, numPvContributors, rapidity, cosThetaStarEP, origin, ptBhadMother, areDauInAcc, resoChannelLc, charge, partRecoDstar, *centrality); From 9318d3ae120d61a069c29b194ae6b090a0d6642d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 10 Nov 2026 16:03:19 +0100 Subject: [PATCH 643/648] [DPG] Fix unused variables, parameters, values (#18639) --- DPG/Tasks/AOTTrack/qaEfficiency.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DPG/Tasks/AOTTrack/qaEfficiency.cxx b/DPG/Tasks/AOTTrack/qaEfficiency.cxx index 22d7b6b5497..c051ae4cc9e 100644 --- a/DPG/Tasks/AOTTrack/qaEfficiency.cxx +++ b/DPG/Tasks/AOTTrack/qaEfficiency.cxx @@ -1802,10 +1802,10 @@ struct QaEfficiency { } float centrality = -1.; float occupancy = -1.; - if (doOccupancy) { - centrality = getCentralityColl(groupedCollisions); - occupancy = getOccupancyColl(groupedCollisions); - } + // if (doOccupancy) { + // centrality = getCentralityColl(groupedCollisions); // FIXME: unused + // occupancy = getOccupancyColl(groupedCollisions); // FIXME: unused + // } histos.fill(HIST("MC/generatedCollisions"), 2); if (skipEventsWithoutTPCTracks) { int nTPCTracks = 0; From 6b46975b35f94d1a222265c013cb3f034c354a7b Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Tue, 10 Nov 2026 16:36:05 +0100 Subject: [PATCH 644/648] [PWGHF] Fix matching to MC for B2JPsi analyses to account for photons produced by EVTGEN (#18668) --- .../dataCreatorJpsiHadReduced.cxx | 97 +++++++++++-------- PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx | 8 +- PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx | 8 +- PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx | 8 +- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx index 49ca42dcee4..55aa2f61dd5 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx @@ -535,11 +535,11 @@ struct HfDataCreatorJpsiHadReduced { // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } } @@ -568,11 +568,11 @@ struct HfDataCreatorJpsiHadReduced { // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToEEK; } } @@ -615,16 +615,16 @@ struct HfDataCreatorJpsiHadReduced { std::vector dauIndices{}; const int8_t signB = sign; // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); if (indexRec > -1) { @@ -652,16 +652,16 @@ struct HfDataCreatorJpsiHadReduced { const int8_t signB = sign; // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToEEPiK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); if (indexRec > -1) { @@ -715,16 +715,16 @@ struct HfDataCreatorJpsiHadReduced { std::vector dauIndices{}; const int8_t signB = sign; // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); if (indexRec > -1) { @@ -752,16 +752,16 @@ struct HfDataCreatorJpsiHadReduced { const int8_t signB = sign; // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false - if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi - std::vector jpsiDauIndices{}; + std::vector jpsiDauPdgs{}; for (const auto& dau : jpsiPart.template daughters_as()) { - jpsiDauIndices.push_back(dau.pdgCode()); + jpsiDauPdgs.push_back(dau.pdgCode()); } - if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + if (jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToEEKK; indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); if (indexRec > -1) { @@ -844,14 +844,21 @@ struct HfDataCreatorJpsiHadReduced { // Match generated particles. for (const auto& particle : mcParticlesPerMcColl) { - int8_t sign{0}, flag{0}, channel{0}; + int8_t sign = particle.pdgCode() > 0 ? 1 : -1; + int8_t flag{0}, channel{0}; if constexpr (DecChannel == DecayChannel::BplusToJpsiK) { // B+ → J/Psi K+ → (µ+µ-) K+ - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBPlus, std::array{static_cast(Pdg::kJPsi), +kKPlus}, true, &sign)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kBPlus, std::array{static_cast(Pdg::kJPsi), sign * kKPlus}, false)) { // Match J/Psi -> µ+µ- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - // Printf("Checking J/Psi -> µ+µ-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } } @@ -880,16 +887,22 @@ struct HfDataCreatorJpsiHadReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { // B0 → J/Psi K*0 → (µ+µ-) (K+pi-) - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kB0, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kPiPlus}, true, &sign, 2)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kB0, std::array{static_cast(Pdg::kJPsi), sign * kKPlus, -1 * sign * kPiPlus}, false, nullptr, 2)) { // Match J/Psi -> µ+µ- and K*0 -> K+pi- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - auto candK0StarMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); - // Printf("Checking J/Psi -> µ+µ- and K*0 -> K+pi-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; } + const auto& kStarPart = particlesMc.rawIteratorAt(particle.daughtersIds().back()); // Check K*0 -> K+pi- - if (RecoDecay::isMatchedMCGen(particlesMc, candK0StarMC, static_cast(Pdg::kK0Star892), std::array{+kKPlus, -kPiPlus}, true)) { + if (RecoDecay::isMatchedMCGen(particlesMc, kStarPart, static_cast(Pdg::kK0Star892), std::array{+kKPlus, -kPiPlus}, true)) { channel = o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; } } @@ -918,16 +931,22 @@ struct HfDataCreatorJpsiHadReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { // Bs → J/Psi phi → (µ+µ-) (K+K-) - if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBS, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kKPlus}, true, &sign, 2)) { + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, particle, sign * Pdg::kBS, std::array{static_cast(Pdg::kJPsi), sign * kKPlus, -1 * sign * kKPlus}, false, nullptr, 2)) { // Match J/Psi -> µ+µ- and phi -> K+K- - auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); - auto candPhiMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); - // Printf("Checking J/Psi -> µ+µ- and phi -> K+K-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauPdgs{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauPdgs.push_back(dau.pdgCode()); + } + if (std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kMuonMinus) == 1 && std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), -kMuonMinus) && jpsiDauPdgs.size() - std::count(jpsiDauPdgs.begin(), jpsiDauPdgs.end(), kGamma) == nProngsJpsi) { flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; } + const auto& phiPart = particlesMc.rawIteratorAt(particle.daughtersIds().back()); // Check phi -> K+K- - if (RecoDecay::isMatchedMCGen(particlesMc, candPhiMC, static_cast(Pdg::kPhi), std::array{-kKPlus, +kKPlus}, true)) { + if (RecoDecay::isMatchedMCGen(particlesMc, phiPart, static_cast(Pdg::kPhi), std::array{-kKPlus, +kKPlus}, true)) { channel = o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; } } diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx index 9431ca25590..15d69d2f1bb 100644 --- a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -342,7 +342,7 @@ struct HfTaskB0ToJpsiK0StarReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtK0Star{100, 0.f, 10.f}; registry.add("hMass", b0CandTitle + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); @@ -353,7 +353,8 @@ struct HfTaskB0ToJpsiK0StarReduced { if (doprocessMc || doprocessMcWithB0Ml) { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtK0StarGen", mcParticleMatched + "K*^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtK0Star, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", b0CandMatch + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2}); B^{0} " + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); registry.add("hMassJpsiRecSig", b0CandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hMassK0StarRecSig", b0CandMatch + "inv. mass K^{+}#pi^{#minus} (GeV/#it{c}^{2}); K*^{0} " + stringPt, {HistType::kTH2F, {axisMassK0Star, axisPtK0Star}}); @@ -679,9 +680,10 @@ struct HfTaskB0ToJpsiK0StarReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtK0StarGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated B0 with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index 6f1ea153985..316bdad3a22 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -303,7 +303,7 @@ struct HfTaskBplusToJpsiKReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtKa{100, 0.f, 10.f}; registry.add("hMass", bPlusCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); @@ -314,7 +314,8 @@ struct HfTaskBplusToJpsiKReduced { if (doprocessMc || doprocessMcWithBplusMl) { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtKGen", mcParticleMatched + "Kaon #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B^{+} #it{p}_{T}^{gen} (GeV/#it{c}); B^{+} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", bPlusCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B^{+} " + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); registry.add("hMassJpsiRecSig", bPlusCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hd0KRecSig", bPlusCandMatch + "Kaon DCAxy to prim. vertex (cm); K^{+} " + stringPt, {HistType::kTH2F, {axisImpactPar, axisPtKa}}); @@ -526,9 +527,10 @@ struct HfTaskBplusToJpsiKReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtKGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated B+ with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index 86d6a7cfbab..4cc6089befb 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -329,7 +329,7 @@ struct HfTaskBsToJpsiPhiReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtB{500, 0., 50., "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; const AxisSpec axisPtPhi{100, 0.f, 10.f}; registry.add("hMass", bsCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); @@ -341,7 +341,8 @@ struct HfTaskBsToJpsiPhiReduced { registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtPhiGen", mcParticleMatched + "#phi #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtKGen", mcParticleMatched + "Kaon #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hPtYGenSig", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); + registry.add("hPtYWithProngsInAccepanceGenSig", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtB, axisRapidity}}); registry.add("hMassRecSig", bsCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); registry.add("hMassJpsiRecSig", bsCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); registry.add("hMassPhiRecSig", bsCandMatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); @@ -575,9 +576,10 @@ struct HfTaskBsToJpsiPhiReduced { registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); registry.fill(HIST("hPtKGen"), ptProngs[1], ptParticle); + registry.fill(HIST("hPtYGenSig"), ptParticle, yParticle); // generated Bs with daughters in geometrical acceptance if (prongsInAcc) { - registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + registry.fill(HIST("hPtYWithProngsInAccepanceGenSig"), ptParticle, yParticle); } } From 4bc2c8ce0417b1bb4c6f43935e1fdc12258d5c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:43:38 +0100 Subject: [PATCH 645/648] [PWGLF,PWGMM] Fix unused variables, parameters, values (#18646) --- PWGMM/Lumi/Tasks/LumiFDDFT0.cxx | 2 +- PWGMM/Lumi/Tasks/lumi.cxx | 2 +- PWGMM/Lumi/Tasks/lumiStability.cxx | 22 +++++++++---------- PWGMM/Lumi/macros/merger.C | 2 +- PWGMM/Lumi/macros/non_fac.C | 3 ++- .../ambiguousTrackPropagation.cxx | 4 ++-- PWGMM/Mult/Tasks/dndeta-hi.cxx | 2 +- PWGMM/Mult/Tasks/vertexing-fwd.cxx | 2 +- 8 files changed, 20 insertions(+), 19 deletions(-) diff --git a/PWGMM/Lumi/Tasks/LumiFDDFT0.cxx b/PWGMM/Lumi/Tasks/LumiFDDFT0.cxx index 629c2a7078d..6a4fa937154 100644 --- a/PWGMM/Lumi/Tasks/LumiFDDFT0.cxx +++ b/PWGMM/Lumi/Tasks/LumiFDDFT0.cxx @@ -226,7 +226,7 @@ struct LumiFDDFT0 { Pvtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); // configure PVertexer - o2::dataformats::VertexBase PVbase_recalculated; + // o2::dataformats::VertexBase PVbase_recalculated; // FIXME: unused o2::vertexing::PVertexer vertexer; o2::conf::ConfigurableParam::updateFromString( "pvertexer.useMeanVertexConstraint=false"); // we want to refit w/o diff --git a/PWGMM/Lumi/Tasks/lumi.cxx b/PWGMM/Lumi/Tasks/lumi.cxx index b1fcd5910d5..66078d9a975 100644 --- a/PWGMM/Lumi/Tasks/lumi.cxx +++ b/PWGMM/Lumi/Tasks/lumi.cxx @@ -179,7 +179,7 @@ struct lumiTask { collision.covXZ(), collision.covYZ(), collision.covZZ()); // configure PVertexer - o2::dataformats::VertexBase PVbase_recalculated; + // o2::dataformats::VertexBase PVbase_recalculated; // FIXME: unused o2::vertexing::PVertexer vertexer; o2::conf::ConfigurableParam::updateFromString( diff --git a/PWGMM/Lumi/Tasks/lumiStability.cxx b/PWGMM/Lumi/Tasks/lumiStability.cxx index ab1dca52f6e..7df9b90ff31 100644 --- a/PWGMM/Lumi/Tasks/lumiStability.cxx +++ b/PWGMM/Lumi/Tasks/lumiStability.cxx @@ -276,11 +276,11 @@ struct LumiStabilityTask { int executionCounter = 0; int nbin = o2::constants::lhc::LHCMaxBunches; uint32_t nOrbitsPerTF = 0; // 128 in 2022, 32 in 2023 - if (is2022Data) { - nOrbitsPerTF = 128; // 128 in 2022, 32 in 2023 - } else { - nOrbitsPerTF = 32; // 128 in 2022, 32 in 2023 - } + // if (is2022Data) { + // nOrbitsPerTF = 128; // 128 in 2022, 32 in 2023 // FIXME: unused + // } else { + // nOrbitsPerTF = 32; // 128 in 2022, 32 in 2023 // FIXME: unused + // } int runNumber = bcs.iteratorAt(0).runNumber(); if (runNumber != lastRunNumber && executionCounter < 1) { tsSOR = 0; @@ -497,8 +497,8 @@ struct LumiStabilityTask { pastActivityFDDVertex |= (vertexPast); } } - deltaIndex = 0; - deltaBC = 0; + // deltaIndex = 0; // FIXME: unused + // deltaBC = 0; // FIXME: unused if (pastActivityFDDVertex == false) { histos.fill(HIST("FDD/hCounts"), 2); @@ -645,8 +645,8 @@ struct LumiStabilityTask { pastActivityFDDTriggerCCoincidenceC |= (triggerCPast & isCoinCPast); } } - deltaIndex = 0; - deltaBC = 0; + // deltaIndex = 0; // FIXME: unused + // deltaBC = 0; // FIXME: unused if (pastActivityFDDVertexCoincidences == false) { histos.fill(HIST("FDD/hCounts"), 4); @@ -782,8 +782,8 @@ struct LumiStabilityTask { pastActivityFT0TriggerC |= triggerCPast; } } - deltaIndex = 0; - deltaBC = 0; + // deltaIndex = 0; // FIXME: unused + // deltaBC = 0; // FIXME: unused histos.fill(HIST("FT0/hCounts"), 1); if (pastActivityFT0Vertex == false) { diff --git a/PWGMM/Lumi/macros/merger.C b/PWGMM/Lumi/macros/merger.C index 1ad2cd5f920..02aa16e5f03 100644 --- a/PWGMM/Lumi/macros/merger.C +++ b/PWGMM/Lumi/macros/merger.C @@ -30,7 +30,7 @@ void merger() TFile* fout = new TFile("mergedOutput.root", "recreate"); TTree* tin = new TTree("EventInfo_merged", ""); - TList* list = fin->GetListOfKeys(); + // TList* list = fin->GetListOfKeys(); // FIXME: unused TIter next(fin->GetListOfKeys()); ULong64_t fTimeStamp; diff --git a/PWGMM/Lumi/macros/non_fac.C b/PWGMM/Lumi/macros/non_fac.C index ea858e409f0..a9822bc2526 100644 --- a/PWGMM/Lumi/macros/non_fac.C +++ b/PWGMM/Lumi/macros/non_fac.C @@ -45,7 +45,7 @@ double tmax; int Countig; -void chi2(Int_t& npar, Double_t* gin, Double_t& f, Double_t* par, Int_t flag) +void chi2(Int_t& /*npar*/, Double_t* /*gin*/, Double_t& f, Double_t* par, Int_t /*flag*/) { TMatrixD sigma(2, 2); double results = 0.; @@ -241,4 +241,5 @@ void non_fac() for (int i = 0; i < 10; i++) { hParam[i]->Write(); } + fout->Close(); } diff --git a/PWGMM/Mult/TableProducer/ambiguousTrackPropagation.cxx b/PWGMM/Mult/TableProducer/ambiguousTrackPropagation.cxx index af3a895eec3..2c14f75d51e 100644 --- a/PWGMM/Mult/TableProducer/ambiguousTrackPropagation.cxx +++ b/PWGMM/Mult/TableProducer/ambiguousTrackPropagation.cxx @@ -292,7 +292,7 @@ struct AmbiguousTrackPropagation { o2::track::TrackParCovFwd bestTrackPar; for (auto const& atrack : atracks) { - dcaInfo = 999; // DCAxy + // dcaInfo = 999; // DCAxy // FIXME: unused bestDCA = 999; auto track = atrack.mfttrack(); @@ -375,7 +375,7 @@ struct AmbiguousTrackPropagation { o2::track::TrackParCovFwd bestTrackPar; for (auto const& track : tracks) { - dcaInfo = 999; // DCAxy + // dcaInfo = 999; // DCAxy // FIXME: unused bestDCA = 999; auto bestCol = track.has_collision() ? track.collisionId() : -1; diff --git a/PWGMM/Mult/Tasks/dndeta-hi.cxx b/PWGMM/Mult/Tasks/dndeta-hi.cxx index 630922994cb..f898924600e 100644 --- a/PWGMM/Mult/Tasks/dndeta-hi.cxx +++ b/PWGMM/Mult/Tasks/dndeta-hi.cxx @@ -835,7 +835,7 @@ struct MultiplicityCounter { auto pTrack = v0.template posTrack_as(); auto nTrack = v0.template negTrack_as(); - if (0 && v0.v0radius() > v0radius && + if (0 && v0.v0radius() > v0radius && // FIXME: always false v0.v0cosPA() > v0cospa && abs(pTrack.eta()) < etadau && abs(nTrack.eta()) < etadau) { diff --git a/PWGMM/Mult/Tasks/vertexing-fwd.cxx b/PWGMM/Mult/Tasks/vertexing-fwd.cxx index f5dd786acc7..66a588057b6 100644 --- a/PWGMM/Mult/Tasks/vertexing-fwd.cxx +++ b/PWGMM/Mult/Tasks/vertexing-fwd.cxx @@ -148,7 +148,7 @@ struct vertexingfwd { for (auto& atrack : atracks) { - dcaXY = 999; // DCAxy + // dcaXY = 999; // DCAxy // FIXME: unused bestDCA = 999; auto track = atrack.mfttrack_as(); From 92ce54a2e7b19717ed0cc4aacfe6474f025457bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:45:07 +0100 Subject: [PATCH 646/648] [PWGLF] Fix unused variables, parameters, values (#18645) --- PWGLF/TableProducer/Common/zdcvector.cxx | 8 ++++---- PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx | 2 +- PWGLF/Tasks/Nuspex/NucleiEfficiencyTask.cxx | 6 +++--- PWGLF/Tasks/Nuspex/QAHistTask.cxx | 2 +- PWGLF/Tasks/Strangeness/cascpolsp.cxx | 2 +- PWGLF/Tasks/Strangeness/lambdapolsp.cxx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PWGLF/TableProducer/Common/zdcvector.cxx b/PWGLF/TableProducer/Common/zdcvector.cxx index ff67bf9f081..a07543d8615 100644 --- a/PWGLF/TableProducer/Common/zdcvector.cxx +++ b/PWGLF/TableProducer/Common/zdcvector.cxx @@ -449,10 +449,10 @@ struct zdcvector { } if (sumA <= 1e-4 || sumC <= 1e-4) { - qxZDCA = 0.0; - qxZDCC = 0.0; - qyZDCA = 0.0; - qyZDCC = 0.0; + // qxZDCA = 0.0; // FIXME: unused + // qxZDCC = 0.0; // FIXME: unused + // qyZDCA = 0.0; // FIXME: unused + // qyZDCC = 0.0; // FIXME: unused triggerevent = false; // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); diff --git a/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx b/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx index 7f9a02892f5..8af356d4b91 100644 --- a/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx +++ b/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx @@ -246,7 +246,7 @@ struct pidTOFGeneric { // Reset the event time eventTime = 0.f; sumOfWeights = 0.f; - weight = 0.f; + // weight = 0.f; // FIXME: unused // Remove the bias on TOF ev. time if constexpr (kRemoveTOFEvTimeBias) { evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); diff --git a/PWGLF/Tasks/Nuspex/NucleiEfficiencyTask.cxx b/PWGLF/Tasks/Nuspex/NucleiEfficiencyTask.cxx index f4b9b94a9cb..e7001f7e836 100644 --- a/PWGLF/Tasks/Nuspex/NucleiEfficiencyTask.cxx +++ b/PWGLF/Tasks/Nuspex/NucleiEfficiencyTask.cxx @@ -333,7 +333,7 @@ struct NucleiEfficiencyTask { pdgbin = 13; break; default: - pdgbin = -10; + // pdgbin = -10; // FIXME: unused continue; break; } @@ -453,7 +453,7 @@ struct NucleiEfficiencyTask { pdgbin = 13; break; default: - pdgbin = -10; + // pdgbin = -10; // FIXME: unused continue; break; } @@ -539,7 +539,7 @@ struct NucleiEfficiencyTask { pdgbin = 13; break; default: - pdgbin = -10; + // pdgbin = -10; // FIXME: unused continue; break; } diff --git a/PWGLF/Tasks/Nuspex/QAHistTask.cxx b/PWGLF/Tasks/Nuspex/QAHistTask.cxx index c8ebb7ca97f..f6997f52f17 100644 --- a/PWGLF/Tasks/Nuspex/QAHistTask.cxx +++ b/PWGLF/Tasks/Nuspex/QAHistTask.cxx @@ -737,7 +737,7 @@ struct QAHistTask { lorentzVector_particle_MC.SetPtEtaPhiM(track.pt() * 2.0, track.eta(), track.phi(), constants::physics::MassAlpha); break; default: - pdgbin = -10; + // pdgbin = -10; // FIXME: unused continue; break; } diff --git a/PWGLF/Tasks/Strangeness/cascpolsp.cxx b/PWGLF/Tasks/Strangeness/cascpolsp.cxx index 123f9605154..09e3406b7da 100644 --- a/PWGLF/Tasks/Strangeness/cascpolsp.cxx +++ b/PWGLF/Tasks/Strangeness/cascpolsp.cxx @@ -423,7 +423,7 @@ struct cascpolsp { } int taga = LambdaTag; - int tagb = aLambdaTag; + int tagb{}; // = aLambdaTag; // FIXME: unused if (LambdaTag) { Lambda = Proton + AntiPion; diff --git a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx index 45d7fd49b01..41411125a1d 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx @@ -1851,7 +1851,7 @@ struct lambdapolsp { continue; int taga = LambdaTag; - int tagb = aLambdaTag; + int tagb{}; // = aLambdaTag; // FIXME: unused if (useAccCorr) { accprofileL = ccdb->getForTimeStamp(ConfAccPathL.value, bc.timestamp()); @@ -2220,7 +2220,7 @@ struct lambdapolsp { } int taga = LambdaTag; - int tagb = aLambdaTag; + int tagb{}; // = aLambdaTag; // FIXME: unused int tagc = K0sTag; if (analyzeK0s && K0sTag) { From 2703a74a173e2812edd28fdb4f8beb97519c79b0 Mon Sep 17 00:00:00 2001 From: Junlee Kim Date: Wed, 11 Nov 2026 04:11:52 +0900 Subject: [PATCH 647/648] [PWGLF] cha01710analysis.cxx: fix bug for the Armenteros cut selection (#18675) --- PWGLF/Tasks/Resonances/cha01710analysis.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx index 6a23c0dec06..664cf2185b9 100644 --- a/PWGLF/Tasks/Resonances/cha01710analysis.cxx +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -337,7 +337,7 @@ struct Cha01710analysis { std::abs(v0.mAntiLambda() - constants::physics::MassLambda0) < v0Cuts.cfgLambdaMassWindow)) { return V0MassRegion::kReject; } - if (v0Cuts.cfgApplyArmenterosCut && v0.qtarm() < v0Cuts.cfgArmenterosCut) { + if (v0Cuts.cfgApplyArmenterosCut && v0.qtarm() < v0Cuts.cfgArmenterosCut * std::fabs(v0.alpha())) { return V0MassRegion::kReject; } From a6b6fb64f3f5cc5a6849e5947d662565cfca113e Mon Sep 17 00:00:00 2001 From: iulianflorinandreicovici-144 Date: Tue, 10 Nov 2026 22:13:07 +0200 Subject: [PATCH 648/648] [PWGCF] Update mean-pT profiles for angular correlation regions in delta phi (#18662) --- .../Tasks/CMakeLists.txt | 2 +- ...relation.cxx => foxWolframCorrelation.cxx} | 245 ++++++++++-------- 2 files changed, 140 insertions(+), 107 deletions(-) rename PWGCF/TwoParticleCorrelations/Tasks/{FoxWolframCorrelation.cxx => foxWolframCorrelation.cxx} (85%) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index 83f52b206e0..ae59372d44d 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -144,7 +144,7 @@ o2physics_add_dpl_workflow(lambda-or-anti-lambda-producer-with-spin COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fox-wolfram-correlation - SOURCES FoxWolframCorrelation.cxx + SOURCES foxWolframCorrelation.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/foxWolframCorrelation.cxx similarity index 85% rename from PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx rename to PWGCF/TwoParticleCorrelations/Tasks/foxWolframCorrelation.cxx index 7cd6f5fdf87..c76e92489fb 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/foxWolframCorrelation.cxx @@ -9,12 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FoxWolframCorrelation.cxx +/// \file foxWolframCorrelation.cxx /// \author Andreicovici Iulian Florin /// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. /// Classifies events by topology (triangular), fills correlation histograms, /// and produces custom SOA tables for mixed-event background estimation. -/// \since 06 Nov 2026 +/// \since 10 Nov 2026 #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -117,11 +117,13 @@ template bool passesLeadAssoc(int mode, bool foundPart, Track const& track1, Track const& track2, TLead const& triggTrk, TLead const& triggTrkPart) { + auto ptUpperLim1 = 3.f, ptUpperLim2 = 2.f; // low and high limits in pt + if (mode == kAllPairs) { // all i-j pairs - return (track1.pt() < 3.f) && (track2.pt() < 3.f); + return (track1.pt() < ptUpperLim1) && (track2.pt() < ptUpperLim1); } if (mode == kpTLeadConstrain) { // L-A: Leading pt in (2,3), Assoc pt < 2 - return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < 2.f); + return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < ptUpperLim2); } if (mode == kpTLeadFree) { // L-A return (track1.globalIndex() == triggTrk.globalIndex()) && (track2.pt() < track1.pt()); @@ -140,6 +142,8 @@ auto findLeadingPart(T const& tracks) double ptMaxAll = -1., ptMaxPart = -1.; bool foundPart = false; + auto ptLim1 = 2.f, ptLim2 = 3.f; // low and high limits in pt + for (const auto& track : tracks) { // pT Leading (no constraint) if (track.pt() > ptMaxAll) { @@ -148,7 +152,7 @@ auto findLeadingPart(T const& tracks) } // pT Leading in (2,3) - if (track.pt() > 2.f && track.pt() < 3.f && track.pt() > ptMaxPart) { + if (track.pt() > ptLim1 && track.pt() < ptLim2 && track.pt() > ptMaxPart) { ptMaxPart = track.pt(); leadingPartId = i; foundPart = true; @@ -190,6 +194,9 @@ struct FoxWolframCorrelation { Configurable topologyMode{"topologyMode", kTriEvSetNom, "SelOpt: 0 AllEv, 1 TriNom, 2 IsoNom, 3 TriSet1p, 4 TriSet2p, 5 TriSet3p, 6 TriSet1m, 7 TriSet2m, 8 TriSet3m"}; + //// Minimum associated pT included in mean-pT profiles //// + Configurable meanPtMin{"meanPtMin", 0.5, "Associated pT threshold for mean-pT profiles (GeV/c)"}; + // Choose the pair selection for same-event correlations. Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; @@ -241,39 +248,48 @@ struct FoxWolframCorrelation { static constexpr int MaxMultBins = 10; int nMultBins = 0; // set in init() from multLower/multUpper - static constexpr std::array binNames = { + static constexpr std::array BinNames = { "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; //// Class-level names for compile-time HIST lookups in nested loops //// - static constexpr std::array nearNames = { + static constexpr std::array NearNames = { "nearPtD0_", "nearPtD1_", "nearPtD2_", "nearPtD3_", "nearPtD4_", "nearPtD5_"}; - static constexpr std::array awayNames = { + static constexpr std::array AwayNames = { "awayPtD0_", "awayPtD1_", "awayPtD2_", "awayPtD3_", "awayPtD4_", "awayPtD5_"}; - static constexpr std::array allLRNames = { + //// Profile names match the folded LR declarations //// + static constexpr std::array NearMeanNames = { + "nearPtDMean0_", "nearPtDMean1_", "nearPtDMean2_", + "nearPtDMean3_", "nearPtDMean4_", "nearPtDMean5_"}; + + static constexpr std::array AwayMeanNames = { + "awayPtDMean0_", "awayPtDMean1_", "awayPtDMean2_", + "awayPtDMean3_", "awayPtDMean4_", "awayPtDMean5_"}; + + static constexpr std::array AllLrNames = { "allLRNear", "allLRValley", "allLRAway"}; - static constexpr std::array allSRNames = { + static constexpr std::array AllSrNames = { "allSRNear", "allSRValley", "allSRAway"}; - static constexpr std::array triLRNames = { + static constexpr std::array TriLrNames = { "triLRPeak1", "triLRPeak2", "triLRPeak3", "triLRValleys"}; - static constexpr std::array triSRNames = { + static constexpr std::array TriSrNames = { "triSRPeak1", "triSRPeak2", "triSRPeak3", "triSRValleys"}; // Map each unique pair of Fox-Wolfram moments to its histogram. - static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; - static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; - static constexpr std::array fwmMultHNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; + static constexpr std::array FwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; + static constexpr std::array FwmHlNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; + static constexpr std::array FwmMulthNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; - static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; - static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; - static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; + static constexpr std::array FwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + static constexpr std::array FwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; + static constexpr std::array FwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; // Register event, track, moment, and correlation histograms. void init(InitContext const&) @@ -288,9 +304,7 @@ struct FoxWolframCorrelation { LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); } nMultBins = static_cast(multLower.value.size()); - if (nMultBins < 3) { - LOGF(fatal, "At least three broad multiplicity ranges are required"); - } + for (int i = 0; i < nMultBins; ++i) { if (multLower.value[i] > multUpper.value[i]) { LOGF(fatal, "Multiplicity bin %d has multLower (%d) > multUpper (%d)", i + 1, multLower.value[i], multUpper.value[i]); @@ -417,10 +431,14 @@ struct FoxWolframCorrelation { histos.add(fmt::format("Spectra/etaphiTriEv_BIN{}", i).c_str(), fmt::format("etaphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); histos.add(fmt::format("Spectra/ptphiTriEv_BIN{}", i).c_str(), fmt::format("ptphiTriEv_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); - //// Six near spectra and six combined 120/240-degree spectra per multiplicity bin //// - for (int angularBin = 0; angularBin < 6; ++angularBin) { - foldedHistos.add(fmt::format("triEvFoldedLR/nearPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR near: {}-{} deg from 0", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); - foldedHistos.add(fmt::format("triEvFoldedLR/awayPtD{}_BIN{}", angularBin, i).c_str(), fmt::format("LR away: {}-{} deg from 120 and 240", 10 * angularBin, 10 * (angularBin + 1)).c_str(), kTH1D, {axispT}); + //// Folded LR pT spectra and mean pT versus multiplicity //// + for (int AngBin = 0; AngBin < 6; ++AngBin) { + + foldedHistos.add(fmt::format("triEvFoldedLR/nearPtD{}_BIN{}", AngBin, i).c_str(), fmt::format("LR near: {}-{} deg from 0", 10 * AngBin, 10 * (AngBin + 1)).c_str(), kTH1D, {axispT}); + foldedHistos.add(fmt::format("triEvFoldedLR/nearPtDMean{}_BIN{}", AngBin, i).c_str(), fmt::format("LR near: {}-{} deg from 0", 10 * AngBin, 10 * (AngBin + 1)).c_str(), kTProfile, {axisMultip}); + + foldedHistos.add(fmt::format("triEvFoldedLR/awayPtD{}_BIN{}", AngBin, i).c_str(), fmt::format("LR away: {}-{} deg from 120 and 240", 10 * AngBin, 10 * (AngBin + 1)).c_str(), kTH1D, {axispT}); + foldedHistos.add(fmt::format("triEvFoldedLR/awayPtDMean{}_BIN{}", AngBin, i).c_str(), fmt::format("LR away: {}-{} deg from 120 and 240", 10 * AngBin, 10 * (AngBin + 1)).c_str(), kTProfile, {axisMultip}); } //// ALL: pT spectra (TH1D) and mean associated pT versus multiplicity (TProfile) //// @@ -478,14 +496,13 @@ struct FoxWolframCorrelation { // Register optional pairwise moment correlations. if (doFWMCorrelations) { - for (size_t k = 0; k < fwmCorrelationNames.size(); ++k) { - - const int first = fwmCorrelationFirst[k] + 1; - const int second = fwmCorrelationSecond[k] + 1; + for (size_t k = 0; k < FwmCorrelationNames.size(); ++k) { + const int first = FwmCorrelationFirst[k] + 1; + const int second = FwmCorrelationSecond[k] + 1; - histos.add(fmt::format("FWM_BIN1/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); - histos.add(fmt::format("FWM_BIN2/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); - histos.add(fmt::format("FWM_BIN3/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN1/{}", FwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN2/{}", FwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN3/{}", FwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); } } } @@ -635,61 +652,68 @@ struct FoxWolframCorrelation { // Fill inclusive spectra and selected-topology spectra for this interval. static_for<0, 9>([&](auto i) { - constexpr int idx = i.value; + constexpr int IdxVal = i.value; - if (binIndex == idx + 1) { + if (binIndex == IdxVal + 1) { for (const auto& track : tracks) { - histos.fill(HIST("Spectra/etaphiAllEv_") + HIST(binNames[idx]), track.phi(), track.eta()); - histos.fill(HIST("Spectra/ptphiAllEv_") + HIST(binNames[idx]), track.phi(), track.pt()); + histos.fill(HIST("Spectra/etaphiAllEv_") + HIST(BinNames[IdxVal]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiAllEv_") + HIST(BinNames[IdxVal]), track.phi(), track.pt()); if (!isSelectedTopology) { continue; } // TRI Ev. - histos.fill(HIST("Spectra/etaphiTriEv_") + HIST(binNames[idx]), track.phi(), track.eta()); - histos.fill(HIST("Spectra/ptphiTriEv_") + HIST(binNames[idx]), track.phi(), track.pt()); + histos.fill(HIST("Spectra/etaphiTriEv_") + HIST(BinNames[IdxVal]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiTriEv_") + HIST(BinNames[IdxVal]), track.phi(), track.pt()); } } }); // per Multip Bin } - //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// + //// Helper for one multiplicity bin (IdxVal is a template parameter, not a captured lambda variable) //// //// Avoids the GCC 14 ICE in tsubst_expr caused by nested generic static_for lambdas //// - template - void fillFoldedBin(double deltaPhi, double pt) + template + void fillFoldedBin(int nTracks, double deltaPhi, double pt) { const double degToRad = constants::math::PI / 180.; - //// Adjacent windows include the lower edge and exclude the upper edge //// + //// Six angular intervals with the existing strict boundary cuts //// static_for<0, 5>([&](auto j) { - constexpr int angularBin = j.value; - constexpr double lo = 10. * angularBin; - constexpr double hi = 10. * (angularBin + 1); + constexpr int AngBin = j.value; + constexpr double LowLim = 10. * AngBin; + constexpr double HighLim = 10. * (AngBin + 1); //// Combine the two sides of the near peak at zero //// - const bool inNear = (deltaPhi > -hi * degToRad && deltaPhi < -lo * degToRad) || (deltaPhi > lo * degToRad && deltaPhi < hi * degToRad); + const bool inNear = (deltaPhi > -HighLim * degToRad && deltaPhi < -LowLim * degToRad) || (deltaPhi > LowLim * degToRad && deltaPhi < HighLim * degToRad); if (inNear) { - foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(nearNames[angularBin]) + HIST(binNames[Idx]), pt); - } + if (pt > meanPtMin.value) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(NearNames[AngBin]) + HIST(BinNames[IdxVal]), pt); + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(NearMeanNames[AngBin]) + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin + } // PEAK 1 //// Combine both sides of 120 and 240 degrees //// //// The final window maps angles above 270 degrees to negative deltaPhi //// - const bool inAway = (deltaPhi > (120. - hi) * degToRad && deltaPhi < (120. - lo) * degToRad) || (deltaPhi > (120. + lo) * degToRad && deltaPhi < (120. + hi) * degToRad) || - (deltaPhi > (240. - hi) * degToRad && deltaPhi < (240. - lo) * degToRad) || (deltaPhi > (240. + lo) * degToRad && deltaPhi < (240. + hi) * degToRad) || - (deltaPhi > (240. + lo - 360.) * degToRad && deltaPhi < (240. + hi - 360.) * degToRad); + const bool inAway = (deltaPhi > (120. - HighLim) * degToRad && deltaPhi < (120. - LowLim) * degToRad) || (deltaPhi > (120. + LowLim) * degToRad && deltaPhi < (120. + HighLim) * degToRad) || + (deltaPhi > (240. - HighLim) * degToRad && deltaPhi < (240. - LowLim) * degToRad) || (deltaPhi > (240. + LowLim) * degToRad && deltaPhi < (240. + HighLim) * degToRad) || + (deltaPhi > (240. + LowLim - 360.) * degToRad && deltaPhi < (240. + HighLim - 360.) * degToRad); if (inAway) { - foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(awayNames[angularBin]) + HIST(binNames[Idx]), pt); - } + if (pt > meanPtMin.value) { + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(AwayNames[AngBin]) + HIST(BinNames[IdxVal]), pt); + foldedHistos.fill(HIST("triEvFoldedLR/") + HIST(AwayMeanNames[AngBin]) + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin + } // PEAKS 2 AND 3 }); } //// TRI LR pT spectra in six symmetric angular intervals //// template void fillSameEventFoldedLR(int binIndex, + int nTracks, Track const& associatedTrack, double deltaPhi, double deltaEta, @@ -716,18 +740,18 @@ struct FoxWolframCorrelation { //// Fill only the requested multiplicity interval //// static_for<0, 9>([&](auto i) { - constexpr int idx = i.value; + constexpr int IdxVal = i.value; - if (binIndex != idx + 1) { + if (binIndex != IdxVal + 1) { return; } - fillFoldedBin(deltaPhi, associatedTrack.pt()); + fillFoldedBin(nTracks, deltaPhi, associatedTrack.pt()); }); } - //// Helper for one multiplicity bin (Idx is a template parameter, not a captured lambda variable) //// - template + //// Helper for one multiplicity bin (IdxVal is a template parameter, not a captured lambda variable) //// + template void fillPtBin(bool isAll, bool isTri, const std::array& allWindows, const std::array& triWindows, bool inLR, bool inSR, int nTracks, double pt) @@ -735,20 +759,25 @@ struct FoxWolframCorrelation { //// Fill ALL spectra and profiles with one entry per accepted pair //// if (isAll) { static_for<0, 2>([&](auto j) { - constexpr int region = j.value; + constexpr int RegionVal = j.value; - if (!allWindows[region]) { + if (!allWindows[RegionVal]) { return; } if (inLR) { - histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); - histos.fill(HIST("allEvPkVall/") + HIST(allLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + if (pt > meanPtMin.value) { + histos.fill(HIST("allEvPkVall/") + HIST(AllLrNames[RegionVal]) + HIST("Pt_") + HIST(BinNames[IdxVal]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(AllLrNames[RegionVal]) + HIST("PtMultip_") + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin } if (inSR) { - histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); - histos.fill(HIST("allEvPkVall/") + HIST(allSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + + if (pt > meanPtMin.value) { + histos.fill(HIST("allEvPkVall/") + HIST(AllSrNames[RegionVal]) + HIST("Pt_") + HIST(BinNames[IdxVal]), pt); + histos.fill(HIST("allEvPkVall/") + HIST(AllSrNames[RegionVal]) + HIST("PtMultip_") + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin } }); } @@ -756,20 +785,24 @@ struct FoxWolframCorrelation { //// Fill TRI spectra and profiles with one entry per accepted pair //// if (isTri) { static_for<0, 3>([&](auto j) { - constexpr int region = j.value; + constexpr int RegionVal = j.value; - if (!triWindows[region]) { + if (!triWindows[RegionVal]) { return; } if (inLR) { - histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); - histos.fill(HIST("triEvPkVall/") + HIST(triLRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + if (pt > meanPtMin.value) { + histos.fill(HIST("triEvPkVall/") + HIST(TriLrNames[RegionVal]) + HIST("Pt_") + HIST(BinNames[IdxVal]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(TriLrNames[RegionVal]) + HIST("PtMultip_") + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin } if (inSR) { - histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("Pt_") + HIST(binNames[Idx]), pt); - histos.fill(HIST("triEvPkVall/") + HIST(triSRNames[region]) + HIST("PtMultip_") + HIST(binNames[Idx]), nTracks, pt); + if (pt > meanPtMin.value) { + histos.fill(HIST("triEvPkVall/") + HIST(TriSrNames[RegionVal]) + HIST("Pt_") + HIST(BinNames[IdxVal]), pt); + histos.fill(HIST("triEvPkVall/") + HIST(TriSrNames[RegionVal]) + HIST("PtMultip_") + HIST(BinNames[IdxVal]), nTracks, pt); + } // for pt > meanPtMin } }); } @@ -797,8 +830,8 @@ struct FoxWolframCorrelation { const bool inSR = absEta < 0.35; //// Preserve the original open angular windows //// - const auto inPhi = [&](double lo, double hi) { - return deltaPhi > lo * degToRad && deltaPhi < hi * degToRad; + const auto inPhi = [&](double lowLim, double highLim) { + return deltaPhi > lowLim * degToRad && deltaPhi < highLim * degToRad; }; //// Event topology //// @@ -822,13 +855,13 @@ struct FoxWolframCorrelation { //// Fill only the requested multiplicity interval //// static_for<0, 9>([&](auto i) { - constexpr int idx = i.value; + constexpr int IdxVal = i.value; - if (binIndex != idx + 1) { + if (binIndex != IdxVal + 1) { return; } - fillPtBin(isAll, isTri, allWindows, triWindows, inLR, inSR, nTracks, pt); + fillPtBin(isAll, isTri, allWindows, triWindows, inLR, inSR, nTracks, pt); }); } @@ -842,9 +875,9 @@ struct FoxWolframCorrelation { { static_for<0, 9>([&](auto i) { - constexpr int idx = i.value; + constexpr int IdxVal = i.value; - if (binIndex != idx + 1) { + if (binIndex != IdxVal + 1) { return; } @@ -854,8 +887,8 @@ struct FoxWolframCorrelation { //// Count the pair and fill its original angular coordinates //// histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); - histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); - histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); + histos.fill(HIST("Corelations/sProjTRI_") + HIST(BinNames[IdxVal]), deltaPhi); + histos.fill(HIST("Corelations/sTRI_") + HIST(BinNames[IdxVal]), deltaPhi, deltaEta); }); } @@ -865,15 +898,15 @@ struct FoxWolframCorrelation { { // H[0] = H0 (normalizare), H[1..8] = H1..H8 static_for<0, 7>([&](auto j) { - constexpr int jj = j.value; - histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmHNames[jj]), H[jj + 1]); - histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmMultHNames[jj]), nTrk, H[jj + 1]); + constexpr int JjIt = j.value; + histos.fill(HIST(FwmRangeNames[R]) + HIST(FwmHlNames[JjIt]), H[JjIt + 1]); + histos.fill(HIST(FwmRangeNames[R]) + HIST(FwmMulthNames[JjIt]), nTrk, H[JjIt + 1]); }); if (doFWMCorrelations) { static_for<0, 27>([&](auto k) { - constexpr int kk = k.value; - histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmCorrelationNames[kk]), H[fwmCorrelationFirst[kk] + 1], H[fwmCorrelationSecond[kk] + 1]); + constexpr int KkIt = k.value; + histos.fill(HIST(FwmRangeNames[R]) + HIST(FwmCorrelationNames[KkIt]), H[FwmCorrelationFirst[KkIt] + 1], H[FwmCorrelationSecond[KkIt] + 1]); }); } } @@ -1041,7 +1074,7 @@ struct FoxWolframCorrelation { //// Fixed-window spectra and mean-pT profiles //// fillSameEventPtHist(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); //// TRI LR spectra in symmetric 10-degree windows //// - fillSameEventFoldedLR(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + fillSameEventFoldedLR(i + 1, nGlobalTracks, track2, deltaPhi, deltaEta, isLeadAssocPair); } } } @@ -1122,12 +1155,12 @@ struct FoxWolframCorrelation { // Build mixed-event backgrounds from the selected event tables. struct FoxWolframCorrelationMixing { - SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6, cache_bin7, cache_bin8, cache_bin9, cache_bin10; + SliceCache cacheBin1, cacheBin2, cacheBin3, cacheBin4, cacheBin5, cacheBin6, cacheBin7, cacheBin8, cacheBin9, cacheBin10; Preslice triCol = aod::track_tri::triangleCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - static constexpr std::array mixedBinNames = { + static constexpr std::array MixedBinNames = { "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; Configurable nBin{"nBin", 100, "No of bis per axis"}; @@ -1176,12 +1209,12 @@ struct FoxWolframCorrelationMixing { //// Each mixing axis must cover its configured integer multiplicity interval //// for (int i = 0; i < nMultBins; ++i) { const auto& edges = axisEdges[i]; - const double lo = multLower.value[i] - 0.5; - const double hi = multUpper.value[i] + 0.5; + const double lowLim = multLower.value[i] - 0.5; + const double highLim = multUpper.value[i] + 0.5; if (multLower.value[i] > multUpper.value[i] || edges.size() < 2 || - std::abs(edges[edges.size() - 2] - lo) > 1.e-6 || - std::abs(edges.back() - hi) > 1.e-6) { - LOGF(fatal, "axisMult%d must match multiplicity limits [%.1f, %.1f]", i + 1, lo, hi); + std::abs(edges[edges.size() - 2] - lowLim) > 1.e-6 || + std::abs(edges.back() - highLim) > 1.e-6) { + LOGF(fatal, "axisMult%d must match multiplicity limits [%.1f, %.1f]", i + 1, lowLim, highLim); } } @@ -1221,16 +1254,16 @@ struct FoxWolframCorrelationMixing { BinningType binningOnPosMultip10{{axisVtx, axisMult10}}; // Create independent mixing pools for the ten multiplicity intervals. - SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cache_bin1}; - SameKindPair pairBIN2{binningOnPosMultip2, nMixedEvents, -1, &cache_bin2}; - SameKindPair pairBIN3{binningOnPosMultip3, nMixedEvents, -1, &cache_bin3}; - SameKindPair pairBIN4{binningOnPosMultip4, nMixedEvents, -1, &cache_bin4}; - SameKindPair pairBIN5{binningOnPosMultip5, nMixedEvents, -1, &cache_bin5}; - SameKindPair pairBIN6{binningOnPosMultip6, nMixedEvents, -1, &cache_bin6}; - SameKindPair pairBIN7{binningOnPosMultip7, nMixedEvents, -1, &cache_bin7}; - SameKindPair pairBIN8{binningOnPosMultip8, nMixedEvents, -1, &cache_bin8}; - SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cache_bin9}; - SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cache_bin10}; + SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cacheBin1}; + SameKindPair pairBIN2{binningOnPosMultip2, nMixedEvents, -1, &cacheBin2}; + SameKindPair pairBIN3{binningOnPosMultip3, nMixedEvents, -1, &cacheBin3}; + SameKindPair pairBIN4{binningOnPosMultip4, nMixedEvents, -1, &cacheBin4}; + SameKindPair pairBIN5{binningOnPosMultip5, nMixedEvents, -1, &cacheBin5}; + SameKindPair pairBIN6{binningOnPosMultip6, nMixedEvents, -1, &cacheBin6}; + SameKindPair pairBIN7{binningOnPosMultip7, nMixedEvents, -1, &cacheBin7}; + SameKindPair pairBIN8{binningOnPosMultip8, nMixedEvents, -1, &cacheBin8}; + SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cacheBin9}; + SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cacheBin10}; // Pointers to the pools so they can be processed in a loop (filled in init()). using MixPair = SameKindPair; @@ -1265,10 +1298,10 @@ struct FoxWolframCorrelationMixing { // Count accepted pairs in the corresponding multiplicity interval. histos.fill(HIST("MixingPairs/MixingPairsNoLA"), binIndex); static_for<0, 9>([&](auto i) { - constexpr int idx = i.value; - if (binIndex == idx + 1) { - histos.fill(HIST("Corelations/bProjTRI_") + HIST(mixedBinNames[idx]), deltaPhi); - histos.fill(HIST("Corelations/bTRI_") + HIST(mixedBinNames[idx]), deltaPhi, deltaEta); + constexpr int IdxVal = i.value; + if (binIndex == IdxVal + 1) { + histos.fill(HIST("Corelations/bProjTRI_") + HIST(MixedBinNames[IdxVal]), deltaPhi); + histos.fill(HIST("Corelations/bTRI_") + HIST(MixedBinNames[IdxVal]), deltaPhi, deltaEta); } }); }